waygraph 0.16.2 → 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 (123) 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 +43 -1
  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/usage.js +6 -0
  13. package/dist/demo/narration/stub-ctx.js +126 -124
  14. package/dist/demo/overlay/cursor.js +1 -1
  15. package/dist/demo/overlay/inpage/banner.js +5 -0
  16. package/dist/demo/overlay/inpage/core.js +1 -0
  17. package/dist/demo/overlay/inpage/network.js +1 -1
  18. package/dist/demo/overlay/inpage/todos.js +0 -15
  19. package/dist/demo/overlay/network-capture.js +2 -2
  20. package/dist/demo/overlay/overlay-css.js +18 -21
  21. package/dist/demo/overlay/overlay-stage.js +1 -1
  22. package/dist/demo/overlay/phase-fixtures.js +5 -5
  23. package/dist/demo/overlay/pilot/shell.js +7 -5
  24. package/dist/demo/overlay/ui/components/ring.js +2 -2
  25. package/dist/demo/overlay/ui/overlay.css +98 -35
  26. package/dist/demo/overlay/ui/shadow.js +69 -2
  27. package/dist/demo/stepper/demo-log.js +8 -8
  28. package/dist/demo/stepper/slides.d.ts +1 -1
  29. package/dist/demo/stepper/slides.js +1 -1
  30. package/dist/demo/stepper/stepper.js +1 -1
  31. package/dist/engine/block/kind.d.ts +3 -0
  32. package/dist/engine/block/kind.js +11 -0
  33. package/dist/engine/errors.d.ts +12 -0
  34. package/dist/engine/errors.js +12 -0
  35. package/dist/engine/read/read-block.d.ts +61 -0
  36. package/dist/engine/read/read-block.js +94 -0
  37. package/dist/engine/read/read-key.d.ts +32 -0
  38. package/dist/engine/read/read-key.js +34 -0
  39. package/dist/engine/read/read-page.d.ts +19 -0
  40. package/dist/engine/read/read-page.js +50 -0
  41. package/dist/engine/read/readers.d.ts +82 -0
  42. package/dist/engine/read/readers.js +43 -0
  43. package/dist/engine/read/run-read.d.ts +17 -0
  44. package/dist/engine/read/run-read.js +156 -0
  45. package/dist/engine/run/connect.js +3 -2
  46. package/dist/engine/run/events.d.ts +28 -10
  47. package/dist/engine/run/events.js +52 -38
  48. package/dist/engine/run/run.js +25 -13
  49. package/dist/engine/stub/stub.d.ts +26 -21
  50. package/dist/index.d.ts +7 -4
  51. package/dist/index.js +9 -4
  52. package/dist/live/auto/auto.js +2 -3
  53. package/dist/live/auto/menu.js +3 -2
  54. package/dist/live/auto-session/auto-session.d.ts +9 -2
  55. package/dist/live/auto-session/auto-session.js +23 -20
  56. package/dist/live/auto-session/feed.d.ts +16 -0
  57. package/dist/live/auto-session/feed.js +71 -0
  58. package/dist/live/auto-session/helpers.d.ts +7 -0
  59. package/dist/live/auto-session/helpers.js +9 -0
  60. package/dist/live/auto-session/ipc.d.ts +15 -47
  61. package/dist/live/auto-session/ipc.js +34 -4
  62. package/dist/live/auto-session/protocol.d.ts +127 -0
  63. package/dist/live/auto-session/protocol.js +6 -0
  64. package/dist/live/auto-session/read.d.ts +25 -0
  65. package/dist/live/auto-session/read.js +50 -0
  66. package/dist/live/traverse/worker.d.ts +1 -0
  67. package/dist/live/traverse/worker.js +6 -1
  68. package/dist/manifest/manifest.d.ts +22 -2
  69. package/dist/manifest/manifest.js +27 -1
  70. package/dist/manifest/project.d.ts +10 -1
  71. package/dist/manifest/project.js +9 -12
  72. package/dist/manifest/read-blocks.d.ts +2 -0
  73. package/dist/manifest/read-blocks.js +23 -0
  74. package/dist/manifest/scan.d.ts +3 -2
  75. package/dist/manifest/scan.js +8 -4
  76. package/dist/runner/runner.d.ts +0 -1
  77. package/dist/runner/runner.js +6 -6
  78. package/dist/runtime/full-launch.d.ts +2 -1
  79. package/dist/runtime/full-launch.js +29 -27
  80. package/dist/runtime/run-log.d.ts +2 -0
  81. package/dist/runtime/run-log.js +13 -6
  82. package/examples/saucedemo/README.md +2 -2
  83. package/examples/saucedemo/src/blocks/SITE-MAP.md +3 -0
  84. package/examples/saucedemo/src/blocks/saucedemo-web/cart/cart.read.block.ts +26 -0
  85. package/examples/saucedemo/src/blocks/saucedemo-web/header.read.block.ts +25 -0
  86. package/examples/saucedemo/src/blocks/saucedemo-web/inventory/inventory.read.block.ts +28 -0
  87. package/examples/saucedemo/src/flows/api-demo.flow.ts +1 -1
  88. package/examples/saucedemo/src/flows/cart-bulk.flow.ts +1 -1
  89. package/examples/saucedemo/src/flows/cart-continue.flow.ts +1 -1
  90. package/examples/saucedemo/src/flows/catalog-logout.flow.ts +1 -1
  91. package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +1 -1
  92. package/examples/saucedemo/src/flows/checkout.flow.ts +11 -11
  93. package/examples/saucedemo/src/flows/login.flow.ts +1 -1
  94. package/examples/saucedemo/src/flows/shop.flow.ts +1 -1
  95. package/examples/saucedemo/src/flows/stub-on-error.flow.ts +1 -1
  96. package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +1 -1
  97. package/examples/saucedemo/src/states/checkout.reads.ts +9 -0
  98. package/examples/saucedemo/src/stubs/device-showcase.ts +37 -37
  99. package/examples/saucedemo/src/stubs/finish-order.ts +2 -2
  100. package/examples/saucedemo/src/stubs/login.ts +5 -5
  101. package/package.json +5 -1
  102. package/templates/quickstart/src/flows/api-demo.flow.ts +1 -1
  103. package/templates/quickstart/src/flows/cart-bulk.flow.ts +1 -1
  104. package/templates/quickstart/src/flows/cart-continue.flow.ts +1 -1
  105. package/templates/quickstart/src/flows/catalog-logout.flow.ts +1 -1
  106. package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +1 -1
  107. package/templates/quickstart/src/flows/checkout.flow.ts +11 -11
  108. package/templates/quickstart/src/flows/login.flow.ts +1 -1
  109. package/templates/quickstart/src/flows/shop.flow.ts +1 -1
  110. package/templates/quickstart/src/flows/stub-on-error.flow.ts +1 -1
  111. package/templates/quickstart/src/flows/viewer-blocked.flow.ts +1 -1
  112. package/templates/quickstart/src/stubs/device-showcase.ts +37 -37
  113. package/templates/quickstart/src/stubs/finish-order.ts +2 -2
  114. package/templates/quickstart/src/stubs/login.ts +5 -5
  115. package/templates/scaffold/src/flows/(external)/hoppscotch/auth.flow.ts +1 -1
  116. package/templates/scaffold/src/flows/(external)/hoppscotch/get.flow.ts +1 -1
  117. package/templates/scaffold/src/flows/(external)/hoppscotch/post.flow.ts +1 -1
  118. package/templates/scaffold/src/flows/(user-stories)/email-verification.flow.ts +4 -4
  119. package/templates/scaffold/src/flows/README.md +1 -1
  120. package/templates/scaffold/src/flows/home/cart.flow.ts +7 -7
  121. package/templates/scaffold/src/flows/home/hello.flow.ts +8 -8
  122. package/templates/skills/waygraph-convention.skill.md +10 -2
  123. package/templates/skills/waygraph-pilot.skill.md +7 -0
@@ -3,12 +3,12 @@ import { CheckoutOverviewSel } from "../blocks/saucedemo-web/checkout-step-two/m
3
3
 
4
4
  /** Rings Finish while the order is placed. */
5
5
  export function placingOrder(ctx: StubCtx): void {
6
- ctx.ring("finish", { selector: CheckoutOverviewSel.finishBtn, label: "Placing the order" });
6
+ ctx.ui.ring("finish", { selector: CheckoutOverviewSel.finishBtn, label: "Placing the order" });
7
7
  }
8
8
 
9
9
  /** Yap slides after the order is placed. They play on the completion page, so they ring its header, not #finish. */
10
10
  export function orderPlacedYap(ctx: StubCtx): void {
11
- ctx.slides([
11
+ ctx.ui.slides([
12
12
  {
13
13
  caption: "Checkout is a short pipeline",
14
14
  detail: "Cart -> info -> overview -> done. You just placed the order.",
@@ -3,8 +3,8 @@ import { LoginSel } from "../blocks/saucedemo-web/methods/login.sel.js";
3
3
 
4
4
  /** What a refused login looks like on screen: the title, a finished checklist and the error banner. */
5
5
  export function loginRefused(ctx: StubCtx): void {
6
- ctx.title("Login blocked");
7
- ctx.todos(
6
+ ctx.ui.title("Login blocked");
7
+ ctx.ui.todos(
8
8
  "saucedemo",
9
9
  [
10
10
  { id: "login-user", text: "Enter username", done: true },
@@ -13,7 +13,7 @@ export function loginRefused(ctx: StubCtx): void {
13
13
  ],
14
14
  { title: "Sign in", style: "checklist" },
15
15
  );
16
- ctx.ring("error", {
16
+ ctx.ui.ring("error", {
17
17
  selector: LoginSel.errorBanner,
18
18
  label: "Login error banner",
19
19
  detail: "tone: danger (red)",
@@ -25,8 +25,8 @@ export function loginRefused(ctx: StubCtx): void {
25
25
 
26
26
  /** Rings the error banner when the login step itself throws. */
27
27
  export function loginFailed(ctx: StubCtx): void {
28
- ctx.title("Login failed");
29
- ctx.ring("error", {
28
+ ctx.ui.title("Login failed");
29
+ ctx.ui.ring("error", {
30
30
  selector: LoginSel.errorBanner,
31
31
  label: "Login error banner",
32
32
  detail: "tone: danger (red)",
@@ -19,7 +19,7 @@ export const hoppscotchAuthFlow = engine
19
19
  .start()
20
20
  .gotoExternal(
21
21
  NavHoppscotchSettingsBlock.stubBefore((ctx) => {
22
- ctx.title("Scaffold: Hoppscotch GET with query params and bearer token");
22
+ ctx.ui.title("Scaffold: Hoppscotch GET with query params and bearer token");
23
23
  }),
24
24
  )
25
25
  .method(PickExtensionInterceptorBlock)
@@ -13,7 +13,7 @@ export const hoppscotchGetFlow = engine
13
13
  .start()
14
14
  .gotoExternal(
15
15
  NavHoppscotchSettingsBlock.stubBefore((ctx) => {
16
- ctx.title("Scaffold: Hoppscotch GET");
16
+ ctx.ui.title("Scaffold: Hoppscotch GET");
17
17
  }),
18
18
  )
19
19
  .method(PickExtensionInterceptorBlock)
@@ -18,7 +18,7 @@ export const hoppscotchPostFlow = engine
18
18
  .start()
19
19
  .gotoExternal(
20
20
  NavHoppscotchSettingsBlock.stubBefore((ctx) => {
21
- ctx.title("Scaffold: Hoppscotch POST with header and JSON body");
21
+ ctx.ui.title("Scaffold: Hoppscotch POST with header and JSON body");
22
22
  }),
23
23
  )
24
24
  .method(PickExtensionInterceptorBlock)
@@ -14,12 +14,12 @@ export const emailVerificationFlow = engine
14
14
  .start()
15
15
  .gotoExternal(
16
16
  NavMailpitInboxBlock.stubBefore((ctx) => {
17
- ctx.title("Scaffold: Cross-origin mail verification (QA-rich)");
17
+ ctx.ui.title("Scaffold: Cross-origin mail verification (QA-rich)");
18
18
  }),
19
19
  )
20
20
  .assert(
21
21
  AssertEmailReceivedBlock.stubAfter((ctx) => {
22
- ctx.ring("note", {
22
+ ctx.ui.ring("note", {
23
23
  selector: MailpitSel.messageRow,
24
24
  label: "AC · email arrived",
25
25
  detail: "Confirmed before opening anything.",
@@ -30,12 +30,12 @@ export const emailVerificationFlow = engine
30
30
  .method(OpenMessageBlock)
31
31
  .assert(
32
32
  AssertEmailContentBlock.stubAfter((ctx) => {
33
- ctx.ring("note", { selector: MailpitSel.previewIframe, label: "AC · email body matches expected copy", tag: "AC" });
33
+ ctx.ui.ring("note", { selector: MailpitSel.previewIframe, label: "AC · email body matches expected copy", tag: "AC" });
34
34
  }),
35
35
  )
36
36
  .method(
37
37
  ExtractEmailLinkBlock.stubAfter((ctx) => {
38
- ctx.ring("note", {
38
+ ctx.ui.ring("note", {
39
39
  selector: MailpitSel.previewIframe,
40
40
  label: "AC · link found",
41
41
  detail: "Read from the message preview iframe.",
@@ -34,6 +34,6 @@ src/flows/
34
34
 
35
35
  - Build it with `engine.map({ memStub })…end()`; nest another with `.flow(sub, { reset: true })`;
36
36
  `.end()` takes nothing.
37
- - The flow's name and narration are ctx stubs chained on the Block where they apply: `ctx.title("…")`, `ctx.ring(…)`.
37
+ - The flow's name and narration are ctx stubs chained on the Block where they apply: `ctx.ui.title("…")`, `ctx.ui.ring(…)`.
38
38
  - A flow expected to fail (a locked-out user) says so in `map({ expectedFailure: "…" })`.
39
39
  - A step's speed in the demo is `{ pace }`: `.method(FillBlock, { pace: "blitz" })`.
@@ -10,10 +10,10 @@ import "../../map/(app_base)/home/_page.block.js";
10
10
 
11
11
  // `map({ memStub: true })` fills SelectedItem (alpha) from src/states/demo.mem-stubs.ts; --data overrides it.
12
12
  const AddShown = AddItemBlock.stubBefore((ctx) => {
13
- ctx.ring("add", { selector: DemoSel.catalog, label: "Catalog" });
13
+ ctx.ui.ring("add", { selector: DemoSel.catalog, label: "Catalog" });
14
14
  })
15
15
  .stubAfter((ctx) => {
16
- ctx.ring("cart", {
16
+ ctx.ui.ring("cart", {
17
17
  selector: DemoSel.cartCount,
18
18
  label: "AC · item in cart",
19
19
  detail: "Cart count bumped after Add.",
@@ -22,17 +22,17 @@ const AddShown = AddItemBlock.stubBefore((ctx) => {
22
22
  });
23
23
  })
24
24
  .stubOnError((ctx) => {
25
- ctx.ring("cart", { selector: DemoSel.cartCount, label: "Cart at failure", tag: "FAIL" });
25
+ ctx.ui.ring("cart", { selector: DemoSel.cartCount, label: "Cart at failure", tag: "FAIL" });
26
26
  });
27
27
 
28
28
  const RemoveShown = RemoveItemBlock.stubAfter((ctx) => {
29
- ctx.ring("cart", { selector: DemoSel.cartCount, label: "Cart count", duration: true });
29
+ ctx.ui.ring("cart", { selector: DemoSel.cartCount, label: "Cart count", duration: true });
30
30
  });
31
31
 
32
32
  const ClearShown = ClearCartBlock.stubBefore((ctx) => {
33
- ctx.ring("clear", { selector: DemoSel.clear, label: "Clear cart" });
33
+ ctx.ui.ring("clear", { selector: DemoSel.clear, label: "Clear cart" });
34
34
  }).stubAfter((ctx) => {
35
- ctx.ring("cart", { selector: DemoSel.cartCount, label: "Empty cart", duration: true });
35
+ ctx.ui.ring("cart", { selector: DemoSel.cartCount, label: "Empty cart", duration: true });
36
36
  });
37
37
 
38
38
  export const homeCartFlow = engine
@@ -40,7 +40,7 @@ export const homeCartFlow = engine
40
40
  .start()
41
41
  .gotoPage(
42
42
  NavHomeBlock.stubBefore((ctx) => {
43
- ctx.title("Scaffold: Shop (Effect + clear)");
43
+ ctx.ui.title("Scaffold: Shop (Effect + clear)");
44
44
  }),
45
45
  )
46
46
  .method(AddShown)
@@ -13,32 +13,32 @@ export const homeHelloFlow = engine
13
13
  .start()
14
14
  .gotoPage(
15
15
  NavHomeBlock.stubBefore((ctx) => {
16
- ctx.title("Scaffold: Hello Waygraph");
16
+ ctx.ui.title("Scaffold: Hello Waygraph");
17
17
  }).stubAfter((ctx) => {
18
- ctx.ring("title", { selector: DemoSel.title, label: "Home heading", duration: true });
18
+ ctx.ui.ring("title", { selector: DemoSel.title, label: "Home heading", duration: true });
19
19
  }).stubOnError((ctx) => {
20
- ctx.ring("title", { selector: DemoSel.title, label: "Home heading at failure" });
20
+ ctx.ui.ring("title", { selector: DemoSel.title, label: "Home heading at failure" });
21
21
  }),
22
22
  )
23
23
  .assert(
24
24
  AssertHelloBlock.stubBefore((ctx) => {
25
- ctx.ring("title", { selector: DemoSel.title, label: "Heading about to check" });
25
+ ctx.ui.ring("title", { selector: DemoSel.title, label: "Heading about to check" });
26
26
  })
27
27
  .stubAfter((ctx) => {
28
- ctx.ring("title", {
28
+ ctx.ui.ring("title", {
29
29
  selector: DemoSel.title,
30
30
  label: "AC · Hello visible",
31
31
  detail: "Replace this copy with your ticket lines.",
32
32
  tag: "AC",
33
33
  duration: true,
34
34
  });
35
- ctx.ring("sub", { selector: DemoSel.sub, label: "Subcopy", duration: 1500 });
36
- ctx.slides([
35
+ ctx.ui.ring("sub", { selector: DemoSel.sub, label: "Subcopy", duration: 1500 });
36
+ ctx.ui.slides([
37
37
  { caption: "Scaffold covers auto + narration", detail: "Page hub, Effect instanceOptions, stubs, YAP.", tag: "YAP", duration: true },
38
38
  ]);
39
39
  })
40
40
  .stubOnError((ctx) => {
41
- ctx.ring("title", { selector: DemoSel.title, label: "Heading at failure", tag: "FAIL" });
41
+ ctx.ui.ring("title", { selector: DemoSel.title, label: "Heading at failure", tag: "FAIL" });
42
42
  }),
43
43
  )
44
44
  .gotoPage(NavDocsBlock)
@@ -68,18 +68,26 @@ against a live site, use `--skill-pilot-blind`.
68
68
  when a *different* Block right after it in the `.flow.ts` is what actually confirms the
69
69
  transition (e.g. an `AssertXBlock`) - `// waygraph-ignore: empty-verify`.
70
70
 
71
+ 9. **Never hand-write UI.** All demo UI goes through `ctx.ui` (`ring`, `caption`, `title`, `todos`, `slides`,
72
+ `device`, `zoom`, `showNetworkTab`), chained where the flow uses the Block. Never build your own caption, highlight
73
+ or banner in the page (`page.evaluate` + `document.createElement` / `innerHTML` / `.style`, `page.addStyleTag`),
74
+ and never restyle or call waygraph's overlay (`#wg-*`, `.wg-*`, `window.__wg*`). It changes the page under test (an
75
+ injected banner can cover a button, an injected style can hide a bug) and breaks silently when the overlay changes.
76
+ If `ctx.ui` lacks something, say so and ask for it in waygraph - don't override. `waygraph check` flags it
77
+ (`hand-written-ui`).
78
+
71
79
  ## Flows, stubs and config (the scaffold's shape)
72
80
 
73
81
  - **Build flows with `map()`** (`engine.map({ memStub }).start().gotoPage(Nav).method(B, { pace }).assert(A).end()`;
74
82
  `.gotoExternal` for another origin); it is the only way to build one. Flow settings (`memStub`, `expectedFailure`, `demoPace`, `sessionReset`) go in `map({ … })` before the chain
75
- and `.end()` takes nothing. The flow's title is `ctx.title(...)` in a stub. **Nest flows with `.flow(sub, { reset: true })`**.
83
+ and `.end()` takes nothing. The flow's title is `ctx.ui.title(...)` in a stub. **Nest flows with `.flow(sub, { reset: true })`**.
76
84
  `defineFlow`, `chainFlow` and the `with*` flow wrappers were removed in 0.16.0; `waygraph upgrade --codemod flows` converts them.
77
85
  - **`src/flows/` holds use cases and mirrors the front end's endpoints**: a folder per page (`flows/auth/login/`) with one
78
86
  file per scenario named for its outcome (`success.flow.ts`, `wrong-password.flow.ts`, `empty-fields.flow.ts`), and
79
87
  `(user-stories)/` (any `(group)`) for flows that cross pages. Export names are `<page><Outcome>Flow`. Not one file per
80
88
  Block chain, not a flat folder of unrelated flows.
81
89
  - **Blocks carry no stubs; the flow does.** Demo highlights are ctx stubs chained on the Block where the flow uses it:
82
- `AddItemBlock.stubAfter((ctx) => { ctx.ring("cart", { selector: DemoSel.cartCount, label: "AC · item in cart" }); })`.
90
+ `AddItemBlock.stubAfter((ctx) => { ctx.ui.ring("cart", { selector: DemoSel.cartCount, label: "AC · item in cart" }); })`.
83
91
  That chained function is the only form: a Block's definition (its options or `instruction`) takes no stub, and a stub
84
92
  is never a slot map. A Block used twice gets one `const AddShown = AddItemBlock.stubAfter(...)`. A stub that throws
85
93
  fails its step in the demo, named.
@@ -157,6 +157,13 @@ the entire reason this package exists. If a needed action truly has no Block, wr
157
157
  properly (mirroring a sibling Block's shape) and `auto reload` it in rather than raw-clicking
158
158
  past the gap.
159
159
 
160
+ To know what the page shows, read the `read` field every `status`/`send`/`reach` reply
161
+ carries (or `pilot read <id> [path]` for one part, uncut): it comes from the project's Read
162
+ Blocks (`*.read.block.ts`, beta) as typed JSON. Reach for `auto dom` only when `read` is empty
163
+ (no Read Block covers the page) or misses what you need - and then consider writing the Read
164
+ Block. `pilot watch <id>` streams every step, read and snapshot as JSON lines if you need to
165
+ follow changes you did not cause (another user, a server push).
166
+
160
167
  The one legitimate exception: `auto dom` to resolve a real ambiguity the menu's own labels
161
168
  can't (e.g. two products tied at the same displayed price) - and say so explicitly when you
162
169
  do it.