@vinext/cloudflare 1.0.0-beta.10 → 1.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,10 +43,9 @@ export default defineConfig({
43
43
  `cdnAdapter()` is optional. Configuring it asks the Cloudflare build for two
44
44
  Worker entrypoints: the default entrypoint runs middleware and request-time
45
45
  routing with caching disabled, while `VinextCachedResponse` lazily loads the
46
- render stage with Workers Cache enabled. These settings are written to the
47
- generated `dist/server/wrangler.json`; do not enable Workers Cache on the
48
- default entrypoint in your source config. The generated config also declares
49
- the version metadata binding used for staged warmup.
46
+ render stage with Workers Cache enabled. Legacy Cloudflare Vite plugin builds
47
+ write these settings and the version metadata binding to the generated
48
+ `dist/server/wrangler.json`.
50
49
 
51
50
  ```ts
52
51
  import { cdnAdapter } from "@vinext/cloudflare/cache/cdn-adapter";
@@ -54,10 +53,30 @@ import { cdnAdapter } from "@vinext/cloudflare/cache/cdn-adapter";
54
53
  vinext({ cache: { cdn: cdnAdapter() } });
55
54
  ```
56
55
 
56
+ Cloudflare Vite plugin v2 uses Build Output and treats `cloudflare.config.ts`
57
+ as the deployment source of truth. Declare the equivalent policies there:
58
+
59
+ ```ts
60
+ import { defineWorker } from "@cloudflare/vite-plugin/experimental-config";
61
+ import { createWorkersCacheConfig } from "@vinext/cloudflare/cache/config";
62
+
63
+ const workersCache = await createWorkersCacheConfig();
64
+
65
+ export default defineWorker({
66
+ // ...
67
+ ...workersCache,
68
+ env: {
69
+ ...workersCache.env,
70
+ // Other application bindings...
71
+ },
72
+ });
73
+ ```
74
+
57
75
  The generated version metadata binding lets staged warmup prove that every
58
76
  discovery, probe, and fill request reached the uploaded Worker version. Pass
59
- `versionMetadataBinding` to `cdnAdapter()` only when the deployment needs a
60
- custom binding name.
77
+ the same `versionMetadataBinding` to `cdnAdapter()` and
78
+ `createWorkersCacheConfig()` only when the deployment needs a custom binding
79
+ name.
61
80
 
62
81
  Use `--experimental-warm-cdn-cache` for the two-stage deploy. The default flow
63
82
  makes one final fill request per admitted identity. Add `--warm-cdn-certify`
@@ -91,6 +110,61 @@ npx @vinext/cloudflare deploy
91
110
  `vinext-cloudflare deploy` never creates, rewrites, or deploys the Response
92
111
  Store Worker.
93
112
 
113
+ Cloudflare Vite plugin v2 projects can define the same service-binding setup in
114
+ `cloudflare.config.ts` without a second Wrangler config:
115
+
116
+ ```ts
117
+ import { defineConfig, defineWorker } from "@cloudflare/vite-plugin/experimental-config";
118
+ import { createWorkersResponseStoreServiceBindingConfig } from "@vinext/cloudflare/cache/config";
119
+
120
+ const responseStore = await createWorkersResponseStoreServiceBindingConfig({
121
+ accountId: "<your-account-id>",
122
+ worker: {
123
+ name: "example-response-store",
124
+ compatibilityDate: "2026-09-15",
125
+ compatibilityFlags: ["nodejs_compat"],
126
+ observability: {
127
+ enabled: true,
128
+ headSamplingRate: 1,
129
+ logs: { enabled: true, invocationLogs: true },
130
+ traces: { enabled: true, headSamplingRate: 0.1 },
131
+ },
132
+ },
133
+ bucket: "example-response-store-cache-bodies",
134
+ });
135
+
136
+ export const responseStoreServiceBinding = responseStore.serviceBindingWorker;
137
+
138
+ export default defineConfig({
139
+ accountId: responseStore.accountId,
140
+ worker: defineWorker({
141
+ ...responseStore.applicationWorker,
142
+ name: "example",
143
+ entrypoint: "./worker.ts",
144
+ }),
145
+ });
146
+ ```
147
+
148
+ `worker.observability` accepts the full observability configuration supported by
149
+ your installed Cloudflare Vite plugin, including logs and traces settings.
150
+ The optional `accountId` is returned at the top level: pass it to `defineConfig`,
151
+ not to either Worker. It applies to both Workers in the project.
152
+
153
+ The async config helpers import bindings and exports from your installed
154
+ `@cloudflare/vite-plugin/experimental-config`. The plugin is an optional peer
155
+ dependency of `@vinext/cloudflare`; these helpers require v2, but Wrangler-only
156
+ projects do not need it.
157
+
158
+ Register the exported auxiliary Worker with the Cloudflare Vite plugin so it
159
+ is built alongside the application:
160
+
161
+ ```ts
162
+ import { cloudflare } from "@cloudflare/vite-plugin";
163
+ import { responseStoreServiceBinding } from "./cloudflare.config.ts";
164
+
165
+ cloudflare({ auxiliaryWorkers: [{ config: responseStoreServiceBinding }] });
166
+ ```
167
+
94
168
  Metadata sharding is opt-in and works in either deployment mode:
95
169
 
96
170
  ```ts
@@ -121,6 +195,9 @@ import { responseStoreAdapter } from "@vinext/cloudflare/cache/response-store-ad
121
195
  vinext({ cache: responseStoreAdapter({ mode: "self-contained" }) });
122
196
  ```
123
197
 
198
+ The corresponding typed config helper is
199
+ `createWorkersResponseStoreSelfContainedConfig`.
200
+
124
201
  In this mode `vinext init` places the required R2, SQLite Durable Object,
125
202
  Workers Cache entrypoint, and version-metadata configuration in
126
203
  `wrangler.jsonc`; no second Wrangler config is required.
@@ -133,5 +210,27 @@ Deploy Cloudflare Workers projects with the package CLI:
133
210
  npx @vinext/cloudflare deploy
134
211
  ```
135
212
 
213
+ Projects with `cloudflare.config.ts` opt into the experimental Cloudflare Vite
214
+ plugin v2 path and deploy their generated Build Output with `cf`. Existing
215
+ Wrangler-configured projects continue to use Wrangler. A normal typed-config
216
+ deploy needs neither `wrangler.jsonc` nor the Wrangler package.
217
+
218
+ `vinext-cloudflare deploy` only deploys the entry Worker. It never deploys named
219
+ auxiliary Workers, including during staged warming or `--no-promote` uploads.
220
+ Deploy a configured Response Store Worker explicitly after building, when first
221
+ setting it up or when its code/config changes:
222
+
223
+ ```sh
224
+ cf deploy --prebuilt --mode production --worker example-response-store
225
+ ```
226
+
227
+ Use the mode and Worker name from your build. `cloudflare.config.ts` remains the
228
+ source of truth; no auxiliary Wrangler config is required.
229
+
230
+ Experimental staged CDN warming uses `cf` to upload a version, read deployment
231
+ status, stage and promote traffic, and apply triggers. Typed-config projects do
232
+ not need an equivalent Wrangler config. Existing Wrangler-configured projects
233
+ continue to use Wrangler for this flow.
234
+
136
235
  With Vite+, use `vpx @vinext/cloudflare deploy`, or
137
236
  `vp exec vinext-cloudflare deploy` when running the locally installed bin.
@@ -21,7 +21,8 @@ export declare function configureCdnVersionMetadata(config: WranglerOutputConfig
21
21
  /** Add the per-entrypoint Workers Cache policy to an emitted Wrangler config. */
22
22
  export declare function configureWorkersCacheEntrypoints(config: WranglerOutputConfig): WranglerOutputConfig;
23
23
  /** Apply the complete CDN adapter policy to the primary generated config. */
24
- export declare function finalizeCdnAdapterBuildOutput({ outDir, isPrimaryServerOutput, binding, bindingIsExplicit }: {
24
+ export declare function finalizeCdnAdapterBuildOutput({ root, outDir, isPrimaryServerOutput, binding, bindingIsExplicit }: {
25
+ root: string;
25
26
  outDir: string;
26
27
  isPrimaryServerOutput: boolean;
27
28
  binding: string;
@@ -72,11 +72,19 @@ async function readGeneratedConfig(generatedConfigPath, allowMissing) {
72
72
  async function writeGeneratedConfig(generatedConfigPath, config) {
73
73
  await fs.writeFile(generatedConfigPath, `${JSON.stringify(config, null, 2)}\n`);
74
74
  }
75
+ async function usesCloudflareBuildOutput(root) {
76
+ try {
77
+ return (await fs.stat(path.resolve(root, "cloudflare.config.ts"))).isFile();
78
+ } catch (cause) {
79
+ if (cause !== null && typeof cause === "object" && "code" in cause && cause.code === "ENOENT") return false;
80
+ throw cause;
81
+ }
82
+ }
75
83
  /** Apply the complete CDN adapter policy to the primary generated config. */
76
- async function finalizeCdnAdapterBuildOutput({ outDir, isPrimaryServerOutput, binding, bindingIsExplicit }) {
84
+ async function finalizeCdnAdapterBuildOutput({ root, outDir, isPrimaryServerOutput, binding, bindingIsExplicit }) {
77
85
  if (!isPrimaryServerOutput) return;
78
86
  const generatedConfigPath = path.resolve(outDir, "wrangler.json");
79
- const generatedConfig = await readGeneratedConfig(generatedConfigPath, false);
87
+ const generatedConfig = await readGeneratedConfig(generatedConfigPath, await usesCloudflareBuildOutput(root));
80
88
  if (!generatedConfig) return;
81
89
  await writeGeneratedConfig(generatedConfigPath, configureWorkersCacheEntrypoints(configureCdnVersionMetadata(generatedConfig, {
82
90
  binding,
@@ -15,12 +15,12 @@ export type CdnAdapterOptions = {
15
15
  * request-time routing before dispatching to cached or uncached response-stage
16
16
  * entrypoints.
17
17
  *
18
- * The emitted Wrangler configuration enables Workers Cache only for that
19
- * response-stage export, so cache hits do not start the application stage.
20
- * The generated deployment config automatically enables Workers Cache for the
21
- * cached response entrypoint and adds the version metadata binding used by
22
- * warmup. The uncached response entrypoint keeps bypass and probe renders out
23
- * of the gateway without enabling Workers Cache for them.
18
+ * The deployment configuration enables Workers Cache only for that response-stage
19
+ * export, so cache hits do not start the application stage. Legacy Cloudflare
20
+ * builds receive this policy in their generated Wrangler config. Cloudflare Vite
21
+ * plugin v2 builds declare the same entrypoint policy in `cloudflare.config.ts`.
22
+ * The uncached response entrypoint keeps bypass and probe renders out of the
23
+ * gateway without enabling Workers Cache for them.
24
24
  *
25
25
  * The adapter adds a transport-only URL digest so distinct response-stage
26
26
  * identities cannot collide. Workers Cache owns this key independently of
@@ -40,7 +40,8 @@ export declare function cdnAdapter(options?: CdnAdapterOptions): {
40
40
  code: string;
41
41
  id: string;
42
42
  }): string | null;
43
- finalizeBuildOutput({ outDir, isPrimaryServerOutput }: {
43
+ finalizeBuildOutput({ root, outDir, isPrimaryServerOutput }: {
44
+ root: string;
44
45
  outDir: string;
45
46
  isPrimaryServerOutput: boolean;
46
47
  }): Promise<void>;
@@ -13,12 +13,12 @@ const CLOUDFLARE_WORKER_ENTRY_ID = "virtual:cloudflare/worker-entry";
13
13
  * request-time routing before dispatching to cached or uncached response-stage
14
14
  * entrypoints.
15
15
  *
16
- * The emitted Wrangler configuration enables Workers Cache only for that
17
- * response-stage export, so cache hits do not start the application stage.
18
- * The generated deployment config automatically enables Workers Cache for the
19
- * cached response entrypoint and adds the version metadata binding used by
20
- * warmup. The uncached response entrypoint keeps bypass and probe renders out
21
- * of the gateway without enabling Workers Cache for them.
16
+ * The deployment configuration enables Workers Cache only for that response-stage
17
+ * export, so cache hits do not start the application stage. Legacy Cloudflare
18
+ * builds receive this policy in their generated Wrangler config. Cloudflare Vite
19
+ * plugin v2 builds declare the same entrypoint policy in `cloudflare.config.ts`.
20
+ * The uncached response entrypoint keeps bypass and probe renders out of the
21
+ * gateway without enabling Workers Cache for them.
22
22
  *
23
23
  * The adapter adds a transport-only URL digest so distinct response-stage
24
24
  * identities cannot collide. Workers Cache owns this key independently of
@@ -41,8 +41,9 @@ function cdnAdapter(options) {
41
41
  if ((id.charCodeAt(0) === 0 ? id.slice(1) : id) !== CLOUDFLARE_WORKER_ENTRY_ID) return null;
42
42
  return `${code}\nexport { VinextCachedResponse, VinextUncachedResponse } from ${JSON.stringify(workerEntry)};\n`;
43
43
  },
44
- finalizeBuildOutput({ outDir, isPrimaryServerOutput }) {
44
+ finalizeBuildOutput({ root, outDir, isPrimaryServerOutput }) {
45
45
  return finalizeCdnAdapterBuildOutput({
46
+ root,
46
47
  outDir,
47
48
  isPrimaryServerOutput,
48
49
  binding: versionMetadataBinding,
@@ -1,6 +1,6 @@
1
1
  //#region src/cache/cdn-build-id.d.ts
2
2
  /** Build identity stamped by the Cloudflare CDN adapter on page responses. */
3
3
  export declare const VINEXT_CDN_BUILD_ID_HEADER = "X-Vinext-Build-Id";
4
- /** Opaque identity shared by every server entry emitted by one vinext build. */
4
+ /** Opaque identity shared by every server entry emitted by one vite build. */
5
5
  export declare function getVinextCdnBuildIdentity(): string | null;
6
6
  //#endregion
@@ -1,7 +1,7 @@
1
1
  //#region src/cache/cdn-build-id.ts
2
2
  /** Build identity stamped by the Cloudflare CDN adapter on page responses. */
3
3
  const VINEXT_CDN_BUILD_ID_HEADER = "X-Vinext-Build-Id";
4
- /** Opaque identity shared by every server entry emitted by one vinext build. */
4
+ /** Opaque identity shared by every server entry emitted by one vite build. */
5
5
  function getVinextCdnBuildIdentity() {
6
6
  return process.env.__VINEXT_RSC_BUILD_IDENTITY || process.env.__VINEXT_BUILD_ID || null;
7
7
  }
@@ -0,0 +1,137 @@
1
+ import { CloudflareConfig, WorkerConfig } from "@cloudflare/vite-plugin/experimental-config";
2
+ //#region src/cache/config.d.ts
3
+ type ResponseStoreStorageOptions = {
4
+ worker: string;
5
+ bucket: string;
6
+ };
7
+ export declare function createWorkersResponseStoreSelfContainedConfig(options: ResponseStoreStorageOptions): Promise<{
8
+ cache: {
9
+ enabled: true;
10
+ };
11
+ exports: {
12
+ CacheMetadata: {
13
+ state?: "created";
14
+ type: "durable-object";
15
+ storage: "sqlite";
16
+ container: undefined;
17
+ } | {
18
+ state?: "created";
19
+ type: "durable-object";
20
+ storage: "sqlite";
21
+ container: undefined;
22
+ };
23
+ ResponseStoreBinding: import("@cloudflare/vite-plugin/experimental-config").WorkerEntrypointExport;
24
+ };
25
+ env: {
26
+ CACHE_BODIES: import("@cloudflare/vite-plugin/experimental-config").R2Binding;
27
+ CACHE_METADATA: {
28
+ type: "durable-object";
29
+ worker: string;
30
+ exportName: string;
31
+ };
32
+ CF_VERSION_METADATA: import("@cloudflare/vite-plugin/experimental-config").VersionMetadataBinding;
33
+ };
34
+ }>;
35
+ export declare function createWorkersResponseStoreServiceBindingConfig(options: {
36
+ accountId?: CloudflareConfig["accountId"];
37
+ worker: {
38
+ name: string;
39
+ compatibilityDate: string;
40
+ compatibilityFlags?: string[];
41
+ observability?: WorkerConfig["observability"];
42
+ };
43
+ bucket: string;
44
+ }): Promise<{
45
+ accountId: string | undefined;
46
+ serviceBindingWorker: {
47
+ cache: {
48
+ enabled: true;
49
+ };
50
+ exports: {
51
+ CacheMetadata: {
52
+ state?: "created";
53
+ type: "durable-object";
54
+ storage: "sqlite";
55
+ container: undefined;
56
+ } | {
57
+ state?: "created";
58
+ type: "durable-object";
59
+ storage: "sqlite";
60
+ container: undefined;
61
+ };
62
+ ResponseStoreBinding: import("@cloudflare/vite-plugin/experimental-config").WorkerEntrypointExport;
63
+ };
64
+ env: {
65
+ CACHE_BODIES: import("@cloudflare/vite-plugin/experimental-config").R2Binding;
66
+ CACHE_METADATA: {
67
+ type: "durable-object";
68
+ worker: string;
69
+ exportName: string;
70
+ };
71
+ };
72
+ name: string;
73
+ compatibilityDate: string;
74
+ compatibilityFlags?: string[];
75
+ observability?: WorkerConfig["observability"];
76
+ entrypoint: string;
77
+ workersDev: false;
78
+ previewUrls: false;
79
+ };
80
+ applicationWorker: {
81
+ cache: {
82
+ enabled: false;
83
+ };
84
+ env: {
85
+ RESPONSE_STORE: import("@cloudflare/vite-plugin/experimental-config").WorkerBinding<{
86
+ cache: {
87
+ enabled: true;
88
+ };
89
+ exports: {
90
+ CacheMetadata: {
91
+ state?: "created";
92
+ type: "durable-object";
93
+ storage: "sqlite";
94
+ container: undefined;
95
+ } | {
96
+ state?: "created";
97
+ type: "durable-object";
98
+ storage: "sqlite";
99
+ container: undefined;
100
+ };
101
+ ResponseStoreBinding: import("@cloudflare/vite-plugin/experimental-config").WorkerEntrypointExport;
102
+ };
103
+ env: {
104
+ CACHE_BODIES: import("@cloudflare/vite-plugin/experimental-config").R2Binding;
105
+ CACHE_METADATA: {
106
+ type: "durable-object";
107
+ worker: string;
108
+ exportName: string;
109
+ };
110
+ };
111
+ name: string;
112
+ compatibilityDate: string;
113
+ compatibilityFlags?: string[];
114
+ observability?: WorkerConfig["observability"];
115
+ entrypoint: string;
116
+ workersDev: false;
117
+ previewUrls: false;
118
+ }, "ResponseStoreService">;
119
+ CF_VERSION_METADATA: import("@cloudflare/vite-plugin/experimental-config").VersionMetadataBinding;
120
+ };
121
+ };
122
+ }>;
123
+ export declare function createWorkersCacheConfig({ versionMetadataBinding }?: {
124
+ versionMetadataBinding?: string;
125
+ }): Promise<{
126
+ cache: {
127
+ enabled: false;
128
+ };
129
+ env: {
130
+ [versionMetadataBinding]: import("@cloudflare/vite-plugin/experimental-config").VersionMetadataBinding;
131
+ };
132
+ exports: {
133
+ VinextCachedResponse: import("@cloudflare/vite-plugin/experimental-config").WorkerEntrypointExport;
134
+ VinextUncachedResponse: import("@cloudflare/vite-plugin/experimental-config").WorkerEntrypointExport;
135
+ };
136
+ }>;
137
+ //#endregion
@@ -0,0 +1,87 @@
1
+ //#region src/cache/config.ts
2
+ const RESPONSE_STORE_BINDING = "RESPONSE_STORE";
3
+ const RESPONSE_STORE_ENTRYPOINT = "ResponseStoreService";
4
+ const DEFAULT_VERSION_METADATA_BINDING = "CF_VERSION_METADATA";
5
+ const CACHE_BODIES_BINDING = "CACHE_BODIES";
6
+ const CACHE_METADATA_BINDING = "CACHE_METADATA";
7
+ const CACHE_METADATA_CLASS = "CacheMetadata";
8
+ const RESPONSE_STORE_CACHE_ENTRYPOINT = "ResponseStoreBinding";
9
+ const RESPONSE_STORE_SERVICE_ENTRYPOINT = "@cloudflare/workers-response-store/service";
10
+ async function createResponseStoreWorkerConfig(options) {
11
+ const { bindings, exports } = await import("@cloudflare/vite-plugin/experimental-config");
12
+ return {
13
+ cache: { enabled: true },
14
+ exports: {
15
+ [CACHE_METADATA_CLASS]: {
16
+ ...exports.durableObject({ storage: "sqlite" }),
17
+ storage: "sqlite",
18
+ container: void 0
19
+ },
20
+ [RESPONSE_STORE_CACHE_ENTRYPOINT]: exports.worker({ cache: { enabled: true } })
21
+ },
22
+ env: {
23
+ [CACHE_BODIES_BINDING]: bindings.r2({ name: options.bucket }),
24
+ [CACHE_METADATA_BINDING]: {
25
+ ...bindings.durableObject({
26
+ worker: options.worker,
27
+ exportName: CACHE_METADATA_CLASS
28
+ }),
29
+ worker: options.worker,
30
+ exportName: CACHE_METADATA_CLASS
31
+ }
32
+ }
33
+ };
34
+ }
35
+ async function createWorkersResponseStoreSelfContainedConfig(options) {
36
+ const { bindings } = await import("@cloudflare/vite-plugin/experimental-config");
37
+ const storage = await createResponseStoreWorkerConfig(options);
38
+ return {
39
+ ...storage,
40
+ env: {
41
+ ...storage.env,
42
+ [DEFAULT_VERSION_METADATA_BINDING]: bindings.versionMetadata()
43
+ }
44
+ };
45
+ }
46
+ async function createWorkersResponseStoreServiceBindingConfig(options) {
47
+ const { bindings } = await import("@cloudflare/vite-plugin/experimental-config");
48
+ const storage = await createResponseStoreWorkerConfig({
49
+ worker: options.worker.name,
50
+ bucket: options.bucket
51
+ });
52
+ const serviceBindingWorker = {
53
+ ...options.worker,
54
+ entrypoint: RESPONSE_STORE_SERVICE_ENTRYPOINT,
55
+ workersDev: false,
56
+ previewUrls: false,
57
+ ...storage
58
+ };
59
+ return {
60
+ accountId: options.accountId,
61
+ serviceBindingWorker,
62
+ applicationWorker: {
63
+ cache: { enabled: false },
64
+ env: {
65
+ [RESPONSE_STORE_BINDING]: bindings.worker({
66
+ worker: serviceBindingWorker,
67
+ exportName: RESPONSE_STORE_ENTRYPOINT
68
+ }),
69
+ [DEFAULT_VERSION_METADATA_BINDING]: bindings.versionMetadata()
70
+ }
71
+ }
72
+ };
73
+ }
74
+ async function createWorkersCacheConfig({ versionMetadataBinding = DEFAULT_VERSION_METADATA_BINDING } = {}) {
75
+ if (versionMetadataBinding.length === 0) throw new TypeError("versionMetadataBinding must be a non-empty string");
76
+ const { bindings, exports } = await import("@cloudflare/vite-plugin/experimental-config");
77
+ return {
78
+ cache: { enabled: false },
79
+ env: { [versionMetadataBinding]: bindings.versionMetadata() },
80
+ exports: {
81
+ VinextCachedResponse: exports.worker({ cache: { enabled: true } }),
82
+ VinextUncachedResponse: exports.worker({ cache: { enabled: false } })
83
+ }
84
+ };
85
+ }
86
+ //#endregion
87
+ export { createWorkersCacheConfig, createWorkersResponseStoreSelfContainedConfig, createWorkersResponseStoreServiceBindingConfig };
@@ -1,9 +1,10 @@
1
1
  import { CacheabilityManifest } from "vinext/internal/server/cacheability-manifest";
2
2
  //#region src/cacheability-artifact.d.ts
3
+ export type CloudflareDeploymentTool = "cf" | "wrangler";
3
4
  /**
4
5
  * Write the version-specific manifest into the built Worker artifact.
5
6
  * The application build already imports this stable module asset, so the
6
7
  * completed dist directory remains the exact input to the final upload.
7
8
  */
8
- export declare function writeCacheabilityManifestArtifact(root: string, configuredPath: string | undefined, manifest: CacheabilityManifest): string;
9
+ export declare function writeCacheabilityManifestArtifact(root: string, configuredPath: string | undefined, manifest: CacheabilityManifest, deploymentTool?: CloudflareDeploymentTool): string;
9
10
  //#endregion
@@ -115,16 +115,12 @@ function hasStaticModuleSpecifier(source, expected) {
115
115
  }
116
116
  return false;
117
117
  }
118
- function resolveGeneratedServerConfig(root, configuredPath) {
118
+ function resolveGeneratedWranglerArtifact(root, configuredPath) {
119
119
  const distDirectory = path.join(root, "dist");
120
120
  const configPath = path.resolve(root, configuredPath ?? "dist/server/wrangler.json");
121
121
  const relativeConfigPath = path.relative(distDirectory, configPath);
122
122
  if (relativeConfigPath.startsWith("..") || path.isAbsolute(relativeConfigPath)) throw new Error("Two-stage CDN warming requires a generated Wrangler config under dist/. Pass --config dist/server/wrangler.json (or another generated dist config).");
123
123
  if (!fs.existsSync(configPath) || !fs.lstatSync(configPath).isFile()) throw new Error(`Two-stage CDN warming requires the generated Wrangler config at ${path.relative(root, configPath)}. Rebuild the app before deploying.`);
124
- return configPath;
125
- }
126
- function assertManifestModuleReachable(configPath) {
127
- const serverDirectory = path.dirname(configPath);
128
124
  let config;
129
125
  try {
130
126
  config = JSON.parse(fs.readFileSync(configPath, "utf8"));
@@ -133,8 +129,36 @@ function assertManifestModuleReachable(configPath) {
133
129
  }
134
130
  const main = config && typeof config === "object" && !Array.isArray(config) ? config.main : void 0;
135
131
  if (typeof main !== "string" || main.length === 0) throw new Error("Two-stage CDN warming requires a generated Wrangler main module.");
132
+ return {
133
+ configPath,
134
+ main,
135
+ serverDirectory: path.dirname(configPath)
136
+ };
137
+ }
138
+ function resolveBuildOutputArtifact(root) {
139
+ const outputDirectory = path.join(root, ".cloudflare", "output");
140
+ const configPaths = fs.existsSync(outputDirectory) ? fs.readdirSync(outputDirectory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path.join(outputDirectory, entry.name, "workers", "default", "worker.config.json")).filter((candidate) => fs.existsSync(candidate) && fs.lstatSync(candidate).isFile()) : [];
141
+ if (configPaths.length !== 1) throw new Error("Two-stage CDN warming requires one default Worker Build Output config under .cloudflare/output/. Rebuild the app before deploying.");
142
+ const configPath = configPaths[0];
143
+ let config;
144
+ try {
145
+ config = JSON.parse(fs.readFileSync(configPath, "utf8"));
146
+ } catch (cause) {
147
+ throw new Error("Two-stage CDN warming could not parse the generated Build Output config.", { cause });
148
+ }
149
+ const manifest = config && typeof config === "object" && !Array.isArray(config) ? config.manifest : void 0;
150
+ const main = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? manifest.mainModule : void 0;
151
+ if (typeof main !== "string" || main.length === 0) throw new Error("Two-stage CDN warming requires a generated Build Output main module.");
152
+ return {
153
+ configPath,
154
+ main,
155
+ serverDirectory: path.join(path.dirname(configPath), "bundle")
156
+ };
157
+ }
158
+ function assertManifestModuleReachable(artifact) {
159
+ const { main, serverDirectory } = artifact;
136
160
  const mainPath = path.resolve(serverDirectory, main);
137
- if (!fs.existsSync(mainPath) || !fs.lstatSync(mainPath).isFile()) throw new Error("Two-stage CDN warming could not find the generated Wrangler main module.");
161
+ if (!fs.existsSync(mainPath) || !fs.lstatSync(mainPath).isFile()) throw new Error("Two-stage CDN warming could not find the generated Worker main module.");
138
162
  const viteManifestPath = path.join(serverDirectory, ".vite", "manifest.json");
139
163
  let viteManifest;
140
164
  try {
@@ -168,10 +192,10 @@ function assertManifestModuleReachable(configPath) {
168
192
  * The application build already imports this stable module asset, so the
169
193
  * completed dist directory remains the exact input to the final upload.
170
194
  */
171
- function writeCacheabilityManifestArtifact(root, configuredPath, manifest) {
172
- const configPath = resolveGeneratedServerConfig(root, configuredPath);
173
- assertManifestModuleReachable(configPath);
174
- const serverDirectory = path.dirname(configPath);
195
+ function writeCacheabilityManifestArtifact(root, configuredPath, manifest, deploymentTool = "wrangler") {
196
+ const artifact = deploymentTool === "cf" ? resolveBuildOutputArtifact(root) : resolveGeneratedWranglerArtifact(root, configuredPath);
197
+ assertManifestModuleReachable(artifact);
198
+ const { configPath, serverDirectory } = artifact;
175
199
  const manifestPath = path.join(serverDirectory, CACHEABILITY_MANIFEST_MODULE);
176
200
  if (!fs.existsSync(manifestPath) || !fs.lstatSync(manifestPath).isFile()) throw new Error(`Two-stage CDN warming requires ${CACHEABILITY_MANIFEST_MODULE} in the generated Worker artifact. Rebuild the app before deploying.`);
177
201
  const serializedManifest = JSON.stringify(manifest);
@@ -52,6 +52,7 @@ export declare function workerEntryHasCacheHandler(root: string): boolean;
52
52
  */
53
53
  export declare function formatMissingCacheAdapterError(options: {
54
54
  configFile?: string;
55
+ typedConfig?: boolean;
55
56
  }): string;
56
- export declare function formatImageOptimizationHint(): string;
57
+ export declare function formatImageOptimizationHint(typedConfig?: boolean): string;
57
58
  //#endregion
@@ -178,9 +178,10 @@ function workerEntryHasCacheHandler(root) {
178
178
  */
179
179
  function formatMissingCacheAdapterError(options) {
180
180
  const configRef = options.configFile ? options.configFile : "your Vite config";
181
- return `[vinext] This app uses ISR / caching but no cache adapter is configured in ${configRef}.\n\n Production deployments need a persistent cache backend. Declare one on the\n vinext() plugin in ${configRef}:\n\n import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";\n\n export default defineConfig({\n plugins: [\n vinext({\n cache: {\n data: kvDataAdapter(), // KV-backed data cache (binding: VINEXT_KV_CACHE)\n },\n }),\n cloudflare(),\n ],\n });\n\n The VINEXT_KV_CACHE namespace binding is added to wrangler.jsonc for you.\n Create the namespace with:\n\n npx wrangler kv namespace create VINEXT_KV_CACHE`;
181
+ return `[vinext] This app uses ISR / caching but no cache adapter is configured in ${configRef}.\n\n Production deployments need a persistent cache backend. Declare one on the\n vinext() plugin in ${configRef}:\n\n import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";\n\n export default defineConfig({\n plugins: [\n vinext({\n cache: {\n data: kvDataAdapter(), // KV-backed data cache (binding: VINEXT_KV_CACHE)\n },\n }),\n cloudflare(),\n ],\n });\n\n` + (options.typedConfig ? ` Configure the VINEXT_KV_CACHE namespace binding in cloudflare.config.ts.` : " The VINEXT_KV_CACHE namespace binding is added to wrangler.jsonc for you.\n Create the namespace with:\n\n npx wrangler kv namespace create VINEXT_KV_CACHE");
182
182
  }
183
- function formatImageOptimizationHint() {
183
+ function formatImageOptimizationHint(typedConfig = false) {
184
+ if (typedConfig) return " [vinext] next/image is served unoptimized. To enable edge image\n optimization via Cloudflare Images, configure imagesOptimizer() in your\n Vite config and the IMAGES binding in cloudflare.config.ts.";
184
185
  return " [vinext] next/image is served unoptimized. To enable edge image\n optimization via Cloudflare Images, run:\n\n vinext init --platform=cloudflare --image-optimization=cloudflare-images\n\n This adds the imagesOptimizer() option to your Vite config and the matching\n IMAGES binding to your Wrangler config without replacing existing settings.";
185
186
  }
186
187
  //#endregion
@@ -9,16 +9,16 @@ function formatDeployHelp() {
9
9
  - Detects App Router or Pages Router
10
10
  - Validates setup from vinext init --platform=cloudflare
11
11
  - Builds the project with Vite
12
- - Deploys via wrangler
12
+ - Deploys with cf for typed Cloudflare config projects, or Wrangler otherwise
13
13
 
14
14
  Options:
15
15
  --preview Deploy to preview environment (same as --env preview)
16
- --env <name> Deploy using wrangler env.<name>
17
- --name <name> Custom Worker name (default: from package.json)
16
+ --env <name> Cloudflare mode for typed config; Wrangler environment otherwise
17
+ --name <name> Custom Worker name for Wrangler config projects
18
18
  --config <path> Wrangler config path (default: wrangler.jsonc/json/toml)
19
19
  --skip-build Skip the build step (use existing dist/)
20
20
  --dry-run Validate setup without building or deploying
21
- --verbose Print raw output from internal Wrangler commands
21
+ --verbose Print raw output from internal Cloudflare CLI commands
22
22
  --no-promote Do not promote the uploaded Worker version to 100%
23
23
  traffic
24
24
  --prerender-all Pre-render discovered routes after building (future
@@ -91,7 +91,7 @@ function formatDeployHelp() {
91
91
  vpx @vinext/cloudflare deploy Build and deploy with Vite+
92
92
  vp exec vinext-cloudflare deploy Run the locally installed Vite+ bin
93
93
  vinext-cloudflare deploy --preview Deploy to a preview URL
94
- vinext-cloudflare deploy --env staging Deploy using wrangler env.staging
94
+ vinext-cloudflare deploy --env staging Deploy using Cloudflare mode/environment staging
95
95
  vinext-cloudflare deploy --config dist/server/wrangler.json Deploy using a generated Wrangler config
96
96
  vinext-cloudflare deploy --dry-run Validate setup without building or deploying
97
97
  vinext-cloudflare deploy --name my-app Deploy with a custom Worker name