diff --git a/examples/api/app/api/hello/route.ts b/examples/api/app/api/hello/route.ts index 9bb7d2b4..c42676cc 100644 --- a/examples/api/app/api/hello/route.ts +++ b/examples/api/app/api/hello/route.ts @@ -1,10 +1,19 @@ import { headers } from "next/headers"; +import { getCloudflareContext } from "@opennextjs/cloudflare"; + export async function GET() { - // Note: we use headers just so that the route is not built as a static one const headersList = headers(); - const sayHi = !!headersList.get("should-say-hi"); - return new Response(sayHi ? "Hi World!" : "Hello World!"); + + const fromCloudflareContext = headersList.has("from-cloudflare-context"); + + if (!fromCloudflareContext) { + return new Response("Hello World!"); + } + + // Retrieve the bindings defined in wrangler.toml + const { env } = await getCloudflareContext(); + return new Response(env.hello); } export async function POST(request: Request) { diff --git a/examples/api/e2e-tests/base.spec.ts b/examples/api/e2e-tests/base.spec.ts index 2b7a88c0..a4f8811e 100644 --- a/examples/api/e2e-tests/base.spec.ts +++ b/examples/api/e2e-tests/base.spec.ts @@ -15,6 +15,16 @@ test("the hello-world api GET route works as intended", async ({ page }) => { expect(await res.text()).toEqual("Hello World!"); }); +test("returns a hello world string from the cloudflare context env", async ({ page }) => { + const res = await page.request.get("/api/hello", { + headers: { + "from-cloudflare-context": "true", + }, + }); + expect(res.headers()["content-type"]).toContain("text/plain"); + expect(await res.text()).toEqual("Hello World from the cloudflare context!"); +}); + test("the hello-world api POST route works as intended", async ({ page }) => { const res = await page.request.post("/api/hello", { data: "some body" }); expect(res.headers()["content-type"]).toContain("text/plain"); diff --git a/examples/api/package.json b/examples/api/package.json index dd63f6cd..54bb2149 100644 --- a/examples/api/package.json +++ b/examples/api/package.json @@ -10,7 +10,8 @@ "build:worker": "pnpm cloudflare", "dev:worker": "wrangler dev --port 8770", "preview:worker": "pnpm build:worker && pnpm dev:worker", - "e2e": "playwright test" + "e2e": "playwright test", + "cf-typegen": "wrangler types --env-interface CloudflareEnv" }, "dependencies": { "next": "catalog:", diff --git a/examples/api/tsconfig.json b/examples/api/tsconfig.json index 2f289147..5c536dfe 100644 --- a/examples/api/tsconfig.json +++ b/examples/api/tsconfig.json @@ -18,6 +18,6 @@ } ] }, - "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], + "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx", "worker-configuration.d.ts"], "exclude": ["node_modules"] } diff --git a/examples/api/worker-configuration.d.ts b/examples/api/worker-configuration.d.ts new file mode 100644 index 00000000..7775bd52 --- /dev/null +++ b/examples/api/worker-configuration.d.ts @@ -0,0 +1,5 @@ +// Generated by Wrangler by running `wrangler types --env-interface CloudflareEnv` + +interface CloudflareEnv { + hello: "Hello World from the cloudflare context!"; +} diff --git a/examples/api/wrangler.toml b/examples/api/wrangler.toml index 8c4a84ce..5a0b0de4 100644 --- a/examples/api/wrangler.toml +++ b/examples/api/wrangler.toml @@ -5,3 +5,6 @@ compatibility_date = "2024-09-16" compatibility_flags = ["nodejs_compat_v2"] experimental_assets = { directory = ".worker-next/assets", binding = "ASSETS" } + +[vars] +hello = 'Hello World from the cloudflare context!' diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json index a7c805f2..439af866 100644 --- a/packages/cloudflare/package.json +++ b/packages/cloudflare/package.json @@ -8,7 +8,15 @@ "test": "vitest --run", "test:watch": "vitest" }, - "bin": "dist/index.mjs", + "bin": "dist/cli/index.mjs", + "main": "./dist/api/index.mjs", + "types": "./dist/api/index.d.mts", + "exports": { + ".": { + "import": "./dist/api/index.mjs", + "types": "./dist/api/index.d.mts" + } + }, "files": [ "README.md", "dist" diff --git a/packages/cloudflare/src/api/get-cloudflare-context.ts b/packages/cloudflare/src/api/get-cloudflare-context.ts new file mode 100644 index 00000000..b9afec87 --- /dev/null +++ b/packages/cloudflare/src/api/get-cloudflare-context.ts @@ -0,0 +1,53 @@ +import "server-only"; + +declare global { + // eslint-disable-next-line @typescript-eslint/no-empty-interface + interface CloudflareEnv {} +} + +export type CloudflareContext< + CfProperties extends Record = IncomingRequestCfProperties, + Context = ExecutionContext, +> = { + /** + * the worker's [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) + */ + env: CloudflareEnv; + /** + * the request's [cf properties](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties) + */ + cf: CfProperties; + /** + * the current [execution context](https://developers.cloudflare.com/workers/runtime-apis/context) + */ + ctx: Context; +}; + +// Note: this symbol needs to be kept in sync with the one used in `src/cli/templates/worker.ts` +const cloudflareContextSymbol = Symbol.for("__cloudflare-context__"); + +/** + * Utility to get the current Cloudflare context + * + * Throws an error if the context could not be retrieved + * + * @returns the cloudflare context + */ +export async function getCloudflareContext< + CfProperties extends Record = IncomingRequestCfProperties, + Context = ExecutionContext, +>(): Promise> { + const cloudflareContext = ( + globalThis as unknown as { + [cloudflareContextSymbol]: CloudflareContext | undefined; + } + )[cloudflareContextSymbol]; + + if (!cloudflareContext) { + // TODO: cloudflareContext should always be present in production/preview, if not it means that this + // is running under `next dev`, in this case use `getPlatformProxy` to return local proxies + throw new Error("Cloudflare context is not defined!"); + } + + return cloudflareContext; +} diff --git a/packages/cloudflare/src/api/index.ts b/packages/cloudflare/src/api/index.ts new file mode 100644 index 00000000..120e08a0 --- /dev/null +++ b/packages/cloudflare/src/api/index.ts @@ -0,0 +1 @@ +export * from "./get-cloudflare-context"; diff --git a/packages/cloudflare/src/build/patches/investigated/copy-package.ts b/packages/cloudflare/src/build/patches/investigated/copy-package.ts deleted file mode 100644 index 5c499521..00000000 --- a/packages/cloudflare/src/build/patches/investigated/copy-package.ts +++ /dev/null @@ -1,11 +0,0 @@ -import path from "node:path"; -import { Config } from "../../../config"; -import { cpSync } from "node:fs"; - -/** - * Copy the builder package in the standalone node_modules folder. - */ -export function copyPackage(srcDir: string, config: Config) { - console.log("# copyPackage"); - cpSync(srcDir, config.paths.internalPackage, { recursive: true }); -} diff --git a/packages/cloudflare/src/args.ts b/packages/cloudflare/src/cli/args.ts similarity index 100% rename from packages/cloudflare/src/args.ts rename to packages/cloudflare/src/cli/args.ts diff --git a/packages/cloudflare/src/build/build-next-app.ts b/packages/cloudflare/src/cli/build/build-next-app.ts similarity index 100% rename from packages/cloudflare/src/build/build-next-app.ts rename to packages/cloudflare/src/cli/build/build-next-app.ts diff --git a/packages/cloudflare/src/build/build-worker.ts b/packages/cloudflare/src/cli/build/build-worker.ts similarity index 94% rename from packages/cloudflare/src/build/build-worker.ts rename to packages/cloudflare/src/cli/build/build-worker.ts index 3051feb8..7ba17032 100644 --- a/packages/cloudflare/src/build/build-worker.ts +++ b/packages/cloudflare/src/cli/build/build-worker.ts @@ -1,12 +1,12 @@ import { Config } from "../config"; import { build, Plugin } from "esbuild"; -import { existsSync, readFileSync, cpSync } from "node:fs"; +import { existsSync, readFileSync } from "node:fs"; import { cp, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { patchRequire } from "./patches/investigated/patch-require"; -import { copyPackage } from "./patches/investigated/copy-package"; +import { copyPackageCliFiles } from "./patches/investigated/copy-package-cli-files"; import { patchReadFile } from "./patches/to-investigate/patch-read-file"; import { patchFindDir } from "./patches/to-investigate/patch-find-dir"; @@ -16,8 +16,8 @@ import { patchWranglerDeps } from "./patches/to-investigate/wrangler-deps"; import { updateWebpackChunksFile } from "./patches/investigated/update-webpack-chunks-file"; import { patchCache } from "./patches/investigated/patch-cache"; -/** The directory containing the Cloudflare template files. */ -const packageDir = path.dirname(fileURLToPath(import.meta.url)); +/** The dist directory of the Cloudflare adapter package */ +const packageDistDir = path.join(path.dirname(fileURLToPath(import.meta.url)), ".."); /** * Using the Next.js build output in the `.next` directory builds a workerd compatible output @@ -45,9 +45,9 @@ export async function buildWorker(config: Config): Promise { }); } - copyPackage(packageDir, config); + copyPackageCliFiles(packageDistDir, config); - const templateDir = path.join(config.paths.internalPackage, "templates"); + const templateDir = path.join(config.paths.internalPackage, "cli", "templates"); const workerEntrypoint = path.join(templateDir, "worker.ts"); const workerOutputFile = path.join(config.paths.builderOutput, "index.mjs"); diff --git a/packages/cloudflare/src/build/build.ts b/packages/cloudflare/src/cli/build/index.ts similarity index 100% rename from packages/cloudflare/src/build/build.ts rename to packages/cloudflare/src/cli/build/index.ts diff --git a/packages/cloudflare/src/cli/build/patches/investigated/copy-package-cli-files.ts b/packages/cloudflare/src/cli/build/patches/investigated/copy-package-cli-files.ts new file mode 100644 index 00000000..544b8d41 --- /dev/null +++ b/packages/cloudflare/src/cli/build/patches/investigated/copy-package-cli-files.ts @@ -0,0 +1,14 @@ +import { Config } from "../../../config"; +import { cpSync } from "node:fs"; +import path from "node:path"; + +/** + * Copies the template files present in the cloudflare adapter package into the standalone node_modules folder + */ +export function copyPackageCliFiles(packageDistDir: string, config: Config) { + console.log("# copyPackageTemplateFiles"); + const sourceDir = path.join(packageDistDir, "cli"); + const destinationDir = path.join(config.paths.internalPackage, "cli"); + + cpSync(sourceDir, destinationDir, { recursive: true }); +} diff --git a/packages/cloudflare/src/build/patches/investigated/patch-cache.ts b/packages/cloudflare/src/cli/build/patches/investigated/patch-cache.ts similarity index 95% rename from packages/cloudflare/src/build/patches/investigated/patch-cache.ts rename to packages/cloudflare/src/cli/build/patches/investigated/patch-cache.ts index 9224d7b5..83e8cc4b 100644 --- a/packages/cloudflare/src/build/patches/investigated/patch-cache.ts +++ b/packages/cloudflare/src/cli/build/patches/investigated/patch-cache.ts @@ -7,7 +7,7 @@ import { Config } from "../../../config"; export function patchCache(code: string, config: Config): string { console.log("# patchCached"); - const cacheHandler = path.join(config.paths.internalPackage, "cache-handler.mjs"); + const cacheHandler = path.join(config.paths.internalPackage, "cli", "cache-handler.mjs"); const patchedCode = code.replace( "const { cacheHandler } = this.nextConfig;", diff --git a/packages/cloudflare/src/build/patches/investigated/patch-require.ts b/packages/cloudflare/src/cli/build/patches/investigated/patch-require.ts similarity index 100% rename from packages/cloudflare/src/build/patches/investigated/patch-require.ts rename to packages/cloudflare/src/cli/build/patches/investigated/patch-require.ts diff --git a/packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.test.ts b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.test.ts similarity index 100% rename from packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.test.ts rename to packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.test.ts diff --git a/packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.ts b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.ts similarity index 100% rename from packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.ts rename to packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/get-chunk-installation-identifiers.ts diff --git a/packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.test.ts b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.test.ts similarity index 100% rename from packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.test.ts rename to packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.test.ts diff --git a/packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.ts b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.ts similarity index 100% rename from packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.ts rename to packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/get-file-content-with-updated-webpack-f-require-code.ts diff --git a/packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.test.ts b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.test.ts similarity index 100% rename from packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.test.ts rename to packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.test.ts diff --git a/packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.ts b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.ts similarity index 100% rename from packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.ts rename to packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/get-updated-webpack-chunks-file-content.ts diff --git a/packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/index.ts b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/index.ts similarity index 96% rename from packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/index.ts rename to packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/index.ts index 3e2e1fc9..8f3ceaf4 100644 --- a/packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/index.ts +++ b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/index.ts @@ -1,7 +1,7 @@ import { readdirSync, readFileSync, writeFileSync } from "node:fs"; import path from "node:path"; -import { Config } from "../../../../config"; +import { Config } from "../../../../cli/config"; import { getUpdatedWebpackChunksFileContent } from "./get-updated-webpack-chunks-file-content"; /** diff --git a/packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/test-fixtures/minified-webpacks-file.js b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/test-fixtures/minified-webpacks-file.js similarity index 100% rename from packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/test-fixtures/minified-webpacks-file.js rename to packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/test-fixtures/minified-webpacks-file.js diff --git a/packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/test-fixtures/unminified-webpacks-file.js b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/test-fixtures/unminified-webpacks-file.js similarity index 100% rename from packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/test-fixtures/unminified-webpacks-file.js rename to packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/test-fixtures/unminified-webpacks-file.js diff --git a/packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/test-snapshots/minified-webpacks-file.js b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/test-snapshots/minified-webpacks-file.js similarity index 100% rename from packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/test-snapshots/minified-webpacks-file.js rename to packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/test-snapshots/minified-webpacks-file.js diff --git a/packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/test-snapshots/unminified-webpacks-file.js b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/test-snapshots/unminified-webpacks-file.js similarity index 100% rename from packages/cloudflare/src/build/patches/investigated/update-webpack-chunks-file/test-snapshots/unminified-webpacks-file.js rename to packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/test-snapshots/unminified-webpacks-file.js diff --git a/packages/cloudflare/src/build/patches/to-investigate/inline-eval-manifest.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-eval-manifest.ts similarity index 97% rename from packages/cloudflare/src/build/patches/to-investigate/inline-eval-manifest.ts rename to packages/cloudflare/src/cli/build/patches/to-investigate/inline-eval-manifest.ts index fd6b24e9..8565154b 100644 --- a/packages/cloudflare/src/build/patches/to-investigate/inline-eval-manifest.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-eval-manifest.ts @@ -1,6 +1,6 @@ import { globSync } from "glob"; import path from "node:path"; -import { Config } from "../../../config"; +import { Config } from "../../../cli/config"; /** * `evalManifest` relies on readFileSync so we need to patch the function so that it instead returns the content of the manifest files diff --git a/packages/cloudflare/src/build/patches/to-investigate/inline-next-require.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-next-require.ts similarity index 97% rename from packages/cloudflare/src/build/patches/to-investigate/inline-next-require.ts rename to packages/cloudflare/src/cli/build/patches/to-investigate/inline-next-require.ts index 025cfe5f..6cf56b79 100644 --- a/packages/cloudflare/src/build/patches/to-investigate/inline-next-require.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-next-require.ts @@ -1,5 +1,5 @@ import { readFileSync, existsSync } from "node:fs"; -import { Config } from "../../../config"; +import { Config } from "../../../cli/config"; import path from "node:path"; /** diff --git a/packages/cloudflare/src/build/patches/to-investigate/patch-find-dir.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-find-dir.ts similarity index 95% rename from packages/cloudflare/src/build/patches/to-investigate/patch-find-dir.ts rename to packages/cloudflare/src/cli/build/patches/to-investigate/patch-find-dir.ts index ee390892..86dd9dad 100644 --- a/packages/cloudflare/src/build/patches/to-investigate/patch-find-dir.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-find-dir.ts @@ -1,5 +1,5 @@ import path from "node:path"; -import { Config } from "../../../config"; +import { Config } from "../../../cli/config"; import { existsSync } from "node:fs"; /** diff --git a/packages/cloudflare/src/build/patches/to-investigate/patch-read-file.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-read-file.ts similarity index 97% rename from packages/cloudflare/src/build/patches/to-investigate/patch-read-file.ts rename to packages/cloudflare/src/cli/build/patches/to-investigate/patch-read-file.ts index e2e94eb2..bfc2618b 100644 --- a/packages/cloudflare/src/build/patches/to-investigate/patch-read-file.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-read-file.ts @@ -1,6 +1,6 @@ import { readFileSync } from "node:fs"; import { globSync } from "glob"; -import { Config } from "../../../config"; +import { Config } from "../../../cli/config"; import path from "node:path"; export function patchReadFile(code: string, config: Config): string { diff --git a/packages/cloudflare/src/build/patches/to-investigate/wrangler-deps.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/wrangler-deps.ts similarity index 97% rename from packages/cloudflare/src/build/patches/to-investigate/wrangler-deps.ts rename to packages/cloudflare/src/cli/build/patches/to-investigate/wrangler-deps.ts index cf37a29f..7e2d38a2 100644 --- a/packages/cloudflare/src/build/patches/to-investigate/wrangler-deps.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/wrangler-deps.ts @@ -1,6 +1,6 @@ import path from "node:path"; import fs, { writeFileSync } from "node:fs"; -import { Config } from "../../../config"; +import { Config } from "../../../cli/config"; export function patchWranglerDeps(config: Config) { console.log("# patchWranglerDeps"); diff --git a/packages/cloudflare/src/build/utils/index.ts b/packages/cloudflare/src/cli/build/utils/index.ts similarity index 100% rename from packages/cloudflare/src/build/utils/index.ts rename to packages/cloudflare/src/cli/build/utils/index.ts diff --git a/packages/cloudflare/src/build/utils/ts-parse-file.ts b/packages/cloudflare/src/cli/build/utils/ts-parse-file.ts similarity index 100% rename from packages/cloudflare/src/build/utils/ts-parse-file.ts rename to packages/cloudflare/src/cli/build/utils/ts-parse-file.ts diff --git a/packages/cloudflare/src/cache-handler.ts b/packages/cloudflare/src/cli/cache-handler.ts similarity index 100% rename from packages/cloudflare/src/cache-handler.ts rename to packages/cloudflare/src/cli/cache-handler.ts diff --git a/packages/cloudflare/src/config.ts b/packages/cloudflare/src/cli/config.ts similarity index 100% rename from packages/cloudflare/src/config.ts rename to packages/cloudflare/src/cli/config.ts diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/cli/index.ts similarity index 93% rename from packages/cloudflare/src/index.ts rename to packages/cloudflare/src/cli/index.ts index b5f7c295..ec96fcf7 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/cli/index.ts @@ -2,7 +2,7 @@ import { resolve } from "node:path"; import { getArgs } from "./args"; import { existsSync } from "node:fs"; -import { build } from "./build/build"; +import { build } from "./build"; const nextAppDir = resolve("."); diff --git a/packages/cloudflare/src/templates/shims/empty.ts b/packages/cloudflare/src/cli/templates/shims/empty.ts similarity index 100% rename from packages/cloudflare/src/templates/shims/empty.ts rename to packages/cloudflare/src/cli/templates/shims/empty.ts diff --git a/packages/cloudflare/src/templates/shims/env.ts b/packages/cloudflare/src/cli/templates/shims/env.ts similarity index 100% rename from packages/cloudflare/src/templates/shims/env.ts rename to packages/cloudflare/src/cli/templates/shims/env.ts diff --git a/packages/cloudflare/src/templates/shims/node-fs.ts b/packages/cloudflare/src/cli/templates/shims/node-fs.ts similarity index 100% rename from packages/cloudflare/src/templates/shims/node-fs.ts rename to packages/cloudflare/src/cli/templates/shims/node-fs.ts diff --git a/packages/cloudflare/src/templates/shims/throw.ts b/packages/cloudflare/src/cli/templates/shims/throw.ts similarity index 100% rename from packages/cloudflare/src/templates/shims/throw.ts rename to packages/cloudflare/src/cli/templates/shims/throw.ts diff --git a/packages/cloudflare/src/templates/worker.ts b/packages/cloudflare/src/cli/templates/worker.ts similarity index 62% rename from packages/cloudflare/src/templates/worker.ts rename to packages/cloudflare/src/cli/templates/worker.ts index 6b362cf3..ebc2be0c 100644 --- a/packages/cloudflare/src/templates/worker.ts +++ b/packages/cloudflare/src/cli/templates/worker.ts @@ -1,46 +1,64 @@ +import { AsyncLocalStorage } from "node:async_hooks"; import Stream from "node:stream"; import type { NextConfig } from "next"; import { NodeNextRequest, NodeNextResponse } from "next/dist/server/base-http/node"; import { MockedResponse } from "next/dist/server/lib/mock-request"; import NextNodeServer, { NodeRequestHandler } from "next/dist/server/next-server"; import type { IncomingMessage } from "node:http"; +import { type CloudflareContext } from "../../api"; const NON_BODY_RESPONSES = new Set([101, 204, 205, 304]); +const cloudflareContextALS = new AsyncLocalStorage(); + +// Note: this symbol needs to be kept in sync with the one defined in `src/api/get-cloudflare-context.ts` +(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), + } +); + // Injected at build time const nextConfig: NextConfig = JSON.parse(process.env.__NEXT_PRIVATE_STANDALONE_CONFIG ?? "{}"); let requestHandler: NodeRequestHandler | null = null; export default { - async fetch(request: Request, env: any, ctx: any) { - if (requestHandler == null) { - globalThis.process.env = { ...globalThis.process.env, ...env }; - requestHandler = new NextNodeServer({ - conf: { ...nextConfig, env }, - customServer: false, - dev: false, - dir: "", - minimalMode: false, - }).getRequestHandler(); - } - - const url = new URL(request.url); - - if (url.pathname === "/_next/image") { - let 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)); + async fetch(request: Request & { cf: IncomingRequestCfProperties }, env: any, ctx: any) { + return cloudflareContextALS.run({ env, ctx, cf: request.cf }, async () => { + if (requestHandler == null) { + globalThis.process.env = { ...globalThis.process.env, ...env }; + requestHandler = new NextNodeServer({ + conf: { ...nextConfig, env }, + customServer: false, + dev: false, + dir: "", + minimalMode: false, + }).getRequestHandler(); + } + + const url = new URL(request.url); + + if (url.pathname === "/_next/image") { + let 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 } } as any); } - return fetch(imageUrl, { cf: { cacheEverything: true } } as any); - } - const { req, res, webResponse } = getWrappedStreams(request, ctx); + const { req, res, webResponse } = getWrappedStreams(request, ctx); - ctx.waitUntil(requestHandler(new NodeNextRequest(req), new NodeNextResponse(res))); + ctx.waitUntil(requestHandler(new NodeNextRequest(req), new NodeNextResponse(res))); - return await webResponse(); + return await webResponse(); + }); }, }; diff --git a/packages/cloudflare/tsup.config.ts b/packages/cloudflare/tsup.config.ts index cd15be8d..367faabe 100644 --- a/packages/cloudflare/tsup.config.ts +++ b/packages/cloudflare/tsup.config.ts @@ -1,16 +1,27 @@ import { cp } from "fs/promises"; import { defineConfig } from "tsup"; -export default defineConfig({ - entry: ["src/index.ts", "src/cache-handler.ts"], - outDir: "dist", +const cliConfig = defineConfig({ + entry: ["src/cli/index.ts", "src/cli/cache-handler.ts"], + outDir: "dist/cli", dts: false, format: ["esm"], platform: "node", external: ["esbuild"], onSuccess: async () => { - await cp(`${__dirname}/src/templates`, `${__dirname}/dist/templates`, { + await cp(`${__dirname}/src/cli/templates`, `${__dirname}/dist/cli/templates`, { recursive: true, }); }, }); + +const apiConfig = defineConfig({ + entry: ["src/api"], + outDir: "dist/api", + dts: true, + format: ["esm"], + platform: "node", + external: ["server-only"], +}); + +export default [cliConfig, apiConfig];