@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
|
@@ -12,10 +12,16 @@ import {
|
|
|
12
12
|
runWithRequestContext,
|
|
13
13
|
type RequestContext,
|
|
14
14
|
} from "../../server/request-context.js";
|
|
15
|
+
import { drainOnResponseCallbacks } from "../../rsc/helpers.js";
|
|
15
16
|
import { resolveLocationStateEntries } from "../../browser/react/location-state-shared.js";
|
|
16
17
|
import { createReverseFunction } from "../../router/handler-context.js";
|
|
17
18
|
import { normalizeBasename } from "../../router/basename.js";
|
|
18
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
seedVariables,
|
|
21
|
+
resolveSeededStateCookieName,
|
|
22
|
+
type VarsInit,
|
|
23
|
+
type StateCookieSeed,
|
|
24
|
+
} from "./seed-vars.js";
|
|
19
25
|
import type { ThemeConfig } from "../../theme/types.js";
|
|
20
26
|
import { resolveThemeConfig } from "../../theme/constants.js";
|
|
21
27
|
import type { SegmentCacheStore } from "../../cache/types.js";
|
|
@@ -23,15 +29,8 @@ import type { CacheProfile } from "../../cache/profile-registry.js";
|
|
|
23
29
|
|
|
24
30
|
const DEFAULT_ORIGIN = "http://localhost/";
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* set). Either a plain object keyed by var name (the common, best-inferring
|
|
29
|
-
* form: `{ user: u }`) or a list of `[key, value]` tuples where the key may be a
|
|
30
|
-
* `createVar()` handle or a string (`[[userVar, u], ["flag", true]]`).
|
|
31
|
-
*/
|
|
32
|
-
export type VarsInit =
|
|
33
|
-
| Record<string, unknown>
|
|
34
|
-
| ReadonlyArray<readonly [ContextVar<unknown> | string, unknown]>;
|
|
32
|
+
export type { VarsInit, StateCookieSeed };
|
|
33
|
+
export { seedVariables };
|
|
35
34
|
|
|
36
35
|
/** Normalize a Request | string | undefined into a concrete Request. */
|
|
37
36
|
export function toRequest(
|
|
@@ -39,32 +38,9 @@ export function toRequest(
|
|
|
39
38
|
init?: RequestInit,
|
|
40
39
|
): Request {
|
|
41
40
|
if (request instanceof Request) return request;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return new Request(DEFAULT_ORIGIN, init);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Preload variables as if set by upstream middleware. Accepts entries keyed by
|
|
50
|
-
* either a ContextVar (from createVar) or a string, matching ctx.set().
|
|
51
|
-
*/
|
|
52
|
-
export function seedVariables(
|
|
53
|
-
variables: Record<string, unknown>,
|
|
54
|
-
vars?: VarsInit,
|
|
55
|
-
): Record<string, unknown> {
|
|
56
|
-
if (!vars) return variables;
|
|
57
|
-
// Array/iterable form -> use the tuples as-is; plain object -> its entries.
|
|
58
|
-
const entries: Iterable<readonly [ContextVar<unknown> | string, unknown]> =
|
|
59
|
-
Symbol.iterator in (vars as object)
|
|
60
|
-
? (vars as ReadonlyArray<
|
|
61
|
-
readonly [ContextVar<unknown> | string, unknown]
|
|
62
|
-
>)
|
|
63
|
-
: Object.entries(vars as Record<string, unknown>);
|
|
64
|
-
for (const [key, value] of entries) {
|
|
65
|
-
contextSet(variables, key as ContextVar<unknown>, value);
|
|
66
|
-
}
|
|
67
|
-
return variables;
|
|
41
|
+
return typeof request === "string"
|
|
42
|
+
? new Request(new URL(request, DEFAULT_ORIGIN), init)
|
|
43
|
+
: new Request(DEFAULT_ORIGIN, init);
|
|
68
44
|
}
|
|
69
45
|
|
|
70
46
|
export interface CreateTestContextOptions<TEnv> {
|
|
@@ -108,13 +84,49 @@ export interface CreateTestContextOptions<TEnv> {
|
|
|
108
84
|
* `{ themes: [...] }`) to exercise a handler that reads them.
|
|
109
85
|
*/
|
|
110
86
|
theme?: ThemeConfig | true;
|
|
87
|
+
/**
|
|
88
|
+
* Customize the rango state cookie that `invalidateClientCache()` rotates.
|
|
89
|
+
* The name is ALWAYS seeded (default `rango-state_router_0`) so a call to
|
|
90
|
+
* `invalidateClientCache()` rotates and emits the `Set-Cookie` exactly as in
|
|
91
|
+
* production, rather than silently no-opping. Override `prefix`/`routerId` to
|
|
92
|
+
* match your `createRouter({ stateCookiePrefix, id })` so the test asserts the
|
|
93
|
+
* same name, or `version` (the build identifier prefixed to the rotated
|
|
94
|
+
* `{version}:{timestamp}` value, default `"0"`). Assert
|
|
95
|
+
* `response.headers.getSetCookie()` against the resolved `stateCookieName`
|
|
96
|
+
* (returned by `runInRequestContext`).
|
|
97
|
+
*/
|
|
98
|
+
stateCookie?: StateCookieSeed;
|
|
111
99
|
}
|
|
112
100
|
|
|
101
|
+
/**
|
|
102
|
+
* The seeded RequestContext with its `reverse` RELAXED to accept any route NAME
|
|
103
|
+
* from the `routeMap` you passed, rather than the global `Rango.GeneratedRouteMap`
|
|
104
|
+
* union — so reversing a test-only route name is not a type error (it works at
|
|
105
|
+
* runtime; the names come from your `routeMap`). Mirrors runLoader's
|
|
106
|
+
* `TestLoaderContext.reverse`. Everything else is the real `RequestContext`.
|
|
107
|
+
*/
|
|
108
|
+
export type TestRequestContextObject<TEnv> = Omit<
|
|
109
|
+
RequestContext<TEnv>,
|
|
110
|
+
"reverse"
|
|
111
|
+
> & {
|
|
112
|
+
reverse: (
|
|
113
|
+
name: string,
|
|
114
|
+
params?: Record<string, string>,
|
|
115
|
+
search?: Record<string, unknown>,
|
|
116
|
+
) => string;
|
|
117
|
+
};
|
|
118
|
+
|
|
113
119
|
export interface TestRequestContext<TEnv> {
|
|
114
|
-
ctx:
|
|
120
|
+
ctx: TestRequestContextObject<TEnv>;
|
|
115
121
|
request: Request;
|
|
116
122
|
url: URL;
|
|
117
123
|
variables: Record<string, unknown>;
|
|
124
|
+
/**
|
|
125
|
+
* The resolved rango state cookie name seeded into the context (default
|
|
126
|
+
* `rango-state_router_0`, or composed from `opts.stateCookie`). The name a
|
|
127
|
+
* call to `invalidateClientCache()` rotates.
|
|
128
|
+
*/
|
|
129
|
+
stateCookieName: string;
|
|
118
130
|
}
|
|
119
131
|
|
|
120
132
|
/**
|
|
@@ -132,6 +144,7 @@ export function createTestRequestContext<TEnv>(
|
|
|
132
144
|
const request = toRequest(opts.request, opts.requestInit);
|
|
133
145
|
const url = new URL(request.url);
|
|
134
146
|
const variables = seedVariables(opts.variables ?? {}, opts.vars);
|
|
147
|
+
const stateCookieName = resolveSeededStateCookieName(opts.stateCookie);
|
|
135
148
|
const ctx = createRequestContext<TEnv>({
|
|
136
149
|
env: (opts.env ?? {}) as TEnv,
|
|
137
150
|
request,
|
|
@@ -141,6 +154,8 @@ export function createTestRequestContext<TEnv>(
|
|
|
141
154
|
opts.theme === undefined ? undefined : resolveThemeConfig(opts.theme),
|
|
142
155
|
cacheStore: opts.cacheStore,
|
|
143
156
|
cacheProfiles: opts.cacheProfiles,
|
|
157
|
+
stateCookieName,
|
|
158
|
+
version: opts.stateCookie?.version,
|
|
144
159
|
});
|
|
145
160
|
if (opts.basename !== undefined)
|
|
146
161
|
ctx._basename = normalizeBasename(opts.basename);
|
|
@@ -153,7 +168,13 @@ export function createTestRequestContext<TEnv>(
|
|
|
153
168
|
opts.params ?? {},
|
|
154
169
|
) as RequestContext<TEnv>["reverse"];
|
|
155
170
|
}
|
|
156
|
-
return {
|
|
171
|
+
return {
|
|
172
|
+
ctx: ctx as unknown as TestRequestContextObject<TEnv>,
|
|
173
|
+
request,
|
|
174
|
+
url,
|
|
175
|
+
variables,
|
|
176
|
+
stateCookieName,
|
|
177
|
+
};
|
|
157
178
|
}
|
|
158
179
|
|
|
159
180
|
/**
|
|
@@ -162,14 +183,28 @@ export function createTestRequestContext<TEnv>(
|
|
|
162
183
|
* assert what an action produced.
|
|
163
184
|
*/
|
|
164
185
|
export interface RunInRequestContextResult<T> {
|
|
165
|
-
/** The value `fn` returned (awaited if it returned a promise). */
|
|
166
|
-
result: T;
|
|
167
186
|
/**
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
|
|
172
|
-
|
|
187
|
+
* The value `fn` returned (awaited), or `undefined` if `fn` threw — in which
|
|
188
|
+
* case the thrown value is on {@link thrown}. The snapshot below is captured
|
|
189
|
+
* either way.
|
|
190
|
+
*/
|
|
191
|
+
result: T | undefined;
|
|
192
|
+
/**
|
|
193
|
+
* The value `fn` threw, or `undefined` if it returned normally. Commonly a
|
|
194
|
+
* `Response` from `throw redirect(...)` / `throw notFound()` — the dominant
|
|
195
|
+
* cookie+flash case is an action that sets them then throws a redirect — so
|
|
196
|
+
* this (and the snapshot below) is observable WITHOUT wrapping the action in
|
|
197
|
+
* your own try/catch. NOTE: the value is captured, NOT re-thrown; assert on it
|
|
198
|
+
* for a throwing action.
|
|
199
|
+
*/
|
|
200
|
+
thrown: unknown;
|
|
201
|
+
/**
|
|
202
|
+
* A Response carrying the status, headers, and Set-Cookie the run set (via
|
|
203
|
+
* `cookies().set()`, `ctx.header()`, etc.). Assert Set-Cookie with
|
|
204
|
+
* `response.headers.getSetCookie()`. When `fn` threw a `Response` (a redirect),
|
|
205
|
+
* THIS is that Response with the accumulated Set-Cookie/headers merged in
|
|
206
|
+
* (mirroring how the framework merges them in production), so a redirect's
|
|
207
|
+
* Location AND the cookies it set are both observable here.
|
|
173
208
|
*/
|
|
174
209
|
response: Response;
|
|
175
210
|
/**
|
|
@@ -177,6 +212,14 @@ export interface RunInRequestContextResult<T> {
|
|
|
177
212
|
* anything the run set or deleted (last-write-wins), as `{ name: value }`.
|
|
178
213
|
*/
|
|
179
214
|
cookies: Record<string, string>;
|
|
215
|
+
/**
|
|
216
|
+
* The response headers the run set (via `ctx.header(...)`, plus a thrown
|
|
217
|
+
* redirect's `Location`), as a plain `{ name: value }` object — the same view
|
|
218
|
+
* as `response.headers`, but assertable like `cookies`/`locationState`.
|
|
219
|
+
* EXCLUDES `set-cookie` (use `cookies`, or `response.headers.getSetCookie()`).
|
|
220
|
+
* Header names are lowercased (HTTP headers are case-insensitive).
|
|
221
|
+
*/
|
|
222
|
+
headers: Record<string, string>;
|
|
180
223
|
/**
|
|
181
224
|
* Location state the run set via `ctx.setLocationState()` / `redirect({ state })`,
|
|
182
225
|
* resolved to the flat `{ key: value }` shape the client reads off
|
|
@@ -184,13 +227,15 @@ export interface RunInRequestContextResult<T> {
|
|
|
184
227
|
* is assertable at the unit layer.
|
|
185
228
|
*/
|
|
186
229
|
locationState: Record<string, unknown>;
|
|
230
|
+
/**
|
|
231
|
+
* The resolved rango state cookie name seeded into the run (default
|
|
232
|
+
* `rango-state_router_0`, or composed from `opts.stateCookie`). Assert an
|
|
233
|
+
* action's `invalidateClientCache()` rotation against it without recomputing:
|
|
234
|
+
* `response.headers.getSetCookie().some((c) => c.startsWith(stateCookieName + "="))`.
|
|
235
|
+
*/
|
|
236
|
+
stateCookieName: string;
|
|
187
237
|
}
|
|
188
238
|
|
|
189
|
-
/**
|
|
190
|
-
* Snapshot the observable effects a run left on `ctx` (cookies + location
|
|
191
|
-
* state). Reads the fields directly off the ctx object, so it works both inside
|
|
192
|
-
* and outside the AsyncLocalStorage scope (no `getRequestContext()`).
|
|
193
|
-
*/
|
|
194
239
|
export function snapshotRunEffects<TEnv>(ctx: RequestContext<TEnv>): {
|
|
195
240
|
cookies: Record<string, string>;
|
|
196
241
|
locationState: Record<string, unknown>;
|
|
@@ -201,6 +246,74 @@ export function snapshotRunEffects<TEnv>(ctx: RequestContext<TEnv>): {
|
|
|
201
246
|
};
|
|
202
247
|
}
|
|
203
248
|
|
|
249
|
+
export function headersToObject(headers: Headers): Record<string, string> {
|
|
250
|
+
const out: Record<string, string> = {};
|
|
251
|
+
headers.forEach((value, name) => {
|
|
252
|
+
if (name.toLowerCase() === "set-cookie") return;
|
|
253
|
+
out[name] = value;
|
|
254
|
+
});
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function buildRunResponse<TEnv>(
|
|
259
|
+
ctx: RequestContext<TEnv>,
|
|
260
|
+
thrown: unknown,
|
|
261
|
+
): Response {
|
|
262
|
+
const stub = ctx.res;
|
|
263
|
+
let response: Response;
|
|
264
|
+
if (thrown instanceof Response) {
|
|
265
|
+
const headers = new Headers(thrown.headers);
|
|
266
|
+
for (const cookie of stub.headers.getSetCookie()) {
|
|
267
|
+
headers.append("set-cookie", cookie);
|
|
268
|
+
}
|
|
269
|
+
stub.headers.forEach((value, name) => {
|
|
270
|
+
if (name.toLowerCase() === "set-cookie") return;
|
|
271
|
+
if (!headers.has(name)) headers.set(name, value);
|
|
272
|
+
});
|
|
273
|
+
response = new Response(null, { status: thrown.status, headers });
|
|
274
|
+
} else {
|
|
275
|
+
response = new Response(null, {
|
|
276
|
+
status: stub.status,
|
|
277
|
+
headers: stub.headers,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
// Mirror production response finalization: every response-finalization path
|
|
281
|
+
// drains ctx.onResponse() callbacks (createResponseWithMergedHeaders /
|
|
282
|
+
// finalizeResponse). buildRunResponse runs AFTER runWithRequestContext has
|
|
283
|
+
// exited, so _getRequestContext() (and finalizeResponse) would no-op — drain
|
|
284
|
+
// ctx._onResponseCallbacks explicitly. Reuses the SAME production drain
|
|
285
|
+
// (swap-before-iterate + external-redirect brand preservation) so a callback's
|
|
286
|
+
// header mutations / returned replacement Response are reflected on the result
|
|
287
|
+
// the harness surfaces. rsc/helpers is plugin-rsc-free on its eager graph
|
|
288
|
+
// (dispatch.ts in this same testing barrel already statically imports from it).
|
|
289
|
+
// drainOnResponseCallbacks is typed against the default-env RequestContext (it
|
|
290
|
+
// touches only the env-agnostic _onResponseCallbacks); the harness ctx is
|
|
291
|
+
// RequestContext<TEnv> — assignable in the router's own tsc but not when a
|
|
292
|
+
// consumer pins a concrete Env, so cast to the param type.
|
|
293
|
+
return drainOnResponseCallbacks(
|
|
294
|
+
ctx as Parameters<typeof drainOnResponseCallbacks>[0],
|
|
295
|
+
response,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function buildRunSnapshot<TEnv>(
|
|
300
|
+
ctx: RequestContext<TEnv>,
|
|
301
|
+
thrown: unknown,
|
|
302
|
+
stateCookieName: string,
|
|
303
|
+
): {
|
|
304
|
+
thrown: unknown;
|
|
305
|
+
response: Response;
|
|
306
|
+
cookies: Record<string, string>;
|
|
307
|
+
headers: Record<string, string>;
|
|
308
|
+
locationState: Record<string, unknown>;
|
|
309
|
+
stateCookieName: string;
|
|
310
|
+
} {
|
|
311
|
+
const { cookies, locationState } = snapshotRunEffects(ctx);
|
|
312
|
+
const response = buildRunResponse(ctx, thrown);
|
|
313
|
+
const headers = headersToObject(response.headers);
|
|
314
|
+
return { thrown, response, cookies, headers, locationState, stateCookieName };
|
|
315
|
+
}
|
|
316
|
+
|
|
204
317
|
/**
|
|
205
318
|
* Build a seeded RequestContext (via {@link createTestRequestContext}) and run
|
|
206
319
|
* `fn` inside it, so code under test that calls `getRequestContext()`,
|
|
@@ -214,16 +327,19 @@ export function snapshotRunEffects<TEnv>(ctx: RequestContext<TEnv>): {
|
|
|
214
327
|
* still needs a real request context to read the cookie and resolve
|
|
215
328
|
* `getRequestContext()`.
|
|
216
329
|
*
|
|
217
|
-
* Returns `{ result, response, cookies, locationState }` so the
|
|
218
|
-
* (Set-Cookie, headers, flash) is assertable without
|
|
219
|
-
* `@internal` `ctx.res` / `ctx.cookies()`. `fn` may be async — the context
|
|
220
|
-
*
|
|
221
|
-
*
|
|
330
|
+
* Returns `{ result, thrown, response, cookies, headers, locationState }` so the
|
|
331
|
+
* action's OUTPUT (Set-Cookie, response headers, flash) is assertable without
|
|
332
|
+
* casting through the `@internal` `ctx.res` / `ctx.cookies()`. `fn` may be async — the context stays
|
|
333
|
+
* active across its awaits (AsyncLocalStorage), and the snapshot is captured
|
|
334
|
+
* whether `fn` returns OR throws. The throw path matters: the most common
|
|
335
|
+
* cookie+flash case is an auth action that sets a cookie + flash then
|
|
336
|
+
* `throw redirect(...)` on success — the thrown redirect is on `thrown` (NOT
|
|
337
|
+
* re-thrown) and its Location plus the cookies are on `response`/`cookies`.
|
|
222
338
|
*
|
|
223
339
|
* @example
|
|
224
340
|
* ```ts
|
|
225
|
-
* const { result, cookies, response } = await runInRequestContext(
|
|
226
|
-
* () => loginAction(input),
|
|
341
|
+
* const { result, cookies, response, thrown } = await runInRequestContext(
|
|
342
|
+
* () => loginAction(input), // sets a session cookie, then `throw redirect("/app")`
|
|
227
343
|
* {
|
|
228
344
|
* env,
|
|
229
345
|
* request: new Request("https://app.test/", {
|
|
@@ -232,6 +348,8 @@ export function snapshotRunEffects<TEnv>(ctx: RequestContext<TEnv>): {
|
|
|
232
348
|
* },
|
|
233
349
|
* );
|
|
234
350
|
* expect(cookies.session).toBe("new-token");
|
|
351
|
+
* expect(headers.location).toBe("/app"); // response headers as a plain object
|
|
352
|
+
* expect((thrown as Response).headers.get("Location")).toBe("/app");
|
|
235
353
|
* expect(response.headers.getSetCookie()).toContainEqual(
|
|
236
354
|
* expect.stringContaining("session="),
|
|
237
355
|
* );
|
|
@@ -241,15 +359,13 @@ export async function runInRequestContext<T, TEnv = unknown>(
|
|
|
241
359
|
fn: (ctx: RequestContext<TEnv>) => T | Promise<T>,
|
|
242
360
|
opts: CreateTestContextOptions<TEnv> = {},
|
|
243
361
|
): Promise<RunInRequestContextResult<T>> {
|
|
244
|
-
const { ctx } = createTestRequestContext<TEnv>(opts);
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
});
|
|
254
|
-
return { result, response, cookies, locationState };
|
|
362
|
+
const { ctx, stateCookieName } = createTestRequestContext<TEnv>(opts);
|
|
363
|
+
let result: T | undefined;
|
|
364
|
+
let thrown: unknown;
|
|
365
|
+
try {
|
|
366
|
+
result = (await runWithRequestContext(ctx, () => fn(ctx))) as T;
|
|
367
|
+
} catch (error) {
|
|
368
|
+
thrown = error;
|
|
369
|
+
}
|
|
370
|
+
return { result, ...buildRunSnapshot(ctx, thrown, stateCookieName) };
|
|
255
371
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Side-effect module: define the webpack-style globals the vendored
|
|
3
|
+
* react-server-dom CLIENT deserializer reads at module-eval time.
|
|
4
|
+
*
|
|
5
|
+
* In a real app the plugin-rsc Vite plugin rewrites `__webpack_require__` ->
|
|
6
|
+
* `__vite_rsc_require__` and `__webpack_require__.u` -> `({}).u`
|
|
7
|
+
* (@vitejs/plugin-rsc `core/plugin.js`). That transform does NOT run in a bare
|
|
8
|
+
* Vitest process, so the vendored client's free `__webpack_require__` /
|
|
9
|
+
* `__webpack_chunk_load__` references would be undefined. We provide minimal
|
|
10
|
+
* shims: `__webpack_require__` routes to the loader installed via
|
|
11
|
+
* `setRequireModule`, and `__webpack_chunk_load__` is a no-op (renderServerTree
|
|
12
|
+
* serializes with empty `chunks`, so no chunk fetch ever happens).
|
|
13
|
+
*
|
|
14
|
+
* MUST be imported (for side effect) BEFORE `@vitejs/plugin-rsc/react/browser`,
|
|
15
|
+
* which is why flight-tree.ts lists it first.
|
|
16
|
+
*/
|
|
17
|
+
const g = globalThis as unknown as {
|
|
18
|
+
__webpack_require__?: ((id: string) => unknown) & { u?: unknown };
|
|
19
|
+
__webpack_chunk_load__?: (chunkId: string) => Promise<unknown>;
|
|
20
|
+
__vite_rsc_client_require__?: (id: string) => unknown;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
if (!g.__webpack_require__) {
|
|
24
|
+
g.__webpack_require__ = (id: string) => g.__vite_rsc_client_require__!(id);
|
|
25
|
+
}
|
|
26
|
+
if (!g.__webpack_chunk_load__) {
|
|
27
|
+
g.__webpack_chunk_load__ = async () => {};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Variable seeding shared by the node/DOM testing tier (internal/context.ts)
|
|
3
|
+
* AND the react-server Flight tier (flight.ts). Depends only on the
|
|
4
|
+
* dependency-free `context-var` module and the env-agnostic state-cookie-name
|
|
5
|
+
* composition (no window/document), so it is safe to import under the
|
|
6
|
+
* `react-server` condition (unlike internal/context.ts, which pulls
|
|
7
|
+
* client/browser modules).
|
|
8
|
+
*/
|
|
9
|
+
import { contextSet, type ContextVar } from "../../context-var.js";
|
|
10
|
+
import { resolveStateCookieName } from "../../router/state-cookie-name.js";
|
|
11
|
+
|
|
12
|
+
export interface StateCookieSeed {
|
|
13
|
+
/**
|
|
14
|
+
* Cookie-name prefix, sanitized then composed with `routerId` exactly like
|
|
15
|
+
* `createRouter({ stateCookiePrefix })`. Defaults to `"rango-state"`.
|
|
16
|
+
*/
|
|
17
|
+
prefix?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Router id; the resolved name is `{sanitizedPrefix}_{sanitizedRouterId}`.
|
|
20
|
+
* Defaults to `"router_0"` (the name a single default router resolves to), so
|
|
21
|
+
* the default name is `rango-state_router_0`.
|
|
22
|
+
*/
|
|
23
|
+
routerId?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Build version used as the rotated value's prefix (`{version}:{timestamp}`).
|
|
26
|
+
* Defaults to `"0"` (resolved inside createRequestContext).
|
|
27
|
+
*/
|
|
28
|
+
version?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function resolveSeededStateCookieName(seed?: StateCookieSeed): string {
|
|
32
|
+
return resolveStateCookieName(seed?.prefix, seed?.routerId ?? "router_0");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type VarsInit =
|
|
36
|
+
| Record<string, unknown>
|
|
37
|
+
| ReadonlyArray<readonly [ContextVar<unknown> | string, unknown]>;
|
|
38
|
+
|
|
39
|
+
export function seedVariables(
|
|
40
|
+
variables: Record<string, unknown>,
|
|
41
|
+
vars?: VarsInit,
|
|
42
|
+
): Record<string, unknown> {
|
|
43
|
+
if (!vars) return variables;
|
|
44
|
+
const entries: Iterable<readonly [ContextVar<unknown> | string, unknown]> =
|
|
45
|
+
Symbol.iterator in (vars as object)
|
|
46
|
+
? (vars as ReadonlyArray<
|
|
47
|
+
readonly [ContextVar<unknown> | string, unknown]
|
|
48
|
+
>)
|
|
49
|
+
: Object.entries(vars as Record<string, unknown>);
|
|
50
|
+
for (const [key, value] of entries) {
|
|
51
|
+
contextSet(variables, key as ContextVar<unknown>, value);
|
|
52
|
+
}
|
|
53
|
+
return variables;
|
|
54
|
+
}
|