vinext 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/README.md +5 -2
  2. package/THIRD_PARTY_LICENSES.md +47 -0
  3. package/dist/_virtual/_rolldown/runtime.js +3 -2
  4. package/dist/build/prerender.js +1 -1
  5. package/dist/build/report.d.ts +0 -14
  6. package/dist/build/report.js +1 -51
  7. package/dist/check.js +1 -1
  8. package/dist/cli.js +3 -0
  9. package/dist/client/dev-error-overlay.js +1 -1
  10. package/dist/client/navigation-runtime.d.ts +13 -1
  11. package/dist/client/navigation-runtime.js +1 -1
  12. package/dist/config/next-config.d.ts +19 -0
  13. package/dist/config/next-config.js +64 -1
  14. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/dbcs-codec.js +327 -0
  15. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/dbcs-data.js +123 -0
  16. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/index.js +29 -0
  17. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/internal.js +134 -0
  18. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/sbcs-codec.js +50 -0
  19. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/sbcs-data-generated.js +455 -0
  20. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/sbcs-data.js +147 -0
  21. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/big5-added.json.js +169 -0
  22. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/cp936.json.js +2584 -0
  23. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/cp949.json.js +2220 -0
  24. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/cp950.json.js +227 -0
  25. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/eucjp.json.js +425 -0
  26. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/gb18030-ranges.json.js +427 -0
  27. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/gbk-added.json.js +229 -0
  28. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/tables/shiftjis.json.js +292 -0
  29. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/utf16.js +106 -0
  30. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/encodings/utf7.js +182 -0
  31. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/bom-handling.js +42 -0
  32. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/extend-node.js +147 -0
  33. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/index.js +94 -0
  34. package/dist/deps/.pnpm/iconv-lite@0.4.24/deps/iconv-lite/lib/streams.js +95 -0
  35. package/dist/deps/.pnpm/safer-buffer@2.1.2/deps/safer-buffer/safer.js +45 -0
  36. package/dist/entries/app-rsc-entry.js +17 -48
  37. package/dist/entries/app-rsc-manifest.js +3 -4
  38. package/dist/entries/app-ssr-entry.js +1 -0
  39. package/dist/entries/pages-server-entry.js +33 -8
  40. package/dist/index.js +83 -70
  41. package/dist/init-cloudflare.d.ts +1 -3
  42. package/dist/init-cloudflare.js +10 -48
  43. package/dist/init-platform.d.ts +2 -0
  44. package/dist/init-platform.js +4 -0
  45. package/dist/plugins/extensionless-dynamic-import.js +6 -2
  46. package/dist/plugins/next-intl-catalog.d.ts +12 -0
  47. package/dist/plugins/next-intl-catalog.js +85 -0
  48. package/dist/plugins/pages-node-externals.d.ts +1 -0
  49. package/dist/plugins/pages-node-externals.js +1 -1
  50. package/dist/plugins/use-cache-callable.d.ts +1 -1
  51. package/dist/plugins/use-cache-callable.js +136 -94
  52. package/dist/routing/app-route-graph.d.ts +6 -0
  53. package/dist/routing/app-route-graph.js +9 -1
  54. package/dist/server/api-handler.js +19 -4
  55. package/dist/server/app-bfcache-identity.js +1 -1
  56. package/dist/server/app-browser-entry.js +79 -63
  57. package/dist/server/app-browser-history-controller.d.ts +5 -4
  58. package/dist/server/app-browser-history-controller.js +93 -15
  59. package/dist/server/app-browser-navigation-controller.d.ts +2 -2
  60. package/dist/server/app-browser-navigation-controller.js +1 -2
  61. package/dist/server/app-browser-rsc-redirect.js +4 -4
  62. package/dist/server/app-browser-server-action-client.d.ts +2 -0
  63. package/dist/server/app-browser-server-action-client.js +4 -3
  64. package/dist/server/app-browser-state.d.ts +2 -4
  65. package/dist/server/app-browser-state.js +9 -4
  66. package/dist/server/app-browser-supplemental-refresh.js +16 -1
  67. package/dist/server/app-browser-visible-commit.d.ts +1 -1
  68. package/dist/server/app-browser-visible-commit.js +2 -12
  69. package/dist/server/app-elements-wire.d.ts +6 -38
  70. package/dist/server/app-elements-wire.js +4 -41
  71. package/dist/server/app-elements.d.ts +2 -2
  72. package/dist/server/app-elements.js +2 -2
  73. package/dist/server/app-history-state.d.ts +2 -2
  74. package/dist/server/app-history-state.js +5 -2
  75. package/dist/server/app-mounted-layouts.d.ts +21 -0
  76. package/dist/server/app-mounted-layouts.js +67 -0
  77. package/dist/server/app-optimistic-routing.d.ts +19 -1
  78. package/dist/server/app-optimistic-routing.js +42 -2
  79. package/dist/server/app-page-boundary-render.js +7 -6
  80. package/dist/server/app-page-boundary.js +2 -2
  81. package/dist/server/app-page-cache-finalizer.d.ts +12 -1
  82. package/dist/server/app-page-cache-finalizer.js +70 -29
  83. package/dist/server/app-page-cache.d.ts +13 -1
  84. package/dist/server/app-page-cache.js +31 -9
  85. package/dist/server/app-page-dispatch.d.ts +5 -10
  86. package/dist/server/app-page-dispatch.js +84 -154
  87. package/dist/server/app-page-element-builder.d.ts +2 -2
  88. package/dist/server/app-page-element-builder.js +10 -9
  89. package/dist/server/app-page-execution.d.ts +17 -52
  90. package/dist/server/app-page-execution.js +99 -95
  91. package/dist/server/app-page-head.d.ts +13 -0
  92. package/dist/server/app-page-head.js +44 -16
  93. package/dist/server/app-page-http-access-fallback-metadata.d.ts +1 -0
  94. package/dist/server/app-page-http-access-fallback-metadata.js +2 -2
  95. package/dist/server/app-page-layout-gate.d.ts +35 -0
  96. package/dist/server/app-page-layout-gate.js +50 -0
  97. package/dist/server/app-page-params.d.ts +0 -1
  98. package/dist/server/app-page-params.js +1 -14
  99. package/dist/server/app-page-probe.d.ts +3 -21
  100. package/dist/server/app-page-probe.js +4 -37
  101. package/dist/server/app-page-render.d.ts +2 -12
  102. package/dist/server/app-page-render.js +89 -220
  103. package/dist/server/app-page-request.d.ts +1 -3
  104. package/dist/server/app-page-request.js +1 -1
  105. package/dist/server/app-page-response.js +3 -3
  106. package/dist/server/app-page-route-wiring.d.ts +13 -6
  107. package/dist/server/app-page-route-wiring.js +67 -61
  108. package/dist/server/app-page-rsc-render-status.d.ts +27 -1
  109. package/dist/server/app-page-rsc-render-status.js +121 -13
  110. package/dist/server/app-page-stream.d.ts +2 -0
  111. package/dist/server/app-page-stream.js +2 -2
  112. package/dist/server/app-pages-bridge.d.ts +4 -0
  113. package/dist/server/app-pages-bridge.js +7 -3
  114. package/dist/server/app-render-dependency.d.ts +11 -4
  115. package/dist/server/app-render-dependency.js +86 -38
  116. package/dist/server/app-request-stage-dispatch.js +1 -1
  117. package/dist/server/app-route-handler-cache.js +2 -2
  118. package/dist/server/app-route-handler-dispatch.js +14 -9
  119. package/dist/server/app-route-handler-execution.js +8 -3
  120. package/dist/server/app-route-handler-policy.d.ts +1 -1
  121. package/dist/server/app-route-handler-policy.js +3 -3
  122. package/dist/server/app-route-handler-response.js +1 -1
  123. package/dist/server/app-route-handler-runtime.js +17 -4
  124. package/dist/server/app-rsc-cache-busting.d.ts +2 -2
  125. package/dist/server/app-rsc-cache-busting.js +4 -4
  126. package/dist/server/app-rsc-error-handler.d.ts +2 -1
  127. package/dist/server/app-rsc-error-handler.js +9 -1
  128. package/dist/server/app-rsc-errors.d.ts +8 -0
  129. package/dist/server/app-rsc-errors.js +8 -1
  130. package/dist/server/app-rsc-handler.d.ts +5 -5
  131. package/dist/server/app-rsc-handler.js +30 -24
  132. package/dist/server/app-rsc-request-normalization.d.ts +0 -4
  133. package/dist/server/app-rsc-request-normalization.js +1 -4
  134. package/dist/server/app-rsc-response-finalizer.js +2 -2
  135. package/dist/server/app-rsc-response-stage.js +5 -3
  136. package/dist/server/app-server-action-execution.d.ts +3 -1
  137. package/dist/server/app-server-action-execution.js +7 -4
  138. package/dist/server/app-ssr-entry.d.ts +2 -0
  139. package/dist/server/app-ssr-entry.js +9 -8
  140. package/dist/server/app-ssr-stream.js +1 -1
  141. package/dist/server/app-visited-response-cache.d.ts +36 -1
  142. package/dist/server/app-visited-response-cache.js +46 -5
  143. package/dist/server/app-worker-stages.d.ts +25 -3
  144. package/dist/server/app-worker-stages.js +45 -6
  145. package/dist/server/artifact-compatibility.d.ts +0 -1
  146. package/dist/server/artifact-compatibility.js +1 -10
  147. package/dist/server/cache-proof.d.ts +1 -115
  148. package/dist/server/cache-proof.js +1 -544
  149. package/dist/server/cacheability-request.js +13 -8
  150. package/dist/server/dev-server.js +4 -3
  151. package/dist/server/headers.d.ts +6 -3
  152. package/dist/server/headers.js +9 -5
  153. package/dist/server/isr-cache.d.ts +8 -2
  154. package/dist/server/isr-cache.js +26 -1
  155. package/dist/server/metadata-route-response.js +32 -15
  156. package/dist/server/multi-stage.d.ts +52 -11
  157. package/dist/server/multi-stage.js +13 -1
  158. package/dist/server/navigation-planner.js +1 -1
  159. package/dist/server/pages-api-route.js +28 -19
  160. package/dist/server/pages-media-type.d.ts +21 -1
  161. package/dist/server/pages-media-type.js +69 -5
  162. package/dist/server/pages-node-compat.js +52 -27
  163. package/dist/server/pages-page-handler.js +11 -20
  164. package/dist/server/pages-request-pipeline.d.ts +8 -0
  165. package/dist/server/pages-request-pipeline.js +2 -2
  166. package/dist/server/pages-request-stage-entry.js +14 -12
  167. package/dist/server/pages-response-stage-entry.js +9 -3
  168. package/dist/server/prod-server.js +5 -1
  169. package/dist/server/request-pipeline.d.ts +6 -0
  170. package/dist/server/request-pipeline.js +21 -1
  171. package/dist/server/shared-cache-identity.d.ts +23 -0
  172. package/dist/server/shared-cache-identity.js +36 -0
  173. package/dist/server/worker-stages.d.ts +16 -3
  174. package/dist/server/worker-stages.js +23 -4
  175. package/dist/shims/cache-handler.js +2 -2
  176. package/dist/shims/cache-request-state.d.ts +17 -20
  177. package/dist/shims/cache-request-state.js +16 -21
  178. package/dist/shims/cache-runtime.js +27 -11
  179. package/dist/shims/cache.d.ts +8 -7
  180. package/dist/shims/cache.js +17 -19
  181. package/dist/shims/cacheability-classification.d.ts +2 -0
  182. package/dist/shims/cdn-cache.d.ts +22 -5
  183. package/dist/shims/client-page-root.js +2 -2
  184. package/dist/shims/dynamic.js +9 -6
  185. package/dist/shims/error-boundary.js +2 -2
  186. package/dist/shims/fetch-cache-state.d.ts +5 -1
  187. package/dist/shims/fetch-cache-state.js +1 -3
  188. package/dist/shims/fetch-cache.d.ts +2 -1
  189. package/dist/shims/fetch-cache.js +34 -24
  190. package/dist/shims/form.js +2 -2
  191. package/dist/shims/headers.d.ts +8 -23
  192. package/dist/shims/headers.js +17 -67
  193. package/dist/shims/internal/navigation-untracked.js +1 -1
  194. package/dist/shims/link.js +3 -1
  195. package/dist/shims/metadata.d.ts +23 -21
  196. package/dist/shims/metadata.js +62 -24
  197. package/dist/shims/navigation-server.js +1 -1
  198. package/dist/shims/navigation.d.ts +74 -7
  199. package/dist/shims/navigation.js +183 -29
  200. package/dist/shims/navigation.react-server.js +2 -2
  201. package/dist/shims/request-state-types.d.ts +2 -2
  202. package/dist/shims/slot.d.ts +12 -0
  203. package/dist/shims/slot.js +37 -9
  204. package/dist/shims/unified-request-context.d.ts +1 -1
  205. package/dist/shims/unified-request-context.js +2 -3
  206. package/dist/utils/cache-control-metadata.d.ts +5 -0
  207. package/dist/utils/cache-control-metadata.js +9 -1
  208. package/dist/utils/project.d.ts +2 -2
  209. package/dist/utils/project.js +3 -16
  210. package/dist/utils/regex.d.ts +5 -0
  211. package/dist/utils/regex.js +11 -1
  212. package/dist/utils/text-stream.d.ts +10 -5
  213. package/dist/utils/text-stream.js +6 -6
  214. package/package.json +2 -1
  215. package/dist/build/layout-classification-types.d.ts +0 -60
  216. package/dist/build/layout-classification-types.js +0 -1
  217. package/dist/build/layout-classification.d.ts +0 -62
  218. package/dist/build/layout-classification.js +0 -98
  219. package/dist/build/route-classification-injector.d.ts +0 -33
  220. package/dist/build/route-classification-injector.js +0 -59
  221. package/dist/build/route-classification-manifest.d.ts +0 -54
  222. package/dist/build/route-classification-manifest.js +0 -143
  223. package/dist/server/app-browser-client-reuse-manifest.d.ts +0 -11
  224. package/dist/server/app-browser-client-reuse-manifest.js +0 -101
  225. package/dist/server/app-layout-param-observation.d.ts +0 -42
  226. package/dist/server/app-layout-param-observation.js +0 -168
  227. package/dist/server/client-reuse-manifest.d.ts +0 -103
  228. package/dist/server/client-reuse-manifest.js +0 -209
  229. package/dist/server/skip-cache-proof.d.ts +0 -60
  230. package/dist/server/skip-cache-proof.js +0 -168
  231. package/dist/server/static-layout-client-reuse-proof.d.ts +0 -14
  232. package/dist/server/static-layout-client-reuse-proof.js +0 -33
  233. package/dist/utils/sorted-array.d.ts +0 -8
  234. package/dist/utils/sorted-array.js +0 -20
@@ -11,7 +11,7 @@ import { createPprFallbackShellSuspensePromise, trackPprFallbackShellCacheTask }
11
11
  import { createUseCacheAccessError, markDynamicUsage, recordInvalidDynamicUsageError } from "./internal/headers-state.js";
12
12
  import { isDraftModeEnabled } from "./headers.js";
13
13
  import { _registerCacheContextAccessor } from "./internal/cache-context-accessor.js";
14
- import { _hasPendingRevalidatedTag, _setRequestScopedCacheLife, cacheLifeProfiles, runWithDetachedCacheObservations, shouldServeStaleUnstableCacheEntry } from "./cache-request-state.js";
14
+ import { _hasPendingRevalidatedTag, _isOnDemandRevalidate, _setRequestScopedCacheLife, cacheLifeProfiles, runWithDetachedCacheObservations, shouldServeStaleUnstableCacheEntry } from "./cache-request-state.js";
15
15
  import "./fetch-cache.js";
16
16
  import { hasUseCacheLayoutMarker, hasUseCachePageMarker, isUseCacheFunctionReference, withoutUseCacheSegmentMarker } from "./internal/app-page-props-cache-key.js";
17
17
  import { isWellKnownProperty } from "./internal/thenable-well-known-properties.js";
@@ -389,7 +389,7 @@ function registerCachedFunction(fn, id, variant, options = {}) {
389
389
  if (knownRootParamNames && rootParams) cacheKey += computeRootParamsCacheKeySuffix(rootParams, knownRootParamNames);
390
390
  const softTags = getCurrentFetchSoftTags();
391
391
  let existing = null;
392
- if (!_hasPendingRevalidatedTag(softTags)) try {
392
+ if (!_hasPendingRevalidatedTag(softTags) && !_isOnDemandRevalidate()) try {
393
393
  existing = await handler.get(cacheKey, {
394
394
  kind: "FETCH",
395
395
  softTags
@@ -398,8 +398,10 @@ function registerCachedFunction(fn, id, variant, options = {}) {
398
398
  console.error("[vinext] use cache: handler.get failed; treating as a cache miss:", error);
399
399
  }
400
400
  const redirectValue = existing?.value;
401
+ let staleRedirect;
401
402
  if (isRootParamRedirect(existing) && isServableCacheState(existing?.cacheState) && rootParams && redirectValue?.kind === "FETCH" && !_hasPendingRevalidatedTag([...redirectValue.tags ?? [], ...softTags])) {
402
403
  cacheKey = coarseCacheKey + computeRootParamsCacheKeySuffix(rootParams, addKnownRootParamNames(id, rootParamNamesFromTags(redirectValue.tags)));
404
+ if (existing?.cacheState) staleRedirect = existing;
403
405
  try {
404
406
  existing = await handler.get(cacheKey, {
405
407
  kind: "FETCH",
@@ -410,7 +412,8 @@ function registerCachedFunction(fn, id, variant, options = {}) {
410
412
  existing = null;
411
413
  }
412
414
  }
413
- const generate = async (propagate) => {
415
+ const generate = async (propagate, staleEntry) => {
416
+ const timestamp = Date.now();
414
417
  const { result, ctx, effectiveLife, collectedResult } = await runCachedFunctionWithContext(callFn, callArgs, cacheVariant, (value) => serializeCacheResult(value, rsc));
415
418
  const rootParamNames = ctx.readRootParamNames && ctx.readRootParamNames.size > 0 ? addKnownRootParamNames(id, ctx.readRootParamNames) : knownRootParamsByFunctionId.get(id);
416
419
  if (propagate) {
@@ -442,6 +445,7 @@ function registerCachedFunction(fn, id, variant, options = {}) {
442
445
  const cacheContext = {
443
446
  fetchCache: true,
444
447
  tags: ctx.tags,
448
+ timestamp,
445
449
  ...cacheFunctionInvocation ? { cacheFunctionInvocation } : {},
446
450
  cacheControl: {
447
451
  revalidate: revalidateSeconds,
@@ -463,11 +467,18 @@ function registerCachedFunction(fn, id, variant, options = {}) {
463
467
  revalidate: revalidateSeconds
464
468
  }, {
465
469
  ...cacheContext,
466
- tags: redirectTags
470
+ tags: redirectTags,
471
+ ...coarseCacheKey === cacheKey ? staleEntry && { staleEntry } : staleRedirect && { staleEntry: staleRedirect }
467
472
  });
468
473
  cacheValue.data.url = specificCacheKey;
469
- await handler.set(specificCacheKey, cacheValue, cacheContext);
470
- } else await handler.set(cacheKey, cacheValue, cacheContext);
474
+ await handler.set(specificCacheKey, cacheValue, {
475
+ ...cacheContext,
476
+ ...staleEntry && specificCacheKey === cacheKey ? { staleEntry } : {}
477
+ });
478
+ } else await handler.set(cacheKey, cacheValue, {
479
+ ...cacheContext,
480
+ ...staleEntry ? { staleEntry } : {}
481
+ });
471
482
  } catch {}
472
483
  return collectedResult ? collectedResult.result : result;
473
484
  };
@@ -476,10 +487,13 @@ function registerCachedFunction(fn, id, variant, options = {}) {
476
487
  propagateCacheTagsToRequest(existing.value.tags);
477
488
  const result = await rsc.createFromReadableStream(uint8ToStream(base64ToUint8(existing.value.data.body)), {}, { preserveServerReferences: true });
478
489
  recordRequestScopedCacheControl(existing.cacheControl);
479
- if (existing.cacheState === "stale") regenerateInBackground(id, () => generate(false));
490
+ if (existing.cacheState === "stale") {
491
+ const staleEntry = existing;
492
+ regenerateInBackground(id, () => generate(false, staleEntry));
493
+ }
480
494
  return result;
481
495
  } catch {}
482
- return generate(true);
496
+ return generate(true, existing?.cacheState ? existing : void 0);
483
497
  }, cacheVariant);
484
498
  };
485
499
  const cachedFn = memoizeInCacheScope((...args) => invoke(args));
@@ -589,9 +603,11 @@ async function runCachedFunctionWithContext(fn, args, variant, collectResult) {
589
603
  let collectedResult;
590
604
  const workUnitType = variant === "private" ? "private-cache" : "cache";
591
605
  const result = await workUnitAsyncStorage.run({ type: workUnitType }, () => cacheContextStorage.run(ctx, async () => {
592
- const value = await fn(...args);
593
- if (collectResult) collectedResult = await collectResult(value, ctx);
594
- return value;
606
+ if (!collectResult) return await fn(...args);
607
+ let value;
608
+ const lazyValue = { then: (onFulfilled, onRejected) => (value ??= new Promise((resolve) => resolve(fn(...args)))).then(onFulfilled, onRejected) };
609
+ collectedResult = await collectResult(lazyValue, ctx);
610
+ return await lazyValue;
595
611
  }));
596
612
  if (ctx.invalidDynamicUsageError) throw ctx.invalidDynamicUsageError;
597
613
  const effectiveLife = resolveCacheLife(ctx.lifeConfigs);
@@ -1,7 +1,7 @@
1
1
  import { ExecutionContextLike, getRequestExecutionContext, runWithExecutionContext } from "./request-context.js";
2
- import { _recordUseCacheRootParamRead, _registerCacheContextAccessor, getRegisteredCacheContext } from "./internal/cache-context-accessor.js";
3
- import { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, ActionRevalidationKind, CacheLifeConfig, CacheLifeSink, CacheState, PendingCdnTagInvalidation, UnstableCacheObservation, UnstableCacheRevalidationMode, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _queuePendingCdnTagInvalidation, _queuePendingRevalidation, _revalidateCdnTags, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, markActionRevalidation, recordUnstableCacheObservation, runWithDetachedCacheObservations, shouldServeStaleUnstableCacheEntry } from "./cache-request-state.js";
4
2
  import { CacheControlMetadata, CacheHandler, CacheHandlerContext, CacheHandlerValue, CachedAppPageValue, CachedFetchValue, CachedImageValue, CachedPagesValue, CachedRedirectValue, CachedRouteValue, IncrementalCacheValue, MemoryCacheHandler, NoOpCacheHandler, configureMemoryCacheHandler, getCacheHandler, getDataCacheHandler, registerDataCacheHandler, registerLazyDataCacheHandler, setCacheHandler, setDataCacheHandler } from "./cache-handler.js";
3
+ import { _recordUseCacheRootParamRead, _registerCacheContextAccessor, getRegisteredCacheContext } from "./internal/cache-context-accessor.js";
4
+ import { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, ActionRevalidationKind, CacheLifeConfig, CacheState, PendingCdnTagInvalidation, UnstableCacheRevalidationMode, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _isOnDemandRevalidate, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _queuePendingCdnTagInvalidation, _queuePendingRevalidation, _revalidateCdnTags, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, markActionRevalidation, runWithDetachedCacheObservations, shouldServeStaleUnstableCacheEntry } from "./cache-request-state.js";
5
5
  //#region src/shims/cache.d.ts
6
6
  /**
7
7
  * Revalidate cached data associated with a specific cache tag.
@@ -61,10 +61,11 @@ export declare function updateTag(tag: string): undefined;
61
61
  /**
62
62
  * Opt out of static rendering and indicate a particular component should not be cached.
63
63
  *
64
- * In Next.js, calling noStore() inside a Server Component ensures the component
65
- * is dynamically rendered. In our implementation, this is a no-op since we don't
66
- * have the same static/dynamic rendering split — all server rendering is on-demand.
67
- * It's provided for API compatibility so apps importing it don't break.
64
+ * Without Cache Components this marks the render dynamic, so the page is not
65
+ * stored. With Cache Components it does nothing, as in Next.js: its prerenders
66
+ * ignore it, and on a request it doesn't make the page dynamic, so the page
67
+ * stays static and cacheable.
68
+ * Source: https://github.com/vercel/next.js/blob/canary/packages/next/src/server/web/spec-extension/unstable-no-store.ts
68
69
  */
69
70
  export declare function unstable_noStore(): void;
70
71
  /**
@@ -142,4 +143,4 @@ type UnstableCacheOptions = {
142
143
  */
143
144
  export declare function unstable_cache<T extends (...args: any[]) => Promise<any>>(fn: T, keyParts?: string[], options?: UnstableCacheOptions): T;
144
145
  //#endregion
145
- export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, ActionRevalidationKind, CacheControlMetadata, CacheHandler, CacheHandlerContext, CacheHandlerValue, CacheLifeConfig, CacheLifeSink, CacheState, CachedAppPageValue, CachedFetchValue, CachedImageValue, CachedPagesValue, CachedRedirectValue, CachedRouteValue, type ExecutionContextLike, IncrementalCacheValue, MemoryCacheHandler, NoOpCacheHandler, PendingCdnTagInvalidation, UnstableCacheObservation, UnstableCacheRevalidationMode, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _queuePendingCdnTagInvalidation, _queuePendingRevalidation, _recordUseCacheRootParamRead, _registerCacheContextAccessor, _revalidateCdnTags, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, configureMemoryCacheHandler, getAndClearActionRevalidationKind, getCacheHandler, getDataCacheHandler, getRegisteredCacheContext, getRequestExecutionContext, markActionRevalidation, unstable_noStore as noStore, recordUnstableCacheObservation, registerDataCacheHandler, registerLazyDataCacheHandler, runWithDetachedCacheObservations, runWithExecutionContext, setCacheHandler, setDataCacheHandler, shouldServeStaleUnstableCacheEntry };
146
+ export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, ActionRevalidationKind, CacheControlMetadata, CacheHandler, CacheHandlerContext, CacheHandlerValue, CacheLifeConfig, CacheState, CachedAppPageValue, CachedFetchValue, CachedImageValue, CachedPagesValue, CachedRedirectValue, CachedRouteValue, type ExecutionContextLike, IncrementalCacheValue, MemoryCacheHandler, NoOpCacheHandler, PendingCdnTagInvalidation, UnstableCacheRevalidationMode, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _isOnDemandRevalidate, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _queuePendingCdnTagInvalidation, _queuePendingRevalidation, _recordUseCacheRootParamRead, _registerCacheContextAccessor, _revalidateCdnTags, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, configureMemoryCacheHandler, getAndClearActionRevalidationKind, getCacheHandler, getDataCacheHandler, getRegisteredCacheContext, getRequestExecutionContext, markActionRevalidation, unstable_noStore as noStore, registerDataCacheHandler, registerLazyDataCacheHandler, runWithDetachedCacheObservations, runWithExecutionContext, setCacheHandler, setDataCacheHandler, shouldServeStaleUnstableCacheEntry };
@@ -6,13 +6,13 @@ import { workUnitAsyncStorage } from "./internal/work-unit-async-storage.js";
6
6
  import { isStagedCacheabilityProbeActive } from "./cacheability-classification.js";
7
7
  import { MemoryCacheHandler, NoOpCacheHandler, configureMemoryCacheHandler, getCacheHandler, getDataCacheHandler, registerDataCacheHandler, registerLazyDataCacheHandler, setCacheHandler, setDataCacheHandler } from "./cache-handler.js";
8
8
  import { encodeCacheTag, encodeCacheTags } from "../utils/encode-cache-tag.js";
9
- import { fnv1a64, sha256Hex } from "../utils/hash.js";
9
+ import { sha256Hex } from "../utils/hash.js";
10
10
  import { addCollectedRequestTags, getCurrentFetchSoftTags, runWithDetachedFetchObservations } from "./fetch-cache-state.js";
11
11
  import { makeHangingPromise } from "./internal/make-hanging-promise.js";
12
12
  import { getHeadersAccessPhase, markDynamicUsage } from "./internal/headers-state.js";
13
13
  import { isDraftModeEnabled } from "./headers.js";
14
14
  import { _recordUseCacheRootParamRead, _registerCacheContextAccessor, getRegisteredCacheContext } from "./internal/cache-context-accessor.js";
15
- import { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _queuePendingCdnTagInvalidation, _queuePendingRevalidation, _revalidateCdnTags, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, markActionRevalidation, recordUnstableCacheObservation, runWithDetachedCacheObservations, shouldServeStaleUnstableCacheEntry } from "./cache-request-state.js";
15
+ import { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _isOnDemandRevalidate, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _queuePendingCdnTagInvalidation, _queuePendingRevalidation, _revalidateCdnTags, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, markActionRevalidation, runWithDetachedCacheObservations, shouldServeStaleUnstableCacheEntry } from "./cache-request-state.js";
16
16
  import { isPathKnownNotEdgeCached } from "./cdn-purge-routes.js";
17
17
  //#region src/shims/cache.ts
18
18
  /**
@@ -154,13 +154,15 @@ function updateTag(tag) {
154
154
  /**
155
155
  * Opt out of static rendering and indicate a particular component should not be cached.
156
156
  *
157
- * In Next.js, calling noStore() inside a Server Component ensures the component
158
- * is dynamically rendered. In our implementation, this is a no-op since we don't
159
- * have the same static/dynamic rendering split — all server rendering is on-demand.
160
- * It's provided for API compatibility so apps importing it don't break.
157
+ * Without Cache Components this marks the render dynamic, so the page is not
158
+ * stored. With Cache Components it does nothing, as in Next.js: its prerenders
159
+ * ignore it, and on a request it doesn't make the page dynamic, so the page
160
+ * stays static and cacheable.
161
+ * Source: https://github.com/vercel/next.js/blob/canary/packages/next/src/server/web/spec-extension/unstable-no-store.ts
161
162
  */
162
163
  function unstable_noStore() {
163
164
  if (isInsideUnstableCacheScope()) return;
165
+ if (String(process.env.__NEXT_CACHE_COMPONENTS) === "true") return;
164
166
  markDynamicUsage();
165
167
  }
166
168
  /**
@@ -394,7 +396,7 @@ function scheduleUnstableCacheBackgroundRevalidation(cacheKey, refresh) {
394
396
  pending.set(cacheKey, trackedRevalidation);
395
397
  waitUntilUnstableCacheRevalidation(trackedRevalidation);
396
398
  }
397
- async function refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds) {
399
+ async function refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds, staleEntry) {
398
400
  const argsUnchanged = serializeArgsOrNull(args) === keyedArgs;
399
401
  const result = await _unstableCacheAls.run(true, () => _unstableCacheCallbackAls.run({ useCacheContext: _useCacheContextAls.getStore() }, () => fn(...args)));
400
402
  if (!argsUnchanged) return result;
@@ -411,7 +413,8 @@ async function refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, r
411
413
  await getDataCacheHandler().set(cacheKey, cacheValue, {
412
414
  fetchCache: true,
413
415
  tags,
414
- revalidate: revalidateSeconds
416
+ revalidate: revalidateSeconds,
417
+ ...staleEntry ? { staleEntry } : {}
415
418
  });
416
419
  return result;
417
420
  }
@@ -439,16 +442,10 @@ function unstable_cache(fn, keyParts, options) {
439
442
  const keyedArgs = JSON.stringify(args);
440
443
  const invocationKey = `${fixedKey}${keyedArgs}`;
441
444
  addCollectedRequestTags(tags);
442
- recordUnstableCacheObservation({
443
- kind: "unstable_cache",
444
- keyHash: fnv1a64(invocationKey),
445
- revalidate: typeof revalidateSeconds === "number" ? revalidateSeconds : revalidateSeconds === false ? false : null,
446
- tagCount: tags.length,
447
- tagHash: tags.length > 0 ? fnv1a64(JSON.stringify(tags)) : null
448
- });
449
445
  const cacheKey = `${UNSTABLE_CACHE_KEY_PREFIX}:${await sha256Hex(invocationKey)}`;
450
446
  const isDraftMode = isDraftModeEnabled();
451
- if (!isDraftMode && !isNestedUnstableCacheCall()) {
447
+ let staleEntry;
448
+ if (!isDraftMode && !isNestedUnstableCacheCall() && !_isOnDemandRevalidate()) {
452
449
  const softTags = getCurrentFetchSoftTags();
453
450
  const existing = _hasPendingRevalidatedTag([...tags, ...softTags]) ? null : await getDataCacheHandler().get(cacheKey, {
454
451
  kind: "FETCH",
@@ -457,10 +454,11 @@ function unstable_cache(fn, keyParts, options) {
457
454
  });
458
455
  if (existing?.value && existing.value.kind === "FETCH") {
459
456
  const cached = tryDeserializeUnstableCacheResult(existing.value.data.body);
457
+ if (existing.cacheState === "stale") staleEntry = existing;
460
458
  if (cached.ok) {
461
459
  if (existing.cacheState === "stale") {
462
460
  if (shouldServeStaleUnstableCacheEntry()) {
463
- scheduleUnstableCacheBackgroundRevalidation(cacheKey, () => refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds));
461
+ scheduleUnstableCacheBackgroundRevalidation(cacheKey, () => refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds, staleEntry));
464
462
  return cached.value;
465
463
  }
466
464
  } else return cached.value;
@@ -468,9 +466,9 @@ function unstable_cache(fn, keyParts, options) {
468
466
  }
469
467
  }
470
468
  if (isDraftMode) return await _unstableCacheAls.run(true, () => fn(...args));
471
- return await refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds);
469
+ return await refreshUnstableCacheResult(fn, args, keyedArgs, cacheKey, tags, revalidateSeconds, staleEntry);
472
470
  };
473
471
  return cachedFn;
474
472
  }
475
473
  //#endregion
476
- export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, MemoryCacheHandler, NoOpCacheHandler, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _queuePendingCdnTagInvalidation, _queuePendingRevalidation, _recordUseCacheRootParamRead, _registerCacheContextAccessor, _revalidateCdnTags, _runWithCacheState, _setRequestScopedCacheLife, cacheLife, cacheLifeProfiles, cacheTag, configureMemoryCacheHandler, getAndClearActionRevalidationKind, getCacheHandler, getDataCacheHandler, getRegisteredCacheContext, getRequestExecutionContext, io, isInsideUnstableCacheScope, markActionRevalidation, unstable_noStore as noStore, unstable_noStore, recordUnstableCacheObservation, refresh, registerDataCacheHandler, registerLazyDataCacheHandler, revalidatePath, revalidateTag, runWithDetachedCacheObservations, runWithExecutionContext, setCacheHandler, setDataCacheHandler, shouldServeStaleUnstableCacheEntry, unstable_cache, unstable_cacheLife, unstable_cacheTag, unstable_io, updateTag };
474
+ export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, MemoryCacheHandler, NoOpCacheHandler, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _isOnDemandRevalidate, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _queuePendingCdnTagInvalidation, _queuePendingRevalidation, _recordUseCacheRootParamRead, _registerCacheContextAccessor, _revalidateCdnTags, _runWithCacheState, _setRequestScopedCacheLife, cacheLife, cacheLifeProfiles, cacheTag, configureMemoryCacheHandler, getAndClearActionRevalidationKind, getCacheHandler, getDataCacheHandler, getRegisteredCacheContext, getRequestExecutionContext, io, isInsideUnstableCacheScope, markActionRevalidation, unstable_noStore as noStore, unstable_noStore, refresh, registerDataCacheHandler, registerLazyDataCacheHandler, revalidatePath, revalidateTag, runWithDetachedCacheObservations, runWithExecutionContext, setCacheHandler, setDataCacheHandler, shouldServeStaleUnstableCacheEntry, unstable_cache, unstable_cacheLife, unstable_cacheTag, unstable_io, updateTag };
@@ -17,6 +17,8 @@ export type RouteCacheabilityOutcome = {
17
17
  */
18
18
  status?: number;
19
19
  headers?: Readonly<Record<string, string>>;
20
+ /** App pages: the client-router stale time from the completed render's cacheLife. */
21
+ staleTimeSeconds?: number;
20
22
  tags?: readonly string[];
21
23
  };
22
24
  export type RouteCacheabilityState = {
@@ -32,6 +32,12 @@ export type CdnCacheableHeaderInput = {
32
32
  */
33
33
  tags?: readonly string[];
34
34
  };
35
+ /** A page's document, rendered from an RSC request's payload. */
36
+ export type CdnAppPageDocument = {
37
+ html: string;
38
+ /** The renderer's own `Link` header. */
39
+ linkHeader?: string;
40
+ };
35
41
  export type CdnResponsePolicy = {
36
42
  /** Whether a provider-specific response header controls shared caching. */
37
43
  isHeader(name: string): boolean;
@@ -76,12 +82,15 @@ export type CdnCacheAdapter = {
76
82
  */
77
83
  readonly requiresCompletedResponseAdmission?: boolean;
78
84
  /**
79
- * This adapter's response-stage transport keys shared App page dispatches by
80
- * `VinextResponseStageDispatchOptions.cacheIdentity` when core supplies one.
81
- * Core supplies it only when completed-response admission is also required,
82
- * because that admission is what makes a query-free identity safe.
85
+ * This adapter's response-stage transport keys shared dispatches by
86
+ * `VinextResponseStageDispatchOptions.cacheIdentity` when core supplies
87
+ * one. Core supplies it only when completed-response admission is also
88
+ * required, because that admission is what makes a host-free identity, and
89
+ * a page's query-free one, safe. `"query-free"` also drops the user query
90
+ * from App page identities; `"host-free"` keeps every dispatch's query, for
91
+ * a transport whose cache keys the dispatched query itself.
83
92
  */
84
- readonly responseStageCacheIdentity?: "query-free";
93
+ readonly responseStageCacheIdentity?: "host-free" | "query-free";
85
94
  /**
86
95
  * Optionally return a foreground page response while completed-response
87
96
  * admission continues on an independent body branch. Returning `null` keeps
@@ -90,6 +99,14 @@ export type CdnCacheAdapter = {
90
99
  deferCompletedPageResponseAdmission?(response: Response, complete: (response: Response) => Promise<Response>): Response | null;
91
100
  /** Capture the full-route RSC side stream while admitting a completed HTML response. */
92
101
  captureAppPageRscData?(rscData: Promise<ArrayBuffer>): void;
102
+ /**
103
+ * Store the page's document beside an RSC request's completed response, as
104
+ * Next.js keeps a page's document and RSC payload in one entry. Returns the
105
+ * callback admission calls with the document the render rendered from its
106
+ * payload, or with `null` when the page has no document to store, or
107
+ * `undefined` when this render's response stores no document.
108
+ */
109
+ captureAppPageDocument?(): ((document: CdnAppPageDocument | null) => void) | undefined;
93
110
  /**
94
111
  * Whether this adapter sends the 304 for a Pages render whose If-None-Match
95
112
  * matches. When it does, the render returns the page, so an adapter that
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { searchParamsToRecord } from "../utils/query.js";
3
3
  import { getNavigationContext } from "./navigation-context-state.js";
4
+ import { getClientNavigationRenderContext, useSearchParams } from "./navigation.js";
4
5
  import "./navigation-server.js";
5
- import { isOwnPropertyCheck, isWellKnownProperty } from "./internal/thenable-well-known-properties.js";
6
6
  import { RenderedSearchContext } from "./slot.js";
7
- import { getClientNavigationRenderContext, useSearchParams } from "./navigation.js";
7
+ import { isOwnPropertyCheck, isWellKnownProperty } from "./internal/thenable-well-known-properties.js";
8
8
  import { createElement, use, useMemo } from "react";
9
9
  //#region src/shims/client-page-root.tsx
10
10
  /**
@@ -89,9 +89,11 @@ function createElementWithProps(Component, props) {
89
89
  }
90
90
  /**
91
91
  * Lightweight error boundary that renders the loading component with the error
92
- * when a dynamic() loader rejects. Without this, loader failures would propagate
93
- * uncaught through React's rendering — this preserves the Next.js behavior where
94
- * the `loading` component can display errors.
92
+ * when a dynamic() loader rejects, as the Pages Router's react-loadable does by
93
+ * passing `error` to `loading`. App Router trees never get it: Next.js's App
94
+ * Router next/dynamic is React.lazy inside Suspense with no boundary of its own,
95
+ * so loader and render errors reach the nearest error.tsx.
96
+ * https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/lazy-dynamic/loadable.tsx
95
97
  *
96
98
  * Lazily created because React.Component is not available in the RSC environment
97
99
  * (server components use a slimmed-down React that doesn't include class components).
@@ -202,6 +204,7 @@ function dynamic(dynamicInput, options) {
202
204
  }
203
205
  const InitialLazyComponent = createLazyComponent(loader);
204
206
  const ClientSSRFalse = (props) => {
207
+ const isAppRouterTree = useIsAppRouterTree();
205
208
  const [mounted, setMounted] = React.useState(false);
206
209
  const { LazyComponent, retry, retryKey } = useRetryableLazyComponent(loader, InitialLazyComponent);
207
210
  React.useEffect(() => setMounted(true), []);
@@ -209,7 +212,7 @@ function dynamic(dynamicInput, options) {
209
212
  const fallback = LoadingComponent ? React.createElement(LoadingComponent, createDynamicLoadingProps({ retry })) : null;
210
213
  const lazyElement = createElementWithProps(LazyComponent, props);
211
214
  let content = lazyElement;
212
- if (LoadingComponent) {
215
+ if (LoadingComponent && !isAppRouterTree) {
213
216
  const ErrorBoundary = getDynamicErrorBoundary();
214
217
  if (ErrorBoundary) content = React.createElement(ErrorBoundary, {
215
218
  fallback: LoadingComponent,
@@ -237,7 +240,7 @@ function dynamic(dynamicInput, options) {
237
240
  const fallback = LoadingComponent ? React.createElement(LoadingComponent, createDynamicLoadingProps()) : null;
238
241
  const lazyElement = createElementWithProps(LazyServer, props);
239
242
  let content = lazyElement;
240
- if (LoadingComponent) {
243
+ if (LoadingComponent && !isAppRouterTree) {
241
244
  const ErrorBoundary = getDynamicErrorBoundary();
242
245
  if (ErrorBoundary) content = React.createElement(ErrorBoundary, {
243
246
  fallback: LoadingComponent,
@@ -257,7 +260,7 @@ function dynamic(dynamicInput, options) {
257
260
  const fallback = LoadingComponent ? React.createElement(LoadingComponent, createDynamicLoadingProps({ retry })) : null;
258
261
  const lazyElement = createElementWithProps(LazyComponent, props);
259
262
  let content = lazyElement;
260
- if (LoadingComponent) {
263
+ if (LoadingComponent && !isAppRouterTree) {
261
264
  const ErrorBoundary = getDynamicErrorBoundary();
262
265
  if (ErrorBoundary) content = React.createElement(ErrorBoundary, {
263
266
  fallback: LoadingComponent,
@@ -1,11 +1,11 @@
1
1
  "use client";
2
2
  import { isNavigationSignalError } from "../utils/navigation-signal.js";
3
+ import { handleAppNavigationFailure } from "../client/app-nav-failure-handler.js";
4
+ import { VINEXT_DEV_ERROR_RECOVERY_EVENT } from "../utils/dev-error-recovery-event.js";
3
5
  import { decodeRedirectError, forbidden, isRedirectError, notFound, unauthorized } from "./navigation-errors.js";
4
6
  import "./navigation-server.js";
5
7
  import { isRedirectToCurrentUrl, useErrorBoundaryPathname, useErrorBoundaryRouter } from "./error-boundary-navigation.js";
6
8
  import DefaultGlobalError from "./default-global-error.js";
7
- import { handleAppNavigationFailure } from "../client/app-nav-failure-handler.js";
8
- import { VINEXT_DEV_ERROR_RECOVERY_EVENT } from "../utils/dev-error-recovery-event.js";
9
9
  import { consumeCaughtNotFoundBoundary, useCaughtNotFoundBoundary } from "./caught-not-found-boundaries.js";
10
10
  import React from "react";
11
11
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -11,12 +11,16 @@ export type FetchDedupeEntry = {
11
11
  response: Response | null;
12
12
  };
13
13
  export type FetchCacheState = {
14
- cacheableFetchUrls: Set<string>;
15
14
  currentRequestTags: string[];
16
15
  currentFetchSoftTags: string[];
17
16
  currentFetchCacheMode: FetchCacheMode | null;
18
17
  currentFetchRevalidate: number | null;
19
18
  currentForceDynamicFetchDefault: boolean;
19
+ /**
20
+ * Pages Router preview/draft render. App Router draft state is read from the
21
+ * headers context instead; Pages renders don't have one.
22
+ */
23
+ isDraftMode: boolean;
20
24
  dynamicFetchUrls: Set<string>;
21
25
  refreshStaleFetchesInForeground: boolean;
22
26
  isFetchDedupeActive: boolean;
@@ -11,12 +11,12 @@ const _FALLBACK_KEY = Symbol.for("vinext.fetchCache.fallback");
11
11
  const fetchCacheAls = getOrCreateAls("vinext.fetchCache.als");
12
12
  function createFetchCacheState() {
13
13
  return {
14
- cacheableFetchUrls: /* @__PURE__ */ new Set(),
15
14
  currentRequestTags: [],
16
15
  currentFetchSoftTags: [],
17
16
  currentFetchCacheMode: null,
18
17
  currentFetchRevalidate: null,
19
18
  currentForceDynamicFetchDefault: false,
19
+ isDraftMode: false,
20
20
  dynamicFetchUrls: /* @__PURE__ */ new Set(),
21
21
  refreshStaleFetchesInForeground: false,
22
22
  isFetchDedupeActive: false,
@@ -77,13 +77,11 @@ function getCurrentFetchSoftTags() {
77
77
  */
78
78
  function runWithDetachedFetchObservations(fn) {
79
79
  if (isInsideUnifiedScope()) return runWithUnifiedStateMutation((uCtx) => {
80
- uCtx.cacheableFetchUrls = /* @__PURE__ */ new Set();
81
80
  uCtx.currentRequestTags = [];
82
81
  uCtx.dynamicFetchUrls = /* @__PURE__ */ new Set();
83
82
  }, fn);
84
83
  return fetchCacheAls.run({
85
84
  ...getFetchCacheState(),
86
- cacheableFetchUrls: /* @__PURE__ */ new Set(),
87
85
  currentRequestTags: [],
88
86
  dynamicFetchUrls: /* @__PURE__ */ new Set()
89
87
  }, fn);
@@ -11,7 +11,6 @@ declare global {
11
11
  }
12
12
  /** @internal Reset dedup state — exposed for test isolation only. */
13
13
  export declare function _resetPendingRefetches(): void;
14
- export declare function peekCacheableFetchObservations(): string[];
15
14
  export declare function peekDynamicFetchObservations(): string[];
16
15
  export declare function consumeDynamicFetchObservations(): string[];
17
16
  /**
@@ -31,6 +30,8 @@ export declare function setCurrentFetchSoftTags(tags: string[]): void;
31
30
  export declare function setCurrentFetchCacheMode(mode: FetchCacheMode | null): void;
32
31
  export declare function setCurrentFetchRevalidate(revalidate: number | null): void;
33
32
  export declare function setCurrentForceDynamicFetchDefault(enabled: boolean): void;
33
+ /** Mark the current Pages Router render as a preview/draft render. */
34
+ export declare function setCurrentFetchDraftMode(enabled: boolean): void;
34
35
  export declare function setRefreshStaleFetchesInForeground(enabled: boolean): void;
35
36
  /**
36
37
  * Install the patched fetch and reset per-request tag state.
@@ -6,8 +6,8 @@ import { sha256Hex } from "../utils/hash.js";
6
6
  import { traceAppFetch } from "../server/app-fetch-tracing.js";
7
7
  import { addCollectedRequestTags, fallbackFetchCacheState, fetchCacheAls, getCurrentFetchSoftTags, getFetchCacheState, resetFallbackFetchCacheState, runWithDetachedFetchObservations } from "./fetch-cache-state.js";
8
8
  import { isInsideAnyCacheScope, markDynamicUsage } from "./internal/headers-state.js";
9
- import { getHeadersContext } from "./headers.js";
10
- import { _hasPendingRevalidatedTag, _setRequestScopedCacheLife } from "./cache-request-state.js";
9
+ import { getHeadersContext, isDraftModeEnabled } from "./headers.js";
10
+ import { _hasPendingRevalidatedTag, _isOnDemandRevalidate, _setRequestScopedCacheLife } from "./cache-request-state.js";
11
11
  import { Buffer } from "node:buffer";
12
12
  //#region src/shims/fetch-cache.ts
13
13
  /**
@@ -463,9 +463,6 @@ function markUncachedFetchForPageOutput(input) {
463
463
  if (getHeadersContext()?.forceStatic !== true) getFetchCacheState().currentFetchRevalidate = 0;
464
464
  markDynamicUsage();
465
465
  }
466
- function recordCacheableFetchObservation(input) {
467
- getFetchCacheState().cacheableFetchUrls.add(getFetchObservationUrl(input));
468
- }
469
466
  function recordFiniteFetchRevalidate(revalidateSeconds) {
470
467
  if (Number.isFinite(revalidateSeconds) && revalidateSeconds > 0) _setRequestScopedCacheLife({ revalidate: revalidateSeconds });
471
468
  }
@@ -503,7 +500,8 @@ async function writeFetchCacheResponse(handler, cacheKey, response, tags, revali
503
500
  await handler.set(cacheKey, cacheValue, {
504
501
  fetchCache: true,
505
502
  tags,
506
- revalidate: revalidateSeconds
503
+ revalidate: revalidateSeconds,
504
+ ...options?.staleEntry ? { staleEntry: options.staleEntry } : {}
507
505
  });
508
506
  }
509
507
  async function lowerFetchCacheRevalidateIfNeeded(handler, cacheKey, cachedValue, tags, revalidateSeconds) {
@@ -520,9 +518,6 @@ async function lowerFetchCacheRevalidateIfNeeded(handler, cacheKey, cachedValue,
520
518
  revalidate: revalidateSeconds
521
519
  });
522
520
  }
523
- function peekCacheableFetchObservations() {
524
- return [...getFetchCacheState().cacheableFetchUrls].sort();
525
- }
526
521
  function peekDynamicFetchObservations() {
527
522
  return [...getFetchCacheState().dynamicFetchUrls].sort();
528
523
  }
@@ -559,6 +554,10 @@ function setCurrentFetchRevalidate(revalidate) {
559
554
  function setCurrentForceDynamicFetchDefault(enabled) {
560
555
  getFetchCacheState().currentForceDynamicFetchDefault = enabled;
561
556
  }
557
+ /** Mark the current Pages Router render as a preview/draft render. */
558
+ function setCurrentFetchDraftMode(enabled) {
559
+ getFetchCacheState().isDraftMode = enabled;
560
+ }
562
561
  function setRefreshStaleFetchesInForeground(enabled) {
563
562
  getFetchCacheState().refreshStaleFetchesInForeground = enabled;
564
563
  }
@@ -705,8 +704,12 @@ function dedupeFetch(input, init) {
705
704
  */
706
705
  function createPatchedFetch() {
707
706
  const patchedFetch = async function patchedFetch(input, init, span) {
708
- const nextOpts = init?.next;
709
707
  const state = getFetchCacheState();
708
+ if (state.isDraftMode || isDraftModeEnabled()) {
709
+ recordDynamicFetchObservation(input);
710
+ return dedupeFetch(input, init);
711
+ }
712
+ const nextOpts = init?.next;
710
713
  const fetchIdx = isInsideUnifiedScope() || fetchCacheAls.getStore() !== void 0 || fallbackFetchCacheState.isFetchDedupeActive ? (state.nextFetchId ?? 1) + 1 : void 0;
711
714
  if (fetchIdx !== void 0) state.nextFetchId = fetchIdx;
712
715
  const recordFetchOutcome = (response, cacheStatus, cacheReason) => {
@@ -753,7 +756,6 @@ function createPatchedFetch() {
753
756
  return recordFetchOutcome(await dedupeFetch(input, cleanInit), "skip", "auto no cache");
754
757
  }
755
758
  const cacheReason = fetchCacheMode === "force-cache" || fetchCacheMode === "default-cache" ? `fetchCache = ${fetchCacheMode}` : `revalidate: ${nextOpts?.revalidate === false || cacheDirective === "force-cache" ? 4294967294 : revalidateSeconds}`;
756
- recordCacheableFetchObservation(input);
757
759
  if (typeof nextOpts?.revalidate === "number" && nextOpts.revalidate > 0) {
758
760
  lowerCurrentFetchRevalidate(nextOpts.revalidate);
759
761
  recordFiniteFetchRevalidate(nextOpts.revalidate);
@@ -785,8 +787,9 @@ function createPatchedFetch() {
785
787
  }
786
788
  const handler = getDataCacheHandler();
787
789
  let mustBypassPendingRevalidation = _hasPendingRevalidatedTag([...tags, ...softTags]);
790
+ let staleEntry;
788
791
  try {
789
- let cached = mustBypassPendingRevalidation ? null : await handler.get(cacheKey, {
792
+ let cached = mustBypassPendingRevalidation || _isOnDemandRevalidate() ? null : await handler.get(cacheKey, {
790
793
  kind: "FETCH",
791
794
  tags,
792
795
  softTags,
@@ -806,15 +809,19 @@ function createPatchedFetch() {
806
809
  return recordFetchOutcome(buildCachedFetchResponse(cachedData, input), "hit", cacheReason);
807
810
  }
808
811
  if (cached?.value && cached.value.kind === "FETCH" && cached.cacheState === "stale") {
812
+ staleEntry = cached;
809
813
  if (shouldRefreshStaleFetchInForeground()) {
810
814
  const freshResponse = await dedupeFetch(input, fetchInit);
811
- await writeFetchCacheResponse(handler, cacheKey, freshResponse, tags, revalidateSeconds);
815
+ await writeFetchCacheResponse(handler, cacheKey, freshResponse, tags, revalidateSeconds, { staleEntry: cached });
812
816
  return recordFetchOutcome(freshResponse, "miss", cacheReason);
813
817
  }
814
818
  const staleData = cached.value.data;
815
819
  if (!pendingRefetches.has(cacheKey)) {
816
820
  const refetchPromise = originalFetch(input, fetchInit).then(async (freshResp) => {
817
- await writeFetchCacheResponse(handler, cacheKey, freshResp, tags, revalidateSeconds, { cloneForReturn: false });
821
+ await writeFetchCacheResponse(handler, cacheKey, freshResp, tags, revalidateSeconds, {
822
+ cloneForReturn: false,
823
+ staleEntry: cached
824
+ });
818
825
  }).catch((err) => {
819
826
  const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
820
827
  console.error(`[vinext] fetch cache background revalidation failed for ${url} (key=${cacheKey.slice(0, 12)}...):`, err);
@@ -835,13 +842,17 @@ function createPatchedFetch() {
835
842
  }
836
843
  const response = await (mustBypassPendingRevalidation ? originalFetch(input, fetchInit) : dedupeFetch(input, fetchInit));
837
844
  const cacheValue = await buildFetchCacheValue(response, tags, revalidateSeconds);
838
- if (cacheValue) handler.set(cacheKey, cacheValue, {
839
- fetchCache: true,
840
- tags,
841
- revalidate: revalidateSeconds
842
- }).catch((err) => {
843
- console.error("[vinext] fetch cache write error:", err);
844
- });
845
+ if (cacheValue) {
846
+ const write = handler.set(cacheKey, cacheValue, {
847
+ fetchCache: true,
848
+ tags,
849
+ revalidate: revalidateSeconds,
850
+ ...staleEntry ? { staleEntry } : {}
851
+ }).catch((err) => {
852
+ console.error("[vinext] fetch cache write error:", err);
853
+ });
854
+ getRequestExecutionContext()?.waitUntil(write);
855
+ }
845
856
  return recordFetchOutcome(response, "miss", cacheReason);
846
857
  };
847
858
  return ((input, init) => {
@@ -894,7 +905,6 @@ function withFetchCache() {
894
905
  async function runWithFetchCache(fn) {
895
906
  _ensurePatchInstalled();
896
907
  if (isInsideUnifiedScope()) return await runWithUnifiedStateMutation((uCtx) => {
897
- uCtx.cacheableFetchUrls = /* @__PURE__ */ new Set();
898
908
  uCtx.currentRequestTags = [];
899
909
  uCtx.currentFetchSoftTags = [];
900
910
  uCtx.dynamicFetchUrls = /* @__PURE__ */ new Set();
@@ -904,12 +914,12 @@ async function runWithFetchCache(fn) {
904
914
  uCtx.nextFetchId = 1;
905
915
  }, fn);
906
916
  return fetchCacheAls.run({
907
- cacheableFetchUrls: /* @__PURE__ */ new Set(),
908
917
  currentRequestTags: [],
909
918
  currentFetchSoftTags: [],
910
919
  currentFetchCacheMode: null,
911
920
  currentFetchRevalidate: null,
912
921
  currentForceDynamicFetchDefault: false,
922
+ isDraftMode: false,
913
923
  dynamicFetchUrls: /* @__PURE__ */ new Set(),
914
924
  refreshStaleFetchesInForeground: false,
915
925
  isFetchDedupeActive: true,
@@ -950,4 +960,4 @@ function getOriginalFetch() {
950
960
  return originalFetch;
951
961
  }
952
962
  //#endregion
953
- export { _resetPendingRefetches, addCollectedRequestTags, consumeDynamicFetchObservations, ensureFetchPatch, getCollectedFetchTags, getCurrentFetchSoftTags, getOriginalFetch, peekCacheableFetchObservations, peekDynamicFetchObservations, runWithDetachedFetchObservations, runWithFetchCache, runWithFetchDedupe, setCurrentFetchCacheMode, setCurrentFetchRevalidate, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault, setRefreshStaleFetchesInForeground, withFetchCache };
963
+ export { _resetPendingRefetches, addCollectedRequestTags, consumeDynamicFetchObservations, ensureFetchPatch, getCollectedFetchTags, getCurrentFetchSoftTags, getOriginalFetch, peekDynamicFetchObservations, runWithDetachedFetchObservations, runWithFetchCache, runWithFetchDedupe, setCurrentFetchCacheMode, setCurrentFetchDraftMode, setCurrentFetchRevalidate, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault, setRefreshStaleFetchesInForeground, withFetchCache };
@@ -5,10 +5,10 @@ import "../server/headers.js";
5
5
  import { assertSafeNavigationUrl } from "./url-safety.js";
6
6
  import { APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL } from "../server/app-rsc-render-mode.js";
7
7
  import { withBasePath } from "./url-utils.js";
8
- import { AppElementsWire } from "../server/app-elements-wire.js";
9
- import "../server/app-elements.js";
10
8
  import { createRscRequestHeaders, createRscRequestUrl } from "../server/app-rsc-cache-busting.js";
11
9
  import { hasAppNavigationRuntime, loadNavigationRuntimeRouteManifest } from "../client/navigation-runtime.js";
10
+ import { AppElementsWire } from "../server/app-elements-wire.js";
11
+ import "../server/app-elements.js";
12
12
  import { getMountedSlotsHeader, getPrefetchInterceptionContext, getPrefetchedUrls, hasPrefetchCacheEntryForNavigation, navigateClientSide, prefetchRscResponse } from "./navigation.js";
13
13
  import { useMergedRef } from "./use-merged-ref.js";
14
14
  import { forwardRef, useActionState, useCallback, useEffect, useRef } from "react";