@rangojs/router 0.0.0-experimental.9c9afef3 → 0.0.0-experimental.a014d2b7

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 (402) hide show
  1. package/AGENTS.md +8 -0
  2. package/README.md +245 -49
  3. package/dist/bin/rango.js +440 -133
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3373 -1176
  6. package/dist/vite/index.js.bak +5448 -0
  7. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  8. package/package.json +68 -14
  9. package/skills/api-client/SKILL.md +211 -0
  10. package/skills/breadcrumbs/SKILL.md +64 -2
  11. package/skills/bundle-analysis/SKILL.md +159 -0
  12. package/skills/cache-guide/SKILL.md +224 -32
  13. package/skills/caching/SKILL.md +279 -17
  14. package/skills/composability/SKILL.md +27 -3
  15. package/skills/css/SKILL.md +76 -0
  16. package/skills/debug-manifest/SKILL.md +4 -2
  17. package/skills/document-cache/SKILL.md +78 -55
  18. package/skills/handler-use/SKILL.md +364 -0
  19. package/skills/hooks/SKILL.md +250 -30
  20. package/skills/host-router/SKILL.md +83 -23
  21. package/skills/i18n/SKILL.md +276 -0
  22. package/skills/intercept/SKILL.md +87 -18
  23. package/skills/layout/SKILL.md +35 -9
  24. package/skills/links/SKILL.md +249 -17
  25. package/skills/loader/SKILL.md +235 -9
  26. package/skills/middleware/SKILL.md +52 -13
  27. package/skills/migrate-nextjs/SKILL.md +584 -0
  28. package/skills/migrate-react-router/SKILL.md +771 -0
  29. package/skills/mime-routes/SKILL.md +28 -1
  30. package/skills/observability/SKILL.md +172 -0
  31. package/skills/parallel/SKILL.md +77 -7
  32. package/skills/prerender/SKILL.md +172 -125
  33. package/skills/rango/SKILL.md +251 -22
  34. package/skills/react-compiler/SKILL.md +168 -0
  35. package/skills/response-routes/SKILL.md +123 -48
  36. package/skills/route/SKILL.md +70 -5
  37. package/skills/router-setup/SKILL.md +65 -8
  38. package/skills/scripts/SKILL.md +179 -0
  39. package/skills/server-actions/SKILL.md +775 -0
  40. package/skills/streams-and-websockets/SKILL.md +283 -0
  41. package/skills/tailwind/SKILL.md +27 -3
  42. package/skills/testing/SKILL.md +130 -0
  43. package/skills/testing/bindings.md +103 -0
  44. package/skills/testing/cache-prerender.md +127 -0
  45. package/skills/testing/client-components.md +124 -0
  46. package/skills/testing/e2e-parity.md +125 -0
  47. package/skills/testing/flight.md +91 -0
  48. package/skills/testing/handles.md +129 -0
  49. package/skills/testing/loader.md +128 -0
  50. package/skills/testing/middleware.md +99 -0
  51. package/skills/testing/render-handler.md +122 -0
  52. package/skills/testing/response-routes.md +95 -0
  53. package/skills/testing/reverse-and-types.md +84 -0
  54. package/skills/testing/server-actions.md +107 -0
  55. package/skills/testing/server-tree.md +128 -0
  56. package/skills/testing/setup.md +123 -0
  57. package/skills/typesafety/SKILL.md +322 -29
  58. package/skills/use-cache/SKILL.md +57 -14
  59. package/skills/view-transitions/SKILL.md +337 -0
  60. package/src/__augment-tests__/augment.ts +81 -0
  61. package/src/__augment-tests__/augmented.check.ts +116 -0
  62. package/src/__internal.ts +1 -66
  63. package/src/browser/action-coordinator.ts +53 -36
  64. package/src/browser/action-fence.ts +47 -0
  65. package/src/browser/app-shell.ts +39 -0
  66. package/src/browser/app-version.ts +14 -0
  67. package/src/browser/connection-warmup.ts +134 -0
  68. package/src/browser/cookie-name.ts +140 -0
  69. package/src/browser/event-controller.ts +192 -150
  70. package/src/browser/history-state.ts +21 -0
  71. package/src/browser/index.ts +3 -3
  72. package/src/browser/invalidate-client-cache.ts +52 -0
  73. package/src/browser/navigation-bridge.ts +131 -30
  74. package/src/browser/navigation-client.ts +186 -100
  75. package/src/browser/navigation-store-handle.ts +38 -0
  76. package/src/browser/navigation-store.ts +157 -74
  77. package/src/browser/navigation-transaction.ts +9 -59
  78. package/src/browser/network-error-handler.ts +34 -7
  79. package/src/browser/partial-update.ts +165 -112
  80. package/src/browser/prefetch/cache.ts +205 -62
  81. package/src/browser/prefetch/fetch.ts +347 -39
  82. package/src/browser/prefetch/queue.ts +42 -8
  83. package/src/browser/rango-state.ts +158 -76
  84. package/src/browser/react/Link.tsx +102 -15
  85. package/src/browser/react/NavigationProvider.tsx +295 -119
  86. package/src/browser/react/ScrollRestoration.tsx +10 -6
  87. package/src/browser/react/context.ts +7 -2
  88. package/src/browser/react/deferred-handle-resolution.ts +75 -0
  89. package/src/browser/react/filter-segment-order.ts +66 -7
  90. package/src/browser/react/index.ts +0 -48
  91. package/src/browser/react/location-state-shared.ts +178 -8
  92. package/src/browser/react/location-state.ts +39 -14
  93. package/src/browser/react/use-action.ts +6 -15
  94. package/src/browser/react/use-handle.ts +23 -69
  95. package/src/browser/react/use-href.tsx +8 -1
  96. package/src/browser/react/use-link-status.ts +33 -8
  97. package/src/browser/react/use-navigation.ts +32 -7
  98. package/src/browser/react/use-params.ts +20 -10
  99. package/src/browser/react/use-reverse.ts +106 -0
  100. package/src/browser/react/use-router.ts +46 -11
  101. package/src/browser/react/use-search-params.ts +0 -5
  102. package/src/browser/react/use-segments.ts +11 -21
  103. package/src/browser/response-adapter.ts +99 -8
  104. package/src/browser/rsc-router.tsx +114 -24
  105. package/src/browser/scroll-restoration.ts +37 -22
  106. package/src/browser/segment-reconciler.ts +36 -14
  107. package/src/browser/segment-structure-assert.ts +2 -2
  108. package/src/browser/server-action-bridge.ts +222 -72
  109. package/src/browser/types.ts +102 -12
  110. package/src/browser/validate-redirect-origin.ts +43 -16
  111. package/src/build/collect-fallback-refs.ts +107 -0
  112. package/src/build/generate-manifest.ts +65 -40
  113. package/src/build/generate-route-types.ts +5 -1
  114. package/src/build/index.ts +8 -2
  115. package/src/build/prefix-tree-utils.ts +123 -0
  116. package/src/build/route-trie.ts +165 -36
  117. package/src/build/route-types/ast-route-extraction.ts +15 -8
  118. package/src/build/route-types/codegen.ts +16 -5
  119. package/src/build/route-types/include-resolution.ts +125 -24
  120. package/src/build/route-types/param-extraction.ts +6 -3
  121. package/src/build/route-types/per-module-writer.ts +22 -6
  122. package/src/build/route-types/router-processing.ts +260 -94
  123. package/src/build/route-types/scan-filter.ts +9 -2
  124. package/src/build/route-types/source-scan.ts +216 -0
  125. package/src/build/runtime-discovery.ts +9 -20
  126. package/src/cache/cache-error.ts +104 -0
  127. package/src/cache/cache-key-utils.ts +29 -13
  128. package/src/cache/cache-policy.ts +108 -34
  129. package/src/cache/cache-runtime.ts +224 -41
  130. package/src/cache/cache-scope.ts +188 -82
  131. package/src/cache/cache-tag.ts +103 -0
  132. package/src/cache/cf/cf-base64.ts +33 -0
  133. package/src/cache/cf/cf-cache-constants.ts +127 -0
  134. package/src/cache/cf/cf-cache-store.ts +1989 -378
  135. package/src/cache/cf/cf-cache-types.ts +349 -0
  136. package/src/cache/cf/cf-kv-utils.ts +46 -0
  137. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  138. package/src/cache/cf/index.ts +6 -16
  139. package/src/cache/document-cache.ts +89 -21
  140. package/src/cache/handle-snapshot.ts +70 -0
  141. package/src/cache/index.ts +10 -20
  142. package/src/cache/memory-segment-store.ts +136 -37
  143. package/src/cache/profile-registry.ts +46 -31
  144. package/src/cache/read-through-swr.ts +56 -12
  145. package/src/cache/segment-codec.ts +9 -17
  146. package/src/cache/tag-invalidation.ts +230 -0
  147. package/src/cache/types.ts +37 -100
  148. package/src/client.rsc.tsx +44 -21
  149. package/src/client.tsx +119 -290
  150. package/src/cloudflare/index.ts +11 -0
  151. package/src/cloudflare/tracing.ts +109 -0
  152. package/src/component-utils.ts +19 -0
  153. package/src/components/DefaultDocument.tsx +8 -2
  154. package/src/context-var.ts +18 -6
  155. package/src/decode-loader-results.ts +52 -0
  156. package/src/defer.ts +196 -0
  157. package/src/deps/ssr.ts +0 -1
  158. package/src/encode-kv.ts +49 -0
  159. package/src/errors.ts +30 -4
  160. package/src/escape-script.ts +52 -0
  161. package/src/handle.ts +70 -22
  162. package/src/handles/MetaTags.tsx +62 -19
  163. package/src/handles/Scripts.tsx +183 -0
  164. package/src/handles/breadcrumbs.ts +37 -8
  165. package/src/handles/is-thenable.ts +19 -0
  166. package/src/handles/meta.ts +51 -40
  167. package/src/handles/script.ts +244 -0
  168. package/src/host/cookie-handler.ts +9 -60
  169. package/src/host/errors.ts +0 -24
  170. package/src/host/index.ts +8 -2
  171. package/src/host/pattern-matcher.ts +23 -52
  172. package/src/host/router.ts +107 -99
  173. package/src/host/testing.ts +40 -27
  174. package/src/host/types.ts +37 -4
  175. package/src/host/utils.ts +1 -1
  176. package/src/href-client.ts +137 -22
  177. package/src/index.rsc.ts +99 -13
  178. package/src/index.ts +139 -19
  179. package/src/internal-debug.ts +11 -10
  180. package/src/loader-store.ts +500 -0
  181. package/src/loader.rsc.ts +20 -13
  182. package/src/loader.ts +12 -11
  183. package/src/missing-id-error.ts +68 -0
  184. package/src/outlet-context.ts +1 -1
  185. package/src/outlet-provider.tsx +1 -5
  186. package/src/prerender/param-hash.ts +16 -16
  187. package/src/prerender/store.ts +37 -41
  188. package/src/prerender.ts +198 -82
  189. package/src/redirect-origin.ts +100 -0
  190. package/src/regex-escape.ts +8 -0
  191. package/src/render-error-thrower.tsx +20 -0
  192. package/src/response-utils.ts +62 -0
  193. package/src/reverse.ts +65 -15
  194. package/src/root-error-boundary.tsx +1 -19
  195. package/src/route-content-wrapper.tsx +19 -77
  196. package/src/route-definition/dsl-helpers.ts +461 -304
  197. package/src/route-definition/helper-factories.ts +28 -140
  198. package/src/route-definition/helpers-types.ts +143 -69
  199. package/src/route-definition/index.ts +4 -2
  200. package/src/route-definition/redirect.ts +51 -10
  201. package/src/route-definition/resolve-handler-use.ts +160 -0
  202. package/src/route-definition/use-item-types.ts +29 -0
  203. package/src/route-map-builder.ts +0 -16
  204. package/src/route-types.ts +37 -46
  205. package/src/router/basename.ts +14 -0
  206. package/src/router/content-negotiation.ts +164 -17
  207. package/src/router/error-handling.ts +45 -18
  208. package/src/router/find-match.ts +44 -23
  209. package/src/router/handler-context.ts +52 -31
  210. package/src/router/instrument.ts +350 -0
  211. package/src/router/intercept-resolution.ts +48 -24
  212. package/src/router/lazy-includes.ts +15 -52
  213. package/src/router/loader-resolution.ts +268 -56
  214. package/src/router/logging.ts +0 -6
  215. package/src/router/manifest.ts +40 -42
  216. package/src/router/match-api.ts +124 -204
  217. package/src/router/match-context.ts +0 -22
  218. package/src/router/match-handlers.ts +58 -58
  219. package/src/router/match-middleware/background-revalidation.ts +40 -24
  220. package/src/router/match-middleware/cache-lookup.ts +170 -276
  221. package/src/router/match-middleware/cache-store.ts +64 -52
  222. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  223. package/src/router/match-middleware/segment-resolution.ts +45 -14
  224. package/src/router/match-pipelines.ts +1 -42
  225. package/src/router/match-result.ts +87 -39
  226. package/src/router/metrics.ts +0 -34
  227. package/src/router/middleware-types.ts +7 -140
  228. package/src/router/middleware.ts +266 -169
  229. package/src/router/navigation-snapshot.ts +131 -0
  230. package/src/router/params-util.ts +23 -0
  231. package/src/router/pattern-matching.ts +132 -90
  232. package/src/router/prefetch-cache-ttl.ts +51 -0
  233. package/src/router/prerender-match.ts +195 -56
  234. package/src/router/preview-match.ts +32 -102
  235. package/src/router/request-classification.ts +276 -0
  236. package/src/router/revalidation.ts +123 -73
  237. package/src/router/route-snapshot.ts +244 -0
  238. package/src/router/router-context.ts +3 -28
  239. package/src/router/router-interfaces.ts +115 -35
  240. package/src/router/router-options.ts +172 -15
  241. package/src/router/router-registry.ts +2 -5
  242. package/src/router/segment-resolution/fresh.ts +162 -84
  243. package/src/router/segment-resolution/helpers.ts +86 -6
  244. package/src/router/segment-resolution/loader-cache.ts +76 -39
  245. package/src/router/segment-resolution/revalidation.ts +351 -321
  246. package/src/router/segment-resolution/static-store.ts +19 -5
  247. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  248. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  249. package/src/router/segment-resolution.ts +5 -1
  250. package/src/router/segment-wrappers.ts +6 -5
  251. package/src/router/state-cookie-name.ts +33 -0
  252. package/src/router/substitute-pattern-params.ts +56 -0
  253. package/src/router/telemetry-otel.ts +161 -199
  254. package/src/router/telemetry.ts +96 -19
  255. package/src/router/timeout.ts +0 -20
  256. package/src/router/tracing.ts +206 -0
  257. package/src/router/trie-matching.ts +163 -59
  258. package/src/router/types.ts +9 -63
  259. package/src/router/url-params.ts +44 -0
  260. package/src/router.ts +157 -54
  261. package/src/rsc/handler-context.ts +3 -2
  262. package/src/rsc/handler.ts +655 -529
  263. package/src/rsc/helpers.ts +168 -46
  264. package/src/rsc/index.ts +2 -5
  265. package/src/rsc/json-route-result.ts +38 -0
  266. package/src/rsc/loader-fetch.ts +122 -31
  267. package/src/rsc/manifest-init.ts +33 -42
  268. package/src/rsc/origin-guard.ts +39 -25
  269. package/src/rsc/progressive-enhancement.ts +131 -14
  270. package/src/rsc/redirect-guard.ts +99 -0
  271. package/src/rsc/response-cache-serve.ts +238 -0
  272. package/src/rsc/response-error.ts +79 -12
  273. package/src/rsc/response-route-handler.ts +99 -189
  274. package/src/rsc/rsc-rendering.ts +109 -74
  275. package/src/rsc/runtime-warnings.ts +23 -10
  276. package/src/rsc/server-action.ts +287 -115
  277. package/src/rsc/ssr-setup.ts +18 -2
  278. package/src/rsc/transition-gate.ts +89 -0
  279. package/src/rsc/types.ts +29 -9
  280. package/src/runtime-env.ts +18 -0
  281. package/src/search-params.ts +35 -30
  282. package/src/segment-content-promise.ts +67 -0
  283. package/src/segment-loader-promise.ts +149 -0
  284. package/src/segment-system.tsx +236 -202
  285. package/src/serialize.ts +243 -0
  286. package/src/server/context.ts +224 -52
  287. package/src/server/cookie-parse.ts +32 -0
  288. package/src/server/cookie-store.ts +80 -5
  289. package/src/server/handle-store.ts +40 -38
  290. package/src/server/loader-registry.ts +38 -46
  291. package/src/server/request-context.ts +401 -173
  292. package/src/ssr/index.tsx +24 -16
  293. package/src/static-handler.ts +27 -18
  294. package/src/testing/cache-status.ts +162 -0
  295. package/src/testing/collect-handle.ts +40 -0
  296. package/src/testing/dispatch.ts +701 -0
  297. package/src/testing/dom.entry.ts +22 -0
  298. package/src/testing/e2e/fixture.ts +188 -0
  299. package/src/testing/e2e/index.ts +128 -0
  300. package/src/testing/e2e/matchers.ts +35 -0
  301. package/src/testing/e2e/page-helpers.ts +272 -0
  302. package/src/testing/e2e/parity.ts +387 -0
  303. package/src/testing/e2e/server.ts +195 -0
  304. package/src/testing/flight-matchers.ts +97 -0
  305. package/src/testing/flight-normalize.ts +11 -0
  306. package/src/testing/flight-runtime.d.ts +57 -0
  307. package/src/testing/flight-tree.ts +682 -0
  308. package/src/testing/flight.entry.ts +52 -0
  309. package/src/testing/flight.ts +257 -0
  310. package/src/testing/generated-routes.ts +183 -0
  311. package/src/testing/index.ts +105 -0
  312. package/src/testing/internal/context.ts +371 -0
  313. package/src/testing/internal/flight-client-globals.ts +30 -0
  314. package/src/testing/internal/seed-vars.ts +54 -0
  315. package/src/testing/render-handler.ts +357 -0
  316. package/src/testing/render-route.tsx +581 -0
  317. package/src/testing/run-loader.ts +385 -0
  318. package/src/testing/run-middleware.ts +205 -0
  319. package/src/testing/run-transition-when.ts +164 -0
  320. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  321. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  322. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  323. package/src/testing/vitest-stubs/version.ts +5 -0
  324. package/src/testing/vitest.ts +305 -0
  325. package/src/theme/ThemeProvider.tsx +20 -58
  326. package/src/theme/ThemeScript.tsx +7 -9
  327. package/src/theme/constants.ts +52 -13
  328. package/src/theme/index.ts +0 -7
  329. package/src/theme/theme-context.ts +1 -5
  330. package/src/theme/theme-script.ts +22 -21
  331. package/src/theme/use-theme.ts +0 -3
  332. package/src/types/boundaries.ts +0 -35
  333. package/src/types/cache-types.ts +17 -8
  334. package/src/types/error-types.ts +30 -90
  335. package/src/types/global-namespace.ts +54 -41
  336. package/src/types/handler-context.ts +125 -71
  337. package/src/types/index.ts +3 -10
  338. package/src/types/loader-types.ts +40 -11
  339. package/src/types/request-scope.ts +112 -0
  340. package/src/types/route-config.ts +6 -50
  341. package/src/types/route-entry.ts +12 -7
  342. package/src/types/segments.ts +136 -15
  343. package/src/urls/include-helper.ts +33 -70
  344. package/src/urls/index.ts +1 -11
  345. package/src/urls/path-helper-types.ts +68 -18
  346. package/src/urls/path-helper.ts +57 -111
  347. package/src/urls/pattern-types.ts +48 -19
  348. package/src/urls/response-types.ts +25 -22
  349. package/src/urls/type-extraction.ts +58 -139
  350. package/src/urls/urls-function.ts +1 -19
  351. package/src/use-loader.tsx +346 -89
  352. package/src/vite/debug.ts +185 -0
  353. package/src/vite/discovery/bundle-postprocess.ts +36 -38
  354. package/src/vite/discovery/discover-routers.ts +130 -85
  355. package/src/vite/discovery/discovery-errors.ts +194 -0
  356. package/src/vite/discovery/gate-state.ts +171 -0
  357. package/src/vite/discovery/prerender-collection.ts +214 -132
  358. package/src/vite/discovery/route-types-writer.ts +40 -84
  359. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  360. package/src/vite/discovery/state.ts +57 -4
  361. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  362. package/src/vite/index.ts +6 -0
  363. package/src/vite/inject-client-debug.ts +36 -0
  364. package/src/vite/plugin-types.ts +178 -5
  365. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  366. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  367. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  368. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  369. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  370. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  371. package/src/vite/plugins/expose-action-id.ts +48 -95
  372. package/src/vite/plugins/expose-id-utils.ts +96 -51
  373. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  374. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  375. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  376. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  377. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  378. package/src/vite/plugins/performance-tracks.ts +64 -170
  379. package/src/vite/plugins/refresh-cmd.ts +89 -27
  380. package/src/vite/plugins/use-cache-transform.ts +73 -83
  381. package/src/vite/plugins/version-injector.ts +40 -29
  382. package/src/vite/plugins/version-plugin.ts +37 -40
  383. package/src/vite/plugins/virtual-entries.ts +39 -25
  384. package/src/vite/rango.ts +118 -114
  385. package/src/vite/router-discovery.ts +941 -142
  386. package/src/vite/utils/ast-handler-extract.ts +26 -35
  387. package/src/vite/utils/banner.ts +1 -1
  388. package/src/vite/utils/bundle-analysis.ts +10 -15
  389. package/src/vite/utils/client-chunks.ts +184 -0
  390. package/src/vite/utils/directive-prologue.ts +40 -0
  391. package/src/vite/utils/forward-user-plugins.ts +171 -0
  392. package/src/vite/utils/manifest-utils.ts +4 -59
  393. package/src/vite/utils/package-resolution.ts +20 -52
  394. package/src/vite/utils/prerender-utils.ts +81 -34
  395. package/src/vite/utils/shared-utils.ts +92 -42
  396. package/src/browser/action-response-classifier.ts +0 -99
  397. package/src/browser/debug-channel.ts +0 -93
  398. package/src/browser/react/use-client-cache.ts +0 -58
  399. package/src/browser/shallow.ts +0 -40
  400. package/src/handles/index.ts +0 -7
  401. package/src/network-error-thrower.tsx +0 -23
  402. package/src/router/middleware-cookies.ts +0 -55
@@ -1,10 +1,3 @@
1
- /**
2
- * Match API
3
- *
4
- * Extracted from createRouter closure. Contains match context creation functions
5
- * and the matchError function for error boundary resolution.
6
- */
7
-
8
1
  import { CacheScope, createCacheScope } from "../cache/cache-scope.js";
9
2
  import { RouteNotFoundError } from "../errors";
10
3
  import {
@@ -22,10 +15,10 @@ import { collectRouteMiddleware } from "./middleware.js";
22
15
  import { traverseBack } from "./pattern-matching.js";
23
16
  import { DefaultErrorFallback } from "../default-error-boundary.js";
24
17
  import {
25
- EntryData,
26
- LoaderEntry,
18
+ type EntryData,
19
+ type LoaderEntry,
27
20
  getContext,
28
- InterceptSelectorContext,
21
+ type InterceptSelectorContext,
29
22
  } from "../server/context";
30
23
  import type { ErrorBoundaryHandler, ErrorInfo, MatchResult } from "../types";
31
24
  import type { ReactNode } from "react";
@@ -36,7 +29,14 @@ import {
36
29
  setRequestContextPrevRouteKey,
37
30
  } from "../server/request-context.js";
38
31
  import { isAutoGeneratedRouteName } from "../route-name.js";
32
+ import type { DefaultRouteName } from "../types/global-namespace.js";
39
33
  import { debugLog, debugWarn } from "./logging.js";
34
+ import {
35
+ resolveRoute,
36
+ ensureFullRouteSnapshot,
37
+ type RouteSnapshot,
38
+ } from "./route-snapshot.js";
39
+ import { resolveNavigation } from "./navigation-snapshot.js";
40
40
 
41
41
  /**
42
42
  * Create match context for full requests (document/SSR).
@@ -52,58 +52,33 @@ export async function createMatchContextForFull<TEnv>(
52
52
 
53
53
  const metricsStore = deps.getMetricsStore();
54
54
 
55
- const routeMatchStart = metricsStore ? performance.now() : 0;
56
- const matched = deps.findMatch(pathname, metricsStore);
57
- if (metricsStore) {
58
- metricsStore.metrics.push({
59
- label: "route-matching",
60
- duration: performance.now() - routeMatchStart,
61
- startTime: routeMatchStart - metricsStore.requestStart,
62
- });
63
- }
55
+ const result = await resolveRoute<TEnv>(pathname, {
56
+ findMatch: (p) => deps.findMatch(p, metricsStore),
57
+ metricsStore,
58
+ isSSR: true,
59
+ });
64
60
 
65
- if (!matched) {
61
+ if (!result) {
66
62
  throw new RouteNotFoundError(`No route matched for ${pathname}`, {
67
63
  cause: { pathname, method: request.method },
68
64
  });
69
65
  }
70
66
 
71
- if (matched.redirectTo) {
67
+ if (result.type === "redirect") {
72
68
  return {
73
69
  type: "redirect",
74
- redirectUrl: matched.redirectTo + url.search,
70
+ redirectUrl: result.redirectTo + url.search,
75
71
  };
76
72
  }
77
73
 
78
- const manifestStart = metricsStore ? performance.now() : 0;
79
- const manifestEntry = await loadManifest(
80
- matched.entry,
81
- matched.routeKey,
82
- pathname,
83
- metricsStore,
84
- true,
85
- );
86
- if (metricsStore) {
87
- metricsStore.metrics.push({
88
- label: "manifest-loading",
89
- duration: performance.now() - manifestStart,
90
- startTime: manifestStart - metricsStore.requestStart,
91
- });
92
- }
74
+ const snapshot = result.snapshot;
93
75
 
94
- if (
95
- manifestEntry.type === "route" &&
96
- manifestEntry.prerenderDef?.options?.passthrough === true
97
- ) {
76
+ const { matched } = snapshot;
77
+
78
+ if (snapshot.isPassthrough) {
98
79
  matched.pt = true;
99
80
  }
100
81
 
101
- const routeMiddleware = collectRouteMiddleware(
102
- traverseBack(manifestEntry),
103
- matched.params,
104
- );
105
-
106
- // Clean URL without internal _rsc* params for userland access
107
82
  const cleanUrl = stripInternalParams(url);
108
83
 
109
84
  const handlerContext = createHandlerContext(
@@ -134,14 +109,6 @@ export async function createMatchContextForFull<TEnv>(
134
109
  Store.metrics = metricsStore;
135
110
  }
136
111
 
137
- const entries = [...traverseBack(manifestEntry)];
138
- let cacheScope: CacheScope | null = null;
139
- for (const entry of entries) {
140
- if (entry.cache) {
141
- cacheScope = createCacheScope(entry.cache, cacheScope);
142
- }
143
- }
144
-
145
112
  return {
146
113
  request,
147
114
  url: cleanUrl,
@@ -154,12 +121,10 @@ export async function createMatchContextForFull<TEnv>(
154
121
  prevParams: {},
155
122
  prevMatch: null,
156
123
  matched,
157
- manifestEntry,
158
- entries,
124
+ manifestEntry: snapshot.manifestEntry,
125
+ entries: snapshot.entries,
159
126
  routeKey: matched.routeKey,
160
- localRouteName: matched.routeKey.includes(".")
161
- ? matched.routeKey.split(".").pop()!
162
- : matched.routeKey,
127
+ localRouteName: snapshot.localRouteName,
163
128
  handlerContext,
164
129
  loaderPromises,
165
130
  routeMap: deps.getRouteMap(),
@@ -175,16 +140,16 @@ export async function createMatchContextForFull<TEnv>(
175
140
  segments: { path: [], ids: [] },
176
141
  toRouteName:
177
142
  matched.routeKey && !isAutoGeneratedRouteName(matched.routeKey)
178
- ? matched.routeKey
143
+ ? (matched.routeKey as DefaultRouteName)
179
144
  : undefined,
180
145
  },
181
146
  isSameRouteNavigation: false,
182
147
  interceptResult: null,
183
- cacheScope,
148
+ cacheScope: snapshot.cacheScope,
184
149
  isIntercept: false,
185
150
  actionContext: undefined,
186
151
  isAction: false,
187
- routeMiddleware,
152
+ routeMiddleware: snapshot.routeMiddleware,
188
153
  isFullMatch: true,
189
154
  };
190
155
  }
@@ -204,104 +169,80 @@ export async function createMatchContextForPartial<TEnv>(
204
169
 
205
170
  const metricsStore = deps.getMetricsStore();
206
171
 
207
- const clientSegmentIds =
208
- url.searchParams.get("_rsc_segments")?.split(",").filter(Boolean) || [];
209
- const stale = url.searchParams.get("_rsc_stale") === "true";
210
- const previousUrl =
211
- request.headers.get("X-RSC-Router-Client-Path") ||
212
- request.headers.get("Referer");
213
- const interceptSourceUrl = request.headers.get(
214
- "X-RSC-Router-Intercept-Source",
215
- );
172
+ const isHmr = !!request.headers.get("X-RSC-HMR");
216
173
 
217
174
  // HMR: clear manifest cache so stale handler references are discarded
218
- if (request.headers.get("X-RSC-HMR")) {
175
+ if (isHmr) {
219
176
  clearManifestCache();
220
177
  }
221
178
 
222
- if (!previousUrl) {
223
- return null;
224
- }
225
-
226
- let prevUrl: URL;
227
- try {
228
- prevUrl = new URL(previousUrl, url.origin);
229
- } catch {
230
- return null;
231
- }
179
+ // Reuse the classified snapshot when available and not invalidated by HMR.
180
+ // classifyRequest already called resolveRoute(lite) with isSSR=false, which
181
+ // matches the partial path. On HMR, discard to pick up manifest changes.
182
+ const classifiedRoute = isHmr
183
+ ? undefined
184
+ : getRequestContext()?._classifiedRoute;
185
+
186
+ // Time route matching. On the reuse path, only nav findMatch calls are new
187
+ // (current-route findMatch and manifest-loading were already timed during
188
+ // classifyRequest via its metricsStore). On the fresh path, all findMatch
189
+ // calls are measured together.
190
+ const routeMatchStart = metricsStore ? performance.now() : 0;
232
191
 
233
- let interceptContextUrl: URL;
234
- try {
235
- interceptContextUrl = interceptSourceUrl
236
- ? new URL(interceptSourceUrl, url.origin)
237
- : prevUrl;
238
- } catch {
239
- interceptContextUrl = prevUrl;
240
- }
192
+ let snapshot: RouteSnapshot<TEnv>;
193
+ if (classifiedRoute && classifiedRoute.manifestEntry) {
194
+ snapshot = ensureFullRouteSnapshot(classifiedRoute);
195
+ } else {
196
+ const result = await resolveRoute<TEnv>(pathname, {
197
+ findMatch: (p) => deps.findMatch(p, metricsStore),
198
+ metricsStore,
199
+ isSSR: false,
200
+ skipRouteMatchMetric: true,
201
+ });
241
202
 
242
- const routeMatchStart = metricsStore ? performance.now() : 0;
243
- const prevMatch = deps.findMatch(prevUrl.pathname);
244
- const prevParams = prevMatch?.params || {};
245
- const interceptContextMatch = interceptSourceUrl
246
- ? deps.findMatch(interceptContextUrl.pathname)
247
- : prevMatch;
203
+ if (!result) {
204
+ throw new RouteNotFoundError(`No route matched for ${pathname}`, {
205
+ cause: { pathname, method: request.method },
206
+ });
207
+ }
248
208
 
249
- const matched = deps.findMatch(pathname, metricsStore);
209
+ if (result.type === "redirect") {
210
+ return null;
211
+ }
250
212
 
251
- if (metricsStore) {
252
- metricsStore.metrics.push({
253
- label: "route-matching",
254
- duration: performance.now() - routeMatchStart,
255
- startTime: routeMatchStart - metricsStore.requestStart,
256
- });
213
+ snapshot = result.snapshot;
257
214
  }
258
215
 
259
- if (!matched) {
260
- throw new RouteNotFoundError(`No route matched for ${pathname}`, {
261
- cause: { pathname, method: request.method, previousUrl },
262
- });
263
- }
216
+ const { matched } = snapshot;
264
217
 
265
- if (matched.redirectTo) {
266
- return null;
218
+ // Backward compat: downstream middleware reads matched.pt
219
+ if (snapshot.isPassthrough) {
220
+ matched.pt = true;
267
221
  }
268
222
 
269
- if (prevMatch && prevMatch.entry !== matched.entry && !matched.pr) {
270
- debugLog("matchPartial", "route group changed", {
271
- from: prevMatch.routeKey,
272
- to: matched.routeKey,
273
- });
223
+ const nav = resolveNavigation(request, url, matched.routeKey, {
224
+ findMatch: deps.findMatch,
225
+ });
226
+ if (!nav) {
227
+ return null;
274
228
  }
275
229
 
276
- const manifestStart = metricsStore ? performance.now() : 0;
277
- const manifestEntry = await loadManifest(
278
- matched.entry,
279
- matched.routeKey,
280
- pathname,
281
- metricsStore,
282
- false,
283
- );
284
230
  if (metricsStore) {
231
+ const isReuse = !!classifiedRoute;
285
232
  metricsStore.metrics.push({
286
- label: "manifest-loading",
287
- duration: performance.now() - manifestStart,
288
- startTime: manifestStart - metricsStore.requestStart,
233
+ label: isReuse ? "route-matching:nav" : "route-matching",
234
+ duration: performance.now() - routeMatchStart,
235
+ startTime: routeMatchStart - metricsStore.requestStart,
289
236
  });
290
237
  }
291
238
 
292
- if (
293
- manifestEntry.type === "route" &&
294
- manifestEntry.prerenderDef?.options?.passthrough === true
295
- ) {
296
- matched.pt = true;
239
+ if (nav.prevMatch && nav.prevMatch.entry !== matched.entry && !matched.pr) {
240
+ debugLog("matchPartial", "route group changed", {
241
+ from: nav.prevMatch.routeKey,
242
+ to: matched.routeKey,
243
+ });
297
244
  }
298
245
 
299
- const routeMiddleware = collectRouteMiddleware(
300
- traverseBack(manifestEntry),
301
- matched.params,
302
- );
303
-
304
- // Clean URL without internal _rsc* params for userland access
305
246
  const cleanUrl = stripInternalParams(url);
306
247
 
307
248
  const handlerContext = createHandlerContext(
@@ -317,9 +258,8 @@ export async function createMatchContextForPartial<TEnv>(
317
258
  matched.pt === true,
318
259
  );
319
260
 
320
- const clientSegmentSet = new Set(clientSegmentIds);
321
261
  debugLog("matchPartial", "client segments", {
322
- segments: Array.from(clientSegmentSet),
262
+ segments: Array.from(nav.clientSegmentSet),
323
263
  });
324
264
 
325
265
  const loaderPromises = new Map<string, Promise<any>>();
@@ -337,100 +277,79 @@ export async function createMatchContextForPartial<TEnv>(
337
277
  Store.metrics = metricsStore;
338
278
  }
339
279
 
340
- const isSameRouteNavigation = !!(
341
- interceptContextMatch && interceptContextMatch.routeKey === matched.routeKey
342
- );
343
-
344
- if (interceptSourceUrl) {
280
+ if (nav.hasInterceptSource) {
345
281
  debugLog("matchPartial.intercept", "intercept context detected", {
346
282
  currentUrl: pathname,
347
- interceptSource: interceptSourceUrl,
348
- contextRoute: interceptContextMatch?.routeKey,
283
+ interceptSource: nav.interceptContextUrl.href,
284
+ contextRoute: nav.interceptContextMatch?.routeKey,
349
285
  currentRoute: matched.routeKey,
350
- sameRouteNavigation: isSameRouteNavigation,
286
+ sameRouteNavigation: nav.isSameRouteNavigation,
351
287
  });
352
288
  }
353
289
 
354
- const localRouteName = matched.routeKey.includes(".")
355
- ? matched.routeKey.split(".").pop()!
356
- : matched.routeKey;
357
-
358
- const filteredSegmentIds = clientSegmentIds.filter((id) => {
359
- if (id.includes(".@")) return false;
360
- if (/D\d+\./.test(id)) return false;
361
- return true;
362
- });
363
- const effectiveFromUrl = interceptSourceUrl ? interceptContextUrl : prevUrl;
364
- const effectiveFromMatch = interceptSourceUrl
365
- ? interceptContextMatch
366
- : prevMatch;
367
-
368
- // Store previous route key on the request context for revalidation
369
- // fromRouteName. Uses effectiveFromMatch so intercept-source navigations
370
- // see the intercept origin route, not the plain previous URL route.
371
- setRequestContextPrevRouteKey(effectiveFromMatch?.routeKey);
290
+ setRequestContextPrevRouteKey(
291
+ nav.effectiveFromMatch?.routeKey,
292
+ nav.effectiveFromUrl,
293
+ nav.effectiveFromMatch?.params ?? nav.prevParams,
294
+ );
372
295
 
373
296
  const interceptSelectorContext: InterceptSelectorContext = {
374
- from: effectiveFromUrl,
297
+ from: nav.effectiveFromUrl,
375
298
  to: cleanUrl,
376
299
  params: matched.params,
377
300
  request,
378
301
  env,
379
302
  segments: {
380
- path: effectiveFromUrl.pathname.split("/").filter(Boolean),
381
- ids: filteredSegmentIds,
303
+ path: nav.effectiveFromUrl.pathname.split("/").filter(Boolean),
304
+ ids: nav.filteredSegmentIds,
382
305
  },
383
306
  fromRouteName:
384
- effectiveFromMatch?.routeKey &&
385
- !isAutoGeneratedRouteName(effectiveFromMatch.routeKey)
386
- ? effectiveFromMatch.routeKey
307
+ nav.effectiveFromMatch?.routeKey &&
308
+ !isAutoGeneratedRouteName(nav.effectiveFromMatch.routeKey)
309
+ ? (nav.effectiveFromMatch.routeKey as DefaultRouteName)
387
310
  : undefined,
388
311
  toRouteName:
389
312
  matched.routeKey && !isAutoGeneratedRouteName(matched.routeKey)
390
- ? matched.routeKey
313
+ ? (matched.routeKey as DefaultRouteName)
391
314
  : undefined,
392
315
  };
393
316
  const isAction = !!actionContext;
394
317
 
395
- const clientHasInterceptSegments = [...clientSegmentSet].some((id) =>
318
+ const clientHasInterceptSegments = [...nav.clientSegmentSet].some((id) =>
396
319
  id.includes(".@"),
397
320
  );
398
321
  const skipInterceptForAction = isAction && !clientHasInterceptSegments;
399
322
  const interceptResult =
400
- isSameRouteNavigation || skipInterceptForAction
323
+ nav.isSameRouteNavigation || skipInterceptForAction
401
324
  ? null
402
325
  : findInterceptForRoute(
403
326
  matched.routeKey,
404
- manifestEntry.parent,
327
+ snapshot.manifestEntry.parent,
405
328
  interceptSelectorContext,
406
329
  isAction,
407
330
  ) ||
408
- (localRouteName !== matched.routeKey
331
+ (snapshot.localRouteName !== matched.routeKey
409
332
  ? findInterceptForRoute(
410
- localRouteName,
411
- manifestEntry.parent,
333
+ snapshot.localRouteName,
334
+ snapshot.manifestEntry.parent,
412
335
  interceptSelectorContext,
413
336
  isAction,
414
337
  )
415
338
  : null);
416
339
 
340
+ // Make a mutable copy of clientSegmentSet so we can delete entries
341
+ // for same-route navigation forcing
342
+ const clientSegmentSet = new Set(nav.clientSegmentSet);
343
+
417
344
  if (
418
- isSameRouteNavigation &&
419
- manifestEntry.type === "route" &&
420
- interceptSourceUrl
345
+ nav.isSameRouteNavigation &&
346
+ snapshot.manifestEntry.type === "route" &&
347
+ nav.hasInterceptSource
421
348
  ) {
422
349
  debugLog("matchPartial.intercept", "forcing route segment render", {
423
- segmentId: manifestEntry.shortCode,
350
+ segmentId: snapshot.manifestEntry.shortCode,
424
351
  });
425
- clientSegmentSet.delete(manifestEntry.shortCode);
426
- }
427
-
428
- const entries = [...traverseBack(manifestEntry)];
429
- let cacheScope: CacheScope | null = null;
430
- for (const entry of entries) {
431
- if (entry.cache) {
432
- cacheScope = createCacheScope(entry.cache, cacheScope);
433
- }
352
+ clientSegmentSet.delete(snapshot.manifestEntry.shortCode);
434
353
  }
435
354
 
436
355
  const isIntercept = !!interceptResult;
@@ -440,31 +359,31 @@ export async function createMatchContextForPartial<TEnv>(
440
359
  url: cleanUrl,
441
360
  pathname,
442
361
  env,
443
- clientSegmentIds,
362
+ clientSegmentIds: nav.clientSegmentIds,
444
363
  clientSegmentSet,
445
- stale,
446
- prevUrl,
447
- prevParams,
448
- prevMatch,
364
+ stale: nav.stale,
365
+ prevUrl: nav.prevUrl,
366
+ prevParams: nav.prevParams,
367
+ prevMatch: nav.prevMatch,
449
368
  matched,
450
- manifestEntry,
451
- entries,
369
+ manifestEntry: snapshot.manifestEntry,
370
+ entries: snapshot.entries,
452
371
  routeKey: matched.routeKey,
453
- localRouteName,
372
+ localRouteName: snapshot.localRouteName,
454
373
  handlerContext,
455
374
  loaderPromises,
456
375
  routeMap: deps.getRouteMap(),
457
376
  metricsStore,
458
377
  Store,
459
- interceptContextMatch,
378
+ interceptContextMatch: nav.interceptContextMatch,
460
379
  interceptSelectorContext,
461
- isSameRouteNavigation,
380
+ isSameRouteNavigation: nav.isSameRouteNavigation,
462
381
  interceptResult,
463
- cacheScope,
382
+ cacheScope: snapshot.cacheScope,
464
383
  isIntercept,
465
384
  actionContext,
466
385
  isAction,
467
- routeMiddleware,
386
+ routeMiddleware: snapshot.routeMiddleware,
468
387
  isFullMatch: false,
469
388
  };
470
389
  }
@@ -615,6 +534,7 @@ export async function matchError<TEnv>(
615
534
  segments: [errorSegment],
616
535
  matched: matchedIds,
617
536
  diff: [errorSegment.id],
537
+ resolvedIds: [errorSegment.id],
618
538
  params: matched.params,
619
539
  };
620
540
  }
@@ -242,25 +242,3 @@ export function createPipelineState(): MatchPipelineState {
242
242
  slots: {},
243
243
  };
244
244
  }
245
-
246
- /**
247
- * Input parameters for createMatchContext
248
- */
249
- export interface CreateMatchContextInput<TEnv = any> {
250
- request: Request;
251
- env: TEnv;
252
- actionContext?: ActionContext;
253
- }
254
-
255
- /**
256
- * Result from createMatchContext - either a context or null (fall back to full match)
257
- */
258
- export type CreateMatchContextResult<TEnv = any> =
259
- | { type: "context"; ctx: MatchContext<TEnv> }
260
- | { type: "fallback"; reason: string }
261
- | { type: "error"; error: Error };
262
-
263
- // Note: createMatchContext() will be implemented in Step J10 when we wire everything together.
264
- // It requires access to RouterContext (findMatch, loadManifest, etc.) which are closure
265
- // functions from createRouter(). The implementation will live in router.ts initially
266
- // and call getRouterContext() to access these dependencies.