@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/cdn-warm.js CHANGED
@@ -38,7 +38,7 @@ function readPrerenderPathManifest(manifestPath) {
38
38
  const parsed = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
39
39
  if (!parsed || typeof parsed !== "object") return null;
40
40
  const manifest = parsed;
41
- if (!Array.isArray(manifest.paths) || !manifest.paths.every((pathname) => typeof pathname === "string") || manifest.appPaths !== void 0 && (!Array.isArray(manifest.appPaths) || !manifest.appPaths.every((pathname) => typeof pathname === "string")) || manifest.pagesPaths !== void 0 && (!Array.isArray(manifest.pagesPaths) || !manifest.pagesPaths.every((pathname) => typeof pathname === "string")) || manifest.pagesDataPaths !== void 0 && (!Array.isArray(manifest.pagesDataPaths) || !manifest.pagesDataPaths.every((pathname) => typeof pathname === "string")) || manifest.excludedWarmPaths !== void 0 && (!Array.isArray(manifest.excludedWarmPaths) || !manifest.excludedWarmPaths.every((pathname) => typeof pathname === "string")) || manifest.fallbackRoutePatterns !== void 0 && (!Array.isArray(manifest.fallbackRoutePatterns) || !manifest.fallbackRoutePatterns.every((route) => route !== null && typeof route === "object" && !Array.isArray(route) && (route.kind === "app-page" || route.kind === "app-route" || route.kind === "pages-page") && typeof route.pattern === "string" && route.pattern.startsWith("/"))) || manifest.rscPaths !== void 0 && (!Array.isArray(manifest.rscPaths) || !manifest.rscPaths.every((pathname) => typeof pathname === "string")) || manifest.routeHandlerPaths !== void 0 && (!Array.isArray(manifest.routeHandlerPaths) || !manifest.routeHandlerPaths.every((pathname) => typeof pathname === "string")) || manifest.routePatterns !== void 0 && (!manifest.routePatterns || typeof manifest.routePatterns !== "object" || Array.isArray(manifest.routePatterns) || !Object.entries(manifest.routePatterns).every(([pathname, route]) => pathname.startsWith("/") && route !== null && typeof route === "object" && !Array.isArray(route) && (route.kind === "app-page" || route.kind === "app-route" || route.kind === "pages-page") && typeof route.pattern === "string" && route.pattern.startsWith("/") && (route.cacheabilityProbe === void 0 || route.cacheabilityProbe !== null && typeof route.cacheabilityProbe === "object" && !Array.isArray(route.cacheabilityProbe) && typeof route.cacheabilityProbe.canPrunePattern === "boolean" && (route.cacheabilityProbe.concretePathname === void 0 || typeof route.cacheabilityProbe.concretePathname === "string" && route.cacheabilityProbe.concretePathname.startsWith("/")) && (route.cacheabilityProbe.routeMayResolve === void 0 || typeof route.cacheabilityProbe.routeMayResolve === "boolean") && (route.cacheabilityProbe.requestStageMayTerminate === void 0 || typeof route.cacheabilityProbe.requestStageMayTerminate === "boolean")))) || manifest.loadingShellPaths !== void 0 && (!Array.isArray(manifest.loadingShellPaths) || !manifest.loadingShellPaths.every((pathname) => typeof pathname === "string")) || manifest.basePath !== void 0 && typeof manifest.basePath !== "string" || manifest.buildIdentity !== void 0 && typeof manifest.buildIdentity !== "string" || manifest.deploymentId !== void 0 && typeof manifest.deploymentId !== "string" || manifest.rscBuildId !== void 0 && typeof manifest.rscBuildId !== "string" || manifest.trailingSlash !== void 0 && typeof manifest.trailingSlash !== "boolean" || manifest.responseVary !== void 0 && manifest.responseVary !== "verbatim") return null;
41
+ if (!Array.isArray(manifest.paths) || !manifest.paths.every((pathname) => typeof pathname === "string") || manifest.appPaths !== void 0 && (!Array.isArray(manifest.appPaths) || !manifest.appPaths.every((pathname) => typeof pathname === "string")) || manifest.pagesPaths !== void 0 && (!Array.isArray(manifest.pagesPaths) || !manifest.pagesPaths.every((pathname) => typeof pathname === "string")) || manifest.pagesDataPaths !== void 0 && (!Array.isArray(manifest.pagesDataPaths) || !manifest.pagesDataPaths.every((pathname) => typeof pathname === "string")) || manifest.excludedWarmPaths !== void 0 && (!Array.isArray(manifest.excludedWarmPaths) || !manifest.excludedWarmPaths.every((pathname) => typeof pathname === "string")) || manifest.fallbackRoutePatterns !== void 0 && (!Array.isArray(manifest.fallbackRoutePatterns) || !manifest.fallbackRoutePatterns.every((route) => route !== null && typeof route === "object" && !Array.isArray(route) && (route.kind === "app-page" || route.kind === "app-route" || route.kind === "pages-page") && typeof route.pattern === "string" && route.pattern.startsWith("/"))) || manifest.rscPaths !== void 0 && (!Array.isArray(manifest.rscPaths) || !manifest.rscPaths.every((pathname) => typeof pathname === "string")) || manifest.routeHandlerPaths !== void 0 && (!Array.isArray(manifest.routeHandlerPaths) || !manifest.routeHandlerPaths.every((pathname) => typeof pathname === "string")) || manifest.routePatterns !== void 0 && (!manifest.routePatterns || typeof manifest.routePatterns !== "object" || Array.isArray(manifest.routePatterns) || !Object.entries(manifest.routePatterns).every(([pathname, route]) => pathname.startsWith("/") && route !== null && typeof route === "object" && !Array.isArray(route) && (route.kind === "app-page" || route.kind === "app-route" || route.kind === "pages-page") && typeof route.pattern === "string" && route.pattern.startsWith("/") && (route.cacheabilityProbe === void 0 || route.cacheabilityProbe !== null && typeof route.cacheabilityProbe === "object" && !Array.isArray(route.cacheabilityProbe) && typeof route.cacheabilityProbe.canPrunePattern === "boolean" && (route.cacheabilityProbe.concretePathname === void 0 || typeof route.cacheabilityProbe.concretePathname === "string" && route.cacheabilityProbe.concretePathname.startsWith("/")) && (route.cacheabilityProbe.routeMayResolve === void 0 || typeof route.cacheabilityProbe.routeMayResolve === "boolean") && (route.cacheabilityProbe.requestStageMayTerminate === void 0 || typeof route.cacheabilityProbe.requestStageMayTerminate === "boolean") && (route.cacheabilityProbe.unlisted === void 0 || typeof route.cacheabilityProbe.unlisted === "boolean")))) || manifest.loadingShellPaths !== void 0 && (!Array.isArray(manifest.loadingShellPaths) || !manifest.loadingShellPaths.every((pathname) => typeof pathname === "string")) || manifest.loadingBoundaryRoutePatterns !== void 0 && (!Array.isArray(manifest.loadingBoundaryRoutePatterns) || !manifest.loadingBoundaryRoutePatterns.every((pattern) => typeof pattern === "string" && pattern.startsWith("/"))) || manifest.basePath !== void 0 && typeof manifest.basePath !== "string" || manifest.buildIdentity !== void 0 && typeof manifest.buildIdentity !== "string" || manifest.deploymentId !== void 0 && typeof manifest.deploymentId !== "string" || manifest.rscBuildId !== void 0 && typeof manifest.rscBuildId !== "string" || manifest.trailingSlash !== void 0 && typeof manifest.trailingSlash !== "boolean" || manifest.responseVary !== void 0 && manifest.responseVary !== "verbatim") return null;
42
42
  return manifest;
43
43
  } catch (error) {
44
44
  console.warn(`[vinext] Failed to read prerender path manifest at ${manifestPath}:`, error);
@@ -90,6 +90,7 @@ function createPrerenderWarmPlan(root, manifest, options) {
90
90
  ...manifest.buildIdentity ? { buildIdentity: manifest.buildIdentity } : {},
91
91
  ...manifest.deploymentId ? { deploymentId: manifest.deploymentId } : {},
92
92
  ...manifest.fallbackRoutePatterns ? { fallbackRoutePatterns: manifest.fallbackRoutePatterns } : {},
93
+ ...manifest.loadingBoundaryRoutePatterns ? { loadingBoundaryRoutePatterns: manifest.loadingBoundaryRoutePatterns } : {},
93
94
  loadingShellPaths: supportsCanonicalRsc ? (manifest.loadingShellPaths ?? []).map(applyConfig) : [],
94
95
  ...manifest.pagesDataPaths ? { pagesDataPaths: manifest.pagesDataPaths } : {},
95
96
  ...manifest.pagesPaths ? { pagesPaths: manifest.pagesPaths.map(applyConfig) } : {},
@@ -365,6 +366,7 @@ const REUSABLE_CF_CACHE_STATUSES = /* @__PURE__ */ new Set([
365
366
  "REVALIDATED",
366
367
  "UPDATING"
367
368
  ]);
369
+ const TRANSITIONAL_VINEXT_CACHE_STATUSES = /* @__PURE__ */ new Set(["MISS", "UPDATING"]);
368
370
  const NON_CACHEABLE_CF_CACHE_STATUSES = /* @__PURE__ */ new Set(["BYPASS"]);
369
371
  const CDN_CACHE_POLICY_HEADERS = [
370
372
  "Cloudflare-CDN-Cache-Control",
@@ -445,20 +447,29 @@ function validateCachePolicy(response, requireCacheStatus, requireCacheHit = fal
445
447
  error: `CF-Cache-Status is ${cacheStatus}`
446
448
  };
447
449
  }
448
- function validateVinextCacheStatus(response) {
450
+ function validateVinextCacheStatus(response, missingStatus = "failed", requireCacheHit = false) {
449
451
  const status = response.headers.get(VINEXT_CACHE_HEADER)?.trim().toUpperCase();
452
+ if (requireCacheHit && status !== "HIT") return {
453
+ outcome: "failed",
454
+ error: `${VINEXT_CACHE_HEADER} is ${status ?? "missing"}; the cache fill is not reusable`
455
+ };
450
456
  if (status === "MISS" || status === "HIT" || status === "UPDATING") return { outcome: "warmed" };
451
457
  if (status === "BYPASS" || status === "DYNAMIC") return {
452
458
  outcome: "skipped",
453
459
  reason: `${VINEXT_CACHE_HEADER} is ${status}`
454
460
  };
455
- return {
461
+ return missingStatus === "skipped" ? {
462
+ outcome: "skipped",
463
+ reason: `response is missing ${VINEXT_CACHE_HEADER}`
464
+ } : {
456
465
  outcome: "failed",
457
466
  error: `response is missing ${VINEXT_CACHE_HEADER}`
458
467
  };
459
468
  }
460
469
  function validateWarmAdmission(response, statusSource, requireCacheHit) {
461
- return statusSource === "vinext" ? validateVinextCacheStatus(response) : validateCachePolicy(response, true, requireCacheHit);
470
+ if (statusSource === "vinext") return validateVinextCacheStatus(response, "failed", requireCacheHit);
471
+ if (statusSource === "data-cache") return validateVinextCacheStatus(response, "skipped", requireCacheHit);
472
+ return validateCachePolicy(response, true, requireCacheHit);
462
473
  }
463
474
  function validateBuildIdentity(response, expectedBuildId) {
464
475
  if (expectedBuildId !== void 0 && response.headers.get("X-Vinext-Build-Id") !== expectedBuildId) {
@@ -645,6 +656,14 @@ function shouldRetryValidationFailure(response, target, options) {
645
656
  if (expectedIdentities.some((matches) => !matches)) return true;
646
657
  return isRetryableStatus(response.status, options.retryNotFound);
647
658
  }
659
+ function isRetryableCertificationFailure(response, statusSource, retryPropagationFailures) {
660
+ if (statusSource === "cloudflare") {
661
+ const status = response.headers.get("CF-Cache-Status")?.trim().toUpperCase();
662
+ return !REUSABLE_CF_CACHE_STATUSES.has(status ?? "") && (retryPropagationFailures || ADMITTED_CF_CACHE_STATUSES.has(status ?? ""));
663
+ }
664
+ const status = response.headers.get(VINEXT_CACHE_HEADER)?.trim().toUpperCase();
665
+ return TRANSITIONAL_VINEXT_CACHE_STATUSES.has(status ?? "");
666
+ }
648
667
  async function warmOnePath(target, options) {
649
668
  const url = buildWarmupUrl(options.targetUrl, target.pathname);
650
669
  let lastError = "request failed before the first attempt";
@@ -707,8 +726,7 @@ async function warmOnePath(target, options) {
707
726
  }
708
727
  lastSkippedReason = null;
709
728
  lastError = validation.error;
710
- const cacheStatus = response.headers.get("CF-Cache-Status")?.trim().toUpperCase();
711
- lastRetryable = options.requireCacheHit && ADMITTED_CF_CACHE_STATUSES.has(cacheStatus ?? "") && !REUSABLE_CF_CACHE_STATUSES.has(cacheStatus ?? "") || shouldRetryValidationFailure(response, target, options);
729
+ lastRetryable = options.requireCacheHit && isRetryableCertificationFailure(response, options.statusSource, options.retryPropagationFailures) || shouldRetryValidationFailure(response, target, options);
712
730
  if (!lastRetryable) break;
713
731
  if (!canRetry(attempt)) break;
714
732
  if (!await waitBeforeRetry()) return {
@@ -745,8 +763,7 @@ async function warmOnePath(target, options) {
745
763
  }
746
764
  lastSkippedReason = null;
747
765
  lastError = validation.error;
748
- const cacheStatus = response.headers.get("CF-Cache-Status")?.trim().toUpperCase();
749
- lastRetryable = options.requireCacheHit && ADMITTED_CF_CACHE_STATUSES.has(cacheStatus ?? "") && !REUSABLE_CF_CACHE_STATUSES.has(cacheStatus ?? "") || shouldRetryValidationFailure(response, target, options);
766
+ lastRetryable = options.requireCacheHit && isRetryableCertificationFailure(response, options.statusSource, options.retryPropagationFailures) || shouldRetryValidationFailure(response, target, options);
750
767
  if (!lastRetryable) break;
751
768
  } catch (error) {
752
769
  lastSkippedReason = null;
@@ -1,4 +1,4 @@
1
- import { VinextCacheConfig } from "vinext/internal/cache-adapters";
1
+ import { VinextCacheConfig } from "vinext/internal/config/prerender";
2
2
  //#region src/deploy-config.d.ts
3
3
  /**
4
4
  * Check whether an existing vite.config file already imports and uses the
@@ -18,16 +18,10 @@ export declare function viteConfigHasCloudflarePlugin(root: string): boolean;
18
18
  * blocked on a false negative.
19
19
  */
20
20
  export declare function viteConfigHasCacheAdapter(root: string): boolean;
21
- export type ResolvedKvDataAdapterConfig = {
22
- binding: string;
23
- appPrefix?: string;
24
- ttlSeconds?: number;
25
- };
26
21
  export type ResolvedCdnAdapterConfig = {
27
22
  versionMetadataBinding: string;
28
23
  };
29
24
  export declare function resolveCdnAdapterConfig(cache: VinextCacheConfig | null | undefined): ResolvedCdnAdapterConfig | null;
30
- export declare function resolveKvDataAdapterConfig(cache: VinextCacheConfig | null | undefined): ResolvedKvDataAdapterConfig | null;
31
25
  export declare function viteConfigHasImageAdapter(root: string): boolean;
32
26
  /**
33
27
  * Detect whether an existing user-authored Worker entry wires up a cache
@@ -52,6 +46,7 @@ export declare function workerEntryHasCacheHandler(root: string): boolean;
52
46
  */
53
47
  export declare function formatMissingCacheAdapterError(options: {
54
48
  configFile?: string;
49
+ typedConfig?: boolean;
55
50
  }): string;
56
- export declare function formatImageOptimizationHint(): string;
51
+ export declare function formatImageOptimizationHint(typedConfig?: boolean): string;
57
52
  //#endregion
@@ -1,5 +1,4 @@
1
- import { DEFAULT_KV_DATA_CACHE_BINDING } from "./cache/kv-data-adapter.js";
2
- import { DEFAULT_CDN_VERSION_METADATA_BINDING } from "./cache/cdn-adapter.js";
1
+ import { DEFAULT_CDN_VERSION_METADATA_BINDING } from "./cache/workers-cache-cdn-adapter.js";
3
2
  import fs from "node:fs";
4
3
  import path from "node:path";
5
4
  import { fileURLToPath } from "node:url";
@@ -105,24 +104,6 @@ function resolveCdnAdapterConfig(cache) {
105
104
  const options = cdn.options;
106
105
  return { versionMetadataBinding: typeof options?.versionMetadataBinding === "string" && options.versionMetadataBinding.length > 0 ? options.versionMetadataBinding : DEFAULT_CDN_VERSION_METADATA_BINDING };
107
106
  }
108
- function isCloudflareKvDataAdapterPath(adapter) {
109
- const normalized = adapter.replace(/\\/g, "/");
110
- return normalized === "@vinext/cloudflare/cache/kv-data-adapter.runtime" || normalized === "@vinext/cloudflare/cache/kv-data-adapter.runtime.js" || normalized.endsWith("/cache/kv-data-adapter.runtime.js");
111
- }
112
- function readPositiveNumberOption(options, field) {
113
- const value = options?.[field];
114
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
115
- }
116
- function resolveKvDataAdapterConfig(cache) {
117
- const data = cache?.data;
118
- if (!data?.adapter || !isCloudflareKvDataAdapterPath(data.adapter)) return null;
119
- const options = data.options;
120
- return {
121
- binding: typeof options?.binding === "string" && options.binding.length > 0 ? options.binding : DEFAULT_KV_DATA_CACHE_BINDING,
122
- ...typeof options?.appPrefix === "string" && options.appPrefix.length > 0 ? { appPrefix: options.appPrefix } : {},
123
- ...readPositiveNumberOption(options, "ttlSeconds") !== void 0 ? { ttlSeconds: readPositiveNumberOption(options, "ttlSeconds") } : {}
124
- };
125
- }
126
107
  function viteConfigHasImageAdapter(root) {
127
108
  const configPath = findViteConfigPath(root);
128
109
  if (!configPath) return true;
@@ -178,10 +159,11 @@ function workerEntryHasCacheHandler(root) {
178
159
  */
179
160
  function formatMissingCacheAdapterError(options) {
180
161
  const configRef = options.configFile ? options.configFile : "your Vite config";
181
- return `[vinext] This app uses ISR / caching but no cache adapter is configured in ${configRef}.\n\n Production deployments need a persistent cache backend. Declare one on the\n vinext() plugin in ${configRef}:\n\n import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";\n\n export default defineConfig({\n plugins: [\n vinext({\n cache: {\n data: kvDataAdapter(), // KV-backed data cache (binding: VINEXT_KV_CACHE)\n },\n }),\n cloudflare(),\n ],\n });\n\n The VINEXT_KV_CACHE namespace binding is added to wrangler.jsonc for you.\n Create the namespace with:\n\n npx wrangler kv namespace create VINEXT_KV_CACHE`;
162
+ return `[vinext] This app uses ISR / caching but no cache adapter is configured in ${configRef}.\n\n Production deployments need a persistent cache backend. Declare one on the\n vinext() plugin in ${configRef}:\n\n import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";\n\n export default defineConfig({\n plugins: [\n vinext({\n cache: {\n data: kvDataAdapter(), // KV-backed data cache (binding: VINEXT_KV_CACHE)\n },\n }),\n cloudflare(),\n ],\n });\n\n` + (options.typedConfig ? ` Configure the VINEXT_KV_CACHE namespace binding in cloudflare.config.ts.` : " The VINEXT_KV_CACHE namespace binding is added to wrangler.jsonc for you.\n Create the namespace with:\n\n npx wrangler kv namespace create VINEXT_KV_CACHE");
182
163
  }
183
- function formatImageOptimizationHint() {
164
+ function formatImageOptimizationHint(typedConfig = false) {
165
+ if (typedConfig) return " [vinext] next/image is served unoptimized. To enable edge image\n optimization via Cloudflare Images, configure imagesOptimizer() in your\n Vite config and the IMAGES binding in cloudflare.config.ts.";
184
166
  return " [vinext] next/image is served unoptimized. To enable edge image\n optimization via Cloudflare Images, run:\n\n vinext init --platform=cloudflare --image-optimization=cloudflare-images\n\n This adds the imagesOptimizer() option to your Vite config and the matching\n IMAGES binding to your Wrangler config without replacing existing settings.";
185
167
  }
186
168
  //#endregion
187
- export { formatImageOptimizationHint, formatMissingCacheAdapterError, resolveCdnAdapterConfig, resolveKvDataAdapterConfig, viteConfigHasCacheAdapter, viteConfigHasCloudflarePlugin, viteConfigHasImageAdapter, workerEntryHasCacheHandler };
169
+ export { formatImageOptimizationHint, formatMissingCacheAdapterError, resolveCdnAdapterConfig, viteConfigHasCacheAdapter, viteConfigHasCloudflarePlugin, viteConfigHasImageAdapter, workerEntryHasCacheHandler };
@@ -9,76 +9,81 @@ function formatDeployHelp() {
9
9
  - Detects App Router or Pages Router
10
10
  - Validates setup from vinext init --platform=cloudflare
11
11
  - Builds the project with Vite
12
- - Deploys via wrangler
12
+ - Deploys with cf for typed Cloudflare config projects, or Wrangler otherwise
13
13
 
14
14
  Options:
15
15
  --preview Deploy to preview environment (same as --env preview)
16
- --env <name> Deploy using wrangler env.<name>
17
- --name <name> Custom Worker name (default: from package.json)
16
+ --env <name> Cloudflare mode for typed config; Wrangler environment otherwise
17
+ --name <name> Custom Worker name for Wrangler config projects
18
18
  --config <path> Wrangler config path (default: wrangler.jsonc/json/toml)
19
19
  --skip-build Skip the build step (use existing dist/)
20
20
  --dry-run Validate setup without building or deploying
21
- --verbose Print raw output from internal Wrangler commands
22
- --prerender-all Pre-render discovered routes after building (future
23
- releases will auto-populate the remote cache)
21
+ --verbose Print raw output from internal Cloudflare CLI commands
22
+ --no-promote Do not promote the uploaded Worker version to 100%
23
+ traffic
24
+ --prerender-all Deprecated for Worker deployments; use
25
+ --warm-cache instead (still honored for static
26
+ export and cache adapters that package local
27
+ prerender output)
24
28
  --prerender-concurrency <count>
25
- Maximum number of routes to pre-render in parallel
26
- --experimental-warm-cdn-cache
27
- Upload a Worker version, warm build-discovered paths
28
- through the production URL, then promote it (experimental)
29
- --warm-cdn-target <origin>
29
+ Maximum parallel routes for local prerendering
30
+ --warm-cache Upload a Worker version, warm build-discovered paths
31
+ through the production URL, then promote it
32
+ --warm-cache-target <origin>
30
33
  HTTPS origin to use for discovery, probing, and warming
31
34
  (overrides URLs inferred from Wrangler output)
32
- --warm-cdn-concurrency <count>
35
+ Also supported with traffic-aware warming
36
+ --warm-cache-concurrency <count>
33
37
  Maximum number of CDN warmup requests in parallel (default: 25)
34
- --warm-cdn-timeout <ms> Per-request CDN warmup timeout (default: 10000)
35
- --warm-cdn-retries <n> Retries per failed CDN warmup request (default: 1;
38
+ --warm-cache-timeout <ms> Per-request CDN warmup timeout (default: 10000)
39
+ --warm-cache-retries <n> Retries per failed CDN warmup request (default: 1;
36
40
  staged-version propagation default: 60)
37
- --warm-cdn-discovery-timeout <ms>
41
+ --warm-cache-discovery-timeout <ms>
38
42
  Total staged path-discovery deadline (default: 120000)
39
- --warm-cdn-discovery-retries <n>
43
+ --warm-cache-discovery-retries <n>
40
44
  Optional staged path-discovery retry limit
41
45
  (default: derived from the discovery deadline)
42
- --warm-cdn-probe-timeout <ms>
46
+ --warm-cache-probe-timeout <ms>
43
47
  Abort when cacheability probing makes no progress for
44
48
  this duration (default: 120000)
45
- --warm-cdn-probe-retries <n>
49
+ --warm-cache-probe-retries <n>
46
50
  Cacheability-probe retries (default: 2)
47
- --warm-cdn-certify With --experimental-warm-cdn-cache, re-request warmed
48
- entries using headers only and require every planned
49
- entry to be reusable before promotion
50
- --warm-cdn-readiness-timeout <ms>
51
+ --warm-cache-certify With either warming mode, re-request warmed
52
+ entries using headers only and require reusable hits.
53
+ Traffic-aware warming requires every selected entry
54
+ to be reusable before promotion
55
+ --warm-cache-readiness-timeout <ms>
51
56
  Explicit total staged-readiness deadline (default:
52
57
  120000)
53
- --warm-cdn-readiness-retries <n>
58
+ --warm-cache-readiness-retries <n>
54
59
  Staged-readiness retries (default: 60)
55
- --warm-cdn-readiness-probes <count>
60
+ --warm-cache-readiness-probes <count>
56
61
  Consecutive successful staged-readiness probes
57
62
  required before warming (default: 6)
58
- --warm-cdn-readiness-probe-delay <ms>
63
+ --warm-cache-readiness-probe-delay <ms>
59
64
  Delay between staged-readiness probes (default: 1000)
60
- --dangerously-promote-on-cdn-warm-error
65
+ --dangerously-promote-on-warm-cache-error
61
66
  Promote even when ordinary staged warmup cannot be
62
- verified (never bypasses --warm-cdn-certify)
63
- --warm-cdn-no-promote Leave the warmed Worker version staged at 0% traffic;
64
- production triggers are still applied before warming
65
- --warm-cdn-promotion-delay <ms>
67
+ verified (never bypasses --warm-cache-certify)
68
+ --warm-cache-promotion-delay <ms>
66
69
  Delay before promotion after warmup (default: 15000)
67
- --warm-cdn-include-fallbacks
70
+ --warm-cache-include-fallbacks
68
71
  Also warm PPR fallback-shell placeholder paths
69
72
  -h, --help Show this help
70
73
 
71
- Experimental:
72
- --experimental-tpr Enable Traffic-aware Pre-Rendering
73
- --tpr-coverage <pct> Traffic coverage target, 0-100 (default: 90)
74
- --tpr-limit <count> Hard cap on pages to pre-render (default: 1000)
75
- --tpr-window <hours> Analytics lookback window in hours (default: 24)
74
+ Traffic-aware warming:
75
+ --traffic-aware-warm-cache Select cache warming routes from traffic
76
+ --traffic-aware-coverage <pct> Traffic coverage target, 0-100 (default: 90)
77
+ --traffic-aware-limit <count> Hard cap on selected routes (default: 1000)
78
+ --traffic-aware-window <hours> Analytics lookback window in hours (default: 24)
76
79
 
77
- TPR (Traffic-aware Pre-Rendering) uses Cloudflare zone analytics to determine
78
- which pages get the most traffic and pre-renders them into KV cache during
79
- deploy. This feature is experimental and must be explicitly enabled. Requires
80
- a custom domain (zone analytics are unavailable on *.workers.dev) and the
81
- CLOUDFLARE_API_TOKEN environment variable with Zone.Analytics read permission.
80
+ Traffic-aware warming uses Cloudflare zone analytics to select the
81
+ highest-traffic routes, then feeds those routes into the same staged CDN
82
+ pre-warming flow used by --warm-cache. It requires a custom
83
+ domain and a CLOUDFLARE_API_TOKEN with Zone > Analytics > Read and
84
+ Zone > Zone > Read permissions for that zone.
85
+ Use --warm-cache-target <origin> to provide the production HTTPS origin
86
+ manually for traffic-aware analytics selection, discovery, probing, and warming.
82
87
 
83
88
  Workers Cache automatically uses tiered caching. Warmed entries can therefore
84
89
  be reused outside the data center reached by the warmup request after cache
@@ -89,16 +94,22 @@ function formatDeployHelp() {
89
94
  vpx @vinext/cloudflare deploy Build and deploy with Vite+
90
95
  vp exec vinext-cloudflare deploy Run the locally installed Vite+ bin
91
96
  vinext-cloudflare deploy --preview Deploy to a preview URL
92
- vinext-cloudflare deploy --env staging Deploy using wrangler env.staging
97
+ vinext-cloudflare deploy --env staging Deploy using Cloudflare mode/environment staging
93
98
  vinext-cloudflare deploy --config dist/server/wrangler.json Deploy using a generated Wrangler config
94
99
  vinext-cloudflare deploy --dry-run Validate setup without building or deploying
95
100
  vinext-cloudflare deploy --name my-app Deploy with a custom Worker name
96
- vinext-cloudflare deploy --experimental-warm-cdn-cache Warm build-discovered paths during version deploy (experimental)
97
- vinext-cloudflare deploy --experimental-warm-cdn-cache --warm-cdn-target https://example.com
101
+ vinext-cloudflare deploy --no-promote Upload a version without changing deployment traffic
102
+ vinext-cloudflare deploy --warm-cache Warm build-discovered paths during version deploy
103
+ vinext-cloudflare deploy --warm-cache --warm-cache-target https://example.com
98
104
  Warm an explicit production origin
99
- vinext-cloudflare deploy --experimental-tpr Enable TPR during deploy
100
- vinext-cloudflare deploy --experimental-tpr --tpr-coverage 95 Cover 95% of traffic
101
- vinext-cloudflare deploy --experimental-tpr --tpr-limit 500 Cap at 500 pages
105
+ vinext-cloudflare deploy --traffic-aware-warm-cache
106
+ Enable traffic-aware warming
107
+ vinext-cloudflare deploy --traffic-aware-warm-cache --traffic-aware-coverage 95
108
+ Cover 95% of traffic
109
+ vinext-cloudflare deploy --traffic-aware-warm-cache --traffic-aware-limit 500
110
+ Cap at 500 routes
111
+ vinext-cloudflare deploy --traffic-aware-warm-cache --warm-cache-target https://example.com
112
+ Select and warm traffic routes for an explicit origin
102
113
  `;
103
114
  }
104
115
  function printDeployHelp() {
package/dist/deploy.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { CdnWarmOptions, CdnWarmRequestPlan, PrerenderWarmPlan } from "./cdn-warm.js";
2
- import { parseWranglerConfig } from "./tpr.js";
3
- import { WranglerDeploymentStatus, WranglerVersionTraffic } from "./version-deploy.js";
4
- import { KVBulkPair } from "./prerender-kv-populate.js";
2
+ import { parseWranglerConfig } from "./wrangler-config.js";
3
+ import { TrafficEntry } from "./tpr.js";
4
+ import { WranglerDeploymentStatus, WranglerVersionTraffic, WranglerVersionUploadResult } from "./version-deploy.js";
5
5
  import { spawn } from "node:child_process";
6
- import { VinextCacheConfig } from "vinext/internal/config/prerender";
6
+ import { VinextCacheConfig, VinextRouteRootConfig } from "vinext/internal/config/prerender";
7
7
  import { ProjectInfo } from "vinext/internal/utils/project";
8
8
  //#region src/deploy.d.ts
9
9
  export declare const DEFAULT_CDN_WARM_PROMOTION_DELAY_MS = 15000;
@@ -58,21 +58,24 @@ export type DeployOptions = {
58
58
  warmCdnReadinessProbeDelay?: number;
59
59
  /** Promote even when staged CDN warmup cannot be completed */
60
60
  dangerouslyPromoteOnCdnWarmError?: boolean;
61
- /** Promote the warmed Worker version to 100% traffic (default: true) */
61
+ /** Promote the uploaded Worker version to 100% traffic (default: true) */
62
62
  warmCdnPromote?: boolean;
63
63
  /** Delay between successful warmup and promotion in milliseconds */
64
64
  warmCdnPromotionDelay?: number;
65
65
  /** Include PPR fallback-shell placeholder paths during CDN warmup */
66
66
  warmCdnIncludeFallbacks?: boolean;
67
- /** Enable experimental TPR (Traffic-aware Pre-Rendering) */
67
+ /** Select CDN pre-warm routes using traffic analytics */
68
68
  experimentalTPR?: boolean;
69
69
  /** TPR: traffic coverage percentage target (0–100, default: 90) */
70
70
  tprCoverage?: number;
71
- /** TPR: hard cap on number of pages to pre-render (default: 1000) */
71
+ /** TPR: hard cap on selected routes (default: 1000) */
72
72
  tprLimit?: number;
73
73
  /** TPR: analytics lookback window in hours (default: 24) */
74
74
  tprWindow?: number;
75
75
  };
76
+ export type DeploymentTool = "cf" | "wrangler";
77
+ /** The typed cf Vite plugin emits Worker and asset bundles into Build Output. */
78
+ export declare function resolvePrerenderOutputDirs(root: string, deploymentTool: DeploymentTool, configured: VinextRouteRootConfig | null): VinextRouteRootConfig | null;
76
79
  export declare function parseDeployArgs(args: string[]): {
77
80
  help: boolean;
78
81
  preview: boolean;
@@ -120,21 +123,20 @@ export declare function parseDeployArgs(args: string[]): {
120
123
  * Passing `undefined` is a no-op; the callback runs with `process.env` untouched.
121
124
  */
122
125
  export declare function withCloudflareEnv<T>(env: string | undefined, fn: () => Promise<T>): Promise<T>;
126
+ export declare function resolveViteBuildMode(deploymentTool: DeploymentTool, env: string | undefined): string;
123
127
  type WranglerDeployArgs = {
124
128
  args: string[];
125
129
  env: string | undefined;
126
130
  };
127
- type WranglerKVBulkPutArgs = {
131
+ type CfDeployArgs = {
128
132
  args: string[];
129
- env: string | undefined;
133
+ mode: string | undefined;
130
134
  };
131
135
  export declare function validateWranglerEnvName(env: string): string;
132
136
  export declare function buildWranglerDeployArgs(options: Pick<DeployOptions, "preview" | "env" | "name" | "config">): WranglerDeployArgs;
133
- export declare function buildWranglerKVBulkPutArgs(options: {
134
- binding: string;
135
- env?: string;
136
- filePath: string;
137
- }): WranglerKVBulkPutArgs;
137
+ export declare function resolveDeploymentTool(root: string): DeploymentTool;
138
+ export declare function isCfCliInstalled(root: string, resolvePackageJson?: (root: string) => string | null): boolean;
139
+ export declare function buildCfDeployArgs(options: Pick<DeployOptions, "preview" | "env">): CfDeployArgs;
138
140
  /**
139
141
  * Resolve Wrangler's JavaScript CLI entrypoint in node_modules.
140
142
  *
@@ -142,6 +144,7 @@ export declare function buildWranglerKVBulkPutArgs(options: {
142
144
  * shim and command shell that package managers create on Windows.
143
145
  */
144
146
  export declare function resolveWranglerBin(root: string, resolvePackageJson?: (root: string) => string | null): string;
147
+ export declare function resolveCfBin(root: string, resolvePackageJson?: (root: string) => string | null): string;
145
148
  export declare function buildNodeCliInvocation(scriptPath: string, args: string[], nodeExecutable?: string): {
146
149
  file: string;
147
150
  args: string[];
@@ -151,30 +154,37 @@ export declare function buildWranglerInvocation(root: string, options: Pick<Depl
151
154
  args: string[];
152
155
  env: string | undefined;
153
156
  };
154
- export declare function runWranglerKVBulkPut(root: string, options: {
155
- binding: string;
156
- env?: string;
157
- pairs: KVBulkPair[];
158
- tempDir?: string;
159
- }, execute?: typeof spawn, nodeExecutable?: string): Promise<void>;
160
- export declare function runWranglerDeploy(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config">, execute?: typeof spawn): Promise<string>;
157
+ export declare function runWranglerDeploy(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose"> & {
158
+ promote?: boolean;
159
+ }, execute?: typeof spawn): Promise<string>;
160
+ export declare function runCfDeploy(root: string, options: Pick<DeployOptions, "preview" | "env">, execute?: typeof spawn): Promise<string>;
161
161
  export declare function hasCdnWarmRequests(plan: Omit<CdnWarmRequestPlan, "pagesDataPaths"> & {
162
162
  pagesDataPaths?: readonly string[];
163
163
  }): boolean;
164
+ export declare function selectTPRWarmPlan(plan: PrerenderWarmPlan, traffic: readonly TrafficEntry[], coverage: number, limit: number): PrerenderWarmPlan;
164
165
  export declare function projectRequiresRouteCacheabilityProbeManifest(project: Pick<ProjectInfo, "isAppRouter" | "isPagesRouter">, cacheConfig: VinextCacheConfig | null): boolean;
165
166
  type CdnWarmDeployOptions = Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose" | "warmCdnTarget" | "warmCdnConcurrency" | "warmCdnTimeout" | "warmCdnRetries" | "warmCdnDiscoveryTimeout" | "warmCdnDiscoveryRetries" | "warmCdnProbeTimeout" | "warmCdnProbeRetries" | "warmCdnCertify" | "warmCdnReadinessTimeout" | "warmCdnReadinessRetries" | "warmCdnReadinessProbes" | "warmCdnReadinessProbeDelay" | "dangerouslyPromoteOnCdnWarmError" | "warmCdnPromote" | "warmCdnPromotionDelay"> & Pick<CdnWarmOptions, "deploymentId" | "expectedBuildId" | "expectedRscBuildId" | "loadingShellPaths" | "pagesDataPaths" | "routeHandlerPaths" | "routePatterns" | "rscPaths" | "statusSource"> & {
167
+ deploymentTool?: DeploymentTool;
168
+ /** Allow optional route selection to discover no warmable requests. */
169
+ allowEmptyWarmPlan?: boolean;
166
170
  /** Probe a staged Worker and upload the resulting manifest as a second version. */
167
171
  cacheabilityProbe?: boolean;
168
172
  discoverWarmPlan?: (target: {
169
173
  headers?: HeadersInit;
170
174
  targetUrl: string;
171
175
  }) => Promise<PrerenderWarmPlan>;
176
+ /** Narrow the final warm requests without changing discovery or probe metadata. */
177
+ selectWarmPlan?: (plan: PrerenderWarmPlan) => PrerenderWarmPlan;
178
+ };
179
+ type DeploymentControlPlaneOptions = Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose"> & {
180
+ deploymentTool?: DeploymentTool;
172
181
  };
173
182
  export declare function deployWithCdnWarmup(root: string, paths: readonly string[], options: CdnWarmDeployOptions): Promise<string>;
174
183
  export declare function resolveCdnWarmupTargetUrl(root: string, deployedUrl: string | null): string | null;
175
184
  export declare function resolveCdnWarmupTargetUrl(root: string, deployedUrl: string | null, options: Pick<DeployOptions, "preview" | "env" | "config" | "warmCdnTarget">): string | null;
176
185
  export declare function getZeroPercentStagingTraffic(deployment: WranglerDeploymentStatus | null, versionId: string): WranglerVersionTraffic[] | null;
177
186
  type ParsedWranglerConfig = NonNullable<ReturnType<typeof parseWranglerConfig>>;
187
+ export declare function resolveDeploymentControlPlaneOptions(options: DeploymentControlPlaneOptions, upload: Pick<WranglerVersionUploadResult, "workerName">): DeploymentControlPlaneOptions;
178
188
  export declare function resolveWorkerNameForVersionOverride(config: ParsedWranglerConfig | null, options: Pick<DeployOptions, "preview" | "env" | "name">): string | undefined;
179
189
  export declare function buildVersionOverrideHeaders(workerName: string | undefined, versionId: string): HeadersInit | undefined;
180
190
  export declare function deploy(options: DeployOptions): Promise<void>;