@rangojs/router 0.0.0-experimental.b9cb8739 → 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 +303 -741
- package/dist/bin/rango.js +730 -184
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +4344 -1335
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +86 -15
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +85 -6
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +251 -24
- package/skills/caching/SKILL.md +375 -17
- 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 +110 -4
- 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 +87 -56
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +73 -691
- 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 +129 -27
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +94 -18
- package/skills/layout/SKILL.md +62 -19
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +302 -54
- package/skills/middleware/SKILL.md +59 -16
- package/skills/migrate-nextjs/SKILL.md +745 -0
- package/skills/migrate-react-router/SKILL.md +153 -0
- 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 +29 -2
- package/skills/observability/SKILL.md +202 -0
- package/skills/parallel/SKILL.md +225 -10
- package/skills/ppr/SKILL.md +622 -0
- package/skills/prerender/SKILL.md +178 -124
- package/skills/rango/SKILL.md +318 -24
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +138 -49
- package/skills/route/SKILL.md +172 -9
- package/skills/router-setup/SKILL.md +131 -11
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +776 -0
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +28 -4
- 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 +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 -616
- 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 +74 -15
- package/skills/vercel/SKILL.md +128 -0
- 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 +257 -158
- 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/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +132 -33
- package/src/browser/navigation-client.ts +218 -68
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +203 -80
- package/src/browser/navigation-transaction.ts +18 -66
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +241 -127
- package/src/browser/prefetch/cache.ts +271 -44
- package/src/browser/prefetch/fetch.ts +367 -40
- package/src/browser/prefetch/queue.ts +144 -23
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +121 -16
- package/src/browser/react/NavigationProvider.tsx +240 -122
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +66 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +178 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +23 -69
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +32 -7
- package/src/browser/react/use-params.ts +20 -10
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +46 -11
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +99 -8
- package/src/browser/rsc-router.tsx +272 -80
- package/src/browser/scroll-restoration.ts +56 -22
- package/src/browser/segment-reconciler.ts +44 -7
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +244 -71
- package/src/browser/types.ts +136 -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 +207 -158
- package/src/build/generate-route-types.ts +6 -1
- package/src/build/index.ts +11 -3
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +198 -41
- 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 +464 -63
- 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 +336 -110
- 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 +13 -21
- 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 -97
- package/src/cache/cache-scope.ts +235 -103
- 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 +2446 -170
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +11 -17
- package/src/cache/document-cache.ts +144 -49
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +24 -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/taint.ts +55 -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 +41 -21
- package/src/client.tsx +116 -290
- 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 +84 -2
- package/src/debug.ts +2 -2
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +185 -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 +104 -34
- 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 +9 -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 +100 -13
- package/src/index.ts +143 -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 +215 -86
- 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 +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 +485 -303
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +153 -77
- package/src/route-definition/index.ts +4 -2
- package/src/route-definition/redirect.ts +53 -12
- 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 +48 -21
- 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 +130 -29
- package/src/router/handler-context.ts +83 -39
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +50 -24
- package/src/router/lazy-includes.ts +89 -63
- package/src/router/loader-resolution.ts +286 -56
- package/src/router/logging.ts +5 -8
- package/src/router/manifest.ts +105 -56
- package/src/router/match-api.ts +178 -218
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +211 -165
- package/src/router/match-middleware/background-revalidation.ts +66 -22
- package/src/router/match-middleware/cache-lookup.ts +214 -263
- package/src/router/match-middleware/cache-store.ts +105 -50
- package/src/router/match-middleware/intercept-resolution.ts +8 -28
- package/src/router/match-middleware/segment-resolution.ts +52 -18
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +128 -44
- package/src/router/metrics.ts +5 -34
- package/src/router/middleware-types.ts +13 -142
- package/src/router/middleware.ts +301 -177
- package/src/router/navigation-snapshot.ts +133 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +181 -150
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +203 -58
- package/src/router/preview-match.ts +35 -103
- package/src/router/request-classification.ts +291 -0
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +256 -0
- package/src/router/router-context.ts +11 -29
- package/src/router/router-interfaces.ts +146 -35
- package/src/router/router-options.ts +202 -15
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +301 -78
- package/src/router/segment-resolution/helpers.ts +115 -30
- package/src/router/segment-resolution/loader-cache.ts +156 -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 +477 -385
- 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 +8 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +75 -0
- package/src/router/telemetry-otel.ts +160 -200
- package/src/router/telemetry.ts +105 -20
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +215 -0
- package/src/router/trie-matching.ts +171 -59
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +57 -0
- package/src/router.ts +210 -71
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +682 -508
- 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/nonce.ts +10 -1
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +138 -15
- 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 +99 -189
- package/src/rsc/rsc-rendering.ts +509 -73
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +287 -113
- package/src/rsc/shell-capture.ts +1190 -0
- package/src/rsc/shell-serve.ts +181 -0
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +62 -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 +167 -0
- package/src/segment-system.tsx +449 -132
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +367 -61
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +152 -5
- package/src/server/handle-store.ts +40 -38
- package/src/server/loader-registry.ts +38 -46
- package/src/server/request-context.ts +558 -173
- package/src/ssr/index.tsx +491 -174
- package/src/ssr/inject-rsc-eager.ts +167 -0
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +27 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +46 -0
- package/src/testing/dispatch.ts +813 -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 +199 -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 +584 -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 +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 +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 +20 -52
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +137 -14
- package/src/urls/include-helper.ts +40 -75
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +2 -11
- package/src/urls/path-helper-types.ts +102 -23
- package/src/urls/path-helper.ts +62 -111
- package/src/urls/pattern-types.ts +84 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +98 -154
- package/src/urls/urls-function.ts +1 -19
- package/src/use-loader.tsx +346 -89
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +130 -85
- package/src/vite/discovery/discovery-errors.ts +255 -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 -6
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +15 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +234 -62
- 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 +49 -98
- package/src/vite/plugins/expose-id-utils.ts +96 -51
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
- package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
- package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
- package/src/vite/plugins/expose-internal-ids.ts +553 -317
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +46 -37
- package/src/vite/plugins/virtual-entries.ts +138 -27
- package/src/vite/rango.ts +353 -303
- package/src/vite/router-discovery.ts +1090 -166
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +98 -38
- package/src/vite/utils/shared-utils.ts +144 -44
- 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
|
@@ -33,10 +33,116 @@ import type { ErrorBoundaryHandler, NotFoundBoundaryHandler } from "../types";
|
|
|
33
33
|
import type { MiddlewareFn } from "./middleware.js";
|
|
34
34
|
import {
|
|
35
35
|
type TelemetrySink,
|
|
36
|
+
type CacheSegmentSignal,
|
|
36
37
|
safeEmit,
|
|
37
38
|
resolveSink,
|
|
38
39
|
getRequestId,
|
|
40
|
+
buildCacheSignalSegments,
|
|
39
41
|
} from "./telemetry.js";
|
|
42
|
+
import { _getRequestContext } from "../server/request-context.js";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Per-call telemetry lifecycle emitter for match()/matchPartial(). Each method
|
|
46
|
+
* reproduces the exact event object the two functions used to emit inline and is
|
|
47
|
+
* gated on the same per-call flag (`enabled` = the request's `emitTelemetry`), so
|
|
48
|
+
* a PPR shell-capture run (enabled=false) emits nothing while a foreground run
|
|
49
|
+
* emits byte-identical events. Extracted so the two transactions can't drift;
|
|
50
|
+
* pinned by thrown-response-telemetry.test.ts and
|
|
51
|
+
* shell-capture-telemetry-suppression.test.ts.
|
|
52
|
+
*/
|
|
53
|
+
interface LifecycleEmitter {
|
|
54
|
+
start(): void;
|
|
55
|
+
end(segmentCount: number, cacheHit: boolean, status?: number): void;
|
|
56
|
+
cacheDecision(
|
|
57
|
+
routeKey: string,
|
|
58
|
+
state: {
|
|
59
|
+
cacheHit: boolean;
|
|
60
|
+
cacheSource?: "runtime" | "prerender";
|
|
61
|
+
shouldRevalidate?: boolean;
|
|
62
|
+
},
|
|
63
|
+
segments: CacheSegmentSignal[],
|
|
64
|
+
): void;
|
|
65
|
+
error(error: Error, phase: string): void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function createLifecycleEmitter(args: {
|
|
69
|
+
enabled: boolean;
|
|
70
|
+
sink: TelemetrySink;
|
|
71
|
+
requestId: string | undefined;
|
|
72
|
+
method: string;
|
|
73
|
+
pathname: string;
|
|
74
|
+
transaction: "match" | "matchPartial";
|
|
75
|
+
isPartial: boolean;
|
|
76
|
+
matchStart: number;
|
|
77
|
+
}): LifecycleEmitter {
|
|
78
|
+
return {
|
|
79
|
+
start(): void {
|
|
80
|
+
if (!args.enabled) return;
|
|
81
|
+
safeEmit(args.sink, {
|
|
82
|
+
type: "request.start",
|
|
83
|
+
timestamp: args.matchStart,
|
|
84
|
+
requestId: args.requestId,
|
|
85
|
+
method: args.method,
|
|
86
|
+
pathname: args.pathname,
|
|
87
|
+
transaction: args.transaction,
|
|
88
|
+
isPartial: args.isPartial,
|
|
89
|
+
});
|
|
90
|
+
},
|
|
91
|
+
end(segmentCount: number, cacheHit: boolean, status?: number): void {
|
|
92
|
+
if (!args.enabled) return;
|
|
93
|
+
safeEmit(args.sink, {
|
|
94
|
+
type: "request.end",
|
|
95
|
+
timestamp: performance.now(),
|
|
96
|
+
requestId: args.requestId,
|
|
97
|
+
method: args.method,
|
|
98
|
+
pathname: args.pathname,
|
|
99
|
+
transaction: args.transaction,
|
|
100
|
+
durationMs: performance.now() - args.matchStart,
|
|
101
|
+
segmentCount,
|
|
102
|
+
cacheHit,
|
|
103
|
+
// Only a thrown-Response short-circuit passes a status; a normal render
|
|
104
|
+
// completion omits it (the Response is built after match()).
|
|
105
|
+
...(status !== undefined && { status }),
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
cacheDecision(
|
|
109
|
+
routeKey: string,
|
|
110
|
+
state: {
|
|
111
|
+
cacheHit: boolean;
|
|
112
|
+
cacheSource?: "runtime" | "prerender";
|
|
113
|
+
shouldRevalidate?: boolean;
|
|
114
|
+
},
|
|
115
|
+
segments: CacheSegmentSignal[],
|
|
116
|
+
): void {
|
|
117
|
+
if (!args.enabled) return;
|
|
118
|
+
safeEmit(args.sink, {
|
|
119
|
+
type: "cache.decision",
|
|
120
|
+
timestamp: performance.now(),
|
|
121
|
+
requestId: args.requestId,
|
|
122
|
+
pathname: args.pathname,
|
|
123
|
+
routeKey,
|
|
124
|
+
hit: state.cacheHit,
|
|
125
|
+
shouldRevalidate: !!state.shouldRevalidate,
|
|
126
|
+
source: state.cacheSource,
|
|
127
|
+
segments,
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
error(error: Error, phase: string): void {
|
|
131
|
+
if (!args.enabled) return;
|
|
132
|
+
safeEmit(args.sink, {
|
|
133
|
+
type: "request.error",
|
|
134
|
+
timestamp: performance.now(),
|
|
135
|
+
requestId: args.requestId,
|
|
136
|
+
method: args.method,
|
|
137
|
+
pathname: args.pathname,
|
|
138
|
+
transaction: args.transaction,
|
|
139
|
+
error,
|
|
140
|
+
phase,
|
|
141
|
+
durationMs: performance.now() - args.matchStart,
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
40
146
|
|
|
41
147
|
export interface MatchHandlerDeps<TEnv = any> {
|
|
42
148
|
buildRouterContext: () => RouterContext<TEnv>;
|
|
@@ -51,6 +157,12 @@ export interface MatchHandlerDeps<TEnv = any> {
|
|
|
51
157
|
isAction: boolean,
|
|
52
158
|
) => { intercept: InterceptEntry; entry: EntryData } | null;
|
|
53
159
|
telemetry?: TelemetrySink;
|
|
160
|
+
/**
|
|
161
|
+
* DEVELOPMENT/TEST ONLY gate for the X-Rango-Cache debug header. When true,
|
|
162
|
+
* match/matchPartial stash a coarse route-level cache signal on the request
|
|
163
|
+
* context for the response-finalization path to emit. Default off.
|
|
164
|
+
*/
|
|
165
|
+
cacheSignalEnabled?: boolean;
|
|
54
166
|
}
|
|
55
167
|
|
|
56
168
|
export interface MatchHandlers<TEnv = any> {
|
|
@@ -80,20 +192,6 @@ export interface MatchHandlers<TEnv = any> {
|
|
|
80
192
|
negotiated?: boolean;
|
|
81
193
|
manifestEntry?: EntryData;
|
|
82
194
|
} | null>;
|
|
83
|
-
createMatchContextForFull: (
|
|
84
|
-
request: Request,
|
|
85
|
-
env: TEnv,
|
|
86
|
-
) => Promise<MatchContext<TEnv> | { type: "redirect"; redirectUrl: string }>;
|
|
87
|
-
createMatchContextForPartial: (
|
|
88
|
-
request: Request,
|
|
89
|
-
env: TEnv,
|
|
90
|
-
actionContext?: {
|
|
91
|
-
actionId?: string;
|
|
92
|
-
actionUrl?: URL;
|
|
93
|
-
actionResult?: any;
|
|
94
|
-
formData?: FormData;
|
|
95
|
-
},
|
|
96
|
-
) => Promise<MatchContext<TEnv> | null>;
|
|
97
195
|
}
|
|
98
196
|
|
|
99
197
|
/**
|
|
@@ -113,6 +211,20 @@ export function createMatchHandlers<TEnv = any>(
|
|
|
113
211
|
} = deps;
|
|
114
212
|
const hasTelemetry = !!deps.telemetry;
|
|
115
213
|
const telemetry = resolveSink(deps.telemetry);
|
|
214
|
+
const cacheSignalEnabled = !!deps.cacheSignalEnabled;
|
|
215
|
+
const buildSignal = (
|
|
216
|
+
routeKey: string,
|
|
217
|
+
state: {
|
|
218
|
+
cacheHit: boolean;
|
|
219
|
+
cacheSource?: "runtime" | "prerender";
|
|
220
|
+
shouldRevalidate?: boolean;
|
|
221
|
+
},
|
|
222
|
+
): CacheSegmentSignal[] => buildCacheSignalSegments(routeKey, state);
|
|
223
|
+
const recordSignalIfEnabled = (segments: CacheSegmentSignal[]): void => {
|
|
224
|
+
if (!cacheSignalEnabled) return;
|
|
225
|
+
const reqCtx = _getRequestContext();
|
|
226
|
+
if (reqCtx) reqCtx._cacheSignal = segments;
|
|
227
|
+
};
|
|
116
228
|
|
|
117
229
|
async function createMatchContextForFull(
|
|
118
230
|
request: Request,
|
|
@@ -145,57 +257,49 @@ export function createMatchHandlers<TEnv = any>(
|
|
|
145
257
|
);
|
|
146
258
|
}
|
|
147
259
|
|
|
148
|
-
/**
|
|
149
|
-
* Match request and return segments (document/SSR requests)
|
|
150
|
-
*
|
|
151
|
-
* Uses generator middleware pipeline for clean separation of concerns:
|
|
152
|
-
* - cache-lookup: Check cache first
|
|
153
|
-
* - segment-resolution: Resolve segments on cache miss
|
|
154
|
-
* - cache-store: Store results in cache
|
|
155
|
-
* - background-revalidation: SWR revalidation
|
|
156
|
-
*/
|
|
157
260
|
async function match(request: Request, env: TEnv): Promise<MatchResult> {
|
|
158
|
-
|
|
261
|
+
// Silence telemetry for the PPR background shell capture: it re-runs match()
|
|
262
|
+
// under a derived request context flagged _shellCaptureRun (shell-capture.ts
|
|
263
|
+
// attemptCapture), re-using the foreground Request — a second request.start/
|
|
264
|
+
// cache.decision/request.end stamped with the same WeakMap-keyed requestId
|
|
265
|
+
// would double-count dashboards. Derived here (inside the capture's active
|
|
266
|
+
// request-context ALS) so the read sees the derived context, not module state.
|
|
267
|
+
const emitTelemetry =
|
|
268
|
+
hasTelemetry && !_getRequestContext()?._shellCaptureRun;
|
|
269
|
+
const requestId = emitTelemetry ? getRequestId(request) : undefined;
|
|
159
270
|
return runWithRouterLogContext({ request, transaction: "match" }, () => {
|
|
160
271
|
const routerCtx = buildRouterContext();
|
|
272
|
+
// Also mute in-pipeline observeEvent emitters (revalidation.decision,
|
|
273
|
+
// cache-lookup's cache.decision) which read routerCtx.telemetry.
|
|
274
|
+
if (!emitTelemetry) routerCtx.telemetry = undefined;
|
|
161
275
|
routerCtx.requestId = requestId;
|
|
162
276
|
return runWithRouterContext(routerCtx, async () =>
|
|
163
277
|
withRouterLogScope("match", async () => {
|
|
164
278
|
const matchStart = performance.now();
|
|
165
|
-
const pathname =
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
279
|
+
const pathname =
|
|
280
|
+
_getRequestContext()?.url?.pathname ??
|
|
281
|
+
new URL(request.url).pathname;
|
|
282
|
+
const emitter = createLifecycleEmitter({
|
|
283
|
+
enabled: emitTelemetry,
|
|
284
|
+
sink: telemetry,
|
|
285
|
+
requestId,
|
|
286
|
+
method: request.method,
|
|
287
|
+
pathname,
|
|
288
|
+
transaction: "match",
|
|
289
|
+
isPartial: false,
|
|
290
|
+
matchStart,
|
|
291
|
+
});
|
|
292
|
+
emitter.start();
|
|
177
293
|
|
|
178
294
|
const result = await createMatchContextForFull(request, env);
|
|
179
295
|
|
|
180
|
-
// Handle redirect case
|
|
181
296
|
if ("type" in result && result.type === "redirect") {
|
|
182
|
-
|
|
183
|
-
safeEmit(telemetry, {
|
|
184
|
-
type: "request.end",
|
|
185
|
-
timestamp: performance.now(),
|
|
186
|
-
requestId,
|
|
187
|
-
method: request.method,
|
|
188
|
-
pathname,
|
|
189
|
-
transaction: "match",
|
|
190
|
-
durationMs: performance.now() - matchStart,
|
|
191
|
-
segmentCount: 0,
|
|
192
|
-
cacheHit: false,
|
|
193
|
-
});
|
|
194
|
-
}
|
|
297
|
+
emitter.end(0, false);
|
|
195
298
|
return {
|
|
196
299
|
segments: [],
|
|
197
300
|
matched: [],
|
|
198
301
|
diff: [],
|
|
302
|
+
resolvedIds: [],
|
|
199
303
|
params: {},
|
|
200
304
|
redirect: result.redirectUrl,
|
|
201
305
|
};
|
|
@@ -207,48 +311,29 @@ export function createMatchHandlers<TEnv = any>(
|
|
|
207
311
|
const state = createPipelineState();
|
|
208
312
|
const pipeline = createMatchPartialPipeline(ctx, state);
|
|
209
313
|
const matchResult = await collectMatchResult(pipeline, ctx, state);
|
|
210
|
-
if (hasTelemetry) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
requestId,
|
|
215
|
-
pathname,
|
|
216
|
-
routeKey: ctx.routeKey,
|
|
217
|
-
hit: state.cacheHit,
|
|
218
|
-
shouldRevalidate: !!state.shouldRevalidate,
|
|
219
|
-
source: state.cacheSource,
|
|
220
|
-
});
|
|
221
|
-
safeEmit(telemetry, {
|
|
222
|
-
type: "request.end",
|
|
223
|
-
timestamp: performance.now(),
|
|
224
|
-
requestId,
|
|
225
|
-
method: request.method,
|
|
226
|
-
pathname,
|
|
227
|
-
transaction: "match",
|
|
228
|
-
durationMs: performance.now() - matchStart,
|
|
229
|
-
segmentCount: matchResult.segments.length,
|
|
230
|
-
cacheHit: state.cacheHit,
|
|
231
|
-
});
|
|
314
|
+
if (hasTelemetry || cacheSignalEnabled) {
|
|
315
|
+
const signalSegments = buildSignal(ctx.routeKey, state);
|
|
316
|
+
recordSignalIfEnabled(signalSegments);
|
|
317
|
+
emitter.cacheDecision(ctx.routeKey, state, signalSegments);
|
|
232
318
|
}
|
|
319
|
+
emitter.end(matchResult.segments.length, state.cacheHit);
|
|
233
320
|
return matchResult;
|
|
234
321
|
} catch (error) {
|
|
235
|
-
if (
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
error: errorObj,
|
|
246
|
-
phase: error instanceof Response ? "redirect" : "routing",
|
|
247
|
-
durationMs: performance.now() - matchStart,
|
|
248
|
-
});
|
|
322
|
+
if (error instanceof Response) {
|
|
323
|
+
// A thrown Response (middleware short-circuit — redirect / auth
|
|
324
|
+
// gate) is a COMPLETED request from the consumer's seat, not an
|
|
325
|
+
// error: emit request.end (the same shape the non-thrown redirect
|
|
326
|
+
// result above already emits), never request.error with a
|
|
327
|
+
// synthetic "[object Response]" error. Rethrow so the caller
|
|
328
|
+
// drives the redirect. Carry the Response's status so a sink can
|
|
329
|
+
// tell a 3xx short-circuit from a 2xx completion.
|
|
330
|
+
emitter.end(0, false, error.status);
|
|
331
|
+
throw error;
|
|
249
332
|
}
|
|
250
|
-
|
|
251
|
-
|
|
333
|
+
emitter.error(
|
|
334
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
335
|
+
"routing",
|
|
336
|
+
);
|
|
252
337
|
callOnError(error, "routing", {
|
|
253
338
|
request,
|
|
254
339
|
url: ctx.url,
|
|
@@ -283,42 +368,39 @@ export function createMatchHandlers<TEnv = any>(
|
|
|
283
368
|
);
|
|
284
369
|
}
|
|
285
370
|
|
|
286
|
-
/**
|
|
287
|
-
* Match partial request with revalidation
|
|
288
|
-
*
|
|
289
|
-
* Uses generator middleware pipeline for clean separation of concerns:
|
|
290
|
-
* - cache-lookup: Check cache first
|
|
291
|
-
* - segment-resolution: Resolve segments on cache miss
|
|
292
|
-
* - intercept-resolution: Handle intercept routes
|
|
293
|
-
* - cache-store: Store results in cache
|
|
294
|
-
* - background-revalidation: SWR revalidation
|
|
295
|
-
*/
|
|
296
371
|
async function matchPartial(
|
|
297
372
|
request: Request,
|
|
298
373
|
context: TEnv,
|
|
299
374
|
actionContext?: ActionContext,
|
|
300
375
|
): Promise<MatchResult | null> {
|
|
301
|
-
|
|
376
|
+
// See match() above: the PPR shell capture re-runs matchPartial() under a
|
|
377
|
+
// _shellCaptureRun context and must stay invisible to the sink.
|
|
378
|
+
const emitTelemetry =
|
|
379
|
+
hasTelemetry && !_getRequestContext()?._shellCaptureRun;
|
|
380
|
+
const partialRequestId = emitTelemetry ? getRequestId(request) : undefined;
|
|
302
381
|
return runWithRouterLogContext(
|
|
303
382
|
{ request, transaction: "matchPartial" },
|
|
304
383
|
() => {
|
|
305
384
|
const routerCtx = buildRouterContext();
|
|
385
|
+
if (!emitTelemetry) routerCtx.telemetry = undefined;
|
|
306
386
|
routerCtx.requestId = partialRequestId;
|
|
307
387
|
return runWithRouterContext(routerCtx, async () =>
|
|
308
388
|
withRouterLogScope("matchPartial", async () => {
|
|
309
389
|
const matchStart = performance.now();
|
|
310
|
-
const pathname =
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
390
|
+
const pathname =
|
|
391
|
+
_getRequestContext()?.url?.pathname ??
|
|
392
|
+
new URL(request.url).pathname;
|
|
393
|
+
const emitter = createLifecycleEmitter({
|
|
394
|
+
enabled: emitTelemetry,
|
|
395
|
+
sink: telemetry,
|
|
396
|
+
requestId: partialRequestId,
|
|
397
|
+
method: request.method,
|
|
398
|
+
pathname,
|
|
399
|
+
transaction: "matchPartial",
|
|
400
|
+
isPartial: true,
|
|
401
|
+
matchStart,
|
|
402
|
+
});
|
|
403
|
+
emitter.start();
|
|
322
404
|
|
|
323
405
|
const ctx = await createMatchContextForPartial(
|
|
324
406
|
request,
|
|
@@ -326,19 +408,7 @@ export function createMatchHandlers<TEnv = any>(
|
|
|
326
408
|
actionContext,
|
|
327
409
|
);
|
|
328
410
|
if (!ctx) {
|
|
329
|
-
|
|
330
|
-
safeEmit(telemetry, {
|
|
331
|
-
type: "request.end",
|
|
332
|
-
timestamp: performance.now(),
|
|
333
|
-
requestId: partialRequestId,
|
|
334
|
-
method: request.method,
|
|
335
|
-
pathname,
|
|
336
|
-
transaction: "matchPartial",
|
|
337
|
-
durationMs: performance.now() - matchStart,
|
|
338
|
-
segmentCount: 0,
|
|
339
|
-
cacheHit: false,
|
|
340
|
-
});
|
|
341
|
-
}
|
|
411
|
+
emitter.end(0, false);
|
|
342
412
|
return null;
|
|
343
413
|
}
|
|
344
414
|
|
|
@@ -362,50 +432,28 @@ export function createMatchHandlers<TEnv = any>(
|
|
|
362
432
|
state,
|
|
363
433
|
);
|
|
364
434
|
flushRevalidationTrace();
|
|
365
|
-
if (hasTelemetry) {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
requestId: partialRequestId,
|
|
370
|
-
pathname,
|
|
371
|
-
routeKey: ctx.routeKey,
|
|
372
|
-
hit: state.cacheHit,
|
|
373
|
-
shouldRevalidate: !!state.shouldRevalidate,
|
|
374
|
-
source: state.cacheSource,
|
|
375
|
-
});
|
|
376
|
-
safeEmit(telemetry, {
|
|
377
|
-
type: "request.end",
|
|
378
|
-
timestamp: performance.now(),
|
|
379
|
-
requestId: partialRequestId,
|
|
380
|
-
method: request.method,
|
|
381
|
-
pathname,
|
|
382
|
-
transaction: "matchPartial",
|
|
383
|
-
durationMs: performance.now() - matchStart,
|
|
384
|
-
segmentCount: matchResult.segments.length,
|
|
385
|
-
cacheHit: state.cacheHit,
|
|
386
|
-
});
|
|
435
|
+
if (hasTelemetry || cacheSignalEnabled) {
|
|
436
|
+
const signalSegments = buildSignal(ctx.routeKey, state);
|
|
437
|
+
recordSignalIfEnabled(signalSegments);
|
|
438
|
+
emitter.cacheDecision(ctx.routeKey, state, signalSegments);
|
|
387
439
|
}
|
|
440
|
+
emitter.end(matchResult.segments.length, state.cacheHit);
|
|
388
441
|
return matchResult;
|
|
389
442
|
} catch (error) {
|
|
390
443
|
flushRevalidationTrace();
|
|
391
|
-
if (
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
method: request.method,
|
|
400
|
-
pathname,
|
|
401
|
-
transaction: "matchPartial",
|
|
402
|
-
error: errorObj,
|
|
403
|
-
phase: error instanceof Response ? "redirect" : phase,
|
|
404
|
-
durationMs: performance.now() - matchStart,
|
|
405
|
-
});
|
|
444
|
+
if (error instanceof Response) {
|
|
445
|
+
// A thrown Response (middleware short-circuit — redirect / auth
|
|
446
|
+
// gate) is a COMPLETED request, not an error: emit request.end
|
|
447
|
+
// (parity with match()), never request.error. Rethrow so the
|
|
448
|
+
// caller drives the redirect. Carry the Response's status so a
|
|
449
|
+
// sink can tell a 3xx short-circuit from a 2xx completion.
|
|
450
|
+
emitter.end(0, false, error.status);
|
|
451
|
+
throw error;
|
|
406
452
|
}
|
|
407
|
-
|
|
408
|
-
|
|
453
|
+
emitter.error(
|
|
454
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
455
|
+
actionContext ? "action" : "revalidation",
|
|
456
|
+
);
|
|
409
457
|
callOnError(error, actionContext ? "action" : "revalidation", {
|
|
410
458
|
request,
|
|
411
459
|
url: ctx.url,
|
|
@@ -434,7 +482,5 @@ export function createMatchHandlers<TEnv = any>(
|
|
|
434
482
|
matchPartial: matchPartial,
|
|
435
483
|
matchError: matchError,
|
|
436
484
|
previewMatch: previewMatch,
|
|
437
|
-
createMatchContextForFull: createMatchContextForFull,
|
|
438
|
-
createMatchContextForPartial: createMatchContextForPartial,
|
|
439
485
|
};
|
|
440
486
|
}
|
|
@@ -103,7 +103,12 @@ import type { ResolvedSegment } from "../../types.js";
|
|
|
103
103
|
import type { MatchContext, MatchPipelineState } from "../match-context.js";
|
|
104
104
|
import { getRouterContext } from "../router-context.js";
|
|
105
105
|
import type { GeneratorMiddleware } from "./cache-lookup.js";
|
|
106
|
-
import { debugLog, debugWarn } from "../logging.js";
|
|
106
|
+
import { debugLog, debugWarn, getOrCreateRequestId } from "../logging.js";
|
|
107
|
+
import { INTERNAL_RANGO_DEBUG } from "../../internal-debug.js";
|
|
108
|
+
import {
|
|
109
|
+
runWithRequestContext,
|
|
110
|
+
type RequestContext,
|
|
111
|
+
} from "../../server/request-context.js";
|
|
107
112
|
|
|
108
113
|
/**
|
|
109
114
|
* Creates background revalidation middleware
|
|
@@ -143,8 +148,19 @@ export function withBackgroundRevalidation<TEnv>(
|
|
|
143
148
|
|
|
144
149
|
const requestCtx = getRequestContext();
|
|
145
150
|
const cacheScope = ctx.cacheScope;
|
|
151
|
+
const reqId = INTERNAL_RANGO_DEBUG
|
|
152
|
+
? getOrCreateRequestId(ctx.request)
|
|
153
|
+
: undefined;
|
|
146
154
|
|
|
147
155
|
requestCtx?.waitUntil(async () => {
|
|
156
|
+
// Prevent background metrics from polluting foreground timeline.
|
|
157
|
+
// The foreground uses its own metricsStore reference directly (via
|
|
158
|
+
// appendMetric), so nulling Store.metrics only affects track() calls
|
|
159
|
+
// inside this background Store.run() scope.
|
|
160
|
+
const savedMetrics = ctx.Store.metrics;
|
|
161
|
+
ctx.Store.metrics = undefined;
|
|
162
|
+
|
|
163
|
+
const start = performance.now();
|
|
148
164
|
debugLog("backgroundRevalidation", "revalidating stale route", {
|
|
149
165
|
pathname: ctx.pathname,
|
|
150
166
|
fullMatch: ctx.isFullMatch,
|
|
@@ -156,8 +172,6 @@ export function withBackgroundRevalidation<TEnv>(
|
|
|
156
172
|
requestCtx._handleStore = createHandleStore();
|
|
157
173
|
|
|
158
174
|
try {
|
|
159
|
-
// Create fresh handler context and loader promises to avoid
|
|
160
|
-
// reusing memoized results from the foreground pass
|
|
161
175
|
const freshHandlerContext = createHandlerContext(
|
|
162
176
|
ctx.matched.params,
|
|
163
177
|
ctx.request,
|
|
@@ -173,29 +187,46 @@ export function withBackgroundRevalidation<TEnv>(
|
|
|
173
187
|
const freshLoaderPromises = new Map<string, Promise<any>>();
|
|
174
188
|
setupLoaderAccess(freshHandlerContext, freshLoaderPromises);
|
|
175
189
|
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
190
|
+
// Re-establish the request-context ALS around the re-render. ctx.Store
|
|
191
|
+
// is a different ALS (DSL build context); on workerd a waitUntil task
|
|
192
|
+
// runs detached from the request's I/O context, so a handler/component
|
|
193
|
+
// that reads the ambient getRequestContext() during this background
|
|
194
|
+
// re-render would otherwise throw "called outside of a request context".
|
|
195
|
+
const freshSegments = await runWithRequestContext(
|
|
196
|
+
requestCtx as RequestContext<TEnv>,
|
|
197
|
+
() =>
|
|
198
|
+
ctx.Store.run(() =>
|
|
199
|
+
resolveAllSegments(
|
|
200
|
+
ctx.entries,
|
|
201
|
+
ctx.routeKey,
|
|
202
|
+
ctx.matched.params,
|
|
203
|
+
freshHandlerContext,
|
|
204
|
+
freshLoaderPromises,
|
|
205
|
+
{ skipLoaders: true },
|
|
206
|
+
),
|
|
207
|
+
),
|
|
186
208
|
);
|
|
187
209
|
|
|
188
|
-
// Also resolve intercept segments fresh if applicable
|
|
189
210
|
let freshInterceptSegments: ResolvedSegment[] = [];
|
|
190
211
|
if (ctx.interceptResult) {
|
|
191
|
-
freshInterceptSegments = await
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
ctx.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
212
|
+
freshInterceptSegments = await runWithRequestContext(
|
|
213
|
+
requestCtx as RequestContext<TEnv>,
|
|
214
|
+
() =>
|
|
215
|
+
ctx.Store.run(() =>
|
|
216
|
+
resolveInterceptEntry(
|
|
217
|
+
ctx.interceptResult!.intercept,
|
|
218
|
+
ctx.interceptResult!.entry,
|
|
219
|
+
ctx.matched.params,
|
|
220
|
+
freshHandlerContext,
|
|
221
|
+
true,
|
|
222
|
+
undefined,
|
|
223
|
+
// Skip intercept middleware: this is a post-response background
|
|
224
|
+
// re-render to refresh a stale cached route. The foreground
|
|
225
|
+
// already ran the middleware; re-running it would double its
|
|
226
|
+
// side effects and a short-circuit Response would abort the write.
|
|
227
|
+
{ skipMiddleware: true },
|
|
228
|
+
),
|
|
229
|
+
),
|
|
199
230
|
);
|
|
200
231
|
}
|
|
201
232
|
|
|
@@ -207,16 +238,29 @@ export function withBackgroundRevalidation<TEnv>(
|
|
|
207
238
|
completeSegments,
|
|
208
239
|
ctx.isIntercept,
|
|
209
240
|
);
|
|
241
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
242
|
+
const dur = performance.now() - start;
|
|
243
|
+
console.log(
|
|
244
|
+
`[RSC Background][req:${reqId}] SWR revalidation ${ctx.pathname} (${dur.toFixed(2)}ms) segments=${completeSegments.length}`,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
210
247
|
debugLog("backgroundRevalidation", "revalidation complete", {
|
|
211
248
|
pathname: ctx.pathname,
|
|
212
249
|
});
|
|
213
250
|
} catch (error) {
|
|
251
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
252
|
+
const dur = performance.now() - start;
|
|
253
|
+
console.log(
|
|
254
|
+
`[RSC Background][req:${reqId}] SWR revalidation ${ctx.pathname} FAILED (${dur.toFixed(2)}ms) error=${String(error)}`,
|
|
255
|
+
);
|
|
256
|
+
}
|
|
214
257
|
debugWarn("backgroundRevalidation", "revalidation failed", {
|
|
215
258
|
pathname: ctx.pathname,
|
|
216
259
|
error: String(error),
|
|
217
260
|
});
|
|
218
261
|
} finally {
|
|
219
262
|
requestCtx._handleStore = originalHandleStore;
|
|
263
|
+
ctx.Store.metrics = savedMetrics;
|
|
220
264
|
}
|
|
221
265
|
});
|
|
222
266
|
};
|