@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
@@ -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,8 +31,10 @@ export type HandleData = Record<string, Record<string, unknown[]>>;
32
31
  export interface RscMetadata {
33
32
  pathname: string;
34
33
  segments: ResolvedSegment[];
35
- /** Router instance ID. When this changes between navigations, the client
36
- * forces a full tree replacement (app switch via host router). */
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. */
37
38
  routerId?: string;
38
39
  isPartial?: boolean;
39
40
  isError?: boolean;
@@ -69,6 +70,22 @@ export interface RscMetadata {
69
70
  * Sent on initial render so the browser can configure its cache duration.
70
71
  */
71
72
  prefetchCacheTTL?: number;
73
+ /**
74
+ * Max entries in the client-side in-memory prefetch cache (FIFO eviction).
75
+ * Sent on initial render so the browser can configure its cache capacity.
76
+ */
77
+ prefetchCacheSize?: number;
78
+ /**
79
+ * Max concurrent speculative prefetch requests on the client.
80
+ * Sent on initial render so the browser can configure its prefetch queue.
81
+ */
82
+ prefetchConcurrency?: number;
83
+ /**
84
+ * Server-resolved rango state cookie name (`{prefix}_{routerId}`). The client
85
+ * reads it verbatim and binds the rango state cookie to it; composition
86
+ * happens only server-side.
87
+ */
88
+ stateCookieName?: string;
72
89
  /**
73
90
  * Theme configuration from router.
74
91
  * Included when theme is enabled in router config.
@@ -83,8 +100,18 @@ export interface RscMetadata {
83
100
  basename?: string;
84
101
  /** Whether connection warmup is enabled */
85
102
  warmupEnabled?: boolean;
86
- /** Server-side redirect with optional state (for partial requests) */
87
- redirect?: { url: string };
103
+ /**
104
+ * Whether the client should hydrate inside React.StrictMode. Carried on the
105
+ * initial full-render payload only; the browser entry reads it once at
106
+ * hydration. Defaults to true on the client when omitted.
107
+ */
108
+ strictMode?: boolean;
109
+ /**
110
+ * Server-side redirect with optional state (for partial requests).
111
+ * `external: true` (from redirect(url, { external: true })) tells the client
112
+ * to hard-navigate to an off-host target instead of validating same-origin.
113
+ */
114
+ redirect?: { url: string; external?: boolean };
88
115
  /** Server-set location state to include in history.pushState */
89
116
  locationState?: Record<string, unknown>;
90
117
  }
@@ -185,6 +212,15 @@ export interface TrackedActionState {
185
212
  result: unknown | null;
186
213
  }
187
214
 
215
+ /**
216
+ * The value returned by {@link useAction} when called without a selector.
217
+ *
218
+ * This is the stable, public name for the action-state shape; consumers can
219
+ * name it in their own signatures (e.g. a wrapper hook). It aliases the
220
+ * internal {@link TrackedActionState}.
221
+ */
222
+ export type ActionState = TrackedActionState;
223
+
188
224
  /**
189
225
  * Listener for action state changes
190
226
  *
@@ -322,8 +358,14 @@ export interface RouterInstance {
322
358
  replace(url: string, options?: RouterNavigateOptions): Promise<void>;
323
359
  /** Refresh the current route (re-fetch server data, preserve client state) */
324
360
  refresh(): Promise<void>;
325
- /** Prefetch a URL for faster client-side transition */
326
- prefetch(url: string): void;
361
+ /**
362
+ * Prefetch a URL for faster client-side transition.
363
+ *
364
+ * Pass `{ key: ":source" }` to source-scope the prefetch cache entry (parity
365
+ * with `<Link prefetchKey=":source">`) when the target's response can differ
366
+ * by source page.
367
+ */
368
+ prefetch(url: string, options?: { key?: ":source" }): void;
327
369
  /** Go back in browser history */
328
370
  back(): void;
329
371
  /** Go forward in browser history */
@@ -415,6 +457,10 @@ export interface NavigationStore {
415
457
  // History-based segment cache (for back/forward navigation and partial merging)
416
458
  getHistoryKey(): string;
417
459
  setHistoryKey(key: string): void;
460
+ /** Monotonic token of the most recently committed navigation. */
461
+ getNavInstance(): number;
462
+ /** Nav-instance token recorded on a cache entry (undefined if absent). */
463
+ getCacheEntryInstance(historyKey: string): number | undefined;
418
464
  cacheSegmentsForHistory(
419
465
  historyKey: string,
420
466
  segments: ResolvedSegment[],
@@ -426,20 +472,44 @@ export interface NavigationStore {
426
472
  stale: boolean;
427
473
  handleData?: HandleData;
428
474
  routerId?: string;
475
+ /**
476
+ * True when the entry's handle data is incomplete (a deferred Meta was
477
+ * still pending at navigate-away). A popstate return must revalidate with
478
+ * a FULL re-render so the server re-streams handles.
479
+ */
480
+ handlesPending?: boolean;
429
481
  }
430
482
  | undefined;
431
483
  hasHistoryCache(historyKey: string): boolean;
432
- updateCacheHandleData(historyKey: string, handleData: HandleData): void;
484
+ /**
485
+ * Update only the handleData (and optionally the stale / handlesPending flags)
486
+ * of an existing cache entry. When a flag is omitted the entry's current value
487
+ * is preserved. `stale=true` marks a single entry stale so a popstate return
488
+ * revalidates it; `handlesPending=true` additionally forces that revalidation
489
+ * to be a full re-render (so a deferred Meta re-streams).
490
+ */
491
+ updateCacheHandleData(
492
+ historyKey: string,
493
+ handleData: HandleData,
494
+ stale?: boolean,
495
+ handlesPending?: boolean,
496
+ ): void;
497
+ /**
498
+ * Owner-guarded variant of updateCacheHandleData: writes only when the entry
499
+ * is still owned by `ownerInstance`. Folds the caller's separate ownership
500
+ * probe and write into one historyCache scan for the per-yield streaming path.
501
+ */
502
+ updateCacheHandleDataIfOwned(
503
+ historyKey: string,
504
+ handleData: HandleData,
505
+ ownerInstance: number,
506
+ stale?: boolean,
507
+ handlesPending?: boolean,
508
+ ): void;
433
509
  markCacheAsStale(): void;
510
+ markHistoryCacheStale(): void;
434
511
  markCacheAsStaleAndBroadcast(): void;
435
512
  clearHistoryCache(): void;
436
- /**
437
- * Clear this tab's nav + prefetch caches without broadcasting or rotating
438
- * shared state. Intended for app-switch transitions that affect only this
439
- * tab's session.
440
- */
441
- clearHistoryCacheLocal(): void;
442
- broadcastCacheInvalidation(): void;
443
513
 
444
514
  // Cross-tab refresh callback (set by navigation bridge)
445
515
  setCrossTabRefreshCallback(callback: () => void): void;
@@ -495,6 +565,15 @@ export interface FetchPartialResult {
495
565
  payload: RscPayload;
496
566
  /** Promise that resolves when the response stream is fully consumed */
497
567
  streamComplete: Promise<void>;
568
+ /**
569
+ * True only when this payload came from a prefetch-cache hit whose stream had
570
+ * ALREADY fully drained at fetch time (the route was fully prefetched). The
571
+ * commit then runs in a startTransition so loading()/Suspense content — already
572
+ * resolved — swaps in directly without flashing a fallback. A partially-warmed
573
+ * (still-streaming) prefetch hit and a cold fetch leave this false so their
574
+ * fallbacks stream as usual.
575
+ */
576
+ fullyPrefetched?: boolean;
498
577
  }
499
578
 
500
579
  /**
@@ -556,13 +635,6 @@ export interface NavigationBridge {
556
635
  getVersion(): string | undefined;
557
636
  /** Update the RSC version (e.g. after HMR). Clears prefetch cache. */
558
637
  updateVersion(newVersion: string): void;
559
- /**
560
- * Replace the active app-shell snapshot (rootLayout, basename, version)
561
- * atomically. Used on cross-app navigations when the response's routerId
562
- * indicates the user entered a different app. Theme, warmup, and prefetch
563
- * TTL are document-lifetime and not part of the shell.
564
- */
565
- updateAppShell(next: import("./app-shell.js").AppShell): void;
566
638
  }
567
639
 
568
640
  /**
@@ -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
  }
@@ -17,6 +17,11 @@ import type { TrailingSlashMode } from "../types.js";
17
17
  import { createRouteHelpers } from "../route-definition.js";
18
18
  import MapRootLayout from "../server/root-layout.js";
19
19
  import { collectFallbackClientRefs } from "./collect-fallback-refs.js";
20
+ import {
21
+ isIncludeProvider,
22
+ resolveIncludeModule,
23
+ type IncludeProvider,
24
+ } from "../urls/include-provider.js";
20
25
 
21
26
  /**
22
27
  * Node in the prefix tree
@@ -61,13 +66,32 @@ export interface GeneratedManifest {
61
66
  // Merge tracked nested includes into `target`. Multiple includes can share a
62
67
  // fullPrefix (e.g. include("/", a), include("/", b)) — concat their routes and
63
68
  // Object.assign children rather than overwrite.
64
- function mergeIncludeNodes(
69
+ async function mergeIncludeNodes(
65
70
  target: Record<string, PrefixTreeNode>,
66
71
  includes: TrackedInclude[],
67
- buildChild: (include: TrackedInclude) => PrefixTreeNode,
68
- ): void {
72
+ buildChild: (include: TrackedInclude) => Promise<PrefixTreeNode>,
73
+ ): Promise<void> {
69
74
  for (const include of includes) {
70
- const node = buildChild(include);
75
+ let node: PrefixTreeNode;
76
+ try {
77
+ node = await buildChild(include);
78
+ } catch (err) {
79
+ // Discovery (build-time, and the dev trie-rebuild) populates the
80
+ // manifest / trie / generated types for the WHOLE app. A failing async
81
+ // include provider here — a broken import, a module that throws at eval —
82
+ // must HARD-FAIL, not be swallowed: swallowing produces a green build with
83
+ // the entire route group silently absent from the manifest/trie/types, so
84
+ // CI passes, the deploy ships, and every one of that group's URLs then
85
+ // 404s/500s in production. On main an eager include that threw failed the
86
+ // build loudly; the async form must keep that contract. Rethrow with the
87
+ // offending prefix so the failure is actionable. (Sibling isolation
88
+ // belongs at PER-REQUEST runtime — see find-match.ts — not at discovery.)
89
+ throw new Error(
90
+ `[@rangojs/router] Failed to resolve include at prefix "${include.fullPrefix}" ` +
91
+ `during route discovery: ${(err as Error)?.message ?? String(err)}`,
92
+ { cause: err },
93
+ );
94
+ }
71
95
  const existing = target[include.fullPrefix];
72
96
  if (existing) {
73
97
  existing.routes.push(...node.routes);
@@ -78,10 +102,10 @@ function mergeIncludeNodes(
78
102
  }
79
103
  }
80
104
 
81
- function buildPrefixTreeNode(
105
+ async function buildPrefixTreeNode(
82
106
  urlPrefix: string,
83
107
  namePrefix: string | undefined,
84
- patterns: UrlPatterns<any>,
108
+ patternsOrProvider: UrlPatterns<any> | IncludeProvider<any>,
85
109
  routeManifest: Record<string, string>,
86
110
  routeAncestry: Record<string, string[]>, // internal: feeds trie building, not exported
87
111
  mountIndex: number,
@@ -92,7 +116,14 @@ function buildPrefixTreeNode(
92
116
  passthroughRoutes?: string[],
93
117
  responseTypeRoutes?: Record<string, string>,
94
118
  routeSearchSchemas?: Record<string, Record<string, string>>,
95
- ): PrefixTreeNode {
119
+ ): Promise<PrefixTreeNode> {
120
+ // Resolve an async include provider (`() => import("./routes")`) so its routes
121
+ // are walked into the build-time manifest/types/href. Runtime matching still
122
+ // defers the import via lazy-includes; this only runs during build/dev
123
+ // discovery, which is async.
124
+ const patterns: UrlPatterns<any> = isIncludeProvider(patternsOrProvider)
125
+ ? resolveIncludeModule(await patternsOrProvider(), urlPrefix)
126
+ : patternsOrProvider;
96
127
  if (visited.has(patterns)) {
97
128
  console.warn(
98
129
  `[@rangojs/router] Circular include detected at prefix "${urlPrefix}". Skipping.`,
@@ -106,118 +137,117 @@ function buildPrefixTreeNode(
106
137
  };
107
138
  }
108
139
  visited.add(patterns);
109
- // Create context for running patterns with include tracking
110
- const manifest = new Map<string, EntryData>();
111
- const patternsMap = new Map<string, string>();
112
- const patternsByPrefix = new Map<string, Map<string, string>>();
113
- const trailingSlashMap = new Map<string, TrailingSlashMode>();
114
- const searchSchemasMap = new Map<string, Record<string, string>>();
115
- const trackedIncludes: TrackedInclude[] = [];
140
+ try {
141
+ // Create context for running patterns with include tracking
142
+ const manifest = new Map<string, EntryData>();
143
+ const patternsMap = new Map<string, string>();
144
+ const patternsByPrefix = new Map<string, Map<string, string>>();
145
+ const trailingSlashMap = new Map<string, TrailingSlashMode>();
146
+ const searchSchemasMap = new Map<string, Record<string, string>>();
147
+ const trackedIncludes: TrackedInclude[] = [];
116
148
 
117
- RangoContext.run(
118
- {
119
- manifest,
120
- patterns: patternsMap,
121
- patternsByPrefix,
122
- trailingSlash: trailingSlashMap,
123
- searchSchemas: searchSchemasMap,
124
- namespace: "build",
125
- parent: null,
126
- counters: {},
127
- mountIndex,
128
- trackedIncludes, // Enable nested include tracking
129
- },
130
- () => {
131
- const helpers = createRouteHelpers();
132
- // Wrap in root layout for correct parent hierarchy (matches runtime)
133
- helpers.layout(MapRootLayout, () => {
134
- if (urlPrefix || namePrefix) {
135
- return runWithPrefixes(urlPrefix, namePrefix, () => {
136
- return patterns.handler() as AllUseItems[];
137
- });
138
- }
139
- return patterns.handler() as AllUseItems[];
140
- });
141
- },
142
- );
149
+ RangoContext.run(
150
+ {
151
+ manifest,
152
+ patterns: patternsMap,
153
+ patternsByPrefix,
154
+ trailingSlash: trailingSlashMap,
155
+ searchSchemas: searchSchemasMap,
156
+ namespace: "build",
157
+ parent: null,
158
+ counters: {},
159
+ mountIndex,
160
+ trackedIncludes, // Enable nested include tracking
161
+ },
162
+ () => {
163
+ const helpers = createRouteHelpers();
164
+ // Wrap in root layout for correct parent hierarchy (matches runtime)
165
+ helpers.layout(MapRootLayout, () => {
166
+ if (urlPrefix || namePrefix) {
167
+ return runWithPrefixes(urlPrefix, namePrefix, () => {
168
+ return patterns.handler() as AllUseItems[];
169
+ });
170
+ }
171
+ return patterns.handler() as AllUseItems[];
172
+ });
173
+ },
174
+ );
143
175
 
144
- // Collect route names defined in this include (routes have prefixes applied)
145
- const routes: string[] = [];
146
- for (const [name, pattern] of patternsMap.entries()) {
147
- routes.push(name);
148
- routeManifest[name] = pattern;
149
- }
176
+ // Collect route names defined in this include (routes have prefixes applied)
177
+ const routes = [...patternsMap.keys()];
178
+ Object.assign(routeManifest, Object.fromEntries(patternsMap));
150
179
 
151
- // Collect trailing slash config
152
- if (routeTrailingSlash) {
153
- for (const [name, mode] of trailingSlashMap.entries()) {
154
- routeTrailingSlash[name] = mode;
180
+ // Collect trailing slash config
181
+ if (routeTrailingSlash) {
182
+ for (const [name, mode] of trailingSlashMap.entries()) {
183
+ routeTrailingSlash[name] = mode;
184
+ }
155
185
  }
156
- }
157
- if (routeSearchSchemas) {
158
- for (const [name, schema] of searchSchemasMap.entries()) {
159
- routeSearchSchemas[name] = schema;
186
+ if (routeSearchSchemas) {
187
+ for (const [name, schema] of searchSchemasMap.entries()) {
188
+ routeSearchSchemas[name] = schema;
189
+ }
160
190
  }
161
- }
162
191
 
163
- // Capture ancestry from manifest entries' parent chains
164
- captureAncestry(manifest, routeAncestry);
192
+ // Capture ancestry from manifest entries' parent chains
193
+ captureAncestry(manifest, routeAncestry);
165
194
 
166
- // Collect prerender route names and handler definitions from manifest entries
167
- if (prerenderRoutes) {
168
- for (const [name, entry] of manifest) {
169
- if (entry.type === "route" && entry.isPrerender) {
170
- prerenderRoutes.push(name);
171
- if (prerenderDefs && entry.prerenderDef) {
172
- prerenderDefs[name] = entry.prerenderDef;
173
- }
174
- if (passthroughRoutes && entry.isPassthrough === true) {
175
- passthroughRoutes.push(name);
195
+ // Collect prerender route names and handler definitions from manifest entries
196
+ if (prerenderRoutes) {
197
+ for (const [name, entry] of manifest) {
198
+ if (entry.type === "route" && entry.isPrerender) {
199
+ prerenderRoutes.push(name);
200
+ if (prerenderDefs && entry.prerenderDef) {
201
+ prerenderDefs[name] = entry.prerenderDef;
202
+ }
203
+ if (passthroughRoutes && entry.isPassthrough === true) {
204
+ passthroughRoutes.push(name);
205
+ }
176
206
  }
177
207
  }
178
208
  }
179
- }
180
209
 
181
- // Collect response type routes from manifest entries
182
- if (responseTypeRoutes) {
183
- for (const [name, entry] of manifest) {
184
- if (entry.type === "route" && entry.responseType) {
185
- responseTypeRoutes[name] = entry.responseType;
210
+ // Collect response type routes from manifest entries
211
+ if (responseTypeRoutes) {
212
+ for (const [name, entry] of manifest) {
213
+ if (entry.type === "route" && entry.responseType) {
214
+ responseTypeRoutes[name] = entry.responseType;
215
+ }
186
216
  }
187
217
  }
188
- }
189
-
190
- const children: Record<string, PrefixTreeNode> = {};
191
- mergeIncludeNodes(children, trackedIncludes, (include) =>
192
- buildPrefixTreeNode(
193
- include.fullPrefix,
194
- include.namePrefix,
195
- include.patterns as UrlPatterns<any>,
196
- routeManifest,
197
- routeAncestry,
198
- mountIndex,
199
- visited,
200
- routeTrailingSlash,
201
- prerenderRoutes,
202
- prerenderDefs,
203
- passthroughRoutes,
204
- responseTypeRoutes,
205
- routeSearchSchemas,
206
- ),
207
- );
208
218
 
209
- // Remove from visited so sibling branches can reuse the same patterns
210
- // without false circular-include detection. Only ancestors in the current
211
- // recursion path should trigger the cycle guard.
212
- visited.delete(patterns);
219
+ const children: Record<string, PrefixTreeNode> = {};
220
+ await mergeIncludeNodes(children, trackedIncludes, (include) =>
221
+ buildPrefixTreeNode(
222
+ include.fullPrefix,
223
+ include.namePrefix,
224
+ include.patterns as UrlPatterns<any> | IncludeProvider<any>,
225
+ routeManifest,
226
+ routeAncestry,
227
+ mountIndex,
228
+ visited,
229
+ routeTrailingSlash,
230
+ prerenderRoutes,
231
+ prerenderDefs,
232
+ passthroughRoutes,
233
+ responseTypeRoutes,
234
+ routeSearchSchemas,
235
+ ),
236
+ );
213
237
 
214
- return {
215
- staticPrefix: extractStaticPrefix(urlPrefix),
216
- fullPrefix: urlPrefix,
217
- namePrefix: namePrefix || undefined,
218
- children,
219
- routes,
220
- };
238
+ return {
239
+ staticPrefix: extractStaticPrefix(urlPrefix),
240
+ fullPrefix: urlPrefix,
241
+ namePrefix: namePrefix || undefined,
242
+ children,
243
+ routes,
244
+ };
245
+ } finally {
246
+ // Remove from visited so sibling branches can reuse the same patterns without
247
+ // false circular-include detection — and so a throwing handler (caught by the
248
+ // parent mergeIncludeNodes) does not leak this entry into the shared set.
249
+ visited.delete(patterns);
250
+ }
221
251
  }
222
252
 
223
253
  /**
@@ -261,7 +291,8 @@ export interface FullManifest extends GeneratedManifest {
261
291
  * import { generateManifest } from "@rangojs/router/build";
262
292
  * import { urlpatterns } from "./urls";
263
293
  *
264
- * const manifest = generateManifest(urlpatterns);
294
+ * // Async: awaits async include() providers (`() => import("./routes")`).
295
+ * const manifest = await generateManifest(urlpatterns);
265
296
  * // Write to file for runtime use
266
297
  * fs.writeFileSync(
267
298
  * "src/generated/route-manifest.json",
@@ -269,15 +300,12 @@ export interface FullManifest extends GeneratedManifest {
269
300
  * );
270
301
  * ```
271
302
  */
272
- export function generateManifest<TEnv>(
303
+ export async function generateManifest<TEnv>(
273
304
  urlpatterns: UrlPatterns<TEnv, any>,
274
305
  mountIndex: number = 0,
275
- ): GeneratedManifest {
276
- const {
277
- _routeAncestry: _,
278
- _prerenderDefs: __,
279
- ...publicManifest
280
- } = generateManifestFull(urlpatterns, mountIndex);
306
+ ): Promise<GeneratedManifest> {
307
+ const { _routeAncestry, _prerenderDefs, ...publicManifest } =
308
+ await generateManifestFull(urlpatterns, mountIndex);
281
309
  return publicManifest;
282
310
  }
283
311
 
@@ -288,7 +316,7 @@ export function generateManifest<TEnv>(
288
316
  * @rangojs/router/build), manifest-init (direct import), and trie
289
317
  * building. Not intended for external use.
290
318
  */
291
- export function generateManifestFull<TEnv>(
319
+ export async function generateManifestFull<TEnv>(
292
320
  urlpatterns: UrlPatterns<TEnv, any>,
293
321
  mountIndex: number = 0,
294
322
  options?: {
@@ -302,7 +330,7 @@ export function generateManifestFull<TEnv>(
302
330
  */
303
331
  collectClientFallbackRef?: (refKey: string) => void;
304
332
  },
305
- ): FullManifest {
333
+ ): Promise<FullManifest> {
306
334
  const routeManifest: Record<string, string> = {};
307
335
  const routeAncestry: Record<string, string[]> = {};
308
336
  const prefixTree: Record<string, PrefixTreeNode> = {};
@@ -356,17 +384,13 @@ export function generateManifestFull<TEnv>(
356
384
  }
357
385
 
358
386
  // Collect root-level routes and trailing slash config
359
- const routeTrailingSlash: Record<string, string> = {};
360
- for (const [name, pattern] of patternsMap.entries()) {
361
- routeManifest[name] = pattern;
362
- }
363
- for (const [name, mode] of trailingSlashMap.entries()) {
364
- routeTrailingSlash[name] = mode;
365
- }
366
- const routeSearchSchemas: Record<string, Record<string, string>> = {};
367
- for (const [name, schema] of searchSchemasMap.entries()) {
368
- routeSearchSchemas[name] = schema;
369
- }
387
+ Object.assign(routeManifest, Object.fromEntries(patternsMap));
388
+ const routeTrailingSlash: Record<string, string> =
389
+ Object.fromEntries(trailingSlashMap);
390
+ const routeSearchSchemas: Record<
391
+ string,
392
+ Record<string, string>
393
+ > = Object.fromEntries(searchSchemasMap);
370
394
 
371
395
  // Capture ancestry from manifest entries' parent chains
372
396
  captureAncestry(manifest, routeAncestry);
@@ -393,11 +417,11 @@ export function generateManifestFull<TEnv>(
393
417
 
394
418
  // Shared visited set for cycle detection across all root-level includes.
395
419
  const visited = new Set<unknown>();
396
- mergeIncludeNodes(prefixTree, trackedIncludes, (include) =>
420
+ await mergeIncludeNodes(prefixTree, trackedIncludes, (include) =>
397
421
  buildPrefixTreeNode(
398
422
  include.fullPrefix,
399
423
  include.namePrefix,
400
- include.patterns as UrlPatterns<any>,
424
+ include.patterns as UrlPatterns<any> | IncludeProvider<any>,
401
425
  routeManifest,
402
426
  routeAncestry,
403
427
  mountIndex,
@@ -441,14 +465,14 @@ export function generateManifestFull<TEnv>(
441
465
  *
442
466
  * @example
443
467
  * ```typescript
444
- * const code = generateManifestCode(urlpatterns);
468
+ * const code = await generateManifestCode(urlpatterns);
445
469
  * fs.writeFileSync("src/generated/route-manifest.ts", code);
446
470
  * ```
447
471
  */
448
- export function generateManifestCode<TEnv>(
472
+ export async function generateManifestCode<TEnv>(
449
473
  urlpatterns: UrlPatterns<TEnv, any>,
450
- ): string {
451
- const manifest = generateManifest(urlpatterns);
474
+ ): Promise<string> {
475
+ const manifest = await generateManifest(urlpatterns);
452
476
 
453
477
  return `/**
454
478
  * Auto-generated route manifest
@@ -24,7 +24,6 @@ export {
24
24
  extractIncludesWithDiagnostics,
25
25
  } from "./route-types/include-resolution.js";
26
26
  export {
27
- extractUrlsVariableFromRouter,
28
27
  extractUrlsFromRouter,
29
28
  extractBasenameFromRouter,
30
29
  type UrlsExtractionResult,
@@ -34,6 +33,7 @@ export {
34
33
  findNestedRouterConflict,
35
34
  formatNestedRouterConflictError,
36
35
  findRouterFiles,
36
+ findHostRouterFiles,
37
37
  writeCombinedRouteTypes,
38
38
  genFileTsPath,
39
39
  resolveSearchSchemas,