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

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 (64) hide show
  1. package/README.md +94 -1
  2. package/dist/cache/cdn-adapter-config.d.ts +30 -0
  3. package/dist/cache/cdn-adapter-config.js +87 -0
  4. package/dist/cache/cdn-adapter.d.ts +47 -15
  5. package/dist/cache/cdn-adapter.js +49 -13
  6. package/dist/cache/cdn-adapter.runtime.d.ts +26 -4
  7. package/dist/cache/cdn-adapter.runtime.js +150 -37
  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 +6 -0
  11. package/dist/cache/cdn-build-id.js +9 -0
  12. package/dist/cache/kv-data-adapter.d.ts +32 -8
  13. package/dist/cache/kv-data-adapter.js +5 -1
  14. package/dist/cache/kv-data-adapter.runtime.d.ts +45 -13
  15. package/dist/cache/kv-data-adapter.runtime.js +132 -52
  16. package/dist/cache/kv-key.d.ts +15 -0
  17. package/dist/cache/kv-key.js +43 -0
  18. package/dist/cache/response-store-adapter.d.ts +73 -0
  19. package/dist/cache/response-store-adapter.js +64 -0
  20. package/dist/cache/response-store-adapter.self-contained.worker.d.ts +18 -0
  21. package/dist/cache/response-store-adapter.self-contained.worker.js +12 -0
  22. package/dist/cache/response-store-adapter.service-binding.worker.d.ts +16 -0
  23. package/dist/cache/response-store-adapter.service-binding.worker.js +11 -0
  24. package/dist/cache/response-store-adapter.worker.d.ts +14 -0
  25. package/dist/cache/response-store-adapter.worker.js +278 -0
  26. package/dist/cache/response-store-cdn.runtime.d.ts +5 -0
  27. package/dist/cache/response-store-cdn.runtime.js +41 -0
  28. package/dist/cache/response-store-data.runtime.d.ts +34 -0
  29. package/dist/cache/response-store-data.runtime.js +300 -0
  30. package/dist/cacheability-artifact.d.ts +9 -0
  31. package/dist/cacheability-artifact.js +191 -0
  32. package/dist/cacheability-manifest-limits.d.ts +4 -0
  33. package/dist/cacheability-manifest-limits.js +7 -0
  34. package/dist/cacheability-probe.d.ts +49 -0
  35. package/dist/cacheability-probe.js +660 -0
  36. package/dist/cdn-warm.d.ts +110 -12
  37. package/dist/cdn-warm.js +880 -82
  38. package/dist/cli.d.ts +1 -1
  39. package/dist/cli.js +15 -1
  40. package/dist/deploy-config.d.ts +13 -11
  41. package/dist/deploy-config.js +21 -3
  42. package/dist/deploy-help.d.ts +3 -4
  43. package/dist/deploy-help.js +61 -18
  44. package/dist/deploy.d.ts +121 -41
  45. package/dist/deploy.js +762 -104
  46. package/dist/images/images-optimizer.d.ts +5 -5
  47. package/dist/images/images-optimizer.runtime.d.ts +1 -5
  48. package/dist/prerender-kv-populate.d.ts +3 -4
  49. package/dist/prerender-kv-populate.js +51 -18
  50. package/dist/tpr.d.ts +32 -70
  51. package/dist/tpr.js +45 -602
  52. package/dist/utils/cache-control-metadata.d.ts +3 -4
  53. package/dist/version-deploy.d.ts +19 -17
  54. package/dist/version-deploy.js +32 -13
  55. package/dist/version-headers.d.ts +2 -0
  56. package/dist/version-headers.js +2 -0
  57. package/dist/worker-deployment-url.d.ts +9 -0
  58. package/dist/worker-deployment-url.js +45 -0
  59. package/dist/workers-dev-url.d.ts +2 -3
  60. package/dist/wrangler-config.d.ts +19 -0
  61. package/dist/wrangler-config.js +294 -0
  62. package/dist/wrangler-version-metadata.d.ts +7 -0
  63. package/dist/wrangler-version-metadata.js +9 -0
  64. package/package.json +14 -11
@@ -0,0 +1,660 @@
1
+ import { MAX_CACHEABILITY_MANIFEST_BYTES, cacheabilityManifestByteLimitError } from "./cacheability-manifest-limits.js";
2
+ import "./cache/cdn-build-id.js";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { Buffer } from "node:buffer";
6
+ import { cacheabilityManifestRouteKey, cacheabilityRequestIdentity, cacheabilityRoutePathname, normalizeCacheabilityRoutePathname } from "vinext/internal/server/cacheability-manifest";
7
+ import { randomUUID } from "node:crypto";
8
+ import { setTimeout as setTimeout$1 } from "node:timers/promises";
9
+ import { VINEXT_CACHEABILITY_PROBE_HEADER, VINEXT_CACHEABILITY_PROBE_QUERY_PARAM, VINEXT_CACHEABILITY_PROBE_ROUTE_HEADER, VINEXT_PRERENDER_SECRET_HEADER } from "vinext/internal/server/headers";
10
+ //#region src/cacheability-probe.ts
11
+ const DEFAULT_CACHEABILITY_PROBE_PHASE_TIMEOUT_MS = 12e4;
12
+ const DEFAULT_CACHEABILITY_PROBE_RETRIES = 2;
13
+ const DEFAULT_CACHEABILITY_PROBE_RETRY_DELAY_MS = 1e3;
14
+ const DEFAULT_CACHEABILITY_PROBE_REQUEST_TIMEOUT_MS = 3e4;
15
+ const MAX_CACHEABILITY_PROBE_ENVELOPE_BYTES = 65536;
16
+ function sharedPathPrefix(pathnames) {
17
+ if (pathnames.length === 0) return null;
18
+ let prefix = pathnames[0];
19
+ for (const pathname of pathnames.slice(1)) {
20
+ const limit = Math.min(prefix.length, pathname.length);
21
+ let length = 0;
22
+ while (length < limit && prefix.charCodeAt(length) === pathname.charCodeAt(length)) length++;
23
+ prefix = prefix.slice(0, length);
24
+ }
25
+ while (prefix.length > 1 && pathnames.some((pathname) => pathname !== prefix && (prefix.endsWith("/") ? !pathname.startsWith(prefix) : !pathname.startsWith(`${prefix}/`)))) {
26
+ const slash = prefix.lastIndexOf("/");
27
+ prefix = slash <= 0 ? "/" : prefix.slice(0, slash + 1);
28
+ }
29
+ return prefix || null;
30
+ }
31
+ /** Remove a shared pathname prefix only when it makes the serialized route smaller. */
32
+ function compactManifestRoutePaths(route) {
33
+ const pathPrefix = sharedPathPrefix([...route.runtimePaths ?? [], ...Object.values(route.staticPaths ?? {}).flatMap((paths) => paths ?? [])]);
34
+ if (!pathPrefix) return route;
35
+ const compacted = {
36
+ ...route,
37
+ pathPrefix,
38
+ ...route.runtimePaths ? { runtimePaths: route.runtimePaths.map((pathname) => pathname.slice(pathPrefix.length)) } : {},
39
+ ...route.staticPaths ? { staticPaths: Object.fromEntries(Object.entries(route.staticPaths).map(([representation, paths]) => [representation, paths.map((pathname) => pathname.slice(pathPrefix.length))])) } : {}
40
+ };
41
+ return Buffer.byteLength(JSON.stringify(compacted)) < Buffer.byteLength(JSON.stringify(route)) ? compacted : route;
42
+ }
43
+ function isProbeRouteState(value) {
44
+ return value === "static-candidate" || value === "dynamic" || value === "probe-failed";
45
+ }
46
+ function isRetryableProbeHttpStatus(status) {
47
+ return status === 404 || status === 408 || status === 429 || status >= 502;
48
+ }
49
+ function readPrerenderSecret(root) {
50
+ const manifestPath = path.join(root, "dist", "server", "vinext-server.json");
51
+ const parsed = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
52
+ const secret = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed.prerenderSecret : void 0;
53
+ if (typeof secret !== "string" || secret.length === 0) throw new Error("Cannot probe staged Worker cacheability because dist/server/vinext-server.json does not contain a prerender secret. Rebuild the app before deploying.");
54
+ return secret;
55
+ }
56
+ async function readProbeEnvelope(response) {
57
+ if (!response.body) throw new Error("probe returned invalid JSON");
58
+ const reader = response.body.getReader();
59
+ const chunks = [];
60
+ let bytes = 0;
61
+ try {
62
+ while (true) {
63
+ const result = await reader.read();
64
+ if (result.done) break;
65
+ bytes += result.value.byteLength;
66
+ if (bytes > MAX_CACHEABILITY_PROBE_ENVELOPE_BYTES) {
67
+ await reader.cancel().catch(() => {});
68
+ return {
69
+ reason: `probe response exceeded ${MAX_CACHEABILITY_PROBE_ENVELOPE_BYTES} bytes`,
70
+ state: "probe-failed",
71
+ version: 1
72
+ };
73
+ }
74
+ chunks.push(result.value);
75
+ }
76
+ } finally {
77
+ reader.releaseLock();
78
+ }
79
+ try {
80
+ return JSON.parse(Buffer.concat(chunks, bytes).toString("utf8"));
81
+ } catch {
82
+ throw new Error("probe returned invalid JSON");
83
+ }
84
+ }
85
+ async function probeTarget(options) {
86
+ const headers = new Headers(options.headers);
87
+ for (const [name, value] of new Headers(options.target.headers)) headers.set(name, value);
88
+ headers.set(VINEXT_CACHEABILITY_PROBE_HEADER, "1");
89
+ if (options.target.route) headers.set(VINEXT_CACHEABILITY_PROBE_ROUTE_HEADER, encodeURIComponent(JSON.stringify([options.target.route.kind, options.target.route.pattern])));
90
+ headers.set(VINEXT_PRERENDER_SECRET_HEADER, options.secret);
91
+ let reason = "probe failed";
92
+ const probeId = randomUUID();
93
+ const phaseTimeoutPayload = () => ({
94
+ phaseTimedOut: true,
95
+ reason: `cacheability probing made no progress for ${options.phaseTimeoutMs}ms`,
96
+ state: "probe-failed",
97
+ version: 1
98
+ });
99
+ let ordinaryFailures = 0;
100
+ let retryable = true;
101
+ for (let attempt = 0;; attempt++) {
102
+ if (options.getDeadlineAt() - Date.now() <= 0) return phaseTimeoutPayload();
103
+ retryable = true;
104
+ const controller = new AbortController();
105
+ const requestDeadlineAt = Date.now() + options.timeoutMs;
106
+ let timeout;
107
+ let timedOutBy = null;
108
+ let retryUntilDeadline = false;
109
+ try {
110
+ const url = new URL(options.target.pathname, options.targetUrl);
111
+ url.searchParams.set(VINEXT_CACHEABILITY_PROBE_QUERY_PARAM, `${probeId}-${attempt}`);
112
+ const request = (async () => {
113
+ const response = await options.fetchImpl(url, {
114
+ headers,
115
+ redirect: "manual",
116
+ signal: controller.signal
117
+ });
118
+ if (options.expectedBuildId !== void 0 && response.headers.get("X-Vinext-Build-Id") !== options.expectedBuildId) {
119
+ response.body?.cancel().catch(() => {});
120
+ return {
121
+ kind: "retry",
122
+ reason: "probe reached an unexpected Worker build",
123
+ retryable: true,
124
+ retryUntilDeadline: true
125
+ };
126
+ }
127
+ if (!response.ok) {
128
+ response.body?.cancel().catch(() => {});
129
+ return {
130
+ kind: "retry",
131
+ reason: `probe returned HTTP ${response.status}`,
132
+ retryable: isRetryableProbeHttpStatus(response.status),
133
+ retryUntilDeadline: false
134
+ };
135
+ }
136
+ const payload = await readProbeEnvelope(response);
137
+ if (payload.state === "probe-failed" && payload.retryable === true) return {
138
+ kind: "retry",
139
+ reason: payload.reason ?? "probe failed",
140
+ retryable: true,
141
+ retryUntilDeadline: false
142
+ };
143
+ return {
144
+ kind: "complete",
145
+ payload
146
+ };
147
+ })();
148
+ const timedOut = new Promise((_resolve, reject) => {
149
+ const checkDeadline = () => {
150
+ const phaseDeadlineAt = options.getDeadlineAt();
151
+ const remainingMs = Math.min(requestDeadlineAt, phaseDeadlineAt) - Date.now();
152
+ if (remainingMs > 0) {
153
+ timeout = setTimeout(checkDeadline, remainingMs);
154
+ return;
155
+ }
156
+ timedOutBy = requestDeadlineAt <= phaseDeadlineAt ? "request" : "phase";
157
+ controller.abort();
158
+ reject(new DOMException("Probe deadline exceeded", "AbortError"));
159
+ };
160
+ checkDeadline();
161
+ });
162
+ const result = await Promise.race([request, timedOut]);
163
+ if (Date.now() >= options.getDeadlineAt()) return phaseTimeoutPayload();
164
+ if (result.kind === "complete") return result.payload;
165
+ reason = result.reason;
166
+ retryable = result.retryable;
167
+ retryUntilDeadline = result.retryUntilDeadline;
168
+ } catch (error) {
169
+ if (timedOutBy === "phase" || Date.now() >= options.getDeadlineAt()) return phaseTimeoutPayload();
170
+ reason = error instanceof Error && error.name === "AbortError" ? `probe timed out after ${options.timeoutMs}ms` : error instanceof Error ? error.message : String(error);
171
+ } finally {
172
+ if (timeout !== void 0) clearTimeout(timeout);
173
+ }
174
+ if (!retryable) break;
175
+ if (!retryUntilDeadline && ++ordinaryFailures > options.retries) break;
176
+ if (options.retryDelayMs > 0) {
177
+ const delayMs = Math.min(options.retryDelayMs, Math.max(0, options.getDeadlineAt() - Date.now()));
178
+ if (delayMs <= 0) return phaseTimeoutPayload();
179
+ await setTimeout$1(delayMs);
180
+ }
181
+ }
182
+ return {
183
+ reason,
184
+ ...retryable ? { retryable: true } : {},
185
+ state: "probe-failed",
186
+ version: 1
187
+ };
188
+ }
189
+ async function probeStagedWorkerCacheability(options) {
190
+ const secret = readPrerenderSecret(options.root);
191
+ const concurrency = Math.max(1, options.concurrency ?? 25);
192
+ const retries = Math.max(0, options.retries ?? 2);
193
+ const retryDelayMs = Math.max(0, options.retryDelayMs ?? 1e3);
194
+ const timeoutMs = Math.max(1, options.timeoutMs ?? DEFAULT_CACHEABILITY_PROBE_REQUEST_TIMEOUT_MS);
195
+ const phaseTimeoutMs = Math.max(1, options.phaseTimeoutMs ?? 12e4);
196
+ let lastProgressAt = Date.now();
197
+ const getDeadlineAt = () => lastProgressAt + phaseTimeoutMs;
198
+ const routes = {};
199
+ const cacheableTargets = [];
200
+ const speculativeTargets = [];
201
+ const failures = [];
202
+ const maxManifestBytes = Math.min(options.manifestLimits?.maxBytes ?? 2097152, MAX_CACHEABILITY_MANIFEST_BYTES);
203
+ const emptyManifest = {
204
+ buildId: options.buildId,
205
+ routes: {},
206
+ version: 1
207
+ };
208
+ let manifestBytes = Buffer.byteLength(JSON.stringify(emptyManifest));
209
+ const routeEntryBytes = /* @__PURE__ */ new Map();
210
+ let limitFailure = null;
211
+ let phaseTimedOut = false;
212
+ let probed = 0;
213
+ let completedPathCount = 0;
214
+ let skippedPathCount = 0;
215
+ let staticPathCount = 0;
216
+ let dynamicPathCount = 0;
217
+ const targetPreference = (target) => {
218
+ if (target.route?.kind === "app-route") return target.kind === "app-route" ? 0 : 1;
219
+ return target.kind === "html" ? 0 : target.kind === "rsc-full" ? 1 : 2;
220
+ };
221
+ const patterns = /* @__PURE__ */ new Map();
222
+ const routableTargets = [];
223
+ let missingRouteMetadata = 0;
224
+ for (const target of options.targets) {
225
+ if (!target.route) {
226
+ missingRouteMetadata++;
227
+ continue;
228
+ }
229
+ const key = cacheabilityManifestRouteKey(target.route.kind, target.route.pattern);
230
+ const pattern = patterns.get(key) ?? {
231
+ canPrune: true,
232
+ groups: [],
233
+ key,
234
+ resultKeys: /* @__PURE__ */ new Set(),
235
+ pruned: false,
236
+ results: /* @__PURE__ */ new Map(),
237
+ route: target.route,
238
+ requestStageMayTerminate: false
239
+ };
240
+ pattern.requestStageMayTerminate ||= target.route.cacheabilityProbe?.requestStageMayTerminate === true;
241
+ pattern.canPrune &&= target.route.cacheabilityProbe?.canPrunePattern === true && target.route.cacheabilityProbe.routeMayResolve !== true && target.route.cacheabilityProbe.requestStageMayTerminate !== true;
242
+ patterns.set(key, pattern);
243
+ routableTargets.push(target);
244
+ }
245
+ if (missingRouteMetadata > 0) failures.push(`${missingRouteMetadata} warm target${missingRouteMetadata === 1 ? " is" : "s are"} missing route-pattern metadata`);
246
+ const targetGroups = /* @__PURE__ */ new Map();
247
+ for (const target of routableTargets) {
248
+ const route = target.route;
249
+ const key = cacheabilityManifestRouteKey(route.kind, route.pattern);
250
+ const pattern = patterns.get(key);
251
+ const routePathname = route.cacheabilityProbe?.concretePathname ?? cacheabilityRoutePathname(target.pathname, target.kind);
252
+ const resultKey = routePathname;
253
+ pattern.resultKeys.add(resultKey);
254
+ const concreteKey = `${key}\0${resultKey}`;
255
+ const group = targetGroups.get(concreteKey) ?? {
256
+ pattern,
257
+ resultKey,
258
+ routePathname,
259
+ targets: []
260
+ };
261
+ group.targets.push(target);
262
+ targetGroups.set(concreteKey, group);
263
+ }
264
+ const groups = Array.from(targetGroups.values(), (targetGroup) => {
265
+ targetGroup.targets.sort((first, second) => {
266
+ return targetPreference(first) - targetPreference(second) || first.sourcePathname.localeCompare(second.sourcePathname);
267
+ });
268
+ const group = {
269
+ ...targetGroup,
270
+ deferred: false,
271
+ primary: targetGroup.targets[0]
272
+ };
273
+ targetGroup.pattern.groups.push(group);
274
+ return group;
275
+ });
276
+ const reportProgress = () => {
277
+ options.onProgress?.({
278
+ completed: completedPathCount + (missingRouteMetadata > 0 ? 1 : 0),
279
+ dynamic: dynamicPathCount,
280
+ failed: failures.length,
281
+ probed,
282
+ skipped: skippedPathCount,
283
+ static: staticPathCount,
284
+ total: groups.length + (missingRouteMetadata > 0 ? 1 : 0)
285
+ });
286
+ };
287
+ const addRouteWithinManifestLimits = (key, route) => {
288
+ const previousBytes = routeEntryBytes.get(key);
289
+ const nextEntryBytes = Buffer.byteLength(`${JSON.stringify(key)}:${JSON.stringify(route)}`);
290
+ const nextManifestBytes = previousBytes === void 0 ? manifestBytes + nextEntryBytes + (routeEntryBytes.size > 0 ? 1 : 0) : manifestBytes - previousBytes + nextEntryBytes;
291
+ if (nextManifestBytes > maxManifestBytes) {
292
+ limitFailure = cacheabilityManifestByteLimitError(nextManifestBytes, maxManifestBytes);
293
+ return false;
294
+ }
295
+ routes[key] = route;
296
+ routeEntryBytes.set(key, nextEntryBytes);
297
+ manifestBytes = nextManifestBytes;
298
+ return true;
299
+ };
300
+ const moveGroupToResolvedRoute = (group, route) => {
301
+ const previousPattern = group.pattern;
302
+ const key = cacheabilityManifestRouteKey(route.kind, route.pattern);
303
+ let pattern = patterns.get(key);
304
+ if (!pattern) {
305
+ pattern = {
306
+ canPrune: false,
307
+ groups: [],
308
+ key,
309
+ resultKeys: /* @__PURE__ */ new Set(),
310
+ pruned: false,
311
+ results: /* @__PURE__ */ new Map(),
312
+ route,
313
+ requestStageMayTerminate: previousPattern.requestStageMayTerminate
314
+ };
315
+ patterns.set(key, pattern);
316
+ }
317
+ pattern.canPrune = false;
318
+ pattern.requestStageMayTerminate ||= previousPattern.requestStageMayTerminate;
319
+ pattern.pruned = false;
320
+ if (pattern === previousPattern) return pattern;
321
+ const previousGroupIndex = previousPattern.groups.indexOf(group);
322
+ if (previousGroupIndex !== -1) previousPattern.groups.splice(previousGroupIndex, 1);
323
+ if (!previousPattern.groups.some((candidate) => candidate.resultKey === group.resultKey)) previousPattern.resultKeys.delete(group.resultKey);
324
+ pattern.groups.push(group);
325
+ pattern.resultKeys.add(group.resultKey);
326
+ group.pattern = pattern;
327
+ return pattern;
328
+ };
329
+ const updateGroupRoutePathname = (group, routePathname) => {
330
+ const normalized = normalizeCacheabilityRoutePathname(routePathname);
331
+ if (normalized === group.routePathname) return;
332
+ const pattern = group.pattern;
333
+ const previousResultKey = group.resultKey;
334
+ group.routePathname = normalized;
335
+ group.resultKey = normalized;
336
+ if (!pattern.groups.some((candidate) => candidate.resultKey === previousResultKey)) pattern.resultKeys.delete(previousResultKey);
337
+ pattern.resultKeys.add(group.resultKey);
338
+ };
339
+ const deferPairedRepresentationsAtOriginalRoute = (group) => {
340
+ const pairedTargets = group.targets.filter((target) => target !== group.primary);
341
+ if (pairedTargets.length === 0) return;
342
+ group.targets = [group.primary];
343
+ for (const target of pairedTargets) {
344
+ const routePathname = target.route?.cacheabilityProbe?.concretePathname ?? cacheabilityRoutePathname(target.pathname, target.kind);
345
+ const deferredGroup = {
346
+ deferred: true,
347
+ pattern: group.pattern,
348
+ primary: target,
349
+ resultKey: routePathname,
350
+ routePathname,
351
+ targets: [target]
352
+ };
353
+ group.pattern.groups.push(deferredGroup);
354
+ group.pattern.resultKeys.add(routePathname);
355
+ }
356
+ };
357
+ const classifyConcretePath = async (group, attemptOptions) => {
358
+ if (group.pattern.pruned) {
359
+ skippedPathCount += 1;
360
+ completedPathCount += 1;
361
+ reportProgress();
362
+ return "done";
363
+ }
364
+ const target = group.primary;
365
+ const request = new Request(new URL(target.pathname, options.targetUrl), { headers: target.headers });
366
+ const identity = cacheabilityRequestIdentity(request);
367
+ if (!identity || identity.representation !== target.kind) {
368
+ failures.push(`${target.label}: warm request does not have a cacheable request identity`);
369
+ completedPathCount += 1;
370
+ reportProgress();
371
+ return "done";
372
+ }
373
+ const result = await probeTarget({
374
+ expectedBuildId: options.expectedResponseBuildId,
375
+ fetchImpl: options.fetchImpl ?? fetch,
376
+ getDeadlineAt,
377
+ headers: options.headers,
378
+ retries: attemptOptions.inlineRetries,
379
+ retryDelayMs,
380
+ phaseTimeoutMs,
381
+ secret,
382
+ target,
383
+ targetUrl: options.targetUrl,
384
+ timeoutMs
385
+ });
386
+ if (limitFailure) return "done";
387
+ if (result.phaseTimedOut) {
388
+ phaseTimedOut = true;
389
+ return "done";
390
+ }
391
+ if (result.state === "probe-failed" && result.retryable === true && attemptOptions.deferRetry) return "retry";
392
+ lastProgressAt = Date.now();
393
+ probed += 1;
394
+ if (result.version !== 1 || result.kind !== "app-page" && result.kind !== "app-route" && result.kind !== "pages-page" || typeof result.pattern !== "string" || !result.pattern.startsWith("/") || !isProbeRouteState(result.state) || result.scope !== void 0 && result.scope !== "identity" && result.scope !== "pattern" || result.scope === "pattern" && result.state !== "dynamic" || result.routePathname !== void 0 && (typeof result.routePathname !== "string" || !result.routePathname.startsWith("/") || result.routePathname.includes("?") || result.routePathname.includes("#")) || result.terminal !== void 0 && result.terminal !== true || result.terminal === true && (result.state !== "dynamic" || result.scope !== "identity" || !group.pattern.requestStageMayTerminate) || result.rendererStatic !== void 0 && typeof result.rendererStatic !== "boolean" || result.retryable !== void 0 && result.retryable !== true || result.retryable === true && result.state !== "probe-failed" || !Number.isInteger(result.status) || result.status < 100 || result.status > 599) {
395
+ failures.push(`${target.label}: ${result.reason ?? "probe returned an invalid envelope"}`);
396
+ completedPathCount += 1;
397
+ reportProgress();
398
+ return "done";
399
+ }
400
+ if (result.state === "probe-failed") {
401
+ failures.push(`${target.label}: ${result.reason ?? "probe failed"}`);
402
+ completedPathCount += 1;
403
+ reportProgress();
404
+ return "done";
405
+ }
406
+ if (!target.route) {
407
+ failures.push(`${target.label}: probe resolved without route metadata`);
408
+ completedPathCount += 1;
409
+ reportProgress();
410
+ return "done";
411
+ }
412
+ const resolvedRouteChanged = result.kind !== target.route.kind || result.pattern !== target.route.pattern;
413
+ const resolvedPathnameChanged = result.routePathname !== void 0 && normalizeCacheabilityRoutePathname(result.routePathname) !== group.routePathname;
414
+ if (resolvedRouteChanged) {
415
+ if (target.route.cacheabilityProbe?.routeMayResolve !== true) {
416
+ failures.push(`${target.label}: probe resolved to unexpected route ${result.pattern ?? ""}`);
417
+ completedPathCount += 1;
418
+ reportProgress();
419
+ return "done";
420
+ }
421
+ if (result.routePathname === void 0) {
422
+ failures.push(`${target.label}: probe resolved without a concrete route pathname`);
423
+ completedPathCount += 1;
424
+ reportProgress();
425
+ return "done";
426
+ }
427
+ }
428
+ if (target.route.cacheabilityProbe?.routeMayResolve === true && (resolvedRouteChanged || resolvedPathnameChanged)) deferPairedRepresentationsAtOriginalRoute(group);
429
+ if (resolvedRouteChanged) moveGroupToResolvedRoute(group, {
430
+ kind: result.kind,
431
+ pattern: result.pattern
432
+ });
433
+ if (result.routePathname !== void 0) updateGroupRoutePathname(group, result.routePathname);
434
+ const classification = {
435
+ rendererStatic: result.rendererStatic === true,
436
+ representation: target.kind,
437
+ state: result.state,
438
+ terminal: result.terminal === true
439
+ };
440
+ group.result = classification;
441
+ const previousClassification = group.pattern.results.get(group.resultKey);
442
+ if (!previousClassification || previousClassification.state === "static-candidate" && classification.state === "dynamic" || previousClassification.state === classification.state && previousClassification.rendererStatic && !classification.rendererStatic) group.pattern.results.set(group.resultKey, classification);
443
+ if (result.state === "dynamic" && result.scope === "pattern" && group.pattern.canPrune) {
444
+ group.pattern.pruned = true;
445
+ dynamicPathCount += 1;
446
+ completedPathCount += 1;
447
+ reportProgress();
448
+ return "done";
449
+ }
450
+ if (result.state === "static-candidate") staticPathCount += 1;
451
+ else dynamicPathCount += 1;
452
+ completedPathCount += 1;
453
+ reportProgress();
454
+ return "done";
455
+ };
456
+ reportProgress();
457
+ const routeMovingGroups = groups.filter((group) => group.primary.route?.cacheabilityProbe?.routeMayResolve === true);
458
+ const routeMovingGroupSet = new Set(routeMovingGroups);
459
+ const readyGroups = [...routeMovingGroups];
460
+ const readyGroupSet = new Set(routeMovingGroups);
461
+ for (const pattern of patterns.values()) {
462
+ const representative = pattern.groups[0];
463
+ if (!representative || readyGroupSet.has(representative)) continue;
464
+ readyGroups.push(representative);
465
+ readyGroupSet.add(representative);
466
+ }
467
+ for (const group of groups) {
468
+ if (readyGroupSet.has(group)) continue;
469
+ readyGroups.push(group);
470
+ readyGroupSet.add(group);
471
+ }
472
+ const deferredUntilRouteMoversSettle = [];
473
+ const retriesUsedByGroup = /* @__PURE__ */ new Map();
474
+ const retryGroupSet = /* @__PURE__ */ new Set();
475
+ const active = /* @__PURE__ */ new Set();
476
+ let nextReadyIndex = 0;
477
+ let pendingRouteMovers = routeMovingGroups.length;
478
+ const enqueue = (scheduledGroups) => {
479
+ readyGroups.push(...scheduledGroups);
480
+ };
481
+ const startGroup = (group) => {
482
+ const task = (async () => {
483
+ const isRouteMover = routeMovingGroupSet.has(group);
484
+ if (!isRouteMover && group.pattern.pruned && pendingRouteMovers > 0) {
485
+ deferredUntilRouteMoversSettle.push(group);
486
+ return;
487
+ }
488
+ const retriesUsed = retriesUsedByGroup.get(group) ?? 0;
489
+ if (await classifyConcretePath(group, {
490
+ deferRetry: retriesUsed < retries,
491
+ inlineRetries: 0
492
+ }) === "retry") {
493
+ retriesUsedByGroup.set(group, retriesUsed + 1);
494
+ retryGroupSet.add(group);
495
+ return;
496
+ }
497
+ if (isRouteMover && --pendingRouteMovers === 0) enqueue(deferredUntilRouteMoversSettle.splice(0));
498
+ })();
499
+ active.add(task);
500
+ task.then(() => active.delete(task), () => active.delete(task));
501
+ };
502
+ const runReadyGroups = async () => {
503
+ while (!limitFailure && !phaseTimedOut && (nextReadyIndex < readyGroups.length || active.size > 0)) {
504
+ while (active.size < concurrency && nextReadyIndex < readyGroups.length) startGroup(readyGroups[nextReadyIndex++]);
505
+ if (active.size > 0) await Promise.race(active);
506
+ }
507
+ await Promise.all(active);
508
+ };
509
+ await runReadyGroups();
510
+ let retryGroups = groups.filter((group) => retryGroupSet.has(group));
511
+ while (retryGroups.length > 0) {
512
+ if (retryDelayMs > 0) {
513
+ const delayMs = Math.min(retryDelayMs, Math.max(0, getDeadlineAt() - Date.now()));
514
+ if (delayMs <= 0) {
515
+ phaseTimedOut = true;
516
+ break;
517
+ }
518
+ await setTimeout$1(delayMs);
519
+ }
520
+ retryGroupSet.clear();
521
+ readyGroups.splice(0, readyGroups.length, ...retryGroups);
522
+ nextReadyIndex = 0;
523
+ await runReadyGroups();
524
+ retryGroups = groups.filter((group) => retryGroupSet.has(group));
525
+ }
526
+ if (limitFailure) throw limitFailure;
527
+ if (phaseTimedOut || Date.now() >= getDeadlineAt()) throw new Error(`cacheability probing made no progress for ${phaseTimeoutMs}ms`);
528
+ let classified = 0;
529
+ let dynamic = 0;
530
+ let skipped = 0;
531
+ const fallbackRoutes = /* @__PURE__ */ new Map();
532
+ for (const fallbackRoute of options.fallbackRoutePatterns ?? []) {
533
+ const key = cacheabilityManifestRouteKey(fallbackRoute.kind, fallbackRoute.pattern);
534
+ fallbackRoutes.set(key, fallbackRoute);
535
+ }
536
+ for (const [key, fallbackRoute] of fallbackRoutes) {
537
+ if (patterns.has(key)) continue;
538
+ if (!addRouteWithinManifestLimits(key, {
539
+ kind: fallbackRoute.kind,
540
+ pattern: fallbackRoute.pattern,
541
+ state: "static-candidate"
542
+ })) break;
543
+ classified += 1;
544
+ }
545
+ if (limitFailure) throw limitFailure;
546
+ for (const pattern of patterns.values()) {
547
+ if (pattern.pruned) {
548
+ classified += 1;
549
+ dynamic += 1;
550
+ skipped += 1;
551
+ const loadingShellTargets = pattern.groups.flatMap((group) => group.targets.filter((target) => target.kind === "rsc-loading-shell"));
552
+ if (loadingShellTargets.length > 0) {
553
+ cacheableTargets.push(...loadingShellTargets);
554
+ speculativeTargets.push(...loadingShellTargets);
555
+ const route = {
556
+ kind: pattern.route.kind,
557
+ pattern: pattern.route.pattern,
558
+ runtimeRepresentation: "rsc-loading-shell",
559
+ state: "runtime-check"
560
+ };
561
+ if (!addRouteWithinManifestLimits(pattern.key, route)) break;
562
+ }
563
+ continue;
564
+ }
565
+ if (pattern.results.size === 0 && !pattern.groups.some((group) => group.deferred)) continue;
566
+ classified += 1;
567
+ if (Array.from(pattern.results.values()).some((result) => result.state === "dynamic")) dynamic += 1;
568
+ const rendererStaticTargets = /* @__PURE__ */ new Map();
569
+ const runtimePathSet = /* @__PURE__ */ new Set();
570
+ for (const group of pattern.groups) {
571
+ if (group.deferred) {
572
+ runtimePathSet.add(group.routePathname);
573
+ cacheableTargets.push(...group.targets);
574
+ speculativeTargets.push(...group.targets);
575
+ continue;
576
+ }
577
+ const result = group.result;
578
+ if (result?.state === "static-candidate") {
579
+ if (result.rendererStatic) {
580
+ const previous = rendererStaticTargets.get(group.routePathname);
581
+ if (!previous || targetPreference(group.primary) < targetPreference(previous)) rendererStaticTargets.set(group.routePathname, group.primary);
582
+ } else runtimePathSet.add(group.routePathname);
583
+ cacheableTargets.push(...group.targets);
584
+ speculativeTargets.push(...group.targets.filter((target) => target !== group.primary));
585
+ continue;
586
+ }
587
+ const pairedTargets = group.targets.filter((target) => target !== group.primary);
588
+ if (result?.terminal !== true || pairedTargets.length > 0) runtimePathSet.add(group.routePathname);
589
+ if (!pattern.pruned) {
590
+ cacheableTargets.push(...pairedTargets);
591
+ speculativeTargets.push(...pairedTargets);
592
+ }
593
+ }
594
+ const staticPaths = {};
595
+ for (const [routePathname, staticTarget] of rendererStaticTargets) {
596
+ if (runtimePathSet.has(routePathname)) continue;
597
+ const paths = staticPaths[staticTarget.kind] ?? [];
598
+ paths.push(routePathname);
599
+ staticPaths[staticTarget.kind] = paths;
600
+ }
601
+ for (const paths of Object.values(staticPaths)) paths?.sort();
602
+ const allObservedPathsStaticallyGenerated = pattern.results.size === pattern.resultKeys.size && Array.from(pattern.results.values()).every((result) => result.state === "static-candidate") && Array.from(pattern.results.values()).every((result) => result.rendererStatic);
603
+ const hasStaticFallback = fallbackRoutes.has(pattern.key);
604
+ const soleGroup = pattern.groups.length === 1 ? pattern.groups[0] : null;
605
+ const literalPatternNamesSolePath = soleGroup !== null && !/(^|\/):/.test(pattern.route.pattern) && normalizeCacheabilityRoutePathname(pattern.route.pattern) === soleGroup.routePathname;
606
+ let route;
607
+ if (literalPatternNamesSolePath) {
608
+ const result = pattern.results.get(soleGroup.resultKey);
609
+ route = result?.state === "static-candidate" ? pattern.route.kind === "app-route" ? {
610
+ kind: pattern.route.kind,
611
+ pattern: pattern.route.pattern,
612
+ state: "static-candidate"
613
+ } : result.rendererStatic ? {
614
+ kind: pattern.route.kind,
615
+ pattern: pattern.route.pattern,
616
+ state: "runtime-check",
617
+ staticRepresentation: result.representation
618
+ } : {
619
+ kind: pattern.route.kind,
620
+ pattern: pattern.route.pattern,
621
+ state: "runtime-check"
622
+ } : {
623
+ kind: pattern.route.kind,
624
+ pattern: pattern.route.pattern,
625
+ state: "runtime-check"
626
+ };
627
+ } else route = compactManifestRoutePaths({
628
+ kind: pattern.route.kind,
629
+ pattern: pattern.route.pattern,
630
+ state: "runtime-check",
631
+ ...hasStaticFallback || allObservedPathsStaticallyGenerated ? {
632
+ allowUnknown: true,
633
+ unknownState: "static-candidate"
634
+ } : {},
635
+ ...runtimePathSet.size > 0 ? { runtimePaths: Array.from(runtimePathSet).sort() } : {},
636
+ ...Object.keys(staticPaths).length > 0 ? { staticPaths } : {}
637
+ });
638
+ if (!addRouteWithinManifestLimits(pattern.key, route)) break;
639
+ }
640
+ if (limitFailure) throw limitFailure;
641
+ const sortedRoutes = Object.fromEntries(Object.entries(routes).sort(([first], [second]) => first.localeCompare(second)));
642
+ cacheableTargets.sort((first, second) => `${first.kind}\0${first.sourcePathname}`.localeCompare(`${second.kind}\0${second.sourcePathname}`));
643
+ speculativeTargets.sort((first, second) => `${first.kind}\0${first.sourcePathname}`.localeCompare(`${second.kind}\0${second.sourcePathname}`));
644
+ return {
645
+ cacheableTargets,
646
+ classified,
647
+ dynamic,
648
+ failures,
649
+ manifest: {
650
+ buildId: options.buildId,
651
+ routes: sortedRoutes,
652
+ version: 1
653
+ },
654
+ probed,
655
+ skipped,
656
+ speculativeTargets
657
+ };
658
+ }
659
+ //#endregion
660
+ export { DEFAULT_CACHEABILITY_PROBE_PHASE_TIMEOUT_MS, DEFAULT_CACHEABILITY_PROBE_RETRIES, DEFAULT_CACHEABILITY_PROBE_RETRY_DELAY_MS, probeStagedWorkerCacheability, readPrerenderSecret };