vinext 0.1.0 → 0.1.2

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 (205) hide show
  1. package/README.md +2 -5
  2. package/dist/build/assets-ignore.d.ts +32 -0
  3. package/dist/build/assets-ignore.js +48 -0
  4. package/dist/build/client-build-config.d.ts +33 -1
  5. package/dist/build/client-build-config.js +66 -1
  6. package/dist/check.js +4 -3
  7. package/dist/cli.js +2 -0
  8. package/dist/client/navigation-runtime.d.ts +11 -2
  9. package/dist/client/navigation-runtime.js +1 -1
  10. package/dist/client/vinext-next-data.d.ts +2 -1
  11. package/dist/client/window-next.d.ts +6 -4
  12. package/dist/config/config-matchers.d.ts +31 -5
  13. package/dist/config/config-matchers.js +50 -3
  14. package/dist/config/next-config.d.ts +29 -3
  15. package/dist/config/next-config.js +32 -2
  16. package/dist/deploy.js +47 -304
  17. package/dist/entries/app-rsc-entry.d.ts +8 -2
  18. package/dist/entries/app-rsc-entry.js +61 -5
  19. package/dist/entries/app-rsc-manifest.js +20 -2
  20. package/dist/entries/pages-client-entry.js +1 -1
  21. package/dist/entries/pages-server-entry.js +16 -7
  22. package/dist/index.d.ts +0 -2
  23. package/dist/index.js +233 -280
  24. package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
  25. package/dist/plugins/dynamic-preload-metadata.js +415 -0
  26. package/dist/plugins/og-assets.js +2 -2
  27. package/dist/plugins/optimize-imports.d.ts +8 -4
  28. package/dist/plugins/optimize-imports.js +16 -12
  29. package/dist/plugins/postcss.js +18 -14
  30. package/dist/plugins/require-context.d.ts +6 -0
  31. package/dist/plugins/require-context.js +184 -0
  32. package/dist/plugins/sass.d.ts +53 -24
  33. package/dist/plugins/sass.js +249 -1
  34. package/dist/plugins/wasm-module-import.d.ts +15 -0
  35. package/dist/plugins/wasm-module-import.js +50 -0
  36. package/dist/routing/app-route-graph.d.ts +35 -2
  37. package/dist/routing/app-route-graph.js +179 -8
  38. package/dist/routing/file-matcher.js +1 -1
  39. package/dist/routing/route-pattern.d.ts +2 -1
  40. package/dist/routing/route-pattern.js +16 -1
  41. package/dist/server/api-handler.js +4 -0
  42. package/dist/server/app-browser-entry.js +155 -215
  43. package/dist/server/app-browser-error.d.ts +4 -1
  44. package/dist/server/app-browser-error.js +7 -1
  45. package/dist/server/app-browser-history-controller.d.ts +104 -0
  46. package/dist/server/app-browser-history-controller.js +210 -0
  47. package/dist/server/app-browser-interception-context.d.ts +2 -1
  48. package/dist/server/app-browser-interception-context.js +15 -2
  49. package/dist/server/app-browser-navigation-controller.d.ts +13 -2
  50. package/dist/server/app-browser-navigation-controller.js +83 -4
  51. package/dist/server/app-browser-popstate.d.ts +12 -3
  52. package/dist/server/app-browser-popstate.js +19 -4
  53. package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
  54. package/dist/server/app-browser-rsc-redirect.js +30 -8
  55. package/dist/server/app-browser-state.d.ts +3 -0
  56. package/dist/server/app-browser-state.js +10 -10
  57. package/dist/server/app-browser-visible-commit.js +10 -8
  58. package/dist/server/app-fallback-renderer.d.ts +2 -1
  59. package/dist/server/app-fallback-renderer.js +3 -1
  60. package/dist/server/app-history-state.d.ts +45 -1
  61. package/dist/server/app-history-state.js +109 -1
  62. package/dist/server/app-middleware.js +1 -0
  63. package/dist/server/app-optimistic-routing.js +22 -1
  64. package/dist/server/app-page-boundary-render.d.ts +2 -1
  65. package/dist/server/app-page-boundary-render.js +45 -21
  66. package/dist/server/app-page-cache.js +9 -7
  67. package/dist/server/app-page-dispatch.d.ts +14 -0
  68. package/dist/server/app-page-dispatch.js +21 -6
  69. package/dist/server/app-page-element-builder.d.ts +23 -2
  70. package/dist/server/app-page-element-builder.js +58 -17
  71. package/dist/server/app-page-execution.d.ts +1 -1
  72. package/dist/server/app-page-execution.js +32 -17
  73. package/dist/server/app-page-render.d.ts +7 -1
  74. package/dist/server/app-page-render.js +11 -16
  75. package/dist/server/app-page-request.d.ts +9 -6
  76. package/dist/server/app-page-request.js +14 -10
  77. package/dist/server/app-page-response.d.ts +2 -2
  78. package/dist/server/app-page-response.js +2 -2
  79. package/dist/server/app-page-route-wiring.d.ts +3 -1
  80. package/dist/server/app-page-route-wiring.js +10 -8
  81. package/dist/server/app-page-stream.d.ts +37 -7
  82. package/dist/server/app-page-stream.js +36 -6
  83. package/dist/server/app-pages-bridge.d.ts +16 -0
  84. package/dist/server/app-pages-bridge.js +23 -3
  85. package/dist/server/app-route-handler-cache.d.ts +1 -0
  86. package/dist/server/app-route-handler-cache.js +1 -0
  87. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  88. package/dist/server/app-route-handler-dispatch.js +2 -0
  89. package/dist/server/app-route-handler-execution.d.ts +1 -0
  90. package/dist/server/app-route-handler-execution.js +1 -0
  91. package/dist/server/app-route-handler-response.js +11 -10
  92. package/dist/server/app-route-handler-runtime.d.ts +1 -0
  93. package/dist/server/app-route-handler-runtime.js +15 -3
  94. package/dist/server/app-rsc-handler.d.ts +1 -0
  95. package/dist/server/app-rsc-handler.js +5 -4
  96. package/dist/server/app-rsc-response-finalizer.js +1 -1
  97. package/dist/server/app-rsc-route-matching.d.ts +20 -1
  98. package/dist/server/app-rsc-route-matching.js +29 -4
  99. package/dist/server/app-server-action-execution.d.ts +22 -1
  100. package/dist/server/app-server-action-execution.js +73 -12
  101. package/dist/server/app-ssr-entry.d.ts +6 -0
  102. package/dist/server/app-ssr-entry.js +19 -3
  103. package/dist/server/app-ssr-stream.js +9 -1
  104. package/dist/server/dev-lockfile.js +2 -1
  105. package/dist/server/dev-server.d.ts +1 -1
  106. package/dist/server/dev-server.js +97 -43
  107. package/dist/server/headers.d.ts +8 -1
  108. package/dist/server/headers.js +8 -1
  109. package/dist/server/instrumentation-runtime.d.ts +6 -0
  110. package/dist/server/instrumentation-runtime.js +8 -0
  111. package/dist/server/isr-cache.d.ts +37 -1
  112. package/dist/server/isr-cache.js +85 -1
  113. package/dist/server/isr-decision.d.ts +79 -0
  114. package/dist/server/isr-decision.js +70 -0
  115. package/dist/server/metadata-route-response.js +5 -3
  116. package/dist/server/middleware-runtime.d.ts +13 -0
  117. package/dist/server/middleware-runtime.js +11 -7
  118. package/dist/server/middleware.js +1 -0
  119. package/dist/server/navigation-planner.d.ts +62 -1
  120. package/dist/server/navigation-planner.js +193 -3
  121. package/dist/server/navigation-trace.d.ts +12 -2
  122. package/dist/server/navigation-trace.js +11 -1
  123. package/dist/server/normalize-path.d.ts +0 -8
  124. package/dist/server/normalize-path.js +3 -1
  125. package/dist/server/otel-tracer-extension.d.ts +45 -0
  126. package/dist/server/otel-tracer-extension.js +89 -0
  127. package/dist/server/pages-api-route.d.ts +14 -3
  128. package/dist/server/pages-api-route.js +6 -1
  129. package/dist/server/pages-asset-tags.d.ts +15 -4
  130. package/dist/server/pages-asset-tags.js +18 -12
  131. package/dist/server/pages-data-route.js +5 -1
  132. package/dist/server/pages-node-compat.d.ts +5 -11
  133. package/dist/server/pages-node-compat.js +175 -118
  134. package/dist/server/pages-page-data.d.ts +38 -7
  135. package/dist/server/pages-page-data.js +64 -18
  136. package/dist/server/pages-page-handler.d.ts +10 -2
  137. package/dist/server/pages-page-handler.js +49 -20
  138. package/dist/server/pages-page-response.d.ts +55 -2
  139. package/dist/server/pages-page-response.js +74 -6
  140. package/dist/server/pages-readiness.d.ts +36 -0
  141. package/dist/server/pages-readiness.js +21 -0
  142. package/dist/server/pages-request-pipeline.d.ts +113 -0
  143. package/dist/server/pages-request-pipeline.js +230 -0
  144. package/dist/server/pages-revalidate.d.ts +15 -0
  145. package/dist/server/pages-revalidate.js +19 -0
  146. package/dist/server/prod-server.d.ts +45 -3
  147. package/dist/server/prod-server.js +182 -234
  148. package/dist/server/socket-error-backstop.d.ts +19 -1
  149. package/dist/server/socket-error-backstop.js +77 -4
  150. package/dist/shims/app-router-scroll.js +22 -4
  151. package/dist/shims/cache-runtime.js +39 -2
  152. package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
  153. package/dist/shims/dynamic-preload-chunks.js +77 -0
  154. package/dist/shims/dynamic.d.ts +4 -0
  155. package/dist/shims/dynamic.js +4 -2
  156. package/dist/shims/error-boundary.d.ts +17 -7
  157. package/dist/shims/error-boundary.js +8 -1
  158. package/dist/shims/error.js +37 -11
  159. package/dist/shims/fetch-cache.d.ts +22 -1
  160. package/dist/shims/fetch-cache.js +28 -1
  161. package/dist/shims/hash-scroll.d.ts +1 -0
  162. package/dist/shims/hash-scroll.js +3 -1
  163. package/dist/shims/head.js +6 -1
  164. package/dist/shims/headers.d.ts +16 -2
  165. package/dist/shims/headers.js +37 -1
  166. package/dist/shims/image-config.js +7 -1
  167. package/dist/shims/internal/app-route-detection.d.ts +6 -3
  168. package/dist/shims/internal/app-route-detection.js +10 -6
  169. package/dist/shims/internal/app-router-context.d.ts +5 -0
  170. package/dist/shims/internal/link-status-registry.d.ts +43 -0
  171. package/dist/shims/internal/link-status-registry.js +42 -0
  172. package/dist/shims/internal/route-pattern-for-warning.d.ts +27 -0
  173. package/dist/shims/internal/route-pattern-for-warning.js +40 -0
  174. package/dist/shims/internal/utils.d.ts +1 -0
  175. package/dist/shims/link.js +20 -6
  176. package/dist/shims/metadata.d.ts +6 -2
  177. package/dist/shims/metadata.js +32 -14
  178. package/dist/shims/navigation.d.ts +9 -18
  179. package/dist/shims/navigation.js +96 -23
  180. package/dist/shims/router-state.d.ts +1 -0
  181. package/dist/shims/router-state.js +2 -0
  182. package/dist/shims/router.d.ts +6 -3
  183. package/dist/shims/router.js +156 -22
  184. package/dist/shims/script-nonce-context.d.ts +1 -1
  185. package/dist/shims/script-nonce-context.js +11 -3
  186. package/dist/shims/server.d.ts +17 -1
  187. package/dist/shims/server.js +31 -6
  188. package/dist/shims/slot.js +1 -1
  189. package/dist/shims/unified-request-context.js +1 -0
  190. package/dist/typegen.js +1 -0
  191. package/dist/utils/client-build-manifest.d.ts +8 -1
  192. package/dist/utils/client-build-manifest.js +41 -6
  193. package/dist/utils/client-entry-manifest.d.ts +11 -0
  194. package/dist/utils/client-entry-manifest.js +29 -0
  195. package/dist/utils/client-runtime-metadata.d.ts +45 -0
  196. package/dist/utils/client-runtime-metadata.js +63 -0
  197. package/dist/utils/hash.d.ts +17 -1
  198. package/dist/utils/hash.js +36 -1
  199. package/dist/utils/lazy-chunks.d.ts +27 -1
  200. package/dist/utils/lazy-chunks.js +65 -1
  201. package/dist/utils/manifest-paths.d.ts +20 -2
  202. package/dist/utils/manifest-paths.js +38 -3
  203. package/dist/utils/path.d.ts +2 -1
  204. package/dist/utils/path.js +5 -1
  205. package/package.json +6 -2
@@ -1,4 +1,3 @@
1
- import { stripBasePath } from "../utils/base-path.js";
2
1
  import { ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_PARAMS_HEADER, VINEXT_RSC_REDIRECT_HEADER } from "./headers.js";
3
2
  import { DANGEROUS_URL_BLOCK_MESSAGE, isDangerousScheme } from "../shims/url-safety.js";
4
3
  import { AppElementsWire } from "./app-elements-wire.js";
@@ -9,12 +8,13 @@ import { installWindowNext } from "../client/window-next.js";
9
8
  import { retryScrollTo, scrollToHashTargetOnNextFrame } from "../shims/hash-scroll.js";
10
9
  import { getNavigationRuntime, registerNavigationRuntimeBootstrap, registerNavigationRuntimeFunctions } from "../client/navigation-runtime.js";
11
10
  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";
14
- import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, getVinextRscCompatibilityId, resolveHardNavigationTargetFromRscResponse, resolveRscCompatibilityNavigationDecision } from "./app-rsc-cache-busting.js";
11
+ import { resolveManifestNavigationInterceptionContext, resolveMiddlewareRewriteNavigationInterceptionContext } from "./app-browser-interception-context.js";
12
+ import { readHistoryStatePreviousNextUrl } from "./app-history-state.js";
13
+ import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, getVinextRscCompatibilityId, resolveRscCompatibilityNavigationDecision } from "./app-rsc-cache-busting.js";
15
14
  import { AppBrowserMpaNavigationScheduler } from "./app-browser-mpa-navigation.js";
15
+ import { navigationPlanner } from "./navigation-planner.js";
16
16
  import { beginAppRouterScrollIntent, consumeAppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
17
- import { __basePath, appRouterInstance, commitClientNavigationState, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, decodeRedirectError, getBfcacheIdMapContext, getClientNavigationRenderContext, getPrefetchCache, invalidatePrefetchCache, isRedirectError, pushHistoryStateWithoutNotify, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, setClientParams, setMountedSlotsHeader, setNavigationContext, setPendingPathname, useRouter } from "../shims/navigation.js";
17
+ import { __basePath, appRouterInstance, commitClientNavigationState, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, decodeRedirectError, getBfcacheIdMapContext, getClientNavigationRenderContext, getPrefetchCache, invalidatePrefetchCache, isRedirectError, pushHistoryStateWithoutNotify, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, setClientParams, setMountedSlotsHeader, setNavigationContext, setPendingPathname, useRouter } from "../shims/navigation.js";
18
18
  import { DevRecoveryBoundary, RedirectBoundary } from "../shims/error-boundary.js";
19
19
  import { AppRouterScrollCommitProvider } from "../shims/app-router-scroll.js";
20
20
  import { BfcacheStateKeyMapContext, ElementsContext, Slot } from "../shims/slot.js";
@@ -23,18 +23,19 @@ 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 } from "./app-browser-navigation-controller.js";
27
27
  import { consumeInitialFormState, createVinextHydrateRootOptions, hydrateRootInTransition } from "./app-browser-hydration.js";
28
+ import { AppBrowserHistoryController } from "./app-browser-history-controller.js";
28
29
  import { createVisitedResponseCacheEntry, isVisitedResponseCacheEntryFresh } from "./app-visited-response-cache.js";
29
- import { createPopstateRestoreHandler } from "./app-browser-popstate.js";
30
- import { createOnUncaughtError } from "./app-browser-error.js";
30
+ import { createPopstateRestoreHandler, restoreSynchronousPopstateScrollPosition } from "./app-browser-popstate.js";
31
+ import { createOnUncaughtError, prodOnCaughtError } from "./app-browser-error.js";
31
32
  import { dismissOverlay } from "./dev-error-overlay-store.js";
32
33
  import { devOnCaughtError, devOnUncaughtError, installDevErrorOverlay, installViteHmrErrorHandler, reportInitialDevServerErrors } from "./dev-error-overlay.js";
33
34
  import { throwOnServerActionNotFound } from "./server-action-not-found.js";
34
- 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
@@ -57,9 +58,26 @@ const optimisticRouteTemplateLearning = /* @__PURE__ */ new Map();
57
58
  function getBrowserRouteManifest() {
58
59
  return getNavigationRuntime()?.bootstrap.routeManifest ?? null;
59
60
  }
61
+ const historyController = new AppBrowserHistoryController({
62
+ initialHistoryState: window.history.state,
63
+ maxHistoryStateSnapshots: 50,
64
+ readHistoryState: () => window.history.state,
65
+ readCurrentHref: () => window.location.href,
66
+ pushHistoryState: (state, href) => pushHistoryStateWithoutNotify(state, "", href),
67
+ replaceHistoryState: (state, href) => replaceHistoryStateWithoutNotify(state, "", href),
68
+ readVisibleNavigationMetadata: () => {
69
+ if (!hasBrowserRouterState()) return null;
70
+ const routerState = getBrowserRouterState();
71
+ return {
72
+ bfcacheIds: routerState.bfcacheIds,
73
+ previousNextUrl: routerState.previousNextUrl
74
+ };
75
+ }
76
+ });
60
77
  const browserNavigationController = createAppBrowserNavigationController({
78
+ basePath: __basePath,
61
79
  getRouteManifest: getBrowserRouteManifest,
62
- syncHistoryStatePreviousNextUrl: syncCurrentHistoryStatePreviousNextUrl
80
+ syncHistoryStatePreviousNextUrl: (previousNextUrl, bfcacheIds) => historyController.syncCurrentHistoryStatePreviousNextUrl(previousNextUrl, bfcacheIds)
63
81
  });
64
82
  const discardedServerActionRefreshScheduler = createDiscardedServerActionRefreshScheduler({ runRefresh() {
65
83
  clearClientNavigationCaches();
@@ -96,67 +114,30 @@ const mpaNavigationScheduler = new AppBrowserMpaNavigationScheduler();
96
114
  const unresolvedMpaNavigation = new Promise(() => {});
97
115
  const RSC_HMR_SETTLE_DELAY_MS = 150;
98
116
  let latestRscHmrUpdateId = 0;
99
- const initialHistoryBfcacheVersion = readHistoryStateBfcacheVersion(window.history.state);
100
- let currentBfcacheVersion = initialHistoryBfcacheVersion === null ? 0 : initialHistoryBfcacheVersion + 1;
101
- let currentHistoryTraversalIndex = readHistoryStateTraversalIndex(window.history.state) ?? 0;
102
- let nextHistoryTraversalIndex = currentHistoryTraversalIndex;
103
- function isCurrentBfcacheVersion(state) {
104
- return isHistoryStateBfcacheVersionCurrent(state, currentBfcacheVersion);
105
- }
117
+ let synchronousPopstateScrollRestoreNavigationId = null;
106
118
  function waitForRscHmrSettle(delayMs = RSC_HMR_SETTLE_DELAY_MS) {
107
119
  return new Promise((resolve) => {
108
120
  window.setTimeout(resolve, delayMs);
109
121
  });
110
122
  }
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
- function allocateNavigationHistoryTraversalIndex(historyUpdateMode) {
120
- switch (historyUpdateMode) {
121
- case "push": return nextHistoryTraversalIndex + 1;
122
- case "replace": return currentHistoryTraversalIndex;
123
- case void 0: return null;
124
- default: throw new Error("[vinext] Unknown history update mode: " + String(historyUpdateMode));
125
- }
126
- }
127
- function commitHistoryTraversalIndex(index) {
128
- currentHistoryTraversalIndex = index;
129
- if (index !== null) nextHistoryTraversalIndex = Math.max(nextHistoryTraversalIndex, index);
130
- }
131
- function commitHashOnlyNavigation(href, historyUpdateMode, scroll) {
132
- const navigationHistoryIndex = allocateNavigationHistoryTraversalIndex(historyUpdateMode);
133
- const previousNextUrl = hasBrowserRouterState() ? getBrowserRouterState().previousNextUrl : readHistoryStatePreviousNextUrl(window.history.state);
134
- const bfcacheIds = hasBrowserRouterState() ? getBrowserRouterState().bfcacheIds : readCurrentBfcacheVersionHistoryIds(window.history.state);
135
- const historyState = createHistoryStateWithNavigationMetadata(createHashOnlyNavigationBaseHistoryState(historyUpdateMode, scroll), {
136
- bfcacheIds,
137
- bfcacheVersion: bfcacheIds === null ? void 0 : currentBfcacheVersion,
138
- previousNextUrl,
139
- traversalIndex: navigationHistoryIndex
123
+ function restoreHistoryStateSnapshot(historyState) {
124
+ const navId = browserNavigationController.getActiveNavigationId();
125
+ let restored = false;
126
+ flushSync(() => {
127
+ restored = historyController.restoreHistorySnapshot({
128
+ historyState,
129
+ stageClientParams,
130
+ approveVisibleRestore: ({ state, beforeCommit }) => browserNavigationController.restoreHistorySnapshotVisibleState({
131
+ beforeCommit,
132
+ navId,
133
+ state,
134
+ targetHref: window.location.href
135
+ })
136
+ });
140
137
  });
141
- if (historyUpdateMode === "replace") replaceHistoryStateWithoutNotify(historyState, "", href);
142
- else pushHistoryStateWithoutNotify(historyState, "", href);
143
- commitHistoryTraversalIndex(navigationHistoryIndex);
144
- }
145
- function createHashOnlyNavigationBaseHistoryState(historyUpdateMode, scroll) {
146
- if (historyUpdateMode !== "replace") return null;
147
- return scroll ? stripVinextScrollState(window.history.state) : window.history.state;
148
- }
149
- function stripVinextScrollState(state) {
150
- if (!state || typeof state !== "object") return state;
151
- const nextState = {};
152
- for (const [key, value] of Object.entries(state)) {
153
- if (key === "__vinext_scrollX" || key === "__vinext_scrollY") continue;
154
- nextState[key] = value;
155
- }
156
- return Object.keys(nextState).length > 0 ? nextState : null;
157
- }
158
- function commitTraversalIndexFromHistoryState(historyState) {
159
- commitHistoryTraversalIndex(readHistoryStateTraversalIndex(historyState));
138
+ if (!restored) return false;
139
+ commitClientNavigationState();
140
+ return true;
160
141
  }
161
142
  function getBrowserRouterState() {
162
143
  return browserNavigationController.getBrowserRouterState();
@@ -190,7 +171,7 @@ function clearPrefetchState() {
190
171
  function clearClientNavigationCaches() {
191
172
  clearVisitedResponseCache();
192
173
  clearPrefetchState();
193
- invalidateRestorableBfcacheIds();
174
+ historyController.invalidateRestorableClientState();
194
175
  }
195
176
  function isSettledPrefetchCacheEntry(entry) {
196
177
  return entry.outcome === "cache-seeded" && entry.pending === void 0 && entry.snapshot !== void 0;
@@ -258,28 +239,6 @@ async function learnOptimisticRouteTemplatesFromPrefetchCache(options) {
258
239
  if (learning.length === 0) return;
259
240
  await Promise.allSettled(learning);
260
241
  }
261
- function areBfcacheIdMapsEqual(a, b) {
262
- if (a === b) return true;
263
- if (a === null || b === null) return false;
264
- const aEntries = Object.entries(a);
265
- const bEntries = Object.entries(b);
266
- if (aEntries.length !== bEntries.length) return false;
267
- return aEntries.every(([key, value]) => b[key] === value);
268
- }
269
- function isHistoryStateNavigationMetadataInSync(state, previousNextUrl, bfcacheIds) {
270
- return readHistoryStatePreviousNextUrl(state) === previousNextUrl && (bfcacheIds === void 0 || areBfcacheIdMapsEqual(readHistoryStateBfcacheIds(state), bfcacheIds) && isCurrentBfcacheVersion(state));
271
- }
272
- function syncCurrentHistoryStatePreviousNextUrl(previousNextUrl, bfcacheIds) {
273
- if (isHistoryStateNavigationMetadataInSync(window.history.state, previousNextUrl, bfcacheIds)) return;
274
- const nextHistoryState = createHistoryStateWithNavigationMetadata(window.history.state, {
275
- bfcacheIds,
276
- bfcacheVersion: bfcacheIds === void 0 ? void 0 : currentBfcacheVersion,
277
- previousNextUrl
278
- });
279
- replaceHistoryStateWithoutNotify(nextHistoryState, "", window.location.href);
280
- if (isHistoryStateNavigationMetadataInSync(window.history.state, previousNextUrl, bfcacheIds)) return;
281
- window.history.replaceState(nextHistoryState, "", window.location.href);
282
- }
283
242
  function createActionInitiationSnapshot() {
284
243
  const routerState = getBrowserRouterState();
285
244
  return createServerActionInitiationSnapshot({
@@ -295,37 +254,19 @@ function createNavigationCommitEffect(options) {
295
254
  commitClientNavigationState(void 0, { releaseSnapshot: true });
296
255
  return;
297
256
  }
298
- const targetHref = new URL(href, window.location.origin).href;
299
- const preserveExistingState = historyUpdateMode === "replace";
300
- const navigationHistoryIndex = targetHistoryIndex !== void 0 ? targetHistoryIndex : allocateNavigationHistoryTraversalIndex(historyUpdateMode);
301
- const historyState = createHistoryStateWithNavigationMetadata(preserveExistingState ? window.history.state : null, {
257
+ historyController.commitNavigationHistory({
302
258
  bfcacheIds,
303
- bfcacheVersion: currentBfcacheVersion,
259
+ href,
260
+ historyUpdateMode,
304
261
  previousNextUrl,
305
- traversalIndex: navigationHistoryIndex
262
+ stageClientParams: () => stageClientParams(params),
263
+ targetHistoryIndex
306
264
  });
307
- let wroteHistoryState = false;
308
- if (historyUpdateMode === "replace" && window.location.href !== targetHref) {
309
- stageClientParams(params);
310
- replaceHistoryStateWithoutNotify(historyState, "", href);
311
- wroteHistoryState = true;
312
- commitHistoryTraversalIndex(navigationHistoryIndex);
313
- } else if (historyUpdateMode === "push" && window.location.href !== targetHref) {
314
- stageClientParams(params);
315
- pushHistoryStateWithoutNotify(historyState, "", href);
316
- wroteHistoryState = true;
317
- commitHistoryTraversalIndex(navigationHistoryIndex);
318
- }
319
- if (!wroteHistoryState) {
320
- syncCurrentHistoryStatePreviousNextUrl(previousNextUrl, bfcacheIds);
321
- stageClientParams(params);
322
- if (targetHistoryIndex !== void 0) commitHistoryTraversalIndex(targetHistoryIndex);
323
- }
324
265
  pendingNavigationRecoveryHref = null;
325
266
  commitClientNavigationState(navId);
326
267
  };
327
268
  }
328
- async function renderNavigationPayload(payload, navigationSnapshot, targetHref, navId, historyUpdateMode, params, previousNextUrl, pendingRouterState, payloadOrigin, actionType = "navigate", operationLane = "navigation", traversalIntent = null, scrollIntent = null, restoredBfcacheIds = null) {
269
+ 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") {
329
270
  syncServerActionHttpFallbackHead(null);
330
271
  try {
331
272
  return await browserNavigationController.renderNavigationPayload({
@@ -344,9 +285,11 @@ async function renderNavigationPayload(payload, navigationSnapshot, targetHref,
344
285
  previousNextUrl,
345
286
  scrollIntent,
346
287
  restoredBfcacheIds,
288
+ reuseCurrentBfcacheIds,
347
289
  targetHistoryIndex: traversalIntent === null ? void 0 : traversalIntent.targetHistoryIndex,
348
290
  targetHref,
349
- navId
291
+ navId,
292
+ visibleCommitMode
350
293
  });
351
294
  } catch (error) {
352
295
  pendingNavigationRecoveryHref = null;
@@ -450,9 +393,8 @@ function storeVisitedResponseSnapshot(rscUrl, interceptionContext, snapshot, par
450
393
  response: snapshot
451
394
  }));
452
395
  }
453
- function isSamePageSearchNavigation(currentSnapshot, targetUrl) {
454
- if (stripBasePath(targetUrl.pathname, __basePath) !== currentSnapshot.pathname) return false;
455
- return targetUrl.searchParams.toString() !== currentSnapshot.searchParams.toString();
396
+ function clientNavigationSnapshotHref(snapshot) {
397
+ return `${window.location.origin}${createSnapshotPathAndSearch(snapshot)}`;
456
398
  }
457
399
  function getRequestState(navigationKind, targetPathname, previousNextUrlOverride, traverseHistoryState) {
458
400
  if (previousNextUrlOverride !== void 0) return {
@@ -476,6 +418,16 @@ function getRequestState(navigationKind, targetPathname, previousNextUrlOverride
476
418
  interceptionContext: manifestInterceptionContext,
477
419
  previousNextUrl: window.location.pathname + window.location.search
478
420
  };
421
+ const middlewareRewriteInterceptionContext = resolveMiddlewareRewriteNavigationInterceptionContext({
422
+ basePath: __basePath,
423
+ currentPathname: window.location.pathname,
424
+ routeManifest: getBrowserRouteManifest(),
425
+ targetPathname
426
+ });
427
+ if (middlewareRewriteInterceptionContext !== null) return {
428
+ interceptionContext: middlewareRewriteInterceptionContext,
429
+ previousNextUrl: window.location.pathname + window.location.search
430
+ };
479
431
  return {
480
432
  interceptionContext: null,
481
433
  previousNextUrl: null
@@ -584,6 +536,9 @@ function BrowserRoot({ initialElements, initialNavigationSnapshot }) {
584
536
  setMountedSlotsHeader(null);
585
537
  };
586
538
  }, [setTreeStateValue]);
539
+ useLayoutEffect(() => {
540
+ historyController.rememberHistoryStateSnapshot(treeState);
541
+ }, [treeState]);
587
542
  useLayoutEffect(() => {
588
543
  setMountedSlotsHeader(getMountedSlotIdsHeader(stateRef.current.elements));
589
544
  removeStylesheetLinksCoveredByInlineCss();
@@ -591,12 +546,10 @@ function BrowserRoot({ initialElements, initialNavigationSnapshot }) {
591
546
  }, [treeState.elements]);
592
547
  useLayoutEffect(() => {
593
548
  if (treeState.renderId !== 0) return;
594
- replaceHistoryStateWithoutNotify(createHistoryStateWithNavigationMetadata(window.history.state, {
549
+ historyController.writeHydratedHistoryMetadata({
595
550
  bfcacheIds: treeState.bfcacheIds,
596
- bfcacheVersion: currentBfcacheVersion,
597
- previousNextUrl: treeState.previousNextUrl,
598
- traversalIndex: currentHistoryTraversalIndex
599
- }), "", window.location.href);
551
+ previousNextUrl: treeState.previousNextUrl
552
+ });
600
553
  }, [
601
554
  treeState.bfcacheIds,
602
555
  treeState.previousNextUrl,
@@ -634,16 +587,16 @@ function restorePopstateScrollPosition(state, options) {
634
587
  return;
635
588
  }
636
589
  const y = Number(state.__vinext_scrollY);
637
- retryScrollTo("__vinext_scrollX" in state ? Number(state.__vinext_scrollX) : 0, y, { shouldContinue });
590
+ retryScrollTo("__vinext_scrollX" in state ? Number(state.__vinext_scrollX) : 0, y, {
591
+ minFrames: 1,
592
+ shouldContinue
593
+ });
638
594
  }
639
595
  function isSameAppRoutePopstateTarget(href) {
640
596
  if (!hasBrowserRouterState()) return false;
641
597
  const target = new URL(href, window.location.origin);
642
598
  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;
599
+ return createBasePathStrippedPathAndSearch(target, __basePath) === createSnapshotPathAndSearch(routerState.navigationSnapshot);
647
600
  }
648
601
  let isPageUnloading = false;
649
602
  const RSC_RELOAD_KEY = "__vinext_rsc_initial_reload__";
@@ -724,11 +677,11 @@ function applyRuntimeRscBootstrap(rsc) {
724
677
  if (rsc.nav) restoreHydrationNavigationContext(rsc.nav.pathname, rsc.nav.searchParams, params);
725
678
  }
726
679
  function registerServerActionCallback() {
727
- setServerCallback(async (id, args) => {
680
+ const serverActionCallback = async (id, args) => {
728
681
  syncServerActionHttpFallbackHead(null);
729
682
  const temporaryReferences = createTemporaryReferenceSet();
730
683
  const actionInitiation = createActionInitiationSnapshot();
731
- syncCurrentHistoryStatePreviousNextUrl(actionInitiation.routerState.previousNextUrl, actionInitiation.routerState.bfcacheIds);
684
+ historyController.syncCurrentHistoryStatePreviousNextUrl(actionInitiation.routerState.previousNextUrl, actionInitiation.routerState.bfcacheIds);
732
685
  const body = await encodeReply(args, { temporaryReferences });
733
686
  const { headers } = resolveServerActionRequestState({
734
687
  actionId: id,
@@ -766,6 +719,7 @@ function registerServerActionCallback() {
766
719
  return;
767
720
  }
768
721
  const revalidation = parseServerActionRevalidationHeader(fetchResponse.headers);
722
+ if (revalidation !== "none") clearClientNavigationCaches();
769
723
  const invalidResponseError = await readInvalidServerActionResponseError(fetchResponse.clone(), actionRedirectTarget !== null);
770
724
  if (invalidResponseError) throw invalidResponseError;
771
725
  if (actionRedirectTarget && !shouldCheckRscCompatibilityForServerActionResponse(fetchResponse)) {
@@ -778,7 +732,7 @@ function registerServerActionCallback() {
778
732
  statusText: "OK"
779
733
  }) : fetchResponse;
780
734
  const result = await createFromFetch(Promise.resolve(flightResponse), { temporaryReferences });
781
- if (shouldClearClientNavigationCachesForServerActionResult(result, revalidation)) clearClientNavigationCaches();
735
+ if (revalidation === "none" && shouldClearClientNavigationCachesForServerActionResult(result, revalidation)) clearClientNavigationCaches();
782
736
  if (actionRedirectTarget) {
783
737
  if (isServerActionResult(result) && result.root !== void 0) {
784
738
  const decoded = AppElementsWire.decode(result.root);
@@ -809,6 +763,10 @@ function registerServerActionCallback() {
809
763
  return;
810
764
  }
811
765
  return commitSameUrlNavigatePayload(Promise.resolve(AppElementsWire.decode(result)), actionInitiation, void 0, revalidation);
766
+ };
767
+ setServerCallback((id, args) => {
768
+ const releaseCacheInvalidationGuard = historyController.beginCacheInvalidationGuard();
769
+ return Promise.resolve().then(() => serverActionCallback(id, args)).finally(releaseCacheInvalidationGuard);
812
770
  });
813
771
  }
814
772
  async function main() {
@@ -825,10 +783,7 @@ async function main() {
825
783
  function bootstrapHydration(rscStream) {
826
784
  const root = decodeAppElementsPromise(createFromReadableStream(rscStream));
827
785
  const initialNavigationSnapshot = createClientNavigationRenderSnapshot(window.location.href, latestClientParams);
828
- replaceHistoryStateWithoutNotify(createHistoryStateWithNavigationMetadata(window.history.state, {
829
- previousNextUrl: null,
830
- traversalIndex: currentHistoryTraversalIndex
831
- }), "", window.location.href);
786
+ historyController.writeBootstrapHistoryMetadata();
832
787
  const onUncaughtError = import.meta.env.DEV ? devOnUncaughtError : createOnUncaughtError(() => pendingNavigationRecoveryHref);
833
788
  const formState = consumeInitialFormState(getVinextBrowserGlobal());
834
789
  const hydrateRootOptions = import.meta.env.DEV ? createVinextHydrateRootOptions({
@@ -837,6 +792,7 @@ function bootstrapHydration(rscStream) {
837
792
  onUncaughtError
838
793
  }) : createVinextHydrateRootOptions({
839
794
  formState,
795
+ onCaughtError: prodOnCaughtError,
840
796
  onUncaughtError
841
797
  });
842
798
  window.__VINEXT_RSC_ROOT__ = hydrateRootInTransition({
@@ -851,8 +807,8 @@ function bootstrapHydration(rscStream) {
851
807
  });
852
808
  registerNavigationRuntimeFunctions({
853
809
  clearNavigationCaches: clearClientNavigationCaches,
854
- commitHashNavigation: commitHashOnlyNavigation,
855
- navigate: async function navigateRsc(href, redirectDepth = 0, navigationKind = "navigate", historyUpdateMode, previousNextUrlOverride, programmaticTransition = false, traversalIntent, scrollIntent) {
810
+ commitHashNavigation: (href, historyUpdateMode, scroll) => historyController.commitHashOnlyNavigation(href, historyUpdateMode, scroll),
811
+ navigate: async function navigateRsc(href, redirectDepth = 0, navigationKind = "navigate", historyUpdateMode, previousNextUrlOverride, programmaticTransition = false, traversalIntent, scrollIntent, visibleCommitMode = "transition") {
856
812
  let pendingRouterState = null;
857
813
  const navId = browserNavigationController.beginNavigation();
858
814
  discardedServerActionRefreshScheduler.markNavigationStart();
@@ -861,15 +817,13 @@ function bootstrapHydration(rscStream) {
861
817
  let currentPrevNextUrl = previousNextUrlOverride;
862
818
  let redirectCount = redirectDepth;
863
819
  let detachedNavigationCommits = false;
864
- const activeTraversalIntent = navigationKind === "traverse" ? traversalIntent ?? resolveHistoryTraversalIntent({
865
- currentHistoryIndex: currentHistoryTraversalIndex,
866
- historyState: window.history.state
867
- }) : null;
820
+ const activeTraversalIntent = navigationKind === "traverse" ? traversalIntent ?? historyController.resolveTraversalIntent(window.history.state) : null;
868
821
  const performHardNavigationForScrollIntent = (targetHref) => {
869
822
  consumeAppRouterScrollIntent(scrollIntent ?? null);
870
823
  return browserNavigationController.performHardNavigation(targetHref);
871
824
  };
872
- let restoredBfcacheIds = navigationKind === "traverse" ? readCurrentBfcacheVersionHistoryIds(activeTraversalIntent?.historyState ?? window.history.state) : null;
825
+ let restoredBfcacheIds = navigationKind === "traverse" ? historyController.readCurrentBfcacheVersionHistoryIds(activeTraversalIntent?.historyState ?? window.history.state) : null;
826
+ const reuseCurrentBfcacheIds = navigationKind !== "traverse" || !historyController.isCacheInvalidationGuarded() && historyController.isCurrentBfcacheVersion(activeTraversalIntent?.historyState ?? window.history.state);
873
827
  try {
874
828
  const shouldUsePendingRouterState = programmaticTransition;
875
829
  if (shouldUsePendingRouterState && hasBrowserRouterState()) pendingRouterState = beginPendingBrowserRouterState();
@@ -883,12 +837,19 @@ function bootstrapHydration(rscStream) {
883
837
  const requestState = getRequestState(navigationKind, url.pathname, currentPrevNextUrl, activeTraversalIntent?.historyState);
884
838
  const requestInterceptionContext = requestState.interceptionContext;
885
839
  const requestPreviousNextUrl = requestState.previousNextUrl;
886
- if (navigationKind === "refresh") syncCurrentHistoryStatePreviousNextUrl(requestPreviousNextUrl, getBrowserRouterState().bfcacheIds);
840
+ if (navigationKind === "refresh") historyController.syncCurrentHistoryStatePreviousNextUrl(requestPreviousNextUrl, getBrowserRouterState().bfcacheIds);
887
841
  setPendingPathname(url.pathname, navId);
888
842
  const routerStateAtNavStart = getBrowserRouterState();
889
843
  const elementsAtNavStart = routerStateAtNavStart.elements;
890
844
  const mountedSlotsHeader = getMountedSlotIdsHeader(elementsAtNavStart);
891
- const shouldBypassNavigationCache = navigationKind === "navigate" && isSamePageSearchNavigation(routerStateAtNavStart.navigationSnapshot, url);
845
+ const earlyIntentDecision = navigationKind === "navigate" ? navigationPlanner.classifyEarlyNavigationIntent({
846
+ basePath: __basePath,
847
+ currentHref: clientNavigationSnapshotHref(routerStateAtNavStart.navigationSnapshot),
848
+ mode: "push",
849
+ scroll: false,
850
+ targetHref: url.href
851
+ }) : null;
852
+ const shouldBypassNavigationCache = earlyIntentDecision?.kind === "flightNavigation" && earlyIntentDecision.bypassNavigationCache;
892
853
  const requestHeaders = createRscRequestHeaders({
893
854
  interceptionContext: requestInterceptionContext,
894
855
  mountedSlotsHeader,
@@ -897,36 +858,32 @@ function bootstrapHydration(rscStream) {
897
858
  const rscUrl = await createRscRequestUrl(url.pathname + url.search, requestHeaders);
898
859
  const cachedRoute = shouldBypassNavigationCache ? null : getVisitedResponse(rscUrl, requestInterceptionContext, mountedSlotsHeader, navigationKind);
899
860
  if (cachedRoute) {
900
- const compatibilityDecision = resolveRscCompatibilityNavigationDecision({
861
+ const cachedFetchDecision = navigationPlanner.classifyRscFetchResult({
901
862
  clientCompatibilityId: CLIENT_RSC_COMPATIBILITY_ID,
863
+ compatibilityIdHeader: cachedRoute.response.compatibilityIdHeader ?? null,
902
864
  currentHref,
903
- origin: window.location.origin,
904
- responseCompatibilityId: cachedRoute.response.compatibilityIdHeader,
905
- responseUrl: cachedRoute.response.url
906
- });
907
- if (compatibilityDecision.kind === "hard-navigate") {
908
- performHardNavigationForScrollIntent(compatibilityDecision.hardNavigationTarget);
909
- return;
910
- }
911
- const cachedRedirectDecision = resolveRscRedirectLifecycleHop({
912
- currentHref,
913
- historyUpdateMode: currentHistoryMode ?? "replace",
865
+ effectiveHistoryUpdateMode: currentHistoryMode ?? "replace",
866
+ hasBody: true,
867
+ isRscContentType: true,
914
868
  origin: window.location.origin,
915
869
  redirectDepth: redirectCount,
916
870
  requestPreviousNextUrl,
917
- responseUrl: cachedRoute.response.url
871
+ responseOk: true,
872
+ responseUrl: cachedRoute.response.url,
873
+ source: "cached",
874
+ streamedRedirectTarget: null
918
875
  });
919
- if (cachedRedirectDecision.kind === "terminal-hard-navigation") {
920
- if (cachedRedirectDecision.reason === "maxRedirectsExceeded") console.error("[vinext] Too many RSC redirects — aborting navigation to prevent infinite loop.");
921
- performHardNavigationForScrollIntent(cachedRedirectDecision.href);
876
+ if (cachedFetchDecision.kind === "hardNavigate") {
877
+ if (cachedFetchDecision.reason === "redirectDepthExhausted") console.error("[vinext] Too many RSC redirects — aborting navigation to prevent infinite loop.");
878
+ performHardNavigationForScrollIntent(cachedFetchDecision.url);
922
879
  return;
923
880
  }
924
- if (cachedRedirectDecision.kind === "follow") {
881
+ if (cachedFetchDecision.kind === "followRedirect") {
925
882
  if (navigationKind === "traverse") restoredBfcacheIds = null;
926
- currentHref = cachedRedirectDecision.href;
927
- currentHistoryMode = cachedRedirectDecision.historyUpdateMode;
928
- currentPrevNextUrl = cachedRedirectDecision.previousNextUrl;
929
- redirectCount = cachedRedirectDecision.redirectDepth;
883
+ currentHref = cachedFetchDecision.redirect.href;
884
+ currentHistoryMode = cachedFetchDecision.redirect.historyUpdateMode;
885
+ currentPrevNextUrl = cachedFetchDecision.redirect.previousNextUrl;
886
+ redirectCount = cachedFetchDecision.redirect.redirectDepth;
930
887
  continue;
931
888
  }
932
889
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
@@ -934,7 +891,7 @@ function bootstrapHydration(rscStream) {
934
891
  const cachedNavigationSnapshot = createClientNavigationRenderSnapshot(currentHref, cachedParams);
935
892
  const cachedPayload = decodeAppElementsPromise(createFromFetch(Promise.resolve(restoreRscResponse(cachedRoute.response))));
936
893
  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") {
894
+ 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, visibleCommitMode) === "no-commit") {
938
895
  deleteVisitedResponse(rscUrl, requestInterceptionContext);
939
896
  continue;
940
897
  }
@@ -972,7 +929,7 @@ function bootstrapHydration(rscStream) {
972
929
  if (optimisticPayload !== null) {
973
930
  detachedNavigationCommits = true;
974
931
  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) => {
932
+ 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).catch((error) => {
976
933
  if (browserNavigationController.isCurrentNavigation(navId)) console.error("[vinext] Optimistic RSC navigation error:", error);
977
934
  });
978
935
  }
@@ -989,61 +946,36 @@ function bootstrapHydration(rscStream) {
989
946
  });
990
947
  }
991
948
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
992
- const isRscResponse = (navResponse.headers.get("content-type") ?? "").startsWith("text/x-component");
993
- if (!navResponse.ok || !isRscResponse || !navResponse.body) {
994
- performHardNavigationForScrollIntent(resolveHardNavigationTargetFromRscResponse(navResponseUrl ?? navResponse.url, currentHref, window.location.origin));
995
- return;
996
- }
997
- const compatibilityDecision = resolveRscCompatibilityNavigationDecision({
949
+ const navContentType = navResponse.headers.get("content-type") ?? "";
950
+ const liveFetchDecision = navigationPlanner.classifyRscFetchResult({
998
951
  clientCompatibilityId: CLIENT_RSC_COMPATIBILITY_ID,
952
+ compatibilityIdHeader: navResponse.headers.get(VINEXT_RSC_COMPATIBILITY_ID_HEADER),
999
953
  currentHref,
1000
- origin: window.location.origin,
1001
- responseCompatibilityId: navResponse.headers.get(VINEXT_RSC_COMPATIBILITY_ID_HEADER),
1002
- responseUrl: navResponseUrl ?? navResponse.url
1003
- });
1004
- if (compatibilityDecision.kind === "hard-navigate") {
1005
- performHardNavigationForScrollIntent(compatibilityDecision.hardNavigationTarget);
1006
- return;
1007
- }
1008
- const redirectDecision = resolveRscRedirectLifecycleHop({
1009
- currentHref,
1010
- historyUpdateMode: currentHistoryMode ?? "replace",
954
+ effectiveHistoryUpdateMode: currentHistoryMode ?? "replace",
955
+ hasBody: navResponse.body !== null,
956
+ isRscContentType: navContentType.startsWith(VINEXT_RSC_CONTENT_TYPE),
1011
957
  origin: window.location.origin,
1012
958
  redirectDepth: redirectCount,
1013
959
  requestPreviousNextUrl,
1014
- responseUrl: navResponseUrl ?? navResponse.url
960
+ responseOk: navResponse.ok,
961
+ responseUrl: navResponseUrl ?? navResponse.url,
962
+ source: "live",
963
+ streamedRedirectTarget: navResponse.headers.get(VINEXT_RSC_REDIRECT_HEADER)
1015
964
  });
1016
- if (redirectDecision.kind === "terminal-hard-navigation") {
1017
- if (redirectDecision.reason === "maxRedirectsExceeded") console.error("[vinext] Too many RSC redirects — aborting navigation to prevent infinite loop.");
1018
- performHardNavigationForScrollIntent(redirectDecision.href);
965
+ if (liveFetchDecision.kind === "hardNavigate") {
966
+ if (liveFetchDecision.discardBody) navResponse.body?.cancel().catch(() => {});
967
+ if (liveFetchDecision.reason === "redirectDepthExhausted") console.error("[vinext] Too many RSC redirects — aborting navigation to prevent infinite loop.");
968
+ if (liveFetchDecision.reason === "streamedRedirectLoop") console.error("[vinext] RSC streamed redirect resolved to the current URL — aborting navigation to prevent infinite loop.");
969
+ performHardNavigationForScrollIntent(liveFetchDecision.url);
1019
970
  return;
1020
971
  }
1021
- if (redirectDecision.kind === "follow") {
1022
- if (navigationKind === "traverse") restoredBfcacheIds = null;
1023
- currentHref = redirectDecision.href;
1024
- currentHistoryMode = redirectDecision.historyUpdateMode;
1025
- currentPrevNextUrl = redirectDecision.previousNextUrl;
1026
- redirectCount = redirectDecision.redirectDepth;
1027
- continue;
1028
- }
1029
- const flightRedirectTarget = navResponse.headers.get(VINEXT_RSC_REDIRECT_HEADER);
1030
- if (flightRedirectTarget) {
1031
- navResponse.body?.cancel().catch(() => {});
1032
- const resolvedTarget = new URL(flightRedirectTarget, window.location.origin);
1033
- if (resolvedTarget.origin !== window.location.origin) {
1034
- performHardNavigationForScrollIntent(resolvedTarget.href);
1035
- return;
1036
- }
1037
- if (redirectCount >= 10) {
1038
- console.error("[vinext] Too many RSC redirects — aborting navigation to prevent infinite loop.");
1039
- performHardNavigationForScrollIntent(resolvedTarget.href);
1040
- return;
1041
- }
972
+ if (liveFetchDecision.kind === "followRedirect") {
973
+ if (liveFetchDecision.discardBody) navResponse.body?.cancel().catch(() => {});
1042
974
  if (navigationKind === "traverse") restoredBfcacheIds = null;
1043
- currentHref = `${resolvedTarget.pathname}${resolvedTarget.search}${resolvedTarget.hash}`;
1044
- currentHistoryMode = currentHistoryMode ?? "replace";
1045
- currentPrevNextUrl = requestPreviousNextUrl;
1046
- redirectCount += 1;
975
+ currentHref = liveFetchDecision.redirect.href;
976
+ currentHistoryMode = liveFetchDecision.redirect.historyUpdateMode;
977
+ currentPrevNextUrl = liveFetchDecision.redirect.previousNextUrl;
978
+ redirectCount = liveFetchDecision.redirect.redirectDepth;
1047
979
  continue;
1048
980
  }
1049
981
  const navParams = parseEncodedJsonHeader(navResponse.headers.get("X-Vinext-Params")) ?? {};
@@ -1059,7 +991,7 @@ function bootstrapHydration(rscStream) {
1059
991
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
1060
992
  const rscPayload = decodeAppElementsPromise(createFromFetch(Promise.resolve(reactResponse)));
1061
993
  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;
994
+ 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) !== "committed") return;
1063
995
  if (!browserNavigationController.isCurrentNavigation(navId)) return;
1064
996
  try {
1065
997
  const renderedElements = await rscPayload;
@@ -1087,7 +1019,6 @@ function bootstrapHydration(rscStream) {
1087
1019
  }
1088
1020
  }
1089
1021
  });
1090
- if ("scrollRestoration" in history) history.scrollRestoration = "manual";
1091
1022
  const handlePopstate = createPopstateRestoreHandler({
1092
1023
  getActiveNavigationId: browserNavigationController.getActiveNavigationId.bind(browserNavigationController),
1093
1024
  getPendingNavigation: () => window.__VINEXT_RSC_PENDING__,
@@ -1099,17 +1030,26 @@ function bootstrapHydration(rscStream) {
1099
1030
  restorePopstateScrollPosition,
1100
1031
  setPendingNavigation: (pendingNavigation) => {
1101
1032
  window.__VINEXT_RSC_PENDING__ = pendingNavigation;
1102
- }
1033
+ },
1034
+ shouldSkipScrollRestore: (navId) => synchronousPopstateScrollRestoreNavigationId === navId
1103
1035
  });
1104
1036
  window.addEventListener("popstate", (event) => {
1105
1037
  const href = window.location.href;
1106
1038
  if (isSameAppRoutePopstateTarget(href)) {
1107
1039
  notifyAppRouterTransitionStart(href, "traverse");
1108
- commitTraversalIndexFromHistoryState(event.state);
1040
+ historyController.commitTraversalIndexFromHistoryState(event.state);
1109
1041
  restorePopstateScrollPosition(event.state);
1110
1042
  return;
1111
1043
  }
1112
1044
  handlePopstate(event);
1045
+ if (restoreHistoryStateSnapshot(event.state)) restoreSynchronousPopstateScrollPosition({
1046
+ getActiveNavigationId: () => browserNavigationController.getActiveNavigationId(),
1047
+ isCurrentNavigation: (navId) => browserNavigationController.isCurrentNavigation(navId),
1048
+ markScrollRestoreConsumed: (navId) => {
1049
+ synchronousPopstateScrollRestoreNavigationId = navId;
1050
+ },
1051
+ restorePopstateScrollPosition
1052
+ }, event.state);
1113
1053
  });
1114
1054
  if (import.meta.hot) {
1115
1055
  const applyRscHmrUpdate = async (updateId) => {