| 1 | /** |
| 2 | * Async (non-Effect) Tail Worker fixture for `TailConsumers.test.ts` and |
| 3 | * `TailConsumers.local.test.ts`. |
| 4 | * |
| 5 | * Cloudflare invokes the exported `tail()` handler with the trace items of |
| 6 | * each invocation of any producer Worker that lists this script in its |
| 7 | * `tail_consumers`. Every batch is persisted to the bound KV namespace under |
| 8 | * a key prefixed with the *producing* script's name, so the live test |
| 9 | * verifies delivery out-of-band (distilled KV reads) without depending on |
| 10 | * which isolate served which request. The `/events` fetch route returns every |
| 11 | * recorded batch for the local test, where no out-of-band KV API exists — |
| 12 | * the local simulator is only reachable through the worker's own binding. |
| 13 | */ |
| 14 | |
| 15 | interface TailEventsKV { |
| 16 | put(key: string, value: string): Promise<void>; |
| 17 | get(key: string): Promise<string | null>; |
| 18 | list(options?: { prefix?: string }): Promise<{ keys: { name: string }[] }>; |
| 19 | } |
| 20 | |
| 21 | interface TraceItemLite { |
| 22 | scriptName?: string | null; |
| 23 | outcome?: string; |
| 24 | logs?: { message?: unknown[]; level?: string; timestamp?: number }[]; |
| 25 | } |
| 26 | |
| 27 | export default { |
| 28 | async fetch( |
| 29 | request: Request, |
| 30 | env: { EVENTS: TailEventsKV }, |
| 31 | ): Promise<Response> { |
| 32 | const url = new URL(request.url); |
| 33 | if (url.pathname === "/events") { |
| 34 | const list = await env.EVENTS.list({ prefix: "evt:" }); |
| 35 | const batches = await Promise.all( |
| 36 | list.keys.map((key) => env.EVENTS.get(key.name)), |
| 37 | ); |
| 38 | return Response.json({ |
| 39 | keys: list.keys.map((key) => key.name), |
| 40 | batches: batches.filter((batch): batch is string => batch !== null), |
| 41 | }); |
| 42 | } |
| 43 | return new Response("tail-consumer-ok"); |
| 44 | }, |
| 45 | async tail( |
| 46 | events: TraceItemLite[], |
| 47 | env: { EVENTS: TailEventsKV }, |
| 48 | ): Promise<void> { |
| 49 | const producer = events[0]?.scriptName ?? "unknown"; |
| 50 | await env.EVENTS.put( |
| 51 | `evt:${producer}:${Date.now()}`, |
| 52 | JSON.stringify(events), |
| 53 | ); |
| 54 | }, |
| 55 | }; |