vinext 0.1.0 → 0.1.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 (119) hide show
  1. package/dist/build/assets-ignore.d.ts +32 -0
  2. package/dist/build/assets-ignore.js +48 -0
  3. package/dist/build/client-build-config.d.ts +27 -1
  4. package/dist/build/client-build-config.js +58 -1
  5. package/dist/cli.js +2 -0
  6. package/dist/client/navigation-runtime.d.ts +8 -0
  7. package/dist/client/navigation-runtime.js +1 -1
  8. package/dist/client/vinext-next-data.d.ts +2 -1
  9. package/dist/config/config-matchers.d.ts +20 -1
  10. package/dist/config/config-matchers.js +35 -1
  11. package/dist/config/next-config.d.ts +16 -3
  12. package/dist/config/next-config.js +30 -2
  13. package/dist/deploy.js +40 -304
  14. package/dist/entries/app-rsc-entry.d.ts +8 -2
  15. package/dist/entries/app-rsc-entry.js +54 -4
  16. package/dist/entries/app-rsc-manifest.js +20 -2
  17. package/dist/entries/pages-server-entry.js +9 -1
  18. package/dist/index.js +162 -217
  19. package/dist/plugins/postcss.js +18 -14
  20. package/dist/plugins/require-context.d.ts +6 -0
  21. package/dist/plugins/require-context.js +184 -0
  22. package/dist/routing/app-route-graph.d.ts +12 -1
  23. package/dist/routing/app-route-graph.js +137 -5
  24. package/dist/routing/route-pattern.d.ts +2 -1
  25. package/dist/routing/route-pattern.js +16 -1
  26. package/dist/server/api-handler.js +4 -0
  27. package/dist/server/app-browser-entry.js +84 -39
  28. package/dist/server/app-browser-interception-context.d.ts +2 -1
  29. package/dist/server/app-browser-interception-context.js +15 -2
  30. package/dist/server/app-browser-navigation-controller.d.ts +11 -1
  31. package/dist/server/app-browser-navigation-controller.js +77 -1
  32. package/dist/server/app-browser-popstate.d.ts +12 -3
  33. package/dist/server/app-browser-popstate.js +19 -4
  34. package/dist/server/app-browser-state.d.ts +3 -0
  35. package/dist/server/app-browser-state.js +6 -3
  36. package/dist/server/app-browser-visible-commit.js +9 -7
  37. package/dist/server/app-history-state.d.ts +45 -1
  38. package/dist/server/app-history-state.js +109 -1
  39. package/dist/server/app-page-boundary-render.js +41 -19
  40. package/dist/server/app-page-dispatch.d.ts +6 -0
  41. package/dist/server/app-page-dispatch.js +3 -1
  42. package/dist/server/app-page-element-builder.d.ts +1 -0
  43. package/dist/server/app-page-element-builder.js +22 -10
  44. package/dist/server/app-page-render.d.ts +6 -0
  45. package/dist/server/app-page-render.js +5 -3
  46. package/dist/server/app-page-request.d.ts +8 -6
  47. package/dist/server/app-page-request.js +12 -9
  48. package/dist/server/app-page-response.d.ts +2 -2
  49. package/dist/server/app-page-response.js +1 -1
  50. package/dist/server/app-page-route-wiring.js +2 -1
  51. package/dist/server/app-page-stream.d.ts +37 -2
  52. package/dist/server/app-page-stream.js +36 -3
  53. package/dist/server/app-pages-bridge.d.ts +16 -0
  54. package/dist/server/app-pages-bridge.js +23 -3
  55. package/dist/server/app-route-handler-cache.d.ts +1 -0
  56. package/dist/server/app-route-handler-cache.js +1 -0
  57. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  58. package/dist/server/app-route-handler-dispatch.js +2 -0
  59. package/dist/server/app-route-handler-execution.d.ts +1 -0
  60. package/dist/server/app-route-handler-execution.js +1 -0
  61. package/dist/server/app-route-handler-runtime.d.ts +1 -0
  62. package/dist/server/app-route-handler-runtime.js +3 -2
  63. package/dist/server/app-rsc-handler.d.ts +1 -0
  64. package/dist/server/app-rsc-handler.js +4 -3
  65. package/dist/server/app-rsc-route-matching.d.ts +20 -1
  66. package/dist/server/app-rsc-route-matching.js +29 -4
  67. package/dist/server/app-server-action-execution.d.ts +11 -1
  68. package/dist/server/app-server-action-execution.js +68 -10
  69. package/dist/server/app-ssr-entry.d.ts +6 -0
  70. package/dist/server/app-ssr-entry.js +17 -1
  71. package/dist/server/dev-server.d.ts +1 -1
  72. package/dist/server/dev-server.js +54 -31
  73. package/dist/server/isr-cache.d.ts +37 -1
  74. package/dist/server/isr-cache.js +85 -1
  75. package/dist/server/navigation-planner.js +5 -3
  76. package/dist/server/navigation-trace.d.ts +1 -1
  77. package/dist/server/pages-node-compat.d.ts +2 -0
  78. package/dist/server/pages-node-compat.js +4 -0
  79. package/dist/server/pages-page-data.d.ts +10 -7
  80. package/dist/server/pages-page-data.js +4 -2
  81. package/dist/server/pages-page-handler.d.ts +9 -2
  82. package/dist/server/pages-page-handler.js +29 -16
  83. package/dist/server/pages-page-response.d.ts +11 -2
  84. package/dist/server/pages-page-response.js +8 -1
  85. package/dist/server/pages-readiness.d.ts +36 -0
  86. package/dist/server/pages-readiness.js +21 -0
  87. package/dist/server/pages-request-pipeline.d.ts +99 -0
  88. package/dist/server/pages-request-pipeline.js +209 -0
  89. package/dist/server/pages-revalidate.d.ts +15 -0
  90. package/dist/server/pages-revalidate.js +19 -0
  91. package/dist/server/prod-server.d.ts +6 -2
  92. package/dist/server/prod-server.js +101 -217
  93. package/dist/server/socket-error-backstop.d.ts +19 -1
  94. package/dist/server/socket-error-backstop.js +77 -4
  95. package/dist/shims/app-router-scroll.js +22 -4
  96. package/dist/shims/cache-runtime.js +31 -1
  97. package/dist/shims/error-boundary.d.ts +21 -11
  98. package/dist/shims/error-boundary.js +8 -1
  99. package/dist/shims/fetch-cache.d.ts +14 -1
  100. package/dist/shims/fetch-cache.js +18 -1
  101. package/dist/shims/hash-scroll.d.ts +1 -0
  102. package/dist/shims/hash-scroll.js +3 -1
  103. package/dist/shims/internal/link-status-registry.d.ts +43 -0
  104. package/dist/shims/internal/link-status-registry.js +42 -0
  105. package/dist/shims/internal/route-pattern-for-warning.d.ts +27 -0
  106. package/dist/shims/internal/route-pattern-for-warning.js +40 -0
  107. package/dist/shims/internal/utils.d.ts +1 -0
  108. package/dist/shims/link.js +20 -6
  109. package/dist/shims/navigation.d.ts +2 -2
  110. package/dist/shims/navigation.js +63 -7
  111. package/dist/shims/router-state.d.ts +1 -0
  112. package/dist/shims/router-state.js +2 -0
  113. package/dist/shims/router.d.ts +6 -3
  114. package/dist/shims/router.js +128 -21
  115. package/dist/utils/client-build-manifest.d.ts +8 -1
  116. package/dist/utils/client-build-manifest.js +30 -5
  117. package/dist/utils/client-entry-manifest.d.ts +11 -0
  118. package/dist/utils/client-entry-manifest.js +29 -0
  119. package/package.json +5 -1
@@ -9,8 +9,8 @@ import { installWindowNext } from "../client/window-next.js";
9
9
  import { retryScrollTo, scrollToHashTargetOnNextFrame } from "../shims/hash-scroll.js";
10
10
  import { getNavigationRuntime, registerNavigationRuntimeBootstrap, registerNavigationRuntimeFunctions } from "../client/navigation-runtime.js";
11
11
  import { notifyAppRouterTransitionStart } from "../client/instrumentation-client-state.js";
12
- import { resolveManifestNavigationInterceptionContext } from "./app-browser-interception-context.js";
13
- import { createHistoryStateWithNavigationMetadata, isHistoryStateBfcacheVersionCurrent, readHistoryStateBfcacheIds, readHistoryStateBfcacheVersion, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent } from "./app-history-state.js";
12
+ import { resolveManifestNavigationInterceptionContext, resolveMiddlewareRewriteNavigationInterceptionContext } from "./app-browser-interception-context.js";
13
+ import { RestorableClientStateController, createHistoryStateWithNavigationMetadata, readHistoryStateBfcacheIds, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent } from "./app-history-state.js";
14
14
  import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, getVinextRscCompatibilityId, resolveHardNavigationTargetFromRscResponse, resolveRscCompatibilityNavigationDecision } from "./app-rsc-cache-busting.js";
15
15
  import { AppBrowserMpaNavigationScheduler } from "./app-browser-mpa-navigation.js";
16
16
  import { beginAppRouterScrollIntent, consumeAppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
@@ -23,10 +23,10 @@ import { createDiscardedServerActionRefreshScheduler, createServerActionInitiati
23
23
  import { createClientReuseManifestHeaderFromVisibleAppState } from "./app-browser-client-reuse-manifest.js";
24
24
  import { chunksToReadableStream, createProgressiveRscStream, getVinextBrowserGlobal } from "./app-browser-stream.js";
25
25
  import { FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, createBfcacheSegmentStateKeyMap, createInitialBfcacheIdMap, isCacheRestorableAppPayloadMetadata, resolveInterceptionContextFromPreviousNextUrl, resolveServerActionRequestState } from "./app-browser-state.js";
26
- import { clearHardNavigationLoopGuard, createAppBrowserNavigationController } from "./app-browser-navigation-controller.js";
26
+ import { clearHardNavigationLoopGuard, createAppBrowserNavigationController, createBasePathStrippedPathAndSearch, createSnapshotPathAndSearch } from "./app-browser-navigation-controller.js";
27
27
  import { consumeInitialFormState, createVinextHydrateRootOptions, hydrateRootInTransition } from "./app-browser-hydration.js";
28
28
  import { createVisitedResponseCacheEntry, isVisitedResponseCacheEntryFresh } from "./app-visited-response-cache.js";
29
- import { createPopstateRestoreHandler } from "./app-browser-popstate.js";
29
+ import { createPopstateRestoreHandler, restoreSynchronousPopstateScrollPosition } from "./app-browser-popstate.js";
30
30
  import { createOnUncaughtError } from "./app-browser-error.js";
31
31
  import { dismissOverlay } from "./dev-error-overlay-store.js";
32
32
  import { devOnCaughtError, devOnUncaughtError, installDevErrorOverlay, installViteHmrErrorHandler, reportInitialDevServerErrors } from "./dev-error-overlay.js";
@@ -35,6 +35,7 @@ import { resolveRscRedirectLifecycleHop } from "./app-browser-rsc-redirect.js";
35
35
  import { createOptimisticRouteTemplate, getOptimisticPrefetchSourceKey, getOptimisticRouteTemplateKey, resolveOptimisticNavigationPayload } from "./app-optimistic-routing.js";
36
36
  import { removeStylesheetLinksCoveredByInlineCss } from "./app-inline-css-client.js";
37
37
  import { createElement, startTransition, use, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
38
+ import { flushSync } from "react-dom";
38
39
  import { createFromFetch, createFromReadableStream, createTemporaryReferenceSet, encodeReply, setServerCallback } from "@vitejs/plugin-rsc/browser";
39
40
  import { hydrateRoot } from "react-dom/client";
40
41
  //#region src/server/app-browser-entry.ts
@@ -58,6 +59,7 @@ function getBrowserRouteManifest() {
58
59
  return getNavigationRuntime()?.bootstrap.routeManifest ?? null;
59
60
  }
60
61
  const browserNavigationController = createAppBrowserNavigationController({
62
+ basePath: __basePath,
61
63
  getRouteManifest: getBrowserRouteManifest,
62
64
  syncHistoryStatePreviousNextUrl: syncCurrentHistoryStatePreviousNextUrl
63
65
  });
@@ -96,26 +98,18 @@ const mpaNavigationScheduler = new AppBrowserMpaNavigationScheduler();
96
98
  const unresolvedMpaNavigation = new Promise(() => {});
97
99
  const RSC_HMR_SETTLE_DELAY_MS = 150;
98
100
  let latestRscHmrUpdateId = 0;
99
- const initialHistoryBfcacheVersion = readHistoryStateBfcacheVersion(window.history.state);
100
- let currentBfcacheVersion = initialHistoryBfcacheVersion === null ? 0 : initialHistoryBfcacheVersion + 1;
101
101
  let currentHistoryTraversalIndex = readHistoryStateTraversalIndex(window.history.state) ?? 0;
102
102
  let nextHistoryTraversalIndex = currentHistoryTraversalIndex;
103
- function isCurrentBfcacheVersion(state) {
104
- return isHistoryStateBfcacheVersionCurrent(state, currentBfcacheVersion);
105
- }
103
+ let synchronousPopstateScrollRestoreNavigationId = null;
104
+ const restorableClientState = new RestorableClientStateController({
105
+ initialHistoryState: window.history.state,
106
+ maxHistoryStateSnapshots: 50
107
+ });
106
108
  function waitForRscHmrSettle(delayMs = RSC_HMR_SETTLE_DELAY_MS) {
107
109
  return new Promise((resolve) => {
108
110
  window.setTimeout(resolve, delayMs);
109
111
  });
110
112
  }
111
- function readCurrentBfcacheVersionHistoryIds(state) {
112
- const ids = readHistoryStateBfcacheIds(state);
113
- if (ids === null) return null;
114
- return isCurrentBfcacheVersion(state) ? ids : null;
115
- }
116
- function invalidateRestorableBfcacheIds() {
117
- currentBfcacheVersion += 1;
118
- }
119
113
  function allocateNavigationHistoryTraversalIndex(historyUpdateMode) {
120
114
  switch (historyUpdateMode) {
121
115
  case "push": return nextHistoryTraversalIndex + 1;
@@ -131,10 +125,10 @@ function commitHistoryTraversalIndex(index) {
131
125
  function commitHashOnlyNavigation(href, historyUpdateMode, scroll) {
132
126
  const navigationHistoryIndex = allocateNavigationHistoryTraversalIndex(historyUpdateMode);
133
127
  const previousNextUrl = hasBrowserRouterState() ? getBrowserRouterState().previousNextUrl : readHistoryStatePreviousNextUrl(window.history.state);
134
- const bfcacheIds = hasBrowserRouterState() ? getBrowserRouterState().bfcacheIds : readCurrentBfcacheVersionHistoryIds(window.history.state);
128
+ const bfcacheIds = hasBrowserRouterState() ? getBrowserRouterState().bfcacheIds : restorableClientState.readCurrentBfcacheVersionHistoryIds(window.history.state);
135
129
  const historyState = createHistoryStateWithNavigationMetadata(createHashOnlyNavigationBaseHistoryState(historyUpdateMode, scroll), {
136
130
  bfcacheIds,
137
- bfcacheVersion: bfcacheIds === null ? void 0 : currentBfcacheVersion,
131
+ bfcacheVersion: bfcacheIds === null ? void 0 : restorableClientState.currentBfcacheVersion,
138
132
  previousNextUrl,
139
133
  traversalIndex: navigationHistoryIndex
140
134
  });
@@ -158,6 +152,26 @@ function stripVinextScrollState(state) {
158
152
  function commitTraversalIndexFromHistoryState(historyState) {
159
153
  commitHistoryTraversalIndex(readHistoryStateTraversalIndex(historyState));
160
154
  }
155
+ function restoreHistoryStateSnapshot(historyState) {
156
+ const decision = restorableClientState.resolveHistoryStateSnapshotRestore(historyState);
157
+ if (decision.kind === "skip") return false;
158
+ const navId = browserNavigationController.getActiveNavigationId();
159
+ let restored = false;
160
+ flushSync(() => {
161
+ restored = browserNavigationController.restoreHistorySnapshotVisibleState({
162
+ beforeCommit: () => {
163
+ commitHistoryTraversalIndex(decision.targetHistoryIndex);
164
+ stageClientParams(decision.state.navigationSnapshot.params);
165
+ },
166
+ navId,
167
+ state: decision.state,
168
+ targetHref: window.location.href
169
+ });
170
+ });
171
+ if (!restored) return false;
172
+ commitClientNavigationState();
173
+ return true;
174
+ }
161
175
  function getBrowserRouterState() {
162
176
  return browserNavigationController.getBrowserRouterState();
163
177
  }
@@ -190,7 +204,7 @@ function clearPrefetchState() {
190
204
  function clearClientNavigationCaches() {
191
205
  clearVisitedResponseCache();
192
206
  clearPrefetchState();
193
- invalidateRestorableBfcacheIds();
207
+ restorableClientState.invalidateClientState();
194
208
  }
195
209
  function isSettledPrefetchCacheEntry(entry) {
196
210
  return entry.outcome === "cache-seeded" && entry.pending === void 0 && entry.snapshot !== void 0;
@@ -267,18 +281,18 @@ function areBfcacheIdMapsEqual(a, b) {
267
281
  return aEntries.every(([key, value]) => b[key] === value);
268
282
  }
269
283
  function isHistoryStateNavigationMetadataInSync(state, previousNextUrl, bfcacheIds) {
270
- return readHistoryStatePreviousNextUrl(state) === previousNextUrl && (bfcacheIds === void 0 || areBfcacheIdMapsEqual(readHistoryStateBfcacheIds(state), bfcacheIds) && isCurrentBfcacheVersion(state));
284
+ return readHistoryStatePreviousNextUrl(state) === previousNextUrl && (bfcacheIds === void 0 || areBfcacheIdMapsEqual(readHistoryStateBfcacheIds(state), bfcacheIds) && restorableClientState.isCurrentBfcacheVersion(state));
271
285
  }
272
286
  function syncCurrentHistoryStatePreviousNextUrl(previousNextUrl, bfcacheIds) {
273
287
  if (isHistoryStateNavigationMetadataInSync(window.history.state, previousNextUrl, bfcacheIds)) return;
274
288
  const nextHistoryState = createHistoryStateWithNavigationMetadata(window.history.state, {
275
289
  bfcacheIds,
276
- bfcacheVersion: bfcacheIds === void 0 ? void 0 : currentBfcacheVersion,
290
+ bfcacheVersion: bfcacheIds === void 0 ? void 0 : restorableClientState.currentBfcacheVersion,
277
291
  previousNextUrl
278
292
  });
279
293
  replaceHistoryStateWithoutNotify(nextHistoryState, "", window.location.href);
280
294
  if (isHistoryStateNavigationMetadataInSync(window.history.state, previousNextUrl, bfcacheIds)) return;
281
- window.history.replaceState(nextHistoryState, "", window.location.href);
295
+ replaceHistoryStateWithoutNotify(nextHistoryState, "", window.location.href);
282
296
  }
283
297
  function createActionInitiationSnapshot() {
284
298
  const routerState = getBrowserRouterState();
@@ -300,7 +314,7 @@ function createNavigationCommitEffect(options) {
300
314
  const navigationHistoryIndex = targetHistoryIndex !== void 0 ? targetHistoryIndex : allocateNavigationHistoryTraversalIndex(historyUpdateMode);
301
315
  const historyState = createHistoryStateWithNavigationMetadata(preserveExistingState ? window.history.state : null, {
302
316
  bfcacheIds,
303
- bfcacheVersion: currentBfcacheVersion,
317
+ bfcacheVersion: restorableClientState.currentBfcacheVersion,
304
318
  previousNextUrl,
305
319
  traversalIndex: navigationHistoryIndex
306
320
  });
@@ -325,7 +339,7 @@ function createNavigationCommitEffect(options) {
325
339
  commitClientNavigationState(navId);
326
340
  };
327
341
  }
328
- async function renderNavigationPayload(payload, navigationSnapshot, targetHref, navId, historyUpdateMode, params, previousNextUrl, pendingRouterState, payloadOrigin, actionType = "navigate", operationLane = "navigation", traversalIntent = null, scrollIntent = null, restoredBfcacheIds = null) {
342
+ async function renderNavigationPayload(payload, navigationSnapshot, targetHref, navId, historyUpdateMode, params, previousNextUrl, pendingRouterState, payloadOrigin, actionType = "navigate", operationLane = "navigation", traversalIntent = null, scrollIntent = null, restoredBfcacheIds = null, reuseCurrentBfcacheIds = true) {
329
343
  syncServerActionHttpFallbackHead(null);
330
344
  try {
331
345
  return await browserNavigationController.renderNavigationPayload({
@@ -344,6 +358,7 @@ async function renderNavigationPayload(payload, navigationSnapshot, targetHref,
344
358
  previousNextUrl,
345
359
  scrollIntent,
346
360
  restoredBfcacheIds,
361
+ reuseCurrentBfcacheIds,
347
362
  targetHistoryIndex: traversalIntent === null ? void 0 : traversalIntent.targetHistoryIndex,
348
363
  targetHref,
349
364
  navId
@@ -476,6 +491,16 @@ function getRequestState(navigationKind, targetPathname, previousNextUrlOverride
476
491
  interceptionContext: manifestInterceptionContext,
477
492
  previousNextUrl: window.location.pathname + window.location.search
478
493
  };
494
+ const middlewareRewriteInterceptionContext = resolveMiddlewareRewriteNavigationInterceptionContext({
495
+ basePath: __basePath,
496
+ currentPathname: window.location.pathname,
497
+ routeManifest: getBrowserRouteManifest(),
498
+ targetPathname
499
+ });
500
+ if (middlewareRewriteInterceptionContext !== null) return {
501
+ interceptionContext: middlewareRewriteInterceptionContext,
502
+ previousNextUrl: window.location.pathname + window.location.search
503
+ };
479
504
  return {
480
505
  interceptionContext: null,
481
506
  previousNextUrl: null
@@ -584,6 +609,12 @@ function BrowserRoot({ initialElements, initialNavigationSnapshot }) {
584
609
  setMountedSlotsHeader(null);
585
610
  };
586
611
  }, [setTreeStateValue]);
612
+ useLayoutEffect(() => {
613
+ restorableClientState.rememberHistoryStateSnapshot({
614
+ historyIndex: currentHistoryTraversalIndex,
615
+ state: treeState
616
+ });
617
+ }, [treeState]);
587
618
  useLayoutEffect(() => {
588
619
  setMountedSlotsHeader(getMountedSlotIdsHeader(stateRef.current.elements));
589
620
  removeStylesheetLinksCoveredByInlineCss();
@@ -593,7 +624,7 @@ function BrowserRoot({ initialElements, initialNavigationSnapshot }) {
593
624
  if (treeState.renderId !== 0) return;
594
625
  replaceHistoryStateWithoutNotify(createHistoryStateWithNavigationMetadata(window.history.state, {
595
626
  bfcacheIds: treeState.bfcacheIds,
596
- bfcacheVersion: currentBfcacheVersion,
627
+ bfcacheVersion: restorableClientState.currentBfcacheVersion,
597
628
  previousNextUrl: treeState.previousNextUrl,
598
629
  traversalIndex: currentHistoryTraversalIndex
599
630
  }), "", window.location.href);
@@ -634,16 +665,16 @@ function restorePopstateScrollPosition(state, options) {
634
665
  return;
635
666
  }
636
667
  const y = Number(state.__vinext_scrollY);
637
- retryScrollTo("__vinext_scrollX" in state ? Number(state.__vinext_scrollX) : 0, y, { shouldContinue });
668
+ retryScrollTo("__vinext_scrollX" in state ? Number(state.__vinext_scrollX) : 0, y, {
669
+ minFrames: 1,
670
+ shouldContinue
671
+ });
638
672
  }
639
673
  function isSameAppRoutePopstateTarget(href) {
640
674
  if (!hasBrowserRouterState()) return false;
641
675
  const target = new URL(href, window.location.origin);
642
676
  const routerState = getBrowserRouterState();
643
- const targetPathname = stripBasePath(target.pathname, __basePath);
644
- const targetSearch = new URLSearchParams(target.search).toString();
645
- const currentSearch = routerState.navigationSnapshot.searchParams.toString();
646
- return targetPathname === routerState.navigationSnapshot.pathname && targetSearch === currentSearch;
677
+ return createBasePathStrippedPathAndSearch(target, __basePath) === createSnapshotPathAndSearch(routerState.navigationSnapshot);
647
678
  }
648
679
  let isPageUnloading = false;
649
680
  const RSC_RELOAD_KEY = "__vinext_rsc_initial_reload__";
@@ -724,7 +755,7 @@ function applyRuntimeRscBootstrap(rsc) {
724
755
  if (rsc.nav) restoreHydrationNavigationContext(rsc.nav.pathname, rsc.nav.searchParams, params);
725
756
  }
726
757
  function registerServerActionCallback() {
727
- setServerCallback(async (id, args) => {
758
+ const serverActionCallback = async (id, args) => {
728
759
  syncServerActionHttpFallbackHead(null);
729
760
  const temporaryReferences = createTemporaryReferenceSet();
730
761
  const actionInitiation = createActionInitiationSnapshot();
@@ -766,6 +797,7 @@ function registerServerActionCallback() {
766
797
  return;
767
798
  }
768
799
  const revalidation = parseServerActionRevalidationHeader(fetchResponse.headers);
800
+ if (revalidation !== "none") clearClientNavigationCaches();
769
801
  const invalidResponseError = await readInvalidServerActionResponseError(fetchResponse.clone(), actionRedirectTarget !== null);
770
802
  if (invalidResponseError) throw invalidResponseError;
771
803
  if (actionRedirectTarget && !shouldCheckRscCompatibilityForServerActionResponse(fetchResponse)) {
@@ -778,7 +810,7 @@ function registerServerActionCallback() {
778
810
  statusText: "OK"
779
811
  }) : fetchResponse;
780
812
  const result = await createFromFetch(Promise.resolve(flightResponse), { temporaryReferences });
781
- if (shouldClearClientNavigationCachesForServerActionResult(result, revalidation)) clearClientNavigationCaches();
813
+ if (revalidation === "none" && shouldClearClientNavigationCachesForServerActionResult(result, revalidation)) clearClientNavigationCaches();
782
814
  if (actionRedirectTarget) {
783
815
  if (isServerActionResult(result) && result.root !== void 0) {
784
816
  const decoded = AppElementsWire.decode(result.root);
@@ -809,6 +841,10 @@ function registerServerActionCallback() {
809
841
  return;
810
842
  }
811
843
  return commitSameUrlNavigatePayload(Promise.resolve(AppElementsWire.decode(result)), actionInitiation, void 0, revalidation);
844
+ };
845
+ setServerCallback((id, args) => {
846
+ const releaseCacheInvalidationGuard = restorableClientState.beginCacheInvalidationGuard();
847
+ return Promise.resolve().then(() => serverActionCallback(id, args)).finally(releaseCacheInvalidationGuard);
812
848
  });
813
849
  }
814
850
  async function main() {
@@ -869,7 +905,8 @@ function bootstrapHydration(rscStream) {
869
905
  consumeAppRouterScrollIntent(scrollIntent ?? null);
870
906
  return browserNavigationController.performHardNavigation(targetHref);
871
907
  };
872
- let restoredBfcacheIds = navigationKind === "traverse" ? readCurrentBfcacheVersionHistoryIds(activeTraversalIntent?.historyState ?? window.history.state) : null;
908
+ let restoredBfcacheIds = navigationKind === "traverse" ? restorableClientState.readCurrentBfcacheVersionHistoryIds(activeTraversalIntent?.historyState ?? window.history.state) : null;
909
+ const reuseCurrentBfcacheIds = navigationKind !== "traverse" || !restorableClientState.isCacheInvalidationGuarded() && restorableClientState.isCurrentBfcacheVersion(activeTraversalIntent?.historyState ?? window.history.state);
873
910
  try {
874
911
  const shouldUsePendingRouterState = programmaticTransition;
875
912
  if (shouldUsePendingRouterState && hasBrowserRouterState()) pendingRouterState = beginPendingBrowserRouterState();
@@ -934,7 +971,7 @@ function bootstrapHydration(rscStream) {
934
971
  const cachedNavigationSnapshot = createClientNavigationRenderSnapshot(currentHref, cachedParams);
935
972
  const cachedPayload = decodeAppElementsPromise(createFromFetch(Promise.resolve(restoreRscResponse(cachedRoute.response))));
936
973
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
937
- if (await renderNavigationPayload(cachedPayload, cachedNavigationSnapshot, currentHref, navId, currentHistoryMode, cachedParams, requestPreviousNextUrl, detachedNavigationCommits ? null : pendingRouterState, VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, toActionType(navigationKind), toOperationLane(navigationKind), activeTraversalIntent, scrollIntent, restoredBfcacheIds) === "no-commit") {
974
+ if (await renderNavigationPayload(cachedPayload, cachedNavigationSnapshot, currentHref, navId, currentHistoryMode, cachedParams, requestPreviousNextUrl, detachedNavigationCommits ? null : pendingRouterState, VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, toActionType(navigationKind), toOperationLane(navigationKind), activeTraversalIntent, scrollIntent, restoredBfcacheIds, reuseCurrentBfcacheIds) === "no-commit") {
938
975
  deleteVisitedResponse(rscUrl, requestInterceptionContext);
939
976
  continue;
940
977
  }
@@ -972,7 +1009,7 @@ function bootstrapHydration(rscStream) {
972
1009
  if (optimisticPayload !== null) {
973
1010
  detachedNavigationCommits = true;
974
1011
  const optimisticNavigationSnapshot = createClientNavigationRenderSnapshot(currentHref, optimisticPayload.params);
975
- 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).catch((error) => {
1012
+ 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).catch((error) => {
976
1013
  if (browserNavigationController.isCurrentNavigation(navId)) console.error("[vinext] Optimistic RSC navigation error:", error);
977
1014
  });
978
1015
  }
@@ -1059,7 +1096,7 @@ function bootstrapHydration(rscStream) {
1059
1096
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
1060
1097
  const rscPayload = decodeAppElementsPromise(createFromFetch(Promise.resolve(reactResponse)));
1061
1098
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
1062
- 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) !== "committed") return;
1099
+ 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) !== "committed") return;
1063
1100
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
1064
1101
  try {
1065
1102
  const renderedElements = await rscPayload;
@@ -1087,7 +1124,6 @@ function bootstrapHydration(rscStream) {
1087
1124
  }
1088
1125
  }
1089
1126
  });
1090
- if ("scrollRestoration" in history) history.scrollRestoration = "manual";
1091
1127
  const handlePopstate = createPopstateRestoreHandler({
1092
1128
  getActiveNavigationId: browserNavigationController.getActiveNavigationId.bind(browserNavigationController),
1093
1129
  getPendingNavigation: () => window.__VINEXT_RSC_PENDING__,
@@ -1099,7 +1135,8 @@ function bootstrapHydration(rscStream) {
1099
1135
  restorePopstateScrollPosition,
1100
1136
  setPendingNavigation: (pendingNavigation) => {
1101
1137
  window.__VINEXT_RSC_PENDING__ = pendingNavigation;
1102
- }
1138
+ },
1139
+ shouldSkipScrollRestore: (navId) => synchronousPopstateScrollRestoreNavigationId === navId
1103
1140
  });
1104
1141
  window.addEventListener("popstate", (event) => {
1105
1142
  const href = window.location.href;
@@ -1110,6 +1147,14 @@ function bootstrapHydration(rscStream) {
1110
1147
  return;
1111
1148
  }
1112
1149
  handlePopstate(event);
1150
+ if (restoreHistoryStateSnapshot(event.state)) restoreSynchronousPopstateScrollPosition({
1151
+ getActiveNavigationId: () => browserNavigationController.getActiveNavigationId(),
1152
+ isCurrentNavigation: (navId) => browserNavigationController.isCurrentNavigation(navId),
1153
+ markScrollRestoreConsumed: (navId) => {
1154
+ synchronousPopstateScrollRestoreNavigationId = navId;
1155
+ },
1156
+ restorePopstateScrollPosition
1157
+ }, event.state);
1113
1158
  });
1114
1159
  if (import.meta.hot) {
1115
1160
  const applyRscHmrUpdate = async (updateId) => {
@@ -19,5 +19,6 @@ type ResolveManifestNavigationInterceptionContextOptions = {
19
19
  * is owned by the deterministic route graph builder.
20
20
  */
21
21
  declare function resolveManifestNavigationInterceptionContext(options: ResolveManifestNavigationInterceptionContextOptions): string | null;
22
+ declare function resolveMiddlewareRewriteNavigationInterceptionContext(options: ResolveManifestNavigationInterceptionContextOptions): string | null;
22
23
  //#endregion
23
- export { resolveManifestNavigationInterceptionContext };
24
+ export { resolveManifestNavigationInterceptionContext, resolveMiddlewareRewriteNavigationInterceptionContext };
@@ -1,6 +1,6 @@
1
1
  import { splitPathnameForRouteMatch } from "../routing/utils.js";
2
2
  import { stripBasePath } from "../utils/base-path.js";
3
- import { matchRoutePattern, matchRoutePatternPrefix } from "../routing/route-pattern.js";
3
+ import { matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternWithOptionalDynamicSegments } from "../routing/route-pattern.js";
4
4
  //#region src/server/app-browser-interception-context.ts
5
5
  /**
6
6
  * Resolve the first-hop interception context from declared route topology.
@@ -26,5 +26,18 @@ function resolveManifestNavigationInterceptionContext(options) {
26
26
  }
27
27
  return null;
28
28
  }
29
+ function resolveMiddlewareRewriteNavigationInterceptionContext(options) {
30
+ if (options.routeManifest === null) return null;
31
+ const currentPathname = stripBasePath(options.currentPathname, options.basePath);
32
+ const targetPathname = stripBasePath(options.targetPathname, options.basePath);
33
+ const sourceParts = splitPathnameForRouteMatch(currentPathname);
34
+ const targetParts = splitPathnameForRouteMatch(targetPathname);
35
+ for (const interception of options.routeManifest.segmentGraph.interceptions.values()) {
36
+ if (!matchRoutePatternPrefix(sourceParts, interception.sourcePatternParts)) continue;
37
+ if (!matchRoutePatternWithOptionalDynamicSegments(targetParts, interception.targetPatternParts)) continue;
38
+ return currentPathname;
39
+ }
40
+ return null;
41
+ }
29
42
  //#endregion
30
- export { resolveManifestNavigationInterceptionContext };
43
+ export { resolveManifestNavigationInterceptionContext, resolveMiddlewareRewriteNavigationInterceptionContext };
@@ -35,6 +35,7 @@ type SameUrlServerActionLifecycleOptions = {
35
35
  targetHref?: string;
36
36
  };
37
37
  type BrowserNavigationControllerDeps = {
38
+ basePath?: string;
38
39
  commitClientNavigationState?: typeof commitClientNavigationState;
39
40
  performHardNavigation?: (href: string, mode?: HardNavigationMode) => boolean;
40
41
  getRouteManifest?: () => RouteManifest | null;
@@ -51,6 +52,12 @@ type BrowserNavigationController = {
51
52
  attachBrowserRouterState(setter: Dispatch<AppRouterState | Promise<AppRouterState>>, stateRef: BrowserRouterStateRef): () => void;
52
53
  beginPendingBrowserRouterState(): PendingBrowserRouterState;
53
54
  finalizeNavigation(navId: number, pending: PendingBrowserRouterState | null | undefined): void;
55
+ restoreHistorySnapshotVisibleState(options: {
56
+ beforeCommit?: () => void;
57
+ navId: number;
58
+ state: AppRouterState;
59
+ targetHref: string;
60
+ }): boolean;
54
61
  renderNavigationPayload(options: {
55
62
  actionType: "navigate" | "replace" | "traverse";
56
63
  createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;
@@ -64,6 +71,7 @@ type BrowserNavigationController = {
64
71
  previousNextUrl: string | null;
65
72
  scrollIntent?: AppRouterScrollIntent | null;
66
73
  restoredBfcacheIds?: Readonly<Record<string, string>> | null;
74
+ reuseCurrentBfcacheIds?: boolean;
67
75
  targetHistoryIndex?: number | null;
68
76
  targetHref: string;
69
77
  navId: number;
@@ -91,6 +99,8 @@ type BrowserNavigationController = {
91
99
  }): ReactNode;
92
100
  };
93
101
  declare function clearHardNavigationLoopGuard(): void;
102
+ declare function createSnapshotPathAndSearch(snapshot: ClientNavigationRenderSnapshot): string;
103
+ declare function createBasePathStrippedPathAndSearch(url: URL, basePath: string): string;
94
104
  declare function createAppBrowserNavigationController(deps?: BrowserNavigationControllerDeps): BrowserNavigationController;
95
105
  //#endregion
96
- export { HistoryUpdateMode, NavigationPayloadOutcome, PendingBrowserRouterState, clearHardNavigationLoopGuard, createAppBrowserNavigationController };
106
+ export { HistoryUpdateMode, NavigationPayloadOutcome, PendingBrowserRouterState, clearHardNavigationLoopGuard, createAppBrowserNavigationController, createBasePathStrippedPathAndSearch, createSnapshotPathAndSearch };
@@ -1,3 +1,4 @@
1
+ import { stripBasePath } from "../utils/base-path.js";
1
2
  import { claimAppRouterScrollIntentForCommit, consumeAppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
2
3
  import { activateNavigationSnapshot, clearPendingPathname, commitClientNavigationState } from "../shims/navigation.js";
3
4
  import { shouldScheduleRefreshForDiscardedServerAction } from "./app-browser-action-result.js";
@@ -49,7 +50,25 @@ function performHardNavigationWithLoopGuard(href, mode = "assign") {
49
50
  else window.location.assign(href);
50
51
  return true;
51
52
  }
53
+ function createSnapshotPathAndSearch(snapshot) {
54
+ const query = snapshot.searchParams.toString();
55
+ return query === "" ? snapshot.pathname : `${snapshot.pathname}?${query}`;
56
+ }
57
+ function createBasePathStrippedPathAndSearch(url, basePath) {
58
+ const pathname = stripBasePath(url.pathname, basePath);
59
+ const query = new URLSearchParams(url.search).toString();
60
+ return query === "" ? pathname : `${pathname}?${query}`;
61
+ }
62
+ function isSnapshotTargetHref(basePath, snapshot, targetHref) {
63
+ try {
64
+ const baseHref = typeof window === "undefined" ? "http://localhost" : window.location.href;
65
+ return createBasePathStrippedPathAndSearch(new URL(targetHref, baseHref), basePath) === createSnapshotPathAndSearch(snapshot);
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
52
70
  function createAppBrowserNavigationController(deps = {}) {
71
+ const basePath = deps.basePath ?? "";
53
72
  const commitClientNavigationStateImpl = deps.commitClientNavigationState ?? commitClientNavigationState;
54
73
  const performHardNavigation = deps.performHardNavigation ?? performHardNavigationWithLoopGuard;
55
74
  const getRouteManifest = deps.getRouteManifest ?? (() => null);
@@ -215,6 +234,61 @@ function createAppBrowserNavigationController(deps = {}) {
215
234
  function dispatchSynchronousVisibleCommit(commit) {
216
235
  getBrowserRouterStateSetter()(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
217
236
  }
237
+ function createRestoredHistorySnapshotCommit(options) {
238
+ const operation = {
239
+ id: options.renderId,
240
+ lane: "traverse",
241
+ startedVisibleCommitVersion: options.currentState.visibleCommitVersion,
242
+ state: "pending"
243
+ };
244
+ return {
245
+ action: {
246
+ bfcacheIds: options.restoredState.bfcacheIds,
247
+ elements: options.restoredState.elements,
248
+ interception: options.restoredState.interception,
249
+ interceptionContext: options.restoredState.interceptionContext,
250
+ layoutFlags: options.restoredState.layoutFlags,
251
+ layoutIds: options.restoredState.layoutIds,
252
+ navigationSnapshot: options.restoredState.navigationSnapshot,
253
+ operation,
254
+ previousNextUrl: options.restoredState.previousNextUrl,
255
+ renderId: options.renderId,
256
+ rootLayoutTreePath: options.restoredState.rootLayoutTreePath,
257
+ reuseCurrentBfcacheIds: false,
258
+ routeId: options.restoredState.routeId,
259
+ skippedLayoutIds: [],
260
+ slotBindings: options.restoredState.slotBindings,
261
+ type: "traverse"
262
+ },
263
+ interception: options.restoredState.interception,
264
+ interceptionContext: options.restoredState.interceptionContext,
265
+ previousNextUrl: options.restoredState.previousNextUrl,
266
+ rootLayoutTreePath: options.restoredState.rootLayoutTreePath,
267
+ routeId: options.restoredState.routeId,
268
+ skippedLayoutIds: []
269
+ };
270
+ }
271
+ function restoreHistorySnapshotVisibleState(options) {
272
+ if (!isSnapshotTargetHref(basePath, options.state.navigationSnapshot, options.targetHref)) return false;
273
+ const currentState = getBrowserRouterState();
274
+ const pending = createRestoredHistorySnapshotCommit({
275
+ currentState,
276
+ renderId: allocateRenderId(),
277
+ restoredState: options.state
278
+ });
279
+ const approval = approvePendingNavigationCommit({
280
+ activeNavigationId,
281
+ currentState,
282
+ pending,
283
+ routeManifest: getRouteManifest(),
284
+ startedNavigationId: options.navId,
285
+ targetHref: options.targetHref
286
+ });
287
+ if (approval.approvedCommit === null) return false;
288
+ options.beforeCommit?.();
289
+ dispatchSynchronousVisibleCommit(approval.approvedCommit);
290
+ return true;
291
+ }
218
292
  function notifyDiscardedServerActionRevalidation(lifecycleOptions) {
219
293
  if (!shouldScheduleRefreshForDiscardedServerAction(lifecycleOptions?.revalidation ?? "none")) return;
220
294
  lifecycleOptions?.onDiscardedRevalidation?.();
@@ -237,6 +311,7 @@ function createAppBrowserNavigationController(deps = {}) {
237
311
  previousNextUrl: options.previousNextUrl,
238
312
  renderId,
239
313
  restoredBfcacheIds: options.restoredBfcacheIds,
314
+ reuseCurrentBfcacheIds: options.reuseCurrentBfcacheIds,
240
315
  type: options.actionType
241
316
  });
242
317
  const approval = approvePendingNavigationCommit({
@@ -354,6 +429,7 @@ function createAppBrowserNavigationController(deps = {}) {
354
429
  attachBrowserRouterState,
355
430
  beginPendingBrowserRouterState,
356
431
  finalizeNavigation,
432
+ restoreHistorySnapshotVisibleState,
357
433
  renderNavigationPayload,
358
434
  commitSameUrlNavigatePayload,
359
435
  hmrReplaceTree,
@@ -362,4 +438,4 @@ function createAppBrowserNavigationController(deps = {}) {
362
438
  };
363
439
  }
364
440
  //#endregion
365
- export { clearHardNavigationLoopGuard, createAppBrowserNavigationController };
441
+ export { clearHardNavigationLoopGuard, createAppBrowserNavigationController, createBasePathStrippedPathAndSearch, createSnapshotPathAndSearch };
@@ -1,17 +1,26 @@
1
+ import { NavigationRuntimeNavigate } from "../client/navigation-runtime.js";
2
+
1
3
  //#region src/server/app-browser-popstate.d.ts
2
4
  type RestoreScrollPosition = (state: unknown, options?: {
3
5
  shouldContinue?: () => boolean;
4
6
  }) => void;
5
- type NavigateRsc = (href: string, redirectDepth?: number, navigationKind?: "navigate" | "traverse" | "refresh") => Promise<void>;
6
7
  type BrowserPopstateRestoreDeps = {
7
8
  getActiveNavigationId: () => number;
8
9
  getPendingNavigation: () => Promise<void> | null | undefined;
9
- getNavigate: () => NavigateRsc | undefined;
10
+ getNavigate: () => NavigationRuntimeNavigate | undefined;
10
11
  isCurrentNavigation: (navId: number) => boolean;
11
12
  notifyAppRouterTransitionStart: (href: string) => void;
12
13
  restorePopstateScrollPosition: RestoreScrollPosition;
13
14
  setPendingNavigation: (pendingNavigation: Promise<void> | null) => void;
15
+ shouldSkipScrollRestore: (navId: number) => boolean;
14
16
  };
17
+ type SynchronousPopstateScrollRestoreDeps = {
18
+ getActiveNavigationId: () => number;
19
+ isCurrentNavigation: (navId: number) => boolean;
20
+ markScrollRestoreConsumed: (navId: number) => void;
21
+ restorePopstateScrollPosition: RestoreScrollPosition;
22
+ };
23
+ declare function restoreSynchronousPopstateScrollPosition(deps: SynchronousPopstateScrollRestoreDeps, state: unknown): void;
15
24
  declare function createPopstateRestoreHandler(deps: BrowserPopstateRestoreDeps): (event: PopStateEvent) => void;
16
25
  //#endregion
17
- export { createPopstateRestoreHandler };
26
+ export { createPopstateRestoreHandler, restoreSynchronousPopstateScrollPosition };
@@ -1,7 +1,14 @@
1
+ import { readHistoryStateTraversalIndex } from "./app-history-state.js";
2
+ import "./app-browser-state.js";
1
3
  //#region src/server/app-browser-popstate.ts
2
4
  function hasSavedScrollPosition(state) {
3
5
  return Boolean(state && typeof state === "object" && "__vinext_scrollY" in state);
4
6
  }
7
+ function restoreSynchronousPopstateScrollPosition(deps, state) {
8
+ const navId = deps.getActiveNavigationId();
9
+ deps.markScrollRestoreConsumed(navId);
10
+ deps.restorePopstateScrollPosition(state, { shouldContinue: () => deps.isCurrentNavigation(navId) });
11
+ }
5
12
  function scheduleAfterFrame(callback) {
6
13
  if (typeof window !== "undefined" && typeof window.requestAnimationFrame === "function") {
7
14
  window.requestAnimationFrame(callback);
@@ -9,21 +16,29 @@ function scheduleAfterFrame(callback) {
9
16
  }
10
17
  queueMicrotask(callback);
11
18
  }
19
+ function createPopstateTraversalIntent(historyState) {
20
+ return {
21
+ direction: "unknown",
22
+ historyState,
23
+ targetHistoryIndex: readHistoryStateTraversalIndex(historyState)
24
+ };
25
+ }
12
26
  function createPopstateRestoreHandler(deps) {
13
27
  return (event) => {
14
28
  deps.notifyAppRouterTransitionStart(window.location.href);
15
- const pendingNavigation = deps.getNavigate()?.(window.location.href, 0, "traverse") ?? Promise.resolve();
29
+ const pendingNavigation = deps.getNavigate()?.(window.location.href, 0, "traverse", void 0, void 0, false, createPopstateTraversalIntent(event.state)) ?? Promise.resolve();
16
30
  const popstateNavId = deps.getActiveNavigationId();
17
31
  deps.setPendingNavigation(pendingNavigation);
18
32
  const shouldRestoreSavedScroll = hasSavedScrollPosition(event.state);
33
+ const shouldRestoreScrollForNavigation = () => deps.isCurrentNavigation(popstateNavId) && !deps.shouldSkipScrollRestore(popstateNavId);
19
34
  if (shouldRestoreSavedScroll) scheduleAfterFrame(() => {
20
- if (deps.isCurrentNavigation(popstateNavId)) deps.restorePopstateScrollPosition(event.state, { shouldContinue: () => deps.isCurrentNavigation(popstateNavId) });
35
+ if (shouldRestoreScrollForNavigation()) deps.restorePopstateScrollPosition(event.state, { shouldContinue: shouldRestoreScrollForNavigation });
21
36
  });
22
37
  pendingNavigation.finally(() => {
23
- if (deps.isCurrentNavigation(popstateNavId) && !shouldRestoreSavedScroll) deps.restorePopstateScrollPosition(event.state);
38
+ if (shouldRestoreScrollForNavigation() && !shouldRestoreSavedScroll) deps.restorePopstateScrollPosition(event.state);
24
39
  if (deps.getPendingNavigation() === pendingNavigation) deps.setPendingNavigation(null);
25
40
  });
26
41
  };
27
42
  }
28
43
  //#endregion
29
- export { createPopstateRestoreHandler };
44
+ export { createPopstateRestoreHandler, restoreSynchronousPopstateScrollPosition };
@@ -49,6 +49,7 @@ type AppRouterAction = {
49
49
  previousNextUrl: string | null;
50
50
  renderId: number;
51
51
  rootLayoutTreePath: string | null;
52
+ reuseCurrentBfcacheIds: boolean;
52
53
  routeId: string;
53
54
  skippedLayoutIds: readonly string[];
54
55
  slotBindings: readonly AppElementsSlotBinding[];
@@ -102,6 +103,7 @@ declare function createNextBfcacheIdMap(options: {
102
103
  elements: AppElements;
103
104
  nextPathname: string;
104
105
  restored?: BfcacheIdMap | null;
106
+ reuseCurrent?: boolean;
105
107
  }): BfcacheIdMap;
106
108
  declare function preserveBfcacheIdsForMergedElements(options: {
107
109
  elements: AppElements;
@@ -150,6 +152,7 @@ declare function createPendingNavigationCommit(options: {
150
152
  previousNextUrl?: string | null;
151
153
  renderId: number;
152
154
  restoredBfcacheIds?: BfcacheIdMap | null;
155
+ reuseCurrentBfcacheIds?: boolean;
153
156
  type: "navigate" | "replace" | "traverse";
154
157
  }): Promise<PendingNavigationCommit>;
155
158
  //#endregion