@rangojs/router 0.18.0 → 0.19.0
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/dist/types/browser/event-controller.d.ts +3 -3
- package/dist/types/browser/history-state.d.ts +2 -0
- package/dist/types/browser/navigation-transaction.d.ts +20 -2
- package/dist/types/browser/partial-update.d.ts +16 -1
- package/dist/types/browser/prefetch/cache.d.ts +6 -6
- package/dist/types/browser/react/Link.d.ts +19 -9
- package/dist/types/browser/react/context.d.ts +2 -2
- package/dist/types/browser/react/location-state-shared.d.ts +95 -56
- package/dist/types/browser/react/location-state.d.ts +1 -1
- package/dist/types/browser/transition-when.d.ts +80 -0
- package/dist/types/browser/types.d.ts +104 -9
- package/dist/types/build/generate-manifest.d.ts +8 -0
- package/dist/types/cache/background-task.d.ts +24 -2
- package/dist/types/cache/cache-exec-scope.d.ts +31 -0
- package/dist/types/cache/cache-key-utils.d.ts +44 -13
- package/dist/types/cache/cache-policy.d.ts +1 -1
- package/dist/types/cache/cache-runtime.d.ts +7 -0
- package/dist/types/cache/cache-scope.d.ts +117 -35
- package/dist/types/cache/cache-tag.d.ts +24 -4
- package/dist/types/cache/cf/cf-cache-store.d.ts +55 -44
- package/dist/types/cache/cf/cf-shell-frame.d.ts +1 -1
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +0 -15
- package/dist/types/cache/document-cache.d.ts +10 -0
- package/dist/types/cache/handle-snapshot.d.ts +40 -8
- package/dist/types/cache/invalidation-order.d.ts +23 -0
- package/dist/types/cache/request-tag-mask.d.ts +54 -0
- package/dist/types/cache/shell-snapshot.d.ts +88 -141
- package/dist/types/cache/tag-invalidation.d.ts +38 -14
- package/dist/types/cache/types.d.ts +68 -61
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +23 -16
- package/dist/types/client-urls/navigation.d.ts +28 -2
- package/dist/types/client-urls/server-projection.d.ts +5 -2
- package/dist/types/client-urls/types.d.ts +5 -5
- package/dist/types/client.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/route-definition/helpers-types.d.ts +11 -11
- package/dist/types/route-name.d.ts +7 -0
- package/dist/types/router/error-handling.d.ts +1 -1
- package/dist/types/router/intercept-resolution.d.ts +2 -0
- package/dist/types/router/match-context.d.ts +5 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +0 -4
- package/dist/types/router/router-options.d.ts +4 -1
- package/dist/types/router/segment-resolution/loader-cache.d.ts +18 -1
- package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +14 -40
- package/dist/types/router/segment-resolution/mask-nested.d.ts +11 -3
- package/dist/types/router/segment-resolution/view-transition-default.d.ts +7 -5
- package/dist/types/router/transition-when.d.ts +24 -10
- package/dist/types/rsc/attach-transition-when.d.ts +18 -0
- package/dist/types/rsc/full-payload.d.ts +14 -7
- package/dist/types/rsc/response-cache-serve.d.ts +15 -12
- package/dist/types/rsc/shell-build-manifest.d.ts +4 -3
- package/dist/types/rsc/shell-capture-constants.d.ts +77 -20
- package/dist/types/rsc/shell-capture.d.ts +78 -81
- package/dist/types/rsc/shell-serve.d.ts +19 -48
- package/dist/types/rsc/types.d.ts +2 -0
- package/dist/types/segment-system.d.ts +7 -0
- package/dist/types/server/context.d.ts +89 -61
- package/dist/types/server/cookie-store.d.ts +30 -18
- package/dist/types/server/handle-store.d.ts +52 -1
- package/dist/types/server/request-context.d.ts +66 -45
- package/dist/types/server/request-headers.d.ts +44 -0
- package/dist/types/server.d.ts +1 -0
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/index.d.ts +3 -2
- package/dist/types/testing/internal/action-id.d.ts +7 -0
- package/dist/types/testing/location-state-key.d.ts +24 -0
- package/dist/types/testing/render-route.d.ts +17 -1
- package/dist/types/testing/run-transition-when.d.ts +44 -69
- package/dist/types/testing/serve-shell-request.d.ts +7 -6
- package/dist/types/testing/shell-status.d.ts +34 -22
- package/dist/types/transition-when-ref.d.ts +79 -0
- package/dist/types/types/cache-types.d.ts +15 -3
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/segments.d.ts +86 -71
- package/dist/types/urls/path-helper-types.d.ts +3 -4
- package/dist/types/urls/pattern-types.d.ts +3 -4
- package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
- package/dist/vite/index.js +960 -97
- package/package.json +1 -1
- package/skills/cache-guide/SKILL.md +76 -49
- package/skills/caching/SKILL.md +167 -69
- package/skills/client-urls/SKILL.md +1 -1
- package/skills/comparison/references/framework-comparison.md +8 -5
- package/skills/document-cache/SKILL.md +24 -0
- package/skills/hooks/SKILL.md +3 -1
- package/skills/hooks/navigation.md +23 -2
- package/skills/hooks/state.md +98 -6
- package/skills/intercept/SKILL.md +29 -12
- package/skills/loader/SKILL.md +52 -21
- package/skills/middleware/SKILL.md +18 -0
- package/skills/migrate-nextjs/SKILL.md +3 -3
- package/skills/observability/SKILL.md +6 -5
- package/skills/parallel/SKILL.md +1 -1
- package/skills/ppr/SKILL.md +129 -77
- package/skills/prerender/SKILL.md +3 -3
- package/skills/rango/SKILL.md +25 -6
- package/skills/route/SKILL.md +2 -0
- package/skills/testing/SKILL.md +27 -25
- package/skills/testing/cache-prerender.md +9 -9
- package/skills/testing/client-components.md +45 -3
- package/skills/testing/loader.md +3 -2
- package/skills/testing/middleware.md +1 -0
- package/skills/testing/predicates.md +49 -49
- package/skills/testing/render-handler.md +1 -0
- package/skills/testing/serve-shell-request.md +14 -0
- package/skills/testing/server-actions.md +9 -3
- package/skills/theme/SKILL.md +8 -4
- package/skills/typesafety/env-and-bindings.md +8 -0
- package/skills/use-cache/SKILL.md +64 -19
- package/skills/view-transitions/SKILL.md +150 -29
- package/src/__augment-tests__/augmented.check.ts +23 -0
- package/src/browser/event-controller.ts +4 -4
- package/src/browser/history-state.ts +52 -1
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +77 -18
- package/src/browser/navigation-client.ts +4 -3
- package/src/browser/navigation-store.ts +43 -1
- package/src/browser/navigation-transaction.ts +31 -11
- package/src/browser/partial-update.ts +146 -35
- package/src/browser/prefetch/cache.ts +6 -6
- package/src/browser/prefetch/fetch.ts +2 -2
- package/src/browser/react/Link.tsx +38 -33
- package/src/browser/react/NavigationProvider.tsx +2 -2
- package/src/browser/react/context.ts +2 -2
- package/src/browser/react/location-state-shared.ts +241 -49
- package/src/browser/react/location-state.ts +5 -2
- package/src/browser/rsc-router.tsx +4 -0
- package/src/browser/server-action-bridge.ts +84 -15
- package/src/browser/transition-when.ts +205 -0
- package/src/browser/types.ts +124 -10
- package/src/build/generate-manifest.ts +77 -1
- package/src/cache/background-task.ts +55 -2
- package/src/cache/cache-exec-scope.ts +48 -1
- package/src/cache/cache-key-utils.ts +56 -16
- package/src/cache/cache-policy.ts +9 -4
- package/src/cache/cache-runtime.ts +197 -144
- package/src/cache/cache-scope.ts +304 -112
- package/src/cache/cache-tag.ts +105 -13
- package/src/cache/cf/cf-cache-store.ts +152 -76
- package/src/cache/cf/cf-shell-frame.ts +2 -2
- package/src/cache/cf/cf-tag-marker-memo.ts +11 -100
- package/src/cache/document-cache.ts +53 -7
- package/src/cache/handle-snapshot.ts +52 -9
- package/src/cache/invalidation-order.ts +84 -0
- package/src/cache/request-tag-mask.ts +147 -0
- package/src/cache/shell-snapshot.ts +130 -348
- package/src/cache/tag-invalidation.ts +57 -51
- package/src/cache/types.ts +71 -67
- package/src/cache/vercel/vercel-cache-store.ts +61 -58
- package/src/client-urls/client-root.tsx +8 -4
- package/src/client-urls/client-urls.ts +8 -6
- package/src/client-urls/navigation.ts +55 -4
- package/src/client-urls/server-projection.ts +31 -10
- package/src/client-urls/types.ts +12 -5
- package/src/client.tsx +2 -0
- package/src/host/cookie-handler.ts +2 -1
- package/src/index.rsc.ts +3 -0
- package/src/index.ts +3 -0
- package/src/loader-redirect.tsx +7 -3
- package/src/prerender/build-shell-capture.ts +155 -146
- package/src/route-definition/dsl-helpers.ts +2 -0
- package/src/route-definition/helpers-types.ts +11 -11
- package/src/route-name.ts +12 -0
- package/src/router/content-negotiation.ts +4 -1
- package/src/router/error-handling.ts +5 -2
- package/src/router/handler-context.ts +10 -7
- package/src/router/intercept-resolution.ts +11 -1
- package/src/router/loader-resolution.ts +28 -3
- package/src/router/logging.ts +5 -3
- package/src/router/match-api.ts +62 -34
- package/src/router/match-context.ts +5 -0
- package/src/router/match-handlers.ts +9 -29
- package/src/router/match-middleware/background-revalidation.ts +3 -5
- package/src/router/match-middleware/cache-lookup.ts +35 -19
- package/src/router/match-middleware/cache-store.ts +15 -9
- package/src/router/match-result.ts +1 -0
- package/src/router/middleware.ts +2 -5
- package/src/router/navigation-snapshot.ts +6 -5
- package/src/router/prerender-match.ts +4 -0
- package/src/router/request-classification.ts +7 -4
- package/src/router/router-options.ts +4 -1
- package/src/router/segment-resolution/fresh.ts +0 -4
- package/src/router/segment-resolution/loader-cache.ts +150 -25
- package/src/router/segment-resolution/loader-mask.ts +3 -4
- package/src/router/segment-resolution/loader-snapshot.ts +15 -78
- package/src/router/segment-resolution/mask-nested.ts +47 -22
- package/src/router/segment-resolution/revalidation.ts +0 -3
- package/src/router/segment-resolution/view-transition-default.ts +6 -21
- package/src/router/telemetry.ts +6 -3
- package/src/router/transition-when.ts +34 -52
- package/src/router.ts +2 -1
- package/src/rsc/attach-transition-when.ts +47 -0
- package/src/rsc/full-payload.ts +30 -10
- package/src/rsc/handler.ts +12 -10
- package/src/rsc/loader-fetch.ts +2 -1
- package/src/rsc/manifest-init.ts +4 -0
- package/src/rsc/origin-guard.ts +8 -5
- package/src/rsc/progressive-enhancement.ts +7 -30
- package/src/rsc/response-cache-serve.ts +68 -80
- package/src/rsc/response-route-handler.ts +2 -2
- package/src/rsc/rsc-rendering.ts +206 -252
- package/src/rsc/server-action.ts +8 -20
- package/src/rsc/shell-build-manifest.ts +22 -20
- package/src/rsc/shell-capture-constants.ts +136 -20
- package/src/rsc/shell-capture.ts +352 -589
- package/src/rsc/shell-serve.ts +73 -109
- package/src/rsc/ssr-setup.ts +3 -2
- package/src/rsc/types.ts +2 -0
- package/src/segment-system.tsx +40 -7
- package/src/server/context.ts +189 -124
- package/src/server/cookie-store.ts +153 -127
- package/src/server/handle-store.ts +245 -27
- package/src/server/request-context.ts +123 -84
- package/src/server/request-headers.ts +134 -0
- package/src/server.ts +4 -0
- package/src/testing/dispatch.ts +4 -7
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/internal/action-id.ts +25 -0
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/location-state-key.ts +45 -0
- package/src/testing/render-handler.ts +1 -1
- package/src/testing/render-route.tsx +109 -16
- package/src/testing/run-client-revalidate.ts +3 -19
- package/src/testing/run-loader.ts +5 -1
- package/src/testing/run-transition-when.ts +109 -172
- package/src/testing/serve-shell-request.ts +10 -7
- package/src/testing/shell-status.ts +60 -56
- package/src/transition-when-ref.ts +220 -0
- package/src/types/cache-types.ts +15 -3
- package/src/types/index.ts +3 -0
- package/src/types/segments.ts +94 -91
- package/src/urls/path-helper-types.ts +3 -4
- package/src/urls/pattern-types.ts +3 -4
- package/src/vite/discovery/discover-routers.ts +3 -0
- package/src/vite/discovery/shell-prerender-phase.ts +0 -2
- package/src/vite/discovery/virtual-module-codegen.ts +16 -6
- package/src/vite/plugins/transition-when-hoist.ts +1064 -0
- package/src/vite/rango.ts +2 -0
- package/src/vite/router-discovery.ts +75 -5
- package/dist/types/rsc/transition-gate.d.ts +0 -23
- package/src/rsc/transition-gate.ts +0 -86
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
peekLocationStateKey,
|
|
3
|
+
type LocationStateDefinition,
|
|
4
|
+
} from "../browser/react/location-state-shared.js";
|
|
5
|
+
|
|
6
|
+
// Typed history.state slots must carry this prefix: buildHistoryState spreads
|
|
7
|
+
// only `__rsc_ls_*` keys onto history.state (history-state.ts).
|
|
8
|
+
const KEY_PREFIX = "__rsc_ls_";
|
|
9
|
+
|
|
10
|
+
let syntheticKeyCounter = 0;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Assign a location-state key to a `createLocationState()` definition in a unit
|
|
14
|
+
* test that runs without the rango Vite plugin (which injects the key in dev
|
|
15
|
+
* and build). Outside production, a definition without a key throws on first
|
|
16
|
+
* use (`MyState(value)`, `useLocationState(MyState)`, `.read()`).
|
|
17
|
+
*
|
|
18
|
+
* - `withLocationStateKey(GridState, "GridState")` sets `__rsc_ls_GridState`
|
|
19
|
+
* (a name that already starts with `__rsc_ls_` is used as-is).
|
|
20
|
+
* - Without a name, a key already set (by the plugin or an earlier call) is
|
|
21
|
+
* kept; otherwise the definition gets a synthetic `__rsc_ls_test_<n>` key,
|
|
22
|
+
* which then stays on it.
|
|
23
|
+
*
|
|
24
|
+
* Returns the definition.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { withLocationStateKey } from "@rangojs/router/testing";
|
|
29
|
+
* import { GridState } from "../src/location-states";
|
|
30
|
+
*
|
|
31
|
+
* withLocationStateKey(GridState);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function withLocationStateKey<
|
|
35
|
+
TDefinition extends LocationStateDefinition<any, any>,
|
|
36
|
+
>(definition: TDefinition, name?: string): TDefinition {
|
|
37
|
+
if (name !== undefined) {
|
|
38
|
+
definition.__rsc_ls_key = name.startsWith(KEY_PREFIX)
|
|
39
|
+
? name
|
|
40
|
+
: KEY_PREFIX + name;
|
|
41
|
+
} else if (!peekLocationStateKey(definition)) {
|
|
42
|
+
definition.__rsc_ls_key = `${KEY_PREFIX}test_${syntheticKeyCounter++}`;
|
|
43
|
+
}
|
|
44
|
+
return definition;
|
|
45
|
+
}
|
|
@@ -30,6 +30,11 @@
|
|
|
30
30
|
* model is the held-navigation useLoader().isLoading: a `transition` spec
|
|
31
31
|
* plus a pending Promise seeded via navigate(url, { loaders }) commits
|
|
32
32
|
* through production's commitInTransition (browser/partial-update.ts).
|
|
33
|
+
* - A spec's transition({ when }) decides every commit with production's
|
|
34
|
+
* browser decision: navigate() and useRouter().push model kind "push",
|
|
35
|
+
* useRouter().replace kind "replace", refresh() kind "revalidate".
|
|
36
|
+
* Back/forward, action and optimistic clientUrls() swap decisions are
|
|
37
|
+
* e2e territory.
|
|
33
38
|
* What it DOES cover: client hooks that read NavigationProvider /
|
|
34
39
|
* OutletContext — useParams, useReverse, useHref, useMount, useNavigation,
|
|
35
40
|
* useRouter, usePathname, useSearchParams, Outlet/useOutlet nesting and seeded
|
|
@@ -42,6 +47,7 @@
|
|
|
42
47
|
* (the segment chain is wrapped in a MountContext exactly as in production).
|
|
43
48
|
*/
|
|
44
49
|
|
|
50
|
+
import { decideTransitionGatedOff } from "../browser/transition-when.js";
|
|
45
51
|
import { useEffect, type ReactNode, type ComponentType } from "react";
|
|
46
52
|
import type { RenderResult } from "@testing-library/react";
|
|
47
53
|
import { renderSegments } from "../segment-system.js";
|
|
@@ -56,6 +62,7 @@ import {
|
|
|
56
62
|
shouldStartViewTransition,
|
|
57
63
|
} from "../browser/partial-update.js";
|
|
58
64
|
import type {
|
|
65
|
+
NavigateOptionsInternal,
|
|
59
66
|
NavigationStore,
|
|
60
67
|
NavigationBridge,
|
|
61
68
|
UpdateSubscriber,
|
|
@@ -68,8 +75,19 @@ import {
|
|
|
68
75
|
buildParamsFromMatch,
|
|
69
76
|
} from "../router/pattern-matching.js";
|
|
70
77
|
import { normalizeBasename } from "../router/basename.js";
|
|
71
|
-
import type {
|
|
78
|
+
import type {
|
|
79
|
+
LoaderDefinition,
|
|
80
|
+
TransitionConfig,
|
|
81
|
+
TransitionWhenKind,
|
|
82
|
+
} from "../types.js";
|
|
72
83
|
import type { LocationStateDefinition } from "../browser/react/location-state-shared.js";
|
|
84
|
+
import {
|
|
85
|
+
buildHistoryState,
|
|
86
|
+
hasLocationState,
|
|
87
|
+
pushHistoryWithIdx,
|
|
88
|
+
resolveNavigationState,
|
|
89
|
+
} from "../browser/history-state.js";
|
|
90
|
+
import { withLocationStateKey } from "./location-state-key.js";
|
|
73
91
|
import type { Handle } from "../handle.js";
|
|
74
92
|
import type { ThemeConfig } from "../theme/types.js";
|
|
75
93
|
import { resolveThemeConfig } from "../theme/constants.js";
|
|
@@ -93,18 +111,15 @@ export type HandleDataSeed = Record<string, Record<string, unknown[]>>;
|
|
|
93
111
|
const syntheticIds = new WeakMap<object, string>();
|
|
94
112
|
let syntheticIdCounter = 0;
|
|
95
113
|
|
|
96
|
-
function ensureSyntheticId(
|
|
97
|
-
|
|
98
|
-
field: "$$id" | "__rsc_ls_key",
|
|
99
|
-
): string {
|
|
100
|
-
const existing = (handle as Record<string, string>)[field];
|
|
114
|
+
function ensureSyntheticId(loader: object): string {
|
|
115
|
+
const existing = (loader as { $$id?: string }).$$id;
|
|
101
116
|
if (existing) return existing;
|
|
102
|
-
let id = syntheticIds.get(
|
|
117
|
+
let id = syntheticIds.get(loader);
|
|
103
118
|
if (!id) {
|
|
104
119
|
id = `__rango_test_id_${syntheticIdCounter++}`;
|
|
105
|
-
syntheticIds.set(
|
|
120
|
+
syntheticIds.set(loader, id);
|
|
106
121
|
}
|
|
107
|
-
(
|
|
122
|
+
(loader as { $$id?: string }).$$id = id;
|
|
108
123
|
return id;
|
|
109
124
|
}
|
|
110
125
|
|
|
@@ -337,11 +352,20 @@ export interface TestRouterHandle {
|
|
|
337
352
|
* // reader: isLoading true, old data
|
|
338
353
|
* await act(async () => resolve({ name: "Product 2" }));
|
|
339
354
|
* // reader: isLoading false, new data
|
|
355
|
+
*
|
|
356
|
+
* `options.transition: false` is the per-navigation opt-out
|
|
357
|
+
* (`router.push(url, { transition: false })`, `<Link transition={false}>`):
|
|
358
|
+
* the commit is urgent and no transition({ when }) predicate is called.
|
|
340
359
|
*/
|
|
341
360
|
navigate(
|
|
342
361
|
url: string,
|
|
343
|
-
options?: Pick<RenderRouteOptions, "loaders"
|
|
362
|
+
options?: Pick<RenderRouteOptions, "loaders"> & { transition?: boolean },
|
|
344
363
|
): Promise<void>;
|
|
364
|
+
/**
|
|
365
|
+
* Re-render the current location, as router.refresh() does: a spec's
|
|
366
|
+
* transition({ when }) decides it with kind "revalidate" (`to` is `from`).
|
|
367
|
+
*/
|
|
368
|
+
refresh(): Promise<void>;
|
|
345
369
|
/** The current committed pathname. */
|
|
346
370
|
pathname(): string;
|
|
347
371
|
/** The current committed params. */
|
|
@@ -508,7 +532,7 @@ export async function renderRoute(
|
|
|
508
532
|
): Record<string, unknown> => {
|
|
509
533
|
const out = { ...base };
|
|
510
534
|
for (const [loader, data] of loaders ?? []) {
|
|
511
|
-
out[ensureSyntheticId(loader
|
|
535
|
+
out[ensureSyntheticId(loader)] = data;
|
|
512
536
|
}
|
|
513
537
|
return out;
|
|
514
538
|
};
|
|
@@ -517,7 +541,7 @@ export async function renderRoute(
|
|
|
517
541
|
if (typeof window !== "undefined") {
|
|
518
542
|
const stateObj: Record<string, unknown> = {};
|
|
519
543
|
for (const [def, value] of options.locationState ?? []) {
|
|
520
|
-
stateObj[
|
|
544
|
+
stateObj[withLocationStateKey(def).__rsc_ls_key] = value;
|
|
521
545
|
}
|
|
522
546
|
window.history.replaceState(stateObj, "");
|
|
523
547
|
}
|
|
@@ -597,8 +621,30 @@ export async function renderRoute(
|
|
|
597
621
|
let warnedNavLifecycle = false;
|
|
598
622
|
const navigate = async (
|
|
599
623
|
target: string,
|
|
600
|
-
navOptions?: Pick<RenderRouteOptions, "loaders"
|
|
624
|
+
navOptions?: Pick<RenderRouteOptions, "loaders"> & {
|
|
625
|
+
history?: Pick<
|
|
626
|
+
NavigateOptionsInternal,
|
|
627
|
+
"state" | "replace" | "transition"
|
|
628
|
+
>;
|
|
629
|
+
kind?: TransitionWhenKind;
|
|
630
|
+
transition?: boolean;
|
|
631
|
+
},
|
|
601
632
|
): Promise<void> => {
|
|
633
|
+
// A useRouter().push/replace or <Link> navigation (`history`) writes its
|
|
634
|
+
// entry the way production does (navigation-bridge.ts navigate ->
|
|
635
|
+
// navigation-transaction.ts commit): the dev state check, typed entries
|
|
636
|
+
// spread onto history.state, the idx stamp, and the __rsc_locationstate
|
|
637
|
+
// event when the old or new entry carries location state. The URL stays
|
|
638
|
+
// put: renderRoute tracks location on the event controller, not
|
|
639
|
+
// window.location. router.navigate() and refresh() leave history alone.
|
|
640
|
+
const history = navOptions?.history;
|
|
641
|
+
const historyState = history
|
|
642
|
+
? buildHistoryState(
|
|
643
|
+
history.state !== undefined
|
|
644
|
+
? resolveNavigationState(history.state)
|
|
645
|
+
: undefined,
|
|
646
|
+
)
|
|
647
|
+
: undefined;
|
|
602
648
|
// No server fetch, so the navigation lifecycle never starts: the state
|
|
603
649
|
// useNavigation()/useLinkStatus()/useAction() read stays "idle" — asserting
|
|
604
650
|
// a pending/loading/submitting state here proves nothing. Warn once (per
|
|
@@ -623,30 +669,76 @@ export async function renderRoute(
|
|
|
623
669
|
mount,
|
|
624
670
|
);
|
|
625
671
|
const metadata = makeMetadata(nextUrl.pathname, segments, match.params);
|
|
672
|
+
// Production's browser-run transition({ when }) decision
|
|
673
|
+
// (browser/partial-update.ts): the committed location is the source.
|
|
674
|
+
const kind: TransitionWhenKind =
|
|
675
|
+
navOptions?.kind ?? (history?.replace ? "replace" : "push");
|
|
676
|
+
// `transition: false` gates off without calling a predicate
|
|
677
|
+
// (navigation-bridge.ts navigate).
|
|
678
|
+
const transitionOptOut =
|
|
679
|
+
(navOptions?.transition ?? history?.transition) === false;
|
|
680
|
+
const gatedOff =
|
|
681
|
+
transitionOptOut ||
|
|
682
|
+
decideTransitionGatedOff(segments, () => ({
|
|
683
|
+
kind,
|
|
684
|
+
from: {
|
|
685
|
+
url: eventController.getLocation().href,
|
|
686
|
+
params: eventController.getParams(),
|
|
687
|
+
routeName: leaf.name,
|
|
688
|
+
state: window.history.state,
|
|
689
|
+
},
|
|
690
|
+
to: {
|
|
691
|
+
url: nextUrl,
|
|
692
|
+
params: match.params,
|
|
693
|
+
routeName: leaf.name,
|
|
694
|
+
state: historyState ?? null,
|
|
695
|
+
},
|
|
696
|
+
}));
|
|
626
697
|
const root = await renderSegments(segments, {
|
|
627
698
|
outletPending: options.outletPending,
|
|
699
|
+
transitionGatedOff: gatedOff,
|
|
628
700
|
});
|
|
629
701
|
eventController.setLocation(nextUrl);
|
|
630
702
|
eventController.setParams(match.params);
|
|
631
703
|
store.setCurrentUrl(nextUrl.href);
|
|
632
704
|
store.setSegmentIds(segments.map((s) => s.id));
|
|
705
|
+
let notifyLocationState = false;
|
|
706
|
+
if (history) {
|
|
707
|
+
notifyLocationState =
|
|
708
|
+
hasLocationState(window.history.state) ||
|
|
709
|
+
hasLocationState(historyState);
|
|
710
|
+
pushHistoryWithIdx(
|
|
711
|
+
historyState ?? null,
|
|
712
|
+
window.location.href,
|
|
713
|
+
history.replace ?? false,
|
|
714
|
+
);
|
|
715
|
+
}
|
|
633
716
|
const emit: UpdateSubscriber = (update) => store.emitUpdate(update);
|
|
634
717
|
await act(async () => {
|
|
635
718
|
// Production's transition() lane (browser/partial-update.ts). Its
|
|
636
719
|
// same-structure / fully-prefetched / optimistic hold lanes are not
|
|
637
720
|
// modeled: without transition() the commit stays urgent.
|
|
638
|
-
if (shouldStartViewTransition(segments)) {
|
|
721
|
+
if (shouldStartViewTransition(segments, gatedOff)) {
|
|
639
722
|
commitInTransition(emit, segments, { root, metadata }, ["navigation"]);
|
|
640
723
|
} else {
|
|
641
724
|
emit({ root, metadata });
|
|
642
725
|
}
|
|
726
|
+
if (notifyLocationState) {
|
|
727
|
+
window.dispatchEvent(new Event("__rsc_locationstate"));
|
|
728
|
+
}
|
|
643
729
|
});
|
|
644
730
|
};
|
|
645
731
|
|
|
732
|
+
const refresh = (): Promise<void> => {
|
|
733
|
+
const current = new URL(eventController.getLocation().href);
|
|
734
|
+
return navigate(current.pathname + current.search, { kind: "revalidate" });
|
|
735
|
+
};
|
|
736
|
+
|
|
646
737
|
let prefetchRoot: HTMLElement | undefined;
|
|
647
738
|
const bridge: NavigationBridge = {
|
|
648
|
-
navigate: (target) =>
|
|
649
|
-
|
|
739
|
+
navigate: (target, navigateOptions) =>
|
|
740
|
+
navigate(target, { history: navigateOptions ?? {} }),
|
|
741
|
+
refresh: () => refresh(),
|
|
650
742
|
handlePopstate: async () => {},
|
|
651
743
|
registerLinkInterception: () => () => {},
|
|
652
744
|
registerDelegatedPrefetch: () =>
|
|
@@ -704,6 +796,7 @@ export async function renderRoute(
|
|
|
704
796
|
|
|
705
797
|
const router: TestRouterHandle = {
|
|
706
798
|
navigate,
|
|
799
|
+
refresh,
|
|
707
800
|
pathname: () => new URL(eventController.getLocation().href).pathname,
|
|
708
801
|
params: () => eventController.getParams(),
|
|
709
802
|
store,
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* Synchronous: client revalidate functions must be sync.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { makeIsAction
|
|
13
|
+
import { makeIsAction } from "../router/is-action.js";
|
|
14
|
+
import { resolveTestActionId } from "./internal/action-id.js";
|
|
14
15
|
import {
|
|
15
16
|
lockedClientDefault,
|
|
16
17
|
runClientRevalidateChain,
|
|
@@ -23,23 +24,6 @@ import type {
|
|
|
23
24
|
|
|
24
25
|
const DEFAULT_URL = "http://localhost/";
|
|
25
26
|
|
|
26
|
-
function resolveActionId(
|
|
27
|
-
action: ((...args: never[]) => unknown) | string | undefined,
|
|
28
|
-
): string | undefined {
|
|
29
|
-
if (action === undefined) return undefined;
|
|
30
|
-
if (typeof action === "string") return action;
|
|
31
|
-
const id = resolveActionRefId(action);
|
|
32
|
-
if (id === undefined) {
|
|
33
|
-
throw new Error(
|
|
34
|
-
"runClientRevalidate: `action` must be a single imported server action " +
|
|
35
|
-
"(carrying its build-injected id) or an actionId string. The passed " +
|
|
36
|
-
"function has no $id/$$id — outside a built app, pass the id string " +
|
|
37
|
-
'your predicate should match (e.g. "src/actions/cart.ts#addToCart").',
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
return id;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
27
|
/**
|
|
44
28
|
* Options for {@link runClientRevalidate}. Defaults model a same-URL
|
|
45
29
|
* navigation with no action (locked default `false`).
|
|
@@ -80,7 +64,7 @@ export function runClientRevalidate(
|
|
|
80
64
|
const currentParams = opts.currentParams ?? {};
|
|
81
65
|
const nextParams = opts.nextParams ?? currentParams;
|
|
82
66
|
const inAction = opts.action !== undefined;
|
|
83
|
-
const actionId =
|
|
67
|
+
const actionId = resolveTestActionId(opts.action, "runClientRevalidate");
|
|
84
68
|
const defaultShouldRevalidate = lockedClientDefault({
|
|
85
69
|
actionRequest: opts.actionRequest ?? inAction,
|
|
86
70
|
currentParams,
|
|
@@ -51,6 +51,7 @@ import type { CacheProfile } from "../cache/profile-registry.js";
|
|
|
51
51
|
import type { PartialCacheOptions } from "../types/cache-types.js";
|
|
52
52
|
import type { HandlerContext } from "../types/handler-context.js";
|
|
53
53
|
import { resolveLoaderData } from "../router/segment-resolution/loader-cache.js";
|
|
54
|
+
import { armLoaderTagSets } from "../cache/cache-tag.js";
|
|
54
55
|
import {
|
|
55
56
|
createTestRequestContext,
|
|
56
57
|
buildRunSnapshot,
|
|
@@ -385,7 +386,9 @@ function cacheLoaderId(loader: RunnableLoader<unknown>): string {
|
|
|
385
386
|
/**
|
|
386
387
|
* Call the loader: directly, or through its `cache()` binding (the funnel a
|
|
387
388
|
* route's DSL loaders take, loader-cache.ts resolveLoaderData), whose MISS
|
|
388
|
-
* reads the binding through the handler ctx's use().
|
|
389
|
+
* reads the binding through the handler ctx's use(). A binding arms the
|
|
390
|
+
* per-execution loader tag sets first, as a match that resolves one does
|
|
391
|
+
* (match-api.ts, bindsLoaderCache).
|
|
389
392
|
*/
|
|
390
393
|
function invokeLoader<T>(
|
|
391
394
|
loader: RunnableLoader<T>,
|
|
@@ -395,6 +398,7 @@ function invokeLoader<T>(
|
|
|
395
398
|
): Promise<T> {
|
|
396
399
|
const run = () => Promise.resolve(loaderFn(loaderCtx));
|
|
397
400
|
if (!opts.cache) return run();
|
|
401
|
+
armLoaderTagSets();
|
|
398
402
|
const handlerCtx = {
|
|
399
403
|
params: loaderCtx.params,
|
|
400
404
|
use: run,
|
|
@@ -1,195 +1,132 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* runTransitionWhen — unit-test a transition({ when }) predicate
|
|
2
|
+
* runTransitionWhen — unit-test a transition({ when }) browser predicate.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* (
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* assembled `whenContext` so tests can assert the exact fields without reaching
|
|
12
|
-
* into private request-context state.
|
|
4
|
+
* Builds the context and evaluates the predicate through the SAME browser
|
|
5
|
+
* functions the router uses at navigation time (browser/transition-when.ts):
|
|
6
|
+
* a throw counts as false and is logged. `from`/`to` take a URL string or a
|
|
7
|
+
* partial RouteLocation; a `state` given as location-state entries
|
|
8
|
+
* (`[Def(value)]`, what Link/push take) is stored the way a push stores it,
|
|
9
|
+
* so `Def.read(ctx.to)` reads it back. Accepts the predicate or a whole
|
|
10
|
+
* TransitionConfig.
|
|
13
11
|
*
|
|
14
|
-
*
|
|
15
|
-
* match -> render pipeline that wires these together only runs under real RSC
|
|
16
|
-
* rendering (which the Flight primitives do not drive), so without this primitive
|
|
17
|
-
* a consumer could not test their predicate through @rangojs/router/testing.
|
|
18
|
-
*
|
|
19
|
-
* Synchronous: a transition predicate returns a boolean and the gate has no I/O.
|
|
12
|
+
* Synchronous: a transition predicate returns a boolean and has no I/O.
|
|
20
13
|
*/
|
|
21
14
|
|
|
22
15
|
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
import {
|
|
28
|
-
|
|
16
|
+
createTransitionWhenContext,
|
|
17
|
+
evaluateTransitionWhen,
|
|
18
|
+
type RouteLocationInput,
|
|
19
|
+
} from "../browser/transition-when.js";
|
|
20
|
+
import {
|
|
21
|
+
buildHistoryState,
|
|
22
|
+
resolveNavigationState,
|
|
23
|
+
} from "../browser/history-state.js";
|
|
24
|
+
import { isLocationStateEntry } from "../browser/react/location-state-shared.js";
|
|
25
|
+
import { resolveTestActionId } from "./internal/action-id.js";
|
|
29
26
|
import type {
|
|
30
|
-
|
|
27
|
+
RouteLocation,
|
|
31
28
|
TransitionConfig,
|
|
32
29
|
TransitionWhenContext,
|
|
30
|
+
TransitionWhenFn,
|
|
31
|
+
TransitionWhenKind,
|
|
33
32
|
} from "../types/segments.js";
|
|
34
|
-
import type { OnErrorCallback } from "../types/error-types.js";
|
|
35
|
-
import type { EntryData } from "../server/context.js";
|
|
36
|
-
import { evaluatePprTransitionWhen } from "../router/transition-when.js";
|
|
37
|
-
import { invokeOnError } from "../router/error-handling.js";
|
|
38
|
-
import { toURL } from "./to-url.js";
|
|
39
33
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
/** Target route name (`toRouteName`). */
|
|
53
|
-
toRouteName?: string;
|
|
54
|
-
/** Environment bindings surfaced as `env` (and `ctx.env`). */
|
|
55
|
-
env?: TEnv;
|
|
56
|
-
/** Variables readable via the predicate's `get()`. With `ppr`, these model pre-handler middleware/input state. */
|
|
57
|
-
vars?: VarsInit;
|
|
58
|
-
/** Navigation SOURCE url (`currentUrl`): a URL or path string. */
|
|
59
|
-
currentUrl?: string | URL;
|
|
60
|
-
/** Source route params (`currentParams`). */
|
|
61
|
-
currentParams?: Record<string, string>;
|
|
62
|
-
/** Source route name (`fromRouteName`). */
|
|
63
|
-
fromRouteName?: string;
|
|
64
|
-
/** Id of the action that triggered a revalidation (`actionId`). */
|
|
65
|
-
actionId?: string;
|
|
66
|
-
/** Url the action was submitted from (`actionUrl`). */
|
|
67
|
-
actionUrl?: string | URL;
|
|
68
|
-
/** The action's return value (`actionResult`). */
|
|
69
|
-
actionResult?: unknown;
|
|
70
|
-
/** FormData from a form action (`formData`). */
|
|
34
|
+
type ImportedAction = (...args: never[]) => unknown;
|
|
35
|
+
|
|
36
|
+
/** A navigation side: a URL, or a partial RouteLocation. */
|
|
37
|
+
export type RunTransitionWhenLocation =
|
|
38
|
+
| string
|
|
39
|
+
| URL
|
|
40
|
+
| (Partial<Omit<RouteLocation, "url">> & { url?: string | URL });
|
|
41
|
+
|
|
42
|
+
/** The action fields of a `kind: "action"` context. */
|
|
43
|
+
export interface RunTransitionWhenAction {
|
|
44
|
+
/** The triggering action: an imported server action or its actionId. */
|
|
45
|
+
ref?: ImportedAction | string;
|
|
71
46
|
formData?: FormData;
|
|
72
|
-
/**
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
|
|
47
|
+
/** The action's return value on success. */
|
|
48
|
+
result?: unknown;
|
|
49
|
+
/** What a failed action threw (the error lane). */
|
|
50
|
+
error?: unknown;
|
|
76
51
|
}
|
|
77
52
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
dropped: boolean;
|
|
53
|
+
export interface RunTransitionWhenOptions {
|
|
54
|
+
/** Defaults to `"action"` when `action` is given, `"push"` otherwise. */
|
|
55
|
+
kind?: TransitionWhenKind;
|
|
56
|
+
/** The committed location being left. Defaults to `http://localhost/`. */
|
|
57
|
+
from?: RunTransitionWhenLocation;
|
|
58
|
+
/** The destination. Defaults to `from`; always `from` for "action" and "revalidate". */
|
|
59
|
+
to?: RunTransitionWhenLocation;
|
|
86
60
|
/**
|
|
87
|
-
* The
|
|
88
|
-
*
|
|
61
|
+
* The triggering action (like runClientRevalidate's): an imported server
|
|
62
|
+
* action (id via `$id ?? $$id`), an actionId string, or
|
|
63
|
+
* `{ ref, formData, result, error }`.
|
|
89
64
|
*/
|
|
90
|
-
|
|
91
|
-
/** The underlying RequestContext, for additional assertions (`ctx.get(...)`, etc.). */
|
|
92
|
-
ctx: RequestContext<TEnv>;
|
|
65
|
+
action?: ImportedAction | string | RunTransitionWhenAction;
|
|
93
66
|
}
|
|
94
67
|
|
|
95
|
-
export
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
params: opts.params,
|
|
104
|
-
});
|
|
105
|
-
const reqCtx = ctx as unknown as RequestContext<TEnv>;
|
|
68
|
+
export interface RunTransitionWhenResult {
|
|
69
|
+
/** True when the navigation holds (the predicate did not return false or throw, or there is none). */
|
|
70
|
+
applied: boolean;
|
|
71
|
+
/** Inverse of `applied`. */
|
|
72
|
+
gatedOff: boolean;
|
|
73
|
+
/** The context the predicate received. */
|
|
74
|
+
context: TransitionWhenContext;
|
|
75
|
+
}
|
|
106
76
|
|
|
107
|
-
|
|
108
|
-
if (opts.toRouteName !== undefined)
|
|
109
|
-
reqCtx.routeName = opts.toRouteName as RequestContext<TEnv>["routeName"];
|
|
110
|
-
// Source (match-time) data the gate reads for currentUrl/currentParams/fromRouteName.
|
|
111
|
-
if (opts.currentUrl !== undefined)
|
|
112
|
-
reqCtx._gateCurrentUrl = toURL(opts.currentUrl, reqCtx.url);
|
|
113
|
-
if (opts.currentParams !== undefined)
|
|
114
|
-
reqCtx._gateCurrentParams = opts.currentParams;
|
|
115
|
-
if (opts.fromRouteName !== undefined)
|
|
116
|
-
reqCtx._prevRouteKey = opts.fromRouteName;
|
|
117
|
-
// Action data the gate reads at the action-bearing call sites.
|
|
118
|
-
if (opts.actionId !== undefined) reqCtx._gateActionId = opts.actionId;
|
|
119
|
-
if (opts.actionUrl !== undefined)
|
|
120
|
-
reqCtx._gateActionUrl = toURL(opts.actionUrl, reqCtx.url);
|
|
121
|
-
if (opts.actionResult !== undefined)
|
|
122
|
-
reqCtx._gateActionResult = opts.actionResult;
|
|
123
|
-
if (opts.formData !== undefined) reqCtx._gateFormData = opts.formData;
|
|
77
|
+
const DEFAULT_URL = "http://localhost/";
|
|
124
78
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
79
|
+
function toStoredState(state: unknown): unknown {
|
|
80
|
+
if (
|
|
81
|
+
Array.isArray(state) &&
|
|
82
|
+
state.length > 0 &&
|
|
83
|
+
isLocationStateEntry(state[0])
|
|
84
|
+
) {
|
|
85
|
+
return buildHistoryState(resolveNavigationState(state));
|
|
86
|
+
}
|
|
87
|
+
return state;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function toLocation(
|
|
91
|
+
value: RunTransitionWhenLocation | undefined,
|
|
92
|
+
fallback: string | URL,
|
|
93
|
+
): RouteLocationInput {
|
|
94
|
+
if (value === undefined) return { url: fallback };
|
|
95
|
+
if (typeof value === "string" || value instanceof URL) return { url: value };
|
|
96
|
+
return {
|
|
97
|
+
url: value.url ?? fallback,
|
|
98
|
+
params: value.params,
|
|
99
|
+
routeName: value.routeName,
|
|
100
|
+
state: toStoredState(value.state),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
141
103
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
params: reqCtx.params,
|
|
166
|
-
segmentId,
|
|
167
|
-
},
|
|
168
|
-
"RSC",
|
|
169
|
-
),
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
// Ordinary predicates are collected here and evaluated by the gate. PPR
|
|
173
|
-
// predicates keep the decision already evaluated before this step.
|
|
174
|
-
const serialized = applyViewTransitionDefault(
|
|
175
|
-
configForGate,
|
|
176
|
-
undefined,
|
|
177
|
-
"tx-when-seg",
|
|
178
|
-
);
|
|
179
|
-
const segment = {
|
|
180
|
-
id: "tx-when-seg",
|
|
181
|
-
namespace: "r",
|
|
182
|
-
type: "route",
|
|
183
|
-
index: 0,
|
|
184
|
-
component: null,
|
|
185
|
-
transition: serialized,
|
|
186
|
-
} as ResolvedSegment;
|
|
187
|
-
const [gatedSegment] = gateTransitions(
|
|
188
|
-
[segment],
|
|
189
|
-
reqCtx as Parameters<typeof gateTransitions>[1],
|
|
190
|
-
opts.onError,
|
|
191
|
-
);
|
|
192
|
-
const kept = gatedSegment.transition !== undefined;
|
|
193
|
-
return { kept, dropped: !kept, whenContext, ctx: reqCtx };
|
|
104
|
+
export function runTransitionWhen(
|
|
105
|
+
whenOrConfig: TransitionWhenFn | TransitionConfig,
|
|
106
|
+
opts: RunTransitionWhenOptions = {},
|
|
107
|
+
): RunTransitionWhenResult {
|
|
108
|
+
const actionOption =
|
|
109
|
+
typeof opts.action === "function" || typeof opts.action === "string"
|
|
110
|
+
? { ref: opts.action }
|
|
111
|
+
: opts.action;
|
|
112
|
+
const from = toLocation(opts.from, DEFAULT_URL);
|
|
113
|
+
const context = createTransitionWhenContext({
|
|
114
|
+
kind: opts.kind ?? (actionOption ? "action" : "push"),
|
|
115
|
+
from,
|
|
116
|
+
to: opts.to === undefined ? undefined : toLocation(opts.to, from.url),
|
|
117
|
+
...(actionOption
|
|
118
|
+
? {
|
|
119
|
+
action: {
|
|
120
|
+
id: resolveTestActionId(actionOption.ref, "runTransitionWhen"),
|
|
121
|
+
formData: actionOption.formData,
|
|
122
|
+
result: actionOption.result,
|
|
123
|
+
error: actionOption.error,
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
: {}),
|
|
194
127
|
});
|
|
128
|
+
const when =
|
|
129
|
+
typeof whenOrConfig === "function" ? whenOrConfig : whenOrConfig.when;
|
|
130
|
+
const applied = when ? evaluateTransitionWhen(when, context) : true;
|
|
131
|
+
return { applied, gatedOff: !applied, context };
|
|
195
132
|
}
|