waygraph 0.15.33 → 0.15.34
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/dist/engine/core.js +5 -0
- package/dist/engine/engine-class.d.ts +37 -0
- package/dist/engine/engine-class.js +98 -0
- package/examples/saucedemo/.waygraph-auto/102c38ca.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2fdbd916.log +0 -0
- package/examples/saucedemo/.waygraph-auto/331c6e8e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/38f6799d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/81257bf0.log +0 -0
- package/examples/saucedemo/.waygraph-auto/a4b36275.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b5569336.log +0 -0
- package/package.json +1 -1
package/dist/engine/core.js
CHANGED
|
@@ -45,6 +45,11 @@ export function branch(block, routes) {
|
|
|
45
45
|
},
|
|
46
46
|
routes: routesAsData,
|
|
47
47
|
});
|
|
48
|
+
// Without this, branch()'s output carried no __waygraphKind/__waygraphSalt at all (defineBlock
|
|
49
|
+
// never stamps them - factories do, after defineBlock returns), so a branched Block could never
|
|
50
|
+
// pass MapBuilder's assertMapKind/assertMapSalt checks. Real bug: branching was unusable from the
|
|
51
|
+
// Waygraph Map, only from a raw runGraph() call.
|
|
52
|
+
copyWaygraphRuntime(block, routed);
|
|
48
53
|
return routed;
|
|
49
54
|
}
|
|
50
55
|
/**
|
|
@@ -269,6 +269,43 @@ export declare class MapBuilder<Out extends Checkpoint<string>> {
|
|
|
269
269
|
method<NextOut extends Checkpoint<string>>(block: (MethodBlock<Out, NextOut> | EffectBlock<Out, NextOut>) & {
|
|
270
270
|
name: string;
|
|
271
271
|
}): MapBuilder<NextOut>;
|
|
272
|
+
/**
|
|
273
|
+
* Branch: run everything added so far, then continue into whichever `routes[tag]` matches the
|
|
274
|
+
* resolved Checkpoint's tag - same page, same context, same mem. `routes` must cover every tag
|
|
275
|
+
* of `Out` (a missing one is a compile error, matching {@link branch}'s own exhaustiveness);
|
|
276
|
+
* `null` means that tag is terminal (the branched Flow just returns that Checkpoint).
|
|
277
|
+
*
|
|
278
|
+
* Each non-null route is a function, not a ready-made `Flow` - a fresh Flow always starts at the
|
|
279
|
+
* special `S` (start) Checkpoint, but a branch's blocks start from the TAG it routes on (e.g.
|
|
280
|
+
* `LoggedIn`), so the function is handed a brand-new `MapBuilder` already seeded at that exact
|
|
281
|
+
* Checkpoint to chain `.method()/.gotoPage()/.assert()/.end()` (or another `.branch()`) off of.
|
|
282
|
+
*
|
|
283
|
+
* Real gap this fixes: the plain `branch()` helper's `.next` routing is only ever consulted by
|
|
284
|
+
* `runGraph`'s own graph walk - `defineFlow`/`.end()` compose steps pairwise via `connect()`,
|
|
285
|
+
* which never looks at `.next` at all, so a Map-built Flow had no way to branch. This method
|
|
286
|
+
* branches at the Flow level instead: it finalizes the prefix, runs it, and picks the next Flow
|
|
287
|
+
* itself once the real tag is known - no engine-level routing involved.
|
|
288
|
+
*
|
|
289
|
+
* Only the `run(context, mem[, options])` signature is supported - the page must survive across
|
|
290
|
+
* the branch, and the bare `run(mem, config)` convenience form always closes its own browser
|
|
291
|
+
* before a tag is even known, so it throws instead of silently reopening a fresh (state-losing) page.
|
|
292
|
+
*
|
|
293
|
+
* The route's first Block still sees `input.__state === "__start__"` in `act()`, not the tag that
|
|
294
|
+
* routed there - `runGraph` seeds every Flow's entry Block that way unconditionally, the same as
|
|
295
|
+
* any standalone `defineFlow([start, block, end])` already does. Read `page`/`mem`, not `input`,
|
|
296
|
+
* in `act()` - every real Block in this codebase already follows that convention.
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* map({ homeOrigin }).gotoPage(NavCartBlock).branch({
|
|
300
|
+
* ItemInCart: (m) => m.method(RemoveFromCartBlock).end(),
|
|
301
|
+
* LoggedIn: (m) => m.method(AddToCartBlock).end(),
|
|
302
|
+
* });
|
|
303
|
+
*/
|
|
304
|
+
branch<Routes extends {
|
|
305
|
+
[K in Out["__state"]]: ((m: MapBuilder<Extract<Out, Checkpoint<K>>>) => Flow<any>) | null;
|
|
306
|
+
}>(routes: Routes): Flow<{
|
|
307
|
+
[K in keyof Routes & string]: Routes[K] extends (m: any) => Flow<infer R> ? R : Extract<Out, Checkpoint<K>>;
|
|
308
|
+
}[keyof Routes & string]>;
|
|
272
309
|
/**
|
|
273
310
|
* Finalizes this chain into a real, runnable {@link Flow} - same object
|
|
274
311
|
* `defineFlow` returns, so `withBlockVerify`/`modBlockVerify` and every
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { end, start } from "./run-graph.js";
|
|
2
2
|
import { buildFlow } from "./flow.js";
|
|
3
|
+
import { MemPage } from "../mem-page.js";
|
|
3
4
|
import { fastForwardComposeBlock } from "./compose.js";
|
|
4
5
|
/**
|
|
5
6
|
* Holds engine-level configuration shared across every flow defined from it -
|
|
@@ -104,6 +105,59 @@ function assertMapOrigin(block, homeOrigin, expect, method) {
|
|
|
104
105
|
`home origin (${wantOrigin}) - use .gotoPage() for an internal destination.`);
|
|
105
106
|
}
|
|
106
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Builds the `Flow` object `MapBuilder.branch()` returns: run `before`, then dispatch on the
|
|
110
|
+
* resolved tag into whichever `routes` entry matches (same page/context/mem), or return that
|
|
111
|
+
* Checkpoint as-is when its route is `null`. Factored out so `withBlockVerify`/`modBlockVerify`
|
|
112
|
+
* can patch `before` and rebuild the same branched shape, exactly like a plain Flow's own.
|
|
113
|
+
*/
|
|
114
|
+
function buildBranchedFlow(before, routes) {
|
|
115
|
+
const run = (async (contextOrMem, memOrConfig, options) => {
|
|
116
|
+
if (contextOrMem instanceof MemPage) {
|
|
117
|
+
throw new Error("Waygraph map: a .branch()-ed Flow can't run via run(mem, config) - that convenience form " +
|
|
118
|
+
"always closes its own browser/page before a branch's tag is even known. Call " +
|
|
119
|
+
"run(context, mem[, options]) instead, keeping the same page across the branch.");
|
|
120
|
+
}
|
|
121
|
+
const context = contextOrMem;
|
|
122
|
+
const mem = memOrConfig;
|
|
123
|
+
const gotOwnPage = options?.page === undefined;
|
|
124
|
+
const page = options?.page ?? (await context.newPage());
|
|
125
|
+
const closeOnFinish = options?.closeOnFinish ?? gotOwnPage;
|
|
126
|
+
const first = (await before.run(context, mem, { ...options, page, closeOnFinish: false }));
|
|
127
|
+
const branchFlow = routes[first.result.__state];
|
|
128
|
+
if (!branchFlow) {
|
|
129
|
+
if (options?.closeOnFinish === false)
|
|
130
|
+
return { result: first.result, page: first.page };
|
|
131
|
+
if (closeOnFinish)
|
|
132
|
+
await first.page.close();
|
|
133
|
+
return first.result;
|
|
134
|
+
}
|
|
135
|
+
return branchFlow.run(context, mem, { ...options, page: first.page, closeOnFinish });
|
|
136
|
+
});
|
|
137
|
+
return {
|
|
138
|
+
resetSession: before.resetSession,
|
|
139
|
+
...(before.title !== undefined ? { title: before.title } : {}),
|
|
140
|
+
...(before.expectedFailureReason !== undefined ? { expectedFailureReason: before.expectedFailureReason } : {}),
|
|
141
|
+
...(before.highlightFixtures !== undefined ? { highlightFixtures: before.highlightFixtures } : {}),
|
|
142
|
+
...(before.demoPace !== undefined ? { demoPace: before.demoPace } : {}),
|
|
143
|
+
...(before.highlightStyle !== undefined ? { highlightStyle: before.highlightStyle } : {}),
|
|
144
|
+
...(before.memStub !== undefined ? { memStub: before.memStub } : {}),
|
|
145
|
+
// Static introspection (`waygraph graph`/list): the fixed prefix, plus every branch's own
|
|
146
|
+
// Blocks prefixed by the tag that leads to them - can't know at analysis time which one a real
|
|
147
|
+
// run takes, so this shows all of them rather than none.
|
|
148
|
+
blocks: () => [
|
|
149
|
+
...before.blocks(),
|
|
150
|
+
...Object.entries(routes).flatMap(([tag, f]) => f ? f.blocks().map((b) => ({ ...b, name: `${tag} -> ${b.name}` })) : []),
|
|
151
|
+
],
|
|
152
|
+
run,
|
|
153
|
+
withBlockVerify(block, verify) {
|
|
154
|
+
return buildBranchedFlow(before.withBlockVerify(block, verify), routes);
|
|
155
|
+
},
|
|
156
|
+
modBlockVerify(block, nameOrIndex, newCheck) {
|
|
157
|
+
return buildBranchedFlow(before.modBlockVerify(block, nameOrIndex, newCheck), routes);
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
107
161
|
/**
|
|
108
162
|
* Fluent builder over {@link Engine.defineFlow} - see `map()`'s own doc
|
|
109
163
|
* comment for why this exists. Each step method is scoped to exactly the
|
|
@@ -226,6 +280,50 @@ export class MapBuilder {
|
|
|
226
280
|
assertMapSalt(block, ["method", "effect"], "method");
|
|
227
281
|
return this.appendStep(block);
|
|
228
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Branch: run everything added so far, then continue into whichever `routes[tag]` matches the
|
|
285
|
+
* resolved Checkpoint's tag - same page, same context, same mem. `routes` must cover every tag
|
|
286
|
+
* of `Out` (a missing one is a compile error, matching {@link branch}'s own exhaustiveness);
|
|
287
|
+
* `null` means that tag is terminal (the branched Flow just returns that Checkpoint).
|
|
288
|
+
*
|
|
289
|
+
* Each non-null route is a function, not a ready-made `Flow` - a fresh Flow always starts at the
|
|
290
|
+
* special `S` (start) Checkpoint, but a branch's blocks start from the TAG it routes on (e.g.
|
|
291
|
+
* `LoggedIn`), so the function is handed a brand-new `MapBuilder` already seeded at that exact
|
|
292
|
+
* Checkpoint to chain `.method()/.gotoPage()/.assert()/.end()` (or another `.branch()`) off of.
|
|
293
|
+
*
|
|
294
|
+
* Real gap this fixes: the plain `branch()` helper's `.next` routing is only ever consulted by
|
|
295
|
+
* `runGraph`'s own graph walk - `defineFlow`/`.end()` compose steps pairwise via `connect()`,
|
|
296
|
+
* which never looks at `.next` at all, so a Map-built Flow had no way to branch. This method
|
|
297
|
+
* branches at the Flow level instead: it finalizes the prefix, runs it, and picks the next Flow
|
|
298
|
+
* itself once the real tag is known - no engine-level routing involved.
|
|
299
|
+
*
|
|
300
|
+
* Only the `run(context, mem[, options])` signature is supported - the page must survive across
|
|
301
|
+
* the branch, and the bare `run(mem, config)` convenience form always closes its own browser
|
|
302
|
+
* before a tag is even known, so it throws instead of silently reopening a fresh (state-losing) page.
|
|
303
|
+
*
|
|
304
|
+
* The route's first Block still sees `input.__state === "__start__"` in `act()`, not the tag that
|
|
305
|
+
* routed there - `runGraph` seeds every Flow's entry Block that way unconditionally, the same as
|
|
306
|
+
* any standalone `defineFlow([start, block, end])` already does. Read `page`/`mem`, not `input`,
|
|
307
|
+
* in `act()` - every real Block in this codebase already follows that convention.
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* map({ homeOrigin }).gotoPage(NavCartBlock).branch({
|
|
311
|
+
* ItemInCart: (m) => m.method(RemoveFromCartBlock).end(),
|
|
312
|
+
* LoggedIn: (m) => m.method(AddToCartBlock).end(),
|
|
313
|
+
* });
|
|
314
|
+
*/
|
|
315
|
+
branch(routes) {
|
|
316
|
+
if (this.steps.length === 0) {
|
|
317
|
+
throw new Error("Waygraph map: .branch() needs at least one prior step - nothing to branch from. " +
|
|
318
|
+
"Add .gotoPage()/.gotoExternal()/.method()/.assert() first.");
|
|
319
|
+
}
|
|
320
|
+
const before = this.end();
|
|
321
|
+
const resolvedRoutes = {};
|
|
322
|
+
for (const [tag, fn] of Object.entries(routes)) {
|
|
323
|
+
resolvedRoutes[tag] = fn ? fn(new MapBuilder(this.engine, [], this.homeOrigin, null)) : null;
|
|
324
|
+
}
|
|
325
|
+
return buildBranchedFlow(before, resolvedRoutes);
|
|
326
|
+
}
|
|
229
327
|
/**
|
|
230
328
|
* Finalizes this chain into a real, runnable {@link Flow} - same object
|
|
231
329
|
* `defineFlow` returns, so `withBlockVerify`/`modBlockVerify` and every
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|