@rangojs/router 0.0.0-experimental.10
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.
- package/CLAUDE.md +43 -0
- package/README.md +19 -0
- package/dist/bin/rango.js +227 -0
- package/dist/vite/index.js +3039 -0
- package/package.json +171 -0
- package/skills/caching/SKILL.md +191 -0
- package/skills/debug-manifest/SKILL.md +108 -0
- package/skills/document-cache/SKILL.md +180 -0
- package/skills/fonts/SKILL.md +165 -0
- package/skills/hooks/SKILL.md +442 -0
- package/skills/intercept/SKILL.md +190 -0
- package/skills/layout/SKILL.md +213 -0
- package/skills/links/SKILL.md +180 -0
- package/skills/loader/SKILL.md +246 -0
- package/skills/middleware/SKILL.md +202 -0
- package/skills/mime-routes/SKILL.md +124 -0
- package/skills/parallel/SKILL.md +228 -0
- package/skills/prerender/SKILL.md +283 -0
- package/skills/rango/SKILL.md +54 -0
- package/skills/response-routes/SKILL.md +358 -0
- package/skills/route/SKILL.md +173 -0
- package/skills/router-setup/SKILL.md +346 -0
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +78 -0
- package/skills/typesafety/SKILL.md +394 -0
- package/src/__internal.ts +175 -0
- package/src/bin/rango.ts +24 -0
- package/src/browser/event-controller.ts +876 -0
- package/src/browser/index.ts +18 -0
- package/src/browser/link-interceptor.ts +121 -0
- package/src/browser/lru-cache.ts +69 -0
- package/src/browser/merge-segment-loaders.ts +126 -0
- package/src/browser/navigation-bridge.ts +913 -0
- package/src/browser/navigation-client.ts +165 -0
- package/src/browser/navigation-store.ts +823 -0
- package/src/browser/partial-update.ts +600 -0
- package/src/browser/react/Link.tsx +248 -0
- package/src/browser/react/NavigationProvider.tsx +346 -0
- package/src/browser/react/ScrollRestoration.tsx +94 -0
- package/src/browser/react/context.ts +53 -0
- package/src/browser/react/index.ts +52 -0
- package/src/browser/react/location-state-shared.ts +120 -0
- package/src/browser/react/location-state.ts +62 -0
- package/src/browser/react/mount-context.ts +32 -0
- package/src/browser/react/use-action.ts +240 -0
- package/src/browser/react/use-client-cache.ts +56 -0
- package/src/browser/react/use-handle.ts +203 -0
- package/src/browser/react/use-href.tsx +40 -0
- package/src/browser/react/use-link-status.ts +134 -0
- package/src/browser/react/use-mount.ts +31 -0
- package/src/browser/react/use-navigation.ts +140 -0
- package/src/browser/react/use-segments.ts +188 -0
- package/src/browser/request-controller.ts +164 -0
- package/src/browser/rsc-router.tsx +352 -0
- package/src/browser/scroll-restoration.ts +324 -0
- package/src/browser/segment-structure-assert.ts +67 -0
- package/src/browser/server-action-bridge.ts +762 -0
- package/src/browser/shallow.ts +35 -0
- package/src/browser/types.ts +478 -0
- package/src/build/generate-manifest.ts +377 -0
- package/src/build/generate-route-types.ts +828 -0
- package/src/build/index.ts +36 -0
- package/src/build/route-trie.ts +239 -0
- package/src/cache/cache-scope.ts +563 -0
- package/src/cache/cf/cf-cache-store.ts +428 -0
- package/src/cache/cf/index.ts +19 -0
- package/src/cache/document-cache.ts +340 -0
- package/src/cache/index.ts +58 -0
- package/src/cache/memory-segment-store.ts +150 -0
- package/src/cache/memory-store.ts +253 -0
- package/src/cache/types.ts +392 -0
- package/src/client.rsc.tsx +83 -0
- package/src/client.tsx +643 -0
- package/src/component-utils.ts +76 -0
- package/src/components/DefaultDocument.tsx +23 -0
- package/src/debug.ts +233 -0
- package/src/default-error-boundary.tsx +88 -0
- package/src/deps/browser.ts +8 -0
- package/src/deps/html-stream-client.ts +2 -0
- package/src/deps/html-stream-server.ts +2 -0
- package/src/deps/rsc.ts +10 -0
- package/src/deps/ssr.ts +2 -0
- package/src/errors.ts +295 -0
- package/src/handle.ts +130 -0
- package/src/handles/MetaTags.tsx +193 -0
- package/src/handles/index.ts +6 -0
- package/src/handles/meta.ts +247 -0
- package/src/host/cookie-handler.ts +159 -0
- package/src/host/errors.ts +97 -0
- package/src/host/index.ts +56 -0
- package/src/host/pattern-matcher.ts +214 -0
- package/src/host/router.ts +330 -0
- package/src/host/testing.ts +79 -0
- package/src/host/types.ts +138 -0
- package/src/host/utils.ts +25 -0
- package/src/href-client.ts +202 -0
- package/src/href-context.ts +33 -0
- package/src/index.rsc.ts +121 -0
- package/src/index.ts +165 -0
- package/src/loader.rsc.ts +207 -0
- package/src/loader.ts +47 -0
- package/src/network-error-thrower.tsx +21 -0
- package/src/outlet-context.ts +15 -0
- package/src/prerender/param-hash.ts +35 -0
- package/src/prerender/store.ts +40 -0
- package/src/prerender.ts +156 -0
- package/src/reverse.ts +267 -0
- package/src/root-error-boundary.tsx +277 -0
- package/src/route-content-wrapper.tsx +193 -0
- package/src/route-definition.ts +1431 -0
- package/src/route-map-builder.ts +242 -0
- package/src/route-types.ts +220 -0
- package/src/router/error-handling.ts +287 -0
- package/src/router/handler-context.ts +158 -0
- package/src/router/intercept-resolution.ts +387 -0
- package/src/router/loader-resolution.ts +327 -0
- package/src/router/manifest.ts +216 -0
- package/src/router/match-api.ts +621 -0
- package/src/router/match-context.ts +264 -0
- package/src/router/match-middleware/background-revalidation.ts +236 -0
- package/src/router/match-middleware/cache-lookup.ts +382 -0
- package/src/router/match-middleware/cache-store.ts +276 -0
- package/src/router/match-middleware/index.ts +81 -0
- package/src/router/match-middleware/intercept-resolution.ts +281 -0
- package/src/router/match-middleware/segment-resolution.ts +184 -0
- package/src/router/match-pipelines.ts +214 -0
- package/src/router/match-result.ts +213 -0
- package/src/router/metrics.ts +62 -0
- package/src/router/middleware.ts +791 -0
- package/src/router/pattern-matching.ts +407 -0
- package/src/router/revalidation.ts +190 -0
- package/src/router/router-context.ts +301 -0
- package/src/router/segment-resolution.ts +1315 -0
- package/src/router/trie-matching.ts +172 -0
- package/src/router/types.ts +163 -0
- package/src/router.gen.ts +6 -0
- package/src/router.ts +2423 -0
- package/src/rsc/handler.ts +1443 -0
- package/src/rsc/helpers.ts +64 -0
- package/src/rsc/index.ts +56 -0
- package/src/rsc/nonce.ts +18 -0
- package/src/rsc/types.ts +236 -0
- package/src/segment-system.tsx +442 -0
- package/src/server/context.ts +466 -0
- package/src/server/handle-store.ts +229 -0
- package/src/server/loader-registry.ts +174 -0
- package/src/server/request-context.ts +554 -0
- package/src/server/root-layout.tsx +10 -0
- package/src/server/tsconfig.json +14 -0
- package/src/server.ts +171 -0
- package/src/ssr/index.tsx +296 -0
- package/src/theme/ThemeProvider.tsx +291 -0
- package/src/theme/ThemeScript.tsx +61 -0
- package/src/theme/constants.ts +59 -0
- package/src/theme/index.ts +58 -0
- package/src/theme/theme-context.ts +70 -0
- package/src/theme/theme-script.ts +152 -0
- package/src/theme/types.ts +182 -0
- package/src/theme/use-theme.ts +44 -0
- package/src/types.ts +1757 -0
- package/src/urls.gen.ts +8 -0
- package/src/urls.ts +1282 -0
- package/src/use-loader.tsx +346 -0
- package/src/vite/expose-action-id.ts +344 -0
- package/src/vite/expose-handle-id.ts +209 -0
- package/src/vite/expose-loader-id.ts +426 -0
- package/src/vite/expose-location-state-id.ts +177 -0
- package/src/vite/expose-prerender-handler-id.ts +429 -0
- package/src/vite/index.ts +2068 -0
- package/src/vite/package-resolution.ts +125 -0
- package/src/vite/version.d.ts +12 -0
- package/src/vite/virtual-entries.ts +114 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: typesafety
|
|
3
|
+
description: Set up type-safe routes, params, and environment types in @rangojs/router
|
|
4
|
+
argument-hint: [setup]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Type Safety Setup
|
|
8
|
+
|
|
9
|
+
@rangojs/router provides end-to-end type safety for routes, parameters, and environment.
|
|
10
|
+
|
|
11
|
+
## Router Setup
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// router.tsx
|
|
15
|
+
import { createRouter } from "@rangojs/router";
|
|
16
|
+
import { urlpatterns } from "./urls";
|
|
17
|
+
|
|
18
|
+
const router = createRouter<AppEnv>({
|
|
19
|
+
document: Document,
|
|
20
|
+
urls: urlpatterns,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Server-side named-route reverse (type-safe via routeMap)
|
|
24
|
+
export const reverse = router.reverse;
|
|
25
|
+
|
|
26
|
+
export default router;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Route Definition with Type-Safe Names
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// urls.tsx
|
|
33
|
+
import { urls } from "@rangojs/router";
|
|
34
|
+
|
|
35
|
+
export const urlpatterns = urls(({ path, layout }) => [
|
|
36
|
+
path("/", HomePage, { name: "home" }),
|
|
37
|
+
path("/products", ProductsPage, { name: "products" }),
|
|
38
|
+
path("/product/:slug", ProductPage, { name: "product" }),
|
|
39
|
+
path("/cart", CartPage, { name: "cart" }),
|
|
40
|
+
path("/checkout/:step?", CheckoutPage, { name: "checkout" }),
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
// Route names are inferred from the { name } option
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Type-Safe href()
|
|
47
|
+
|
|
48
|
+
### Server: ctx.reverse + scopedReverse
|
|
49
|
+
|
|
50
|
+
In route handlers, use `scopedReverse()` for local route name autocomplete:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { scopedReverse } from "@rangojs/router";
|
|
54
|
+
|
|
55
|
+
path("/product/:slug", (ctx) => {
|
|
56
|
+
const reverse = scopedReverse<typeof shopPatterns>(ctx.reverse);
|
|
57
|
+
|
|
58
|
+
reverse("cart"); // Type-safe local name
|
|
59
|
+
reverse("product", { slug: "widget" }); // Type-safe with params
|
|
60
|
+
reverse("blog.post"); // Absolute names always allowed
|
|
61
|
+
|
|
62
|
+
return <ProductPage slug={ctx.params.slug} />;
|
|
63
|
+
}, { name: "product" })
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Client: href + useHref
|
|
67
|
+
|
|
68
|
+
On the client, `href()` validates paths against registered route patterns at compile time:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
"use client";
|
|
72
|
+
import { href, useHref, Link } from "@rangojs/router/client";
|
|
73
|
+
|
|
74
|
+
// href() validates absolute paths via PatternToPath types
|
|
75
|
+
href("/about"); // Valid path
|
|
76
|
+
href("/blog/hello"); // Matches /blog/:slug
|
|
77
|
+
|
|
78
|
+
// useHref() auto-prefixes with include() mount
|
|
79
|
+
function ShopNav() {
|
|
80
|
+
const href = useHref();
|
|
81
|
+
return <Link to={href("/cart")}>Cart</Link>; // "/shop/cart"
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
See `/links` for full URL generation guide.
|
|
86
|
+
|
|
87
|
+
## Environment Type Setup
|
|
88
|
+
|
|
89
|
+
Define your app's environment for type-safe bindings and variables:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// env.ts
|
|
93
|
+
import type { RouterEnv } from "@rangojs/router";
|
|
94
|
+
|
|
95
|
+
// Cloudflare bindings
|
|
96
|
+
interface AppBindings {
|
|
97
|
+
DB: D1Database;
|
|
98
|
+
KV: KVNamespace;
|
|
99
|
+
CACHE: KVNamespace;
|
|
100
|
+
AI: Ai;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Variables set by middleware
|
|
104
|
+
interface AppVariables {
|
|
105
|
+
user?: { id: string; email: string; role: string };
|
|
106
|
+
requestId?: string;
|
|
107
|
+
permissions?: string[];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Combined environment type
|
|
111
|
+
export type AppEnv = RouterEnv<AppBindings, AppVariables>;
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Using Environment Types
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// router.tsx
|
|
118
|
+
import type { AppEnv } from "./env";
|
|
119
|
+
|
|
120
|
+
const router = createRouter<AppEnv>({
|
|
121
|
+
document: Document,
|
|
122
|
+
urls: urlpatterns,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// middleware - typed ctx.env.Variables
|
|
126
|
+
import { createMiddleware } from "@rangojs/router";
|
|
127
|
+
|
|
128
|
+
export const authMiddleware = createMiddleware(async (ctx, next) => {
|
|
129
|
+
ctx.env.Variables.user = { id: "123", email: "user@example.com", role: "admin" };
|
|
130
|
+
await next();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// loaders - typed context
|
|
134
|
+
export const UserLoader = createLoader("user", async (ctx) => {
|
|
135
|
+
const db = ctx.env.Bindings.DB; // D1Database
|
|
136
|
+
const userId = ctx.env.Variables.user?.id;
|
|
137
|
+
return db.prepare("SELECT * FROM users WHERE id = ?").bind(userId).first();
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Global Environment Registration
|
|
142
|
+
|
|
143
|
+
Register environment types globally for implicit typing:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// router.tsx
|
|
147
|
+
declare global {
|
|
148
|
+
namespace RSCRouter {
|
|
149
|
+
interface Env extends AppEnv {}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Now handlers have typed context without explicit imports:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// In loaders
|
|
158
|
+
export const DashboardLoader = createLoader("dashboard", async (ctx) => {
|
|
159
|
+
// ctx.env.Variables.user is typed from global Env
|
|
160
|
+
// ctx.params is typed from route pattern
|
|
161
|
+
const user = ctx.env.Variables.user;
|
|
162
|
+
return { user };
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Loader Type Safety
|
|
167
|
+
|
|
168
|
+
Loaders have typed return values:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
// loaders/product.ts
|
|
172
|
+
export const ProductLoader = createLoader("product", async (ctx) => {
|
|
173
|
+
return {
|
|
174
|
+
id: ctx.params.slug,
|
|
175
|
+
name: "Widget",
|
|
176
|
+
price: 99,
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// In server component - type is inferred
|
|
181
|
+
import { useLoader } from "@rangojs/router";
|
|
182
|
+
|
|
183
|
+
async function ProductPage() {
|
|
184
|
+
const product = await useLoader(ProductLoader);
|
|
185
|
+
// product: { id: string; name: string; price: number }
|
|
186
|
+
return <h1>{product.name}</h1>;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// In client component - same type
|
|
190
|
+
"use client";
|
|
191
|
+
import { useLoaderData } from "@rangojs/router/client";
|
|
192
|
+
|
|
193
|
+
function ProductPrice() {
|
|
194
|
+
const { product } = useLoaderData(ProductLoader);
|
|
195
|
+
// product: { id: string; name: string; price: number }
|
|
196
|
+
return <span>${product.price}</span>;
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Handle Type Safety
|
|
201
|
+
|
|
202
|
+
Handles have typed data:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
// handles/breadcrumbs.ts
|
|
206
|
+
import { createHandle } from "@rangojs/router";
|
|
207
|
+
|
|
208
|
+
export const Breadcrumbs = createHandle<{ label: string; href: string }>();
|
|
209
|
+
|
|
210
|
+
// In route definition - use handle() DSL
|
|
211
|
+
import { urls } from "@rangojs/router";
|
|
212
|
+
|
|
213
|
+
export const urlpatterns = urls(({ path, handle }) => [
|
|
214
|
+
path("/shop/product/:slug", ProductPage, { name: "product" }, () => [
|
|
215
|
+
handle(Breadcrumbs, { label: "Products", href: "/shop/products" }),
|
|
216
|
+
]),
|
|
217
|
+
]);
|
|
218
|
+
|
|
219
|
+
// In client - typed array
|
|
220
|
+
function BreadcrumbNav() {
|
|
221
|
+
const crumbs = useHandle(Breadcrumbs);
|
|
222
|
+
// crumbs: Array<{ label: string; href: string }>
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Ref Prop Type Safety (Loaders & Handles)
|
|
227
|
+
|
|
228
|
+
Loaders and handles can be passed as props from server to client components.
|
|
229
|
+
Use `typeof` to get the full typed definition without manually specifying generics:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// loaders.ts
|
|
233
|
+
export const ProductLoader = createLoader(async (ctx) => {
|
|
234
|
+
return { product: await fetchProduct(ctx.params.slug) };
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// handles.ts
|
|
238
|
+
export const Breadcrumbs = createHandle<{ label: string; href: string }>();
|
|
239
|
+
|
|
240
|
+
// Client component — typeof infers all generics
|
|
241
|
+
"use client";
|
|
242
|
+
import { useLoader, useHandle } from "@rangojs/router/client";
|
|
243
|
+
import type { ProductLoader } from "../loaders";
|
|
244
|
+
import type { Breadcrumbs } from "../handles";
|
|
245
|
+
|
|
246
|
+
function MyComponent({
|
|
247
|
+
loader,
|
|
248
|
+
handle,
|
|
249
|
+
}: {
|
|
250
|
+
loader: typeof ProductLoader; // LoaderDefinition<{ product: Product }>
|
|
251
|
+
handle: typeof Breadcrumbs; // Handle<{ label: string; href: string }>
|
|
252
|
+
}) {
|
|
253
|
+
const { data } = useLoader(loader); // data is typed
|
|
254
|
+
const crumbs = useHandle(handle); // crumbs is typed array
|
|
255
|
+
// ...
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
RSC Flight serialization calls `toJSON()` on both loaders and handles,
|
|
260
|
+
sending only `{ __brand, $$id }` to the client. The hooks recover the
|
|
261
|
+
full functionality from module-level registries.
|
|
262
|
+
|
|
263
|
+
## Location State Type Safety
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// location-states.ts
|
|
267
|
+
import { createLocationState } from "@rangojs/router";
|
|
268
|
+
|
|
269
|
+
export const ProductPreview = createLocationState<{
|
|
270
|
+
name: string;
|
|
271
|
+
price: number;
|
|
272
|
+
image: string;
|
|
273
|
+
}>();
|
|
274
|
+
|
|
275
|
+
// Passing state through Link
|
|
276
|
+
<Link
|
|
277
|
+
to={href("product", { slug: "widget" })}
|
|
278
|
+
state={[ProductPreview({ name: "Widget", price: 99, image: "/img.jpg" })]}
|
|
279
|
+
>
|
|
280
|
+
View Product
|
|
281
|
+
</Link>
|
|
282
|
+
|
|
283
|
+
// Reading state in component
|
|
284
|
+
function ProductHeader() {
|
|
285
|
+
const preview = useLocationState(ProductPreview);
|
|
286
|
+
// preview: { name: string; price: number; image: string } | undefined
|
|
287
|
+
|
|
288
|
+
if (preview) {
|
|
289
|
+
return <h1>{preview.name} - ${preview.price}</h1>;
|
|
290
|
+
}
|
|
291
|
+
return <h1>Loading...</h1>;
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Multi-Project tsconfig Setup
|
|
296
|
+
|
|
297
|
+
For monorepos or multi-app setups, use a shared base tsconfig. Each app only needs
|
|
298
|
+
to extend the base and add its `router.tsx` to `files` so TypeScript picks up the
|
|
299
|
+
global type declarations (like `RSCRouter.Env`).
|
|
300
|
+
|
|
301
|
+
```jsonc
|
|
302
|
+
// tsconfig.base.json (root)
|
|
303
|
+
{
|
|
304
|
+
"compilerOptions": {
|
|
305
|
+
"target": "ES2022",
|
|
306
|
+
"module": "ESNext",
|
|
307
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
308
|
+
"jsx": "react-jsx",
|
|
309
|
+
"moduleResolution": "bundler",
|
|
310
|
+
"strict": true,
|
|
311
|
+
"noEmit": true,
|
|
312
|
+
"skipLibCheck": true,
|
|
313
|
+
"isolatedModules": true,
|
|
314
|
+
"esModuleInterop": true,
|
|
315
|
+
"resolveJsonModule": true
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
```jsonc
|
|
321
|
+
// apps/shop/tsconfig.json
|
|
322
|
+
{
|
|
323
|
+
"extends": "../../tsconfig.base.json",
|
|
324
|
+
"include": ["src"],
|
|
325
|
+
"files": ["src/router.tsx"]
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
```jsonc
|
|
330
|
+
// apps/blog/tsconfig.json
|
|
331
|
+
{
|
|
332
|
+
"extends": "../../tsconfig.base.json",
|
|
333
|
+
"include": ["src"],
|
|
334
|
+
"files": ["src/router.tsx"]
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
The `files` array ensures `router.tsx` (which contains `declare global { namespace RSCRouter { ... } }`)
|
|
339
|
+
is always included in the compilation even if nothing directly imports it. Each app gets its own
|
|
340
|
+
typed environment without interfering with other apps.
|
|
341
|
+
|
|
342
|
+
## Complete Type-Safe Setup
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// 1. env.ts - Environment types
|
|
346
|
+
export type AppEnv = RouterEnv<AppBindings, AppVariables>;
|
|
347
|
+
|
|
348
|
+
// 2. urls.tsx - Route definitions with names
|
|
349
|
+
import { urls } from "@rangojs/router";
|
|
350
|
+
|
|
351
|
+
export const urlpatterns = urls(({ path, layout, loader }) => [
|
|
352
|
+
path("/", HomePage, { name: "home" }),
|
|
353
|
+
|
|
354
|
+
layout(<ShopLayout />, () => [
|
|
355
|
+
path("/shop", ShopIndex, { name: "shop" }),
|
|
356
|
+
path("/shop/product/:slug", ProductPage, { name: "product" }, () => [
|
|
357
|
+
loader(ProductLoader),
|
|
358
|
+
]),
|
|
359
|
+
]),
|
|
360
|
+
]);
|
|
361
|
+
|
|
362
|
+
// 3. router.tsx - Registration
|
|
363
|
+
const router = createRouter<AppEnv>({
|
|
364
|
+
document: Document,
|
|
365
|
+
urls: urlpatterns,
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
declare global {
|
|
369
|
+
namespace RSCRouter {
|
|
370
|
+
interface Env extends AppEnv {}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export default router;
|
|
375
|
+
|
|
376
|
+
// 4. loaders/*.ts - Type-safe loaders
|
|
377
|
+
export const ProductLoader = createLoader("product", async (ctx) => {
|
|
378
|
+
// ctx.params: { slug: string }
|
|
379
|
+
// ctx.env.Variables.user: User | undefined
|
|
380
|
+
// ctx.env.Bindings.DB: D1Database
|
|
381
|
+
return { product: await fetchProduct(ctx.params.slug) };
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// 5. Server: ctx.reverse for named routes
|
|
385
|
+
path("/product/:slug", (ctx) => {
|
|
386
|
+
const reverse = scopedReverse<typeof urlpatterns>(ctx.reverse);
|
|
387
|
+
return <Link to={reverse("shop")}>Back to Shop</Link>;
|
|
388
|
+
}, { name: "product" })
|
|
389
|
+
|
|
390
|
+
// 6. Client: useHref for mounted paths, href for absolute
|
|
391
|
+
"use client";
|
|
392
|
+
import { useHref, href, Link } from "@rangojs/router/client";
|
|
393
|
+
<Link to={href("/shop/product/widget")}>Widget</Link>
|
|
394
|
+
```
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal Types - Implementation Details
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
* These types are for internal use only and should not be relied upon by consumers.
|
|
6
|
+
* They may change without notice between versions.
|
|
7
|
+
*
|
|
8
|
+
* If you find yourself needing these types, please open an issue to discuss
|
|
9
|
+
* adding them to the public API.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Segment Resolution (Internal)
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @internal
|
|
18
|
+
* Internal representation of a resolved route segment.
|
|
19
|
+
* Used during route matching and rendering pipeline.
|
|
20
|
+
*/
|
|
21
|
+
export type { ResolvedSegment, SegmentMetadata } from "./types.js";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @internal
|
|
25
|
+
* Internal match result from route resolution.
|
|
26
|
+
*/
|
|
27
|
+
export type { MatchResult, SlotState } from "./types.js";
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Intercept System (Internal)
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @internal
|
|
35
|
+
* Context for intercept route selection.
|
|
36
|
+
*/
|
|
37
|
+
export type {
|
|
38
|
+
InterceptSelectorContext,
|
|
39
|
+
InterceptSegmentsState,
|
|
40
|
+
InterceptWhenFn,
|
|
41
|
+
} from "./server/context.js";
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Browser State (Internal)
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @internal
|
|
49
|
+
* RSC protocol payload structure.
|
|
50
|
+
*/
|
|
51
|
+
export type { RscPayload, ReactFormState } from "./rsc/types.js";
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @internal
|
|
55
|
+
* Internal navigation store interface.
|
|
56
|
+
*/
|
|
57
|
+
export type {
|
|
58
|
+
NavigationStore,
|
|
59
|
+
NavigationBridge,
|
|
60
|
+
RscMetadata,
|
|
61
|
+
ActionResult,
|
|
62
|
+
InflightAction,
|
|
63
|
+
NavigationState,
|
|
64
|
+
TrackedActionState,
|
|
65
|
+
ActionStateListener,
|
|
66
|
+
SegmentCache,
|
|
67
|
+
SegmentState,
|
|
68
|
+
NavigationUpdate,
|
|
69
|
+
} from "./browser/types.js";
|
|
70
|
+
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// Handle System (Internal)
|
|
73
|
+
// ============================================================================
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @internal
|
|
77
|
+
* Internal handle storage mechanism.
|
|
78
|
+
*/
|
|
79
|
+
export type { HandleStore, HandleData } from "./server/handle-store.js";
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @internal
|
|
83
|
+
* Segment handle data in cache.
|
|
84
|
+
*/
|
|
85
|
+
export type { SegmentHandleData } from "./cache/types.js";
|
|
86
|
+
|
|
87
|
+
// ============================================================================
|
|
88
|
+
// Cache Internals
|
|
89
|
+
// ============================================================================
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @internal
|
|
93
|
+
* Internal cache entry data structure.
|
|
94
|
+
*/
|
|
95
|
+
export type {
|
|
96
|
+
CachedEntryData,
|
|
97
|
+
CachedEntryResult,
|
|
98
|
+
CacheGetResult,
|
|
99
|
+
SerializedSegmentData,
|
|
100
|
+
CacheDefaults,
|
|
101
|
+
} from "./cache/types.js";
|
|
102
|
+
|
|
103
|
+
// ============================================================================
|
|
104
|
+
// Router Context (Internal)
|
|
105
|
+
// ============================================================================
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @internal
|
|
109
|
+
* Router context for AsyncLocalStorage.
|
|
110
|
+
*/
|
|
111
|
+
export type { RouterContext, RevalidationContext, InterceptResult } from "./router/router-context.js";
|
|
112
|
+
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// Match Pipeline (Internal)
|
|
115
|
+
// ============================================================================
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @internal
|
|
119
|
+
* Route match context during pipeline processing.
|
|
120
|
+
*/
|
|
121
|
+
export type { MatchContext, MatchPipelineState } from "./router/match-context.js";
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @internal
|
|
125
|
+
* Pattern matching result.
|
|
126
|
+
*/
|
|
127
|
+
export type { RouteMatchResult } from "./router/pattern-matching.js";
|
|
128
|
+
|
|
129
|
+
// ============================================================================
|
|
130
|
+
// Server Context (Internal)
|
|
131
|
+
// ============================================================================
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @internal
|
|
135
|
+
* Entry data during route traversal.
|
|
136
|
+
*/
|
|
137
|
+
export type {
|
|
138
|
+
EntryData,
|
|
139
|
+
InterceptEntry,
|
|
140
|
+
MetricsStore,
|
|
141
|
+
PerformanceMetric,
|
|
142
|
+
EntryCacheConfig,
|
|
143
|
+
EntryPropCommon,
|
|
144
|
+
EntryPropDatas,
|
|
145
|
+
LoaderEntry,
|
|
146
|
+
EntryPropSegments,
|
|
147
|
+
} from "./server/context.js";
|
|
148
|
+
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// Handler Context (Internal)
|
|
151
|
+
// ============================================================================
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @internal
|
|
155
|
+
* Internal handler context with additional props for router internals.
|
|
156
|
+
* Includes `_originalRequest` and `_currentSegmentId`.
|
|
157
|
+
*/
|
|
158
|
+
export type { InternalHandlerContext } from "./types.js";
|
|
159
|
+
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// Debug Utilities (Internal)
|
|
162
|
+
// ============================================================================
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @internal
|
|
166
|
+
* Debug utilities for manifest inspection and comparison.
|
|
167
|
+
*/
|
|
168
|
+
export {
|
|
169
|
+
serializeManifest,
|
|
170
|
+
compareManifests,
|
|
171
|
+
formatManifestDiff,
|
|
172
|
+
type SerializedEntry,
|
|
173
|
+
type SerializedManifest,
|
|
174
|
+
} from "./debug.js";
|
|
175
|
+
|
package/src/bin/rango.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { findTsFiles, writePerModuleRouteTypesForFile } from "../build/generate-route-types.ts";
|
|
3
|
+
|
|
4
|
+
const [command, ...args] = process.argv.slice(2);
|
|
5
|
+
|
|
6
|
+
if (command === "extract-names") {
|
|
7
|
+
const dir = args[0] ?? "./src";
|
|
8
|
+
const resolvedDir = resolve(dir);
|
|
9
|
+
console.log(`[rango] Scanning ${resolvedDir} for url modules...`);
|
|
10
|
+
|
|
11
|
+
const files = findTsFiles(resolvedDir);
|
|
12
|
+
for (const filePath of files) {
|
|
13
|
+
writePerModuleRouteTypesForFile(filePath);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
console.log(`[rango] Scanned ${files.length} file(s)`);
|
|
17
|
+
process.exit(0);
|
|
18
|
+
} else {
|
|
19
|
+
console.log(`Usage: rango <command>
|
|
20
|
+
|
|
21
|
+
Commands:
|
|
22
|
+
extract-names [dir] Extract route names from url modules (default: ./src)`);
|
|
23
|
+
process.exit(command ? 1 : 0);
|
|
24
|
+
}
|