waygraph 0.16.2 → 0.16.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/analysis/practices-check.d.ts +1 -1
- package/dist/analysis/practices-check.js +36 -0
- package/dist/beta.d.ts +6 -0
- package/dist/beta.js +5 -0
- package/dist/cli/commands/codemod-flows.js +2 -2
- package/dist/cli/commands/codemod-stubs.js +43 -1
- package/dist/cli/commands/pilot.js +3 -16
- package/dist/cli/commands/session-ops.js +26 -1
- package/dist/cli/flags.d.ts +1 -1
- package/dist/cli/flags.js +1 -1
- package/dist/cli/usage.js +6 -0
- package/dist/demo/narration/stub-ctx.js +126 -124
- package/dist/demo/overlay/cursor.js +1 -1
- package/dist/demo/overlay/inpage/banner.js +5 -0
- package/dist/demo/overlay/inpage/core.js +1 -0
- package/dist/demo/overlay/inpage/network.js +1 -1
- package/dist/demo/overlay/inpage/todos.js +0 -15
- package/dist/demo/overlay/network-capture.js +2 -2
- package/dist/demo/overlay/overlay-css.js +18 -21
- package/dist/demo/overlay/overlay-stage.js +1 -1
- package/dist/demo/overlay/phase-fixtures.js +5 -5
- package/dist/demo/overlay/pilot/shell.js +7 -5
- package/dist/demo/overlay/ui/components/ring.js +2 -2
- package/dist/demo/overlay/ui/overlay.css +98 -35
- package/dist/demo/overlay/ui/shadow.js +69 -2
- package/dist/demo/stepper/demo-log.js +8 -8
- package/dist/demo/stepper/slides.d.ts +1 -1
- package/dist/demo/stepper/slides.js +1 -1
- package/dist/demo/stepper/stepper.js +1 -1
- package/dist/engine/block/kind.d.ts +3 -0
- package/dist/engine/block/kind.js +11 -0
- package/dist/engine/errors.d.ts +12 -0
- package/dist/engine/errors.js +12 -0
- package/dist/engine/read/read-block.d.ts +61 -0
- package/dist/engine/read/read-block.js +94 -0
- package/dist/engine/read/read-key.d.ts +32 -0
- package/dist/engine/read/read-key.js +34 -0
- package/dist/engine/read/read-page.d.ts +19 -0
- package/dist/engine/read/read-page.js +50 -0
- package/dist/engine/read/readers.d.ts +82 -0
- package/dist/engine/read/readers.js +43 -0
- package/dist/engine/read/run-read.d.ts +17 -0
- package/dist/engine/read/run-read.js +156 -0
- package/dist/engine/run/connect.js +3 -2
- package/dist/engine/run/events.d.ts +28 -10
- package/dist/engine/run/events.js +52 -38
- package/dist/engine/run/run.js +25 -13
- package/dist/engine/stub/stub.d.ts +26 -21
- package/dist/index.d.ts +7 -4
- package/dist/index.js +9 -4
- package/dist/live/auto/auto.js +2 -3
- package/dist/live/auto/menu.js +3 -2
- package/dist/live/auto-session/auto-session.d.ts +9 -2
- package/dist/live/auto-session/auto-session.js +23 -20
- package/dist/live/auto-session/feed.d.ts +16 -0
- package/dist/live/auto-session/feed.js +71 -0
- package/dist/live/auto-session/helpers.d.ts +7 -0
- package/dist/live/auto-session/helpers.js +9 -0
- package/dist/live/auto-session/ipc.d.ts +15 -47
- package/dist/live/auto-session/ipc.js +34 -4
- package/dist/live/auto-session/protocol.d.ts +127 -0
- package/dist/live/auto-session/protocol.js +6 -0
- package/dist/live/auto-session/read.d.ts +25 -0
- package/dist/live/auto-session/read.js +50 -0
- package/dist/live/traverse/worker.d.ts +1 -0
- package/dist/live/traverse/worker.js +6 -1
- package/dist/manifest/manifest.d.ts +22 -2
- package/dist/manifest/manifest.js +27 -1
- package/dist/manifest/project.d.ts +10 -1
- package/dist/manifest/project.js +9 -12
- package/dist/manifest/read-blocks.d.ts +2 -0
- package/dist/manifest/read-blocks.js +23 -0
- package/dist/manifest/scan.d.ts +3 -2
- package/dist/manifest/scan.js +8 -4
- package/dist/runner/runner.d.ts +0 -1
- package/dist/runner/runner.js +6 -6
- package/dist/runtime/full-launch.d.ts +2 -1
- package/dist/runtime/full-launch.js +29 -27
- package/dist/runtime/run-log.d.ts +2 -0
- package/dist/runtime/run-log.js +13 -6
- package/examples/saucedemo/README.md +2 -2
- package/examples/saucedemo/src/blocks/SITE-MAP.md +3 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/cart/cart.read.block.ts +26 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/header.read.block.ts +25 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory/inventory.read.block.ts +28 -0
- package/examples/saucedemo/src/flows/api-demo.flow.ts +1 -1
- package/examples/saucedemo/src/flows/cart-bulk.flow.ts +1 -1
- package/examples/saucedemo/src/flows/cart-continue.flow.ts +1 -1
- package/examples/saucedemo/src/flows/catalog-logout.flow.ts +1 -1
- package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +1 -1
- package/examples/saucedemo/src/flows/checkout.flow.ts +11 -11
- package/examples/saucedemo/src/flows/login.flow.ts +1 -1
- package/examples/saucedemo/src/flows/shop.flow.ts +1 -1
- package/examples/saucedemo/src/flows/stub-on-error.flow.ts +1 -1
- package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +1 -1
- package/examples/saucedemo/src/states/checkout.reads.ts +9 -0
- package/examples/saucedemo/src/stubs/device-showcase.ts +37 -37
- package/examples/saucedemo/src/stubs/finish-order.ts +2 -2
- package/examples/saucedemo/src/stubs/login.ts +5 -5
- package/package.json +5 -1
- package/templates/quickstart/src/flows/api-demo.flow.ts +1 -1
- package/templates/quickstart/src/flows/cart-bulk.flow.ts +1 -1
- package/templates/quickstart/src/flows/cart-continue.flow.ts +1 -1
- package/templates/quickstart/src/flows/catalog-logout.flow.ts +1 -1
- package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +1 -1
- package/templates/quickstart/src/flows/checkout.flow.ts +11 -11
- package/templates/quickstart/src/flows/login.flow.ts +1 -1
- package/templates/quickstart/src/flows/shop.flow.ts +1 -1
- package/templates/quickstart/src/flows/stub-on-error.flow.ts +1 -1
- package/templates/quickstart/src/flows/viewer-blocked.flow.ts +1 -1
- package/templates/quickstart/src/stubs/device-showcase.ts +37 -37
- package/templates/quickstart/src/stubs/finish-order.ts +2 -2
- package/templates/quickstart/src/stubs/login.ts +5 -5
- package/templates/scaffold/src/flows/(external)/hoppscotch/auth.flow.ts +1 -1
- package/templates/scaffold/src/flows/(external)/hoppscotch/get.flow.ts +1 -1
- package/templates/scaffold/src/flows/(external)/hoppscotch/post.flow.ts +1 -1
- package/templates/scaffold/src/flows/(user-stories)/email-verification.flow.ts +4 -4
- package/templates/scaffold/src/flows/README.md +1 -1
- package/templates/scaffold/src/flows/home/cart.flow.ts +7 -7
- package/templates/scaffold/src/flows/home/hello.flow.ts +8 -8
- package/templates/skills/waygraph-convention.skill.md +10 -2
- package/templates/skills/waygraph-pilot.skill.md +7 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { ignorePageGone, ignoreTimeout } from "../page-gone.js";
|
|
2
|
+
import { Reader } from "./readers.js";
|
|
3
|
+
/** In the page (self-contained, shipped by evaluateAll): reads a field, list rows or a table from the matched elements. */
|
|
4
|
+
function readInPage(matched, ask) {
|
|
5
|
+
const shown = (el) => {
|
|
6
|
+
const box = el.getBoundingClientRect();
|
|
7
|
+
const style = getComputedStyle(el);
|
|
8
|
+
return box.width > 0 && box.height > 0 && style.visibility !== "hidden" && style.display !== "none";
|
|
9
|
+
};
|
|
10
|
+
// textContent, whitespace collapsed: innerText forces layout per element (125ms vs 3ms for 8,000 table cells).
|
|
11
|
+
const textOf = (el) => (el.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
12
|
+
const take = (els, plan) => {
|
|
13
|
+
const first = els[0];
|
|
14
|
+
switch (plan.kind) {
|
|
15
|
+
case "count":
|
|
16
|
+
return els.length;
|
|
17
|
+
case "visible":
|
|
18
|
+
return els.some(shown);
|
|
19
|
+
case "texts":
|
|
20
|
+
return els.map(textOf);
|
|
21
|
+
case "attr":
|
|
22
|
+
return first ? first.getAttribute(plan.attr ?? "") : null;
|
|
23
|
+
case "value":
|
|
24
|
+
return first && "value" in first ? String(first.value) : null;
|
|
25
|
+
default:
|
|
26
|
+
// text, and number (parsed in Node from the text)
|
|
27
|
+
return first ? textOf(first) : null;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const within = (root, plan) => (plan.selector ? [...root.querySelectorAll(plan.selector)] : [root]);
|
|
31
|
+
if (ask.mode === "field")
|
|
32
|
+
return take(matched, ask.plan);
|
|
33
|
+
if (ask.mode === "list") {
|
|
34
|
+
return matched.map((row) => {
|
|
35
|
+
const out = {};
|
|
36
|
+
for (const [name, plan] of Object.entries(ask.fields))
|
|
37
|
+
out[name] = take(within(row, plan), plan);
|
|
38
|
+
return out;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const table = matched[0];
|
|
42
|
+
if (!table)
|
|
43
|
+
return null;
|
|
44
|
+
const HEAD = "th, [role=columnheader]";
|
|
45
|
+
const CELL = "td, th, [role=cell], [role=gridcell], [role=rowheader], [role=columnheader]";
|
|
46
|
+
const rows = [...table.querySelectorAll("tr, [role=row]")];
|
|
47
|
+
const headRow = rows.find((r) => r.querySelector(HEAD) && !r.querySelector("td, [role=cell], [role=gridcell]"));
|
|
48
|
+
const headers = headRow ? [...headRow.querySelectorAll(CELL)].map(textOf) : [];
|
|
49
|
+
const index = {};
|
|
50
|
+
const absent = [];
|
|
51
|
+
for (const [name, header] of Object.entries(ask.columns)) {
|
|
52
|
+
const at = headers.findIndex((h) => h.toLowerCase() === header.toLowerCase());
|
|
53
|
+
if (at < 0)
|
|
54
|
+
absent.push(name);
|
|
55
|
+
else
|
|
56
|
+
index[name] = at;
|
|
57
|
+
}
|
|
58
|
+
const body = rows.filter((r) => r !== headRow && r.querySelector("td, [role=cell], [role=gridcell], [role=rowheader]"));
|
|
59
|
+
const out = body.map((r) => {
|
|
60
|
+
const cells = [...r.querySelectorAll(CELL)];
|
|
61
|
+
const row = {};
|
|
62
|
+
for (const name of Object.keys(ask.columns)) {
|
|
63
|
+
const cell = index[name] === undefined ? undefined : cells[index[name]];
|
|
64
|
+
row[name] = cell ? textOf(cell) : null;
|
|
65
|
+
}
|
|
66
|
+
return row;
|
|
67
|
+
});
|
|
68
|
+
const declared = Number(table.getAttribute("aria-rowcount"));
|
|
69
|
+
return { rows: out, absent, rowCount: Number.isFinite(declared) && declared > 0 ? declared - (headRow ? 1 : 0) : out.length };
|
|
70
|
+
}
|
|
71
|
+
/** In the page: resolves once the DOM has had no change for `quietMs` (or after `capMs`, whichever comes first). */
|
|
72
|
+
function domQuiet({ quietMs, capMs }) {
|
|
73
|
+
return new Promise((resolve) => {
|
|
74
|
+
const done = () => {
|
|
75
|
+
observer.disconnect();
|
|
76
|
+
clearTimeout(quiet);
|
|
77
|
+
clearTimeout(cap);
|
|
78
|
+
resolve();
|
|
79
|
+
};
|
|
80
|
+
let quiet = setTimeout(done, quietMs);
|
|
81
|
+
const cap = setTimeout(done, capMs);
|
|
82
|
+
const observer = new MutationObserver(() => {
|
|
83
|
+
clearTimeout(quiet);
|
|
84
|
+
quiet = setTimeout(done, quietMs);
|
|
85
|
+
});
|
|
86
|
+
observer.observe(document, { subtree: true, childList: true, characterData: true, attributes: true });
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Waits for the page to stop changing before a read. A step's verify can pass before the app has rendered where it
|
|
91
|
+
* landed (a URL check passes the moment a client-side route changes), so a read right after it can see the old page.
|
|
92
|
+
*/
|
|
93
|
+
export async function settlePage(page, quietMs = 150, capMs = 2000) {
|
|
94
|
+
await page.waitForLoadState("domcontentloaded", { timeout: capMs }).catch(ignoreTimeout);
|
|
95
|
+
await page.evaluate(domQuiet, { quietMs, capMs }).catch(ignorePageGone);
|
|
96
|
+
}
|
|
97
|
+
/** The first number in a display text: `"$1,299.00"` -> 1299, `"41 likes"` -> 41, no number -> null. */
|
|
98
|
+
export function firstNumber(text) {
|
|
99
|
+
if (typeof text !== "string")
|
|
100
|
+
return null;
|
|
101
|
+
const m = /-?\d[\d,]*(?:\.\d+)?/.exec(text);
|
|
102
|
+
return m ? Number(m[0].replace(/,/g, "")) : null;
|
|
103
|
+
}
|
|
104
|
+
function finishField(plan, raw) {
|
|
105
|
+
return plan.kind === "number" ? firstNumber(raw) : raw;
|
|
106
|
+
}
|
|
107
|
+
async function readOne(page, mem, plan, path, notes) {
|
|
108
|
+
if (plan.kind === "fn")
|
|
109
|
+
return (await plan.fn(page, mem)) ?? null;
|
|
110
|
+
const matched = page.locator(plan.selector);
|
|
111
|
+
if (plan.kind === "list") {
|
|
112
|
+
const rows = (await matched.evaluateAll(readInPage, { mode: "list", fields: plan.fields }));
|
|
113
|
+
return rows.map((row) => {
|
|
114
|
+
for (const [name, field] of Object.entries(plan.fields))
|
|
115
|
+
row[name] = finishField(field, row[name]);
|
|
116
|
+
return row;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (plan.kind === "table") {
|
|
120
|
+
const got = (await matched.evaluateAll(readInPage, { mode: "table", columns: plan.columns }));
|
|
121
|
+
if (!got)
|
|
122
|
+
return null;
|
|
123
|
+
const why = [];
|
|
124
|
+
if (got.absent.length)
|
|
125
|
+
why.push(`no column ${got.absent.map((c) => JSON.stringify(plan.columns[c])).join(", ")}`);
|
|
126
|
+
if (got.rowCount > got.rows.length)
|
|
127
|
+
why.push(`${got.rows.length} of ${got.rowCount} rows rendered (scroll or page to read more)`);
|
|
128
|
+
if (why.length)
|
|
129
|
+
notes.set(path, why.join("; "));
|
|
130
|
+
return got.rows;
|
|
131
|
+
}
|
|
132
|
+
return finishField(plan, await matched.evaluateAll(readInPage, { mode: "field", plan }));
|
|
133
|
+
}
|
|
134
|
+
/** Reads every field of every spec, in parallel, into leaves by path. */
|
|
135
|
+
export async function runReadSpecs(page, mem, specs) {
|
|
136
|
+
const values = new Map();
|
|
137
|
+
const notes = new Map();
|
|
138
|
+
const jobs = [];
|
|
139
|
+
const walk = (fields, prefix) => {
|
|
140
|
+
for (const [name, value] of Object.entries(fields)) {
|
|
141
|
+
if (value === undefined)
|
|
142
|
+
continue;
|
|
143
|
+
const path = `${prefix}.${name}`;
|
|
144
|
+
if (value instanceof Reader) {
|
|
145
|
+
values.set(path, null);
|
|
146
|
+
jobs.push(readOne(page, mem, value.plan, path, notes).then((v) => void values.set(path, v)));
|
|
147
|
+
}
|
|
148
|
+
else
|
|
149
|
+
walk(value, path);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
for (const spec of specs)
|
|
153
|
+
walk(spec.fields, spec.namespace);
|
|
154
|
+
await Promise.all(jobs);
|
|
155
|
+
return { values, notes };
|
|
156
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { runVerify, runPrecondition } from "../trait/trait.js";
|
|
2
2
|
import { isConnected, lastLeaf, stepEnded, stepStarted } from "./events.js";
|
|
3
|
+
import { blockKindOf } from "../block/kind.js";
|
|
3
4
|
/**
|
|
4
5
|
* Composes two Blocks into one, typechecked so this only compiles when A's output
|
|
5
6
|
* tag equals B's input tag. The composed Block's act runs A's full instruction
|
|
@@ -25,7 +26,7 @@ export function connect(a, b, layouts) {
|
|
|
25
26
|
instruction: {
|
|
26
27
|
async act(page, input, mem) {
|
|
27
28
|
if (!isConnected(a))
|
|
28
|
-
stepStarted(a.name, input.__state);
|
|
29
|
+
stepStarted(a.name, blockKindOf(a), input.__state);
|
|
29
30
|
await runPrecondition(a.instruction.precondition, input, page, mem, a.name);
|
|
30
31
|
await a.instruction.act(page, input, mem);
|
|
31
32
|
const aObserved = a.instruction.observe
|
|
@@ -42,7 +43,7 @@ export function connect(a, b, layouts) {
|
|
|
42
43
|
}
|
|
43
44
|
stepEnded(lastLeaf(a), mid.__state);
|
|
44
45
|
if (!isConnected(b))
|
|
45
|
-
stepStarted(b.name, mid.__state);
|
|
46
|
+
stepStarted(b.name, blockKindOf(b), mid.__state);
|
|
46
47
|
await runPrecondition(b.instruction.precondition, mid, page, mem, b.name);
|
|
47
48
|
await b.instruction.act(page, mid, mem);
|
|
48
49
|
},
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BlockKind } from "../block/kind.js";
|
|
1
2
|
export type RunEvent = {
|
|
2
3
|
type: "run:start";
|
|
3
4
|
runId: string;
|
|
@@ -9,6 +10,7 @@ export type RunEvent = {
|
|
|
9
10
|
at: number;
|
|
10
11
|
index: number;
|
|
11
12
|
block: string;
|
|
13
|
+
kind: BlockKind;
|
|
12
14
|
from: string;
|
|
13
15
|
} | {
|
|
14
16
|
type: "step:end";
|
|
@@ -16,6 +18,7 @@ export type RunEvent = {
|
|
|
16
18
|
at: number;
|
|
17
19
|
index: number;
|
|
18
20
|
block: string;
|
|
21
|
+
kind: BlockKind;
|
|
19
22
|
to: string;
|
|
20
23
|
ms: number;
|
|
21
24
|
} | {
|
|
@@ -24,9 +27,18 @@ export type RunEvent = {
|
|
|
24
27
|
at: number;
|
|
25
28
|
index: number;
|
|
26
29
|
block: string;
|
|
30
|
+
kind: BlockKind;
|
|
27
31
|
error: string;
|
|
28
32
|
ms: number;
|
|
29
|
-
} | {
|
|
33
|
+
} | ({
|
|
34
|
+
type: "step:read";
|
|
35
|
+
runId: string;
|
|
36
|
+
at: number;
|
|
37
|
+
index: number;
|
|
38
|
+
block: string;
|
|
39
|
+
kind: BlockKind;
|
|
40
|
+
checkpoint: string;
|
|
41
|
+
} & StepRead) | {
|
|
30
42
|
type: "run:end";
|
|
31
43
|
runId: string;
|
|
32
44
|
at: number;
|
|
@@ -41,22 +53,28 @@ export type RunEvent = {
|
|
|
41
53
|
kind: "video" | "trace" | "screenshot";
|
|
42
54
|
path: string;
|
|
43
55
|
};
|
|
56
|
+
/** What the page showed after a step, from the Read Blocks for the Checkpoint it landed on (beta). */
|
|
57
|
+
export interface StepRead {
|
|
58
|
+
read: Record<string, unknown>;
|
|
59
|
+
missing: string[];
|
|
60
|
+
notes: Record<string, string>;
|
|
61
|
+
readBlocks: string[];
|
|
62
|
+
/** A Read Block that failed (two reading one key, say): the step still ran; the read is empty. */
|
|
63
|
+
error?: string;
|
|
64
|
+
}
|
|
44
65
|
/** What {@link onRunEvent} calls with each event. */
|
|
45
66
|
export type RunEventListener = (event: RunEvent) => void;
|
|
46
67
|
/** Subscribes to every run event in this process; returns the unsubscribe function. */
|
|
47
68
|
export declare function onRunEvent(listener: RunEventListener): () => void;
|
|
48
69
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
70
|
+
* Runs `body` as a run: opens one (`run:start` ... `run:end`), or joins the run its caller is already in (a flow nested in
|
|
71
|
+
* another, a runner driving a chain step by step). `resultOf` names the outcome for `run:end`.
|
|
51
72
|
*/
|
|
52
|
-
export declare function
|
|
53
|
-
|
|
54
|
-
export declare function endRun(opened: boolean, outcome: {
|
|
55
|
-
result?: string;
|
|
56
|
-
error?: unknown;
|
|
57
|
-
}): void;
|
|
58
|
-
export declare function stepStarted(block: string, from: string): void;
|
|
73
|
+
export declare function inRun<T>(label: string | undefined, body: () => Promise<T>, resultOf?: (value: T) => string | undefined): Promise<T>;
|
|
74
|
+
export declare function stepStarted(block: string, kind: BlockKind, from: string): void;
|
|
59
75
|
export declare function stepEnded(block: string, to: string): void;
|
|
76
|
+
/** What the page showed after the current step (emitted between its verify and its `step:end`). */
|
|
77
|
+
export declare function stepRead(checkpoint: string, read: StepRead): void;
|
|
60
78
|
/** The step that started last failed. */
|
|
61
79
|
export declare function stepFailed(error: unknown): void;
|
|
62
80
|
/** The id of the run open right now, if any. */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Run events: what happened, step by step, during a run. Core emits them; subscribers (the run log, the demo overlay,
|
|
2
2
|
// reports, Waygraph UI) listen. Emitting never throws into the run.
|
|
3
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
4
|
const listeners = new Set();
|
|
4
5
|
/** Subscribes to every run event in this process; returns the unsubscribe function. */
|
|
5
6
|
export function onRunEvent(listener) {
|
|
@@ -16,68 +17,81 @@ function emit(event) {
|
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
|
-
let open;
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* The run each async call chain is in. Per chain, not per process: two runs at once (parallel traverse workers, a UI
|
|
22
|
+
* hosting several) each get their own id and step counter; a flow nested in another joins its parent's run.
|
|
23
23
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
/** Closes the run when the matching `beginRun` opened it. */
|
|
35
|
-
export function endRun(opened, outcome) {
|
|
36
|
-
if (!open)
|
|
37
|
-
return;
|
|
38
|
-
open.depth -= 1;
|
|
39
|
-
if (!opened)
|
|
40
|
-
return;
|
|
24
|
+
const scope = new AsyncLocalStorage();
|
|
25
|
+
/**
|
|
26
|
+
* Runs `body` as a run: opens one (`run:start` ... `run:end`), or joins the run its caller is already in (a flow nested in
|
|
27
|
+
* another, a runner driving a chain step by step). `resultOf` names the outcome for `run:end`.
|
|
28
|
+
*/
|
|
29
|
+
export async function inRun(label, body, resultOf) {
|
|
30
|
+
if (scope.getStore())
|
|
31
|
+
return body();
|
|
41
32
|
const at = Date.now();
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
33
|
+
const run = { runId: `${new Date(at).toISOString().replace(/[:.]/g, "-")}-${crypto.randomUUID().slice(0, 8)}`, startedAt: at, index: 0 };
|
|
34
|
+
emit({ type: "run:start", runId: run.runId, at, ...(label ? { label } : {}) });
|
|
35
|
+
const end = (outcome) => {
|
|
36
|
+
const done = Date.now();
|
|
37
|
+
emit({
|
|
38
|
+
type: "run:end",
|
|
39
|
+
runId: run.runId,
|
|
40
|
+
at: done,
|
|
41
|
+
ok: !("error" in outcome),
|
|
42
|
+
...(outcome.result !== undefined ? { result: outcome.result } : {}),
|
|
43
|
+
...("error" in outcome ? { error: outcome.error instanceof Error ? outcome.error.message : String(outcome.error) } : {}),
|
|
44
|
+
ms: done - run.startedAt,
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
try {
|
|
48
|
+
const value = await scope.run(run, body);
|
|
49
|
+
end({ result: resultOf?.(value) });
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
end({ error: err });
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
52
56
|
}
|
|
53
|
-
export function stepStarted(block, from) {
|
|
57
|
+
export function stepStarted(block, kind, from) {
|
|
58
|
+
const open = scope.getStore();
|
|
54
59
|
if (!open)
|
|
55
60
|
return;
|
|
56
61
|
const at = Date.now();
|
|
57
|
-
open.step = { index: open.index++, block, startedAt: at };
|
|
58
|
-
emit({ type: "step:start", runId: open.runId, at, index: open.step.index, block, from });
|
|
62
|
+
open.step = { index: open.index++, block, kind, startedAt: at };
|
|
63
|
+
emit({ type: "step:start", runId: open.runId, at, index: open.step.index, block, kind, from });
|
|
59
64
|
}
|
|
60
65
|
export function stepEnded(block, to) {
|
|
66
|
+
const open = scope.getStore();
|
|
61
67
|
if (!open?.step)
|
|
62
68
|
return;
|
|
63
69
|
const at = Date.now();
|
|
64
|
-
emit({ type: "step:end", runId: open.runId, at, index: open.step.index, block, to, ms: at - open.step.startedAt });
|
|
70
|
+
emit({ type: "step:end", runId: open.runId, at, index: open.step.index, block, kind: open.step.kind, to, ms: at - open.step.startedAt });
|
|
65
71
|
open.step = undefined;
|
|
66
72
|
}
|
|
73
|
+
/** What the page showed after the current step (emitted between its verify and its `step:end`). */
|
|
74
|
+
export function stepRead(checkpoint, read) {
|
|
75
|
+
const open = scope.getStore();
|
|
76
|
+
if (!open?.step)
|
|
77
|
+
return;
|
|
78
|
+
emit({ type: "step:read", runId: open.runId, at: Date.now(), index: open.step.index, block: open.step.block, kind: open.step.kind, checkpoint, ...read });
|
|
79
|
+
}
|
|
67
80
|
/** The step that started last failed. */
|
|
68
81
|
export function stepFailed(error) {
|
|
82
|
+
const open = scope.getStore();
|
|
69
83
|
if (!open?.step)
|
|
70
84
|
return;
|
|
71
85
|
const at = Date.now();
|
|
72
|
-
const { index, block, startedAt } = open.step;
|
|
73
|
-
emit({ type: "step:error", runId: open.runId, at, index, block, error: error instanceof Error ? error.message : String(error), ms: at - startedAt });
|
|
86
|
+
const { index, block, kind, startedAt } = open.step;
|
|
87
|
+
emit({ type: "step:error", runId: open.runId, at, index, block, kind, error: error instanceof Error ? error.message : String(error), ms: at - startedAt });
|
|
74
88
|
open.step = undefined;
|
|
75
89
|
}
|
|
76
90
|
/** The id of the run open right now, if any. */
|
|
77
91
|
export function currentRunId() {
|
|
78
|
-
return
|
|
92
|
+
return scope.getStore()?.runId;
|
|
79
93
|
}
|
|
80
|
-
export function recordArtifact(kind, path, runId =
|
|
94
|
+
export function recordArtifact(kind, path, runId = scope.getStore()?.runId) {
|
|
81
95
|
if (runId)
|
|
82
96
|
emit({ type: "run:artifact", runId, at: Date.now(), kind, path });
|
|
83
97
|
}
|
package/dist/engine/run/run.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { WaygraphError, MSG_PREFLIGHT_MISSING_KEY, MSG_RUNGRAPH_MAX_STEPS, MSG_RUNGRAPH_NOT_TERMINAL } from "../errors.js";
|
|
2
2
|
import { checkpoint } from "../checkpoint/checkpoint.js";
|
|
3
|
-
import {
|
|
3
|
+
import { inRun, isConnected, lastLeaf, stepEnded, stepFailed, stepRead, stepStarted } from "./events.js";
|
|
4
|
+
import { readBlocksFor, readCheckpoint } from "../read/read-block.js";
|
|
5
|
+
import { blockKindOf } from "../block/kind.js";
|
|
4
6
|
import { runPrecondition, runVerify } from "../trait/trait.js";
|
|
5
7
|
/**
|
|
6
8
|
* Checks `block.requires` against `mem` and throws, naming every missing key at
|
|
@@ -78,9 +80,13 @@ export async function runGraph(entry, terminals, context, mem, maxSteps = 5000,
|
|
|
78
80
|
preflight(mem, entry);
|
|
79
81
|
const page = options?.page ?? (await context.newPage());
|
|
80
82
|
const closeOnFinish = options?.closeOnFinish ?? true;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
return inRun(undefined, () => playGraph(entry, terminals, page, mem, maxSteps, options), (out) => out.__state).finally(async () => {
|
|
84
|
+
if (closeOnFinish)
|
|
85
|
+
await page.close();
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/** The step loop of {@link runGraph}, inside its run: act, observe, resolve, verify, then the next Block. */
|
|
89
|
+
async function playGraph(entry, terminals, page, mem, maxSteps, options) {
|
|
84
90
|
try {
|
|
85
91
|
let current = entry;
|
|
86
92
|
let input = checkpoint("__start__");
|
|
@@ -90,7 +96,7 @@ export async function runGraph(entry, terminals, context, mem, maxSteps = 5000,
|
|
|
90
96
|
throw new WaygraphError("WG_RUNGRAPH_MAX_STEPS", MSG_RUNGRAPH_MAX_STEPS(maxSteps));
|
|
91
97
|
}
|
|
92
98
|
if (!isConnected(current))
|
|
93
|
-
stepStarted(current.name, input.__state);
|
|
99
|
+
stepStarted(current.name, blockKindOf(current), input.__state);
|
|
94
100
|
await runPrecondition(current.instruction.precondition, input, page, mem, current.name);
|
|
95
101
|
await current.instruction.act(page, input, mem);
|
|
96
102
|
const observed = current.instruction.observe
|
|
@@ -99,13 +105,13 @@ export async function runGraph(entry, terminals, context, mem, maxSteps = 5000,
|
|
|
99
105
|
const checkpoint = await current.instruction.resolve(observed);
|
|
100
106
|
await runVerify(current.instruction.verify, checkpoint, page, mem, current.name);
|
|
101
107
|
await runLayouts(options?.layouts, checkpoint.__state, checkpoint, page, mem, current.name);
|
|
108
|
+
await readLanding(page, mem, checkpoint.__state);
|
|
102
109
|
stepEnded(lastLeaf(current), checkpoint.__state);
|
|
103
110
|
const next = current.next?.(checkpoint);
|
|
104
111
|
if (!next) {
|
|
105
112
|
if (terminals && !terminals.has(checkpoint.__state)) {
|
|
106
113
|
throw new WaygraphError("WG_RUNGRAPH_NOT_TERMINAL", MSG_RUNGRAPH_NOT_TERMINAL(current.name, checkpoint.__state, [...terminals].join(", ")));
|
|
107
114
|
}
|
|
108
|
-
result = checkpoint.__state;
|
|
109
115
|
return checkpoint;
|
|
110
116
|
}
|
|
111
117
|
current = next;
|
|
@@ -113,16 +119,9 @@ export async function runGraph(entry, terminals, context, mem, maxSteps = 5000,
|
|
|
113
119
|
}
|
|
114
120
|
}
|
|
115
121
|
catch (err) {
|
|
116
|
-
failure = err;
|
|
117
122
|
stepFailed(err);
|
|
118
123
|
throw err;
|
|
119
124
|
}
|
|
120
|
-
finally {
|
|
121
|
-
endRun(opened, failure === undefined ? { ...(result !== undefined ? { result } : {}) } : { error: failure });
|
|
122
|
-
if (closeOnFinish) {
|
|
123
|
-
await page.close();
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
125
|
}
|
|
127
126
|
/**
|
|
128
127
|
* Drives a genuinely separate second tab through its own Block/Flow, in the
|
|
@@ -143,3 +142,16 @@ export async function runGraph(entry, terminals, context, mem, maxSteps = 5000,
|
|
|
143
142
|
export async function spawnTab(entry, page, mem, maxSteps = 5000) {
|
|
144
143
|
return runGraph(entry, undefined, page.context(), mem, maxSteps);
|
|
145
144
|
}
|
|
145
|
+
/** After a step's verify: what the page shows, from the Read Blocks for where it landed (none loaded: nothing to do). */
|
|
146
|
+
async function readLanding(page, mem, checkpoint) {
|
|
147
|
+
if (readBlocksFor(checkpoint).length === 0)
|
|
148
|
+
return;
|
|
149
|
+
try {
|
|
150
|
+
const { read, blocks } = await readCheckpoint(page, mem, checkpoint);
|
|
151
|
+
stepRead(checkpoint, { read: read.toJSON(), missing: [...read.missing], notes: { ...read.notes }, readBlocks: blocks });
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
// A broken Read Block is reported on the step, never fails the run it observes.
|
|
155
|
+
stepRead(checkpoint, { read: {}, missing: [], notes: {}, readBlocks: [], error: err instanceof Error ? err.message : String(err) });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -22,7 +22,7 @@ export type StubPhaseFixtures = {
|
|
|
22
22
|
todos?: readonly WaygraphTodoInput[];
|
|
23
23
|
/** Which todo is current (0-based). Rows before = done, after = pending. */
|
|
24
24
|
todoIndex?: number;
|
|
25
|
-
/** Heading above a single {@link
|
|
25
|
+
/** Heading above a single {@link StubUi.todos} list. */
|
|
26
26
|
todoTitle?: string;
|
|
27
27
|
/**
|
|
28
28
|
* Stable dock id (`data-wg-todo-id`) for external redirect asserts
|
|
@@ -85,15 +85,15 @@ export type StubPhaseFixtures = {
|
|
|
85
85
|
*
|
|
86
86
|
* @example
|
|
87
87
|
* LoginBlock.stubBefore((ctx) => {
|
|
88
|
-
* ctx.todos("sign-in", ["Enter email", "Enter password", "Click Sign in"], { index: 0 });
|
|
89
|
-
* ctx.zoom(1.35);
|
|
90
|
-
* ctx.ring("email", { selector: "#email", label: "Email" });
|
|
91
|
-
* ctx.ring("submit", { selector: "#login-button", label: "Sign in" });
|
|
88
|
+
* ctx.ui.todos("sign-in", ["Enter email", "Enter password", "Click Sign in"], { index: 0 });
|
|
89
|
+
* ctx.ui.zoom(1.35);
|
|
90
|
+
* ctx.ui.ring("email", { selector: "#email", label: "Email" });
|
|
91
|
+
* ctx.ui.ring("submit", { selector: "#login-button", label: "Sign in" });
|
|
92
92
|
* })
|
|
93
93
|
* // Sequential plan: bump todoIndex in a later phase / a later Block's stubBefore
|
|
94
94
|
* .stubAfter((ctx) => {
|
|
95
|
-
* ctx.todoIndex(2);
|
|
96
|
-
* ctx.ring("inventory", { selector: ".inventory_list", label: "Landed" });
|
|
95
|
+
* ctx.ui.todoIndex(2);
|
|
96
|
+
* ctx.ui.ring("inventory", { selector: ".inventory_list", label: "Landed" });
|
|
97
97
|
* });
|
|
98
98
|
*/
|
|
99
99
|
export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
|
|
@@ -107,17 +107,22 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
|
|
|
107
107
|
readonly out: Out | undefined;
|
|
108
108
|
/** Present on stubOnError when the step threw. */
|
|
109
109
|
readonly error: unknown | undefined;
|
|
110
|
+
/** Everything a stub paints in the demo: rings, todos, title, caption, device, zoom, network tab, slides. */
|
|
111
|
+
readonly ui: StubUi;
|
|
112
|
+
};
|
|
113
|
+
/** The demo UI a stub paints through: `ctx.ui.ring(...)`, `ctx.ui.todos(...)`, ... */
|
|
114
|
+
export type StubUi = {
|
|
110
115
|
/** Paints one named ring this phase; the same id again replaces it. */
|
|
111
116
|
ring(id: string, stub: WaygraphHighlightStub): void;
|
|
112
117
|
/**
|
|
113
|
-
* Floating checklist: `ctx.todos("login", ["Enter email", "Enter password"], { title: "Sign in", index: 0 })`.
|
|
118
|
+
* Floating checklist: `ctx.ui.todos("login", ["Enter email", "Enter password"], { title: "Sign in", index: 0 })`.
|
|
114
119
|
* The id is the dock's `data-wg-todo-id` (stable through redirects). One dock at a time (setting todos replaces it);
|
|
115
120
|
* pass `{ parallel: true }` to keep docks with other ids. An empty list hides the dock.
|
|
116
121
|
*/
|
|
117
122
|
todos(id: string, items: readonly WaygraphTodoInput[], opts?: WaygraphTodosOpts): void;
|
|
118
123
|
/** Moves the current dock's current item (sequential plans: a later step bumps it). */
|
|
119
124
|
todoIndex(n: number): void;
|
|
120
|
-
/** Several titled lists in one dock (e.g. Functional Requirements / Scenarios / ACs), with the same id and options as {@link
|
|
125
|
+
/** Several titled lists in one dock (e.g. Functional Requirements / Scenarios / ACs), with the same id and options as {@link StubUi.todos}. */
|
|
121
126
|
todoGroups(id: string, groups: readonly WaygraphTodoGroupInput[], opts?: Omit<WaygraphTodosOpts, "index">): void;
|
|
122
127
|
/**
|
|
123
128
|
* Hide the floating todo dock. Omit todos on a later block = **keep** the
|
|
@@ -127,7 +132,7 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
|
|
|
127
132
|
/**
|
|
128
133
|
* Viewport / device fixture (0.13+). `mobile` | `tablet` | `desktop`.
|
|
129
134
|
* Omit on later blocks = **keep** (same persist as todos). Use
|
|
130
|
-
* {@link
|
|
135
|
+
* {@link StubUi.hideDevice} or `device("desktop")` to return to desktop.
|
|
131
136
|
*/
|
|
132
137
|
device(preset: DevicePreset | DeviceState): void;
|
|
133
138
|
/** Explicit CSS-pixel viewport (implies mobile-ish unless desktop-sized). */
|
|
@@ -158,14 +163,14 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
|
|
|
158
163
|
titlePos(side: BannerPos): void;
|
|
159
164
|
/**
|
|
160
165
|
* Banner UX: `{ collision: false }` keeps it put even over a ring; `{ hidden: true }` hides it.
|
|
161
|
-
* @example ctx.bannerUi({ collision: false });
|
|
166
|
+
* @example ctx.ui.bannerUi({ collision: false });
|
|
162
167
|
*/
|
|
163
168
|
bannerUi(opts: BannerUiOpts): void;
|
|
164
169
|
/**
|
|
165
170
|
* Bottom narration caption - a white rounded card (black text, shadow), bottom-centered above
|
|
166
|
-
* the step pill; distinct from {@link
|
|
167
|
-
* on change. Pass `""` (or call {@link
|
|
168
|
-
* @example ctx.caption("Click Add to cart to continue");
|
|
171
|
+
* the step pill; distinct from {@link StubUi.title}, which is the top banner card. Fades in/out
|
|
172
|
+
* on change. Pass `""` (or call {@link StubUi.hideCaption}) to clear.
|
|
173
|
+
* @example ctx.ui.caption("Click Add to cart to continue");
|
|
169
174
|
*/
|
|
170
175
|
caption(text: string): void;
|
|
171
176
|
/** Clears the caption (fades it out). */
|
|
@@ -173,7 +178,7 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
|
|
|
173
178
|
/**
|
|
174
179
|
* Multi-step "yap" captions with Next between each, for explaining a longer process. In `stubBefore` they play
|
|
175
180
|
* before the step runs, in `stubAfter` after it lands; a slide with a `selector` rings it while it is up.
|
|
176
|
-
* @example ctx.slides([
|
|
181
|
+
* @example ctx.ui.slides([
|
|
177
182
|
* { caption: "Checkout is three screens", tag: "YAP" },
|
|
178
183
|
* { caption: "Overview confirms totals", selector: ".summary_info", duration: true },
|
|
179
184
|
* ]);
|
|
@@ -184,7 +189,7 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
|
|
|
184
189
|
/**
|
|
185
190
|
* Shows the network-tab overlay (Postman-style inspector of real page traffic + defineApiBlock
|
|
186
191
|
* calls) - capture always runs in the background; this just reveals the panel. Hidden by default.
|
|
187
|
-
* @example ctx.showNetworkTab();
|
|
192
|
+
* @example ctx.ui.showNetworkTab();
|
|
188
193
|
*/
|
|
189
194
|
showNetworkTab(): void;
|
|
190
195
|
/** Hides the network-tab overlay (capture keeps running - only the panel disappears). */
|
|
@@ -193,7 +198,7 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
|
|
|
193
198
|
* Todo-dock UX: compact fold, collision flip, dim-behind-ring.
|
|
194
199
|
* Defaults are smart-on; pass false fields to opt out for this phase.
|
|
195
200
|
* @example
|
|
196
|
-
* ctx.todoDockUi({ compact: false }); // always show the full list
|
|
201
|
+
* ctx.ui.todoDockUi({ compact: false }); // always show the full list
|
|
197
202
|
*/
|
|
198
203
|
todoDockUi(opts: TodoDockUiOpts): void;
|
|
199
204
|
};
|
|
@@ -213,7 +218,7 @@ export type StubPhaseResult = {
|
|
|
213
218
|
* - `keep` - author omitted todos; demo must leave previous dock alone
|
|
214
219
|
*/
|
|
215
220
|
todoSync?: "set" | "clear" | "keep";
|
|
216
|
-
/** Todo-dock UX for this phase (when author called {@link
|
|
221
|
+
/** Todo-dock UX for this phase (when author called {@link StubUi.todoDockUi}). */
|
|
217
222
|
todoDockUi?: TodoDockUiOpts;
|
|
218
223
|
/** Resolved device after this phase (undefined when keep with no prior). */
|
|
219
224
|
device?: DeviceState;
|
|
@@ -231,7 +236,7 @@ export type StubPhaseResult = {
|
|
|
231
236
|
bannerUi?: BannerUiOpts;
|
|
232
237
|
caption?: string;
|
|
233
238
|
networkTabVisible?: boolean;
|
|
234
|
-
/** Slides to play in this phase ({@link
|
|
239
|
+
/** Slides to play in this phase ({@link StubUi.slides}); empty when the stub set none. */
|
|
235
240
|
slides: WaygraphSlide[];
|
|
236
241
|
todoPos?: "left" | "right";
|
|
237
242
|
/** When true, keep other docks (multi-todo). Default false = replace. */
|
|
@@ -256,9 +261,9 @@ export type StubBagState = {
|
|
|
256
261
|
/** Author asked to reset to desktop. */
|
|
257
262
|
hideDevice?: boolean | undefined;
|
|
258
263
|
device?: DeviceState | undefined;
|
|
259
|
-
/** Touch override when set via ctx.touch() alone or with device. */
|
|
264
|
+
/** Touch override when set via ctx.ui.touch() alone or with device. */
|
|
260
265
|
touchOverride?: boolean | undefined;
|
|
261
|
-
/** Orientation override when set via ctx.orientation / landscape / portrait. */
|
|
266
|
+
/** Orientation override when set via ctx.ui.orientation / landscape / portrait. */
|
|
262
267
|
orientationOverride?: DeviceOrientation | undefined;
|
|
263
268
|
zoom?: number | undefined;
|
|
264
269
|
zoomOut?: boolean | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import "./runtime/full-launch.js";
|
|
2
|
-
import "./runtime/run-log.js";
|
|
3
1
|
export type { Checkpoint, Start } from "./engine/checkpoint/checkpoint.js";
|
|
4
2
|
export type { Instruction, Block, DefinedBlock, SomeBlock, SomeDefinedBlock } from "./engine/block/block.js";
|
|
5
3
|
export type { WaygraphInstanceOption } from "./engine/block/action-page.js";
|
|
@@ -55,7 +53,7 @@ export { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./ana
|
|
|
55
53
|
export { buildManifest } from "./manifest/manifest.js";
|
|
56
54
|
export { onRunEvent } from "./engine/run/events.js";
|
|
57
55
|
export type { RunEvent } from "./engine/run/events.js";
|
|
58
|
-
export type { Manifest, ManifestBlock, ManifestFlow } from "./manifest/manifest.js";
|
|
56
|
+
export type { Manifest, ManifestBlock, ManifestFlow, ManifestReadBlock } from "./manifest/manifest.js";
|
|
59
57
|
export type { WaygraphGraph, WaygraphNode, WaygraphEdge, SkippedBlock, OrphanBlock } from "./analysis/graph.js";
|
|
60
58
|
export { isFileSelectToken, parseBlocksSelect, matchBlocksSelect, globToRegExp } from "./manifest/select.js";
|
|
61
59
|
export type { BlocksSelect, BlocksSelectKind } from "./manifest/select.js";
|
|
@@ -67,6 +65,10 @@ export type { TraverseCoverageReport } from "./live/traverse/coverage.js";
|
|
|
67
65
|
export { WG_UI_SEL, WG_READY_PANEL_SEL, WG_READY_AUTO_SEL, WG_READY_BANNER_SEL, readWgOverlayBeacon, readWgOverlayBeaconInPage, assertWgOverlayReady } from "./demo/overlay/beacon.js";
|
|
68
66
|
export type { WgModalKind, WgBeaconRow } from "./demo/overlay/beacon.js";
|
|
69
67
|
export { AutoSession } from "./live/auto-session/auto-session.js";
|
|
68
|
+
export { SESSION_OPS, SESSION_PROTOCOL_VERSION } from "./live/auto-session/protocol.js";
|
|
69
|
+
export type { SessionRequest, SessionResponse, SessionStreamMessage, HelloResponse } from "./live/auto-session/protocol.js";
|
|
70
|
+
export { subscribeSession } from "./live/auto-session/feed.js";
|
|
71
|
+
export { requestSession } from "./live/auto-session/ipc.js";
|
|
70
72
|
export { stubFilePath } from "./live/auto-session/helpers.js";
|
|
71
73
|
export type { AutoSessionInit, SessionSnapshot, SessionSnapshotEdge, SessionSnapshotSection, ApplyPickResult, ApplyPathResult, TraceStep, ConsoleLogEntry, StorageSnapshot, ServiceWorkerRegistrationInfo, CookieInfo, StubFileKind } from "./live/auto-session/helpers.js";
|
|
72
74
|
export { pilotStart } from "./live/pilot/pilot.js";
|
|
@@ -83,7 +85,8 @@ export type { ConnectLayout } from "./engine/run/connect.js";
|
|
|
83
85
|
export type { EffectExtras } from "./engine/block/kinds/effect.js";
|
|
84
86
|
export type { BlockBase, RoutedBlockBase } from "./engine/checkpoint/route.js";
|
|
85
87
|
export type { RunEventListener } from "./engine/run/events.js";
|
|
86
|
-
export type {
|
|
88
|
+
export type { ScanOptions } from "./manifest/project.js";
|
|
89
|
+
export type { BlockKind } from "./engine/block/kind.js";
|
|
87
90
|
export type { DomSnapshot, InspectDomOptions, InspectDomResult } from "./live/auto-session/helpers.js";
|
|
88
91
|
export type { BrowserStartInit } from "./live/auto-session/start.js";
|
|
89
92
|
export type { PilotFixtureRing, PilotHighlightFixtures } from "./demo/overlay/pilot/fixtures.js";
|