waygraph 0.15.42 → 0.15.44
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/analysis/coverage-gap.d.ts +11 -0
- package/dist/analysis/coverage-gap.js +119 -0
- package/dist/analysis/graph.d.ts +92 -0
- package/dist/analysis/graph.js +467 -0
- package/dist/analysis/map-check.d.ts +18 -0
- package/dist/analysis/map-check.js +140 -0
- package/dist/analysis/practices-check.d.ts +14 -0
- package/dist/analysis/practices-check.js +195 -0
- package/dist/auto-explore.d.ts +1 -1
- package/dist/auto-explore.js +1 -1
- package/dist/auto-session/session.js +2 -2
- package/dist/cli/util.d.ts +8 -1
- package/dist/cli/util.js +9 -2
- package/dist/commands/check.js +2 -2
- package/dist/commands/map.js +1 -1
- package/dist/commands/session.js +2 -2
- package/dist/commands/traverse.js +2 -2
- package/dist/engine/branch-regression.js +2 -1
- package/dist/engine/compose.js +3 -2
- package/dist/engine/core.js +5 -4
- package/dist/engine/engine-class.js +13 -12
- package/dist/engine/flow.js +4 -3
- package/dist/engine/run-graph.js +4 -3
- package/dist/errors.d.ts +122 -0
- package/dist/errors.js +125 -0
- package/dist/highlights/shorthand.d.ts +24 -0
- package/dist/highlights/shorthand.js +30 -0
- package/dist/index.d.ts +9 -7
- package/dist/index.js +5 -4
- package/dist/mem-page.js +2 -1
- package/dist/pilot.d.ts +1 -1
- package/dist/pilot.js +1 -1
- package/dist/practices-check.js +7 -5
- package/dist/traverse/coverage.d.ts +59 -0
- package/dist/traverse/coverage.js +127 -0
- package/dist/traverse/lease.d.ts +18 -0
- package/dist/traverse/lease.js +62 -0
- package/dist/traverse/run.d.ts +47 -0
- package/dist/traverse/run.js +439 -0
- package/examples/saucedemo/.waygraph-auto/050e5b55.log +0 -0
- package/examples/saucedemo/.waygraph-auto/096fea05.log +20 -0
- package/examples/saucedemo/.waygraph-auto/123d1cad.log +5 -0
- package/examples/saucedemo/.waygraph-auto/12ed8e28.log +0 -0
- package/examples/saucedemo/.waygraph-auto/1cb6f44c.log +5 -0
- package/examples/saucedemo/.waygraph-auto/1fdb2100.log +0 -0
- package/examples/saucedemo/.waygraph-auto/573abedd.log +0 -0
- package/examples/saucedemo/.waygraph-auto/9a26134c.log +20 -0
- package/examples/saucedemo/.waygraph-auto/aa226dd0.log +22 -0
- package/examples/saucedemo/.waygraph-auto/ac5dde3d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/cae91469.log +0 -0
- package/examples/saucedemo/.waygraph-auto/d846cd78.log +0 -0
- package/examples/saucedemo/.waygraph-auto/eb38d92e.log +22 -0
- package/examples/saucedemo/.waygraph-auto/ee86203e.log +0 -0
- package/package.json +7 -4
- package/templates/scaffold/scripts/README.md +13 -0
- package/templates/scaffold/src/flows/README.md +18 -0
- package/templates/scaffold/src/map/README.md +31 -0
- package/templates/scaffold/src/states/README.md +15 -0
- package/templates/scaffold/tests/README.md +10 -0
- package/templates/skills/waygraph-convention.skill.md +6 -4
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BlockEntry } from "../auto-explore.js";
|
|
2
|
+
/** Collect selector strings from NavClick blocks and Block/*Sel source files. */
|
|
3
|
+
export declare function collectKnownInteractionSelectors(projectDir: string, library: {
|
|
4
|
+
navBlocks: BlockEntry[];
|
|
5
|
+
}, inject?: string[]): string[];
|
|
6
|
+
export interface UnmappedInteractionPayload {
|
|
7
|
+
knownPathnames: string[];
|
|
8
|
+
knownSelectors: string[];
|
|
9
|
+
}
|
|
10
|
+
/** Page-side: warn once per unmapped same-origin href or visible button. */
|
|
11
|
+
export declare function unmappedInteractionsPageScript(payload: UnmappedInteractionPayload): void;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime coverage-gap helpers: detect live page links/buttons with no Block
|
|
3
|
+
* selector or NavBlock URL in the loaded library (surfaced via console.warn →
|
|
4
|
+
* `auto console`, same channel as unmapped href detection).
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { exploreRoots } from "../block-inject.js";
|
|
9
|
+
const SELECTOR_LITERAL = /(?:locator|visible|click|fill|press|selector|getByRole)\(\s*['"`]([^'"`]+)['"`]/g;
|
|
10
|
+
const HASH_ID = /#([a-zA-Z][\w-]*)/g;
|
|
11
|
+
const DATA_TEST = /\[data-test(?:-id)?(?:=["'`][^"'`]+["'`])?\]/g;
|
|
12
|
+
function walkBlockAndSelFiles(dir, acc = []) {
|
|
13
|
+
for (const name of readdirSync(dir)) {
|
|
14
|
+
const p = join(dir, name);
|
|
15
|
+
try {
|
|
16
|
+
const st = statSync(p);
|
|
17
|
+
if (st.isDirectory()) {
|
|
18
|
+
if (name === "node_modules" || name === "dist" || name.startsWith("."))
|
|
19
|
+
continue;
|
|
20
|
+
walkBlockAndSelFiles(p, acc);
|
|
21
|
+
}
|
|
22
|
+
else if (/\.(block|sel)\.ts$/.test(name)) {
|
|
23
|
+
acc.push(p);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
/* ignore */
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return acc;
|
|
31
|
+
}
|
|
32
|
+
/** Collect selector strings from NavClick blocks and Block/*Sel source files. */
|
|
33
|
+
export function collectKnownInteractionSelectors(projectDir, library, inject) {
|
|
34
|
+
const out = new Set();
|
|
35
|
+
for (const entry of library.navBlocks) {
|
|
36
|
+
const click = entry.block
|
|
37
|
+
.__waygraphNavClick;
|
|
38
|
+
if (typeof click === "string")
|
|
39
|
+
out.add(click);
|
|
40
|
+
}
|
|
41
|
+
for (const root of exploreRoots(projectDir, inject)) {
|
|
42
|
+
for (const file of walkBlockAndSelFiles(root)) {
|
|
43
|
+
const src = readFileSync(file, "utf-8");
|
|
44
|
+
for (const re of [SELECTOR_LITERAL, HASH_ID, DATA_TEST]) {
|
|
45
|
+
re.lastIndex = 0;
|
|
46
|
+
let m;
|
|
47
|
+
while ((m = re.exec(src))) {
|
|
48
|
+
const sel = m[0].startsWith("#") || m[0].startsWith("[") ? m[0] : (m[1] ?? m[0]);
|
|
49
|
+
if (sel.startsWith("#") || sel.includes("data-test") || sel.includes("[")) {
|
|
50
|
+
out.add(sel.startsWith("#") || sel.startsWith("[") ? sel : `#${sel}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return Array.from(out);
|
|
57
|
+
}
|
|
58
|
+
/** Page-side: warn once per unmapped same-origin href or visible button. */
|
|
59
|
+
export function unmappedInteractionsPageScript(payload) {
|
|
60
|
+
const w = window;
|
|
61
|
+
w.__wgWarnedPaths ??= new Set();
|
|
62
|
+
w.__wgWarnedButtons ??= new Set();
|
|
63
|
+
const matchesKnown = (el, selectors) => {
|
|
64
|
+
for (const sel of selectors) {
|
|
65
|
+
try {
|
|
66
|
+
if (el.matches(sel))
|
|
67
|
+
return true;
|
|
68
|
+
if (document.querySelector(sel) === el)
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
/* invalid selector in library — skip */
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
};
|
|
77
|
+
const seenPaths = new Set();
|
|
78
|
+
for (const a of Array.from(document.querySelectorAll("a[href]"))) {
|
|
79
|
+
let url;
|
|
80
|
+
try {
|
|
81
|
+
url = new URL(a.getAttribute("href") || "", location.href);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (url.origin !== location.origin)
|
|
87
|
+
continue;
|
|
88
|
+
const path = url.pathname;
|
|
89
|
+
if (seenPaths.has(path) || payload.knownPathnames.includes(path) || w.__wgWarnedPaths.has(path)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
seenPaths.add(path);
|
|
93
|
+
w.__wgWarnedPaths.add(path);
|
|
94
|
+
console.warn(`[waygraph] unmapped nav link on this page: ${path} - no NavBlock covers this URL; add defineNavBlock`);
|
|
95
|
+
}
|
|
96
|
+
const buttonLike = Array.from(document.querySelectorAll('button, [role="button"], input[type="submit"], input[type="button"]')).filter((el) => {
|
|
97
|
+
const style = window.getComputedStyle(el);
|
|
98
|
+
return style.visibility !== "hidden" && style.display !== "none";
|
|
99
|
+
});
|
|
100
|
+
for (const el of buttonLike) {
|
|
101
|
+
const id = el.id;
|
|
102
|
+
const dt = el.getAttribute("data-test") ?? el.getAttribute("data-testid");
|
|
103
|
+
const name = el.getAttribute("name");
|
|
104
|
+
const label = id
|
|
105
|
+
? `#${id}`
|
|
106
|
+
: dt
|
|
107
|
+
? `[data-test="${dt}"]`
|
|
108
|
+
: name
|
|
109
|
+
? `[name="${name}"]`
|
|
110
|
+
: el.textContent?.trim().slice(0, 40) || el.tagName.toLowerCase();
|
|
111
|
+
const key = `${el.tagName}:${label}`;
|
|
112
|
+
if (w.__wgWarnedButtons.has(key))
|
|
113
|
+
continue;
|
|
114
|
+
if (matchesKnown(el, payload.knownSelectors))
|
|
115
|
+
continue;
|
|
116
|
+
w.__wgWarnedButtons.add(key);
|
|
117
|
+
console.warn(`[waygraph] unmapped button on this page: ${label} - no Method/NavClick Block selector matches; consider defineMethodBlock or defineNavClickBlock`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { type BlocksSelect } from "../blocks-select.js";
|
|
2
|
+
/**
|
|
3
|
+
* The state-machine shape `waygraph auto` discovers from a project's own
|
|
4
|
+
* Blocks - Checkpoints are nodes, Blocks are edges. Kept as plain data (not
|
|
5
|
+
* classes/functions) so it's trivially JSON-serializable for a script/agent
|
|
6
|
+
* reading `waygraph auto`'s stdout.
|
|
7
|
+
*/
|
|
8
|
+
export interface WaygraphNode {
|
|
9
|
+
checkpoint: string;
|
|
10
|
+
}
|
|
11
|
+
export interface WaygraphEdge {
|
|
12
|
+
/** The Block's own runtime `.name`. */
|
|
13
|
+
block: string;
|
|
14
|
+
/** Path the Block was discovered in, relative to the project root. */
|
|
15
|
+
file: string;
|
|
16
|
+
/** `"*"` for a NavBlock - reachable from anywhere, by design. */
|
|
17
|
+
from: string;
|
|
18
|
+
to: string;
|
|
19
|
+
kind: "nav" | "action";
|
|
20
|
+
}
|
|
21
|
+
export interface SkippedBlock {
|
|
22
|
+
block: string;
|
|
23
|
+
file: string;
|
|
24
|
+
reason: string;
|
|
25
|
+
}
|
|
26
|
+
export interface WaygraphGraph {
|
|
27
|
+
nodes: WaygraphNode[];
|
|
28
|
+
edges: WaygraphEdge[];
|
|
29
|
+
/** Blocks whose Out tag couldn't be statically resolved - not a crash, just excluded. */
|
|
30
|
+
skipped: SkippedBlock[];
|
|
31
|
+
}
|
|
32
|
+
/** A Block export that no `.flow.ts` defineFlow array references yet. */
|
|
33
|
+
export interface OrphanBlock {
|
|
34
|
+
/** Export identifier in the `.block.ts` file. */
|
|
35
|
+
exportName: string;
|
|
36
|
+
/** Runtime Block `.name` when import succeeds; else derived from export. */
|
|
37
|
+
block: string;
|
|
38
|
+
/** Path relative to the project root. */
|
|
39
|
+
file: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Renders a `WaygraphGraph` as Mermaid `stateDiagram-v2` text - paste
|
|
43
|
+
* straight into a `.md` file's ```mermaid fence or a live editor. `"*"`
|
|
44
|
+
* renders as Mermaid's own `[*]` start marker, matching how a NavBlock's
|
|
45
|
+
* `from` already means "reachable from anywhere."
|
|
46
|
+
* @example console.log(toMermaid(graph))
|
|
47
|
+
*/
|
|
48
|
+
export declare function toMermaid(graph: WaygraphGraph): string;
|
|
49
|
+
/**
|
|
50
|
+
* Blocks present on disk but never wired into a `.flow.ts` defineFlow array.
|
|
51
|
+
* Required before `chain auto` can build a path from the discovered graph -
|
|
52
|
+
* orphan Blocks are intentionally excluded from auto shorthand. Arrival-only
|
|
53
|
+
* Page hubs and Blocks listed on a Page's `methods` are not orphans.
|
|
54
|
+
*/
|
|
55
|
+
export declare function findOrphanBlocks(projectDir: string): Promise<OrphanBlock[]>;
|
|
56
|
+
/**
|
|
57
|
+
* Shortest Block-name path between two Checkpoint tags using `discoverGraph`
|
|
58
|
+
* edges. NavBlocks (`from: "*"`) count from any state.
|
|
59
|
+
*/
|
|
60
|
+
export declare function findBlockPath(graph: WaygraphGraph, fromTag: string, toTag: string): string[] | null;
|
|
61
|
+
export interface BlockPathStep {
|
|
62
|
+
block: string;
|
|
63
|
+
/** The specific Checkpoint this hop's edge targets - NOT re-derivable by
|
|
64
|
+
* looking up `block` by name afterward, since a union-Out Block (e.g.
|
|
65
|
+
* "Union Out tags create duplicate edges for the same Block", see
|
|
66
|
+
* `discoverGraph`'s own comment above) can have several edges with the
|
|
67
|
+
* same `block` name but different `to` tags; only the specific edge BFS
|
|
68
|
+
* actually routed through here is correct. */
|
|
69
|
+
to: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Same BFS as `findBlockPath`, but returns each hop's specific edge (block
|
|
73
|
+
* name + the Checkpoint it targets), not just the block name - needed by any
|
|
74
|
+
* caller that must verify a step actually landed where the route intended,
|
|
75
|
+
* not just re-guess it afterward. `findBlockPath` itself is unchanged and
|
|
76
|
+
* still used wherever only the block-name sequence matters.
|
|
77
|
+
*/
|
|
78
|
+
export declare function findBlockPathDetailed(graph: WaygraphGraph, fromTag: string, toTag: string): BlockPathStep[] | null;
|
|
79
|
+
/**
|
|
80
|
+
* Walks every `*.block.ts` file under `projectDir` and builds the app's own
|
|
81
|
+
* state graph. Shares the same in-process-import limitation `waygraph
|
|
82
|
+
* check` already has: a target project whose own `waygraph`/`playwright`
|
|
83
|
+
* are genuinely separate installs (not symlinked into this same
|
|
84
|
+
* node_modules resolution) can crash Playwright's own "second copy" guard
|
|
85
|
+
* on import - acceptable for an introspection command the same way it
|
|
86
|
+
* already is for `check`, unlike `chain`, which spawns a child process
|
|
87
|
+
* specifically because it also EXECUTES Blocks, not just imports them.
|
|
88
|
+
*/
|
|
89
|
+
export declare function discoverGraph(projectDir: string, opts?: {
|
|
90
|
+
blocksSelect?: BlocksSelect;
|
|
91
|
+
inject?: string[];
|
|
92
|
+
}): Promise<WaygraphGraph>;
|