@rangojs/router 0.0.0-experimental.135c6902 → 0.0.0-experimental.136
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 -0
- package/README.md +245 -49
- package/dist/bin/rango.js +440 -133
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3386 -1111
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +78 -25
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +64 -2
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +247 -23
- package/skills/caching/SKILL.md +281 -11
- package/skills/composability/SKILL.md +27 -3
- 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 +250 -30
- package/skills/host-router/SKILL.md +83 -23
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +87 -18
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +289 -53
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -0
- package/skills/migrate-react-router/SKILL.md +914 -0
- package/skills/mime-routes/SKILL.md +28 -1
- package/skills/observability/SKILL.md +172 -0
- package/skills/parallel/SKILL.md +144 -7
- package/skills/prerender/SKILL.md +172 -125
- package/skills/rango/SKILL.md +251 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +123 -48
- package/skills/route/SKILL.md +101 -5
- package/skills/router-setup/SKILL.md +117 -10
- 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 +130 -0
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +129 -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 +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 +332 -29
- package/skills/use-cache/SKILL.md +57 -14
- package/skills/view-transitions/SKILL.md +337 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +1 -66
- 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 +192 -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/navigation-bridge.ts +131 -30
- package/src/browser/navigation-client.ts +199 -86
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +157 -74
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +174 -109
- package/src/browser/prefetch/cache.ts +222 -69
- package/src/browser/prefetch/fetch.ts +347 -39
- package/src/browser/prefetch/queue.ts +113 -32
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +102 -15
- package/src/browser/react/NavigationProvider.tsx +300 -122
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/deferred-handle-resolution.ts +75 -0
- 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 +99 -8
- package/src/browser/rsc-router.tsx +125 -28
- package/src/browser/scroll-restoration.ts +37 -22
- package/src/browser/segment-reconciler.ts +36 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +222 -61
- package/src/browser/types.ts +115 -12
- 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 +5 -1
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +165 -36
- 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 +260 -94
- 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 +239 -52
- package/src/cache/cache-scope.ts +234 -87
- 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 +1989 -378
- 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 +89 -21
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +10 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- 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 +44 -21
- package/src/client.tsx +119 -290
- 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/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 +62 -19
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +37 -8
- 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 -2
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +99 -13
- package/src/index.ts +139 -19
- 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 +37 -41
- 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 +19 -77
- package/src/route-definition/dsl-helpers.ts +461 -304
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +149 -74
- package/src/route-definition/index.ts +4 -2
- package/src/route-definition/redirect.ts +51 -10
- package/src/route-definition/resolve-handler-use.ts +160 -0
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-map-builder.ts +0 -16
- package/src/route-types.ts +37 -46
- 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 +44 -23
- package/src/router/handler-context.ts +83 -39
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +48 -24
- package/src/router/lazy-includes.ts +15 -52
- package/src/router/loader-resolution.ts +274 -56
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +40 -42
- package/src/router/match-api.ts +124 -204
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +54 -25
- package/src/router/match-middleware/cache-lookup.ts +205 -271
- package/src/router/match-middleware/cache-store.ts +81 -50
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +45 -14
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +93 -39
- package/src/router/metrics.ts +5 -34
- package/src/router/middleware-types.ts +13 -142
- package/src/router/middleware.ts +268 -171
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +132 -90
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +195 -56
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +3 -27
- package/src/router/router-interfaces.ts +129 -35
- package/src/router/router-options.ts +202 -15
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +197 -92
- package/src/router/segment-resolution/helpers.ts +115 -30
- package/src/router/segment-resolution/loader-cache.ts +76 -39
- package/src/router/segment-resolution/revalidation.ts +392 -338
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +56 -0
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +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 +163 -59
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +174 -54
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +660 -518
- package/src/rsc/helpers.ts +168 -46
- package/src/rsc/index.ts +2 -5
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +127 -31
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +133 -13
- 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 +115 -73
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +287 -113
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +36 -6
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +149 -0
- package/src/segment-system.tsx +269 -202
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +235 -51
- 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 +38 -46
- package/src/server/request-context.ts +442 -173
- package/src/ssr/index.tsx +24 -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 +105 -0
- package/src/testing/internal/context.ts +371 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +357 -0
- package/src/testing/render-route.tsx +581 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +20 -58
- 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 +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +234 -82
- package/src/types/index.ts +3 -10
- package/src/types/loader-types.ts +44 -15
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +12 -7
- package/src/types/segments.ts +136 -15
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +68 -18
- 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 -19
- package/src/use-loader.tsx +346 -89
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/discover-routers.ts +130 -85
- 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 +214 -132
- 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 +57 -4
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +6 -0
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +178 -5
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
- package/src/vite/plugins/expose-action-id.ts +48 -95
- package/src/vite/plugins/expose-id-utils.ts +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 +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +37 -40
- package/src/vite/plugins/virtual-entries.ts +39 -25
- package/src/vite/rango.ts +119 -111
- package/src/vite/router-discovery.ts +941 -142
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +81 -34
- package/src/vite/utils/shared-utils.ts +92 -42
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
package/src/browser/types.ts
CHANGED
|
@@ -14,7 +14,6 @@ import type { RenderSegmentsOptions } from "../segment-system.js";
|
|
|
14
14
|
export interface RscPayload<TMetadata = RscMetadata> {
|
|
15
15
|
metadata?: TMetadata;
|
|
16
16
|
returnValue?: ActionResult;
|
|
17
|
-
formState?: unknown;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
/**
|
|
@@ -32,10 +31,21 @@ export type HandleData = Record<string, Record<string, unknown[]>>;
|
|
|
32
31
|
export interface RscMetadata {
|
|
33
32
|
pathname: string;
|
|
34
33
|
segments: ResolvedSegment[];
|
|
34
|
+
/** Router instance ID — the current app's identity. A mismatch with the
|
|
35
|
+
* client's id (sent as _rsc_rid) is detected server-side and answered with
|
|
36
|
+
* X-RSC-Reload (full document load), so the client never swaps apps
|
|
37
|
+
* in-session; within a session this always equals the current app. */
|
|
38
|
+
routerId?: string;
|
|
35
39
|
isPartial?: boolean;
|
|
36
40
|
isError?: boolean;
|
|
37
41
|
matched?: string[];
|
|
38
42
|
diff?: string[];
|
|
43
|
+
/**
|
|
44
|
+
* All segment ids re-resolved on the server, including null-component
|
|
45
|
+
* ones excluded from `segments`/`diff`. Drives client-side handle-bucket
|
|
46
|
+
* cleanup. Superset of `diff`. See MatchResult.resolvedIds.
|
|
47
|
+
*/
|
|
48
|
+
resolvedIds?: string[];
|
|
39
49
|
/** Merged route params from the matched route */
|
|
40
50
|
params?: Record<string, string>;
|
|
41
51
|
/**
|
|
@@ -60,6 +70,22 @@ export interface RscMetadata {
|
|
|
60
70
|
* Sent on initial render so the browser can configure its cache duration.
|
|
61
71
|
*/
|
|
62
72
|
prefetchCacheTTL?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Max entries in the client-side in-memory prefetch cache (FIFO eviction).
|
|
75
|
+
* Sent on initial render so the browser can configure its cache capacity.
|
|
76
|
+
*/
|
|
77
|
+
prefetchCacheSize?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Max concurrent speculative prefetch requests on the client.
|
|
80
|
+
* Sent on initial render so the browser can configure its prefetch queue.
|
|
81
|
+
*/
|
|
82
|
+
prefetchConcurrency?: number;
|
|
83
|
+
/**
|
|
84
|
+
* Server-resolved rango state cookie name (`{prefix}_{routerId}`). The client
|
|
85
|
+
* reads it verbatim and binds the rango state cookie to it; composition
|
|
86
|
+
* happens only server-side.
|
|
87
|
+
*/
|
|
88
|
+
stateCookieName?: string;
|
|
63
89
|
/**
|
|
64
90
|
* Theme configuration from router.
|
|
65
91
|
* Included when theme is enabled in router config.
|
|
@@ -70,10 +96,22 @@ export interface RscMetadata {
|
|
|
70
96
|
* Included when theme is enabled in router config.
|
|
71
97
|
*/
|
|
72
98
|
initialTheme?: Theme;
|
|
99
|
+
/** URL prefix for all routes (from createRouter({ basename })). */
|
|
100
|
+
basename?: string;
|
|
73
101
|
/** Whether connection warmup is enabled */
|
|
74
102
|
warmupEnabled?: boolean;
|
|
75
|
-
/**
|
|
76
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Whether the client should hydrate inside React.StrictMode. Carried on the
|
|
105
|
+
* initial full-render payload only; the browser entry reads it once at
|
|
106
|
+
* hydration. Defaults to true on the client when omitted.
|
|
107
|
+
*/
|
|
108
|
+
strictMode?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Server-side redirect with optional state (for partial requests).
|
|
111
|
+
* `external: true` (from redirect(url, { external: true })) tells the client
|
|
112
|
+
* to hard-navigate to an off-host target instead of validating same-origin.
|
|
113
|
+
*/
|
|
114
|
+
redirect?: { url: string; external?: boolean };
|
|
77
115
|
/** Server-set location state to include in history.pushState */
|
|
78
116
|
locationState?: Record<string, unknown>;
|
|
79
117
|
}
|
|
@@ -174,6 +212,15 @@ export interface TrackedActionState {
|
|
|
174
212
|
result: unknown | null;
|
|
175
213
|
}
|
|
176
214
|
|
|
215
|
+
/**
|
|
216
|
+
* The value returned by {@link useAction} when called without a selector.
|
|
217
|
+
*
|
|
218
|
+
* This is the stable, public name for the action-state shape; consumers can
|
|
219
|
+
* name it in their own signatures (e.g. a wrapper hook). It aliases the
|
|
220
|
+
* internal {@link TrackedActionState}.
|
|
221
|
+
*/
|
|
222
|
+
export type ActionState = TrackedActionState;
|
|
223
|
+
|
|
177
224
|
/**
|
|
178
225
|
* Listener for action state changes
|
|
179
226
|
*
|
|
@@ -311,8 +358,14 @@ export interface RouterInstance {
|
|
|
311
358
|
replace(url: string, options?: RouterNavigateOptions): Promise<void>;
|
|
312
359
|
/** Refresh the current route (re-fetch server data, preserve client state) */
|
|
313
360
|
refresh(): Promise<void>;
|
|
314
|
-
/**
|
|
315
|
-
|
|
361
|
+
/**
|
|
362
|
+
* Prefetch a URL for faster client-side transition.
|
|
363
|
+
*
|
|
364
|
+
* Pass `{ key: ":source" }` to source-scope the prefetch cache entry (parity
|
|
365
|
+
* with `<Link prefetchKey=":source">`) when the target's response can differ
|
|
366
|
+
* by source page.
|
|
367
|
+
*/
|
|
368
|
+
prefetch(url: string, options?: { key?: ":source" }): void;
|
|
316
369
|
/** Go back in browser history */
|
|
317
370
|
back(): void;
|
|
318
371
|
/** Go forward in browser history */
|
|
@@ -341,7 +394,13 @@ export type ReadonlyURLSearchParams = Omit<
|
|
|
341
394
|
export interface RscBrowserDependencies {
|
|
342
395
|
createFromFetch: <T>(
|
|
343
396
|
response: Promise<Response>,
|
|
344
|
-
options?: {
|
|
397
|
+
options?: {
|
|
398
|
+
temporaryReferences?: any;
|
|
399
|
+
findSourceMapURL?: (
|
|
400
|
+
filename: string,
|
|
401
|
+
environmentName: string,
|
|
402
|
+
) => string | null;
|
|
403
|
+
},
|
|
345
404
|
) => Promise<T>;
|
|
346
405
|
createFromReadableStream: <T>(stream: ReadableStream) => Promise<T>;
|
|
347
406
|
encodeReply: (
|
|
@@ -398,22 +457,47 @@ export interface NavigationStore {
|
|
|
398
457
|
// History-based segment cache (for back/forward navigation and partial merging)
|
|
399
458
|
getHistoryKey(): string;
|
|
400
459
|
setHistoryKey(key: string): void;
|
|
460
|
+
/** Monotonic token of the most recently committed navigation. */
|
|
461
|
+
getNavInstance(): number;
|
|
462
|
+
/** Nav-instance token recorded on a cache entry (undefined if absent). */
|
|
463
|
+
getCacheEntryInstance(historyKey: string): number | undefined;
|
|
401
464
|
cacheSegmentsForHistory(
|
|
402
465
|
historyKey: string,
|
|
403
466
|
segments: ResolvedSegment[],
|
|
404
467
|
handleData?: HandleData,
|
|
405
468
|
): void;
|
|
406
|
-
getCachedSegments(
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
469
|
+
getCachedSegments(historyKey: string):
|
|
470
|
+
| {
|
|
471
|
+
segments: ResolvedSegment[];
|
|
472
|
+
stale: boolean;
|
|
473
|
+
handleData?: HandleData;
|
|
474
|
+
routerId?: string;
|
|
475
|
+
/**
|
|
476
|
+
* True when the entry's handle data is incomplete (a deferred Meta was
|
|
477
|
+
* still pending at navigate-away). A popstate return must revalidate with
|
|
478
|
+
* a FULL re-render so the server re-streams handles.
|
|
479
|
+
*/
|
|
480
|
+
handlesPending?: boolean;
|
|
481
|
+
}
|
|
410
482
|
| undefined;
|
|
411
483
|
hasHistoryCache(historyKey: string): boolean;
|
|
412
|
-
|
|
484
|
+
/**
|
|
485
|
+
* Update only the handleData (and optionally the stale / handlesPending flags)
|
|
486
|
+
* of an existing cache entry. When a flag is omitted the entry's current value
|
|
487
|
+
* is preserved. `stale=true` marks a single entry stale so a popstate return
|
|
488
|
+
* revalidates it; `handlesPending=true` additionally forces that revalidation
|
|
489
|
+
* to be a full re-render (so a deferred Meta re-streams).
|
|
490
|
+
*/
|
|
491
|
+
updateCacheHandleData(
|
|
492
|
+
historyKey: string,
|
|
493
|
+
handleData: HandleData,
|
|
494
|
+
stale?: boolean,
|
|
495
|
+
handlesPending?: boolean,
|
|
496
|
+
): void;
|
|
413
497
|
markCacheAsStale(): void;
|
|
498
|
+
markHistoryCacheStale(): void;
|
|
414
499
|
markCacheAsStaleAndBroadcast(): void;
|
|
415
500
|
clearHistoryCache(): void;
|
|
416
|
-
broadcastCacheInvalidation(): void;
|
|
417
501
|
|
|
418
502
|
// Cross-tab refresh callback (set by navigation bridge)
|
|
419
503
|
setCrossTabRefreshCallback(callback: () => void): void;
|
|
@@ -422,6 +506,10 @@ export interface NavigationStore {
|
|
|
422
506
|
getInterceptSourceUrl(): string | null;
|
|
423
507
|
setInterceptSourceUrl(url: string | null): void;
|
|
424
508
|
|
|
509
|
+
// Router identity tracking (for cross-app navigation detection)
|
|
510
|
+
getRouterId?(): string | undefined;
|
|
511
|
+
setRouterId?(id: string): void;
|
|
512
|
+
|
|
425
513
|
// UI update notifications
|
|
426
514
|
onUpdate(callback: UpdateSubscriber): () => void;
|
|
427
515
|
emitUpdate(update: NavigationUpdate): void;
|
|
@@ -452,6 +540,8 @@ export interface FetchPartialOptions {
|
|
|
452
540
|
interceptSourceUrl?: string;
|
|
453
541
|
/** RSC version for cache invalidation detection */
|
|
454
542
|
version?: string;
|
|
543
|
+
/** Current router ID — server detects app switch and returns full response */
|
|
544
|
+
routerId?: string;
|
|
455
545
|
/** If true, this is an HMR refetch - server should invalidate manifest cache */
|
|
456
546
|
hmr?: boolean;
|
|
457
547
|
}
|
|
@@ -463,6 +553,15 @@ export interface FetchPartialResult {
|
|
|
463
553
|
payload: RscPayload;
|
|
464
554
|
/** Promise that resolves when the response stream is fully consumed */
|
|
465
555
|
streamComplete: Promise<void>;
|
|
556
|
+
/**
|
|
557
|
+
* True only when this payload came from a prefetch-cache hit whose stream had
|
|
558
|
+
* ALREADY fully drained at fetch time (the route was fully prefetched). The
|
|
559
|
+
* commit then runs in a startTransition so loading()/Suspense content — already
|
|
560
|
+
* resolved — swaps in directly without flashing a fallback. A partially-warmed
|
|
561
|
+
* (still-streaming) prefetch hit and a cold fetch leave this false so their
|
|
562
|
+
* fallbacks stream as usual.
|
|
563
|
+
*/
|
|
564
|
+
fullyPrefetched?: boolean;
|
|
466
565
|
}
|
|
467
566
|
|
|
468
567
|
/**
|
|
@@ -520,6 +619,10 @@ export interface NavigationBridge {
|
|
|
520
619
|
refresh(): Promise<void>;
|
|
521
620
|
handlePopstate(): Promise<void>;
|
|
522
621
|
registerLinkInterception(): () => void;
|
|
622
|
+
/** Current RSC version (live, reflects the latest updateVersion). */
|
|
623
|
+
getVersion(): string | undefined;
|
|
624
|
+
/** Update the RSC version (e.g. after HMR). Clears prefetch cache. */
|
|
625
|
+
updateVersion(newVersion: string): void;
|
|
523
626
|
}
|
|
524
627
|
|
|
525
628
|
/**
|
|
@@ -1,29 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveSameOriginRedirect,
|
|
3
|
+
resolveExternalRedirect,
|
|
4
|
+
} from "../redirect-origin.js";
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
7
|
* Validate that a client-consumed redirect URL (from headers or Flight payload)
|
|
3
8
|
* targets the same origin as the current page. Prevents open-redirect attacks
|
|
4
9
|
* via crafted responses.
|
|
5
10
|
*
|
|
11
|
+
* Thin wrapper over the shared {@link resolveSameOriginRedirect} rule (also used
|
|
12
|
+
* by the server guard in `rsc/redirect-guard.ts`) so client and server enforce
|
|
13
|
+
* the identical same-origin contract. Adds the client-side `console.error` on a
|
|
14
|
+
* block; the resolver itself stays pure.
|
|
15
|
+
*
|
|
6
16
|
* @returns The canonical (normalized) URL string on success, or null if blocked.
|
|
7
17
|
*/
|
|
8
18
|
export function validateRedirectOrigin(
|
|
9
19
|
url: string,
|
|
10
20
|
currentOrigin: string,
|
|
11
21
|
): string | null {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
const resolved = resolveSameOriginRedirect(url, currentOrigin);
|
|
23
|
+
if (resolved === null) {
|
|
24
|
+
console.error(
|
|
25
|
+
`[rango] Redirect blocked: cross-origin or invalid target "${url}"`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return resolved;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Validate an explicit off-origin redirect (`redirect(url, { external: true })`)
|
|
33
|
+
* the client is about to hard-navigate to via `window.location.assign()`.
|
|
34
|
+
*
|
|
35
|
+
* Thin wrapper over the shared {@link resolveExternalRedirect} rule (also used by
|
|
36
|
+
* the server guard in `rsc/redirect-guard.ts`) so client and server enforce the
|
|
37
|
+
* identical contract: `external` allows an off-origin target but only an
|
|
38
|
+
* http(s) scheme. This stops a forged or mistaken external payload carrying a
|
|
39
|
+
* `javascript:`/`data:` URL from turning `location.assign` into a scriptable
|
|
40
|
+
* navigation. Adds the client-side `console.error` on a block; the resolver
|
|
41
|
+
* itself stays pure.
|
|
42
|
+
*
|
|
43
|
+
* @returns The normalized URL string on success, or null if blocked.
|
|
44
|
+
*/
|
|
45
|
+
export function validateExternalRedirect(
|
|
46
|
+
url: string,
|
|
47
|
+
currentOrigin: string,
|
|
48
|
+
): string | null {
|
|
49
|
+
const resolved = resolveExternalRedirect(url, currentOrigin);
|
|
50
|
+
if (resolved === null) {
|
|
51
|
+
console.error(
|
|
52
|
+
`[rango] External redirect blocked: non-http(s) target "${url}"`,
|
|
53
|
+
);
|
|
28
54
|
}
|
|
55
|
+
return resolved;
|
|
29
56
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Collect the `"use client"` client-reference keys reachable from an error /
|
|
2
|
+
// notFound boundary registration, for routing them into the dedicated
|
|
3
|
+
// `app-fallback` chunk (see vite/utils/client-chunks.ts).
|
|
4
|
+
//
|
|
5
|
+
// A boundary registration is not always a bare client element. The common,
|
|
6
|
+
// load-bearing pattern wraps the client boundary in providers a thrown handler
|
|
7
|
+
// needs (the layout that would normally supply them did not mount):
|
|
8
|
+
//
|
|
9
|
+
// defaultErrorBoundary: ({ error }) => (
|
|
10
|
+
// <FallbackIntl locales={...}>
|
|
11
|
+
// <ThemedError error={error} /> // <- the real "use client" boundary
|
|
12
|
+
// </FallbackIntl>
|
|
13
|
+
// )
|
|
14
|
+
//
|
|
15
|
+
// So the value may be (a) a handler FUNCTION returning a tree, or (b) an element
|
|
16
|
+
// tree with the client boundary nested below server wrappers. We:
|
|
17
|
+
// 1. If it's a function, CALL it with synthetic props to get the returned tree.
|
|
18
|
+
// This only constructs JSX — the inner components are element `type`s, never
|
|
19
|
+
// invoked — so no hooks run. Guarded: a boundary that needs a real render
|
|
20
|
+
// context (request globals, etc.) throws and is skipped (graceful: it simply
|
|
21
|
+
// stays on the default grouping, as before).
|
|
22
|
+
// 2. Walk the resulting tree and report every element whose `.type` is a
|
|
23
|
+
// plugin-rsc client reference.
|
|
24
|
+
//
|
|
25
|
+
// Limit: a boundary that *conditionally* renders different client components based
|
|
26
|
+
// on the runtime error cannot be resolved statically — only the branch taken with
|
|
27
|
+
// the synthetic error is seen. Such cases fall back to the default chunk; the
|
|
28
|
+
// custom `clientChunks` function is the escape hatch.
|
|
29
|
+
|
|
30
|
+
const CLIENT_REF = Symbol.for("react.client.reference");
|
|
31
|
+
const MAX_DEPTH = 40;
|
|
32
|
+
|
|
33
|
+
// Synthetic props covering the error-boundary (`{ error, reset }`) and notFound
|
|
34
|
+
// (`{ pathname }`) handler shapes. The handler destructures what it needs.
|
|
35
|
+
const SYNTHETIC_PROPS = {
|
|
36
|
+
error: new Error("rango: build-time fallback-chunk discovery"),
|
|
37
|
+
reset: () => {},
|
|
38
|
+
pathname: "/",
|
|
39
|
+
info: { componentStack: "" },
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
interface MaybeElement {
|
|
43
|
+
type?: { $$typeof?: symbol; $$id?: string };
|
|
44
|
+
props?: Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isReactNodeLike(v: unknown): boolean {
|
|
48
|
+
return (
|
|
49
|
+
Array.isArray(v) ||
|
|
50
|
+
(typeof v === "object" && v !== null && "$$typeof" in (v as object))
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function walkElementTree(
|
|
55
|
+
node: unknown,
|
|
56
|
+
report: (refKey: string) => void,
|
|
57
|
+
depth: number,
|
|
58
|
+
): void {
|
|
59
|
+
if (node == null || depth > MAX_DEPTH) return;
|
|
60
|
+
if (Array.isArray(node)) {
|
|
61
|
+
for (const child of node) walkElementTree(child, report, depth + 1);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (typeof node !== "object") return;
|
|
65
|
+
|
|
66
|
+
const el = node as MaybeElement;
|
|
67
|
+
const type = el.type;
|
|
68
|
+
if (type?.$$typeof === CLIENT_REF && typeof type.$$id === "string") {
|
|
69
|
+
// $$id is `<referenceKey>#<exportName>` in build mode — keep the referenceKey.
|
|
70
|
+
report(type.$$id.split("#")[0]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const props = el.props;
|
|
74
|
+
if (props && typeof props === "object") {
|
|
75
|
+
// Children are always nodes; other props are followed only when they look
|
|
76
|
+
// like React nodes (slots/icons), never arbitrary data objects.
|
|
77
|
+
walkElementTree(props.children, report, depth + 1);
|
|
78
|
+
for (const key in props) {
|
|
79
|
+
if (key === "children") continue;
|
|
80
|
+
const value = props[key];
|
|
81
|
+
if (isReactNodeLike(value)) walkElementTree(value, report, depth + 1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Report every `"use client"` client-reference key reachable from a single
|
|
88
|
+
* error/notFound boundary registration (handler function or element tree).
|
|
89
|
+
*/
|
|
90
|
+
export function collectFallbackClientRefs(
|
|
91
|
+
boundary: unknown,
|
|
92
|
+
report: (refKey: string) => void,
|
|
93
|
+
): void {
|
|
94
|
+
try {
|
|
95
|
+
let node = boundary;
|
|
96
|
+
if (typeof node === "function") {
|
|
97
|
+
node = (node as (props: unknown) => unknown)(SYNTHETIC_PROPS);
|
|
98
|
+
}
|
|
99
|
+
walkElementTree(node, report, 0);
|
|
100
|
+
} catch {
|
|
101
|
+
// The boundary needs a real render context (request globals, hooks at the
|
|
102
|
+
// top level) or its tree has hostile getters. Its client refs can't be
|
|
103
|
+
// resolved statically — skip. It stays on the default grouping (no
|
|
104
|
+
// regression vs. not collecting), and the custom clientChunks fn is the
|
|
105
|
+
// escape hatch for such cases.
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
import type { UrlPatterns } from "../urls.js";
|
|
12
12
|
import type { AllUseItems } from "../route-types.js";
|
|
13
13
|
import { extractStaticPrefix } from "../router/pattern-matching.js";
|
|
14
|
-
import {
|
|
14
|
+
import { RangoContext, runWithPrefixes } from "../server/context.js";
|
|
15
15
|
import type { EntryData, TrackedInclude } from "../server/context.js";
|
|
16
16
|
import type { TrailingSlashMode } from "../types.js";
|
|
17
17
|
import { createRouteHelpers } from "../route-definition.js";
|
|
18
18
|
import MapRootLayout from "../server/root-layout.js";
|
|
19
|
+
import { collectFallbackClientRefs } from "./collect-fallback-refs.js";
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Node in the prefix tree
|
|
@@ -45,7 +46,7 @@ export interface GeneratedManifest {
|
|
|
45
46
|
routeTrailingSlash?: Record<string, string>;
|
|
46
47
|
/** Route names using Prerender (for dev-mode Node.js delegation) */
|
|
47
48
|
prerenderRoutes?: string[];
|
|
48
|
-
/** Route names with
|
|
49
|
+
/** Route names wrapped with Passthrough() (live handler for runtime fallback) */
|
|
49
50
|
passthroughRoutes?: string[];
|
|
50
51
|
/** Route name → response type for non-RSC routes */
|
|
51
52
|
responseTypeRoutes?: Record<string, string>;
|
|
@@ -57,6 +58,26 @@ export interface GeneratedManifest {
|
|
|
57
58
|
* Build prefix tree node by running the patterns with proper context.
|
|
58
59
|
* Uses a visited set to detect circular includes and prevent infinite recursion.
|
|
59
60
|
*/
|
|
61
|
+
// Merge tracked nested includes into `target`. Multiple includes can share a
|
|
62
|
+
// fullPrefix (e.g. include("/", a), include("/", b)) — concat their routes and
|
|
63
|
+
// Object.assign children rather than overwrite.
|
|
64
|
+
function mergeIncludeNodes(
|
|
65
|
+
target: Record<string, PrefixTreeNode>,
|
|
66
|
+
includes: TrackedInclude[],
|
|
67
|
+
buildChild: (include: TrackedInclude) => PrefixTreeNode,
|
|
68
|
+
): void {
|
|
69
|
+
for (const include of includes) {
|
|
70
|
+
const node = buildChild(include);
|
|
71
|
+
const existing = target[include.fullPrefix];
|
|
72
|
+
if (existing) {
|
|
73
|
+
existing.routes.push(...node.routes);
|
|
74
|
+
Object.assign(existing.children, node.children);
|
|
75
|
+
} else {
|
|
76
|
+
target[include.fullPrefix] = node;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
60
81
|
function buildPrefixTreeNode(
|
|
61
82
|
urlPrefix: string,
|
|
62
83
|
namePrefix: string | undefined,
|
|
@@ -93,7 +114,7 @@ function buildPrefixTreeNode(
|
|
|
93
114
|
const searchSchemasMap = new Map<string, Record<string, string>>();
|
|
94
115
|
const trackedIncludes: TrackedInclude[] = [];
|
|
95
116
|
|
|
96
|
-
|
|
117
|
+
RangoContext.run(
|
|
97
118
|
{
|
|
98
119
|
manifest,
|
|
99
120
|
patterns: patternsMap,
|
|
@@ -150,10 +171,7 @@ function buildPrefixTreeNode(
|
|
|
150
171
|
if (prerenderDefs && entry.prerenderDef) {
|
|
151
172
|
prerenderDefs[name] = entry.prerenderDef;
|
|
152
173
|
}
|
|
153
|
-
if (
|
|
154
|
-
passthroughRoutes &&
|
|
155
|
-
entry.prerenderDef?.options?.passthrough === true
|
|
156
|
-
) {
|
|
174
|
+
if (passthroughRoutes && entry.isPassthrough === true) {
|
|
157
175
|
passthroughRoutes.push(name);
|
|
158
176
|
}
|
|
159
177
|
}
|
|
@@ -169,13 +187,9 @@ function buildPrefixTreeNode(
|
|
|
169
187
|
}
|
|
170
188
|
}
|
|
171
189
|
|
|
172
|
-
// Build children from tracked nested includes.
|
|
173
|
-
// Multiple includes can share the same fullPrefix (e.g., include("/", patternsA),
|
|
174
|
-
// include("/", patternsB)). Merge their routes instead of overwriting.
|
|
175
190
|
const children: Record<string, PrefixTreeNode> = {};
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const childNode = buildPrefixTreeNode(
|
|
191
|
+
mergeIncludeNodes(children, trackedIncludes, (include) =>
|
|
192
|
+
buildPrefixTreeNode(
|
|
179
193
|
include.fullPrefix,
|
|
180
194
|
include.namePrefix,
|
|
181
195
|
include.patterns as UrlPatterns<any>,
|
|
@@ -189,16 +203,8 @@ function buildPrefixTreeNode(
|
|
|
189
203
|
passthroughRoutes,
|
|
190
204
|
responseTypeRoutes,
|
|
191
205
|
routeSearchSchemas,
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
const existing = children[include.fullPrefix];
|
|
195
|
-
if (existing) {
|
|
196
|
-
existing.routes.push(...childNode.routes);
|
|
197
|
-
Object.assign(existing.children, childNode.children);
|
|
198
|
-
} else {
|
|
199
|
-
children[include.fullPrefix] = childNode;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
206
|
+
),
|
|
207
|
+
);
|
|
202
208
|
|
|
203
209
|
// Remove from visited so sibling branches can reuse the same patterns
|
|
204
210
|
// without false circular-include detection. Only ancestors in the current
|
|
@@ -285,6 +291,17 @@ export function generateManifest<TEnv>(
|
|
|
285
291
|
export function generateManifestFull<TEnv>(
|
|
286
292
|
urlpatterns: UrlPatterns<TEnv, any>,
|
|
287
293
|
mountIndex: number = 0,
|
|
294
|
+
options?: {
|
|
295
|
+
urlPrefix?: string;
|
|
296
|
+
/**
|
|
297
|
+
* Called once per `"use client"` component registered as an
|
|
298
|
+
* errorBoundary/notFoundBoundary fallback, with its client-reference key
|
|
299
|
+
* (`$$id`). Lets the build collect fallback module ids for dedicated
|
|
300
|
+
* chunking without exposing the otherwise-discarded EntryData tree. The
|
|
301
|
+
* EntryData map built below is local; this is the only seam that surfaces it.
|
|
302
|
+
*/
|
|
303
|
+
collectClientFallbackRef?: (refKey: string) => void;
|
|
304
|
+
},
|
|
288
305
|
): FullManifest {
|
|
289
306
|
const routeManifest: Record<string, string> = {};
|
|
290
307
|
const routeAncestry: Record<string, string[]> = {};
|
|
@@ -298,7 +315,7 @@ export function generateManifestFull<TEnv>(
|
|
|
298
315
|
const searchSchemasMap = new Map<string, Record<string, string>>();
|
|
299
316
|
const trackedIncludes: TrackedInclude[] = [];
|
|
300
317
|
|
|
301
|
-
|
|
318
|
+
RangoContext.run(
|
|
302
319
|
{
|
|
303
320
|
manifest,
|
|
304
321
|
patterns: patternsMap,
|
|
@@ -310,6 +327,8 @@ export function generateManifestFull<TEnv>(
|
|
|
310
327
|
counters: {},
|
|
311
328
|
mountIndex,
|
|
312
329
|
trackedIncludes, // Enable include tracking
|
|
330
|
+
// basename sets the initial URL prefix for all path() registrations
|
|
331
|
+
...(options?.urlPrefix ? { urlPrefix: options.urlPrefix } : {}),
|
|
313
332
|
},
|
|
314
333
|
() => {
|
|
315
334
|
const helpers = createRouteHelpers();
|
|
@@ -320,6 +339,22 @@ export function generateManifestFull<TEnv>(
|
|
|
320
339
|
},
|
|
321
340
|
);
|
|
322
341
|
|
|
342
|
+
// Surface the "use client" components registered as error/notFound fallbacks
|
|
343
|
+
// (route-tree errorBoundary()/notFoundBoundary() helpers, stored on EntryData).
|
|
344
|
+
// The boundary may be a handler function and/or wrap the client boundary in
|
|
345
|
+
// server providers, so walk the whole tree (see collectFallbackClientRefs).
|
|
346
|
+
if (options?.collectClientFallbackRef) {
|
|
347
|
+
const report = options.collectClientFallbackRef;
|
|
348
|
+
const collect = (boundary: unknown[] | undefined) => {
|
|
349
|
+
for (const item of boundary ?? [])
|
|
350
|
+
collectFallbackClientRefs(item, report);
|
|
351
|
+
};
|
|
352
|
+
for (const entry of manifest.values()) {
|
|
353
|
+
collect(entry.errorBoundary);
|
|
354
|
+
collect(entry.notFoundBoundary);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
323
358
|
// Collect root-level routes and trailing slash config
|
|
324
359
|
const routeTrailingSlash: Record<string, string> = {};
|
|
325
360
|
for (const [name, pattern] of patternsMap.entries()) {
|
|
@@ -347,7 +382,7 @@ export function generateManifestFull<TEnv>(
|
|
|
347
382
|
if (entry.prerenderDef) {
|
|
348
383
|
prerenderDefs[name] = entry.prerenderDef;
|
|
349
384
|
}
|
|
350
|
-
if (entry.
|
|
385
|
+
if (entry.isPassthrough === true) {
|
|
351
386
|
passthroughRoutes.push(name);
|
|
352
387
|
}
|
|
353
388
|
}
|
|
@@ -356,12 +391,10 @@ export function generateManifestFull<TEnv>(
|
|
|
356
391
|
}
|
|
357
392
|
}
|
|
358
393
|
|
|
359
|
-
//
|
|
360
|
-
// Multiple includes can share the same fullPrefix (e.g., include("/", patternsA),
|
|
361
|
-
// include("/", patternsB)). Merge their routes instead of overwriting.
|
|
394
|
+
// Shared visited set for cycle detection across all root-level includes.
|
|
362
395
|
const visited = new Set<unknown>();
|
|
363
|
-
|
|
364
|
-
|
|
396
|
+
mergeIncludeNodes(prefixTree, trackedIncludes, (include) =>
|
|
397
|
+
buildPrefixTreeNode(
|
|
365
398
|
include.fullPrefix,
|
|
366
399
|
include.namePrefix,
|
|
367
400
|
include.patterns as UrlPatterns<any>,
|
|
@@ -375,16 +408,8 @@ export function generateManifestFull<TEnv>(
|
|
|
375
408
|
passthroughRoutes,
|
|
376
409
|
responseTypeRoutes,
|
|
377
410
|
routeSearchSchemas,
|
|
378
|
-
)
|
|
379
|
-
|
|
380
|
-
const existing = prefixTree[include.fullPrefix];
|
|
381
|
-
if (existing) {
|
|
382
|
-
existing.routes.push(...node.routes);
|
|
383
|
-
Object.assign(existing.children, node.children);
|
|
384
|
-
} else {
|
|
385
|
-
prefixTree[include.fullPrefix] = node;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
411
|
+
),
|
|
412
|
+
);
|
|
388
413
|
|
|
389
414
|
return {
|
|
390
415
|
prefixTree,
|
|
@@ -24,7 +24,9 @@ export {
|
|
|
24
24
|
extractIncludesWithDiagnostics,
|
|
25
25
|
} from "./route-types/include-resolution.js";
|
|
26
26
|
export {
|
|
27
|
-
|
|
27
|
+
extractUrlsFromRouter,
|
|
28
|
+
extractBasenameFromRouter,
|
|
29
|
+
type UrlsExtractionResult,
|
|
28
30
|
buildCombinedRouteMapForRouterFile,
|
|
29
31
|
detectUnresolvableIncludes,
|
|
30
32
|
detectUnresolvableIncludesForUrlsFile,
|
|
@@ -32,5 +34,7 @@ export {
|
|
|
32
34
|
formatNestedRouterConflictError,
|
|
33
35
|
findRouterFiles,
|
|
34
36
|
writeCombinedRouteTypes,
|
|
37
|
+
genFileTsPath,
|
|
38
|
+
resolveSearchSchemas,
|
|
35
39
|
} from "./route-types/router-processing.js";
|
|
36
40
|
export { findUrlsVariableNames } from "./route-types/per-module-writer.js";
|
package/src/build/index.ts
CHANGED
|
@@ -22,8 +22,14 @@ export {
|
|
|
22
22
|
type GeneratedManifest,
|
|
23
23
|
} from "./generate-manifest.js";
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
// buildRouteTrie / buildPerRouterTrie / collectFallbackClientRefs and the
|
|
26
|
+
// TrieNode/TrieLeaf types are NOT exported here: they are build-pipeline
|
|
27
|
+
// internals, not public API. Their only build-time consumer (the Vite
|
|
28
|
+
// discovery pass) imports them directly from source via a relative path
|
|
29
|
+
// (vite/discovery/discover-routers.ts), and the runtime RSC realm likewise
|
|
30
|
+
// imports route-trie.js directly (rsc/manifest-init.ts). Keeping them off the
|
|
31
|
+
// public ./build surface (#569 decision 6) means consumers can't mistake them
|
|
32
|
+
// for intended API. generateManifest* / route-types / hashParams stay public.
|
|
27
33
|
export {
|
|
28
34
|
writePerModuleRouteTypes,
|
|
29
35
|
extractRoutesFromSource,
|