@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
package/src/rsc/handler.ts
CHANGED
|
@@ -8,12 +8,11 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { createElement } from "react";
|
|
11
|
-
import {
|
|
11
|
+
import { isRouteNotFoundError } from "../errors.js";
|
|
12
12
|
import { matchMiddleware, executeMiddleware } from "../router/middleware.js";
|
|
13
13
|
import {
|
|
14
14
|
runWithRequestContext,
|
|
15
15
|
setRequestContextParams,
|
|
16
|
-
requireRequestContext,
|
|
17
16
|
getRequestContext,
|
|
18
17
|
_getRequestContext,
|
|
19
18
|
createRequestContext,
|
|
@@ -31,6 +30,12 @@ import {
|
|
|
31
30
|
interceptRedirectForPartial,
|
|
32
31
|
buildRouteMiddlewareEntries,
|
|
33
32
|
} from "./helpers.js";
|
|
33
|
+
import { guardOutgoingRedirect } from "./redirect-guard.js";
|
|
34
|
+
import { resolvedHandleStream } from "../handles/deferred-resolution.js";
|
|
35
|
+
import {
|
|
36
|
+
isWebSocketUpgradeResponse,
|
|
37
|
+
appendVaryAccept,
|
|
38
|
+
} from "../response-utils.js";
|
|
34
39
|
import {
|
|
35
40
|
handleResponseRoute,
|
|
36
41
|
type ResponseRouteMatch,
|
|
@@ -44,8 +49,6 @@ import {
|
|
|
44
49
|
createReverseFunction,
|
|
45
50
|
stripInternalParams,
|
|
46
51
|
} from "../router/handler-context.js";
|
|
47
|
-
import { getRouterContext } from "../router/router-context.js";
|
|
48
|
-
import { resolveSink, safeEmit } from "../router/telemetry.js";
|
|
49
52
|
import { contextSet } from "../context-var.js";
|
|
50
53
|
import {
|
|
51
54
|
hasCachedManifest,
|
|
@@ -56,6 +59,8 @@ import {
|
|
|
56
59
|
getRouterTrie,
|
|
57
60
|
} from "../route-map-builder.js";
|
|
58
61
|
import type { HandlerContext } from "./handler-context.js";
|
|
62
|
+
import type { CacheErrorCategory } from "../cache/cache-error.js";
|
|
63
|
+
import type { SegmentCacheStore } from "../cache/types.js";
|
|
59
64
|
import { buildRouterTrieFromUrlpatterns } from "./manifest-init.js";
|
|
60
65
|
import { handleProgressiveEnhancement } from "./progressive-enhancement.js";
|
|
61
66
|
import {
|
|
@@ -64,7 +69,10 @@ import {
|
|
|
64
69
|
type ActionContinuation,
|
|
65
70
|
} from "./server-action.js";
|
|
66
71
|
import { handleLoaderFetch } from "./loader-fetch.js";
|
|
67
|
-
import {
|
|
72
|
+
import {
|
|
73
|
+
checkRequestOrigin,
|
|
74
|
+
ORIGIN_CHECK_PHASE_BY_MODE,
|
|
75
|
+
} from "./origin-guard.js";
|
|
68
76
|
import { handleRscRendering } from "./rsc-rendering.js";
|
|
69
77
|
import {
|
|
70
78
|
withTimeout,
|
|
@@ -77,10 +85,13 @@ import {
|
|
|
77
85
|
appendMetric,
|
|
78
86
|
buildMetricsTiming,
|
|
79
87
|
} from "../router/metrics.js";
|
|
88
|
+
import { observePhase, PHASES } from "../router/instrument.js";
|
|
89
|
+
import { safeEmit, resolveSink, getRequestId } from "../router/telemetry.js";
|
|
80
90
|
import {
|
|
81
91
|
startSSRSetup,
|
|
82
92
|
getSSRSetup,
|
|
83
93
|
mayNeedSSR,
|
|
94
|
+
isRscRequest,
|
|
84
95
|
SSR_SETUP_VAR,
|
|
85
96
|
} from "./ssr-setup.js";
|
|
86
97
|
import {
|
|
@@ -122,12 +133,35 @@ import {
|
|
|
122
133
|
* });
|
|
123
134
|
* ```
|
|
124
135
|
*/
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Response that tells the client to do a full document navigation. Shared by
|
|
139
|
+
* the terminal reload plans (version-mismatch and app-switch): an empty 200
|
|
140
|
+
* carrying X-RSC-Reload, which the client turns into window.location.href.
|
|
141
|
+
*/
|
|
142
|
+
function createReloadResponse(reloadUrl: string) {
|
|
143
|
+
return createResponseWithMergedHeaders(null, {
|
|
144
|
+
status: 200,
|
|
145
|
+
headers: {
|
|
146
|
+
"X-RSC-Reload": reloadUrl,
|
|
147
|
+
"content-type": "text/x-component;charset=utf-8",
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
125
152
|
export function createRSCHandler<
|
|
126
153
|
TEnv = unknown,
|
|
127
154
|
TRoutes extends Record<string, string> = Record<string, string>,
|
|
128
155
|
>(options: CreateRSCHandlerOptions<TEnv, TRoutes>) {
|
|
129
156
|
const { router, version = VERSION, nonce: nonceProvider } = options;
|
|
130
157
|
|
|
158
|
+
// Handler-owned registry of explicit per-scope stores from cache({ store }).
|
|
159
|
+
// Lives in the closure so it is scoped per handler (multi-router deployments
|
|
160
|
+
// get separate registries) and accumulates every explicit store this handler
|
|
161
|
+
// resolves across requests. updateTag()/revalidateTag() iterate it to reach
|
|
162
|
+
// stores not covered by the app-level ctx._cacheStore.
|
|
163
|
+
const explicitTaggedStores = new Set<SegmentCacheStore>();
|
|
164
|
+
|
|
131
165
|
// Use provided deps or default to @vitejs/plugin-rsc/rsc exports
|
|
132
166
|
const deps = options.deps ?? rscDeps;
|
|
133
167
|
const {
|
|
@@ -213,23 +247,18 @@ export function createRSCHandler<
|
|
|
213
247
|
metadata: { timeout: true, phase, durationMs },
|
|
214
248
|
});
|
|
215
249
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
customHandler: !!router.onTimeout,
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
} catch {
|
|
232
|
-
// Router context may not be available
|
|
250
|
+
if (router.telemetry) {
|
|
251
|
+
safeEmit(resolveSink(router.telemetry), {
|
|
252
|
+
type: "request.timeout",
|
|
253
|
+
timestamp: performance.now(),
|
|
254
|
+
requestId: getRequestId(request),
|
|
255
|
+
phase,
|
|
256
|
+
pathname: url.pathname,
|
|
257
|
+
routeKey,
|
|
258
|
+
actionId,
|
|
259
|
+
durationMs,
|
|
260
|
+
customHandler: !!router.onTimeout,
|
|
261
|
+
});
|
|
233
262
|
}
|
|
234
263
|
|
|
235
264
|
if (router.onTimeout) {
|
|
@@ -262,16 +291,27 @@ export function createRSCHandler<
|
|
|
262
291
|
function createRedirectFlightResponse(
|
|
263
292
|
redirectUrl: string,
|
|
264
293
|
locationState?: Record<string, unknown>,
|
|
294
|
+
external?: boolean,
|
|
265
295
|
): Response {
|
|
266
296
|
const redirectPayload: RscPayload = {
|
|
267
297
|
metadata: {
|
|
268
298
|
pathname: redirectUrl,
|
|
269
299
|
segments: [],
|
|
270
|
-
redirect: { url: redirectUrl },
|
|
300
|
+
redirect: { url: redirectUrl, ...(external && { external: true }) },
|
|
271
301
|
...(locationState && { locationState }),
|
|
272
302
|
},
|
|
273
303
|
};
|
|
274
|
-
const rscStream = renderToReadableStream<RscPayload>(redirectPayload
|
|
304
|
+
const rscStream = renderToReadableStream<RscPayload>(redirectPayload, {
|
|
305
|
+
onError: (error: unknown) => {
|
|
306
|
+
const reqCtx = _getRequestContext<TEnv>();
|
|
307
|
+
if (!reqCtx) return;
|
|
308
|
+
callOnError(error, "rendering", {
|
|
309
|
+
request: reqCtx.request,
|
|
310
|
+
url: reqCtx.url,
|
|
311
|
+
env: reqCtx.env,
|
|
312
|
+
});
|
|
313
|
+
},
|
|
314
|
+
});
|
|
275
315
|
return createResponseWithMergedHeaders(rscStream, {
|
|
276
316
|
status: 200,
|
|
277
317
|
headers: { "content-type": "text/x-component;charset=utf-8" },
|
|
@@ -352,7 +392,7 @@ export function createRSCHandler<
|
|
|
352
392
|
// Resolve cache store configuration
|
|
353
393
|
// Priority: options.cache (handler override) > router.cache (router default)
|
|
354
394
|
// Store is enabled only if: config provided, enabled, and no ?__no_cache query param
|
|
355
|
-
let cacheStore
|
|
395
|
+
let cacheStore: SegmentCacheStore | undefined;
|
|
356
396
|
const cacheOption = options.cache ?? router.cache;
|
|
357
397
|
if (cacheOption && !url.searchParams.has("__no_cache")) {
|
|
358
398
|
const cacheConfig =
|
|
@@ -419,10 +459,18 @@ export function createRSCHandler<
|
|
|
419
459
|
url,
|
|
420
460
|
variables,
|
|
421
461
|
cacheStore,
|
|
462
|
+
explicitTaggedStores,
|
|
422
463
|
cacheProfiles: router.cacheProfiles,
|
|
423
464
|
executionContext: executionCtx,
|
|
424
465
|
themeConfig: router.themeConfig,
|
|
466
|
+
stateCookieName: router.resolvedStateCookieName,
|
|
467
|
+
version,
|
|
425
468
|
});
|
|
469
|
+
// Thread the true request entry timestamp onto the context so a metrics
|
|
470
|
+
// store created MID-request (ctx.debugPerformance() / getMetricsStore) anchors
|
|
471
|
+
// to the real start, not the opt-in moment — phases that began earlier then
|
|
472
|
+
// report non-negative offsets. Set unconditionally: debug may be enabled later.
|
|
473
|
+
requestContext._handlerStart = handlerStart;
|
|
426
474
|
if (earlyMetricsStore) {
|
|
427
475
|
requestContext._debugPerformance = true;
|
|
428
476
|
requestContext._metricsStore = earlyMetricsStore;
|
|
@@ -431,7 +479,7 @@ export function createRSCHandler<
|
|
|
431
479
|
// can surface non-fatal errors through the router's onError callback.
|
|
432
480
|
requestContext._reportBackgroundError = (
|
|
433
481
|
error: unknown,
|
|
434
|
-
category:
|
|
482
|
+
category: CacheErrorCategory,
|
|
435
483
|
) => {
|
|
436
484
|
callOnError(error, "cache", {
|
|
437
485
|
request,
|
|
@@ -464,79 +512,112 @@ export function createRSCHandler<
|
|
|
464
512
|
// Store basename on request context (scoped per-request via existing ALS)
|
|
465
513
|
requestContext._basename = router.basename;
|
|
466
514
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
);
|
|
515
|
+
// Resolved span tracing for this request (read at each traced phase).
|
|
516
|
+
requestContext._tracing = router.tracing;
|
|
517
|
+
|
|
518
|
+
// The "rango.request" span is opened inside the request context so the
|
|
519
|
+
// Cloudflare runner can read executionContext.tracing, and so every nested
|
|
520
|
+
// phase span (and the platform's automatic KV/D1/fetch spans) nests under
|
|
521
|
+
// it. Construction-bound: the span ends when the Response is built, never
|
|
522
|
+
// wrapping the streamed body. metric:false — handler:total is metered
|
|
523
|
+
// directly below (a grand total incl. the pre-context bootstrap timings).
|
|
524
|
+
// When tracing is off this is a direct pass-through.
|
|
525
|
+
return runWithRequestContext(requestContext, () =>
|
|
526
|
+
observePhase(PHASES.request, async (span) => {
|
|
527
|
+
span.setAttribute("http.method", request.method);
|
|
528
|
+
// The matched route template is not known until match() runs later, so
|
|
529
|
+
// emit the concrete path as url.path (low-level), NOT http.route — the
|
|
530
|
+
// latter is reserved for the low-cardinality template (OTel convention).
|
|
531
|
+
span.setAttribute("url.path", url.pathname);
|
|
532
|
+
|
|
533
|
+
// Core handler logic (wrapped by middleware)
|
|
534
|
+
const coreHandler = async (): Promise<Response> => {
|
|
535
|
+
return coreRequestHandler(request, env, url, variables, nonce);
|
|
536
|
+
};
|
|
484
537
|
|
|
485
|
-
if
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
538
|
+
// Execute middleware chain if any, otherwise call core handler directly
|
|
539
|
+
let response: Response;
|
|
540
|
+
if (matchedMiddleware.length > 0) {
|
|
541
|
+
const mwResponse = await executeMiddleware(
|
|
542
|
+
matchedMiddleware,
|
|
543
|
+
request,
|
|
544
|
+
env,
|
|
545
|
+
variables,
|
|
546
|
+
coreHandler,
|
|
547
|
+
createReverseFunction(getRequiredRouteMap()),
|
|
492
548
|
);
|
|
493
|
-
|
|
549
|
+
|
|
550
|
+
if (
|
|
551
|
+
url.searchParams.has("_rsc_partial") ||
|
|
552
|
+
url.searchParams.has("_rsc_action")
|
|
553
|
+
) {
|
|
554
|
+
const intercepted = interceptRedirectForPartial(
|
|
555
|
+
mwResponse,
|
|
556
|
+
createRedirectFlightResponse,
|
|
557
|
+
);
|
|
558
|
+
response = intercepted ?? finalizeResponse(mwResponse);
|
|
559
|
+
} else {
|
|
560
|
+
response = finalizeResponse(mwResponse);
|
|
561
|
+
}
|
|
494
562
|
} else {
|
|
495
|
-
response =
|
|
563
|
+
response = await coreHandler();
|
|
496
564
|
}
|
|
497
|
-
} else {
|
|
498
|
-
response = await coreHandler();
|
|
499
|
-
}
|
|
500
565
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
566
|
+
// Finalize metrics after all middleware (including post-next work)
|
|
567
|
+
// has completed so :post spans are captured in the timeline.
|
|
568
|
+
// Handler timing parts are always emitted (even without debug metrics)
|
|
569
|
+
// so non-debug requests still get bootstrap Server-Timing entries.
|
|
570
|
+
const handlerTimingArr: string[] = variables.__handlerTiming || [];
|
|
571
|
+
// Preserve any existing Server-Timing set by response routes or middleware
|
|
572
|
+
const existingTiming = response.headers.get("Server-Timing");
|
|
573
|
+
const timingParts = existingTiming
|
|
574
|
+
? [existingTiming, ...handlerTimingArr]
|
|
575
|
+
: [...handlerTimingArr];
|
|
576
|
+
|
|
577
|
+
const metricsStore = requestContext._metricsStore;
|
|
578
|
+
if (metricsStore) {
|
|
579
|
+
// When the store was created at handler start (earlyMetricsStore),
|
|
580
|
+
// handler:total covers the full request. When ctx.debugPerformance()
|
|
581
|
+
// created the store mid-request its requestStart is now the threaded
|
|
582
|
+
// _handlerStart (== handlerStart), so both branches yield the true
|
|
583
|
+
// request entry; reading the store's own anchor keeps this correct even
|
|
584
|
+
// if a store ever lands without the threading (falls back to its start).
|
|
585
|
+
const totalStart = earlyMetricsStore
|
|
586
|
+
? handlerStart
|
|
587
|
+
: metricsStore.requestStart;
|
|
588
|
+
appendMetric(
|
|
589
|
+
metricsStore,
|
|
590
|
+
"handler:total",
|
|
591
|
+
totalStart,
|
|
592
|
+
performance.now() - totalStart,
|
|
593
|
+
);
|
|
594
|
+
const metricsTiming = buildMetricsTiming(
|
|
595
|
+
request.method,
|
|
596
|
+
url.pathname,
|
|
597
|
+
metricsStore,
|
|
598
|
+
);
|
|
599
|
+
if (metricsTiming) timingParts.push(metricsTiming);
|
|
600
|
+
}
|
|
534
601
|
|
|
535
|
-
|
|
536
|
-
|
|
602
|
+
const fullTiming = timingParts.join(", ");
|
|
603
|
+
if (fullTiming && !isWebSocketUpgradeResponse(response)) {
|
|
604
|
+
try {
|
|
605
|
+
response.headers.set("Server-Timing", fullTiming);
|
|
606
|
+
} catch {
|
|
607
|
+
// Immutable headers (e.g. a passed-through platform Response) — drop
|
|
608
|
+
// the timing header, never the response. Instrumentation must not
|
|
609
|
+
// 500 a request.
|
|
610
|
+
}
|
|
611
|
+
}
|
|
537
612
|
|
|
538
|
-
|
|
539
|
-
|
|
613
|
+
// Single open-redirect chokepoint: every response (PE, full-page,
|
|
614
|
+
// middleware short-circuit, response-route) funnels through here, so
|
|
615
|
+
// guarding browser-followed (3xx) redirects once covers them all and any
|
|
616
|
+
// future redirect exit. Soft SPA/Flight redirects are 200/204 and pass
|
|
617
|
+
// through untouched (validated client-side instead).
|
|
618
|
+
return guardOutgoingRedirect(response, url.origin, router.basename);
|
|
619
|
+
}),
|
|
620
|
+
);
|
|
540
621
|
};
|
|
541
622
|
|
|
542
623
|
// Core request handling logic (separated for middleware wrapping).
|
|
@@ -593,10 +674,7 @@ export function createRSCHandler<
|
|
|
593
674
|
routerId: router.id,
|
|
594
675
|
});
|
|
595
676
|
} catch (error) {
|
|
596
|
-
if (
|
|
597
|
-
error instanceof RouteNotFoundError ||
|
|
598
|
-
(error instanceof Error && error.name === "RouteNotFoundError")
|
|
599
|
-
) {
|
|
677
|
+
if (isRouteNotFoundError(error)) {
|
|
600
678
|
// Let the render path handle 404 — match()/matchPartial() will
|
|
601
679
|
// re-throw RouteNotFoundError and the catch block in
|
|
602
680
|
// executeRenderWithMiddleware renders the not-found page.
|
|
@@ -637,24 +715,18 @@ export function createRSCHandler<
|
|
|
637
715
|
console.log(
|
|
638
716
|
`[RSC] Version mismatch: client=${url.searchParams.get("_rsc_v")}, server=${version}. Forcing reload.`,
|
|
639
717
|
);
|
|
640
|
-
return
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
718
|
+
return createReloadResponse(plan.reloadUrl);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
if (plan.mode === "app-switch") {
|
|
722
|
+
// Cross-app SPA navigation crossed a host-router app boundary. Force a
|
|
723
|
+
// real document navigation so the target app's document is re-established
|
|
724
|
+
// (stylesheets, theme, warmup, prefetch-TTL). See request-classification.
|
|
725
|
+
return createReloadResponse(plan.reloadUrl);
|
|
647
726
|
}
|
|
648
727
|
|
|
649
728
|
// ---- 3. Origin guard (gate for action/loader/PE modes) ----
|
|
650
|
-
const originPhase
|
|
651
|
-
plan.mode === "action"
|
|
652
|
-
? "action"
|
|
653
|
-
: plan.mode === "loader"
|
|
654
|
-
? "loader"
|
|
655
|
-
: plan.mode === "pe-render"
|
|
656
|
-
? "pe-form"
|
|
657
|
-
: null;
|
|
729
|
+
const originPhase = ORIGIN_CHECK_PHASE_BY_MODE[plan.mode];
|
|
658
730
|
if (originPhase) {
|
|
659
731
|
const originResult = await checkRequestOrigin(
|
|
660
732
|
request,
|
|
@@ -682,22 +754,17 @@ export function createRSCHandler<
|
|
|
682
754
|
},
|
|
683
755
|
});
|
|
684
756
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
host: request.headers.get("host"),
|
|
697
|
-
});
|
|
698
|
-
}
|
|
699
|
-
} catch {
|
|
700
|
-
// Router context may not be available
|
|
757
|
+
if (router.telemetry) {
|
|
758
|
+
safeEmit(resolveSink(router.telemetry), {
|
|
759
|
+
type: "request.origin-rejected",
|
|
760
|
+
timestamp: performance.now(),
|
|
761
|
+
requestId: getRequestId(request),
|
|
762
|
+
method: request.method,
|
|
763
|
+
pathname: url.pathname,
|
|
764
|
+
phase: originPhase,
|
|
765
|
+
origin: request.headers.get("origin"),
|
|
766
|
+
host: request.headers.get("host"),
|
|
767
|
+
});
|
|
701
768
|
}
|
|
702
769
|
|
|
703
770
|
return originResult;
|
|
@@ -728,11 +795,11 @@ export function createRSCHandler<
|
|
|
728
795
|
nonce: string | undefined,
|
|
729
796
|
): Promise<Response> {
|
|
730
797
|
// Common setup
|
|
731
|
-
const handleStore =
|
|
798
|
+
const handleStore = getRequestContext()._handleStore;
|
|
732
799
|
|
|
733
800
|
// Wire up error reporting for late streaming-handle failures
|
|
734
801
|
handleStore.onError = (error: Error) => {
|
|
735
|
-
const reqCtx =
|
|
802
|
+
const reqCtx = getRequestContext();
|
|
736
803
|
callOnError(error, "handler", {
|
|
737
804
|
request,
|
|
738
805
|
url,
|
|
@@ -740,22 +807,17 @@ export function createRSCHandler<
|
|
|
740
807
|
params: reqCtx.params as Record<string, string>,
|
|
741
808
|
handledByBoundary: true,
|
|
742
809
|
});
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
params: reqCtx.params as Record<string, string>,
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
|
-
} catch {
|
|
758
|
-
// Router context may not be available (e.g. prerender path)
|
|
810
|
+
if (router.telemetry) {
|
|
811
|
+
safeEmit(resolveSink(router.telemetry), {
|
|
812
|
+
type: "handler.error",
|
|
813
|
+
timestamp: performance.now(),
|
|
814
|
+
requestId: getRequestId(request),
|
|
815
|
+
error,
|
|
816
|
+
handledByBoundary: true,
|
|
817
|
+
pathname: url.pathname,
|
|
818
|
+
routeKey: reqCtx._routeName,
|
|
819
|
+
params: reqCtx.params as Record<string, string>,
|
|
820
|
+
});
|
|
759
821
|
}
|
|
760
822
|
};
|
|
761
823
|
|
|
@@ -764,7 +826,7 @@ export function createRSCHandler<
|
|
|
764
826
|
// instead of calling resolveRoute again.
|
|
765
827
|
if (plan.mode !== "redirect") {
|
|
766
828
|
setRequestContextParams(plan.route.params, plan.route.routeKey);
|
|
767
|
-
|
|
829
|
+
getRequestContext()._classifiedRoute = plan.route;
|
|
768
830
|
}
|
|
769
831
|
|
|
770
832
|
const routeReverse = createReverseFunction(getRequiredRouteMap());
|
|
@@ -804,8 +866,10 @@ export function createRSCHandler<
|
|
|
804
866
|
);
|
|
805
867
|
}
|
|
806
868
|
const response = responseOutcome.result;
|
|
807
|
-
if (plan.negotiated) {
|
|
808
|
-
|
|
869
|
+
if (plan.negotiated && !isWebSocketUpgradeResponse(response)) {
|
|
870
|
+
// handleResponseRoute (callHandlerWithVary) already appends Vary: Accept
|
|
871
|
+
// for negotiated responses; dedup so we don't emit Vary: Accept, Accept.
|
|
872
|
+
appendVaryAccept(response);
|
|
809
873
|
}
|
|
810
874
|
return response;
|
|
811
875
|
}
|
|
@@ -813,14 +877,28 @@ export function createRSCHandler<
|
|
|
813
877
|
// SSR setup: kick off in parallel for modes that need HTML rendering.
|
|
814
878
|
// Placed after response-route short-circuit so response/mime routes
|
|
815
879
|
// never pay for SSR work.
|
|
816
|
-
|
|
880
|
+
//
|
|
881
|
+
// Only kick off when the request will actually render HTML, so the
|
|
882
|
+
// eager loadSSRModule() + user resolveStreaming() are not started (and
|
|
883
|
+
// never consumed) for a request that returns an RSC stream — that wasted
|
|
884
|
+
// work also leaves an orphaned Promise.all that can reject (D7). PE form
|
|
885
|
+
// submissions always render HTML (handleProgressiveEnhancement renders via
|
|
886
|
+
// getSSRSetup regardless of Accept). For full/partial-render and action,
|
|
887
|
+
// the render-time HTML decision is exactly !isRscRequest — mayNeedSSR is
|
|
888
|
+
// the coarse transport pre-filter, isRscRequest is the precise Accept call
|
|
889
|
+
// (it, unlike mayNeedSSR, treats a MISSING Accept as RSC). Both must pass.
|
|
890
|
+
const willRenderHtml =
|
|
891
|
+
plan.mode === "pe-render" ||
|
|
892
|
+
(mayNeedSSR(request, url) &&
|
|
893
|
+
!isRscRequest(request, url, plan.mode === "partial-render"));
|
|
894
|
+
if (plan.mode !== "loader" && willRenderHtml) {
|
|
817
895
|
variables[SSR_SETUP_VAR] = startSSRSetup(
|
|
818
896
|
handlerCtx,
|
|
819
897
|
request,
|
|
820
898
|
env,
|
|
821
899
|
url,
|
|
822
900
|
router.debugPerformance
|
|
823
|
-
? () =>
|
|
901
|
+
? () => getRequestContext()._metricsStore
|
|
824
902
|
: undefined,
|
|
825
903
|
);
|
|
826
904
|
}
|
|
@@ -861,14 +939,20 @@ export function createRSCHandler<
|
|
|
861
939
|
if (plan.mode === "action") {
|
|
862
940
|
let actionContinuation: ActionContinuation | undefined;
|
|
863
941
|
try {
|
|
942
|
+
// Instrument the action execution as its own phase (action:<actionId> +
|
|
943
|
+
// rango.action), so a POST shows the mutation time AND which action ran,
|
|
944
|
+
// not just the downstream revalidation render. The action's own
|
|
945
|
+
// loaders/fetches nest under rango.action.
|
|
864
946
|
const actionOutcome = await withTimeout(
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
947
|
+
observePhase(PHASES.action(plan.actionId), () =>
|
|
948
|
+
executeServerAction(
|
|
949
|
+
handlerCtx,
|
|
950
|
+
request,
|
|
951
|
+
env,
|
|
952
|
+
url,
|
|
953
|
+
plan.actionId,
|
|
954
|
+
handleStore,
|
|
955
|
+
),
|
|
872
956
|
),
|
|
873
957
|
router.timeouts.actionMs,
|
|
874
958
|
"action",
|
|
@@ -921,47 +1005,17 @@ export function createRSCHandler<
|
|
|
921
1005
|
);
|
|
922
1006
|
}
|
|
923
1007
|
|
|
924
|
-
//
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
plan.
|
|
931
|
-
|
|
932
|
-
request,
|
|
933
|
-
env,
|
|
934
|
-
url,
|
|
935
|
-
variables,
|
|
936
|
-
nonce,
|
|
937
|
-
handleStore,
|
|
938
|
-
isPartial,
|
|
939
|
-
);
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
// PE that fell through (handleProgressiveEnhancement returned null)
|
|
943
|
-
// falls back to full render
|
|
944
|
-
if (plan.mode === "pe-render") {
|
|
945
|
-
return executeRenderWithMiddleware(
|
|
946
|
-
plan.route.routeMiddleware,
|
|
947
|
-
false,
|
|
948
|
-
plan.route.routeKey,
|
|
949
|
-
routeReverse,
|
|
950
|
-
request,
|
|
951
|
-
env,
|
|
952
|
-
url,
|
|
953
|
-
variables,
|
|
954
|
-
nonce,
|
|
955
|
-
handleStore,
|
|
956
|
-
false,
|
|
957
|
-
);
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
// Redirect plan that wasn't handled above (full-page redirect — let
|
|
961
|
-
// the pipeline handle it via match() which returns { redirect: url })
|
|
1008
|
+
// Full render, partial render, fallen-through PE, and full-page redirect all
|
|
1009
|
+
// render through the same middleware-wrapped path. Only full/partial-render
|
|
1010
|
+
// carry negotiation + the partial flag; pe/redirect render plainly.
|
|
1011
|
+
const isPartial = plan.mode === "partial-render";
|
|
1012
|
+
const negotiated =
|
|
1013
|
+
plan.mode === "full-render" || plan.mode === "partial-render"
|
|
1014
|
+
? plan.negotiated
|
|
1015
|
+
: false;
|
|
962
1016
|
return executeRenderWithMiddleware(
|
|
963
1017
|
plan.route.routeMiddleware,
|
|
964
|
-
|
|
1018
|
+
negotiated,
|
|
965
1019
|
plan.route.routeKey,
|
|
966
1020
|
routeReverse,
|
|
967
1021
|
request,
|
|
@@ -970,7 +1024,7 @@ export function createRSCHandler<
|
|
|
970
1024
|
variables,
|
|
971
1025
|
nonce,
|
|
972
1026
|
handleStore,
|
|
973
|
-
|
|
1027
|
+
isPartial,
|
|
974
1028
|
);
|
|
975
1029
|
}
|
|
976
1030
|
|
|
@@ -987,7 +1041,7 @@ export function createRSCHandler<
|
|
|
987
1041
|
url: URL,
|
|
988
1042
|
variables: Record<string, any>,
|
|
989
1043
|
nonce: string | undefined,
|
|
990
|
-
handleStore: ReturnType<typeof
|
|
1044
|
+
handleStore: ReturnType<typeof getRequestContext>["_handleStore"],
|
|
991
1045
|
isPartial: boolean,
|
|
992
1046
|
actionContinuation?: ActionContinuation,
|
|
993
1047
|
): Promise<Response> {
|
|
@@ -1014,17 +1068,28 @@ export function createRSCHandler<
|
|
|
1014
1068
|
nonce,
|
|
1015
1069
|
);
|
|
1016
1070
|
}
|
|
1017
|
-
if (negotiated) {
|
|
1018
|
-
response
|
|
1071
|
+
if (negotiated && !isWebSocketUpgradeResponse(response)) {
|
|
1072
|
+
// handleRscRendering bakes `accept` into the RSC response's Vary list;
|
|
1073
|
+
// dedup so the negotiated append does not list accept twice.
|
|
1074
|
+
appendVaryAccept(response);
|
|
1019
1075
|
}
|
|
1020
1076
|
return response;
|
|
1021
1077
|
} catch (error) {
|
|
1022
1078
|
// Check if middleware/handler returned Response
|
|
1023
1079
|
if (error instanceof Response) {
|
|
1080
|
+
// An action revalidation render is delivered to the client over the
|
|
1081
|
+
// same Flight-parsing path as a partial navigation, so a Response
|
|
1082
|
+
// thrown during it must be converted exactly like a partial one
|
|
1083
|
+
// (raw 200 -> hard-nav hint, 3xx -> Flight redirect). Without this,
|
|
1084
|
+
// the no-middleware path returns the raw Response (the with-middleware
|
|
1085
|
+
// path is already covered by the isPartial || actionContinuation
|
|
1086
|
+
// guard below).
|
|
1087
|
+
const treatAsPartial = isPartial || actionContinuation != null;
|
|
1088
|
+
|
|
1024
1089
|
// During partial (client-side navigation), a 200 Response from a handler
|
|
1025
1090
|
// means the route serves raw content (JSON, text, etc.), not JSX.
|
|
1026
1091
|
// Signal the browser to hard-navigate so it renders the raw response.
|
|
1027
|
-
if (
|
|
1092
|
+
if (treatAsPartial && error.status === 200) {
|
|
1028
1093
|
console.warn(
|
|
1029
1094
|
`[RSC] Route handler at ${url.pathname} returned a Response during client-side navigation. ` +
|
|
1030
1095
|
`Falling back to hard navigation. Use data-external on the <Link> to avoid the extra round-trip.`,
|
|
@@ -1038,7 +1103,7 @@ export function createRSCHandler<
|
|
|
1038
1103
|
});
|
|
1039
1104
|
}
|
|
1040
1105
|
|
|
1041
|
-
if (
|
|
1106
|
+
if (treatAsPartial) {
|
|
1042
1107
|
const intercepted = interceptRedirectForPartial(
|
|
1043
1108
|
error,
|
|
1044
1109
|
createRedirectFlightResponse,
|
|
@@ -1050,10 +1115,7 @@ export function createRSCHandler<
|
|
|
1050
1115
|
}
|
|
1051
1116
|
|
|
1052
1117
|
// Render 404 page for unmatched routes
|
|
1053
|
-
|
|
1054
|
-
error instanceof RouteNotFoundError ||
|
|
1055
|
-
(error instanceof Error && error.name === "RouteNotFoundError");
|
|
1056
|
-
if (isRouteNotFound) {
|
|
1118
|
+
if (isRouteNotFoundError(error)) {
|
|
1057
1119
|
callOnError(error, "routing", {
|
|
1058
1120
|
request,
|
|
1059
1121
|
url,
|
|
@@ -1084,13 +1146,26 @@ export function createRSCHandler<
|
|
|
1084
1146
|
segments: [notFoundSegment],
|
|
1085
1147
|
matched: [],
|
|
1086
1148
|
diff: [],
|
|
1149
|
+
// Shape parity with buildFullPayload (rsc-rendering.ts): the 404
|
|
1150
|
+
// payload carries params/resolvedIds/prefetchCacheTTL the same way a
|
|
1151
|
+
// matched full render does. resolvedIds mirrors the rendered segment
|
|
1152
|
+
// list (the single notFound segment) like the error-boundary path
|
|
1153
|
+
// (match-api.ts) does for its boundary segment.
|
|
1154
|
+
resolvedIds: [notFoundSegment.id],
|
|
1155
|
+
params: {},
|
|
1087
1156
|
isPartial: false,
|
|
1088
1157
|
rootLayout: router.rootLayout,
|
|
1089
|
-
|
|
1158
|
+
// Full (404) render: resolve deferred handle values server-side.
|
|
1159
|
+
handles: resolvedHandleStream(handleStore),
|
|
1090
1160
|
version,
|
|
1161
|
+
prefetchCacheTTL: router.prefetchCacheTTL,
|
|
1162
|
+
prefetchCacheSize: router.prefetchCacheSize,
|
|
1163
|
+
prefetchConcurrency: router.prefetchConcurrency,
|
|
1164
|
+
stateCookieName: router.resolvedStateCookieName,
|
|
1091
1165
|
themeConfig: router.themeConfig,
|
|
1092
1166
|
warmupEnabled: router.warmupEnabled,
|
|
1093
|
-
|
|
1167
|
+
strictMode: router.strictMode,
|
|
1168
|
+
initialTheme: getRequestContext().theme,
|
|
1094
1169
|
},
|
|
1095
1170
|
};
|
|
1096
1171
|
|
|
@@ -1100,16 +1175,15 @@ export function createRSCHandler<
|
|
|
1100
1175
|
},
|
|
1101
1176
|
});
|
|
1102
1177
|
|
|
1103
|
-
|
|
1104
|
-
isPartial ||
|
|
1105
|
-
(!request.headers.get("accept")?.includes("text/html") &&
|
|
1106
|
-
!url.searchParams.has("__html")) ||
|
|
1107
|
-
url.searchParams.has("__rsc");
|
|
1108
|
-
|
|
1109
|
-
if (isRscRequest) {
|
|
1178
|
+
if (isRscRequest(request, url, isPartial)) {
|
|
1110
1179
|
return createResponseWithMergedHeaders(rscStream, {
|
|
1111
1180
|
status: 404,
|
|
1112
|
-
headers: {
|
|
1181
|
+
headers: {
|
|
1182
|
+
"content-type": "text/x-component;charset=utf-8",
|
|
1183
|
+
// Router identity for the client's pre-decode integrity check; a
|
|
1184
|
+
// same-app 404 matches and applies in place. See response-adapter.
|
|
1185
|
+
"X-RSC-Router-Id": router.id,
|
|
1186
|
+
},
|
|
1113
1187
|
});
|
|
1114
1188
|
}
|
|
1115
1189
|
|
|
@@ -1118,7 +1192,7 @@ export function createRSCHandler<
|
|
|
1118
1192
|
request,
|
|
1119
1193
|
env,
|
|
1120
1194
|
url,
|
|
1121
|
-
|
|
1195
|
+
getRequestContext()._metricsStore,
|
|
1122
1196
|
);
|
|
1123
1197
|
const htmlStream = await ssrModule.renderHTML(rscStream, {
|
|
1124
1198
|
nonce,
|