truecourse 0.6.0-next.9 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/cli.mjs +257 -40
- package/package.json +1 -1
- package/server.mjs +247 -39
package/README.md
CHANGED
|
@@ -366,7 +366,7 @@ Patterns are anchored to the file's location, so `src/generated/` matches the to
|
|
|
366
366
|
|
|
367
367
|
## Telemetry
|
|
368
368
|
|
|
369
|
-
TrueCourse collects anonymous usage data
|
|
369
|
+
TrueCourse collects anonymous usage data to improve the product — one event per command (`analyze`, `spec_scan`, `contracts_generate`, `verify`, `infer`), each carrying only coarse, bucketed counts (file/artifact/drift/decision *ranges*, duration range), the surface (CLI vs dashboard), OS, and tool version. No source code, file paths, identities, or violation/drift details are collected. It is automatically disabled in CI environments.
|
|
370
370
|
|
|
371
371
|
```bash
|
|
372
372
|
truecourse telemetry status # Check telemetry status
|
package/cli.mjs
CHANGED
|
@@ -12451,10 +12451,10 @@ function computeFunctionMetrics(node2) {
|
|
|
12451
12451
|
if (!bodyNode) {
|
|
12452
12452
|
return { lineCount, statementCount: 0, maxNestingDepth: 0 };
|
|
12453
12453
|
}
|
|
12454
|
-
let
|
|
12454
|
+
let statementCount2 = 0;
|
|
12455
12455
|
for (const child of bodyNode.namedChildren) {
|
|
12456
12456
|
if (STATEMENT_NODE_TYPES.has(child.type)) {
|
|
12457
|
-
|
|
12457
|
+
statementCount2++;
|
|
12458
12458
|
}
|
|
12459
12459
|
}
|
|
12460
12460
|
let maxNestingDepth2 = 0;
|
|
@@ -12471,7 +12471,7 @@ function computeFunctionMetrics(node2) {
|
|
|
12471
12471
|
}
|
|
12472
12472
|
}
|
|
12473
12473
|
walkNesting(bodyNode, 0);
|
|
12474
|
-
return { lineCount, statementCount, maxNestingDepth: maxNestingDepth2 };
|
|
12474
|
+
return { lineCount, statementCount: statementCount2, maxNestingDepth: maxNestingDepth2 };
|
|
12475
12475
|
}
|
|
12476
12476
|
var NESTING_NODE_TYPES, STATEMENT_NODE_TYPES;
|
|
12477
12477
|
var init_common = __esm({
|
|
@@ -13369,10 +13369,10 @@ function computePythonFunctionMetrics(node2) {
|
|
|
13369
13369
|
if (!bodyNode) {
|
|
13370
13370
|
return { lineCount, statementCount: 0, maxNestingDepth: 0 };
|
|
13371
13371
|
}
|
|
13372
|
-
let
|
|
13372
|
+
let statementCount2 = 0;
|
|
13373
13373
|
for (const child of bodyNode.namedChildren) {
|
|
13374
13374
|
if (PYTHON_STATEMENT_NODE_TYPES.has(child.type)) {
|
|
13375
|
-
|
|
13375
|
+
statementCount2++;
|
|
13376
13376
|
}
|
|
13377
13377
|
}
|
|
13378
13378
|
let maxNestingDepth2 = 0;
|
|
@@ -13389,7 +13389,7 @@ function computePythonFunctionMetrics(node2) {
|
|
|
13389
13389
|
}
|
|
13390
13390
|
}
|
|
13391
13391
|
walkNesting(bodyNode, 0);
|
|
13392
|
-
return { lineCount, statementCount, maxNestingDepth: maxNestingDepth2 };
|
|
13392
|
+
return { lineCount, statementCount: statementCount2, maxNestingDepth: maxNestingDepth2 };
|
|
13393
13393
|
}
|
|
13394
13394
|
function isNestedInFunction3(node2) {
|
|
13395
13395
|
let current = node2.parent;
|
|
@@ -48980,6 +48980,19 @@ var init_prototype_pollution = __esm({
|
|
|
48980
48980
|
});
|
|
48981
48981
|
|
|
48982
48982
|
// packages/analyzer/dist/rules/bugs/visitors/javascript/void-zero-argument.js
|
|
48983
|
+
function operandIsCall(operand) {
|
|
48984
|
+
let cur = operand;
|
|
48985
|
+
while (cur) {
|
|
48986
|
+
if (cur.type === "call_expression" || cur.type === "new_expression")
|
|
48987
|
+
return true;
|
|
48988
|
+
if (cur.type === "parenthesized_expression" || cur.type === "await_expression") {
|
|
48989
|
+
cur = cur.namedChildren[cur.namedChildren.length - 1] ?? null;
|
|
48990
|
+
continue;
|
|
48991
|
+
}
|
|
48992
|
+
return false;
|
|
48993
|
+
}
|
|
48994
|
+
return false;
|
|
48995
|
+
}
|
|
48983
48996
|
var voidZeroArgumentVisitor;
|
|
48984
48997
|
var init_void_zero_argument = __esm({
|
|
48985
48998
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/void-zero-argument.js"() {
|
|
@@ -48994,6 +49007,8 @@ var init_void_zero_argument = __esm({
|
|
|
48994
49007
|
const op = node2.children.find((c2) => c2.text === "void");
|
|
48995
49008
|
if (!op)
|
|
48996
49009
|
return null;
|
|
49010
|
+
if (operandIsCall(node2.children[node2.children.length - 1]))
|
|
49011
|
+
return null;
|
|
48997
49012
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Unnecessary void expression", `\`${node2.text}\` can be replaced with \`undefined\` directly.`, sourceCode, "Use `undefined` instead of `void 0`.");
|
|
48998
49013
|
}
|
|
48999
49014
|
};
|
|
@@ -51816,12 +51831,34 @@ function isModuleLevel(node2) {
|
|
|
51816
51831
|
function isMutableInit(node2) {
|
|
51817
51832
|
return node2.type === "object" || node2.type === "array" || node2.type === "new_expression";
|
|
51818
51833
|
}
|
|
51819
|
-
|
|
51834
|
+
function isClientSideFile(filePath, program3) {
|
|
51835
|
+
for (const tok of CLIENT_PATH_TOKENS) {
|
|
51836
|
+
if (filePath.includes(tok))
|
|
51837
|
+
return true;
|
|
51838
|
+
}
|
|
51839
|
+
const first2 = program3?.namedChildren[0];
|
|
51840
|
+
if (first2?.type === "expression_statement") {
|
|
51841
|
+
const text = first2.text.trim().replace(/;$/, "").replace(/['"`]/g, "");
|
|
51842
|
+
if (text === "use client")
|
|
51843
|
+
return true;
|
|
51844
|
+
}
|
|
51845
|
+
return false;
|
|
51846
|
+
}
|
|
51847
|
+
var CLIENT_PATH_TOKENS, sharedMutableModuleStateVisitor;
|
|
51820
51848
|
var init_shared_mutable_module_state = __esm({
|
|
51821
51849
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/shared-mutable-module-state.js"() {
|
|
51822
51850
|
"use strict";
|
|
51823
51851
|
init_types();
|
|
51824
51852
|
init_helpers2();
|
|
51853
|
+
CLIENT_PATH_TOKENS = [
|
|
51854
|
+
"/client/",
|
|
51855
|
+
"/client-only/",
|
|
51856
|
+
"/hooks/",
|
|
51857
|
+
"/primitives/",
|
|
51858
|
+
"/components/",
|
|
51859
|
+
".client.ts",
|
|
51860
|
+
".client.tsx"
|
|
51861
|
+
];
|
|
51825
51862
|
sharedMutableModuleStateVisitor = {
|
|
51826
51863
|
ruleKey: "bugs/deterministic/shared-mutable-module-state",
|
|
51827
51864
|
languages: JS_LANGUAGES,
|
|
@@ -51829,6 +51866,11 @@ var init_shared_mutable_module_state = __esm({
|
|
|
51829
51866
|
visit(node2, filePath, sourceCode) {
|
|
51830
51867
|
if (!isModuleLevel(node2))
|
|
51831
51868
|
return null;
|
|
51869
|
+
let program3 = node2;
|
|
51870
|
+
while (program3 && program3.type !== "program")
|
|
51871
|
+
program3 = program3.parent;
|
|
51872
|
+
if (isClientSideFile(filePath, program3))
|
|
51873
|
+
return null;
|
|
51832
51874
|
const kindChild = node2.children[0];
|
|
51833
51875
|
if (!kindChild || kindChild.text === "const")
|
|
51834
51876
|
return null;
|
|
@@ -53917,13 +53959,38 @@ var init_switch_exhaustiveness = __esm({
|
|
|
53917
53959
|
});
|
|
53918
53960
|
|
|
53919
53961
|
// packages/analyzer/dist/rules/bugs/visitors/javascript/non-number-arithmetic.js
|
|
53920
|
-
|
|
53962
|
+
function isConfidentlyNonNumeric(t2) {
|
|
53963
|
+
if (CONFIDENTLY_NON_NUMERIC.has(t2))
|
|
53964
|
+
return true;
|
|
53965
|
+
if (/^["']/.test(t2))
|
|
53966
|
+
return true;
|
|
53967
|
+
if (t2 === "true" || t2 === "false")
|
|
53968
|
+
return true;
|
|
53969
|
+
if (/\[\]$/.test(t2) || /^readonly\s/.test(t2))
|
|
53970
|
+
return true;
|
|
53971
|
+
if (t2.includes("=>") || t2.startsWith("{"))
|
|
53972
|
+
return true;
|
|
53973
|
+
return false;
|
|
53974
|
+
}
|
|
53975
|
+
var ARITHMETIC_OPS, CONFIDENTLY_NON_NUMERIC, nonNumberArithmeticVisitor;
|
|
53921
53976
|
var init_non_number_arithmetic = __esm({
|
|
53922
53977
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/non-number-arithmetic.js"() {
|
|
53923
53978
|
"use strict";
|
|
53924
53979
|
init_types();
|
|
53925
53980
|
init_helpers2();
|
|
53926
53981
|
ARITHMETIC_OPS = /* @__PURE__ */ new Set(["-", "*", "/", "%", "**"]);
|
|
53982
|
+
CONFIDENTLY_NON_NUMERIC = /* @__PURE__ */ new Set([
|
|
53983
|
+
"string",
|
|
53984
|
+
"boolean",
|
|
53985
|
+
"null",
|
|
53986
|
+
"undefined",
|
|
53987
|
+
"void",
|
|
53988
|
+
"symbol",
|
|
53989
|
+
"never",
|
|
53990
|
+
"object",
|
|
53991
|
+
// Common runtime types that don't auto-coerce to number in arithmetic.
|
|
53992
|
+
"Date"
|
|
53993
|
+
]);
|
|
53927
53994
|
nonNumberArithmeticVisitor = {
|
|
53928
53995
|
ruleKey: "bugs/deterministic/non-number-arithmetic",
|
|
53929
53996
|
languages: TS_LANGUAGES,
|
|
@@ -53943,16 +54010,14 @@ var init_non_number_arithmetic = __esm({
|
|
|
53943
54010
|
const rightType = typeQuery.getTypeAtPosition(filePath, right.startPosition.row, right.startPosition.column, right.endPosition.row, right.endPosition.column);
|
|
53944
54011
|
if (!leftType || !rightType)
|
|
53945
54012
|
return null;
|
|
53946
|
-
const numericTypes = /* @__PURE__ */ new Set(["number", "bigint", "any"]);
|
|
53947
|
-
const leftOk = numericTypes.has(leftType) || /^\d+$/.test(leftType);
|
|
53948
|
-
const rightOk = numericTypes.has(rightType) || /^\d+$/.test(rightType);
|
|
53949
54013
|
if (left.type === "number" || right.type === "number")
|
|
53950
54014
|
return null;
|
|
53951
|
-
|
|
53952
|
-
|
|
53953
|
-
|
|
53954
|
-
|
|
53955
|
-
|
|
54015
|
+
const leftBad = isConfidentlyNonNumeric(leftType);
|
|
54016
|
+
const rightBad = isConfidentlyNonNumeric(rightType);
|
|
54017
|
+
if (!leftBad && !rightBad)
|
|
54018
|
+
return null;
|
|
54019
|
+
const badSide = leftBad ? `left operand is \`${leftType}\`` : `right operand is \`${rightType}\``;
|
|
54020
|
+
return makeViolation(this.ruleKey, node2, filePath, "high", "Non-numeric value in arithmetic", `Arithmetic operator \`${operator.text}\` used with non-numeric operand \u2014 ${badSide}. This will produce \`NaN\` at runtime.`, sourceCode, "Convert operands to numbers or fix the expression.");
|
|
53956
54021
|
}
|
|
53957
54022
|
};
|
|
53958
54023
|
}
|
|
@@ -66811,12 +66876,29 @@ var init_no_var_declaration = __esm({
|
|
|
66811
66876
|
});
|
|
66812
66877
|
|
|
66813
66878
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/cognitive-complexity.js
|
|
66814
|
-
|
|
66879
|
+
function isInsideJsxExpression(node2, functionNodeId) {
|
|
66880
|
+
let cur = node2.parent;
|
|
66881
|
+
while (cur && cur.id !== functionNodeId) {
|
|
66882
|
+
if (JSX_EXPRESSION_TYPES.has(cur.type))
|
|
66883
|
+
return true;
|
|
66884
|
+
if (JS_FUNCTION_TYPES.includes(cur.type))
|
|
66885
|
+
return false;
|
|
66886
|
+
cur = cur.parent;
|
|
66887
|
+
}
|
|
66888
|
+
return false;
|
|
66889
|
+
}
|
|
66890
|
+
var JSX_EXPRESSION_TYPES, cognitiveComplexityVisitor;
|
|
66815
66891
|
var init_cognitive_complexity = __esm({
|
|
66816
66892
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/cognitive-complexity.js"() {
|
|
66817
66893
|
"use strict";
|
|
66818
66894
|
init_types();
|
|
66819
66895
|
init_helpers4();
|
|
66896
|
+
JSX_EXPRESSION_TYPES = /* @__PURE__ */ new Set([
|
|
66897
|
+
"jsx_expression",
|
|
66898
|
+
// {expr} attribute / child
|
|
66899
|
+
"jsx_attribute"
|
|
66900
|
+
// attr={expr}
|
|
66901
|
+
]);
|
|
66820
66902
|
cognitiveComplexityVisitor = {
|
|
66821
66903
|
ruleKey: "code-quality/deterministic/cognitive-complexity",
|
|
66822
66904
|
languages: ["typescript", "tsx", "javascript"],
|
|
@@ -66832,14 +66914,17 @@ var init_cognitive_complexity = __esm({
|
|
|
66832
66914
|
if (JS_FUNCTION_TYPES.includes(n.type) && n.id !== node2.id)
|
|
66833
66915
|
return;
|
|
66834
66916
|
if (INCREMENT_TYPES.has(n.type)) {
|
|
66835
|
-
|
|
66917
|
+
if (n.type === "ternary_expression" && isInsideJsxExpression(n, node2.id)) {
|
|
66918
|
+
} else {
|
|
66919
|
+
complexity += 1 + nesting;
|
|
66920
|
+
}
|
|
66836
66921
|
}
|
|
66837
66922
|
if (n.type === "else_clause") {
|
|
66838
66923
|
complexity += 1;
|
|
66839
66924
|
}
|
|
66840
66925
|
if (n.type === "binary_expression") {
|
|
66841
66926
|
const op = n.children.find((c2) => c2.type === "&&" || c2.type === "||");
|
|
66842
|
-
if (op)
|
|
66927
|
+
if (op && !isInsideJsxExpression(n, node2.id))
|
|
66843
66928
|
complexity += 1;
|
|
66844
66929
|
}
|
|
66845
66930
|
const nextNesting = NESTING_TYPES3.has(n.type) ? nesting + 1 : nesting;
|
|
@@ -67351,6 +67436,19 @@ var init_no_proto = __esm({
|
|
|
67351
67436
|
});
|
|
67352
67437
|
|
|
67353
67438
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/no-void.js
|
|
67439
|
+
function operandIsCall2(operand) {
|
|
67440
|
+
let cur = operand;
|
|
67441
|
+
while (cur) {
|
|
67442
|
+
if (cur.type === "call_expression" || cur.type === "new_expression")
|
|
67443
|
+
return true;
|
|
67444
|
+
if (cur.type === "parenthesized_expression" || cur.type === "await_expression") {
|
|
67445
|
+
cur = cur.namedChildren[cur.namedChildren.length - 1] ?? null;
|
|
67446
|
+
continue;
|
|
67447
|
+
}
|
|
67448
|
+
return false;
|
|
67449
|
+
}
|
|
67450
|
+
return false;
|
|
67451
|
+
}
|
|
67354
67452
|
var noVoidVisitor;
|
|
67355
67453
|
var init_no_void = __esm({
|
|
67356
67454
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/no-void.js"() {
|
|
@@ -67367,6 +67465,8 @@ var init_no_void = __esm({
|
|
|
67367
67465
|
const operand = node2.children[1];
|
|
67368
67466
|
if (operand?.text === "0")
|
|
67369
67467
|
return null;
|
|
67468
|
+
if (operandIsCall2(operand))
|
|
67469
|
+
return null;
|
|
67370
67470
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Void expression", "The `void` operator is confusing. Use `undefined` directly or omit the return value.", sourceCode, "Replace `void expr` with `undefined` or remove the expression.");
|
|
67371
67471
|
}
|
|
67372
67472
|
};
|
|
@@ -70901,7 +71001,7 @@ var init_unnamed_regex_capture = __esm({
|
|
|
70901
71001
|
visit(node2, filePath, sourceCode) {
|
|
70902
71002
|
const pattern = node2.namedChildren.find((c2) => c2.type === "regex_pattern");
|
|
70903
71003
|
const src = pattern?.text ?? "";
|
|
70904
|
-
let
|
|
71004
|
+
let unnamedCount = 0;
|
|
70905
71005
|
for (let i = 0; i < src.length; i++) {
|
|
70906
71006
|
if (src[i] === "\\") {
|
|
70907
71007
|
i++;
|
|
@@ -70924,19 +71024,22 @@ var init_unnamed_regex_capture = __esm({
|
|
|
70924
71024
|
j2++;
|
|
70925
71025
|
}
|
|
70926
71026
|
const groupContent = src.slice(i + 1, j2 - 1);
|
|
71027
|
+
const after = src[j2];
|
|
71028
|
+
if (after === "+" || after === "*" || after === "?" || after === "{") {
|
|
71029
|
+
continue;
|
|
71030
|
+
}
|
|
70927
71031
|
if (groupContent.includes("|")) {
|
|
70928
71032
|
const alternatives = groupContent.split("|");
|
|
70929
71033
|
const isSimpleAlternation = alternatives.every((alt) => /^[a-zA-Z0-9_\\?.^$*+\-]+$/.test(alt) && alt.length <= 10);
|
|
70930
71034
|
if (isSimpleAlternation)
|
|
70931
71035
|
continue;
|
|
70932
71036
|
}
|
|
70933
|
-
|
|
70934
|
-
break;
|
|
71037
|
+
unnamedCount++;
|
|
70935
71038
|
}
|
|
70936
71039
|
}
|
|
70937
71040
|
}
|
|
70938
|
-
if (
|
|
70939
|
-
return makeViolation(this.ruleKey, node2, filePath, "low", "Unnamed capture
|
|
71041
|
+
if (unnamedCount >= 2) {
|
|
71042
|
+
return makeViolation(this.ruleKey, node2, filePath, "low", "Unnamed capture groups", "Regex contains multiple unnamed capture groups. Use named groups `(?<name>...)` for better readability.", sourceCode, "Convert capture groups to named: `(?<name>...)` or non-capturing: `(?:...)`.");
|
|
70940
71043
|
}
|
|
70941
71044
|
return null;
|
|
70942
71045
|
}
|
|
@@ -71425,6 +71528,25 @@ var init_computed_enum_value = __esm({
|
|
|
71425
71528
|
});
|
|
71426
71529
|
|
|
71427
71530
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/useless-empty-export.js
|
|
71531
|
+
function fileHasOtherTopLevelModuleStatement(emptyExport) {
|
|
71532
|
+
let program3 = emptyExport;
|
|
71533
|
+
while (program3 && program3.type !== "program")
|
|
71534
|
+
program3 = program3.parent;
|
|
71535
|
+
if (!program3)
|
|
71536
|
+
return false;
|
|
71537
|
+
for (const child of program3.namedChildren) {
|
|
71538
|
+
if (child.id === emptyExport.id)
|
|
71539
|
+
continue;
|
|
71540
|
+
if (child.type === "import_statement")
|
|
71541
|
+
return true;
|
|
71542
|
+
if (child.type === "export_statement") {
|
|
71543
|
+
const inner = child.namedChildren.find((c2) => c2.type === "named_exports" || c2.type === "export_clause");
|
|
71544
|
+
if (!inner || inner.namedChildCount > 0)
|
|
71545
|
+
return true;
|
|
71546
|
+
}
|
|
71547
|
+
}
|
|
71548
|
+
return false;
|
|
71549
|
+
}
|
|
71428
71550
|
var uselessEmptyExportVisitor;
|
|
71429
71551
|
var init_useless_empty_export = __esm({
|
|
71430
71552
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/useless-empty-export.js"() {
|
|
@@ -71438,10 +71560,11 @@ var init_useless_empty_export = __esm({
|
|
|
71438
71560
|
const namedExports = node2.namedChildren.find((c2) => c2.type === "named_exports" || c2.type === "export_clause");
|
|
71439
71561
|
if (!namedExports)
|
|
71440
71562
|
return null;
|
|
71441
|
-
if (namedExports.namedChildCount
|
|
71442
|
-
return
|
|
71443
|
-
|
|
71444
|
-
|
|
71563
|
+
if (namedExports.namedChildCount !== 0)
|
|
71564
|
+
return null;
|
|
71565
|
+
if (!fileHasOtherTopLevelModuleStatement(node2))
|
|
71566
|
+
return null;
|
|
71567
|
+
return makeViolation(this.ruleKey, node2, filePath, "low", "Useless empty export", "`export {}` does nothing useful. Remove it unless it is needed to mark the file as a module.", sourceCode, "Remove the empty `export {}` statement.");
|
|
71445
71568
|
}
|
|
71446
71569
|
};
|
|
71447
71570
|
}
|
|
@@ -71745,6 +71868,14 @@ var init_multiline_block_without_braces = __esm({
|
|
|
71745
71868
|
});
|
|
71746
71869
|
|
|
71747
71870
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/negated-condition.js
|
|
71871
|
+
function statementCount(body) {
|
|
71872
|
+
if (!body)
|
|
71873
|
+
return 0;
|
|
71874
|
+
if (body.type === "statement_block") {
|
|
71875
|
+
return body.namedChildren.filter((c2) => c2.type !== "comment").length;
|
|
71876
|
+
}
|
|
71877
|
+
return 1;
|
|
71878
|
+
}
|
|
71748
71879
|
var negatedConditionVisitor;
|
|
71749
71880
|
var init_negated_condition = __esm({
|
|
71750
71881
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/negated-condition.js"() {
|
|
@@ -71768,6 +71899,11 @@ var init_negated_condition = __esm({
|
|
|
71768
71899
|
const elseBody = elsePart.namedChildren[0];
|
|
71769
71900
|
if (elseBody?.type === "if_statement")
|
|
71770
71901
|
return null;
|
|
71902
|
+
const ifBody = node2.childForFieldName("consequence");
|
|
71903
|
+
const ifCount = statementCount(ifBody);
|
|
71904
|
+
const elseCount = statementCount(elseBody);
|
|
71905
|
+
if (elseCount < ifCount + 2)
|
|
71906
|
+
return null;
|
|
71771
71907
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Negated condition with else", "Condition is negated but has an else block. Invert the condition and swap the branches for better readability.", sourceCode, "Invert the condition and swap the if/else bodies.");
|
|
71772
71908
|
}
|
|
71773
71909
|
};
|
|
@@ -73338,6 +73474,15 @@ var init_undefined_as_identifier = __esm({
|
|
|
73338
73474
|
});
|
|
73339
73475
|
|
|
73340
73476
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/require-unicode-regexp.js
|
|
73477
|
+
function unicodeFlagWouldMatter(pattern) {
|
|
73478
|
+
if (/[^\x00-\x7F]/.test(pattern))
|
|
73479
|
+
return true;
|
|
73480
|
+
if (/\\u\{/.test(pattern))
|
|
73481
|
+
return true;
|
|
73482
|
+
if (/\\[pP]\{/.test(pattern))
|
|
73483
|
+
return true;
|
|
73484
|
+
return false;
|
|
73485
|
+
}
|
|
73341
73486
|
var requireUnicodeRegexpVisitor;
|
|
73342
73487
|
var init_require_unicode_regexp = __esm({
|
|
73343
73488
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/require-unicode-regexp.js"() {
|
|
@@ -73352,6 +73497,9 @@ var init_require_unicode_regexp = __esm({
|
|
|
73352
73497
|
const flagText = flags?.text ?? "";
|
|
73353
73498
|
if (flagText.includes("u") || flagText.includes("v"))
|
|
73354
73499
|
return null;
|
|
73500
|
+
const pattern = node2.namedChildren.find((c2) => c2.type === "regex_pattern")?.text ?? "";
|
|
73501
|
+
if (!unicodeFlagWouldMatter(pattern))
|
|
73502
|
+
return null;
|
|
73355
73503
|
return makeViolation(this.ruleKey, node2, filePath, "low", "RegExp missing unicode flag", "Regular expression should use the `u` or `v` flag for correct Unicode character handling.", sourceCode, `Add the \`u\` flag: ${node2.text}u`);
|
|
73356
73504
|
}
|
|
73357
73505
|
};
|
|
@@ -77217,6 +77365,18 @@ var init_readonly_parameter_types = __esm({
|
|
|
77217
77365
|
});
|
|
77218
77366
|
|
|
77219
77367
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/complex-type-alias.js
|
|
77368
|
+
function flattenUnionMembers(node2) {
|
|
77369
|
+
if (node2.type !== "union_type")
|
|
77370
|
+
return [node2];
|
|
77371
|
+
const out = [];
|
|
77372
|
+
for (const child of node2.namedChildren) {
|
|
77373
|
+
if (child.type === "union_type")
|
|
77374
|
+
out.push(...flattenUnionMembers(child));
|
|
77375
|
+
else
|
|
77376
|
+
out.push(child);
|
|
77377
|
+
}
|
|
77378
|
+
return out;
|
|
77379
|
+
}
|
|
77220
77380
|
function maxBracketDepth(text) {
|
|
77221
77381
|
let depth = 0;
|
|
77222
77382
|
let max = 0;
|
|
@@ -77244,11 +77404,20 @@ function countUnionIntersectionMembers(text) {
|
|
|
77244
77404
|
}
|
|
77245
77405
|
return count;
|
|
77246
77406
|
}
|
|
77247
|
-
var DEPTH_THRESHOLD, MEMBER_THRESHOLD, complexTypeAliasVisitor;
|
|
77407
|
+
var SIMPLE_UNION_MEMBER_TYPES, DEPTH_THRESHOLD, MEMBER_THRESHOLD, complexTypeAliasVisitor;
|
|
77248
77408
|
var init_complex_type_alias = __esm({
|
|
77249
77409
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/complex-type-alias.js"() {
|
|
77250
77410
|
"use strict";
|
|
77251
77411
|
init_types();
|
|
77412
|
+
SIMPLE_UNION_MEMBER_TYPES = /* @__PURE__ */ new Set([
|
|
77413
|
+
"literal_type",
|
|
77414
|
+
"string",
|
|
77415
|
+
"number",
|
|
77416
|
+
"type_identifier",
|
|
77417
|
+
"predefined_type",
|
|
77418
|
+
"null",
|
|
77419
|
+
"undefined"
|
|
77420
|
+
]);
|
|
77252
77421
|
DEPTH_THRESHOLD = 4;
|
|
77253
77422
|
MEMBER_THRESHOLD = 6;
|
|
77254
77423
|
complexTypeAliasVisitor = {
|
|
@@ -77263,11 +77432,9 @@ var init_complex_type_alias = __esm({
|
|
|
77263
77432
|
const depth = maxBracketDepth(typeText);
|
|
77264
77433
|
const members = countUnionIntersectionMembers(typeText);
|
|
77265
77434
|
if (typeValue.type === "union_type") {
|
|
77266
|
-
const
|
|
77267
|
-
|
|
77268
|
-
|
|
77269
|
-
});
|
|
77270
|
-
if (allLiterals)
|
|
77435
|
+
const members2 = flattenUnionMembers(typeValue);
|
|
77436
|
+
const allSimple = members2.every((m) => SIMPLE_UNION_MEMBER_TYPES.has(m.type));
|
|
77437
|
+
if (allSimple)
|
|
77271
77438
|
return null;
|
|
77272
77439
|
}
|
|
77273
77440
|
if (depth >= DEPTH_THRESHOLD || members >= MEMBER_THRESHOLD) {
|
|
@@ -128244,7 +128411,7 @@ function readToolVersion() {
|
|
|
128244
128411
|
if (cachedVersion)
|
|
128245
128412
|
return cachedVersion;
|
|
128246
128413
|
if (true) {
|
|
128247
|
-
cachedVersion = "0.6.0
|
|
128414
|
+
cachedVersion = "0.6.0";
|
|
128248
128415
|
return cachedVersion;
|
|
128249
128416
|
}
|
|
128250
128417
|
try {
|
|
@@ -148507,6 +148674,7 @@ function deleteVerifyDiff(repoPath) {
|
|
|
148507
148674
|
}
|
|
148508
148675
|
|
|
148509
148676
|
// packages/core/dist/commands/spec-in-process.js
|
|
148677
|
+
init_telemetry_service();
|
|
148510
148678
|
function perfNow3() {
|
|
148511
148679
|
return Number(process.hrtime.bigint() / 1000000n);
|
|
148512
148680
|
}
|
|
@@ -148585,6 +148753,7 @@ function buildScanState(result) {
|
|
|
148585
148753
|
}
|
|
148586
148754
|
async function scanInProcess(repoRoot6, options = {}) {
|
|
148587
148755
|
const { tracker } = options;
|
|
148756
|
+
const startedAt = Date.now();
|
|
148588
148757
|
let docsSeen = 0;
|
|
148589
148758
|
let blocksTotal = 0;
|
|
148590
148759
|
let blocksDone = 0;
|
|
@@ -148701,6 +148870,15 @@ async function scanInProcess(repoRoot6, options = {}) {
|
|
|
148701
148870
|
const tWriteStart = perfNow3();
|
|
148702
148871
|
writeScanState(repoRoot6, scanState);
|
|
148703
148872
|
debugLog3(`scan: buildScanState=${(tWriteStart - tBuildStart).toFixed(0)}ms writeScanState=${(perfNow3() - tWriteStart).toFixed(0)}ms`);
|
|
148873
|
+
if (options.source) {
|
|
148874
|
+
await trackEvent("spec_scan", {
|
|
148875
|
+
source: options.source,
|
|
148876
|
+
docsScannedRange: bucketFileCount(result.extract.docsScanned),
|
|
148877
|
+
claimsRange: bucketFileCount(result.extract.claims.length),
|
|
148878
|
+
openConflicts: result.merge.openConflicts.length,
|
|
148879
|
+
durationRange: bucketDuration(Date.now() - startedAt)
|
|
148880
|
+
});
|
|
148881
|
+
}
|
|
148704
148882
|
return { consolidate: result, scanState };
|
|
148705
148883
|
}
|
|
148706
148884
|
async function resolveAllDefaultsInProcess(repoRoot6, options = {}) {
|
|
@@ -148800,6 +148978,7 @@ function legacyVerifyStatePath(repoRoot6) {
|
|
|
148800
148978
|
}
|
|
148801
148979
|
async function verifyInProcess(repoRoot6, options = {}) {
|
|
148802
148980
|
const { tracker } = options;
|
|
148981
|
+
const startedAt = Date.now();
|
|
148803
148982
|
const contractsDir = options.contractsDir ?? path53.join(repoRoot6, ".truecourse", "contracts");
|
|
148804
148983
|
const codeDir = options.codeDir ?? autodetectCodeDir(repoRoot6);
|
|
148805
148984
|
if (!fs46.existsSync(contractsDir)) {
|
|
@@ -148871,6 +149050,16 @@ async function verifyInProcess(repoRoot6, options = {}) {
|
|
|
148871
149050
|
resolverErrors: result.resolverErrors,
|
|
148872
149051
|
unresolvedRefs: result.unresolvedRefs
|
|
148873
149052
|
};
|
|
149053
|
+
if (options.source) {
|
|
149054
|
+
await trackEvent("verify", {
|
|
149055
|
+
source: options.source,
|
|
149056
|
+
mode: "full",
|
|
149057
|
+
artifactCountRange: bucketFileCount(result.artifactCount),
|
|
149058
|
+
operationCountRange: bucketFileCount(result.extractedOperationCount),
|
|
149059
|
+
driftCountRange: bucketFileCount(result.drifts.length),
|
|
149060
|
+
durationRange: bucketDuration(Date.now() - startedAt)
|
|
149061
|
+
});
|
|
149062
|
+
}
|
|
148874
149063
|
return { verify: result, state };
|
|
148875
149064
|
}
|
|
148876
149065
|
async function gitMeta(repoRoot6) {
|
|
@@ -148936,6 +149125,7 @@ async function gitChangedFiles(repoRoot6) {
|
|
|
148936
149125
|
}
|
|
148937
149126
|
async function verifyDiffInProcess(repoRoot6, options = {}) {
|
|
148938
149127
|
const { tracker } = options;
|
|
149128
|
+
const startedAt = Date.now();
|
|
148939
149129
|
const contractsDir = options.contractsDir ?? path53.join(repoRoot6, ".truecourse", "contracts");
|
|
148940
149130
|
const codeDir = options.codeDir ?? autodetectCodeDir(repoRoot6);
|
|
148941
149131
|
if (!await isGitRepo(repoRoot6)) {
|
|
@@ -148983,10 +149173,20 @@ async function verifyDiffInProcess(repoRoot6, options = {}) {
|
|
|
148983
149173
|
};
|
|
148984
149174
|
writeVerifyDiff(repoRoot6, diff);
|
|
148985
149175
|
tracker?.done("compare", `+${added.length} / -${resolved.length} drift${added.length + resolved.length === 1 ? "" : "s"}`);
|
|
149176
|
+
if (options.source) {
|
|
149177
|
+
await trackEvent("verify", {
|
|
149178
|
+
source: options.source,
|
|
149179
|
+
mode: "diff",
|
|
149180
|
+
addedRange: bucketFileCount(added.length),
|
|
149181
|
+
resolvedRange: bucketFileCount(resolved.length),
|
|
149182
|
+
durationRange: bucketDuration(Date.now() - startedAt)
|
|
149183
|
+
});
|
|
149184
|
+
}
|
|
148986
149185
|
return { verify: result, diff };
|
|
148987
149186
|
}
|
|
148988
149187
|
async function inferInProcess(repoRoot6, options = {}) {
|
|
148989
149188
|
const { tracker } = options;
|
|
149189
|
+
const startedAt = Date.now();
|
|
148990
149190
|
const contractsDir = options.contractsDir ?? path53.join(repoRoot6, ".truecourse", "contracts");
|
|
148991
149191
|
const codeDir = options.codeDir ?? autodetectCodeDir(repoRoot6);
|
|
148992
149192
|
tracker?.start("load");
|
|
@@ -149006,6 +149206,14 @@ async function inferInProcess(repoRoot6, options = {}) {
|
|
|
149006
149206
|
dryRun: options.dryRun
|
|
149007
149207
|
});
|
|
149008
149208
|
tracker?.done("write", options.dryRun ? `${proposed.length} would be written` : `${written.length} written`);
|
|
149209
|
+
if (options.source) {
|
|
149210
|
+
await trackEvent("infer", {
|
|
149211
|
+
source: options.source,
|
|
149212
|
+
decisionsRange: bucketFileCount(result.decisions.length),
|
|
149213
|
+
dryRun: !!options.dryRun,
|
|
149214
|
+
durationRange: bucketDuration(Date.now() - startedAt)
|
|
149215
|
+
});
|
|
149216
|
+
}
|
|
149009
149217
|
return { infer: result, written, proposed };
|
|
149010
149218
|
}
|
|
149011
149219
|
function autodetectCodeDir(repoRoot6) {
|
|
@@ -149109,6 +149317,7 @@ function removeManualInclude(repoRoot6, docPath) {
|
|
|
149109
149317
|
}
|
|
149110
149318
|
|
|
149111
149319
|
// tools/cli/src/commands/contracts.ts
|
|
149320
|
+
init_telemetry_service();
|
|
149112
149321
|
init_helpers();
|
|
149113
149322
|
|
|
149114
149323
|
// tools/cli/src/commands/git-guard.ts
|
|
@@ -149123,6 +149332,7 @@ async function requireGitRepo(root) {
|
|
|
149123
149332
|
// tools/cli/src/commands/contracts.ts
|
|
149124
149333
|
async function runContractsGenerate(options = {}) {
|
|
149125
149334
|
const repoRoot6 = options.cwd ?? process.cwd();
|
|
149335
|
+
const startedAt = Date.now();
|
|
149126
149336
|
mt(options.diff ? "Contracts (dry run)" : "Contracts");
|
|
149127
149337
|
await requireGitRepo(repoRoot6);
|
|
149128
149338
|
if (!hasCanonicalSpec(repoRoot6)) {
|
|
@@ -149214,6 +149424,12 @@ async function runContractsGenerate(options = {}) {
|
|
|
149214
149424
|
return;
|
|
149215
149425
|
}
|
|
149216
149426
|
stampGeneratedMarker(repoRoot6);
|
|
149427
|
+
await trackEvent("contracts_generate", {
|
|
149428
|
+
source: "cli",
|
|
149429
|
+
artifactsWrittenRange: bucketFileCount(result.write.written.length),
|
|
149430
|
+
validationIssues: result.validationIssues.length,
|
|
149431
|
+
durationRange: bucketDuration(Date.now() - startedAt)
|
|
149432
|
+
});
|
|
149217
149433
|
if (result.write.written.length === 0) {
|
|
149218
149434
|
gt("Up to date \u2014 run `truecourse verify`.");
|
|
149219
149435
|
return;
|
|
@@ -149406,7 +149622,7 @@ async function runSpecScan(opts = {}) {
|
|
|
149406
149622
|
await requireGitRepo(root);
|
|
149407
149623
|
const { renderer, tracker } = withTracker(SCAN_STEPS);
|
|
149408
149624
|
try {
|
|
149409
|
-
const { consolidate: consolidate2 } = await scanInProcess(root, { tracker });
|
|
149625
|
+
const { consolidate: consolidate2 } = await scanInProcess(root, { tracker, source: "cli" });
|
|
149410
149626
|
renderer.dispose();
|
|
149411
149627
|
const { extract: extract4, merge: merge2 } = consolidate2;
|
|
149412
149628
|
O2.step(`docs ${extract4.docsScanned}`);
|
|
@@ -149503,7 +149719,7 @@ async function runVerify(opts = {}) {
|
|
|
149503
149719
|
await requireGitRepo(root);
|
|
149504
149720
|
const { renderer, tracker } = withTracker(VERIFY_STEPS);
|
|
149505
149721
|
try {
|
|
149506
|
-
const { verify: verify2 } = await verifyInProcess(root, { tracker, codeDir: opts.codeDir, skipStash });
|
|
149722
|
+
const { verify: verify2 } = await verifyInProcess(root, { tracker, codeDir: opts.codeDir, skipStash, source: "cli" });
|
|
149507
149723
|
renderer.dispose();
|
|
149508
149724
|
O2.step(`artifacts ${verify2.artifactCount}`);
|
|
149509
149725
|
O2.step(`operations ${verify2.extractedOperationCount} (extracted from code)`);
|
|
@@ -149542,7 +149758,7 @@ async function runVerifyDiff(opts) {
|
|
|
149542
149758
|
await requireGitRepo(root);
|
|
149543
149759
|
const { renderer, tracker } = withTracker(VERIFY_STEPS);
|
|
149544
149760
|
try {
|
|
149545
|
-
const { diff } = await verifyDiffInProcess(root, { tracker, codeDir: opts.codeDir });
|
|
149761
|
+
const { diff } = await verifyDiffInProcess(root, { tracker, codeDir: opts.codeDir, source: "cli" });
|
|
149546
149762
|
renderer.dispose();
|
|
149547
149763
|
O2.step(`added ${diff.summary.added}`);
|
|
149548
149764
|
O2.step(`resolved ${diff.summary.resolved}`);
|
|
@@ -149576,7 +149792,8 @@ async function runInfer(opts = {}) {
|
|
|
149576
149792
|
const { infer: infer2, written, proposed } = await inferInProcess(root, {
|
|
149577
149793
|
tracker,
|
|
149578
149794
|
codeDir: opts.codeDir,
|
|
149579
|
-
dryRun: opts.dryRun
|
|
149795
|
+
dryRun: opts.dryRun,
|
|
149796
|
+
source: "cli"
|
|
149580
149797
|
});
|
|
149581
149798
|
renderer.dispose();
|
|
149582
149799
|
const byKind = /* @__PURE__ */ new Map();
|
|
@@ -152935,7 +153152,7 @@ async function runHooksRun() {
|
|
|
152935
153152
|
|
|
152936
153153
|
// tools/cli/src/index.ts
|
|
152937
153154
|
var program2 = new Command();
|
|
152938
|
-
program2.name("truecourse").version("0.6.0
|
|
153155
|
+
program2.name("truecourse").version("0.6.0").description("TrueCourse CLI \u2014 analyze your repository and open the dashboard");
|
|
152939
153156
|
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) => {
|
|
152940
153157
|
if (options.service && options.console) {
|
|
152941
153158
|
console.error("error: --service and --console are mutually exclusive");
|
package/package.json
CHANGED
package/server.mjs
CHANGED
|
@@ -42492,10 +42492,10 @@ function computeFunctionMetrics(node2) {
|
|
|
42492
42492
|
if (!bodyNode) {
|
|
42493
42493
|
return { lineCount, statementCount: 0, maxNestingDepth: 0 };
|
|
42494
42494
|
}
|
|
42495
|
-
let
|
|
42495
|
+
let statementCount2 = 0;
|
|
42496
42496
|
for (const child of bodyNode.namedChildren) {
|
|
42497
42497
|
if (STATEMENT_NODE_TYPES.has(child.type)) {
|
|
42498
|
-
|
|
42498
|
+
statementCount2++;
|
|
42499
42499
|
}
|
|
42500
42500
|
}
|
|
42501
42501
|
let maxNestingDepth2 = 0;
|
|
@@ -42512,7 +42512,7 @@ function computeFunctionMetrics(node2) {
|
|
|
42512
42512
|
}
|
|
42513
42513
|
}
|
|
42514
42514
|
walkNesting(bodyNode, 0);
|
|
42515
|
-
return { lineCount, statementCount, maxNestingDepth: maxNestingDepth2 };
|
|
42515
|
+
return { lineCount, statementCount: statementCount2, maxNestingDepth: maxNestingDepth2 };
|
|
42516
42516
|
}
|
|
42517
42517
|
var NESTING_NODE_TYPES, STATEMENT_NODE_TYPES;
|
|
42518
42518
|
var init_common = __esm({
|
|
@@ -43410,10 +43410,10 @@ function computePythonFunctionMetrics(node2) {
|
|
|
43410
43410
|
if (!bodyNode) {
|
|
43411
43411
|
return { lineCount, statementCount: 0, maxNestingDepth: 0 };
|
|
43412
43412
|
}
|
|
43413
|
-
let
|
|
43413
|
+
let statementCount2 = 0;
|
|
43414
43414
|
for (const child of bodyNode.namedChildren) {
|
|
43415
43415
|
if (PYTHON_STATEMENT_NODE_TYPES.has(child.type)) {
|
|
43416
|
-
|
|
43416
|
+
statementCount2++;
|
|
43417
43417
|
}
|
|
43418
43418
|
}
|
|
43419
43419
|
let maxNestingDepth2 = 0;
|
|
@@ -43430,7 +43430,7 @@ function computePythonFunctionMetrics(node2) {
|
|
|
43430
43430
|
}
|
|
43431
43431
|
}
|
|
43432
43432
|
walkNesting(bodyNode, 0);
|
|
43433
|
-
return { lineCount, statementCount, maxNestingDepth: maxNestingDepth2 };
|
|
43433
|
+
return { lineCount, statementCount: statementCount2, maxNestingDepth: maxNestingDepth2 };
|
|
43434
43434
|
}
|
|
43435
43435
|
function isNestedInFunction3(node2) {
|
|
43436
43436
|
let current = node2.parent;
|
|
@@ -79021,6 +79021,19 @@ var init_prototype_pollution = __esm({
|
|
|
79021
79021
|
});
|
|
79022
79022
|
|
|
79023
79023
|
// packages/analyzer/dist/rules/bugs/visitors/javascript/void-zero-argument.js
|
|
79024
|
+
function operandIsCall(operand) {
|
|
79025
|
+
let cur = operand;
|
|
79026
|
+
while (cur) {
|
|
79027
|
+
if (cur.type === "call_expression" || cur.type === "new_expression")
|
|
79028
|
+
return true;
|
|
79029
|
+
if (cur.type === "parenthesized_expression" || cur.type === "await_expression") {
|
|
79030
|
+
cur = cur.namedChildren[cur.namedChildren.length - 1] ?? null;
|
|
79031
|
+
continue;
|
|
79032
|
+
}
|
|
79033
|
+
return false;
|
|
79034
|
+
}
|
|
79035
|
+
return false;
|
|
79036
|
+
}
|
|
79024
79037
|
var voidZeroArgumentVisitor;
|
|
79025
79038
|
var init_void_zero_argument = __esm({
|
|
79026
79039
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/void-zero-argument.js"() {
|
|
@@ -79035,6 +79048,8 @@ var init_void_zero_argument = __esm({
|
|
|
79035
79048
|
const op = node2.children.find((c) => c.text === "void");
|
|
79036
79049
|
if (!op)
|
|
79037
79050
|
return null;
|
|
79051
|
+
if (operandIsCall(node2.children[node2.children.length - 1]))
|
|
79052
|
+
return null;
|
|
79038
79053
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Unnecessary void expression", `\`${node2.text}\` can be replaced with \`undefined\` directly.`, sourceCode, "Use `undefined` instead of `void 0`.");
|
|
79039
79054
|
}
|
|
79040
79055
|
};
|
|
@@ -81857,12 +81872,34 @@ function isModuleLevel(node2) {
|
|
|
81857
81872
|
function isMutableInit(node2) {
|
|
81858
81873
|
return node2.type === "object" || node2.type === "array" || node2.type === "new_expression";
|
|
81859
81874
|
}
|
|
81860
|
-
|
|
81875
|
+
function isClientSideFile(filePath, program) {
|
|
81876
|
+
for (const tok of CLIENT_PATH_TOKENS) {
|
|
81877
|
+
if (filePath.includes(tok))
|
|
81878
|
+
return true;
|
|
81879
|
+
}
|
|
81880
|
+
const first2 = program?.namedChildren[0];
|
|
81881
|
+
if (first2?.type === "expression_statement") {
|
|
81882
|
+
const text = first2.text.trim().replace(/;$/, "").replace(/['"`]/g, "");
|
|
81883
|
+
if (text === "use client")
|
|
81884
|
+
return true;
|
|
81885
|
+
}
|
|
81886
|
+
return false;
|
|
81887
|
+
}
|
|
81888
|
+
var CLIENT_PATH_TOKENS, sharedMutableModuleStateVisitor;
|
|
81861
81889
|
var init_shared_mutable_module_state = __esm({
|
|
81862
81890
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/shared-mutable-module-state.js"() {
|
|
81863
81891
|
"use strict";
|
|
81864
81892
|
init_types2();
|
|
81865
81893
|
init_helpers();
|
|
81894
|
+
CLIENT_PATH_TOKENS = [
|
|
81895
|
+
"/client/",
|
|
81896
|
+
"/client-only/",
|
|
81897
|
+
"/hooks/",
|
|
81898
|
+
"/primitives/",
|
|
81899
|
+
"/components/",
|
|
81900
|
+
".client.ts",
|
|
81901
|
+
".client.tsx"
|
|
81902
|
+
];
|
|
81866
81903
|
sharedMutableModuleStateVisitor = {
|
|
81867
81904
|
ruleKey: "bugs/deterministic/shared-mutable-module-state",
|
|
81868
81905
|
languages: JS_LANGUAGES,
|
|
@@ -81870,6 +81907,11 @@ var init_shared_mutable_module_state = __esm({
|
|
|
81870
81907
|
visit(node2, filePath, sourceCode) {
|
|
81871
81908
|
if (!isModuleLevel(node2))
|
|
81872
81909
|
return null;
|
|
81910
|
+
let program = node2;
|
|
81911
|
+
while (program && program.type !== "program")
|
|
81912
|
+
program = program.parent;
|
|
81913
|
+
if (isClientSideFile(filePath, program))
|
|
81914
|
+
return null;
|
|
81873
81915
|
const kindChild = node2.children[0];
|
|
81874
81916
|
if (!kindChild || kindChild.text === "const")
|
|
81875
81917
|
return null;
|
|
@@ -83958,13 +84000,38 @@ var init_switch_exhaustiveness = __esm({
|
|
|
83958
84000
|
});
|
|
83959
84001
|
|
|
83960
84002
|
// packages/analyzer/dist/rules/bugs/visitors/javascript/non-number-arithmetic.js
|
|
83961
|
-
|
|
84003
|
+
function isConfidentlyNonNumeric(t) {
|
|
84004
|
+
if (CONFIDENTLY_NON_NUMERIC.has(t))
|
|
84005
|
+
return true;
|
|
84006
|
+
if (/^["']/.test(t))
|
|
84007
|
+
return true;
|
|
84008
|
+
if (t === "true" || t === "false")
|
|
84009
|
+
return true;
|
|
84010
|
+
if (/\[\]$/.test(t) || /^readonly\s/.test(t))
|
|
84011
|
+
return true;
|
|
84012
|
+
if (t.includes("=>") || t.startsWith("{"))
|
|
84013
|
+
return true;
|
|
84014
|
+
return false;
|
|
84015
|
+
}
|
|
84016
|
+
var ARITHMETIC_OPS, CONFIDENTLY_NON_NUMERIC, nonNumberArithmeticVisitor;
|
|
83962
84017
|
var init_non_number_arithmetic = __esm({
|
|
83963
84018
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/non-number-arithmetic.js"() {
|
|
83964
84019
|
"use strict";
|
|
83965
84020
|
init_types2();
|
|
83966
84021
|
init_helpers();
|
|
83967
84022
|
ARITHMETIC_OPS = /* @__PURE__ */ new Set(["-", "*", "/", "%", "**"]);
|
|
84023
|
+
CONFIDENTLY_NON_NUMERIC = /* @__PURE__ */ new Set([
|
|
84024
|
+
"string",
|
|
84025
|
+
"boolean",
|
|
84026
|
+
"null",
|
|
84027
|
+
"undefined",
|
|
84028
|
+
"void",
|
|
84029
|
+
"symbol",
|
|
84030
|
+
"never",
|
|
84031
|
+
"object",
|
|
84032
|
+
// Common runtime types that don't auto-coerce to number in arithmetic.
|
|
84033
|
+
"Date"
|
|
84034
|
+
]);
|
|
83968
84035
|
nonNumberArithmeticVisitor = {
|
|
83969
84036
|
ruleKey: "bugs/deterministic/non-number-arithmetic",
|
|
83970
84037
|
languages: TS_LANGUAGES,
|
|
@@ -83984,16 +84051,14 @@ var init_non_number_arithmetic = __esm({
|
|
|
83984
84051
|
const rightType = typeQuery.getTypeAtPosition(filePath, right.startPosition.row, right.startPosition.column, right.endPosition.row, right.endPosition.column);
|
|
83985
84052
|
if (!leftType || !rightType)
|
|
83986
84053
|
return null;
|
|
83987
|
-
const numericTypes = /* @__PURE__ */ new Set(["number", "bigint", "any"]);
|
|
83988
|
-
const leftOk = numericTypes.has(leftType) || /^\d+$/.test(leftType);
|
|
83989
|
-
const rightOk = numericTypes.has(rightType) || /^\d+$/.test(rightType);
|
|
83990
84054
|
if (left.type === "number" || right.type === "number")
|
|
83991
84055
|
return null;
|
|
83992
|
-
|
|
83993
|
-
|
|
83994
|
-
|
|
83995
|
-
|
|
83996
|
-
|
|
84056
|
+
const leftBad = isConfidentlyNonNumeric(leftType);
|
|
84057
|
+
const rightBad = isConfidentlyNonNumeric(rightType);
|
|
84058
|
+
if (!leftBad && !rightBad)
|
|
84059
|
+
return null;
|
|
84060
|
+
const badSide = leftBad ? `left operand is \`${leftType}\`` : `right operand is \`${rightType}\``;
|
|
84061
|
+
return makeViolation(this.ruleKey, node2, filePath, "high", "Non-numeric value in arithmetic", `Arithmetic operator \`${operator.text}\` used with non-numeric operand \u2014 ${badSide}. This will produce \`NaN\` at runtime.`, sourceCode, "Convert operands to numbers or fix the expression.");
|
|
83997
84062
|
}
|
|
83998
84063
|
};
|
|
83999
84064
|
}
|
|
@@ -96852,12 +96917,29 @@ var init_no_var_declaration = __esm({
|
|
|
96852
96917
|
});
|
|
96853
96918
|
|
|
96854
96919
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/cognitive-complexity.js
|
|
96855
|
-
|
|
96920
|
+
function isInsideJsxExpression(node2, functionNodeId) {
|
|
96921
|
+
let cur = node2.parent;
|
|
96922
|
+
while (cur && cur.id !== functionNodeId) {
|
|
96923
|
+
if (JSX_EXPRESSION_TYPES.has(cur.type))
|
|
96924
|
+
return true;
|
|
96925
|
+
if (JS_FUNCTION_TYPES.includes(cur.type))
|
|
96926
|
+
return false;
|
|
96927
|
+
cur = cur.parent;
|
|
96928
|
+
}
|
|
96929
|
+
return false;
|
|
96930
|
+
}
|
|
96931
|
+
var JSX_EXPRESSION_TYPES, cognitiveComplexityVisitor;
|
|
96856
96932
|
var init_cognitive_complexity = __esm({
|
|
96857
96933
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/cognitive-complexity.js"() {
|
|
96858
96934
|
"use strict";
|
|
96859
96935
|
init_types2();
|
|
96860
96936
|
init_helpers3();
|
|
96937
|
+
JSX_EXPRESSION_TYPES = /* @__PURE__ */ new Set([
|
|
96938
|
+
"jsx_expression",
|
|
96939
|
+
// {expr} attribute / child
|
|
96940
|
+
"jsx_attribute"
|
|
96941
|
+
// attr={expr}
|
|
96942
|
+
]);
|
|
96861
96943
|
cognitiveComplexityVisitor = {
|
|
96862
96944
|
ruleKey: "code-quality/deterministic/cognitive-complexity",
|
|
96863
96945
|
languages: ["typescript", "tsx", "javascript"],
|
|
@@ -96873,14 +96955,17 @@ var init_cognitive_complexity = __esm({
|
|
|
96873
96955
|
if (JS_FUNCTION_TYPES.includes(n.type) && n.id !== node2.id)
|
|
96874
96956
|
return;
|
|
96875
96957
|
if (INCREMENT_TYPES.has(n.type)) {
|
|
96876
|
-
|
|
96958
|
+
if (n.type === "ternary_expression" && isInsideJsxExpression(n, node2.id)) {
|
|
96959
|
+
} else {
|
|
96960
|
+
complexity += 1 + nesting;
|
|
96961
|
+
}
|
|
96877
96962
|
}
|
|
96878
96963
|
if (n.type === "else_clause") {
|
|
96879
96964
|
complexity += 1;
|
|
96880
96965
|
}
|
|
96881
96966
|
if (n.type === "binary_expression") {
|
|
96882
96967
|
const op = n.children.find((c) => c.type === "&&" || c.type === "||");
|
|
96883
|
-
if (op)
|
|
96968
|
+
if (op && !isInsideJsxExpression(n, node2.id))
|
|
96884
96969
|
complexity += 1;
|
|
96885
96970
|
}
|
|
96886
96971
|
const nextNesting = NESTING_TYPES3.has(n.type) ? nesting + 1 : nesting;
|
|
@@ -97392,6 +97477,19 @@ var init_no_proto = __esm({
|
|
|
97392
97477
|
});
|
|
97393
97478
|
|
|
97394
97479
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/no-void.js
|
|
97480
|
+
function operandIsCall2(operand) {
|
|
97481
|
+
let cur = operand;
|
|
97482
|
+
while (cur) {
|
|
97483
|
+
if (cur.type === "call_expression" || cur.type === "new_expression")
|
|
97484
|
+
return true;
|
|
97485
|
+
if (cur.type === "parenthesized_expression" || cur.type === "await_expression") {
|
|
97486
|
+
cur = cur.namedChildren[cur.namedChildren.length - 1] ?? null;
|
|
97487
|
+
continue;
|
|
97488
|
+
}
|
|
97489
|
+
return false;
|
|
97490
|
+
}
|
|
97491
|
+
return false;
|
|
97492
|
+
}
|
|
97395
97493
|
var noVoidVisitor;
|
|
97396
97494
|
var init_no_void = __esm({
|
|
97397
97495
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/no-void.js"() {
|
|
@@ -97408,6 +97506,8 @@ var init_no_void = __esm({
|
|
|
97408
97506
|
const operand = node2.children[1];
|
|
97409
97507
|
if (operand?.text === "0")
|
|
97410
97508
|
return null;
|
|
97509
|
+
if (operandIsCall2(operand))
|
|
97510
|
+
return null;
|
|
97411
97511
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Void expression", "The `void` operator is confusing. Use `undefined` directly or omit the return value.", sourceCode, "Replace `void expr` with `undefined` or remove the expression.");
|
|
97412
97512
|
}
|
|
97413
97513
|
};
|
|
@@ -100942,7 +101042,7 @@ var init_unnamed_regex_capture = __esm({
|
|
|
100942
101042
|
visit(node2, filePath, sourceCode) {
|
|
100943
101043
|
const pattern = node2.namedChildren.find((c) => c.type === "regex_pattern");
|
|
100944
101044
|
const src = pattern?.text ?? "";
|
|
100945
|
-
let
|
|
101045
|
+
let unnamedCount = 0;
|
|
100946
101046
|
for (let i = 0; i < src.length; i++) {
|
|
100947
101047
|
if (src[i] === "\\") {
|
|
100948
101048
|
i++;
|
|
@@ -100965,19 +101065,22 @@ var init_unnamed_regex_capture = __esm({
|
|
|
100965
101065
|
j++;
|
|
100966
101066
|
}
|
|
100967
101067
|
const groupContent = src.slice(i + 1, j - 1);
|
|
101068
|
+
const after = src[j];
|
|
101069
|
+
if (after === "+" || after === "*" || after === "?" || after === "{") {
|
|
101070
|
+
continue;
|
|
101071
|
+
}
|
|
100968
101072
|
if (groupContent.includes("|")) {
|
|
100969
101073
|
const alternatives = groupContent.split("|");
|
|
100970
101074
|
const isSimpleAlternation = alternatives.every((alt) => /^[a-zA-Z0-9_\\?.^$*+\-]+$/.test(alt) && alt.length <= 10);
|
|
100971
101075
|
if (isSimpleAlternation)
|
|
100972
101076
|
continue;
|
|
100973
101077
|
}
|
|
100974
|
-
|
|
100975
|
-
break;
|
|
101078
|
+
unnamedCount++;
|
|
100976
101079
|
}
|
|
100977
101080
|
}
|
|
100978
101081
|
}
|
|
100979
|
-
if (
|
|
100980
|
-
return makeViolation(this.ruleKey, node2, filePath, "low", "Unnamed capture
|
|
101082
|
+
if (unnamedCount >= 2) {
|
|
101083
|
+
return makeViolation(this.ruleKey, node2, filePath, "low", "Unnamed capture groups", "Regex contains multiple unnamed capture groups. Use named groups `(?<name>...)` for better readability.", sourceCode, "Convert capture groups to named: `(?<name>...)` or non-capturing: `(?:...)`.");
|
|
100981
101084
|
}
|
|
100982
101085
|
return null;
|
|
100983
101086
|
}
|
|
@@ -101466,6 +101569,25 @@ var init_computed_enum_value = __esm({
|
|
|
101466
101569
|
});
|
|
101467
101570
|
|
|
101468
101571
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/useless-empty-export.js
|
|
101572
|
+
function fileHasOtherTopLevelModuleStatement(emptyExport) {
|
|
101573
|
+
let program = emptyExport;
|
|
101574
|
+
while (program && program.type !== "program")
|
|
101575
|
+
program = program.parent;
|
|
101576
|
+
if (!program)
|
|
101577
|
+
return false;
|
|
101578
|
+
for (const child of program.namedChildren) {
|
|
101579
|
+
if (child.id === emptyExport.id)
|
|
101580
|
+
continue;
|
|
101581
|
+
if (child.type === "import_statement")
|
|
101582
|
+
return true;
|
|
101583
|
+
if (child.type === "export_statement") {
|
|
101584
|
+
const inner = child.namedChildren.find((c) => c.type === "named_exports" || c.type === "export_clause");
|
|
101585
|
+
if (!inner || inner.namedChildCount > 0)
|
|
101586
|
+
return true;
|
|
101587
|
+
}
|
|
101588
|
+
}
|
|
101589
|
+
return false;
|
|
101590
|
+
}
|
|
101469
101591
|
var uselessEmptyExportVisitor;
|
|
101470
101592
|
var init_useless_empty_export = __esm({
|
|
101471
101593
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/useless-empty-export.js"() {
|
|
@@ -101479,10 +101601,11 @@ var init_useless_empty_export = __esm({
|
|
|
101479
101601
|
const namedExports = node2.namedChildren.find((c) => c.type === "named_exports" || c.type === "export_clause");
|
|
101480
101602
|
if (!namedExports)
|
|
101481
101603
|
return null;
|
|
101482
|
-
if (namedExports.namedChildCount
|
|
101483
|
-
return
|
|
101484
|
-
|
|
101485
|
-
|
|
101604
|
+
if (namedExports.namedChildCount !== 0)
|
|
101605
|
+
return null;
|
|
101606
|
+
if (!fileHasOtherTopLevelModuleStatement(node2))
|
|
101607
|
+
return null;
|
|
101608
|
+
return makeViolation(this.ruleKey, node2, filePath, "low", "Useless empty export", "`export {}` does nothing useful. Remove it unless it is needed to mark the file as a module.", sourceCode, "Remove the empty `export {}` statement.");
|
|
101486
101609
|
}
|
|
101487
101610
|
};
|
|
101488
101611
|
}
|
|
@@ -101786,6 +101909,14 @@ var init_multiline_block_without_braces = __esm({
|
|
|
101786
101909
|
});
|
|
101787
101910
|
|
|
101788
101911
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/negated-condition.js
|
|
101912
|
+
function statementCount(body) {
|
|
101913
|
+
if (!body)
|
|
101914
|
+
return 0;
|
|
101915
|
+
if (body.type === "statement_block") {
|
|
101916
|
+
return body.namedChildren.filter((c) => c.type !== "comment").length;
|
|
101917
|
+
}
|
|
101918
|
+
return 1;
|
|
101919
|
+
}
|
|
101789
101920
|
var negatedConditionVisitor;
|
|
101790
101921
|
var init_negated_condition = __esm({
|
|
101791
101922
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/negated-condition.js"() {
|
|
@@ -101809,6 +101940,11 @@ var init_negated_condition = __esm({
|
|
|
101809
101940
|
const elseBody = elsePart.namedChildren[0];
|
|
101810
101941
|
if (elseBody?.type === "if_statement")
|
|
101811
101942
|
return null;
|
|
101943
|
+
const ifBody = node2.childForFieldName("consequence");
|
|
101944
|
+
const ifCount = statementCount(ifBody);
|
|
101945
|
+
const elseCount = statementCount(elseBody);
|
|
101946
|
+
if (elseCount < ifCount + 2)
|
|
101947
|
+
return null;
|
|
101812
101948
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Negated condition with else", "Condition is negated but has an else block. Invert the condition and swap the branches for better readability.", sourceCode, "Invert the condition and swap the if/else bodies.");
|
|
101813
101949
|
}
|
|
101814
101950
|
};
|
|
@@ -103379,6 +103515,15 @@ var init_undefined_as_identifier = __esm({
|
|
|
103379
103515
|
});
|
|
103380
103516
|
|
|
103381
103517
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/require-unicode-regexp.js
|
|
103518
|
+
function unicodeFlagWouldMatter(pattern) {
|
|
103519
|
+
if (/[^\x00-\x7F]/.test(pattern))
|
|
103520
|
+
return true;
|
|
103521
|
+
if (/\\u\{/.test(pattern))
|
|
103522
|
+
return true;
|
|
103523
|
+
if (/\\[pP]\{/.test(pattern))
|
|
103524
|
+
return true;
|
|
103525
|
+
return false;
|
|
103526
|
+
}
|
|
103382
103527
|
var requireUnicodeRegexpVisitor;
|
|
103383
103528
|
var init_require_unicode_regexp = __esm({
|
|
103384
103529
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/require-unicode-regexp.js"() {
|
|
@@ -103393,6 +103538,9 @@ var init_require_unicode_regexp = __esm({
|
|
|
103393
103538
|
const flagText = flags?.text ?? "";
|
|
103394
103539
|
if (flagText.includes("u") || flagText.includes("v"))
|
|
103395
103540
|
return null;
|
|
103541
|
+
const pattern = node2.namedChildren.find((c) => c.type === "regex_pattern")?.text ?? "";
|
|
103542
|
+
if (!unicodeFlagWouldMatter(pattern))
|
|
103543
|
+
return null;
|
|
103396
103544
|
return makeViolation(this.ruleKey, node2, filePath, "low", "RegExp missing unicode flag", "Regular expression should use the `u` or `v` flag for correct Unicode character handling.", sourceCode, `Add the \`u\` flag: ${node2.text}u`);
|
|
103397
103545
|
}
|
|
103398
103546
|
};
|
|
@@ -107258,6 +107406,18 @@ var init_readonly_parameter_types = __esm({
|
|
|
107258
107406
|
});
|
|
107259
107407
|
|
|
107260
107408
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/complex-type-alias.js
|
|
107409
|
+
function flattenUnionMembers(node2) {
|
|
107410
|
+
if (node2.type !== "union_type")
|
|
107411
|
+
return [node2];
|
|
107412
|
+
const out = [];
|
|
107413
|
+
for (const child of node2.namedChildren) {
|
|
107414
|
+
if (child.type === "union_type")
|
|
107415
|
+
out.push(...flattenUnionMembers(child));
|
|
107416
|
+
else
|
|
107417
|
+
out.push(child);
|
|
107418
|
+
}
|
|
107419
|
+
return out;
|
|
107420
|
+
}
|
|
107261
107421
|
function maxBracketDepth(text) {
|
|
107262
107422
|
let depth = 0;
|
|
107263
107423
|
let max = 0;
|
|
@@ -107285,11 +107445,20 @@ function countUnionIntersectionMembers(text) {
|
|
|
107285
107445
|
}
|
|
107286
107446
|
return count;
|
|
107287
107447
|
}
|
|
107288
|
-
var DEPTH_THRESHOLD, MEMBER_THRESHOLD, complexTypeAliasVisitor;
|
|
107448
|
+
var SIMPLE_UNION_MEMBER_TYPES, DEPTH_THRESHOLD, MEMBER_THRESHOLD, complexTypeAliasVisitor;
|
|
107289
107449
|
var init_complex_type_alias = __esm({
|
|
107290
107450
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/complex-type-alias.js"() {
|
|
107291
107451
|
"use strict";
|
|
107292
107452
|
init_types2();
|
|
107453
|
+
SIMPLE_UNION_MEMBER_TYPES = /* @__PURE__ */ new Set([
|
|
107454
|
+
"literal_type",
|
|
107455
|
+
"string",
|
|
107456
|
+
"number",
|
|
107457
|
+
"type_identifier",
|
|
107458
|
+
"predefined_type",
|
|
107459
|
+
"null",
|
|
107460
|
+
"undefined"
|
|
107461
|
+
]);
|
|
107293
107462
|
DEPTH_THRESHOLD = 4;
|
|
107294
107463
|
MEMBER_THRESHOLD = 6;
|
|
107295
107464
|
complexTypeAliasVisitor = {
|
|
@@ -107304,11 +107473,9 @@ var init_complex_type_alias = __esm({
|
|
|
107304
107473
|
const depth = maxBracketDepth(typeText);
|
|
107305
107474
|
const members = countUnionIntersectionMembers(typeText);
|
|
107306
107475
|
if (typeValue.type === "union_type") {
|
|
107307
|
-
const
|
|
107308
|
-
|
|
107309
|
-
|
|
107310
|
-
});
|
|
107311
|
-
if (allLiterals)
|
|
107476
|
+
const members2 = flattenUnionMembers(typeValue);
|
|
107477
|
+
const allSimple = members2.every((m) => SIMPLE_UNION_MEMBER_TYPES.has(m.type));
|
|
107478
|
+
if (allSimple)
|
|
107312
107479
|
return null;
|
|
107313
107480
|
}
|
|
107314
107481
|
if (depth >= DEPTH_THRESHOLD || members >= MEMBER_THRESHOLD) {
|
|
@@ -156921,7 +157088,7 @@ function readToolVersion() {
|
|
|
156921
157088
|
if (cachedVersion)
|
|
156922
157089
|
return cachedVersion;
|
|
156923
157090
|
if (true) {
|
|
156924
|
-
cachedVersion = "0.6.0
|
|
157091
|
+
cachedVersion = "0.6.0";
|
|
156925
157092
|
return cachedVersion;
|
|
156926
157093
|
}
|
|
156927
157094
|
try {
|
|
@@ -176027,6 +176194,7 @@ function buildScanState(result) {
|
|
|
176027
176194
|
}
|
|
176028
176195
|
async function scanInProcess(repoRoot, options = {}) {
|
|
176029
176196
|
const { tracker } = options;
|
|
176197
|
+
const startedAt = Date.now();
|
|
176030
176198
|
let docsSeen = 0;
|
|
176031
176199
|
let blocksTotal = 0;
|
|
176032
176200
|
let blocksDone = 0;
|
|
@@ -176143,6 +176311,15 @@ async function scanInProcess(repoRoot, options = {}) {
|
|
|
176143
176311
|
const tWriteStart = perfNow3();
|
|
176144
176312
|
writeScanState(repoRoot, scanState);
|
|
176145
176313
|
debugLog3(`scan: buildScanState=${(tWriteStart - tBuildStart).toFixed(0)}ms writeScanState=${(perfNow3() - tWriteStart).toFixed(0)}ms`);
|
|
176314
|
+
if (options.source) {
|
|
176315
|
+
await trackEvent("spec_scan", {
|
|
176316
|
+
source: options.source,
|
|
176317
|
+
docsScannedRange: bucketFileCount(result.extract.docsScanned),
|
|
176318
|
+
claimsRange: bucketFileCount(result.extract.claims.length),
|
|
176319
|
+
openConflicts: result.merge.openConflicts.length,
|
|
176320
|
+
durationRange: bucketDuration(Date.now() - startedAt)
|
|
176321
|
+
});
|
|
176322
|
+
}
|
|
176146
176323
|
return { consolidate: result, scanState };
|
|
176147
176324
|
}
|
|
176148
176325
|
async function resolveAllDefaultsInProcess(repoRoot, options = {}) {
|
|
@@ -176238,6 +176415,7 @@ function isChainConflict2(c) {
|
|
|
176238
176415
|
}
|
|
176239
176416
|
async function generateContractsInProcess(repoRoot, options = {}) {
|
|
176240
176417
|
const { tracker } = options;
|
|
176418
|
+
const startedAt = Date.now();
|
|
176241
176419
|
if (!hasCanonicalSpec(repoRoot)) {
|
|
176242
176420
|
tracker?.start("il");
|
|
176243
176421
|
tracker?.done("il", "skipped \u2014 no canonical spec");
|
|
@@ -176285,6 +176463,14 @@ async function generateContractsInProcess(repoRoot, options = {}) {
|
|
|
176285
176463
|
if (issueCount === 0) {
|
|
176286
176464
|
stampGeneratedMarker(repoRoot);
|
|
176287
176465
|
}
|
|
176466
|
+
if (options.source) {
|
|
176467
|
+
await trackEvent("contracts_generate", {
|
|
176468
|
+
source: options.source,
|
|
176469
|
+
artifactsWrittenRange: bucketFileCount(wrote),
|
|
176470
|
+
validationIssues: issueCount,
|
|
176471
|
+
durationRange: bucketDuration(Date.now() - startedAt)
|
|
176472
|
+
});
|
|
176473
|
+
}
|
|
176288
176474
|
return { il: { kind: "extracted", result: il } };
|
|
176289
176475
|
} catch (e) {
|
|
176290
176476
|
tracker?.error("il", e.message);
|
|
@@ -176331,6 +176517,7 @@ function readVerifyRunState(repoRoot, runId) {
|
|
|
176331
176517
|
}
|
|
176332
176518
|
async function verifyInProcess(repoRoot, options = {}) {
|
|
176333
176519
|
const { tracker } = options;
|
|
176520
|
+
const startedAt = Date.now();
|
|
176334
176521
|
const contractsDir = options.contractsDir ?? path45.join(repoRoot, ".truecourse", "contracts");
|
|
176335
176522
|
const codeDir = options.codeDir ?? autodetectCodeDir(repoRoot);
|
|
176336
176523
|
if (!fs41.existsSync(contractsDir)) {
|
|
@@ -176402,6 +176589,16 @@ async function verifyInProcess(repoRoot, options = {}) {
|
|
|
176402
176589
|
resolverErrors: result.resolverErrors,
|
|
176403
176590
|
unresolvedRefs: result.unresolvedRefs
|
|
176404
176591
|
};
|
|
176592
|
+
if (options.source) {
|
|
176593
|
+
await trackEvent("verify", {
|
|
176594
|
+
source: options.source,
|
|
176595
|
+
mode: "full",
|
|
176596
|
+
artifactCountRange: bucketFileCount(result.artifactCount),
|
|
176597
|
+
operationCountRange: bucketFileCount(result.extractedOperationCount),
|
|
176598
|
+
driftCountRange: bucketFileCount(result.drifts.length),
|
|
176599
|
+
durationRange: bucketDuration(Date.now() - startedAt)
|
|
176600
|
+
});
|
|
176601
|
+
}
|
|
176405
176602
|
return { verify: result, state };
|
|
176406
176603
|
}
|
|
176407
176604
|
async function gitMeta(repoRoot) {
|
|
@@ -176467,6 +176664,7 @@ async function gitChangedFiles(repoRoot) {
|
|
|
176467
176664
|
}
|
|
176468
176665
|
async function verifyDiffInProcess(repoRoot, options = {}) {
|
|
176469
176666
|
const { tracker } = options;
|
|
176667
|
+
const startedAt = Date.now();
|
|
176470
176668
|
const contractsDir = options.contractsDir ?? path45.join(repoRoot, ".truecourse", "contracts");
|
|
176471
176669
|
const codeDir = options.codeDir ?? autodetectCodeDir(repoRoot);
|
|
176472
176670
|
if (!await isGitRepo(repoRoot)) {
|
|
@@ -176514,6 +176712,15 @@ async function verifyDiffInProcess(repoRoot, options = {}) {
|
|
|
176514
176712
|
};
|
|
176515
176713
|
writeVerifyDiff(repoRoot, diff);
|
|
176516
176714
|
tracker?.done("compare", `+${added.length} / -${resolved.length} drift${added.length + resolved.length === 1 ? "" : "s"}`);
|
|
176715
|
+
if (options.source) {
|
|
176716
|
+
await trackEvent("verify", {
|
|
176717
|
+
source: options.source,
|
|
176718
|
+
mode: "diff",
|
|
176719
|
+
addedRange: bucketFileCount(added.length),
|
|
176720
|
+
resolvedRange: bucketFileCount(resolved.length),
|
|
176721
|
+
durationRange: bucketDuration(Date.now() - startedAt)
|
|
176722
|
+
});
|
|
176723
|
+
}
|
|
176517
176724
|
return { verify: result, diff };
|
|
176518
176725
|
}
|
|
176519
176726
|
function autodetectCodeDir(repoRoot) {
|
|
@@ -176646,7 +176853,7 @@ router10.get(
|
|
|
176646
176853
|
return;
|
|
176647
176854
|
}
|
|
176648
176855
|
const tracker = createSocketSpecTracker(repoIdForCleanup, SCAN_STEPS.map((s) => ({ ...s })));
|
|
176649
|
-
const { scanState } = await scanInProcess(repo.path, { tracker });
|
|
176856
|
+
const { scanState } = await scanInProcess(repo.path, { tracker, source: "dashboard" });
|
|
176650
176857
|
emitSpecComplete(repoIdForCleanup, "scan");
|
|
176651
176858
|
res.json(scanState);
|
|
176652
176859
|
} catch (e) {
|
|
@@ -176995,7 +177202,7 @@ router11.post(
|
|
|
176995
177202
|
repoIdForCleanup,
|
|
176996
177203
|
GENERATE_STEPS.map((s) => ({ ...s }))
|
|
176997
177204
|
);
|
|
176998
|
-
const outcome = await generateContractsInProcess(repo.path, { tracker });
|
|
177205
|
+
const outcome = await generateContractsInProcess(repo.path, { tracker, source: "dashboard" });
|
|
176999
177206
|
const response = {};
|
|
177000
177207
|
if (outcome.il.kind === "extracted") {
|
|
177001
177208
|
response.il = {
|
|
@@ -177082,7 +177289,8 @@ router12.post(
|
|
|
177082
177289
|
);
|
|
177083
177290
|
const { state } = await verifyInProcess(repo.path, {
|
|
177084
177291
|
tracker,
|
|
177085
|
-
skipStash: stashDecision === "no-stash"
|
|
177292
|
+
skipStash: stashDecision === "no-stash",
|
|
177293
|
+
source: "dashboard"
|
|
177086
177294
|
});
|
|
177087
177295
|
emitSpecComplete(repoIdForCleanup, "verify");
|
|
177088
177296
|
res.json(state);
|
|
@@ -177168,7 +177376,7 @@ router12.post(
|
|
|
177168
177376
|
repoIdForCleanup,
|
|
177169
177377
|
VERIFY_STEPS.map((s) => ({ ...s }))
|
|
177170
177378
|
);
|
|
177171
|
-
const { diff } = await verifyDiffInProcess(repo.path, { tracker });
|
|
177379
|
+
const { diff } = await verifyDiffInProcess(repo.path, { tracker, source: "dashboard" });
|
|
177172
177380
|
emitSpecComplete(repoIdForCleanup, "verify");
|
|
177173
177381
|
res.json(diff);
|
|
177174
177382
|
} catch (e) {
|