From 335fac5b2f1f2062b9f9401d720df64d6535313f Mon Sep 17 00:00:00 2001 From: James Date: Sun, 13 Oct 2024 00:37:26 +0100 Subject: [PATCH 1/8] refactor: extract proxy creation into utility --- .../cli/templates/utils/create-als-proxy.ts | 18 ++++++++++++++++++ .../src/cli/templates/utils/index.ts | 1 + .../cloudflare/src/cli/templates/worker.ts | 12 ++---------- 3 files changed, 21 insertions(+), 10 deletions(-) create mode 100644 packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts create mode 100644 packages/cloudflare/src/cli/templates/utils/index.ts diff --git a/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts b/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts new file mode 100644 index 00000000..1c17ce92 --- /dev/null +++ b/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts @@ -0,0 +1,18 @@ +import type { AsyncLocalStorage } from "node:async_hooks"; + +/** + * Creates a proxy for to use for an instance of AsyncLocalStorage. + * + * @param als AsyncLocalStorage instance. + */ +export function createALSProxy(als: AsyncLocalStorage) { + return new Proxy( + {}, + { + ownKeys: () => Reflect.ownKeys(als.getStore()!), + getOwnPropertyDescriptor: (_, ...args) => Reflect.getOwnPropertyDescriptor(als.getStore()!, ...args), + get: (_, property) => Reflect.get(als.getStore()!, property), + set: (_, property, value) => Reflect.set(als.getStore()!, property, value), + } + ); +} diff --git a/packages/cloudflare/src/cli/templates/utils/index.ts b/packages/cloudflare/src/cli/templates/utils/index.ts new file mode 100644 index 00000000..59eb65f8 --- /dev/null +++ b/packages/cloudflare/src/cli/templates/utils/index.ts @@ -0,0 +1 @@ +export * from "./create-als-proxy"; diff --git a/packages/cloudflare/src/cli/templates/worker.ts b/packages/cloudflare/src/cli/templates/worker.ts index ce048089..dacbf4d4 100644 --- a/packages/cloudflare/src/cli/templates/worker.ts +++ b/packages/cloudflare/src/cli/templates/worker.ts @@ -1,6 +1,7 @@ import { AsyncLocalStorage } from "node:async_hooks"; import type { IncomingMessage } from "node:http"; import Stream from "node:stream"; +import { createALSProxy } from "./utils"; import type { ExportedHandler, Fetcher } from "@cloudflare/workers-types"; import type { NextConfig } from "next"; @@ -16,16 +17,7 @@ const cloudflareContextALS = new AsyncLocalStorage(); // Note: this symbol needs to be kept in sync with the one defined in `src/api/get-cloudflare-context.ts` // eslint-disable-next-line @typescript-eslint/no-explicit-any -(globalThis as any)[Symbol.for("__cloudflare-context__")] = new Proxy( - {}, - { - ownKeys: () => Reflect.ownKeys(cloudflareContextALS.getStore()!), - getOwnPropertyDescriptor: (_, ...args) => - Reflect.getOwnPropertyDescriptor(cloudflareContextALS.getStore()!, ...args), - get: (_, property) => Reflect.get(cloudflareContextALS.getStore()!, property), - set: (_, property, value) => Reflect.set(cloudflareContextALS.getStore()!, property, value), - } -); +(globalThis as any)[Symbol.for("__cloudflare-context__")] = createALSProxy(cloudflareContextALS); // Injected at build time const nextConfig: NextConfig = JSON.parse(process.env.__NEXT_PRIVATE_STANDALONE_CONFIG ?? "{}"); From 627d10b7c1250c0e895a198f8ff481572b3c6cee Mon Sep 17 00:00:00 2001 From: James Date: Sun, 13 Oct 2024 00:45:56 +0100 Subject: [PATCH 2/8] refactor: move process.env to ALS proxy --- .../cloudflare/src/cli/templates/worker.ts | 64 +++++++++++-------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/packages/cloudflare/src/cli/templates/worker.ts b/packages/cloudflare/src/cli/templates/worker.ts index dacbf4d4..2eebb066 100644 --- a/packages/cloudflare/src/cli/templates/worker.ts +++ b/packages/cloudflare/src/cli/templates/worker.ts @@ -13,12 +13,19 @@ import type { CloudflareContext } from "../../api"; const NON_BODY_RESPONSES = new Set([101, 204, 205, 304]); +const processEnvALS = new AsyncLocalStorage>(); const cloudflareContextALS = new AsyncLocalStorage(); // Note: this symbol needs to be kept in sync with the one defined in `src/api/get-cloudflare-context.ts` // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any)[Symbol.for("__cloudflare-context__")] = createALSProxy(cloudflareContextALS); +globalThis.process = { + ...globalThis.process, + // @ts-expect-error - populated when we run inside the ALS context + env: createALSProxy(processEnvALS), +}; + // Injected at build time const nextConfig: NextConfig = JSON.parse(process.env.__NEXT_PRIVATE_STANDALONE_CONFIG ?? "{}"); @@ -26,40 +33,41 @@ let requestHandler: NodeRequestHandler | null = null; export default { async fetch(request, env, ctx) { - return cloudflareContextALS.run({ env, ctx, cf: request.cf }, async () => { - if (requestHandler == null) { - globalThis.process.env = { ...globalThis.process.env, ...env }; - // Note: "next/dist/server/next-server" is a cjs module so we have to `require` it not to confuse esbuild - // (since esbuild can run in projects with different module resolutions) - // eslint-disable-next-line @typescript-eslint/no-require-imports - const NextNodeServer = require("next/dist/server/next-server") - .default as typeof import("next/dist/server/next-server").default; - - requestHandler = new NextNodeServer({ - conf: nextConfig, - customServer: false, - dev: false, - dir: "", - minimalMode: false, - }).getRequestHandler(); - } + return processEnvALS.run({ NODE_ENV: "production", ...env }, () => { + return cloudflareContextALS.run({ env, ctx, cf: request.cf }, async () => { + if (requestHandler == null) { + // Note: "next/dist/server/next-server" is a cjs module so we have to `require` it not to confuse esbuild + // (since esbuild can run in projects with different module resolutions) + // eslint-disable-next-line @typescript-eslint/no-require-imports + const NextNodeServer = require("next/dist/server/next-server") + .default as typeof import("next/dist/server/next-server").default; + + requestHandler = new NextNodeServer({ + conf: nextConfig, + customServer: false, + dev: false, + dir: "", + minimalMode: false, + }).getRequestHandler(); + } - const url = new URL(request.url); + const url = new URL(request.url); - if (url.pathname === "/_next/image") { - const imageUrl = - url.searchParams.get("url") ?? "https://developers.cloudflare.com/_astro/logo.BU9hiExz.svg"; - if (imageUrl.startsWith("/")) { - return env.ASSETS.fetch(new URL(imageUrl, request.url)); + if (url.pathname === "/_next/image") { + const imageUrl = + url.searchParams.get("url") ?? "https://developers.cloudflare.com/_astro/logo.BU9hiExz.svg"; + if (imageUrl.startsWith("/")) { + return env.ASSETS.fetch(new URL(imageUrl, request.url)); + } + return fetch(imageUrl, { cf: { cacheEverything: true } }); } - return fetch(imageUrl, { cf: { cacheEverything: true } }); - } - const { req, res, webResponse } = getWrappedStreams(request, ctx); + const { req, res, webResponse } = getWrappedStreams(request, ctx); - ctx.waitUntil(Promise.resolve(requestHandler(new NodeNextRequest(req), new NodeNextResponse(res)))); + ctx.waitUntil(Promise.resolve(requestHandler(new NodeNextRequest(req), new NodeNextResponse(res)))); - return await webResponse(); + return await webResponse(); + }); }); }, } as ExportedHandler<{ ASSETS: Fetcher }>; From e7f920683e442caa394ca63f7789bca22fe0c24f Mon Sep 17 00:00:00 2001 From: James Anderson Date: Fri, 18 Oct 2024 14:07:00 +0100 Subject: [PATCH 3/8] Apply suggestions from code review Co-authored-by: Dario Piotrowicz --- .../cloudflare/src/cli/templates/utils/create-als-proxy.ts | 4 ++-- packages/cloudflare/src/cli/templates/worker.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts b/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts index 1c17ce92..ea709b00 100644 --- a/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts +++ b/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts @@ -1,9 +1,9 @@ import type { AsyncLocalStorage } from "node:async_hooks"; /** - * Creates a proxy for to use for an instance of AsyncLocalStorage. + * Creates a proxy that exposes values from an AsyncLocalStorage store * - * @param als AsyncLocalStorage instance. + * @param als AsyncLocalStorage instance */ export function createALSProxy(als: AsyncLocalStorage) { return new Proxy( diff --git a/packages/cloudflare/src/cli/templates/worker.ts b/packages/cloudflare/src/cli/templates/worker.ts index 2eebb066..4e0b83c5 100644 --- a/packages/cloudflare/src/cli/templates/worker.ts +++ b/packages/cloudflare/src/cli/templates/worker.ts @@ -1,7 +1,6 @@ import { AsyncLocalStorage } from "node:async_hooks"; import type { IncomingMessage } from "node:http"; import Stream from "node:stream"; -import { createALSProxy } from "./utils"; import type { ExportedHandler, Fetcher } from "@cloudflare/workers-types"; import type { NextConfig } from "next"; @@ -10,6 +9,7 @@ import { MockedResponse } from "next/dist/server/lib/mock-request"; import type { NodeRequestHandler } from "next/dist/server/next-server"; import type { CloudflareContext } from "../../api"; +import { createALSProxy } from "./utils"; const NON_BODY_RESPONSES = new Set([101, 204, 205, 304]); From 4be38fa27605edc2dbe3de8f187ce63ad92f1d77 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 23 Oct 2024 20:23:56 +0100 Subject: [PATCH 4/8] use ALS for entire process object --- .../cli/templates/utils/create-als-proxy.ts | 17 ++--- .../cloudflare/src/cli/templates/worker.ts | 71 +++++++++---------- 2 files changed, 42 insertions(+), 46 deletions(-) diff --git a/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts b/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts index ea709b00..6993c3b0 100644 --- a/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts +++ b/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts @@ -5,14 +5,11 @@ import type { AsyncLocalStorage } from "node:async_hooks"; * * @param als AsyncLocalStorage instance */ -export function createALSProxy(als: AsyncLocalStorage) { - return new Proxy( - {}, - { - ownKeys: () => Reflect.ownKeys(als.getStore()!), - getOwnPropertyDescriptor: (_, ...args) => Reflect.getOwnPropertyDescriptor(als.getStore()!, ...args), - get: (_, property) => Reflect.get(als.getStore()!, property), - set: (_, property, value) => Reflect.set(als.getStore()!, property, value), - } - ); +export function createALSProxy(als: AsyncLocalStorage) { + return new Proxy({} as T, { + ownKeys: () => Reflect.ownKeys(als.getStore()!), + getOwnPropertyDescriptor: (_, ...args) => Reflect.getOwnPropertyDescriptor(als.getStore()!, ...args), + get: (_, property) => Reflect.get(als.getStore()!, property), + set: (_, property, value) => Reflect.set(als.getStore()!, property, value), + }); } diff --git a/packages/cloudflare/src/cli/templates/worker.ts b/packages/cloudflare/src/cli/templates/worker.ts index 4e0b83c5..d1d914cf 100644 --- a/packages/cloudflare/src/cli/templates/worker.ts +++ b/packages/cloudflare/src/cli/templates/worker.ts @@ -13,18 +13,14 @@ import { createALSProxy } from "./utils"; const NON_BODY_RESPONSES = new Set([101, 204, 205, 304]); -const processEnvALS = new AsyncLocalStorage>(); +const processALS = new AsyncLocalStorage(); const cloudflareContextALS = new AsyncLocalStorage(); // Note: this symbol needs to be kept in sync with the one defined in `src/api/get-cloudflare-context.ts` // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any)[Symbol.for("__cloudflare-context__")] = createALSProxy(cloudflareContextALS); -globalThis.process = { - ...globalThis.process, - // @ts-expect-error - populated when we run inside the ALS context - env: createALSProxy(processEnvALS), -}; +globalThis.process = createALSProxy(processALS); // Injected at build time const nextConfig: NextConfig = JSON.parse(process.env.__NEXT_PRIVATE_STANDALONE_CONFIG ?? "{}"); @@ -33,42 +29,45 @@ let requestHandler: NodeRequestHandler | null = null; export default { async fetch(request, env, ctx) { - return processEnvALS.run({ NODE_ENV: "production", ...env }, () => { - return cloudflareContextALS.run({ env, ctx, cf: request.cf }, async () => { - if (requestHandler == null) { - // Note: "next/dist/server/next-server" is a cjs module so we have to `require` it not to confuse esbuild - // (since esbuild can run in projects with different module resolutions) - // eslint-disable-next-line @typescript-eslint/no-require-imports - const NextNodeServer = require("next/dist/server/next-server") - .default as typeof import("next/dist/server/next-server").default; - - requestHandler = new NextNodeServer({ - conf: nextConfig, - customServer: false, - dev: false, - dir: "", - minimalMode: false, - }).getRequestHandler(); - } + return processALS.run( + { ...globalThis.process, env: { ...globalThis.process.env, NODE_ENV: "production", ...env } }, + () => { + return cloudflareContextALS.run({ env, ctx, cf: request.cf }, async () => { + if (requestHandler == null) { + // Note: "next/dist/server/next-server" is a cjs module so we have to `require` it not to confuse esbuild + // (since esbuild can run in projects with different module resolutions) + // eslint-disable-next-line @typescript-eslint/no-require-imports + const NextNodeServer = require("next/dist/server/next-server") + .default as typeof import("next/dist/server/next-server").default; + + requestHandler = new NextNodeServer({ + conf: nextConfig, + customServer: false, + dev: false, + dir: "", + minimalMode: false, + }).getRequestHandler(); + } - const url = new URL(request.url); + const url = new URL(request.url); - if (url.pathname === "/_next/image") { - const imageUrl = - url.searchParams.get("url") ?? "https://developers.cloudflare.com/_astro/logo.BU9hiExz.svg"; - if (imageUrl.startsWith("/")) { - return env.ASSETS.fetch(new URL(imageUrl, request.url)); + if (url.pathname === "/_next/image") { + const imageUrl = + url.searchParams.get("url") ?? "https://developers.cloudflare.com/_astro/logo.BU9hiExz.svg"; + if (imageUrl.startsWith("/")) { + return env.ASSETS.fetch(new URL(imageUrl, request.url)); + } + return fetch(imageUrl, { cf: { cacheEverything: true } }); } - return fetch(imageUrl, { cf: { cacheEverything: true } }); - } - const { req, res, webResponse } = getWrappedStreams(request, ctx); + const { req, res, webResponse } = getWrappedStreams(request, ctx); - ctx.waitUntil(Promise.resolve(requestHandler(new NodeNextRequest(req), new NodeNextResponse(res)))); + ctx.waitUntil(Promise.resolve(requestHandler(new NodeNextRequest(req), new NodeNextResponse(res)))); - return await webResponse(); - }); - }); + return await webResponse(); + }); + } + ); }, } as ExportedHandler<{ ASSETS: Fetcher }>; From 4628b628669ef000b565bfd77446ced7b24dedcf Mon Sep 17 00:00:00 2001 From: James Date: Wed, 23 Oct 2024 20:27:28 +0100 Subject: [PATCH 5/8] changeset --- .changeset/funny-impalas-attend.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/funny-impalas-attend.md diff --git a/.changeset/funny-impalas-attend.md b/.changeset/funny-impalas-attend.md new file mode 100644 index 00000000..c6ba4bca --- /dev/null +++ b/.changeset/funny-impalas-attend.md @@ -0,0 +1,7 @@ +--- +"@opennextjs/cloudflare": patch +--- + +refactor: use ALS for `process.env` object. + +The adaptor was previously manipulating the global process.env object on every request, without accounting for other requests. ALS has been introduced to change this behavior, so that each process.env object is scoped to the request. From 9ecefdfc13cddf43d73da7fd4180fa950d302108 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 23 Oct 2024 20:51:32 +0100 Subject: [PATCH 6/8] Revert "use ALS for entire process object" This reverts commit 4be38fa27605edc2dbe3de8f187ce63ad92f1d77. --- .../cli/templates/utils/create-als-proxy.ts | 17 +++-- .../cloudflare/src/cli/templates/worker.ts | 71 ++++++++++--------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts b/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts index 6993c3b0..ea709b00 100644 --- a/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts +++ b/packages/cloudflare/src/cli/templates/utils/create-als-proxy.ts @@ -5,11 +5,14 @@ import type { AsyncLocalStorage } from "node:async_hooks"; * * @param als AsyncLocalStorage instance */ -export function createALSProxy(als: AsyncLocalStorage) { - return new Proxy({} as T, { - ownKeys: () => Reflect.ownKeys(als.getStore()!), - getOwnPropertyDescriptor: (_, ...args) => Reflect.getOwnPropertyDescriptor(als.getStore()!, ...args), - get: (_, property) => Reflect.get(als.getStore()!, property), - set: (_, property, value) => Reflect.set(als.getStore()!, property, value), - }); +export function createALSProxy(als: AsyncLocalStorage) { + return new Proxy( + {}, + { + ownKeys: () => Reflect.ownKeys(als.getStore()!), + getOwnPropertyDescriptor: (_, ...args) => Reflect.getOwnPropertyDescriptor(als.getStore()!, ...args), + get: (_, property) => Reflect.get(als.getStore()!, property), + set: (_, property, value) => Reflect.set(als.getStore()!, property, value), + } + ); } diff --git a/packages/cloudflare/src/cli/templates/worker.ts b/packages/cloudflare/src/cli/templates/worker.ts index d1d914cf..4e0b83c5 100644 --- a/packages/cloudflare/src/cli/templates/worker.ts +++ b/packages/cloudflare/src/cli/templates/worker.ts @@ -13,14 +13,18 @@ import { createALSProxy } from "./utils"; const NON_BODY_RESPONSES = new Set([101, 204, 205, 304]); -const processALS = new AsyncLocalStorage(); +const processEnvALS = new AsyncLocalStorage>(); const cloudflareContextALS = new AsyncLocalStorage(); // Note: this symbol needs to be kept in sync with the one defined in `src/api/get-cloudflare-context.ts` // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any)[Symbol.for("__cloudflare-context__")] = createALSProxy(cloudflareContextALS); -globalThis.process = createALSProxy(processALS); +globalThis.process = { + ...globalThis.process, + // @ts-expect-error - populated when we run inside the ALS context + env: createALSProxy(processEnvALS), +}; // Injected at build time const nextConfig: NextConfig = JSON.parse(process.env.__NEXT_PRIVATE_STANDALONE_CONFIG ?? "{}"); @@ -29,45 +33,42 @@ let requestHandler: NodeRequestHandler | null = null; export default { async fetch(request, env, ctx) { - return processALS.run( - { ...globalThis.process, env: { ...globalThis.process.env, NODE_ENV: "production", ...env } }, - () => { - return cloudflareContextALS.run({ env, ctx, cf: request.cf }, async () => { - if (requestHandler == null) { - // Note: "next/dist/server/next-server" is a cjs module so we have to `require` it not to confuse esbuild - // (since esbuild can run in projects with different module resolutions) - // eslint-disable-next-line @typescript-eslint/no-require-imports - const NextNodeServer = require("next/dist/server/next-server") - .default as typeof import("next/dist/server/next-server").default; - - requestHandler = new NextNodeServer({ - conf: nextConfig, - customServer: false, - dev: false, - dir: "", - minimalMode: false, - }).getRequestHandler(); - } + return processEnvALS.run({ NODE_ENV: "production", ...env }, () => { + return cloudflareContextALS.run({ env, ctx, cf: request.cf }, async () => { + if (requestHandler == null) { + // Note: "next/dist/server/next-server" is a cjs module so we have to `require` it not to confuse esbuild + // (since esbuild can run in projects with different module resolutions) + // eslint-disable-next-line @typescript-eslint/no-require-imports + const NextNodeServer = require("next/dist/server/next-server") + .default as typeof import("next/dist/server/next-server").default; + + requestHandler = new NextNodeServer({ + conf: nextConfig, + customServer: false, + dev: false, + dir: "", + minimalMode: false, + }).getRequestHandler(); + } - const url = new URL(request.url); + const url = new URL(request.url); - if (url.pathname === "/_next/image") { - const imageUrl = - url.searchParams.get("url") ?? "https://developers.cloudflare.com/_astro/logo.BU9hiExz.svg"; - if (imageUrl.startsWith("/")) { - return env.ASSETS.fetch(new URL(imageUrl, request.url)); - } - return fetch(imageUrl, { cf: { cacheEverything: true } }); + if (url.pathname === "/_next/image") { + const imageUrl = + url.searchParams.get("url") ?? "https://developers.cloudflare.com/_astro/logo.BU9hiExz.svg"; + if (imageUrl.startsWith("/")) { + return env.ASSETS.fetch(new URL(imageUrl, request.url)); } + return fetch(imageUrl, { cf: { cacheEverything: true } }); + } - const { req, res, webResponse } = getWrappedStreams(request, ctx); + const { req, res, webResponse } = getWrappedStreams(request, ctx); - ctx.waitUntil(Promise.resolve(requestHandler(new NodeNextRequest(req), new NodeNextResponse(res)))); + ctx.waitUntil(Promise.resolve(requestHandler(new NodeNextRequest(req), new NodeNextResponse(res)))); - return await webResponse(); - }); - } - ); + return await webResponse(); + }); + }); }, } as ExportedHandler<{ ASSETS: Fetcher }>; From d3a2bdee0073191d0cfc18903efec14917af41e2 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 23 Oct 2024 20:53:23 +0100 Subject: [PATCH 7/8] change overriding to only be the env obj --- packages/cloudflare/src/cli/templates/worker.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/cloudflare/src/cli/templates/worker.ts b/packages/cloudflare/src/cli/templates/worker.ts index 4e0b83c5..7470f1f5 100644 --- a/packages/cloudflare/src/cli/templates/worker.ts +++ b/packages/cloudflare/src/cli/templates/worker.ts @@ -20,11 +20,8 @@ const cloudflareContextALS = new AsyncLocalStorage(); // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any)[Symbol.for("__cloudflare-context__")] = createALSProxy(cloudflareContextALS); -globalThis.process = { - ...globalThis.process, - // @ts-expect-error - populated when we run inside the ALS context - env: createALSProxy(processEnvALS), -}; +// @ts-expect-error - populated when we run inside the ALS context +globalThis.process.env = createALSProxy(processEnvALS); // Injected at build time const nextConfig: NextConfig = JSON.parse(process.env.__NEXT_PRIVATE_STANDALONE_CONFIG ?? "{}"); From 9f13e52d53e36eb986cd896a49a12f918834eb23 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 4 Nov 2024 08:06:33 +0000 Subject: [PATCH 8/8] capture original process.env value --- packages/cloudflare/src/cli/templates/worker.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cloudflare/src/cli/templates/worker.ts b/packages/cloudflare/src/cli/templates/worker.ts index 7470f1f5..980460f2 100644 --- a/packages/cloudflare/src/cli/templates/worker.ts +++ b/packages/cloudflare/src/cli/templates/worker.ts @@ -20,6 +20,7 @@ const cloudflareContextALS = new AsyncLocalStorage(); // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any)[Symbol.for("__cloudflare-context__")] = createALSProxy(cloudflareContextALS); +const originalEnv: Partial = { ...globalThis.process.env }; // @ts-expect-error - populated when we run inside the ALS context globalThis.process.env = createALSProxy(processEnvALS); @@ -30,7 +31,7 @@ let requestHandler: NodeRequestHandler | null = null; export default { async fetch(request, env, ctx) { - return processEnvALS.run({ NODE_ENV: "production", ...env }, () => { + return processEnvALS.run({ NODE_ENV: "production", ...originalEnv, ...env }, () => { return cloudflareContextALS.run({ env, ctx, cf: request.cf }, async () => { if (requestHandler == null) { // Note: "next/dist/server/next-server" is a cjs module so we have to `require` it not to confuse esbuild