@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
@@ -1,7 +1,7 @@
1
- import { PrerenderRoutePattern } from "vinext/internal/build/prerender-paths";
1
+ import { PrerenderPathManifest, PrerenderRoutePattern } from "vinext/internal/build/prerender-paths";
2
2
  import { PrerenderManifest, PrerenderedPathSelectionOptions } from "vinext/internal/server/prerender-manifest";
3
3
  //#region src/cdn-warm.d.ts
4
- type CdnWarmOptions = {
4
+ export type CdnWarmOptions = {
5
5
  targetUrl: string;
6
6
  paths: readonly string[];
7
7
  /** Pages Router JSON data identities used by client navigation. */
@@ -27,23 +27,27 @@ type CdnWarmOptions = {
27
27
  phaseTimeoutMs?: number;
28
28
  /** Retry a newly staged version or preview alias until its routing has propagated. */
29
29
  propagatingTarget?: boolean;
30
+ /** @internal Matching skipped targets that may be transient while a staged version propagates. */
31
+ retrySkippedTargetKeys?: ReadonlySet<string>;
30
32
  /** Require the response to come from a reusable cache entry, not merely an eligible MISS. */
31
33
  requireCacheHit?: boolean;
32
34
  strict?: boolean;
35
+ /** Cache-admission signal exposed by the deployed adapter. */
36
+ statusSource?: "cloudflare" | "data-cache" | "vinext";
33
37
  fetchImpl?: typeof fetch;
34
38
  };
35
- declare const DEFAULT_CDN_WARM_CONCURRENCY = 25;
36
- declare const DEFAULT_CDN_WARM_TIMEOUT_MS = 10000;
37
- declare const DEFAULT_STAGED_READINESS_RETRIES = 60;
38
- declare const DEFAULT_STAGED_READINESS_INTERVAL_MS = 1000;
39
- declare const DEFAULT_STAGED_READINESS_PHASE_TIMEOUT_MS = 120000;
40
- type PrerenderCdnWarmOptions = Omit<CdnWarmOptions, "paths"> & {
39
+ export declare const DEFAULT_CDN_WARM_CONCURRENCY = 25;
40
+ export declare const DEFAULT_CDN_WARM_TIMEOUT_MS = 10000;
41
+ export declare const DEFAULT_STAGED_READINESS_RETRIES = 60;
42
+ export declare const DEFAULT_STAGED_READINESS_INTERVAL_MS = 1000;
43
+ export declare const DEFAULT_STAGED_READINESS_PHASE_TIMEOUT_MS = 120000;
44
+ export type PrerenderCdnWarmOptions = Omit<CdnWarmOptions, "paths"> & {
41
45
  root: string;
42
46
  includeFallbackShells?: boolean;
43
47
  /** Use the manifest build identity unless the configured adapter cannot expose it. */
44
48
  validateBuildIdentity?: boolean;
45
49
  };
46
- type CdnWarmResult = {
50
+ export type CdnWarmResult = {
47
51
  total: number;
48
52
  warmed: number;
49
53
  skipped: number;
@@ -56,7 +60,7 @@ type CdnWarmResult = {
56
60
  warmedPlan: CdnWarmRequestPlan;
57
61
  retryPlan: CdnWarmRequestPlan;
58
62
  };
59
- type CdnWarmRequestPlan = {
63
+ export type CdnWarmRequestPlan = {
60
64
  loadingShellPaths: string[];
61
65
  pagesDataPaths: string[];
62
66
  paths: string[];
@@ -64,13 +68,13 @@ type CdnWarmRequestPlan = {
64
68
  routeHandlerPaths?: string[];
65
69
  routePatterns?: Record<string, PrerenderRoutePattern>;
66
70
  };
67
- type CdnWarmReadinessResult = {
71
+ export type CdnWarmReadinessResult = {
68
72
  ready: true;
69
73
  } | {
70
74
  error: string;
71
75
  ready: false;
72
76
  };
73
- type PrerenderWarmPlan = {
77
+ export type PrerenderWarmPlan = {
74
78
  appPaths?: string[];
75
79
  buildId?: string;
76
80
  buildIdentity?: string;
@@ -85,17 +89,20 @@ type PrerenderWarmPlan = {
85
89
  rscBuildId?: string;
86
90
  rscPaths: string[];
87
91
  };
88
- declare function readPrerenderWarmPlan(root: string, options?: {
92
+ type PrerenderWarmPlanOptions = {
93
+ includeCanonicalRsc?: boolean;
89
94
  includeFallbackShells?: boolean;
90
95
  strict?: boolean;
91
- }): PrerenderWarmPlan;
92
- declare function readPrerenderWarmPaths(root: string, options?: {
96
+ };
97
+ export declare function createPrerenderWarmPlan(root: string, manifest: PrerenderPathManifest, options?: PrerenderWarmPlanOptions): PrerenderWarmPlan;
98
+ export declare function readPrerenderWarmPlan(root: string, options?: PrerenderWarmPlanOptions): PrerenderWarmPlan;
99
+ export declare function readPrerenderWarmPaths(root: string, options?: {
93
100
  includeFallbackShells?: boolean;
94
101
  strict?: boolean;
95
102
  }): string[];
96
- declare function getWarmPathsFromPrerenderManifest(manifest: PrerenderManifest, options?: PrerenderedPathSelectionOptions): string[];
97
- declare function buildWarmupUrl(targetUrl: string, pathname: string): URL;
98
- type CdnWarmTarget = {
103
+ export declare function getWarmPathsFromPrerenderManifest(manifest: PrerenderManifest, options?: PrerenderedPathSelectionOptions): string[];
104
+ export declare function buildWarmupUrl(targetUrl: string, pathname: string): URL;
105
+ export type CdnWarmTarget = {
99
106
  headers?: HeadersInit;
100
107
  kind: "app-route" | "html" | "pages-data" | "rsc-full" | "rsc-loading-shell";
101
108
  label: string;
@@ -103,8 +110,8 @@ type CdnWarmTarget = {
103
110
  sourcePathname: string;
104
111
  route?: PrerenderRoutePattern;
105
112
  };
106
- declare function createCdnWarmTargets(options: Pick<CdnWarmOptions, "deploymentId" | "headers" | "loadingShellPaths" | "pagesDataPaths" | "paths" | "routeHandlerPaths" | "routePatterns" | "rscPaths">): Promise<CdnWarmTarget[]>;
107
- declare class CdnOperationProgress {
113
+ export declare function createCdnWarmTargets(options: Pick<CdnWarmOptions, "deploymentId" | "headers" | "loadingShellPaths" | "pagesDataPaths" | "paths" | "routeHandlerPaths" | "routePatterns" | "rscPaths">): Promise<CdnWarmTarget[]>;
114
+ export declare class CdnOperationProgress {
108
115
  private readonly isTTY;
109
116
  private lastLineLength;
110
117
  update(completed: number, total: number, label: string, phase?: string): void;
@@ -115,7 +122,7 @@ declare class CdnOperationProgress {
115
122
  * before any real cache key is filled. Every probe has a unique query key, so
116
123
  * an early response cannot make a later readiness attempt pass from cache.
117
124
  */
118
- declare function waitForCdnWarmTargetReadiness(options: Pick<CdnWarmOptions, "deploymentId" | "expectedBuildId" | "expectedRscBuildId" | "fetchImpl" | "headers" | "retries" | "targetUrl" | "timeoutMs"> & {
125
+ export declare function waitForCdnWarmTargetReadiness(options: Pick<CdnWarmOptions, "deploymentId" | "expectedBuildId" | "expectedRscBuildId" | "fetchImpl" | "headers" | "retries" | "targetUrl" | "timeoutMs"> & {
119
126
  plan: CdnWarmRequestPlan;
120
127
  maxAttempts?: number;
121
128
  phaseTimeoutMs?: number;
@@ -123,7 +130,6 @@ declare function waitForCdnWarmTargetReadiness(options: Pick<CdnWarmOptions, "de
123
130
  probeIntervalMs?: number;
124
131
  requiredConsecutiveSuccesses?: number;
125
132
  }): Promise<CdnWarmReadinessResult>;
126
- declare function warmCdnCache(options: CdnWarmOptions): Promise<CdnWarmResult>;
127
- declare function warmCdnCacheFromPrerender(options: PrerenderCdnWarmOptions): Promise<CdnWarmResult>;
128
- //#endregion
129
- export { CdnOperationProgress, CdnWarmOptions, CdnWarmReadinessResult, CdnWarmRequestPlan, CdnWarmResult, CdnWarmTarget, DEFAULT_CDN_WARM_CONCURRENCY, DEFAULT_CDN_WARM_TIMEOUT_MS, DEFAULT_STAGED_READINESS_INTERVAL_MS, DEFAULT_STAGED_READINESS_PHASE_TIMEOUT_MS, DEFAULT_STAGED_READINESS_RETRIES, PrerenderCdnWarmOptions, PrerenderWarmPlan, buildWarmupUrl, createCdnWarmTargets, getWarmPathsFromPrerenderManifest, readPrerenderWarmPaths, readPrerenderWarmPlan, waitForCdnWarmTargetReadiness, warmCdnCache, warmCdnCacheFromPrerender };
133
+ export declare function warmCdnCache(options: CdnWarmOptions): Promise<CdnWarmResult>;
134
+ export declare function warmCdnCacheFromPrerender(options: PrerenderCdnWarmOptions): Promise<CdnWarmResult>;
135
+ //#endregion
package/dist/cdn-warm.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import { VINEXT_CDN_BUILD_ID_HEADER } from "./cache/cdn-build-id.js";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
+ import { cacheabilityRoutePathname } from "vinext/internal/server/cacheability-manifest";
4
5
  import { randomUUID } from "node:crypto";
5
6
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
6
- import { VINEXT_PRERENDER_READINESS_HEADER, VINEXT_PRERENDER_READINESS_PATH, VINEXT_PRERENDER_SECRET_HEADER } from "vinext/internal/server/headers";
7
+ import { VINEXT_CACHE_HEADER, VINEXT_PRERENDER_READINESS_HEADER, VINEXT_PRERENDER_READINESS_PATH, VINEXT_PRERENDER_SECRET_HEADER } from "vinext/internal/server/headers";
7
8
  import { PRERENDER_PATHS_MANIFEST } from "vinext/internal/build/prerender-paths";
8
9
  import { getPrerenderedConcretePaths, readPrerenderManifest } from "vinext/internal/server/prerender-manifest";
9
- import { VINEXT_RSC_BUILD_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER, createCanonicalLoadingShellRscRequestHeaders, createCanonicalRscRequestHeaders, createCanonicalRscRequestUrl, createRscRequestUrl } from "vinext/internal/server/app-rsc-cache-busting";
10
+ import { VINEXT_RSC_BUILD_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER as VINEXT_RSC_VARY_HEADER$1, createCanonicalLoadingShellRscRequestHeaders, createCanonicalRscRequestHeaders, createCanonicalRscRequestUrl, createRscRequestUrl } from "vinext/internal/server/app-rsc-cache-busting";
10
11
  import { isNonCacheableCacheControl } from "vinext/shims/cdn-cache";
11
12
  import { normalizePathTrailingSlash } from "vinext/shims/url-utils";
12
13
  //#region src/cdn-warm.ts
@@ -18,7 +19,8 @@ const DEFAULT_STAGED_READINESS_PHASE_TIMEOUT_MS = 12e4;
18
19
  const DEFAULT_STAGED_READINESS_SUCCESSES = 6;
19
20
  const STAGED_READINESS_QUERY_PARAM = "__vinext_cdn_warm_readiness";
20
21
  function applyWarmPathConfig(pathname, config) {
21
- const normalized = normalizePathTrailingSlash(config.basePath ? pathname === "/" ? config.basePath : `${config.basePath}${pathname}` : pathname, config.trailingSlash === true);
22
+ const withBasePath = config.basePath ? pathname === "/" ? config.basePath : `${config.basePath}${pathname}` : pathname;
23
+ const normalized = normalizePathTrailingSlash(withBasePath, config.trailingSlash === true);
22
24
  return new URL(normalized, "http://vinext.local").pathname;
23
25
  }
24
26
  function readBuiltBuildId(root) {
@@ -36,16 +38,14 @@ function readPrerenderPathManifest(manifestPath) {
36
38
  const parsed = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
37
39
  if (!parsed || typeof parsed !== "object") return null;
38
40
  const manifest = parsed;
39
- 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("/"))))) || 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")))) || 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;
40
42
  return manifest;
41
43
  } catch (error) {
42
44
  console.warn(`[vinext] Failed to read prerender path manifest at ${manifestPath}:`, error);
43
45
  return null;
44
46
  }
45
47
  }
46
- function readPrerenderPathWarmPaths(root, options) {
47
- const manifest = readPrerenderPathManifest(path.join(root, "dist", "server", PRERENDER_PATHS_MANIFEST));
48
- if (!manifest) return null;
48
+ function createPrerenderPathWarmPaths(root, manifest, options) {
49
49
  const builtBuildId = readBuiltBuildId(root);
50
50
  if (!manifest.buildId || !builtBuildId || manifest.buildId !== builtBuildId) {
51
51
  const message = "[vinext] CDN warmup skipped: prerender path manifest buildId does not match dist/server/BUILD_ID.";
@@ -59,10 +59,10 @@ function readPrerenderPathWarmPaths(root, options) {
59
59
  paths: manifest.paths.filter((pathname) => pathname.startsWith("/")).filter((pathname) => !manifest.excludedWarmPaths?.includes(pathname)).map((pathname) => applyWarmPathConfig(pathname, manifest))
60
60
  };
61
61
  }
62
- function readPrerenderWarmPlan(root, options) {
63
- const pathPlan = readPrerenderPathWarmPaths(root, options);
62
+ function createPrerenderWarmPlan(root, manifest, options) {
63
+ const pathPlan = createPrerenderPathWarmPaths(root, manifest, options);
64
64
  if (!pathPlan) {
65
- const message = "[vinext] CDN warmup skipped: prerender path manifest not found.";
65
+ const message = "[vinext] CDN warmup skipped: prerender path discovery is invalid.";
66
66
  if (options?.strict) throw new Error(message);
67
67
  return {
68
68
  loadingShellPaths: [],
@@ -70,8 +70,7 @@ function readPrerenderWarmPlan(root, options) {
70
70
  rscPaths: []
71
71
  };
72
72
  }
73
- const { manifest } = pathPlan;
74
- const supportsCanonicalRsc = manifest.responseVary === "verbatim" && manifest.rscPaths !== void 0 && manifest.rscBuildId !== void 0;
73
+ const supportsCanonicalRsc = (manifest.responseVary === "verbatim" || options?.includeCanonicalRsc === true) && manifest.rscPaths !== void 0 && manifest.rscBuildId !== void 0;
75
74
  const applyConfig = (pathname) => applyWarmPathConfig(pathname, manifest);
76
75
  const routePatterns = manifest.routePatterns ? Object.fromEntries(Object.entries(manifest.routePatterns).map(([pathname, route]) => [pathname.includes("/_next/data/") ? pathname : applyConfig(pathname), route])) : void 0;
77
76
  let htmlPaths = pathPlan.paths;
@@ -101,6 +100,19 @@ function readPrerenderWarmPlan(root, options) {
101
100
  ...routePatterns ? { routePatterns } : {}
102
101
  };
103
102
  }
103
+ function readPrerenderWarmPlan(root, options) {
104
+ const manifest = readPrerenderPathManifest(path.join(root, "dist", "server", PRERENDER_PATHS_MANIFEST));
105
+ if (!manifest) {
106
+ const message = "[vinext] CDN warmup skipped: prerender path manifest not found.";
107
+ if (options?.strict) throw new Error(message);
108
+ return {
109
+ loadingShellPaths: [],
110
+ paths: [],
111
+ rscPaths: []
112
+ };
113
+ }
114
+ return createPrerenderWarmPlan(root, manifest, options);
115
+ }
104
116
  function readPrerenderWarmPaths(root, options) {
105
117
  return readPrerenderWarmPlan(root, options).paths;
106
118
  }
@@ -169,6 +181,89 @@ async function fetchHeadersWithTimeout(fetchImpl, url, timeoutMs, headers, metho
169
181
  if (timer !== void 0) clearTimeout(timer);
170
182
  }
171
183
  }
184
+ const MAX_CDN_WARM_REPORT_ROUTES = 10;
185
+ const MAX_CDN_WARM_REPORT_PATTERN_WIDTH = 48;
186
+ const CDN_WARM_ROUTE_KIND_LABELS = {
187
+ "app-page": "App page",
188
+ "app-route": "Route Handler",
189
+ "pages-page": "Pages page"
190
+ };
191
+ function formatCount(count, singular, plural = `${singular}s`) {
192
+ return `${count.toLocaleString("en-US")} ${count === 1 ? singular : plural}`;
193
+ }
194
+ function formatNumber(count) {
195
+ return count.toLocaleString("en-US");
196
+ }
197
+ function truncateMiddle(value, maxLength) {
198
+ if (value.length <= maxLength) return value;
199
+ const remaining = maxLength - 1;
200
+ const startLength = Math.ceil(remaining / 2);
201
+ return `${value.slice(0, startLength)}…${value.slice(-(remaining - startLength))}`;
202
+ }
203
+ function printCdnWarmTable(headings, rows, rightAlignedFrom) {
204
+ const widths = headings.map((heading, index) => Math.max(heading.length, ...rows.map((row) => row[index]?.length ?? 0)));
205
+ for (const row of [headings, ...rows]) console.log(` ${row.map((value, index) => index >= rightAlignedFrom ? value.padStart(widths[index]) : value.padEnd(widths[index])).join(" ")}`);
206
+ }
207
+ function createCdnWarmRouteReport(targets, outcomes) {
208
+ const routes = /* @__PURE__ */ new Map();
209
+ for (const [index, target] of targets.entries()) {
210
+ const key = target.route ? `${target.route.kind}\0${target.route.pattern}` : "unmatched";
211
+ const route = routes.get(key) ?? {
212
+ failed: 0,
213
+ kind: target.route ? CDN_WARM_ROUTE_KIND_LABELS[target.route.kind] : "Other",
214
+ key,
215
+ pattern: target.route?.pattern ?? "Other discovered requests",
216
+ paths: /* @__PURE__ */ new Set(),
217
+ skipped: 0,
218
+ total: 0,
219
+ warmed: 0
220
+ };
221
+ route.paths.add(target.route?.cacheabilityProbe?.concretePathname ?? cacheabilityRoutePathname(target.pathname, target.kind));
222
+ route.total++;
223
+ const outcome = outcomes?.[index];
224
+ if (outcome) route[outcome]++;
225
+ routes.set(key, route);
226
+ }
227
+ return [...routes.values()].sort((left, right) => right.total - left.total || left.key.localeCompare(right.key));
228
+ }
229
+ function printCdnWarmRouteReport(targets, outcomes) {
230
+ if (!targets.some((target) => target.route)) return;
231
+ const routes = createCdnWarmRouteReport(targets, outcomes);
232
+ if (routes.length === 0) return;
233
+ console.log(` CDN warmup ${outcomes ? "result" : "plan"} by route:`);
234
+ const visible = routes.slice(0, MAX_CDN_WARM_REPORT_ROUTES);
235
+ const routeColumns = (route) => [truncateMiddle(route.pattern, MAX_CDN_WARM_REPORT_PATTERN_WIDTH), route.kind];
236
+ if (outcomes) printCdnWarmTable([
237
+ "Route pattern",
238
+ "Kind",
239
+ "Warmed",
240
+ "Skipped",
241
+ "Failed"
242
+ ], visible.map((route) => [
243
+ ...routeColumns(route),
244
+ `${formatNumber(route.warmed)}/${formatNumber(route.total)}`,
245
+ formatNumber(route.skipped),
246
+ formatNumber(route.failed)
247
+ ]), 2);
248
+ else printCdnWarmTable([
249
+ "Route pattern",
250
+ "Kind",
251
+ "Paths",
252
+ "Entries"
253
+ ], visible.map((route) => [
254
+ ...routeColumns(route),
255
+ formatNumber(route.paths.size),
256
+ formatNumber(route.total)
257
+ ]), 2);
258
+ const omitted = routes.slice(MAX_CDN_WARM_REPORT_ROUTES);
259
+ if (omitted.length > 0) {
260
+ const omittedEntries = omitted.reduce((total, route) => total + route.total, 0);
261
+ console.log(` ... ${formatCount(omitted.length, "additional route pattern")} omitted (${formatCount(omittedEntries, "cache entry", "cache entries")})`);
262
+ }
263
+ }
264
+ function cdnWarmTargetKey(target) {
265
+ return `${target.kind}\0${target.sourcePathname}`;
266
+ }
172
267
  async function createCdnWarmTargets(options) {
173
268
  const requests = [];
174
269
  const fullRscPaths = new Set(options.rscPaths ?? []);
@@ -257,7 +352,7 @@ var CdnOperationProgress = class {
257
352
  this.lastLineLength = 0;
258
353
  }
259
354
  };
260
- const REQUIRED_RSC_VARY_HEADERS = VINEXT_RSC_VARY_HEADER.split(",").map((name) => name.trim().toLowerCase());
355
+ const REQUIRED_RSC_VARY_HEADERS = VINEXT_RSC_VARY_HEADER$1.split(",").map((name) => name.trim().toLowerCase());
261
356
  const ADMITTED_CF_CACHE_STATUSES = /* @__PURE__ */ new Set([
262
357
  "HIT",
263
358
  "MISS",
@@ -270,6 +365,7 @@ const REUSABLE_CF_CACHE_STATUSES = /* @__PURE__ */ new Set([
270
365
  "REVALIDATED",
271
366
  "UPDATING"
272
367
  ]);
368
+ const TRANSITIONAL_VINEXT_CACHE_STATUSES = /* @__PURE__ */ new Set(["MISS", "UPDATING"]);
273
369
  const NON_CACHEABLE_CF_CACHE_STATUSES = /* @__PURE__ */ new Set(["BYPASS"]);
274
370
  const CDN_CACHE_POLICY_HEADERS = [
275
371
  "Cloudflare-CDN-Cache-Control",
@@ -350,17 +446,44 @@ function validateCachePolicy(response, requireCacheStatus, requireCacheHit = fal
350
446
  error: `CF-Cache-Status is ${cacheStatus}`
351
447
  };
352
448
  }
353
- function validateBuildIdentity(response, expectedBuildId) {
354
- if (expectedBuildId !== void 0 && response.headers.get("X-Vinext-Build-Id") !== expectedBuildId) return {
449
+ function validateVinextCacheStatus(response, missingStatus = "failed", requireCacheHit = false) {
450
+ const status = response.headers.get(VINEXT_CACHE_HEADER)?.trim().toUpperCase();
451
+ if (requireCacheHit && status !== "HIT") return {
355
452
  outcome: "failed",
356
- error: `response ${VINEXT_CDN_BUILD_ID_HEADER} does not match build ${expectedBuildId} (received ${response.headers.get("X-Vinext-Build-Id") ?? "missing"})`
453
+ error: `${VINEXT_CACHE_HEADER} is ${status ?? "missing"}; the cache fill is not reusable`
357
454
  };
455
+ if (status === "MISS" || status === "HIT" || status === "UPDATING") return { outcome: "warmed" };
456
+ if (status === "BYPASS" || status === "DYNAMIC") return {
457
+ outcome: "skipped",
458
+ reason: `${VINEXT_CACHE_HEADER} is ${status}`
459
+ };
460
+ return missingStatus === "skipped" ? {
461
+ outcome: "skipped",
462
+ reason: `response is missing ${VINEXT_CACHE_HEADER}`
463
+ } : {
464
+ outcome: "failed",
465
+ error: `response is missing ${VINEXT_CACHE_HEADER}`
466
+ };
467
+ }
468
+ function validateWarmAdmission(response, statusSource, requireCacheHit) {
469
+ if (statusSource === "vinext") return validateVinextCacheStatus(response, "failed", requireCacheHit);
470
+ if (statusSource === "data-cache") return validateVinextCacheStatus(response, "skipped", requireCacheHit);
471
+ return validateCachePolicy(response, true, requireCacheHit);
472
+ }
473
+ function validateBuildIdentity(response, expectedBuildId) {
474
+ if (expectedBuildId !== void 0 && response.headers.get("X-Vinext-Build-Id") !== expectedBuildId) {
475
+ const actualBuildId = response.headers.get("X-Vinext-Build-Id") ?? "missing";
476
+ return {
477
+ outcome: "failed",
478
+ error: `response ${VINEXT_CDN_BUILD_ID_HEADER} does not match build ${expectedBuildId} (received ${actualBuildId})`
479
+ };
480
+ }
358
481
  return null;
359
482
  }
360
483
  function isExpectedTerminalStatus(status) {
361
484
  return status >= 300 && status < 400 || status === 404;
362
485
  }
363
- function validateRscWarmResponse(response, expectedBuildId, expectedRscBuildId, requireCacheHit = false) {
486
+ function validateRscWarmResponse(response, expectedBuildId, expectedRscBuildId, requireCacheHit = false, statusSource = "cloudflare") {
364
487
  const buildIdentityValidation = validateBuildIdentity(response, expectedBuildId);
365
488
  if (buildIdentityValidation) return buildIdentityValidation;
366
489
  if (expectedRscBuildId !== void 0 && response.headers.get(VINEXT_RSC_BUILD_ID_HEADER) !== expectedRscBuildId) return {
@@ -381,7 +504,7 @@ function validateRscWarmResponse(response, expectedBuildId, expectedRscBuildId,
381
504
  outcome: "failed",
382
505
  error: `expected ${VINEXT_RSC_CONTENT_TYPE} response`
383
506
  };
384
- const cachePolicyValidation = validateCachePolicy(response, true, requireCacheHit);
507
+ const cachePolicyValidation = validateWarmAdmission(response, statusSource, requireCacheHit);
385
508
  if (cachePolicyValidation.outcome !== "warmed") return cachePolicyValidation;
386
509
  if (terminalResponse && !response.headers.get("Content-Type")?.toLowerCase().startsWith(VINEXT_RSC_CONTENT_TYPE)) return {
387
510
  outcome: "failed",
@@ -395,7 +518,7 @@ function validateRscWarmResponse(response, expectedBuildId, expectedRscBuildId,
395
518
  };
396
519
  return { outcome: "warmed" };
397
520
  }
398
- function validateHtmlWarmResponse(response, expectedBuildId, requireCacheHit = false) {
521
+ function validateHtmlWarmResponse(response, expectedBuildId, requireCacheHit = false, statusSource = "cloudflare") {
399
522
  const buildIdentityValidation = validateBuildIdentity(response, expectedBuildId);
400
523
  if (buildIdentityValidation) return buildIdentityValidation;
401
524
  if (response.redirected) return {
@@ -408,12 +531,12 @@ function validateHtmlWarmResponse(response, expectedBuildId, requireCacheHit = f
408
531
  error: `HTTP ${response.status}`
409
532
  };
410
533
  }
411
- const cachePolicyValidation = validateCachePolicy(response, true, requireCacheHit);
534
+ const cachePolicyValidation = validateWarmAdmission(response, statusSource, requireCacheHit);
412
535
  if (cachePolicyValidation.outcome !== "warmed") return cachePolicyValidation;
413
536
  return { outcome: "warmed" };
414
537
  }
415
- function validatePagesDataWarmResponse(response, expectedBuildId, requireCacheHit = false) {
416
- const validation = validateHtmlWarmResponse(response, expectedBuildId, requireCacheHit);
538
+ function validatePagesDataWarmResponse(response, expectedBuildId, requireCacheHit = false, statusSource = "cloudflare") {
539
+ const validation = validateHtmlWarmResponse(response, expectedBuildId, requireCacheHit, statusSource);
417
540
  if (validation.outcome !== "warmed") return validation;
418
541
  if (!response.headers.get("Content-Type")?.toLowerCase().startsWith("application/json")) return {
419
542
  outcome: "failed",
@@ -532,10 +655,19 @@ function shouldRetryValidationFailure(response, target, options) {
532
655
  if (expectedIdentities.some((matches) => !matches)) return true;
533
656
  return isRetryableStatus(response.status, options.retryNotFound);
534
657
  }
658
+ function isRetryableCertificationFailure(response, statusSource, retryPropagationFailures) {
659
+ if (statusSource === "cloudflare") {
660
+ const status = response.headers.get("CF-Cache-Status")?.trim().toUpperCase();
661
+ return !REUSABLE_CF_CACHE_STATUSES.has(status ?? "") && (retryPropagationFailures || ADMITTED_CF_CACHE_STATUSES.has(status ?? ""));
662
+ }
663
+ const status = response.headers.get(VINEXT_CACHE_HEADER)?.trim().toUpperCase();
664
+ return TRANSITIONAL_VINEXT_CACHE_STATUSES.has(status ?? "");
665
+ }
535
666
  async function warmOnePath(target, options) {
536
667
  const url = buildWarmupUrl(options.targetUrl, target.pathname);
537
668
  let lastError = "request failed before the first attempt";
538
669
  let lastRetryable = true;
670
+ let lastSkippedReason = null;
539
671
  const phaseDeadlineError = () => `CDN warmup exceeded its ${options.phaseTimeoutMs}ms phase deadline`;
540
672
  const remainingPhaseMs = () => options.deadlineAt === void 0 ? options.timeoutMs : options.deadlineAt - Date.now();
541
673
  const canRetry = (attempt) => attempt < options.retries;
@@ -555,6 +687,7 @@ async function warmOnePath(target, options) {
555
687
  retryable: false
556
688
  };
557
689
  const attemptTimeoutMs = Math.min(options.timeoutMs, remainingMs);
690
+ const phaseLimitedAttempt = options.deadlineAt !== void 0 && remainingMs <= options.timeoutMs;
558
691
  try {
559
692
  const response = options.requireCacheHit ? await fetchHeadersWithTimeout(options.fetchImpl, url, attemptTimeoutMs, target.headers ?? options.headers) : (await fetchWithTimeout(options.fetchImpl, url, attemptTimeoutMs, target.headers ?? options.headers, "manual")).response;
560
693
  if (options.deadlineAt !== void 0 && Date.now() >= options.deadlineAt) return {
@@ -566,21 +699,33 @@ async function warmOnePath(target, options) {
566
699
  if (options.requireCacheHit) response.body?.cancel().catch(() => {});
567
700
  if (process.env.VINEXT_CDN_WARM_DEBUG === "1") console.log(` CDN warm debug ${target.label} attempt ${attempt + 1}: ${url.pathname}${url.search} HTTP ${response.status} cache=${response.headers.get("CF-Cache-Status") ?? "missing"} ray=${response.headers.get("CF-Ray") ?? "missing"} encoding=${response.headers.get("Content-Encoding") ?? "identity"} rscBuild=${response.headers.get(VINEXT_RSC_BUILD_ID_HEADER) ?? "missing"}`);
568
701
  if (target.kind.startsWith("rsc-")) {
569
- const validation = validateRscWarmResponse(response, options.expectedBuildId, options.expectedRscBuildId, options.requireCacheHit);
702
+ const validation = validateRscWarmResponse(response, options.expectedBuildId, options.expectedRscBuildId, options.requireCacheHit, options.statusSource);
570
703
  if (validation.outcome === "warmed") return {
571
704
  path: target.label,
572
705
  ok: true,
573
706
  skipped: false
574
707
  };
575
- if (validation.outcome === "skipped") return {
576
- path: target.label,
577
- ok: true,
578
- skipped: true,
579
- reason: validation.reason
580
- };
708
+ if (validation.outcome === "skipped") {
709
+ if (!options.retrySkipped) return {
710
+ path: target.label,
711
+ ok: true,
712
+ skipped: true,
713
+ reason: validation.reason
714
+ };
715
+ lastSkippedReason = validation.reason;
716
+ lastRetryable = true;
717
+ if (!canRetry(attempt)) break;
718
+ if (!await waitBeforeRetry()) return {
719
+ path: target.label,
720
+ ok: false,
721
+ error: phaseDeadlineError(),
722
+ retryable: false
723
+ };
724
+ continue;
725
+ }
726
+ lastSkippedReason = null;
581
727
  lastError = validation.error;
582
- const cacheStatus = response.headers.get("CF-Cache-Status")?.trim().toUpperCase();
583
- lastRetryable = options.requireCacheHit && ADMITTED_CF_CACHE_STATUSES.has(cacheStatus ?? "") && !REUSABLE_CF_CACHE_STATUSES.has(cacheStatus ?? "") || shouldRetryValidationFailure(response, target, options);
728
+ lastRetryable = options.requireCacheHit && isRetryableCertificationFailure(response, options.statusSource, options.retryPropagationFailures) || shouldRetryValidationFailure(response, target, options);
584
729
  if (!lastRetryable) break;
585
730
  if (!canRetry(attempt)) break;
586
731
  if (!await waitBeforeRetry()) return {
@@ -591,26 +736,46 @@ async function warmOnePath(target, options) {
591
736
  };
592
737
  continue;
593
738
  }
594
- const validation = target.kind === "pages-data" ? validatePagesDataWarmResponse(response, options.expectedBuildId, options.requireCacheHit) : validateHtmlWarmResponse(response, options.expectedBuildId, options.requireCacheHit);
739
+ const validation = target.kind === "pages-data" ? validatePagesDataWarmResponse(response, options.expectedBuildId, options.requireCacheHit, options.statusSource) : validateHtmlWarmResponse(response, options.expectedBuildId, options.requireCacheHit, options.statusSource);
595
740
  if (validation.outcome === "warmed") return {
596
741
  path: target.label,
597
742
  ok: true,
598
743
  skipped: false
599
744
  };
600
- if (validation.outcome === "skipped") return {
601
- path: target.label,
602
- ok: true,
603
- skipped: true,
604
- reason: validation.reason
605
- };
745
+ if (validation.outcome === "skipped") {
746
+ if (!options.retrySkipped) return {
747
+ path: target.label,
748
+ ok: true,
749
+ skipped: true,
750
+ reason: validation.reason
751
+ };
752
+ lastSkippedReason = validation.reason;
753
+ lastRetryable = true;
754
+ if (!canRetry(attempt)) break;
755
+ if (!await waitBeforeRetry()) return {
756
+ path: target.label,
757
+ ok: false,
758
+ error: phaseDeadlineError(),
759
+ retryable: false
760
+ };
761
+ continue;
762
+ }
763
+ lastSkippedReason = null;
606
764
  lastError = validation.error;
607
- const cacheStatus = response.headers.get("CF-Cache-Status")?.trim().toUpperCase();
608
- lastRetryable = options.requireCacheHit && ADMITTED_CF_CACHE_STATUSES.has(cacheStatus ?? "") && !REUSABLE_CF_CACHE_STATUSES.has(cacheStatus ?? "") || shouldRetryValidationFailure(response, target, options);
765
+ lastRetryable = options.requireCacheHit && isRetryableCertificationFailure(response, options.statusSource, options.retryPropagationFailures) || shouldRetryValidationFailure(response, target, options);
609
766
  if (!lastRetryable) break;
610
767
  } catch (error) {
768
+ lastSkippedReason = null;
611
769
  lastRetryable = true;
612
- if (error instanceof DOMException && error.name === "AbortError") lastError = options.deadlineAt !== void 0 && Date.now() >= options.deadlineAt ? phaseDeadlineError() : `timed out after ${attemptTimeoutMs}ms`;
613
- else lastError = error instanceof Error ? error.message : String(error);
770
+ if (error instanceof DOMException && error.name === "AbortError") {
771
+ if (phaseLimitedAttempt) return {
772
+ path: target.label,
773
+ ok: false,
774
+ error: phaseDeadlineError(),
775
+ retryable: false
776
+ };
777
+ lastError = `timed out after ${attemptTimeoutMs}ms`;
778
+ } else lastError = error instanceof Error ? error.message : String(error);
614
779
  }
615
780
  if (!canRetry(attempt)) break;
616
781
  if (!await waitBeforeRetry()) return {
@@ -620,11 +785,16 @@ async function warmOnePath(target, options) {
620
785
  retryable: false
621
786
  };
622
787
  }
623
- return {
788
+ return lastSkippedReason === null ? {
624
789
  path: target.label,
625
790
  ok: false,
626
791
  error: lastError,
627
792
  retryable: lastRetryable
793
+ } : {
794
+ path: target.label,
795
+ ok: true,
796
+ skipped: true,
797
+ reason: lastSkippedReason
628
798
  };
629
799
  }
630
800
  async function runWithConcurrency(items, concurrency, fn) {
@@ -677,7 +847,8 @@ async function warmCdnCache(options) {
677
847
  rscPaths: []
678
848
  }
679
849
  };
680
- console.log(`\n Warming CDN cache with ${requests.length} discovered request(s)...`);
850
+ console.log(`\n Warming ${formatCount(requests.length, "CDN cache entry", "CDN cache entries")}...`);
851
+ printCdnWarmRouteReport(requests);
681
852
  const progress = new CdnOperationProgress();
682
853
  let completedRequests = 0;
683
854
  progress.update(0, requests.length, "starting warmup");
@@ -697,7 +868,9 @@ async function warmCdnCache(options) {
697
868
  retryDelayMs: isPropagationRequest ? propagationRetryDelayMs : normalRetryDelayMs,
698
869
  retryNotFound: isPropagationRequest,
699
870
  phaseTimeoutMs,
700
- requireCacheHit: options.requireCacheHit === true
871
+ requireCacheHit: options.requireCacheHit === true,
872
+ statusSource: options.statusSource ?? "cloudflare",
873
+ retrySkipped: isPropagationRequest && options.retrySkippedTargetKeys?.has(cdnWarmTargetKey(target)) === true
701
874
  });
702
875
  };
703
876
  const warmRequest = async (target, retryMode = "normal") => {
@@ -708,24 +881,24 @@ async function warmCdnCache(options) {
708
881
  };
709
882
  const warmPropagatingPass = async (targets) => {
710
883
  const results = await runWithConcurrency(targets, concurrency, (target) => warmRequest(target, "propagation-pass"));
711
- const retryableFailed = targets.map((target, index) => ({
884
+ const retryable = targets.map((target, index) => ({
712
885
  index,
713
886
  result: results[index],
714
887
  target
715
- })).filter((entry) => !entry.result.ok).filter(({ result }) => result.retryable);
716
- if (retryableFailed.length === 0 || propagationRetries === 0) return results;
888
+ })).filter(({ result, target }) => result.ok ? result.skipped && options.retrySkippedTargetKeys?.has(cdnWarmTargetKey(target)) === true : result.retryable);
889
+ if (retryable.length === 0 || propagationRetries === 0) return results;
717
890
  progress.finish();
718
- console.log(` CDN warmup: retrying ${retryableFailed.length} failed request(s) after completing the initial pass...`);
891
+ console.log(` CDN warmup: retrying ${retryable.length} unresolved request(s) after completing the initial pass...`);
719
892
  let completedRetries = 0;
720
- progress.update(0, retryableFailed.length, "starting retry pass", "Retrying CDN cache");
721
- const retried = await runWithConcurrency(retryableFailed.map(({ target }) => target), concurrency, async (target) => {
893
+ progress.update(0, retryable.length, "starting retry pass", "Retrying CDN cache");
894
+ const retried = await runWithConcurrency(retryable.map(({ target }) => target), concurrency, async (target) => {
722
895
  const result = await warmTarget(target, "propagation-retry");
723
896
  completedRetries++;
724
- progress.update(completedRetries, retryableFailed.length, target.label, "Retrying CDN cache");
897
+ progress.update(completedRetries, retryable.length, target.label, "Retrying CDN cache");
725
898
  return result;
726
899
  });
727
900
  progress.finish();
728
- for (const [retryIndex, { index }] of retryableFailed.entries()) results[index] = retried[retryIndex];
901
+ for (const [retryIndex, { index }] of retryable.entries()) results[index] = retried[retryIndex];
729
902
  return results;
730
903
  };
731
904
  let results;
@@ -752,6 +925,7 @@ async function warmCdnCache(options) {
752
925
  const warmed = results.length - failures.length - skippedResults.length;
753
926
  const warmedRouteHandlerPaths = warmedRequests.filter((target) => target.kind === "app-route").map((target) => target.sourcePathname);
754
927
  const failedRouteHandlerPaths = failedRequests.filter(({ target }) => target.kind === "app-route").map(({ target }) => target.sourcePathname);
928
+ printCdnWarmRouteReport(requests, results.map((result) => !result.ok ? "failed" : result.skipped ? "skipped" : "warmed"));
755
929
  console.log(` CDN warmup: ${warmed} warmed, ${skippedResults.length} skipped, ${failures.length} failed.`);
756
930
  for (const skipped of skippedResults.slice(0, 5)) console.log(` CDN warmup skipped ${skipped.path}: ${skipped.reason}`);
757
931
  if (failures.length > 0) {
@@ -804,4 +978,4 @@ async function warmCdnCacheFromPrerender(options) {
804
978
  });
805
979
  }
806
980
  //#endregion
807
- export { CdnOperationProgress, DEFAULT_CDN_WARM_CONCURRENCY, DEFAULT_CDN_WARM_TIMEOUT_MS, DEFAULT_STAGED_READINESS_INTERVAL_MS, DEFAULT_STAGED_READINESS_PHASE_TIMEOUT_MS, DEFAULT_STAGED_READINESS_RETRIES, buildWarmupUrl, createCdnWarmTargets, getWarmPathsFromPrerenderManifest, readPrerenderWarmPaths, readPrerenderWarmPlan, waitForCdnWarmTargetReadiness, warmCdnCache, warmCdnCacheFromPrerender };
981
+ export { CdnOperationProgress, DEFAULT_CDN_WARM_CONCURRENCY, DEFAULT_CDN_WARM_TIMEOUT_MS, DEFAULT_STAGED_READINESS_INTERVAL_MS, DEFAULT_STAGED_READINESS_PHASE_TIMEOUT_MS, DEFAULT_STAGED_READINESS_RETRIES, buildWarmupUrl, createCdnWarmTargets, createPrerenderWarmPlan, getWarmPathsFromPrerenderManifest, readPrerenderWarmPaths, readPrerenderWarmPlan, waitForCdnWarmTargetReadiness, warmCdnCache, warmCdnCacheFromPrerender };