| 1 | // Async (non-Effect) Worker that exercises the native R2 binding against |
| 2 | // the local workerd simulator: put / get / head / list / delete. |
| 3 | interface R2ObjectLike { |
| 4 | key: string; |
| 5 | etag: string; |
| 6 | httpEtag: string; |
| 7 | size: number; |
| 8 | text(): Promise<string>; |
| 9 | } |
| 10 | |
| 11 | interface Env { |
| 12 | BUCKET: { |
| 13 | put( |
| 14 | key: string, |
| 15 | value: string, |
| 16 | options?: { httpMetadata?: { contentType?: string } }, |
| 17 | ): Promise<R2ObjectLike>; |
| 18 | get(key: string): Promise<R2ObjectLike | null>; |
| 19 | head(key: string): Promise<R2ObjectLike | null>; |
| 20 | list(): Promise<{ objects: Array<{ key: string; size: number }> }>; |
| 21 | delete(key: string): Promise<void>; |
| 22 | }; |
| 23 | } |
| 24 | |
| 25 | export default { |
| 26 | fetch: async (request: Request, env: Env) => { |
| 27 | const url = new URL(request.url); |
| 28 | if (url.pathname === "/seed") { |
| 29 | // Writes without deleting, so the test can verify out-of-band (via |
| 30 | // the cloud API for an `Alchemy.remote()` bucket) that the object |
| 31 | // actually landed in the bound bucket. |
| 32 | await env.BUCKET.put("seed.txt", "seeded by worker", { |
| 33 | httpMetadata: { contentType: "text/plain" }, |
| 34 | }); |
| 35 | const head = await env.BUCKET.head("seed.txt"); |
| 36 | return Response.json({ etag: head?.etag ?? null }); |
| 37 | } |
| 38 | if (url.pathname === "/roundtrip") { |
| 39 | await env.BUCKET.put("greeting.txt", "hello r2", { |
| 40 | httpMetadata: { contentType: "text/plain" }, |
| 41 | }); |
| 42 | const obj = await env.BUCKET.get("greeting.txt"); |
| 43 | const text = obj === null ? null : await obj.text(); |
| 44 | const head = await env.BUCKET.head("greeting.txt"); |
| 45 | const list = await env.BUCKET.list(); |
| 46 | await env.BUCKET.delete("greeting.txt"); |
| 47 | const afterDelete = await env.BUCKET.get("greeting.txt"); |
| 48 | return Response.json({ |
| 49 | text, |
| 50 | etag: head?.etag ?? null, |
| 51 | size: head?.size ?? null, |
| 52 | keys: list.objects.map((o) => o.key), |
| 53 | afterDelete: afterDelete === null, |
| 54 | }); |
| 55 | } |
| 56 | if (url.pathname === "/get") { |
| 57 | const obj = await env.BUCKET.get(url.searchParams.get("key") ?? ""); |
| 58 | return Response.json({ text: obj === null ? null : await obj.text() }); |
| 59 | } |
| 60 | return new Response("not found", { status: 404 }); |
| 61 | }, |
| 62 | }; |