@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
@@ -50,20 +50,22 @@ export const urlpatterns = urls(({ path, layout, loader, loading }) => [
50
50
  The `urls()` function provides a callback with all available DSL functions:
51
51
 
52
52
  ```typescript
53
- urls(({
54
- path, // Define a route
55
- layout, // Wrap routes in a layout
56
- parallel, // Define parallel routes (slots)
57
- loader, // Add data loader
58
- loading, // Add loading skeleton
59
- cache, // Configure caching
60
- middleware, // Add middleware
61
- revalidate, // Control revalidation
62
- intercept, // Intercept routes for modals
63
- when, // Conditional rendering
64
- }) => [
65
- // Route definitions here
66
- ]);
53
+ urls(
54
+ ({
55
+ path, // Define a route
56
+ layout, // Wrap routes in a layout
57
+ parallel, // Define parallel routes (slots)
58
+ loader, // Add data loader
59
+ loading, // Add loading skeleton
60
+ cache, // Configure caching
61
+ middleware, // Add middleware
62
+ revalidate, // Control revalidation
63
+ intercept, // Intercept routes for modals
64
+ when, // Conditional rendering
65
+ }) => [
66
+ // Route definitions here
67
+ ],
68
+ );
67
69
  ```
68
70
 
69
71
  ## Router Options
@@ -76,16 +78,21 @@ interface RSCRouterOptions<TEnv> {
76
78
  // Document component wrapping entire app
77
79
  document?: ComponentType<{ children: ReactNode }>;
78
80
 
79
- // Enable performance metrics
81
+ // URL prefix for sub-path deployments (e.g. "/admin")
82
+ // All routes, reverse(), href(), Link, redirect(), and router.use()
83
+ // patterns are automatically prefixed. Route names stay unprefixed.
84
+ basename?: string;
85
+
86
+ // Enable per-request performance timeline (console waterfall + Server-Timing header)
80
87
  debugPerformance?: boolean;
81
88
 
82
89
  // Default error boundary
83
90
  defaultErrorBoundary?: ReactNode | ErrorBoundaryHandler;
84
91
 
85
- // Default not-found boundary
92
+ // Default not-found boundary for notFound() thrown in handlers/loaders
86
93
  defaultNotFoundBoundary?: ReactNode | NotFoundBoundaryHandler;
87
94
 
88
- // Component for 404 routes
95
+ // Component for 404 (no route match, or notFound() without a boundary)
89
96
  notFound?: ReactNode | ((props: { pathname: string }) => ReactNode);
90
97
 
91
98
  // Error logging callback
@@ -97,17 +104,61 @@ interface RSCRouterOptions<TEnv> {
97
104
  // Theme configuration
98
105
  theme?: ThemeConfig | true;
99
106
 
107
+ // SSR options (streaming policy)
108
+ ssr?: SSROptions<TEnv>;
109
+
110
+ // Telemetry sink for structured lifecycle events
111
+ telemetry?: TelemetrySink;
112
+
100
113
  // Connection warmup (default: true)
101
114
  warmup?: boolean;
102
115
 
116
+ // Prefetch cache TTL in seconds (default: 300)
117
+ // Controls in-memory cache duration and Cache-Control max-age for prefetch responses.
118
+ // Set to false to disable prefetch caching.
119
+ prefetchCacheTTL?: number | false;
120
+
103
121
  // CSP nonce provider (for router.fetch)
104
- nonce?: (request: Request, env: TEnv) => string | true | Promise<string | true>;
122
+ nonce?: (
123
+ request: Request,
124
+ env: TEnv,
125
+ ) => string | true | Promise<string | true>;
105
126
 
106
127
  // RSC version string (for router.fetch)
107
128
  version?: string;
108
129
  }
109
130
  ```
110
131
 
132
+ ## Basename (Sub-Path Deployment)
133
+
134
+ When your app is served under a sub-path (e.g. `/admin` or `/v2`), set `basename`:
135
+
136
+ ```typescript
137
+ const router = createRouter({
138
+ basename: "/admin",
139
+ document: Document,
140
+ }).routes(({ path, include }) => [
141
+ path("/", Dashboard, { name: "home" }), // matches /admin
142
+ path("/users", Users, { name: "users" }), // matches /admin/users
143
+ include("/api", apiPatterns, { name: "api" }), // matches /admin/api/*
144
+ ]);
145
+
146
+ router.reverse("home"); // "/admin"
147
+ router.reverse("users"); // "/admin/users"
148
+ ```
149
+
150
+ Router-owned APIs are basename-aware:
151
+
152
+ - `reverse()` returns prefixed paths
153
+ - `<Link to="/users">` renders `<a href="/admin/users">`
154
+ - `redirect("/login")` redirects to `"/admin/login"`
155
+ - `router.use("/users/*", mw)` matches `/admin/users/*`
156
+ - `useRouter().push("/users")` navigates to `/admin/users`
157
+ - Route names stay unprefixed (`"home"`, not `"admin.home"`)
158
+
159
+ Note: `href()` is a raw path helper and does **not** auto-prefix with basename.
160
+ Use `reverse()` or `<Link>` for basename-aware URLs.
161
+
111
162
  ## Using the Request Handler
112
163
 
113
164
  The router provides a `fetch` method to handle RSC requests:
@@ -160,7 +211,7 @@ import { createRouter } from "@rangojs/router";
160
211
  import { Document } from "./document";
161
212
  import { urlpatterns } from "./urls";
162
213
 
163
- export const router = createRouter<AppEnv>({
214
+ export const router = createRouter<AppBindings>({
164
215
  document: Document,
165
216
  urls: urlpatterns,
166
217
  });
@@ -170,7 +221,7 @@ import { router } from "./router";
170
221
 
171
222
  export default {
172
223
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
173
- return router.fetch(request, { Bindings: env, Variables: {}, ctx });
224
+ return router.fetch(request, { env, ctx });
174
225
  },
175
226
  };
176
227
  ```
@@ -184,12 +235,12 @@ For per-request cache configuration (e.g., Cloudflare Workers with ExecutionCont
184
235
  import { createRouter } from "@rangojs/router";
185
236
  import { CFCacheStore } from "@rangojs/router/cache";
186
237
 
187
- export const router = createRouter<AppEnv>({
238
+ export const router = createRouter<AppBindings>({
188
239
  document: Document,
189
240
  urls: urlpatterns,
190
- // Cache config receives env with ctx for ExecutionContext access
191
- cache: (env) => ({
192
- store: new CFCacheStore({ ctx: env.ctx, defaults: { ttl: 60 } }),
241
+ // Cache config receives (env, ctx) separately
242
+ cache: (_env, ctx) => ({
243
+ store: new CFCacheStore({ ctx: ctx!, defaults: { ttl: 60 } }),
193
244
  }),
194
245
  });
195
246
 
@@ -198,7 +249,7 @@ import { router } from "./router";
198
249
 
199
250
  export default {
200
251
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
201
- return router.fetch(request, { Bindings: env, Variables: {}, ctx });
252
+ return router.fetch(request, { env, ctx });
202
253
  },
203
254
  };
204
255
  ```
@@ -274,6 +325,56 @@ const router = createRouter({
274
325
  export default router;
275
326
  ```
276
327
 
328
+ ## Not Found Handling
329
+
330
+ Two distinct 404 scenarios:
331
+
332
+ **1. No route matches the URL** — the router renders the `notFound` component from `createRouter()` config. This is automatic.
333
+
334
+ **2. A handler/loader calls `notFound()`** — signals that the route matched but the data doesn't exist (e.g., invalid product ID).
335
+
336
+ ```typescript
337
+ import { notFound } from "@rangojs/router";
338
+
339
+ // In a handler or loader
340
+ path("/product/:slug", async (ctx) => {
341
+ const product = await db.getProduct(ctx.params.slug);
342
+ if (!product) notFound("Product not found");
343
+ return <ProductPage product={product} />;
344
+ });
345
+ ```
346
+
347
+ ### Fallback chain for `notFound()`
348
+
349
+ When `notFound()` is thrown, the router looks for a fallback in this order:
350
+
351
+ 1. **`notFoundBoundary()`** — nearest boundary in the route tree (route-level)
352
+ 2. **`defaultNotFoundBoundary`** — from `createRouter()` config (app-level)
353
+ 3. **`notFound`** — from `createRouter()` config (same component used for no-route-match)
354
+ 4. **Default `<h1>Not Found</h1>`** — built-in fallback
355
+
356
+ All cases set HTTP 404 status.
357
+
358
+ ### notFoundBoundary
359
+
360
+ Wrap routes with `notFoundBoundary()` for route-specific not-found UI:
361
+
362
+ ```typescript
363
+ urls(({ path, layout }) => [
364
+ layout(ShopLayout, () => [
365
+ notFoundBoundary(({ notFound: info }) => (
366
+ <div>
367
+ <h1>Not Found</h1>
368
+ <p>{info.message}</p>
369
+ </div>
370
+ )),
371
+ path("/product/:slug", ProductPage),
372
+ ]),
373
+ ]);
374
+ ```
375
+
376
+ `notFoundBoundary` receives `{ notFound: NotFoundInfo }` where `NotFoundInfo` contains `message`, `segmentId`, `segmentType`, and `pathname`.
377
+
277
378
  ## Including Sub-patterns
278
379
 
279
380
  ```typescript
@@ -286,10 +387,10 @@ export const shopPatterns = urls(({ path, layout }) => [
286
387
  ]);
287
388
 
288
389
  // src/urls.tsx
289
- import { urls, include } from "@rangojs/router";
390
+ import { urls } from "@rangojs/router";
290
391
  import { shopPatterns } from "./urls/shop";
291
392
 
292
- export const urlpatterns = urls(({ path }) => [
393
+ export const urlpatterns = urls(({ path, include }) => [
293
394
  path("/", HomePage, { name: "home" }),
294
395
  include("/shop", shopPatterns, { name: "shop" }),
295
396
  ]);
@@ -298,23 +399,29 @@ export const urlpatterns = urls(({ path }) => [
298
399
  ## Environment Types
299
400
 
300
401
  ```typescript
301
- import type { RouterEnv } from "@rangojs/router";
302
-
402
+ // Bindings passed as TEnv to createRouter<TEnv>()
303
403
  interface AppBindings {
304
404
  DB: D1Database;
305
405
  KV: KVNamespace;
306
406
  }
307
407
 
408
+ // Variables declared via module augmentation
308
409
  interface AppVariables {
309
410
  user?: { id: string; name: string };
310
411
  }
311
412
 
312
- type AppEnv = RouterEnv<AppBindings, AppVariables>;
313
-
314
- const router = createRouter<AppEnv>({
413
+ const router = createRouter<AppBindings>({
315
414
  document: Document,
316
415
  urls: urlpatterns,
317
416
  });
417
+
418
+ // Register types globally for implicit typing
419
+ declare global {
420
+ namespace RSCRouter {
421
+ interface Env extends AppBindings {}
422
+ interface Vars extends AppVariables {}
423
+ }
424
+ }
318
425
  ```
319
426
 
320
427
  ## Connection Warmup
@@ -344,3 +451,74 @@ const router = createRouter({
344
451
 
345
452
  The warmup request is relative to the current page path, so it works correctly
346
453
  with subpath deployments (reverse proxy, base path).
454
+
455
+ ## Telemetry
456
+
457
+ The router emits structured lifecycle events through a pluggable telemetry sink.
458
+ Zero overhead when not configured.
459
+
460
+ ```typescript
461
+ // Console sink for development
462
+ import { createRouter, createConsoleSink } from "@rangojs/router";
463
+
464
+ const router = createRouter({
465
+ document: Document,
466
+ urls: urlpatterns,
467
+ telemetry: createConsoleSink(),
468
+ });
469
+ ```
470
+
471
+ ```typescript
472
+ // OpenTelemetry for production
473
+ import { createRouter, createOTelSink } from "@rangojs/router";
474
+ import { trace } from "@opentelemetry/api";
475
+
476
+ const router = createRouter({
477
+ document: Document,
478
+ urls: urlpatterns,
479
+ telemetry: createOTelSink(trace.getTracer("my-app")),
480
+ });
481
+ ```
482
+
483
+ ```typescript
484
+ // Custom sink
485
+ const router = createRouter({
486
+ telemetry: {
487
+ emit(event) {
488
+ // Send to any observability backend
489
+ myTracer.record(event);
490
+ },
491
+ },
492
+ });
493
+ ```
494
+
495
+ Events emitted: `request.start/end/error`, `loader.start/end/error`,
496
+ `handler.error`, `cache.decision`, `revalidation.decision`.
497
+
498
+ ## SSR Streaming Policy
499
+
500
+ Control whether HTML SSR responses stream progressively or wait for all content:
501
+
502
+ ```typescript
503
+ import { createRouter, type SSRStreamMode } from "@rangojs/router";
504
+
505
+ const router = createRouter({
506
+ ssr: {
507
+ resolveStreaming: ({ request }) => {
508
+ const ua = request.headers.get("user-agent") ?? "";
509
+ // Bots that can't process streamed HTML get a fully resolved page
510
+ if (/Googlebot|bingbot/i.test(ua)) return "allReady";
511
+ return "stream";
512
+ },
513
+ },
514
+ });
515
+ ```
516
+
517
+ `SSRStreamMode` is `"stream" | "allReady"`:
518
+
519
+ - `"stream"` (default) — flush HTML as React renders. Suspense fallbacks appear first, then resolved content streams in. Best for real users (fastest TTFB).
520
+ - `"allReady"` — await `stream.allReady` before flushing. The full page arrives in one shot. Use for bots that cannot execute JavaScript or process chunked HTML.
521
+
522
+ The resolver receives `{ request, env, url }` and may be sync or async. It only runs on HTML SSR paths — RSC partials, `__rsc` requests, and response routes are unaffected.
523
+
524
+ When `resolveStreaming` is not configured, the default is `"stream"`.