@vinext/cloudflare 1.0.0-beta.8 → 1.0.0

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.
Files changed (59) hide show
  1. package/README.md +174 -37
  2. package/dist/cache/cdn-adapter-config.d.ts +2 -1
  3. package/dist/cache/cdn-adapter-config.js +13 -5
  4. package/dist/cache/cdn-adapter.d.ts +5 -56
  5. package/dist/cache/cdn-adapter.js +3 -61
  6. package/dist/cache/cdn-adapter.runtime.d.ts +1 -0
  7. package/dist/cache/cdn-adapter.runtime.js +17 -3
  8. package/dist/cache/cdn-adapter.worker.d.ts +1 -0
  9. package/dist/cache/cdn-adapter.worker.js +6 -4
  10. package/dist/cache/cdn-build-id.d.ts +1 -1
  11. package/dist/cache/cdn-build-id.js +1 -1
  12. package/dist/cache/config.d.ts +137 -0
  13. package/dist/cache/config.js +87 -0
  14. package/dist/cache/kv-data-adapter.d.ts +5 -1
  15. package/dist/cache/kv-data-adapter.js +6 -2
  16. package/dist/cache/kv-data-adapter.runtime.js +13 -9
  17. package/dist/cache/kv-key.d.ts +7 -4
  18. package/dist/cache/kv-key.js +16 -5
  19. package/dist/cache/response-store-adapter.d.ts +36 -0
  20. package/dist/cache/response-store-adapter.js +26 -1
  21. package/dist/cache/response-store-adapter.self-contained.worker.d.ts +1 -0
  22. package/dist/cache/response-store-adapter.self-contained.worker.js +3 -1
  23. package/dist/cache/response-store-adapter.service-binding.worker.d.ts +1 -0
  24. package/dist/cache/response-store-adapter.service-binding.worker.js +3 -1
  25. package/dist/cache/response-store-adapter.worker.d.ts +1 -1
  26. package/dist/cache/response-store-adapter.worker.js +148 -81
  27. package/dist/cache/response-store-cdn.runtime.js +1 -0
  28. package/dist/cache/response-store-data.runtime.js +53 -31
  29. package/dist/cache/static-assets-adapter.build.d.ts +6 -0
  30. package/dist/cache/static-assets-adapter.build.js +65 -0
  31. package/dist/cache/static-assets-adapter.d.ts +27 -0
  32. package/dist/cache/static-assets-adapter.js +26 -0
  33. package/dist/cache/static-assets-adapter.runtime.d.ts +26 -0
  34. package/dist/cache/static-assets-adapter.runtime.js +77 -0
  35. package/dist/cache/static-assets-adapter.shared.d.ts +12 -0
  36. package/dist/cache/static-assets-adapter.shared.js +4 -0
  37. package/dist/cache/workers-cache-cdn-adapter.d.ts +58 -0
  38. package/dist/cache/workers-cache-cdn-adapter.js +65 -0
  39. package/dist/cacheability-artifact.d.ts +7 -1
  40. package/dist/cacheability-artifact.js +60 -21
  41. package/dist/cacheability-probe.d.ts +2 -0
  42. package/dist/cacheability-probe.js +107 -13
  43. package/dist/cdn-warm.d.ts +2 -1
  44. package/dist/cdn-warm.js +25 -8
  45. package/dist/deploy-config.d.ts +3 -8
  46. package/dist/deploy-config.js +5 -23
  47. package/dist/deploy-help.js +58 -47
  48. package/dist/deploy.d.ts +31 -21
  49. package/dist/deploy.js +501 -286
  50. package/dist/tpr.d.ts +29 -72
  51. package/dist/tpr.js +52 -601
  52. package/dist/version-deploy.d.ts +27 -0
  53. package/dist/version-deploy.js +125 -5
  54. package/dist/wrangler-config.d.ts +19 -0
  55. package/dist/wrangler-config.js +294 -0
  56. package/dist/wrangler-version-metadata.js +1 -1
  57. package/package.json +16 -9
  58. package/dist/prerender-kv-populate.d.ts +0 -24
  59. package/dist/prerender-kv-populate.js +0 -153
package/dist/deploy.js CHANGED
@@ -1,27 +1,29 @@
1
1
  import { writeCacheabilityManifestArtifact } from "./cacheability-artifact.js";
2
2
  import { DEFAULT_CACHEABILITY_PROBE_RETRY_DELAY_MS, probeStagedWorkerCacheability, readPrerenderSecret } from "./cacheability-probe.js";
3
3
  import { CdnOperationProgress, createCdnWarmTargets, createPrerenderWarmPlan, waitForCdnWarmTargetReadiness, warmCdnCache } from "./cdn-warm.js";
4
- import { parseWranglerConfig, runTPR } from "./tpr.js";
4
+ import { parseWranglerConfig } from "./wrangler-config.js";
5
+ import { resolveTPRRoutes, selectRoutes } from "./tpr.js";
5
6
  import { VINEXT_EXPECTED_WORKER_VERSION_HEADER } from "./version-headers.js";
6
- import { formatImageOptimizationHint, formatMissingCacheAdapterError, resolveCdnAdapterConfig, resolveKvDataAdapterConfig, viteConfigHasCacheAdapter, viteConfigHasCloudflarePlugin, viteConfigHasImageAdapter, workerEntryHasCacheHandler } from "./deploy-config.js";
7
+ import { formatImageOptimizationHint, formatMissingCacheAdapterError, resolveCdnAdapterConfig, viteConfigHasCacheAdapter, viteConfigHasCloudflarePlugin, viteConfigHasImageAdapter, workerEntryHasCacheHandler } from "./deploy-config.js";
7
8
  import { assertCdnVersionMetadataConfig } from "./wrangler-version-metadata.js";
8
9
  import { parseWorkerDeploymentUrl } from "./worker-deployment-url.js";
9
- import { runWranglerDeploymentStatus, runWranglerTriggersDeploy, runWranglerVersionDeploy, runWranglerVersionUpload } from "./version-deploy.js";
10
- import { buildPrerenderKVPairs } from "./prerender-kv-populate.js";
10
+ import { readBuildOutputWorkerName, runCfDeploymentStatus, runCfTriggersDeploy, runCfVersionDeploy, runCfVersionUpload, runWranglerDeploymentStatus, runWranglerTriggersDeploy, runWranglerVersionDeploy, runWranglerVersionUpload } from "./version-deploy.js";
11
11
  import { createRequire } from "node:module";
12
12
  import fs from "node:fs";
13
13
  import path from "node:path";
14
14
  import { cacheabilityRoutePathname } from "vinext/internal/server/cacheability-manifest";
15
15
  import { setTimeout } from "node:timers/promises";
16
16
  import { DEFAULT_REMOTE_PATH_DISCOVERY_PHASE_TIMEOUT_MS, DEFAULT_REMOTE_PATH_DISCOVERY_RETRY_DELAY_MS, discoverPrerenderPathManifest, emitPrerenderPathManifest } from "vinext/internal/build/prerender-paths";
17
- import os from "node:os";
17
+ import { normalizePathTrailingSlash } from "vinext/shims/url-utils";
18
18
  import { spawn } from "node:child_process";
19
19
  import { parseArgs } from "node:util";
20
20
  import { pathToFileURL } from "node:url";
21
+ import { VINEXT_BUILD_LIFECYCLE_CONFIG } from "vinext/internal/build/lifecycle";
21
22
  import { runPrerender } from "vinext/internal/build/run-prerender";
23
+ import { printBuildReport } from "vinext/internal/build/report";
22
24
  import { loadDotenv } from "vinext/internal/config/dotenv";
23
25
  import { findVinextNextConfigInPlugins, loadNextConfig, resolveNextConfig, resolveNextConfigInput } from "vinext/internal/config/next-config";
24
- import { findVinextCacheConfigInPlugins, findVinextPrerenderConfigInPlugins, findVinextRouteRootConfigInPlugins, formatVinextPrerenderLabel, hasBuildIdentityResponseHeader, hasUncachedRequestRouting, hasVerbatimResponseVary, isConfiguredCdnResponsePolicyHeader, requiresRouteCacheabilityProbeManifest, resolveVinextPrerenderDecision, supportsCanonicalRscWarmup, usesVinextCacheWarmupStatus } from "vinext/internal/config/prerender";
26
+ import { cacheWarmupStatusSource, finalizeCacheAdapterPrerenderOutput, findVinextCacheConfigInPlugins, findVinextPrerenderConfigInPlugins, findVinextRouteRootConfigInPlugins, formatVinextPrerenderLabel, hasBuildIdentityResponseHeader, hasCacheAdapterPrerenderOutput, hasUncachedRequestRouting, hasVerbatimResponseVary, isConfiguredCdnResponsePolicyHeader, requiresRouteCacheabilityProbeManifest, resolveVinextPrerenderDecision, supportsCanonicalRscWarmup } from "vinext/internal/config/prerender";
25
27
  import { detectProject, findInNodeModules, formatMissingCloudflarePluginError, getMissingDeps } from "vinext/internal/utils/project";
26
28
  import { PHASE_PRODUCTION_BUILD } from "vinext/shims/constants";
27
29
  //#region src/deploy.ts
@@ -35,6 +37,17 @@ import { PHASE_PRODUCTION_BUILD } from "vinext/shims/constants";
35
37
  * 3. Deploys to Cloudflare Workers via Wrangler
36
38
  */
37
39
  const DEFAULT_CDN_WARM_PROMOTION_DELAY_MS = 15e3;
40
+ /** The typed cf Vite plugin emits Worker and asset bundles into Build Output. */
41
+ function resolvePrerenderOutputDirs(root, deploymentTool, configured) {
42
+ if (deploymentTool !== "cf") return configured;
43
+ const workerDir = path.join(root, ".cloudflare", "output", "v0", "workers", "default");
44
+ if (!fs.existsSync(path.join(workerDir, "worker.config.json"))) return configured;
45
+ return {
46
+ ...configured,
47
+ rscOutDir: path.join(workerDir, "bundle"),
48
+ clientOutDir: path.join(workerDir, "assets")
49
+ };
50
+ }
38
51
  function parsePositiveIntegerArg(raw, flag) {
39
52
  if (raw === "") throw new Error(`${flag} requires a value, but none was provided.`);
40
53
  const parsed = Number(raw);
@@ -54,7 +67,7 @@ function validateTimerDelay(value, flag, raw = String(value)) {
54
67
  return value;
55
68
  }
56
69
  function validatePromotionDelay(value, raw = String(value)) {
57
- return validateTimerDelay(value, "--warm-cdn-promotion-delay", raw);
70
+ return validateTimerDelay(value, "--warm-cache-promotion-delay", raw);
58
71
  }
59
72
  function validateCdnWarmTarget(raw) {
60
73
  const value = raw.trim();
@@ -62,15 +75,16 @@ function validateCdnWarmTarget(raw) {
62
75
  try {
63
76
  url = new URL(value);
64
77
  } catch {
65
- throw new Error(`--warm-cdn-target expects an HTTPS origin, but got "${raw}".`);
78
+ throw new Error(`--warm-cache-target expects an HTTPS origin, but got "${raw}".`);
66
79
  }
67
- if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.port !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "") throw new Error(`--warm-cdn-target expects an HTTPS origin without a path, query, credentials, or port, but got "${raw}".`);
80
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.port !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "") throw new Error(`--warm-cache-target expects an HTTPS origin without a path, query, credentials, or port, but got "${raw}".`);
68
81
  return url.origin;
69
82
  }
70
83
  function formatUnknownError(error) {
71
84
  if (error instanceof Error && error.message) return error.message;
72
85
  return String(error);
73
86
  }
87
+ var StagedWarmupError = class extends Error {};
74
88
  /** Deploy command flag definitions for util.parseArgs. */
75
89
  const deployArgOptions = {
76
90
  help: {
@@ -102,6 +116,54 @@ const deployArgOptions = {
102
116
  default: false
103
117
  },
104
118
  "prerender-concurrency": { type: "string" },
119
+ "warm-cache": {
120
+ type: "boolean",
121
+ default: false
122
+ },
123
+ "warm-cache-target": { type: "string" },
124
+ "warm-cache-concurrency": { type: "string" },
125
+ "warm-cache-timeout": { type: "string" },
126
+ "warm-cache-retries": { type: "string" },
127
+ "warm-cache-discovery-timeout": { type: "string" },
128
+ "warm-cache-discovery-retries": { type: "string" },
129
+ "warm-cache-probe-timeout": { type: "string" },
130
+ "warm-cache-probe-retries": { type: "string" },
131
+ "warm-cache-certify": {
132
+ type: "boolean",
133
+ default: false
134
+ },
135
+ "warm-cache-readiness-timeout": { type: "string" },
136
+ "warm-cache-readiness-retries": { type: "string" },
137
+ "warm-cache-readiness-probes": { type: "string" },
138
+ "warm-cache-readiness-probe-delay": { type: "string" },
139
+ "dangerously-promote-on-warm-cache-error": {
140
+ type: "boolean",
141
+ default: false
142
+ },
143
+ "no-promote": {
144
+ type: "boolean",
145
+ default: false
146
+ },
147
+ "warm-cache-no-promote": {
148
+ type: "boolean",
149
+ default: false
150
+ },
151
+ "warm-cache-promotion-delay": { type: "string" },
152
+ "warm-cache-include-fallbacks": {
153
+ type: "boolean",
154
+ default: false
155
+ },
156
+ "traffic-aware-warm-cache": {
157
+ type: "boolean",
158
+ default: false
159
+ },
160
+ "traffic-aware-coverage": { type: "string" },
161
+ "traffic-aware-limit": { type: "string" },
162
+ "traffic-aware-window": { type: "string" },
163
+ "dangerously-promote-on-cdn-warm-error": {
164
+ type: "boolean",
165
+ default: false
166
+ },
105
167
  "experimental-warm-cdn-cache": {
106
168
  type: "boolean",
107
169
  default: false
@@ -122,10 +184,6 @@ const deployArgOptions = {
122
184
  "warm-cdn-readiness-retries": { type: "string" },
123
185
  "warm-cdn-readiness-probes": { type: "string" },
124
186
  "warm-cdn-readiness-probe-delay": { type: "string" },
125
- "dangerously-promote-on-cdn-warm-error": {
126
- type: "boolean",
127
- default: false
128
- },
129
187
  "warm-cdn-no-promote": {
130
188
  type: "boolean",
131
189
  default: false
@@ -135,6 +193,10 @@ const deployArgOptions = {
135
193
  type: "boolean",
136
194
  default: false
137
195
  },
196
+ "experimental-traffic-aware-warm-cache": {
197
+ type: "boolean",
198
+ default: false
199
+ },
138
200
  "experimental-tpr": {
139
201
  type: "boolean",
140
202
  default: false
@@ -149,8 +211,29 @@ function parseDeployArgs(args) {
149
211
  options: deployArgOptions,
150
212
  strict: true
151
213
  });
152
- if (values["warm-cdn-certify"] && !values["experimental-warm-cdn-cache"]) throw new Error("--warm-cdn-certify requires --experimental-warm-cdn-cache.");
153
- if (values["warm-cdn-target"] && !values["experimental-warm-cdn-cache"]) throw new Error("--warm-cdn-target requires --experimental-warm-cdn-cache.");
214
+ values["dangerously-promote-on-warm-cache-error"] ||= values["dangerously-promote-on-cdn-warm-error"];
215
+ values["warm-cache"] ||= values["experimental-warm-cdn-cache"];
216
+ values["warm-cache-target"] ??= values["warm-cdn-target"];
217
+ values["warm-cache-concurrency"] ??= values["warm-cdn-concurrency"];
218
+ values["warm-cache-timeout"] ??= values["warm-cdn-timeout"];
219
+ values["warm-cache-retries"] ??= values["warm-cdn-retries"];
220
+ values["warm-cache-discovery-timeout"] ??= values["warm-cdn-discovery-timeout"];
221
+ values["warm-cache-discovery-retries"] ??= values["warm-cdn-discovery-retries"];
222
+ values["warm-cache-probe-timeout"] ??= values["warm-cdn-probe-timeout"];
223
+ values["warm-cache-probe-retries"] ??= values["warm-cdn-probe-retries"];
224
+ values["warm-cache-certify"] ||= values["warm-cdn-certify"];
225
+ values["warm-cache-readiness-timeout"] ??= values["warm-cdn-readiness-timeout"];
226
+ values["warm-cache-readiness-retries"] ??= values["warm-cdn-readiness-retries"];
227
+ values["warm-cache-readiness-probes"] ??= values["warm-cdn-readiness-probes"];
228
+ values["warm-cache-readiness-probe-delay"] ??= values["warm-cdn-readiness-probe-delay"];
229
+ values["warm-cache-no-promote"] ||= values["warm-cdn-no-promote"];
230
+ values["warm-cache-promotion-delay"] ??= values["warm-cdn-promotion-delay"];
231
+ values["warm-cache-include-fallbacks"] ||= values["warm-cdn-include-fallbacks"];
232
+ values["traffic-aware-warm-cache"] ||= values["experimental-traffic-aware-warm-cache"];
233
+ const trafficAwareWarmCache = values["traffic-aware-warm-cache"] || values["experimental-tpr"];
234
+ const warming = values["warm-cache"] || trafficAwareWarmCache;
235
+ if (values["warm-cache-certify"] && !warming) throw new Error("--warm-cache-certify requires --warm-cache or --traffic-aware-warm-cache.");
236
+ if (values["warm-cache-target"] && !warming) throw new Error("--warm-cache-target requires --warm-cache or --traffic-aware-warm-cache.");
154
237
  function parseIntArg(name, raw) {
155
238
  if (!raw) return void 0;
156
239
  const n = parseInt(raw, 10);
@@ -171,28 +254,28 @@ function parseDeployArgs(args) {
171
254
  verbose: values.verbose,
172
255
  prerenderAll: values["prerender-all"],
173
256
  prerenderConcurrency: values["prerender-concurrency"] === void 0 ? void 0 : parsePositiveIntegerArg(values["prerender-concurrency"], "--prerender-concurrency"),
174
- warmCdnCache: values["experimental-warm-cdn-cache"],
175
- warmCdnTarget: values["warm-cdn-target"] === void 0 ? void 0 : validateCdnWarmTarget(values["warm-cdn-target"]),
176
- warmCdnConcurrency: values["warm-cdn-concurrency"] === void 0 ? void 0 : parsePositiveIntegerArg(values["warm-cdn-concurrency"], "--warm-cdn-concurrency"),
177
- warmCdnTimeout: values["warm-cdn-timeout"] === void 0 ? void 0 : parsePositiveIntegerArg(values["warm-cdn-timeout"], "--warm-cdn-timeout"),
178
- warmCdnRetries: values["warm-cdn-retries"] === void 0 ? void 0 : parseNonNegativeIntegerArg(values["warm-cdn-retries"], "--warm-cdn-retries"),
179
- warmCdnDiscoveryTimeout: values["warm-cdn-discovery-timeout"] === void 0 ? void 0 : parsePositiveIntegerArg(values["warm-cdn-discovery-timeout"], "--warm-cdn-discovery-timeout"),
180
- warmCdnDiscoveryRetries: values["warm-cdn-discovery-retries"] === void 0 ? void 0 : parseNonNegativeIntegerArg(values["warm-cdn-discovery-retries"], "--warm-cdn-discovery-retries"),
181
- warmCdnProbeTimeout: values["warm-cdn-probe-timeout"] === void 0 ? void 0 : parsePositiveIntegerArg(values["warm-cdn-probe-timeout"], "--warm-cdn-probe-timeout"),
182
- warmCdnProbeRetries: values["warm-cdn-probe-retries"] === void 0 ? void 0 : parseNonNegativeIntegerArg(values["warm-cdn-probe-retries"], "--warm-cdn-probe-retries"),
183
- warmCdnCertify: values["warm-cdn-certify"],
184
- warmCdnReadinessTimeout: values["warm-cdn-readiness-timeout"] === void 0 ? void 0 : parsePositiveIntegerArg(values["warm-cdn-readiness-timeout"], "--warm-cdn-readiness-timeout"),
185
- warmCdnReadinessRetries: values["warm-cdn-readiness-retries"] === void 0 ? void 0 : parseNonNegativeIntegerArg(values["warm-cdn-readiness-retries"], "--warm-cdn-readiness-retries"),
186
- warmCdnReadinessProbes: values["warm-cdn-readiness-probes"] === void 0 ? void 0 : parsePositiveIntegerArg(values["warm-cdn-readiness-probes"], "--warm-cdn-readiness-probes"),
187
- warmCdnReadinessProbeDelay: values["warm-cdn-readiness-probe-delay"] === void 0 ? void 0 : validateTimerDelay(parseNonNegativeIntegerArg(values["warm-cdn-readiness-probe-delay"], "--warm-cdn-readiness-probe-delay"), "--warm-cdn-readiness-probe-delay", values["warm-cdn-readiness-probe-delay"]),
188
- dangerouslyPromoteOnCdnWarmError: values["dangerously-promote-on-cdn-warm-error"],
189
- warmCdnPromote: !values["warm-cdn-no-promote"],
190
- warmCdnPromotionDelay: values["warm-cdn-promotion-delay"] === void 0 ? void 0 : validatePromotionDelay(parseNonNegativeIntegerArg(values["warm-cdn-promotion-delay"], "--warm-cdn-promotion-delay"), values["warm-cdn-promotion-delay"]),
191
- warmCdnIncludeFallbacks: values["warm-cdn-include-fallbacks"],
192
- experimentalTPR: values["experimental-tpr"],
193
- tprCoverage: parseIntArg("tpr-coverage", values["tpr-coverage"]),
194
- tprLimit: parseIntArg("tpr-limit", values["tpr-limit"]),
195
- tprWindow: parseIntArg("tpr-window", values["tpr-window"])
257
+ warmCdnCache: values["warm-cache"],
258
+ warmCdnTarget: values["warm-cache-target"] === void 0 ? void 0 : validateCdnWarmTarget(values["warm-cache-target"]),
259
+ warmCdnConcurrency: values["warm-cache-concurrency"] === void 0 ? void 0 : parsePositiveIntegerArg(values["warm-cache-concurrency"], "--warm-cache-concurrency"),
260
+ warmCdnTimeout: values["warm-cache-timeout"] === void 0 ? void 0 : parsePositiveIntegerArg(values["warm-cache-timeout"], "--warm-cache-timeout"),
261
+ warmCdnRetries: values["warm-cache-retries"] === void 0 ? void 0 : parseNonNegativeIntegerArg(values["warm-cache-retries"], "--warm-cache-retries"),
262
+ warmCdnDiscoveryTimeout: values["warm-cache-discovery-timeout"] === void 0 ? void 0 : parsePositiveIntegerArg(values["warm-cache-discovery-timeout"], "--warm-cache-discovery-timeout"),
263
+ warmCdnDiscoveryRetries: values["warm-cache-discovery-retries"] === void 0 ? void 0 : parseNonNegativeIntegerArg(values["warm-cache-discovery-retries"], "--warm-cache-discovery-retries"),
264
+ warmCdnProbeTimeout: values["warm-cache-probe-timeout"] === void 0 ? void 0 : parsePositiveIntegerArg(values["warm-cache-probe-timeout"], "--warm-cache-probe-timeout"),
265
+ warmCdnProbeRetries: values["warm-cache-probe-retries"] === void 0 ? void 0 : parseNonNegativeIntegerArg(values["warm-cache-probe-retries"], "--warm-cache-probe-retries"),
266
+ warmCdnCertify: values["warm-cache-certify"],
267
+ warmCdnReadinessTimeout: values["warm-cache-readiness-timeout"] === void 0 ? void 0 : parsePositiveIntegerArg(values["warm-cache-readiness-timeout"], "--warm-cache-readiness-timeout"),
268
+ warmCdnReadinessRetries: values["warm-cache-readiness-retries"] === void 0 ? void 0 : parseNonNegativeIntegerArg(values["warm-cache-readiness-retries"], "--warm-cache-readiness-retries"),
269
+ warmCdnReadinessProbes: values["warm-cache-readiness-probes"] === void 0 ? void 0 : parsePositiveIntegerArg(values["warm-cache-readiness-probes"], "--warm-cache-readiness-probes"),
270
+ warmCdnReadinessProbeDelay: values["warm-cache-readiness-probe-delay"] === void 0 ? void 0 : validateTimerDelay(parseNonNegativeIntegerArg(values["warm-cache-readiness-probe-delay"], "--warm-cache-readiness-probe-delay"), "--warm-cache-readiness-probe-delay", values["warm-cache-readiness-probe-delay"]),
271
+ dangerouslyPromoteOnCdnWarmError: values["dangerously-promote-on-warm-cache-error"],
272
+ warmCdnPromote: !values["no-promote"] && !values["warm-cache-no-promote"],
273
+ warmCdnPromotionDelay: values["warm-cache-promotion-delay"] === void 0 ? void 0 : validatePromotionDelay(parseNonNegativeIntegerArg(values["warm-cache-promotion-delay"], "--warm-cache-promotion-delay"), values["warm-cache-promotion-delay"]),
274
+ warmCdnIncludeFallbacks: values["warm-cache-include-fallbacks"],
275
+ experimentalTPR: trafficAwareWarmCache,
276
+ tprCoverage: parseIntArg("traffic-aware-coverage", values["traffic-aware-coverage"] ?? values["tpr-coverage"]),
277
+ tprLimit: parseIntArg("traffic-aware-limit", values["traffic-aware-limit"] ?? values["tpr-limit"]),
278
+ tprWindow: parseIntArg("traffic-aware-window", values["traffic-aware-window"] ?? values["tpr-window"])
196
279
  };
197
280
  }
198
281
  /**
@@ -242,10 +325,13 @@ async function loadProjectWranglerApi(root) {
242
325
  pathToFileURL(wranglerPath).href
243
326
  );
244
327
  }
245
- async function loadDeployViteConfigMetadata(root) {
328
+ function resolveViteBuildMode(deploymentTool, env) {
329
+ return deploymentTool === "cf" ? env ?? "production" : "production";
330
+ }
331
+ async function loadDeployViteConfigMetadata(root, mode) {
246
332
  const plugins = (await (await loadProjectViteApi(root)).loadConfigFromFile({
247
333
  command: "build",
248
- mode: "production"
334
+ mode
249
335
  }, void 0, root))?.config.plugins;
250
336
  return {
251
337
  cacheConfig: await findVinextCacheConfigInPlugins(plugins),
@@ -254,32 +340,22 @@ async function loadDeployViteConfigMetadata(root) {
254
340
  routeRootConfig: await findVinextRouteRootConfigInPlugins(plugins)
255
341
  };
256
342
  }
257
- async function runBuild(info, env) {
343
+ async function runBuild(info, env, mode) {
258
344
  console.log("\n Building for Cloudflare Workers...\n");
259
345
  const { createBuilder } = await loadProjectViteApi(info.root);
346
+ let completed = false;
260
347
  await withCloudflareEnv(env, async () => {
261
- await (await createBuilder({ root: info.root })).buildApp();
262
- });
263
- }
264
- async function populateKVCacheFromPrerenderedArtifacts(root, wranglerEnv, cacheConfig) {
265
- const kvConfig = resolveKvDataAdapterConfig(cacheConfig);
266
- if (!kvConfig) return;
267
- const { routeCount, pairs } = buildPrerenderKVPairs(path.join(root, "dist", "server"), {
268
- appPrefix: kvConfig.appPrefix,
269
- ttlSeconds: kvConfig.ttlSeconds
270
- });
271
- if (pairs.length === 0) {
272
- console.log(" KV cache: Skipping prerender upload (no App Router prerendered cache entries found).");
273
- return;
274
- }
275
- await runWranglerKVBulkPut(root, {
276
- binding: kvConfig.binding,
277
- env: wranglerEnv,
278
- pairs
348
+ const invocation = { onComplete() {
349
+ completed = true;
350
+ } };
351
+ await (await createBuilder({
352
+ root: info.root,
353
+ mode,
354
+ [VINEXT_BUILD_LIFECYCLE_CONFIG]: invocation
355
+ })).buildApp();
279
356
  });
280
- console.log(` KV cache: Uploaded ${pairs.length} entr${pairs.length === 1 ? "y" : "ies"} for ${routeCount} prerendered route${routeCount === 1 ? "" : "s"}.`);
357
+ if (!completed) throw new Error("[vinext] The Cloudflare build lifecycle did not complete.");
281
358
  }
282
- const KV_BULK_PUT_CHUNK_SIZE = 25;
283
359
  function validateWranglerEnvName(env) {
284
360
  if (env.includes("\0")) throw new Error("Wrangler environment names cannot contain null bytes.");
285
361
  return env;
@@ -295,21 +371,25 @@ function buildWranglerDeployArgs(options) {
295
371
  env
296
372
  };
297
373
  }
298
- function buildWranglerKVBulkPutArgs(options) {
299
- const env = options.env || void 0;
300
- const args = [
301
- "kv",
302
- "bulk",
303
- "put",
304
- options.filePath,
305
- "--binding",
306
- options.binding,
307
- "--remote"
308
- ];
309
- if (env) args.push("--env", validateWranglerEnvName(env));
374
+ function resolveDeploymentTool(root) {
375
+ return fs.existsSync(path.join(root, "cloudflare.config.ts")) ? "cf" : "wrangler";
376
+ }
377
+ function isCfCliInstalled(root, resolvePackageJson = (projectRoot) => {
378
+ try {
379
+ return createRequire(path.join(projectRoot, "package.json")).resolve("cf/package.json");
380
+ } catch {
381
+ return findInNodeModules(projectRoot, "cf/package.json");
382
+ }
383
+ }) {
384
+ return resolvePackageJson(root) !== null;
385
+ }
386
+ function buildCfDeployArgs(options) {
387
+ const mode = options.env || (options.preview ? "preview" : void 0);
388
+ const args = ["deploy", "--prebuilt"];
389
+ if (mode) args.push("--mode", validateWranglerEnvName(mode));
310
390
  return {
311
391
  args,
312
- env
392
+ mode
313
393
  };
314
394
  }
315
395
  /**
@@ -333,6 +413,21 @@ function resolveWranglerBin(root, resolvePackageJson = (projectRoot) => {
333
413
  }
334
414
  return path.join(root, "node_modules", "wrangler", "bin", "wrangler.js");
335
415
  }
416
+ function resolveCfBin(root, resolvePackageJson = (projectRoot) => {
417
+ try {
418
+ return createRequire(path.join(projectRoot, "package.json")).resolve("cf/package.json");
419
+ } catch {
420
+ return findInNodeModules(projectRoot, "cf/package.json");
421
+ }
422
+ }) {
423
+ const packageJsonPath = resolvePackageJson(root);
424
+ if (packageJsonPath) {
425
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
426
+ const bin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.cf;
427
+ if (bin) return path.resolve(path.dirname(packageJsonPath), bin);
428
+ }
429
+ return path.join(root, "node_modules", "cf", "bin", "cf");
430
+ }
336
431
  function buildNodeCliInvocation(scriptPath, args, nodeExecutable = process.execPath) {
337
432
  return {
338
433
  file: nodeExecutable,
@@ -347,46 +442,8 @@ function buildWranglerInvocation(root, options, nodeExecutable = process.execPat
347
442
  env
348
443
  };
349
444
  }
350
- async function runWranglerKVBulkPut(root, options, execute = spawn, nodeExecutable = process.execPath) {
351
- const tempDir = fs.mkdtempSync(path.join(options.tempDir ?? os.tmpdir(), "vinext-kv-bulk-"));
352
- try {
353
- const wranglerBin = resolveWranglerBin(root);
354
- const totalChunks = Math.ceil(options.pairs.length / KV_BULK_PUT_CHUNK_SIZE);
355
- for (let i = 0; i < totalChunks; i++) {
356
- const filePath = path.join(tempDir, `prerender-kv-${i}.json`);
357
- const chunk = options.pairs.slice(i * KV_BULK_PUT_CHUNK_SIZE, (i + 1) * KV_BULK_PUT_CHUNK_SIZE);
358
- fs.writeFileSync(filePath, JSON.stringify(chunk), "utf-8");
359
- const { args } = buildWranglerKVBulkPutArgs({
360
- binding: options.binding,
361
- env: options.env,
362
- filePath
363
- });
364
- const invocation = buildNodeCliInvocation(wranglerBin, args, nodeExecutable);
365
- const child = execute(invocation.file, invocation.args, {
366
- cwd: root,
367
- stdio: "inherit",
368
- shell: false
369
- });
370
- await new Promise((resolve, reject) => {
371
- child.once("error", reject);
372
- child.once("close", (code, signal) => {
373
- if (code === 0) {
374
- resolve();
375
- return;
376
- }
377
- const exitReason = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
378
- reject(/* @__PURE__ */ new Error(`Wrangler KV bulk put failed with ${exitReason}.`));
379
- });
380
- });
381
- }
382
- } finally {
383
- fs.rmSync(tempDir, {
384
- recursive: true,
385
- force: true
386
- });
387
- }
388
- }
389
445
  async function runWranglerDeploy(root, options, execute = spawn) {
446
+ if (options.promote === false) return runWranglerVersionUpload(root, options).previewUrl ?? "(Preview URL not detected in wrangler output)";
390
447
  const spawnOptions = {
391
448
  cwd: root,
392
449
  stdio: [
@@ -424,12 +481,95 @@ async function runWranglerDeploy(root, options, execute = spawn) {
424
481
  });
425
482
  return parseWorkerDeploymentUrl(output) ?? "(URL not detected in wrangler output)";
426
483
  }
484
+ async function runCfDeploy(root, options, execute = spawn) {
485
+ const { args, mode } = buildCfDeployArgs(options);
486
+ console.log(mode ? `\n Deploying Build Output in mode: ${mode}...` : "\n Deploying Build Output...");
487
+ const child = execute(process.execPath, [resolveCfBin(root), ...args], {
488
+ cwd: root,
489
+ stdio: [
490
+ "inherit",
491
+ "pipe",
492
+ "pipe"
493
+ ],
494
+ shell: false
495
+ });
496
+ let output = "";
497
+ child.stdout?.on("data", (chunk) => {
498
+ const text = chunk.toString();
499
+ output += text;
500
+ process.stdout.write(text);
501
+ });
502
+ child.stderr?.on("data", (chunk) => {
503
+ const text = chunk.toString();
504
+ output += text;
505
+ process.stderr.write(text);
506
+ });
507
+ await new Promise((resolve, reject) => {
508
+ child.once("error", reject);
509
+ child.once("close", (code, signal) => {
510
+ if (code === 0) resolve();
511
+ else reject(/* @__PURE__ */ new Error(`cf deploy failed with ${signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`}.`));
512
+ });
513
+ });
514
+ return parseWorkerDeploymentUrl(output) ?? "(URL not detected in cf output)";
515
+ }
427
516
  function hasCdnWarmRequests(plan) {
428
517
  return plan.paths.length + (plan.pagesDataPaths?.length ?? 0) + (plan.routeHandlerPaths?.length ?? 0) + plan.rscPaths.length + plan.loadingShellPaths.length > 0;
429
518
  }
519
+ function selectTPRWarmPlan(plan, traffic, coverage, limit) {
520
+ const canonical = (pathname) => normalizePathTrailingSlash(pathname, false);
521
+ const resolved = new Set(Object.entries(plan.routePatterns ?? {}).map(([pathname, route]) => canonical(route.cacheabilityProbe?.concretePathname ?? pathname)));
522
+ const requestsByPath = /* @__PURE__ */ new Map();
523
+ for (const { path, requests } of traffic) {
524
+ const pathname = canonical(path);
525
+ if (!resolved.has(pathname)) continue;
526
+ requestsByPath.set(pathname, (requestsByPath.get(pathname) ?? 0) + requests);
527
+ }
528
+ const selected = new Set(selectRoutes(Array.from(requestsByPath, ([path, requests]) => ({
529
+ path,
530
+ requests
531
+ })).sort((a, b) => b.requests - a.requests), coverage, limit).routes.map(({ path }) => path));
532
+ const includes = (pathname) => selected.has(canonical(plan.routePatterns?.[pathname]?.cacheabilityProbe?.concretePathname ?? pathname));
533
+ const filter = (pathnames) => pathnames?.filter(includes);
534
+ return {
535
+ ...plan,
536
+ appPaths: filter(plan.appPaths),
537
+ loadingShellPaths: filter(plan.loadingShellPaths) ?? [],
538
+ pagesDataPaths: filter(plan.pagesDataPaths),
539
+ pagesPaths: filter(plan.pagesPaths),
540
+ paths: filter(plan.paths) ?? [],
541
+ routeHandlerPaths: filter(plan.routeHandlerPaths),
542
+ routePatterns: plan.routePatterns ? Object.fromEntries(Object.entries(plan.routePatterns).filter(([pathname]) => includes(pathname))) : void 0,
543
+ rscPaths: filter(plan.rscPaths) ?? []
544
+ };
545
+ }
430
546
  function projectRequiresRouteCacheabilityProbeManifest(project, cacheConfig) {
431
547
  return (project.isAppRouter || project.isPagesRouter) && requiresRouteCacheabilityProbeManifest(cacheConfig);
432
548
  }
549
+ function deploymentWorkerName(options) {
550
+ if (!options.name) throw new Error("Could not detect the Worker name for its deployment.");
551
+ return options.name;
552
+ }
553
+ function runDeploymentStatus(root, options) {
554
+ return options.deploymentTool === "cf" ? runCfDeploymentStatus(root, {
555
+ name: deploymentWorkerName(options),
556
+ env: options.env,
557
+ verbose: options.verbose
558
+ }) : runWranglerDeploymentStatus(root, options);
559
+ }
560
+ function runVersionDeploy(root, traffic, options, phase) {
561
+ return options.deploymentTool === "cf" ? runCfVersionDeploy(root, traffic, {
562
+ name: deploymentWorkerName(options),
563
+ env: options.env,
564
+ verbose: options.verbose
565
+ }, phase) : runWranglerVersionDeploy(root, traffic, options, phase);
566
+ }
567
+ function runTriggersDeploy(root, options) {
568
+ return options.deploymentTool === "cf" ? runCfTriggersDeploy(root, options) : runWranglerTriggersDeploy(root, options);
569
+ }
570
+ function runVersionUpload(root, options) {
571
+ return options.deploymentTool === "cf" ? runCfVersionUpload(root, options) : runWranglerVersionUpload(root, options);
572
+ }
433
573
  function cdnWarmTargetKey(target) {
434
574
  return `${target.kind}\0${target.sourcePathname}`;
435
575
  }
@@ -449,14 +589,14 @@ async function deployWithCdnWarmup(root, paths, options) {
449
589
  ...options,
450
590
  warmCdnTarget: validateCdnWarmTarget(options.warmCdnTarget)
451
591
  };
452
- if (options.warmCdnDiscoveryTimeout !== void 0) parsePositiveIntegerArg(String(options.warmCdnDiscoveryTimeout), "--warm-cdn-discovery-timeout");
453
- if (options.warmCdnDiscoveryRetries !== void 0) parseNonNegativeIntegerArg(String(options.warmCdnDiscoveryRetries), "--warm-cdn-discovery-retries");
454
- if (options.warmCdnProbeTimeout !== void 0) parsePositiveIntegerArg(String(options.warmCdnProbeTimeout), "--warm-cdn-probe-timeout");
455
- if (options.warmCdnProbeRetries !== void 0) parseNonNegativeIntegerArg(String(options.warmCdnProbeRetries), "--warm-cdn-probe-retries");
456
- if (options.warmCdnReadinessTimeout !== void 0) parsePositiveIntegerArg(String(options.warmCdnReadinessTimeout), "--warm-cdn-readiness-timeout");
457
- if (options.warmCdnReadinessRetries !== void 0) parseNonNegativeIntegerArg(String(options.warmCdnReadinessRetries), "--warm-cdn-readiness-retries");
458
- if (options.warmCdnReadinessProbes !== void 0) parsePositiveIntegerArg(String(options.warmCdnReadinessProbes), "--warm-cdn-readiness-probes");
459
- if (options.warmCdnReadinessProbeDelay !== void 0) validateTimerDelay(options.warmCdnReadinessProbeDelay, "--warm-cdn-readiness-probe-delay");
592
+ if (options.warmCdnDiscoveryTimeout !== void 0) parsePositiveIntegerArg(String(options.warmCdnDiscoveryTimeout), "--warm-cache-discovery-timeout");
593
+ if (options.warmCdnDiscoveryRetries !== void 0) parseNonNegativeIntegerArg(String(options.warmCdnDiscoveryRetries), "--warm-cache-discovery-retries");
594
+ if (options.warmCdnProbeTimeout !== void 0) parsePositiveIntegerArg(String(options.warmCdnProbeTimeout), "--warm-cache-probe-timeout");
595
+ if (options.warmCdnProbeRetries !== void 0) parseNonNegativeIntegerArg(String(options.warmCdnProbeRetries), "--warm-cache-probe-retries");
596
+ if (options.warmCdnReadinessTimeout !== void 0) parsePositiveIntegerArg(String(options.warmCdnReadinessTimeout), "--warm-cache-readiness-timeout");
597
+ if (options.warmCdnReadinessRetries !== void 0) parseNonNegativeIntegerArg(String(options.warmCdnReadinessRetries), "--warm-cache-readiness-retries");
598
+ if (options.warmCdnReadinessProbes !== void 0) parsePositiveIntegerArg(String(options.warmCdnReadinessProbes), "--warm-cache-readiness-probes");
599
+ if (options.warmCdnReadinessProbeDelay !== void 0) validateTimerDelay(options.warmCdnReadinessProbeDelay, "--warm-cache-readiness-probe-delay");
460
600
  if (options.warmCdnPromotionDelay !== void 0) validatePromotionDelay(options.warmCdnPromotionDelay);
461
601
  if (options.cacheabilityProbe) return deployWithCacheabilityProbe(root, options);
462
602
  return deployUploadedVersionWithCdnWarmup(root, paths, options);
@@ -481,7 +621,7 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
481
621
  if (plan.paths.length === 0 && plan.pagesDataPaths.length === 0 && (plan.routeHandlerPaths?.length ?? 0) === 0 || expectedBuildId !== void 0) return plan;
482
622
  if (!allowUnverifiedPromotion) {
483
623
  const warmupKind = plan.paths.length > 0 ? "CDN HTML warmup" : plan.pagesDataPaths.length > 0 ? "CDN Pages data warmup" : "CDN Route Handler warmup";
484
- throw new Error(`${warmupKind} requires a CDN adapter that declares build-identity response headers. Configure that adapter capability or deploy without --experimental-warm-cdn-cache.`);
624
+ throw new Error(`${warmupKind} requires a CDN adapter that declares build-identity response headers. Configure that adapter capability or deploy without --warm-cache.`);
485
625
  }
486
626
  console.warn(` CDN warmup: skipping ${plan.paths.length} HTML, ${plan.pagesDataPaths.length} Pages data, and ${plan.routeHandlerPaths?.length ?? 0} Route Handler request(s) because the CDN adapter does not declare build-identity response headers.`);
487
627
  return {
@@ -493,10 +633,11 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
493
633
  };
494
634
  const discoverWarmPlan = async (targetUrl, headers) => {
495
635
  if (!options.discoverWarmPlan) return;
496
- const plan = await options.discoverWarmPlan({
636
+ const discovered = await options.discoverWarmPlan({
497
637
  headers,
498
638
  targetUrl
499
639
  });
640
+ const plan = options.selectWarmPlan?.(discovered) ?? discovered;
500
641
  deploymentId = plan.deploymentId;
501
642
  expectedBuildId = plan.buildIdentity;
502
643
  expectedRscBuildId = plan.rscBuildId;
@@ -512,7 +653,8 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
512
653
  warmPlanDiscovered = true;
513
654
  };
514
655
  if (!options.discoverWarmPlan || hasPreparedWarmPlan) remainingWarmPlan = prepareWarmPlan(remainingWarmPlan);
515
- const upload = options.uploadedVersion ?? runWranglerVersionUpload(root, options);
656
+ const upload = options.uploadedVersion ?? runVersionUpload(root, options);
657
+ const wranglerOptions = resolveDeploymentControlPlaneOptions(options, upload);
516
658
  const warmUploadedVersion = (targetUrl, headers, propagatingTarget = false, plan = {
517
659
  loadingShellPaths: remainingWarmPlan.loadingShellPaths,
518
660
  pagesDataPaths: remainingWarmPlan.pagesDataPaths,
@@ -541,11 +683,17 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
541
683
  strict: requireCacheHit || !allowUnverifiedPromotion,
542
684
  statusSource: options.statusSource
543
685
  });
544
- const wranglerConfig = parseWranglerConfig(root, options.config);
545
- const deploymentStatus = runWranglerDeploymentStatus(root, options);
546
- if (options.expectedDeploymentState && !deploymentStateEquals(deploymentStatus, options.expectedDeploymentState)) throw new Error("Two-stage CDN warming stopped because Worker deployment traffic or deployment identity changed before the final version could be staged. No final version was promoted.");
686
+ const wranglerConfig = options.deploymentTool === "cf" ? null : parseWranglerConfig(root, options.config);
687
+ let deploymentStatus;
688
+ try {
689
+ deploymentStatus = runDeploymentStatus(root, wranglerOptions);
690
+ } catch (error) {
691
+ if (!hasPreparedWarmPlan) throw error;
692
+ throw new StagedWarmupError(formatUnknownError(error), { cause: error });
693
+ }
694
+ if (options.expectedDeploymentState && !deploymentStateEquals(deploymentStatus, options.expectedDeploymentState)) throw new StagedWarmupError("Two-stage CDN warming stopped because Worker deployment traffic or deployment identity changed before the final version could be staged. No final version was promoted.");
547
695
  const stagingTraffic = getZeroPercentStagingTraffic(deploymentStatus, upload.versionId);
548
- if (hasPreparedWarmPlan && !stagingTraffic) throw new Error("Two-stage CDN warming stopped because Worker deployment traffic changed before the final version could be staged. No final version was promoted.");
696
+ if (hasPreparedWarmPlan && !stagingTraffic) throw new StagedWarmupError("Two-stage CDN warming stopped because Worker deployment traffic changed before the final version could be staged. No final version was promoted.");
549
697
  let staged = null;
550
698
  let triggersDeployedUrl = options.triggersDeployedUrl ?? null;
551
699
  let stagedCacheFilled = false;
@@ -554,14 +702,14 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
554
702
  let triggersApplied = options.triggersAlreadyApplied === true;
555
703
  function applyTriggers() {
556
704
  if (triggersApplied) return;
557
- triggersDeployedUrl = runWranglerTriggersDeploy(root, options).deployedUrl;
705
+ triggersDeployedUrl = runTriggersDeploy(root, wranglerOptions).deployedUrl;
558
706
  triggersApplied = true;
559
707
  }
560
708
  if (stagingTraffic) {
561
709
  try {
562
- staged = runWranglerVersionDeploy(root, stagingTraffic, options, "stage");
710
+ staged = runVersionDeploy(root, stagingTraffic, wranglerOptions, "stage");
563
711
  } catch (error) {
564
- throw reconcileVersionDeployFailure(root, options, error, {
712
+ throw reconcileVersionDeployFailure(root, wranglerOptions, error, {
565
713
  desiredTraffic: stagingTraffic,
566
714
  desiredDescription: "The uploaded version is staged at 0% with the previous version still serving 100% traffic; Worker triggers/routes were not changed.",
567
715
  priorState: deploymentStatus,
@@ -570,7 +718,7 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
570
718
  }
571
719
  try {
572
720
  if (hasPreparedWarmPlan) {
573
- stagedDeploymentState = runWranglerDeploymentStatus(root, options);
721
+ stagedDeploymentState = runDeploymentStatus(root, wranglerOptions);
574
722
  if (!deploymentTrafficEquals(stagedDeploymentState.versions, stagingTraffic)) throw new Error("Two-stage CDN warming stopped because Worker deployment traffic changed before production triggers could be applied. No final version was promoted.");
575
723
  }
576
724
  applyTriggers();
@@ -578,7 +726,7 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
578
726
  throw withStagedVersionCleanupNote(error);
579
727
  }
580
728
  const targetUrl = resolveCdnWarmupTargetUrl(root, triggersDeployedUrl, options) ?? staged.deployedUrl;
581
- const headers = buildVersionOverrideHeaders(options.name ?? upload.workerName ?? resolveWorkerNameForVersionOverride(wranglerConfig, options), upload.versionId);
729
+ const headers = buildVersionOverrideHeaders(options.name ?? upload.workerName ?? resolveWorkerNameForVersionOverride(wranglerConfig, wranglerOptions), upload.versionId);
582
730
  if (targetUrl && headers) try {
583
731
  if (!warmPlanDiscovered) {
584
732
  console.log(" CDN warmup: discovering paths from the staged Worker version...");
@@ -620,7 +768,7 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
620
768
  console.log(" CDN warmup: staged Worker version is stable.");
621
769
  const warmResult = await warmUploadedVersion(targetUrl, headers, true, stagedWarmPlan);
622
770
  const optionalSkipped = options.optionalWarmTargetKeys ? warmResult.skippedTargets.filter((target) => options.optionalWarmTargetKeys.has(cdnWarmTargetKey(target))).length : 0;
623
- if (hasPreparedWarmPlan && options.warmCdnCertify) {
771
+ if ((hasPreparedWarmPlan || options.selectWarmPlan) && options.warmCdnCertify) {
624
772
  if (warmResult.warmed + optionalSkipped !== stagedWarmRequests) throw new Error(`CDN warmup cannot certify the staged cache because only ${warmResult.warmed}/${stagedWarmRequests - optionalSkipped} cacheable entries completed their initial fill.`);
625
773
  }
626
774
  const requiredSkipped = warmResult.skipped - optionalSkipped;
@@ -637,7 +785,7 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
637
785
  routeHandlerPaths: warmResult.retryPlan.routeHandlerPaths,
638
786
  rscPaths: warmResult.retryPlan.rscPaths
639
787
  };
640
- if (hasPreparedWarmPlan && options.warmCdnCertify && warmResult.warmed > 0) {
788
+ if (options.warmCdnCertify && warmResult.warmed > 0) {
641
789
  console.log(` CDN warmup: certifying ${warmResult.warmed} staged cache entr${warmResult.warmed === 1 ? "y" : "ies"} before promotion...`);
642
790
  const certification = await warmUploadedVersion(targetUrl, headers, true, warmResult.warmedPlan, true);
643
791
  if (certification.warmed !== warmResult.warmed) throw new Error(`CDN warmup certified ${certification.warmed}/${warmResult.warmed} staged cache entries as reusable.`);
@@ -649,8 +797,8 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
649
797
  throw withStagedVersionCleanupNote(error);
650
798
  }
651
799
  else if (initialWarmRequests > 0) {
652
- const message = "CDN warmup failed: pre-traffic warmup needs a production URL and Worker name for version overrides. Configure a route/custom domain and Worker name, or deploy without --experimental-warm-cdn-cache.";
653
- if (!allowUnverifiedPromotion) throw new Error(`${message} ${getStagedVersionCleanupNote()}`);
800
+ const message = "CDN warmup failed: pre-traffic warmup needs a production URL and Worker name for version overrides. Configure a route/custom domain and Worker name, or deploy without --warm-cache.";
801
+ if (!allowUnverifiedPromotion) throw new StagedWarmupError(`${message} ${getStagedVersionCleanupNote()}`);
654
802
  console.warn(` ${message} Promoting because the dangerous override is enabled.`);
655
803
  }
656
804
  } else {
@@ -661,23 +809,26 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
661
809
  }
662
810
  console.warn(" CDN warmup: pre-traffic version override skipped because the current deployment is not one version serving 100% traffic.");
663
811
  }
812
+ if (options.selectWarmPlan && options.warmCdnCertify && !stagedCacheFilled) {
813
+ const error = /* @__PURE__ */ new Error("CDN warmup cannot succeed because no cache entries were certified.");
814
+ throw staged ? withStagedVersionCleanupNote(error) : error;
815
+ }
664
816
  const countRemainingWarmRequests = () => remainingWarmPlan.paths.length + remainingWarmPlan.pagesDataPaths.length + (remainingWarmPlan.routeHandlerPaths?.length ?? 0) + remainingWarmPlan.rscPaths.length + remainingWarmPlan.loadingShellPaths.length;
665
817
  if (options.warmCdnPromote === false) {
666
818
  if (!staged) throw new Error("CDN warmup cannot skip promotion because the uploaded Worker version could not be staged at 0% traffic. The current deployment must have exactly one version serving 100% traffic.");
667
819
  const remainingWarmRequests = countRemainingWarmRequests();
668
- if (!hasPreparedWarmPlan && options.discoverWarmPlan && discoveredWarmRequests === 0) throw withStagedVersionCleanupNote(/* @__PURE__ */ new Error("CDN warmup cannot skip promotion because no build-discovered requests were found to warm."));
820
+ if (!options.allowEmptyWarmPlan && !hasPreparedWarmPlan && options.discoverWarmPlan && discoveredWarmRequests === 0) throw withStagedVersionCleanupNote(/* @__PURE__ */ new Error("CDN warmup cannot skip promotion because no build-discovered requests were found to warm."));
669
821
  if (remainingWarmRequests > 0) throw withStagedVersionCleanupNote(/* @__PURE__ */ new Error(`CDN warmup cannot skip promotion because ${remainingWarmRequests} request(s) remain unwarmed.`));
670
822
  if (hasPreparedWarmPlan && stagedDeploymentState) {
671
823
  let currentDeployment;
672
824
  try {
673
- currentDeployment = runWranglerDeploymentStatus(root, options);
825
+ currentDeployment = runDeploymentStatus(root, wranglerOptions);
674
826
  } catch (error) {
675
- const message = error instanceof Error ? error.message : String(error);
676
- throw new Error(`${message} CDN warmup cannot confirm that the uploaded Worker remains staged at 0% traffic; production Worker triggers/routes remain applied. Inspect \`wrangler deployments status\` before continuing.`, { cause: error });
827
+ throw new StagedWarmupError(`${error instanceof Error ? error.message : String(error)} CDN warmup cannot confirm that the uploaded Worker remains staged at 0% traffic; production Worker triggers/routes remain applied. Inspect the active Worker deployment before continuing.`, { cause: error });
677
828
  }
678
- if (!deploymentStateEquals(currentDeployment, stagedDeploymentState)) throw new Error("Two-stage CDN warming stopped because Worker deployment traffic or deployment identity changed before the no-promote handoff. The uploaded Worker is not confirmed staged at 0% traffic; production Worker triggers/routes remain applied.");
829
+ if (!deploymentStateEquals(currentDeployment, stagedDeploymentState)) throw new StagedWarmupError("Two-stage CDN warming stopped because Worker deployment traffic or deployment identity changed before the no-promote handoff. The uploaded Worker is not confirmed staged at 0% traffic; production Worker triggers/routes remain applied.");
679
830
  }
680
- console.log(" CDN warmup: promotion disabled; uploaded Worker version remains staged at 0% traffic and production Worker triggers/routes remain applied.");
831
+ console.log(` CDN warmup: promotion disabled; uploaded Worker version ${upload.versionId} remains staged at 0% traffic and production Worker triggers/routes remain applied.`);
681
832
  return staged.deployedUrl ?? triggersDeployedUrl ?? upload.previewUrl ?? "(URL not detected in wrangler output)";
682
833
  }
683
834
  let deployed;
@@ -696,13 +847,13 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
696
847
  }
697
848
  }
698
849
  if (hasPreparedWarmPlan && stagingTraffic) {
699
- prePromotionState = runWranglerDeploymentStatus(root, options);
850
+ prePromotionState = runDeploymentStatus(root, wranglerOptions);
700
851
  if (!deploymentTrafficEquals(prePromotionState.versions, stagingTraffic) || stagedDeploymentState && !deploymentStateEquals(prePromotionState, stagedDeploymentState)) throw new Error("Two-stage CDN warming stopped because Worker deployment traffic or deployment identity changed before the final version could be promoted. No final version was promoted.");
701
852
  }
702
853
  promotionAttempted = true;
703
- deployed = runWranglerVersionDeploy(root, promotionTraffic, options, stagedCacheFilled ? "promote-warmed" : "promote-uploaded");
854
+ deployed = runVersionDeploy(root, promotionTraffic, wranglerOptions, stagedCacheFilled ? "promote-warmed" : "promote-uploaded");
704
855
  } catch (error) {
705
- if (promotionAttempted) throw reconcileVersionDeployFailure(root, options, error, {
856
+ if (promotionAttempted) throw reconcileVersionDeployFailure(root, wranglerOptions, error, {
706
857
  desiredTraffic: promotionTraffic,
707
858
  desiredDescription: "The uploaded version is already promoted to 100%; Worker triggers/routes may already have changed.",
708
859
  priorState: prePromotionState ?? (stagingTraffic ? {
@@ -734,7 +885,7 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
734
885
  } catch (error) {
735
886
  throw withPromotedVersionWarmupNote(error);
736
887
  }
737
- else if (!allowUnverifiedPromotion) throw withPromotedVersionWarmupNote(/* @__PURE__ */ new Error("CDN warmup failed: no production URL could be inferred from wrangler config or output. Configure a route/custom domain, ensure Wrangler prints a workers.dev URL, or deploy without --experimental-warm-cdn-cache."));
888
+ else if (!allowUnverifiedPromotion) throw withPromotedVersionWarmupNote(/* @__PURE__ */ new Error("CDN warmup failed: no production URL could be inferred from wrangler config or output. Configure a route/custom domain, ensure Wrangler prints a workers.dev URL, or deploy without --warm-cache."));
738
889
  else console.warn(" CDN warmup skipped: no production URL could be inferred after promotion; the dangerous override was enabled.");
739
890
  }
740
891
  return deployed.deployedUrl ?? triggersDeployedUrl ?? staged?.deployedUrl ?? upload.previewUrl ?? "(URL not detected in wrangler output)";
@@ -752,31 +903,36 @@ function deploymentStateEquals(actual, expected) {
752
903
  function reconcileVersionDeployFailure(root, options, error, expected) {
753
904
  const message = error instanceof Error ? error.message : String(error);
754
905
  let reconciliation;
906
+ let mayNeedCleanup = true;
755
907
  try {
756
- const current = runWranglerDeploymentStatus(root, options);
908
+ const current = runDeploymentStatus(root, options);
757
909
  if (deploymentTrafficEquals(current.versions, expected.desiredTraffic)) reconciliation = expected.desiredDescription;
758
- else if (deploymentStateEquals(current, expected.priorState)) reconciliation = expected.priorDescription;
759
- else reconciliation = "Worker deployment state differs from both the requested and prior state, so the deployment outcome is unknown or another deployment changed it.";
910
+ else if (deploymentStateEquals(current, expected.priorState)) {
911
+ reconciliation = expected.priorDescription;
912
+ mayNeedCleanup = false;
913
+ } else reconciliation = "Worker deployment state differs from both the requested and prior state, so the deployment outcome is unknown or another deployment changed it.";
760
914
  } catch (reconciliationError) {
761
915
  reconciliation = `Worker deployment status could not be read after the command failed, so the deployment outcome is unknown (${reconciliationError instanceof Error ? reconciliationError.message : String(reconciliationError)}).`;
762
916
  }
763
- return new Error(`${message} ${reconciliation}`, { cause: error });
917
+ const detail = `${message} ${reconciliation}`;
918
+ return mayNeedCleanup ? new StagedWarmupError(detail, { cause: error }) : new Error(detail, { cause: error });
764
919
  }
765
920
  function assertDeploymentStateUnchanged(root, options, expected, message) {
766
- if (!deploymentStateEquals(runWranglerDeploymentStatus(root, options), expected)) throw new Error(message);
921
+ if (!deploymentStateEquals(runDeploymentStatus(root, options), expected)) throw new Error(message);
767
922
  }
768
923
  async function deployWithCacheabilityProbe(root, options) {
769
924
  if (!options.discoverWarmPlan) throw new Error("Two-stage CDN warming requires staged Worker route discovery before cacheability probing.");
770
925
  const prerenderSecret = readPrerenderSecret(root);
771
- const probeUpload = runWranglerVersionUpload(root, options);
772
- const initialDeployment = runWranglerDeploymentStatus(root, options);
926
+ const probeUpload = runVersionUpload(root, options);
927
+ const wranglerOptions = resolveDeploymentControlPlaneOptions(options, probeUpload);
928
+ const initialDeployment = runDeploymentStatus(root, wranglerOptions);
773
929
  const probeTraffic = getZeroPercentStagingTraffic(initialDeployment, probeUpload.versionId);
774
930
  if (!probeTraffic) throw new Error("Two-stage CDN warming cannot stage the probe Worker at 0% because the current deployment is not exactly one version serving 100% traffic. No traffic or triggers were changed.");
775
931
  let stagedProbe;
776
932
  try {
777
- stagedProbe = runWranglerVersionDeploy(root, probeTraffic, options, "stage");
933
+ stagedProbe = runVersionDeploy(root, probeTraffic, wranglerOptions, "stage");
778
934
  } catch (error) {
779
- throw reconcileVersionDeployFailure(root, options, error, {
935
+ throw reconcileVersionDeployFailure(root, wranglerOptions, error, {
780
936
  desiredTraffic: probeTraffic,
781
937
  desiredDescription: "The probe version is staged at 0% with the previous version still serving 100% traffic; production Worker triggers/routes were not changed.",
782
938
  priorState: initialDeployment,
@@ -785,17 +941,16 @@ async function deployWithCacheabilityProbe(root, options) {
785
941
  }
786
942
  let stagedProbeDeployment;
787
943
  try {
788
- stagedProbeDeployment = runWranglerDeploymentStatus(root, options);
944
+ stagedProbeDeployment = runDeploymentStatus(root, wranglerOptions);
789
945
  if (!deploymentTrafficEquals(stagedProbeDeployment.versions, probeTraffic)) throw new Error("Two-stage CDN warming stopped because Worker deployment traffic changed immediately after the probe version was staged.");
790
946
  } catch (error) {
791
- const message = error instanceof Error ? error.message : String(error);
792
- throw new Error(`${message} The probe staging command completed, but its current deployment state could not be confirmed; production Worker triggers/routes were not changed. Inspect \`wrangler deployments status\` and remove the staged probe version if necessary.`, { cause: error });
947
+ throw new StagedWarmupError(`${error instanceof Error ? error.message : String(error)} The probe staging command completed, but its current deployment state could not be confirmed; production Worker triggers/routes were not changed. Inspect the active Worker deployment and remove the staged probe version if necessary.`, { cause: error });
793
948
  }
794
949
  let prepared;
795
950
  try {
796
951
  const targetUrl = resolveCdnWarmupTargetUrl(root, stagedProbe.deployedUrl ?? probeUpload.previewUrl, options);
797
- const wranglerConfig = parseWranglerConfig(root, options.config);
798
- const headers = buildVersionOverrideHeaders(options.name ?? probeUpload.workerName ?? resolveWorkerNameForVersionOverride(wranglerConfig, options), probeUpload.versionId);
952
+ const wranglerConfig = options.deploymentTool === "cf" ? null : parseWranglerConfig(root, wranglerOptions.config);
953
+ const headers = buildVersionOverrideHeaders(options.name ?? probeUpload.workerName ?? resolveWorkerNameForVersionOverride(wranglerConfig, wranglerOptions), probeUpload.versionId);
799
954
  if (!targetUrl || !headers) throw new Error("Two-stage CDN warming needs a production URL and Worker name for version-overridden cacheability probes.");
800
955
  console.log(" CDN warmup: discovering paths from the staged probe Worker...");
801
956
  const discovered = await options.discoverWarmPlan({
@@ -861,6 +1016,7 @@ async function deployWithCacheabilityProbe(root, options) {
861
1016
  concurrency: options.warmCdnConcurrency,
862
1017
  expectedResponseBuildId: plan.buildIdentity,
863
1018
  fallbackRoutePatterns: plan.fallbackRoutePatterns,
1019
+ loadingBoundaryRoutePatterns: plan.loadingBoundaryRoutePatterns,
864
1020
  phaseTimeoutMs: options.warmCdnProbeTimeout ?? 12e4,
865
1021
  retries: options.warmCdnProbeRetries ?? options.warmCdnRetries ?? 2,
866
1022
  retryDelayMs: DEFAULT_CACHEABILITY_PROBE_RETRY_DELAY_MS,
@@ -885,19 +1041,17 @@ async function deployWithCacheabilityProbe(root, options) {
885
1041
  routeHandlerPaths: probe.cacheableTargets.filter((target) => target.kind === "app-route").map((target) => target.sourcePathname),
886
1042
  rscPaths: probe.cacheableTargets.filter((target) => target.kind === "rsc-full").map((target) => target.sourcePathname)
887
1043
  };
888
- assertDeploymentStateUnchanged(root, options, stagedProbeDeployment, "Two-stage CDN warming stopped because Worker deployment traffic or deployment identity changed while cacheability was being probed. No final version was promoted.");
889
- const finalConfig = writeCacheabilityManifestArtifact(root, options.config, probe.manifest);
890
- const finalUpload = runWranglerVersionUpload(root, {
891
- config: finalConfig,
892
- env: options.env,
893
- name: options.name,
894
- preview: options.preview,
895
- verbose: options.verbose
1044
+ const selectedFinalPlan = options.selectWarmPlan?.(finalPlan) ?? finalPlan;
1045
+ assertDeploymentStateUnchanged(root, wranglerOptions, stagedProbeDeployment, "Two-stage CDN warming stopped because Worker deployment traffic or deployment identity changed while cacheability was being probed. No final version was promoted.");
1046
+ const finalConfig = writeCacheabilityManifestArtifact(root, options.config, probe.manifest, options.deploymentTool);
1047
+ const finalUpload = runVersionUpload(root, {
1048
+ ...options,
1049
+ config: options.deploymentTool === "cf" ? options.config : finalConfig
896
1050
  });
897
1051
  prepared = {
898
1052
  optionalWarmTargetKeys: new Set(probe.speculativeTargets.map(cdnWarmTargetKey)),
899
1053
  prerenderSecret,
900
- plan: finalPlan,
1054
+ plan: selectedFinalPlan,
901
1055
  upload: finalUpload
902
1056
  };
903
1057
  } catch (error) {
@@ -934,6 +1088,15 @@ function getZeroPercentStagingTraffic(deployment, versionId) {
934
1088
  function getWranglerTargetEnv(options) {
935
1089
  return options.env || (options.preview ? "preview" : void 0);
936
1090
  }
1091
+ function resolveDeploymentControlPlaneOptions(options, upload) {
1092
+ if (options.deploymentTool !== "cf") return options;
1093
+ const name = upload.workerName ?? options.name;
1094
+ if (!name) throw new Error("Could not detect the uploaded Worker name needed for staged CDN warming.");
1095
+ return {
1096
+ ...options,
1097
+ name
1098
+ };
1099
+ }
937
1100
  function resolveWorkerNameForVersionOverride(config, options) {
938
1101
  if (options.name) return options.name;
939
1102
  const env = getWranglerTargetEnv(options);
@@ -952,32 +1115,35 @@ function buildVersionOverrideHeaders(workerName, versionId) {
952
1115
  };
953
1116
  }
954
1117
  function withStagedVersionCleanupNote(error) {
955
- const message = error instanceof Error ? error.message : String(error);
956
- return new Error(`${message} ${getStagedVersionCleanupNote()}`, { cause: error });
1118
+ return new StagedWarmupError(`${error instanceof Error ? error.message : String(error)} ${getStagedVersionCleanupNote()}`, { cause: error });
957
1119
  }
958
1120
  function withStagedProbeVersionCleanupNote(error) {
959
- const message = error instanceof Error ? error.message : String(error);
960
- return new Error(`${message} The probe version may remain staged at 0% with the previous version still serving 100% traffic; production Worker triggers/routes were not changed.`, { cause: error });
1121
+ return new StagedWarmupError(`${error instanceof Error ? error.message : String(error)} The probe version may remain staged at 0% with the previous version still serving 100% traffic; production Worker triggers/routes were not changed.`, { cause: error });
961
1122
  }
962
1123
  function getStagedVersionCleanupNote() {
963
- return "The uploaded version may remain staged at 0% with the previous version still serving 100% traffic; Worker triggers/routes may also have changed because trigger deployment runs before warming. Rerun deploy to promote it or use `wrangler versions deploy` to choose the desired version split.";
1124
+ return "The uploaded version may remain staged at 0% with the previous version still serving 100% traffic; Worker triggers/routes may also have changed because trigger deployment runs before warming. Rerun deploy to promote it or create a deployment with the desired version split.";
964
1125
  }
965
1126
  function withPromotedVersionTriggerNote(error) {
966
1127
  const message = error instanceof Error ? error.message : String(error);
967
- return new Error(`${message} The uploaded version may already be promoted to 100%, but Worker triggers/routes may not be updated; rerun deploy or \`wrangler triggers deploy\` after fixing the trigger error.`, { cause: error });
1128
+ return new Error(`${message} The uploaded version may already be promoted to 100%, but Worker triggers/routes may not be updated; rerun deploy or apply the Worker triggers after fixing the trigger error.`, { cause: error });
968
1129
  }
969
1130
  function withPromotedVersionWarmupNote(error) {
970
1131
  const message = error instanceof Error ? error.message : String(error);
971
- return new Error(`${message} The uploaded version is already promoted to 100% and its Worker triggers/routes were updated; rerun deploy to retry cache warming or roll back with \`wrangler versions deploy\`.`, { cause: error });
1132
+ return new Error(`${message} The uploaded version is already promoted to 100% and its Worker triggers/routes were updated; rerun deploy to retry cache warming or create a deployment for the previous version.`, { cause: error });
972
1133
  }
973
1134
  async function deploy(options) {
974
- if (options.warmCdnTarget !== void 0 && !options.warmCdnCache) throw new Error("--warm-cdn-target requires --experimental-warm-cdn-cache.");
1135
+ if (options.warmCdnTarget !== void 0 && !options.warmCdnCache && !options.experimentalTPR) throw new Error("--warm-cache-target requires --warm-cache or --traffic-aware-warm-cache.");
975
1136
  const warmCdnTarget = options.warmCdnTarget === void 0 ? void 0 : validateCdnWarmTarget(options.warmCdnTarget);
976
- const deployEnv = validateWranglerEnvName(options.env || (options.preview ? "preview" : "production"));
1137
+ const deployEnv = options.env || (options.preview ? "preview" : void 0);
1138
+ if (deployEnv) validateWranglerEnvName(deployEnv);
977
1139
  const root = path.resolve(options.root);
1140
+ const deploymentTool = resolveDeploymentTool(root);
1141
+ if (deploymentTool === "cf" && options.name) throw new Error("--name is not supported for cloudflare.config.ts projects. Set `name` in cloudflare.config.ts instead.");
1142
+ const viteMode = resolveViteBuildMode(deploymentTool, deployEnv);
1143
+ const wranglerFallbackEnv = deploymentTool === "cf" ? void 0 : deployEnv;
978
1144
  loadDotenv({
979
1145
  root,
980
- mode: "production"
1146
+ mode: viteMode
981
1147
  });
982
1148
  console.log("\n vinext-cloudflare deploy\n");
983
1149
  const info = detectProject(root);
@@ -995,20 +1161,20 @@ async function deploy(options) {
995
1161
  if (missingScaffolding.length > 0) throw new Error(`Missing Cloudflare deployment setup: ${missingScaffolding.join(", ")}. Run \`vinext init --platform=cloudflare\` first.`);
996
1162
  const missingDeps = getMissingDeps(info);
997
1163
  if (missingDeps.length > 0) throw new Error(`Missing deployment dependencies: ${missingDeps.map((dependency) => dependency.name).join(", ")}. Run \`vinext init --platform=cloudflare\` first.`);
1164
+ if (deploymentTool === "cf" && !isCfCliInstalled(root)) throw new Error("Missing deployment dependencies: cf. Run `vinext init --platform=cloudflare` first.");
998
1165
  if (info.hasViteConfig && !viteConfigHasCloudflarePlugin(root)) throw new Error(formatMissingCloudflarePluginError({ isAppRouter: info.isAppRouter }));
999
- if (info.hasISR && !viteConfigHasCacheAdapter(root) && !workerEntryHasCacheHandler(root)) throw new Error(formatMissingCacheAdapterError({}));
1166
+ if (info.hasISR && !viteConfigHasCacheAdapter(root) && !workerEntryHasCacheHandler(root)) throw new Error(formatMissingCacheAdapterError({ typedConfig: deploymentTool === "cf" }));
1000
1167
  if (!viteConfigHasImageAdapter(root)) {
1001
1168
  console.log();
1002
- console.log(formatImageOptimizationHint());
1169
+ console.log(formatImageOptimizationHint(deploymentTool === "cf"));
1003
1170
  }
1004
1171
  if (options.dryRun) {
1005
1172
  console.log("\n Dry run complete. No build or deploy performed.\n");
1006
1173
  return;
1007
1174
  }
1008
- const buildEnv = deployEnv === "production" && !options.env ? void 0 : deployEnv;
1009
- const viteConfigMetadata = await withCloudflareEnv(buildEnv, () => loadDeployViteConfigMetadata(info.root));
1175
+ const viteConfigMetadata = await withCloudflareEnv(deployEnv, () => loadDeployViteConfigMetadata(info.root, viteMode));
1010
1176
  const cdnAdapterConfig = resolveCdnAdapterConfig(viteConfigMetadata.cacheConfig);
1011
- const nextConfig = await withCloudflareEnv(buildEnv, async () => {
1177
+ const nextConfig = await withCloudflareEnv(deployEnv, async () => {
1012
1178
  const inlineNextConfig = viteConfigMetadata.nextConfig;
1013
1179
  const rawNextConfig = inlineNextConfig ? await resolveNextConfigInput(inlineNextConfig, PHASE_PRODUCTION_BUILD) : await loadNextConfig(info.root, PHASE_PRODUCTION_BUILD);
1014
1180
  return resolveNextConfig(rawNextConfig, info.root);
@@ -1019,35 +1185,82 @@ async function deploy(options) {
1019
1185
  vinextPrerenderConfig,
1020
1186
  nextOutput: nextConfig.output
1021
1187
  });
1188
+ const shouldPrerenderLocally = Boolean(prerenderDecision && (nextConfig.output === "export" || hasCacheAdapterPrerenderOutput(viteConfigMetadata.cacheConfig)));
1022
1189
  const hasStrictResponseVary = hasVerbatimResponseVary(viteConfigMetadata.cacheConfig);
1023
- const hasStagedRequestRouting = hasUncachedRequestRouting(viteConfigMetadata.cacheConfig);
1190
+ const warmupStatusSource = cacheWarmupStatusSource(viteConfigMetadata.cacheConfig);
1191
+ const hasStagedRequestRouting = hasUncachedRequestRouting(viteConfigMetadata.cacheConfig) || warmupStatusSource === "data-cache";
1024
1192
  const hasBuildIdentityHeader = hasBuildIdentityResponseHeader(viteConfigMetadata.cacheConfig);
1025
1193
  const hasCanonicalRscWarmup = supportsCanonicalRscWarmup(viteConfigMetadata.cacheConfig);
1026
- const hasVinextCacheWarmupStatus = usesVinextCacheWarmupStatus(viteConfigMetadata.cacheConfig);
1027
1194
  const needsCacheabilityProbeManifest = projectRequiresRouteCacheabilityProbeManifest(info, viteConfigMetadata.cacheConfig);
1028
- const shouldEmitPrerenderPathManifest = !options.skipBuild && prerenderDecision;
1029
- if (!options.skipBuild) await runBuild(info, buildEnv);
1195
+ const shouldEmitPrerenderPathManifest = !options.skipBuild && shouldPrerenderLocally;
1196
+ if (prerenderDecision && !shouldPrerenderLocally) {
1197
+ const trigger = prerenderDecision.reason === "flag" ? "--prerender-all" : "vinext prerender config";
1198
+ const replacement = options.warmCdnCache ? "Routes will be rendered and warmed through the staged Worker instead." : "Use --warm-cache to render and warm routes through the deployed Worker instead.";
1199
+ console.warn(`\n Warning: ${trigger} is ignored by Cloudflare deploy. ${replacement}`);
1200
+ }
1201
+ if (!options.skipBuild) await runBuild(info, deployEnv, viteMode);
1030
1202
  else console.log("\n Skipping build (--skip-build)");
1031
- if (options.warmCdnCache && cdnAdapterConfig) {
1032
- const wrangler = await loadProjectWranglerApi(info.root);
1033
- const previousCwd = process.cwd();
1203
+ const prerenderOutputDirs = resolvePrerenderOutputDirs(info.root, deploymentTool, viteConfigMetadata.routeRootConfig);
1204
+ const canWarmTpr = options.experimentalTPR && !shouldPrerenderLocally && hasBuildIdentityHeader;
1205
+ if (options.experimentalTPR && shouldPrerenderLocally) console.log(" TPR: Skipping route selection (all-route prerendering configured)");
1206
+ else if (options.experimentalTPR && !hasBuildIdentityHeader) console.log(workerEntryHasCacheHandler(root) ? " TPR: Skipping pre-warm (legacy imperative cache handlers must migrate to declarative vinext({ cache }) for standard warming)" : " TPR: Skipping pre-warm (configured cache does not expose build identity)");
1207
+ const tpr = canWarmTpr ? await resolveTPRRoutes({
1208
+ root,
1209
+ config: options.config,
1210
+ env: wranglerFallbackEnv,
1211
+ hostname: warmCdnTarget ? new URL(warmCdnTarget).hostname : void 0,
1212
+ typedConfig: deploymentTool === "cf",
1213
+ window: Math.max(1, options.tprWindow ?? 24)
1214
+ }) : null;
1215
+ if (tpr?.skipped) console.log(` TPR: Skipped (${tpr.skipped})`);
1216
+ const tprRoutes = tpr?.routes ?? [];
1217
+ const wranglerOptions = {
1218
+ env: deploymentTool === "cf" ? viteMode : deployEnv,
1219
+ name: deploymentTool === "cf" ? readBuildOutputWorkerName(root) : options.name,
1220
+ config: options.config,
1221
+ verbose: options.verbose,
1222
+ deploymentTool
1223
+ };
1224
+ let shouldWarmTpr = tprRoutes.length > 0;
1225
+ if (shouldWarmTpr && !options.warmCdnCache) {
1034
1226
  try {
1035
- process.chdir(info.root);
1036
- const config = wrangler.unstable_readConfig({
1037
- config: options.config,
1038
- env: buildEnv
1039
- }, {
1040
- hideWarnings: true,
1041
- preserveOriginalMain: true,
1042
- useRedirectIfAvailable: true
1043
- });
1044
- assertCdnVersionMetadataConfig({
1045
- binding: cdnAdapterConfig.versionMetadataBinding,
1046
- configuredBinding: config.version_metadata?.binding,
1047
- configPath: config.configPath
1048
- });
1049
- } finally {
1050
- process.chdir(previousCwd);
1227
+ shouldWarmTpr = getZeroPercentStagingTraffic(runDeploymentStatus(root, wranglerOptions), "tpr-preflight") !== null;
1228
+ } catch {
1229
+ shouldWarmTpr = false;
1230
+ }
1231
+ if (!shouldWarmTpr) console.log(" TPR: Skipping pre-warm (current deployment cannot be staged safely)");
1232
+ }
1233
+ const shouldWarmCdnCache = options.warmCdnCache || shouldWarmTpr;
1234
+ if (options.warmCdnCertify && !shouldWarmCdnCache) throw new Error("Cannot certify traffic-aware warming because pre-warming was skipped.");
1235
+ const shouldSelectTpr = shouldWarmTpr && !options.warmCdnCache;
1236
+ const candidatePathsOnly = shouldSelectTpr && !needsCacheabilityProbeManifest;
1237
+ if (shouldWarmCdnCache && cdnAdapterConfig) {
1238
+ if (deploymentTool === "cf") {
1239
+ const configPath = path.join(root, ".cloudflare/output/v0/workers/default/worker.config.json");
1240
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
1241
+ const binding = cdnAdapterConfig.versionMetadataBinding;
1242
+ if (config.env?.[binding]?.type !== "version-metadata") throw new Error(`[vinext] Cloudflare CDN warmup requires version metadata binding ${JSON.stringify(binding)} in the generated Build Output Worker config ${configPath}.`);
1243
+ } else {
1244
+ const wrangler = await loadProjectWranglerApi(info.root);
1245
+ const previousCwd = process.cwd();
1246
+ try {
1247
+ process.chdir(info.root);
1248
+ const config = wrangler.unstable_readConfig({
1249
+ config: options.config,
1250
+ env: wranglerFallbackEnv
1251
+ }, {
1252
+ hideWarnings: true,
1253
+ preserveOriginalMain: true,
1254
+ useRedirectIfAvailable: true
1255
+ });
1256
+ assertCdnVersionMetadataConfig({
1257
+ binding: cdnAdapterConfig.versionMetadataBinding,
1258
+ configuredBinding: config.version_metadata?.binding,
1259
+ configPath: config.configPath
1260
+ });
1261
+ } finally {
1262
+ process.chdir(previousCwd);
1263
+ }
1051
1264
  }
1052
1265
  }
1053
1266
  if (shouldEmitPrerenderPathManifest) await emitPrerenderPathManifest({
@@ -1057,100 +1270,102 @@ async function deploy(options) {
1057
1270
  requestRouting: hasStagedRequestRouting ? "uncached-stage" : void 0,
1058
1271
  responseVary: hasStrictResponseVary ? "verbatim" : void 0,
1059
1272
  isResponsePolicyHeader: (name) => isConfiguredCdnResponsePolicyHeader(viteConfigMetadata.cacheConfig, name),
1060
- routeRootConfig: viteConfigMetadata.routeRootConfig
1273
+ routeRootConfig: prerenderOutputDirs
1061
1274
  });
1062
- let ranPrerender = false;
1063
- if (prerenderDecision) {
1275
+ let prerenderResult = void 0;
1276
+ if (shouldPrerenderLocally && prerenderDecision) {
1064
1277
  console.log(`\n ${formatVinextPrerenderLabel(prerenderDecision)}`);
1065
1278
  if (nextConfig.enablePrerenderSourceMaps) {
1066
1279
  process.setSourceMapsEnabled(true);
1067
1280
  Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 50);
1068
1281
  }
1069
- await runPrerender({
1282
+ prerenderResult = await runPrerender({
1070
1283
  root: info.root,
1071
- concurrency: options.prerenderConcurrency,
1284
+ concurrency: options.prerenderConcurrency ?? viteConfigMetadata.prerenderConfig?.concurrency,
1072
1285
  nextConfig,
1073
- routeRootConfig: viteConfigMetadata.routeRootConfig
1286
+ routeRootConfig: prerenderOutputDirs
1287
+ });
1288
+ if (nextConfig.output !== "export") await finalizeCacheAdapterPrerenderOutput(viteConfigMetadata.cacheConfig, info.root, { clientOutDir: prerenderOutputDirs?.clientOutDir });
1289
+ }
1290
+ if (!options.skipBuild) {
1291
+ await printBuildReport({
1292
+ root: info.root,
1293
+ pageExtensions: nextConfig.pageExtensions,
1294
+ prerenderResult: prerenderResult ?? void 0
1074
1295
  });
1075
- ranPrerender = true;
1296
+ console.log("\n Build complete.\n");
1076
1297
  }
1077
- if (ranPrerender) try {
1078
- await populateKVCacheFromPrerenderedArtifacts(root, deployEnv === "production" && !options.env ? void 0 : deployEnv, viteConfigMetadata.cacheConfig);
1298
+ let url;
1299
+ if (shouldWarmCdnCache) try {
1300
+ url = await deployWithCdnWarmup(root, [], {
1301
+ ...wranglerOptions,
1302
+ allowEmptyWarmPlan: tprRoutes.length > 0 && !options.warmCdnCache,
1303
+ cacheabilityProbe: needsCacheabilityProbeManifest,
1304
+ discoverWarmPlan: async ({ headers, targetUrl }) => {
1305
+ const discovery = await discoverPrerenderPathManifest({
1306
+ root: info.root,
1307
+ candidatePaths: tprRoutes.map(({ path }) => path),
1308
+ candidatePathsOnly,
1309
+ nextConfig,
1310
+ buildIdentity: hasBuildIdentityHeader ? "response-header" : void 0,
1311
+ includeCanonicalRsc: hasCanonicalRscWarmup,
1312
+ requestRouting: hasStagedRequestRouting ? "uncached-stage" : void 0,
1313
+ responseVary: hasStrictResponseVary ? "verbatim" : void 0,
1314
+ isResponsePolicyHeader: (name) => isConfiguredCdnResponsePolicyHeader(viteConfigMetadata.cacheConfig, name),
1315
+ routeRootConfig: viteConfigMetadata.routeRootConfig,
1316
+ pathDiscoveryTarget: {
1317
+ baseUrl: targetUrl,
1318
+ headers,
1319
+ phaseTimeoutMs: options.warmCdnDiscoveryTimeout ?? DEFAULT_REMOTE_PATH_DISCOVERY_PHASE_TIMEOUT_MS,
1320
+ retries: options.warmCdnDiscoveryRetries,
1321
+ retryDelayMs: DEFAULT_REMOTE_PATH_DISCOVERY_RETRY_DELAY_MS
1322
+ }
1323
+ });
1324
+ if (!discovery) return {
1325
+ loadingShellPaths: [],
1326
+ paths: [],
1327
+ rscPaths: []
1328
+ };
1329
+ return createPrerenderWarmPlan(root, discovery, {
1330
+ includeCanonicalRsc: hasCanonicalRscWarmup,
1331
+ includeFallbackShells: options.warmCdnIncludeFallbacks,
1332
+ strict: options.warmCdnCertify === true || !options.dangerouslyPromoteOnCdnWarmError
1333
+ });
1334
+ },
1335
+ selectWarmPlan: shouldSelectTpr ? (plan) => selectTPRWarmPlan(plan, tprRoutes, Math.max(1, Math.min(100, options.tprCoverage ?? 90)), Math.max(1, options.tprLimit ?? 1e3)) : void 0,
1336
+ statusSource: warmupStatusSource,
1337
+ warmCdnConcurrency: options.warmCdnConcurrency,
1338
+ warmCdnTarget: warmCdnTarget ?? tpr?.targetUrl,
1339
+ warmCdnTimeout: options.warmCdnTimeout,
1340
+ warmCdnRetries: options.warmCdnRetries,
1341
+ warmCdnDiscoveryTimeout: options.warmCdnDiscoveryTimeout,
1342
+ warmCdnDiscoveryRetries: options.warmCdnDiscoveryRetries,
1343
+ warmCdnProbeTimeout: options.warmCdnProbeTimeout,
1344
+ warmCdnProbeRetries: options.warmCdnProbeRetries,
1345
+ warmCdnCertify: options.warmCdnCertify,
1346
+ warmCdnReadinessTimeout: options.warmCdnReadinessTimeout,
1347
+ warmCdnReadinessRetries: options.warmCdnReadinessRetries,
1348
+ warmCdnReadinessProbes: options.warmCdnReadinessProbes,
1349
+ warmCdnReadinessProbeDelay: options.warmCdnReadinessProbeDelay,
1350
+ dangerouslyPromoteOnCdnWarmError: options.dangerouslyPromoteOnCdnWarmError,
1351
+ warmCdnPromote: options.warmCdnPromote,
1352
+ warmCdnPromotionDelay: options.warmCdnPromotionDelay
1353
+ });
1079
1354
  } catch (error) {
1080
- console.log(` KV cache: Skipping prerender upload (${formatUnknownError(error)}). Continuing with deploy.`);
1355
+ if (options.warmCdnCache || options.warmCdnCertify || options.warmCdnPromote === false && error instanceof StagedWarmupError) throw error;
1356
+ console.log(` TPR: Skipping pre-warm (${formatUnknownError(error)}). Continuing with deploy.`);
1081
1357
  }
1082
- if (options.experimentalTPR) {
1083
- console.log();
1084
- const tprResult = await runTPR({
1085
- root,
1086
- config: options.config,
1087
- coverage: Math.max(1, Math.min(100, options.tprCoverage ?? 90)),
1088
- limit: Math.max(1, options.tprLimit ?? 1e3),
1089
- window: Math.max(1, options.tprWindow ?? 24)
1358
+ if (url === void 0) {
1359
+ if (deploymentTool === "cf" && options.warmCdnPromote === false) url = runCfVersionUpload(root, wranglerOptions).previewUrl ?? "(Preview URL not detected in cf output)";
1360
+ else if (deploymentTool === "cf") url = await runCfDeploy(root, wranglerOptions);
1361
+ else url = await runWranglerDeploy(root, {
1362
+ ...wranglerOptions,
1363
+ promote: options.warmCdnPromote
1090
1364
  });
1091
- if (tprResult.skipped) console.log(` TPR: Skipped (${tprResult.skipped})`);
1092
1365
  }
1093
- const wranglerOptions = {
1094
- env: deployEnv === "production" && !options.env ? void 0 : deployEnv,
1095
- name: options.name,
1096
- config: options.config,
1097
- verbose: options.verbose
1098
- };
1099
- let url;
1100
- if (options.warmCdnCache) url = await deployWithCdnWarmup(root, [], {
1101
- ...wranglerOptions,
1102
- cacheabilityProbe: needsCacheabilityProbeManifest,
1103
- discoverWarmPlan: async ({ headers, targetUrl }) => {
1104
- const discovery = await discoverPrerenderPathManifest({
1105
- root: info.root,
1106
- nextConfig,
1107
- buildIdentity: hasBuildIdentityHeader ? "response-header" : void 0,
1108
- includeCanonicalRsc: hasCanonicalRscWarmup,
1109
- requestRouting: hasStagedRequestRouting ? "uncached-stage" : void 0,
1110
- responseVary: hasStrictResponseVary ? "verbatim" : void 0,
1111
- isResponsePolicyHeader: (name) => isConfiguredCdnResponsePolicyHeader(viteConfigMetadata.cacheConfig, name),
1112
- routeRootConfig: viteConfigMetadata.routeRootConfig,
1113
- pathDiscoveryTarget: {
1114
- baseUrl: targetUrl,
1115
- headers,
1116
- phaseTimeoutMs: options.warmCdnDiscoveryTimeout ?? DEFAULT_REMOTE_PATH_DISCOVERY_PHASE_TIMEOUT_MS,
1117
- retries: options.warmCdnDiscoveryRetries,
1118
- retryDelayMs: DEFAULT_REMOTE_PATH_DISCOVERY_RETRY_DELAY_MS
1119
- }
1120
- });
1121
- if (!discovery) return {
1122
- loadingShellPaths: [],
1123
- paths: [],
1124
- rscPaths: []
1125
- };
1126
- return createPrerenderWarmPlan(root, discovery, {
1127
- includeCanonicalRsc: hasCanonicalRscWarmup,
1128
- includeFallbackShells: options.warmCdnIncludeFallbacks,
1129
- strict: options.warmCdnCertify === true || !options.dangerouslyPromoteOnCdnWarmError
1130
- });
1131
- },
1132
- statusSource: hasVinextCacheWarmupStatus ? "vinext" : "cloudflare",
1133
- warmCdnConcurrency: options.warmCdnConcurrency,
1134
- warmCdnTarget,
1135
- warmCdnTimeout: options.warmCdnTimeout,
1136
- warmCdnRetries: options.warmCdnRetries,
1137
- warmCdnDiscoveryTimeout: options.warmCdnDiscoveryTimeout,
1138
- warmCdnDiscoveryRetries: options.warmCdnDiscoveryRetries,
1139
- warmCdnProbeTimeout: options.warmCdnProbeTimeout,
1140
- warmCdnProbeRetries: options.warmCdnProbeRetries,
1141
- warmCdnCertify: options.warmCdnCertify,
1142
- warmCdnReadinessTimeout: options.warmCdnReadinessTimeout,
1143
- warmCdnReadinessRetries: options.warmCdnReadinessRetries,
1144
- warmCdnReadinessProbes: options.warmCdnReadinessProbes,
1145
- warmCdnReadinessProbeDelay: options.warmCdnReadinessProbeDelay,
1146
- dangerouslyPromoteOnCdnWarmError: options.dangerouslyPromoteOnCdnWarmError,
1147
- warmCdnPromote: options.warmCdnPromote,
1148
- warmCdnPromotionDelay: options.warmCdnPromotionDelay
1149
- });
1150
- else url = await runWranglerDeploy(root, wranglerOptions);
1151
1366
  console.log("\n ─────────────────────────────────────────");
1152
- console.log(` Deployed to: ${url}`);
1367
+ console.log(` ${options.warmCdnPromote === false ? "Version URL" : "Deployed to"}: ${url}`);
1153
1368
  console.log(" ─────────────────────────────────────────\n");
1154
1369
  }
1155
1370
  //#endregion
1156
- export { DEFAULT_CDN_WARM_PROMOTION_DELAY_MS, buildNodeCliInvocation, buildVersionOverrideHeaders, buildWranglerDeployArgs, buildWranglerInvocation, buildWranglerKVBulkPutArgs, deploy, deployWithCdnWarmup, getZeroPercentStagingTraffic, hasCdnWarmRequests, parseDeployArgs, projectRequiresRouteCacheabilityProbeManifest, resolveCdnWarmupTargetUrl, resolveWorkerNameForVersionOverride, resolveWranglerBin, runWranglerDeploy, runWranglerKVBulkPut, validateWranglerEnvName, withCloudflareEnv };
1371
+ export { DEFAULT_CDN_WARM_PROMOTION_DELAY_MS, buildCfDeployArgs, buildNodeCliInvocation, buildVersionOverrideHeaders, buildWranglerDeployArgs, buildWranglerInvocation, deploy, deployWithCdnWarmup, getZeroPercentStagingTraffic, hasCdnWarmRequests, isCfCliInstalled, parseDeployArgs, projectRequiresRouteCacheabilityProbeManifest, resolveCdnWarmupTargetUrl, resolveCfBin, resolveDeploymentControlPlaneOptions, resolveDeploymentTool, resolvePrerenderOutputDirs, resolveViteBuildMode, resolveWorkerNameForVersionOverride, resolveWranglerBin, runCfDeploy, runWranglerDeploy, selectTPRWarmPlan, validateWranglerEnvName, withCloudflareEnv };