waygraph 0.10.1 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  A typed graph of reusable Blocks for driving a browser through E2E flows, in place of a
4
4
  flat pile of ad hoc helper functions.
5
5
 
6
- **Current:** `0.10.0` — CLI less-is-more (`auto` / `demo` / `run` + flags). Page hubs still as in 0.9.0.
6
+ **Current:** `0.10.2` — CLI less-is-more (`auto` / `demo` / `run` + flags). Page hubs still as in 0.9.0.
7
7
 
8
8
  This repo is the `waygraph` npm package itself. The full live **Sauce Demo** example
9
9
  (Page inventory hub, Effect Add/Remove, MemNav Open details, `waygraph auto`) ships
@@ -262,7 +262,7 @@ Deliberately **not yet implemented** (tracked on the project board):
262
262
  Mermaid) is `waygraph graph [project]` (`--mermaid`). Unattended JSON execution is
263
263
  `chain` + `WAYGRAPH_JSON=1`.
264
264
 
265
- **Friendly demo / run / auto (0.10.0 — less is more):**
265
+ **Friendly demo / run / auto (0.10.2 — less is more):**
266
266
 
267
267
  | Want | Command |
268
268
  |------|---------|
@@ -538,8 +538,14 @@ export async function runAutoExplore(projectDir, options = {}) {
538
538
  if (cli)
539
539
  console.error(`waygraph auto: ${menu.flat.length} move(s) available`);
540
540
  if (menu.flat.length === 0) {
541
+ const fromHere = graph.edges.filter((e) => e.from === here).map((e) => e.block);
542
+ const missing = [...new Set(fromHere)].filter((b) => !library.byName.has(b));
541
543
  console.error(here
542
- ? `waygraph auto: no runnable Blocks on "${here}" - nothing on this page matches the graph.`
544
+ ? `waygraph auto: no runnable Blocks on "${here}"` +
545
+ (fromHere.length
546
+ ? ` (graph has: ${[...new Set(fromHere)].join(", ")}${missing.length ? `; not loaded: ${missing.join(", ")}` : ""})`
547
+ : " - nothing on this page matches the graph.") +
548
+ (cli ? "" : " Tip: waygraph auto --cli")
543
549
  : "waygraph auto: location unknown - pick nav-login (Start here) if shown.");
544
550
  if (cli)
545
551
  break;
@@ -85,14 +85,18 @@ export async function loadBlockLibrary(projectDir) {
85
85
  const byName = new Map();
86
86
  const navBlocks = [];
87
87
  for (const file of walkDir(projectDir, /\.block\.ts$/)) {
88
+ const relFile = relative(projectDir, file);
88
89
  let mod;
89
90
  try {
90
91
  mod = await importModule(file);
91
92
  }
92
- catch {
93
+ catch (err) {
94
+ const msg = err instanceof Error ? err.message : String(err);
95
+ if (/second time|playwright/i.test(msg)) {
96
+ console.error(`waygraph auto: skip ${relFile}: ${msg.split("\n")[0]}`);
97
+ }
93
98
  continue;
94
99
  }
95
- const relFile = relative(projectDir, file);
96
100
  for (const [exportName, exported] of Object.entries(mod)) {
97
101
  if (!isBlockLike(exported))
98
102
  continue;
@@ -284,9 +288,40 @@ export async function buildExploreMenu(page, graph, library, here) {
284
288
  }
285
289
  }
286
290
  const flat = sections.flatMap((s) => s.edges);
291
+ // Stuck screen: graph knows actions from `here` but none made it into the menu
292
+ // (failed library load, empty instanceOptions, etc.). Surface them so LoginPage
293
+ // never shows a dead "No moves" when submit-login is in the graph.
294
+ if (here !== null && flat.length === 0) {
295
+ const rescue = [];
296
+ const rescued = new Set();
297
+ for (const edge of graph.edges) {
298
+ if (edge.kind !== "action" || edge.from !== here)
299
+ continue;
300
+ if (rescued.has(edge.block))
301
+ continue;
302
+ const entry = library.byName.get(edge.block);
303
+ if (!entry)
304
+ continue;
305
+ if (entry.block.instanceOptions)
306
+ continue; // still need live rows
307
+ rescued.add(edge.block);
308
+ rescue.push(edge);
309
+ }
310
+ if (rescue.length > 0) {
311
+ sections.push({
312
+ title: `Methods from ${here}`,
313
+ edges: rescue.sort((a, b) => a.block.localeCompare(b.block)),
314
+ });
315
+ return { here, sections, flat: sections.flatMap((s) => s.edges) };
316
+ }
317
+ }
287
318
  return { here, sections, flat };
288
319
  }
289
320
  export async function buildExploreContext(projectDir) {
290
- const [graph, library] = await Promise.all([discoverGraph(projectDir), loadBlockLibrary(projectDir)]);
321
+ // Sequential: parallel import of the same *.block.ts tree raced Playwright's
322
+ // dual-copy guard in some installs and left graph edges without library entries
323
+ // (LoginPage → "No moves").
324
+ const library = await loadBlockLibrary(projectDir);
325
+ const graph = await discoverGraph(projectDir);
291
326
  return { graph, library };
292
327
  }
package/dist/cli.js CHANGED
@@ -2548,24 +2548,41 @@ async function runTryDemo() {
2548
2548
  "Permanent full example in this package: examples/saucedemo\n" +
2549
2549
  "Keep a permanent scaffold in your tree: waygraph init my-app\n");
2550
2550
  }
2551
+ const TRY_AUTO_DATA = '{"saucedemo.credentials":{"username":"standard_user","password":"secret_sauce"}}';
2551
2552
  /**
2552
- * `waygraph try auto` - same temp Sauce Demo as try demo, then headed
2553
- * `waygraph auto` (same menus as `npm run auto:cli`).
2553
+ * `waygraph try auto` / `try auto:cli` - temp Sauce Demo explore.
2554
+ * Default is CLI menu (same as `npm run auto:cli`). Pass `--headed` for the browser panel.
2555
+ *
2556
+ * Runs as a child in the temp project so Block imports share that install's
2557
+ * `@playwright/test`. In-process explore from the outer CLI against a packed
2558
+ * `file:*.tgz` copy double-loads Playwright and silently yields 0 graph edges
2559
+ * ("LoginPage / No moves").
2554
2560
  */
2555
- async function runTryAuto() {
2561
+ async function runTryAuto(opts = { cli: true }) {
2556
2562
  const destDir = await prepareTryQuickstart("waygraph-try-auto");
2557
2563
  if (!destDir)
2558
2564
  return;
2559
2565
  process.env.WAYGRAPH_BASE_URL ??= "https://www.saucedemo.com";
2560
- console.log("waygraph try auto: headed explore on Sauce Demo (temp dir).\n" +
2561
- " Prefer CLI next time: cd <temp> && npm run auto:cli\n" +
2562
- " Pick LoginPage / submit-login (creds pre-seeded via --data on auto:cli).\n" +
2563
- " On inventory: Add/Remove Effect rows + Open details MemNav.\n" +
2564
- " Quit from the panel when done.\n");
2565
- await runAutoExplore(destDir, { baseURL: process.env.WAYGRAPH_BASE_URL });
2566
+ const cli = opts.cli !== false;
2567
+ console.log(cli
2568
+ ? "waygraph try auto: CLI explore on Sauce Demo (temp dir).\n" +
2569
+ " Creds pre-seeded (saucedemo.credentials).\n" +
2570
+ " Pick [1] submit-login, then inventory Add/Remove / Open details.\n" +
2571
+ " Type q to quit. Headed panel: waygraph try auto --headed\n"
2572
+ : "waygraph try auto: headed explore on Sauce Demo (temp dir).\n" +
2573
+ " Creds pre-seeded. Prefer CLI: waygraph try auto:cli\n" +
2574
+ " Pick submit-login, then inventory menus. Quit from the panel.\n");
2575
+ const bin = join(destDir, "node_modules", ".bin", "waygraph");
2576
+ const args = ["auto"];
2577
+ if (cli)
2578
+ args.push("--cli");
2579
+ args.push("--data", TRY_AUTO_DATA);
2580
+ const code = await runInherited(bin, args, destDir);
2581
+ if (code !== 0)
2582
+ process.exitCode = code;
2566
2583
  console.log("\nwaygraph try auto: explorer closed.\n\n" +
2567
2584
  `Temp project:\n ${destDir}\n\n` +
2568
- "Run again (CLI first):\n" +
2585
+ "Run again:\n" +
2569
2586
  ` cd ${destDir} && npm run auto:cli\n` +
2570
2587
  ` cd ${destDir} && npm run auto\n\n` +
2571
2588
  "In-package: examples/saucedemo\n" +
@@ -2781,6 +2798,9 @@ function parseRunFlags(argv) {
2781
2798
  else if (a === "--cli") {
2782
2799
  out.cli = true;
2783
2800
  }
2801
+ else if (a === "--headed") {
2802
+ out.headed = true;
2803
+ }
2784
2804
  else if (a === "--mermaid") {
2785
2805
  out.mermaid = true;
2786
2806
  }
@@ -2971,10 +2991,12 @@ Primary (less is more):
2971
2991
 
2972
2992
  Also:
2973
2993
  waygraph list | nav | validate | check | graph | init <name>
2974
- waygraph try [demo|auto] One-shot saucedemo in a temp dir
2994
+ waygraph try [demo|auto|auto:cli] One-shot saucedemo in a temp dir
2995
+ try auto --headed Browser panel instead of CLI
2975
2996
 
2976
2997
  Examples:
2977
- waygraph auto --cli
2998
+ waygraph try auto:cli
2999
+ waygraph auto --cli --data '{"saucedemo.credentials":{...}}'
2978
3000
  waygraph auto --blocks LoginPage OrderComplete
2979
3001
  waygraph demo --blocks shopFlow --auto-next
2980
3002
  waygraph demo --blocks cartBulkFlow --data '{"saucedemo.credentials":{...}}' --auto-play-video
@@ -3170,15 +3192,27 @@ async function main() {
3170
3192
  case "try": {
3171
3193
  const flags = parseRunFlags(args.slice(1));
3172
3194
  applyRunFlags(flags);
3173
- const mode = (flags.positionals[0] ?? "demo").toLowerCase();
3174
- if (mode === "auto") {
3175
- await runTryAuto();
3195
+ const modeRaw = (flags.positionals[0] ?? "demo").toLowerCase();
3196
+ // auto:cli / auto-cli = CLI explore; auto --headed = browser panel; auto = CLI default
3197
+ const mode = modeRaw === "auto:cli" || modeRaw === "auto-cli"
3198
+ ? "auto:cli"
3199
+ : modeRaw === "auto"
3200
+ ? "auto"
3201
+ : modeRaw;
3202
+ if (mode === "auto" || mode === "auto:cli") {
3203
+ // CLI is the default for try auto (same menus as headed). --headed = panel.
3204
+ const cli = mode === "auto:cli" || flags.cli === true
3205
+ ? true
3206
+ : flags.headed === true || flags.nonHeadless === true
3207
+ ? false
3208
+ : true;
3209
+ await runTryAuto({ cli });
3176
3210
  }
3177
3211
  else if (mode === "demo" || mode === "") {
3178
3212
  await runTryDemo();
3179
3213
  }
3180
3214
  else {
3181
- console.error(`waygraph try: unknown mode "${mode}" - use "demo" or "auto"`);
3215
+ console.error(`waygraph try: unknown mode "${modeRaw}" - use "demo", "auto", or "auto:cli"`);
3182
3216
  process.exitCode = 1;
3183
3217
  }
3184
3218
  break;
package/dist/graph.js CHANGED
@@ -307,7 +307,12 @@ export async function discoverGraph(projectDir) {
307
307
  try {
308
308
  mod = await importModule(file);
309
309
  }
310
- catch {
310
+ catch (err) {
311
+ const msg = err instanceof Error ? err.message : String(err);
312
+ // Dual @playwright/test installs fail here - surface instead of empty graph.
313
+ if (/second time|playwright/i.test(msg)) {
314
+ console.error(`waygraph graph: skip ${relFile}: ${msg.split("\n")[0]}`);
315
+ }
311
316
  continue;
312
317
  }
313
318
  const src = readFileSync(file, "utf-8");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waygraph",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "type": "module",
5
5
  "description": "A typed graph of reusable Blocks for driving a browser through E2E flows.",
6
6
  "license": "MIT",
@@ -23,7 +23,7 @@
23
23
  "baseUrl": "https://www.saucedemo.com"
24
24
  },
25
25
  "dependencies": {
26
- "waygraph": "^0.10.1"
26
+ "waygraph": "^0.10.2"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@playwright/test": "^1.55.0",