@rangojs/router 0.0.0-experimental.79 → 0.0.0-experimental.7c7e4327
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -4
- package/README.md +301 -797
- package/dist/bin/rango.js +603 -145
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3750 -1160
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +96 -24
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +85 -6
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +228 -33
- package/skills/caching/SKILL.md +336 -19
- package/skills/catalog.json +271 -0
- package/skills/comparison/SKILL.md +50 -0
- package/skills/comparison/agents/openai.yaml +4 -0
- package/skills/comparison/references/framework-comparison.md +837 -0
- package/skills/composability/SKILL.md +110 -4
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +5 -3
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +87 -56
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +12 -10
- package/skills/hooks/SKILL.md +73 -691
- package/skills/hooks/data.md +273 -0
- package/skills/hooks/handle-and-actions.md +103 -0
- package/skills/hooks/navigation.md +110 -0
- package/skills/hooks/outlets.md +41 -0
- package/skills/hooks/state.md +228 -0
- package/skills/hooks/urls.md +135 -0
- package/skills/host-router/SKILL.md +129 -27
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +75 -19
- package/skills/layout/SKILL.md +40 -19
- package/skills/links/SKILL.md +247 -17
- package/skills/loader/SKILL.md +248 -10
- package/skills/middleware/SKILL.md +25 -13
- package/skills/migrate-nextjs/SKILL.md +205 -20
- package/skills/migrate-react-router/SKILL.md +59 -670
- package/skills/migrate-react-router/cloudflare-workers.md +129 -0
- package/skills/migrate-react-router/component-migration.md +196 -0
- package/skills/migrate-react-router/data-and-actions.md +225 -0
- package/skills/migrate-react-router/route-mapping.md +271 -0
- package/skills/mime-routes/SKILL.md +29 -2
- package/skills/observability/SKILL.md +202 -0
- package/skills/parallel/SKILL.md +40 -10
- package/skills/ppr/SKILL.md +616 -0
- package/skills/prerender/SKILL.md +72 -60
- package/skills/rango/SKILL.md +318 -26
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +138 -49
- package/skills/route/SKILL.md +117 -9
- package/skills/router-setup/SKILL.md +44 -9
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +776 -0
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +28 -4
- package/skills/testing/SKILL.md +130 -0
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +131 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +85 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/theme/SKILL.md +1 -1
- package/skills/typesafety/SKILL.md +45 -626
- package/skills/typesafety/env-and-bindings.md +254 -0
- package/skills/typesafety/generated-files-and-cli.md +335 -0
- package/skills/typesafety/params-and-search.md +153 -0
- package/skills/typesafety/route-types.md +209 -0
- package/skills/use-cache/SKILL.md +74 -15
- 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 +0 -65
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +252 -158
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +94 -25
- package/src/browser/navigation-client.ts +144 -79
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +161 -73
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +183 -144
- package/src/browser/prefetch/cache.ts +242 -77
- package/src/browser/prefetch/fetch.ts +325 -69
- package/src/browser/prefetch/queue.ts +61 -12
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +58 -20
- package/src/browser/react/NavigationProvider.tsx +202 -120
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +66 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +178 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +17 -14
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +32 -7
- package/src/browser/react/use-params.ts +20 -10
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +25 -3
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +99 -8
- package/src/browser/rsc-router.tsx +145 -28
- package/src/browser/scroll-restoration.ts +37 -22
- package/src/browser/segment-reconciler.ts +31 -21
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +236 -65
- package/src/browser/types.ts +102 -9
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +203 -154
- package/src/build/generate-route-types.ts +3 -1
- package/src/build/index.ts +11 -3
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +152 -21
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +16 -5
- package/src/build/route-types/include-resolution.ts +456 -62
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +22 -6
- package/src/build/route-types/router-processing.ts +128 -51
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +13 -21
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +58 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +421 -58
- package/src/cache/cache-scope.ts +187 -96
- package/src/cache/cache-tag.ts +149 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +2202 -372
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +126 -41
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +243 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +13 -21
- package/src/cache/shell-snapshot.ts +417 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +180 -99
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1127 -0
- package/src/client.rsc.tsx +41 -21
- package/src/client.tsx +33 -61
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +108 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +18 -6
- 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 +30 -4
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +67 -37
- package/src/handles/MetaTags.tsx +24 -53
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +35 -8
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +14 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +13 -22
- package/src/host/index.ts +9 -2
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +97 -12
- package/src/index.ts +98 -14
- package/src/internal-debug.ts +11 -10
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +78 -10
- package/src/redirect-origin.ts +114 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +62 -0
- package/src/reverse.ts +65 -39
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +19 -77
- package/src/route-definition/dsl-helpers.ts +304 -309
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +87 -59
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +12 -1
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-map-builder.ts +41 -20
- package/src/route-types.ts +19 -46
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +73 -25
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +129 -30
- package/src/router/handler-context.ts +27 -42
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +39 -20
- package/src/router/lazy-includes.ts +82 -59
- package/src/router/loader-resolution.ts +167 -72
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +74 -40
- package/src/router/match-api.ts +80 -55
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +211 -165
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +159 -285
- package/src/router/match-middleware/cache-store.ts +64 -52
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +69 -79
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +7 -134
- package/src/router/middleware.ts +298 -172
- package/src/router/navigation-snapshot.ts +7 -56
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +181 -150
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +112 -67
- package/src/router/preview-match.ts +6 -2
- package/src/router/request-classification.ts +50 -69
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +14 -3
- package/src/router/router-context.ts +6 -29
- package/src/router/router-interfaces.ts +115 -36
- package/src/router/router-options.ts +166 -5
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +131 -86
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +139 -39
- package/src/router/segment-resolution/loader-mask.ts +67 -0
- package/src/router/segment-resolution/loader-snapshot.ts +251 -0
- package/src/router/segment-resolution/revalidation.ts +272 -320
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +56 -0
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +75 -0
- package/src/router/telemetry-otel.ts +160 -200
- package/src/router/telemetry.ts +105 -20
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +215 -0
- package/src/router/trie-matching.ts +171 -59
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +57 -0
- package/src/router.ts +157 -71
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +291 -217
- package/src/rsc/helpers.ts +168 -46
- package/src/rsc/index.ts +2 -5
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +29 -42
- package/src/rsc/nonce.ts +10 -1
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +124 -13
- package/src/rsc/redirect-guard.ts +100 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +99 -189
- package/src/rsc/rsc-rendering.ts +421 -76
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +282 -116
- package/src/rsc/shell-capture.ts +1158 -0
- package/src/rsc/shell-serve.ts +150 -0
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +53 -5
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-loader-promise.ts +49 -4
- package/src/segment-system.tsx +350 -149
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +208 -51
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +152 -5
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +395 -176
- package/src/ssr/index.tsx +458 -178
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +46 -0
- package/src/testing/dispatch.ts +813 -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 +56 -84
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +110 -62
- package/src/types/index.ts +3 -10
- package/src/types/loader-types.ts +11 -9
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +20 -52
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +135 -14
- package/src/urls/include-helper.ts +19 -64
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +2 -11
- package/src/urls/path-helper-types.ts +63 -17
- package/src/urls/path-helper.ts +22 -106
- package/src/urls/pattern-types.ts +72 -19
- package/src/urls/response-types.ts +22 -29
- package/src/urls/type-extraction.ts +98 -154
- package/src/urls/urls-function.ts +1 -19
- package/src/use-loader.tsx +292 -107
- 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 +8 -7
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +127 -86
- 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 +96 -68
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +45 -1
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +4 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +210 -10
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- 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/plugins/expose-action-id.ts +48 -95
- package/src/vite/plugins/expose-id-utils.ts +88 -55
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
- package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
- package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
- package/src/vite/plugins/expose-internal-ids.ts +505 -486
- package/src/vite/plugins/performance-tracks.ts +26 -25
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +37 -40
- package/src/vite/plugins/virtual-entries.ts +138 -27
- package/src/vite/rango.ts +236 -138
- package/src/vite/router-discovery.ts +927 -136
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +10 -15
- 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 +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +71 -43
- package/src/vite/utils/shared-utils.ts +142 -43
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: prerender
|
|
3
|
-
description: Pre-render route segments at build time with Prerender and Passthrough live fallback
|
|
3
|
+
description: Pre-render route segments at build time with Prerender and Passthrough live fallback. Use when a page's content is mostly static and shouldn't render on every request, speeding up cold responses, or deciding which routes to prerender vs render live.
|
|
4
4
|
argument-hint: [passthrough]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -11,8 +11,12 @@ deserialization path, same segment system. The worker handles every request --
|
|
|
11
11
|
there are NO static .html or .rsc files served from assets. The worker reads
|
|
12
12
|
pre-computed Flight payloads instead of executing handler code.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
## Not this skill if…
|
|
15
|
+
|
|
16
|
+
- You want a cached HTML shell captured at runtime, with holes and loaders
|
|
17
|
+
staying live per request — see `/ppr`.
|
|
18
|
+
- You want runtime segment caching with TTL/SWR — that is the `cache()` DSL:
|
|
19
|
+
see `/caching`. Prerender is the same cache filled at build time.
|
|
16
20
|
|
|
17
21
|
## API: Prerender
|
|
18
22
|
|
|
@@ -122,6 +126,8 @@ interface BuildContext<TParams> {
|
|
|
122
126
|
use: <T>(handle: Handle<T>) => (data: T) => void; // Push handle data
|
|
123
127
|
url: URL; // Synthetic URL from pattern + params
|
|
124
128
|
pathname: string; // Pathname from synthetic URL
|
|
129
|
+
searchParams: URLSearchParams; // URLSearchParams from the synthetic URL (always empty for prerender)
|
|
130
|
+
search: {}; // Typed search params -- always {} for prerender (no real query string)
|
|
125
131
|
set(key: string, value: any): void; // Set context variable (string key)
|
|
126
132
|
set<T>(contextVar: ContextVar<T>, value: T): void; // Set typed context variable
|
|
127
133
|
get(key: string): any; // Read context variable (string key)
|
|
@@ -244,16 +250,16 @@ path("/blog/:slug", BlogPost, { name: "blog.post" }, () => [
|
|
|
244
250
|
|
|
245
251
|
## Interaction with DSL Items
|
|
246
252
|
|
|
247
|
-
| DSL item | Behavior with Prerender
|
|
248
|
-
| -------------- |
|
|
249
|
-
| `loader()` | Live at runtime, bundled normally. Use `cache()` for caching.
|
|
250
|
-
| `revalidate()` | Not allowed without Passthrough. Allowed with Passthrough.
|
|
251
|
-
| `cache()` | Orthogonal -- use on parent layouts and loaders.
|
|
252
|
-
| `layout()` | Child layouts inside path are pre-rendered. Parent layouts are live.
|
|
253
|
-
| `parallel()` | Parallel slots inside path are pre-rendered.
|
|
254
|
-
| `middleware()` | Skipped during pre-render (no request). Runs at request time for loaders.
|
|
255
|
-
| `loading()` | Ignored without Passthrough. Works for live fallback with Passthrough.
|
|
256
|
-
| `intercept()` | Pre-rendered at build time. Intercept variant stored under `/i` key alongside main segments. At runtime, the correct variant is served based on `ctx.isIntercept`. `when
|
|
253
|
+
| DSL item | Behavior with Prerender |
|
|
254
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
255
|
+
| `loader()` | Live at runtime, bundled normally. Use `cache()` for caching. |
|
|
256
|
+
| `revalidate()` | Not allowed without Passthrough. Allowed with Passthrough. |
|
|
257
|
+
| `cache()` | Orthogonal -- use on parent layouts and loaders. |
|
|
258
|
+
| `layout()` | Child layouts inside path are pre-rendered. Parent layouts are live. |
|
|
259
|
+
| `parallel()` | Parallel slots inside path are pre-rendered. |
|
|
260
|
+
| `middleware()` | Skipped during pre-render (no request). Runs at request time for loaders. |
|
|
261
|
+
| `loading()` | Ignored without Passthrough. Works for live fallback with Passthrough. |
|
|
262
|
+
| `intercept()` | Pre-rendered at build time. Intercept variant stored under `/i` key alongside main segments. At runtime, the correct variant is served based on `ctx.isIntercept`. `when` config conditions are skipped at build time (all intercepts are pre-rendered unconditionally). |
|
|
257
263
|
|
|
258
264
|
When Passthrough revalidation is enabled, remember that revalidation is
|
|
259
265
|
still partial: opting a child segment into revalidation does not
|
|
@@ -346,14 +352,31 @@ export const TocSidebar = Static(() => {
|
|
|
346
352
|
|
|
347
353
|
### Error behavior at build time
|
|
348
354
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
+
When a render throws a non-`Skip` error, it is **surfaced to the build** — never
|
|
356
|
+
baked into a frozen error page served as a 200 (issue #587). What happens next is
|
|
357
|
+
controlled by `prerender.onError` in your `rango()` options:
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
rango({ prerender: { onError: "warn" } }); // default is "fail"
|
|
361
|
+
```
|
|
355
362
|
|
|
356
|
-
|
|
363
|
+
| Handler outcome | `onError: "fail"` (default) | `onError: "warn"` |
|
|
364
|
+
| --------------------------- | -------------------------------------------- | -------------------------------- |
|
|
365
|
+
| JSX / `null` | Normal prerender entry, log OK | Normal prerender entry, log OK |
|
|
366
|
+
| `return ctx.passthrough()` | Skip entry, log PASS (Passthrough routes) | Skip entry, log PASS |
|
|
367
|
+
| `throw new Skip("reason")` | Skip entry, log SKIP, continue | Skip entry, log SKIP, continue |
|
|
368
|
+
| `throw new Error("reason")` | Log FAIL, stop ALL pre-rendering, fail build | Log WARN, skip the URL, continue |
|
|
369
|
+
|
|
370
|
+
With `"warn"` the errored entry is logged and left un-baked (never served as a baked
|
|
371
|
+
200 error page). `"warn"` is a build-unblock, not a runtime contract: the route falls
|
|
372
|
+
through to normal resolution — it may render live (its handler is still bundled) or
|
|
373
|
+
404 (once other baked entries trigger prerender handler eviction), so the outcome
|
|
374
|
+
depends on the rest of the build, and a skipped `Static()` handler's evicted code can
|
|
375
|
+
surface as an error. For DEFINED runtime behavior reach for `Passthrough()` (a live
|
|
376
|
+
fallback) or `throw new Skip()` (an intentional skip — works in the render fn, not
|
|
377
|
+
only `getParams()`); otherwise prefer the default `"fail"`.
|
|
378
|
+
|
|
379
|
+
Both `Skip` and hard errors propagate to the router's `onError` callback with phase
|
|
357
380
|
`"prerender"` or `"static"`.
|
|
358
381
|
|
|
359
382
|
### Build logs
|
|
@@ -361,21 +384,23 @@ Both error types propagate to the router's `onError` callback with phase
|
|
|
361
384
|
The build produces per-URL timing logs:
|
|
362
385
|
|
|
363
386
|
```
|
|
364
|
-
[
|
|
365
|
-
[
|
|
366
|
-
[
|
|
367
|
-
[
|
|
368
|
-
[
|
|
369
|
-
|
|
370
|
-
[
|
|
371
|
-
[
|
|
372
|
-
[
|
|
373
|
-
[
|
|
387
|
+
[rango] Pre-rendering 12 URL(s) (concurrency: 4)...
|
|
388
|
+
[rango] OK /articles/hello (42ms)
|
|
389
|
+
[rango] PASS /articles/remote-only (5ms) - live fallback
|
|
390
|
+
[rango] SKIP /articles/draft-post (3ms) - Article is a draft
|
|
391
|
+
[rango] Pre-render complete: 11 done, 1 skipped (1204ms total)
|
|
392
|
+
|
|
393
|
+
[rango] Rendering 3 static handler(s)...
|
|
394
|
+
[rango] OK DocsLayout (28ms)
|
|
395
|
+
[rango] SKIP TocSidebar (1ms) - Not ready
|
|
396
|
+
[rango] Static render complete: 2 done, 1 skipped (120ms total)
|
|
374
397
|
```
|
|
375
398
|
|
|
376
|
-
A `FAIL` line is logged per-URL when a handler throws a non-Skip error
|
|
377
|
-
error is re-thrown immediately, so no
|
|
378
|
-
stops at the first failure.
|
|
399
|
+
A `FAIL` line is logged per-URL when a handler throws a non-Skip error (with the
|
|
400
|
+
default `prerender.onError: "fail"`). The error is re-thrown immediately, so no
|
|
401
|
+
summary line is printed — the build stops at the first failure. Under
|
|
402
|
+
`prerender.onError: "warn"` the same case logs a `WARN` line, skips that URL, and
|
|
403
|
+
the build continues.
|
|
379
404
|
|
|
380
405
|
### Dev mode behavior
|
|
381
406
|
|
|
@@ -466,9 +491,9 @@ export const Product = Passthrough(ProductDef, async (ctx) => {
|
|
|
466
491
|
Passthrough entries are logged distinctly:
|
|
467
492
|
|
|
468
493
|
```
|
|
469
|
-
[
|
|
470
|
-
[
|
|
471
|
-
[
|
|
494
|
+
[rango] OK /blog/a (42ms)
|
|
495
|
+
[rango] PASS /blog/b (3ms) - live fallback
|
|
496
|
+
[rango] OK /blog/c (38ms)
|
|
472
497
|
```
|
|
473
498
|
|
|
474
499
|
## Edge Cases and Constraints
|
|
@@ -591,12 +616,12 @@ At runtime, the cache-lookup middleware checks `ctx.isIntercept`:
|
|
|
591
616
|
(filtered by `namespace?.startsWith("intercept:")`) and sets up slots.
|
|
592
617
|
- **Direct navigation**: looks up `paramHash` (no suffix). Standard prerender path.
|
|
593
618
|
- **Intercept miss (no `/i` entry)**: falls through to the normal pipeline so
|
|
594
|
-
intercept-resolution middleware runs live. This handles `when
|
|
619
|
+
intercept-resolution middleware runs live. This handles `when` config conditions
|
|
595
620
|
that prevented pre-rendering.
|
|
596
621
|
|
|
597
|
-
The `when
|
|
622
|
+
The `when` config selector receives an `InterceptSelectorContext` with `from.pathname`
|
|
598
623
|
which is unknown at build time. All intercepts are pre-rendered unconditionally;
|
|
599
|
-
`when
|
|
624
|
+
`when` is evaluated at runtime by the intercept-resolution middleware.
|
|
600
625
|
|
|
601
626
|
### Example: Pre-rendered route with intercept
|
|
602
627
|
|
|
@@ -615,10 +640,13 @@ layout(ShopLayout, () => [
|
|
|
615
640
|
|
|
616
641
|
// Intercept detail from shop index into a modal.
|
|
617
642
|
// At build time, this is resolved and stored under the /i key.
|
|
618
|
-
intercept(
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
643
|
+
intercept(
|
|
644
|
+
"@modal",
|
|
645
|
+
".detail",
|
|
646
|
+
<ProductModal />,
|
|
647
|
+
{ when: ({ from }) => from.pathname === "/shop" },
|
|
648
|
+
() => [loader(ProductLoader)],
|
|
649
|
+
),
|
|
622
650
|
])
|
|
623
651
|
```
|
|
624
652
|
|
|
@@ -640,16 +668,7 @@ At runtime, the cache-lookup middleware uses these flags:
|
|
|
640
668
|
|
|
641
669
|
## Contributor Checklist
|
|
642
670
|
|
|
643
|
-
Before changing prerender behavior,
|
|
644
|
-
|
|
645
|
-
### Docs to re-read
|
|
646
|
-
|
|
647
|
-
- [Prerender API design](../../docs/prerender-api-design.md) -- canonical
|
|
648
|
-
architecture: build-time flow, runtime flow, storage, Passthrough, intercept
|
|
649
|
-
- [Execution model](../../docs/internal/execution-model.md) -- handler-first
|
|
650
|
-
ordering, middleware scope, context visibility rules
|
|
651
|
-
- [Semantic change checklist](../../docs/internal/semantic-change-checklist.md)
|
|
652
|
-
-- gate for any change to execution semantics
|
|
671
|
+
Before changing prerender behavior, run these tests.
|
|
653
672
|
|
|
654
673
|
### Tests to run
|
|
655
674
|
|
|
@@ -676,10 +695,3 @@ pnpm --filter @rangojs/router exec playwright test handler-first
|
|
|
676
695
|
dev/build-only and do not need a production counterpart.
|
|
677
696
|
- Behavioral assertions (rendered content, loader freshness, Passthrough
|
|
678
697
|
fallback, intercept variant selection) must work in the production build.
|
|
679
|
-
|
|
680
|
-
## Maintenance References
|
|
681
|
-
|
|
682
|
-
- [Stability next steps plan](../../docs/internal/stability-next-steps-plan.md)
|
|
683
|
-
-- completed parity and cleanup pass (reference for decisions made)
|
|
684
|
-
- [Test quality baseline](../../docs/internal/test-quality-baseline.md) --
|
|
685
|
-
measured test inventory, sleep debt, production coverage gaps
|
package/skills/rango/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rango
|
|
3
|
-
description: Overview of @rangojs/router and available skills
|
|
3
|
+
description: Overview of @rangojs/router and available skills. Use when unsure which skill to reach for, starting a new task in a Rango app, or asking "what can this router do".
|
|
4
4
|
argument-hint:
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,32 +8,311 @@ argument-hint:
|
|
|
8
8
|
|
|
9
9
|
Django-inspired RSC router with composable URL patterns, type-safe href, and server components.
|
|
10
10
|
|
|
11
|
+
This page is the mental model to read **before** the catalog. A flat list of
|
|
12
|
+
skills gives nothing to slot details into, so a reader free-associates from local
|
|
13
|
+
vocabulary — which is exactly how `revalidate()` gets misread as caching. Start
|
|
14
|
+
with the shape, then pick a primitive.
|
|
15
|
+
|
|
16
|
+
## The shape of rango (read first)
|
|
17
|
+
|
|
18
|
+
- **Routes are expressed, not configured.** The `urls()` tree shows where every
|
|
19
|
+
route, layout, loader, and cache lives. No file-system convention, no hunting.
|
|
20
|
+
- **Two freshness axes, orthogonal:**
|
|
21
|
+
- _stored-value freshness_ — `"use cache"`, `cache()`, loader `cache()`
|
|
22
|
+
(SWR is first-class where the store supports it; `"use cache"` ships a
|
|
23
|
+
default SWR window; see `/cache-guide`)
|
|
24
|
+
- _client-update selection_ — `revalidate()`
|
|
25
|
+
- **Loaders are the live data layer** — fresh every request by default, even
|
|
26
|
+
inside a cached render. They run **in parallel** right after middleware and
|
|
27
|
+
**stream**, so data latency overlaps first paint instead of blocking it (a
|
|
28
|
+
cache hit streams UI instantly while loaders resolve fresh alongside). Opt into
|
|
29
|
+
caching explicitly. See `/loader` → "Parallel and streaming".
|
|
30
|
+
- **One identity, one store** — loaders, handles, cached fns, and actions are all
|
|
31
|
+
`path#export`; all caches share one store. Entries expire by TTL/SWR, and are
|
|
32
|
+
tagged via `cache({ tags })` or runtime `cacheTag(...tags)`; built-in stores
|
|
33
|
+
index by tag and invalidate via `updateTag(...tags)` (awaitable, read-your-own-writes)
|
|
34
|
+
or `revalidateTag(...tags)` (background, non-blocking).
|
|
35
|
+
- **Type-safe end to end** — route names, params, search schemas, loader return
|
|
36
|
+
types, context vars, and `href` / `reverse` are checked at compile time
|
|
37
|
+
(`/typesafety`).
|
|
38
|
+
- **See where time goes** — turn on `debugPerformance` early (router option, or
|
|
39
|
+
`ctx.debugPerformance()` in middleware for per-request opt-in). It prints a
|
|
40
|
+
per-request waterfall + `Server-Timing` header; loaders should overlap the
|
|
41
|
+
render bar, not serialize after it. For production, wire `telemetry` to a
|
|
42
|
+
console, OpenTelemetry, or custom sink. See `/observability`.
|
|
43
|
+
|
|
44
|
+
Most features are **just-in-time**: the core is `urls()`, `path()`, `layout()`,
|
|
45
|
+
`include()`, and `reverse()`. Caching, parallel routes, intercepts, prerender,
|
|
46
|
+
i18n, themes, and the rest are opt-in — reach for them when a requirement
|
|
47
|
+
appears, not up front.
|
|
48
|
+
|
|
49
|
+
## Composability: structure vs config
|
|
50
|
+
|
|
51
|
+
- `path()` / `include()` are **structure** — they define URLs and must stay
|
|
52
|
+
visible in `urls()`. They cannot be hidden in a factory. `include()` composes
|
|
53
|
+
whole modules (separation of real concerns); `path()` places a leaf.
|
|
54
|
+
- Everything else — `cache`, `loader`, `loading`, `middleware`, `revalidate`,
|
|
55
|
+
`parallel`, `intercept`, `errorBoundary`, … — is **config**. It attaches to a
|
|
56
|
+
node via its `use` callback, is importable, and extracts into factories that
|
|
57
|
+
return arrays (`withAuth()`, `withCaching()`), flattened automatically.
|
|
58
|
+
|
|
59
|
+
To decide where something can live: **does it define a URL? structure, stays in
|
|
60
|
+
`urls()`. Does it modify a node? config, compose freely.**
|
|
61
|
+
|
|
62
|
+
## Passing data down the tree
|
|
63
|
+
|
|
64
|
+
Four ways to get per-request data to a segment below you, ordered safest-first.
|
|
65
|
+
Reach for the next rung only when the one above doesn't fit — the higher rungs
|
|
66
|
+
are immune to partial-revalidation staleness by construction.
|
|
67
|
+
|
|
68
|
+
1. **A loader** (`loader()` + `useLoader()`). Loaders resolve fresh on every
|
|
69
|
+
pass — full renders, action revalidations, cache hits. Nothing to keep in
|
|
70
|
+
sync. If the data can be a loader, make it a loader.
|
|
71
|
+
2. **Middleware `ctx.set()`**. Route middleware wraps every render pass,
|
|
72
|
+
including post-action revalidation and PE re-renders, so its variables are
|
|
73
|
+
never stale. Right for request-shaped context: auth, session, locale.
|
|
74
|
+
3. **Handler `ctx.set()` to its own children** —
|
|
75
|
+
`path(handler, ..., () => [layout(...)])`. Orphan layouts and their
|
|
76
|
+
parallels belong to the route entry: on an action the whole entry re-runs
|
|
77
|
+
together by default (handler-first preserved), so the data stays consistent
|
|
78
|
+
with zero configuration. Right for data the page must compute anyway —
|
|
79
|
+
e.g. pagination, where the handler's search decides how many pages the
|
|
80
|
+
layout chrome renders. One rule: if you narrow the entry's revalidation
|
|
81
|
+
with a predicate that can return a hard `false`, put the same contract on
|
|
82
|
+
the entry's children too — a hard `false` on one side of a
|
|
83
|
+
producer/consumer pair desyncs it.
|
|
84
|
+
4. **Cross-entry sharing** — an outer `layout()` entry feeding descendants.
|
|
85
|
+
Outer entries do NOT revalidate on actions by default (the revalidation
|
|
86
|
+
trace calls this `action:parent-chain-skip`), so this rung always requires
|
|
87
|
+
a shared revalidation contract: the same named `revalidate()` function on
|
|
88
|
+
the producer and every consumer. See `/layout` → "Revalidation Contracts".
|
|
89
|
+
Before writing one, check whether the producer can move down a rung.
|
|
90
|
+
|
|
91
|
+
The failure mode this ladder prevents: a consumer re-runs, its producer
|
|
92
|
+
doesn't, `ctx.get()` reads `undefined`, and fallback UI silently replaces good
|
|
93
|
+
UI after an action. Rungs 1–3 make that unrepresentable; rung 4 makes it a
|
|
94
|
+
stated, greppable contract.
|
|
95
|
+
|
|
96
|
+
## Pick a primitive
|
|
97
|
+
|
|
98
|
+
| I need to… | Use | Skill |
|
|
99
|
+
| --------------------------------------- | ---------------------------------- | ----------------------- |
|
|
100
|
+
| render data fresh every request | `loader()` + `useLoader()` | /loader |
|
|
101
|
+
| cache a rendered subtree | `cache()` on a segment | /caching |
|
|
102
|
+
| cache one function/component's result | `"use cache"` | /use-cache |
|
|
103
|
+
| cache a loader's data | `loader(L, () => [cache()])` | /loader, /caching |
|
|
104
|
+
| re-render a segment after an action | `revalidate()` | /loader |
|
|
105
|
+
| mutate | `"use server"` action | /server-actions |
|
|
106
|
+
| debug a slow request | `debugPerformance` / telemetry | /observability |
|
|
107
|
+
| share config across routes | factory returning a helper array | /composability |
|
|
108
|
+
| compose a sub-app / module | `include()` | /route |
|
|
109
|
+
| modal / soft navigation | `intercept()` | /intercept |
|
|
110
|
+
| pre-render a route at build time | `Prerender(...)` wrapper | /prerender |
|
|
111
|
+
| feed live loaders from a cached shell | replayed handle + `ctx.rendered()` | /shell-manifest |
|
|
112
|
+
| cache the HTML shell, keep loaders live | `ppr` path option | /ppr |
|
|
113
|
+
| stream SSE / upgrade a WebSocket | `path.stream()` / `path.any()` | /streams-and-websockets |
|
|
114
|
+
|
|
115
|
+
## Invariants
|
|
116
|
+
|
|
117
|
+
- `path()`/`include()` are always visible in `urls()`; config helpers are extractable.
|
|
118
|
+
- **Cache decides freshness; `revalidate()` decides client-update.** Orthogonal; compose.
|
|
119
|
+
- Loaders resolve fresh every request (even inside `cache()`) and never run twice/request.
|
|
120
|
+
- **The consumption-lane rule.** For every shared artifact (`cache()`,
|
|
121
|
+
`"use cache"`, the PPR shell): server-side handler consumption
|
|
122
|
+
(`await ctx.use(loader)`) yields a BAKED copy — identity reads
|
|
123
|
+
(`cookies()`/`headers()`) are permitted there and the capture-time value
|
|
124
|
+
freezes into the shared artifact (a documented footgun; see `/caching` →
|
|
125
|
+
"Cache purity & tainted objects"). Client-side consumption (`useLoader` in
|
|
126
|
+
a `"use client"` component) is the LIVE lane. DSL `loader()` segments
|
|
127
|
+
follow their lane machinery (live under renderable `loading()`, bake
|
|
128
|
+
otherwise). Pinned by semantic-matrix row PPR3.
|
|
129
|
+
- Inside `"use cache"`: `cookies()`/`headers()` and `ctx` side-effects
|
|
130
|
+
(`set`/`header`/`setTheme`/`onResponse`/`setLocationState`) throw; `ctx.use(Handle)`
|
|
131
|
+
is captured on miss and replayed on hit. (The non-cacheable read guard is a
|
|
132
|
+
separate `cache()`-boundary check — see the correctness bullet below.)
|
|
133
|
+
- One identity `path#export` (`functionId`/`$$id`/`actionId`); one store. Freshness
|
|
134
|
+
is TTL/SWR expiry plus tag-based invalidation: tag via `cache({ tags })` /
|
|
135
|
+
`cacheTag(...tags)`, then `updateTag(...tags)` (awaitable) or `revalidateTag(...tags)`
|
|
136
|
+
(background). Built-in stores index by tag.
|
|
137
|
+
- `useLoader` / `useHandle` / `useFetchLoader` are client-only.
|
|
138
|
+
- Caches are correctness-first: persistent store keys are version-segmented (no
|
|
139
|
+
cross-deploy drift), the forward/back cache is mutation-aware, and
|
|
140
|
+
`createVar({ cache: false })` throws on a **direct** read inside a `cache()`
|
|
141
|
+
boundary (a deliberately non-propagating guard). See `/cache-guide` →
|
|
142
|
+
"Correctness & invalidation".
|
|
143
|
+
- Nested caches: the outer cache window bounds the inner — an inner shorter TTL
|
|
144
|
+
only applies when the enclosing cache recomputes; put a value in a loader if it
|
|
145
|
+
must be fresher. See `/cache-guide` → "Combining Both".
|
|
146
|
+
|
|
147
|
+
## Don't confuse
|
|
148
|
+
|
|
149
|
+
- `revalidate()` ≠ cache invalidation — partial-render selection vs value freshness.
|
|
150
|
+
- host router `.lazy()` (lazy import of a handler/sub-app) vs `.map()` (inline Response).
|
|
151
|
+
- `cache()` (segment, in the DSL) vs `"use cache"` (function/component directive).
|
|
152
|
+
- `loader()` registration (server) vs `useLoader()` consumption (client).
|
|
153
|
+
|
|
154
|
+
### Coming from another framework (false friends)
|
|
155
|
+
|
|
156
|
+
Same words, different jobs — this is the most common source of the
|
|
157
|
+
`revalidate()`-is-caching misread.
|
|
158
|
+
|
|
159
|
+
| You may know | Maps to Rango axis | Watch out |
|
|
160
|
+
| --------------------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
161
|
+
| Next.js `export const revalidate = N` | **Axis 1** (cache) | Same word, opposite meaning. Next's `revalidate` is time-based cache expiry; Rango's `revalidate()` is **axis 2**. Use `cache({ ttl })` for the Next behavior. |
|
|
162
|
+
| Next.js `revalidateTag` / `updateTag` | **Axis 1** (cache) | Cache busting by tag. Tag via `cache({ tags })` / `cacheTag(...tags)`; invalidate with `updateTag(...tags)` (awaitable, read-your-own-writes) or `revalidateTag(...tags)` (background, non-blocking). Built-in stores index by tag. No `revalidatePath` (path-based busting); use tags. |
|
|
163
|
+
| React Router / Remix `shouldRevalidate` | **Axis 2** | This is the correct mental model for Rango's `revalidate()`. |
|
|
164
|
+
| HTTP `Cache-Control` / ISR | **Axis 1** | Edge/document layer — see `/document-cache`. Separate from both `cache()` and `revalidate()`. |
|
|
165
|
+
| Next.js PPR (partial prerendering) | HTML shell layer | Same idea, different wiring: the opt-in `ppr` path option captures at runtime (no build-time default); holes are render-defined — `loading()` subtrees plus pending promises under a consumer's own `<Suspense>`. See `/ppr`. |
|
|
166
|
+
| Remix/RR `loader` | live data | Like Rango loaders, fresh per request — but Rango loaders run in parallel and stream (latency overlaps first paint), and can opt into caching on demand. |
|
|
167
|
+
|
|
168
|
+
See `/cache-guide` for the axis-1 decision guide, `/loader` and `/route` for
|
|
169
|
+
`revalidate()` (axis 2), and `/document-cache` for the edge layer.
|
|
170
|
+
|
|
171
|
+
## Canonical shape
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
export const urlpatterns = urls(({ path, layout, loader, loading, cache, revalidate }) => [
|
|
175
|
+
layout(<ShopLayout />, () => [ // structure: wraps children
|
|
176
|
+
loader(CartLoader, () => [ // config: live data
|
|
177
|
+
// partial-render axis: re-run on cart actions, defer otherwise.
|
|
178
|
+
// ctx.isAction() matches by reference (rename-safe), not by string.
|
|
179
|
+
revalidate((ctx) => ctx.isAction(CartActions) || undefined),
|
|
180
|
+
]),
|
|
181
|
+
path("/shop/:slug", ProductPage, { name: "product" }, () => [ // structure: leaf
|
|
182
|
+
loader(ProductLoader, () => [cache({ ttl: 60 })]), // config: cache loader DATA
|
|
183
|
+
loading(<ProductSkeleton />), // config
|
|
184
|
+
withRecs(), // composed factory (config array)
|
|
185
|
+
]),
|
|
186
|
+
]),
|
|
187
|
+
]);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
One tree, both axes visible: structure (`layout`/`path`) vs config (everything
|
|
191
|
+
else), freshness (`cache`) vs client-update (`revalidate`). Actions are matched
|
|
192
|
+
by reference with `ctx.isAction(Action)` (rename-safe, where `CartActions` is an
|
|
193
|
+
`import * as CartActions from "./actions/cart"`); see `/typesafety` → "Stable
|
|
194
|
+
identity".
|
|
195
|
+
|
|
196
|
+
The predicate arg carries the action's full context, not just its identity. Match
|
|
197
|
+
_which_ action with `ctx.isAction(addToCart)` (rename-safe); branch on _what it
|
|
198
|
+
returned_ with `ctx.actionResult` — the value your `"use server"` function
|
|
199
|
+
returned, for outcome-conditional revalidation. The arg also exposes `actionId`
|
|
200
|
+
(raw `path#export`), `actionUrl`, `formData`, `method`, and `stale` (cross-tab
|
|
201
|
+
`_rsc_stale` signal). All are `undefined` on plain navigation (no action).
|
|
202
|
+
|
|
203
|
+
Two idioms, picked by what an _unrelated_ action should do. `ctx.isAction()`
|
|
204
|
+
returns a raw boolean, so combine it with `|| undefined` to **defer** ("mine,
|
|
205
|
+
else let the default decide": `ctx.isAction(CartActions) || undefined`) or leave
|
|
206
|
+
it bare to **suppress** ("mine only": `ctx.isAction(CartActions)`). Prefer the
|
|
207
|
+
defer form unless a sibling segment must own the unrelated-action decision.
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
// re-render only when checkout actually succeeded; defer otherwise
|
|
211
|
+
revalidate((ctx) => (ctx.isAction(checkout) && ctx.actionResult?.ok) || undefined),
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**The source is the source of truth.** Structure, types, and update policy are
|
|
215
|
+
visible and local in the tree — read top-down, no hidden global model to hold in
|
|
216
|
+
your head. A snippet earns its place only if, from the code alone, you can answer:
|
|
217
|
+
_what URLs exist and who owns them?_ (composition), _can I trust this reference
|
|
218
|
+
without leaving the call site?_ (type-safety), _what re-renders after this
|
|
219
|
+
action?_ (partial rendering). If any answer needs another file, it isn't legible
|
|
220
|
+
yet.
|
|
221
|
+
|
|
222
|
+
**Reading Rango's own source.** Rango is consumed as raw TypeScript — the
|
|
223
|
+
`exports` map resolves `@rangojs/router` and its subpaths to `./src/*.ts` for
|
|
224
|
+
both types and runtime, so a consuming app bundles Rango straight from source.
|
|
225
|
+
Only the `./vite` plugin entry and the CLI `bin` load from `dist/`. To confirm
|
|
226
|
+
any runtime or type detail against an installed copy, read the resolved source
|
|
227
|
+
under `node_modules/@rangojs/router/src/`, not `dist/` — the runtime does not
|
|
228
|
+
resolve `dist/` outside `./vite`, and it may lag `src/`.
|
|
229
|
+
|
|
11
230
|
## Skills
|
|
12
231
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
| `/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
| `/
|
|
26
|
-
| `/
|
|
27
|
-
| `/
|
|
28
|
-
| `/
|
|
29
|
-
| `/
|
|
30
|
-
| `/
|
|
31
|
-
| `/
|
|
32
|
-
| `/
|
|
33
|
-
| `/
|
|
34
|
-
| `/
|
|
35
|
-
| `/
|
|
36
|
-
| `/
|
|
232
|
+
Grouped by concern — read when you need to…
|
|
233
|
+
|
|
234
|
+
**Positioning & evaluation**:
|
|
235
|
+
|
|
236
|
+
| Skill | Description |
|
|
237
|
+
| ------------- | ------------------------------------------------------------ |
|
|
238
|
+
| `/comparison` | Compare Rango with Next.js, TanStack Start, and Waku fairly. |
|
|
239
|
+
|
|
240
|
+
**Structure & routing** — shape URLs, layouts, navigation, and request processing:
|
|
241
|
+
|
|
242
|
+
| Skill | Description |
|
|
243
|
+
| ------------------------- | -------------------------------------------------------------------------- |
|
|
244
|
+
| `/router-setup` | Create and configure the RSC router |
|
|
245
|
+
| `/route` | Define routes with `urls()`, `path()`, and `include()` |
|
|
246
|
+
| `/layout` | Layouts that wrap child routes |
|
|
247
|
+
| `/parallel` | Multi-column layouts and sidebars |
|
|
248
|
+
| `/intercept` | Modal/slide-over patterns for soft navigation |
|
|
249
|
+
| `/middleware` | Request processing and authentication |
|
|
250
|
+
| `/host-router` | Multi-app host routing with domain/subdomain patterns |
|
|
251
|
+
| `/links` | URL generation: ctx.reverse, href, useHref, useMount, scopedReverse |
|
|
252
|
+
| `/response-routes` | JSON/text/HTML/XML/stream endpoints with `path.json()`, `path.text()` |
|
|
253
|
+
| `/api-client` | Typed client for consuming your own response-route JSON APIs (recipe) |
|
|
254
|
+
| `/mime-routes` | Content negotiation — same URL, different response types via Accept header |
|
|
255
|
+
| `/streams-and-websockets` | SSE via `path.stream` and WebSocket upgrades via `path.any` |
|
|
256
|
+
| `/handler-use` | Attach default loaders/middleware to a handler via `handler.use` |
|
|
257
|
+
| `/composability` | Reusable route-helper factories (structure vs config) |
|
|
258
|
+
|
|
259
|
+
**Data & caching** — fetch, mutate, and cache:
|
|
260
|
+
|
|
261
|
+
| Skill | Description |
|
|
262
|
+
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
263
|
+
| `/loader` | Data loaders with `createLoader()` and `revalidate()` |
|
|
264
|
+
| `/server-actions` | Mutations with `"use server"`, useActionState, validation, revalidation |
|
|
265
|
+
| `/caching` | Segment caching with memory or KV stores |
|
|
266
|
+
| `/use-cache` | Function-level caching with `"use cache"` directive |
|
|
267
|
+
| `/cache-guide` | When to use `cache()` vs `"use cache"` — differences and decision guide |
|
|
268
|
+
| `/document-cache` | Edge caching with Cache-Control headers |
|
|
269
|
+
| `/ppr` | PPR shell caching: cached shell served instantly, live holes resumed — a hole is a `loading()` subtree OR a pending promise under `<Suspense>` (no loader needed) |
|
|
270
|
+
| `/prerender` | Pre-render route segments at build time (Passthrough live fallback) |
|
|
271
|
+
| `/shell-manifest` | Replayed handles as cache metadata read by live loaders (frozen shell, batched live holes) |
|
|
272
|
+
|
|
273
|
+
**Client & presentation** — build the client-side UX:
|
|
274
|
+
|
|
275
|
+
| Skill | Description |
|
|
276
|
+
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
277
|
+
| `/hooks` | Client-side React hooks |
|
|
278
|
+
| `/theme` | Light/dark mode with FOUC prevention |
|
|
279
|
+
| `/i18n` | Locale routing with `:locale?`, resolution chains, react-intl integration |
|
|
280
|
+
| `/fonts` | Load web fonts with preload hints |
|
|
281
|
+
| `/css` | Import CSS in the Document `<head>` (`?url` + managed `precedence` links) |
|
|
282
|
+
| `/scripts` | Inject third-party scripts (GTM/analytics) into head/body via the `Script` handle; nonce auto-applied to document-rendered scripts |
|
|
283
|
+
| `/tailwind` | Set up Tailwind CSS v4 with `?url` imports |
|
|
284
|
+
| `/view-transitions` | React View Transitions on layouts, routes, and parallel slots |
|
|
285
|
+
| `/defer-hydration` | Full body HTML in the PPR shell + hydration off the critical path (gated Suspense boundary, content-as-fallback) |
|
|
286
|
+
| `/breadcrumbs` | Built-in Breadcrumbs handle for breadcrumb navigation |
|
|
287
|
+
| `/react-compiler` | Enable React Compiler (opt-in) the vite-rsc way; client-only scope |
|
|
288
|
+
|
|
289
|
+
**Observability & production health**:
|
|
290
|
+
|
|
291
|
+
| Skill | Description |
|
|
292
|
+
| ------------------ | ------------------------------------------------------------------------ |
|
|
293
|
+
| `/observability` | `debugPerformance`, `Server-Timing`, structured telemetry, tracing |
|
|
294
|
+
| `/bundle-analysis` | Audit your app's production bundle for server leaks and oversized chunks |
|
|
295
|
+
| `/debug-manifest` | Inspect route manifest structure |
|
|
296
|
+
|
|
297
|
+
**Deployment**:
|
|
298
|
+
|
|
299
|
+
| Skill | Description |
|
|
300
|
+
| --------- | ----------------------------------------------------------------------------------------- |
|
|
301
|
+
| `/vercel` | Deploy to Vercel Functions (`preset: "vercel"`), Runtime Cache, and `createVercelTracing` |
|
|
302
|
+
|
|
303
|
+
**Testing**:
|
|
304
|
+
|
|
305
|
+
| Skill | Description |
|
|
306
|
+
| ---------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
307
|
+
| `/testing` | Unit (loaders/middleware/reverse/components), integration (dispatch/Flight), and e2e (dev+prod parity, progressive enhancement) |
|
|
308
|
+
|
|
309
|
+
**Setup, types & migration**:
|
|
310
|
+
|
|
311
|
+
| Skill | Description |
|
|
312
|
+
| ----------------------- | ----------------------------------------------- |
|
|
313
|
+
| `/typesafety` | Type-safe routes, params, href, and environment |
|
|
314
|
+
| `/migrate-nextjs` | Migrate a Next.js App Router project to Rango |
|
|
315
|
+
| `/migrate-react-router` | Migrate a React Router / Remix project to Rango |
|
|
37
316
|
|
|
38
317
|
## Quick Start
|
|
39
318
|
|
|
@@ -89,10 +368,23 @@ Each file is classified by its contents:
|
|
|
89
368
|
Directories are scanned recursively for `.ts`/`.tsx` files, skipping `node_modules`,
|
|
90
369
|
dotfiles, and existing `.gen.` files.
|
|
91
370
|
|
|
371
|
+
> The two generated files are **not interchangeable surfaces**.
|
|
372
|
+
> `router.named-routes.gen.ts` augments the global `GeneratedRouteMap` for
|
|
373
|
+
> named-route typing (`Handler<"name">`, `ctx.reverse("name")`, prerender).
|
|
374
|
+
> Per-module `*.gen.ts` exports a local `routes` map for `useReverse(routes)`
|
|
375
|
+
> and explicit local handler typing (`Handler<".name", routes>`). Neither
|
|
376
|
+
> carries response payloads — response/MIME payload inference comes from
|
|
377
|
+
> `typeof router.routeMap` via `RegisteredRoutes`, not `*.named-routes.gen.ts`.
|
|
378
|
+
> See `/typesafety` for the full surface breakdown.
|
|
379
|
+
|
|
92
380
|
### Recursive includes
|
|
93
381
|
|
|
94
382
|
The generator follows `include()` calls across files, resolving imports to build
|
|
95
|
-
the full route tree.
|
|
383
|
+
the full route tree. It resolves both the eager form `include("/x", patterns)`
|
|
384
|
+
and the code-split async form `include("/x", () => import("./x"))` — for the
|
|
385
|
+
latter it walks the imported module's `export default urls(...)`, including any
|
|
386
|
+
nested `include()`s inside it — so a code-split route group is still fully typed
|
|
387
|
+
(see `/composability`). Circular includes are detected and warned about.
|
|
96
388
|
|
|
97
389
|
### First-wins deduplication
|
|
98
390
|
|