@rangojs/router 0.0.0-experimental.e9c0b2f2 → 0.0.0-experimental.ea9f40f2
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 +6 -10
- package/README.md +289 -938
- package/dist/bin/rango.js +271 -46
- package/dist/vite/index.js +673 -193
- package/package.json +10 -8
- package/skills/api-client/SKILL.md +1 -1
- package/skills/breadcrumbs/SKILL.md +31 -14
- package/skills/cache-guide/SKILL.md +5 -2
- package/skills/caching/SKILL.md +59 -4
- package/skills/catalog.json +271 -0
- package/skills/comparison/SKILL.md +50 -0
- package/skills/comparison/agents/openai.yaml +4 -0
- package/skills/comparison/references/framework-comparison.md +837 -0
- package/skills/composability/SKILL.md +83 -2
- package/skills/debug-manifest/SKILL.md +1 -1
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +9 -1
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +8 -8
- package/skills/hooks/SKILL.md +54 -892
- package/skills/hooks/data.md +273 -0
- package/skills/hooks/handle-and-actions.md +103 -0
- package/skills/hooks/navigation.md +110 -0
- package/skills/hooks/outlets.md +41 -0
- package/skills/hooks/state.md +228 -0
- package/skills/hooks/urls.md +135 -0
- package/skills/host-router/SKILL.md +4 -4
- package/skills/i18n/SKILL.md +1 -1
- package/skills/intercept/SKILL.md +46 -14
- package/skills/layout/SKILL.md +27 -10
- package/skills/links/SKILL.md +1 -1
- package/skills/loader/SKILL.md +23 -1
- package/skills/middleware/SKILL.md +7 -3
- package/skills/migrate-nextjs/SKILL.md +167 -6
- package/skills/migrate-react-router/SKILL.md +59 -677
- package/skills/migrate-react-router/cloudflare-workers.md +129 -0
- package/skills/migrate-react-router/component-migration.md +196 -0
- package/skills/migrate-react-router/data-and-actions.md +225 -0
- package/skills/migrate-react-router/route-mapping.md +271 -0
- package/skills/mime-routes/SKILL.md +1 -1
- package/skills/observability/SKILL.md +9 -1
- package/skills/parallel/SKILL.md +23 -4
- package/skills/ppr/SKILL.md +622 -0
- package/skills/prerender/SKILL.md +28 -18
- package/skills/rango/SKILL.md +84 -25
- package/skills/response-routes/SKILL.md +15 -1
- package/skills/route/SKILL.md +71 -4
- package/skills/router-setup/SKILL.md +14 -3
- package/skills/scripts/SKILL.md +1 -1
- package/skills/server-actions/SKILL.md +3 -2
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +1 -1
- package/skills/tailwind/SKILL.md +1 -1
- package/skills/testing/SKILL.md +2 -1
- package/skills/testing/handles.md +4 -2
- package/skills/testing/render-handler.md +15 -14
- package/skills/testing/reverse-and-types.md +8 -7
- package/skills/theme/SKILL.md +1 -1
- package/skills/typesafety/SKILL.md +45 -919
- package/skills/typesafety/env-and-bindings.md +254 -0
- package/skills/typesafety/generated-files-and-cli.md +335 -0
- package/skills/typesafety/params-and-search.md +153 -0
- package/skills/typesafety/route-types.md +209 -0
- package/skills/use-cache/SKILL.md +30 -3
- package/skills/vercel/SKILL.md +1 -1
- package/skills/view-transitions/SKILL.md +44 -1
- package/src/browser/event-controller.ts +62 -10
- package/src/browser/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +65 -16
- package/src/browser/navigation-client.ts +32 -2
- package/src/browser/navigation-store.ts +128 -14
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +76 -17
- package/src/browser/prefetch/cache.ts +51 -11
- package/src/browser/prefetch/fetch.ts +59 -21
- package/src/browser/prefetch/queue.ts +19 -4
- package/src/browser/react/Link.tsx +13 -3
- package/src/browser/react/NavigationProvider.tsx +108 -4
- package/src/browser/response-adapter.ts +38 -9
- package/src/browser/rsc-router.tsx +54 -4
- package/src/browser/scroll-restoration.ts +7 -5
- package/src/browser/segment-reconciler.ts +31 -21
- package/src/browser/server-action-bridge.ts +22 -10
- package/src/browser/types.ts +54 -1
- package/src/build/generate-manifest.ts +155 -131
- package/src/build/index.ts +3 -1
- package/src/build/route-trie.ts +35 -7
- package/src/build/route-types/include-resolution.ts +347 -47
- package/src/build/runtime-discovery.ts +4 -1
- package/src/cache/cache-key-utils.ts +29 -0
- package/src/cache/cache-runtime.ts +262 -71
- package/src/cache/cache-scope.ts +2 -17
- package/src/cache/cache-tag.ts +60 -14
- package/src/cache/cf/cf-cache-store.ts +243 -20
- package/src/cache/document-cache.ts +54 -21
- package/src/cache/index.ts +1 -0
- package/src/cache/memory-segment-store.ts +110 -3
- package/src/cache/profile-registry.ts +15 -0
- package/src/cache/read-through-swr.ts +15 -1
- package/src/cache/segment-codec.ts +4 -4
- package/src/cache/shell-snapshot.ts +417 -0
- package/src/cache/types.ts +158 -0
- package/src/cache/vercel/vercel-cache-store.ts +401 -124
- package/src/client.rsc.tsx +0 -3
- package/src/client.tsx +0 -3
- package/src/cloudflare/tracing.ts +7 -8
- package/src/defer.ts +11 -22
- package/src/handle.ts +37 -15
- package/src/handles/MetaTags.tsx +16 -82
- package/src/handles/breadcrumbs.ts +12 -14
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +7 -8
- package/src/handles/meta.ts +7 -44
- package/src/host/errors.ts +15 -0
- package/src/host/index.ts +1 -0
- package/src/index.rsc.ts +8 -2
- package/src/index.ts +19 -13
- package/src/internal-debug.ts +11 -8
- package/src/prerender.ts +17 -4
- package/src/redirect-origin.ts +14 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/route-content-wrapper.tsx +12 -5
- package/src/route-definition/dsl-helpers.ts +21 -32
- package/src/route-definition/helper-factories.ts +0 -2
- package/src/route-definition/helpers-types.ts +43 -43
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/resolve-handler-use.ts +0 -1
- package/src/route-definition/use-item-types.ts +3 -6
- package/src/route-map-builder.ts +41 -4
- package/src/route-types.ts +0 -5
- package/src/router/find-match.ts +86 -8
- package/src/router/instrument.ts +9 -4
- package/src/router/lazy-includes.ts +72 -12
- package/src/router/loader-resolution.ts +14 -2
- package/src/router/manifest.ts +56 -11
- package/src/router/match-api.ts +76 -32
- package/src/router/match-handlers.ts +181 -135
- package/src/router/match-middleware/background-revalidation.ts +40 -23
- package/src/router/match-middleware/cache-store.ts +39 -24
- package/src/router/match-result.ts +35 -15
- package/src/router/middleware.ts +64 -38
- package/src/router/navigation-snapshot.ts +7 -5
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +53 -64
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +11 -5
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +23 -8
- package/src/router/route-snapshot.ts +14 -2
- package/src/router/router-context.ts +3 -1
- package/src/router/router-interfaces.ts +32 -1
- package/src/router/router-options.ts +30 -0
- package/src/router/segment-resolution/fresh.ts +39 -3
- package/src/router/segment-resolution/loader-cache.ts +93 -2
- package/src/router/segment-resolution/loader-mask.ts +60 -0
- package/src/router/segment-resolution/loader-snapshot.ts +259 -0
- package/src/router/segment-resolution/mask-nested.ts +83 -0
- package/src/router/segment-resolution/revalidation.ts +3 -0
- package/src/router/segment-resolution/view-transition-default.ts +35 -15
- package/src/router/substitute-pattern-params.ts +54 -35
- package/src/router/telemetry-otel.ts +6 -8
- package/src/router/telemetry.ts +9 -1
- package/src/router/tracing.ts +14 -5
- package/src/router/trie-matching.ts +19 -11
- package/src/router/url-params.ts +13 -0
- package/src/router.ts +47 -16
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler.ts +60 -33
- package/src/rsc/manifest-init.ts +1 -1
- package/src/rsc/nonce.ts +10 -1
- package/src/rsc/progressive-enhancement.ts +61 -4
- package/src/rsc/redirect-guard.ts +2 -1
- package/src/rsc/rsc-rendering.ts +429 -37
- package/src/rsc/server-action.ts +25 -2
- package/src/rsc/shell-capture.ts +1190 -0
- package/src/rsc/shell-serve.ts +181 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +30 -0
- package/src/segment-loader-promise.ts +18 -0
- package/src/segment-system.tsx +149 -14
- package/src/server/context.ts +67 -9
- package/src/server/cookie-store.ts +73 -1
- package/src/server/loader-registry.ts +13 -1
- package/src/server/request-context.ts +169 -10
- package/src/ssr/index.tsx +462 -178
- package/src/ssr/inject-rsc-eager.ts +167 -0
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/testing/collect-handle.ts +14 -8
- package/src/testing/dispatch.ts +152 -40
- package/src/testing/generated-routes.ts +27 -11
- package/src/testing/index.ts +6 -0
- package/src/testing/render-handler.ts +14 -0
- package/src/testing/render-route.tsx +13 -10
- package/src/testing/run-transition-when.ts +164 -0
- package/src/theme/ThemeProvider.tsx +36 -26
- package/src/types/handler-context.ts +1 -1
- package/src/types/index.ts +2 -0
- package/src/types/route-config.ts +19 -7
- package/src/types/segments.ts +100 -0
- package/src/urls/include-helper.ts +10 -8
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +1 -0
- package/src/urls/path-helper-types.ts +44 -12
- package/src/urls/path-helper.ts +5 -0
- package/src/urls/pattern-types.ts +36 -0
- package/src/urls/type-extraction.ts +43 -18
- package/src/urls/urls-function.ts +0 -1
- package/src/vercel/tracing.ts +7 -7
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +1 -1
- package/src/vite/discovery/discovery-errors.ts +61 -0
- package/src/vite/index.ts +7 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugins/vercel-output.ts +114 -25
- package/src/vite/plugins/version-injector.ts +22 -7
- package/src/vite/plugins/virtual-entries.ts +80 -22
- package/src/vite/rango.ts +29 -19
- package/src/vite/router-discovery.ts +171 -43
- package/src/vite/utils/prerender-utils.ts +17 -4
- package/src/vite/utils/shared-utils.ts +47 -0
- package/src/network-error-thrower.tsx +0 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rangojs/router",
|
|
3
|
-
"version": "0.0.0-experimental.
|
|
3
|
+
"version": "0.0.0-experimental.ea9f40f2",
|
|
4
4
|
"description": "Django-inspired RSC router with composable URL patterns",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -180,7 +180,7 @@
|
|
|
180
180
|
},
|
|
181
181
|
"dependencies": {
|
|
182
182
|
"@types/debug": "^4.1.12",
|
|
183
|
-
"@vitejs/plugin-rsc": "^0.5.
|
|
183
|
+
"@vitejs/plugin-rsc": "^0.5.27",
|
|
184
184
|
"debug": "^4.4.1",
|
|
185
185
|
"magic-string": "^0.30.17",
|
|
186
186
|
"picomatch": "^4.0.4",
|
|
@@ -190,6 +190,8 @@
|
|
|
190
190
|
},
|
|
191
191
|
"devDependencies": {
|
|
192
192
|
"@opentelemetry/api": "^1.9.0",
|
|
193
|
+
"@opentelemetry/context-async-hooks": "^2.9.0",
|
|
194
|
+
"@opentelemetry/sdk-trace-base": "^2.9.0",
|
|
193
195
|
"@playwright/test": "^1.49.1",
|
|
194
196
|
"@shared/e2e": "workspace:*",
|
|
195
197
|
"@testing-library/dom": "^10.4.1",
|
|
@@ -197,24 +199,24 @@
|
|
|
197
199
|
"@types/node": "^24.10.1",
|
|
198
200
|
"@types/react": "catalog:",
|
|
199
201
|
"@types/react-dom": "catalog:",
|
|
200
|
-
"esbuild": "^0.
|
|
202
|
+
"esbuild": "^0.28.1",
|
|
201
203
|
"happy-dom": "^20.10.1",
|
|
202
|
-
"jiti": "^2.
|
|
204
|
+
"jiti": "^2.7.0",
|
|
203
205
|
"react": "catalog:",
|
|
204
206
|
"react-dom": "catalog:",
|
|
205
207
|
"typescript": "^5.3.0",
|
|
206
|
-
"vitest": "^4.
|
|
208
|
+
"vitest": "^4.1.9"
|
|
207
209
|
},
|
|
208
210
|
"peerDependencies": {
|
|
209
|
-
"@cloudflare/vite-plugin": "^1.
|
|
211
|
+
"@cloudflare/vite-plugin": "^1.42.1",
|
|
210
212
|
"@opentelemetry/api": "^1.9.0",
|
|
211
213
|
"@playwright/test": "^1.49.1",
|
|
212
214
|
"@testing-library/react": ">=16",
|
|
213
215
|
"@vercel/functions": "^3.0.0",
|
|
214
|
-
"@vitejs/plugin-rsc": "^0.5.
|
|
216
|
+
"@vitejs/plugin-rsc": "^0.5.27",
|
|
215
217
|
"react": ">=19.2.6 <20",
|
|
216
218
|
"react-dom": ">=19.2.6 <20",
|
|
217
|
-
"vite": "^8.0.
|
|
219
|
+
"vite": "^8.0.16",
|
|
218
220
|
"vitest": ">=3"
|
|
219
221
|
},
|
|
220
222
|
"peerDependenciesMeta": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: api-client
|
|
3
|
-
description: Build a typed client for consuming your own response-route JSON APIs (no codegen)
|
|
3
|
+
description: Build a typed client for consuming your own response-route JSON APIs (no codegen). Use when calling your own JSON endpoints from another service or script, or you want typed fetch calls without a codegen step.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Typed API Client
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: breadcrumbs
|
|
3
|
-
description: Built-in Breadcrumbs handle for accumulating breadcrumb navigation across route segments
|
|
4
|
-
argument-hint: [setup]
|
|
3
|
+
description: Built-in Breadcrumbs handle for accumulating breadcrumb navigation across route segments. Use when building a breadcrumb trail for nested routes, or asking how to show the current navigation path in a layout.
|
|
4
|
+
argument-hint: "[setup]"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Breadcrumbs
|
|
@@ -90,7 +90,9 @@ a deep async component, not the handler — call `.defer()` on the push function
|
|
|
90
90
|
slot synchronously and returns a **resolver that is push-equal** — you call it
|
|
91
91
|
later, anywhere in the render, with the same argument you'd have passed to the
|
|
92
92
|
push (a value, a `Promise`, or a thunk). The only added behavior is a timeout, so a
|
|
93
|
-
forgotten resolve can't
|
|
93
|
+
forgotten resolve can't hang the render (and the HTTP response): resolve-by-default
|
|
94
|
+
awaits the reserved slot before any consumer reads it, and the timeout guarantees it
|
|
95
|
+
settles to `else` instead of blocking forever.
|
|
94
96
|
|
|
95
97
|
Reserve the slot in the handler, then resolve it from a nested async component
|
|
96
98
|
that closes over the resolver — no extra wiring (the resolver is a plain closure,
|
|
@@ -131,15 +133,17 @@ hung request. `timeoutMs: 0` or `Infinity` disable the timeout intentionally; an
|
|
|
131
133
|
other non-finite or negative value falls back to the default rather than silently
|
|
132
134
|
disabling the safety net.
|
|
133
135
|
|
|
134
|
-
**Consumer note
|
|
135
|
-
|
|
136
|
-
`Promise
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
136
|
+
**Consumer note (resolve-by-default):** a deferred crumb is RESOLVED before any
|
|
137
|
+
consumer sees it — `useHandle(Breadcrumbs)` returns the resolved item, never a
|
|
138
|
+
`Promise`, so you read it like any sync crumb (no `use()`, no thenable narrowing).
|
|
139
|
+
On a full/SSR load the value is resolved server-side; on a soft navigation the
|
|
140
|
+
breadcrumbs HOLD the previous resolved value until the deferred value lands, then
|
|
141
|
+
swap in — no blank, no pending entry. If the slot times out to `else: null`/
|
|
142
|
+
undefined, the entry is simply dropped. Use `.defer()` only when even
|
|
143
|
+
`label`/`href` are unknown at handler time — if you know them and only the
|
|
144
|
+
`content` is async, push a concrete item with a `Promise` `content` field instead
|
|
145
|
+
(the `content` field is a nested promise you resolve with `use()` in your
|
|
146
|
+
component; no `.defer()` needed).
|
|
143
147
|
|
|
144
148
|
## Consuming Breadcrumbs (Client)
|
|
145
149
|
|
|
@@ -274,15 +278,28 @@ Create your own handle with `createHandle()`:
|
|
|
274
278
|
```typescript
|
|
275
279
|
import { createHandle } from "@rangojs/router";
|
|
276
280
|
|
|
277
|
-
//
|
|
281
|
+
// Custom collect: last value wins.
|
|
278
282
|
export const PageTitle = createHandle<string, string>(
|
|
279
283
|
(segments) => segments.flat().at(-1) ?? "Default Title",
|
|
280
284
|
);
|
|
281
285
|
|
|
282
|
-
// No collect
|
|
286
|
+
// No collect: the DEFAULT is the identity (lossless) — `collect` receives the
|
|
287
|
+
// per-segment data (TData[][], one array per segment that pushed, in segment
|
|
288
|
+
// order) and passes it through as-is. `useHandle(Warnings)` is `string[][]`, so a
|
|
289
|
+
// consumer can tell which/how-many segments contributed.
|
|
283
290
|
export const Warnings = createHandle<string>();
|
|
291
|
+
|
|
292
|
+
// Want a single flat list instead? Opt in:
|
|
293
|
+
export const FlatWarnings = createHandle<string, string[]>((segments) =>
|
|
294
|
+
segments.flat(),
|
|
295
|
+
);
|
|
284
296
|
```
|
|
285
297
|
|
|
298
|
+
A handle whose module is never imported (so `createHandle()` never ran to register
|
|
299
|
+
its collect) falls back to this same identity default and **warns in dev** — a
|
|
300
|
+
handle with a custom collect that failed to register would otherwise return the
|
|
301
|
+
wrong shape silently, and the runtime can't tell it from one that wanted the default.
|
|
302
|
+
|
|
286
303
|
The Vite `exposeInternalIds` plugin auto-injects a stable `$$id` based on
|
|
287
304
|
file path and export name. No manual naming required for project-local code.
|
|
288
305
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cache-guide
|
|
3
|
-
description: When to use cache() DSL vs "use cache" directive — key differences and decision guide
|
|
3
|
+
description: When to use cache() DSL vs "use cache" directive — key differences and decision guide. Use when unsure which caching mechanism fits a given problem, comparing route/segment caching, function-level caching, and document-level caching, or asking "which cache API should I use".
|
|
4
4
|
argument-hint:
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -20,7 +20,9 @@ caching:
|
|
|
20
20
|
1. **Stored-value freshness** — _is a cached value still good?_
|
|
21
21
|
→ `"use cache"` (fn/component), `cache()` (segment), loader `cache()` (loader data).
|
|
22
22
|
Entries expire by **TTL/SWR** and can be tagged (`cache({ tags })` or runtime
|
|
23
|
-
`cacheTag(...tags)`
|
|
23
|
+
`cacheTag(...tags)` — inside `"use cache"` it tags that entry; called during a
|
|
24
|
+
request render outside `"use cache"` it tags the document/shell artifact).
|
|
25
|
+
Built-in stores (`MemorySegmentCacheStore`, `CFCacheStore`)
|
|
24
26
|
index by tag; invalidate on demand with `updateTag(...tags)` (awaitable,
|
|
25
27
|
read-your-own-writes) or `revalidateTag(...tags)` (background, non-blocking).
|
|
26
28
|
Both hard-purge; the difference is awaitability, not stale-serving.
|
|
@@ -484,3 +486,4 @@ overrides — see `/loader` for the full reference.
|
|
|
484
486
|
- `/caching` — cache() DSL setup, stores, nested boundaries
|
|
485
487
|
- `/use-cache` — "use cache" directive details, profiles, transforms, guards
|
|
486
488
|
- `/document-cache` — Edge caching with Cache-Control headers (different layer)
|
|
489
|
+
- `/ppr` — PPR shell caching: cached HTML shell + live loader holes (different layer)
|
package/skills/caching/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: caching
|
|
3
|
-
description: Configure segment caching with memory
|
|
4
|
-
argument-hint: [setup]
|
|
3
|
+
description: Configure route/segment-subtree caching with memory, Cloudflare KV, or Vercel cache stores in @rangojs/router. Use when responses should be cached or revalidated, data is stale or not updating after code changes, or you are wiring up a cache store.
|
|
4
|
+
argument-hint: "[setup]"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Caching
|
|
@@ -14,6 +14,16 @@ argument-hint: [setup]
|
|
|
14
14
|
> route-segment entries expire at TTL with no background revalidation — use
|
|
15
15
|
> `CFCacheStore` for real segment SWR. See `/cache-guide`.
|
|
16
16
|
|
|
17
|
+
## Not this skill if…
|
|
18
|
+
|
|
19
|
+
- You want to cache ONE function or component's return value — that is
|
|
20
|
+
`"use cache"`: see `/use-cache`.
|
|
21
|
+
- You want the whole HTTP response frozen at the edge, loader output included —
|
|
22
|
+
see `/document-cache`.
|
|
23
|
+
- You want the HTML shell cached while loaders stay live per request — see
|
|
24
|
+
`/ppr`.
|
|
25
|
+
- You are unsure which cache layer you need — start at `/cache-guide`.
|
|
26
|
+
|
|
17
27
|
## cache() is Partial Prerendering (PPR)
|
|
18
28
|
|
|
19
29
|
`cache()` caches **everything except loaders**. On a cache hit, the cached
|
|
@@ -81,9 +91,27 @@ cache(
|
|
|
81
91
|
);
|
|
82
92
|
```
|
|
83
93
|
|
|
94
|
+
## When cache() does not pay
|
|
95
|
+
|
|
96
|
+
A cache hit is not free: it still runs middleware, the store read, and the
|
|
97
|
+
document render AROUND the cached segment. The win is proportional to what
|
|
98
|
+
the cached render itself costs — measured on a deployed Cloudflare worker
|
|
99
|
+
(2026-07), a trivial page inside `cache()` served hits at p50 36 ms while
|
|
100
|
+
misses (render + store) served at 35 ms: indistinguishable. The same
|
|
101
|
+
boundary around an expensive render (slow data, big trees) is where the TTL
|
|
102
|
+
pays for itself.
|
|
103
|
+
|
|
104
|
+
Rule of thumb: reach for `cache()` when the segment's own render cost is
|
|
105
|
+
meaningfully above your latency floor — expensive render-embedded data work,
|
|
106
|
+
large component trees, third-party calls captured in the render. Do not wrap cheap
|
|
107
|
+
pages "just in case": you add store traffic and invalidation surface for no
|
|
108
|
+
latency win. If the data is what's expensive and it changes per-request,
|
|
109
|
+
prefer a loader with `cache()` on the loader DATA (see "Loader-Level
|
|
110
|
+
Caching") over caching the rendered segment.
|
|
111
|
+
|
|
84
112
|
## Tag-Based Invalidation
|
|
85
113
|
|
|
86
|
-
Tag cached entries, then invalidate them on demand. Tags can be attached
|
|
114
|
+
Tag cached entries, then invalidate them on demand. Tags can be attached four ways:
|
|
87
115
|
|
|
88
116
|
```typescript
|
|
89
117
|
// 1. Static tags in the cache() DSL
|
|
@@ -101,8 +129,22 @@ async function getProduct(id: string) {
|
|
|
101
129
|
cacheTag(`product:${id}`, "products"); // variadic, additive
|
|
102
130
|
return db.getProduct(id);
|
|
103
131
|
}
|
|
132
|
+
|
|
133
|
+
// 4. Render-callable — a plain server component (no "use cache" in its tree)
|
|
134
|
+
// records onto the request's document/shell artifact.
|
|
135
|
+
function CampaignBanner() {
|
|
136
|
+
cacheTag("campaign:spring"); // rides ctx._requestTags → shell/document entry
|
|
137
|
+
return <aside>Spring sale</aside>;
|
|
138
|
+
}
|
|
104
139
|
```
|
|
105
140
|
|
|
141
|
+
Form 4 is how you make a PPR shell or a `/document-cache` page tag-invalidatable
|
|
142
|
+
without wrapping anything in `"use cache"`: the tag rides the request's
|
|
143
|
+
`_requestTags` onto the shell/document entry, and `revalidateTag` then evicts it.
|
|
144
|
+
On a route that is neither PPR nor document-cached the tag records where nothing
|
|
145
|
+
reads it — a silent no-op, so don't expect a bare `cacheTag()` to tag an ordinary
|
|
146
|
+
uncached page.
|
|
147
|
+
|
|
106
148
|
Invalidate with one of two server-only verbs (both variadic, imported from
|
|
107
149
|
`@rangojs/router`):
|
|
108
150
|
|
|
@@ -143,7 +185,8 @@ converge within `tagCacheTtl` (the **maximum extra cross-colo invalidation
|
|
|
143
185
|
latency** when no purge is wired). Keep it small (e.g. 30–60), or wire a purge
|
|
144
186
|
(below) and set it large. (Contrast `tagInvalidationTtl`, which must be _large_
|
|
145
187
|
— it bounds how long the KV marker itself lives and must exceed your max entry
|
|
146
|
-
TTL+SWR.
|
|
188
|
+
TTL+SWR. Left unset there is no expiry: KV markers accumulate unbounded under
|
|
189
|
+
high-cardinality tags, so set it above your largest entry TTL+SWR to bound them.)
|
|
147
190
|
|
|
148
191
|
To make other colos prompt without a short `tagCacheTtl`, pass `onRevalidateTag`:
|
|
149
192
|
each cached marker carries a namespaced Cloudflare `Cache-Tag`, and the hook is
|
|
@@ -235,9 +278,15 @@ import { MemorySegmentCacheStore } from "@rangojs/router/cache";
|
|
|
235
278
|
|
|
236
279
|
const store = new MemorySegmentCacheStore({
|
|
237
280
|
defaults: { ttl: 60, swr: 300 },
|
|
281
|
+
maxEntries: 1000, // per-family FIFO cap (default 1000)
|
|
238
282
|
});
|
|
239
283
|
```
|
|
240
284
|
|
|
285
|
+
Each internal family (segments, responses, `"use cache"` items, PPR shells) is
|
|
286
|
+
capped at `maxEntries`; on insert past the cap the oldest entry is evicted FIFO
|
|
287
|
+
and its tag-index entries are cleaned up, so a long-lived process cannot grow
|
|
288
|
+
without bound. TTL expiry stays lazy on top of the cap.
|
|
289
|
+
|
|
241
290
|
### Cloudflare Edge Cache Store
|
|
242
291
|
|
|
243
292
|
For distributed caching on Cloudflare Workers using the Cache API:
|
|
@@ -401,6 +450,12 @@ is **not** guarded. `ctx.use()` is a server-side escape hatch for non-rendered
|
|
|
401
450
|
uses (set a ctx var, make a routing decision); never render its result inside a
|
|
402
451
|
cached handler.
|
|
403
452
|
|
|
453
|
+
This is the **consumption-lane rule**, and it holds identically for every
|
|
454
|
+
shared artifact — `cache()`, `"use cache"`, and the PPR shell (`/ppr`):
|
|
455
|
+
handler consumption = baked copy with identity reads permitted; client-side
|
|
456
|
+
`useLoader` = live. Stated once in `/rango` → Invariants; pinned by
|
|
457
|
+
semantic-matrix row PPR3 and the `e2e/cache.test.ts` "baked copy" case.
|
|
458
|
+
|
|
404
459
|
```typescript
|
|
405
460
|
// WRONG — throws: cookies() read directly in a cached handler
|
|
406
461
|
cache({ ttl: 60 }, () => [
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema-note": "Generated by tools/generate-skills-catalog.mjs — do not edit by hand.",
|
|
3
|
+
"package": "@rangojs/router",
|
|
4
|
+
"entry": "rango",
|
|
5
|
+
"skills": [
|
|
6
|
+
{
|
|
7
|
+
"name": "api-client",
|
|
8
|
+
"description": "Build a typed client for consuming your own response-route JSON APIs (no codegen). Use when calling your own JSON endpoints from another service or script, or you want typed fetch calls without a codegen step.",
|
|
9
|
+
"argumentHint": "",
|
|
10
|
+
"path": "skills/api-client/SKILL.md"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "breadcrumbs",
|
|
14
|
+
"description": "Built-in Breadcrumbs handle for accumulating breadcrumb navigation across route segments. Use when building a breadcrumb trail for nested routes, or asking how to show the current navigation path in a layout.",
|
|
15
|
+
"argumentHint": "[setup]",
|
|
16
|
+
"path": "skills/breadcrumbs/SKILL.md"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"name": "bundle-analysis",
|
|
20
|
+
"description": "Audit a Rango app's production bundle for server-side code leaking into the client, dev/prod React duplication, oversized chunks, and inefficient client-reference grouping. Use when investigating bundle size growth, before a production deploy, or when the client/SSR/RSC output suddenly balloons.",
|
|
21
|
+
"argumentHint": "<app-dir>",
|
|
22
|
+
"path": "skills/bundle-analysis/SKILL.md"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "cache-guide",
|
|
26
|
+
"description": "When to use cache() DSL vs \"use cache\" directive — key differences and decision guide. Use when unsure which caching mechanism fits a given problem, comparing route/segment caching, function-level caching, and document-level caching, or asking \"which cache API should I use\".",
|
|
27
|
+
"argumentHint": "",
|
|
28
|
+
"path": "skills/cache-guide/SKILL.md"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "caching",
|
|
32
|
+
"description": "Configure route/segment-subtree caching with memory, Cloudflare KV, or Vercel cache stores in @rangojs/router. Use when responses should be cached or revalidated, data is stale or not updating after code changes, or you are wiring up a cache store.",
|
|
33
|
+
"argumentHint": "[setup]",
|
|
34
|
+
"path": "skills/caching/SKILL.md"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "comparison",
|
|
38
|
+
"description": "Compare Rango with Next.js App Router, TanStack Start, and Waku. Use when evaluating React frameworks, explaining why Rango, writing positioning or adoption guidance, answering migration questions, or checking claims about Rango's routing, loaders, caching, rendering, prefetching, safety, testing, observability, and production behavior.",
|
|
39
|
+
"argumentHint": "",
|
|
40
|
+
"path": "skills/comparison/SKILL.md"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "composability",
|
|
44
|
+
"description": "Reusable composition patterns with globally importable route helpers in @rangojs/router. Use when sharing loaders, middleware, or handler logic across multiple route files, or avoiding copy-pasting the same route setup everywhere.",
|
|
45
|
+
"argumentHint": "pattern-name",
|
|
46
|
+
"path": "skills/composability/SKILL.md"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "css",
|
|
50
|
+
"description": "Import and apply CSS in a Rango app. Render document/app stylesheets in the Document `<head>` with Vite's `?url` import plus a `precedence`-managed `<link rel=\"stylesheet\">` (React 19 resource model — deduped, ordered, loaded before paint). Use when wiring global/app CSS or a Document `<head>` stylesheet, or when deciding between `?url` + `<link>` and side-effect imports. Cross-app (host-router) navigation is a full document load, so each app's document CSS is always re-established by its own load.",
|
|
51
|
+
"argumentHint": "",
|
|
52
|
+
"path": "skills/css/SKILL.md"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "debug-manifest",
|
|
56
|
+
"description": "Debug and inspect route manifest structure. Use when routes aren't matching as expected, you need to see the generated route tree, or a path resolves to the wrong handler.",
|
|
57
|
+
"argumentHint": "",
|
|
58
|
+
"path": "skills/debug-manifest/SKILL.md"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"name": "defer-hydration",
|
|
62
|
+
"description": "Keep the full body HTML in a PPR shell's first paint while moving a heavy subtree's hydration off the initial main-thread task — a gated Suspense boundary with the content as its own fallback, released on first idle. Use when a shell HIT paints fast but one long hydration task blocks the main thread, TTI/INP is poor despite instant paint, or a plain Suspense boundary left an empty hole in the frozen prelude.",
|
|
63
|
+
"argumentHint": "",
|
|
64
|
+
"path": "skills/defer-hydration/SKILL.md"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"name": "document-cache",
|
|
68
|
+
"description": "Cache the whole HTTP response at the edge with Cache-Control headers. Use when caching an entire page or response at a CDN edge, setting Cache-Control headers, or cutting origin hits for public pages — not for caching a single segment or function.",
|
|
69
|
+
"argumentHint": "[setup]",
|
|
70
|
+
"path": "skills/document-cache/SKILL.md"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "fonts",
|
|
74
|
+
"description": "Load and configure web fonts with preload hints for optimal performance. Use when adding a custom font to a Rango app, fonts flash or swap on load (FOUT/FOIT), or you want font preload for performance.",
|
|
75
|
+
"argumentHint": "[provider]",
|
|
76
|
+
"path": "skills/fonts/SKILL.md"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"name": "handler-use",
|
|
80
|
+
"description": "Attach default loaders, middleware, parallels, and other use items directly to handlers via handler.use, and compose them with explicit use() at mount sites. Use when a handler needs its own default middleware/loader without repeating use() at every mount site, or deciding between handler-level defaults and per-route use().",
|
|
81
|
+
"argumentHint": "[handler]",
|
|
82
|
+
"path": "skills/handler-use/SKILL.md"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"name": "hooks",
|
|
86
|
+
"description": "Client-side React hooks for navigation, loaders, and state in @rangojs/router. Use when a client component needs the current URL, params, search params, navigation state, or loader data — e.g. \"how do I read the route param in a component\".",
|
|
87
|
+
"argumentHint": "[hook-name]",
|
|
88
|
+
"path": "skills/hooks/SKILL.md"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"name": "host-router",
|
|
92
|
+
"description": "Multi-app host routing with domain/subdomain patterns. Use when running multiple apps behind one domain or across subdomains, or routing requests to different apps based on hostname.",
|
|
93
|
+
"argumentHint": "",
|
|
94
|
+
"path": "skills/host-router/SKILL.md"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"name": "i18n",
|
|
98
|
+
"description": "Locale-aware routing with `include(\"/:locale?\", ...)`, locale resolution chains, and react-intl integration. Use when building a multi-language app, routes need a locale segment, or wiring up react-intl translations.",
|
|
99
|
+
"argumentHint": "[topic]",
|
|
100
|
+
"path": "skills/i18n/SKILL.md"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "intercept",
|
|
104
|
+
"description": "Define intercept routes for modals, slide-overs, and soft navigation patterns in @rangojs/router. Use when opening a route as a modal/overlay on top of the current page while keeping the URL shareable, or asking \"how do I show this page in a modal\".",
|
|
105
|
+
"argumentHint": "[@slot-name] [route-to-intercept]",
|
|
106
|
+
"path": "skills/intercept/SKILL.md"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"name": "layout",
|
|
110
|
+
"description": "Define layout routes that wrap child routes in @rangojs/router. Use when sharing a persistent UI shell (nav, sidebar) across nested routes, or asking how to wrap child pages with a common layout.",
|
|
111
|
+
"argumentHint": "[component]",
|
|
112
|
+
"path": "skills/layout/SKILL.md"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "links",
|
|
116
|
+
"description": "URL generation with ctx.reverse (server default), href (client), useHref (mounted), useMount, useReverse, and scopedReverse. Use when generating a link to a route by name instead of hardcoding a path, or a link breaks after routes move or get mounted elsewhere.",
|
|
117
|
+
"argumentHint": "[ctx.reverse|href|useHref|useMount|useReverse|scopedReverse]",
|
|
118
|
+
"path": "skills/links/SKILL.md"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"name": "loader",
|
|
122
|
+
"description": "Define data loaders for fetching data in routes with createLoader. Use when pages need per-request data that stays fresh, data should stream while the page renders, or client components need reactive server data.",
|
|
123
|
+
"argumentHint": "[loader]",
|
|
124
|
+
"path": "skills/loader/SKILL.md"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"name": "middleware",
|
|
128
|
+
"description": "Define middleware for authentication, logging, and request processing in @rangojs/router. Use when gating routes behind auth checks, logging requests, or running shared logic before a handler runs.",
|
|
129
|
+
"argumentHint": "[middleware-name]",
|
|
130
|
+
"path": "skills/middleware/SKILL.md"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"name": "migrate-nextjs",
|
|
134
|
+
"description": "Migrate a Next.js App Router project to @rangojs/router. Use when the user asks to \"migrate from Next.js\", \"convert Next.js to Rango\", \"replace Next.js\", or has a Next.js app they want to port.",
|
|
135
|
+
"argumentHint": "[path-to-nextjs-app]",
|
|
136
|
+
"path": "skills/migrate-nextjs/SKILL.md"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"name": "migrate-react-router",
|
|
140
|
+
"description": "Migrate a React Router v7/v6 project to @rangojs/router. Use when the user asks to \"migrate from React Router\", \"convert React Router to Rango\", \"replace React Router\", \"move from Remix to Rango\", or has a React Router / Remix app they want to port.",
|
|
141
|
+
"argumentHint": "path-to-react-router-app",
|
|
142
|
+
"path": "skills/migrate-react-router/SKILL.md"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"name": "mime-routes",
|
|
146
|
+
"description": "Content negotiation — serve different response types (RSC, JSON, text, XML) from the same URL based on Accept header. Use when the same URL needs to return JSON for API clients and HTML/RSC for browsers, or branching a handler on the Accept header.",
|
|
147
|
+
"argumentHint": "[negotiate|vary|accept]",
|
|
148
|
+
"path": "skills/mime-routes/SKILL.md"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"name": "observability",
|
|
152
|
+
"description": "Debug Rango request performance with debugPerformance, Server-Timing, structured telemetry, and tracing. Use when a request feels slow and you need to see where time is spent, or wiring up tracing/telemetry for production requests.",
|
|
153
|
+
"argumentHint": "",
|
|
154
|
+
"path": "skills/observability/SKILL.md"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"name": "parallel",
|
|
158
|
+
"description": "Define parallel routes for multi-column layouts, sidebars, and modal slots in @rangojs/router. Use when a layout needs multiple independently-loading regions (e.g. a sidebar and main panel), or rendering more than one route segment at the same URL.",
|
|
159
|
+
"argumentHint": "[@slot-name]",
|
|
160
|
+
"path": "skills/parallel/SKILL.md"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"name": "ppr",
|
|
164
|
+
"description": "PPR shell caching — opt a page route in with the `ppr` path option; the router serves the cached HTML shell instantly and resumes the live holes. Use when a page should render instantly from a cached shell while specific parts stay live, or asking about partial prerendering in Rango.",
|
|
165
|
+
"argumentHint": "[setup]",
|
|
166
|
+
"path": "skills/ppr/SKILL.md"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"name": "prerender",
|
|
170
|
+
"description": "Pre-render route segments at build time with Prerender and Passthrough live fallback. Use when a page's content is mostly static and shouldn't render on every request, speeding up cold responses, or deciding which routes to prerender vs render live.",
|
|
171
|
+
"argumentHint": "[passthrough]",
|
|
172
|
+
"path": "skills/prerender/SKILL.md"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"name": "rango",
|
|
176
|
+
"description": "Overview of @rangojs/router and available skills. Use when unsure which skill to reach for, starting a new task in a Rango app, or asking \"what can this router do\".",
|
|
177
|
+
"argumentHint": "",
|
|
178
|
+
"path": "skills/rango/SKILL.md"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"name": "react-compiler",
|
|
182
|
+
"description": "Enable the React Compiler in a Rango app the @vitejs/plugin-rsc way — a separate @rolldown/plugin-babel running reactCompilerPreset(), ordered after react() and before the plugin that supplies @vitejs/plugin-rsc. Use when a consumer wants to turn React Compiler on, hits the dead plugin-react v6 `react({ babel })` path, or is unsure why server components aren't being compiled.",
|
|
183
|
+
"argumentHint": "",
|
|
184
|
+
"path": "skills/react-compiler/SKILL.md"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"name": "response-routes",
|
|
188
|
+
"description": "Response routes (path.json, path.text, etc.) for non-RSC endpoints with typed responses. Use when building a JSON/text API endpoint alongside your pages, or asking how to return raw JSON instead of RSC from a route.",
|
|
189
|
+
"argumentHint": "[json|text|html|xml|md|image|stream]",
|
|
190
|
+
"path": "skills/response-routes/SKILL.md"
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"name": "route",
|
|
194
|
+
"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.",
|
|
195
|
+
"argumentHint": "[pattern]",
|
|
196
|
+
"path": "skills/route/SKILL.md"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"name": "router-setup",
|
|
200
|
+
"description": "Create and configure the RSC router with createRouter. Use when bootstrapping a new Rango app, or configuring top-level router options like base path, cache store, or environment.",
|
|
201
|
+
"argumentHint": "[option]",
|
|
202
|
+
"path": "skills/router-setup/SKILL.md"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"name": "scripts",
|
|
206
|
+
"description": "Inject third-party scripts (GTM, analytics, widgets) into the document head/body via the Script handle. Use when adding Google Tag Manager, an analytics snippet, or a third-party widget script to the page.",
|
|
207
|
+
"argumentHint": "[vendor]",
|
|
208
|
+
"path": "skills/scripts/SKILL.md"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"name": "server-actions",
|
|
212
|
+
"description": "Define and call server actions (`\"use server\"`) — forms, useActionState, useOptimistic, validation, error handling, redirects, revalidation. Use when handling a form submission on the server, calling a server function from a client component, or needing optimistic UI after a mutation.",
|
|
213
|
+
"argumentHint": "[action]",
|
|
214
|
+
"path": "skills/server-actions/SKILL.md"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"name": "shell-manifest",
|
|
218
|
+
"description": "Shell manifest pattern — replayed handles as cache metadata that live loaders read, e.g. a prerendered product list with batched live prices. Use when a cached/prerendered shell needs to feed IDs or metadata to a live loader for freshly-fetched data.",
|
|
219
|
+
"argumentHint": "",
|
|
220
|
+
"path": "skills/shell-manifest/SKILL.md"
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"name": "streams-and-websockets",
|
|
224
|
+
"description": "Long-lived Response handlers — Server-Sent Events (SSE) via path.stream and WebSocket upgrades via path.any on Cloudflare Workers, including middleware interaction and runtime caveats. Use when streaming live updates to the browser, or opening a WebSocket connection from a route on Cloudflare Workers.",
|
|
225
|
+
"argumentHint": "[sse | websocket | agents]",
|
|
226
|
+
"path": "skills/streams-and-websockets/SKILL.md"
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"name": "tailwind",
|
|
230
|
+
"description": "Set up Tailwind CSS v4 with the Document component and CSS imports. Use when adding Tailwind CSS to a Rango app, or Tailwind classes aren't being applied or generated.",
|
|
231
|
+
"argumentHint": "[setup]",
|
|
232
|
+
"path": "skills/tailwind/SKILL.md"
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"name": "testing",
|
|
236
|
+
"description": "Test @rangojs/router apps — unit (loaders/middleware/reverse/components), integration (dispatch/Flight), and e2e (dev+prod parity, progressive enhancement). Use when writing a unit test for a loader or middleware, or asking how to test a route end-to-end in dev and production.",
|
|
237
|
+
"argumentHint": "[layer]",
|
|
238
|
+
"path": "skills/testing/SKILL.md"
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"name": "theme",
|
|
242
|
+
"description": "Opt-in theme system with FOUC prevention for light/dark mode. Use when adding a light/dark mode toggle, or the page flashes the wrong theme on load (FOUC).",
|
|
243
|
+
"argumentHint": "[setup]",
|
|
244
|
+
"path": "skills/theme/SKILL.md"
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"name": "typesafety",
|
|
248
|
+
"description": "Set up type-safe routes, params, and environment types in @rangojs/router. Use when route or search params aren't typed, TypeScript can't infer a loader's return type, or wiring up typed environment bindings.",
|
|
249
|
+
"argumentHint": "[setup]",
|
|
250
|
+
"path": "skills/typesafety/SKILL.md"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"name": "use-cache",
|
|
254
|
+
"description": "Function-level caching with the \"use cache\" directive for RSC data functions and components in @rangojs/router. Use when a single function or component should memoize its own output, not a whole route/segment subtree or HTTP response.",
|
|
255
|
+
"argumentHint": "[profile-name]",
|
|
256
|
+
"path": "skills/use-cache/SKILL.md"
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"name": "vercel",
|
|
260
|
+
"description": "Deploy a Rango app to Vercel Functions (Build Output API v3). Use when deploying a Rango app to Vercel, or asking how the router works with Vercel Functions and the Build Output API.",
|
|
261
|
+
"argumentHint": "",
|
|
262
|
+
"path": "skills/vercel/SKILL.md"
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"name": "view-transitions",
|
|
266
|
+
"description": "Configure React View Transitions on layouts, routes, and parallel slots in @rangojs/router. Use when navigation should animate smoothly between pages, or wiring up React View Transitions on a route or layout.",
|
|
267
|
+
"argumentHint": "[layout|route|parallel|intercept]",
|
|
268
|
+
"path": "skills/view-transitions/SKILL.md"
|
|
269
|
+
}
|
|
270
|
+
]
|
|
271
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: comparison
|
|
3
|
+
description: Compare Rango with Next.js App Router, TanStack Start, and Waku. Use when evaluating React frameworks, explaining why Rango, writing positioning or adoption guidance, answering migration questions, or checking claims about Rango's routing, loaders, caching, rendering, prefetching, safety, testing, observability, and production behavior.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Compare Rango with other React frameworks
|
|
7
|
+
|
|
8
|
+
Use the canonical [framework comparison](references/framework-comparison.md) as
|
|
9
|
+
the factual baseline. Read the sections relevant to the question; read the full
|
|
10
|
+
reference when producing an overall evaluation or editing the comparison itself.
|
|
11
|
+
|
|
12
|
+
## Comparison rules
|
|
13
|
+
|
|
14
|
+
1. Compare capabilities and architecture separately from ecosystem size, hiring,
|
|
15
|
+
integrations, and organizational familiarity.
|
|
16
|
+
2. Present Rango's range clearly: start with `path()` and a component, then add
|
|
17
|
+
named routes, `include()`, loaders, caching, `revalidate()`, slots, intercepts,
|
|
18
|
+
safety, and diagnostics in the same declared route graph.
|
|
19
|
+
3. Do not confuse Rango's `revalidate()` with cache invalidation. Cache APIs decide
|
|
20
|
+
stored-value freshness; `revalidate()` decides client render selection.
|
|
21
|
+
4. Describe loaders as live-by-default RSC data slots beneath cached or prerendered
|
|
22
|
+
UI, not as renamed Remix or TanStack route loaders.
|
|
23
|
+
5. Include production correctness where relevant: Rango State, userland client-cache
|
|
24
|
+
invalidation, deployment-skew recovery, tainted request context, CSP nonce
|
|
25
|
+
propagation, default CSRF origin checks, prefetch guards, testing, and timing.
|
|
26
|
+
6. State boundaries and tradeoffs. In particular, distinguish origin checking from
|
|
27
|
+
token-based CSRF protection, nonce plumbing from application-owned CSP policy,
|
|
28
|
+
and reload-based skew recovery from Vercel deployment pinning.
|
|
29
|
+
7. Credit competing frameworks where they lead. Next.js leads in ecosystem and
|
|
30
|
+
hiring, TanStack in search-param ergonomics and client/data devtools, and Waku
|
|
31
|
+
in minimal surface area.
|
|
32
|
+
|
|
33
|
+
## Currency and evidence
|
|
34
|
+
|
|
35
|
+
- Treat Rango identifiers and behavior as source-verifiable. Check the current
|
|
36
|
+
package source or the relevant Rango skill when changing a precise claim.
|
|
37
|
+
- Competitor details age quickly. Before publishing or materially revising a claim,
|
|
38
|
+
verify it against current official documentation and link that primary source.
|
|
39
|
+
- Do not weaken a current, sourced claim merely because an older model remembers a
|
|
40
|
+
previous release. Check first: examples include TanStack Start's Rsbuild support,
|
|
41
|
+
Next.js `proxy.ts` and Cache Components, and Waku handler interceptors.
|
|
42
|
+
- Avoid declaring a framework universally "better." State which model fits the
|
|
43
|
+
application's constraints and why.
|
|
44
|
+
|
|
45
|
+
## Output shape
|
|
46
|
+
|
|
47
|
+
Lead with the decision or differentiator. For a short answer, use the TL;DR and the
|
|
48
|
+
relevant framework-specific paragraph from the reference. For a decision memo,
|
|
49
|
+
cover the at-a-glance table, the simple-to-complex growth path, substantive runtime
|
|
50
|
+
differences, and the section where competitors still lead.
|