All files / src/core/bindings R2Binding.ts

29.41% Statements 5/17
0% Branches 0/7
10% Functions 1/10
31.25% Lines 5/16

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 14528x 28x                                       28x               28x   28x                                                                                                                                                                                                                                  
import { Binding } from '@/core/bindings/Binding'
import { lazySettings } from '@/utils/lazySettings'
import { R2Settings } from '@/types/R2Settings'
import type { CloudflareEnv } from '@/types/CloudflareEnv'
 
/**
 * Accessor for the R2 bucket binding (`r2_buckets` in `wrangler.json`).
 *
 * @example
 * ```ts
 * const storage = new R2Binding() // binding name from the configuration layer
 * await storage.put(ctx.env, 'reports/2026-07.csv', body)
 * const object = await storage.get(ctx.env, 'reports/2026-07.csv')
 * ```
 *
 * @class
 *
 * @author Bayu Dwiyan Satria
 * @version 1.0.0
 * @since 1.0.0
 */
export class R2Binding<E extends CloudflareEnv = CloudflareEnv> extends Binding<R2Bucket, E> {
  /**
   * Constructs an R2Binding instance.
   *
   * @param settings Resolved settings. Defaults to `resolve('r2')`, so callers
   *   normally construct this with no arguments at all.
   */
  constructor(settings?: R2Settings) {
    const settle = lazySettings<R2Settings>('r2', settings)
 
    super(() => settle().binding)
  }
 
  /**
   * Reads an object, body included.
   *
   * @param env The Worker environment.
   * @param key The object key.
   * @param options Get options — range and conditional headers.
   * @returns The object, or `null` when it does not exist.
   * @throws {@link MissingBindingError} When the R2 binding is absent.
   */
  public async get(env: E, key: string, options?: R2GetOptions): Promise<R2ObjectBody | null> {
    return await this.resolve(env).get(key, options)
  }
 
  /**
   * Reads an object's body as text.
   *
   * @param env The Worker environment.
   * @param key The object key.
   * @returns The body as text, or `null` when the object does not exist.
   */
  public async getText(env: E, key: string): Promise<string | null> {
    const object = await this.get(env, key)
 
    return object ? await object.text() : null
  }
 
  /**
   * Reads an object's body and parses it as JSON.
   *
   * @param env The Worker environment.
   * @param key The object key.
   * @returns The parsed body, or `null` when the object does not exist.
   */
  public async getJson<T = unknown>(env: E, key: string): Promise<T | null> {
    const object = await this.get(env, key)
 
    return object ? await object.json<T>() : null
  }
 
  /**
   * Reads an object's metadata without transferring its body.
   *
   * @param env The Worker environment.
   * @param key The object key.
   * @returns The object metadata, or `null` when it does not exist.
   */
  public async head(env: E, key: string): Promise<R2Object | null> {
    return await this.resolve(env).head(key)
  }
 
  /**
   * Writes an object.
   *
   * @param env The Worker environment.
   * @param key The object key.
   * @param value The object body.
   * @param options Put options — HTTP metadata, custom metadata, and checksums.
   * @returns The stored object's metadata.
   */
  public async put(
    env: E,
    key: string,
    value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob,
    options?: R2PutOptions
  ): Promise<R2Object> {
    return await this.resolve(env).put(key, value, options)
  }
 
  /**
   * Serialises a value to JSON and writes it as an object.
   *
   * @param env The Worker environment.
   * @param key The object key.
   * @param value The value to serialise and store.
   * @param options Put options. The JSON content type is applied when unset.
   * @returns The stored object's metadata.
   */
  public async putJson(env: E, key: string, value: unknown, options: R2PutOptions = {}): Promise<R2Object> {
    const httpMetadata = options.httpMetadata || { contentType: 'application/json' }
 
    return await this.put(env, key, JSON.stringify(value), { ...options, httpMetadata })
  }
 
  /**
   * Deletes one or more objects.
   *
   * Deleting a key that does not exist is not an error.
   *
   * @param env The Worker environment.
   * @param keys A single object key, or a list of keys.
   */
  public async delete(env: E, keys: string | string[]): Promise<void> {
    await this.resolve(env).delete(keys)
  }
 
  /**
   * Lists objects in the bucket.
   *
   * @remarks
   * Results are paginated — check `truncated` and pass the returned `cursor`
   * back in to read the next page.
   *
   * @param env The Worker environment.
   * @param options List options — prefix, delimiter, limit, and cursor.
   * @returns The matched objects and pagination state.
   */
  public async list(env: E, options?: R2ListOptions): Promise<R2Objects> {
    return await this.resolve(env).list(options)
  }
}