truecourse 0.6.6 → 0.6.7-next.1
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/cli.mjs +1324 -288
- package/package.json +1 -1
- package/server.mjs +1343 -315
package/cli.mjs
CHANGED
|
@@ -4882,7 +4882,7 @@ function configureLogger(config2) {
|
|
|
4882
4882
|
async function closeLogger() {
|
|
4883
4883
|
while (stack.length > 0) {
|
|
4884
4884
|
const sink = stack.pop();
|
|
4885
|
-
await new Promise((
|
|
4885
|
+
await new Promise((resolve10) => sink.stream.end(resolve10));
|
|
4886
4886
|
}
|
|
4887
4887
|
}
|
|
4888
4888
|
function currentSink() {
|
|
@@ -11147,6 +11147,14 @@ function parseFile(filePath, code, language) {
|
|
|
11147
11147
|
throw new Error(`Failed to parse file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
11148
11148
|
}
|
|
11149
11149
|
}
|
|
11150
|
+
function withParsedTree(filePath, code, language, use) {
|
|
11151
|
+
const tree = parseFile(filePath, code, language);
|
|
11152
|
+
try {
|
|
11153
|
+
return use(tree);
|
|
11154
|
+
} finally {
|
|
11155
|
+
tree.delete();
|
|
11156
|
+
}
|
|
11157
|
+
}
|
|
11150
11158
|
var _require, BUNDLED_WASM_DIR, GRAMMAR_WASM, languageCache, parserCache, initPromise, initialized;
|
|
11151
11159
|
var init_parser = __esm({
|
|
11152
11160
|
"packages/analyzer/dist/parser.js"() {
|
|
@@ -14006,53 +14014,15 @@ async function analyzeFile(filePath) {
|
|
|
14006
14014
|
}
|
|
14007
14015
|
try {
|
|
14008
14016
|
const content = await readFile(filePath, "utf-8");
|
|
14009
|
-
|
|
14010
|
-
let functions, classes, imports, exports;
|
|
14011
|
-
switch (language) {
|
|
14012
|
-
case "typescript":
|
|
14013
|
-
case "tsx":
|
|
14014
|
-
functions = extractTypeScriptFunctions(tree, filePath, language);
|
|
14015
|
-
classes = extractTypeScriptClasses(tree, filePath, language);
|
|
14016
|
-
imports = extractTypeScriptImports(tree, filePath, language);
|
|
14017
|
-
exports = extractTypeScriptExports(tree, filePath, language);
|
|
14018
|
-
break;
|
|
14019
|
-
case "javascript":
|
|
14020
|
-
functions = extractJavaScriptFunctions(tree, filePath);
|
|
14021
|
-
classes = extractJavaScriptClasses(tree, filePath);
|
|
14022
|
-
imports = extractJavaScriptImports(tree, filePath);
|
|
14023
|
-
exports = extractJavaScriptExports(tree, filePath);
|
|
14024
|
-
break;
|
|
14025
|
-
case "python":
|
|
14026
|
-
functions = extractPythonFunctions(tree, filePath);
|
|
14027
|
-
classes = extractPythonClasses(tree, filePath);
|
|
14028
|
-
imports = extractPythonImports(tree, filePath);
|
|
14029
|
-
exports = extractPythonExports(tree, filePath);
|
|
14030
|
-
break;
|
|
14031
|
-
default:
|
|
14032
|
-
throw new Error(`Unsupported language: ${language}`);
|
|
14033
|
-
}
|
|
14034
|
-
const functionContext = buildFunctionContext(functions, classes);
|
|
14035
|
-
const calls = extractCalls(tree, filePath, language, functionContext);
|
|
14036
|
-
const httpCalls = extractHttpCalls(tree, filePath, language, functions, classes);
|
|
14037
|
-
const { routes: routeRegistrations, mounts: routerMounts } = extractRouteRegistrations(tree, filePath, language);
|
|
14038
|
-
return {
|
|
14039
|
-
filePath,
|
|
14040
|
-
language,
|
|
14041
|
-
functions,
|
|
14042
|
-
classes,
|
|
14043
|
-
imports,
|
|
14044
|
-
exports,
|
|
14045
|
-
calls,
|
|
14046
|
-
httpCalls,
|
|
14047
|
-
...routeRegistrations.length > 0 ? { routeRegistrations } : {},
|
|
14048
|
-
...routerMounts.length > 0 ? { routerMounts } : {}
|
|
14049
|
-
};
|
|
14017
|
+
return withParsedTree(filePath, content, language, (tree) => buildFileAnalysis(tree, filePath, language));
|
|
14050
14018
|
} catch (error) {
|
|
14051
14019
|
throw new Error(`Failed to analyze file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
14052
14020
|
}
|
|
14053
14021
|
}
|
|
14054
14022
|
function analyzeFileContent(filePath, content, language) {
|
|
14055
|
-
|
|
14023
|
+
return withParsedTree(filePath, content, language, (tree) => buildFileAnalysis(tree, filePath, language));
|
|
14024
|
+
}
|
|
14025
|
+
function buildFileAnalysis(tree, filePath, language) {
|
|
14056
14026
|
let functions, classes, imports, exports;
|
|
14057
14027
|
switch (language) {
|
|
14058
14028
|
case "typescript":
|
|
@@ -14068,6 +14038,12 @@ function analyzeFileContent(filePath, content, language) {
|
|
|
14068
14038
|
imports = extractJavaScriptImports(tree, filePath);
|
|
14069
14039
|
exports = extractJavaScriptExports(tree, filePath);
|
|
14070
14040
|
break;
|
|
14041
|
+
case "python":
|
|
14042
|
+
functions = extractPythonFunctions(tree, filePath);
|
|
14043
|
+
classes = extractPythonClasses(tree, filePath);
|
|
14044
|
+
imports = extractPythonImports(tree, filePath);
|
|
14045
|
+
exports = extractPythonExports(tree, filePath);
|
|
14046
|
+
break;
|
|
14071
14047
|
default:
|
|
14072
14048
|
throw new Error(`Unsupported language: ${language}`);
|
|
14073
14049
|
}
|
|
@@ -18783,11 +18759,6 @@ var init_entities = __esm({
|
|
|
18783
18759
|
init_patterns();
|
|
18784
18760
|
init_parser();
|
|
18785
18761
|
TypeScriptEntityDetector = class {
|
|
18786
|
-
// Lazy: do not call getParser() at construction time — callers may
|
|
18787
|
-
// instantiate this before initParsers() has completed.
|
|
18788
|
-
get parser() {
|
|
18789
|
-
return getParser("typescript");
|
|
18790
|
-
}
|
|
18791
18762
|
shouldScanFile(filePath) {
|
|
18792
18763
|
if (!/\.(ts|tsx|js|jsx)$/.test(filePath)) {
|
|
18793
18764
|
return false;
|
|
@@ -18802,25 +18773,24 @@ var init_entities = __esm({
|
|
|
18802
18773
|
return hasEntityDir || hasEntityPattern;
|
|
18803
18774
|
}
|
|
18804
18775
|
detectEntities(sourceCode, filePath, service) {
|
|
18805
|
-
|
|
18806
|
-
|
|
18807
|
-
|
|
18808
|
-
|
|
18809
|
-
|
|
18810
|
-
|
|
18811
|
-
|
|
18812
|
-
|
|
18813
|
-
entities.push(entity);
|
|
18776
|
+
return withParsedTree(filePath, sourceCode, "typescript", (tree) => {
|
|
18777
|
+
const entities = [];
|
|
18778
|
+
const classNodes = this.findClassDeclarations(tree.rootNode);
|
|
18779
|
+
for (const classNode of classNodes) {
|
|
18780
|
+
const entity = this.detectEntityFromClass(classNode, sourceCode, filePath, service);
|
|
18781
|
+
if (entity) {
|
|
18782
|
+
entities.push(entity);
|
|
18783
|
+
}
|
|
18814
18784
|
}
|
|
18815
|
-
|
|
18816
|
-
|
|
18817
|
-
|
|
18818
|
-
|
|
18819
|
-
|
|
18820
|
-
|
|
18785
|
+
const interfaceNodes = this.findInterfaceDeclarations(tree.rootNode);
|
|
18786
|
+
for (const interfaceNode of interfaceNodes) {
|
|
18787
|
+
const entity = this.detectEntityFromInterface(interfaceNode, sourceCode, filePath, service);
|
|
18788
|
+
if (entity) {
|
|
18789
|
+
entities.push(entity);
|
|
18790
|
+
}
|
|
18821
18791
|
}
|
|
18822
|
-
|
|
18823
|
-
|
|
18792
|
+
return entities;
|
|
18793
|
+
});
|
|
18824
18794
|
}
|
|
18825
18795
|
findClassDeclarations(node2) {
|
|
18826
18796
|
const classes = [];
|
|
@@ -19188,14 +19158,14 @@ var init_lsp_client = __esm({
|
|
|
19188
19158
|
this.sendNotification("exit", null);
|
|
19189
19159
|
} catch {
|
|
19190
19160
|
}
|
|
19191
|
-
await new Promise((
|
|
19161
|
+
await new Promise((resolve10) => {
|
|
19192
19162
|
const timeout = setTimeout(() => {
|
|
19193
19163
|
this.process?.kill("SIGKILL");
|
|
19194
|
-
|
|
19164
|
+
resolve10();
|
|
19195
19165
|
}, 2e3);
|
|
19196
19166
|
this.process.on("exit", () => {
|
|
19197
19167
|
clearTimeout(timeout);
|
|
19198
|
-
|
|
19168
|
+
resolve10();
|
|
19199
19169
|
});
|
|
19200
19170
|
});
|
|
19201
19171
|
this.process = null;
|
|
@@ -19338,9 +19308,9 @@ var init_lsp_client = __esm({
|
|
|
19338
19308
|
// Internal: JSON-RPC transport
|
|
19339
19309
|
// -------------------------------------------------------------------------
|
|
19340
19310
|
sendRequest(method, params) {
|
|
19341
|
-
return new Promise((
|
|
19311
|
+
return new Promise((resolve10, reject) => {
|
|
19342
19312
|
const id = ++this.requestId;
|
|
19343
|
-
this.pendingRequests.set(id, { resolve:
|
|
19313
|
+
this.pendingRequests.set(id, { resolve: resolve10, reject });
|
|
19344
19314
|
const msg = { jsonrpc: "2.0", id, method, params };
|
|
19345
19315
|
this.process.stdin.write(encodeMessage(msg));
|
|
19346
19316
|
});
|
|
@@ -19397,7 +19367,7 @@ var init_lsp_client = __esm({
|
|
|
19397
19367
|
*/
|
|
19398
19368
|
waitForDiagnostics(fileCount) {
|
|
19399
19369
|
const waitMs = Math.min(Math.max(fileCount * 100, 1e3), 1e4);
|
|
19400
|
-
return new Promise((
|
|
19370
|
+
return new Promise((resolve10) => setTimeout(resolve10, waitMs));
|
|
19401
19371
|
}
|
|
19402
19372
|
};
|
|
19403
19373
|
}
|
|
@@ -47403,6 +47373,9 @@ var init_no_self_compare = __esm({
|
|
|
47403
47373
|
});
|
|
47404
47374
|
|
|
47405
47375
|
// packages/analyzer/dist/rules/bugs/visitors/javascript/duplicate-class-members.js
|
|
47376
|
+
function reportDuplicate(ruleKey, child, filePath, sourceCode, name) {
|
|
47377
|
+
return makeViolation(ruleKey, child, filePath, "high", "Duplicate class member", `Member \`${name}\` is defined more than once \u2014 the later definition silently overwrites the earlier one.`, sourceCode, "Remove the duplicate member or rename one of them.");
|
|
47378
|
+
}
|
|
47406
47379
|
var duplicateClassMembersVisitor;
|
|
47407
47380
|
var init_duplicate_class_members = __esm({
|
|
47408
47381
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/duplicate-class-members.js"() {
|
|
@@ -47416,18 +47389,37 @@ var init_duplicate_class_members = __esm({
|
|
|
47416
47389
|
visit(node2, filePath, sourceCode) {
|
|
47417
47390
|
const seen = /* @__PURE__ */ new Map();
|
|
47418
47391
|
for (const child of node2.namedChildren) {
|
|
47419
|
-
|
|
47420
|
-
|
|
47421
|
-
|
|
47422
|
-
|
|
47423
|
-
|
|
47392
|
+
if (child.type !== "method_definition" && child.type !== "public_field_definition" && child.type !== "field_definition")
|
|
47393
|
+
continue;
|
|
47394
|
+
const nameNode = child.childForFieldName("name");
|
|
47395
|
+
if (!nameNode)
|
|
47396
|
+
continue;
|
|
47397
|
+
const name = nameNode.text;
|
|
47398
|
+
let isStatic = false;
|
|
47399
|
+
let accessor = null;
|
|
47400
|
+
for (let i = 0; i < child.childCount; i++) {
|
|
47401
|
+
const c2 = child.child(i);
|
|
47402
|
+
if (!c2)
|
|
47403
|
+
continue;
|
|
47404
|
+
if (c2.type === "static")
|
|
47405
|
+
isStatic = true;
|
|
47406
|
+
else if (c2.type === "get")
|
|
47407
|
+
accessor = "get";
|
|
47408
|
+
else if (c2.type === "set")
|
|
47409
|
+
accessor = "set";
|
|
47410
|
+
}
|
|
47411
|
+
const kind = accessor ?? (child.type === "method_definition" ? "method" : "field");
|
|
47412
|
+
const key2 = `${isStatic ? "static" : "instance"}:${name}`;
|
|
47413
|
+
const existing = seen.get(key2);
|
|
47414
|
+
if (!existing) {
|
|
47415
|
+
seen.set(key2, { kinds: /* @__PURE__ */ new Set([kind]) });
|
|
47416
|
+
continue;
|
|
47424
47417
|
}
|
|
47425
|
-
|
|
47426
|
-
|
|
47427
|
-
|
|
47428
|
-
}
|
|
47429
|
-
seen.set(name, child);
|
|
47418
|
+
const isGetSetPair = existing.kinds.size === 1 && existing.kinds.has("get") && kind === "set" || existing.kinds.size === 1 && existing.kinds.has("set") && kind === "get";
|
|
47419
|
+
if (!isGetSetPair) {
|
|
47420
|
+
return reportDuplicate(this.ruleKey, child, filePath, sourceCode, name);
|
|
47430
47421
|
}
|
|
47422
|
+
existing.kinds.add(kind);
|
|
47431
47423
|
}
|
|
47432
47424
|
return null;
|
|
47433
47425
|
}
|
|
@@ -49774,7 +49766,9 @@ var init_element_overwrite = __esm({
|
|
|
49774
49766
|
hasAwait2 = true;
|
|
49775
49767
|
}
|
|
49776
49768
|
const isRefCurrent = left.type === "member_expression" && indexOrProp.text === "current";
|
|
49777
|
-
|
|
49769
|
+
const right = expr.childForFieldName("right");
|
|
49770
|
+
const readsSelfInRhs = right ? right.text.includes(left.text) : false;
|
|
49771
|
+
if (!wasRead && !readsSelfInRhs && !(isRefCurrent && hasAwait2)) {
|
|
49778
49772
|
return makeViolation(this.ruleKey, expr, filePath, "high", "Element overwritten before read", `\`${key2}\` is assigned again before being read \u2014 the first assignment has no effect.`, sourceCode, "Remove the first assignment or use the value before overwriting it.");
|
|
49779
49773
|
}
|
|
49780
49774
|
}
|
|
@@ -51138,6 +51132,17 @@ var init_promise_executor_return = __esm({
|
|
|
51138
51132
|
});
|
|
51139
51133
|
|
|
51140
51134
|
// packages/analyzer/dist/rules/bugs/visitors/javascript/empty-pattern.js
|
|
51135
|
+
function isFunctionParameter(node2) {
|
|
51136
|
+
const parent = node2.parent;
|
|
51137
|
+
if (!parent)
|
|
51138
|
+
return false;
|
|
51139
|
+
if (parent.type === "formal_parameters")
|
|
51140
|
+
return true;
|
|
51141
|
+
if (parent.type === "required_parameter" || parent.type === "optional_parameter") {
|
|
51142
|
+
return parent.parent?.type === "formal_parameters";
|
|
51143
|
+
}
|
|
51144
|
+
return false;
|
|
51145
|
+
}
|
|
51141
51146
|
var emptyPatternVisitor;
|
|
51142
51147
|
var init_empty_pattern = __esm({
|
|
51143
51148
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/empty-pattern.js"() {
|
|
@@ -51151,6 +51156,8 @@ var init_empty_pattern = __esm({
|
|
|
51151
51156
|
visit(node2, filePath, sourceCode) {
|
|
51152
51157
|
const bindings = node2.namedChildren.filter((c2) => c2.type !== "comment");
|
|
51153
51158
|
if (bindings.length === 0) {
|
|
51159
|
+
if (node2.type === "object_pattern" && isFunctionParameter(node2))
|
|
51160
|
+
return null;
|
|
51154
51161
|
const kind = node2.type === "object_pattern" ? "{}" : "[]";
|
|
51155
51162
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Empty destructuring pattern", `Empty destructuring pattern \`${kind}\` does not bind any variables.`, sourceCode, "Add variable bindings to the destructuring pattern or remove it entirely.");
|
|
51156
51163
|
}
|
|
@@ -52702,6 +52709,15 @@ var init_contradictory_non_null_coalescing = __esm({
|
|
|
52702
52709
|
});
|
|
52703
52710
|
|
|
52704
52711
|
// packages/analyzer/dist/rules/bugs/visitors/javascript/empty-object-type.js
|
|
52712
|
+
function isReactPropsParameter(typeAnnotation, filePath) {
|
|
52713
|
+
if (!/\.(tsx|jsx)$/i.test(filePath))
|
|
52714
|
+
return false;
|
|
52715
|
+
const param = typeAnnotation.parent;
|
|
52716
|
+
if (param?.type !== "required_parameter" && param?.type !== "optional_parameter")
|
|
52717
|
+
return false;
|
|
52718
|
+
const pattern = param.childForFieldName("pattern");
|
|
52719
|
+
return pattern?.text === "props";
|
|
52720
|
+
}
|
|
52705
52721
|
var emptyObjectTypeVisitor;
|
|
52706
52722
|
var init_empty_object_type = __esm({
|
|
52707
52723
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/empty-object-type.js"() {
|
|
@@ -52726,7 +52742,7 @@ var init_empty_object_type = __esm({
|
|
|
52726
52742
|
};
|
|
52727
52743
|
if (node2.type === "type_annotation") {
|
|
52728
52744
|
const emptyObj = checkForEmptyObject(node2);
|
|
52729
|
-
if (emptyObj) {
|
|
52745
|
+
if (emptyObj && !isReactPropsParameter(node2, filePath)) {
|
|
52730
52746
|
return makeViolation(this.ruleKey, emptyObj, filePath, "high", "Empty object type {}", "`{}` type matches everything except `null` and `undefined` \u2014 this is rarely intentional. Use `object` for non-primitive objects or `Record<string, unknown>` for generic objects.", sourceCode, "Replace `{}` with `object`, `Record<string, unknown>`, or a specific type.");
|
|
52731
52747
|
}
|
|
52732
52748
|
}
|
|
@@ -69319,6 +69335,12 @@ var init_prefer_immediate_return = __esm({
|
|
|
69319
69335
|
const nameNode = decl.childForFieldName("name");
|
|
69320
69336
|
if (nameNode?.text !== retName)
|
|
69321
69337
|
return null;
|
|
69338
|
+
const valueNode = decl.childForFieldName("value");
|
|
69339
|
+
if (valueNode) {
|
|
69340
|
+
const lineSpan = valueNode.endPosition.row - valueNode.startPosition.row + 1;
|
|
69341
|
+
if (lineSpan > 10)
|
|
69342
|
+
return null;
|
|
69343
|
+
}
|
|
69322
69344
|
let usageCount = 0;
|
|
69323
69345
|
function countUsages2(n) {
|
|
69324
69346
|
if (n.type === "identifier" && n.text === retName) {
|
|
@@ -71464,6 +71486,14 @@ function isDirectCallbackArg(fn) {
|
|
|
71464
71486
|
p2 = p2.parent;
|
|
71465
71487
|
return p2?.type === "arguments" ? p2 : null;
|
|
71466
71488
|
}
|
|
71489
|
+
function isTrivialCallback(fn) {
|
|
71490
|
+
const body = fn.childForFieldName("body");
|
|
71491
|
+
if (!body)
|
|
71492
|
+
return false;
|
|
71493
|
+
if (body.type !== "statement_block")
|
|
71494
|
+
return true;
|
|
71495
|
+
return body.namedChildren.length === 0;
|
|
71496
|
+
}
|
|
71467
71497
|
function findEnclosingFn(start) {
|
|
71468
71498
|
let n = start;
|
|
71469
71499
|
while (n) {
|
|
@@ -71485,6 +71515,8 @@ var init_deep_callback_nesting = __esm({
|
|
|
71485
71515
|
languages: ["typescript", "tsx", "javascript"],
|
|
71486
71516
|
nodeTypes: ["function_expression", "arrow_function"],
|
|
71487
71517
|
visit(node2, filePath, sourceCode) {
|
|
71518
|
+
if (isTrivialCallback(node2))
|
|
71519
|
+
return null;
|
|
71488
71520
|
let depth = 0;
|
|
71489
71521
|
let current = node2;
|
|
71490
71522
|
while (true) {
|
|
@@ -71603,6 +71635,10 @@ var init_default_parameter_position = __esm({
|
|
|
71603
71635
|
const isOptional = param.type === "optional_parameter" || param.text.includes("?");
|
|
71604
71636
|
if (isOptional)
|
|
71605
71637
|
continue;
|
|
71638
|
+
const patternNode = param.childForFieldName("pattern") ?? param.namedChildren[0];
|
|
71639
|
+
const isDestructured = param.type === "object_pattern" || param.type === "array_pattern" || patternNode?.type === "object_pattern" || patternNode?.type === "array_pattern";
|
|
71640
|
+
if (isDestructured)
|
|
71641
|
+
continue;
|
|
71606
71642
|
const nameNode = param.childForFieldName("pattern") ?? param.childForFieldName("name") ?? param.namedChildren[0];
|
|
71607
71643
|
const name = nameNode?.text ?? "parameter";
|
|
71608
71644
|
return makeViolation(this.ruleKey, param, filePath, "low", "Default parameter not last", `Required parameter \`${name}\` appears after a default parameter. Default parameters should come last.`, sourceCode, "Move default parameters to the end of the parameter list.");
|
|
@@ -72348,7 +72384,8 @@ var init_public_static_readonly = __esm({
|
|
|
72348
72384
|
const isStatic = member.children.some((c2) => c2.type === "static");
|
|
72349
72385
|
const isPublic = !member.children.some((c2) => c2.type === "accessibility_modifier" && (c2.text === "private" || c2.text === "protected"));
|
|
72350
72386
|
const isReadonly = member.children.some((c2) => c2.type === "readonly");
|
|
72351
|
-
|
|
72387
|
+
const hasInitializer = member.childForFieldName("value") != null;
|
|
72388
|
+
if (isStatic && isPublic && !isReadonly && hasInitializer) {
|
|
72352
72389
|
const nameNode = member.childForFieldName("name");
|
|
72353
72390
|
const name = nameNode?.text ?? "field";
|
|
72354
72391
|
return makeViolation(this.ruleKey, member, filePath, "medium", "Mutable public static field", `Public static field \`${name}\` is not \`readonly\`. Static public fields that are constants should be readonly.`, sourceCode, `Add the \`readonly\` modifier: \`public static readonly ${name}\`.`);
|
|
@@ -77799,11 +77836,18 @@ var init_confusing_void_expression = __esm({
|
|
|
77799
77836
|
}
|
|
77800
77837
|
parent = parent.parent;
|
|
77801
77838
|
}
|
|
77802
|
-
|
|
77839
|
+
const isContainingFnVoid = () => {
|
|
77840
|
+
if (!containingFn)
|
|
77841
|
+
return false;
|
|
77803
77842
|
const fnReturnType = typeQuery.getReturnType(filePath, containingFn.startPosition.row, containingFn.startPosition.column, containingFn.endPosition.row, containingFn.endPosition.column);
|
|
77804
|
-
|
|
77843
|
+
return !!fnReturnType && /^(void|undefined|Promise<\s*(void|undefined)\s*>)$/.test(fnReturnType);
|
|
77844
|
+
};
|
|
77845
|
+
if (containingFn?.type === "arrow_function") {
|
|
77846
|
+
if (isContainingFnVoid())
|
|
77847
|
+
return null;
|
|
77848
|
+
} else if (value.type === "await_expression" && (containingFn?.type === "function_declaration" || containingFn?.type === "function_expression" || containingFn?.type === "method_definition" || containingFn?.type === "function")) {
|
|
77849
|
+
if (isContainingFnVoid())
|
|
77805
77850
|
return null;
|
|
77806
|
-
}
|
|
77807
77851
|
}
|
|
77808
77852
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Returning void expression", `Returning a void expression \u2014 \`${value.text.slice(0, 40)}\` returns \`undefined\`. This is confusing because it looks like the return value matters.`, sourceCode, "Put the expression on its own line and use a bare `return` statement.");
|
|
77809
77853
|
}
|
|
@@ -77989,6 +78033,32 @@ var init_unnecessary_type_parameter = __esm({
|
|
|
77989
78033
|
});
|
|
77990
78034
|
|
|
77991
78035
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/prefer-this-return-type.js
|
|
78036
|
+
function collectMethodReturns(body) {
|
|
78037
|
+
const returns = [];
|
|
78038
|
+
const NESTED_SCOPES = /* @__PURE__ */ new Set([
|
|
78039
|
+
"function_declaration",
|
|
78040
|
+
"function_expression",
|
|
78041
|
+
"function",
|
|
78042
|
+
"arrow_function",
|
|
78043
|
+
"generator_function",
|
|
78044
|
+
"generator_function_declaration",
|
|
78045
|
+
"method_definition",
|
|
78046
|
+
"class_declaration",
|
|
78047
|
+
"class"
|
|
78048
|
+
]);
|
|
78049
|
+
const walk11 = (n) => {
|
|
78050
|
+
for (const child of n.namedChildren) {
|
|
78051
|
+
if (child.type === "return_statement") {
|
|
78052
|
+
returns.push(child);
|
|
78053
|
+
}
|
|
78054
|
+
if (!NESTED_SCOPES.has(child.type)) {
|
|
78055
|
+
walk11(child);
|
|
78056
|
+
}
|
|
78057
|
+
}
|
|
78058
|
+
};
|
|
78059
|
+
walk11(body);
|
|
78060
|
+
return returns;
|
|
78061
|
+
}
|
|
77992
78062
|
function findEnclosingClass(node2) {
|
|
77993
78063
|
let current = node2.parent;
|
|
77994
78064
|
while (current) {
|
|
@@ -78041,6 +78111,23 @@ var init_prefer_this_return_type = __esm({
|
|
|
78041
78111
|
if (!typeText)
|
|
78042
78112
|
return null;
|
|
78043
78113
|
if (typeText === className) {
|
|
78114
|
+
const body = node2.childForFieldName("body");
|
|
78115
|
+
if (!body)
|
|
78116
|
+
return null;
|
|
78117
|
+
const returns = collectMethodReturns(body);
|
|
78118
|
+
if (returns.length === 0)
|
|
78119
|
+
return null;
|
|
78120
|
+
let returnsThis = false;
|
|
78121
|
+
for (const ret of returns) {
|
|
78122
|
+
const arg = ret.namedChildren[0];
|
|
78123
|
+
if (arg && arg.type === "this") {
|
|
78124
|
+
returnsThis = true;
|
|
78125
|
+
} else {
|
|
78126
|
+
return null;
|
|
78127
|
+
}
|
|
78128
|
+
}
|
|
78129
|
+
if (!returnsThis)
|
|
78130
|
+
return null;
|
|
78044
78131
|
return makeViolation(this.ruleKey, returnTypeNode, filePath, "low", "Method should return `this` instead of class name", `Method \`${methodName}\` returns \`${className}\` but should return \`this\` to support subclass chaining.`, sourceCode, `Change the return type from \`${className}\` to \`this\`.`);
|
|
78045
78132
|
}
|
|
78046
78133
|
return null;
|
|
@@ -91788,12 +91875,32 @@ var init_regex_in_loop = __esm({
|
|
|
91788
91875
|
});
|
|
91789
91876
|
|
|
91790
91877
|
// packages/analyzer/dist/rules/performance/visitors/javascript/spread-in-reduce.js
|
|
91791
|
-
function
|
|
91792
|
-
if (
|
|
91793
|
-
return
|
|
91878
|
+
function getAccumulatorName(callback) {
|
|
91879
|
+
if (callback.type !== "arrow_function" && callback.type !== "function_expression" && callback.type !== "function") {
|
|
91880
|
+
return null;
|
|
91881
|
+
}
|
|
91882
|
+
const params = callback.childForFieldName("parameters");
|
|
91883
|
+
if (!params)
|
|
91884
|
+
return null;
|
|
91885
|
+
const first2 = params.namedChildren.find((c2) => c2.type !== "comment");
|
|
91886
|
+
if (!first2)
|
|
91887
|
+
return null;
|
|
91888
|
+
if (first2.type === "identifier")
|
|
91889
|
+
return first2.text;
|
|
91890
|
+
const pattern = first2.childForFieldName("pattern");
|
|
91891
|
+
if (pattern && pattern.type === "identifier")
|
|
91892
|
+
return pattern.text;
|
|
91893
|
+
return null;
|
|
91894
|
+
}
|
|
91895
|
+
function spreadsIdentifier(node2, name) {
|
|
91896
|
+
if (node2.type === "spread_element") {
|
|
91897
|
+
const arg = node2.namedChildren[0];
|
|
91898
|
+
if (arg && arg.type === "identifier" && arg.text === name)
|
|
91899
|
+
return true;
|
|
91900
|
+
}
|
|
91794
91901
|
for (let i = 0; i < node2.childCount; i++) {
|
|
91795
91902
|
const child = node2.child(i);
|
|
91796
|
-
if (child &&
|
|
91903
|
+
if (child && spreadsIdentifier(child, name))
|
|
91797
91904
|
return true;
|
|
91798
91905
|
}
|
|
91799
91906
|
return false;
|
|
@@ -91820,7 +91927,10 @@ var init_spread_in_reduce = __esm({
|
|
|
91820
91927
|
const callback = args.namedChildren[0];
|
|
91821
91928
|
if (!callback)
|
|
91822
91929
|
return null;
|
|
91823
|
-
|
|
91930
|
+
const accName = getAccumulatorName(callback);
|
|
91931
|
+
if (!accName)
|
|
91932
|
+
return null;
|
|
91933
|
+
if (spreadsIdentifier(callback, accName)) {
|
|
91824
91934
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Spread operator in reduce callback", "Using spread in a reduce callback creates a new copy on every iteration, resulting in O(n^2) time complexity.", sourceCode, "Use Object.assign() or direct mutation of the accumulator instead of spread.");
|
|
91825
91935
|
}
|
|
91826
91936
|
return null;
|
|
@@ -91890,6 +92000,8 @@ var init_sync_fs_in_request_handler = __esm({
|
|
|
91890
92000
|
return null;
|
|
91891
92001
|
if (SEED_FILE_PATH_PATTERN2.test(filePath))
|
|
91892
92002
|
return null;
|
|
92003
|
+
if (sourceCode.startsWith("#!"))
|
|
92004
|
+
return null;
|
|
91893
92005
|
if (!isInsideAsyncFunctionOrHandler(node2))
|
|
91894
92006
|
return null;
|
|
91895
92007
|
const enclosingName = findEnclosingFunctionName(node2);
|
|
@@ -92070,6 +92182,100 @@ var init_large_bundle_import = __esm({
|
|
|
92070
92182
|
});
|
|
92071
92183
|
|
|
92072
92184
|
// packages/analyzer/dist/rules/performance/visitors/javascript/json-parse-in-loop.js
|
|
92185
|
+
function isDynamicPerIteration(arg, callNode) {
|
|
92186
|
+
switch (arg.type) {
|
|
92187
|
+
case "parenthesized_expression": {
|
|
92188
|
+
const inner = arg.namedChildren[0];
|
|
92189
|
+
return inner ? isDynamicPerIteration(inner, callNode) : false;
|
|
92190
|
+
}
|
|
92191
|
+
case "call_expression":
|
|
92192
|
+
case "subscript_expression":
|
|
92193
|
+
case "template_string":
|
|
92194
|
+
return true;
|
|
92195
|
+
case "ternary_expression": {
|
|
92196
|
+
const cons = arg.childForFieldName("consequence");
|
|
92197
|
+
const alt = arg.childForFieldName("alternative");
|
|
92198
|
+
return !!cons && isDynamicPerIteration(cons, callNode) || !!alt && isDynamicPerIteration(alt, callNode);
|
|
92199
|
+
}
|
|
92200
|
+
case "binary_expression": {
|
|
92201
|
+
const left = arg.childForFieldName("left");
|
|
92202
|
+
const right = arg.childForFieldName("right");
|
|
92203
|
+
return !!left && isDynamicPerIteration(left, callNode) || !!right && isDynamicPerIteration(right, callNode);
|
|
92204
|
+
}
|
|
92205
|
+
case "identifier":
|
|
92206
|
+
return isLoopBoundIdentifier(arg.text, callNode);
|
|
92207
|
+
case "member_expression": {
|
|
92208
|
+
const inner = arg.childForFieldName("object");
|
|
92209
|
+
if (!inner)
|
|
92210
|
+
return false;
|
|
92211
|
+
if (inner.type === "identifier")
|
|
92212
|
+
return isLoopBoundIdentifier(inner.text, callNode);
|
|
92213
|
+
return isDynamicPerIteration(inner, callNode);
|
|
92214
|
+
}
|
|
92215
|
+
default:
|
|
92216
|
+
return false;
|
|
92217
|
+
}
|
|
92218
|
+
}
|
|
92219
|
+
function isLoopBoundIdentifier(varName, callNode) {
|
|
92220
|
+
let current = callNode.parent;
|
|
92221
|
+
let innermostLoop = null;
|
|
92222
|
+
while (current) {
|
|
92223
|
+
if (current.type === "for_in_statement" || current.type === "for_of_statement") {
|
|
92224
|
+
const left = current.childForFieldName("left");
|
|
92225
|
+
if (left && containsIdentifierExact(left, varName))
|
|
92226
|
+
return true;
|
|
92227
|
+
if (!innermostLoop)
|
|
92228
|
+
innermostLoop = current;
|
|
92229
|
+
}
|
|
92230
|
+
if (current.type === "for_statement") {
|
|
92231
|
+
const init = current.childForFieldName("initializer");
|
|
92232
|
+
if (init && containsIdentifierExact(init, varName))
|
|
92233
|
+
return true;
|
|
92234
|
+
if (!innermostLoop)
|
|
92235
|
+
innermostLoop = current;
|
|
92236
|
+
}
|
|
92237
|
+
if (current.type === "while_statement" || current.type === "do_statement") {
|
|
92238
|
+
if (!innermostLoop)
|
|
92239
|
+
innermostLoop = current;
|
|
92240
|
+
}
|
|
92241
|
+
if (current.type === "arrow_function" || current.type === "function_expression" || current.type === "function") {
|
|
92242
|
+
const params = current.childForFieldName("parameters");
|
|
92243
|
+
if (params && containsIdentifierExact(params, varName)) {
|
|
92244
|
+
const callParent = current.parent?.parent;
|
|
92245
|
+
if (callParent?.type === "call_expression")
|
|
92246
|
+
return true;
|
|
92247
|
+
}
|
|
92248
|
+
}
|
|
92249
|
+
current = current.parent;
|
|
92250
|
+
}
|
|
92251
|
+
if (innermostLoop) {
|
|
92252
|
+
const body = innermostLoop.childForFieldName("body");
|
|
92253
|
+
if (body && containsBindingInBlock(body, varName))
|
|
92254
|
+
return true;
|
|
92255
|
+
}
|
|
92256
|
+
return false;
|
|
92257
|
+
}
|
|
92258
|
+
function containsBindingInBlock(node2, varName) {
|
|
92259
|
+
if (node2.type === "lexical_declaration" || node2.type === "variable_declaration") {
|
|
92260
|
+
for (const decl of node2.namedChildren) {
|
|
92261
|
+
if (decl.type === "variable_declarator") {
|
|
92262
|
+
const name = decl.childForFieldName("name");
|
|
92263
|
+
if (name && containsIdentifierExact(name, varName))
|
|
92264
|
+
return true;
|
|
92265
|
+
}
|
|
92266
|
+
}
|
|
92267
|
+
return false;
|
|
92268
|
+
}
|
|
92269
|
+
if (node2.type === "function_declaration" || node2.type === "arrow_function" || node2.type === "function" || node2.type === "function_expression" || node2.type === "method_definition" || node2.type === "class_declaration") {
|
|
92270
|
+
return false;
|
|
92271
|
+
}
|
|
92272
|
+
for (let i = 0; i < node2.childCount; i++) {
|
|
92273
|
+
const child = node2.child(i);
|
|
92274
|
+
if (child && containsBindingInBlock(child, varName))
|
|
92275
|
+
return true;
|
|
92276
|
+
}
|
|
92277
|
+
return false;
|
|
92278
|
+
}
|
|
92073
92279
|
var jsonParseInLoopVisitor;
|
|
92074
92280
|
var init_json_parse_in_loop = __esm({
|
|
92075
92281
|
"packages/analyzer/dist/rules/performance/visitors/javascript/json-parse-in-loop.js"() {
|
|
@@ -92094,64 +92300,9 @@ var init_json_parse_in_loop = __esm({
|
|
|
92094
92300
|
if (!isInsideLoop(node2))
|
|
92095
92301
|
return null;
|
|
92096
92302
|
const args = node2.childForFieldName("arguments");
|
|
92097
|
-
|
|
92098
|
-
|
|
92099
|
-
|
|
92100
|
-
if (firstArg.type === "call_expression")
|
|
92101
|
-
return null;
|
|
92102
|
-
if (firstArg.type === "subscript_expression")
|
|
92103
|
-
return null;
|
|
92104
|
-
if (firstArg.type === "template_string")
|
|
92105
|
-
return null;
|
|
92106
|
-
if (firstArg.type === "identifier") {
|
|
92107
|
-
const varName = firstArg.text;
|
|
92108
|
-
let current = node2.parent;
|
|
92109
|
-
while (current) {
|
|
92110
|
-
if (current.type === "for_in_statement" || current.type === "for_of_statement") {
|
|
92111
|
-
const left = current.childForFieldName("left");
|
|
92112
|
-
if (left && containsIdentifierExact(left, varName))
|
|
92113
|
-
return null;
|
|
92114
|
-
}
|
|
92115
|
-
if (current.type === "for_statement") {
|
|
92116
|
-
const init = current.childForFieldName("initializer");
|
|
92117
|
-
if (init && containsIdentifierExact(init, varName))
|
|
92118
|
-
return null;
|
|
92119
|
-
}
|
|
92120
|
-
if (current.type === "arrow_function" || current.type === "function_expression" || current.type === "function") {
|
|
92121
|
-
const params = current.childForFieldName("parameters");
|
|
92122
|
-
if (params && containsIdentifierExact(params, varName)) {
|
|
92123
|
-
const callParent = current.parent?.parent;
|
|
92124
|
-
if (callParent?.type === "call_expression")
|
|
92125
|
-
return null;
|
|
92126
|
-
}
|
|
92127
|
-
}
|
|
92128
|
-
current = current.parent;
|
|
92129
|
-
}
|
|
92130
|
-
}
|
|
92131
|
-
if (firstArg.type === "member_expression") {
|
|
92132
|
-
const innerObj = firstArg.childForFieldName("object");
|
|
92133
|
-
if (innerObj?.type === "identifier") {
|
|
92134
|
-
let current = node2.parent;
|
|
92135
|
-
while (current) {
|
|
92136
|
-
if (current.type === "for_in_statement" || current.type === "for_of_statement") {
|
|
92137
|
-
const left = current.childForFieldName("left");
|
|
92138
|
-
if (left && containsIdentifierExact(left, innerObj.text))
|
|
92139
|
-
return null;
|
|
92140
|
-
}
|
|
92141
|
-
if (current.type === "arrow_function" || current.type === "function_expression" || current.type === "function") {
|
|
92142
|
-
const params = current.childForFieldName("parameters");
|
|
92143
|
-
if (params && containsIdentifierExact(params, innerObj.text)) {
|
|
92144
|
-
const callParent = current.parent?.parent;
|
|
92145
|
-
if (callParent?.type === "call_expression")
|
|
92146
|
-
return null;
|
|
92147
|
-
}
|
|
92148
|
-
}
|
|
92149
|
-
current = current.parent;
|
|
92150
|
-
}
|
|
92151
|
-
}
|
|
92152
|
-
}
|
|
92153
|
-
}
|
|
92154
|
-
}
|
|
92303
|
+
const firstArg = args?.namedChildren[0];
|
|
92304
|
+
if (firstArg && isDynamicPerIteration(firstArg, node2))
|
|
92305
|
+
return null;
|
|
92155
92306
|
return makeViolation(this.ruleKey, node2, filePath, "medium", `JSON.${prop.text}() inside loop`, `JSON.${prop.text}() is expensive and calling it inside a loop degrades performance. Move it outside the loop if possible.`, sourceCode, `Cache the result of JSON.${prop.text}() outside the loop.`);
|
|
92156
92307
|
}
|
|
92157
92308
|
};
|
|
@@ -92484,6 +92635,19 @@ var init_missing_usememo_expensive = __esm({
|
|
|
92484
92635
|
return null;
|
|
92485
92636
|
}
|
|
92486
92637
|
}
|
|
92638
|
+
if (prop.text === "reduce") {
|
|
92639
|
+
const args = node2.childForFieldName("arguments");
|
|
92640
|
+
const callback = args?.namedChild(0);
|
|
92641
|
+
if (callback?.type === "arrow_function" || callback?.type === "function_expression" || callback?.type === "function") {
|
|
92642
|
+
const body = callback.childForFieldName("body");
|
|
92643
|
+
let inspect = body;
|
|
92644
|
+
if (inspect?.type === "parenthesized_expression") {
|
|
92645
|
+
inspect = inspect.namedChildren[0] ?? inspect;
|
|
92646
|
+
}
|
|
92647
|
+
if (inspect?.type === "binary_expression")
|
|
92648
|
+
return null;
|
|
92649
|
+
}
|
|
92650
|
+
}
|
|
92487
92651
|
const enclosingFn = findEnclosingFunctionNode(node2);
|
|
92488
92652
|
if (!enclosingFn)
|
|
92489
92653
|
return null;
|
|
@@ -93479,6 +93643,37 @@ var init_python9 = __esm({
|
|
|
93479
93643
|
});
|
|
93480
93644
|
|
|
93481
93645
|
// packages/analyzer/dist/rules/reliability/visitors/javascript/catch-without-error-type.js
|
|
93646
|
+
function narrowsViaTypeGuard(node2, paramName2) {
|
|
93647
|
+
if (node2.type === "call_expression") {
|
|
93648
|
+
const fn = node2.childForFieldName("function");
|
|
93649
|
+
let calleeName = null;
|
|
93650
|
+
if (fn?.type === "identifier")
|
|
93651
|
+
calleeName = fn.text;
|
|
93652
|
+
else if (fn?.type === "member_expression")
|
|
93653
|
+
calleeName = fn.childForFieldName("property")?.text ?? null;
|
|
93654
|
+
if (calleeName && TYPE_GUARD_NAME.test(calleeName)) {
|
|
93655
|
+
const args = node2.childForFieldName("arguments");
|
|
93656
|
+
if (args && referencesBinding(args, paramName2))
|
|
93657
|
+
return true;
|
|
93658
|
+
}
|
|
93659
|
+
}
|
|
93660
|
+
for (let i = 0; i < node2.childCount; i++) {
|
|
93661
|
+
const ch = node2.child(i);
|
|
93662
|
+
if (ch && narrowsViaTypeGuard(ch, paramName2))
|
|
93663
|
+
return true;
|
|
93664
|
+
}
|
|
93665
|
+
return false;
|
|
93666
|
+
}
|
|
93667
|
+
function referencesBinding(node2, name) {
|
|
93668
|
+
if (node2.type === "identifier" && node2.text === name)
|
|
93669
|
+
return true;
|
|
93670
|
+
for (let i = 0; i < node2.childCount; i++) {
|
|
93671
|
+
const ch = node2.child(i);
|
|
93672
|
+
if (ch && referencesBinding(ch, name))
|
|
93673
|
+
return true;
|
|
93674
|
+
}
|
|
93675
|
+
return false;
|
|
93676
|
+
}
|
|
93482
93677
|
function hasBranchingConstruct(node2) {
|
|
93483
93678
|
if (node2.type === "if_statement" || node2.type === "switch_statement" || node2.type === "ternary_expression")
|
|
93484
93679
|
return true;
|
|
@@ -93491,7 +93686,7 @@ function hasBranchingConstruct(node2) {
|
|
|
93491
93686
|
}
|
|
93492
93687
|
return false;
|
|
93493
93688
|
}
|
|
93494
|
-
var catchWithoutErrorTypeVisitor;
|
|
93689
|
+
var catchWithoutErrorTypeVisitor, TYPE_GUARD_NAME;
|
|
93495
93690
|
var init_catch_without_error_type = __esm({
|
|
93496
93691
|
"packages/analyzer/dist/rules/reliability/visitors/javascript/catch-without-error-type.js"() {
|
|
93497
93692
|
"use strict";
|
|
@@ -93519,9 +93714,13 @@ var init_catch_without_error_type = __esm({
|
|
|
93519
93714
|
return null;
|
|
93520
93715
|
if (!hasBranchingConstruct(body))
|
|
93521
93716
|
return null;
|
|
93717
|
+
const paramName2 = param.type === "identifier" ? param.text : null;
|
|
93718
|
+
if (paramName2 && narrowsViaTypeGuard(body, paramName2))
|
|
93719
|
+
return null;
|
|
93522
93720
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Catch without error type discrimination", "Catch block does not check or narrow the error type. Different error types may need different handling.", sourceCode, "Use instanceof checks or type guards in the catch block to handle specific error types.");
|
|
93523
93721
|
}
|
|
93524
93722
|
};
|
|
93723
|
+
TYPE_GUARD_NAME = /^(is|has|assert)[A-Z]/;
|
|
93525
93724
|
}
|
|
93526
93725
|
});
|
|
93527
93726
|
|
|
@@ -98763,7 +98962,8 @@ __export(dist_exports2, {
|
|
|
98763
98962
|
shouldExtractEntities: () => shouldExtractEntities,
|
|
98764
98963
|
toLayerDetectionResults: () => toLayerDetectionResults,
|
|
98765
98964
|
traceFlows: () => traceFlows,
|
|
98766
|
-
walkAstWithVisitors: () => walkAstWithVisitors
|
|
98965
|
+
walkAstWithVisitors: () => walkAstWithVisitors,
|
|
98966
|
+
withParsedTree: () => withParsedTree
|
|
98767
98967
|
});
|
|
98768
98968
|
async function analyzeRepository(rootPath) {
|
|
98769
98969
|
await initParsers();
|
|
@@ -104140,6 +104340,12 @@ function loadTcIgnore(startDir) {
|
|
|
104140
104340
|
if (rel === "" || rel.startsWith(".."))
|
|
104141
104341
|
return false;
|
|
104142
104342
|
return ig.ignores(rel);
|
|
104343
|
+
},
|
|
104344
|
+
reincludes(absPath) {
|
|
104345
|
+
const rel = path12.relative(root, path12.resolve(absPath)).split(path12.sep).join("/");
|
|
104346
|
+
if (rel === "" || rel.startsWith(".."))
|
|
104347
|
+
return false;
|
|
104348
|
+
return ig.test(rel).unignored;
|
|
104143
104349
|
}
|
|
104144
104350
|
};
|
|
104145
104351
|
}
|
|
@@ -104377,12 +104583,12 @@ var require_isexe = __commonJS({
|
|
|
104377
104583
|
if (typeof Promise !== "function") {
|
|
104378
104584
|
throw new TypeError("callback not provided");
|
|
104379
104585
|
}
|
|
104380
|
-
return new Promise(function(
|
|
104586
|
+
return new Promise(function(resolve10, reject) {
|
|
104381
104587
|
isexe(path61, options || {}, function(er, is) {
|
|
104382
104588
|
if (er) {
|
|
104383
104589
|
reject(er);
|
|
104384
104590
|
} else {
|
|
104385
|
-
|
|
104591
|
+
resolve10(is);
|
|
104386
104592
|
}
|
|
104387
104593
|
});
|
|
104388
104594
|
});
|
|
@@ -104448,27 +104654,27 @@ var require_which = __commonJS({
|
|
|
104448
104654
|
opt = {};
|
|
104449
104655
|
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
104450
104656
|
const found = [];
|
|
104451
|
-
const step = (i) => new Promise((
|
|
104657
|
+
const step = (i) => new Promise((resolve10, reject) => {
|
|
104452
104658
|
if (i === pathEnv.length)
|
|
104453
|
-
return opt.all && found.length ?
|
|
104659
|
+
return opt.all && found.length ? resolve10(found) : reject(getNotFoundError(cmd));
|
|
104454
104660
|
const ppRaw = pathEnv[i];
|
|
104455
104661
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
104456
104662
|
const pCmd = path61.join(pathPart, cmd);
|
|
104457
104663
|
const p2 = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
104458
|
-
|
|
104664
|
+
resolve10(subStep(p2, i, 0));
|
|
104459
104665
|
});
|
|
104460
|
-
const subStep = (p2, i, ii) => new Promise((
|
|
104666
|
+
const subStep = (p2, i, ii) => new Promise((resolve10, reject) => {
|
|
104461
104667
|
if (ii === pathExt.length)
|
|
104462
|
-
return
|
|
104668
|
+
return resolve10(step(i + 1));
|
|
104463
104669
|
const ext2 = pathExt[ii];
|
|
104464
104670
|
isexe(p2 + ext2, { pathExt: pathExtExe }, (er, is) => {
|
|
104465
104671
|
if (!er && is) {
|
|
104466
104672
|
if (opt.all)
|
|
104467
104673
|
found.push(p2 + ext2);
|
|
104468
104674
|
else
|
|
104469
|
-
return
|
|
104675
|
+
return resolve10(p2 + ext2);
|
|
104470
104676
|
}
|
|
104471
|
-
return
|
|
104677
|
+
return resolve10(subStep(p2, i, ii + 1));
|
|
104472
104678
|
});
|
|
104473
104679
|
});
|
|
104474
104680
|
return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
|
|
@@ -104873,27 +105079,27 @@ function pLimit(concurrency) {
|
|
|
104873
105079
|
activeCount--;
|
|
104874
105080
|
resumeNext();
|
|
104875
105081
|
};
|
|
104876
|
-
const run = async (function_,
|
|
105082
|
+
const run = async (function_, resolve10, arguments_) => {
|
|
104877
105083
|
const result = (async () => function_(...arguments_))();
|
|
104878
|
-
|
|
105084
|
+
resolve10(result);
|
|
104879
105085
|
try {
|
|
104880
105086
|
await result;
|
|
104881
105087
|
} catch {
|
|
104882
105088
|
}
|
|
104883
105089
|
next();
|
|
104884
105090
|
};
|
|
104885
|
-
const enqueue = (function_,
|
|
105091
|
+
const enqueue = (function_, resolve10, reject, arguments_) => {
|
|
104886
105092
|
const queueItem = { reject };
|
|
104887
105093
|
new Promise((internalResolve) => {
|
|
104888
105094
|
queueItem.run = internalResolve;
|
|
104889
105095
|
queue.enqueue(queueItem);
|
|
104890
|
-
}).then(run.bind(void 0, function_,
|
|
105096
|
+
}).then(run.bind(void 0, function_, resolve10, arguments_));
|
|
104891
105097
|
if (activeCount < concurrency) {
|
|
104892
105098
|
resumeNext();
|
|
104893
105099
|
}
|
|
104894
105100
|
};
|
|
104895
|
-
const generator = (function_, ...arguments_) => new Promise((
|
|
104896
|
-
enqueue(function_,
|
|
105101
|
+
const generator = (function_, ...arguments_) => new Promise((resolve10, reject) => {
|
|
105102
|
+
enqueue(function_, resolve10, reject, arguments_);
|
|
104897
105103
|
});
|
|
104898
105104
|
Object.defineProperties(generator, {
|
|
104899
105105
|
activeCount: {
|
|
@@ -107746,7 +107952,7 @@ var init_cli_provider = __esm({
|
|
|
107746
107952
|
jsonSchemaStr,
|
|
107747
107953
|
...opts?.extraArgs ?? []
|
|
107748
107954
|
];
|
|
107749
|
-
return new Promise((
|
|
107955
|
+
return new Promise((resolve10, reject) => {
|
|
107750
107956
|
const child = (0, import_cross_spawn.default)(this.binaryName, args, {
|
|
107751
107957
|
env: this.getCleanEnv(),
|
|
107752
107958
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -107794,7 +108000,7 @@ var init_cli_provider = __esm({
|
|
|
107794
108000
|
reject(new Error(`[CLI] ${this.binaryName} exited with code ${code}: ${detail}`));
|
|
107795
108001
|
return;
|
|
107796
108002
|
}
|
|
107797
|
-
|
|
108003
|
+
resolve10(stdout);
|
|
107798
108004
|
});
|
|
107799
108005
|
child.on("error", (err) => {
|
|
107800
108006
|
clearTimeout(timer);
|
|
@@ -109303,12 +109509,12 @@ async function runViolationPipeline(input) {
|
|
|
109303
109509
|
const fileContents = /* @__PURE__ */ new Map();
|
|
109304
109510
|
const totalToScan = filesToScan.length;
|
|
109305
109511
|
let scanned = 0;
|
|
109306
|
-
for (const { filePath, resolve:
|
|
109512
|
+
for (const { filePath, resolve: resolve10 } of filesToScan) {
|
|
109307
109513
|
try {
|
|
109308
109514
|
const lang = detectLanguage(filePath);
|
|
109309
109515
|
if (!lang)
|
|
109310
109516
|
continue;
|
|
109311
|
-
const absPath =
|
|
109517
|
+
const absPath = resolve10 ? path14.resolve(repoPath, filePath) : path14.isAbsolute(filePath) ? filePath : path14.join(repoPath, filePath);
|
|
109312
109518
|
if (!fs12.existsSync(absPath))
|
|
109313
109519
|
continue;
|
|
109314
109520
|
const content = fs12.readFileSync(absPath, "utf-8");
|
|
@@ -109413,18 +109619,17 @@ async function runViolationPipeline(input) {
|
|
|
109413
109619
|
const DETAIL_UPDATE_MS = 100;
|
|
109414
109620
|
let lastYieldMs = Date.now();
|
|
109415
109621
|
let lastDetailMs = lastYieldMs;
|
|
109416
|
-
for (const { filePath, resolve:
|
|
109622
|
+
for (const { filePath, resolve: resolve10 } of filesToScan) {
|
|
109417
109623
|
try {
|
|
109418
109624
|
const lang = detectLanguage(filePath);
|
|
109419
109625
|
if (!lang)
|
|
109420
109626
|
continue;
|
|
109421
|
-
const absPath =
|
|
109627
|
+
const absPath = resolve10 ? path14.resolve(repoPath, filePath) : path14.isAbsolute(filePath) ? filePath : path14.join(repoPath, filePath);
|
|
109422
109628
|
const key2 = changedFileSet ? absPath : filePath;
|
|
109423
109629
|
const fc = fileContents.get(key2);
|
|
109424
109630
|
if (!fc)
|
|
109425
109631
|
continue;
|
|
109426
|
-
const
|
|
109427
|
-
const codeRuleViolations = checkCodeRules(tree, changedFileSet ? absPath : filePath, fc.content, enabledCodeRules, lang, typeQuery, schemaIndex);
|
|
109632
|
+
const codeRuleViolations = withParsedTree(filePath, fc.content, lang, (tree) => checkCodeRules(tree, changedFileSet ? absPath : filePath, fc.content, enabledCodeRules, lang, typeQuery, schemaIndex));
|
|
109428
109633
|
allCodeViolations.push(...codeRuleViolations);
|
|
109429
109634
|
} catch {
|
|
109430
109635
|
}
|
|
@@ -123260,10 +123465,10 @@ var require_axios = __commonJS({
|
|
|
123260
123465
|
this.__CANCEL__ = true;
|
|
123261
123466
|
}
|
|
123262
123467
|
};
|
|
123263
|
-
function settle(
|
|
123468
|
+
function settle(resolve10, reject, response) {
|
|
123264
123469
|
const validateStatus = response.config.validateStatus;
|
|
123265
123470
|
if (!response.status || !validateStatus || validateStatus(response.status)) {
|
|
123266
|
-
|
|
123471
|
+
resolve10(response);
|
|
123267
123472
|
} else {
|
|
123268
123473
|
reject(new AxiosError("Request failed with status code " + response.status, [AxiosError.ERR_BAD_REQUEST, AxiosError.ERR_BAD_RESPONSE][Math.floor(response.status / 100) - 4], response.config, response.request, response));
|
|
123269
123474
|
}
|
|
@@ -123906,7 +124111,7 @@ var require_axios = __commonJS({
|
|
|
123906
124111
|
}
|
|
123907
124112
|
var isHttpAdapterSupported = typeof process !== "undefined" && utils$1.kindOf(process) === "process";
|
|
123908
124113
|
var wrapAsync = (asyncExecutor) => {
|
|
123909
|
-
return new Promise((
|
|
124114
|
+
return new Promise((resolve10, reject) => {
|
|
123910
124115
|
let onDone;
|
|
123911
124116
|
let isDone;
|
|
123912
124117
|
const done = (value, isRejected) => {
|
|
@@ -123916,7 +124121,7 @@ var require_axios = __commonJS({
|
|
|
123916
124121
|
};
|
|
123917
124122
|
const _resolve = (value) => {
|
|
123918
124123
|
done(value);
|
|
123919
|
-
|
|
124124
|
+
resolve10(value);
|
|
123920
124125
|
};
|
|
123921
124126
|
const _reject = (reason) => {
|
|
123922
124127
|
done(reason, true);
|
|
@@ -123977,7 +124182,7 @@ var require_axios = __commonJS({
|
|
|
123977
124182
|
}
|
|
123978
124183
|
};
|
|
123979
124184
|
var httpAdapter = isHttpAdapterSupported && function httpAdapter2(config2) {
|
|
123980
|
-
return wrapAsync(async function dispatchHttpRequest(
|
|
124185
|
+
return wrapAsync(async function dispatchHttpRequest(resolve10, reject, onDone) {
|
|
123981
124186
|
let {
|
|
123982
124187
|
data,
|
|
123983
124188
|
lookup,
|
|
@@ -124069,7 +124274,7 @@ var require_axios = __commonJS({
|
|
|
124069
124274
|
}
|
|
124070
124275
|
let convertedData;
|
|
124071
124276
|
if (method !== "GET") {
|
|
124072
|
-
return settle(
|
|
124277
|
+
return settle(resolve10, reject, {
|
|
124073
124278
|
status: 405,
|
|
124074
124279
|
statusText: "method not allowed",
|
|
124075
124280
|
headers: {},
|
|
@@ -124091,7 +124296,7 @@ var require_axios = __commonJS({
|
|
|
124091
124296
|
} else if (responseType === "stream") {
|
|
124092
124297
|
convertedData = stream.Readable.from(convertedData);
|
|
124093
124298
|
}
|
|
124094
|
-
return settle(
|
|
124299
|
+
return settle(resolve10, reject, {
|
|
124095
124300
|
data: convertedData,
|
|
124096
124301
|
status: 200,
|
|
124097
124302
|
statusText: "OK",
|
|
@@ -124286,7 +124491,7 @@ var require_axios = __commonJS({
|
|
|
124286
124491
|
};
|
|
124287
124492
|
if (responseType === "stream") {
|
|
124288
124493
|
response.data = responseStream;
|
|
124289
|
-
settle(
|
|
124494
|
+
settle(resolve10, reject, response);
|
|
124290
124495
|
} else {
|
|
124291
124496
|
const responseBuffer = [];
|
|
124292
124497
|
let totalResponseBytes = 0;
|
|
@@ -124324,7 +124529,7 @@ var require_axios = __commonJS({
|
|
|
124324
124529
|
} catch (err) {
|
|
124325
124530
|
return reject(AxiosError.from(err, null, config2, response.request, response));
|
|
124326
124531
|
}
|
|
124327
|
-
settle(
|
|
124532
|
+
settle(resolve10, reject, response);
|
|
124328
124533
|
});
|
|
124329
124534
|
}
|
|
124330
124535
|
abortEmitter.once("abort", (err) => {
|
|
@@ -124562,7 +124767,7 @@ var require_axios = __commonJS({
|
|
|
124562
124767
|
};
|
|
124563
124768
|
var isXHRAdapterSupported = typeof XMLHttpRequest !== "undefined";
|
|
124564
124769
|
var xhrAdapter = isXHRAdapterSupported && function(config2) {
|
|
124565
|
-
return new Promise(function dispatchXhrRequest(
|
|
124770
|
+
return new Promise(function dispatchXhrRequest(resolve10, reject) {
|
|
124566
124771
|
const _config = resolveConfig(config2);
|
|
124567
124772
|
let requestData = _config.data;
|
|
124568
124773
|
const requestHeaders = AxiosHeaders.from(_config.headers).normalize();
|
|
@@ -124598,7 +124803,7 @@ var require_axios = __commonJS({
|
|
|
124598
124803
|
request
|
|
124599
124804
|
};
|
|
124600
124805
|
settle(function _resolve(value) {
|
|
124601
|
-
|
|
124806
|
+
resolve10(value);
|
|
124602
124807
|
done();
|
|
124603
124808
|
}, function _reject(err) {
|
|
124604
124809
|
reject(err);
|
|
@@ -124969,8 +125174,8 @@ var require_axios = __commonJS({
|
|
|
124969
125174
|
responseType = responseType || "text";
|
|
124970
125175
|
let responseData = await resolvers[utils$1.findKey(resolvers, responseType) || "text"](response, config2);
|
|
124971
125176
|
!isStreamResponse && unsubscribe && unsubscribe();
|
|
124972
|
-
return await new Promise((
|
|
124973
|
-
settle(
|
|
125177
|
+
return await new Promise((resolve10, reject) => {
|
|
125178
|
+
settle(resolve10, reject, {
|
|
124974
125179
|
data: responseData,
|
|
124975
125180
|
headers: AxiosHeaders.from(response.headers),
|
|
124976
125181
|
status: response.status,
|
|
@@ -125339,8 +125544,8 @@ var require_axios = __commonJS({
|
|
|
125339
125544
|
throw new TypeError("executor must be a function.");
|
|
125340
125545
|
}
|
|
125341
125546
|
let resolvePromise;
|
|
125342
|
-
this.promise = new Promise(function promiseExecutor(
|
|
125343
|
-
resolvePromise =
|
|
125547
|
+
this.promise = new Promise(function promiseExecutor(resolve10) {
|
|
125548
|
+
resolvePromise = resolve10;
|
|
125344
125549
|
});
|
|
125345
125550
|
const token = this;
|
|
125346
125551
|
this.promise.then((cancel) => {
|
|
@@ -125353,9 +125558,9 @@ var require_axios = __commonJS({
|
|
|
125353
125558
|
});
|
|
125354
125559
|
this.promise.then = (onfulfilled) => {
|
|
125355
125560
|
let _resolve;
|
|
125356
|
-
const promise = new Promise((
|
|
125357
|
-
token.subscribe(
|
|
125358
|
-
_resolve =
|
|
125561
|
+
const promise = new Promise((resolve10) => {
|
|
125562
|
+
token.subscribe(resolve10);
|
|
125563
|
+
_resolve = resolve10;
|
|
125359
125564
|
}).then(onfulfilled);
|
|
125360
125565
|
promise.cancel = function reject() {
|
|
125361
125566
|
token.unsubscribe(_resolve);
|
|
@@ -125969,14 +126174,14 @@ async function addSourceContext(frames) {
|
|
|
125969
126174
|
return frames;
|
|
125970
126175
|
}
|
|
125971
126176
|
function getContextLinesFromFile(path61, ranges, output) {
|
|
125972
|
-
return new Promise((
|
|
126177
|
+
return new Promise((resolve10) => {
|
|
125973
126178
|
const stream = createReadStream(path61);
|
|
125974
126179
|
const lineReaded = createInterface2({
|
|
125975
126180
|
input: stream
|
|
125976
126181
|
});
|
|
125977
126182
|
function destroyStreamAndResolve() {
|
|
125978
126183
|
stream.destroy();
|
|
125979
|
-
|
|
126184
|
+
resolve10();
|
|
125980
126185
|
}
|
|
125981
126186
|
let lineNumber = 0;
|
|
125982
126187
|
let currentRangeIndex = 0;
|
|
@@ -129082,15 +129287,15 @@ var init_node = __esm({
|
|
|
129082
129287
|
if (this.featureFlagsPoller === void 0) {
|
|
129083
129288
|
return false;
|
|
129084
129289
|
}
|
|
129085
|
-
return new Promise((
|
|
129290
|
+
return new Promise((resolve10) => {
|
|
129086
129291
|
const timeout = setTimeout(() => {
|
|
129087
129292
|
cleanup();
|
|
129088
|
-
|
|
129293
|
+
resolve10(false);
|
|
129089
129294
|
}, timeoutMs);
|
|
129090
129295
|
const cleanup = this._events.on("localEvaluationFlagsLoaded", (count) => {
|
|
129091
129296
|
clearTimeout(timeout);
|
|
129092
129297
|
cleanup();
|
|
129093
|
-
|
|
129298
|
+
resolve10(count > 0);
|
|
129094
129299
|
});
|
|
129095
129300
|
});
|
|
129096
129301
|
}
|
|
@@ -129415,7 +129620,7 @@ function readToolVersion() {
|
|
|
129415
129620
|
if (cachedVersion)
|
|
129416
129621
|
return cachedVersion;
|
|
129417
129622
|
if (true) {
|
|
129418
|
-
cachedVersion = "0.6.
|
|
129623
|
+
cachedVersion = "0.6.7-next.1";
|
|
129419
129624
|
return cachedVersion;
|
|
129420
129625
|
}
|
|
129421
129626
|
try {
|
|
@@ -132744,6 +132949,77 @@ var init_operation2 = __esm({
|
|
|
132744
132949
|
});
|
|
132745
132950
|
|
|
132746
132951
|
// packages/contract-verifier/dist/extractor/operation-fastapi.js
|
|
132952
|
+
function extractStarletteRoutesFromFile(filePath, source, tree) {
|
|
132953
|
+
const out = [];
|
|
132954
|
+
walk(tree.rootNode, (node2) => {
|
|
132955
|
+
if (node2.type !== "call")
|
|
132956
|
+
return;
|
|
132957
|
+
const fn = node2.childForFieldName("function");
|
|
132958
|
+
if (fn?.type !== "identifier" || source.slice(fn.startIndex, fn.endIndex) !== "Route")
|
|
132959
|
+
return;
|
|
132960
|
+
const args = node2.childForFieldName("arguments");
|
|
132961
|
+
if (!args)
|
|
132962
|
+
return;
|
|
132963
|
+
const first2 = args.namedChild(0);
|
|
132964
|
+
if (first2?.type !== "string")
|
|
132965
|
+
return;
|
|
132966
|
+
const rawPath = pyStr(first2, source);
|
|
132967
|
+
if (!rawPath.startsWith("/"))
|
|
132968
|
+
return;
|
|
132969
|
+
const second = args.namedChild(1);
|
|
132970
|
+
if (!second || second.type === "keyword_argument")
|
|
132971
|
+
return;
|
|
132972
|
+
const methods = readStarletteMethods(args, source);
|
|
132973
|
+
const identities = starletteIdentitiesForPath(rawPath);
|
|
132974
|
+
const line = node2.startPosition.row + 1;
|
|
132975
|
+
for (const method of methods) {
|
|
132976
|
+
for (const path61 of identities) {
|
|
132977
|
+
out.push({
|
|
132978
|
+
identity: `${method} ${path61}`,
|
|
132979
|
+
contract: {
|
|
132980
|
+
protocol: "http",
|
|
132981
|
+
method,
|
|
132982
|
+
path: path61,
|
|
132983
|
+
responses: [],
|
|
132984
|
+
tags: []
|
|
132985
|
+
},
|
|
132986
|
+
filePath,
|
|
132987
|
+
declarationLine: line,
|
|
132988
|
+
observed: { queryParams: [], numericClamps: [], hasClampCall: false }
|
|
132989
|
+
});
|
|
132990
|
+
}
|
|
132991
|
+
}
|
|
132992
|
+
});
|
|
132993
|
+
return out;
|
|
132994
|
+
}
|
|
132995
|
+
function readStarletteMethods(args, source) {
|
|
132996
|
+
for (let i = 0; i < args.namedChildCount; i++) {
|
|
132997
|
+
const a = args.namedChild(i);
|
|
132998
|
+
if (a?.type !== "keyword_argument")
|
|
132999
|
+
continue;
|
|
133000
|
+
const name = a.childForFieldName("name");
|
|
133001
|
+
if (!name || source.slice(name.startIndex, name.endIndex) !== "methods")
|
|
133002
|
+
continue;
|
|
133003
|
+
const value = a.childForFieldName("value");
|
|
133004
|
+
if (value?.type !== "list")
|
|
133005
|
+
return ["GET"];
|
|
133006
|
+
const methods = [];
|
|
133007
|
+
for (let j2 = 0; j2 < value.namedChildCount; j2++) {
|
|
133008
|
+
const el = value.namedChild(j2);
|
|
133009
|
+
if (el?.type === "string")
|
|
133010
|
+
methods.push(pyStr(el, source).toUpperCase());
|
|
133011
|
+
}
|
|
133012
|
+
return methods.length > 0 ? methods : ["GET"];
|
|
133013
|
+
}
|
|
133014
|
+
return ["GET"];
|
|
133015
|
+
}
|
|
133016
|
+
function starletteIdentitiesForPath(rawPath) {
|
|
133017
|
+
const catchAll = rawPath.match(/^(\/.*\/)\{\w+:path\}$/);
|
|
133018
|
+
if (catchAll) {
|
|
133019
|
+
return [catchAll[1].replace(/\{(\w+):[^}]+\}/g, "{$1}")];
|
|
133020
|
+
}
|
|
133021
|
+
return [rawPath.replace(/\{(\w+):[^}]+\}/g, "{$1}")];
|
|
133022
|
+
}
|
|
132747
133023
|
function extractFastApiOperationsFromFile(filePath, source, tree) {
|
|
132748
133024
|
const routers = collectRouters(tree.rootNode, source);
|
|
132749
133025
|
const stringVars = collectStringVars(tree.rootNode, source);
|
|
@@ -136348,9 +136624,85 @@ var init_ts_enums = __esm({
|
|
|
136348
136624
|
}
|
|
136349
136625
|
});
|
|
136350
136626
|
|
|
136627
|
+
// packages/contract-verifier/dist/extractor/py-string-resolver.js
|
|
136628
|
+
function collectStringConstantTable(rootNode, source) {
|
|
136629
|
+
const table = /* @__PURE__ */ new Map();
|
|
136630
|
+
for (let i = 0; i < rootNode.namedChildCount; i++) {
|
|
136631
|
+
const stmt = rootNode.namedChild(i);
|
|
136632
|
+
if (stmt?.type !== "expression_statement")
|
|
136633
|
+
continue;
|
|
136634
|
+
const assign = stmt.namedChild(0);
|
|
136635
|
+
if (assign?.type !== "assignment")
|
|
136636
|
+
continue;
|
|
136637
|
+
const left = assign.childForFieldName("left");
|
|
136638
|
+
if (left?.type !== "identifier")
|
|
136639
|
+
continue;
|
|
136640
|
+
const right = assign.childForFieldName("right");
|
|
136641
|
+
if (right?.type !== "string")
|
|
136642
|
+
continue;
|
|
136643
|
+
const name = source.slice(left.startIndex, left.endIndex);
|
|
136644
|
+
if (table.has(name))
|
|
136645
|
+
continue;
|
|
136646
|
+
table.set(name, right);
|
|
136647
|
+
}
|
|
136648
|
+
return table;
|
|
136649
|
+
}
|
|
136650
|
+
function stringValue(node2, source, table) {
|
|
136651
|
+
return resolve9(node2, source, table, 0);
|
|
136652
|
+
}
|
|
136653
|
+
function resolve9(node2, source, table, depth) {
|
|
136654
|
+
if (node2.type !== "string")
|
|
136655
|
+
return null;
|
|
136656
|
+
if (depth > MAX_DEPTH3)
|
|
136657
|
+
return null;
|
|
136658
|
+
let content = "";
|
|
136659
|
+
let sawContent = false;
|
|
136660
|
+
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
136661
|
+
const c2 = node2.namedChild(i);
|
|
136662
|
+
if (!c2)
|
|
136663
|
+
continue;
|
|
136664
|
+
if (c2.type === "string_content") {
|
|
136665
|
+
content += source.slice(c2.startIndex, c2.endIndex);
|
|
136666
|
+
sawContent = true;
|
|
136667
|
+
continue;
|
|
136668
|
+
}
|
|
136669
|
+
if (c2.type === "interpolation") {
|
|
136670
|
+
if (c2.namedChildren.some((g) => g?.type === "format_specifier"))
|
|
136671
|
+
return null;
|
|
136672
|
+
const expr = c2.namedChild(0);
|
|
136673
|
+
if (expr?.type !== "identifier" || !table)
|
|
136674
|
+
return null;
|
|
136675
|
+
const referent = table.get(source.slice(expr.startIndex, expr.endIndex));
|
|
136676
|
+
if (!referent)
|
|
136677
|
+
return null;
|
|
136678
|
+
const resolved = resolve9(referent, source, table, depth + 1);
|
|
136679
|
+
if (resolved === null)
|
|
136680
|
+
return null;
|
|
136681
|
+
content += resolved;
|
|
136682
|
+
sawContent = true;
|
|
136683
|
+
continue;
|
|
136684
|
+
}
|
|
136685
|
+
if (c2.type === "format_specifier")
|
|
136686
|
+
return null;
|
|
136687
|
+
}
|
|
136688
|
+
if (sawContent)
|
|
136689
|
+
return content;
|
|
136690
|
+
const raw = source.slice(node2.startIndex, node2.endIndex);
|
|
136691
|
+
const m = raw.match(/^[a-zA-Z]*('''|"""|'|")([\s\S]*)\1$/);
|
|
136692
|
+
return m ? m[2] : null;
|
|
136693
|
+
}
|
|
136694
|
+
var MAX_DEPTH3;
|
|
136695
|
+
var init_py_string_resolver = __esm({
|
|
136696
|
+
"packages/contract-verifier/dist/extractor/py-string-resolver.js"() {
|
|
136697
|
+
"use strict";
|
|
136698
|
+
MAX_DEPTH3 = 4;
|
|
136699
|
+
}
|
|
136700
|
+
});
|
|
136701
|
+
|
|
136351
136702
|
// packages/contract-verifier/dist/extractor/enum/py-enums.js
|
|
136352
136703
|
function extractPyEnumsFromFile(filePath, source, tree) {
|
|
136353
136704
|
const out = [];
|
|
136705
|
+
const stringTable = collectStringConstantTable(tree.rootNode, source);
|
|
136354
136706
|
walk7(tree.rootNode, (node2) => {
|
|
136355
136707
|
if (node2.type === "class_definition") {
|
|
136356
136708
|
const decl = extractEnumClass(node2, filePath, source);
|
|
@@ -136366,6 +136718,302 @@ function extractPyEnumsFromFile(filePath, source, tree) {
|
|
|
136366
136718
|
}
|
|
136367
136719
|
return true;
|
|
136368
136720
|
});
|
|
136721
|
+
out.push(...synthesizeInstanceRegistryEnum(tree.rootNode, filePath, source));
|
|
136722
|
+
out.push(...synthesizeDiscriminatedUnionEnum(tree.rootNode, filePath, source));
|
|
136723
|
+
out.push(...synthesizeConstantClusterEnums(tree.rootNode, filePath, source, stringTable));
|
|
136724
|
+
return out;
|
|
136725
|
+
}
|
|
136726
|
+
function synthesizeDiscriminatedUnionEnum(root, filePath, source) {
|
|
136727
|
+
const discriminatorOf = /* @__PURE__ */ new Map();
|
|
136728
|
+
walk7(root, (node2) => {
|
|
136729
|
+
if (node2.type !== "class_definition")
|
|
136730
|
+
return true;
|
|
136731
|
+
const className = textOfField(node2, "name", source);
|
|
136732
|
+
const body = node2.childForFieldName("body");
|
|
136733
|
+
if (!className || !body)
|
|
136734
|
+
return true;
|
|
136735
|
+
for (let i = 0; i < body.namedChildCount; i++) {
|
|
136736
|
+
const stmt = body.namedChild(i);
|
|
136737
|
+
if (stmt?.type !== "expression_statement")
|
|
136738
|
+
continue;
|
|
136739
|
+
const assign = stmt.namedChild(0);
|
|
136740
|
+
if (assign?.type !== "assignment")
|
|
136741
|
+
continue;
|
|
136742
|
+
const left = assign.childForFieldName("left");
|
|
136743
|
+
const typeAnn = assign.childForFieldName("type");
|
|
136744
|
+
if (left?.type !== "identifier" || !typeAnn)
|
|
136745
|
+
continue;
|
|
136746
|
+
if (source.slice(left.startIndex, left.endIndex) !== "type")
|
|
136747
|
+
continue;
|
|
136748
|
+
const lit = literalStringOfAnnotation(typeAnn, source);
|
|
136749
|
+
if (lit !== null)
|
|
136750
|
+
discriminatorOf.set(className, lit);
|
|
136751
|
+
}
|
|
136752
|
+
return true;
|
|
136753
|
+
});
|
|
136754
|
+
if (discriminatorOf.size === 0)
|
|
136755
|
+
return [];
|
|
136756
|
+
const out = [];
|
|
136757
|
+
for (let i = 0; i < root.namedChildCount; i++) {
|
|
136758
|
+
const stmt = root.namedChild(i);
|
|
136759
|
+
if (stmt?.type !== "expression_statement")
|
|
136760
|
+
continue;
|
|
136761
|
+
const assign = stmt.namedChild(0);
|
|
136762
|
+
if (assign?.type !== "assignment")
|
|
136763
|
+
continue;
|
|
136764
|
+
const left = assign.childForFieldName("left");
|
|
136765
|
+
if (left?.type !== "identifier")
|
|
136766
|
+
continue;
|
|
136767
|
+
const aliasName = source.slice(left.startIndex, left.endIndex);
|
|
136768
|
+
const right = assign.childForFieldName("right");
|
|
136769
|
+
if (!right)
|
|
136770
|
+
continue;
|
|
136771
|
+
const memberClasses = unionMemberIdentifiers(right, source);
|
|
136772
|
+
if (memberClasses.length === 0)
|
|
136773
|
+
continue;
|
|
136774
|
+
const values = memberClasses.map((m) => discriminatorOf.get(m)).filter((v) => v !== void 0);
|
|
136775
|
+
if (values.length < 3)
|
|
136776
|
+
continue;
|
|
136777
|
+
out.push(mkEnum2(aliasName, values, "py-discriminated-union", root, filePath));
|
|
136778
|
+
}
|
|
136779
|
+
return out;
|
|
136780
|
+
}
|
|
136781
|
+
function literalStringOfAnnotation(ann, source) {
|
|
136782
|
+
let node2 = ann;
|
|
136783
|
+
if (node2.type === "type")
|
|
136784
|
+
node2 = node2.namedChild(0);
|
|
136785
|
+
if (!node2 || node2.type !== "generic_type" && node2.type !== "subscript")
|
|
136786
|
+
return null;
|
|
136787
|
+
const base = node2.namedChild(0);
|
|
136788
|
+
if (!base || !source.slice(base.startIndex, base.endIndex).endsWith("Literal"))
|
|
136789
|
+
return null;
|
|
136790
|
+
const strings = collectStringsDeep(node2, source);
|
|
136791
|
+
return strings.length === 1 ? strings[0] : null;
|
|
136792
|
+
}
|
|
136793
|
+
function collectStringsDeep(node2, source) {
|
|
136794
|
+
const out = [];
|
|
136795
|
+
const visit = (n) => {
|
|
136796
|
+
if (n.type === "string") {
|
|
136797
|
+
const v = stringValue(n, source);
|
|
136798
|
+
if (v !== null)
|
|
136799
|
+
out.push(v);
|
|
136800
|
+
return;
|
|
136801
|
+
}
|
|
136802
|
+
for (let i = 0; i < n.namedChildCount; i++) {
|
|
136803
|
+
const c2 = n.namedChild(i);
|
|
136804
|
+
if (c2)
|
|
136805
|
+
visit(c2);
|
|
136806
|
+
}
|
|
136807
|
+
};
|
|
136808
|
+
visit(node2);
|
|
136809
|
+
return out;
|
|
136810
|
+
}
|
|
136811
|
+
function unionMemberIdentifiers(node2, source) {
|
|
136812
|
+
if (node2.type === "subscript") {
|
|
136813
|
+
const value = node2.childForFieldName("value");
|
|
136814
|
+
if (value && source.slice(value.startIndex, value.endIndex).endsWith("Union")) {
|
|
136815
|
+
const out = [];
|
|
136816
|
+
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
136817
|
+
const c2 = node2.namedChild(i);
|
|
136818
|
+
if (c2 && c2 !== value)
|
|
136819
|
+
collectTypeIdentifiers(c2, out, source);
|
|
136820
|
+
}
|
|
136821
|
+
return out;
|
|
136822
|
+
}
|
|
136823
|
+
return [];
|
|
136824
|
+
}
|
|
136825
|
+
if (node2.type === "binary_operator") {
|
|
136826
|
+
const text = source.slice(node2.startIndex, node2.endIndex);
|
|
136827
|
+
if (text.includes("|")) {
|
|
136828
|
+
const out = [];
|
|
136829
|
+
collectTypeIdentifiers(node2, out, source);
|
|
136830
|
+
return out;
|
|
136831
|
+
}
|
|
136832
|
+
}
|
|
136833
|
+
return [];
|
|
136834
|
+
}
|
|
136835
|
+
function collectTypeIdentifiers(node2, out, source) {
|
|
136836
|
+
if (node2.type === "identifier") {
|
|
136837
|
+
out.push(source.slice(node2.startIndex, node2.endIndex));
|
|
136838
|
+
return;
|
|
136839
|
+
}
|
|
136840
|
+
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
136841
|
+
const c2 = node2.namedChild(i);
|
|
136842
|
+
if (c2)
|
|
136843
|
+
collectTypeIdentifiers(c2, out, source);
|
|
136844
|
+
}
|
|
136845
|
+
}
|
|
136846
|
+
function synthesizeConstantClusterEnums(root, filePath, source, stringTable) {
|
|
136847
|
+
const named = [];
|
|
136848
|
+
for (const [name, node2] of stringTable) {
|
|
136849
|
+
const v = stringValue(node2, source, stringTable);
|
|
136850
|
+
if (v === null)
|
|
136851
|
+
continue;
|
|
136852
|
+
named.push({ name, value: v });
|
|
136853
|
+
}
|
|
136854
|
+
if (named.length < MIN_CLUSTER_MEMBERS)
|
|
136855
|
+
return [];
|
|
136856
|
+
named.sort((a, b) => a.value < b.value ? -1 : a.value > b.value ? 1 : 0);
|
|
136857
|
+
const clusters = [];
|
|
136858
|
+
let i = 0;
|
|
136859
|
+
while (i < named.length) {
|
|
136860
|
+
let j2 = i + 1;
|
|
136861
|
+
let prefix = named[i].value;
|
|
136862
|
+
while (j2 < named.length) {
|
|
136863
|
+
const next = commonPrefix(prefix, named[j2].value);
|
|
136864
|
+
if (next.length < MIN_PREFIX)
|
|
136865
|
+
break;
|
|
136866
|
+
prefix = next;
|
|
136867
|
+
j2++;
|
|
136868
|
+
}
|
|
136869
|
+
if (j2 - i >= MIN_CLUSTER_MEMBERS && j2 - i <= MAX_CLUSTER_MEMBERS && prefix.length >= MIN_PREFIX) {
|
|
136870
|
+
clusters.push({ prefix, members: named.slice(i, j2) });
|
|
136871
|
+
}
|
|
136872
|
+
i = j2 === i + 1 ? i + 1 : j2;
|
|
136873
|
+
}
|
|
136874
|
+
const out = [];
|
|
136875
|
+
for (const cluster of clusters) {
|
|
136876
|
+
const realMembers = cluster.members.filter((m) => m.value.length > cluster.prefix.length);
|
|
136877
|
+
if (realMembers.length < MIN_CLUSTER_MEMBERS)
|
|
136878
|
+
continue;
|
|
136879
|
+
let prefix = realMembers[0].value;
|
|
136880
|
+
for (let k = 1; k < realMembers.length; k++) {
|
|
136881
|
+
prefix = commonPrefix(prefix, realMembers[k].value);
|
|
136882
|
+
if (prefix.length < MIN_PREFIX)
|
|
136883
|
+
break;
|
|
136884
|
+
}
|
|
136885
|
+
if (prefix.length < MIN_PREFIX)
|
|
136886
|
+
continue;
|
|
136887
|
+
const trailingDelim = /[/\-_.:]$/.test(prefix);
|
|
136888
|
+
const allCleanTails = trailingDelim || realMembers.every((m) => /^[A-Za-z0-9_\-./:]+$/.test(m.value.slice(prefix.length)));
|
|
136889
|
+
if (!allCleanTails)
|
|
136890
|
+
continue;
|
|
136891
|
+
const trimmedName = prefix.replace(/[^A-Za-z0-9]+$/, "") || prefix;
|
|
136892
|
+
const firstNode = stringTable.get(realMembers[0].name);
|
|
136893
|
+
const lastNode = stringTable.get(realMembers[realMembers.length - 1].name);
|
|
136894
|
+
out.push({
|
|
136895
|
+
name: trimmedName,
|
|
136896
|
+
values: [...new Set(realMembers.map((m) => m.value))].sort(),
|
|
136897
|
+
shape: "py-constant-cluster",
|
|
136898
|
+
source: {
|
|
136899
|
+
filePath,
|
|
136900
|
+
lineStart: firstNode ? firstNode.startPosition.row + 1 : 1,
|
|
136901
|
+
lineEnd: lastNode ? lastNode.endPosition.row + 1 : 1
|
|
136902
|
+
}
|
|
136903
|
+
});
|
|
136904
|
+
}
|
|
136905
|
+
return out;
|
|
136906
|
+
}
|
|
136907
|
+
function commonPrefix(a, b) {
|
|
136908
|
+
const len = Math.min(a.length, b.length);
|
|
136909
|
+
let i = 0;
|
|
136910
|
+
while (i < len && a.charCodeAt(i) === b.charCodeAt(i))
|
|
136911
|
+
i++;
|
|
136912
|
+
return a.slice(0, i);
|
|
136913
|
+
}
|
|
136914
|
+
function synthesizeInstanceRegistryEnum(root, filePath, source) {
|
|
136915
|
+
const baseOf = /* @__PURE__ */ new Map();
|
|
136916
|
+
for (let i = 0; i < root.namedChildCount; i++) {
|
|
136917
|
+
const child = root.namedChild(i);
|
|
136918
|
+
const node2 = child?.type === "class_definition" ? child : child?.type === "decorated_definition" ? child.childForFieldName("definition") : null;
|
|
136919
|
+
if (node2?.type !== "class_definition")
|
|
136920
|
+
continue;
|
|
136921
|
+
const name = textOfField(node2, "name", source);
|
|
136922
|
+
const supers = node2.childForFieldName("superclasses");
|
|
136923
|
+
if (!name || !supers)
|
|
136924
|
+
continue;
|
|
136925
|
+
const first2 = supers.namedChild(0);
|
|
136926
|
+
if (first2?.type === "identifier")
|
|
136927
|
+
baseOf.set(name, source.slice(first2.startIndex, first2.endIndex));
|
|
136928
|
+
}
|
|
136929
|
+
const subclassCount = /* @__PURE__ */ new Map();
|
|
136930
|
+
for (const base of baseOf.values())
|
|
136931
|
+
subclassCount.set(base, (subclassCount.get(base) ?? 0) + 1);
|
|
136932
|
+
const categoricalBases = new Set([...subclassCount].filter(([, n]) => n >= 2).map(([b]) => b));
|
|
136933
|
+
if (categoricalBases.size === 0)
|
|
136934
|
+
return [];
|
|
136935
|
+
const resolveBase = (cls) => {
|
|
136936
|
+
let cur = cls;
|
|
136937
|
+
for (let hops = 0; hops < 8; hops++) {
|
|
136938
|
+
if (categoricalBases.has(cur))
|
|
136939
|
+
return cur;
|
|
136940
|
+
const next = baseOf.get(cur);
|
|
136941
|
+
if (!next)
|
|
136942
|
+
return null;
|
|
136943
|
+
cur = next;
|
|
136944
|
+
}
|
|
136945
|
+
return null;
|
|
136946
|
+
};
|
|
136947
|
+
const group = /* @__PURE__ */ new Map();
|
|
136948
|
+
const memberBase = /* @__PURE__ */ new Map();
|
|
136949
|
+
const deferred2 = [];
|
|
136950
|
+
for (let i = 0; i < root.namedChildCount; i++) {
|
|
136951
|
+
const stmt = root.namedChild(i);
|
|
136952
|
+
if (stmt?.type !== "expression_statement")
|
|
136953
|
+
continue;
|
|
136954
|
+
const assign = stmt.namedChild(0);
|
|
136955
|
+
if (assign?.type !== "assignment")
|
|
136956
|
+
continue;
|
|
136957
|
+
if (assign.childForFieldName("type"))
|
|
136958
|
+
continue;
|
|
136959
|
+
const left = assign.childForFieldName("left");
|
|
136960
|
+
if (left?.type !== "identifier")
|
|
136961
|
+
continue;
|
|
136962
|
+
const name = source.slice(left.startIndex, left.endIndex);
|
|
136963
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(name))
|
|
136964
|
+
continue;
|
|
136965
|
+
const right = assign.childForFieldName("right");
|
|
136966
|
+
if (!right)
|
|
136967
|
+
continue;
|
|
136968
|
+
if (right.type === "call") {
|
|
136969
|
+
const fn = right.childForFieldName("function");
|
|
136970
|
+
if (fn?.type !== "identifier")
|
|
136971
|
+
continue;
|
|
136972
|
+
const ctor = source.slice(fn.startIndex, fn.endIndex);
|
|
136973
|
+
const base = resolveBase(ctor);
|
|
136974
|
+
if (base) {
|
|
136975
|
+
if (!group.has(base))
|
|
136976
|
+
group.set(base, []);
|
|
136977
|
+
group.get(base).push(name);
|
|
136978
|
+
memberBase.set(name, base);
|
|
136979
|
+
}
|
|
136980
|
+
} else {
|
|
136981
|
+
deferred2.push({ name, rhs: right });
|
|
136982
|
+
}
|
|
136983
|
+
}
|
|
136984
|
+
for (const { name, rhs } of deferred2) {
|
|
136985
|
+
const ids = collectIdentifiers3(rhs, source);
|
|
136986
|
+
if (ids.length === 0)
|
|
136987
|
+
continue;
|
|
136988
|
+
const bases = new Set(ids.map((id) => memberBase.get(id)).filter((b) => !!b));
|
|
136989
|
+
if (bases.size === 1) {
|
|
136990
|
+
const base = [...bases][0];
|
|
136991
|
+
group.get(base).push(name);
|
|
136992
|
+
memberBase.set(name, base);
|
|
136993
|
+
}
|
|
136994
|
+
}
|
|
136995
|
+
const out = [];
|
|
136996
|
+
for (const [base, members] of group) {
|
|
136997
|
+
if (members.length < 3)
|
|
136998
|
+
continue;
|
|
136999
|
+
out.push(mkEnum2(base, members, "py-instance-registry", root, filePath));
|
|
137000
|
+
}
|
|
137001
|
+
return out;
|
|
137002
|
+
}
|
|
137003
|
+
function collectIdentifiers3(node2, source) {
|
|
137004
|
+
const out = [];
|
|
137005
|
+
const visit = (n) => {
|
|
137006
|
+
if (n.type === "identifier") {
|
|
137007
|
+
out.push(source.slice(n.startIndex, n.endIndex));
|
|
137008
|
+
return;
|
|
137009
|
+
}
|
|
137010
|
+
for (let i = 0; i < n.namedChildCount; i++) {
|
|
137011
|
+
const c2 = n.namedChild(i);
|
|
137012
|
+
if (c2)
|
|
137013
|
+
visit(c2);
|
|
137014
|
+
}
|
|
137015
|
+
};
|
|
137016
|
+
visit(node2);
|
|
136369
137017
|
return out;
|
|
136370
137018
|
}
|
|
136371
137019
|
function extractEnumClass(node2, filePath, source) {
|
|
@@ -136442,6 +137090,22 @@ function extractAssignmentEnum(node2, filePath, source) {
|
|
|
136442
137090
|
}
|
|
136443
137091
|
return null;
|
|
136444
137092
|
}
|
|
137093
|
+
if (right.type === "set" || right.type === "list") {
|
|
137094
|
+
const attrs = attributeSetValues(right, source);
|
|
137095
|
+
if (attrs) {
|
|
137096
|
+
return mkEnum2(name, attrs, right.type === "set" ? "py-set" : "py-list", node2, filePath);
|
|
137097
|
+
}
|
|
137098
|
+
}
|
|
137099
|
+
const diff = parseSetDifference(right, source);
|
|
137100
|
+
if (diff) {
|
|
137101
|
+
return {
|
|
137102
|
+
name,
|
|
137103
|
+
values: [],
|
|
137104
|
+
shape: "py-set-difference",
|
|
137105
|
+
source: { filePath, lineStart: node2.startPosition.row + 1, lineEnd: node2.endPosition.row + 1 },
|
|
137106
|
+
unresolved: diff
|
|
137107
|
+
};
|
|
137108
|
+
}
|
|
136445
137109
|
if (!nameLooksLikeEnumConst2(name))
|
|
136446
137110
|
return null;
|
|
136447
137111
|
if (right.type === "set" || right.type === "list") {
|
|
@@ -136468,6 +137132,60 @@ function extractAssignmentEnum(node2, filePath, source) {
|
|
|
136468
137132
|
function nameLooksLikeEnumConst2(name) {
|
|
136469
137133
|
return ENUM_CONVENTION_NAME2.test(name) || ENUM_CONVENTION_SUFFIX2.test(name);
|
|
136470
137134
|
}
|
|
137135
|
+
function attributeSetValues(node2, source) {
|
|
137136
|
+
const out = [];
|
|
137137
|
+
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
137138
|
+
const c2 = node2.namedChild(i);
|
|
137139
|
+
if (!c2)
|
|
137140
|
+
continue;
|
|
137141
|
+
if (c2.type !== "attribute")
|
|
137142
|
+
return null;
|
|
137143
|
+
const attr = c2.childForFieldName("attribute");
|
|
137144
|
+
if (!attr)
|
|
137145
|
+
return null;
|
|
137146
|
+
out.push(source.slice(attr.startIndex, attr.endIndex));
|
|
137147
|
+
}
|
|
137148
|
+
return out.length >= 2 ? out : null;
|
|
137149
|
+
}
|
|
137150
|
+
function parseSetDifference(node2, source) {
|
|
137151
|
+
let n = node2;
|
|
137152
|
+
if (n.type === "call") {
|
|
137153
|
+
const fn = n.childForFieldName("function");
|
|
137154
|
+
const fnName = fn ? source.slice(fn.startIndex, fn.endIndex) : "";
|
|
137155
|
+
if (/^(list|set|frozenset|tuple)$/.test(fnName)) {
|
|
137156
|
+
const inner = n.childForFieldName("arguments")?.namedChild(0);
|
|
137157
|
+
if (inner)
|
|
137158
|
+
n = inner;
|
|
137159
|
+
}
|
|
137160
|
+
}
|
|
137161
|
+
if (n.type !== "binary_operator")
|
|
137162
|
+
return null;
|
|
137163
|
+
if (!source.slice(n.startIndex, n.endIndex).includes("-"))
|
|
137164
|
+
return null;
|
|
137165
|
+
const left = n.childForFieldName("left");
|
|
137166
|
+
const right = n.childForFieldName("right");
|
|
137167
|
+
if (!left || !right)
|
|
137168
|
+
return null;
|
|
137169
|
+
const base = enumOperandName(left, source);
|
|
137170
|
+
const minus = enumOperandName(right, source);
|
|
137171
|
+
return base && minus ? { base, minus } : null;
|
|
137172
|
+
}
|
|
137173
|
+
function enumOperandName(node2, source) {
|
|
137174
|
+
let n = node2;
|
|
137175
|
+
if (n.type === "call") {
|
|
137176
|
+
const inner = n.childForFieldName("arguments")?.namedChild(0);
|
|
137177
|
+
if (inner)
|
|
137178
|
+
n = inner;
|
|
137179
|
+
}
|
|
137180
|
+
if (n.type === "identifier")
|
|
137181
|
+
return source.slice(n.startIndex, n.endIndex);
|
|
137182
|
+
if (n.type === "attribute") {
|
|
137183
|
+
const a = n.childForFieldName("attribute");
|
|
137184
|
+
if (a)
|
|
137185
|
+
return source.slice(a.startIndex, a.endIndex);
|
|
137186
|
+
}
|
|
137187
|
+
return null;
|
|
137188
|
+
}
|
|
136471
137189
|
function collectStringChildren(node2, source) {
|
|
136472
137190
|
const out = [];
|
|
136473
137191
|
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
@@ -136480,24 +137198,6 @@ function collectStringChildren(node2, source) {
|
|
|
136480
137198
|
}
|
|
136481
137199
|
return out;
|
|
136482
137200
|
}
|
|
136483
|
-
function stringValue(node2, source) {
|
|
136484
|
-
let content = "";
|
|
136485
|
-
let sawContent = false;
|
|
136486
|
-
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
136487
|
-
const c2 = node2.namedChild(i);
|
|
136488
|
-
if (c2?.type === "string_content") {
|
|
136489
|
-
content += source.slice(c2.startIndex, c2.endIndex);
|
|
136490
|
-
sawContent = true;
|
|
136491
|
-
} else if (c2?.type === "interpolation" || c2?.type === "format_specifier") {
|
|
136492
|
-
return null;
|
|
136493
|
-
}
|
|
136494
|
-
}
|
|
136495
|
-
if (sawContent)
|
|
136496
|
-
return content;
|
|
136497
|
-
const raw = source.slice(node2.startIndex, node2.endIndex);
|
|
136498
|
-
const m = raw.match(/^[a-zA-Z]*('''|"""|'|")([\s\S]*)\1$/);
|
|
136499
|
-
return m ? m[2] : null;
|
|
136500
|
-
}
|
|
136501
137201
|
function textOfField(node2, field, source) {
|
|
136502
137202
|
const c2 = node2.childForFieldName(field);
|
|
136503
137203
|
return c2 ? source.slice(c2.startIndex, c2.endIndex) : "";
|
|
@@ -136520,12 +137220,16 @@ function walk7(node2, visit) {
|
|
|
136520
137220
|
walk7(c2, visit);
|
|
136521
137221
|
}
|
|
136522
137222
|
}
|
|
136523
|
-
var ENUM_CONVENTION_NAME2, ENUM_CONVENTION_SUFFIX2;
|
|
137223
|
+
var ENUM_CONVENTION_NAME2, ENUM_CONVENTION_SUFFIX2, MIN_PREFIX, MIN_CLUSTER_MEMBERS, MAX_CLUSTER_MEMBERS;
|
|
136524
137224
|
var init_py_enums = __esm({
|
|
136525
137225
|
"packages/contract-verifier/dist/extractor/enum/py-enums.js"() {
|
|
136526
137226
|
"use strict";
|
|
137227
|
+
init_py_string_resolver();
|
|
136527
137228
|
ENUM_CONVENTION_NAME2 = /^(?:VALID|ALLOWED|KNOWN|ENUM)_/i;
|
|
136528
137229
|
ENUM_CONVENTION_SUFFIX2 = /_(?:VALUES|SET|CLASSIFICATIONS|STATUSES|KINDS|TYPES|OPTIONS|CHOICES)$/i;
|
|
137230
|
+
MIN_PREFIX = 6;
|
|
137231
|
+
MIN_CLUSTER_MEMBERS = 3;
|
|
137232
|
+
MAX_CLUSTER_MEMBERS = 200;
|
|
136529
137233
|
}
|
|
136530
137234
|
});
|
|
136531
137235
|
|
|
@@ -136559,8 +137263,27 @@ async function extractEnumsFromDir(rootDir) {
|
|
|
136559
137263
|
source: { filePath: entries[0].filePath, lineStart: 1, lineEnd: 1 }
|
|
136560
137264
|
});
|
|
136561
137265
|
}
|
|
136562
|
-
const
|
|
137266
|
+
const norm = (s) => s.replace(/[^A-Za-z0-9]/g, "").toLowerCase();
|
|
137267
|
+
const valuesByName = /* @__PURE__ */ new Map();
|
|
137268
|
+
for (const e of raw) {
|
|
137269
|
+
if (e.shape !== "py-set-difference")
|
|
137270
|
+
valuesByName.set(norm(e.name), e.values);
|
|
137271
|
+
}
|
|
136563
137272
|
for (const e of raw) {
|
|
137273
|
+
if (e.shape !== "py-set-difference" || !e.unresolved)
|
|
137274
|
+
continue;
|
|
137275
|
+
const baseVals = valuesByName.get(norm(e.unresolved.base));
|
|
137276
|
+
const minusVals = valuesByName.get(norm(e.unresolved.minus));
|
|
137277
|
+
if (baseVals && minusVals) {
|
|
137278
|
+
const minusSet = new Set(minusVals.map((v) => v.replace(/[^A-Za-z0-9]/g, "").toLowerCase()));
|
|
137279
|
+
e.values = [...new Set(baseVals.filter((v) => !minusSet.has(v.replace(/[^A-Za-z0-9]/g, "").toLowerCase())))].sort();
|
|
137280
|
+
e.shape = "py-set";
|
|
137281
|
+
delete e.unresolved;
|
|
137282
|
+
}
|
|
137283
|
+
}
|
|
137284
|
+
const resolved = raw.filter((e) => e.shape !== "py-set-difference");
|
|
137285
|
+
const seen = /* @__PURE__ */ new Map();
|
|
137286
|
+
for (const e of resolved) {
|
|
136564
137287
|
const key2 = `${e.name}|${e.values.join(",")}`;
|
|
136565
137288
|
if (!seen.has(key2))
|
|
136566
137289
|
seen.set(key2, e);
|
|
@@ -137254,6 +137977,13 @@ var init_ts_constants = __esm({
|
|
|
137254
137977
|
// packages/contract-verifier/dist/extractor/constant/py-constants.js
|
|
137255
137978
|
function extractPyConstantsFromFile(filePath, source, tree) {
|
|
137256
137979
|
const out = [];
|
|
137980
|
+
const stringTable = collectStringConstantTable(tree.rootNode, source);
|
|
137981
|
+
walk10(tree.rootNode, (node2) => {
|
|
137982
|
+
if (node2.type === "class_definition") {
|
|
137983
|
+
out.push(...extractSettingsFields(node2, source, filePath, stringTable));
|
|
137984
|
+
}
|
|
137985
|
+
return true;
|
|
137986
|
+
});
|
|
137257
137987
|
walk10(tree.rootNode, (node2) => {
|
|
137258
137988
|
if (node2.type !== "assignment")
|
|
137259
137989
|
return true;
|
|
@@ -137263,23 +137993,169 @@ function extractPyConstantsFromFile(filePath, source, tree) {
|
|
|
137263
137993
|
const right = node2.childForFieldName("right");
|
|
137264
137994
|
if (!right)
|
|
137265
137995
|
return true;
|
|
137266
|
-
const
|
|
137267
|
-
|
|
137996
|
+
const name = source.slice(left.startIndex, left.endIndex);
|
|
137997
|
+
const pos = { filePath, lineStart: node2.startPosition.row + 1, lineEnd: node2.endPosition.row + 1 };
|
|
137998
|
+
const value = parseLiteral(right, source, stringTable);
|
|
137999
|
+
if (value !== UNPARSEABLE2) {
|
|
138000
|
+
out.push({ name, value, shape: "const-literal", source: pos });
|
|
138001
|
+
return true;
|
|
138002
|
+
}
|
|
138003
|
+
if (!node2.childForFieldName("type"))
|
|
138004
|
+
return true;
|
|
138005
|
+
if (right.type !== "call")
|
|
138006
|
+
return true;
|
|
138007
|
+
const fn = right.childForFieldName("function");
|
|
138008
|
+
if (!fn)
|
|
138009
|
+
return true;
|
|
138010
|
+
if (!source.slice(fn.startIndex, fn.endIndex).endsWith("Field"))
|
|
138011
|
+
return true;
|
|
138012
|
+
const callArgs = right.childForFieldName("arguments");
|
|
138013
|
+
if (!callArgs)
|
|
138014
|
+
return true;
|
|
138015
|
+
let defaultVal = UNPARSEABLE2;
|
|
138016
|
+
const aliasStrings = [];
|
|
138017
|
+
for (let i = 0; i < callArgs.namedChildCount; i++) {
|
|
138018
|
+
const arg = callArgs.namedChild(i);
|
|
138019
|
+
if (arg?.type !== "keyword_argument")
|
|
138020
|
+
continue;
|
|
138021
|
+
const kwName = arg.childForFieldName("name");
|
|
138022
|
+
const kwVal = arg.childForFieldName("value");
|
|
138023
|
+
if (!kwName || !kwVal)
|
|
138024
|
+
continue;
|
|
138025
|
+
const kw = source.slice(kwName.startIndex, kwName.endIndex);
|
|
138026
|
+
if (kw === "default" && defaultVal === UNPARSEABLE2) {
|
|
138027
|
+
defaultVal = parseLiteral(kwVal, source, stringTable);
|
|
138028
|
+
} else if (kw === "validation_alias") {
|
|
138029
|
+
aliasStrings.push(...extractAliasChoiceStrings(kwVal, source, stringTable));
|
|
138030
|
+
}
|
|
138031
|
+
}
|
|
138032
|
+
if (defaultVal === UNPARSEABLE2)
|
|
137268
138033
|
return true;
|
|
138034
|
+
out.push({ name, value: defaultVal, shape: "const-literal", source: pos });
|
|
138035
|
+
for (const alias of aliasStrings) {
|
|
138036
|
+
out.push({ name: alias, value: defaultVal, shape: "const-literal", source: pos });
|
|
138037
|
+
}
|
|
138038
|
+
return true;
|
|
138039
|
+
});
|
|
138040
|
+
return out;
|
|
138041
|
+
}
|
|
138042
|
+
function extractSettingsFields(classNode, source, filePath, stringTable) {
|
|
138043
|
+
const body = classNode.childForFieldName("body");
|
|
138044
|
+
if (!body)
|
|
138045
|
+
return [];
|
|
138046
|
+
const scope = deriveSettingsScope(body, source, stringTable);
|
|
138047
|
+
if (scope === null)
|
|
138048
|
+
return [];
|
|
138049
|
+
const out = [];
|
|
138050
|
+
for (let i = 0; i < body.namedChildCount; i++) {
|
|
138051
|
+
const stmt = body.namedChild(i);
|
|
138052
|
+
if (stmt?.type !== "expression_statement")
|
|
138053
|
+
continue;
|
|
138054
|
+
const assign = stmt.namedChild(0);
|
|
138055
|
+
if (assign?.type !== "assignment")
|
|
138056
|
+
continue;
|
|
138057
|
+
if (!assign.childForFieldName("type"))
|
|
138058
|
+
continue;
|
|
138059
|
+
const left = assign.childForFieldName("left");
|
|
138060
|
+
if (left?.type !== "identifier")
|
|
138061
|
+
continue;
|
|
138062
|
+
const fieldName = source.slice(left.startIndex, left.endIndex);
|
|
138063
|
+
if (fieldName === "model_config")
|
|
138064
|
+
continue;
|
|
138065
|
+
const right = assign.childForFieldName("right");
|
|
138066
|
+
if (!right)
|
|
138067
|
+
continue;
|
|
138068
|
+
const value = fieldDefaultValue(right, source, stringTable);
|
|
138069
|
+
if (value === UNPARSEABLE2)
|
|
138070
|
+
continue;
|
|
138071
|
+
const name = `${scope}_${fieldName}`.toUpperCase();
|
|
137269
138072
|
out.push({
|
|
137270
|
-
name
|
|
138073
|
+
name,
|
|
137271
138074
|
value,
|
|
137272
|
-
shape: "
|
|
137273
|
-
source: { filePath, lineStart:
|
|
138075
|
+
shape: "settings-field",
|
|
138076
|
+
source: { filePath, lineStart: assign.startPosition.row + 1, lineEnd: assign.endPosition.row + 1 }
|
|
137274
138077
|
});
|
|
137275
|
-
|
|
137276
|
-
});
|
|
138078
|
+
}
|
|
137277
138079
|
return out;
|
|
137278
138080
|
}
|
|
137279
|
-
function
|
|
138081
|
+
function deriveSettingsScope(body, source, stringTable) {
|
|
138082
|
+
for (let i = 0; i < body.namedChildCount; i++) {
|
|
138083
|
+
const stmt = body.namedChild(i);
|
|
138084
|
+
if (stmt?.type !== "expression_statement")
|
|
138085
|
+
continue;
|
|
138086
|
+
const assign = stmt.namedChild(0);
|
|
138087
|
+
if (assign?.type !== "assignment")
|
|
138088
|
+
continue;
|
|
138089
|
+
const left = assign.childForFieldName("left");
|
|
138090
|
+
if (left?.type !== "identifier")
|
|
138091
|
+
continue;
|
|
138092
|
+
if (source.slice(left.startIndex, left.endIndex) !== "model_config")
|
|
138093
|
+
continue;
|
|
138094
|
+
const right = assign.childForFieldName("right");
|
|
138095
|
+
if (right?.type !== "call")
|
|
138096
|
+
continue;
|
|
138097
|
+
const args = right.childForFieldName("arguments");
|
|
138098
|
+
if (!args)
|
|
138099
|
+
continue;
|
|
138100
|
+
for (let j2 = 0; j2 < args.namedChildCount; j2++) {
|
|
138101
|
+
const arg = args.namedChild(j2);
|
|
138102
|
+
if (arg?.type !== "keyword_argument")
|
|
138103
|
+
continue;
|
|
138104
|
+
const kw = arg.childForFieldName("name");
|
|
138105
|
+
const val = arg.childForFieldName("value");
|
|
138106
|
+
if (kw && val && source.slice(kw.startIndex, kw.endIndex) === "env_prefix" && val.type === "string") {
|
|
138107
|
+
const prefix = stringValue(val, source, stringTable);
|
|
138108
|
+
if (prefix)
|
|
138109
|
+
return prefix.replace(/_+$/, "").toLowerCase();
|
|
138110
|
+
}
|
|
138111
|
+
}
|
|
138112
|
+
for (let j2 = 0; j2 < args.namedChildCount; j2++) {
|
|
138113
|
+
const arg = args.namedChild(j2);
|
|
138114
|
+
if (arg?.type !== "tuple")
|
|
138115
|
+
continue;
|
|
138116
|
+
const parts = [];
|
|
138117
|
+
for (let k = 0; k < arg.namedChildCount; k++) {
|
|
138118
|
+
const el = arg.namedChild(k);
|
|
138119
|
+
if (el?.type === "string") {
|
|
138120
|
+
const v = stringValue(el, source, stringTable);
|
|
138121
|
+
if (v)
|
|
138122
|
+
parts.push(v);
|
|
138123
|
+
}
|
|
138124
|
+
}
|
|
138125
|
+
if (parts.length > 0)
|
|
138126
|
+
return parts.join("_").toLowerCase();
|
|
138127
|
+
}
|
|
138128
|
+
}
|
|
138129
|
+
return null;
|
|
138130
|
+
}
|
|
138131
|
+
function fieldDefaultValue(right, source, stringTable) {
|
|
138132
|
+
const direct = parseLiteral(right, source, stringTable);
|
|
138133
|
+
if (direct !== UNPARSEABLE2)
|
|
138134
|
+
return direct;
|
|
138135
|
+
if (right.type !== "call")
|
|
138136
|
+
return UNPARSEABLE2;
|
|
138137
|
+
const fn = right.childForFieldName("function");
|
|
138138
|
+
if (!fn || !source.slice(fn.startIndex, fn.endIndex).endsWith("Field"))
|
|
138139
|
+
return UNPARSEABLE2;
|
|
138140
|
+
const args = right.childForFieldName("arguments");
|
|
138141
|
+
if (!args)
|
|
138142
|
+
return UNPARSEABLE2;
|
|
138143
|
+
for (let i = 0; i < args.namedChildCount; i++) {
|
|
138144
|
+
const arg = args.namedChild(i);
|
|
138145
|
+
if (arg?.type !== "keyword_argument")
|
|
138146
|
+
continue;
|
|
138147
|
+
const kw = arg.childForFieldName("name");
|
|
138148
|
+
const val = arg.childForFieldName("value");
|
|
138149
|
+
if (kw && val && source.slice(kw.startIndex, kw.endIndex) === "default") {
|
|
138150
|
+
return parseLiteral(val, source, stringTable);
|
|
138151
|
+
}
|
|
138152
|
+
}
|
|
138153
|
+
return UNPARSEABLE2;
|
|
138154
|
+
}
|
|
138155
|
+
function parseLiteral(node2, source, stringTable) {
|
|
137280
138156
|
switch (node2.type) {
|
|
137281
138157
|
case "string": {
|
|
137282
|
-
const v =
|
|
138158
|
+
const v = stringValue(node2, source, stringTable);
|
|
137283
138159
|
return v === null ? UNPARSEABLE2 : v;
|
|
137284
138160
|
}
|
|
137285
138161
|
case "integer":
|
|
@@ -137303,7 +138179,7 @@ function parseLiteral(node2, source) {
|
|
|
137303
138179
|
const c2 = node2.namedChild(i);
|
|
137304
138180
|
if (!c2)
|
|
137305
138181
|
continue;
|
|
137306
|
-
const v = parseLiteral(c2, source);
|
|
138182
|
+
const v = parseLiteral(c2, source, stringTable);
|
|
137307
138183
|
if (v === UNPARSEABLE2)
|
|
137308
138184
|
return UNPARSEABLE2;
|
|
137309
138185
|
items.push(v);
|
|
@@ -137322,12 +138198,12 @@ function parseLiteral(node2, source) {
|
|
|
137322
138198
|
continue;
|
|
137323
138199
|
let key2 = null;
|
|
137324
138200
|
if (keyNode.type === "string")
|
|
137325
|
-
key2 =
|
|
138201
|
+
key2 = stringValue(keyNode, source, stringTable);
|
|
137326
138202
|
else if (keyNode.type === "identifier")
|
|
137327
138203
|
key2 = source.slice(keyNode.startIndex, keyNode.endIndex);
|
|
137328
138204
|
if (key2 === null)
|
|
137329
138205
|
return UNPARSEABLE2;
|
|
137330
|
-
const v = parseLiteral(valNode, source);
|
|
138206
|
+
const v = parseLiteral(valNode, source, stringTable);
|
|
137331
138207
|
if (v === UNPARSEABLE2)
|
|
137332
138208
|
return UNPARSEABLE2;
|
|
137333
138209
|
obj[key2] = v;
|
|
@@ -137338,23 +138214,28 @@ function parseLiteral(node2, source) {
|
|
|
137338
138214
|
return UNPARSEABLE2;
|
|
137339
138215
|
}
|
|
137340
138216
|
}
|
|
137341
|
-
function
|
|
137342
|
-
|
|
137343
|
-
|
|
137344
|
-
|
|
137345
|
-
|
|
137346
|
-
|
|
137347
|
-
|
|
137348
|
-
|
|
137349
|
-
|
|
137350
|
-
|
|
138217
|
+
function extractAliasChoiceStrings(node2, source, stringTable) {
|
|
138218
|
+
if (node2.type !== "call")
|
|
138219
|
+
return [];
|
|
138220
|
+
const fn = node2.childForFieldName("function");
|
|
138221
|
+
if (!fn)
|
|
138222
|
+
return [];
|
|
138223
|
+
const fnText = source.slice(fn.startIndex, fn.endIndex);
|
|
138224
|
+
if (!fnText.endsWith("AliasChoices"))
|
|
138225
|
+
return [];
|
|
138226
|
+
const args = node2.childForFieldName("arguments");
|
|
138227
|
+
if (!args)
|
|
138228
|
+
return [];
|
|
138229
|
+
const result = [];
|
|
138230
|
+
for (let i = 0; i < args.namedChildCount; i++) {
|
|
138231
|
+
const c2 = args.namedChild(i);
|
|
138232
|
+
if (c2?.type === "string") {
|
|
138233
|
+
const v = stringValue(c2, source, stringTable);
|
|
138234
|
+
if (v !== null)
|
|
138235
|
+
result.push(v);
|
|
137351
138236
|
}
|
|
137352
138237
|
}
|
|
137353
|
-
|
|
137354
|
-
return content;
|
|
137355
|
-
const raw = source.slice(node2.startIndex, node2.endIndex);
|
|
137356
|
-
const m = raw.match(/^[a-zA-Z]*('''|"""|'|")([\s\S]*)\1$/);
|
|
137357
|
-
return m ? m[2] : null;
|
|
138238
|
+
return result;
|
|
137358
138239
|
}
|
|
137359
138240
|
function walk10(node2, visit) {
|
|
137360
138241
|
const recurse = visit(node2);
|
|
@@ -137370,6 +138251,7 @@ var UNPARSEABLE2;
|
|
|
137370
138251
|
var init_py_constants = __esm({
|
|
137371
138252
|
"packages/contract-verifier/dist/extractor/constant/py-constants.js"() {
|
|
137372
138253
|
"use strict";
|
|
138254
|
+
init_py_string_resolver();
|
|
137373
138255
|
UNPARSEABLE2 = Symbol("unparseable");
|
|
137374
138256
|
}
|
|
137375
138257
|
});
|
|
@@ -137855,7 +138737,7 @@ function readTransitionMap(node2, s) {
|
|
|
137855
138737
|
for (const elem of v.namedChildren) {
|
|
137856
138738
|
if (elem.type !== "string")
|
|
137857
138739
|
return null;
|
|
137858
|
-
const text =
|
|
138740
|
+
const text = stringValue2(elem, s);
|
|
137859
138741
|
if (text === null)
|
|
137860
138742
|
return null;
|
|
137861
138743
|
tos.push(text);
|
|
@@ -137894,7 +138776,7 @@ function readFieldAssignment(node2, s) {
|
|
|
137894
138776
|
return null;
|
|
137895
138777
|
if (rhs.type !== "string")
|
|
137896
138778
|
return null;
|
|
137897
|
-
const value =
|
|
138779
|
+
const value = stringValue2(rhs, s);
|
|
137898
138780
|
if (value === null)
|
|
137899
138781
|
return null;
|
|
137900
138782
|
return {
|
|
@@ -138001,13 +138883,13 @@ function matchPair(member, literal, s) {
|
|
|
138001
138883
|
return [{
|
|
138002
138884
|
receiver: s.source.slice(obj.startIndex, obj.endIndex),
|
|
138003
138885
|
field: s.source.slice(prop.startIndex, prop.endIndex),
|
|
138004
|
-
value:
|
|
138886
|
+
value: stringValue2(literal, s) ?? ""
|
|
138005
138887
|
}];
|
|
138006
138888
|
}
|
|
138007
138889
|
function stripQuotes(str2) {
|
|
138008
138890
|
return str2.replace(/^['"]|['"]$/g, "");
|
|
138009
138891
|
}
|
|
138010
|
-
function
|
|
138892
|
+
function stringValue2(node2, s) {
|
|
138011
138893
|
if (node2.type !== "string")
|
|
138012
138894
|
return null;
|
|
138013
138895
|
const childType = s.lang === "python" ? "string_content" : "string_fragment";
|
|
@@ -139170,7 +140052,11 @@ async function extractOperationsFromDir(rootDir) {
|
|
|
139170
140052
|
await eachParsedSource(rootDir, (s) => {
|
|
139171
140053
|
if (s.lang !== "python")
|
|
139172
140054
|
return;
|
|
139173
|
-
|
|
140055
|
+
const pythonOps = [
|
|
140056
|
+
...extractFastApiOperationsFromFile(s.filePath, s.source, s.tree),
|
|
140057
|
+
...extractStarletteRoutesFromFile(s.filePath, s.source, s.tree)
|
|
140058
|
+
];
|
|
140059
|
+
for (const op of pythonOps) {
|
|
139174
140060
|
if (!seen.has(op.identity)) {
|
|
139175
140061
|
expressOps.push(op);
|
|
139176
140062
|
seen.add(op.identity);
|
|
@@ -140402,7 +141288,9 @@ import { randomUUID as randomUUID19 } from "node:crypto";
|
|
|
140402
141288
|
function compareEnum(input) {
|
|
140403
141289
|
const { ref, contract, codeEnums } = input;
|
|
140404
141290
|
const drifts = [];
|
|
140405
|
-
const
|
|
141291
|
+
const matched = matchByName(contract, codeEnums, ref.identity);
|
|
141292
|
+
const valueOnlyMatch = matched.byName.length === 0;
|
|
141293
|
+
const nameMatches = matched.byName.length > 0 ? matched.byName : bestValueMatch(contract, matched.byValue);
|
|
140406
141294
|
if (nameMatches.length === 0) {
|
|
140407
141295
|
drifts.push({
|
|
140408
141296
|
id: randomUUID19(),
|
|
@@ -140418,21 +141306,37 @@ function compareEnum(input) {
|
|
|
140418
141306
|
codeSide: "<no match>"
|
|
140419
141307
|
});
|
|
140420
141308
|
} else {
|
|
141309
|
+
const specNorm = new Map(contract.values.map((v) => [normalizeValue(v), v]));
|
|
141310
|
+
const cloudOnlyValues = new Set((contract.triggerSubsets ?? []).filter((s) => isEnvironmentGatedSubset(s.name)).flatMap((s) => s.values.map(normalizeValue)));
|
|
141311
|
+
const byName = /* @__PURE__ */ new Map();
|
|
140421
141312
|
for (const m of nameMatches) {
|
|
140422
|
-
const
|
|
140423
|
-
|
|
140424
|
-
|
|
140425
|
-
const
|
|
141313
|
+
const key2 = normalizeName(m.name);
|
|
141314
|
+
if (!byName.has(key2))
|
|
141315
|
+
byName.set(key2, { values: /* @__PURE__ */ new Set(), repr: m });
|
|
141316
|
+
const g = byName.get(key2);
|
|
141317
|
+
for (const v of m.values)
|
|
141318
|
+
g.values.add(normalizeValue(v));
|
|
141319
|
+
}
|
|
141320
|
+
const specNormSet = new Set(contract.values.map(normalizeValue));
|
|
141321
|
+
for (const g of byName.values()) {
|
|
141322
|
+
if (valueOnlyMatch && isStrictSubset(g.values, specNormSet))
|
|
141323
|
+
continue;
|
|
141324
|
+
const missing = contract.values.filter((v) => !g.values.has(normalizeValue(v)) && !cloudOnlyValues.has(normalizeValue(v)));
|
|
140426
141325
|
for (const v of missing) {
|
|
140427
|
-
drifts.push(mkValueDrift(ref, "missing-value", v,
|
|
141326
|
+
drifts.push(mkValueDrift(ref, "missing-value", v, g.repr, contract.values));
|
|
140428
141327
|
}
|
|
141328
|
+
}
|
|
141329
|
+
for (const m of nameMatches) {
|
|
141330
|
+
if (isSynthesizedEnumShape(m.shape))
|
|
141331
|
+
continue;
|
|
141332
|
+
const extra = m.values.filter((v) => !specNorm.has(normalizeValue(v)));
|
|
140429
141333
|
for (const v of extra) {
|
|
140430
141334
|
drifts.push(mkValueDrift(ref, "extra-value", v, m, contract.values));
|
|
140431
141335
|
}
|
|
140432
141336
|
}
|
|
140433
141337
|
}
|
|
140434
141338
|
for (const subset of contract.triggerSubsets ?? []) {
|
|
140435
|
-
const subsetMatches = matchSubsetByName(subset.name, codeEnums);
|
|
141339
|
+
const subsetMatches = matchSubsetByName(subset.name, subset.values, codeEnums);
|
|
140436
141340
|
if (subsetMatches.length === 0) {
|
|
140437
141341
|
drifts.push({
|
|
140438
141342
|
id: randomUUID19(),
|
|
@@ -140473,13 +141377,17 @@ function normalizeName(s) {
|
|
|
140473
141377
|
}
|
|
140474
141378
|
return n;
|
|
140475
141379
|
}
|
|
141380
|
+
function isEnvironmentGatedSubset(name) {
|
|
141381
|
+
return normalizeName(name) === "cloudonly";
|
|
141382
|
+
}
|
|
140476
141383
|
function matchByName(contract, codeEnums, specName) {
|
|
140477
141384
|
const target = normalizeName(specName);
|
|
140478
|
-
const
|
|
141385
|
+
const byName = [];
|
|
141386
|
+
const byValue = [];
|
|
140479
141387
|
for (const e of codeEnums) {
|
|
140480
141388
|
const codeName = normalizeName(e.name);
|
|
140481
141389
|
if (codeName === target) {
|
|
140482
|
-
|
|
141390
|
+
byName.push(e);
|
|
140483
141391
|
continue;
|
|
140484
141392
|
}
|
|
140485
141393
|
if (codeName.includes(target) || target.includes(codeName)) {
|
|
@@ -140490,7 +141398,7 @@ function matchByName(contract, codeEnums, specName) {
|
|
|
140490
141398
|
}
|
|
140491
141399
|
}
|
|
140492
141400
|
if (valueSetOverlap(contract.values, e.values) >= 0.5) {
|
|
140493
|
-
|
|
141401
|
+
byName.push(e);
|
|
140494
141402
|
continue;
|
|
140495
141403
|
}
|
|
140496
141404
|
}
|
|
@@ -140499,21 +141407,40 @@ function matchByName(contract, codeEnums, specName) {
|
|
|
140499
141407
|
const overlap = valueSetOverlap(contract.values, e.values);
|
|
140500
141408
|
const sizeDiff = Math.abs(contract.values.length - e.values.length);
|
|
140501
141409
|
if (overlap >= 0.6 && sizeDiff <= 2) {
|
|
140502
|
-
|
|
141410
|
+
byValue.push(e);
|
|
140503
141411
|
}
|
|
140504
141412
|
} else if (minLen >= 2) {
|
|
140505
141413
|
const overlap = valueSetOverlap(contract.values, e.values);
|
|
140506
141414
|
const sizeDiff = Math.abs(contract.values.length - e.values.length);
|
|
140507
141415
|
if (overlap === 1 && sizeDiff === 0) {
|
|
140508
|
-
|
|
141416
|
+
byValue.push(e);
|
|
140509
141417
|
}
|
|
140510
141418
|
}
|
|
140511
141419
|
}
|
|
140512
|
-
return
|
|
141420
|
+
return { byName, byValue };
|
|
141421
|
+
}
|
|
141422
|
+
function bestValueMatch(contract, byValue) {
|
|
141423
|
+
if (byValue.length <= 1)
|
|
141424
|
+
return byValue;
|
|
141425
|
+
let best = -1;
|
|
141426
|
+
for (const e of byValue)
|
|
141427
|
+
best = Math.max(best, valueSetOverlap(contract.values, e.values));
|
|
141428
|
+
return byValue.filter((e) => valueSetOverlap(contract.values, e.values) === best);
|
|
141429
|
+
}
|
|
141430
|
+
function isSynthesizedEnumShape(shape) {
|
|
141431
|
+
return shape === "sibling-id-literal" || shape === "py-instance-registry" || shape === "py-discriminated-union" || shape === "py-constant-cluster";
|
|
140513
141432
|
}
|
|
140514
141433
|
function normalizeValue(v) {
|
|
140515
141434
|
return v.replace(/[^A-Za-z0-9]/g, "").toLowerCase();
|
|
140516
141435
|
}
|
|
141436
|
+
function isStrictSubset(codeValues, specNorm) {
|
|
141437
|
+
if (codeValues.size === 0 || codeValues.size >= specNorm.size)
|
|
141438
|
+
return false;
|
|
141439
|
+
for (const v of codeValues)
|
|
141440
|
+
if (!specNorm.has(v))
|
|
141441
|
+
return false;
|
|
141442
|
+
return true;
|
|
141443
|
+
}
|
|
140517
141444
|
function valueSetOverlap(a, b) {
|
|
140518
141445
|
const aSet = new Set(a.map(normalizeValue));
|
|
140519
141446
|
const bSet = new Set(b.map(normalizeValue));
|
|
@@ -140521,9 +141448,21 @@ function valueSetOverlap(a, b) {
|
|
|
140521
141448
|
const union = (/* @__PURE__ */ new Set([...aSet, ...bSet])).size;
|
|
140522
141449
|
return union === 0 ? 0 : intersection / union;
|
|
140523
141450
|
}
|
|
140524
|
-
function matchSubsetByName(subsetName, codeEnums) {
|
|
141451
|
+
function matchSubsetByName(subsetName, subsetValues, codeEnums) {
|
|
140525
141452
|
const target = normalizeName(subsetName);
|
|
140526
|
-
|
|
141453
|
+
const out = [];
|
|
141454
|
+
for (const e of codeEnums) {
|
|
141455
|
+
const codeName = normalizeName(e.name);
|
|
141456
|
+
if (codeName === target) {
|
|
141457
|
+
out.push(e);
|
|
141458
|
+
continue;
|
|
141459
|
+
}
|
|
141460
|
+
if (codeName.includes(target) || target.includes(codeName)) {
|
|
141461
|
+
if (valueSetOverlap(subsetValues, e.values) >= 0.5)
|
|
141462
|
+
out.push(e);
|
|
141463
|
+
}
|
|
141464
|
+
}
|
|
141465
|
+
return out;
|
|
140527
141466
|
}
|
|
140528
141467
|
function mkValueDrift(ref, kind, value, codeEnum, specValues) {
|
|
140529
141468
|
const severity = kind === "missing-value" ? "high" : "medium";
|
|
@@ -140648,6 +141587,21 @@ function compareNamedConstant(input) {
|
|
|
140648
141587
|
return false;
|
|
140649
141588
|
});
|
|
140650
141589
|
}
|
|
141590
|
+
if (matches.length === 0) {
|
|
141591
|
+
matches = codeConstants.filter((c2) => {
|
|
141592
|
+
if (c2.shape !== "settings-field")
|
|
141593
|
+
return false;
|
|
141594
|
+
const codeName = normalizeName2(c2.name);
|
|
141595
|
+
if (codeName.length < 8 || !target.endsWith(codeName))
|
|
141596
|
+
return false;
|
|
141597
|
+
return contract.expectedValue === void 0 || deepEqual(
|
|
141598
|
+
contract.expectedValue,
|
|
141599
|
+
c2.value,
|
|
141600
|
+
/*allowExtraCodeKeys*/
|
|
141601
|
+
true
|
|
141602
|
+
);
|
|
141603
|
+
});
|
|
141604
|
+
}
|
|
140651
141605
|
if (matches.length === 0) {
|
|
140652
141606
|
return [{
|
|
140653
141607
|
id: randomUUID21(),
|
|
@@ -140891,6 +141845,8 @@ async function verify(opts) {
|
|
|
140891
141845
|
if (!code2) {
|
|
140892
141846
|
if (st2 === "planned" || st2 === "deferred" || st2 === "out-of-scope")
|
|
140893
141847
|
continue;
|
|
141848
|
+
if (/^[A-Z]+ https?:\/\//.test(artifact.ref.identity))
|
|
141849
|
+
continue;
|
|
140894
141850
|
drifts.push({
|
|
140895
141851
|
id: cryptoRandomId(),
|
|
140896
141852
|
type: "contract-drift",
|
|
@@ -142199,7 +143155,7 @@ function stripCodeFences(text) {
|
|
|
142199
143155
|
}
|
|
142200
143156
|
function cliTransport(opts = {}) {
|
|
142201
143157
|
const bin = opts.bin ?? resolveClaudeBinary();
|
|
142202
|
-
return (req) => new Promise((
|
|
143158
|
+
return (req) => new Promise((resolve10, reject) => {
|
|
142203
143159
|
const modelArgs = [];
|
|
142204
143160
|
if (req.model)
|
|
142205
143161
|
modelArgs.push("--model", req.model);
|
|
@@ -142244,7 +143200,7 @@ function cliTransport(opts = {}) {
|
|
|
142244
143200
|
reject(new Error("claude returned no text"));
|
|
142245
143201
|
return;
|
|
142246
143202
|
}
|
|
142247
|
-
|
|
143203
|
+
resolve10(text);
|
|
142248
143204
|
} catch (e) {
|
|
142249
143205
|
reject(e instanceof Error ? e : new Error(String(e)));
|
|
142250
143206
|
}
|
|
@@ -142378,7 +143334,7 @@ function checkClaudeAuth(binary2 = resolveClaudeBinary(), options = {}) {
|
|
|
142378
143334
|
return Promise.resolve({ ok: false, reason: "not-found" });
|
|
142379
143335
|
}
|
|
142380
143336
|
const timeoutMs = options.timeoutMs ?? 6e4;
|
|
142381
|
-
return new Promise((
|
|
143337
|
+
return new Promise((resolve10) => {
|
|
142382
143338
|
const proc = (0, import_cross_spawn2.spawn)(binary2, ["-p", "Reply with the single word: ok"], {
|
|
142383
143339
|
stdio: ["ignore", "pipe", "pipe"],
|
|
142384
143340
|
env: cleanClaudeEnv()
|
|
@@ -142390,7 +143346,7 @@ function checkClaudeAuth(binary2 = resolveClaudeBinary(), options = {}) {
|
|
|
142390
143346
|
return;
|
|
142391
143347
|
settled = true;
|
|
142392
143348
|
clearTimeout(timer);
|
|
142393
|
-
|
|
143349
|
+
resolve10(r);
|
|
142394
143350
|
};
|
|
142395
143351
|
const timer = setTimeout(() => {
|
|
142396
143352
|
proc.kill("SIGKILL");
|
|
@@ -143088,7 +144044,7 @@ var WindowsService = class {
|
|
|
143088
144044
|
const { Service } = nw;
|
|
143089
144045
|
const logDir = path22.dirname(logPath);
|
|
143090
144046
|
const truecourseHome = path22.join(os7.homedir(), ".truecourse");
|
|
143091
|
-
return new Promise((
|
|
144047
|
+
return new Promise((resolve10, reject) => {
|
|
143092
144048
|
const svc = new Service({
|
|
143093
144049
|
name: SERVICE_DISPLAY_NAME,
|
|
143094
144050
|
description: "TrueCourse Server",
|
|
@@ -143104,7 +144060,7 @@ var WindowsService = class {
|
|
|
143104
144060
|
});
|
|
143105
144061
|
svc.on("install", () => {
|
|
143106
144062
|
svc.start();
|
|
143107
|
-
|
|
144063
|
+
resolve10();
|
|
143108
144064
|
});
|
|
143109
144065
|
svc.on("error", (err) => reject(err));
|
|
143110
144066
|
svc.install();
|
|
@@ -143113,13 +144069,13 @@ var WindowsService = class {
|
|
|
143113
144069
|
async uninstall() {
|
|
143114
144070
|
const nw = await this.getNodeWindows();
|
|
143115
144071
|
const { Service } = nw;
|
|
143116
|
-
return new Promise((
|
|
144072
|
+
return new Promise((resolve10, reject) => {
|
|
143117
144073
|
const svc = new Service({
|
|
143118
144074
|
name: SERVICE_DISPLAY_NAME,
|
|
143119
144075
|
script: ""
|
|
143120
144076
|
// Not needed for uninstall
|
|
143121
144077
|
});
|
|
143122
|
-
svc.on("uninstall", () =>
|
|
144078
|
+
svc.on("uninstall", () => resolve10());
|
|
143123
144079
|
svc.on("error", (err) => reject(err));
|
|
143124
144080
|
svc.uninstall();
|
|
143125
144081
|
});
|
|
@@ -144583,6 +145539,27 @@ not narrow the set.
|
|
|
144583
145539
|
substitute for \`immutable\`. If a derived/server-computed field is also frozen
|
|
144584
145540
|
after creation, emit BOTH \`computed-at\` AND \`immutable\`.
|
|
144585
145541
|
|
|
145542
|
+
**NEVER infer \`immutable\` from what a field IS \u2014 only from what the spec SAYS
|
|
145543
|
+
about changing it.** The following do NOT imply immutability, alone or combined:
|
|
145544
|
+
|
|
145545
|
+
- The field is required (\`Required? yes\` in a field table).
|
|
145546
|
+
- The field is an identifier (\`id\`, \`uuid\`, "identifier of this X").
|
|
145547
|
+
- The field is a timestamp ("when the event happened", \`occurred\`, \`createdAt\`).
|
|
145548
|
+
- The field is client-provided or server-assigned (provenance is not mutability).
|
|
145549
|
+
|
|
145550
|
+
Counter-example \u2014 a field table like:
|
|
145551
|
+
|
|
145552
|
+
| Name | Type | Required? | Description |
|
|
145553
|
+
| -------- | ------ | --------- | ---------------------------------------- |
|
|
145554
|
+
| occurred | String | yes | When the event happened |
|
|
145555
|
+
| id | String | yes | Client-provided identifier of this event |
|
|
145556
|
+
|
|
145557
|
+
asserts NOTHING about mutability. Both fields get plain \`field occurred: string {}\`
|
|
145558
|
+
/ \`field id: string {}\` \u2014 no \`immutable\`, even though an id "sounds" immutable.
|
|
145559
|
+
Emitting un-stated \`immutable\` creates a false drift against every codebase whose
|
|
145560
|
+
model simply doesn't freeze the field. If the spec is silent on mutability, the
|
|
145561
|
+
contract is silent on mutability.
|
|
145562
|
+
|
|
144586
145563
|
# Enum extraction \u2014 required whenever spec defines an enum
|
|
144587
145564
|
|
|
144588
145565
|
Whenever the spec text contains any of:
|
|
@@ -144605,6 +145582,27 @@ data document. If \`Entity:Customer\` references \`Enum:LoyaltyTier\` and the sa
|
|
|
144605
145582
|
slice contains "LoyaltyTier values: standard, silver, gold", emit BOTH the entity
|
|
144606
145583
|
fragment AND the enum fragment.
|
|
144607
145584
|
|
|
145585
|
+
**An enum is a closed set of DATA VALUES the code compares against** (the
|
|
145586
|
+
string/number literals a field is set to or checked against) \u2014 NOT a catalog of
|
|
145587
|
+
code symbols a caller picks between. Before emitting, check what the listed items
|
|
145588
|
+
ARE. Emit nothing when the list is:
|
|
145589
|
+
|
|
145590
|
+
- **Implementation / plugin classes** \u2014 e.g. "run launchers: \`DefaultRunLauncher\`,
|
|
145591
|
+
\`DockerRunLauncher\`, \`K8sRunLauncher\`", storage backends, compute-log managers.
|
|
145592
|
+
These are swappable extension points (a user can add their own); the items are
|
|
145593
|
+
class names, not a closed value set.
|
|
145594
|
+
- **API functions, decorators, or methods** \u2014 e.g. "asset decorators: \`asset\`,
|
|
145595
|
+
\`multi_asset\`, \`graph_asset\`". These are symbols a user calls, not values.
|
|
145596
|
+
- **An incidental excerpt** \u2014 a few items quoted inside a troubleshooting,
|
|
145597
|
+
example, or how-to passage ("relevant event types: \u2026") rather than a
|
|
145598
|
+
definitional "the valid values of X are \u2026". A subset mentioned in passing is
|
|
145599
|
+
not an enum definition.
|
|
145600
|
+
|
|
145601
|
+
Discriminator: if the items are **names of code symbols** (classes / functions a
|
|
145602
|
+
caller selects), do NOT emit an enum. Only emit when the items are **literal data
|
|
145603
|
+
values** the code stores or compares against (config keys, status strings, tag
|
|
145604
|
+
keys, numeric codes).
|
|
145605
|
+
|
|
144608
145606
|
**Never reference an enum without defining it.** Every \`Enum:X\` identifier you
|
|
144609
145607
|
emit (in \`field: Enum:X\` or \`states Enum:X\`) MUST have a matching \`enum X { \u2026 }\`
|
|
144610
145608
|
artifact somewhere in the same slice (or you must assume another slice provides
|
|
@@ -144934,6 +145932,42 @@ Don't emit constants whose value is a function call, expression, or
|
|
|
144934
145932
|
external reference \u2014 only literal values (strings, numbers, booleans,
|
|
144935
145933
|
arrays of literals, flat object literals of literals).
|
|
144936
145934
|
|
|
145935
|
+
**Don't extract constants from customization examples.** A constant asserts
|
|
145936
|
+
"the code MUST hold this value". A doc that shows users how to CUSTOMIZE or
|
|
145937
|
+
OVERRIDE something is asserting the opposite \u2014 "you can put whatever value
|
|
145938
|
+
you want here" \u2014 and its example values are illustrations, not obligations.
|
|
145939
|
+
Skip a candidate value when EITHER signal is present:
|
|
145940
|
+
|
|
145941
|
+
1. **Context signal**: the page title or enclosing section heading describes
|
|
145942
|
+
a customization/override mechanism \u2014 "Customize \u2026", "Override \u2026",
|
|
145943
|
+
"\u2026 template", "\u2026 variables", "Configure your own \u2026", "Example
|
|
145944
|
+
configuration".
|
|
145945
|
+
2. **Shape signal**: the value sits inside a JSON-schema-style variables
|
|
145946
|
+
block, i.e. an object of the form
|
|
145947
|
+
\`"<name>": { "title": \u2026, "description": \u2026, "default": <value>, "type": \u2026 }\`.
|
|
145948
|
+
That quadruple is a schema DECLARING an overridable variable; the
|
|
145949
|
+
\`"default"\` there is a starting point the user is expected to change.
|
|
145950
|
+
|
|
145951
|
+
Counter-example \u2014 a page titled "Customize Base Job Templates" containing:
|
|
145952
|
+
|
|
145953
|
+
\`\`\`json
|
|
145954
|
+
"cpu_request": {
|
|
145955
|
+
"title": "CPU Request",
|
|
145956
|
+
"description": "CPU allocation to request for this pod",
|
|
145957
|
+
"default": "100m",
|
|
145958
|
+
"type": "string"
|
|
145959
|
+
}
|
|
145960
|
+
\`\`\`
|
|
145961
|
+
|
|
145962
|
+
emits NO constant. Both signals fire: the page is a customization guide, and
|
|
145963
|
+
the value is a variables-schema \`default\`. Extracting
|
|
145964
|
+
\`constant cpu_request { expected-value "100m" }\` would demand every codebase
|
|
145965
|
+
hard-code the example \u2014 a guaranteed false drift.
|
|
145966
|
+
|
|
145967
|
+
By contrast, prose like "the scheduler polls every 15 seconds" or "the API
|
|
145968
|
+
version is \`v2\`" in a behavioral spec section IS an assertion about the
|
|
145969
|
+
system \u2014 extract those normally.
|
|
145970
|
+
|
|
144937
145971
|
# ArchitectureDecision \u2014 extract when the spec/ADR fixes a platform choice
|
|
144938
145972
|
|
|
144939
145973
|
A spec or ADR that records a system-wide technology choice \u2014 "we use
|
|
@@ -145431,6 +146465,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
145431
146465
|
".cache",
|
|
145432
146466
|
"coverage"
|
|
145433
146467
|
]);
|
|
146468
|
+
var SKIP_DIR_PROBE = "__tc_skipdir_probe__.md";
|
|
145434
146469
|
var PREVIEW_LINE_LIMIT = 200;
|
|
145435
146470
|
function discoverDocs(rootDir, opts = {}) {
|
|
145436
146471
|
const previewLines = opts.previewLines ?? PREVIEW_LINE_LIMIT;
|
|
@@ -145445,11 +146480,12 @@ function discoverDocs(rootDir, opts = {}) {
|
|
|
145445
146480
|
}
|
|
145446
146481
|
entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
145447
146482
|
for (const entry of entries) {
|
|
145448
|
-
|
|
146483
|
+
const full = path26.join(dir, entry.name);
|
|
146484
|
+
if (SKIP_DIRS.has(entry.name) && !(entry.isDirectory() && tcIgnore.reincludes(path26.join(full, SKIP_DIR_PROBE)))) {
|
|
145449
146485
|
continue;
|
|
146486
|
+
}
|
|
145450
146487
|
if (entry.name.startsWith(".") && entry.name !== ".")
|
|
145451
146488
|
continue;
|
|
145452
|
-
const full = path26.join(dir, entry.name);
|
|
145453
146489
|
if (tcIgnore.ignores(full))
|
|
145454
146490
|
continue;
|
|
145455
146491
|
if (entry.isDirectory()) {
|
|
@@ -147308,14 +148344,14 @@ async function explainConflicts(repoRoot6, conflicts2, opts = {}) {
|
|
|
147308
148344
|
opts.onStart?.(conflicts2.length);
|
|
147309
148345
|
let active = 0;
|
|
147310
148346
|
let cursor = 0;
|
|
147311
|
-
await new Promise((
|
|
148347
|
+
await new Promise((resolve10) => {
|
|
147312
148348
|
const launch = () => {
|
|
147313
148349
|
while (active < concurrency && cursor < conflicts2.length) {
|
|
147314
148350
|
const conflict = conflicts2[cursor++];
|
|
147315
148351
|
if (conflict.explanation) {
|
|
147316
148352
|
opts.onDone?.();
|
|
147317
148353
|
if (cursor >= conflicts2.length && active === 0)
|
|
147318
|
-
|
|
148354
|
+
resolve10();
|
|
147319
148355
|
continue;
|
|
147320
148356
|
}
|
|
147321
148357
|
active++;
|
|
@@ -147323,13 +148359,13 @@ async function explainConflicts(repoRoot6, conflicts2, opts = {}) {
|
|
|
147323
148359
|
opts.onDone?.();
|
|
147324
148360
|
active--;
|
|
147325
148361
|
if (cursor >= conflicts2.length && active === 0)
|
|
147326
|
-
|
|
148362
|
+
resolve10();
|
|
147327
148363
|
else
|
|
147328
148364
|
launch();
|
|
147329
148365
|
});
|
|
147330
148366
|
}
|
|
147331
148367
|
if (cursor >= conflicts2.length && active === 0)
|
|
147332
|
-
|
|
148368
|
+
resolve10();
|
|
147333
148369
|
};
|
|
147334
148370
|
launch();
|
|
147335
148371
|
});
|
|
@@ -147562,7 +148598,7 @@ async function resolveConflicts(repoRoot6, conflicts2, opts = {}) {
|
|
|
147562
148598
|
const out = [];
|
|
147563
148599
|
let cursor = 0;
|
|
147564
148600
|
let active = 0;
|
|
147565
|
-
await new Promise((
|
|
148601
|
+
await new Promise((resolve10) => {
|
|
147566
148602
|
const launch = () => {
|
|
147567
148603
|
while (active < concurrency && cursor < conflicts2.length) {
|
|
147568
148604
|
const conflict = conflicts2[cursor++];
|
|
@@ -147581,13 +148617,13 @@ async function resolveConflicts(repoRoot6, conflicts2, opts = {}) {
|
|
|
147581
148617
|
}).finally(() => {
|
|
147582
148618
|
active--;
|
|
147583
148619
|
if (cursor >= conflicts2.length && active === 0)
|
|
147584
|
-
|
|
148620
|
+
resolve10();
|
|
147585
148621
|
else
|
|
147586
148622
|
launch();
|
|
147587
148623
|
});
|
|
147588
148624
|
}
|
|
147589
148625
|
if (cursor >= conflicts2.length && active === 0)
|
|
147590
|
-
|
|
148626
|
+
resolve10();
|
|
147591
148627
|
};
|
|
147592
148628
|
launch();
|
|
147593
148629
|
});
|
|
@@ -147807,7 +148843,7 @@ async function filterByRelevance(repoRoot6, docs, opts = {}) {
|
|
|
147807
148843
|
const verdicts = /* @__PURE__ */ new Map();
|
|
147808
148844
|
let cursor = 0;
|
|
147809
148845
|
let active = 0;
|
|
147810
|
-
await new Promise((
|
|
148846
|
+
await new Promise((resolve10) => {
|
|
147811
148847
|
const launch = () => {
|
|
147812
148848
|
while (active < concurrency && cursor < docs.length) {
|
|
147813
148849
|
const doc = docs[cursor++];
|
|
@@ -147815,7 +148851,7 @@ async function filterByRelevance(repoRoot6, docs, opts = {}) {
|
|
|
147815
148851
|
verdicts.set(doc.path, { path: doc.path, include: true, reason: "manual include" });
|
|
147816
148852
|
markDone();
|
|
147817
148853
|
if (cursor >= docs.length && active === 0)
|
|
147818
|
-
|
|
148854
|
+
resolve10();
|
|
147819
148855
|
continue;
|
|
147820
148856
|
}
|
|
147821
148857
|
active++;
|
|
@@ -147831,13 +148867,13 @@ async function filterByRelevance(repoRoot6, docs, opts = {}) {
|
|
|
147831
148867
|
markDone();
|
|
147832
148868
|
active--;
|
|
147833
148869
|
if (cursor >= docs.length && active === 0)
|
|
147834
|
-
|
|
148870
|
+
resolve10();
|
|
147835
148871
|
else
|
|
147836
148872
|
launch();
|
|
147837
148873
|
});
|
|
147838
148874
|
}
|
|
147839
148875
|
if (cursor >= docs.length && active === 0)
|
|
147840
|
-
|
|
148876
|
+
resolve10();
|
|
147841
148877
|
};
|
|
147842
148878
|
launch();
|
|
147843
148879
|
});
|
|
@@ -154574,7 +155610,7 @@ async function runHooksRun() {
|
|
|
154574
155610
|
|
|
154575
155611
|
// tools/cli/src/index.ts
|
|
154576
155612
|
var program2 = new Command();
|
|
154577
|
-
program2.name("truecourse").version("0.6.
|
|
155613
|
+
program2.name("truecourse").version("0.6.7-next.1").description("TrueCourse CLI \u2014 analyze your repository and open the dashboard");
|
|
154578
155614
|
var dashboardCmd = program2.command("dashboard").description("Start the TrueCourse dashboard and open it in your browser").option("--reconfigure", "Re-prompt for console vs background service mode").option("--service", "Run as a background service (skips mode prompt)").option("--console", "Run in this terminal (skips mode prompt)").action(async (options) => {
|
|
154579
155615
|
if (options.service && options.console) {
|
|
154580
155616
|
console.error("error: --service and --console are mutually exclusive");
|