/** * Initialization for the workerd runtime. * * The file must be imported at the top level the worker. */ import { AsyncLocalStorage } from "node:async_hooks"; import process from "node:process"; import stream from "node:stream"; // @ts-expect-error: resolved by wrangler build import * as nextEnvVars from "./next-env.mjs"; const cloudflareContextALS = new AsyncLocalStorage(); // Note: this symbol needs to be kept in sync with `src/api/get-cloudflare-context.ts` Object.defineProperty(globalThis, Symbol.for("__cloudflare-context__"), { get() { return cloudflareContextALS.getStore(); }, }); /** * Executes the handler with the Cloudflare context. */ export async function runWithCloudflareRequestContext(request, env, ctx, handler) { init(request, env); return cloudflareContextALS.run({ env, ctx, cf: request.cf }, handler); } let initialized = false; /** * Initializes the runtime on the first call, * no-op on subsequent invocations. */ function init(request, env) { if (initialized) { return; } initialized = true; const url = new URL(request.url); initRuntime(); populateProcessEnv(url, env); } function initRuntime() { // Some packages rely on `process.version` and `process.versions.node` (i.e. Jose@4) // TODO: Remove when https://github.com/unjs/unenv/pull/493 is merged Object.assign(process, { version: process.version || "v22.14.0" }); // @ts-expect-error Node type does not match workerd Object.assign(process.versions, { node: "22.14.0", ...process.versions }); globalThis.__dirname ??= ""; globalThis.__filename ??= ""; // Some packages rely on `import.meta.url` but it is undefined in workerd // For example it causes a bunch of issues, and will make even import crash with payload import.meta.url ??= "file:///worker.js"; // Do not crash on cache not supported // https://github.com/cloudflare/workerd/pull/2434 // compatibility flag "cache_option_enabled" -> does not support "force-cache" const __original_fetch = globalThis.fetch; globalThis.fetch = (input, init) => { if (init) { delete init.cache; } return __original_fetch(input, init); }; const CustomRequest = class extends globalThis.Request { constructor(input, init) { if (init) { delete init.cache; // https://github.com/cloudflare/workerd/issues/2746 // https://github.com/cloudflare/workerd/issues/3245 Object.defineProperty(init, "body", { // @ts-ignore value: init.body instanceof stream.Readable ? ReadableStream.from(init.body) : init.body, }); } super(input, init); } }; Object.assign(globalThis, { Request: CustomRequest, __BUILD_TIMESTAMP_MS__, __NEXT_BASE_PATH__, __ASSETS_RUN_WORKER_FIRST__, __TRAILING_SLASH__, // The external middleware will use the convertTo function of the `edge` converter // by default it will try to fetch the request, but since we are running everything in the same worker // we need to use the request as is. __dangerous_ON_edge_converter_returns_request: true, }); } /** * Populate process.env with: * - the environment variables and secrets from the cloudflare platform * - the variables from Next .env* files * - the origin resolver information */ function populateProcessEnv(url, env) { for (const [key, value] of Object.entries(env)) { if (typeof value === "string") { process.env[key] = value; } } const mode = env.NEXTJS_ENV ?? "production"; if (nextEnvVars[mode]) { for (const key in nextEnvVars[mode]) { process.env[key] ??= nextEnvVars[mode][key]; } } // Set the default Origin for the origin resolver. // This is only needed for an external middleware bundle process.env.OPEN_NEXT_ORIGIN = JSON.stringify({ default: { host: url.hostname, protocol: url.protocol.slice(0, -1), port: url.port, }, }); /* We need to set this environment variable to make redirects work properly in preview mode. * Next sets this in standalone mode during `startServer`. Without this the protocol would always be `https` here: * https://github.com/vercel/next.js/blob/6b1e48080e896e0d44a05fe009cb79d2d3f91774/packages/next/src/server/app-render/action-handler.ts#L307-L316 */ process.env.__NEXT_PRIVATE_ORIGIN = url.origin; // `__DEPLOYMENT_ID__` is a string (passed via ESBuild). if (__DEPLOYMENT_ID__) { process.env.DEPLOYMENT_ID = __DEPLOYMENT_ID__; } }