1/**
2 * `@alchemy.run/frontend-frameworks/nuxt/cloudflare` — the Cloudflare Workers deploy target
3 * for `@alchemy.run/frontend-frameworks/nuxt`.
4 *
5 * Nuxt 4's Cloudflare story is nitro's `cloudflare_module` preset: the
6 * `.output` it produces is already self-contained workerd ESM
7 * (`.output/server/index.mjs` + chunks, `.output/public` static assets with
8 * prerendered pages and `_headers` merged in), so this target needs no
9 * finishing pass. What it owns:
10 *
11 * - **`nitroPreset`** — `"cloudflare_module"`, enforced as the last word on
12 * the resolved nitro config.
13 * - **`configureNitro`** — wrangler-free doctrine: `cloudflare.deployConfig:
14 * false` (nitro must never write a `wrangler.json` into user projects) and
15 * `cloudflare.nodeCompat: true` by default (without a wrangler config on
16 * disk nitro would otherwise skip its hybrid workerd node-compat; the
17 * deployed worker enables the `nodejs_compat` flag to match). Also wires
18 * the user-entry seam: a configured `main` becomes nitro's `entry`.
19 * - **`entry`** — surfaces `config.main` as the generic user-entry carriage.
20 * Nitro's entry module is the exports seam: the emitted worker exports are
21 * exactly the entry module's exports, so a user entry that re-exports
22 * nitro's runtime handler (`nitropack/presets/cloudflare/runtime/cloudflare-module`)
23 * and adds its own classes gets Durable Objects et al. onto the same
24 * worker.
25 * - **`bundle`** — workerd resolve conditions and the `cloudflare:`
26 * externals, for callers that post-process server code.
27 * - **`devPlatform`** — the wrangler-free dev bridge: opens
28 * cloudflare-runtime's platform proxy (a local workerd instance hosting
29 * the configured binding hooks) and injects a nitro plugin that serves
30 * `event.context.cf` / `event.context.waitUntil` /
31 * `event.context.cloudflare = { request, env, context }` inside nitro's
32 * dev SSR worker thread (see `./dev/host.ts`).
33 */
34import { makeDeployTarget } from "../core/index.ts";
35import { makeCloudflareDevPlatform } from "./dev/host.ts";
36import type { NuxtTarget, NuxtTargetConfig } from "./Nuxt.ts";
37
38/** The nitro deployment preset this target builds with. */
39export const NITRO_PRESET = "cloudflare_module";
40
41/**
42 * The importable specifier of nitro's cloudflare-module runtime handler — the
43 * module a USER worker entry re-exports to wrap the framework's fetch
44 * handler (the "wrappable handler" half of the user-entry seam):
45 *
46 * ```ts
47 * // worker-entry.mjs
48 * import nitroHandler from "nitropack/presets/cloudflare/runtime/cloudflare-module";
49 * export { MyDurableObject } from "./durable-object.mjs";
50 * export default nitroHandler;
51 * ```
52 */
53export const NITRO_HANDLER_SPECIFIER =
54 "nitropack/presets/cloudflare/runtime/cloudflare-module";
55
56/**
57 * Create the Cloudflare Workers {@link NuxtTarget}. See the module doc for
58 * the seams. The config's `compatibilityDate`/`compatibilityFlags` are
59 * carried for serve/deploy consumers; the build itself only needs
60 * `nodeCompat` (default `true`) and the optional user `main`.
61 */
62export const makeCloudflareTarget = (
63 config: NuxtTargetConfig = {},
64): NuxtTarget =>
65 makeDeployTarget({
66 platform: "cloudflare",
67 config,
68 bundle: {
69 conditions: ["workerd", "worker", "module", "browser"],
70 external: ["cloudflare:"],
71 },
72 entry: config.main !== undefined ? { main: config.main } : undefined,
73 nitroPreset: NITRO_PRESET,
74 configureNitro: (nitroConfig, _context) => {
75 const cloudflare =
76 nitroConfig.cloudflare !== null &&
77 typeof nitroConfig.cloudflare === "object"
78 ? nitroConfig.cloudflare
79 : {};
80 nitroConfig.cloudflare = {
81 ...cloudflare,
82 // Wrangler-free: never write a wrangler.json into the user project.
83 deployConfig: false,
84 // Without a wrangler config on disk, nitro only enables its hybrid
85 // workerd node-compat when told to. The deployed worker enables the
86 // `nodejs_compat` compatibility flag to match.
87 nodeCompat: config.nodeCompat ?? true,
88 };
89 // NOTE: the user entry (context.entry) is deliberately NOT wired here.
90 // The framework package sets it on the nitro INSTANCE at `nitro:init`:
91 // a config-level entry would leak into the prerenderer's Node-preset
92 // clone (it copies `options._config`) and a workerd-flavored entry
93 // (importing `cloudflare:` modules) cannot load there.
94 },
95 // Dev: `event.context.cloudflare` served through cloudflare-runtime's
96 // platform proxy (workerd hosting the binding hooks), bridged into
97 // nitro's dev SSR worker thread by the shipped nitro plugin. Note the
98 // proxy cannot host user worker-entry classes (Durable Objects declared
99 // via the entry/exports seam only exist in the production build), so
100 // DO namespace bindings are not servable in dev yet.
101 devPlatform: makeCloudflareDevPlatform({
102 compatibilityDate: config.compatibilityDate,
103 compatibilityFlags: config.compatibilityFlags,
104 }),
105 });
106
107/**
108 * The deploy-target module contract (`resolveDeployTarget` accepts the
109 * default export — or the named `target` export — as a value or factory).
110 */
111export const target = makeCloudflareTarget;
112
113export default makeCloudflareTarget;