@rangojs/router 0.0.0-experimental.10 → 0.0.0-experimental.100

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 (329) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +1037 -4
  3. package/dist/bin/rango.js +1619 -157
  4. package/dist/vite/index.js +5762 -2301
  5. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  6. package/package.json +71 -63
  7. package/skills/breadcrumbs/SKILL.md +252 -0
  8. package/skills/cache-guide/SKILL.md +294 -0
  9. package/skills/caching/SKILL.md +93 -23
  10. package/skills/composability/SKILL.md +172 -0
  11. package/skills/debug-manifest/SKILL.md +12 -8
  12. package/skills/document-cache/SKILL.md +18 -16
  13. package/skills/fonts/SKILL.md +6 -4
  14. package/skills/handler-use/SKILL.md +364 -0
  15. package/skills/hooks/SKILL.md +367 -71
  16. package/skills/host-router/SKILL.md +218 -0
  17. package/skills/i18n/SKILL.md +276 -0
  18. package/skills/intercept/SKILL.md +176 -8
  19. package/skills/layout/SKILL.md +124 -3
  20. package/skills/links/SKILL.md +304 -25
  21. package/skills/loader/SKILL.md +474 -47
  22. package/skills/middleware/SKILL.md +207 -37
  23. package/skills/migrate-nextjs/SKILL.md +562 -0
  24. package/skills/migrate-react-router/SKILL.md +769 -0
  25. package/skills/mime-routes/SKILL.md +15 -11
  26. package/skills/parallel/SKILL.md +272 -1
  27. package/skills/prerender/SKILL.md +467 -65
  28. package/skills/rango/SKILL.md +89 -21
  29. package/skills/response-routes/SKILL.md +152 -91
  30. package/skills/route/SKILL.md +305 -14
  31. package/skills/router-setup/SKILL.md +210 -32
  32. package/skills/server-actions/SKILL.md +739 -0
  33. package/skills/streams-and-websockets/SKILL.md +283 -0
  34. package/skills/theme/SKILL.md +9 -8
  35. package/skills/typesafety/SKILL.md +333 -86
  36. package/skills/use-cache/SKILL.md +324 -0
  37. package/skills/view-transitions/SKILL.md +212 -0
  38. package/src/__internal.ts +102 -4
  39. package/src/bin/rango.ts +312 -15
  40. package/src/browser/action-coordinator.ts +97 -0
  41. package/src/browser/action-response-classifier.ts +99 -0
  42. package/src/browser/app-shell.ts +52 -0
  43. package/src/browser/app-version.ts +14 -0
  44. package/src/browser/event-controller.ts +136 -68
  45. package/src/browser/history-state.ts +80 -0
  46. package/src/browser/intercept-utils.ts +52 -0
  47. package/src/browser/link-interceptor.ts +24 -4
  48. package/src/browser/logging.ts +55 -0
  49. package/src/browser/merge-segment-loaders.ts +20 -12
  50. package/src/browser/navigation-bridge.ts +374 -561
  51. package/src/browser/navigation-client.ts +228 -70
  52. package/src/browser/navigation-store.ts +97 -55
  53. package/src/browser/navigation-transaction.ts +297 -0
  54. package/src/browser/network-error-handler.ts +61 -0
  55. package/src/browser/partial-update.ts +376 -315
  56. package/src/browser/prefetch/cache.ts +314 -0
  57. package/src/browser/prefetch/fetch.ts +282 -0
  58. package/src/browser/prefetch/observer.ts +65 -0
  59. package/src/browser/prefetch/policy.ts +48 -0
  60. package/src/browser/prefetch/queue.ts +191 -0
  61. package/src/browser/prefetch/resource-ready.ts +77 -0
  62. package/src/browser/rango-state.ts +152 -0
  63. package/src/browser/react/Link.tsx +255 -71
  64. package/src/browser/react/NavigationProvider.tsx +152 -24
  65. package/src/browser/react/context.ts +11 -0
  66. package/src/browser/react/filter-segment-order.ts +55 -0
  67. package/src/browser/react/index.ts +15 -12
  68. package/src/browser/react/location-state-shared.ts +95 -53
  69. package/src/browser/react/location-state.ts +60 -15
  70. package/src/browser/react/mount-context.ts +6 -1
  71. package/src/browser/react/nonce-context.ts +23 -0
  72. package/src/browser/react/shallow-equal.ts +27 -0
  73. package/src/browser/react/use-action.ts +29 -51
  74. package/src/browser/react/use-client-cache.ts +5 -3
  75. package/src/browser/react/use-handle.ts +30 -120
  76. package/src/browser/react/use-link-status.ts +6 -5
  77. package/src/browser/react/use-navigation.ts +44 -65
  78. package/src/browser/react/use-params.ts +78 -0
  79. package/src/browser/react/use-pathname.ts +47 -0
  80. package/src/browser/react/use-reverse.ts +99 -0
  81. package/src/browser/react/use-router.ts +83 -0
  82. package/src/browser/react/use-search-params.ts +56 -0
  83. package/src/browser/react/use-segments.ts +85 -99
  84. package/src/browser/response-adapter.ts +73 -0
  85. package/src/browser/rsc-router.tsx +246 -64
  86. package/src/browser/scroll-restoration.ts +127 -52
  87. package/src/browser/segment-reconciler.ts +243 -0
  88. package/src/browser/segment-structure-assert.ts +16 -0
  89. package/src/browser/server-action-bridge.ts +510 -603
  90. package/src/browser/shallow.ts +6 -1
  91. package/src/browser/types.ts +158 -48
  92. package/src/browser/validate-redirect-origin.ts +29 -0
  93. package/src/build/generate-manifest.ts +84 -23
  94. package/src/build/generate-route-types.ts +39 -828
  95. package/src/build/index.ts +4 -5
  96. package/src/build/route-trie.ts +85 -32
  97. package/src/build/route-types/ast-helpers.ts +25 -0
  98. package/src/build/route-types/ast-route-extraction.ts +98 -0
  99. package/src/build/route-types/codegen.ts +102 -0
  100. package/src/build/route-types/include-resolution.ts +418 -0
  101. package/src/build/route-types/param-extraction.ts +48 -0
  102. package/src/build/route-types/per-module-writer.ts +128 -0
  103. package/src/build/route-types/router-processing.ts +618 -0
  104. package/src/build/route-types/scan-filter.ts +85 -0
  105. package/src/build/runtime-discovery.ts +231 -0
  106. package/src/cache/background-task.ts +34 -0
  107. package/src/cache/cache-key-utils.ts +44 -0
  108. package/src/cache/cache-policy.ts +125 -0
  109. package/src/cache/cache-runtime.ts +342 -0
  110. package/src/cache/cache-scope.ts +167 -307
  111. package/src/cache/cf/cf-cache-store.ts +573 -21
  112. package/src/cache/cf/index.ts +13 -3
  113. package/src/cache/document-cache.ts +116 -77
  114. package/src/cache/handle-capture.ts +81 -0
  115. package/src/cache/handle-snapshot.ts +41 -0
  116. package/src/cache/index.ts +1 -15
  117. package/src/cache/memory-segment-store.ts +191 -13
  118. package/src/cache/profile-registry.ts +73 -0
  119. package/src/cache/read-through-swr.ts +134 -0
  120. package/src/cache/segment-codec.ts +256 -0
  121. package/src/cache/taint.ts +153 -0
  122. package/src/cache/types.ts +72 -122
  123. package/src/client.rsc.tsx +6 -1
  124. package/src/client.tsx +118 -302
  125. package/src/component-utils.ts +4 -4
  126. package/src/components/DefaultDocument.tsx +5 -1
  127. package/src/context-var.ts +156 -0
  128. package/src/debug.ts +19 -9
  129. package/src/errors.ts +77 -7
  130. package/src/handle.ts +55 -10
  131. package/src/handles/MetaTags.tsx +73 -20
  132. package/src/handles/breadcrumbs.ts +66 -0
  133. package/src/handles/index.ts +1 -0
  134. package/src/handles/meta.ts +30 -13
  135. package/src/host/cookie-handler.ts +21 -15
  136. package/src/host/errors.ts +8 -8
  137. package/src/host/index.ts +4 -7
  138. package/src/host/pattern-matcher.ts +27 -27
  139. package/src/host/router.ts +61 -39
  140. package/src/host/testing.ts +8 -8
  141. package/src/host/types.ts +15 -7
  142. package/src/host/utils.ts +1 -1
  143. package/src/href-client.ts +65 -45
  144. package/src/index.rsc.ts +138 -21
  145. package/src/index.ts +206 -51
  146. package/src/internal-debug.ts +11 -0
  147. package/src/loader.rsc.ts +25 -143
  148. package/src/loader.ts +27 -10
  149. package/src/network-error-thrower.tsx +3 -1
  150. package/src/outlet-context.ts +1 -1
  151. package/src/outlet-provider.tsx +45 -0
  152. package/src/prerender/param-hash.ts +4 -2
  153. package/src/prerender/store.ts +159 -13
  154. package/src/prerender.ts +397 -29
  155. package/src/response-utils.ts +28 -0
  156. package/src/reverse.ts +231 -121
  157. package/src/root-error-boundary.tsx +41 -29
  158. package/src/route-content-wrapper.tsx +7 -4
  159. package/src/route-definition/dsl-helpers.ts +1134 -0
  160. package/src/route-definition/helper-factories.ts +200 -0
  161. package/src/route-definition/helpers-types.ts +483 -0
  162. package/src/route-definition/index.ts +55 -0
  163. package/src/route-definition/redirect.ts +101 -0
  164. package/src/route-definition/resolve-handler-use.ts +155 -0
  165. package/src/route-definition.ts +1 -1431
  166. package/src/route-map-builder.ts +162 -123
  167. package/src/route-name.ts +53 -0
  168. package/src/route-types.ts +66 -9
  169. package/src/router/content-negotiation.ts +215 -0
  170. package/src/router/debug-manifest.ts +72 -0
  171. package/src/router/error-handling.ts +9 -9
  172. package/src/router/find-match.ts +160 -0
  173. package/src/router/handler-context.ts +418 -86
  174. package/src/router/intercept-resolution.ts +35 -20
  175. package/src/router/lazy-includes.ts +237 -0
  176. package/src/router/loader-resolution.ts +359 -128
  177. package/src/router/logging.ts +251 -0
  178. package/src/router/manifest.ts +98 -32
  179. package/src/router/match-api.ts +196 -261
  180. package/src/router/match-context.ts +4 -2
  181. package/src/router/match-handlers.ts +441 -0
  182. package/src/router/match-middleware/background-revalidation.ts +108 -93
  183. package/src/router/match-middleware/cache-lookup.ts +415 -86
  184. package/src/router/match-middleware/cache-store.ts +91 -29
  185. package/src/router/match-middleware/intercept-resolution.ts +48 -21
  186. package/src/router/match-middleware/segment-resolution.ts +73 -9
  187. package/src/router/match-pipelines.ts +10 -45
  188. package/src/router/match-result.ts +154 -35
  189. package/src/router/metrics.ts +240 -15
  190. package/src/router/middleware-cookies.ts +55 -0
  191. package/src/router/middleware-types.ts +209 -0
  192. package/src/router/middleware.ts +373 -371
  193. package/src/router/navigation-snapshot.ts +182 -0
  194. package/src/router/pattern-matching.ts +292 -52
  195. package/src/router/prerender-match.ts +502 -0
  196. package/src/router/preview-match.ts +98 -0
  197. package/src/router/request-classification.ts +310 -0
  198. package/src/router/revalidation.ts +152 -39
  199. package/src/router/route-snapshot.ts +245 -0
  200. package/src/router/router-context.ts +41 -21
  201. package/src/router/router-interfaces.ts +484 -0
  202. package/src/router/router-options.ts +618 -0
  203. package/src/router/router-registry.ts +24 -0
  204. package/src/router/segment-resolution/fresh.ts +756 -0
  205. package/src/router/segment-resolution/helpers.ts +268 -0
  206. package/src/router/segment-resolution/loader-cache.ts +199 -0
  207. package/src/router/segment-resolution/revalidation.ts +1407 -0
  208. package/src/router/segment-resolution/static-store.ts +67 -0
  209. package/src/router/segment-resolution.ts +21 -1315
  210. package/src/router/segment-wrappers.ts +291 -0
  211. package/src/router/substitute-pattern-params.ts +56 -0
  212. package/src/router/telemetry-otel.ts +299 -0
  213. package/src/router/telemetry.ts +300 -0
  214. package/src/router/timeout.ts +148 -0
  215. package/src/router/trie-matching.ts +111 -39
  216. package/src/router/types.ts +17 -9
  217. package/src/router/url-params.ts +49 -0
  218. package/src/router.ts +642 -2011
  219. package/src/rsc/handler-context.ts +45 -0
  220. package/src/rsc/handler.ts +864 -1114
  221. package/src/rsc/helpers.ts +181 -19
  222. package/src/rsc/index.ts +0 -20
  223. package/src/rsc/loader-fetch.ts +229 -0
  224. package/src/rsc/manifest-init.ts +90 -0
  225. package/src/rsc/nonce.ts +14 -0
  226. package/src/rsc/origin-guard.ts +141 -0
  227. package/src/rsc/progressive-enhancement.ts +395 -0
  228. package/src/rsc/response-error.ts +37 -0
  229. package/src/rsc/response-route-handler.ts +360 -0
  230. package/src/rsc/rsc-rendering.ts +256 -0
  231. package/src/rsc/runtime-warnings.ts +42 -0
  232. package/src/rsc/server-action.ts +360 -0
  233. package/src/rsc/ssr-setup.ts +128 -0
  234. package/src/rsc/types.ts +52 -11
  235. package/src/search-params.ts +230 -0
  236. package/src/segment-content-promise.ts +67 -0
  237. package/src/segment-loader-promise.ts +122 -0
  238. package/src/segment-system.tsx +187 -38
  239. package/src/server/context.ts +333 -59
  240. package/src/server/cookie-store.ts +190 -0
  241. package/src/server/fetchable-loader-store.ts +37 -0
  242. package/src/server/handle-store.ts +113 -15
  243. package/src/server/loader-registry.ts +24 -64
  244. package/src/server/request-context.ts +603 -109
  245. package/src/server.ts +35 -155
  246. package/src/ssr/index.tsx +107 -30
  247. package/src/static-handler.ts +126 -0
  248. package/src/theme/ThemeProvider.tsx +21 -15
  249. package/src/theme/ThemeScript.tsx +5 -5
  250. package/src/theme/constants.ts +5 -2
  251. package/src/theme/index.ts +4 -14
  252. package/src/theme/theme-context.ts +4 -30
  253. package/src/theme/theme-script.ts +21 -18
  254. package/src/types/boundaries.ts +158 -0
  255. package/src/types/cache-types.ts +198 -0
  256. package/src/types/error-types.ts +192 -0
  257. package/src/types/global-namespace.ts +100 -0
  258. package/src/types/handler-context.ts +764 -0
  259. package/src/types/index.ts +88 -0
  260. package/src/types/loader-types.ts +209 -0
  261. package/src/types/request-scope.ts +126 -0
  262. package/src/types/route-config.ts +170 -0
  263. package/src/types/route-entry.ts +120 -0
  264. package/src/types/segments.ts +167 -0
  265. package/src/types.ts +1 -1757
  266. package/src/urls/include-helper.ts +207 -0
  267. package/src/urls/index.ts +53 -0
  268. package/src/urls/path-helper-types.ts +372 -0
  269. package/src/urls/path-helper.ts +364 -0
  270. package/src/urls/pattern-types.ts +107 -0
  271. package/src/urls/response-types.ts +108 -0
  272. package/src/urls/type-extraction.ts +372 -0
  273. package/src/urls/urls-function.ts +98 -0
  274. package/src/urls.ts +1 -1282
  275. package/src/use-loader.tsx +161 -81
  276. package/src/vite/debug.ts +184 -0
  277. package/src/vite/discovery/bundle-postprocess.ts +181 -0
  278. package/src/vite/discovery/discover-routers.ts +376 -0
  279. package/src/vite/discovery/gate-state.ts +171 -0
  280. package/src/vite/discovery/prerender-collection.ts +486 -0
  281. package/src/vite/discovery/route-types-writer.ts +258 -0
  282. package/src/vite/discovery/self-gen-tracking.ts +73 -0
  283. package/src/vite/discovery/state.ts +117 -0
  284. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  285. package/src/vite/index.ts +15 -2063
  286. package/src/vite/plugin-types.ts +103 -0
  287. package/src/vite/plugins/cjs-to-esm.ts +98 -0
  288. package/src/vite/plugins/client-ref-dedup.ts +131 -0
  289. package/src/vite/plugins/client-ref-hashing.ts +117 -0
  290. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  291. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  292. package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
  293. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +107 -64
  294. package/src/vite/plugins/expose-id-utils.ts +299 -0
  295. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  296. package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
  297. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  298. package/src/vite/plugins/expose-ids/router-transform.ts +127 -0
  299. package/src/vite/plugins/expose-ids/types.ts +45 -0
  300. package/src/vite/plugins/expose-internal-ids.ts +816 -0
  301. package/src/vite/plugins/performance-tracks.ts +96 -0
  302. package/src/vite/plugins/refresh-cmd.ts +127 -0
  303. package/src/vite/plugins/use-cache-transform.ts +336 -0
  304. package/src/vite/plugins/version-injector.ts +109 -0
  305. package/src/vite/plugins/version-plugin.ts +266 -0
  306. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  307. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  308. package/src/vite/rango.ts +497 -0
  309. package/src/vite/router-discovery.ts +1423 -0
  310. package/src/vite/utils/ast-handler-extract.ts +517 -0
  311. package/src/vite/utils/banner.ts +36 -0
  312. package/src/vite/utils/bundle-analysis.ts +137 -0
  313. package/src/vite/utils/manifest-utils.ts +70 -0
  314. package/src/vite/utils/package-resolution.ts +161 -0
  315. package/src/vite/utils/prerender-utils.ts +222 -0
  316. package/src/vite/utils/shared-utils.ts +170 -0
  317. package/CLAUDE.md +0 -43
  318. package/src/browser/lru-cache.ts +0 -69
  319. package/src/browser/request-controller.ts +0 -164
  320. package/src/cache/memory-store.ts +0 -253
  321. package/src/href-context.ts +0 -33
  322. package/src/router.gen.ts +0 -6
  323. package/src/urls.gen.ts +0 -8
  324. package/src/vite/expose-handle-id.ts +0 -209
  325. package/src/vite/expose-loader-id.ts +0 -426
  326. package/src/vite/expose-location-state-id.ts +0 -177
  327. package/src/vite/expose-prerender-handler-id.ts +0 -429
  328. package/src/vite/package-resolution.ts +0 -125
  329. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -10,26 +10,32 @@ Django-inspired RSC router with composable URL patterns, type-safe href, and ser
10
10
 
11
11
  ## Skills
12
12
 
13
- | Skill | Description |
14
- |-------|-------------|
15
- | `/router-setup` | Create and configure the RSC router |
16
- | `/route` | Define routes with `urls()` and `path()` |
17
- | `/layout` | Layouts that wrap child routes |
18
- | `/loader` | Data loaders with `createLoader()` |
19
- | `/middleware` | Request processing and authentication |
20
- | `/intercept` | Modal/slide-over patterns for soft navigation |
21
- | `/parallel` | Multi-column layouts and sidebars |
22
- | `/caching` | Segment caching with memory or KV stores |
23
- | `/document-cache` | Edge caching with Cache-Control headers |
24
- | `/theme` | Light/dark mode with FOUC prevention |
25
- | `/links` | URL generation: ctx.reverse, href, useHref, useMount, scopedReverse |
26
- | `/hooks` | Client-side React hooks |
27
- | `/typesafety` | Type-safe routes, params, href, and environment |
28
- | `/host-router` | Multi-app host routing with domain/subdomain patterns |
29
- | `/tailwind` | Set up Tailwind CSS v4 with `?url` imports |
30
- | `/response-routes` | JSON/text/HTML/XML/stream endpoints with `path.json()`, `path.text()` |
31
- | `/mime-routes` | Content negotiation same URL, different response types via Accept header |
32
- | `/fonts` | Load web fonts with preload hints |
13
+ | Skill | Description |
14
+ | ----------------------- | -------------------------------------------------------------------------- |
15
+ | `/router-setup` | Create and configure the RSC router |
16
+ | `/route` | Define routes with `urls()` and `path()` |
17
+ | `/layout` | Layouts that wrap child routes |
18
+ | `/loader` | Data loaders with `createLoader()` |
19
+ | `/server-actions` | Mutations with `"use server"`, useActionState, validation, revalidation |
20
+ | `/i18n` | Locale routing with `:locale?`, resolution chains, react-intl integration |
21
+ | `/middleware` | Request processing and authentication |
22
+ | `/intercept` | Modal/slide-over patterns for soft navigation |
23
+ | `/parallel` | Multi-column layouts and sidebars |
24
+ | `/caching` | Segment caching with memory or KV stores |
25
+ | `/use-cache` | Function-level caching with `"use cache"` directive |
26
+ | `/cache-guide` | When to use `cache()` vs `"use cache"` — differences and decision guide |
27
+ | `/document-cache` | Edge caching with Cache-Control headers |
28
+ | `/theme` | Light/dark mode with FOUC prevention |
29
+ | `/links` | URL generation: ctx.reverse, href, useHref, useMount, scopedReverse |
30
+ | `/hooks` | Client-side React hooks |
31
+ | `/typesafety` | Type-safe routes, params, href, and environment |
32
+ | `/host-router` | Multi-app host routing with domain/subdomain patterns |
33
+ | `/tailwind` | Set up Tailwind CSS v4 with `?url` imports |
34
+ | `/response-routes` | JSON/text/HTML/XML/stream endpoints with `path.json()`, `path.text()` |
35
+ | `/mime-routes` | Content negotiation — same URL, different response types via Accept header |
36
+ | `/fonts` | Load web fonts with preload hints |
37
+ | `/migrate-nextjs` | Migrate a Next.js App Router project to Rango |
38
+ | `/migrate-react-router` | Migrate a React Router / Remix project to Rango |
33
39
 
34
40
  ## Quick Start
35
41
 
@@ -48,7 +54,69 @@ export const urlpatterns = urls(({ path, layout }) => [
48
54
  import { createRouter } from "@rangojs/router";
49
55
  import { urlpatterns } from "./urls";
50
56
 
51
- export default createRouter({ document: Document }).urls(urlpatterns);
57
+ export default createRouter({ document: Document }).routes(urlpatterns);
52
58
  ```
53
59
 
54
60
  Use `/typesafety` for type-safe href and environment setup.
61
+
62
+ ## CLI: `npx rango generate`
63
+
64
+ Single command to generate `.gen.ts` route type files. Auto-detects file type and
65
+ generates the appropriate output.
66
+
67
+ ```bash
68
+ # Single file
69
+ npx rango generate src/urls.tsx
70
+
71
+ # Multiple files
72
+ npx rango generate src/router.tsx src/urls.tsx
73
+
74
+ # Directory (recursive scan)
75
+ npx rango generate src/
76
+
77
+ # Mix of files and directories
78
+ npx rango generate src/urls.tsx src/api/
79
+ ```
80
+
81
+ ### Auto-detection
82
+
83
+ Each file is classified by its contents:
84
+
85
+ | Contains | Generated output |
86
+ | -------------- | ---------------------------------------------------------------- |
87
+ | `urls(` | Per-module `*.gen.ts` with route names, patterns, params, search |
88
+ | `createRouter` | Per-router `*.named-routes.gen.ts` with global route map |
89
+ | Both | Both files |
90
+
91
+ Directories are scanned recursively for `.ts`/`.tsx` files, skipping `node_modules`,
92
+ dotfiles, and existing `.gen.` files.
93
+
94
+ ### Recursive includes
95
+
96
+ The generator follows `include()` calls across files, resolving imports to build
97
+ the full route tree. Circular includes are detected and warned about.
98
+
99
+ ### First-wins deduplication
100
+
101
+ When a route name appears more than once, the first definition wins and duplicates
102
+ are dropped with a warning. This applies only to the generated `.gen.ts` type files.
103
+ Define the primary route before any fallback variant that reuses the same name.
104
+
105
+ Content negotiation (see `/mime-routes`) is unaffected — negotiated routes use
106
+ distinct names (e.g. `"product"` and `"productJson"`) and the Accept header
107
+ dispatching happens at runtime in the trie, not in the type generator.
108
+
109
+ ### Limitations
110
+
111
+ The CLI uses static source analysis (AST walking), not runtime execution. It cannot
112
+ extract routes defined dynamically:
113
+
114
+ - `Array.from()` or `.map()` generating path() calls
115
+ - Conditional routes behind `import.meta.env` or feature flags
116
+ - Routes computed from external data (databases, config files)
117
+ - Template literal patterns with interpolated variables
118
+
119
+ These routes are only discovered by the Vite plugin's runtime discovery during
120
+ `pnpm dev` or `pnpm build`. The CLI-generated `.gen.ts` may have fewer routes
121
+ than the runtime-generated version. During dev, the `preserveIfLarger` guard
122
+ prevents the static parser from overwriting a larger runtime-discovered file.
@@ -14,7 +14,7 @@ XML feeds, image proxies, and any route that returns a `Response` instead of Rea
14
14
  Inside any `urls()` callback, use `path.json()`, `path.text()`, or other tags alongside regular RSC routes:
15
15
 
16
16
  ```typescript
17
- import { urls, RouterError } from "@rangojs/router/server";
17
+ import { urls, RouterError } from "@rangojs/router";
18
18
 
19
19
  export const urlpatterns = urls(({ path, layout, include }) => [
20
20
  // RSC routes (normal)
@@ -22,43 +22,62 @@ export const urlpatterns = urls(({ path, layout, include }) => [
22
22
  path("/about", AboutPage, { name: "about" }),
23
23
 
24
24
  // JSON API route (inline, alongside RSC routes)
25
- path.json("/api/status", (ctx) => ({
26
- status: "ok",
27
- timestamp: Date.now(),
28
- }), { name: "status" }),
25
+ path.json(
26
+ "/api/status",
27
+ (ctx) => ({
28
+ status: "ok",
29
+ timestamp: Date.now(),
30
+ }),
31
+ { name: "status" },
32
+ ),
29
33
 
30
34
  // Text route
31
- path.text("/robots.txt", (ctx) => {
32
- return "User-agent: *\nAllow: /\nDisallow: /api/\n";
33
- }, { name: "robots" }),
35
+ path.text(
36
+ "/robots.txt",
37
+ (ctx) => {
38
+ return "User-agent: *\nAllow: /\nDisallow: /api/\n";
39
+ },
40
+ { name: "robots" },
41
+ ),
34
42
 
35
43
  // Markdown route
36
- path.md("/docs/:slug.md", (ctx) => {
37
- return `# ${ctx.params.slug}\n\nDocumentation content here.`;
38
- }, { name: "docs" }),
44
+ path.md(
45
+ "/docs/:slug.md",
46
+ (ctx) => {
47
+ return `# ${ctx.params.slug}\n\nDocumentation content here.`;
48
+ },
49
+ { name: "docs" },
50
+ ),
39
51
 
40
52
  // Response route (full control, returns Response directly)
41
- path.image("/og/:slug.png", async (ctx) => {
42
- const image = await generateOgImage(ctx.params.slug);
43
- return new Response(image, {
44
- headers: { "Content-Type": "image/png", "Cache-Control": "public, max-age=86400" },
45
- });
46
- }, { name: "ogImage" }),
53
+ path.image(
54
+ "/og/:slug.png",
55
+ async (ctx) => {
56
+ const image = await generateOgImage(ctx.params.slug);
57
+ return new Response(image, {
58
+ headers: {
59
+ "Content-Type": "image/png",
60
+ "Cache-Control": "public, max-age=86400",
61
+ },
62
+ });
63
+ },
64
+ { name: "ogImage" },
65
+ ),
47
66
  ]);
48
67
  ```
49
68
 
50
69
  ## Available Tags
51
70
 
52
- | Tag | Usage | Handler returns | Auto-wrap |
53
- |-----|-------|-----------------|-----------|
54
- | `json` | `path.json()` | plain object/array | `{ data: T }` envelope |
55
- | `text` | `path.text()` | string | text/plain Response |
56
- | `html` | `path.html()` | string | text/html Response |
57
- | `xml` | `path.xml()` | string | application/xml Response |
58
- | `md` | `path.md()` | string | text/markdown Response |
59
- | `image` | `path.image()` | Response | pass-through |
60
- | `stream` | `path.stream()` | Response | pass-through |
61
- | `any` | `path.any()` | Response | pass-through |
71
+ | Tag | Usage | Handler returns | Auto-wrap |
72
+ | -------- | --------------- | ------------------ | ------------------------ |
73
+ | `json` | `path.json()` | plain object/array | `{ data: T }` envelope |
74
+ | `text` | `path.text()` | string | text/plain Response |
75
+ | `html` | `path.html()` | string | text/html Response |
76
+ | `xml` | `path.xml()` | string | application/xml Response |
77
+ | `md` | `path.md()` | string | text/markdown Response |
78
+ | `image` | `path.image()` | Response | pass-through |
79
+ | `stream` | `path.stream()` | Response | pass-through |
80
+ | `any` | `path.any()` | Response | pass-through |
62
81
 
63
82
  ## ResponseHandlerContext
64
83
 
@@ -67,14 +86,15 @@ Response route handlers receive a lighter context (no `ctx.use()`, no `ctx.res`)
67
86
  ```typescript
68
87
  interface ResponseHandlerContext<TParams, TEnv> {
69
88
  request: Request;
70
- params: TParams; // Typed from URL pattern
71
- env: Bindings; // Extracted from RouterEnv (DB, KV, etc.)
89
+ params: TParams; // Typed from URL pattern
90
+ env: TEnv; // Plain bindings (DB, KV, etc.)
72
91
  searchParams: URLSearchParams;
73
92
  url: URL;
74
93
  pathname: string;
75
- href: (name: string, params?: Record<string, string>) => string;
94
+ reverse: (name: string, params?: Record<string, string>) => string;
95
+ get: GetVariableFn; // Read middleware variables
76
96
  header: (name: string, value: string) => void;
77
- setCookie: (name: string, value: string, options?: CookieOptions) => void;
97
+ // Use cookies().set(name, value, opts) for cookie mutations (standalone API)
78
98
  }
79
99
  ```
80
100
 
@@ -84,31 +104,39 @@ String-returning handlers (json, text, html, xml, md) can set custom headers and
84
104
  without constructing a full Response:
85
105
 
86
106
  ```typescript
87
- path.md("/docs/:slug.md", (ctx) => {
88
- ctx.header("Cache-Control", "public, max-age=3600");
89
- ctx.setCookie("last-doc", ctx.params.slug, { path: "/" });
90
- return `# ${ctx.params.slug}\n\nContent here.`;
91
- }, { name: "docs" });
107
+ path.md(
108
+ "/docs/:slug.md",
109
+ (ctx) => {
110
+ ctx.header("Cache-Control", "public, max-age=3600");
111
+ cookies().set("last-doc", ctx.params.slug, { path: "/" });
112
+ return `# ${ctx.params.slug}\n\nContent here.`;
113
+ },
114
+ { name: "docs" },
115
+ );
92
116
  ```
93
117
 
94
- Headers and cookies set via `ctx.header()` / `ctx.setCookie()` are merged into the
118
+ Headers set via `ctx.header()` and cookies set via `cookies().set()` are merged into the
95
119
  auto-wrapped Response. If the handler returns a `Response` directly, these are ignored
96
120
  (use the Response headers instead).
97
121
 
98
- ### Environment Type Extraction
122
+ ### Environment Access
99
123
 
100
- `env` extracts bindings from `RouterEnv`, not the full env:
124
+ `ctx.env` is always the plain bindings passed as TEnv to `createRouter<TEnv>()`:
101
125
 
102
126
  ```typescript
103
- type AppEnv = RouterEnv<{ DB: D1Database; KV: KVNamespace }, { user: User }>;
127
+ // createRouter<{ DB: D1Database; KV: KVNamespace }>({ ... })
104
128
 
105
129
  // In a response handler:
106
- path.json("/api/data", (ctx) => {
107
- ctx.env.DB; // D1Database (bindings extracted)
108
- ctx.env.KV; // KVNamespace
109
- // ctx.env.user -- NOT available (variables are not on response ctx.env)
110
- return { data: "ok" };
111
- }, { name: "data" });
130
+ path.json(
131
+ "/api/data",
132
+ (ctx) => {
133
+ ctx.env.DB; // D1Database (plain bindings)
134
+ ctx.env.KV; // KVNamespace
135
+ // Variables are accessed via ctx.get("key") or ctx.get(ContextVar)
136
+ return { data: "ok" };
137
+ },
138
+ { name: "data" },
139
+ );
112
140
  ```
113
141
 
114
142
  ## JSON Envelope
@@ -129,18 +157,24 @@ in a `ResponseEnvelope<T>` discriminated union:
129
157
  Throw `RouterError` to return structured error envelopes:
130
158
 
131
159
  ```typescript
132
- import { RouterError } from "@rangojs/router/server";
133
-
134
- path.json("/api/users/:id", (ctx) => {
135
- const user = users.get(ctx.params.id);
136
- if (!user) {
137
- throw new RouterError("NOT_FOUND", `User ${ctx.params.id} not found`, { status: 404 });
138
- }
139
- if (!hasPermission(ctx)) {
140
- throw new RouterError("FORBIDDEN", "Access denied", { status: 403 });
141
- }
142
- return user;
143
- }, { name: "user" });
160
+ import { RouterError } from "@rangojs/router";
161
+
162
+ path.json(
163
+ "/api/users/:id",
164
+ (ctx) => {
165
+ const user = users.get(ctx.params.id);
166
+ if (!user) {
167
+ throw new RouterError("NOT_FOUND", `User ${ctx.params.id} not found`, {
168
+ status: 404,
169
+ });
170
+ }
171
+ if (!hasPermission(ctx)) {
172
+ throw new RouterError("FORBIDDEN", "Access denied", { status: 403 });
173
+ }
174
+ return user;
175
+ },
176
+ { name: "user" },
177
+ );
144
178
  ```
145
179
 
146
180
  ### Returning Response Directly
@@ -148,15 +182,19 @@ path.json("/api/users/:id", (ctx) => {
148
182
  JSON handlers can return `Response` to bypass auto-wrap (custom status, headers, streaming):
149
183
 
150
184
  ```typescript
151
- path.json("/api/export", (ctx) => {
152
- const csv = generateCsv();
153
- return new Response(csv, {
154
- headers: {
155
- "Content-Type": "text/csv",
156
- "Content-Disposition": "attachment; filename=export.csv",
157
- },
158
- });
159
- }, { name: "export" });
185
+ path.json(
186
+ "/api/export",
187
+ (ctx) => {
188
+ const csv = generateCsv();
189
+ return new Response(csv, {
190
+ headers: {
191
+ "Content-Type": "text/csv",
192
+ "Content-Disposition": "attachment; filename=export.csv",
193
+ },
194
+ });
195
+ },
196
+ { name: "export" },
197
+ );
160
198
  ```
161
199
 
162
200
  ## Client-Side Type Safety
@@ -188,7 +226,7 @@ if (isResponseError(result)) {
188
226
  Look up response type from a `path.json()` or `path.text()` module by route name:
189
227
 
190
228
  ```typescript
191
- import type { RouteResponse } from "@rangojs/router/server";
229
+ import type { RouteResponse } from "@rangojs/router";
192
230
 
193
231
  // From the apiPatterns module (before include)
194
232
  type HealthData = RouteResponse<typeof apiPatterns, "health">;
@@ -269,25 +307,36 @@ A self-contained module with RSC pages + JSON APIs, mountable via `include()`:
269
307
 
270
308
  ```typescript
271
309
  // blog/api/urls.tsx
272
- import { urls, RouterError } from "@rangojs/router/server";
310
+ import { urls, RouterError } from "@rangojs/router";
273
311
 
274
312
  export const blogApiPatterns = urls(({ path }) => [
275
- path.json("/stats", (ctx) => ({
276
- views: 1200, visitors: 450,
277
- }), { name: "stats" }),
278
-
279
- path.json("/:slug/likes", (ctx) => ({
280
- slug: ctx.params.slug,
281
- count: 42,
282
- }), { name: "likes" }),
283
-
284
- path.json("/:slug/comments", (ctx) => ([
285
- { id: "c1", body: "Great post", author: "alice" },
286
- ]), { name: "comments" }),
313
+ path.json(
314
+ "/stats",
315
+ (ctx) => ({
316
+ views: 1200,
317
+ visitors: 450,
318
+ }),
319
+ { name: "stats" },
320
+ ),
321
+
322
+ path.json(
323
+ "/:slug/likes",
324
+ (ctx) => ({
325
+ slug: ctx.params.slug,
326
+ count: 42,
327
+ }),
328
+ { name: "likes" },
329
+ ),
330
+
331
+ path.json(
332
+ "/:slug/comments",
333
+ (ctx) => [{ id: "c1", body: "Great post", author: "alice" }],
334
+ { name: "comments" },
335
+ ),
287
336
  ]);
288
337
 
289
338
  // blog/urls.tsx
290
- import { urls } from "@rangojs/router/server";
339
+ import { urls } from "@rangojs/router";
291
340
  import { blogApiPatterns } from "./api/urls";
292
341
 
293
342
  export const blogPatterns = urls(({ path, include }) => [
@@ -299,7 +348,7 @@ export const blogPatterns = urls(({ path, include }) => [
299
348
  ]);
300
349
 
301
350
  // app/urls.tsx
302
- import { urls } from "@rangojs/router/server";
351
+ import { urls } from "@rangojs/router";
303
352
  import { blogPatterns } from "./blog/urls";
304
353
 
305
354
  export const urlpatterns = urls(({ path, include }) => [
@@ -311,7 +360,7 @@ export const urlpatterns = urls(({ path, include }) => [
311
360
  ### Type safety after mounting
312
361
 
313
362
  ```typescript
314
- import type { RouteResponse } from "@rangojs/router/server";
363
+ import type { RouteResponse } from "@rangojs/router";
315
364
  import type { PathResponse, ParamsFor } from "@rangojs/router/client";
316
365
 
317
366
  // Scoped (before mount) -- use the module directly
@@ -333,13 +382,17 @@ Response route handlers inside a mounted module can reference local names:
333
382
 
334
383
  ```typescript
335
384
  // Inside blogApiPatterns handler
336
- path("/:slug/likes", (ctx) => {
337
- // ctx.reverse resolves names relative to the mount point
338
- const commentsUrl = ctx.reverse("comments", { slug: ctx.params.slug });
339
- // -> "/blog/api/my-post/comments"
340
-
341
- return { slug: ctx.params.slug, count: 42, commentsUrl };
342
- }, { name: "likes" });
385
+ path(
386
+ "/:slug/likes",
387
+ (ctx) => {
388
+ // ctx.reverse resolves names relative to the mount point
389
+ const commentsUrl = ctx.reverse("comments", { slug: ctx.params.slug });
390
+ // -> "/blog/api/my-post/comments"
391
+
392
+ return { slug: ctx.params.slug, count: 42, commentsUrl };
393
+ },
394
+ { name: "likes" },
395
+ );
343
396
  ```
344
397
 
345
398
  ## Content Negotiation
@@ -347,6 +400,14 @@ path("/:slug/likes", (ctx) => {
347
400
  Multiple response types can share the same URL pattern. See `/mime-routes` for the
348
401
  full content negotiation API (Accept header matching, Vary: Accept, multi-variant routes).
349
402
 
403
+ ## Long-Lived Responses (SSE / WebSocket)
404
+
405
+ For Server-Sent Events (`path.stream`) and WebSocket upgrades (`path.any`
406
+ returning a 101 / `webSocket` Response), see `/streams-and-websockets`.
407
+ Upgrade responses flow through without reconstruction; `Vary` and
408
+ `Server-Timing` are skipped, and stub headers are applied in place on a
409
+ best-effort basis.
410
+
350
411
  ## How It Works
351
412
 
352
413
  1. `path.json()` tags the route at the trie level with a MIME type