waygraph 0.15.24 → 0.15.26

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.
Files changed (47) hide show
  1. package/dist/engine/blocks/effect.d.ts +41 -0
  2. package/dist/engine/blocks/effect.js +23 -0
  3. package/dist/engine/blocks/method.d.ts +90 -0
  4. package/dist/engine/blocks/method.js +70 -0
  5. package/dist/engine/blocks/nav.d.ts +138 -0
  6. package/dist/engine/blocks/nav.js +110 -0
  7. package/dist/engine/blocks/page.d.ts +59 -0
  8. package/dist/engine/blocks/page.js +91 -0
  9. package/dist/engine/compose.d.ts +75 -0
  10. package/dist/engine/compose.js +76 -0
  11. package/dist/engine/config.d.ts +55 -0
  12. package/dist/engine/config.js +17 -0
  13. package/dist/engine/core.d.ts +92 -0
  14. package/dist/engine/core.js +256 -0
  15. package/dist/engine/engine-class.d.ts +289 -0
  16. package/dist/engine/engine-class.js +257 -0
  17. package/dist/engine/flow.d.ts +266 -0
  18. package/dist/engine/flow.js +387 -0
  19. package/dist/engine/locate.d.ts +26 -0
  20. package/dist/engine/locate.js +62 -0
  21. package/dist/engine/run-graph.d.ts +147 -0
  22. package/dist/engine/run-graph.js +223 -0
  23. package/dist/engine.d.ts +11 -1224
  24. package/dist/engine.js +12 -1550
  25. package/dist/highlights/css.d.ts +14 -0
  26. package/dist/highlights/css.js +58 -0
  27. package/dist/highlights/demo-pace.d.ts +56 -0
  28. package/dist/highlights/demo-pace.js +184 -0
  29. package/dist/highlights/device.d.ts +29 -0
  30. package/dist/highlights/device.js +171 -0
  31. package/dist/highlights/merge.d.ts +8 -0
  32. package/dist/highlights/merge.js +173 -0
  33. package/dist/highlights/run-phase.d.ts +43 -0
  34. package/dist/highlights/run-phase.js +211 -0
  35. package/dist/highlights/stub-ctx.d.ts +10 -0
  36. package/dist/highlights/stub-ctx.js +281 -0
  37. package/dist/highlights/style.d.ts +27 -0
  38. package/dist/highlights/style.js +84 -0
  39. package/dist/highlights/todo-dock.d.ts +52 -0
  40. package/dist/highlights/todo-dock.js +248 -0
  41. package/dist/highlights/types.d.ts +536 -0
  42. package/dist/highlights/types.js +1 -0
  43. package/dist/highlights.d.ts +9 -734
  44. package/dist/highlights.js +10 -1416
  45. package/examples/saucedemo/.waygraph-auto/008f4e6e.log +0 -0
  46. package/examples/saucedemo/.waygraph-auto/22fefa13.log +0 -0
  47. package/package.json +1 -1
package/dist/engine.d.ts CHANGED
@@ -1,1224 +1,11 @@
1
- import type { BrowserContext, BrowserType, Page, Locator } from "@playwright/test";
2
- import type { Block, Checkpoint, Instruction, DefinedBlock, ActionPage, WaygraphHighlight } from "./types.js";
3
- import { MemPage } from "./mem-page.js";
4
- import type { MemKey } from "./mem-page.js";
5
- import type { Trait } from "./trait.js";
6
- import { type HighlightStyleDefaults, type DemoPace } from "./highlights.js";
7
- /**
8
- * Checks `block.requires` against `mem` and throws, naming every missing key at
9
- * once, if any are unset. `connect()` unions each Block's `requires` into the
10
- * composed Block, so calling this on a `defineFlow`-built Flow's entry - which
11
- * `runGraph` does automatically, before opening a tab - covers every Block in
12
- * that flow. It does not see Blocks only reachable through `branch()` routing,
13
- * since those depend on a runtime decision that hasn't happened yet.
14
- */
15
- export declare function preflight(mem: MemPage, block: Block<any, any>): void;
16
- /**
17
- * A cross-cutting, automatically-enforced verify - the real engine
18
- * primitive behind "persistent UI" (a sidebar, a header, anything that's
19
- * supposed to be present on many/most Checkpoints, not just one page's own
20
- * Block). Deliberately NOT tied to any folder convention - `appliesTo`
21
- * matches by Checkpoint tag, explicitly, so this works the same whether a
22
- * project uses the Waygraph Map folder convention or the older freeform
23
- * "manual mode" layout real production consumers (pia-waygraph,
24
- * zsign-atomic-waygraph) already use - registering a Layout with an Engine
25
- * is the only requirement, folder structure is irrelevant to it.
26
- *
27
- * Not a Block: no `act`/`resolve`/Checkpoint of its own, never a graph
28
- * node - it only ever adds extra `verify` Traits to Checkpoints something
29
- * else already resolved to, the same way `runVerify` already runs after
30
- * any Block's own `resolve()` (see `runGraph`'s own verify step, just
31
- * below) - a Layout is that mechanism made reusable across many
32
- * Checkpoints instead of hand-repeated per Block.
33
- */
34
- export interface Layout {
35
- name: string;
36
- description?: string;
37
- appliesTo: (tag: string) => boolean;
38
- /** Function form gets the full resolved Checkpoint, same shape `runVerify`/a Block's own `verify` already use - not just the tag. */
39
- verify: Trait[] | ((out: Checkpoint<string>) => Trait[]);
40
- }
41
- export interface LayoutOptions {
42
- name: string;
43
- description?: string;
44
- /** A fixed tag list, or a predicate for open-ended matching (e.g. a naming pattern). */
45
- appliesTo: readonly string[] | ((tag: string) => boolean);
46
- verify: Trait[] | ((out: Checkpoint<string>) => Trait[]);
47
- }
48
- /**
49
- * @example
50
- * const AppShellLayout = defineLayout({
51
- * name: "app-shell",
52
- * appliesTo: ["AppHome", "Chats", "Notifications", "Settings"],
53
- * verify: [Trait.visible(SidebarSel.root)],
54
- * });
55
- * const engine = new Engine({ layouts: [AppShellLayout] });
56
- * // Now every Block resolving to one of those four Checkpoints, in any
57
- * // Flow this engine defines, also gets AppShellLayout's own verify run
58
- * // automatically - a page that silently drops the sidebar fails loud,
59
- * // even though no individual Block's own `verify` mentions it.
60
- */
61
- export declare function defineLayout(options: LayoutOptions): Layout;
62
- /**
63
- * Runs `entry` against a fresh tab and returns the resulting terminal Checkpoint.
64
- * The engine owns the tab's lifecycle, not any individual Block: the tab is
65
- * created before the first `act` runs and closed in `finally`, so it closes even
66
- * if a step throws.
67
- *
68
- * After each Block resolves, its `next` (if any, from `branch()`) is followed to
69
- * the following Block, in the same tab - a Block with no `next` runs exactly once,
70
- * unchanged from before `branch()` existed. `next` returning the same Block is a
71
- * self-loop; `maxSteps` bounds the loop so a routing bug fails loud instead of
72
- * hanging.
73
- *
74
- * `terminals`, if given, is a sanity check: the Checkpoint the run stops on must be
75
- * one of the tags the caller actually expects, or the run fails loud instead of
76
- * silently returning an unexpected result. Omit it when there is only ever one
77
- * reachable outcome by construction (as with a `defineFlow`-built Flow, which has
78
- * no routing at all) - there is nothing to validate against.
79
- */
80
- export declare function runGraph<TOut extends Checkpoint<string>>(entry: Block<Checkpoint<"__start__">, TOut>, terminals: ReadonlySet<string> | undefined, context: BrowserContext, mem: MemPage, maxSteps?: number, options?: RunGraphOptions): Promise<TOut>;
81
- /** Options for `runGraph`'s trailing `options` arg and for {@link Flow.run}'s `options` overload. */
82
- export interface RunGraphOptions {
83
- /**
84
- * Drive this already-open page instead of opening a new one via
85
- * `context.newPage()`. Lets a run continue on a tab the caller already
86
- * has open (e.g. one that's already logged in), instead of every run
87
- * being forced to start its own fresh tab and lose access to whatever was
88
- * already there.
89
- */
90
- page?: Page;
91
- /**
92
- * `Flow.run` defaults this to `true` only when it opened the page itself
93
- * (no `page` given) - matching every call from before this option
94
- * existed. If you pass your own `page`, it defaults to `false` instead:
95
- * a page you opened is yours to close, not this run's to close out from
96
- * under you. Either way an explicit value here always wins. `runGraph`
97
- * itself has no such page-aware default (its caller already decided
98
- * `page` and `closeOnFinish` together) - it just does what you ask.
99
- */
100
- closeOnFinish?: boolean;
101
- /**
102
- * Layouts (see {@link defineLayout}) whose `verify` runs automatically
103
- * after any Block in this run resolves to a Checkpoint they apply to -
104
- * `Flow.run` threads this in from `EngineConfig.layouts` by default; pass
105
- * it directly here to call `runGraph` standalone (outside a `Flow`) with
106
- * layouts still enforced.
107
- */
108
- layouts?: readonly Layout[];
109
- }
110
- /**
111
- * Drives a genuinely separate second tab through its own Block/Flow, in the
112
- * same browser context an existing `page` already belongs to - the primitive
113
- * `observe()` is specifically allowed to reach for (see {@link Instruction.observe}),
114
- * since `act`/`resolve` can't touch `page.context()` at all. A thin, named
115
- * wrapper around `runGraph` (which already opens its own page from whatever
116
- * context it's given) rather than a new mechanism - the point is making the
117
- * pattern discoverable and giving it a real name, not inventing new tab-lifecycle
118
- * logic. The spawned tab is closed (by `runGraph`'s own `finally`) before this
119
- * resolves; the original `page` is never touched.
120
- * @example
121
- * async observe(page, mem) {
122
- * const receipt = await spawnTab(EmailReceiptFlow, page, mem);
123
- * mem.set(ReceiptCode, receipt.__state);
124
- * }
125
- */
126
- export declare function spawnTab<TOut extends Checkpoint<string>>(entry: Block<Checkpoint<"__start__">, TOut>, page: Page, mem: MemPage, maxSteps?: number): Promise<TOut>;
127
- /**
128
- * Wraps one `act()` interaction with a caption and highlight, for a Block
129
- * author who wants to explicitly narrate a specific step rather than rely
130
- * on the generic per-fill/per-click narration `waygraph chain
131
- * WAYGRAPH_STEP=1` already does automatically -
132
- * `await narrate(submitButton, "confirms the order", () => submitButton.click())`
133
- * reads at the call site instead of being inferred.
134
- *
135
- * A true no-op everywhere except inside a `WAYGRAPH_STEP=1` run: it checks
136
- * for a `window.__wgPositionRing` hook the step-mode overlay installs on
137
- * the page before doing anything, so a headless/CI/automated run (nobody
138
- * watching, per-character delays and highlight pauses would just be wasted
139
- * time) pays zero extra cost - not even the boundingBox() lookup runs. The
140
- * `action` itself always runs regardless; only the narration is
141
- * conditional.
142
- * @example await narrate(submitButton, "confirms the order", () => submitButton.click())
143
- */
144
- export declare function narrate<T>(locator: Locator, caption: string, action: () => Promise<T>): Promise<T>;
145
- /** Reserved markers bookending a `defineFlow([start, ...blocks, end])` call. */
146
- export declare const start: unique symbol;
147
- export declare const end: unique symbol;
148
- type StartMarker = typeof start;
149
- type EndMarker = typeof end;
150
- /**
151
- * A composed, runnable flow. Always begins from Checkpoint<"__start__">, same as
152
- * runGraph's `entry`. `run(context, mem)` uses a context you already have (the
153
- * usual case inside a `@playwright/test` file, via its `context` fixture) -
154
- * there's no browser for this Flow to launch, so no config to pass. `run(mem,
155
- * config?)` - no context - has the Engine launch and own its own browser for
156
- * this one run instead, and closes it in `finally`; use this outside a
157
- * Playwright test file, or when a flow genuinely needs its own browser instance
158
- * rather than sharing the test's. `config` here overrides, per key, whatever
159
- * `new Engine(...)` was constructed with - so one flow (module-level, imported
160
- * by many specs) can still have every run choose its own `headless`/`slowMo`
161
- * rather than being stuck with whatever the flow's own file happened to set.
162
- *
163
- * `withBlockVerify`/`modBlockVerify` do what {@link withVerify}/{@link modVerify}
164
- * do for a single Block, one level up - a spec that only imports the finished
165
- * Flow (not necessarily the individual Blocks it was built from) can still
166
- * patch one Block's verify for just that call, without editing the flow's own
167
- * file or hand-rebuilding its Block list. Addressed the same three ways
168
- * `modVerify` already addresses a Trait - the Block reference itself
169
- * (`LoginBlock`, imported like any other Block; typo-proof, refactor-safe,
170
- * jump-to-definition works), a plain string name, or a numeric index into this
171
- * Flow's Block list (position between `start`/`end`, 0-based) for a spec that
172
- * genuinely doesn't have the Block imported and doesn't want to spell its
173
- * name. A reference or string resolves by `.name` under the hood (a Block's
174
- * own stable identity in this codebase, same as every error message elsewhere
175
- * in this file already uses) rather than object identity - it stays
176
- * resolvable even after an earlier patch already swapped that slot for a new
177
- * object, since the name survives every `withVerify`/`modVerify` in the
178
- * chain; an index is checked fresh against this Flow's current Block list
179
- * each call, so it's still correct after a patch too. Returns a new Flow -
180
- * the original is untouched, same as every other decorator in this module.
181
- */
182
- /**
183
- * One Block's identity as exposed by graph introspection - {@link Flow.blocks},
184
- * {@link ComposedBlock.steps} - without needing to execute anything. Does not
185
- * include the Block's literal Checkpoint `In`/`Out` tags: those are TypeScript
186
- * literal types, erased at runtime, and can only be recovered by a build-time
187
- * static-analysis tool reading the source's own type annotations - see the
188
- * `graph-visualization` openspec change for that piece.
189
- */
190
- export interface BlockInfo {
191
- name: string;
192
- /** `branch()`'s routing table, if this Block has one - see {@link Block.routes}. */
193
- routes?: Readonly<Record<string, string | null>>;
194
- /**
195
- * True when {@link chainFlow} inserted a session-reset boundary
196
- * immediately before this Block - i.e. this is the first Block of a
197
- * sub-flow that was wrapped in {@link withSessionReset}, and it isn't
198
- * the very first sub-flow in the chain. Absent (not just false) from
199
- * `Flow.blocks()` on an ordinary, non-chained Flow.
200
- */
201
- resetSessionBefore?: boolean;
202
- /**
203
- * The actual Block, re-runnable on its own via
204
- * `engine.defineFlow([start, info.block, end])` - e.g. step-through
205
- * tooling that runs a Flow's Blocks one at a time. Introspection that only
206
- * wants to read the shape (a visualizer) can ignore this and use `name`/
207
- * `routes` alone.
208
- */
209
- block: Block<any, any>;
210
- }
211
- export interface Flow<Out extends Checkpoint<string>> {
212
- /**
213
- * True once this Flow has been wrapped in {@link withSessionReset} - a
214
- * hint for `chainFlow`, not something `run()` itself checks: it marks
215
- * this Flow as one that expects to start from a genuinely fresh,
216
- * unauthenticated browser state (e.g. a login flow meant to be
217
- * re-entered later in a longer chain). `chainFlow` clears cookies and
218
- * storage right before this Flow's first Block runs, but only when this
219
- * Flow isn't the very first thing in the chain (nothing to reset yet).
220
- */
221
- readonly resetSession: boolean;
222
- /**
223
- * Set once this Flow has been wrapped in {@link withTitle} - a display
224
- * hint for a step-through overlay/showcase (e.g. "Ticket #123"), not
225
- * something `run()` itself uses.
226
- */
227
- readonly title?: string;
228
- /**
229
- * Set once this Flow has been wrapped in {@link withExpectedFailure} - a
230
- * display hint for a step-through overlay: this Flow is DESIGNED to throw
231
- * (e.g. a blocked user's login attempt genuinely failing), not a bug in
232
- * the demo itself. `run()` never reads this - the Flow still throws
233
- * exactly as it would otherwise; only the overlay's error panel reads it,
234
- * to show an informational "this is the point" panel instead of an
235
- * alarming one indistinguishable from a real break.
236
- */
237
- readonly expectedFailureReason?: string;
238
- /**
239
- * Set once this Flow has been wrapped in {@link withHighlightFixtures} -
240
- * demo narration overrides (unlimited purposes on `.flow.ts`) keyed by
241
- * block.name. `run()` never reads this; step/`demo` merges onto block stubs.
242
- */
243
- readonly highlightFixtures?: import("./highlights.js").HighlightFixtureMap;
244
- /**
245
- * Demo pacing for this Flow's episode (`withDemoPace`).
246
- * `run()` ignores; step/demo applies per Block in the episode.
247
- */
248
- readonly demoPace?: import("./highlights.js").DemoPace;
249
- /**
250
- * Default highlight size/weight/tone for this Flow's episode (`withHighlightStyle`).
251
- * Per-slot stub/fixture values win. `run()` ignores.
252
- */
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;
263
- /**
264
- * This Flow's constituent Blocks, in order between `start`/`end`, as plain
265
- * data - for introspection/visualization tools, without running anything.
266
- * @example loginFlow.blocks() // [{ name: "login" }, { name: "add-to-cart", routes: {...} }]
267
- */
268
- blocks(): readonly BlockInfo[];
269
- run(context: BrowserContext, mem: MemPage): Promise<Out>;
270
- /**
271
- * `options.closeOnFinish: false` hands the driven page back instead of
272
- * closing it - use this overload (a literal `false`, not a `boolean`) to
273
- * get the `{ result, page }` shape typed correctly.
274
- * @example const { result, page } = await loginFlow.run(context, mem, { closeOnFinish: false });
275
- */
276
- run(context: BrowserContext, mem: MemPage, options: RunGraphOptions & {
277
- closeOnFinish: false;
278
- }): Promise<{
279
- result: Out;
280
- page: Page;
281
- }>;
282
- /**
283
- * `options.page` drives that already-open page instead of opening a new
284
- * one - see {@link RunGraphOptions}.
285
- * @example await loginFlow.run(context, mem, { page: existingPage });
286
- */
287
- run(context: BrowserContext, mem: MemPage, options?: RunGraphOptions): Promise<Out>;
288
- run(mem: MemPage, config?: EngineConfig): Promise<Out>;
289
- /**
290
- * Replaces the given Block's whole verify list, wherever it sits in this
291
- * Flow - same rules as {@link withVerify}. Throws if no Block in this Flow
292
- * matches.
293
- * @example checkoutFlow.withBlockVerify(LoginBlock, [])
294
- * @example checkoutFlow.withBlockVerify("login", [])
295
- * @example checkoutFlow.withBlockVerify(0, [])
296
- */
297
- withBlockVerify(block: Block<any, any> | string | number, verify: Trait[] | ((out: any) => Trait[])): Flow<Out>;
298
- /**
299
- * Replaces one Trait inside the given Block's verify list - same rules as
300
- * {@link modVerify}. Throws if no Block in this Flow matches, or if that
301
- * Block has no verify trait at the given name/index.
302
- * @example checkoutFlow.modBlockVerify(LoginBlock, "url-matches", Trait.url({ pathname: "/inventory.html" }))
303
- */
304
- modBlockVerify(block: Block<any, any> | string | number, nameOrIndex: string | number, newCheck: Trait["check"] | Trait): Flow<Out>;
305
- }
306
- /**
307
- * Marks a Flow as expecting to start from a genuinely fresh, unauthenticated
308
- * browser state - e.g. a login flow meant to be re-entered later in a longer
309
- * showcase. Non-destructive, same pattern as {@link withVerify}: returns a
310
- * new Flow: the original is untouched, and the flag survives any further
311
- * `withBlockVerify`/`modBlockVerify` patch applied to the result.
312
- *
313
- * By itself this does nothing - `Flow.run()` never reads `resetSession`.
314
- * It's a hint {@link chainFlow} looks for: right before this Flow's first
315
- * Block runs (skipped if it's the very first thing in the chain - nothing
316
- * to reset yet), chainFlow clears cookies and storage so this Flow starts
317
- * exactly as if a human had opened the app fresh, not wherever the
318
- * previous Flow in the chain happened to leave the page.
319
- * @example const loginFlow = withSessionReset(engine.defineFlow([start, LoginBlock, end]));
320
- */
321
- export declare function withSessionReset<Out extends Checkpoint<string>>(flow: Flow<Out>): Flow<Out>;
322
- /**
323
- * Names a Flow for display - a step-through overlay/showcase (waygraph's
324
- * `chain --step`) shows this as the running banner while this Flow's own
325
- * Blocks are executing, instead of one fixed title for the whole run.
326
- * Non-destructive, same pattern as {@link withSessionReset}.
327
- * @example const ticket123 = withTitle(loginFlow, "Ticket #123 - Sales rep signs in");
328
- */
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>;
341
- /**
342
- * Demo pacing for a whole Flow episode (fast review vs slow gap/wrong walkthrough).
343
- * Non-destructive. `fastForwardComposeBlock` is always blitz for its own step;
344
- * use this when an entire episode should be fast or slow.
345
- * @example withDemoPace(withTitle(gapFlow, "Episode 2 - gaps"), "slow")
346
- * @example withDemoPace(happyFlow, 0.5) // 2x faster than normal
347
- * @example withDemoPace(gapFlow, 2.5) // 2.5x slower
348
- * @example withDemoPace(gapFlow, 4500) // absolute ~4.5s gates/dwells
349
- */
350
- export declare function withDemoPace<Out extends Checkpoint<string>>(flow: Flow<Out>, pace: DemoPace): Flow<Out>;
351
- /**
352
- * Default highlight look for a Flow episode (size / weight / optional tone).
353
- * Per-slot stub or {@link withHighlightFixtures} values win when set.
354
- * @example withHighlightStyle(withDemoPace(gapFlow, "slow"), { size: "lg", weight: "bold" })
355
- */
356
- export declare function withHighlightStyle<Out extends Checkpoint<string>>(flow: Flow<Out>, style: HighlightStyleDefaults): Flow<Out>;
357
- /**
358
- * Demo pacing for one Block or compose unit (not FF - use fastForwardComposeBlock for blitz).
359
- * @example withBlockPace(composeBlock("gap-review", [...]), "slow")
360
- */
361
- export declare function withBlockPace<In extends Checkpoint<string>, Out extends Checkpoint<string>>(block: Block<In, Out>, pace: DemoPace): Block<In, Out>;
362
- /**
363
- * Marks a Flow as one that's SUPPOSED to throw - e.g. a locked-out user's
364
- * login attempt genuinely failing, demonstrating the app's own real
365
- * behavior rather than a bug in the demo. Non-destructive, same pattern as
366
- * {@link withTitle}.
367
- *
368
- * By itself this changes nothing about how the Flow runs - it still throws
369
- * Marks a Flow whose last step is an intentional product failure (demo overlay).
370
- * Step mode shows stubOnError rings + an amber "expected outcome" panel when:
371
- * - the last block **throws**, or
372
- * - the last block **succeeds** on the fail branch (e.g. LoginPage + error banner
373
- * after submit-login branched instead of throwing).
374
- * Headless `run()` is unchanged - branching success returns the checkpoint.
375
- * @example const viewerBlockedFlow = withExpectedFailure(
376
- * engine.defineFlow([start, NavLoginBlock, SubmitLoginActionBlock, end]),
377
- * "locked_out_user stays on LoginPage with the error banner - working as intended.",
378
- * );
379
- */
380
- export declare function withExpectedFailure<Out extends Checkpoint<string>>(flow: Flow<Out>, reason: string): Flow<Out>;
381
- /**
382
- * Attaches demo highlight fixtures to a Flow - unlimited purposes on `.flow.ts`
383
- * (AC copy, BUG/GATE tags, detail) merged onto each block's stubBefore/stubAfter/
384
- * stubOnError slots. Non-destructive, same pattern as {@link withTitle}.
385
- * Demo/`chain --step` reads `flow.highlightFixtures`; `run()` ignores it.
386
- * @example withHighlightFixtures(issueFlow, { "submit-login": { stubBefore: { email: { label: "AC-1" } } } })
387
- */
388
- export declare function withHighlightFixtures<Out extends Checkpoint<string>>(flow: Flow<Out>, fixtures: import("./highlights.js").HighlightFixtureMap): Flow<Out>;
389
- /**
390
- * Runs several complete, independent Flows one after another, reusing the
391
- * same page/context/mem in order - each remains its own validated
392
- * start-to-end unit (a Flow's own `[start, ...blocks, end]` contract is
393
- * untouched), this only sequences whole Flows. Deliberately NOT the same
394
- * thing as {@link composeBlock}: composeBlock nests several Blocks into
395
- * one Block that can drop back into another `defineFlow([...])` - a
396
- * chainFlow result is a Flow, but not meant to be nested inside another
397
- * Flow's own Block list. Recursive nesting stays composeBlock's job alone;
398
- * chainFlow is a flat, one-level sequencing of complete showcases (e.g.
399
- * "ticket #123 end to end, then ticket #456 end to end").
400
- *
401
- * A sub-flow wrapped in {@link withSessionReset} gets its cookies/storage
402
- * cleared right before it runs (skipped for the very first flow in the
403
- * chain - nothing to reset yet). The combined result is whatever the LAST
404
- * flow resolved to.
405
- * @example chainFlow(ticket123Flow, ticket456Flow).run(context, mem)
406
- */
407
- export declare function chainFlow(...flows: readonly Flow<any>[]): Flow<any>;
408
- /**
409
- * A Block guaranteed to have `withStepVerify`/`modStepVerify` attached - what
410
- * {@link composeBlock} returns. Extends plain `Block`, so it drops straight into
411
- * `defineFlow([start, ..., composed, ..., end])` like any other Block; no special
412
- * casing needed anywhere that only expects a `Block`.
413
- */
414
- export interface ComposedBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>> extends Block<In, Out> {
415
- /**
416
- * This composed Block's constituent steps, in order, as plain data - same
417
- * shape and purpose as {@link Flow.blocks}.
418
- * @example GovFormBlock.steps() // [{ name: "step-1-name" }, { name: "step-2-address" }, ...]
419
- */
420
- steps(): readonly BlockInfo[];
421
- /**
422
- * Replaces one step's whole verify list, wherever it sits in this composed
423
- * Block - same rules as {@link withVerify}. Throws if no step matches.
424
- * @example GovFormBlock.withStepVerify("step-2-address", [])
425
- */
426
- withStepVerify(step: Block<any, any> | string | number, verify: Trait[] | ((out: any) => Trait[])): ComposedBlock<In, Out>;
427
- /**
428
- * Replaces one Trait inside one step's verify list - same rules as
429
- * {@link modVerify}. Throws if no step matches, or that step has no verify
430
- * trait at the given name/index.
431
- * @example GovFormBlock.modStepVerify("step-2-address", "zip-visible", Trait.visible("#zip-confirmed"))
432
- */
433
- modStepVerify(step: Block<any, any> | string | number, nameOrIndex: string | number, newCheck: Trait["check"] | Trait): ComposedBlock<In, Out>;
434
- }
435
- /**
436
- * Chains several Blocks into one named, exported unit - what a naturally
437
- * multi-step feature (a stepper form with several pages, say) wants instead of
438
- * either one giant Block with no per-step confirmation, or several separately
439
- * exported Blocks the caller has to wire together by hand every time. Each
440
- * step keeps its own real `act`/`resolve`/`verify`, chained the same way
441
- * `connect()` already chains any two Blocks - `composeBlock` is not a new
442
- * execution mechanism, just `connect()` reduced over the list plus a name and
443
- * the same step-list closure `Flow` already keeps for its own Block list, so
444
- * `withStepVerify`/`modStepVerify` can patch one step from outside without the
445
- * caller needing to hand-rebuild the chain. Resolution is by `.name`, not
446
- * object identity, so a step reference stays valid even after an earlier patch
447
- * swapped that slot for a new object - see {@link Flow.withBlockVerify} for
448
- * the identical guarantee one level up. Each call returns a new composed
449
- * Block; the original is untouched.
450
- * @example composeBlock("gov-form", [step1, step2, step3, step4])
451
- */
452
- export declare function composeBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>>(name: string, steps: readonly [Block<In, Out>]): ComposedBlock<In, Out>;
453
- export declare function composeBlock<In extends Checkpoint<string>, B extends Checkpoint<string>, Out extends Checkpoint<string>>(name: string, steps: readonly [Block<In, B>, Block<B, Out>]): ComposedBlock<In, Out>;
454
- export declare function composeBlock<In extends Checkpoint<string>, B extends Checkpoint<string>, C extends Checkpoint<string>, Out extends Checkpoint<string>>(name: string, steps: readonly [Block<In, B>, Block<B, C>, Block<C, Out>]): ComposedBlock<In, Out>;
455
- export declare function composeBlock<In extends Checkpoint<string>, B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, Out extends Checkpoint<string>>(name: string, steps: readonly [Block<In, B>, Block<B, C>, Block<C, D>, Block<D, Out>]): ComposedBlock<In, Out>;
456
- export declare function composeBlock<In extends Checkpoint<string>, B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, Out extends Checkpoint<string>>(name: string, steps: readonly [Block<In, B>, Block<B, C>, Block<C, D>, Block<D, E>, Block<E, Out>]): ComposedBlock<In, Out>;
457
- export declare function composeBlock<In extends Checkpoint<string>, B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, Out extends Checkpoint<string>>(name: string, steps: readonly [Block<In, B>, Block<B, C>, Block<C, D>, Block<D, E>, Block<E, F>, Block<F, Out>]): ComposedBlock<In, Out>;
458
- /**
459
- * Same shape as {@link composeBlock}, marked {@link FastForwardComposedBlock.fastForward}
460
- * so demo/run/traverse treat it as **one opaque step** that **blitzes wall-clock**
461
- * (skip overlay theater + no Playwright slowMo on that step's acts) unless
462
- * `--ff-expand` / `WAYGRAPH_FF_EXPAND=1`. Inner acts still run for real;
463
- * failures are prefixed `ff-name > …` so the locus names both the FF unit and
464
- * the inner step. Prefer this for boring or heavy-check prefixes (auth, seed,
465
- * dashboard settle) — you may put **several** FF units in one Flow with normal
466
- * Blocks between them.
467
- * @example fastForwardComposeBlock("ff-owner-auth", [NavLoginBlock, SubmitLoginForFlow])
468
- */
469
- export interface FastForwardComposedBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>> extends ComposedBlock<In, Out> {
470
- readonly fastForward: true;
471
- }
472
- /** True when `block` came from {@link fastForwardComposeBlock}. */
473
- export declare function isFastForwardBlock(block: Block<any, any>): block is FastForwardComposedBlock<any, any>;
474
- export declare function fastForwardComposeBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>>(name: string, steps: readonly [Block<In, Out>]): FastForwardComposedBlock<In, Out>;
475
- export declare function fastForwardComposeBlock<In extends Checkpoint<string>, B extends Checkpoint<string>, Out extends Checkpoint<string>>(name: string, steps: readonly [Block<In, B>, Block<B, Out>]): FastForwardComposedBlock<In, Out>;
476
- export declare function fastForwardComposeBlock<In extends Checkpoint<string>, B extends Checkpoint<string>, C extends Checkpoint<string>, Out extends Checkpoint<string>>(name: string, steps: readonly [Block<In, B>, Block<B, C>, Block<C, Out>]): FastForwardComposedBlock<In, Out>;
477
- export declare function fastForwardComposeBlock<In extends Checkpoint<string>, B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, Out extends Checkpoint<string>>(name: string, steps: readonly [Block<In, B>, Block<B, C>, Block<C, D>, Block<D, Out>]): FastForwardComposedBlock<In, Out>;
478
- export declare function fastForwardComposeBlock<In extends Checkpoint<string>, B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, Out extends Checkpoint<string>>(name: string, steps: readonly [Block<In, B>, Block<B, C>, Block<C, D>, Block<D, E>, Block<E, Out>]): FastForwardComposedBlock<In, Out>;
479
- export declare function fastForwardComposeBlock<In extends Checkpoint<string>, B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, Out extends Checkpoint<string>>(name: string, steps: readonly [Block<In, B>, Block<B, C>, Block<C, D>, Block<D, E>, Block<E, F>, Block<F, Out>]): FastForwardComposedBlock<In, Out>;
480
- /** Configures how an Engine launches its own browser - only used by `Flow.run(mem)` (no context given); ignored by `Flow.run(context, mem)`, since that context is already launched. */
481
- export interface EngineConfig {
482
- /** Default `true`, matching Playwright's own default. */
483
- headless?: boolean;
484
- /** Default `"chromium"`. */
485
- browserName?: "chromium" | "firefox" | "webkit";
486
- /** Milliseconds Playwright pauses before each operation - for watching a non-headless run with your own eyes, not for real runs. Default `0`. */
487
- slowMo?: number;
488
- /**
489
- * Overrides which `BrowserType` actually launches, per browser name -
490
- * default is the real `@playwright/test` export for whichever one
491
- * `browserName` picks. Accepts anything shaped like Playwright's own
492
- * `chromium`/`firefox`/`webkit` (same `.launch()` signature) - a
493
- * stealth-patched or otherwise customized launcher (e.g. `playwright-extra`
494
- * plus a stealth plugin) drops in here unmodified. Puppeteer is a
495
- * different `Page`/`BrowserContext` shape entirely and isn't supported by
496
- * this seam.
497
- * @example new Engine({ browsers: { chromium: stealthChromium } })
498
- */
499
- browsers?: Partial<Record<"chromium" | "firefox" | "webkit", BrowserType>>;
500
- /**
501
- * Playwright `recordVideo` - saves a `.webm` when the context closes.
502
- * Works headless. Lighter than a full clip-engine pipeline (no overlay/ffmpeg).
503
- * @example new Engine({ recordVideo: { dir: "./videos" } })
504
- */
505
- recordVideo?: {
506
- dir: string;
507
- size?: {
508
- width: number;
509
- height: number;
510
- };
511
- };
512
- /**
513
- * Cross-cutting, automatically-enforced verify (see {@link defineLayout})
514
- * - applies to every Flow this Engine defines. Works the same regardless
515
- * of folder convention (Waygraph Map or freeform "manual mode") since
516
- * matching is by Checkpoint tag, never by file path.
517
- * @example new Engine({ layouts: [AppShellLayout] })
518
- */
519
- layouts?: readonly Layout[];
520
- }
521
- type S = Checkpoint<"__start__">;
522
- /**
523
- * Holds engine-level configuration shared across every flow defined from it -
524
- * for now, just how to launch a browser when a Flow owns its own
525
- * (`headless`/`browserName`/`slowMo`); traits/interstitials/watchers land here
526
- * in a later change.
527
- * @example const engine = new Engine({ headless: false, slowMo: 250 });
528
- */
529
- export declare class Engine {
530
- private readonly config;
531
- constructor(config?: EngineConfig);
532
- /**
533
- * Builds a runnable {@link Flow} from `[start, ...Blocks, end]`, typechecked
534
- * so each Block's `In` must match the previous Block's `Out` - the same
535
- * check `connect()` does, just declared as a flat array instead of hand-nested
536
- * calls. Overloaded for 2-9 array slots (1-8 real Blocks between `start`/`end`)
537
- * rather than one fully-generic recursive tuple type, so each arity is as
538
- * reliably checked as `connect<A,B,C>` itself.
539
- * @example engine.defineFlow([start, LoginBlock, AddToCartBlock, end])
540
- */
541
- defineFlow<B extends Checkpoint<string>>(blocks: readonly [StartMarker, Block<S, B>, EndMarker]): Flow<B>;
542
- defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>>(blocks: readonly [StartMarker, Block<S, B>, Block<B, C>, EndMarker]): Flow<C>;
543
- defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>>(blocks: readonly [StartMarker, Block<S, B>, Block<B, C>, Block<C, D>, EndMarker]): Flow<D>;
544
- defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>>(blocks: readonly [StartMarker, Block<S, B>, Block<B, C>, Block<C, D>, Block<D, E>, EndMarker]): Flow<E>;
545
- defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>>(blocks: readonly [
546
- StartMarker,
547
- Block<S, B>,
548
- Block<B, C>,
549
- Block<C, D>,
550
- Block<D, E>,
551
- Block<E, F>,
552
- EndMarker
553
- ]): Flow<F>;
554
- defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, G extends Checkpoint<string>>(blocks: readonly [
555
- StartMarker,
556
- Block<S, B>,
557
- Block<B, C>,
558
- Block<C, D>,
559
- Block<D, E>,
560
- Block<E, F>,
561
- Block<F, G>,
562
- EndMarker
563
- ]): Flow<G>;
564
- defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, G extends Checkpoint<string>, H extends Checkpoint<string>>(blocks: readonly [
565
- StartMarker,
566
- Block<S, B>,
567
- Block<B, C>,
568
- Block<C, D>,
569
- Block<D, E>,
570
- Block<E, F>,
571
- Block<F, G>,
572
- Block<G, H>,
573
- EndMarker
574
- ]): Flow<H>;
575
- defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, G extends Checkpoint<string>, H extends Checkpoint<string>, I extends Checkpoint<string>>(blocks: readonly [
576
- StartMarker,
577
- Block<S, B>,
578
- Block<B, C>,
579
- Block<C, D>,
580
- Block<D, E>,
581
- Block<E, F>,
582
- Block<F, G>,
583
- Block<G, H>,
584
- Block<H, I>,
585
- EndMarker
586
- ]): Flow<I>;
587
- defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, G extends Checkpoint<string>, H extends Checkpoint<string>, I extends Checkpoint<string>, J extends Checkpoint<string>>(blocks: readonly [
588
- StartMarker,
589
- Block<S, B>,
590
- Block<B, C>,
591
- Block<C, D>,
592
- Block<D, E>,
593
- Block<E, F>,
594
- Block<F, G>,
595
- Block<G, H>,
596
- Block<H, I>,
597
- Block<I, J>,
598
- EndMarker
599
- ]): Flow<J>;
600
- defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, G extends Checkpoint<string>, H extends Checkpoint<string>, I extends Checkpoint<string>, J extends Checkpoint<string>, K extends Checkpoint<string>>(blocks: readonly [
601
- StartMarker,
602
- Block<S, B>,
603
- Block<B, C>,
604
- Block<C, D>,
605
- Block<D, E>,
606
- Block<E, F>,
607
- Block<F, G>,
608
- Block<G, H>,
609
- Block<H, I>,
610
- Block<I, J>,
611
- Block<J, K>,
612
- EndMarker
613
- ]): Flow<K>;
614
- defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, G extends Checkpoint<string>, H extends Checkpoint<string>, I extends Checkpoint<string>, J extends Checkpoint<string>, K extends Checkpoint<string>, L extends Checkpoint<string>>(blocks: readonly [
615
- StartMarker,
616
- Block<S, B>,
617
- Block<B, C>,
618
- Block<C, D>,
619
- Block<D, E>,
620
- Block<E, F>,
621
- Block<F, G>,
622
- Block<G, H>,
623
- Block<H, I>,
624
- Block<I, J>,
625
- Block<J, K>,
626
- Block<K, L>,
627
- EndMarker
628
- ]): Flow<L>;
629
- defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, G extends Checkpoint<string>, H extends Checkpoint<string>, I extends Checkpoint<string>, J extends Checkpoint<string>, K extends Checkpoint<string>, L extends Checkpoint<string>, M extends Checkpoint<string>>(blocks: readonly [
630
- StartMarker,
631
- Block<S, B>,
632
- Block<B, C>,
633
- Block<C, D>,
634
- Block<D, E>,
635
- Block<E, F>,
636
- Block<F, G>,
637
- Block<G, H>,
638
- Block<H, I>,
639
- Block<I, J>,
640
- Block<J, K>,
641
- Block<K, L>,
642
- Block<L, M>,
643
- EndMarker
644
- ]): Flow<M>;
645
- defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, G extends Checkpoint<string>, H extends Checkpoint<string>, I extends Checkpoint<string>, J extends Checkpoint<string>, K extends Checkpoint<string>, L extends Checkpoint<string>, M extends Checkpoint<string>, N extends Checkpoint<string>>(blocks: readonly [
646
- StartMarker,
647
- Block<S, B>,
648
- Block<B, C>,
649
- Block<C, D>,
650
- Block<D, E>,
651
- Block<E, F>,
652
- Block<F, G>,
653
- Block<G, H>,
654
- Block<H, I>,
655
- Block<I, J>,
656
- Block<J, K>,
657
- Block<K, L>,
658
- Block<L, M>,
659
- Block<M, N>,
660
- EndMarker
661
- ]): Flow<N>;
662
- defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, G extends Checkpoint<string>, H extends Checkpoint<string>, I extends Checkpoint<string>, J extends Checkpoint<string>, K extends Checkpoint<string>, L extends Checkpoint<string>, M extends Checkpoint<string>, N extends Checkpoint<string>, O extends Checkpoint<string>>(blocks: readonly [
663
- StartMarker,
664
- Block<S, B>,
665
- Block<B, C>,
666
- Block<C, D>,
667
- Block<D, E>,
668
- Block<E, F>,
669
- Block<F, G>,
670
- Block<G, H>,
671
- Block<H, I>,
672
- Block<I, J>,
673
- Block<J, K>,
674
- Block<K, L>,
675
- Block<L, M>,
676
- Block<M, N>,
677
- Block<N, O>,
678
- EndMarker
679
- ]): Flow<O>;
680
- /**
681
- * Fluent, kind-checked alternative to `defineFlow([start, ...blocks, end])` -
682
- * see {@link MapBuilder}. Real, direct request this responds to: pia/zsign's
683
- * own agents kept hand-editing/hand-composing Blocks into ad hoc shapes
684
- * ("locks" convention tried, still got broken) - `map()` forces every step
685
- * through this Engine's own `define*Block` factories (checked by the same
686
- * `__waygraphKind`/`__waygraphSalt` runtime markers `graph.ts`/`map-check.ts`
687
- * already trust), so a hand-rolled plain-object Block can never silently
688
- * pass as a real navigation/assertion/method step. `homeOrigin`, if given,
689
- * also gates `.gotoPage()`/`.gotoExternal()` against each Nav/Page Block's
690
- * own static `url` (skipped, honestly, for click-based/dynamic nav - not
691
- * statically checkable, same limitation `map-check.ts` already documents).
692
- * @example
693
- * const flow = engine.map({ homeOrigin: "https://app.example.com" })
694
- * .start()
695
- * .gotoPage(NavHomeBlock)
696
- * .assert(AssertHelloBlock)
697
- * .gotoExternal(NavMailpitBlock)
698
- * .end();
699
- */
700
- map(options?: MapBuilderOptions): MapBuilder<S>;
701
- }
702
- export interface MapBuilderOptions {
703
- /**
704
- * This project's own origin (e.g. `"https://app.example.com"`) - enables
705
- * `.gotoPage()`/`.gotoExternal()`'s origin check against a Block's static
706
- * `url`. Omit to skip that check entirely (still fully kind-checked either
707
- * way; only the internal/external origin distinction is opt-in).
708
- */
709
- homeOrigin?: string;
710
- }
711
- /**
712
- * Fluent builder over {@link Engine.defineFlow} - see `map()`'s own doc
713
- * comment for why this exists. Each step method is scoped to exactly the
714
- * Block kind its name promises, checked at RUNTIME against the
715
- * `__waygraphKind`/`__waygraphSalt` markers `defineNavBlock`/`definePageBlock`/
716
- * `defineAssertBlock`/`defineMethodBlock`/`defineEffectBlock` already set
717
- * (the same markers `graph.ts` and `map-check.ts` trust) - not just a type
718
- * hint, since a hand-rolled object can satisfy the TypeScript `Block<In,Out>`
719
- * shape without ever going through a real factory. Each call also
720
- * typechecks the accumulated Checkpoint chain exactly like `defineFlow`'s
721
- * own tuple overloads do (a step's `In` must equal the previous step's
722
- * `Out`) - this is what "no teleporting" means: there is no method on this
723
- * builder that can skip from one Checkpoint to an unrelated one without a
724
- * real, kind-correct Block in between.
725
- */
726
- export declare class MapBuilder<Out extends Checkpoint<string>> {
727
- private readonly engine;
728
- private readonly steps;
729
- private readonly homeOrigin;
730
- private readonly ff;
731
- private constructor();
732
- /** @internal - use `engine.map()` or the standalone `map()` export. */
733
- static begin(engine: Engine, homeOrigin: string | undefined): MapBuilder<S>;
734
- /** Readable chain-opener - mirrors `defineFlow`'s leading `start` sentinel. Returns `this` unchanged; entirely optional. */
735
- start(): MapBuilder<Out>;
736
- /**
737
- * Open a fast-forward window - steps until {@link ffEnd} collapse into one
738
- * opaque `fastForwardComposeBlock` (blitz theater). Kind/salt checks still
739
- * run on each inner step as it is added.
740
- */
741
- ffStart(name?: string): MapBuilder<Out>;
742
- /**
743
- * Close the current fast-forward window and append one FFCompose step.
744
- * Throws if empty or if {@link ffStart} was never opened.
745
- */
746
- ffEnd(): MapBuilder<Out>;
747
- private appendStep;
748
- /**
749
- * Appends an internal navigation/page-arrival step - requires a Block from
750
- * `defineNavBlock`/`defineMemNavBlock`/`definePageBlock`. Throws if given
751
- * anything else, including a `homeOrigin`-mismatched static `url`.
752
- * Compile-time: {@link NavBlock} or {@link PageBlock} only (not Assert/Method).
753
- */
754
- gotoPage<NextOut extends Checkpoint<string>>(block: (NavBlock<NextOut> | PageBlock<NextOut>) & {
755
- name: string;
756
- }): MapBuilder<NextOut>;
757
- /**
758
- * Appends a genuinely cross-origin navigation step (e.g. `(external)/`
759
- * Blocks in the Waygraph Map convention: mailpit, maildrop.cc) - same
760
- * kind requirement as {@link gotoPage}, plus the inverse `homeOrigin` check.
761
- * Compile-time: {@link NavBlock} or {@link PageBlock} only.
762
- */
763
- gotoExternal<NextOut extends Checkpoint<string>>(block: (NavBlock<NextOut> | PageBlock<NextOut>) & {
764
- name: string;
765
- }): MapBuilder<NextOut>;
766
- /**
767
- * Appends a self-loop verification step - requires a Block from
768
- * `defineAssertBlock`. Compile-time: {@link AssertBlock} only (not `.method()`).
769
- */
770
- assert(block: AssertBlock<Out> & {
771
- name: string;
772
- }): MapBuilder<Out>;
773
- /**
774
- * Appends a non-navigating page action (submit, upload, add/remove an
775
- * instance) - requires a Block from `defineMethodBlock`/`defineActionBlock`/
776
- * `defineEffectBlock`/`defineMemEffectBlock`.
777
- * Compile-time: {@link MethodBlock} or {@link EffectBlock} only - Assert
778
- * Blocks must use `.assert()`. Runtime also rejects `__waygraphKind =
779
- * "assert"` (those still carry method salt from the factory internals).
780
- */
781
- method<NextOut extends Checkpoint<string>>(block: (MethodBlock<Out, NextOut> | EffectBlock<Out, NextOut>) & {
782
- name: string;
783
- }): MapBuilder<NextOut>;
784
- /**
785
- * Finalizes this chain into a real, runnable {@link Flow} - same object
786
- * `defineFlow` returns, so `withBlockVerify`/`modBlockVerify` and every
787
- * `withTitle`/`withHighlightFixtures`/etc. decorator still apply exactly
788
- * as they do today; this builder only changes how the chain is assembled,
789
- * never what it produces. Throws if no step was ever added - an empty map
790
- * isn't a flow.
791
- */
792
- end(): Flow<Out>;
793
- }
794
- /**
795
- * Standalone convenience for `new Engine(config).map(options)` - use this
796
- * when a call site doesn't otherwise need its own `Engine` instance (no
797
- * shared `headless`/`slowMo`/`layouts` across several flows).
798
- * @example const flow = map({ homeOrigin: "https://app.example.com" }).gotoPage(NavHomeBlock).end();
799
- */
800
- export declare function map(options?: MapBuilderOptions & EngineConfig): MapBuilder<S>;
801
- /**
802
- * Attaches routing to `block` without touching its act/observe/resolve/verify -
803
- * `routes` must cover every tag of `block`'s output (a missing route is a compile
804
- * error), and the returned Block runs exactly as `block` always did, just followed
805
- * by whichever next Block the resolved tag routes to. `null` for a tag means that
806
- * tag is terminal - the run stops there, same as a Block with no routing at all.
807
- * A route pointing back at `block` itself is a self-loop; see `runGraph`'s
808
- * `maxSteps` for its safety net. The return type is deliberately `Block<In, any>` -
809
- * a route can lead anywhere, including back through itself, so there is no fixed
810
- * Out type to express; state the expected outcomes to `runGraph` via `terminals`
811
- * instead.
812
- */
813
- export declare function branch<In extends Checkpoint<string>, Out extends Checkpoint<string>>(block: Block<In, Out>, routes: {
814
- [K in Out["__state"]]: Block<Extract<Out, Checkpoint<K>>, any> | null;
815
- }): DefinedBlock<In, any>;
816
- /**
817
- * Builds a Block with `withVerify`/`modVerify`/`modVerifyAll` attached as
818
- * methods - `LoginBlock.withVerify([...])`, discoverable by typing
819
- * `LoginBlock.` in an editor, not just callable as `withVerify(LoginBlock, ...)`
820
- * if you already know it exists. A raw object literal is still a perfectly
821
- * valid Block (every test fixture in this codebase is one) - it just won't have
822
- * these attached. The methods are thin: they delegate to the identically-named
823
- * free functions, which remain the one place this logic actually lives.
824
- */
825
- export declare function defineBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>>(base: {
826
- name: string;
827
- description?: Block<In, Out>["description"];
828
- instruction: Omit<Instruction<In, Out, any>, "act"> & {
829
- act(page: ActionPage, input: In, mem: MemPage): Promise<void>;
830
- };
831
- next?: Block<In, Out>["next"];
832
- requires?: Block<In, Out>["requires"];
833
- routes?: Block<In, Out>["routes"];
834
- instanceOptions?: Block<In, Out>["instanceOptions"];
835
- }): DefinedBlock<In, Out>;
836
- /**
837
- * A Block whose only possible action is navigating to a URL - built via
838
- * {@link defineNavBlock}, never with a hand-authored `act()`. Extends `Block`
839
- * (same shape `DefinedBlock`/`ComposedBlock` already use), so it drops into
840
- * `defineFlow([...])`, `connect()`, `composeBlock()` exactly like any other
841
- * Block - no special-casing needed anywhere that only expects a `Block`.
842
- * See `openspec/specs/nav-block-and-check/spec.md` for why this exists.
843
- *
844
- * `__wgFactory` is a TypeScript-only brand so {@link MapBuilder.gotoPage} /
845
- * {@link MapBuilder.gotoExternal} can reject Assert/Method Blocks at compile
846
- * time (runtime still checks `__waygraphKind`). Decorate methods are re-stated
847
- * (via Omit) so `.stubBefore()` / `.withVerify()` keep the brand.
848
- */
849
- export type NavBlock<Out extends Checkpoint<string>> = Omit<DefinedBlock<Checkpoint<string>, Out>, "withVerify" | "modVerify" | "modVerifyAll" | "stubBefore" | "stubAfter" | "stubOnError"> & {
850
- readonly __wgFactory?: "nav";
851
- withVerify(verify: Trait[] | ((out: Out) => Trait[])): NavBlock<Out>;
852
- modVerify(nameOrIndex: string | number, newCheck: Trait["check"] | Trait): NavBlock<Out>;
853
- modVerifyAll(patches: Record<string, Trait["check"] | Trait>): NavBlock<Out>;
854
- stubBefore(stub: import("./highlights.js").HighlightStubPhase): NavBlock<Out>;
855
- stubBefore(stub: import("./highlights.js").StubLifecycleFn<Out>): NavBlock<Out>;
856
- stubBefore(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): NavBlock<Out>;
857
- stubAfter(stub: import("./highlights.js").HighlightStubPhase): NavBlock<Out>;
858
- stubAfter(stub: import("./highlights.js").StubLifecycleFn<Out>): NavBlock<Out>;
859
- stubAfter(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): NavBlock<Out>;
860
- stubOnError(stub: import("./highlights.js").HighlightStubPhase): NavBlock<Out>;
861
- stubOnError(stub: import("./highlights.js").StubLifecycleFn<Out>): NavBlock<Out>;
862
- stubOnError(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): NavBlock<Out>;
863
- };
864
- /**
865
- * `defineNavBlock`'s own options - exactly one of `url`/`click` required,
866
- * enforced at compile time (giving both, or neither, is a type error - see
867
- * `typecheck/nav-block.types.ts`). `checkpoint` is the tag this NavBlock
868
- * resolves to once navigation completes - a NavBlock never branches on
869
- * observed evidence the way a regular Block can, so there's no `resolve`
870
- * logic to author; it always just declares "arrived."
871
- */
872
- export type NavBlockOptions<Out extends Checkpoint<string>> = {
873
- name: string;
874
- description?: string;
875
- checkpoint: Out["__state"];
876
- requires?: readonly MemKey<any>[];
877
- verify?: Trait[] | ((out: Out) => Trait[]);
878
- stubBefore?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
879
- stubAfter?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
880
- stubOnError?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
881
- /** @deprecated Prefer stubAfter. */
882
- highlights?: readonly WaygraphHighlight[] | ((out: Out) => readonly WaygraphHighlight[]);
883
- /**
884
- * Live per-instance menu rows for `waygraph auto` (see {@link Block.instanceOptions}).
885
- * When set, the explore menu lists one row per option instead of a single
886
- * generic nav edge - used by {@link defineMemNavBlock}.
887
- */
888
- instanceOptions?: Block<Checkpoint<string>, Out>["instanceOptions"];
889
- } & ({
890
- /**
891
- * Direct URL navigation. Accepts a plain string or a function
892
- * `(mem) => string` for parameterized routes (e.g.
893
- * `/dashboard/requests/:id` with a real id read from mem). The
894
- * escape hatch for the few real cases with no click path to get
895
- * there (a deep link from an email, a public share link) - not for
896
- * skipping past navigation an agent didn't feel like finding. Prefer
897
- * `click` whenever the target app actually has one.
898
- */
899
- url: string | ((mem: MemPage) => string);
900
- click?: never;
901
- } | {
902
- /**
903
- * Navigates by clicking a real element already on the page (a nav
904
- * link, a sidebar item) instead of teleporting straight to a URL -
905
- * the opinionated default. Accepts a Playwright selector string, or
906
- * a function `(mem) => selector` for a target that depends on mem
907
- * (e.g. clicking the row for a specific id). Verifying arrival is
908
- * `verify`'s job (typically `Trait.url(...)`), same as a `url`-based
909
- * NavBlock - the click itself is only ever awaited for its own
910
- * completion, not for whatever navigation it triggers.
911
- */
912
- click: string | ((mem: MemPage) => string);
913
- url?: never;
914
- });
915
- /**
916
- * Builds a {@link NavBlock}. The generated `act()` is always exactly
917
- * `page.goto(url)` or `page.locator(click).click()` - never anything else,
918
- * never author-supplied. Callers get a real `Page` here internally (the one
919
- * legitimate place navigation belongs); `defineBlock`'s own `ActionPage`
920
- * narrowing is what discourages navigation everywhere else, and never
921
- * applies to this generated function since no author ever writes it by
922
- * hand.
923
- * @example defineNavBlock({ name: "nav-web-login", checkpoint: "LoginForm", url: "/login" })
924
- * @example defineNavBlock({ name: "nav-web-request-detail", checkpoint: "RequestDetail", url: (mem) => `/dashboard/requests/${mem.get(RequestId.key)}` })
925
- * @example defineNavClickBlock({ name: "nav-web-documents", checkpoint: "DocumentsList", click: "nav >> text=Documents" })
926
- * For click-nav in app code prefer {@link defineNavClickBlock} (url stays here).
927
- */
928
- export declare function defineNavBlock<Out extends Checkpoint<string>>(options: NavBlockOptions<Out>): NavBlock<Out>;
929
- /**
930
- * Click-only {@link NavBlock}. Same runtime as {@link defineNavBlock} with
931
- * `click` - use this in app code so you never wonder whether a Nav is url or
932
- * click (no `__waygraphNavClick` sniffing at author time). Prefer
933
- * {@link defineNavBlock} only for `url` / `goto` deep links.
934
- */
935
- export type NavClickBlockOptions<Out extends Checkpoint<string>> = {
936
- name: string;
937
- description?: string;
938
- checkpoint: Out["__state"];
939
- click: string | ((mem: MemPage) => string);
940
- requires?: readonly MemKey<any>[];
941
- verify?: Trait[] | ((out: Out) => Trait[]);
942
- stubBefore?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
943
- stubAfter?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
944
- stubOnError?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
945
- /** @deprecated Prefer stubAfter. */
946
- highlights?: readonly WaygraphHighlight[] | ((out: Out) => readonly WaygraphHighlight[]);
947
- instanceOptions?: Block<Checkpoint<string>, Out>["instanceOptions"];
948
- };
949
- export type NavClickBlock<Out extends Checkpoint<string>> = NavBlock<Out>;
950
- /** @see {@link NavClickBlockOptions} */
951
- export declare function defineNavClickBlock<Out extends Checkpoint<string>>(options: NavClickBlockOptions<Out>): NavClickBlock<Out>;
952
- /**
953
- * TypeScript salt over {@link defineBlock} for one-shot non-nav app steps
954
- * (submit, upload, finish, logout) - **methods** on a page. Same runtime Block;
955
- * marks `__waygraphSalt = "method"` so auto/docs can tell Method from bare
956
- * {@link defineBlock}. Does not force `instanceOptions` - use
957
- * {@link defineEffectBlock} when the auto menu should spawn one row per live
958
- * DOM instance. Prefer hanging methods off a {@link definePageBlock} hub.
959
- */
960
- export type MethodBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>> = Omit<DefinedBlock<In, Out>, "withVerify" | "modVerify" | "modVerifyAll" | "stubBefore" | "stubAfter" | "stubOnError"> & {
961
- readonly __wgFactory?: "method";
962
- withVerify(verify: Trait[] | ((out: Out) => Trait[])): MethodBlock<In, Out>;
963
- modVerify(nameOrIndex: string | number, newCheck: Trait["check"] | Trait): MethodBlock<In, Out>;
964
- modVerifyAll(patches: Record<string, Trait["check"] | Trait>): MethodBlock<In, Out>;
965
- stubBefore(stub: import("./highlights.js").HighlightStubPhase): MethodBlock<In, Out>;
966
- stubBefore(stub: import("./highlights.js").StubLifecycleFn<Out>): MethodBlock<In, Out>;
967
- stubBefore(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): MethodBlock<In, Out>;
968
- stubAfter(stub: import("./highlights.js").HighlightStubPhase): MethodBlock<In, Out>;
969
- stubAfter(stub: import("./highlights.js").StubLifecycleFn<Out>): MethodBlock<In, Out>;
970
- stubAfter(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): MethodBlock<In, Out>;
971
- stubOnError(stub: import("./highlights.js").HighlightStubPhase): MethodBlock<In, Out>;
972
- stubOnError(stub: import("./highlights.js").StubLifecycleFn<Out>): MethodBlock<In, Out>;
973
- stubOnError(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): MethodBlock<In, Out>;
974
- };
975
- /** @deprecated Prefer {@link MethodBlock} - same type; Action was the 0.7 name. */
976
- export type ActionBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>> = MethodBlock<In, Out>;
977
- export declare function defineMethodBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>>(base: Parameters<typeof defineBlock<In, Out>>[0]): MethodBlock<In, Out>;
978
- /** @deprecated Prefer {@link defineMethodBlock} - identical helper (0.7 name). */
979
- export declare const defineActionBlock: typeof defineMethodBlock;
980
- export interface AssertBlockOptions<Out extends Checkpoint<string> = Checkpoint<string>> {
981
- name: string;
982
- description?: string;
983
- /** Self-loop target - the Checkpoint this assertion runs on and returns to. */
984
- checkpoint: Out["__state"];
985
- /**
986
- * Shorthand for the wait-then-assert shape found repeatedly in hand-written
987
- * assertion Blocks: waits for a heading with this accessible name before
988
- * `verify` runs. Omit for a pure "assert whatever is already on the page"
989
- * check with no wait. Not a general `act()` override - a Block that needs
990
- * more than this stays a plain `defineMethodBlock`.
991
- */
992
- waitForHeading?: string;
993
- verify: Trait[] | ((out: Out) => Trait[]);
994
- /** Mem keys this assertion's `verify` reads (e.g. a mem-aware Trait) - checked by preflight before the flow runs. */
995
- requires?: readonly MemKey<any>[];
996
- stubBefore?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
997
- stubAfter?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
998
- stubOnError?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
999
- /** Multi-step yap slides (demo only) - same field `Instruction.slides` exposes elsewhere. */
1000
- slides?: import("./highlights.js").WaygraphSlidesOrFn<Out>;
1001
- }
1002
- /**
1003
- * Sugar for a self-loop-only "pure assertion" Block: no hand-written
1004
- * `act`/`resolve`, just a name, a `checkpoint` tag, and a `verify` array.
1005
- * `resolve` is generated from the one `checkpoint` field given, closing the
1006
- * copy/rename footgun where a hand-restated checkpoint string can silently
1007
- * drift wrong across a copy/pasted file. Mirrors `definePageBlock`'s own
1008
- * construction pattern (flat options in, `defineBlock` built internally).
1009
- * See openspec/changes/waygraph-agent-skill-hardening/spec.md.
1010
- * @example
1011
- * defineAssertBlock({
1012
- * name: "assert-raw-materials-table",
1013
- * checkpoint: "RawMaterials",
1014
- * waitForHeading: "Raw Materials & Lead Times",
1015
- * verify: [Trait.visible(RawMaterialsSel.reorderBadge)],
1016
- * });
1017
- */
1018
- export declare function defineAssertBlock<Out extends Checkpoint<string> = Checkpoint<string>>(options: AssertBlockOptions<Out>): AssertBlock<Out>;
1019
- /**
1020
- * Self-loop verify Block from {@link defineAssertBlock}. Branded for
1021
- * {@link MapBuilder.assert} (not accepted by `.method()`).
1022
- */
1023
- export type AssertBlock<Out extends Checkpoint<string>> = Omit<DefinedBlock<Out, Out>, "withVerify" | "modVerify" | "modVerifyAll" | "stubBefore" | "stubAfter" | "stubOnError"> & {
1024
- readonly __wgFactory?: "assert";
1025
- withVerify(verify: Trait[] | ((out: Out) => Trait[])): AssertBlock<Out>;
1026
- modVerify(nameOrIndex: string | number, newCheck: Trait["check"] | Trait): AssertBlock<Out>;
1027
- modVerifyAll(patches: Record<string, Trait["check"] | Trait>): AssertBlock<Out>;
1028
- stubBefore(stub: import("./highlights.js").HighlightStubPhase): AssertBlock<Out>;
1029
- stubBefore(stub: import("./highlights.js").StubLifecycleFn<Out>): AssertBlock<Out>;
1030
- stubBefore(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): AssertBlock<Out>;
1031
- stubAfter(stub: import("./highlights.js").HighlightStubPhase): AssertBlock<Out>;
1032
- stubAfter(stub: import("./highlights.js").StubLifecycleFn<Out>): AssertBlock<Out>;
1033
- stubAfter(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): AssertBlock<Out>;
1034
- stubOnError(stub: import("./highlights.js").HighlightStubPhase): AssertBlock<Out>;
1035
- stubOnError(stub: import("./highlights.js").StubLifecycleFn<Out>): AssertBlock<Out>;
1036
- stubOnError(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): AssertBlock<Out>;
1037
- };
1038
- /** One Block (or lazy factory) registered on a {@link PageBlock}. */
1039
- export type PageMethodEntry = DefinedBlock<any, any> | (() => DefinedBlock<any, any>);
1040
- export type PageBlockOptions<Out extends Checkpoint<string>> = {
1041
- name: string;
1042
- description?: string;
1043
- /** Checkpoint tag for this screen (hub). */
1044
- checkpoint: Out["__state"];
1045
- verify?: Trait[] | ((out: Out) => Trait[]);
1046
- stubBefore?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
1047
- stubAfter?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
1048
- stubOnError?: import("./highlights.js").HighlightStubPhaseOrFn<Out>;
1049
- /** @deprecated Prefer stubAfter. */
1050
- highlights?: readonly WaygraphHighlight[] | ((out: Out) => readonly WaygraphHighlight[]);
1051
- requires?: readonly MemKey<any>[];
1052
- /**
1053
- * Methods available on this page. Array, or a record of Blocks / `() => Block`
1054
- * factories (lazy so the page object can colocate method definitions).
1055
- */
1056
- methods?: readonly PageMethodEntry[] | Record<string, PageMethodEntry>;
1057
- /**
1058
- * Optional deep-link onto this page (`goto`). XOR with `click`. Omit when the
1059
- * hub is arrival-only (previous Block already resolved here).
1060
- */
1061
- url?: string | ((mem: MemPage) => string);
1062
- /** Optional click-nav onto this page. XOR with `url`. */
1063
- click?: string | ((mem: MemPage) => string);
1064
- instanceOptions?: Block<Checkpoint<string>, Out>["instanceOptions"];
1065
- };
1066
- /**
1067
- * Page hub Block: one Checkpoint = this screen; methods hang off the hub for
1068
- * readability / auto grouping. Runtime is still a Block (`__waygraphKind = "page"`).
1069
- * `act` is goto/click when `url`/`click` is set, otherwise a no-op (already here).
1070
- * Child methods stay ordinary Blocks for graph edges - the page only registers them.
1071
- * Branded `__wgFactory: "page"` (not `"nav"`) - both are accepted by
1072
- * {@link MapBuilder.gotoPage} / {@link MapBuilder.gotoExternal}.
1073
- */
1074
- export type PageBlock<Out extends Checkpoint<string>> = Omit<DefinedBlock<Checkpoint<string>, Out>, "withVerify" | "modVerify" | "modVerifyAll" | "stubBefore" | "stubAfter" | "stubOnError"> & {
1075
- readonly __wgFactory?: "page";
1076
- /** Resolved method Blocks registered at define time. */
1077
- readonly methods: readonly DefinedBlock<any, any>[];
1078
- withVerify(verify: Trait[] | ((out: Out) => Trait[])): PageBlock<Out>;
1079
- modVerify(nameOrIndex: string | number, newCheck: Trait["check"] | Trait): PageBlock<Out>;
1080
- modVerifyAll(patches: Record<string, Trait["check"] | Trait>): PageBlock<Out>;
1081
- stubBefore(stub: import("./highlights.js").HighlightStubPhase): PageBlock<Out>;
1082
- stubBefore(stub: import("./highlights.js").StubLifecycleFn<Out>): PageBlock<Out>;
1083
- stubBefore(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): PageBlock<Out>;
1084
- stubAfter(stub: import("./highlights.js").HighlightStubPhase): PageBlock<Out>;
1085
- stubAfter(stub: import("./highlights.js").StubLifecycleFn<Out>): PageBlock<Out>;
1086
- stubAfter(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): PageBlock<Out>;
1087
- stubOnError(stub: import("./highlights.js").HighlightStubPhase): PageBlock<Out>;
1088
- stubOnError(stub: import("./highlights.js").StubLifecycleFn<Out>): PageBlock<Out>;
1089
- stubOnError(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): PageBlock<Out>;
1090
- };
1091
- /** @see {@link PageBlockOptions} */
1092
- export declare function definePageBlock<Out extends Checkpoint<string>>(options: PageBlockOptions<Out>): PageBlock<Out>;
1093
- /**
1094
- * TypeScript salt over {@link defineBlock}: same runtime Block, but the type
1095
- * forces `requires` + `instanceOptions` so `waygraph auto` can list one menu
1096
- * row per live instance (add/remove/toggle). Not a separate engine concept -
1097
- * everything is still a Block.
1098
- */
1099
- export type EffectBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>> = Omit<DefinedBlock<In, Out>, "withVerify" | "modVerify" | "modVerifyAll" | "stubBefore" | "stubAfter" | "stubOnError"> & {
1100
- readonly __wgFactory?: "effect";
1101
- requires: readonly MemKey<any>[];
1102
- instanceOptions: NonNullable<Block<In, Out>["instanceOptions"]>;
1103
- withVerify(verify: Trait[] | ((out: Out) => Trait[])): EffectBlock<In, Out>;
1104
- modVerify(nameOrIndex: string | number, newCheck: Trait["check"] | Trait): EffectBlock<In, Out>;
1105
- modVerifyAll(patches: Record<string, Trait["check"] | Trait>): EffectBlock<In, Out>;
1106
- stubBefore(stub: import("./highlights.js").HighlightStubPhase): EffectBlock<In, Out>;
1107
- stubBefore(stub: import("./highlights.js").StubLifecycleFn<Out>): EffectBlock<In, Out>;
1108
- stubBefore(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): EffectBlock<In, Out>;
1109
- stubAfter(stub: import("./highlights.js").HighlightStubPhase): EffectBlock<In, Out>;
1110
- stubAfter(stub: import("./highlights.js").StubLifecycleFn<Out>): EffectBlock<In, Out>;
1111
- stubAfter(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): EffectBlock<In, Out>;
1112
- stubOnError(stub: import("./highlights.js").HighlightStubPhase): EffectBlock<In, Out>;
1113
- stubOnError(stub: import("./highlights.js").StubLifecycleFn<Out>): EffectBlock<In, Out>;
1114
- stubOnError(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): EffectBlock<In, Out>;
1115
- };
1116
- /** @deprecated Prefer {@link EffectBlock} - same type; Mem* was an early name. */
1117
- export type MemEffectBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>> = EffectBlock<In, Out>;
1118
- /**
1119
- * TypeScript salt over {@link defineNavBlock}: mem-picked destination
1120
- * (which product row / which request id). Still a NavBlock / Block at runtime.
1121
- */
1122
- export type MemNavBlock<Out extends Checkpoint<string>> = NavBlock<Out> & {
1123
- requires: readonly MemKey<any>[];
1124
- instanceOptions: NonNullable<Block<Checkpoint<string>, Out>["instanceOptions"]>;
1125
- };
1126
- /**
1127
- * {@link defineBlock} constrained for effect-style instance menus: `requires` +
1128
- * `instanceOptions` are mandatory. Prefer this when the Block adds / removes /
1129
- * toggles a live instance rather than navigating. Runtime result is an ordinary
1130
- * Block - the helper is TypeScript salt only.
1131
- */
1132
- export declare function defineEffectBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>>(base: Parameters<typeof defineBlock<In, Out>>[0] & {
1133
- requires: readonly MemKey<any>[];
1134
- instanceOptions: NonNullable<Block<In, Out>["instanceOptions"]>;
1135
- }): EffectBlock<In, Out>;
1136
- /** @deprecated Prefer {@link defineEffectBlock} - identical helper. */
1137
- export declare const defineMemEffectBlock: typeof defineEffectBlock;
1138
- /**
1139
- * {@link defineNavBlock} constrained to mem-picked navigation: requires +
1140
- * instanceOptions mandatory, and `click`/`url` should depend on that mem
1141
- * (typically `click: (mem) => …`). Prefer this when the Block opens one of
1142
- * several live rows/items rather than a fixed nav target. Still a Block.
1143
- */
1144
- export declare function defineMemNavBlock<Out extends Checkpoint<string>>(options: NavBlockOptions<Out> & {
1145
- requires: readonly MemKey<any>[];
1146
- instanceOptions: NonNullable<Block<Checkpoint<string>, Out>["instanceOptions"]>;
1147
- }): MemNavBlock<Out>;
1148
- /**
1149
- * "Where am I" - reverse-matches a live `page` against every NavBlock /
1150
- * PageBlock in `library`, in order, returning the first one whose own `verify`
1151
- * Traits all pass (its `checkpoint`), or `null` if none match. Deliberately
1152
- * Nav/Page hubs only: each has exactly one fixed `checkpoint` and a `verify`
1153
- * list whose whole job is already "confirm arrival here," so it's a reliable
1154
- * fingerprint - a regular `defineBlock` can branch to different `Out` tags
1155
- * depending on runtime evidence, so its `verify` alone doesn't identify a
1156
- * single state the way a hub's does.
1157
- *
1158
- * The primitive `waygraph auto`'s discovered graph is *for* - the graph says
1159
- * what pages exist and how to reach them; `locate()` is how an autonomous
1160
- * run figures out which one it's actually looking at right now, without a
1161
- * human telling it.
1162
- *
1163
- * More than one NavBlock matching is a real possibility (two Checkpoints
1164
- * whose `verify` Traits are both satisfied by the same page) - `locate()`
1165
- * returns the first match rather than treating that as an error; resolving
1166
- * genuine ambiguity is a follow-up, not solved here.
1167
- * @example const here = await locate(page, [NavLoginBlock, NavDashboardBlock]);
1168
- */
1169
- export declare function locate(page: Page, library: readonly NavBlock<any>[], options?: {
1170
- timeoutMs?: number;
1171
- }): Promise<string | null>;
1172
- /**
1173
- * Returns a new Block that runs exactly like `block` - same act/observe/resolve,
1174
- * same routing - but with `verify` replaced. For giving one flow stricter (or
1175
- * looser, or just different) confirmation on a Block it didn't author, without
1176
- * that Block needing to have been written as a parameterized factory in
1177
- * anticipation. Same move as `branch()`: decorate an existing Block, don't ask
1178
- * every Block to pre-build its own override mechanism.
1179
- *
1180
- * Wrap the one Block you want to change, in place, wherever it already sits in
1181
- * a `defineFlow([...])` array - every other Block stays untouched:
1182
- * `defineFlow([start, LoginBlock, withVerify(AddToCartBlock, [...]), end])`.
1183
- * There is no separate positional array to keep in sync and no `null`
1184
- * placeholder for "skip this one" - `defineFlow` has no per-slot verify
1185
- * argument at all, only whatever `verify` each Block's own `instruction` (or
1186
- * a `withVerify(...)` wrapping it) carries with it.
1187
- */
1188
- export declare function withVerify<B extends Block<any, any>>(block: B, verify: Trait[] | ((out: any) => Trait[])): B;
1189
- type StubPhaseFn<Out extends Checkpoint<string>> = import("./highlights.js").HighlightStubPhaseOrFn<Out>;
1190
- /**
1191
- * Attach / chain a stubBefore lifecycle on a Block. Prefer
1192
- * `(ctx) => { ctx.ring(...); const x = ctx.mem?.get(Key); }`.
1193
- * @example FillUsernameBlock.stubBefore((ctx) => { ctx.ring("username", { ... }) })
1194
- */
1195
- export declare function withStubBefore<B extends Block<any, any>>(block: B, stub: StubPhaseFn<any>): B;
1196
- /** @see {@link withStubBefore} */
1197
- export declare function withStubAfter<B extends Block<any, any>>(block: B, stub: StubPhaseFn<any>): B;
1198
- /** @see {@link withStubBefore} */
1199
- export declare function withStubOnError<B extends Block<any, any>>(block: B, stub: StubPhaseFn<any>): B;
1200
- /**
1201
- * Replaces one Trait inside `block`'s verify list, leaving every other check
1202
- * untouched - the v1/v2/legacy-variant case, where one check needs a different
1203
- * selector or URL pattern but the rest of the Block is identical. Addressed by
1204
- * the Trait's own `name` (recommended - inserting or reordering other checks
1205
- * can't silently retarget it) or, if you genuinely know the position and don't
1206
- * need that safety, a numeric index. Either way a bad address fails loud
1207
- * immediately instead of quietly doing nothing. `newCheck` can be a bare
1208
- * `check` function (keeps the existing name) or a full `Trait` (to rename too).
1209
- * Only works on a flat `verify` array - a function-form `verify` (from
1210
- * `verify: (out) => Trait[]`) has no fixed list to address into, so this
1211
- * throws rather than guess; use `withVerify` to replace it wholesale instead.
1212
- */
1213
- export declare function modVerify<In extends Checkpoint<string>, Out extends Checkpoint<string>>(block: Block<In, Out>, nameOrIndex: string | number, newCheck: Trait["check"] | Trait): DefinedBlock<In, Out>;
1214
- /**
1215
- * `modVerify`, but for several named checks in one call - a v2 variant that
1216
- * differs in five ways from v1 shouldn't need five nested `modVerify(modVerify(
1217
- * modVerify(...)))` calls to track by eye. `patches` keys are plain strings (a
1218
- * Trait's `name`, not a `MemKey` instance), so an ordinary object literal is
1219
- * fine here - unlike `MemPage`, there's no identity-collision reason to need
1220
- * `[name, check]` pairs. Applies each patch in turn via `modVerify`, so the
1221
- * same fail-loud-on-unknown-name behavior holds for every entry.
1222
- */
1223
- export declare function modVerifyAll<In extends Checkpoint<string>, Out extends Checkpoint<string>>(block: Block<In, Out>, patches: Record<string, Trait["check"] | Trait>): DefinedBlock<In, Out>;
1224
- export {};
1
+ export * from "./engine/config.js";
2
+ export * from "./engine/run-graph.js";
3
+ export * from "./engine/flow.js";
4
+ export * from "./engine/core.js";
5
+ export * from "./engine/compose.js";
6
+ export * from "./engine/engine-class.js";
7
+ export * from "./engine/blocks/nav.js";
8
+ export * from "./engine/blocks/method.js";
9
+ export * from "./engine/blocks/page.js";
10
+ export * from "./engine/blocks/effect.js";
11
+ export * from "./engine/locate.js";