waygraph 0.16.1 → 0.16.4

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 (154) hide show
  1. package/README.md +12 -0
  2. package/dist/analysis/practices-check.d.ts +1 -1
  3. package/dist/analysis/practices-check.js +36 -0
  4. package/dist/beta.d.ts +6 -0
  5. package/dist/beta.js +5 -0
  6. package/dist/cli/commands/codemod-flows.js +2 -2
  7. package/dist/cli/commands/codemod-stubs.js +132 -2
  8. package/dist/cli/commands/pilot.js +3 -16
  9. package/dist/cli/commands/session-ops.js +26 -1
  10. package/dist/cli/flags.d.ts +1 -1
  11. package/dist/cli/flags.js +1 -1
  12. package/dist/cli/run-chain.js +2 -2
  13. package/dist/cli/usage.js +6 -0
  14. package/dist/cli/util.js +1 -1
  15. package/dist/demo/narration/demo-pace.d.ts +1 -1
  16. package/dist/demo/narration/demo-pace.js +1 -1
  17. package/dist/demo/narration/merge.d.ts +1 -1
  18. package/dist/demo/narration/merge.js +1 -1
  19. package/dist/demo/narration/narration.d.ts +2 -6
  20. package/dist/demo/narration/narration.js +5 -17
  21. package/dist/demo/narration/stub-ctx.js +126 -124
  22. package/dist/demo/overlay/cursor.js +1 -1
  23. package/dist/demo/overlay/inpage/banner.js +5 -0
  24. package/dist/demo/overlay/inpage/core.js +1 -6
  25. package/dist/demo/overlay/inpage/network.js +1 -1
  26. package/dist/demo/overlay/inpage/todos.js +0 -15
  27. package/dist/demo/overlay/network-capture.js +2 -2
  28. package/dist/demo/overlay/overlay-css.js +19 -76
  29. package/dist/demo/overlay/overlay-stage.js +1 -1
  30. package/dist/demo/overlay/overlay.js +3 -2
  31. package/dist/demo/overlay/phase-fixtures.js +5 -5
  32. package/dist/demo/overlay/pilot/shell.js +7 -5
  33. package/dist/demo/overlay/rings.d.ts +0 -8
  34. package/dist/demo/overlay/rings.js +0 -15
  35. package/dist/demo/overlay/ui/components/ring.js +2 -2
  36. package/dist/demo/overlay/ui/overlay.css +136 -33
  37. package/dist/demo/overlay/ui/shadow.js +69 -2
  38. package/dist/demo/stepper/after.js +4 -17
  39. package/dist/demo/stepper/before.js +3 -1
  40. package/dist/demo/stepper/demo-log.js +8 -8
  41. package/dist/demo/stepper/interaction-patch.js +3 -15
  42. package/dist/demo/stepper/panel-after.js +1 -1
  43. package/dist/demo/stepper/slides.d.ts +3 -0
  44. package/dist/demo/stepper/slides.js +18 -2
  45. package/dist/demo/stepper/stepper.js +1 -1
  46. package/dist/demo/stepper/teardown.js +0 -1
  47. package/dist/demo/verbose-log.js +1 -1
  48. package/dist/engine/block/block.d.ts +1 -12
  49. package/dist/engine/block/kind.d.ts +3 -0
  50. package/dist/engine/block/kind.js +11 -0
  51. package/dist/engine/block/kinds/method.d.ts +0 -2
  52. package/dist/engine/block/kinds/method.js +0 -1
  53. package/dist/engine/block/kinds/nav.js +1 -1
  54. package/dist/engine/errors.d.ts +12 -0
  55. package/dist/engine/errors.js +12 -0
  56. package/dist/engine/read/read-block.d.ts +61 -0
  57. package/dist/engine/read/read-block.js +94 -0
  58. package/dist/engine/read/read-key.d.ts +32 -0
  59. package/dist/engine/read/read-key.js +34 -0
  60. package/dist/engine/read/read-page.d.ts +19 -0
  61. package/dist/engine/read/read-page.js +50 -0
  62. package/dist/engine/read/readers.d.ts +82 -0
  63. package/dist/engine/read/readers.js +43 -0
  64. package/dist/engine/read/run-read.d.ts +17 -0
  65. package/dist/engine/read/run-read.js +156 -0
  66. package/dist/engine/run/connect.js +3 -2
  67. package/dist/engine/run/events.d.ts +28 -10
  68. package/dist/engine/run/events.js +52 -38
  69. package/dist/engine/run/run.d.ts +1 -19
  70. package/dist/engine/run/run.js +21 -97
  71. package/dist/engine/stub/ring.d.ts +2 -4
  72. package/dist/engine/stub/ring.js +1 -0
  73. package/dist/engine/stub/stub.d.ts +38 -25
  74. package/dist/index.d.ts +9 -7
  75. package/dist/index.js +11 -6
  76. package/dist/live/auto/auto.js +2 -3
  77. package/dist/live/auto/menu.js +3 -2
  78. package/dist/live/auto-session/auto-session.d.ts +9 -2
  79. package/dist/live/auto-session/auto-session.js +23 -20
  80. package/dist/live/auto-session/feed.d.ts +16 -0
  81. package/dist/live/auto-session/feed.js +71 -0
  82. package/dist/live/auto-session/helpers.d.ts +7 -0
  83. package/dist/live/auto-session/helpers.js +9 -0
  84. package/dist/live/auto-session/ipc.d.ts +15 -47
  85. package/dist/live/auto-session/ipc.js +34 -4
  86. package/dist/live/auto-session/protocol.d.ts +127 -0
  87. package/dist/live/auto-session/protocol.js +6 -0
  88. package/dist/live/auto-session/read.d.ts +25 -0
  89. package/dist/live/auto-session/read.js +50 -0
  90. package/dist/live/traverse/worker.d.ts +1 -0
  91. package/dist/live/traverse/worker.js +6 -1
  92. package/dist/manifest/manifest.d.ts +22 -2
  93. package/dist/manifest/manifest.js +27 -1
  94. package/dist/manifest/project.d.ts +10 -1
  95. package/dist/manifest/project.js +9 -12
  96. package/dist/manifest/read-blocks.d.ts +2 -0
  97. package/dist/manifest/read-blocks.js +23 -0
  98. package/dist/manifest/scan.d.ts +3 -2
  99. package/dist/manifest/scan.js +8 -4
  100. package/dist/runner/plan/find.js +2 -2
  101. package/dist/runner/plan/plan.js +1 -1
  102. package/dist/runner/plan/seed-mem.js +5 -5
  103. package/dist/runner/plan/spec.js +1 -1
  104. package/dist/runner/runner.d.ts +0 -1
  105. package/dist/runner/runner.js +6 -6
  106. package/dist/runtime/full-launch.d.ts +2 -1
  107. package/dist/runtime/full-launch.js +29 -27
  108. package/dist/runtime/run-log.d.ts +2 -0
  109. package/dist/runtime/run-log.js +13 -6
  110. package/examples/saucedemo/README.md +2 -2
  111. package/examples/saucedemo/src/blocks/SITE-MAP.md +3 -0
  112. package/examples/saucedemo/src/blocks/saucedemo-web/cart/cart.read.block.ts +26 -0
  113. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +2 -34
  114. package/examples/saucedemo/src/blocks/saucedemo-web/header.read.block.ts +25 -0
  115. package/examples/saucedemo/src/blocks/saucedemo-web/inventory/inventory.read.block.ts +28 -0
  116. package/examples/saucedemo/src/flows/api-demo.flow.ts +1 -1
  117. package/examples/saucedemo/src/flows/cart-bulk.flow.ts +1 -1
  118. package/examples/saucedemo/src/flows/cart-continue.flow.ts +1 -1
  119. package/examples/saucedemo/src/flows/catalog-logout.flow.ts +1 -1
  120. package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +3 -2
  121. package/examples/saucedemo/src/flows/checkout.flow.ts +13 -12
  122. package/examples/saucedemo/src/flows/login.flow.ts +1 -1
  123. package/examples/saucedemo/src/flows/shop.flow.ts +3 -2
  124. package/examples/saucedemo/src/flows/stub-on-error.flow.ts +1 -1
  125. package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +1 -1
  126. package/examples/saucedemo/src/states/checkout.reads.ts +9 -0
  127. package/examples/saucedemo/src/stubs/device-showcase.ts +70 -76
  128. package/examples/saucedemo/src/stubs/finish-order.ts +38 -0
  129. package/examples/saucedemo/src/stubs/login.ts +5 -5
  130. package/package.json +5 -1
  131. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +2 -34
  132. package/templates/quickstart/src/flows/api-demo.flow.ts +1 -1
  133. package/templates/quickstart/src/flows/cart-bulk.flow.ts +1 -1
  134. package/templates/quickstart/src/flows/cart-continue.flow.ts +1 -1
  135. package/templates/quickstart/src/flows/catalog-logout.flow.ts +1 -1
  136. package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +3 -2
  137. package/templates/quickstart/src/flows/checkout.flow.ts +13 -12
  138. package/templates/quickstart/src/flows/login.flow.ts +1 -1
  139. package/templates/quickstart/src/flows/shop.flow.ts +3 -2
  140. package/templates/quickstart/src/flows/stub-on-error.flow.ts +1 -1
  141. package/templates/quickstart/src/flows/viewer-blocked.flow.ts +1 -1
  142. package/templates/quickstart/src/stubs/device-showcase.ts +70 -76
  143. package/templates/quickstart/src/stubs/finish-order.ts +38 -0
  144. package/templates/quickstart/src/stubs/login.ts +5 -5
  145. package/templates/scaffold/src/flows/(external)/hoppscotch/auth.flow.ts +1 -1
  146. package/templates/scaffold/src/flows/(external)/hoppscotch/get.flow.ts +1 -1
  147. package/templates/scaffold/src/flows/(external)/hoppscotch/post.flow.ts +1 -1
  148. package/templates/scaffold/src/flows/(user-stories)/email-verification.flow.ts +4 -4
  149. package/templates/scaffold/src/flows/README.md +1 -1
  150. package/templates/scaffold/src/flows/home/cart.flow.ts +7 -7
  151. package/templates/scaffold/src/flows/home/hello.flow.ts +10 -7
  152. package/templates/scaffold/src/map/(app_base)/home/_methods/assert-hello.method.block.ts +1 -9
  153. package/templates/skills/waygraph-convention.skill.md +10 -2
  154. package/templates/skills/waygraph-pilot.skill.md +7 -0
@@ -2,7 +2,7 @@ import type { Checkpoint } from "../checkpoint/checkpoint.js";
2
2
  import type { MemPage } from "../mem/mem.js";
3
3
  import type { TodoDockState, TodoDockUiOpts, TodoListStyle, WaygraphTodoGroupInput, WaygraphTodoInput, WaygraphTodoItem, WaygraphTodosOpts } from "./todos.js";
4
4
  import type { DeviceOrientation, DevicePreset, DeviceState, DeviceViewport } from "./viewport.js";
5
- import type { HighlightStubPhase, ResolvedHighlight, WaygraphHighlightStub } from "./ring.js";
5
+ import type { HighlightStubPhase, ResolvedHighlight, WaygraphHighlightStub, WaygraphSlide } from "./ring.js";
6
6
  /** Where the top banner sits. */
7
7
  export type BannerPos = "left" | "center" | "right";
8
8
  /** Banner UX, mirrors {@link TodoDockUiOpts}: pass `false` fields to opt out for this phase. */
@@ -22,7 +22,7 @@ export type StubPhaseFixtures = {
22
22
  todos?: readonly WaygraphTodoInput[];
23
23
  /** Which todo is current (0-based). Rows before = done, after = pending. */
24
24
  todoIndex?: number;
25
- /** Heading above a single {@link StubCtx.todos} list. */
25
+ /** Heading above a single {@link StubUi.todos} list. */
26
26
  todoTitle?: string;
27
27
  /**
28
28
  * Stable dock id (`data-wg-todo-id`) for external redirect asserts
@@ -85,17 +85,15 @@ export type StubPhaseFixtures = {
85
85
  *
86
86
  * @example
87
87
  * LoginBlock.stubBefore((ctx) => {
88
- * ctx.todos("sign-in", ["Enter email", "Enter password", "Click Sign in"], { index: 0 });
89
- * ctx.zoom(1.35);
90
- * ctx.highlights({
91
- * email: { selector: "#email", label: "Email" },
92
- * submit: { selector: "#login-button", label: "Sign in" },
93
- * });
88
+ * ctx.ui.todos("sign-in", ["Enter email", "Enter password", "Click Sign in"], { index: 0 });
89
+ * ctx.ui.zoom(1.35);
90
+ * ctx.ui.ring("email", { selector: "#email", label: "Email" });
91
+ * ctx.ui.ring("submit", { selector: "#login-button", label: "Sign in" });
94
92
  * })
95
93
  * // Sequential plan: bump todoIndex in a later phase / a later Block's stubBefore
96
94
  * .stubAfter((ctx) => {
97
- * ctx.todoIndex(2);
98
- * ctx.ring("inventory", { selector: ".inventory_list", label: "Landed" });
95
+ * ctx.ui.todoIndex(2);
96
+ * ctx.ui.ring("inventory", { selector: ".inventory_list", label: "Landed" });
99
97
  * });
100
98
  */
101
99
  export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
@@ -109,19 +107,22 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
109
107
  readonly out: Out | undefined;
110
108
  /** Present on stubOnError when the step threw. */
111
109
  readonly error: unknown | undefined;
112
- /** Replace named highlight rings for this phase. */
113
- highlights(slots: HighlightStubPhase): void;
114
- /** Set / overwrite one named ring. */
110
+ /** Everything a stub paints in the demo: rings, todos, title, caption, device, zoom, network tab, slides. */
111
+ readonly ui: StubUi;
112
+ };
113
+ /** The demo UI a stub paints through: `ctx.ui.ring(...)`, `ctx.ui.todos(...)`, ... */
114
+ export type StubUi = {
115
+ /** Paints one named ring this phase; the same id again replaces it. */
115
116
  ring(id: string, stub: WaygraphHighlightStub): void;
116
117
  /**
117
- * Floating checklist: `ctx.todos("login", ["Enter email", "Enter password"], { title: "Sign in", index: 0 })`.
118
+ * Floating checklist: `ctx.ui.todos("login", ["Enter email", "Enter password"], { title: "Sign in", index: 0 })`.
118
119
  * The id is the dock's `data-wg-todo-id` (stable through redirects). One dock at a time (setting todos replaces it);
119
120
  * pass `{ parallel: true }` to keep docks with other ids. An empty list hides the dock.
120
121
  */
121
122
  todos(id: string, items: readonly WaygraphTodoInput[], opts?: WaygraphTodosOpts): void;
122
123
  /** Moves the current dock's current item (sequential plans: a later step bumps it). */
123
124
  todoIndex(n: number): void;
124
- /** Several titled lists in one dock (e.g. Functional Requirements / Scenarios / ACs), with the same id and options as {@link StubCtx.todos}. */
125
+ /** Several titled lists in one dock (e.g. Functional Requirements / Scenarios / ACs), with the same id and options as {@link StubUi.todos}. */
125
126
  todoGroups(id: string, groups: readonly WaygraphTodoGroupInput[], opts?: Omit<WaygraphTodosOpts, "index">): void;
126
127
  /**
127
128
  * Hide the floating todo dock. Omit todos on a later block = **keep** the
@@ -131,7 +132,7 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
131
132
  /**
132
133
  * Viewport / device fixture (0.13+). `mobile` | `tablet` | `desktop`.
133
134
  * Omit on later blocks = **keep** (same persist as todos). Use
134
- * {@link StubCtx.hideDevice} or `device("desktop")` to return to desktop.
135
+ * {@link StubUi.hideDevice} or `device("desktop")` to return to desktop.
135
136
  */
136
137
  device(preset: DevicePreset | DeviceState): void;
137
138
  /** Explicit CSS-pixel viewport (implies mobile-ish unless desktop-sized). */
@@ -162,24 +163,33 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
162
163
  titlePos(side: BannerPos): void;
163
164
  /**
164
165
  * Banner UX: `{ collision: false }` keeps it put even over a ring; `{ hidden: true }` hides it.
165
- * @example ctx.bannerUi({ collision: false });
166
+ * @example ctx.ui.bannerUi({ collision: false });
166
167
  */
167
168
  bannerUi(opts: BannerUiOpts): void;
168
169
  /**
169
170
  * Bottom narration caption - a white rounded card (black text, shadow), bottom-centered above
170
- * the step pill; distinct from {@link StubCtx.title}, which is the top banner card. Fades in/out
171
- * on change. Pass `""` (or call {@link StubCtx.hideCaption}) to clear.
172
- * @example ctx.caption("Click Add to cart to continue");
171
+ * the step pill; distinct from {@link StubUi.title}, which is the top banner card. Fades in/out
172
+ * on change. Pass `""` (or call {@link StubUi.hideCaption}) to clear.
173
+ * @example ctx.ui.caption("Click Add to cart to continue");
173
174
  */
174
175
  caption(text: string): void;
175
176
  /** Clears the caption (fades it out). */
176
177
  hideCaption(): void;
178
+ /**
179
+ * Multi-step "yap" captions with Next between each, for explaining a longer process. In `stubBefore` they play
180
+ * before the step runs, in `stubAfter` after it lands; a slide with a `selector` rings it while it is up.
181
+ * @example ctx.ui.slides([
182
+ * { caption: "Checkout is three screens", tag: "YAP" },
183
+ * { caption: "Overview confirms totals", selector: ".summary_info", duration: true },
184
+ * ]);
185
+ */
186
+ slides(slides: readonly WaygraphSlide[]): void;
177
187
  /** Shorthand for `bannerUi({ hidden: true })`. */
178
188
  hideBanner(): void;
179
189
  /**
180
190
  * Shows the network-tab overlay (Postman-style inspector of real page traffic + defineApiBlock
181
191
  * calls) - capture always runs in the background; this just reveals the panel. Hidden by default.
182
- * @example ctx.showNetworkTab();
192
+ * @example ctx.ui.showNetworkTab();
183
193
  */
184
194
  showNetworkTab(): void;
185
195
  /** Hides the network-tab overlay (capture keeps running - only the panel disappears). */
@@ -188,7 +198,7 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
188
198
  * Todo-dock UX: compact fold, collision flip, dim-behind-ring.
189
199
  * Defaults are smart-on; pass false fields to opt out for this phase.
190
200
  * @example
191
- * ctx.todoDockUi({ compact: false }); // always show the full list
201
+ * ctx.ui.todoDockUi({ compact: false }); // always show the full list
192
202
  */
193
203
  todoDockUi(opts: TodoDockUiOpts): void;
194
204
  };
@@ -208,7 +218,7 @@ export type StubPhaseResult = {
208
218
  * - `keep` - author omitted todos; demo must leave previous dock alone
209
219
  */
210
220
  todoSync?: "set" | "clear" | "keep";
211
- /** Todo-dock UX for this phase (when author called {@link StubCtx.todoDockUi}). */
221
+ /** Todo-dock UX for this phase (when author called {@link StubUi.todoDockUi}). */
212
222
  todoDockUi?: TodoDockUiOpts;
213
223
  /** Resolved device after this phase (undefined when keep with no prior). */
214
224
  device?: DeviceState;
@@ -226,6 +236,8 @@ export type StubPhaseResult = {
226
236
  bannerUi?: BannerUiOpts;
227
237
  caption?: string;
228
238
  networkTabVisible?: boolean;
239
+ /** Slides to play in this phase ({@link StubUi.slides}); empty when the stub set none. */
240
+ slides: WaygraphSlide[];
229
241
  todoPos?: "left" | "right";
230
242
  /** When true, keep other docks (multi-todo). Default false = replace. */
231
243
  todoParallel?: boolean;
@@ -249,9 +261,9 @@ export type StubBagState = {
249
261
  /** Author asked to reset to desktop. */
250
262
  hideDevice?: boolean | undefined;
251
263
  device?: DeviceState | undefined;
252
- /** Touch override when set via ctx.touch() alone or with device. */
264
+ /** Touch override when set via ctx.ui.touch() alone or with device. */
253
265
  touchOverride?: boolean | undefined;
254
- /** Orientation override when set via ctx.orientation / landscape / portrait. */
266
+ /** Orientation override when set via ctx.ui.orientation / landscape / portrait. */
255
267
  orientationOverride?: DeviceOrientation | undefined;
256
268
  zoom?: number | undefined;
257
269
  zoomOut?: boolean | undefined;
@@ -260,6 +272,7 @@ export type StubBagState = {
260
272
  bannerUi?: BannerUiOpts | undefined;
261
273
  caption?: string | undefined;
262
274
  networkTabVisible?: boolean | undefined;
275
+ slides?: readonly WaygraphSlide[] | undefined;
263
276
  todoPos?: "left" | "right" | undefined;
264
277
  /** Author patched todo-dock UX this phase. */
265
278
  todoDockUi?: TodoDockUiOpts | undefined;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- import "./runtime/full-launch.js";
2
- import "./runtime/run-log.js";
3
1
  export type { Checkpoint, Start } from "./engine/checkpoint/checkpoint.js";
4
2
  export type { Instruction, Block, DefinedBlock, SomeBlock, SomeDefinedBlock } from "./engine/block/block.js";
5
3
  export type { WaygraphInstanceOption } from "./engine/block/action-page.js";
@@ -11,7 +9,7 @@ export { css, byText, byRole } from "./engine/trait/selector.js";
11
9
  export { WaygraphError, WAYGRAPH_ERRORS } from "./engine/errors.js";
12
10
  export type { WaygraphErrorCode } from "./engine/errors.js";
13
11
  export { Trait, urlMatches, textEquals, visible, disabled, enabled, frameVisible, frameTextEquals, frameContainsText } from "./engine/trait/trait.js";
14
- export { runGraph, spawnTab, narrate, preflight, defineLayout } from "./engine/run/run.js";
12
+ export { runGraph, spawnTab, preflight, defineLayout } from "./engine/run/run.js";
15
13
  export { Engine, map } from "./engine/engine.js";
16
14
  export { branch } from "./engine/block/branch.js";
17
15
  export { withVerify, modVerify, modVerifyAll, defineBlock } from "./engine/block/block.js";
@@ -29,7 +27,7 @@ export type { BranchEntry, BranchRunResult, RunBranchRegressionOptions } from ".
29
27
  export type { ApiBlockOptions, ApiCallResult } from "./engine/block/kinds/api.js";
30
28
  export type { MapStepOpts } from "./engine/flow/map-steps.js";
31
29
  export type { MapFlowOpts } from "./engine/flow/map-builder.js";
32
- export { resolveHighlightSlots, resolveSlides, runStubPhase, hasAuthoredStubAfter, hasAuthoredStubOnError } from "./demo/narration/narration.js";
30
+ export { resolveHighlightSlots, runStubPhase, hasAuthoredStubAfter, hasAuthoredStubOnError } from "./demo/narration/narration.js";
33
31
  export { resolveFixtureDwellMs, normalizeDemoPace, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT } from "./demo/narration/demo-pace.js";
34
32
  export { formatHighlightCaption, applyHighlightStyleDefaults, toneIconPrefix } from "./demo/narration/style.js";
35
33
  export { normalizeHighlightTone, normalizeHighlightSize, normalizeHighlightWeight } from "./engine/stub/ring.js";
@@ -55,7 +53,7 @@ export { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./ana
55
53
  export { buildManifest } from "./manifest/manifest.js";
56
54
  export { onRunEvent } from "./engine/run/events.js";
57
55
  export type { RunEvent } from "./engine/run/events.js";
58
- export type { Manifest, ManifestBlock, ManifestFlow } from "./manifest/manifest.js";
56
+ export type { Manifest, ManifestBlock, ManifestFlow, ManifestReadBlock } from "./manifest/manifest.js";
59
57
  export type { WaygraphGraph, WaygraphNode, WaygraphEdge, SkippedBlock, OrphanBlock } from "./analysis/graph.js";
60
58
  export { isFileSelectToken, parseBlocksSelect, matchBlocksSelect, globToRegExp } from "./manifest/select.js";
61
59
  export type { BlocksSelect, BlocksSelectKind } from "./manifest/select.js";
@@ -67,6 +65,10 @@ export type { TraverseCoverageReport } from "./live/traverse/coverage.js";
67
65
  export { WG_UI_SEL, WG_READY_PANEL_SEL, WG_READY_AUTO_SEL, WG_READY_BANNER_SEL, readWgOverlayBeacon, readWgOverlayBeaconInPage, assertWgOverlayReady } from "./demo/overlay/beacon.js";
68
66
  export type { WgModalKind, WgBeaconRow } from "./demo/overlay/beacon.js";
69
67
  export { AutoSession } from "./live/auto-session/auto-session.js";
68
+ export { SESSION_OPS, SESSION_PROTOCOL_VERSION } from "./live/auto-session/protocol.js";
69
+ export type { SessionRequest, SessionResponse, SessionStreamMessage, HelloResponse } from "./live/auto-session/protocol.js";
70
+ export { subscribeSession } from "./live/auto-session/feed.js";
71
+ export { requestSession } from "./live/auto-session/ipc.js";
70
72
  export { stubFilePath } from "./live/auto-session/helpers.js";
71
73
  export type { AutoSessionInit, SessionSnapshot, SessionSnapshotEdge, SessionSnapshotSection, ApplyPickResult, ApplyPathResult, TraceStep, ConsoleLogEntry, StorageSnapshot, ServiceWorkerRegistrationInfo, CookieInfo, StubFileKind } from "./live/auto-session/helpers.js";
72
74
  export { pilotStart } from "./live/pilot/pilot.js";
@@ -79,12 +81,12 @@ export { resolveExtension, KNOWN_EXTENSIONS } from "./runtime/extensions.js";
79
81
  export type { ExtensionSpec } from "./engine/config.js";
80
82
  export type { WebServerConfig } from "./runtime/webserver.js";
81
83
  export type { MapStart } from "./engine/engine.js";
82
- export type { WaygraphSlidesOrFn } from "./engine/stub/ring.js";
83
84
  export type { ConnectLayout } from "./engine/run/connect.js";
84
85
  export type { EffectExtras } from "./engine/block/kinds/effect.js";
85
86
  export type { BlockBase, RoutedBlockBase } from "./engine/checkpoint/route.js";
86
87
  export type { RunEventListener } from "./engine/run/events.js";
87
- export type { BlockKind, ScanOptions } from "./manifest/project.js";
88
+ export type { ScanOptions } from "./manifest/project.js";
89
+ export type { BlockKind } from "./engine/block/kind.js";
88
90
  export type { DomSnapshot, InspectDomOptions, InspectDomResult } from "./live/auto-session/helpers.js";
89
91
  export type { BrowserStartInit } from "./live/auto-session/start.js";
90
92
  export type { PilotFixtureRing, PilotHighlightFixtures } from "./demo/overlay/pilot/fixtures.js";
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
- // The runtime layer supplies how an Engine launches its own browser (config, Chromium path, extensions).
2
- import "./runtime/full-launch.js";
3
- // Run events are written to the project's run log (.waygraph/runs) when the CLI turns it on.
4
- import "./runtime/run-log.js";
1
+ import { installFullLaunch } from "./runtime/full-launch.js";
2
+ import { installRunLog } from "./runtime/run-log.js";
3
+ // The package entry wires the runtime in, explicitly: how an Engine launches its own browser (config, Chromium path,
4
+ // extensions), and the run log (.waygraph/runs) the CLI turns on. No module switches itself on by being imported.
5
+ installFullLaunch();
6
+ installRunLog();
5
7
  export { connect } from "./engine/run/connect.js";
6
8
  export { checkpoint } from "./engine/checkpoint/checkpoint.js";
7
9
  export { MemKey, MemPage, key, keyGroup } from "./engine/mem/mem.js";
@@ -9,7 +11,7 @@ export { registerMemStub, seedMemStub, getMemStub } from "./engine/mem/mem-stub.
9
11
  export { css, byText, byRole } from "./engine/trait/selector.js";
10
12
  export { WaygraphError, WAYGRAPH_ERRORS } from "./engine/errors.js";
11
13
  export { Trait, urlMatches, textEquals, visible, disabled, enabled, frameVisible, frameTextEquals, frameContainsText } from "./engine/trait/trait.js";
12
- export { runGraph, spawnTab, narrate, preflight, defineLayout } from "./engine/run/run.js";
14
+ export { runGraph, spawnTab, preflight, defineLayout } from "./engine/run/run.js";
13
15
  export { Engine, map } from "./engine/engine.js";
14
16
  export { branch } from "./engine/block/branch.js";
15
17
  export { withVerify, modVerify, modVerifyAll, defineBlock } from "./engine/block/block.js";
@@ -23,7 +25,7 @@ export { locate } from "./engine/locate.js";
23
25
  export { composeBlock, fastForwardComposeBlock, isFastForwardBlock } from "./engine/block/compose.js";
24
26
  export { MapBuilder } from "./engine/flow/map-builder.js";
25
27
  export { branchRoutes, collectBranchFlows, runBranchRegression } from "./engine/flow/branch-regression.js";
26
- export { resolveHighlightSlots, resolveSlides, runStubPhase, hasAuthoredStubAfter, hasAuthoredStubOnError } from "./demo/narration/narration.js";
28
+ export { resolveHighlightSlots, runStubPhase, hasAuthoredStubAfter, hasAuthoredStubOnError } from "./demo/narration/narration.js";
27
29
  export { resolveFixtureDwellMs, normalizeDemoPace, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT } from "./demo/narration/demo-pace.js";
28
30
  export { formatHighlightCaption, applyHighlightStyleDefaults, toneIconPrefix } from "./demo/narration/style.js";
29
31
  export { normalizeHighlightTone, normalizeHighlightSize, normalizeHighlightWeight } from "./engine/stub/ring.js";
@@ -39,6 +41,9 @@ export { runTraverse } from "./live/traverse/traverse.js";
39
41
  export { buildCoverageReport, collectGraphEdgeKeys, defaultCoverageOutPath, formatCoverageLine, normalizeHitKey, parseMinEdgeCoverage, writeCoverageReport } from "./live/traverse/coverage.js";
40
42
  export { WG_UI_SEL, WG_READY_PANEL_SEL, WG_READY_AUTO_SEL, WG_READY_BANNER_SEL, readWgOverlayBeacon, readWgOverlayBeaconInPage, assertWgOverlayReady } from "./demo/overlay/beacon.js";
41
43
  export { AutoSession } from "./live/auto-session/auto-session.js";
44
+ export { SESSION_OPS, SESSION_PROTOCOL_VERSION } from "./live/auto-session/protocol.js";
45
+ export { subscribeSession } from "./live/auto-session/feed.js";
46
+ export { requestSession } from "./live/auto-session/ipc.js";
42
47
  export { stubFilePath } from "./live/auto-session/helpers.js";
43
48
  export { pilotStart } from "./live/pilot/pilot.js";
44
49
  export { defineConfig, resolveBaseUrl } from "./runtime/waygraph-config.js";
@@ -61,8 +61,6 @@ export async function runOneBlock(engine, entry, context, page, mem, headful) {
61
61
  await page.evaluate("delete window.__wgPendingNavClickLabel").catch(ignorePageGone);
62
62
  return result;
63
63
  }
64
- /** Shown in the headful panel after each Block run. */
65
- let lastRunNote = null;
66
64
  /**
67
65
  * Interactive waygraph auto: locate current Checkpoint, list runnable Blocks, pick one, repeat.
68
66
  */
@@ -70,7 +68,8 @@ export async function runAutoExplore(projectDir, options = {}) {
70
68
  await withWebServer(projectDir, () => runAutoExploreCore(projectDir, options));
71
69
  }
72
70
  async function runAutoExploreCore(projectDir, options = {}) {
73
- lastRunNote = null;
71
+ /** Shown in the headful panel after each Block run. */
72
+ let lastRunNote = null;
74
73
  const cli = options.cli === true;
75
74
  const baseURL = options.baseURL ?? (await resolveBaseUrl(projectDir)) ?? envString("WAYGRAPH_BASE_URL");
76
75
  const startUrl = options.startUrl ?? baseURL;
@@ -27,8 +27,9 @@ export async function setAutoPanelRunning(page, label) {
27
27
  }, label)
28
28
  .catch(ignorePageGone);
29
29
  }
30
- export const PANEL_CSS = "#wg-auto-panel{position:fixed;z-index:2147483647;left:50%;bottom:12px;transform:translateX(-50%);" +
31
- "max-width:min(92vw,720px);max-height:calc(100vh - 24px);overflow-y:auto;box-sizing:border-box;" +
30
+ // In the bottom-center slot of the overlay layout (demo/overlay/ui/css/layout.css), like the demo's step panel.
31
+ export const PANEL_CSS = "#wg-auto-panel{z-index:var(--wg-z-top);" +
32
+ "width:min(92vw,720px);overflow-y:auto;box-sizing:border-box;" +
32
33
  "background:rgba(20,10,40,.94);color:#fff;border-radius:14px;padding:16px 20px;" +
33
34
  "font:14px/1.4 system-ui,sans-serif;box-shadow:0 12px 30px rgba(0,0,0,.35);}" +
34
35
  "#wg-auto-panel h3{margin:0 0 10px;font-size:16px;}" +
@@ -1,5 +1,6 @@
1
1
  import type { PilotHighlightFixtures } from "../../demo/overlay/pilot/fixtures.js";
2
2
  import type { ApplyPathResult, ApplyPickResult, AutoSessionInit, ConsoleLogEntry, InspectDomOptions, InspectDomResult, SessionSnapshot, StorageSnapshot, StubFileKind, TraceStep } from "./helpers.js";
3
+ import { type SessionRead } from "./read.js";
3
4
  /**
4
5
  * A live session driven over IPC (`browser start`, `pilot start`): the same graph, library, mem and browser as
5
6
  * `waygraph auto`, moved one call at a time (`applyPick`, `applyPath`, the raw actions) instead of a menu loop.
@@ -17,12 +18,12 @@ export declare class AutoSession {
17
18
  private page;
18
19
  private readonly startUrl;
19
20
  private readonly sessionId;
21
+ private readonly knownInteractionSelectors;
22
+ private readonly headless;
20
23
  private here;
21
24
  private lastRunNote;
22
25
  private readonly trace;
23
26
  private readonly consoleLog;
24
- private readonly knownInteractionSelectors;
25
- private readonly headless;
26
27
  private lastOverlayInfo;
27
28
  private constructor();
28
29
  static start(init: AutoSessionInit): Promise<AutoSession>;
@@ -43,8 +44,14 @@ export declare class AutoSession {
43
44
  * Deduped per page load so repeated status/send calls stay quiet.
44
45
  */
45
46
  private warnUnmappedInteractions;
47
+ /** The Checkpoint the session is at, if known. */
48
+ get where(): string | null;
46
49
  /** Pure getter - no Block runs, no mem/page mutation. */
47
50
  currentSnapshot(): Promise<SessionSnapshot>;
51
+ /** The menu as a snapshot, with what the page shows (Read Blocks) when any covers it. */
52
+ private snapshotOf;
53
+ /** Pure getter - what the page shows, from the project's Read Blocks (beta). */
54
+ read(path?: string): Promise<SessionRead>;
48
55
  /**
49
56
  * Pure getter - reads the live page's structure at the requested fidelity.
50
57
  * No Block runs, no mem/page mutation. `selector` scopes either mode to one
@@ -3,7 +3,7 @@ import { findBlockPathDetailed } from "../../analysis/graph.js";
3
3
  import { runStubPhase } from "../../demo/narration/narration.js";
4
4
  import { describeOverlayFixtures, phaseToOverlayFixtures } from "../../demo/overlay/phase-fixtures.js";
5
5
  import { showPilotFixtures } from "../../demo/overlay/pilot/fixtures.js";
6
- import { showPilotActivity, showPilotVision } from "../../demo/overlay/pilot/shell.js";
6
+ import { showPilotActivity } from "../../demo/overlay/pilot/shell.js";
7
7
  import { keepOverlayOnNavigation } from "../../demo/overlay/reattach.js";
8
8
  import { verboseLog } from "../../demo/verbose-log.js";
9
9
  import { Engine } from "../../engine/engine.js";
@@ -15,17 +15,10 @@ import { buildExploreContext, buildExploreMenu } from "../auto/explore.js";
15
15
  import { ensureMem, seedDefaultMem } from "../auto/mem.js";
16
16
  import { updatePilotOverlay } from "../pilot/panel.js";
17
17
  import { recordPageConsole } from "./console.js";
18
- import { CONSOLE_LOG_MAX_ENTRIES, TRACE_MAX_STEPS, buildSessionSnapshot, describeHighlight, parsePick, stubFilePath, stubPhaseHasContent } from "./helpers.js";
18
+ import { CONSOLE_LOG_MAX_ENTRIES, TRACE_MAX_STEPS, buildSessionSnapshot, describeHighlight, parsePick, rawTarget, stubFilePath, stubPhaseHasContent } from "./helpers.js";
19
19
  import { inspectDom, inspectStorage } from "./inspect.js";
20
20
  import { openSessionBrowser } from "./open-browser.js";
21
- /** The first element `selector` matches, ringed with the pilot's vision ring; or why there is none. */
22
- async function rawTarget(page, selector, label) {
23
- const locator = page.locator(selector).first();
24
- if ((await locator.count()) === 0)
25
- return `no element matches selector "${selector}"`;
26
- await showPilotVision(page, selector, label);
27
- return locator;
28
- }
21
+ import { readSession, snapshotRead } from "./read.js";
29
22
  /**
30
23
  * A live session driven over IPC (`browser start`, `pilot start`): the same graph, library, mem and browser as
31
24
  * `waygraph auto`, moved one call at a time (`applyPick`, `applyPath`, the raw actions) instead of a menu loop.
@@ -43,12 +36,12 @@ export class AutoSession {
43
36
  page;
44
37
  startUrl;
45
38
  sessionId;
39
+ knownInteractionSelectors;
40
+ headless;
46
41
  here = null;
47
42
  lastRunNote = null;
48
43
  trace = [];
49
44
  consoleLog = [];
50
- knownInteractionSelectors;
51
- headless;
52
45
  lastOverlayInfo = null;
53
46
  constructor(projectDir, inject, blocksSelect, graph, library, mem, engine, closeBrowser, context, page, startUrl, sessionId, knownInteractionSelectors, headless) {
54
47
  this.projectDir = projectDir;
@@ -118,7 +111,7 @@ export class AutoSession {
118
111
  // watching sees the state an API caller just got back, not a frame behind.
119
112
  this.lastOverlayInfo = {
120
113
  sessionId: this.sessionId,
121
- snapshot: buildSessionSnapshot(menu, this.library.byName, this.here, this.lastRunNote),
114
+ snapshot: await this.snapshotOf(menu),
122
115
  graph: this.graph,
123
116
  };
124
117
  await updatePilotOverlay(this.page, this.lastOverlayInfo);
@@ -163,10 +156,20 @@ export class AutoSession {
163
156
  };
164
157
  await this.page.evaluate(unmappedInteractionsPageScript, payload).catch(ignorePageGone);
165
158
  }
159
+ /** The Checkpoint the session is at, if known. */
160
+ get where() { return this.here; }
166
161
  /** Pure getter - no Block runs, no mem/page mutation. */
167
162
  async currentSnapshot() {
168
- const menu = await this.currentMenu();
169
- return buildSessionSnapshot(menu, this.library.byName, this.here, this.lastRunNote);
163
+ return this.snapshotOf(await this.currentMenu());
164
+ }
165
+ /** The menu as a snapshot, with what the page shows (Read Blocks) when any covers it. */
166
+ async snapshotOf(menu) {
167
+ return { ...buildSessionSnapshot(menu, this.library.byName, this.here, this.lastRunNote), ...(await snapshotRead(this.page, this.mem, this.here)) };
168
+ }
169
+ /** Pure getter - what the page shows, from the project's Read Blocks (beta). */
170
+ async read(path) {
171
+ this.page = await ensureLivePage(this.context, this.page, this.startUrl);
172
+ return readSession(this.page, this.mem, this.here, path);
170
173
  }
171
174
  /**
172
175
  * Pure getter - reads the live page's structure at the requested fidelity.
@@ -238,7 +241,7 @@ export class AutoSession {
238
241
  const nextMenu = await this.currentMenu();
239
242
  return {
240
243
  ok: true,
241
- snapshot: buildSessionSnapshot(nextMenu, this.library.byName, this.here, this.lastRunNote),
244
+ snapshot: await this.snapshotOf(nextMenu),
242
245
  quit: false,
243
246
  };
244
247
  }
@@ -251,7 +254,7 @@ export class AutoSession {
251
254
  if (pick.type === "quit") {
252
255
  return {
253
256
  ok: true,
254
- snapshot: buildSessionSnapshot(menu, this.library.byName, this.here, this.lastRunNote),
257
+ snapshot: await this.snapshotOf(menu),
255
258
  quit: true,
256
259
  };
257
260
  }
@@ -292,7 +295,7 @@ export class AutoSession {
292
295
  this.lastRunNote = `resync -> ${this.here ?? "Unknown"}`;
293
296
  return {
294
297
  ok: true,
295
- snapshot: buildSessionSnapshot(menu, this.library.byName, this.here, this.lastRunNote),
298
+ snapshot: await this.snapshotOf(menu),
296
299
  quit: false,
297
300
  };
298
301
  }
@@ -363,7 +366,7 @@ export class AutoSession {
363
366
  this.lastRunNote = `${what} -> ${this.here ?? "Unknown"}`;
364
367
  return {
365
368
  ok: true,
366
- snapshot: buildSessionSnapshot(menu, this.library.byName, this.here, this.lastRunNote),
369
+ snapshot: await this.snapshotOf(menu),
367
370
  quit: false,
368
371
  };
369
372
  }
@@ -390,7 +393,7 @@ export class AutoSession {
390
393
  ok: true,
391
394
  painted,
392
395
  missing,
393
- snapshot: buildSessionSnapshot(menu, this.library.byName, this.here, this.lastRunNote),
396
+ snapshot: await this.snapshotOf(menu),
394
397
  };
395
398
  }
396
399
  catch (err) {
@@ -0,0 +1,16 @@
1
+ import { type Socket } from "node:net";
2
+ import type { SessionStreamMessage } from "./protocol.js";
3
+ /** The server side: subscribed sockets, fed every run event in this process until `close`. */
4
+ export declare function createFeed(): {
5
+ add(sock: Socket): void;
6
+ send(message: SessionStreamMessage): void;
7
+ close(): void;
8
+ };
9
+ /**
10
+ * The client side: subscribes to a running session and calls `onMessage` for each line until `close()` (or the session
11
+ * ends). Rejects when the session is unknown or does not answer the subscribe.
12
+ */
13
+ export declare function subscribeSession(projectDir: string, sessionId: string, onMessage: (message: SessionStreamMessage) => void, timeoutMs?: number): Promise<{
14
+ close(): void;
15
+ closed: Promise<void>;
16
+ }>;
@@ -0,0 +1,71 @@
1
+ // A session's live feed (`subscribe`): every run event in the session (steps, reads) and the snapshot after each change,
2
+ // as JSON lines to every subscribed socket. Waygraph UI watches a session through it; `pilot watch <id>` prints it.
3
+ import { connect } from "node:net";
4
+ import { onRunEvent } from "../../engine/run/events.js";
5
+ import { resolveSessionMeta } from "./files.js";
6
+ import { SESSION_PROTOCOL_VERSION } from "./protocol.js";
7
+ /** The server side: subscribed sockets, fed every run event in this process until `close`. */
8
+ export function createFeed() {
9
+ const subscribers = new Set();
10
+ const send = (message) => {
11
+ const line = JSON.stringify(message) + "\n";
12
+ for (const sock of subscribers)
13
+ if (!sock.destroyed)
14
+ sock.write(line);
15
+ };
16
+ const off = onRunEvent((event) => send({ type: "event", event }));
17
+ return {
18
+ add(sock) {
19
+ subscribers.add(sock);
20
+ sock.on("close", () => subscribers.delete(sock));
21
+ sock.write(JSON.stringify({ ok: true, protocol: SESSION_PROTOCOL_VERSION }) + "\n");
22
+ },
23
+ send,
24
+ close() {
25
+ off();
26
+ for (const sock of subscribers)
27
+ sock.end();
28
+ subscribers.clear();
29
+ },
30
+ };
31
+ }
32
+ /**
33
+ * The client side: subscribes to a running session and calls `onMessage` for each line until `close()` (or the session
34
+ * ends). Rejects when the session is unknown or does not answer the subscribe.
35
+ */
36
+ export function subscribeSession(projectDir, sessionId, onMessage, timeoutMs = 15_000) {
37
+ const meta = resolveSessionMeta(sessionId, projectDir);
38
+ if (!meta)
39
+ return Promise.reject(new Error(`no such session "${sessionId}" - \`waygraph pilot sessions\` lists live ids`));
40
+ return new Promise((resolve, reject) => {
41
+ const sock = connect(meta.socketPath);
42
+ const timer = setTimeout(() => {
43
+ sock.destroy();
44
+ reject(new Error(`session "${sessionId}" did not answer subscribe within ${timeoutMs}ms`));
45
+ }, timeoutMs);
46
+ const closed = new Promise((done) => sock.on("close", () => done()));
47
+ let buf = "";
48
+ let acked = false;
49
+ sock.on("connect", () => sock.write(JSON.stringify({ op: "subscribe" }) + "\n"));
50
+ sock.on("error", (err) => {
51
+ clearTimeout(timer);
52
+ if (!acked)
53
+ reject(err);
54
+ });
55
+ sock.on("data", (chunk) => {
56
+ buf += chunk.toString("utf-8");
57
+ let nl;
58
+ while ((nl = buf.indexOf("\n")) !== -1) {
59
+ const line = buf.slice(0, nl);
60
+ buf = buf.slice(nl + 1);
61
+ if (!acked) {
62
+ acked = true;
63
+ clearTimeout(timer);
64
+ resolve({ close: () => sock.end(), closed });
65
+ continue;
66
+ }
67
+ onMessage(JSON.parse(line));
68
+ }
69
+ });
70
+ });
71
+ }
@@ -1,3 +1,4 @@
1
+ import type { Locator, Page } from "@playwright/test";
1
2
  import type { PilotHighlightFixtures } from "../../demo/overlay/pilot/fixtures.js";
2
3
  import type { StubPhaseResult } from "../../engine/stub/stub.js";
3
4
  import type { BlockEntry } from "../../manifest/project.js";
@@ -27,6 +28,10 @@ export interface SessionSnapshot {
27
28
  /** True when there are no runnable moves from `here` right now. */
28
29
  done: boolean;
29
30
  lastRunNote: string | null;
31
+ /** What the page shows, from the project's Read Blocks (beta); absent when none covers this page. */
32
+ read?: Record<string, unknown>;
33
+ missing?: string[];
34
+ readNotes?: Record<string, string>;
30
35
  }
31
36
  /** Plain, JSON-serializable view of a menu - no Block/MemKey/class references. */
32
37
  export declare function buildSessionSnapshot(menu: ExploreMenu, library: Map<string, BlockEntry>, here: string | null, lastRunNote: string | null): SessionSnapshot;
@@ -214,4 +219,6 @@ export interface StorageSnapshot {
214
219
  }
215
220
  /** What a highlight call paints, in a few words, for the session's activity line. */
216
221
  export declare function describeHighlight(fixtures: PilotHighlightFixtures): string;
222
+ /** The first element `selector` matches, ringed with the pilot's vision ring; or why there is none. */
223
+ export declare function rawTarget(page: Page, selector: string, label: string): Promise<Locator | string>;
217
224
  export {};
@@ -1,6 +1,7 @@
1
1
  // The session's shared shapes and pure helpers: snapshots, picks, trace / console entries, DOM walking, stub files.
2
2
  import { dirname, join, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { showPilotVision } from "../../demo/overlay/pilot/shell.js";
4
5
  /** True when a stub-phase result actually carries authored content worth keeping. */
5
6
  export function stubPhaseHasContent(r) {
6
7
  return (Object.keys(r.highlights).length > 0 ||
@@ -163,3 +164,11 @@ export function describeHighlight(fixtures) {
163
164
  .filter(Boolean)
164
165
  .join(", ") || "fixtures");
165
166
  }
167
+ /** The first element `selector` matches, ringed with the pilot's vision ring; or why there is none. */
168
+ export async function rawTarget(page, selector, label) {
169
+ const locator = page.locator(selector).first();
170
+ if ((await locator.count()) === 0)
171
+ return `no element matches selector "${selector}"`;
172
+ await showPilotVision(page, selector, label);
173
+ return locator;
174
+ }