@rangojs/router 0.0.0-experimental.19 → 0.0.0-experimental.1c0bdfad
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 +17 -0
- package/README.md +291 -61
- package/dist/bin/rango.js +544 -143
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3744 -1329
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +67 -13
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +312 -0
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +247 -23
- package/skills/caching/SKILL.md +322 -19
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +4 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +282 -60
- package/skills/host-router/SKILL.md +278 -0
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +50 -6
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +297 -31
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -0
- package/skills/migrate-react-router/SKILL.md +771 -0
- package/skills/mime-routes/SKILL.md +28 -1
- package/skills/observability/SKILL.md +172 -0
- package/skills/parallel/SKILL.md +203 -7
- package/skills/prerender/SKILL.md +155 -111
- package/skills/rango/SKILL.md +251 -23
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +123 -48
- package/skills/route/SKILL.md +104 -9
- package/skills/router-setup/SKILL.md +124 -11
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +125 -222
- 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 +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +121 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -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/typesafety/SKILL.md +357 -52
- package/skills/use-cache/SKILL.md +46 -14
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +67 -40
- package/src/bin/rango.ts +18 -0
- 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/app-version.ts +14 -0
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +197 -150
- 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/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +200 -30
- package/src/browser/navigation-client.ts +217 -58
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +76 -67
- package/src/browser/navigation-transaction.ts +18 -66
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +187 -112
- package/src/browser/prefetch/cache.ts +312 -30
- package/src/browser/prefetch/fetch.ts +344 -47
- package/src/browser/prefetch/policy.ts +6 -0
- package/src/browser/prefetch/queue.ts +126 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +125 -18
- package/src/browser/react/NavigationProvider.tsx +135 -120
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- 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 +23 -69
- 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 +46 -11
- 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 +80 -5
- package/src/browser/rsc-router.tsx +226 -75
- package/src/browser/scroll-restoration.ts +54 -42
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +619 -442
- package/src/browser/types.ts +115 -11
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +65 -40
- package/src/build/generate-route-types.ts +7 -1
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +182 -37
- 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 +125 -24
- 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 +392 -106
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +29 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +214 -48
- package/src/cache/cache-scope.ts +236 -89
- package/src/cache/cache-tag.ts +103 -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 +2224 -171
- 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 +11 -17
- package/src/cache/document-cache.ts +89 -27
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +11 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +31 -31
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +9 -17
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +55 -0
- package/src/cache/types.ts +37 -100
- package/src/client.rsc.tsx +45 -21
- package/src/client.tsx +120 -336
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +84 -2
- package/src/debug.ts +2 -2
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +196 -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 +70 -22
- package/src/handles/MetaTags.tsx +56 -19
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +95 -0
- package/src/handles/is-thenable.ts +19 -0
- package/src/handles/meta.ts +51 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -5
- 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 +79 -29
- package/src/index.ts +149 -65
- 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 +63 -26
- package/src/prerender.ts +198 -82
- package/src/redirect-origin.ts +100 -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 -15
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +7 -72
- package/src/route-definition/dsl-helpers.ts +469 -276
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +113 -37
- package/src/route-definition/index.ts +3 -3
- package/src/route-definition/redirect.ts +53 -12
- package/src/route-definition/resolve-handler-use.ts +161 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +7 -17
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +164 -17
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +45 -22
- package/src/router/handler-context.ts +110 -39
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +50 -24
- package/src/router/lazy-includes.ts +19 -53
- package/src/router/loader-resolution.ts +274 -56
- package/src/router/logging.ts +5 -8
- package/src/router/manifest.ts +49 -45
- package/src/router/match-api.ts +121 -205
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +33 -6
- package/src/router/match-middleware/cache-lookup.ts +214 -263
- package/src/router/match-middleware/cache-store.ts +73 -33
- package/src/router/match-middleware/intercept-resolution.ts +8 -28
- package/src/router/match-middleware/segment-resolution.ts +52 -18
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +104 -49
- package/src/router/metrics.ts +217 -26
- package/src/router/middleware-types.ts +24 -110
- package/src/router/middleware.ts +384 -197
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +148 -91
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prerender-match.ts +199 -56
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +144 -74
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +8 -28
- package/src/router/router-interfaces.ts +129 -36
- package/src/router/router-options.ts +185 -23
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +281 -76
- package/src/router/segment-resolution/helpers.ts +116 -31
- package/src/router/segment-resolution/loader-cache.ts +63 -37
- package/src/router/segment-resolution/revalidation.ts +493 -391
- 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 +36 -0
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +8 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +161 -199
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +180 -58
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +182 -54
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +702 -460
- package/src/rsc/helpers.ts +168 -46
- package/src/rsc/index.ts +2 -25
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +127 -31
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +98 -19
- package/src/rsc/redirect-guard.ts +99 -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 +126 -106
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +269 -114
- package/src/rsc/ssr-setup.ts +144 -0
- package/src/rsc/types.ts +34 -6
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +49 -41
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +149 -0
- package/src/segment-system.tsx +281 -129
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +317 -63
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +40 -38
- package/src/server/loader-registry.ts +26 -46
- package/src/server/request-context.ts +425 -177
- package/src/server.ts +6 -0
- package/src/ssr/index.tsx +25 -16
- package/src/static-handler.ts +27 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +701 -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 +183 -0
- package/src/testing/index.ts +99 -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 +343 -0
- package/src/testing/render-route.tsx +581 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -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 +20 -58
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +3 -19
- 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 +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +236 -88
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +44 -15
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +10 -45
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +37 -19
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +58 -11
- package/src/urls/path-helper.ts +57 -111
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -18
- package/src/use-loader.tsx +346 -89
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +64 -91
- package/src/vite/discovery/discover-routers.ts +147 -88
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +247 -145
- 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 +61 -13
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +10 -3
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +155 -65
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- package/src/vite/plugins/client-ref-dedup.ts +120 -0
- 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 +49 -98
- package/src/vite/plugins/expose-id-utils.ts +96 -51
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
- 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 +553 -317
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/version-injector.ts +21 -25
- package/src/vite/plugins/version-plugin.ts +46 -37
- package/src/vite/plugins/virtual-entries.ts +13 -18
- package/src/vite/rango.ts +241 -287
- package/src/vite/router-discovery.ts +956 -149
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +4 -4
- 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 +141 -34
- package/src/vite/utils/shared-utils.ts +92 -42
- package/CLAUDE.md +0 -5
- 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 -6
- package/src/network-error-thrower.tsx +0 -23
- package/src/route-definition/route-function.ts +0 -119
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -5,18 +5,19 @@ import {
|
|
|
5
5
|
runWithRequestContext,
|
|
6
6
|
type RequestContext,
|
|
7
7
|
} from "../server/request-context.js";
|
|
8
|
-
import { contextGet, contextSet } from "../context-var.js";
|
|
8
|
+
import { contextGet, contextSet, hasContextVars } from "../context-var.js";
|
|
9
9
|
import {
|
|
10
10
|
createPrerenderContext,
|
|
11
11
|
createStaticContext,
|
|
12
12
|
createReverseFunction,
|
|
13
13
|
} from "./handler-context.js";
|
|
14
|
-
import {
|
|
14
|
+
import { detectPrerenderPassthrough } from "../prerender.js";
|
|
15
15
|
import { isRouteRootScoped } from "../route-map-builder.js";
|
|
16
16
|
import { setupBuildUse } from "./loader-resolution.js";
|
|
17
17
|
import { loadManifest } from "./manifest.js";
|
|
18
18
|
import { traverseBack } from "./pattern-matching.js";
|
|
19
19
|
import type { RouterContext } from "./router-context.js";
|
|
20
|
+
import type { ResolveSegmentOptions } from "./segment-resolution.js";
|
|
20
21
|
import { runWithRouterContext } from "./router-context.js";
|
|
21
22
|
import type { EntryData, InterceptEntry } from "../server/context";
|
|
22
23
|
import type {
|
|
@@ -40,7 +41,7 @@ export interface PrerenderMatchDeps<TEnv = any> {
|
|
|
40
41
|
params: Record<string, string>,
|
|
41
42
|
context: HandlerContext<any, TEnv>,
|
|
42
43
|
loaderPromises: Map<string, Promise<any>>,
|
|
43
|
-
options?:
|
|
44
|
+
options?: ResolveSegmentOptions,
|
|
44
45
|
) => Promise<ResolvedSegment[]>;
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -54,13 +55,21 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
54
55
|
deps: PrerenderMatchDeps<TEnv>,
|
|
55
56
|
buildVars?: Record<string, any>,
|
|
56
57
|
isPassthroughRoute?: boolean,
|
|
58
|
+
buildEnv?: TEnv,
|
|
59
|
+
/** Dev-only: check getParams() for passthrough routes to skip unknown params. */
|
|
60
|
+
devMode?: boolean,
|
|
57
61
|
): Promise<{
|
|
58
62
|
segments: SerializedSegmentData[];
|
|
59
|
-
|
|
63
|
+
/** RSC-encoded handle map ("" when none) — see handle-snapshot.ts. Encoded in
|
|
64
|
+
* the producer (where the Flight codec resolves) so the node-side build/dev
|
|
65
|
+
* sinks can persist it without touching the codec. */
|
|
66
|
+
handles: string;
|
|
60
67
|
routeName: string;
|
|
61
68
|
params: Record<string, string>;
|
|
62
69
|
interceptSegments?: SerializedSegmentData[];
|
|
63
|
-
|
|
70
|
+
/** RSC-encoded MERGED (main + intercept) handle map for the intercept artifact;
|
|
71
|
+
* the sinks store it as-is (no longer merge raw records). */
|
|
72
|
+
interceptHandles?: string;
|
|
64
73
|
passthrough?: true;
|
|
65
74
|
} | null> {
|
|
66
75
|
// 1. Find the matching route entry
|
|
@@ -74,6 +83,17 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
74
83
|
// Build RouterContext for loadManifest/traverseBack
|
|
75
84
|
const routerCtx = deps.buildRouterContext();
|
|
76
85
|
|
|
86
|
+
// Passthrough sentinel result: an unknown-param Passthrough route falls
|
|
87
|
+
// through to the live handler at runtime, so no artifact is baked. A fresh
|
|
88
|
+
// object is returned per call (no site mutates or identity-compares it).
|
|
89
|
+
const passthroughResult = () => ({
|
|
90
|
+
segments: [],
|
|
91
|
+
handles: "",
|
|
92
|
+
routeName: matched.routeKey,
|
|
93
|
+
params: matchedParams,
|
|
94
|
+
passthrough: true as const,
|
|
95
|
+
});
|
|
96
|
+
|
|
77
97
|
return runWithRouterContext(routerCtx, async () => {
|
|
78
98
|
// 2. Load the manifest entry tree
|
|
79
99
|
const manifestEntry = await loadManifest(
|
|
@@ -90,20 +110,91 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
90
110
|
entries.push(entry);
|
|
91
111
|
}
|
|
92
112
|
|
|
113
|
+
// 3b. Dev-mode passthrough shortcut: if the route is a Passthrough route
|
|
114
|
+
// and has getParams(), check if the matched params are in the known list.
|
|
115
|
+
// In production, only known params are pre-rendered; unknown params fall
|
|
116
|
+
// through to the live handler. Mirror that behavior in dev mode to avoid
|
|
117
|
+
// rendering unknown params with build: true.
|
|
118
|
+
// Vars collected from getParams() probe — merged into render context below.
|
|
119
|
+
let devProbeBuildVars: Record<string, any> | undefined;
|
|
120
|
+
|
|
121
|
+
if (devMode && matchedPassthroughRoute) {
|
|
122
|
+
const routeEntry = entries.find(
|
|
123
|
+
(
|
|
124
|
+
e,
|
|
125
|
+
): e is EntryData & {
|
|
126
|
+
type: "route";
|
|
127
|
+
prerenderDef: { getParams: (ctx: any) => Promise<any[]> | any[] };
|
|
128
|
+
} =>
|
|
129
|
+
e.type === "route" &&
|
|
130
|
+
!!(e as any).isPassthrough &&
|
|
131
|
+
!!(e as any).prerenderDef?.getParams,
|
|
132
|
+
);
|
|
133
|
+
if (routeEntry) {
|
|
134
|
+
try {
|
|
135
|
+
const probeBuildVars: Record<string, any> = {};
|
|
136
|
+
const knownParamsList = await routeEntry.prerenderDef.getParams({
|
|
137
|
+
build: true as const,
|
|
138
|
+
dev: true,
|
|
139
|
+
set: ((keyOrVar: any, value: any) => {
|
|
140
|
+
contextSet(probeBuildVars, keyOrVar, value);
|
|
141
|
+
}) as any,
|
|
142
|
+
reverse: createReverseFunction(deps.mergedRouteMap),
|
|
143
|
+
get env() {
|
|
144
|
+
if (buildEnv !== undefined) return buildEnv;
|
|
145
|
+
throw new Error(
|
|
146
|
+
"[rango] ctx.env is not available during dev-mode getParams(). " +
|
|
147
|
+
"Configure buildEnv in your rango() plugin options to enable build-time env access.",
|
|
148
|
+
);
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
// Compare only the keys returned by getParams — ignore mount params
|
|
152
|
+
// from include() prefixes that aren't part of the handler's params.
|
|
153
|
+
const isKnown = knownParamsList.some((known: Record<string, any>) => {
|
|
154
|
+
const knownKeys = Object.keys(known);
|
|
155
|
+
return knownKeys.every(
|
|
156
|
+
(k) => String(known[k]) === String(matchedParams[k]),
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
if (!isKnown) {
|
|
160
|
+
return passthroughResult();
|
|
161
|
+
}
|
|
162
|
+
// Preserve vars set by getParams() for the render context
|
|
163
|
+
if (hasContextVars(probeBuildVars)) {
|
|
164
|
+
devProbeBuildVars = probeBuildVars;
|
|
165
|
+
}
|
|
166
|
+
} catch (err: any) {
|
|
167
|
+
// Mirror production semantics (prerender-collection.ts):
|
|
168
|
+
// Skip errors are intentional — treat as passthrough.
|
|
169
|
+
// All other errors propagate so dev surfaces them.
|
|
170
|
+
if (err?.name === "Skip") {
|
|
171
|
+
return passthroughResult();
|
|
172
|
+
}
|
|
173
|
+
throw err;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
93
178
|
// 4. Create handle store for collecting handle data
|
|
94
179
|
const handleStore = createHandleStore();
|
|
95
180
|
|
|
96
181
|
// 5. Create a minimal request context with the handle store
|
|
97
|
-
// Shallow-copy getParams vars so each param set is independent
|
|
98
|
-
|
|
182
|
+
// Shallow-copy getParams vars so each param set is independent.
|
|
183
|
+
// In dev mode, merge vars from the getParams() probe if the caller
|
|
184
|
+
// didn't provide buildVars (production passes them from expandPrerenderRoutes).
|
|
185
|
+
const effectiveBuildVars = buildVars ?? devProbeBuildVars;
|
|
186
|
+
const variables: Record<string, any> = effectiveBuildVars
|
|
187
|
+
? { ...effectiveBuildVars }
|
|
188
|
+
: {};
|
|
99
189
|
const stubRes = new Response(null, { status: 200 });
|
|
100
190
|
const minimalRequestContext: RequestContext<TEnv> = {
|
|
101
|
-
env: {} as TEnv,
|
|
191
|
+
env: buildEnv ?? ({} as TEnv),
|
|
102
192
|
request: new Request("http://prerender" + pathname),
|
|
103
193
|
url: new URL("http://prerender" + pathname),
|
|
194
|
+
originalUrl: new URL("http://prerender" + pathname),
|
|
104
195
|
pathname,
|
|
105
196
|
searchParams: new URLSearchParams(),
|
|
106
|
-
|
|
197
|
+
_variables: variables,
|
|
107
198
|
get: ((keyOrVar: any) => contextGet(variables, keyOrVar)) as any,
|
|
108
199
|
set: ((keyOrVar: any, value: any) => {
|
|
109
200
|
contextSet(variables, keyOrVar, value);
|
|
@@ -116,16 +207,22 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
116
207
|
deleteCookie: () => {},
|
|
117
208
|
header: () => {},
|
|
118
209
|
setStatus: () => {},
|
|
210
|
+
_setStatus: () => {},
|
|
211
|
+
_rotateStateCookie: () => {},
|
|
212
|
+
_setKeepCacheDirective: () => {},
|
|
119
213
|
use: (() => {
|
|
120
214
|
throw new Error("use() not available during pre-rendering");
|
|
121
215
|
}) as any,
|
|
122
216
|
method: "GET",
|
|
123
217
|
_handleStore: handleStore,
|
|
218
|
+
_requestTags: new Set<string>(),
|
|
124
219
|
waitUntil: () => {},
|
|
125
220
|
onResponse: () => {},
|
|
126
221
|
_onResponseCallbacks: [],
|
|
127
222
|
setLocationState() {},
|
|
128
223
|
_locationState: undefined,
|
|
224
|
+
_renderBarrier: Promise.resolve(),
|
|
225
|
+
_resolveRenderBarrier: () => {},
|
|
129
226
|
_reportedErrors: new WeakSet<object>(),
|
|
130
227
|
reverse: createReverseFunction(
|
|
131
228
|
deps.mergedRouteMap,
|
|
@@ -138,7 +235,7 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
138
235
|
return runWithRequestContext(minimalRequestContext, async () => {
|
|
139
236
|
// 6. Create prerender context with synthetic URL.
|
|
140
237
|
// Prerender handlers get params, pathname, url, searchParams, search,
|
|
141
|
-
// reverse,
|
|
238
|
+
// reverse, use(handle), and optionally env (when buildEnv is configured).
|
|
142
239
|
const buildCtx = createPrerenderContext<TEnv>(
|
|
143
240
|
matchedParams,
|
|
144
241
|
pathname,
|
|
@@ -146,6 +243,8 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
146
243
|
matched.routeKey,
|
|
147
244
|
variables,
|
|
148
245
|
matchedPassthroughRoute,
|
|
246
|
+
buildEnv,
|
|
247
|
+
devMode,
|
|
149
248
|
);
|
|
150
249
|
|
|
151
250
|
// 7. Wire use() for handles only (loaders throw)
|
|
@@ -159,20 +258,18 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
159
258
|
matchedParams,
|
|
160
259
|
buildCtx,
|
|
161
260
|
loaderPromises,
|
|
162
|
-
|
|
261
|
+
// throwOnError: a render failure (or `throw new Skip()`) must reach the
|
|
262
|
+
// build/dev caller, not be baked into a frozen error page (issue #587).
|
|
263
|
+
{ skipLoaders: true, throwOnError: true },
|
|
163
264
|
);
|
|
164
265
|
|
|
165
|
-
// 9. Detect passthrough sentinel: handler returned ctx.passthrough()
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
params: matchedParams,
|
|
173
|
-
passthrough: true as const,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
266
|
+
// 9. Detect passthrough sentinel: handler returned ctx.passthrough().
|
|
267
|
+
// When the route declares loading(), the handler result is deferred so the
|
|
268
|
+
// component is a thenable resolving to the sentinel — detectPrerenderPassthrough
|
|
269
|
+
// resolves thenables before testing (a sync check would miss it and bake a
|
|
270
|
+
// corrupt artifact).
|
|
271
|
+
if (await detectPrerenderPassthrough(allSegments)) {
|
|
272
|
+
return passthroughResult();
|
|
176
273
|
}
|
|
177
274
|
|
|
178
275
|
// 10. Filter out any loader segments (belt-and-suspenders)
|
|
@@ -184,16 +281,22 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
184
281
|
|
|
185
282
|
// 12. Serialize segments using the cache serializer
|
|
186
283
|
const { serializeSegments } = await import("../cache/segment-codec.js");
|
|
284
|
+
const { encodeHandles } = await import("../cache/handle-snapshot.js");
|
|
187
285
|
const serializedSegments = await serializeSegments(nonLoaderSegments);
|
|
188
286
|
|
|
189
|
-
// 13. Collect handle data per segment (skip segments with no handle data)
|
|
190
|
-
|
|
287
|
+
// 13. Collect handle data per segment (skip segments with no handle data).
|
|
288
|
+
// Encoded through the Flight codec (not stored raw) so Promise/ReactNode
|
|
289
|
+
// handle values survive the JSON-serialized build artifact / dev wire —
|
|
290
|
+
// the same fix the runtime cache uses. Encode happens here, in the RSC
|
|
291
|
+
// environment where the codec resolves; the node-side sinks only persist.
|
|
292
|
+
const handlesRecord: Record<string, SegmentHandleData> = {};
|
|
191
293
|
for (const seg of nonLoaderSegments) {
|
|
192
294
|
const segHandles = handleStore.getDataForSegment(seg.id);
|
|
193
295
|
if (Object.keys(segHandles).length > 0) {
|
|
194
|
-
|
|
296
|
+
handlesRecord[seg.id] = segHandles;
|
|
195
297
|
}
|
|
196
298
|
}
|
|
299
|
+
const handles = await encodeHandles(handlesRecord);
|
|
197
300
|
|
|
198
301
|
// Use the trie-level route key (e.g., "docs", "docs.article")
|
|
199
302
|
const routeName = matched.routeKey;
|
|
@@ -203,7 +306,7 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
203
306
|
// evaluation -- we pre-render all intercepts unconditionally and let
|
|
204
307
|
// runtime matching decide which to serve.
|
|
205
308
|
let interceptSegments: SerializedSegmentData[] | undefined;
|
|
206
|
-
let interceptHandles:
|
|
309
|
+
let interceptHandles: string | undefined;
|
|
207
310
|
|
|
208
311
|
const foundIntercepts: {
|
|
209
312
|
intercept: InterceptEntry;
|
|
@@ -211,24 +314,14 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
211
314
|
}[] = [];
|
|
212
315
|
let current: EntryData | null = manifestEntry;
|
|
213
316
|
while (current) {
|
|
214
|
-
|
|
215
|
-
|
|
317
|
+
// Flatten the entry and its sibling layouts into one source list, the
|
|
318
|
+
// same traversal findInterceptForRoute uses; the build keeps ALL matches
|
|
319
|
+
// (not just the innermost) and skips when(). intercept/layout are
|
|
320
|
+
// non-optional arrays, so empty ones are a no-op here.
|
|
321
|
+
for (const source of [current, ...current.layout]) {
|
|
322
|
+
for (const ic of source.intercept) {
|
|
216
323
|
if (ic.routeName === matched.routeKey) {
|
|
217
|
-
foundIntercepts.push({ intercept: ic, entry:
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
if (current.layout && current.layout.length > 0) {
|
|
222
|
-
for (const siblingLayout of current.layout) {
|
|
223
|
-
if (siblingLayout.intercept && siblingLayout.intercept.length > 0) {
|
|
224
|
-
for (const ic of siblingLayout.intercept) {
|
|
225
|
-
if (ic.routeName === matched.routeKey) {
|
|
226
|
-
foundIntercepts.push({
|
|
227
|
-
intercept: ic,
|
|
228
|
-
entry: siblingLayout,
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
}
|
|
324
|
+
foundIntercepts.push({ intercept: ic, entry: source });
|
|
232
325
|
}
|
|
233
326
|
}
|
|
234
327
|
}
|
|
@@ -239,6 +332,18 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
239
332
|
const interceptResolvedSegments: typeof nonLoaderSegments = [];
|
|
240
333
|
|
|
241
334
|
for (const { intercept, entry: parentEntry } of foundIntercepts) {
|
|
335
|
+
// setupBuildUse keys handle pushes by ctx._currentSegmentId. The main
|
|
336
|
+
// resolveAllSegments pass left it on the route's segment id, so pin it
|
|
337
|
+
// to THIS intercept's slot id before resolving the handler/layout --
|
|
338
|
+
// otherwise the intercept's ctx.use() handle pushes land in the wrong
|
|
339
|
+
// bucket and getDataForSegment(seg.id) below drops them from the baked
|
|
340
|
+
// artifact. Re-pinned per iteration so multiple intercepts targeting
|
|
341
|
+
// the same route each get their own id (mirrors fresh.ts parallel-slot
|
|
342
|
+
// pinning).
|
|
343
|
+
const interceptSegmentId = `${parentEntry.shortCode}.${intercept.slotName}`;
|
|
344
|
+
(buildCtx as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
345
|
+
interceptSegmentId;
|
|
346
|
+
|
|
242
347
|
// Resolve handler
|
|
243
348
|
const handlerRaw =
|
|
244
349
|
typeof intercept.handler === "function"
|
|
@@ -265,7 +370,7 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
265
370
|
}
|
|
266
371
|
|
|
267
372
|
interceptResolvedSegments.push({
|
|
268
|
-
id:
|
|
373
|
+
id: interceptSegmentId,
|
|
269
374
|
namespace: `intercept:${intercept.routeName}`,
|
|
270
375
|
type: "parallel" as const,
|
|
271
376
|
index: 0,
|
|
@@ -285,13 +390,20 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
285
390
|
interceptSegments = await serializeSegments(
|
|
286
391
|
interceptResolvedSegments,
|
|
287
392
|
);
|
|
288
|
-
|
|
393
|
+
const interceptHandlesRecord: Record<string, SegmentHandleData> = {};
|
|
289
394
|
for (const seg of interceptResolvedSegments) {
|
|
290
395
|
const segHandles = handleStore.getDataForSegment(seg.id);
|
|
291
396
|
if (Object.keys(segHandles).length > 0) {
|
|
292
|
-
|
|
397
|
+
interceptHandlesRecord[seg.id] = segHandles;
|
|
293
398
|
}
|
|
294
399
|
}
|
|
400
|
+
// The intercept artifact serves main + intercept segments together, so
|
|
401
|
+
// encode the MERGED handle map here (the sinks no longer merge raw
|
|
402
|
+
// records — they store this pre-encoded string as-is).
|
|
403
|
+
interceptHandles = await encodeHandles({
|
|
404
|
+
...handlesRecord,
|
|
405
|
+
...interceptHandlesRecord,
|
|
406
|
+
});
|
|
295
407
|
}
|
|
296
408
|
}
|
|
297
409
|
|
|
@@ -318,7 +430,9 @@ export async function renderStaticSegment<TEnv = any>(
|
|
|
318
430
|
handlerId: string,
|
|
319
431
|
mergedRouteMap: Record<string, string>,
|
|
320
432
|
routeName?: string,
|
|
321
|
-
|
|
433
|
+
buildEnv?: TEnv,
|
|
434
|
+
devMode?: boolean,
|
|
435
|
+
): Promise<{ encoded: string; handles: string } | null> {
|
|
322
436
|
const syntheticUrl = new URL("http://prerender/");
|
|
323
437
|
const syntheticRequest = new Request(syntheticUrl);
|
|
324
438
|
|
|
@@ -328,12 +442,13 @@ export async function renderStaticSegment<TEnv = any>(
|
|
|
328
442
|
// Minimal request context so setupBuildUse can find the HandleStore
|
|
329
443
|
const stubRes = new Response(null, { status: 200 });
|
|
330
444
|
const minimalRequestContext: RequestContext<TEnv> = {
|
|
331
|
-
env: {} as TEnv,
|
|
445
|
+
env: buildEnv ?? ({} as TEnv),
|
|
332
446
|
request: syntheticRequest,
|
|
333
447
|
url: syntheticUrl,
|
|
448
|
+
originalUrl: syntheticUrl,
|
|
334
449
|
pathname: "/",
|
|
335
450
|
searchParams: syntheticUrl.searchParams,
|
|
336
|
-
|
|
451
|
+
_variables: {},
|
|
337
452
|
get: () => undefined as any,
|
|
338
453
|
set: () => {},
|
|
339
454
|
params: {},
|
|
@@ -344,16 +459,22 @@ export async function renderStaticSegment<TEnv = any>(
|
|
|
344
459
|
deleteCookie: () => {},
|
|
345
460
|
header: () => {},
|
|
346
461
|
setStatus: () => {},
|
|
462
|
+
_setStatus: () => {},
|
|
463
|
+
_rotateStateCookie: () => {},
|
|
464
|
+
_setKeepCacheDirective: () => {},
|
|
347
465
|
use: (() => {
|
|
348
466
|
throw new Error("use() not available during static pre-rendering");
|
|
349
467
|
}) as any,
|
|
350
468
|
method: "GET",
|
|
351
469
|
_handleStore: handleStore,
|
|
470
|
+
_requestTags: new Set<string>(),
|
|
352
471
|
waitUntil: () => {},
|
|
353
472
|
onResponse: () => {},
|
|
354
473
|
_onResponseCallbacks: [],
|
|
355
474
|
setLocationState() {},
|
|
356
475
|
_locationState: undefined,
|
|
476
|
+
_renderBarrier: Promise.resolve(),
|
|
477
|
+
_resolveRenderBarrier: () => {},
|
|
357
478
|
_reportedErrors: new WeakSet<object>(),
|
|
358
479
|
reverse: createReverseFunction(
|
|
359
480
|
mergedRouteMap,
|
|
@@ -364,9 +485,13 @@ export async function renderStaticSegment<TEnv = any>(
|
|
|
364
485
|
};
|
|
365
486
|
|
|
366
487
|
return runWithRequestContext(minimalRequestContext, async () => {
|
|
367
|
-
// Static handlers get only reverse
|
|
368
|
-
|
|
369
|
-
|
|
488
|
+
// Static handlers get only reverse, use(handle), and optionally env.
|
|
489
|
+
const buildCtx = createStaticContext<TEnv>(
|
|
490
|
+
mergedRouteMap,
|
|
491
|
+
routeName,
|
|
492
|
+
buildEnv,
|
|
493
|
+
devMode,
|
|
494
|
+
);
|
|
370
495
|
|
|
371
496
|
// Set segment ID so handle pushes are keyed correctly
|
|
372
497
|
(buildCtx as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
@@ -375,23 +500,41 @@ export async function renderStaticSegment<TEnv = any>(
|
|
|
375
500
|
setupBuildUse(buildCtx);
|
|
376
501
|
|
|
377
502
|
const raw = await handler(buildCtx);
|
|
378
|
-
|
|
503
|
+
|
|
504
|
+
// Static handlers must return a ReactNode. A returned Response (e.g. an
|
|
505
|
+
// accidental redirect()) would otherwise be serialized as a corrupt build
|
|
506
|
+
// artifact; fail loudly instead. The fresh/revalidation paths route handler
|
|
507
|
+
// results through handleHandlerResult, which throws Responses; the static
|
|
508
|
+
// build path bypasses that, so guard here.
|
|
509
|
+
if (raw instanceof Response) {
|
|
510
|
+
throw new TypeError(
|
|
511
|
+
`Static handler "${routeName}" returned a Response. Static handlers must return a ReactNode; ` +
|
|
512
|
+
`Responses (redirects, file responses) are not supported during static pre-rendering.`,
|
|
513
|
+
);
|
|
514
|
+
}
|
|
379
515
|
|
|
380
516
|
const segment: ResolvedSegment = {
|
|
381
517
|
id: handlerId,
|
|
382
518
|
namespace: handlerId,
|
|
383
519
|
type: "layout",
|
|
384
520
|
index: 0,
|
|
385
|
-
component,
|
|
521
|
+
component: raw,
|
|
386
522
|
params: {},
|
|
387
523
|
belongsToRoute: false,
|
|
388
524
|
};
|
|
389
525
|
|
|
390
526
|
const { serializeSegments } = await import("../cache/segment-codec.js");
|
|
527
|
+
const { encodeHandleValue } = await import("../cache/handle-snapshot.js");
|
|
391
528
|
const [serialized] = await serializeSegments([segment]);
|
|
392
529
|
|
|
393
|
-
// Collect handle data pushed during rendering
|
|
394
|
-
|
|
530
|
+
// Collect handle data pushed during rendering and Flight-encode it (so
|
|
531
|
+
// Promise/ReactNode handle values survive the JSON build artifact). "" when
|
|
532
|
+
// nothing was pushed.
|
|
533
|
+
const segHandles = handleStore.getDataForSegment(handlerId);
|
|
534
|
+
const handles =
|
|
535
|
+
Object.keys(segHandles).length > 0
|
|
536
|
+
? await encodeHandleValue(segHandles)
|
|
537
|
+
: "";
|
|
395
538
|
|
|
396
539
|
return { encoded: serialized.encoded, handles };
|
|
397
540
|
});
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { traverseBack } from "./pattern-matching.js";
|
|
3
|
-
import { collectRouteMiddleware } from "./middleware.js";
|
|
4
|
-
import {
|
|
5
|
-
parseAcceptTypes,
|
|
6
|
-
RSC_RESPONSE_TYPE,
|
|
7
|
-
pickNegotiateVariant,
|
|
8
|
-
} from "./content-negotiation.js";
|
|
1
|
+
import { negotiateRoute } from "./content-negotiation.js";
|
|
9
2
|
import { runWithRouterLogContext, withRouterLogScope } from "./logging.js";
|
|
10
3
|
import type { EntryData } from "../server/context";
|
|
11
4
|
import type { RouteMatchResult } from "./pattern-matching.js";
|
|
12
5
|
import type { MiddlewareFn } from "./middleware.js";
|
|
6
|
+
import { resolveRoute } from "./route-snapshot.js";
|
|
13
7
|
|
|
14
8
|
export interface PreviewMatchDeps<TEnv = any> {
|
|
15
9
|
findMatch: (pathname: string) => RouteMatchResult<TEnv> | null;
|
|
@@ -42,110 +36,46 @@ export async function previewMatch<TEnv = any>(
|
|
|
42
36
|
const url = new URL(request.url);
|
|
43
37
|
const pathname = url.pathname;
|
|
44
38
|
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
// Route resolution via snapshot (lite mode: skip entries/cacheScope
|
|
40
|
+
// since previewMatch only needs matched, manifestEntry, routeMiddleware,
|
|
41
|
+
// and responseType)
|
|
42
|
+
const result = await resolveRoute<TEnv>(pathname, {
|
|
43
|
+
findMatch: deps.findMatch,
|
|
44
|
+
lite: true,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (!result) {
|
|
48
48
|
return null;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// Skip redirect check - will be handled in full match
|
|
52
|
-
if (
|
|
52
|
+
if (result.type === "redirect") {
|
|
53
53
|
return { routeMiddleware: undefined };
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
const manifestEntry
|
|
58
|
-
|
|
59
|
-
matched.routeKey,
|
|
60
|
-
pathname,
|
|
61
|
-
undefined, // No metrics store for preview
|
|
62
|
-
false, // isSSR - doesn't matter for preview
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
// Collect route-level middleware from entry tree
|
|
66
|
-
// Includes middleware from orphan layouts (inline layouts within routes)
|
|
67
|
-
const routeMiddleware = collectRouteMiddleware(
|
|
68
|
-
traverseBack(manifestEntry),
|
|
69
|
-
matched.params,
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
// Check for response type (from trie match or manifest entry)
|
|
73
|
-
const responseType =
|
|
74
|
-
matched.responseType ||
|
|
75
|
-
(manifestEntry.type === "route"
|
|
76
|
-
? manifestEntry.responseType
|
|
77
|
-
: undefined);
|
|
78
|
-
|
|
79
|
-
// Content negotiation: when negotiate variants exist, pick the best
|
|
80
|
-
// handler based on the Accept header. Uses q-values and client order
|
|
81
|
-
// as tiebreaker (matching Express/Hono behavior). RSC routes participate
|
|
82
|
-
// as text/html candidates so browsers naturally get HTML without
|
|
83
|
-
// special-casing.
|
|
84
|
-
if (matched.negotiateVariants && matched.negotiateVariants.length > 0) {
|
|
85
|
-
const acceptEntries = parseAcceptTypes(
|
|
86
|
-
request.headers.get("accept") || "",
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
// Build candidate list preserving definition order.
|
|
90
|
-
// For wildcard (*/*) and no-Accept fallback, the first candidate wins.
|
|
91
|
-
const variants = matched.negotiateVariants;
|
|
92
|
-
let candidates: Array<{ routeKey: string; responseType: string }>;
|
|
93
|
-
if (responseType) {
|
|
94
|
-
// Primary is response-type — include it as a candidate
|
|
95
|
-
candidates = [
|
|
96
|
-
...variants,
|
|
97
|
-
{ routeKey: matched.routeKey, responseType },
|
|
98
|
-
];
|
|
99
|
-
} else {
|
|
100
|
-
// Primary is RSC — insert as text/html candidate in definition order
|
|
101
|
-
const rscCandidate = {
|
|
102
|
-
routeKey: matched.routeKey,
|
|
103
|
-
responseType: RSC_RESPONSE_TYPE,
|
|
104
|
-
};
|
|
105
|
-
candidates = matched.rscFirst
|
|
106
|
-
? [rscCandidate, ...variants]
|
|
107
|
-
: [...variants, rscCandidate];
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const variant = pickNegotiateVariant(acceptEntries, candidates);
|
|
56
|
+
const snapshot = result.snapshot;
|
|
57
|
+
const { matched, manifestEntry, routeMiddleware, responseType } =
|
|
58
|
+
snapshot;
|
|
111
59
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
traverseBack(negotiateEntry),
|
|
129
|
-
matched.params,
|
|
130
|
-
);
|
|
131
|
-
return {
|
|
132
|
-
routeMiddleware:
|
|
133
|
-
variantMiddleware.length > 0 ? variantMiddleware : undefined,
|
|
134
|
-
responseType: variant.responseType,
|
|
135
|
-
handler:
|
|
136
|
-
negotiateEntry.type === "route"
|
|
137
|
-
? negotiateEntry.handler
|
|
138
|
-
: undefined,
|
|
139
|
-
params: matched.params,
|
|
140
|
-
negotiated: true,
|
|
141
|
-
manifestEntry: negotiateEntry,
|
|
142
|
-
routeKey: matched.routeKey,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
60
|
+
const negotiation = await negotiateRoute(request, pathname, snapshot);
|
|
61
|
+
if (negotiation) {
|
|
62
|
+
return {
|
|
63
|
+
routeMiddleware:
|
|
64
|
+
negotiation.routeMiddleware.length > 0
|
|
65
|
+
? negotiation.routeMiddleware
|
|
66
|
+
: undefined,
|
|
67
|
+
responseType: negotiation.responseType,
|
|
68
|
+
handler: negotiation.handler,
|
|
69
|
+
params: matched.params,
|
|
70
|
+
manifestEntry: negotiation.manifestEntry,
|
|
71
|
+
routeKey: matched.routeKey,
|
|
72
|
+
// omitted unless a variant negotiated, preserving the prior public
|
|
73
|
+
// shape (absent for plain response routes, not negotiated:false)
|
|
74
|
+
...(negotiation.negotiated ? { negotiated: true } : {}),
|
|
75
|
+
};
|
|
145
76
|
}
|
|
146
77
|
|
|
147
|
-
//
|
|
148
|
-
// negotiated so the handler sets Vary: Accept on the response.
|
|
78
|
+
// No negotiation or RSC won — return default route info
|
|
149
79
|
const hasVariants =
|
|
150
80
|
matched.negotiateVariants && matched.negotiateVariants.length > 0;
|
|
151
81
|
return {
|