@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
package/src/router/middleware.ts
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
/// <reference types="vite/types/importMeta.d.ts" />
|
|
2
|
-
/**
|
|
3
|
-
* Middleware Execution
|
|
4
|
-
*
|
|
5
|
-
* True middleware that wraps the entire RSC handler.
|
|
6
|
-
* - `await next()` returns actual Response
|
|
7
|
-
* - Can modify response headers
|
|
8
|
-
* - Can catch errors from RSC rendering
|
|
9
|
-
* - Forgiving API: if middleware doesn't return, original response is used
|
|
10
|
-
*/
|
|
11
2
|
|
|
12
3
|
import { contextGet, contextSet } from "../context-var.js";
|
|
4
|
+
import { escapeRegExp } from "../regex-escape.js";
|
|
5
|
+
import { parsePattern as parseRoutePattern } from "./pattern-matching.js";
|
|
13
6
|
import { safeDecodeURIComponent } from "./url-params.js";
|
|
14
7
|
import { fireAndForgetWaitUntil } from "../types/request-scope.js";
|
|
15
8
|
import type {
|
|
@@ -21,28 +14,29 @@ import type {
|
|
|
21
14
|
ResponseHolder,
|
|
22
15
|
} from "./middleware-types.js";
|
|
23
16
|
import { _getRequestContext } from "../server/request-context.js";
|
|
17
|
+
import {
|
|
18
|
+
EXTERNAL_REDIRECT_MARKER,
|
|
19
|
+
isExternalRedirect,
|
|
20
|
+
markExternalRedirect,
|
|
21
|
+
} from "../redirect-origin.js";
|
|
24
22
|
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
25
23
|
import { appendMetric, createMetricsStore } from "./metrics.js";
|
|
24
|
+
import { observePhase, PHASES } from "./instrument.js";
|
|
26
25
|
import { stripInternalParams } from "./handler-context.js";
|
|
27
26
|
import { isWebSocketUpgradeResponse } from "../response-utils.js";
|
|
28
27
|
|
|
29
|
-
// Re-export types
|
|
28
|
+
// Re-export types consumed through this module's path.
|
|
30
29
|
export type {
|
|
31
30
|
CookieOptions,
|
|
32
|
-
CollectedMiddleware,
|
|
33
|
-
MiddlewareCollectableEntry,
|
|
34
31
|
MiddlewareContext,
|
|
35
32
|
MiddlewareEntry,
|
|
36
33
|
MiddlewareFn,
|
|
37
|
-
ResponseHolder,
|
|
38
34
|
} from "./middleware-types.js";
|
|
39
|
-
export { parseCookies, serializeCookie } from "./middleware-cookies.js";
|
|
40
35
|
|
|
41
36
|
const MIDDLEWARE_METRIC_DEPTH = 1;
|
|
42
|
-
/** Ignore post-next() durations below this threshold (measurement noise). */
|
|
43
37
|
const POST_METRIC_MIN_DURATION_MS = 0.01;
|
|
44
38
|
|
|
45
|
-
function
|
|
39
|
+
function getMiddlewareMetricLabel<TEnv>(
|
|
46
40
|
entry: MiddlewareEntry<TEnv>,
|
|
47
41
|
ordinal: number,
|
|
48
42
|
): string {
|
|
@@ -50,24 +44,49 @@ function getMiddlewareMetricBase<TEnv>(
|
|
|
50
44
|
const scope = entry.pattern ?? "*";
|
|
51
45
|
|
|
52
46
|
if (handlerName) {
|
|
53
|
-
return
|
|
47
|
+
return `middleware:${handlerName}@${scope}`;
|
|
54
48
|
}
|
|
55
49
|
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function getMiddlewareMetricLabel<TEnv>(
|
|
60
|
-
entry: MiddlewareEntry<TEnv>,
|
|
61
|
-
ordinal: number,
|
|
62
|
-
): string {
|
|
63
|
-
return `middleware:${getMiddlewareMetricBase(entry, ordinal)}`;
|
|
50
|
+
return `middleware:${scope}#${ordinal + 1}`;
|
|
64
51
|
}
|
|
65
52
|
|
|
66
53
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
54
|
+
* Compile a middleware scope pattern to a regex + param names.
|
|
55
|
+
*
|
|
56
|
+
* Middleware scopes reuse the route pattern parser (`parsePattern` from
|
|
57
|
+
* pattern-matching.ts), so they support the same param forms as routes —
|
|
58
|
+
* optional (`:x?`), constrained (`:x(en|gb)`), and suffix (`:x.html`) — in
|
|
59
|
+
* addition to the trailing-`*` wildcard middleware relies on. Before this
|
|
60
|
+
* unification the middleware-side parser handled only static, bare `:param`,
|
|
61
|
+
* and trailing `*`, so e.g. `router.use("/:locale(en|gb)/*", mw)` silently
|
|
62
|
+
* named the param "locale(en|gb)" and never enforced the constraint.
|
|
63
|
+
*
|
|
64
|
+
* Middleware matching semantics deliberately differ from route matching, so we
|
|
65
|
+
* emit the regex here rather than route through `compilePattern`:
|
|
66
|
+
* - `*` alone matches every path (`/^.*$/`).
|
|
67
|
+
* - A trailing `*` segment is an OPTIONAL subtree match (`(?:/.*)?`): `/admin/*`
|
|
68
|
+
* matches `/admin`, `/admin/`, and `/admin/users`. It contributes no param
|
|
69
|
+
* name (unlike route wildcards, which capture `*`).
|
|
70
|
+
* - A NON-trailing `*` is also OPTIONAL (`(?:/.*)?`), matching zero-or-more
|
|
71
|
+
* intermediate segments: `/a/<star>/b` matches both `/a/b` and `/a/x/b`. This
|
|
72
|
+
* mirrors the pre-unification parser, which compiled every `*` part as
|
|
73
|
+
* optional regardless of position.
|
|
74
|
+
* - A pattern without a trailing `*` tolerates a trailing slash (`/?$`).
|
|
75
|
+
* - Constraints are baked into the regex as an alternation so `matchMiddleware`
|
|
76
|
+
* (a bare `regex.test`) enforces them without extra validation. Constraint
|
|
77
|
+
* values are matched against the raw (still URL-encoded) path segment, which
|
|
78
|
+
* matches the pre-unification middleware behavior (it never decoded for
|
|
79
|
+
* matching); the constraint string is regex-escaped so values like `en.gb`
|
|
80
|
+
* are treated literally.
|
|
81
|
+
*
|
|
82
|
+
* The route segment parser only recognizes `/`-prefixed segments, but the
|
|
83
|
+
* pre-unification middleware parser split on `/` and dropped empty parts, so a
|
|
84
|
+
* leading slash was irrelevant: `use("admin/*")` and `use("/admin/*")` scoped
|
|
85
|
+
* identically. Normalize a non-`*` pattern to have a leading slash before
|
|
86
|
+
* parsing so that behavior is preserved (without it, `parseRoutePattern("admin/*")`
|
|
87
|
+
* drops the static `admin` and the scope explodes to every path).
|
|
69
88
|
*/
|
|
70
|
-
export function
|
|
89
|
+
export function compileMiddlewarePattern(pattern: string): {
|
|
71
90
|
regex: RegExp;
|
|
72
91
|
paramNames: string[];
|
|
73
92
|
} {
|
|
@@ -75,45 +94,56 @@ export function parsePattern(pattern: string): {
|
|
|
75
94
|
return { regex: /^.*$/, paramNames: [] };
|
|
76
95
|
}
|
|
77
96
|
|
|
97
|
+
const normalizedPattern = pattern.startsWith("/") ? pattern : `/${pattern}`;
|
|
98
|
+
const segments = parseRoutePattern(normalizedPattern);
|
|
78
99
|
const paramNames: string[] = [];
|
|
79
100
|
let regexStr = "^";
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
101
|
+
let hasTrailingWildcard = false;
|
|
102
|
+
|
|
103
|
+
for (let i = 0; i < segments.length; i++) {
|
|
104
|
+
const segment = segments[i];
|
|
105
|
+
|
|
106
|
+
if (segment.type === "wildcard") {
|
|
107
|
+
if (segment.value === "*") {
|
|
108
|
+
// Bare `*`: optional subtree match, no capture (parity with the original
|
|
109
|
+
// middleware parser). A trailing `*` matches the subtree; a non-trailing
|
|
110
|
+
// `*` matches zero-or-more intermediate segments, so `/a/<star>/b` still
|
|
111
|
+
// matches `/a/b`.
|
|
112
|
+
regexStr += "(?:/.*)?";
|
|
113
|
+
} else {
|
|
114
|
+
// Named catch-all `:name+` / `:name*`: capture the remainder under the
|
|
115
|
+
// name so a scoping middleware sees `ctx.params.<name>`, and respect the
|
|
116
|
+
// one-or-more arity (`+` must not match the bare prefix), mirroring the
|
|
117
|
+
// route matcher instead of collapsing to the bare-`*` subtree.
|
|
118
|
+
paramNames.push(segment.value);
|
|
119
|
+
regexStr += segment.oneOrMore ? "/(.+)" : "(?:/(.*))?";
|
|
120
|
+
}
|
|
121
|
+
if (i === segments.length - 1) {
|
|
122
|
+
hasTrailingWildcard = true;
|
|
123
|
+
}
|
|
124
|
+
} else if (segment.type === "param") {
|
|
125
|
+
paramNames.push(segment.value);
|
|
126
|
+
const suffixPattern = segment.suffix ? escapeRegExp(segment.suffix) : "";
|
|
127
|
+
const valuePattern = segment.constraint
|
|
128
|
+
? `(${segment.constraint.map(escapeRegExp).join("|")})`
|
|
129
|
+
: "([^/]+)";
|
|
130
|
+
if (segment.optional) {
|
|
131
|
+
regexStr += `(?:/${valuePattern}${suffixPattern})?`;
|
|
132
|
+
} else {
|
|
133
|
+
regexStr += `/${valuePattern}${suffixPattern}`;
|
|
134
|
+
}
|
|
94
135
|
} else {
|
|
95
|
-
//
|
|
96
|
-
regexStr += "/" +
|
|
136
|
+
// Static literal
|
|
137
|
+
regexStr += "/" + escapeRegExp(segment.value);
|
|
97
138
|
}
|
|
98
139
|
}
|
|
99
140
|
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
regexStr += "/?$";
|
|
103
|
-
} else {
|
|
104
|
-
regexStr += "$";
|
|
105
|
-
}
|
|
141
|
+
// Without a trailing `*`, match exactly with an optional trailing slash.
|
|
142
|
+
regexStr += hasTrailingWildcard ? "$" : "/?$";
|
|
106
143
|
|
|
107
144
|
return { regex: new RegExp(regexStr), paramNames };
|
|
108
145
|
}
|
|
109
146
|
|
|
110
|
-
/**
|
|
111
|
-
* Escape special regex characters
|
|
112
|
-
*/
|
|
113
|
-
function escapeRegex(str: string): string {
|
|
114
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
115
|
-
}
|
|
116
|
-
|
|
117
147
|
/**
|
|
118
148
|
* Extract params from a pathname using a pattern's regex and param names.
|
|
119
149
|
*
|
|
@@ -156,7 +186,15 @@ export function createMiddlewareContext<TEnv>(
|
|
|
156
186
|
search?: Record<string, unknown>,
|
|
157
187
|
) => string,
|
|
158
188
|
): MiddlewareContext<TEnv> {
|
|
159
|
-
|
|
189
|
+
// Reuse the request context's clean URL when available. Each middleware still
|
|
190
|
+
// gets its OWN URL clone (a middleware may mutate ctx.url / ctx.searchParams),
|
|
191
|
+
// but cloning the already-clean URL is cheaper than re-parsing + re-stripping
|
|
192
|
+
// request.url per middleware. Fall back to parsing when no ALS context.
|
|
193
|
+
const reqCtx = _getRequestContext();
|
|
194
|
+
const ctxUrl = reqCtx?.url;
|
|
195
|
+
const url = ctxUrl
|
|
196
|
+
? new URL(ctxUrl)
|
|
197
|
+
: stripInternalParams(new URL(request.url));
|
|
160
198
|
|
|
161
199
|
// Track the initial response to detect pre/post-next() phase.
|
|
162
200
|
// Before next(): responseHolder.response === initialResponse (the stub).
|
|
@@ -187,12 +225,11 @@ export function createMiddlewareContext<TEnv>(
|
|
|
187
225
|
return responseHolder.response;
|
|
188
226
|
};
|
|
189
227
|
|
|
190
|
-
//
|
|
228
|
+
// reqCtx captured once above: the request-scoped platform fields
|
|
191
229
|
// (originalUrl, executionContext, waitUntil) are immutable per request,
|
|
192
230
|
// so snapshotting beats re-reading ALS on every access. The lazy getters
|
|
193
231
|
// below (routeName, theme, setTheme) stay lazy because those can change
|
|
194
232
|
// during `await next()`.
|
|
195
|
-
const reqCtx = _getRequestContext();
|
|
196
233
|
return {
|
|
197
234
|
request,
|
|
198
235
|
url,
|
|
@@ -261,9 +298,13 @@ export function createMiddlewareContext<TEnv>(
|
|
|
261
298
|
|
|
262
299
|
reverse:
|
|
263
300
|
reverse ??
|
|
264
|
-
((
|
|
301
|
+
((
|
|
302
|
+
name: string,
|
|
303
|
+
_params?: Record<string, string>,
|
|
304
|
+
_search?: Record<string, unknown>,
|
|
305
|
+
) => {
|
|
265
306
|
throw new Error(
|
|
266
|
-
`ctx.reverse() is not available
|
|
307
|
+
`ctx.reverse(${JSON.stringify(name)}) is not available: no route map is bound to this middleware context.`,
|
|
267
308
|
);
|
|
268
309
|
}),
|
|
269
310
|
|
|
@@ -271,7 +312,10 @@ export function createMiddlewareContext<TEnv>(
|
|
|
271
312
|
const reqCtx = _getRequestContext();
|
|
272
313
|
if (reqCtx) {
|
|
273
314
|
reqCtx._debugPerformance = true;
|
|
274
|
-
reqCtx.
|
|
315
|
+
// Anchor to the true request entry (reqCtx._handlerStart) so phases that
|
|
316
|
+
// began before this opt-in report non-negative offsets; undefined falls
|
|
317
|
+
// back to performance.now() in createMetricsStore.
|
|
318
|
+
reqCtx._metricsStore ??= createMetricsStore(true, reqCtx._handlerStart);
|
|
275
319
|
}
|
|
276
320
|
},
|
|
277
321
|
};
|
|
@@ -297,9 +341,16 @@ export function matchMiddleware<TEnv>(
|
|
|
297
341
|
continue;
|
|
298
342
|
}
|
|
299
343
|
|
|
300
|
-
//
|
|
301
|
-
|
|
302
|
-
|
|
344
|
+
// Run the scope regex ONCE per entry. The old code ran test() then, on a
|
|
345
|
+
// hit, extractParams' match() — a second full pass over the same string for
|
|
346
|
+
// every matching entry on the per-request hot path. The regexes carry no
|
|
347
|
+
// `g` flag, so there is no lastIndex statefulness across this single match.
|
|
348
|
+
const m = pathname.match(entry.regex);
|
|
349
|
+
if (m) {
|
|
350
|
+
const params: Record<string, string> = {};
|
|
351
|
+
for (let i = 0; i < entry.paramNames.length; i++) {
|
|
352
|
+
params[entry.paramNames[i]] = safeDecodeURIComponent(m[i + 1] || "");
|
|
353
|
+
}
|
|
303
354
|
matches.push({ entry, params });
|
|
304
355
|
}
|
|
305
356
|
}
|
|
@@ -316,6 +367,10 @@ function mergeStubHeaders(
|
|
|
316
367
|
stubOverridesNonCookie: boolean,
|
|
317
368
|
): void {
|
|
318
369
|
stub.forEach((value, name) => {
|
|
370
|
+
// The reserved external-redirect marker is internal and never a trust
|
|
371
|
+
// signal; never copy a stub value (e.g. a stray ctx.header() call) onto a
|
|
372
|
+
// browser-facing response. The opt-in is the out-of-band brand.
|
|
373
|
+
if (name.toLowerCase() === EXTERNAL_REDIRECT_MARKER) return;
|
|
319
374
|
if (name.toLowerCase() === "set-cookie") {
|
|
320
375
|
target.append(name, value);
|
|
321
376
|
} else if (stubOverridesNonCookie || !target.has(name)) {
|
|
@@ -341,12 +396,50 @@ function mergeReqCtxStub(
|
|
|
341
396
|
}
|
|
342
397
|
}
|
|
343
398
|
reqCtx.res.headers.forEach((value, name) => {
|
|
399
|
+
// Never propagate the reserved external-redirect marker (see mergeStubHeaders).
|
|
400
|
+
if (name.toLowerCase() === EXTERNAL_REDIRECT_MARKER) return;
|
|
344
401
|
if (name !== "set-cookie" && !target.has(name)) {
|
|
345
402
|
target.set(name, value);
|
|
346
403
|
}
|
|
347
404
|
});
|
|
348
405
|
}
|
|
349
406
|
|
|
407
|
+
// Clone `base` with stub headers merged into a fresh Headers (the clone keeps
|
|
408
|
+
// the body mutable for post-next() modifications). Set-Cookie is always
|
|
409
|
+
// appended; other headers obey stubOverridesNonCookie (see mergeStubHeaders).
|
|
410
|
+
// mergeReqCtx folds in RequestContext stub cookies/headers; the intercept
|
|
411
|
+
// short-circuit path passes false (its reqCtx headers are not merged here),
|
|
412
|
+
// which is the one deliberate divergence between the call sites.
|
|
413
|
+
function mergeResponse(
|
|
414
|
+
base: Response,
|
|
415
|
+
stub: Headers,
|
|
416
|
+
opts: { stubOverridesNonCookie: boolean; mergeReqCtx: boolean },
|
|
417
|
+
): Response {
|
|
418
|
+
const mergedHeaders = new Headers(base.headers);
|
|
419
|
+
// The reserved external-redirect marker is never a trust signal and must never
|
|
420
|
+
// reach the browser. The guard strips it on 3xx redirects; strip it here too so
|
|
421
|
+
// a forged value cannot ride a non-3xx middleware response (which the 3xx-only
|
|
422
|
+
// guard would not touch) to the client. The opt-in is the out-of-band brand.
|
|
423
|
+
mergedHeaders.delete(EXTERNAL_REDIRECT_MARKER);
|
|
424
|
+
mergeStubHeaders(mergedHeaders, stub, opts.stubOverridesNonCookie);
|
|
425
|
+
if (opts.mergeReqCtx) {
|
|
426
|
+
mergeReqCtxStub(mergedHeaders, _getRequestContext());
|
|
427
|
+
}
|
|
428
|
+
const merged = new Response(base.body, {
|
|
429
|
+
status: base.status,
|
|
430
|
+
statusText: base.statusText,
|
|
431
|
+
headers: mergedHeaders,
|
|
432
|
+
});
|
|
433
|
+
// Transfer the out-of-band external-redirect brand across this rebuild: a
|
|
434
|
+
// middleware short-circuit `return redirect(url, { external: true })` reaches
|
|
435
|
+
// the open-redirect guard only after this merge, and the brand lives on the
|
|
436
|
+
// Response object, not in its headers.
|
|
437
|
+
if (isExternalRedirect(base)) {
|
|
438
|
+
markExternalRedirect(merged);
|
|
439
|
+
}
|
|
440
|
+
return merged;
|
|
441
|
+
}
|
|
442
|
+
|
|
350
443
|
/**
|
|
351
444
|
* Execute middleware chain
|
|
352
445
|
*
|
|
@@ -355,7 +448,7 @@ function mergeReqCtxStub(
|
|
|
355
448
|
* - `ctx.headers` available before and after `await next()`
|
|
356
449
|
* - `ctx.header()` shorthand for setting a single header
|
|
357
450
|
* - Forgiving: if middleware doesn't return, uses the downstream response
|
|
358
|
-
* - Short-circuit: return Response to stop chain
|
|
451
|
+
* - Short-circuit: return OR throw a Response to stop chain
|
|
359
452
|
* - Error catching: try/catch around `next()` works
|
|
360
453
|
*/
|
|
361
454
|
export async function executeMiddleware<TEnv>(
|
|
@@ -385,20 +478,16 @@ export async function executeMiddleware<TEnv>(
|
|
|
385
478
|
// End of chain - call actual RSC handler
|
|
386
479
|
const response = await finalHandler();
|
|
387
480
|
|
|
388
|
-
const mergedHeaders = new Headers(response.headers);
|
|
389
|
-
mergeStubHeaders(mergedHeaders, stubResponse.headers, true);
|
|
390
|
-
mergeReqCtxStub(mergedHeaders, _getRequestContext());
|
|
391
|
-
|
|
392
481
|
if (isWebSocketUpgradeResponse(response)) {
|
|
393
482
|
responseHolder.response = response;
|
|
394
483
|
return response;
|
|
395
484
|
}
|
|
396
485
|
|
|
397
|
-
//
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
486
|
+
// Chain ran to completion: stub headers overwrite (stubOverridesNonCookie)
|
|
487
|
+
// and reqCtx stub headers are merged in.
|
|
488
|
+
responseHolder.response = mergeResponse(response, stubResponse.headers, {
|
|
489
|
+
stubOverridesNonCookie: true,
|
|
490
|
+
mergeReqCtx: true,
|
|
402
491
|
});
|
|
403
492
|
|
|
404
493
|
return responseHolder.response;
|
|
@@ -456,22 +545,36 @@ export async function executeMiddleware<TEnv>(
|
|
|
456
545
|
return nextPromise;
|
|
457
546
|
};
|
|
458
547
|
|
|
548
|
+
// Wrap the middleware (including its downstream next() chain) in its span
|
|
549
|
+
// via the unified phase API. metric:false — the middleware's perf metric is
|
|
550
|
+
// its exclusive pre/post own-time, recorded directly above and below, finer
|
|
551
|
+
// than a single wrap. Spans nest by async context, so this onions
|
|
552
|
+
// middleware-over-middleware and the core handler underneath. Pass-through
|
|
553
|
+
// when neither surface is active.
|
|
459
554
|
let result: Response | void;
|
|
460
555
|
try {
|
|
461
|
-
result = await
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
556
|
+
result = await observePhase(PHASES.middleware(metricLabel), async () => {
|
|
557
|
+
try {
|
|
558
|
+
return await entry.handler(ctx, wrappedNext);
|
|
559
|
+
} catch (error) {
|
|
560
|
+
// Thrown Response is short-circuit control flow, not an error —
|
|
561
|
+
// absorb it INSIDE the span so the tracing runner settles the
|
|
562
|
+
// rango.middleware span as success, not STATUS_ERROR (every auth
|
|
563
|
+
// redirect would otherwise inflate trace error rates). Returning it
|
|
564
|
+
// routes through the `if (result instanceof Response)` branch below,
|
|
565
|
+
// so stub headers and request-context cookies merge identically to an
|
|
566
|
+
// explicit `return new Response(...)`. Segment handlers already follow
|
|
567
|
+
// this convention (segment-resolution/helpers.ts keeps result handling
|
|
568
|
+
// outside the span). Real errors propagate past the span.
|
|
569
|
+
if (error instanceof Response) return error;
|
|
570
|
+
throw error;
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
} finally {
|
|
574
|
+
// Settle the middleware own-time metric once on both the success and
|
|
575
|
+
// error paths (idempotent guard in finishMiddleware).
|
|
576
|
+
finishMiddleware();
|
|
473
577
|
}
|
|
474
|
-
finishMiddleware();
|
|
475
578
|
|
|
476
579
|
// Record post-next() processing time when middleware did work after
|
|
477
580
|
// the downstream chain resolved (e.g. adding headers, logging).
|
|
@@ -490,20 +593,18 @@ export async function executeMiddleware<TEnv>(
|
|
|
490
593
|
|
|
491
594
|
// Explicit return takes precedence (middleware short-circuit).
|
|
492
595
|
// Merge stub headers (from ctx.header before this point) and
|
|
493
|
-
// RequestContext stub headers (from
|
|
596
|
+
// RequestContext stub headers (from cookies().set()) into the
|
|
494
597
|
// returned Response so they are not lost.
|
|
495
598
|
if (result instanceof Response) {
|
|
496
599
|
if (isWebSocketUpgradeResponse(result)) {
|
|
497
600
|
responseHolder.response = result;
|
|
498
601
|
return result;
|
|
499
602
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
statusText: result.statusText,
|
|
506
|
-
headers: mergedHeaders,
|
|
603
|
+
// Explicit short-circuit: the returned Response's own headers win
|
|
604
|
+
// (stubOverridesNonCookie=false); reqCtx stub headers still merge in.
|
|
605
|
+
const merged = mergeResponse(result, stubResponse.headers, {
|
|
606
|
+
stubOverridesNonCookie: false,
|
|
607
|
+
mergeReqCtx: true,
|
|
507
608
|
});
|
|
508
609
|
responseHolder.response = merged;
|
|
509
610
|
return merged;
|
|
@@ -564,7 +665,7 @@ export async function executeMiddleware<TEnv>(
|
|
|
564
665
|
*
|
|
565
666
|
* Intercepts use a shared stubResponse from the request context. This function:
|
|
566
667
|
* - Runs middleware in sequence with a simple next() chain
|
|
567
|
-
* - Returns Response if any middleware short-circuits (returns Response or redirects BEFORE next())
|
|
668
|
+
* - Returns Response if any middleware short-circuits (returns OR throws a Response, or redirects, BEFORE next())
|
|
568
669
|
* - Returns null if all middleware calls next() - headers set after next() remain on stubResponse
|
|
569
670
|
*
|
|
570
671
|
* @param middlewares - Array of middleware functions
|
|
@@ -602,6 +703,7 @@ export async function executeInterceptMiddleware<TEnv>(
|
|
|
602
703
|
return stubResponse;
|
|
603
704
|
}
|
|
604
705
|
|
|
706
|
+
const ordinal = index;
|
|
605
707
|
const middleware = middlewares[index++];
|
|
606
708
|
const ctx = createMiddlewareContext(
|
|
607
709
|
request,
|
|
@@ -623,18 +725,30 @@ export async function executeInterceptMiddleware<TEnv>(
|
|
|
623
725
|
return next();
|
|
624
726
|
};
|
|
625
727
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
728
|
+
// Span-wrap each intercept middleware as rango.middleware (metric:false —
|
|
729
|
+
// intercept runs inside the render phase already metered by render:total, so
|
|
730
|
+
// it contributes a span but no separate perf metric). Bare MiddlewareFns
|
|
731
|
+
// have no pattern, so the label is scoped to "*" like a pattern-less entry.
|
|
732
|
+
const label = getMiddlewareMetricLabel(
|
|
733
|
+
{ handler: middleware, pattern: null } as MiddlewareEntry<TEnv>,
|
|
734
|
+
ordinal,
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
const result: Response | void = await observePhase(
|
|
738
|
+
PHASES.middleware(label),
|
|
739
|
+
async () => {
|
|
740
|
+
try {
|
|
741
|
+
return await middleware(ctx, guardedNext);
|
|
742
|
+
} catch (error) {
|
|
743
|
+
// Thrown Response is short-circuit control flow, parity with the
|
|
744
|
+
// explicit-return path below. Absorb it INSIDE the span so the tracing
|
|
745
|
+
// runner settles rango.middleware as success, not STATUS_ERROR (same
|
|
746
|
+
// reasoning as executeMiddleware's main chain). Real errors propagate.
|
|
747
|
+
if (error instanceof Response) return error;
|
|
748
|
+
throw error;
|
|
749
|
+
}
|
|
750
|
+
},
|
|
751
|
+
);
|
|
638
752
|
|
|
639
753
|
if (result instanceof Response) {
|
|
640
754
|
earlyResponse = result;
|
|
@@ -658,15 +772,13 @@ export async function executeInterceptMiddleware<TEnv>(
|
|
|
658
772
|
});
|
|
659
773
|
|
|
660
774
|
if (hasStubHeaders) {
|
|
661
|
-
//
|
|
662
|
-
//
|
|
663
|
-
//
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
statusText: response.statusText,
|
|
669
|
-
headers: mergedHeaders,
|
|
775
|
+
// Only fill in missing headers — the returned Response's explicit headers
|
|
776
|
+
// take precedence (stubOverridesNonCookie=false), matching executeMiddleware.
|
|
777
|
+
// mergeReqCtx=false: the intercept path deliberately does NOT merge reqCtx
|
|
778
|
+
// stub headers here (pinned by intercept-middleware-headers.test.ts).
|
|
779
|
+
return mergeResponse(response, stubResponse.headers, {
|
|
780
|
+
stubOverridesNonCookie: false,
|
|
781
|
+
mergeReqCtx: false,
|
|
670
782
|
});
|
|
671
783
|
}
|
|
672
784
|
return response;
|
|
@@ -707,7 +819,6 @@ export async function executeLoaderMiddleware<TEnv>(
|
|
|
707
819
|
regex: null,
|
|
708
820
|
paramNames: [],
|
|
709
821
|
handler,
|
|
710
|
-
mountPrefix: null,
|
|
711
822
|
} as MiddlewareEntry<TEnv>,
|
|
712
823
|
params,
|
|
713
824
|
}));
|