vinext 0.1.8 → 0.2.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 (285) hide show
  1. package/README.md +51 -30
  2. package/dist/build/css-url-assets.d.ts +3 -1
  3. package/dist/build/css-url-assets.js +18 -1
  4. package/dist/build/inject-pregenerated-paths.d.ts +7 -0
  5. package/dist/build/inject-pregenerated-paths.js +21 -0
  6. package/dist/build/layout-classification.js +1 -1
  7. package/dist/build/nitro-route-rules.js +1 -1
  8. package/dist/build/pages-client-assets-module.d.ts +11 -0
  9. package/dist/build/pages-client-assets-module.js +27 -0
  10. package/dist/build/prerender-server-entry.d.ts +1 -0
  11. package/dist/build/prerender-server-entry.js +49 -0
  12. package/dist/build/prerender-server-pool.d.ts +44 -0
  13. package/dist/build/prerender-server-pool.js +194 -0
  14. package/dist/build/prerender.d.ts +2 -1
  15. package/dist/build/prerender.js +98 -27
  16. package/dist/build/report.d.ts +13 -2
  17. package/dist/build/report.js +99 -2
  18. package/dist/build/run-prerender.d.ts +7 -0
  19. package/dist/build/run-prerender.js +12 -2
  20. package/dist/build/standalone.js +2 -0
  21. package/dist/cache/cache-adapters-virtual.js +3 -3
  22. package/dist/check.d.ts +4 -6
  23. package/dist/check.js +48 -11
  24. package/dist/cli-args.d.ts +1 -2
  25. package/dist/cli-args.js +1 -1
  26. package/dist/cli-dev-config.d.ts +12 -0
  27. package/dist/cli-dev-config.js +23 -0
  28. package/dist/cli.js +112 -96
  29. package/dist/{server → client}/dev-error-overlay-store.d.ts +1 -1
  30. package/dist/{server → client}/dev-error-overlay-store.js +1 -1
  31. package/dist/{server → client}/dev-error-overlay.d.ts +1 -1
  32. package/dist/{server → client}/dev-error-overlay.js +2 -2
  33. package/dist/client/navigation-runtime.d.ts +2 -0
  34. package/dist/client/navigation-runtime.js +3 -1
  35. package/dist/client/vinext-next-data.d.ts +4 -1
  36. package/dist/client/window-next.d.ts +2 -1
  37. package/dist/cloudflare/index.d.ts +2 -2
  38. package/dist/cloudflare/index.js +2 -2
  39. package/dist/config/config-matchers.js +45 -19
  40. package/dist/config/next-config.d.ts +41 -9
  41. package/dist/config/next-config.js +60 -3
  42. package/dist/config/prerender.d.ts +34 -0
  43. package/dist/config/prerender.js +55 -0
  44. package/dist/entries/app-browser-entry.js +5 -1
  45. package/dist/entries/app-rsc-entry.d.ts +5 -3
  46. package/dist/entries/app-rsc-entry.js +44 -10
  47. package/dist/entries/app-rsc-manifest.js +2 -0
  48. package/dist/entries/pages-client-entry.d.ts +3 -0
  49. package/dist/entries/pages-client-entry.js +28 -3
  50. package/dist/entries/pages-server-entry.js +23 -6
  51. package/dist/image/image-adapters-virtual.d.ts +59 -0
  52. package/dist/image/image-adapters-virtual.js +59 -0
  53. package/dist/index.d.ts +33 -0
  54. package/dist/index.js +482 -120
  55. package/dist/init-cloudflare.d.ts +43 -0
  56. package/dist/init-cloudflare.js +839 -0
  57. package/dist/init-platform.d.ts +40 -0
  58. package/dist/init-platform.js +195 -0
  59. package/dist/init.d.ts +16 -38
  60. package/dist/init.js +209 -99
  61. package/dist/node_modules/.pnpm/am-i-vibing@0.5.0/node_modules/am-i-vibing/dist/detector-1yx2Hoe0.js +294 -0
  62. package/dist/node_modules/.pnpm/process-ancestry@0.1.0/node_modules/process-ancestry/dist/index.js +94 -0
  63. package/dist/{cloudflare → packages/cloudflare}/src/cache/cdn-adapter.runtime.js +1 -1
  64. package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.d.ts +2 -2
  65. package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.js +9 -2
  66. package/dist/packages/cloudflare/src/deploy-config.js +150 -0
  67. package/dist/packages/cloudflare/src/deploy-help.js +55 -0
  68. package/dist/packages/cloudflare/src/deploy.js +272 -0
  69. package/dist/{cloudflare → packages/cloudflare/src}/tpr.d.ts +4 -41
  70. package/dist/{cloudflare → packages/cloudflare/src}/tpr.js +44 -20
  71. package/dist/plugins/ast-scope.d.ts +16 -0
  72. package/dist/plugins/ast-scope.js +62 -0
  73. package/dist/plugins/ast-utils.d.ts +27 -1
  74. package/dist/plugins/ast-utils.js +32 -1
  75. package/dist/plugins/css-module-imports.d.ts +14 -0
  76. package/dist/plugins/css-module-imports.js +59 -0
  77. package/dist/plugins/extensionless-dynamic-import.js +2 -2
  78. package/dist/plugins/fonts.js +1 -1
  79. package/dist/plugins/ignore-dynamic-requests.d.ts +11 -0
  80. package/dist/plugins/ignore-dynamic-requests.js +530 -0
  81. package/dist/plugins/og-assets.js +2 -1
  82. package/dist/plugins/optimize-imports.js +1 -1
  83. package/dist/plugins/rsc-reference-validation-normalizer.d.ts +12 -0
  84. package/dist/plugins/rsc-reference-validation-normalizer.js +48 -0
  85. package/dist/plugins/sass.d.ts +17 -1
  86. package/dist/plugins/sass.js +74 -1
  87. package/dist/plugins/styled-jsx.d.ts +16 -0
  88. package/dist/plugins/styled-jsx.js +149 -0
  89. package/dist/plugins/typeof-window.d.ts +1 -1
  90. package/dist/plugins/typeof-window.js +28 -56
  91. package/dist/routing/app-route-graph.d.ts +23 -2
  92. package/dist/routing/app-route-graph.js +138 -38
  93. package/dist/routing/file-matcher.d.ts +11 -1
  94. package/dist/routing/file-matcher.js +18 -3
  95. package/dist/routing/pages-router.js +2 -2
  96. package/dist/server/api-handler.js +9 -14
  97. package/dist/server/app-browser-action-result.d.ts +4 -1
  98. package/dist/server/app-browser-action-result.js +8 -1
  99. package/dist/server/app-browser-entry.js +198 -57
  100. package/dist/server/app-browser-interception-context.d.ts +1 -0
  101. package/dist/server/app-browser-interception-context.js +4 -2
  102. package/dist/server/app-browser-navigation-controller.d.ts +2 -0
  103. package/dist/server/app-browser-navigation-controller.js +60 -31
  104. package/dist/server/app-browser-server-action-client.d.ts +1 -1
  105. package/dist/server/app-browser-server-action-client.js +8 -7
  106. package/dist/server/app-browser-state.d.ts +12 -1
  107. package/dist/server/app-browser-state.js +25 -8
  108. package/dist/server/app-browser-visible-commit.d.ts +6 -1
  109. package/dist/server/app-browser-visible-commit.js +42 -24
  110. package/dist/server/app-elements-wire.d.ts +9 -2
  111. package/dist/server/app-elements-wire.js +8 -1
  112. package/dist/server/app-elements.d.ts +2 -2
  113. package/dist/server/app-elements.js +4 -3
  114. package/dist/server/app-fallback-renderer.d.ts +2 -1
  115. package/dist/server/app-fallback-renderer.js +10 -6
  116. package/dist/server/app-hydration-cache-publication.d.ts +11 -0
  117. package/dist/server/app-hydration-cache-publication.js +43 -0
  118. package/dist/server/app-middleware.d.ts +4 -3
  119. package/dist/server/app-middleware.js +8 -3
  120. package/dist/server/app-optimistic-routing.js +11 -4
  121. package/dist/server/app-page-boundary.js +2 -1
  122. package/dist/server/app-page-cache-finalizer.d.ts +1 -0
  123. package/dist/server/app-page-cache-finalizer.js +5 -4
  124. package/dist/server/app-page-cache-render.d.ts +1 -0
  125. package/dist/server/app-page-cache-render.js +8 -4
  126. package/dist/server/app-page-cache.d.ts +1 -0
  127. package/dist/server/app-page-cache.js +27 -7
  128. package/dist/server/app-page-dispatch.d.ts +1 -0
  129. package/dist/server/app-page-dispatch.js +26 -8
  130. package/dist/server/app-page-element-builder.d.ts +2 -1
  131. package/dist/server/app-page-element-builder.js +59 -4
  132. package/dist/server/app-page-execution.js +2 -1
  133. package/dist/server/app-page-probe.js +5 -0
  134. package/dist/server/app-page-render-identity.d.ts +2 -0
  135. package/dist/server/app-page-render-identity.js +3 -2
  136. package/dist/server/app-page-render.d.ts +2 -0
  137. package/dist/server/app-page-render.js +85 -16
  138. package/dist/server/app-page-request.d.ts +14 -0
  139. package/dist/server/app-page-request.js +69 -4
  140. package/dist/server/app-page-response.d.ts +7 -0
  141. package/dist/server/app-page-response.js +16 -4
  142. package/dist/server/app-page-route-wiring.d.ts +8 -1
  143. package/dist/server/app-page-route-wiring.js +32 -18
  144. package/dist/server/app-page-segment-state.d.ts +2 -1
  145. package/dist/server/app-page-segment-state.js +3 -1
  146. package/dist/server/app-page-stream.d.ts +7 -1
  147. package/dist/server/app-page-stream.js +3 -1
  148. package/dist/server/app-route-handler-execution.js +5 -1
  149. package/dist/server/app-route-handler-response.js +1 -0
  150. package/dist/server/app-route-handler-runtime.js +1 -1
  151. package/dist/server/app-route-tree-prefetch.d.ts +43 -0
  152. package/dist/server/app-route-tree-prefetch.js +187 -0
  153. package/dist/server/app-router-entry.js +9 -2
  154. package/dist/server/app-rsc-cache-busting.d.ts +2 -1
  155. package/dist/server/app-rsc-cache-busting.js +9 -4
  156. package/dist/server/app-rsc-handler.d.ts +7 -0
  157. package/dist/server/app-rsc-handler.js +148 -39
  158. package/dist/server/app-rsc-render-mode.d.ts +3 -2
  159. package/dist/server/app-rsc-render-mode.js +4 -1
  160. package/dist/server/app-rsc-request-normalization.d.ts +6 -6
  161. package/dist/server/app-rsc-request-normalization.js +10 -8
  162. package/dist/server/app-segment-config.d.ts +11 -0
  163. package/dist/server/app-segment-config.js +62 -6
  164. package/dist/server/app-server-action-execution.d.ts +1 -0
  165. package/dist/server/app-server-action-execution.js +61 -30
  166. package/dist/server/app-ssr-entry.d.ts +3 -0
  167. package/dist/server/app-ssr-entry.js +13 -5
  168. package/dist/server/app-ssr-stream.d.ts +7 -3
  169. package/dist/server/app-ssr-stream.js +10 -6
  170. package/dist/server/app-visited-response-cache.d.ts +6 -0
  171. package/dist/server/app-visited-response-cache.js +3 -1
  172. package/dist/server/cache-control.d.ts +3 -1
  173. package/dist/server/cache-control.js +13 -1
  174. package/dist/server/client-trace-metadata.js +26 -0
  175. package/dist/server/default-global-not-found-module.d.ts +14 -0
  176. package/dist/server/default-global-not-found-module.js +14 -0
  177. package/dist/server/dev-route-files.js +4 -0
  178. package/dist/server/dev-server.d.ts +11 -1
  179. package/dist/server/dev-server.js +94 -35
  180. package/dist/server/dev-stack-sourcemap.d.ts +1 -1
  181. package/dist/server/dev-stack-sourcemap.js +1 -1
  182. package/dist/server/fetch-handler.d.ts +2 -0
  183. package/dist/server/fetch-handler.js +18 -0
  184. package/dist/server/headers.d.ts +7 -15
  185. package/dist/server/headers.js +11 -18
  186. package/dist/server/image-optimization.d.ts +51 -1
  187. package/dist/server/image-optimization.js +52 -2
  188. package/dist/server/implicit-tags.js +1 -1
  189. package/dist/server/isr-cache.d.ts +3 -2
  190. package/dist/server/isr-cache.js +9 -5
  191. package/dist/server/metadata-routes.d.ts +3 -1
  192. package/dist/server/metadata-routes.js +12 -2
  193. package/dist/server/middleware-runtime.js +6 -1
  194. package/dist/server/navigation-planner.d.ts +1 -0
  195. package/dist/server/navigation-planner.js +14 -3
  196. package/dist/server/pages-api-route.js +2 -2
  197. package/dist/server/pages-asset-tags.d.ts +4 -6
  198. package/dist/server/pages-asset-tags.js +12 -12
  199. package/dist/server/pages-client-assets.d.ts +12 -0
  200. package/dist/server/pages-client-assets.js +10 -0
  201. package/dist/server/pages-data-route.d.ts +4 -2
  202. package/dist/server/pages-data-route.js +18 -4
  203. package/dist/server/pages-dev-module-url.d.ts +2 -1
  204. package/dist/server/pages-dev-module-url.js +6 -3
  205. package/dist/server/pages-node-compat.d.ts +19 -1
  206. package/dist/server/pages-node-compat.js +81 -4
  207. package/dist/server/pages-page-data.d.ts +33 -1
  208. package/dist/server/pages-page-data.js +62 -33
  209. package/dist/server/pages-page-handler.d.ts +2 -1
  210. package/dist/server/pages-page-handler.js +38 -19
  211. package/dist/server/pages-page-response.d.ts +8 -0
  212. package/dist/server/pages-page-response.js +7 -3
  213. package/dist/server/pages-request-pipeline.d.ts +9 -5
  214. package/dist/server/pages-request-pipeline.js +55 -7
  215. package/dist/server/pages-router-entry.d.ts +30 -0
  216. package/dist/server/pages-router-entry.js +108 -0
  217. package/dist/server/prerender-manifest.d.ts +3 -1
  218. package/dist/server/prerender-route-params.js +1 -1
  219. package/dist/server/prod-server.d.ts +5 -3
  220. package/dist/server/prod-server.js +68 -39
  221. package/dist/server/request-pipeline.js +2 -1
  222. package/dist/server/seed-cache.js +4 -4
  223. package/dist/shims/app-router-scroll-state.d.ts +1 -0
  224. package/dist/shims/app-router-scroll-state.js +1 -0
  225. package/dist/shims/app-router-scroll.js +2 -1
  226. package/dist/shims/cache-handler.js +8 -1
  227. package/dist/shims/cache.js +22 -15
  228. package/dist/shims/cdn-cache.js +1 -1
  229. package/dist/shims/dynamic-preload-chunks.js +2 -1
  230. package/dist/shims/fetch-cache.d.ts +3 -1
  231. package/dist/shims/fetch-cache.js +77 -52
  232. package/dist/shims/form.d.ts +4 -2
  233. package/dist/shims/form.js +37 -43
  234. package/dist/shims/headers.d.ts +7 -1
  235. package/dist/shims/headers.js +17 -3
  236. package/dist/shims/image.js +75 -14
  237. package/dist/shims/internal/app-prefetch-fetch-queue.d.ts +10 -0
  238. package/dist/shims/internal/app-prefetch-fetch-queue.js +61 -0
  239. package/dist/shims/internal/app-route-detection.d.ts +2 -17
  240. package/dist/shims/internal/app-route-detection.js +4 -17
  241. package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +23 -0
  242. package/dist/shims/internal/hybrid-client-route-owner-direct.js +51 -0
  243. package/dist/shims/internal/hybrid-client-route-owner.d.ts +3 -15
  244. package/dist/shims/internal/hybrid-client-route-owner.js +40 -58
  245. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +10 -5
  246. package/dist/shims/internal/pages-data-fetch-dedup.js +84 -14
  247. package/dist/shims/internal/pages-data-target.d.ts +18 -12
  248. package/dist/shims/internal/pages-data-target.js +97 -13
  249. package/dist/shims/internal/pages-router-components.d.ts +7 -0
  250. package/dist/shims/internal/pages-router-components.js +13 -0
  251. package/dist/shims/layout-segment-context.d.ts +4 -1
  252. package/dist/shims/layout-segment-context.js +18 -4
  253. package/dist/shims/link.d.ts +1 -1
  254. package/dist/shims/link.js +224 -58
  255. package/dist/shims/navigation.d.ts +19 -3
  256. package/dist/shims/navigation.js +245 -47
  257. package/dist/shims/request-context.js +18 -0
  258. package/dist/shims/root-params.d.ts +15 -1
  259. package/dist/shims/root-params.js +21 -1
  260. package/dist/shims/router.d.ts +5 -7
  261. package/dist/shims/router.js +387 -102
  262. package/dist/shims/server.js +3 -2
  263. package/dist/shims/slot.js +3 -6
  264. package/dist/shims/unified-request-context.js +1 -0
  265. package/dist/typegen.js +1 -1
  266. package/dist/utils/client-runtime-metadata.d.ts +2 -18
  267. package/dist/utils/client-runtime-metadata.js +31 -22
  268. package/dist/utils/dev-stack-sourcemap-endpoint.d.ts +4 -0
  269. package/dist/{server → utils}/dev-stack-sourcemap-endpoint.js +1 -1
  270. package/dist/utils/domain-locale.d.ts +6 -3
  271. package/dist/{server → utils}/middleware-request-headers.d.ts +1 -1
  272. package/dist/{server → utils}/middleware-request-headers.js +3 -3
  273. package/dist/utils/project.d.ts +48 -2
  274. package/dist/utils/project.js +225 -4
  275. package/dist/utils/protocol-headers.d.ts +23 -0
  276. package/dist/utils/protocol-headers.js +23 -0
  277. package/dist/utils/react-version.d.ts +4 -0
  278. package/dist/utils/react-version.js +44 -0
  279. package/dist/utils/vite-version.d.ts +2 -0
  280. package/dist/utils/vite-version.js +11 -1
  281. package/package.json +47 -2
  282. package/dist/deploy.d.ts +0 -197
  283. package/dist/deploy.js +0 -1040
  284. package/dist/server/dev-stack-sourcemap-endpoint.d.ts +0 -4
  285. /package/dist/{cloudflare → packages/cloudflare}/src/utils/cache-control-metadata.js +0 -0
@@ -1,18 +1,18 @@
1
1
  import { stripBasePath } from "../utils/base-path.js";
2
- import { VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_PARAMS_HEADER } from "../server/headers.js";
2
+ import { VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_PARAMS_HEADER, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER } from "../server/headers.js";
3
3
  import { assertSafeNavigationUrl } from "./url-safety.js";
4
4
  import { AppElementsWire } from "../server/app-elements-wire.js";
5
5
  import "../server/app-elements.js";
6
6
  import { markPprFallbackShellDynamicBoundary } from "./ppr-fallback-shell.js";
7
7
  import { AppRouterContext } from "./internal/app-router-context.js";
8
- import { resolveHybridClientRouteOwner } from "./internal/hybrid-client-route-owner.js";
9
8
  import { isAbsoluteOrProtocolRelativeUrl, toBrowserNavigationHref, toSameOriginAppPath, withBasePath } from "./url-utils.js";
10
9
  import { retryScrollTo, scrollToHashTarget } from "./hash-scroll.js";
11
10
  import { GLOBAL_ACCESSORS_KEY, ServerInsertedHTMLContext, _registerStateAccessors, clearClientHydrationContext, clearServerInsertedHTML, flushServerInsertedHTML, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getLayoutSegmentContext, getNavigationContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "./navigation-context-state.js";
12
11
  import { BailoutToCSRError, DynamicServerError, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, decodeRedirectError, forbidden, getAccessFallbackHTTPStatus, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, unauthorized, unstable_rethrow } from "./navigation-errors.js";
12
+ import { isBotUserAgent } from "../utils/html-limited-bots.js";
13
13
  import { clearAppNavigationFailureTarget, stageAppNavigationFailureTarget } from "../client/app-nav-failure-handler.js";
14
14
  import { beginAppRouterScrollIntent, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent } from "./app-router-scroll-state.js";
15
- import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, createRscRequestHeaders, createRscRequestUrl, stripRscCacheBustingSearchParam } from "../server/app-rsc-cache-busting.js";
15
+ import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, createRscRequestHeaders, createRscRequestUrl, stripRscCacheBustingSearchParam, stripRscSuffix } from "../server/app-rsc-cache-busting.js";
16
16
  import { getNavigationRuntime, hasAppNavigationRuntime } from "../client/navigation-runtime.js";
17
17
  import { notifyAppRouterTransitionStart } from "../client/instrumentation-client-state.js";
18
18
  import { PUBLIC_INITIAL_BFCACHE_ID } from "../server/app-bfcache-id.js";
@@ -22,6 +22,8 @@ import { hasPendingAppRouterPageRedirect } from "../server/app-browser-mpa-navig
22
22
  import { navigationPlanner } from "../server/navigation-planner.js";
23
23
  import { ReadonlyURLSearchParams } from "./readonly-url-search-params.js";
24
24
  import { getPagesNavigationContext } from "./internal/pages-router-accessor.js";
25
+ import { resolveHybridClientRouteOwner } from "./internal/hybrid-client-route-owner.js";
26
+ import { releaseAppPrefetchFetchSlot, scheduleAppPrefetchFetch } from "./internal/app-prefetch-fetch-queue.js";
25
27
  import { UnrecognizedActionError, unstable_isUnrecognizedActionError } from "./unrecognized-action-error.js";
26
28
  import * as React$1 from "react";
27
29
  //#region src/shims/navigation.ts
@@ -41,7 +43,7 @@ function useChildSegments(parallelRoutesKey = "children") {
41
43
  const ctx = getLayoutSegmentContext();
42
44
  if (!ctx) return [];
43
45
  try {
44
- return React$1.useContext(ctx)[parallelRoutesKey] ?? [];
46
+ return (React$1.useContext(ctx)[parallelRoutesKey] ?? []).filter((segment) => !segment.startsWith("__PAGE__"));
45
47
  } catch {
46
48
  return [];
47
49
  }
@@ -53,8 +55,9 @@ const PAGES_NAVIGATION_NOTIFY_KEY = Symbol.for("vinext.navigation.pagesNavigatio
53
55
  const isServer = typeof window === "undefined";
54
56
  /** basePath from next.config.js, injected by the plugin at build time */
55
57
  const __basePath = process.env.__NEXT_ROUTER_BASEPATH ?? "";
56
- /** Maximum number of entries in the RSC prefetch cache. */
57
- const MAX_PREFETCH_CACHE_SIZE = 50;
58
+ /** Maximum buffered bytes in the RSC prefetch cache. Mirrors Next.js' 50 MB LRU. */
59
+ const MAX_PREFETCH_CACHE_SIZE = 50 * 1024 * 1024;
60
+ const PREFETCH_CACHE_EVICTION_TARGET_SIZE = MAX_PREFETCH_CACHE_SIZE * .9;
58
61
  /**
59
62
  * TTL for prefetch cache entries in ms.
60
63
  *
@@ -69,14 +72,15 @@ const MAX_PREFETCH_CACHE_SIZE = 50;
69
72
  * has not set `experimental.staleTimes`, Next.js' 300s default applies
70
73
  * (see `resolveStaleTimes` in `config/next-config.ts`).
71
74
  */
72
- function resolvePrefetchCacheTtl() {
73
- const raw = process.env.__NEXT_CLIENT_ROUTER_STATIC_STALETIME;
74
- if (raw === void 0 || raw === "") return 3e4;
75
+ function resolveClientRouterStaleTime(raw, fallbackMs) {
76
+ if (raw === void 0 || raw === "") return fallbackMs;
75
77
  const seconds = Number(raw);
76
- if (!Number.isFinite(seconds) || seconds < 0) return 3e4;
78
+ if (!Number.isFinite(seconds) || seconds < 0) return fallbackMs;
77
79
  return seconds * 1e3;
78
80
  }
79
- const PREFETCH_CACHE_TTL = resolvePrefetchCacheTtl();
81
+ const DYNAMIC_NAVIGATION_CACHE_TTL = resolveClientRouterStaleTime(process.env.__NEXT_CLIENT_ROUTER_DYNAMIC_STALETIME, 3e4);
82
+ const PREFETCH_CACHE_TTL = resolveClientRouterStaleTime(process.env.__NEXT_CLIENT_ROUTER_STATIC_STALETIME, 3e4);
83
+ const MIN_PREFETCH_STALE_TIME_MS = 3e4;
80
84
  function getCurrentInterceptionContext() {
81
85
  if (isServer) return null;
82
86
  return stripBasePath(window.location.pathname, __basePath);
@@ -135,6 +139,12 @@ function resolveCachedRscResponseExpiresAt(timestamp, cached, fallbackTtlMs) {
135
139
  if (isCacheExpiresAt(cached.expiresAt)) return cached.expiresAt;
136
140
  return timestamp + resolveCachedRscResponseTtlMs(cached, fallbackTtlMs);
137
141
  }
142
+ function resolvePrefetchedRscResponseExpiresAt(timestamp, cached, fallbackTtlMs) {
143
+ if (isCacheExpiresAt(cached.expiresAt)) return cached.expiresAt;
144
+ const seconds = cached.dynamicStaleTimeSeconds;
145
+ if (!isDynamicStaleTimeSeconds(seconds)) return timestamp + Math.max(fallbackTtlMs, MIN_PREFETCH_STALE_TIME_MS);
146
+ return timestamp + Math.max(seconds * 1e3, MIN_PREFETCH_STALE_TIME_MS);
147
+ }
138
148
  function resolvePrefetchCacheEntryExpiresAt(entry) {
139
149
  if (entry.expiresAt !== void 0) return entry.expiresAt;
140
150
  if (entry.snapshot) return resolveCachedRscResponseExpiresAt(entry.timestamp, entry.snapshot, PREFETCH_CACHE_TTL);
@@ -153,6 +163,13 @@ function normalizeRscCacheLookupUrl(rscUrl) {
153
163
  return null;
154
164
  }
155
165
  }
166
+ function normalizeRscCacheLookupPathname(rscUrl) {
167
+ try {
168
+ return stripRscSuffix(new URL(rscUrl, "http://vinext.local").pathname);
169
+ } catch {
170
+ return null;
171
+ }
172
+ }
156
173
  function parsePrefetchCacheKey(cacheKey) {
157
174
  const separatorIndex = cacheKey.indexOf("\0");
158
175
  if (separatorIndex === -1) return {
@@ -168,22 +185,25 @@ function isPrefetchCacheEntryCompatibleWithMountedSlots(entry, mountedSlotsHeade
168
185
  if (resolvePrefetchCacheEntryMountedSlotsHeader(entry) === mountedSlotsHeader) return true;
169
186
  return (entry.snapshot?.mountedSlotsHeader ?? null) === mountedSlotsHeader;
170
187
  }
171
- function findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader) {
172
- const exactCacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
188
+ function findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader, additionalRscUrls = []) {
173
189
  const cache = getPrefetchCache();
174
- const exactEntry = cache.get(exactCacheKey);
175
- if (exactEntry && exactEntry.cacheForNavigation !== false && isPrefetchCacheEntryCompatibleWithMountedSlots(exactEntry, mountedSlotsHeader)) return {
176
- cacheKey: exactCacheKey,
177
- entry: exactEntry
178
- };
179
- const normalizedTarget = normalizeRscCacheLookupUrl(rscUrl);
180
- if (normalizedTarget === null) return null;
190
+ const rscUrls = [rscUrl, ...additionalRscUrls];
191
+ for (const lookupRscUrl of rscUrls) {
192
+ const exactCacheKey = AppElementsWire.encodeCacheKey(lookupRscUrl, interceptionContext);
193
+ const exactEntry = cache.get(exactCacheKey);
194
+ if (exactEntry && exactEntry.cacheForNavigation !== false && isPrefetchCacheEntryCompatibleWithMountedSlots(exactEntry, mountedSlotsHeader)) return {
195
+ cacheKey: exactCacheKey,
196
+ entry: exactEntry
197
+ };
198
+ }
199
+ const normalizedTargets = new Set(rscUrls.map((lookupRscUrl) => normalizeRscCacheLookupUrl(lookupRscUrl)).filter((lookupRscUrl) => lookupRscUrl !== null));
200
+ if (normalizedTargets.size === 0) return null;
181
201
  for (const [cacheKey, entry] of cache) {
182
- if (cacheKey === exactCacheKey) continue;
183
202
  if (entry.cacheForNavigation === false) continue;
184
203
  const source = parsePrefetchCacheKey(cacheKey);
185
204
  if (source.interceptionContext !== interceptionContext) continue;
186
- if (normalizeRscCacheLookupUrl(source.rscUrl) !== normalizedTarget) continue;
205
+ const normalizedSource = normalizeRscCacheLookupUrl(source.rscUrl);
206
+ if (normalizedSource === null || !normalizedTargets.has(normalizedSource)) continue;
187
207
  if (!isPrefetchCacheEntryCompatibleWithMountedSlots(entry, mountedSlotsHeader)) continue;
188
208
  return {
189
209
  cacheKey,
@@ -193,31 +213,100 @@ function findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mounte
193
213
  return null;
194
214
  }
195
215
  function hasPrefetchCacheEntryForNavigation(rscUrl, interceptionContext = null, mountedSlotsHeader = null, options = {}) {
196
- const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader);
216
+ const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader, options.additionalRscUrls);
197
217
  if (match === null) return false;
198
- if (match.entry.pending !== void 0) return true;
199
- if (resolvePrefetchCacheEntryExpiresAt(match.entry) > Date.now()) return true;
218
+ if (match.entry.pending !== void 0) {
219
+ touchPrefetchCacheEntry(getPrefetchCache(), match.cacheKey, match.entry);
220
+ return true;
221
+ }
222
+ if (resolvePrefetchCacheEntryExpiresAt(match.entry) > Date.now()) {
223
+ touchPrefetchCacheEntry(getPrefetchCache(), match.cacheKey, match.entry);
224
+ return true;
225
+ }
200
226
  deletePrefetchCacheEntry(getPrefetchCache(), getPrefetchedUrls(), match.cacheKey, match.entry, options.notifyInvalidation ?? true);
201
227
  return false;
202
228
  }
229
+ function hasSearchAgnosticPrefetchShellForRoute(rscUrl, interceptionContext = null, mountedSlotsHeader = null) {
230
+ const normalizedTargetPathname = normalizeRscCacheLookupPathname(rscUrl);
231
+ if (normalizedTargetPathname === null) return false;
232
+ const cache = getPrefetchCache();
233
+ for (const [cacheKey, entry] of cache) {
234
+ if (entry.searchAgnosticShell !== true) continue;
235
+ const source = parsePrefetchCacheKey(cacheKey);
236
+ if (source.interceptionContext !== interceptionContext) continue;
237
+ if (normalizeRscCacheLookupPathname(source.rscUrl) !== normalizedTargetPathname) continue;
238
+ if (!isPrefetchCacheEntryCompatibleWithMountedSlots(entry, mountedSlotsHeader)) continue;
239
+ if (entry.pending !== void 0) return true;
240
+ if (resolvePrefetchCacheEntryExpiresAt(entry) > Date.now()) return true;
241
+ deletePrefetchCacheEntry(cache, getPrefetchedUrls(), cacheKey, entry, true);
242
+ }
243
+ return false;
244
+ }
245
+ function getPrefetchCacheEntrySize(entry) {
246
+ return entry.snapshot?.buffer.byteLength ?? entry.size ?? 0;
247
+ }
248
+ let trackedPrefetchCache = null;
249
+ let trackedPrefetchCacheByteSize = 0;
250
+ function getPrefetchCacheByteSize(cache) {
251
+ if (trackedPrefetchCache === cache) return trackedPrefetchCacheByteSize;
252
+ let total = 0;
253
+ const seen = /* @__PURE__ */ new Set();
254
+ for (const entry of cache.values()) {
255
+ if (seen.has(entry)) continue;
256
+ seen.add(entry);
257
+ total += getPrefetchCacheEntrySize(entry);
258
+ }
259
+ trackedPrefetchCache = cache;
260
+ trackedPrefetchCacheByteSize = total;
261
+ return total;
262
+ }
263
+ function adjustPrefetchCacheByteSize(cache, delta) {
264
+ if (trackedPrefetchCache !== cache) return;
265
+ trackedPrefetchCacheByteSize = Math.max(0, trackedPrefetchCacheByteSize + delta);
266
+ }
267
+ function touchPrefetchCacheEntry(cache, cacheKey, entry) {
268
+ if (cache.get(cacheKey) !== entry) return;
269
+ cache.delete(cacheKey);
270
+ cache.set(cacheKey, entry);
271
+ for (const key of entry.cacheKeys ?? []) {
272
+ if (key === cacheKey || cache.get(key) !== entry) continue;
273
+ cache.delete(key);
274
+ cache.set(key, entry);
275
+ }
276
+ }
203
277
  /**
204
- * Evict prefetch cache entries if at capacity.
205
- * First sweeps expired entries, then falls back to FIFO eviction.
278
+ * Evict prefetch cache entries if buffered payloads exceed the byte budget.
279
+ * Sweeps expired entries only after the cheap byte-budget check says cleanup is
280
+ * needed, then evicts least-recently-used entries down to the target size.
206
281
  */
207
282
  function evictPrefetchCacheIfNeeded() {
208
283
  const cache = getPrefetchCache();
209
- if (cache.size < 50) return;
284
+ let totalSize = getPrefetchCacheByteSize(cache);
285
+ if (totalSize <= 52428800) return;
210
286
  const now = Date.now();
211
287
  const prefetched = getPrefetchedUrls();
212
288
  for (const [key, entry] of cache) if (resolvePrefetchCacheEntryExpiresAt(entry) <= now) deletePrefetchCacheEntry(cache, prefetched, key, entry, true);
213
- while (cache.size >= 50) {
289
+ totalSize = getPrefetchCacheByteSize(cache);
290
+ if (totalSize <= 52428800) return;
291
+ let inspectedEntries = 0;
292
+ while (totalSize > PREFETCH_CACHE_EVICTION_TARGET_SIZE && inspectedEntries < cache.size) {
214
293
  const oldest = cache.keys().next().value;
215
294
  if (oldest !== void 0) {
216
295
  const entry = cache.get(oldest);
217
- if (entry) deletePrefetchCacheEntry(cache, prefetched, oldest, entry, true);
218
- else {
296
+ if (entry) {
297
+ const entrySize = getPrefetchCacheEntrySize(entry);
298
+ if (entry.pending !== void 0 && entrySize === 0) {
299
+ touchPrefetchCacheEntry(cache, oldest, entry);
300
+ inspectedEntries += 1;
301
+ continue;
302
+ }
303
+ totalSize -= entrySize;
304
+ deletePrefetchCacheEntry(cache, prefetched, oldest, entry, true);
305
+ inspectedEntries = 0;
306
+ } else {
219
307
  cache.delete(oldest);
220
308
  prefetched.delete(oldest);
309
+ inspectedEntries += 1;
221
310
  }
222
311
  } else break;
223
312
  }
@@ -241,8 +330,13 @@ function notifyPrefetchInvalidated(entry) {
241
330
  }
242
331
  }
243
332
  function deletePrefetchCacheEntry(cache, prefetched, cacheKey, entry, notify) {
244
- cache.delete(cacheKey);
245
- prefetched.delete(cacheKey);
333
+ adjustPrefetchCacheByteSize(cache, -getPrefetchCacheEntrySize(entry));
334
+ const cacheKeys = entry.cacheKeys ?? new Set([cacheKey]);
335
+ for (const key of cacheKeys) {
336
+ if (cache.get(key) === entry) cache.delete(key);
337
+ prefetched.delete(key);
338
+ }
339
+ entry.cacheKeys = void 0;
246
340
  if (notify) notifyPrefetchInvalidated(entry);
247
341
  else {
248
342
  clearPrefetchInvalidation(entry);
@@ -282,6 +376,35 @@ function invalidatePrefetchCache() {
282
376
  prefetched.clear();
283
377
  if (!isServer) getNavigationRuntime()?.functions.pingVisibleLinks?.();
284
378
  }
379
+ function seedPrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext = null, mountedSlotsHeader = null, fallbackTtlMs = DYNAMIC_NAVIGATION_CACHE_TTL) {
380
+ const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
381
+ const cache = getPrefetchCache();
382
+ const existing = cache.get(cacheKey);
383
+ if (existing) deletePrefetchCacheEntry(cache, getPrefetchedUrls(), cacheKey, existing, false);
384
+ const timestamp = Date.now();
385
+ const entry = {
386
+ cacheForNavigation: true,
387
+ cacheKeys: new Set([cacheKey]),
388
+ expiresAt: resolveCachedRscResponseExpiresAt(timestamp, snapshot, fallbackTtlMs),
389
+ mountedSlotsHeader,
390
+ outcome: "cache-seeded",
391
+ size: snapshot.buffer.byteLength,
392
+ snapshot,
393
+ timestamp
394
+ };
395
+ cache.set(cacheKey, entry);
396
+ adjustPrefetchCacheByteSize(cache, snapshot.buffer.byteLength);
397
+ getPrefetchedUrls().add(cacheKey);
398
+ schedulePrefetchInvalidation(cacheKey, entry);
399
+ evictPrefetchCacheIfNeeded();
400
+ }
401
+ function deletePrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext = null) {
402
+ const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
403
+ const cache = getPrefetchCache();
404
+ const entry = cache.get(cacheKey);
405
+ if (entry?.snapshot !== snapshot) return;
406
+ deletePrefetchCacheEntry(cache, getPrefetchedUrls(), cacheKey, entry, false);
407
+ }
285
408
  /**
286
409
  * Store a prefetched RSC response in the cache by snapshotting it to an
287
410
  * ArrayBuffer. The snapshot completes asynchronously; during that window
@@ -300,27 +423,37 @@ function invalidatePrefetchCache() {
300
423
  */
301
424
  function storePrefetchResponse(rscUrl, response, interceptionContext = null, options) {
302
425
  const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
303
- evictPrefetchCacheIfNeeded();
426
+ const cache = getPrefetchCache();
427
+ const prefetched = getPrefetchedUrls();
428
+ const existing = cache.get(cacheKey);
429
+ if (existing) deletePrefetchCacheEntry(cache, prefetched, cacheKey, existing, false);
304
430
  const entry = {
431
+ cacheKeys: new Set([cacheKey]),
305
432
  mountedSlotsHeader: null,
306
433
  outcome: "pending",
307
434
  timestamp: Date.now()
308
435
  };
309
436
  addPrefetchInvalidationCallback(entry, options?.onInvalidate);
310
437
  entry.pending = snapshotRscResponse(response).then((snapshot) => {
438
+ if (cache.get(cacheKey) !== entry) return;
439
+ const previousSize = getPrefetchCacheEntrySize(entry);
311
440
  entry.mountedSlotsHeader = snapshot.mountedSlotsHeader ?? null;
312
441
  entry.snapshot = snapshot;
442
+ entry.size = snapshot.buffer.byteLength;
443
+ adjustPrefetchCacheByteSize(cache, entry.size - previousSize);
313
444
  entry.expiresAt = resolveCachedRscResponseExpiresAt(entry.timestamp, snapshot, PREFETCH_CACHE_TTL);
445
+ evictPrefetchCacheIfNeeded();
314
446
  }).catch(() => {
315
- deletePrefetchCacheEntry(getPrefetchCache(), getPrefetchedUrls(), cacheKey, entry, false);
447
+ deletePrefetchCacheEntry(cache, prefetched, cacheKey, entry, false);
316
448
  }).finally(() => {
449
+ if (cache.get(cacheKey) !== entry) return;
317
450
  entry.pending = void 0;
318
451
  if (entry.snapshot) {
319
452
  entry.outcome = "cache-seeded";
320
453
  schedulePrefetchInvalidation(cacheKey, entry);
321
454
  }
322
455
  });
323
- getPrefetchCache().set(cacheKey, entry);
456
+ cache.set(cacheKey, entry);
324
457
  }
325
458
  function createCachedRscResponseSnapshot(response, buffer, responseUrl = null) {
326
459
  const dynamicStaleTimeSeconds = parseDynamicStaleTimeSeconds(response.headers.get(VINEXT_DYNAMIC_STALE_TIME_HEADER));
@@ -331,15 +464,29 @@ function createCachedRscResponseSnapshot(response, buffer, responseUrl = null) {
331
464
  ...dynamicStaleTimeSeconds !== void 0 ? { dynamicStaleTimeSeconds } : {},
332
465
  mountedSlotsHeader: response.headers.get(VINEXT_MOUNTED_SLOTS_HEADER),
333
466
  paramsHeader: response.headers.get(VINEXT_PARAMS_HEADER),
467
+ renderedPathAndSearch: parseRenderedPathAndSearchHeader(response.headers.get(VINEXT_RENDERED_PATH_AND_SEARCH_HEADER)),
334
468
  url: responseUrl ?? response.url
335
469
  };
336
470
  }
471
+ function parseRenderedPathAndSearchHeader(value) {
472
+ if (value === null || value === "") return null;
473
+ try {
474
+ const decoded = decodeURIComponent(value);
475
+ return decoded.startsWith("/") ? decoded : null;
476
+ } catch {
477
+ return null;
478
+ }
479
+ }
337
480
  /**
338
481
  * Snapshot an RSC response to an ArrayBuffer for caching and replay.
339
482
  * Consumes the response body and stores it with content-type and URL metadata.
340
483
  */
341
484
  async function snapshotRscResponse(response) {
342
- return createCachedRscResponseSnapshot(response, await response.arrayBuffer());
485
+ try {
486
+ return createCachedRscResponseSnapshot(response, await response.arrayBuffer());
487
+ } finally {
488
+ releaseAppPrefetchFetchSlot(response);
489
+ }
343
490
  }
344
491
  /**
345
492
  * Reconstruct a Response from a cached RSC snapshot.
@@ -362,6 +509,7 @@ function restoreRscResponse(cached, copy = true) {
362
509
  if (cached.compatibilityIdHeader != null) headers.set(VINEXT_RSC_COMPATIBILITY_ID_HEADER, cached.compatibilityIdHeader);
363
510
  if (isDynamicStaleTimeSeconds(cached.dynamicStaleTimeSeconds)) headers.set(VINEXT_DYNAMIC_STALE_TIME_HEADER, String(cached.dynamicStaleTimeSeconds));
364
511
  if (cached.paramsHeader != null) headers.set(VINEXT_PARAMS_HEADER, cached.paramsHeader);
512
+ if (cached.renderedPathAndSearch != null) headers.set(VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, encodeURIComponent(cached.renderedPathAndSearch));
365
513
  return new Response(copy ? cached.buffer.slice(0) : cached.buffer, {
366
514
  status: 200,
367
515
  headers
@@ -379,22 +527,38 @@ function prefetchRscResponse(rscUrl, fetchPromise, interceptionContext = null, m
379
527
  const cache = getPrefetchCache();
380
528
  const prefetched = getPrefetchedUrls();
381
529
  const now = Date.now();
530
+ const existing = cache.get(cacheKey);
531
+ if (existing) deletePrefetchCacheEntry(cache, prefetched, cacheKey, existing, false);
382
532
  const entry = {
383
533
  cacheForNavigation: behavior.cacheForNavigation ?? true,
534
+ cacheKeys: new Set([cacheKey]),
384
535
  mountedSlotsHeader,
385
536
  optimisticRouteShell: behavior.optimisticRouteShell === true,
386
537
  outcome: "pending",
538
+ prefetchKind: behavior.prefetchKind ?? (behavior.optimisticRouteShell === true ? "loading-shell" : "navigation"),
539
+ searchAgnosticShell: behavior.searchAgnosticShell === true,
387
540
  timestamp: now
388
541
  };
389
542
  addPrefetchInvalidationCallback(entry, options?.onInvalidate);
390
543
  entry.pending = fetchPromise.then(async (response) => {
391
544
  if (response.ok) {
392
- entry.snapshot = await snapshotRscResponse(response);
393
- entry.expiresAt = resolveCachedRscResponseExpiresAt(entry.timestamp, entry.snapshot, PREFETCH_CACHE_TTL);
394
- } else deletePrefetchCacheEntry(cache, prefetched, cacheKey, entry, false);
545
+ const snapshot = await snapshotRscResponse(response);
546
+ if (cache.get(cacheKey) !== entry) return;
547
+ const previousSize = getPrefetchCacheEntrySize(entry);
548
+ entry.snapshot = snapshot;
549
+ entry.size = snapshot.buffer.byteLength;
550
+ adjustPrefetchCacheByteSize(cache, entry.size - previousSize);
551
+ entry.expiresAt = resolvePrefetchedRscResponseExpiresAt(entry.timestamp, entry.snapshot, behavior.fallbackTtlMs ?? PREFETCH_CACHE_TTL);
552
+ addRenderedPathAndSearchPrefetchAlias(cache, prefetched, cacheKey, entry);
553
+ evictPrefetchCacheIfNeeded();
554
+ } else {
555
+ releaseAppPrefetchFetchSlot(response);
556
+ deletePrefetchCacheEntry(cache, prefetched, cacheKey, entry, false);
557
+ }
395
558
  }).catch(() => {
396
559
  deletePrefetchCacheEntry(cache, prefetched, cacheKey, entry, false);
397
560
  }).finally(() => {
561
+ if (cache.get(cacheKey) !== entry) return;
398
562
  entry.pending = void 0;
399
563
  if (entry.snapshot) {
400
564
  entry.outcome = "cache-seeded";
@@ -404,6 +568,36 @@ function prefetchRscResponse(rscUrl, fetchPromise, interceptionContext = null, m
404
568
  cache.set(cacheKey, entry);
405
569
  evictPrefetchCacheIfNeeded();
406
570
  }
571
+ function addRenderedPathAndSearchPrefetchAlias(cache, prefetched, primaryCacheKey, entry) {
572
+ if (entry.cacheForNavigation === false) return;
573
+ const renderedPathAndSearch = entry.snapshot?.renderedPathAndSearch;
574
+ if (!renderedPathAndSearch) return;
575
+ const source = parsePrefetchCacheKey(primaryCacheKey);
576
+ const aliasCacheKey = AppElementsWire.encodeCacheKey(renderedPathAndSearch, source.interceptionContext);
577
+ if (aliasCacheKey === primaryCacheKey) return;
578
+ const existing = cache.get(aliasCacheKey);
579
+ if (existing && existing !== entry) deletePrefetchCacheEntry(cache, prefetched, aliasCacheKey, existing, false);
580
+ entry.cacheKeys ??= new Set([primaryCacheKey]);
581
+ entry.cacheKeys.add(aliasCacheKey);
582
+ cache.set(aliasCacheKey, entry);
583
+ prefetched.add(aliasCacheKey);
584
+ }
585
+ function peekPrefetchResponseForNavigation(rscUrl, interceptionContext = null, mountedSlotsHeader = null) {
586
+ const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader);
587
+ if (!match) return null;
588
+ const { cacheKey, entry } = match;
589
+ if (entry.pending || entry.outcome !== "cache-seeded") return null;
590
+ if (entry.cacheForNavigation === false || !entry.snapshot) return null;
591
+ if (resolvePrefetchCacheEntryExpiresAt(entry) <= Date.now()) {
592
+ deletePrefetchCacheEntry(getPrefetchCache(), getPrefetchedUrls(), cacheKey, entry, true);
593
+ return null;
594
+ }
595
+ if (entry.expiresAt !== void 0 || entry.snapshot.expiresAt !== void 0) return {
596
+ ...entry.snapshot,
597
+ expiresAt: resolvePrefetchCacheEntryExpiresAt(entry)
598
+ };
599
+ return entry.snapshot;
600
+ }
407
601
  /**
408
602
  * Consume a prefetched response for a given rscUrl.
409
603
  * Only returns settled (non-pending) snapshots synchronously.
@@ -417,9 +611,12 @@ function consumePrefetchResponse(rscUrl, interceptionContext = null, mountedSlot
417
611
  const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader);
418
612
  if (!match) return null;
419
613
  const { cacheKey, entry } = match;
614
+ return consumeMatchedPrefetchResponse(cacheKey, entry, mountedSlotsHeader);
615
+ }
616
+ function consumeMatchedPrefetchResponse(cacheKey, entry, mountedSlotsHeader) {
420
617
  if (entry.pending || entry.outcome !== "cache-seeded") return null;
421
618
  if (entry.cacheForNavigation === false) return null;
422
- deletePrefetchCacheEntry(cache, getPrefetchedUrls(), cacheKey, entry, false);
619
+ deletePrefetchCacheEntry(getPrefetchCache(), getPrefetchedUrls(), cacheKey, entry, false);
423
620
  if (entry.snapshot) {
424
621
  if (!isPrefetchCacheEntryCompatibleWithMountedSlots(entry, mountedSlotsHeader)) return null;
425
622
  if (resolvePrefetchCacheEntryExpiresAt(entry) <= Date.now()) return null;
@@ -433,7 +630,7 @@ function consumePrefetchResponse(rscUrl, interceptionContext = null, mountedSlot
433
630
  }
434
631
  async function consumePrefetchResponseForNavigation(rscUrl, interceptionContext = null, mountedSlotsHeader = null, options) {
435
632
  const cache = getPrefetchCache();
436
- const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader);
633
+ const match = findPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader, options?.additionalRscUrls);
437
634
  if (!match) return null;
438
635
  const { cacheKey, entry } = match;
439
636
  if (entry.pending !== void 0) {
@@ -441,7 +638,7 @@ async function consumePrefetchResponseForNavigation(rscUrl, interceptionContext
441
638
  if (cache.get(cacheKey) !== entry) return null;
442
639
  }
443
640
  if (options?.shouldConsume?.() === false) return null;
444
- return consumePrefetchResponse(rscUrl, interceptionContext, mountedSlotsHeader);
641
+ return consumeMatchedPrefetchResponse(cacheKey, entry, mountedSlotsHeader);
445
642
  }
446
643
  const _CLIENT_NAV_STATE_KEY = Symbol.for("vinext.clientNavigationState");
447
644
  const _MOUNTED_SLOTS_HEADER_KEY = Symbol.for("vinext.mountedSlotsHeader");
@@ -772,7 +969,7 @@ function commitClientNavigationState(navId, options) {
772
969
  if (isServer) return;
773
970
  const state = getClientNavigationState();
774
971
  if (!state) return;
775
- if ((navId !== void 0 || options?.releaseSnapshot === true) && state.navigationSnapshotActiveCount > 0) state.navigationSnapshotActiveCount -= 1;
972
+ if ((options?.releaseSnapshot ?? navId !== void 0) && state.navigationSnapshotActiveCount > 0) state.navigationSnapshotActiveCount -= 1;
776
973
  const urlChanged = syncCommittedUrlStateFromLocation();
777
974
  let paramsChanged = false;
778
975
  if (state.pendingClientParams !== null && state.pendingClientParamsJson !== null) {
@@ -1033,6 +1230,7 @@ const _appRouter = {
1033
1230
  prefetch(href, options) {
1034
1231
  assertSafeNavigationUrl(href);
1035
1232
  if (isServer) return;
1233
+ if (isBotUserAgent(window.navigator?.userAgent ?? "")) return;
1036
1234
  try {
1037
1235
  new URL(withBasePath(href, __basePath), window.location.href);
1038
1236
  } catch {
@@ -1060,11 +1258,11 @@ const _appRouter = {
1060
1258
  return;
1061
1259
  }
1062
1260
  prefetched.add(cacheKey);
1063
- prefetchRscResponse(rscUrl, fetch(rscUrl, {
1261
+ prefetchRscResponse(rscUrl, scheduleAppPrefetchFetch(() => fetch(rscUrl, {
1064
1262
  headers,
1065
1263
  credentials: "include",
1066
1264
  priority: "low"
1067
- }), interceptionContext, mountedSlotsHeader, options);
1265
+ }), "low"), interceptionContext, mountedSlotsHeader, options);
1068
1266
  })().catch((error) => {
1069
1267
  console.error("[vinext] RSC prefetch setup error:", error);
1070
1268
  });
@@ -1203,4 +1401,4 @@ if (!isServer) {
1203
1401
  }
1204
1402
  }
1205
1403
  //#endregion
1206
- export { BailoutToCSRError, DynamicServerError, GLOBAL_ACCESSORS_KEY, HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, PREFETCH_CACHE_TTL, ReadonlyURLSearchParams, RedirectType, ServerInsertedHTMLContext, UnrecognizedActionError, __basePath, _registerStateAccessors, activateNavigationSnapshot, appRouterInstance, applyAppRouterScrollFallback, clearPendingPathname, clearServerInsertedHTML, commitClientNavigationState, consumePrefetchResponse, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, decodeRedirectError, flushServerInsertedHTML, forbidden, getAccessFallbackHTTPStatus, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getClientNavigationRenderContext, getClientNavigationState, getClientParams, getCurrentInterceptionContext, getCurrentNextUrl, getLayoutSegmentContext, getMountedSlotsHeader, getNavigationContext, getPrefetchCache, getPrefetchInterceptionContext, getPrefetchedUrls, hasPrefetchCacheEntryForNavigation, invalidatePrefetchCache, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, navigateClientSide, notFound, permanentRedirect, prefetchRscResponse, pushHistoryStateWithoutNotify, redirect, renderServerInsertedHTML, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveCachedRscResponseExpiresAt, resolveCachedRscResponseTtlMs, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, setClientParams, setMountedSlotsHeader, setNavigationContext, setPendingPathname, snapshotRscResponse, storePrefetchResponse, unauthorized, unstable_isUnrecognizedActionError, unstable_rethrow, useClientNavigationRenderSnapshot, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useServerInsertedHTML };
1404
+ export { BailoutToCSRError, DYNAMIC_NAVIGATION_CACHE_TTL, DynamicServerError, GLOBAL_ACCESSORS_KEY, HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, PREFETCH_CACHE_TTL, ReadonlyURLSearchParams, RedirectType, ServerInsertedHTMLContext, UnrecognizedActionError, __basePath, _registerStateAccessors, activateNavigationSnapshot, appRouterInstance, applyAppRouterScrollFallback, clearPendingPathname, clearServerInsertedHTML, commitClientNavigationState, consumePrefetchResponse, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, decodeRedirectError, deletePrefetchResponseSnapshot, flushServerInsertedHTML, forbidden, getAccessFallbackHTTPStatus, getBfcacheIdMapContext, getBfcacheSegmentIdContext, getClientNavigationRenderContext, getClientNavigationState, getClientParams, getCurrentInterceptionContext, getCurrentNextUrl, getLayoutSegmentContext, getMountedSlotsHeader, getNavigationContext, getPrefetchCache, getPrefetchInterceptionContext, getPrefetchedUrls, hasPrefetchCacheEntryForNavigation, hasSearchAgnosticPrefetchShellForRoute, invalidatePrefetchCache, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, navigateClientSide, notFound, peekPrefetchResponseForNavigation, permanentRedirect, prefetchRscResponse, pushHistoryStateWithoutNotify, redirect, renderServerInsertedHTML, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveCachedRscResponseExpiresAt, resolveCachedRscResponseTtlMs, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setNavigationContext, setPendingPathname, snapshotRscResponse, storePrefetchResponse, unauthorized, unstable_isUnrecognizedActionError, unstable_rethrow, useClientNavigationRenderSnapshot, useParams, usePathname, useRouter, useSearchParams, useSelectedLayoutSegment, useSelectedLayoutSegments, useServerInsertedHTML };
@@ -24,6 +24,24 @@ import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation }
24
24
  * ctx?.waitUntil(somePromise);
25
25
  */
26
26
  const _als = getOrCreateAls("vinext.requestContext.als");
27
+ const OPEN_NEXT_CLOUDFLARE_CONTEXT_SYMBOL = Symbol.for("__cloudflare-context__");
28
+ let openNextCloudflareContextFallback;
29
+ function installOpenNextCloudflareContextBridge() {
30
+ const descriptor = Object.getOwnPropertyDescriptor(globalThis, OPEN_NEXT_CLOUDFLARE_CONTEXT_SYMBOL);
31
+ if (descriptor && !descriptor.configurable) return;
32
+ openNextCloudflareContextFallback = descriptor && "value" in descriptor ? descriptor.value : descriptor?.get?.call(globalThis);
33
+ Object.defineProperty(globalThis, OPEN_NEXT_CLOUDFLARE_CONTEXT_SYMBOL, {
34
+ configurable: true,
35
+ get() {
36
+ const ctx = getRequestExecutionContext();
37
+ return ctx ? { ctx } : openNextCloudflareContextFallback;
38
+ },
39
+ set(value) {
40
+ openNextCloudflareContextFallback = value;
41
+ }
42
+ });
43
+ }
44
+ installOpenNextCloudflareContextBridge();
27
45
  function runWithExecutionContext(ctx, fn) {
28
46
  if (isInsideUnifiedScope()) return runWithUnifiedStateMutation((uCtx) => {
29
47
  uCtx.executionContext = ctx;
@@ -3,10 +3,24 @@ type RootParams = Record<string, string | string[] | undefined>;
3
3
  type RootParamsState = {
4
4
  rootParams: RootParams | null;
5
5
  };
6
+ type RootParamsUsage = {
7
+ kind: "route";
8
+ } | {
9
+ kind: "server-action";
10
+ } | {
11
+ kind: "route-handler";
12
+ routePattern: string;
13
+ };
14
+ type RootParamsUsageController = {
15
+ transitionToRender(): void;
16
+ };
6
17
  declare function pickRootParams(params: RootParams, rootParamNames: readonly string[] | null | undefined): RootParams;
7
18
  declare function setRootParams(params: RootParams | null): void;
8
19
  declare function getRootParam(name: string): Promise<string | string[] | undefined>;
20
+ declare function runWithRootParamsUsage<T>(usage: RootParamsUsage, fn: () => Promise<T>, controller?: RootParamsUsageController): Promise<T>;
21
+ declare function runWithRootParamsUsage<T>(usage: RootParamsUsage, fn: () => T | Promise<T>, controller?: RootParamsUsageController): T | Promise<T>;
22
+ declare function createRootParamsUsageController(): RootParamsUsageController;
9
23
  declare function runWithRootParamsScope<T>(params: RootParams, fn: () => Promise<T>): Promise<T>;
10
24
  declare function runWithRootParamsScope<T>(params: RootParams, fn: () => T | Promise<T>): T | Promise<T>;
11
25
  //#endregion
12
- export { RootParams, RootParamsState, getRootParam, pickRootParams, runWithRootParamsScope, setRootParams };
26
+ export { RootParams, RootParamsState, RootParamsUsage, RootParamsUsageController, createRootParamsUsageController, getRootParam, pickRootParams, runWithRootParamsScope, runWithRootParamsUsage, setRootParams };
@@ -1,9 +1,13 @@
1
1
  import { getOrCreateAls } from "./internal/als-registry.js";
2
2
  import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
3
3
  //#region src/shims/root-params.ts
4
+ function createRootParamsUsageError(message) {
5
+ return new Error(message);
6
+ }
4
7
  const _FALLBACK_KEY = Symbol.for("vinext.rootParams.fallback");
5
8
  const _g = globalThis;
6
9
  const _als = getOrCreateAls("vinext.rootParams.als");
10
+ const _usageAls = getOrCreateAls("vinext.rootParams.usage.als");
7
11
  const _fallbackState = _g[_FALLBACK_KEY] ??= { rootParams: null };
8
12
  function getState() {
9
13
  if (isInsideUnifiedScope()) return getRequestContext();
@@ -18,8 +22,24 @@ function setRootParams(params) {
18
22
  getState().rootParams = params;
19
23
  }
20
24
  function getRootParam(name) {
25
+ const usage = _usageAls.getStore();
26
+ if (usage?.kind === "server-action" && usage.phase === "active") throw createRootParamsUsageError(`\`import('next/root-params').${name}()\` was used inside a Server Action. This is not supported. Functions from 'next/root-params' can only be called in the context of a route.`);
27
+ if (usage?.kind === "route-handler" && usage.phase === "active") throw createRootParamsUsageError(`Route ${usage.routePattern} used \`import('next/root-params').${name}()\` inside a Route Handler. Support for this API in Route Handlers is planned for a future version of Next.js.`);
21
28
  return Promise.resolve(getState().rootParams?.[name]);
22
29
  }
30
+ function runWithRootParamsUsage(usage, fn, controller) {
31
+ const state = {
32
+ ...usage,
33
+ phase: "active"
34
+ };
35
+ if (controller) controller.transitionToRender = () => {
36
+ if (usage.kind === "server-action") state.phase = "render";
37
+ };
38
+ return _usageAls.run(state, fn);
39
+ }
40
+ function createRootParamsUsageController() {
41
+ return { transitionToRender() {} };
42
+ }
23
43
  function runWithRootParamsScope(params, fn) {
24
44
  if (isInsideUnifiedScope()) return runWithUnifiedStateMutation((ctx) => {
25
45
  ctx.rootParams = params;
@@ -27,4 +47,4 @@ function runWithRootParamsScope(params, fn) {
27
47
  else return _als.run({ rootParams: params }, fn);
28
48
  }
29
49
  //#endregion
30
- export { getRootParam, pickRootParams, runWithRootParamsScope, setRootParams };
50
+ export { createRootParamsUsageController, getRootParam, pickRootParams, runWithRootParamsScope, runWithRootParamsUsage, setRootParams };
@@ -1,4 +1,5 @@
1
1
  import { VinextNextData } from "../client/vinext-next-data.js";
2
+ import { PagesRouterComponentsMap } from "./internal/pages-router-components.js";
2
3
  import { UrlQuery } from "../utils/query.js";
3
4
  import { ComponentType, ReactElement } from "react";
4
5
 
@@ -25,7 +26,7 @@ type NextRouter = {
25
26
  replace(url: string | UrlObject, as?: string, options?: TransitionOptions): Promise<boolean>; /** Go back */
26
27
  back(): void; /** Reload the page */
27
28
  reload(): void; /** Prefetch a page (injects <link rel="prefetch">) */
28
- prefetch(url: string): Promise<void>; /** Register a callback to run before popstate navigation */
29
+ prefetch(url: string, as?: string): Promise<void>; /** Register a callback to run before popstate navigation */
29
30
  beforePopState(cb: BeforePopStateCallback): void; /** Listen for route changes */
30
31
  events: RouterEvents;
31
32
  };
@@ -174,16 +175,13 @@ type ExcludeRouterProps<P> = Pick<P, Exclude<keyof P, keyof WithRouterProps>>;
174
175
  */
175
176
  declare function withRouter<P extends WithRouterProps>(ComposedComponent: ComponentType<P>): ComponentType<ExcludeRouterProps<P>>;
176
177
  declare const RouterMethods: {
177
- /** See `_components` comment above for the dual role this map plays. */components: {
178
- [x: string]: {
179
- __appRouter: true;
180
- } | Record<string, unknown>;
181
- };
178
+ /** See `_components` comment above for the dual role this map plays. */components: PagesRouterComponentsMap;
179
+ sdc: Record<string, Promise<Response>>;
182
180
  push: (url: string | UrlObject, as?: string, options?: TransitionOptions) => Promise<boolean>;
183
181
  replace: (url: string | UrlObject, as?: string, options?: TransitionOptions) => Promise<boolean>;
184
182
  back: () => void;
185
183
  reload: () => void;
186
- prefetch: (url: string) => Promise<void>;
184
+ prefetch: (url: string, as?: string) => Promise<void>;
187
185
  beforePopState: (cb: BeforePopStateCallback) => void;
188
186
  events: RouterEvents;
189
187
  };