| 1 | /** |
| 2 | * Path-`main` Worker: the stack declares it as |
| 3 | * `Cloudflare.Worker("ApiWorker", { main: "./src/api-worker.ts", … })` and |
| 4 | * never imports this module. `bun --watch` therefore cannot see it, so an |
| 5 | * edit here travels the OTHER dev reload path — the local Worker |
| 6 | * provider's bundler watch loop rebuilds and hot-swaps the script while the |
| 7 | * exec child, the plan, and every other resource stay exactly as they are. |
| 8 | * |
| 9 | * Deliberately free of `alchemy` imports so the bundle graph is tiny and |
| 10 | * the measured rebuild latency is the dev server's, not rolldown's. |
| 11 | * |
| 12 | * Routes: |
| 13 | * - `GET /marker` → marker + the movable module's value |
| 14 | * - `GET /kv` → native KV binding roundtrip |
| 15 | * - `GET /env` → plain vars, including the cross-cloud Lambda URL |
| 16 | * - `GET /aws/*` → CROSS-CLOUD: fetches the AWS Lambda function URL |
| 17 | * served by the floci emulator over its own TLS cert |
| 18 | */ |
| 19 | import { API_MARKER } from "./api/marker.ts"; |
| 20 | import { message } from "./api/message.ts"; |
| 21 | |
| 22 | interface KVNamespaceLike { |
| 23 | put(key: string, value: string): Promise<void>; |
| 24 | get(key: string): Promise<string | null>; |
| 25 | } |
| 26 | |
| 27 | interface Env { |
| 28 | KV: KVNamespaceLike; |
| 29 | API_VARIABLE: string; |
| 30 | /** Added and removed by the binding-churn phase; absent most of the time. */ |
| 31 | API_EXTRA?: string; |
| 32 | /** The cross-cloud edge; absent while the AWS half is deleted. */ |
| 33 | AWS_LAMBDA_URL?: string; |
| 34 | } |
| 35 | |
| 36 | export default { |
| 37 | fetch: async (request: Request, env: Env) => { |
| 38 | const url = new URL(request.url); |
| 39 | |
| 40 | if (url.pathname === "/marker") { |
| 41 | return Response.json({ marker: API_MARKER, message: message() }); |
| 42 | } |
| 43 | |
| 44 | if (url.pathname === "/kv") { |
| 45 | const key = url.searchParams.get("key") ?? "hello"; |
| 46 | await env.KV.put(key, `kv:${key}`); |
| 47 | return Response.json({ value: await env.KV.get(key) }); |
| 48 | } |
| 49 | |
| 50 | if (url.pathname === "/env") { |
| 51 | return Response.json({ |
| 52 | API_VARIABLE: env.API_VARIABLE ?? null, |
| 53 | API_EXTRA: env.API_EXTRA ?? null, |
| 54 | AWS_LAMBDA_URL: env.AWS_LAMBDA_URL ?? null, |
| 55 | }); |
| 56 | } |
| 57 | |
| 58 | if (url.pathname.startsWith("/aws")) { |
| 59 | // Cross-cloud hop: local workerd → the AWS Lambda's function URL, |
| 60 | // which under `alchemy dev` is served by the floci emulator behind a |
| 61 | // self-signed cert. It only resolves because `alchemy dev` puts the |
| 62 | // emulator CA on NODE_EXTRA_CA_CERTS for every process it spawns. |
| 63 | const base = env.AWS_LAMBDA_URL; |
| 64 | if (!base) return Response.json({ error: "no lambda url" }, { status: 503 }); |
| 65 | const target = new URL(url.pathname.slice("/aws".length) || "/", base); |
| 66 | target.search = url.search; |
| 67 | const upstream = await fetch(target, { |
| 68 | method: request.method, |
| 69 | headers: { "content-type": "application/json" }, |
| 70 | body: request.method === "GET" ? undefined : await request.text(), |
| 71 | }); |
| 72 | return new Response(await upstream.text(), { |
| 73 | status: upstream.status, |
| 74 | headers: { "content-type": "application/json" }, |
| 75 | }); |
| 76 | } |
| 77 | |
| 78 | return Response.json({ marker: API_MARKER }); |
| 79 | }, |
| 80 | }; |