waygraph 0.7.11 → 0.7.13

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 (84) hide show
  1. package/README.md +2 -0
  2. package/dist/auto-explore-run.js +15 -1
  3. package/dist/cli.d.ts +1 -1
  4. package/dist/cli.js +61 -21
  5. package/dist/step-overlay.js +29 -20
  6. package/examples/saucedemo/README.md +35 -8
  7. package/examples/saucedemo/docs/CONVENTION.md +32 -14
  8. package/examples/saucedemo/package-lock.json +118 -0
  9. package/examples/saucedemo/scripts/capture-wrong-login.mjs +2 -2
  10. package/examples/saucedemo/scripts/prove-wrong-login-runtime.mjs +2 -2
  11. package/examples/saucedemo/src/blocks/SITE-MAP.md +43 -0
  12. package/examples/saucedemo/src/blocks/saucedemo-web/NAV.md +26 -0
  13. package/examples/saucedemo/src/blocks/saucedemo-web/actions/submit-login-for-flow.ts +13 -0
  14. package/examples/saucedemo/src/blocks/{actions → saucedemo-web/actions}/submit-login.action.block.ts +14 -10
  15. package/{templates/quickstart/src/blocks → examples/saucedemo/src/blocks/saucedemo-web}/actions/submit-logout.action.block.ts +8 -3
  16. package/examples/saucedemo/src/blocks/saucedemo-web/cart/NAV.md +22 -0
  17. package/{templates/quickstart/src/blocks → examples/saucedemo/src/blocks/saucedemo-web/cart}/nav-cart.block.ts +8 -3
  18. package/{templates/quickstart/src/blocks → examples/saucedemo/src/blocks/saucedemo-web/cart}/nav-checkout-info.block.ts +8 -1
  19. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/NAV.md +21 -0
  20. package/{templates/quickstart/src/blocks → examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one}/actions/submit-checkout-info.action.block.ts +9 -6
  21. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/NAV.md +21 -0
  22. package/{templates/quickstart/src/blocks → examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two}/actions/finish-order.action.block.ts +8 -1
  23. package/examples/saucedemo/src/blocks/saucedemo-web/inventory/NAV.md +26 -0
  24. package/examples/saucedemo/src/blocks/{actions → saucedemo-web/inventory/actions}/add-to-cart.effect.block.ts +10 -4
  25. package/{templates/quickstart/src/blocks → examples/saucedemo/src/blocks/saucedemo-web/inventory}/actions/inventory-items.ts +8 -0
  26. package/{templates/quickstart/src/blocks → examples/saucedemo/src/blocks/saucedemo-web/inventory}/actions/remove-from-cart.effect.block.ts +10 -4
  27. package/examples/saucedemo/src/blocks/{nav-item-detail.block.ts → saucedemo-web/inventory/nav-item-detail.block.ts} +10 -4
  28. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/NAV.md +25 -0
  29. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-back-to-inventory.block.ts +18 -0
  30. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-back-to-products.block.ts +18 -0
  31. package/examples/saucedemo/src/blocks/{nav-continue-shopping.block.ts → saucedemo-web/inventory-item/nav-continue-shopping.block.ts} +8 -2
  32. package/examples/saucedemo/src/blocks/{nav-login.block.ts → saucedemo-web/nav-login.block.ts} +8 -3
  33. package/examples/saucedemo/src/flows/cart-continue.flow.ts +2 -2
  34. package/examples/saucedemo/src/flows/cart-mem.flow.ts +1 -1
  35. package/examples/saucedemo/src/flows/catalog-logout.flow.ts +3 -3
  36. package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +8 -8
  37. package/examples/saucedemo/src/flows/checkout.flow.ts +7 -7
  38. package/examples/saucedemo/src/flows/login.flow.ts +2 -2
  39. package/examples/saucedemo/src/flows/shop.flow.ts +5 -5
  40. package/examples/saucedemo/src/flows/smoke.flow.ts +3 -3
  41. package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +2 -2
  42. package/examples/saucedemo/tests/flow-block-verify.spec.ts +1 -1
  43. package/examples/saucedemo/tests/submit-login-branch.spec.ts +2 -2
  44. package/package.json +1 -1
  45. package/templates/quickstart/README.md +10 -0
  46. package/templates/quickstart/docs/CONVENTION.md +48 -26
  47. package/templates/quickstart/package.json +1 -1
  48. package/templates/quickstart/src/blocks/SITE-MAP.md +43 -0
  49. package/templates/quickstart/src/blocks/saucedemo-web/NAV.md +26 -0
  50. package/templates/quickstart/src/blocks/saucedemo-web/actions/submit-login-for-flow.ts +13 -0
  51. package/templates/quickstart/src/blocks/{actions → saucedemo-web/actions}/submit-login.action.block.ts +14 -10
  52. package/{examples/saucedemo/src/blocks → templates/quickstart/src/blocks/saucedemo-web}/actions/submit-logout.action.block.ts +8 -3
  53. package/templates/quickstart/src/blocks/saucedemo-web/cart/NAV.md +22 -0
  54. package/{examples/saucedemo/src/blocks → templates/quickstart/src/blocks/saucedemo-web/cart}/nav-cart.block.ts +8 -3
  55. package/{examples/saucedemo/src/blocks → templates/quickstart/src/blocks/saucedemo-web/cart}/nav-checkout-info.block.ts +8 -1
  56. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/NAV.md +21 -0
  57. package/{examples/saucedemo/src/blocks → templates/quickstart/src/blocks/saucedemo-web/checkout-step-one}/actions/submit-checkout-info.action.block.ts +9 -6
  58. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/NAV.md +21 -0
  59. package/{examples/saucedemo/src/blocks → templates/quickstart/src/blocks/saucedemo-web/checkout-step-two}/actions/finish-order.action.block.ts +8 -1
  60. package/templates/quickstart/src/blocks/saucedemo-web/inventory/NAV.md +26 -0
  61. package/templates/quickstart/src/blocks/{actions → saucedemo-web/inventory/actions}/add-to-cart.effect.block.ts +10 -4
  62. package/{examples/saucedemo/src/blocks → templates/quickstart/src/blocks/saucedemo-web/inventory}/actions/inventory-items.ts +8 -0
  63. package/{examples/saucedemo/src/blocks → templates/quickstart/src/blocks/saucedemo-web/inventory}/actions/remove-from-cart.effect.block.ts +10 -4
  64. package/templates/quickstart/src/blocks/{nav-item-detail.block.ts → saucedemo-web/inventory/nav-item-detail.block.ts} +10 -4
  65. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/NAV.md +25 -0
  66. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-back-to-inventory.block.ts +18 -0
  67. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-back-to-products.block.ts +18 -0
  68. package/templates/quickstart/src/blocks/{nav-continue-shopping.block.ts → saucedemo-web/inventory-item/nav-continue-shopping.block.ts} +8 -2
  69. package/templates/quickstart/src/blocks/{nav-login.block.ts → saucedemo-web/nav-login.block.ts} +8 -3
  70. package/templates/quickstart/src/flows/cart-continue.flow.ts +2 -2
  71. package/templates/quickstart/src/flows/cart-mem.flow.ts +1 -1
  72. package/templates/quickstart/src/flows/catalog-logout.flow.ts +3 -3
  73. package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +8 -8
  74. package/templates/quickstart/src/flows/checkout.flow.ts +7 -7
  75. package/templates/quickstart/src/flows/login.flow.ts +2 -2
  76. package/templates/quickstart/src/flows/shop.flow.ts +5 -5
  77. package/templates/quickstart/src/flows/smoke.flow.ts +3 -3
  78. package/templates/quickstart/src/flows/viewer-blocked.flow.ts +2 -2
  79. package/examples/saucedemo/src/blocks/actions/submit-login-for-flow.ts +0 -10
  80. package/examples/saucedemo/src/blocks/nav-back-to-inventory.block.ts +0 -15
  81. package/examples/saucedemo/src/blocks/nav-back-to-products.block.ts +0 -14
  82. package/templates/quickstart/src/blocks/actions/submit-login-for-flow.ts +0 -10
  83. package/templates/quickstart/src/blocks/nav-back-to-inventory.block.ts +0 -15
  84. package/templates/quickstart/src/blocks/nav-back-to-products.block.ts +0 -14
package/README.md CHANGED
@@ -23,6 +23,8 @@ Browser docs (quick start, **demo / step / autoplay**, **auto explore**, engine
23
23
  - **Demo contract:** [`docs/demo.html`](./docs/demo.html) — `--step` vs `--autoplay`
24
24
  - **Auto explore:** [`docs/auto.html`](./docs/auto.html) — Effect / MemNav picker (TS salt over Blocks)
25
25
  - **In-repo example:** [`examples/saucedemo`](./examples/saucedemo) — full Sauce Demo project
26
+ - **Convention showcase (Pages):** [`docs/saucedemo/`](./docs/saucedemo/) —
27
+ https://deviate-dv8.github.io/waygraph/saucedemo/
26
28
 
27
29
  This README stays the in-repo API narrative; Pages is the same material for eyeballing and cross-mesh handoff.
28
30
 
@@ -182,7 +182,21 @@ async function runOneBlock(engine, entry, context, page, mem, headful) {
182
182
  if (headful)
183
183
  await setAutoPanelRunning(page, entry.block.name);
184
184
  const flow = engine.defineFlow([start, entry.block, end]);
185
- await flow.run(context, mem, { page, closeOnFinish: false });
185
+ const BLOCK_TIMEOUT_MS = 45_000;
186
+ let timer;
187
+ try {
188
+ await Promise.race([
189
+ flow.run(context, mem, { page, closeOnFinish: false }),
190
+ new Promise((_, reject) => {
191
+ timer = setTimeout(() => reject(new Error(`Block "${entry.block.name}" timed out after ${BLOCK_TIMEOUT_MS / 1000}s - ` +
192
+ "click Quit and retry, or check network / mem credentials")), BLOCK_TIMEOUT_MS);
193
+ }),
194
+ ]);
195
+ }
196
+ finally {
197
+ if (timer)
198
+ clearTimeout(timer);
199
+ }
186
200
  await page.evaluate("delete window.__wgPendingNavClickLabel").catch(() => { });
187
201
  }
188
202
  const PANEL_CSS = "#wg-auto-panel{position:fixed;z-index:2147483647;left:50%;bottom:12px;transform:translateX(-50%);" +
package/dist/cli.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Commands (watch first, then run, then inspect, then scaffold):
6
6
  * demo <flow|spec> [project] Watch a flow/chain with step overlay (default STEP+headed)
7
- * try [demo] One-shot saucedemo onboarding in an OS temp dir
7
+ * try [demo|auto] One-shot saucedemo in an OS temp dir (demo=stepper, auto=explore)
8
8
  * chain <spec> [project] Ad-hoc Blocks by name (+ --step/--autoplay like demo)
9
9
  * run <flow> [project] Run a named flow (no overlay unless --step)
10
10
  * list / nav / validate Discover and inspect flows
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Commands (watch first, then run, then inspect, then scaffold):
6
6
  * demo <flow|spec> [project] Watch a flow/chain with step overlay (default STEP+headed)
7
- * try [demo] One-shot saucedemo onboarding in an OS temp dir
7
+ * try [demo|auto] One-shot saucedemo in an OS temp dir (demo=stepper, auto=explore)
8
8
  * chain <spec> [project] Ad-hoc Blocks by name (+ --step/--autoplay like demo)
9
9
  * run <flow> [project] Run a named flow (no overlay unless --step)
10
10
  * list / nav / validate Discover and inspect flows
@@ -2459,15 +2459,12 @@ async function wireQuickstartToPackageRoot(destDir) {
2459
2459
  writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
2460
2460
  }
2461
2461
  /**
2462
- * `waygraph try demo` - copy a self-contained saucedemo project into a temp
2463
- * dir (never the caller's cwd), run a chainFlow step demo (Sign In + Shop +
2464
- * blocked Viewer), then run the headless Playwright test, and print where
2465
- * everything lives.
2462
+ * Copy templates/quickstart into a fresh OS temp dir and wire it to this
2463
+ * package build. Returns destDir, or null after setting exitCode on failure.
2466
2464
  */
2467
- async function runTryDemo() {
2468
- const destDir = mkdtempSync(join(tmpdir(), "waygraph-try-demo-"));
2465
+ async function prepareTryQuickstart(label) {
2466
+ const destDir = mkdtempSync(join(tmpdir(), `${label}-`));
2469
2467
  cpSync(join(packageRoot(), "templates", "quickstart"), destDir, { recursive: true });
2470
- // Never copy dev node_modules / test output into the temp demo (Playwright double-load).
2471
2468
  rmSync(join(destDir, "node_modules"), { recursive: true, force: true });
2472
2469
  rmSync(join(destDir, "test-results"), { recursive: true, force: true });
2473
2470
  rmSync(join(destDir, "package-lock.json"), { force: true });
@@ -2477,14 +2474,26 @@ async function runTryDemo() {
2477
2474
  catch (err) {
2478
2475
  console.error(err instanceof Error ? err.message : String(err));
2479
2476
  process.exitCode = 1;
2480
- return;
2477
+ return null;
2481
2478
  }
2482
- const problem = await ensureDirPrereqs(destDir, "waygraph try demo");
2479
+ const problem = await ensureDirPrereqs(destDir, label);
2483
2480
  if (problem) {
2484
- console.error(`waygraph try demo: couldn't get ready.\n${problem}`);
2481
+ console.error(`${label}: couldn't get ready.\n${problem}`);
2485
2482
  process.exitCode = 1;
2486
- return;
2483
+ return null;
2487
2484
  }
2485
+ return destDir;
2486
+ }
2487
+ /**
2488
+ * `waygraph try demo` - copy a self-contained saucedemo project into a temp
2489
+ * dir (never the caller's cwd), run a chainFlow step demo (Sign In + Shop +
2490
+ * blocked Viewer), then run the headless Playwright test, and print where
2491
+ * everything lives.
2492
+ */
2493
+ async function runTryDemo() {
2494
+ const destDir = await prepareTryQuickstart("waygraph-try-demo");
2495
+ if (!destDir)
2496
+ return;
2488
2497
  process.env.WAYGRAPH_BASE_URL ??= "https://www.saucedemo.com";
2489
2498
  // Default: headed stepper, manual Next. --video records that session (not headless rush).
2490
2499
  process.env.WAYGRAPH_HEADED ??= "1";
@@ -2529,6 +2538,27 @@ async function runTryDemo() {
2529
2538
  "Permanent full example in this package: examples/saucedemo\n" +
2530
2539
  "Keep a permanent scaffold in your tree: waygraph init my-app\n");
2531
2540
  }
2541
+ /**
2542
+ * `waygraph try auto` - same temp Sauce Demo as try demo, but opens the
2543
+ * headed `waygraph auto` explorer (Effect Add/Remove, MemNav Open details).
2544
+ */
2545
+ async function runTryAuto() {
2546
+ const destDir = await prepareTryQuickstart("waygraph-try-auto");
2547
+ if (!destDir)
2548
+ return;
2549
+ process.env.WAYGRAPH_BASE_URL ??= "https://www.saucedemo.com";
2550
+ console.log("waygraph try auto: headed explore on Sauce Demo (temp dir).\n" +
2551
+ " Pick nav-login, then submit-login (creds are pre-seeded).\n" +
2552
+ " On inventory: Add/Remove Effect rows + Open details MemNav.\n" +
2553
+ " Quit from the panel when done.\n");
2554
+ await runAutoExplore(destDir, { baseURL: process.env.WAYGRAPH_BASE_URL });
2555
+ console.log("\nwaygraph try auto: explorer closed.\n\n" +
2556
+ `Temp project:\n ${destDir}\n\n` +
2557
+ "Run again:\n" +
2558
+ ` cd ${destDir} && npm run auto\n\n` +
2559
+ "In-package: examples/saucedemo\n" +
2560
+ "Docs: https://deviate-dv8.github.io/waygraph/saucedemo/\n");
2561
+ }
2532
2562
  async function validateFlows(projectDir) {
2533
2563
  const files = discoverFlows(projectDir);
2534
2564
  const results = [];
@@ -2858,7 +2888,7 @@ Usage (watch -> run -> inspect -> scaffold):
2858
2888
  --base-url, else WAYGRAPH_BASE_URL, else
2859
2889
  package.json waygraph.baseUrl, else
2860
2890
  playwright.config baseURL.
2861
- waygraph try [demo] One-shot saucedemo (temp dir) + npm test
2891
+ waygraph try [demo|auto] One-shot saucedemo (temp dir): demo=stepper+test, auto=explore
2862
2892
  waygraph chain <spec> [project] Ad-hoc Blocks by name ("a then b"); add
2863
2893
  --step / --autoplay for the same overlay
2864
2894
  waygraph run <flow> [project] Run a named flow (no overlay unless --step)
@@ -2885,6 +2915,7 @@ Demo / step / autoplay (read this - agents get this wrong):
2885
2915
  Same overlay on a chain: waygraph chain "login then nav-cart" . --step
2886
2916
  Unattended (no overlay): waygraph demo shopFlow . --no-step
2887
2917
  Onboarding (temp dir): npx waygraph try demo
2918
+ Explore (temp dir): npx waygraph try auto
2888
2919
 
2889
2920
  Other run flags (chain / demo / try) - flags beat WAYGRAPH_* env:
2890
2921
  --base-url <url> base URL for relative page.goto()
@@ -2921,13 +2952,12 @@ Prefer flags (or npm scripts that pass them) over an ENV soup of WAYGRAPH_*.
2921
2952
  a stateDiagram-v2 text export. Reports how
2922
2953
  many Blocks' Out couldn't be resolved
2923
2954
  (skipped, not crashed).
2924
- waygraph try [demo] One-shot saucedemo onboarding - "npx waygraph
2925
- try demo" (bare "try" is the same). Copies
2926
- into an OS temp dir (never your cwd),
2927
- npm installs there, runs chainFlow(loginFlow,
2928
- shopFlow) in step mode (Episode 1 + 2,
2929
- manual Next by default), then npm test,
2930
- then prints the temp path + source files.
2955
+ waygraph try [demo|auto] One-shot saucedemo - "npx waygraph try demo"
2956
+ (bare "try" = demo) or "npx waygraph try auto"
2957
+ for the headed explorer. Copies into an OS
2958
+ temp dir (never your cwd), npm installs there.
2959
+ demo: chainFlow step-through then npm test.
2960
+ auto: waygraph auto (Effect / MemNav picker).
2931
2961
  Permanent scaffold: waygraph init <name>
2932
2962
 
2933
2963
  "check" is a secondary sweep, complementary to the always-on TypeScript
@@ -3157,7 +3187,17 @@ async function main() {
3157
3187
  case "try": {
3158
3188
  const flags = parseRunFlags(args.slice(1));
3159
3189
  applyRunFlags(flags);
3160
- await runTryDemo();
3190
+ const mode = (flags.positionals[0] ?? "demo").toLowerCase();
3191
+ if (mode === "auto") {
3192
+ await runTryAuto();
3193
+ }
3194
+ else if (mode === "demo" || mode === "") {
3195
+ await runTryDemo();
3196
+ }
3197
+ else {
3198
+ console.error(`waygraph try: unknown mode "${mode}" - use "demo" or "auto"`);
3199
+ process.exitCode = 1;
3200
+ }
3161
3201
  break;
3162
3202
  }
3163
3203
  case "check": {
@@ -140,47 +140,56 @@ export function instrumentInteractionHighlighting(page, mem, slowMo) {
140
140
  // Keep unbound — originalFill.call(this, …) must receive the Locator as `this`, not the prototype.
141
141
  const originalFill = proto.fill;
142
142
  proto.fill = async function (value, options) {
143
+ const livePage = (typeof this.page === "function" ? this.page() : page);
143
144
  try {
144
- await installDemoChrome(page);
145
+ await installDemoChrome(livePage);
145
146
  const box = await this.boundingBox();
146
147
  if (box) {
147
148
  const label = memTrack.lastKeyName && Date.now() - memTrack.at < 3000
148
149
  ? "from mem: " + memTrack.lastKeyName
149
150
  : "writing";
150
- await moveCursorTo(page, box, cursorMs(500));
151
- await showRing(page, box, label);
151
+ await moveCursorTo(livePage, box, cursorMs(500));
152
+ await showRing(livePage, box, label);
152
153
  await new Promise((r) => setTimeout(r, 200));
153
154
  }
154
155
  }
155
156
  catch {
156
157
  /* best-effort */
157
158
  }
158
- try {
159
- await originalFill.call(this, "", options);
160
- const seqOpts = { delay: typeDelay() };
161
- if (options?.timeout !== undefined)
162
- seqOpts.timeout = options.timeout;
163
- await this.pressSequentially(String(value), seqOpts);
164
- }
165
- catch {
159
+ // Prefer a single fill - clear+pressSequentially can hang when the auto
160
+ // panel steals focus mid-type (looks stuck on "Running: submit-login").
161
+ if (slowMo || typeDelay() === 0) {
166
162
  await originalFill.call(this, value, options);
167
163
  }
168
- await hideRing(page);
164
+ else {
165
+ try {
166
+ await originalFill.call(this, "", options);
167
+ const seqOpts = { delay: typeDelay() };
168
+ if (options?.timeout !== undefined)
169
+ seqOpts.timeout = options.timeout;
170
+ await this.pressSequentially(String(value), seqOpts);
171
+ }
172
+ catch {
173
+ await originalFill.call(this, value, options);
174
+ }
175
+ }
176
+ await hideRing(livePage);
169
177
  };
170
178
  }
171
179
  if (!proto.__wgClickPatched) {
172
180
  proto.__wgClickPatched = true;
173
181
  const originalClick = proto.click;
174
182
  proto.click = async function (options) {
183
+ const livePage = (typeof this.page === "function" ? this.page() : page);
175
184
  let clickPoint = null;
176
185
  try {
177
- await installDemoChrome(page);
178
- await this.waitFor({ state: "visible", timeout: options?.timeout ?? 30000 }).catch(() => { });
186
+ await installDemoChrome(livePage);
187
+ await this.waitFor({ state: "visible", timeout: options?.timeout ?? 15000 }).catch(() => { });
179
188
  const box = await this.boundingBox();
180
189
  if (box) {
181
190
  let label = "click";
182
191
  try {
183
- const navLabel = await page.evaluate(() => window.__wgPendingNavClickLabel ?? null);
192
+ const navLabel = await livePage.evaluate(() => window.__wgPendingNavClickLabel ?? null);
184
193
  if (navLabel)
185
194
  label = String(navLabel);
186
195
  else {
@@ -192,8 +201,8 @@ export function instrumentInteractionHighlighting(page, mem, slowMo) {
192
201
  catch {
193
202
  /* ignore */
194
203
  }
195
- clickPoint = await moveCursorTo(page, box, cursorMs(600));
196
- await showRing(page, box, label);
204
+ clickPoint = await moveCursorTo(livePage, box, cursorMs(600));
205
+ await showRing(livePage, box, label);
197
206
  await new Promise((r) => setTimeout(r, clickPrePop()));
198
207
  }
199
208
  }
@@ -201,12 +210,12 @@ export function instrumentInteractionHighlighting(page, mem, slowMo) {
201
210
  /* best-effort */
202
211
  }
203
212
  if (clickPoint) {
204
- await clickPulseAt(page, clickPoint.x, clickPoint.y);
205
- await new Promise((r) => setTimeout(r, 500));
213
+ await clickPulseAt(livePage, clickPoint.x, clickPoint.y);
214
+ await new Promise((r) => setTimeout(r, 200));
206
215
  }
207
216
  const result = await originalClick.call(this, options);
208
217
  await new Promise((r) => setTimeout(r, clickPostPop()));
209
- await hideRing(page);
218
+ await hideRing(livePage).catch(() => { });
210
219
  return result;
211
220
  };
212
221
  }
@@ -1,12 +1,21 @@
1
- # Sauce Demo example
1
+ # Sauce Demo - convention showcase
2
2
 
3
- Full live consumer for [saucedemo.com](https://www.saucedemo.com) - Effect
4
- Add/Remove (`defineEffectBlock` / `*.effect.block.ts`), MemNav Open details,
5
- `waygraph auto`, and Playwright flows. Helpers are TypeScript salt: runtime is
6
- still just Blocks.
3
+ Full live consumer for [saucedemo.com](https://www.saucedemo.com). This tree is
4
+ the **reference** for waygraph consumer conventions:
7
5
 
8
- This tree is the in-package reference. `waygraph try demo` copies the sibling
9
- template at `templates/quickstart` (same Blocks) into an OS temp dir.
6
+ - Route folders under `src/blocks/saucedemo-web/` = real Swag Labs URLs
7
+ - **Nav** / **Effect** / **Action** file kinds (helpers are TypeScript salt;
8
+ runtime is still `Block`)
9
+ - `instanceOptions` menus for `waygraph auto` (Add/Remove + Open details)
10
+ - Detailed Kind / Helper / Route comments on every Block file
11
+ - Per-route `NAV.md` + root `src/blocks/SITE-MAP.md`
12
+
13
+ **GitHub Pages walkthrough:**
14
+ [docs/saucedemo](https://deviate-dv8.github.io/waygraph/saucedemo/)
15
+ (`docs/saucedemo/` in this repo - README, SITE-MAP, kinds, layout, auto, comments).
16
+
17
+ `waygraph try demo` copies the sibling template at `templates/quickstart`
18
+ (same Blocks) into an OS temp dir.
10
19
 
11
20
  ## Setup
12
21
 
@@ -28,4 +37,22 @@ npx playwright install chromium
28
37
  | Stepper (Auto-advance) | `npm run demo:autoplay` |
29
38
  | List flows | `npm run flows` |
30
39
 
31
- Docs: [Auto explore](../../docs/auto.html) · [Demo / step](../../docs/demo.html)
40
+ ## Layout (short)
41
+
42
+ ```text
43
+ src/blocks/
44
+ SITE-MAP.md
45
+ saucedemo-web/ # URL /
46
+ NAV.md
47
+ nav-login.block.ts
48
+ actions/ # submit-login, logout, …
49
+ inventory/ # URL /inventory.html
50
+ nav-item-detail.block.ts # MemNav
51
+ actions/*.effect.block.ts # Add / Remove
52
+ cart/ # URL /cart.html
53
+ checkout-step-one/
54
+ checkout-step-two/
55
+ ```
56
+
57
+ Docs: [Sauce Demo showcase](../../docs/saucedemo/) ·
58
+ [Auto explore](../../docs/auto.html) · [Consumer layout](../../docs/consumer.html)
@@ -27,20 +27,38 @@ app changes URL as a side effect of `#continue`.
27
27
 
28
28
  ```text
29
29
  src/blocks/
30
- nav-login.block.ts
31
- nav-cart.block.ts
32
- nav-checkout-info.block.ts
33
- nav-item-detail.block.ts
34
- nav-back-to-inventory.block.ts
35
- nav-continue-shopping.block.ts
36
- nav-back-to-products.block.ts
37
- actions/
38
- submit-login.action.block.ts
39
- add-to-cart.effect.block.ts
40
- remove-from-cart.effect.block.ts
41
- submit-checkout-info.action.block.ts
42
- finish-order.action.block.ts
43
- submit-logout.action.block.ts
30
+ SITE-MAP.md
31
+ saucedemo-web/ # THIS FOLDER IS URL "/"
32
+ NAV.md
33
+ nav-login.block.ts
34
+ actions/
35
+ submit-login.action.block.ts
36
+ submit-login-for-flow.ts
37
+ submit-logout.action.block.ts
38
+ inventory/ # /inventory.html
39
+ NAV.md
40
+ nav-item-detail.block.ts
41
+ actions/
42
+ inventory-items.ts
43
+ add-to-cart.effect.block.ts
44
+ remove-from-cart.effect.block.ts
45
+ inventory-item/ # /inventory-item.html
46
+ NAV.md
47
+ nav-continue-shopping.block.ts
48
+ nav-back-to-products.block.ts
49
+ nav-back-to-inventory.block.ts
50
+ cart/ # /cart.html
51
+ NAV.md
52
+ nav-cart.block.ts
53
+ nav-checkout-info.block.ts
54
+ checkout-step-one/ # /checkout-step-one.html
55
+ NAV.md
56
+ actions/
57
+ submit-checkout-info.action.block.ts
58
+ checkout-step-two/ # /checkout-step-two.html
59
+ NAV.md
60
+ actions/
61
+ finish-order.action.block.ts
44
62
  src/states/
45
63
  src/flows/
46
64
  ```
@@ -0,0 +1,118 @@
1
+ {
2
+ "name": "waygraph-example-saucedemo",
3
+ "version": "0.0.1",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "waygraph-example-saucedemo",
9
+ "version": "0.0.1",
10
+ "dependencies": {
11
+ "waygraph": "file:../.."
12
+ },
13
+ "devDependencies": {
14
+ "@playwright/test": "^1.55.0",
15
+ "@types/node": "^26.0.0",
16
+ "typescript": "^5.6.0"
17
+ }
18
+ },
19
+ "../..": {
20
+ "version": "0.7.11",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "tsx": "^4.0.0"
24
+ },
25
+ "bin": {
26
+ "waygraph": "bin/waygraph"
27
+ },
28
+ "devDependencies": {
29
+ "@playwright/test": "^1.55.0",
30
+ "@types/node": "^26.0.0",
31
+ "typescript": "^5.6.0"
32
+ },
33
+ "peerDependencies": {
34
+ "@playwright/test": "^1.40.0"
35
+ }
36
+ },
37
+ "node_modules/@playwright/test": {
38
+ "version": "1.63.0",
39
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.63.0.tgz",
40
+ "integrity": "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==",
41
+ "dev": true,
42
+ "license": "Apache-2.0",
43
+ "dependencies": {
44
+ "playwright": "1.63.0"
45
+ },
46
+ "bin": {
47
+ "playwright": "cli.js"
48
+ },
49
+ "engines": {
50
+ "node": ">=20"
51
+ }
52
+ },
53
+ "node_modules/@types/node": {
54
+ "version": "26.6.1",
55
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.1.tgz",
56
+ "integrity": "sha512-VqGJBMCtdhqkBUCcBLvywI0NJ+KLuVzgNnlBUNFOQjqVxzo2lxLUNg1DSey8+u2u6ktswSAxg+s68QLzWHNOuA==",
57
+ "dev": true,
58
+ "license": "MIT",
59
+ "dependencies": {
60
+ "undici-types": "~8.9.0"
61
+ }
62
+ },
63
+ "node_modules/playwright": {
64
+ "version": "1.63.0",
65
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz",
66
+ "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==",
67
+ "dev": true,
68
+ "license": "Apache-2.0",
69
+ "dependencies": {
70
+ "playwright-core": "1.63.0"
71
+ },
72
+ "bin": {
73
+ "playwright": "cli.js"
74
+ },
75
+ "engines": {
76
+ "node": ">=20"
77
+ }
78
+ },
79
+ "node_modules/playwright-core": {
80
+ "version": "1.63.0",
81
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz",
82
+ "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==",
83
+ "dev": true,
84
+ "license": "Apache-2.0",
85
+ "bin": {
86
+ "playwright-core": "cli.js"
87
+ },
88
+ "engines": {
89
+ "node": ">=20"
90
+ }
91
+ },
92
+ "node_modules/typescript": {
93
+ "version": "5.9.3",
94
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
95
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
96
+ "dev": true,
97
+ "license": "Apache-2.0",
98
+ "bin": {
99
+ "tsc": "bin/tsc",
100
+ "tsserver": "bin/tsserver"
101
+ },
102
+ "engines": {
103
+ "node": ">=14.17"
104
+ }
105
+ },
106
+ "node_modules/undici-types": {
107
+ "version": "8.9.0",
108
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz",
109
+ "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==",
110
+ "dev": true,
111
+ "license": "MIT"
112
+ },
113
+ "node_modules/waygraph": {
114
+ "resolved": "../..",
115
+ "link": true
116
+ }
117
+ }
118
+ }
@@ -86,9 +86,9 @@ async function snap(page, name) {
86
86
  const { graph, library } = await buildExploreContext(root);
87
87
  const nodeNames = graph.nodes.map((n) => n.checkpoint);
88
88
 
89
- const { NavLoginBlock } = await import(pathToFileURL(join(root, "src/blocks/nav-login.block.js")).href);
89
+ const { NavLoginBlock } = await import(pathToFileURL(join(root, "src/blocks/saucedemo-web/nav-login.block.js")).href);
90
90
  const { SubmitLoginActionBlock } = await import(
91
- pathToFileURL(join(root, "src/blocks/actions/submit-login.action.block.js")).href
91
+ pathToFileURL(join(root, "src/blocks/saucedemo-web/actions/submit-login.action.block.js")).href
92
92
  );
93
93
  const { LoginCreds } = await import(pathToFileURL(join(root, "src/states/checkout.mem-keys.js")).href);
94
94
 
@@ -13,9 +13,9 @@ await import(require.resolve("tsx/esm"));
13
13
  const root = join(dirname(fileURLToPath(import.meta.url)), "..");
14
14
  const waygraphDist = join(root, "../../dist");
15
15
  const { Engine, start, end, MemPage } = await import(pathToFileURL(join(waygraphDist, "index.js")).href);
16
- const { NavLoginBlock } = await import(pathToFileURL(join(root, "src/blocks/nav-login.block.js")).href);
16
+ const { NavLoginBlock } = await import(pathToFileURL(join(root, "src/blocks/saucedemo-web/nav-login.block.js")).href);
17
17
  const { SubmitLoginActionBlock } = await import(
18
- pathToFileURL(join(root, "src/blocks/actions/submit-login.action.block.js")).href
18
+ pathToFileURL(join(root, "src/blocks/saucedemo-web/actions/submit-login.action.block.js")).href
19
19
  );
20
20
  const { LoginCreds } = await import(pathToFileURL(join(root, "src/states/checkout.mem-keys.js")).href);
21
21
 
@@ -0,0 +1,43 @@
1
+ # SITE-MAP - Sauce Demo blocks
2
+
3
+ Namespace `saucedemo-web/` is URL `/` for [saucedemo.com](https://www.saucedemo.com).
4
+ Route folders mirror real Swag Labs paths (consumer convention).
5
+
6
+ ```text
7
+ src/blocks/
8
+ SITE-MAP.md
9
+ saucedemo-web/ # URL /
10
+ NAV.md
11
+ nav-login.block.ts # Nav defineNavBlock url:/
12
+ actions/
13
+ submit-login.action.block.ts # Action defineBlock
14
+ submit-login-for-flow.ts # helper (not a discoverable *.block.ts alone)
15
+ submit-logout.action.block.ts # Action
16
+ inventory/ # URL /inventory.html
17
+ NAV.md
18
+ nav-item-detail.block.ts # Nav defineMemNavBlock (instance row)
19
+ actions/
20
+ inventory-items.ts # DOM helpers for Effects (not a Block)
21
+ add-to-cart.effect.block.ts # Effect defineEffectBlock
22
+ remove-from-cart.effect.block.ts
23
+ inventory-item/ # URL /inventory-item.html
24
+ NAV.md
25
+ nav-continue-shopping.block.ts
26
+ nav-back-to-products.block.ts
27
+ nav-back-to-inventory.block.ts
28
+ cart/ # URL /cart.html
29
+ NAV.md
30
+ nav-cart.block.ts # Nav click header cart
31
+ nav-checkout-info.block.ts
32
+ checkout-step-one/ # URL /checkout-step-one.html
33
+ NAV.md
34
+ actions/
35
+ submit-checkout-info.action.block.ts
36
+ checkout-step-two/ # URL /checkout-step-two.html
37
+ NAV.md
38
+ actions/
39
+ finish-order.action.block.ts
40
+ ```
41
+
42
+ Checkpoints and mem keys live under `src/states/`. Flows under `src/flows/`
43
+ compose Blocks into episodes for `waygraph demo` / Playwright.
@@ -0,0 +1,26 @@
1
+ # saucedemo-web/ - URL `/`
2
+
3
+ Login page for Swag Labs. This folder is the namespace root (`/` in the browser).
4
+
5
+ ## URL
6
+
7
+ - `/` (login form)
8
+
9
+ ## Nav blocks
10
+
11
+ | File | Helper | Notes |
12
+ |------|--------|-------|
13
+ | `nav-login.block.ts` | `defineNavBlock` | `url: "/"` - only goto onto login |
14
+
15
+ ## Effects / actions
16
+
17
+ | File | Kind | Notes |
18
+ |------|------|-------|
19
+ | `actions/submit-login.action.block.ts` | Action | Form submit; branches LoggedIn vs LoginPage |
20
+ | `actions/submit-login-for-flow.ts` | helper | Typed alias for linear Flows |
21
+ | `actions/submit-logout.action.block.ts` | Action | Burger menu logout -> LoginPage |
22
+
23
+ ## Related mem keys / checkpoints
24
+
25
+ - Mem: `saucedemo.credentials` (`LoginCreds`)
26
+ - Checkpoints: `LoginPage`, `LoggedIn` (success path of submit-login)
@@ -0,0 +1,13 @@
1
+ import type { DefinedBlock } from "waygraph";
2
+ import type { LoginPage, LoggedIn } from "../../../states/checkout.states.js";
3
+ import { SubmitLoginActionBlock } from "./submit-login.action.block.js";
4
+
5
+ /**
6
+ * Kind: helper (not a discoverable Block file)
7
+ * Helper: typed alias over SubmitLoginActionBlock
8
+ * Route: saucedemo-web/actions/ (page URL /)
9
+ *
10
+ * DefinedBlock cast so linear Flows can chain .withVerify on Out=LoggedIn;
11
+ * runtime still branches on bad credentials via SubmitLoginActionBlock.
12
+ */
13
+ export const SubmitLoginForFlow = SubmitLoginActionBlock as unknown as DefinedBlock<LoginPage, LoggedIn>;