@rangojs/router 0.0.0-experimental.bd6e11bc → 0.0.0-experimental.bdaf10aa
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 +8 -4
- package/README.md +296 -887
- package/dist/bin/rango.js +459 -91
- package/dist/testing/vitest.js +36 -2
- package/dist/vite/index.js +1708 -414
- package/package.json +35 -10
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +82 -5
- package/skills/bundle-analysis/SKILL.md +2 -2
- package/skills/cache-guide/SKILL.md +14 -9
- package/skills/caching/SKILL.md +221 -12
- 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/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +5 -3
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +11 -3
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +9 -9
- package/skills/hooks/SKILL.md +73 -900
- 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 +84 -7
- package/skills/i18n/SKILL.md +1 -1
- package/skills/intercept/SKILL.md +51 -17
- package/skills/layout/SKILL.md +38 -16
- package/skills/links/SKILL.md +1 -1
- package/skills/loader/SKILL.md +48 -20
- package/skills/middleware/SKILL.md +11 -5
- package/skills/migrate-nextjs/SKILL.md +203 -20
- package/skills/migrate-react-router/SKILL.md +59 -675
- 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 +3 -3
- package/skills/observability/SKILL.md +70 -5
- package/skills/parallel/SKILL.md +32 -8
- package/skills/ppr/SKILL.md +622 -0
- package/skills/prerender/SKILL.md +59 -28
- package/skills/rango/SKILL.md +124 -50
- package/skills/response-routes/SKILL.md +78 -46
- package/skills/route/SKILL.md +85 -6
- package/skills/router-setup/SKILL.md +41 -6
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +28 -3
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +1 -1
- package/skills/tailwind/SKILL.md +28 -4
- package/skills/testing/SKILL.md +68 -654
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +131 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +85 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/theme/SKILL.md +1 -1
- package/skills/typesafety/SKILL.md +45 -918
- 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 +47 -17
- package/skills/vercel/SKILL.md +128 -0
- package/skills/view-transitions/SKILL.md +44 -1
- package/src/__augment-tests__/augmented.check.ts +2 -3
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +1 -1
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +14 -27
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +178 -100
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +81 -68
- package/src/browser/navigation-client.ts +115 -70
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +153 -88
- package/src/browser/navigation-transaction.ts +0 -32
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +157 -144
- package/src/browser/prefetch/cache.ts +148 -81
- package/src/browser/prefetch/fetch.ts +231 -51
- package/src/browser/prefetch/queue.ts +25 -7
- package/src/browser/rango-state.ts +157 -115
- package/src/browser/react/Link.tsx +40 -7
- package/src/browser/react/NavigationProvider.tsx +140 -99
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +17 -2
- package/src/browser/react/index.ts +0 -51
- package/src/browser/react/location-state-shared.ts +14 -15
- package/src/browser/react/location-state.ts +0 -1
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +0 -5
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +10 -5
- package/src/browser/react/use-params.ts +0 -2
- package/src/browser/react/use-router.ts +6 -4
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +0 -13
- package/src/browser/response-adapter.ts +74 -8
- package/src/browser/rsc-router.tsx +97 -22
- package/src/browser/scroll-restoration.ts +15 -8
- package/src/browser/segment-reconciler.ts +31 -21
- package/src/browser/server-action-bridge.ts +216 -38
- package/src/browser/types.ts +94 -22
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/generate-manifest.ts +155 -131
- package/src/build/generate-route-types.ts +1 -1
- package/src/build/index.ts +11 -5
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +152 -22
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +12 -1
- package/src/build/route-types/include-resolution.ts +455 -61
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +15 -2
- package/src/build/route-types/router-processing.ts +77 -41
- package/src/build/route-types/source-scan.ts +105 -7
- package/src/build/runtime-discovery.ts +4 -1
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +58 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +454 -101
- package/src/cache/cache-scope.ts +159 -54
- package/src/cache/cache-tag.ts +149 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +2170 -377
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +126 -41
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +243 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +13 -21
- package/src/cache/shell-snapshot.ts +417 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +194 -99
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1132 -0
- package/src/client.rsc.tsx +39 -22
- package/src/client.tsx +28 -58
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +108 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +13 -1
- package/src/decode-loader-results.ts +18 -2
- package/src/defer.ts +185 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +0 -3
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +57 -40
- package/src/handles/MetaTags.tsx +24 -53
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +35 -8
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +14 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +13 -22
- package/src/host/index.ts +7 -0
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +1 -65
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +6 -2
- package/src/href-client.ts +7 -12
- package/src/index.rsc.ts +88 -8
- package/src/index.ts +90 -16
- package/src/internal-debug.ts +11 -10
- package/src/loader.rsc.ts +19 -9
- package/src/loader.ts +12 -4
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +75 -7
- package/src/redirect-origin.ts +114 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +25 -0
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +13 -49
- package/src/route-definition/dsl-helpers.ts +60 -53
- package/src/route-definition/helper-factories.ts +0 -2
- package/src/route-definition/helpers-types.ts +46 -46
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +6 -1
- package/src/route-definition/use-item-types.ts +3 -6
- package/src/route-map-builder.ts +41 -20
- package/src/route-types.ts +0 -5
- package/src/router/content-negotiation.ts +58 -23
- package/src/router/error-handling.ts +44 -17
- package/src/router/find-match.ts +129 -30
- package/src/router/handler-context.ts +6 -1
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +35 -2
- package/src/router/lazy-includes.ts +79 -56
- package/src/router/loader-resolution.ts +151 -73
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +74 -40
- package/src/router/match-api.ts +76 -52
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +181 -178
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +115 -194
- package/src/router/match-middleware/cache-store.ts +61 -50
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +36 -67
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +0 -116
- package/src/router/middleware.ts +231 -120
- package/src/router/navigation-snapshot.ts +7 -56
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +99 -152
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +111 -66
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +47 -42
- package/src/router/revalidation.ts +75 -81
- package/src/router/route-snapshot.ts +14 -3
- package/src/router/router-context.ts +6 -29
- package/src/router/router-interfaces.ts +70 -8
- package/src/router/router-options.ts +126 -4
- package/src/router/segment-resolution/fresh.ts +104 -80
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +155 -39
- 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 +215 -304
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +35 -15
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +54 -35
- package/src/router/telemetry-otel.ts +160 -200
- package/src/router/telemetry.ts +9 -23
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +215 -0
- package/src/router/trie-matching.ts +171 -64
- package/src/router/types.ts +1 -63
- package/src/router/url-params.ts +13 -5
- package/src/router.ts +119 -48
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +1 -0
- package/src/rsc/handler.ts +267 -152
- package/src/rsc/helpers.ts +78 -4
- package/src/rsc/index.ts +1 -4
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +29 -42
- package/src/rsc/nonce.ts +10 -1
- package/src/rsc/origin-guard.ts +11 -15
- package/src/rsc/progressive-enhancement.ts +120 -13
- package/src/rsc/redirect-guard.ts +100 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +58 -141
- package/src/rsc/rsc-rendering.ts +492 -49
- package/src/rsc/runtime-warnings.ts +14 -0
- package/src/rsc/server-action.ts +268 -82
- 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 +45 -3
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +31 -26
- package/src/segment-loader-promise.ts +49 -4
- package/src/segment-system.tsx +260 -95
- package/src/server/context.ts +99 -9
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +125 -2
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +379 -138
- package/src/ssr/index.tsx +491 -182
- package/src/ssr/inject-rsc-eager.ts +167 -0
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +44 -48
- package/src/testing/collect-handle.ts +14 -31
- package/src/testing/dispatch.ts +533 -160
- package/src/testing/e2e/fixture.ts +45 -11
- package/src/testing/e2e/index.ts +1 -22
- package/src/testing/e2e/matchers.ts +0 -16
- package/src/testing/e2e/parity.ts +85 -4
- package/src/testing/e2e/server.ts +12 -0
- package/src/testing/flight-matchers.ts +7 -14
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +36 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +30 -0
- package/src/testing/flight.ts +145 -70
- package/src/testing/generated-routes.ts +26 -50
- package/src/testing/index.ts +18 -19
- package/src/testing/internal/context.ts +184 -68
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +357 -0
- package/src/testing/render-route.tsx +134 -115
- package/src/testing/run-loader.ts +140 -51
- package/src/testing/run-middleware.ts +59 -33
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +1 -1
- package/src/testing/vitest-stubs/cloudflare-workers.ts +1 -1
- package/src/testing/vitest.ts +138 -16
- package/src/theme/ThemeProvider.tsx +56 -84
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +15 -15
- package/src/types/handler-context.ts +45 -15
- package/src/types/index.ts +2 -10
- package/src/types/loader-types.ts +6 -3
- package/src/types/request-scope.ts +8 -22
- package/src/types/route-config.ts +20 -52
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +100 -13
- package/src/urls/include-helper.ts +10 -12
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +2 -8
- package/src/urls/path-helper-types.ts +52 -14
- package/src/urls/path-helper.ts +5 -54
- package/src/urls/pattern-types.ts +36 -0
- package/src/urls/type-extraction.ts +76 -42
- package/src/urls/urls-function.ts +0 -14
- package/src/use-loader.tsx +0 -186
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/discovery/bundle-postprocess.ts +2 -1
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +34 -43
- package/src/vite/discovery/discovery-errors.ts +61 -0
- package/src/vite/discovery/prerender-collection.ts +33 -46
- package/src/vite/discovery/state.ts +12 -1
- package/src/vite/discovery/virtual-module-codegen.ts +1 -11
- package/src/vite/index.ts +9 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +143 -10
- package/src/vite/plugins/cjs-to-esm.ts +8 -12
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +0 -10
- package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
- package/src/vite/plugins/expose-action-id.ts +2 -73
- package/src/vite/plugins/expose-id-utils.ts +85 -56
- package/src/vite/plugins/expose-ids/export-analysis.ts +30 -43
- package/src/vite/plugins/expose-ids/handler-transform.ts +5 -31
- package/src/vite/plugins/expose-ids/loader-transform.ts +12 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +98 -26
- package/src/vite/plugins/expose-internal-ids.ts +10 -1
- package/src/vite/plugins/performance-tracks.ts +0 -3
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +21 -46
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +22 -27
- package/src/vite/plugins/version-plugin.ts +6 -66
- package/src/vite/plugins/virtual-entries.ts +137 -26
- package/src/vite/rango.ts +146 -135
- package/src/vite/router-discovery.ts +189 -48
- package/src/vite/utils/ast-handler-extract.ts +11 -20
- package/src/vite/utils/bundle-analysis.ts +6 -13
- package/src/vite/utils/client-chunks.ts +0 -6
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +0 -22
- package/src/vite/utils/manifest-utils.ts +4 -75
- package/src/vite/utils/package-resolution.ts +1 -73
- package/src/vite/utils/prerender-utils.ts +71 -44
- package/src/vite/utils/shared-utils.ts +55 -37
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
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,10 +91,116 @@ cache(
|
|
|
81
91
|
);
|
|
82
92
|
```
|
|
83
93
|
|
|
84
|
-
##
|
|
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.
|
|
85
111
|
|
|
86
|
-
|
|
87
|
-
|
|
112
|
+
## Tag-Based Invalidation
|
|
113
|
+
|
|
114
|
+
Tag cached entries, then invalidate them on demand. Tags can be attached four ways:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// 1. Static tags in the cache() DSL
|
|
118
|
+
cache({ ttl: 300, tags: ["products"] }, () => [path("/products", List)]);
|
|
119
|
+
|
|
120
|
+
// 2. Dynamic tags (function of ctx)
|
|
121
|
+
cache(
|
|
122
|
+
{ ttl: 300, tags: (ctx) => [`product:${ctx.params.id}`, "products"] },
|
|
123
|
+
() => [path("/products/:id", Detail)],
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// 3. Runtime tags inside a "use cache" function
|
|
127
|
+
async function getProduct(id: string) {
|
|
128
|
+
"use cache";
|
|
129
|
+
cacheTag(`product:${id}`, "products"); // variadic, additive
|
|
130
|
+
return db.getProduct(id);
|
|
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
|
+
}
|
|
139
|
+
```
|
|
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
|
+
|
|
148
|
+
Invalidate with one of two server-only verbs (both variadic, imported from
|
|
149
|
+
`@rangojs/router`):
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// Server Action — read-your-own-writes. Await it so the action's own re-render
|
|
153
|
+
// (and the next navigation) sees fresh data.
|
|
154
|
+
async function updateProduct(formData: FormData) {
|
|
155
|
+
"use server";
|
|
156
|
+
await db.updateProduct(formData);
|
|
157
|
+
await updateTag("products");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Route handler / webhook — background, non-blocking (waitUntil). Hard-purge:
|
|
161
|
+
// the next read re-renders fresh (NOT stale-while-revalidate).
|
|
162
|
+
export async function POST() {
|
|
163
|
+
"use server";
|
|
164
|
+
revalidateTag("products");
|
|
165
|
+
return new Response("ok");
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
| API | Timing | Use in | Semantics |
|
|
170
|
+
| ------------------------ | --------------------------- | ------------------------- | ----------------------------------------------------- |
|
|
171
|
+
| `updateTag(...tags)` | awaitable (`Promise<void>`) | server actions | immediate; next read is fresh |
|
|
172
|
+
| `revalidateTag(...tags)` | background (`void`) | route handlers / webhooks | background (non-blocking); next read re-renders fresh |
|
|
173
|
+
|
|
174
|
+
Both built-in stores support tags. For `CFCacheStore`, distributed (cross-colo)
|
|
175
|
+
invalidation requires a `kv` namespace — the tag-invalidation markers live in
|
|
176
|
+
that same namespace; there is **no** separate tag-invalidation store to wire.
|
|
177
|
+
If no tag-capable store is configured, `updateTag`/`revalidateTag` warn and no-op.
|
|
178
|
+
|
|
179
|
+
By default `CFCacheStore` reads the KV marker on every tagged cache read
|
|
180
|
+
(strongest invalidation latency). To cut KV reads on hot tagged routes, set
|
|
181
|
+
`tagCacheTtl` (seconds) to cache each marker in the per-colo edge cache for that
|
|
182
|
+
window — the colo running `updateTag`/`revalidateTag` writes the fresh marker
|
|
183
|
+
into its own edge cache immediately (read-your-own-writes), while other colos
|
|
184
|
+
converge within `tagCacheTtl` (the **maximum extra cross-colo invalidation
|
|
185
|
+
latency** when no purge is wired). Keep it small (e.g. 30–60), or wire a purge
|
|
186
|
+
(below) and set it large. (Contrast `tagInvalidationTtl`, which must be _large_
|
|
187
|
+
— it bounds how long the KV marker itself lives and must exceed your max entry
|
|
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.)
|
|
190
|
+
|
|
191
|
+
To make other colos prompt without a short `tagCacheTtl`, pass `onRevalidateTag`:
|
|
192
|
+
each cached marker carries a namespaced Cloudflare `Cache-Tag`, and the hook is
|
|
193
|
+
handed exactly those tags (batched, once per `updateTag`/`revalidateTag` call) to
|
|
194
|
+
feed Cloudflare's purge-by-tag API — evicting the cached lookups everywhere.
|
|
195
|
+
Purge-by-tag is available on all plans (since April 2025), subject to per-plan
|
|
196
|
+
rate limits, so the batched single call matters. With a purge wired, `tagCacheTtl`
|
|
197
|
+
becomes a pure read-cost reducer + fallback window.
|
|
198
|
+
|
|
199
|
+
## Named Cache Profiles
|
|
200
|
+
|
|
201
|
+
Define named profiles in `createRouter({ cacheProfiles })` so the same TTL/SWR
|
|
202
|
+
values can be shared across the DSL and `"use cache"` functions without repetition.
|
|
203
|
+
Unknown names throw at boot time.
|
|
88
204
|
|
|
89
205
|
```typescript
|
|
90
206
|
// Define profiles in router
|
|
@@ -95,19 +211,25 @@ createRouter({
|
|
|
95
211
|
long: { ttl: 3600, swr: 7200 },
|
|
96
212
|
},
|
|
97
213
|
});
|
|
214
|
+
```
|
|
98
215
|
|
|
99
|
-
|
|
216
|
+
In the DSL, pass the profile's options directly to `cache()`:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
100
219
|
export const urlpatterns = urls(({ path, cache }) => [
|
|
101
|
-
cache(
|
|
220
|
+
cache({ ttl: 3600, swr: 7200 }, () => [
|
|
221
|
+
path("/blog", BlogIndex, { name: "blog" }),
|
|
222
|
+
]),
|
|
102
223
|
|
|
103
|
-
//
|
|
104
|
-
cache(
|
|
224
|
+
// Orphan cache boundary (covers subsequent siblings)
|
|
225
|
+
cache({ ttl: 60, swr: 120 }),
|
|
105
226
|
path("/feed", FeedPage, { name: "feed" }),
|
|
106
227
|
]);
|
|
107
228
|
```
|
|
108
229
|
|
|
109
|
-
|
|
110
|
-
|
|
230
|
+
The DSL `cache()` helper does NOT accept a string profile name — strings are only
|
|
231
|
+
valid in the `"use cache: <name>"` directive inside server functions. See
|
|
232
|
+
`/use-cache` for function-level caching with named profiles.
|
|
111
233
|
|
|
112
234
|
## Loader-Level Caching
|
|
113
235
|
|
|
@@ -156,9 +278,15 @@ import { MemorySegmentCacheStore } from "@rangojs/router/cache";
|
|
|
156
278
|
|
|
157
279
|
const store = new MemorySegmentCacheStore({
|
|
158
280
|
defaults: { ttl: 60, swr: 300 },
|
|
281
|
+
maxEntries: 1000, // per-family FIFO cap (default 1000)
|
|
159
282
|
});
|
|
160
283
|
```
|
|
161
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
|
+
|
|
162
290
|
### Cloudflare Edge Cache Store
|
|
163
291
|
|
|
164
292
|
For distributed caching on Cloudflare Workers using the Cache API:
|
|
@@ -212,6 +340,80 @@ const router = createRouter<AppBindings>({
|
|
|
212
340
|
KV entries require `expirationTtl >= 60s`. Short-lived entries (< 60s total TTL)
|
|
213
341
|
are only cached in L1.
|
|
214
342
|
|
|
343
|
+
### Resilience & latency budgets
|
|
344
|
+
|
|
345
|
+
Every cache read is **fail-safe**: a degraded tier never stalls or fails the
|
|
346
|
+
request — it degrades to the next tier (L1 → L2 → render). Three optional latency
|
|
347
|
+
budgets (milliseconds) bound each tier so a slow colo or KV namespace cannot pin
|
|
348
|
+
a request behind it:
|
|
349
|
+
|
|
350
|
+
| Option | Default | Bounds |
|
|
351
|
+
| --------------------- | ------- | ----------------------------------- |
|
|
352
|
+
| `edgeLookupTimeoutMs` | `10` | L1 `cache.match` (the lookup) |
|
|
353
|
+
| `edgeReadTimeoutMs` | `20` | L1 body read (CF streams it lazily) |
|
|
354
|
+
| `kvReadTimeoutMs` | `170` | L2 / KV read |
|
|
355
|
+
|
|
356
|
+
Set any to `0` (or a negative value) to disable that budget and always await the
|
|
357
|
+
read. A non-finite value (e.g. `Number(env.UNSET)`) falls back to the default.
|
|
358
|
+
The tag-invalidation marker reads inherit these same budgets and **fail open** on
|
|
359
|
+
a KV timeout — the entry is served rather than wrongly treated as invalidated.
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
new CFCacheStore({
|
|
363
|
+
ctx,
|
|
364
|
+
kv: env.CACHE_KV,
|
|
365
|
+
defaults: { ttl: 60, swr: 300 },
|
|
366
|
+
// Raise a budget only if your HEALTHY reads legitimately run slower (large
|
|
367
|
+
// Flight payloads, far-from-colo regions); measure the p99 first. These are
|
|
368
|
+
// degradation guard-rails, not tuning levers for "slow is normal here".
|
|
369
|
+
kvReadTimeoutMs: 250,
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Failure handling, by kind — none of these fail the request:
|
|
374
|
+
|
|
375
|
+
| Failure | Behavior |
|
|
376
|
+
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
377
|
+
| Transient read error (5xx/blip) | Degrade to the next tier; entry left intact |
|
|
378
|
+
| Read budget exceeded (timeout) | Abandon the read, degrade to the next tier |
|
|
379
|
+
| Corrupt / unparseable L1 entry | Reported corrupt; degrade to L2 (served if present). The L1 entry is evicted ONLY when L2 has no copy — so the evict can't race the L2→L1 promote |
|
|
380
|
+
| Corrupt / unparseable KV entry | Reported corrupt; evicted (self-heal) + render (no tier below it) |
|
|
381
|
+
| Write failure | No-op (entry simply not cached); never throws |
|
|
382
|
+
|
|
383
|
+
Each is surfaced to the router's `onError` callback (phase `"cache"`, with
|
|
384
|
+
`metadata.category` one of `cache-read`, `cache-corrupt`, `cache-write`,
|
|
385
|
+
`cache-delete`, `cache-invalidate`, `stale-revalidation`) so you can observe
|
|
386
|
+
cache health without affecting users.
|
|
387
|
+
|
|
388
|
+
### Validating cache behavior with `debug`
|
|
389
|
+
|
|
390
|
+
Pass `debug` to emit one structured event per L1 read — use it to confirm on a
|
|
391
|
+
real deployment (via `wrangler tail`) that the store behaves as expected before
|
|
392
|
+
relying on it. It is intended for validation, not steady-state production.
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
new CFCacheStore({
|
|
396
|
+
ctx,
|
|
397
|
+
kv: env.CACHE_KV,
|
|
398
|
+
debug: true, // logs each CFCacheReadDebugEvent to the console
|
|
399
|
+
// ...or capture programmatically:
|
|
400
|
+
// debug: (event) => myTelemetry.record(event),
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Each event reports which tier answered and why (`outcome`: `l1-fresh`,
|
|
405
|
+
`l1-stale-revalidate`, `l1-revalidating-guarded`, `match-timeout`, `match-error`,
|
|
406
|
+
`body-timeout`, `body-error`, `non-200`, `tag-invalidated`, `l1-miss`, `kv-fresh`,
|
|
407
|
+
`kv-stale`, `kv-stale-suppressed`, `kv-miss`, `kv-timeout`, `error`), the
|
|
408
|
+
staleness / revalidating timestamps, and the measured per-tier durations:
|
|
409
|
+
`matchMs` (the L1 `match`), `markerMs` (the tag-marker resolution tail for a
|
|
410
|
+
tagged entry, between `matchMs` and `bodyReadMs`; absent or 0 for an untagged
|
|
411
|
+
entry or a per-request memo hit), and `bodyReadMs` (the L1 body read). A
|
|
412
|
+
persistently large `markerMs` signals a degraded KV namespace; on a healthy
|
|
413
|
+
deployment KV keeps markers hot in its per-colo edge cache, so it stays a few
|
|
414
|
+
milliseconds. `match-error` (a transient `cache.match` rejection that falls
|
|
415
|
+
through to L2) is kept distinct from a plain `l1-miss`.
|
|
416
|
+
|
|
215
417
|
## Cache purity & tainted objects
|
|
216
418
|
|
|
217
419
|
A `cache()` boundary caches everything except loaders, so anything read inside a
|
|
@@ -248,6 +450,12 @@ is **not** guarded. `ctx.use()` is a server-side escape hatch for non-rendered
|
|
|
248
450
|
uses (set a ctx var, make a routing decision); never render its result inside a
|
|
249
451
|
cached handler.
|
|
250
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
|
+
|
|
251
459
|
```typescript
|
|
252
460
|
// WRONG — throws: cookies() read directly in a cached handler
|
|
253
461
|
cache({ ttl: 60 }, () => [
|
|
@@ -325,6 +533,7 @@ cache({ store: checkoutCache }, () => [
|
|
|
325
533
|
```typescript
|
|
326
534
|
import { urls } from "@rangojs/router";
|
|
327
535
|
import { MemorySegmentCacheStore } from "@rangojs/router/cache";
|
|
536
|
+
import * as CartActions from "./actions/cart";
|
|
328
537
|
|
|
329
538
|
// Custom store for checkout (short TTL)
|
|
330
539
|
const checkoutCache = new MemorySegmentCacheStore({
|
|
@@ -353,7 +562,7 @@ export const urlpatterns = urls(({ path, layout, cache, loader, revalidate }) =>
|
|
|
353
562
|
path("/shop/product/:slug", ProductPage, { name: "product" }, () => [
|
|
354
563
|
loader(ProductLoader, () => [cache({ ttl: 120 })]),
|
|
355
564
|
loader(CartLoader, () => [
|
|
356
|
-
revalidate((
|
|
565
|
+
revalidate((ctx) => ctx.isAction(CartActions) || undefined),
|
|
357
566
|
]),
|
|
358
567
|
]),
|
|
359
568
|
]),
|
|
@@ -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.
|