vinext 0.1.1 → 0.1.3

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 (266) hide show
  1. package/README.md +2 -5
  2. package/dist/build/client-build-config.d.ts +7 -1
  3. package/dist/build/client-build-config.js +9 -1
  4. package/dist/build/prerender.d.ts +9 -1
  5. package/dist/build/prerender.js +41 -12
  6. package/dist/build/run-prerender.d.ts +10 -2
  7. package/dist/build/run-prerender.js +15 -1
  8. package/dist/check.js +4 -3
  9. package/dist/client/app-nav-failure-handler.d.ts +8 -0
  10. package/dist/client/app-nav-failure-handler.js +44 -0
  11. package/dist/client/navigation-runtime.d.ts +3 -2
  12. package/dist/client/vinext-next-data.d.ts +18 -1
  13. package/dist/client/window-next.d.ts +8 -5
  14. package/dist/client/window-next.js +12 -1
  15. package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
  16. package/dist/config/config-matchers.d.ts +11 -4
  17. package/dist/config/config-matchers.js +88 -16
  18. package/dist/config/next-config.d.ts +59 -4
  19. package/dist/config/next-config.js +149 -48
  20. package/dist/deploy.d.ts +30 -11
  21. package/dist/deploy.js +189 -101
  22. package/dist/entries/app-browser-entry.d.ts +9 -3
  23. package/dist/entries/app-browser-entry.js +21 -3
  24. package/dist/entries/app-rsc-entry.d.ts +2 -0
  25. package/dist/entries/app-rsc-entry.js +71 -6
  26. package/dist/entries/app-rsc-manifest.js +2 -0
  27. package/dist/entries/app-ssr-entry.js +1 -1
  28. package/dist/entries/pages-client-entry.js +54 -9
  29. package/dist/entries/pages-server-entry.js +48 -11
  30. package/dist/index.d.ts +0 -2
  31. package/dist/index.js +285 -139
  32. package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
  33. package/dist/plugins/dynamic-preload-metadata.js +415 -0
  34. package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
  35. package/dist/plugins/extensionless-dynamic-import.js +152 -0
  36. package/dist/plugins/og-assets.js +2 -2
  37. package/dist/plugins/optimize-imports.d.ts +10 -5
  38. package/dist/plugins/optimize-imports.js +27 -21
  39. package/dist/plugins/postcss.js +7 -7
  40. package/dist/plugins/sass.d.ts +53 -24
  41. package/dist/plugins/sass.js +249 -1
  42. package/dist/plugins/typeof-window.d.ts +14 -0
  43. package/dist/plugins/typeof-window.js +150 -0
  44. package/dist/plugins/wasm-module-import.d.ts +15 -0
  45. package/dist/plugins/wasm-module-import.js +50 -0
  46. package/dist/routing/app-route-graph.d.ts +25 -2
  47. package/dist/routing/app-route-graph.js +91 -22
  48. package/dist/routing/file-matcher.d.ts +10 -1
  49. package/dist/routing/file-matcher.js +23 -2
  50. package/dist/routing/pages-router.js +3 -3
  51. package/dist/routing/utils.d.ts +35 -6
  52. package/dist/routing/utils.js +59 -7
  53. package/dist/server/api-handler.d.ts +6 -1
  54. package/dist/server/api-handler.js +21 -15
  55. package/dist/server/app-browser-action-result.d.ts +19 -6
  56. package/dist/server/app-browser-action-result.js +19 -10
  57. package/dist/server/app-browser-entry.js +269 -297
  58. package/dist/server/app-browser-error.d.ts +10 -3
  59. package/dist/server/app-browser-error.js +47 -6
  60. package/dist/server/app-browser-history-controller.d.ts +104 -0
  61. package/dist/server/app-browser-history-controller.js +210 -0
  62. package/dist/server/app-browser-hydration.d.ts +2 -0
  63. package/dist/server/app-browser-hydration.js +1 -0
  64. package/dist/server/app-browser-navigation-controller.d.ts +7 -4
  65. package/dist/server/app-browser-navigation-controller.js +33 -9
  66. package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
  67. package/dist/server/app-browser-rsc-redirect.js +30 -8
  68. package/dist/server/app-browser-server-action-navigation.d.ts +6 -0
  69. package/dist/server/app-browser-server-action-navigation.js +9 -0
  70. package/dist/server/app-browser-state.js +4 -7
  71. package/dist/server/app-browser-stream.js +86 -43
  72. package/dist/server/app-browser-visible-commit.js +1 -1
  73. package/dist/server/app-elements-wire.d.ts +6 -1
  74. package/dist/server/app-elements-wire.js +14 -4
  75. package/dist/server/app-elements.d.ts +2 -2
  76. package/dist/server/app-elements.js +2 -2
  77. package/dist/server/app-fallback-renderer.d.ts +3 -1
  78. package/dist/server/app-fallback-renderer.js +6 -2
  79. package/dist/server/app-middleware.js +1 -0
  80. package/dist/server/app-optimistic-routing.js +24 -3
  81. package/dist/server/app-page-boundary-render.d.ts +3 -1
  82. package/dist/server/app-page-boundary-render.js +31 -16
  83. package/dist/server/app-page-cache-render.d.ts +53 -0
  84. package/dist/server/app-page-cache-render.js +91 -0
  85. package/dist/server/app-page-cache.d.ts +16 -2
  86. package/dist/server/app-page-cache.js +71 -8
  87. package/dist/server/app-page-dispatch.d.ts +34 -0
  88. package/dist/server/app-page-dispatch.js +167 -97
  89. package/dist/server/app-page-element-builder.d.ts +23 -2
  90. package/dist/server/app-page-element-builder.js +42 -10
  91. package/dist/server/app-page-execution.d.ts +7 -2
  92. package/dist/server/app-page-execution.js +53 -18
  93. package/dist/server/app-page-probe.d.ts +1 -0
  94. package/dist/server/app-page-probe.js +4 -0
  95. package/dist/server/app-page-render-observation.d.ts +3 -1
  96. package/dist/server/app-page-render-observation.js +17 -1
  97. package/dist/server/app-page-render.d.ts +13 -2
  98. package/dist/server/app-page-render.js +48 -17
  99. package/dist/server/app-page-request.d.ts +3 -0
  100. package/dist/server/app-page-request.js +5 -3
  101. package/dist/server/app-page-response.js +1 -1
  102. package/dist/server/app-page-route-wiring.d.ts +5 -1
  103. package/dist/server/app-page-route-wiring.js +21 -11
  104. package/dist/server/app-page-stream.d.ts +16 -9
  105. package/dist/server/app-page-stream.js +12 -9
  106. package/dist/server/app-pages-bridge.d.ts +18 -0
  107. package/dist/server/app-pages-bridge.js +22 -5
  108. package/dist/server/app-ppr-fallback-shell-render.d.ts +17 -0
  109. package/dist/server/app-ppr-fallback-shell-render.js +26 -0
  110. package/dist/server/app-ppr-fallback-shell.d.ts +13 -1
  111. package/dist/server/app-ppr-fallback-shell.js +8 -1
  112. package/dist/server/app-route-handler-dispatch.js +9 -2
  113. package/dist/server/app-route-handler-policy.d.ts +1 -0
  114. package/dist/server/app-route-handler-response.js +11 -10
  115. package/dist/server/app-route-handler-runtime.js +12 -1
  116. package/dist/server/app-router-entry.js +5 -0
  117. package/dist/server/app-rsc-cache-busting.js +2 -0
  118. package/dist/server/app-rsc-handler.d.ts +25 -0
  119. package/dist/server/app-rsc-handler.js +153 -53
  120. package/dist/server/app-rsc-response-finalizer.js +1 -1
  121. package/dist/server/app-rsc-route-matching.d.ts +3 -0
  122. package/dist/server/app-rsc-route-matching.js +2 -0
  123. package/dist/server/app-segment-config.d.ts +9 -1
  124. package/dist/server/app-segment-config.js +12 -3
  125. package/dist/server/app-server-action-execution.d.ts +12 -0
  126. package/dist/server/app-server-action-execution.js +47 -15
  127. package/dist/server/app-ssr-entry.d.ts +2 -0
  128. package/dist/server/app-ssr-entry.js +81 -8
  129. package/dist/server/app-ssr-stream.js +9 -1
  130. package/dist/server/cache-control.js +4 -0
  131. package/dist/server/dev-lockfile.js +2 -1
  132. package/dist/server/dev-server.d.ts +2 -2
  133. package/dist/server/dev-server.js +287 -63
  134. package/dist/server/headers.d.ts +8 -1
  135. package/dist/server/headers.js +8 -1
  136. package/dist/server/hybrid-route-priority.d.ts +22 -0
  137. package/dist/server/hybrid-route-priority.js +33 -0
  138. package/dist/server/image-optimization.d.ts +18 -9
  139. package/dist/server/image-optimization.js +37 -23
  140. package/dist/server/implicit-tags.d.ts +2 -1
  141. package/dist/server/implicit-tags.js +4 -1
  142. package/dist/server/instrumentation-runtime.d.ts +6 -0
  143. package/dist/server/instrumentation-runtime.js +8 -0
  144. package/dist/server/isr-decision.d.ts +79 -0
  145. package/dist/server/isr-decision.js +70 -0
  146. package/dist/server/metadata-route-response.js +5 -3
  147. package/dist/server/middleware-runtime.d.ts +13 -0
  148. package/dist/server/middleware-runtime.js +11 -7
  149. package/dist/server/middleware.js +1 -0
  150. package/dist/server/navigation-planner.d.ts +186 -22
  151. package/dist/server/navigation-planner.js +302 -0
  152. package/dist/server/navigation-trace.d.ts +18 -1
  153. package/dist/server/navigation-trace.js +18 -1
  154. package/dist/server/normalize-path.d.ts +0 -8
  155. package/dist/server/normalize-path.js +3 -1
  156. package/dist/server/otel-tracer-extension.d.ts +45 -0
  157. package/dist/server/otel-tracer-extension.js +89 -0
  158. package/dist/server/pages-api-route.d.ts +20 -3
  159. package/dist/server/pages-api-route.js +19 -3
  160. package/dist/server/pages-asset-tags.d.ts +16 -4
  161. package/dist/server/pages-asset-tags.js +22 -12
  162. package/dist/server/pages-data-route.d.ts +8 -1
  163. package/dist/server/pages-data-route.js +16 -3
  164. package/dist/server/pages-get-initial-props.d.ts +54 -4
  165. package/dist/server/pages-get-initial-props.js +43 -1
  166. package/dist/server/pages-node-compat.d.ts +3 -11
  167. package/dist/server/pages-node-compat.js +175 -122
  168. package/dist/server/pages-page-data.d.ts +39 -2
  169. package/dist/server/pages-page-data.js +261 -46
  170. package/dist/server/pages-page-handler.d.ts +5 -2
  171. package/dist/server/pages-page-handler.js +78 -25
  172. package/dist/server/pages-page-response.d.ts +47 -2
  173. package/dist/server/pages-page-response.js +73 -9
  174. package/dist/server/pages-readiness.d.ts +1 -1
  175. package/dist/server/pages-request-pipeline.d.ts +16 -1
  176. package/dist/server/pages-request-pipeline.js +96 -38
  177. package/dist/server/pregenerated-concrete-paths.d.ts +1 -17
  178. package/dist/server/pregenerated-concrete-paths.js +2 -19
  179. package/dist/server/prerender-manifest.d.ts +33 -0
  180. package/dist/server/prerender-manifest.js +54 -0
  181. package/dist/server/prerender-route-params.d.ts +1 -2
  182. package/dist/server/prod-server.d.ts +39 -1
  183. package/dist/server/prod-server.js +107 -37
  184. package/dist/server/request-pipeline.d.ts +3 -15
  185. package/dist/server/request-pipeline.js +58 -47
  186. package/dist/server/rsc-stream-hints.d.ts +5 -1
  187. package/dist/server/rsc-stream-hints.js +6 -1
  188. package/dist/server/seed-cache.js +10 -18
  189. package/dist/shims/app-router-scroll-state.d.ts +3 -1
  190. package/dist/shims/app-router-scroll-state.js +14 -2
  191. package/dist/shims/app-router-scroll.d.ts +3 -0
  192. package/dist/shims/app-router-scroll.js +28 -18
  193. package/dist/shims/cache-runtime.js +12 -4
  194. package/dist/shims/cache.d.ts +1 -0
  195. package/dist/shims/cache.js +1 -1
  196. package/dist/shims/cdn-cache.d.ts +5 -5
  197. package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
  198. package/dist/shims/dynamic-preload-chunks.js +79 -0
  199. package/dist/shims/dynamic.d.ts +4 -0
  200. package/dist/shims/dynamic.js +4 -2
  201. package/dist/shims/error-boundary.d.ts +6 -4
  202. package/dist/shims/error-boundary.js +7 -0
  203. package/dist/shims/error.js +38 -11
  204. package/dist/shims/error.react-server.d.ts +9 -0
  205. package/dist/shims/error.react-server.js +6 -0
  206. package/dist/shims/fetch-cache.d.ts +11 -1
  207. package/dist/shims/fetch-cache.js +55 -20
  208. package/dist/shims/hash-scroll.js +6 -1
  209. package/dist/shims/head.js +6 -1
  210. package/dist/shims/headers.d.ts +16 -2
  211. package/dist/shims/headers.js +66 -5
  212. package/dist/shims/image-config.js +7 -1
  213. package/dist/shims/internal/als-registry.js +28 -1
  214. package/dist/shims/internal/app-route-detection.d.ts +6 -3
  215. package/dist/shims/internal/app-route-detection.js +18 -23
  216. package/dist/shims/internal/app-router-context.d.ts +5 -0
  217. package/dist/shims/internal/hybrid-client-route-owner.d.ts +31 -0
  218. package/dist/shims/internal/hybrid-client-route-owner.js +143 -0
  219. package/dist/shims/internal/navigation-untracked.d.ts +35 -0
  220. package/dist/shims/internal/navigation-untracked.js +55 -0
  221. package/dist/shims/internal/pages-data-target.d.ts +7 -2
  222. package/dist/shims/internal/pages-data-target.js +17 -8
  223. package/dist/shims/internal/pages-router-accessor.d.ts +19 -0
  224. package/dist/shims/internal/pages-router-accessor.js +13 -0
  225. package/dist/shims/internal/router-context.d.ts +2 -1
  226. package/dist/shims/internal/router-context.js +3 -1
  227. package/dist/shims/link.js +12 -5
  228. package/dist/shims/metadata.d.ts +6 -2
  229. package/dist/shims/metadata.js +32 -14
  230. package/dist/shims/navigation.d.ts +14 -17
  231. package/dist/shims/navigation.js +93 -46
  232. package/dist/shims/ppr-fallback-shell.d.ts +5 -1
  233. package/dist/shims/ppr-fallback-shell.js +28 -7
  234. package/dist/shims/router.d.ts +13 -2
  235. package/dist/shims/router.js +434 -116
  236. package/dist/shims/script-nonce-context.d.ts +1 -1
  237. package/dist/shims/script-nonce-context.js +11 -3
  238. package/dist/shims/server.d.ts +33 -2
  239. package/dist/shims/server.js +75 -18
  240. package/dist/shims/slot.js +1 -1
  241. package/dist/shims/unified-request-context.js +2 -0
  242. package/dist/typegen.js +1 -0
  243. package/dist/utils/built-asset-url.d.ts +4 -0
  244. package/dist/utils/built-asset-url.js +11 -0
  245. package/dist/utils/client-build-manifest.js +15 -5
  246. package/dist/utils/client-runtime-metadata.d.ts +45 -0
  247. package/dist/utils/client-runtime-metadata.js +63 -0
  248. package/dist/utils/commonjs-loader.d.ts +16 -0
  249. package/dist/utils/commonjs-loader.js +100 -0
  250. package/dist/utils/deployment-id.d.ts +8 -0
  251. package/dist/utils/deployment-id.js +22 -0
  252. package/dist/utils/hash.d.ts +17 -1
  253. package/dist/utils/hash.js +36 -1
  254. package/dist/utils/html-limited-bots.d.ts +18 -1
  255. package/dist/utils/html-limited-bots.js +23 -1
  256. package/dist/utils/lazy-chunks.d.ts +27 -1
  257. package/dist/utils/lazy-chunks.js +65 -1
  258. package/dist/utils/manifest-paths.d.ts +20 -2
  259. package/dist/utils/manifest-paths.js +38 -3
  260. package/dist/utils/parse-cookie.d.ts +13 -0
  261. package/dist/utils/parse-cookie.js +52 -0
  262. package/dist/utils/path.d.ts +8 -1
  263. package/dist/utils/path.js +13 -1
  264. package/package.json +2 -2
  265. package/dist/shims/internal/parse-cookie-header.d.ts +0 -14
  266. package/dist/shims/internal/parse-cookie-header.js +0 -30
@@ -1,6 +1,13 @@
1
1
  //#region src/server/app-browser-error.d.ts
2
- declare function createOnUncaughtError(getRecoveryHref: () => string | null): (error: unknown, errorInfo: {
2
+ type VinextHydrateRootErrorInfo = {
3
3
  componentStack?: string;
4
- }) => void;
4
+ errorBoundary?: unknown;
5
+ };
6
+ type HydrateRootErrorHandler = (error: unknown, errorInfo: VinextHydrateRootErrorInfo) => void;
7
+ declare function createOnUncaughtError(): HydrateRootErrorHandler;
8
+ declare function createProdOnCaughtError(onImplicitRootError: HydrateRootErrorHandler): HydrateRootErrorHandler;
9
+ declare function createDevOnCaughtError(onCaughtError: HydrateRootErrorHandler, onImplicitRootError: HydrateRootErrorHandler): HydrateRootErrorHandler;
10
+ declare function prodOnCaughtError(error: unknown, errorInfo: VinextHydrateRootErrorInfo): void;
11
+ declare function prodOnRecoverableError(error: unknown): void;
5
12
  //#endregion
6
- export { createOnUncaughtError };
13
+ export { createDevOnCaughtError, createOnUncaughtError, createProdOnCaughtError, prodOnCaughtError, prodOnRecoverableError };
@@ -1,11 +1,52 @@
1
+ import { isUnknownRecord } from "../utils/record.js";
2
+ import { isNavigationSignalError } from "../utils/navigation-signal.js";
1
3
  //#region src/server/app-browser-error.ts
2
- function createOnUncaughtError(getRecoveryHref) {
4
+ function isImplicitRootErrorBoundary(errorInfo) {
5
+ if (!isUnknownRecord(errorInfo.errorBoundary)) return false;
6
+ const props = errorInfo.errorBoundary.props;
7
+ return isUnknownRecord(props) && props.isImplicitRootErrorBoundary === true;
8
+ }
9
+ function logCaughtError(error, errorInfo) {
10
+ console.error(error);
11
+ if (errorInfo?.componentStack) console.error("The above error occurred in a React component:\n" + errorInfo.componentStack);
12
+ }
13
+ function reportGlobalError(error) {
14
+ if (typeof globalThis.reportError === "function") {
15
+ globalThis.reportError(error);
16
+ return;
17
+ }
18
+ console.error(error);
19
+ }
20
+ function createOnUncaughtError() {
21
+ return (error) => {
22
+ reportGlobalError(error);
23
+ };
24
+ }
25
+ function createProdOnCaughtError(onImplicitRootError) {
3
26
  return (error, errorInfo) => {
4
- console.error(error);
5
- if (errorInfo?.componentStack) console.error("The above error occurred in a React component:\n" + errorInfo.componentStack);
6
- const recoveryHref = getRecoveryHref();
7
- if (recoveryHref !== null) window.location.assign(recoveryHref);
27
+ if (isNavigationSignalError(error)) return;
28
+ if (isImplicitRootErrorBoundary(errorInfo)) {
29
+ onImplicitRootError(error, errorInfo);
30
+ return;
31
+ }
32
+ logCaughtError(error, errorInfo);
8
33
  };
9
34
  }
35
+ function createDevOnCaughtError(onCaughtError, onImplicitRootError) {
36
+ return (error, errorInfo) => {
37
+ if (isImplicitRootErrorBoundary(errorInfo)) {
38
+ onImplicitRootError(error, errorInfo);
39
+ return;
40
+ }
41
+ onCaughtError(error, errorInfo);
42
+ };
43
+ }
44
+ function prodOnCaughtError(error, errorInfo) {
45
+ if (isNavigationSignalError(error)) return;
46
+ logCaughtError(error, errorInfo);
47
+ }
48
+ function prodOnRecoverableError(error) {
49
+ reportGlobalError(error instanceof Error && error.cause !== void 0 ? error.cause : error);
50
+ }
10
51
  //#endregion
11
- export { createOnUncaughtError };
52
+ export { createDevOnCaughtError, createOnUncaughtError, createProdOnCaughtError, prodOnCaughtError, prodOnRecoverableError };
@@ -0,0 +1,104 @@
1
+ import { BfcacheIdMap, HistoryTraversalIntent } from "./app-history-state.js";
2
+ import { AppRouterState } from "./app-browser-state.js";
3
+ import { HistoryUpdateMode } from "./app-browser-navigation-controller.js";
4
+
5
+ //#region src/server/app-browser-history-controller.d.ts
6
+ /**
7
+ * Visible router-state metadata at the instant a hash-only navigation commits.
8
+ * `null` means the browser router tree has not committed yet, so the controller
9
+ * falls back to reading the same facts off the live history entry.
10
+ */
11
+ type VisibleNavigationMetadata = {
12
+ bfcacheIds: BfcacheIdMap | null;
13
+ previousNextUrl: string | null;
14
+ };
15
+ type AppBrowserHistoryControllerDeps = {
16
+ initialHistoryState: unknown;
17
+ maxHistoryStateSnapshots: number; /** Reads `window.history.state`. Injected so the controller stays unit-testable. */
18
+ readHistoryState: () => unknown; /** Reads `window.location.href`. Injected so the controller stays unit-testable. */
19
+ readCurrentHref: () => string; /** Wraps `pushHistoryStateWithoutNotify(state, "", href)`. */
20
+ pushHistoryState: (state: unknown, href: string) => void; /** Wraps `replaceHistoryStateWithoutNotify(state, "", href)`. */
21
+ replaceHistoryState: (state: unknown, href: string) => void;
22
+ readVisibleNavigationMetadata: () => VisibleNavigationMetadata | null;
23
+ };
24
+ /**
25
+ * Candidate visible state resolved from a restorable history snapshot, handed to
26
+ * the entry's approved-visible-restore callback. The controller resolves the
27
+ * candidate and owns the traversal-index commit; the entry owns the actual
28
+ * `AppBrowserNavigationController.restoreHistorySnapshotVisibleState()` call and
29
+ * the `ApprovedVisibleCommit` boundary.
30
+ */
31
+ type RestorableSnapshotCandidate = {
32
+ state: AppRouterState;
33
+ beforeCommit: () => void;
34
+ };
35
+ type RestoreHistorySnapshotOptions = {
36
+ historyState: unknown;
37
+ stageClientParams: (params: Record<string, string | string[]>) => void;
38
+ approveVisibleRestore: (candidate: RestorableSnapshotCandidate) => boolean;
39
+ };
40
+ type CommitNavigationHistoryOptions = {
41
+ bfcacheIds: BfcacheIdMap;
42
+ href: string;
43
+ historyUpdateMode: HistoryUpdateMode | undefined;
44
+ previousNextUrl: string | null;
45
+ targetHistoryIndex?: number | null;
46
+ stageClientParams: () => void;
47
+ };
48
+ /**
49
+ * Owns App Router browser-history metadata and traversal bookkeeping behind a
50
+ * typed seam: traversal index allocation/commit, push/replace/traverse/hash-only
51
+ * history-state writes, BFCache epoch/snapshot invalidation through
52
+ * `RestorableClientStateController`, and restorable-snapshot candidate
53
+ * resolution.
54
+ *
55
+ * Ownership boundary: this is not a second router or visible-state authority. It
56
+ * resolves history facts and delegates visible restoration through an injected
57
+ * approved-commit callback. It never sets router state directly, never imports
58
+ * `applyApprovedVisibleCommit()`, and never bypasses the `ApprovedVisibleCommit`
59
+ * boundary owned by `AppBrowserNavigationController`.
60
+ */
61
+ declare class AppBrowserHistoryController {
62
+ #private;
63
+ constructor(deps: AppBrowserHistoryControllerDeps);
64
+ get currentHistoryTraversalIndex(): number | null;
65
+ allocateNavigationHistoryTraversalIndex(historyUpdateMode: HistoryUpdateMode | undefined): number | null;
66
+ commitHistoryTraversalIndex(index: number | null): void;
67
+ commitTraversalIndexFromHistoryState(historyState: unknown): void;
68
+ resolveTraversalIntent(historyState: unknown): HistoryTraversalIntent;
69
+ readCurrentBfcacheVersionHistoryIds(historyState: unknown): BfcacheIdMap | null;
70
+ isCacheInvalidationGuarded(): boolean;
71
+ isCurrentBfcacheVersion(historyState: unknown): boolean;
72
+ beginCacheInvalidationGuard(): () => void;
73
+ invalidateRestorableClientState(): void;
74
+ rememberHistoryStateSnapshot(state: AppRouterState): void;
75
+ commitHashOnlyNavigation(href: string, historyUpdateMode: HistoryUpdateMode, scroll: boolean): void;
76
+ /**
77
+ * Writes the history entry for an approved push/replace/traverse commit and
78
+ * advances the traversal index. `stageClientParams` runs at the exact point it
79
+ * ran inline in the browser-entry commit effect so client-param staging stays
80
+ * ordered relative to the history write. Mirrors Next.js committing tree state
81
+ * into the history entry during the navigation commit.
82
+ */
83
+ commitNavigationHistory(options: CommitNavigationHistoryOptions): void;
84
+ syncCurrentHistoryStatePreviousNextUrl(previousNextUrl: string | null, bfcacheIds?: BfcacheIdMap | null): void;
85
+ /** Initial history write performed before hydration starts. */
86
+ writeBootstrapHistoryMetadata(): void;
87
+ /** History write performed on the first committed (hydrated) render. */
88
+ writeHydratedHistoryMetadata(options: {
89
+ bfcacheIds: BfcacheIdMap;
90
+ previousNextUrl: string | null;
91
+ }): void;
92
+ /**
93
+ * Resolves a restorable snapshot candidate for the given history entry and
94
+ * commits the traversal index after, and only after, the injected
95
+ * approved-visible-restore callback succeeds. The traversal-index commit and
96
+ * client-param staging run inside `beforeCommit`, which the
97
+ * `AppBrowserNavigationController` invokes only once the `ApprovedVisibleCommit`
98
+ * is approved. Returns false when no snapshot is restorable or the restore is
99
+ * not approved.
100
+ */
101
+ restoreHistorySnapshot(options: RestoreHistorySnapshotOptions): boolean;
102
+ }
103
+ //#endregion
104
+ export { AppBrowserHistoryController, RestorableSnapshotCandidate };
@@ -0,0 +1,210 @@
1
+ import { RestorableClientStateController, createHistoryStateWithNavigationMetadata, readHistoryStateBfcacheIds, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent } from "./app-history-state.js";
2
+ //#region src/server/app-browser-history-controller.ts
3
+ function stripVinextScrollState(state) {
4
+ if (!state || typeof state !== "object") return state;
5
+ const nextState = {};
6
+ for (const [key, value] of Object.entries(state)) {
7
+ if (key === "__vinext_scrollX" || key === "__vinext_scrollY") continue;
8
+ nextState[key] = value;
9
+ }
10
+ return Object.keys(nextState).length > 0 ? nextState : null;
11
+ }
12
+ /**
13
+ * Owns App Router browser-history metadata and traversal bookkeeping behind a
14
+ * typed seam: traversal index allocation/commit, push/replace/traverse/hash-only
15
+ * history-state writes, BFCache epoch/snapshot invalidation through
16
+ * `RestorableClientStateController`, and restorable-snapshot candidate
17
+ * resolution.
18
+ *
19
+ * Ownership boundary: this is not a second router or visible-state authority. It
20
+ * resolves history facts and delegates visible restoration through an injected
21
+ * approved-commit callback. It never sets router state directly, never imports
22
+ * `applyApprovedVisibleCommit()`, and never bypasses the `ApprovedVisibleCommit`
23
+ * boundary owned by `AppBrowserNavigationController`.
24
+ */
25
+ var AppBrowserHistoryController = class {
26
+ #restorableClientState;
27
+ #readHistoryState;
28
+ #readCurrentHref;
29
+ #pushHistoryState;
30
+ #replaceHistoryState;
31
+ #readVisibleNavigationMetadata;
32
+ #currentHistoryTraversalIndex;
33
+ #nextHistoryTraversalIndex;
34
+ constructor(deps) {
35
+ this.#readHistoryState = deps.readHistoryState;
36
+ this.#readCurrentHref = deps.readCurrentHref;
37
+ this.#pushHistoryState = deps.pushHistoryState;
38
+ this.#replaceHistoryState = deps.replaceHistoryState;
39
+ this.#readVisibleNavigationMetadata = deps.readVisibleNavigationMetadata;
40
+ this.#restorableClientState = new RestorableClientStateController({
41
+ initialHistoryState: deps.initialHistoryState,
42
+ maxHistoryStateSnapshots: deps.maxHistoryStateSnapshots
43
+ });
44
+ this.#currentHistoryTraversalIndex = readHistoryStateTraversalIndex(deps.initialHistoryState) ?? 0;
45
+ this.#nextHistoryTraversalIndex = this.#currentHistoryTraversalIndex;
46
+ }
47
+ get currentHistoryTraversalIndex() {
48
+ return this.#currentHistoryTraversalIndex;
49
+ }
50
+ allocateNavigationHistoryTraversalIndex(historyUpdateMode) {
51
+ switch (historyUpdateMode) {
52
+ case "push": return this.#nextHistoryTraversalIndex + 1;
53
+ case "replace": return this.#currentHistoryTraversalIndex;
54
+ case void 0: return null;
55
+ default: throw new Error("[vinext] Unknown history update mode: " + String(historyUpdateMode));
56
+ }
57
+ }
58
+ commitHistoryTraversalIndex(index) {
59
+ this.#currentHistoryTraversalIndex = index;
60
+ if (index !== null) this.#nextHistoryTraversalIndex = Math.max(this.#nextHistoryTraversalIndex, index);
61
+ }
62
+ commitTraversalIndexFromHistoryState(historyState) {
63
+ this.commitHistoryTraversalIndex(readHistoryStateTraversalIndex(historyState));
64
+ }
65
+ resolveTraversalIntent(historyState) {
66
+ return resolveHistoryTraversalIntent({
67
+ currentHistoryIndex: this.#currentHistoryTraversalIndex,
68
+ historyState
69
+ });
70
+ }
71
+ readCurrentBfcacheVersionHistoryIds(historyState) {
72
+ return this.#restorableClientState.readCurrentBfcacheVersionHistoryIds(historyState);
73
+ }
74
+ isCacheInvalidationGuarded() {
75
+ return this.#restorableClientState.isCacheInvalidationGuarded();
76
+ }
77
+ isCurrentBfcacheVersion(historyState) {
78
+ return this.#restorableClientState.isCurrentBfcacheVersion(historyState);
79
+ }
80
+ beginCacheInvalidationGuard() {
81
+ return this.#restorableClientState.beginCacheInvalidationGuard();
82
+ }
83
+ invalidateRestorableClientState() {
84
+ this.#restorableClientState.invalidateClientState();
85
+ }
86
+ rememberHistoryStateSnapshot(state) {
87
+ this.#restorableClientState.rememberHistoryStateSnapshot({
88
+ historyIndex: this.#currentHistoryTraversalIndex,
89
+ state
90
+ });
91
+ }
92
+ commitHashOnlyNavigation(href, historyUpdateMode, scroll) {
93
+ const navigationHistoryIndex = this.allocateNavigationHistoryTraversalIndex(historyUpdateMode);
94
+ const historyState = this.#readHistoryState();
95
+ const visible = this.#readVisibleNavigationMetadata();
96
+ const previousNextUrl = visible ? visible.previousNextUrl : readHistoryStatePreviousNextUrl(historyState);
97
+ const bfcacheIds = visible ? visible.bfcacheIds : this.#restorableClientState.readCurrentBfcacheVersionHistoryIds(historyState);
98
+ const nextHistoryState = createHistoryStateWithNavigationMetadata(this.#createHashOnlyNavigationBaseHistoryState(historyUpdateMode, scroll), {
99
+ bfcacheIds,
100
+ bfcacheVersion: bfcacheIds === null ? void 0 : this.#restorableClientState.currentBfcacheVersion,
101
+ previousNextUrl,
102
+ traversalIndex: navigationHistoryIndex
103
+ });
104
+ if (historyUpdateMode === "replace") this.#replaceHistoryState(nextHistoryState, href);
105
+ else this.#pushHistoryState(nextHistoryState, href);
106
+ this.commitHistoryTraversalIndex(navigationHistoryIndex);
107
+ }
108
+ #createHashOnlyNavigationBaseHistoryState(historyUpdateMode, scroll) {
109
+ if (historyUpdateMode !== "replace") return null;
110
+ const historyState = this.#readHistoryState();
111
+ return scroll ? stripVinextScrollState(historyState) : historyState;
112
+ }
113
+ /**
114
+ * Writes the history entry for an approved push/replace/traverse commit and
115
+ * advances the traversal index. `stageClientParams` runs at the exact point it
116
+ * ran inline in the browser-entry commit effect so client-param staging stays
117
+ * ordered relative to the history write. Mirrors Next.js committing tree state
118
+ * into the history entry during the navigation commit.
119
+ */
120
+ commitNavigationHistory(options) {
121
+ const currentHref = this.#readCurrentHref();
122
+ const origin = new URL(currentHref).origin;
123
+ const targetHref = new URL(options.href, origin).href;
124
+ const preserveExistingState = options.historyUpdateMode === "replace";
125
+ const navigationHistoryIndex = options.targetHistoryIndex !== void 0 ? options.targetHistoryIndex : this.allocateNavigationHistoryTraversalIndex(options.historyUpdateMode);
126
+ const historyState = createHistoryStateWithNavigationMetadata(preserveExistingState ? this.#readHistoryState() : null, {
127
+ bfcacheIds: options.bfcacheIds,
128
+ bfcacheVersion: this.#restorableClientState.currentBfcacheVersion,
129
+ previousNextUrl: options.previousNextUrl,
130
+ traversalIndex: navigationHistoryIndex
131
+ });
132
+ let wroteHistoryState = false;
133
+ if (options.historyUpdateMode === "replace" && currentHref !== targetHref) {
134
+ options.stageClientParams();
135
+ this.#replaceHistoryState(historyState, options.href);
136
+ wroteHistoryState = true;
137
+ this.commitHistoryTraversalIndex(navigationHistoryIndex);
138
+ } else if (options.historyUpdateMode === "push" && currentHref !== targetHref) {
139
+ options.stageClientParams();
140
+ this.#pushHistoryState(historyState, options.href);
141
+ wroteHistoryState = true;
142
+ this.commitHistoryTraversalIndex(navigationHistoryIndex);
143
+ }
144
+ if (!wroteHistoryState) {
145
+ this.syncCurrentHistoryStatePreviousNextUrl(options.previousNextUrl, options.bfcacheIds);
146
+ options.stageClientParams();
147
+ if (options.targetHistoryIndex !== void 0) this.commitHistoryTraversalIndex(options.targetHistoryIndex);
148
+ }
149
+ }
150
+ syncCurrentHistoryStatePreviousNextUrl(previousNextUrl, bfcacheIds) {
151
+ if (this.#isHistoryStateNavigationMetadataInSync(this.#readHistoryState(), previousNextUrl, bfcacheIds)) return;
152
+ const nextHistoryState = createHistoryStateWithNavigationMetadata(this.#readHistoryState(), {
153
+ bfcacheIds,
154
+ bfcacheVersion: bfcacheIds === void 0 ? void 0 : this.#restorableClientState.currentBfcacheVersion,
155
+ previousNextUrl
156
+ });
157
+ this.#replaceHistoryState(nextHistoryState, this.#readCurrentHref());
158
+ if (this.#isHistoryStateNavigationMetadataInSync(this.#readHistoryState(), previousNextUrl, bfcacheIds)) return;
159
+ this.#replaceHistoryState(nextHistoryState, this.#readCurrentHref());
160
+ }
161
+ #isHistoryStateNavigationMetadataInSync(state, previousNextUrl, bfcacheIds) {
162
+ return readHistoryStatePreviousNextUrl(state) === previousNextUrl && (bfcacheIds === void 0 || areBfcacheIdMapsEqual(readHistoryStateBfcacheIds(state), bfcacheIds) && this.#restorableClientState.isCurrentBfcacheVersion(state));
163
+ }
164
+ /** Initial history write performed before hydration starts. */
165
+ writeBootstrapHistoryMetadata() {
166
+ this.#replaceHistoryState(createHistoryStateWithNavigationMetadata(this.#readHistoryState(), {
167
+ previousNextUrl: null,
168
+ traversalIndex: this.#currentHistoryTraversalIndex
169
+ }), this.#readCurrentHref());
170
+ }
171
+ /** History write performed on the first committed (hydrated) render. */
172
+ writeHydratedHistoryMetadata(options) {
173
+ this.#replaceHistoryState(createHistoryStateWithNavigationMetadata(this.#readHistoryState(), {
174
+ bfcacheIds: options.bfcacheIds,
175
+ bfcacheVersion: this.#restorableClientState.currentBfcacheVersion,
176
+ previousNextUrl: options.previousNextUrl,
177
+ traversalIndex: this.#currentHistoryTraversalIndex
178
+ }), this.#readCurrentHref());
179
+ }
180
+ /**
181
+ * Resolves a restorable snapshot candidate for the given history entry and
182
+ * commits the traversal index after, and only after, the injected
183
+ * approved-visible-restore callback succeeds. The traversal-index commit and
184
+ * client-param staging run inside `beforeCommit`, which the
185
+ * `AppBrowserNavigationController` invokes only once the `ApprovedVisibleCommit`
186
+ * is approved. Returns false when no snapshot is restorable or the restore is
187
+ * not approved.
188
+ */
189
+ restoreHistorySnapshot(options) {
190
+ const decision = this.#restorableClientState.resolveHistoryStateSnapshotRestore(options.historyState);
191
+ if (decision.kind === "skip") return false;
192
+ return options.approveVisibleRestore({
193
+ state: decision.state,
194
+ beforeCommit: () => {
195
+ this.commitHistoryTraversalIndex(decision.targetHistoryIndex);
196
+ options.stageClientParams(decision.state.navigationSnapshot.params);
197
+ }
198
+ });
199
+ }
200
+ };
201
+ function areBfcacheIdMapsEqual(a, b) {
202
+ if (a === b) return true;
203
+ if (a === null || b === null) return false;
204
+ const aEntries = Object.entries(a);
205
+ const bEntries = Object.entries(b);
206
+ if (aEntries.length !== bEntries.length) return false;
207
+ return aEntries.every(([key, value]) => b[key] === value);
208
+ }
209
+ //#endregion
210
+ export { AppBrowserHistoryController };
@@ -8,6 +8,7 @@ type HydrateRootChildren = Parameters<HydrateRoot>[1];
8
8
  type HydrateRootReturn = ReturnType<HydrateRoot>;
9
9
  type HydrateRootCaughtErrorHandler = NonNullable<HydrateRootOptions["onCaughtError"]>;
10
10
  type HydrateRootUncaughtErrorHandler = NonNullable<HydrateRootOptions["onUncaughtError"]>;
11
+ type HydrateRootRecoverableErrorHandler = NonNullable<HydrateRootOptions["onRecoverableError"]>;
11
12
  type StartTransition = (action: () => void) => void;
12
13
  declare const RSC_FORM_STATE_GLOBAL = "__VINEXT_RSC_FORM_STATE__";
13
14
  type FormStateGlobal = {
@@ -17,6 +18,7 @@ declare function consumeInitialFormState(global: FormStateGlobal): ReactFormStat
17
18
  declare function createVinextHydrateRootOptions(options: {
18
19
  formState: ReactFormState | null;
19
20
  onCaughtError?: HydrateRootCaughtErrorHandler;
21
+ onRecoverableError?: HydrateRootRecoverableErrorHandler;
20
22
  onUncaughtError: HydrateRootUncaughtErrorHandler;
21
23
  }): HydrateRootOptions;
22
24
  declare function hydrateRootInTransition(options: {
@@ -8,6 +8,7 @@ function consumeInitialFormState(global) {
8
8
  function createVinextHydrateRootOptions(options) {
9
9
  const hydrateOptions = {
10
10
  formState: options.formState,
11
+ ...options.onRecoverableError ? { onRecoverableError: options.onRecoverableError } : {},
11
12
  onUncaughtError: options.onUncaughtError
12
13
  };
13
14
  if (options.onCaughtError) return {
@@ -1,9 +1,10 @@
1
1
  import { RouteManifest } from "../routing/app-route-graph.js";
2
2
  import { AppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
3
- import { ServerActionRevalidationKind } from "./app-browser-action-result.js";
3
+ import { NavigationRuntimeVisibleCommitMode } from "../client/navigation-runtime.js";
4
4
  import { AppElements } from "./app-elements-wire.js";
5
5
  import { OperationLane } from "./navigation-planner.js";
6
- import { ClientNavigationRenderSnapshot, commitClientNavigationState } from "../shims/navigation.js";
6
+ import { ServerActionRevalidationKind } from "./app-browser-action-result.js";
7
+ import { ClientNavigationRenderSnapshot, commitClientNavigationState, createSnapshotPathAndSearch } from "../shims/navigation.js";
7
8
  import { AppNavigationPayloadOrigin, AppRouterState } from "./app-browser-state.js";
8
9
  import { Dispatch, ReactNode } from "react";
9
10
 
@@ -16,6 +17,7 @@ type PendingBrowserRouterState = {
16
17
  };
17
18
  type NavigationPayloadOutcome = "committed" | "no-commit" | "hard-navigate";
18
19
  type HardNavigationMode = "assign" | "replace";
20
+ type BrowserNavigationCommitEffect = () => void;
19
21
  type BrowserNavigationCommitEffectFactory = (options: {
20
22
  bfcacheIds: Readonly<Record<string, string>>;
21
23
  href: string;
@@ -24,7 +26,7 @@ type BrowserNavigationCommitEffectFactory = (options: {
24
26
  params: Record<string, string | string[]>;
25
27
  previousNextUrl: string | null;
26
28
  targetHistoryIndex?: number | null;
27
- }) => () => void;
29
+ }) => BrowserNavigationCommitEffect;
28
30
  type BrowserRouterStateRef = {
29
31
  current: AppRouterState;
30
32
  };
@@ -75,6 +77,7 @@ type BrowserNavigationController = {
75
77
  targetHistoryIndex?: number | null;
76
78
  targetHref: string;
77
79
  navId: number;
80
+ visibleCommitMode?: NavigationRuntimeVisibleCommitMode;
78
81
  }): Promise<NavigationPayloadOutcome>;
79
82
  commitSameUrlNavigatePayload(nextElements: Promise<AppElements>, navigationSnapshot: ClientNavigationRenderSnapshot, returnValue?: {
80
83
  ok: boolean;
@@ -90,6 +93,7 @@ type BrowserNavigationController = {
90
93
  * navigation would otherwise be lost.
91
94
  */
92
95
  drainPrePaintEffects(renderId: number): void;
96
+ clearCommittedNavigationFailureTargets(renderId: number): void;
93
97
  NavigationCommitSignal(this: void, {
94
98
  renderId,
95
99
  children
@@ -99,7 +103,6 @@ type BrowserNavigationController = {
99
103
  }): ReactNode;
100
104
  };
101
105
  declare function clearHardNavigationLoopGuard(): void;
102
- declare function createSnapshotPathAndSearch(snapshot: ClientNavigationRenderSnapshot): string;
103
106
  declare function createBasePathStrippedPathAndSearch(url: URL, basePath: string): string;
104
107
  declare function createAppBrowserNavigationController(deps?: BrowserNavigationControllerDeps): BrowserNavigationController;
105
108
  //#endregion
@@ -1,10 +1,12 @@
1
1
  import { stripBasePath } from "../utils/base-path.js";
2
+ import { clearAppNavigationFailureTarget, getAppNavigationFailureTarget } from "../client/app-nav-failure-handler.js";
2
3
  import { claimAppRouterScrollIntentForCommit, consumeAppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
3
- import { activateNavigationSnapshot, clearPendingPathname, commitClientNavigationState } from "../shims/navigation.js";
4
+ import { activateNavigationSnapshot, clearPendingPathname, commitClientNavigationState, createSnapshotPathAndSearch } from "../shims/navigation.js";
4
5
  import { shouldScheduleRefreshForDiscardedServerAction } from "./app-browser-action-result.js";
5
6
  import { FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, createPendingNavigationCommit } from "./app-browser-state.js";
6
7
  import { applyApprovedVisibleCommit, approveHmrVisibleCommit, approvePendingNavigationCommit, resolveAndClassifyNavigationCommit } from "./app-browser-visible-commit.js";
7
- import { startTransition, useLayoutEffect } from "react";
8
+ import { startTransition, useInsertionEffect, useLayoutEffect } from "react";
9
+ import { flushSync } from "react-dom";
8
10
  //#region src/server/app-browser-navigation-controller.ts
9
11
  const HARD_NAVIGATION_LOOP_GUARD_KEY = "__vinext_hard_navigation_target__";
10
12
  function normalizeBrowserHref(href) {
@@ -50,10 +52,6 @@ function performHardNavigationWithLoopGuard(href, mode = "assign") {
50
52
  else window.location.assign(href);
51
53
  return true;
52
54
  }
53
- function createSnapshotPathAndSearch(snapshot) {
54
- const query = snapshot.searchParams.toString();
55
- return query === "" ? snapshot.pathname : `${snapshot.pathname}?${query}`;
56
- }
57
55
  function createBasePathStrippedPathAndSearch(url, basePath) {
58
56
  const pathname = stripBasePath(url.pathname, basePath);
59
57
  const query = new URLSearchParams(url.search).toString();
@@ -76,6 +74,7 @@ function createAppBrowserNavigationController(deps = {}) {
76
74
  let nextNavigationRenderId = 0;
77
75
  let activeNavigationId = 0;
78
76
  const pendingNavigationCommits = /* @__PURE__ */ new Map();
77
+ const pendingNavigationFailureTargets = /* @__PURE__ */ new Map();
79
78
  const pendingNavigationPrePaintEffects = /* @__PURE__ */ new Map();
80
79
  let setBrowserRouterState = null;
81
80
  let browserRouterStateRef = null;
@@ -194,6 +193,13 @@ function createAppBrowserNavigationController(deps = {}) {
194
193
  resolve();
195
194
  }
196
195
  }
196
+ function clearCommittedNavigationFailureTargets(renderId) {
197
+ for (const [pendingId, targetHref] of pendingNavigationFailureTargets) {
198
+ if (pendingId > renderId) continue;
199
+ pendingNavigationFailureTargets.delete(pendingId);
200
+ clearAppNavigationFailureTarget(targetHref);
201
+ }
202
+ }
197
203
  async function hmrReplaceTree(nextElements, navigationSnapshot) {
198
204
  if (!hasBrowserRouterState()) return;
199
205
  const pending = await createPendingNavigationCommit({
@@ -209,6 +215,9 @@ function createAppBrowserNavigationController(deps = {}) {
209
215
  dispatchSynchronousVisibleCommit(approveHmrVisibleCommit(pending));
210
216
  }
211
217
  function NavigationCommitSignal({ renderId, children }) {
218
+ useInsertionEffect(() => {
219
+ clearCommittedNavigationFailureTargets(renderId);
220
+ }, [renderId]);
212
221
  useLayoutEffect(() => {
213
222
  drainPrePaintEffects(renderId);
214
223
  const frame = requestAnimationFrame(() => {
@@ -221,12 +230,18 @@ function createAppBrowserNavigationController(deps = {}) {
221
230
  }, [renderId]);
222
231
  return children;
223
232
  }
224
- function dispatchApprovedVisibleCommit(commit, pendingRouterState) {
233
+ function dispatchApprovedVisibleCommit(commit, pendingRouterState, visibleCommitMode) {
225
234
  const setter = getBrowserRouterStateSetter();
226
235
  if (pendingRouterState) {
227
236
  resolvePendingBrowserRouterState(pendingRouterState, commit);
228
237
  return;
229
238
  }
239
+ if (visibleCommitMode === "synchronous") {
240
+ flushSync(() => {
241
+ setter(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
242
+ });
243
+ return;
244
+ }
230
245
  startTransition(() => {
231
246
  setter(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
232
247
  });
@@ -295,6 +310,8 @@ function createAppBrowserNavigationController(deps = {}) {
295
310
  }
296
311
  async function renderNavigationPayload(options) {
297
312
  const renderId = allocateRenderId();
313
+ const failureTarget = getAppNavigationFailureTarget(options.targetHref);
314
+ if (failureTarget) pendingNavigationFailureTargets.set(renderId, failureTarget);
298
315
  let resolveCommitted;
299
316
  const committed = new Promise((resolve) => {
300
317
  resolveCommitted = resolve;
@@ -324,6 +341,8 @@ function createAppBrowserNavigationController(deps = {}) {
324
341
  });
325
342
  if (approval.decision.disposition === "no-commit") {
326
343
  settlePendingBrowserRouterState(options.pendingRouterState);
344
+ pendingNavigationFailureTargets.delete(renderId);
345
+ if (failureTarget) clearAppNavigationFailureTarget(failureTarget);
327
346
  pendingNavigationCommits.delete(renderId);
328
347
  resolveCommitted?.();
329
348
  consumeAppRouterScrollIntent(options.scrollIntent ?? null);
@@ -331,9 +350,12 @@ function createAppBrowserNavigationController(deps = {}) {
331
350
  }
332
351
  if (approval.decision.disposition === "hard-navigate") {
333
352
  settlePendingBrowserRouterState(options.pendingRouterState);
353
+ pendingNavigationFailureTargets.delete(renderId);
334
354
  pendingNavigationCommits.delete(renderId);
335
355
  consumeAppRouterScrollIntent(options.scrollIntent ?? null);
336
- return performHardNavigation(options.targetHref) ? "hard-navigate" : "no-commit";
356
+ if (performHardNavigation(options.targetHref)) return "hard-navigate";
357
+ if (failureTarget) clearAppNavigationFailureTarget(failureTarget);
358
+ return "no-commit";
337
359
  }
338
360
  const approvedCommit = approval.approvedCommit;
339
361
  if (approvedCommit === null) throw new Error("[vinext] Commit decision did not approve a visible commit");
@@ -349,8 +371,9 @@ function createAppBrowserNavigationController(deps = {}) {
349
371
  claimAppRouterScrollIntentForCommit(options.scrollIntent, renderId);
350
372
  activateNavigationSnapshot();
351
373
  snapshotActivated = true;
352
- dispatchApprovedVisibleCommit(approvedCommit, options.pendingRouterState);
374
+ dispatchApprovedVisibleCommit(approvedCommit, options.pendingRouterState, options.visibleCommitMode ?? "transition");
353
375
  } catch (error) {
376
+ pendingNavigationFailureTargets.delete(renderId);
354
377
  pendingNavigationPrePaintEffects.delete(renderId);
355
378
  pendingNavigationCommits.delete(renderId);
356
379
  if (snapshotActivated) commitClientNavigationStateImpl(options.navId);
@@ -434,6 +457,7 @@ function createAppBrowserNavigationController(deps = {}) {
434
457
  commitSameUrlNavigatePayload,
435
458
  hmrReplaceTree,
436
459
  drainPrePaintEffects,
460
+ clearCommittedNavigationFailureTargets,
437
461
  NavigationCommitSignal
438
462
  };
439
463
  }
@@ -1,7 +1,7 @@
1
1
  //#region src/server/app-browser-rsc-redirect.d.ts
2
- declare const MAX_RSC_REDIRECT_DEPTH = 10;
3
2
  type RscRedirectHistoryUpdateMode = "push" | "replace" | undefined;
4
3
  type RscRedirectLifecycleDecision = {
4
+ href: string;
5
5
  kind: "no-redirect";
6
6
  } | {
7
7
  href: string;
@@ -24,5 +24,14 @@ declare function resolveRscRedirectLifecycleHop(options: {
24
24
  requestPreviousNextUrl: string | null;
25
25
  responseUrl: string;
26
26
  }): RscRedirectLifecycleDecision;
27
+ declare function resolveStreamedRscRedirectLifecycleHop(options: {
28
+ currentHref: string;
29
+ historyUpdateMode: Exclude<RscRedirectHistoryUpdateMode, undefined>;
30
+ maxRedirectDepth?: number;
31
+ origin: string;
32
+ redirectDepth: number;
33
+ requestPreviousNextUrl: string | null;
34
+ streamedRedirectTarget: string;
35
+ }): RscRedirectLifecycleDecision;
27
36
  //#endregion
28
- export { MAX_RSC_REDIRECT_DEPTH, resolveRscRedirectLifecycleHop };
37
+ export { resolveRscRedirectLifecycleHop, resolveStreamedRscRedirectLifecycleHop };