@rangojs/router 0.0.0-experimental.14 → 0.0.0-experimental.140
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 +17 -0
- package/README.md +432 -7
- package/dist/bin/rango.js +2073 -213
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +7258 -2714
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +140 -67
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +329 -0
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +487 -0
- package/skills/caching/SKILL.md +357 -25
- 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 +246 -0
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +16 -10
- package/skills/document-cache/SKILL.md +87 -62
- package/skills/fonts/SKILL.md +6 -4
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +557 -79
- package/skills/host-router/SKILL.md +320 -0
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +207 -15
- package/skills/layout/SKILL.md +146 -6
- package/skills/links/SKILL.md +304 -25
- package/skills/loader/SKILL.md +616 -54
- package/skills/middleware/SKILL.md +217 -37
- package/skills/migrate-nextjs/SKILL.md +611 -0
- package/skills/migrate-react-router/SKILL.md +927 -0
- package/skills/mime-routes/SKILL.md +42 -11
- package/skills/observability/SKILL.md +194 -0
- package/skills/parallel/SKILL.md +284 -3
- package/skills/ppr/SKILL.md +426 -0
- package/skills/prerender/SKILL.md +437 -52
- package/skills/rango/SKILL.md +369 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +263 -121
- package/skills/route/SKILL.md +350 -21
- package/skills/router-setup/SKILL.md +246 -33
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +126 -222
- 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 +9 -8
- package/skills/typesafety/SKILL.md +532 -103
- package/skills/use-cache/SKILL.md +367 -0
- package/skills/vercel/SKILL.md +128 -0
- package/skills/view-transitions/SKILL.md +337 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +77 -44
- package/src/bin/rango.ts +312 -15
- package/src/browser/action-coordinator.ts +114 -0
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +293 -202
- package/src/browser/history-state.ts +101 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +11 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +385 -576
- package/src/browser/navigation-client.ts +245 -75
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +184 -118
- package/src/browser/navigation-transaction.ts +247 -0
- package/src/browser/network-error-handler.ts +88 -0
- package/src/browser/partial-update.ts +412 -364
- package/src/browser/prefetch/cache.ts +359 -0
- package/src/browser/prefetch/fetch.ts +452 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +209 -0
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +194 -0
- package/src/browser/react/Link.tsx +275 -68
- package/src/browser/react/NavigationProvider.tsx +265 -109
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +11 -0
- package/src/browser/react/filter-segment-order.ts +70 -0
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +272 -60
- package/src/browser/react/location-state.ts +90 -20
- package/src/browser/react/mount-context.ts +6 -1
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +35 -66
- package/src/browser/react/use-handle.ts +39 -126
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +39 -13
- package/src/browser/react/use-navigation.ts +53 -69
- package/src/browser/react/use-params.ts +75 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +98 -0
- package/src/browser/react/use-search-params.ts +51 -0
- package/src/browser/react/use-segments.ts +72 -99
- package/src/browser/response-adapter.ts +164 -0
- package/src/browser/rsc-router.tsx +300 -72
- package/src/browser/scroll-restoration.ts +138 -50
- package/src/browser/segment-reconciler.ts +243 -0
- package/src/browser/segment-structure-assert.ts +17 -1
- package/src/browser/server-action-bridge.ts +668 -613
- package/src/browser/types.ts +223 -51
- package/src/browser/validate-redirect-origin.ts +56 -0
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +252 -161
- package/src/build/generate-route-types.ts +41 -1038
- package/src/build/index.ts +12 -7
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +225 -42
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +105 -0
- package/src/build/route-types/codegen.ts +113 -0
- package/src/build/route-types/include-resolution.ts +812 -0
- package/src/build/route-types/param-extraction.ts +51 -0
- package/src/build/route-types/per-module-writer.ts +144 -0
- package/src/build/route-types/router-processing.ts +695 -0
- package/src/build/route-types/scan-filter.ts +85 -0
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +223 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +60 -0
- package/src/cache/cache-policy.ts +199 -0
- package/src/cache/cache-runtime.ts +525 -0
- package/src/cache/cache-scope.ts +298 -332
- package/src/cache/cache-tag.ts +103 -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 +2500 -158
- 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 +17 -17
- package/src/cache/document-cache.ts +199 -92
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +111 -0
- package/src/cache/index.ts +29 -35
- package/src/cache/memory-segment-store.ts +363 -30
- package/src/cache/profile-registry.ts +88 -0
- package/src/cache/read-through-swr.ts +178 -0
- package/src/cache/segment-codec.ts +248 -0
- package/src/cache/shell-cache.ts +386 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +153 -0
- package/src/cache/types.ts +156 -211
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1102 -0
- package/src/client.rsc.tsx +43 -21
- package/src/client.tsx +131 -347
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +23 -4
- package/src/components/DefaultDocument.tsx +13 -3
- package/src/context-var.ts +168 -0
- package/src/debug.ts +19 -9
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +185 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +106 -10
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +110 -35
- package/src/handles/MetaTags.tsx +83 -59
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +93 -0
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +44 -53
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +20 -65
- package/src/host/errors.ts +21 -30
- package/src/host/index.ts +13 -9
- package/src/host/pattern-matcher.ts +50 -79
- package/src/host/router.ts +151 -121
- package/src/host/testing.ts +45 -32
- package/src/host/types.ts +52 -11
- package/src/host/utils.ts +2 -2
- package/src/href-client.ts +192 -57
- package/src/index.rsc.ts +177 -35
- package/src/index.ts +255 -71
- package/src/internal-debug.ts +9 -2
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +31 -99
- package/src/loader.ts +30 -12
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +41 -0
- package/src/prerender/param-hash.ts +16 -14
- package/src/prerender/store.ts +121 -21
- package/src/prerender.ts +460 -26
- package/src/redirect-origin.ts +100 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +62 -0
- package/src/reverse.ts +198 -128
- package/src/root-error-boundary.tsx +42 -48
- package/src/route-content-wrapper.tsx +22 -77
- package/src/route-definition/dsl-helpers.ts +1116 -0
- package/src/route-definition/helper-factories.ts +88 -0
- package/src/route-definition/helpers-types.ts +505 -0
- package/src/route-definition/index.ts +54 -0
- package/src/route-definition/redirect.ts +134 -0
- package/src/route-definition/resolve-handler-use.ts +160 -0
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-definition.ts +1 -1481
- package/src/route-map-builder.ts +82 -144
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +71 -45
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +263 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +54 -27
- package/src/router/find-match.ts +245 -0
- package/src/router/handler-context.ts +377 -125
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +59 -28
- package/src/router/lazy-includes.ts +254 -0
- package/src/router/loader-resolution.ts +421 -157
- package/src/router/logging.ts +106 -6
- package/src/router/manifest.ts +131 -57
- package/src/router/match-api.ts +167 -246
- package/src/router/match-context.ts +4 -24
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +117 -93
- package/src/router/match-middleware/cache-lookup.ts +297 -150
- package/src/router/match-middleware/cache-store.ts +123 -51
- package/src/router/match-middleware/intercept-resolution.ts +44 -43
- package/src/router/match-middleware/segment-resolution.ts +64 -22
- package/src/router/match-pipelines.ts +11 -87
- package/src/router/match-result.ts +121 -50
- package/src/router/metrics.ts +219 -28
- package/src/router/middleware-types.ts +93 -0
- package/src/router/middleware.ts +505 -441
- package/src/router/navigation-snapshot.ts +133 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +311 -142
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +547 -0
- package/src/router/preview-match.ts +102 -0
- package/src/router/request-classification.ts +278 -0
- package/src/router/revalidation.ts +203 -62
- package/src/router/route-snapshot.ts +246 -0
- package/src/router/router-context.ts +45 -48
- package/src/router/router-interfaces.ts +554 -0
- package/src/router/router-options.ts +779 -0
- package/src/router/router-registry.ts +21 -0
- package/src/router/segment-resolution/fresh.ts +772 -0
- package/src/router/segment-resolution/helpers.ts +348 -0
- package/src/router/segment-resolution/loader-cache.ts +250 -0
- package/src/router/segment-resolution/loader-mask.ts +44 -0
- package/src/router/segment-resolution/revalidation.ts +1331 -0
- package/src/router/segment-resolution/static-store.ts +81 -0
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +56 -0
- package/src/router/segment-resolution.ts +25 -1354
- package/src/router/segment-wrappers.ts +292 -0
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +75 -0
- package/src/router/telemetry-otel.ts +261 -0
- package/src/router/telemetry.ts +377 -0
- package/src/router/timeout.ts +128 -0
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +240 -61
- package/src/router/types.ts +23 -70
- package/src/router/url-params.ts +57 -0
- package/src/router.ts +781 -2378
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +46 -0
- package/src/rsc/handler.ts +905 -1142
- package/src/rsc/helpers.ts +275 -19
- package/src/rsc/index.ts +2 -25
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +305 -0
- package/src/rsc/manifest-init.ts +77 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +155 -0
- package/src/rsc/progressive-enhancement.ts +502 -0
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +104 -0
- package/src/rsc/response-route-handler.ts +257 -0
- package/src/rsc/rsc-rendering.ts +337 -0
- package/src/rsc/runtime-warnings.ts +55 -0
- package/src/rsc/server-action.ts +522 -0
- package/src/rsc/shell-capture.ts +439 -0
- package/src/rsc/ssr-setup.ts +144 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +95 -12
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +99 -82
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +149 -0
- package/src/segment-system.tsx +349 -134
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +452 -85
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +310 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +123 -42
- package/src/server/live.ts +130 -0
- package/src/server/loader-registry.ts +51 -100
- package/src/server/request-context.ts +842 -157
- package/src/server.ts +15 -8
- package/src/ssr/index.tsx +412 -136
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +45 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +46 -0
- package/src/testing/dispatch.ts +701 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +257 -0
- package/src/testing/generated-routes.ts +199 -0
- package/src/testing/index.ts +105 -0
- package/src/testing/internal/context.ts +371 -0
- 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 +584 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +40 -72
- package/src/theme/ThemeScript.tsx +12 -14
- package/src/theme/constants.ts +57 -15
- package/src/theme/index.ts +3 -20
- package/src/theme/theme-context.ts +5 -35
- package/src/theme/theme-script.ts +43 -39
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +123 -0
- package/src/types/cache-types.ts +207 -0
- package/src/types/error-types.ts +132 -0
- package/src/types/global-namespace.ts +113 -0
- package/src/types/handler-context.ts +839 -0
- package/src/types/index.ts +81 -0
- package/src/types/loader-types.ts +212 -0
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +138 -0
- package/src/types/route-entry.ts +114 -0
- package/src/types/segments.ts +271 -0
- package/src/types.ts +1 -1795
- package/src/urls/include-helper.ts +162 -0
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +43 -0
- package/src/urls/path-helper-types.ts +413 -0
- package/src/urls/path-helper.ts +275 -0
- package/src/urls/pattern-types.ts +124 -0
- package/src/urls/response-types.ts +109 -0
- package/src/urls/type-extraction.ts +316 -0
- package/src/urls/urls-function.ts +80 -0
- package/src/urls.ts +1 -1341
- package/src/use-loader.tsx +406 -141
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +182 -0
- package/src/vite/discovery/discover-routers.ts +389 -0
- package/src/vite/discovery/discovery-errors.ts +255 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +467 -0
- package/src/vite/discovery/route-types-writer.ts +214 -0
- package/src/vite/discovery/self-gen-tracking.ts +73 -0
- package/src/vite/discovery/state.ts +161 -0
- package/src/vite/discovery/virtual-module-codegen.ts +183 -0
- package/src/vite/index.ts +23 -2255
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +303 -0
- package/src/vite/plugins/cjs-to-esm.ts +90 -0
- package/src/vite/plugins/client-ref-dedup.ts +120 -0
- package/src/vite/plugins/client-ref-hashing.ts +118 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +88 -110
- package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +89 -79
- package/src/vite/plugins/expose-ids/export-analysis.ts +363 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +130 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +64 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +199 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +805 -0
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/use-cache-transform.ts +313 -0
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +94 -0
- package/src/vite/plugins/version-plugin.ts +263 -0
- package/src/vite/plugins/virtual-entries.ts +234 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +560 -0
- package/src/vite/router-discovery.ts +1638 -0
- package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +200 -37
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +132 -0
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +15 -0
- package/src/vite/utils/package-resolution.ts +89 -0
- package/src/vite/utils/prerender-utils.ts +249 -0
- package/src/vite/utils/shared-utils.ts +269 -0
- package/CLAUDE.md +0 -43
- package/dist/vite/index.named-routes.gen.ts +0 -103
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/react/use-client-cache.ts +0 -56
- package/src/browser/request-controller.ts +0 -164
- package/src/browser/shallow.ts +0 -35
- package/src/cache/memory-store.ts +0 -253
- package/src/handles/index.ts +0 -6
- package/src/href-context.ts +0 -33
- package/src/network-error-thrower.tsx +0 -21
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- package/src/vite/expose-internal-ids.ts +0 -1167
- package/src/vite/package-resolution.ts +0 -125
- package/src/vite/virtual-entries.ts +0 -114
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
package/skills/caching/SKILL.md
CHANGED
|
@@ -1,13 +1,53 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: caching
|
|
3
3
|
description: Configure segment caching with memory or Cloudflare KV stores in @rangojs/router
|
|
4
|
-
argument-hint: [setup]
|
|
4
|
+
argument-hint: "[setup]"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Caching
|
|
8
8
|
|
|
9
9
|
@rangojs/router supports segment-level caching with stale-while-revalidate (SWR) for optimal performance.
|
|
10
10
|
|
|
11
|
+
> SWR support is store-specific. `CFCacheStore` revalidates segment, response,
|
|
12
|
+
> and `"use cache"` entries in the background. `MemorySegmentCacheStore`
|
|
13
|
+
> supports SWR for response and `"use cache"` item entries, but its
|
|
14
|
+
> route-segment entries expire at TTL with no background revalidation — use
|
|
15
|
+
> `CFCacheStore` for real segment SWR. See `/cache-guide`.
|
|
16
|
+
|
|
17
|
+
## cache() is Partial Prerendering (PPR)
|
|
18
|
+
|
|
19
|
+
`cache()` caches **everything except loaders**. On a cache hit, the cached
|
|
20
|
+
segments (layouts, route components, parallels — including any resolved
|
|
21
|
+
Suspense) are served from the store, and **loaders re-run fresh on every
|
|
22
|
+
request**, streaming their results into the same response. Loaders are the
|
|
23
|
+
dynamic "holes" of an otherwise-cached tree.
|
|
24
|
+
|
|
25
|
+
This means a `cache()` boundary at the document root **is** whole-document
|
|
26
|
+
Partial Prerendering: the static shell is cached and served instantly while
|
|
27
|
+
per-request/per-user data stays live — in one streamed response, no extra round
|
|
28
|
+
trip. The browser cannot tell the shell came from cache.
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
cache({ ttl: 60, swr: 300 }, () => [
|
|
32
|
+
layout(<RootLayout />), // cached shell
|
|
33
|
+
path("/dashboard", Dashboard, { name: "dashboard" }, () => [
|
|
34
|
+
loader(StatsLoader), // DYNAMIC HOLE — re-runs every request
|
|
35
|
+
]),
|
|
36
|
+
]);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The consumer rule: **want it cached? render it inline. want it dynamic? put it
|
|
40
|
+
in a loader and read it with `useLoader()` in a client component.** Anything
|
|
41
|
+
read with `cookies()`, `headers()`, or a non-cacheable variable belongs in a
|
|
42
|
+
loader (loaders always run fresh). Reading it directly in a cached handler
|
|
43
|
+
throws; awaiting it with `ctx.use()` and rendering the result in a cached
|
|
44
|
+
handler silently bakes per-request data into the shared shell (see "Cache purity
|
|
45
|
+
& tainted objects" below).
|
|
46
|
+
|
|
47
|
+
Pre-rendering (`/prerender`) is the build-time twin: it caches the same shell at
|
|
48
|
+
build time instead of on first request. Both feed the segment system
|
|
49
|
+
identically, and loaders always run fresh at request time.
|
|
50
|
+
|
|
11
51
|
## Route-Level Caching with cache()
|
|
12
52
|
|
|
13
53
|
Use the `cache()` DSL function to cache routes:
|
|
@@ -30,14 +70,124 @@ export const urlpatterns = urls(({ path, cache }) => [
|
|
|
30
70
|
## Cache Options
|
|
31
71
|
|
|
32
72
|
```typescript
|
|
33
|
-
cache(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
73
|
+
cache(
|
|
74
|
+
{
|
|
75
|
+
ttl: 60, // Time-to-live in seconds (default: 60)
|
|
76
|
+
swr: 300, // Stale-while-revalidate window (default: 300)
|
|
77
|
+
},
|
|
78
|
+
() => [
|
|
79
|
+
// Cached routes
|
|
80
|
+
],
|
|
81
|
+
);
|
|
39
82
|
```
|
|
40
83
|
|
|
84
|
+
## Tag-Based Invalidation
|
|
85
|
+
|
|
86
|
+
Tag cached entries, then invalidate them on demand. Tags can be attached three ways:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// 1. Static tags in the cache() DSL
|
|
90
|
+
cache({ ttl: 300, tags: ["products"] }, () => [path("/products", List)]);
|
|
91
|
+
|
|
92
|
+
// 2. Dynamic tags (function of ctx)
|
|
93
|
+
cache(
|
|
94
|
+
{ ttl: 300, tags: (ctx) => [`product:${ctx.params.id}`, "products"] },
|
|
95
|
+
() => [path("/products/:id", Detail)],
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// 3. Runtime tags inside a "use cache" function
|
|
99
|
+
async function getProduct(id: string) {
|
|
100
|
+
"use cache";
|
|
101
|
+
cacheTag(`product:${id}`, "products"); // variadic, additive
|
|
102
|
+
return db.getProduct(id);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Invalidate with one of two server-only verbs (both variadic, imported from
|
|
107
|
+
`@rangojs/router`):
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// Server Action — read-your-own-writes. Await it so the action's own re-render
|
|
111
|
+
// (and the next navigation) sees fresh data.
|
|
112
|
+
async function updateProduct(formData: FormData) {
|
|
113
|
+
"use server";
|
|
114
|
+
await db.updateProduct(formData);
|
|
115
|
+
await updateTag("products");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Route handler / webhook — background, non-blocking (waitUntil). Hard-purge:
|
|
119
|
+
// the next read re-renders fresh (NOT stale-while-revalidate).
|
|
120
|
+
export async function POST() {
|
|
121
|
+
"use server";
|
|
122
|
+
revalidateTag("products");
|
|
123
|
+
return new Response("ok");
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
| API | Timing | Use in | Semantics |
|
|
128
|
+
| ------------------------ | --------------------------- | ------------------------- | ----------------------------------------------------- |
|
|
129
|
+
| `updateTag(...tags)` | awaitable (`Promise<void>`) | server actions | immediate; next read is fresh |
|
|
130
|
+
| `revalidateTag(...tags)` | background (`void`) | route handlers / webhooks | background (non-blocking); next read re-renders fresh |
|
|
131
|
+
|
|
132
|
+
Both built-in stores support tags. For `CFCacheStore`, distributed (cross-colo)
|
|
133
|
+
invalidation requires a `kv` namespace — the tag-invalidation markers live in
|
|
134
|
+
that same namespace; there is **no** separate tag-invalidation store to wire.
|
|
135
|
+
If no tag-capable store is configured, `updateTag`/`revalidateTag` warn and no-op.
|
|
136
|
+
|
|
137
|
+
By default `CFCacheStore` reads the KV marker on every tagged cache read
|
|
138
|
+
(strongest invalidation latency). To cut KV reads on hot tagged routes, set
|
|
139
|
+
`tagCacheTtl` (seconds) to cache each marker in the per-colo edge cache for that
|
|
140
|
+
window — the colo running `updateTag`/`revalidateTag` writes the fresh marker
|
|
141
|
+
into its own edge cache immediately (read-your-own-writes), while other colos
|
|
142
|
+
converge within `tagCacheTtl` (the **maximum extra cross-colo invalidation
|
|
143
|
+
latency** when no purge is wired). Keep it small (e.g. 30–60), or wire a purge
|
|
144
|
+
(below) and set it large. (Contrast `tagInvalidationTtl`, which must be _large_
|
|
145
|
+
— it bounds how long the KV marker itself lives and must exceed your max entry
|
|
146
|
+
TTL+SWR.)
|
|
147
|
+
|
|
148
|
+
To make other colos prompt without a short `tagCacheTtl`, pass `onRevalidateTag`:
|
|
149
|
+
each cached marker carries a namespaced Cloudflare `Cache-Tag`, and the hook is
|
|
150
|
+
handed exactly those tags (batched, once per `updateTag`/`revalidateTag` call) to
|
|
151
|
+
feed Cloudflare's purge-by-tag API — evicting the cached lookups everywhere.
|
|
152
|
+
Purge-by-tag is available on all plans (since April 2025), subject to per-plan
|
|
153
|
+
rate limits, so the batched single call matters. With a purge wired, `tagCacheTtl`
|
|
154
|
+
becomes a pure read-cost reducer + fallback window.
|
|
155
|
+
|
|
156
|
+
## Named Cache Profiles
|
|
157
|
+
|
|
158
|
+
Define named profiles in `createRouter({ cacheProfiles })` so the same TTL/SWR
|
|
159
|
+
values can be shared across the DSL and `"use cache"` functions without repetition.
|
|
160
|
+
Unknown names throw at boot time.
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// Define profiles in router
|
|
164
|
+
createRouter({
|
|
165
|
+
cacheProfiles: {
|
|
166
|
+
default: { ttl: 900, swr: 1800 },
|
|
167
|
+
short: { ttl: 60, swr: 120 },
|
|
168
|
+
long: { ttl: 3600, swr: 7200 },
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
In the DSL, pass the profile's options directly to `cache()`:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
export const urlpatterns = urls(({ path, cache }) => [
|
|
177
|
+
cache({ ttl: 3600, swr: 7200 }, () => [
|
|
178
|
+
path("/blog", BlogIndex, { name: "blog" }),
|
|
179
|
+
]),
|
|
180
|
+
|
|
181
|
+
// Orphan cache boundary (covers subsequent siblings)
|
|
182
|
+
cache({ ttl: 60, swr: 120 }),
|
|
183
|
+
path("/feed", FeedPage, { name: "feed" }),
|
|
184
|
+
]);
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The DSL `cache()` helper does NOT accept a string profile name — strings are only
|
|
188
|
+
valid in the `"use cache: <name>"` directive inside server functions. See
|
|
189
|
+
`/use-cache` for function-level caching with named profiles.
|
|
190
|
+
|
|
41
191
|
## Loader-Level Caching
|
|
42
192
|
|
|
43
193
|
Cache individual loaders:
|
|
@@ -45,13 +195,11 @@ Cache individual loaders:
|
|
|
45
195
|
```typescript
|
|
46
196
|
path("/product/:slug", ProductPage, { name: "product" }, () => [
|
|
47
197
|
// Cache this loader's results
|
|
48
|
-
loader(ProductLoader, () => [
|
|
49
|
-
cache({ ttl: 300 }),
|
|
50
|
-
]),
|
|
198
|
+
loader(ProductLoader, () => [cache({ ttl: 300 })]),
|
|
51
199
|
|
|
52
200
|
// This loader is not cached
|
|
53
201
|
loader(CartLoader),
|
|
54
|
-
])
|
|
202
|
+
]);
|
|
55
203
|
```
|
|
56
204
|
|
|
57
205
|
## Global Cache Configuration
|
|
@@ -60,7 +208,7 @@ Configure a cache store in the router:
|
|
|
60
208
|
|
|
61
209
|
```typescript
|
|
62
210
|
import { createRouter } from "@rangojs/router";
|
|
63
|
-
import { MemorySegmentCacheStore } from "@rangojs/router/
|
|
211
|
+
import { MemorySegmentCacheStore } from "@rangojs/router/cache";
|
|
64
212
|
|
|
65
213
|
const store = new MemorySegmentCacheStore({
|
|
66
214
|
defaults: { ttl: 60, swr: 300 },
|
|
@@ -83,34 +231,217 @@ const router = createRouter({
|
|
|
83
231
|
For single-instance deployments:
|
|
84
232
|
|
|
85
233
|
```typescript
|
|
86
|
-
import { MemorySegmentCacheStore } from "@rangojs/router/
|
|
234
|
+
import { MemorySegmentCacheStore } from "@rangojs/router/cache";
|
|
87
235
|
|
|
88
236
|
const store = new MemorySegmentCacheStore({
|
|
89
237
|
defaults: { ttl: 60, swr: 300 },
|
|
90
|
-
maxSize: 1000, // Max entries
|
|
91
238
|
});
|
|
92
239
|
```
|
|
93
240
|
|
|
94
|
-
### Cloudflare
|
|
241
|
+
### Cloudflare Edge Cache Store
|
|
95
242
|
|
|
96
|
-
For distributed caching on Cloudflare Workers:
|
|
243
|
+
For distributed caching on Cloudflare Workers using the Cache API:
|
|
97
244
|
|
|
98
245
|
```typescript
|
|
99
|
-
import { CFCacheStore } from "@rangojs/router/cache
|
|
246
|
+
import { CFCacheStore } from "@rangojs/router/cache";
|
|
100
247
|
|
|
101
|
-
const router = createRouter({
|
|
248
|
+
const router = createRouter<AppBindings>({
|
|
102
249
|
document: Document,
|
|
103
250
|
urls: urlpatterns,
|
|
104
|
-
cache: (env) => ({
|
|
251
|
+
cache: (env, ctx) => ({
|
|
105
252
|
store: new CFCacheStore({
|
|
106
|
-
|
|
107
|
-
|
|
253
|
+
ctx,
|
|
254
|
+
defaults: { ttl: 60, swr: 300 },
|
|
108
255
|
}),
|
|
109
256
|
enabled: true,
|
|
110
257
|
}),
|
|
111
258
|
});
|
|
112
259
|
```
|
|
113
260
|
|
|
261
|
+
### With KV L2 Persistence
|
|
262
|
+
|
|
263
|
+
Add a KV namespace for global cross-colo persistence. On Cache API miss, KV is
|
|
264
|
+
checked and hits are promoted back to L1. Writes go to both layers.
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
import { CFCacheStore } from "@rangojs/router/cache";
|
|
268
|
+
|
|
269
|
+
const router = createRouter<AppBindings>({
|
|
270
|
+
document: Document,
|
|
271
|
+
urls: urlpatterns,
|
|
272
|
+
cache: (env, ctx) => ({
|
|
273
|
+
store: new CFCacheStore({
|
|
274
|
+
ctx,
|
|
275
|
+
kv: env.CACHE_KV, // optional KV namespace binding
|
|
276
|
+
defaults: { ttl: 60, swr: 300 },
|
|
277
|
+
}),
|
|
278
|
+
enabled: true,
|
|
279
|
+
}),
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**How the two layers work:**
|
|
284
|
+
|
|
285
|
+
| Scenario | L1 (Cache API) | L2 (KV) | Result |
|
|
286
|
+
| ------------ | -------------- | ------- | ----------------------------- |
|
|
287
|
+
| Hot request | HIT | — | Serve from L1 (fast) |
|
|
288
|
+
| Cold colo | MISS | HIT | Serve from KV, promote to L1 |
|
|
289
|
+
| First render | MISS | MISS | Render, write to both L1 + KV |
|
|
290
|
+
|
|
291
|
+
KV entries require `expirationTtl >= 60s`. Short-lived entries (< 60s total TTL)
|
|
292
|
+
are only cached in L1.
|
|
293
|
+
|
|
294
|
+
### Resilience & latency budgets
|
|
295
|
+
|
|
296
|
+
Every cache read is **fail-safe**: a degraded tier never stalls or fails the
|
|
297
|
+
request — it degrades to the next tier (L1 → L2 → render). Three optional latency
|
|
298
|
+
budgets (milliseconds) bound each tier so a slow colo or KV namespace cannot pin
|
|
299
|
+
a request behind it:
|
|
300
|
+
|
|
301
|
+
| Option | Default | Bounds |
|
|
302
|
+
| --------------------- | ------- | ----------------------------------- |
|
|
303
|
+
| `edgeLookupTimeoutMs` | `10` | L1 `cache.match` (the lookup) |
|
|
304
|
+
| `edgeReadTimeoutMs` | `20` | L1 body read (CF streams it lazily) |
|
|
305
|
+
| `kvReadTimeoutMs` | `170` | L2 / KV read |
|
|
306
|
+
|
|
307
|
+
Set any to `0` (or a negative value) to disable that budget and always await the
|
|
308
|
+
read. A non-finite value (e.g. `Number(env.UNSET)`) falls back to the default.
|
|
309
|
+
The tag-invalidation marker reads inherit these same budgets and **fail open** on
|
|
310
|
+
a KV timeout — the entry is served rather than wrongly treated as invalidated.
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
new CFCacheStore({
|
|
314
|
+
ctx,
|
|
315
|
+
kv: env.CACHE_KV,
|
|
316
|
+
defaults: { ttl: 60, swr: 300 },
|
|
317
|
+
// Raise a budget only if your HEALTHY reads legitimately run slower (large
|
|
318
|
+
// Flight payloads, far-from-colo regions); measure the p99 first. These are
|
|
319
|
+
// degradation guard-rails, not tuning levers for "slow is normal here".
|
|
320
|
+
kvReadTimeoutMs: 250,
|
|
321
|
+
});
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Failure handling, by kind — none of these fail the request:
|
|
325
|
+
|
|
326
|
+
| Failure | Behavior |
|
|
327
|
+
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
328
|
+
| Transient read error (5xx/blip) | Degrade to the next tier; entry left intact |
|
|
329
|
+
| Read budget exceeded (timeout) | Abandon the read, degrade to the next tier |
|
|
330
|
+
| 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 |
|
|
331
|
+
| Corrupt / unparseable KV entry | Reported corrupt; evicted (self-heal) + render (no tier below it) |
|
|
332
|
+
| Write failure | No-op (entry simply not cached); never throws |
|
|
333
|
+
|
|
334
|
+
Each is surfaced to the router's `onError` callback (phase `"cache"`, with
|
|
335
|
+
`metadata.category` one of `cache-read`, `cache-corrupt`, `cache-write`,
|
|
336
|
+
`cache-delete`, `cache-invalidate`, `stale-revalidation`) so you can observe
|
|
337
|
+
cache health without affecting users.
|
|
338
|
+
|
|
339
|
+
### Validating cache behavior with `debug`
|
|
340
|
+
|
|
341
|
+
Pass `debug` to emit one structured event per L1 read — use it to confirm on a
|
|
342
|
+
real deployment (via `wrangler tail`) that the store behaves as expected before
|
|
343
|
+
relying on it. It is intended for validation, not steady-state production.
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
new CFCacheStore({
|
|
347
|
+
ctx,
|
|
348
|
+
kv: env.CACHE_KV,
|
|
349
|
+
debug: true, // logs each CFCacheReadDebugEvent to the console
|
|
350
|
+
// ...or capture programmatically:
|
|
351
|
+
// debug: (event) => myTelemetry.record(event),
|
|
352
|
+
});
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Each event reports which tier answered and why (`outcome`: `l1-fresh`,
|
|
356
|
+
`l1-stale-revalidate`, `l1-revalidating-guarded`, `match-timeout`, `match-error`,
|
|
357
|
+
`body-timeout`, `body-error`, `non-200`, `tag-invalidated`, `l1-miss`, `kv-fresh`,
|
|
358
|
+
`kv-stale`, `kv-stale-suppressed`, `kv-miss`, `kv-timeout`, `error`), the
|
|
359
|
+
staleness / revalidating timestamps, and the measured per-tier durations:
|
|
360
|
+
`matchMs` (the L1 `match`), `markerMs` (the tag-marker resolution tail for a
|
|
361
|
+
tagged entry, between `matchMs` and `bodyReadMs`; absent or 0 for an untagged
|
|
362
|
+
entry or a per-request memo hit), and `bodyReadMs` (the L1 body read). A
|
|
363
|
+
persistently large `markerMs` signals a degraded KV namespace; on a healthy
|
|
364
|
+
deployment KV keeps markers hot in its per-colo edge cache, so it stays a few
|
|
365
|
+
milliseconds. `match-error` (a transient `cache.match` rejection that falls
|
|
366
|
+
through to L2) is kept distinct from a plain `l1-miss`.
|
|
367
|
+
|
|
368
|
+
## Cache purity & tainted objects
|
|
369
|
+
|
|
370
|
+
A `cache()` boundary caches everything except loaders, so anything read inside a
|
|
371
|
+
cached handler is **frozen into the shared cache entry** and served to every
|
|
372
|
+
subsequent visitor. To stop one user's request-scoped data from leaking to
|
|
373
|
+
another, request-scoped APIs are guarded inside a cache scope:
|
|
374
|
+
|
|
375
|
+
| Inside a `cache()` boundary | Behavior |
|
|
376
|
+
| --------------------------------------------------------------- | --------------------------------------------------- |
|
|
377
|
+
| `cookies()` / `headers()` (read or write) | **throws** — request-scoped, would poison the entry |
|
|
378
|
+
| `ctx.header()` / `setCookie()` / `setStatus()` / `onResponse()` | **throws** — response side effects lost on a hit |
|
|
379
|
+
| `ctx.get(var)` where the var is `{ cache: false }` | **throws** on read |
|
|
380
|
+
| `ctx.set(var, value)` for a cacheable var | allowed (children are cached too) |
|
|
381
|
+
| Any of the above **inside a loader** | **allowed** — loaders always run fresh |
|
|
382
|
+
|
|
383
|
+
**Tainted objects.** Request-scoped objects (`ctx`, `env`, `request`) carry an
|
|
384
|
+
internal taint symbol so they are excluded from `"use cache"` cache keys, and
|
|
385
|
+
the cache scope is tracked via async-local state. Two flags back the guards:
|
|
386
|
+
`INSIDE_CACHE_EXEC` (set while a `"use cache"` function runs) and the `cache()`
|
|
387
|
+
DSL scope (`isInsideCacheScope()`). `isInsideCacheScope()` deliberately returns
|
|
388
|
+
`false` inside loaders — which is exactly why loaders are the dynamic holes:
|
|
389
|
+
they may read `cookies()`/`headers()` and re-run on every request.
|
|
390
|
+
|
|
391
|
+
The fix for "I need request data in a cached route": register a `loader()` and
|
|
392
|
+
**consume it with `useLoader()` in a client component**. The loader is the
|
|
393
|
+
dynamic hole — its data rides the fresh (never-cached) loader segment and is
|
|
394
|
+
rendered in the client component, so it never lands in the cached shell.
|
|
395
|
+
|
|
396
|
+
This is NOT the same as awaiting the loader in the handler. A cached handler
|
|
397
|
+
that does `await ctx.use(Loader)` and renders the result bakes that per-request
|
|
398
|
+
data straight into the shared cached segment — the loader running "fresh" does
|
|
399
|
+
not help, because its output was inlined into the cached parent, and `ctx.use()`
|
|
400
|
+
is **not** guarded. `ctx.use()` is a server-side escape hatch for non-rendered
|
|
401
|
+
uses (set a ctx var, make a routing decision); never render its result inside a
|
|
402
|
+
cached handler.
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
// WRONG — throws: cookies() read directly in a cached handler
|
|
406
|
+
cache({ ttl: 60 }, () => [
|
|
407
|
+
path("/me", () => <Profile id={cookies().get("uid")?.value} />),
|
|
408
|
+
]);
|
|
409
|
+
|
|
410
|
+
// ALSO WRONG (unguarded, but leaks) — the awaited loader data is rendered into
|
|
411
|
+
// the cached handler, so the user's data is frozen into the shared shell.
|
|
412
|
+
cache({ ttl: 60 }, () => [
|
|
413
|
+
path(
|
|
414
|
+
"/me",
|
|
415
|
+
async (ctx) => {
|
|
416
|
+
const { user } = await ctx.use(MeLoader); // runs fresh…
|
|
417
|
+
return <Profile user={user} />; // …but inlined into the CACHED segment → leak
|
|
418
|
+
},
|
|
419
|
+
{ name: "me" },
|
|
420
|
+
() => [loader(MeLoader)],
|
|
421
|
+
),
|
|
422
|
+
]);
|
|
423
|
+
|
|
424
|
+
// RIGHT — consume the loader in a CLIENT component via useLoader(). The cached
|
|
425
|
+
// route segment holds only the <Profile/> reference; the user data rides the
|
|
426
|
+
// fresh loader segment and renders client-side.
|
|
427
|
+
|
|
428
|
+
// profile.tsx (client component)
|
|
429
|
+
"use client";
|
|
430
|
+
import { useLoader } from "@rangojs/router/client";
|
|
431
|
+
|
|
432
|
+
export function Profile() {
|
|
433
|
+
const { user } = useLoader(MeLoader); // fresh per request; never cached
|
|
434
|
+
return <span>{user.name}</span>;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// urls — register the loader; MeLoader reads cookies() inside the loader (allowed)
|
|
438
|
+
cache({ ttl: 60 }, () => [
|
|
439
|
+
path("/me", () => <Profile />, { name: "me" }, () => [loader(MeLoader)]),
|
|
440
|
+
]);
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
See `/cache-guide` for the full decision guide and the `cache()` vs `"use cache"` comparison.
|
|
444
|
+
|
|
114
445
|
## Nested Cache Boundaries
|
|
115
446
|
|
|
116
447
|
Override cache settings for specific sections:
|
|
@@ -124,7 +455,7 @@ cache({ ttl: 300 }, () => [
|
|
|
124
455
|
cache({ ttl: 30 }, () => [
|
|
125
456
|
path("/blog/:slug", BlogPost, { name: "blogPost" }),
|
|
126
457
|
]),
|
|
127
|
-
])
|
|
458
|
+
]);
|
|
128
459
|
```
|
|
129
460
|
|
|
130
461
|
## Custom Cache Store
|
|
@@ -139,14 +470,15 @@ const checkoutCache = new MemorySegmentCacheStore({
|
|
|
139
470
|
// In urls
|
|
140
471
|
cache({ store: checkoutCache }, () => [
|
|
141
472
|
path("/checkout", CheckoutPage, { name: "checkout" }),
|
|
142
|
-
])
|
|
473
|
+
]);
|
|
143
474
|
```
|
|
144
475
|
|
|
145
476
|
## Complete Example
|
|
146
477
|
|
|
147
478
|
```typescript
|
|
148
479
|
import { urls } from "@rangojs/router";
|
|
149
|
-
import { MemorySegmentCacheStore } from "@rangojs/router/
|
|
480
|
+
import { MemorySegmentCacheStore } from "@rangojs/router/cache";
|
|
481
|
+
import * as CartActions from "./actions/cart";
|
|
150
482
|
|
|
151
483
|
// Custom store for checkout (short TTL)
|
|
152
484
|
const checkoutCache = new MemorySegmentCacheStore({
|
|
@@ -175,7 +507,7 @@ export const urlpatterns = urls(({ path, layout, cache, loader, revalidate }) =>
|
|
|
175
507
|
path("/shop/product/:slug", ProductPage, { name: "product" }, () => [
|
|
176
508
|
loader(ProductLoader, () => [cache({ ttl: 120 })]),
|
|
177
509
|
loader(CartLoader, () => [
|
|
178
|
-
revalidate((
|
|
510
|
+
revalidate((ctx) => ctx.isAction(CartActions) || undefined),
|
|
179
511
|
]),
|
|
180
512
|
]),
|
|
181
513
|
]),
|
|
@@ -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.
|