waygraph 0.15.18 → 0.15.20
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 +68 -18
- package/dist/auto-session.d.ts +10 -1
- package/dist/auto-session.js +27 -7
- package/dist/cli.js +98 -31
- package/dist/engine.d.ts +20 -0
- package/dist/engine.js +18 -0
- package/dist/highlight-shorthand.d.ts +24 -0
- package/dist/highlight-shorthand.js +30 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/mem-stub.d.ts +41 -0
- package/dist/mem-stub.js +36 -0
- package/dist/practices-check.d.ts +1 -1
- package/dist/practices-check.js +36 -0
- package/examples/saucedemo/.waygraph-auto/11c497cd.log +44 -0
- package/examples/saucedemo/.waygraph-auto/29d11a49.log +44 -0
- package/examples/saucedemo/.waygraph-auto/2c9a4003.log +44 -0
- package/examples/saucedemo/.waygraph-auto/32ef32a8.log +44 -0
- package/examples/saucedemo/.waygraph-auto/36ba01f7.log +44 -0
- package/examples/saucedemo/.waygraph-auto/4c6b9e15.log +0 -0
- package/examples/saucedemo/.waygraph-auto/537af83d.log +44 -0
- package/examples/saucedemo/.waygraph-auto/5adfacd5.log +44 -0
- package/examples/saucedemo/.waygraph-auto/76c7d418.log +44 -0
- package/examples/saucedemo/.waygraph-auto/78197960.log +44 -0
- package/examples/saucedemo/.waygraph-auto/7c2acbc2.log +44 -0
- package/examples/saucedemo/.waygraph-auto/7eded669.log +0 -0
- package/examples/saucedemo/.waygraph-auto/844b63ff.log +44 -0
- package/examples/saucedemo/.waygraph-auto/88a4200b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b0eed995.log +44 -0
- package/examples/saucedemo/.waygraph-auto/bb69dc1a.log +44 -0
- package/examples/saucedemo/.waygraph-auto/bc4ae56f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/c17253b0.log +44 -0
- package/examples/saucedemo/.waygraph-auto/c31a9465.log +0 -0
- package/examples/saucedemo/.waygraph-auto/c5ac2393.log +44 -0
- package/examples/saucedemo/.waygraph-auto/c701e3c2.log +0 -0
- package/examples/saucedemo/.waygraph-auto/d207a000.log +44 -0
- package/examples/saucedemo/.waygraph-auto/fa3c5655.log +44 -0
- package/examples/saucedemo/package-lock.json +20 -532
- package/examples/saucedemo/package.json +0 -1
- package/examples/saucedemo/src/flows/mem-stub-demo.flow.ts +23 -0
- package/package.json +1 -1
- package/templates/scaffold/src/map/(external)/mailpit/_methods/open-message.method.block.ts +1 -0
- package/templates/skills/waygraph-convention.skill.md +18 -4
- package/examples/saucedemo/.waygraph-auto/4adfbe91.json +0 -8
- /package/examples/saucedemo/.waygraph-auto/{4adfbe91.log → 32e2c26f.log} +0 -0
package/README.md
CHANGED
|
@@ -249,6 +249,40 @@ A Block's `requires: MemKey[]` gets checked by `preflight()` (built into `runGra
|
|
|
249
249
|
before a tab even opens - a missing credential fails in milliseconds, not after several
|
|
250
250
|
real browser actions already ran.
|
|
251
251
|
|
|
252
|
+
### memStub - fake values for `requires`, opt-in per Flow
|
|
253
|
+
|
|
254
|
+
Hand-supplying `--data '{...}'` every run gets old fast for a `requires` key that's
|
|
255
|
+
genuinely arbitrary (a username, an email, a name) rather than something the run actually
|
|
256
|
+
depends on. `registerMemStub(key, fake)` registers a generator for one `MemKey` (bring your
|
|
257
|
+
own - Faker.js or anything else; waygraph takes no dependency on it); `withMemStub(flow)`
|
|
258
|
+
opts that Flow into filling any `requires` key with no `--data` coverage from that registry
|
|
259
|
+
instead of failing preflight. A key with no registration still fails exactly as before -
|
|
260
|
+
this narrows the case that needs real input, it doesn't remove the guard:
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
import { registerMemStub, withMemStub } from "waygraph";
|
|
264
|
+
import { faker } from "@faker-js/faker";
|
|
265
|
+
|
|
266
|
+
registerMemStub(LoginCreds.key, () => ({
|
|
267
|
+
username: faker.internet.userName(),
|
|
268
|
+
password: faker.internet.password(),
|
|
269
|
+
}));
|
|
270
|
+
|
|
271
|
+
export const demoLoginFlow = withMemStub(loginFlow);
|
|
272
|
+
```
|
|
273
|
+
```bash
|
|
274
|
+
waygraph run demoLoginFlow # zero --data - fills from the registry
|
|
275
|
+
waygraph run loginFlow --mem-stub # same effect without a Flow-level wrapper -
|
|
276
|
+
# --mem-stub / WAYGRAPH_MEM_STUB=1 force it
|
|
277
|
+
# for one CLI call, opt-in or not
|
|
278
|
+
```
|
|
279
|
+
`--data` always wins for a key it actually covers - memStub only fills what's left over.
|
|
280
|
+
Registered by `MemKey` *identity*, not by name (matching `MemPage`'s own store): two keys
|
|
281
|
+
sharing a debug name never cross-contaminate. For a bare library `flow.run(mem)` call
|
|
282
|
+
outside the CLI, call `seedMemStub(mem, flow)` yourself before `run()` when
|
|
283
|
+
`flow.memStub` is true - `run()` never reads Flow flags itself, same as
|
|
284
|
+
`resetSession`/`title`/every other `with*` wrapper.
|
|
285
|
+
|
|
252
286
|
### Engine config and pluggable browsers
|
|
253
287
|
|
|
254
288
|
`new Engine({ headless, browserName, slowMo })` configures a browser the Engine launches
|
|
@@ -298,10 +332,26 @@ code, an autonomous agent authoring Blocks) - it warns, never fails the process.
|
|
|
298
332
|
|
|
299
333
|
**`waygraph typecheck [project]`** runs `tsc --noEmit` then the same **bad-practice** scan
|
|
300
334
|
(wildcard `Checkpoint<string>`, assert blocks missing type args, multi-`.fill()` Methods,
|
|
301
|
-
fill+click in one Method
|
|
302
|
-
skip the scan. Opt out per
|
|
303
|
-
`// waygraph-ignore: multi-input, combined-action
|
|
304
|
-
`waygraph --skill-convention`.
|
|
335
|
+
fill+click in one Method, empty `verify` on a transition Block or `defineAssertBlock`).
|
|
336
|
+
Practices warn only unless `tsc` fails; use `--no-practices` to skip the scan. Opt out per
|
|
337
|
+
file: `// waygraph-ignore-practices` or `// waygraph-ignore: multi-input, combined-action,
|
|
338
|
+
empty-verify`. Print full agent guidance: `waygraph --skill-convention`.
|
|
339
|
+
|
|
340
|
+
**Empty verify is the one that matters most for codegen'd Blocks.** A framework can enforce
|
|
341
|
+
that a check *exists* and stays structurally honest (typed, wired, right file) - it cannot
|
|
342
|
+
author the semantic content of that check, since only someone who knows what "success" means
|
|
343
|
+
for this specific app can write it. That's a correct place to draw the line, but it shifts
|
|
344
|
+
all the risk onto whether anything catches a Block left with a placeholder `verify: []` by
|
|
345
|
+
the time it ships - which would otherwise pass every static check, the orphan scan, and
|
|
346
|
+
`tsc`, and run green in CI forever, because an empty array isn't a wrong check, it's the
|
|
347
|
+
*absence* of one. `waygraph check`/`typecheck` now flag exactly that: a `defineMethodBlock`/
|
|
348
|
+
`defineEffectBlock` whose `In`/`Out` Checkpoints actually differ (a real transition, not a
|
|
349
|
+
self-loop like `fill-username`) with no `verify` key or a literal `verify: []`, and the same
|
|
350
|
+
for `defineAssertBlock` (whose entire job is checking). Silence it deliberately, the same as
|
|
351
|
+
any other practice warning, with `// waygraph-ignore: empty-verify` - e.g. when a downstream
|
|
352
|
+
`defineAssertBlock` right after it in the `.flow.ts` is what actually confirms the transition
|
|
353
|
+
(see `templates/scaffold`'s own `open-message.method.block.ts`, confirmed by
|
|
354
|
+
`AssertEmailContentBlock` right after it, not by its own `verify`).
|
|
305
355
|
|
|
306
356
|
### Demo narration
|
|
307
357
|
|
|
@@ -998,25 +1048,22 @@ really a Nav/Page/Assert/Method from this package's own factories.
|
|
|
998
1048
|
|------|---------|
|
|
999
1049
|
| Watch Sauce Demo step-through (temp dir only) | `npx waygraph try demo` |
|
|
1000
1050
|
| Interactive explore (Add/Remove/Open details from live page) | `cd examples/saucedemo && npm i && npx waygraph auto` |
|
|
1001
|
-
| Scaffold a new **offline** project (green `npm test` on local fixture `:4177`) | `npx
|
|
1051
|
+
| Scaffold a new **offline** project (green `npm test` on local fixture `:4177`) | `npx waygraph init my-app` |
|
|
1002
1052
|
| Install coding-agent defs for diving an app into Blocks | `npx waygraph agent-dive --loop claude` (also `opencode` / `cursor` / `vscode`) |
|
|
1003
1053
|
| Print a packaged agent skill to stdout | `npx waygraph --skill` (list) / `--skill-pilot` / `--skill-pilot-blind` / `--skill-convention` |
|
|
1004
1054
|
| Add waygraph to an existing repo | `npm install waygraph @playwright/test` |
|
|
1005
1055
|
|
|
1006
|
-
**Scaffold is not hidden inside waygraph alone** - the offline starter also ships as
|
|
1007
|
-
[`create-waygraph`](https://github.com/deviate-dv8/create-waygraph) on npm (same template
|
|
1008
|
-
as `waygraph init` since 0.7.5). Use whichever entry you already have:
|
|
1009
|
-
`npx create-waygraph my-app` when reading the GitHub Pages docs, or `npx waygraph init my-app`
|
|
1010
|
-
when the CLI is already installed. After either:
|
|
1011
|
-
|
|
1012
1056
|
```bash
|
|
1057
|
+
npx waygraph init my-app
|
|
1013
1058
|
cd my-app && npm install && npx playwright install chromium && npm test
|
|
1014
1059
|
```
|
|
1015
1060
|
|
|
1016
1061
|
`try demo` is different: live saucedemo.com, step-through **Sign In -> Shop & Checkout ->
|
|
1017
1062
|
blocked Viewer login**, then headless tests. Permanent full example:
|
|
1018
|
-
[`examples/saucedemo`](./examples/saucedemo). Offline empty scaffold: `init
|
|
1019
|
-
|
|
1063
|
+
[`examples/saucedemo`](./examples/saucedemo). Offline empty scaffold: `init`.
|
|
1064
|
+
|
|
1065
|
+
**`create-waygraph` (npm) is deprecated** - it was the identical `init` template shipped as
|
|
1066
|
+
a second package for no real benefit. Use `npx waygraph init my-app` instead.
|
|
1020
1067
|
|
|
1021
1068
|
```bash
|
|
1022
1069
|
# From this checkout, after npm run build:
|
|
@@ -1090,8 +1137,8 @@ suite - it forwards to the local `node_modules/.bin/playwright` (falling back to
|
|
|
1090
1137
|
`npx playwright` if the project hasn't installed it locally yet), so `waygraph test` behaves
|
|
1091
1138
|
exactly like `playwright test`, and anything after it (`--grep`, a spec path, `--headed`, …)
|
|
1092
1139
|
passes straight through. `waygraph test ui` (or `waygraph test --ui`) launches Playwright's
|
|
1093
|
-
interactive UI Mode the same way. Scaffolded projects (`waygraph init
|
|
1094
|
-
|
|
1140
|
+
interactive UI Mode the same way. Scaffolded projects (`waygraph init`) wire this up as
|
|
1141
|
+
`npm test` / `npm run test:ui`, and ship `trace: "retain-on-failure"` plus
|
|
1095
1142
|
the `html` reporter in `playwright.config.ts`, so a failing `waygraph test` already has a
|
|
1096
1143
|
trace waiting - `waygraph test report` opens the HTML report, and
|
|
1097
1144
|
`waygraph test show-trace <trace.zip>` opens one trace directly (both forward to Playwright's
|
|
@@ -1122,7 +1169,9 @@ Flows are files (0.10.5+):
|
|
|
1122
1169
|
| Attach to existing session (graph only) | `waygraph pilot attach <sessionId>` |
|
|
1123
1170
|
| Path-find | `waygraph auto --blocks LoginPage OrderComplete` |
|
|
1124
1171
|
| Run `--cli` as a background session (legacy) | `waygraph auto --cli --detach` -> `{sessionId, socketPath}` |
|
|
1125
|
-
| Drive a detached session (no TTY) | `waygraph browser send <sessionId> "<pick>"` -> JSON state |
|
|
1172
|
+
| Drive a detached session (no TTY) | `waygraph browser send <sessionId> "<pick>"` -> JSON state (`<pick>` is a menu index or a Block name) |
|
|
1173
|
+
| Paint highlight rings (shorthand) | `waygraph browser highlight <sessionId> "#x\|Label\|tone; .y\|Label2"` |
|
|
1174
|
+
| Paint highlight rings (full JSON) | `waygraph browser highlight <sessionId> '{"rings":[{"selector":"#x","label":"X"}]}'` |
|
|
1126
1175
|
| Read a detached session's state | `waygraph browser status <sessionId>` (no side effects) |
|
|
1127
1176
|
| Reattach a terminal to a detached session | `waygraph browser attach <sessionId>` |
|
|
1128
1177
|
| Read the live page (aria, small/default) | `waygraph auto dom <sessionId>` |
|
|
@@ -1161,7 +1210,8 @@ Long FR/AC checklists used to bury highlight rings. **Smart defaults are on:**
|
|
|
1161
1210
|
|
|
1162
1211
|
Per-knob env also: `WAYGRAPH_TODO_COMPACT`, `WAYGRAPH_TODO_CAP` (default 5),
|
|
1163
1212
|
`WAYGRAPH_TODO_EXPAND_CAP` (default 14). Author API: `ctx.todoDockUi({ ... })`.
|
|
1164
|
-
|
|
1213
|
+
`browser highlight` (same as `auto`/`pilot highlight` - identical command, see "auto vs
|
|
1214
|
+
browser vs pilot" above) accepts the same knobs as `todoUi` on the JSON body.
|
|
1165
1215
|
|
|
1166
1216
|
Run `waygraph --help` (or any subcommand with no args) for the full flag reference kept
|
|
1167
1217
|
in `src/cli.ts`'s own `usage()` - that's the source of truth for flags, this table is the
|
|
@@ -1321,7 +1371,7 @@ src/
|
|
|
1321
1371
|
index.ts public barrel
|
|
1322
1372
|
examples/saucedemo/ live Sauce Demo (Page + methods/ + Sel)
|
|
1323
1373
|
templates/quickstart/ try demo / init template (mirrors sauce)
|
|
1324
|
-
templates/scaffold/ offline
|
|
1374
|
+
templates/scaffold/ offline waygraph init scaffold template
|
|
1325
1375
|
templates/agents/ waygraph agent-dive coding-agent definitions
|
|
1326
1376
|
docs/ GitHub Pages static HTML + docs/proposals/ RFCs
|
|
1327
1377
|
openspec/ spec-driven planning: specs/ (current), changes/ (in-flight),
|
package/dist/auto-session.d.ts
CHANGED
|
@@ -31,8 +31,17 @@ export type ParsedPick = PickResult | {
|
|
|
31
31
|
/**
|
|
32
32
|
* Same number/`q`/range rules `cliPick` already applies, but returning a
|
|
33
33
|
* result instead of looping to re-prompt - one request gets one answer.
|
|
34
|
+
* Also accepts a Block name (exact, case-sensitive) as a shorthand for its
|
|
35
|
+
* menu index - `send <id> "submit-login"` instead of hunting the number.
|
|
36
|
+
* Ambiguous (the same Block name appearing more than once in one menu)
|
|
37
|
+
* refuses rather than guessing which occurrence was meant - a pick runs a
|
|
38
|
+
* real browser action, not something to silently get wrong.
|
|
34
39
|
*/
|
|
35
|
-
export declare function parsePick(raw: string,
|
|
40
|
+
export declare function parsePick(raw: string, menu: {
|
|
41
|
+
readonly flat: readonly {
|
|
42
|
+
readonly block: string;
|
|
43
|
+
}[];
|
|
44
|
+
}): ParsedPick;
|
|
36
45
|
export interface DomSnapshot {
|
|
37
46
|
mode: "aria" | "full";
|
|
38
47
|
selector?: string;
|
package/dist/auto-session.js
CHANGED
|
@@ -37,18 +37,38 @@ export function buildSessionSnapshot(menu, library, here, lastRunNote) {
|
|
|
37
37
|
/**
|
|
38
38
|
* Same number/`q`/range rules `cliPick` already applies, but returning a
|
|
39
39
|
* result instead of looping to re-prompt - one request gets one answer.
|
|
40
|
+
* Also accepts a Block name (exact, case-sensitive) as a shorthand for its
|
|
41
|
+
* menu index - `send <id> "submit-login"` instead of hunting the number.
|
|
42
|
+
* Ambiguous (the same Block name appearing more than once in one menu)
|
|
43
|
+
* refuses rather than guessing which occurrence was meant - a pick runs a
|
|
44
|
+
* real browser action, not something to silently get wrong.
|
|
40
45
|
*/
|
|
41
|
-
export function parsePick(raw,
|
|
42
|
-
const ans = raw.trim()
|
|
43
|
-
|
|
46
|
+
export function parsePick(raw, menu) {
|
|
47
|
+
const ans = raw.trim();
|
|
48
|
+
const lower = ans.toLowerCase();
|
|
49
|
+
if (lower === "q" || lower === "quit")
|
|
44
50
|
return { type: "quit" };
|
|
45
|
-
const n = Number(
|
|
46
|
-
if (Number.isInteger(n) && n >= 1 && n <=
|
|
51
|
+
const n = Number(lower);
|
|
52
|
+
if (Number.isInteger(n) && n >= 1 && n <= menu.flat.length) {
|
|
47
53
|
return { type: "pick", index: n - 1 };
|
|
48
54
|
}
|
|
55
|
+
const matches = [];
|
|
56
|
+
menu.flat.forEach((edge, i) => {
|
|
57
|
+
if (edge.block === ans)
|
|
58
|
+
matches.push(i);
|
|
59
|
+
});
|
|
60
|
+
if (matches.length === 1) {
|
|
61
|
+
return { type: "pick", index: matches[0] };
|
|
62
|
+
}
|
|
63
|
+
if (matches.length > 1) {
|
|
64
|
+
return {
|
|
65
|
+
type: "invalid",
|
|
66
|
+
reason: `"${raw}" matches ${matches.length} menu entries - use the numeric index instead of the Block name`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
49
69
|
return {
|
|
50
70
|
type: "invalid",
|
|
51
|
-
reason: `"${raw}" is not a valid pick - enter a number from 1 to ${
|
|
71
|
+
reason: `"${raw}" is not a valid pick - enter a number from 1 to ${menu.flat.length}, a Block name, or "q" to quit`,
|
|
52
72
|
};
|
|
53
73
|
}
|
|
54
74
|
const FULL_MODE_DEFAULT_DEPTH = 12;
|
|
@@ -499,7 +519,7 @@ export class AutoSession {
|
|
|
499
519
|
/** Applies exactly one pick: runs at most one Block, returns the resulting state. */
|
|
500
520
|
async applyPick(raw) {
|
|
501
521
|
const menu = await this.currentMenu();
|
|
502
|
-
const pick = parsePick(raw, menu
|
|
522
|
+
const pick = parsePick(raw, menu);
|
|
503
523
|
if (pick.type === "invalid")
|
|
504
524
|
return { ok: false, error: pick.reason };
|
|
505
525
|
if (pick.type === "quit") {
|
package/dist/cli.js
CHANGED
|
@@ -31,6 +31,7 @@ import { runAgentDive, printSkillIndex, readSkillMarkdown, SKILL_FLAG_MAP, } fro
|
|
|
31
31
|
import { runTraverse } from "./traverse-run.js";
|
|
32
32
|
import { parseMinEdgeCoverage } from "./traverse-coverage.js";
|
|
33
33
|
import { isFileSelectToken, parseBlocksSelect, } from "./blocks-select.js";
|
|
34
|
+
import { parseHighlightShorthand } from "./highlight-shorthand.js";
|
|
34
35
|
// ---------------------------------------------------------------------------
|
|
35
36
|
// Filesystem
|
|
36
37
|
// ---------------------------------------------------------------------------
|
|
@@ -242,6 +243,7 @@ import {
|
|
|
242
243
|
resolveDeviceState,
|
|
243
244
|
resolveTodoDockUi,
|
|
244
245
|
WAYGRAPH_RING_CSS,
|
|
246
|
+
getMemStub,
|
|
245
247
|
} from "waygraph";
|
|
246
248
|
|
|
247
249
|
function walkDir(dir, pattern) {
|
|
@@ -341,7 +343,7 @@ function parseChainSpec(spec) {
|
|
|
341
343
|
});
|
|
342
344
|
}
|
|
343
345
|
|
|
344
|
-
function seedMemFromRequires(mem, requires, json, label) {
|
|
346
|
+
function seedMemFromRequires(mem, requires, json, label, stubActive) {
|
|
345
347
|
if (requires.length === 0) {
|
|
346
348
|
if (json !== undefined) {
|
|
347
349
|
throw new Error(
|
|
@@ -353,21 +355,34 @@ function seedMemFromRequires(mem, requires, json, label) {
|
|
|
353
355
|
if (json === undefined) {
|
|
354
356
|
// --data / WAYGRAPH_DATA supplies the same payload shape as blockName({...}).
|
|
355
357
|
const fromEnv = process.env.WAYGRAPH_DATA;
|
|
356
|
-
if (fromEnv && fromEnv.trim())
|
|
357
|
-
|
|
358
|
-
|
|
358
|
+
if (fromEnv && fromEnv.trim()) json = fromEnv;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let parsed;
|
|
362
|
+
let haveParsed = false;
|
|
363
|
+
if (json !== undefined) {
|
|
364
|
+
try {
|
|
365
|
+
parsed = JSON.parse(json);
|
|
366
|
+
haveParsed = true;
|
|
367
|
+
} catch (err) {
|
|
368
|
+
throw new Error("waygraph chain: \\"" + label + "\\" payload is not valid JSON - " + String(err));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (!haveParsed) {
|
|
373
|
+
// No payload at all - only a hard failure when memStub isn't active for
|
|
374
|
+
// this flow; when it is, fall through with an empty object so the
|
|
375
|
+
// per-key loop below gets a chance to fill each one from the registry.
|
|
376
|
+
if (!stubActive) {
|
|
359
377
|
throw new Error(
|
|
360
378
|
"waygraph chain: \\"" + label + "\\" requires " + requires.map((k) => k.name).join(", ") +
|
|
361
|
-
" - give a JSON payload (inline blockName({...}) or --data '{...}')"
|
|
379
|
+
" - give a JSON payload (inline blockName({...}) or --data '{...}'), or enable --mem-stub" +
|
|
380
|
+
" with registerMemStub for these keys",
|
|
362
381
|
);
|
|
363
382
|
}
|
|
383
|
+
parsed = {};
|
|
364
384
|
}
|
|
365
|
-
|
|
366
|
-
try {
|
|
367
|
-
parsed = JSON.parse(json);
|
|
368
|
-
} catch (err) {
|
|
369
|
-
throw new Error("waygraph chain: \\"" + label + "\\" payload is not valid JSON - " + String(err));
|
|
370
|
-
}
|
|
385
|
+
|
|
371
386
|
// Keyed-by-name when every require name is a top-level key (preferred for
|
|
372
387
|
// --data and for {"saucedemo.credentials":{...}} even with one require).
|
|
373
388
|
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
@@ -377,26 +392,42 @@ function seedMemFromRequires(mem, requires, json, label) {
|
|
|
377
392
|
return;
|
|
378
393
|
}
|
|
379
394
|
}
|
|
380
|
-
|
|
395
|
+
// Single-key shorthand: the whole payload IS that one key's value - only
|
|
396
|
+
// when a real payload was actually given (the stub-only "{}" fallback
|
|
397
|
+
// above must fall through to per-key stub resolution instead).
|
|
398
|
+
if (haveParsed && requires.length === 1) {
|
|
381
399
|
mem.set(requires[0], parsed);
|
|
382
400
|
return;
|
|
383
401
|
}
|
|
384
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
402
|
+
if (haveParsed && (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))) {
|
|
385
403
|
throw new Error(
|
|
386
404
|
"waygraph chain: \\"" + label + "\\" requires " + requires.length + " keys (" +
|
|
387
405
|
requires.map((k) => k.name).join(", ") + ") - payload must be an object keyed by each key's name",
|
|
388
406
|
);
|
|
389
407
|
}
|
|
408
|
+
const stillMissing = [];
|
|
390
409
|
for (const k of requires) {
|
|
391
|
-
if (
|
|
392
|
-
|
|
410
|
+
if (k.name in parsed) {
|
|
411
|
+
mem.set(k, parsed[k.name]);
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
const fake = stubActive ? getMemStub(k) : undefined;
|
|
415
|
+
if (fake) {
|
|
416
|
+
mem.set(k, fake());
|
|
417
|
+
continue;
|
|
393
418
|
}
|
|
394
|
-
|
|
419
|
+
stillMissing.push(k);
|
|
420
|
+
}
|
|
421
|
+
if (stillMissing.length > 0) {
|
|
422
|
+
throw new Error(
|
|
423
|
+
"waygraph chain: \\"" + label + "\\" payload is missing required key(s): " +
|
|
424
|
+
stillMissing.map((k) => "\\"" + k.name + "\\"").join(", "),
|
|
425
|
+
);
|
|
395
426
|
}
|
|
396
427
|
}
|
|
397
428
|
|
|
398
|
-
function seedMemForBlock(mem, resolved, json) {
|
|
399
|
-
seedMemFromRequires(mem, resolved.block.requires ?? [], json, resolved.exportName);
|
|
429
|
+
function seedMemForBlock(mem, resolved, json, stubActive) {
|
|
430
|
+
seedMemFromRequires(mem, resolved.block.requires ?? [], json, resolved.exportName, stubActive);
|
|
400
431
|
}
|
|
401
432
|
|
|
402
433
|
/**
|
|
@@ -406,14 +437,14 @@ function seedMemForBlock(mem, resolved, json) {
|
|
|
406
437
|
* name) and seeds them all from one JSON payload, same keyed-by-name shape
|
|
407
438
|
* as a multi-key Block payload already uses.
|
|
408
439
|
*/
|
|
409
|
-
function seedMemForFlow(mem, flowBlocks, json, label) {
|
|
440
|
+
function seedMemForFlow(mem, flowBlocks, json, label, stubActive) {
|
|
410
441
|
const seen = new Map();
|
|
411
442
|
for (const bi of flowBlocks) {
|
|
412
443
|
for (const k of bi.block.requires ?? []) {
|
|
413
444
|
if (!seen.has(k.name)) seen.set(k.name, k);
|
|
414
445
|
}
|
|
415
446
|
}
|
|
416
|
-
seedMemFromRequires(mem, Array.from(seen.values()), json, label);
|
|
447
|
+
seedMemFromRequires(mem, Array.from(seen.values()), json, label, stubActive);
|
|
417
448
|
}
|
|
418
449
|
|
|
419
450
|
// ---------------------------------------------------------------------------
|
|
@@ -5898,7 +5929,8 @@ async function main() {
|
|
|
5898
5929
|
// Same --data / WAYGRAPH_DATA seeding as the multi-segment path.
|
|
5899
5930
|
// Without this, \`waygraph run shop.flow.ts --data '{...}'\` (and bare
|
|
5900
5931
|
// export names) hit preflight with an empty MemPage.
|
|
5901
|
-
const
|
|
5932
|
+
const stubActive = flow.memStub === true || process.env.WAYGRAPH_MEM_STUB === "1";
|
|
5933
|
+
const seedMem = () => seedMemForFlow(mem, blockInfos, undefined, bareRef, stubActive);
|
|
5902
5934
|
resolved = blockInfos.map((bi, idx) => ({
|
|
5903
5935
|
block: bi.block,
|
|
5904
5936
|
exportName: bi.name,
|
|
@@ -5969,6 +6001,7 @@ async function main() {
|
|
|
5969
6001
|
if (flow && typeof flow.blocks === "function") {
|
|
5970
6002
|
flows.push(flow);
|
|
5971
6003
|
episodeCounter += 1;
|
|
6004
|
+
const stubActive = flow.memStub === true || process.env.WAYGRAPH_MEM_STUB === "1";
|
|
5972
6005
|
flowMeta.push({
|
|
5973
6006
|
episodeNumber: episodeCounter,
|
|
5974
6007
|
episodeTitle: flow.title || seg.ref,
|
|
@@ -5976,18 +6009,21 @@ async function main() {
|
|
|
5976
6009
|
highlightFixtures: flow.highlightFixtures,
|
|
5977
6010
|
demoPace: flow.demoPace,
|
|
5978
6011
|
highlightStyle: flow.highlightStyle,
|
|
5979
|
-
seedMem: () => seedMemForFlow(mem, flow.blocks(), seg.json, seg.ref),
|
|
6012
|
+
seedMem: () => seedMemForFlow(mem, flow.blocks(), seg.json, seg.ref, stubActive),
|
|
5980
6013
|
});
|
|
5981
6014
|
} else {
|
|
5982
6015
|
const r = await findBlock(projectDir, seg.ref);
|
|
5983
6016
|
flows.push(wrapEngine.defineFlow([start, r.block, end]));
|
|
6017
|
+
// A bare Block segment (no Flow, no withMemStub to opt in) only gets
|
|
6018
|
+
// memStub via the global --mem-stub/WAYGRAPH_MEM_STUB override.
|
|
6019
|
+
const stubActive = process.env.WAYGRAPH_MEM_STUB === "1";
|
|
5984
6020
|
flowMeta.push({
|
|
5985
6021
|
episodeNumber: undefined,
|
|
5986
6022
|
episodeTitle: undefined,
|
|
5987
6023
|
highlightFixtures: undefined,
|
|
5988
6024
|
demoPace: undefined,
|
|
5989
6025
|
highlightStyle: undefined,
|
|
5990
|
-
seedMem: () => seedMemForBlock(mem, r, seg.json),
|
|
6026
|
+
seedMem: () => seedMemForBlock(mem, r, seg.json, stubActive),
|
|
5991
6027
|
});
|
|
5992
6028
|
}
|
|
5993
6029
|
}
|
|
@@ -6889,6 +6925,9 @@ function parseRunFlags(argv) {
|
|
|
6889
6925
|
out.data = t.value;
|
|
6890
6926
|
i = t.nextI;
|
|
6891
6927
|
}
|
|
6928
|
+
else if (a === "--mem-stub") {
|
|
6929
|
+
out.memStub = true;
|
|
6930
|
+
}
|
|
6892
6931
|
else if (a === "--video" || a.startsWith("--video=")) {
|
|
6893
6932
|
if (a.startsWith("--video=")) {
|
|
6894
6933
|
out.video = a.slice("--video=".length);
|
|
@@ -7016,6 +7055,9 @@ function applyRunFlags(flags, opts) {
|
|
|
7016
7055
|
if (flags.data !== undefined) {
|
|
7017
7056
|
process.env.WAYGRAPH_DATA = flags.data;
|
|
7018
7057
|
}
|
|
7058
|
+
if (flags.memStub) {
|
|
7059
|
+
process.env.WAYGRAPH_MEM_STUB = "1";
|
|
7060
|
+
}
|
|
7019
7061
|
if (flags.fast) {
|
|
7020
7062
|
process.env.WAYGRAPH_DEMO_FAST = "1";
|
|
7021
7063
|
}
|
|
@@ -7233,7 +7275,12 @@ Primary (less is more):
|
|
|
7233
7275
|
--cli --detach --non-headless Detached session with a real visible browser
|
|
7234
7276
|
(same --non-headless flag run/demo already use)
|
|
7235
7277
|
waygraph auto send <sessionId> "<pick>" Send one pick to a --detach session, print
|
|
7236
|
-
the resulting state as JSON (no TTY needed)
|
|
7278
|
+
the resulting state as JSON (no TTY needed).
|
|
7279
|
+
<pick> is a 1-based menu index, a Block name
|
|
7280
|
+
(exact match; ambiguous names refuse rather than
|
|
7281
|
+
guess), or "q"/"quit". Same command as
|
|
7282
|
+
browser send <sessionId> "<pick>" (prefer that
|
|
7283
|
+
prefix for a --detach/persistent session).
|
|
7237
7284
|
--timeout <ms> Override the 15s default wait for this one call -
|
|
7238
7285
|
a single Block can legitimately run a slow real
|
|
7239
7286
|
interaction (e.g. a multi-step mouse drag)
|
|
@@ -7297,6 +7344,15 @@ Primary (less is more):
|
|
|
7297
7344
|
label,tone?,size?,weight?}], todos[], todoIndex?,
|
|
7298
7345
|
todoTitle?, holdMs? (0=until next), clear:true.
|
|
7299
7346
|
Missing selectors listed in response, not fatal.
|
|
7347
|
+
Same command as browser highlight <sessionId>
|
|
7348
|
+
'<json>' (prefer that prefix for a --detach/
|
|
7349
|
+
persistent session).
|
|
7350
|
+
<selector>|<label>[|<tone>] Shorthand for 1-3 rings, no JSON braces/quotes
|
|
7351
|
+
to escape - rings separated by ";", e.g.
|
|
7352
|
+
"#x|Login button|warning; .err|Error banner|danger".
|
|
7353
|
+
Covers selector/label/tone only; anything else
|
|
7354
|
+
(size/weight/zoom/focus/todos/...) needs the JSON
|
|
7355
|
+
form above.
|
|
7300
7356
|
waygraph browser Show browser subcommands
|
|
7301
7357
|
waygraph browser start Open a new session (headful by default, about:blank)
|
|
7302
7358
|
--inject preset|path Merge an external Block library (e.g. saucedemo)
|
|
@@ -7316,6 +7372,10 @@ Primary (less is more):
|
|
|
7316
7372
|
layer; use browser/pilot for agent-driven sessions.
|
|
7317
7373
|
waygraph demo [--blocks <flow|file|spec>] Watch with step overlay (QA path)
|
|
7318
7374
|
--data '{...}' Mem seed JSON (or inline flow({...}))
|
|
7375
|
+
--mem-stub Fill any requires key --data didn't cover from
|
|
7376
|
+
registerMemStub's registry (needs the key
|
|
7377
|
+
registered, or the flow's own withMemStub - a
|
|
7378
|
+
key with neither still fails preflight)
|
|
7319
7379
|
--auto-next Auto-advance steps (alias: --autoplay)
|
|
7320
7380
|
--fast Shorter auto-next / Next gates (keeps smooth cursor)
|
|
7321
7381
|
--full Classic wrap-all block chips (default: carousel)
|
|
@@ -7332,6 +7392,7 @@ Primary (less is more):
|
|
|
7332
7392
|
--title / --base-url
|
|
7333
7393
|
waygraph run [--blocks <flow|file|spec>] Execute (no overlay unless --step)
|
|
7334
7394
|
--data '{...}'
|
|
7395
|
+
--mem-stub Same as demo's --mem-stub
|
|
7335
7396
|
--non-headless Show browser
|
|
7336
7397
|
--video [dir] Record .webm
|
|
7337
7398
|
--video-viewport WxH Recording size (default run: 1280x720)
|
|
@@ -7460,7 +7521,7 @@ async function main() {
|
|
|
7460
7521
|
" waygraph run src/flows/shop.flow.ts\n" +
|
|
7461
7522
|
" waygraph run --blocks shopFlow\n" +
|
|
7462
7523
|
" waygraph list # file → export map\n" +
|
|
7463
|
-
" Flags: --blocks --data --non-headless --video [dir] --step/--no-step");
|
|
7524
|
+
" Flags: --blocks --data --mem-stub --non-headless --video [dir] --step/--no-step");
|
|
7464
7525
|
process.exit(1);
|
|
7465
7526
|
}
|
|
7466
7527
|
const proj = flags.blocks
|
|
@@ -7779,7 +7840,7 @@ Agents shipped: waygraph-planner, waygraph-author, waygraph-healer.
|
|
|
7779
7840
|
console.error("waygraph demo: missing flow/spec — e.g.\n" +
|
|
7780
7841
|
" waygraph demo src/flows/shop.flow.ts\n" +
|
|
7781
7842
|
" waygraph demo --blocks shopFlow\n" +
|
|
7782
|
-
" Flags: --blocks --data --auto-next --fast --full --mini --ff-expand --ff-disabled --auto-play-video --title --base-url --video");
|
|
7843
|
+
" Flags: --blocks --data --mem-stub --auto-next --fast --full --mini --ff-expand --ff-disabled --auto-play-video --title --base-url --video");
|
|
7783
7844
|
process.exit(1);
|
|
7784
7845
|
}
|
|
7785
7846
|
const proj = flags.blocks
|
|
@@ -8223,19 +8284,25 @@ Agents shipped: waygraph-planner, waygraph-author, waygraph-healer.
|
|
|
8223
8284
|
if (sub === "highlight") {
|
|
8224
8285
|
const raw = args[3];
|
|
8225
8286
|
if (raw === undefined) {
|
|
8226
|
-
console.error(
|
|
8287
|
+
console.error(`waygraph ${command} highlight: usage:\n` +
|
|
8288
|
+
` waygraph ${command} highlight <sessionId> '{"rings":[{"selector":"#x","label":"X"}]}'\n` +
|
|
8289
|
+
` waygraph ${command} highlight <sessionId> "#x|X" (shorthand: <selector>|<label>[|<tone>], rings separated by ";")`);
|
|
8227
8290
|
process.exit(1);
|
|
8228
8291
|
}
|
|
8229
8292
|
let fixtures;
|
|
8230
8293
|
try {
|
|
8231
8294
|
fixtures = JSON.parse(raw);
|
|
8232
8295
|
}
|
|
8233
|
-
catch {
|
|
8234
|
-
|
|
8235
|
-
|
|
8296
|
+
catch (jsonErr) {
|
|
8297
|
+
const shorthand = parseHighlightShorthand(raw);
|
|
8298
|
+
if (shorthand.type === "error") {
|
|
8299
|
+
console.error(`waygraph ${command} highlight: body is neither valid JSON (${jsonErr instanceof Error ? jsonErr.message : String(jsonErr)}) nor valid shorthand (${shorthand.reason})`);
|
|
8300
|
+
process.exit(1);
|
|
8301
|
+
}
|
|
8302
|
+
fixtures = shorthand.fixtures;
|
|
8236
8303
|
}
|
|
8237
8304
|
if (fixtures.op !== undefined && fixtures.op !== "highlight") {
|
|
8238
|
-
console.error(
|
|
8305
|
+
console.error(`waygraph ${command} highlight: do not set "op" (or set it to "highlight")`);
|
|
8239
8306
|
process.exit(1);
|
|
8240
8307
|
}
|
|
8241
8308
|
const { op: _ignore, ...rest } = fixtures;
|
package/dist/engine.d.ts
CHANGED
|
@@ -251,6 +251,15 @@ export interface Flow<Out extends Checkpoint<string>> {
|
|
|
251
251
|
* Per-slot stub/fixture values win. `run()` ignores.
|
|
252
252
|
*/
|
|
253
253
|
readonly highlightStyle?: import("./highlights.js").HighlightStyleDefaults;
|
|
254
|
+
/**
|
|
255
|
+
* Set once this Flow has been wrapped in {@link withMemStub} - opts into
|
|
256
|
+
* auto-filling `requires` keys with registered fake values
|
|
257
|
+
* (`registerMemStub`, `src/mem-stub.ts`) instead of failing preflight when
|
|
258
|
+
* the caller didn't supply them. `run()` never reads this - the CLI's own
|
|
259
|
+
* `--data`/`--mem-stub` seeding, or a bare-library caller's own explicit
|
|
260
|
+
* `seedMemStub(mem, flow)` call, is what actually fills mem before `run()`.
|
|
261
|
+
*/
|
|
262
|
+
readonly memStub?: boolean;
|
|
254
263
|
/**
|
|
255
264
|
* This Flow's constituent Blocks, in order between `start`/`end`, as plain
|
|
256
265
|
* data - for introspection/visualization tools, without running anything.
|
|
@@ -318,6 +327,17 @@ export declare function withSessionReset<Out extends Checkpoint<string>>(flow: F
|
|
|
318
327
|
* @example const ticket123 = withTitle(loginFlow, "Ticket #123 - Sales rep signs in");
|
|
319
328
|
*/
|
|
320
329
|
export declare function withTitle<Out extends Checkpoint<string>>(flow: Flow<Out>, title: string): Flow<Out>;
|
|
330
|
+
/**
|
|
331
|
+
* Opts this Flow into memStub: `requires` keys the caller didn't supply get
|
|
332
|
+
* filled from `registerMemStub`'s registry (`src/mem-stub.ts`) instead of
|
|
333
|
+
* failing preflight, for any key that has a registered fake generator - a
|
|
334
|
+
* required key with no registration still fails exactly as before.
|
|
335
|
+
* Non-destructive, same pattern as {@link withTitle}. `run()` never reads
|
|
336
|
+
* this itself - the CLI's `--data`/`--mem-stub` seeding, or a bare-library
|
|
337
|
+
* caller's own explicit `seedMemStub(mem, flow)` call, does the filling.
|
|
338
|
+
* @example const demoLogin = withMemStub(loginFlow);
|
|
339
|
+
*/
|
|
340
|
+
export declare function withMemStub<Out extends Checkpoint<string>>(flow: Flow<Out>): Flow<Out>;
|
|
321
341
|
/**
|
|
322
342
|
* Demo pacing for a whole Flow episode (fast review vs slow gap/wrong walkthrough).
|
|
323
343
|
* Non-destructive. `fastForwardComposeBlock` is always blitz for its own step;
|
package/dist/engine.js
CHANGED
|
@@ -401,6 +401,24 @@ export function withTitle(flow, title) {
|
|
|
401
401
|
modBlockVerify: (block, nameOrIndex, newCheck) => withTitle(flow.modBlockVerify(block, nameOrIndex, newCheck), title),
|
|
402
402
|
};
|
|
403
403
|
}
|
|
404
|
+
/**
|
|
405
|
+
* Opts this Flow into memStub: `requires` keys the caller didn't supply get
|
|
406
|
+
* filled from `registerMemStub`'s registry (`src/mem-stub.ts`) instead of
|
|
407
|
+
* failing preflight, for any key that has a registered fake generator - a
|
|
408
|
+
* required key with no registration still fails exactly as before.
|
|
409
|
+
* Non-destructive, same pattern as {@link withTitle}. `run()` never reads
|
|
410
|
+
* this itself - the CLI's `--data`/`--mem-stub` seeding, or a bare-library
|
|
411
|
+
* caller's own explicit `seedMemStub(mem, flow)` call, does the filling.
|
|
412
|
+
* @example const demoLogin = withMemStub(loginFlow);
|
|
413
|
+
*/
|
|
414
|
+
export function withMemStub(flow) {
|
|
415
|
+
return {
|
|
416
|
+
...flow,
|
|
417
|
+
memStub: true,
|
|
418
|
+
withBlockVerify: (block, verify) => withMemStub(flow.withBlockVerify(block, verify)),
|
|
419
|
+
modBlockVerify: (block, nameOrIndex, newCheck) => withMemStub(flow.modBlockVerify(block, nameOrIndex, newCheck)),
|
|
420
|
+
};
|
|
421
|
+
}
|
|
404
422
|
/**
|
|
405
423
|
* Demo pacing for a whole Flow episode (fast review vs slow gap/wrong walkthrough).
|
|
406
424
|
* Non-destructive. `fastForwardComposeBlock` is always blitz for its own step;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact alternative to full-JSON `browser highlight` payloads, for the
|
|
3
|
+
* common case (a couple of rings, each just selector + label + maybe tone).
|
|
4
|
+
* Full JSON stays fully supported for anything needing size/weight/zoom/
|
|
5
|
+
* focus/detail/tag/todos - this only ever covers a subset.
|
|
6
|
+
*
|
|
7
|
+
* Grammar: `<selector>|<label>[|<tone>]` rings joined by `;`.
|
|
8
|
+
* "#submit|Login button|warning; .error|Error banner|danger"
|
|
9
|
+
*
|
|
10
|
+
* `;` and `|` were picked because neither is valid inside a CSS/Playwright
|
|
11
|
+
* selector or an ordinary label - unlike `,`/`:`, which real selectors use
|
|
12
|
+
* constantly (`:nth-child(2)`, `:not(.x)`, `[href*="mailto:"]`, compound
|
|
13
|
+
* `a, b` lists). A selector/label containing a literal `;` or `|` needs the
|
|
14
|
+
* full-JSON form instead - documented, not silently mishandled.
|
|
15
|
+
*/
|
|
16
|
+
import type { PilotHighlightFixtures } from "./pilot-overlay.js";
|
|
17
|
+
export type ParsedHighlightShorthand = {
|
|
18
|
+
type: "ok";
|
|
19
|
+
fixtures: PilotHighlightFixtures;
|
|
20
|
+
} | {
|
|
21
|
+
type: "error";
|
|
22
|
+
reason: string;
|
|
23
|
+
};
|
|
24
|
+
export declare function parseHighlightShorthand(raw: string): ParsedHighlightShorthand;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function parseHighlightShorthand(raw) {
|
|
2
|
+
const tokens = raw
|
|
3
|
+
.split(";")
|
|
4
|
+
.map((t) => t.trim())
|
|
5
|
+
.filter((t) => t.length > 0);
|
|
6
|
+
if (tokens.length === 0) {
|
|
7
|
+
return { type: "error", reason: "empty highlight shorthand - give at least one <selector>|<label> ring" };
|
|
8
|
+
}
|
|
9
|
+
const rings = [];
|
|
10
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
11
|
+
const token = tokens[i];
|
|
12
|
+
const parts = token.split("|").map((p) => p.trim());
|
|
13
|
+
if (parts.length < 2 || parts.length > 3) {
|
|
14
|
+
return {
|
|
15
|
+
type: "error",
|
|
16
|
+
reason: `ring ${i + 1} ("${token}") must be <selector>|<label> or <selector>|<label>|<tone> ` +
|
|
17
|
+
`- got ${parts.length} part(s) separated by "|"`,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const [selector, label, tone] = parts;
|
|
21
|
+
if (!selector) {
|
|
22
|
+
return { type: "error", reason: `ring ${i + 1} ("${token}") has an empty selector` };
|
|
23
|
+
}
|
|
24
|
+
if (!label) {
|
|
25
|
+
return { type: "error", reason: `ring ${i + 1} ("${token}") has an empty label` };
|
|
26
|
+
}
|
|
27
|
+
rings.push(tone ? { selector, label, tone } : { selector, label });
|
|
28
|
+
}
|
|
29
|
+
return { type: "ok", fixtures: { rings } };
|
|
30
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export type { Checkpoint, Instruction, Block, DefinedBlock, Start, WaygraphInstanceOption } from "./types.js";
|
|
2
2
|
export { connect, checkpoint } from "./types.js";
|
|
3
3
|
export { MemKey, MemPage, key, keyGroup } from "./mem-page.js";
|
|
4
|
+
export { registerMemStub, seedMemStub, getMemStub } from "./mem-stub.js";
|
|
4
5
|
export { Trait, urlMatches, textEquals, visible, disabled, enabled, frameVisible, frameTextEquals, frameContainsText, } from "./trait.js";
|
|
5
|
-
export { runGraph, spawnTab, narrate, Engine, start, end, branch, withVerify, modVerify, modVerifyAll, defineBlock, defineNavBlock, defineNavClickBlock, defineMethodBlock, defineActionBlock, defineAssertBlock, definePageBlock, defineEffectBlock, defineMemEffectBlock, defineMemNavBlock, locate, composeBlock, fastForwardComposeBlock, isFastForwardBlock, preflight, withSessionReset, withTitle, withDemoPace, withBlockPace, withHighlightStyle, withExpectedFailure, withHighlightFixtures, withStubBefore, withStubAfter, withStubOnError, chainFlow, defineLayout, map, MapBuilder, } from "./engine.js";
|
|
6
|
+
export { runGraph, spawnTab, narrate, Engine, start, end, branch, withVerify, modVerify, modVerifyAll, defineBlock, defineNavBlock, defineNavClickBlock, defineMethodBlock, defineActionBlock, defineAssertBlock, definePageBlock, defineEffectBlock, defineMemEffectBlock, defineMemNavBlock, locate, composeBlock, fastForwardComposeBlock, isFastForwardBlock, preflight, withSessionReset, withTitle, withMemStub, withDemoPace, withBlockPace, withHighlightStyle, withExpectedFailure, withHighlightFixtures, withStubBefore, withStubAfter, withStubOnError, chainFlow, defineLayout, map, MapBuilder, } from "./engine.js";
|
|
6
7
|
export { resolveHighlightSlots, resolveSlides, runStubPhase, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, applyHighlightStyleDefaults, toneIconPrefix, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, normalizeTodos, normalizeTodoPos, normalizeTodoStyle, buildTodoDock, applyTodoPhase, advanceTodoDock, completeSequentialTodoDock, applyDevicePhase, resolveDeviceState, normalizeDevicePreset, normalizeDeviceOrientation, applyOrientation, orientationFromViewport, DEVICE_PRESETS, resolveTodoDockUi, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, WAYGRAPH_RING_CSS, } from "./highlights.js";
|
|
7
8
|
export type { WaygraphHighlightStub, WaygraphHighlightFixture, WaygraphSlide, WaygraphTodoItem, WaygraphTodoInput, WaygraphTodoGroup, WaygraphTodoGroupInput, WaygraphTodosOpts, TodoDockUiOpts, TodoDockUiResolved, TodoDockState, TodoListStyle, FixtureDurationFields, HighlightStubPhase, HighlightStubPhaseName, HighlightFixtureMap, ResolvedHighlight, WaygraphHighlight, HighlightTone, HighlightSize, HighlightWeight, HighlightStyleDefaults, DemoPace, DemoPaceName, StubCtx, StubLifecycleFn, StubPhaseResult, StubPhaseFixtures, DevicePreset, DeviceViewport, DeviceState, DeviceOrientation, } from "./highlights.js";
|
|
8
9
|
export type { Flow, EngineConfig, ComposedBlock, FastForwardComposedBlock, RunGraphOptions, BlockInfo, NavBlock, NavBlockOptions, NavClickBlock, NavClickBlockOptions, MethodBlock, ActionBlock, AssertBlock, AssertBlockOptions, PageBlock, PageBlockOptions, PageMethodEntry, EffectBlock, MemEffectBlock, MemNavBlock, Layout, LayoutOptions, MapBuilderOptions, } from "./engine.js";
|