@rangojs/router 0.0.0-experimental.bd6e11bc → 0.0.0-experimental.bdaf10aa
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -4
- package/README.md +296 -887
- package/dist/bin/rango.js +459 -91
- package/dist/testing/vitest.js +36 -2
- package/dist/vite/index.js +1708 -414
- package/package.json +35 -10
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +82 -5
- package/skills/bundle-analysis/SKILL.md +2 -2
- package/skills/cache-guide/SKILL.md +14 -9
- package/skills/caching/SKILL.md +221 -12
- package/skills/catalog.json +271 -0
- package/skills/comparison/SKILL.md +50 -0
- package/skills/comparison/agents/openai.yaml +4 -0
- package/skills/comparison/references/framework-comparison.md +837 -0
- package/skills/composability/SKILL.md +83 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +5 -3
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +11 -3
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +9 -9
- package/skills/hooks/SKILL.md +73 -900
- package/skills/hooks/data.md +273 -0
- package/skills/hooks/handle-and-actions.md +103 -0
- package/skills/hooks/navigation.md +110 -0
- package/skills/hooks/outlets.md +41 -0
- package/skills/hooks/state.md +228 -0
- package/skills/hooks/urls.md +135 -0
- package/skills/host-router/SKILL.md +84 -7
- package/skills/i18n/SKILL.md +1 -1
- package/skills/intercept/SKILL.md +51 -17
- package/skills/layout/SKILL.md +38 -16
- package/skills/links/SKILL.md +1 -1
- package/skills/loader/SKILL.md +48 -20
- package/skills/middleware/SKILL.md +11 -5
- package/skills/migrate-nextjs/SKILL.md +203 -20
- package/skills/migrate-react-router/SKILL.md +59 -675
- package/skills/migrate-react-router/cloudflare-workers.md +129 -0
- package/skills/migrate-react-router/component-migration.md +196 -0
- package/skills/migrate-react-router/data-and-actions.md +225 -0
- package/skills/migrate-react-router/route-mapping.md +271 -0
- package/skills/mime-routes/SKILL.md +3 -3
- package/skills/observability/SKILL.md +70 -5
- package/skills/parallel/SKILL.md +32 -8
- package/skills/ppr/SKILL.md +622 -0
- package/skills/prerender/SKILL.md +59 -28
- package/skills/rango/SKILL.md +124 -50
- package/skills/response-routes/SKILL.md +78 -46
- package/skills/route/SKILL.md +85 -6
- package/skills/router-setup/SKILL.md +41 -6
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +28 -3
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +1 -1
- package/skills/tailwind/SKILL.md +28 -4
- package/skills/testing/SKILL.md +68 -654
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +131 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +85 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/theme/SKILL.md +1 -1
- package/skills/typesafety/SKILL.md +45 -918
- package/skills/typesafety/env-and-bindings.md +254 -0
- package/skills/typesafety/generated-files-and-cli.md +335 -0
- package/skills/typesafety/params-and-search.md +153 -0
- package/skills/typesafety/route-types.md +209 -0
- package/skills/use-cache/SKILL.md +47 -17
- package/skills/vercel/SKILL.md +128 -0
- package/skills/view-transitions/SKILL.md +44 -1
- package/src/__augment-tests__/augmented.check.ts +2 -3
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +1 -1
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +14 -27
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +178 -100
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +81 -68
- package/src/browser/navigation-client.ts +115 -70
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +153 -88
- package/src/browser/navigation-transaction.ts +0 -32
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +157 -144
- package/src/browser/prefetch/cache.ts +148 -81
- package/src/browser/prefetch/fetch.ts +231 -51
- package/src/browser/prefetch/queue.ts +25 -7
- package/src/browser/rango-state.ts +157 -115
- package/src/browser/react/Link.tsx +40 -7
- package/src/browser/react/NavigationProvider.tsx +140 -99
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +17 -2
- package/src/browser/react/index.ts +0 -51
- package/src/browser/react/location-state-shared.ts +14 -15
- package/src/browser/react/location-state.ts +0 -1
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +0 -5
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +10 -5
- package/src/browser/react/use-params.ts +0 -2
- package/src/browser/react/use-router.ts +6 -4
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +0 -13
- package/src/browser/response-adapter.ts +74 -8
- package/src/browser/rsc-router.tsx +97 -22
- package/src/browser/scroll-restoration.ts +15 -8
- package/src/browser/segment-reconciler.ts +31 -21
- package/src/browser/server-action-bridge.ts +216 -38
- package/src/browser/types.ts +94 -22
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/generate-manifest.ts +155 -131
- package/src/build/generate-route-types.ts +1 -1
- package/src/build/index.ts +11 -5
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +152 -22
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +12 -1
- package/src/build/route-types/include-resolution.ts +455 -61
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +15 -2
- package/src/build/route-types/router-processing.ts +77 -41
- package/src/build/route-types/source-scan.ts +105 -7
- package/src/build/runtime-discovery.ts +4 -1
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +58 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +454 -101
- package/src/cache/cache-scope.ts +159 -54
- package/src/cache/cache-tag.ts +149 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +2170 -377
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +126 -41
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +243 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +13 -21
- package/src/cache/shell-snapshot.ts +417 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +194 -99
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1132 -0
- package/src/client.rsc.tsx +39 -22
- package/src/client.tsx +28 -58
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +108 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +13 -1
- package/src/decode-loader-results.ts +18 -2
- package/src/defer.ts +185 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +0 -3
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +57 -40
- package/src/handles/MetaTags.tsx +24 -53
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +35 -8
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +14 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +13 -22
- package/src/host/index.ts +7 -0
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +1 -65
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +6 -2
- package/src/href-client.ts +7 -12
- package/src/index.rsc.ts +88 -8
- package/src/index.ts +90 -16
- package/src/internal-debug.ts +11 -10
- package/src/loader.rsc.ts +19 -9
- package/src/loader.ts +12 -4
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +75 -7
- package/src/redirect-origin.ts +114 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +25 -0
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +13 -49
- package/src/route-definition/dsl-helpers.ts +60 -53
- package/src/route-definition/helper-factories.ts +0 -2
- package/src/route-definition/helpers-types.ts +46 -46
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +6 -1
- package/src/route-definition/use-item-types.ts +3 -6
- package/src/route-map-builder.ts +41 -20
- package/src/route-types.ts +0 -5
- package/src/router/content-negotiation.ts +58 -23
- package/src/router/error-handling.ts +44 -17
- package/src/router/find-match.ts +129 -30
- package/src/router/handler-context.ts +6 -1
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +35 -2
- package/src/router/lazy-includes.ts +79 -56
- package/src/router/loader-resolution.ts +151 -73
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +74 -40
- package/src/router/match-api.ts +76 -52
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +181 -178
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +115 -194
- package/src/router/match-middleware/cache-store.ts +61 -50
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +36 -67
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +0 -116
- package/src/router/middleware.ts +231 -120
- package/src/router/navigation-snapshot.ts +7 -56
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +99 -152
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +111 -66
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +47 -42
- package/src/router/revalidation.ts +75 -81
- package/src/router/route-snapshot.ts +14 -3
- package/src/router/router-context.ts +6 -29
- package/src/router/router-interfaces.ts +70 -8
- package/src/router/router-options.ts +126 -4
- package/src/router/segment-resolution/fresh.ts +104 -80
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +155 -39
- package/src/router/segment-resolution/loader-mask.ts +60 -0
- package/src/router/segment-resolution/loader-snapshot.ts +259 -0
- package/src/router/segment-resolution/mask-nested.ts +83 -0
- package/src/router/segment-resolution/revalidation.ts +215 -304
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +35 -15
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +54 -35
- package/src/router/telemetry-otel.ts +160 -200
- package/src/router/telemetry.ts +9 -23
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +215 -0
- package/src/router/trie-matching.ts +171 -64
- package/src/router/types.ts +1 -63
- package/src/router/url-params.ts +13 -5
- package/src/router.ts +119 -48
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +1 -0
- package/src/rsc/handler.ts +267 -152
- package/src/rsc/helpers.ts +78 -4
- package/src/rsc/index.ts +1 -4
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +29 -42
- package/src/rsc/nonce.ts +10 -1
- package/src/rsc/origin-guard.ts +11 -15
- package/src/rsc/progressive-enhancement.ts +120 -13
- package/src/rsc/redirect-guard.ts +100 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +58 -141
- package/src/rsc/rsc-rendering.ts +492 -49
- package/src/rsc/runtime-warnings.ts +14 -0
- package/src/rsc/server-action.ts +268 -82
- package/src/rsc/shell-capture.ts +1190 -0
- package/src/rsc/shell-serve.ts +181 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +45 -3
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +31 -26
- package/src/segment-loader-promise.ts +49 -4
- package/src/segment-system.tsx +260 -95
- package/src/server/context.ts +99 -9
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +125 -2
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +379 -138
- package/src/ssr/index.tsx +491 -182
- package/src/ssr/inject-rsc-eager.ts +167 -0
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +44 -48
- package/src/testing/collect-handle.ts +14 -31
- package/src/testing/dispatch.ts +533 -160
- package/src/testing/e2e/fixture.ts +45 -11
- package/src/testing/e2e/index.ts +1 -22
- package/src/testing/e2e/matchers.ts +0 -16
- package/src/testing/e2e/parity.ts +85 -4
- package/src/testing/e2e/server.ts +12 -0
- package/src/testing/flight-matchers.ts +7 -14
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +36 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +30 -0
- package/src/testing/flight.ts +145 -70
- package/src/testing/generated-routes.ts +26 -50
- package/src/testing/index.ts +18 -19
- package/src/testing/internal/context.ts +184 -68
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +357 -0
- package/src/testing/render-route.tsx +134 -115
- package/src/testing/run-loader.ts +140 -51
- package/src/testing/run-middleware.ts +59 -33
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +1 -1
- package/src/testing/vitest-stubs/cloudflare-workers.ts +1 -1
- package/src/testing/vitest.ts +138 -16
- package/src/theme/ThemeProvider.tsx +56 -84
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +15 -15
- package/src/types/handler-context.ts +45 -15
- package/src/types/index.ts +2 -10
- package/src/types/loader-types.ts +6 -3
- package/src/types/request-scope.ts +8 -22
- package/src/types/route-config.ts +20 -52
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +100 -13
- package/src/urls/include-helper.ts +10 -12
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +2 -8
- package/src/urls/path-helper-types.ts +52 -14
- package/src/urls/path-helper.ts +5 -54
- package/src/urls/pattern-types.ts +36 -0
- package/src/urls/type-extraction.ts +76 -42
- package/src/urls/urls-function.ts +0 -14
- package/src/use-loader.tsx +0 -186
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/discovery/bundle-postprocess.ts +2 -1
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +34 -43
- package/src/vite/discovery/discovery-errors.ts +61 -0
- package/src/vite/discovery/prerender-collection.ts +33 -46
- package/src/vite/discovery/state.ts +12 -1
- package/src/vite/discovery/virtual-module-codegen.ts +1 -11
- package/src/vite/index.ts +9 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +143 -10
- package/src/vite/plugins/cjs-to-esm.ts +8 -12
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +0 -10
- package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
- package/src/vite/plugins/expose-action-id.ts +2 -73
- package/src/vite/plugins/expose-id-utils.ts +85 -56
- package/src/vite/plugins/expose-ids/export-analysis.ts +30 -43
- package/src/vite/plugins/expose-ids/handler-transform.ts +5 -31
- package/src/vite/plugins/expose-ids/loader-transform.ts +12 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +98 -26
- package/src/vite/plugins/expose-internal-ids.ts +10 -1
- package/src/vite/plugins/performance-tracks.ts +0 -3
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +21 -46
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +22 -27
- package/src/vite/plugins/version-plugin.ts +6 -66
- package/src/vite/plugins/virtual-entries.ts +137 -26
- package/src/vite/rango.ts +146 -135
- package/src/vite/router-discovery.ts +189 -48
- package/src/vite/utils/ast-handler-extract.ts +11 -20
- package/src/vite/utils/bundle-analysis.ts +6 -13
- package/src/vite/utils/client-chunks.ts +0 -6
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +0 -22
- package/src/vite/utils/manifest-utils.ts +4 -75
- package/src/vite/utils/package-resolution.ts +1 -73
- package/src/vite/utils/prerender-utils.ts +71 -44
- package/src/vite/utils/shared-utils.ts +55 -37
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: server-actions
|
|
3
|
-
description: Define and call server actions (`"use server"`) — forms, useActionState, useOptimistic, validation, error handling, redirects, revalidation
|
|
3
|
+
description: Define and call server actions (`"use server"`) — forms, useActionState, useOptimistic, validation, error handling, redirects, revalidation. Use when handling a form submission on the server, calling a server function from a client component, or needing optimistic UI after a mutation.
|
|
4
4
|
argument-hint: "[action]"
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -24,7 +24,8 @@ with no framework wrapper. All standard React hooks (`useActionState`,
|
|
|
24
24
|
Use loaders and route handlers for reads. Use actions for writes. After an
|
|
25
25
|
action runs, the matched route tree can partially re-render so handlers and
|
|
26
26
|
loaders that opt into revalidation see the new state — see "Revalidation"
|
|
27
|
-
below.
|
|
27
|
+
below. For the read-side APIs (`createLoader`, `useLoader`, `useFetchLoader`),
|
|
28
|
+
see `/loader`.
|
|
28
29
|
|
|
29
30
|
## Revalidation Model
|
|
30
31
|
|
|
@@ -465,7 +466,10 @@ See `/middleware` for the full cross-segment revalidation contract.
|
|
|
465
466
|
|
|
466
467
|
`redirect()` works inside actions. Both `return redirect(...)` and
|
|
467
468
|
`throw redirect(...)` are supported and behave the same way for the
|
|
468
|
-
client. Throwing is clearer when the redirect is conditional
|
|
469
|
+
client. Throwing is clearer when the redirect is conditional, and it keeps
|
|
470
|
+
the action's return type narrow (e.g. `Promise<void>`) — `redirect()` returns
|
|
471
|
+
a `Response`, so the `return` form needs `Promise<Response>` in the signature.
|
|
472
|
+
Prefer `throw redirect(...)`; never cast with `as any`.
|
|
469
473
|
|
|
470
474
|
```typescript
|
|
471
475
|
"use server";
|
|
@@ -490,6 +494,27 @@ Redirects from actions render the **target** route tree's matched segments
|
|
|
490
494
|
source page's — the target is what the user sees next. See `/hooks
|
|
491
495
|
useLocationState` for reading flash state on the target page.
|
|
492
496
|
|
|
497
|
+
### Same-origin by default (open-redirect protection)
|
|
498
|
+
|
|
499
|
+
`redirect()` is same-origin by default on every path — JS, no-JS PE, and
|
|
500
|
+
full-page. A cross-origin target (e.g. from unvalidated user input) is blocked
|
|
501
|
+
and the user is sent to the app root instead, so `redirect(userInput)` can never
|
|
502
|
+
become an open redirect. To intentionally redirect off-host (an OAuth provider,
|
|
503
|
+
say), opt in explicitly:
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
throw redirect("https://accounts.google.com/o/oauth2/v2/auth?...", {
|
|
507
|
+
external: true,
|
|
508
|
+
});
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
`{ external: true }` is the audit point: passing user input with it re-opens the
|
|
512
|
+
cross-origin risk and is your responsibility (the Rails `allow_other_host: true`
|
|
513
|
+
model). Omit it and off-host targets stay blocked. `external` only waives the
|
|
514
|
+
**same-origin** rule, not scheme safety: the target must be `http(s)` — a
|
|
515
|
+
`javascript:` or `data:` URL is still neutralized, so a forged or mistaken
|
|
516
|
+
`external` target can never become a scriptable navigation.
|
|
517
|
+
|
|
493
518
|
## Error Handling
|
|
494
519
|
|
|
495
520
|
### Validation errors — return them as state
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: shell-manifest
|
|
3
|
+
description: Shell manifest pattern — replayed handles as cache metadata that live loaders read, e.g. a prerendered product list with batched live prices. Use when a cached/prerendered shell needs to feed IDs or metadata to a live loader for freshly-fetched data.
|
|
4
|
+
argument-hint:
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Shell Manifest — cache metadata for live loaders
|
|
8
|
+
|
|
9
|
+
Use this when a cached or prerendered shell has dynamic holes, and the live
|
|
10
|
+
data layer needs to know **what the shell actually contains** — which
|
|
11
|
+
products, which slots, which keys. The frozen render describes itself
|
|
12
|
+
through a handle; loaders (always live) read that description and fetch
|
|
13
|
+
exactly the dynamic data the shell needs, in one batch.
|
|
14
|
+
|
|
15
|
+
Canonical case: a prerendered product list where prices must stay live.
|
|
16
|
+
|
|
17
|
+
## The problem this solves
|
|
18
|
+
|
|
19
|
+
Any cached-shell-plus-live-holes design has a coordination gap: how does the
|
|
20
|
+
live layer know what the holes need?
|
|
21
|
+
|
|
22
|
+
- **Per-hole fetching** (each `<Price>` component fetching for itself) is the
|
|
23
|
+
N+1 default — N visible products, N queries.
|
|
24
|
+
- **A loader that re-queries the list** ("current top products") drifts from
|
|
25
|
+
a stale shell — right prices attached to wrong products.
|
|
26
|
+
|
|
27
|
+
The shell manifest closes the gap with a consistency guarantee: the loader
|
|
28
|
+
reads the ids the shell _actually rendered_, replayed from the same stored
|
|
29
|
+
artifact, so the holes can never desync from the shell and the query is
|
|
30
|
+
batched.
|
|
31
|
+
|
|
32
|
+
## The mechanism (three features composed)
|
|
33
|
+
|
|
34
|
+
1. **Handles record data at render time.** The handler pushes to a handle
|
|
35
|
+
(`ctx.use(Handle)`) while it renders — at build time for `Prerender`, on
|
|
36
|
+
the cache miss for `cache()`.
|
|
37
|
+
2. **Replay on every hit.** Handle data is stored with the Flight payload
|
|
38
|
+
and replayed into the handle store on cache/prerender hits — handler code
|
|
39
|
+
does not re-run, but its pushes do.
|
|
40
|
+
3. **Loaders read after the render barrier.** A DSL loader can
|
|
41
|
+
`await ctx.rendered()` (waits for all non-loader segments to settle —
|
|
42
|
+
fresh render or replay alike), then `ctx.use(Handle)` returns the
|
|
43
|
+
**collected** handle data.
|
|
44
|
+
|
|
45
|
+
Loaders are live by default, so the read happens on every request even when
|
|
46
|
+
the shell is a hit.
|
|
47
|
+
|
|
48
|
+
## Canonical example: prerendered list, live prices
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
// handles/rendered-products.ts
|
|
52
|
+
import { createHandle } from "@rangojs/router";
|
|
53
|
+
|
|
54
|
+
// TData = string (one push per product id), collected to a flat string[]
|
|
55
|
+
export const RenderedProducts = createHandle<string, string[]>((segments) =>
|
|
56
|
+
segments.flat(),
|
|
57
|
+
);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
// routes/products.tsx — the list is baked at build time; prices are not
|
|
62
|
+
import { Prerender } from "@rangojs/router";
|
|
63
|
+
import { RenderedProducts } from "../handles/rendered-products";
|
|
64
|
+
import { Price } from "../components/price";
|
|
65
|
+
|
|
66
|
+
export const ProductList = Prerender(
|
|
67
|
+
async () => [{ category: "espresso" }, { category: "filter" }],
|
|
68
|
+
async (ctx) => {
|
|
69
|
+
const products = await db.productsByCategory(ctx.params.category);
|
|
70
|
+
const track = ctx.use(RenderedProducts);
|
|
71
|
+
for (const p of products) track(p.id);
|
|
72
|
+
return (
|
|
73
|
+
<ul>
|
|
74
|
+
{products.map((p) => (
|
|
75
|
+
<li key={p.id}>
|
|
76
|
+
{p.name} <Price id={p.id} />
|
|
77
|
+
</li>
|
|
78
|
+
))}
|
|
79
|
+
</ul>
|
|
80
|
+
);
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
// loaders/prices.ts — one batched query for exactly the rendered products
|
|
87
|
+
import { createLoader } from "@rangojs/router";
|
|
88
|
+
import { RenderedProducts } from "../handles/rendered-products";
|
|
89
|
+
|
|
90
|
+
export const PriceLoader = createLoader(async (ctx) => {
|
|
91
|
+
"use server";
|
|
92
|
+
await ctx.rendered();
|
|
93
|
+
const ids = ctx.use(RenderedProducts);
|
|
94
|
+
return db.pricesFor(ids); // Map<string, number> keyed by product id
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
// urls.tsx — wire the route and register the loader
|
|
100
|
+
path("/products/:category", ProductList, { name: "products" }, () => [
|
|
101
|
+
loader(PriceLoader),
|
|
102
|
+
]);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
// components/price.tsx — live hole in the frozen shell
|
|
107
|
+
"use client";
|
|
108
|
+
import { useLoader } from "@rangojs/router/client";
|
|
109
|
+
import { PriceLoader } from "../loaders/prices";
|
|
110
|
+
|
|
111
|
+
export function Price({ id }: { id: string }) {
|
|
112
|
+
const { data } = useLoader(PriceLoader);
|
|
113
|
+
return <span>{formatPrice(data[id])}</span>;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Request flow on a hit: stored payload replays (handler never runs) → handle
|
|
118
|
+
data lands in the store → render barrier resolves → `PriceLoader` reads the
|
|
119
|
+
replayed ids → one query → prices stream into `<Price>` components.
|
|
120
|
+
|
|
121
|
+
## Works with runtime cache() too
|
|
122
|
+
|
|
123
|
+
`Prerender` is build-time caching; the replay mechanism is identical for the
|
|
124
|
+
runtime segment cache. Wrap the route in `cache()` instead and the handler
|
|
125
|
+
pushes on the miss, replays on every hit:
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
cache({ ttl: 600, tags: ["products"] }, () => [
|
|
129
|
+
path("/products/:category", ProductList, { name: "products" }, () => [
|
|
130
|
+
loader(PriceLoader),
|
|
131
|
+
]),
|
|
132
|
+
]);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Contract and gotchas
|
|
136
|
+
|
|
137
|
+
- **The manifest is exactly as fresh as the shell.** Replayed handle data is
|
|
138
|
+
frozen with the payload. To change _which_ products render, invalidate the
|
|
139
|
+
shell (`updateTag("products")`, TTL expiry, rebuild) — never treat the
|
|
140
|
+
loader as the refresh path for the list itself. This is the point:
|
|
141
|
+
shell and holes cannot desync because they share one artifact.
|
|
142
|
+
- **No request-scoped data in a manifest handle.** The handle data is baked
|
|
143
|
+
into a shared artifact — the same cross-user rule as any cached content.
|
|
144
|
+
Ids, slugs, slot names, variant keys: yes. Anything derived from
|
|
145
|
+
`cookies()`/`headers()`: no.
|
|
146
|
+
- **`ctx.rendered()` is experimental and DSL-loaders-only.** It throws in
|
|
147
|
+
fetchable/standalone loader calls that run outside a route render.
|
|
148
|
+
- **The reading loader serializes after the shell.** `await ctx.rendered()`
|
|
149
|
+
deliberately gives up loader/render parallelism — on a miss the loader
|
|
150
|
+
waits for segment resolution; on a hit (the common case for a cached
|
|
151
|
+
shell) replay is immediate and the wait is negligible. A
|
|
152
|
+
`debugPerformance` waterfall shows this loader after the render bar; for
|
|
153
|
+
this pattern that is the contract, not a regression.
|
|
154
|
+
- **`ctx.use(Handle)` before `await ctx.rendered()` throws** in a loader,
|
|
155
|
+
with an error saying to await the barrier first.
|
|
156
|
+
- **Deferred handle values are resolved before storage** (resolve-by-default),
|
|
157
|
+
so the manifest read always sees plain values, never promises.
|
|
158
|
+
|
|
159
|
+
## Testing
|
|
160
|
+
|
|
161
|
+
`runLoader` seeds the barrier and the collected handle value directly —
|
|
162
|
+
matched by handle reference (the same seeding style as loader deps):
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { runLoader } from "@rangojs/router/testing";
|
|
166
|
+
|
|
167
|
+
const prices = await runLoader(PriceLoader, {
|
|
168
|
+
rendered: true,
|
|
169
|
+
handles: [[RenderedProducts, ["widget-a", "widget-b"]]],
|
|
170
|
+
env: { DB: fakeDb },
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
This tests the loader's post-barrier logic. The real
|
|
175
|
+
push → store → replay → barrier wiring is covered at the e2e tier (dev +
|
|
176
|
+
production), like every cache-path behavior.
|
|
177
|
+
|
|
178
|
+
## Related
|
|
179
|
+
|
|
180
|
+
- `/prerender` — `Prerender`/`Passthrough`, build flow, passthrough fallback
|
|
181
|
+
- `/caching` — segment `cache()`, stores, tags
|
|
182
|
+
- `/loader` — loader context, `ctx.rendered()`, streaming
|
|
183
|
+
- `/hooks` — `useHandle` for reading handle data in client components
|
|
184
|
+
- `/rango` → "Passing data down the tree" — this pattern is the frozen→live
|
|
185
|
+
counterpart of that ladder
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: streams-and-websockets
|
|
3
|
-
description: Long-lived Response handlers — Server-Sent Events (SSE) via path.stream and WebSocket upgrades via path.any on Cloudflare Workers, including middleware interaction and runtime caveats.
|
|
3
|
+
description: Long-lived Response handlers — Server-Sent Events (SSE) via path.stream and WebSocket upgrades via path.any on Cloudflare Workers, including middleware interaction and runtime caveats. Use when streaming live updates to the browser, or opening a WebSocket connection from a route on Cloudflare Workers.
|
|
4
4
|
argument-hint: "[sse | websocket | agents]"
|
|
5
5
|
---
|
|
6
6
|
|
package/skills/tailwind/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tailwind
|
|
3
|
-
description: Set up Tailwind CSS v4 with the Document component and CSS imports
|
|
3
|
+
description: Set up Tailwind CSS v4 with the Document component and CSS imports. Use when adding Tailwind CSS to a Rango app, or Tailwind classes aren't being applied or generated.
|
|
4
4
|
argument-hint: [setup]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -37,7 +37,11 @@ export default defineConfig({
|
|
|
37
37
|
|
|
38
38
|
## Document Component
|
|
39
39
|
|
|
40
|
-
Import the CSS file with `?url` to get a hashed URL, then preload and link it in
|
|
40
|
+
Import the CSS file with `?url` to get a hashed URL, then preload and link it in
|
|
41
|
+
`<head>`. Give the `<link rel="stylesheet">` a `precedence` prop so React 19
|
|
42
|
+
manages it as a resource — de-duped by `href`, ordered, and loaded before paint
|
|
43
|
+
(no flash of unstyled content). See
|
|
44
|
+
[Stylesheets and cross-app navigation](#stylesheets-and-cross-app-navigation):
|
|
41
45
|
|
|
42
46
|
```tsx
|
|
43
47
|
// src/document.tsx
|
|
@@ -51,8 +55,8 @@ export function Document({ children }: { children: ReactNode }) {
|
|
|
51
55
|
return (
|
|
52
56
|
<html lang="en">
|
|
53
57
|
<head>
|
|
54
|
-
<link rel="preload" href={styles} as="style" />
|
|
55
|
-
<link rel="stylesheet" href={styles} />
|
|
58
|
+
<link rel="preload" href={styles} as="style" precedence="default" />
|
|
59
|
+
<link rel="stylesheet" href={styles} precedence="default" />
|
|
56
60
|
<MetaTags />
|
|
57
61
|
</head>
|
|
58
62
|
<body className="font-sans antialiased text-slate-900 bg-slate-50">
|
|
@@ -63,6 +67,26 @@ export function Document({ children }: { children: ReactNode }) {
|
|
|
63
67
|
}
|
|
64
68
|
```
|
|
65
69
|
|
|
70
|
+
## Stylesheets and cross-app navigation
|
|
71
|
+
|
|
72
|
+
The `precedence` prop opts a `<link rel="stylesheet">` into React 19's managed
|
|
73
|
+
stylesheet model — React de-duplicates it by `href`, orders it by precedence, and
|
|
74
|
+
loads it before paint (avoiding a flash of unstyled content). It is the
|
|
75
|
+
recommended way to render a stylesheet link, which is why the example above uses
|
|
76
|
+
it. (A bare side-effect `import "./index.css"` also produces managed CSS via
|
|
77
|
+
`@vitejs/plugin-rsc`, but carries an SSR-streaming caveat — prefer the `?url` +
|
|
78
|
+
`<link precedence>` form for document CSS. See `/css`.)
|
|
79
|
+
|
|
80
|
+
For **host-router** apps (`/host-router`), a client-side navigation that crosses
|
|
81
|
+
an app boundary is a **full document load**, not a soft swap — the framework
|
|
82
|
+
redirects on an app switch. So each app's document (its stylesheets, theme, meta)
|
|
83
|
+
is always re-established cleanly by the target app's own load; you do not have to
|
|
84
|
+
coordinate stylesheet `href`s or `precedence` across apps. (This replaced an
|
|
85
|
+
earlier soft cross-app swap, under which a stylesheet shared across apps — every
|
|
86
|
+
app's `@import "tailwindcss"` compiles to one hashed asset — could be dropped by
|
|
87
|
+
React's by-`href` resource dedup if the apps disagreed on `precedence`. The full
|
|
88
|
+
reload removes that footgun entirely.)
|
|
89
|
+
|
|
66
90
|
The `?url` suffix tells Vite to return the processed CSS file's URL instead of injecting it as a side effect. This gives you a stable, hashed asset path that works in both development and production.
|
|
67
91
|
|
|
68
92
|
## Customizing the Theme
|