@vinext/cloudflare 1.0.0-beta.7 → 1.0.0-beta.9

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 (56) hide show
  1. package/README.md +64 -22
  2. package/dist/cache/cdn-adapter-config.d.ts +16 -8
  3. package/dist/cache/cdn-adapter-config.js +57 -17
  4. package/dist/cache/cdn-adapter.d.ts +25 -24
  5. package/dist/cache/cdn-adapter.js +24 -20
  6. package/dist/cache/cdn-adapter.runtime.d.ts +9 -3
  7. package/dist/cache/cdn-adapter.runtime.js +63 -40
  8. package/dist/cache/cdn-adapter.worker.d.ts +31 -0
  9. package/dist/cache/cdn-adapter.worker.js +373 -0
  10. package/dist/cache/cdn-build-id.d.ts +4 -3
  11. package/dist/cache/cdn-build-id.js +5 -1
  12. package/dist/cache/kv-data-adapter.d.ts +25 -5
  13. package/dist/cache/kv-data-adapter.js +5 -1
  14. package/dist/cache/kv-data-adapter.runtime.d.ts +38 -4
  15. package/dist/cache/kv-data-adapter.runtime.js +115 -41
  16. package/dist/cache/kv-key.d.ts +4 -5
  17. package/dist/cache/response-store-adapter.d.ts +70 -0
  18. package/dist/cache/response-store-adapter.js +45 -0
  19. package/dist/cache/response-store-adapter.self-contained.worker.d.ts +18 -0
  20. package/dist/cache/response-store-adapter.self-contained.worker.js +12 -0
  21. package/dist/cache/response-store-adapter.service-binding.worker.d.ts +16 -0
  22. package/dist/cache/response-store-adapter.service-binding.worker.js +11 -0
  23. package/dist/cache/response-store-adapter.worker.d.ts +14 -0
  24. package/dist/cache/response-store-adapter.worker.js +264 -0
  25. package/dist/cache/response-store-cdn.runtime.d.ts +5 -0
  26. package/dist/cache/response-store-cdn.runtime.js +41 -0
  27. package/dist/cache/response-store-data.runtime.d.ts +34 -0
  28. package/dist/cache/response-store-data.runtime.js +289 -0
  29. package/dist/cacheability-artifact.d.ts +2 -3
  30. package/dist/cacheability-artifact.js +27 -1
  31. package/dist/cacheability-manifest-limits.d.ts +3 -4
  32. package/dist/cacheability-manifest-limits.js +1 -1
  33. package/dist/cacheability-probe.d.ts +8 -9
  34. package/dist/cacheability-probe.js +255 -91
  35. package/dist/cdn-warm.d.ts +31 -25
  36. package/dist/cdn-warm.js +228 -54
  37. package/dist/deploy-config.d.ts +11 -12
  38. package/dist/deploy-config.js +8 -3
  39. package/dist/deploy-help.d.ts +3 -4
  40. package/dist/deploy-help.js +20 -14
  41. package/dist/deploy.d.ts +35 -28
  42. package/dist/deploy.js +204 -95
  43. package/dist/images/images-optimizer.d.ts +3 -4
  44. package/dist/prerender-kv-populate.d.ts +3 -4
  45. package/dist/prerender-kv-populate.js +3 -2
  46. package/dist/tpr.d.ts +26 -74
  47. package/dist/tpr.js +45 -603
  48. package/dist/utils/cache-control-metadata.d.ts +3 -4
  49. package/dist/version-deploy.d.ts +17 -17
  50. package/dist/version-deploy.js +10 -5
  51. package/dist/worker-deployment-url.d.ts +3 -4
  52. package/dist/workers-dev-url.d.ts +2 -3
  53. package/dist/wrangler-config.d.ts +19 -0
  54. package/dist/wrangler-config.js +294 -0
  55. package/dist/wrangler-version-metadata.d.ts +2 -3
  56. package/package.json +13 -11
package/dist/deploy.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { writeCacheabilityManifestArtifact } from "./cacheability-artifact.js";
2
2
  import { DEFAULT_CACHEABILITY_PROBE_RETRY_DELAY_MS, probeStagedWorkerCacheability, readPrerenderSecret } from "./cacheability-probe.js";
3
- import { CdnOperationProgress, createCdnWarmTargets, readPrerenderWarmPlan, waitForCdnWarmTargetReadiness, warmCdnCache } from "./cdn-warm.js";
4
- import { parseWranglerConfig, runTPR } from "./tpr.js";
3
+ import { CdnOperationProgress, createCdnWarmTargets, createPrerenderWarmPlan, waitForCdnWarmTargetReadiness, warmCdnCache } from "./cdn-warm.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
7
  import { formatImageOptimizationHint, formatMissingCacheAdapterError, resolveCdnAdapterConfig, resolveKvDataAdapterConfig, viteConfigHasCacheAdapter, viteConfigHasCloudflarePlugin, viteConfigHasImageAdapter, workerEntryHasCacheHandler } from "./deploy-config.js";
7
8
  import { assertCdnVersionMetadataConfig } from "./wrangler-version-metadata.js";
@@ -13,7 +14,8 @@ import fs from "node:fs";
13
14
  import path from "node:path";
14
15
  import { cacheabilityRoutePathname } from "vinext/internal/server/cacheability-manifest";
15
16
  import { setTimeout } from "node:timers/promises";
16
- import { DEFAULT_REMOTE_PATH_DISCOVERY_PHASE_TIMEOUT_MS, DEFAULT_REMOTE_PATH_DISCOVERY_RETRY_DELAY_MS, emitPrerenderPathManifest } from "vinext/internal/build/prerender-paths";
17
+ import { DEFAULT_REMOTE_PATH_DISCOVERY_PHASE_TIMEOUT_MS, DEFAULT_REMOTE_PATH_DISCOVERY_RETRY_DELAY_MS, discoverPrerenderPathManifest, emitPrerenderPathManifest } from "vinext/internal/build/prerender-paths";
18
+ import { normalizePathTrailingSlash } from "vinext/shims/url-utils";
17
19
  import os from "node:os";
18
20
  import { spawn } from "node:child_process";
19
21
  import { parseArgs } from "node:util";
@@ -21,7 +23,7 @@ import { pathToFileURL } from "node:url";
21
23
  import { runPrerender } from "vinext/internal/build/run-prerender";
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, hasVerbatimResponseVary, requiresRouteCacheabilityProbeManifest, resolveVinextPrerenderDecision } from "vinext/internal/config/prerender";
26
+ import { cacheWarmupStatusSource, findVinextCacheConfigInPlugins, findVinextPrerenderConfigInPlugins, findVinextRouteRootConfigInPlugins, formatVinextPrerenderLabel, hasBuildIdentityResponseHeader, 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
@@ -71,6 +73,7 @@ function formatUnknownError(error) {
71
73
  if (error instanceof Error && error.message) return error.message;
72
74
  return String(error);
73
75
  }
76
+ var StagedWarmupError = class extends Error {};
74
77
  /** Deploy command flag definitions for util.parseArgs. */
75
78
  const deployArgOptions = {
76
79
  help: {
@@ -126,6 +129,10 @@ const deployArgOptions = {
126
129
  type: "boolean",
127
130
  default: false
128
131
  },
132
+ "no-promote": {
133
+ type: "boolean",
134
+ default: false
135
+ },
129
136
  "warm-cdn-no-promote": {
130
137
  type: "boolean",
131
138
  default: false
@@ -135,6 +142,13 @@ const deployArgOptions = {
135
142
  type: "boolean",
136
143
  default: false
137
144
  },
145
+ "experimental-traffic-aware-warm-cache": {
146
+ type: "boolean",
147
+ default: false
148
+ },
149
+ "traffic-aware-coverage": { type: "string" },
150
+ "traffic-aware-limit": { type: "string" },
151
+ "traffic-aware-window": { type: "string" },
138
152
  "experimental-tpr": {
139
153
  type: "boolean",
140
154
  default: false
@@ -186,13 +200,13 @@ function parseDeployArgs(args) {
186
200
  warmCdnReadinessProbes: values["warm-cdn-readiness-probes"] === void 0 ? void 0 : parsePositiveIntegerArg(values["warm-cdn-readiness-probes"], "--warm-cdn-readiness-probes"),
187
201
  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
202
  dangerouslyPromoteOnCdnWarmError: values["dangerously-promote-on-cdn-warm-error"],
189
- warmCdnPromote: !values["warm-cdn-no-promote"],
203
+ warmCdnPromote: !values["no-promote"] && !values["warm-cdn-no-promote"],
190
204
  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
205
  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"])
206
+ experimentalTPR: values["experimental-traffic-aware-warm-cache"] || values["experimental-tpr"],
207
+ tprCoverage: parseIntArg("traffic-aware-coverage", values["traffic-aware-coverage"] ?? values["tpr-coverage"]),
208
+ tprLimit: parseIntArg("traffic-aware-limit", values["traffic-aware-limit"] ?? values["tpr-limit"]),
209
+ tprWindow: parseIntArg("traffic-aware-window", values["traffic-aware-window"] ?? values["tpr-window"])
196
210
  };
197
211
  }
198
212
  /**
@@ -234,7 +248,8 @@ async function loadProjectWranglerApi(root) {
234
248
  try {
235
249
  wranglerPath = req.resolve("wrangler");
236
250
  } catch {
237
- wranglerPath = createRequire(req.resolve("@cloudflare/vite-plugin")).resolve("wrangler");
251
+ const cloudflarePluginPath = req.resolve("@cloudflare/vite-plugin");
252
+ wranglerPath = createRequire(cloudflarePluginPath).resolve("wrangler");
238
253
  }
239
254
  return await import(
240
255
  /* @vite-ignore */
@@ -386,6 +401,7 @@ async function runWranglerKVBulkPut(root, options, execute = spawn, nodeExecutab
386
401
  }
387
402
  }
388
403
  async function runWranglerDeploy(root, options, execute = spawn) {
404
+ if (options.promote === false) return runWranglerVersionUpload(root, options).previewUrl ?? "(Preview URL not detected in wrangler output)";
389
405
  const spawnOptions = {
390
406
  cwd: root,
391
407
  stdio: [
@@ -426,12 +442,50 @@ async function runWranglerDeploy(root, options, execute = spawn) {
426
442
  function hasCdnWarmRequests(plan) {
427
443
  return plan.paths.length + (plan.pagesDataPaths?.length ?? 0) + (plan.routeHandlerPaths?.length ?? 0) + plan.rscPaths.length + plan.loadingShellPaths.length > 0;
428
444
  }
445
+ function selectTPRWarmPlan(plan, traffic, coverage, limit) {
446
+ const canonical = (pathname) => normalizePathTrailingSlash(pathname, false);
447
+ const resolved = new Set(Object.entries(plan.routePatterns ?? {}).map(([pathname, route]) => canonical(route.cacheabilityProbe?.concretePathname ?? pathname)));
448
+ const requestsByPath = /* @__PURE__ */ new Map();
449
+ for (const { path, requests } of traffic) {
450
+ const pathname = canonical(path);
451
+ if (!resolved.has(pathname)) continue;
452
+ requestsByPath.set(pathname, (requestsByPath.get(pathname) ?? 0) + requests);
453
+ }
454
+ const selected = new Set(selectRoutes(Array.from(requestsByPath, ([path, requests]) => ({
455
+ path,
456
+ requests
457
+ })).sort((a, b) => b.requests - a.requests), coverage, limit).routes.map(({ path }) => path));
458
+ const includes = (pathname) => selected.has(canonical(plan.routePatterns?.[pathname]?.cacheabilityProbe?.concretePathname ?? pathname));
459
+ const filter = (pathnames) => pathnames?.filter(includes);
460
+ return {
461
+ ...plan,
462
+ appPaths: filter(plan.appPaths),
463
+ loadingShellPaths: filter(plan.loadingShellPaths) ?? [],
464
+ pagesDataPaths: filter(plan.pagesDataPaths),
465
+ pagesPaths: filter(plan.pagesPaths),
466
+ paths: filter(plan.paths) ?? [],
467
+ routeHandlerPaths: filter(plan.routeHandlerPaths),
468
+ routePatterns: plan.routePatterns ? Object.fromEntries(Object.entries(plan.routePatterns).filter(([pathname]) => includes(pathname))) : void 0,
469
+ rscPaths: filter(plan.rscPaths) ?? []
470
+ };
471
+ }
429
472
  function projectRequiresRouteCacheabilityProbeManifest(project, cacheConfig) {
430
473
  return (project.isAppRouter || project.isPagesRouter) && requiresRouteCacheabilityProbeManifest(cacheConfig);
431
474
  }
432
475
  function cdnWarmTargetKey(target) {
433
476
  return `${target.kind}\0${target.sourcePathname}`;
434
477
  }
478
+ function requiredCdnWarmTargetKeys(plan, optionalTargetKeys) {
479
+ const keys = /* @__PURE__ */ new Set([
480
+ ...plan.paths.map((pathname) => `html\0${pathname}`),
481
+ ...plan.pagesDataPaths.map((pathname) => `pages-data\0${pathname}`),
482
+ ...(plan.routeHandlerPaths ?? []).map((pathname) => `app-route\0${pathname}`),
483
+ ...plan.rscPaths.map((pathname) => `rsc-full\0${pathname}`),
484
+ ...plan.loadingShellPaths.map((pathname) => `rsc-loading-shell\0${pathname}`)
485
+ ]);
486
+ if (optionalTargetKeys) for (const key of optionalTargetKeys) keys.delete(key);
487
+ return keys;
488
+ }
435
489
  async function deployWithCdnWarmup(root, paths, options) {
436
490
  if (options.warmCdnTarget !== void 0) options = {
437
491
  ...options,
@@ -481,10 +535,11 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
481
535
  };
482
536
  const discoverWarmPlan = async (targetUrl, headers) => {
483
537
  if (!options.discoverWarmPlan) return;
484
- const plan = await options.discoverWarmPlan({
538
+ const discovered = await options.discoverWarmPlan({
485
539
  headers,
486
540
  targetUrl
487
541
  });
542
+ const plan = options.selectWarmPlan?.(discovered) ?? discovered;
488
543
  deploymentId = plan.deploymentId;
489
544
  expectedBuildId = plan.buildIdentity;
490
545
  expectedRscBuildId = plan.rscBuildId;
@@ -524,14 +579,22 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
524
579
  concurrency: options.warmCdnConcurrency,
525
580
  timeoutMs: options.warmCdnTimeout,
526
581
  retries: options.warmCdnRetries,
582
+ retrySkippedTargetKeys: propagatingTarget && hasPreparedWarmPlan ? requiredCdnWarmTargetKeys(plan, options.optionalWarmTargetKeys) : void 0,
527
583
  requireCacheHit,
528
- strict: requireCacheHit || !allowUnverifiedPromotion
584
+ strict: requireCacheHit || !allowUnverifiedPromotion,
585
+ statusSource: options.statusSource
529
586
  });
530
587
  const wranglerConfig = parseWranglerConfig(root, options.config);
531
- const deploymentStatus = runWranglerDeploymentStatus(root, options);
532
- 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.");
588
+ let deploymentStatus;
589
+ try {
590
+ deploymentStatus = runWranglerDeploymentStatus(root, options);
591
+ } catch (error) {
592
+ if (!hasPreparedWarmPlan) throw error;
593
+ throw new StagedWarmupError(formatUnknownError(error), { cause: error });
594
+ }
595
+ 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.");
533
596
  const stagingTraffic = getZeroPercentStagingTraffic(deploymentStatus, upload.versionId);
534
- 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.");
597
+ 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.");
535
598
  let staged = null;
536
599
  let triggersDeployedUrl = options.triggersDeployedUrl ?? null;
537
600
  let stagedCacheFilled = false;
@@ -577,6 +640,7 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
577
640
  pagesDataPaths: remainingWarmPlan.pagesDataPaths,
578
641
  paths: remainingWarmPlan.paths,
579
642
  routeHandlerPaths: remainingWarmPlan.routeHandlerPaths,
643
+ routePatterns: remainingWarmPlan.routePatterns,
580
644
  rscPaths: remainingWarmPlan.rscPaths
581
645
  };
582
646
  const stagedWarmRequests = stagedWarmPlan.paths.length + stagedWarmPlan.pagesDataPaths.length + (stagedWarmPlan.routeHandlerPaths?.length ?? 0) + stagedWarmPlan.rscPaths.length + stagedWarmPlan.loadingShellPaths.length;
@@ -622,7 +686,7 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
622
686
  routeHandlerPaths: warmResult.retryPlan.routeHandlerPaths,
623
687
  rscPaths: warmResult.retryPlan.rscPaths
624
688
  };
625
- if (hasPreparedWarmPlan && options.warmCdnCertify && warmResult.warmed > 0) {
689
+ if (options.warmCdnCertify && warmResult.warmed > 0) {
626
690
  console.log(` CDN warmup: certifying ${warmResult.warmed} staged cache entr${warmResult.warmed === 1 ? "y" : "ies"} before promotion...`);
627
691
  const certification = await warmUploadedVersion(targetUrl, headers, true, warmResult.warmedPlan, true);
628
692
  if (certification.warmed !== warmResult.warmed) throw new Error(`CDN warmup certified ${certification.warmed}/${warmResult.warmed} staged cache entries as reusable.`);
@@ -635,7 +699,7 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
635
699
  }
636
700
  else if (initialWarmRequests > 0) {
637
701
  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.";
638
- if (!allowUnverifiedPromotion) throw new Error(`${message} ${getStagedVersionCleanupNote()}`);
702
+ if (!allowUnverifiedPromotion) throw new StagedWarmupError(`${message} ${getStagedVersionCleanupNote()}`);
639
703
  console.warn(` ${message} Promoting because the dangerous override is enabled.`);
640
704
  }
641
705
  } else {
@@ -650,19 +714,18 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
650
714
  if (options.warmCdnPromote === false) {
651
715
  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.");
652
716
  const remainingWarmRequests = countRemainingWarmRequests();
653
- 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."));
717
+ 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."));
654
718
  if (remainingWarmRequests > 0) throw withStagedVersionCleanupNote(/* @__PURE__ */ new Error(`CDN warmup cannot skip promotion because ${remainingWarmRequests} request(s) remain unwarmed.`));
655
719
  if (hasPreparedWarmPlan && stagedDeploymentState) {
656
720
  let currentDeployment;
657
721
  try {
658
722
  currentDeployment = runWranglerDeploymentStatus(root, options);
659
723
  } catch (error) {
660
- const message = error instanceof Error ? error.message : String(error);
661
- 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 });
724
+ 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 \`wrangler deployments status\` before continuing.`, { cause: error });
662
725
  }
663
- 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.");
726
+ 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.");
664
727
  }
665
- console.log(" CDN warmup: promotion disabled; uploaded Worker version remains staged at 0% traffic and production Worker triggers/routes remain applied.");
728
+ console.log(` CDN warmup: promotion disabled; uploaded Worker version ${upload.versionId} remains staged at 0% traffic and production Worker triggers/routes remain applied.`);
666
729
  return staged.deployedUrl ?? triggersDeployedUrl ?? upload.previewUrl ?? "(URL not detected in wrangler output)";
667
730
  }
668
731
  let deployed;
@@ -673,7 +736,7 @@ async function deployUploadedVersionWithCdnWarmup(root, paths, options) {
673
736
  }];
674
737
  let promotionAttempted = false;
675
738
  try {
676
- if (stagedCacheFilled) {
739
+ if (stagedCacheFilled && options.statusSource !== "vinext") {
677
740
  const promotionDelay = options.warmCdnPromotionDelay ?? 15e3;
678
741
  if (promotionDelay > 0) {
679
742
  console.log(` CDN warmup: waiting ${promotionDelay / 1e3} seconds for cache propagation before promotion...`);
@@ -737,15 +800,19 @@ function deploymentStateEquals(actual, expected) {
737
800
  function reconcileVersionDeployFailure(root, options, error, expected) {
738
801
  const message = error instanceof Error ? error.message : String(error);
739
802
  let reconciliation;
803
+ let mayNeedCleanup = true;
740
804
  try {
741
805
  const current = runWranglerDeploymentStatus(root, options);
742
806
  if (deploymentTrafficEquals(current.versions, expected.desiredTraffic)) reconciliation = expected.desiredDescription;
743
- else if (deploymentStateEquals(current, expected.priorState)) reconciliation = expected.priorDescription;
744
- else reconciliation = "Worker deployment state differs from both the requested and prior state, so the deployment outcome is unknown or another deployment changed it.";
807
+ else if (deploymentStateEquals(current, expected.priorState)) {
808
+ reconciliation = expected.priorDescription;
809
+ mayNeedCleanup = false;
810
+ } else reconciliation = "Worker deployment state differs from both the requested and prior state, so the deployment outcome is unknown or another deployment changed it.";
745
811
  } catch (reconciliationError) {
746
812
  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)}).`;
747
813
  }
748
- return new Error(`${message} ${reconciliation}`, { cause: error });
814
+ const detail = `${message} ${reconciliation}`;
815
+ return mayNeedCleanup ? new StagedWarmupError(detail, { cause: error }) : new Error(detail, { cause: error });
749
816
  }
750
817
  function assertDeploymentStateUnchanged(root, options, expected, message) {
751
818
  if (!deploymentStateEquals(runWranglerDeploymentStatus(root, options), expected)) throw new Error(message);
@@ -773,8 +840,7 @@ async function deployWithCacheabilityProbe(root, options) {
773
840
  stagedProbeDeployment = runWranglerDeploymentStatus(root, options);
774
841
  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.");
775
842
  } catch (error) {
776
- const message = error instanceof Error ? error.message : String(error);
777
- 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 });
843
+ 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 \`wrangler deployments status\` and remove the staged probe version if necessary.`, { cause: error });
778
844
  }
779
845
  let prepared;
780
846
  try {
@@ -870,9 +936,11 @@ async function deployWithCacheabilityProbe(root, options) {
870
936
  routeHandlerPaths: probe.cacheableTargets.filter((target) => target.kind === "app-route").map((target) => target.sourcePathname),
871
937
  rscPaths: probe.cacheableTargets.filter((target) => target.kind === "rsc-full").map((target) => target.sourcePathname)
872
938
  };
939
+ const selectedFinalPlan = options.selectWarmPlan?.(finalPlan) ?? finalPlan;
873
940
  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.");
941
+ const finalConfig = writeCacheabilityManifestArtifact(root, options.config, probe.manifest);
874
942
  const finalUpload = runWranglerVersionUpload(root, {
875
- config: writeCacheabilityManifestArtifact(root, options.config, probe.manifest),
943
+ config: finalConfig,
876
944
  env: options.env,
877
945
  name: options.name,
878
946
  preview: options.preview,
@@ -881,7 +949,7 @@ async function deployWithCacheabilityProbe(root, options) {
881
949
  prepared = {
882
950
  optionalWarmTargetKeys: new Set(probe.speculativeTargets.map(cdnWarmTargetKey)),
883
951
  prerenderSecret,
884
- plan: finalPlan,
952
+ plan: selectedFinalPlan,
885
953
  upload: finalUpload
886
954
  };
887
955
  } catch (error) {
@@ -936,12 +1004,10 @@ function buildVersionOverrideHeaders(workerName, versionId) {
936
1004
  };
937
1005
  }
938
1006
  function withStagedVersionCleanupNote(error) {
939
- const message = error instanceof Error ? error.message : String(error);
940
- return new Error(`${message} ${getStagedVersionCleanupNote()}`, { cause: error });
1007
+ return new StagedWarmupError(`${error instanceof Error ? error.message : String(error)} ${getStagedVersionCleanupNote()}`, { cause: error });
941
1008
  }
942
1009
  function withStagedProbeVersionCleanupNote(error) {
943
- const message = error instanceof Error ? error.message : String(error);
944
- 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 });
1010
+ 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 });
945
1011
  }
946
1012
  function getStagedVersionCleanupNote() {
947
1013
  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.";
@@ -994,7 +1060,8 @@ async function deploy(options) {
994
1060
  const cdnAdapterConfig = resolveCdnAdapterConfig(viteConfigMetadata.cacheConfig);
995
1061
  const nextConfig = await withCloudflareEnv(buildEnv, async () => {
996
1062
  const inlineNextConfig = viteConfigMetadata.nextConfig;
997
- return resolveNextConfig(inlineNextConfig ? await resolveNextConfigInput(inlineNextConfig, PHASE_PRODUCTION_BUILD) : await loadNextConfig(info.root, PHASE_PRODUCTION_BUILD), info.root);
1063
+ const rawNextConfig = inlineNextConfig ? await resolveNextConfigInput(inlineNextConfig, PHASE_PRODUCTION_BUILD) : await loadNextConfig(info.root, PHASE_PRODUCTION_BUILD);
1064
+ return resolveNextConfig(rawNextConfig, info.root);
998
1065
  });
999
1066
  const vinextPrerenderConfig = !options.prerenderAll && nextConfig.output !== "export" ? viteConfigMetadata.prerenderConfig : null;
1000
1067
  const prerenderDecision = resolveVinextPrerenderDecision({
@@ -1003,12 +1070,46 @@ async function deploy(options) {
1003
1070
  nextOutput: nextConfig.output
1004
1071
  });
1005
1072
  const hasStrictResponseVary = hasVerbatimResponseVary(viteConfigMetadata.cacheConfig);
1073
+ const warmupStatusSource = cacheWarmupStatusSource(viteConfigMetadata.cacheConfig);
1074
+ const hasStagedRequestRouting = hasUncachedRequestRouting(viteConfigMetadata.cacheConfig) || warmupStatusSource === "data-cache";
1006
1075
  const hasBuildIdentityHeader = hasBuildIdentityResponseHeader(viteConfigMetadata.cacheConfig);
1076
+ const hasCanonicalRscWarmup = supportsCanonicalRscWarmup(viteConfigMetadata.cacheConfig);
1007
1077
  const needsCacheabilityProbeManifest = projectRequiresRouteCacheabilityProbeManifest(info, viteConfigMetadata.cacheConfig);
1008
1078
  const shouldEmitPrerenderPathManifest = !options.skipBuild && prerenderDecision;
1009
1079
  if (!options.skipBuild) await runBuild(info, buildEnv);
1010
1080
  else console.log("\n Skipping build (--skip-build)");
1011
- if (options.warmCdnCache && cdnAdapterConfig) {
1081
+ const canWarmTpr = options.experimentalTPR && !prerenderDecision && hasBuildIdentityHeader;
1082
+ if (options.experimentalTPR && prerenderDecision) console.log(" TPR: Skipping route selection (all-route prerendering configured)");
1083
+ 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)");
1084
+ const tpr = canWarmTpr ? await resolveTPRRoutes({
1085
+ root,
1086
+ config: options.config,
1087
+ env: buildEnv,
1088
+ hostname: warmCdnTarget ? new URL(warmCdnTarget).hostname : void 0,
1089
+ window: Math.max(1, options.tprWindow ?? 24)
1090
+ }) : null;
1091
+ if (tpr?.skipped) console.log(` TPR: Skipped (${tpr.skipped})`);
1092
+ const tprRoutes = tpr?.routes ?? [];
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 shouldWarmTpr = tprRoutes.length > 0;
1100
+ if (shouldWarmTpr && !options.warmCdnCache) {
1101
+ try {
1102
+ shouldWarmTpr = getZeroPercentStagingTraffic(runWranglerDeploymentStatus(root, wranglerOptions), "tpr-preflight") !== null;
1103
+ } catch {
1104
+ shouldWarmTpr = false;
1105
+ }
1106
+ if (!shouldWarmTpr) console.log(" TPR: Skipping pre-warm (current deployment cannot be staged safely)");
1107
+ }
1108
+ const shouldWarmCdnCache = options.warmCdnCache || shouldWarmTpr;
1109
+ const shouldSelectTpr = shouldWarmTpr && !options.warmCdnCache;
1110
+ const candidatePathsOnly = shouldSelectTpr && !needsCacheabilityProbeManifest;
1111
+ const shouldPrerenderLocally = prerenderDecision && (!shouldWarmCdnCache || prerenderDecision.reason === "next-export");
1112
+ if (shouldWarmCdnCache && cdnAdapterConfig) {
1012
1113
  const wrangler = await loadProjectWranglerApi(info.root);
1013
1114
  const previousCwd = process.cwd();
1014
1115
  try {
@@ -1034,11 +1135,13 @@ async function deploy(options) {
1034
1135
  root: info.root,
1035
1136
  nextConfig,
1036
1137
  buildIdentity: hasBuildIdentityHeader ? "response-header" : void 0,
1138
+ requestRouting: hasStagedRequestRouting ? "uncached-stage" : void 0,
1037
1139
  responseVary: hasStrictResponseVary ? "verbatim" : void 0,
1140
+ isResponsePolicyHeader: (name) => isConfiguredCdnResponsePolicyHeader(viteConfigMetadata.cacheConfig, name),
1038
1141
  routeRootConfig: viteConfigMetadata.routeRootConfig
1039
1142
  });
1040
1143
  let ranPrerender = false;
1041
- if (prerenderDecision) {
1144
+ if (shouldPrerenderLocally) {
1042
1145
  console.log(`\n ${formatVinextPrerenderLabel(prerenderDecision)}`);
1043
1146
  if (nextConfig.enablePrerenderSourceMaps) {
1044
1147
  process.setSourceMapsEnabled(true);
@@ -1047,7 +1150,8 @@ async function deploy(options) {
1047
1150
  await runPrerender({
1048
1151
  root: info.root,
1049
1152
  concurrency: options.prerenderConcurrency,
1050
- nextConfig
1153
+ nextConfig,
1154
+ routeRootConfig: viteConfigMetadata.routeRootConfig
1051
1155
  });
1052
1156
  ranPrerender = true;
1053
1157
  }
@@ -1056,68 +1160,73 @@ async function deploy(options) {
1056
1160
  } catch (error) {
1057
1161
  console.log(` KV cache: Skipping prerender upload (${formatUnknownError(error)}). Continuing with deploy.`);
1058
1162
  }
1059
- if (options.experimentalTPR) {
1060
- console.log();
1061
- const tprResult = await runTPR({
1062
- root,
1063
- config: options.config,
1064
- coverage: Math.max(1, Math.min(100, options.tprCoverage ?? 90)),
1065
- limit: Math.max(1, options.tprLimit ?? 1e3),
1066
- window: Math.max(1, options.tprWindow ?? 24)
1163
+ let url;
1164
+ if (shouldWarmCdnCache) try {
1165
+ url = await deployWithCdnWarmup(root, [], {
1166
+ ...wranglerOptions,
1167
+ allowEmptyWarmPlan: tprRoutes.length > 0 && !options.warmCdnCache,
1168
+ cacheabilityProbe: needsCacheabilityProbeManifest,
1169
+ discoverWarmPlan: async ({ headers, targetUrl }) => {
1170
+ const discovery = await discoverPrerenderPathManifest({
1171
+ root: info.root,
1172
+ candidatePaths: tprRoutes.map(({ path }) => path),
1173
+ candidatePathsOnly,
1174
+ nextConfig,
1175
+ buildIdentity: hasBuildIdentityHeader ? "response-header" : void 0,
1176
+ includeCanonicalRsc: hasCanonicalRscWarmup,
1177
+ requestRouting: hasStagedRequestRouting ? "uncached-stage" : void 0,
1178
+ responseVary: hasStrictResponseVary ? "verbatim" : void 0,
1179
+ isResponsePolicyHeader: (name) => isConfiguredCdnResponsePolicyHeader(viteConfigMetadata.cacheConfig, name),
1180
+ routeRootConfig: viteConfigMetadata.routeRootConfig,
1181
+ pathDiscoveryTarget: {
1182
+ baseUrl: targetUrl,
1183
+ headers,
1184
+ phaseTimeoutMs: options.warmCdnDiscoveryTimeout ?? DEFAULT_REMOTE_PATH_DISCOVERY_PHASE_TIMEOUT_MS,
1185
+ retries: options.warmCdnDiscoveryRetries,
1186
+ retryDelayMs: DEFAULT_REMOTE_PATH_DISCOVERY_RETRY_DELAY_MS
1187
+ }
1188
+ });
1189
+ if (!discovery) return {
1190
+ loadingShellPaths: [],
1191
+ paths: [],
1192
+ rscPaths: []
1193
+ };
1194
+ return createPrerenderWarmPlan(root, discovery, {
1195
+ includeCanonicalRsc: hasCanonicalRscWarmup,
1196
+ includeFallbackShells: options.warmCdnIncludeFallbacks,
1197
+ strict: options.warmCdnCertify === true || !options.dangerouslyPromoteOnCdnWarmError
1198
+ });
1199
+ },
1200
+ selectWarmPlan: shouldSelectTpr ? (plan) => selectTPRWarmPlan(plan, tprRoutes, Math.max(1, Math.min(100, options.tprCoverage ?? 90)), Math.max(1, options.tprLimit ?? 1e3)) : void 0,
1201
+ statusSource: warmupStatusSource,
1202
+ warmCdnConcurrency: options.warmCdnConcurrency,
1203
+ warmCdnTarget,
1204
+ warmCdnTimeout: options.warmCdnTimeout,
1205
+ warmCdnRetries: options.warmCdnRetries,
1206
+ warmCdnDiscoveryTimeout: options.warmCdnDiscoveryTimeout,
1207
+ warmCdnDiscoveryRetries: options.warmCdnDiscoveryRetries,
1208
+ warmCdnProbeTimeout: options.warmCdnProbeTimeout,
1209
+ warmCdnProbeRetries: options.warmCdnProbeRetries,
1210
+ warmCdnCertify: options.warmCdnCertify,
1211
+ warmCdnReadinessTimeout: options.warmCdnReadinessTimeout,
1212
+ warmCdnReadinessRetries: options.warmCdnReadinessRetries,
1213
+ warmCdnReadinessProbes: options.warmCdnReadinessProbes,
1214
+ warmCdnReadinessProbeDelay: options.warmCdnReadinessProbeDelay,
1215
+ dangerouslyPromoteOnCdnWarmError: options.dangerouslyPromoteOnCdnWarmError,
1216
+ warmCdnPromote: options.warmCdnPromote,
1217
+ warmCdnPromotionDelay: options.warmCdnPromotionDelay
1067
1218
  });
1068
- if (tprResult.skipped) console.log(` TPR: Skipped (${tprResult.skipped})`);
1219
+ } catch (error) {
1220
+ if (options.warmCdnCache || options.warmCdnPromote === false && error instanceof StagedWarmupError) throw error;
1221
+ console.log(` TPR: Skipping pre-warm (${formatUnknownError(error)}). Continuing with deploy.`);
1069
1222
  }
1070
- const wranglerOptions = {
1071
- env: deployEnv === "production" && !options.env ? void 0 : deployEnv,
1072
- name: options.name,
1073
- config: options.config,
1074
- verbose: options.verbose
1075
- };
1076
- let url;
1077
- if (options.warmCdnCache) url = await deployWithCdnWarmup(root, [], {
1223
+ if (url === void 0) url = await runWranglerDeploy(root, {
1078
1224
  ...wranglerOptions,
1079
- cacheabilityProbe: needsCacheabilityProbeManifest,
1080
- discoverWarmPlan: async ({ headers, targetUrl }) => {
1081
- await emitPrerenderPathManifest({
1082
- root: info.root,
1083
- nextConfig,
1084
- buildIdentity: hasBuildIdentityHeader ? "response-header" : void 0,
1085
- responseVary: hasStrictResponseVary ? "verbatim" : void 0,
1086
- routeRootConfig: viteConfigMetadata.routeRootConfig,
1087
- pathDiscoveryTarget: {
1088
- baseUrl: targetUrl,
1089
- headers,
1090
- phaseTimeoutMs: options.warmCdnDiscoveryTimeout ?? DEFAULT_REMOTE_PATH_DISCOVERY_PHASE_TIMEOUT_MS,
1091
- retries: options.warmCdnDiscoveryRetries,
1092
- retryDelayMs: DEFAULT_REMOTE_PATH_DISCOVERY_RETRY_DELAY_MS
1093
- }
1094
- });
1095
- return readPrerenderWarmPlan(root, {
1096
- includeFallbackShells: options.warmCdnIncludeFallbacks,
1097
- strict: options.warmCdnCertify === true || !options.dangerouslyPromoteOnCdnWarmError
1098
- });
1099
- },
1100
- warmCdnConcurrency: options.warmCdnConcurrency,
1101
- warmCdnTarget,
1102
- warmCdnTimeout: options.warmCdnTimeout,
1103
- warmCdnRetries: options.warmCdnRetries,
1104
- warmCdnDiscoveryTimeout: options.warmCdnDiscoveryTimeout,
1105
- warmCdnDiscoveryRetries: options.warmCdnDiscoveryRetries,
1106
- warmCdnProbeTimeout: options.warmCdnProbeTimeout,
1107
- warmCdnProbeRetries: options.warmCdnProbeRetries,
1108
- warmCdnCertify: options.warmCdnCertify,
1109
- warmCdnReadinessTimeout: options.warmCdnReadinessTimeout,
1110
- warmCdnReadinessRetries: options.warmCdnReadinessRetries,
1111
- warmCdnReadinessProbes: options.warmCdnReadinessProbes,
1112
- warmCdnReadinessProbeDelay: options.warmCdnReadinessProbeDelay,
1113
- dangerouslyPromoteOnCdnWarmError: options.dangerouslyPromoteOnCdnWarmError,
1114
- warmCdnPromote: options.warmCdnPromote,
1115
- warmCdnPromotionDelay: options.warmCdnPromotionDelay
1225
+ promote: options.warmCdnPromote
1116
1226
  });
1117
- else url = await runWranglerDeploy(root, wranglerOptions);
1118
1227
  console.log("\n ─────────────────────────────────────────");
1119
- console.log(` Deployed to: ${url}`);
1228
+ console.log(` ${options.warmCdnPromote === false ? "Version URL" : "Deployed to"}: ${url}`);
1120
1229
  console.log(" ─────────────────────────────────────────\n");
1121
1230
  }
1122
1231
  //#endregion
1123
- 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 };
1232
+ export { DEFAULT_CDN_WARM_PROMOTION_DELAY_MS, buildNodeCliInvocation, buildVersionOverrideHeaders, buildWranglerDeployArgs, buildWranglerInvocation, buildWranglerKVBulkPutArgs, deploy, deployWithCdnWarmup, getZeroPercentStagingTraffic, hasCdnWarmRequests, parseDeployArgs, projectRequiresRouteCacheabilityProbeManifest, resolveCdnWarmupTargetUrl, resolveWorkerNameForVersionOverride, resolveWranglerBin, runWranglerDeploy, runWranglerKVBulkPut, selectTPRWarmPlan, validateWranglerEnvName, withCloudflareEnv };
@@ -1,6 +1,6 @@
1
1
  //#region src/images/images-optimizer.d.ts
2
2
  /** Options accepted by {@link imagesOptimizer}, forwarded to the runtime factory. */
3
- type ImagesOptimizerOptions = {
3
+ export type ImagesOptimizerOptions = {
4
4
  /** Cloudflare Images binding name on the Worker `env`. @default "IMAGES" */
5
5
  binding?: string;
6
6
  };
@@ -21,9 +21,8 @@ type ImagesOptimizerOptions = {
21
21
  * plugins: [vinext({ images: { optimizer: imagesOptimizer() } })],
22
22
  * });
23
23
  */
24
- declare function imagesOptimizer(options?: ImagesOptimizerOptions): {
24
+ export declare function imagesOptimizer(options?: ImagesOptimizerOptions): {
25
25
  adapter: string;
26
26
  options: ImagesOptimizerOptions | undefined;
27
27
  };
28
- //#endregion
29
- export { ImagesOptimizerOptions, imagesOptimizer };
28
+ //#endregion
@@ -7,13 +7,13 @@
7
7
  * KVCacheEntry shape written by KVCacheHandler.set(), and returns Wrangler KV
8
8
  * bulk import records for deploy-time upload.
9
9
  */
10
- type KVBulkPair = {
10
+ export type KVBulkPair = {
11
11
  key: string;
12
12
  value: string;
13
13
  expiration_ttl?: number;
14
14
  metadata?: Record<string, unknown>;
15
15
  };
16
- declare function buildPrerenderKVPairs(serverDir: string, options?: {
16
+ export declare function buildPrerenderKVPairs(serverDir: string, options?: {
17
17
  appPrefix?: string;
18
18
  now?: number;
19
19
  ttlSeconds?: number;
@@ -21,5 +21,4 @@ declare function buildPrerenderKVPairs(serverDir: string, options?: {
21
21
  routeCount: number;
22
22
  pairs: KVBulkPair[];
23
23
  };
24
- //#endregion
25
- export { KVBulkPair, buildPrerenderKVPairs };
24
+ //#endregion
@@ -16,7 +16,7 @@ import { getAppRouteOutputPath, getOutputPath, getRscOutputPath } from "vinext/i
16
16
  * bulk import records for deploy-time upload.
17
17
  */
18
18
  /** Default KV expiration TTL used by KVCacheHandler for revalidating entries. */
19
- const DEFAULT_KV_TTL_SECONDS = 720 * 3600;
19
+ const DEFAULT_KV_TTL_SECONDS = 2592e3;
20
20
  function resolveContainedFile(rootDir, relativePath) {
21
21
  const resolvedRoot = path.resolve(rootDir);
22
22
  const resolvedFile = path.resolve(resolvedRoot, relativePath);
@@ -48,7 +48,8 @@ function buildMetadata(tags) {
48
48
  return JSON.stringify(metadata).length <= 1024 ? metadata : void 0;
49
49
  }
50
50
  function buildPrerenderKVPairs(serverDir, options) {
51
- const manifest = readPrerenderManifest(path.join(serverDir, "vinext-prerender.json"));
51
+ const manifestPath = path.join(serverDir, "vinext-prerender.json");
52
+ const manifest = readPrerenderManifest(manifestPath);
52
53
  if (!manifest?.buildId || !Array.isArray(manifest.routes)) return {
53
54
  routeCount: 0,
54
55
  pairs: []