waygraph 0.15.23 → 0.15.24
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/cli/discover.d.ts +40 -0
- package/dist/cli/discover.js +148 -0
- package/dist/cli/flags.d.ts +91 -0
- package/dist/cli/flags.js +442 -0
- package/dist/cli/run-chain.d.ts +8 -0
- package/dist/cli/run-chain.js +67 -0
- package/dist/cli/usage.d.ts +4 -0
- package/dist/cli/usage.js +236 -0
- package/dist/cli/util.d.ts +4 -0
- package/dist/cli/util.js +24 -0
- package/dist/cli.js +45 -2448
- package/dist/commands/agent-dive.d.ts +1 -0
- package/dist/commands/agent-dive.js +52 -0
- package/dist/commands/check.d.ts +4 -0
- package/dist/commands/check.js +192 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +54 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.js +67 -0
- package/dist/commands/map.d.ts +1 -0
- package/dist/commands/map.js +29 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.js +104 -0
- package/dist/commands/session.d.ts +2 -0
- package/dist/commands/session.js +600 -0
- package/dist/commands/test.d.ts +1 -0
- package/dist/commands/test.js +33 -0
- package/dist/commands/traverse.d.ts +1 -0
- package/dist/commands/traverse.js +177 -0
- package/dist/commands/try.d.ts +1 -0
- package/dist/commands/try.js +298 -0
- package/examples/saucedemo/.waygraph-auto/0ac58118.log +0 -0
- package/examples/saucedemo/.waygraph-auto/309c107f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/3ab7c7bd.log +44 -0
- package/examples/saucedemo/.waygraph-auto/45864828.log +0 -0
- package/examples/saucedemo/.waygraph-auto/95bd8371.log +0 -0
- package/examples/saucedemo/.waygraph-auto/bcb9e31a.log +0 -0
- package/examples/saucedemo/.waygraph-auto/cd3e528c.log +0 -0
- package/examples/saucedemo/.waygraph-auto/dce1535d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/e280bf20.log +0 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function agentDiveCase(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
|
|
2
|
+
import { runAgentDive } from "../agent-dive.js";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { existsSync, statSync } from "node:fs";
|
|
5
|
+
export async function agentDiveCase(args) {
|
|
6
|
+
// Playwright analogue: npx playwright init-agents --loop <provider>
|
|
7
|
+
const rest = args.slice(1);
|
|
8
|
+
let loop = "claude";
|
|
9
|
+
let prompts = false;
|
|
10
|
+
let projectDir = process.cwd();
|
|
11
|
+
for (let i = 0; i < rest.length; i++) {
|
|
12
|
+
const a = rest[i];
|
|
13
|
+
if (a === "--prompts") {
|
|
14
|
+
prompts = true;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (a === "--loop" || a.startsWith("--loop=")) {
|
|
18
|
+
const v = a.startsWith("--loop=") ? a.slice("--loop=".length) : rest[++i];
|
|
19
|
+
if (!v || !["claude", "opencode", "cursor", "vscode"].includes(v)) {
|
|
20
|
+
console.error("waygraph agent-dive: --loop must be claude | opencode | cursor | vscode");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
loop = v;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (a === "--help" || a === "-h") {
|
|
27
|
+
console.log(`waygraph agent-dive [--loop claude|opencode|cursor|vscode] [--prompts] [project]
|
|
28
|
+
|
|
29
|
+
Initialize coding-agent definitions for diving an app into waygraph Blocks
|
|
30
|
+
(Playwright \`init-agents\` analogue).
|
|
31
|
+
|
|
32
|
+
--loop claude write .claude/agents/*.md (default)
|
|
33
|
+
--loop opencode write .opencode/prompts/*.md
|
|
34
|
+
--loop cursor write .cursor/rules/waygraph-*.mdc
|
|
35
|
+
--loop vscode write .github/agents/*.md
|
|
36
|
+
--prompts also copy docs/waygraph-agents/*.md
|
|
37
|
+
|
|
38
|
+
Agents shipped: waygraph-planner, waygraph-author, waygraph-healer.
|
|
39
|
+
`);
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
if (!a.startsWith("-")) {
|
|
43
|
+
projectDir = resolve(a);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!existsSync(projectDir) || !statSync(projectDir).isDirectory()) {
|
|
47
|
+
console.error(`waygraph agent-dive: no such directory: ${projectDir}`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
runAgentDive({ loop, projectDir, prompts });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function runChainAuto(projectDir: string, fromTag: string, toTag: string): Promise<void>;
|
|
2
|
+
export declare function validateCase(args: string[]): Promise<void>;
|
|
3
|
+
export declare function typecheckCase(args: string[]): Promise<void>;
|
|
4
|
+
export declare function checkCase(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
|
|
2
|
+
import { INLINE_SELECTOR_CALL, NAV_METHOD_CALLS, discoverBlocks, discoverFlows, importFlowFile, importModule, isBlockLike, isFlowLike, isNavBlockMarked, walkDir } from "../cli/discover.js";
|
|
3
|
+
import { basename, relative, resolve } from "node:path";
|
|
4
|
+
import { discoverGraph, findBlockPath, findOrphanBlocks } from "../graph.js";
|
|
5
|
+
import { parseRunFlags, resolveBaseUrl } from "../cli/flags.js";
|
|
6
|
+
import { runChain } from "../cli/run-chain.js";
|
|
7
|
+
import { collectPracticeWarnings, practiceKindLabel } from "../practices-check.js";
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
async function validateFlows(projectDir) {
|
|
10
|
+
const files = discoverFlows(projectDir);
|
|
11
|
+
const results = [];
|
|
12
|
+
for (const file of files) {
|
|
13
|
+
try {
|
|
14
|
+
const mod = await importFlowFile(file);
|
|
15
|
+
for (const [exportName, exported] of Object.entries(mod)) {
|
|
16
|
+
if (isFlowLike(exported)) {
|
|
17
|
+
results.push({ file, name: exportName, valid: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
23
|
+
results.push({ file, name: basename(file, ".ts"), valid: false, error: msg });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return results;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Walks every `*.block.ts` file under `projectDir`, imports each to find its
|
|
30
|
+
* exported Blocks, and for any Block NOT built via `defineNavBlock`, scans
|
|
31
|
+
* that file's own source text for a navigation call. Warning only - never
|
|
32
|
+
* throws, never changes the caller's exit code on its own account (a Block
|
|
33
|
+
* file that fails to import for unrelated reasons is silently skipped here;
|
|
34
|
+
* `validate` is the command that reports import failures).
|
|
35
|
+
*/
|
|
36
|
+
function printOrphanReport(projectDir, orphans) {
|
|
37
|
+
if (orphans.length === 0) {
|
|
38
|
+
console.log(`waygraph check: no orphan Blocks (every *.block.ts export is wired into a flow)`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
for (const o of orphans) {
|
|
42
|
+
console.warn(`waygraph check: orphan Block ${o.file} (${o.exportName} / "${o.block}") is not referenced in any defineFlow([...]) - wire it into a .flow.ts before chain auto shorthand`);
|
|
43
|
+
}
|
|
44
|
+
console.log(`waygraph check: ${orphans.length} orphan Block${orphans.length === 1 ? "" : "s"}`);
|
|
45
|
+
}
|
|
46
|
+
async function requireNoOrphans(projectDir, forCommand) {
|
|
47
|
+
const orphans = await findOrphanBlocks(projectDir);
|
|
48
|
+
if (orphans.length === 0)
|
|
49
|
+
return true;
|
|
50
|
+
console.error(`waygraph ${forCommand}: ${orphans.length} orphan Block(s) - wire every Block into a .flow.ts before auto shorthand. Run: waygraph check ${projectDir === process.cwd() ? "." : JSON.stringify(projectDir)}`);
|
|
51
|
+
for (const o of orphans) {
|
|
52
|
+
console.error(` - ${o.file} (${o.exportName} / "${o.block}")`);
|
|
53
|
+
}
|
|
54
|
+
process.exitCode = 1;
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
export async function runChainAuto(projectDir, fromTag, toTag) {
|
|
58
|
+
if (!(await requireNoOrphans(projectDir, "chain auto")))
|
|
59
|
+
return;
|
|
60
|
+
const graph = await discoverGraph(projectDir);
|
|
61
|
+
const path = findBlockPath(graph, fromTag, toTag);
|
|
62
|
+
if (!path) {
|
|
63
|
+
console.error(`waygraph chain auto: no Block path from "${fromTag}" to "${toTag}" in the discovered graph`);
|
|
64
|
+
process.exitCode = 1;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const spec = path.join(" then ");
|
|
68
|
+
console.error(`waygraph chain auto: ${fromTag} -> ${toTag} via ${spec}`);
|
|
69
|
+
if (!process.env.WAYGRAPH_BASE_URL) {
|
|
70
|
+
const resolved = resolveBaseUrl(projectDir);
|
|
71
|
+
if (resolved)
|
|
72
|
+
process.env.WAYGRAPH_BASE_URL = resolved;
|
|
73
|
+
}
|
|
74
|
+
await runChain(projectDir, spec);
|
|
75
|
+
}
|
|
76
|
+
function printPracticeReport(projectDir, practiceWarnings) {
|
|
77
|
+
if (practiceWarnings.length === 0) {
|
|
78
|
+
console.log(`waygraph check: no bad-practice patterns under ${projectDir}`);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
for (const w of practiceWarnings) {
|
|
82
|
+
console.warn(`waygraph check: ${w.file} (${practiceKindLabel(w.kind)}) — ${w.detail}`);
|
|
83
|
+
}
|
|
84
|
+
console.log(`waygraph check: ${practiceWarnings.length} bad-practice warning${practiceWarnings.length === 1 ? "" : "s"}`);
|
|
85
|
+
}
|
|
86
|
+
async function checkCommand(projectDir, opts) {
|
|
87
|
+
const files = discoverBlocks(projectDir);
|
|
88
|
+
const navWarnings = [];
|
|
89
|
+
const selWarnings = [];
|
|
90
|
+
for (const file of files) {
|
|
91
|
+
let mod;
|
|
92
|
+
try {
|
|
93
|
+
mod = await importModule(file);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
let src;
|
|
99
|
+
for (const [exportName, exported] of Object.entries(mod)) {
|
|
100
|
+
if (!isBlockLike(exported))
|
|
101
|
+
continue;
|
|
102
|
+
src ??= readFileSync(file, "utf-8");
|
|
103
|
+
// Nav-escape: NavBlocks are exempt (their generated act() is the one
|
|
104
|
+
// legitimate goto/click call site).
|
|
105
|
+
if (!isNavBlockMarked(exported) &&
|
|
106
|
+
NAV_METHOD_CALLS.some((needle) => src.includes(needle))) {
|
|
107
|
+
navWarnings.push({ file, blockName: exported.name, exportName });
|
|
108
|
+
}
|
|
109
|
+
// Inline selector: applies to every Block kind, including NavBlocks -
|
|
110
|
+
// a Nav's own `verify` array is just as likely to inline a selector.
|
|
111
|
+
if (INLINE_SELECTOR_CALL.test(src)) {
|
|
112
|
+
selWarnings.push({ file, blockName: exported.name, exportName });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const practiceWarnings = collectPracticeWarnings(projectDir, walkDir, {
|
|
117
|
+
disabled: opts?.noPractices === true,
|
|
118
|
+
});
|
|
119
|
+
return { navWarnings, selWarnings, practiceWarnings };
|
|
120
|
+
}
|
|
121
|
+
async function runTypecheckCommand(projectDir, noPractices) {
|
|
122
|
+
const { spawnSync } = await import("node:child_process");
|
|
123
|
+
const tsc = spawnSync("npx", ["tsc", "--noEmit"], {
|
|
124
|
+
cwd: projectDir,
|
|
125
|
+
stdio: "inherit",
|
|
126
|
+
shell: false,
|
|
127
|
+
});
|
|
128
|
+
if (tsc.status !== 0) {
|
|
129
|
+
process.exit(tsc.status === null ? 1 : tsc.status);
|
|
130
|
+
}
|
|
131
|
+
if (noPractices)
|
|
132
|
+
return;
|
|
133
|
+
const practiceWarnings = collectPracticeWarnings(projectDir, walkDir);
|
|
134
|
+
printPracticeReport(projectDir, practiceWarnings);
|
|
135
|
+
}
|
|
136
|
+
export async function validateCase(args) {
|
|
137
|
+
const proj = resolve(args[1] ?? process.cwd());
|
|
138
|
+
const results = await validateFlows(proj);
|
|
139
|
+
let failed = 0;
|
|
140
|
+
for (const r of results) {
|
|
141
|
+
const rel = relative(proj, r.file);
|
|
142
|
+
if (r.valid) {
|
|
143
|
+
console.log(`OK ${rel} ${r.name}`);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
failed++;
|
|
147
|
+
console.log(`FAIL ${rel} ${r.name} -- ${r.error}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (failed > 0)
|
|
151
|
+
process.exitCode = 1;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
export async function typecheckCase(args) {
|
|
155
|
+
const flags = parseRunFlags(args.slice(1));
|
|
156
|
+
const proj = resolve(flags.positionals[0] ?? process.cwd());
|
|
157
|
+
await runTypecheckCommand(proj, flags.noPractices === true);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
export async function checkCase(args) {
|
|
161
|
+
const flags = parseRunFlags(args.slice(1));
|
|
162
|
+
const proj = resolve(flags.positionals[0] ?? process.cwd());
|
|
163
|
+
const { navWarnings, selWarnings, practiceWarnings } = await checkCommand(proj, flags.noPractices ? { noPractices: true } : undefined);
|
|
164
|
+
if (navWarnings.length === 0) {
|
|
165
|
+
console.log(`waygraph check: no navigation found outside NavBlocks under ${proj}`);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
for (const w of navWarnings) {
|
|
169
|
+
const rel = relative(proj, w.file);
|
|
170
|
+
console.warn(`waygraph check: ${rel} (${w.blockName}) calls page.goto/reload/goBack/goForward outside a NavBlock - consider defineNavBlock instead`);
|
|
171
|
+
}
|
|
172
|
+
console.log(`waygraph check: ${navWarnings.length} nav warning${navWarnings.length === 1 ? "" : "s"}`);
|
|
173
|
+
}
|
|
174
|
+
if (selWarnings.length === 0) {
|
|
175
|
+
console.log(`waygraph check: no inline selectors found in verify arrays under ${proj}`);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
for (const w of selWarnings) {
|
|
179
|
+
const rel = relative(proj, w.file);
|
|
180
|
+
console.warn(`waygraph check: ${rel} (${w.blockName}) has an inline selector literal in verify - move it into a *Sel object`);
|
|
181
|
+
}
|
|
182
|
+
console.log(`waygraph check: ${selWarnings.length} inline-selector warning${selWarnings.length === 1 ? "" : "s"}`);
|
|
183
|
+
}
|
|
184
|
+
if (!flags.noPractices) {
|
|
185
|
+
printPracticeReport(proj, practiceWarnings);
|
|
186
|
+
}
|
|
187
|
+
const orphans = await findOrphanBlocks(proj);
|
|
188
|
+
printOrphanReport(proj, orphans);
|
|
189
|
+
// Warnings only - never fail exit code. Orphans are reported for
|
|
190
|
+
// human/agent cleanup; chain auto refuses while any remain.
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function initCase(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
|
|
2
|
+
import { packageRoot } from "../cli/util.js";
|
|
3
|
+
import { basename, join, relative, resolve } from "node:path";
|
|
4
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
function initCommand(projectName) {
|
|
6
|
+
if (!projectName) {
|
|
7
|
+
console.error("waygraph init: missing <project-name>, e.g. waygraph init my-app");
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
const targetDir = resolve(process.cwd(), projectName);
|
|
11
|
+
const pkgName = basename(targetDir);
|
|
12
|
+
if (existsSync(targetDir)) {
|
|
13
|
+
const entries = readdirSync(targetDir);
|
|
14
|
+
if (entries.length > 0) {
|
|
15
|
+
console.error(`waygraph init: "${targetDir}" already exists and is not empty`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
mkdirSync(targetDir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
const templateDir = join(packageRoot(), "templates", "scaffold");
|
|
23
|
+
if (!existsSync(templateDir)) {
|
|
24
|
+
console.error(`waygraph init: scaffold template missing at ${templateDir}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
cpSync(templateDir, targetDir, { recursive: true });
|
|
28
|
+
const gitignoreSrc = join(targetDir, "gitignore");
|
|
29
|
+
if (existsSync(gitignoreSrc)) {
|
|
30
|
+
writeFileSync(join(targetDir, ".gitignore"), readFileSync(gitignoreSrc, "utf-8"));
|
|
31
|
+
rmSync(gitignoreSrc);
|
|
32
|
+
}
|
|
33
|
+
const packageJsonPath = join(targetDir, "package.json");
|
|
34
|
+
writeFileSync(packageJsonPath, readFileSync(packageJsonPath, "utf-8").replaceAll("__PROJECT_NAME__", pkgName));
|
|
35
|
+
console.log(`Scaffolded ${pkgName}/`);
|
|
36
|
+
console.log("");
|
|
37
|
+
console.log(` cd ${relative(process.cwd(), targetDir) || pkgName}`);
|
|
38
|
+
console.log(" npm install");
|
|
39
|
+
console.log(" npx playwright install chromium");
|
|
40
|
+
console.log(" npm test");
|
|
41
|
+
console.log(" waygraph list # .flow.ts → export map");
|
|
42
|
+
console.log(" waygraph check # nav hygiene + inline selectors + bad practices + orphan Blocks");
|
|
43
|
+
console.log(" waygraph typecheck # tsc --noEmit + bad-practice warnings (use --no-practices to skip)");
|
|
44
|
+
console.log(" waygraph auto # interactive explore (headed panel)");
|
|
45
|
+
console.log(" waygraph auto --cli # same menus in the terminal");
|
|
46
|
+
console.log(" waygraph demo --blocks exampleFlow");
|
|
47
|
+
console.log(" waygraph graph # static state graph JSON");
|
|
48
|
+
console.log("");
|
|
49
|
+
console.log(" Layout: see STRUCTURE.md (or https://deviate-dv8.github.io/waygraph/scaffold.html)");
|
|
50
|
+
}
|
|
51
|
+
export async function initCase(args) {
|
|
52
|
+
initCommand(args[1] ?? "");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
|
|
2
|
+
import { blockExportToLabel, discoverFlows, extractFlowNames, extractGotos, extractUrlTraits, parseBlockImports, walkDir } from "../cli/discover.js";
|
|
3
|
+
import { relative, resolve } from "node:path";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// `list`
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
function listCommand(projectDir) {
|
|
9
|
+
const files = discoverFlows(projectDir);
|
|
10
|
+
if (files.length === 0) {
|
|
11
|
+
console.log(`waygraph: no flows found under ${projectDir}`);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
for (const file of files) {
|
|
15
|
+
const rel = relative(projectDir, file);
|
|
16
|
+
for (const name of extractFlowNames(file)) {
|
|
17
|
+
console.log(`${rel} ${name}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// `nav`
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
function navCommand(projectDir, flowName) {
|
|
25
|
+
const files = discoverFlows(projectDir);
|
|
26
|
+
const targets = flowName
|
|
27
|
+
? files.filter((f) => extractFlowNames(f).includes(flowName))
|
|
28
|
+
: files;
|
|
29
|
+
if (targets.length === 0) {
|
|
30
|
+
console.error(flowName
|
|
31
|
+
? `waygraph nav: no flow named "${flowName}" found under ${projectDir}`
|
|
32
|
+
: `waygraph: no flows found under ${projectDir}`);
|
|
33
|
+
process.exitCode = 1;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
for (const file of targets) {
|
|
37
|
+
for (const name of extractFlowNames(file)) {
|
|
38
|
+
console.log(`${name} (${relative(projectDir, file)})`);
|
|
39
|
+
const blockDir = projectDir;
|
|
40
|
+
for (const blockExport of parseBlockImports(file)) {
|
|
41
|
+
console.log(` -> ${blockExportToLabel(blockExport)}`);
|
|
42
|
+
const blockFile = walkDir(blockDir, /\.block\.ts$/).find((f) => readFileSync(f, "utf-8").includes(`export const ${blockExport}`));
|
|
43
|
+
if (!blockFile)
|
|
44
|
+
continue;
|
|
45
|
+
for (const url of extractGotos(blockFile))
|
|
46
|
+
console.log(` goto ${url}`);
|
|
47
|
+
for (const url of extractUrlTraits(blockFile))
|
|
48
|
+
console.log(` verify url ~ ${url}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export async function listCase(args) {
|
|
54
|
+
const proj = resolve(args[1] ?? process.cwd());
|
|
55
|
+
if (!existsSync(proj)) {
|
|
56
|
+
console.error(`waygraph: no such directory: ${proj}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
listCommand(proj);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
export async function navCase(args) {
|
|
63
|
+
const flowName = args[1] && args[1] !== "." ? args[1] : undefined;
|
|
64
|
+
const proj = resolve(args[2] ?? process.cwd());
|
|
65
|
+
navCommand(proj, flowName);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function mapCase(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
|
|
2
|
+
import { relative, resolve } from "node:path";
|
|
3
|
+
import { checkMap } from "../map-check.js";
|
|
4
|
+
export async function mapCase(args) {
|
|
5
|
+
const proj = resolve(args[1] ?? process.cwd());
|
|
6
|
+
const result = await checkMap(proj);
|
|
7
|
+
if (!result.hasMapDir) {
|
|
8
|
+
console.log(`waygraph map: no src/map/ directory under ${proj} - nothing to check ` +
|
|
9
|
+
"(this project isn't on the Waygraph Map convention, or hasn't been migrated yet)");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
console.log(`waygraph map: ${result.nodes.length} node(s) found under ${relative(proj, result.mapRoot)}`);
|
|
13
|
+
if (result.violations.length === 0) {
|
|
14
|
+
console.log("waygraph map: 0 violations - every static Nav/Page url verbatim-matches its folder path");
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
for (const v of result.violations) {
|
|
18
|
+
console.error(`waygraph map: ${v.file} (${v.block}) - ${v.reason}`);
|
|
19
|
+
}
|
|
20
|
+
console.error(`waygraph map: ${result.violations.length} violation${result.violations.length === 1 ? "" : "s"}`);
|
|
21
|
+
// Unlike `check`'s warnings-only stance, a Map violation is exactly
|
|
22
|
+
// the class of bug this command exists to catch (a fabricated
|
|
23
|
+
// folder grouping that never matched the real site, e.g. the real
|
|
24
|
+
// (auth)/signin/ mistake this command's own header comment cites) -
|
|
25
|
+
// fail loud, not just warn.
|
|
26
|
+
process.exitCode = 1;
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Split out of the former 8,700-line cli.ts (see src/ARCHITECTURE.md). Behavior unchanged.
|
|
2
|
+
import { applyDemoDefaults, applyRunFlags, parseRunFlags, resolveBaseUrl, resolveProjectDir, resolveSpec } from "../cli/flags.js";
|
|
3
|
+
import { runChain } from "../cli/run-chain.js";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import { runChainAuto } from "./check.js";
|
|
7
|
+
export async function runCase(args) {
|
|
8
|
+
const flags = parseRunFlags(args.slice(1));
|
|
9
|
+
applyRunFlags(flags);
|
|
10
|
+
const spec = resolveSpec(flags);
|
|
11
|
+
if (!spec) {
|
|
12
|
+
console.error("waygraph run: missing flow/spec — e.g.\n" +
|
|
13
|
+
" waygraph run src/flows/shop.flow.ts\n" +
|
|
14
|
+
" waygraph run --blocks shopFlow\n" +
|
|
15
|
+
" waygraph list # file → export map\n" +
|
|
16
|
+
" Flags: --blocks --data --mem-stub --non-headless --video [dir] --step/--no-step");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const proj = flags.blocks
|
|
20
|
+
? resolveProjectDir(flags, 0)
|
|
21
|
+
: resolve(flags.positionals[1] ?? process.cwd());
|
|
22
|
+
if (!existsSync(proj)) {
|
|
23
|
+
console.error(`waygraph: no such directory: ${proj}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
if (!process.env.WAYGRAPH_BASE_URL) {
|
|
27
|
+
const resolved = resolveBaseUrl(proj);
|
|
28
|
+
if (resolved)
|
|
29
|
+
process.env.WAYGRAPH_BASE_URL = resolved;
|
|
30
|
+
}
|
|
31
|
+
await runChain(proj, spec);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
export async function chainCase(args) {
|
|
35
|
+
// Compat alias: chain auto A B -> auto --blocks A B; else -> run --blocks.
|
|
36
|
+
const flags = parseRunFlags(args.slice(1));
|
|
37
|
+
applyRunFlags(flags);
|
|
38
|
+
const first = flags.positionals[0];
|
|
39
|
+
if (first === "auto") {
|
|
40
|
+
const fromTag = flags.positionals[1];
|
|
41
|
+
const toTag = flags.positionals[2];
|
|
42
|
+
if (!fromTag || !toTag) {
|
|
43
|
+
console.error("waygraph chain auto: missing <fromCheckpoint> <toCheckpoint> [project]");
|
|
44
|
+
console.error(" prefer: waygraph auto --blocks LoggedIn OrderComplete");
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
const proj = resolve(flags.positionals[3] ?? process.cwd());
|
|
48
|
+
if (!existsSync(proj)) {
|
|
49
|
+
console.error(`waygraph: no such directory: ${proj}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
await runChainAuto(proj, fromTag, toTag);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const spec = flags.blocks ?? first;
|
|
56
|
+
if (!spec) {
|
|
57
|
+
console.error('waygraph chain: missing <spec> — prefer: waygraph run --blocks "a then b"\n' +
|
|
58
|
+
" or: waygraph auto --blocks <fromCheckpoint> <toCheckpoint>");
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const proj = resolve(flags.blocks ? (flags.positionals[0] ?? process.cwd()) : (flags.positionals[1] ?? process.cwd()));
|
|
62
|
+
if (!process.env.WAYGRAPH_BASE_URL) {
|
|
63
|
+
const resolved = resolveBaseUrl(proj);
|
|
64
|
+
if (resolved)
|
|
65
|
+
process.env.WAYGRAPH_BASE_URL = resolved;
|
|
66
|
+
}
|
|
67
|
+
// If caller asked for step overlay, behave like demo; else like run.
|
|
68
|
+
if (flags.step === true || process.env.WAYGRAPH_STEP === "1") {
|
|
69
|
+
applyDemoDefaults(proj);
|
|
70
|
+
}
|
|
71
|
+
await runChain(proj, spec);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
export async function demoCase(args) {
|
|
75
|
+
const flags = parseRunFlags(args.slice(1));
|
|
76
|
+
applyRunFlags(flags, { allowAutoPlayVideo: true, allowDemoUi: true });
|
|
77
|
+
const spec = resolveSpec(flags);
|
|
78
|
+
if (!spec) {
|
|
79
|
+
console.error("waygraph demo: missing flow/spec — e.g.\n" +
|
|
80
|
+
" waygraph demo src/flows/shop.flow.ts\n" +
|
|
81
|
+
" waygraph demo --blocks shopFlow\n" +
|
|
82
|
+
" Flags: --blocks --data --mem-stub --auto-next --fast --full --mini --ff-expand --ff-disabled --auto-play-video --title --base-url --video");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
const proj = flags.blocks
|
|
86
|
+
? resolveProjectDir(flags, 0)
|
|
87
|
+
: resolve(flags.positionals[1] ?? process.cwd());
|
|
88
|
+
if (!existsSync(proj)) {
|
|
89
|
+
console.error(`waygraph: no such directory: ${proj}`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
if (flags.step === undefined && process.env.WAYGRAPH_STEP === undefined) {
|
|
93
|
+
process.env.WAYGRAPH_STEP = "1";
|
|
94
|
+
}
|
|
95
|
+
applyDemoDefaults(proj);
|
|
96
|
+
console.error(`waygraph demo: STEP=${process.env.WAYGRAPH_STEP === "1" ? "on" : "off"}` +
|
|
97
|
+
` AUTO_NEXT=${process.env.WAYGRAPH_AUTOPLAY === "1" ? "on" : "off"}` +
|
|
98
|
+
` HEADED=${process.env.WAYGRAPH_HEADED === "0" ? "off" : "on"}` +
|
|
99
|
+
` BASE_URL=${process.env.WAYGRAPH_BASE_URL ?? "(unset)"}` +
|
|
100
|
+
(process.env.WAYGRAPH_VIDEO ? ` VIDEO=${process.env.WAYGRAPH_VIDEO}` : "") +
|
|
101
|
+
(process.env.WAYGRAPH_TITLE ? ` TITLE=${JSON.stringify(process.env.WAYGRAPH_TITLE)}` : ""));
|
|
102
|
+
await runChain(proj, spec);
|
|
103
|
+
return;
|
|
104
|
+
}
|