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.
Files changed (143) hide show
  1. package/dist/cli/commands/browser.d.ts +2 -0
  2. package/dist/cli/commands/browser.js +109 -0
  3. package/dist/cli/commands/codemod-stubs.js +90 -2
  4. package/dist/cli/commands/pilot.d.ts +2 -0
  5. package/dist/cli/commands/pilot.js +102 -0
  6. package/dist/cli/commands/session-ops.d.ts +3 -0
  7. package/dist/cli/commands/session-ops.js +257 -0
  8. package/dist/cli/commands/session.js +20 -461
  9. package/dist/cli/run-chain.js +2 -2
  10. package/dist/cli/usage.d.ts +1 -1
  11. package/dist/cli/util.js +1 -1
  12. package/dist/demo/narration/demo-pace.d.ts +2 -2
  13. package/dist/demo/narration/demo-pace.js +1 -1
  14. package/dist/demo/narration/device.d.ts +2 -2
  15. package/dist/demo/narration/merge.d.ts +5 -3
  16. package/dist/demo/narration/merge.js +3 -7
  17. package/dist/demo/narration/narration.d.ts +9 -13
  18. package/dist/demo/narration/narration.js +5 -17
  19. package/dist/demo/narration/stub-ctx.js +3 -3
  20. package/dist/demo/narration/todo-dock.d.ts +5 -5
  21. package/dist/demo/overlay/cursor.d.ts +9 -2
  22. package/dist/demo/overlay/cursor.js +1 -1
  23. package/dist/demo/overlay/device-stage.d.ts +15 -2
  24. package/dist/demo/overlay/inpage/banner.d.ts +2 -1
  25. package/dist/demo/overlay/inpage/banner.js +3 -3
  26. package/dist/demo/overlay/inpage/caption.d.ts +1 -1
  27. package/dist/demo/overlay/inpage/core.d.ts +2 -1
  28. package/dist/demo/overlay/inpage/core.js +1 -7
  29. package/dist/demo/overlay/inpage/device-chip.d.ts +1 -1
  30. package/dist/demo/overlay/inpage/device-chip.js +6 -6
  31. package/dist/demo/overlay/inpage/network.d.ts +3 -2
  32. package/dist/demo/overlay/inpage/network.js +1 -12
  33. package/dist/demo/overlay/inpage/panel-chrome.d.ts +1 -1
  34. package/dist/demo/overlay/inpage/panel-chrome.js +1 -1
  35. package/dist/demo/overlay/inpage/stamp.d.ts +1 -1
  36. package/dist/demo/overlay/inpage/stamp.js +1 -1
  37. package/dist/demo/overlay/inpage/swipe-zoom.d.ts +1 -1
  38. package/dist/demo/overlay/inpage/swipe-zoom.js +1 -1
  39. package/dist/demo/overlay/inpage/todos.d.ts +2 -1
  40. package/dist/demo/overlay/inpage/todos.js +1 -2
  41. package/dist/demo/overlay/network-capture.d.ts +2 -2
  42. package/dist/demo/overlay/network-capture.js +1 -1
  43. package/dist/demo/overlay/overlay-css.js +1 -55
  44. package/dist/demo/overlay/overlay-stage.d.ts +19 -11
  45. package/dist/demo/overlay/overlay.d.ts +2 -2
  46. package/dist/demo/overlay/overlay.js +3 -2
  47. package/dist/demo/overlay/rings.d.ts +38 -18
  48. package/dist/demo/overlay/rings.js +21 -62
  49. package/dist/demo/overlay/ui/overlay.css +40 -0
  50. package/dist/demo/stepper/after.js +11 -34
  51. package/dist/demo/stepper/banner-log.d.ts +4 -6
  52. package/dist/demo/stepper/banner-log.js +3 -1
  53. package/dist/demo/stepper/before.js +6 -4
  54. package/dist/demo/stepper/demo-log.d.ts +26 -9
  55. package/dist/demo/stepper/demo-log.js +10 -27
  56. package/dist/demo/stepper/interaction-patch.d.ts +3 -2
  57. package/dist/demo/stepper/interaction-patch.js +22 -34
  58. package/dist/demo/stepper/panel-after.d.ts +17 -0
  59. package/dist/demo/stepper/panel-after.js +166 -0
  60. package/dist/demo/stepper/panel-before.d.ts +12 -0
  61. package/dist/demo/stepper/panel-before.js +241 -0
  62. package/dist/demo/stepper/slides.d.ts +16 -1
  63. package/dist/demo/stepper/slides.js +21 -5
  64. package/dist/demo/stepper/step-panels.d.ts +57 -9
  65. package/dist/demo/stepper/step-panels.js +10 -427
  66. package/dist/demo/stepper/stepper.js +1 -1
  67. package/dist/demo/stepper/teardown.d.ts +4 -5
  68. package/dist/demo/stepper/teardown.js +4 -6
  69. package/dist/demo/stepper/todo-advance.d.ts +3 -2
  70. package/dist/demo/stepper/todo-advance.js +2 -2
  71. package/dist/demo/stepper/view.d.ts +4 -14
  72. package/dist/demo/types.d.ts +69 -1
  73. package/dist/demo/verbose-log.js +1 -1
  74. package/dist/engine/block/action-page.d.ts +53 -0
  75. package/dist/engine/block/action-page.js +1 -0
  76. package/dist/engine/block/block.d.ts +24 -90
  77. package/dist/engine/block/block.js +2 -77
  78. package/dist/engine/block/branch.d.ts +17 -0
  79. package/dist/engine/block/branch.js +40 -0
  80. package/dist/engine/block/dialog.d.ts +13 -0
  81. package/dist/engine/block/dialog.js +37 -0
  82. package/dist/engine/block/kinds/method.d.ts +0 -2
  83. package/dist/engine/block/kinds/method.js +0 -1
  84. package/dist/engine/block/kinds/nav.js +1 -1
  85. package/dist/engine/flow/map-branch.d.ts +9 -0
  86. package/dist/engine/flow/map-branch.js +57 -0
  87. package/dist/engine/flow/map-builder.d.ts +7 -22
  88. package/dist/engine/flow/map-builder.js +6 -145
  89. package/dist/engine/flow/map-steps.d.ts +30 -0
  90. package/dist/engine/flow/map-steps.js +87 -0
  91. package/dist/engine/run/run.d.ts +1 -19
  92. package/dist/engine/run/run.js +0 -88
  93. package/dist/engine/stub/ring.d.ts +2 -4
  94. package/dist/engine/stub/ring.js +1 -0
  95. package/dist/engine/stub/stub.d.ts +16 -8
  96. package/dist/engine/trait/trait.js +1 -1
  97. package/dist/index.d.ts +15 -12
  98. package/dist/index.js +9 -7
  99. package/dist/live/auto/auto.d.ts +3 -15
  100. package/dist/live/auto/auto.js +10 -420
  101. package/dist/live/auto/explore.d.ts +2 -2
  102. package/dist/live/auto/mem.d.ts +11 -0
  103. package/dist/live/auto/mem.js +85 -0
  104. package/dist/live/auto/menu.d.ts +30 -0
  105. package/dist/live/auto/menu.js +335 -0
  106. package/dist/live/auto-session/auto-session.d.ts +23 -103
  107. package/dist/live/auto-session/auto-session.js +100 -370
  108. package/dist/live/auto-session/console.d.ts +4 -0
  109. package/dist/live/auto-session/console.js +41 -0
  110. package/dist/live/auto-session/files.d.ts +44 -0
  111. package/dist/live/auto-session/files.js +145 -0
  112. package/dist/live/auto-session/helpers.d.ts +5 -2
  113. package/dist/live/auto-session/helpers.js +17 -0
  114. package/dist/live/auto-session/inspect.d.ts +4 -0
  115. package/dist/live/auto-session/inspect.js +75 -0
  116. package/dist/live/auto-session/ipc.d.ts +3 -22
  117. package/dist/live/auto-session/ipc.js +7 -147
  118. package/dist/live/auto-session/open-browser.d.ts +6 -0
  119. package/dist/live/auto-session/open-browser.js +47 -0
  120. package/dist/live/pilot/pilot.d.ts +1 -1
  121. package/dist/live/pilot/pilot.js +2 -1
  122. package/dist/live/traverse/traverse.js +6 -200
  123. package/dist/live/traverse/worker.d.ts +37 -0
  124. package/dist/live/traverse/worker.js +200 -0
  125. package/dist/runner/plan/find.js +2 -2
  126. package/dist/runner/plan/plan.js +1 -1
  127. package/dist/runner/plan/seed-mem.js +5 -5
  128. package/dist/runner/plan/spec.js +1 -1
  129. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +2 -34
  130. package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +2 -1
  131. package/examples/saucedemo/src/flows/checkout.flow.ts +2 -1
  132. package/examples/saucedemo/src/flows/shop.flow.ts +2 -1
  133. package/examples/saucedemo/src/stubs/device-showcase.ts +37 -43
  134. package/examples/saucedemo/src/stubs/finish-order.ts +38 -0
  135. package/package.json +5 -1
  136. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +2 -34
  137. package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +2 -1
  138. package/templates/quickstart/src/flows/checkout.flow.ts +2 -1
  139. package/templates/quickstart/src/flows/shop.flow.ts +2 -1
  140. package/templates/quickstart/src/stubs/device-showcase.ts +37 -43
  141. package/templates/quickstart/src/stubs/finish-order.ts +38 -0
  142. package/templates/scaffold/src/flows/home/hello.flow.ts +3 -0
  143. package/templates/scaffold/src/map/(app_base)/home/_methods/assert-hello.method.block.ts +1 -9
@@ -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
- [key: `__wg${string}`]: any;
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
+ }
@@ -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 runner's `step-mode/demo-log.ts`. Silenced by WAYGRAPH_JSON=1.
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 (step-mode
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
- * Attaches routing to `block` without touching its act/observe/resolve/verify -
254
- * `routes` must cover every tag of `block`'s output (a missing route is a compile
255
- * error), and the returned Block runs exactly as `block` always did, just followed
256
- * by whichever next Block the resolved tag routes to. `null` for a tag means that
257
- * tag is terminal - the run stops there, same as a Block with no routing at all.
258
- * A route pointing back at `block` itself is a self-loop; see `runGraph`'s
259
- * `maxSteps` for its safety net. The return type is deliberately `Block<In, any>` -
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 branch<In extends Checkpoint<string>, Out extends Checkpoint<string>>(block: Block<In, Out>, routes: {
265
- [K in Out["__state"]]: Block<Extract<Out, Checkpoint<K>>, any> | null;
266
- }): DefinedBlock<In, any>;
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: any) => Trait[])): B;
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 { WaygraphError, 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, } from "../errors.js";
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
@@ -42,7 +42,6 @@ export function defineAssertBlock(options) {
42
42
  }
43
43
  },
44
44
  verify: options.verify,
45
- ...(options.slides ? { slides: options.slides } : {}),
46
45
  },
47
46
  });
48
47
  Object.defineProperty(built, "__waygraphKind", {
@@ -28,7 +28,7 @@ export function defineNavBlock(options) {
28
28
  await page.goto(url);
29
29
  }
30
30
  else {
31
- // Always Locator.click() so step-mode's demo cursor/pulse patch
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
+ }