vinext 0.2.0 → 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 (128) hide show
  1. package/dist/build/css-url-assets.d.ts +3 -1
  2. package/dist/build/css-url-assets.js +18 -1
  3. package/dist/build/inject-pregenerated-paths.d.ts +3 -0
  4. package/dist/build/inject-pregenerated-paths.js +4 -1
  5. package/dist/build/prerender-server-entry.d.ts +1 -0
  6. package/dist/build/prerender-server-entry.js +49 -0
  7. package/dist/build/prerender-server-pool.d.ts +44 -0
  8. package/dist/build/prerender-server-pool.js +194 -0
  9. package/dist/build/prerender.js +86 -22
  10. package/dist/build/run-prerender.js +2 -1
  11. package/dist/check.d.ts +4 -6
  12. package/dist/check.js +8 -9
  13. package/dist/cli.js +1 -1
  14. package/dist/client/vinext-next-data.d.ts +4 -1
  15. package/dist/config/config-matchers.js +33 -11
  16. package/dist/config/next-config.d.ts +36 -6
  17. package/dist/config/next-config.js +16 -2
  18. package/dist/entries/app-browser-entry.js +5 -1
  19. package/dist/entries/app-rsc-entry.d.ts +3 -2
  20. package/dist/entries/app-rsc-entry.js +4 -0
  21. package/dist/entries/pages-client-entry.js +14 -3
  22. package/dist/entries/pages-server-entry.js +18 -4
  23. package/dist/index.js +320 -25
  24. package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.js +8 -1
  25. package/dist/packages/cloudflare/src/deploy.js +1 -5
  26. package/dist/packages/cloudflare/src/tpr.js +26 -5
  27. package/dist/plugins/fonts.js +1 -1
  28. package/dist/plugins/ignore-dynamic-requests.js +1 -1
  29. package/dist/plugins/og-assets.js +2 -1
  30. package/dist/plugins/optimize-imports.js +1 -1
  31. package/dist/plugins/rsc-reference-validation-normalizer.d.ts +12 -0
  32. package/dist/plugins/rsc-reference-validation-normalizer.js +48 -0
  33. package/dist/plugins/sass.d.ts +17 -1
  34. package/dist/plugins/sass.js +74 -1
  35. package/dist/plugins/styled-jsx.d.ts +16 -0
  36. package/dist/plugins/styled-jsx.js +149 -0
  37. package/dist/routing/app-route-graph.js +22 -16
  38. package/dist/routing/file-matcher.d.ts +8 -1
  39. package/dist/routing/file-matcher.js +15 -3
  40. package/dist/server/app-browser-entry.js +46 -14
  41. package/dist/server/app-browser-navigation-controller.js +1 -1
  42. package/dist/server/app-browser-server-action-client.js +5 -3
  43. package/dist/server/app-browser-state.d.ts +1 -0
  44. package/dist/server/app-browser-state.js +2 -2
  45. package/dist/server/app-browser-visible-commit.js +1 -1
  46. package/dist/server/app-page-boundary.js +2 -1
  47. package/dist/server/app-page-cache-finalizer.d.ts +1 -0
  48. package/dist/server/app-page-cache-finalizer.js +3 -3
  49. package/dist/server/app-page-cache.js +23 -6
  50. package/dist/server/app-page-dispatch.d.ts +1 -0
  51. package/dist/server/app-page-dispatch.js +25 -8
  52. package/dist/server/app-page-element-builder.js +1 -0
  53. package/dist/server/app-page-execution.js +2 -1
  54. package/dist/server/app-page-render-identity.d.ts +1 -0
  55. package/dist/server/app-page-render-identity.js +2 -1
  56. package/dist/server/app-page-render.d.ts +2 -0
  57. package/dist/server/app-page-render.js +67 -16
  58. package/dist/server/app-page-response.d.ts +7 -0
  59. package/dist/server/app-page-response.js +16 -4
  60. package/dist/server/app-page-stream.d.ts +2 -1
  61. package/dist/server/app-page-stream.js +1 -1
  62. package/dist/server/app-route-handler-execution.js +1 -1
  63. package/dist/server/app-route-tree-prefetch.d.ts +43 -0
  64. package/dist/server/app-route-tree-prefetch.js +187 -0
  65. package/dist/server/app-router-entry.js +1 -1
  66. package/dist/server/app-rsc-cache-busting.d.ts +2 -1
  67. package/dist/server/app-rsc-cache-busting.js +9 -4
  68. package/dist/server/app-rsc-handler.d.ts +6 -0
  69. package/dist/server/app-rsc-handler.js +89 -11
  70. package/dist/server/app-rsc-render-mode.d.ts +3 -2
  71. package/dist/server/app-rsc-render-mode.js +4 -1
  72. package/dist/server/app-rsc-request-normalization.d.ts +3 -4
  73. package/dist/server/app-rsc-request-normalization.js +4 -5
  74. package/dist/server/app-server-action-execution.js +4 -3
  75. package/dist/server/app-ssr-entry.js +3 -3
  76. package/dist/server/cache-control.d.ts +3 -1
  77. package/dist/server/cache-control.js +13 -1
  78. package/dist/server/dev-server.d.ts +11 -1
  79. package/dist/server/dev-server.js +78 -20
  80. package/dist/server/headers.d.ts +6 -2
  81. package/dist/server/headers.js +11 -5
  82. package/dist/server/isr-cache.d.ts +2 -1
  83. package/dist/server/isr-cache.js +7 -3
  84. package/dist/server/pages-data-route.d.ts +4 -2
  85. package/dist/server/pages-data-route.js +18 -4
  86. package/dist/server/pages-dev-module-url.d.ts +2 -1
  87. package/dist/server/pages-dev-module-url.js +6 -3
  88. package/dist/server/pages-node-compat.d.ts +12 -1
  89. package/dist/server/pages-node-compat.js +50 -1
  90. package/dist/server/pages-page-data.d.ts +9 -0
  91. package/dist/server/pages-page-data.js +17 -9
  92. package/dist/server/pages-page-handler.js +19 -7
  93. package/dist/server/pages-page-response.d.ts +1 -0
  94. package/dist/server/pages-page-response.js +3 -2
  95. package/dist/server/pages-request-pipeline.d.ts +8 -6
  96. package/dist/server/pages-request-pipeline.js +30 -6
  97. package/dist/server/pages-router-entry.js +1 -1
  98. package/dist/server/prod-server.d.ts +4 -2
  99. package/dist/server/prod-server.js +38 -22
  100. package/dist/server/request-pipeline.js +1 -1
  101. package/dist/shims/cache-handler.js +8 -1
  102. package/dist/shims/cache.js +3 -0
  103. package/dist/shims/fetch-cache.d.ts +3 -1
  104. package/dist/shims/fetch-cache.js +77 -52
  105. package/dist/shims/form.d.ts +1 -1
  106. package/dist/shims/internal/app-prefetch-fetch-queue.d.ts +10 -0
  107. package/dist/shims/internal/app-prefetch-fetch-queue.js +61 -0
  108. package/dist/shims/internal/hybrid-client-route-owner.d.ts +2 -1
  109. package/dist/shims/internal/hybrid-client-route-owner.js +34 -1
  110. package/dist/shims/internal/pages-data-fetch-dedup.d.ts +2 -1
  111. package/dist/shims/internal/pages-data-fetch-dedup.js +13 -2
  112. package/dist/shims/internal/pages-data-target.d.ts +8 -3
  113. package/dist/shims/internal/pages-data-target.js +9 -4
  114. package/dist/shims/link.d.ts +1 -1
  115. package/dist/shims/link.js +179 -42
  116. package/dist/shims/navigation.d.ts +15 -3
  117. package/dist/shims/navigation.js +202 -39
  118. package/dist/shims/navigation.react-server.js +1 -1
  119. package/dist/shims/request-context.js +18 -0
  120. package/dist/shims/router.js +31 -15
  121. package/dist/shims/unified-request-context.js +1 -0
  122. package/dist/typegen.js +1 -1
  123. package/dist/utils/middleware-request-headers.js +1 -1
  124. package/dist/utils/protocol-headers.d.ts +7 -1
  125. package/dist/utils/protocol-headers.js +7 -1
  126. package/dist/utils/vite-version.d.ts +2 -0
  127. package/dist/utils/vite-version.js +11 -1
  128. package/package.json +2 -2
@@ -13,6 +13,7 @@ import { getCurrentRoutePathnameForWarning } from "./internal/route-pattern-for-
13
13
  import { isBotUserAgent } from "../utils/html-limited-bots.js";
14
14
  import { getNavigationRuntime, hasAppNavigationRuntime, registerNavigationRuntimeFunctions } from "../client/navigation-runtime.js";
15
15
  import { navigatePagesRouterLinkWithFallback, resolvePagesRouterQueryOnlyHref } from "../client/pages-router-link-navigation.js";
16
+ import { scheduleAppPrefetchFetch } from "./internal/app-prefetch-fetch-queue.js";
16
17
  import { getI18nContext } from "./i18n-context.js";
17
18
  import { canLinkIntentPrefetch, canLinkPrefetch, getLinkPrefetchHref } from "./link-prefetch.js";
18
19
  import { clearLinkForCurrentNavigation, notifyLinkNavigationStart, setLinkForCurrentNavigation } from "./internal/link-status-registry.js";
@@ -138,7 +139,7 @@ function getLinkPrefetchRouterMode() {
138
139
  }
139
140
  function resolveMatchedAutoAppRoutePrefetch(route) {
140
141
  return {
141
- cacheForNavigation: !route.canPrefetchLoadingShell,
142
+ cacheForNavigation: !route.canPrefetchLoadingShell && route.requiresDynamicNavigationRequest !== true,
142
143
  prefetchShellFirst: !route.isDynamic,
143
144
  shouldPrefetch: true
144
145
  };
@@ -149,9 +150,8 @@ function canAutoPrefetchFullAppRoute(href) {
149
150
  if (!routes) return false;
150
151
  const routeHref = toSameOriginRouteHref(href);
151
152
  if (routeHref === null) return false;
152
- const match = matchRouteWithTrie(routeHref, routes, linkPrefetchRouteTrieCache);
153
- if (!match) return false;
154
- return resolveMatchedAutoAppRoutePrefetch(match.route).cacheForNavigation;
153
+ if (!matchRouteWithTrie(routeHref, routes, linkPrefetchRouteTrieCache)) return false;
154
+ return resolveAutoAppRoutePrefetch(href).cacheForNavigation;
155
155
  }
156
156
  function resolveAutoAppRoutePrefetch(href) {
157
157
  if (typeof window === "undefined") return {
@@ -177,7 +177,20 @@ function resolveAutoAppRoutePrefetch(href) {
177
177
  prefetchShellFirst: false,
178
178
  shouldPrefetch: false
179
179
  };
180
- return resolveMatchedAutoAppRoutePrefetch(match.route);
180
+ const prefetch = resolveMatchedAutoAppRoutePrefetch(match.route);
181
+ if (new URL(routeHref, "http://vinext.local").search !== "") return {
182
+ ...prefetch,
183
+ cacheForNavigation: false,
184
+ prefetchShellFirst: true
185
+ };
186
+ return prefetch;
187
+ }
188
+ function resolveFullAppRoutePrefetch() {
189
+ return {
190
+ cacheForNavigation: true,
191
+ prefetchShellFirst: true,
192
+ shouldPrefetch: true
193
+ };
181
194
  }
182
195
  /**
183
196
  * Prefetch a URL for faster navigation.
@@ -187,10 +200,11 @@ function resolveAutoAppRoutePrefetch(href) {
187
200
  * For Pages Router: warms the page chunk, prefetches data only for SSG pages,
188
201
  * and falls back to a document prefetch hint when no page loader matches.
189
202
  *
190
- * Uses `requestIdleCallback` (or `setTimeout` fallback) to avoid blocking
191
- * the main thread during initial page load.
203
+ * App Router and high-priority prefetches start immediately. Low-priority
204
+ * Pages Router fallback prefetches use `requestIdleCallback` (or `setTimeout`
205
+ * fallback) to avoid blocking the main thread during initial page load.
192
206
  */
193
- function prefetchUrl(href, mode, priority = "low", pagesRouteHref) {
207
+ function prefetchUrl(href, mode, priority = "low", pagesRouteHref, locale) {
194
208
  if (typeof window === "undefined") return;
195
209
  const prefetchHref = getLinkPrefetchHref({
196
210
  href,
@@ -206,13 +220,11 @@ function prefetchUrl(href, mode, priority = "low", pagesRouteHref) {
206
220
  }) ?? prefetchHref, window.location.href, __basePath);
207
221
  const target = new URL(fullHref, window.location.href);
208
222
  if (target.origin === window.location.origin && target.pathname === window.location.pathname && target.search === window.location.search) return;
209
- (priority === "high" ? (fn) => {
210
- fn();
211
- } : window.requestIdleCallback ?? ((fn) => setTimeout(fn, 100)))(() => {
223
+ const runPrefetch = () => {
212
224
  (async () => {
213
225
  if (hasAppNavigationRuntime()) {
214
226
  if (isBotUserAgent(window.navigator?.userAgent ?? "")) return;
215
- const [navigation, { AppElementsWire }, rscCacheBusting, { APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL }, headersModule, { resolveHybridClientRouteOwner }] = await Promise.all([
227
+ const [navigation, { AppElementsWire }, rscCacheBusting, { APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL }, headersModule, { resolveHybridClientRewriteHref, resolveHybridClientRouteOwner }] = await Promise.all([
216
228
  import("./navigation.js"),
217
229
  import("../server/app-elements.js"),
218
230
  import("../server/app-rsc-cache-busting.js"),
@@ -220,28 +232,40 @@ function prefetchUrl(href, mode, priority = "low", pagesRouteHref) {
220
232
  import("../server/headers.js"),
221
233
  import("./internal/hybrid-client-route-owner.js")
222
234
  ]);
223
- const { getPrefetchInterceptionContext, getPrefetchCache, getPrefetchedUrls, getMountedSlotsHeader, hasPrefetchCacheEntryForNavigation, prefetchRscResponse, PREFETCH_CACHE_TTL } = navigation;
235
+ const { getPrefetchInterceptionContext, getPrefetchCache, getPrefetchedUrls, getMountedSlotsHeader, hasSearchAgnosticPrefetchShellForRoute, hasPrefetchCacheEntryForNavigation, peekPrefetchResponseForNavigation, prefetchRscResponse, restoreRscResponse, PREFETCH_CACHE_TTL } = navigation;
224
236
  const { createRscRequestHeaders, createRscRequestUrl } = rscCacheBusting;
225
- const { NEXT_ROUTER_PREFETCH_HEADER, VINEXT_MOUNTED_SLOTS_HEADER } = headersModule;
237
+ const { NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, VINEXT_MOUNTED_SLOTS_HEADER } = headersModule;
226
238
  const hybridOwner = resolveHybridClientRouteOwner(prefetchHref, __basePath);
227
239
  if (hybridOwner === "pages" || hybridOwner === "document") return;
228
- const autoPrefetch = mode === "auto" ? resolveAutoAppRoutePrefetch(prefetchHref) : {
240
+ const rewrittenPrefetchHref = resolveHybridClientRewriteHref(fullHref, __basePath);
241
+ const autoPrefetch = mode === "auto" ? resolveAutoAppRoutePrefetch(rewrittenPrefetchHref ?? prefetchHref) : mode === "full-after-shell" ? {
229
242
  cacheForNavigation: true,
230
243
  prefetchShellFirst: true,
231
244
  shouldPrefetch: true
232
- };
245
+ } : resolveFullAppRoutePrefetch();
233
246
  if (!autoPrefetch.shouldPrefetch) return;
234
247
  const interceptionContext = getPrefetchInterceptionContext(fullHref);
235
248
  const mountedSlotsHeader = getMountedSlotsHeader();
236
249
  const isOptimisticRouteShellPrefetch = !autoPrefetch.cacheForNavigation;
250
+ const hasSearchParams = new URL(fullHref, window.location.href).search !== "";
251
+ const isAutomaticSearchParamShell = mode === "auto" && isOptimisticRouteShellPrefetch && hasSearchParams;
237
252
  if (isOptimisticRouteShellPrefetch && interceptionContext !== null) return;
253
+ const hasSearchAgnosticShell = isAutomaticSearchParamShell && hasSearchAgnosticPrefetchShellForRoute(await createRscRequestUrl(fullHref, new Headers()), interceptionContext, mountedSlotsHeader);
238
254
  const headers = createRscRequestHeaders({
239
255
  interceptionContext,
240
- renderMode: isOptimisticRouteShellPrefetch ? APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL : void 0
256
+ renderMode: isOptimisticRouteShellPrefetch ? hasSearchAgnosticShell ? APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL : isAutomaticSearchParamShell ? APP_RSC_RENDER_MODE_PREFETCH_DYNAMIC_SHELL : APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL : void 0
241
257
  });
242
258
  if (mountedSlotsHeader) headers.set(VINEXT_MOUNTED_SLOTS_HEADER, mountedSlotsHeader);
243
- if (isOptimisticRouteShellPrefetch) headers.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
259
+ const shouldSendSegmentPrefetchHeaders = isOptimisticRouteShellPrefetch || mode === "auto";
260
+ if (__prefetchInlining && autoPrefetch.cacheForNavigation) {
261
+ headers.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
262
+ headers.set(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, "/__PAGE__");
263
+ } else if (shouldSendSegmentPrefetchHeaders) {
264
+ headers.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
265
+ headers.set(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, "1");
266
+ }
244
267
  const rscUrl = await createRscRequestUrl(fullHref, headers);
268
+ const additionalRscUrls = rewrittenPrefetchHref && rewrittenPrefetchHref !== fullHref ? [await createRscRequestUrl(rewrittenPrefetchHref, headers)] : [];
245
269
  const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
246
270
  const prefetched = getPrefetchedUrls();
247
271
  if (prefetched.has(cacheKey)) {
@@ -249,41 +273,122 @@ function prefetchUrl(href, mode, priority = "low", pagesRouteHref) {
249
273
  const existing = getPrefetchCache().get(cacheKey);
250
274
  if (existing?.cacheForNavigation === false) existing.cacheForNavigation = true;
251
275
  }
252
- if (autoPrefetch.cacheForNavigation && hasPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader)) return;
253
- prefetched.add(cacheKey);
254
- prefetchRscResponse(rscUrl, __prefetchInlining && autoPrefetch.cacheForNavigation && autoPrefetch.prefetchShellFirst ? (async () => {
276
+ const fetchFullRscPayload = () => scheduleAppPrefetchFetch(() => fetch(rscUrl, {
277
+ headers,
278
+ credentials: "include",
279
+ priority,
280
+ purpose: "prefetch"
281
+ }), priority);
282
+ const fetchLoadingShellForReuse = async () => {
255
283
  const shellHeaders = createRscRequestHeaders({
256
284
  interceptionContext,
257
285
  renderMode: APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL
258
286
  });
287
+ shellHeaders.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
288
+ shellHeaders.set(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, "1");
259
289
  if (mountedSlotsHeader) shellHeaders.set(VINEXT_MOUNTED_SLOTS_HEADER, mountedSlotsHeader);
260
290
  const shellRscUrl = await createRscRequestUrl(fullHref, shellHeaders);
261
- const shellResponse = await fetch(shellRscUrl, {
262
- headers: shellHeaders,
291
+ const shellCacheKey = AppElementsWire.encodeCacheKey(shellRscUrl, interceptionContext);
292
+ const shellCache = getPrefetchCache();
293
+ let shellEntry = shellCache.get(shellCacheKey);
294
+ if (shellEntry === void 0) {
295
+ getPrefetchedUrls().add(shellCacheKey);
296
+ prefetchRscResponse(shellRscUrl, scheduleAppPrefetchFetch(() => fetch(shellRscUrl, {
297
+ headers: shellHeaders,
298
+ credentials: "include",
299
+ priority,
300
+ purpose: "prefetch"
301
+ }), priority), interceptionContext, mountedSlotsHeader, void 0, {
302
+ cacheForNavigation: false,
303
+ optimisticRouteShell: true,
304
+ prefetchKind: "loading-shell"
305
+ });
306
+ shellEntry = shellCache.get(shellCacheKey);
307
+ }
308
+ await shellEntry?.pending?.catch(() => {});
309
+ };
310
+ const fetchAliasCacheHitProbe = async () => {
311
+ const probeHeaders = createRscRequestHeaders({
312
+ interceptionContext,
313
+ renderMode: APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL
314
+ });
315
+ probeHeaders.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
316
+ probeHeaders.set(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, "1");
317
+ if (mountedSlotsHeader) probeHeaders.set(VINEXT_MOUNTED_SLOTS_HEADER, mountedSlotsHeader);
318
+ const probeRscUrl = await createRscRequestUrl(fullHref, probeHeaders);
319
+ return fetch(probeRscUrl, {
320
+ method: "HEAD",
321
+ headers: probeHeaders,
263
322
  credentials: "include",
264
323
  priority,
265
324
  purpose: "prefetch"
266
325
  });
267
- if (!shellResponse.ok) return shellResponse;
268
- await shellResponse.arrayBuffer().catch(() => {});
269
- return fetch(rscUrl, {
326
+ };
327
+ const hasExactNavigationCacheEntry = autoPrefetch.cacheForNavigation && hasPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader);
328
+ if (hasExactNavigationCacheEntry || autoPrefetch.cacheForNavigation && hasPrefetchCacheEntryForNavigation(rscUrl, interceptionContext, mountedSlotsHeader, { additionalRscUrls })) {
329
+ if (!hasExactNavigationCacheEntry && !prefetched.has(cacheKey) && additionalRscUrls.length > 0 && autoPrefetch.prefetchShellFirst && mountedSlotsHeader === null) {
330
+ prefetched.add(cacheKey);
331
+ fetchAliasCacheHitProbe().then((response) => response.arrayBuffer()).catch(() => {});
332
+ }
333
+ return;
334
+ }
335
+ prefetched.add(cacheKey);
336
+ const gateViaRouteTree = __prefetchInlining && mode === "auto" && autoPrefetch.prefetchShellFirst;
337
+ const gateViaExplicitSearchShell = mode === "full" && hasSearchParams && autoPrefetch.prefetchShellFirst && mountedSlotsHeader === null;
338
+ const gateViaLoadingShell = (mode === "full-after-shell" || gateViaExplicitSearchShell) && autoPrefetch.prefetchShellFirst;
339
+ const fetchPromise = autoPrefetch.cacheForNavigation && (gateViaRouteTree || gateViaLoadingShell) ? (async () => {
340
+ if (gateViaLoadingShell) {
341
+ await fetchLoadingShellForReuse();
342
+ return fetchFullRscPayload();
343
+ }
344
+ const shellHeaders = createRscRequestHeaders({
345
+ interceptionContext,
346
+ renderMode: void 0
347
+ });
348
+ shellHeaders.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
349
+ shellHeaders.set(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, "/_tree");
350
+ if (mountedSlotsHeader) shellHeaders.set(VINEXT_MOUNTED_SLOTS_HEADER, mountedSlotsHeader);
351
+ const shellRscUrl = await createRscRequestUrl(fullHref, shellHeaders);
352
+ const shellCacheKey = AppElementsWire.encodeCacheKey(shellRscUrl, interceptionContext);
353
+ const shellCache = getPrefetchCache();
354
+ let shellEntry = shellCache.get(shellCacheKey);
355
+ if (shellEntry === void 0) {
356
+ getPrefetchedUrls().add(shellCacheKey);
357
+ prefetchRscResponse(shellRscUrl, scheduleAppPrefetchFetch(() => fetch(shellRscUrl, {
358
+ headers: shellHeaders,
359
+ credentials: "include",
360
+ priority,
361
+ purpose: "prefetch"
362
+ }), priority), interceptionContext, mountedSlotsHeader, void 0, {
363
+ cacheForNavigation: false,
364
+ optimisticRouteShell: false,
365
+ prefetchKind: "route-tree"
366
+ });
367
+ shellEntry = shellCache.get(shellCacheKey);
368
+ }
369
+ await shellEntry?.pending?.catch(() => {});
370
+ const renderedPathAndSearch = shellEntry?.snapshot?.renderedPathAndSearch;
371
+ if (renderedPathAndSearch) {
372
+ const cachedRenderedResponse = peekPrefetchResponseForNavigation(await createRscRequestUrl(renderedPathAndSearch, headers), interceptionContext, mountedSlotsHeader);
373
+ if (cachedRenderedResponse) return restoreRscResponse(cachedRenderedResponse);
374
+ }
375
+ return scheduleAppPrefetchFetch(() => fetch(rscUrl, {
270
376
  headers,
271
377
  credentials: "include",
272
378
  priority,
273
379
  purpose: "prefetch"
274
- });
275
- })() : fetch(rscUrl, {
276
- headers,
277
- credentials: "include",
278
- priority,
279
- purpose: "prefetch"
280
- }), interceptionContext, mountedSlotsHeader, void 0, {
380
+ }), priority);
381
+ })() : fetchFullRscPayload();
382
+ if (mode === "full" && autoPrefetch.cacheForNavigation && autoPrefetch.prefetchShellFirst && mountedSlotsHeader === null && !gateViaExplicitSearchShell) fetchLoadingShellForReuse();
383
+ prefetchRscResponse(rscUrl, fetchPromise, interceptionContext, mountedSlotsHeader, void 0, {
281
384
  cacheForNavigation: autoPrefetch.cacheForNavigation,
282
385
  fallbackTtlMs: PREFETCH_CACHE_TTL,
283
- optimisticRouteShell: isOptimisticRouteShellPrefetch
386
+ optimisticRouteShell: isOptimisticRouteShellPrefetch,
387
+ prefetchKind: isOptimisticRouteShellPrefetch ? "loading-shell" : "navigation",
388
+ searchAgnosticShell: isAutomaticSearchParamShell && !hasSearchAgnosticShell
284
389
  });
285
390
  } else if (HAS_PAGES_ROUTER && window.__NEXT_DATA__) {
286
- const dataTarget = resolvePagesDataNavigationTarget(fullRouteHref, __basePath);
391
+ const dataTarget = resolvePagesDataNavigationTarget(fullRouteHref, __basePath, { locale });
287
392
  if (dataTarget) {
288
393
  const middlewareDataHref = fullRouteHref === fullHref ? dataTarget.middlewareDataHref : getPagesMiddlewareDataHref(fullHref, __basePath) ?? void 0;
289
394
  prefetchPagesData({
@@ -302,7 +407,12 @@ function prefetchUrl(href, mode, priority = "low", pagesRouteHref) {
302
407
  })().catch((error) => {
303
408
  console.error("[vinext] RSC prefetch setup error:", error);
304
409
  });
305
- });
410
+ };
411
+ if (priority === "high" || hasAppNavigationRuntime()) {
412
+ runPrefetch();
413
+ return;
414
+ }
415
+ (window.requestIdleCallback ?? ((fn) => setTimeout(fn, 100)))(runPrefetch);
306
416
  }
307
417
  async function promotePrefetchEntriesForNavigation(href) {
308
418
  if (typeof window === "undefined") return;
@@ -316,6 +426,7 @@ async function promotePrefetchEntriesForNavigation(href) {
316
426
  }
317
427
  for (const [cacheKey, entry] of getPrefetchCache()) {
318
428
  if (entry.optimisticRouteShell === true) continue;
429
+ if (entry.prefetchKind === "route-tree") continue;
319
430
  const [rscUrl] = cacheKey.split("\0", 1);
320
431
  let cached;
321
432
  try {
@@ -334,12 +445,33 @@ async function promotePrefetchEntriesForNavigation(href) {
334
445
  let sharedObserver = null;
335
446
  const observedLinkPrefetches = /* @__PURE__ */ new WeakMap();
336
447
  const visibleLinkPrefetches = /* @__PURE__ */ new Set();
448
+ const visibleAppPrefetchQueue = [];
449
+ let visibleAppPrefetchDrainScheduled = false;
450
+ function drainVisibleAppPrefetchQueue() {
451
+ visibleAppPrefetchDrainScheduled = false;
452
+ while (true) {
453
+ const instance = visibleAppPrefetchQueue.pop();
454
+ if (!instance) return;
455
+ instance.queuedViewportPrefetch = false;
456
+ if (!instance.isVisible || instance.routerMode !== "app") continue;
457
+ prefetchUrl(instance.href, instance.mode, "low", instance.pagesRouteHref);
458
+ }
459
+ }
460
+ function scheduleVisibleAppPrefetch(instance) {
461
+ if (instance.queuedViewportPrefetch) return;
462
+ instance.queuedViewportPrefetch = true;
463
+ visibleAppPrefetchQueue.push(instance);
464
+ if (visibleAppPrefetchDrainScheduled) return;
465
+ visibleAppPrefetchDrainScheduled = true;
466
+ queueMicrotask(drainVisibleAppPrefetchQueue);
467
+ }
337
468
  function setVisibleLinkPrefetch(instance, isVisible) {
338
469
  instance.isVisible = isVisible;
339
470
  if (isVisible) {
340
471
  visibleLinkPrefetches.add(instance);
341
472
  if (instance.routerMode === "pages" && instance.viewportPrefetched) return;
342
- prefetchUrl(instance.href, instance.mode, "low", instance.pagesRouteHref);
473
+ if (instance.routerMode === "app") scheduleVisibleAppPrefetch(instance);
474
+ else prefetchUrl(instance.href, instance.mode, "low", instance.pagesRouteHref, instance.locale);
343
475
  instance.viewportPrefetched = true;
344
476
  } else visibleLinkPrefetches.delete(instance);
345
477
  }
@@ -348,7 +480,7 @@ function registerVisibleLinkPing() {
348
480
  registerNavigationRuntimeFunctions({ pingVisibleLinks: pingVisibleLinkPrefetches });
349
481
  }
350
482
  function pingVisibleLinkPrefetches() {
351
- for (const instance of visibleLinkPrefetches) if (instance.isVisible && instance.routerMode === "app") prefetchUrl(instance.href, instance.mode, "low", instance.pagesRouteHref);
483
+ for (const instance of visibleLinkPrefetches) if (instance.isVisible && instance.routerMode === "app") scheduleVisibleAppPrefetch(instance);
352
484
  }
353
485
  function getSharedObserver() {
354
486
  if (typeof window === "undefined" || typeof IntersectionObserver === "undefined") return null;
@@ -491,12 +623,14 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
491
623
  const instance = {
492
624
  href: hrefToPrefetch,
493
625
  isVisible: false,
626
+ locale,
494
627
  mode: prefetchMode,
495
628
  pagesRouteHref: normalizedRouteHref === normalizedHref ? void 0 : getLinkPrefetchHref({
496
629
  href: normalizedRouteHref,
497
630
  basePath: __basePath,
498
631
  currentOrigin: window.location.origin
499
632
  }) ?? void 0,
633
+ queuedViewportPrefetch: false,
500
634
  routerMode: getLinkPrefetchRouterMode(),
501
635
  viewportPrefetched: false
502
636
  };
@@ -506,12 +640,14 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
506
640
  observer.unobserve(node);
507
641
  observedLinkPrefetches.delete(node);
508
642
  visibleLinkPrefetches.delete(instance);
643
+ instance.isVisible = false;
509
644
  };
510
645
  }, [
511
646
  shouldViewportPrefetch,
512
647
  prefetchMode,
513
648
  normalizedHref,
514
- normalizedRouteHref
649
+ normalizedRouteHref,
650
+ locale
515
651
  ]);
516
652
  const prefetchOnIntent = useCallback(() => {
517
653
  if (!canLinkIntentPrefetch({
@@ -520,19 +656,20 @@ const Link = forwardRef(function Link({ href, as, replace = false, prefetch: pre
520
656
  isDangerous,
521
657
  routerMode: getLinkPrefetchRouterMode()
522
658
  })) return;
523
- const intentMode = unstable_dynamicOnHover ? "full" : prefetchMode;
659
+ const intentMode = unstable_dynamicOnHover ? "full-after-shell" : prefetchMode;
524
660
  if (unstable_dynamicOnHover && internalRef.current) {
525
661
  const instance = observedLinkPrefetches.get(internalRef.current);
526
- if (instance) instance.mode = "full";
662
+ if (instance) instance.mode = "full-after-shell";
527
663
  promotePrefetchEntriesForNavigation(normalizedHref);
528
664
  }
529
- prefetchUrl(normalizedHref, intentMode, "high", normalizedRouteHref === normalizedHref ? void 0 : normalizedRouteHref);
665
+ prefetchUrl(normalizedHref, intentMode, "high", normalizedRouteHref === normalizedHref ? void 0 : normalizedRouteHref, locale);
530
666
  }, [
531
667
  prefetchProp,
532
668
  isDangerous,
533
669
  prefetchMode,
534
670
  normalizedHref,
535
671
  normalizedRouteHref,
672
+ locale,
536
673
  unstable_dynamicOnHover
537
674
  ]);
538
675
  const handleMouseEnter = useCallback((e) => {
@@ -10,8 +10,8 @@ import * as React$1 from "react";
10
10
  //#region src/shims/navigation.d.ts
11
11
  /** basePath from next.config.js, injected by the plugin at build time */
12
12
  declare const __basePath: string;
13
- /** Maximum number of entries in the RSC prefetch cache. */
14
- declare const MAX_PREFETCH_CACHE_SIZE = 50;
13
+ /** Maximum buffered bytes in the RSC prefetch cache. Mirrors Next.js' 50 MB LRU. */
14
+ declare const MAX_PREFETCH_CACHE_SIZE: number;
15
15
  declare const DYNAMIC_NAVIGATION_CACHE_TTL: number;
16
16
  declare const PREFETCH_CACHE_TTL: number;
17
17
  /** A buffered RSC response stored as an ArrayBuffer for replay. */
@@ -23,12 +23,14 @@ type CachedRscResponse = {
23
23
  expiresAt?: number;
24
24
  mountedSlotsHeader?: string | null;
25
25
  paramsHeader: string | null;
26
+ renderedPathAndSearch: string | null;
26
27
  url: string;
27
28
  };
28
29
  type PrefetchOptions = {
29
30
  kind?: unknown;
30
31
  onInvalidate?: () => void;
31
32
  };
33
+ type PrefetchCacheKind = "loading-shell" | "navigation" | "route-tree";
32
34
  type PrefetchCacheEntry = {
33
35
  cacheForNavigation?: boolean;
34
36
  expiresAt?: number;
@@ -38,7 +40,11 @@ type PrefetchCacheEntry = {
38
40
  optimisticRouteShell?: boolean;
39
41
  outcome: "pending" | "cache-seeded";
40
42
  snapshot?: CachedRscResponse;
43
+ cacheKeys?: Set<string>;
41
44
  pending?: Promise<void>;
45
+ prefetchKind?: PrefetchCacheKind;
46
+ searchAgnosticShell?: boolean;
47
+ size?: number;
42
48
  timestamp: number;
43
49
  };
44
50
  declare function getCurrentInterceptionContext(): string | null;
@@ -55,8 +61,10 @@ declare function resolveCachedRscResponseTtlMs(cached: Pick<CachedRscResponse, "
55
61
  declare function resolveCachedRscResponseExpiresAt(timestamp: number, cached: Pick<CachedRscResponse, "dynamicStaleTimeSeconds" | "expiresAt">, fallbackTtlMs: number): number;
56
62
  declare function resolvePrefetchCacheEntryMountedSlotsHeader(entry: PrefetchCacheEntry): string | null;
57
63
  declare function hasPrefetchCacheEntryForNavigation(rscUrl: string, interceptionContext?: string | null, mountedSlotsHeader?: string | null, options?: {
64
+ additionalRscUrls?: readonly string[];
58
65
  notifyInvalidation?: boolean;
59
66
  }): boolean;
67
+ declare function hasSearchAgnosticPrefetchShellForRoute(rscUrl: string, interceptionContext?: string | null, mountedSlotsHeader?: string | null): boolean;
60
68
  declare function invalidatePrefetchCache(): void;
61
69
  declare function seedPrefetchResponseSnapshot(rscUrl: string, snapshot: CachedRscResponse, interceptionContext?: string | null, mountedSlotsHeader?: string | null, fallbackTtlMs?: number): void;
62
70
  declare function deletePrefetchResponseSnapshot(rscUrl: string, snapshot: CachedRscResponse, interceptionContext?: string | null): void;
@@ -110,7 +118,10 @@ declare function prefetchRscResponse(rscUrl: string, fetchPromise: Promise<Respo
110
118
  cacheForNavigation?: boolean;
111
119
  fallbackTtlMs?: number;
112
120
  optimisticRouteShell?: boolean;
121
+ prefetchKind?: PrefetchCacheKind;
122
+ searchAgnosticShell?: boolean;
113
123
  }): void;
124
+ declare function peekPrefetchResponseForNavigation(rscUrl: string, interceptionContext?: string | null, mountedSlotsHeader?: string | null): CachedRscResponse | null;
114
125
  /**
115
126
  * Consume a prefetched response for a given rscUrl.
116
127
  * Only returns settled (non-pending) snapshots synchronously.
@@ -125,6 +136,7 @@ declare function consumePrefetchResponse(rscUrl: string, interceptionContext?: s
125
136
  * one in-flight network request that navigation should share.
126
137
  */
127
138
  type ConsumePrefetchResponseForNavigationOptions = {
139
+ additionalRscUrls?: readonly string[];
128
140
  shouldConsume?: () => boolean;
129
141
  };
130
142
  declare function consumePrefetchResponseForNavigation(rscUrl: string, interceptionContext?: string | null, mountedSlotsHeader?: string | null, options?: ConsumePrefetchResponseForNavigationOptions): Promise<CachedRscResponse | null>;
@@ -287,4 +299,4 @@ declare function useSelectedLayoutSegments(parallelRoutesKey?: string): string[]
287
299
  */
288
300
  declare function useServerInsertedHTML(callback: () => unknown): void;
289
301
  //#endregion
290
- export { BailoutToCSRError, CachedRscResponse, ClientNavigationRenderSnapshot, DYNAMIC_NAVIGATION_CACHE_TTL, DynamicServerError, GLOBAL_ACCESSORS_KEY, HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, type NavigationContext, type NavigationStateAccessors, PREFETCH_CACHE_TTL, PrefetchCacheEntry, PrefetchOptions, ReadonlyURLSearchParams, RedirectType, type SegmentMap, 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, invalidatePrefetchCache, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, navigateClientSide, notFound, 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 };
302
+ export { BailoutToCSRError, CachedRscResponse, ClientNavigationRenderSnapshot, DYNAMIC_NAVIGATION_CACHE_TTL, DynamicServerError, GLOBAL_ACCESSORS_KEY, HTTP_ERROR_FALLBACK_ERROR_CODE, MAX_PREFETCH_CACHE_SIZE, type NavigationContext, type NavigationStateAccessors, PREFETCH_CACHE_TTL, PrefetchCacheEntry, PrefetchCacheKind, PrefetchOptions, ReadonlyURLSearchParams, RedirectType, type SegmentMap, 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 };