import type { H3Event } from "h3"; import type { Env } from "./types"; /** * Cloudflare bindings (KV, D1, secrets) from the request context. Works on * Cloudflare Workers (fetch and scheduled) and in tests that stub * `event.context.cloudflare`. Throws with a clear message in other runtimes. */ export function cfEnv(event: H3Event): Env { const env = (event.context.cloudflare as { env?: Env } | undefined)?.env; if (!env) { throw new Error("Cloudflare bindings unavailable — run via wrangler dev/deploy"); } return env; } /** * The raw web `Request` for this event. On the Cloudflare module preset it * lives in `event.context.cloudflare.request`; on other web runtimes h3 * exposes it as `event.web.request`. * * NOTE: nitro pre-reads the request body into its internal buffers, so the * returned Request's body stream is already consumed — use `readRawBody()` * (or `readBody()`) to get the payload instead of `request.text()`. */ export function rawRequest(event: H3Event): Request { const cf = (event.context.cloudflare as { request?: Request } | undefined)?.request; const web = event.web?.request; const req = cf ?? web; if (!req) { throw new Error("Raw request unavailable — run via wrangler dev/deploy"); } return req; } /** `waitUntil` from the CF execution context (no-op outside workers). */ export function cfWaitUntil(event: H3Event): (promise: Promise) => void { const cloudflare = event.context.cloudflare as | { ctx?: { waitUntil?: (p: Promise) => void }; context?: { waitUntil?: (p: Promise) => void }; } | undefined; const waitUntil = (event.context.waitUntil as ((p: Promise) => void) | undefined) ?? cloudflare?.context?.waitUntil ?? cloudflare?.ctx?.waitUntil; return waitUntil ? waitUntil.bind(event.context) : () => undefined; } /** Lowercased request headers (the provider detection reads lowercase keys). */ export function headersFrom(event: H3Event): Record { const out: Record = {}; event.headers.forEach((value, key) => { out[key.toLowerCase()] = value; }); return out; }