vinext 1.0.0 → 1.0.1

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 (148) hide show
  1. package/THIRD_PARTY_LICENSES.md +26 -0
  2. package/dist/build/lifecycle.js +10 -10
  3. package/dist/build/pages-client-assets-module.js +6 -1
  4. package/dist/build/precompress.js +1 -1
  5. package/dist/build/prerender-paths.js +3 -7
  6. package/dist/build/prerender.d.ts +8 -1
  7. package/dist/build/prerender.js +134 -38
  8. package/dist/build/run-prerender.js +4 -3
  9. package/dist/build/server-entry.d.ts +2 -0
  10. package/dist/build/server-entry.js +7 -1
  11. package/dist/client/browser-globals.d.ts +4 -0
  12. package/dist/client/browser-globals.js +5 -0
  13. package/dist/client/navigation-runtime.d.ts +0 -1
  14. package/dist/client/navigation-runtime.js +2 -2
  15. package/dist/config/dotenv.js +14 -3
  16. package/dist/entries/app-rsc-entry.js +3 -2
  17. package/dist/index.js +142 -71
  18. package/dist/init-cloudflare.d.ts +1 -1
  19. package/dist/init-cloudflare.js +6 -6
  20. package/dist/init-platform.js +1 -1
  21. package/dist/init.js +4 -2
  22. package/dist/plugins/commonjs-syntax.d.ts +16 -0
  23. package/dist/plugins/commonjs-syntax.js +22 -0
  24. package/dist/plugins/ignore-dynamic-requests.d.ts +11 -0
  25. package/dist/plugins/ignore-dynamic-requests.js +54 -3
  26. package/dist/plugins/import-meta-url.js +6 -6
  27. package/dist/plugins/og-asset-ownership.d.ts +2 -0
  28. package/dist/plugins/og-asset-ownership.js +24 -13
  29. package/dist/plugins/og-assets.d.ts +18 -0
  30. package/dist/plugins/og-assets.js +50 -8
  31. package/dist/plugins/og-harfbuzz.js +38 -27
  32. package/dist/plugins/optimize-imports.js +2 -1
  33. package/dist/plugins/require-context.d.ts +1 -0
  34. package/dist/plugins/require-context.js +28 -1
  35. package/dist/plugins/scan-build-css.d.ts +12 -0
  36. package/dist/plugins/scan-build-css.js +39 -0
  37. package/dist/plugins/strip-server-exports.d.ts +2 -0
  38. package/dist/plugins/strip-server-exports.js +3 -1
  39. package/dist/plugins/transform-result.d.ts +18 -0
  40. package/dist/plugins/transform-result.js +23 -2
  41. package/dist/plugins/typeof-window.d.ts +10 -0
  42. package/dist/plugins/typeof-window.js +33 -9
  43. package/dist/routing/app-route-graph.d.ts +0 -1
  44. package/dist/routing/app-route-graph.js +2 -4
  45. package/dist/routing/app-route-ids.d.ts +3 -0
  46. package/dist/routing/app-route-ids.js +6 -0
  47. package/dist/server/app-bfcache-id.d.ts +1 -0
  48. package/dist/server/app-bfcache-id.js +7 -1
  49. package/dist/server/app-bfcache-identity.js +1 -2
  50. package/dist/server/app-browser-entry.js +4 -2
  51. package/dist/server/app-browser-hydration.d.ts +1 -1
  52. package/dist/server/app-browser-hydration.js +2 -2
  53. package/dist/server/app-browser-navigation-controller.js +1 -1
  54. package/dist/server/app-browser-state.js +1 -1
  55. package/dist/server/app-browser-stream.d.ts +1 -1
  56. package/dist/server/app-history-state.d.ts +0 -1
  57. package/dist/server/app-history-state.js +2 -5
  58. package/dist/server/app-page-cache-render.js +2 -1
  59. package/dist/server/app-page-render.js +2 -1
  60. package/dist/server/app-page-search-params-observation.js +1 -1
  61. package/dist/server/app-page-stream.js +1 -1
  62. package/dist/server/app-page-tracing.js +2 -1
  63. package/dist/server/app-pages-bridge.js +3 -2
  64. package/dist/server/app-render-abort-error.d.ts +3 -0
  65. package/dist/server/app-render-abort-error.js +8 -0
  66. package/dist/server/app-request-stage-independent-entry.js +4 -7
  67. package/dist/server/app-route-handler-cache.d.ts +2 -1
  68. package/dist/server/app-route-handler-cache.js +11 -7
  69. package/dist/server/app-route-handler-dispatch.js +10 -3
  70. package/dist/server/app-route-handler-execution.d.ts +9 -1
  71. package/dist/server/app-route-handler-execution.js +28 -13
  72. package/dist/server/app-route-handler-policy.js +2 -2
  73. package/dist/server/app-route-handler-response.d.ts +1 -1
  74. package/dist/server/app-route-handler-response.js +14 -5
  75. package/dist/server/app-router-entry.d.ts +2 -1
  76. package/dist/server/app-router-entry.js +5 -9
  77. package/dist/server/app-rsc-errors.d.ts +0 -1
  78. package/dist/server/app-rsc-errors.js +2 -6
  79. package/dist/server/app-rsc-handler.d.ts +1 -0
  80. package/dist/server/app-rsc-handler.js +18 -7
  81. package/dist/server/app-rsc-request-normalization.d.ts +1 -1
  82. package/dist/server/app-rsc-request-normalization.js +3 -2
  83. package/dist/server/app-rsc-response-finalizer.js +4 -1
  84. package/dist/server/app-rsc-route-matching.js +1 -1
  85. package/dist/server/app-server-action-execution.d.ts +2 -1
  86. package/dist/server/app-ssr-entry.js +3 -3
  87. package/dist/server/app-ssr-search-params-gate.js +1 -1
  88. package/dist/server/app-ssr-stream.js +1 -1
  89. package/dist/server/cache-control.js +3 -11
  90. package/dist/server/cacheability-request.js +49 -49
  91. package/dist/server/config-headers.js +1 -1
  92. package/dist/server/dev-server.js +11 -3
  93. package/dist/server/headers.d.ts +2 -0
  94. package/dist/server/headers.js +5 -1
  95. package/dist/server/http-error-responses.js +1 -0
  96. package/dist/server/isr-cache.d.ts +2 -0
  97. package/dist/server/isr-cache.js +6 -1
  98. package/dist/server/metadata-route-response.js +94 -37
  99. package/dist/server/pages-api-route.js +8 -2
  100. package/dist/server/pages-asset-tags.d.ts +1 -0
  101. package/dist/server/pages-asset-tags.js +73 -22
  102. package/dist/server/pages-client-assets.d.ts +2 -0
  103. package/dist/server/pages-page-data.d.ts +6 -1
  104. package/dist/server/pages-page-data.js +71 -65
  105. package/dist/server/pages-page-handler.js +29 -11
  106. package/dist/server/pages-page-response.js +3 -1
  107. package/dist/server/pages-request-pipeline.d.ts +2 -1
  108. package/dist/server/pages-request-pipeline.js +20 -5
  109. package/dist/server/pages-request-stage-entry.js +6 -4
  110. package/dist/server/prerender-manifest.d.ts +6 -0
  111. package/dist/server/prerender-manifest.js +1 -1
  112. package/dist/server/prod-server.js +3 -13
  113. package/dist/server/response-stage-cacheability.js +1 -1
  114. package/dist/server/worker-revalidation-context.d.ts +2 -1
  115. package/dist/server/worker-revalidation-context.js +1 -0
  116. package/dist/server/workers-tracing.d.ts +17 -0
  117. package/dist/server/workers-tracing.js +48 -8
  118. package/dist/shims/cache-request-state.d.ts +3 -14
  119. package/dist/shims/cache-request-state.js +2 -13
  120. package/dist/shims/cache-runtime.js +4 -2
  121. package/dist/shims/cache.d.ts +2 -1
  122. package/dist/shims/cache.js +4 -2
  123. package/dist/shims/cacheability-classification.d.ts +1 -1
  124. package/dist/shims/cacheability-classification.js +2 -2
  125. package/dist/shims/cdn-cache.d.ts +10 -1
  126. package/dist/shims/cdn-cache.js +16 -9
  127. package/dist/shims/error.d.ts +4 -18
  128. package/dist/shims/error.js +17 -10
  129. package/dist/shims/error.react-server.d.ts +4 -6
  130. package/dist/shims/error.react-server.js +4 -1
  131. package/dist/shims/fetch-cache.js +4 -3
  132. package/dist/shims/headers.d.ts +6 -69
  133. package/dist/shims/headers.js +37 -181
  134. package/dist/shims/internal/cache-context-accessor.d.ts +16 -0
  135. package/dist/shims/internal/cache-context-accessor.js +15 -0
  136. package/dist/shims/internal/headers-state.d.ts +74 -0
  137. package/dist/shims/internal/headers-state.js +157 -0
  138. package/dist/shims/navigation.js +2 -2
  139. package/dist/shims/request-state-types.d.ts +2 -1
  140. package/dist/shims/root-params.js +1 -1
  141. package/dist/shims/server.js +52 -2
  142. package/dist/shims/unified-request-context.d.ts +1 -1
  143. package/dist/utils/encode-cache-tag.d.ts +1 -0
  144. package/dist/utils/encode-cache-tag.js +11 -1
  145. package/dist/utils/vite-cli-invocation.js +8 -1
  146. package/package.json +6 -2
  147. package/dist/server/metadata-route-cache-policy.d.ts +0 -5
  148. package/dist/server/metadata-route-cache-policy.js +0 -6
@@ -348,13 +348,17 @@ async function captureCacheabilityAdmissionBody(body, deadlineAt, limit = CACHEA
348
348
  throw error;
349
349
  }
350
350
  }
351
- function responseWithCachePolicy(response, body, outcome) {
351
+ function responseWithCachePolicy(response, body, outcome, browserCacheControl) {
352
352
  const headers = new Headers(response.headers);
353
353
  if (typeof body === "string") headers.delete("Content-Length");
354
354
  applyCdnResponseHeaders(headers, outcome?.cacheable === true && outcome.cacheControl ? {
355
355
  cacheControl: outcome.cacheControl,
356
- tags: outcome.tags
357
- } : { cacheControl: NO_STORE_CACHE_CONTROL });
356
+ tags: outcome.tags,
357
+ browserCacheControl
358
+ } : {
359
+ cacheControl: NO_STORE_CACHE_CONTROL,
360
+ browserCacheControl
361
+ });
358
362
  return new Response(body, {
359
363
  headers,
360
364
  status: response.status,
@@ -367,21 +371,15 @@ async function stripSharedHtmlClientTraceMetadata(body, marker) {
367
371
  const { stripClientTraceMetadataBlock } = await import("./client-trace-metadata.js");
368
372
  return stripClientTraceMetadataBlock(html, marker);
369
373
  }
370
- function inferFinalAppPageCacheability(response, state) {
371
- if (!state.explicitConfigCachePolicy && !state.frameworkResponseCachePolicy) return null;
372
- const cacheControl = readCdnResponseCacheControl(response.headers);
373
- if (cacheControl === null || !state.explicitConfigCachePolicy && cacheControl === readCdnResponseCacheControl(state.frameworkResponseCachePolicy)) return null;
374
- if (isNonCacheableCacheControl(cacheControl)) return { cacheable: false };
375
- return {
376
- cacheable: true,
377
- cacheControl,
378
- ...state.cdnCacheTags ? { tags: state.cdnCacheTags } : {}
379
- };
380
- }
381
- function inferPagesPageCacheability(response, state) {
374
+ /** Only an explicitly authored provider policy may override framework storage. */
375
+ function explicitProviderOutcome(response, state) {
376
+ const name = readCdnResponsePolicyHeaderName(response.headers);
377
+ if (!name || name === "cache-control") return null;
378
+ const value = response.headers.get(name);
379
+ if (!(state.explicitResponseCachePolicy || state.configCdnCachePolicy?.get(name) === value || state.frameworkResponseCachePolicy && state.frameworkResponseCachePolicy.get(name) !== value) || !value) return null;
382
380
  const cacheControl = readCdnResponseCacheControl(response.headers);
383
- if (!cacheControl || isNonCacheableCacheControl(cacheControl)) return { cacheable: false };
384
- return {
381
+ if (!cacheControl) return null;
382
+ return isNonCacheableCacheControl(cacheControl) ? { cacheable: false } : {
385
383
  cacheable: true,
386
384
  cacheControl,
387
385
  ...state.cdnCacheTags ? { tags: state.cdnCacheTags } : {}
@@ -397,18 +395,17 @@ function completedRouteOutcome(response, state, rendererOutcome = state.outcome
397
395
  cacheable: false,
398
396
  reason: varyRejectionReason
399
397
  };
400
- if (state.route?.kind === "app-route") {
401
- if (response.headers.has("set-cookie")) return {
402
- cacheable: false,
403
- reason: "response sets a cookie"
404
- };
405
- return inferPagesPageCacheability(response, state);
406
- }
407
- if (state.route?.kind === "app-page") return inferFinalAppPageCacheability(response, state) ?? rendererOutcome;
408
- if (state.route?.kind !== "pages-page") return rendererOutcome;
409
- if (response.headers.has("set-cookie") || hasExplicitNonCacheableResponsePolicy(response.headers)) return { cacheable: false };
410
- const responseOutcome = inferPagesPageCacheability(response, state);
411
- return responseOutcome.cacheable ? responseOutcome : rendererOutcome ?? responseOutcome;
398
+ if (response.headers.has("set-cookie")) return {
399
+ cacheable: false,
400
+ reason: "response sets a cookie"
401
+ };
402
+ if (rendererOutcome?.dynamicUsage) return rendererOutcome;
403
+ const outcome = explicitProviderOutcome(response, state) ?? rendererOutcome;
404
+ const tags = outcome?.tags ?? rendererOutcome?.tags ?? state.cdnCacheTags;
405
+ return outcome && tags && outcome.tags !== tags ? {
406
+ ...outcome,
407
+ tags
408
+ } : outcome;
412
409
  }
413
410
  function staticToDynamicResponse(route) {
414
411
  const headers = new Headers({ "Content-Type": "text/plain; charset=utf-8" });
@@ -427,30 +424,32 @@ function cacheabilityEvaluationFailureResponse(pattern) {
427
424
  });
428
425
  }
429
426
  function hasStrictFinalResponseVeto(response, state) {
430
- if (state.finalResponseVetoReason || response.headers.has("set-cookie")) return true;
431
- return hasExplicitNonCacheableResponsePolicy(response.headers, state.frameworkResponseCachePolicy);
427
+ const providerHeaders = new Headers(response.headers);
428
+ providerHeaders.delete("Cache-Control");
429
+ return Boolean(state.finalResponseVetoReason || response.headers.has("set-cookie") || hasExplicitNonCacheableResponsePolicy(providerHeaders, state.frameworkResponseCachePolicy));
432
430
  }
433
431
  async function finalizeWorkerCacheabilityAdmission(response, state) {
434
432
  if (state.preserveResponseCachePolicy) return response;
433
+ const browserCacheControl = state.explicitResponseCachePolicy || state.configCdnCachePolicy?.has("cache-control") || state.route?.kind === "pages-api" || state.route?.kind === "pages-page" && state.outcome?.cacheable !== true ? response.headers.get("Cache-Control") ?? void 0 : void 0;
435
434
  const admission = state.admission;
436
435
  if (state.route?.kind === "app-route" || state.route?.kind === "pages-api") {
437
436
  let manifestRoute = null;
438
- const responseOutcome = inferPagesPageCacheability(response, state);
437
+ const responseOutcome = completedRouteOutcome(response, state);
439
438
  const representation = admission?.representation ? resolveCacheabilityRepresentation(admission.representation, state.route.kind) : null;
440
- const hasExplicitRuntimePolicy = state.explicitResponseCachePolicy === true || state.explicitConfigCachePolicy === true || state.route.kind === "pages-api" && responseOutcome.cacheable;
441
- if (!admission || admission.policy === "deny" || !representation || !admission.requestKey) return responseWithCachePolicy(response, response.body, null);
439
+ const hasExplicitRuntimePolicy = responseOutcome?.cacheable === true;
440
+ if (!admission || admission.policy === "deny" || !representation || !admission.requestKey) return responseWithCachePolicy(response, response.body, null, browserCacheControl);
442
441
  if (admission.policy === "manifest" && state.route.kind === "app-route") {
443
442
  const manifest = admission.manifest;
444
443
  manifestRoute = findCacheabilityManifestRoute(manifest, state.route.kind, state.route.pattern);
445
444
  }
446
445
  const isManifestAuthorized = manifestRoute !== null && admission.routePathname !== void 0 && cacheabilityManifestRouteState(manifestRoute, admission.routePathname, representation) !== null;
447
446
  const canUseBoundedRuntimeAdmission = hasExplicitRuntimePolicy && (admission.policy === "runtime" || admission.policy === "manifest" && !isManifestAuthorized);
448
- if (!isManifestAuthorized && !canUseBoundedRuntimeAdmission || response.status >= 500 || state.credentialedRequest || state.forcedDynamicReason || hasStrictFinalResponseVeto(response, state) || cacheabilityVaryRejectionReason(response.headers, state) !== null) return responseWithCachePolicy(response, response.body, null);
447
+ if (!isManifestAuthorized && !canUseBoundedRuntimeAdmission || response.status >= 500 || state.credentialedRequest || state.forcedDynamicReason || hasStrictFinalResponseVeto(response, state) || cacheabilityVaryRejectionReason(response.headers, state) !== null) return responseWithCachePolicy(response, response.body, null, browserCacheControl);
449
448
  const outcome = responseOutcome;
450
- if (!outcome.cacheable || !outcome.cacheControl) return responseWithCachePolicy(response, response.body, null);
451
449
  if (state.completedResponseBody) {
450
+ if (!outcome?.cacheable || !outcome.cacheControl) return responseWithCachePolicy(response, response.body, null, browserCacheControl);
452
451
  if (!state.applyCompletedResponsePolicy) return response;
453
- return responseWithCachePolicy(response, response.body, outcome);
452
+ return responseWithCachePolicy(response, response.body, outcome, browserCacheControl);
454
453
  }
455
454
  let captured;
456
455
  try {
@@ -458,35 +457,36 @@ async function finalizeWorkerCacheabilityAdmission(response, state) {
458
457
  } catch {
459
458
  return cacheabilityEvaluationFailureResponse(state.route.pattern);
460
459
  }
461
- if (captured.kind === "fallback") return responseWithCachePolicy(response, captured.body, null);
462
- return responseWithCachePolicy(response, captured.body, outcome);
460
+ if (captured.kind === "fallback") return responseWithCachePolicy(response, captured.body, null, browserCacheControl);
461
+ return responseWithCachePolicy(response, captured.body, outcome, browserCacheControl);
463
462
  }
464
- if (!admission || admission.policy === "deny" || !admission.representation || !admission.requestKey || !state.route || response.status >= 500) return responseWithCachePolicy(response, response.body, null);
463
+ if (!admission || admission.policy === "deny" || !admission.representation || !admission.requestKey || !state.route || response.status >= 500) return responseWithCachePolicy(response, response.body, null, browserCacheControl);
465
464
  const pageRoute = {
466
465
  kind: state.route.kind,
467
466
  pattern: state.route.pattern
468
467
  };
469
468
  const requestRepresentation = admission.representation;
470
469
  const representation = resolveCacheabilityRepresentation(requestRepresentation, pageRoute.kind);
471
- if (!cacheabilityRepresentationMatchesPageRoute(pageRoute.kind, representation)) return responseWithCachePolicy(response, response.body, null);
470
+ if (!cacheabilityRepresentationMatchesPageRoute(pageRoute.kind, representation)) return responseWithCachePolicy(response, response.body, null, browserCacheControl);
472
471
  let manifestRoute = null;
473
472
  let manifestRouteState = null;
474
473
  if (admission.policy === "manifest") {
475
474
  const manifest = admission.manifest;
476
475
  manifestRoute = findCacheabilityManifestRoute(manifest, pageRoute.kind, pageRoute.pattern);
477
476
  manifestRouteState = admission.routePathname ? cacheabilityManifestPageState(manifest, pageRoute, requestRepresentation, admission.routePathname) : null;
478
- if (!manifestRoute || !manifestRouteState) return responseWithCachePolicy(response, response.body, null);
477
+ if (!manifestRoute || !manifestRouteState) return responseWithCachePolicy(response, response.body, null, browserCacheControl);
479
478
  }
480
- if (state.forcedDynamicReason) return responseWithCachePolicy(response, response.body, null);
481
- if (hasStrictFinalResponseVeto(response, state)) return responseWithCachePolicy(response, response.body, null);
482
- if (cacheabilityVaryRejectionReason(response.headers, state) !== null) return responseWithCachePolicy(response, response.body, null);
479
+ if (state.forcedDynamicReason) return responseWithCachePolicy(response, response.body, null, browserCacheControl);
480
+ if (hasStrictFinalResponseVeto(response, state)) return responseWithCachePolicy(response, response.body, null, browserCacheControl);
481
+ if (cacheabilityVaryRejectionReason(response.headers, state) !== null) return responseWithCachePolicy(response, response.body, null, browserCacheControl);
482
+ if (state.route.kind === "pages-page" && !state.completion && completedRouteOutcome(response, state)?.cacheable !== true) return responseWithCachePolicy(response, response.body, null, browserCacheControl);
483
483
  let captured;
484
484
  try {
485
485
  captured = await captureCacheabilityAdmissionBody(response.body, state.captureDeadlineAt, CACHEABILITY_ADMISSION_RESPONSE_BODY_LIMIT, state.captureBudget ?? isolateCaptureBudget);
486
486
  } catch {
487
487
  return cacheabilityEvaluationFailureResponse(state.route.pattern);
488
488
  }
489
- if (captured.kind === "fallback") return responseWithCachePolicy(response, captured.body, null);
489
+ if (captured.kind === "fallback") return responseWithCachePolicy(response, captured.body, null, browserCacheControl);
490
490
  const rendererOutcome = state.completion ? await state.completion : state.outcome ?? null;
491
491
  const outcome = completedRouteOutcome(response, state, rendererOutcome);
492
492
  if (outcome?.cacheable !== true || !outcome.cacheControl) {
@@ -494,12 +494,12 @@ async function finalizeWorkerCacheabilityAdmission(response, state) {
494
494
  await captured.body?.cancel().catch(() => {});
495
495
  return staticToDynamicResponse(manifestRoute);
496
496
  }
497
- return responseWithCachePolicy(response, captured.body, null);
497
+ return responseWithCachePolicy(response, captured.body, null, browserCacheControl);
498
498
  }
499
499
  const effectivePolicyHeader = readCdnResponsePolicyHeaderName(response.headers);
500
500
  const replacesRendererPolicy = outcome !== rendererOutcome && effectivePolicyHeader !== null && state.configCdnCachePolicy?.get(effectivePolicyHeader) === response.headers.get(effectivePolicyHeader);
501
- if (state.route.kind === "app-page" && !replacesRendererPolicy && rendererOutcome?.searchParamsUnread !== true) return responseWithCachePolicy(response, captured.body, null);
502
- return responseWithCachePolicy(response, representation === "html" && state.clientTraceMetadataMarker ? await stripSharedHtmlClientTraceMetadata(captured.body, state.clientTraceMetadataMarker) : captured.body, outcome);
501
+ if (state.route.kind === "app-page" && !replacesRendererPolicy && rendererOutcome?.searchParamsUnread !== true) return responseWithCachePolicy(response, captured.body, null, browserCacheControl);
502
+ return responseWithCachePolicy(response, representation === "html" && state.clientTraceMetadataMarker ? await stripSharedHtmlClientTraceMetadata(captured.body, state.clientTraceMetadataMarker) : captured.body, outcome, browserCacheControl);
503
503
  }
504
504
  async function finalizeWorkerCacheabilityResponse(response, ctx) {
505
505
  const state = readState(ctx);
@@ -28,7 +28,7 @@ function markExplicitConfigResponseVeto(matched) {
28
28
  continue;
29
29
  }
30
30
  if (isCdnResponsePolicyHeader(name)) markRouteCacheabilityConfigCdnCachePolicy(name, header.value);
31
- if (isCdnResponsePolicyHeader(name) && isNonCacheableCdnResponsePolicy(name, header.value)) markRouteCacheabilityFinalResponseUncacheable(`next.config headers set a non-cacheable ${header.key} policy`);
31
+ if (name !== "cache-control" && isCdnResponsePolicyHeader(name) && isNonCacheableCdnResponsePolicy(name, header.value)) markRouteCacheabilityFinalResponseUncacheable(`next.config headers set a non-cacheable ${header.key} policy`);
32
32
  }
33
33
  }
34
34
  function retainLastSingularConfigValues(matched) {
@@ -504,13 +504,17 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
504
504
  if (typeof pageModule.getStaticPaths === "function" && route.isDynamic) {
505
505
  const pathsResult = await pageModule.getStaticPaths({
506
506
  locales: i18nConfig?.locales ?? [],
507
- defaultLocale: currentDefaultLocale ?? ""
507
+ defaultLocale: i18nConfig?.defaultLocale ?? ""
508
508
  });
509
509
  const fallback = pathsResult?.fallback ?? false;
510
510
  const paths = pathsResult?.paths ?? [];
511
511
  const routePattern = patternToNextFormat(route.pattern);
512
512
  const routeParams = getPagesRouteParams(routePattern);
513
- const isValidPath = paths.some((pathEntry) => matchesPagesStaticPath(pathEntry, params, routeParams, url));
513
+ const isValidPath = paths.some((pathEntry) => matchesPagesStaticPath(pathEntry, params, routeParams, localeStrippedUrl, {
514
+ locale,
515
+ locales: i18nConfig?.locales,
516
+ defaultLocale: i18nConfig?.defaultLocale
517
+ }));
514
518
  if (fallback === false && !isValidPath && requestPreviewData === false) {
515
519
  if (isDataReq) {
516
520
  const deploymentId = process.env.__VINEXT_DEPLOYMENT_ID || process.env.NEXT_DEPLOYMENT_ID;
@@ -745,6 +749,7 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
745
749
  }
746
750
  if ((statusCode ?? 200) === 200) dataHeaders["x-nextjs-matched-path"] = `${locale ? `/${locale}` : ""}${patternToNextFormat(route.pattern)}`;
747
751
  if (gsspExtraHeaders) for (const [k, v] of Object.entries(gsspExtraHeaders)) dataHeaders[k] = v;
752
+ dataHeaders["Cache-Control"] = DEV_PAGES_CACHE_CONTROL;
748
753
  applyDevPagesPreviewHeaders(dataHeaders, requestPreview);
749
754
  const dataRoutePattern = patternToNextFormat(route.pattern);
750
755
  if (dataRoutePattern !== "/_error" && dataRoutePattern !== "/500") {
@@ -836,7 +841,10 @@ function createSSRHandler(server, runner, routes, pagesDir, i18nConfig, fileMatc
836
841
  domainLocales,
837
842
  ...serializedPagesNextData
838
843
  })}<\/script>`}\n ${createInlineScriptTag(`window.__VINEXT_PAGE_PATTERNS__=${safeJsonStringify(pagePatterns)}`, scriptNonce)}\n ${hydrationScript}`;
839
- const extraHeaders = { ...gsspExtraHeaders };
844
+ const extraHeaders = {
845
+ ...gsspExtraHeaders,
846
+ "Cache-Control": DEV_PAGES_CACHE_CONTROL
847
+ };
840
848
  if (typeof pageModule.getStaticProps === "function") {
841
849
  extraHeaders["Cache-Control"] = DEV_PAGES_CACHE_CONTROL;
842
850
  extraHeaders[NEXTJS_CACHE_HEADER] = isFallbackRender ? "MISS" : isOnDemandRevalidate ? "REVALIDATED" : "HIT";
@@ -62,6 +62,8 @@ export declare const VINEXT_RSC_COMPLETION_METADATA_HEADER = "X-Vinext-Rsc-Compl
62
62
  export declare const VINEXT_RENDERED_PATH_AND_SEARCH_HEADER = "X-Vinext-Rendered-Path-And-Search";
63
63
  /** Prerender-only JSON side channel carrying request cacheLife metadata. */
64
64
  export declare const VINEXT_PRERENDER_CACHE_LIFE_HEADER = "x-vinext-prerender-cache-life";
65
+ /** Prerender-only Pages marker: "0" confirms the page render used the requested URL. */
66
+ export declare const VINEXT_PRERENDER_REWRITTEN_HEADER = "x-vinext-prerender-rewritten";
65
67
  /** Marks a local prerender-server 500 that originated from a thrown render error. */
66
68
  export declare const VINEXT_PRERENDER_RENDER_ERROR_HEADER = "x-vinext-prerender-render-error";
67
69
  /** Internal marker persisted only inside metadata-route APP_ROUTE cache values. */
@@ -62,6 +62,8 @@ const VINEXT_RSC_COMPLETION_METADATA_HEADER = "X-Vinext-Rsc-Completion-Metadata"
62
62
  const VINEXT_RENDERED_PATH_AND_SEARCH_HEADER = "X-Vinext-Rendered-Path-And-Search";
63
63
  /** Prerender-only JSON side channel carrying request cacheLife metadata. */
64
64
  const VINEXT_PRERENDER_CACHE_LIFE_HEADER = "x-vinext-prerender-cache-life";
65
+ /** Prerender-only Pages marker: "0" confirms the page render used the requested URL. */
66
+ const VINEXT_PRERENDER_REWRITTEN_HEADER = "x-vinext-prerender-rewritten";
65
67
  /** Marks a local prerender-server 500 that originated from a thrown render error. */
66
68
  const VINEXT_PRERENDER_RENDER_ERROR_HEADER = "x-vinext-prerender-render-error";
67
69
  /** Internal marker persisted only inside metadata-route APP_ROUTE cache values. */
@@ -161,9 +163,11 @@ const VINEXT_INTERNAL_HEADERS = [
161
163
  VINEXT_CACHEABILITY_PROBE_HEADER.toLowerCase(),
162
164
  VINEXT_CACHEABILITY_PROBE_ROUTE_HEADER.toLowerCase(),
163
165
  VINEXT_EXPECTED_WORKER_VERSION_HEADER.toLowerCase(),
166
+ VINEXT_MW_CTX_HEADER,
164
167
  VINEXT_PRERENDER_ROUTE_PARAMS_HEADER,
165
168
  VINEXT_PRERENDER_SPECULATIVE_HEADER,
166
169
  VINEXT_PRERENDER_CACHE_LIFE_HEADER,
170
+ VINEXT_PRERENDER_REWRITTEN_HEADER,
167
171
  VINEXT_REVALIDATE_HOST_HEADER,
168
172
  VINEXT_REVALIDATED_CACHE_TAG_HEADER,
169
173
  VINEXT_TRACE_ERROR_HEADER.toLowerCase(),
@@ -171,4 +175,4 @@ const VINEXT_INTERNAL_HEADERS = [
171
175
  VINEXT_TRACE_ROUTE_HEADER.toLowerCase()
172
176
  ];
173
177
  //#endregion
174
- export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_CACHE_TAGS_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STALE_TIME_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHEABILITY_PROBE_HEADER, VINEXT_CACHEABILITY_PROBE_QUERY_PARAM, VINEXT_CACHEABILITY_PROBE_ROUTE_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_EXPECTED_WORKER_VERSION_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERCEPTION_ID_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_METADATA_ROUTE_CACHE_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_METADATA_ROUTES_PATH, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_READINESS_HEADER, VINEXT_PRERENDER_READINESS_PATH, VINEXT_PRERENDER_RENDER_ERROR_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATED_CACHE_TAG_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_REVALIDATE_HOST_HEADER, VINEXT_RSC_COMPLETION_METADATA_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_STATE_FINGERPRINT_HEADER, VINEXT_RSC_VARY_HEADER, VINEXT_STALE_TIME_PENDING_HEADER, VINEXT_TIMING_HEADER, VINEXT_TRACE_BUFFERED_BODY_HEADER, VINEXT_TRACE_ERROR_HEADER, VINEXT_TRACE_ROUTE_HEADER, isVinextRscVaryField };
178
+ export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_CACHE_TAGS_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STALE_TIME_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHEABILITY_PROBE_HEADER, VINEXT_CACHEABILITY_PROBE_QUERY_PARAM, VINEXT_CACHEABILITY_PROBE_ROUTE_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_EXPECTED_WORKER_VERSION_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERCEPTION_ID_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_METADATA_ROUTE_CACHE_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_METADATA_ROUTES_PATH, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_READINESS_HEADER, VINEXT_PRERENDER_READINESS_PATH, VINEXT_PRERENDER_RENDER_ERROR_HEADER, VINEXT_PRERENDER_REWRITTEN_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATED_CACHE_TAG_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_REVALIDATE_HOST_HEADER, VINEXT_RSC_COMPLETION_METADATA_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_STATE_FINGERPRINT_HEADER, VINEXT_RSC_VARY_HEADER, VINEXT_STALE_TIME_PENDING_HEADER, VINEXT_TIMING_HEADER, VINEXT_TRACE_BUFFERED_BODY_HEADER, VINEXT_TRACE_ERROR_HEADER, VINEXT_TRACE_ROUTE_HEADER, isVinextRscVaryField };
@@ -77,6 +77,7 @@ function notFoundStaticAssetResponse(headers) {
77
77
  responseHeaders.delete("content-length");
78
78
  responseHeaders.delete("transfer-encoding");
79
79
  responseHeaders.set("Content-Type", "text/plain; charset=utf-8");
80
+ responseHeaders.set("Cache-Control", "private, no-cache, no-store, max-age=0, must-revalidate");
80
81
  return new Response("Not Found", {
81
82
  status: 404,
82
83
  headers: responseHeaders
@@ -102,6 +102,8 @@ export declare function buildAppPageCacheValue(html: string, rscData?: ArrayBuff
102
102
  * Long pathnames are hashed to stay within KV key-length limits (512 bytes).
103
103
  */
104
104
  export declare function isrCacheKey(router: string, pathname: string, buildId?: string): string;
105
+ /** Build and request time share the same locale-stripped Pages pathname identity. */
106
+ export declare function pagesIsrCacheKey(pathname: string, buildId?: string, i18nCacheVariant?: string | null): string;
105
107
  /**
106
108
  * Compute an App Router ISR key for one cache artifact.
107
109
  *
@@ -1,4 +1,5 @@
1
1
  import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER } from "../utils/protocol-headers.js";
2
+ import { normalizePathnameForRouteMatch } from "../routing/utils.js";
2
3
  import { getRequestExecutionContext } from "../shims/request-context.js";
3
4
  import { reportRequestError } from "./instrumentation.js";
4
5
  import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
@@ -164,6 +165,10 @@ function buildCacheKey(prefix, pathname, suffix) {
164
165
  function isrCacheKey(router, pathname, buildId) {
165
166
  return buildCacheKey(buildId ? `${router}:${buildId}` : router, pathname);
166
167
  }
168
+ /** Build and request time share the same locale-stripped Pages pathname identity. */
169
+ function pagesIsrCacheKey(pathname, buildId, i18nCacheVariant) {
170
+ return isrCacheKey("pages:v2", normalizeCachePathname(normalizePathnameForRouteMatch(pathname)) + (i18nCacheVariant ? `::i18n=${encodeURIComponent(i18nCacheVariant)}` : ""), buildId);
171
+ }
167
172
  /**
168
173
  * Compute an App Router ISR key for one cache artifact.
169
174
  *
@@ -206,4 +211,4 @@ function appIsrRouteKey(pathname) {
206
211
  return appIsrCacheKey(pathname, pathname !== "/" && pathname.endsWith("/") ? "route:trailing-slash" : "route");
207
212
  }
208
213
  //#endregion
209
- export { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrCacheKey, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, coalesceOnDemandRevalidation, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, isrCacheControl, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, resolveRouteExpireSeconds, triggerBackgroundRegeneration };
214
+ export { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, appIsrCacheKey, appIsrHtmlKey, appIsrRouteKey, appIsrRscKey, buildAppPageCacheValue, buildPagesCacheValue, coalesceOnDemandRevalidation, getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret, isrCacheControl, isrCacheKey, isrGet, isrSet, isrSetPrerenderedAppPage, normalizeMountedSlotsHeader, pagesIsrCacheKey, resolveRouteExpireSeconds, triggerBackgroundRegeneration };
@@ -4,23 +4,28 @@ import { decodeMatchedParams } from "../routing/utils.js";
4
4
  import { getRequestExecutionContext } from "../shims/request-context.js";
5
5
  import { VINEXT_METADATA_ROUTE_CACHE_HEADER } from "./headers.js";
6
6
  import { notFoundResponse } from "./http-error-responses.js";
7
- import { beginRouteCacheability, markRouteCacheabilityExplicitResponsePolicy } from "../shims/cacheability-classification.js";
8
- import { hasCdnResponsePolicy, hasExplicitNonCacheableResponsePolicy } from "./cache-control.js";
7
+ import { beginRouteCacheability, markRouteCacheabilityExplicitResponsePolicy, markRouteCacheabilityResponseBodyComplete, recordRouteCacheability } from "../shims/cacheability-classification.js";
8
+ import { NEVER_CACHE_CONTROL, applyCdnResponseHeaders, hasCdnResponsePolicy } from "./cache-control.js";
9
+ import { buildAppRouteMissIsrCacheControl } from "./isr-decision.js";
9
10
  import { isrCacheControl } from "./isr-cache.js";
10
11
  import { resolveClientStaleTimeSeconds } from "../utils/cache-control-metadata.js";
11
- import { _consumeRequestScopedCacheLife, cacheLifeProfiles } from "../shims/cache-request-state.js";
12
+ import { isRenderDynamicLatched } from "../shims/internal/headers-state.js";
13
+ import { getActiveDraftModeState, getHeadersContext, hasDraftModeCookieHeader, replaceHeadersContext } from "../shims/headers.js";
14
+ import { _consumeRequestScopedCacheLife } from "../shims/cache-request-state.js";
12
15
  import { ensureFetchPatch, getCollectedFetchTags, setCurrentFetchSoftTags } from "../shims/fetch-cache.js";
13
16
  import { isValidMetadataImageId, manifestToJson, matchMetadataRoutePattern, robotsToText, sitemapToXml } from "./metadata-routes.js";
14
17
  import { applyPrerenderCacheLifeHeader, applyPrerenderCacheTagsHeader } from "./prerender-cache-life-header.js";
15
18
  import { canonicalizeAppPageParams } from "./app-page-segment-state.js";
16
19
  import { buildPageCacheTags } from "./implicit-tags.js";
17
20
  import { buildAppRouteCacheValue, buildRouteHandlerCachedResponse } from "./app-route-handler-response.js";
18
- import { isMetadataResponseCacheable } from "./metadata-route-cache-policy.js";
21
+ import { createStaticGenerationHeadersContext } from "./app-static-generation.js";
22
+ import { completeAppRouteHandlerResponse } from "./app-route-handler-execution.js";
19
23
  //#region src/server/metadata-route-response.ts
20
24
  function isOuterMetadataCacheEnabled() {
21
25
  return process.env.NODE_ENV !== "development";
22
26
  }
23
27
  const routeFunctionCache = /* @__PURE__ */ new WeakMap();
28
+ const userMetadataResponses = /* @__PURE__ */ new WeakSet();
24
29
  const USE_CACHE_FUNCTION_SYMBOL = Symbol.for("vinext.useCacheFunction");
25
30
  const CACHE_HEADERS = {
26
31
  noCache: "no-cache, no-store",
@@ -53,17 +58,19 @@ function metadataRouteCacheHeader(route) {
53
58
  if (route.isDynamic && isImageMetadataRoute(route) && process.env.NODE_ENV === "development") return CACHE_HEADERS.noCache;
54
59
  return CACHE_HEADERS.revalidate;
55
60
  }
56
- function withMetadataRouteCacheHeader(response, route) {
57
- if (hasCdnResponsePolicy(response.headers) && !hasExplicitNonCacheableResponsePolicy(response.headers)) markRouteCacheabilityExplicitResponsePolicy();
58
- const headers = new Headers(response.headers);
59
- if (!headers.has("Cache-Control")) headers.set("Cache-Control", metadataRouteCacheHeader(route));
60
- return new Response(response.body, {
61
- headers,
62
- status: response.status,
63
- statusText: response.statusText
64
- });
65
- }
66
61
  function beginMetadataRouteCacheability(route) {
62
+ const dynamic = route.module?.dynamic;
63
+ if (dynamic === "force-static" || dynamic === "error") {
64
+ const current = getHeadersContext();
65
+ const staticContext = createStaticGenerationHeadersContext({
66
+ dynamicConfig: dynamic,
67
+ routeKind: "route",
68
+ routePattern: route.servedUrl,
69
+ draftModeEnabled: getActiveDraftModeState() ?? void 0,
70
+ draftModeSecret: current?.draftModeSecret
71
+ });
72
+ replaceHeadersContext(staticContext);
73
+ }
67
74
  beginRouteCacheability("app-route", route.patternParts ? `/${route.patternParts.join("/")}` : route.servedUrl);
68
75
  }
69
76
  function getMetadataRouteFunctions(route) {
@@ -137,29 +144,68 @@ function markMetadataRouteCacheValue(value) {
137
144
  function buildMetadataRouteTags(route, cleanPathname, collectedTags) {
138
145
  return buildPageCacheTags(cleanPathname, collectedTags, route.routeSegments ?? [], "route");
139
146
  }
140
- function captureRenderedMetadataRoute(response) {
141
- const cacheLife = _consumeRequestScopedCacheLife();
147
+ async function captureRenderedMetadataRoute(response, route, cleanPathname) {
148
+ const isUserResponse = userMetadataResponses.has(response);
149
+ let completed = false;
150
+ if (isOuterMetadataCacheEnabled()) {
151
+ const result = await completeAppRouteHandlerResponse(response);
152
+ response = result.response;
153
+ completed = result.completed;
154
+ if (completed) markRouteCacheabilityResponseBodyComplete();
155
+ }
156
+ const browserCacheControl = response.headers.get("Cache-Control") ?? void 0;
157
+ const hasUserPolicy = isUserResponse && hasCdnResponsePolicy(response.headers);
158
+ if (hasUserPolicy) markRouteCacheabilityExplicitResponsePolicy();
159
+ const observedCacheLife = _consumeRequestScopedCacheLife();
142
160
  const collectedTags = getCollectedFetchTags();
161
+ const revalidate = typeof route.module?.revalidate === "number" ? route.module.revalidate : Infinity;
162
+ const frameworkRevalidate = Math.min(observedCacheLife?.revalidate ?? Infinity, revalidate);
163
+ const cacheLife = {
164
+ ...observedCacheLife,
165
+ revalidate: frameworkRevalidate
166
+ };
167
+ const cacheable = (response.status < 400 || response.status === 404) && completed && !isRenderDynamicLatched() && !getActiveDraftModeState() && !hasDraftModeCookieHeader() && route.module?.dynamic !== "force-dynamic" && frameworkRevalidate > 0 && !response.headers.has("set-cookie");
168
+ const cacheControl = cacheable ? buildAppRouteMissIsrCacheControl(frameworkRevalidate, cacheLife?.expire) : NEVER_CACHE_CONTROL;
169
+ if (isUserResponse && !hasUserPolicy) {
170
+ response = new Response(response.body, {
171
+ headers: new Headers(response.headers),
172
+ status: response.status,
173
+ statusText: response.statusText
174
+ });
175
+ applyCdnResponseHeaders(response.headers, { cacheControl: isOuterMetadataCacheEnabled() ? cacheControl : metadataRouteCacheHeader(route) });
176
+ }
177
+ recordRouteCacheability(cacheable ? {
178
+ cacheable: true,
179
+ cacheControl,
180
+ tags: buildMetadataRouteTags(route, cleanPathname, collectedTags)
181
+ } : {
182
+ cacheable: false,
183
+ dynamicUsage: true
184
+ });
143
185
  if (process.env.VINEXT_PRERENDER === "1") {
144
- applyPrerenderCacheLifeHeader(response.headers, cacheLife);
186
+ applyPrerenderCacheLifeHeader(response.headers, {
187
+ ...cacheLife,
188
+ revalidate: cacheable ? frameworkRevalidate : 0
189
+ });
145
190
  applyPrerenderCacheTagsHeader(response.headers, collectedTags);
146
191
  }
147
192
  return {
193
+ browserCacheControl,
148
194
  cacheLife,
195
+ cacheable,
149
196
  collectedTags,
150
197
  response
151
198
  };
152
199
  }
153
200
  async function writeRenderedMetadataRoute(options, route, routeKey, rendered, previousEntry) {
154
- if (!options.isrSet || !rendered.response.ok || !isMetadataResponseCacheable(rendered.response)) return;
201
+ if (!options.isrSet || !rendered.cacheable) return;
155
202
  const previousCacheControl = previousEntry?.value.cacheControl;
156
- const defaultCacheLife = cacheLifeProfiles.default;
157
- const revalidate = rendered.cacheLife?.revalidate ?? previousCacheControl?.revalidate ?? defaultCacheLife.revalidate ?? 900;
158
- const expire = rendered.cacheLife?.expire ?? previousCacheControl?.expire ?? defaultCacheLife.expire;
203
+ const revalidate = rendered.cacheLife?.revalidate ?? previousCacheControl?.revalidate ?? (typeof route.module?.revalidate === "number" ? route.module.revalidate : false);
204
+ const expire = rendered.cacheLife?.expire ?? previousCacheControl?.expire;
159
205
  const stale = resolveClientStaleTimeSeconds(rendered.cacheLife) ?? previousCacheControl?.stale;
160
- const value = markMetadataRouteCacheValue(await buildAppRouteCacheValue(rendered.response));
206
+ const value = markMetadataRouteCacheValue(await buildAppRouteCacheValue(rendered.response, rendered.browserCacheControl));
161
207
  await options.isrSet(routeKey, value, {
162
- cacheControl: isrCacheControl(revalidate, {
208
+ cacheControl: isrCacheControl(revalidate === Infinity ? false : revalidate, {
163
209
  ...typeof expire === "number" ? { expireSeconds: expire } : {},
164
210
  ...typeof stale === "number" ? { staleSeconds: stale } : {}
165
211
  }),
@@ -171,16 +217,21 @@ async function runMetadataRouteRegeneration(options, route, render, routeKey, pr
171
217
  try {
172
218
  await runWithRequestContext(requestContext, async () => {
173
219
  ensureFetchPatch();
220
+ beginMetadataRouteCacheability(route);
174
221
  setCurrentFetchSoftTags(buildMetadataRouteTags(route, options.cleanPathname, []));
175
222
  const rendered = await render();
176
- if (rendered) await writeRenderedMetadataRoute(options, route, routeKey, rendered, previousEntry);
223
+ if (rendered) try {
224
+ await writeRenderedMetadataRoute(options, route, routeKey, rendered, previousEntry);
225
+ } finally {
226
+ rendered.response.body?.cancel().catch(() => {});
227
+ }
177
228
  });
178
229
  } finally {
179
230
  await closeAfterResponse(requestContext);
180
231
  }
181
232
  }
182
- async function readMatchedPrerenderedMetadataRouteResponse(options, route, functions, render) {
183
- if (!isOuterMetadataCacheEnabled() || !options.isrGet || !options.isrRouteKey) return {
233
+ async function readMatchedPrerenderedMetadataRouteResponse(options, route, render) {
234
+ if (!isOuterMetadataCacheEnabled() || !options.isrGet || !options.isrRouteKey || getActiveDraftModeState() || hasDraftModeCookieHeader() || route.module?.dynamic === "force-dynamic" || route.module?.revalidate === 0) return {
184
235
  cachedEntry: null,
185
236
  response: null
186
237
  };
@@ -214,7 +265,7 @@ async function readMatchedPrerenderedMetadataRouteResponse(options, route, funct
214
265
  revalidateSeconds
215
266
  })
216
267
  };
217
- if (isUseCacheFunction(functions.defaultExport) && options.isrSet && options.scheduleBackgroundRegeneration) {
268
+ if (options.isrSet && options.scheduleBackgroundRegeneration) {
218
269
  const executionContext = getRequestExecutionContext();
219
270
  options.scheduleBackgroundRegeneration(routeKey, () => runMetadataRouteRegeneration(options, route, render, routeKey, cached, executionContext), {
220
271
  routerKind: "App Router",
@@ -330,7 +381,10 @@ async function handleGeneratedSitemap(route, cleanPathname, functions) {
330
381
  const matchedId = findGeneratedSitemapId(await functions.generateSitemaps({}), rawId);
331
382
  if (!matchedId) return notFoundResponse();
332
383
  const result = await functions.defaultExport({ id: makeThenableMetadataRouteId(matchedId) });
333
- if (result instanceof Response) return withMetadataRouteCacheHeader(result, route);
384
+ if (result instanceof Response) {
385
+ userMetadataResponses.add(result);
386
+ return result;
387
+ }
334
388
  if (!isSitemapEntries(result)) throw new TypeError("Metadata sitemap routes must return an array.");
335
389
  return markFullyBufferedBody(new Response(sitemapToXml(result), { headers: {
336
390
  "Content-Type": route.contentType,
@@ -367,7 +421,10 @@ async function callDynamicMetadataRoute(route, match, makeThenableParams, functi
367
421
  id: makeThenableMetadataRouteId(matchedImageId)
368
422
  });
369
423
  } else result = await functions.defaultExport({ params: paramsThenable });
370
- if (result instanceof Response) return withMetadataRouteCacheHeader(result, route);
424
+ if (result instanceof Response) {
425
+ userMetadataResponses.add(result);
426
+ return result;
427
+ }
371
428
  let body;
372
429
  if (route.type === "sitemap") {
373
430
  if (!isSitemapEntries(result)) throw new TypeError("Metadata sitemap routes must return an array.");
@@ -410,8 +467,8 @@ function isMetadataRouteRequestPath(metadataRoutes, cleanPathname) {
410
467
  }
411
468
  return false;
412
469
  }
413
- async function writeMetadataRouteMiss(options, route, functions, rendered, cachedEntry) {
414
- if (process.env.VINEXT_PRERENDER === "1" || !isOuterMetadataCacheEnabled() || !rendered.response.ok || !isMetadataResponseCacheable(rendered.response) || !isUseCacheFunction(functions.defaultExport) || !options.isrRouteKey || !options.isrSet) return;
470
+ async function writeMetadataRouteMiss(options, route, rendered, cachedEntry) {
471
+ if (process.env.VINEXT_PRERENDER === "1" || !isOuterMetadataCacheEnabled() || !rendered.cacheable || !options.isrRouteKey || !options.isrSet) return;
415
472
  try {
416
473
  await writeRenderedMetadataRoute(options, route, options.isrRouteKey(options.cleanPathname), {
417
474
  ...rendered,
@@ -433,13 +490,13 @@ async function handleMetadataRouteRequest(options) {
433
490
  const render = async () => {
434
491
  setCurrentFetchSoftTags(buildMetadataRouteTags(route, options.cleanPathname, []));
435
492
  const response = await handleGeneratedSitemap(route, options.cleanPathname, functions);
436
- return response ? captureRenderedMetadataRoute(response) : null;
493
+ return response ? captureRenderedMetadataRoute(response, route, options.cleanPathname) : null;
437
494
  };
438
- const cached = await readMatchedPrerenderedMetadataRouteResponse(options, route, functions, render);
495
+ const cached = await readMatchedPrerenderedMetadataRouteResponse(options, route, render);
439
496
  if (cached.response) return cached.response;
440
497
  const rendered = await render();
441
498
  if (rendered) {
442
- await writeMetadataRouteMiss(options, route, functions, rendered, cached.cachedEntry);
499
+ await writeMetadataRouteMiss(options, route, rendered, cached.cachedEntry);
443
500
  return rendered.response;
444
501
  }
445
502
  }
@@ -471,12 +528,12 @@ async function handleMetadataRouteRequest(options) {
471
528
  }
472
529
  const render = async () => {
473
530
  setCurrentFetchSoftTags(buildMetadataRouteTags(route, options.cleanPathname, []));
474
- return captureRenderedMetadataRoute(route.isDynamic ? await callDynamicMetadataRoute(route, match, options.makeThenableParams, functions) : serveStaticMetadataRoute(route));
531
+ return captureRenderedMetadataRoute(route.isDynamic ? await callDynamicMetadataRoute(route, match, options.makeThenableParams, functions) : serveStaticMetadataRoute(route), route, options.cleanPathname);
475
532
  };
476
- const cached = await readMatchedPrerenderedMetadataRouteResponse(options, route, functions, render);
533
+ const cached = await readMatchedPrerenderedMetadataRouteResponse(options, route, render);
477
534
  if (cached.response) return cached.response;
478
535
  const rendered = await render();
479
- await writeMetadataRouteMiss(options, route, functions, rendered, cached.cachedEntry);
536
+ await writeMetadataRouteMiss(options, route, rendered, cached.cachedEntry);
480
537
  return rendered.response;
481
538
  }
482
539
  return null;
@@ -3,6 +3,8 @@ import "./server-globals.js";
3
3
  import { NextRequest } from "../shims/server.js";
4
4
  import { internalServerErrorResponse } from "./http-error-responses.js";
5
5
  import { cloneRequestWithUrl } from "./request-pipeline.js";
6
+ import { markRouteCacheabilityExplicitResponsePolicy } from "../shims/cacheability-classification.js";
7
+ import { hasCdnResponsePolicy } from "./cache-control.js";
6
8
  import { mergeRouteParamsIntoQuery, parseQueryString, urlQueryToSearchParams } from "../utils/query.js";
7
9
  import { tracePagesApiHandler } from "./pages-execution-tracing.js";
8
10
  import { resolveBodyParserConfig } from "./pages-body-parser-config.js";
@@ -29,8 +31,12 @@ function isNodeApiRouteModule(module) {
29
31
  return typeof module.default === "function" && !isEdgeApiRuntime(resolveModuleRuntime(module));
30
32
  }
31
33
  async function handlePagesApiRoute(options) {
32
- if (options.ctx) return runWithExecutionContext(options.ctx, () => _handlePagesApiRoute(options));
33
- return _handlePagesApiRoute(options);
34
+ const run = async () => {
35
+ const response = await _handlePagesApiRoute(options);
36
+ if (hasCdnResponsePolicy(response.headers)) markRouteCacheabilityExplicitResponsePolicy();
37
+ return response;
38
+ };
39
+ return options.ctx ? runWithExecutionContext(options.ctx, run) : run();
34
40
  }
35
41
  async function _handlePagesApiRoute(options) {
36
42
  if (!options.match) return new Response("404 - API route not found", { status: 404 });
@@ -9,6 +9,7 @@
9
9
  * unit-testable and lives in a normal typed module rather than a codegen
10
10
  * template string.
11
11
  */
12
+ export declare function getSharedChunkFiles(manifest: Record<string, string[]>): string[];
12
13
  /**
13
14
  * Resolve the effective SSR manifest: prefer the caller-supplied object and
14
15
  * fall back to the registered client build metadata.