@rangojs/router 0.0.0-experimental.bd6e11bc → 0.0.0-experimental.bdaf10aa

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 (411) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +296 -887
  3. package/dist/bin/rango.js +459 -91
  4. package/dist/testing/vitest.js +36 -2
  5. package/dist/vite/index.js +1708 -414
  6. package/package.json +35 -10
  7. package/skills/api-client/SKILL.md +211 -0
  8. package/skills/breadcrumbs/SKILL.md +82 -5
  9. package/skills/bundle-analysis/SKILL.md +2 -2
  10. package/skills/cache-guide/SKILL.md +14 -9
  11. package/skills/caching/SKILL.md +221 -12
  12. package/skills/catalog.json +271 -0
  13. package/skills/comparison/SKILL.md +50 -0
  14. package/skills/comparison/agents/openai.yaml +4 -0
  15. package/skills/comparison/references/framework-comparison.md +837 -0
  16. package/skills/composability/SKILL.md +83 -2
  17. package/skills/css/SKILL.md +76 -0
  18. package/skills/debug-manifest/SKILL.md +5 -3
  19. package/skills/defer-hydration/SKILL.md +235 -0
  20. package/skills/document-cache/SKILL.md +11 -3
  21. package/skills/fonts/SKILL.md +1 -1
  22. package/skills/handler-use/SKILL.md +9 -9
  23. package/skills/hooks/SKILL.md +73 -900
  24. package/skills/hooks/data.md +273 -0
  25. package/skills/hooks/handle-and-actions.md +103 -0
  26. package/skills/hooks/navigation.md +110 -0
  27. package/skills/hooks/outlets.md +41 -0
  28. package/skills/hooks/state.md +228 -0
  29. package/skills/hooks/urls.md +135 -0
  30. package/skills/host-router/SKILL.md +84 -7
  31. package/skills/i18n/SKILL.md +1 -1
  32. package/skills/intercept/SKILL.md +51 -17
  33. package/skills/layout/SKILL.md +38 -16
  34. package/skills/links/SKILL.md +1 -1
  35. package/skills/loader/SKILL.md +48 -20
  36. package/skills/middleware/SKILL.md +11 -5
  37. package/skills/migrate-nextjs/SKILL.md +203 -20
  38. package/skills/migrate-react-router/SKILL.md +59 -675
  39. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  40. package/skills/migrate-react-router/component-migration.md +196 -0
  41. package/skills/migrate-react-router/data-and-actions.md +225 -0
  42. package/skills/migrate-react-router/route-mapping.md +271 -0
  43. package/skills/mime-routes/SKILL.md +3 -3
  44. package/skills/observability/SKILL.md +70 -5
  45. package/skills/parallel/SKILL.md +32 -8
  46. package/skills/ppr/SKILL.md +622 -0
  47. package/skills/prerender/SKILL.md +59 -28
  48. package/skills/rango/SKILL.md +124 -50
  49. package/skills/response-routes/SKILL.md +78 -46
  50. package/skills/route/SKILL.md +85 -6
  51. package/skills/router-setup/SKILL.md +41 -6
  52. package/skills/scripts/SKILL.md +179 -0
  53. package/skills/server-actions/SKILL.md +28 -3
  54. package/skills/shell-manifest/SKILL.md +185 -0
  55. package/skills/streams-and-websockets/SKILL.md +1 -1
  56. package/skills/tailwind/SKILL.md +28 -4
  57. package/skills/testing/SKILL.md +68 -654
  58. package/skills/testing/bindings.md +103 -0
  59. package/skills/testing/cache-prerender.md +127 -0
  60. package/skills/testing/client-components.md +124 -0
  61. package/skills/testing/e2e-parity.md +125 -0
  62. package/skills/testing/flight.md +91 -0
  63. package/skills/testing/handles.md +131 -0
  64. package/skills/testing/loader.md +128 -0
  65. package/skills/testing/middleware.md +99 -0
  66. package/skills/testing/render-handler.md +122 -0
  67. package/skills/testing/response-routes.md +95 -0
  68. package/skills/testing/reverse-and-types.md +85 -0
  69. package/skills/testing/server-actions.md +107 -0
  70. package/skills/testing/server-tree.md +128 -0
  71. package/skills/testing/setup.md +123 -0
  72. package/skills/theme/SKILL.md +1 -1
  73. package/skills/typesafety/SKILL.md +45 -918
  74. package/skills/typesafety/env-and-bindings.md +254 -0
  75. package/skills/typesafety/generated-files-and-cli.md +335 -0
  76. package/skills/typesafety/params-and-search.md +153 -0
  77. package/skills/typesafety/route-types.md +209 -0
  78. package/skills/use-cache/SKILL.md +47 -17
  79. package/skills/vercel/SKILL.md +128 -0
  80. package/skills/view-transitions/SKILL.md +44 -1
  81. package/src/__augment-tests__/augmented.check.ts +2 -3
  82. package/src/__internal.ts +0 -65
  83. package/src/browser/action-coordinator.ts +1 -1
  84. package/src/browser/action-fence.ts +47 -0
  85. package/src/browser/app-shell.ts +14 -27
  86. package/src/browser/connection-warmup.ts +134 -0
  87. package/src/browser/cookie-name.ts +140 -0
  88. package/src/browser/event-controller.ts +178 -100
  89. package/src/browser/invalidate-client-cache.ts +52 -0
  90. package/src/browser/logging.ts +28 -0
  91. package/src/browser/merge-segment-loaders.ts +6 -4
  92. package/src/browser/navigation-bridge.ts +81 -68
  93. package/src/browser/navigation-client.ts +115 -70
  94. package/src/browser/navigation-store-handle.ts +38 -0
  95. package/src/browser/navigation-store.ts +153 -88
  96. package/src/browser/navigation-transaction.ts +0 -32
  97. package/src/browser/network-error-handler.ts +34 -7
  98. package/src/browser/partial-update.ts +157 -144
  99. package/src/browser/prefetch/cache.ts +148 -81
  100. package/src/browser/prefetch/fetch.ts +231 -51
  101. package/src/browser/prefetch/queue.ts +25 -7
  102. package/src/browser/rango-state.ts +157 -115
  103. package/src/browser/react/Link.tsx +40 -7
  104. package/src/browser/react/NavigationProvider.tsx +140 -99
  105. package/src/browser/react/ScrollRestoration.tsx +10 -6
  106. package/src/browser/react/filter-segment-order.ts +17 -2
  107. package/src/browser/react/index.ts +0 -51
  108. package/src/browser/react/location-state-shared.ts +14 -15
  109. package/src/browser/react/location-state.ts +0 -1
  110. package/src/browser/react/use-action.ts +6 -15
  111. package/src/browser/react/use-handle.ts +0 -5
  112. package/src/browser/react/use-href.tsx +8 -1
  113. package/src/browser/react/use-link-status.ts +33 -8
  114. package/src/browser/react/use-navigation.ts +10 -5
  115. package/src/browser/react/use-params.ts +0 -2
  116. package/src/browser/react/use-router.ts +6 -4
  117. package/src/browser/react/use-search-params.ts +0 -5
  118. package/src/browser/react/use-segments.ts +0 -13
  119. package/src/browser/response-adapter.ts +74 -8
  120. package/src/browser/rsc-router.tsx +97 -22
  121. package/src/browser/scroll-restoration.ts +15 -8
  122. package/src/browser/segment-reconciler.ts +31 -21
  123. package/src/browser/server-action-bridge.ts +216 -38
  124. package/src/browser/types.ts +94 -22
  125. package/src/browser/validate-redirect-origin.ts +43 -16
  126. package/src/build/generate-manifest.ts +155 -131
  127. package/src/build/generate-route-types.ts +1 -1
  128. package/src/build/index.ts +11 -5
  129. package/src/build/prefix-tree-utils.ts +123 -0
  130. package/src/build/route-trie.ts +152 -22
  131. package/src/build/route-types/ast-route-extraction.ts +15 -8
  132. package/src/build/route-types/codegen.ts +12 -1
  133. package/src/build/route-types/include-resolution.ts +455 -61
  134. package/src/build/route-types/param-extraction.ts +6 -3
  135. package/src/build/route-types/per-module-writer.ts +15 -2
  136. package/src/build/route-types/router-processing.ts +77 -41
  137. package/src/build/route-types/source-scan.ts +105 -7
  138. package/src/build/runtime-discovery.ts +4 -1
  139. package/src/cache/cache-error.ts +104 -0
  140. package/src/cache/cache-key-utils.ts +58 -13
  141. package/src/cache/cache-policy.ts +108 -34
  142. package/src/cache/cache-runtime.ts +454 -101
  143. package/src/cache/cache-scope.ts +159 -54
  144. package/src/cache/cache-tag.ts +149 -0
  145. package/src/cache/cf/cf-base64.ts +33 -0
  146. package/src/cache/cf/cf-cache-constants.ts +127 -0
  147. package/src/cache/cf/cf-cache-store.ts +2170 -377
  148. package/src/cache/cf/cf-cache-types.ts +349 -0
  149. package/src/cache/cf/cf-kv-utils.ts +46 -0
  150. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  151. package/src/cache/cf/index.ts +6 -16
  152. package/src/cache/document-cache.ts +126 -41
  153. package/src/cache/handle-snapshot.ts +70 -0
  154. package/src/cache/index.ts +23 -20
  155. package/src/cache/memory-segment-store.ts +243 -37
  156. package/src/cache/profile-registry.ts +46 -31
  157. package/src/cache/read-through-swr.ts +56 -12
  158. package/src/cache/segment-codec.ts +13 -21
  159. package/src/cache/shell-snapshot.ts +417 -0
  160. package/src/cache/tag-invalidation.ts +230 -0
  161. package/src/cache/types.ts +194 -99
  162. package/src/cache/vercel/index.ts +11 -0
  163. package/src/cache/vercel/vercel-cache-store.ts +1132 -0
  164. package/src/client.rsc.tsx +39 -22
  165. package/src/client.tsx +28 -58
  166. package/src/cloudflare/index.ts +11 -0
  167. package/src/cloudflare/tracing.ts +108 -0
  168. package/src/component-utils.ts +19 -0
  169. package/src/components/DefaultDocument.tsx +8 -2
  170. package/src/context-var.ts +13 -1
  171. package/src/decode-loader-results.ts +18 -2
  172. package/src/defer.ts +185 -0
  173. package/src/deps/ssr.ts +0 -1
  174. package/src/encode-kv.ts +49 -0
  175. package/src/errors.ts +0 -3
  176. package/src/escape-script.ts +52 -0
  177. package/src/handle.ts +57 -40
  178. package/src/handles/MetaTags.tsx +24 -53
  179. package/src/handles/Scripts.tsx +183 -0
  180. package/src/handles/breadcrumbs.ts +35 -8
  181. package/src/handles/deferred-resolution.ts +127 -0
  182. package/src/handles/is-thenable.ts +18 -0
  183. package/src/handles/meta.ts +14 -40
  184. package/src/handles/script.ts +244 -0
  185. package/src/host/cookie-handler.ts +9 -60
  186. package/src/host/errors.ts +13 -22
  187. package/src/host/index.ts +7 -0
  188. package/src/host/pattern-matcher.ts +23 -52
  189. package/src/host/router.ts +1 -65
  190. package/src/host/testing.ts +40 -27
  191. package/src/host/types.ts +6 -2
  192. package/src/href-client.ts +7 -12
  193. package/src/index.rsc.ts +88 -8
  194. package/src/index.ts +90 -16
  195. package/src/internal-debug.ts +11 -10
  196. package/src/loader.rsc.ts +19 -9
  197. package/src/loader.ts +12 -4
  198. package/src/outlet-provider.tsx +1 -5
  199. package/src/prerender/param-hash.ts +16 -16
  200. package/src/prerender/store.ts +32 -37
  201. package/src/prerender.ts +75 -7
  202. package/src/redirect-origin.ts +114 -0
  203. package/src/regex-escape.ts +8 -0
  204. package/src/render-error-thrower.tsx +20 -0
  205. package/src/response-utils.ts +25 -0
  206. package/src/root-error-boundary.tsx +1 -19
  207. package/src/route-content-wrapper.tsx +13 -49
  208. package/src/route-definition/dsl-helpers.ts +60 -53
  209. package/src/route-definition/helper-factories.ts +0 -2
  210. package/src/route-definition/helpers-types.ts +46 -46
  211. package/src/route-definition/index.ts +1 -2
  212. package/src/route-definition/redirect.ts +44 -11
  213. package/src/route-definition/resolve-handler-use.ts +6 -1
  214. package/src/route-definition/use-item-types.ts +3 -6
  215. package/src/route-map-builder.ts +41 -20
  216. package/src/route-types.ts +0 -5
  217. package/src/router/content-negotiation.ts +58 -23
  218. package/src/router/error-handling.ts +44 -17
  219. package/src/router/find-match.ts +129 -30
  220. package/src/router/handler-context.ts +6 -1
  221. package/src/router/instrument.ts +355 -0
  222. package/src/router/intercept-resolution.ts +35 -2
  223. package/src/router/lazy-includes.ts +79 -56
  224. package/src/router/loader-resolution.ts +151 -73
  225. package/src/router/logging.ts +0 -6
  226. package/src/router/manifest.ts +74 -40
  227. package/src/router/match-api.ts +76 -52
  228. package/src/router/match-context.ts +0 -22
  229. package/src/router/match-handlers.ts +181 -178
  230. package/src/router/match-middleware/background-revalidation.ts +40 -24
  231. package/src/router/match-middleware/cache-lookup.ts +115 -194
  232. package/src/router/match-middleware/cache-store.ts +61 -50
  233. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  234. package/src/router/match-middleware/segment-resolution.ts +0 -22
  235. package/src/router/match-pipelines.ts +1 -42
  236. package/src/router/match-result.ts +36 -67
  237. package/src/router/metrics.ts +0 -34
  238. package/src/router/middleware-types.ts +0 -116
  239. package/src/router/middleware.ts +231 -120
  240. package/src/router/navigation-snapshot.ts +7 -56
  241. package/src/router/params-util.ts +23 -0
  242. package/src/router/parse-pattern.ts +115 -0
  243. package/src/router/pattern-matching.ts +99 -152
  244. package/src/router/prefetch-cache-ttl.ts +51 -0
  245. package/src/router/prefetch-limits.ts +37 -0
  246. package/src/router/prerender-match.ts +111 -66
  247. package/src/router/preview-match.ts +3 -1
  248. package/src/router/request-classification.ts +47 -42
  249. package/src/router/revalidation.ts +75 -81
  250. package/src/router/route-snapshot.ts +14 -3
  251. package/src/router/router-context.ts +6 -29
  252. package/src/router/router-interfaces.ts +70 -8
  253. package/src/router/router-options.ts +126 -4
  254. package/src/router/segment-resolution/fresh.ts +104 -80
  255. package/src/router/segment-resolution/helpers.ts +86 -6
  256. package/src/router/segment-resolution/loader-cache.ts +155 -39
  257. package/src/router/segment-resolution/loader-mask.ts +60 -0
  258. package/src/router/segment-resolution/loader-snapshot.ts +259 -0
  259. package/src/router/segment-resolution/mask-nested.ts +83 -0
  260. package/src/router/segment-resolution/revalidation.ts +215 -304
  261. package/src/router/segment-resolution/static-store.ts +19 -5
  262. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  263. package/src/router/segment-resolution/view-transition-default.ts +35 -15
  264. package/src/router/segment-resolution.ts +5 -1
  265. package/src/router/segment-wrappers.ts +6 -5
  266. package/src/router/state-cookie-name.ts +33 -0
  267. package/src/router/substitute-pattern-params.ts +54 -35
  268. package/src/router/telemetry-otel.ts +160 -200
  269. package/src/router/telemetry.ts +9 -23
  270. package/src/router/timeout.ts +0 -20
  271. package/src/router/tracing.ts +215 -0
  272. package/src/router/trie-matching.ts +171 -64
  273. package/src/router/types.ts +1 -63
  274. package/src/router/url-params.ts +13 -5
  275. package/src/router.ts +119 -48
  276. package/src/rsc/full-payload.ts +70 -0
  277. package/src/rsc/handler-context.ts +1 -0
  278. package/src/rsc/handler.ts +267 -152
  279. package/src/rsc/helpers.ts +78 -4
  280. package/src/rsc/index.ts +1 -4
  281. package/src/rsc/json-route-result.ts +38 -0
  282. package/src/rsc/loader-fetch.ts +114 -38
  283. package/src/rsc/manifest-init.ts +29 -42
  284. package/src/rsc/nonce.ts +10 -1
  285. package/src/rsc/origin-guard.ts +11 -15
  286. package/src/rsc/progressive-enhancement.ts +120 -13
  287. package/src/rsc/redirect-guard.ts +100 -0
  288. package/src/rsc/response-cache-serve.ts +238 -0
  289. package/src/rsc/response-error.ts +79 -12
  290. package/src/rsc/response-route-handler.ts +58 -141
  291. package/src/rsc/rsc-rendering.ts +492 -49
  292. package/src/rsc/runtime-warnings.ts +14 -0
  293. package/src/rsc/server-action.ts +268 -82
  294. package/src/rsc/shell-capture.ts +1190 -0
  295. package/src/rsc/shell-serve.ts +181 -0
  296. package/src/rsc/transition-gate.ts +89 -0
  297. package/src/rsc/types.ts +45 -3
  298. package/src/runtime-env.ts +18 -0
  299. package/src/search-params.ts +31 -26
  300. package/src/segment-loader-promise.ts +49 -4
  301. package/src/segment-system.tsx +260 -95
  302. package/src/server/context.ts +99 -9
  303. package/src/server/cookie-parse.ts +32 -0
  304. package/src/server/cookie-store.ts +125 -2
  305. package/src/server/handle-store.ts +21 -38
  306. package/src/server/loader-registry.ts +33 -42
  307. package/src/server/request-context.ts +379 -138
  308. package/src/ssr/index.tsx +491 -182
  309. package/src/ssr/inject-rsc-eager.ts +167 -0
  310. package/src/ssr/ssr-root.tsx +228 -0
  311. package/src/static-handler.ts +10 -13
  312. package/src/testing/cache-status.ts +44 -48
  313. package/src/testing/collect-handle.ts +14 -31
  314. package/src/testing/dispatch.ts +533 -160
  315. package/src/testing/e2e/fixture.ts +45 -11
  316. package/src/testing/e2e/index.ts +1 -22
  317. package/src/testing/e2e/matchers.ts +0 -16
  318. package/src/testing/e2e/parity.ts +85 -4
  319. package/src/testing/e2e/server.ts +12 -0
  320. package/src/testing/flight-matchers.ts +7 -14
  321. package/src/testing/flight-normalize.ts +11 -0
  322. package/src/testing/flight-runtime.d.ts +36 -0
  323. package/src/testing/flight-tree.ts +682 -0
  324. package/src/testing/flight.entry.ts +30 -0
  325. package/src/testing/flight.ts +145 -70
  326. package/src/testing/generated-routes.ts +26 -50
  327. package/src/testing/index.ts +18 -19
  328. package/src/testing/internal/context.ts +184 -68
  329. package/src/testing/internal/flight-client-globals.ts +30 -0
  330. package/src/testing/internal/seed-vars.ts +54 -0
  331. package/src/testing/render-handler.ts +357 -0
  332. package/src/testing/render-route.tsx +134 -115
  333. package/src/testing/run-loader.ts +140 -51
  334. package/src/testing/run-middleware.ts +59 -33
  335. package/src/testing/run-transition-when.ts +164 -0
  336. package/src/testing/vitest-stubs/cloudflare-email.ts +1 -1
  337. package/src/testing/vitest-stubs/cloudflare-workers.ts +1 -1
  338. package/src/testing/vitest.ts +138 -16
  339. package/src/theme/ThemeProvider.tsx +56 -84
  340. package/src/theme/ThemeScript.tsx +7 -9
  341. package/src/theme/constants.ts +52 -13
  342. package/src/theme/index.ts +0 -7
  343. package/src/theme/theme-context.ts +1 -5
  344. package/src/theme/theme-script.ts +22 -21
  345. package/src/theme/use-theme.ts +0 -3
  346. package/src/types/boundaries.ts +0 -35
  347. package/src/types/cache-types.ts +13 -4
  348. package/src/types/error-types.ts +30 -90
  349. package/src/types/global-namespace.ts +15 -15
  350. package/src/types/handler-context.ts +45 -15
  351. package/src/types/index.ts +2 -10
  352. package/src/types/loader-types.ts +6 -3
  353. package/src/types/request-scope.ts +8 -22
  354. package/src/types/route-config.ts +20 -52
  355. package/src/types/route-entry.ts +0 -6
  356. package/src/types/segments.ts +100 -13
  357. package/src/urls/include-helper.ts +10 -12
  358. package/src/urls/include-provider.ts +71 -0
  359. package/src/urls/index.ts +2 -8
  360. package/src/urls/path-helper-types.ts +52 -14
  361. package/src/urls/path-helper.ts +5 -54
  362. package/src/urls/pattern-types.ts +36 -0
  363. package/src/urls/type-extraction.ts +76 -42
  364. package/src/urls/urls-function.ts +0 -14
  365. package/src/use-loader.tsx +0 -186
  366. package/src/vercel/index.ts +11 -0
  367. package/src/vercel/tracing.ts +88 -0
  368. package/src/vite/discovery/bundle-postprocess.ts +2 -1
  369. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  370. package/src/vite/discovery/discover-routers.ts +34 -43
  371. package/src/vite/discovery/discovery-errors.ts +61 -0
  372. package/src/vite/discovery/prerender-collection.ts +33 -46
  373. package/src/vite/discovery/state.ts +12 -1
  374. package/src/vite/discovery/virtual-module-codegen.ts +1 -11
  375. package/src/vite/index.ts +9 -0
  376. package/src/vite/inject-client-debug.ts +88 -0
  377. package/src/vite/plugin-types.ts +143 -10
  378. package/src/vite/plugins/cjs-to-esm.ts +8 -12
  379. package/src/vite/plugins/client-ref-dedup.ts +0 -11
  380. package/src/vite/plugins/client-ref-hashing.ts +0 -10
  381. package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
  382. package/src/vite/plugins/expose-action-id.ts +2 -73
  383. package/src/vite/plugins/expose-id-utils.ts +85 -56
  384. package/src/vite/plugins/expose-ids/export-analysis.ts +30 -43
  385. package/src/vite/plugins/expose-ids/handler-transform.ts +5 -31
  386. package/src/vite/plugins/expose-ids/loader-transform.ts +12 -20
  387. package/src/vite/plugins/expose-ids/router-transform.ts +98 -26
  388. package/src/vite/plugins/expose-internal-ids.ts +10 -1
  389. package/src/vite/plugins/performance-tracks.ts +0 -3
  390. package/src/vite/plugins/refresh-cmd.ts +1 -1
  391. package/src/vite/plugins/use-cache-transform.ts +21 -46
  392. package/src/vite/plugins/vercel-output.ts +384 -0
  393. package/src/vite/plugins/version-injector.ts +22 -27
  394. package/src/vite/plugins/version-plugin.ts +6 -66
  395. package/src/vite/plugins/virtual-entries.ts +137 -26
  396. package/src/vite/rango.ts +146 -135
  397. package/src/vite/router-discovery.ts +189 -48
  398. package/src/vite/utils/ast-handler-extract.ts +11 -20
  399. package/src/vite/utils/bundle-analysis.ts +6 -13
  400. package/src/vite/utils/client-chunks.ts +0 -6
  401. package/src/vite/utils/directive-prologue.ts +40 -0
  402. package/src/vite/utils/forward-user-plugins.ts +0 -22
  403. package/src/vite/utils/manifest-utils.ts +4 -75
  404. package/src/vite/utils/package-resolution.ts +1 -73
  405. package/src/vite/utils/prerender-utils.ts +71 -44
  406. package/src/vite/utils/shared-utils.ts +55 -37
  407. package/src/browser/react/use-client-cache.ts +0 -58
  408. package/src/browser/shallow.ts +0 -40
  409. package/src/handles/index.ts +0 -7
  410. package/src/network-error-thrower.tsx +0 -23
  411. package/src/router/middleware-cookies.ts +0 -55
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Span tracing hook (platform-agnostic).
3
+ *
4
+ * The core router emits its existing performance phases (request, middleware, action,
5
+ * loaders, render, ssr) as spans by calling traceSpan() at a small set of
6
+ * execution boundaries. When no tracing is configured the call is a direct
7
+ * pass-through: fn is invoked with a no-op span, with no wrapper and no
8
+ * allocation, so a non-traced request behaves exactly as before.
9
+ *
10
+ * A platform integration supplies a SpanRunner that wraps fn in a real span.
11
+ * Two runners ship: the Cloudflare one (createCloudflareTracing in
12
+ * src/cloudflare/tracing.ts), which bridges onto executionContext.tracing.
13
+ * enterSpan, and the OTel one (createOTelTracing in router/telemetry-otel.ts),
14
+ * which bridges onto tracer.startActiveSpan. Both wrap the actual work — not a
15
+ * post-hoc event — so spans nest by async context and the platform's automatic
16
+ * spans (KV/D1/fetch) nest under the right phase.
17
+ *
18
+ * traceSpan() below is the low-level wrap primitive. It is INTERNAL: the only
19
+ * caller is observePhase() (instrument.ts), the single phase-instrumentation
20
+ * API, which co-emits the span AND the debugPerformance perf metric from one
21
+ * wrap site (or just the span, for metric:false phases) so the two surfaces
22
+ * can't drift. Every router phase routes through observePhase via the PHASES
23
+ * registry; do not call traceSpan directly from new code.
24
+ *
25
+ * Phase coverage (all via observePhase): rango.request (span-only; handler:total
26
+ * metered directly), rango.middleware (span-only incl. intercept middleware;
27
+ * pre/post metered directly), rango.action (action:<id>; server-action
28
+ * execution, JS + no-JS/PE), rango.loader (loader:<id>; single metering site at
29
+ * useLoader, plus the fetchable path), rango.handler (span-only, one per segment
30
+ * route/layout handler execution; the handler:<id> perf metric is owned by the
31
+ * track() at the call site), rango.render (render:total:<route>; normal AND
32
+ * action-revalidation renders), rango.ssr (ssr:render-html).
33
+ *
34
+ * Span-duration caveat (best-effort, never buffers): a span ends when its
35
+ * callback's value (or promise) settles. For the streaming phases (request,
36
+ * render, ssr) that is when the Response / HTML / RSC stream is CONSTRUCTED, not
37
+ * when the body finishes draining — instrumentation never wraps or buffers the
38
+ * response body, so it cannot regress response latency or streaming. A loader /
39
+ * Suspense child that resolves while the body streams therefore keeps a
40
+ * rango.loader span that can extend past its render parent; overlapping spans are
41
+ * valid (the loader really did take that long). Phase spans bound the work up to
42
+ * stream-handoff, which is also what the co-emitted perf metric measures.
43
+ *
44
+ * Both shipped runners (Cloudflare, OTel) keep the core agnostic: the
45
+ * platform-specific bridge lives at the edge behind the SpanRunner contract.
46
+ */
47
+
48
+ /**
49
+ * Minimal span handle passed to traced work. Structurally compatible with both
50
+ * Cloudflare's `Span` and OTel's `Span` (only setAttribute is used here).
51
+ */
52
+ export interface TraceSpan {
53
+ setAttribute(key: string, value: string | number | boolean): void;
54
+ }
55
+
56
+ /**
57
+ * Wraps a unit of work in a span. A runner MUST invoke fn exactly once, pass it
58
+ * a span, return fn's result unchanged, and propagate thrown errors / rejected
59
+ * promises unchanged. When fn returns a promise the span ends once it settles.
60
+ */
61
+ export type SpanRunner = <T>(name: string, fn: (span: TraceSpan) => T) => T;
62
+
63
+ /** The router phases that can be wrapped in a span. */
64
+ export type TracePhase =
65
+ | "request"
66
+ | "middleware"
67
+ | "action"
68
+ | "loader"
69
+ | "handler"
70
+ | "render"
71
+ | "ssr";
72
+
73
+ /** Per-phase span toggles. Omitted phases default to enabled. */
74
+ export interface TracePhaseToggles {
75
+ request?: boolean;
76
+ middleware?: boolean;
77
+ action?: boolean;
78
+ loader?: boolean;
79
+ handler?: boolean;
80
+ render?: boolean;
81
+ ssr?: boolean;
82
+ }
83
+
84
+ /**
85
+ * The option pair shared by every tracing factory (enabled master switch +
86
+ * per-phase span toggles). Extended by OTelTracingOptions,
87
+ * CloudflareTracingOptions, VercelTracingOptions, and RouterTracingConfig so
88
+ * a phase added to TracePhaseToggles propagates everywhere from one place.
89
+ */
90
+ export interface TracingToggleOptions {
91
+ /** Master switch. Defaults to true. */
92
+ enabled?: boolean;
93
+ /** Per-phase span toggles. Omitted phases default to enabled. */
94
+ spans?: TracePhaseToggles;
95
+ }
96
+
97
+ /**
98
+ * Value passed to `createRouter({ tracing })`. Produced by a platform factory
99
+ * such as `createCloudflareTracing()`.
100
+ */
101
+ export interface RouterTracingConfig extends TracingToggleOptions {
102
+ /** Platform span runner. */
103
+ runner: SpanRunner;
104
+ }
105
+
106
+ /**
107
+ * Resolved tracing state stored on the router/request context. `undefined`
108
+ * means tracing is fully disabled and every traceSpan() call is a pass-through.
109
+ */
110
+ export interface ResolvedTracing {
111
+ runner: SpanRunner;
112
+ phases: Record<TracePhase, boolean>;
113
+ }
114
+
115
+ /** Shared no-op span. setAttribute is a no-op so disabled call sites stay free. */
116
+ export const NOOP_TRACE_SPAN: TraceSpan = {
117
+ setAttribute() {},
118
+ };
119
+
120
+ const ALL_PHASES_ON: Record<TracePhase, boolean> = {
121
+ request: true,
122
+ middleware: true,
123
+ action: true,
124
+ loader: true,
125
+ handler: true,
126
+ render: true,
127
+ ssr: true,
128
+ };
129
+
130
+ /**
131
+ * Resolve a user-supplied tracing config into the fast internal form, or
132
+ * `undefined` when tracing is off (no config, `enabled: false`, or no runner).
133
+ */
134
+ export function resolveTracing(
135
+ config: RouterTracingConfig | undefined,
136
+ ): ResolvedTracing | undefined {
137
+ if (
138
+ !config ||
139
+ config.enabled === false ||
140
+ typeof config.runner !== "function"
141
+ ) {
142
+ return undefined;
143
+ }
144
+ const spans = config.spans;
145
+ return {
146
+ runner: config.runner,
147
+ phases: spans
148
+ ? {
149
+ request: spans.request ?? true,
150
+ middleware: spans.middleware ?? true,
151
+ action: spans.action ?? true,
152
+ loader: spans.loader ?? true,
153
+ handler: spans.handler ?? true,
154
+ render: spans.render ?? true,
155
+ ssr: spans.ssr ?? true,
156
+ }
157
+ : ALL_PHASES_ON,
158
+ };
159
+ }
160
+
161
+ /**
162
+ * Wrap `fn` in a span for `phase`. When tracing is off (or the phase is
163
+ * disabled) fn runs directly with a no-op span — identical to the untraced
164
+ * path. Otherwise the platform runner wraps fn so the span covers the real
165
+ * work and nests by async context.
166
+ */
167
+ export function traceSpan<T>(
168
+ tracing: ResolvedTracing | undefined,
169
+ phase: TracePhase,
170
+ name: string,
171
+ fn: (span: TraceSpan) => T,
172
+ ): T {
173
+ if (tracing === undefined || tracing.phases[phase] === false) {
174
+ return fn(NOOP_TRACE_SPAN);
175
+ }
176
+ return tracing.runner(name, fn);
177
+ }
178
+
179
+ /**
180
+ * Run `fn` once and invoke `onSettle` exactly once when it terminates — on a
181
+ * synchronous return, a synchronous throw, an async resolution, or an async
182
+ * rejection. `onSettle` receives the error (or `undefined` on success). fn's
183
+ * value is returned and errors propagate unchanged.
184
+ *
185
+ * Centralizes the run-once-then-settle control flow shared by the two span
186
+ * surfaces: observePhase records the perf metric on settle, and the OTel runner
187
+ * ends (or error-marks) the span on settle. The Cloudflare runner delegates
188
+ * settling to enterSpan, so it does not use this.
189
+ */
190
+ export function runThenSettle<T>(
191
+ fn: () => T,
192
+ onSettle: (error: unknown) => void,
193
+ ): T {
194
+ let out: T;
195
+ try {
196
+ out = fn();
197
+ } catch (error) {
198
+ onSettle(error);
199
+ throw error;
200
+ }
201
+ if (out instanceof Promise) {
202
+ return out.then(
203
+ (value) => {
204
+ onSettle(undefined);
205
+ return value;
206
+ },
207
+ (error) => {
208
+ onSettle(error);
209
+ throw error;
210
+ },
211
+ ) as unknown as T;
212
+ }
213
+ onSettle(undefined);
214
+ return out;
215
+ }
@@ -5,7 +5,11 @@
5
5
  * Falls back to null when no match is found (caller uses regex fallback).
6
6
  */
7
7
 
8
- import type { TrieNode, TrieLeaf } from "../build/route-trie.js";
8
+ import type {
9
+ TrieNode,
10
+ TrieLeaf,
11
+ NegotiateVariant,
12
+ } from "../build/route-trie.js";
9
13
  import { safeDecodeURIComponent } from "./url-params.js";
10
14
 
11
15
  export interface TrieMatchResult {
@@ -15,12 +19,6 @@ export interface TrieMatchResult {
15
19
  sp: string;
16
20
  /** Matched route params */
17
21
  params: Record<string, string>;
18
- /** Optional param names declared on the route. Absent params are omitted
19
- * from `params` (read as `undefined`), matching the
20
- * `ExtractParams<"/:locale?/...">` type. */
21
- optionalParams?: string[];
22
- /** Ancestry shortCodes for layout pruning */
23
- ancestry: string[];
24
22
  /** Redirect target if trailing slash requires it */
25
23
  redirectTo?: string;
26
24
  /** Route has pre-rendered data available */
@@ -30,7 +28,7 @@ export interface TrieMatchResult {
30
28
  /** Response type for non-RSC routes (json, text, image, any) */
31
29
  responseType?: string;
32
30
  /** Negotiate variants: response-type routes sharing this path */
33
- negotiateVariants?: Array<{ routeKey: string; responseType: string }>;
31
+ negotiateVariants?: NegotiateVariant[];
34
32
  /** RSC-first: RSC route was defined before response-type variants */
35
33
  rscFirst?: true;
36
34
  }
@@ -45,14 +43,12 @@ export function tryTrieMatch(
45
43
  ): TrieMatchResult | null {
46
44
  if (!trie) return null;
47
45
 
48
- // Split pathname into segments, filtering empty strings from leading/trailing slashes
49
46
  const pathnameHasTrailingSlash =
50
47
  pathname.length > 1 && pathname.endsWith("/");
51
48
  const normalizedPath = pathnameHasTrailingSlash
52
49
  ? pathname.slice(0, -1)
53
50
  : pathname;
54
51
 
55
- // Handle root path
56
52
  if (normalizedPath === "" || normalizedPath === "/") {
57
53
  if (trie.r) {
58
54
  return validateAndBuild(
@@ -63,13 +59,25 @@ export function tryTrieMatch(
63
59
  pathnameHasTrailingSlash,
64
60
  );
65
61
  }
62
+ // A root-level wildcard ("/*") matches "/" with an empty remainder, the
63
+ // same value the regex matcher produces for the bare prefix. Without this
64
+ // the trie misses, the regex fallback runs, and its no-config branch emits
65
+ // a corrupt slice-off redirect. The static terminal still wins above.
66
+ // A one-or-more catch-all (`w1`, from `:name+`) rejects this empty case.
67
+ if (trie.w && !trie.w.w1) {
68
+ return validateAndBuild(
69
+ trie.w,
70
+ [],
71
+ "",
72
+ pathname,
73
+ pathnameHasTrailingSlash,
74
+ );
75
+ }
66
76
  return null;
67
77
  }
68
78
 
69
- // Remove leading slash and split
70
79
  const segments = normalizedPath.slice(1).split("/");
71
80
 
72
- // Try exact match with normalized path (no trailing slash)
73
81
  const result = walkTrie(trie, segments, 0, []);
74
82
  if (result) {
75
83
  return validateAndBuild(
@@ -78,6 +86,7 @@ export function tryTrieMatch(
78
86
  result.wildcardValue,
79
87
  pathname,
80
88
  pathnameHasTrailingSlash,
89
+ result.validatedParams,
81
90
  );
82
91
  }
83
92
 
@@ -88,11 +97,75 @@ interface WalkResult {
88
97
  leaf: TrieLeaf;
89
98
  paramValues: string[];
90
99
  wildcardValue?: string;
100
+ /**
101
+ * For a constraint-bearing leaf (leaf.cv set), the params map that
102
+ * leafConstraintsPass already decoded AND validated during the walk. Carried
103
+ * forward so validateAndBuild reuses it instead of re-decoding paramValues and
104
+ * re-running constraintsSatisfied a second time on the winning leaf. Undefined
105
+ * for unconstrained leaves (no decode/validate happened in the walk).
106
+ */
107
+ validatedParams?: Record<string, string>;
108
+ }
109
+
110
+ /**
111
+ * Check a leaf's constraints (leaf.cv) against already-resolved named params.
112
+ * Empty/undefined values are exempt (optional params that were not bound).
113
+ */
114
+ function constraintsSatisfied(
115
+ leaf: TrieLeaf,
116
+ params: Record<string, string>,
117
+ ): boolean {
118
+ if (!leaf.cv) return true;
119
+ for (const paramName in leaf.cv) {
120
+ const allowed = leaf.cv[paramName]!;
121
+ const value = params[paramName];
122
+ if (value !== undefined && value !== "" && !allowed.includes(value)) {
123
+ return false;
124
+ }
125
+ }
126
+ return true;
127
+ }
128
+
129
+ /**
130
+ * Constraint check for a candidate terminal DURING the walk. Builds the named
131
+ * params from positional walk values (decoded the same way validateAndBuild
132
+ * does) and validates leaf.cv. Returning null lets walkTrie unwind to a
133
+ * lower-priority sibling instead of committing to a leaf that would only be
134
+ * rejected post-walk — that post-walk rejection is what forced the regex
135
+ * fallback (and its false "trie gap" R3 warning) for perfectly valid configs.
136
+ *
137
+ * On success returns the built+validated params for a constraint-bearing leaf so
138
+ * walkTrie can carry them to validateAndBuild (avoiding a second decode + a
139
+ * second constraintsSatisfied pass on the winner); returns the shared EMPTY_PASS
140
+ * sentinel for an unconstrained leaf (no work was done, nothing to carry).
141
+ */
142
+ const EMPTY_PASS: Record<string, string> = {};
143
+ function leafConstraintsPass(
144
+ leaf: TrieLeaf,
145
+ paramValues: string[],
146
+ wildcardValue: string | undefined,
147
+ ): Record<string, string> | null {
148
+ if (!leaf.cv) return EMPTY_PASS;
149
+ const params: Record<string, string> = {};
150
+ if (leaf.pa) {
151
+ for (let i = 0; i < leaf.pa.length && i < paramValues.length; i++) {
152
+ params[leaf.pa[i]] = safeDecodeURIComponent(paramValues[i]);
153
+ }
154
+ }
155
+ if (wildcardValue !== undefined && "pn" in leaf) {
156
+ params[(leaf as TrieLeaf & { pn: string }).pn] =
157
+ safeDecodeURIComponent(wildcardValue);
158
+ }
159
+ return constraintsSatisfied(leaf, params) ? params : null;
91
160
  }
92
161
 
93
162
  /**
94
- * Walk the trie by segments with priority: static > param > wildcard.
95
- * Uses backtracking to try all possible matches.
163
+ * Walk the trie by segments with priority: static > suffix-param > param >
164
+ * wildcard (Priority 1-4 below; matches the canonical M4 ordering in
165
+ * docs/internal/matching-and-lazy-discovery.md).
166
+ * Uses backtracking to try all possible matches. Per-leaf constraints are
167
+ * enforced at each candidate terminal so a constraint miss backtracks to a
168
+ * lower-priority sibling rather than aborting the whole match.
96
169
  */
97
170
  function walkTrie(
98
171
  node: TrieNode,
@@ -100,10 +173,38 @@ function walkTrie(
100
173
  index: number,
101
174
  paramValues: string[],
102
175
  ): WalkResult | null {
103
- // All segments consumed: check for terminal
104
176
  if (index === segments.length) {
105
177
  if (node.r) {
106
- return { leaf: node.r, paramValues: [...paramValues] };
178
+ const validatedParams = leafConstraintsPass(
179
+ node.r,
180
+ paramValues,
181
+ undefined,
182
+ );
183
+ if (validatedParams) {
184
+ return {
185
+ leaf: node.r,
186
+ paramValues: [...paramValues],
187
+ validatedParams,
188
+ };
189
+ }
190
+ }
191
+ // A wildcard at this node matches the bare prefix with an empty remainder
192
+ // (e.g. "/files" against "/files/*"), mirroring the regex matcher's `*=""`.
193
+ // walkTrie otherwise only reaches node.w in the index<length branch below,
194
+ // so without this a request to the wildcard's own prefix misses the trie
195
+ // and the regex fallback emits a corrupt redirect. A static terminal
196
+ // (node.r) still wins. A one-or-more catch-all (`w1`, from `:name+`) rejects
197
+ // this empty case — it requires at least one trailing segment.
198
+ if (node.w && !node.w.w1) {
199
+ const validatedParams = leafConstraintsPass(node.w, paramValues, "");
200
+ if (validatedParams) {
201
+ return {
202
+ leaf: node.w,
203
+ paramValues: [...paramValues],
204
+ wildcardValue: "",
205
+ validatedParams,
206
+ };
207
+ }
107
208
  }
108
209
  return null;
109
210
  }
@@ -111,14 +212,15 @@ function walkTrie(
111
212
  const segment = segments[index];
112
213
  const staticChild = node.s?.[segment];
113
214
 
114
- // Priority 1: Static match
115
215
  if (staticChild) {
116
216
  const result = walkTrie(staticChild, segments, index + 1, paramValues);
117
217
  if (result) return result;
118
218
  }
119
219
 
120
- // Priority 2: Suffix-param match (e.g., :productId.html)
121
220
  if (node.xp) {
221
+ // node.xp keys are pre-sorted longest-suffix-first at build time
222
+ // (route-trie.ts sortSuffixParams), so the first match is the most specific
223
+ // suffix: `/app.min.js` matches `:file.min.js` before `:file.js`.
122
224
  for (const suffix in node.xp) {
123
225
  if (segment.endsWith(suffix) && segment.length > suffix.length) {
124
226
  const paramValue = segment.slice(0, -suffix.length);
@@ -135,22 +237,36 @@ function walkTrie(
135
237
  }
136
238
  }
137
239
 
138
- // Priority 3: Param match
139
- if (node.p) {
240
+ // A required single-segment param captures 1+ chars (the regex matcher emits
241
+ // `([^/]+)`), so an empty path segment from a double slash (`/a//b`) must NOT
242
+ // bind `:s` to "". Reject it here so the trie matches the regex contract and
243
+ // a malformed URL 404s instead of running the handler with an empty param.
244
+ // The suffix-param branch above already requires `segment.length > suffix`,
245
+ // and node.w may legitimately be empty, so only this branch needs the guard.
246
+ if (node.p && segment !== "") {
140
247
  paramValues.push(segment);
141
248
  const result = walkTrie(node.p.c, segments, index + 1, paramValues);
142
249
  paramValues.pop();
143
250
  if (result) return result;
144
251
  }
145
252
 
146
- // Priority 4: Wildcard match (consumes rest)
147
253
  if (node.w) {
148
254
  const rest = joinRemainingSegments(segments, index);
149
- return {
150
- leaf: node.w,
151
- paramValues: [...paramValues],
152
- wildcardValue: rest,
153
- };
255
+ // A one-or-more catch-all (`w1`, from `:name+`) requires at least one
256
+ // non-empty trailing segment. `rest` can still be "" here on a malformed
257
+ // double-slash URL (e.g. `/docs//` splits to a trailing "" segment), so
258
+ // guard this in-path site the same way the root and base-case sites are.
259
+ if (!(node.w.w1 && rest === "")) {
260
+ const validatedParams = leafConstraintsPass(node.w, paramValues, rest);
261
+ if (validatedParams) {
262
+ return {
263
+ leaf: node.w,
264
+ paramValues: [...paramValues],
265
+ wildcardValue: rest,
266
+ validatedParams,
267
+ };
268
+ }
269
+ }
154
270
  }
155
271
 
156
272
  return null;
@@ -158,16 +274,20 @@ function walkTrie(
158
274
 
159
275
  function joinRemainingSegments(segments: string[], start: number): string {
160
276
  if (start >= segments.length) return "";
161
-
162
- let rest = segments[start]!;
163
- for (let i = start + 1; i < segments.length; i++) {
164
- rest += "/" + segments[i]!;
165
- }
166
- return rest;
277
+ return segments.slice(start).join("/");
167
278
  }
168
279
 
169
280
  /**
170
281
  * Post-match: validate constraints and handle trailing slash logic.
282
+ *
283
+ * `validatedParams` is the params map walkTrie already decoded AND validated via
284
+ * leafConstraintsPass for a constraint-bearing winning leaf. When present (and
285
+ * non-empty) we reuse it verbatim and SKIP the second decode + the second
286
+ * constraintsSatisfied pass — both are byte-identical to the walk-time work.
287
+ * When absent (unconstrained leaf, or the root-path call sites that never walk)
288
+ * we still BUILD the params here (that is not redundant — they must be returned)
289
+ * and run constraintsSatisfied for safety; an unconstrained leaf's check is a
290
+ * cheap early `!leaf.cv` return.
171
291
  */
172
292
  function validateAndBuild(
173
293
  leaf: TrieLeaf,
@@ -175,43 +295,32 @@ function validateAndBuild(
175
295
  wildcardValue: string | undefined,
176
296
  originalPathname: string,
177
297
  pathnameHasTrailingSlash: boolean,
298
+ validatedParams?: Record<string, string>,
178
299
  ): TrieMatchResult | null {
179
- // Build named params by zipping leaf.pa with positional paramValues.
180
- // Params are URL-decoded at this boundary so ctx.params holds the values
181
- // apps expect (matching Express/React Router) and round-trip cleanly
182
- // through ctx.reverse.
183
- const params: Record<string, string> = {};
184
- if (leaf.pa) {
185
- for (let i = 0; i < leaf.pa.length && i < paramValues.length; i++) {
186
- params[leaf.pa[i]] = safeDecodeURIComponent(paramValues[i]);
300
+ let params: Record<string, string>;
301
+ // EMPTY_PASS (the unconstrained sentinel) and undefined both mean "nothing was
302
+ // pre-validated"; only a populated map carried from a constraint-bearing leaf
303
+ // lets us skip the rebuild + re-check.
304
+ if (validatedParams && validatedParams !== EMPTY_PASS) {
305
+ params = validatedParams;
306
+ } else {
307
+ params = {};
308
+ if (leaf.pa) {
309
+ for (let i = 0; i < leaf.pa.length && i < paramValues.length; i++) {
310
+ params[leaf.pa[i]] = safeDecodeURIComponent(paramValues[i]);
311
+ }
187
312
  }
188
- }
189
313
 
190
- // Add wildcard param (wildcard leaves have pn from TrieNode.w type)
191
- if (wildcardValue !== undefined && "pn" in leaf) {
192
- params[(leaf as TrieLeaf & { pn: string }).pn] =
193
- safeDecodeURIComponent(wildcardValue);
194
- }
314
+ if (wildcardValue !== undefined && "pn" in leaf) {
315
+ params[(leaf as TrieLeaf & { pn: string }).pn] =
316
+ safeDecodeURIComponent(wildcardValue);
317
+ }
195
318
 
196
- // Validate constraints against decoded values so constraint lists can be
197
- // written in decoded form (e.g. ["en-GB", "en US"]).
198
- if (leaf.cv) {
199
- for (const paramName in leaf.cv) {
200
- const allowed = leaf.cv[paramName]!;
201
- const value = params[paramName];
202
- if (value !== undefined && value !== "" && !allowed.includes(value)) {
203
- return null;
204
- }
319
+ if (!constraintsSatisfied(leaf, params)) {
320
+ return null;
205
321
  }
206
322
  }
207
323
 
208
- // Optional params that weren't matched are left absent from `params` so
209
- // `ctx.params.locale` reads as `undefined`, matching the
210
- // `ExtractParams<"/:locale?/...">` type (`{ locale?: string }`). Both
211
- // internal consumers — the constraint check above and `reverse()` —
212
- // already treat missing/undefined as the absent form.
213
-
214
- // Trailing slash handling
215
324
  const tsMode = leaf.ts as "never" | "always" | "ignore" | undefined;
216
325
  let redirectTo: string | undefined;
217
326
 
@@ -229,10 +338,8 @@ function validateAndBuild(
229
338
  routeKey: leaf.n,
230
339
  sp: leaf.sp,
231
340
  params,
232
- ancestry: leaf.a,
233
341
  };
234
342
 
235
- if (leaf.op) result.optionalParams = leaf.op;
236
343
  if (redirectTo) result.redirectTo = redirectTo;
237
344
  if (leaf.pr) result.pr = true;
238
345
  if (leaf.pt) result.pt = true;
@@ -22,27 +22,11 @@ import type {
22
22
  ShouldRevalidateFn,
23
23
  } from "../types";
24
24
 
25
- /**
26
- * Result of resolving loaders with revalidation
27
- * Contains both segments to render and all matched segment IDs
28
- */
29
- export interface LoaderRevalidationResult {
30
- segments: ResolvedSegment[];
31
- matchedIds: string[];
32
- }
33
-
34
- /**
35
- * Result of resolving segments with revalidation
36
- * Contains both segments to render and all matched segment IDs
37
- */
38
25
  export interface SegmentRevalidationResult {
39
26
  segments: ResolvedSegment[];
40
27
  matchedIds: string[];
41
28
  }
42
29
 
43
- /**
44
- * Action context type for revalidation
45
- */
46
30
  export type ActionContext = {
47
31
  actionId?: string;
48
32
  actionUrl?: URL;
@@ -50,23 +34,6 @@ export type ActionContext = {
50
34
  formData?: FormData;
51
35
  };
52
36
 
53
- /**
54
- * Dependencies passed to segment resolution functions
55
- * These are created within createRouter and passed to extracted utilities
56
- */
57
- export interface RouterDependencies<TEnv> {
58
- findNearestErrorBoundary: (
59
- entry: EntryData | null,
60
- ) => ReactNode | ErrorBoundaryHandler | null;
61
- findNearestNotFoundBoundary: (
62
- entry: EntryData | null,
63
- ) => ReactNode | NotFoundBoundaryHandler | null;
64
- }
65
-
66
- /**
67
- * Dependencies injected from createRouter closure into extracted segment resolution functions.
68
- * These are the closure-bound helpers that cannot be imported directly.
69
- */
70
37
  export interface SegmentResolutionDeps<TEnv = any> {
71
38
  wrapLoaderPromise: <T>(
72
39
  promise: Promise<T>,
@@ -108,21 +75,6 @@ export interface SegmentResolutionDeps<TEnv = any> {
108
75
  viewTransitionDefault?: "auto" | false;
109
76
  }
110
77
 
111
- /**
112
- * Dependencies injected from createRouter closure into extracted intercept resolution functions.
113
- */
114
- export interface InterceptResolutionDeps<TEnv = any> {
115
- wrapLoaderPromise: SegmentResolutionDeps<TEnv>["wrapLoaderPromise"];
116
- evaluateInterceptWhen: (
117
- intercept: InterceptEntry,
118
- selectorContext: InterceptSelectorContext | null,
119
- isAction: boolean,
120
- ) => boolean;
121
- }
122
-
123
- /**
124
- * Dependencies injected from createRouter closure into extracted match API functions.
125
- */
126
78
  export interface MatchApiDeps<TEnv = any> {
127
79
  findMatch: (pathname: string, ms?: any) => any;
128
80
  getMetricsStore: () => any;
@@ -137,23 +89,13 @@ export interface MatchApiDeps<TEnv = any> {
137
89
  getRouteMap: () => Record<string, string>;
138
90
  }
139
91
 
140
- /**
141
- * Title descriptor types for template support
142
- */
143
92
  export type TitleDescriptor =
144
93
  | string
145
94
  | { template: string; default: string } // For layouts - template applied to child titles
146
- | { absolute: string }; // Bypass parent template
95
+ | { absolute: string };
147
96
 
148
- /**
149
- * Unset descriptor to remove inherited meta
150
- * Key format matches getMetaKey output: "title", "name:description", "property:og:image"
151
- */
152
97
  export type UnsetDescriptor = { unset: string };
153
98
 
154
- /**
155
- * Base meta descriptor types (sync values)
156
- */
157
99
  export type MetaDescriptorBase =
158
100
  | { charSet: "utf-8" }
159
101
  | { title: TitleDescriptor }
@@ -165,10 +107,6 @@ export type MetaDescriptorBase =
165
107
  | UnsetDescriptor
166
108
  | { [name: string]: unknown };
167
109
 
168
- /**
169
- * Meta descriptor that can be sync or async.
170
- * Use Promise<MetaDescriptorBase> for streaming meta that resolves after initial render.
171
- */
172
110
  export type MetaDescriptor = MetaDescriptorBase | Promise<MetaDescriptorBase>;
173
111
 
174
112
  type LdJsonObject = { [Key in string]: LdJsonValue } & {