@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
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { RouterError } from "../errors.js";
|
|
10
|
-
import {
|
|
10
|
+
import { getRequestContext } from "../server/request-context.js";
|
|
11
11
|
import { contextGet } from "../context-var.js";
|
|
12
12
|
import { NOCACHE_SYMBOL } from "../cache/taint.js";
|
|
13
|
-
import {
|
|
14
|
-
import { createCacheScope } from "../cache/cache-scope.js";
|
|
13
|
+
import { RESPONSE_TYPE_MIME } from "../router/content-negotiation.js";
|
|
14
|
+
import { createCacheScope, resolveCacheTags } from "../cache/cache-scope.js";
|
|
15
|
+
import { serveResponseRouteWithCache } from "./response-cache-serve.js";
|
|
15
16
|
import { executeMiddleware } from "../router/middleware.js";
|
|
16
17
|
import {
|
|
17
18
|
createReverseFunction,
|
|
@@ -20,13 +21,21 @@ import {
|
|
|
20
21
|
import type { MiddlewareFn } from "../router/middleware.js";
|
|
21
22
|
import type { EntryData } from "../server/context.js";
|
|
22
23
|
import type { HandlerContext } from "./handler-context.js";
|
|
23
|
-
import {
|
|
24
|
+
import { createProblemDetails } from "./response-error.js";
|
|
24
25
|
import {
|
|
25
26
|
createResponseWithMergedHeaders,
|
|
26
27
|
finalizeResponse,
|
|
27
28
|
isCacheableStatus,
|
|
28
29
|
buildRouteMiddlewareEntries,
|
|
30
|
+
mergeStubHeadersAndFinalize,
|
|
29
31
|
} from "./helpers.js";
|
|
32
|
+
import { isWebSocketUpgradeResponse } from "../response-utils.js";
|
|
33
|
+
import { stringifyJsonRouteResult } from "./json-route-result.js";
|
|
34
|
+
import {
|
|
35
|
+
EXTERNAL_REDIRECT_MARKER,
|
|
36
|
+
isExternalRedirect,
|
|
37
|
+
markExternalRedirect,
|
|
38
|
+
} from "../redirect-origin.js";
|
|
30
39
|
|
|
31
40
|
export interface ResponseRouteMatch {
|
|
32
41
|
responseType: string;
|
|
@@ -70,7 +79,7 @@ export async function handleResponseRoute<TEnv>(
|
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
// Build lightweight context for response handler
|
|
73
|
-
const reqCtx =
|
|
82
|
+
const reqCtx = getRequestContext();
|
|
74
83
|
const cleanUrl = stripInternalParams(url);
|
|
75
84
|
const responseHandlerCtx = {
|
|
76
85
|
request,
|
|
@@ -78,10 +87,13 @@ export async function handleResponseRoute<TEnv>(
|
|
|
78
87
|
env,
|
|
79
88
|
searchParams: cleanUrl.searchParams,
|
|
80
89
|
url: cleanUrl,
|
|
90
|
+
originalUrl: reqCtx.originalUrl,
|
|
81
91
|
pathname: url.pathname,
|
|
82
92
|
reverse: createReverseFunction(handlerCtx.getRequiredRouteMap()),
|
|
83
93
|
get: ((keyOrVar: any) => contextGet(variables, keyOrVar)) as any,
|
|
84
94
|
header: (name: string, value: string) => reqCtx.header(name, value),
|
|
95
|
+
waitUntil: reqCtx.waitUntil.bind(reqCtx),
|
|
96
|
+
executionContext: reqCtx.executionContext,
|
|
85
97
|
_responseType: preview.responseType,
|
|
86
98
|
};
|
|
87
99
|
// Brand with taint symbol so "use cache" detects it as request-scoped
|
|
@@ -96,51 +108,39 @@ export async function handleResponseRoute<TEnv>(
|
|
|
96
108
|
// so that stub headers (cookies, custom headers set via ctx.header()) are included.
|
|
97
109
|
// Use Headers (not Record<string, string>) to preserve duplicate entries like Set-Cookie.
|
|
98
110
|
const rewrapResponse = (result: Response) => {
|
|
111
|
+
// 204/205/304 are NOT short-circuited — they're valid for the Response
|
|
112
|
+
// constructor and must honor ctx.setStatus() overrides. Only upgrade
|
|
113
|
+
// responses (status 101 / `webSocket` property) bypass reconstruction.
|
|
114
|
+
if (isWebSocketUpgradeResponse(result)) {
|
|
115
|
+
return mergeStubHeadersAndFinalize(result);
|
|
116
|
+
}
|
|
99
117
|
const headers = new Headers();
|
|
100
118
|
result.headers.forEach((value, key) => {
|
|
119
|
+
// Never copy the reserved external-redirect marker off a handler result.
|
|
120
|
+
// It is not a trust signal -- the opt-in is the out-of-band brand below
|
|
121
|
+
// -- and a proxy-style route returning an attacker-controlled upstream
|
|
122
|
+
// response must not let a forged value ride through to the browser.
|
|
123
|
+
if (key.toLowerCase() === EXTERNAL_REDIRECT_MARKER) return;
|
|
101
124
|
if (key.toLowerCase() === "set-cookie") {
|
|
102
125
|
headers.append(key, value);
|
|
103
126
|
} else {
|
|
104
127
|
headers.set(key, value);
|
|
105
128
|
}
|
|
106
129
|
});
|
|
107
|
-
|
|
130
|
+
const rewrapped = createResponseWithMergedHeaders(result.body, {
|
|
108
131
|
status: result.status,
|
|
109
132
|
headers,
|
|
110
133
|
});
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (result instanceof Response) {
|
|
118
|
-
return rewrapResponse(result);
|
|
119
|
-
}
|
|
120
|
-
return createResponseWithMergedHeaders(
|
|
121
|
-
JSON.stringify({ data: result }),
|
|
122
|
-
{
|
|
123
|
-
status: 200,
|
|
124
|
-
headers: { "content-type": "application/json;charset=utf-8" },
|
|
125
|
-
},
|
|
126
|
-
);
|
|
127
|
-
} catch (error) {
|
|
128
|
-
handlerCtx.callOnError(error, "handler", errorCtx);
|
|
129
|
-
const isDev = process.env.NODE_ENV !== "production";
|
|
130
|
-
const status = error instanceof RouterError ? error.status : 500;
|
|
131
|
-
return createResponseWithMergedHeaders(
|
|
132
|
-
JSON.stringify({
|
|
133
|
-
error: createResponseErrorPayload(error, isDev),
|
|
134
|
-
}),
|
|
135
|
-
{
|
|
136
|
-
status,
|
|
137
|
-
headers: { "content-type": "application/json;charset=utf-8" },
|
|
138
|
-
},
|
|
139
|
-
);
|
|
134
|
+
// Transfer the out-of-band external brand only when the handler result is
|
|
135
|
+
// genuinely branded (a real redirect(url, { external: true })). A proxied
|
|
136
|
+
// upstream Response is never branded, so an attacker cannot opt a response
|
|
137
|
+
// route's redirect out of the same-origin guard by injecting the header.
|
|
138
|
+
if (isExternalRedirect(result)) {
|
|
139
|
+
markExternalRedirect(rewrapped);
|
|
140
140
|
}
|
|
141
|
-
|
|
141
|
+
return rewrapped;
|
|
142
|
+
};
|
|
142
143
|
|
|
143
|
-
// Non-JSON response routes: catch errors and return plain Response
|
|
144
144
|
try {
|
|
145
145
|
const result = await (preview.handler as Function)(responseHandlerCtx);
|
|
146
146
|
|
|
@@ -148,38 +148,56 @@ export async function handleResponseRoute<TEnv>(
|
|
|
148
148
|
return rewrapResponse(result);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
});
|
|
163
|
-
case "xml":
|
|
164
|
-
return createResponseWithMergedHeaders(String(result), {
|
|
165
|
-
status: 200,
|
|
166
|
-
headers: { "content-type": "application/xml;charset=utf-8" },
|
|
167
|
-
});
|
|
168
|
-
case "md":
|
|
169
|
-
return createResponseWithMergedHeaders(String(result), {
|
|
170
|
-
status: 200,
|
|
171
|
-
headers: { "content-type": "text/markdown;charset=utf-8" },
|
|
172
|
-
});
|
|
173
|
-
default:
|
|
174
|
-
// image, stream, any -- must return Response
|
|
175
|
-
throw new Error(
|
|
176
|
-
`Response route handler for "${preview.responseType}" must return a Response object, got ${typeof result}`,
|
|
177
|
-
);
|
|
151
|
+
// Handled before the MIME lookup (json is also a RESPONSE_TYPE_MIME key).
|
|
152
|
+
if (preview.responseType === "json") {
|
|
153
|
+
// Runtime guard: the json() return type rejects nested Promises at
|
|
154
|
+
// compile time, but an `as`-cast or untyped (JS) handler can still slip
|
|
155
|
+
// one through. stringifyJsonRouteResult throws a clear error instead of
|
|
156
|
+
// shipping empty data (shared with dispatch() so the two cannot drift).
|
|
157
|
+
const body = stringifyJsonRouteResult(result);
|
|
158
|
+
return createResponseWithMergedHeaders(body, {
|
|
159
|
+
status: 200,
|
|
160
|
+
headers: { "content-type": "application/json;charset=utf-8" },
|
|
161
|
+
});
|
|
178
162
|
}
|
|
163
|
+
|
|
164
|
+
// Object.hasOwn (not truthiness) so prototype names like "toString" are not
|
|
165
|
+
// matched; image/stream/any are absent and fall through to the throw.
|
|
166
|
+
if (Object.hasOwn(RESPONSE_TYPE_MIME, preview.responseType)) {
|
|
167
|
+
return createResponseWithMergedHeaders(String(result), {
|
|
168
|
+
status: 200,
|
|
169
|
+
headers: {
|
|
170
|
+
"content-type": `${RESPONSE_TYPE_MIME[preview.responseType]};charset=utf-8`,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
throw new Error(
|
|
176
|
+
`Response route handler for "${preview.responseType}" must return a Response object, got ${typeof result}`,
|
|
177
|
+
);
|
|
179
178
|
} catch (error) {
|
|
180
179
|
handlerCtx.callOnError(error, "handler", errorCtx);
|
|
181
180
|
const isDev = process.env.NODE_ENV !== "production";
|
|
182
|
-
const
|
|
181
|
+
const derivedStatus = error instanceof RouterError ? error.status : 500;
|
|
182
|
+
// Resolve the effective status the same way createResponseWithMergedHeaders
|
|
183
|
+
// will (ctx.res.status override) so the problem body's status/title match
|
|
184
|
+
// the actual HTTP status — e.g. when a handler called ctx.setStatus()
|
|
185
|
+
// before throwing.
|
|
186
|
+
const status =
|
|
187
|
+
reqCtx.res.status !== 200 ? reqCtx.res.status : derivedStatus;
|
|
188
|
+
|
|
189
|
+
if (preview.responseType === "json") {
|
|
190
|
+
return createResponseWithMergedHeaders(
|
|
191
|
+
JSON.stringify(createProblemDetails(error, status, isDev)),
|
|
192
|
+
{
|
|
193
|
+
status,
|
|
194
|
+
headers: {
|
|
195
|
+
"content-type": "application/problem+json;charset=utf-8",
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
183
201
|
const message =
|
|
184
202
|
error instanceof RouterError
|
|
185
203
|
? error.message
|
|
@@ -196,7 +214,9 @@ export async function handleResponseRoute<TEnv>(
|
|
|
196
214
|
// Wrap callHandler to append Vary: Accept on content-negotiated responses
|
|
197
215
|
const callHandlerWithVary = async () => {
|
|
198
216
|
const response = await callHandler();
|
|
199
|
-
if (preview.negotiated) {
|
|
217
|
+
if (preview.negotiated && !isWebSocketUpgradeResponse(response)) {
|
|
218
|
+
// Skip Vary on upgrade responses: headers are semantically immutable
|
|
219
|
+
// on some runtimes, and Vary is meaningless for a 101 response.
|
|
200
220
|
response.headers.append("Vary", "Accept");
|
|
201
221
|
}
|
|
202
222
|
return response;
|
|
@@ -217,130 +237,20 @@ export async function handleResponseRoute<TEnv>(
|
|
|
217
237
|
return callHandlerWithVary();
|
|
218
238
|
};
|
|
219
239
|
|
|
220
|
-
//
|
|
240
|
+
// Response-route cache: resolved through the shared serve leaf
|
|
241
|
+
// (rsc/response-cache-serve.ts) so production and the dispatch testing
|
|
242
|
+
// primitive share ONE owner of the cache contract. Returns undefined when no
|
|
243
|
+
// cache applies, so we fall through to a plain handler run.
|
|
221
244
|
if (preview.manifestEntry) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
let conditionPassed = true;
|
|
232
|
-
if (cacheScope.config !== false && cacheScope.config.condition) {
|
|
233
|
-
try {
|
|
234
|
-
conditionPassed = !!cacheScope.config.condition(reqCtx);
|
|
235
|
-
} catch {
|
|
236
|
-
conditionPassed = false;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const store = cacheScope.getStore() ?? reqCtx._cacheStore;
|
|
241
|
-
if (conditionPassed && store?.getResponse && store?.putResponse) {
|
|
242
|
-
// Build cache key with response:{type}: prefix to avoid collision
|
|
243
|
-
// with segment keys and differentiate between response types.
|
|
244
|
-
// Include host and url.search so query-driven and multi-host
|
|
245
|
-
// responses cache separately.
|
|
246
|
-
let cacheKey = `response:${preview.responseType}:${url.host}${url.pathname}${url.search}`;
|
|
247
|
-
|
|
248
|
-
// Priority 1: Route-level key function (full override)
|
|
249
|
-
if (cacheScope.config !== false && cacheScope.config.key) {
|
|
250
|
-
try {
|
|
251
|
-
const customKey = await cacheScope.config.key(reqCtx);
|
|
252
|
-
cacheKey = `response:${customKey}`;
|
|
253
|
-
} catch {
|
|
254
|
-
// Fall back to default key on route-level key failure
|
|
255
|
-
}
|
|
256
|
-
} else if (store.keyGenerator) {
|
|
257
|
-
// Priority 2: Store-level keyGenerator (modifies default key)
|
|
258
|
-
try {
|
|
259
|
-
cacheKey = await store.keyGenerator(reqCtx, cacheKey);
|
|
260
|
-
} catch {
|
|
261
|
-
// Fall back to default key on keyGenerator failure
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Save pre-handler callbacks (registered by app-level middleware
|
|
266
|
-
// before we reach the cache block) and clear the live array.
|
|
267
|
-
// createResponseWithMergedHeaders (inside the handler) eagerly
|
|
268
|
-
// executes any callbacks present in _onResponseCallbacks, so
|
|
269
|
-
// handler-registered callbacks are baked into the handler's
|
|
270
|
-
// response and the cached artifact. Pre-handler callbacks are
|
|
271
|
-
// NOT in the live array during execution, so they are applied
|
|
272
|
-
// once per serve on every path (hit + miss) below.
|
|
273
|
-
const savedCallbacks = reqCtx._onResponseCallbacks;
|
|
274
|
-
reqCtx._onResponseCallbacks = [];
|
|
275
|
-
|
|
276
|
-
const applyPreHandlerCallbacks = (response: Response): Response => {
|
|
277
|
-
let result = response;
|
|
278
|
-
for (const callback of savedCallbacks) {
|
|
279
|
-
result = callback(result) ?? result;
|
|
280
|
-
}
|
|
281
|
-
return result;
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
try {
|
|
285
|
-
const cached = await store.getResponse(cacheKey);
|
|
286
|
-
|
|
287
|
-
if (cached && isCacheableStatus(cached.response.status)) {
|
|
288
|
-
if (!cached.shouldRevalidate) {
|
|
289
|
-
// Fresh hit
|
|
290
|
-
return applyPreHandlerCallbacks(cached.response);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Stale hit (SWR) - return cached, revalidate in background
|
|
294
|
-
reqCtx.waitUntil(async () => {
|
|
295
|
-
try {
|
|
296
|
-
// finalizeResponse drains any onResponse callbacks registered
|
|
297
|
-
// during middleware execution (e.g. middleware short-circuit)
|
|
298
|
-
// that createResponseWithMergedHeaders didn't reach.
|
|
299
|
-
const fresh = finalizeResponse(await executeHandler());
|
|
300
|
-
if (isCacheableStatus(fresh.status)) {
|
|
301
|
-
await store.putResponse!(
|
|
302
|
-
cacheKey,
|
|
303
|
-
fresh.clone(),
|
|
304
|
-
cacheScope!.ttl,
|
|
305
|
-
cacheScope!.swr,
|
|
306
|
-
);
|
|
307
|
-
}
|
|
308
|
-
} catch (error) {
|
|
309
|
-
console.error(`[ResponseCache] Revalidation failed:`, error);
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
return applyPreHandlerCallbacks(cached.response);
|
|
314
|
-
}
|
|
315
|
-
} catch (error) {
|
|
316
|
-
console.error(`[ResponseCache] Cache lookup failed:`, error);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Cache miss - execute handler and cache the result.
|
|
320
|
-
// createResponseWithMergedHeaders inside the handler drains callbacks
|
|
321
|
-
// registered during handler execution. finalizeResponse catches any
|
|
322
|
-
// remaining callbacks (e.g. from middleware short-circuit where the
|
|
323
|
-
// handler never ran) so the cached artifact includes all transforms.
|
|
324
|
-
const response = finalizeResponse(await executeHandler());
|
|
325
|
-
|
|
326
|
-
if (isCacheableStatus(response.status)) {
|
|
327
|
-
reqCtx.waitUntil(async () => {
|
|
328
|
-
try {
|
|
329
|
-
await store.putResponse!(
|
|
330
|
-
cacheKey,
|
|
331
|
-
response.clone(),
|
|
332
|
-
cacheScope!.ttl,
|
|
333
|
-
cacheScope!.swr,
|
|
334
|
-
);
|
|
335
|
-
} catch (error) {
|
|
336
|
-
console.error(`[ResponseCache] Cache write failed:`, error);
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return applyPreHandlerCallbacks(response);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
245
|
+
const cached = await serveResponseRouteWithCache({
|
|
246
|
+
reqCtx,
|
|
247
|
+
manifestEntry: preview.manifestEntry,
|
|
248
|
+
responseType: preview.responseType,
|
|
249
|
+
url,
|
|
250
|
+
executeHandler,
|
|
251
|
+
deps: { createCacheScope, resolveCacheTags },
|
|
252
|
+
});
|
|
253
|
+
if (cached !== undefined) return cached;
|
|
344
254
|
}
|
|
345
255
|
|
|
346
256
|
return executeHandler().then(finalizeResponse);
|