waygraph 0.16.0 → 0.16.2
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/cli/commands/browser.d.ts +2 -0
- package/dist/cli/commands/browser.js +109 -0
- package/dist/cli/commands/codemod-stubs.js +90 -2
- package/dist/cli/commands/pilot.d.ts +2 -0
- package/dist/cli/commands/pilot.js +102 -0
- package/dist/cli/commands/session-ops.d.ts +3 -0
- package/dist/cli/commands/session-ops.js +257 -0
- package/dist/cli/commands/session.js +20 -461
- package/dist/cli/run-chain.js +2 -2
- package/dist/cli/usage.d.ts +1 -1
- package/dist/cli/util.js +1 -1
- package/dist/demo/narration/demo-pace.d.ts +2 -2
- package/dist/demo/narration/demo-pace.js +1 -1
- package/dist/demo/narration/device.d.ts +2 -2
- package/dist/demo/narration/merge.d.ts +5 -3
- package/dist/demo/narration/merge.js +3 -7
- package/dist/demo/narration/narration.d.ts +9 -13
- package/dist/demo/narration/narration.js +5 -17
- package/dist/demo/narration/stub-ctx.js +3 -3
- package/dist/demo/narration/todo-dock.d.ts +5 -5
- package/dist/demo/overlay/cursor.d.ts +9 -2
- package/dist/demo/overlay/cursor.js +1 -1
- package/dist/demo/overlay/device-stage.d.ts +15 -2
- package/dist/demo/overlay/inpage/banner.d.ts +2 -1
- package/dist/demo/overlay/inpage/banner.js +3 -3
- package/dist/demo/overlay/inpage/caption.d.ts +1 -1
- package/dist/demo/overlay/inpage/core.d.ts +2 -1
- package/dist/demo/overlay/inpage/core.js +1 -7
- package/dist/demo/overlay/inpage/device-chip.d.ts +1 -1
- package/dist/demo/overlay/inpage/device-chip.js +6 -6
- package/dist/demo/overlay/inpage/network.d.ts +3 -2
- package/dist/demo/overlay/inpage/network.js +1 -12
- package/dist/demo/overlay/inpage/panel-chrome.d.ts +1 -1
- package/dist/demo/overlay/inpage/panel-chrome.js +1 -1
- package/dist/demo/overlay/inpage/stamp.d.ts +1 -1
- package/dist/demo/overlay/inpage/stamp.js +1 -1
- package/dist/demo/overlay/inpage/swipe-zoom.d.ts +1 -1
- package/dist/demo/overlay/inpage/swipe-zoom.js +1 -1
- package/dist/demo/overlay/inpage/todos.d.ts +2 -1
- package/dist/demo/overlay/inpage/todos.js +1 -2
- package/dist/demo/overlay/network-capture.d.ts +2 -2
- package/dist/demo/overlay/network-capture.js +1 -1
- package/dist/demo/overlay/overlay-css.js +1 -55
- package/dist/demo/overlay/overlay-stage.d.ts +19 -11
- package/dist/demo/overlay/overlay.d.ts +2 -2
- package/dist/demo/overlay/overlay.js +3 -2
- package/dist/demo/overlay/rings.d.ts +38 -18
- package/dist/demo/overlay/rings.js +21 -62
- package/dist/demo/overlay/ui/overlay.css +40 -0
- package/dist/demo/stepper/after.js +11 -34
- package/dist/demo/stepper/banner-log.d.ts +4 -6
- package/dist/demo/stepper/banner-log.js +3 -1
- package/dist/demo/stepper/before.js +6 -4
- package/dist/demo/stepper/demo-log.d.ts +26 -9
- package/dist/demo/stepper/demo-log.js +10 -27
- package/dist/demo/stepper/interaction-patch.d.ts +3 -2
- package/dist/demo/stepper/interaction-patch.js +22 -34
- package/dist/demo/stepper/panel-after.d.ts +17 -0
- package/dist/demo/stepper/panel-after.js +166 -0
- package/dist/demo/stepper/panel-before.d.ts +12 -0
- package/dist/demo/stepper/panel-before.js +241 -0
- package/dist/demo/stepper/slides.d.ts +16 -1
- package/dist/demo/stepper/slides.js +21 -5
- package/dist/demo/stepper/step-panels.d.ts +57 -9
- package/dist/demo/stepper/step-panels.js +10 -427
- package/dist/demo/stepper/stepper.js +1 -1
- package/dist/demo/stepper/teardown.d.ts +4 -5
- package/dist/demo/stepper/teardown.js +4 -6
- package/dist/demo/stepper/todo-advance.d.ts +3 -2
- package/dist/demo/stepper/todo-advance.js +2 -2
- package/dist/demo/stepper/view.d.ts +4 -14
- package/dist/demo/types.d.ts +69 -1
- package/dist/demo/verbose-log.js +1 -1
- package/dist/engine/block/action-page.d.ts +53 -0
- package/dist/engine/block/action-page.js +1 -0
- package/dist/engine/block/block.d.ts +24 -90
- package/dist/engine/block/block.js +2 -77
- package/dist/engine/block/branch.d.ts +17 -0
- package/dist/engine/block/branch.js +40 -0
- package/dist/engine/block/dialog.d.ts +13 -0
- package/dist/engine/block/dialog.js +37 -0
- package/dist/engine/block/kinds/method.d.ts +0 -2
- package/dist/engine/block/kinds/method.js +0 -1
- package/dist/engine/block/kinds/nav.js +1 -1
- package/dist/engine/flow/map-branch.d.ts +9 -0
- package/dist/engine/flow/map-branch.js +57 -0
- package/dist/engine/flow/map-builder.d.ts +7 -22
- package/dist/engine/flow/map-builder.js +6 -145
- package/dist/engine/flow/map-steps.d.ts +30 -0
- package/dist/engine/flow/map-steps.js +87 -0
- package/dist/engine/run/run.d.ts +1 -19
- package/dist/engine/run/run.js +0 -88
- package/dist/engine/stub/ring.d.ts +2 -4
- package/dist/engine/stub/ring.js +1 -0
- package/dist/engine/stub/stub.d.ts +16 -8
- package/dist/engine/trait/trait.js +1 -1
- package/dist/index.d.ts +15 -12
- package/dist/index.js +9 -7
- package/dist/live/auto/auto.d.ts +3 -15
- package/dist/live/auto/auto.js +10 -420
- package/dist/live/auto/explore.d.ts +2 -2
- package/dist/live/auto/mem.d.ts +11 -0
- package/dist/live/auto/mem.js +85 -0
- package/dist/live/auto/menu.d.ts +30 -0
- package/dist/live/auto/menu.js +335 -0
- package/dist/live/auto-session/auto-session.d.ts +23 -103
- package/dist/live/auto-session/auto-session.js +100 -370
- package/dist/live/auto-session/console.d.ts +4 -0
- package/dist/live/auto-session/console.js +41 -0
- package/dist/live/auto-session/files.d.ts +44 -0
- package/dist/live/auto-session/files.js +145 -0
- package/dist/live/auto-session/helpers.d.ts +5 -2
- package/dist/live/auto-session/helpers.js +17 -0
- package/dist/live/auto-session/inspect.d.ts +4 -0
- package/dist/live/auto-session/inspect.js +75 -0
- package/dist/live/auto-session/ipc.d.ts +3 -22
- package/dist/live/auto-session/ipc.js +7 -147
- package/dist/live/auto-session/open-browser.d.ts +6 -0
- package/dist/live/auto-session/open-browser.js +47 -0
- package/dist/live/pilot/pilot.d.ts +1 -1
- package/dist/live/pilot/pilot.js +2 -1
- package/dist/live/traverse/traverse.js +6 -200
- package/dist/live/traverse/worker.d.ts +37 -0
- package/dist/live/traverse/worker.js +200 -0
- package/dist/runner/plan/find.js +2 -2
- package/dist/runner/plan/plan.js +1 -1
- package/dist/runner/plan/seed-mem.js +5 -5
- package/dist/runner/plan/spec.js +1 -1
- package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +2 -34
- package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +2 -1
- package/examples/saucedemo/src/flows/checkout.flow.ts +2 -1
- package/examples/saucedemo/src/flows/shop.flow.ts +2 -1
- package/examples/saucedemo/src/stubs/device-showcase.ts +37 -43
- package/examples/saucedemo/src/stubs/finish-order.ts +38 -0
- package/package.json +5 -1
- package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +2 -34
- package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +2 -1
- package/templates/quickstart/src/flows/checkout.flow.ts +2 -1
- package/templates/quickstart/src/flows/shop.flow.ts +2 -1
- package/templates/quickstart/src/stubs/device-showcase.ts +37 -43
- package/templates/quickstart/src/stubs/finish-order.ts +38 -0
- package/templates/scaffold/src/flows/home/hello.flow.ts +3 -0
- package/templates/scaffold/src/map/(app_base)/home/_methods/assert-hello.method.block.ts +1 -9
package/dist/demo/types.d.ts
CHANGED
|
@@ -1,13 +1,51 @@
|
|
|
1
1
|
import type { Page } from "@playwright/test";
|
|
2
|
+
import type { ResolvedHighlight } from "../engine/stub/ring.js";
|
|
3
|
+
import type { BannerPos } from "../engine/stub/stub.js";
|
|
4
|
+
import type { TodoDockState, TodoDockUiResolved } from "../engine/stub/todos.js";
|
|
5
|
+
import type { DeviceState } from "../engine/stub/viewport.js";
|
|
2
6
|
export type WgPage = Page;
|
|
3
7
|
declare module "playwright-core" {
|
|
4
8
|
interface Page {
|
|
5
|
-
|
|
9
|
+
/** The recordVideo canvas size, when recording: the stage a device shell is centered in. */
|
|
10
|
+
__wgVideoViewport?: {
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
} | undefined;
|
|
14
|
+
/** Without a video, the window size at the first device switch (the stage from then on). */
|
|
15
|
+
__wgHeadedStage?: {
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
} | undefined;
|
|
19
|
+
/** The device shell's size, rebuilt after a navigation wipes it. */
|
|
20
|
+
__wgDeviceShell?: {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
} | undefined;
|
|
24
|
+
__wgDesktopFlat?: boolean | undefined;
|
|
25
|
+
__wgDevicePreset?: string | undefined;
|
|
26
|
+
__wgDeviceOrient?: string | undefined;
|
|
27
|
+
/** The real setViewportSize, kept while the demo makes the page's own one do nothing. */
|
|
28
|
+
__wgRealSetViewportSize?: Page["setViewportSize"] | undefined;
|
|
29
|
+
/** The network panel's rows; the panel's DOM does not survive a navigation, this does. */
|
|
30
|
+
__wgNetworkHistory?: NetworkEntry[] | undefined;
|
|
31
|
+
__wgNetworkNeedsRestore?: boolean | undefined;
|
|
32
|
+
__wgNetworkCaptureInstalled?: boolean | undefined;
|
|
33
|
+
__wgNetworkTabVisible?: boolean | undefined;
|
|
6
34
|
}
|
|
7
35
|
interface Keyboard {
|
|
8
36
|
__wgBeatPatched?: boolean;
|
|
9
37
|
}
|
|
10
38
|
}
|
|
39
|
+
/** A value the stepper's parts share and update (the todo dock as it advances, the device, the step's rings). */
|
|
40
|
+
export interface Ref<T> {
|
|
41
|
+
current: T;
|
|
42
|
+
}
|
|
43
|
+
/** What the demo carries between a step's panels, its fill / click theater and a re-paint after navigation. */
|
|
44
|
+
export interface StepperRefs {
|
|
45
|
+
stubBefore: Ref<ResolvedHighlight[]>;
|
|
46
|
+
todoDock: Ref<TodoDockState | undefined>;
|
|
47
|
+
device: Ref<DeviceState | undefined>;
|
|
48
|
+
}
|
|
11
49
|
/** A box on the page, in CSS pixels. */
|
|
12
50
|
export interface WgBox {
|
|
13
51
|
x: number;
|
|
@@ -15,3 +53,33 @@ export interface WgBox {
|
|
|
15
53
|
width: number;
|
|
16
54
|
height: number;
|
|
17
55
|
}
|
|
56
|
+
/** The banner UX installOverlay takes: where it sits and how it behaves. */
|
|
57
|
+
export interface BannerUiState {
|
|
58
|
+
pos?: BannerPos;
|
|
59
|
+
collision?: boolean;
|
|
60
|
+
hidden?: boolean;
|
|
61
|
+
captionFadeMs?: number;
|
|
62
|
+
}
|
|
63
|
+
/** What installOverlay hands every in-page installer (inpage/*.ts) through page.evaluate; auto and pilot pass only what they use. */
|
|
64
|
+
export interface OverlayInstallArgs {
|
|
65
|
+
title?: string | undefined;
|
|
66
|
+
favicon?: string;
|
|
67
|
+
bannerPos?: BannerPos;
|
|
68
|
+
todoPos?: "left" | "right";
|
|
69
|
+
envAutoplay?: boolean;
|
|
70
|
+
todoDockUi?: TodoDockUiResolved;
|
|
71
|
+
bannerUi?: BannerUiState | null;
|
|
72
|
+
tag?: string;
|
|
73
|
+
}
|
|
74
|
+
/** One row of the network panel (inpage/network.ts): a page request or an API Block's call. */
|
|
75
|
+
export interface NetworkEntry {
|
|
76
|
+
method?: string | undefined;
|
|
77
|
+
url?: string | undefined;
|
|
78
|
+
status?: number | undefined;
|
|
79
|
+
ok?: boolean | undefined;
|
|
80
|
+
ms?: number | undefined;
|
|
81
|
+
reqHeaders?: Record<string, string> | undefined;
|
|
82
|
+
resHeaders?: Record<string, string> | undefined;
|
|
83
|
+
reqBody?: string | undefined;
|
|
84
|
+
resBody?: string | undefined;
|
|
85
|
+
}
|
package/dist/demo/verbose-log.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { envFlag } from "../engine/env.js";
|
|
2
2
|
/**
|
|
3
3
|
* One log format for the live overlay surfaces (auto / browser / pilot): `[HH:MM:SS.mmm] waygraph <surface>: ...`, the
|
|
4
|
-
* same shape as the demo
|
|
4
|
+
* same shape as the demo stepper's `stepper/demo-log.ts`. Silenced by WAYGRAPH_JSON=1.
|
|
5
5
|
*/
|
|
6
6
|
function ts() {
|
|
7
7
|
const d = new Date();
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Page } from "@playwright/test";
|
|
2
|
+
/**
|
|
3
|
+
* The `page` a regular Block's `act()` is typed against via `defineBlock` -
|
|
4
|
+
* structurally identical to Playwright's own `Page` (the same real object is
|
|
5
|
+
* passed at runtime; this is a type-only narrowing), except `goto`, `reload`,
|
|
6
|
+
* `goBack`, and `goForward` are re-declared under `@deprecated`. Nothing is
|
|
7
|
+
* blocked - every method is still fully present and callable, and this never
|
|
8
|
+
* causes a build to fail - but a TypeScript-aware editor shows the call
|
|
9
|
+
* struck through with a hover warning the instant it's typed, pointing at
|
|
10
|
+
* `defineNavBlock` instead. `NavBlock`'s own generated `act()` (the one place
|
|
11
|
+
* navigation actually belongs) uses the real `Page`, not this type - that
|
|
12
|
+
* distinction lives entirely inside `defineNavBlock` and never reaches a
|
|
13
|
+
* NavBlock's own author, who never writes its `act()` by hand.
|
|
14
|
+
*/
|
|
15
|
+
import type { MemKey } from "../mem/mem.js";
|
|
16
|
+
export type ActionPage = Omit<Page, "goto" | "reload" | "goBack" | "goForward"> & {
|
|
17
|
+
/** @deprecated Navigation doesn't belong in a regular Block's act() - use defineNavBlock instead. */
|
|
18
|
+
goto: Page["goto"];
|
|
19
|
+
/** @deprecated Navigation doesn't belong in a regular Block's act() - use defineNavBlock instead. */
|
|
20
|
+
reload: Page["reload"];
|
|
21
|
+
/** @deprecated Navigation doesn't belong in a regular Block's act() - use defineNavBlock instead. */
|
|
22
|
+
goBack: Page["goBack"];
|
|
23
|
+
/** @deprecated Navigation doesn't belong in a regular Block's act() - use defineNavBlock instead. */
|
|
24
|
+
goForward: Page["goForward"];
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* One selectable option for a Block whose real choices depend on the live
|
|
28
|
+
* page - "which of these items to add to cart" is only knowable by looking
|
|
29
|
+
* at the DOM, not from static analysis. `waygraph auto` calls
|
|
30
|
+
* {@link Block.instanceOptions} while building its menu and lists one row
|
|
31
|
+
* per option (via `label`) instead of a single generic "block -> checkpoint"
|
|
32
|
+
* row; picking a row does `mem.set(key, value)` before that Block's own
|
|
33
|
+
* `act()` runs, so `act()` just reads mem exactly like any other required
|
|
34
|
+
* key - it never needs to know whether it was driven by a human-authored
|
|
35
|
+
* Flow or a dynamic auto-explore menu.
|
|
36
|
+
* @example { id: "sauce-labs-backpack", label: 'Add "Sauce Labs Backpack" to cart', key: SelectedItem.key, value: { id: "sauce-labs-backpack", name: "Sauce Labs Backpack" } }
|
|
37
|
+
*/
|
|
38
|
+
export interface WaygraphInstanceOption<T = unknown> {
|
|
39
|
+
/** Stable id for this option - used to dedupe menu rows across rebuilds. */
|
|
40
|
+
id: string;
|
|
41
|
+
/** Shown in the menu instead of a generic "block -> checkpoint" line. */
|
|
42
|
+
label: string;
|
|
43
|
+
/** Must be one of this Block's own `requires` keys. */
|
|
44
|
+
key: MemKey<T>;
|
|
45
|
+
/** `mem.set(key, value)` right before this Block's `act()` runs. */
|
|
46
|
+
value: T;
|
|
47
|
+
/**
|
|
48
|
+
* Optional CSS selector for the element this option will act on.
|
|
49
|
+
* `waygraph auto` (headful) rings that element while the menu row is hovered
|
|
50
|
+
* so you can see what a pick would click before committing.
|
|
51
|
+
*/
|
|
52
|
+
highlight?: string;
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,33 +1,10 @@
|
|
|
1
1
|
import type { Page } from "@playwright/test";
|
|
2
2
|
import type { Checkpoint } from "../checkpoint/checkpoint.js";
|
|
3
|
-
import type { StubLifecycleFn } from "../stub/stub.js";
|
|
4
|
-
import type { WaygraphSlidesOrFn } from "../stub/ring.js";
|
|
5
|
-
import { MemPage } from "../mem/mem.js";
|
|
6
3
|
import type { MemKey } from "../mem/mem.js";
|
|
4
|
+
import { MemPage } from "../mem/mem.js";
|
|
5
|
+
import type { StubLifecycleFn } from "../stub/stub.js";
|
|
7
6
|
import type { Trait } from "../trait/trait.js";
|
|
8
|
-
|
|
9
|
-
* The `page` a regular Block's `act()` is typed against via `defineBlock` -
|
|
10
|
-
* structurally identical to Playwright's own `Page` (the same real object is
|
|
11
|
-
* passed at runtime; this is a type-only narrowing), except `goto`, `reload`,
|
|
12
|
-
* `goBack`, and `goForward` are re-declared under `@deprecated`. Nothing is
|
|
13
|
-
* blocked - every method is still fully present and callable, and this never
|
|
14
|
-
* causes a build to fail - but a TypeScript-aware editor shows the call
|
|
15
|
-
* struck through with a hover warning the instant it's typed, pointing at
|
|
16
|
-
* `defineNavBlock` instead. `NavBlock`'s own generated `act()` (the one place
|
|
17
|
-
* navigation actually belongs) uses the real `Page`, not this type - that
|
|
18
|
-
* distinction lives entirely inside `defineNavBlock` and never reaches a
|
|
19
|
-
* NavBlock's own author, who never writes its `act()` by hand.
|
|
20
|
-
*/
|
|
21
|
-
export type ActionPage = Omit<Page, "goto" | "reload" | "goBack" | "goForward"> & {
|
|
22
|
-
/** @deprecated Navigation doesn't belong in a regular Block's act() - use defineNavBlock instead. */
|
|
23
|
-
goto: Page["goto"];
|
|
24
|
-
/** @deprecated Navigation doesn't belong in a regular Block's act() - use defineNavBlock instead. */
|
|
25
|
-
reload: Page["reload"];
|
|
26
|
-
/** @deprecated Navigation doesn't belong in a regular Block's act() - use defineNavBlock instead. */
|
|
27
|
-
goBack: Page["goBack"];
|
|
28
|
-
/** @deprecated Navigation doesn't belong in a regular Block's act() - use defineNavBlock instead. */
|
|
29
|
-
goForward: Page["goForward"];
|
|
30
|
-
};
|
|
7
|
+
import type { ActionPage, WaygraphInstanceOption } from "./action-page.js";
|
|
31
8
|
/**
|
|
32
9
|
* The four-phase pipeline every Block runs. Most Blocks only need `act` + a
|
|
33
10
|
* one-line `resolve` - add `observe` only once a Block genuinely branches, add
|
|
@@ -78,44 +55,6 @@ export interface Instruction<In extends Checkpoint<string>, Out extends Checkpoi
|
|
|
78
55
|
stubAfter?: StubLifecycleFn<Out>;
|
|
79
56
|
/** When the step fails, before the failure panel; written by `Block.stubOnError((ctx) => ...)`. */
|
|
80
57
|
stubOnError?: StubLifecycleFn<Out>;
|
|
81
|
-
/**
|
|
82
|
-
* Multi-step demo captions ("yap" slides) with Next between each -
|
|
83
|
-
* not block lifecycle. Explains a long process; optional `selector` rings
|
|
84
|
-
* while that slide is up. Flow fixtures may override via `slides`.
|
|
85
|
-
* @example slides: [
|
|
86
|
-
* { caption: "Checkout is three screens", tag: "YAP" },
|
|
87
|
-
* { caption: "Overview confirms totals", selector: ".summary_info" },
|
|
88
|
-
* ]
|
|
89
|
-
*/
|
|
90
|
-
slides?: WaygraphSlidesOrFn<Out>;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* One selectable option for a Block whose real choices depend on the live
|
|
94
|
-
* page - "which of these items to add to cart" is only knowable by looking
|
|
95
|
-
* at the DOM, not from static analysis. `waygraph auto` calls
|
|
96
|
-
* {@link Block.instanceOptions} while building its menu and lists one row
|
|
97
|
-
* per option (via `label`) instead of a single generic "block -> checkpoint"
|
|
98
|
-
* row; picking a row does `mem.set(key, value)` before that Block's own
|
|
99
|
-
* `act()` runs, so `act()` just reads mem exactly like any other required
|
|
100
|
-
* key - it never needs to know whether it was driven by a human-authored
|
|
101
|
-
* Flow or a dynamic auto-explore menu.
|
|
102
|
-
* @example { id: "sauce-labs-backpack", label: 'Add "Sauce Labs Backpack" to cart', key: SelectedItem.key, value: { id: "sauce-labs-backpack", name: "Sauce Labs Backpack" } }
|
|
103
|
-
*/
|
|
104
|
-
export interface WaygraphInstanceOption<T = unknown> {
|
|
105
|
-
/** Stable id for this option - used to dedupe menu rows across rebuilds. */
|
|
106
|
-
id: string;
|
|
107
|
-
/** Shown in the menu instead of a generic "block -> checkpoint" line. */
|
|
108
|
-
label: string;
|
|
109
|
-
/** Must be one of this Block's own `requires` keys. */
|
|
110
|
-
key: MemKey<T>;
|
|
111
|
-
/** `mem.set(key, value)` right before this Block's `act()` runs. */
|
|
112
|
-
value: T;
|
|
113
|
-
/**
|
|
114
|
-
* Optional CSS selector for the element this option will act on.
|
|
115
|
-
* `waygraph auto` (headful) rings that element while the menu row is hovered
|
|
116
|
-
* so you can see what a pick would click before committing.
|
|
117
|
-
*/
|
|
118
|
-
highlight?: string;
|
|
119
58
|
}
|
|
120
59
|
/**
|
|
121
60
|
* A Block wraps one instruction. Its Observed type is an internal detail, erased here.
|
|
@@ -140,7 +79,7 @@ export interface Block<In extends Checkpoint<string>, Out extends Checkpoint<str
|
|
|
140
79
|
* A human-readable sentence describing what this Block actually tests or
|
|
141
80
|
* does - "the outsider case: a user outside a request's audience is
|
|
142
81
|
* denied its detail page," not a restatement of the code. Purely
|
|
143
|
-
* additive, optional narration for a human watching a run (
|
|
82
|
+
* additive, optional narration for a human watching a run (demo
|
|
144
83
|
* tooling shows it before the Block runs) - never read by
|
|
145
84
|
* `runGraph`/`connect`/anything that decides behavior. A Block with none
|
|
146
85
|
* just shows its `name` instead.
|
|
@@ -246,24 +185,29 @@ export type DefinedBlock<In extends Checkpoint<string>, Out extends Checkpoint<s
|
|
|
246
185
|
* and no one Checkpoint type covers every Block: `any` is that "some Block", written once here.
|
|
247
186
|
*/
|
|
248
187
|
export type SomeBlock = Block<any, any>;
|
|
188
|
+
/** The Checkpoint a Block resolves to. */
|
|
189
|
+
type BlockOut<B> = B extends Block<infer _In, infer Out> ? Out : never;
|
|
249
190
|
/** Any {@link DefinedBlock} (see {@link SomeBlock}). */
|
|
250
191
|
export type SomeDefinedBlock = DefinedBlock<any, any>;
|
|
251
192
|
export declare function findBlockIndex(middle: readonly SomeBlock[], block: SomeBlock | string | number): number;
|
|
252
193
|
/**
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
* a route can lead anywhere, including back through itself, so there is no fixed
|
|
261
|
-
* Out type to express; state the expected outcomes to `runGraph` via `terminals`
|
|
262
|
-
* instead.
|
|
194
|
+
* Strips `withVerify`/`modVerify`/`modVerifyAll` off a Block, keeping everything
|
|
195
|
+
* else - `name`, `instruction`, `next`, `requires`. Every function below that
|
|
196
|
+
* returns a new Block calls this on its input first, then rebuilds through
|
|
197
|
+
* `defineBlock`. Without it, a Block's attached methods close over the shape it
|
|
198
|
+
* had *when they were attached* - call `.withVerify()` on a Block that
|
|
199
|
+
* `branch()` added routing to afterward, and without this step the result would
|
|
200
|
+
* silently drop that routing, since the stale closure never knew about it.
|
|
263
201
|
*/
|
|
264
|
-
export declare function
|
|
265
|
-
|
|
266
|
-
|
|
202
|
+
export declare function stripMethods<In extends Checkpoint<string>, Out extends Checkpoint<string>>(block: Block<In, Out>): {
|
|
203
|
+
name: string;
|
|
204
|
+
description?: Block<In, Out>["description"];
|
|
205
|
+
instruction: Block<In, Out>["instruction"];
|
|
206
|
+
next?: Block<In, Out>["next"];
|
|
207
|
+
requires?: Block<In, Out>["requires"];
|
|
208
|
+
routes?: Block<In, Out>["routes"];
|
|
209
|
+
instanceOptions?: Block<In, Out>["instanceOptions"];
|
|
210
|
+
};
|
|
267
211
|
/** Copy non-enumerable waygraph markers so decorate/withVerify keep map() kind checks. */
|
|
268
212
|
export declare function copyWaygraphRuntime(from: object, to: object): void;
|
|
269
213
|
/**
|
|
@@ -297,18 +241,7 @@ export declare function defineBlock<In extends Checkpoint<string>, Out extends C
|
|
|
297
241
|
* Wrap the one Block you want to change, in place, where a flow uses it:
|
|
298
242
|
* `map().gotoPage(NavLoginBlock).method(withVerify(AddToCartBlock, [...]))`.
|
|
299
243
|
*/
|
|
300
|
-
export declare function withVerify<B extends SomeBlock>(block: B, verify: Trait[] | ((out:
|
|
301
|
-
/**
|
|
302
|
-
* Answers the browser's own dialog (`confirm`, `alert`, `prompt`, `beforeunload`) that this Block's action
|
|
303
|
-
* triggers. A dialog freezes the page until it is answered, so the answer has to be armed before the click -
|
|
304
|
-
* this does that without folding it into the Block's own `act`. The dialog's text can be kept in mem.
|
|
305
|
-
* @example withDialog(CancelBlock, { accept: true, into: LeaveWarning })
|
|
306
|
-
*/
|
|
307
|
-
export declare function withDialog<B extends SomeBlock>(block: B, options?: {
|
|
308
|
-
accept?: boolean;
|
|
309
|
-
into?: MemKey<string>;
|
|
310
|
-
promptText?: string;
|
|
311
|
-
}): B;
|
|
244
|
+
export declare function withVerify<B extends SomeBlock>(block: B, verify: Trait[] | ((out: BlockOut<B>) => Trait[])): B;
|
|
312
245
|
/**
|
|
313
246
|
* Replaces one Trait inside `block`'s verify list, leaving every other check
|
|
314
247
|
* untouched - the v1/v2/legacy-variant case, where one check needs a different
|
|
@@ -333,3 +266,4 @@ export declare function modVerify<In extends Checkpoint<string>, Out extends Che
|
|
|
333
266
|
* same fail-loud-on-unknown-name behavior holds for every entry.
|
|
334
267
|
*/
|
|
335
268
|
export declare function modVerifyAll<In extends Checkpoint<string>, Out extends Checkpoint<string>>(block: Block<In, Out>, patches: Record<string, Trait["check"] | Trait>): DefinedBlock<In, Out>;
|
|
269
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MSG_FLOW_BLOCK_NOT_FOUND_BY_INDEX, MSG_FLOW_BLOCK_NOT_FOUND_BY_NAME, MSG_MODVERIFY_FUNCTION_FORM, MSG_MODVERIFY_NOT_FOUND_BY_INDEX, MSG_MODVERIFY_NOT_FOUND_BY_NAME, WaygraphError, } from "../errors.js";
|
|
2
2
|
export function findBlockIndex(middle, block) {
|
|
3
3
|
if (typeof block === "number") {
|
|
4
4
|
if (block < 0 || block >= middle.length) {
|
|
@@ -13,45 +13,6 @@ export function findBlockIndex(middle, block) {
|
|
|
13
13
|
}
|
|
14
14
|
return index;
|
|
15
15
|
}
|
|
16
|
-
/**
|
|
17
|
-
* Attaches routing to `block` without touching its act/observe/resolve/verify -
|
|
18
|
-
* `routes` must cover every tag of `block`'s output (a missing route is a compile
|
|
19
|
-
* error), and the returned Block runs exactly as `block` always did, just followed
|
|
20
|
-
* by whichever next Block the resolved tag routes to. `null` for a tag means that
|
|
21
|
-
* tag is terminal - the run stops there, same as a Block with no routing at all.
|
|
22
|
-
* A route pointing back at `block` itself is a self-loop; see `runGraph`'s
|
|
23
|
-
* `maxSteps` for its safety net. The return type is deliberately `Block<In, any>` -
|
|
24
|
-
* a route can lead anywhere, including back through itself, so there is no fixed
|
|
25
|
-
* Out type to express; state the expected outcomes to `runGraph` via `terminals`
|
|
26
|
-
* instead.
|
|
27
|
-
*/
|
|
28
|
-
export function branch(block, routes) {
|
|
29
|
-
const base = stripMethods(block);
|
|
30
|
-
let routed;
|
|
31
|
-
const routesAsData = {};
|
|
32
|
-
for (const [tag, target] of Object.entries(routes)) {
|
|
33
|
-
routesAsData[tag] = target === null ? null : target === block ? base.name : target.name;
|
|
34
|
-
}
|
|
35
|
-
routed = defineBlock({
|
|
36
|
-
...base,
|
|
37
|
-
next: (checkpoint) => {
|
|
38
|
-
const target = routes[checkpoint.__state];
|
|
39
|
-
if (target === undefined || target === null)
|
|
40
|
-
return undefined;
|
|
41
|
-
// A route naming `block` itself (the only self-reference in scope when
|
|
42
|
-
// `routes` was written) means "loop back with routing intact" - substitute
|
|
43
|
-
// the routed Block, not the original with no `next` of its own.
|
|
44
|
-
return target === block ? routed : target;
|
|
45
|
-
},
|
|
46
|
-
routes: routesAsData,
|
|
47
|
-
});
|
|
48
|
-
// Without this, branch()'s output carried no __waygraphKind/__waygraphSalt at all (defineBlock
|
|
49
|
-
// never stamps them - factories do, after defineBlock returns), so a branched Block could never
|
|
50
|
-
// pass MapBuilder's assertMapKind/assertMapSalt checks. Real bug: branching was unusable from the
|
|
51
|
-
// Waygraph Map, only from a raw runGraph() call.
|
|
52
|
-
copyWaygraphRuntime(block, routed);
|
|
53
|
-
return routed;
|
|
54
|
-
}
|
|
55
16
|
/**
|
|
56
17
|
* Strips `withVerify`/`modVerify`/`modVerifyAll` off a Block, keeping everything
|
|
57
18
|
* else - `name`, `instruction`, `next`, `requires`. Every function below that
|
|
@@ -61,7 +22,7 @@ export function branch(block, routes) {
|
|
|
61
22
|
* `branch()` added routing to afterward, and without this step the result would
|
|
62
23
|
* silently drop that routing, since the stale closure never knew about it.
|
|
63
24
|
*/
|
|
64
|
-
function stripMethods(block) {
|
|
25
|
+
export function stripMethods(block) {
|
|
65
26
|
return {
|
|
66
27
|
name: block.name,
|
|
67
28
|
instruction: block.instruction,
|
|
@@ -174,42 +135,6 @@ function withStubPhase(block, phase, stub) {
|
|
|
174
135
|
copyWaygraphRuntime(block, next);
|
|
175
136
|
return next;
|
|
176
137
|
}
|
|
177
|
-
/**
|
|
178
|
-
* Answers the browser's own dialog (`confirm`, `alert`, `prompt`, `beforeunload`) that this Block's action
|
|
179
|
-
* triggers. A dialog freezes the page until it is answered, so the answer has to be armed before the click -
|
|
180
|
-
* this does that without folding it into the Block's own `act`. The dialog's text can be kept in mem.
|
|
181
|
-
* @example withDialog(CancelBlock, { accept: true, into: LeaveWarning })
|
|
182
|
-
*/
|
|
183
|
-
export function withDialog(block, options = {}) {
|
|
184
|
-
const base = stripMethods(block);
|
|
185
|
-
const act = base.instruction.act.bind(base.instruction);
|
|
186
|
-
const next = defineBlock({
|
|
187
|
-
...base,
|
|
188
|
-
instruction: {
|
|
189
|
-
...base.instruction,
|
|
190
|
-
async act(page, input, mem) {
|
|
191
|
-
const answer = async (dialog) => {
|
|
192
|
-
if (options.into)
|
|
193
|
-
mem.set(options.into, dialog.message());
|
|
194
|
-
if (options.accept === false)
|
|
195
|
-
await dialog.dismiss();
|
|
196
|
-
else
|
|
197
|
-
await dialog.accept(options.promptText);
|
|
198
|
-
};
|
|
199
|
-
const raw = page;
|
|
200
|
-
raw.once("dialog", answer);
|
|
201
|
-
try {
|
|
202
|
-
await act(page, input, mem);
|
|
203
|
-
}
|
|
204
|
-
finally {
|
|
205
|
-
raw.off("dialog", answer);
|
|
206
|
-
}
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
|
-
});
|
|
210
|
-
copyWaygraphRuntime(block, next);
|
|
211
|
-
return next;
|
|
212
|
-
}
|
|
213
138
|
/**
|
|
214
139
|
* Replaces one Trait inside `block`'s verify list, leaving every other check
|
|
215
140
|
* untouched - the v1/v2/legacy-variant case, where one check needs a different
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Checkpoint } from "../checkpoint/checkpoint.js";
|
|
2
|
+
import { Block, DefinedBlock } from "./block.js";
|
|
3
|
+
/**
|
|
4
|
+
* Attaches routing to `block` without touching its act/observe/resolve/verify -
|
|
5
|
+
* `routes` must cover every tag of `block`'s output (a missing route is a compile
|
|
6
|
+
* error), and the returned Block runs exactly as `block` always did, just followed
|
|
7
|
+
* by whichever next Block the resolved tag routes to. `null` for a tag means that
|
|
8
|
+
* tag is terminal - the run stops there, same as a Block with no routing at all.
|
|
9
|
+
* A route pointing back at `block` itself is a self-loop; see `runGraph`'s
|
|
10
|
+
* `maxSteps` for its safety net. The return type is deliberately `Block<In, any>` -
|
|
11
|
+
* a route can lead anywhere, including back through itself, so there is no fixed
|
|
12
|
+
* Out type to express; state the expected outcomes to `runGraph` via `terminals`
|
|
13
|
+
* instead.
|
|
14
|
+
*/
|
|
15
|
+
export declare function branch<In extends Checkpoint<string>, Out extends Checkpoint<string>>(block: Block<In, Out>, routes: {
|
|
16
|
+
[K in Out["__state"]]: Block<Extract<Out, Checkpoint<K>>, any> | null;
|
|
17
|
+
}): DefinedBlock<In, any>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { copyWaygraphRuntime, defineBlock, stripMethods } from "./block.js";
|
|
2
|
+
/**
|
|
3
|
+
* Attaches routing to `block` without touching its act/observe/resolve/verify -
|
|
4
|
+
* `routes` must cover every tag of `block`'s output (a missing route is a compile
|
|
5
|
+
* error), and the returned Block runs exactly as `block` always did, just followed
|
|
6
|
+
* by whichever next Block the resolved tag routes to. `null` for a tag means that
|
|
7
|
+
* tag is terminal - the run stops there, same as a Block with no routing at all.
|
|
8
|
+
* A route pointing back at `block` itself is a self-loop; see `runGraph`'s
|
|
9
|
+
* `maxSteps` for its safety net. The return type is deliberately `Block<In, any>` -
|
|
10
|
+
* a route can lead anywhere, including back through itself, so there is no fixed
|
|
11
|
+
* Out type to express; state the expected outcomes to `runGraph` via `terminals`
|
|
12
|
+
* instead.
|
|
13
|
+
*/
|
|
14
|
+
export function branch(block, routes) {
|
|
15
|
+
const base = stripMethods(block);
|
|
16
|
+
let routed;
|
|
17
|
+
const routesAsData = {};
|
|
18
|
+
for (const [tag, target] of Object.entries(routes)) {
|
|
19
|
+
routesAsData[tag] = target === null ? null : target === block ? base.name : target.name;
|
|
20
|
+
}
|
|
21
|
+
routed = defineBlock({
|
|
22
|
+
...base,
|
|
23
|
+
next: (checkpoint) => {
|
|
24
|
+
const target = routes[checkpoint.__state];
|
|
25
|
+
if (target === undefined || target === null)
|
|
26
|
+
return undefined;
|
|
27
|
+
// A route naming `block` itself (the only self-reference in scope when
|
|
28
|
+
// `routes` was written) means "loop back with routing intact" - substitute
|
|
29
|
+
// the routed Block, not the original with no `next` of its own.
|
|
30
|
+
return target === block ? routed : target;
|
|
31
|
+
},
|
|
32
|
+
routes: routesAsData,
|
|
33
|
+
});
|
|
34
|
+
// Without this, branch()'s output carried no __waygraphKind/__waygraphSalt at all (defineBlock
|
|
35
|
+
// never stamps them - factories do, after defineBlock returns), so a branched Block could never
|
|
36
|
+
// pass MapBuilder's assertMapKind/assertMapSalt checks. Real bug: branching was unusable from the
|
|
37
|
+
// Waygraph Map, only from a raw runGraph() call.
|
|
38
|
+
copyWaygraphRuntime(block, routed);
|
|
39
|
+
return routed;
|
|
40
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MemKey } from "../mem/mem.js";
|
|
2
|
+
import { SomeBlock } from "./block.js";
|
|
3
|
+
/**
|
|
4
|
+
* Answers the browser's own dialog (`confirm`, `alert`, `prompt`, `beforeunload`) that this Block's action
|
|
5
|
+
* triggers. A dialog freezes the page until it is answered, so the answer has to be armed before the click -
|
|
6
|
+
* this does that without folding it into the Block's own `act`. The dialog's text can be kept in mem.
|
|
7
|
+
* @example withDialog(CancelBlock, { accept: true, into: LeaveWarning })
|
|
8
|
+
*/
|
|
9
|
+
export declare function withDialog<B extends SomeBlock>(block: B, options?: {
|
|
10
|
+
accept?: boolean;
|
|
11
|
+
into?: MemKey<string>;
|
|
12
|
+
promptText?: string;
|
|
13
|
+
}): B;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { copyWaygraphRuntime, defineBlock, stripMethods } from "./block.js";
|
|
2
|
+
/**
|
|
3
|
+
* Answers the browser's own dialog (`confirm`, `alert`, `prompt`, `beforeunload`) that this Block's action
|
|
4
|
+
* triggers. A dialog freezes the page until it is answered, so the answer has to be armed before the click -
|
|
5
|
+
* this does that without folding it into the Block's own `act`. The dialog's text can be kept in mem.
|
|
6
|
+
* @example withDialog(CancelBlock, { accept: true, into: LeaveWarning })
|
|
7
|
+
*/
|
|
8
|
+
export function withDialog(block, options = {}) {
|
|
9
|
+
const base = stripMethods(block);
|
|
10
|
+
const act = base.instruction.act.bind(base.instruction);
|
|
11
|
+
const next = defineBlock({
|
|
12
|
+
...base,
|
|
13
|
+
instruction: {
|
|
14
|
+
...base.instruction,
|
|
15
|
+
async act(page, input, mem) {
|
|
16
|
+
const answer = async (dialog) => {
|
|
17
|
+
if (options.into)
|
|
18
|
+
mem.set(options.into, dialog.message());
|
|
19
|
+
if (options.accept === false)
|
|
20
|
+
await dialog.dismiss();
|
|
21
|
+
else
|
|
22
|
+
await dialog.accept(options.promptText);
|
|
23
|
+
};
|
|
24
|
+
const raw = page;
|
|
25
|
+
raw.once("dialog", answer);
|
|
26
|
+
try {
|
|
27
|
+
await act(page, input, mem);
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
raw.off("dialog", answer);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
copyWaygraphRuntime(block, next);
|
|
36
|
+
return next;
|
|
37
|
+
}
|
|
@@ -39,8 +39,6 @@ export interface AssertBlockOptions<Out extends Checkpoint<string> = Checkpoint<
|
|
|
39
39
|
verify: Trait[] | ((out: Out) => Trait[]);
|
|
40
40
|
/** Mem keys this assertion's `verify` reads (e.g. a mem-aware Trait) - checked by preflight before the flow runs. */
|
|
41
41
|
requires?: readonly MemKey<unknown>[];
|
|
42
|
-
/** Multi-step yap slides (demo only) - same field `Instruction.slides` exposes elsewhere. */
|
|
43
|
-
slides?: import("../../stub/ring.js").WaygraphSlidesOrFn<Out>;
|
|
44
42
|
}
|
|
45
43
|
/**
|
|
46
44
|
* Sugar for a self-loop-only "pure assertion" Block: no hand-written
|
|
@@ -28,7 +28,7 @@ export function defineNavBlock(options) {
|
|
|
28
28
|
await page.goto(url);
|
|
29
29
|
}
|
|
30
30
|
else {
|
|
31
|
-
// Always Locator.click() so
|
|
31
|
+
// Always Locator.click() so the demo stepper's cursor/pulse patch
|
|
32
32
|
// (instrumentInteractionHighlighting) sees NavBlock click-nav -
|
|
33
33
|
// never page.click() / goto teleport.
|
|
34
34
|
const selector = typeof options.click === "function" ? options.click(mem) : options.click;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Checkpoint } from "../checkpoint/checkpoint.js";
|
|
2
|
+
import type { Flow } from "./flow.js";
|
|
3
|
+
/**
|
|
4
|
+
* Builds the `Flow` object `MapBuilder.branch()` returns: run `before`, then dispatch on the
|
|
5
|
+
* resolved tag into whichever `routes` entry matches (same page/context/mem), or return that
|
|
6
|
+
* Checkpoint as-is when its route is `null`. Factored out so `withBlockVerify`/`modBlockVerify`
|
|
7
|
+
* can patch `before` and rebuild the same branched shape, exactly like a plain Flow's own.
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildBranchedFlow<Out extends Checkpoint<string>>(before: Flow<Out>, routes: Record<string, Flow<Checkpoint<string>> | null>): Flow<Checkpoint<string>>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { MSG_MAP_BRANCH_SHARED_SESSION_UNSUPPORTED, WaygraphError } from "../errors.js";
|
|
2
|
+
import { MemPage } from "../mem/mem.js";
|
|
3
|
+
/**
|
|
4
|
+
* Builds the `Flow` object `MapBuilder.branch()` returns: run `before`, then dispatch on the
|
|
5
|
+
* resolved tag into whichever `routes` entry matches (same page/context/mem), or return that
|
|
6
|
+
* Checkpoint as-is when its route is `null`. Factored out so `withBlockVerify`/`modBlockVerify`
|
|
7
|
+
* can patch `before` and rebuild the same branched shape, exactly like a plain Flow's own.
|
|
8
|
+
*/
|
|
9
|
+
export function buildBranchedFlow(before, routes) {
|
|
10
|
+
const run = (async (contextOrMem, memOrConfig, options) => {
|
|
11
|
+
if (contextOrMem instanceof MemPage) {
|
|
12
|
+
throw new WaygraphError("WG_MAP_BRANCH_SHARED_SESSION_UNSUPPORTED", MSG_MAP_BRANCH_SHARED_SESSION_UNSUPPORTED);
|
|
13
|
+
}
|
|
14
|
+
const context = contextOrMem;
|
|
15
|
+
const mem = memOrConfig;
|
|
16
|
+
const gotOwnPage = options?.page === undefined;
|
|
17
|
+
const page = options?.page ?? (await context.newPage());
|
|
18
|
+
const closeOnFinish = options?.closeOnFinish ?? gotOwnPage;
|
|
19
|
+
const first = (await before.run(context, mem, { ...options, page, closeOnFinish: false }));
|
|
20
|
+
const branchFlow = routes[first.result.__state];
|
|
21
|
+
if (!branchFlow) {
|
|
22
|
+
if (options?.closeOnFinish === false)
|
|
23
|
+
return { result: first.result, page: first.page };
|
|
24
|
+
if (closeOnFinish)
|
|
25
|
+
await first.page.close();
|
|
26
|
+
return first.result;
|
|
27
|
+
}
|
|
28
|
+
return branchFlow.run(context, mem, { ...options, page: first.page, closeOnFinish });
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
resetSession: before.resetSession,
|
|
32
|
+
config: before.config,
|
|
33
|
+
...(before.expectedFailureReason !== undefined ? { expectedFailureReason: before.expectedFailureReason } : {}),
|
|
34
|
+
...(before.demoPace !== undefined ? { demoPace: before.demoPace } : {}),
|
|
35
|
+
...(before.highlightStyle !== undefined ? { highlightStyle: before.highlightStyle } : {}),
|
|
36
|
+
...(before.memStub !== undefined ? { memStub: before.memStub } : {}),
|
|
37
|
+
// Read by branchRoutes()/branchInfo() (branch-regression.ts) - real, ORDINARY (enumerable)
|
|
38
|
+
// properties, not hidden ones, specifically so a `{ ...flow, ... }` spread (withSettings) carries them
|
|
39
|
+
// through instead of silently dropping them -
|
|
40
|
+
// the same class of bug branch()'s missing copyWaygraphRuntime call already was.
|
|
41
|
+
__wgBranch: { before, routes },
|
|
42
|
+
// Static introspection (`waygraph graph`/list): the fixed prefix, plus every branch's own
|
|
43
|
+
// Blocks prefixed by the tag that leads to them - can't know at analysis time which one a real
|
|
44
|
+
// run takes, so this shows all of them rather than none.
|
|
45
|
+
blocks: () => [
|
|
46
|
+
...before.blocks(),
|
|
47
|
+
...Object.entries(routes).flatMap(([tag, f]) => f ? f.blocks().map((b) => ({ ...b, name: `${tag} -> ${b.name}` })) : []),
|
|
48
|
+
],
|
|
49
|
+
run,
|
|
50
|
+
withBlockVerify(block, verify) {
|
|
51
|
+
return buildBranchedFlow(before.withBlockVerify(block, verify), routes);
|
|
52
|
+
},
|
|
53
|
+
modBlockVerify(block, nameOrIndex, newCheck) {
|
|
54
|
+
return buildBranchedFlow(before.modBlockVerify(block, nameOrIndex, newCheck), routes);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|