waygraph 0.16.0 → 0.16.1

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 (107) 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/pilot.d.ts +2 -0
  4. package/dist/cli/commands/pilot.js +102 -0
  5. package/dist/cli/commands/session-ops.d.ts +3 -0
  6. package/dist/cli/commands/session-ops.js +257 -0
  7. package/dist/cli/commands/session.js +20 -461
  8. package/dist/cli/usage.d.ts +1 -1
  9. package/dist/demo/narration/demo-pace.d.ts +1 -1
  10. package/dist/demo/narration/device.d.ts +2 -2
  11. package/dist/demo/narration/merge.d.ts +4 -2
  12. package/dist/demo/narration/merge.js +2 -6
  13. package/dist/demo/narration/narration.d.ts +8 -8
  14. package/dist/demo/narration/todo-dock.d.ts +5 -5
  15. package/dist/demo/overlay/cursor.d.ts +9 -2
  16. package/dist/demo/overlay/cursor.js +1 -1
  17. package/dist/demo/overlay/device-stage.d.ts +15 -2
  18. package/dist/demo/overlay/inpage/banner.d.ts +2 -1
  19. package/dist/demo/overlay/inpage/banner.js +3 -3
  20. package/dist/demo/overlay/inpage/caption.d.ts +1 -1
  21. package/dist/demo/overlay/inpage/core.d.ts +2 -1
  22. package/dist/demo/overlay/inpage/core.js +1 -1
  23. package/dist/demo/overlay/inpage/device-chip.d.ts +1 -1
  24. package/dist/demo/overlay/inpage/device-chip.js +6 -6
  25. package/dist/demo/overlay/inpage/network.d.ts +3 -2
  26. package/dist/demo/overlay/inpage/network.js +1 -12
  27. package/dist/demo/overlay/inpage/panel-chrome.d.ts +1 -1
  28. package/dist/demo/overlay/inpage/panel-chrome.js +1 -1
  29. package/dist/demo/overlay/inpage/stamp.d.ts +1 -1
  30. package/dist/demo/overlay/inpage/stamp.js +1 -1
  31. package/dist/demo/overlay/inpage/swipe-zoom.d.ts +1 -1
  32. package/dist/demo/overlay/inpage/swipe-zoom.js +1 -1
  33. package/dist/demo/overlay/inpage/todos.d.ts +2 -1
  34. package/dist/demo/overlay/inpage/todos.js +1 -2
  35. package/dist/demo/overlay/network-capture.d.ts +2 -2
  36. package/dist/demo/overlay/network-capture.js +1 -1
  37. package/dist/demo/overlay/overlay-stage.d.ts +19 -11
  38. package/dist/demo/overlay/overlay.d.ts +2 -2
  39. package/dist/demo/overlay/rings.d.ts +38 -10
  40. package/dist/demo/overlay/rings.js +21 -47
  41. package/dist/demo/stepper/after.js +9 -19
  42. package/dist/demo/stepper/banner-log.d.ts +4 -6
  43. package/dist/demo/stepper/banner-log.js +3 -1
  44. package/dist/demo/stepper/before.js +3 -3
  45. package/dist/demo/stepper/demo-log.d.ts +26 -9
  46. package/dist/demo/stepper/demo-log.js +9 -26
  47. package/dist/demo/stepper/interaction-patch.d.ts +3 -2
  48. package/dist/demo/stepper/interaction-patch.js +19 -19
  49. package/dist/demo/stepper/panel-after.d.ts +17 -0
  50. package/dist/demo/stepper/panel-after.js +166 -0
  51. package/dist/demo/stepper/panel-before.d.ts +12 -0
  52. package/dist/demo/stepper/panel-before.js +241 -0
  53. package/dist/demo/stepper/slides.d.ts +13 -1
  54. package/dist/demo/stepper/slides.js +3 -3
  55. package/dist/demo/stepper/step-panels.d.ts +57 -9
  56. package/dist/demo/stepper/step-panels.js +10 -427
  57. package/dist/demo/stepper/stepper.js +1 -1
  58. package/dist/demo/stepper/teardown.d.ts +4 -5
  59. package/dist/demo/stepper/teardown.js +4 -5
  60. package/dist/demo/stepper/todo-advance.d.ts +3 -2
  61. package/dist/demo/stepper/todo-advance.js +2 -2
  62. package/dist/demo/stepper/view.d.ts +4 -14
  63. package/dist/demo/types.d.ts +69 -1
  64. package/dist/engine/block/action-page.d.ts +53 -0
  65. package/dist/engine/block/action-page.js +1 -0
  66. package/dist/engine/block/block.d.ts +24 -79
  67. package/dist/engine/block/block.js +2 -77
  68. package/dist/engine/block/branch.d.ts +17 -0
  69. package/dist/engine/block/branch.js +40 -0
  70. package/dist/engine/block/dialog.d.ts +13 -0
  71. package/dist/engine/block/dialog.js +37 -0
  72. package/dist/engine/flow/map-branch.d.ts +9 -0
  73. package/dist/engine/flow/map-branch.js +57 -0
  74. package/dist/engine/flow/map-builder.d.ts +7 -22
  75. package/dist/engine/flow/map-builder.js +6 -145
  76. package/dist/engine/flow/map-steps.d.ts +30 -0
  77. package/dist/engine/flow/map-steps.js +87 -0
  78. package/dist/engine/trait/trait.js +1 -1
  79. package/dist/index.d.ts +13 -9
  80. package/dist/index.js +7 -5
  81. package/dist/live/auto/auto.d.ts +3 -15
  82. package/dist/live/auto/auto.js +10 -420
  83. package/dist/live/auto/explore.d.ts +2 -2
  84. package/dist/live/auto/mem.d.ts +11 -0
  85. package/dist/live/auto/mem.js +85 -0
  86. package/dist/live/auto/menu.d.ts +30 -0
  87. package/dist/live/auto/menu.js +335 -0
  88. package/dist/live/auto-session/auto-session.d.ts +23 -103
  89. package/dist/live/auto-session/auto-session.js +100 -370
  90. package/dist/live/auto-session/console.d.ts +4 -0
  91. package/dist/live/auto-session/console.js +41 -0
  92. package/dist/live/auto-session/files.d.ts +44 -0
  93. package/dist/live/auto-session/files.js +145 -0
  94. package/dist/live/auto-session/helpers.d.ts +5 -2
  95. package/dist/live/auto-session/helpers.js +17 -0
  96. package/dist/live/auto-session/inspect.d.ts +4 -0
  97. package/dist/live/auto-session/inspect.js +75 -0
  98. package/dist/live/auto-session/ipc.d.ts +3 -22
  99. package/dist/live/auto-session/ipc.js +7 -147
  100. package/dist/live/auto-session/open-browser.d.ts +6 -0
  101. package/dist/live/auto-session/open-browser.js +47 -0
  102. package/dist/live/pilot/pilot.d.ts +1 -1
  103. package/dist/live/pilot/pilot.js +2 -1
  104. package/dist/live/traverse/traverse.js +6 -200
  105. package/dist/live/traverse/worker.d.ts +37 -0
  106. package/dist/live/traverse/worker.js +200 -0
  107. package/package.json +5 -1
@@ -30,10 +30,9 @@ import { ignorePageGone } from "../../engine/page-gone.js";
30
30
  * ("subtree intercepts pointer events"). Empty/partial fills + a lucky
31
31
  * edge click on Login then produced Sauce Demo's Epic sadface, verify
32
32
  * still passed on the error banner, and the next block blew up. Collapse
33
- * also matches the user's "step is running / can't interrupt" ask. (_opts kept
34
- * for call-site compat; autoCollapsePanel is now unconditional.)
33
+ * also matches the user's "step is running / can't interrupt" ask.
35
34
  */
36
- export async function markStepRunning(page, _opts) {
35
+ export async function markStepRunning(page) {
37
36
  await page
38
37
  .evaluate(() => {
39
38
  const runBtn = __wgById("wg-run");
@@ -82,13 +81,13 @@ export async function markStepRunning(page, _opts) {
82
81
  export async function restoreTheaterAfterNavigation(page, todoDockRef, deviceRef, stubBeforeRef) {
83
82
  const needs = (await page
84
83
  .evaluate(() => !window.__wgSyncTodos || !__wgById("wg-ring"))
85
- .catch(() => true)) || false;
84
+ .catch(ignorePageGone)) ?? true;
86
85
  if (!needs) {
87
86
  // Overlay survived (SPA / no full document wipe) - still refresh dock
88
87
  // if Node has a carry and the DOM lost .wg-todo-dock.
89
88
  const hasDock = await page
90
89
  .evaluate(() => !!__wgQ(".wg-todo-dock, #wg-todo-dock"))
91
- .catch(() => false);
90
+ .catch(ignorePageGone);
92
91
  if (hasDock || !todoDockRef || !todoDockRef.current)
93
92
  return;
94
93
  }
@@ -1,6 +1,7 @@
1
- import type { WgPage } from "../types.js";
1
+ import type { StepperRefs, WgPage } from "../types.js";
2
+ import type { ResolvedHighlight } from "../../engine/stub/ring.js";
2
3
  /** Advance sequential todo dock to stubIndex and push to the page (Method act). */
3
- export declare function syncTodoDockAdvance(page: WgPage, todoDockRef: any, stubIndex: number, stub: any): Promise<void>;
4
+ export declare function syncTodoDockAdvance(page: WgPage, todoDockRef: StepperRefs["todoDock"] | undefined, stubIndex: number, stub: ResolvedHighlight | null | undefined): Promise<void>;
4
5
  export declare function probeTodoDocksOnPage(page: WgPage): Promise<never[] | {
5
6
  key: string;
6
7
  pos: string;
@@ -22,7 +22,7 @@ function resolveTodoAdvanceIndex(dock, stub, stubIndex) {
22
22
  return stubIndex;
23
23
  const curIdx = items.findIndex((t) => t.current);
24
24
  let idx = stubIndex;
25
- const key = stub && (stub.todo || stub.slotId || stub.id);
25
+ const key = stub && (stub.todo || stub.slotId);
26
26
  if (key) {
27
27
  const k = String(key).trim().toLowerCase();
28
28
  let found = items.findIndex((t) => t.id && String(t.id).toLowerCase() === k);
@@ -90,7 +90,7 @@ export async function probeTodoDocksOnPage(page) {
90
90
  let current = "";
91
91
  items.forEach((li) => {
92
92
  if (li.classList.contains("wg-todo-current")) {
93
- current = (li.textContent || "").replace(/s+/g, " ").trim().slice(0, 48);
93
+ current = (li.textContent || "").replace(/\s+/g, " ").trim().slice(0, 48);
94
94
  }
95
95
  });
96
96
  return {
@@ -1,14 +1,14 @@
1
1
  import type { MemPage } from "../../engine/mem/mem.js";
2
- import type { Block } from "../../engine/block/block.js";
2
+ import type { SomeBlock } from "../../engine/block/block.js";
3
3
  import type { DemoPace } from "../../engine/stub/pace.js";
4
4
  import type { HighlightStyleDefaults } from "../../engine/stub/ring.js";
5
- import type { WgPage } from "../types.js";
5
+ import type { StepperRefs, WgPage } from "../types.js";
6
6
  import type { createOverlayStage } from "../overlay/overlay-stage.js";
7
7
  import type { Gate } from "./gate.js";
8
8
  import type { Pacing } from "./pacing.js";
9
9
  /** A step as the stepper sees it (the runner's PlayStep). */
10
10
  export interface StepperStep {
11
- block: Block<any, any> & {
11
+ block: SomeBlock & {
12
12
  description?: string;
13
13
  fastForward?: boolean;
14
14
  demoPace?: DemoPace;
@@ -30,17 +30,7 @@ export interface StepperContext {
30
30
  stage: ReturnType<typeof createOverlayStage>;
31
31
  gate: Gate;
32
32
  pacing: Pacing;
33
- refs: {
34
- stubBefore: {
35
- current: any[];
36
- };
37
- todoDock: {
38
- current: any;
39
- };
40
- device: {
41
- current: any;
42
- };
43
- };
33
+ refs: StepperRefs;
44
34
  title: string | undefined;
45
35
  stepperMode: "full" | "carousel";
46
36
  /** A title set by a stub (`ctx.title`) names the flow from that step on, until another stub sets a new one. */
@@ -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
+ }
@@ -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,11 @@
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 { WaygraphSlidesOrFn } from "../stub/ring.js";
6
+ import type { StubLifecycleFn } from "../stub/stub.js";
7
7
  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
- };
8
+ import type { ActionPage, WaygraphInstanceOption } from "./action-page.js";
31
9
  /**
32
10
  * The four-phase pipeline every Block runs. Most Blocks only need `act` + a
33
11
  * one-line `resolve` - add `observe` only once a Block genuinely branches, add
@@ -89,34 +67,6 @@ export interface Instruction<In extends Checkpoint<string>, Out extends Checkpoi
89
67
  */
90
68
  slides?: WaygraphSlidesOrFn<Out>;
91
69
  }
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
- }
120
70
  /**
121
71
  * A Block wraps one instruction. Its Observed type is an internal detail, erased here.
122
72
  * `next` is optional routing attached by `branch()` - unset on a plain or
@@ -246,24 +196,29 @@ export type DefinedBlock<In extends Checkpoint<string>, Out extends Checkpoint<s
246
196
  * and no one Checkpoint type covers every Block: `any` is that "some Block", written once here.
247
197
  */
248
198
  export type SomeBlock = Block<any, any>;
199
+ /** The Checkpoint a Block resolves to. */
200
+ type BlockOut<B> = B extends Block<infer _In, infer Out> ? Out : never;
249
201
  /** Any {@link DefinedBlock} (see {@link SomeBlock}). */
250
202
  export type SomeDefinedBlock = DefinedBlock<any, any>;
251
203
  export declare function findBlockIndex(middle: readonly SomeBlock[], block: SomeBlock | string | number): number;
252
204
  /**
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.
205
+ * Strips `withVerify`/`modVerify`/`modVerifyAll` off a Block, keeping everything
206
+ * else - `name`, `instruction`, `next`, `requires`. Every function below that
207
+ * returns a new Block calls this on its input first, then rebuilds through
208
+ * `defineBlock`. Without it, a Block's attached methods close over the shape it
209
+ * had *when they were attached* - call `.withVerify()` on a Block that
210
+ * `branch()` added routing to afterward, and without this step the result would
211
+ * silently drop that routing, since the stale closure never knew about it.
263
212
  */
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>;
213
+ export declare function stripMethods<In extends Checkpoint<string>, Out extends Checkpoint<string>>(block: Block<In, Out>): {
214
+ name: string;
215
+ description?: Block<In, Out>["description"];
216
+ instruction: Block<In, Out>["instruction"];
217
+ next?: Block<In, Out>["next"];
218
+ requires?: Block<In, Out>["requires"];
219
+ routes?: Block<In, Out>["routes"];
220
+ instanceOptions?: Block<In, Out>["instanceOptions"];
221
+ };
267
222
  /** Copy non-enumerable waygraph markers so decorate/withVerify keep map() kind checks. */
268
223
  export declare function copyWaygraphRuntime(from: object, to: object): void;
269
224
  /**
@@ -297,18 +252,7 @@ export declare function defineBlock<In extends Checkpoint<string>, Out extends C
297
252
  * Wrap the one Block you want to change, in place, where a flow uses it:
298
253
  * `map().gotoPage(NavLoginBlock).method(withVerify(AddToCartBlock, [...]))`.
299
254
  */
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;
255
+ export declare function withVerify<B extends SomeBlock>(block: B, verify: Trait[] | ((out: BlockOut<B>) => Trait[])): B;
312
256
  /**
313
257
  * Replaces one Trait inside `block`'s verify list, leaving every other check
314
258
  * untouched - the v1/v2/legacy-variant case, where one check needs a different
@@ -333,3 +277,4 @@ export declare function modVerify<In extends Checkpoint<string>, Out extends Che
333
277
  * same fail-loud-on-unknown-name behavior holds for every entry.
334
278
  */
335
279
  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>;
280
+ 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
+ }
@@ -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>>;