@kevinrabun/judges 3.57.0 → 3.59.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/CHANGELOG.md +24 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +112 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/api-misuse.d.ts +5 -0
- package/dist/commands/api-misuse.d.ts.map +1 -0
- package/dist/commands/api-misuse.js +261 -0
- package/dist/commands/api-misuse.js.map +1 -0
- package/dist/commands/assertion-density.d.ts +5 -0
- package/dist/commands/assertion-density.d.ts.map +1 -0
- package/dist/commands/assertion-density.js +264 -0
- package/dist/commands/assertion-density.js.map +1 -0
- package/dist/commands/async-safety.d.ts +5 -0
- package/dist/commands/async-safety.d.ts.map +1 -0
- package/dist/commands/async-safety.js +267 -0
- package/dist/commands/async-safety.js.map +1 -0
- package/dist/commands/clone-detect.d.ts +5 -0
- package/dist/commands/clone-detect.d.ts.map +1 -0
- package/dist/commands/clone-detect.js +233 -0
- package/dist/commands/clone-detect.js.map +1 -0
- package/dist/commands/completion-audit.d.ts +5 -0
- package/dist/commands/completion-audit.d.ts.map +1 -0
- package/dist/commands/completion-audit.js +297 -0
- package/dist/commands/completion-audit.js.map +1 -0
- package/dist/commands/contract-verify.d.ts +5 -0
- package/dist/commands/contract-verify.d.ts.map +1 -0
- package/dist/commands/contract-verify.js +317 -0
- package/dist/commands/contract-verify.js.map +1 -0
- package/dist/commands/cross-file-consistency.d.ts +5 -0
- package/dist/commands/cross-file-consistency.d.ts.map +1 -0
- package/dist/commands/cross-file-consistency.js +255 -0
- package/dist/commands/cross-file-consistency.js.map +1 -0
- package/dist/commands/dead-code-detect.d.ts +5 -0
- package/dist/commands/dead-code-detect.d.ts.map +1 -0
- package/dist/commands/dead-code-detect.js +256 -0
- package/dist/commands/dead-code-detect.js.map +1 -0
- package/dist/commands/encoding-safety.d.ts +5 -0
- package/dist/commands/encoding-safety.d.ts.map +1 -0
- package/dist/commands/encoding-safety.js +276 -0
- package/dist/commands/encoding-safety.js.map +1 -0
- package/dist/commands/example-leak.d.ts +5 -0
- package/dist/commands/example-leak.d.ts.map +1 -0
- package/dist/commands/example-leak.js +233 -0
- package/dist/commands/example-leak.js.map +1 -0
- package/dist/commands/input-guard.d.ts +5 -0
- package/dist/commands/input-guard.d.ts.map +1 -0
- package/dist/commands/input-guard.js +256 -0
- package/dist/commands/input-guard.js.map +1 -0
- package/dist/commands/logic-lint.d.ts +5 -0
- package/dist/commands/logic-lint.d.ts.map +1 -0
- package/dist/commands/logic-lint.js +256 -0
- package/dist/commands/logic-lint.js.map +1 -0
- package/dist/commands/phantom-import.d.ts +5 -0
- package/dist/commands/phantom-import.d.ts.map +1 -0
- package/dist/commands/phantom-import.js +261 -0
- package/dist/commands/phantom-import.js.map +1 -0
- package/dist/commands/review-focus.d.ts +5 -0
- package/dist/commands/review-focus.d.ts.map +1 -0
- package/dist/commands/review-focus.js +197 -0
- package/dist/commands/review-focus.js.map +1 -0
- package/dist/commands/spec-conform.d.ts +5 -0
- package/dist/commands/spec-conform.d.ts.map +1 -0
- package/dist/commands/spec-conform.js +305 -0
- package/dist/commands/spec-conform.js.map +1 -0
- package/dist/commands/state-integrity.d.ts +5 -0
- package/dist/commands/state-integrity.d.ts.map +1 -0
- package/dist/commands/state-integrity.js +284 -0
- package/dist/commands/state-integrity.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completion audit — verify AI-generated code is complete and not truncated.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, readdirSync, statSync } from "fs";
|
|
5
|
+
import { join, extname } from "path";
|
|
6
|
+
// ─── File Collection ────────────────────────────────────────────────────────
|
|
7
|
+
const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go", ".rs", ".cs"]);
|
|
8
|
+
function collectFiles(dir, max = 300) {
|
|
9
|
+
const files = [];
|
|
10
|
+
function walk(d) {
|
|
11
|
+
if (files.length >= max)
|
|
12
|
+
return;
|
|
13
|
+
let entries;
|
|
14
|
+
try {
|
|
15
|
+
entries = readdirSync(d);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
for (const e of entries) {
|
|
21
|
+
if (files.length >= max)
|
|
22
|
+
return;
|
|
23
|
+
if (e.startsWith(".") || e === "node_modules" || e === "dist" || e === "build")
|
|
24
|
+
continue;
|
|
25
|
+
const full = join(d, e);
|
|
26
|
+
try {
|
|
27
|
+
if (statSync(full).isDirectory())
|
|
28
|
+
walk(full);
|
|
29
|
+
else if (CODE_EXTS.has(extname(full)))
|
|
30
|
+
files.push(full);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* skip */
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
walk(dir);
|
|
38
|
+
return files;
|
|
39
|
+
}
|
|
40
|
+
// ─── Analysis ───────────────────────────────────────────────────────────────
|
|
41
|
+
function analyzeFile(filepath) {
|
|
42
|
+
const issues = [];
|
|
43
|
+
let content;
|
|
44
|
+
try {
|
|
45
|
+
content = readFileSync(filepath, "utf-8");
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return issues;
|
|
49
|
+
}
|
|
50
|
+
const lines = content.split("\n");
|
|
51
|
+
// Check bracket balance (unmatched braces, parens, brackets)
|
|
52
|
+
let braces = 0;
|
|
53
|
+
let parens = 0;
|
|
54
|
+
let brackets = 0;
|
|
55
|
+
let inString = false;
|
|
56
|
+
let stringChar = "";
|
|
57
|
+
for (let i = 0; i < content.length; i++) {
|
|
58
|
+
const ch = content[i];
|
|
59
|
+
const prev = i > 0 ? content[i - 1] : "";
|
|
60
|
+
if (inString) {
|
|
61
|
+
if (ch === stringChar && prev !== "\\")
|
|
62
|
+
inString = false;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
66
|
+
inString = true;
|
|
67
|
+
stringChar = ch;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (ch === "/" && (content[i + 1] === "/" || content[i + 1] === "*")) {
|
|
71
|
+
if (content[i + 1] === "/") {
|
|
72
|
+
while (i < content.length && content[i] !== "\n")
|
|
73
|
+
i++;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
i += 2;
|
|
77
|
+
while (i < content.length - 1 && !(content[i] === "*" && content[i + 1] === "/"))
|
|
78
|
+
i++;
|
|
79
|
+
i++;
|
|
80
|
+
}
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (ch === "{")
|
|
84
|
+
braces++;
|
|
85
|
+
if (ch === "}")
|
|
86
|
+
braces--;
|
|
87
|
+
if (ch === "(")
|
|
88
|
+
parens++;
|
|
89
|
+
if (ch === ")")
|
|
90
|
+
parens--;
|
|
91
|
+
if (ch === "[")
|
|
92
|
+
brackets++;
|
|
93
|
+
if (ch === "]")
|
|
94
|
+
brackets--;
|
|
95
|
+
}
|
|
96
|
+
if (braces > 0) {
|
|
97
|
+
issues.push({
|
|
98
|
+
file: filepath,
|
|
99
|
+
line: lines.length,
|
|
100
|
+
issue: "Unmatched opening brace(s)",
|
|
101
|
+
severity: "high",
|
|
102
|
+
detail: `${braces} unclosed \`{\` — code may be truncated mid-block`,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (parens > 0) {
|
|
106
|
+
issues.push({
|
|
107
|
+
file: filepath,
|
|
108
|
+
line: lines.length,
|
|
109
|
+
issue: "Unmatched opening parenthesis",
|
|
110
|
+
severity: "high",
|
|
111
|
+
detail: `${parens} unclosed \`(\` — function call or expression may be incomplete`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (brackets > 0) {
|
|
115
|
+
issues.push({
|
|
116
|
+
file: filepath,
|
|
117
|
+
line: lines.length,
|
|
118
|
+
issue: "Unmatched opening bracket",
|
|
119
|
+
severity: "high",
|
|
120
|
+
detail: `${brackets} unclosed \`[\` — array literal may be truncated`,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
for (let i = 0; i < lines.length; i++) {
|
|
124
|
+
const line = lines[i];
|
|
125
|
+
const trimmed = line.trim();
|
|
126
|
+
// "... rest of implementation" style truncation markers
|
|
127
|
+
if (/\/\/\s*\.{3}\s*(?:rest|remaining|more|other|additional|etc|and so on|similar|same as|continue|implement)/i.test(trimmed)) {
|
|
128
|
+
issues.push({
|
|
129
|
+
file: filepath,
|
|
130
|
+
line: i + 1,
|
|
131
|
+
issue: "Truncation marker comment",
|
|
132
|
+
severity: "high",
|
|
133
|
+
detail: 'AI left a "... rest" placeholder — code is incomplete',
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
// TODO/FIXME indicating incomplete implementation
|
|
137
|
+
if (/(?:\/\/|#)\s*TODO:?\s*(?:implement|add|finish|complete|fill|write|handle)/i.test(trimmed)) {
|
|
138
|
+
issues.push({
|
|
139
|
+
file: filepath,
|
|
140
|
+
line: i + 1,
|
|
141
|
+
issue: "TODO indicates unfinished implementation",
|
|
142
|
+
severity: "medium",
|
|
143
|
+
detail: "TODO comment suggests code needs additional implementation",
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
// throw new Error("not implemented")
|
|
147
|
+
if (/throw\s+new\s+Error\s*\(\s*['"](?:not implemented|todo|fixme|implement me|unimplemented|stub)/i.test(trimmed)) {
|
|
148
|
+
issues.push({
|
|
149
|
+
file: filepath,
|
|
150
|
+
line: i + 1,
|
|
151
|
+
issue: "Not-implemented error thrown",
|
|
152
|
+
severity: "high",
|
|
153
|
+
detail: "Function throws 'not implemented' — AI left a stub that will crash at runtime",
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// pass/NotImplementedError (Python)
|
|
157
|
+
if (/raise\s+NotImplementedError/.test(trimmed)) {
|
|
158
|
+
issues.push({
|
|
159
|
+
file: filepath,
|
|
160
|
+
line: i + 1,
|
|
161
|
+
issue: "NotImplementedError raised",
|
|
162
|
+
severity: "high",
|
|
163
|
+
detail: "Python function raises NotImplementedError — implementation is missing",
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
// Empty function/method bodies
|
|
167
|
+
if (/(?:function|def|fn)\s+\w+/.test(trimmed)) {
|
|
168
|
+
const block = lines
|
|
169
|
+
.slice(i + 1, Math.min(i + 4, lines.length))
|
|
170
|
+
.join("\n")
|
|
171
|
+
.trim();
|
|
172
|
+
if (/^(?:\{[\s]*\}|pass\s*$)/.test(block)) {
|
|
173
|
+
issues.push({
|
|
174
|
+
file: filepath,
|
|
175
|
+
line: i + 1,
|
|
176
|
+
issue: "Empty function body",
|
|
177
|
+
severity: "medium",
|
|
178
|
+
detail: "Function declared but body is empty — may be unfinished stub",
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Ellipsis in code (not in strings or comments)
|
|
183
|
+
if (/^\s*\.{3}\s*$/.test(trimmed) && !/['"]/.test(line)) {
|
|
184
|
+
issues.push({
|
|
185
|
+
file: filepath,
|
|
186
|
+
line: i + 1,
|
|
187
|
+
issue: "Ellipsis placeholder",
|
|
188
|
+
severity: "high",
|
|
189
|
+
detail: "Bare `...` on its own line — indicates truncated AI output",
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
// Comment indicating AI truncation
|
|
193
|
+
if (/\/\*\s*\.\.\.\s*\*\//.test(trimmed) || /\/\/\s*\.\.\.\s*$/.test(trimmed)) {
|
|
194
|
+
issues.push({
|
|
195
|
+
file: filepath,
|
|
196
|
+
line: i + 1,
|
|
197
|
+
issue: "Ellipsis comment",
|
|
198
|
+
severity: "medium",
|
|
199
|
+
detail: "Comment with just `...` — AI may have truncated output here",
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
// "your code here" / "add your logic"
|
|
203
|
+
if (/(?:your|add|put|insert|write)\s+(?:code|logic|implementation|handling)\s+here/i.test(trimmed)) {
|
|
204
|
+
issues.push({
|
|
205
|
+
file: filepath,
|
|
206
|
+
line: i + 1,
|
|
207
|
+
issue: "Placeholder instruction comment",
|
|
208
|
+
severity: "high",
|
|
209
|
+
detail: 'AI left a "your code here" placeholder — implementation required',
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// Interface/type with no usage
|
|
213
|
+
if (/(?:interface|type)\s+(\w+)\s*[={<]/.test(trimmed)) {
|
|
214
|
+
const typeName = trimmed.match(/(?:interface|type)\s+(\w+)/)?.[1];
|
|
215
|
+
if (typeName) {
|
|
216
|
+
const usageCount = (content.match(new RegExp(`\\b${typeName}\\b`, "g")) || []).length;
|
|
217
|
+
if (usageCount <= 1 && !/export/.test(trimmed)) {
|
|
218
|
+
issues.push({
|
|
219
|
+
file: filepath,
|
|
220
|
+
line: i + 1,
|
|
221
|
+
issue: "Declared type never used",
|
|
222
|
+
severity: "low",
|
|
223
|
+
detail: `\`${typeName}\` declared but never referenced — may be leftover scaffold`,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// File ends abruptly (last non-empty line is not a closing bracket/brace)
|
|
230
|
+
const lastNonEmpty = lines.filter((l) => l.trim()).pop() || "";
|
|
231
|
+
if (lastNonEmpty.trim() && !/^[}\])]|^$|^\/\/|^\*\/|^#/.test(lastNonEmpty.trim()) && braces !== 0) {
|
|
232
|
+
issues.push({
|
|
233
|
+
file: filepath,
|
|
234
|
+
line: lines.length,
|
|
235
|
+
issue: "File may end abruptly",
|
|
236
|
+
severity: "medium",
|
|
237
|
+
detail: "File ends with unclosed blocks — output may have been truncated",
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return issues;
|
|
241
|
+
}
|
|
242
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
243
|
+
export function runCompletionAudit(argv) {
|
|
244
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
245
|
+
console.log(`
|
|
246
|
+
judges completion-audit — Verify AI-generated code is complete and not truncated
|
|
247
|
+
|
|
248
|
+
Usage:
|
|
249
|
+
judges completion-audit [dir]
|
|
250
|
+
judges completion-audit src/ --format json
|
|
251
|
+
|
|
252
|
+
Options:
|
|
253
|
+
[dir] Directory to scan (default: .)
|
|
254
|
+
--format json JSON output
|
|
255
|
+
--help, -h Show this help
|
|
256
|
+
|
|
257
|
+
Checks: unmatched brackets/braces, truncation markers, TODO stubs, NotImplementedError,
|
|
258
|
+
empty function bodies, ellipsis placeholders, "your code here" comments, unused types.
|
|
259
|
+
`);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
263
|
+
const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
|
|
264
|
+
const files = collectFiles(dir);
|
|
265
|
+
const allIssues = [];
|
|
266
|
+
for (const f of files)
|
|
267
|
+
allIssues.push(...analyzeFile(f));
|
|
268
|
+
const highCount = allIssues.filter((i) => i.severity === "high").length;
|
|
269
|
+
const medCount = allIssues.filter((i) => i.severity === "medium").length;
|
|
270
|
+
const score = Math.max(0, 100 - highCount * 15 - medCount * 5);
|
|
271
|
+
if (format === "json") {
|
|
272
|
+
console.log(JSON.stringify({
|
|
273
|
+
issues: allIssues,
|
|
274
|
+
score,
|
|
275
|
+
summary: { high: highCount, medium: medCount, total: allIssues.length },
|
|
276
|
+
timestamp: new Date().toISOString(),
|
|
277
|
+
}, null, 2));
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
const badge = score >= 80 ? "✅ COMPLETE" : score >= 50 ? "⚠️ GAPS" : "❌ INCOMPLETE";
|
|
281
|
+
console.log(`\n Completion Audit: ${badge} (${score}/100)\n ─────────────────────────────`);
|
|
282
|
+
if (allIssues.length === 0) {
|
|
283
|
+
console.log(" No completeness issues detected.\n");
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
for (const issue of allIssues.slice(0, 25)) {
|
|
287
|
+
const icon = issue.severity === "high" ? "🔴" : issue.severity === "medium" ? "🟡" : "🔵";
|
|
288
|
+
console.log(` ${icon} ${issue.issue}`);
|
|
289
|
+
console.log(` ${issue.file}:${issue.line}`);
|
|
290
|
+
console.log(` ${issue.detail}`);
|
|
291
|
+
}
|
|
292
|
+
if (allIssues.length > 25)
|
|
293
|
+
console.log(` ... and ${allIssues.length - 25} more`);
|
|
294
|
+
console.log(`\n Total: ${allIssues.length} | High: ${highCount} | Medium: ${medCount} | Score: ${score}/100\n`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=completion-audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion-audit.js","sourceRoot":"","sources":["../../src/commands/completion-audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-verify.d.ts","sourceRoot":"","sources":["../../src/commands/contract-verify.ts"],"names":[],"mappings":"AAAA;;GAEG;AA6QH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAiFtD"}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract verify — check that API implementations match declared contracts (OpenAPI, GraphQL, protobuf).
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, readdirSync, statSync, existsSync } from "fs";
|
|
5
|
+
import { join, extname } from "path";
|
|
6
|
+
// ─── File Collection ────────────────────────────────────────────────────────
|
|
7
|
+
const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".java", ".go"]);
|
|
8
|
+
function collectFiles(dir, max = 300) {
|
|
9
|
+
const files = [];
|
|
10
|
+
function walk(d) {
|
|
11
|
+
if (files.length >= max)
|
|
12
|
+
return;
|
|
13
|
+
let entries;
|
|
14
|
+
try {
|
|
15
|
+
entries = readdirSync(d);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
for (const e of entries) {
|
|
21
|
+
if (files.length >= max)
|
|
22
|
+
return;
|
|
23
|
+
if (e.startsWith(".") || e === "node_modules" || e === "dist" || e === "build")
|
|
24
|
+
continue;
|
|
25
|
+
const full = join(d, e);
|
|
26
|
+
try {
|
|
27
|
+
if (statSync(full).isDirectory())
|
|
28
|
+
walk(full);
|
|
29
|
+
else if (CODE_EXTS.has(extname(full)) || /\.(json|ya?ml|graphql|gql|proto)$/.test(full))
|
|
30
|
+
files.push(full);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* skip */
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
walk(dir);
|
|
38
|
+
return files;
|
|
39
|
+
}
|
|
40
|
+
// ─── OpenAPI Analysis ───────────────────────────────────────────────────────
|
|
41
|
+
function findOpenApiSpecs(dir) {
|
|
42
|
+
const specs = [];
|
|
43
|
+
const candidates = [
|
|
44
|
+
"openapi.json",
|
|
45
|
+
"openapi.yaml",
|
|
46
|
+
"openapi.yml",
|
|
47
|
+
"swagger.json",
|
|
48
|
+
"swagger.yaml",
|
|
49
|
+
"swagger.yml",
|
|
50
|
+
"api-spec.json",
|
|
51
|
+
"api-spec.yaml",
|
|
52
|
+
];
|
|
53
|
+
for (const name of candidates) {
|
|
54
|
+
const p = join(dir, name);
|
|
55
|
+
if (existsSync(p))
|
|
56
|
+
specs.push(p);
|
|
57
|
+
}
|
|
58
|
+
// Also check docs/ and api/ subdirectories
|
|
59
|
+
for (const sub of ["docs", "api", "spec", "specs"]) {
|
|
60
|
+
const subDir = join(dir, sub);
|
|
61
|
+
if (existsSync(subDir)) {
|
|
62
|
+
for (const name of candidates) {
|
|
63
|
+
const p = join(subDir, name);
|
|
64
|
+
if (existsSync(p))
|
|
65
|
+
specs.push(p);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return specs;
|
|
70
|
+
}
|
|
71
|
+
function extractOpenApiPaths(specContent) {
|
|
72
|
+
const routes = [];
|
|
73
|
+
try {
|
|
74
|
+
const spec = JSON.parse(specContent);
|
|
75
|
+
const paths = spec.paths || {};
|
|
76
|
+
for (const [path, methods] of Object.entries(paths)) {
|
|
77
|
+
if (typeof methods !== "object" || methods === null)
|
|
78
|
+
continue;
|
|
79
|
+
for (const [method, detail] of Object.entries(methods)) {
|
|
80
|
+
if (["get", "post", "put", "delete", "patch"].includes(method)) {
|
|
81
|
+
const responses = detail && typeof detail === "object" && "responses" in detail
|
|
82
|
+
? Object.keys(detail.responses)
|
|
83
|
+
: [];
|
|
84
|
+
routes.push({ method: method.toUpperCase(), path, responses });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
/* not valid JSON spec */
|
|
91
|
+
}
|
|
92
|
+
return routes;
|
|
93
|
+
}
|
|
94
|
+
// ─── Implementation Analysis ────────────────────────────────────────────────
|
|
95
|
+
function analyzeContractDrift(files, specRoutes) {
|
|
96
|
+
const issues = [];
|
|
97
|
+
const implementedRoutes = new Set();
|
|
98
|
+
// Find implemented routes in code
|
|
99
|
+
for (const filepath of files) {
|
|
100
|
+
let content;
|
|
101
|
+
try {
|
|
102
|
+
content = readFileSync(filepath, "utf-8");
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (!/\.(?:ts|tsx|js|jsx|py|java|go)$/.test(filepath))
|
|
108
|
+
continue;
|
|
109
|
+
const lines = content.split("\n");
|
|
110
|
+
for (let i = 0; i < lines.length; i++) {
|
|
111
|
+
const line = lines[i];
|
|
112
|
+
const routeMatch = line.match(/(?:router|app)\.\s*(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]/i);
|
|
113
|
+
if (routeMatch) {
|
|
114
|
+
const method = routeMatch[1].toUpperCase();
|
|
115
|
+
const path = routeMatch[2];
|
|
116
|
+
implementedRoutes.add(`${method} ${path}`);
|
|
117
|
+
// Check response status codes used
|
|
118
|
+
const handlerBlock = lines.slice(i, Math.min(i + 30, lines.length)).join("\n");
|
|
119
|
+
const usedStatuses = [...handlerBlock.matchAll(/\.status\s*\(\s*(\d+)\s*\)/g)].map((m) => m[1]);
|
|
120
|
+
// Compare with spec
|
|
121
|
+
const specRoute = specRoutes.find((r) => r.method === method && normalizePath(r.path) === normalizePath(path));
|
|
122
|
+
if (specRoute) {
|
|
123
|
+
for (const status of usedStatuses) {
|
|
124
|
+
if (!specRoute.responses.includes(status)) {
|
|
125
|
+
issues.push({
|
|
126
|
+
file: filepath,
|
|
127
|
+
line: i + 1,
|
|
128
|
+
issue: "Undocumented response status",
|
|
129
|
+
severity: "medium",
|
|
130
|
+
detail: `${method} ${path} returns status ${status} but spec only declares ${specRoute.responses.join(", ")}`,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Response shape mismatch (heuristic: returning fields not in type)
|
|
137
|
+
if (/res\.(?:json|send)\s*\(\s*\{/.test(line)) {
|
|
138
|
+
const block = lines.slice(i, Math.min(i + 10, lines.length)).join("\n");
|
|
139
|
+
// Check for ad-hoc response properties that suggest undocumented fields
|
|
140
|
+
if (/password|secret|token|internal|debug|_\w+:/.test(block)) {
|
|
141
|
+
issues.push({
|
|
142
|
+
file: filepath,
|
|
143
|
+
line: i + 1,
|
|
144
|
+
issue: "Potentially sensitive field in response",
|
|
145
|
+
severity: "high",
|
|
146
|
+
detail: "Response object may include sensitive or internal-only fields not in API contract",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Find spec routes not implemented
|
|
153
|
+
for (const route of specRoutes) {
|
|
154
|
+
const key = `${route.method} ${route.path}`;
|
|
155
|
+
const _normalizedKey = `${route.method} ${normalizePath(route.path)}`;
|
|
156
|
+
let found = false;
|
|
157
|
+
for (const impl of implementedRoutes) {
|
|
158
|
+
if (impl === key || normalizePath(impl.split(" ")[1]) === normalizePath(route.path)) {
|
|
159
|
+
found = true;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (!found) {
|
|
164
|
+
issues.push({
|
|
165
|
+
file: "api-spec",
|
|
166
|
+
line: 0,
|
|
167
|
+
issue: "Spec route not implemented",
|
|
168
|
+
severity: "high",
|
|
169
|
+
detail: `${route.method} ${route.path} declared in OpenAPI spec but no matching handler found in code`,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return issues;
|
|
174
|
+
}
|
|
175
|
+
function normalizePath(path) {
|
|
176
|
+
return path.replace(/\{[^}]+\}/g, ":param").replace(/\/+$/, "");
|
|
177
|
+
}
|
|
178
|
+
// ─── General Contract Checks ────────────────────────────────────────────────
|
|
179
|
+
function analyzeGeneralContracts(files) {
|
|
180
|
+
const issues = [];
|
|
181
|
+
for (const filepath of files) {
|
|
182
|
+
let content;
|
|
183
|
+
try {
|
|
184
|
+
content = readFileSync(filepath, "utf-8");
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (!/\.(?:ts|tsx|js|jsx)$/.test(filepath))
|
|
190
|
+
continue;
|
|
191
|
+
const lines = content.split("\n");
|
|
192
|
+
for (let i = 0; i < lines.length; i++) {
|
|
193
|
+
const line = lines[i];
|
|
194
|
+
// Interface/type declared but response doesn't match
|
|
195
|
+
if (/interface\s+(\w+Response|.*Response)\s*\{/.test(line)) {
|
|
196
|
+
const ifaceName = line.match(/interface\s+(\w+)/)?.[1];
|
|
197
|
+
if (ifaceName && !content.includes(`as ${ifaceName}`) && !content.includes(`: ${ifaceName}`)) {
|
|
198
|
+
issues.push({
|
|
199
|
+
file: filepath,
|
|
200
|
+
line: i + 1,
|
|
201
|
+
issue: "Response type declared but not enforced",
|
|
202
|
+
severity: "medium",
|
|
203
|
+
detail: `\`${ifaceName}\` defined but never used as type constraint — response shape is unverified`,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// API version in URL doesn't match spec version
|
|
208
|
+
if (/['"]\/api\/v(\d+)/.test(line)) {
|
|
209
|
+
const version = line.match(/\/api\/v(\d+)/)?.[1];
|
|
210
|
+
if (version) {
|
|
211
|
+
const otherVersions = content.match(/\/api\/v(\d+)/g);
|
|
212
|
+
if (otherVersions) {
|
|
213
|
+
const versions = new Set(otherVersions.map((v) => v.match(/v(\d+)/)?.[1]));
|
|
214
|
+
if (versions.size > 1) {
|
|
215
|
+
issues.push({
|
|
216
|
+
file: filepath,
|
|
217
|
+
line: i + 1,
|
|
218
|
+
issue: "Mixed API versions in same file",
|
|
219
|
+
severity: "medium",
|
|
220
|
+
detail: `Multiple API versions found (${[...versions].join(", ")}) — may indicate incomplete migration`,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Endpoint returns different shape based on condition
|
|
227
|
+
if (/res\.(?:json|send)\s*\(/.test(line)) {
|
|
228
|
+
const funcBlock = lines.slice(Math.max(0, i - 20), Math.min(i + 5, lines.length)).join("\n");
|
|
229
|
+
const jsonCalls = (funcBlock.match(/res\.(?:json|send)\s*\(/g) || []).length;
|
|
230
|
+
if (jsonCalls > 2) {
|
|
231
|
+
issues.push({
|
|
232
|
+
file: filepath,
|
|
233
|
+
line: i + 1,
|
|
234
|
+
issue: "Multiple response shapes in single handler",
|
|
235
|
+
severity: "low",
|
|
236
|
+
detail: `Handler has ${jsonCalls} different response points — clients may receive inconsistent shapes`,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return issues;
|
|
243
|
+
}
|
|
244
|
+
// ─── CLI ────────────────────────────────────────────────────────────────────
|
|
245
|
+
export function runContractVerify(argv) {
|
|
246
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
247
|
+
console.log(`
|
|
248
|
+
judges contract-verify — Check API implementations match declared contracts
|
|
249
|
+
|
|
250
|
+
Usage:
|
|
251
|
+
judges contract-verify [dir]
|
|
252
|
+
judges contract-verify src/ --format json
|
|
253
|
+
|
|
254
|
+
Options:
|
|
255
|
+
[dir] Directory to scan (default: .)
|
|
256
|
+
--format json JSON output
|
|
257
|
+
--help, -h Show this help
|
|
258
|
+
|
|
259
|
+
Checks: spec routes not implemented, undocumented response statuses, sensitive fields in responses,
|
|
260
|
+
unenforced response types, mixed API versions, inconsistent response shapes.
|
|
261
|
+
`);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const format = argv.find((_a, i) => argv[i - 1] === "--format") || "text";
|
|
265
|
+
const dir = argv.find((a) => !a.startsWith("-") && argv.indexOf(a) > 0) || ".";
|
|
266
|
+
const files = collectFiles(dir);
|
|
267
|
+
// Find OpenAPI specs
|
|
268
|
+
const specFiles = findOpenApiSpecs(dir);
|
|
269
|
+
let specRoutes = [];
|
|
270
|
+
for (const s of specFiles) {
|
|
271
|
+
try {
|
|
272
|
+
const specContent = readFileSync(s, "utf-8");
|
|
273
|
+
specRoutes = specRoutes.concat(extractOpenApiPaths(specContent));
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
/* skip */
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const allIssues = [];
|
|
280
|
+
if (specRoutes.length > 0) {
|
|
281
|
+
allIssues.push(...analyzeContractDrift(files, specRoutes));
|
|
282
|
+
}
|
|
283
|
+
allIssues.push(...analyzeGeneralContracts(files));
|
|
284
|
+
const highCount = allIssues.filter((i) => i.severity === "high").length;
|
|
285
|
+
const medCount = allIssues.filter((i) => i.severity === "medium").length;
|
|
286
|
+
const score = Math.max(0, 100 - highCount * 10 - medCount * 4);
|
|
287
|
+
if (format === "json") {
|
|
288
|
+
console.log(JSON.stringify({
|
|
289
|
+
issues: allIssues,
|
|
290
|
+
score,
|
|
291
|
+
summary: { high: highCount, medium: medCount, total: allIssues.length, specRoutes: specRoutes.length },
|
|
292
|
+
timestamp: new Date().toISOString(),
|
|
293
|
+
}, null, 2));
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
const badge = score >= 80 ? "✅ ALIGNED" : score >= 50 ? "⚠️ DRIFTED" : "❌ MISMATCHED";
|
|
297
|
+
console.log(`\n Contract Alignment: ${badge} (${score}/100)\n ─────────────────────────────`);
|
|
298
|
+
if (specRoutes.length > 0)
|
|
299
|
+
console.log(` OpenAPI spec: ${specRoutes.length} routes found`);
|
|
300
|
+
else
|
|
301
|
+
console.log(" No OpenAPI spec found — running general contract checks only");
|
|
302
|
+
if (allIssues.length === 0) {
|
|
303
|
+
console.log(" No contract issues detected.\n");
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
for (const issue of allIssues.slice(0, 25)) {
|
|
307
|
+
const icon = issue.severity === "high" ? "🔴" : issue.severity === "medium" ? "🟡" : "🔵";
|
|
308
|
+
console.log(` ${icon} ${issue.issue}`);
|
|
309
|
+
console.log(` ${issue.file}:${issue.line}`);
|
|
310
|
+
console.log(` ${issue.detail}`);
|
|
311
|
+
}
|
|
312
|
+
if (allIssues.length > 25)
|
|
313
|
+
console.log(` ... and ${allIssues.length - 25} more`);
|
|
314
|
+
console.log(`\n Total: ${allIssues.length} | High: ${highCount} | Medium: ${medCount} | Score: ${score}/100\n`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
//# sourceMappingURL=contract-verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-verify.js","sourceRoot":"","sources":["../../src/commands/contract-verify.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AACrE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAYrC,+EAA+E;AAE/E,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;AAEjF,SAAS,YAAY,CAAC,GAAW,EAAE,GAAG,GAAG,GAAG;IAC1C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,SAAS,IAAI,CAAC,CAAS;QACrB,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;YAAE,OAAO;QAChC,IAAI,OAAiB,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,CAAC,CAAwB,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;gBAAE,OAAO;YAChC,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,cAAc,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,OAAO;gBAAE,SAAS;YACzF,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACxB,IAAI,CAAC;gBACH,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE;oBAAE,IAAI,CAAC,IAAI,CAAC,CAAC;qBACxC,IAAI,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC;oBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5G,CAAC;YAAC,MAAM,CAAC;gBACP,UAAU;YACZ,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,KAAK,CAAC;AACf,CAAC;AAED,+EAA+E;AAE/E,SAAS,gBAAgB,CAAC,GAAW;IACnC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAG;QACjB,cAAc;QACd,cAAc;QACd,aAAa;QACb,cAAc;QACd,cAAc;QACd,aAAa;QACb,eAAe;QACf,eAAe;KAChB,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,UAAU,CAAC,CAAC,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,2CAA2C;IAC3C,KAAK,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC9B,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACvB,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;gBAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAC7B,IAAI,UAAU,CAAC,CAAC,CAAC;oBAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,mBAAmB,CAAC,WAAmB;IAC9C,MAAM,MAAM,GAA4D,EAAE,CAAC;IAC3E,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAC/B,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACpD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI;gBAAE,SAAS;YAC9D,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAkC,CAAC,EAAE,CAAC;gBAClF,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC/D,MAAM,SAAS,GACb,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,WAAW,IAAI,MAAM;wBAC3D,CAAC,CAAC,MAAM,CAAC,IAAI,CAAE,MAAiD,CAAC,SAAS,CAAC;wBAC3E,CAAC,CAAC,EAAE,CAAC;oBACT,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;gBACjE,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,yBAAyB;IAC3B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,+EAA+E;AAE/E,SAAS,oBAAoB,CAC3B,KAAe,EACf,UAAmE;IAEnE,MAAM,MAAM,GAAoB,EAAE,CAAC;IACnC,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAU,CAAC;IAE5C,kCAAkC;IAClC,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;QAC7B,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,CAAC,iCAAiC,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,SAAS;QAEhE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,yEAAyE,CAAC,CAAC;YACzG,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;gBAC3C,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;gBAC3B,iBAAiB,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;gBAE3C,mCAAmC;gBACnC,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC/E,MAAM,YAAY,GAAG,CAAC,GAAG,YAAY,CAAC,QAAQ,CAAC,6BAA6B,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAEhG,oBAAoB;gBACpB,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC/G,IAAI,SAAS,EAAE,CAAC;oBACd,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;wBAClC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;4BAC1C,MAAM,CAAC,IAAI,CAAC;gCACV,IAAI,EAAE,QAAQ;gCACd,IAAI,EAAE,CAAC,GAAG,CAAC;gCACX,KAAK,EAAE,8BAA8B;gCACrC,QAAQ,EAAE,QAAQ;gCAClB,MAAM,EAAE,GAAG,MAAM,IAAI,IAAI,mBAAmB,MAAM,2BAA2B,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;6BAC9G,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAED,oEAAoE;YACpE,IAAI,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACxE,wEAAwE;gBACxE,IAAI,4CAA4C,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC7D,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,CAAC,GAAG,CAAC;wBACX,KAAK,EAAE,yCAAyC;wBAChD,QAAQ,EAAE,MAAM;wBAChB,MAAM,EAAE,mFAAmF;qBAC5F,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,mCAAmC;IACnC,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC5C,MAAM,cAAc,GAAG,GAAG,KAAK,CAAC,MAAM,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACtE,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,KAAK,MAAM,IAAI,IAAI,iBAAiB,EAAE,CAAC;YACrC,IAAI,IAAI,KAAK,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpF,KAAK,GAAG,IAAI,CAAC;gBACb,MAAM;YACR,CAAC;QACH,CAAC;QACD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,CAAC;gBACP,KAAK,EAAE,4BAA4B;gBACnC,QAAQ,EAAE,MAAM;gBAChB,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,iEAAiE;aACvG,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,+EAA+E;AAE/E,SAAS,uBAAuB,CAAC,KAAe;IAC9C,MAAM,MAAM,GAAoB,EAAE,CAAC;IAEnC,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;QAC7B,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,SAAS;QAErD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAElC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAEtB,qDAAqD;YACrD,IAAI,2CAA2C,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC3D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBACvD,IAAI,SAAS,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,SAAS,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,SAAS,EAAE,CAAC,EAAE,CAAC;oBAC7F,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,CAAC,GAAG,CAAC;wBACX,KAAK,EAAE,yCAAyC;wBAChD,QAAQ,EAAE,QAAQ;wBAClB,MAAM,EAAE,KAAK,SAAS,6EAA6E;qBACpG,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,gDAAgD;YAChD,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBACjD,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;oBACtD,IAAI,aAAa,EAAE,CAAC;wBAClB,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;wBAC3E,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;4BACtB,MAAM,CAAC,IAAI,CAAC;gCACV,IAAI,EAAE,QAAQ;gCACd,IAAI,EAAE,CAAC,GAAG,CAAC;gCACX,KAAK,EAAE,iCAAiC;gCACxC,QAAQ,EAAE,QAAQ;gCAClB,MAAM,EAAE,gCAAgC,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,uCAAuC;6BACxG,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAED,sDAAsD;YACtD,IAAI,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC7F,MAAM,SAAS,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,0BAA0B,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;gBAC7E,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;oBAClB,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,CAAC,GAAG,CAAC;wBACX,KAAK,EAAE,4CAA4C;wBACnD,QAAQ,EAAE,KAAK;wBACf,MAAM,EAAE,eAAe,SAAS,sEAAsE;qBACvG,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,+EAA+E;AAE/E,MAAM,UAAU,iBAAiB,CAAC,IAAc;IAC9C,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;CAcf,CAAC,CAAC;QACC,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,UAAU,CAAC,IAAI,MAAM,CAAC;IAC1F,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC;IAE/E,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAEhC,qBAAqB;IACrB,MAAM,SAAS,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,UAAU,GAA4D,EAAE,CAAC;IAC7E,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YAC7C,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC,CAAC;QACnE,CAAC;QAAC,MAAM,CAAC;YACP,UAAU;QACZ,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAoB,EAAE,CAAC;IACtC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,SAAS,CAAC,IAAI,CAAC,GAAG,oBAAoB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,SAAS,CAAC,IAAI,CAAC,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC,CAAC;IAElD,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IACxE,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;IACzE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,SAAS,GAAG,EAAE,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC;IAE/D,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ;YACE,MAAM,EAAE,SAAS;YACjB,KAAK;YACL,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,MAAM,EAAE;YACtG,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,MAAM,KAAK,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC;QACvF,OAAO,CAAC,GAAG,CAAC,2BAA2B,KAAK,KAAK,KAAK,wCAAwC,CAAC,CAAC;QAChG,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,UAAU,CAAC,MAAM,eAAe,CAAC,CAAC;;YACzF,OAAO,CAAC,GAAG,CAAC,kEAAkE,CAAC,CAAC;QAErF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;YAC1F,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YAC1C,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,SAAS,CAAC,MAAM,GAAG,EAAE;YAAE,OAAO,CAAC,GAAG,CAAC,eAAe,SAAS,CAAC,MAAM,GAAG,EAAE,OAAO,CAAC,CAAC;QAEpF,OAAO,CAAC,GAAG,CAAC,gBAAgB,SAAS,CAAC,MAAM,YAAY,SAAS,cAAc,QAAQ,aAAa,KAAK,QAAQ,CAAC,CAAC;IACrH,CAAC;AACH,CAAC"}
|