@rangojs/router 0.0.0-experimental.bd6e11bc → 0.0.0-experimental.bdaf10aa
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +8 -4
- package/README.md +296 -887
- package/dist/bin/rango.js +459 -91
- package/dist/testing/vitest.js +36 -2
- package/dist/vite/index.js +1708 -414
- package/package.json +35 -10
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +82 -5
- package/skills/bundle-analysis/SKILL.md +2 -2
- package/skills/cache-guide/SKILL.md +14 -9
- package/skills/caching/SKILL.md +221 -12
- package/skills/catalog.json +271 -0
- package/skills/comparison/SKILL.md +50 -0
- package/skills/comparison/agents/openai.yaml +4 -0
- package/skills/comparison/references/framework-comparison.md +837 -0
- package/skills/composability/SKILL.md +83 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +5 -3
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +11 -3
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +9 -9
- package/skills/hooks/SKILL.md +73 -900
- package/skills/hooks/data.md +273 -0
- package/skills/hooks/handle-and-actions.md +103 -0
- package/skills/hooks/navigation.md +110 -0
- package/skills/hooks/outlets.md +41 -0
- package/skills/hooks/state.md +228 -0
- package/skills/hooks/urls.md +135 -0
- package/skills/host-router/SKILL.md +84 -7
- package/skills/i18n/SKILL.md +1 -1
- package/skills/intercept/SKILL.md +51 -17
- package/skills/layout/SKILL.md +38 -16
- package/skills/links/SKILL.md +1 -1
- package/skills/loader/SKILL.md +48 -20
- package/skills/middleware/SKILL.md +11 -5
- package/skills/migrate-nextjs/SKILL.md +203 -20
- package/skills/migrate-react-router/SKILL.md +59 -675
- package/skills/migrate-react-router/cloudflare-workers.md +129 -0
- package/skills/migrate-react-router/component-migration.md +196 -0
- package/skills/migrate-react-router/data-and-actions.md +225 -0
- package/skills/migrate-react-router/route-mapping.md +271 -0
- package/skills/mime-routes/SKILL.md +3 -3
- package/skills/observability/SKILL.md +70 -5
- package/skills/parallel/SKILL.md +32 -8
- package/skills/ppr/SKILL.md +622 -0
- package/skills/prerender/SKILL.md +59 -28
- package/skills/rango/SKILL.md +124 -50
- package/skills/response-routes/SKILL.md +78 -46
- package/skills/route/SKILL.md +85 -6
- package/skills/router-setup/SKILL.md +41 -6
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +28 -3
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +1 -1
- package/skills/tailwind/SKILL.md +28 -4
- package/skills/testing/SKILL.md +68 -654
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +131 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +85 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/theme/SKILL.md +1 -1
- package/skills/typesafety/SKILL.md +45 -918
- package/skills/typesafety/env-and-bindings.md +254 -0
- package/skills/typesafety/generated-files-and-cli.md +335 -0
- package/skills/typesafety/params-and-search.md +153 -0
- package/skills/typesafety/route-types.md +209 -0
- package/skills/use-cache/SKILL.md +47 -17
- package/skills/vercel/SKILL.md +128 -0
- package/skills/view-transitions/SKILL.md +44 -1
- package/src/__augment-tests__/augmented.check.ts +2 -3
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +1 -1
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +14 -27
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +178 -100
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +81 -68
- package/src/browser/navigation-client.ts +115 -70
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +153 -88
- package/src/browser/navigation-transaction.ts +0 -32
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +157 -144
- package/src/browser/prefetch/cache.ts +148 -81
- package/src/browser/prefetch/fetch.ts +231 -51
- package/src/browser/prefetch/queue.ts +25 -7
- package/src/browser/rango-state.ts +157 -115
- package/src/browser/react/Link.tsx +40 -7
- package/src/browser/react/NavigationProvider.tsx +140 -99
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +17 -2
- package/src/browser/react/index.ts +0 -51
- package/src/browser/react/location-state-shared.ts +14 -15
- package/src/browser/react/location-state.ts +0 -1
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +0 -5
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +10 -5
- package/src/browser/react/use-params.ts +0 -2
- package/src/browser/react/use-router.ts +6 -4
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +0 -13
- package/src/browser/response-adapter.ts +74 -8
- package/src/browser/rsc-router.tsx +97 -22
- package/src/browser/scroll-restoration.ts +15 -8
- package/src/browser/segment-reconciler.ts +31 -21
- package/src/browser/server-action-bridge.ts +216 -38
- package/src/browser/types.ts +94 -22
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/generate-manifest.ts +155 -131
- package/src/build/generate-route-types.ts +1 -1
- package/src/build/index.ts +11 -5
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +152 -22
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +12 -1
- package/src/build/route-types/include-resolution.ts +455 -61
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +15 -2
- package/src/build/route-types/router-processing.ts +77 -41
- package/src/build/route-types/source-scan.ts +105 -7
- package/src/build/runtime-discovery.ts +4 -1
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +58 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +454 -101
- package/src/cache/cache-scope.ts +159 -54
- package/src/cache/cache-tag.ts +149 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +2170 -377
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +126 -41
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +243 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +13 -21
- package/src/cache/shell-snapshot.ts +417 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +194 -99
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1132 -0
- package/src/client.rsc.tsx +39 -22
- package/src/client.tsx +28 -58
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +108 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +13 -1
- package/src/decode-loader-results.ts +18 -2
- package/src/defer.ts +185 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +0 -3
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +57 -40
- package/src/handles/MetaTags.tsx +24 -53
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +35 -8
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +14 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +13 -22
- package/src/host/index.ts +7 -0
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +1 -65
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +6 -2
- package/src/href-client.ts +7 -12
- package/src/index.rsc.ts +88 -8
- package/src/index.ts +90 -16
- package/src/internal-debug.ts +11 -10
- package/src/loader.rsc.ts +19 -9
- package/src/loader.ts +12 -4
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +75 -7
- package/src/redirect-origin.ts +114 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +25 -0
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +13 -49
- package/src/route-definition/dsl-helpers.ts +60 -53
- package/src/route-definition/helper-factories.ts +0 -2
- package/src/route-definition/helpers-types.ts +46 -46
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +6 -1
- package/src/route-definition/use-item-types.ts +3 -6
- package/src/route-map-builder.ts +41 -20
- package/src/route-types.ts +0 -5
- package/src/router/content-negotiation.ts +58 -23
- package/src/router/error-handling.ts +44 -17
- package/src/router/find-match.ts +129 -30
- package/src/router/handler-context.ts +6 -1
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +35 -2
- package/src/router/lazy-includes.ts +79 -56
- package/src/router/loader-resolution.ts +151 -73
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +74 -40
- package/src/router/match-api.ts +76 -52
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +181 -178
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +115 -194
- package/src/router/match-middleware/cache-store.ts +61 -50
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +36 -67
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +0 -116
- package/src/router/middleware.ts +231 -120
- package/src/router/navigation-snapshot.ts +7 -56
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +99 -152
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +111 -66
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +47 -42
- package/src/router/revalidation.ts +75 -81
- package/src/router/route-snapshot.ts +14 -3
- package/src/router/router-context.ts +6 -29
- package/src/router/router-interfaces.ts +70 -8
- package/src/router/router-options.ts +126 -4
- package/src/router/segment-resolution/fresh.ts +104 -80
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +155 -39
- package/src/router/segment-resolution/loader-mask.ts +60 -0
- package/src/router/segment-resolution/loader-snapshot.ts +259 -0
- package/src/router/segment-resolution/mask-nested.ts +83 -0
- package/src/router/segment-resolution/revalidation.ts +215 -304
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +35 -15
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +54 -35
- package/src/router/telemetry-otel.ts +160 -200
- package/src/router/telemetry.ts +9 -23
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +215 -0
- package/src/router/trie-matching.ts +171 -64
- package/src/router/types.ts +1 -63
- package/src/router/url-params.ts +13 -5
- package/src/router.ts +119 -48
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +1 -0
- package/src/rsc/handler.ts +267 -152
- package/src/rsc/helpers.ts +78 -4
- package/src/rsc/index.ts +1 -4
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +29 -42
- package/src/rsc/nonce.ts +10 -1
- package/src/rsc/origin-guard.ts +11 -15
- package/src/rsc/progressive-enhancement.ts +120 -13
- package/src/rsc/redirect-guard.ts +100 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +58 -141
- package/src/rsc/rsc-rendering.ts +492 -49
- package/src/rsc/runtime-warnings.ts +14 -0
- package/src/rsc/server-action.ts +268 -82
- package/src/rsc/shell-capture.ts +1190 -0
- package/src/rsc/shell-serve.ts +181 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +45 -3
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +31 -26
- package/src/segment-loader-promise.ts +49 -4
- package/src/segment-system.tsx +260 -95
- package/src/server/context.ts +99 -9
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +125 -2
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +379 -138
- package/src/ssr/index.tsx +491 -182
- package/src/ssr/inject-rsc-eager.ts +167 -0
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +44 -48
- package/src/testing/collect-handle.ts +14 -31
- package/src/testing/dispatch.ts +533 -160
- package/src/testing/e2e/fixture.ts +45 -11
- package/src/testing/e2e/index.ts +1 -22
- package/src/testing/e2e/matchers.ts +0 -16
- package/src/testing/e2e/parity.ts +85 -4
- package/src/testing/e2e/server.ts +12 -0
- package/src/testing/flight-matchers.ts +7 -14
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +36 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +30 -0
- package/src/testing/flight.ts +145 -70
- package/src/testing/generated-routes.ts +26 -50
- package/src/testing/index.ts +18 -19
- package/src/testing/internal/context.ts +184 -68
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +357 -0
- package/src/testing/render-route.tsx +134 -115
- package/src/testing/run-loader.ts +140 -51
- package/src/testing/run-middleware.ts +59 -33
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +1 -1
- package/src/testing/vitest-stubs/cloudflare-workers.ts +1 -1
- package/src/testing/vitest.ts +138 -16
- package/src/theme/ThemeProvider.tsx +56 -84
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +15 -15
- package/src/types/handler-context.ts +45 -15
- package/src/types/index.ts +2 -10
- package/src/types/loader-types.ts +6 -3
- package/src/types/request-scope.ts +8 -22
- package/src/types/route-config.ts +20 -52
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +100 -13
- package/src/urls/include-helper.ts +10 -12
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +2 -8
- package/src/urls/path-helper-types.ts +52 -14
- package/src/urls/path-helper.ts +5 -54
- package/src/urls/pattern-types.ts +36 -0
- package/src/urls/type-extraction.ts +76 -42
- package/src/urls/urls-function.ts +0 -14
- package/src/use-loader.tsx +0 -186
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/discovery/bundle-postprocess.ts +2 -1
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +34 -43
- package/src/vite/discovery/discovery-errors.ts +61 -0
- package/src/vite/discovery/prerender-collection.ts +33 -46
- package/src/vite/discovery/state.ts +12 -1
- package/src/vite/discovery/virtual-module-codegen.ts +1 -11
- package/src/vite/index.ts +9 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +143 -10
- package/src/vite/plugins/cjs-to-esm.ts +8 -12
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +0 -10
- package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
- package/src/vite/plugins/expose-action-id.ts +2 -73
- package/src/vite/plugins/expose-id-utils.ts +85 -56
- package/src/vite/plugins/expose-ids/export-analysis.ts +30 -43
- package/src/vite/plugins/expose-ids/handler-transform.ts +5 -31
- package/src/vite/plugins/expose-ids/loader-transform.ts +12 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +98 -26
- package/src/vite/plugins/expose-internal-ids.ts +10 -1
- package/src/vite/plugins/performance-tracks.ts +0 -3
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +21 -46
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +22 -27
- package/src/vite/plugins/version-plugin.ts +6 -66
- package/src/vite/plugins/virtual-entries.ts +137 -26
- package/src/vite/rango.ts +146 -135
- package/src/vite/router-discovery.ts +189 -48
- package/src/vite/utils/ast-handler-extract.ts +11 -20
- package/src/vite/utils/bundle-analysis.ts +6 -13
- package/src/vite/utils/client-chunks.ts +0 -6
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +0 -22
- package/src/vite/utils/manifest-utils.ts +4 -75
- package/src/vite/utils/package-resolution.ts +1 -73
- package/src/vite/utils/prerender-utils.ts +71 -44
- package/src/vite/utils/shared-utils.ts +55 -37
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import type { RevalidationTraceEntry } from "./logging.js";
|
|
15
15
|
import { _getRequestContext } from "../server/request-context.js";
|
|
16
16
|
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
17
|
+
import { paramsEqual } from "./params-util.js";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Resolve a server-action reference's stable id, mirroring how the action
|
|
@@ -32,16 +33,21 @@ function resolveActionRefId(ref: unknown): string | undefined {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
|
-
* Build the `isAction()` helper bound to the current action's id.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
36
|
+
* Build the `isAction()` helper bound to the current action's id. Called with no
|
|
37
|
+
* arguments it answers "is this request an action at all?" (any action) — `true`
|
|
38
|
+
* during action handling, `false` on plain navigation. Called with one or more
|
|
39
|
+
* action references it narrows to those: a single imported action, several
|
|
40
|
+
* (variadic), or any export of a namespace import (`import * as Mod`). Returns
|
|
41
|
+
* `false` when there is no action (plain navigation) or nothing matches.
|
|
39
42
|
*/
|
|
40
43
|
function makeIsAction(
|
|
41
44
|
currentActionId: string | undefined,
|
|
42
45
|
): (...actions: ActionRef[]) => boolean {
|
|
43
46
|
return (...actions: ActionRef[]): boolean => {
|
|
44
47
|
if (!currentActionId) return false;
|
|
48
|
+
// Bare isAction(): an action is in flight (currentActionId is set) and the
|
|
49
|
+
// caller did not narrow to a specific action, so this is "any action".
|
|
50
|
+
if (actions.length === 0) return true;
|
|
45
51
|
for (const action of actions) {
|
|
46
52
|
if (typeof action === "function") {
|
|
47
53
|
if (resolveActionRefId(action) === currentActionId) return true;
|
|
@@ -56,22 +62,6 @@ function makeIsAction(
|
|
|
56
62
|
};
|
|
57
63
|
}
|
|
58
64
|
|
|
59
|
-
function paramsEqual(
|
|
60
|
-
a: Record<string, string>,
|
|
61
|
-
b: Record<string, string>,
|
|
62
|
-
): boolean {
|
|
63
|
-
if (a === b) return true;
|
|
64
|
-
|
|
65
|
-
const keysA = Object.keys(a);
|
|
66
|
-
if (keysA.length !== Object.keys(b).length) return false;
|
|
67
|
-
|
|
68
|
-
for (const key of keysA) {
|
|
69
|
-
if (a[key] !== b[key]) return false;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
65
|
/**
|
|
76
66
|
* Options for revalidation evaluation
|
|
77
67
|
*/
|
|
@@ -136,8 +126,6 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
136
126
|
const paramsChanged = !paramsEqual(nextParams, prevParams);
|
|
137
127
|
const searchChanged = prevUrl.search !== nextUrl.search;
|
|
138
128
|
|
|
139
|
-
// Trace helper: push a structured entry to the request-scoped trace buffer.
|
|
140
|
-
// Guarded by isTraceActive() so object construction is skipped in production.
|
|
141
129
|
function pushTrace(
|
|
142
130
|
defaultVal: boolean,
|
|
143
131
|
finalVal: boolean,
|
|
@@ -156,43 +144,28 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
156
144
|
});
|
|
157
145
|
}
|
|
158
146
|
|
|
159
|
-
// Calculate default revalidation based on segment type and request method
|
|
160
147
|
let defaultShouldRevalidate: boolean;
|
|
161
148
|
let defaultReason: string;
|
|
162
149
|
|
|
163
150
|
if (defaultOverride) {
|
|
164
|
-
// Caller injected the seed (e.g. parallel slot not in clientSegmentIds).
|
|
165
|
-
// Skip the type-derived heuristic — caller knows better in this context.
|
|
166
151
|
defaultShouldRevalidate = defaultOverride.value;
|
|
167
152
|
defaultReason = defaultOverride.reason;
|
|
168
153
|
} else if (request.method === "POST") {
|
|
169
|
-
// Actions: revalidate segments that belong to the route, skip parent chain
|
|
170
154
|
if (segment.type === "route") {
|
|
171
|
-
// Route segment always revalidates on actions
|
|
172
155
|
defaultShouldRevalidate = true;
|
|
173
156
|
defaultReason = "action:route-segment";
|
|
174
157
|
} else if (segment.type === "loader") {
|
|
175
|
-
// Loaders always revalidate on actions - they often contain action-sensitive data
|
|
176
|
-
// (e.g., cart count after add-to-cart action)
|
|
177
158
|
defaultShouldRevalidate = true;
|
|
178
159
|
defaultReason = "action:loader-segment";
|
|
179
160
|
} else if (segment.belongsToRoute) {
|
|
180
|
-
// Segment belongs to route (orphan layouts/parallels) - revalidate
|
|
181
161
|
defaultShouldRevalidate = true;
|
|
182
162
|
defaultReason = "action:belongs-to-route";
|
|
183
163
|
} else {
|
|
184
|
-
// Parent chain segment (shared layouts/parallels) - don't revalidate
|
|
185
164
|
defaultShouldRevalidate = false;
|
|
186
165
|
defaultReason = "action:parent-chain-skip";
|
|
187
166
|
}
|
|
188
167
|
} else {
|
|
189
|
-
// Navigation (GET): Conservative defaults to minimize unnecessary revalidations
|
|
190
|
-
// Only the route segment revalidates by default - all others require explicit opt-in
|
|
191
|
-
|
|
192
168
|
if (segment.type === "route") {
|
|
193
|
-
// Route segments revalidate when path params OR search params change.
|
|
194
|
-
// Search params (e.g., ?page=2&sort=price) are server-parsed via ctx.search,
|
|
195
|
-
// so the handler must re-execute to produce updated content.
|
|
196
169
|
const routeChanged = paramsChanged || searchChanged;
|
|
197
170
|
defaultShouldRevalidate = routeChanged;
|
|
198
171
|
defaultReason = paramsChanged
|
|
@@ -208,8 +181,6 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
208
181
|
});
|
|
209
182
|
}
|
|
210
183
|
} else if (segment.belongsToRoute && (paramsChanged || searchChanged)) {
|
|
211
|
-
// Children of the route path (loaders, orphan layouts/parallels)
|
|
212
|
-
// revalidate when path params or search params change
|
|
213
184
|
defaultShouldRevalidate = true;
|
|
214
185
|
defaultReason = paramsChanged
|
|
215
186
|
? "nav:route-child-params-changed"
|
|
@@ -221,9 +192,6 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
221
192
|
searchChanged,
|
|
222
193
|
});
|
|
223
194
|
} else {
|
|
224
|
-
// Parent layouts and parallels default to no revalidation
|
|
225
|
-
// Cannot assume these segments depend on params without explicit declaration
|
|
226
|
-
// Use custom revalidation functions to opt-in when needed
|
|
227
195
|
defaultShouldRevalidate = false;
|
|
228
196
|
defaultReason = "nav:non-route-skip";
|
|
229
197
|
debugLog("revalidation", "non-route segment skipped by default", {
|
|
@@ -233,7 +201,6 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
233
201
|
}
|
|
234
202
|
}
|
|
235
203
|
|
|
236
|
-
// No custom revalidations defined - return default behavior without prev segment
|
|
237
204
|
if (revalidations.length === 0) {
|
|
238
205
|
if (defaultShouldRevalidate) {
|
|
239
206
|
debugLog("revalidation", "default revalidate=true", {
|
|
@@ -250,14 +217,10 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
250
217
|
return defaultShouldRevalidate;
|
|
251
218
|
}
|
|
252
219
|
|
|
253
|
-
// Custom revalidations exist - may need full prev segment
|
|
254
|
-
// Lazy load prev segment only if getPrevSegment provided
|
|
255
220
|
const prevSegment = getPrevSegment ? await getPrevSegment() : null;
|
|
256
221
|
|
|
257
|
-
// Execute revalidation functions with soft/hard decision pattern
|
|
258
222
|
let currentSuggestion = defaultShouldRevalidate;
|
|
259
223
|
|
|
260
|
-
// Compute public route names (filtered: undefined for auto-generated routes)
|
|
261
224
|
const toRouteName =
|
|
262
225
|
routeKey && !isAutoGeneratedRouteName(routeKey) ? routeKey : undefined;
|
|
263
226
|
const reqCtx = _getRequestContext();
|
|
@@ -268,37 +231,73 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
268
231
|
: undefined;
|
|
269
232
|
|
|
270
233
|
for (const { name, fn } of revalidations) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
234
|
+
let result: any;
|
|
235
|
+
try {
|
|
236
|
+
result = fn({
|
|
237
|
+
currentParams: prevSegment?.params || prevParams, // Use segment params if available, else route params
|
|
238
|
+
currentUrl: prevUrl,
|
|
239
|
+
nextParams,
|
|
240
|
+
nextUrl,
|
|
241
|
+
defaultShouldRevalidate: currentSuggestion,
|
|
242
|
+
context,
|
|
243
|
+
// Segment metadata (which segment is being evaluated)
|
|
244
|
+
segmentType: segment.type,
|
|
245
|
+
layoutName: segment.layoutName,
|
|
246
|
+
slotName: segment.slot,
|
|
247
|
+
// Action context (only populated when triggered by server action)
|
|
248
|
+
actionId: actionContext?.actionId,
|
|
249
|
+
isAction: makeIsAction(actionContext?.actionId),
|
|
250
|
+
actionUrl: actionContext?.actionUrl,
|
|
251
|
+
actionResult: actionContext?.actionResult,
|
|
252
|
+
formData: actionContext?.formData,
|
|
253
|
+
method: request.method,
|
|
254
|
+
routeName: toRouteName,
|
|
255
|
+
fromRouteName,
|
|
256
|
+
toRouteName,
|
|
257
|
+
stale,
|
|
258
|
+
});
|
|
259
|
+
} catch (error) {
|
|
260
|
+
// A thrown Response is control flow (e.g. `throw redirect(...)`), not a
|
|
261
|
+
// failure: re-throw it so the handler chokepoint (match-handlers.ts)
|
|
262
|
+
// turns it into the intended redirect/response. This mirrors how that
|
|
263
|
+
// catch special-cases `error instanceof Response`.
|
|
264
|
+
if (error instanceof Response) throw error;
|
|
265
|
+
// Fail open for genuine errors: a buggy user revalidate fn must not
|
|
266
|
+
// collapse the whole entry's loader batch into a failed partial render.
|
|
267
|
+
// Mirror the dynamic-tags fail-open in cache/cache-policy.ts: log and
|
|
268
|
+
// defer to the current default decision, leaving currentSuggestion
|
|
269
|
+
// unchanged. TODO: route through callOnError(phase "revalidation") once
|
|
270
|
+
// evaluateRevalidation is given the onError seam (today the error only
|
|
271
|
+
// reaches onError via the entry-collapse path in match-handlers.ts).
|
|
272
|
+
console.error(
|
|
273
|
+
`[revalidate] "${name}" threw for segment "${segment.id}"; using default decision:`,
|
|
274
|
+
error,
|
|
275
|
+
);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// The revalidate fn contract (handler-context.ts) is SYNCHRONOUS: it must
|
|
280
|
+
// return a boolean, a { defaultShouldRevalidate } object, or null/undefined.
|
|
281
|
+
// A Promise-returning (async) fn matches none of the decision branches below
|
|
282
|
+
// and silently falls through keeping the current default — a hard-to-find
|
|
283
|
+
// misuse. We do NOT await it (that would change the sync contract); instead
|
|
284
|
+
// we surface it as a dev-mode warning so the silent drop is diagnosable.
|
|
285
|
+
// Mirrors defer.ts: gated to dev, stripped from production builds.
|
|
286
|
+
if (
|
|
287
|
+
process.env.NODE_ENV !== "production" &&
|
|
288
|
+
result != null &&
|
|
289
|
+
typeof (result as { then?: unknown }).then === "function"
|
|
290
|
+
) {
|
|
291
|
+
console.warn(
|
|
292
|
+
`[rango] revalidate fn "${name}" returned a Promise; revalidate ` +
|
|
293
|
+
`functions must be synchronous (return a boolean, ` +
|
|
294
|
+
`{ defaultShouldRevalidate }, or null/undefined). The async result ` +
|
|
295
|
+
`was IGNORED and the default (${currentSuggestion}) was kept. ` +
|
|
296
|
+
`Move async work into a loader instead.`,
|
|
297
|
+
);
|
|
298
|
+
}
|
|
295
299
|
|
|
296
|
-
// Check return type:
|
|
297
|
-
// - boolean: hard decision, short-circuit immediately
|
|
298
|
-
// - { defaultShouldRevalidate: boolean }: soft decision, update suggestion and continue
|
|
299
|
-
// - null/undefined: use default behavior (equivalent to returning { defaultShouldRevalidate })
|
|
300
300
|
if (typeof result === "boolean") {
|
|
301
|
-
// Hard decision - short-circuit
|
|
302
301
|
debugLog("revalidation", "hard decision", {
|
|
303
302
|
segmentId: segment.id,
|
|
304
303
|
revalidator: name,
|
|
@@ -311,7 +310,6 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
311
310
|
typeof result === "object" &&
|
|
312
311
|
"defaultShouldRevalidate" in result
|
|
313
312
|
) {
|
|
314
|
-
// Soft decision - update suggestion and continue
|
|
315
313
|
currentSuggestion = result.defaultShouldRevalidate;
|
|
316
314
|
debugLog("revalidation", "soft decision", {
|
|
317
315
|
segmentId: segment.id,
|
|
@@ -319,18 +317,14 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
319
317
|
revalidate: currentSuggestion,
|
|
320
318
|
});
|
|
321
319
|
} else if (result === null || result === undefined) {
|
|
322
|
-
// Defer to default - equivalent to { defaultShouldRevalidate: currentSuggestion }
|
|
323
|
-
// This means "I don't care, use whatever the default is"
|
|
324
320
|
debugLog("revalidation", "deferred to current default", {
|
|
325
321
|
segmentId: segment.id,
|
|
326
322
|
revalidator: name,
|
|
327
323
|
revalidate: currentSuggestion,
|
|
328
324
|
});
|
|
329
|
-
// currentSuggestion stays the same, continue to next function
|
|
330
325
|
}
|
|
331
326
|
}
|
|
332
327
|
|
|
333
|
-
// All revalidators completed - use final suggestion
|
|
334
328
|
debugLog("revalidation", "final decision", {
|
|
335
329
|
segmentId: segment.id,
|
|
336
330
|
revalidate: currentSuggestion,
|
|
@@ -48,6 +48,15 @@ export interface RouteSnapshot<TEnv = any> {
|
|
|
48
48
|
isPassthrough: boolean;
|
|
49
49
|
/** Response type for non-RSC routes (e.g. "application/json") */
|
|
50
50
|
responseType?: string;
|
|
51
|
+
/**
|
|
52
|
+
* The isSSR flag the manifest was resolved with. Recorded so consumers can
|
|
53
|
+
* decide whether the snapshot is reusable: a snapshot resolved with isSSR
|
|
54
|
+
* differs (loading({ ssr: false }) entries) and is cached under a different
|
|
55
|
+
* manifest partition, so the full (document) path only reuses an isSSR:true
|
|
56
|
+
* snapshot and the partial path only reuses a non-isSSR one. Undefined on
|
|
57
|
+
* inline snapshots (redirect / not-found) that are never reused.
|
|
58
|
+
*/
|
|
59
|
+
isSSR?: boolean;
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
export type ResolveRouteResult<TEnv = any> =
|
|
@@ -56,7 +65,9 @@ export type ResolveRouteResult<TEnv = any> =
|
|
|
56
65
|
| null;
|
|
57
66
|
|
|
58
67
|
export interface ResolveRouteDeps<TEnv = any> {
|
|
59
|
-
findMatch: (
|
|
68
|
+
findMatch: (
|
|
69
|
+
pathname: string,
|
|
70
|
+
) => RouteMatchResult<TEnv> | null | Promise<RouteMatchResult<TEnv> | null>;
|
|
60
71
|
metricsStore?: MetricsStore;
|
|
61
72
|
isSSR?: boolean;
|
|
62
73
|
/**
|
|
@@ -105,7 +116,7 @@ export async function resolveRoute<TEnv = any>(
|
|
|
105
116
|
|
|
106
117
|
const routeMatchStart =
|
|
107
118
|
metricsStore && !skipRouteMatchMetric ? performance.now() : 0;
|
|
108
|
-
const matched = deps.findMatch(pathname);
|
|
119
|
+
const matched = await deps.findMatch(pathname);
|
|
109
120
|
if (metricsStore && !skipRouteMatchMetric) {
|
|
110
121
|
metricsStore.metrics.push({
|
|
111
122
|
label: "route-matching",
|
|
@@ -175,6 +186,7 @@ export async function resolveRoute<TEnv = any>(
|
|
|
175
186
|
cacheScope,
|
|
176
187
|
isPassthrough,
|
|
177
188
|
responseType,
|
|
189
|
+
isSSR,
|
|
178
190
|
},
|
|
179
191
|
};
|
|
180
192
|
}
|
|
@@ -230,7 +242,6 @@ export function createRouteSnapshot<TEnv = any>(
|
|
|
230
242
|
entry: {} as any,
|
|
231
243
|
routeKey: "test",
|
|
232
244
|
params: {},
|
|
233
|
-
optionalParams: new Set(),
|
|
234
245
|
} as RouteMatchResult<TEnv>,
|
|
235
246
|
manifestEntry: { type: "route", shortCode: "R0", parent: null } as any,
|
|
236
247
|
entries: [],
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
} from "../types.js";
|
|
20
20
|
import type { RouteMatchResult } from "./pattern-matching.js";
|
|
21
21
|
import type { TelemetrySink } from "./telemetry.js";
|
|
22
|
+
import type { ResolveSegmentOptions } from "./segment-resolution.js";
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Revalidation context passed to segment resolution
|
|
@@ -54,10 +55,10 @@ export interface InterceptResult {
|
|
|
54
55
|
* Instead of passing 20+ parameters, middleware calls getRouterContext() to access them.
|
|
55
56
|
*/
|
|
56
57
|
export interface RouterContext<TEnv = any> {
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
findMatch: (
|
|
59
|
+
pathname: string,
|
|
60
|
+
) => RouteMatchResult | null | Promise<RouteMatchResult | null>;
|
|
59
61
|
|
|
60
|
-
// Manifest loading
|
|
61
62
|
loadManifest: (
|
|
62
63
|
entry: any,
|
|
63
64
|
routeKey: string,
|
|
@@ -66,10 +67,8 @@ export interface RouterContext<TEnv = any> {
|
|
|
66
67
|
isSSR?: boolean,
|
|
67
68
|
) => Promise<EntryData>;
|
|
68
69
|
|
|
69
|
-
// Entry traversal
|
|
70
70
|
traverseBack: (entry: EntryData) => Generator<EntryData>;
|
|
71
71
|
|
|
72
|
-
// Handler context creation
|
|
73
72
|
createHandlerContext: (
|
|
74
73
|
params: Record<string, string>,
|
|
75
74
|
request: Request,
|
|
@@ -83,7 +82,6 @@ export interface RouterContext<TEnv = any> {
|
|
|
83
82
|
isPassthroughRoute?: boolean,
|
|
84
83
|
) => HandlerContext<any, TEnv>;
|
|
85
84
|
|
|
86
|
-
// Loader setup
|
|
87
85
|
setupLoaderAccess: (
|
|
88
86
|
ctx: HandlerContext<any, TEnv>,
|
|
89
87
|
loaderPromises: Map<string, Promise<any>>,
|
|
@@ -94,7 +92,6 @@ export interface RouterContext<TEnv = any> {
|
|
|
94
92
|
loaderPromises: Map<string, Promise<any>>,
|
|
95
93
|
) => void;
|
|
96
94
|
|
|
97
|
-
// Context access
|
|
98
95
|
getContext: () => {
|
|
99
96
|
getOrCreateStore: (key: string) => any;
|
|
100
97
|
runWithStore: <T>(
|
|
@@ -105,16 +102,13 @@ export interface RouterContext<TEnv = any> {
|
|
|
105
102
|
) => T;
|
|
106
103
|
};
|
|
107
104
|
|
|
108
|
-
// Metrics
|
|
109
105
|
getMetricsStore: () => MetricsStore | undefined;
|
|
110
106
|
|
|
111
|
-
// Cache
|
|
112
107
|
createCacheScope: (
|
|
113
108
|
cacheConfig: any,
|
|
114
109
|
parent: CacheScope | null,
|
|
115
110
|
) => CacheScope | null;
|
|
116
111
|
|
|
117
|
-
// Intercept detection
|
|
118
112
|
findInterceptForRoute: (
|
|
119
113
|
routeKey: string,
|
|
120
114
|
parentEntry: EntryData | null,
|
|
@@ -122,7 +116,6 @@ export interface RouterContext<TEnv = any> {
|
|
|
122
116
|
isAction: boolean,
|
|
123
117
|
) => InterceptResult | null;
|
|
124
118
|
|
|
125
|
-
// Segment resolution (with revalidation)
|
|
126
119
|
resolveAllSegmentsWithRevalidation: (
|
|
127
120
|
entries: EntryData[],
|
|
128
121
|
routeKey: string,
|
|
@@ -133,7 +126,6 @@ export interface RouterContext<TEnv = any> {
|
|
|
133
126
|
request: Request,
|
|
134
127
|
prevUrl: URL,
|
|
135
128
|
nextUrl: URL,
|
|
136
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
137
129
|
actionContext: any | undefined,
|
|
138
130
|
interceptResult: InterceptResult | null,
|
|
139
131
|
localRouteName: string,
|
|
@@ -164,14 +156,13 @@ export interface RouterContext<TEnv = any> {
|
|
|
164
156
|
handlerContext: HandlerContext<any, TEnv>,
|
|
165
157
|
belongsToRoute: boolean,
|
|
166
158
|
revalidationContext?: RevalidationContext,
|
|
159
|
+
options?: { skipMiddleware?: boolean },
|
|
167
160
|
) => Promise<ResolvedSegment[]>;
|
|
168
161
|
|
|
169
|
-
// Collect with markers
|
|
170
162
|
collectWithMarkers?: <T>(
|
|
171
163
|
gen: AsyncGenerator<T | { __type: "id"; id: string }>,
|
|
172
164
|
) => Promise<{ items: T[]; matchedIds: string[] }>;
|
|
173
165
|
|
|
174
|
-
// Revalidation evaluation
|
|
175
166
|
evaluateRevalidation: (params: {
|
|
176
167
|
segment: ResolvedSegment;
|
|
177
168
|
prevParams: Record<string, string>;
|
|
@@ -195,7 +186,6 @@ export interface RouterContext<TEnv = any> {
|
|
|
195
186
|
| "intercept-loader";
|
|
196
187
|
}) => Promise<boolean>;
|
|
197
188
|
|
|
198
|
-
// Request context
|
|
199
189
|
getRequestContext: () =>
|
|
200
190
|
| {
|
|
201
191
|
waitUntil: (fn: () => Promise<void>) => void;
|
|
@@ -203,17 +193,15 @@ export interface RouterContext<TEnv = any> {
|
|
|
203
193
|
}
|
|
204
194
|
| undefined;
|
|
205
195
|
|
|
206
|
-
// Simple segment resolution (without revalidation - for full match)
|
|
207
196
|
resolveAllSegments: (
|
|
208
197
|
entries: EntryData[],
|
|
209
198
|
routeKey: string,
|
|
210
199
|
params: Record<string, string>,
|
|
211
200
|
handlerContext: HandlerContext<any, TEnv>,
|
|
212
201
|
loaderPromises: Map<string, Promise<any>>,
|
|
213
|
-
options?:
|
|
202
|
+
options?: ResolveSegmentOptions,
|
|
214
203
|
) => Promise<ResolvedSegment[]>;
|
|
215
204
|
|
|
216
|
-
// Generator-based simple resolution
|
|
217
205
|
resolveAllSegmentsGenerator?: (
|
|
218
206
|
entries: EntryData[],
|
|
219
207
|
routeKey: string,
|
|
@@ -222,21 +210,17 @@ export interface RouterContext<TEnv = any> {
|
|
|
222
210
|
loaderPromises: Map<string, Promise<any>>,
|
|
223
211
|
) => AsyncGenerator<ResolvedSegment | { __type: "id"; id: string }>;
|
|
224
212
|
|
|
225
|
-
// Collect segments from generator
|
|
226
213
|
collectSegmentsFromGenerator?: <T>(
|
|
227
214
|
gen: AsyncGenerator<T | { __type: "id"; id: string }>,
|
|
228
215
|
) => Promise<T[]>;
|
|
229
216
|
|
|
230
|
-
// Handle store
|
|
231
217
|
createHandleStore: () => any;
|
|
232
218
|
|
|
233
|
-
// Loaders-only resolution (for full match cache hit - no revalidation)
|
|
234
219
|
resolveLoadersOnly?: (
|
|
235
220
|
entries: EntryData[],
|
|
236
221
|
handlerContext: HandlerContext<any, TEnv>,
|
|
237
222
|
) => Promise<ResolvedSegment[]>;
|
|
238
223
|
|
|
239
|
-
// Loaders-only resolution (for cache hit scenarios)
|
|
240
224
|
resolveLoadersOnlyWithRevalidation?: (
|
|
241
225
|
entries: EntryData[],
|
|
242
226
|
handlerContext: HandlerContext<any, TEnv>,
|
|
@@ -258,10 +242,8 @@ export interface RouterContext<TEnv = any> {
|
|
|
258
242
|
// Telemetry sink (optional, no-op when undefined)
|
|
259
243
|
telemetry?: TelemetrySink;
|
|
260
244
|
|
|
261
|
-
// Request ID for telemetry span correlation (set per-request in match handlers)
|
|
262
245
|
requestId?: string;
|
|
263
246
|
|
|
264
|
-
// Intercept loaders only (for cache hit + intercept scenarios)
|
|
265
247
|
resolveInterceptLoadersOnly?: (
|
|
266
248
|
intercept: InterceptEntry,
|
|
267
249
|
entry: EntryData,
|
|
@@ -284,7 +266,6 @@ export interface RouterContext<TEnv = any> {
|
|
|
284
266
|
} | null>;
|
|
285
267
|
}
|
|
286
268
|
|
|
287
|
-
// AsyncLocalStorage instance for router context
|
|
288
269
|
const routerContext = new AsyncLocalStorage<RouterContext<any>>();
|
|
289
270
|
|
|
290
271
|
/**
|
|
@@ -308,10 +289,6 @@ export function getRouterContext<TEnv = any>(): RouterContext<TEnv> {
|
|
|
308
289
|
*
|
|
309
290
|
* All async code within fn() can call getRouterContext() to access router closures.
|
|
310
291
|
* This works across async boundaries thanks to AsyncLocalStorage.
|
|
311
|
-
*
|
|
312
|
-
* @param deps Router dependencies to make available
|
|
313
|
-
* @param fn Function to run with dependencies available
|
|
314
|
-
* @returns Result of fn()
|
|
315
292
|
*/
|
|
316
293
|
export function runWithRouterContext<T, TEnv = any>(
|
|
317
294
|
deps: RouterContext<TEnv>,
|
|
@@ -7,15 +7,16 @@ import type { EntryData } from "../server/context";
|
|
|
7
7
|
import type { ErrorInfo, MatchResult } from "../types";
|
|
8
8
|
import type { NonceProvider } from "../rsc/types.js";
|
|
9
9
|
import type { ExecutionContext } from "../server/request-context.js";
|
|
10
|
-
import type {
|
|
11
|
-
SerializedSegmentData,
|
|
12
|
-
SegmentHandleData,
|
|
13
|
-
} from "../cache/types.js";
|
|
10
|
+
import type { SerializedSegmentData } from "../cache/types.js";
|
|
14
11
|
import type { MiddlewareEntry, MiddlewareFn } from "./middleware.js";
|
|
12
|
+
import type { RouteMatchResult } from "./pattern-matching.js";
|
|
13
|
+
import type { ExtractParams } from "../types/route-config.js";
|
|
15
14
|
import { RSC_ROUTER_BRAND } from "./router-registry.js";
|
|
16
15
|
import type { RangoOptions, RootLayoutProps } from "./router-options.js";
|
|
17
16
|
import type { DefaultVars } from "../types/global-namespace.js";
|
|
18
17
|
import type { ResolvedTimeouts, OnTimeoutCallback } from "./timeout.js";
|
|
18
|
+
import type { ResolvedTracing } from "./tracing.js";
|
|
19
|
+
import type { TelemetrySink } from "./telemetry.js";
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Options passed to router.fetch(), router.match(), and other request entrypoints.
|
|
@@ -108,9 +109,14 @@ export interface Rango<
|
|
|
108
109
|
* createRouter({ document: RootLayout })
|
|
109
110
|
* .use(loggerMiddleware) // All routes
|
|
110
111
|
* .use("/api/*", rateLimiter) // Pattern match
|
|
112
|
+
* .use("/users/:id", (ctx) => {}) // ctx.params.id is typed
|
|
111
113
|
* .routes(urlpatterns)
|
|
112
114
|
* ```
|
|
113
115
|
*/
|
|
116
|
+
use<Pattern extends string>(
|
|
117
|
+
pattern: Pattern,
|
|
118
|
+
middleware: MiddlewareFn<TEnv, ExtractParams<Pattern>>,
|
|
119
|
+
): Rango<TEnv, TRoutes>;
|
|
114
120
|
use(
|
|
115
121
|
patternOrMiddleware: string | MiddlewareFn<TEnv>,
|
|
116
122
|
middleware?: MiddlewareFn<TEnv>,
|
|
@@ -228,6 +234,10 @@ export interface RangoInternal<
|
|
|
228
234
|
/**
|
|
229
235
|
* Add global middleware that runs on all routes
|
|
230
236
|
*/
|
|
237
|
+
use<Pattern extends string>(
|
|
238
|
+
pattern: Pattern,
|
|
239
|
+
middleware: MiddlewareFn<TEnv, ExtractParams<Pattern>>,
|
|
240
|
+
): Rango<TEnv, TRoutes>;
|
|
231
241
|
use(
|
|
232
242
|
patternOrMiddleware: string | MiddlewareFn<TEnv>,
|
|
233
243
|
middleware?: MiddlewareFn<TEnv>,
|
|
@@ -293,6 +303,27 @@ export interface RangoInternal<
|
|
|
293
303
|
*/
|
|
294
304
|
readonly prefetchCacheTTL: number;
|
|
295
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Maximum number of decoded prefetch payloads the client keeps in its
|
|
308
|
+
* in-memory prefetch cache (FIFO eviction at capacity). Shipped to the
|
|
309
|
+
* client in payload metadata. Derived from prefetchCacheSize.
|
|
310
|
+
*/
|
|
311
|
+
readonly prefetchCacheSize: number;
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Maximum number of speculative prefetch requests the client runs
|
|
315
|
+
* concurrently. Shipped to the client in payload metadata. Derived from
|
|
316
|
+
* prefetchConcurrency.
|
|
317
|
+
*/
|
|
318
|
+
readonly prefetchConcurrency: number;
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Resolved rango state cookie name (`{prefix}_{routerId}`), composed once at
|
|
322
|
+
* router init and shipped to the client in payload metadata. The server-side
|
|
323
|
+
* cookie writer reads it from here; the client reads it from metadata.
|
|
324
|
+
*/
|
|
325
|
+
readonly resolvedStateCookieName: string;
|
|
326
|
+
|
|
296
327
|
/**
|
|
297
328
|
* Whether connection warmup is enabled.
|
|
298
329
|
* When true, the client sends HEAD /?_rsc_warmup after idle periods
|
|
@@ -300,12 +331,34 @@ export interface RangoInternal<
|
|
|
300
331
|
*/
|
|
301
332
|
readonly warmupEnabled: boolean;
|
|
302
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Whether the client hydrates inside React.StrictMode. Resolved from
|
|
336
|
+
* createRouter({ strictMode }) (default true) and shipped to the client in
|
|
337
|
+
* the initial payload metadata.
|
|
338
|
+
*/
|
|
339
|
+
readonly strictMode: boolean;
|
|
340
|
+
|
|
303
341
|
/**
|
|
304
342
|
* Whether router-wide performance debugging is enabled.
|
|
305
343
|
* Used by the request handler to create metrics before middleware runs.
|
|
306
344
|
*/
|
|
307
345
|
readonly debugPerformance?: boolean;
|
|
308
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Resolved platform phase-span tracing (Cloudflare custom spans or OTel), or
|
|
349
|
+
* undefined when off. Threaded onto the request context and read at each
|
|
350
|
+
* traced phase.
|
|
351
|
+
*/
|
|
352
|
+
readonly tracing?: ResolvedTracing;
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Raw telemetry sink from RangoOptions, exposed so handler-level emitters
|
|
356
|
+
* (rsc/handler.ts timeout/origin/late-handle) can emit WITHOUT the
|
|
357
|
+
* RouterContext ALS, which only match()/matchPartial() enter. See
|
|
358
|
+
* observeEvent's emitter list in router/instrument.ts.
|
|
359
|
+
*/
|
|
360
|
+
readonly telemetry?: TelemetrySink;
|
|
361
|
+
|
|
309
362
|
/**
|
|
310
363
|
* Whether ?__debug_manifest is allowed in production.
|
|
311
364
|
* Always enabled in development.
|
|
@@ -395,11 +448,13 @@ export interface RangoInternal<
|
|
|
395
448
|
devMode?: boolean,
|
|
396
449
|
): Promise<{
|
|
397
450
|
segments: SerializedSegmentData[];
|
|
398
|
-
|
|
451
|
+
/** RSC-encoded handle map ("" when none) — see handle-snapshot.ts. */
|
|
452
|
+
handles: string;
|
|
399
453
|
routeName: string;
|
|
400
454
|
params: Record<string, string>;
|
|
401
455
|
interceptSegments?: SerializedSegmentData[];
|
|
402
|
-
|
|
456
|
+
/** RSC-encoded MERGED (main + intercept) handle map for the intercept artifact. */
|
|
457
|
+
interceptHandles?: string;
|
|
403
458
|
passthrough?: true;
|
|
404
459
|
} | null>;
|
|
405
460
|
|
|
@@ -413,7 +468,7 @@ export interface RangoInternal<
|
|
|
413
468
|
routeName?: string,
|
|
414
469
|
buildEnv?: any,
|
|
415
470
|
devMode?: boolean,
|
|
416
|
-
): Promise<{ encoded: string; handles:
|
|
471
|
+
): Promise<{ encoded: string; handles: string } | null>;
|
|
417
472
|
|
|
418
473
|
/**
|
|
419
474
|
* Preview match - returns route middleware without segment resolution.
|
|
@@ -471,7 +526,14 @@ export interface RangoInternal<
|
|
|
471
526
|
* Used by classifyRequest() for request classification without
|
|
472
527
|
* entering the full match pipeline.
|
|
473
528
|
*/
|
|
474
|
-
|
|
529
|
+
// Async since a lazy async include (`() => import()`) must resolve before its
|
|
530
|
+
// routes can match. Typed (not `any`) so a consumer doing
|
|
531
|
+
// `const m = router.findMatch(p); if (!m) ...; m.entry` gets a compile error
|
|
532
|
+
// (m is a Promise) instead of the silent always-truthy bug.
|
|
533
|
+
findMatch(
|
|
534
|
+
pathname: string,
|
|
535
|
+
metricsStore?: any,
|
|
536
|
+
): Promise<RouteMatchResult<TEnv> | null>;
|
|
475
537
|
|
|
476
538
|
/**
|
|
477
539
|
* Debug utility to serialize the manifest for inspection
|