vinext 0.0.50 → 0.0.51

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 (309) hide show
  1. package/dist/build/google-fonts/fallback-metrics-data.js +14031 -0
  2. package/dist/build/google-fonts/fallback-metrics-data.js.map +1 -0
  3. package/dist/build/google-fonts/fallback-metrics.d.ts +13 -0
  4. package/dist/build/google-fonts/fallback-metrics.js +46 -0
  5. package/dist/build/google-fonts/fallback-metrics.js.map +1 -0
  6. package/dist/build/precompress.d.ts +13 -2
  7. package/dist/build/precompress.js +12 -3
  8. package/dist/build/precompress.js.map +1 -1
  9. package/dist/build/prerender.d.ts +1 -1
  10. package/dist/build/prerender.js +44 -14
  11. package/dist/build/prerender.js.map +1 -1
  12. package/dist/build/report.d.ts +5 -4
  13. package/dist/build/report.js +196 -348
  14. package/dist/build/report.js.map +1 -1
  15. package/dist/check.js +1 -0
  16. package/dist/check.js.map +1 -1
  17. package/dist/cli.js +60 -3
  18. package/dist/cli.js.map +1 -1
  19. package/dist/client/window-next.d.ts +3 -1
  20. package/dist/client/window-next.js.map +1 -1
  21. package/dist/config/dotenv.d.ts +11 -1
  22. package/dist/config/dotenv.js.map +1 -1
  23. package/dist/config/next-config.d.ts +87 -3
  24. package/dist/config/next-config.js +222 -6
  25. package/dist/config/next-config.js.map +1 -1
  26. package/dist/config/tsconfig-paths.d.ts +13 -0
  27. package/dist/config/tsconfig-paths.js +117 -0
  28. package/dist/config/tsconfig-paths.js.map +1 -0
  29. package/dist/deploy.js +3 -2
  30. package/dist/deploy.js.map +1 -1
  31. package/dist/entries/app-browser-entry.d.ts +2 -2
  32. package/dist/entries/app-browser-entry.js +26 -1
  33. package/dist/entries/app-browser-entry.js.map +1 -1
  34. package/dist/entries/app-rsc-entry.d.ts +19 -1
  35. package/dist/entries/app-rsc-entry.js +38 -12
  36. package/dist/entries/app-rsc-entry.js.map +1 -1
  37. package/dist/entries/app-rsc-manifest.d.ts +9 -0
  38. package/dist/entries/app-rsc-manifest.js +4 -1
  39. package/dist/entries/app-rsc-manifest.js.map +1 -1
  40. package/dist/entries/pages-client-entry.js +3 -5
  41. package/dist/entries/pages-client-entry.js.map +1 -1
  42. package/dist/entries/pages-server-entry.js +19 -1
  43. package/dist/entries/pages-server-entry.js.map +1 -1
  44. package/dist/index.js +130 -37
  45. package/dist/index.js.map +1 -1
  46. package/dist/plugins/client-reference-dedup.d.ts +15 -2
  47. package/dist/plugins/client-reference-dedup.js +138 -16
  48. package/dist/plugins/client-reference-dedup.js.map +1 -1
  49. package/dist/plugins/fonts.d.ts +2 -2
  50. package/dist/plugins/fonts.js +15 -6
  51. package/dist/plugins/fonts.js.map +1 -1
  52. package/dist/plugins/sass.d.ts +34 -0
  53. package/dist/plugins/sass.js +22 -0
  54. package/dist/plugins/sass.js.map +1 -0
  55. package/dist/routing/app-route-graph.d.ts +31 -2
  56. package/dist/routing/app-route-graph.js +82 -10
  57. package/dist/routing/app-route-graph.js.map +1 -1
  58. package/dist/routing/route-pattern.d.ts +56 -1
  59. package/dist/routing/route-pattern.js +60 -1
  60. package/dist/routing/route-pattern.js.map +1 -1
  61. package/dist/server/app-browser-action-result.d.ts +27 -2
  62. package/dist/server/app-browser-action-result.js +63 -2
  63. package/dist/server/app-browser-action-result.js.map +1 -1
  64. package/dist/server/app-browser-entry.js +262 -108
  65. package/dist/server/app-browser-entry.js.map +1 -1
  66. package/dist/server/app-browser-hydration.d.ts +13 -1
  67. package/dist/server/app-browser-hydration.js +9 -1
  68. package/dist/server/app-browser-hydration.js.map +1 -1
  69. package/dist/server/app-browser-navigation-controller.d.ts +14 -1
  70. package/dist/server/app-browser-navigation-controller.js +28 -9
  71. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  72. package/dist/server/app-browser-popstate.d.ts +16 -0
  73. package/dist/server/app-browser-popstate.js +17 -0
  74. package/dist/server/app-browser-popstate.js.map +1 -0
  75. package/dist/server/app-browser-rsc-redirect.d.ts +28 -0
  76. package/dist/server/app-browser-rsc-redirect.js +37 -0
  77. package/dist/server/app-browser-rsc-redirect.js.map +1 -0
  78. package/dist/server/app-browser-state.d.ts +11 -7
  79. package/dist/server/app-browser-state.js +45 -27
  80. package/dist/server/app-browser-state.js.map +1 -1
  81. package/dist/server/app-browser-stream.d.ts +5 -4
  82. package/dist/server/app-browser-stream.js +5 -6
  83. package/dist/server/app-browser-stream.js.map +1 -1
  84. package/dist/server/app-browser-visible-commit.d.ts +5 -0
  85. package/dist/server/app-browser-visible-commit.js +38 -5
  86. package/dist/server/app-browser-visible-commit.js.map +1 -1
  87. package/dist/server/app-elements-wire.d.ts +38 -6
  88. package/dist/server/app-elements-wire.js +106 -6
  89. package/dist/server/app-elements-wire.js.map +1 -1
  90. package/dist/server/app-elements.d.ts +2 -2
  91. package/dist/server/app-elements.js +2 -2
  92. package/dist/server/app-elements.js.map +1 -1
  93. package/dist/server/app-fallback-renderer.d.ts +10 -1
  94. package/dist/server/app-fallback-renderer.js +37 -1
  95. package/dist/server/app-fallback-renderer.js.map +1 -1
  96. package/dist/server/app-history-state.d.ts +26 -0
  97. package/dist/server/app-history-state.js +53 -0
  98. package/dist/server/app-history-state.js.map +1 -0
  99. package/dist/server/app-page-boundary-render.d.ts +10 -1
  100. package/dist/server/app-page-boundary-render.js +13 -6
  101. package/dist/server/app-page-boundary-render.js.map +1 -1
  102. package/dist/server/app-page-boundary.js +3 -2
  103. package/dist/server/app-page-boundary.js.map +1 -1
  104. package/dist/server/app-page-cache.d.ts +13 -0
  105. package/dist/server/app-page-cache.js +25 -8
  106. package/dist/server/app-page-cache.js.map +1 -1
  107. package/dist/server/app-page-dispatch.d.ts +5 -0
  108. package/dist/server/app-page-dispatch.js +68 -11
  109. package/dist/server/app-page-dispatch.js.map +1 -1
  110. package/dist/server/app-page-element-builder.d.ts +7 -0
  111. package/dist/server/app-page-element-builder.js +32 -4
  112. package/dist/server/app-page-element-builder.js.map +1 -1
  113. package/dist/server/app-page-execution.js +2 -3
  114. package/dist/server/app-page-execution.js.map +1 -1
  115. package/dist/server/app-page-head.d.ts +7 -0
  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-probe.d.ts +23 -1
  119. package/dist/server/app-page-probe.js +29 -1
  120. package/dist/server/app-page-probe.js.map +1 -1
  121. package/dist/server/app-page-render-observation.d.ts +35 -0
  122. package/dist/server/app-page-render-observation.js +68 -0
  123. package/dist/server/app-page-render-observation.js.map +1 -0
  124. package/dist/server/app-page-render.d.ts +5 -1
  125. package/dist/server/app-page-render.js +79 -3
  126. package/dist/server/app-page-render.js.map +1 -1
  127. package/dist/server/app-page-request.d.ts +1 -0
  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 +3 -1
  132. package/dist/server/app-page-route-wiring.js +42 -14
  133. package/dist/server/app-page-route-wiring.js.map +1 -1
  134. package/dist/server/app-page-stream.d.ts +2 -0
  135. package/dist/server/app-page-stream.js +1 -0
  136. package/dist/server/app-page-stream.js.map +1 -1
  137. package/dist/server/app-router-entry.js +1 -13
  138. package/dist/server/app-router-entry.js.map +1 -1
  139. package/dist/server/app-rsc-cache-busting.d.ts +19 -1
  140. package/dist/server/app-rsc-cache-busting.js +36 -1
  141. package/dist/server/app-rsc-cache-busting.js.map +1 -1
  142. package/dist/server/app-rsc-embedded-chunks.d.ts +9 -0
  143. package/dist/server/app-rsc-embedded-chunks.js +34 -0
  144. package/dist/server/app-rsc-embedded-chunks.js.map +1 -0
  145. package/dist/server/app-rsc-errors.d.ts +4 -1
  146. package/dist/server/app-rsc-errors.js +1 -1
  147. package/dist/server/app-rsc-errors.js.map +1 -1
  148. package/dist/server/app-rsc-handler.d.ts +12 -4
  149. package/dist/server/app-rsc-handler.js +6 -1
  150. package/dist/server/app-rsc-handler.js.map +1 -1
  151. package/dist/server/app-rsc-route-matching.d.ts +23 -0
  152. package/dist/server/app-rsc-route-matching.js +45 -23
  153. package/dist/server/app-rsc-route-matching.js.map +1 -1
  154. package/dist/server/app-server-action-execution.d.ts +35 -3
  155. package/dist/server/app-server-action-execution.js +87 -33
  156. package/dist/server/app-server-action-execution.js.map +1 -1
  157. package/dist/server/app-ssr-entry.d.ts +1 -0
  158. package/dist/server/app-ssr-entry.js +37 -13
  159. package/dist/server/app-ssr-entry.js.map +1 -1
  160. package/dist/server/app-ssr-error-meta.d.ts +14 -0
  161. package/dist/server/app-ssr-error-meta.js +50 -0
  162. package/dist/server/app-ssr-error-meta.js.map +1 -0
  163. package/dist/server/app-ssr-stream.d.ts +1 -1
  164. package/dist/server/app-ssr-stream.js +9 -12
  165. package/dist/server/app-ssr-stream.js.map +1 -1
  166. package/dist/server/artifact-compatibility.d.ts +12 -2
  167. package/dist/server/artifact-compatibility.js +12 -8
  168. package/dist/server/artifact-compatibility.js.map +1 -1
  169. package/dist/server/cache-proof.d.ts +124 -5
  170. package/dist/server/cache-proof.js +416 -18
  171. package/dist/server/cache-proof.js.map +1 -1
  172. package/dist/server/dev-lockfile.d.ts +110 -0
  173. package/dist/server/dev-lockfile.js +180 -0
  174. package/dist/server/dev-lockfile.js.map +1 -0
  175. package/dist/server/dev-server.js +15 -5
  176. package/dist/server/dev-server.js.map +1 -1
  177. package/dist/server/file-based-metadata.d.ts +13 -0
  178. package/dist/server/file-based-metadata.js +49 -2
  179. package/dist/server/file-based-metadata.js.map +1 -1
  180. package/dist/server/headers.d.ts +3 -1
  181. package/dist/server/headers.js +5 -2
  182. package/dist/server/headers.js.map +1 -1
  183. package/dist/server/html.js +1 -1
  184. package/dist/server/html.js.map +1 -1
  185. package/dist/server/http-error-responses.d.ts +10 -0
  186. package/dist/server/http-error-responses.js +11 -1
  187. package/dist/server/http-error-responses.js.map +1 -1
  188. package/dist/server/isr-cache.d.ts +2 -1
  189. package/dist/server/isr-cache.js +4 -2
  190. package/dist/server/isr-cache.js.map +1 -1
  191. package/dist/server/metadata-route-response.js +22 -5
  192. package/dist/server/metadata-route-response.js.map +1 -1
  193. package/dist/server/metadata-routes.js +27 -8
  194. package/dist/server/metadata-routes.js.map +1 -1
  195. package/dist/server/middleware-runtime.js +1 -0
  196. package/dist/server/middleware-runtime.js.map +1 -1
  197. package/dist/server/middleware.d.ts +12 -0
  198. package/dist/server/middleware.js +12 -0
  199. package/dist/server/middleware.js.map +1 -1
  200. package/dist/server/navigation-planner.d.ts +19 -5
  201. package/dist/server/navigation-planner.js +278 -17
  202. package/dist/server/navigation-planner.js.map +1 -1
  203. package/dist/server/navigation-trace.d.ts +8 -1
  204. package/dist/server/navigation-trace.js +7 -0
  205. package/dist/server/navigation-trace.js.map +1 -1
  206. package/dist/server/normalize-path.d.ts +2 -1
  207. package/dist/server/normalize-path.js +4 -1
  208. package/dist/server/normalize-path.js.map +1 -1
  209. package/dist/server/pages-api-route.js +1 -0
  210. package/dist/server/pages-api-route.js.map +1 -1
  211. package/dist/server/pages-page-data.d.ts +3 -2
  212. package/dist/server/pages-page-data.js +22 -3
  213. package/dist/server/pages-page-data.js.map +1 -1
  214. package/dist/server/pages-page-response.js +1 -1
  215. package/dist/server/prod-server.d.ts +28 -1
  216. package/dist/server/prod-server.js +62 -9
  217. package/dist/server/prod-server.js.map +1 -1
  218. package/dist/server/server-action-not-found.d.ts +16 -3
  219. package/dist/server/server-action-not-found.js +19 -1
  220. package/dist/server/server-action-not-found.js.map +1 -1
  221. package/dist/server/server-globals.d.ts +5 -0
  222. package/dist/server/server-globals.js +37 -0
  223. package/dist/server/server-globals.js.map +1 -0
  224. package/dist/server/static-file-cache.js +1 -1
  225. package/dist/server/static-file-cache.js.map +1 -1
  226. package/dist/shims/cache-runtime.d.ts +19 -2
  227. package/dist/shims/cache-runtime.js +67 -11
  228. package/dist/shims/cache-runtime.js.map +1 -1
  229. package/dist/shims/cache.d.ts +5 -18
  230. package/dist/shims/cache.js +2 -0
  231. package/dist/shims/cache.js.map +1 -1
  232. package/dist/shims/error-boundary.js +6 -8
  233. package/dist/shims/error-boundary.js.map +1 -1
  234. package/dist/shims/error.d.ts +18 -1
  235. package/dist/shims/error.js +56 -1
  236. package/dist/shims/error.js.map +1 -1
  237. package/dist/shims/fetch-cache.d.ts +4 -1
  238. package/dist/shims/fetch-cache.js +40 -5
  239. package/dist/shims/fetch-cache.js.map +1 -1
  240. package/dist/shims/font-google-base.d.ts +22 -8
  241. package/dist/shims/font-google-base.js +41 -71
  242. package/dist/shims/font-google-base.js.map +1 -1
  243. package/dist/shims/font-local.d.ts +3 -20
  244. package/dist/shims/font-local.js +23 -75
  245. package/dist/shims/font-local.js.map +1 -1
  246. package/dist/shims/font-utils.d.ts +51 -0
  247. package/dist/shims/font-utils.js +97 -0
  248. package/dist/shims/font-utils.js.map +1 -0
  249. package/dist/shims/hash-scroll.d.ts +7 -0
  250. package/dist/shims/hash-scroll.js +30 -0
  251. package/dist/shims/hash-scroll.js.map +1 -0
  252. package/dist/shims/headers.d.ts +8 -11
  253. package/dist/shims/headers.js +22 -2
  254. package/dist/shims/headers.js.map +1 -1
  255. package/dist/shims/image.d.ts +1 -0
  256. package/dist/shims/image.js +144 -78
  257. package/dist/shims/image.js.map +1 -1
  258. package/dist/shims/internal/app-router-context.d.ts +6 -6
  259. package/dist/shims/internal/app-router-context.js +17 -6
  260. package/dist/shims/internal/app-router-context.js.map +1 -1
  261. package/dist/shims/link-prefetch.d.ts +9 -1
  262. package/dist/shims/link-prefetch.js +11 -6
  263. package/dist/shims/link-prefetch.js.map +1 -1
  264. package/dist/shims/link.d.ts +12 -2
  265. package/dist/shims/link.js +78 -32
  266. package/dist/shims/link.js.map +1 -1
  267. package/dist/shims/metadata.d.ts +16 -30
  268. package/dist/shims/metadata.js +87 -28
  269. package/dist/shims/metadata.js.map +1 -1
  270. package/dist/shims/navigation.d.ts +158 -17
  271. package/dist/shims/navigation.js +324 -74
  272. package/dist/shims/navigation.js.map +1 -1
  273. package/dist/shims/navigation.react-server.d.ts +3 -2
  274. package/dist/shims/navigation.react-server.js +5 -2
  275. package/dist/shims/navigation.react-server.js.map +1 -1
  276. package/dist/shims/pages-router-runtime.d.ts +7 -0
  277. package/dist/shims/pages-router-runtime.js +16 -0
  278. package/dist/shims/pages-router-runtime.js.map +1 -0
  279. package/dist/shims/router.d.ts +32 -6
  280. package/dist/shims/router.js +197 -242
  281. package/dist/shims/router.js.map +1 -1
  282. package/dist/shims/script.js +110 -32
  283. package/dist/shims/script.js.map +1 -1
  284. package/dist/shims/server.js +2 -1
  285. package/dist/shims/server.js.map +1 -1
  286. package/dist/shims/slot.d.ts +1 -0
  287. package/dist/shims/slot.js +41 -1
  288. package/dist/shims/slot.js.map +1 -1
  289. package/dist/shims/unified-request-context.js +2 -0
  290. package/dist/shims/unified-request-context.js.map +1 -1
  291. package/dist/shims/unrecognized-action-error.d.ts +35 -0
  292. package/dist/shims/unrecognized-action-error.js +41 -0
  293. package/dist/shims/unrecognized-action-error.js.map +1 -0
  294. package/dist/shims/url-utils.d.ts +21 -1
  295. package/dist/shims/url-utils.js +67 -3
  296. package/dist/shims/url-utils.js.map +1 -1
  297. package/dist/utils/asset-prefix.d.ts +69 -0
  298. package/dist/utils/asset-prefix.js +91 -0
  299. package/dist/utils/asset-prefix.js.map +1 -0
  300. package/dist/utils/base-path.d.ts +7 -1
  301. package/dist/utils/base-path.js +10 -1
  302. package/dist/utils/base-path.js.map +1 -1
  303. package/dist/utils/navigation-signal.d.ts +1 -2
  304. package/dist/utils/navigation-signal.js +1 -1
  305. package/dist/utils/navigation-signal.js.map +1 -1
  306. package/dist/utils/sorted-array.d.ts +9 -0
  307. package/dist/utils/sorted-array.js +22 -0
  308. package/dist/utils/sorted-array.js.map +1 -0
  309. package/package.json +3 -3
@@ -1,13 +1,20 @@
1
+ import { normalizePathnameForRouteMatch } from "../routing/utils.js";
2
+ import { matchRoutePattern, matchRoutePatternPrefix } from "../routing/route-pattern.js";
3
+ import { compareAppElementsSlotIds } from "./app-elements-wire.js";
4
+ import "./app-elements.js";
1
5
  import { NavigationTraceReasonCodes, createNavigationLifecycleTraceFields, createNavigationTrace } from "./navigation-trace.js";
2
6
  //#region src/server/navigation-planner.ts
7
+ const ROUTE_INTERCEPTION_CONTEXT_SEPARATOR = "\0";
3
8
  function createRequestWorkDecision(options) {
9
+ const traverseFields = options.work.kind === "traverseFlight" ? { traverseDirection: options.work.direction } : {};
4
10
  return {
5
11
  kind: "requestWork",
6
12
  token: options.state.nextOperationToken,
7
13
  work: options.work,
8
14
  trace: createNavigationTrace(NavigationTraceReasonCodes.requestWork, {
9
15
  eventKind: options.eventKind,
10
- targetHref: getRequestedWorkTargetHref(options.work)
16
+ targetHref: getRequestedWorkTargetHref(options.work),
17
+ ...traverseFields
11
18
  })
12
19
  };
13
20
  }
@@ -19,11 +26,111 @@ function getRequestedWorkTargetHref(work) {
19
26
  default: throw new Error("[vinext] Unknown requested navigation work: " + String(work));
20
27
  }
21
28
  }
29
+ function createSnapshotRouteTopology(snapshot) {
30
+ return {
31
+ layoutIds: snapshot.layoutIds,
32
+ rootBoundaryId: snapshot.rootBoundaryId,
33
+ rootLayoutTreePath: snapshot.rootBoundaryId,
34
+ slotBindings: snapshot.slotBindings
35
+ };
36
+ }
37
+ function stripInterceptionContextFromRouteId(routeId) {
38
+ const separatorIndex = routeId.indexOf(ROUTE_INTERCEPTION_CONTEXT_SEPARATOR);
39
+ return separatorIndex === -1 ? routeId : routeId.slice(0, separatorIndex);
40
+ }
41
+ function getMatchedUrlPathname(matchedUrl) {
42
+ try {
43
+ return new URL(matchedUrl, "https://vinext.local").pathname;
44
+ } catch {
45
+ const [withoutHash = ""] = matchedUrl.split("#");
46
+ const [pathname = ""] = withoutHash.split("?");
47
+ return pathname === "" ? "/" : pathname;
48
+ }
49
+ }
50
+ function splitMatchedUrlIntoRouteParts(matchedUrl) {
51
+ return normalizePathnameForRouteMatch(getMatchedUrlPathname(matchedUrl)).split("/").filter((part) => part.length > 0);
52
+ }
53
+ function findRouteManifestRouteByMatchedUrl(routeManifest, matchedUrl) {
54
+ const urlParts = splitMatchedUrlIntoRouteParts(matchedUrl);
55
+ for (const route of routeManifest.segmentGraph.routes.values()) if (matchRoutePattern(urlParts, route.patternParts) !== null) return route;
56
+ return null;
57
+ }
58
+ function routeManifestRouteMatchesUrl(route, matchedUrl) {
59
+ return matchRoutePattern(splitMatchedUrlIntoRouteParts(matchedUrl), route.patternParts) !== null;
60
+ }
61
+ function findRouteManifestRouteByIdOrMatchedUrl(options) {
62
+ const routeId = stripInterceptionContextFromRouteId(options.routeId);
63
+ const route = options.routeManifest.segmentGraph.routes.get(routeId);
64
+ if (route && routeManifestRouteMatchesUrl(route, options.matchedUrl)) return route;
65
+ return findRouteManifestRouteByMatchedUrl(options.routeManifest, options.matchedUrl);
66
+ }
67
+ function findRouteManifestRouteForSnapshot(routeManifest, snapshot) {
68
+ if (snapshot.interception !== null) return findRouteManifestRouteByIdOrMatchedUrl({
69
+ matchedUrl: snapshot.interception.sourceMatchedUrl,
70
+ routeId: snapshot.interception.sourceRouteId,
71
+ routeManifest
72
+ });
73
+ return findRouteManifestRouteByIdOrMatchedUrl({
74
+ matchedUrl: snapshot.matchedUrl,
75
+ routeId: snapshot.routeId,
76
+ routeManifest
77
+ });
78
+ }
79
+ function resolveRouteManifestSlotBindings(routeManifest, route) {
80
+ const bindings = [];
81
+ for (const slotId of route.slotIds) {
82
+ const binding = routeManifest.segmentGraph.slotBindings.get(`${route.id}::${slotId}`);
83
+ if (!binding) continue;
84
+ bindings.push({
85
+ ownerLayoutId: binding.ownerLayoutId,
86
+ slotId: binding.slotId,
87
+ state: binding.state
88
+ });
89
+ }
90
+ return bindings.sort((left, right) => compareAppElementsSlotIds(left.slotId, right.slotId));
91
+ }
92
+ function resolveRouteManifestRootLayoutTreePath(routeManifest, route) {
93
+ if (route.rootBoundaryId === null) return null;
94
+ return routeManifest.segmentGraph.rootBoundaries.get(route.rootBoundaryId)?.treePath ?? null;
95
+ }
96
+ function resolveRouteTopologySnapshot(options) {
97
+ const route = options.routeManifest === null ? null : findRouteManifestRouteForSnapshot(options.routeManifest, options.snapshot);
98
+ if (route === null || options.routeManifest === null) return createSnapshotRouteTopology(options.snapshot);
99
+ const shouldUseManifestSlotBindings = options.slotBindingSource === "manifestTarget" && options.snapshot.interception === null;
100
+ return {
101
+ layoutIds: route.layoutIds,
102
+ rootBoundaryId: route.rootBoundaryId,
103
+ rootLayoutTreePath: resolveRouteManifestRootLayoutTreePath(options.routeManifest, route),
104
+ slotBindings: shouldUseManifestSlotBindings ? resolveRouteManifestSlotBindings(options.routeManifest, route) : options.snapshot.slotBindings
105
+ };
106
+ }
107
+ function findRouteManifestInterceptionForProof(routeManifest, proof) {
108
+ const sourceParts = splitMatchedUrlIntoRouteParts(proof.sourceMatchedUrl);
109
+ const targetParts = splitMatchedUrlIntoRouteParts(proof.targetMatchedUrl);
110
+ const targetRoute = findRouteManifestRouteByIdOrMatchedUrl({
111
+ matchedUrl: proof.targetMatchedUrl,
112
+ routeId: proof.targetRouteId,
113
+ routeManifest
114
+ });
115
+ const candidateInterceptions = routeManifest.segmentGraph.interceptionsBySlotId.get(proof.slotId) ?? [];
116
+ for (const interception of candidateInterceptions) {
117
+ if (!matchRoutePatternPrefix(sourceParts, interception.sourcePatternParts)) continue;
118
+ if (matchRoutePattern(targetParts, interception.targetPatternParts) === null) continue;
119
+ if (interception.targetRouteId !== null && targetRoute?.id !== interception.targetRouteId) continue;
120
+ return interception;
121
+ }
122
+ return null;
123
+ }
22
124
  function createRootBoundaryTraceFields(options) {
23
- return options.state.traceFields ?? createNavigationLifecycleTraceFields({
24
- currentRootLayoutTreePath: options.state.visibleSnapshot.rootBoundaryId,
125
+ if (options.state.traceFields) return {
126
+ ...options.state.traceFields,
127
+ currentRootLayoutTreePath: options.currentRootLayoutTreePath,
128
+ nextRootLayoutTreePath: options.nextRootLayoutTreePath
129
+ };
130
+ return createNavigationLifecycleTraceFields({
131
+ currentRootLayoutTreePath: options.currentRootLayoutTreePath,
25
132
  currentVisibleCommitVersion: options.state.visibleCommitVersion,
26
- nextRootLayoutTreePath: options.event.result.targetSnapshot.rootBoundaryId,
133
+ nextRootLayoutTreePath: options.nextRootLayoutTreePath,
27
134
  startedVisibleCommitVersion: options.event.token.baseVisibleCommitVersion
28
135
  });
29
136
  }
@@ -32,12 +139,15 @@ function classifyRootBoundaryTransition(currentRootBoundaryId, nextRootBoundaryI
32
139
  return currentRootBoundaryId === nextRootBoundaryId ? "currentRootBoundary" : "rootBoundaryChanged";
33
140
  }
34
141
  function resolveSameLayoutAncestorPersistence(currentSnapshot, targetSnapshot) {
35
- if (classifyRootBoundaryTransition(currentSnapshot.rootBoundaryId, targetSnapshot.rootBoundaryId) !== "currentRootBoundary") return [];
142
+ return resolveSameLayoutAncestorPersistenceForTopologies(createSnapshotRouteTopology(currentSnapshot), createSnapshotRouteTopology(targetSnapshot));
143
+ }
144
+ function resolveSameLayoutAncestorPersistenceForTopologies(currentTopology, targetTopology) {
145
+ if (classifyRootBoundaryTransition(currentTopology.rootBoundaryId, targetTopology.rootBoundaryId) !== "currentRootBoundary") return [];
36
146
  const commonLayoutIds = [];
37
- const maxLength = Math.min(currentSnapshot.layoutIds.length, targetSnapshot.layoutIds.length);
147
+ const maxLength = Math.min(currentTopology.layoutIds.length, targetTopology.layoutIds.length);
38
148
  for (let index = 0; index < maxLength; index++) {
39
- const layoutId = currentSnapshot.layoutIds[index];
40
- if (layoutId !== targetSnapshot.layoutIds[index]) break;
149
+ const layoutId = currentTopology.layoutIds[index];
150
+ if (layoutId !== targetTopology.layoutIds[index]) break;
41
151
  commonLayoutIds.push(layoutId);
42
152
  }
43
153
  return commonLayoutIds;
@@ -64,19 +174,163 @@ function resolveCurrentRootBoundaryElementPersistence(currentSnapshot, targetSna
64
174
  return [...preservedLayoutIds, ...resolveMountedParallelSlotPersistenceForLayouts(currentSnapshot, preservedLayoutIds)];
65
175
  }
66
176
  function resolveCurrentRootBoundaryCommitElementPersistence(options) {
67
- const preservedLayoutIds = resolveSameLayoutAncestorPersistence(options.currentSnapshot, options.targetSnapshot);
68
- if (options.lane === "traverse") return preservedLayoutIds;
69
- return [...preservedLayoutIds, ...resolveMountedParallelSlotPersistenceForLayouts(options.currentSnapshot, preservedLayoutIds)];
177
+ return resolveSameLayoutAncestorPersistenceForTopologies(options.currentTopology, options.targetTopology);
178
+ }
179
+ function resolveCurrentRootBoundaryCommitSlotPersistence(options) {
180
+ if (options.lane === "traverse") return [];
181
+ const preservedLayoutIds = resolveSameLayoutAncestorPersistenceForTopologies(options.currentTopology, options.targetTopology);
182
+ if (preservedLayoutIds.length === 0) return [];
183
+ return resolveDefaultOrUnmatchedSlotPersistenceForLayouts({
184
+ currentSlotBindings: options.currentTopology.slotBindings,
185
+ preservedLayoutIds,
186
+ targetSlotBindings: options.targetTopology.slotBindings
187
+ });
188
+ }
189
+ /**
190
+ * Default/unmatched slot preservation law:
191
+ *
192
+ * A target default/unmatched slot may reuse previous content only when:
193
+ * - the slot's owner layout is part of the preserved layout ancestor set;
194
+ * - the current visible snapshot proves the same slot had renderable content;
195
+ * - the navigation is not a traversal.
196
+ *
197
+ * Wire absence and UNMATCHED_SLOT markers are not semantic proof.
198
+ */
199
+ function resolveDefaultOrUnmatchedSlotPersistenceForLayouts(options) {
200
+ const preservedLayoutIdSet = new Set(options.preservedLayoutIds);
201
+ const slotIdsWithContent = /* @__PURE__ */ new Set();
202
+ for (const binding of options.currentSlotBindings) {
203
+ if (binding.state === "unmatched") continue;
204
+ slotIdsWithContent.add(binding.slotId);
205
+ }
206
+ const preservedSlotIds = [];
207
+ const seenSlotIds = /* @__PURE__ */ new Set();
208
+ for (const binding of options.targetSlotBindings) {
209
+ if (binding.ownerLayoutId === null) continue;
210
+ if (!preservedLayoutIdSet.has(binding.ownerLayoutId)) continue;
211
+ if (binding.state === "active") continue;
212
+ if (!slotIdsWithContent.has(binding.slotId)) continue;
213
+ if (seenSlotIds.has(binding.slotId)) continue;
214
+ preservedSlotIds.push(binding.slotId);
215
+ seenSlotIds.add(binding.slotId);
216
+ }
217
+ return preservedSlotIds.sort(compareAppElementsSlotIds);
218
+ }
219
+ function getVisibleInterceptionSourceIdentity(snapshot) {
220
+ if (snapshot.interception) return {
221
+ matchedUrl: snapshot.interception.sourceMatchedUrl,
222
+ routeId: snapshot.interception.sourceRouteId
223
+ };
224
+ return {
225
+ matchedUrl: snapshot.matchedUrl,
226
+ routeId: snapshot.routeId
227
+ };
228
+ }
229
+ function createInterceptionProofRejectedDecision(options) {
230
+ return {
231
+ kind: "hardNavigate",
232
+ reason: "interceptionProofRejected",
233
+ token: options.event.token,
234
+ trace: createNavigationTrace(options.reasonCode, options.traceFields),
235
+ url: options.event.result.href
236
+ };
237
+ }
238
+ function validateInterceptedPreservation(options) {
239
+ const proof = options.targetSnapshot.interception;
240
+ if (!proof) return {
241
+ kind: "rejected",
242
+ reasonCode: NavigationTraceReasonCodes.interceptedRejectedMissingProof
243
+ };
244
+ if (proof.targetMatchedUrl !== options.targetSnapshot.matchedUrl) return {
245
+ kind: "rejected",
246
+ reasonCode: NavigationTraceReasonCodes.interceptedRejectedTargetMismatch
247
+ };
248
+ const sourceIdentity = getVisibleInterceptionSourceIdentity(options.currentSnapshot);
249
+ if (proof.sourceMatchedUrl !== sourceIdentity.matchedUrl || proof.sourceRouteId !== sourceIdentity.routeId) return {
250
+ kind: "rejected",
251
+ reasonCode: NavigationTraceReasonCodes.interceptedRejectedUnknownSource
252
+ };
253
+ const declaredInterception = options.routeManifest === null ? null : findRouteManifestInterceptionForProof(options.routeManifest, proof);
254
+ if (options.routeManifest !== null && declaredInterception === null) return {
255
+ kind: "rejected",
256
+ reasonCode: NavigationTraceReasonCodes.interceptedRejectedUndeclaredTopology
257
+ };
258
+ const preservedLayoutIds = resolveSameLayoutAncestorPersistenceForTopologies(options.currentTopology, options.targetTopology);
259
+ if (preservedLayoutIds.length === 0) return {
260
+ kind: "rejected",
261
+ reasonCode: NavigationTraceReasonCodes.interceptedRejectedIncompatibleRoot
262
+ };
263
+ const preservedLayoutIdSet = new Set(preservedLayoutIds);
264
+ const targetSlotBinding = options.targetTopology.slotBindings.find((binding) => binding.slotId === proof.slotId);
265
+ if (!targetSlotBinding || targetSlotBinding.state !== "active" || targetSlotBinding.ownerLayoutId === null || !preservedLayoutIdSet.has(targetSlotBinding.ownerLayoutId)) return {
266
+ kind: "rejected",
267
+ reasonCode: NavigationTraceReasonCodes.interceptedRejectedMissingSlotProof
268
+ };
269
+ if (declaredInterception !== null && targetSlotBinding.ownerLayoutId !== declaredInterception.ownerLayoutId) return {
270
+ kind: "rejected",
271
+ reasonCode: NavigationTraceReasonCodes.interceptedRejectedUndeclaredTopology
272
+ };
273
+ return {
274
+ kind: "approved",
275
+ preserveElementIds: preservedLayoutIds,
276
+ preservePreviousSlotIds: resolveDefaultOrUnmatchedSlotPersistenceForLayouts({
277
+ currentSlotBindings: options.currentTopology.slotBindings,
278
+ preservedLayoutIds,
279
+ targetSlotBindings: options.targetTopology.slotBindings
280
+ }).filter((slotId) => slotId !== proof.slotId)
281
+ };
70
282
  }
71
283
  function planFlightResponseArrived(options) {
72
- const traceFields = createRootBoundaryTraceFields(options);
284
+ const targetSnapshot = options.event.result.targetSnapshot;
285
+ const currentTopology = resolveRouteTopologySnapshot({
286
+ routeManifest: options.routeManifest,
287
+ slotBindingSource: "snapshot",
288
+ snapshot: options.state.visibleSnapshot
289
+ });
290
+ const targetTopology = resolveRouteTopologySnapshot({
291
+ routeManifest: options.routeManifest,
292
+ slotBindingSource: "manifestTarget",
293
+ snapshot: targetSnapshot
294
+ });
295
+ const traceFields = createRootBoundaryTraceFields({
296
+ currentRootLayoutTreePath: currentTopology.rootLayoutTreePath,
297
+ event: options.event,
298
+ nextRootLayoutTreePath: targetTopology.rootLayoutTreePath,
299
+ state: options.state
300
+ });
73
301
  if (options.event.token.lane === "prefetch") return {
74
302
  kind: "noCommit",
75
303
  reason: "prefetchOnly",
76
304
  token: options.event.token,
77
305
  trace: createNavigationTrace(NavigationTraceReasonCodes.prefetchOnly, traceFields)
78
306
  };
79
- const transition = classifyRootBoundaryTransition(options.state.visibleSnapshot.rootBoundaryId, options.event.result.targetSnapshot.rootBoundaryId);
307
+ if (targetSnapshot.interception !== null) {
308
+ const validation = validateInterceptedPreservation({
309
+ currentSnapshot: options.state.visibleSnapshot,
310
+ currentTopology,
311
+ routeManifest: options.routeManifest,
312
+ targetSnapshot,
313
+ targetTopology
314
+ });
315
+ if (validation.kind === "rejected") return createInterceptionProofRejectedDecision({
316
+ event: options.event,
317
+ reasonCode: validation.reasonCode,
318
+ traceFields
319
+ });
320
+ return {
321
+ kind: "proposeCommit",
322
+ proposal: {
323
+ preserveAbsentSlots: false,
324
+ preserveElementIds: validation.preserveElementIds,
325
+ preservePreviousSlotIds: validation.preservePreviousSlotIds,
326
+ reason: "interceptedCurrentRootBoundary",
327
+ targetSnapshot
328
+ },
329
+ token: options.event.token,
330
+ trace: createNavigationTrace(NavigationTraceReasonCodes.interceptedCommitCurrent, traceFields)
331
+ };
332
+ }
333
+ const transition = classifyRootBoundaryTransition(currentTopology.rootBoundaryId, targetTopology.rootBoundaryId);
80
334
  if (transition === "rootBoundaryChanged") return {
81
335
  kind: "hardNavigate",
82
336
  reason: "rootBoundaryChanged",
@@ -89,8 +343,9 @@ function planFlightResponseArrived(options) {
89
343
  proposal: {
90
344
  preserveAbsentSlots: true,
91
345
  preserveElementIds: [],
346
+ preservePreviousSlotIds: [],
92
347
  reason: "rootBoundaryUnknownFallback",
93
- targetSnapshot: options.event.result.targetSnapshot
348
+ targetSnapshot
94
349
  },
95
350
  token: options.event.token,
96
351
  trace: createNavigationTrace(NavigationTraceReasonCodes.rootBoundaryUnknown, traceFields)
@@ -100,12 +355,17 @@ function planFlightResponseArrived(options) {
100
355
  proposal: {
101
356
  preserveAbsentSlots: false,
102
357
  preserveElementIds: resolveCurrentRootBoundaryCommitElementPersistence({
103
- currentSnapshot: options.state.visibleSnapshot,
358
+ currentTopology,
359
+ lane: options.event.token.lane,
360
+ targetTopology
361
+ }),
362
+ preservePreviousSlotIds: resolveCurrentRootBoundaryCommitSlotPersistence({
363
+ currentTopology,
104
364
  lane: options.event.token.lane,
105
- targetSnapshot: options.event.result.targetSnapshot
365
+ targetTopology
106
366
  }),
107
367
  reason: "currentRootBoundary",
108
- targetSnapshot: options.event.result.targetSnapshot
368
+ targetSnapshot
109
369
  },
110
370
  token: options.event.token,
111
371
  trace: createNavigationTrace(NavigationTraceReasonCodes.commitCurrent, traceFields)
@@ -150,6 +410,7 @@ function planNavigation(input) {
150
410
  });
151
411
  case "flightResponseArrived": return planFlightResponseArrived({
152
412
  event: input.event,
413
+ routeManifest: input.routeManifest,
153
414
  state: input.state
154
415
  });
155
416
  default: {
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-planner.js","names":["_exhaustive"],"sources":["../../src/server/navigation-planner.ts"],"sourcesContent":["import type { RouteManifest } from \"../routing/app-route-graph.js\";\nimport {\n NavigationTraceReasonCodes,\n createNavigationLifecycleTraceFields,\n createNavigationTrace,\n type NavigationTrace,\n type NavigationTraceFields,\n} from \"./navigation-trace.js\";\n\nexport type OperationLane =\n | \"hmr\"\n | \"navigation\"\n | \"prefetch\"\n | \"refresh\"\n | \"server-action\"\n | \"traverse\";\n\nexport type OperationToken = {\n operationId: number;\n lane: OperationLane;\n baseVisibleCommitVersion: number;\n graphVersion: string | null;\n deploymentVersion: string | null;\n targetSnapshotFingerprint: string;\n cacheVariantFingerprint?: string;\n};\n\nexport type RouteSnapshotV0 = {\n routeId: string;\n // Ordered ancestor-first, with the root layout at index 0. Same-layout\n // persistence uses prefix comparison, so callers must preserve this order.\n layoutIds: readonly string[];\n mountedParallelSlots: readonly MountedParallelSlotSnapshotV0[];\n rootBoundaryId: string | null;\n displayUrl: string;\n matchedUrl: string;\n};\n\nexport type MountedParallelSlotSnapshotV0 = {\n slotId: string;\n ownerLayoutId: string | null;\n};\n\nexport type NavigationPlannerStateV0 = {\n // V0 keeps a single state shape so intent events and result events can move\n // through one planner surface. flightResponseArrived uses event.token; later\n // #726 slices can split this by event kind once more result paths are routed\n // through the planner.\n nextOperationToken: OperationToken;\n // Callers that have lifecycle authority should pass the complete trace\n // context. When absent, the planner emits the stable root-boundary facts it\n // can derive from the event and visible snapshot.\n traceFields?: NavigationTraceFields;\n visibleCommitVersion: number;\n visibleSnapshot: RouteSnapshotV0;\n};\n\nexport type RefreshScope = \"visible\";\n\nexport type NavigationEvent =\n | { kind: \"navigate\"; href: string; mode: \"push\" | \"replace\" }\n | { kind: \"refresh\"; scope: RefreshScope }\n | { kind: \"traverse\"; direction: \"back\" | \"forward\"; historyState: unknown }\n | { kind: \"prefetch\"; href: string }\n | { kind: \"flightResponseArrived\"; token: OperationToken; result: FlightResultV0 };\n\nexport type RequestedWork =\n | { kind: \"flight\"; href: string; mode: \"push\" | \"replace\" | \"refresh\" }\n | { direction: \"back\" | \"forward\"; historyState: unknown; kind: \"traverseFlight\" }\n | { kind: \"prefetch\"; href: string };\n\nexport type CommitProposal = {\n preserveAbsentSlots: boolean;\n preserveElementIds: readonly string[];\n reason: \"currentRootBoundary\" | \"rootBoundaryUnknownFallback\";\n targetSnapshot: RouteSnapshotV0;\n};\n\nexport type NoCommitReason = \"prefetchOnly\";\nexport type HardNavigationReason = \"rootBoundaryChanged\";\nexport type RootBoundaryTransition =\n | \"currentRootBoundary\"\n | \"rootBoundaryChanged\"\n | \"rootBoundaryUnknownFallback\";\n\nexport type NavigationDecisionV0 =\n | {\n kind: \"requestWork\";\n token: OperationToken;\n work: RequestedWork;\n trace: NavigationTrace;\n }\n | {\n kind: \"proposeCommit\";\n token: OperationToken;\n proposal: CommitProposal;\n trace: NavigationTrace;\n }\n | {\n kind: \"noCommit\";\n token: OperationToken;\n reason: NoCommitReason;\n trace: NavigationTrace;\n }\n | {\n kind: \"hardNavigate\";\n token: OperationToken;\n url: string;\n reason: HardNavigationReason;\n trace: NavigationTrace;\n };\n\nexport type FlightResultV0 = {\n href: string;\n targetSnapshot: RouteSnapshotV0;\n};\n\nexport type NavigationPlannerInput = {\n // Reserved for #726-CORE-09 route-graph-aware planning. CORE-07/08 only\n // routes the existing root-boundary decision through the planner, so browser\n // callers pass null until route topology becomes part of the decision input.\n routeManifest: RouteManifest | null;\n state: NavigationPlannerStateV0;\n event: NavigationEvent;\n};\n\nfunction createRequestWorkDecision(options: {\n eventKind: NavigationEvent[\"kind\"];\n state: NavigationPlannerStateV0;\n work: RequestedWork;\n}): NavigationDecisionV0 {\n return {\n kind: \"requestWork\",\n token: options.state.nextOperationToken,\n work: options.work,\n trace: createNavigationTrace(NavigationTraceReasonCodes.requestWork, {\n eventKind: options.eventKind,\n targetHref: getRequestedWorkTargetHref(options.work),\n }),\n };\n}\n\nfunction getRequestedWorkTargetHref(work: RequestedWork): string | null {\n switch (work.kind) {\n case \"flight\":\n case \"prefetch\":\n return work.href;\n case \"traverseFlight\":\n return null;\n default: {\n const _exhaustive: never = work;\n throw new Error(\"[vinext] Unknown requested navigation work: \" + String(_exhaustive));\n }\n }\n}\n\nfunction createRootBoundaryTraceFields(options: {\n event: Extract<NavigationEvent, { kind: \"flightResponseArrived\" }>;\n state: NavigationPlannerStateV0;\n}): NavigationTraceFields {\n // Browser commit approval supplies lifecycle trace context before calling\n // the planner. This fallback exists for pure planner callers and tests; it\n // intentionally cannot invent lifecycle-only fields such as active nav id.\n return (\n options.state.traceFields ??\n createNavigationLifecycleTraceFields({\n currentRootLayoutTreePath: options.state.visibleSnapshot.rootBoundaryId,\n currentVisibleCommitVersion: options.state.visibleCommitVersion,\n nextRootLayoutTreePath: options.event.result.targetSnapshot.rootBoundaryId,\n startedVisibleCommitVersion: options.event.token.baseVisibleCommitVersion,\n })\n );\n}\n\nfunction classifyRootBoundaryTransition(\n currentRootBoundaryId: string | null,\n nextRootBoundaryId: string | null,\n): RootBoundaryTransition {\n if (currentRootBoundaryId === null || nextRootBoundaryId === null) {\n // Both null directions intentionally share the v0 fallback because this\n // slice only knows boundary identity from the current flight payload.\n // #726-CORE-09 can split \"unknown current\" from \"unknown target\" once the\n // planner consumes graph-owned root boundary facts for both sides.\n return \"rootBoundaryUnknownFallback\";\n }\n\n return currentRootBoundaryId === nextRootBoundaryId\n ? \"currentRootBoundary\"\n : \"rootBoundaryChanged\";\n}\n\nfunction resolveSameLayoutAncestorPersistence(\n currentSnapshot: RouteSnapshotV0,\n targetSnapshot: RouteSnapshotV0,\n): readonly string[] {\n if (\n classifyRootBoundaryTransition(\n currentSnapshot.rootBoundaryId,\n targetSnapshot.rootBoundaryId,\n ) !== \"currentRootBoundary\"\n ) {\n return [];\n }\n\n const commonLayoutIds: string[] = [];\n const maxLength = Math.min(currentSnapshot.layoutIds.length, targetSnapshot.layoutIds.length);\n for (let index = 0; index < maxLength; index++) {\n const layoutId = currentSnapshot.layoutIds[index];\n if (layoutId !== targetSnapshot.layoutIds[index]) break;\n commonLayoutIds.push(layoutId);\n }\n return commonLayoutIds;\n}\n\nfunction resolveMountedParallelSlotPersistence(\n currentSnapshot: RouteSnapshotV0,\n targetSnapshot: RouteSnapshotV0,\n): readonly string[] {\n const preservedLayoutIds = resolveSameLayoutAncestorPersistence(currentSnapshot, targetSnapshot);\n return resolveMountedParallelSlotPersistenceForLayouts(currentSnapshot, preservedLayoutIds);\n}\n\nfunction resolveMountedParallelSlotPersistenceForLayouts(\n currentSnapshot: RouteSnapshotV0,\n preservedLayoutIds: readonly string[],\n): readonly string[] {\n if (preservedLayoutIds.length === 0) return [];\n const preservedLayoutIdSet = new Set(preservedLayoutIds);\n\n const preservedSlotIds: string[] = [];\n const seenSlotIds = new Set<string>();\n for (const slot of currentSnapshot.mountedParallelSlots) {\n if (slot.ownerLayoutId === null) continue;\n if (!preservedLayoutIdSet.has(slot.ownerLayoutId)) continue;\n if (seenSlotIds.has(slot.slotId)) continue;\n\n preservedSlotIds.push(slot.slotId);\n seenSlotIds.add(slot.slotId);\n }\n return preservedSlotIds;\n}\n\nfunction resolveCurrentRootBoundaryElementPersistence(\n currentSnapshot: RouteSnapshotV0,\n targetSnapshot: RouteSnapshotV0,\n): readonly string[] {\n const preservedLayoutIds = resolveSameLayoutAncestorPersistence(currentSnapshot, targetSnapshot);\n return [\n ...preservedLayoutIds,\n ...resolveMountedParallelSlotPersistenceForLayouts(currentSnapshot, preservedLayoutIds),\n ];\n}\n\nfunction resolveCurrentRootBoundaryCommitElementPersistence(options: {\n currentSnapshot: RouteSnapshotV0;\n lane: OperationLane;\n targetSnapshot: RouteSnapshotV0;\n}): readonly string[] {\n const preservedLayoutIds = resolveSameLayoutAncestorPersistence(\n options.currentSnapshot,\n options.targetSnapshot,\n );\n\n if (options.lane === \"traverse\") {\n return preservedLayoutIds;\n }\n\n return [\n ...preservedLayoutIds,\n ...resolveMountedParallelSlotPersistenceForLayouts(options.currentSnapshot, preservedLayoutIds),\n ];\n}\n\nfunction planFlightResponseArrived(options: {\n event: Extract<NavigationEvent, { kind: \"flightResponseArrived\" }>;\n state: NavigationPlannerStateV0;\n}): NavigationDecisionV0 {\n const traceFields = createRootBoundaryTraceFields(options);\n\n if (options.event.token.lane === \"prefetch\") {\n return {\n kind: \"noCommit\",\n reason: \"prefetchOnly\",\n token: options.event.token,\n trace: createNavigationTrace(NavigationTraceReasonCodes.prefetchOnly, traceFields),\n };\n }\n\n const transition = classifyRootBoundaryTransition(\n options.state.visibleSnapshot.rootBoundaryId,\n options.event.result.targetSnapshot.rootBoundaryId,\n );\n\n if (transition === \"rootBoundaryChanged\") {\n return {\n kind: \"hardNavigate\",\n reason: \"rootBoundaryChanged\",\n token: options.event.token,\n trace: createNavigationTrace(NavigationTraceReasonCodes.rootBoundaryChanged, traceFields),\n url: options.event.result.href,\n };\n }\n\n if (transition === \"rootBoundaryUnknownFallback\") {\n // Unknown root identity is an uncertainty fallback, not evidence that\n // reuse is safe. #726-CORE-09 can delete the legacy soft-commit writer\n // once every promoted caller supplies graph-owned root boundary IDs from\n // the route graph read model documented in routing/app-router.ts.\n return {\n kind: \"proposeCommit\",\n proposal: {\n preserveAbsentSlots: true,\n preserveElementIds: [],\n reason: \"rootBoundaryUnknownFallback\",\n targetSnapshot: options.event.result.targetSnapshot,\n },\n token: options.event.token,\n trace: createNavigationTrace(NavigationTraceReasonCodes.rootBoundaryUnknown, traceFields),\n };\n }\n\n return {\n kind: \"proposeCommit\",\n proposal: {\n preserveAbsentSlots: false,\n preserveElementIds: resolveCurrentRootBoundaryCommitElementPersistence({\n currentSnapshot: options.state.visibleSnapshot,\n lane: options.event.token.lane,\n targetSnapshot: options.event.result.targetSnapshot,\n }),\n reason: \"currentRootBoundary\",\n targetSnapshot: options.event.result.targetSnapshot,\n },\n token: options.event.token,\n trace: createNavigationTrace(NavigationTraceReasonCodes.commitCurrent, traceFields),\n };\n}\n\nfunction planNavigation(input: NavigationPlannerInput): NavigationDecisionV0 {\n switch (input.event.kind) {\n case \"navigate\":\n return createRequestWorkDecision({\n eventKind: input.event.kind,\n state: input.state,\n work: {\n href: input.event.href,\n kind: \"flight\",\n mode: input.event.mode,\n },\n });\n case \"refresh\":\n return createRequestWorkDecision({\n eventKind: input.event.kind,\n state: input.state,\n work: {\n href: input.state.visibleSnapshot.displayUrl,\n kind: \"flight\",\n mode: \"refresh\",\n },\n });\n case \"traverse\":\n return createRequestWorkDecision({\n eventKind: input.event.kind,\n state: input.state,\n work: {\n direction: input.event.direction,\n historyState: input.event.historyState,\n kind: \"traverseFlight\",\n },\n });\n case \"prefetch\":\n return createRequestWorkDecision({\n eventKind: input.event.kind,\n state: input.state,\n work: {\n href: input.event.href,\n kind: \"prefetch\",\n },\n });\n case \"flightResponseArrived\":\n return planFlightResponseArrived({\n event: input.event,\n state: input.state,\n });\n default: {\n const _exhaustive: never = input.event;\n throw new Error(\"[vinext] Unknown navigation event: \" + String(_exhaustive));\n }\n }\n}\n\nexport const navigationPlanner = {\n classifyRootBoundaryTransition,\n plan: planNavigation,\n resolveCurrentRootBoundaryElementPersistence,\n resolveMountedParallelSlotPersistence,\n resolveSameLayoutAncestorPersistence,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"navigation-planner.js","names":["_exhaustive"],"sources":["../../src/server/navigation-planner.ts"],"sourcesContent":["import { matchRoutePattern, matchRoutePatternPrefix } from \"../routing/route-pattern.js\";\nimport { normalizePathnameForRouteMatch } from \"../routing/utils.js\";\nimport type {\n RouteManifest,\n RouteManifestInterception,\n RouteManifestRoute,\n} from \"../routing/app-route-graph.js\";\nimport { compareAppElementsSlotIds, type AppElementsSlotBinding } from \"./app-elements.js\";\nimport {\n NavigationTraceReasonCodes,\n createNavigationLifecycleTraceFields,\n createNavigationTrace,\n type NavigationTrace,\n type NavigationTraceFields,\n type NavigationTraceReasonCode,\n} from \"./navigation-trace.js\";\n\nexport type OperationLane =\n | \"hmr\"\n | \"navigation\"\n | \"prefetch\"\n | \"refresh\"\n | \"server-action\"\n | \"traverse\";\n\nexport type OperationToken = {\n operationId: number;\n lane: OperationLane;\n baseVisibleCommitVersion: number;\n graphVersion: string | null;\n deploymentVersion: string | null;\n targetSnapshotFingerprint: string;\n cacheVariantFingerprint?: string;\n};\n\nexport type RouteSnapshotV0 = {\n interception: InterceptionSnapshotV0 | null;\n interceptionContext: string | null;\n routeId: string;\n // Ordered ancestor-first, with the root layout at index 0. Same-layout\n // persistence uses prefix comparison, so callers must preserve this order.\n layoutIds: readonly string[];\n mountedParallelSlots: readonly MountedParallelSlotSnapshotV0[];\n rootBoundaryId: string | null;\n displayUrl: string;\n matchedUrl: string;\n slotBindings: readonly ParallelSlotBindingSnapshotV0[];\n};\n\nexport type InterceptionSnapshotV0 = {\n sourceMatchedUrl: string;\n sourceRouteId: string;\n slotId: string;\n targetMatchedUrl: string;\n targetRouteId: string;\n};\n\nexport type MountedParallelSlotSnapshotV0 = {\n slotId: string;\n ownerLayoutId: string | null;\n};\n\n// Planner snapshots consume the same canonical slot-binding facts decoded from\n// AppElements metadata. Keep the alias explicit so route-state and transport\n// readers cannot drift into structurally identical but semantically separate\n// shapes.\nexport type ParallelSlotBindingSnapshotV0 = AppElementsSlotBinding;\n\nexport type NavigationPlannerStateV0 = {\n // V0 keeps a single state shape so intent events and result events can move\n // through one planner surface. flightResponseArrived uses event.token; later\n // #726 slices can split this by event kind once more result paths are routed\n // through the planner.\n nextOperationToken: OperationToken;\n // Callers that have lifecycle authority should pass the complete trace\n // context. When absent, the planner emits the stable root-boundary facts it\n // can derive from the event and visible snapshot.\n traceFields?: NavigationTraceFields;\n visibleCommitVersion: number;\n visibleSnapshot: RouteSnapshotV0;\n};\n\nexport type RefreshScope = \"visible\";\nexport type TraverseDirection = \"back\" | \"forward\" | \"unknown\";\n\nexport type NavigationEvent =\n | { kind: \"navigate\"; href: string; mode: \"push\" | \"replace\" }\n | { kind: \"refresh\"; scope: RefreshScope }\n | { kind: \"traverse\"; direction: TraverseDirection; historyState: unknown }\n | { kind: \"prefetch\"; href: string }\n | { kind: \"flightResponseArrived\"; token: OperationToken; result: FlightResultV0 };\n\nexport type RequestedWork =\n | { kind: \"flight\"; href: string; mode: \"push\" | \"replace\" | \"refresh\" }\n | { direction: TraverseDirection; historyState: unknown; kind: \"traverseFlight\" }\n | { kind: \"prefetch\"; href: string };\n\nexport type CommitProposal = {\n preserveAbsentSlots: boolean;\n preserveElementIds: readonly string[];\n preservePreviousSlotIds: readonly string[];\n reason: \"currentRootBoundary\" | \"interceptedCurrentRootBoundary\" | \"rootBoundaryUnknownFallback\";\n targetSnapshot: RouteSnapshotV0;\n};\n\nexport type NoCommitReason = \"prefetchOnly\";\nexport type HardNavigationReason = \"interceptionProofRejected\" | \"rootBoundaryChanged\";\nexport type RootBoundaryTransition =\n | \"currentRootBoundary\"\n | \"rootBoundaryChanged\"\n | \"rootBoundaryUnknownFallback\";\n\nexport type NavigationDecisionV0 =\n | {\n kind: \"requestWork\";\n token: OperationToken;\n work: RequestedWork;\n trace: NavigationTrace;\n }\n | {\n kind: \"proposeCommit\";\n token: OperationToken;\n proposal: CommitProposal;\n trace: NavigationTrace;\n }\n | {\n kind: \"noCommit\";\n token: OperationToken;\n reason: NoCommitReason;\n trace: NavigationTrace;\n }\n | {\n kind: \"hardNavigate\";\n token: OperationToken;\n url: string;\n reason: HardNavigationReason;\n trace: NavigationTrace;\n };\n\nexport type FlightResultV0 = {\n href: string;\n targetSnapshot: RouteSnapshotV0;\n};\n\nexport type NavigationPlannerInput = {\n // Graph-owned route topology is the semantic authority for root/layout/slot\n // decisions whenever the caller can supply it. Null keeps the legacy\n // snapshot-only path for low-level tests and unknown route shapes.\n routeManifest: RouteManifest | null;\n state: NavigationPlannerStateV0;\n event: NavigationEvent;\n};\n\ntype RouteTopologySnapshot = {\n layoutIds: readonly string[];\n rootBoundaryId: string | null;\n rootLayoutTreePath: string | null;\n slotBindings: readonly ParallelSlotBindingSnapshotV0[];\n};\n\ntype RouteTopologySlotBindingSource = \"snapshot\" | \"manifestTarget\";\n\nconst ROUTE_INTERCEPTION_CONTEXT_SEPARATOR = \"\\0\";\n\nfunction createRequestWorkDecision(options: {\n eventKind: NavigationEvent[\"kind\"];\n state: NavigationPlannerStateV0;\n work: RequestedWork;\n}): NavigationDecisionV0 {\n const traverseFields =\n options.work.kind === \"traverseFlight\" ? { traverseDirection: options.work.direction } : {};\n return {\n kind: \"requestWork\",\n token: options.state.nextOperationToken,\n work: options.work,\n trace: createNavigationTrace(NavigationTraceReasonCodes.requestWork, {\n eventKind: options.eventKind,\n targetHref: getRequestedWorkTargetHref(options.work),\n ...traverseFields,\n }),\n };\n}\n\nfunction getRequestedWorkTargetHref(work: RequestedWork): string | null {\n switch (work.kind) {\n case \"flight\":\n case \"prefetch\":\n return work.href;\n case \"traverseFlight\":\n return null;\n default: {\n const _exhaustive: never = work;\n throw new Error(\"[vinext] Unknown requested navigation work: \" + String(_exhaustive));\n }\n }\n}\n\nfunction createSnapshotRouteTopology(snapshot: RouteSnapshotV0): RouteTopologySnapshot {\n return {\n layoutIds: snapshot.layoutIds,\n rootBoundaryId: snapshot.rootBoundaryId,\n rootLayoutTreePath: snapshot.rootBoundaryId,\n slotBindings: snapshot.slotBindings,\n };\n}\n\nfunction stripInterceptionContextFromRouteId(routeId: string): string {\n const separatorIndex = routeId.indexOf(ROUTE_INTERCEPTION_CONTEXT_SEPARATOR);\n return separatorIndex === -1 ? routeId : routeId.slice(0, separatorIndex);\n}\n\nfunction getMatchedUrlPathname(matchedUrl: string): string {\n try {\n return new URL(matchedUrl, \"https://vinext.local\").pathname;\n } catch {\n const [withoutHash = \"\"] = matchedUrl.split(\"#\");\n const [pathname = \"\"] = withoutHash.split(\"?\");\n return pathname === \"\" ? \"/\" : pathname;\n }\n}\n\nfunction splitMatchedUrlIntoRouteParts(matchedUrl: string): string[] {\n return normalizePathnameForRouteMatch(getMatchedUrlPathname(matchedUrl))\n .split(\"/\")\n .filter((part) => part.length > 0);\n}\n\nfunction findRouteManifestRouteByMatchedUrl(\n routeManifest: RouteManifest,\n matchedUrl: string,\n): RouteManifestRoute | null {\n const urlParts = splitMatchedUrlIntoRouteParts(matchedUrl);\n\n // RouteManifest preserves buildAppRouteGraph's compareRoutes() order, so the\n // first pattern match follows the same static/dynamic/catch-all precedence as\n // request-time route matching instead of raw filesystem scan order.\n for (const route of routeManifest.segmentGraph.routes.values()) {\n if (matchRoutePattern(urlParts, route.patternParts) !== null) {\n return route;\n }\n }\n\n return null;\n}\n\nfunction routeManifestRouteMatchesUrl(route: RouteManifestRoute, matchedUrl: string): boolean {\n return matchRoutePattern(splitMatchedUrlIntoRouteParts(matchedUrl), route.patternParts) !== null;\n}\n\nfunction findRouteManifestRouteByIdOrMatchedUrl(options: {\n matchedUrl: string;\n routeId: string;\n routeManifest: RouteManifest;\n}): RouteManifestRoute | null {\n const routeId = stripInterceptionContextFromRouteId(options.routeId);\n const route = options.routeManifest.segmentGraph.routes.get(routeId);\n if (route && routeManifestRouteMatchesUrl(route, options.matchedUrl)) {\n return route;\n }\n\n return findRouteManifestRouteByMatchedUrl(options.routeManifest, options.matchedUrl);\n}\n\nfunction findRouteManifestRouteForSnapshot(\n routeManifest: RouteManifest,\n snapshot: RouteSnapshotV0,\n): RouteManifestRoute | null {\n if (snapshot.interception !== null) {\n return findRouteManifestRouteByIdOrMatchedUrl({\n matchedUrl: snapshot.interception.sourceMatchedUrl,\n routeId: snapshot.interception.sourceRouteId,\n routeManifest,\n });\n }\n\n return findRouteManifestRouteByIdOrMatchedUrl({\n matchedUrl: snapshot.matchedUrl,\n routeId: snapshot.routeId,\n routeManifest,\n });\n}\n\nfunction resolveRouteManifestSlotBindings(\n routeManifest: RouteManifest,\n route: RouteManifestRoute,\n): readonly ParallelSlotBindingSnapshotV0[] {\n const bindings: ParallelSlotBindingSnapshotV0[] = [];\n for (const slotId of route.slotIds) {\n const binding = routeManifest.segmentGraph.slotBindings.get(`${route.id}::${slotId}`);\n if (!binding) continue;\n bindings.push({\n ownerLayoutId: binding.ownerLayoutId,\n slotId: binding.slotId,\n state: binding.state,\n });\n }\n\n return bindings.sort((left, right) => compareAppElementsSlotIds(left.slotId, right.slotId));\n}\n\nfunction resolveRouteManifestRootLayoutTreePath(\n routeManifest: RouteManifest,\n route: RouteManifestRoute,\n): string | null {\n if (route.rootBoundaryId === null) return null;\n return routeManifest.segmentGraph.rootBoundaries.get(route.rootBoundaryId)?.treePath ?? null;\n}\n\nfunction resolveRouteTopologySnapshot(options: {\n routeManifest: RouteManifest | null;\n slotBindingSource: RouteTopologySlotBindingSource;\n snapshot: RouteSnapshotV0;\n}): RouteTopologySnapshot {\n const route =\n options.routeManifest === null\n ? null\n : findRouteManifestRouteForSnapshot(options.routeManifest, options.snapshot);\n if (route === null || options.routeManifest === null) {\n return createSnapshotRouteTopology(options.snapshot);\n }\n\n // Intercepted targets carry the source route's tree topology, not the direct\n // target route's, so direct-target manifest slot bindings do not apply.\n const shouldUseManifestSlotBindings =\n options.slotBindingSource === \"manifestTarget\" && options.snapshot.interception === null;\n\n return {\n layoutIds: route.layoutIds,\n rootBoundaryId: route.rootBoundaryId,\n rootLayoutTreePath: resolveRouteManifestRootLayoutTreePath(options.routeManifest, route),\n slotBindings: shouldUseManifestSlotBindings\n ? resolveRouteManifestSlotBindings(options.routeManifest, route)\n : options.snapshot.slotBindings,\n };\n}\n\nfunction findRouteManifestInterceptionForProof(\n routeManifest: RouteManifest,\n proof: InterceptionSnapshotV0,\n): RouteManifestInterception | null {\n const sourceParts = splitMatchedUrlIntoRouteParts(proof.sourceMatchedUrl);\n const targetParts = splitMatchedUrlIntoRouteParts(proof.targetMatchedUrl);\n const targetRoute = findRouteManifestRouteByIdOrMatchedUrl({\n matchedUrl: proof.targetMatchedUrl,\n routeId: proof.targetRouteId,\n routeManifest,\n });\n const candidateInterceptions =\n routeManifest.segmentGraph.interceptionsBySlotId.get(proof.slotId) ?? [];\n\n for (const interception of candidateInterceptions) {\n if (!matchRoutePatternPrefix(sourceParts, interception.sourcePatternParts)) {\n continue;\n }\n if (matchRoutePattern(targetParts, interception.targetPatternParts) === null) continue;\n if (interception.targetRouteId !== null && targetRoute?.id !== interception.targetRouteId) {\n continue;\n }\n return interception;\n }\n\n return null;\n}\n\nfunction createRootBoundaryTraceFields(options: {\n currentRootLayoutTreePath: string | null;\n event: Extract<NavigationEvent, { kind: \"flightResponseArrived\" }>;\n nextRootLayoutTreePath: string | null;\n state: NavigationPlannerStateV0;\n}): NavigationTraceFields {\n // Browser commit approval supplies lifecycle trace context before calling\n // the planner. This fallback exists for pure planner callers and tests; it\n // intentionally cannot invent lifecycle-only fields such as active nav id.\n if (options.state.traceFields) {\n return {\n ...options.state.traceFields,\n currentRootLayoutTreePath: options.currentRootLayoutTreePath,\n nextRootLayoutTreePath: options.nextRootLayoutTreePath,\n };\n }\n\n return createNavigationLifecycleTraceFields({\n currentRootLayoutTreePath: options.currentRootLayoutTreePath,\n currentVisibleCommitVersion: options.state.visibleCommitVersion,\n nextRootLayoutTreePath: options.nextRootLayoutTreePath,\n startedVisibleCommitVersion: options.event.token.baseVisibleCommitVersion,\n });\n}\n\nfunction classifyRootBoundaryTransition(\n currentRootBoundaryId: string | null,\n nextRootBoundaryId: string | null,\n): RootBoundaryTransition {\n if (currentRootBoundaryId === null || nextRootBoundaryId === null) {\n // Both null directions intentionally share the fallback because unresolved\n // routes or legacy callers cannot prove either same-root reuse or root\n // changes from semantic topology.\n return \"rootBoundaryUnknownFallback\";\n }\n\n return currentRootBoundaryId === nextRootBoundaryId\n ? \"currentRootBoundary\"\n : \"rootBoundaryChanged\";\n}\n\nfunction resolveSameLayoutAncestorPersistence(\n currentSnapshot: RouteSnapshotV0,\n targetSnapshot: RouteSnapshotV0,\n): readonly string[] {\n return resolveSameLayoutAncestorPersistenceForTopologies(\n createSnapshotRouteTopology(currentSnapshot),\n createSnapshotRouteTopology(targetSnapshot),\n );\n}\n\nfunction resolveSameLayoutAncestorPersistenceForTopologies(\n currentTopology: RouteTopologySnapshot,\n targetTopology: RouteTopologySnapshot,\n): readonly string[] {\n if (\n classifyRootBoundaryTransition(\n currentTopology.rootBoundaryId,\n targetTopology.rootBoundaryId,\n ) !== \"currentRootBoundary\"\n ) {\n return [];\n }\n\n const commonLayoutIds: string[] = [];\n const maxLength = Math.min(currentTopology.layoutIds.length, targetTopology.layoutIds.length);\n for (let index = 0; index < maxLength; index++) {\n const layoutId = currentTopology.layoutIds[index];\n if (layoutId !== targetTopology.layoutIds[index]) break;\n commonLayoutIds.push(layoutId);\n }\n return commonLayoutIds;\n}\n\nfunction resolveMountedParallelSlotPersistence(\n currentSnapshot: RouteSnapshotV0,\n targetSnapshot: RouteSnapshotV0,\n): readonly string[] {\n const preservedLayoutIds = resolveSameLayoutAncestorPersistence(currentSnapshot, targetSnapshot);\n return resolveMountedParallelSlotPersistenceForLayouts(currentSnapshot, preservedLayoutIds);\n}\n\nfunction resolveMountedParallelSlotPersistenceForLayouts(\n currentSnapshot: RouteSnapshotV0,\n preservedLayoutIds: readonly string[],\n): readonly string[] {\n if (preservedLayoutIds.length === 0) return [];\n const preservedLayoutIdSet = new Set(preservedLayoutIds);\n\n const preservedSlotIds: string[] = [];\n const seenSlotIds = new Set<string>();\n for (const slot of currentSnapshot.mountedParallelSlots) {\n if (slot.ownerLayoutId === null) continue;\n if (!preservedLayoutIdSet.has(slot.ownerLayoutId)) continue;\n if (seenSlotIds.has(slot.slotId)) continue;\n\n preservedSlotIds.push(slot.slotId);\n seenSlotIds.add(slot.slotId);\n }\n return preservedSlotIds;\n}\n\nfunction resolveCurrentRootBoundaryElementPersistence(\n currentSnapshot: RouteSnapshotV0,\n targetSnapshot: RouteSnapshotV0,\n): readonly string[] {\n const preservedLayoutIds = resolveSameLayoutAncestorPersistence(currentSnapshot, targetSnapshot);\n // Non-commit consumers still receive the legacy mounted-slot element list.\n // Commit promotion uses preservePreviousSlotIds instead so default/unmatched\n // slot reuse requires route-state proof.\n return [\n ...preservedLayoutIds,\n ...resolveMountedParallelSlotPersistenceForLayouts(currentSnapshot, preservedLayoutIds),\n ];\n}\n\nfunction resolveCurrentRootBoundaryCommitElementPersistence(options: {\n currentTopology: RouteTopologySnapshot;\n lane: OperationLane;\n targetTopology: RouteTopologySnapshot;\n}): readonly string[] {\n // Commit element persistence only keeps layout IDs. Default/unmatched slot\n // reuse is handled separately by preservePreviousSlotIds, using slot-binding\n // metadata as proof; payloads without __slotBindings get no semantic reuse.\n // resolveCurrentRootBoundaryCommitSlotPersistence recomputes this same\n // ancestor set; planner correctness relies on both calls agreeing so any\n // preserved slot's owner layout is also present in preserveElementIds.\n return resolveSameLayoutAncestorPersistenceForTopologies(\n options.currentTopology,\n options.targetTopology,\n );\n}\n\nfunction resolveCurrentRootBoundaryCommitSlotPersistence(options: {\n currentTopology: RouteTopologySnapshot;\n lane: OperationLane;\n targetTopology: RouteTopologySnapshot;\n}): readonly string[] {\n if (options.lane === \"traverse\") return [];\n\n const preservedLayoutIds = resolveSameLayoutAncestorPersistenceForTopologies(\n options.currentTopology,\n options.targetTopology,\n );\n if (preservedLayoutIds.length === 0) return [];\n\n return resolveDefaultOrUnmatchedSlotPersistenceForLayouts({\n currentSlotBindings: options.currentTopology.slotBindings,\n preservedLayoutIds,\n targetSlotBindings: options.targetTopology.slotBindings,\n });\n}\n\n/**\n * Default/unmatched slot preservation law:\n *\n * A target default/unmatched slot may reuse previous content only when:\n * - the slot's owner layout is part of the preserved layout ancestor set;\n * - the current visible snapshot proves the same slot had renderable content;\n * - the navigation is not a traversal.\n *\n * Wire absence and UNMATCHED_SLOT markers are not semantic proof.\n */\nfunction resolveDefaultOrUnmatchedSlotPersistenceForLayouts(options: {\n currentSlotBindings: readonly ParallelSlotBindingSnapshotV0[];\n preservedLayoutIds: readonly string[];\n targetSlotBindings: readonly ParallelSlotBindingSnapshotV0[];\n}): readonly string[] {\n const preservedLayoutIdSet = new Set(options.preservedLayoutIds);\n const slotIdsWithContent = new Set<string>();\n for (const binding of options.currentSlotBindings) {\n if (binding.state === \"unmatched\") continue;\n slotIdsWithContent.add(binding.slotId);\n }\n\n const preservedSlotIds: string[] = [];\n const seenSlotIds = new Set<string>();\n for (const binding of options.targetSlotBindings) {\n if (binding.ownerLayoutId === null) continue;\n if (!preservedLayoutIdSet.has(binding.ownerLayoutId)) continue;\n if (binding.state === \"active\") continue;\n if (!slotIdsWithContent.has(binding.slotId)) continue;\n if (seenSlotIds.has(binding.slotId)) continue;\n\n preservedSlotIds.push(binding.slotId);\n seenSlotIds.add(binding.slotId);\n }\n return preservedSlotIds.sort(compareAppElementsSlotIds);\n}\n\ntype VisibleInterceptionSourceIdentity = {\n matchedUrl: string;\n routeId: string;\n};\n\ntype InterceptedPreservationValidation =\n | {\n kind: \"approved\";\n preserveElementIds: readonly string[];\n preservePreviousSlotIds: readonly string[];\n }\n | {\n kind: \"rejected\";\n reasonCode: NavigationTraceReasonCode;\n };\n\nfunction getVisibleInterceptionSourceIdentity(\n snapshot: RouteSnapshotV0,\n): VisibleInterceptionSourceIdentity {\n if (snapshot.interception) {\n return {\n matchedUrl: snapshot.interception.sourceMatchedUrl,\n routeId: snapshot.interception.sourceRouteId,\n };\n }\n return {\n matchedUrl: snapshot.matchedUrl,\n routeId: snapshot.routeId,\n };\n}\n\nfunction createInterceptionProofRejectedDecision(options: {\n event: Extract<NavigationEvent, { kind: \"flightResponseArrived\" }>;\n reasonCode: NavigationTraceReasonCode;\n traceFields: NavigationTraceFields;\n}): NavigationDecisionV0 {\n return {\n kind: \"hardNavigate\",\n reason: \"interceptionProofRejected\",\n token: options.event.token,\n trace: createNavigationTrace(options.reasonCode, options.traceFields),\n url: options.event.result.href,\n };\n}\n\nfunction validateInterceptedPreservation(options: {\n currentSnapshot: RouteSnapshotV0;\n currentTopology: RouteTopologySnapshot;\n routeManifest: RouteManifest | null;\n targetSnapshot: RouteSnapshotV0;\n targetTopology: RouteTopologySnapshot;\n}): InterceptedPreservationValidation {\n const proof = options.targetSnapshot.interception;\n if (!proof) {\n return {\n kind: \"rejected\",\n reasonCode: NavigationTraceReasonCodes.interceptedRejectedMissingProof,\n };\n }\n\n if (proof.targetMatchedUrl !== options.targetSnapshot.matchedUrl) {\n return {\n kind: \"rejected\",\n reasonCode: NavigationTraceReasonCodes.interceptedRejectedTargetMismatch,\n };\n }\n\n const sourceIdentity = getVisibleInterceptionSourceIdentity(options.currentSnapshot);\n if (\n proof.sourceMatchedUrl !== sourceIdentity.matchedUrl ||\n proof.sourceRouteId !== sourceIdentity.routeId\n ) {\n return {\n kind: \"rejected\",\n reasonCode: NavigationTraceReasonCodes.interceptedRejectedUnknownSource,\n };\n }\n\n const declaredInterception =\n options.routeManifest === null\n ? null\n : findRouteManifestInterceptionForProof(options.routeManifest, proof);\n if (options.routeManifest !== null && declaredInterception === null) {\n return {\n kind: \"rejected\",\n reasonCode: NavigationTraceReasonCodes.interceptedRejectedUndeclaredTopology,\n };\n }\n\n const preservedLayoutIds = resolveSameLayoutAncestorPersistenceForTopologies(\n options.currentTopology,\n options.targetTopology,\n );\n if (preservedLayoutIds.length === 0) {\n return {\n kind: \"rejected\",\n reasonCode: NavigationTraceReasonCodes.interceptedRejectedIncompatibleRoot,\n };\n }\n\n const preservedLayoutIdSet = new Set(preservedLayoutIds);\n const targetSlotBinding = options.targetTopology.slotBindings.find(\n (binding) => binding.slotId === proof.slotId,\n );\n if (\n !targetSlotBinding ||\n targetSlotBinding.state !== \"active\" ||\n targetSlotBinding.ownerLayoutId === null ||\n !preservedLayoutIdSet.has(targetSlotBinding.ownerLayoutId)\n ) {\n return {\n kind: \"rejected\",\n reasonCode: NavigationTraceReasonCodes.interceptedRejectedMissingSlotProof,\n };\n }\n if (\n declaredInterception !== null &&\n targetSlotBinding.ownerLayoutId !== declaredInterception.ownerLayoutId\n ) {\n return {\n kind: \"rejected\",\n reasonCode: NavigationTraceReasonCodes.interceptedRejectedUndeclaredTopology,\n };\n }\n\n const preservePreviousSlotIds = resolveDefaultOrUnmatchedSlotPersistenceForLayouts({\n currentSlotBindings: options.currentTopology.slotBindings,\n preservedLayoutIds,\n targetSlotBindings: options.targetTopology.slotBindings,\n }).filter((slotId) => slotId !== proof.slotId);\n\n return {\n kind: \"approved\",\n preserveElementIds: preservedLayoutIds,\n preservePreviousSlotIds,\n };\n}\n\nfunction planFlightResponseArrived(options: {\n event: Extract<NavigationEvent, { kind: \"flightResponseArrived\" }>;\n routeManifest: RouteManifest | null;\n state: NavigationPlannerStateV0;\n}): NavigationDecisionV0 {\n const targetSnapshot = options.event.result.targetSnapshot;\n const currentTopology = resolveRouteTopologySnapshot({\n routeManifest: options.routeManifest,\n slotBindingSource: \"snapshot\",\n snapshot: options.state.visibleSnapshot,\n });\n const targetTopology = resolveRouteTopologySnapshot({\n routeManifest: options.routeManifest,\n slotBindingSource: \"manifestTarget\",\n snapshot: targetSnapshot,\n });\n const traceFields = createRootBoundaryTraceFields({\n currentRootLayoutTreePath: currentTopology.rootLayoutTreePath,\n event: options.event,\n nextRootLayoutTreePath: targetTopology.rootLayoutTreePath,\n state: options.state,\n });\n\n if (options.event.token.lane === \"prefetch\") {\n return {\n kind: \"noCommit\",\n reason: \"prefetchOnly\",\n token: options.event.token,\n trace: createNavigationTrace(NavigationTraceReasonCodes.prefetchOnly, traceFields),\n };\n }\n\n // interceptionContext is transport evidence, not authority. Normal payloads\n // can carry it when a request was sent from an intercepted visible world, so\n // only explicit __interception proof enters the preservation branch.\n const hasInterceptedPayload = targetSnapshot.interception !== null;\n if (hasInterceptedPayload) {\n const validation = validateInterceptedPreservation({\n currentSnapshot: options.state.visibleSnapshot,\n currentTopology,\n routeManifest: options.routeManifest,\n targetSnapshot,\n targetTopology,\n });\n if (validation.kind === \"rejected\") {\n return createInterceptionProofRejectedDecision({\n event: options.event,\n reasonCode: validation.reasonCode,\n traceFields,\n });\n }\n\n return {\n kind: \"proposeCommit\",\n proposal: {\n preserveAbsentSlots: false,\n preserveElementIds: validation.preserveElementIds,\n preservePreviousSlotIds: validation.preservePreviousSlotIds,\n reason: \"interceptedCurrentRootBoundary\",\n targetSnapshot,\n },\n token: options.event.token,\n trace: createNavigationTrace(\n NavigationTraceReasonCodes.interceptedCommitCurrent,\n traceFields,\n ),\n };\n }\n\n const transition = classifyRootBoundaryTransition(\n currentTopology.rootBoundaryId,\n targetTopology.rootBoundaryId,\n );\n\n if (transition === \"rootBoundaryChanged\") {\n return {\n kind: \"hardNavigate\",\n reason: \"rootBoundaryChanged\",\n token: options.event.token,\n trace: createNavigationTrace(NavigationTraceReasonCodes.rootBoundaryChanged, traceFields),\n url: options.event.result.href,\n };\n }\n\n if (transition === \"rootBoundaryUnknownFallback\") {\n // Unknown root identity is an uncertainty fallback, not evidence that\n // reuse is safe. It remains only for callers without a manifest or routes\n // that cannot be matched back to RouteManifest topology.\n return {\n kind: \"proposeCommit\",\n proposal: {\n preserveAbsentSlots: true,\n preserveElementIds: [],\n preservePreviousSlotIds: [],\n reason: \"rootBoundaryUnknownFallback\",\n targetSnapshot,\n },\n token: options.event.token,\n trace: createNavigationTrace(NavigationTraceReasonCodes.rootBoundaryUnknown, traceFields),\n };\n }\n\n return {\n kind: \"proposeCommit\",\n proposal: {\n preserveAbsentSlots: false,\n preserveElementIds: resolveCurrentRootBoundaryCommitElementPersistence({\n currentTopology,\n lane: options.event.token.lane,\n targetTopology,\n }),\n preservePreviousSlotIds: resolveCurrentRootBoundaryCommitSlotPersistence({\n currentTopology,\n lane: options.event.token.lane,\n targetTopology,\n }),\n reason: \"currentRootBoundary\",\n targetSnapshot,\n },\n token: options.event.token,\n trace: createNavigationTrace(NavigationTraceReasonCodes.commitCurrent, traceFields),\n };\n}\n\nfunction planNavigation(input: NavigationPlannerInput): NavigationDecisionV0 {\n switch (input.event.kind) {\n case \"navigate\":\n return createRequestWorkDecision({\n eventKind: input.event.kind,\n state: input.state,\n work: {\n href: input.event.href,\n kind: \"flight\",\n mode: input.event.mode,\n },\n });\n case \"refresh\":\n return createRequestWorkDecision({\n eventKind: input.event.kind,\n state: input.state,\n work: {\n href: input.state.visibleSnapshot.displayUrl,\n kind: \"flight\",\n mode: \"refresh\",\n },\n });\n case \"traverse\":\n return createRequestWorkDecision({\n eventKind: input.event.kind,\n state: input.state,\n work: {\n direction: input.event.direction,\n historyState: input.event.historyState,\n kind: \"traverseFlight\",\n },\n });\n case \"prefetch\":\n return createRequestWorkDecision({\n eventKind: input.event.kind,\n state: input.state,\n work: {\n href: input.event.href,\n kind: \"prefetch\",\n },\n });\n case \"flightResponseArrived\":\n return planFlightResponseArrived({\n event: input.event,\n routeManifest: input.routeManifest,\n state: input.state,\n });\n default: {\n const _exhaustive: never = input.event;\n throw new Error(\"[vinext] Unknown navigation event: \" + String(_exhaustive));\n }\n }\n}\n\nexport const navigationPlanner = {\n classifyRootBoundaryTransition,\n plan: planNavigation,\n resolveCurrentRootBoundaryElementPersistence,\n resolveMountedParallelSlotPersistence,\n resolveSameLayoutAncestorPersistence,\n};\n"],"mappings":"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"}
@@ -3,6 +3,13 @@ declare const NAVIGATION_TRACE_SCHEMA_VERSION = 0;
3
3
  type NavigationTraceSchemaVersion = 0;
4
4
  declare const NavigationTraceReasonCodes: {
5
5
  commitCurrent: "NC_COMMIT";
6
+ interceptedCommitCurrent: "NC_INTERCEPT_COMMIT";
7
+ interceptedRejectedIncompatibleRoot: "NC_INTERCEPT_REJECT_ROOT";
8
+ interceptedRejectedMissingProof: "NC_INTERCEPT_REJECT_MISSING_PROOF";
9
+ interceptedRejectedMissingSlotProof: "NC_INTERCEPT_REJECT_SLOT";
10
+ interceptedRejectedTargetMismatch: "NC_INTERCEPT_REJECT_TARGET";
11
+ interceptedRejectedUndeclaredTopology: "NC_INTERCEPT_REJECT_GRAPH";
12
+ interceptedRejectedUnknownSource: "NC_INTERCEPT_REJECT_SOURCE";
6
13
  prefetchOnly: "NC_PREFETCH_ONLY";
7
14
  requestWork: "NC_REQUEST";
8
15
  rootBoundaryChanged: "NC_ROOT";
@@ -17,7 +24,7 @@ declare const NavigationTraceTransactionCodes: {
17
24
  type NavigationTraceReasonCode = (typeof NavigationTraceReasonCodes)[keyof typeof NavigationTraceReasonCodes];
18
25
  type NavigationTraceTransactionCode = (typeof NavigationTraceTransactionCodes)[keyof typeof NavigationTraceTransactionCodes];
19
26
  type NavigationTraceCode = NavigationTraceReasonCode | NavigationTraceTransactionCode;
20
- type NavigationTraceFieldName = "activeNavigationId" | "currentRootLayoutTreePath" | "currentVisibleCommitVersion" | "nextRootLayoutTreePath" | "eventKind" | "operationLane" | "pendingOperationId" | "startedVisibleCommitVersion" | "startedNavigationId" | "targetHref";
27
+ type NavigationTraceFieldName = "activeNavigationId" | "currentRootLayoutTreePath" | "currentVisibleCommitVersion" | "nextRootLayoutTreePath" | "eventKind" | "operationLane" | "pendingOperationId" | "startedVisibleCommitVersion" | "startedNavigationId" | "targetHref" | "traverseDirection";
21
28
  type NavigationTraceFieldValue = string | number | boolean | null;
22
29
  type NavigationTraceFields = Readonly<Partial<Record<NavigationTraceFieldName, NavigationTraceFieldValue>>>;
23
30
  type NavigationTraceEntry = Readonly<{
@@ -2,6 +2,13 @@
2
2
  const NAVIGATION_TRACE_SCHEMA_VERSION = 0;
3
3
  const NavigationTraceReasonCodes = {
4
4
  commitCurrent: "NC_COMMIT",
5
+ interceptedCommitCurrent: "NC_INTERCEPT_COMMIT",
6
+ interceptedRejectedIncompatibleRoot: "NC_INTERCEPT_REJECT_ROOT",
7
+ interceptedRejectedMissingProof: "NC_INTERCEPT_REJECT_MISSING_PROOF",
8
+ interceptedRejectedMissingSlotProof: "NC_INTERCEPT_REJECT_SLOT",
9
+ interceptedRejectedTargetMismatch: "NC_INTERCEPT_REJECT_TARGET",
10
+ interceptedRejectedUndeclaredTopology: "NC_INTERCEPT_REJECT_GRAPH",
11
+ interceptedRejectedUnknownSource: "NC_INTERCEPT_REJECT_SOURCE",
5
12
  prefetchOnly: "NC_PREFETCH_ONLY",
6
13
  requestWork: "NC_REQUEST",
7
14
  rootBoundaryChanged: "NC_ROOT",
@@ -1 +1 @@
1
- {"version":3,"file":"navigation-trace.js","names":[],"sources":["../../src/server/navigation-trace.ts"],"sourcesContent":["export const NAVIGATION_TRACE_SCHEMA_VERSION = 0;\n\nexport type NavigationTraceSchemaVersion = 0;\n\nexport const NavigationTraceReasonCodes = {\n commitCurrent: \"NC_COMMIT\",\n prefetchOnly: \"NC_PREFETCH_ONLY\",\n requestWork: \"NC_REQUEST\",\n rootBoundaryChanged: \"NC_ROOT\",\n rootBoundaryUnknown: \"NC_ROOT_UNKNOWN\",\n staleOperation: \"NC_STALE\",\n} satisfies Readonly<{\n commitCurrent: \"NC_COMMIT\";\n prefetchOnly: \"NC_PREFETCH_ONLY\";\n requestWork: \"NC_REQUEST\";\n rootBoundaryChanged: \"NC_ROOT\";\n rootBoundaryUnknown: \"NC_ROOT_UNKNOWN\";\n staleOperation: \"NC_STALE\";\n}>;\n\nexport const NavigationTraceTransactionCodes = {\n hardNavigate: \"NT_HARD_NAVIGATE\",\n noCommit: \"NT_NO_COMMIT\",\n visibleCommit: \"NT_VISIBLE_COMMIT\",\n} satisfies Readonly<{\n hardNavigate: \"NT_HARD_NAVIGATE\";\n noCommit: \"NT_NO_COMMIT\";\n visibleCommit: \"NT_VISIBLE_COMMIT\";\n}>;\n\nexport type NavigationTraceReasonCode =\n (typeof NavigationTraceReasonCodes)[keyof typeof NavigationTraceReasonCodes];\n\nexport type NavigationTraceTransactionCode =\n (typeof NavigationTraceTransactionCodes)[keyof typeof NavigationTraceTransactionCodes];\n\nexport type NavigationTraceCode = NavigationTraceReasonCode | NavigationTraceTransactionCode;\n\nexport type NavigationTraceFieldName =\n | \"activeNavigationId\"\n | \"currentRootLayoutTreePath\"\n | \"currentVisibleCommitVersion\"\n | \"nextRootLayoutTreePath\"\n | \"eventKind\"\n | \"operationLane\"\n | \"pendingOperationId\"\n | \"startedVisibleCommitVersion\"\n | \"startedNavigationId\"\n | \"targetHref\";\n\nexport type NavigationTraceFieldValue = string | number | boolean | null;\n\nexport type NavigationTraceFields = Readonly<\n Partial<Record<NavigationTraceFieldName, NavigationTraceFieldValue>>\n>;\n\nexport type NavigationTraceEntry = Readonly<{\n code: NavigationTraceCode;\n fields: NavigationTraceFields;\n}>;\n\nexport type NavigationTrace = Readonly<{\n schemaVersion: NavigationTraceSchemaVersion;\n entries: readonly NavigationTraceEntry[];\n}>;\n\nexport function createNavigationLifecycleTraceFields(options: {\n activeNavigationId?: number;\n currentRootLayoutTreePath: string | null;\n currentVisibleCommitVersion: number;\n nextRootLayoutTreePath: string | null;\n startedNavigationId?: number;\n startedVisibleCommitVersion: number;\n}): NavigationTraceFields {\n return {\n ...(options.activeNavigationId !== undefined\n ? { activeNavigationId: options.activeNavigationId }\n : {}),\n currentRootLayoutTreePath: options.currentRootLayoutTreePath,\n currentVisibleCommitVersion: options.currentVisibleCommitVersion,\n nextRootLayoutTreePath: options.nextRootLayoutTreePath,\n ...(options.startedNavigationId !== undefined\n ? { startedNavigationId: options.startedNavigationId }\n : {}),\n startedVisibleCommitVersion: options.startedVisibleCommitVersion,\n };\n}\n\nfunction createNavigationTraceEntry(\n code: NavigationTraceCode,\n fields: NavigationTraceFields = {},\n): NavigationTraceEntry {\n return {\n code,\n fields: { ...fields },\n };\n}\n\nexport function createNavigationTrace(\n code: NavigationTraceCode,\n fields: NavigationTraceFields = {},\n): NavigationTrace {\n return {\n schemaVersion: NAVIGATION_TRACE_SCHEMA_VERSION,\n entries: [createNavigationTraceEntry(code, fields)],\n };\n}\n\nexport function prependNavigationTraceEntry(\n trace: NavigationTrace,\n code: NavigationTraceCode,\n fields: NavigationTraceFields = {},\n): NavigationTrace {\n return {\n schemaVersion: trace.schemaVersion,\n entries: [createNavigationTraceEntry(code, fields), ...trace.entries],\n };\n}\n"],"mappings":";AAAA,MAAa,kCAAkC;AAI/C,MAAa,6BAA6B;CACxC,eAAe;CACf,cAAc;CACd,aAAa;CACb,qBAAqB;CACrB,qBAAqB;CACrB,gBAAgB;CACjB;AASD,MAAa,kCAAkC;CAC7C,cAAc;CACd,UAAU;CACV,eAAe;CAChB;AA0CD,SAAgB,qCAAqC,SAO3B;CACxB,OAAO;EACL,GAAI,QAAQ,uBAAuB,KAAA,IAC/B,EAAE,oBAAoB,QAAQ,oBAAoB,GAClD,EAAE;EACN,2BAA2B,QAAQ;EACnC,6BAA6B,QAAQ;EACrC,wBAAwB,QAAQ;EAChC,GAAI,QAAQ,wBAAwB,KAAA,IAChC,EAAE,qBAAqB,QAAQ,qBAAqB,GACpD,EAAE;EACN,6BAA6B,QAAQ;EACtC;;AAGH,SAAS,2BACP,MACA,SAAgC,EAAE,EACZ;CACtB,OAAO;EACL;EACA,QAAQ,EAAE,GAAG,QAAQ;EACtB;;AAGH,SAAgB,sBACd,MACA,SAAgC,EAAE,EACjB;CACjB,OAAO;EACL,eAAA;EACA,SAAS,CAAC,2BAA2B,MAAM,OAAO,CAAC;EACpD;;AAGH,SAAgB,4BACd,OACA,MACA,SAAgC,EAAE,EACjB;CACjB,OAAO;EACL,eAAe,MAAM;EACrB,SAAS,CAAC,2BAA2B,MAAM,OAAO,EAAE,GAAG,MAAM,QAAQ;EACtE"}
1
+ {"version":3,"file":"navigation-trace.js","names":[],"sources":["../../src/server/navigation-trace.ts"],"sourcesContent":["export const NAVIGATION_TRACE_SCHEMA_VERSION = 0;\n\nexport type NavigationTraceSchemaVersion = 0;\n\nexport const NavigationTraceReasonCodes = {\n commitCurrent: \"NC_COMMIT\",\n interceptedCommitCurrent: \"NC_INTERCEPT_COMMIT\",\n interceptedRejectedIncompatibleRoot: \"NC_INTERCEPT_REJECT_ROOT\",\n interceptedRejectedMissingProof: \"NC_INTERCEPT_REJECT_MISSING_PROOF\",\n interceptedRejectedMissingSlotProof: \"NC_INTERCEPT_REJECT_SLOT\",\n interceptedRejectedTargetMismatch: \"NC_INTERCEPT_REJECT_TARGET\",\n interceptedRejectedUndeclaredTopology: \"NC_INTERCEPT_REJECT_GRAPH\",\n interceptedRejectedUnknownSource: \"NC_INTERCEPT_REJECT_SOURCE\",\n prefetchOnly: \"NC_PREFETCH_ONLY\",\n requestWork: \"NC_REQUEST\",\n rootBoundaryChanged: \"NC_ROOT\",\n rootBoundaryUnknown: \"NC_ROOT_UNKNOWN\",\n staleOperation: \"NC_STALE\",\n} satisfies Readonly<{\n commitCurrent: \"NC_COMMIT\";\n interceptedCommitCurrent: \"NC_INTERCEPT_COMMIT\";\n interceptedRejectedIncompatibleRoot: \"NC_INTERCEPT_REJECT_ROOT\";\n interceptedRejectedMissingProof: \"NC_INTERCEPT_REJECT_MISSING_PROOF\";\n interceptedRejectedMissingSlotProof: \"NC_INTERCEPT_REJECT_SLOT\";\n interceptedRejectedTargetMismatch: \"NC_INTERCEPT_REJECT_TARGET\";\n interceptedRejectedUndeclaredTopology: \"NC_INTERCEPT_REJECT_GRAPH\";\n interceptedRejectedUnknownSource: \"NC_INTERCEPT_REJECT_SOURCE\";\n prefetchOnly: \"NC_PREFETCH_ONLY\";\n requestWork: \"NC_REQUEST\";\n rootBoundaryChanged: \"NC_ROOT\";\n rootBoundaryUnknown: \"NC_ROOT_UNKNOWN\";\n staleOperation: \"NC_STALE\";\n}>;\n\nexport const NavigationTraceTransactionCodes = {\n hardNavigate: \"NT_HARD_NAVIGATE\",\n noCommit: \"NT_NO_COMMIT\",\n visibleCommit: \"NT_VISIBLE_COMMIT\",\n} satisfies Readonly<{\n hardNavigate: \"NT_HARD_NAVIGATE\";\n noCommit: \"NT_NO_COMMIT\";\n visibleCommit: \"NT_VISIBLE_COMMIT\";\n}>;\n\nexport type NavigationTraceReasonCode =\n (typeof NavigationTraceReasonCodes)[keyof typeof NavigationTraceReasonCodes];\n\nexport type NavigationTraceTransactionCode =\n (typeof NavigationTraceTransactionCodes)[keyof typeof NavigationTraceTransactionCodes];\n\nexport type NavigationTraceCode = NavigationTraceReasonCode | NavigationTraceTransactionCode;\n\nexport type NavigationTraceFieldName =\n | \"activeNavigationId\"\n | \"currentRootLayoutTreePath\"\n | \"currentVisibleCommitVersion\"\n | \"nextRootLayoutTreePath\"\n | \"eventKind\"\n | \"operationLane\"\n | \"pendingOperationId\"\n | \"startedVisibleCommitVersion\"\n | \"startedNavigationId\"\n | \"targetHref\"\n | \"traverseDirection\";\n\nexport type NavigationTraceFieldValue = string | number | boolean | null;\n\nexport type NavigationTraceFields = Readonly<\n Partial<Record<NavigationTraceFieldName, NavigationTraceFieldValue>>\n>;\n\nexport type NavigationTraceEntry = Readonly<{\n code: NavigationTraceCode;\n fields: NavigationTraceFields;\n}>;\n\nexport type NavigationTrace = Readonly<{\n schemaVersion: NavigationTraceSchemaVersion;\n entries: readonly NavigationTraceEntry[];\n}>;\n\nexport function createNavigationLifecycleTraceFields(options: {\n activeNavigationId?: number;\n currentRootLayoutTreePath: string | null;\n currentVisibleCommitVersion: number;\n nextRootLayoutTreePath: string | null;\n startedNavigationId?: number;\n startedVisibleCommitVersion: number;\n}): NavigationTraceFields {\n return {\n ...(options.activeNavigationId !== undefined\n ? { activeNavigationId: options.activeNavigationId }\n : {}),\n currentRootLayoutTreePath: options.currentRootLayoutTreePath,\n currentVisibleCommitVersion: options.currentVisibleCommitVersion,\n nextRootLayoutTreePath: options.nextRootLayoutTreePath,\n ...(options.startedNavigationId !== undefined\n ? { startedNavigationId: options.startedNavigationId }\n : {}),\n startedVisibleCommitVersion: options.startedVisibleCommitVersion,\n };\n}\n\nfunction createNavigationTraceEntry(\n code: NavigationTraceCode,\n fields: NavigationTraceFields = {},\n): NavigationTraceEntry {\n return {\n code,\n fields: { ...fields },\n };\n}\n\nexport function createNavigationTrace(\n code: NavigationTraceCode,\n fields: NavigationTraceFields = {},\n): NavigationTrace {\n return {\n schemaVersion: NAVIGATION_TRACE_SCHEMA_VERSION,\n entries: [createNavigationTraceEntry(code, fields)],\n };\n}\n\nexport function prependNavigationTraceEntry(\n trace: NavigationTrace,\n code: NavigationTraceCode,\n fields: NavigationTraceFields = {},\n): NavigationTrace {\n return {\n schemaVersion: trace.schemaVersion,\n entries: [createNavigationTraceEntry(code, fields), ...trace.entries],\n };\n}\n"],"mappings":";AAAA,MAAa,kCAAkC;AAI/C,MAAa,6BAA6B;CACxC,eAAe;CACf,0BAA0B;CAC1B,qCAAqC;CACrC,iCAAiC;CACjC,qCAAqC;CACrC,mCAAmC;CACnC,uCAAuC;CACvC,kCAAkC;CAClC,cAAc;CACd,aAAa;CACb,qBAAqB;CACrB,qBAAqB;CACrB,gBAAgB;CACjB;AAgBD,MAAa,kCAAkC;CAC7C,cAAc;CACd,UAAU;CACV,eAAe;CAChB;AA2CD,SAAgB,qCAAqC,SAO3B;CACxB,OAAO;EACL,GAAI,QAAQ,uBAAuB,KAAA,IAC/B,EAAE,oBAAoB,QAAQ,oBAAoB,GAClD,EAAE;EACN,2BAA2B,QAAQ;EACnC,6BAA6B,QAAQ;EACrC,wBAAwB,QAAQ;EAChC,GAAI,QAAQ,wBAAwB,KAAA,IAChC,EAAE,qBAAqB,QAAQ,qBAAqB,GACpD,EAAE;EACN,6BAA6B,QAAQ;EACtC;;AAGH,SAAS,2BACP,MACA,SAAgC,EAAE,EACZ;CACtB,OAAO;EACL;EACA,QAAQ,EAAE,GAAG,QAAQ;EACtB;;AAGH,SAAgB,sBACd,MACA,SAAgC,EAAE,EACjB;CACjB,OAAO;EACL,eAAA;EACA,SAAS,CAAC,2BAA2B,MAAM,OAAO,CAAC;EACpD;;AAGH,SAAgB,4BACd,OACA,MACA,SAAgC,EAAE,EACjB;CACjB,OAAO;EACL,eAAe,MAAM;EACrB,SAAS,CAAC,2BAA2B,MAAM,OAAO,EAAE,GAAG,MAAM,QAAQ;EACtE"}
@@ -20,6 +20,7 @@ declare function escapePathDelimiters(segment: string, escapeEncoded?: boolean):
20
20
  * https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/decode-path-params.ts
21
21
  */
22
22
  declare function decodePathParams(pathname: string): string;
23
+ declare function isInterceptionMatchedUrlPath(value: string): boolean;
23
24
  /**
24
25
  * Path normalization utility for request handling.
25
26
  *
@@ -42,5 +43,5 @@ declare function decodePathParams(pathname: string): string;
42
43
  */
43
44
  declare function normalizePath(pathname: string): string;
44
45
  //#endregion
45
- export { decodePathParams, escapePathDelimiters, normalizePath };
46
+ export { decodePathParams, escapePathDelimiters, isInterceptionMatchedUrlPath, normalizePath };
46
47
  //# sourceMappingURL=normalize-path.d.ts.map
@@ -30,6 +30,9 @@ function decodePathParams(pathname) {
30
30
  }
31
31
  }).join("/");
32
32
  }
33
+ function isInterceptionMatchedUrlPath(value) {
34
+ return value.startsWith("/") && !value.startsWith("//") && !value.includes("?") && !value.includes("#") && !value.includes("\0");
35
+ }
33
36
  /**
34
37
  * Path normalization utility for request handling.
35
38
  *
@@ -62,6 +65,6 @@ function normalizePath(pathname) {
62
65
  return "/" + resolved.join("/");
63
66
  }
64
67
  //#endregion
65
- export { decodePathParams, escapePathDelimiters, normalizePath };
68
+ export { decodePathParams, escapePathDelimiters, isInterceptionMatchedUrlPath, normalizePath };
66
69
 
67
70
  //# sourceMappingURL=normalize-path.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"normalize-path.js","names":[],"sources":["../../src/server/normalize-path.ts"],"sourcesContent":["/**\n * Re-encode path delimiter characters that were decoded by decodeURIComponent.\n * After decoding a URL segment, characters like / # ? \\ need to be re-encoded\n * so they don't change the path structure.\n *\n * Ported from Next.js: packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts\n */\nexport function escapePathDelimiters(segment: string, escapeEncoded?: boolean): string {\n return segment.replace(\n new RegExp(`([/#?]${escapeEncoded ? \"|%(2f|23|3f|5c)\" : \"\"})`, \"gi\"),\n (char: string) => encodeURIComponent(char),\n );\n}\n\n/**\n * Decode a URL pathname segment-by-segment, preserving encoded path delimiters.\n * Non-ASCII characters (e.g. %C3%A9 -> e) are decoded, but structural characters\n * like %2F (/) %23 (#) %3F (?) %5C (\\) are re-encoded after decoding.\n *\n * This prevents encoded slashes from changing the path structure (e.g.\n * /admin%2Fpanel stays as a single segment, not /admin/panel).\n *\n * Ported from Next.js: packages/next/src/server/lib/router-utils/decode-path-params.ts\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/decode-path-params.ts\n */\nexport function decodePathParams(pathname: string): string {\n return pathname\n .split(\"/\")\n .map((seg) => {\n try {\n return escapePathDelimiters(decodeURIComponent(seg), true);\n } catch {\n return seg;\n }\n })\n .join(\"/\");\n}\n\n/**\n * Path normalization utility for request handling.\n *\n * Normalizes URL pathnames to a canonical form BEFORE any matching occurs\n * (middleware, routing, redirects, rewrites). This ensures middleware and\n * the router always see the same path, preventing path-confusion issues like\n * double-slash mismatches.\n *\n * Normalization rules:\n * 1. Collapse consecutive slashes: //foo///bar → /foo/bar\n * 2. Resolve single-dot segments: /foo/./bar → /foo/bar\n * 3. Resolve double-dot segments: /foo/../bar → /bar\n * 4. Ensure leading slash: foo/bar → /foo/bar\n * 5. Preserve root: / → /\n *\n * This function does NOT:\n * - Strip or add trailing slashes (handled separately by trailingSlash config)\n * - Decode percent-encoded characters (callers should decode before calling this)\n * - Lowercase the path (route matching is case-sensitive)\n */\nexport function normalizePath(pathname: string): string {\n // Fast path: already canonical (single leading /, no //, no /./, no /../)\n if (\n pathname === \"/\" ||\n (pathname.length > 1 &&\n pathname[0] === \"/\" &&\n !pathname.includes(\"//\") &&\n !pathname.includes(\"/./\") &&\n !pathname.includes(\"/../\") &&\n !pathname.endsWith(\"/.\") &&\n !pathname.endsWith(\"/..\"))\n ) {\n return pathname;\n }\n\n const segments = pathname.split(\"/\");\n const resolved: string[] = [];\n\n for (const segment of segments) {\n if (segment === \"\" || segment === \".\") {\n // Skip empty segments (from // or leading /) and single-dot segments\n continue;\n }\n if (segment === \"..\") {\n // Go up one level, but never above root\n resolved.pop();\n } else {\n resolved.push(segment);\n }\n }\n\n return \"/\" + resolved.join(\"/\");\n}\n"],"mappings":";;;;;;;;;AAQA,SAAgB,qBAAqB,SAAiB,eAAiC;CACrF,OAAO,QAAQ,QACb,IAAI,OAAO,SAAS,gBAAgB,oBAAoB,GAAG,IAAI,KAAK,GACnE,SAAiB,mBAAmB,KAAK,CAC3C;;;;;;;;;;;;;AAcH,SAAgB,iBAAiB,UAA0B;CACzD,OAAO,SACJ,MAAM,IAAI,CACV,KAAK,QAAQ;EACZ,IAAI;GACF,OAAO,qBAAqB,mBAAmB,IAAI,EAAE,KAAK;UACpD;GACN,OAAO;;GAET,CACD,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;AAuBd,SAAgB,cAAc,UAA0B;CAEtD,IACE,aAAa,OACZ,SAAS,SAAS,KACjB,SAAS,OAAO,OAChB,CAAC,SAAS,SAAS,KAAK,IACxB,CAAC,SAAS,SAAS,MAAM,IACzB,CAAC,SAAS,SAAS,OAAO,IAC1B,CAAC,SAAS,SAAS,KAAK,IACxB,CAAC,SAAS,SAAS,MAAM,EAE3B,OAAO;CAGT,MAAM,WAAW,SAAS,MAAM,IAAI;CACpC,MAAM,WAAqB,EAAE;CAE7B,KAAK,MAAM,WAAW,UAAU;EAC9B,IAAI,YAAY,MAAM,YAAY,KAEhC;EAEF,IAAI,YAAY,MAEd,SAAS,KAAK;OAEd,SAAS,KAAK,QAAQ;;CAI1B,OAAO,MAAM,SAAS,KAAK,IAAI"}
1
+ {"version":3,"file":"normalize-path.js","names":[],"sources":["../../src/server/normalize-path.ts"],"sourcesContent":["/**\n * Re-encode path delimiter characters that were decoded by decodeURIComponent.\n * After decoding a URL segment, characters like / # ? \\ need to be re-encoded\n * so they don't change the path structure.\n *\n * Ported from Next.js: packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/router/utils/escape-path-delimiters.ts\n */\nexport function escapePathDelimiters(segment: string, escapeEncoded?: boolean): string {\n return segment.replace(\n new RegExp(`([/#?]${escapeEncoded ? \"|%(2f|23|3f|5c)\" : \"\"})`, \"gi\"),\n (char: string) => encodeURIComponent(char),\n );\n}\n\n/**\n * Decode a URL pathname segment-by-segment, preserving encoded path delimiters.\n * Non-ASCII characters (e.g. %C3%A9 -> e) are decoded, but structural characters\n * like %2F (/) %23 (#) %3F (?) %5C (\\) are re-encoded after decoding.\n *\n * This prevents encoded slashes from changing the path structure (e.g.\n * /admin%2Fpanel stays as a single segment, not /admin/panel).\n *\n * Ported from Next.js: packages/next/src/server/lib/router-utils/decode-path-params.ts\n * https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/decode-path-params.ts\n */\nexport function decodePathParams(pathname: string): string {\n return pathname\n .split(\"/\")\n .map((seg) => {\n try {\n return escapePathDelimiters(decodeURIComponent(seg), true);\n } catch {\n return seg;\n }\n })\n .join(\"/\");\n}\n\nexport function isInterceptionMatchedUrlPath(value: string): boolean {\n return (\n value.startsWith(\"/\") &&\n !value.startsWith(\"//\") &&\n !value.includes(\"?\") &&\n !value.includes(\"#\") &&\n !value.includes(\"\\0\")\n );\n}\n\n/**\n * Path normalization utility for request handling.\n *\n * Normalizes URL pathnames to a canonical form BEFORE any matching occurs\n * (middleware, routing, redirects, rewrites). This ensures middleware and\n * the router always see the same path, preventing path-confusion issues like\n * double-slash mismatches.\n *\n * Normalization rules:\n * 1. Collapse consecutive slashes: //foo///bar → /foo/bar\n * 2. Resolve single-dot segments: /foo/./bar → /foo/bar\n * 3. Resolve double-dot segments: /foo/../bar → /bar\n * 4. Ensure leading slash: foo/bar → /foo/bar\n * 5. Preserve root: / → /\n *\n * This function does NOT:\n * - Strip or add trailing slashes (handled separately by trailingSlash config)\n * - Decode percent-encoded characters (callers should decode before calling this)\n * - Lowercase the path (route matching is case-sensitive)\n */\nexport function normalizePath(pathname: string): string {\n // Fast path: already canonical (single leading /, no //, no /./, no /../)\n if (\n pathname === \"/\" ||\n (pathname.length > 1 &&\n pathname[0] === \"/\" &&\n !pathname.includes(\"//\") &&\n !pathname.includes(\"/./\") &&\n !pathname.includes(\"/../\") &&\n !pathname.endsWith(\"/.\") &&\n !pathname.endsWith(\"/..\"))\n ) {\n return pathname;\n }\n\n const segments = pathname.split(\"/\");\n const resolved: string[] = [];\n\n for (const segment of segments) {\n if (segment === \"\" || segment === \".\") {\n // Skip empty segments (from // or leading /) and single-dot segments\n continue;\n }\n if (segment === \"..\") {\n // Go up one level, but never above root\n resolved.pop();\n } else {\n resolved.push(segment);\n }\n }\n\n return \"/\" + resolved.join(\"/\");\n}\n"],"mappings":";;;;;;;;;AAQA,SAAgB,qBAAqB,SAAiB,eAAiC;CACrF,OAAO,QAAQ,QACb,IAAI,OAAO,SAAS,gBAAgB,oBAAoB,GAAG,IAAI,KAAK,GACnE,SAAiB,mBAAmB,KAAK,CAC3C;;;;;;;;;;;;;AAcH,SAAgB,iBAAiB,UAA0B;CACzD,OAAO,SACJ,MAAM,IAAI,CACV,KAAK,QAAQ;EACZ,IAAI;GACF,OAAO,qBAAqB,mBAAmB,IAAI,EAAE,KAAK;UACpD;GACN,OAAO;;GAET,CACD,KAAK,IAAI;;AAGd,SAAgB,6BAA6B,OAAwB;CACnE,OACE,MAAM,WAAW,IAAI,IACrB,CAAC,MAAM,WAAW,KAAK,IACvB,CAAC,MAAM,SAAS,IAAI,IACpB,CAAC,MAAM,SAAS,IAAI,IACpB,CAAC,MAAM,SAAS,KAAK;;;;;;;;;;;;;;;;;;;;;;AAwBzB,SAAgB,cAAc,UAA0B;CAEtD,IACE,aAAa,OACZ,SAAS,SAAS,KACjB,SAAS,OAAO,OAChB,CAAC,SAAS,SAAS,KAAK,IACxB,CAAC,SAAS,SAAS,MAAM,IACzB,CAAC,SAAS,SAAS,OAAO,IAC1B,CAAC,SAAS,SAAS,KAAK,IACxB,CAAC,SAAS,SAAS,MAAM,EAE3B,OAAO;CAGT,MAAM,WAAW,SAAS,MAAM,IAAI;CACpC,MAAM,WAAqB,EAAE;CAE7B,KAAK,MAAM,WAAW,UAAU;EAC9B,IAAI,YAAY,MAAM,YAAY,KAEhC;EAEF,IAAI,YAAY,MAEd,SAAS,KAAK;OAEd,SAAS,KAAK,QAAQ;;CAI1B,OAAO,MAAM,SAAS,KAAK,IAAI"}
@@ -1,3 +1,4 @@
1
+ import "./server-globals.js";
1
2
  import { internalServerErrorResponse } from "./http-error-responses.js";
2
3
  import { mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
3
4
  import { PagesBodyParseError } from "./pages-media-type.js";