@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
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: composability
|
|
3
|
+
description: Reusable composition patterns with globally importable route helpers in @rangojs/router
|
|
4
|
+
argument-hint: "pattern-name"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Composability
|
|
8
|
+
|
|
9
|
+
Route helpers can be imported directly from `@rangojs/router` and used to build reusable composition factories. This enables sharing common route configurations across multiple routes and modules.
|
|
10
|
+
|
|
11
|
+
## Globally Importable Helpers
|
|
12
|
+
|
|
13
|
+
These helpers can be imported and called outside the `urls()` callback parameter:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import {
|
|
17
|
+
layout,
|
|
18
|
+
cache,
|
|
19
|
+
middleware,
|
|
20
|
+
revalidate,
|
|
21
|
+
loader,
|
|
22
|
+
loading,
|
|
23
|
+
parallel,
|
|
24
|
+
intercept,
|
|
25
|
+
errorBoundary,
|
|
26
|
+
notFoundBoundary,
|
|
27
|
+
} from "@rangojs/router";
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
They work because they use AsyncLocalStorage internally and resolve context at call time, not import time.
|
|
31
|
+
|
|
32
|
+
## Why path() and include() Are Not Global
|
|
33
|
+
|
|
34
|
+
`path()` and `include()` remain exclusive to the `urls()` callback:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
urls(({ path, include }) => [
|
|
38
|
+
path("/blog", BlogPage, { name: "blog" }),
|
|
39
|
+
include("/shop", shopPatterns, { name: "shop" }),
|
|
40
|
+
]);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
They define the route structure -- the URL patterns and how modules compose. Keeping them in the `urls()` callback makes the route tree readable at a glance. When scanning a URL file, `path()` and `include()` calls show what renders where. Moving them into factories would hide the routing structure and make it harder to understand which URLs exist and how they nest.
|
|
44
|
+
|
|
45
|
+
The globally importable helpers (`cache`, `middleware`, `loading`, etc.) are configuration -- they modify behavior of routes but don't define routes themselves. Extracting them into factories doesn't obscure the route structure.
|
|
46
|
+
|
|
47
|
+
## Composition Factories
|
|
48
|
+
|
|
49
|
+
Define reusable factories that return arrays of use items:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { cache, revalidate, loading, errorBoundary, middleware } from "@rangojs/router";
|
|
53
|
+
|
|
54
|
+
// Shared caching configuration
|
|
55
|
+
const withCaching = () => [
|
|
56
|
+
cache({ ttl: 600_000 }),
|
|
57
|
+
// Defer on navigation (|| undefined) so each route keeps its own param/search
|
|
58
|
+
// revalidation default; only force a re-run when an action ran.
|
|
59
|
+
revalidate(({ actionId }) => (actionId ? true : undefined)),
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
// Shared loading and error handling
|
|
63
|
+
const withLoadingAndError = (skeleton: ReactNode) => [
|
|
64
|
+
loading(skeleton),
|
|
65
|
+
errorBoundary(() => <div>Something went wrong</div>),
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
// Shared auth middleware
|
|
69
|
+
const withAuth = () => [
|
|
70
|
+
middleware(authMiddleware),
|
|
71
|
+
middleware(loggingMiddleware),
|
|
72
|
+
];
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
> **Factories compose logic, not just values.** A `revalidate()` predicate in a
|
|
76
|
+
> shared factory applies its logic to _every_ route that composes it, so a
|
|
77
|
+
> footgun here is amplified across the app. Two rules:
|
|
78
|
+
>
|
|
79
|
+
> 1. Use `|| undefined` (defer), not `?? false` (hard short-circuit), in shared
|
|
80
|
+
> predicates — a hard `false` ends the chain and overrides each consuming
|
|
81
|
+
> route's own default, and a downstream revalidator never runs. See `/loader`
|
|
82
|
+
> → "`|| undefined` (defer) vs `?? false` (hard)".
|
|
83
|
+
> 2. Match actions with `ctx.isAction(Action)`, not an inline
|
|
84
|
+
> `actionId.includes("…")` buried in a factory: it resolves the action from an
|
|
85
|
+
> imported reference, so a rename is a compile error in one place instead of
|
|
86
|
+
> silent drift across every consumer.
|
|
87
|
+
>
|
|
88
|
+
> Remember the axis: a factory's `revalidate()` controls client-update
|
|
89
|
+
> selection, while its `cache()` controls stored-value freshness. They are
|
|
90
|
+
> independent even when bundled in the same factory (`/cache-guide` → "Two axes").
|
|
91
|
+
|
|
92
|
+
> **Keep factories small and intention-named.** The anti-pattern that kills
|
|
93
|
+
> readability is over-bundling — a `withDefaults()` that secretly adds five
|
|
94
|
+
> things — and factory-of-factories nesting (leaning on `.flat(3)`). Surprising
|
|
95
|
+
> config stays inline; extract only the boring, repeated parts; compose by
|
|
96
|
+
> _naming concerns_ (`withAuth()`, `withCaching()`), not by hiding them.
|
|
97
|
+
|
|
98
|
+
## Using Factories in Routes
|
|
99
|
+
|
|
100
|
+
Place factory calls inside `path()` or `layout()` use callbacks. The returned arrays are flattened automatically (up to 3 levels):
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { urls } from "@rangojs/router";
|
|
104
|
+
import { withCaching, withLoadingAndError, withAuth } from "./route-config";
|
|
105
|
+
|
|
106
|
+
export const urlpatterns = urls(({ path, layout }) => [
|
|
107
|
+
layout(<AppLayout />, () => [
|
|
108
|
+
withAuth(),
|
|
109
|
+
|
|
110
|
+
path("/blog", BlogIndex, { name: "blog" }, () => [
|
|
111
|
+
withCaching(),
|
|
112
|
+
withLoadingAndError(<BlogSkeleton />),
|
|
113
|
+
]),
|
|
114
|
+
|
|
115
|
+
path("/shop", ShopIndex, { name: "shop" }, () => [
|
|
116
|
+
withCaching(),
|
|
117
|
+
withLoadingAndError(<ShopSkeleton />),
|
|
118
|
+
]),
|
|
119
|
+
]),
|
|
120
|
+
]);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Sharing Across Modules
|
|
124
|
+
|
|
125
|
+
Factories can be defined in shared modules and reused across separate `urls()` definitions:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// src/route-config.ts
|
|
129
|
+
import { cache, revalidate, middleware } from "@rangojs/router";
|
|
130
|
+
import { authMiddleware } from "./middleware/auth";
|
|
131
|
+
|
|
132
|
+
export const withPublicDefaults = () => [
|
|
133
|
+
cache({ ttl: 300 }),
|
|
134
|
+
revalidate(({ actionId }) => (actionId ? true : undefined)),
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
export const withProtectedDefaults = () => [
|
|
138
|
+
middleware(authMiddleware),
|
|
139
|
+
cache({ ttl: 60 }),
|
|
140
|
+
];
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// src/urls/blog.ts
|
|
145
|
+
import { urls } from "@rangojs/router";
|
|
146
|
+
import { withPublicDefaults } from "../route-config";
|
|
147
|
+
|
|
148
|
+
export const blogPatterns = urls(({ path }) => [
|
|
149
|
+
path("/", BlogIndex, { name: "index" }, () => [withPublicDefaults()]),
|
|
150
|
+
]);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// src/urls/admin.ts
|
|
155
|
+
import { urls } from "@rangojs/router";
|
|
156
|
+
import { withProtectedDefaults } from "../route-config";
|
|
157
|
+
|
|
158
|
+
export const adminPatterns = urls(({ path }) => [
|
|
159
|
+
path("/", AdminDashboard, { name: "index" }, () => [withProtectedDefaults()]),
|
|
160
|
+
]);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Code-splitting a route group with async include()
|
|
164
|
+
|
|
165
|
+
`include()` takes a route module two ways. Eager — the module is already in the
|
|
166
|
+
graph:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { shopPatterns } from "./shop-patterns";
|
|
170
|
+
|
|
171
|
+
urls(({ include }) => [include("/shop", shopPatterns, { name: "shop" })]);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Async — the module is code-split behind a `() => import()` thunk. It becomes its
|
|
175
|
+
own chunk that is NOT evaluated at startup; the router imports it on the first
|
|
176
|
+
request that reaches the prefix, then caches it:
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
urls(({ include }) => [
|
|
180
|
+
include("/shop", () => import("./shop-patterns"), { name: "shop" }),
|
|
181
|
+
]);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The split module exposes its `urls()` value as the default export (convention):
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
// src/shop-patterns.ts
|
|
188
|
+
import { urls } from "@rangojs/router";
|
|
189
|
+
|
|
190
|
+
export const shopPatterns = urls(({ path, include }) => [
|
|
191
|
+
path("/", ShopHome, { name: "home" }),
|
|
192
|
+
include("/product", productPatterns, { name: "product" }), // nesting is fine
|
|
193
|
+
]);
|
|
194
|
+
|
|
195
|
+
export default shopPatterns; // async include() resolves this
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Prefer the async form** for any route group that is a natural,
|
|
199
|
+
independently-loadable unit (a localized section, an admin area, an API surface
|
|
200
|
+
with heavy handlers) — it trims the eagerly-parsed entry bundle and keeps that
|
|
201
|
+
subgraph off the cold-start path. The **eager form is still fully valid** (not
|
|
202
|
+
deprecated): keep it for small groups, or ones that share most of their module
|
|
203
|
+
graph with the entry (the bundler keeps shared modules common regardless, so
|
|
204
|
+
splitting a thin group buys little). Both match identically at runtime — only the
|
|
205
|
+
module's runtime evaluation timing differs.
|
|
206
|
+
|
|
207
|
+
What you do NOT lose by splitting: build-time discovery `await`s the provider, so
|
|
208
|
+
`href()`, `reverse()`, generated route types, and prerender still see every route
|
|
209
|
+
in the group — including nested `include()`s inside the split module. Only the
|
|
210
|
+
module's runtime evaluation defers. `rango generate` resolves the `() => import()`
|
|
211
|
+
the same way, so a code-split group is still fully typed.
|
|
212
|
+
|
|
213
|
+
## Composition Types
|
|
214
|
+
|
|
215
|
+
For typed factories, import the composition types:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
import type { RouteUseItem, LayoutUseItem, UseItems } from "@rangojs/router";
|
|
219
|
+
|
|
220
|
+
// Factory for path() use callbacks
|
|
221
|
+
const withCaching = (): RouteUseItem[] => [
|
|
222
|
+
cache({ ttl: 600_000 }),
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
// Factory for layout() use callbacks
|
|
226
|
+
const withAuth = (): LayoutUseItem[] => [
|
|
227
|
+
middleware(authMiddleware),
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
// Factory that nests other factories (use UseItems for nested arrays)
|
|
231
|
+
const withEverything = (): UseItems<RouteUseItem> => [
|
|
232
|
+
withCaching(),
|
|
233
|
+
loading(<Skeleton />),
|
|
234
|
+
];
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
- `RouteUseItem[]` -- flat array for `path()` use callbacks
|
|
238
|
+
- `LayoutUseItem[]` -- flat array for `layout()` use callbacks
|
|
239
|
+
- `UseItems<T>` -- allows nested arrays from composing factories together
|
|
240
|
+
|
|
241
|
+
## Rules
|
|
242
|
+
|
|
243
|
+
- Helpers execute lazily -- factory functions are defined anywhere, but only called inside a `urls()` context (within `path()` or `layout()` use callbacks)
|
|
244
|
+
- Calling helpers outside a `urls()` context throws an error
|
|
245
|
+
- Nested arrays from factories are flattened automatically via `.flat(3)`
|
|
246
|
+
- `path()` and `include()` cannot be used in factories -- they define route structure and must remain visible in the `urls()` callback
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: css
|
|
3
|
+
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.
|
|
4
|
+
argument-hint:
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# CSS imports
|
|
8
|
+
|
|
9
|
+
Document/app CSS in Rango lives in the Document `<head>`, loaded with Vite's
|
|
10
|
+
`?url` import and a `precedence`-managed `<link rel="stylesheet">`. This page is
|
|
11
|
+
the why and the one cross-app caveat; `/tailwind` is the concrete setup, `/theme`
|
|
12
|
+
is dark mode, `/fonts` is fonts.
|
|
13
|
+
|
|
14
|
+
## The pattern
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
// document.tsx
|
|
18
|
+
"use client";
|
|
19
|
+
|
|
20
|
+
import type { ReactNode } from "react";
|
|
21
|
+
import { MetaTags } from "@rangojs/router/client";
|
|
22
|
+
import styles from "./index.css?url";
|
|
23
|
+
|
|
24
|
+
export function Document({ children }: { children: ReactNode }) {
|
|
25
|
+
return (
|
|
26
|
+
<html lang="en">
|
|
27
|
+
<head>
|
|
28
|
+
<link rel="preload" href={styles} as="style" precedence="default" />
|
|
29
|
+
<link rel="stylesheet" href={styles} precedence="default" />
|
|
30
|
+
<MetaTags />
|
|
31
|
+
</head>
|
|
32
|
+
<body>{children}</body>
|
|
33
|
+
</html>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
- **`?url`** returns the processed file's hashed URL instead of injecting it as a
|
|
39
|
+
side effect, giving a stable asset path that works in dev and production.
|
|
40
|
+
- **`precedence`** opts the `<link rel="stylesheet">` into React 19's managed
|
|
41
|
+
stylesheet model: React de-duplicates it by `href`, orders it by precedence
|
|
42
|
+
(any string value — it only decides cascade order relative to other managed
|
|
43
|
+
sheets), and loads it **before paint**, so there is no flash of unstyled
|
|
44
|
+
content. This is the recommended way to render a stylesheet link.
|
|
45
|
+
|
|
46
|
+
## Cross-app (host-router) navigation
|
|
47
|
+
|
|
48
|
+
You do **not** need to coordinate CSS across apps mounted under one host router.
|
|
49
|
+
A client-side navigation that crosses an app boundary is a **full document load**
|
|
50
|
+
(the server returns `X-RSC-Reload` on an app switch — see `/host-router`), so the
|
|
51
|
+
target app's entire document — its stylesheets, theme, meta — is re-established
|
|
52
|
+
by the target app's own load. Each app owns its document; how one app renders a
|
|
53
|
+
stylesheet has no effect on another.
|
|
54
|
+
|
|
55
|
+
(This replaced an earlier soft cross-app swap. Under it, a stylesheet shared
|
|
56
|
+
across apps by `href` — classically every app's `@import "tailwindcss"` compiling
|
|
57
|
+
to one hashed asset — could be silently dropped by React's by-`href` resource
|
|
58
|
+
dedup when the apps disagreed on `precedence` (one unmanaged, one managed). The
|
|
59
|
+
full reload removes that footgun entirely, which is the main reason cross-app
|
|
60
|
+
navigation is a hard boundary.)
|
|
61
|
+
|
|
62
|
+
## Side-effect imports vs `?url`
|
|
63
|
+
|
|
64
|
+
A bare `import "./index.css"` (no `?url`, no `<link>`) also produces _managed_ CSS
|
|
65
|
+
— `@vitejs/plugin-rsc` collects it via `import.meta.viteRsc.loadCss` and injects
|
|
66
|
+
it with a precedence. It is fine for **component-local** CSS that loads with its
|
|
67
|
+
client chunk. For **document-level** CSS, prefer the `?url` + `<link precedence>`
|
|
68
|
+
form above: a side-effect import is not guaranteed to be in the initial streamed
|
|
69
|
+
`<head>` (an SSR-streaming caveat), whereas the explicit `<link>` is.
|
|
70
|
+
|
|
71
|
+
## Related
|
|
72
|
+
|
|
73
|
+
- `/tailwind` — Tailwind v4 setup using this pattern.
|
|
74
|
+
- `/host-router` — multi-app routing; why cross-app navigation is a full reload.
|
|
75
|
+
- `/theme` — dark mode / theme attribute.
|
|
76
|
+
- `/fonts` — self-hosted fonts via `@fontsource`.
|
|
@@ -11,11 +11,12 @@ Inspect the route manifest to verify parent relationships, shortCodes, and route
|
|
|
11
11
|
## Quick Access
|
|
12
12
|
|
|
13
13
|
In development, visit:
|
|
14
|
+
|
|
14
15
|
```
|
|
15
|
-
http://localhost:PORT
|
|
16
|
+
http://localhost:PORT/?__debug_manifest
|
|
16
17
|
```
|
|
17
18
|
|
|
18
|
-
Returns formatted JSON
|
|
19
|
+
Returns formatted JSON. The HTTP endpoint shape is `{ routerId, routeManifest, routeAncestry, routeTrie, precomputedEntries }` (see below for the programmatic API shape).
|
|
19
20
|
|
|
20
21
|
## Programmatic Access
|
|
21
22
|
|
|
@@ -31,6 +32,8 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
31
32
|
|
|
32
33
|
## Manifest Structure
|
|
33
34
|
|
|
35
|
+
The programmatic `router.debugManifest()` call returns `{ routes, layouts, totalRoutes, totalLayouts }`:
|
|
36
|
+
|
|
34
37
|
```json
|
|
35
38
|
{
|
|
36
39
|
"routes": {
|
|
@@ -62,13 +65,13 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
62
65
|
|
|
63
66
|
## ShortCode Format
|
|
64
67
|
|
|
65
|
-
| Prefix | Meaning
|
|
66
|
-
|
|
67
|
-
| **M**
|
|
68
|
-
| **L**
|
|
69
|
-
| **C**
|
|
70
|
-
| **R**
|
|
71
|
-
| **P**
|
|
68
|
+
| Prefix | Meaning |
|
|
69
|
+
| ------ | ---------------------------------------- |
|
|
70
|
+
| **M** | Mount index (multiple `.routes()` calls) |
|
|
71
|
+
| **L** | Layout |
|
|
72
|
+
| **C** | Cache boundary |
|
|
73
|
+
| **R** | Route |
|
|
74
|
+
| **P** | Parallel slot |
|
|
72
75
|
|
|
73
76
|
Example: `M0L0L1C0R0` = Mount 0 → Root Layout → Nested Layout → Cache → Route
|
|
74
77
|
|
|
@@ -85,7 +88,7 @@ Example: `M0L0L1C0R0` = Mount 0 → Root Layout → Nested Layout → Cache →
|
|
|
85
88
|
import {
|
|
86
89
|
serializeManifest,
|
|
87
90
|
compareManifests,
|
|
88
|
-
formatManifestDiff
|
|
91
|
+
formatManifestDiff,
|
|
89
92
|
} from "@rangojs/router/__internal";
|
|
90
93
|
|
|
91
94
|
const oldManifest = await router.debugManifest();
|
|
@@ -99,10 +102,13 @@ console.log(formatManifestDiff(diff));
|
|
|
99
102
|
## Common Issues
|
|
100
103
|
|
|
101
104
|
### Routes have `parentShortCode: null`
|
|
105
|
+
|
|
102
106
|
Routes should have a layout parent. Check that `urls()` handler is being wrapped in root layout.
|
|
103
107
|
|
|
104
108
|
### Missing layouts in hierarchy
|
|
109
|
+
|
|
105
110
|
Verify `layout()` calls wrap child routes correctly.
|
|
106
111
|
|
|
107
112
|
### Wrong mount index
|
|
113
|
+
|
|
108
114
|
Multiple `.routes()` calls create separate mounts (M0, M1, etc.). Use `include()` to share context.
|
|
@@ -10,73 +10,82 @@ Caches complete HTTP responses (HTML/RSC) at the edge based on Cache-Control hea
|
|
|
10
10
|
|
|
11
11
|
## Setup
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Document caching is a middleware. Add `createDocumentCacheMiddleware()` to the
|
|
14
|
+
router with `.use()`. The cache store it reads from is the app-level store you
|
|
15
|
+
configure on `createRouter({ cache })` (available on the request context as
|
|
16
|
+
`requestCtx._cacheStore`), not a store passed to the middleware.
|
|
14
17
|
|
|
15
18
|
```typescript
|
|
16
19
|
import { createRouter } from "@rangojs/router";
|
|
17
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
createDocumentCacheMiddleware,
|
|
22
|
+
CFCacheStore,
|
|
23
|
+
} from "@rangojs/router/cache";
|
|
18
24
|
import { urlpatterns } from "./urls";
|
|
19
25
|
|
|
20
|
-
const router = createRouter<
|
|
26
|
+
const router = createRouter<AppBindings>({
|
|
21
27
|
document: Document,
|
|
22
28
|
urls: urlpatterns,
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
// App-level cache store. The document cache middleware uses this store's
|
|
30
|
+
// getResponse/putResponse methods.
|
|
31
|
+
cache: (_env, ctx) => ({ store: new CFCacheStore({ ctx: ctx! }) }),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
router.use(
|
|
35
|
+
createDocumentCacheMiddleware({
|
|
25
36
|
skipPaths: ["/api", "/admin"],
|
|
26
37
|
debug: process.env.NODE_ENV === "development",
|
|
27
38
|
}),
|
|
28
|
-
|
|
39
|
+
);
|
|
29
40
|
|
|
30
41
|
export default router;
|
|
31
42
|
```
|
|
32
43
|
|
|
33
|
-
## Route Opt-In with
|
|
44
|
+
## Route Opt-In with Cache-Control
|
|
34
45
|
|
|
35
|
-
Routes opt-in to document caching
|
|
46
|
+
Routes opt-in to document caching by setting a `Cache-Control` response header
|
|
47
|
+
with `s-maxage`. The middleware caches responses whose `Cache-Control` includes
|
|
48
|
+
`s-maxage`; `stale-while-revalidate` enables background revalidation (SWR).
|
|
36
49
|
|
|
37
50
|
```typescript
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// No cache for dashboard (no documentCache option)
|
|
52
|
-
path("/dashboard", Dashboard, { name: "dashboard" }),
|
|
53
|
-
]);
|
|
51
|
+
// Cache full page for 5 min, serve stale for 1 hour
|
|
52
|
+
function BlogIndexHandler(ctx) {
|
|
53
|
+
ctx.headers.set("Cache-Control", "s-maxage=300, stale-while-revalidate=3600");
|
|
54
|
+
return <BlogIndex />;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Long cache for individual posts
|
|
58
|
+
function BlogPostHandler(ctx) {
|
|
59
|
+
ctx.headers.set("Cache-Control", "s-maxage=3600, stale-while-revalidate=86400");
|
|
60
|
+
return <BlogPost />;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Dashboard sets no Cache-Control header, so it is never document-cached.
|
|
54
64
|
```
|
|
55
65
|
|
|
56
66
|
## Document Cache Options
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
createRouter({
|
|
60
|
-
// ...
|
|
61
|
-
documentCache: (env) => ({
|
|
62
|
-
// Cache store (required)
|
|
63
|
-
store: new CFCacheStore({ ctx: env.ctx }),
|
|
68
|
+
`createDocumentCacheMiddleware(options?)` accepts:
|
|
64
69
|
|
|
65
|
-
|
|
66
|
-
|
|
70
|
+
```typescript
|
|
71
|
+
createDocumentCacheMiddleware({
|
|
72
|
+
// Skip specific paths (matched by pathname prefix)
|
|
73
|
+
skipPaths: ["/api", "/admin"],
|
|
67
74
|
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
// Custom cache key generator
|
|
76
|
+
keyGenerator: (url) => url.pathname,
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
// Conditional caching, evaluated per request
|
|
79
|
+
isEnabled: (ctx) => !ctx.request.headers.has("x-preview"),
|
|
73
80
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}),
|
|
81
|
+
// Debug logging (HIT, MISS, STALE, REVALIDATED)
|
|
82
|
+
debug: true,
|
|
77
83
|
});
|
|
78
84
|
```
|
|
79
85
|
|
|
86
|
+
The cache store is not a middleware option — it comes from the app-level
|
|
87
|
+
`createRouter({ cache })` store.
|
|
88
|
+
|
|
80
89
|
## How It Works
|
|
81
90
|
|
|
82
91
|
```
|
|
@@ -89,7 +98,7 @@ Request → Check Cache
|
|
|
89
98
|
↓ ↓
|
|
90
99
|
Fresh? Run handler
|
|
91
100
|
│ │
|
|
92
|
-
Yes → Return Has
|
|
101
|
+
Yes → Return Has s-maxage?
|
|
93
102
|
│ │
|
|
94
103
|
No (stale) Yes → Cache + Return
|
|
95
104
|
│ │
|
|
@@ -102,6 +111,7 @@ Request → Check Cache
|
|
|
102
111
|
## Cache Status Header
|
|
103
112
|
|
|
104
113
|
Response includes `x-document-cache-status`:
|
|
114
|
+
|
|
105
115
|
- `HIT` - Fresh cache hit
|
|
106
116
|
- `STALE` - Served stale, revalidating in background
|
|
107
117
|
- `MISS` - Cache miss, response was generated fresh
|
|
@@ -109,6 +119,7 @@ Response includes `x-document-cache-status`:
|
|
|
109
119
|
## Cache Key Generation
|
|
110
120
|
|
|
111
121
|
Default keys differentiate:
|
|
122
|
+
|
|
112
123
|
- HTML requests: `{pathname}:html`
|
|
113
124
|
- RSC partials: `{pathname}:{segmentHash}:rsc`
|
|
114
125
|
|
|
@@ -118,13 +129,14 @@ Segment hash ensures different cached responses for navigations from different s
|
|
|
118
129
|
|
|
119
130
|
- Full HTML responses (document requests)
|
|
120
131
|
- RSC payloads (client navigation)
|
|
121
|
-
- Only 200 OK responses
|
|
132
|
+
- Only 200 OK responses whose `Cache-Control` includes `s-maxage`
|
|
122
133
|
|
|
123
134
|
## What's NOT Cached
|
|
124
135
|
|
|
125
136
|
- Server actions (`_rsc_action`)
|
|
126
137
|
- Loader requests (`_rsc_loader`)
|
|
127
|
-
-
|
|
138
|
+
- Non-GET requests
|
|
139
|
+
- Responses without an `s-maxage` `Cache-Control` directive
|
|
128
140
|
- Non-200 responses
|
|
129
141
|
|
|
130
142
|
## Complete Example
|
|
@@ -132,49 +144,62 @@ Segment hash ensures different cached responses for navigations from different s
|
|
|
132
144
|
```typescript
|
|
133
145
|
// router.tsx
|
|
134
146
|
import { createRouter } from "@rangojs/router";
|
|
135
|
-
import { CFCacheStore } from "@rangojs/router/cache
|
|
147
|
+
import { createDocumentCacheMiddleware, CFCacheStore } from "@rangojs/router/cache";
|
|
136
148
|
import { urlpatterns } from "./urls";
|
|
137
149
|
|
|
138
|
-
const router = createRouter<
|
|
150
|
+
const router = createRouter<AppBindings>({
|
|
139
151
|
document: Document,
|
|
140
152
|
urls: urlpatterns,
|
|
141
|
-
|
|
142
|
-
|
|
153
|
+
cache: (_env, ctx) => ({ store: new CFCacheStore({ ctx: ctx! }) }),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
router.use(
|
|
157
|
+
createDocumentCacheMiddleware({
|
|
143
158
|
skipPaths: ["/api"],
|
|
144
159
|
debug: process.env.NODE_ENV === "development",
|
|
145
160
|
}),
|
|
146
|
-
|
|
161
|
+
);
|
|
147
162
|
|
|
148
163
|
export default router;
|
|
149
164
|
|
|
150
165
|
// urls.tsx
|
|
151
166
|
import { urls } from "@rangojs/router";
|
|
152
167
|
|
|
153
|
-
export const urlpatterns = urls(({ path, layout,
|
|
154
|
-
// Blog
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
]),
|
|
168
|
+
export const urlpatterns = urls(({ path, layout, loader }) => [
|
|
169
|
+
// Blog pages opt into document caching via Cache-Control headers set in
|
|
170
|
+
// their handlers (see BlogIndex / BlogPost below).
|
|
171
|
+
layout(<BlogLayout />, () => [
|
|
172
|
+
path("/blog", BlogIndex, { name: "blog" }),
|
|
173
|
+
path("/blog/:slug", BlogPost, { name: "blogPost" }, () => [
|
|
174
|
+
loader(BlogPostLoader),
|
|
161
175
|
]),
|
|
162
176
|
]),
|
|
163
177
|
|
|
164
|
-
// Dashboard
|
|
178
|
+
// Dashboard sets no Cache-Control header, so it is never document-cached.
|
|
165
179
|
layout(<DashboardLayout />, () => [
|
|
166
180
|
path("/dashboard", Dashboard, { name: "dashboard" }),
|
|
167
181
|
]),
|
|
168
182
|
]);
|
|
183
|
+
|
|
184
|
+
// Blog handlers set s-maxage to opt into the document cache.
|
|
185
|
+
function BlogIndex(ctx) {
|
|
186
|
+
ctx.headers.set("Cache-Control", "s-maxage=300, stale-while-revalidate=3600");
|
|
187
|
+
return <BlogIndexPage />;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function BlogPost(ctx) {
|
|
191
|
+
ctx.headers.set("Cache-Control", "s-maxage=300, stale-while-revalidate=3600");
|
|
192
|
+
return <BlogPostPage />;
|
|
193
|
+
}
|
|
169
194
|
```
|
|
170
195
|
|
|
171
196
|
## Document Cache vs Segment Cache
|
|
172
197
|
|
|
173
|
-
| Feature
|
|
174
|
-
|
|
175
|
-
| Granularity
|
|
176
|
-
| Opt-in
|
|
177
|
-
| Use case
|
|
178
|
-
| Key includes | URL + segment hash
|
|
198
|
+
| Feature | Document Cache | Segment Cache |
|
|
199
|
+
| ------------ | -------------------------- | --------------------- |
|
|
200
|
+
| Granularity | Full response | Individual segments |
|
|
201
|
+
| Opt-in | `Cache-Control` `s-maxage` | `cache({ ttl, swr })` |
|
|
202
|
+
| Use case | Static pages | Dynamic compositions |
|
|
203
|
+
| Key includes | URL + segment hash | Route params |
|
|
179
204
|
|
|
180
205
|
Use document cache for mostly-static pages. Use segment cache when different parts of a page have different cache requirements.
|
package/skills/fonts/SKILL.md
CHANGED
|
@@ -24,7 +24,11 @@ export function Document({ children }: { children: ReactNode }) {
|
|
|
24
24
|
<head>
|
|
25
25
|
{/* Preconnect to Google Fonts */}
|
|
26
26
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
27
|
-
<link
|
|
27
|
+
<link
|
|
28
|
+
rel="preconnect"
|
|
29
|
+
href="https://fonts.gstatic.com"
|
|
30
|
+
crossOrigin="anonymous"
|
|
31
|
+
/>
|
|
28
32
|
|
|
29
33
|
{/* Load font stylesheet */}
|
|
30
34
|
<link
|
|
@@ -37,9 +41,7 @@ export function Document({ children }: { children: ReactNode }) {
|
|
|
37
41
|
<link rel="stylesheet" href={styles} />
|
|
38
42
|
<MetaTags />
|
|
39
43
|
</head>
|
|
40
|
-
<body>
|
|
41
|
-
{children}
|
|
42
|
-
</body>
|
|
44
|
+
<body>{children}</body>
|
|
43
45
|
</html>
|
|
44
46
|
);
|
|
45
47
|
}
|