@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
package/src/rsc/handler.ts
CHANGED
|
@@ -13,7 +13,6 @@ 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,7 +30,12 @@ import {
|
|
|
31
30
|
interceptRedirectForPartial,
|
|
32
31
|
buildRouteMiddlewareEntries,
|
|
33
32
|
} from "./helpers.js";
|
|
34
|
-
import {
|
|
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";
|
|
35
39
|
import {
|
|
36
40
|
handleResponseRoute,
|
|
37
41
|
type ResponseRouteMatch,
|
|
@@ -45,8 +49,6 @@ import {
|
|
|
45
49
|
createReverseFunction,
|
|
46
50
|
stripInternalParams,
|
|
47
51
|
} from "../router/handler-context.js";
|
|
48
|
-
import { getRouterContext } from "../router/router-context.js";
|
|
49
|
-
import { resolveSink, safeEmit } from "../router/telemetry.js";
|
|
50
52
|
import { contextSet } from "../context-var.js";
|
|
51
53
|
import {
|
|
52
54
|
hasCachedManifest,
|
|
@@ -57,6 +59,7 @@ import {
|
|
|
57
59
|
getRouterTrie,
|
|
58
60
|
} from "../route-map-builder.js";
|
|
59
61
|
import type { HandlerContext } from "./handler-context.js";
|
|
62
|
+
import type { CacheErrorCategory } from "../cache/cache-error.js";
|
|
60
63
|
import type { SegmentCacheStore } from "../cache/types.js";
|
|
61
64
|
import { buildRouterTrieFromUrlpatterns } from "./manifest-init.js";
|
|
62
65
|
import { handleProgressiveEnhancement } from "./progressive-enhancement.js";
|
|
@@ -82,6 +85,8 @@ import {
|
|
|
82
85
|
appendMetric,
|
|
83
86
|
buildMetricsTiming,
|
|
84
87
|
} from "../router/metrics.js";
|
|
88
|
+
import { observePhase, PHASES } from "../router/instrument.js";
|
|
89
|
+
import { safeEmit, resolveSink, getRequestId } from "../router/telemetry.js";
|
|
85
90
|
import {
|
|
86
91
|
startSSRSetup,
|
|
87
92
|
getSSRSetup,
|
|
@@ -128,12 +133,35 @@ import {
|
|
|
128
133
|
* });
|
|
129
134
|
* ```
|
|
130
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
|
+
|
|
131
152
|
export function createRSCHandler<
|
|
132
153
|
TEnv = unknown,
|
|
133
154
|
TRoutes extends Record<string, string> = Record<string, string>,
|
|
134
155
|
>(options: CreateRSCHandlerOptions<TEnv, TRoutes>) {
|
|
135
156
|
const { router, version = VERSION, nonce: nonceProvider } = options;
|
|
136
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
|
+
|
|
137
165
|
// Use provided deps or default to @vitejs/plugin-rsc/rsc exports
|
|
138
166
|
const deps = options.deps ?? rscDeps;
|
|
139
167
|
const {
|
|
@@ -219,23 +247,18 @@ export function createRSCHandler<
|
|
|
219
247
|
metadata: { timeout: true, phase, durationMs },
|
|
220
248
|
});
|
|
221
249
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
customHandler: !!router.onTimeout,
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
} catch {
|
|
238
|
-
// 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
|
+
});
|
|
239
262
|
}
|
|
240
263
|
|
|
241
264
|
if (router.onTimeout) {
|
|
@@ -268,16 +291,27 @@ export function createRSCHandler<
|
|
|
268
291
|
function createRedirectFlightResponse(
|
|
269
292
|
redirectUrl: string,
|
|
270
293
|
locationState?: Record<string, unknown>,
|
|
294
|
+
external?: boolean,
|
|
271
295
|
): Response {
|
|
272
296
|
const redirectPayload: RscPayload = {
|
|
273
297
|
metadata: {
|
|
274
298
|
pathname: redirectUrl,
|
|
275
299
|
segments: [],
|
|
276
|
-
redirect: { url: redirectUrl },
|
|
300
|
+
redirect: { url: redirectUrl, ...(external && { external: true }) },
|
|
277
301
|
...(locationState && { locationState }),
|
|
278
302
|
},
|
|
279
303
|
};
|
|
280
|
-
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
|
+
});
|
|
281
315
|
return createResponseWithMergedHeaders(rscStream, {
|
|
282
316
|
status: 200,
|
|
283
317
|
headers: { "content-type": "text/x-component;charset=utf-8" },
|
|
@@ -425,10 +459,18 @@ export function createRSCHandler<
|
|
|
425
459
|
url,
|
|
426
460
|
variables,
|
|
427
461
|
cacheStore,
|
|
462
|
+
explicitTaggedStores,
|
|
428
463
|
cacheProfiles: router.cacheProfiles,
|
|
429
464
|
executionContext: executionCtx,
|
|
430
465
|
themeConfig: router.themeConfig,
|
|
466
|
+
stateCookieName: router.resolvedStateCookieName,
|
|
467
|
+
version,
|
|
431
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;
|
|
432
474
|
if (earlyMetricsStore) {
|
|
433
475
|
requestContext._debugPerformance = true;
|
|
434
476
|
requestContext._metricsStore = earlyMetricsStore;
|
|
@@ -437,7 +479,7 @@ export function createRSCHandler<
|
|
|
437
479
|
// can surface non-fatal errors through the router's onError callback.
|
|
438
480
|
requestContext._reportBackgroundError = (
|
|
439
481
|
error: unknown,
|
|
440
|
-
category:
|
|
482
|
+
category: CacheErrorCategory,
|
|
441
483
|
) => {
|
|
442
484
|
callOnError(error, "cache", {
|
|
443
485
|
request,
|
|
@@ -470,81 +512,112 @@ export function createRSCHandler<
|
|
|
470
512
|
// Store basename on request context (scoped per-request via existing ALS)
|
|
471
513
|
requestContext._basename = router.basename;
|
|
472
514
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
);
|
|
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
|
+
};
|
|
490
537
|
|
|
491
|
-
if
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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()),
|
|
498
548
|
);
|
|
499
|
-
|
|
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
|
+
}
|
|
500
562
|
} else {
|
|
501
|
-
response =
|
|
563
|
+
response = await coreHandler();
|
|
502
564
|
}
|
|
503
|
-
} else {
|
|
504
|
-
response = await coreHandler();
|
|
505
|
-
}
|
|
506
565
|
|
|
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
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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
|
+
}
|
|
540
601
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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
|
+
}
|
|
545
612
|
|
|
546
|
-
|
|
547
|
-
|
|
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
|
+
);
|
|
548
621
|
};
|
|
549
622
|
|
|
550
623
|
// Core request handling logic (separated for middleware wrapping).
|
|
@@ -642,13 +715,14 @@ export function createRSCHandler<
|
|
|
642
715
|
console.log(
|
|
643
716
|
`[RSC] Version mismatch: client=${url.searchParams.get("_rsc_v")}, server=${version}. Forcing reload.`,
|
|
644
717
|
);
|
|
645
|
-
return
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
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);
|
|
652
726
|
}
|
|
653
727
|
|
|
654
728
|
// ---- 3. Origin guard (gate for action/loader/PE modes) ----
|
|
@@ -680,22 +754,17 @@ export function createRSCHandler<
|
|
|
680
754
|
},
|
|
681
755
|
});
|
|
682
756
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
host: request.headers.get("host"),
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
} catch {
|
|
698
|
-
// 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
|
+
});
|
|
699
768
|
}
|
|
700
769
|
|
|
701
770
|
return originResult;
|
|
@@ -726,11 +795,11 @@ export function createRSCHandler<
|
|
|
726
795
|
nonce: string | undefined,
|
|
727
796
|
): Promise<Response> {
|
|
728
797
|
// Common setup
|
|
729
|
-
const handleStore =
|
|
798
|
+
const handleStore = getRequestContext()._handleStore;
|
|
730
799
|
|
|
731
800
|
// Wire up error reporting for late streaming-handle failures
|
|
732
801
|
handleStore.onError = (error: Error) => {
|
|
733
|
-
const reqCtx =
|
|
802
|
+
const reqCtx = getRequestContext();
|
|
734
803
|
callOnError(error, "handler", {
|
|
735
804
|
request,
|
|
736
805
|
url,
|
|
@@ -738,22 +807,17 @@ export function createRSCHandler<
|
|
|
738
807
|
params: reqCtx.params as Record<string, string>,
|
|
739
808
|
handledByBoundary: true,
|
|
740
809
|
});
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
params: reqCtx.params as Record<string, string>,
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
|
-
} catch {
|
|
756
|
-
// 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
|
+
});
|
|
757
821
|
}
|
|
758
822
|
};
|
|
759
823
|
|
|
@@ -762,7 +826,7 @@ export function createRSCHandler<
|
|
|
762
826
|
// instead of calling resolveRoute again.
|
|
763
827
|
if (plan.mode !== "redirect") {
|
|
764
828
|
setRequestContextParams(plan.route.params, plan.route.routeKey);
|
|
765
|
-
|
|
829
|
+
getRequestContext()._classifiedRoute = plan.route;
|
|
766
830
|
}
|
|
767
831
|
|
|
768
832
|
const routeReverse = createReverseFunction(getRequiredRouteMap());
|
|
@@ -803,7 +867,9 @@ export function createRSCHandler<
|
|
|
803
867
|
}
|
|
804
868
|
const response = responseOutcome.result;
|
|
805
869
|
if (plan.negotiated && !isWebSocketUpgradeResponse(response)) {
|
|
806
|
-
|
|
870
|
+
// handleResponseRoute (callHandlerWithVary) already appends Vary: Accept
|
|
871
|
+
// for negotiated responses; dedup so we don't emit Vary: Accept, Accept.
|
|
872
|
+
appendVaryAccept(response);
|
|
807
873
|
}
|
|
808
874
|
return response;
|
|
809
875
|
}
|
|
@@ -811,14 +877,28 @@ export function createRSCHandler<
|
|
|
811
877
|
// SSR setup: kick off in parallel for modes that need HTML rendering.
|
|
812
878
|
// Placed after response-route short-circuit so response/mime routes
|
|
813
879
|
// never pay for SSR work.
|
|
814
|
-
|
|
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) {
|
|
815
895
|
variables[SSR_SETUP_VAR] = startSSRSetup(
|
|
816
896
|
handlerCtx,
|
|
817
897
|
request,
|
|
818
898
|
env,
|
|
819
899
|
url,
|
|
820
900
|
router.debugPerformance
|
|
821
|
-
? () =>
|
|
901
|
+
? () => getRequestContext()._metricsStore
|
|
822
902
|
: undefined,
|
|
823
903
|
);
|
|
824
904
|
}
|
|
@@ -859,14 +939,20 @@ export function createRSCHandler<
|
|
|
859
939
|
if (plan.mode === "action") {
|
|
860
940
|
let actionContinuation: ActionContinuation | undefined;
|
|
861
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.
|
|
862
946
|
const actionOutcome = await withTimeout(
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
947
|
+
observePhase(PHASES.action(plan.actionId), () =>
|
|
948
|
+
executeServerAction(
|
|
949
|
+
handlerCtx,
|
|
950
|
+
request,
|
|
951
|
+
env,
|
|
952
|
+
url,
|
|
953
|
+
plan.actionId,
|
|
954
|
+
handleStore,
|
|
955
|
+
),
|
|
870
956
|
),
|
|
871
957
|
router.timeouts.actionMs,
|
|
872
958
|
"action",
|
|
@@ -955,7 +1041,7 @@ export function createRSCHandler<
|
|
|
955
1041
|
url: URL,
|
|
956
1042
|
variables: Record<string, any>,
|
|
957
1043
|
nonce: string | undefined,
|
|
958
|
-
handleStore: ReturnType<typeof
|
|
1044
|
+
handleStore: ReturnType<typeof getRequestContext>["_handleStore"],
|
|
959
1045
|
isPartial: boolean,
|
|
960
1046
|
actionContinuation?: ActionContinuation,
|
|
961
1047
|
): Promise<Response> {
|
|
@@ -983,16 +1069,27 @@ export function createRSCHandler<
|
|
|
983
1069
|
);
|
|
984
1070
|
}
|
|
985
1071
|
if (negotiated && !isWebSocketUpgradeResponse(response)) {
|
|
986
|
-
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);
|
|
987
1075
|
}
|
|
988
1076
|
return response;
|
|
989
1077
|
} catch (error) {
|
|
990
1078
|
// Check if middleware/handler returned Response
|
|
991
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
|
+
|
|
992
1089
|
// During partial (client-side navigation), a 200 Response from a handler
|
|
993
1090
|
// means the route serves raw content (JSON, text, etc.), not JSX.
|
|
994
1091
|
// Signal the browser to hard-navigate so it renders the raw response.
|
|
995
|
-
if (
|
|
1092
|
+
if (treatAsPartial && error.status === 200) {
|
|
996
1093
|
console.warn(
|
|
997
1094
|
`[RSC] Route handler at ${url.pathname} returned a Response during client-side navigation. ` +
|
|
998
1095
|
`Falling back to hard navigation. Use data-external on the <Link> to avoid the extra round-trip.`,
|
|
@@ -1006,7 +1103,7 @@ export function createRSCHandler<
|
|
|
1006
1103
|
});
|
|
1007
1104
|
}
|
|
1008
1105
|
|
|
1009
|
-
if (
|
|
1106
|
+
if (treatAsPartial) {
|
|
1010
1107
|
const intercepted = interceptRedirectForPartial(
|
|
1011
1108
|
error,
|
|
1012
1109
|
createRedirectFlightResponse,
|
|
@@ -1049,13 +1146,26 @@ export function createRSCHandler<
|
|
|
1049
1146
|
segments: [notFoundSegment],
|
|
1050
1147
|
matched: [],
|
|
1051
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: {},
|
|
1052
1156
|
isPartial: false,
|
|
1053
1157
|
rootLayout: router.rootLayout,
|
|
1054
|
-
|
|
1158
|
+
// Full (404) render: resolve deferred handle values server-side.
|
|
1159
|
+
handles: resolvedHandleStream(handleStore),
|
|
1055
1160
|
version,
|
|
1161
|
+
prefetchCacheTTL: router.prefetchCacheTTL,
|
|
1162
|
+
prefetchCacheSize: router.prefetchCacheSize,
|
|
1163
|
+
prefetchConcurrency: router.prefetchConcurrency,
|
|
1164
|
+
stateCookieName: router.resolvedStateCookieName,
|
|
1056
1165
|
themeConfig: router.themeConfig,
|
|
1057
1166
|
warmupEnabled: router.warmupEnabled,
|
|
1058
|
-
|
|
1167
|
+
strictMode: router.strictMode,
|
|
1168
|
+
initialTheme: getRequestContext().theme,
|
|
1059
1169
|
},
|
|
1060
1170
|
};
|
|
1061
1171
|
|
|
@@ -1068,7 +1178,12 @@ export function createRSCHandler<
|
|
|
1068
1178
|
if (isRscRequest(request, url, isPartial)) {
|
|
1069
1179
|
return createResponseWithMergedHeaders(rscStream, {
|
|
1070
1180
|
status: 404,
|
|
1071
|
-
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
|
+
},
|
|
1072
1187
|
});
|
|
1073
1188
|
}
|
|
1074
1189
|
|
|
@@ -1077,7 +1192,7 @@ export function createRSCHandler<
|
|
|
1077
1192
|
request,
|
|
1078
1193
|
env,
|
|
1079
1194
|
url,
|
|
1080
|
-
|
|
1195
|
+
getRequestContext()._metricsStore,
|
|
1081
1196
|
);
|
|
1082
1197
|
const htmlStream = await ssrModule.renderHTML(rscStream, {
|
|
1083
1198
|
nonce,
|