@rangojs/router 0.16.0 → 0.18.0
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 +255 -89
- package/dist/testing/vitest.js +252 -0
- package/dist/types/browser/cookie-name.d.ts +6 -0
- package/dist/types/browser/history-state.d.ts +16 -0
- package/dist/types/browser/navigation-transaction.d.ts +6 -0
- package/dist/types/browser/partial-update.d.ts +11 -0
- package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
- package/dist/types/browser/rsc-router.d.ts +1 -1
- package/dist/types/browser/scroll-restoration.d.ts +7 -0
- package/dist/types/cache/cache-exec-scope.d.ts +18 -1
- package/dist/types/cache/cache-key-utils.d.ts +17 -2
- package/dist/types/cache/cache-policy.d.ts +17 -0
- package/dist/types/cache/cache-scope.d.ts +132 -19
- package/dist/types/cache/cache-tag.d.ts +92 -7
- package/dist/types/cache/cf/cf-base64.d.ts +6 -0
- package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
- package/dist/types/cache/cf/cf-cache-store.d.ts +185 -44
- package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
- package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +34 -0
- package/dist/types/cache/handle-capture.d.ts +41 -3
- package/dist/types/cache/handle-snapshot.d.ts +54 -13
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/segment-codec.d.ts +10 -3
- package/dist/types/cache/shell-memo.d.ts +178 -0
- package/dist/types/cache/shell-snapshot.d.ts +110 -21
- package/dist/types/cache/tag-invalidation.d.ts +30 -3
- package/dist/types/cache/taint.d.ts +1 -1
- package/dist/types/cache/types.d.ts +220 -44
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
- package/dist/types/client.d.ts +4 -4
- package/dist/types/client.rsc.d.ts +4 -5
- package/dist/types/components/DefaultDocument.d.ts +5 -2
- package/dist/types/context-var.d.ts +6 -5
- package/dist/types/escape-script.d.ts +1 -1
- package/dist/types/handles/MetaTags.d.ts +5 -4
- package/dist/types/handles/Scripts.d.ts +8 -7
- package/dist/types/handles/meta.d.ts +2 -2
- package/dist/types/handles/script.d.ts +17 -16
- package/dist/types/href-client.d.ts +1 -1
- package/dist/types/html.d.ts +12 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +3 -2
- package/dist/types/loader-store.d.ts +7 -4
- package/dist/types/prerender/build-shell-capture.d.ts +11 -4
- package/dist/types/reverse.d.ts +36 -4
- package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
- package/dist/types/route-definition/helpers-types.d.ts +33 -9
- package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
- package/dist/types/route-types.d.ts +1 -1
- package/dist/types/router/debug-manifest.d.ts +3 -2
- package/dist/types/router/error-handling.d.ts +20 -0
- package/dist/types/router/handler-context.d.ts +6 -0
- package/dist/types/router/instrument.d.ts +8 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
- package/dist/types/router/middleware-types.d.ts +9 -3
- package/dist/types/router/router-context.d.ts +3 -1
- package/dist/types/router/router-interfaces.d.ts +24 -2
- package/dist/types/router/router-options.d.ts +8 -5
- package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +66 -1
- package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +23 -12
- package/dist/types/router/segment-resolution/mask-nested.d.ts +67 -26
- package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
- package/dist/types/router/segment-wrappers.d.ts +1 -1
- package/dist/types/rsc/full-payload.d.ts +14 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/response-route-handler.d.ts +1 -1
- package/dist/types/rsc/rsc-rendering.d.ts +1 -2
- package/dist/types/rsc/shell-build-manifest.d.ts +8 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +61 -6
- package/dist/types/rsc/shell-capture.d.ts +151 -19
- package/dist/types/rsc/shell-serve.d.ts +140 -17
- package/dist/types/rsc/ssr-setup.d.ts +6 -6
- package/dist/types/rsc/types.d.ts +17 -0
- package/dist/types/server/context.d.ts +139 -15
- package/dist/types/server/cookie-store.d.ts +21 -0
- package/dist/types/server/handle-store.d.ts +26 -2
- package/dist/types/server/request-context.d.ts +182 -53
- package/dist/types/ssr/index.d.ts +24 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/static-handler.d.ts +4 -1
- package/dist/types/testing/flight.entry.d.ts +7 -0
- package/dist/types/testing/generated-routes.d.ts +2 -2
- package/dist/types/testing/internal/context.d.ts +1 -1
- package/dist/types/testing/render-route.d.ts +32 -6
- package/dist/types/testing/run-loader.d.ts +18 -3
- package/dist/types/testing/serve-shell-request.d.ts +132 -0
- package/dist/types/testing/shell-status.d.ts +23 -10
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
- package/dist/types/testing/vitest.d.ts +35 -1
- package/dist/types/theme/ThemeScript.d.ts +3 -3
- package/dist/types/types/cache-types.d.ts +14 -10
- package/dist/types/types/handler-context.d.ts +28 -1
- package/dist/types/types/loader-types.d.ts +8 -6
- package/dist/types/urls/pattern-types.d.ts +22 -18
- package/dist/types/urls/response-types.d.ts +3 -12
- package/dist/types/use-loader.d.ts +12 -3
- package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
- package/dist/types/vite/rango.d.ts +1 -1
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +1420 -1348
- package/package.json +2 -2
- package/skills/api-client/SKILL.md +22 -10
- package/skills/breadcrumbs/SKILL.md +75 -22
- package/skills/bundle-analysis/SKILL.md +22 -22
- package/skills/cache-guide/SKILL.md +306 -124
- package/skills/caching/SKILL.md +530 -98
- package/skills/catalog.json +16 -16
- package/skills/client-urls/SKILL.md +65 -38
- package/skills/cloudflare/SKILL.md +87 -20
- package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
- package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
- package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
- package/skills/comparison/SKILL.md +5 -0
- package/skills/comparison/references/framework-comparison.md +75 -57
- package/skills/composability/SKILL.md +38 -29
- package/skills/css/SKILL.md +10 -13
- package/skills/debug-manifest/SKILL.md +74 -31
- package/skills/defer-hydration/SKILL.md +52 -31
- package/skills/deployment-caching/SKILL.md +11 -3
- package/skills/document-cache/SKILL.md +98 -28
- package/skills/fonts/SKILL.md +16 -8
- package/skills/handler-use/SKILL.md +55 -17
- package/skills/hooks/SKILL.md +28 -20
- package/skills/hooks/data.md +23 -3
- package/skills/hooks/handle-and-actions.md +34 -17
- package/skills/hooks/navigation.md +39 -10
- package/skills/hooks/outlets.md +13 -6
- package/skills/hooks/state.md +39 -15
- package/skills/hooks/urls.md +7 -5
- package/skills/host-router/SKILL.md +64 -12
- package/skills/i18n/SKILL.md +60 -34
- package/skills/intercept/SKILL.md +161 -26
- package/skills/layout/SKILL.md +106 -50
- package/skills/links/SKILL.md +51 -19
- package/skills/loader/SKILL.md +386 -128
- package/skills/middleware/SKILL.md +129 -26
- package/skills/migrate-nextjs/SKILL.md +156 -70
- package/skills/migrate-nextjs/backend-host-swap.md +5 -2
- package/skills/migrate-react-router/SKILL.md +51 -39
- package/skills/migrate-react-router/component-migration.md +28 -7
- package/skills/migrate-react-router/data-and-actions.md +21 -6
- package/skills/migrate-react-router/route-mapping.md +19 -10
- package/skills/mime-routes/SKILL.md +12 -7
- package/skills/observability/SKILL.md +92 -14
- package/skills/parallel/SKILL.md +88 -47
- package/skills/ppr/SKILL.md +866 -379
- package/skills/prerender/SKILL.md +210 -108
- package/skills/rango/SKILL.md +115 -65
- package/skills/react-compiler/SKILL.md +6 -5
- package/skills/response-routes/SKILL.md +133 -25
- package/skills/route/SKILL.md +170 -60
- package/skills/router-setup/SKILL.md +420 -240
- package/skills/scripts/SKILL.md +39 -21
- package/skills/server-actions/SKILL.md +231 -59
- package/skills/shell-manifest/SKILL.md +28 -14
- package/skills/streams-and-websockets/SKILL.md +32 -14
- package/skills/tailwind/SKILL.md +37 -33
- package/skills/testing/SKILL.md +73 -33
- package/skills/testing/cache-prerender.md +60 -33
- package/skills/testing/client-components.md +59 -6
- package/skills/testing/e2e-parity.md +12 -2
- package/skills/testing/flight.md +19 -11
- package/skills/testing/handles.md +8 -5
- package/skills/testing/loader.md +26 -6
- package/skills/testing/middleware.md +8 -3
- package/skills/testing/predicates.md +120 -0
- package/skills/testing/render-handler.md +70 -22
- package/skills/testing/response-routes.md +11 -4
- package/skills/testing/reverse-and-types.md +20 -14
- package/skills/testing/serve-shell-request.md +188 -0
- package/skills/testing/server-actions.md +12 -3
- package/skills/testing/server-tree.md +9 -0
- package/skills/testing/setup.md +9 -5
- package/skills/theme/SKILL.md +155 -30
- package/skills/typesafety/SKILL.md +20 -9
- package/skills/typesafety/env-and-bindings.md +137 -50
- package/skills/typesafety/generated-files-and-cli.md +26 -14
- package/skills/typesafety/params-and-search.md +44 -19
- package/skills/typesafety/route-types.md +50 -14
- package/skills/use-cache/SKILL.md +224 -83
- package/skills/vercel/SKILL.md +35 -13
- package/skills/view-transitions/SKILL.md +42 -21
- package/src/__augment-tests__/augmented.check.ts +26 -1
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/history-state.ts +23 -0
- package/src/browser/index.ts +2 -2
- package/src/browser/navigation-bridge.ts +3 -4
- package/src/browser/navigation-transaction.ts +22 -2
- package/src/browser/partial-update.ts +35 -25
- package/src/browser/react/ScrollRestoration.tsx +6 -6
- package/src/browser/rsc-router.tsx +6 -1
- package/src/browser/scroll-restoration.ts +53 -6
- package/src/cache/cache-exec-scope.ts +37 -4
- package/src/cache/cache-key-utils.ts +23 -8
- package/src/cache/cache-policy.ts +22 -0
- package/src/cache/cache-runtime.ts +264 -68
- package/src/cache/cache-scope.ts +419 -43
- package/src/cache/cache-tag.ts +375 -11
- package/src/cache/cf/cf-base64.ts +36 -4
- package/src/cache/cf/cf-cache-constants.ts +12 -0
- package/src/cache/cf/cf-cache-store.ts +990 -348
- package/src/cache/cf/cf-cache-types.ts +32 -4
- package/src/cache/cf/cf-shell-frame.ts +258 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +143 -39
- package/src/cache/document-cache.ts +15 -0
- package/src/cache/handle-capture.ts +90 -10
- package/src/cache/handle-snapshot.ts +133 -28
- package/src/cache/index.ts +2 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/segment-codec.ts +26 -7
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +217 -27
- package/src/cache/tag-invalidation.ts +108 -22
- package/src/cache/taint.ts +1 -1
- package/src/cache/types.ts +225 -44
- package/src/cache/vercel/vercel-cache-store.ts +545 -61
- package/src/client.rsc.tsx +10 -9
- package/src/client.tsx +5 -4
- package/src/components/DefaultDocument.tsx +7 -2
- package/src/context-var.ts +6 -5
- package/src/escape-script.ts +1 -1
- package/src/handles/MetaTags.tsx +13 -11
- package/src/handles/Scripts.tsx +11 -10
- package/src/handles/meta.ts +2 -2
- package/src/handles/script.ts +17 -16
- package/src/href-client.ts +1 -1
- package/src/html.ts +12 -0
- package/src/index.rsc.ts +5 -0
- package/src/index.ts +1 -0
- package/src/loader-store.ts +7 -4
- package/src/prerender/build-shell-capture.ts +73 -22
- package/src/reverse.ts +44 -4
- package/src/route-definition/dsl-helpers.ts +121 -33
- package/src/route-definition/helpers-types.ts +33 -9
- package/src/route-definition/resolve-handler-use.ts +121 -6
- package/src/route-types.ts +0 -3
- package/src/router/debug-manifest.ts +10 -2
- package/src/router/error-handling.ts +51 -15
- package/src/router/handler-context.ts +71 -28
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +141 -43
- package/src/router/loader-resolution.ts +115 -39
- package/src/router/match-api.ts +40 -26
- package/src/router/match-handlers.ts +5 -0
- package/src/router/match-middleware/background-revalidation.ts +161 -96
- package/src/router/match-middleware/cache-lookup.ts +110 -10
- package/src/router/match-middleware/cache-store.ts +48 -101
- package/src/router/middleware-types.ts +9 -6
- package/src/router/middleware.ts +75 -13
- package/src/router/prerender-match.ts +59 -19
- package/src/router/route-snapshot.ts +11 -1
- package/src/router/router-context.ts +2 -0
- package/src/router/router-interfaces.ts +25 -2
- package/src/router/router-options.ts +8 -5
- package/src/router/segment-resolution/fresh.ts +62 -21
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +407 -76
- package/src/router/segment-resolution/loader-mask.ts +12 -22
- package/src/router/segment-resolution/loader-snapshot.ts +208 -56
- package/src/router/segment-resolution/mask-nested.ts +138 -44
- package/src/router/segment-resolution/revalidation.ts +61 -16
- package/src/router/segment-wrappers.ts +4 -0
- package/src/rsc/full-payload.ts +25 -1
- package/src/rsc/handler.ts +14 -2
- package/src/rsc/progressive-enhancement.ts +3 -2
- package/src/rsc/render-pipeline.ts +3 -0
- package/src/rsc/response-cache-serve.ts +9 -6
- package/src/rsc/response-route-handler.ts +11 -3
- package/src/rsc/rsc-rendering.ts +608 -225
- package/src/rsc/shell-build-manifest.ts +27 -9
- package/src/rsc/shell-capture-constants.ts +73 -6
- package/src/rsc/shell-capture.ts +1068 -296
- package/src/rsc/shell-serve.ts +316 -34
- package/src/rsc/ssr-setup.ts +14 -8
- package/src/rsc/types.ts +18 -0
- package/src/server/context.ts +300 -32
- package/src/server/cookie-store.ts +87 -61
- package/src/server/handle-store.ts +232 -7
- package/src/server/request-context.ts +297 -67
- package/src/ssr/index.tsx +136 -44
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/static-handler.ts +4 -1
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/flight.entry.ts +14 -0
- package/src/testing/generated-routes.ts +2 -2
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/internal/flight-client-globals.ts +7 -5
- package/src/testing/render-route.tsx +85 -24
- package/src/testing/run-loader.ts +68 -6
- package/src/testing/serve-shell-request.ts +378 -0
- package/src/testing/shell-status.ts +34 -12
- package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
- package/src/testing/vitest.ts +62 -1
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/theme/ThemeScript.tsx +3 -3
- package/src/types/cache-types.ts +14 -10
- package/src/types/handler-context.ts +28 -1
- package/src/types/loader-types.ts +8 -6
- package/src/urls/pattern-types.ts +22 -18
- package/src/urls/response-types.ts +3 -23
- package/src/use-loader.tsx +12 -3
- package/src/vite/discovery/shell-prerender-phase.ts +9 -0
- package/src/vite/plugins/use-cache-transform.ts +118 -74
- package/src/vite/rango.ts +7 -3
- package/src/vite/router-discovery.ts +14 -4
package/skills/route/SKILL.md
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: route
|
|
3
|
-
description: Define routes with path() in @rangojs/router. Use when creating a new page or route, or asking how to define a URL path and its handler.
|
|
3
|
+
description: Define routes with path() in @rangojs/router — URL params, catch-alls, typed search params, the handler context, handler-to-child data, and redirects. Use when creating a new page or route, or asking how to define a URL path and its handler.
|
|
4
4
|
argument-hint: [pattern]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Defining Routes with path()
|
|
8
8
|
|
|
9
|
+
`path(pattern, handler, options?, use?)` maps a URL pattern to a handler inside
|
|
10
|
+
`urls()`. This skill covers patterns and params, typed search params, the handler
|
|
11
|
+
context, passing handler data to child segments, and redirects.
|
|
12
|
+
|
|
13
|
+
Related skills: `/layout` (wrap routes in shared UI), `/response-routes`
|
|
14
|
+
(`path.json()`, `path.text()`, … endpoints), `/links` (URL generation),
|
|
15
|
+
`/composability` (`include()` and reusable config), `/loader` (data).
|
|
16
|
+
|
|
9
17
|
## Basic Route
|
|
10
18
|
|
|
11
19
|
```typescript
|
|
@@ -57,7 +65,8 @@ see `/i18n`.
|
|
|
57
65
|
|
|
58
66
|
A catch-all consumes the **rest of the path** and exposes it as a single
|
|
59
67
|
decoded string at `ctx.params.<name>`, with the internal `/` separators kept.
|
|
60
|
-
It
|
|
68
|
+
It only acts as a catch-all in the **last** segment of the pattern (see the
|
|
69
|
+
parsing rule at the end of this section).
|
|
61
70
|
|
|
62
71
|
- `:name+` — **one-or-more** segments (Next `[...name]`, React-Router splat).
|
|
63
72
|
`/docs/:slug+` matches `/docs/a` and `/docs/a/b/c`, but **not** the bare
|
|
@@ -81,9 +90,9 @@ urls(({ path }) => [
|
|
|
81
90
|
```
|
|
82
91
|
|
|
83
92
|
`ctx.params.<name>` is always a `string` for a catch-all (never `undefined`) —
|
|
84
|
-
`:name*` binds `""` for the empty case, so read it directly.
|
|
85
|
-
`ctx.reverse()`
|
|
86
|
-
`reverse("docs", { slug: "a/b" })` -> `/docs/a/b`.
|
|
93
|
+
`:name*` binds `""` for the empty case, so read it directly. URL generation
|
|
94
|
+
(`ctx.reverse()`, `href()`) rebuilds the URL with separators preserved:
|
|
95
|
+
`ctx.reverse("docs", { slug: "a/b" })` -> `/docs/a/b`.
|
|
87
96
|
|
|
88
97
|
The value is the URL-decoded remainder. `split("/")` recovers the segments in the
|
|
89
98
|
common case, but note that a segment containing an encoded slash (`%2F`) decodes
|
|
@@ -93,8 +102,27 @@ raw pathname instead.
|
|
|
93
102
|
|
|
94
103
|
The bare unnamed wildcard `path("/files/*", …)` still works and is read at
|
|
95
104
|
`ctx.params["*"]`; prefer a named catch-all when you want a typed param key.
|
|
96
|
-
|
|
97
|
-
|
|
105
|
+
|
|
106
|
+
Parsing rule: `+` / `*` is a catch-all modifier **only** when it is the bare
|
|
107
|
+
final character of the last segment. Anywhere else it is read as a literal
|
|
108
|
+
suffix character, not rejected: `/docs/:slug+/edit` matches `/docs/x+/edit`,
|
|
109
|
+
and `:slug*.html`, `:slug?*`, and `:slug(a|b)+` are ordinary params followed by
|
|
110
|
+
a literal suffix.
|
|
111
|
+
|
|
112
|
+
### Constrained and suffixed params
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
urls(({ path }) => [
|
|
116
|
+
// Only /en/about or /gb/about match; ctx.params.locale is typed "en" | "gb"
|
|
117
|
+
path("/:locale(en|gb)/about", AboutPage, { name: "about" }),
|
|
118
|
+
|
|
119
|
+
// Constrained + optional: /pricing, /en/pricing, /gb/pricing
|
|
120
|
+
path("/:locale(en|gb)?/pricing", PricingPage, { name: "pricing" }),
|
|
121
|
+
|
|
122
|
+
// Literal suffix: /files/report.pdf -> ctx.params.name === "report"
|
|
123
|
+
path("/files/:name.pdf", PdfViewer, { name: "pdf" }),
|
|
124
|
+
]);
|
|
125
|
+
```
|
|
98
126
|
|
|
99
127
|
## Route Handler Patterns
|
|
100
128
|
|
|
@@ -135,12 +163,25 @@ path("/product/:slug", async (ctx) => {
|
|
|
135
163
|
|
|
136
164
|
## Route Options
|
|
137
165
|
|
|
166
|
+
All options are optional; with none, pass the `use` callback as the third
|
|
167
|
+
argument: `path("/x", Page, () => [loader(L)])`.
|
|
168
|
+
|
|
138
169
|
```typescript
|
|
139
170
|
path("/product/:slug", ProductPage, {
|
|
140
|
-
name: "product", //
|
|
171
|
+
name: "product", // route name for href(), ctx.reverse(), Handler<"product">
|
|
172
|
+
search: { tab: "string?" }, // typed ctx.search (next section)
|
|
173
|
+
trailingSlash: "never", // "never" | "always" | "ignore"
|
|
174
|
+
ppr: true, // serve a cached HTML shell with live holes (see /ppr)
|
|
141
175
|
});
|
|
142
176
|
```
|
|
143
177
|
|
|
178
|
+
- `name` — unnamed routes still match but can't be reversed by name or typed
|
|
179
|
+
with `Handler<"name">`. Inside an `include()`, the include's `name` option
|
|
180
|
+
decides whether the name is visible globally (see "Nested Routes" below).
|
|
181
|
+
- `trailingSlash` — `"never"` redirects `/docs/` to `/docs`, `"always"` redirects `/docs`
|
|
182
|
+
to `/docs/`, `"ignore"` matches both. Unset, the pattern's own trailing slash
|
|
183
|
+
decides.
|
|
184
|
+
|
|
144
185
|
### Typed Search Params
|
|
145
186
|
|
|
146
187
|
Add a `search` schema to get typed `ctx.search`:
|
|
@@ -158,24 +199,31 @@ Use `Handler<"name">` for typed search params (resolves from the generated route
|
|
|
158
199
|
import type { Handler } from "@rangojs/router";
|
|
159
200
|
|
|
160
201
|
export const SearchPage: Handler<"search"> = (ctx) => {
|
|
161
|
-
// ctx.search is typed: { q: string; page?: number; sort?: string }
|
|
162
|
-
const { q, page, sort } = ctx.search;
|
|
202
|
+
// ctx.search is typed: { q: string | undefined; page?: number; sort?: string }
|
|
203
|
+
const { q = "", page = 1, sort } = ctx.search;
|
|
163
204
|
// ctx.searchParams is always URLSearchParams
|
|
164
205
|
return <SearchResults q={q} page={page} sort={sort} />;
|
|
165
206
|
};
|
|
166
207
|
```
|
|
167
208
|
|
|
168
209
|
Supported types: `"string"`, `"number"`, `"boolean"`, with `?` suffix for optional.
|
|
169
|
-
|
|
170
|
-
|
|
210
|
+
|
|
211
|
+
- **Missing params are `undefined` regardless of required/optional** — a client
|
|
212
|
+
can always omit them, so the handler must default or check. The
|
|
213
|
+
required/optional distinction is a consumer-facing contract that drives
|
|
214
|
+
`href()` / `ctx.reverse()` autocomplete.
|
|
215
|
+
- `"number"` accepts decimal numerals (`42`, `-3.5`, `1e3`). Empty values, hex
|
|
216
|
+
(`0x10`), and non-finite values are treated as missing, not coerced.
|
|
217
|
+
- `"boolean"` is `true` for `"true"` / `"1"` and `false` for any other present
|
|
218
|
+
value.
|
|
171
219
|
|
|
172
220
|
Use `RouteSearchParams<"name">` and `RouteParams<"name">` to extract types for props:
|
|
173
221
|
|
|
174
222
|
```typescript
|
|
175
223
|
import type { RouteSearchParams, RouteParams } from "@rangojs/router";
|
|
176
224
|
|
|
177
|
-
type SP = RouteSearchParams<"search">; // { q: string; page?: number; sort?: string }
|
|
178
|
-
type P = RouteParams<"blogPost">; // { slug: string }
|
|
225
|
+
type SP = RouteSearchParams<"search">; // { q: string | undefined; page?: number; sort?: string }
|
|
226
|
+
type P = RouteParams<"blogPost">; // { year: string; month: string; slug: string }
|
|
179
227
|
```
|
|
180
228
|
|
|
181
229
|
## Route Children
|
|
@@ -194,15 +242,13 @@ path("/product/:slug", ProductPage, { name: "product" }, () => [
|
|
|
194
242
|
|
|
195
243
|
When a route has children (orphan layouts, parallels), the handler executes
|
|
196
244
|
first. Use `ctx.set(key, value)` to share data with children, who read it
|
|
197
|
-
via `ctx.get(key)`.
|
|
198
|
-
|
|
245
|
+
via `ctx.get(key)`. A route-level `cache()` wraps all of the entry's segments
|
|
246
|
+
together, so on a cache hit none of them run and on a miss all of them do.
|
|
199
247
|
|
|
200
|
-
This
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
stays consistent with no configuration. See `/rango` → "Passing data down
|
|
205
|
-
the tree" for the safest-first ladder.
|
|
248
|
+
This stays consistent after actions with no configuration: on an action, the
|
|
249
|
+
route segment, its loaders, and the children declared inside the `path()`
|
|
250
|
+
(orphan layouts and their parallels) all re-run together, handler first. See
|
|
251
|
+
`/rango` → "Passing data down the tree" for the safest-first ladder.
|
|
206
252
|
|
|
207
253
|
### Typed context variables with createVar
|
|
208
254
|
|
|
@@ -248,8 +294,10 @@ path("/dashboard/:id", async (ctx) => {
|
|
|
248
294
|
String keys still work (`ctx.set("key", value)` / `ctx.get("key")`), but
|
|
249
295
|
`createVar<T>()` is preferred for type safety.
|
|
250
296
|
|
|
251
|
-
|
|
252
|
-
|
|
297
|
+
Write with `ctx.set()` from middleware, route handlers, and layout handlers;
|
|
298
|
+
read with `ctx.get()` in the segments they wrap. Handlers run before their
|
|
299
|
+
orphan layouts and parallels (handler-first), so those children see the value.
|
|
300
|
+
Parallels and intercepts render last, so treat them as readers.
|
|
253
301
|
|
|
254
302
|
#### Non-cacheable context variables
|
|
255
303
|
|
|
@@ -268,19 +316,30 @@ ctx.set(Theme, userTheme, { cache: false });
|
|
|
268
316
|
"Least cacheable wins" — if either the var definition or the write site says
|
|
269
317
|
`cache: false`, the value is non-cacheable.
|
|
270
318
|
|
|
271
|
-
Reading a non-cacheable var
|
|
272
|
-
runtime
|
|
319
|
+
Reading a non-cacheable var directly with `ctx.get()` inside a `cache()`
|
|
320
|
+
boundary throws at runtime, so request-specific data can't be baked into a
|
|
321
|
+
cached segment:
|
|
273
322
|
|
|
274
323
|
```typescript
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
324
|
+
cache({ ttl: 60 }, () => [
|
|
325
|
+
path("/account", (ctx) => {
|
|
326
|
+
const session = ctx.get(Session); // throws: non-cacheable read inside cache()
|
|
327
|
+
return <Account session={session} />;
|
|
328
|
+
}, { name: "account" }),
|
|
329
|
+
]);
|
|
281
330
|
```
|
|
282
331
|
|
|
283
|
-
|
|
332
|
+
The same read throws inside a `"use cache"` function body, where the value would
|
|
333
|
+
be stored without being part of the key — pass it in as an argument instead. The
|
|
334
|
+
guard does not follow derived values (a string copied out of `Session` and
|
|
335
|
+
cached elsewhere is not tracked). Under `cache()`, loaders are exempt — a DSL
|
|
336
|
+
loader re-runs on every hit — but a `"use cache"` function a loader calls is
|
|
337
|
+
not, and neither is a loader bound with its own `cache()` without a `key()` (any
|
|
338
|
+
`key()` switches the check off, so it must include the value; `/loader` → "Cache
|
|
339
|
+
Key"). On a `ppr` route the exemption does not cover a loader the shell capture
|
|
340
|
+
waits for (`ssr: false`, or awaited by a handler via `ctx.use()`): the read
|
|
341
|
+
refuses the capture. Cacheable vars (the default) can be read freely inside
|
|
342
|
+
cache scopes. See `/cache-guide` → "Context Variable Cache Safety".
|
|
284
343
|
|
|
285
344
|
### Revalidation Contracts for Handler Data
|
|
286
345
|
|
|
@@ -295,7 +354,8 @@ the route handler and its children re-run together by default, so handler
|
|
|
295
354
|
data stays consistent on its own. Contracts matter in two cases:
|
|
296
355
|
|
|
297
356
|
1. **You narrow the entry's revalidation** with a predicate that can return a
|
|
298
|
-
hard `false` (e.g.
|
|
357
|
+
hard `false` (e.g. `ctx.isAction() ? ctx.isAction(X) : undefined`). A hard
|
|
358
|
+
`false` on one side of a
|
|
299
359
|
producer/consumer pair desyncs it — the child re-runs by default and reads
|
|
300
360
|
`undefined`, or vice versa. Put the same named contract on the route and
|
|
301
361
|
its dependent children so they narrow together.
|
|
@@ -305,17 +365,20 @@ data stays consistent on its own. Contracts matter in two cases:
|
|
|
305
365
|
|
|
306
366
|
```typescript
|
|
307
367
|
// revalidation-contracts.ts
|
|
368
|
+
import type { Revalidate } from "@rangojs/router";
|
|
308
369
|
import * as CheckoutActions from "./actions/checkout";
|
|
309
370
|
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
|
|
313
|
-
|
|
371
|
+
// The route and its children re-run after every action by default, so this
|
|
372
|
+
// contract narrows them together: after an action, re-run only for checkout
|
|
373
|
+
// actions; on navigation, undefined keeps the default. After an action it is a
|
|
374
|
+
// hard decision, so later revalidators on the same segment do not run.
|
|
375
|
+
export const revalidateCheckoutData: Revalidate = (ctx) =>
|
|
376
|
+
ctx.isAction() ? ctx.isAction(CheckoutActions) : undefined;
|
|
314
377
|
|
|
315
378
|
path("/checkout", CheckoutPage, { name: "checkout" }, () => [
|
|
316
|
-
revalidate(revalidateCheckoutData), // producer (route handler)
|
|
379
|
+
revalidate(revalidateCheckoutData), // producer (route handler)
|
|
317
380
|
layout(CheckoutLayout, () => [
|
|
318
|
-
revalidate(revalidateCheckoutData), // consumer
|
|
381
|
+
revalidate(revalidateCheckoutData), // consumer
|
|
319
382
|
parallel({ "@summary": CheckoutSummary }, () => [
|
|
320
383
|
revalidate(revalidateCheckoutData),
|
|
321
384
|
]),
|
|
@@ -323,8 +386,10 @@ path("/checkout", CheckoutPage, { name: "checkout" }, () => [
|
|
|
323
386
|
]);
|
|
324
387
|
```
|
|
325
388
|
|
|
326
|
-
If children depend on multiple upstream domains,
|
|
327
|
-
|
|
389
|
+
If children depend on multiple upstream domains, match them in one narrowing
|
|
390
|
+
contract (`ctx.isAction(CheckoutActions, AuthActions)`): a second narrowing
|
|
391
|
+
contract on the same segment would never run, because the first one's hard
|
|
392
|
+
`false` ends the chain.
|
|
328
393
|
|
|
329
394
|
For cleaner route trees, expose contract helpers and spread them:
|
|
330
395
|
|
|
@@ -341,6 +406,9 @@ path("/checkout", CheckoutPage, { name: "checkout" }, () => [
|
|
|
341
406
|
|
|
342
407
|
## Redirects
|
|
343
408
|
|
|
409
|
+
`redirect(url, statusOrOptions?)` returns a `Response` (default status `302`).
|
|
410
|
+
Return it from a handler or middleware, or throw it from a loader.
|
|
411
|
+
|
|
344
412
|
### Basic redirect
|
|
345
413
|
|
|
346
414
|
```typescript
|
|
@@ -355,6 +423,22 @@ path("/old-page", () => redirect("/new-page"), { name: "oldPage" });
|
|
|
355
423
|
path("/moved", () => redirect("/new-location", 301), { name: "moved" });
|
|
356
424
|
```
|
|
357
425
|
|
|
426
|
+
Root-relative targets are prefixed with the router's `basename` automatically
|
|
427
|
+
(an already-prefixed URL is left alone).
|
|
428
|
+
|
|
429
|
+
### Off-origin redirects
|
|
430
|
+
|
|
431
|
+
Cross-origin targets are blocked by a same-origin guard and replaced with the
|
|
432
|
+
app root. Opt in explicitly for an intended off-host redirect:
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
path(
|
|
436
|
+
"/login",
|
|
437
|
+
() => redirect("https://accounts.example.com/oauth", { external: true }),
|
|
438
|
+
{ name: "login" },
|
|
439
|
+
);
|
|
440
|
+
```
|
|
441
|
+
|
|
358
442
|
> **Redirecting from a route with `loading()`:** an `async` handler that returns
|
|
359
443
|
> a `Response`/`redirect()` on a route that also declares `loading()` is streamed,
|
|
360
444
|
> so the redirect is rendered into the RSC stream instead of becoming an HTTP
|
|
@@ -399,7 +483,8 @@ path(
|
|
|
399
483
|
);
|
|
400
484
|
```
|
|
401
485
|
|
|
402
|
-
Read
|
|
486
|
+
`state` takes one entry or an array. Read it on the target page with
|
|
487
|
+
`useLocationState(FlashMessage)` (from `@rangojs/router/client`). The
|
|
403
488
|
`{ flash: true }` option makes it auto-clear. Without `{ flash: true }`,
|
|
404
489
|
state persists on back/forward. See `/hooks` for details.
|
|
405
490
|
|
|
@@ -426,24 +511,44 @@ State flows to the browser via the RSC payload and is merged into
|
|
|
426
511
|
Every handler receives a context object:
|
|
427
512
|
|
|
428
513
|
```typescript
|
|
514
|
+
// Simplified sketch. Import the real type with
|
|
515
|
+
// `import type { HandlerContext } from "@rangojs/router"`.
|
|
429
516
|
interface HandlerContext<TParams = {}, TEnv = DefaultEnv, TSearch = {}> {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
517
|
+
// Request
|
|
518
|
+
params: TParams; // URL params
|
|
519
|
+
request: Request; // original request (raw transport URL, headers, body)
|
|
520
|
+
url: URL; // request URL with internal _rsc* params stripped
|
|
521
|
+
pathname: string;
|
|
522
|
+
searchParams: URLSearchParams; // always URLSearchParams (_rsc* stripped)
|
|
523
|
+
search: ResolveSearchSchema<TSearch>; // typed from the search schema ({} without one)
|
|
524
|
+
env: TEnv; // platform bindings
|
|
525
|
+
routeName?: string; // matched route name (undefined for unnamed routes)
|
|
526
|
+
|
|
527
|
+
// Data flow
|
|
528
|
+
get<T>(contextVar: ContextVar<T>): T | undefined; // also accepts a string key
|
|
529
|
+
set<T>(
|
|
530
|
+
contextVar: ContextVar<T>,
|
|
531
|
+
value: T,
|
|
532
|
+
options?: { cache?: boolean },
|
|
533
|
+
): void;
|
|
534
|
+
use<T>(loader: LoaderDefinition<T>): Promise<T>; // loader data (memoized per request)
|
|
535
|
+
use<T>(handle: Handle<T>): HandlePush<T>; // push function for a handle
|
|
536
|
+
|
|
537
|
+
// Response
|
|
538
|
+
headers: Headers; // response headers, merged into the final response
|
|
539
|
+
setLocationState(entries: LocationStateEntry | LocationStateEntry[]): void;
|
|
441
540
|
reverse(
|
|
442
541
|
name: string,
|
|
443
542
|
params?: Record<string, string>,
|
|
444
543
|
search?: Record<string, unknown>,
|
|
445
|
-
): string;
|
|
446
|
-
|
|
544
|
+
): string;
|
|
545
|
+
waitUntil(fn: () => Promise<void>): void; // work that continues after the response
|
|
546
|
+
|
|
547
|
+
// Mode
|
|
548
|
+
build: boolean; // true while pre-rendering at build time
|
|
549
|
+
dev: boolean; // true under Vite dev
|
|
550
|
+
dynamic(): void; // opt this request out of PPR shell capture (see /ppr)
|
|
551
|
+
readonly theme?: Theme; // plus setTheme?() — only when the router enables themes; a read is guarded like cookies() (/theme)
|
|
447
552
|
}
|
|
448
553
|
```
|
|
449
554
|
|
|
@@ -460,9 +565,12 @@ path("/product/:slug", (ctx) => {
|
|
|
460
565
|
// Access platform bindings
|
|
461
566
|
const db = ctx.env.DB;
|
|
462
567
|
|
|
463
|
-
//
|
|
464
|
-
const
|
|
465
|
-
|
|
568
|
+
// Push handle data: ctx.use(Handle) returns a push function
|
|
569
|
+
const pushCrumb = ctx.use(Breadcrumbs);
|
|
570
|
+
pushCrumb({ label: "Product", href: `/product/${slug}` });
|
|
571
|
+
|
|
572
|
+
// Set a response header
|
|
573
|
+
ctx.headers.set("Cache-Control", "private, max-age=60");
|
|
466
574
|
|
|
467
575
|
return <ProductPage slug={slug} tab={tab} />;
|
|
468
576
|
}, { name: "product" })
|
|
@@ -505,6 +613,8 @@ A route can configure its own `transition()` — the wrap goes around the route'
|
|
|
505
613
|
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.
|
|
506
614
|
|
|
507
615
|
```typescript
|
|
616
|
+
import { loader, loading, middleware, type Handler } from "@rangojs/router";
|
|
617
|
+
|
|
508
618
|
const ProductPage: Handler<"/product/:slug"> = async (ctx) => {
|
|
509
619
|
const product = await ctx.use(ProductLoader);
|
|
510
620
|
return <ProductView product={product} />;
|
|
@@ -514,7 +624,7 @@ ProductPage.use = () => [
|
|
|
514
624
|
loading(<ProductSkeleton />),
|
|
515
625
|
middleware(async (ctx, next) => {
|
|
516
626
|
await next();
|
|
517
|
-
ctx.
|
|
627
|
+
ctx.headers.set("Cache-Control", "private, max-age=60");
|
|
518
628
|
}),
|
|
519
629
|
];
|
|
520
630
|
|