vinext 1.0.0-beta.2 → 1.0.0-beta.4

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 (174) hide show
  1. package/README.md +6 -9
  2. package/dist/build/client-build-config.js +7 -1
  3. package/dist/build/precompress.js +1 -1
  4. package/dist/build/prerender.d.ts +2 -1
  5. package/dist/build/prerender.js +12 -2
  6. package/dist/check.js +4 -0
  7. package/dist/cli-args.d.ts +2 -1
  8. package/dist/cli-args.js +10 -0
  9. package/dist/cli.js +14 -8
  10. package/dist/client/navigation-runtime.d.ts +6 -0
  11. package/dist/client/navigation-runtime.js +1 -1
  12. package/dist/config/config-matchers.js +4 -1
  13. package/dist/config/dotenv.d.ts +3 -4
  14. package/dist/config/dotenv.js +30 -22
  15. package/dist/config/next-config.d.ts +16 -5
  16. package/dist/config/next-config.js +7 -0
  17. package/dist/entries/app-browser-entry.d.ts +4 -10
  18. package/dist/entries/app-browser-entry.js +20 -4
  19. package/dist/entries/app-rsc-entry.js +21 -3
  20. package/dist/entries/app-rsc-manifest.js +19 -0
  21. package/dist/entries/pages-client-entry.js +0 -1
  22. package/dist/entries/pages-server-entry.js +13 -3
  23. package/dist/index.js +122 -49
  24. package/dist/init-cloudflare.d.ts +3 -1
  25. package/dist/init-cloudflare.js +59 -5
  26. package/dist/plugins/client-reference-dedup.js +7 -5
  27. package/dist/plugins/extensionless-dynamic-import.js +23 -18
  28. package/dist/plugins/ignore-dynamic-requests.js +3 -1
  29. package/dist/plugins/import-meta-url.js +17 -6
  30. package/dist/plugins/require-context.js +20 -15
  31. package/dist/plugins/sass.js +3 -4
  32. package/dist/plugins/transform-cache.d.ts +19 -0
  33. package/dist/plugins/transform-cache.js +36 -0
  34. package/dist/routing/app-route-graph.d.ts +14 -6
  35. package/dist/routing/app-route-graph.js +106 -16
  36. package/dist/server/api-handler.d.ts +2 -0
  37. package/dist/server/api-handler.js +7 -6
  38. package/dist/server/app-bfcache-identity.js +28 -2
  39. package/dist/server/app-browser-entry.js +175 -75
  40. package/dist/server/app-browser-navigation-controller.d.ts +23 -21
  41. package/dist/server/app-browser-navigation-controller.js +30 -5
  42. package/dist/server/app-browser-prefetch-response.d.ts +9 -0
  43. package/dist/server/app-browser-prefetch-response.js +32 -0
  44. package/dist/server/app-browser-state.d.ts +8 -3
  45. package/dist/server/app-browser-state.js +9 -3
  46. package/dist/server/app-page-cache-finalizer.js +1 -1
  47. package/dist/server/app-page-cache.d.ts +1 -1
  48. package/dist/server/app-page-cache.js +16 -2
  49. package/dist/server/app-page-dispatch.d.ts +9 -1
  50. package/dist/server/app-page-dispatch.js +37 -8
  51. package/dist/server/app-page-element-builder.d.ts +4 -1
  52. package/dist/server/app-page-element-builder.js +34 -7
  53. package/dist/server/app-page-head.d.ts +3 -3
  54. package/dist/server/app-page-head.js +58 -13
  55. package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
  56. package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
  57. package/dist/server/app-page-probe.d.ts +4 -0
  58. package/dist/server/app-page-probe.js +6 -2
  59. package/dist/server/app-page-render.d.ts +1 -1
  60. package/dist/server/app-page-render.js +13 -3
  61. package/dist/server/app-page-request.d.ts +3 -1
  62. package/dist/server/app-page-request.js +4 -13
  63. package/dist/server/app-page-response.d.ts +2 -0
  64. package/dist/server/app-page-response.js +7 -2
  65. package/dist/server/app-page-route-wiring.d.ts +10 -2
  66. package/dist/server/app-page-route-wiring.js +269 -59
  67. package/dist/server/app-pages-bridge.d.ts +2 -1
  68. package/dist/server/app-pages-bridge.js +3 -1
  69. package/dist/server/app-route-handler-cache.js +4 -0
  70. package/dist/server/app-route-handler-dispatch.js +9 -3
  71. package/dist/server/app-route-handler-response.js +1 -1
  72. package/dist/server/app-route-module-loader.d.ts +6 -0
  73. package/dist/server/app-route-module-loader.js +3 -0
  74. package/dist/server/app-router-entry.js +6 -3
  75. package/dist/server/app-rsc-cache-busting.d.ts +7 -0
  76. package/dist/server/app-rsc-cache-busting.js +6 -0
  77. package/dist/server/app-rsc-errors.js +10 -0
  78. package/dist/server/app-rsc-handler.d.ts +2 -0
  79. package/dist/server/app-rsc-handler.js +30 -13
  80. package/dist/server/app-rsc-render-mode.d.ts +3 -2
  81. package/dist/server/app-rsc-render-mode.js +4 -1
  82. package/dist/server/app-rsc-request-normalization.js +65 -4
  83. package/dist/server/app-rsc-route-matching.d.ts +9 -0
  84. package/dist/server/app-rsc-route-matching.js +6 -0
  85. package/dist/server/app-server-action-execution.d.ts +1 -1
  86. package/dist/server/app-ssr-entry.js +4 -4
  87. package/dist/server/app-visited-response-cache.d.ts +6 -1
  88. package/dist/server/app-visited-response-cache.js +49 -1
  89. package/dist/server/cache-control.d.ts +2 -2
  90. package/dist/server/cache-control.js +2 -1
  91. package/dist/server/dev-server.d.ts +3 -1
  92. package/dist/server/dev-server.js +204 -348
  93. package/dist/server/edge-api-runtime.d.ts +10 -1
  94. package/dist/server/edge-api-runtime.js +29 -1
  95. package/dist/server/headers.d.ts +4 -2
  96. package/dist/server/headers.js +6 -3
  97. package/dist/server/isr-cache.d.ts +10 -33
  98. package/dist/server/isr-cache.js +33 -43
  99. package/dist/server/isr-decision.d.ts +2 -2
  100. package/dist/server/pages-api-route.d.ts +4 -0
  101. package/dist/server/pages-api-route.js +6 -3
  102. package/dist/server/pages-asset-tags.d.ts +1 -0
  103. package/dist/server/pages-asset-tags.js +6 -4
  104. package/dist/server/pages-dev-hydration.js +0 -1
  105. package/dist/server/pages-document-asset-props.d.ts +26 -0
  106. package/dist/server/pages-document-asset-props.js +69 -0
  107. package/dist/server/pages-get-initial-props.d.ts +1 -1
  108. package/dist/server/pages-get-initial-props.js +1 -4
  109. package/dist/server/pages-i18n.js +64 -16
  110. package/dist/server/pages-node-compat.d.ts +2 -0
  111. package/dist/server/pages-node-compat.js +10 -6
  112. package/dist/server/pages-page-data.d.ts +61 -9
  113. package/dist/server/pages-page-data.js +334 -74
  114. package/dist/server/pages-page-handler.d.ts +6 -1
  115. package/dist/server/pages-page-handler.js +119 -20
  116. package/dist/server/pages-page-response.d.ts +7 -3
  117. package/dist/server/pages-page-response.js +41 -33
  118. package/dist/server/pages-request-pipeline.d.ts +7 -0
  119. package/dist/server/pages-request-pipeline.js +5 -1
  120. package/dist/server/pages-revalidate.d.ts +1 -1
  121. package/dist/server/pages-revalidate.js +54 -8
  122. package/dist/server/pages-router-entry.d.ts +1 -0
  123. package/dist/server/pages-router-entry.js +10 -4
  124. package/dist/server/prerender-manifest.d.ts +1 -0
  125. package/dist/server/prod-server.d.ts +2 -1
  126. package/dist/server/prod-server.js +38 -13
  127. package/dist/server/revalidation-host.d.ts +11 -0
  128. package/dist/server/revalidation-host.js +19 -0
  129. package/dist/server/seed-cache.js +1 -1
  130. package/dist/server/worker-revalidation-context.d.ts +13 -0
  131. package/dist/server/worker-revalidation-context.js +30 -0
  132. package/dist/shims/app-router-scroll-state.d.ts +2 -1
  133. package/dist/shims/app-router-scroll-state.js +7 -2
  134. package/dist/shims/cache-handler.d.ts +1 -1
  135. package/dist/shims/cache-handler.js +10 -7
  136. package/dist/shims/cache-runtime.d.ts +6 -0
  137. package/dist/shims/cache-runtime.js +4 -1
  138. package/dist/shims/cache.js +1 -1
  139. package/dist/shims/document.js +15 -2
  140. package/dist/shims/error.js +1 -1
  141. package/dist/shims/fetch-cache.js +1 -1
  142. package/dist/shims/font-google-base.js +17 -8
  143. package/dist/shims/font-local.js +41 -10
  144. package/dist/shims/hash-scroll.d.ts +1 -1
  145. package/dist/shims/hash-scroll.js +2 -1
  146. package/dist/shims/headers.d.ts +10 -13
  147. package/dist/shims/headers.js +85 -36
  148. package/dist/shims/internal/cookie-serialize.d.ts +10 -9
  149. package/dist/shims/internal/cookie-serialize.js +9 -8
  150. package/dist/shims/internal/interpolate-as.d.ts +15 -1
  151. package/dist/shims/internal/interpolate-as.js +27 -2
  152. package/dist/shims/link.d.ts +2 -0
  153. package/dist/shims/link.js +77 -24
  154. package/dist/shims/metadata.d.ts +18 -34
  155. package/dist/shims/metadata.js +98 -56
  156. package/dist/shims/navigation.d.ts +18 -2
  157. package/dist/shims/navigation.js +70 -21
  158. package/dist/shims/request-context.d.ts +6 -2
  159. package/dist/shims/router.js +18 -7
  160. package/dist/shims/script.js +1 -1
  161. package/dist/shims/server.d.ts +7 -6
  162. package/dist/shims/server.js +156 -57
  163. package/dist/shims/slot.d.ts +2 -1
  164. package/dist/shims/slot.js +6 -2
  165. package/dist/shims/streamed-icons.d.ts +9 -0
  166. package/dist/shims/streamed-icons.js +33 -0
  167. package/dist/shims/unified-request-context.d.ts +21 -2
  168. package/dist/shims/unified-request-context.js +82 -1
  169. package/dist/utils/cache-control-metadata.d.ts +2 -1
  170. package/dist/utils/cache-control-metadata.js +5 -1
  171. package/dist/utils/html-limited-bots.js +1 -1
  172. package/dist/utils/protocol-headers.d.ts +7 -1
  173. package/dist/utils/protocol-headers.js +7 -1
  174. package/package.json +1 -1
@@ -12,7 +12,7 @@ import { DevRecoveryBoundary, GlobalErrorBoundary, RedirectBoundary } from "../s
12
12
  import { beginAppRouterScrollIntent, consumeAppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
13
13
  import { AppRouterScrollCommitProvider } from "../shims/app-router-scroll.js";
14
14
  import { BfcacheStateKeyMapContext, ElementsContext, Slot } from "../shims/slot.js";
15
- import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, getVinextRscCompatibilityId, stripRscCacheBustingSearchParam } from "./app-rsc-cache-busting.js";
15
+ import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, getVinextRscCompatibilityId } from "./app-rsc-cache-busting.js";
16
16
  import { getNavigationRuntime, registerNavigationRuntimeBootstrap, registerNavigationRuntimeFunctions } from "../client/navigation-runtime.js";
17
17
  import { notifyAppRouterTransitionStart } from "../client/instrumentation-client-state.js";
18
18
  import "../client/instrumentation-client.js";
@@ -24,17 +24,18 @@ import { readHistoryStatePreviousNextUrl } from "./app-history-state.js";
24
24
  import { AppBrowserMpaNavigationScheduler } from "./app-browser-mpa-navigation.js";
25
25
  import { blockDangerousStreamedRscRedirect } from "./app-browser-rsc-redirect.js";
26
26
  import { navigationPlanner } from "./navigation-planner.js";
27
- import { DYNAMIC_NAVIGATION_CACHE_TTL, PREFETCH_CACHE_TTL, __basePath, appRouterInstance, commitClientNavigationState, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, deletePrefetchResponseSnapshot, getClientNavigationRenderContext, getMountedSlotsHeader, getPrefetchCache, hasPrefetchCacheEntryForNavigation, invalidatePrefetchCache, preloadHybridClientRouteOwner, pushHistoryStateWithoutNotify, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveLoadedHybridClientRewriteHref, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setPendingPathname, useRouter } from "../shims/navigation.js";
27
+ import { DYNAMIC_NAVIGATION_CACHE_TTL, PREFETCH_CACHE_TTL, __basePath, appRouterInstance, commitClientNavigationState, consumePrefetchResponse, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, deletePrefetchResponseSnapshot, getClientNavigationRenderContext, getMountedSlotsHeader, getPrefetchCache, hasPrefetchCacheEntryForNavigation, invalidatePrefetchCache, preloadHybridClientRouteOwner, pushHistoryStateWithoutNotify, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolveLoadedHybridClientRewriteHref, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setPendingPathname, useRouter } from "../shims/navigation.js";
28
28
  import { chunksToReadableStream, createProgressiveRscStream, getVinextBrowserGlobal } from "./app-browser-stream.js";
29
29
  import { COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, isCacheRestorableAppPayloadMetadata, isCompleteAppPayloadMetadata, resolveInterceptionContextFromPreviousNextUrl } from "./app-browser-state.js";
30
30
  import { clearHardNavigationLoopGuard, createAppBrowserNavigationController, createBasePathStrippedPathAndSearch } from "./app-browser-navigation-controller.js";
31
31
  import { consumeInitialFormState, createVinextHydrateRootOptions, hydrateRootInTransition } from "./app-browser-hydration.js";
32
32
  import { AppBrowserHistoryController } from "./app-browser-history-controller.js";
33
- import { createVisitedResponseCacheEntry, isVisitedResponseCacheEntryFresh } from "./app-visited-response-cache.js";
33
+ import { createVisitedResponseCacheEntry, deleteVisitedResponseCacheEntry, findVisitedResponseCacheEntry, isVisitedResponseCacheEntryFresh } from "./app-visited-response-cache.js";
34
34
  import { createPopstateRestoreHandler, restoreSynchronousPopstateScrollPosition } from "./app-browser-popstate.js";
35
35
  import { createDevOnCaughtError, createOnUncaughtError, createProdOnCaughtError, prodOnRecoverableError } from "./app-browser-error.js";
36
36
  import { createHydrationCachePublication } from "./app-hydration-cache-publication.js";
37
- import { createOptimisticRouteTemplate, getOptimisticPrefetchSourceKey, getOptimisticRouteTemplateKey, resolveOptimisticNavigationPayload } from "./app-optimistic-routing.js";
37
+ import { resolvePrefetchNavigationResponseUrl } from "./app-browser-prefetch-response.js";
38
+ import { createOptimisticRouteTemplate, getOptimisticPrefetchSourceKey, getOptimisticRouteTemplateKey, matchOptimisticRouteManifestRoute, resolveOptimisticNavigationPayload } from "./app-optimistic-routing.js";
38
39
  import { removeStylesheetLinksCoveredByInlineCss } from "./app-inline-css-client.js";
39
40
  import { createElement, startTransition, use, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
40
41
  import { flushSync } from "react-dom";
@@ -126,6 +127,7 @@ let latestClientParams = {};
126
127
  const visitedResponseCache = /* @__PURE__ */ new Map();
127
128
  let clientNavigationCacheGeneration = 0;
128
129
  let browserRouterStateHasEverCommitted = false;
130
+ let initialPrefetchRouterState = null;
129
131
  const mpaNavigationScheduler = new AppBrowserMpaNavigationScheduler();
130
132
  const unresolvedMpaNavigation = new Promise(() => {});
131
133
  const RSC_HMR_SETTLE_DELAY_MS = 150;
@@ -193,21 +195,6 @@ function clearClientNavigationCaches() {
193
195
  clearPrefetchState();
194
196
  historyController.invalidateRestorableClientState();
195
197
  }
196
- function normalizeBrowserRscUrlForReuse(url) {
197
- if (!url) return null;
198
- try {
199
- const parsed = new URL(url, window.location.origin);
200
- stripRscCacheBustingSearchParam(parsed);
201
- return `${parsed.pathname}${parsed.search}`;
202
- } catch {
203
- return null;
204
- }
205
- }
206
- function isAlternatePrefetchResponseUrl(responseUrl, additionalRscUrls) {
207
- const normalizedResponseUrl = normalizeBrowserRscUrlForReuse(responseUrl);
208
- if (normalizedResponseUrl === null) return false;
209
- return additionalRscUrls.some((additionalRscUrl) => normalizeBrowserRscUrlForReuse(additionalRscUrl) === normalizedResponseUrl);
210
- }
211
198
  function isSettledPrefetchCacheEntry(entry) {
212
199
  return entry.outcome === "cache-seeded" && entry.pending === void 0 && entry.snapshot !== void 0;
213
200
  }
@@ -302,28 +289,29 @@ function createNavigationCommitEffect(options) {
302
289
  commitClientNavigationState(navId);
303
290
  };
304
291
  }
305
- async function renderNavigationPayload(payload, navigationSnapshot, targetHref, navId, historyUpdateMode, params, previousNextUrl, pendingRouterState, payloadOrigin, actionType = "navigate", operationLane = "navigation", traversalIntent = null, scrollIntent = null, restoredBfcacheIds = null, reuseCurrentBfcacheIds = true, visibleCommitMode = "transition", onCommittedState, navigationCommitKind) {
292
+ async function renderNavigationPayload(options) {
306
293
  syncServerActionHttpFallbackHead(null);
307
294
  return browserNavigationController.renderNavigationPayload({
308
- actionType,
295
+ actionType: options.actionType ?? "navigate",
309
296
  createNavigationCommitEffect,
310
- historyUpdateMode,
311
- navigationSnapshot,
312
- nextElements: payload,
313
- operationLane,
314
- payloadOrigin,
315
- params,
316
- pendingRouterState,
317
- previousNextUrl,
318
- scrollIntent,
319
- restoredBfcacheIds,
320
- reuseCurrentBfcacheIds,
321
- targetHistoryIndex: traversalIntent === null ? void 0 : traversalIntent.targetHistoryIndex,
322
- targetHref,
323
- navId,
324
- navigationCommitKind,
325
- visibleCommitMode,
326
- onCommittedState
297
+ historyUpdateMode: options.historyUpdateMode,
298
+ navigationCommitKind: options.navigationCommitKind,
299
+ navigationInitiationState: options.navigationInitiationState,
300
+ navigationSnapshot: options.navigationSnapshot,
301
+ navId: options.navId,
302
+ nextElements: options.payload,
303
+ onCommittedState: options.onCommittedState,
304
+ operationLane: options.operationLane ?? "navigation",
305
+ params: options.params,
306
+ payloadOrigin: options.payloadOrigin,
307
+ pendingRouterState: options.pendingRouterState,
308
+ previousNextUrl: options.previousNextUrl,
309
+ restoredBfcacheIds: options.restoredBfcacheIds ?? null,
310
+ reuseCurrentBfcacheIds: options.reuseCurrentBfcacheIds ?? true,
311
+ scrollIntent: options.scrollIntent ?? null,
312
+ targetHistoryIndex: options.traversalIntent?.targetHistoryIndex,
313
+ targetHref: options.targetHref,
314
+ visibleCommitMode: options.visibleCommitMode ?? "transition"
327
315
  });
328
316
  }
329
317
  async function commitSameUrlNavigatePayload(nextElements, actionInitiation, returnValue, revalidation = "none") {
@@ -346,8 +334,8 @@ function evictVisitedResponseCacheIfNeeded() {
346
334
  }
347
335
  function readVisitedResponseCacheCandidate(rscUrl, interceptionContext, mountedSlotsHeader, navigationKind) {
348
336
  const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
349
- const cached = visitedResponseCache.get(cacheKey);
350
- if (!cached) return {
337
+ const match = findVisitedResponseCacheEntry(visitedResponseCache, rscUrl, interceptionContext);
338
+ if (!match) return {
351
339
  cacheKey,
352
340
  entry: null,
353
341
  facts: {
@@ -356,15 +344,15 @@ function readVisitedResponseCacheCandidate(rscUrl, interceptionContext, mountedS
356
344
  }
357
345
  };
358
346
  return {
359
- cacheKey,
360
- entry: cached,
347
+ cacheKey: match.cacheKey,
348
+ entry: match.entry,
361
349
  facts: {
362
350
  candidate: "present",
363
- fresh: isVisitedResponseCacheEntryFresh(cached, {
351
+ fresh: isVisitedResponseCacheEntryFresh(match.entry, {
364
352
  navigationKind,
365
353
  now: Date.now()
366
354
  }),
367
- mountedSlotsMatch: cached.mountedSlotsHeader === mountedSlotsHeader,
355
+ mountedSlotsMatch: match.entry.elements !== void 0 || match.entry.mountedSlotsHeader === mountedSlotsHeader,
368
356
  navigationKind
369
357
  }
370
358
  };
@@ -380,9 +368,9 @@ function applyVisitedResponseCacheCandidateDecision(candidate, decision) {
380
368
  return null;
381
369
  }
382
370
  function deleteVisitedResponse(rscUrl, interceptionContext) {
383
- visitedResponseCache.delete(AppElementsWire.encodeCacheKey(rscUrl, interceptionContext));
371
+ deleteVisitedResponseCacheEntry(visitedResponseCache, rscUrl, interceptionContext);
384
372
  }
385
- function storeVisitedResponseSnapshot(rscUrl, interceptionContext, snapshot, params, prefetchFallbackTtlMs = DYNAMIC_NAVIGATION_CACHE_TTL, requestMountedSlotsHeader = snapshot.mountedSlotsHeader ?? null, elements) {
373
+ function storeVisitedResponseSnapshot(rscUrl, interceptionContext, snapshot, params, prefetchFallbackTtlMs = DYNAMIC_NAVIGATION_CACHE_TTL, requestMountedSlotsHeader = snapshot.mountedSlotsHeader ?? null, elements, seedPrefetchCache = true) {
386
374
  const cacheKey = AppElementsWire.encodeCacheKey(rscUrl, interceptionContext);
387
375
  visitedResponseCache.delete(cacheKey);
388
376
  evictVisitedResponseCacheIfNeeded();
@@ -395,10 +383,10 @@ function storeVisitedResponseSnapshot(rscUrl, interceptionContext, snapshot, par
395
383
  response: snapshot
396
384
  });
397
385
  visitedResponseCache.set(cacheKey, entry);
398
- seedPrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext, requestMountedSlotsHeader, prefetchFallbackTtlMs);
386
+ if (seedPrefetchCache) seedPrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext, requestMountedSlotsHeader, prefetchFallbackTtlMs);
399
387
  return () => {
400
388
  if (visitedResponseCache.get(cacheKey) === entry) visitedResponseCache.delete(cacheKey);
401
- deletePrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext);
389
+ if (seedPrefetchCache) deletePrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext);
402
390
  };
403
391
  }
404
392
  function clientNavigationSnapshotHref(snapshot) {
@@ -725,7 +713,21 @@ function registerServerActionCallback() {
725
713
  const hashIdx = target.href.indexOf("#");
726
714
  const actionScrollIntent = beginAppRouterScrollIntent((hashIdx !== -1 ? target.href.slice(hashIdx) : "") || null);
727
715
  if (target.type === "push") saveScrollPosition();
728
- renderNavigationPayload(Promise.resolve(elements), createClientNavigationRenderSnapshot(target.href, actionInitiation.routerState.navigationSnapshot.params), target.href, actionInitiation.navigationId, target.type === "push" ? "push" : "replace", {}, null, null, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, target.type === "push" ? "navigate" : "replace", resolveServerActionOperationLane(revalidation), null, actionScrollIntent).catch(() => {
716
+ renderNavigationPayload({
717
+ actionType: target.type === "push" ? "navigate" : "replace",
718
+ historyUpdateMode: target.type === "push" ? "push" : "replace",
719
+ navigationInitiationState: actionInitiation.routerState,
720
+ navigationSnapshot: createClientNavigationRenderSnapshot(target.href, actionInitiation.routerState.navigationSnapshot.params),
721
+ navId: actionInitiation.navigationId,
722
+ operationLane: resolveServerActionOperationLane(revalidation),
723
+ params: {},
724
+ payload: Promise.resolve(elements),
725
+ payloadOrigin: FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN,
726
+ pendingRouterState: null,
727
+ previousNextUrl: null,
728
+ scrollIntent: actionScrollIntent,
729
+ targetHref: target.href
730
+ }).catch(() => {
729
731
  browserNavigationController.performHardNavigation(target.href);
730
732
  });
731
733
  },
@@ -763,6 +765,10 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
763
765
  Promise.all([root, initialCacheBuffer]).then(async ([elements, buffer]) => {
764
766
  if (cacheGeneration !== clientNavigationCacheGeneration) return;
765
767
  const metadata = AppElementsWire.readMetadata(elements);
768
+ initialPrefetchRouterState = {
769
+ pathAndSearch: initialPathAndSearch,
770
+ routeId: metadata.routeId
771
+ };
766
772
  if (!isCompleteAppPayloadMetadata(metadata)) return;
767
773
  const mountedSlotsHeader = getMountedSlotIdsHeader(elements);
768
774
  const headers = createRscRequestHeaders({ mountedSlotsHeader });
@@ -781,8 +787,7 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
781
787
  const fallbackTtlMs = initialRscBootstrap?.initialCacheKind === "static" ? PREFETCH_CACHE_TTL : DYNAMIC_NAVIGATION_CACHE_TTL;
782
788
  hydrationCachePublication.publish(() => {
783
789
  if (cacheGeneration !== clientNavigationCacheGeneration) return () => {};
784
- if (isCacheRestorableAppPayloadMetadata(metadata)) return storeVisitedResponseSnapshot(rscUrl, metadata.interceptionContext, snapshot, initialParams, fallbackTtlMs, mountedSlotsHeader);
785
- return storeVisitedResponseSnapshot(rscUrl, metadata.interceptionContext, snapshot, initialParams, fallbackTtlMs, mountedSlotsHeader, elements);
790
+ return storeVisitedResponseSnapshot(rscUrl, metadata.interceptionContext, snapshot, initialParams, fallbackTtlMs, mountedSlotsHeader, elements, false);
786
791
  });
787
792
  }).catch(() => {});
788
793
  historyController.writeBootstrapHistoryMetadata();
@@ -840,6 +845,26 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
840
845
  registerNavigationRuntimeFunctions({
841
846
  clearNavigationCaches: clearClientNavigationCaches,
842
847
  commitHashNavigation: (href, historyUpdateMode, scroll) => historyController.commitHashOnlyNavigation(href, historyUpdateMode, scroll),
848
+ getPrefetchRouterState: () => {
849
+ if (!browserNavigationController.hasBrowserRouterState()) {
850
+ if (initialPrefetchRouterState) return initialPrefetchRouterState;
851
+ const routeManifest = getNavigationRuntime()?.bootstrap.routeManifest ?? null;
852
+ const match = routeManifest ? matchOptimisticRouteManifestRoute({
853
+ basePath: __basePath,
854
+ href: window.location.href,
855
+ routeManifest
856
+ }) : null;
857
+ return match ? {
858
+ pathAndSearch: createBasePathStrippedPathAndSearch(new URL(window.location.href), __basePath),
859
+ routeId: match.route.id
860
+ } : null;
861
+ }
862
+ const state = browserNavigationController.getBrowserRouterState();
863
+ return {
864
+ pathAndSearch: createSnapshotPathAndSearch(state.navigationSnapshot),
865
+ routeId: state.routeId
866
+ };
867
+ },
843
868
  navigate: async function navigateRsc(href, redirectDepth = 0, navigationKind = "navigate", historyUpdateMode, previousNextUrlOverride, programmaticTransition = false, traversalIntent, scrollIntent, visibleCommitMode = "transition") {
844
869
  abortSupersededNavigation();
845
870
  const navigationAbortController = new AbortController();
@@ -864,12 +889,15 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
864
889
  const reuseCurrentBfcacheIds = navigationKind !== "traverse" || !historyController.isCacheInvalidationGuarded() && historyController.isCurrentBfcacheVersion(activeTraversalIntent?.historyState ?? window.history.state);
865
890
  try {
866
891
  const shouldUsePendingRouterState = programmaticTransition;
867
- if (shouldUsePendingRouterState && hasBrowserRouterState()) pendingRouterState = beginPendingBrowserRouterState();
868
- else {
892
+ if (hasBrowserRouterState()) {
893
+ if (shouldUsePendingRouterState) pendingRouterState = beginPendingBrowserRouterState();
894
+ } else {
869
895
  await waitForBrowserRouterStateReady();
870
896
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
871
897
  if (shouldUsePendingRouterState) pendingRouterState = beginPendingBrowserRouterState();
872
898
  }
899
+ const navigationInitiationState = getBrowserRouterState();
900
+ const mountedSlotsHeader = getMountedSlotIdsHeader(navigationInitiationState.elements);
873
901
  while (true) {
874
902
  const url = new URL(currentHref, window.location.origin);
875
903
  const requestState = getRequestState(navigationKind, url.pathname, currentPrevNextUrl, activeTraversalIntent?.historyState);
@@ -877,24 +905,30 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
877
905
  const requestPreviousNextUrl = requestState.previousNextUrl;
878
906
  if (navigationKind === "refresh") historyController.syncCurrentHistoryStatePreviousNextUrl(requestPreviousNextUrl, getBrowserRouterState().bfcacheIds);
879
907
  setPendingPathname(url.pathname, navId);
880
- const routerStateAtNavStart = getBrowserRouterState();
881
- const elementsAtNavStart = routerStateAtNavStart.elements;
882
- const mountedSlotsHeader = getMountedSlotIdsHeader(elementsAtNavStart);
883
908
  const earlyIntentDecision = navigationKind === "navigate" ? navigationPlanner.classifyEarlyNavigationIntent({
884
909
  basePath: __basePath,
885
- currentHref: clientNavigationSnapshotHref(routerStateAtNavStart.navigationSnapshot),
910
+ currentHref: clientNavigationSnapshotHref(navigationInitiationState.navigationSnapshot),
886
911
  mode: "push",
887
912
  scroll: false,
888
913
  targetHref: url.href
889
914
  }) : null;
890
915
  const shouldBypassNavigationCache = earlyIntentDecision?.kind === "flightNavigation" && earlyIntentDecision.bypassNavigationCache;
891
916
  const requestHeaders = createRscRequestHeaders({
917
+ fetchPriority: "auto",
892
918
  interceptionContext: requestInterceptionContext,
893
919
  mountedSlotsHeader
894
920
  });
895
- const rscUrl = await createRscRequestUrl(url.pathname + url.search, requestHeaders);
896
921
  const rewrittenNavigationHref = navigationKind === "navigate" && HAS_CLIENT_REWRITES ? resolveLoadedHybridClientRewriteHref(currentHref, __basePath) : null;
897
- const additionalPrefetchRscUrls = rewrittenNavigationHref && rewrittenNavigationHref !== currentHref ? [await createRscRequestUrl(rewrittenNavigationHref, requestHeaders)] : [];
922
+ const targetPathAndSearch = url.pathname + url.search;
923
+ const additionalPrefetchPathAndSearch = rewrittenNavigationHref && rewrittenNavigationHref !== currentHref ? [rewrittenNavigationHref] : [];
924
+ const settledPrefetchedResponse = navigationKind === "navigate" && !shouldBypassNavigationCache ? consumePrefetchResponse(targetPathAndSearch, requestInterceptionContext, mountedSlotsHeader, { additionalRscUrls: additionalPrefetchPathAndSearch }) : null;
925
+ const rscUrl = settledPrefetchedResponse ? resolvePrefetchNavigationResponseUrl({
926
+ additionalRscUrls: additionalPrefetchPathAndSearch,
927
+ origin: window.location.origin,
928
+ responseUrl: settledPrefetchedResponse.url,
929
+ visibleRscUrl: targetPathAndSearch
930
+ }) : await createRscRequestUrl(targetPathAndSearch, requestHeaders);
931
+ const additionalPrefetchRscUrls = settledPrefetchedResponse ? additionalPrefetchPathAndSearch : await Promise.all(additionalPrefetchPathAndSearch.map((href) => createRscRequestUrl(href, requestHeaders)));
898
932
  const visitedResponseCandidate = shouldBypassNavigationCache ? {
899
933
  cacheKey: AppElementsWire.encodeCacheKey(rscUrl, requestInterceptionContext),
900
934
  entry: null,
@@ -911,7 +945,7 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
911
945
  visitedResponse
912
946
  });
913
947
  let routeManifest = navigationKind === "navigate" ? getBrowserRouteManifest() : null;
914
- const hasPrefetchCandidate = prefetchProbeDecision.kind === "probe" && hasPrefetchCacheEntryForNavigation(rscUrl, requestInterceptionContext, mountedSlotsHeader, {
948
+ const hasPrefetchCandidate = settledPrefetchedResponse !== null || prefetchProbeDecision.kind === "probe" && hasPrefetchCacheEntryForNavigation(rscUrl, requestInterceptionContext, mountedSlotsHeader, {
915
949
  additionalRscUrls: additionalPrefetchRscUrls,
916
950
  notifyInvalidation: false
917
951
  });
@@ -960,7 +994,26 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
960
994
  const cachedNavigationSnapshot = createClientNavigationRenderSnapshot(currentHref, cachedParams);
961
995
  const cachedPayload = cachedRoute.elements ? Promise.resolve(cachedRoute.elements) : decodeAppElementsPromise(createFromFetch(Promise.resolve(restoreRscResponse(cachedRoute.response))));
962
996
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
963
- if (await renderNavigationPayload(cachedPayload, cachedNavigationSnapshot, currentHref, navId, currentHistoryMode, cachedParams, requestPreviousNextUrl, detachedNavigationCommits ? null : pendingRouterState, cachedRoute.elements ? COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN : VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, toActionType(navigationKind), toOperationLane(navigationKind), activeTraversalIntent, scrollIntent, restoredBfcacheIds, reuseCurrentBfcacheIds, visibleCommitMode) === "no-commit") {
997
+ if (await renderNavigationPayload({
998
+ actionType: toActionType(navigationKind),
999
+ historyUpdateMode: currentHistoryMode,
1000
+ navigationCommitKind: detachedNavigationCommits ? "authoritative" : void 0,
1001
+ navigationInitiationState,
1002
+ navigationSnapshot: cachedNavigationSnapshot,
1003
+ navId,
1004
+ operationLane: toOperationLane(navigationKind),
1005
+ params: cachedParams,
1006
+ payload: cachedPayload,
1007
+ payloadOrigin: cachedRoute.elements ? COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN : VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN,
1008
+ pendingRouterState: detachedNavigationCommits ? null : pendingRouterState,
1009
+ previousNextUrl: requestPreviousNextUrl,
1010
+ restoredBfcacheIds,
1011
+ reuseCurrentBfcacheIds,
1012
+ scrollIntent,
1013
+ targetHref: currentHref,
1014
+ traversalIntent: activeTraversalIntent,
1015
+ visibleCommitMode
1016
+ }) === "no-commit") {
964
1017
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
965
1018
  deleteVisitedResponse(rscUrl, requestInterceptionContext);
966
1019
  continue;
@@ -970,17 +1023,24 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
970
1023
  let navResponse;
971
1024
  let navResponseExpiresAt;
972
1025
  let navResponseUrl = null;
1026
+ let prefetchedElements;
973
1027
  let fallbackReuseDecision = reuseDecision;
974
1028
  if (reuseDecision.kind === "consumePrefetch") {
975
- const prefetchedResponse = await consumePrefetchResponseForNavigation(rscUrl, requestInterceptionContext, mountedSlotsHeader, {
1029
+ const prefetchedResponse = settledPrefetchedResponse ?? await consumePrefetchResponseForNavigation(rscUrl, requestInterceptionContext, mountedSlotsHeader, {
976
1030
  additionalRscUrls: additionalPrefetchRscUrls,
977
1031
  shouldConsume: () => browserNavigationController.isCurrentNavigation(navId)
978
1032
  });
979
1033
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
980
1034
  if (prefetchedResponse) {
1035
+ prefetchedElements = prefetchedResponse.preparedElements;
981
1036
  navResponse = restoreRscResponse(prefetchedResponse, false);
982
1037
  navResponseExpiresAt = prefetchedResponse.expiresAt;
983
- navResponseUrl = isAlternatePrefetchResponseUrl(prefetchedResponse.url, additionalPrefetchRscUrls) ? rscUrl : prefetchedResponse.url;
1038
+ navResponseUrl = resolvePrefetchNavigationResponseUrl({
1039
+ additionalRscUrls: additionalPrefetchRscUrls,
1040
+ origin: window.location.origin,
1041
+ responseUrl: prefetchedResponse.url,
1042
+ visibleRscUrl: rscUrl
1043
+ });
984
1044
  }
985
1045
  if (!navResponse) {
986
1046
  routeManifest = navigationKind === "navigate" ? getBrowserRouteManifest() : null;
@@ -1016,7 +1076,26 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
1016
1076
  if (optimisticPayload !== null) {
1017
1077
  detachedNavigationCommits = true;
1018
1078
  const optimisticNavigationSnapshot = createClientNavigationRenderSnapshot(currentHref, optimisticPayload.params);
1019
- renderNavigationPayload(Promise.resolve(optimisticPayload.elements), optimisticNavigationSnapshot, currentHref, navId, currentHistoryMode, optimisticPayload.params, requestPreviousNextUrl, null, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, toActionType(navigationKind), toOperationLane(navigationKind), activeTraversalIntent, scrollIntent, restoredBfcacheIds, reuseCurrentBfcacheIds, visibleCommitMode, void 0, "detached").catch((error) => {
1079
+ renderNavigationPayload({
1080
+ actionType: toActionType(navigationKind),
1081
+ historyUpdateMode: currentHistoryMode,
1082
+ navigationCommitKind: "detached",
1083
+ navigationInitiationState,
1084
+ navigationSnapshot: optimisticNavigationSnapshot,
1085
+ navId,
1086
+ operationLane: toOperationLane(navigationKind),
1087
+ params: optimisticPayload.params,
1088
+ payload: Promise.resolve(optimisticPayload.elements),
1089
+ payloadOrigin: FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN,
1090
+ pendingRouterState: null,
1091
+ previousNextUrl: requestPreviousNextUrl,
1092
+ restoredBfcacheIds,
1093
+ reuseCurrentBfcacheIds,
1094
+ scrollIntent,
1095
+ targetHref: currentHref,
1096
+ traversalIntent: activeTraversalIntent,
1097
+ visibleCommitMode
1098
+ }).catch((error) => {
1020
1099
  if (browserNavigationController.isCurrentNavigation(navId)) console.error("[vinext] Optimistic RSC navigation error:", error);
1021
1100
  });
1022
1101
  }
@@ -1024,12 +1103,13 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
1024
1103
  }
1025
1104
  if (!navResponse) {
1026
1105
  if (navigationKind === "navigate") {
1027
- const clientReuseManifestHeader = createClientReuseManifestHeaderFromVisibleAppState(routerStateAtNavStart);
1106
+ const clientReuseManifestHeader = createClientReuseManifestHeaderFromVisibleAppState(navigationInitiationState);
1028
1107
  if (clientReuseManifestHeader !== null) requestHeaders.set(VINEXT_CLIENT_REUSE_MANIFEST_HEADER, clientReuseManifestHeader);
1029
1108
  }
1030
1109
  navResponse = await fetch(rscUrl, {
1031
1110
  headers: requestHeaders,
1032
1111
  credentials: "include",
1112
+ priority: "auto",
1033
1113
  signal: navigationAbortController.signal
1034
1114
  });
1035
1115
  }
@@ -1083,13 +1163,35 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
1083
1163
  const cacheBufferPromise = new Response(cacheBranch).arrayBuffer();
1084
1164
  cacheBufferPromise.catch(() => {});
1085
1165
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
1086
- const rscPayload = decodeAppElementsPromise(createFromFetch(Promise.resolve(reactResponse)));
1166
+ if (prefetchedElements) reactBranch.cancel().catch(() => {});
1167
+ const rscPayload = prefetchedElements ? prefetchedElements : decodeAppElementsPromise(createFromFetch(Promise.resolve(reactResponse)));
1087
1168
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
1088
1169
  let committedState = null;
1089
- if (await renderNavigationPayload(rscPayload, navigationSnapshot, currentHref, navId, currentHistoryMode, navParams, requestPreviousNextUrl, detachedNavigationCommits ? null : pendingRouterState, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, toActionType(navigationKind), toOperationLane(navigationKind), activeTraversalIntent, scrollIntent, restoredBfcacheIds, reuseCurrentBfcacheIds, visibleCommitMode, (state) => {
1090
- committedState = state;
1091
- if (activeNavigationAbortController === navigationAbortController) activeNavigationAbortController = null;
1092
- }, detachedNavigationCommits ? "authoritative" : void 0) !== "committed") return;
1170
+ if (await renderNavigationPayload({
1171
+ actionType: toActionType(navigationKind),
1172
+ historyUpdateMode: currentHistoryMode,
1173
+ navigationCommitKind: detachedNavigationCommits ? "authoritative" : void 0,
1174
+ navigationInitiationState,
1175
+ navigationSnapshot,
1176
+ navId,
1177
+ onCommittedState: (state) => {
1178
+ committedState = state;
1179
+ if (activeNavigationAbortController === navigationAbortController) activeNavigationAbortController = null;
1180
+ },
1181
+ operationLane: toOperationLane(navigationKind),
1182
+ params: navParams,
1183
+ payload: rscPayload,
1184
+ payloadOrigin: FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN,
1185
+ pendingRouterState: detachedNavigationCommits ? null : pendingRouterState,
1186
+ previousNextUrl: requestPreviousNextUrl,
1187
+ restoredBfcacheIds,
1188
+ reuseCurrentBfcacheIds,
1189
+ scrollIntent,
1190
+ targetHref: currentHref,
1191
+ traversalIntent: activeTraversalIntent,
1192
+ visibleCommitMode: prefetchedElements ? "synchronous" : visibleCommitMode
1193
+ }) !== "committed") return;
1194
+ committedState ??= getBrowserRouterState();
1093
1195
  try {
1094
1196
  const renderedElements = await rscPayload;
1095
1197
  if (navigationCacheGeneration !== clientNavigationCacheGeneration) return;
@@ -1110,7 +1212,7 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
1110
1212
  if (isCacheRestorableAppPayloadMetadata(metadata)) {
1111
1213
  if (navigationCacheGeneration !== clientNavigationCacheGeneration) return;
1112
1214
  storeVisitedResponseSnapshot(rscUrl, interceptionContext, snapshot, navParams, PREFETCH_CACHE_TTL, mountedSlotsHeader);
1113
- } else if (committedState !== null) {
1215
+ } else {
1114
1216
  const state = committedState;
1115
1217
  const committedElements = {
1116
1218
  ...state.elements,
@@ -1121,9 +1223,6 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
1121
1223
  };
1122
1224
  if (navigationCacheGeneration !== clientNavigationCacheGeneration) return;
1123
1225
  storeVisitedResponseSnapshot(rscUrl, interceptionContext, snapshot, navParams, DYNAMIC_NAVIGATION_CACHE_TTL, mountedSlotsHeader, committedElements);
1124
- } else {
1125
- if (navigationCacheGeneration !== clientNavigationCacheGeneration) return;
1126
- seedPrefetchResponseSnapshot(rscUrl, snapshot, interceptionContext, mountedSlotsHeader, DYNAMIC_NAVIGATION_CACHE_TTL);
1127
1226
  }
1128
1227
  } catch {}
1129
1228
  return;
@@ -1137,7 +1236,8 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
1137
1236
  browserNavigationController.finalizeNavigation(navId, pendingRouterState);
1138
1237
  discardedServerActionRefreshScheduler.markNavigationSettled();
1139
1238
  }
1140
- }
1239
+ },
1240
+ preparePrefetchResponse: (response) => decodeAppElementsPromise(createFromFetch(Promise.resolve(response)))
1141
1241
  });
1142
1242
  const handlePopstate = createPopstateRestoreHandler({
1143
1243
  getActiveNavigationId: browserNavigationController.getActiveNavigationId.bind(browserNavigationController),
@@ -43,6 +43,28 @@ type BrowserNavigationControllerDeps = {
43
43
  getRouteManifest?: () => RouteManifest | null;
44
44
  syncHistoryStatePreviousNextUrl?: (previousNextUrl: string | null, bfcacheIds?: Readonly<Record<string, string>> | null) => void;
45
45
  };
46
+ type BrowserNavigationPayloadOptions = {
47
+ actionType: "navigate" | "replace" | "traverse";
48
+ createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;
49
+ historyUpdateMode: HistoryUpdateMode | undefined;
50
+ navigationCommitKind?: "authoritative" | "detached";
51
+ navigationInitiationState: AppRouterState;
52
+ navigationSnapshot: ClientNavigationRenderSnapshot;
53
+ navId: number;
54
+ nextElements: Promise<AppElements> | AppElements;
55
+ onCommittedState?: (state: AppRouterState) => void;
56
+ operationLane: OperationLane;
57
+ params: Record<string, string | string[]>;
58
+ payloadOrigin: AppNavigationPayloadOrigin;
59
+ pendingRouterState: PendingBrowserRouterState | null;
60
+ previousNextUrl: string | null;
61
+ restoredBfcacheIds?: Readonly<Record<string, string>> | null;
62
+ reuseCurrentBfcacheIds?: boolean;
63
+ scrollIntent?: AppRouterScrollIntent | null;
64
+ targetHistoryIndex?: number | null;
65
+ targetHref: string;
66
+ visibleCommitMode?: NavigationRuntimeVisibleCommitMode;
67
+ };
46
68
  type BrowserNavigationController = {
47
69
  beginNavigation(): number;
48
70
  getActiveNavigationId(): number;
@@ -60,27 +82,7 @@ type BrowserNavigationController = {
60
82
  state: AppRouterState;
61
83
  targetHref: string;
62
84
  }): boolean;
63
- renderNavigationPayload(options: {
64
- actionType: "navigate" | "replace" | "traverse";
65
- createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;
66
- historyUpdateMode: HistoryUpdateMode | undefined;
67
- navigationSnapshot: ClientNavigationRenderSnapshot;
68
- nextElements: Promise<AppElements>;
69
- operationLane: OperationLane;
70
- payloadOrigin: AppNavigationPayloadOrigin;
71
- params: Record<string, string | string[]>;
72
- pendingRouterState: PendingBrowserRouterState | null;
73
- previousNextUrl: string | null;
74
- scrollIntent?: AppRouterScrollIntent | null;
75
- restoredBfcacheIds?: Readonly<Record<string, string>> | null;
76
- reuseCurrentBfcacheIds?: boolean;
77
- targetHistoryIndex?: number | null;
78
- targetHref: string;
79
- navId: number;
80
- navigationCommitKind?: "authoritative" | "detached";
81
- visibleCommitMode?: NavigationRuntimeVisibleCommitMode;
82
- onCommittedState?: (state: AppRouterState) => void;
83
- }): Promise<NavigationPayloadOutcome>;
85
+ renderNavigationPayload(options: BrowserNavigationPayloadOptions): Promise<NavigationPayloadOutcome>;
84
86
  commitSameUrlNavigatePayload(nextElements: Promise<AppElements>, navigationSnapshot: ClientNavigationRenderSnapshot, returnValue?: {
85
87
  ok: boolean;
86
88
  data: unknown;
@@ -3,7 +3,7 @@ import { clearAppNavigationFailureTarget, getAppNavigationFailureTarget } from "
3
3
  import { claimAppRouterScrollIntentForCommit, consumeAppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
4
4
  import { resolveServerActionOperationLane, shouldScheduleRefreshForDiscardedServerAction } from "./app-browser-action-result.js";
5
5
  import { activateNavigationSnapshot, clearPendingPathname, commitClientNavigationState, createSnapshotPathAndSearch } from "../shims/navigation.js";
6
- import { FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, createPendingNavigationCommit } from "./app-browser-state.js";
6
+ import { FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, createPendingNavigationCommit, createPendingNavigationCommitFromElements } from "./app-browser-state.js";
7
7
  import { applyApprovedVisibleCommit, approveHmrVisibleCommit, approvePendingNavigationCommit, resolveAndClassifyNavigationCommit } from "./app-browser-visible-commit.js";
8
8
  import { startTransition, useInsertionEffect, useLayoutEffect } from "react";
9
9
  import { flushSync } from "react-dom";
@@ -255,6 +255,9 @@ function createAppBrowserNavigationController(deps = {}) {
255
255
  pendingRouterState.settled = true;
256
256
  pendingRouterState.resolve(committedState);
257
257
  if (activePendingBrowserRouterState === pendingRouterState) activePendingBrowserRouterState = null;
258
+ if (visibleCommitMode === "synchronous") flushSync(() => {
259
+ setter(committedState);
260
+ });
258
261
  return;
259
262
  }
260
263
  if (visibleCommitMode === "synchronous") {
@@ -332,6 +335,22 @@ function createAppBrowserNavigationController(deps = {}) {
332
335
  if (!shouldScheduleRefreshForDiscardedServerAction(lifecycleOptions?.revalidation ?? "none")) return;
333
336
  lifecycleOptions?.onDiscardedRevalidation?.();
334
337
  }
338
+ function isSamePathSearchHashCommit(targetHref) {
339
+ if (typeof window === "undefined") return false;
340
+ try {
341
+ const currentUrl = new URL(window.location.href);
342
+ const targetUrl = new URL(targetHref, currentUrl.href);
343
+ return targetUrl.origin === currentUrl.origin && targetUrl.pathname === currentUrl.pathname && targetUrl.search !== currentUrl.search && targetUrl.hash !== "";
344
+ } catch {
345
+ return false;
346
+ }
347
+ }
348
+ function shouldForceSynchronousCommit(options) {
349
+ if (options.actionType === "traverse") return false;
350
+ if (options.historyUpdateMode === void 0) return false;
351
+ if (options.scrollIntent?.hash == null) return false;
352
+ return isSamePathSearchHashCommit(options.targetHref);
353
+ }
335
354
  async function renderNavigationPayload(options) {
336
355
  if (options.navId === pendingUserNavigationId) pendingUserNavigationLane = options.operationLane;
337
356
  const renderId = allocateRenderId();
@@ -348,11 +367,10 @@ function createAppBrowserNavigationController(deps = {}) {
348
367
  });
349
368
  let snapshotActivated = false;
350
369
  try {
351
- const pending = await createPendingNavigationCommit({
352
- currentState: getBrowserRouterState(),
370
+ const pendingOptions = {
371
+ currentState: options.navigationInitiationState,
353
372
  navigationCommitKind: options.navigationCommitKind,
354
373
  navigationId: options.navId,
355
- nextElements: options.nextElements,
356
374
  navigationSnapshot: options.navigationSnapshot,
357
375
  operationLane: options.operationLane,
358
376
  payloadOrigin: options.payloadOrigin,
@@ -361,6 +379,13 @@ function createAppBrowserNavigationController(deps = {}) {
361
379
  restoredBfcacheIds: options.restoredBfcacheIds,
362
380
  reuseCurrentBfcacheIds: options.reuseCurrentBfcacheIds,
363
381
  type: options.actionType
382
+ };
383
+ const pending = options.nextElements instanceof Promise ? await createPendingNavigationCommit({
384
+ ...pendingOptions,
385
+ nextElements: options.nextElements
386
+ }) : createPendingNavigationCommitFromElements({
387
+ ...pendingOptions,
388
+ nextElements: options.nextElements
364
389
  });
365
390
  const approval = approvePendingNavigationCommit({
366
391
  activeNavigationId,
@@ -402,7 +427,7 @@ function createAppBrowserNavigationController(deps = {}) {
402
427
  claimAppRouterScrollIntentForCommit(options.scrollIntent, renderId);
403
428
  activateNavigationSnapshot();
404
429
  snapshotActivated = true;
405
- dispatchApprovedVisibleCommit(renderId, approvedCommit, options.pendingRouterState, options.visibleCommitMode ?? "transition");
430
+ dispatchApprovedVisibleCommit(renderId, approvedCommit, options.pendingRouterState, shouldForceSynchronousCommit(options) ? "synchronous" : options.visibleCommitMode ?? "transition");
406
431
  } catch (error) {
407
432
  pendingNavigationFailureTargets.delete(renderId);
408
433
  pendingNavigationPrePaintEffects.delete(renderId);
@@ -0,0 +1,9 @@
1
+ //#region src/server/app-browser-prefetch-response.d.ts
2
+ declare function resolvePrefetchNavigationResponseUrl(options: {
3
+ additionalRscUrls: readonly string[];
4
+ origin: string;
5
+ responseUrl: string;
6
+ visibleRscUrl: string;
7
+ }): string;
8
+ //#endregion
9
+ export { resolvePrefetchNavigationResponseUrl };
@@ -0,0 +1,32 @@
1
+ import { stripRscCacheBustingSearchParam } from "./app-rsc-cache-busting.js";
2
+ //#region src/server/app-browser-prefetch-response.ts
3
+ function normalizeBrowserRscUrlForReuse(url, origin) {
4
+ if (!url) return null;
5
+ try {
6
+ const parsed = new URL(url, origin);
7
+ stripRscCacheBustingSearchParam(parsed);
8
+ return `${parsed.pathname}${parsed.search}`;
9
+ } catch {
10
+ return null;
11
+ }
12
+ }
13
+ function isSameOriginBrowserRscUrl(url, origin) {
14
+ try {
15
+ return new URL(url, origin).origin === new URL(origin).origin;
16
+ } catch {
17
+ return false;
18
+ }
19
+ }
20
+ function canonicalizeBrowserRscResponseUrl(responseUrl, origin) {
21
+ try {
22
+ const parsed = new URL(responseUrl, origin);
23
+ if (parsed.origin === new URL(origin).origin) return `${parsed.pathname}${parsed.search}`;
24
+ } catch {}
25
+ return responseUrl;
26
+ }
27
+ function resolvePrefetchNavigationResponseUrl(options) {
28
+ const normalizedResponseUrl = normalizeBrowserRscUrlForReuse(options.responseUrl, options.origin);
29
+ return normalizedResponseUrl !== null && isSameOriginBrowserRscUrl(options.responseUrl, options.origin) && options.additionalRscUrls.some((additionalRscUrl) => normalizeBrowserRscUrlForReuse(additionalRscUrl, options.origin) === normalizedResponseUrl) ? options.visibleRscUrl : canonicalizeBrowserRscResponseUrl(options.responseUrl, options.origin);
30
+ }
31
+ //#endregion
32
+ export { resolvePrefetchNavigationResponseUrl };