@rangojs/router 0.0.0-experimental.bd6e11bc → 0.0.0-experimental.bdaf10aa
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -4
- package/README.md +296 -887
- package/dist/bin/rango.js +459 -91
- package/dist/testing/vitest.js +36 -2
- package/dist/vite/index.js +1708 -414
- package/package.json +35 -10
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +82 -5
- package/skills/bundle-analysis/SKILL.md +2 -2
- package/skills/cache-guide/SKILL.md +14 -9
- package/skills/caching/SKILL.md +221 -12
- package/skills/catalog.json +271 -0
- package/skills/comparison/SKILL.md +50 -0
- package/skills/comparison/agents/openai.yaml +4 -0
- package/skills/comparison/references/framework-comparison.md +837 -0
- package/skills/composability/SKILL.md +83 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +5 -3
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +11 -3
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +9 -9
- package/skills/hooks/SKILL.md +73 -900
- package/skills/hooks/data.md +273 -0
- package/skills/hooks/handle-and-actions.md +103 -0
- package/skills/hooks/navigation.md +110 -0
- package/skills/hooks/outlets.md +41 -0
- package/skills/hooks/state.md +228 -0
- package/skills/hooks/urls.md +135 -0
- package/skills/host-router/SKILL.md +84 -7
- package/skills/i18n/SKILL.md +1 -1
- package/skills/intercept/SKILL.md +51 -17
- package/skills/layout/SKILL.md +38 -16
- package/skills/links/SKILL.md +1 -1
- package/skills/loader/SKILL.md +48 -20
- package/skills/middleware/SKILL.md +11 -5
- package/skills/migrate-nextjs/SKILL.md +203 -20
- package/skills/migrate-react-router/SKILL.md +59 -675
- package/skills/migrate-react-router/cloudflare-workers.md +129 -0
- package/skills/migrate-react-router/component-migration.md +196 -0
- package/skills/migrate-react-router/data-and-actions.md +225 -0
- package/skills/migrate-react-router/route-mapping.md +271 -0
- package/skills/mime-routes/SKILL.md +3 -3
- package/skills/observability/SKILL.md +70 -5
- package/skills/parallel/SKILL.md +32 -8
- package/skills/ppr/SKILL.md +622 -0
- package/skills/prerender/SKILL.md +59 -28
- package/skills/rango/SKILL.md +124 -50
- package/skills/response-routes/SKILL.md +78 -46
- package/skills/route/SKILL.md +85 -6
- package/skills/router-setup/SKILL.md +41 -6
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +28 -3
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +1 -1
- package/skills/tailwind/SKILL.md +28 -4
- package/skills/testing/SKILL.md +68 -654
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +131 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +85 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/theme/SKILL.md +1 -1
- package/skills/typesafety/SKILL.md +45 -918
- package/skills/typesafety/env-and-bindings.md +254 -0
- package/skills/typesafety/generated-files-and-cli.md +335 -0
- package/skills/typesafety/params-and-search.md +153 -0
- package/skills/typesafety/route-types.md +209 -0
- package/skills/use-cache/SKILL.md +47 -17
- package/skills/vercel/SKILL.md +128 -0
- package/skills/view-transitions/SKILL.md +44 -1
- package/src/__augment-tests__/augmented.check.ts +2 -3
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +1 -1
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +14 -27
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +178 -100
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +81 -68
- package/src/browser/navigation-client.ts +115 -70
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +153 -88
- package/src/browser/navigation-transaction.ts +0 -32
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +157 -144
- package/src/browser/prefetch/cache.ts +148 -81
- package/src/browser/prefetch/fetch.ts +231 -51
- package/src/browser/prefetch/queue.ts +25 -7
- package/src/browser/rango-state.ts +157 -115
- package/src/browser/react/Link.tsx +40 -7
- package/src/browser/react/NavigationProvider.tsx +140 -99
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +17 -2
- package/src/browser/react/index.ts +0 -51
- package/src/browser/react/location-state-shared.ts +14 -15
- package/src/browser/react/location-state.ts +0 -1
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +0 -5
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +10 -5
- package/src/browser/react/use-params.ts +0 -2
- package/src/browser/react/use-router.ts +6 -4
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +0 -13
- package/src/browser/response-adapter.ts +74 -8
- package/src/browser/rsc-router.tsx +97 -22
- package/src/browser/scroll-restoration.ts +15 -8
- package/src/browser/segment-reconciler.ts +31 -21
- package/src/browser/server-action-bridge.ts +216 -38
- package/src/browser/types.ts +94 -22
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/generate-manifest.ts +155 -131
- package/src/build/generate-route-types.ts +1 -1
- package/src/build/index.ts +11 -5
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +152 -22
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +12 -1
- package/src/build/route-types/include-resolution.ts +455 -61
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +15 -2
- package/src/build/route-types/router-processing.ts +77 -41
- package/src/build/route-types/source-scan.ts +105 -7
- package/src/build/runtime-discovery.ts +4 -1
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +58 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +454 -101
- package/src/cache/cache-scope.ts +159 -54
- package/src/cache/cache-tag.ts +149 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +2170 -377
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +126 -41
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +243 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +13 -21
- package/src/cache/shell-snapshot.ts +417 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +194 -99
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1132 -0
- package/src/client.rsc.tsx +39 -22
- package/src/client.tsx +28 -58
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +108 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +13 -1
- package/src/decode-loader-results.ts +18 -2
- package/src/defer.ts +185 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +0 -3
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +57 -40
- package/src/handles/MetaTags.tsx +24 -53
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +35 -8
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +14 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +13 -22
- package/src/host/index.ts +7 -0
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +1 -65
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +6 -2
- package/src/href-client.ts +7 -12
- package/src/index.rsc.ts +88 -8
- package/src/index.ts +90 -16
- package/src/internal-debug.ts +11 -10
- package/src/loader.rsc.ts +19 -9
- package/src/loader.ts +12 -4
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +75 -7
- package/src/redirect-origin.ts +114 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +25 -0
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +13 -49
- package/src/route-definition/dsl-helpers.ts +60 -53
- package/src/route-definition/helper-factories.ts +0 -2
- package/src/route-definition/helpers-types.ts +46 -46
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +6 -1
- package/src/route-definition/use-item-types.ts +3 -6
- package/src/route-map-builder.ts +41 -20
- package/src/route-types.ts +0 -5
- package/src/router/content-negotiation.ts +58 -23
- package/src/router/error-handling.ts +44 -17
- package/src/router/find-match.ts +129 -30
- package/src/router/handler-context.ts +6 -1
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +35 -2
- package/src/router/lazy-includes.ts +79 -56
- package/src/router/loader-resolution.ts +151 -73
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +74 -40
- package/src/router/match-api.ts +76 -52
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +181 -178
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +115 -194
- package/src/router/match-middleware/cache-store.ts +61 -50
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +36 -67
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +0 -116
- package/src/router/middleware.ts +231 -120
- package/src/router/navigation-snapshot.ts +7 -56
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +99 -152
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +111 -66
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +47 -42
- package/src/router/revalidation.ts +75 -81
- package/src/router/route-snapshot.ts +14 -3
- package/src/router/router-context.ts +6 -29
- package/src/router/router-interfaces.ts +70 -8
- package/src/router/router-options.ts +126 -4
- package/src/router/segment-resolution/fresh.ts +104 -80
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +155 -39
- package/src/router/segment-resolution/loader-mask.ts +60 -0
- package/src/router/segment-resolution/loader-snapshot.ts +259 -0
- package/src/router/segment-resolution/mask-nested.ts +83 -0
- package/src/router/segment-resolution/revalidation.ts +215 -304
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +35 -15
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +54 -35
- package/src/router/telemetry-otel.ts +160 -200
- package/src/router/telemetry.ts +9 -23
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +215 -0
- package/src/router/trie-matching.ts +171 -64
- package/src/router/types.ts +1 -63
- package/src/router/url-params.ts +13 -5
- package/src/router.ts +119 -48
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +1 -0
- package/src/rsc/handler.ts +267 -152
- package/src/rsc/helpers.ts +78 -4
- package/src/rsc/index.ts +1 -4
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +29 -42
- package/src/rsc/nonce.ts +10 -1
- package/src/rsc/origin-guard.ts +11 -15
- package/src/rsc/progressive-enhancement.ts +120 -13
- package/src/rsc/redirect-guard.ts +100 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +58 -141
- package/src/rsc/rsc-rendering.ts +492 -49
- package/src/rsc/runtime-warnings.ts +14 -0
- package/src/rsc/server-action.ts +268 -82
- package/src/rsc/shell-capture.ts +1190 -0
- package/src/rsc/shell-serve.ts +181 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +45 -3
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +31 -26
- package/src/segment-loader-promise.ts +49 -4
- package/src/segment-system.tsx +260 -95
- package/src/server/context.ts +99 -9
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +125 -2
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +379 -138
- package/src/ssr/index.tsx +491 -182
- package/src/ssr/inject-rsc-eager.ts +167 -0
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +44 -48
- package/src/testing/collect-handle.ts +14 -31
- package/src/testing/dispatch.ts +533 -160
- package/src/testing/e2e/fixture.ts +45 -11
- package/src/testing/e2e/index.ts +1 -22
- package/src/testing/e2e/matchers.ts +0 -16
- package/src/testing/e2e/parity.ts +85 -4
- package/src/testing/e2e/server.ts +12 -0
- package/src/testing/flight-matchers.ts +7 -14
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +36 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +30 -0
- package/src/testing/flight.ts +145 -70
- package/src/testing/generated-routes.ts +26 -50
- package/src/testing/index.ts +18 -19
- package/src/testing/internal/context.ts +184 -68
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +357 -0
- package/src/testing/render-route.tsx +134 -115
- package/src/testing/run-loader.ts +140 -51
- package/src/testing/run-middleware.ts +59 -33
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +1 -1
- package/src/testing/vitest-stubs/cloudflare-workers.ts +1 -1
- package/src/testing/vitest.ts +138 -16
- package/src/theme/ThemeProvider.tsx +56 -84
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +15 -15
- package/src/types/handler-context.ts +45 -15
- package/src/types/index.ts +2 -10
- package/src/types/loader-types.ts +6 -3
- package/src/types/request-scope.ts +8 -22
- package/src/types/route-config.ts +20 -52
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +100 -13
- package/src/urls/include-helper.ts +10 -12
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +2 -8
- package/src/urls/path-helper-types.ts +52 -14
- package/src/urls/path-helper.ts +5 -54
- package/src/urls/pattern-types.ts +36 -0
- package/src/urls/type-extraction.ts +76 -42
- package/src/urls/urls-function.ts +0 -14
- package/src/use-loader.tsx +0 -186
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/discovery/bundle-postprocess.ts +2 -1
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +34 -43
- package/src/vite/discovery/discovery-errors.ts +61 -0
- package/src/vite/discovery/prerender-collection.ts +33 -46
- package/src/vite/discovery/state.ts +12 -1
- package/src/vite/discovery/virtual-module-codegen.ts +1 -11
- package/src/vite/index.ts +9 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +143 -10
- package/src/vite/plugins/cjs-to-esm.ts +8 -12
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +0 -10
- package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
- package/src/vite/plugins/expose-action-id.ts +2 -73
- package/src/vite/plugins/expose-id-utils.ts +85 -56
- package/src/vite/plugins/expose-ids/export-analysis.ts +30 -43
- package/src/vite/plugins/expose-ids/handler-transform.ts +5 -31
- package/src/vite/plugins/expose-ids/loader-transform.ts +12 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +98 -26
- package/src/vite/plugins/expose-internal-ids.ts +10 -1
- package/src/vite/plugins/performance-tracks.ts +0 -3
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +21 -46
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +22 -27
- package/src/vite/plugins/version-plugin.ts +6 -66
- package/src/vite/plugins/virtual-entries.ts +137 -26
- package/src/vite/rango.ts +146 -135
- package/src/vite/router-discovery.ts +189 -48
- package/src/vite/utils/ast-handler-extract.ts +11 -20
- package/src/vite/utils/bundle-analysis.ts +6 -13
- package/src/vite/utils/client-chunks.ts +0 -6
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +0 -22
- package/src/vite/utils/manifest-utils.ts +4 -75
- package/src/vite/utils/package-resolution.ts +1 -73
- package/src/vite/utils/prerender-utils.ts +71 -44
- package/src/vite/utils/shared-utils.ts +55 -37
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import { createHandleStore } from "../server/handle-store.js";
|
|
3
|
+
import { resolveSegmentHandleValues } from "../handles/deferred-resolution.js";
|
|
3
4
|
import { getRequestContext } from "../server/request-context.js";
|
|
4
5
|
import {
|
|
5
6
|
runWithRequestContext,
|
|
6
7
|
type RequestContext,
|
|
7
8
|
} from "../server/request-context.js";
|
|
8
|
-
import { contextGet, contextSet } from "../context-var.js";
|
|
9
|
+
import { contextGet, contextSet, hasContextVars } from "../context-var.js";
|
|
9
10
|
import {
|
|
10
11
|
createPrerenderContext,
|
|
11
12
|
createStaticContext,
|
|
12
13
|
createReverseFunction,
|
|
13
14
|
} from "./handler-context.js";
|
|
14
|
-
import {
|
|
15
|
+
import { detectPrerenderPassthrough } from "../prerender.js";
|
|
15
16
|
import { isRouteRootScoped } from "../route-map-builder.js";
|
|
16
17
|
import { setupBuildUse } from "./loader-resolution.js";
|
|
17
18
|
import { loadManifest } from "./manifest.js";
|
|
18
19
|
import { traverseBack } from "./pattern-matching.js";
|
|
19
20
|
import type { RouterContext } from "./router-context.js";
|
|
21
|
+
import type { ResolveSegmentOptions } from "./segment-resolution.js";
|
|
20
22
|
import { runWithRouterContext } from "./router-context.js";
|
|
21
23
|
import type { EntryData, InterceptEntry } from "../server/context";
|
|
22
24
|
import type {
|
|
@@ -31,7 +33,9 @@ import type {
|
|
|
31
33
|
import type { RouteMatchResult } from "./pattern-matching.js";
|
|
32
34
|
|
|
33
35
|
export interface PrerenderMatchDeps<TEnv = any> {
|
|
34
|
-
findMatch: (
|
|
36
|
+
findMatch: (
|
|
37
|
+
pathname: string,
|
|
38
|
+
) => RouteMatchResult<TEnv> | null | Promise<RouteMatchResult<TEnv> | null>;
|
|
35
39
|
buildRouterContext: () => RouterContext<TEnv>;
|
|
36
40
|
mergedRouteMap: Record<string, string>;
|
|
37
41
|
resolveAllSegments: (
|
|
@@ -40,7 +44,7 @@ export interface PrerenderMatchDeps<TEnv = any> {
|
|
|
40
44
|
params: Record<string, string>,
|
|
41
45
|
context: HandlerContext<any, TEnv>,
|
|
42
46
|
loaderPromises: Map<string, Promise<any>>,
|
|
43
|
-
options?:
|
|
47
|
+
options?: ResolveSegmentOptions,
|
|
44
48
|
) => Promise<ResolvedSegment[]>;
|
|
45
49
|
}
|
|
46
50
|
|
|
@@ -59,15 +63,20 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
59
63
|
devMode?: boolean,
|
|
60
64
|
): Promise<{
|
|
61
65
|
segments: SerializedSegmentData[];
|
|
62
|
-
|
|
66
|
+
/** RSC-encoded handle map ("" when none) — see handle-snapshot.ts. Encoded in
|
|
67
|
+
* the producer (where the Flight codec resolves) so the node-side build/dev
|
|
68
|
+
* sinks can persist it without touching the codec. */
|
|
69
|
+
handles: string;
|
|
63
70
|
routeName: string;
|
|
64
71
|
params: Record<string, string>;
|
|
65
72
|
interceptSegments?: SerializedSegmentData[];
|
|
66
|
-
|
|
73
|
+
/** RSC-encoded MERGED (main + intercept) handle map for the intercept artifact;
|
|
74
|
+
* the sinks store it as-is (no longer merge raw records). */
|
|
75
|
+
interceptHandles?: string;
|
|
67
76
|
passthrough?: true;
|
|
68
77
|
} | null> {
|
|
69
78
|
// 1. Find the matching route entry
|
|
70
|
-
const matched = deps.findMatch(pathname);
|
|
79
|
+
const matched = await deps.findMatch(pathname);
|
|
71
80
|
if (!matched) return null;
|
|
72
81
|
|
|
73
82
|
// Use params from trie match if available, fall back to provided params
|
|
@@ -77,6 +86,17 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
77
86
|
// Build RouterContext for loadManifest/traverseBack
|
|
78
87
|
const routerCtx = deps.buildRouterContext();
|
|
79
88
|
|
|
89
|
+
// Passthrough sentinel result: an unknown-param Passthrough route falls
|
|
90
|
+
// through to the live handler at runtime, so no artifact is baked. A fresh
|
|
91
|
+
// object is returned per call (no site mutates or identity-compares it).
|
|
92
|
+
const passthroughResult = () => ({
|
|
93
|
+
segments: [],
|
|
94
|
+
handles: "",
|
|
95
|
+
routeName: matched.routeKey,
|
|
96
|
+
params: matchedParams,
|
|
97
|
+
passthrough: true as const,
|
|
98
|
+
});
|
|
99
|
+
|
|
80
100
|
return runWithRouterContext(routerCtx, async () => {
|
|
81
101
|
// 2. Load the manifest entry tree
|
|
82
102
|
const manifestEntry = await loadManifest(
|
|
@@ -140,19 +160,10 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
140
160
|
);
|
|
141
161
|
});
|
|
142
162
|
if (!isKnown) {
|
|
143
|
-
return
|
|
144
|
-
segments: [],
|
|
145
|
-
handles: {},
|
|
146
|
-
routeName: matched.routeKey,
|
|
147
|
-
params: matchedParams,
|
|
148
|
-
passthrough: true as const,
|
|
149
|
-
};
|
|
163
|
+
return passthroughResult();
|
|
150
164
|
}
|
|
151
165
|
// Preserve vars set by getParams() for the render context
|
|
152
|
-
if (
|
|
153
|
-
Object.keys(probeBuildVars).length > 0 ||
|
|
154
|
-
Object.getOwnPropertySymbols(probeBuildVars).length > 0
|
|
155
|
-
) {
|
|
166
|
+
if (hasContextVars(probeBuildVars)) {
|
|
156
167
|
devProbeBuildVars = probeBuildVars;
|
|
157
168
|
}
|
|
158
169
|
} catch (err: any) {
|
|
@@ -160,13 +171,7 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
160
171
|
// Skip errors are intentional — treat as passthrough.
|
|
161
172
|
// All other errors propagate so dev surfaces them.
|
|
162
173
|
if (err?.name === "Skip") {
|
|
163
|
-
return
|
|
164
|
-
segments: [],
|
|
165
|
-
handles: {},
|
|
166
|
-
routeName: matched.routeKey,
|
|
167
|
-
params: matchedParams,
|
|
168
|
-
passthrough: true as const,
|
|
169
|
-
};
|
|
174
|
+
return passthroughResult();
|
|
170
175
|
}
|
|
171
176
|
throw err;
|
|
172
177
|
}
|
|
@@ -206,11 +211,14 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
206
211
|
header: () => {},
|
|
207
212
|
setStatus: () => {},
|
|
208
213
|
_setStatus: () => {},
|
|
214
|
+
_rotateStateCookie: () => {},
|
|
215
|
+
_setKeepCacheDirective: () => {},
|
|
209
216
|
use: (() => {
|
|
210
217
|
throw new Error("use() not available during pre-rendering");
|
|
211
218
|
}) as any,
|
|
212
219
|
method: "GET",
|
|
213
220
|
_handleStore: handleStore,
|
|
221
|
+
_requestTags: new Set<string>(),
|
|
214
222
|
waitUntil: () => {},
|
|
215
223
|
onResponse: () => {},
|
|
216
224
|
_onResponseCallbacks: [],
|
|
@@ -253,20 +261,18 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
253
261
|
matchedParams,
|
|
254
262
|
buildCtx,
|
|
255
263
|
loaderPromises,
|
|
256
|
-
|
|
264
|
+
// throwOnError: a render failure (or `throw new Skip()`) must reach the
|
|
265
|
+
// build/dev caller, not be baked into a frozen error page (issue #587).
|
|
266
|
+
{ skipLoaders: true, throwOnError: true },
|
|
257
267
|
);
|
|
258
268
|
|
|
259
|
-
// 9. Detect passthrough sentinel: handler returned ctx.passthrough()
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
params: matchedParams,
|
|
267
|
-
passthrough: true as const,
|
|
268
|
-
};
|
|
269
|
-
}
|
|
269
|
+
// 9. Detect passthrough sentinel: handler returned ctx.passthrough().
|
|
270
|
+
// When the route declares loading(), the handler result is deferred so the
|
|
271
|
+
// component is a thenable resolving to the sentinel — detectPrerenderPassthrough
|
|
272
|
+
// resolves thenables before testing (a sync check would miss it and bake a
|
|
273
|
+
// corrupt artifact).
|
|
274
|
+
if (await detectPrerenderPassthrough(allSegments)) {
|
|
275
|
+
return passthroughResult();
|
|
270
276
|
}
|
|
271
277
|
|
|
272
278
|
// 10. Filter out any loader segments (belt-and-suspenders)
|
|
@@ -278,16 +284,24 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
278
284
|
|
|
279
285
|
// 12. Serialize segments using the cache serializer
|
|
280
286
|
const { serializeSegments } = await import("../cache/segment-codec.js");
|
|
287
|
+
const { encodeHandles } = await import("../cache/handle-snapshot.js");
|
|
281
288
|
const serializedSegments = await serializeSegments(nonLoaderSegments);
|
|
282
289
|
|
|
283
|
-
// 13. Collect handle data per segment (skip segments with no handle data)
|
|
284
|
-
|
|
290
|
+
// 13. Collect handle data per segment (skip segments with no handle data).
|
|
291
|
+
// Encoded through the Flight codec (not stored raw) so Promise/ReactNode
|
|
292
|
+
// handle values survive the JSON-serialized build artifact / dev wire —
|
|
293
|
+
// the same fix the runtime cache uses. Encode happens here, in the RSC
|
|
294
|
+
// environment where the codec resolves; the node-side sinks only persist.
|
|
295
|
+
const handlesRecord: Record<string, SegmentHandleData> = {};
|
|
285
296
|
for (const seg of nonLoaderSegments) {
|
|
286
297
|
const segHandles = handleStore.getDataForSegment(seg.id);
|
|
287
298
|
if (Object.keys(segHandles).length > 0) {
|
|
288
|
-
|
|
299
|
+
// Resolve deferred values before encoding so the baked artifact holds
|
|
300
|
+
// resolved data (prerender = build-time cache).
|
|
301
|
+
handlesRecord[seg.id] = await resolveSegmentHandleValues(segHandles);
|
|
289
302
|
}
|
|
290
303
|
}
|
|
304
|
+
const handles = await encodeHandles(handlesRecord);
|
|
291
305
|
|
|
292
306
|
// Use the trie-level route key (e.g., "docs", "docs.article")
|
|
293
307
|
const routeName = matched.routeKey;
|
|
@@ -297,7 +311,7 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
297
311
|
// evaluation -- we pre-render all intercepts unconditionally and let
|
|
298
312
|
// runtime matching decide which to serve.
|
|
299
313
|
let interceptSegments: SerializedSegmentData[] | undefined;
|
|
300
|
-
let interceptHandles:
|
|
314
|
+
let interceptHandles: string | undefined;
|
|
301
315
|
|
|
302
316
|
const foundIntercepts: {
|
|
303
317
|
intercept: InterceptEntry;
|
|
@@ -305,24 +319,14 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
305
319
|
}[] = [];
|
|
306
320
|
let current: EntryData | null = manifestEntry;
|
|
307
321
|
while (current) {
|
|
308
|
-
|
|
309
|
-
|
|
322
|
+
// Flatten the entry and its sibling layouts into one source list, the
|
|
323
|
+
// same traversal findInterceptForRoute uses; the build keeps ALL matches
|
|
324
|
+
// (not just the innermost) and skips when(). intercept/layout are
|
|
325
|
+
// non-optional arrays, so empty ones are a no-op here.
|
|
326
|
+
for (const source of [current, ...current.layout]) {
|
|
327
|
+
for (const ic of source.intercept) {
|
|
310
328
|
if (ic.routeName === matched.routeKey) {
|
|
311
|
-
foundIntercepts.push({ intercept: ic, entry:
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
if (current.layout && current.layout.length > 0) {
|
|
316
|
-
for (const siblingLayout of current.layout) {
|
|
317
|
-
if (siblingLayout.intercept && siblingLayout.intercept.length > 0) {
|
|
318
|
-
for (const ic of siblingLayout.intercept) {
|
|
319
|
-
if (ic.routeName === matched.routeKey) {
|
|
320
|
-
foundIntercepts.push({
|
|
321
|
-
intercept: ic,
|
|
322
|
-
entry: siblingLayout,
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
}
|
|
329
|
+
foundIntercepts.push({ intercept: ic, entry: source });
|
|
326
330
|
}
|
|
327
331
|
}
|
|
328
332
|
}
|
|
@@ -333,6 +337,18 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
333
337
|
const interceptResolvedSegments: typeof nonLoaderSegments = [];
|
|
334
338
|
|
|
335
339
|
for (const { intercept, entry: parentEntry } of foundIntercepts) {
|
|
340
|
+
// setupBuildUse keys handle pushes by ctx._currentSegmentId. The main
|
|
341
|
+
// resolveAllSegments pass left it on the route's segment id, so pin it
|
|
342
|
+
// to THIS intercept's slot id before resolving the handler/layout --
|
|
343
|
+
// otherwise the intercept's ctx.use() handle pushes land in the wrong
|
|
344
|
+
// bucket and getDataForSegment(seg.id) below drops them from the baked
|
|
345
|
+
// artifact. Re-pinned per iteration so multiple intercepts targeting
|
|
346
|
+
// the same route each get their own id (mirrors fresh.ts parallel-slot
|
|
347
|
+
// pinning).
|
|
348
|
+
const interceptSegmentId = `${parentEntry.shortCode}.${intercept.slotName}`;
|
|
349
|
+
(buildCtx as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
350
|
+
interceptSegmentId;
|
|
351
|
+
|
|
336
352
|
// Resolve handler
|
|
337
353
|
const handlerRaw =
|
|
338
354
|
typeof intercept.handler === "function"
|
|
@@ -359,7 +375,7 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
359
375
|
}
|
|
360
376
|
|
|
361
377
|
interceptResolvedSegments.push({
|
|
362
|
-
id:
|
|
378
|
+
id: interceptSegmentId,
|
|
363
379
|
namespace: `intercept:${intercept.routeName}`,
|
|
364
380
|
type: "parallel" as const,
|
|
365
381
|
index: 0,
|
|
@@ -379,13 +395,21 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
379
395
|
interceptSegments = await serializeSegments(
|
|
380
396
|
interceptResolvedSegments,
|
|
381
397
|
);
|
|
382
|
-
|
|
398
|
+
const interceptHandlesRecord: Record<string, SegmentHandleData> = {};
|
|
383
399
|
for (const seg of interceptResolvedSegments) {
|
|
384
400
|
const segHandles = handleStore.getDataForSegment(seg.id);
|
|
385
401
|
if (Object.keys(segHandles).length > 0) {
|
|
386
|
-
|
|
402
|
+
interceptHandlesRecord[seg.id] =
|
|
403
|
+
await resolveSegmentHandleValues(segHandles);
|
|
387
404
|
}
|
|
388
405
|
}
|
|
406
|
+
// The intercept artifact serves main + intercept segments together, so
|
|
407
|
+
// encode the MERGED handle map here (the sinks no longer merge raw
|
|
408
|
+
// records — they store this pre-encoded string as-is).
|
|
409
|
+
interceptHandles = await encodeHandles({
|
|
410
|
+
...handlesRecord,
|
|
411
|
+
...interceptHandlesRecord,
|
|
412
|
+
});
|
|
389
413
|
}
|
|
390
414
|
}
|
|
391
415
|
|
|
@@ -414,7 +438,7 @@ export async function renderStaticSegment<TEnv = any>(
|
|
|
414
438
|
routeName?: string,
|
|
415
439
|
buildEnv?: TEnv,
|
|
416
440
|
devMode?: boolean,
|
|
417
|
-
): Promise<{ encoded: string; handles:
|
|
441
|
+
): Promise<{ encoded: string; handles: string } | null> {
|
|
418
442
|
const syntheticUrl = new URL("http://prerender/");
|
|
419
443
|
const syntheticRequest = new Request(syntheticUrl);
|
|
420
444
|
|
|
@@ -442,11 +466,14 @@ export async function renderStaticSegment<TEnv = any>(
|
|
|
442
466
|
header: () => {},
|
|
443
467
|
setStatus: () => {},
|
|
444
468
|
_setStatus: () => {},
|
|
469
|
+
_rotateStateCookie: () => {},
|
|
470
|
+
_setKeepCacheDirective: () => {},
|
|
445
471
|
use: (() => {
|
|
446
472
|
throw new Error("use() not available during static pre-rendering");
|
|
447
473
|
}) as any,
|
|
448
474
|
method: "GET",
|
|
449
475
|
_handleStore: handleStore,
|
|
476
|
+
_requestTags: new Set<string>(),
|
|
450
477
|
waitUntil: () => {},
|
|
451
478
|
onResponse: () => {},
|
|
452
479
|
_onResponseCallbacks: [],
|
|
@@ -479,23 +506,41 @@ export async function renderStaticSegment<TEnv = any>(
|
|
|
479
506
|
setupBuildUse(buildCtx);
|
|
480
507
|
|
|
481
508
|
const raw = await handler(buildCtx);
|
|
482
|
-
|
|
509
|
+
|
|
510
|
+
// Static handlers must return a ReactNode. A returned Response (e.g. an
|
|
511
|
+
// accidental redirect()) would otherwise be serialized as a corrupt build
|
|
512
|
+
// artifact; fail loudly instead. The fresh/revalidation paths route handler
|
|
513
|
+
// results through handleHandlerResult, which throws Responses; the static
|
|
514
|
+
// build path bypasses that, so guard here.
|
|
515
|
+
if (raw instanceof Response) {
|
|
516
|
+
throw new TypeError(
|
|
517
|
+
`Static handler "${routeName}" returned a Response. Static handlers must return a ReactNode; ` +
|
|
518
|
+
`Responses (redirects, file responses) are not supported during static pre-rendering.`,
|
|
519
|
+
);
|
|
520
|
+
}
|
|
483
521
|
|
|
484
522
|
const segment: ResolvedSegment = {
|
|
485
523
|
id: handlerId,
|
|
486
524
|
namespace: handlerId,
|
|
487
525
|
type: "layout",
|
|
488
526
|
index: 0,
|
|
489
|
-
component,
|
|
527
|
+
component: raw,
|
|
490
528
|
params: {},
|
|
491
529
|
belongsToRoute: false,
|
|
492
530
|
};
|
|
493
531
|
|
|
494
532
|
const { serializeSegments } = await import("../cache/segment-codec.js");
|
|
533
|
+
const { encodeHandleValue } = await import("../cache/handle-snapshot.js");
|
|
495
534
|
const [serialized] = await serializeSegments([segment]);
|
|
496
535
|
|
|
497
|
-
// Collect handle data pushed during rendering
|
|
498
|
-
|
|
536
|
+
// Collect handle data pushed during rendering and Flight-encode it (so
|
|
537
|
+
// Promise/ReactNode handle values survive the JSON build artifact). "" when
|
|
538
|
+
// nothing was pushed.
|
|
539
|
+
const segHandles = handleStore.getDataForSegment(handlerId);
|
|
540
|
+
const handles =
|
|
541
|
+
Object.keys(segHandles).length > 0
|
|
542
|
+
? await encodeHandleValue(await resolveSegmentHandleValues(segHandles))
|
|
543
|
+
: "";
|
|
499
544
|
|
|
500
545
|
return { encoded: serialized.encoded, handles };
|
|
501
546
|
});
|
|
@@ -6,7 +6,9 @@ import type { MiddlewareFn } from "./middleware.js";
|
|
|
6
6
|
import { resolveRoute } from "./route-snapshot.js";
|
|
7
7
|
|
|
8
8
|
export interface PreviewMatchDeps<TEnv = any> {
|
|
9
|
-
findMatch: (
|
|
9
|
+
findMatch: (
|
|
10
|
+
pathname: string,
|
|
11
|
+
) => RouteMatchResult<TEnv> | null | Promise<RouteMatchResult<TEnv> | null>;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
/**
|
|
@@ -23,10 +23,6 @@ import { negotiateRoute } from "./content-negotiation.js";
|
|
|
23
23
|
import { stripInternalParams } from "./handler-context.js";
|
|
24
24
|
import { resolveRoute, type RouteSnapshot } from "./route-snapshot.js";
|
|
25
25
|
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
// RequestPlan — discriminated union
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
|
|
30
26
|
interface RedirectPlan<TEnv = any> {
|
|
31
27
|
mode: "redirect";
|
|
32
28
|
route: RouteSnapshot<TEnv>;
|
|
@@ -40,6 +36,12 @@ interface VersionMismatchPlan<TEnv = any> {
|
|
|
40
36
|
reloadUrl: string;
|
|
41
37
|
}
|
|
42
38
|
|
|
39
|
+
interface AppSwitchReloadPlan {
|
|
40
|
+
mode: "app-switch";
|
|
41
|
+
/** Clean target URL (internal _rsc_* params stripped) to navigate to. */
|
|
42
|
+
reloadUrl: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
43
45
|
interface ResponseRoutePlan<TEnv = any> {
|
|
44
46
|
mode: "response";
|
|
45
47
|
route: RouteSnapshot<TEnv>;
|
|
@@ -86,6 +88,7 @@ interface PartialRenderPlan<TEnv = any> {
|
|
|
86
88
|
export type RequestPlan<TEnv = any> =
|
|
87
89
|
| RedirectPlan<TEnv>
|
|
88
90
|
| VersionMismatchPlan<TEnv>
|
|
91
|
+
| AppSwitchReloadPlan
|
|
89
92
|
| ResponseRoutePlan<TEnv>
|
|
90
93
|
| LoaderFetchPlan<TEnv>
|
|
91
94
|
| PeRenderPlan<TEnv>
|
|
@@ -94,12 +97,13 @@ export type RequestPlan<TEnv = any> =
|
|
|
94
97
|
| PartialRenderPlan<TEnv>;
|
|
95
98
|
|
|
96
99
|
/**
|
|
97
|
-
* Plans that have passed the terminal-check gate (version-mismatch
|
|
98
|
-
* and are ready for execution. Always have a
|
|
100
|
+
* Plans that have passed the terminal-check gate (version-mismatch and
|
|
101
|
+
* app-switch reloads handled) and are ready for execution. Always have a
|
|
102
|
+
* `route` field.
|
|
99
103
|
*/
|
|
100
104
|
export type ExecutableRequestPlan<TEnv = any> = Exclude<
|
|
101
105
|
RequestPlan<TEnv>,
|
|
102
|
-
VersionMismatchPlan<TEnv>
|
|
106
|
+
VersionMismatchPlan<TEnv> | AppSwitchReloadPlan
|
|
103
107
|
>;
|
|
104
108
|
|
|
105
109
|
/**
|
|
@@ -116,12 +120,10 @@ export type {
|
|
|
116
120
|
PartialRenderPlan,
|
|
117
121
|
};
|
|
118
122
|
|
|
119
|
-
// ---------------------------------------------------------------------------
|
|
120
|
-
// classifyRequest — the single authoritative classification step
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
|
|
123
123
|
export interface ClassifyRequestDeps<TEnv = any> {
|
|
124
|
-
findMatch: (
|
|
124
|
+
findMatch: (
|
|
125
|
+
pathname: string,
|
|
126
|
+
) => RouteMatchResult<TEnv> | null | Promise<RouteMatchResult<TEnv> | null>;
|
|
125
127
|
routerVersion: string;
|
|
126
128
|
routerId: string;
|
|
127
129
|
}
|
|
@@ -148,16 +150,30 @@ export async function classifyRequest<TEnv = any>(
|
|
|
148
150
|
const pathname = url.pathname;
|
|
149
151
|
const isAction =
|
|
150
152
|
request.headers.has("rsc-action") || url.searchParams.has("_rsc_action");
|
|
153
|
+
const isLoaderFetch = url.searchParams.has("_rsc_loader");
|
|
154
|
+
const isPartialReq = url.searchParams.has("_rsc_partial");
|
|
155
|
+
const contentType = request.headers.get("content-type") || "";
|
|
156
|
+
const isFormSubmission =
|
|
157
|
+
contentType.includes("multipart/form-data") ||
|
|
158
|
+
contentType.includes("application/x-www-form-urlencoded");
|
|
159
|
+
const isPeRender = request.method === "POST" && !isAction && isFormSubmission;
|
|
160
|
+
|
|
161
|
+
// A plain document request (full-render mode) is the only mode that renders
|
|
162
|
+
// through match() with isSSR:true; every other mode (action, loader, PE,
|
|
163
|
+
// partial) renders — if at all — through the non-SSR matchPartial() path.
|
|
164
|
+
// Resolve the manifest under the flag the eventual render will use so
|
|
165
|
+
// createMatchContextForFull can reuse this snapshot instead of re-resolving.
|
|
166
|
+
// Response routes ignore isSSR entirely (they short-circuit before match),
|
|
167
|
+
// so the flag is moot for them. Mode detection below reuses these booleans.
|
|
168
|
+
const willFullRender =
|
|
169
|
+
!isAction && !isLoaderFetch && !isPartialReq && !isPeRender;
|
|
151
170
|
|
|
152
|
-
// Version mismatch — runs BEFORE route resolution so stale clients
|
|
153
|
-
// requesting removed routes get a reload, not a 404.
|
|
154
171
|
const clientVersion = url.searchParams.get("_rsc_v");
|
|
155
172
|
if (
|
|
156
173
|
deps.routerVersion &&
|
|
157
174
|
clientVersion &&
|
|
158
175
|
clientVersion !== deps.routerVersion
|
|
159
176
|
) {
|
|
160
|
-
// Strip internal _rsc_* params so the browser reloads to a clean URL
|
|
161
177
|
let reloadUrl = stripInternalParams(url).toString();
|
|
162
178
|
if (isAction) {
|
|
163
179
|
const referer = request.headers.get("referer");
|
|
@@ -167,9 +183,7 @@ export async function classifyRequest<TEnv = any>(
|
|
|
167
183
|
if (refererUrl.origin === url.origin) {
|
|
168
184
|
reloadUrl = referer;
|
|
169
185
|
}
|
|
170
|
-
} catch {
|
|
171
|
-
// Malformed referer, fall back to stripped url
|
|
172
|
-
}
|
|
186
|
+
} catch {}
|
|
173
187
|
}
|
|
174
188
|
}
|
|
175
189
|
|
|
@@ -179,12 +193,22 @@ export async function classifyRequest<TEnv = any>(
|
|
|
179
193
|
};
|
|
180
194
|
}
|
|
181
195
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
196
|
+
const clientRouterId = url.searchParams.get("_rsc_rid");
|
|
197
|
+
if (
|
|
198
|
+
clientRouterId &&
|
|
199
|
+
clientRouterId !== deps.routerId &&
|
|
200
|
+
url.searchParams.has("_rsc_partial")
|
|
201
|
+
) {
|
|
202
|
+
return {
|
|
203
|
+
mode: "app-switch",
|
|
204
|
+
reloadUrl: stripInternalParams(url).toString(),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
185
208
|
const result = await resolveRoute<TEnv>(pathname, {
|
|
186
209
|
findMatch: deps.findMatch,
|
|
187
210
|
lite: true,
|
|
211
|
+
isSSR: willFullRender,
|
|
188
212
|
});
|
|
189
213
|
|
|
190
214
|
if (!result) {
|
|
@@ -193,7 +217,6 @@ export async function classifyRequest<TEnv = any>(
|
|
|
193
217
|
});
|
|
194
218
|
}
|
|
195
219
|
|
|
196
|
-
// Redirect
|
|
197
220
|
if (result.type === "redirect") {
|
|
198
221
|
const snapshot: RouteSnapshot<TEnv> = {
|
|
199
222
|
matched: result as any,
|
|
@@ -215,7 +238,6 @@ export async function classifyRequest<TEnv = any>(
|
|
|
215
238
|
|
|
216
239
|
const snapshot = result.snapshot;
|
|
217
240
|
|
|
218
|
-
// Response route — non-RSC short-circuit (JSON, streaming, etc.)
|
|
219
241
|
const responseResult = await classifyResponseRoute(
|
|
220
242
|
request,
|
|
221
243
|
pathname,
|
|
@@ -225,10 +247,8 @@ export async function classifyRequest<TEnv = any>(
|
|
|
225
247
|
return responseResult;
|
|
226
248
|
}
|
|
227
249
|
|
|
228
|
-
// Mode detection from request signals
|
|
229
250
|
const actionId =
|
|
230
251
|
request.headers.get("rsc-action") || url.searchParams.get("_rsc_action");
|
|
231
|
-
const isLoaderFetch = url.searchParams.has("_rsc_loader");
|
|
232
252
|
|
|
233
253
|
const hasVariants =
|
|
234
254
|
snapshot.matched.negotiateVariants &&
|
|
@@ -243,31 +263,17 @@ export async function classifyRequest<TEnv = any>(
|
|
|
243
263
|
return { mode: "loader", route: snapshot };
|
|
244
264
|
}
|
|
245
265
|
|
|
246
|
-
|
|
247
|
-
const contentType = request.headers.get("content-type") || "";
|
|
248
|
-
const isFormSubmission =
|
|
249
|
-
contentType.includes("multipart/form-data") ||
|
|
250
|
-
contentType.includes("application/x-www-form-urlencoded");
|
|
251
|
-
if (request.method === "POST" && !isAction && isFormSubmission) {
|
|
266
|
+
if (isPeRender) {
|
|
252
267
|
return { mode: "pe-render", route: snapshot };
|
|
253
268
|
}
|
|
254
269
|
|
|
255
|
-
|
|
256
|
-
const clientRouterId = url.searchParams.get("_rsc_rid");
|
|
257
|
-
const isAppSwitch = !!(clientRouterId && clientRouterId !== deps.routerId);
|
|
258
|
-
const isPartial = url.searchParams.has("_rsc_partial") && !isAppSwitch;
|
|
259
|
-
|
|
260
|
-
if (isPartial) {
|
|
270
|
+
if (isPartialReq) {
|
|
261
271
|
return { mode: "partial-render", route: snapshot, negotiated };
|
|
262
272
|
}
|
|
263
273
|
|
|
264
274
|
return { mode: "full-render", route: snapshot, negotiated };
|
|
265
275
|
}
|
|
266
276
|
|
|
267
|
-
// ---------------------------------------------------------------------------
|
|
268
|
-
// Content negotiation for response routes
|
|
269
|
-
// ---------------------------------------------------------------------------
|
|
270
|
-
|
|
271
277
|
/**
|
|
272
278
|
* Check if the route is a response route and perform content negotiation
|
|
273
279
|
* if negotiate variants exist. Returns a ResponseRoutePlan if the route
|
|
@@ -278,7 +284,6 @@ async function classifyResponseRoute<TEnv>(
|
|
|
278
284
|
pathname: string,
|
|
279
285
|
snapshot: RouteSnapshot<TEnv>,
|
|
280
286
|
): Promise<ResponseRoutePlan<TEnv> | null> {
|
|
281
|
-
// negotiateRoute returns the response plan (variant or plain) or null for RSC.
|
|
282
287
|
const negotiation = await negotiateRoute(request, pathname, snapshot);
|
|
283
288
|
return negotiation
|
|
284
289
|
? { mode: "response", route: snapshot, ...negotiation }
|