waygraph 0.10.3 → 0.10.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.
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.2` — CLI less-is-more (`auto` / `demo` / `run` + flags). Page hubs still as in 0.9.0.
6
+ **Current:** `0.10.4` — CLI less-is-more (`auto` / `demo` / `run` + `.flow.ts` paths). 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
@@ -22,7 +22,7 @@ Browser docs (quick start, **demo / run**, **auto explore**, engine handout, con
22
22
  - **Source:** [`docs/`](./docs/) — static HTML, no build step
23
23
  - **Local preview:** `npm run docs:preview` → http://127.0.0.1:4173/
24
24
  - **Deploy:** push to `main` touching `docs/**` runs [`.github/workflows/pages.yml`](./.github/workflows/pages.yml) (`workflow_dispatch` also works)
25
- - **Demo / run contract:** [`docs/demo.html`](./docs/demo.html) — `demo` / `run` / flags (`--auto-next`, `--auto-play-video`)
25
+ - **Demo / run contract:** [`docs/demo.html`](./docs/demo.html) — `list` / `.flow.ts` path / export / flags
26
26
  - **Auto explore:** [`docs/auto.html`](./docs/auto.html) — prefer `auto --cli` / `npm run auto:cli` (same menus as headed)
27
27
  - **In-repo example:** [`examples/saucedemo`](./examples/saucedemo) — full Sauce Demo project
28
28
  - **Convention showcase (Pages):** [`docs/saucedemo/`](./docs/saucedemo/) —
@@ -262,11 +262,14 @@ 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.2 — less is more):**
265
+ **Friendly demo / run / auto (0.10.4 — flows are files):**
266
266
 
267
267
  | Want | Command |
268
268
  |------|---------|
269
- | Manual watch | `waygraph demo --blocks shopFlow` |
269
+ | List flows | `waygraph list` → `src/flows/shop.flow.ts shopFlow` |
270
+ | Run by file | `waygraph run src/flows/shop.flow.ts --data '{…}'` |
271
+ | Run by export | `waygraph run --blocks shopFlow` |
272
+ | Manual watch | `waygraph demo src/flows/shop.flow.ts` |
270
273
  | Auto-advance | `waygraph demo --blocks shopFlow --auto-next` |
271
274
  | QA watch + record | `waygraph demo --blocks shopFlow --auto-play-video` |
272
275
  | Ad-hoc Blocks | `waygraph run --blocks "login then nav-cart" --data '{…}'` |
@@ -274,6 +277,7 @@ Mermaid) is `waygraph graph [project]` (`--mermaid`). Unattended JSON execution
274
277
  | Explore | `waygraph auto` / `waygraph auto --cli` |
275
278
  | Path-find | `waygraph auto --blocks LoginPage OrderComplete` |
276
279
 
280
+ `--blocks` / positional accepts a Flow export, a `.flow.ts` path, or `"a then b"`.
277
281
  `--auto-next` (alias `--autoplay`) = panel Auto-advance. `--auto-play-video` is **demo only**.
278
282
  `chain` remains a compat alias for `run --blocks` / `auto --blocks`.
279
283
 
package/dist/cli.js CHANGED
@@ -2020,9 +2020,14 @@ async function main() {
2020
2020
  const flow = await findFlow(projectDir, bareRef);
2021
2021
  if (flow && typeof flow.blocks === "function") {
2022
2022
  const blockInfos = flow.blocks();
2023
+ // Same --data / WAYGRAPH_DATA seeding as the multi-segment path.
2024
+ // Without this, \`waygraph run shop.flow.ts --data '{...}'\` (and bare
2025
+ // export names) hit preflight with an empty MemPage.
2026
+ const seedMem = () => seedMemForFlow(mem, blockInfos, undefined, bareRef);
2023
2027
  resolved = blockInfos.map((bi, idx) => ({
2024
2028
  block: bi.block,
2025
2029
  exportName: bi.name,
2030
+ seedMem: idx === 0 ? seedMem : undefined,
2026
2031
  expectedFailureReason:
2027
2032
  flow.expectedFailureReason && idx === blockInfos.length - 1
2028
2033
  ? flow.expectedFailureReason
@@ -2291,6 +2296,7 @@ main().catch((err) => {
2291
2296
  * `WAYGRAPH_SLOWMO=<ms>` pass straight through from this process's own env.
2292
2297
  */
2293
2298
  async function runChain(projectDir, spec) {
2299
+ const expanded = await expandSpecFlowFiles(projectDir, spec);
2294
2300
  if (process.env.WAYGRAPH_VIDEO && !process.env.WAYGRAPH_BASE_URL) {
2295
2301
  const resolved = resolveBaseUrl(projectDir);
2296
2302
  if (resolved)
@@ -2301,7 +2307,7 @@ async function runChain(projectDir, spec) {
2301
2307
  writeFileSync(scriptPath, CHAIN_RUNNER_SCRIPT);
2302
2308
  try {
2303
2309
  const code = await new Promise((res, rej) => {
2304
- const child = spawn(process.execPath, ["--import", tsxEsm, scriptPath, projectDir, spec], {
2310
+ const child = spawn(process.execPath, ["--import", tsxEsm, scriptPath, projectDir, expanded], {
2305
2311
  stdio: "inherit",
2306
2312
  env: process.env,
2307
2313
  });
@@ -2969,6 +2975,83 @@ function resolveProjectDir(flags, fallbackIndex = 0) {
2969
2975
  function resolveSpec(flags) {
2970
2976
  return flags.blocks ?? flags.positionals[0];
2971
2977
  }
2978
+ /** `shop.flow.ts` / `src/flows/cart-bulk.flow.ts` / absolute path — not a Checkpoint or export id. */
2979
+ function looksLikeFlowFileRef(ref) {
2980
+ if (!ref || ref.includes("then") || ref.includes("("))
2981
+ return false;
2982
+ return /\.flow\.ts$/i.test(ref) || (/\.ts$/i.test(ref) && (ref.includes("/") || ref.includes("\\")));
2983
+ }
2984
+ function flowExportNameFromBasename(filePath) {
2985
+ const base = basename(filePath).replace(/\.flow\.ts$/i, "").replace(/\.ts$/i, "");
2986
+ const camel = base.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
2987
+ return /Flow$/i.test(camel) ? camel : `${camel}Flow`;
2988
+ }
2989
+ /**
2990
+ * Map a `.flow.ts` path (relative to project or absolute) to its Flow export
2991
+ * name so the chain runner can `findFlow(exportName)`. Prefers
2992
+ * `shop.flow.ts` → `shopFlow`. Reads source only (no import) so the outer CLI
2993
+ * does not dual-load `@playwright/test` against the project tree.
2994
+ */
2995
+ async function resolveFlowFileToExport(projectDir, fileArg) {
2996
+ let abs = resolve(projectDir, fileArg);
2997
+ if (!existsSync(abs)) {
2998
+ const want = basename(fileArg);
2999
+ const hits = walkDir(projectDir, new RegExp(`${want.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`));
3000
+ if (hits.length === 1)
3001
+ abs = hits[0];
3002
+ else if (hits.length === 0) {
3003
+ throw new Error(`waygraph: no flow file "${fileArg}" under ${projectDir}`);
3004
+ }
3005
+ else {
3006
+ throw new Error(`waygraph: "${fileArg}" matches ${hits.length} files - use a path from \`waygraph list\``);
3007
+ }
3008
+ }
3009
+ const expected = flowExportNameFromBasename(abs);
3010
+ const src = readFileSync(abs, "utf-8");
3011
+ if (new RegExp(`\\bexport\\s+(?:const|let|var|async\\s+function|function)\\s+${expected}\\b`).test(src)) {
3012
+ return expected;
3013
+ }
3014
+ if (new RegExp(`\\bexport\\s*\\{[^}]*\\b${expected}\\b`).test(src)) {
3015
+ return expected;
3016
+ }
3017
+ const flowNames = [
3018
+ ...src.matchAll(/\bexport\s+(?:const|let|var|async\s+function|function)\s+(\w*Flow)\b/g),
3019
+ ].map((m) => m[1]);
3020
+ const unique = [...new Set(flowNames)];
3021
+ if (unique.length === 1)
3022
+ return unique[0];
3023
+ if (unique.includes(expected))
3024
+ return expected;
3025
+ if (unique.length === 0) {
3026
+ throw new Error(`waygraph: ${relative(projectDir, abs)} has no exported *Flow (expected ${expected})`);
3027
+ }
3028
+ throw new Error(`waygraph: ${relative(projectDir, abs)} exports ${unique.join(", ")} - pass the export name or rename to ${expected}`);
3029
+ }
3030
+ /**
3031
+ * Expand `.flow.ts` path segments in a run/demo spec to export names.
3032
+ * `src/flows/shop.flow.ts` → `shopFlow`; leaves `loginFlow then nav-cart` alone.
3033
+ */
3034
+ async function expandSpecFlowFiles(projectDir, spec) {
3035
+ const parts = spec.split(/\bthen\b/).map((s) => s.trim()).filter((s) => s.length > 0);
3036
+ const out = [];
3037
+ for (const part of parts) {
3038
+ const m = /^([^\s(]+)(\s*\([\s\S]*\))?$/.exec(part);
3039
+ if (!m) {
3040
+ out.push(part);
3041
+ continue;
3042
+ }
3043
+ const ref = m[1];
3044
+ const payload = m[2] ?? "";
3045
+ if (looksLikeFlowFileRef(ref)) {
3046
+ const exportName = await resolveFlowFileToExport(projectDir, ref);
3047
+ out.push(exportName + payload);
3048
+ }
3049
+ else {
3050
+ out.push(part);
3051
+ }
3052
+ }
3053
+ return out.join(" then ");
3054
+ }
2972
3055
  const args = process.argv.slice(2);
2973
3056
  const command = args[0];
2974
3057
  function usage() {
@@ -2979,12 +3062,12 @@ Primary (less is more):
2979
3062
  --cli Terminal menu instead of browser panel
2980
3063
  --blocks <From> <To> Graph path-find From->To, then run
2981
3064
  --data '{...}' Mem seed (same as demo/run)
2982
- waygraph demo [--blocks <flow|spec>] Watch with step overlay (QA path)
3065
+ waygraph demo [--blocks <flow|file|spec>] Watch with step overlay (QA path)
2983
3066
  --data '{...}' Mem seed JSON (or inline flow({...}))
2984
3067
  --auto-next Auto-advance steps (alias: --autoplay)
2985
3068
  --auto-play-video QA: --auto-next + --video (+ step)
2986
3069
  --title / --base-url
2987
- waygraph run [--blocks <flow|spec>] Execute (no overlay unless --step)
3070
+ waygraph run [--blocks <flow|file|spec>] Execute (no overlay unless --step)
2988
3071
  --data '{...}'
2989
3072
  --non-headless Show browser
2990
3073
  --video [dir] Record .webm
@@ -2995,18 +3078,20 @@ Also:
2995
3078
  try auto --headed Browser panel instead of CLI
2996
3079
 
2997
3080
  Examples:
3081
+ waygraph list # file → export map
3082
+ waygraph run src/flows/shop.flow.ts --data '{...}'
3083
+ waygraph run --blocks shopFlow --non-headless --video
3084
+ waygraph demo --blocks src/flows/cart-bulk.flow.ts --auto-next
2998
3085
  waygraph try auto:cli
2999
3086
  waygraph auto --cli --data '{"saucedemo.credentials":{...}}'
3000
3087
  waygraph auto --blocks LoginPage OrderComplete
3001
- waygraph demo --blocks shopFlow --auto-next
3002
- waygraph demo --blocks cartBulkFlow --data '{"saucedemo.credentials":{...}}' --auto-play-video
3003
3088
  waygraph run --blocks "loginFlow then add-all-to-cart" --data '{...}' --video
3004
- waygraph run --blocks shopFlow --non-headless --video
3005
3089
 
3006
3090
  Aliases (compat): \`chain <spec>\` -> run --blocks; \`chain auto A B\` -> auto --blocks A B;
3007
3091
  --autoplay -> --auto-next. Prefer the primary verbs above.
3008
3092
 
3009
3093
  Project path optional (defaults to cwd). Flags beat WAYGRAPH_* env.
3094
+ \`--blocks\` / positional: Flow export (shopFlow), .flow.ts path, or "a then b" chain.
3010
3095
  `);
3011
3096
  process.exit(0);
3012
3097
  }
@@ -3053,7 +3138,10 @@ async function main() {
3053
3138
  applyRunFlags(flags);
3054
3139
  const spec = resolveSpec(flags);
3055
3140
  if (!spec) {
3056
- console.error('waygraph run: missing flow/spec — e.g. waygraph run --blocks shopFlow\n' +
3141
+ console.error("waygraph run: missing flow/spec — e.g.\n" +
3142
+ " waygraph run src/flows/shop.flow.ts\n" +
3143
+ " waygraph run --blocks shopFlow\n" +
3144
+ " waygraph list # file → export map\n" +
3057
3145
  " Flags: --blocks --data --non-headless --video [dir] --step/--no-step");
3058
3146
  process.exit(1);
3059
3147
  }
@@ -3121,7 +3209,9 @@ async function main() {
3121
3209
  applyRunFlags(flags, { allowAutoPlayVideo: true });
3122
3210
  const spec = resolveSpec(flags);
3123
3211
  if (!spec) {
3124
- console.error('waygraph demo: missing flow/spec — e.g. waygraph demo --blocks shopFlow\n' +
3212
+ console.error("waygraph demo: missing flow/spec — e.g.\n" +
3213
+ " waygraph demo src/flows/shop.flow.ts\n" +
3214
+ " waygraph demo --blocks shopFlow\n" +
3125
3215
  " Flags: --blocks --data --auto-next --auto-play-video --title --base-url --video");
3126
3216
  process.exit(1);
3127
3217
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waygraph",
3
- "version": "0.10.3",
3
+ "version": "0.10.4",
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.2"
26
+ "waygraph": "^0.10.4"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@playwright/test": "^1.55.0",