@rangojs/router 0.0.0-experimental.1b930379 → 0.0.0-experimental.1c0bdfad

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 (400) hide show
  1. package/AGENTS.md +12 -0
  2. package/README.md +245 -49
  3. package/dist/bin/rango.js +441 -134
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3453 -1240
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +76 -21
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +64 -2
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +247 -23
  12. package/skills/caching/SKILL.md +318 -15
  13. package/skills/composability/SKILL.md +27 -2
  14. package/skills/css/SKILL.md +76 -0
  15. package/skills/debug-manifest/SKILL.md +4 -2
  16. package/skills/document-cache/SKILL.md +78 -55
  17. package/skills/handler-use/SKILL.md +364 -0
  18. package/skills/hooks/SKILL.md +250 -30
  19. package/skills/host-router/SKILL.md +83 -23
  20. package/skills/i18n/SKILL.md +276 -0
  21. package/skills/intercept/SKILL.md +50 -6
  22. package/skills/layout/SKILL.md +35 -9
  23. package/skills/links/SKILL.md +249 -17
  24. package/skills/loader/SKILL.md +279 -53
  25. package/skills/middleware/SKILL.md +52 -13
  26. package/skills/migrate-nextjs/SKILL.md +584 -0
  27. package/skills/migrate-react-router/SKILL.md +771 -0
  28. package/skills/mime-routes/SKILL.md +28 -1
  29. package/skills/observability/SKILL.md +172 -0
  30. package/skills/parallel/SKILL.md +203 -7
  31. package/skills/prerender/SKILL.md +153 -109
  32. package/skills/rango/SKILL.md +251 -22
  33. package/skills/react-compiler/SKILL.md +168 -0
  34. package/skills/response-routes/SKILL.md +123 -48
  35. package/skills/route/SKILL.md +101 -5
  36. package/skills/router-setup/SKILL.md +116 -8
  37. package/skills/scripts/SKILL.md +179 -0
  38. package/skills/server-actions/SKILL.md +775 -0
  39. package/skills/streams-and-websockets/SKILL.md +283 -0
  40. package/skills/tailwind/SKILL.md +27 -3
  41. package/skills/testing/SKILL.md +129 -0
  42. package/skills/testing/bindings.md +103 -0
  43. package/skills/testing/cache-prerender.md +127 -0
  44. package/skills/testing/client-components.md +124 -0
  45. package/skills/testing/e2e-parity.md +125 -0
  46. package/skills/testing/flight.md +91 -0
  47. package/skills/testing/handles.md +129 -0
  48. package/skills/testing/loader.md +128 -0
  49. package/skills/testing/middleware.md +99 -0
  50. package/skills/testing/render-handler.md +121 -0
  51. package/skills/testing/response-routes.md +95 -0
  52. package/skills/testing/reverse-and-types.md +84 -0
  53. package/skills/testing/server-actions.md +107 -0
  54. package/skills/testing/server-tree.md +128 -0
  55. package/skills/testing/setup.md +123 -0
  56. package/skills/typesafety/SKILL.md +332 -29
  57. package/skills/use-cache/SKILL.md +46 -14
  58. package/skills/view-transitions/SKILL.md +294 -0
  59. package/src/__augment-tests__/augment.ts +81 -0
  60. package/src/__augment-tests__/augmented.check.ts +116 -0
  61. package/src/__internal.ts +1 -66
  62. package/src/browser/action-coordinator.ts +53 -36
  63. package/src/browser/action-fence.ts +47 -0
  64. package/src/browser/app-shell.ts +39 -0
  65. package/src/browser/app-version.ts +14 -0
  66. package/src/browser/connection-warmup.ts +134 -0
  67. package/src/browser/cookie-name.ts +140 -0
  68. package/src/browser/event-controller.ts +197 -150
  69. package/src/browser/history-state.ts +21 -0
  70. package/src/browser/index.ts +3 -3
  71. package/src/browser/invalidate-client-cache.ts +52 -0
  72. package/src/browser/navigation-bridge.ts +111 -31
  73. package/src/browser/navigation-client.ts +201 -67
  74. package/src/browser/navigation-store-handle.ts +38 -0
  75. package/src/browser/navigation-store.ts +76 -67
  76. package/src/browser/navigation-transaction.ts +18 -66
  77. package/src/browser/network-error-handler.ts +34 -7
  78. package/src/browser/partial-update.ts +187 -112
  79. package/src/browser/prefetch/cache.ts +230 -35
  80. package/src/browser/prefetch/fetch.ts +338 -39
  81. package/src/browser/prefetch/queue.ts +126 -20
  82. package/src/browser/prefetch/resource-ready.ts +77 -0
  83. package/src/browser/rango-state.ts +158 -76
  84. package/src/browser/react/Link.tsx +111 -16
  85. package/src/browser/react/NavigationProvider.tsx +135 -120
  86. package/src/browser/react/ScrollRestoration.tsx +10 -6
  87. package/src/browser/react/context.ts +7 -2
  88. package/src/browser/react/filter-segment-order.ts +66 -7
  89. package/src/browser/react/index.ts +0 -48
  90. package/src/browser/react/location-state-shared.ts +178 -8
  91. package/src/browser/react/location-state.ts +39 -14
  92. package/src/browser/react/use-action.ts +6 -15
  93. package/src/browser/react/use-handle.ts +23 -69
  94. package/src/browser/react/use-href.tsx +8 -1
  95. package/src/browser/react/use-link-status.ts +33 -8
  96. package/src/browser/react/use-navigation.ts +32 -7
  97. package/src/browser/react/use-params.ts +20 -10
  98. package/src/browser/react/use-reverse.ts +106 -0
  99. package/src/browser/react/use-router.ts +46 -11
  100. package/src/browser/react/use-search-params.ts +0 -5
  101. package/src/browser/react/use-segments.ts +11 -21
  102. package/src/browser/response-adapter.ts +80 -5
  103. package/src/browser/rsc-router.tsx +218 -76
  104. package/src/browser/scroll-restoration.ts +54 -42
  105. package/src/browser/segment-reconciler.ts +36 -9
  106. package/src/browser/segment-structure-assert.ts +2 -2
  107. package/src/browser/server-action-bridge.ts +222 -61
  108. package/src/browser/types.ts +91 -11
  109. package/src/browser/validate-redirect-origin.ts +43 -16
  110. package/src/build/collect-fallback-refs.ts +107 -0
  111. package/src/build/generate-manifest.ts +65 -40
  112. package/src/build/generate-route-types.ts +5 -1
  113. package/src/build/index.ts +8 -2
  114. package/src/build/prefix-tree-utils.ts +123 -0
  115. package/src/build/route-trie.ts +165 -36
  116. package/src/build/route-types/ast-route-extraction.ts +15 -8
  117. package/src/build/route-types/codegen.ts +16 -5
  118. package/src/build/route-types/include-resolution.ts +125 -24
  119. package/src/build/route-types/param-extraction.ts +6 -3
  120. package/src/build/route-types/per-module-writer.ts +22 -6
  121. package/src/build/route-types/router-processing.ts +272 -96
  122. package/src/build/route-types/scan-filter.ts +9 -2
  123. package/src/build/route-types/source-scan.ts +216 -0
  124. package/src/build/runtime-discovery.ts +9 -20
  125. package/src/cache/cache-error.ts +104 -0
  126. package/src/cache/cache-key-utils.ts +29 -13
  127. package/src/cache/cache-policy.ts +108 -34
  128. package/src/cache/cache-runtime.ts +214 -48
  129. package/src/cache/cache-scope.ts +236 -89
  130. package/src/cache/cache-tag.ts +103 -0
  131. package/src/cache/cf/cf-base64.ts +33 -0
  132. package/src/cache/cf/cf-cache-constants.ts +127 -0
  133. package/src/cache/cf/cf-cache-store.ts +2224 -171
  134. package/src/cache/cf/cf-cache-types.ts +349 -0
  135. package/src/cache/cf/cf-kv-utils.ts +46 -0
  136. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  137. package/src/cache/cf/index.ts +11 -17
  138. package/src/cache/document-cache.ts +89 -27
  139. package/src/cache/handle-snapshot.ts +70 -0
  140. package/src/cache/index.ts +11 -20
  141. package/src/cache/memory-segment-store.ts +136 -37
  142. package/src/cache/profile-registry.ts +31 -31
  143. package/src/cache/read-through-swr.ts +41 -11
  144. package/src/cache/segment-codec.ts +9 -17
  145. package/src/cache/tag-invalidation.ts +230 -0
  146. package/src/cache/taint.ts +55 -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 +84 -2
  155. package/src/debug.ts +2 -2
  156. package/src/decode-loader-results.ts +52 -0
  157. package/src/defer.ts +196 -0
  158. package/src/deps/ssr.ts +0 -1
  159. package/src/encode-kv.ts +49 -0
  160. package/src/errors.ts +30 -4
  161. package/src/escape-script.ts +52 -0
  162. package/src/handle.ts +70 -22
  163. package/src/handles/MetaTags.tsx +56 -19
  164. package/src/handles/Scripts.tsx +183 -0
  165. package/src/handles/breadcrumbs.ts +37 -8
  166. package/src/handles/is-thenable.ts +19 -0
  167. package/src/handles/meta.ts +51 -40
  168. package/src/handles/script.ts +244 -0
  169. package/src/host/cookie-handler.ts +9 -60
  170. package/src/host/errors.ts +0 -24
  171. package/src/host/index.ts +8 -2
  172. package/src/host/pattern-matcher.ts +23 -52
  173. package/src/host/router.ts +107 -99
  174. package/src/host/testing.ts +40 -27
  175. package/src/host/types.ts +37 -4
  176. package/src/host/utils.ts +1 -1
  177. package/src/href-client.ts +137 -22
  178. package/src/index.rsc.ts +93 -12
  179. package/src/index.ts +133 -15
  180. package/src/internal-debug.ts +11 -10
  181. package/src/loader-store.ts +500 -0
  182. package/src/loader.rsc.ts +20 -13
  183. package/src/loader.ts +12 -11
  184. package/src/missing-id-error.ts +68 -0
  185. package/src/outlet-context.ts +1 -1
  186. package/src/outlet-provider.tsx +1 -5
  187. package/src/prerender/param-hash.ts +16 -16
  188. package/src/prerender/store.ts +37 -41
  189. package/src/prerender.ts +198 -82
  190. package/src/redirect-origin.ts +100 -0
  191. package/src/regex-escape.ts +8 -0
  192. package/src/render-error-thrower.tsx +20 -0
  193. package/src/response-utils.ts +62 -0
  194. package/src/reverse.ts +65 -15
  195. package/src/root-error-boundary.tsx +1 -19
  196. package/src/route-content-wrapper.tsx +7 -72
  197. package/src/route-definition/dsl-helpers.ts +469 -276
  198. package/src/route-definition/helper-factories.ts +29 -139
  199. package/src/route-definition/helpers-types.ts +113 -37
  200. package/src/route-definition/index.ts +3 -0
  201. package/src/route-definition/redirect.ts +53 -12
  202. package/src/route-definition/resolve-handler-use.ts +161 -0
  203. package/src/route-definition/use-item-types.ts +32 -0
  204. package/src/route-map-builder.ts +7 -17
  205. package/src/route-types.ts +37 -41
  206. package/src/router/basename.ts +14 -0
  207. package/src/router/content-negotiation.ts +164 -17
  208. package/src/router/error-handling.ts +45 -18
  209. package/src/router/find-match.ts +45 -22
  210. package/src/router/handler-context.ts +83 -39
  211. package/src/router/instrument.ts +350 -0
  212. package/src/router/intercept-resolution.ts +50 -24
  213. package/src/router/lazy-includes.ts +19 -53
  214. package/src/router/loader-resolution.ts +274 -56
  215. package/src/router/logging.ts +5 -8
  216. package/src/router/manifest.ts +49 -45
  217. package/src/router/match-api.ts +120 -204
  218. package/src/router/match-context.ts +0 -22
  219. package/src/router/match-handlers.ts +58 -58
  220. package/src/router/match-middleware/background-revalidation.ts +33 -6
  221. package/src/router/match-middleware/cache-lookup.ts +214 -263
  222. package/src/router/match-middleware/cache-store.ts +73 -33
  223. package/src/router/match-middleware/intercept-resolution.ts +8 -28
  224. package/src/router/match-middleware/segment-resolution.ts +52 -18
  225. package/src/router/match-pipelines.ts +1 -42
  226. package/src/router/match-result.ts +104 -40
  227. package/src/router/metrics.ts +5 -34
  228. package/src/router/middleware-types.ts +13 -142
  229. package/src/router/middleware.ts +270 -172
  230. package/src/router/navigation-snapshot.ts +131 -0
  231. package/src/router/params-util.ts +23 -0
  232. package/src/router/pattern-matching.ts +132 -90
  233. package/src/router/prefetch-cache-ttl.ts +51 -0
  234. package/src/router/prerender-match.ts +195 -56
  235. package/src/router/preview-match.ts +32 -102
  236. package/src/router/request-classification.ts +276 -0
  237. package/src/router/revalidation.ts +123 -73
  238. package/src/router/route-snapshot.ts +244 -0
  239. package/src/router/router-context.ts +8 -28
  240. package/src/router/router-interfaces.ts +115 -35
  241. package/src/router/router-options.ts +172 -15
  242. package/src/router/router-registry.ts +2 -5
  243. package/src/router/segment-resolution/fresh.ts +264 -77
  244. package/src/router/segment-resolution/helpers.ts +115 -30
  245. package/src/router/segment-resolution/loader-cache.ts +63 -37
  246. package/src/router/segment-resolution/revalidation.ts +474 -385
  247. package/src/router/segment-resolution/static-store.ts +19 -5
  248. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  249. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  250. package/src/router/segment-resolution.ts +5 -1
  251. package/src/router/segment-wrappers.ts +8 -5
  252. package/src/router/state-cookie-name.ts +33 -0
  253. package/src/router/substitute-pattern-params.ts +56 -0
  254. package/src/router/telemetry-otel.ts +161 -199
  255. package/src/router/telemetry.ts +96 -19
  256. package/src/router/timeout.ts +0 -20
  257. package/src/router/tracing.ts +206 -0
  258. package/src/router/trie-matching.ts +163 -59
  259. package/src/router/types.ts +10 -63
  260. package/src/router/url-params.ts +44 -0
  261. package/src/router.ts +163 -55
  262. package/src/rsc/handler-context.ts +3 -2
  263. package/src/rsc/handler.ts +658 -511
  264. package/src/rsc/helpers.ts +168 -46
  265. package/src/rsc/index.ts +2 -5
  266. package/src/rsc/json-route-result.ts +38 -0
  267. package/src/rsc/loader-fetch.ts +127 -31
  268. package/src/rsc/manifest-init.ts +33 -42
  269. package/src/rsc/origin-guard.ts +39 -25
  270. package/src/rsc/progressive-enhancement.ts +77 -11
  271. package/src/rsc/redirect-guard.ts +99 -0
  272. package/src/rsc/response-cache-serve.ts +238 -0
  273. package/src/rsc/response-error.ts +79 -12
  274. package/src/rsc/response-route-handler.ts +99 -189
  275. package/src/rsc/rsc-rendering.ts +105 -72
  276. package/src/rsc/runtime-warnings.ts +23 -10
  277. package/src/rsc/server-action.ts +263 -112
  278. package/src/rsc/ssr-setup.ts +18 -2
  279. package/src/rsc/types.ts +32 -6
  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 +281 -129
  285. package/src/serialize.ts +243 -0
  286. package/src/server/context.ts +309 -61
  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 +26 -46
  291. package/src/server/request-context.ts +398 -172
  292. package/src/ssr/index.tsx +25 -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 +99 -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 +343 -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/vitest-stubs/cloudflare-email.ts +9 -0
  320. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  321. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  322. package/src/testing/vitest-stubs/version.ts +5 -0
  323. package/src/testing/vitest.ts +305 -0
  324. package/src/theme/ThemeProvider.tsx +20 -58
  325. package/src/theme/ThemeScript.tsx +7 -9
  326. package/src/theme/constants.ts +52 -13
  327. package/src/theme/index.ts +0 -7
  328. package/src/theme/theme-context.ts +1 -5
  329. package/src/theme/theme-script.ts +22 -21
  330. package/src/theme/use-theme.ts +0 -3
  331. package/src/types/boundaries.ts +0 -35
  332. package/src/types/cache-types.ts +17 -8
  333. package/src/types/error-types.ts +30 -90
  334. package/src/types/global-namespace.ts +54 -41
  335. package/src/types/handler-context.ts +233 -81
  336. package/src/types/index.ts +1 -10
  337. package/src/types/loader-types.ts +44 -15
  338. package/src/types/request-scope.ts +112 -0
  339. package/src/types/route-config.ts +6 -50
  340. package/src/types/route-entry.ts +19 -7
  341. package/src/types/segments.ts +37 -14
  342. package/src/urls/include-helper.ts +33 -70
  343. package/src/urls/index.ts +1 -11
  344. package/src/urls/path-helper-types.ts +58 -11
  345. package/src/urls/path-helper.ts +57 -111
  346. package/src/urls/pattern-types.ts +48 -19
  347. package/src/urls/response-types.ts +25 -22
  348. package/src/urls/type-extraction.ts +58 -139
  349. package/src/urls/urls-function.ts +1 -18
  350. package/src/use-loader.tsx +346 -89
  351. package/src/vite/debug.ts +185 -0
  352. package/src/vite/discovery/bundle-postprocess.ts +36 -38
  353. package/src/vite/discovery/discover-routers.ts +130 -85
  354. package/src/vite/discovery/discovery-errors.ts +194 -0
  355. package/src/vite/discovery/gate-state.ts +171 -0
  356. package/src/vite/discovery/prerender-collection.ts +214 -132
  357. package/src/vite/discovery/route-types-writer.ts +40 -84
  358. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  359. package/src/vite/discovery/state.ts +57 -6
  360. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  361. package/src/vite/index.ts +6 -0
  362. package/src/vite/inject-client-debug.ts +36 -0
  363. package/src/vite/plugin-types.ts +155 -65
  364. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  365. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  366. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  367. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  368. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  369. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  370. package/src/vite/plugins/expose-action-id.ts +49 -98
  371. package/src/vite/plugins/expose-id-utils.ts +96 -51
  372. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  373. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  374. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  375. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  376. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  377. package/src/vite/plugins/performance-tracks.ts +89 -0
  378. package/src/vite/plugins/refresh-cmd.ts +89 -27
  379. package/src/vite/plugins/use-cache-transform.ts +73 -83
  380. package/src/vite/plugins/version-injector.ts +21 -25
  381. package/src/vite/plugins/version-plugin.ts +46 -37
  382. package/src/vite/plugins/virtual-entries.ts +13 -18
  383. package/src/vite/rango.ts +238 -295
  384. package/src/vite/router-discovery.ts +940 -149
  385. package/src/vite/utils/ast-handler-extract.ts +26 -35
  386. package/src/vite/utils/banner.ts +4 -4
  387. package/src/vite/utils/bundle-analysis.ts +10 -15
  388. package/src/vite/utils/client-chunks.ts +184 -0
  389. package/src/vite/utils/directive-prologue.ts +40 -0
  390. package/src/vite/utils/forward-user-plugins.ts +171 -0
  391. package/src/vite/utils/manifest-utils.ts +4 -59
  392. package/src/vite/utils/package-resolution.ts +20 -52
  393. package/src/vite/utils/prerender-utils.ts +81 -34
  394. package/src/vite/utils/shared-utils.ts +92 -42
  395. package/src/browser/action-response-classifier.ts +0 -99
  396. package/src/browser/react/use-client-cache.ts +0 -58
  397. package/src/browser/shallow.ts +0 -40
  398. package/src/handles/index.ts +0 -7
  399. package/src/network-error-thrower.tsx +0 -23
  400. package/src/router/middleware-cookies.ts +0 -55
@@ -14,7 +14,6 @@ import type { RenderSegmentsOptions } from "../segment-system.js";
14
14
  export interface RscPayload<TMetadata = RscMetadata> {
15
15
  metadata?: TMetadata;
16
16
  returnValue?: ActionResult;
17
- formState?: unknown;
18
17
  }
19
18
 
20
19
  /**
@@ -32,10 +31,21 @@ export type HandleData = Record<string, Record<string, unknown[]>>;
32
31
  export interface RscMetadata {
33
32
  pathname: string;
34
33
  segments: ResolvedSegment[];
34
+ /** Router instance ID — the current app's identity. A mismatch with the
35
+ * client's id (sent as _rsc_rid) is detected server-side and answered with
36
+ * X-RSC-Reload (full document load), so the client never swaps apps
37
+ * in-session; within a session this always equals the current app. */
38
+ routerId?: string;
35
39
  isPartial?: boolean;
36
40
  isError?: boolean;
37
41
  matched?: string[];
38
42
  diff?: string[];
43
+ /**
44
+ * All segment ids re-resolved on the server, including null-component
45
+ * ones excluded from `segments`/`diff`. Drives client-side handle-bucket
46
+ * cleanup. Superset of `diff`. See MatchResult.resolvedIds.
47
+ */
48
+ resolvedIds?: string[];
39
49
  /** Merged route params from the matched route */
40
50
  params?: Record<string, string>;
41
51
  /**
@@ -60,6 +70,12 @@ export interface RscMetadata {
60
70
  * Sent on initial render so the browser can configure its cache duration.
61
71
  */
62
72
  prefetchCacheTTL?: number;
73
+ /**
74
+ * Server-resolved rango state cookie name (`{prefix}_{routerId}`). The client
75
+ * reads it verbatim and binds the rango state cookie to it; composition
76
+ * happens only server-side.
77
+ */
78
+ stateCookieName?: string;
63
79
  /**
64
80
  * Theme configuration from router.
65
81
  * Included when theme is enabled in router config.
@@ -70,10 +86,22 @@ export interface RscMetadata {
70
86
  * Included when theme is enabled in router config.
71
87
  */
72
88
  initialTheme?: Theme;
89
+ /** URL prefix for all routes (from createRouter({ basename })). */
90
+ basename?: string;
73
91
  /** Whether connection warmup is enabled */
74
92
  warmupEnabled?: boolean;
75
- /** Server-side redirect with optional state (for partial requests) */
76
- redirect?: { url: string };
93
+ /**
94
+ * Whether the client should hydrate inside React.StrictMode. Carried on the
95
+ * initial full-render payload only; the browser entry reads it once at
96
+ * hydration. Defaults to true on the client when omitted.
97
+ */
98
+ strictMode?: boolean;
99
+ /**
100
+ * Server-side redirect with optional state (for partial requests).
101
+ * `external: true` (from redirect(url, { external: true })) tells the client
102
+ * to hard-navigate to an off-host target instead of validating same-origin.
103
+ */
104
+ redirect?: { url: string; external?: boolean };
77
105
  /** Server-set location state to include in history.pushState */
78
106
  locationState?: Record<string, unknown>;
79
107
  }
@@ -174,6 +202,15 @@ export interface TrackedActionState {
174
202
  result: unknown | null;
175
203
  }
176
204
 
205
+ /**
206
+ * The value returned by {@link useAction} when called without a selector.
207
+ *
208
+ * This is the stable, public name for the action-state shape; consumers can
209
+ * name it in their own signatures (e.g. a wrapper hook). It aliases the
210
+ * internal {@link TrackedActionState}.
211
+ */
212
+ export type ActionState = TrackedActionState;
213
+
177
214
  /**
178
215
  * Listener for action state changes
179
216
  *
@@ -215,6 +252,15 @@ export interface SegmentState {
215
252
  export interface NavigationUpdate {
216
253
  root: ReactNode | Promise<ReactNode>;
217
254
  metadata: RscMetadata;
255
+ /** Scroll behavior to apply after React commits this update */
256
+ scroll?: {
257
+ /** For back/forward: restore saved position */
258
+ restore?: boolean;
259
+ /** Set to false to disable scrolling entirely */
260
+ enabled?: boolean;
261
+ /** Function to check if streaming is in progress */
262
+ isStreaming?: () => boolean;
263
+ };
218
264
  }
219
265
 
220
266
  /**
@@ -302,8 +348,14 @@ export interface RouterInstance {
302
348
  replace(url: string, options?: RouterNavigateOptions): Promise<void>;
303
349
  /** Refresh the current route (re-fetch server data, preserve client state) */
304
350
  refresh(): Promise<void>;
305
- /** Prefetch a URL for faster client-side transition */
306
- prefetch(url: string): void;
351
+ /**
352
+ * Prefetch a URL for faster client-side transition.
353
+ *
354
+ * Pass `{ key: ":source" }` to source-scope the prefetch cache entry (parity
355
+ * with `<Link prefetchKey=":source">`) when the target's response can differ
356
+ * by source page.
357
+ */
358
+ prefetch(url: string, options?: { key?: ":source" }): void;
307
359
  /** Go back in browser history */
308
360
  back(): void;
309
361
  /** Go forward in browser history */
@@ -332,7 +384,13 @@ export type ReadonlyURLSearchParams = Omit<
332
384
  export interface RscBrowserDependencies {
333
385
  createFromFetch: <T>(
334
386
  response: Promise<Response>,
335
- options?: { temporaryReferences?: any },
387
+ options?: {
388
+ temporaryReferences?: any;
389
+ findSourceMapURL?: (
390
+ filename: string,
391
+ environmentName: string,
392
+ ) => string | null;
393
+ },
336
394
  ) => Promise<T>;
337
395
  createFromReadableStream: <T>(stream: ReadableStream) => Promise<T>;
338
396
  encodeReply: (
@@ -394,17 +452,20 @@ export interface NavigationStore {
394
452
  segments: ResolvedSegment[],
395
453
  handleData?: HandleData,
396
454
  ): void;
397
- getCachedSegments(
398
- historyKey: string,
399
- ):
400
- | { segments: ResolvedSegment[]; stale: boolean; handleData?: HandleData }
455
+ getCachedSegments(historyKey: string):
456
+ | {
457
+ segments: ResolvedSegment[];
458
+ stale: boolean;
459
+ handleData?: HandleData;
460
+ routerId?: string;
461
+ }
401
462
  | undefined;
402
463
  hasHistoryCache(historyKey: string): boolean;
403
464
  updateCacheHandleData(historyKey: string, handleData: HandleData): void;
404
465
  markCacheAsStale(): void;
466
+ markHistoryCacheStale(): void;
405
467
  markCacheAsStaleAndBroadcast(): void;
406
468
  clearHistoryCache(): void;
407
- broadcastCacheInvalidation(): void;
408
469
 
409
470
  // Cross-tab refresh callback (set by navigation bridge)
410
471
  setCrossTabRefreshCallback(callback: () => void): void;
@@ -413,6 +474,10 @@ export interface NavigationStore {
413
474
  getInterceptSourceUrl(): string | null;
414
475
  setInterceptSourceUrl(url: string | null): void;
415
476
 
477
+ // Router identity tracking (for cross-app navigation detection)
478
+ getRouterId?(): string | undefined;
479
+ setRouterId?(id: string): void;
480
+
416
481
  // UI update notifications
417
482
  onUpdate(callback: UpdateSubscriber): () => void;
418
483
  emitUpdate(update: NavigationUpdate): void;
@@ -443,6 +508,8 @@ export interface FetchPartialOptions {
443
508
  interceptSourceUrl?: string;
444
509
  /** RSC version for cache invalidation detection */
445
510
  version?: string;
511
+ /** Current router ID — server detects app switch and returns full response */
512
+ routerId?: string;
446
513
  /** If true, this is an HMR refetch - server should invalidate manifest cache */
447
514
  hmr?: boolean;
448
515
  }
@@ -454,6 +521,15 @@ export interface FetchPartialResult {
454
521
  payload: RscPayload;
455
522
  /** Promise that resolves when the response stream is fully consumed */
456
523
  streamComplete: Promise<void>;
524
+ /**
525
+ * True only when this payload came from a prefetch-cache hit whose stream had
526
+ * ALREADY fully drained at fetch time (the route was fully prefetched). The
527
+ * commit then runs in a startTransition so loading()/Suspense content — already
528
+ * resolved — swaps in directly without flashing a fallback. A partially-warmed
529
+ * (still-streaming) prefetch hit and a cold fetch leave this false so their
530
+ * fallbacks stream as usual.
531
+ */
532
+ fullyPrefetched?: boolean;
457
533
  }
458
534
 
459
535
  /**
@@ -511,6 +587,10 @@ export interface NavigationBridge {
511
587
  refresh(): Promise<void>;
512
588
  handlePopstate(): Promise<void>;
513
589
  registerLinkInterception(): () => void;
590
+ /** Current RSC version (live, reflects the latest updateVersion). */
591
+ getVersion(): string | undefined;
592
+ /** Update the RSC version (e.g. after HMR). Clears prefetch cache. */
593
+ updateVersion(newVersion: string): void;
514
594
  }
515
595
 
516
596
  /**
@@ -1,29 +1,56 @@
1
+ import {
2
+ resolveSameOriginRedirect,
3
+ resolveExternalRedirect,
4
+ } from "../redirect-origin.js";
5
+
1
6
  /**
2
7
  * Validate that a client-consumed redirect URL (from headers or Flight payload)
3
8
  * targets the same origin as the current page. Prevents open-redirect attacks
4
9
  * via crafted responses.
5
10
  *
11
+ * Thin wrapper over the shared {@link resolveSameOriginRedirect} rule (also used
12
+ * by the server guard in `rsc/redirect-guard.ts`) so client and server enforce
13
+ * the identical same-origin contract. Adds the client-side `console.error` on a
14
+ * block; the resolver itself stays pure.
15
+ *
6
16
  * @returns The canonical (normalized) URL string on success, or null if blocked.
7
17
  */
8
18
  export function validateRedirectOrigin(
9
19
  url: string,
10
20
  currentOrigin: string,
11
21
  ): string | null {
12
- try {
13
- const target = new URL(url, currentOrigin);
14
- if (target.origin !== currentOrigin) {
15
- console.error(
16
- `[rango] Redirect blocked: origin mismatch (${target.origin})`,
17
- );
18
- return null;
19
- }
20
- // Return pathname+search+hash for relative inputs, full href for absolute.
21
- // This normalizes protocol-relative and other ambiguous forms.
22
- return target.href.startsWith(currentOrigin)
23
- ? target.href
24
- : target.pathname + target.search + target.hash;
25
- } catch {
26
- console.error(`[rango] Redirect blocked: invalid URL "${url}"`);
27
- return null;
22
+ const resolved = resolveSameOriginRedirect(url, currentOrigin);
23
+ if (resolved === null) {
24
+ console.error(
25
+ `[rango] Redirect blocked: cross-origin or invalid target "${url}"`,
26
+ );
27
+ }
28
+ return resolved;
29
+ }
30
+
31
+ /**
32
+ * Validate an explicit off-origin redirect (`redirect(url, { external: true })`)
33
+ * the client is about to hard-navigate to via `window.location.assign()`.
34
+ *
35
+ * Thin wrapper over the shared {@link resolveExternalRedirect} rule (also used by
36
+ * the server guard in `rsc/redirect-guard.ts`) so client and server enforce the
37
+ * identical contract: `external` allows an off-origin target but only an
38
+ * http(s) scheme. This stops a forged or mistaken external payload carrying a
39
+ * `javascript:`/`data:` URL from turning `location.assign` into a scriptable
40
+ * navigation. Adds the client-side `console.error` on a block; the resolver
41
+ * itself stays pure.
42
+ *
43
+ * @returns The normalized URL string on success, or null if blocked.
44
+ */
45
+ export function validateExternalRedirect(
46
+ url: string,
47
+ currentOrigin: string,
48
+ ): string | null {
49
+ const resolved = resolveExternalRedirect(url, currentOrigin);
50
+ if (resolved === null) {
51
+ console.error(
52
+ `[rango] External redirect blocked: non-http(s) target "${url}"`,
53
+ );
28
54
  }
55
+ return resolved;
29
56
  }
@@ -0,0 +1,107 @@
1
+ // Collect the `"use client"` client-reference keys reachable from an error /
2
+ // notFound boundary registration, for routing them into the dedicated
3
+ // `app-fallback` chunk (see vite/utils/client-chunks.ts).
4
+ //
5
+ // A boundary registration is not always a bare client element. The common,
6
+ // load-bearing pattern wraps the client boundary in providers a thrown handler
7
+ // needs (the layout that would normally supply them did not mount):
8
+ //
9
+ // defaultErrorBoundary: ({ error }) => (
10
+ // <FallbackIntl locales={...}>
11
+ // <ThemedError error={error} /> // <- the real "use client" boundary
12
+ // </FallbackIntl>
13
+ // )
14
+ //
15
+ // So the value may be (a) a handler FUNCTION returning a tree, or (b) an element
16
+ // tree with the client boundary nested below server wrappers. We:
17
+ // 1. If it's a function, CALL it with synthetic props to get the returned tree.
18
+ // This only constructs JSX — the inner components are element `type`s, never
19
+ // invoked — so no hooks run. Guarded: a boundary that needs a real render
20
+ // context (request globals, etc.) throws and is skipped (graceful: it simply
21
+ // stays on the default grouping, as before).
22
+ // 2. Walk the resulting tree and report every element whose `.type` is a
23
+ // plugin-rsc client reference.
24
+ //
25
+ // Limit: a boundary that *conditionally* renders different client components based
26
+ // on the runtime error cannot be resolved statically — only the branch taken with
27
+ // the synthetic error is seen. Such cases fall back to the default chunk; the
28
+ // custom `clientChunks` function is the escape hatch.
29
+
30
+ const CLIENT_REF = Symbol.for("react.client.reference");
31
+ const MAX_DEPTH = 40;
32
+
33
+ // Synthetic props covering the error-boundary (`{ error, reset }`) and notFound
34
+ // (`{ pathname }`) handler shapes. The handler destructures what it needs.
35
+ const SYNTHETIC_PROPS = {
36
+ error: new Error("rango: build-time fallback-chunk discovery"),
37
+ reset: () => {},
38
+ pathname: "/",
39
+ info: { componentStack: "" },
40
+ };
41
+
42
+ interface MaybeElement {
43
+ type?: { $$typeof?: symbol; $$id?: string };
44
+ props?: Record<string, unknown>;
45
+ }
46
+
47
+ function isReactNodeLike(v: unknown): boolean {
48
+ return (
49
+ Array.isArray(v) ||
50
+ (typeof v === "object" && v !== null && "$$typeof" in (v as object))
51
+ );
52
+ }
53
+
54
+ function walkElementTree(
55
+ node: unknown,
56
+ report: (refKey: string) => void,
57
+ depth: number,
58
+ ): void {
59
+ if (node == null || depth > MAX_DEPTH) return;
60
+ if (Array.isArray(node)) {
61
+ for (const child of node) walkElementTree(child, report, depth + 1);
62
+ return;
63
+ }
64
+ if (typeof node !== "object") return;
65
+
66
+ const el = node as MaybeElement;
67
+ const type = el.type;
68
+ if (type?.$$typeof === CLIENT_REF && typeof type.$$id === "string") {
69
+ // $$id is `<referenceKey>#<exportName>` in build mode — keep the referenceKey.
70
+ report(type.$$id.split("#")[0]);
71
+ }
72
+
73
+ const props = el.props;
74
+ if (props && typeof props === "object") {
75
+ // Children are always nodes; other props are followed only when they look
76
+ // like React nodes (slots/icons), never arbitrary data objects.
77
+ walkElementTree(props.children, report, depth + 1);
78
+ for (const key in props) {
79
+ if (key === "children") continue;
80
+ const value = props[key];
81
+ if (isReactNodeLike(value)) walkElementTree(value, report, depth + 1);
82
+ }
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Report every `"use client"` client-reference key reachable from a single
88
+ * error/notFound boundary registration (handler function or element tree).
89
+ */
90
+ export function collectFallbackClientRefs(
91
+ boundary: unknown,
92
+ report: (refKey: string) => void,
93
+ ): void {
94
+ try {
95
+ let node = boundary;
96
+ if (typeof node === "function") {
97
+ node = (node as (props: unknown) => unknown)(SYNTHETIC_PROPS);
98
+ }
99
+ walkElementTree(node, report, 0);
100
+ } catch {
101
+ // The boundary needs a real render context (request globals, hooks at the
102
+ // top level) or its tree has hostile getters. Its client refs can't be
103
+ // resolved statically — skip. It stays on the default grouping (no
104
+ // regression vs. not collecting), and the custom clientChunks fn is the
105
+ // escape hatch for such cases.
106
+ }
107
+ }
@@ -11,11 +11,12 @@
11
11
  import type { UrlPatterns } from "../urls.js";
12
12
  import type { AllUseItems } from "../route-types.js";
13
13
  import { extractStaticPrefix } from "../router/pattern-matching.js";
14
- import { RSCRouterContext, runWithPrefixes } from "../server/context.js";
14
+ import { RangoContext, runWithPrefixes } from "../server/context.js";
15
15
  import type { EntryData, TrackedInclude } from "../server/context.js";
16
16
  import type { TrailingSlashMode } from "../types.js";
17
17
  import { createRouteHelpers } from "../route-definition.js";
18
18
  import MapRootLayout from "../server/root-layout.js";
19
+ import { collectFallbackClientRefs } from "./collect-fallback-refs.js";
19
20
 
20
21
  /**
21
22
  * Node in the prefix tree
@@ -45,7 +46,7 @@ export interface GeneratedManifest {
45
46
  routeTrailingSlash?: Record<string, string>;
46
47
  /** Route names using Prerender (for dev-mode Node.js delegation) */
47
48
  prerenderRoutes?: string[];
48
- /** Route names with passthrough: true (handler kept in bundle for live fallback) */
49
+ /** Route names wrapped with Passthrough() (live handler for runtime fallback) */
49
50
  passthroughRoutes?: string[];
50
51
  /** Route name → response type for non-RSC routes */
51
52
  responseTypeRoutes?: Record<string, string>;
@@ -57,6 +58,26 @@ export interface GeneratedManifest {
57
58
  * Build prefix tree node by running the patterns with proper context.
58
59
  * Uses a visited set to detect circular includes and prevent infinite recursion.
59
60
  */
61
+ // Merge tracked nested includes into `target`. Multiple includes can share a
62
+ // fullPrefix (e.g. include("/", a), include("/", b)) — concat their routes and
63
+ // Object.assign children rather than overwrite.
64
+ function mergeIncludeNodes(
65
+ target: Record<string, PrefixTreeNode>,
66
+ includes: TrackedInclude[],
67
+ buildChild: (include: TrackedInclude) => PrefixTreeNode,
68
+ ): void {
69
+ for (const include of includes) {
70
+ const node = buildChild(include);
71
+ const existing = target[include.fullPrefix];
72
+ if (existing) {
73
+ existing.routes.push(...node.routes);
74
+ Object.assign(existing.children, node.children);
75
+ } else {
76
+ target[include.fullPrefix] = node;
77
+ }
78
+ }
79
+ }
80
+
60
81
  function buildPrefixTreeNode(
61
82
  urlPrefix: string,
62
83
  namePrefix: string | undefined,
@@ -93,7 +114,7 @@ function buildPrefixTreeNode(
93
114
  const searchSchemasMap = new Map<string, Record<string, string>>();
94
115
  const trackedIncludes: TrackedInclude[] = [];
95
116
 
96
- RSCRouterContext.run(
117
+ RangoContext.run(
97
118
  {
98
119
  manifest,
99
120
  patterns: patternsMap,
@@ -150,10 +171,7 @@ function buildPrefixTreeNode(
150
171
  if (prerenderDefs && entry.prerenderDef) {
151
172
  prerenderDefs[name] = entry.prerenderDef;
152
173
  }
153
- if (
154
- passthroughRoutes &&
155
- entry.prerenderDef?.options?.passthrough === true
156
- ) {
174
+ if (passthroughRoutes && entry.isPassthrough === true) {
157
175
  passthroughRoutes.push(name);
158
176
  }
159
177
  }
@@ -169,13 +187,9 @@ function buildPrefixTreeNode(
169
187
  }
170
188
  }
171
189
 
172
- // Build children from tracked nested includes.
173
- // Multiple includes can share the same fullPrefix (e.g., include("/", patternsA),
174
- // include("/", patternsB)). Merge their routes instead of overwriting.
175
190
  const children: Record<string, PrefixTreeNode> = {};
176
-
177
- for (const include of trackedIncludes) {
178
- const childNode = buildPrefixTreeNode(
191
+ mergeIncludeNodes(children, trackedIncludes, (include) =>
192
+ buildPrefixTreeNode(
179
193
  include.fullPrefix,
180
194
  include.namePrefix,
181
195
  include.patterns as UrlPatterns<any>,
@@ -189,16 +203,8 @@ function buildPrefixTreeNode(
189
203
  passthroughRoutes,
190
204
  responseTypeRoutes,
191
205
  routeSearchSchemas,
192
- );
193
-
194
- const existing = children[include.fullPrefix];
195
- if (existing) {
196
- existing.routes.push(...childNode.routes);
197
- Object.assign(existing.children, childNode.children);
198
- } else {
199
- children[include.fullPrefix] = childNode;
200
- }
201
- }
206
+ ),
207
+ );
202
208
 
203
209
  // Remove from visited so sibling branches can reuse the same patterns
204
210
  // without false circular-include detection. Only ancestors in the current
@@ -285,6 +291,17 @@ export function generateManifest<TEnv>(
285
291
  export function generateManifestFull<TEnv>(
286
292
  urlpatterns: UrlPatterns<TEnv, any>,
287
293
  mountIndex: number = 0,
294
+ options?: {
295
+ urlPrefix?: string;
296
+ /**
297
+ * Called once per `"use client"` component registered as an
298
+ * errorBoundary/notFoundBoundary fallback, with its client-reference key
299
+ * (`$$id`). Lets the build collect fallback module ids for dedicated
300
+ * chunking without exposing the otherwise-discarded EntryData tree. The
301
+ * EntryData map built below is local; this is the only seam that surfaces it.
302
+ */
303
+ collectClientFallbackRef?: (refKey: string) => void;
304
+ },
288
305
  ): FullManifest {
289
306
  const routeManifest: Record<string, string> = {};
290
307
  const routeAncestry: Record<string, string[]> = {};
@@ -298,7 +315,7 @@ export function generateManifestFull<TEnv>(
298
315
  const searchSchemasMap = new Map<string, Record<string, string>>();
299
316
  const trackedIncludes: TrackedInclude[] = [];
300
317
 
301
- RSCRouterContext.run(
318
+ RangoContext.run(
302
319
  {
303
320
  manifest,
304
321
  patterns: patternsMap,
@@ -310,6 +327,8 @@ export function generateManifestFull<TEnv>(
310
327
  counters: {},
311
328
  mountIndex,
312
329
  trackedIncludes, // Enable include tracking
330
+ // basename sets the initial URL prefix for all path() registrations
331
+ ...(options?.urlPrefix ? { urlPrefix: options.urlPrefix } : {}),
313
332
  },
314
333
  () => {
315
334
  const helpers = createRouteHelpers();
@@ -320,6 +339,22 @@ export function generateManifestFull<TEnv>(
320
339
  },
321
340
  );
322
341
 
342
+ // Surface the "use client" components registered as error/notFound fallbacks
343
+ // (route-tree errorBoundary()/notFoundBoundary() helpers, stored on EntryData).
344
+ // The boundary may be a handler function and/or wrap the client boundary in
345
+ // server providers, so walk the whole tree (see collectFallbackClientRefs).
346
+ if (options?.collectClientFallbackRef) {
347
+ const report = options.collectClientFallbackRef;
348
+ const collect = (boundary: unknown[] | undefined) => {
349
+ for (const item of boundary ?? [])
350
+ collectFallbackClientRefs(item, report);
351
+ };
352
+ for (const entry of manifest.values()) {
353
+ collect(entry.errorBoundary);
354
+ collect(entry.notFoundBoundary);
355
+ }
356
+ }
357
+
323
358
  // Collect root-level routes and trailing slash config
324
359
  const routeTrailingSlash: Record<string, string> = {};
325
360
  for (const [name, pattern] of patternsMap.entries()) {
@@ -347,7 +382,7 @@ export function generateManifestFull<TEnv>(
347
382
  if (entry.prerenderDef) {
348
383
  prerenderDefs[name] = entry.prerenderDef;
349
384
  }
350
- if (entry.prerenderDef?.options?.passthrough === true) {
385
+ if (entry.isPassthrough === true) {
351
386
  passthroughRoutes.push(name);
352
387
  }
353
388
  }
@@ -356,12 +391,10 @@ export function generateManifestFull<TEnv>(
356
391
  }
357
392
  }
358
393
 
359
- // Build prefix tree from tracked includes (shared visited set for cycle detection).
360
- // Multiple includes can share the same fullPrefix (e.g., include("/", patternsA),
361
- // include("/", patternsB)). Merge their routes instead of overwriting.
394
+ // Shared visited set for cycle detection across all root-level includes.
362
395
  const visited = new Set<unknown>();
363
- for (const include of trackedIncludes) {
364
- const node = buildPrefixTreeNode(
396
+ mergeIncludeNodes(prefixTree, trackedIncludes, (include) =>
397
+ buildPrefixTreeNode(
365
398
  include.fullPrefix,
366
399
  include.namePrefix,
367
400
  include.patterns as UrlPatterns<any>,
@@ -375,16 +408,8 @@ export function generateManifestFull<TEnv>(
375
408
  passthroughRoutes,
376
409
  responseTypeRoutes,
377
410
  routeSearchSchemas,
378
- );
379
-
380
- const existing = prefixTree[include.fullPrefix];
381
- if (existing) {
382
- existing.routes.push(...node.routes);
383
- Object.assign(existing.children, node.children);
384
- } else {
385
- prefixTree[include.fullPrefix] = node;
386
- }
387
- }
411
+ ),
412
+ );
388
413
 
389
414
  return {
390
415
  prefixTree,
@@ -24,7 +24,9 @@ export {
24
24
  extractIncludesWithDiagnostics,
25
25
  } from "./route-types/include-resolution.js";
26
26
  export {
27
- extractUrlsVariableFromRouter,
27
+ extractUrlsFromRouter,
28
+ extractBasenameFromRouter,
29
+ type UrlsExtractionResult,
28
30
  buildCombinedRouteMapForRouterFile,
29
31
  detectUnresolvableIncludes,
30
32
  detectUnresolvableIncludesForUrlsFile,
@@ -32,5 +34,7 @@ export {
32
34
  formatNestedRouterConflictError,
33
35
  findRouterFiles,
34
36
  writeCombinedRouteTypes,
37
+ genFileTsPath,
38
+ resolveSearchSchemas,
35
39
  } from "./route-types/router-processing.js";
36
40
  export { findUrlsVariableNames } from "./route-types/per-module-writer.js";
@@ -22,8 +22,14 @@ export {
22
22
  type GeneratedManifest,
23
23
  } from "./generate-manifest.js";
24
24
 
25
- export { buildRouteTrie, type TrieNode, type TrieLeaf } from "./route-trie.js";
26
-
25
+ // buildRouteTrie / buildPerRouterTrie / collectFallbackClientRefs and the
26
+ // TrieNode/TrieLeaf types are NOT exported here: they are build-pipeline
27
+ // internals, not public API. Their only build-time consumer (the Vite
28
+ // discovery pass) imports them directly from source via a relative path
29
+ // (vite/discovery/discover-routers.ts), and the runtime RSC realm likewise
30
+ // imports route-trie.js directly (rsc/manifest-init.ts). Keeping them off the
31
+ // public ./build surface (#569 decision 6) means consumers can't mistake them
32
+ // for intended API. generateManifest* / route-types / hashParams stay public.
27
33
  export {
28
34
  writePerModuleRouteTypes,
29
35
  extractRoutesFromSource,