@rangojs/router 0.0.0-experimental.66 → 0.0.0-experimental.66cdebe3
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/README.md +112 -17
- package/dist/vite/index.js +1462 -422
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +7 -5
- package/skills/breadcrumbs/SKILL.md +3 -1
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +54 -20
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +45 -0
- package/skills/layout/SKILL.md +24 -0
- package/skills/links/SKILL.md +234 -16
- package/skills/loader/SKILL.md +70 -3
- package/skills/middleware/SKILL.md +34 -3
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/parallel/SKILL.md +68 -0
- package/skills/rango/SKILL.md +26 -22
- package/skills/response-routes/SKILL.md +8 -0
- package/skills/route/SKILL.md +48 -0
- package/skills/server-actions/SKILL.md +739 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/typesafety/SKILL.md +9 -1
- package/skills/view-transitions/SKILL.md +212 -0
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/event-controller.ts +44 -4
- package/src/browser/navigation-bridge.ts +151 -9
- package/src/browser/navigation-client.ts +64 -13
- package/src/browser/navigation-store.ts +25 -1
- package/src/browser/partial-update.ts +58 -12
- package/src/browser/prefetch/cache.ts +129 -21
- package/src/browser/prefetch/fetch.ts +148 -16
- package/src/browser/prefetch/queue.ts +36 -5
- package/src/browser/rango-state.ts +53 -13
- package/src/browser/react/Link.tsx +30 -2
- package/src/browser/react/NavigationProvider.tsx +95 -44
- package/src/browser/react/filter-segment-order.ts +51 -7
- package/src/browser/react/index.ts +3 -0
- package/src/browser/react/use-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +17 -4
- package/src/browser/react/use-reverse.ts +99 -0
- package/src/browser/react/use-router.ts +8 -1
- package/src/browser/react/use-segments.ts +11 -8
- package/src/browser/rsc-router.tsx +34 -6
- package/src/browser/scroll-restoration.ts +69 -28
- package/src/browser/segment-reconciler.ts +36 -14
- package/src/browser/types.ts +19 -0
- package/src/build/route-trie.ts +52 -25
- package/src/cache/cf/cf-cache-store.ts +5 -7
- package/src/client.rsc.tsx +3 -0
- package/src/client.tsx +87 -175
- package/src/href-client.ts +4 -1
- package/src/index.rsc.ts +3 -0
- package/src/index.ts +44 -9
- package/src/outlet-context.ts +1 -1
- package/src/response-utils.ts +28 -0
- package/src/reverse.ts +62 -36
- package/src/route-definition/dsl-helpers.ts +175 -23
- package/src/route-definition/helpers-types.ts +63 -14
- package/src/route-definition/resolve-handler-use.ts +6 -0
- package/src/route-types.ts +7 -0
- package/src/router/handler-context.ts +21 -38
- package/src/router/lazy-includes.ts +6 -6
- package/src/router/loader-resolution.ts +3 -0
- package/src/router/manifest.ts +22 -13
- package/src/router/match-api.ts +4 -3
- package/src/router/match-handlers.ts +1 -0
- package/src/router/match-middleware/cache-lookup.ts +2 -1
- package/src/router/match-result.ts +101 -4
- package/src/router/middleware-types.ts +14 -25
- package/src/router/middleware.ts +54 -7
- package/src/router/pattern-matching.ts +101 -17
- package/src/router/revalidation.ts +15 -1
- package/src/router/segment-resolution/fresh.ts +13 -0
- package/src/router/segment-resolution/revalidation.ts +135 -101
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/trie-matching.ts +18 -13
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +1 -2
- package/src/rsc/handler.ts +16 -8
- package/src/rsc/helpers.ts +69 -41
- package/src/rsc/progressive-enhancement.ts +4 -0
- package/src/rsc/response-route-handler.ts +14 -1
- package/src/rsc/rsc-rendering.ts +10 -0
- package/src/rsc/server-action.ts +4 -0
- package/src/rsc/types.ts +6 -0
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +71 -70
- package/src/server/context.ts +26 -3
- package/src/server/request-context.ts +10 -42
- package/src/ssr/index.tsx +5 -1
- package/src/types/handler-context.ts +12 -39
- package/src/types/loader-types.ts +5 -6
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-entry.ts +11 -0
- package/src/types/segments.ts +18 -1
- package/src/urls/include-helper.ts +24 -14
- package/src/urls/path-helper-types.ts +30 -4
- package/src/urls/response-types.ts +2 -10
- package/src/use-loader.tsx +4 -1
- package/src/vite/debug.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +31 -3
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +172 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/plugins/cjs-to-esm.ts +5 -0
- package/src/vite/plugins/client-ref-dedup.ts +16 -0
- package/src/vite/plugins/client-ref-hashing.ts +16 -4
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/plugins/expose-action-id.ts +52 -28
- package/src/vite/plugins/expose-id-utils.ts +12 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
- package/src/vite/plugins/expose-internal-ids.ts +545 -304
- package/src/vite/plugins/performance-tracks.ts +17 -9
- package/src/vite/plugins/use-cache-transform.ts +56 -43
- package/src/vite/plugins/version-injector.ts +37 -11
- package/src/vite/rango.ts +49 -14
- package/src/vite/router-discovery.ts +558 -53
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/package-resolution.ts +41 -1
- package/src/vite/utils/prerender-utils.ts +21 -6
package/skills/parallel/SKILL.md
CHANGED
|
@@ -206,6 +206,67 @@ parallel(
|
|
|
206
206
|
)
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
+
## Composable Slots via `handler.use`
|
|
210
|
+
|
|
211
|
+
Slot handlers can carry their own loader, loading, error/notFound boundaries, revalidation, and transition defaults via `.use`. The mount site then declares **just the slot names** — no per-call data wiring.
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
const CartSummary: Handler = async (ctx) => {
|
|
215
|
+
const cart = await ctx.use(CartLoader);
|
|
216
|
+
return <CartSummaryView cart={cart} />;
|
|
217
|
+
};
|
|
218
|
+
CartSummary.use = () => [
|
|
219
|
+
loader(CartLoader),
|
|
220
|
+
loading(<CartSkeleton />),
|
|
221
|
+
revalidate(revalidateCartData),
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
// Same slot, no copy-pasted plumbing across layouts.
|
|
225
|
+
layout(<DashboardLayout />, () => [
|
|
226
|
+
parallel({ "@cart": CartSummary }),
|
|
227
|
+
path("/dashboard", DashboardIndex, { name: "dashboard.index" }),
|
|
228
|
+
]);
|
|
229
|
+
|
|
230
|
+
layout(<AccountLayout />, () => [
|
|
231
|
+
parallel({ "@cart": CartSummary }),
|
|
232
|
+
path("/account", AccountIndex, { name: "account.index" }),
|
|
233
|
+
]);
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
A slot's `loading()` (whether from `handler.use` or explicit) makes that slot an independent streaming unit, exactly as in the **Streaming Behavior** section above.
|
|
237
|
+
|
|
238
|
+
The `parallel` mount site has the narrowest allow-list for `handler.use` items — slots cannot bring their own middleware or layout, only `revalidate`, `loader`, `loading`, `errorBoundary`, `notFoundBoundary`, and `transition`. See [skills/handler-use](../handler-use/SKILL.md) for the full table and merge rules.
|
|
239
|
+
|
|
240
|
+
`transition` is allowed in the slot allow-list, but slot-level rendering does **not** currently apply a `<ViewTransition>` wrapper — only the layout/route wraps take effect at render time. For a modal-only morph today, use an element-level React `<ViewTransition>` inside the slot's component. The reverse direction is the useful guarantee: a layout-level `transition()` fires when the layout's default outlet content changes but **not** when a `<ParallelOutlet />` mounts new content (modal opens are not subtree updates of the layout VT). See [skills/view-transitions](../view-transitions/SKILL.md) for the wrap rules and the intercept caveat.
|
|
241
|
+
|
|
242
|
+
### Two scopes for explicit `use`: shared (broadcast) and slot-local
|
|
243
|
+
|
|
244
|
+
`parallel({...slots}, () => [...use])` runs the shared `use()` callback **once per slot** ([dsl-helpers.ts](../../src/route-definition/dsl-helpers.ts)) — items in that callback land on every slot's entry. That's the right behavior for the items the parallel allow-list permits and that accumulate (`loader`, `revalidate`, `errorBoundary`, `notFoundBoundary`, `transition`). (Slots cannot bring `middleware` or `layout` — see the allowed-types note above.)
|
|
245
|
+
|
|
246
|
+
For single-assignment items like `loading()`, broadcasting overwrites every slot's `handler.use` default. Pass a **slot descriptor** `{ handler, use }` instead — items in the descriptor's `use` apply only to that slot:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
// @cart gets a custom skeleton; @notifs keeps its handler.use default.
|
|
250
|
+
parallel({
|
|
251
|
+
"@cart": {
|
|
252
|
+
handler: Cart,
|
|
253
|
+
use: () => [loading(<CustomCartSkeleton />)],
|
|
254
|
+
},
|
|
255
|
+
"@notifs": Notifs,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Opt one slot out of streaming while siblings still stream the broadcast.
|
|
259
|
+
parallel(
|
|
260
|
+
{
|
|
261
|
+
"@cart": { handler: Cart, use: () => [loading(false)] },
|
|
262
|
+
"@notifs": Notifs,
|
|
263
|
+
},
|
|
264
|
+
() => [loading(<BroadcastSkeleton />)],
|
|
265
|
+
);
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Per-slot merge order is **handler.use → shared use → slot-local use**. Slot-local is the narrowest scope, so it wins for last-write-wins items. See [skills/handler-use § `loading()` is a single-assignment item — scope it correctly](../handler-use/SKILL.md#loading-is-a-single-assignment-item--scope-it-correctly) for the full reasoning.
|
|
269
|
+
|
|
209
270
|
## Slot Override Semantics
|
|
210
271
|
|
|
211
272
|
When multiple `parallel()` calls define the same slot name, **the last
|
|
@@ -288,6 +349,13 @@ Revalidating only the parallel does not re-run outer handlers/layouts.
|
|
|
288
349
|
If the slot reads `ctx.get()` data established above it, opt the outer
|
|
289
350
|
segment into revalidation as well.
|
|
290
351
|
|
|
352
|
+
A `revalidate()` callback may return a hard `boolean`, a soft
|
|
353
|
+
`{ defaultShouldRevalidate }` object, or nothing (`void` / `null` /
|
|
354
|
+
`undefined`) to defer to the next revalidator. See
|
|
355
|
+
[loader/SKILL.md#revalidate-return-shapes](../loader/SKILL.md#revalidate-return-shapes)
|
|
356
|
+
for the full contract — it's the same across `loader()`, `path()`,
|
|
357
|
+
`layout()`, `parallel()`, and `intercept()`.
|
|
358
|
+
|
|
291
359
|
### Revalidation Contracts for Parallel Dependencies
|
|
292
360
|
|
|
293
361
|
Prefer named revalidation contracts shared by both the upstream producer and
|
package/skills/rango/SKILL.md
CHANGED
|
@@ -10,28 +10,32 @@ Django-inspired RSC router with composable URL patterns, type-safe href, and ser
|
|
|
10
10
|
|
|
11
11
|
## Skills
|
|
12
12
|
|
|
13
|
-
| Skill
|
|
14
|
-
|
|
|
15
|
-
| `/router-setup`
|
|
16
|
-
| `/route`
|
|
17
|
-
| `/layout`
|
|
18
|
-
| `/loader`
|
|
19
|
-
| `/
|
|
20
|
-
| `/
|
|
21
|
-
| `/
|
|
22
|
-
| `/
|
|
23
|
-
| `/
|
|
24
|
-
| `/
|
|
25
|
-
| `/
|
|
26
|
-
| `/
|
|
27
|
-
| `/
|
|
28
|
-
| `/
|
|
29
|
-
| `/
|
|
30
|
-
| `/
|
|
31
|
-
| `/
|
|
32
|
-
| `/
|
|
33
|
-
| `/
|
|
34
|
-
| `/
|
|
13
|
+
| Skill | Description |
|
|
14
|
+
| ----------------------- | -------------------------------------------------------------------------- |
|
|
15
|
+
| `/router-setup` | Create and configure the RSC router |
|
|
16
|
+
| `/route` | Define routes with `urls()` and `path()` |
|
|
17
|
+
| `/layout` | Layouts that wrap child routes |
|
|
18
|
+
| `/loader` | Data loaders with `createLoader()` |
|
|
19
|
+
| `/server-actions` | Mutations with `"use server"`, useActionState, validation, revalidation |
|
|
20
|
+
| `/i18n` | Locale routing with `:locale?`, resolution chains, react-intl integration |
|
|
21
|
+
| `/middleware` | Request processing and authentication |
|
|
22
|
+
| `/intercept` | Modal/slide-over patterns for soft navigation |
|
|
23
|
+
| `/parallel` | Multi-column layouts and sidebars |
|
|
24
|
+
| `/caching` | Segment caching with memory or KV stores |
|
|
25
|
+
| `/use-cache` | Function-level caching with `"use cache"` directive |
|
|
26
|
+
| `/cache-guide` | When to use `cache()` vs `"use cache"` — differences and decision guide |
|
|
27
|
+
| `/document-cache` | Edge caching with Cache-Control headers |
|
|
28
|
+
| `/theme` | Light/dark mode with FOUC prevention |
|
|
29
|
+
| `/links` | URL generation: ctx.reverse, href, useHref, useMount, scopedReverse |
|
|
30
|
+
| `/hooks` | Client-side React hooks |
|
|
31
|
+
| `/typesafety` | Type-safe routes, params, href, and environment |
|
|
32
|
+
| `/host-router` | Multi-app host routing with domain/subdomain patterns |
|
|
33
|
+
| `/tailwind` | Set up Tailwind CSS v4 with `?url` imports |
|
|
34
|
+
| `/response-routes` | JSON/text/HTML/XML/stream endpoints with `path.json()`, `path.text()` |
|
|
35
|
+
| `/mime-routes` | Content negotiation — same URL, different response types via Accept header |
|
|
36
|
+
| `/fonts` | Load web fonts with preload hints |
|
|
37
|
+
| `/migrate-nextjs` | Migrate a Next.js App Router project to Rango |
|
|
38
|
+
| `/migrate-react-router` | Migrate a React Router / Remix project to Rango |
|
|
35
39
|
|
|
36
40
|
## Quick Start
|
|
37
41
|
|
|
@@ -400,6 +400,14 @@ path(
|
|
|
400
400
|
Multiple response types can share the same URL pattern. See `/mime-routes` for the
|
|
401
401
|
full content negotiation API (Accept header matching, Vary: Accept, multi-variant routes).
|
|
402
402
|
|
|
403
|
+
## Long-Lived Responses (SSE / WebSocket)
|
|
404
|
+
|
|
405
|
+
For Server-Sent Events (`path.stream`) and WebSocket upgrades (`path.any`
|
|
406
|
+
returning a 101 / `webSocket` Response), see `/streams-and-websockets`.
|
|
407
|
+
Upgrade responses flow through without reconstruction; `Vary` and
|
|
408
|
+
`Server-Timing` are skipped, and stub headers are applied in place on a
|
|
409
|
+
best-effort basis.
|
|
410
|
+
|
|
403
411
|
## How It Works
|
|
404
412
|
|
|
405
413
|
1. `path.json()` tags the route at the trie level with a MIME type
|
package/skills/route/SKILL.md
CHANGED
|
@@ -33,6 +33,26 @@ urls(({ path }) => [
|
|
|
33
33
|
]);
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
### Optional URL params at runtime
|
|
37
|
+
|
|
38
|
+
Absent optional params are **omitted from `ctx.params`** — `ctx.params.<name>`
|
|
39
|
+
reads as `undefined`, matching the `RouteParams<"name">` type
|
|
40
|
+
(`{ query?: string }`). Use `??` to default and `=== undefined` to check
|
|
41
|
+
absence:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
path("/search/:query?", (ctx) => {
|
|
45
|
+
const query = ctx.params.query ?? ""; // works — undefined coalesces
|
|
46
|
+
if (ctx.params.query === undefined) return <EmptySearch />;
|
|
47
|
+
return <Results query={ctx.params.query} />;
|
|
48
|
+
}, { name: "search" });
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
For the common pattern of an optional locale prefix
|
|
52
|
+
(`include("/:locale?", routes)`) and the wider react-intl integration —
|
|
53
|
+
locale detection, fallback chains, URL generation with absent locale —
|
|
54
|
+
see `/i18n`.
|
|
55
|
+
|
|
36
56
|
## Route Handler Patterns
|
|
37
57
|
|
|
38
58
|
### Component Function
|
|
@@ -383,6 +403,34 @@ urls(({ path, layout }) => [
|
|
|
383
403
|
])
|
|
384
404
|
```
|
|
385
405
|
|
|
406
|
+
## View Transitions
|
|
407
|
+
|
|
408
|
+
A route can configure its own `transition()` — the wrap goes around the route's component itself (routes are leaves; they have no separate default outlet channel). If the route component renders a `<ParallelOutlet />` directly, that slot remains inside the route's VT subtree, so prefer mounting parallel slots in a layout when combining intercept modals with route-level transitions. See [skills/view-transitions](../view-transitions/SKILL.md) for examples and the wrap-location rules across layouts, routes, and slots.
|
|
409
|
+
|
|
410
|
+
## Handler-attached `.use`
|
|
411
|
+
|
|
412
|
+
Page handlers can carry their own loader, middleware, error boundaries, parallels, and other defaults via a `.use` callback — so the page is self-contained and reusable across mount sites without re-wiring the same items.
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
const ProductPage: Handler<"/product/:slug"> = async (ctx) => {
|
|
416
|
+
const product = await ctx.use(ProductLoader);
|
|
417
|
+
return <ProductView product={product} />;
|
|
418
|
+
};
|
|
419
|
+
ProductPage.use = () => [
|
|
420
|
+
loader(ProductLoader),
|
|
421
|
+
loading(<ProductSkeleton />),
|
|
422
|
+
middleware(async (ctx, next) => {
|
|
423
|
+
await next();
|
|
424
|
+
ctx.header("Cache-Control", "private, max-age=60");
|
|
425
|
+
}),
|
|
426
|
+
];
|
|
427
|
+
|
|
428
|
+
// Mount site has no per-page wiring — defaults travel with the handler.
|
|
429
|
+
path("/product/:slug", ProductPage, { name: "product" });
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Explicit `use()` at the mount site merges with `handler.use` (handler defaults first, explicit second). See [skills/handler-use](../handler-use/SKILL.md) for the merge order, allowed item types per mount site, and override semantics.
|
|
433
|
+
|
|
386
434
|
## Complete Example
|
|
387
435
|
|
|
388
436
|
```typescript
|