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 +8 -4
- package/dist/cli.js +98 -8
- package/package.json +1 -1
- package/templates/quickstart/package.json +1 -1
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.
|
|
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) — `
|
|
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.
|
|
265
|
+
**Friendly demo / run / auto (0.10.4 — flows are files):**
|
|
266
266
|
|
|
267
267
|
| Want | Command |
|
|
268
268
|
|------|---------|
|
|
269
|
-
|
|
|
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,
|
|
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>]
|
|
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>]
|
|
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(
|
|
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(
|
|
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