vinext 0.0.47 → 0.0.49

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 (271) hide show
  1. package/README.md +1 -1
  2. package/dist/build/layout-classification.js +3 -1
  3. package/dist/build/layout-classification.js.map +1 -1
  4. package/dist/build/prerender.js +10 -10
  5. package/dist/build/prerender.js.map +1 -1
  6. package/dist/build/report.d.ts +8 -4
  7. package/dist/build/report.js +17 -7
  8. package/dist/build/report.js.map +1 -1
  9. package/dist/build/run-prerender.d.ts +5 -0
  10. package/dist/build/run-prerender.js +4 -1
  11. package/dist/build/run-prerender.js.map +1 -1
  12. package/dist/build/server-manifest.js +2 -7
  13. package/dist/build/server-manifest.js.map +1 -1
  14. package/dist/build/standalone.js +3 -5
  15. package/dist/build/standalone.js.map +1 -1
  16. package/dist/check.js +45 -29
  17. package/dist/check.js.map +1 -1
  18. package/dist/cli-args.d.ts +3 -1
  19. package/dist/cli-args.js +18 -1
  20. package/dist/cli-args.js.map +1 -1
  21. package/dist/cli.js +9 -1
  22. package/dist/cli.js.map +1 -1
  23. package/dist/config/config-matchers.js +46 -37
  24. package/dist/config/config-matchers.js.map +1 -1
  25. package/dist/deploy.d.ts +18 -2
  26. package/dist/deploy.js +47 -4
  27. package/dist/deploy.js.map +1 -1
  28. package/dist/entries/app-rsc-entry.js +11 -9
  29. package/dist/entries/app-rsc-entry.js.map +1 -1
  30. package/dist/entries/app-rsc-manifest.js +4 -1
  31. package/dist/entries/app-rsc-manifest.js.map +1 -1
  32. package/dist/entries/pages-client-entry.js +3 -2
  33. package/dist/entries/pages-client-entry.js.map +1 -1
  34. package/dist/entries/pages-server-entry.js +21 -62
  35. package/dist/entries/pages-server-entry.js.map +1 -1
  36. package/dist/entries/runtime-entry-module.d.ts +12 -3
  37. package/dist/entries/runtime-entry-module.js +15 -4
  38. package/dist/entries/runtime-entry-module.js.map +1 -1
  39. package/dist/index.js +12 -7
  40. package/dist/index.js.map +1 -1
  41. package/dist/init.d.ts +1 -1
  42. package/dist/init.js +2 -2
  43. package/dist/init.js.map +1 -1
  44. package/dist/plugins/og-assets.js +15 -16
  45. package/dist/plugins/og-assets.js.map +1 -1
  46. package/dist/plugins/rsc-client-shim-excludes.d.ts +2 -1
  47. package/dist/plugins/rsc-client-shim-excludes.js +10 -1
  48. package/dist/plugins/rsc-client-shim-excludes.js.map +1 -1
  49. package/dist/routing/app-route-graph.d.ts +90 -4
  50. package/dist/routing/app-route-graph.js +210 -7
  51. package/dist/routing/app-route-graph.js.map +1 -1
  52. package/dist/routing/app-router.d.ts +15 -3
  53. package/dist/routing/app-router.js +20 -23
  54. package/dist/routing/app-router.js.map +1 -1
  55. package/dist/routing/file-matcher.d.ts +3 -1
  56. package/dist/routing/file-matcher.js +6 -1
  57. package/dist/routing/file-matcher.js.map +1 -1
  58. package/dist/routing/pages-router.js +10 -19
  59. package/dist/routing/pages-router.js.map +1 -1
  60. package/dist/routing/route-matching.d.ts +28 -0
  61. package/dist/routing/route-matching.js +44 -0
  62. package/dist/routing/route-matching.js.map +1 -0
  63. package/dist/routing/route-pattern.js +4 -1
  64. package/dist/routing/route-pattern.js.map +1 -1
  65. package/dist/routing/route-trie.d.ts +8 -0
  66. package/dist/routing/route-trie.js +12 -1
  67. package/dist/routing/route-trie.js.map +1 -1
  68. package/dist/routing/route-validation.js +3 -4
  69. package/dist/routing/route-validation.js.map +1 -1
  70. package/dist/routing/utils.d.ts +8 -1
  71. package/dist/routing/utils.js +25 -2
  72. package/dist/routing/utils.js.map +1 -1
  73. package/dist/server/api-handler.js +2 -8
  74. package/dist/server/api-handler.js.map +1 -1
  75. package/dist/server/app-browser-entry.js +66 -49
  76. package/dist/server/app-browser-entry.js.map +1 -1
  77. package/dist/server/app-browser-navigation-controller.d.ts +7 -5
  78. package/dist/server/app-browser-navigation-controller.js +43 -35
  79. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  80. package/dist/server/app-browser-state.d.ts +33 -15
  81. package/dist/server/app-browser-state.js +52 -59
  82. package/dist/server/app-browser-state.js.map +1 -1
  83. package/dist/server/app-browser-visible-commit.d.ts +68 -0
  84. package/dist/server/app-browser-visible-commit.js +182 -0
  85. package/dist/server/app-browser-visible-commit.js.map +1 -0
  86. package/dist/server/app-client-reference-preloader.d.ts +15 -0
  87. package/dist/server/app-client-reference-preloader.js +46 -0
  88. package/dist/server/app-client-reference-preloader.js.map +1 -0
  89. package/dist/server/app-elements-wire.d.ts +130 -0
  90. package/dist/server/app-elements-wire.js +205 -0
  91. package/dist/server/app-elements-wire.js.map +1 -0
  92. package/dist/server/app-elements.d.ts +2 -84
  93. package/dist/server/app-elements.js +3 -102
  94. package/dist/server/app-elements.js.map +1 -1
  95. package/dist/server/app-fallback-renderer.d.ts +1 -1
  96. package/dist/server/app-middleware.d.ts +2 -1
  97. package/dist/server/app-middleware.js +34 -11
  98. package/dist/server/app-middleware.js.map +1 -1
  99. package/dist/server/app-page-boundary-render.d.ts +1 -1
  100. package/dist/server/app-page-boundary-render.js +8 -5
  101. package/dist/server/app-page-boundary-render.js.map +1 -1
  102. package/dist/server/app-page-boundary.js +2 -1
  103. package/dist/server/app-page-boundary.js.map +1 -1
  104. package/dist/server/app-page-cache.d.ts +1 -0
  105. package/dist/server/app-page-cache.js +8 -13
  106. package/dist/server/app-page-cache.js.map +1 -1
  107. package/dist/server/app-page-dispatch.d.ts +2 -1
  108. package/dist/server/app-page-dispatch.js +18 -10
  109. package/dist/server/app-page-dispatch.js.map +1 -1
  110. package/dist/server/app-page-element-builder.d.ts +1 -1
  111. package/dist/server/app-page-element-builder.js +8 -5
  112. package/dist/server/app-page-element-builder.js.map +1 -1
  113. package/dist/server/app-page-execution.d.ts +23 -5
  114. package/dist/server/app-page-execution.js +39 -24
  115. package/dist/server/app-page-execution.js.map +1 -1
  116. package/dist/server/app-page-head.js +2 -1
  117. package/dist/server/app-page-head.js.map +1 -1
  118. package/dist/server/app-page-method.js +2 -5
  119. package/dist/server/app-page-method.js.map +1 -1
  120. package/dist/server/app-page-probe.d.ts +1 -1
  121. package/dist/server/app-page-probe.js +5 -1
  122. package/dist/server/app-page-probe.js.map +1 -1
  123. package/dist/server/app-page-render.d.ts +1 -1
  124. package/dist/server/app-page-render.js +38 -3
  125. package/dist/server/app-page-render.js.map +1 -1
  126. package/dist/server/app-page-request.d.ts +0 -1
  127. package/dist/server/app-page-request.js +7 -10
  128. package/dist/server/app-page-request.js.map +1 -1
  129. package/dist/server/app-page-response.js +3 -2
  130. package/dist/server/app-page-response.js.map +1 -1
  131. package/dist/server/app-page-route-wiring.d.ts +5 -2
  132. package/dist/server/app-page-route-wiring.js +15 -12
  133. package/dist/server/app-page-route-wiring.js.map +1 -1
  134. package/dist/server/app-page-stream.d.ts +7 -0
  135. package/dist/server/app-page-stream.js +9 -2
  136. package/dist/server/app-page-stream.js.map +1 -1
  137. package/dist/server/app-prerender-endpoints.js +3 -2
  138. package/dist/server/app-prerender-endpoints.js.map +1 -1
  139. package/dist/server/app-route-handler-cache.js +2 -1
  140. package/dist/server/app-route-handler-cache.js.map +1 -1
  141. package/dist/server/app-route-handler-dispatch.js +6 -5
  142. package/dist/server/app-route-handler-dispatch.js.map +1 -1
  143. package/dist/server/app-route-handler-policy.js +13 -13
  144. package/dist/server/app-route-handler-policy.js.map +1 -1
  145. package/dist/server/app-route-handler-response.js +2 -1
  146. package/dist/server/app-route-handler-response.js.map +1 -1
  147. package/dist/server/app-route-handler-runtime.d.ts +9 -1
  148. package/dist/server/app-route-handler-runtime.js +11 -1
  149. package/dist/server/app-route-handler-runtime.js.map +1 -1
  150. package/dist/server/app-router-entry.js +9 -4
  151. package/dist/server/app-router-entry.js.map +1 -1
  152. package/dist/server/app-rsc-cache-busting.d.ts +34 -0
  153. package/dist/server/app-rsc-cache-busting.js +137 -0
  154. package/dist/server/app-rsc-cache-busting.js.map +1 -0
  155. package/dist/server/app-rsc-handler.js +22 -11
  156. package/dist/server/app-rsc-handler.js.map +1 -1
  157. package/dist/server/app-rsc-request-normalization.d.ts +4 -2
  158. package/dist/server/app-rsc-request-normalization.js +10 -6
  159. package/dist/server/app-rsc-request-normalization.js.map +1 -1
  160. package/dist/server/app-rsc-response-finalizer.js +1 -1
  161. package/dist/server/app-rsc-route-matching.js +8 -4
  162. package/dist/server/app-rsc-route-matching.js.map +1 -1
  163. package/dist/server/app-segment-config.js +4 -0
  164. package/dist/server/app-segment-config.js.map +1 -1
  165. package/dist/server/app-server-action-execution.js +43 -51
  166. package/dist/server/app-server-action-execution.js.map +1 -1
  167. package/dist/server/app-ssr-entry.js +21 -20
  168. package/dist/server/app-ssr-entry.js.map +1 -1
  169. package/dist/server/artifact-compatibility.d.ts +44 -0
  170. package/dist/server/artifact-compatibility.js +82 -0
  171. package/dist/server/artifact-compatibility.js.map +1 -0
  172. package/dist/server/cache-proof.d.ts +200 -0
  173. package/dist/server/cache-proof.js +342 -0
  174. package/dist/server/cache-proof.js.map +1 -0
  175. package/dist/server/dev-origin-check.js +8 -4
  176. package/dist/server/dev-origin-check.js.map +1 -1
  177. package/dist/server/dev-server.js +6 -16
  178. package/dist/server/dev-server.js.map +1 -1
  179. package/dist/server/http-error-responses.d.ts +67 -0
  180. package/dist/server/http-error-responses.js +77 -0
  181. package/dist/server/http-error-responses.js.map +1 -0
  182. package/dist/server/image-optimization.js +2 -1
  183. package/dist/server/image-optimization.js.map +1 -1
  184. package/dist/server/metadata-route-response.js +6 -5
  185. package/dist/server/metadata-route-response.js.map +1 -1
  186. package/dist/server/metadata-routes.d.ts +1 -0
  187. package/dist/server/metadata-routes.js +6 -0
  188. package/dist/server/metadata-routes.js.map +1 -1
  189. package/dist/server/middleware-matcher.js +2 -2
  190. package/dist/server/middleware-matcher.js.map +1 -1
  191. package/dist/server/middleware-response-headers.js +21 -0
  192. package/dist/server/middleware-response-headers.js.map +1 -1
  193. package/dist/server/middleware-runtime.js +3 -3
  194. package/dist/server/middleware-runtime.js.map +1 -1
  195. package/dist/server/navigation-trace.d.ts +33 -0
  196. package/dist/server/navigation-trace.js +35 -0
  197. package/dist/server/navigation-trace.js.map +1 -0
  198. package/dist/server/next-error-digest.d.ts +44 -0
  199. package/dist/server/next-error-digest.js +40 -0
  200. package/dist/server/next-error-digest.js.map +1 -0
  201. package/dist/server/pages-api-route.js +4 -7
  202. package/dist/server/pages-api-route.js.map +1 -1
  203. package/dist/server/pages-node-compat.js +4 -16
  204. package/dist/server/pages-node-compat.js.map +1 -1
  205. package/dist/server/pages-page-response.d.ts +2 -8
  206. package/dist/server/pages-page-response.js +44 -14
  207. package/dist/server/pages-page-response.js.map +1 -1
  208. package/dist/server/prod-server.d.ts +6 -0
  209. package/dist/server/prod-server.js +28 -21
  210. package/dist/server/prod-server.js.map +1 -1
  211. package/dist/server/request-pipeline.d.ts +42 -1
  212. package/dist/server/request-pipeline.js +97 -17
  213. package/dist/server/request-pipeline.js.map +1 -1
  214. package/dist/shims/cache-runtime.d.ts +2 -2
  215. package/dist/shims/cache-runtime.js +3 -6
  216. package/dist/shims/cache-runtime.js.map +1 -1
  217. package/dist/shims/cache.js +3 -5
  218. package/dist/shims/cache.js.map +1 -1
  219. package/dist/shims/fetch-cache.js +2 -3
  220. package/dist/shims/fetch-cache.js.map +1 -1
  221. package/dist/shims/head-state.js +2 -3
  222. package/dist/shims/head-state.js.map +1 -1
  223. package/dist/shims/headers.js +4 -44
  224. package/dist/shims/headers.js.map +1 -1
  225. package/dist/shims/i18n-state.js +2 -3
  226. package/dist/shims/i18n-state.js.map +1 -1
  227. package/dist/shims/internal/als-registry.d.ts +15 -0
  228. package/dist/shims/internal/als-registry.js +55 -0
  229. package/dist/shims/internal/als-registry.js.map +1 -0
  230. package/dist/shims/internal/cookie-serialize.d.ts +46 -0
  231. package/dist/shims/internal/cookie-serialize.js +51 -0
  232. package/dist/shims/internal/cookie-serialize.js.map +1 -0
  233. package/dist/shims/link.js +31 -26
  234. package/dist/shims/link.js.map +1 -1
  235. package/dist/shims/metadata.d.ts +26 -1
  236. package/dist/shims/metadata.js +94 -4
  237. package/dist/shims/metadata.js.map +1 -1
  238. package/dist/shims/navigation-state.js +2 -3
  239. package/dist/shims/navigation-state.js.map +1 -1
  240. package/dist/shims/navigation.d.ts +2 -7
  241. package/dist/shims/navigation.js +44 -36
  242. package/dist/shims/navigation.js.map +1 -1
  243. package/dist/shims/request-context.js +2 -4
  244. package/dist/shims/request-context.js.map +1 -1
  245. package/dist/shims/router-state.js +2 -3
  246. package/dist/shims/router-state.js.map +1 -1
  247. package/dist/shims/router.js +2 -2
  248. package/dist/shims/router.js.map +1 -1
  249. package/dist/shims/server.js +5 -30
  250. package/dist/shims/server.js.map +1 -1
  251. package/dist/shims/slot.d.ts +1 -1
  252. package/dist/shims/slot.js +5 -4
  253. package/dist/shims/slot.js.map +1 -1
  254. package/dist/shims/thenable-params.d.ts +5 -2
  255. package/dist/shims/thenable-params.js +26 -6
  256. package/dist/shims/thenable-params.js.map +1 -1
  257. package/dist/shims/unified-request-context.js +2 -14
  258. package/dist/shims/unified-request-context.js.map +1 -1
  259. package/dist/utils/base-path.d.ts +7 -1
  260. package/dist/utils/base-path.js +12 -1
  261. package/dist/utils/base-path.js.map +1 -1
  262. package/dist/utils/query.d.ts +8 -1
  263. package/dist/utils/query.js +12 -1
  264. package/dist/utils/query.js.map +1 -1
  265. package/dist/utils/safe-json-file.d.ts +18 -0
  266. package/dist/utils/safe-json-file.js +25 -0
  267. package/dist/utils/safe-json-file.js.map +1 -0
  268. package/dist/utils/text-stream.d.ts +29 -0
  269. package/dist/utils/text-stream.js +66 -0
  270. package/dist/utils/text-stream.js.map +1 -0
  271. package/package.json +5 -5
@@ -1,6 +1,6 @@
1
1
  import { ClientNavigationRenderSnapshot, commitClientNavigationState } from "../shims/navigation.js";
2
- import { AppElements } from "./app-elements.js";
3
- import { AppRouterState } from "./app-browser-state.js";
2
+ import { AppElements } from "./app-elements-wire.js";
3
+ import { AppRouterState, OperationLane } from "./app-browser-state.js";
4
4
  import { Dispatch, ReactNode } from "react";
5
5
 
6
6
  //#region src/server/app-browser-navigation-controller.d.ts
@@ -10,6 +10,7 @@ type PendingBrowserRouterState = {
10
10
  resolve: (state: AppRouterState) => void;
11
11
  settled: boolean;
12
12
  };
13
+ type NavigationPayloadOutcome = "committed" | "no-commit" | "hard-navigate";
13
14
  type BrowserNavigationCommitEffectFactory = (options: {
14
15
  href: string;
15
16
  historyUpdateMode: HistoryUpdateMode | undefined;
@@ -38,17 +39,18 @@ type BrowserNavigationController = {
38
39
  historyUpdateMode: HistoryUpdateMode | undefined;
39
40
  navigationSnapshot: ClientNavigationRenderSnapshot;
40
41
  nextElements: Promise<AppElements>;
42
+ operationLane: OperationLane;
41
43
  params: Record<string, string | string[]>;
42
44
  pendingRouterState: PendingBrowserRouterState | null;
43
45
  previousNextUrl: string | null;
44
46
  targetHref: string;
45
47
  navId: number;
46
48
  useTransition?: boolean;
47
- }): Promise<void>;
49
+ }): Promise<NavigationPayloadOutcome>;
48
50
  commitSameUrlNavigatePayload(nextElements: Promise<AppElements>, navigationSnapshot: ClientNavigationRenderSnapshot, returnValue?: {
49
51
  ok: boolean;
50
52
  data: unknown;
51
- }): Promise<unknown>;
53
+ }, actionInitiationState?: AppRouterState): Promise<unknown>;
52
54
  hmrReplaceTree(nextElements: Promise<AppElements>, navigationSnapshot: ClientNavigationRenderSnapshot): Promise<void>;
53
55
  /**
54
56
  * Force-drain the queued pre-paint effect for the given renderId without
@@ -69,5 +71,5 @@ type BrowserNavigationController = {
69
71
  };
70
72
  declare function createAppBrowserNavigationController(deps?: BrowserNavigationControllerDeps): BrowserNavigationController;
71
73
  //#endregion
72
- export { HistoryUpdateMode, PendingBrowserRouterState, createAppBrowserNavigationController };
74
+ export { HistoryUpdateMode, NavigationPayloadOutcome, PendingBrowserRouterState, createAppBrowserNavigationController };
73
75
  //# sourceMappingURL=app-browser-navigation-controller.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import { activateNavigationSnapshot, clearPendingPathname, commitClientNavigationState } from "../shims/navigation.js";
2
- import { createPendingNavigationCommit, resolveAndClassifyNavigationCommit, resolvePendingNavigationCommitDisposition, routerReducer } from "./app-browser-state.js";
2
+ import { createPendingNavigationCommit } from "./app-browser-state.js";
3
+ import { applyApprovedVisibleCommit, approveHmrVisibleCommit, approvePendingNavigationCommit, resolveAndClassifyNavigationCommit } from "./app-browser-visible-commit.js";
3
4
  import { startTransition, useLayoutEffect } from "react";
4
5
  //#region src/server/app-browser-navigation-controller.ts
5
6
  function createAppBrowserNavigationController(deps = {}) {
@@ -80,10 +81,10 @@ function createAppBrowserNavigationController(deps = {}) {
80
81
  settlePendingBrowserRouterState(pending);
81
82
  if (isCurrentNavigation(navId)) clearPendingPathname(navId);
82
83
  }
83
- function resolvePendingBrowserRouterState(pending, action) {
84
+ function resolvePendingBrowserRouterState(pending, commit) {
84
85
  if (!pending || pending.settled) return;
85
86
  pending.settled = true;
86
- pending.resolve(routerReducer(getBrowserRouterState(), action));
87
+ pending.resolve(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
87
88
  if (activePendingBrowserRouterState === pending) activePendingBrowserRouterState = null;
88
89
  }
89
90
  function queuePrePaintNavigationEffect(renderId, effect) {
@@ -128,11 +129,12 @@ function createAppBrowserNavigationController(deps = {}) {
128
129
  currentState: getBrowserRouterState(),
129
130
  nextElements,
130
131
  navigationSnapshot,
132
+ operationLane: "hmr",
131
133
  renderId: allocateRenderId(),
132
134
  type: "replace"
133
135
  });
134
136
  if (!hasBrowserRouterState()) return;
135
- dispatchBrowserTree(pending.action.elements, navigationSnapshot, pending.action.renderId, "replace", pending.interceptionContext, pending.action.layoutFlags, pending.previousNextUrl, pending.routeId, pending.rootLayoutTreePath, null, false);
137
+ dispatchApprovedVisibleCommit(approveHmrVisibleCommit(pending), null, false);
136
138
  }
137
139
  function NavigationCommitSignal({ renderId, children }) {
138
140
  useLayoutEffect(() => {
@@ -147,25 +149,14 @@ function createAppBrowserNavigationController(deps = {}) {
147
149
  }, [renderId]);
148
150
  return children;
149
151
  }
150
- function dispatchBrowserTree(elements, navigationSnapshot, renderId, actionType, interceptionContext, layoutFlags, previousNextUrl, routeId, rootLayoutTreePath, pendingRouterState, useTransitionMode) {
152
+ function dispatchApprovedVisibleCommit(commit, pendingRouterState, useTransitionMode) {
151
153
  const setter = getBrowserRouterStateSetter();
152
- const action = {
153
- elements,
154
- interceptionContext,
155
- layoutFlags,
156
- navigationSnapshot,
157
- previousNextUrl,
158
- renderId,
159
- rootLayoutTreePath,
160
- routeId,
161
- type: actionType
162
- };
163
154
  const applyAction = () => {
164
155
  if (pendingRouterState) {
165
- resolvePendingBrowserRouterState(pendingRouterState, action);
156
+ resolvePendingBrowserRouterState(pendingRouterState, commit);
166
157
  return;
167
158
  }
168
- setter(routerReducer(getBrowserRouterState(), action));
159
+ setter(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
169
160
  };
170
161
  if (useTransitionMode) startTransition(applyAction);
171
162
  else applyAction();
@@ -179,43 +170,45 @@ function createAppBrowserNavigationController(deps = {}) {
179
170
  });
180
171
  let snapshotActivated = false;
181
172
  try {
182
- const currentState = getBrowserRouterState();
183
173
  const pending = await createPendingNavigationCommit({
184
- currentState,
174
+ currentState: getBrowserRouterState(),
185
175
  nextElements: options.nextElements,
186
176
  navigationSnapshot: options.navigationSnapshot,
177
+ operationLane: options.operationLane,
187
178
  previousNextUrl: options.previousNextUrl,
188
179
  renderId,
189
180
  type: options.actionType
190
181
  });
191
- const disposition = resolvePendingNavigationCommitDisposition({
182
+ const approval = approvePendingNavigationCommit({
192
183
  activeNavigationId,
193
- currentRootLayoutTreePath: currentState.rootLayoutTreePath,
194
- nextRootLayoutTreePath: pending.rootLayoutTreePath,
184
+ currentState: getBrowserRouterState(),
185
+ pending,
195
186
  startedNavigationId: options.navId
196
187
  });
197
- if (disposition === "skip") {
188
+ if (approval.decision.disposition === "no-commit") {
198
189
  settlePendingBrowserRouterState(options.pendingRouterState);
199
190
  pendingNavigationCommits.delete(renderId);
200
191
  resolveCommitted?.();
201
- return;
192
+ return "no-commit";
202
193
  }
203
- if (disposition === "hard-navigate") {
194
+ if (approval.decision.disposition === "hard-navigate") {
204
195
  settlePendingBrowserRouterState(options.pendingRouterState);
205
196
  pendingNavigationCommits.delete(renderId);
206
197
  window.location.assign(options.targetHref);
207
- return;
198
+ return "hard-navigate";
208
199
  }
200
+ const approvedCommit = approval.approvedCommit;
201
+ if (approvedCommit === null) throw new Error("[vinext] Commit decision did not approve a visible commit");
209
202
  queuePrePaintNavigationEffect(renderId, options.createNavigationCommitEffect({
210
203
  href: options.targetHref,
211
204
  historyUpdateMode: options.historyUpdateMode,
212
205
  navId: options.navId,
213
206
  params: options.params,
214
- previousNextUrl: pending.previousNextUrl
207
+ previousNextUrl: approvedCommit.previousNextUrl
215
208
  }));
216
209
  activateNavigationSnapshot();
217
210
  snapshotActivated = true;
218
- dispatchBrowserTree(pending.action.elements, options.navigationSnapshot, renderId, options.actionType, pending.interceptionContext, pending.action.layoutFlags, pending.previousNextUrl, pending.routeId, pending.rootLayoutTreePath, options.pendingRouterState, options.useTransition ?? true);
211
+ dispatchApprovedVisibleCommit(approvedCommit, options.pendingRouterState, options.useTransition ?? true);
219
212
  } catch (error) {
220
213
  pendingNavigationPrePaintEffects.delete(renderId);
221
214
  pendingNavigationCommits.delete(renderId);
@@ -224,25 +217,40 @@ function createAppBrowserNavigationController(deps = {}) {
224
217
  resolveCommitted?.();
225
218
  throw error;
226
219
  }
227
- return committed;
220
+ return committed.then(() => "committed");
228
221
  }
229
- async function commitSameUrlNavigatePayload(nextElements, navigationSnapshot, returnValue) {
230
- const currentState = getBrowserRouterState();
222
+ async function commitSameUrlNavigatePayload(nextElements, navigationSnapshot, returnValue, actionInitiationState) {
223
+ const currentState = actionInitiationState ?? getBrowserRouterState();
231
224
  const startedNavigationId = activeNavigationId;
232
- const { disposition, pending } = await resolveAndClassifyNavigationCommit({
225
+ const { approvedCommit, decision, pending, trace: _navigationTrace } = await resolveAndClassifyNavigationCommit({
233
226
  activeNavigationId,
234
227
  currentState,
228
+ getActiveNavigationId: () => activeNavigationId,
229
+ getCurrentStateForApproval: getBrowserRouterState,
235
230
  navigationSnapshot,
236
231
  nextElements,
237
232
  renderId: allocateRenderId(),
233
+ operationLane: "server-action",
238
234
  startedNavigationId,
239
235
  type: "navigate"
240
236
  });
241
- if (disposition === "hard-navigate") {
237
+ if (decision.disposition === "hard-navigate") {
242
238
  window.location.assign(window.location.href);
243
239
  return;
244
240
  }
245
- if (disposition === "dispatch") dispatchBrowserTree(pending.action.elements, navigationSnapshot, pending.action.renderId, "navigate", pending.interceptionContext, pending.action.layoutFlags, pending.previousNextUrl, pending.routeId, pending.rootLayoutTreePath, null, false);
241
+ if (approvedCommit) {
242
+ const latestApproval = approvePendingNavigationCommit({
243
+ activeNavigationId,
244
+ currentState: getBrowserRouterState(),
245
+ pending,
246
+ startedNavigationId
247
+ });
248
+ if (latestApproval.decision.disposition === "hard-navigate") {
249
+ window.location.assign(window.location.href);
250
+ return;
251
+ }
252
+ if (latestApproval.approvedCommit) dispatchApprovedVisibleCommit(latestApproval.approvedCommit, null, false);
253
+ }
246
254
  if (returnValue) {
247
255
  if (!returnValue.ok) throw returnValue.data;
248
256
  return returnValue.data;
@@ -1 +1 @@
1
- {"version":3,"file":"app-browser-navigation-controller.js","names":[],"sources":["../../src/server/app-browser-navigation-controller.ts"],"sourcesContent":["import { startTransition, useLayoutEffect, type Dispatch, type ReactNode } from \"react\";\nimport {\n activateNavigationSnapshot,\n clearPendingPathname,\n commitClientNavigationState,\n} from \"vinext/shims/navigation\";\nimport type { ClientNavigationRenderSnapshot } from \"vinext/shims/navigation\";\nimport {\n createPendingNavigationCommit,\n resolveAndClassifyNavigationCommit,\n resolvePendingNavigationCommitDisposition,\n routerReducer,\n type AppRouterAction,\n type AppRouterState,\n} from \"./app-browser-state.js\";\nimport type { AppElements, LayoutFlags } from \"./app-elements.js\";\n\nexport type HistoryUpdateMode = \"push\" | \"replace\";\n\nexport type PendingBrowserRouterState = {\n promise: Promise<AppRouterState>;\n resolve: (state: AppRouterState) => void;\n settled: boolean;\n};\n\ntype BrowserNavigationCommitEffectFactory = (options: {\n href: string;\n historyUpdateMode: HistoryUpdateMode | undefined;\n navId: number;\n params: Record<string, string | string[]>;\n previousNextUrl: string | null;\n}) => () => void;\n\ntype BrowserRouterStateRef = {\n current: AppRouterState;\n};\n\ntype BrowserNavigationControllerDeps = {\n commitClientNavigationState?: typeof commitClientNavigationState;\n};\n\ntype BrowserNavigationController = {\n beginNavigation(): number;\n hasBrowserRouterState(): boolean;\n getBrowserRouterState(): AppRouterState;\n isCurrentNavigation(navId: number): boolean;\n waitForBrowserRouterStateReady(): Promise<void>;\n attachBrowserRouterState(\n setter: Dispatch<AppRouterState | Promise<AppRouterState>>,\n stateRef: BrowserRouterStateRef,\n ): () => void;\n beginPendingBrowserRouterState(): PendingBrowserRouterState;\n finalizeNavigation(navId: number, pending: PendingBrowserRouterState | null | undefined): void;\n renderNavigationPayload(options: {\n actionType: \"navigate\" | \"replace\" | \"traverse\";\n createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;\n historyUpdateMode: HistoryUpdateMode | undefined;\n navigationSnapshot: ClientNavigationRenderSnapshot;\n nextElements: Promise<AppElements>;\n params: Record<string, string | string[]>;\n pendingRouterState: PendingBrowserRouterState | null;\n previousNextUrl: string | null;\n targetHref: string;\n navId: number;\n useTransition?: boolean;\n }): Promise<void>;\n commitSameUrlNavigatePayload(\n nextElements: Promise<AppElements>,\n navigationSnapshot: ClientNavigationRenderSnapshot,\n returnValue?: { ok: boolean; data: unknown },\n ): Promise<unknown>;\n hmrReplaceTree(\n nextElements: Promise<AppElements>,\n navigationSnapshot: ClientNavigationRenderSnapshot,\n ): Promise<void>;\n /**\n * Force-drain the queued pre-paint effect for the given renderId without\n * waiting for NavigationCommitSignal to commit. Used by the dev recovery\n * boundary in app-browser-entry.ts: when a render error replaces\n * NavigationCommitSignal with the boundary's null fallback, its\n * useLayoutEffect never fires, so the URL update for the in-flight\n * navigation would otherwise be lost.\n */\n drainPrePaintEffects(renderId: number): void;\n NavigationCommitSignal(\n this: void,\n {\n renderId,\n children,\n }: {\n renderId: number;\n children?: ReactNode;\n },\n ): ReactNode;\n};\n\nexport function createAppBrowserNavigationController(\n deps: BrowserNavigationControllerDeps = {},\n): BrowserNavigationController {\n const commitClientNavigationStateImpl =\n deps.commitClientNavigationState ?? commitClientNavigationState;\n\n // These are plain module-level variables (inside the controller closure),\n // unlike ClientNavigationState which uses Symbol.for to survive multiple\n // Vite module instances. The browser entry is loaded exactly once (via the\n // RSC plugin's generated bootstrap), so the controller running in a single\n // module instance is safe. If that assumption ever changes, these should be\n // migrated to a Symbol.for-backed global.\n //\n // The most severe consequence of multiple instances would be Map fragmentation:\n // pendingNavigationCommits and pendingNavigationPrePaintEffects would split\n // across instances, so drainPrePaintEffects in one instance could never drain\n // effects queued by the other, permanently leaking navigationSnapshotActiveCount\n // and causing hooks to prefer stale snapshot values indefinitely.\n let nextNavigationRenderId = 0;\n let activeNavigationId = 0;\n const pendingNavigationCommits = new Map<number, () => void>();\n const pendingNavigationPrePaintEffects = new Map<number, () => void>();\n\n let setBrowserRouterState: Dispatch<AppRouterState | Promise<AppRouterState>> | null = null;\n let browserRouterStateRef: BrowserRouterStateRef | null = null;\n let activePendingBrowserRouterState: PendingBrowserRouterState | null = null;\n let resolveBrowserRouterStateReady: (() => void) | null = null;\n let browserRouterStateReadyPromise: Promise<void> | null = null;\n let browserRouterStateHasCommitted = false;\n\n function getBrowserRouterStateSetter(): Dispatch<AppRouterState | Promise<AppRouterState>> {\n if (!setBrowserRouterState) {\n throw new Error(\"[vinext] Browser router state setter is not initialized\");\n }\n return setBrowserRouterState;\n }\n\n function getBrowserRouterState(): AppRouterState {\n if (!browserRouterStateRef) {\n throw new Error(\"[vinext] Browser router state is not initialized\");\n }\n return browserRouterStateRef.current;\n }\n\n function waitForBrowserRouterStateReady(): Promise<void> {\n if (browserRouterStateRef || browserRouterStateHasCommitted) {\n return Promise.resolve();\n }\n\n if (!browserRouterStateReadyPromise) {\n browserRouterStateReadyPromise = new Promise((resolve) => {\n resolveBrowserRouterStateReady = resolve;\n });\n }\n\n return browserRouterStateReadyPromise;\n }\n\n function markBrowserRouterStateReady(): void {\n browserRouterStateHasCommitted = true;\n const resolveReady = resolveBrowserRouterStateReady;\n resolveBrowserRouterStateReady = null;\n browserRouterStateReadyPromise = null;\n resolveReady?.();\n }\n\n function beginNavigation(): number {\n activeNavigationId += 1;\n return activeNavigationId;\n }\n\n function allocateRenderId(): number {\n nextNavigationRenderId += 1;\n return nextNavigationRenderId;\n }\n\n function hasBrowserRouterState(): boolean {\n return browserRouterStateRef !== null;\n }\n\n function isCurrentNavigation(navId: number): boolean {\n return navId === activeNavigationId;\n }\n\n function beginPendingBrowserRouterState(): PendingBrowserRouterState {\n const setter = getBrowserRouterStateSetter();\n\n if (activePendingBrowserRouterState && !activePendingBrowserRouterState.settled) {\n activePendingBrowserRouterState.settled = true;\n activePendingBrowserRouterState.resolve(getBrowserRouterState());\n }\n\n let resolvePending: ((state: AppRouterState) => void) | undefined;\n const promise = new Promise<AppRouterState>((resolve) => {\n resolvePending = resolve;\n });\n\n if (!resolvePending) {\n throw new Error(\"[vinext] Failed to initialize browser router promise\");\n }\n\n const pending: PendingBrowserRouterState = {\n promise,\n resolve: resolvePending,\n settled: false,\n };\n\n activePendingBrowserRouterState = pending;\n setter(promise);\n\n return pending;\n }\n\n function settlePendingBrowserRouterState(\n pending: PendingBrowserRouterState | null | undefined,\n ): void {\n if (!pending || pending.settled) return;\n\n pending.settled = true;\n pending.resolve(getBrowserRouterState());\n\n if (activePendingBrowserRouterState === pending) {\n activePendingBrowserRouterState = null;\n }\n }\n\n function finalizeNavigation(\n navId: number,\n pending: PendingBrowserRouterState | null | undefined,\n ): void {\n settlePendingBrowserRouterState(pending);\n\n if (isCurrentNavigation(navId)) {\n clearPendingPathname(navId);\n }\n }\n\n function resolvePendingBrowserRouterState(\n pending: PendingBrowserRouterState | null | undefined,\n action: AppRouterAction,\n ): void {\n if (!pending || pending.settled) return;\n\n pending.settled = true;\n pending.resolve(routerReducer(getBrowserRouterState(), action));\n\n if (activePendingBrowserRouterState === pending) {\n activePendingBrowserRouterState = null;\n }\n }\n\n function queuePrePaintNavigationEffect(renderId: number, effect: (() => void) | null): void {\n if (!effect) {\n return;\n }\n pendingNavigationPrePaintEffects.set(renderId, effect);\n }\n\n /**\n * Run all queued pre-paint effects for renderIds up to and including the\n * given renderId. When React supersedes a startTransition update (rapid\n * clicks on same-route links), the superseded NavigationCommitSignal never\n * mounts, so its pre-paint effect never fires. By draining all effects\n * <= the committed renderId here, the winning transition cleans up after\n * any superseded ones, keeping the counter balanced.\n *\n * Invariant: each superseded navigation gets a commitClientNavigationState()\n * to balance the activateNavigationSnapshot() from its renderNavigationPayload call.\n */\n function drainPrePaintEffects(upToRenderId: number): void {\n for (const [id, effect] of pendingNavigationPrePaintEffects) {\n if (id > upToRenderId) {\n continue;\n }\n\n pendingNavigationPrePaintEffects.delete(id);\n if (id === upToRenderId) {\n effect();\n } else {\n // Superseded navigations still need to balance the snapshot counter.\n commitClientNavigationStateImpl(undefined, { releaseSnapshot: true });\n }\n }\n }\n\n /**\n * Resolve all pending navigation commits with renderId <= the committed renderId.\n * Note: Map iteration handles concurrent deletion safely — entries are visited in\n * insertion order and deletion doesn't affect the iterator's view of remaining entries.\n * This pattern is also used in drainPrePaintEffects with the same semantics.\n */\n function resolveCommittedNavigations(renderId: number): void {\n for (const [pendingId, resolve] of pendingNavigationCommits) {\n if (pendingId > renderId) {\n continue;\n }\n\n pendingNavigationCommits.delete(pendingId);\n resolve();\n }\n }\n\n async function hmrReplaceTree(\n nextElements: Promise<AppElements>,\n navigationSnapshot: ClientNavigationRenderSnapshot,\n ): Promise<void> {\n if (!hasBrowserRouterState()) return;\n\n const currentState = getBrowserRouterState();\n const renderId = allocateRenderId();\n const pending = await createPendingNavigationCommit({\n currentState,\n nextElements,\n navigationSnapshot,\n renderId,\n type: \"replace\",\n });\n\n // createPendingNavigationCommit awaits the new RSC payload. While\n // suspended, the prior broken render can unmount BrowserRoot. Re-check\n // before dispatching so a racing unmount doesn't surface as an\n // initialized-setter error.\n if (!hasBrowserRouterState()) return;\n\n dispatchBrowserTree(\n pending.action.elements,\n navigationSnapshot,\n pending.action.renderId,\n \"replace\",\n pending.interceptionContext,\n pending.action.layoutFlags,\n pending.previousNextUrl,\n pending.routeId,\n pending.rootLayoutTreePath,\n null,\n false,\n );\n }\n\n function NavigationCommitSignal(\n this: void,\n {\n renderId,\n children,\n }: {\n renderId: number;\n children?: ReactNode;\n },\n ): ReactNode {\n useLayoutEffect(() => {\n drainPrePaintEffects(renderId);\n\n const frame = requestAnimationFrame(() => {\n resolveCommittedNavigations(renderId);\n });\n\n return () => {\n cancelAnimationFrame(frame);\n // Resolve pending commits to prevent callers from hanging if React\n // unmounts this component without committing (e.g., error boundary).\n resolveCommittedNavigations(renderId);\n };\n }, [renderId]);\n\n return children;\n }\n\n function dispatchBrowserTree(\n elements: AppElements,\n navigationSnapshot: ClientNavigationRenderSnapshot,\n renderId: number,\n actionType: \"navigate\" | \"replace\" | \"traverse\",\n interceptionContext: string | null,\n layoutFlags: LayoutFlags,\n previousNextUrl: string | null,\n routeId: string,\n rootLayoutTreePath: string | null,\n pendingRouterState: PendingBrowserRouterState | null,\n useTransitionMode: boolean,\n ): void {\n const setter = getBrowserRouterStateSetter();\n const action: AppRouterAction = {\n elements,\n interceptionContext,\n layoutFlags,\n navigationSnapshot,\n previousNextUrl,\n renderId,\n rootLayoutTreePath,\n routeId,\n type: actionType,\n };\n\n const applyAction = () => {\n if (pendingRouterState) {\n // The programmatic navigation is already running inside React.startTransition\n // (from router.push/replace/refresh), so resolving the deferred promise is\n // sufficient — no additional startTransition wrapper is needed below.\n resolvePendingBrowserRouterState(pendingRouterState, action);\n return;\n }\n\n setter(routerReducer(getBrowserRouterState(), action));\n };\n\n if (useTransitionMode) {\n startTransition(applyAction);\n } else {\n applyAction();\n }\n }\n\n async function renderNavigationPayload(options: {\n actionType: \"navigate\" | \"replace\" | \"traverse\";\n createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;\n historyUpdateMode: HistoryUpdateMode | undefined;\n navigationSnapshot: ClientNavigationRenderSnapshot;\n nextElements: Promise<AppElements>;\n params: Record<string, string | string[]>;\n pendingRouterState: PendingBrowserRouterState | null;\n previousNextUrl: string | null;\n targetHref: string;\n navId: number;\n useTransition?: boolean;\n }): Promise<void> {\n const renderId = allocateRenderId();\n let resolveCommitted: (() => void) | undefined;\n const committed = new Promise<void>((resolve) => {\n resolveCommitted = resolve;\n pendingNavigationCommits.set(renderId, resolve);\n });\n\n let snapshotActivated = false;\n try {\n const currentState = getBrowserRouterState();\n const pending = await createPendingNavigationCommit({\n currentState,\n nextElements: options.nextElements,\n navigationSnapshot: options.navigationSnapshot,\n previousNextUrl: options.previousNextUrl,\n renderId,\n type: options.actionType,\n });\n\n const disposition = resolvePendingNavigationCommitDisposition({\n activeNavigationId,\n currentRootLayoutTreePath: currentState.rootLayoutTreePath,\n nextRootLayoutTreePath: pending.rootLayoutTreePath,\n startedNavigationId: options.navId,\n });\n\n if (disposition === \"skip\") {\n settlePendingBrowserRouterState(options.pendingRouterState);\n pendingNavigationCommits.delete(renderId);\n resolveCommitted?.();\n return;\n }\n\n if (disposition === \"hard-navigate\") {\n settlePendingBrowserRouterState(options.pendingRouterState);\n pendingNavigationCommits.delete(renderId);\n window.location.assign(options.targetHref);\n return;\n }\n\n queuePrePaintNavigationEffect(\n renderId,\n options.createNavigationCommitEffect({\n href: options.targetHref,\n historyUpdateMode: options.historyUpdateMode,\n navId: options.navId,\n params: options.params,\n previousNextUrl: pending.previousNextUrl,\n }),\n );\n activateNavigationSnapshot();\n snapshotActivated = true;\n dispatchBrowserTree(\n pending.action.elements,\n options.navigationSnapshot,\n renderId,\n options.actionType,\n pending.interceptionContext,\n pending.action.layoutFlags,\n pending.previousNextUrl,\n pending.routeId,\n pending.rootLayoutTreePath,\n options.pendingRouterState,\n options.useTransition ?? true,\n );\n } catch (error) {\n pendingNavigationPrePaintEffects.delete(renderId);\n pendingNavigationCommits.delete(renderId);\n if (snapshotActivated) {\n commitClientNavigationStateImpl(options.navId);\n }\n settlePendingBrowserRouterState(options.pendingRouterState);\n resolveCommitted?.();\n throw error;\n }\n\n return committed;\n }\n\n async function commitSameUrlNavigatePayload(\n nextElements: Promise<AppElements>,\n navigationSnapshot: ClientNavigationRenderSnapshot,\n returnValue?: { ok: boolean; data: unknown },\n ): Promise<unknown> {\n const currentState = getBrowserRouterState();\n const startedNavigationId = activeNavigationId;\n // Known limitation: if a same-URL navigation fully commits while this\n // server action is awaiting resolveAndClassifyNavigationCommit(), the action\n // can still dispatch its older payload afterward. The old pre-2c code had\n // the same race, and Next.js has similar behavior. Tightening this would\n // need a stronger commit-version gate than activeNavigationId alone.\n const { disposition, pending } = await resolveAndClassifyNavigationCommit({\n activeNavigationId,\n currentState,\n navigationSnapshot,\n nextElements,\n renderId: allocateRenderId(),\n startedNavigationId,\n type: \"navigate\",\n });\n\n if (disposition === \"hard-navigate\") {\n window.location.assign(window.location.href);\n return undefined;\n }\n\n if (disposition === \"dispatch\") {\n dispatchBrowserTree(\n pending.action.elements,\n navigationSnapshot,\n pending.action.renderId,\n \"navigate\",\n pending.interceptionContext,\n pending.action.layoutFlags,\n pending.previousNextUrl,\n pending.routeId,\n pending.rootLayoutTreePath,\n null,\n false,\n );\n }\n\n // Same-URL server actions still return their action value even if the UI\n // update was skipped due to a superseding navigation. That preserves the\n // existing caller contract; a future Phase 2 router state model could make\n // skipped UI updates observable to the caller without conflating them here.\n if (returnValue) {\n if (!returnValue.ok) {\n throw returnValue.data;\n }\n return returnValue.data;\n }\n\n return undefined;\n }\n\n function attachBrowserRouterState(\n setter: Dispatch<AppRouterState | Promise<AppRouterState>>,\n stateRef: BrowserRouterStateRef,\n ): () => void {\n setBrowserRouterState = setter;\n browserRouterStateRef = stateRef;\n markBrowserRouterStateReady();\n\n return () => {\n if (setBrowserRouterState === setter) {\n setBrowserRouterState = null;\n }\n if (browserRouterStateRef === stateRef) {\n browserRouterStateRef = null;\n browserRouterStateHasCommitted = false;\n }\n };\n }\n\n return {\n beginNavigation,\n hasBrowserRouterState,\n getBrowserRouterState,\n isCurrentNavigation,\n waitForBrowserRouterStateReady,\n attachBrowserRouterState,\n beginPendingBrowserRouterState,\n finalizeNavigation,\n renderNavigationPayload,\n commitSameUrlNavigatePayload,\n hmrReplaceTree,\n drainPrePaintEffects,\n NavigationCommitSignal,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-browser-navigation-controller.js","names":[],"sources":["../../src/server/app-browser-navigation-controller.ts"],"sourcesContent":["import { startTransition, useLayoutEffect, type Dispatch, type ReactNode } from \"react\";\nimport {\n activateNavigationSnapshot,\n clearPendingPathname,\n commitClientNavigationState,\n} from \"vinext/shims/navigation\";\nimport type { ClientNavigationRenderSnapshot } from \"vinext/shims/navigation\";\nimport {\n createPendingNavigationCommit,\n type AppRouterState,\n type OperationLane,\n} from \"./app-browser-state.js\";\nimport {\n applyApprovedVisibleCommit,\n approveHmrVisibleCommit,\n approvePendingNavigationCommit,\n resolveAndClassifyNavigationCommit,\n type ApprovedVisibleCommit,\n} from \"./app-browser-visible-commit.js\";\nimport type { AppElements } from \"./app-elements.js\";\n\nexport type HistoryUpdateMode = \"push\" | \"replace\";\n\nexport type PendingBrowserRouterState = {\n promise: Promise<AppRouterState>;\n resolve: (state: AppRouterState) => void;\n settled: boolean;\n};\nexport type NavigationPayloadOutcome = \"committed\" | \"no-commit\" | \"hard-navigate\";\n\ntype BrowserNavigationCommitEffectFactory = (options: {\n href: string;\n historyUpdateMode: HistoryUpdateMode | undefined;\n navId: number;\n params: Record<string, string | string[]>;\n previousNextUrl: string | null;\n}) => () => void;\n\ntype BrowserRouterStateRef = {\n current: AppRouterState;\n};\n\ntype BrowserNavigationControllerDeps = {\n commitClientNavigationState?: typeof commitClientNavigationState;\n};\n\ntype BrowserNavigationController = {\n beginNavigation(): number;\n hasBrowserRouterState(): boolean;\n getBrowserRouterState(): AppRouterState;\n isCurrentNavigation(navId: number): boolean;\n waitForBrowserRouterStateReady(): Promise<void>;\n attachBrowserRouterState(\n setter: Dispatch<AppRouterState | Promise<AppRouterState>>,\n stateRef: BrowserRouterStateRef,\n ): () => void;\n beginPendingBrowserRouterState(): PendingBrowserRouterState;\n finalizeNavigation(navId: number, pending: PendingBrowserRouterState | null | undefined): void;\n renderNavigationPayload(options: {\n actionType: \"navigate\" | \"replace\" | \"traverse\";\n createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;\n historyUpdateMode: HistoryUpdateMode | undefined;\n navigationSnapshot: ClientNavigationRenderSnapshot;\n nextElements: Promise<AppElements>;\n operationLane: OperationLane;\n params: Record<string, string | string[]>;\n pendingRouterState: PendingBrowserRouterState | null;\n previousNextUrl: string | null;\n targetHref: string;\n navId: number;\n useTransition?: boolean;\n }): Promise<NavigationPayloadOutcome>;\n commitSameUrlNavigatePayload(\n nextElements: Promise<AppElements>,\n navigationSnapshot: ClientNavigationRenderSnapshot,\n returnValue?: { ok: boolean; data: unknown },\n actionInitiationState?: AppRouterState,\n ): Promise<unknown>;\n hmrReplaceTree(\n nextElements: Promise<AppElements>,\n navigationSnapshot: ClientNavigationRenderSnapshot,\n ): Promise<void>;\n /**\n * Force-drain the queued pre-paint effect for the given renderId without\n * waiting for NavigationCommitSignal to commit. Used by the dev recovery\n * boundary in app-browser-entry.ts: when a render error replaces\n * NavigationCommitSignal with the boundary's null fallback, its\n * useLayoutEffect never fires, so the URL update for the in-flight\n * navigation would otherwise be lost.\n */\n drainPrePaintEffects(renderId: number): void;\n NavigationCommitSignal(\n this: void,\n {\n renderId,\n children,\n }: {\n renderId: number;\n children?: ReactNode;\n },\n ): ReactNode;\n};\n\nexport function createAppBrowserNavigationController(\n deps: BrowserNavigationControllerDeps = {},\n): BrowserNavigationController {\n const commitClientNavigationStateImpl =\n deps.commitClientNavigationState ?? commitClientNavigationState;\n\n // These are plain module-level variables (inside the controller closure),\n // unlike ClientNavigationState which uses Symbol.for to survive multiple\n // Vite module instances. The browser entry is loaded exactly once (via the\n // RSC plugin's generated bootstrap), so the controller running in a single\n // module instance is safe. If that assumption ever changes, these should be\n // migrated to a Symbol.for-backed global.\n //\n // The most severe consequence of multiple instances would be Map fragmentation:\n // pendingNavigationCommits and pendingNavigationPrePaintEffects would split\n // across instances, so drainPrePaintEffects in one instance could never drain\n // effects queued by the other, permanently leaking navigationSnapshotActiveCount\n // and causing hooks to prefer stale snapshot values indefinitely.\n let nextNavigationRenderId = 0;\n let activeNavigationId = 0;\n const pendingNavigationCommits = new Map<number, () => void>();\n const pendingNavigationPrePaintEffects = new Map<number, () => void>();\n\n let setBrowserRouterState: Dispatch<AppRouterState | Promise<AppRouterState>> | null = null;\n let browserRouterStateRef: BrowserRouterStateRef | null = null;\n let activePendingBrowserRouterState: PendingBrowserRouterState | null = null;\n let resolveBrowserRouterStateReady: (() => void) | null = null;\n let browserRouterStateReadyPromise: Promise<void> | null = null;\n let browserRouterStateHasCommitted = false;\n\n function getBrowserRouterStateSetter(): Dispatch<AppRouterState | Promise<AppRouterState>> {\n if (!setBrowserRouterState) {\n throw new Error(\"[vinext] Browser router state setter is not initialized\");\n }\n return setBrowserRouterState;\n }\n\n function getBrowserRouterState(): AppRouterState {\n if (!browserRouterStateRef) {\n throw new Error(\"[vinext] Browser router state is not initialized\");\n }\n return browserRouterStateRef.current;\n }\n\n function waitForBrowserRouterStateReady(): Promise<void> {\n if (browserRouterStateRef || browserRouterStateHasCommitted) {\n return Promise.resolve();\n }\n\n if (!browserRouterStateReadyPromise) {\n browserRouterStateReadyPromise = new Promise((resolve) => {\n resolveBrowserRouterStateReady = resolve;\n });\n }\n\n return browserRouterStateReadyPromise;\n }\n\n function markBrowserRouterStateReady(): void {\n browserRouterStateHasCommitted = true;\n const resolveReady = resolveBrowserRouterStateReady;\n resolveBrowserRouterStateReady = null;\n browserRouterStateReadyPromise = null;\n resolveReady?.();\n }\n\n function beginNavigation(): number {\n activeNavigationId += 1;\n return activeNavigationId;\n }\n\n function allocateRenderId(): number {\n nextNavigationRenderId += 1;\n return nextNavigationRenderId;\n }\n\n function hasBrowserRouterState(): boolean {\n return browserRouterStateRef !== null;\n }\n\n function isCurrentNavigation(navId: number): boolean {\n return navId === activeNavigationId;\n }\n\n function beginPendingBrowserRouterState(): PendingBrowserRouterState {\n const setter = getBrowserRouterStateSetter();\n\n if (activePendingBrowserRouterState && !activePendingBrowserRouterState.settled) {\n activePendingBrowserRouterState.settled = true;\n activePendingBrowserRouterState.resolve(getBrowserRouterState());\n }\n\n let resolvePending: ((state: AppRouterState) => void) | undefined;\n const promise = new Promise<AppRouterState>((resolve) => {\n resolvePending = resolve;\n });\n\n if (!resolvePending) {\n throw new Error(\"[vinext] Failed to initialize browser router promise\");\n }\n\n const pending: PendingBrowserRouterState = {\n promise,\n resolve: resolvePending,\n settled: false,\n };\n\n activePendingBrowserRouterState = pending;\n setter(promise);\n\n return pending;\n }\n\n function settlePendingBrowserRouterState(\n pending: PendingBrowserRouterState | null | undefined,\n ): void {\n if (!pending || pending.settled) return;\n\n pending.settled = true;\n pending.resolve(getBrowserRouterState());\n\n if (activePendingBrowserRouterState === pending) {\n activePendingBrowserRouterState = null;\n }\n }\n\n function finalizeNavigation(\n navId: number,\n pending: PendingBrowserRouterState | null | undefined,\n ): void {\n settlePendingBrowserRouterState(pending);\n\n if (isCurrentNavigation(navId)) {\n clearPendingPathname(navId);\n }\n }\n\n function resolvePendingBrowserRouterState(\n pending: PendingBrowserRouterState | null | undefined,\n commit: ApprovedVisibleCommit,\n ): void {\n if (!pending || pending.settled) return;\n\n pending.settled = true;\n pending.resolve(applyApprovedVisibleCommit(getBrowserRouterState(), commit));\n\n if (activePendingBrowserRouterState === pending) {\n activePendingBrowserRouterState = null;\n }\n }\n\n function queuePrePaintNavigationEffect(renderId: number, effect: (() => void) | null): void {\n if (!effect) {\n return;\n }\n pendingNavigationPrePaintEffects.set(renderId, effect);\n }\n\n /**\n * Run all queued pre-paint effects for renderIds up to and including the\n * given renderId. When React supersedes a startTransition update (rapid\n * clicks on same-route links), the superseded NavigationCommitSignal never\n * mounts, so its pre-paint effect never fires. By draining all effects\n * <= the committed renderId here, the winning transition cleans up after\n * any superseded ones, keeping the counter balanced.\n *\n * Invariant: each superseded navigation gets a commitClientNavigationState()\n * to balance the activateNavigationSnapshot() from its renderNavigationPayload call.\n */\n function drainPrePaintEffects(upToRenderId: number): void {\n for (const [id, effect] of pendingNavigationPrePaintEffects) {\n if (id > upToRenderId) {\n continue;\n }\n\n pendingNavigationPrePaintEffects.delete(id);\n if (id === upToRenderId) {\n effect();\n } else {\n // Superseded navigations still need to balance the snapshot counter.\n commitClientNavigationStateImpl(undefined, { releaseSnapshot: true });\n }\n }\n }\n\n /**\n * Resolve all pending navigation commits with renderId <= the committed renderId.\n * Note: Map iteration handles concurrent deletion safely — entries are visited in\n * insertion order and deletion doesn't affect the iterator's view of remaining entries.\n * This pattern is also used in drainPrePaintEffects with the same semantics.\n */\n function resolveCommittedNavigations(renderId: number): void {\n for (const [pendingId, resolve] of pendingNavigationCommits) {\n if (pendingId > renderId) {\n continue;\n }\n\n pendingNavigationCommits.delete(pendingId);\n resolve();\n }\n }\n\n async function hmrReplaceTree(\n nextElements: Promise<AppElements>,\n navigationSnapshot: ClientNavigationRenderSnapshot,\n ): Promise<void> {\n if (!hasBrowserRouterState()) return;\n\n const currentState = getBrowserRouterState();\n const renderId = allocateRenderId();\n const pending = await createPendingNavigationCommit({\n currentState,\n nextElements,\n navigationSnapshot,\n operationLane: \"hmr\",\n renderId,\n type: \"replace\",\n });\n\n // createPendingNavigationCommit awaits the new RSC payload. While\n // suspended, the prior broken render can unmount BrowserRoot. Re-check\n // before dispatching so a racing unmount doesn't surface as an\n // initialized-setter error.\n if (!hasBrowserRouterState()) return;\n\n dispatchApprovedVisibleCommit(approveHmrVisibleCommit(pending), null, false);\n }\n\n function NavigationCommitSignal(\n this: void,\n {\n renderId,\n children,\n }: {\n renderId: number;\n children?: ReactNode;\n },\n ): ReactNode {\n useLayoutEffect(() => {\n drainPrePaintEffects(renderId);\n\n const frame = requestAnimationFrame(() => {\n resolveCommittedNavigations(renderId);\n });\n\n return () => {\n cancelAnimationFrame(frame);\n // Resolve pending commits to prevent callers from hanging if React\n // unmounts this component without committing (e.g., error boundary).\n resolveCommittedNavigations(renderId);\n };\n }, [renderId]);\n\n return children;\n }\n\n function dispatchApprovedVisibleCommit(\n commit: ApprovedVisibleCommit,\n pendingRouterState: PendingBrowserRouterState | null,\n useTransitionMode: boolean,\n ): void {\n const setter = getBrowserRouterStateSetter();\n\n const applyAction = () => {\n if (pendingRouterState) {\n // The programmatic navigation is already running inside React.startTransition\n // (from router.push/replace/refresh), so resolving the deferred promise is\n // sufficient — no additional startTransition wrapper is needed below.\n resolvePendingBrowserRouterState(pendingRouterState, commit);\n return;\n }\n\n setter(applyApprovedVisibleCommit(getBrowserRouterState(), commit));\n };\n\n if (useTransitionMode) {\n startTransition(applyAction);\n } else {\n applyAction();\n }\n }\n\n async function renderNavigationPayload(options: {\n actionType: \"navigate\" | \"replace\" | \"traverse\";\n createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;\n historyUpdateMode: HistoryUpdateMode | undefined;\n navigationSnapshot: ClientNavigationRenderSnapshot;\n nextElements: Promise<AppElements>;\n operationLane: OperationLane;\n params: Record<string, string | string[]>;\n pendingRouterState: PendingBrowserRouterState | null;\n previousNextUrl: string | null;\n targetHref: string;\n navId: number;\n useTransition?: boolean;\n }): Promise<NavigationPayloadOutcome> {\n const renderId = allocateRenderId();\n let resolveCommitted: (() => void) | undefined;\n const committed = new Promise<void>((resolve) => {\n resolveCommitted = resolve;\n pendingNavigationCommits.set(renderId, resolve);\n });\n\n let snapshotActivated = false;\n try {\n const startedState = getBrowserRouterState();\n const pending = await createPendingNavigationCommit({\n currentState: startedState,\n nextElements: options.nextElements,\n navigationSnapshot: options.navigationSnapshot,\n operationLane: options.operationLane,\n previousNextUrl: options.previousNextUrl,\n renderId,\n type: options.actionType,\n });\n\n const approval = approvePendingNavigationCommit({\n activeNavigationId,\n currentState: getBrowserRouterState(),\n pending,\n startedNavigationId: options.navId,\n });\n\n if (approval.decision.disposition === \"no-commit\") {\n settlePendingBrowserRouterState(options.pendingRouterState);\n pendingNavigationCommits.delete(renderId);\n resolveCommitted?.();\n return \"no-commit\";\n }\n\n if (approval.decision.disposition === \"hard-navigate\") {\n settlePendingBrowserRouterState(options.pendingRouterState);\n pendingNavigationCommits.delete(renderId);\n window.location.assign(options.targetHref);\n return \"hard-navigate\";\n }\n\n const approvedCommit = approval.approvedCommit;\n if (approvedCommit === null) {\n throw new Error(\"[vinext] Commit decision did not approve a visible commit\");\n }\n\n queuePrePaintNavigationEffect(\n renderId,\n options.createNavigationCommitEffect({\n href: options.targetHref,\n historyUpdateMode: options.historyUpdateMode,\n navId: options.navId,\n params: options.params,\n previousNextUrl: approvedCommit.previousNextUrl,\n }),\n );\n activateNavigationSnapshot();\n snapshotActivated = true;\n dispatchApprovedVisibleCommit(\n approvedCommit,\n options.pendingRouterState,\n options.useTransition ?? true,\n );\n } catch (error) {\n pendingNavigationPrePaintEffects.delete(renderId);\n pendingNavigationCommits.delete(renderId);\n if (snapshotActivated) {\n commitClientNavigationStateImpl(options.navId);\n }\n settlePendingBrowserRouterState(options.pendingRouterState);\n resolveCommitted?.();\n throw error;\n }\n\n return committed.then(() => \"committed\");\n }\n\n async function commitSameUrlNavigatePayload(\n nextElements: Promise<AppElements>,\n navigationSnapshot: ClientNavigationRenderSnapshot,\n returnValue?: { ok: boolean; data: unknown },\n actionInitiationState?: AppRouterState,\n ): Promise<unknown> {\n const currentState = actionInitiationState ?? getBrowserRouterState();\n const startedNavigationId = activeNavigationId;\n const {\n approvedCommit,\n decision,\n pending,\n // Intentionally retained as #726-OPS-01 trace-shell scaffolding. The\n // same-URL action path can consume this trace once later lifecycle gates\n // need an observable commit explanation.\n trace: _navigationTrace,\n } = await resolveAndClassifyNavigationCommit({\n activeNavigationId,\n currentState,\n getActiveNavigationId: () => activeNavigationId,\n getCurrentStateForApproval: getBrowserRouterState,\n navigationSnapshot,\n nextElements,\n renderId: allocateRenderId(),\n operationLane: \"server-action\",\n startedNavigationId,\n type: \"navigate\",\n });\n\n if (decision.disposition === \"hard-navigate\") {\n window.location.assign(window.location.href);\n return undefined;\n }\n\n if (approvedCommit) {\n // The helper approval and this continuation are separated by a microtask\n // boundary, so re-check lifecycle authority before mutating visible UI.\n const latestApproval = approvePendingNavigationCommit({\n activeNavigationId,\n currentState: getBrowserRouterState(),\n pending,\n startedNavigationId,\n });\n\n if (latestApproval.decision.disposition === \"hard-navigate\") {\n window.location.assign(window.location.href);\n return undefined;\n }\n\n if (latestApproval.approvedCommit) {\n dispatchApprovedVisibleCommit(latestApproval.approvedCommit, null, false);\n }\n }\n\n // Same-URL server actions still return their action value even if the UI\n // update was skipped due to a superseding navigation. That preserves the\n // existing caller contract; a future Phase 2 router state model could make\n // skipped UI updates observable to the caller without conflating them here.\n if (returnValue) {\n if (!returnValue.ok) {\n throw returnValue.data;\n }\n return returnValue.data;\n }\n\n return undefined;\n }\n\n function attachBrowserRouterState(\n setter: Dispatch<AppRouterState | Promise<AppRouterState>>,\n stateRef: BrowserRouterStateRef,\n ): () => void {\n setBrowserRouterState = setter;\n browserRouterStateRef = stateRef;\n markBrowserRouterStateReady();\n\n return () => {\n if (setBrowserRouterState === setter) {\n setBrowserRouterState = null;\n }\n if (browserRouterStateRef === stateRef) {\n browserRouterStateRef = null;\n browserRouterStateHasCommitted = false;\n }\n };\n }\n\n return {\n beginNavigation,\n hasBrowserRouterState,\n getBrowserRouterState,\n isCurrentNavigation,\n waitForBrowserRouterStateReady,\n attachBrowserRouterState,\n beginPendingBrowserRouterState,\n finalizeNavigation,\n renderNavigationPayload,\n commitSameUrlNavigatePayload,\n hmrReplaceTree,\n drainPrePaintEffects,\n NavigationCommitSignal,\n };\n}\n"],"mappings":"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"}
@@ -1,11 +1,27 @@
1
1
  import { ClientNavigationRenderSnapshot } from "../shims/navigation.js";
2
- import { AppElements, LayoutFlags } from "./app-elements.js";
2
+ import { AppElements, LayoutFlags } from "./app-elements-wire.js";
3
+ import { NavigationTrace } from "./navigation-trace.js";
3
4
 
4
5
  //#region src/server/app-browser-state.d.ts
5
6
  type HistoryStateRecord = {
6
7
  [key: string]: unknown;
7
8
  };
9
+ type OperationLane = "navigation" | "refresh" | "traverse" | "server-action" | "hmr";
10
+ type OperationRecordBase = {
11
+ id: number;
12
+ lane: OperationLane;
13
+ startedVisibleCommitVersion: number;
14
+ };
15
+ type PendingOperationRecord = OperationRecordBase & {
16
+ state: "pending";
17
+ };
18
+ type CommittedOperationRecord = OperationRecordBase & {
19
+ state: "committed";
20
+ visibleCommitVersion: number;
21
+ };
22
+ type OperationRecord = PendingOperationRecord | CommittedOperationRecord;
8
23
  type AppRouterState = {
24
+ activeOperation: OperationRecord | null;
9
25
  elements: AppElements;
10
26
  interceptionContext: string | null;
11
27
  layoutFlags: LayoutFlags;
@@ -14,12 +30,14 @@ type AppRouterState = {
14
30
  navigationSnapshot: ClientNavigationRenderSnapshot;
15
31
  rootLayoutTreePath: string | null;
16
32
  routeId: string;
33
+ visibleCommitVersion: number;
17
34
  };
18
35
  type AppRouterAction = {
19
36
  elements: AppElements;
20
37
  interceptionContext: string | null;
21
38
  layoutFlags: LayoutFlags;
22
39
  navigationSnapshot: ClientNavigationRenderSnapshot;
40
+ operation: PendingOperationRecord;
23
41
  previousNextUrl: string | null;
24
42
  renderId: number;
25
43
  rootLayoutTreePath: string | null;
@@ -34,9 +52,9 @@ type PendingNavigationCommit = {
34
52
  routeId: string;
35
53
  };
36
54
  type PendingNavigationCommitDisposition = "dispatch" | "hard-navigate" | "skip";
37
- type ClassifiedPendingNavigationCommit = {
55
+ type PendingNavigationCommitDispositionDecision = {
38
56
  disposition: PendingNavigationCommitDisposition;
39
- pending: PendingNavigationCommit;
57
+ trace: NavigationTrace;
40
58
  };
41
59
  declare function createHistoryStateWithPreviousNextUrl(state: unknown, previousNextUrl: string | null): HistoryStateRecord | null;
42
60
  declare function readHistoryStatePreviousNextUrl(state: unknown): string | null;
@@ -62,32 +80,32 @@ type ResolveServerActionRequestStateResult = {
62
80
  * `findIntercept` lookup.
63
81
  */
64
82
  declare function resolveServerActionRequestState(options: ResolveServerActionRequestStateOptions): ResolveServerActionRequestStateResult;
65
- declare function routerReducer(state: AppRouterState, action: AppRouterAction): AppRouterState;
66
83
  declare function shouldHardNavigate(currentRootLayoutTreePath: string | null, nextRootLayoutTreePath: string | null): boolean;
67
84
  declare function resolvePendingNavigationCommitDisposition(options: {
68
85
  activeNavigationId: number;
86
+ currentVisibleCommitVersion: number;
69
87
  currentRootLayoutTreePath: string | null;
70
88
  nextRootLayoutTreePath: string | null;
71
89
  startedNavigationId: number;
90
+ startedVisibleCommitVersion: number;
72
91
  }): PendingNavigationCommitDisposition;
92
+ declare function resolvePendingNavigationCommitDispositionDecision(options: {
93
+ activeNavigationId: number;
94
+ currentVisibleCommitVersion: number;
95
+ currentRootLayoutTreePath: string | null;
96
+ nextRootLayoutTreePath: string | null;
97
+ startedNavigationId: number;
98
+ startedVisibleCommitVersion: number;
99
+ }): PendingNavigationCommitDispositionDecision;
73
100
  declare function createPendingNavigationCommit(options: {
74
101
  currentState: AppRouterState;
75
102
  nextElements: Promise<AppElements>;
76
103
  navigationSnapshot: ClientNavigationRenderSnapshot;
104
+ operationLane: OperationLane;
77
105
  previousNextUrl?: string | null;
78
106
  renderId: number;
79
107
  type: "navigate" | "replace" | "traverse";
80
108
  }): Promise<PendingNavigationCommit>;
81
- declare function resolveAndClassifyNavigationCommit(options: {
82
- activeNavigationId: number;
83
- currentState: AppRouterState;
84
- navigationSnapshot: ClientNavigationRenderSnapshot;
85
- nextElements: Promise<AppElements>;
86
- previousNextUrl?: string | null;
87
- renderId: number;
88
- startedNavigationId: number;
89
- type: "navigate" | "replace" | "traverse";
90
- }): Promise<ClassifiedPendingNavigationCommit>;
91
109
  //#endregion
92
- export { AppRouterAction, AppRouterState, createHistoryStateWithPreviousNextUrl, createPendingNavigationCommit, readHistoryStatePreviousNextUrl, resolveAndClassifyNavigationCommit, resolveInterceptionContextFromPreviousNextUrl, resolvePendingNavigationCommitDisposition, resolveServerActionRequestState, routerReducer, shouldHardNavigate };
110
+ export { AppRouterAction, AppRouterState, CommittedOperationRecord, OperationLane, OperationRecord, PendingNavigationCommit, PendingOperationRecord, createHistoryStateWithPreviousNextUrl, createPendingNavigationCommit, readHistoryStatePreviousNextUrl, resolveInterceptionContextFromPreviousNextUrl, resolvePendingNavigationCommitDisposition, resolvePendingNavigationCommitDispositionDecision, resolveServerActionRequestState, shouldHardNavigate };
93
111
  //# sourceMappingURL=app-browser-state.d.ts.map
@@ -1,6 +1,8 @@
1
1
  import { stripBasePath } from "../utils/base-path.js";
2
- import { getMountedSlotIdsHeader, readAppElementsMetadata } from "./app-elements.js";
3
- import { mergeElements } from "../shims/slot.js";
2
+ import { AppElementsWire } from "./app-elements-wire.js";
3
+ import { getMountedSlotIdsHeader } from "./app-elements.js";
4
+ import { createRscRequestHeaders } from "./app-rsc-cache-busting.js";
5
+ import { NavigationTraceReasonCodes, createNavigationTrace } from "./navigation-trace.js";
4
6
  //#region src/server/app-browser-state.ts
5
7
  const VINEXT_PREVIOUS_NEXT_URL_HISTORY_STATE_KEY = "__vinext_previousNextUrl";
6
8
  function cloneHistoryState(state) {
@@ -19,6 +21,14 @@ function readHistoryStatePreviousNextUrl(state) {
19
21
  const value = cloneHistoryState(state)[VINEXT_PREVIOUS_NEXT_URL_HISTORY_STATE_KEY];
20
22
  return typeof value === "string" ? value : null;
21
23
  }
24
+ function createOperationRecord(options) {
25
+ return {
26
+ id: options.id,
27
+ lane: options.lane,
28
+ startedVisibleCommitVersion: options.startedVisibleCommitVersion,
29
+ state: "pending"
30
+ };
31
+ }
22
32
  function resolveInterceptionContextFromPreviousNextUrl(previousNextUrl, basePath = "") {
23
33
  if (previousNextUrl === null) return null;
24
34
  return stripBasePath(new URL(previousNextUrl, "http://localhost").pathname, basePath);
@@ -35,59 +45,56 @@ function resolveInterceptionContextFromPreviousNextUrl(previousNextUrl, basePath
35
45
  * `findIntercept` lookup.
36
46
  */
37
47
  function resolveServerActionRequestState(options) {
38
- const headers = new Headers({
39
- Accept: "text/x-component",
40
- "x-rsc-action": options.actionId
41
- });
48
+ const headers = createRscRequestHeaders();
49
+ headers.set("x-rsc-action", options.actionId);
42
50
  const interceptionContext = resolveInterceptionContextFromPreviousNextUrl(options.previousNextUrl, options.basePath);
43
51
  if (interceptionContext !== null) headers.set("X-Vinext-Interception-Context", interceptionContext);
44
52
  const mountedSlotsHeader = getMountedSlotIdsHeader(options.elements);
45
53
  if (mountedSlotsHeader !== null) headers.set("X-Vinext-Mounted-Slots", mountedSlotsHeader);
46
54
  return { headers };
47
55
  }
48
- function routerReducer(state, action) {
49
- switch (action.type) {
50
- case "traverse":
51
- case "navigate": return {
52
- elements: mergeElements(state.elements, action.elements, action.type === "traverse"),
53
- interceptionContext: action.interceptionContext,
54
- layoutFlags: {
55
- ...state.layoutFlags,
56
- ...action.layoutFlags
57
- },
58
- navigationSnapshot: action.navigationSnapshot,
59
- previousNextUrl: action.previousNextUrl,
60
- renderId: action.renderId,
61
- rootLayoutTreePath: action.rootLayoutTreePath,
62
- routeId: action.routeId
63
- };
64
- case "replace": return {
65
- elements: action.elements,
66
- interceptionContext: action.interceptionContext,
67
- layoutFlags: action.layoutFlags,
68
- navigationSnapshot: action.navigationSnapshot,
69
- previousNextUrl: action.previousNextUrl,
70
- renderId: action.renderId,
71
- rootLayoutTreePath: action.rootLayoutTreePath,
72
- routeId: action.routeId
73
- };
74
- default: {
75
- const _exhaustive = action.type;
76
- throw new Error("[vinext] Unknown router action: " + String(_exhaustive));
77
- }
78
- }
79
- }
80
56
  function shouldHardNavigate(currentRootLayoutTreePath, nextRootLayoutTreePath) {
81
57
  return currentRootLayoutTreePath !== null && nextRootLayoutTreePath !== null && currentRootLayoutTreePath !== nextRootLayoutTreePath;
82
58
  }
83
59
  function resolvePendingNavigationCommitDisposition(options) {
84
60
  if (options.startedNavigationId !== options.activeNavigationId) return "skip";
61
+ if (options.startedVisibleCommitVersion !== options.currentVisibleCommitVersion) return "skip";
85
62
  if (shouldHardNavigate(options.currentRootLayoutTreePath, options.nextRootLayoutTreePath)) return "hard-navigate";
86
63
  return "dispatch";
87
64
  }
65
+ function resolvePendingNavigationCommitDispositionDecision(options) {
66
+ const disposition = resolvePendingNavigationCommitDisposition(options);
67
+ const traceFields = {
68
+ activeNavigationId: options.activeNavigationId,
69
+ currentRootLayoutTreePath: options.currentRootLayoutTreePath,
70
+ currentVisibleCommitVersion: options.currentVisibleCommitVersion,
71
+ nextRootLayoutTreePath: options.nextRootLayoutTreePath,
72
+ startedNavigationId: options.startedNavigationId,
73
+ startedVisibleCommitVersion: options.startedVisibleCommitVersion
74
+ };
75
+ return {
76
+ disposition,
77
+ trace: createNavigationTrace(getPendingNavigationCommitDispositionTraceCode({
78
+ currentRootLayoutTreePath: options.currentRootLayoutTreePath,
79
+ disposition,
80
+ nextRootLayoutTreePath: options.nextRootLayoutTreePath
81
+ }), traceFields)
82
+ };
83
+ }
84
+ function getPendingNavigationCommitDispositionTraceCode(options) {
85
+ switch (options.disposition) {
86
+ case "skip": return NavigationTraceReasonCodes.staleOperation;
87
+ case "hard-navigate": return NavigationTraceReasonCodes.rootBoundaryChanged;
88
+ case "dispatch": return options.currentRootLayoutTreePath === null || options.nextRootLayoutTreePath === null ? NavigationTraceReasonCodes.rootBoundaryUnknown : NavigationTraceReasonCodes.commitCurrent;
89
+ default: {
90
+ const _exhaustive = options.disposition;
91
+ throw new Error("[vinext] Unknown navigation commit disposition: " + String(_exhaustive));
92
+ }
93
+ }
94
+ }
88
95
  async function createPendingNavigationCommit(options) {
89
96
  const elements = await options.nextElements;
90
- const metadata = readAppElementsMetadata(elements);
97
+ const metadata = AppElementsWire.readMetadata(elements);
91
98
  const previousNextUrl = options.previousNextUrl !== void 0 ? options.previousNextUrl : options.currentState.previousNextUrl;
92
99
  return {
93
100
  action: {
@@ -95,6 +102,11 @@ async function createPendingNavigationCommit(options) {
95
102
  interceptionContext: metadata.interceptionContext,
96
103
  layoutFlags: metadata.layoutFlags,
97
104
  navigationSnapshot: options.navigationSnapshot,
105
+ operation: createOperationRecord({
106
+ id: options.renderId,
107
+ lane: options.operationLane,
108
+ startedVisibleCommitVersion: options.currentState.visibleCommitVersion
109
+ }),
98
110
  previousNextUrl,
99
111
  renderId: options.renderId,
100
112
  rootLayoutTreePath: metadata.rootLayoutTreePath,
@@ -107,26 +119,7 @@ async function createPendingNavigationCommit(options) {
107
119
  routeId: metadata.routeId
108
120
  };
109
121
  }
110
- async function resolveAndClassifyNavigationCommit(options) {
111
- const pending = await createPendingNavigationCommit({
112
- currentState: options.currentState,
113
- nextElements: options.nextElements,
114
- navigationSnapshot: options.navigationSnapshot,
115
- previousNextUrl: options.previousNextUrl,
116
- renderId: options.renderId,
117
- type: options.type
118
- });
119
- return {
120
- disposition: resolvePendingNavigationCommitDisposition({
121
- activeNavigationId: options.activeNavigationId,
122
- currentRootLayoutTreePath: options.currentState.rootLayoutTreePath,
123
- nextRootLayoutTreePath: pending.rootLayoutTreePath,
124
- startedNavigationId: options.startedNavigationId
125
- }),
126
- pending
127
- };
128
- }
129
122
  //#endregion
130
- export { createHistoryStateWithPreviousNextUrl, createPendingNavigationCommit, readHistoryStatePreviousNextUrl, resolveAndClassifyNavigationCommit, resolveInterceptionContextFromPreviousNextUrl, resolvePendingNavigationCommitDisposition, resolveServerActionRequestState, routerReducer, shouldHardNavigate };
123
+ export { createHistoryStateWithPreviousNextUrl, createPendingNavigationCommit, readHistoryStatePreviousNextUrl, resolveInterceptionContextFromPreviousNextUrl, resolvePendingNavigationCommitDisposition, resolvePendingNavigationCommitDispositionDecision, resolveServerActionRequestState, shouldHardNavigate };
131
124
 
132
125
  //# sourceMappingURL=app-browser-state.js.map