examples/storefront Public
// An R2 stand-in kept in memory: put, get, head, delete, and the parts of R2Object the// storefront reads (body, httpEtag, httpMetadata, writeHttpMetadata).type Stored = { bytes: Uint8Array; httpMetadata: R2HTTPMetadata; etag: string };export class TestR2 { readonly objects = new Map<string, Stored>(); async put(key: string, value: ArrayBuffer | ArrayBufferView | string, options: R2PutOptions = {}) { const bytes = typeof value === "string" ? new TextEncoder().encode(value) : new Uint8Array(ArrayBuffer.isView(value) ? value.buffer : value); const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)); const etag = [...digest.slice(0, 16)].map((b) => b.toString(16).padStart(2, "0")).join(""); this.objects.set(key, { bytes, httpMetadata: (options.httpMetadata ?? {}) as R2HTTPMetadata, etag }); return this.view(key)!; } async head(key: string) { return this.view(key); } async get(key: string) { const v = this.view(key); if (!v) return null; const bytes = this.objects.get(key)!.bytes; return { ...v, body: new Blob([bytes]).stream() }; } async delete(keys: string | string[]) { for (const k of Array.isArray(keys) ? keys : [keys]) this.objects.delete(k); } private view(key: string) { const o = this.objects.get(key); if (!o) return null; return { key, size: o.bytes.byteLength, etag: o.etag, httpEtag: `"${o.etag}"`, httpMetadata: o.httpMetadata, writeHttpMetadata(headers: Headers) { if (o.httpMetadata.contentType) headers.set("content-type", o.httpMetadata.contentType); if (o.httpMetadata.cacheControl) headers.set("cache-control", o.httpMetadata.cacheControl); }, }; }}export function testBucket(): R2Bucket { return new TestR2() as unknown as R2Bucket;}