@rangojs/router 0.0.0-experimental.79 → 0.0.0-experimental.7c7e4327

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 (440) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +301 -797
  3. package/dist/bin/rango.js +603 -145
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3750 -1160
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +96 -24
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +85 -6
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +228 -33
  12. package/skills/caching/SKILL.md +336 -19
  13. package/skills/catalog.json +271 -0
  14. package/skills/comparison/SKILL.md +50 -0
  15. package/skills/comparison/agents/openai.yaml +4 -0
  16. package/skills/comparison/references/framework-comparison.md +837 -0
  17. package/skills/composability/SKILL.md +110 -4
  18. package/skills/css/SKILL.md +76 -0
  19. package/skills/debug-manifest/SKILL.md +5 -3
  20. package/skills/defer-hydration/SKILL.md +235 -0
  21. package/skills/document-cache/SKILL.md +87 -56
  22. package/skills/fonts/SKILL.md +1 -1
  23. package/skills/handler-use/SKILL.md +12 -10
  24. package/skills/hooks/SKILL.md +73 -691
  25. package/skills/hooks/data.md +273 -0
  26. package/skills/hooks/handle-and-actions.md +103 -0
  27. package/skills/hooks/navigation.md +110 -0
  28. package/skills/hooks/outlets.md +41 -0
  29. package/skills/hooks/state.md +228 -0
  30. package/skills/hooks/urls.md +135 -0
  31. package/skills/host-router/SKILL.md +129 -27
  32. package/skills/i18n/SKILL.md +276 -0
  33. package/skills/intercept/SKILL.md +75 -19
  34. package/skills/layout/SKILL.md +40 -19
  35. package/skills/links/SKILL.md +247 -17
  36. package/skills/loader/SKILL.md +248 -10
  37. package/skills/middleware/SKILL.md +25 -13
  38. package/skills/migrate-nextjs/SKILL.md +205 -20
  39. package/skills/migrate-react-router/SKILL.md +59 -670
  40. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  41. package/skills/migrate-react-router/component-migration.md +196 -0
  42. package/skills/migrate-react-router/data-and-actions.md +225 -0
  43. package/skills/migrate-react-router/route-mapping.md +271 -0
  44. package/skills/mime-routes/SKILL.md +29 -2
  45. package/skills/observability/SKILL.md +202 -0
  46. package/skills/parallel/SKILL.md +40 -10
  47. package/skills/ppr/SKILL.md +616 -0
  48. package/skills/prerender/SKILL.md +72 -60
  49. package/skills/rango/SKILL.md +318 -26
  50. package/skills/react-compiler/SKILL.md +168 -0
  51. package/skills/response-routes/SKILL.md +138 -49
  52. package/skills/route/SKILL.md +117 -9
  53. package/skills/router-setup/SKILL.md +44 -9
  54. package/skills/scripts/SKILL.md +179 -0
  55. package/skills/server-actions/SKILL.md +776 -0
  56. package/skills/shell-manifest/SKILL.md +185 -0
  57. package/skills/streams-and-websockets/SKILL.md +283 -0
  58. package/skills/tailwind/SKILL.md +28 -4
  59. package/skills/testing/SKILL.md +130 -0
  60. package/skills/testing/bindings.md +103 -0
  61. package/skills/testing/cache-prerender.md +127 -0
  62. package/skills/testing/client-components.md +124 -0
  63. package/skills/testing/e2e-parity.md +125 -0
  64. package/skills/testing/flight.md +91 -0
  65. package/skills/testing/handles.md +131 -0
  66. package/skills/testing/loader.md +128 -0
  67. package/skills/testing/middleware.md +99 -0
  68. package/skills/testing/render-handler.md +122 -0
  69. package/skills/testing/response-routes.md +95 -0
  70. package/skills/testing/reverse-and-types.md +85 -0
  71. package/skills/testing/server-actions.md +107 -0
  72. package/skills/testing/server-tree.md +128 -0
  73. package/skills/testing/setup.md +123 -0
  74. package/skills/theme/SKILL.md +1 -1
  75. package/skills/typesafety/SKILL.md +45 -626
  76. package/skills/typesafety/env-and-bindings.md +254 -0
  77. package/skills/typesafety/generated-files-and-cli.md +335 -0
  78. package/skills/typesafety/params-and-search.md +153 -0
  79. package/skills/typesafety/route-types.md +209 -0
  80. package/skills/use-cache/SKILL.md +74 -15
  81. package/skills/vercel/SKILL.md +128 -0
  82. package/skills/view-transitions/SKILL.md +337 -0
  83. package/src/__augment-tests__/augment.ts +81 -0
  84. package/src/__augment-tests__/augmented.check.ts +116 -0
  85. package/src/__internal.ts +0 -65
  86. package/src/browser/action-coordinator.ts +53 -36
  87. package/src/browser/action-fence.ts +47 -0
  88. package/src/browser/app-shell.ts +39 -0
  89. package/src/browser/connection-warmup.ts +134 -0
  90. package/src/browser/cookie-name.ts +140 -0
  91. package/src/browser/event-controller.ts +252 -158
  92. package/src/browser/history-state.ts +21 -0
  93. package/src/browser/index.ts +3 -3
  94. package/src/browser/invalidate-client-cache.ts +52 -0
  95. package/src/browser/logging.ts +28 -0
  96. package/src/browser/merge-segment-loaders.ts +6 -4
  97. package/src/browser/navigation-bridge.ts +94 -25
  98. package/src/browser/navigation-client.ts +144 -79
  99. package/src/browser/navigation-store-handle.ts +38 -0
  100. package/src/browser/navigation-store.ts +161 -73
  101. package/src/browser/navigation-transaction.ts +9 -59
  102. package/src/browser/network-error-handler.ts +34 -7
  103. package/src/browser/partial-update.ts +183 -144
  104. package/src/browser/prefetch/cache.ts +242 -77
  105. package/src/browser/prefetch/fetch.ts +325 -69
  106. package/src/browser/prefetch/queue.ts +61 -12
  107. package/src/browser/rango-state.ts +158 -76
  108. package/src/browser/react/Link.tsx +58 -20
  109. package/src/browser/react/NavigationProvider.tsx +202 -120
  110. package/src/browser/react/ScrollRestoration.tsx +10 -6
  111. package/src/browser/react/filter-segment-order.ts +66 -7
  112. package/src/browser/react/index.ts +0 -48
  113. package/src/browser/react/location-state-shared.ts +178 -8
  114. package/src/browser/react/location-state.ts +39 -14
  115. package/src/browser/react/use-action.ts +6 -15
  116. package/src/browser/react/use-handle.ts +17 -14
  117. package/src/browser/react/use-href.tsx +8 -1
  118. package/src/browser/react/use-link-status.ts +33 -8
  119. package/src/browser/react/use-navigation.ts +32 -7
  120. package/src/browser/react/use-params.ts +20 -10
  121. package/src/browser/react/use-reverse.ts +106 -0
  122. package/src/browser/react/use-router.ts +25 -3
  123. package/src/browser/react/use-search-params.ts +0 -5
  124. package/src/browser/react/use-segments.ts +11 -21
  125. package/src/browser/response-adapter.ts +99 -8
  126. package/src/browser/rsc-router.tsx +145 -28
  127. package/src/browser/scroll-restoration.ts +37 -22
  128. package/src/browser/segment-reconciler.ts +31 -21
  129. package/src/browser/segment-structure-assert.ts +2 -2
  130. package/src/browser/server-action-bridge.ts +236 -65
  131. package/src/browser/types.ts +102 -9
  132. package/src/browser/validate-redirect-origin.ts +43 -16
  133. package/src/build/collect-fallback-refs.ts +107 -0
  134. package/src/build/generate-manifest.ts +203 -154
  135. package/src/build/generate-route-types.ts +3 -1
  136. package/src/build/index.ts +11 -3
  137. package/src/build/prefix-tree-utils.ts +123 -0
  138. package/src/build/route-trie.ts +152 -21
  139. package/src/build/route-types/ast-route-extraction.ts +15 -8
  140. package/src/build/route-types/codegen.ts +16 -5
  141. package/src/build/route-types/include-resolution.ts +456 -62
  142. package/src/build/route-types/param-extraction.ts +6 -3
  143. package/src/build/route-types/per-module-writer.ts +22 -6
  144. package/src/build/route-types/router-processing.ts +128 -51
  145. package/src/build/route-types/scan-filter.ts +1 -1
  146. package/src/build/route-types/source-scan.ts +216 -0
  147. package/src/build/runtime-discovery.ts +13 -21
  148. package/src/cache/cache-error.ts +104 -0
  149. package/src/cache/cache-key-utils.ts +58 -13
  150. package/src/cache/cache-policy.ts +108 -34
  151. package/src/cache/cache-runtime.ts +421 -58
  152. package/src/cache/cache-scope.ts +187 -96
  153. package/src/cache/cache-tag.ts +149 -0
  154. package/src/cache/cf/cf-base64.ts +33 -0
  155. package/src/cache/cf/cf-cache-constants.ts +127 -0
  156. package/src/cache/cf/cf-cache-store.ts +2202 -372
  157. package/src/cache/cf/cf-cache-types.ts +349 -0
  158. package/src/cache/cf/cf-kv-utils.ts +46 -0
  159. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  160. package/src/cache/cf/index.ts +6 -16
  161. package/src/cache/document-cache.ts +126 -41
  162. package/src/cache/handle-snapshot.ts +70 -0
  163. package/src/cache/index.ts +23 -20
  164. package/src/cache/memory-segment-store.ts +243 -37
  165. package/src/cache/profile-registry.ts +46 -31
  166. package/src/cache/read-through-swr.ts +56 -12
  167. package/src/cache/segment-codec.ts +13 -21
  168. package/src/cache/shell-snapshot.ts +417 -0
  169. package/src/cache/tag-invalidation.ts +230 -0
  170. package/src/cache/types.ts +180 -99
  171. package/src/cache/vercel/index.ts +11 -0
  172. package/src/cache/vercel/vercel-cache-store.ts +1127 -0
  173. package/src/client.rsc.tsx +41 -21
  174. package/src/client.tsx +33 -61
  175. package/src/cloudflare/index.ts +11 -0
  176. package/src/cloudflare/tracing.ts +108 -0
  177. package/src/component-utils.ts +19 -0
  178. package/src/components/DefaultDocument.tsx +8 -2
  179. package/src/context-var.ts +18 -6
  180. package/src/decode-loader-results.ts +52 -0
  181. package/src/defer.ts +185 -0
  182. package/src/deps/ssr.ts +0 -1
  183. package/src/encode-kv.ts +49 -0
  184. package/src/errors.ts +30 -4
  185. package/src/escape-script.ts +52 -0
  186. package/src/handle.ts +67 -37
  187. package/src/handles/MetaTags.tsx +24 -53
  188. package/src/handles/Scripts.tsx +183 -0
  189. package/src/handles/breadcrumbs.ts +35 -8
  190. package/src/handles/deferred-resolution.ts +127 -0
  191. package/src/handles/is-thenable.ts +18 -0
  192. package/src/handles/meta.ts +14 -40
  193. package/src/handles/script.ts +244 -0
  194. package/src/host/cookie-handler.ts +9 -60
  195. package/src/host/errors.ts +13 -22
  196. package/src/host/index.ts +9 -2
  197. package/src/host/pattern-matcher.ts +23 -52
  198. package/src/host/router.ts +107 -99
  199. package/src/host/testing.ts +40 -27
  200. package/src/host/types.ts +37 -4
  201. package/src/host/utils.ts +1 -1
  202. package/src/href-client.ts +137 -22
  203. package/src/index.rsc.ts +97 -12
  204. package/src/index.ts +98 -14
  205. package/src/internal-debug.ts +11 -10
  206. package/src/loader-store.ts +500 -0
  207. package/src/loader.rsc.ts +20 -13
  208. package/src/loader.ts +12 -11
  209. package/src/missing-id-error.ts +68 -0
  210. package/src/outlet-context.ts +1 -1
  211. package/src/outlet-provider.tsx +1 -5
  212. package/src/prerender/param-hash.ts +16 -16
  213. package/src/prerender/store.ts +32 -37
  214. package/src/prerender.ts +78 -10
  215. package/src/redirect-origin.ts +114 -0
  216. package/src/regex-escape.ts +8 -0
  217. package/src/render-error-thrower.tsx +20 -0
  218. package/src/response-utils.ts +62 -0
  219. package/src/reverse.ts +65 -39
  220. package/src/root-error-boundary.tsx +1 -19
  221. package/src/route-content-wrapper.tsx +19 -77
  222. package/src/route-definition/dsl-helpers.ts +304 -309
  223. package/src/route-definition/helper-factories.ts +28 -140
  224. package/src/route-definition/helpers-types.ts +87 -59
  225. package/src/route-definition/index.ts +1 -2
  226. package/src/route-definition/redirect.ts +44 -11
  227. package/src/route-definition/resolve-handler-use.ts +12 -1
  228. package/src/route-definition/use-item-types.ts +29 -0
  229. package/src/route-map-builder.ts +41 -20
  230. package/src/route-types.ts +19 -46
  231. package/src/router/basename.ts +14 -0
  232. package/src/router/content-negotiation.ts +73 -25
  233. package/src/router/error-handling.ts +45 -18
  234. package/src/router/find-match.ts +129 -30
  235. package/src/router/handler-context.ts +27 -42
  236. package/src/router/instrument.ts +355 -0
  237. package/src/router/intercept-resolution.ts +39 -20
  238. package/src/router/lazy-includes.ts +82 -59
  239. package/src/router/loader-resolution.ts +167 -72
  240. package/src/router/logging.ts +0 -6
  241. package/src/router/manifest.ts +74 -40
  242. package/src/router/match-api.ts +80 -55
  243. package/src/router/match-context.ts +0 -22
  244. package/src/router/match-handlers.ts +211 -165
  245. package/src/router/match-middleware/background-revalidation.ts +40 -24
  246. package/src/router/match-middleware/cache-lookup.ts +159 -285
  247. package/src/router/match-middleware/cache-store.ts +64 -52
  248. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  249. package/src/router/match-middleware/segment-resolution.ts +0 -22
  250. package/src/router/match-pipelines.ts +1 -42
  251. package/src/router/match-result.ts +69 -79
  252. package/src/router/metrics.ts +0 -34
  253. package/src/router/middleware-types.ts +7 -134
  254. package/src/router/middleware.ts +298 -172
  255. package/src/router/navigation-snapshot.ts +7 -56
  256. package/src/router/params-util.ts +23 -0
  257. package/src/router/parse-pattern.ts +115 -0
  258. package/src/router/pattern-matching.ts +181 -150
  259. package/src/router/prefetch-cache-ttl.ts +51 -0
  260. package/src/router/prefetch-limits.ts +37 -0
  261. package/src/router/prerender-match.ts +112 -67
  262. package/src/router/preview-match.ts +6 -2
  263. package/src/router/request-classification.ts +50 -69
  264. package/src/router/revalidation.ts +123 -73
  265. package/src/router/route-snapshot.ts +14 -3
  266. package/src/router/router-context.ts +6 -29
  267. package/src/router/router-interfaces.ts +115 -36
  268. package/src/router/router-options.ts +166 -5
  269. package/src/router/router-registry.ts +2 -5
  270. package/src/router/segment-resolution/fresh.ts +131 -86
  271. package/src/router/segment-resolution/helpers.ts +86 -6
  272. package/src/router/segment-resolution/loader-cache.ts +139 -39
  273. package/src/router/segment-resolution/loader-mask.ts +67 -0
  274. package/src/router/segment-resolution/loader-snapshot.ts +251 -0
  275. package/src/router/segment-resolution/revalidation.ts +272 -320
  276. package/src/router/segment-resolution/static-store.ts +19 -5
  277. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  278. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  279. package/src/router/segment-resolution.ts +5 -1
  280. package/src/router/segment-wrappers.ts +6 -5
  281. package/src/router/state-cookie-name.ts +33 -0
  282. package/src/router/substitute-pattern-params.ts +75 -0
  283. package/src/router/telemetry-otel.ts +160 -200
  284. package/src/router/telemetry.ts +105 -20
  285. package/src/router/timeout.ts +0 -20
  286. package/src/router/tracing.ts +215 -0
  287. package/src/router/trie-matching.ts +171 -59
  288. package/src/router/types.ts +9 -63
  289. package/src/router/url-params.ts +57 -0
  290. package/src/router.ts +157 -71
  291. package/src/rsc/full-payload.ts +70 -0
  292. package/src/rsc/handler-context.ts +3 -2
  293. package/src/rsc/handler.ts +291 -217
  294. package/src/rsc/helpers.ts +168 -46
  295. package/src/rsc/index.ts +2 -5
  296. package/src/rsc/json-route-result.ts +38 -0
  297. package/src/rsc/loader-fetch.ts +114 -38
  298. package/src/rsc/manifest-init.ts +29 -42
  299. package/src/rsc/nonce.ts +10 -1
  300. package/src/rsc/origin-guard.ts +39 -25
  301. package/src/rsc/progressive-enhancement.ts +124 -13
  302. package/src/rsc/redirect-guard.ts +100 -0
  303. package/src/rsc/response-cache-serve.ts +238 -0
  304. package/src/rsc/response-error.ts +79 -12
  305. package/src/rsc/response-route-handler.ts +99 -189
  306. package/src/rsc/rsc-rendering.ts +421 -76
  307. package/src/rsc/runtime-warnings.ts +23 -10
  308. package/src/rsc/server-action.ts +282 -116
  309. package/src/rsc/shell-capture.ts +1158 -0
  310. package/src/rsc/shell-serve.ts +150 -0
  311. package/src/rsc/ssr-setup.ts +16 -0
  312. package/src/rsc/transition-gate.ts +89 -0
  313. package/src/rsc/types.ts +53 -5
  314. package/src/runtime-env.ts +18 -0
  315. package/src/search-params.ts +35 -30
  316. package/src/segment-loader-promise.ts +49 -4
  317. package/src/segment-system.tsx +350 -149
  318. package/src/serialize.ts +243 -0
  319. package/src/server/context.ts +208 -51
  320. package/src/server/cookie-parse.ts +32 -0
  321. package/src/server/cookie-store.ts +152 -5
  322. package/src/server/handle-store.ts +21 -38
  323. package/src/server/loader-registry.ts +33 -42
  324. package/src/server/request-context.ts +395 -176
  325. package/src/ssr/index.tsx +458 -178
  326. package/src/ssr/ssr-root.tsx +228 -0
  327. package/src/static-handler.ts +10 -13
  328. package/src/testing/cache-status.ts +162 -0
  329. package/src/testing/collect-handle.ts +46 -0
  330. package/src/testing/dispatch.ts +813 -0
  331. package/src/testing/dom.entry.ts +22 -0
  332. package/src/testing/e2e/fixture.ts +188 -0
  333. package/src/testing/e2e/index.ts +128 -0
  334. package/src/testing/e2e/matchers.ts +35 -0
  335. package/src/testing/e2e/page-helpers.ts +272 -0
  336. package/src/testing/e2e/parity.ts +387 -0
  337. package/src/testing/e2e/server.ts +195 -0
  338. package/src/testing/flight-matchers.ts +97 -0
  339. package/src/testing/flight-normalize.ts +11 -0
  340. package/src/testing/flight-runtime.d.ts +57 -0
  341. package/src/testing/flight-tree.ts +682 -0
  342. package/src/testing/flight.entry.ts +52 -0
  343. package/src/testing/flight.ts +257 -0
  344. package/src/testing/generated-routes.ts +199 -0
  345. package/src/testing/index.ts +105 -0
  346. package/src/testing/internal/context.ts +371 -0
  347. package/src/testing/internal/flight-client-globals.ts +30 -0
  348. package/src/testing/internal/seed-vars.ts +54 -0
  349. package/src/testing/render-handler.ts +357 -0
  350. package/src/testing/render-route.tsx +584 -0
  351. package/src/testing/run-loader.ts +385 -0
  352. package/src/testing/run-middleware.ts +205 -0
  353. package/src/testing/run-transition-when.ts +164 -0
  354. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  355. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  356. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  357. package/src/testing/vitest-stubs/version.ts +5 -0
  358. package/src/testing/vitest.ts +305 -0
  359. package/src/theme/ThemeProvider.tsx +56 -84
  360. package/src/theme/ThemeScript.tsx +7 -9
  361. package/src/theme/constants.ts +52 -13
  362. package/src/theme/index.ts +0 -7
  363. package/src/theme/theme-context.ts +1 -5
  364. package/src/theme/theme-script.ts +22 -21
  365. package/src/theme/use-theme.ts +0 -3
  366. package/src/types/boundaries.ts +0 -35
  367. package/src/types/cache-types.ts +13 -4
  368. package/src/types/error-types.ts +30 -90
  369. package/src/types/global-namespace.ts +54 -41
  370. package/src/types/handler-context.ts +110 -62
  371. package/src/types/index.ts +3 -10
  372. package/src/types/loader-types.ts +11 -9
  373. package/src/types/request-scope.ts +112 -0
  374. package/src/types/route-config.ts +20 -52
  375. package/src/types/route-entry.ts +0 -6
  376. package/src/types/segments.ts +135 -14
  377. package/src/urls/include-helper.ts +19 -64
  378. package/src/urls/include-provider.ts +71 -0
  379. package/src/urls/index.ts +2 -11
  380. package/src/urls/path-helper-types.ts +63 -17
  381. package/src/urls/path-helper.ts +22 -106
  382. package/src/urls/pattern-types.ts +72 -19
  383. package/src/urls/response-types.ts +22 -29
  384. package/src/urls/type-extraction.ts +98 -154
  385. package/src/urls/urls-function.ts +1 -19
  386. package/src/use-loader.tsx +292 -107
  387. package/src/vercel/index.ts +11 -0
  388. package/src/vercel/tracing.ts +88 -0
  389. package/src/vite/debug.ts +185 -0
  390. package/src/vite/discovery/bundle-postprocess.ts +8 -7
  391. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  392. package/src/vite/discovery/discover-routers.ts +127 -86
  393. package/src/vite/discovery/discovery-errors.ts +255 -0
  394. package/src/vite/discovery/gate-state.ts +171 -0
  395. package/src/vite/discovery/prerender-collection.ts +96 -68
  396. package/src/vite/discovery/route-types-writer.ts +40 -84
  397. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  398. package/src/vite/discovery/state.ts +45 -1
  399. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  400. package/src/vite/index.ts +4 -0
  401. package/src/vite/inject-client-debug.ts +88 -0
  402. package/src/vite/plugin-types.ts +210 -10
  403. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  404. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  405. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  406. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  407. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  408. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  409. package/src/vite/plugins/expose-action-id.ts +48 -95
  410. package/src/vite/plugins/expose-id-utils.ts +88 -55
  411. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  412. package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
  413. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  414. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  415. package/src/vite/plugins/expose-internal-ids.ts +505 -486
  416. package/src/vite/plugins/performance-tracks.ts +26 -25
  417. package/src/vite/plugins/refresh-cmd.ts +1 -1
  418. package/src/vite/plugins/use-cache-transform.ts +73 -83
  419. package/src/vite/plugins/vercel-output.ts +384 -0
  420. package/src/vite/plugins/version-injector.ts +40 -29
  421. package/src/vite/plugins/version-plugin.ts +37 -40
  422. package/src/vite/plugins/virtual-entries.ts +138 -27
  423. package/src/vite/rango.ts +236 -138
  424. package/src/vite/router-discovery.ts +927 -136
  425. package/src/vite/utils/ast-handler-extract.ts +26 -35
  426. package/src/vite/utils/banner.ts +1 -1
  427. package/src/vite/utils/bundle-analysis.ts +10 -15
  428. package/src/vite/utils/client-chunks.ts +184 -0
  429. package/src/vite/utils/directive-prologue.ts +40 -0
  430. package/src/vite/utils/forward-user-plugins.ts +171 -0
  431. package/src/vite/utils/manifest-utils.ts +4 -59
  432. package/src/vite/utils/package-resolution.ts +20 -52
  433. package/src/vite/utils/prerender-utils.ts +71 -43
  434. package/src/vite/utils/shared-utils.ts +142 -43
  435. package/src/browser/action-response-classifier.ts +0 -99
  436. package/src/browser/react/use-client-cache.ts +0 -58
  437. package/src/browser/shallow.ts +0 -40
  438. package/src/handles/index.ts +0 -7
  439. package/src/network-error-thrower.tsx +0 -23
  440. package/src/router/middleware-cookies.ts +0 -55
@@ -7,47 +7,33 @@ export type DocumentProps = {
7
7
  children: ReactNode;
8
8
  };
9
9
 
10
- /**
11
- * Parse constraint values into a union type
12
- * "a|b|c" -> "a" | "b" | "c"
13
- */
14
10
  type ParseConstraint<T extends string> =
15
11
  T extends `${infer First}|${infer Rest}` ? First | ParseConstraint<Rest> : T;
16
12
 
17
- /**
18
- * Extract param info from a param segment
19
- *
20
- * Handles:
21
- * - :param -> { name: "param", optional: false, type: string }
22
- * - :param? -> { name: "param", optional: true, type: string }
23
- * - :param(a|b) -> { name: "param", optional: false, type: "a" | "b" }
24
- * - :param(a|b)? -> { name: "param", optional: true, type: "a" | "b" }
25
- */
13
+ // Named catch-all (`:name*` / `:name+`) is matched BEFORE the `?`/suffix
14
+ // branches. Its modifier is anchored to the END of the token (no trailing
15
+ // `${string}`) so it is a true suffix and never mis-splits a constraint body
16
+ // such as `id(\d+)`. Both are a required `string`: a matched catch-all always
17
+ // binds a value (possibly ""), so the key is always present.
26
18
  type ExtractParamInfo<T extends string> =
27
- // Optional + constrained (with optional suffix): :param(a|b)?suffix
28
19
  T extends `${infer Name}(${infer Constraint})?${string}`
29
20
  ? { name: Name; optional: true; type: ParseConstraint<Constraint> }
30
- : // Constrained (with optional suffix): :param(a|b)suffix
31
- T extends `${infer Name}(${infer Constraint})${string}`
21
+ : T extends `${infer Name}(${infer Constraint})${string}`
32
22
  ? { name: Name; optional: false; type: ParseConstraint<Constraint> }
33
- : // Optional (with optional suffix): :param?suffix
34
- T extends `${infer Name}?${string}`
35
- ? { name: Name; optional: true; type: string }
36
- : // Param with dot-suffix: :param.html
37
- T extends `${infer Name}.${string}`
23
+ : T extends `${infer Name}*`
24
+ ? { name: Name; optional: false; type: string }
25
+ : T extends `${infer Name}+`
38
26
  ? { name: Name; optional: false; type: string }
39
- : // Param with dash-suffix: :param-slug
40
- T extends `${infer Name}-${string}`
41
- ? { name: Name; optional: false; type: string }
42
- : // Param with tilde-suffix: :param~v2
43
- T extends `${infer Name}~${string}`
27
+ : T extends `${infer Name}?${string}`
28
+ ? { name: Name; optional: true; type: string }
29
+ : T extends `${infer Name}.${string}`
44
30
  ? { name: Name; optional: false; type: string }
45
- : // Required: :param (no suffix)
46
- { name: T; optional: false; type: string };
31
+ : T extends `${infer Name}-${string}`
32
+ ? { name: Name; optional: false; type: string }
33
+ : T extends `${infer Name}~${string}`
34
+ ? { name: Name; optional: false; type: string }
35
+ : { name: T; optional: false; type: string };
47
36
 
48
- /**
49
- * Build param object from info
50
- */
51
37
  type ParamFromInfo<Info> = Info extends {
52
38
  name: infer N extends string;
53
39
  optional: true;
@@ -62,10 +48,6 @@ type ParamFromInfo<Info> = Info extends {
62
48
  ? { [K in N]: V }
63
49
  : never;
64
50
 
65
- /**
66
- * Merge two param objects preserving optionality
67
- * Uses Pick to preserve the modifiers from source types
68
- */
69
51
  type MergeParams<A, B> = Pick<A, keyof A> & Pick<B, keyof B> extends infer O
70
52
  ? { [K in keyof O]: O[K] }
71
53
  : never;
@@ -78,12 +60,15 @@ type MergeParams<A, B> = Pick<A, keyof A> & Pick<B, keyof B> extends infer O
78
60
  * - Optional params: /:locale? -> { locale?: string }
79
61
  * - Constrained params: /:locale(en|gb) -> { locale: "en" | "gb" }
80
62
  * - Optional + constrained: /:locale(en|gb)? -> { locale?: "en" | "gb" }
63
+ * - Named catch-all: /:path+ (one-or-more), /:slug* (zero-or-more) -> string
81
64
  *
82
65
  * @example
83
66
  * ExtractParams<"/products/:id"> // { id: string }
84
67
  * ExtractParams<"/:locale?/blog/:slug"> // { locale?: string; slug: string }
85
68
  * ExtractParams<"/:locale(en|gb)/blog"> // { locale: "en" | "gb" }
86
69
  * ExtractParams<"/:locale(en|gb)?/blog/:slug"> // { locale?: "en" | "gb"; slug: string }
70
+ * ExtractParams<"/docs/:slug*"> // { slug: string }
71
+ * ExtractParams<"/shop/:path+"> // { path: string }
87
72
  */
88
73
  export type ExtractParams<
89
74
  T extends string,
@@ -109,17 +94,11 @@ export type ExtractParams<
109
94
  */
110
95
  export type TrailingSlashMode = "never" | "always" | "ignore";
111
96
 
112
- /**
113
- * Route configuration object (alternative to string path)
114
- */
115
97
  export type RouteConfig = {
116
98
  path: string;
117
99
  trailingSlash?: TrailingSlashMode;
118
100
  };
119
101
 
120
- /**
121
- * Route definition options (global defaults)
122
- */
123
102
  export type RouteDefinitionOptions = {
124
103
  trailingSlash?: TrailingSlashMode;
125
104
  };
@@ -128,11 +107,6 @@ export type RouteDefinition = {
128
107
  [key: string]: string | RouteConfig | RouteDefinition;
129
108
  };
130
109
 
131
- /**
132
- * Recursively flatten nested routes with depth limit to prevent infinite recursion
133
- * Transforms: { products: { detail: "/product/:slug" } } => { "products.detail": "/product/:slug" }
134
- * Also handles RouteConfig objects: { api: { path: "/api" } } => { "api": "/api" }
135
- */
136
110
  type FlattenRoutes<
137
111
  T extends RouteDefinition,
138
112
  Prefix extends string = "",
@@ -153,18 +127,12 @@ type FlattenRoutes<
153
127
  : never;
154
128
  }[keyof T];
155
129
 
156
- /**
157
- * Union to intersection helper
158
- */
159
130
  type UnionToIntersection<U> = (
160
131
  U extends unknown ? (k: U) => void : never
161
132
  ) extends (k: infer I) => void
162
133
  ? I
163
134
  : never;
164
135
 
165
- /**
166
- * Resolved route map - flattened route definitions with full paths
167
- */
168
136
  export type ResolvedRouteMap<T extends RouteDefinition> = UnionToIntersection<
169
137
  FlattenRoutes<T>
170
138
  >;
@@ -1,9 +1,6 @@
1
1
  import type { AllUseItems } from "../route-types.js";
2
2
  import type { TrailingSlashMode, ResolvedRouteMap } from "./route-config.js";
3
3
 
4
- /**
5
- * Context captured for lazy include evaluation
6
- */
7
4
  export interface LazyIncludeContext {
8
5
  urlPrefix: string;
9
6
  namePrefix: string | undefined;
@@ -25,9 +22,6 @@ export interface LazyIncludeContext {
25
22
  includeScope?: string;
26
23
  }
27
24
 
28
- /**
29
- * Internal route entry stored in router
30
- */
31
25
  export interface RouteEntry<TEnv = any> {
32
26
  prefix: string;
33
27
  /**
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { ErrorInfo, NotFoundInfo } from "./boundaries.js";
3
+ import type { RevalidateParams, HandlerContext } from "./handler-context.js";
3
4
 
4
5
  /**
5
6
  * CSS class(es) for a ViewTransition phase.
@@ -8,9 +9,102 @@ import type { ErrorInfo, NotFoundInfo } from "./boundaries.js";
8
9
  */
9
10
  export type ViewTransitionClass = Record<string, string> | string;
10
11
 
12
+ /**
13
+ * The context a transition({ when }) predicate receives.
14
+ *
15
+ * It mirrors the {@link ShouldRevalidateFn} args a `revalidate()` predicate
16
+ * gets — the same navigation/action metadata — so the two read the same shape,
17
+ * plus `get`/`env` for post-handler reads. There is no full `HandlerContext`
18
+ * here: the gate runs at the RSC-payload layer with the request context, not a
19
+ * handler context, so handler-only sugar (`search`/`build`/`dev`/`headers`) is
20
+ * absent by design. `get` is the way to read what the handler/middleware set
21
+ * via `ctx.set(...)` this request.
22
+ *
23
+ * Field availability (all source fields are optional — never fabricated):
24
+ * - `currentUrl` / `currentParams` / `fromRouteName` (the navigation SOURCE) are
25
+ * populated on soft navigations and action-success revalidations. They are
26
+ * undefined on an initial full document load and on action-error / no-JS error
27
+ * paths that skip the navigation snapshot — there is no prior page to name.
28
+ * - `nextUrl` / `nextParams` / `get` / `env` / `method` are always present;
29
+ * `toRouteName` is present only when the target route is named (undefined for
30
+ * unnamed/auto-generated routes, like `fromRouteName`).
31
+ * - `actionId` / `actionUrl` / `actionResult` / `formData` are populated only
32
+ * when a server action triggered the render; `method` is "POST" then, "GET"
33
+ * otherwise. On no-JS (progressive-enhancement) action paths `actionId` may be
34
+ * undefined when React cannot surface the action's stable id: the success
35
+ * re-render still sets `actionUrl`/`formData` for a recognized action, but the
36
+ * error-boundary re-render exposes `actionUrl` only when `actionId` resolved.
37
+ * Malformed form bodies that fail before action detection expose no action
38
+ * fields. Treat `actionId` as "the action, if known", not as "was this an
39
+ * action".
40
+ *
41
+ * PREFETCH / CACHE CAVEAT (read this before gating on the source): the gate runs
42
+ * server-side during resolution. A PREFETCHED navigation renders at prefetch
43
+ * time, so `currentUrl`/`currentParams`/`fromRouteName` reflect the page the
44
+ * prefetch fired from, NOT necessarily the page the user actually navigates from
45
+ * — the decision is baked into the stored Flight payload and replayed verbatim.
46
+ * A `cache()`/prerender hit replays the stored transition with the predicate NOT
47
+ * re-run at all. So a source-sensitive predicate can be frozen to prefetch-time
48
+ * or store-time state. This is accepted (~99% of navigations match), but if your
49
+ * gate must reflect the exact click-time source, source-scope the prefetch
50
+ * (`<Link prefetchKey=":source">`) and do not `cache()` that segment.
51
+ */
52
+ export type TransitionWhenContext<
53
+ TParams = Record<string, string>,
54
+ TEnv = unknown,
55
+ > = Partial<
56
+ Pick<
57
+ RevalidateParams<TParams, TEnv>,
58
+ "currentUrl" | "currentParams" | "fromRouteName"
59
+ >
60
+ > &
61
+ Pick<
62
+ RevalidateParams<TParams, TEnv>,
63
+ | "nextUrl"
64
+ | "nextParams"
65
+ | "toRouteName"
66
+ | "actionId"
67
+ | "actionUrl"
68
+ | "actionResult"
69
+ | "formData"
70
+ | "method"
71
+ > &
72
+ Pick<HandlerContext<any, TEnv>, "get" | "env">;
73
+
74
+ /**
75
+ * Predicate that gates whether a transition() applies for the current request.
76
+ *
77
+ * Evaluated server-side AFTER the route's handler runs (so `get(...)` can read
78
+ * handler/middleware-set state) and outside any cache scope. Return false to
79
+ * drop this segment's transition for the request; return true to apply it. The
80
+ * context ({@link TransitionWhenContext}) carries the same navigation/action
81
+ * metadata a `revalidate()` predicate sees plus `get`/`env`. If it throws, the
82
+ * error is reported to the router's onError (phase "rendering") and the
83
+ * transition is dropped (the navigation does not hold).
84
+ *
85
+ * Distinct from intercept()'s `when` config selector, which runs at MATCH time
86
+ * over `{ from, to, params, segments, … }`; a transition `when` runs
87
+ * post-handler over the resolved payload.
88
+ *
89
+ * Scope: dropping a transition removes only THIS segment's contribution to the
90
+ * navigation's hold. The startTransition hold is navigation-wide — it engages if
91
+ * any matched segment still has a transition — so `when: false` makes the
92
+ * navigation stream its loading fallback only when no other matched segment
93
+ * keeps a transition (the common case: a single transition on the route).
94
+ *
95
+ * Evaluated on every fresh (cache-miss) resolution; it is NOT re-run when a
96
+ * segment is replayed from the runtime cache or a build-time prerender, and a
97
+ * prefetched navigation freezes it to prefetch-time state — see the caveat on
98
+ * {@link TransitionWhenContext}.
99
+ */
100
+ export type TransitionWhenFn = (ctx: TransitionWhenContext) => boolean;
101
+
11
102
  /**
12
103
  * Configuration for React's <ViewTransition> component.
13
- * Maps directly to ViewTransitionProps (minus children/ref/callbacks).
104
+ *
105
+ * The phase fields (enter/exit/update/share/default/name) map directly to
106
+ * ViewTransitionProps (minus children/ref/callbacks). The `viewTransition`
107
+ * field is router-specific and is stripped before the config reaches React.
14
108
  */
15
109
  export interface TransitionConfig {
16
110
  enter?: ViewTransitionClass;
@@ -19,19 +113,34 @@ export interface TransitionConfig {
19
113
  share?: ViewTransitionClass;
20
114
  default?: ViewTransitionClass;
21
115
  name?: string;
116
+ /**
117
+ * Whether the router wraps this segment's content in its own
118
+ * <ViewTransition> boundary.
119
+ *
120
+ * - "auto" (default): the router places the boundary, producing the
121
+ * router-owned cross-fade described by the phase fields above.
122
+ * - false: the router places no boundary. The navigation commit is still
123
+ * driven through startTransition (so loaders hold instead of flashing a
124
+ * skeleton, and consumer-placed <ViewTransition> elements still animate),
125
+ * but the router contributes no cross-fade of its own.
126
+ *
127
+ * When unset, inherits the createRouter({ viewTransition }) default.
128
+ */
129
+ viewTransition?: "auto" | false;
130
+ /**
131
+ * Optional server-side predicate that gates this transition per request. When
132
+ * present and it returns false (evaluated post-handler), the router drops this
133
+ * segment's transition for the request, so the navigation streams its loading
134
+ * fallback instead of holding. The predicate is server-only and never
135
+ * serialized to the client; only its resolved effect (transition kept or
136
+ * dropped) crosses. See {@link TransitionWhenFn}.
137
+ */
138
+ when?: TransitionWhenFn;
22
139
  }
23
140
 
24
141
  /**
25
142
  * Resolved segment with component
26
143
  *
27
- * Segment types:
28
- * - layout: Wraps child content via <Outlet />
29
- * - route: The leaf content for a URL
30
- * - parallel: Named slots rendered via <ParallelOutlet name="@slot" />
31
- * - loader: Data segment (no visual rendering, carries loaderData)
32
- * - error: Error fallback segment (replaces failed segment with error UI)
33
- * - notFound: Not found fallback segment (replaces segment when data not found)
34
- *
35
144
  * @internal This type is an implementation detail and may change without notice.
36
145
  */
37
146
  export interface ResolvedSegment {
@@ -62,13 +171,16 @@ export interface ResolvedSegment {
62
171
  notFoundInfo?: NotFoundInfo; // For notFound segments: the not found information
63
172
  // Mount path from include() scope, used for MountContext.Provider wrapping
64
173
  mountPath?: string;
174
+ /**
175
+ * @internal Server-side marker: true when the segment's handler actually ran
176
+ * this request (not skipped via the revalidate cache path). Used by
177
+ * match-result.ts to populate `MatchResult.resolvedIds` for client-side
178
+ * handle-bucket cleanup. Stripped from the wire payload before serialization
179
+ * — never reaches the client.
180
+ */
181
+ _handlerRan?: boolean;
65
182
  }
66
183
 
67
- /**
68
- * Segment metadata (without component)
69
- *
70
- * @internal This type is an implementation detail and may change without notice.
71
- */
72
184
  export interface SegmentMetadata {
73
185
  id: string;
74
186
  type: "layout" | "route" | "parallel" | "loader" | "error" | "notFound";
@@ -116,6 +228,15 @@ export interface MatchResult {
116
228
  segments: ResolvedSegment[];
117
229
  matched: string[];
118
230
  diff: string[];
231
+ /**
232
+ * Every segment id whose handler actually ran on the server this request,
233
+ * including ones with `component === null` that get filtered out of
234
+ * `segments`/`diff` to avoid wasted bytes. Drives the client's handle-
235
+ * cleanup pass — a slot that re-resolves and pushes nothing must clear
236
+ * its previous handle bucket, but `diff` doesn't carry it because the
237
+ * segment payload doesn't either. A superset of `diff`.
238
+ */
239
+ resolvedIds: string[];
119
240
  /**
120
241
  * Merged route params from all matched segments
121
242
  * Available for use by the handler after route matching
@@ -1,15 +1,15 @@
1
1
  import type { AllUseItems, IncludeItem } from "../route-types.js";
2
2
  import {
3
- getContext,
4
- runWithPrefixes,
5
3
  getUrlPrefix,
6
4
  getNamePrefix,
5
+ requireDslContext,
7
6
  } from "../server/context";
8
7
  import {
9
8
  INTERNAL_INCLUDE_SCOPE_PREFIX,
10
9
  validateUserRouteName,
11
10
  } from "../route-name.js";
12
11
  import type { UrlPatterns, IncludeOptions } from "./pattern-types.js";
12
+ import type { IncludeProvider } from "./include-provider.js";
13
13
  import type { IncludeFn } from "./path-helper-types.js";
14
14
 
15
15
  function hasExplicitNameOption(options: IncludeOptions | undefined): boolean {
@@ -26,28 +26,10 @@ function allocateInternalIncludeScopeId(
26
26
  }
27
27
 
28
28
  /**
29
- * Process an IncludeItem by executing its nested patterns with prefixes
30
- * This expands the include into actual route registrations
31
- */
32
- function processIncludeItem(item: IncludeItem): AllUseItems[] {
33
- const { prefix, patterns } = item;
34
- const namePrefix =
35
- (item as IncludeItem & { _lazyContext?: { namePrefix?: string } })
36
- ._lazyContext?.namePrefix ?? item.options?.name;
37
-
38
- // Execute the nested patterns' handler with URL and name prefixes
39
- // The urlPrefix being set tells nested urls() to skip RootLayout wrapping
40
- return runWithPrefixes(prefix, namePrefix, () => {
41
- // Call the nested patterns' handler - this registers routes with prefixed patterns/names
42
- return (patterns as UrlPatterns).handler();
43
- });
44
- }
45
-
46
- /**
47
- * Recursively process items, expanding any IncludeItems
48
- * Returns items with IncludeItems expanded into actual route items
29
+ * Recursively walk items, recursing into layout children.
49
30
  *
50
- * Lazy includes are kept as-is (not expanded) for the router to handle later.
31
+ * All includes are lazy and kept as-is; the router expands them on the first
32
+ * matching request.
51
33
  */
52
34
  export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
53
35
  const result: AllUseItems[] = [];
@@ -56,28 +38,8 @@ export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
56
38
  if (!item) continue;
57
39
 
58
40
  if (item.type === "include") {
59
- const includeItem = item as IncludeItem & {
60
- _expanded?: AllUseItems[];
61
- lazy?: boolean;
62
- };
63
-
64
- // Lazy includes are NOT expanded here - kept for router to handle
65
- if (includeItem.lazy) {
66
- result.push(item);
67
- continue;
68
- }
69
-
70
- // Eager includes are already expanded during include() call
71
- if (includeItem._expanded) {
72
- // Items were expanded immediately - just process them recursively
73
- result.push(...processItems(includeItem._expanded));
74
- } else {
75
- // Fallback for legacy include items without _expanded
76
- const expanded = processIncludeItem(item as IncludeItem);
77
- result.push(...processItems(expanded));
78
- }
41
+ result.push(item);
79
42
  } else if (item.type === "layout" && (item as any).uses) {
80
- // Process nested items in layout
81
43
  const layoutItem = item as any;
82
44
  layoutItem.uses = processItems(layoutItem.uses);
83
45
  result.push(layoutItem);
@@ -92,23 +54,21 @@ export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
92
54
  /**
93
55
  * Create include() helper for composing URL patterns
94
56
  *
95
- * By default, include() IMMEDIATELY expands the nested patterns. This ensures
96
- * that routes from included patterns inherit the correct parent context
97
- * (the layout they're included in).
98
- *
99
- * With `lazy: true`, patterns are NOT expanded at definition time. Instead,
100
- * they're evaluated on first request that matches the prefix. This improves
101
- * cold start time for apps with many routes.
57
+ * All includes are lazy: the nested patterns are NOT expanded at definition
58
+ * time. Instead they are evaluated on the first request that matches the
59
+ * prefix, which improves cold start time for apps with many routes.
102
60
  */
103
61
  export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
104
62
  return (
105
63
  prefix: string,
106
- patterns: UrlPatterns<TEnv>,
64
+ // A `urls()` value (eager) OR an async provider thunk
65
+ // (`() => import("./routes")`) whose evaluation is deferred to the first
66
+ // request matching `prefix`. The provider is stored unevaluated and
67
+ // resolved by the runtime lazy-include expansion / build-time discovery.
68
+ patterns: UrlPatterns<TEnv> | IncludeProvider<TEnv>,
107
69
  options?: IncludeOptions,
108
70
  ): IncludeItem => {
109
- const store = getContext();
110
- const ctx = store.getStore();
111
- if (!ctx) throw new Error("include() must be called inside urls()");
71
+ const { ctx } = requireDslContext("include() must be called inside urls()");
112
72
 
113
73
  const explicitName = options?.name;
114
74
  const hasExplicitName = hasExplicitNameOption(options);
@@ -126,10 +86,9 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
126
86
  ? capturedUrlPrefix + prefix.slice(1)
127
87
  : capturedUrlPrefix + prefix
128
88
  : prefix;
129
- const internalScope = !hasExplicitName
130
- ? allocateInternalIncludeScopeId(ctx.counters)
131
- : undefined;
132
- const nextSegment = hasExplicitName ? explicitName : internalScope;
89
+ const nextSegment = hasExplicitName
90
+ ? explicitName
91
+ : allocateInternalIncludeScopeId(ctx.counters);
133
92
  const fullNamePrefix =
134
93
  nextSegment !== undefined && nextSegment !== ""
135
94
  ? capturedNamePrefix
@@ -162,9 +121,7 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
162
121
  if (capturedParent?.shortCode) {
163
122
  const includeCounterKey = `${capturedParent.shortCode}${parentScope}_include`;
164
123
  ctx.counters[includeCounterKey] ??= 0;
165
- const includeIdx = ctx.counters[includeCounterKey];
166
- ctx.counters[includeCounterKey] = includeIdx + 1;
167
- includeScope = `${parentScope}I${includeIdx}`;
124
+ includeScope = `${parentScope}I${ctx.counters[includeCounterKey]++}`;
168
125
  }
169
126
 
170
127
  // Snapshot parent's counters AFTER allocating the include scope so lazy
@@ -184,8 +141,6 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
184
141
  ? (parentRootScoped ?? false)
185
142
  : parentRootScoped;
186
143
 
187
- // All includes are lazy - patterns are evaluated on first matching request
188
- // This improves cold start time significantly for large route sets
189
144
  return {
190
145
  type: "include",
191
146
  name,
@@ -0,0 +1,71 @@
1
+ import type { UrlPatterns } from "./pattern-types.js";
2
+
3
+ /**
4
+ * What an async `include()` provider may resolve to: a `urls()` value directly,
5
+ * or a module namespace whose `default` export is a `urls()` value (the shape
6
+ * produced by `() => import("./routes")` when the route module does
7
+ * `export default urls(...)`).
8
+ */
9
+ export type IncludeModule<TEnv = any> =
10
+ | UrlPatterns<TEnv>
11
+ | { default: UrlPatterns<TEnv> };
12
+
13
+ /**
14
+ * An async/lazy include provider: a thunk returning a `urls()` value (or a
15
+ * Promise of one). The thunk is stored unevaluated by `include()` and called
16
+ * once, on the first request that matches the prefix — so the route module and
17
+ * its (code-split) subtree are not evaluated at startup.
18
+ *
19
+ * Forward-compatible: `() => import("./routes")` today (async, separate chunk);
20
+ * `() => m.routes` with native `import defer` later (sync, deferred eval).
21
+ */
22
+ export type IncludeProvider<TEnv = any> = () =>
23
+ | IncludeModule<TEnv>
24
+ | Promise<IncludeModule<TEnv>>;
25
+
26
+ /** True when the include() argument is a provider thunk rather than a value. */
27
+ export function isIncludeProvider(value: unknown): value is IncludeProvider {
28
+ // A `urls()` value is a (branded) object; a provider is a function.
29
+ return typeof value === "function";
30
+ }
31
+
32
+ /** A `urls()` value is an object exposing a synchronous `handler()`. */
33
+ function isUrlPatterns(value: unknown): value is UrlPatterns {
34
+ return (
35
+ !!value && typeof (value as { handler?: unknown }).handler === "function"
36
+ );
37
+ }
38
+
39
+ /**
40
+ * Normalize an async provider's resolved value to a `UrlPatterns`. Accepts a
41
+ * `urls()` value directly or a module whose `default` export is one.
42
+ */
43
+ export function resolveIncludeModule<TEnv = any>(
44
+ mod: IncludeModule<TEnv>,
45
+ id?: string,
46
+ ): UrlPatterns<TEnv> {
47
+ // Prefer an explicit `default` export (the `export default urls(...)`
48
+ // convention) BEFORE duck-typing the namespace. isUrlPatterns() keys on a
49
+ // `.handler` function, but a routes module can legitimately carry a NAMED
50
+ // `export function handler(...)` alongside its `export default urls(...)`;
51
+ // checking the namespace first would then misidentify the whole module as the
52
+ // urls() value and invoke the user's helper as the DSL handler (the group
53
+ // 404s with a misleading error). A bare `() => urls(...)` provider (no
54
+ // module) has no `default`, so it still resolves via the mod-as-value branch.
55
+ const def = (mod as { default?: unknown })?.default;
56
+ if (isUrlPatterns(def)) return def as UrlPatterns<TEnv>;
57
+ if (isUrlPatterns(mod)) return mod as UrlPatterns<TEnv>;
58
+ // The common failure is a module namespace whose `default` is missing or not a
59
+ // urls() value (e.g. only named exports); `typeof` alone says "object" and
60
+ // hides that, so name the keys present. "provider" (not "async provider") —
61
+ // synchronous providers are supported (see IncludeProvider).
62
+ const got =
63
+ mod && typeof mod === "object"
64
+ ? `a module with keys [${Object.keys(mod).join(", ") || "none"}] but no valid \`default\``
65
+ : typeof mod;
66
+ throw new Error(
67
+ `[@rangojs/router] include() provider${id ? ` for "${id}"` : ""} must ` +
68
+ `resolve to a urls() value — either returned directly or as the module's ` +
69
+ `\`default\` export (e.g. \`export default urls(...)\`). Got ${got}.`,
70
+ );
71
+ }
package/src/urls/index.ts CHANGED
@@ -1,4 +1,3 @@
1
- // Response types and symbols
2
1
  export {
3
2
  RESPONSE_TYPE,
4
3
  type ResponseHandler,
@@ -8,28 +7,22 @@ export {
8
7
  type ResponseHandlerContext,
9
8
  } from "./response-types.js";
10
9
 
11
- // Pattern types
12
10
  export type {
13
11
  UnnamedRoute,
14
12
  LocalOnlyInclude,
15
13
  PathOptions,
16
- PathDefinition,
14
+ PartialPrerenderProps,
17
15
  UrlPatterns,
18
16
  IncludeOptions,
19
17
  } from "./pattern-types.js";
20
18
 
21
- // Type extraction utilities
22
19
  export type {
23
20
  ExtractRoutes,
24
21
  ExtractResponses,
25
- ExtractRouteNames,
26
- ExtractPathParams,
27
- ResponseError,
28
- ResponseEnvelope,
22
+ ProblemDetails,
29
23
  RouteResponse,
30
24
  } from "./type-extraction.js";
31
25
 
32
- // Path helper types
33
26
  export type {
34
27
  PathFn,
35
28
  ResponsePathFn,
@@ -39,10 +32,8 @@ export type {
39
32
  PathHelpers,
40
33
  } from "./path-helper-types.js";
41
34
 
42
- // Main entry point
43
35
  export { urls } from "./urls-function.js";
44
36
 
45
- // Re-exports from route-types
46
37
  export type {
47
38
  AllUseItems,
48
39
  IncludeItem,