waygraph 0.10.5 → 0.10.7

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.5` — CLI less-is-more (`auto` / `demo` / `run` + `.flow.ts` paths). Page hubs still as in 0.9.0.
6
+ **Current:** `0.10.6` — `try auto` headed by default; `init` / create-waygraph scaffolds on 0.10 + `.flow.ts` scripts.
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
package/dist/cli.js CHANGED
@@ -1286,10 +1286,12 @@ function extractVerifyHighlights(block, resultTag) {
1286
1286
  * __wgNext no-ops once already consumed - but looked live when it wasn't).
1287
1287
  * Disables the button and mem-key textareas, and swaps whichever gate
1288
1288
  * text was showing (manual button or "Auto-advancing...") to "Running...".
1289
+ * When autoCollapsePanel is set (QA video / autoplay), tuck the stepper
1290
+ * away so ring captions + the app fill the frame - Dan: stepper + auto panels.
1289
1291
  */
1290
- async function markStepRunning(page) {
1292
+ async function markStepRunning(page, opts?: { autoCollapsePanel?: boolean }) {
1291
1293
  await page
1292
- .evaluate(() => {
1294
+ .evaluate(({ autoCollapsePanel }) => {
1293
1295
  const runBtn = document.getElementById("wg-run");
1294
1296
  if (runBtn) {
1295
1297
  runBtn.disabled = true;
@@ -1300,7 +1302,15 @@ async function markStepRunning(page) {
1300
1302
  document.querySelectorAll("#wg-panel textarea[data-key]").forEach((ta) => {
1301
1303
  ta.disabled = true;
1302
1304
  });
1303
- })
1305
+ if (autoCollapsePanel) {
1306
+ const panel = document.getElementById("wg-panel");
1307
+ if (panel) {
1308
+ panel.classList.add("wg-collapsed");
1309
+ const toggle = panel.querySelector("[data-wg-toggle]");
1310
+ if (toggle) toggle.textContent = "Show";
1311
+ }
1312
+ }
1313
+ }, { autoCollapsePanel: !!opts?.autoCollapsePanel })
1304
1314
  .catch(() => {});
1305
1315
  }
1306
1316
 
@@ -1815,7 +1825,9 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
1815
1825
  }
1816
1826
  }
1817
1827
  }
1818
- await markStepRunning(page);
1828
+ const recordingVideo = !!process.env.WAYGRAPH_VIDEO;
1829
+ const autoCollapsePanel = recordingVideo || (await currentAutoplay());
1830
+ await markStepRunning(page, { autoCollapsePanel });
1819
1831
  // NavBlock click-nav: label the upcoming Locator.click demo cursor as
1820
1832
  // "nav: <block>" so pia/demo watchers see cursor+pulse on click nav
1821
1833
  // (not only on regular Block clicks).
@@ -1997,6 +2009,18 @@ async function runChainedFlows(chainFlow, chainFlows, context, mem, page) {
1997
2009
  }
1998
2010
  }
1999
2011
 
2012
+ /** Parses WAYGRAPH_VIDEO_VIEWPORT / --video-viewport: "1920x1080" or "1920,1080". */
2013
+ function parseVideoViewport(raw: string | undefined): { width: number; height: number } | null {
2014
+ if (!raw?.trim()) return null;
2015
+ const m = raw.trim().match(/^(\d{3,5})[xX,](\d{3,5})$/);
2016
+ if (!m) return null;
2017
+ const width = Number(m[1]);
2018
+ const height = Number(m[2]);
2019
+ if (!Number.isFinite(width) || !Number.isFinite(height)) return null;
2020
+ if (width < 320 || height < 240 || width > 7680 || height > 4320) return null;
2021
+ return { width, height };
2022
+ }
2023
+
2000
2024
  async function main() {
2001
2025
  const projectDir = process.argv[2];
2002
2026
  const spec = process.argv[3];
@@ -2170,6 +2194,13 @@ async function main() {
2170
2194
  videoDir = videoEnv === "1" ? join(projectDir, ".waygraph-videos") : videoEnv;
2171
2195
  mkdirSync(videoDir, { recursive: true });
2172
2196
  }
2197
+ /** Fixed 16:9 capture for --video (demo QA and headless run). Step+video no longer maximizes. */
2198
+ const DEFAULT_VIDEO_VIEWPORT = { width: 1920, height: 1080 };
2199
+ const DEFAULT_RUN_VIDEO_VIEWPORT = { width: 1280, height: 720 };
2200
+ const videoViewportParsed = parseVideoViewport(process.env.WAYGRAPH_VIDEO_VIEWPORT);
2201
+ const fixedVideoViewport = videoDir
2202
+ ? (videoViewportParsed ?? (step ? DEFAULT_VIDEO_VIEWPORT : DEFAULT_RUN_VIDEO_VIEWPORT))
2203
+ : null;
2173
2204
  const engine = new Engine({ headless: !headed, slowMo });
2174
2205
  let result;
2175
2206
  // Non-interactive execution (no --step) runs the whole chain as one
@@ -2200,14 +2231,20 @@ async function main() {
2200
2231
  const browser = await chromium.launch({
2201
2232
  headless: !headed,
2202
2233
  slowMo,
2203
- args: step ? ["--start-maximized"] : [],
2234
+ // Maximized only for interactive step mode WITHOUT a fixed video viewport -
2235
+ // recording uses a locked 16:9 viewport so .webm has no gray letterboxing.
2236
+ args: step && !fixedVideoViewport ? ["--start-maximized"] : [],
2204
2237
  ...(executablePath ? { executablePath } : {}),
2205
2238
  });
2206
- const contextOpts = step
2207
- ? { baseURL, viewport: null }
2208
- : { baseURL, viewport: { width: 1280, height: 720 } };
2239
+ const contextOpts = fixedVideoViewport
2240
+ ? { baseURL, viewport: fixedVideoViewport }
2241
+ : step
2242
+ ? { baseURL, viewport: null }
2243
+ : { baseURL, viewport: { width: 1280, height: 720 } };
2209
2244
  if (videoDir) {
2210
- contextOpts.recordVideo = { dir: videoDir };
2245
+ contextOpts.recordVideo = fixedVideoViewport
2246
+ ? { dir: videoDir, size: fixedVideoViewport }
2247
+ : { dir: videoDir };
2211
2248
  }
2212
2249
  const context = await browser.newContext(contextOpts);
2213
2250
  let pageForVideo = null;
@@ -2557,26 +2594,27 @@ async function runTryDemo() {
2557
2594
  const TRY_AUTO_DATA = '{"saucedemo.credentials":{"username":"standard_user","password":"secret_sauce"}}';
2558
2595
  /**
2559
2596
  * `waygraph try auto` / `try auto:cli` - temp Sauce Demo explore.
2560
- * Default is CLI menu (same as `npm run auto:cli`). Pass `--headed` for the browser panel.
2597
+ * Default is the **headed** browser panel. Pass `auto:cli` or `--cli` for the
2598
+ * terminal menu (same rows).
2561
2599
  *
2562
2600
  * Runs as a child in the temp project so Block imports share that install's
2563
2601
  * `@playwright/test`. In-process explore from the outer CLI against a packed
2564
2602
  * `file:*.tgz` copy double-loads Playwright and silently yields 0 graph edges
2565
2603
  * ("LoginPage / No moves").
2566
2604
  */
2567
- async function runTryAuto(opts = { cli: true }) {
2605
+ async function runTryAuto(opts = { cli: false }) {
2568
2606
  const destDir = await prepareTryQuickstart("waygraph-try-auto");
2569
2607
  if (!destDir)
2570
2608
  return;
2571
2609
  process.env.WAYGRAPH_BASE_URL ??= "https://www.saucedemo.com";
2572
- const cli = opts.cli !== false;
2610
+ const cli = opts.cli === true;
2573
2611
  console.log(cli
2574
2612
  ? "waygraph try auto: CLI explore on Sauce Demo (temp dir).\n" +
2575
2613
  " Creds pre-seeded (saucedemo.credentials).\n" +
2576
2614
  " Pick [1] submit-login, then inventory Add/Remove / Open details.\n" +
2577
- " Type q to quit. Headed panel: waygraph try auto --headed\n"
2615
+ " Type q to quit. Headed panel: waygraph try auto\n"
2578
2616
  : "waygraph try auto: headed explore on Sauce Demo (temp dir).\n" +
2579
- " Creds pre-seeded. Prefer CLI: waygraph try auto:cli\n" +
2617
+ " Creds pre-seeded. CLI menu: waygraph try auto:cli\n" +
2580
2618
  " Pick submit-login, then inventory menus. Quit from the panel.\n");
2581
2619
  const bin = join(destDir, "node_modules", ".bin", "waygraph");
2582
2620
  const args = ["auto"];
@@ -2589,8 +2627,8 @@ async function runTryAuto(opts = { cli: true }) {
2589
2627
  console.log("\nwaygraph try auto: explorer closed.\n\n" +
2590
2628
  `Temp project:\n ${destDir}\n\n` +
2591
2629
  "Run again:\n" +
2592
- ` cd ${destDir} && npm run auto:cli\n` +
2593
- ` cd ${destDir} && npm run auto\n\n` +
2630
+ ` cd ${destDir} && npm run auto\n` +
2631
+ ` cd ${destDir} && npm run auto:cli\n\n` +
2594
2632
  "In-package: examples/saucedemo\n" +
2595
2633
  "Docs: https://deviate-dv8.github.io/waygraph/auto.html\n");
2596
2634
  }
@@ -2692,6 +2730,7 @@ function initCommand(projectName) {
2692
2730
  process.exit(1);
2693
2731
  }
2694
2732
  const targetDir = resolve(process.cwd(), projectName);
2733
+ const pkgName = basename(targetDir);
2695
2734
  if (existsSync(targetDir)) {
2696
2735
  const entries = readdirSync(targetDir);
2697
2736
  if (entries.length > 0) {
@@ -2714,15 +2753,18 @@ function initCommand(projectName) {
2714
2753
  rmSync(gitignoreSrc);
2715
2754
  }
2716
2755
  const packageJsonPath = join(targetDir, "package.json");
2717
- writeFileSync(packageJsonPath, readFileSync(packageJsonPath, "utf-8").replaceAll("__PROJECT_NAME__", projectName));
2718
- console.log(`Scaffolded ${projectName}/`);
2756
+ writeFileSync(packageJsonPath, readFileSync(packageJsonPath, "utf-8").replaceAll("__PROJECT_NAME__", pkgName));
2757
+ console.log(`Scaffolded ${pkgName}/`);
2719
2758
  console.log("");
2720
- console.log(` cd ${projectName}`);
2759
+ console.log(` cd ${relative(process.cwd(), targetDir) || pkgName}`);
2721
2760
  console.log(" npm install");
2722
2761
  console.log(" npx playwright install chromium");
2723
2762
  console.log(" npm test");
2763
+ console.log(" waygraph list # .flow.ts → export map");
2724
2764
  console.log(" waygraph check # nav hygiene + orphan Blocks");
2725
- console.log(" waygraph auto # interactive explore (headful picker)");
2765
+ console.log(" waygraph auto # interactive explore (headed panel)");
2766
+ console.log(" waygraph auto --cli # same menus in the terminal");
2767
+ console.log(" waygraph demo src/flows/example.flow.ts");
2726
2768
  console.log(" waygraph graph # static state graph JSON");
2727
2769
  }
2728
2770
  async function checkCommand(projectDir) {
@@ -2843,6 +2885,11 @@ function parseRunFlags(argv) {
2843
2885
  }
2844
2886
  }
2845
2887
  }
2888
+ else if (a === "--video-viewport" || a.startsWith("--video-viewport=")) {
2889
+ const t = takeFlagValue(argv, i, a, "--video-viewport");
2890
+ out.videoViewport = t.value;
2891
+ i = t.nextI;
2892
+ }
2846
2893
  else if (a === "--blocks" || a.startsWith("--blocks=")) {
2847
2894
  if (a.startsWith("--blocks=")) {
2848
2895
  out.blocks = a.slice("--blocks=".length);
@@ -2869,6 +2916,21 @@ function parseRunFlags(argv) {
2869
2916
  }
2870
2917
  return out;
2871
2918
  }
2919
+ /** Parses WAYGRAPH_VIDEO_VIEWPORT / --video-viewport: "1920x1080" or "1920,1080". */
2920
+ function parseVideoViewportFlag(raw) {
2921
+ if (!raw?.trim())
2922
+ return null;
2923
+ const m = raw.trim().match(/^(\d{3,5})[xX,](\d{3,5})$/);
2924
+ if (!m)
2925
+ return null;
2926
+ const width = Number(m[1]);
2927
+ const height = Number(m[2]);
2928
+ if (!Number.isFinite(width) || !Number.isFinite(height))
2929
+ return null;
2930
+ if (width < 320 || height < 240 || width > 7680 || height > 4320)
2931
+ return null;
2932
+ return { width, height };
2933
+ }
2872
2934
  /** Writes flag values into process.env so the chain child inherits them. Flags beat prior env. */
2873
2935
  function applyRunFlags(flags, opts) {
2874
2936
  if (flags.autoPlayVideo) {
@@ -2908,6 +2970,13 @@ function applyRunFlags(flags, opts) {
2908
2970
  if (flags.video !== undefined) {
2909
2971
  process.env.WAYGRAPH_VIDEO = flags.video || "1";
2910
2972
  }
2973
+ if (flags.videoViewport !== undefined) {
2974
+ if (!parseVideoViewportFlag(flags.videoViewport)) {
2975
+ console.error('waygraph: --video-viewport must look like "1920x1080"');
2976
+ process.exit(1);
2977
+ }
2978
+ process.env.WAYGRAPH_VIDEO_VIEWPORT = flags.videoViewport;
2979
+ }
2911
2980
  if (flags.data !== undefined) {
2912
2981
  process.env.WAYGRAPH_DATA = flags.data;
2913
2982
  }
@@ -3067,22 +3136,27 @@ Primary (less is more):
3067
3136
  --data '{...}' Mem seed JSON (or inline flow({...}))
3068
3137
  --auto-next Auto-advance steps (alias: --autoplay)
3069
3138
  --auto-play-video QA: --auto-next + --video (+ step)
3139
+ --video-viewport WxH Recording size (default demo: 1920x1080)
3070
3140
  --title / --base-url
3071
3141
  waygraph run [--blocks <flow|file|spec>] Execute (no overlay unless --step)
3072
3142
  --data '{...}'
3073
3143
  --non-headless Show browser
3074
3144
  --video [dir] Record .webm
3145
+ --video-viewport WxH Recording size (default run: 1280x720)
3075
3146
 
3076
3147
  Also:
3077
3148
  waygraph list | nav | validate | check | graph | init <name>
3078
3149
  waygraph try [demo|auto|auto:cli] One-shot saucedemo in a temp dir
3079
- try auto --headed Browser panel instead of CLI
3150
+ try auto Headed browser panel (default)
3151
+ try auto:cli / --cli Terminal menu instead
3152
+ try auto --headed Same as try auto (compat)
3080
3153
 
3081
3154
  Examples:
3082
3155
  waygraph list # file → export map
3083
3156
  waygraph run src/flows/shop.flow.ts --data '{...}'
3084
3157
  waygraph run --blocks shopFlow --non-headless --video
3085
3158
  waygraph demo --blocks src/flows/cart-bulk.flow.ts --auto-next
3159
+ waygraph try auto
3086
3160
  waygraph try auto:cli
3087
3161
  waygraph auto src/flows/shop.flow.ts --data '{...}' # run by file (same as run)
3088
3162
  waygraph auto --cli --data '{"saucedemo.credentials":{...}}'
@@ -3310,19 +3384,18 @@ async function main() {
3310
3384
  const flags = parseRunFlags(args.slice(1));
3311
3385
  applyRunFlags(flags);
3312
3386
  const modeRaw = (flags.positionals[0] ?? "demo").toLowerCase();
3313
- // auto:cli / auto-cli = CLI explore; auto --headed = browser panel; auto = CLI default
3387
+ // auto:cli / --cli = CLI; bare try auto = headed panel
3314
3388
  const mode = modeRaw === "auto:cli" || modeRaw === "auto-cli"
3315
3389
  ? "auto:cli"
3316
3390
  : modeRaw === "auto"
3317
3391
  ? "auto"
3318
3392
  : modeRaw;
3319
3393
  if (mode === "auto" || mode === "auto:cli") {
3320
- // CLI is the default for try auto (same menus as headed). --headed = panel.
3321
3394
  const cli = mode === "auto:cli" || flags.cli === true
3322
3395
  ? true
3323
3396
  : flags.headed === true || flags.nonHeadless === true
3324
3397
  ? false
3325
- : true;
3398
+ : false; // try auto default = headed
3326
3399
  await runTryAuto({ cli });
3327
3400
  }
3328
3401
  else if (mode === "demo" || mode === "") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waygraph",
3
- "version": "0.10.5",
3
+ "version": "0.10.7",
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.5"
26
+ "waygraph": "^0.10.6"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@playwright/test": "^1.55.0",
@@ -6,17 +6,19 @@
6
6
  "scripts": {
7
7
  "typecheck": "tsc --noEmit",
8
8
  "test": "playwright test",
9
- "flows": "waygraph list",
9
+ "list": "waygraph list",
10
10
  "check": "waygraph check",
11
11
  "auto": "waygraph auto",
12
- "graph": "waygraph graph",
13
- "demo": "waygraph demo exampleFlow"
12
+ "auto:cli": "waygraph auto --cli",
13
+ "demo": "waygraph demo src/flows/example.flow.ts",
14
+ "run": "waygraph run src/flows/example.flow.ts",
15
+ "graph": "waygraph graph"
14
16
  },
15
17
  "waygraph": {
16
18
  "baseUrl": "https://example.com"
17
19
  },
18
20
  "dependencies": {
19
- "waygraph": "^0.9.0"
21
+ "waygraph": "^0.10.6"
20
22
  },
21
23
  "devDependencies": {
22
24
  "@playwright/test": "^1.55.0",