waygraph 0.14.0 → 0.15.1
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 +171 -21
- package/dist/cli.js +101 -5
- package/dist/engine.d.ts +161 -0
- package/dist/engine.js +230 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/map-check.d.ts +18 -0
- package/dist/map-check.js +140 -0
- package/dist/types.d.ts +21 -1
- package/dist/types.js +17 -1
- package/examples/routed-demo/.waygraph-auto/4d4902b7.log +0 -0
- package/examples/routed-demo/.waygraph-auto/51ef462e.log +0 -0
- package/examples/routed-demo/.waygraph-auto/71527ad9.log +0 -0
- package/examples/routed-demo/.waygraph-auto/ca16778e.log +0 -0
- package/examples/routed-demo/src/flows/routed-demo-map.flow.ts +39 -0
- package/examples/routed-demo/src/flows/routed-demo.flow.ts +5 -5
- package/examples/routed-demo/src/{routes/(base_app)/dashboard/methods → map/(app_base)/dashboard/_methods}/click-widget.block.ts +2 -2
- package/examples/routed-demo/src/{routes/(base_app)/dashboard/nav.block.ts → map/(app_base)/dashboard/_nav.block.ts} +2 -2
- package/examples/routed-demo/src/{routes/(base_app)/dashboard/page.block.ts → map/(app_base)/dashboard/_page.block.ts} +4 -4
- package/examples/routed-demo/src/{routes/(external)/docs/nav.block.ts → map/(external)/docs/_nav.block.ts} +2 -2
- package/examples/routed-demo/src/{routes/(external)/docs/page.block.ts → map/(external)/docs/_page.block.ts} +1 -1
- package/examples/routed-demo/test-results/.last-run.json +4 -0
- package/examples/routed-demo/tests/routed-demo-map.spec.ts +17 -0
- package/examples/saucedemo/.waygraph-auto/00d5336e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/083cc5b9.log +0 -0
- package/examples/saucedemo/.waygraph-auto/104751ef.log +0 -0
- package/examples/saucedemo/.waygraph-auto/175655f7.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2219f00b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/23e62571.log +0 -0
- package/examples/saucedemo/.waygraph-auto/42cc9113.log +0 -0
- package/examples/saucedemo/.waygraph-auto/439d30aa.log +0 -0
- package/examples/saucedemo/.waygraph-auto/4a85f496.log +0 -0
- package/examples/saucedemo/.waygraph-auto/5c0eef7f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/5d60da77.log +0 -0
- package/examples/saucedemo/.waygraph-auto/5fc3a33b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/61b6546d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/650ed066.log +0 -0
- package/examples/saucedemo/.waygraph-auto/6d9aaad3.log +0 -0
- package/examples/saucedemo/.waygraph-auto/78b21219.log +0 -0
- package/examples/saucedemo/.waygraph-auto/84d6d293.log +0 -0
- package/examples/saucedemo/.waygraph-auto/8a3eb088.log +0 -0
- package/examples/saucedemo/.waygraph-auto/a2dd30dd.log +0 -0
- package/examples/saucedemo/.waygraph-auto/a87bb3ee.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b81e074f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ba58abb7.log +0 -0
- package/examples/saucedemo/.waygraph-auto/bb9fd384.log +0 -0
- package/examples/saucedemo/.waygraph-auto/c02ea583.log +0 -0
- package/examples/saucedemo/.waygraph-auto/c2caf0a8.log +0 -0
- package/examples/saucedemo/.waygraph-auto/c3bdc714.log +0 -0
- package/examples/saucedemo/.waygraph-auto/cd68bbc4.log +0 -0
- package/examples/saucedemo/.waygraph-auto/cdc6db50.log +0 -0
- package/examples/saucedemo/.waygraph-auto/d01906a7.log +0 -0
- package/examples/saucedemo/.waygraph-auto/d9e5f8a4.log +0 -0
- package/examples/saucedemo/.waygraph-auto/dec3076e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/e1973991.log +0 -0
- package/examples/saucedemo/.waygraph-auto/e8306a4b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ec88b3ef.log +0 -0
- package/examples/saucedemo/.waygraph-auto/f34c9a26.log +0 -0
- package/examples/saucedemo/.waygraph-auto/f5525d53.log +0 -0
- package/examples/saucedemo/.waygraph-auto/f90035b3.log +0 -0
- package/examples/saucedemo/.waygraph-auto/fa687bc2.log +0 -0
- package/examples/saucedemo/.waygraph-auto/fb66fba0.log +0 -0
- package/examples/saucedemo/.waygraph-auto/fc48fd76.log +0 -0
- package/package.json +1 -1
- package/templates/scaffold/scripts/fixture-server.mjs +11 -2
- package/templates/scaffold/src/flows/example.flow.ts +8 -6
- package/templates/scaffold/src/flows/mail-verify.flow.ts +8 -8
- package/templates/scaffold/src/flows/shop.flow.ts +6 -6
- package/templates/scaffold/src/{routes/(external)/docs/nav.block.ts → map/(app_base)/docs/_nav.block.ts} +5 -6
- package/templates/scaffold/src/{routes/(external)/docs/page.block.ts → map/(app_base)/docs/_page.block.ts} +2 -2
- package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/add-item.effect.block.ts +3 -3
- package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/assert-hello.method.block.ts +1 -1
- package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/assert-item-added.method.block.ts +3 -3
- package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/clear-cart.method.block.ts +9 -3
- package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/remove-item.effect.block.ts +3 -3
- package/templates/scaffold/src/{blocks/demo-web/nav-home.block.ts → map/(app_base)/home/_nav.block.ts} +2 -2
- package/templates/scaffold/src/{blocks/demo-web/home.page.block.ts → map/(app_base)/home/_page.block.ts} +7 -7
- package/templates/scaffold/src/{blocks/demo-web → map/(app_base)/home}/nav-verification-link.block.ts +4 -4
- package/templates/scaffold/src/{blocks/demo-external/mailpit/methods → map/(external)/mailpit/_methods}/assert-email-content.method.block.ts +2 -2
- package/templates/scaffold/src/{blocks/demo-external/mailpit/methods → map/(external)/mailpit/_methods}/assert-email-received.method.block.ts +2 -2
- package/templates/scaffold/src/{blocks/demo-external/mailpit/methods → map/(external)/mailpit/_methods}/extract-email-link.method.block.ts +2 -2
- package/templates/scaffold/src/{blocks/demo-external/mailpit/methods → map/(external)/mailpit/_methods}/open-message.method.block.ts +2 -2
- package/templates/scaffold/src/{blocks/demo-external/mailpit/nav-mailpit-inbox.block.ts → map/(external)/mailpit/_nav.block.ts} +2 -2
- package/templates/scaffold/src/{blocks/demo-external/mailpit/mailpit-inbox.page.block.ts → map/(external)/mailpit/_page.block.ts} +5 -5
- package/templates/scaffold/src/states/demo.states.ts +2 -4
- package/templates/scaffold/tests/example.spec.ts +2 -2
- package/templates/scaffold/tests/mail-verify.spec.ts +1 -1
- package/templates/scaffold/src/blocks/SITE-MAP.md +0 -12
- package/templates/scaffold/src/blocks/demo-web/NAV.md +0 -29
- package/templates/scaffold/src/flows/routes-demo.flow.ts +0 -14
- /package/examples/routed-demo/src/{routes/(base_app)/dashboard/dashboard.sel.ts → map/(app_base)/dashboard/_sel.ts} +0 -0
- /package/examples/routed-demo/src/{routes/(external)/docs/docs.sel.ts → map/(external)/docs/_sel.ts} +0 -0
- /package/templates/scaffold/src/{routes/(external)/docs/docs.sel.ts → map/(app_base)/docs/_sel.ts} +0 -0
- /package/templates/scaffold/src/{blocks/demo-web → map/(app_base)/docs}/docs.html +0 -0
- /package/templates/scaffold/src/{blocks/demo-web/demo-sel.ts → map/(app_base)/home/_sel.ts} +0 -0
- /package/templates/scaffold/src/{blocks/demo-web → map/(app_base)/home}/home.html +0 -0
- /package/templates/scaffold/src/{blocks/demo-external/mailpit/mailpit-sel.ts → map/(external)/mailpit/_sel.ts} +0 -0
package/README.md
CHANGED
|
@@ -138,6 +138,84 @@ structurally incapable of touching the browser or shared memory. `verify` runs o
|
|
|
138
138
|
its own panel row; former FF inners keep blitz pacing so wall-clock stays comparable.
|
|
139
139
|
`--ff-expand` is the older expand-only flag (same flatten).
|
|
140
140
|
|
|
141
|
+
### Cross-cutting Layouts (`defineLayout`)
|
|
142
|
+
|
|
143
|
+
Real, direct request this responds to: persistent chrome (a sidebar, say) that's supposed
|
|
144
|
+
to stay on screen across most of an app shouldn't need its own `Trait` copy-pasted into
|
|
145
|
+
every single Checkpoint's `verify` - one missed copy and a regression silently ships. A
|
|
146
|
+
**Layout** is a `verify` that's enforced automatically, by Checkpoint tag, wherever it
|
|
147
|
+
applies - not once per Block:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { defineLayout, Engine } from "waygraph";
|
|
151
|
+
|
|
152
|
+
const AppShellLayout = defineLayout({
|
|
153
|
+
name: "AppShell",
|
|
154
|
+
// string[] of exact Checkpoint tags, or a (tag) => boolean predicate for a
|
|
155
|
+
// broader match (e.g. "every Checkpoint except the signed-out ones")
|
|
156
|
+
appliesTo: ["AppHome", "Chats", "Notifications", "Settings"],
|
|
157
|
+
verify: [Trait.visible(AppShellSel.sidebar)],
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const engine = new Engine({ layouts: [AppShellLayout] });
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Every `Flow` this `Engine` defines - **and every intermediate Checkpoint inside it**, not
|
|
164
|
+
just the final one - runs `AppShellLayout.verify` automatically whenever it resolves to a
|
|
165
|
+
tag `appliesTo` matches, alongside that Block's own `verify`. `runGraph`/`Flow.run` also
|
|
166
|
+
take `{ layouts }` directly (`RunGraphOptions.layouts`), for driving a Layout outside an
|
|
167
|
+
`Engine`-owned Flow (e.g. `waygraph auto`'s own runtime pathfinding).
|
|
168
|
+
|
|
169
|
+
Deliberately matched by Checkpoint tag, never by folder path - a Layout works identically
|
|
170
|
+
whether a project uses the [Waygraph Map](#waygraph-map) convention or the older freeform
|
|
171
|
+
folder style, since some real consumers of this package still use the latter.
|
|
172
|
+
|
|
173
|
+
### `map()` - a kind-checked, no-teleporting flow builder
|
|
174
|
+
|
|
175
|
+
`Engine.defineFlow([start, ...blocks, end])` already typechecks that each Block's `Out`
|
|
176
|
+
matches the next one's `In`. It does **not** stop an agent (or a careless human) from
|
|
177
|
+
handing it a hand-rolled plain object that merely *looks* like a Block - TypeScript's
|
|
178
|
+
structural typing can't tell a real `defineNavBlock(...)` output from a copy-pasted
|
|
179
|
+
object literal with the right shape. That gap is exactly what broke down in real
|
|
180
|
+
consumer projects: agents editing/hand-composing Blocks into ad hoc shapes, a "locks"
|
|
181
|
+
convention tried and still bypassed. `map()` closes it with an actual runtime check, not
|
|
182
|
+
just a convention:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { Engine } from "waygraph";
|
|
186
|
+
|
|
187
|
+
const engine = new Engine();
|
|
188
|
+
const flow = engine
|
|
189
|
+
.map({ homeOrigin: "https://app.example.com" })
|
|
190
|
+
.start()
|
|
191
|
+
.gotoPage(NavHomeBlock) // must be defineNavBlock / defineMemNavBlock / definePageBlock
|
|
192
|
+
.assert(AssertHelloBlock) // must be defineAssertBlock
|
|
193
|
+
.gotoExternal(NavMailpitBlock) // same kind requirement, plus the INVERSE origin check
|
|
194
|
+
.method(ClearCartBlock) // must be defineMethodBlock / defineActionBlock / defineEffectBlock
|
|
195
|
+
.end(); // -> a real Flow<Out> - withBlockVerify/withTitle/withHighlightFixtures/etc. all still apply
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Each step method is scoped to exactly the Block kind its name promises, checked against
|
|
199
|
+
the same `__waygraphKind`/`__waygraphSalt` runtime markers `waygraph check`/`graph`/`map`
|
|
200
|
+
already trust - a Block that didn't come from a real `define*Block` factory throws
|
|
201
|
+
immediately, naming the step and the factory it should have used, instead of silently
|
|
202
|
+
entering the chain. Give `homeOrigin` and `.gotoPage()`/`.gotoExternal()` also cross-check
|
|
203
|
+
each Nav/Page Block's own static `url` against it (skipped, honestly, for click-based or
|
|
204
|
+
mem-dependent nav - not statically checkable). The Checkpoint chain itself is still
|
|
205
|
+
typechecked exactly like `defineFlow`'s own tuple overloads - there is no method on this
|
|
206
|
+
builder that can skip from one Checkpoint to an unrelated one without a real, kind-correct
|
|
207
|
+
Block in between, which is what "no teleporting" means here.
|
|
208
|
+
|
|
209
|
+
`map()` is sugar over `defineFlow`, not a new execution engine - the result is a real
|
|
210
|
+
`Flow`, so every existing Flow-level feature (Layouts, `withBlockVerify`, demo narration,
|
|
211
|
+
`chainFlow`) works on it unchanged. `import { map } from "waygraph"` is also available
|
|
212
|
+
standalone (`map(options)` ~= `new Engine(options).map(options)`) for a call site that
|
|
213
|
+
doesn't otherwise need its own `Engine` instance. **Recommended for new projects** over
|
|
214
|
+
hand-assembled `defineFlow([start, ...])` arrays - the freeform array form still works
|
|
215
|
+
and isn't going away (real consumers on the older folder convention depend on it), but
|
|
216
|
+
`map()` is the one that actually stops a broken/hacked Block from entering a flow
|
|
217
|
+
unnoticed.
|
|
218
|
+
|
|
141
219
|
### Traits beyond the page
|
|
142
220
|
|
|
143
221
|
A hand-written `Trait`'s `check(page, mem)` receives `mem` too, for the same-Checkpoint
|
|
@@ -324,7 +402,7 @@ export const AssertItemAddedBlock = defineAssertBlock<ItemInCart>({
|
|
|
324
402
|
engine.defineFlow([start, NavHomeBlock, AddItemBlock, AssertItemAddedBlock, ClearCartBlock, end])
|
|
325
403
|
```
|
|
326
404
|
|
|
327
|
-
Live reference: `templates/scaffold/src/
|
|
405
|
+
Live reference: `templates/scaffold/src/map/(app_base)/home/_methods/assert-item-added.method.block.ts`,
|
|
328
406
|
wired into `shop.flow.ts`.
|
|
329
407
|
|
|
330
408
|
**`methods/` folder:** on-page work lives next to the route, not free-floating.
|
|
@@ -439,7 +517,7 @@ preflight reject the run before it ever gets a chance to produce it - `requires`
|
|
|
439
517
|
keys a caller truly must supply from outside the chain (like `ExpectedRecipient` above).
|
|
440
518
|
|
|
441
519
|
Live reference, proven end to end against a real, throwaway Mailpit container:
|
|
442
|
-
`templates/scaffold/src/
|
|
520
|
+
`templates/scaffold/src/map/(external)/mailpit/` + `src/flows/mail-verify.flow.ts` +
|
|
443
521
|
`tests/mail-verify.spec.ts`.
|
|
444
522
|
|
|
445
523
|
### Reusable across scenarios: mem-driven, not one Block per email
|
|
@@ -720,27 +798,46 @@ recurse through any folder structure, matching only the `.block.ts` filename pat
|
|
|
720
798
|
naming and depth are already 100% cosmetic to every existing command.
|
|
721
799
|
|
|
722
800
|
```text
|
|
723
|
-
src/
|
|
724
|
-
(
|
|
725
|
-
dashboard/ #
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
click-widget.block.ts
|
|
731
|
-
(external)/
|
|
732
|
-
docs/ # a
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
801
|
+
src/map/
|
|
802
|
+
(app_base)/ # the landing/base group - purely organizational, never part of
|
|
803
|
+
dashboard/ # any Checkpoint tag. One folder per Checkpoint - "Dashboard"
|
|
804
|
+
_page.block.ts # arrival hub (definePageBlock) - fixed name, every folder
|
|
805
|
+
_nav.block.ts # navigation (defineNavBlock) - fixed name, every folder
|
|
806
|
+
_sel.ts # selectors for this Checkpoint - fixed name, every folder
|
|
807
|
+
_methods/ # this Checkpoint's own action Blocks - fixed name, every
|
|
808
|
+
click-widget.block.ts # folder. Contents keep their own descriptive names -
|
|
809
|
+
(external)/ # only the folder itself is fixed.
|
|
810
|
+
docs/ # a genuinely cross-origin group, same shape - "Docs"
|
|
811
|
+
_page.block.ts
|
|
812
|
+
_nav.block.ts
|
|
813
|
+
_sel.ts
|
|
736
814
|
```
|
|
737
815
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
816
|
+
**Not just a naming preference - a forced convention.** Folder nesting under `map/(group)/`
|
|
817
|
+
must **verbatim-match** the real site's own URL path segments, `(group)` parens excluded
|
|
818
|
+
(purely organizational, same as a Next.js route group): `/app/chats` lives at
|
|
819
|
+
`map/(app_base)/app/chats/`, never `map/(auth)/chats/` for a fabricated grouping that was
|
|
820
|
+
never a real `/auth/*` URL. `waygraph map [project]` (see [CLI reference](#cli-reference))
|
|
821
|
+
enforces this mechanically and exits 1 on a mismatch - don't rely on review alone. Same Block
|
|
822
|
+
helpers as today's freeform "manual mode" (`definePageBlock`/`defineNavBlock`/
|
|
823
|
+
`defineMethodBlock`/etc.) either way - Map only changes *where files live and what they're
|
|
824
|
+
named*, never what kind of Block authors them or how they execute. A project adopts it
|
|
825
|
+
incrementally, folder by folder. Prefer [`map()`](#map---a-kind-checked-no-teleporting-flow-builder)
|
|
826
|
+
over a hand-assembled `defineFlow([start, ...])` array when wiring these Blocks into a Flow -
|
|
827
|
+
it's the one that can't silently accept a Block that didn't come from a real factory.
|
|
828
|
+
|
|
829
|
+
**Leading underscore = this Checkpoint's own fixed machinery, never a route segment.**
|
|
830
|
+
`_nav.block.ts`/`_page.block.ts`/`_sel.ts`/`_methods/` (same private-folder idea as Next.js's
|
|
831
|
+
own `_folder` convention) mark "this belongs to the endpoint whose folder it's directly in,"
|
|
832
|
+
so a bare (non-underscore, non-`(group)`) folder name always means a real child
|
|
833
|
+
Checkpoint/URL segment - `_methods/` can never be mistaken for a sibling endpoint folder the
|
|
834
|
+
way a bare `methods/` structurally could be, since only ONE of those two readings is
|
|
835
|
+
mechanically distinguishable at a glance. `waygraph map`'s own folder-vs-URL comparison
|
|
836
|
+
excludes underscore segments the same way it excludes `(group)` ones - `_methods/foo.block.ts`
|
|
837
|
+
compares against the URL as if `_methods/` weren't there at all. Individual files inside
|
|
838
|
+
`_methods/` keep their own descriptive names (`join-pool.method.block.ts`, not
|
|
839
|
+
`_join-pool...`) - they're hand-authored per-project content, not fixed convention names; the
|
|
840
|
+
`_methods/` folder boundary alone already marks the whole subtree as non-route.
|
|
744
841
|
|
|
745
842
|
**"waypack" - loading another project's Blocks needs no separate manifest either.** A second
|
|
746
843
|
project depending on a Map-convention project as a plain `file:` dependency (the same
|
|
@@ -763,6 +860,58 @@ Full design history (why "Router" isn't a separate thing to build, two prior wro
|
|
|
763
860
|
of this before landing here, and everything above stated as requirements) in
|
|
764
861
|
`openspec/changes/waygraph-map/`.
|
|
765
862
|
|
|
863
|
+
## Migrating from freeform ("router") to Waygraph Map
|
|
864
|
+
|
|
865
|
+
"Router" isn't a separate mode to turn off - it's this project's own name for today's
|
|
866
|
+
freeform, folder-organized style (`src/blocks/<slug>/`, any nesting, any file names).
|
|
867
|
+
Migrating means moving those files under `src/map/(group)/<page-slug>/` with verbatim
|
|
868
|
+
URL-matching names, nothing more - no engine change, no new Block helpers, no flag to flip.
|
|
869
|
+
Do it incrementally: the two styles coexist in the same project (and even the same Flow) with
|
|
870
|
+
no special-casing, since `discoverGraph`/`loadBlockLibrary` only ever match the `.block.ts`
|
|
871
|
+
filename pattern, never folder depth or naming.
|
|
872
|
+
|
|
873
|
+
1. **List every real URL your NavBlocks/PageBlocks already navigate to.** For each one with a
|
|
874
|
+
static string `url`, note its real path. For each one that's `click`-based (no `url` field
|
|
875
|
+
at all) or a dynamic `(mem) => string` function, note that too - neither is statically
|
|
876
|
+
checkable by `waygraph map`, so they can go wherever makes sense; only a literal string
|
|
877
|
+
`url` is verified.
|
|
878
|
+
2. **Pick your group(s).** `(app_base)/` for the landing/base app (organizational only, never
|
|
879
|
+
part of a Checkpoint tag); `(external)/` for genuinely cross-origin Blocks (mail tools,
|
|
880
|
+
third-party embeds) - verify with the real URL, not the folder's own current name, since a
|
|
881
|
+
folder can already be mis-grouped (a real bug this session found: a project's own
|
|
882
|
+
`(external)/docs/` was actually same-origin the whole time).
|
|
883
|
+
3. **Create `src/map/(group)/<verbatim-url-segments>/` per Checkpoint**, `git mv` each
|
|
884
|
+
Block/Sel file in, and rename the fixed ones: `nav.block.ts` -> `_nav.block.ts`,
|
|
885
|
+
`page.block.ts` -> `_page.block.ts`, your selector file -> `_sel.ts` (drops its slug - now a
|
|
886
|
+
fully fixed name), `methods/` -> `_methods/` (contents keep their own names unchanged).
|
|
887
|
+
**Two Checkpoints sharing one real URL** (a DOM-state distinction, not a URL one - e.g. a
|
|
888
|
+
"joined" vs "not joined" variant of the same page) can't both use the bare fixed names in
|
|
889
|
+
one folder; give each pair a slug prefix instead (`app-home._nav.block.ts` /
|
|
890
|
+
`in-pool._nav.block.ts`, or similar) rather than nesting either under its own subfolder
|
|
891
|
+
(that would add a folder segment the real URL doesn't have, and fail step 5).
|
|
892
|
+
4. **A dynamic per-instance URL** (a real chat/order/ticket id, not a fixed route) - if it's
|
|
893
|
+
currently a plain string constant, wrap it as `url: () => thatConstant` instead. A bare
|
|
894
|
+
string is always treated as a real, checkable static route; a function is correctly treated
|
|
895
|
+
as dynamic/non-canonical and skipped, matching how you'd actually navigate a real instance of
|
|
896
|
+
it in practice anyway.
|
|
897
|
+
5. **Fix every import** the moves break (states/mem-key paths change depth; sibling
|
|
898
|
+
nav/page/methods imports need their new `_`-prefixed names) - let the compiler do this for
|
|
899
|
+
you: `tsc --noEmit` if the project has one, otherwise `waygraph check [project]` and
|
|
900
|
+
`waygraph graph [project]` (both do real dynamic imports of every Block file and report
|
|
901
|
+
import failures by name) are just as effective a proof.
|
|
902
|
+
6. **Run `waygraph map [project]`.** Exit 0 / "0 violations" is the actual finish line - not a
|
|
903
|
+
subjective review. A remaining violation names the exact file, its real folder path, and the
|
|
904
|
+
real URL it doesn't match; fix the folder, not the check.
|
|
905
|
+
7. **Diff `waygraph graph`'s node/edge/skipped/orphan counts against before the move.** A pure
|
|
906
|
+
migration changes zero Block behavior - if a count changed, something broke in transit
|
|
907
|
+
(a bad import silently dropped a Block from discovery is the usual cause), not something to
|
|
908
|
+
wave off as "close enough."
|
|
909
|
+
|
|
910
|
+
Once the folder structure is settled, prefer building/rebuilding Flows over these Blocks with
|
|
911
|
+
[`map()`](#map---a-kind-checked-no-teleporting-flow-builder) instead of a hand-assembled
|
|
912
|
+
`defineFlow([start, ...])` array - it's the one that can't silently accept a Block that isn't
|
|
913
|
+
really a Nav/Page/Assert/Method from this package's own factories.
|
|
914
|
+
|
|
766
915
|
## Getting started (pick one)
|
|
767
916
|
|
|
768
917
|
| Goal | Command |
|
|
@@ -881,6 +1030,7 @@ Flows are files (0.10.5+):
|
|
|
881
1030
|
| Detached session with a real visible browser | `waygraph auto --cli --detach --non-headless` |
|
|
882
1031
|
| Read the session's Checkpoint/Block history | `waygraph auto trace <sessionId>` |
|
|
883
1032
|
| Whole-project nav-escape + inline-selector + orphan sweep | `waygraph check [project]` |
|
|
1033
|
+
| Waygraph Map enforcement (verbatim url-vs-folder, exits 1 on a violation) | `waygraph map [project]` |
|
|
884
1034
|
| Graph crawl | `waygraph traverse [project] --parallel N --min-edge-coverage 80%` |
|
|
885
1035
|
| Coding-agent defs | `waygraph agent-dive --loop claude` |
|
|
886
1036
|
| One-shot temp-dir demo | `waygraph try demo` / `waygraph try auto` / `waygraph try auto:cli` |
|
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ import { tmpdir } from "node:os";
|
|
|
18
18
|
import { fileURLToPath } from "node:url";
|
|
19
19
|
import { spawn } from "node:child_process";
|
|
20
20
|
import { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./graph.js";
|
|
21
|
+
import { checkMap } from "./map-check.js";
|
|
21
22
|
import { runAutoExplore } from "./auto-explore-run.js";
|
|
22
23
|
import { pilotStart } from "./pilot.js";
|
|
23
24
|
import { spawnDetachedSession, requestSession, runAttachLoop, runAutoServeCommand, } from "./auto-session-ipc.js";
|
|
@@ -3808,6 +3809,62 @@ async function hideRing(page) {
|
|
|
3808
3809
|
.catch(() => {});
|
|
3809
3810
|
}
|
|
3810
3811
|
|
|
3812
|
+
/**
|
|
3813
|
+
* Real navigation wipes document + overlay helpers. Reinstall and re-paint
|
|
3814
|
+
* carried todos / device / optional stubBefore ring so keep-dock and nav
|
|
3815
|
+
* highlights do not vanish until the next ctx.todos() / stubAfter (PIA #15).
|
|
3816
|
+
*/
|
|
3817
|
+
async function restoreTheaterAfterNavigation(page, todoDockRef, deviceRef, stubBeforeRef) {
|
|
3818
|
+
const needs =
|
|
3819
|
+
(await page
|
|
3820
|
+
.evaluate(() => !window.__wgSyncTodos || !document.getElementById("wg-ring"))
|
|
3821
|
+
.catch(() => true)) || false;
|
|
3822
|
+
if (!needs) {
|
|
3823
|
+
// Overlay survived (SPA / no full document wipe) - still refresh dock
|
|
3824
|
+
// if Node has a carry and the DOM lost .wg-todo-dock.
|
|
3825
|
+
const hasDock = await page
|
|
3826
|
+
.evaluate(() => !!document.querySelector(".wg-todo-dock, #wg-todo-dock"))
|
|
3827
|
+
.catch(() => false);
|
|
3828
|
+
if (hasDock || !todoDockRef || !todoDockRef.current) return;
|
|
3829
|
+
}
|
|
3830
|
+
await installOverlay(page);
|
|
3831
|
+
const dock = todoDockRef && todoDockRef.current;
|
|
3832
|
+
if (dock) {
|
|
3833
|
+
await page
|
|
3834
|
+
.evaluate((d) => {
|
|
3835
|
+
if (window.__wgSyncTodos) window.__wgSyncTodos({ sync: "set", dock: d });
|
|
3836
|
+
}, dock)
|
|
3837
|
+
.catch(() => {});
|
|
3838
|
+
}
|
|
3839
|
+
if (deviceRef && deviceRef.current) {
|
|
3840
|
+
await applyDeviceToPage(page, deviceRef.current, "set").catch(() => {});
|
|
3841
|
+
}
|
|
3842
|
+
// Re-show first matching stubBefore ring if selector still exists (sidebar
|
|
3843
|
+
// nav targets often survive the route change).
|
|
3844
|
+
const stubs = (stubBeforeRef && stubBeforeRef.current) || [];
|
|
3845
|
+
for (const h of stubs) {
|
|
3846
|
+
if (!h || !h.selector) continue;
|
|
3847
|
+
const box = await page
|
|
3848
|
+
.evaluate((sel) => {
|
|
3849
|
+
const el = document.querySelector(sel);
|
|
3850
|
+
if (!el) return null;
|
|
3851
|
+
const r = el.getBoundingClientRect();
|
|
3852
|
+
if (r.width < 2 || r.height < 2) return null;
|
|
3853
|
+
return { x: r.x, y: r.y, width: r.width, height: r.height };
|
|
3854
|
+
}, h.selector)
|
|
3855
|
+
.catch(() => null);
|
|
3856
|
+
if (box) {
|
|
3857
|
+
await showRing(page, box, formatHighlightCaption(h), h.tone || "planned", {
|
|
3858
|
+
size: h.size,
|
|
3859
|
+
weight: h.weight,
|
|
3860
|
+
selector: h.selector,
|
|
3861
|
+
focus: !!h.focus,
|
|
3862
|
+
}).catch(() => {});
|
|
3863
|
+
break;
|
|
3864
|
+
}
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
|
|
3811
3868
|
/**
|
|
3812
3869
|
* Removes every overlay element (panel, ring, ring-label, cursor,
|
|
3813
3870
|
* click-pulse, banner) and stops the live resize/scroll ring tracker, once
|
|
@@ -4541,6 +4598,7 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBefore
|
|
|
4541
4598
|
// of leaving it lit until the next Block's own before-panel clears
|
|
4542
4599
|
// it - it was sticking around through the whole rest of the step.
|
|
4543
4600
|
await hideRing(page);
|
|
4601
|
+
await restoreTheaterAfterNavigation(page, todoDockRef, deviceRef, stubBeforeRef);
|
|
4544
4602
|
return result;
|
|
4545
4603
|
};
|
|
4546
4604
|
}
|
|
@@ -4669,6 +4727,8 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBefore
|
|
|
4669
4727
|
}
|
|
4670
4728
|
await new Promise((res) => setTimeout(res, clickPostPop()));
|
|
4671
4729
|
await hideRing(page);
|
|
4730
|
+
// Full document navigation drops #wg-todo-dock + rings; restore carry.
|
|
4731
|
+
await restoreTheaterAfterNavigation(page, todoDockRef, deviceRef, stubBeforeRef);
|
|
4672
4732
|
return result;
|
|
4673
4733
|
};
|
|
4674
4734
|
}
|
|
@@ -4920,17 +4980,21 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
4920
4980
|
if (sync === "set" && dock) {
|
|
4921
4981
|
if (!parallel) todoDockById.clear();
|
|
4922
4982
|
todoDockById.set(dockRegistryKey(dock), dock);
|
|
4983
|
+
return;
|
|
4984
|
+
}
|
|
4985
|
+
// keep: refresh / re-seed so syncAllTodoDocks never sees an empty map
|
|
4986
|
+
// while lastTodoDock still carries (nav wipe + mid-act advance).
|
|
4987
|
+
if (sync === "keep" && dock) {
|
|
4988
|
+
todoDockById.set(dockRegistryKey(dock), dock);
|
|
4923
4989
|
}
|
|
4924
4990
|
};
|
|
4925
4991
|
const syncAllTodoDocks = async (opts) => {
|
|
4926
4992
|
const docks = [...todoDockById.values()];
|
|
4927
4993
|
const parallel = !!(opts && opts.parallel);
|
|
4928
4994
|
if (!docks.length) {
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
})
|
|
4933
|
-
.catch(() => {});
|
|
4995
|
+
// Empty registry: do NOT clear the DOM. renderBefore/After may have
|
|
4996
|
+
// just painted from lastTodoDock (keep). Clearing here made todos
|
|
4997
|
+
// vanish until the next ctx.todos() set (PIA #15).
|
|
4934
4998
|
return;
|
|
4935
4999
|
}
|
|
4936
5000
|
await page
|
|
@@ -6971,6 +7035,12 @@ Primary (less is more):
|
|
|
6971
7035
|
|
|
6972
7036
|
Also:
|
|
6973
7037
|
waygraph list | nav | validate | check | graph | init <name>
|
|
7038
|
+
waygraph map [project] Waygraph Map convention enforcement: every static
|
|
7039
|
+
Nav/Page url must verbatim-match its src/map/ folder
|
|
7040
|
+
path ((group) segments excluded) - exits 1 on a
|
|
7041
|
+
violation, unlike check's warnings-only stance. A
|
|
7042
|
+
no-op (exit 0, informational) if the project has no
|
|
7043
|
+
src/map/ directory at all.
|
|
6974
7044
|
waygraph traverse [project] Graph crawl (Phase B-E)
|
|
6975
7045
|
--blocks <glob|/re/|sub> Phase C: filter *.block.ts discovery
|
|
6976
7046
|
--parallel N Phase D: N clone workers (max 4)
|
|
@@ -7801,6 +7871,32 @@ Agents shipped: waygraph-planner, waygraph-author, waygraph-healer.
|
|
|
7801
7871
|
// human/agent cleanup; chain auto refuses while any remain.
|
|
7802
7872
|
break;
|
|
7803
7873
|
}
|
|
7874
|
+
case "map": {
|
|
7875
|
+
const proj = resolve(args[1] ?? process.cwd());
|
|
7876
|
+
const result = await checkMap(proj);
|
|
7877
|
+
if (!result.hasMapDir) {
|
|
7878
|
+
console.log(`waygraph map: no src/map/ directory under ${proj} - nothing to check ` +
|
|
7879
|
+
"(this project isn't on the Waygraph Map convention, or hasn't been migrated yet)");
|
|
7880
|
+
break;
|
|
7881
|
+
}
|
|
7882
|
+
console.log(`waygraph map: ${result.nodes.length} node(s) found under ${relative(proj, result.mapRoot)}`);
|
|
7883
|
+
if (result.violations.length === 0) {
|
|
7884
|
+
console.log("waygraph map: 0 violations - every static Nav/Page url verbatim-matches its folder path");
|
|
7885
|
+
}
|
|
7886
|
+
else {
|
|
7887
|
+
for (const v of result.violations) {
|
|
7888
|
+
console.error(`waygraph map: ${v.file} (${v.block}) - ${v.reason}`);
|
|
7889
|
+
}
|
|
7890
|
+
console.error(`waygraph map: ${result.violations.length} violation${result.violations.length === 1 ? "" : "s"}`);
|
|
7891
|
+
// Unlike `check`'s warnings-only stance, a Map violation is exactly
|
|
7892
|
+
// the class of bug this command exists to catch (a fabricated
|
|
7893
|
+
// folder grouping that never matched the real site, e.g. the real
|
|
7894
|
+
// (auth)/signin/ mistake this command's own header comment cites) -
|
|
7895
|
+
// fail loud, not just warn.
|
|
7896
|
+
process.exitCode = 1;
|
|
7897
|
+
}
|
|
7898
|
+
break;
|
|
7899
|
+
}
|
|
7804
7900
|
default:
|
|
7805
7901
|
console.error(`waygraph: unknown command "${command}"`);
|
|
7806
7902
|
process.exit(1);
|
package/dist/engine.d.ts
CHANGED
|
@@ -13,6 +13,52 @@ import { type HighlightStyleDefaults, type DemoPace } from "./highlights.js";
|
|
|
13
13
|
* since those depend on a runtime decision that hasn't happened yet.
|
|
14
14
|
*/
|
|
15
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;
|
|
16
62
|
/**
|
|
17
63
|
* Runs `entry` against a fresh tab and returns the resulting terminal Checkpoint.
|
|
18
64
|
* The engine owns the tab's lifecycle, not any individual Block: the tab is
|
|
@@ -52,6 +98,14 @@ export interface RunGraphOptions {
|
|
|
52
98
|
* `page` and `closeOnFinish` together) - it just does what you ask.
|
|
53
99
|
*/
|
|
54
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[];
|
|
55
109
|
}
|
|
56
110
|
/**
|
|
57
111
|
* Drives a genuinely separate second tab through its own Block/Flow, in the
|
|
@@ -435,6 +489,14 @@ export interface EngineConfig {
|
|
|
435
489
|
height: number;
|
|
436
490
|
};
|
|
437
491
|
};
|
|
492
|
+
/**
|
|
493
|
+
* Cross-cutting, automatically-enforced verify (see {@link defineLayout})
|
|
494
|
+
* - applies to every Flow this Engine defines. Works the same regardless
|
|
495
|
+
* of folder convention (Waygraph Map or freeform "manual mode") since
|
|
496
|
+
* matching is by Checkpoint tag, never by file path.
|
|
497
|
+
* @example new Engine({ layouts: [AppShellLayout] })
|
|
498
|
+
*/
|
|
499
|
+
layouts?: readonly Layout[];
|
|
438
500
|
}
|
|
439
501
|
type S = Checkpoint<"__start__">;
|
|
440
502
|
/**
|
|
@@ -595,7 +657,106 @@ export declare class Engine {
|
|
|
595
657
|
Block<N, O>,
|
|
596
658
|
EndMarker
|
|
597
659
|
]): Flow<O>;
|
|
660
|
+
/**
|
|
661
|
+
* Fluent, kind-checked alternative to `defineFlow([start, ...blocks, end])` -
|
|
662
|
+
* see {@link MapBuilder}. Real, direct request this responds to: pia/zsign's
|
|
663
|
+
* own agents kept hand-editing/hand-composing Blocks into ad hoc shapes
|
|
664
|
+
* ("locks" convention tried, still got broken) - `map()` forces every step
|
|
665
|
+
* through this Engine's own `define*Block` factories (checked by the same
|
|
666
|
+
* `__waygraphKind`/`__waygraphSalt` runtime markers `graph.ts`/`map-check.ts`
|
|
667
|
+
* already trust), so a hand-rolled plain-object Block can never silently
|
|
668
|
+
* pass as a real navigation/assertion/method step. `homeOrigin`, if given,
|
|
669
|
+
* also gates `.gotoPage()`/`.gotoExternal()` against each Nav/Page Block's
|
|
670
|
+
* own static `url` (skipped, honestly, for click-based/dynamic nav - not
|
|
671
|
+
* statically checkable, same limitation `map-check.ts` already documents).
|
|
672
|
+
* @example
|
|
673
|
+
* const flow = engine.map({ homeOrigin: "https://app.example.com" })
|
|
674
|
+
* .start()
|
|
675
|
+
* .gotoPage(NavHomeBlock)
|
|
676
|
+
* .assert(AssertHelloBlock)
|
|
677
|
+
* .gotoExternal(NavMailpitBlock)
|
|
678
|
+
* .end();
|
|
679
|
+
*/
|
|
680
|
+
map(options?: MapBuilderOptions): MapBuilder<S>;
|
|
598
681
|
}
|
|
682
|
+
export interface MapBuilderOptions {
|
|
683
|
+
/**
|
|
684
|
+
* This project's own origin (e.g. `"https://app.example.com"`) - enables
|
|
685
|
+
* `.gotoPage()`/`.gotoExternal()`'s origin check against a Block's static
|
|
686
|
+
* `url`. Omit to skip that check entirely (still fully kind-checked either
|
|
687
|
+
* way; only the internal/external origin distinction is opt-in).
|
|
688
|
+
*/
|
|
689
|
+
homeOrigin?: string;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Fluent builder over {@link Engine.defineFlow} - see `map()`'s own doc
|
|
693
|
+
* comment for why this exists. Each step method is scoped to exactly the
|
|
694
|
+
* Block kind its name promises, checked at RUNTIME against the
|
|
695
|
+
* `__waygraphKind`/`__waygraphSalt` markers `defineNavBlock`/`definePageBlock`/
|
|
696
|
+
* `defineAssertBlock`/`defineMethodBlock`/`defineEffectBlock` already set
|
|
697
|
+
* (the same markers `graph.ts` and `map-check.ts` trust) - not just a type
|
|
698
|
+
* hint, since a hand-rolled object can satisfy the TypeScript `Block<In,Out>`
|
|
699
|
+
* shape without ever going through a real factory. Each call also
|
|
700
|
+
* typechecks the accumulated Checkpoint chain exactly like `defineFlow`'s
|
|
701
|
+
* own tuple overloads do (a step's `In` must equal the previous step's
|
|
702
|
+
* `Out`) - this is what "no teleporting" means: there is no method on this
|
|
703
|
+
* builder that can skip from one Checkpoint to an unrelated one without a
|
|
704
|
+
* real, kind-correct Block in between.
|
|
705
|
+
*/
|
|
706
|
+
export declare class MapBuilder<Out extends Checkpoint<string>> {
|
|
707
|
+
private readonly engine;
|
|
708
|
+
private readonly steps;
|
|
709
|
+
private readonly homeOrigin;
|
|
710
|
+
private constructor();
|
|
711
|
+
/** @internal - use `engine.map()` or the standalone `map()` export. */
|
|
712
|
+
static begin(engine: Engine, homeOrigin: string | undefined): MapBuilder<S>;
|
|
713
|
+
/** Readable chain-opener - mirrors `defineFlow`'s leading `start` sentinel. Returns `this` unchanged; entirely optional. */
|
|
714
|
+
start(): MapBuilder<Out>;
|
|
715
|
+
/**
|
|
716
|
+
* Appends an internal navigation/page-arrival step - requires a Block from
|
|
717
|
+
* `defineNavBlock`/`defineMemNavBlock`/`definePageBlock`. Throws if given
|
|
718
|
+
* anything else, including a `homeOrigin`-mismatched static `url`.
|
|
719
|
+
*/
|
|
720
|
+
gotoPage<NextOut extends Checkpoint<string>>(block: Block<Out, NextOut> & {
|
|
721
|
+
name: string;
|
|
722
|
+
}): MapBuilder<NextOut>;
|
|
723
|
+
/**
|
|
724
|
+
* Appends a genuinely cross-origin navigation step (e.g. `(external)/`
|
|
725
|
+
* Blocks in the Waygraph Map convention: mailpit, maildrop.cc) - same
|
|
726
|
+
* kind requirement as {@link gotoPage}, plus the inverse `homeOrigin` check.
|
|
727
|
+
*/
|
|
728
|
+
gotoExternal<NextOut extends Checkpoint<string>>(block: Block<Out, NextOut> & {
|
|
729
|
+
name: string;
|
|
730
|
+
}): MapBuilder<NextOut>;
|
|
731
|
+
/** Appends a self-loop verification step - requires a Block from `defineAssertBlock`. */
|
|
732
|
+
assert<NextOut extends Checkpoint<string>>(block: Block<Out, NextOut> & {
|
|
733
|
+
name: string;
|
|
734
|
+
}): MapBuilder<NextOut>;
|
|
735
|
+
/**
|
|
736
|
+
* Appends a non-navigating page action (submit, upload, add/remove an
|
|
737
|
+
* instance) - requires a Block from `defineMethodBlock`/`defineActionBlock`/
|
|
738
|
+
* `defineEffectBlock`/`defineMemEffectBlock`.
|
|
739
|
+
*/
|
|
740
|
+
method<NextOut extends Checkpoint<string>>(block: Block<Out, NextOut> & {
|
|
741
|
+
name: string;
|
|
742
|
+
}): MapBuilder<NextOut>;
|
|
743
|
+
/**
|
|
744
|
+
* Finalizes this chain into a real, runnable {@link Flow} - same object
|
|
745
|
+
* `defineFlow` returns, so `withBlockVerify`/`modBlockVerify` and every
|
|
746
|
+
* `withTitle`/`withHighlightFixtures`/etc. decorator still apply exactly
|
|
747
|
+
* as they do today; this builder only changes how the chain is assembled,
|
|
748
|
+
* never what it produces. Throws if no step was ever added - an empty map
|
|
749
|
+
* isn't a flow.
|
|
750
|
+
*/
|
|
751
|
+
end(): Flow<Out>;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Standalone convenience for `new Engine(config).map(options)` - use this
|
|
755
|
+
* when a call site doesn't otherwise need its own `Engine` instance (no
|
|
756
|
+
* shared `headless`/`slowMo`/`layouts` across several flows).
|
|
757
|
+
* @example const flow = map({ homeOrigin: "https://app.example.com" }).gotoPage(NavHomeBlock).end();
|
|
758
|
+
*/
|
|
759
|
+
export declare function map(options?: MapBuilderOptions & EngineConfig): MapBuilder<S>;
|
|
599
760
|
/**
|
|
600
761
|
* Attaches routing to `block` without touching its act/observe/resolve/verify -
|
|
601
762
|
* `routes` must cover every tag of `block`'s output (a missing route is a compile
|