@rangojs/router 0.0.0-experimental.0f44aca1
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/AGENTS.md +5 -0
- package/README.md +899 -0
- package/dist/bin/rango.js +1601 -0
- package/dist/vite/index.js +5214 -0
- package/package.json +176 -0
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +220 -0
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +112 -0
- package/skills/document-cache/SKILL.md +182 -0
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +704 -0
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +313 -0
- package/skills/layout/SKILL.md +310 -0
- package/skills/links/SKILL.md +239 -0
- package/skills/loader/SKILL.md +596 -0
- package/skills/middleware/SKILL.md +339 -0
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +305 -0
- package/skills/prerender/SKILL.md +643 -0
- package/skills/rango/SKILL.md +118 -0
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +385 -0
- package/skills/router-setup/SKILL.md +439 -0
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +79 -0
- package/skills/typesafety/SKILL.md +623 -0
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +273 -0
- package/src/bin/rango.ts +321 -0
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +899 -0
- package/src/browser/history-state.ts +80 -0
- package/src/browser/index.ts +18 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +141 -0
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +134 -0
- package/src/browser/navigation-bridge.ts +645 -0
- package/src/browser/navigation-client.ts +215 -0
- package/src/browser/navigation-store.ts +806 -0
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +550 -0
- package/src/browser/prefetch/cache.ts +146 -0
- package/src/browser/prefetch/fetch.ts +135 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +360 -0
- package/src/browser/react/NavigationProvider.tsx +386 -0
- package/src/browser/react/ScrollRestoration.tsx +94 -0
- package/src/browser/react/context.ts +59 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +52 -0
- package/src/browser/react/location-state-shared.ts +162 -0
- package/src/browser/react/location-state.ts +107 -0
- package/src/browser/react/mount-context.ts +37 -0
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +218 -0
- package/src/browser/react/use-client-cache.ts +58 -0
- package/src/browser/react/use-handle.ts +162 -0
- package/src/browser/react/use-href.tsx +40 -0
- package/src/browser/react/use-link-status.ts +135 -0
- package/src/browser/react/use-mount.ts +31 -0
- package/src/browser/react/use-navigation.ts +99 -0
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +63 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +171 -0
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +431 -0
- package/src/browser/scroll-restoration.ts +400 -0
- package/src/browser/segment-reconciler.ts +216 -0
- package/src/browser/segment-structure-assert.ts +83 -0
- package/src/browser/server-action-bridge.ts +667 -0
- package/src/browser/shallow.ts +40 -0
- package/src/browser/types.ts +538 -0
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +438 -0
- package/src/build/generate-route-types.ts +36 -0
- package/src/build/index.ts +35 -0
- package/src/build/route-trie.ts +265 -0
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +411 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +469 -0
- package/src/build/route-types/scan-filter.ts +78 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +338 -0
- package/src/cache/cache-scope.ts +382 -0
- package/src/cache/cf/cf-cache-store.ts +540 -0
- package/src/cache/cf/index.ts +25 -0
- package/src/cache/document-cache.ts +369 -0
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +43 -0
- package/src/cache/memory-segment-store.ts +328 -0
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +98 -0
- package/src/cache/types.ts +342 -0
- package/src/client.rsc.tsx +85 -0
- package/src/client.tsx +601 -0
- package/src/component-utils.ts +76 -0
- package/src/components/DefaultDocument.tsx +27 -0
- package/src/context-var.ts +86 -0
- package/src/debug.ts +243 -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 +365 -0
- package/src/handle.ts +135 -0
- package/src/handles/MetaTags.tsx +246 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +7 -0
- package/src/handles/meta.ts +264 -0
- package/src/host/cookie-handler.ts +165 -0
- package/src/host/errors.ts +97 -0
- package/src/host/index.ts +53 -0
- package/src/host/pattern-matcher.ts +214 -0
- package/src/host/router.ts +352 -0
- package/src/host/testing.ts +79 -0
- package/src/host/types.ts +146 -0
- package/src/host/utils.ts +25 -0
- package/src/href-client.ts +222 -0
- package/src/index.rsc.ts +233 -0
- package/src/index.ts +277 -0
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +89 -0
- package/src/loader.ts +64 -0
- package/src/network-error-thrower.tsx +23 -0
- package/src/outlet-context.ts +15 -0
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +37 -0
- package/src/prerender/store.ts +185 -0
- package/src/prerender.ts +463 -0
- package/src/reverse.ts +330 -0
- package/src/root-error-boundary.tsx +289 -0
- package/src/route-content-wrapper.tsx +196 -0
- package/src/route-definition/dsl-helpers.ts +934 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +430 -0
- package/src/route-definition/index.ts +52 -0
- package/src/route-definition/redirect.ts +93 -0
- package/src/route-definition.ts +1 -0
- package/src/route-map-builder.ts +275 -0
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +259 -0
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +287 -0
- package/src/router/find-match.ts +158 -0
- package/src/router/handler-context.ts +451 -0
- package/src/router/intercept-resolution.ts +395 -0
- package/src/router/lazy-includes.ts +234 -0
- package/src/router/loader-resolution.ts +420 -0
- package/src/router/logging.ts +248 -0
- package/src/router/manifest.ts +267 -0
- package/src/router/match-api.ts +620 -0
- package/src/router/match-context.ts +266 -0
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +223 -0
- package/src/router/match-middleware/cache-lookup.ts +634 -0
- package/src/router/match-middleware/cache-store.ts +295 -0
- package/src/router/match-middleware/index.ts +81 -0
- package/src/router/match-middleware/intercept-resolution.ts +306 -0
- package/src/router/match-middleware/segment-resolution.ts +192 -0
- package/src/router/match-pipelines.ts +179 -0
- package/src/router/match-result.ts +219 -0
- package/src/router/metrics.ts +282 -0
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +748 -0
- package/src/router/pattern-matching.ts +563 -0
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +289 -0
- package/src/router/router-context.ts +316 -0
- package/src/router/router-interfaces.ts +452 -0
- package/src/router/router-options.ts +592 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +570 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +198 -0
- package/src/router/segment-resolution/revalidation.ts +1239 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -0
- package/src/router/segment-wrappers.ts +289 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +239 -0
- package/src/router/types.ts +170 -0
- package/src/router.ts +1002 -0
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +1089 -0
- package/src/rsc/helpers.ts +198 -0
- package/src/rsc/index.ts +36 -0
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +32 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +379 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +235 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +348 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +263 -0
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +454 -0
- package/src/server/context.ts +591 -0
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +308 -0
- package/src/server/loader-registry.ts +133 -0
- package/src/server/request-context.ts +914 -0
- package/src/server/root-layout.tsx +10 -0
- package/src/server/tsconfig.json +14 -0
- package/src/server.ts +51 -0
- package/src/ssr/index.tsx +365 -0
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +297 -0
- package/src/theme/ThemeScript.tsx +61 -0
- package/src/theme/constants.ts +62 -0
- package/src/theme/index.ts +48 -0
- package/src/theme/theme-context.ts +44 -0
- package/src/theme/theme-script.ts +155 -0
- package/src/theme/types.ts +182 -0
- package/src/theme/use-theme.ts +44 -0
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +687 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +183 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +102 -0
- package/src/types/segments.ts +148 -0
- package/src/types.ts +1 -0
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +339 -0
- package/src/urls/path-helper.ts +329 -0
- package/src/urls/pattern-types.ts +95 -0
- package/src/urls/response-types.ts +106 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -0
- package/src/use-loader.tsx +354 -0
- package/src/vite/discovery/bundle-postprocess.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +344 -0
- package/src/vite/discovery/prerender-collection.ts +385 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +110 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +16 -0
- package/src/vite/plugin-types.ts +131 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/plugins/expose-action-id.ts +365 -0
- package/src/vite/plugins/expose-id-utils.ts +287 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +569 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +254 -0
- package/src/vite/plugins/version.d.ts +12 -0
- package/src/vite/plugins/virtual-entries.ts +123 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +510 -0
- package/src/vite/router-discovery.ts +785 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/utils/package-resolution.ts +121 -0
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: route
|
|
3
|
+
description: Define routes with path() in @rangojs/router
|
|
4
|
+
argument-hint: [pattern]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Defining Routes with path()
|
|
8
|
+
|
|
9
|
+
## Basic Route
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { urls } from "@rangojs/router";
|
|
13
|
+
|
|
14
|
+
export const urlpatterns = urls(({ path }) => [
|
|
15
|
+
path("/", HomePage, { name: "home" }),
|
|
16
|
+
path("/about", AboutPage, { name: "about" }),
|
|
17
|
+
path("/contact", ContactPage, { name: "contact" }),
|
|
18
|
+
]);
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Route with Parameters
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
urls(({ path }) => [
|
|
25
|
+
// Single parameter
|
|
26
|
+
path("/product/:slug", ProductPage, { name: "product" }),
|
|
27
|
+
|
|
28
|
+
// Multiple parameters
|
|
29
|
+
path("/blog/:year/:month/:slug", BlogPostPage, { name: "blogPost" }),
|
|
30
|
+
|
|
31
|
+
// Optional parameter (add ? suffix)
|
|
32
|
+
path("/search/:query?", SearchPage, { name: "search" }),
|
|
33
|
+
]);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Route Handler Patterns
|
|
37
|
+
|
|
38
|
+
### Component Function
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
path("/about", AboutPage, { name: "about" })
|
|
42
|
+
|
|
43
|
+
// AboutPage receives context
|
|
44
|
+
function AboutPage(ctx: HandlerContext) {
|
|
45
|
+
return <div>About Us</div>;
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Inline JSX
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
path("/about", () => <AboutPage />, { name: "about" })
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Handler with Context Access
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
path("/product/:slug", (ctx) => {
|
|
59
|
+
const { slug } = ctx.params;
|
|
60
|
+
return <ProductPage slug={slug} />;
|
|
61
|
+
}, { name: "product" })
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Async Handler (Streaming)
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
path("/product/:slug", async (ctx) => {
|
|
68
|
+
const product = await fetchProduct(ctx.params.slug);
|
|
69
|
+
return <ProductPage product={product} />;
|
|
70
|
+
}, { name: "product" })
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Route Options
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
path("/product/:slug", ProductPage, {
|
|
77
|
+
name: "product", // Route name for href() and navigation
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Typed Search Params
|
|
82
|
+
|
|
83
|
+
Add a `search` schema to get typed `ctx.search`:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
path("/search", SearchPage, {
|
|
87
|
+
name: "search",
|
|
88
|
+
search: { q: "string", page: "number?", sort: "string?" },
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Use `Handler<"name">` for typed search params (resolves from the generated route map automatically):
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import type { Handler } from "@rangojs/router";
|
|
96
|
+
|
|
97
|
+
export const SearchPage: Handler<"search"> = (ctx) => {
|
|
98
|
+
// ctx.search is typed: { q: string; page?: number; sort?: string }
|
|
99
|
+
const { q, page, sort } = ctx.search;
|
|
100
|
+
// ctx.searchParams is always URLSearchParams
|
|
101
|
+
return <SearchResults q={q} page={page} sort={sort} />;
|
|
102
|
+
};
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Supported types: `"string"`, `"number"`, `"boolean"`, with `?` suffix for optional.
|
|
106
|
+
Missing params are `undefined` regardless of required/optional. The required/optional
|
|
107
|
+
distinction is a consumer-facing contract (for `href()` and `reverse()` autocomplete).
|
|
108
|
+
|
|
109
|
+
Use `RouteSearchParams<"name">` and `RouteParams<"name">` to extract types for props:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import type { RouteSearchParams, RouteParams } from "@rangojs/router";
|
|
113
|
+
|
|
114
|
+
type SP = RouteSearchParams<"search">; // { q: string; page?: number; sort?: string }
|
|
115
|
+
type P = RouteParams<"blogPost">; // { slug: string }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Route Children
|
|
119
|
+
|
|
120
|
+
Add loaders, loading states, and other features as children:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
path("/product/:slug", ProductPage, { name: "product" }, () => [
|
|
124
|
+
loader(ProductLoader),
|
|
125
|
+
loading(<ProductSkeleton />),
|
|
126
|
+
revalidate(productRevalidation),
|
|
127
|
+
])
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Handler Data Ownership
|
|
131
|
+
|
|
132
|
+
When a route has children (orphan layouts, parallels), the handler executes
|
|
133
|
+
first. Use `ctx.set(key, value)` to share data with children, who read it
|
|
134
|
+
via `ctx.get(key)`. Caching wraps all segments together, so either all run
|
|
135
|
+
or none do.
|
|
136
|
+
|
|
137
|
+
### Typed context variables with createVar
|
|
138
|
+
|
|
139
|
+
Use `createVar<T>()` to create a typed token for `ctx.set()`/`ctx.get()`.
|
|
140
|
+
The token is imported by both the handler (producer) and layout (consumer),
|
|
141
|
+
making the data contract explicit and compile-time verified:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { createVar } from "@rangojs/router";
|
|
145
|
+
import { Outlet, ParallelOutlet } from "@rangojs/router/client";
|
|
146
|
+
|
|
147
|
+
// Typed token -- shared between handler and layout
|
|
148
|
+
interface DashboardData {
|
|
149
|
+
title: string;
|
|
150
|
+
stats: { views: number };
|
|
151
|
+
}
|
|
152
|
+
const Dashboard = createVar<DashboardData>();
|
|
153
|
+
|
|
154
|
+
path("/dashboard/:id", async (ctx) => {
|
|
155
|
+
const data = await fetchDashboard(ctx.params.id);
|
|
156
|
+
ctx.set(Dashboard, data); // type-checked
|
|
157
|
+
return <DashboardPage data={data} />;
|
|
158
|
+
}, { name: "dashboard" }, () => [
|
|
159
|
+
layout((ctx) => {
|
|
160
|
+
const data = ctx.get(Dashboard); // typed as DashboardData | undefined
|
|
161
|
+
return (
|
|
162
|
+
<div>
|
|
163
|
+
<h1>{data?.title}</h1>
|
|
164
|
+
<Outlet />
|
|
165
|
+
<ParallelOutlet name="@sidebar" />
|
|
166
|
+
</div>
|
|
167
|
+
);
|
|
168
|
+
}),
|
|
169
|
+
parallel({
|
|
170
|
+
"@sidebar": (ctx) => {
|
|
171
|
+
const data = ctx.get(Dashboard);
|
|
172
|
+
return <Sidebar stats={data?.stats} />;
|
|
173
|
+
},
|
|
174
|
+
}),
|
|
175
|
+
])
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
String keys still work (`ctx.set("key", value)` / `ctx.get("key")`), but
|
|
179
|
+
`createVar<T>()` is preferred for type safety.
|
|
180
|
+
|
|
181
|
+
Only route handlers and middleware can call `ctx.set()`. Layouts, parallels,
|
|
182
|
+
and intercepts can only read via `ctx.get()`.
|
|
183
|
+
|
|
184
|
+
### Revalidation Contracts for Handler Data
|
|
185
|
+
|
|
186
|
+
Handler-first guarantees apply within a single full render pass. For partial
|
|
187
|
+
action revalidation, define named revalidation contracts and reuse them on both
|
|
188
|
+
the producer route and the consumer child segments.
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// revalidation-contracts.ts
|
|
192
|
+
export const revalidateCheckoutData = ({ actionId }) =>
|
|
193
|
+
actionId?.includes("src/actions/checkout.ts#") ?? false;
|
|
194
|
+
|
|
195
|
+
path("/checkout", CheckoutPage, { name: "checkout" }, () => [
|
|
196
|
+
revalidate(revalidateCheckoutData), // producer (route handler) reruns
|
|
197
|
+
layout(CheckoutLayout, () => [
|
|
198
|
+
revalidate(revalidateCheckoutData), // consumer reruns
|
|
199
|
+
parallel({ "@summary": CheckoutSummary }, () => [
|
|
200
|
+
revalidate(revalidateCheckoutData),
|
|
201
|
+
]),
|
|
202
|
+
]),
|
|
203
|
+
]);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
If children depend on multiple upstream domains, compose multiple contracts on
|
|
207
|
+
the same segment (`revalidateAuthData`, `revalidateCheckoutData`, and so on).
|
|
208
|
+
|
|
209
|
+
For cleaner route trees, expose contract helpers and spread them:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
import { revalidate } from "@rangojs/router";
|
|
213
|
+
|
|
214
|
+
export const revalidateCheckout = () => [revalidate(revalidateCheckoutData)];
|
|
215
|
+
|
|
216
|
+
path("/checkout", CheckoutPage, { name: "checkout" }, () => [
|
|
217
|
+
revalidateCheckout(),
|
|
218
|
+
layout(CheckoutLayout, () => [revalidateCheckout()]),
|
|
219
|
+
]);
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
For scope/revalidation guarantees and non-guarantees, see:
|
|
223
|
+
[docs/execution-model.md](../../docs/internal/execution-model.md)
|
|
224
|
+
|
|
225
|
+
## Redirects
|
|
226
|
+
|
|
227
|
+
### Basic redirect
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import { redirect } from "@rangojs/router";
|
|
231
|
+
|
|
232
|
+
path("/old-page", () => redirect("/new-page"), { name: "oldPage" });
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Redirect with custom status
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
path("/moved", () => redirect("/new-location", 301), { name: "moved" });
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Redirect with location state
|
|
242
|
+
|
|
243
|
+
Carry typed state through redirects (e.g. flash messages):
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { redirect, createLocationState } from "@rangojs/router";
|
|
247
|
+
|
|
248
|
+
export const FlashMessage = createLocationState<{ text: string }>({
|
|
249
|
+
flash: true,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
path(
|
|
253
|
+
"/save",
|
|
254
|
+
(ctx) => {
|
|
255
|
+
// ... save logic
|
|
256
|
+
return redirect("/dashboard", {
|
|
257
|
+
state: [FlashMessage({ text: "Item saved!" })],
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
{ name: "save" },
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
// With custom status + state
|
|
264
|
+
path(
|
|
265
|
+
"/action",
|
|
266
|
+
(ctx) => {
|
|
267
|
+
return redirect("/target", {
|
|
268
|
+
status: 303,
|
|
269
|
+
state: [FlashMessage({ text: "Action complete" })],
|
|
270
|
+
});
|
|
271
|
+
},
|
|
272
|
+
{ name: "action" },
|
|
273
|
+
);
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Read the state on the target page with `useLocationState(FlashMessage)`. The
|
|
277
|
+
`{ flash: true }` option makes it auto-clear. Without `{ flash: true }`,
|
|
278
|
+
state persists on back/forward. See `/hooks` for details.
|
|
279
|
+
|
|
280
|
+
### ctx.setLocationState()
|
|
281
|
+
|
|
282
|
+
Attach location state to any server response (not just redirects):
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
path("/dashboard", (ctx) => {
|
|
286
|
+
ctx.setLocationState(ServerInfo({ data: "welcome" }));
|
|
287
|
+
return <Dashboard />;
|
|
288
|
+
}, { name: "dashboard" })
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
State flows to the browser via the RSC payload and is merged into
|
|
292
|
+
`history.pushState()`. Only works for SPA (partial) navigations.
|
|
293
|
+
|
|
294
|
+
## Handler Context
|
|
295
|
+
|
|
296
|
+
Every handler receives a context object:
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
interface HandlerContext<TParams = {}, TEnv = DefaultEnv, TSearch = {}> {
|
|
300
|
+
params: TParams; // URL parameters
|
|
301
|
+
request: Request; // Original request
|
|
302
|
+
searchParams: URLSearchParams; // Query params (always URLSearchParams)
|
|
303
|
+
search: {} | ResolveSearchSchema<TSearch>; // Typed search params (from search schema)
|
|
304
|
+
url: URL; // Parsed URL
|
|
305
|
+
env: TEnv; // Environment (bindings + variables)
|
|
306
|
+
set(key: string, value: any): void; // Set context variable (untyped string key)
|
|
307
|
+
set<T>(contextVar: ContextVar<T>, value: T): void; // Set typed context variable
|
|
308
|
+
get(key: string): any; // Read context variable (untyped string key)
|
|
309
|
+
get<T>(contextVar: ContextVar<T>): T | undefined; // Read typed context variable
|
|
310
|
+
use<T>(handle: Handle<T>): T; // Access handles
|
|
311
|
+
reverse(
|
|
312
|
+
name: string,
|
|
313
|
+
params?: Record<string, string>,
|
|
314
|
+
search?: Record<string, unknown>,
|
|
315
|
+
): string; // URL generation
|
|
316
|
+
setLocationState(entries: LocationStateEntry[]): void; // Attach state to response
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Using Context
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
path("/product/:slug", (ctx) => {
|
|
324
|
+
// Access URL params
|
|
325
|
+
const { slug } = ctx.params;
|
|
326
|
+
|
|
327
|
+
// Access query params (untyped - use search schema for typed access)
|
|
328
|
+
const tab = ctx.searchParams.get("tab");
|
|
329
|
+
|
|
330
|
+
// Access platform bindings
|
|
331
|
+
const db = ctx.env.DB;
|
|
332
|
+
|
|
333
|
+
// Access handles
|
|
334
|
+
const breadcrumbs = ctx.use(Breadcrumbs);
|
|
335
|
+
breadcrumbs.push({ label: "Product", href: `/product/${slug}` });
|
|
336
|
+
|
|
337
|
+
return <ProductPage slug={slug} tab={tab} />;
|
|
338
|
+
}, { name: "product" })
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Nested Routes
|
|
342
|
+
|
|
343
|
+
Use layouts to nest routes:
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
urls(({ path, layout }) => [
|
|
347
|
+
layout(<ShopLayout />, () => [
|
|
348
|
+
path("/shop", ShopIndex, { name: "shop.index" }),
|
|
349
|
+
path("/shop/cart", CartPage, { name: "shop.cart" }),
|
|
350
|
+
path("/shop/product/:slug", ProductPage, { name: "shop.product" }),
|
|
351
|
+
]),
|
|
352
|
+
])
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Complete Example
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
import { urls, Breadcrumbs } from "@rangojs/router";
|
|
359
|
+
|
|
360
|
+
export const urlpatterns = urls(({ path, layout, loader, loading }) => [
|
|
361
|
+
// Simple route
|
|
362
|
+
path("/", HomePage, { name: "home" }),
|
|
363
|
+
|
|
364
|
+
// Route with loader
|
|
365
|
+
path("/about", AboutPage, { name: "about" }, () => [
|
|
366
|
+
loader(TeamLoader),
|
|
367
|
+
]),
|
|
368
|
+
|
|
369
|
+
// Dynamic route with handler
|
|
370
|
+
path("/product/:slug", (ctx) => {
|
|
371
|
+
const push = ctx.use(Breadcrumbs);
|
|
372
|
+
push({ label: ctx.params.slug, href: `/product/${ctx.params.slug}` });
|
|
373
|
+
return <ProductPage slug={ctx.params.slug} />;
|
|
374
|
+
}, { name: "product" }, () => [
|
|
375
|
+
loader(ProductLoader),
|
|
376
|
+
loading(<ProductSkeleton />, { ssr: true }),
|
|
377
|
+
]),
|
|
378
|
+
|
|
379
|
+
// Nested routes in layout
|
|
380
|
+
layout(<BlogLayout />, () => [
|
|
381
|
+
path("/blog", BlogIndex, { name: "blog.index" }),
|
|
382
|
+
path("/blog/:slug", BlogPost, { name: "blog.post" }),
|
|
383
|
+
]),
|
|
384
|
+
]);
|
|
385
|
+
```
|