@jevitate/cli 0.9.0 → 0.11.0
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/anchor-baselines.d.ts +26 -0
- package/dist/anchor-baselines.js +25 -0
- package/dist/anchor-baselines.js.map +1 -0
- package/dist/approval-provenance.d.ts +32 -2
- package/dist/approval-provenance.js +92 -5
- package/dist/approval-provenance.js.map +1 -1
- package/dist/approved-demo.d.ts +86 -0
- package/dist/approved-demo.js +184 -0
- package/dist/approved-demo.js.map +1 -0
- package/dist/bin.js +13352 -7050
- package/dist/build-info.generated.js +2 -2
- package/dist/campaign-api.d.ts +15 -0
- package/dist/campaign-api.js +67 -6
- package/dist/campaign-api.js.map +1 -1
- package/dist/campaign-cli.js +7 -2
- package/dist/campaign-cli.js.map +1 -1
- package/dist/campaign-preflight.d.ts +76 -0
- package/dist/campaign-preflight.js +117 -0
- package/dist/campaign-preflight.js.map +1 -0
- package/dist/catalog-analysis.js +14 -0
- package/dist/catalog-analysis.js.map +1 -1
- package/dist/catalog-api.js +12 -4
- package/dist/catalog-api.js.map +1 -1
- package/dist/catalog-bundle-api.d.ts +83 -0
- package/dist/catalog-bundle-api.js +309 -0
- package/dist/catalog-bundle-api.js.map +1 -0
- package/dist/catalog-bundle-findings.d.ts +64 -0
- package/dist/catalog-bundle-findings.js +326 -0
- package/dist/catalog-bundle-findings.js.map +1 -0
- package/dist/catalog-bundle.d.ts +296 -0
- package/dist/catalog-bundle.js +777 -0
- package/dist/catalog-bundle.js.map +1 -0
- package/dist/catalog-cli.d.ts +6 -0
- package/dist/catalog-cli.js +181 -5
- package/dist/catalog-cli.js.map +1 -1
- package/dist/catalog-refs.d.ts +42 -0
- package/dist/catalog-refs.js +188 -0
- package/dist/catalog-refs.js.map +1 -0
- package/dist/catalog-review.d.ts +3 -1
- package/dist/catalog-review.js +61 -0
- package/dist/catalog-review.js.map +1 -1
- package/dist/catalog.d.ts +27 -3
- package/dist/catalog.js +46 -2
- package/dist/catalog.js.map +1 -1
- package/dist/check-api.js +180 -9
- package/dist/check-api.js.map +1 -1
- package/dist/check-cli.d.ts +2 -0
- package/dist/check-cli.js +96 -2
- package/dist/check-cli.js.map +1 -1
- package/dist/check-execute.d.ts +18 -0
- package/dist/check-execute.js +56 -3
- package/dist/check-execute.js.map +1 -1
- package/dist/check-types.d.ts +105 -0
- package/dist/check-types.js.map +1 -1
- package/dist/cli-output.d.ts +6 -0
- package/dist/cli-output.js +38 -1
- package/dist/cli-output.js.map +1 -1
- package/dist/cli-shared.d.ts +10 -0
- package/dist/cli-shared.js +0 -4
- package/dist/cli-shared.js.map +1 -1
- package/dist/cold-start-report.d.ts +32 -0
- package/dist/cold-start-report.js +31 -0
- package/dist/cold-start-report.js.map +1 -0
- package/dist/defect-evidence.js +10 -5
- package/dist/defect-evidence.js.map +1 -1
- package/dist/demo-aspect-api.d.ts +11 -0
- package/dist/demo-aspect-api.js +27 -2
- package/dist/demo-aspect-api.js.map +1 -1
- package/dist/demo-aspect-cli.js +5 -0
- package/dist/demo-aspect-cli.js.map +1 -1
- package/dist/demo-capture.d.ts +79 -2
- package/dist/demo-capture.js +504 -12
- package/dist/demo-capture.js.map +1 -1
- package/dist/environments.d.ts +8 -1
- package/dist/environments.js +14 -3
- package/dist/environments.js.map +1 -1
- package/dist/exit-codes.js +16 -0
- package/dist/exit-codes.js.map +1 -1
- package/dist/explore-adversarial.js +2 -2
- package/dist/explore-adversarial.js.map +1 -1
- package/dist/explore-shared.d.ts +11 -0
- package/dist/explore-shared.js +9 -0
- package/dist/explore-shared.js.map +1 -1
- package/dist/findings-filing.js +7 -3
- package/dist/findings-filing.js.map +1 -1
- package/dist/forge-verify.d.ts +224 -0
- package/dist/forge-verify.js +649 -0
- package/dist/forge-verify.js.map +1 -0
- package/dist/github-issue-filer.js +15 -9
- package/dist/github-issue-filer.js.map +1 -1
- package/dist/init-codeowners.d.ts +19 -0
- package/dist/init-codeowners.js +66 -0
- package/dist/init-codeowners.js.map +1 -1
- package/dist/job-draft-api.d.ts +52 -0
- package/dist/job-draft-api.js +110 -0
- package/dist/job-draft-api.js.map +1 -0
- package/dist/journeeze-api.d.ts +77 -0
- package/dist/journeeze-api.js +371 -0
- package/dist/journeeze-api.js.map +1 -0
- package/dist/journeeze-connect.d.ts +156 -0
- package/dist/journeeze-connect.js +357 -0
- package/dist/journeeze-connect.js.map +1 -0
- package/dist/journey-anchor-api.d.ts +37 -0
- package/dist/journey-anchor-api.js +58 -0
- package/dist/journey-anchor-api.js.map +1 -0
- package/dist/journey-anchor-cli.d.ts +17 -0
- package/dist/journey-anchor-cli.js +104 -0
- package/dist/journey-anchor-cli.js.map +1 -0
- package/dist/journey-anchor-gate.d.ts +52 -0
- package/dist/journey-anchor-gate.js +101 -0
- package/dist/journey-anchor-gate.js.map +1 -0
- package/dist/journey-api.d.ts +30 -1
- package/dist/journey-api.js +118 -16
- package/dist/journey-api.js.map +1 -1
- package/dist/journey-cli.js +93 -10
- package/dist/journey-cli.js.map +1 -1
- package/dist/journey-demo-api.d.ts +29 -0
- package/dist/journey-demo-api.js +29 -7
- package/dist/journey-demo-api.js.map +1 -1
- package/dist/journey-heal.js +1 -1
- package/dist/journey-heal.js.map +1 -1
- package/dist/journey-lint-sarif.d.ts +3 -2
- package/dist/journey-lint-sarif.js +11 -2
- package/dist/journey-lint-sarif.js.map +1 -1
- package/dist/journey-migrate-api.d.ts +54 -0
- package/dist/journey-migrate-api.js +64 -0
- package/dist/journey-migrate-api.js.map +1 -0
- package/dist/journey-network-checks.d.ts +11 -0
- package/dist/journey-network-checks.js +36 -13
- package/dist/journey-network-checks.js.map +1 -1
- package/dist/journey-proposal-store.d.ts +9 -0
- package/dist/journey-proposal-store.js +23 -0
- package/dist/journey-proposal-store.js.map +1 -1
- package/dist/journey-result-record.d.ts +2 -0
- package/dist/journey-result-record.js +5 -0
- package/dist/journey-result-record.js.map +1 -1
- package/dist/journey-review-api.js +9 -1
- package/dist/journey-review-api.js.map +1 -1
- package/dist/journey-review.d.ts +11 -1
- package/dist/journey-review.js +77 -5
- package/dist/journey-review.js.map +1 -1
- package/dist/journey-stale-api.d.ts +40 -0
- package/dist/journey-stale-api.js +57 -0
- package/dist/journey-stale-api.js.map +1 -0
- package/dist/load-api.js +4 -2
- package/dist/load-api.js.map +1 -1
- package/dist/locator-health-api.d.ts +101 -0
- package/dist/locator-health-api.js +203 -0
- package/dist/locator-health-api.js.map +1 -0
- package/dist/locator-health.d.ts +195 -0
- package/dist/locator-health.js +419 -0
- package/dist/locator-health.js.map +1 -0
- package/dist/mcp-api.d.ts +3 -1
- package/dist/mcp-api.js +21 -2
- package/dist/mcp-api.js.map +1 -1
- package/dist/mcp-cli-tools.d.ts +4 -1
- package/dist/mcp-cli-tools.js +82 -10
- package/dist/mcp-cli-tools.js.map +1 -1
- package/dist/openrouter-call.d.ts +23 -10
- package/dist/openrouter-call.js +14 -11
- package/dist/openrouter-call.js.map +1 -1
- package/dist/png-pixels.d.ts +12 -0
- package/dist/png-pixels.js +96 -0
- package/dist/png-pixels.js.map +1 -0
- package/dist/project-config.d.ts +29 -0
- package/dist/project-config.js +76 -0
- package/dist/project-config.js.map +1 -0
- package/dist/regression-api.js +2 -1
- package/dist/regression-api.js.map +1 -1
- package/dist/server-log-verify.js +2 -1
- package/dist/server-log-verify.js.map +1 -1
- package/dist/signal-triage.js +7 -3
- package/dist/signal-triage.js.map +1 -1
- package/dist/source-run-api.js +2 -2
- package/dist/source-run-api.js.map +1 -1
- package/dist/verify-fix-api.d.ts +29 -0
- package/dist/verify-fix-api.js +43 -2
- package/dist/verify-fix-api.js.map +1 -1
- package/dist/verify-fix-cli.js +4 -0
- package/dist/verify-fix-cli.js.map +1 -1
- package/dist/zip-store.d.ts +15 -0
- package/dist/zip-store.js +93 -0
- package/dist/zip-store.js.map +1 -0
- package/package.json +12 -10
- package/skills/jevitate-ci-check/SKILL.md +16 -0
- package/skills/jevitate-demo/SKILL.md +4 -1
- package/skills/jevitate-getting-started/SKILL.md +7 -2
- package/skills/jevitate-run-journey/SKILL.md +15 -0
- package/skills/jevitate-test-campaign/SKILL.md +26 -0
- package/skills/jevitate-verify-fix/SKILL.md +5 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { looksGenerated } from "@jevitate/recorder";
|
|
2
|
+
// === Classification ===
|
|
3
|
+
const LEVEL_RANK = { low: 0, medium: 1, high: 2 };
|
|
4
|
+
/** One notch down: a rung that only resolves with an ordinal (the recorder's `CAPPED_STABILITY`). */
|
|
5
|
+
const CAPPED = { high: "medium", medium: "low", low: "low" };
|
|
6
|
+
const REASON_TEXT = {
|
|
7
|
+
"no-test-id": "no test id",
|
|
8
|
+
"test-id-not-in-convention": "test id attribute is not in the convention",
|
|
9
|
+
"tflow-id-not-a-test-id": "data-tflow-id is tracking metadata, not a test id",
|
|
10
|
+
"generated-value": "generated-looking id or name",
|
|
11
|
+
"no-accessible-name": "no accessible name",
|
|
12
|
+
"duplicate-name": "duplicate name needs disambiguation (nth)",
|
|
13
|
+
"positional-css": "deep or positional css path",
|
|
14
|
+
"text-copy": "text locator on copy likely to change",
|
|
15
|
+
};
|
|
16
|
+
/** The target a step acts on, or null (navigate, press, assert, a forEach's items are its own). */
|
|
17
|
+
export function stepTarget(step) {
|
|
18
|
+
switch (step.kind) {
|
|
19
|
+
case "click":
|
|
20
|
+
case "fill":
|
|
21
|
+
case "waitFor":
|
|
22
|
+
case "extract":
|
|
23
|
+
case "select":
|
|
24
|
+
case "upload":
|
|
25
|
+
case "editText":
|
|
26
|
+
return step.target;
|
|
27
|
+
default:
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** The recorded rung replay tries (an anchor first, as `resolveTarget` does). */
|
|
32
|
+
function recordedRung(d) {
|
|
33
|
+
if (d.anchor !== undefined && d.testId === undefined && (d.anchor.id !== undefined || d.anchor.name !== undefined))
|
|
34
|
+
return "anchor";
|
|
35
|
+
return ownRung(d);
|
|
36
|
+
}
|
|
37
|
+
function ownRung(d) {
|
|
38
|
+
if (d.testId)
|
|
39
|
+
return "testId";
|
|
40
|
+
if (d.role && d.name)
|
|
41
|
+
return "role+name";
|
|
42
|
+
if (d.label)
|
|
43
|
+
return "label";
|
|
44
|
+
if (d.text)
|
|
45
|
+
return "text";
|
|
46
|
+
if (d.css)
|
|
47
|
+
return "css";
|
|
48
|
+
return d.role ? "role" : "css";
|
|
49
|
+
}
|
|
50
|
+
function asRung(r) {
|
|
51
|
+
return ["testId", "anchor", "role+name", "role", "label", "text", "css"].find((x) => x === r) ?? "css";
|
|
52
|
+
}
|
|
53
|
+
/** How deep a css path is, and whether it is positional. */
|
|
54
|
+
function cssShape(css) {
|
|
55
|
+
return { depth: css.split(">").length, positional: /:nth-(of-type|child)\(|:first-|:last-/.test(css) };
|
|
56
|
+
}
|
|
57
|
+
function describeLocator(d, rung) {
|
|
58
|
+
const nth = d.ordinal === undefined ? "" : ` nth=${d.ordinal}`;
|
|
59
|
+
switch (rung) {
|
|
60
|
+
case "testId":
|
|
61
|
+
return `testId[${d.testIdAttr ?? "data-testid"}]=${d.testId ?? "?"}`;
|
|
62
|
+
case "anchor":
|
|
63
|
+
return d.anchor?.id !== undefined ? `[id=${JSON.stringify(d.anchor.id)}]` : `[name=${JSON.stringify(d.anchor?.name ?? "")}]`;
|
|
64
|
+
case "role+name":
|
|
65
|
+
return `role=${d.role ?? "?"} name=${JSON.stringify(d.name ?? "")}${nth}`;
|
|
66
|
+
case "label":
|
|
67
|
+
return `label=${JSON.stringify(d.label ?? "")}${nth}`;
|
|
68
|
+
case "text":
|
|
69
|
+
return `text=${JSON.stringify(d.text ?? "")}${nth}`;
|
|
70
|
+
default:
|
|
71
|
+
return `css=${d.css ?? "?"}`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* One target's verdict against the convention. `via` (a run's resolution) replaces the recorded rung
|
|
76
|
+
* when present; everything else is read from the descriptor.
|
|
77
|
+
*/
|
|
78
|
+
export function classifyTarget(d, testIdAttributes, via) {
|
|
79
|
+
const rung = via === undefined ? recordedRung(d) : asRung(via.rung);
|
|
80
|
+
const ordinal = via === undefined ? d.ordinal : via.ordinal;
|
|
81
|
+
const codes = [];
|
|
82
|
+
let level;
|
|
83
|
+
switch (rung) {
|
|
84
|
+
case "testId": {
|
|
85
|
+
const attr = (via?.testIdAttr ?? d.testIdAttr ?? "data-testid").toLowerCase();
|
|
86
|
+
const generated = looksGenerated(d.testId ?? "");
|
|
87
|
+
level = generated ? "low" : "high";
|
|
88
|
+
if (attr === "data-tflow-id")
|
|
89
|
+
codes.push("tflow-id-not-a-test-id");
|
|
90
|
+
else if (!testIdAttributes.includes(attr))
|
|
91
|
+
codes.push("test-id-not-in-convention");
|
|
92
|
+
if (generated)
|
|
93
|
+
codes.push("generated-value");
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
case "anchor":
|
|
97
|
+
// A non-generated id/name attribute (the recorder refuses generated ones): sturdy, but not the convention.
|
|
98
|
+
level = "medium";
|
|
99
|
+
codes.push("no-test-id");
|
|
100
|
+
break;
|
|
101
|
+
case "role+name":
|
|
102
|
+
level = looksGenerated(d.name ?? "") ? "low" : "high";
|
|
103
|
+
codes.push("no-test-id");
|
|
104
|
+
if (level === "low")
|
|
105
|
+
codes.push("generated-value");
|
|
106
|
+
break;
|
|
107
|
+
case "label":
|
|
108
|
+
level = "medium";
|
|
109
|
+
codes.push("no-test-id");
|
|
110
|
+
break;
|
|
111
|
+
case "text":
|
|
112
|
+
level = "medium";
|
|
113
|
+
codes.push("no-test-id", "text-copy");
|
|
114
|
+
break;
|
|
115
|
+
default: {
|
|
116
|
+
level = "low";
|
|
117
|
+
codes.push("no-test-id");
|
|
118
|
+
const shape = cssShape(d.css ?? "");
|
|
119
|
+
if (shape.positional || shape.depth > 3)
|
|
120
|
+
codes.push("positional-css");
|
|
121
|
+
if (/#[A-Za-z0-9_-]*\d{4}|[0-9a-fA-F]{8}/.test(d.css ?? ""))
|
|
122
|
+
codes.push("generated-value");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (ordinal !== undefined && rung !== "testId" && rung !== "anchor" && rung !== "css") {
|
|
126
|
+
level = CAPPED[level];
|
|
127
|
+
codes.push("duplicate-name");
|
|
128
|
+
}
|
|
129
|
+
if (rung !== "testId" && !(d.name || d.label))
|
|
130
|
+
codes.push("no-accessible-name");
|
|
131
|
+
if (d.tflowId !== undefined && rung !== "testId" && !codes.includes("tflow-id-not-a-test-id"))
|
|
132
|
+
codes.push("tflow-id-not-a-test-id");
|
|
133
|
+
return { rung, level, codes };
|
|
134
|
+
}
|
|
135
|
+
// === Suggestions ===
|
|
136
|
+
/** `"Save contact"` → `save-contact` (ASCII letters and digits, at most 40 chars). */
|
|
137
|
+
export function kebabCase(s) {
|
|
138
|
+
return s
|
|
139
|
+
.normalize("NFKD")
|
|
140
|
+
.replace(/[̀-ͯ]/g, "")
|
|
141
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
142
|
+
.toLowerCase()
|
|
143
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
144
|
+
.replace(/^-+|-+$/g, "")
|
|
145
|
+
.slice(0, 40)
|
|
146
|
+
.replace(/-+$/g, "");
|
|
147
|
+
}
|
|
148
|
+
/** The path of a page URL (`https://x/contacts/new?a=1` → `/contacts/new`); a path is kept as is. */
|
|
149
|
+
export function routeOf(url) {
|
|
150
|
+
if (url === undefined || url === "")
|
|
151
|
+
return undefined;
|
|
152
|
+
try {
|
|
153
|
+
return new URL(url).pathname;
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
const path = url.split(/[?#]/)[0] ?? url;
|
|
157
|
+
return path.startsWith("/") ? path : `/${path}`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function singular(word) {
|
|
161
|
+
if (/ies$/.test(word))
|
|
162
|
+
return word.replace(/ies$/, "y");
|
|
163
|
+
if (/(ss|us)$/.test(word))
|
|
164
|
+
return word;
|
|
165
|
+
return word.replace(/s$/, "");
|
|
166
|
+
}
|
|
167
|
+
/** The route's first meaningful segment (not an id or a verb like `new`/`edit`), singular. */
|
|
168
|
+
function routeWords(route) {
|
|
169
|
+
return (route ?? "").split("/").filter((s) => s !== "" && !/^\d+$/.test(s) && !looksGenerated(s) && !/^(new|edit|create|index|view)$/i.test(s));
|
|
170
|
+
}
|
|
171
|
+
function routeNoun(route) {
|
|
172
|
+
const first = routeWords(route)[0];
|
|
173
|
+
return first === undefined ? undefined : singular(kebabCase(first));
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* The suggested test id: the element's name in kebab-case, with the route's noun when the name is a
|
|
177
|
+
* single word (`Save` on /contacts/new → `save-contact`); an unnamed element is named by its route
|
|
178
|
+
* and tag (`contacts-div`).
|
|
179
|
+
*/
|
|
180
|
+
export function suggestTestId(name, route, fallbackTag) {
|
|
181
|
+
const base = name === undefined ? "" : kebabCase(name);
|
|
182
|
+
const noun = routeNoun(route);
|
|
183
|
+
if (base !== "")
|
|
184
|
+
return !base.includes("-") && noun !== undefined && !base.includes(noun) ? `${base}-${noun}` : base;
|
|
185
|
+
const where = kebabCase(routeWords(route).join("-"));
|
|
186
|
+
return `${where === "" ? "page" : where}-${fallbackTag}`;
|
|
187
|
+
}
|
|
188
|
+
function lastCssTag(css) {
|
|
189
|
+
const last = (css ?? "").split(">").pop()?.trim() ?? "";
|
|
190
|
+
return /^([a-z][a-z0-9-]*)/i.exec(last)?.[1]?.toLowerCase() ?? "element";
|
|
191
|
+
}
|
|
192
|
+
function describeElement(d) {
|
|
193
|
+
const name = d.name ?? d.label;
|
|
194
|
+
if (d.role && name)
|
|
195
|
+
return `the ${JSON.stringify(name)} ${d.role}`;
|
|
196
|
+
if (d.label)
|
|
197
|
+
return `the ${JSON.stringify(d.label)} field`;
|
|
198
|
+
if (d.role)
|
|
199
|
+
return `the ${d.role}`;
|
|
200
|
+
if (d.text)
|
|
201
|
+
return `the element with text ${JSON.stringify(d.text.length > 40 ? `${d.text.slice(0, 40)}…` : d.text)}`;
|
|
202
|
+
if (d.testId)
|
|
203
|
+
return `the element with ${d.testIdAttr ?? "data-testid"}=${JSON.stringify(d.testId)}`;
|
|
204
|
+
return `the element at css ${JSON.stringify(d.css ?? "?")}`;
|
|
205
|
+
}
|
|
206
|
+
/** The element's identity across steps and runs: route + role + name (else its css). */
|
|
207
|
+
export function suggestionKey(d, route) {
|
|
208
|
+
const name = d.name ?? d.label ?? d.text;
|
|
209
|
+
if (name !== undefined)
|
|
210
|
+
return `${route ?? ""}|${d.role ?? ""}|${name}`;
|
|
211
|
+
if (d.testId !== undefined)
|
|
212
|
+
return `${route ?? ""}|testid|${d.testId}`;
|
|
213
|
+
return `${route ?? ""}|css|${d.css ?? ""}`;
|
|
214
|
+
}
|
|
215
|
+
function fixFor(d, codes, attribute, testId, element, route) {
|
|
216
|
+
const on = route === undefined ? "" : ` on ${route}`;
|
|
217
|
+
const named = codes.includes("no-accessible-name") ? " and give it an accessible name (visible text or aria-label)" : "";
|
|
218
|
+
if (codes.includes("test-id-not-in-convention") && d.testId !== undefined && !looksGenerated(d.testId)) {
|
|
219
|
+
return `use ${attribute}=${JSON.stringify(d.testId)} instead of ${d.testIdAttr ?? "?"} on ${element}${on} (or add ${d.testIdAttr ?? "?"} to testIdAttributes in .jevitate/project.json)`;
|
|
220
|
+
}
|
|
221
|
+
if (d.testId !== undefined && looksGenerated(d.testId)) {
|
|
222
|
+
return `replace the generated ${d.testIdAttr ?? attribute}=${JSON.stringify(d.testId)} on ${element}${on} with a stable ${attribute}=${JSON.stringify(testId)}`;
|
|
223
|
+
}
|
|
224
|
+
return `add ${attribute}=${JSON.stringify(testId)} to ${element}${on}${named}`;
|
|
225
|
+
}
|
|
226
|
+
function buildSuggestion(d, codes, route, attribute, html, occurrence) {
|
|
227
|
+
const name = d.name ?? d.label ?? d.text;
|
|
228
|
+
const testId = suggestTestId(d.testId !== undefined && !looksGenerated(d.testId) ? d.testId : name, route, lastCssTag(d.css));
|
|
229
|
+
const element = describeElement(d);
|
|
230
|
+
return {
|
|
231
|
+
key: suggestionKey(d, route),
|
|
232
|
+
...(route === undefined ? {} : { route }),
|
|
233
|
+
...(d.role === undefined ? {} : { role: d.role }),
|
|
234
|
+
...(name === undefined ? {} : { name }),
|
|
235
|
+
element,
|
|
236
|
+
...(html === undefined ? {} : { html: html.length > 160 ? `${html.slice(0, 160)}…` : html }),
|
|
237
|
+
attribute,
|
|
238
|
+
testId,
|
|
239
|
+
fix: fixFor(d, codes, attribute, testId, element, route),
|
|
240
|
+
reasons: codes.map((c) => REASON_TEXT[c]),
|
|
241
|
+
occurrences: [occurrence],
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/** Merges suggestions for the same element (route + role + name): occurrences and reasons combined. */
|
|
245
|
+
export function dedupeSuggestions(lists) {
|
|
246
|
+
const byKey = new Map();
|
|
247
|
+
for (const s of lists.flat()) {
|
|
248
|
+
const prev = byKey.get(s.key);
|
|
249
|
+
if (prev === undefined) {
|
|
250
|
+
byKey.set(s.key, s);
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
const occ = [...prev.occurrences];
|
|
254
|
+
for (const o of s.occurrences) {
|
|
255
|
+
if (!occ.some((p) => p.journeyId === o.journeyId && p.index === o.index && p.stepId === o.stepId))
|
|
256
|
+
occ.push(o);
|
|
257
|
+
}
|
|
258
|
+
byKey.set(s.key, {
|
|
259
|
+
...prev,
|
|
260
|
+
...(prev.html === undefined && s.html !== undefined ? { html: s.html } : {}),
|
|
261
|
+
reasons: [...new Set([...prev.reasons, ...s.reasons])],
|
|
262
|
+
occurrences: occ,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
// The elements most steps depend on first: the highest-value fixes lead the work list.
|
|
266
|
+
return [...byKey.values()].sort((a, b) => b.occurrences.length - a.occurrences.length || a.key.localeCompare(b.key));
|
|
267
|
+
}
|
|
268
|
+
// === Analysis ===
|
|
269
|
+
function flatWithRoute(rec) {
|
|
270
|
+
return rec.pages.flatMap((p) => p.steps.map((recorded) => ({ recorded, route: routeOf(p.url) })));
|
|
271
|
+
}
|
|
272
|
+
export function emptyLevels() {
|
|
273
|
+
return { high: 0, medium: 0, low: 0 };
|
|
274
|
+
}
|
|
275
|
+
/** `7/9 steps on stable locators; 2 brittle (high 7 · medium 1 · low 1)`. */
|
|
276
|
+
export function healthLine(h) {
|
|
277
|
+
const total = h.stable + h.brittle;
|
|
278
|
+
if (total === 0)
|
|
279
|
+
return "no steps act on a target";
|
|
280
|
+
return `${h.stable}/${total} steps on stable locators; ${h.brittle} brittle (high ${h.levels.high} · medium ${h.levels.medium} · low ${h.levels.low})`;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* A recording's locator health: every step with a target (flat index, as the interpreter numbers
|
|
284
|
+
* them), against the convention; with `resolved`, the rung each step actually matched at replay.
|
|
285
|
+
*/
|
|
286
|
+
export function analyzeRecording(rec, opts) {
|
|
287
|
+
const attribute = opts.testIdAttributes[0] ?? "data-testid";
|
|
288
|
+
const resolvedBy = new Map((opts.resolved ?? []).map((r) => [r.index, r]));
|
|
289
|
+
const steps = [];
|
|
290
|
+
const suggestions = [];
|
|
291
|
+
const levels = { high: 0, medium: 0, low: 0 };
|
|
292
|
+
flatWithRoute(rec).forEach(({ recorded, route }, index) => {
|
|
293
|
+
const d = stepTarget(recorded.step);
|
|
294
|
+
if (d === null)
|
|
295
|
+
return;
|
|
296
|
+
const via = resolvedBy.get(index);
|
|
297
|
+
const v = classifyTarget(d, opts.testIdAttributes, via);
|
|
298
|
+
const brittle = v.codes.length > 0;
|
|
299
|
+
levels[v.level] += 1;
|
|
300
|
+
const stepId = recorded.stepId ?? via?.stepId;
|
|
301
|
+
let key;
|
|
302
|
+
if (brittle) {
|
|
303
|
+
const occurrence = { ...(opts.journeyId === undefined ? {} : { journeyId: opts.journeyId }), index, ...(stepId === undefined ? {} : { stepId }) };
|
|
304
|
+
const s = buildSuggestion(d, v.codes, route, attribute, opts.html?.get(index), occurrence);
|
|
305
|
+
suggestions.push(s);
|
|
306
|
+
key = s.key;
|
|
307
|
+
}
|
|
308
|
+
steps.push({
|
|
309
|
+
...(stepId === undefined ? {} : { stepId }),
|
|
310
|
+
index,
|
|
311
|
+
kind: recorded.step.kind,
|
|
312
|
+
rung: v.rung,
|
|
313
|
+
level: v.level,
|
|
314
|
+
stability: brittle ? "brittle" : "stable",
|
|
315
|
+
source: via === undefined ? "recorded" : "resolved",
|
|
316
|
+
...(route === undefined ? {} : { route }),
|
|
317
|
+
locator: describeLocator(d, v.rung),
|
|
318
|
+
reasons: v.codes.map((c) => REASON_TEXT[c]),
|
|
319
|
+
reasonCodes: v.codes,
|
|
320
|
+
...(key === undefined ? {} : { suggestion: key }),
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
const stable = steps.filter((s) => s.stability === "stable").length;
|
|
324
|
+
const brittle = steps.length - stable;
|
|
325
|
+
return { stable, brittle, steps, levels, line: healthLine({ stable, brittle, levels }), suggestions: dedupeSuggestions([suggestions]) };
|
|
326
|
+
}
|
|
327
|
+
/** Totals over several healths (a report, a check): counts, levels and the de-duplicated work list. */
|
|
328
|
+
export function combineHealth(healths) {
|
|
329
|
+
const levels = { high: 0, medium: 0, low: 0 };
|
|
330
|
+
let stable = 0;
|
|
331
|
+
let brittle = 0;
|
|
332
|
+
for (const h of healths) {
|
|
333
|
+
stable += h.stable;
|
|
334
|
+
brittle += h.brittle;
|
|
335
|
+
levels.high += h.levels.high;
|
|
336
|
+
levels.medium += h.levels.medium;
|
|
337
|
+
levels.low += h.levels.low;
|
|
338
|
+
}
|
|
339
|
+
return { stable, brittle, levels, line: healthLine({ stable, brittle, levels }), suggestions: dedupeSuggestions(healths.map((h) => h.suggestions)) };
|
|
340
|
+
}
|
|
341
|
+
// === Gate ===
|
|
342
|
+
/** `check --max-brittle-steps <n>`: true when a Journey item's brittle steps exceed the opt-in threshold. */
|
|
343
|
+
export function exceedsBrittleSteps(health, maxBrittleSteps) {
|
|
344
|
+
return maxBrittleSteps !== undefined && health.brittle > maxBrittleSteps;
|
|
345
|
+
}
|
|
346
|
+
function stepKey(s) {
|
|
347
|
+
return s.stepId ?? `#${s.index}`;
|
|
348
|
+
}
|
|
349
|
+
function score(s) {
|
|
350
|
+
return (s.stability === "stable" ? 10 : 0) + LEVEL_RANK[s.level ?? (s.stability === "stable" ? "high" : "low")];
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* The trend of `current` against `baseline` (a previous report or run of the same Journey), keyed by
|
|
354
|
+
* step id (else index): a Journey re-recorded or healed after the app added test ids shows its
|
|
355
|
+
* improvement; a step that fell back to a brittle rung shows as a regression.
|
|
356
|
+
*/
|
|
357
|
+
export function compareLocatorHealth(current, baseline) {
|
|
358
|
+
const before = new Map(baseline.steps.map((s) => [stepKey(s), s]));
|
|
359
|
+
const seen = new Set();
|
|
360
|
+
const changes = [];
|
|
361
|
+
let unchanged = 0;
|
|
362
|
+
let added = 0;
|
|
363
|
+
const shape = (s) => ({ rung: s.rung, level: s.level ?? (s.stability === "stable" ? "high" : "low"), stability: s.stability });
|
|
364
|
+
for (const s of current.steps) {
|
|
365
|
+
const key = stepKey(s);
|
|
366
|
+
seen.add(key);
|
|
367
|
+
const b = before.get(key);
|
|
368
|
+
if (b === undefined) {
|
|
369
|
+
added++;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const delta = score(s) - score(b);
|
|
373
|
+
if (delta === 0)
|
|
374
|
+
unchanged++;
|
|
375
|
+
else
|
|
376
|
+
changes.push({ key, from: shape(b), to: shape(s), direction: delta > 0 ? "improved" : "regressed" });
|
|
377
|
+
}
|
|
378
|
+
const removed = [...before.keys()].filter((k) => !seen.has(k)).length;
|
|
379
|
+
return {
|
|
380
|
+
improved: changes.filter((c) => c.direction === "improved").length,
|
|
381
|
+
regressed: changes.filter((c) => c.direction === "regressed").length,
|
|
382
|
+
unchanged,
|
|
383
|
+
added,
|
|
384
|
+
removed,
|
|
385
|
+
brittleDelta: current.brittle - baseline.brittle,
|
|
386
|
+
changes,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
/** `trend vs baseline: 2 improved, 1 regressed (brittle -1)`. */
|
|
390
|
+
export function trendLine(t) {
|
|
391
|
+
const sign = t.brittleDelta > 0 ? `+${t.brittleDelta}` : String(t.brittleDelta);
|
|
392
|
+
return `trend vs baseline: ${t.improved} improved, ${t.regressed} regressed (brittle ${sign})`;
|
|
393
|
+
}
|
|
394
|
+
/** The compact form of a detail: per-step rung/stability/reasons, and the top `top` fixes. */
|
|
395
|
+
export function compactHealth(h, top = 5) {
|
|
396
|
+
return {
|
|
397
|
+
stable: h.stable,
|
|
398
|
+
brittle: h.brittle,
|
|
399
|
+
levels: h.levels,
|
|
400
|
+
line: h.line,
|
|
401
|
+
steps: h.steps.map((s) => ({
|
|
402
|
+
...(s.stepId === undefined ? {} : { stepId: s.stepId }),
|
|
403
|
+
index: s.index,
|
|
404
|
+
rung: s.rung,
|
|
405
|
+
stability: s.stability,
|
|
406
|
+
reasons: s.reasons,
|
|
407
|
+
...("level" in s ? { level: s.level } : {}),
|
|
408
|
+
})),
|
|
409
|
+
suggestions: h.suggestions.slice(0, top).map((s) => ({
|
|
410
|
+
key: s.key,
|
|
411
|
+
element: s.element,
|
|
412
|
+
fix: s.fix,
|
|
413
|
+
reasons: s.reasons,
|
|
414
|
+
...(s.route === undefined ? {} : { route: s.route }),
|
|
415
|
+
steps: s.occurrences.length,
|
|
416
|
+
})),
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
//# sourceMappingURL=locator-health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locator-health.js","sourceRoot":"","sources":["../src/locator-health.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AA6GpD,yBAAyB;AAEzB,MAAM,UAAU,GAA2C,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAC1F,qGAAqG;AACrG,MAAM,MAAM,GAAiD,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;AAE3G,MAAM,WAAW,GAAgD;IAC/D,YAAY,EAAE,YAAY;IAC1B,2BAA2B,EAAE,4CAA4C;IACzE,wBAAwB,EAAE,mDAAmD;IAC7E,iBAAiB,EAAE,8BAA8B;IACjD,oBAAoB,EAAE,oBAAoB;IAC1C,gBAAgB,EAAE,2CAA2C;IAC7D,gBAAgB,EAAE,6BAA6B;IAC/C,WAAW,EAAE,uCAAuC;CACrD,CAAC;AAEF,mGAAmG;AACnG,MAAM,UAAU,UAAU,CAAC,IAAU;IACnC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,OAAO,CAAC;QACb,KAAK,MAAM,CAAC;QACZ,KAAK,SAAS,CAAC;QACf,KAAK,SAAS,CAAC;QACf,KAAK,QAAQ,CAAC;QACd,KAAK,QAAQ,CAAC;QACd,KAAK,UAAU;YACb,OAAO,IAAI,CAAC,MAAM,CAAC;QACrB;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,SAAS,YAAY,CAAC,CAAmB;IACvC,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC;QAAE,OAAO,QAAQ,CAAC;IACpI,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED,SAAS,OAAO,CAAC,CAAmB;IAClC,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAC9B,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI;QAAE,OAAO,WAAW,CAAC;IACzC,IAAI,CAAC,CAAC,KAAK;QAAE,OAAO,OAAO,CAAC;IAC5B,IAAI,CAAC,CAAC,IAAI;QAAE,OAAO,MAAM,CAAC;IAC1B,IAAI,CAAC,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACxB,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;AACjC,CAAC;AAED,SAAS,MAAM,CAAC,CAAS;IACvB,OAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC;AACpH,CAAC;AAED,4DAA4D;AAC5D,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,uCAAuC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AACzG,CAAC;AAED,SAAS,eAAe,CAAC,CAAmB,EAAE,IAAiB;IAC7D,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;IAC/D,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ;YACX,OAAO,UAAU,CAAC,CAAC,UAAU,IAAI,aAAa,KAAK,CAAC,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QACvE,KAAK,QAAQ;YACX,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC;QAC/H,KAAK,WAAW;YACd,OAAO,QAAQ,CAAC,CAAC,IAAI,IAAI,GAAG,SAAS,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC;QAC5E,KAAK,OAAO;YACV,OAAO,SAAS,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC;QACxD,KAAK,MAAM;YACT,OAAO,QAAQ,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC;QACtD;YACE,OAAO,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;IACjC,CAAC;AACH,CAAC;AAQD;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,CAAmB,EAAE,gBAAmC,EAAE,GAAuB;IAC9G,MAAM,IAAI,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC;IAC5D,MAAM,KAAK,GAAwB,EAAE,CAAC;IACtC,IAAI,KAAmB,CAAC;IACxB,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,UAAU,IAAI,CAAC,CAAC,UAAU,IAAI,aAAa,CAAC,CAAC,WAAW,EAAE,CAAC;YAC9E,MAAM,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;YACjD,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;YACnC,IAAI,IAAI,KAAK,eAAe;gBAAE,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;iBAC9D,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;YACnF,IAAI,SAAS;gBAAE,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAC7C,MAAM;QACR,CAAC;QACD,KAAK,QAAQ;YACX,2GAA2G;YAC3G,KAAK,GAAG,QAAQ,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACzB,MAAM;QACR,KAAK,WAAW;YACd,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;YACtD,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACzB,IAAI,KAAK,KAAK,KAAK;gBAAE,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YACnD,MAAM;QACR,KAAK,OAAO;YACV,KAAK,GAAG,QAAQ,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACzB,MAAM;QACR,KAAK,MAAM;YACT,KAAK,GAAG,QAAQ,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;YACtC,MAAM;QACR,OAAO,CAAC,CAAC,CAAC;YACR,KAAK,GAAG,KAAK,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACzB,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;YACpC,IAAI,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACtE,IAAI,qCAAqC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC7F,CAAC;IACH,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACtF,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAChF,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,wBAAwB,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACpI,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAChC,CAAC;AAED,sBAAsB;AAEtB,sFAAsF;AACtF,MAAM,UAAU,SAAS,CAAC,CAAS;IACjC,OAAO,CAAC;SACL,SAAS,CAAC,MAAM,CAAC;SACjB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;SACtC,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACzB,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,OAAO,CAAC,GAAuB;IAC7C,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IACtD,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;QACzC,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAClD,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACxD,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,8FAA8F;AAC9F,SAAS,UAAU,CAAC,KAAyB;IAC3C,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,iCAAiC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAClJ,CAAC;AAED,SAAS,SAAS,CAAC,KAAyB;IAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AACtE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAwB,EAAE,KAAyB,EAAE,WAAmB;IACpG,MAAM,IAAI,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACvD,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAC9B,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACrH,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,OAAO,GAAG,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,WAAW,EAAE,CAAC;AAC3D,CAAC;AAED,SAAS,UAAU,CAAC,GAAuB;IACzC,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxD,OAAO,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,SAAS,CAAC;AAC3E,CAAC;AAED,SAAS,eAAe,CAAC,CAAmB;IAC1C,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC;IAC/B,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI;QAAE,OAAO,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACnE,IAAI,CAAC,CAAC,KAAK;QAAE,OAAO,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC3D,IAAI,CAAC,CAAC,IAAI;QAAE,OAAO,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACnC,IAAI,CAAC,CAAC,IAAI;QAAE,OAAO,yBAAyB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;IACtH,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,oBAAoB,CAAC,CAAC,UAAU,IAAI,aAAa,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACrG,OAAO,sBAAsB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,CAAmB,EAAE,KAAyB;IAC1E,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC;IACzC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,GAAG,KAAK,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC;IACxE,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,GAAG,KAAK,IAAI,EAAE,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC;IACvE,OAAO,GAAG,KAAK,IAAI,EAAE,QAAQ,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC;AAC7C,CAAC;AAED,SAAS,MAAM,CAAC,CAAmB,EAAE,KAAmC,EAAE,SAAiB,EAAE,MAAc,EAAE,OAAe,EAAE,KAAyB;IACrJ,MAAM,EAAE,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC;IACrD,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,8DAA8D,CAAC,CAAC,CAAC,EAAE,CAAC;IACzH,IAAI,KAAK,CAAC,QAAQ,CAAC,2BAA2B,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACvG,OAAO,OAAO,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,UAAU,IAAI,GAAG,OAAO,OAAO,GAAG,EAAE,YAAY,CAAC,CAAC,UAAU,IAAI,GAAG,iDAAiD,CAAC;IAC3L,CAAC;IACD,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QACvD,OAAO,yBAAyB,CAAC,CAAC,UAAU,IAAI,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,OAAO,GAAG,EAAE,kBAAkB,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;IAClK,CAAC;IACD,OAAO,OAAO,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,OAAO,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;AACjF,CAAC;AAED,SAAS,eAAe,CAAC,CAAmB,EAAE,KAAmC,EAAE,KAAyB,EAAE,SAAiB,EAAE,IAAwB,EAAE,UAA6B;IACtL,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC;IACzC,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9H,MAAM,OAAO,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;IACnC,OAAO;QACL,GAAG,EAAE,aAAa,CAAC,CAAC,EAAE,KAAK,CAAC;QAC5B,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;QACzC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACjD,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QACvC,OAAO;QACP,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5F,SAAS;QACT,MAAM;QACN,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;QACxD,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QACzC,WAAW,EAAE,CAAC,UAAU,CAAC;KAC1B,CAAC;AACJ,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,iBAAiB,CAAC,KAAkD;IAClF,MAAM,KAAK,GAAG,IAAI,GAAG,EAA6B,CAAC;IACnD,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACpB,SAAS;QACX,CAAC;QACD,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC;QAClC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;YAC9B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjH,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE;YACf,GAAG,IAAI;YACP,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5E,OAAO,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACtD,WAAW,EAAE,GAAG;SACjB,CAAC,CAAC;IACL,CAAC;IACD,uFAAuF;IACvF,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AACvH,CAAC;AAED,mBAAmB;AAEnB,SAAS,aAAa,CAAC,GAAc;IACnC,OAAO,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACpG,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;AACxC,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,UAAU,CAAC,CAAwF;IACjH,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC;IACnC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,0BAA0B,CAAC;IACnD,OAAO,GAAG,CAAC,CAAC,MAAM,IAAI,KAAK,8BAA8B,CAAC,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,CAAC,MAAM,CAAC,MAAM,UAAU,CAAC,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC;AACzJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAc,EAAE,IAAoB;IACnE,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC;IAC5D,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3E,MAAM,KAAK,GAA8B,EAAE,CAAC;IAC5C,MAAM,WAAW,GAAwB,EAAE,CAAC;IAC5C,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;IAC9C,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE;QACxD,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK,IAAI;YAAE,OAAO;QACvB,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACnC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACrB,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,MAAM,CAAC;QAC9C,IAAI,GAAuB,CAAC;QAC5B,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,UAAU,GAAsB,EAAE,GAAG,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YACrK,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC;YAC3F,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACpB,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;QACd,CAAC;QACD,KAAK,CAAC,IAAI,CAAC;YACT,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;YAC3C,KAAK;YACL,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI;YACxB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ;YACzC,MAAM,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU;YACnD,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;YACzC,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC;YACnC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YAC3C,WAAW,EAAE,CAAC,CAAC,KAAK;YACpB,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC;SAClD,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;IACpE,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;IACtC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;AAC1I,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,aAAa,CAAC,OAAuC;IACnE,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;IAC9C,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC;QACnB,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC;QACrB,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;QAC7B,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;QACjC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IAC7B,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;AACvJ,CAAC;AAED,eAAe;AAEf,6GAA6G;AAC7G,MAAM,UAAU,mBAAmB,CAAC,MAAsC,EAAE,eAAmC;IAC7G,OAAO,eAAe,KAAK,SAAS,IAAI,MAAM,CAAC,OAAO,GAAG,eAAe,CAAC;AAC3E,CAAC;AAyBD,SAAS,OAAO,CAAC,CAA8C;IAC7D,OAAO,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;AACnC,CAAC;AAED,SAAS,KAAK,CAAC,CAAiB;IAC9B,OAAO,CAAC,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAClH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAgF,EAAE,QAAiF;IACtM,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,OAAO,GAAsE,EAAE,CAAC;IACtF,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,KAAK,GAAG,CAAC,CAAiB,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAU,CAAC;IACxJ,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACvB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YACpB,KAAK,EAAE,CAAC;YACR,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,KAAK,KAAK,CAAC;YAAE,SAAS,EAAE,CAAC;;YACxB,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5G,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACtE,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC,MAAM;QAClE,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,WAAW,CAAC,CAAC,MAAM;QACpE,SAAS;QACT,KAAK;QACL,OAAO;QACP,YAAY,EAAE,OAAO,CAAC,OAAO,GAAG,QAAQ,CAAC,OAAO;QAChD,OAAO;KACR,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,SAAS,CAAC,CAAqB;IAC7C,MAAM,IAAI,GAAG,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IAChF,OAAO,sBAAsB,CAAC,CAAC,QAAQ,cAAc,CAAC,CAAC,SAAS,uBAAuB,IAAI,GAAG,CAAC;AACjG,CAAC;AAYD,8FAA8F;AAC9F,MAAM,UAAU,aAAa,CAAC,CAAgG,EAAE,GAAG,GAAG,CAAC;IACrI,OAAO;QACL,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACzB,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;YACvD,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5C,CAAC,CAAC;QACH,WAAW,EAAE,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnD,GAAG,EAAE,CAAC,CAAC,GAAG;YACV,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,GAAG,EAAE,CAAC,CAAC,GAAG;YACV,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,GAAG,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;YACpD,KAAK,EAAE,CAAC,CAAC,WAAW,CAAC,MAAM;SAC5B,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC"}
|
package/dist/mcp-api.d.ts
CHANGED
|
@@ -57,13 +57,15 @@ export interface McpJourneyRunOptions {
|
|
|
57
57
|
readonly fixtures?: (site: string) => MissionFixtures | undefined;
|
|
58
58
|
/** #303: record and compare each replayed step's action delta (`--action-deltas`). */
|
|
59
59
|
readonly actionDeltas?: boolean;
|
|
60
|
+
/** #486: the in-flight response bound of a step's responseStatus check (`--step-response-timeout`). */
|
|
61
|
+
readonly stepResponseTimeoutMs?: number;
|
|
60
62
|
}
|
|
61
63
|
/**
|
|
62
64
|
* #255: what `verify_fix` hands the runner — the finding (resolved result path + fingerprint) and
|
|
63
65
|
* the CLI-parity replay options. Operator-only settings (shell hooks, `cmd:` log sources, re-sending
|
|
64
66
|
* paid/destructive hang writes, redaction literals) are never MCP arguments: targets.json decides.
|
|
65
67
|
*/
|
|
66
|
-
export type McpVerifyFixArgs = Pick<RunVerifyFixOptions, "resultPath" | "fingerprint"> & Partial<Pick<RunVerifyFixOptions, "storageState" | "browser" | "replays" | "invariantFiles" | "fixtureFlags" | "emulation" | "allowEmulationOverride" | "screenshots" | "actionDeltas">>;
|
|
68
|
+
export type McpVerifyFixArgs = Pick<RunVerifyFixOptions, "resultPath" | "fingerprint"> & Partial<Pick<RunVerifyFixOptions, "storageState" | "browser" | "replays" | "invariantFiles" | "fixtureFlags" | "emulation" | "allowEmulationOverride" | "screenshots" | "actionDeltas" | "allow" | "env" | "environmentsFile">>;
|
|
67
69
|
export interface McpApiDeps {
|
|
68
70
|
/** Journeys store directory (`~/.jevitate/journeys` in production). */
|
|
69
71
|
journeysDir: string;
|
package/dist/mcp-api.js
CHANGED
|
@@ -25,6 +25,7 @@ import { parseScreenshotsArg } from "./run-screenshots.js";
|
|
|
25
25
|
import { makeEvidenceSelfHealer } from "./self-heal-adapter.js";
|
|
26
26
|
import { JourneyHealArgsError, MCP_HEAL_NAMES, journeyHealBudget, readJourneyChangeScope, validateJourneyHeal } from "./journey-heal.js";
|
|
27
27
|
import { ChangesArgsError, ChangesInputError } from "./change-context.js";
|
|
28
|
+
import { MAX_STEP_RESPONSE_TIMEOUT_MS } from "./journey-network-checks.js";
|
|
28
29
|
import { McpArgError, argErrorBody, optBool, optEnum, optInt, optNamedSessions, optPath, optExtensions, optRecordVideo, optScreenshots, optString, optStringArray, optStringMap, optViewport } from "./mcp-args.js";
|
|
29
30
|
import { defaultMcpPathRoots } from "./mcp-paths.js";
|
|
30
31
|
import { CLI_TOOL_SPECS, cliToolInputSchema, runCliTool } from "./mcp-cli-tools.js";
|
|
@@ -128,6 +129,7 @@ export function buildMcpTools(deps) {
|
|
|
128
129
|
...(o?.screenshots === undefined ? {} : { screenshots: o.screenshots }),
|
|
129
130
|
...(o?.fixtures === undefined ? {} : { fixtures: o.fixtures }),
|
|
130
131
|
...(o?.actionDeltas === true ? { actionDeltas: true } : {}),
|
|
132
|
+
...(o?.stepResponseTimeoutMs === undefined ? {} : { stepResponseTimeoutMs: o.stepResponseTimeoutMs }),
|
|
131
133
|
})));
|
|
132
134
|
const pathRoots = deps.pathRoots ?? defaultMcpPathRoots();
|
|
133
135
|
// queue_exploration: enqueue over the SAME promoted fs store `jevitate
|
|
@@ -332,6 +334,9 @@ export function buildMcpTools(deps) {
|
|
|
332
334
|
...(a.allowEmulationOverride === undefined ? {} : { allowEmulationOverride: a.allowEmulationOverride }),
|
|
333
335
|
...(a.screenshots === undefined ? {} : { screenshots: a.screenshots }),
|
|
334
336
|
...(a.actionDeltas === true ? { actionDeltas: true } : {}),
|
|
337
|
+
...(a.allow === undefined ? {} : { allow: a.allow }),
|
|
338
|
+
...(a.env === undefined ? {} : { env: a.env }),
|
|
339
|
+
...(a.environmentsFile === undefined ? {} : { environmentsFile: a.environmentsFile }),
|
|
335
340
|
}));
|
|
336
341
|
/** #255: verify_fix's CLI-parity replay options, validated like `verify-fix`'s flags (typed, before any browser). */
|
|
337
342
|
const verifyFixOptions = (args) => {
|
|
@@ -353,6 +358,12 @@ export function buildMcpTools(deps) {
|
|
|
353
358
|
const extension = optExtensions(args, pathRoots);
|
|
354
359
|
const governance = governanceArgs(args);
|
|
355
360
|
const actionDeltas = optBool(args, "actionDeltas");
|
|
361
|
+
// #484: replaying under another allow list widens what the replay may reach — explicit only (never
|
|
362
|
+
// defaulted), and the report carries allowlistOverride. An MCP caller never names origins: only
|
|
363
|
+
// `env` (an operator-configured environment) can override the stored list; `--allow` is CLI-only.
|
|
364
|
+
if (args["allow"] !== undefined)
|
|
365
|
+
throw new McpArgError("'allow' is not an MCP argument: a replay reaches only operator-configured origins — pass 'env' (an environment in environments.json), or run `jevitate verify-fix --allow` yourself");
|
|
366
|
+
const env = optString(args, "env");
|
|
356
367
|
try {
|
|
357
368
|
const browser = browserRunFromFlags({ browserArg: [], ...governance, ...(extension === undefined ? {} : { extension }), ...(headed === undefined ? {} : { headed }), ...(slowMo === undefined ? {} : { slowMo }), ...(recordVideo === undefined ? {} : { recordVideo }) });
|
|
358
369
|
const screenshots = parseScreenshotsArg(shots);
|
|
@@ -369,6 +380,7 @@ export function buildMcpTools(deps) {
|
|
|
369
380
|
...(screenshots === undefined ? {} : { screenshots }),
|
|
370
381
|
...(emulation === undefined ? {} : { emulation }),
|
|
371
382
|
...(actionDeltas === true ? { actionDeltas: true } : {}),
|
|
383
|
+
...(env === undefined ? {} : { env, ...(deps.environmentsFile === undefined ? {} : { environmentsFile: deps.environmentsFile }) }),
|
|
372
384
|
};
|
|
373
385
|
}
|
|
374
386
|
catch (err) {
|
|
@@ -517,6 +529,10 @@ export function buildMcpTools(deps) {
|
|
|
517
529
|
const healMaxMs = optInt(args, "healMaxMs", 1);
|
|
518
530
|
const healMaxRunAttempts = optInt(args, "healMaxRunAttempts", 1);
|
|
519
531
|
const healMaxRunMs = optInt(args, "healMaxRunMs", 1);
|
|
532
|
+
const stepResponseTimeoutMs = optInt(args, "stepResponseTimeoutMs", 1);
|
|
533
|
+
if (stepResponseTimeoutMs !== undefined && stepResponseTimeoutMs > MAX_STEP_RESPONSE_TIMEOUT_MS) {
|
|
534
|
+
throw new McpArgError(`'stepResponseTimeoutMs' must be an integer from 1 to ${MAX_STEP_RESPONSE_TIMEOUT_MS}`);
|
|
535
|
+
}
|
|
520
536
|
const healRequest = {
|
|
521
537
|
selfHeal,
|
|
522
538
|
...(changes === undefined ? {} : { changes }),
|
|
@@ -559,6 +575,7 @@ export function buildMcpTools(deps) {
|
|
|
559
575
|
...(emulation === undefined ? {} : { emulation }),
|
|
560
576
|
...(screenshots === undefined ? {} : { screenshots }),
|
|
561
577
|
...(optBool(args, "actionDeltas") === true ? { actionDeltas: true } : {}),
|
|
578
|
+
...(stepResponseTimeoutMs === undefined ? {} : { stepResponseTimeoutMs }),
|
|
562
579
|
},
|
|
563
580
|
...(usage === undefined ? {} : { usage }),
|
|
564
581
|
};
|
|
@@ -568,7 +585,7 @@ export function buildMcpTools(deps) {
|
|
|
568
585
|
description: "Replay a finding's reproduction (by mission result id — or a finished queue_exploration missionId — + fingerprint) N times in fresh browsers (default 3) — `jevitate verify-fix`. status: fixed (signal absent on every replay) | still-reproduces | intermittent (fired on some but not all replays — never a pass) | inconclusive (replay could not reach the step — never a pass). " +
|
|
569
586
|
"#255 (same as the CLI flags): 'replays' (>= 1); 'recordVideo' (true or a directory: the before/after evidence pair — `evidence.before` is the run's own clip, `evidence.after` a captioned replay — plus videoPaths); 'screenshots'; 'headed'/'slowMo'; " +
|
|
570
587
|
"'storageState' (a path inside the project or ~/.jevitate, never a repo's .jevitate/; overrides the mission's session); 'viewport' {width,height} or 'device' (refused when it differs from the finding's recorded emulation unless 'allowEmulationOverride'); 'geolocation' '<lat>,<lng>[,<accuracy m>]' (#329); " +
|
|
571
|
-
"'invariants' (invariant file paths re-checking a declared-invariant defect); 'fixtures' (a fixtures file overriding the mission's saved one); 'fixtureIdentity' (#243: 'name=<storageState path>' entries re-binding a fixture step's auth.identity; default: the identities the mission recorded); 'extension' (#256: unpacked extension directories — refused unless the same build the finding was recorded with); 'actionDeltas' (#303, opt-in: the defect step's replayed delta vs the recorded one, as evidence on each attempt); 'maxBrowsers'/'maxBrowserMemory' (#205: machine-wide browser cap, browser memory ceiling in MiB); 'tags' (#426: {key: value} run metadata, returned on the result — never a secret). Operator-only settings (shell hooks, cmd: log sources, re-sending paid/destructive hang writes) come from targets.json, never an argument.",
|
|
588
|
+
"'invariants' (invariant file paths re-checking a declared-invariant defect); 'fixtures' (a fixtures file overriding the mission's saved one); 'fixtureIdentity' (#243: 'name=<storageState path>' entries re-binding a fixture step's auth.identity; default: the identities the mission recorded); 'extension' (#256: unpacked extension directories — refused unless the same build the finding was recorded with); 'actionDeltas' (#303, opt-in: the defect step's replayed delta vs the recorded one, as evidence on each attempt); 'maxBrowsers'/'maxBrowserMemory' (#205: machine-wide browser cap, browser memory ceiling in MiB); 'tags' (#426: {key: value} run metadata, returned on the result — never a secret); #484 'env' (a named environment in environments.json: replay under its CURRENT allow list instead of the one stored with the result, e.g. to prove a localhost → 127.0.0.1 config fix; explicit only, never defaulted) — the result then carries allowlistOverride (source, origins, recorded, added, removed); raw origins are CLI-only (`jevitate verify-fix --allow`), never an MCP argument. Operator-only settings (shell hooks, cmd: log sources, re-sending paid/destructive hang writes) come from targets.json, never an argument.",
|
|
572
589
|
inputSchema: {
|
|
573
590
|
type: "object",
|
|
574
591
|
properties: {
|
|
@@ -592,6 +609,7 @@ export function buildMcpTools(deps) {
|
|
|
592
609
|
maxBrowsers: { type: "integer", minimum: 1 },
|
|
593
610
|
maxBrowserMemory: { type: "integer", minimum: 1 },
|
|
594
611
|
tags: { type: "object", additionalProperties: { type: "string" } },
|
|
612
|
+
env: { type: "string" },
|
|
595
613
|
},
|
|
596
614
|
required: ["id", "fingerprint"],
|
|
597
615
|
},
|
|
@@ -615,7 +633,7 @@ export function buildMcpTools(deps) {
|
|
|
615
633
|
"with a clear error when no storageState is given. #255 (same as the CLI flags): 'env' (a named environment in .jevitate/environments.json; its session from ~/.jevitate/targets.json) and/or 'baseUrl'; " +
|
|
616
634
|
"'headed' (needs a display) and 'slowMo'; 'recordVideo' (true, or a directory) → videoPaths; 'screenshots' (true | screens | steps | mode:<dir> | <dir>) → screenshotPaths; " +
|
|
617
635
|
"'viewport' {width,height} or 'device' (mutually exclusive); 'geolocation' '<lat>,<lng>[,<accuracy m>]' (#329); 'fixtures' (a fixtures JSON path: setup before, restore after; 'fixtureIdentity' (#243) 'name=<storageState path>' entries name who a step with auth.identity authenticates as; an environment's shell hooks are never run over MCP — they refuse as the CLI does without --allow-shell-hooks); " +
|
|
618
|
-
"'extension' (#256: unpacked extension directories inside the project or ~/.jevitate; a Journey recorded with extensions needs the same build); 'selfHeal' fail-closed (default) | hybrid | full with 'real' or 'fakeAi' (a write step never self-heals; 'jevProvider' typesafe | openrouter picks the Jev key with real, #429) — #453: a heal mode needs a change context, 'changes' (a git range such as HEAD~1..HEAD, read-only) and/or 'changeNote' (strings), else it is refused; only a break that change explains is retargeted, one-for-one, and the run ends healed-pending-review (exitCode 5: a proposed revision a person accepts, never a pass); budgets 'healMaxAttempts'/'healMaxModelCalls'/'healMaxMs' (per broken step) and 'healMaxRunAttempts'/'healMaxRunMs' (per run), each >= 1; the result's 'exitCode' is the CLI's (0 ok, 5 pending review, 1 failed); 'actionDeltas' (#303, opt-in: each replayed step's action delta, compared with the recorded one → actionDeltas); 'maxBrowsers'/'maxBrowserMemory' (#205: machine-wide browser cap, browser memory ceiling in MiB); 'tags' (#426: {key: value} run metadata, returned on the result — never a secret). A site-policy refusal is {error: throttled, retryAfter}.",
|
|
636
|
+
"'extension' (#256: unpacked extension directories inside the project or ~/.jevitate; a Journey recorded with extensions needs the same build); 'selfHeal' fail-closed (default) | hybrid | full with 'real' or 'fakeAi' (a write step never self-heals; 'jevProvider' typesafe | openrouter picks the Jev key with real, #429) — #453: a heal mode needs a change context, 'changes' (a git range such as HEAD~1..HEAD, read-only) and/or 'changeNote' (strings), else it is refused; only a break that change explains is retargeted, one-for-one, and the run ends healed-pending-review (exitCode 5: a proposed revision a person accepts, never a pass); budgets 'healMaxAttempts'/'healMaxModelCalls'/'healMaxMs' (per broken step) and 'healMaxRunAttempts'/'healMaxRunMs' (per run), each >= 1; the result's 'exitCode' is the CLI's (0 ok, 5 pending review, 1 failed); 'actionDeltas' (#303, opt-in: each replayed step's action delta, compared with the recorded one → actionDeltas); 'stepResponseTimeoutMs' (#486, 1..300000, default 15000: how long a step's responseStatus check waits for a request sent but not yet answered, e.g. a cold start — a late 2xx is an advisory slowSteps finding; a step's own responseTimeoutMs overrides it); 'maxBrowsers'/'maxBrowserMemory' (#205: machine-wide browser cap, browser memory ceiling in MiB); 'tags' (#426: {key: value} run metadata, returned on the result — never a secret). A site-policy refusal is {error: throttled, retryAfter}.",
|
|
619
637
|
inputSchema: {
|
|
620
638
|
type: "object",
|
|
621
639
|
properties: {
|
|
@@ -648,6 +666,7 @@ export function buildMcpTools(deps) {
|
|
|
648
666
|
maxBrowsers: { type: "integer", minimum: 1 },
|
|
649
667
|
maxBrowserMemory: { type: "integer", minimum: 1 },
|
|
650
668
|
actionDeltas: { type: "boolean" },
|
|
669
|
+
stepResponseTimeoutMs: { type: "integer", minimum: 1, maximum: MAX_STEP_RESPONSE_TIMEOUT_MS },
|
|
651
670
|
tags: { type: "object", additionalProperties: { type: "string" } },
|
|
652
671
|
},
|
|
653
672
|
required: ["id"],
|