@rangojs/router 0.0.0-experimental.002d056c

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 (305) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +899 -0
  3. package/dist/bin/rango.js +1606 -0
  4. package/dist/vite/index.js +5153 -0
  5. package/package.json +177 -0
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +253 -0
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +112 -0
  11. package/skills/document-cache/SKILL.md +182 -0
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +704 -0
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +313 -0
  16. package/skills/layout/SKILL.md +310 -0
  17. package/skills/links/SKILL.md +239 -0
  18. package/skills/loader/SKILL.md +596 -0
  19. package/skills/middleware/SKILL.md +339 -0
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +305 -0
  22. package/skills/prerender/SKILL.md +643 -0
  23. package/skills/rango/SKILL.md +118 -0
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +385 -0
  26. package/skills/router-setup/SKILL.md +439 -0
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +79 -0
  29. package/skills/typesafety/SKILL.md +623 -0
  30. package/skills/use-cache/SKILL.md +324 -0
  31. package/src/__internal.ts +273 -0
  32. package/src/bin/rango.ts +321 -0
  33. package/src/browser/action-coordinator.ts +97 -0
  34. package/src/browser/action-response-classifier.ts +99 -0
  35. package/src/browser/event-controller.ts +899 -0
  36. package/src/browser/history-state.ts +80 -0
  37. package/src/browser/index.ts +18 -0
  38. package/src/browser/intercept-utils.ts +52 -0
  39. package/src/browser/link-interceptor.ts +141 -0
  40. package/src/browser/logging.ts +55 -0
  41. package/src/browser/merge-segment-loaders.ts +134 -0
  42. package/src/browser/navigation-bridge.ts +638 -0
  43. package/src/browser/navigation-client.ts +261 -0
  44. package/src/browser/navigation-store.ts +806 -0
  45. package/src/browser/navigation-transaction.ts +297 -0
  46. package/src/browser/network-error-handler.ts +61 -0
  47. package/src/browser/partial-update.ts +582 -0
  48. package/src/browser/prefetch/cache.ts +206 -0
  49. package/src/browser/prefetch/fetch.ts +145 -0
  50. package/src/browser/prefetch/observer.ts +65 -0
  51. package/src/browser/prefetch/policy.ts +48 -0
  52. package/src/browser/prefetch/queue.ts +128 -0
  53. package/src/browser/rango-state.ts +112 -0
  54. package/src/browser/react/Link.tsx +368 -0
  55. package/src/browser/react/NavigationProvider.tsx +413 -0
  56. package/src/browser/react/ScrollRestoration.tsx +94 -0
  57. package/src/browser/react/context.ts +59 -0
  58. package/src/browser/react/filter-segment-order.ts +11 -0
  59. package/src/browser/react/index.ts +52 -0
  60. package/src/browser/react/location-state-shared.ts +162 -0
  61. package/src/browser/react/location-state.ts +107 -0
  62. package/src/browser/react/mount-context.ts +37 -0
  63. package/src/browser/react/nonce-context.ts +23 -0
  64. package/src/browser/react/shallow-equal.ts +27 -0
  65. package/src/browser/react/use-action.ts +218 -0
  66. package/src/browser/react/use-client-cache.ts +58 -0
  67. package/src/browser/react/use-handle.ts +162 -0
  68. package/src/browser/react/use-href.tsx +40 -0
  69. package/src/browser/react/use-link-status.ts +135 -0
  70. package/src/browser/react/use-mount.ts +31 -0
  71. package/src/browser/react/use-navigation.ts +99 -0
  72. package/src/browser/react/use-params.ts +65 -0
  73. package/src/browser/react/use-pathname.ts +47 -0
  74. package/src/browser/react/use-router.ts +63 -0
  75. package/src/browser/react/use-search-params.ts +56 -0
  76. package/src/browser/react/use-segments.ts +171 -0
  77. package/src/browser/response-adapter.ts +73 -0
  78. package/src/browser/rsc-router.tsx +464 -0
  79. package/src/browser/scroll-restoration.ts +397 -0
  80. package/src/browser/segment-reconciler.ts +216 -0
  81. package/src/browser/segment-structure-assert.ts +83 -0
  82. package/src/browser/server-action-bridge.ts +667 -0
  83. package/src/browser/shallow.ts +40 -0
  84. package/src/browser/types.ts +547 -0
  85. package/src/browser/validate-redirect-origin.ts +29 -0
  86. package/src/build/generate-manifest.ts +438 -0
  87. package/src/build/generate-route-types.ts +36 -0
  88. package/src/build/index.ts +35 -0
  89. package/src/build/route-trie.ts +265 -0
  90. package/src/build/route-types/ast-helpers.ts +25 -0
  91. package/src/build/route-types/ast-route-extraction.ts +98 -0
  92. package/src/build/route-types/codegen.ts +102 -0
  93. package/src/build/route-types/include-resolution.ts +411 -0
  94. package/src/build/route-types/param-extraction.ts +48 -0
  95. package/src/build/route-types/per-module-writer.ts +128 -0
  96. package/src/build/route-types/router-processing.ts +479 -0
  97. package/src/build/route-types/scan-filter.ts +78 -0
  98. package/src/build/runtime-discovery.ts +231 -0
  99. package/src/cache/background-task.ts +34 -0
  100. package/src/cache/cache-key-utils.ts +44 -0
  101. package/src/cache/cache-policy.ts +125 -0
  102. package/src/cache/cache-runtime.ts +338 -0
  103. package/src/cache/cache-scope.ts +382 -0
  104. package/src/cache/cf/cf-cache-store.ts +982 -0
  105. package/src/cache/cf/index.ts +29 -0
  106. package/src/cache/document-cache.ts +369 -0
  107. package/src/cache/handle-capture.ts +81 -0
  108. package/src/cache/handle-snapshot.ts +41 -0
  109. package/src/cache/index.ts +44 -0
  110. package/src/cache/memory-segment-store.ts +328 -0
  111. package/src/cache/profile-registry.ts +73 -0
  112. package/src/cache/read-through-swr.ts +134 -0
  113. package/src/cache/segment-codec.ts +256 -0
  114. package/src/cache/taint.ts +98 -0
  115. package/src/cache/types.ts +342 -0
  116. package/src/client.rsc.tsx +85 -0
  117. package/src/client.tsx +601 -0
  118. package/src/component-utils.ts +76 -0
  119. package/src/components/DefaultDocument.tsx +27 -0
  120. package/src/context-var.ts +86 -0
  121. package/src/debug.ts +243 -0
  122. package/src/default-error-boundary.tsx +88 -0
  123. package/src/deps/browser.ts +8 -0
  124. package/src/deps/html-stream-client.ts +2 -0
  125. package/src/deps/html-stream-server.ts +2 -0
  126. package/src/deps/rsc.ts +10 -0
  127. package/src/deps/ssr.ts +2 -0
  128. package/src/errors.ts +365 -0
  129. package/src/handle.ts +135 -0
  130. package/src/handles/MetaTags.tsx +246 -0
  131. package/src/handles/breadcrumbs.ts +66 -0
  132. package/src/handles/index.ts +7 -0
  133. package/src/handles/meta.ts +264 -0
  134. package/src/host/cookie-handler.ts +165 -0
  135. package/src/host/errors.ts +97 -0
  136. package/src/host/index.ts +53 -0
  137. package/src/host/pattern-matcher.ts +214 -0
  138. package/src/host/router.ts +352 -0
  139. package/src/host/testing.ts +79 -0
  140. package/src/host/types.ts +146 -0
  141. package/src/host/utils.ts +25 -0
  142. package/src/href-client.ts +222 -0
  143. package/src/index.rsc.ts +233 -0
  144. package/src/index.ts +277 -0
  145. package/src/internal-debug.ts +11 -0
  146. package/src/loader.rsc.ts +89 -0
  147. package/src/loader.ts +64 -0
  148. package/src/network-error-thrower.tsx +23 -0
  149. package/src/outlet-context.ts +15 -0
  150. package/src/outlet-provider.tsx +45 -0
  151. package/src/prerender/param-hash.ts +37 -0
  152. package/src/prerender/store.ts +185 -0
  153. package/src/prerender.ts +463 -0
  154. package/src/reverse.ts +330 -0
  155. package/src/root-error-boundary.tsx +289 -0
  156. package/src/route-content-wrapper.tsx +196 -0
  157. package/src/route-definition/dsl-helpers.ts +934 -0
  158. package/src/route-definition/helper-factories.ts +200 -0
  159. package/src/route-definition/helpers-types.ts +430 -0
  160. package/src/route-definition/index.ts +52 -0
  161. package/src/route-definition/redirect.ts +93 -0
  162. package/src/route-definition.ts +1 -0
  163. package/src/route-map-builder.ts +281 -0
  164. package/src/route-name.ts +53 -0
  165. package/src/route-types.ts +259 -0
  166. package/src/router/content-negotiation.ts +116 -0
  167. package/src/router/debug-manifest.ts +72 -0
  168. package/src/router/error-handling.ts +287 -0
  169. package/src/router/find-match.ts +160 -0
  170. package/src/router/handler-context.ts +451 -0
  171. package/src/router/intercept-resolution.ts +397 -0
  172. package/src/router/lazy-includes.ts +236 -0
  173. package/src/router/loader-resolution.ts +420 -0
  174. package/src/router/logging.ts +251 -0
  175. package/src/router/manifest.ts +269 -0
  176. package/src/router/match-api.ts +620 -0
  177. package/src/router/match-context.ts +266 -0
  178. package/src/router/match-handlers.ts +440 -0
  179. package/src/router/match-middleware/background-revalidation.ts +223 -0
  180. package/src/router/match-middleware/cache-lookup.ts +634 -0
  181. package/src/router/match-middleware/cache-store.ts +295 -0
  182. package/src/router/match-middleware/index.ts +81 -0
  183. package/src/router/match-middleware/intercept-resolution.ts +306 -0
  184. package/src/router/match-middleware/segment-resolution.ts +193 -0
  185. package/src/router/match-pipelines.ts +179 -0
  186. package/src/router/match-result.ts +219 -0
  187. package/src/router/metrics.ts +282 -0
  188. package/src/router/middleware-cookies.ts +55 -0
  189. package/src/router/middleware-types.ts +222 -0
  190. package/src/router/middleware.ts +749 -0
  191. package/src/router/pattern-matching.ts +563 -0
  192. package/src/router/prerender-match.ts +402 -0
  193. package/src/router/preview-match.ts +170 -0
  194. package/src/router/revalidation.ts +289 -0
  195. package/src/router/router-context.ts +320 -0
  196. package/src/router/router-interfaces.ts +452 -0
  197. package/src/router/router-options.ts +592 -0
  198. package/src/router/router-registry.ts +24 -0
  199. package/src/router/segment-resolution/fresh.ts +570 -0
  200. package/src/router/segment-resolution/helpers.ts +263 -0
  201. package/src/router/segment-resolution/loader-cache.ts +198 -0
  202. package/src/router/segment-resolution/revalidation.ts +1242 -0
  203. package/src/router/segment-resolution/static-store.ts +67 -0
  204. package/src/router/segment-resolution.ts +21 -0
  205. package/src/router/segment-wrappers.ts +291 -0
  206. package/src/router/telemetry-otel.ts +299 -0
  207. package/src/router/telemetry.ts +300 -0
  208. package/src/router/timeout.ts +148 -0
  209. package/src/router/trie-matching.ts +239 -0
  210. package/src/router/types.ts +170 -0
  211. package/src/router.ts +1006 -0
  212. package/src/rsc/handler-context.ts +45 -0
  213. package/src/rsc/handler.ts +1089 -0
  214. package/src/rsc/helpers.ts +198 -0
  215. package/src/rsc/index.ts +36 -0
  216. package/src/rsc/loader-fetch.ts +209 -0
  217. package/src/rsc/manifest-init.ts +86 -0
  218. package/src/rsc/nonce.ts +32 -0
  219. package/src/rsc/origin-guard.ts +141 -0
  220. package/src/rsc/progressive-enhancement.ts +379 -0
  221. package/src/rsc/response-error.ts +37 -0
  222. package/src/rsc/response-route-handler.ts +347 -0
  223. package/src/rsc/rsc-rendering.ts +237 -0
  224. package/src/rsc/runtime-warnings.ts +42 -0
  225. package/src/rsc/server-action.ts +348 -0
  226. package/src/rsc/ssr-setup.ts +128 -0
  227. package/src/rsc/types.ts +263 -0
  228. package/src/search-params.ts +230 -0
  229. package/src/segment-system.tsx +454 -0
  230. package/src/server/context.ts +591 -0
  231. package/src/server/cookie-store.ts +190 -0
  232. package/src/server/fetchable-loader-store.ts +37 -0
  233. package/src/server/handle-store.ts +308 -0
  234. package/src/server/loader-registry.ts +133 -0
  235. package/src/server/request-context.ts +920 -0
  236. package/src/server/root-layout.tsx +10 -0
  237. package/src/server/tsconfig.json +14 -0
  238. package/src/server.ts +51 -0
  239. package/src/ssr/index.tsx +365 -0
  240. package/src/static-handler.ts +114 -0
  241. package/src/theme/ThemeProvider.tsx +297 -0
  242. package/src/theme/ThemeScript.tsx +61 -0
  243. package/src/theme/constants.ts +62 -0
  244. package/src/theme/index.ts +48 -0
  245. package/src/theme/theme-context.ts +44 -0
  246. package/src/theme/theme-script.ts +155 -0
  247. package/src/theme/types.ts +182 -0
  248. package/src/theme/use-theme.ts +44 -0
  249. package/src/types/boundaries.ts +158 -0
  250. package/src/types/cache-types.ts +198 -0
  251. package/src/types/error-types.ts +192 -0
  252. package/src/types/global-namespace.ts +100 -0
  253. package/src/types/handler-context.ts +687 -0
  254. package/src/types/index.ts +88 -0
  255. package/src/types/loader-types.ts +183 -0
  256. package/src/types/route-config.ts +170 -0
  257. package/src/types/route-entry.ts +109 -0
  258. package/src/types/segments.ts +148 -0
  259. package/src/types.ts +1 -0
  260. package/src/urls/include-helper.ts +197 -0
  261. package/src/urls/index.ts +53 -0
  262. package/src/urls/path-helper-types.ts +339 -0
  263. package/src/urls/path-helper.ts +329 -0
  264. package/src/urls/pattern-types.ts +95 -0
  265. package/src/urls/response-types.ts +106 -0
  266. package/src/urls/type-extraction.ts +372 -0
  267. package/src/urls/urls-function.ts +98 -0
  268. package/src/urls.ts +1 -0
  269. package/src/use-loader.tsx +354 -0
  270. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  271. package/src/vite/discovery/discover-routers.ts +344 -0
  272. package/src/vite/discovery/prerender-collection.ts +385 -0
  273. package/src/vite/discovery/route-types-writer.ts +258 -0
  274. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  275. package/src/vite/discovery/state.ts +108 -0
  276. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  277. package/src/vite/index.ts +16 -0
  278. package/src/vite/plugin-types.ts +48 -0
  279. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  280. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  281. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  282. package/src/vite/plugins/expose-action-id.ts +363 -0
  283. package/src/vite/plugins/expose-id-utils.ts +287 -0
  284. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  285. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  286. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  287. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  288. package/src/vite/plugins/expose-ids/types.ts +45 -0
  289. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  290. package/src/vite/plugins/refresh-cmd.ts +65 -0
  291. package/src/vite/plugins/use-cache-transform.ts +323 -0
  292. package/src/vite/plugins/version-injector.ts +83 -0
  293. package/src/vite/plugins/version-plugin.ts +266 -0
  294. package/src/vite/plugins/version.d.ts +12 -0
  295. package/src/vite/plugins/virtual-entries.ts +123 -0
  296. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  297. package/src/vite/rango.ts +445 -0
  298. package/src/vite/router-discovery.ts +777 -0
  299. package/src/vite/utils/ast-handler-extract.ts +517 -0
  300. package/src/vite/utils/banner.ts +36 -0
  301. package/src/vite/utils/bundle-analysis.ts +137 -0
  302. package/src/vite/utils/manifest-utils.ts +70 -0
  303. package/src/vite/utils/package-resolution.ts +121 -0
  304. package/src/vite/utils/prerender-utils.ts +189 -0
  305. package/src/vite/utils/shared-utils.ts +169 -0
@@ -0,0 +1,439 @@
1
+ ---
2
+ name: router-setup
3
+ description: Create and configure the RSC router with createRouter
4
+ argument-hint: [option]
5
+ ---
6
+
7
+ # Router Setup with createRouter
8
+
9
+ ## Basic Router Creation
10
+
11
+ ```typescript
12
+ // src/router.tsx
13
+ import { createRouter } from "@rangojs/router";
14
+ import { Document } from "./document";
15
+ import { urlpatterns } from "./urls";
16
+
17
+ const router = createRouter({
18
+ document: Document,
19
+ urls: urlpatterns,
20
+ });
21
+
22
+ export default router;
23
+ ```
24
+
25
+ ## URL Patterns (Django-style)
26
+
27
+ ```typescript
28
+ // src/urls.tsx
29
+ import { urls } from "@rangojs/router";
30
+ import { HomePage } from "./pages/home";
31
+ import { AboutPage } from "./pages/about";
32
+ import { ProductPage } from "./pages/product";
33
+ import { RootLayout } from "./layouts/RootLayout";
34
+
35
+ export const urlpatterns = urls(({ path, layout, loader, loading }) => [
36
+ path("/", HomePage, { name: "home" }),
37
+ path("/about", AboutPage, { name: "about" }),
38
+
39
+ layout(<RootLayout />, () => [
40
+ path("/product/:slug", ProductPage, { name: "product" }, () => [
41
+ loader(ProductLoader),
42
+ loading(<ProductSkeleton />),
43
+ ]),
44
+ ]),
45
+ ]);
46
+ ```
47
+
48
+ ## The urls() DSL
49
+
50
+ The `urls()` function provides a callback with all available DSL functions:
51
+
52
+ ```typescript
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
+ );
69
+ ```
70
+
71
+ ## Router Options
72
+
73
+ ```typescript
74
+ interface RSCRouterOptions<TEnv> {
75
+ // URL patterns from urls() function
76
+ urls: UrlPatterns;
77
+
78
+ // Document component wrapping entire app
79
+ document?: ComponentType<{ children: ReactNode }>;
80
+
81
+ // Enable per-request performance timeline (console waterfall + Server-Timing header)
82
+ debugPerformance?: boolean;
83
+
84
+ // Default error boundary
85
+ defaultErrorBoundary?: ReactNode | ErrorBoundaryHandler;
86
+
87
+ // Default not-found boundary
88
+ defaultNotFoundBoundary?: ReactNode | NotFoundBoundaryHandler;
89
+
90
+ // Component for 404 routes
91
+ notFound?: ReactNode | ((props: { pathname: string }) => ReactNode);
92
+
93
+ // Error logging callback
94
+ onError?: OnErrorCallback<TEnv>;
95
+
96
+ // Global cache configuration
97
+ cache?: CacheConfig<TEnv>;
98
+
99
+ // Theme configuration
100
+ theme?: ThemeConfig | true;
101
+
102
+ // SSR options (streaming policy)
103
+ ssr?: SSROptions<TEnv>;
104
+
105
+ // Telemetry sink for structured lifecycle events
106
+ telemetry?: TelemetrySink;
107
+
108
+ // Connection warmup (default: true)
109
+ warmup?: boolean;
110
+
111
+ // Prefetch cache TTL in seconds (default: 300)
112
+ // Controls in-memory cache duration and Cache-Control max-age for prefetch responses.
113
+ // Set to false to disable prefetch caching.
114
+ prefetchCacheTTL?: number | false;
115
+
116
+ // CSP nonce provider (for router.fetch)
117
+ nonce?: (
118
+ request: Request,
119
+ env: TEnv,
120
+ ) => string | true | Promise<string | true>;
121
+
122
+ // RSC version string (for router.fetch)
123
+ version?: string;
124
+ }
125
+ ```
126
+
127
+ ## Using the Request Handler
128
+
129
+ The router provides a `fetch` method to handle RSC requests:
130
+
131
+ ```typescript
132
+ // src/router.tsx
133
+ import { createRouter } from "@rangojs/router";
134
+ import { Document } from "./document";
135
+ import { urlpatterns } from "./urls";
136
+
137
+ export const router = createRouter({
138
+ document: Document,
139
+ urls: urlpatterns,
140
+ nonce: () => true, // Auto-generate nonce for CSP
141
+ });
142
+
143
+ // src/worker.tsx (Cloudflare Workers)
144
+ import { router } from "./router";
145
+
146
+ export default { fetch: router.fetch };
147
+ ```
148
+
149
+ ## Document Component
150
+
151
+ ```typescript
152
+ // src/document.tsx
153
+ import type { ReactNode } from "react";
154
+
155
+ export function Document({ children }: { children: ReactNode }) {
156
+ return (
157
+ <html lang="en">
158
+ <head>
159
+ <meta charSet="utf-8" />
160
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
161
+ <title>My App</title>
162
+ </head>
163
+ <body>
164
+ <div id="root">{children}</div>
165
+ </body>
166
+ </html>
167
+ );
168
+ }
169
+ ```
170
+
171
+ ## Using with Cloudflare Workers
172
+
173
+ ```typescript
174
+ // src/router.tsx
175
+ import { createRouter } from "@rangojs/router";
176
+ import { Document } from "./document";
177
+ import { urlpatterns } from "./urls";
178
+
179
+ export const router = createRouter<AppBindings>({
180
+ document: Document,
181
+ urls: urlpatterns,
182
+ });
183
+
184
+ // src/worker.tsx
185
+ import { router } from "./router";
186
+
187
+ export default {
188
+ async fetch(request: Request, env: Env, ctx: ExecutionContext) {
189
+ return router.fetch(request, { env, ctx });
190
+ },
191
+ };
192
+ ```
193
+
194
+ ### With Dynamic Cache Configuration
195
+
196
+ For per-request cache configuration (e.g., Cloudflare Workers with ExecutionContext):
197
+
198
+ ```typescript
199
+ // src/router.tsx
200
+ import { createRouter } from "@rangojs/router";
201
+ import { CFCacheStore } from "@rangojs/router/cache";
202
+
203
+ export const router = createRouter<AppBindings>({
204
+ document: Document,
205
+ urls: urlpatterns,
206
+ // Cache config receives (env, ctx) separately
207
+ cache: (_env, ctx) => ({
208
+ store: new CFCacheStore({ ctx: ctx!, defaults: { ttl: 60 } }),
209
+ }),
210
+ });
211
+
212
+ // src/worker.tsx
213
+ import { router } from "./router";
214
+
215
+ export default {
216
+ async fetch(request: Request, env: Env, ctx: ExecutionContext) {
217
+ return router.fetch(request, { env, ctx });
218
+ },
219
+ };
220
+ ```
221
+
222
+ ## Complete Example
223
+
224
+ ```typescript
225
+ // src/urls.tsx
226
+ import { urls } from "@rangojs/router";
227
+ import { Outlet } from "@rangojs/router/client";
228
+
229
+ // Pages
230
+ import { HomePage } from "./pages/home";
231
+ import { AboutPage } from "./pages/about";
232
+ import { BlogIndexPage, BlogPostPage } from "./pages/blog";
233
+
234
+ // Layouts
235
+ import { RootLayout } from "./layouts/RootLayout";
236
+ import { BlogLayout } from "./layouts/BlogLayout";
237
+
238
+ // Loaders
239
+ import { BlogPostLoader, BlogSidebarLoader } from "./loaders/blog";
240
+
241
+ export const urlpatterns = urls(({ path, layout, parallel, loader, loading, cache }) => [
242
+ // Simple routes
243
+ path("/", HomePage, { name: "home" }),
244
+ path("/about", AboutPage, { name: "about" }),
245
+
246
+ // Blog with layout and loaders
247
+ layout(<BlogLayout />, () => [
248
+ // Sidebar as parallel route
249
+ parallel({ "@sidebar": () => <BlogSidebar /> }, () => [
250
+ loader(BlogSidebarLoader),
251
+ ]),
252
+
253
+ // Cached blog routes
254
+ cache({ ttl: 60 }, () => [
255
+ path("/blog", BlogIndexPage, { name: "blog" }),
256
+ path("/blog/:slug", BlogPostPage, { name: "blogPost" }, () => [
257
+ loader(BlogPostLoader),
258
+ loading(<BlogPostSkeleton />),
259
+ ]),
260
+ ]),
261
+ ]),
262
+ ]);
263
+ ```
264
+
265
+ ```typescript
266
+ // src/router.tsx
267
+ import { createRouter } from "@rangojs/router";
268
+ import { Document } from "./document";
269
+ import { urlpatterns } from "./urls";
270
+
271
+ const router = createRouter({
272
+ document: Document,
273
+ urls: urlpatterns,
274
+
275
+ defaultErrorBoundary: ({ error, reset }) => (
276
+ <div>
277
+ <h1>Something went wrong</h1>
278
+ <button onClick={reset}>Try again</button>
279
+ </div>
280
+ ),
281
+
282
+ notFound: ({ pathname }) => (
283
+ <div>
284
+ <h1>404</h1>
285
+ <p>Page not found: {pathname}</p>
286
+ </div>
287
+ ),
288
+ });
289
+
290
+ export default router;
291
+ ```
292
+
293
+ ## Including Sub-patterns
294
+
295
+ ```typescript
296
+ // src/urls/shop.tsx
297
+ import { urls } from "@rangojs/router";
298
+
299
+ export const shopPatterns = urls(({ path, layout }) => [
300
+ path("/", ShopIndex, { name: "index" }),
301
+ path("/product/:slug", ProductPage, { name: "product" }),
302
+ ]);
303
+
304
+ // src/urls.tsx
305
+ import { urls } from "@rangojs/router";
306
+ import { shopPatterns } from "./urls/shop";
307
+
308
+ export const urlpatterns = urls(({ path, include }) => [
309
+ path("/", HomePage, { name: "home" }),
310
+ include("/shop", shopPatterns, { name: "shop" }),
311
+ ]);
312
+ ```
313
+
314
+ ## Environment Types
315
+
316
+ ```typescript
317
+ // Bindings passed as TEnv to createRouter<TEnv>()
318
+ interface AppBindings {
319
+ DB: D1Database;
320
+ KV: KVNamespace;
321
+ }
322
+
323
+ // Variables declared via module augmentation
324
+ interface AppVariables {
325
+ user?: { id: string; name: string };
326
+ }
327
+
328
+ const router = createRouter<AppBindings>({
329
+ document: Document,
330
+ urls: urlpatterns,
331
+ });
332
+
333
+ // Register types globally for implicit typing
334
+ declare global {
335
+ namespace RSCRouter {
336
+ interface Env extends AppBindings {}
337
+ interface Vars extends AppVariables {}
338
+ }
339
+ }
340
+ ```
341
+
342
+ ## Connection Warmup
343
+
344
+ Enabled by default. Keeps TCP+TLS connections alive so navigations after idle periods
345
+ don't pay handshake costs.
346
+
347
+ After 60s of no user interaction, the connection is marked cold. When the user returns
348
+ (tab becomes visible or first mouse/touch), a `HEAD ?_rsc_warmup` request re-establishes
349
+ the TLS connection before the next navigation. The server responds with 204 No Content
350
+ before any middleware or routing runs.
351
+
352
+ ```typescript
353
+ // Enabled by default
354
+ const router = createRouter({
355
+ document: Document,
356
+ urls: urlpatterns,
357
+ });
358
+
359
+ // Disable warmup
360
+ const router = createRouter({
361
+ document: Document,
362
+ urls: urlpatterns,
363
+ warmup: false,
364
+ });
365
+ ```
366
+
367
+ The warmup request is relative to the current page path, so it works correctly
368
+ with subpath deployments (reverse proxy, base path).
369
+
370
+ ## Telemetry
371
+
372
+ The router emits structured lifecycle events through a pluggable telemetry sink.
373
+ Zero overhead when not configured.
374
+
375
+ ```typescript
376
+ // Console sink for development
377
+ import { createRouter, createConsoleSink } from "@rangojs/router";
378
+
379
+ const router = createRouter({
380
+ document: Document,
381
+ urls: urlpatterns,
382
+ telemetry: createConsoleSink(),
383
+ });
384
+ ```
385
+
386
+ ```typescript
387
+ // OpenTelemetry for production
388
+ import { createRouter, createOTelSink } from "@rangojs/router";
389
+ import { trace } from "@opentelemetry/api";
390
+
391
+ const router = createRouter({
392
+ document: Document,
393
+ urls: urlpatterns,
394
+ telemetry: createOTelSink(trace.getTracer("my-app")),
395
+ });
396
+ ```
397
+
398
+ ```typescript
399
+ // Custom sink
400
+ const router = createRouter({
401
+ telemetry: {
402
+ emit(event) {
403
+ // Send to any observability backend
404
+ myTracer.record(event);
405
+ },
406
+ },
407
+ });
408
+ ```
409
+
410
+ Events emitted: `request.start/end/error`, `loader.start/end/error`,
411
+ `handler.error`, `cache.decision`, `revalidation.decision`.
412
+
413
+ ## SSR Streaming Policy
414
+
415
+ Control whether HTML SSR responses stream progressively or wait for all content:
416
+
417
+ ```typescript
418
+ import { createRouter, type SSRStreamMode } from "@rangojs/router";
419
+
420
+ const router = createRouter({
421
+ ssr: {
422
+ resolveStreaming: ({ request }) => {
423
+ const ua = request.headers.get("user-agent") ?? "";
424
+ // Bots that can't process streamed HTML get a fully resolved page
425
+ if (/Googlebot|bingbot/i.test(ua)) return "allReady";
426
+ return "stream";
427
+ },
428
+ },
429
+ });
430
+ ```
431
+
432
+ `SSRStreamMode` is `"stream" | "allReady"`:
433
+
434
+ - `"stream"` (default) — flush HTML as React renders. Suspense fallbacks appear first, then resolved content streams in. Best for real users (fastest TTFB).
435
+ - `"allReady"` — await `stream.allReady` before flushing. The full page arrives in one shot. Use for bots that cannot execute JavaScript or process chunked HTML.
436
+
437
+ 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.
438
+
439
+ When `resolveStreaming` is not configured, the default is `"stream"`.
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: tailwind
3
+ description: Set up Tailwind CSS v4 with the Document component and CSS imports
4
+ argument-hint: [setup]
5
+ ---
6
+
7
+ # Tailwind CSS
8
+
9
+ Set up Tailwind CSS v4 with the Rango router. Styles are loaded through the Document component using Vite's `?url` CSS import.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pnpm add -D tailwindcss @tailwindcss/vite
15
+ ```
16
+
17
+ ## Vite Plugin
18
+
19
+ ```typescript
20
+ // vite.config.ts
21
+ import tailwindcss from "@tailwindcss/vite";
22
+
23
+ export default defineConfig({
24
+ plugins: [
25
+ tailwindcss(),
26
+ // ... other plugins
27
+ ],
28
+ });
29
+ ```
30
+
31
+ ## CSS Entry Point
32
+
33
+ ```css
34
+ /* src/index.css */
35
+ @import "tailwindcss";
36
+ ```
37
+
38
+ ## Document Component
39
+
40
+ Import the CSS file with `?url` to get a hashed URL, then preload and link it in `<head>`:
41
+
42
+ ```tsx
43
+ // src/document.tsx
44
+ "use client";
45
+
46
+ import type { ReactNode } from "react";
47
+ import { MetaTags } from "@rangojs/router/client";
48
+ import styles from "./index.css?url";
49
+
50
+ export function Document({ children }: { children: ReactNode }) {
51
+ return (
52
+ <html lang="en">
53
+ <head>
54
+ <link rel="preload" href={styles} as="style" />
55
+ <link rel="stylesheet" href={styles} />
56
+ <MetaTags />
57
+ </head>
58
+ <body className="font-sans antialiased text-slate-900 bg-slate-50">
59
+ {children}
60
+ </body>
61
+ </html>
62
+ );
63
+ }
64
+ ```
65
+
66
+ The `?url` suffix tells Vite to return the processed CSS file's URL instead of injecting it as a side effect. This gives you a stable, hashed asset path that works in both development and production.
67
+
68
+ ## Customizing the Theme
69
+
70
+ Tailwind v4 uses CSS `@theme` for customization:
71
+
72
+ ```css
73
+ /* src/index.css */
74
+ @import "tailwindcss";
75
+
76
+ @theme {
77
+ --font-sans: "Inter", system-ui, sans-serif;
78
+ --color-primary: #3b82f6;
79
+ --color-secondary: #64748b;
80
+ --breakpoint-3xl: 1920px;
81
+ }
82
+ ```
83
+
84
+ ## Dark Mode
85
+
86
+ Combine with the Rango theme system (see `/theme`):
87
+
88
+ ```typescript
89
+ const router = createRouter({
90
+ document: Document,
91
+ urls: urlpatterns,
92
+ theme: { attribute: "class" },
93
+ });
94
+ ```
95
+
96
+ Then use Tailwind's `dark:` variant which reads the `class` attribute:
97
+
98
+ ```tsx
99
+ <div className="bg-white dark:bg-slate-900 text-slate-900 dark:text-white">
100
+ Content
101
+ </div>
102
+ ```
103
+
104
+ ## With Custom Fonts
105
+
106
+ Use `@fontsource-variable` for self-hosted fonts bundled by Vite (see `/fonts` for all options):
107
+
108
+ ```bash
109
+ pnpm add @fontsource-variable/inter
110
+ ```
111
+
112
+ ```css
113
+ /* src/index.css */
114
+ @import "@fontsource-variable/inter";
115
+ @import "tailwindcss";
116
+
117
+ @theme {
118
+ --font-sans: "Inter Variable", system-ui, sans-serif;
119
+ }
120
+ ```
121
+
122
+ No extra `<link>` tags needed in the Document -- Vite bundles the font files from `node_modules` automatically.
123
+
124
+ ## Notes
125
+
126
+ - `?url` import is required -- bare CSS imports inject styles as a side effect and do not work with SSR streaming
127
+ - `<link rel="preload" as="style">` eliminates render-blocking by starting the download early
128
+ - Tailwind v4 does not need a `tailwind.config.js` -- use `@theme` in CSS instead
129
+ - The `@tailwindcss/vite` plugin handles content detection automatically
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: theme
3
+ description: Opt-in theme system with FOUC prevention for light/dark mode
4
+ argument-hint: [setup]
5
+ ---
6
+
7
+ # Theme Support
8
+
9
+ Opt-in theme system with FOUC prevention.
10
+
11
+ ## Enable
12
+
13
+ ```typescript
14
+ import { createRouter } from "@rangojs/router";
15
+
16
+ // Simple - all defaults
17
+ const router = createRouter<Env>({
18
+ document: Document,
19
+ urls: urlpatterns,
20
+ theme: true,
21
+ });
22
+
23
+ // Custom config
24
+ const router = createRouter<Env>({
25
+ document: Document,
26
+ urls: urlpatterns,
27
+ theme: {
28
+ defaultTheme: "system", // "light" | "dark" | "system"
29
+ themes: ["light", "dark"],
30
+ attribute: "class", // or "data-theme"
31
+ storageKey: "theme",
32
+ },
33
+ });
34
+ ```
35
+
36
+ ## Server (in loaders/middleware)
37
+
38
+ ```typescript
39
+ import { createLoader } from "@rangojs/router";
40
+ import type { Middleware } from "@rangojs/router";
41
+
42
+ // In a loader
43
+ export const SettingsLoader = createLoader(async (ctx) => {
44
+ const currentTheme = ctx.theme; // read from cookie
45
+ return { theme: currentTheme };
46
+ });
47
+
48
+ // In middleware
49
+ export const themeMiddleware: Middleware = async (ctx, next) => {
50
+ // Set theme based on user preference
51
+ ctx.setTheme("dark");
52
+ await next();
53
+ };
54
+ ```
55
+
56
+ ## Client
57
+
58
+ ```tsx
59
+ "use client";
60
+ import { useTheme } from "@rangojs/router/theme";
61
+
62
+ function ThemeToggle() {
63
+ const { theme, setTheme, resolvedTheme, systemTheme, themes } = useTheme();
64
+
65
+ return (
66
+ <select value={theme} onChange={(e) => setTheme(e.target.value)}>
67
+ <option value="system">System</option>
68
+ <option value="light">Light</option>
69
+ <option value="dark">Dark</option>
70
+ </select>
71
+ );
72
+ }
73
+ ```
74
+
75
+ ## Notes
76
+
77
+ - `<MetaTags />` auto-renders inline script for FOUC prevention
78
+ - Add `suppressHydrationWarning` to `<html>`
79
+ - Theme persists in localStorage + cookie (for SSR)