@lbroth/rothunter 1.0.0-rc.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/LICENSE +21 -0
- package/README.md +141 -0
- package/dist/adapters/llm.d.ts +68 -0
- package/dist/adapters/llm.d.ts.map +1 -0
- package/dist/adapters/llm.js +189 -0
- package/dist/adapters/llm.js.map +1 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +81 -0
- package/dist/config.js.map +1 -0
- package/dist/detector-registry.d.ts +32 -0
- package/dist/detector-registry.d.ts.map +1 -0
- package/dist/detector-registry.js +74 -0
- package/dist/detector-registry.js.map +1 -0
- package/dist/detectors/api-race.d.ts +6 -0
- package/dist/detectors/api-race.d.ts.map +1 -0
- package/dist/detectors/api-race.js +222 -0
- package/dist/detectors/api-race.js.map +1 -0
- package/dist/detectors/bad-config.d.ts +6 -0
- package/dist/detectors/bad-config.d.ts.map +1 -0
- package/dist/detectors/bad-config.js +529 -0
- package/dist/detectors/bad-config.js.map +1 -0
- package/dist/detectors/console-log-prod.d.ts +6 -0
- package/dist/detectors/console-log-prod.d.ts.map +1 -0
- package/dist/detectors/console-log-prod.js +72 -0
- package/dist/detectors/console-log-prod.js.map +1 -0
- package/dist/detectors/dead-api.d.ts +10 -0
- package/dist/detectors/dead-api.d.ts.map +1 -0
- package/dist/detectors/dead-api.js +115 -0
- package/dist/detectors/dead-api.js.map +1 -0
- package/dist/detectors/dead-export.d.ts +12 -0
- package/dist/detectors/dead-export.d.ts.map +1 -0
- package/dist/detectors/dead-export.js +140 -0
- package/dist/detectors/dead-export.js.map +1 -0
- package/dist/detectors/dead-handler.d.ts +12 -0
- package/dist/detectors/dead-handler.d.ts.map +1 -0
- package/dist/detectors/dead-handler.js +40 -0
- package/dist/detectors/dead-handler.js.map +1 -0
- package/dist/detectors/dead-module.d.ts +14 -0
- package/dist/detectors/dead-module.d.ts.map +1 -0
- package/dist/detectors/dead-module.js +50 -0
- package/dist/detectors/dead-module.js.map +1 -0
- package/dist/detectors/deep-nesting.d.ts +12 -0
- package/dist/detectors/deep-nesting.d.ts.map +1 -0
- package/dist/detectors/deep-nesting.js +133 -0
- package/dist/detectors/deep-nesting.js.map +1 -0
- package/dist/detectors/duplicate-function.d.ts +9 -0
- package/dist/detectors/duplicate-function.d.ts.map +1 -0
- package/dist/detectors/duplicate-function.js +199 -0
- package/dist/detectors/duplicate-function.js.map +1 -0
- package/dist/detectors/duplicate-type.d.ts +9 -0
- package/dist/detectors/duplicate-type.d.ts.map +1 -0
- package/dist/detectors/duplicate-type.js +166 -0
- package/dist/detectors/duplicate-type.js.map +1 -0
- package/dist/detectors/hot-hub-file.d.ts +11 -0
- package/dist/detectors/hot-hub-file.d.ts.map +1 -0
- package/dist/detectors/hot-hub-file.js +42 -0
- package/dist/detectors/hot-hub-file.js.map +1 -0
- package/dist/detectors/long-file.d.ts +12 -0
- package/dist/detectors/long-file.d.ts.map +1 -0
- package/dist/detectors/long-file.js +82 -0
- package/dist/detectors/long-file.js.map +1 -0
- package/dist/detectors/long-function.d.ts +12 -0
- package/dist/detectors/long-function.d.ts.map +1 -0
- package/dist/detectors/long-function.js +45 -0
- package/dist/detectors/long-function.js.map +1 -0
- package/dist/detectors/magic-numbers.d.ts +10 -0
- package/dist/detectors/magic-numbers.d.ts.map +1 -0
- package/dist/detectors/magic-numbers.js +332 -0
- package/dist/detectors/magic-numbers.js.map +1 -0
- package/dist/detectors/mutable-globals.d.ts +6 -0
- package/dist/detectors/mutable-globals.d.ts.map +1 -0
- package/dist/detectors/mutable-globals.js +95 -0
- package/dist/detectors/mutable-globals.js.map +1 -0
- package/dist/detectors/mutation.d.ts +11 -0
- package/dist/detectors/mutation.d.ts.map +1 -0
- package/dist/detectors/mutation.js +397 -0
- package/dist/detectors/mutation.js.map +1 -0
- package/dist/detectors/public-any.d.ts +6 -0
- package/dist/detectors/public-any.d.ts.map +1 -0
- package/dist/detectors/public-any.js +52 -0
- package/dist/detectors/public-any.js.map +1 -0
- package/dist/detectors/race-condition.d.ts +6 -0
- package/dist/detectors/race-condition.d.ts.map +1 -0
- package/dist/detectors/race-condition.js +608 -0
- package/dist/detectors/race-condition.js.map +1 -0
- package/dist/detectors/shared-db-write.d.ts +6 -0
- package/dist/detectors/shared-db-write.d.ts.map +1 -0
- package/dist/detectors/shared-db-write.js +656 -0
- package/dist/detectors/shared-db-write.js.map +1 -0
- package/dist/detectors/silent-catch.d.ts +6 -0
- package/dist/detectors/silent-catch.d.ts.map +1 -0
- package/dist/detectors/silent-catch.js +167 -0
- package/dist/detectors/silent-catch.js.map +1 -0
- package/dist/detectors/similar-functions.d.ts +15 -0
- package/dist/detectors/similar-functions.d.ts.map +1 -0
- package/dist/detectors/similar-functions.js +334 -0
- package/dist/detectors/similar-functions.js.map +1 -0
- package/dist/detectors/skip-tests.d.ts +6 -0
- package/dist/detectors/skip-tests.d.ts.map +1 -0
- package/dist/detectors/skip-tests.js +69 -0
- package/dist/detectors/skip-tests.js.map +1 -0
- package/dist/detectors/todo-comments.d.ts +29 -0
- package/dist/detectors/todo-comments.d.ts.map +1 -0
- package/dist/detectors/todo-comments.js +154 -0
- package/dist/detectors/todo-comments.js.map +1 -0
- package/dist/detectors/unused-deps.d.ts +8 -0
- package/dist/detectors/unused-deps.d.ts.map +1 -0
- package/dist/detectors/unused-deps.js +115 -0
- package/dist/detectors/unused-deps.js.map +1 -0
- package/dist/extraction/api-race-confirmer.d.ts +31 -0
- package/dist/extraction/api-race-confirmer.d.ts.map +1 -0
- package/dist/extraction/api-race-confirmer.js +110 -0
- package/dist/extraction/api-race-confirmer.js.map +1 -0
- package/dist/extraction/llm-confirmer.d.ts +25 -0
- package/dist/extraction/llm-confirmer.d.ts.map +1 -0
- package/dist/extraction/llm-confirmer.js +118 -0
- package/dist/extraction/llm-confirmer.js.map +1 -0
- package/dist/extraction/mutation-confirmer.d.ts +30 -0
- package/dist/extraction/mutation-confirmer.d.ts.map +1 -0
- package/dist/extraction/mutation-confirmer.js +73 -0
- package/dist/extraction/mutation-confirmer.js.map +1 -0
- package/dist/extraction/prompt-chunking.d.ts +37 -0
- package/dist/extraction/prompt-chunking.d.ts.map +1 -0
- package/dist/extraction/prompt-chunking.js +61 -0
- package/dist/extraction/prompt-chunking.js.map +1 -0
- package/dist/extraction/race-confirmer.d.ts +28 -0
- package/dist/extraction/race-confirmer.d.ts.map +1 -0
- package/dist/extraction/race-confirmer.js +68 -0
- package/dist/extraction/race-confirmer.js.map +1 -0
- package/dist/extraction/shared-db-write-confirmer.d.ts +31 -0
- package/dist/extraction/shared-db-write-confirmer.d.ts.map +1 -0
- package/dist/extraction/shared-db-write-confirmer.js +141 -0
- package/dist/extraction/shared-db-write-confirmer.js.map +1 -0
- package/dist/extraction/triage-confirmer.d.ts +59 -0
- package/dist/extraction/triage-confirmer.d.ts.map +1 -0
- package/dist/extraction/triage-confirmer.js +104 -0
- package/dist/extraction/triage-confirmer.js.map +1 -0
- package/dist/graph/cfg.d.ts +45 -0
- package/dist/graph/cfg.d.ts.map +1 -0
- package/dist/graph/cfg.js +198 -0
- package/dist/graph/cfg.js.map +1 -0
- package/dist/graph/decorator-entries.d.ts +2 -0
- package/dist/graph/decorator-entries.d.ts.map +1 -0
- package/dist/graph/decorator-entries.js +89 -0
- package/dist/graph/decorator-entries.js.map +1 -0
- package/dist/graph/entry-points.d.ts +12 -0
- package/dist/graph/entry-points.d.ts.map +1 -0
- package/dist/graph/entry-points.js +282 -0
- package/dist/graph/entry-points.js.map +1 -0
- package/dist/graph/handler-conventions.d.ts +2 -0
- package/dist/graph/handler-conventions.d.ts.map +1 -0
- package/dist/graph/handler-conventions.js +26 -0
- package/dist/graph/handler-conventions.js.map +1 -0
- package/dist/graph/iac-entries.d.ts +2 -0
- package/dist/graph/iac-entries.d.ts.map +1 -0
- package/dist/graph/iac-entries.js +123 -0
- package/dist/graph/iac-entries.js.map +1 -0
- package/dist/graph/import-graph.d.ts +48 -0
- package/dist/graph/import-graph.d.ts.map +1 -0
- package/dist/graph/import-graph.js +86 -0
- package/dist/graph/import-graph.js.map +1 -0
- package/dist/graph/monorepo-detect.d.ts +3 -0
- package/dist/graph/monorepo-detect.d.ts.map +1 -0
- package/dist/graph/monorepo-detect.js +166 -0
- package/dist/graph/monorepo-detect.js.map +1 -0
- package/dist/graph/tsconfig-paths.d.ts +23 -0
- package/dist/graph/tsconfig-paths.d.ts.map +1 -0
- package/dist/graph/tsconfig-paths.js +217 -0
- package/dist/graph/tsconfig-paths.js.map +1 -0
- package/dist/multi-workspace-scanner.d.ts +13 -0
- package/dist/multi-workspace-scanner.d.ts.map +1 -0
- package/dist/multi-workspace-scanner.js +130 -0
- package/dist/multi-workspace-scanner.js.map +1 -0
- package/dist/normalizers/type-normalizer.d.ts +16 -0
- package/dist/normalizers/type-normalizer.d.ts.map +1 -0
- package/dist/normalizers/type-normalizer.js +189 -0
- package/dist/normalizers/type-normalizer.js.map +1 -0
- package/dist/parsers/typescript-parser.d.ts +57 -0
- package/dist/parsers/typescript-parser.d.ts.map +1 -0
- package/dist/parsers/typescript-parser.js +502 -0
- package/dist/parsers/typescript-parser.js.map +1 -0
- package/dist/reporter/json-reporter.d.ts +12 -0
- package/dist/reporter/json-reporter.d.ts.map +1 -0
- package/dist/reporter/json-reporter.js +28 -0
- package/dist/reporter/json-reporter.js.map +1 -0
- package/dist/reporter/markdown-reporter.d.ts +11 -0
- package/dist/reporter/markdown-reporter.d.ts.map +1 -0
- package/dist/reporter/markdown-reporter.js +77 -0
- package/dist/reporter/markdown-reporter.js.map +1 -0
- package/dist/rothunter.d.ts +125 -0
- package/dist/rothunter.d.ts.map +1 -0
- package/dist/rothunter.js +1038 -0
- package/dist/rothunter.js.map +1 -0
- package/dist/server/false-positives.d.ts +34 -0
- package/dist/server/false-positives.d.ts.map +1 -0
- package/dist/server/false-positives.js +85 -0
- package/dist/server/false-positives.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +1529 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/marked-to-fix.d.ts +16 -0
- package/dist/server/marked-to-fix.d.ts.map +1 -0
- package/dist/server/marked-to-fix.js +36 -0
- package/dist/server/marked-to-fix.js.map +1 -0
- package/dist/server/scan-store.d.ts +147 -0
- package/dist/server/scan-store.d.ts.map +1 -0
- package/dist/server/scan-store.js +291 -0
- package/dist/server/scan-store.js.map +1 -0
- package/dist/server/settings-store.d.ts +28 -0
- package/dist/server/settings-store.d.ts.map +1 -0
- package/dist/server/settings-store.js +46 -0
- package/dist/server/settings-store.js.map +1 -0
- package/dist/server/workspace-store.d.ts +39 -0
- package/dist/server/workspace-store.d.ts.map +1 -0
- package/dist/server/workspace-store.js +108 -0
- package/dist/server/workspace-store.js.map +1 -0
- package/dist/types/detector-input.d.ts +37 -0
- package/dist/types/detector-input.d.ts.map +1 -0
- package/dist/types/detector-input.js +2 -0
- package/dist/types/detector-input.js.map +1 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/clustering.d.ts +14 -0
- package/dist/utils/clustering.d.ts.map +1 -0
- package/dist/utils/clustering.js +56 -0
- package/dist/utils/clustering.js.map +1 -0
- package/dist/utils/gitignore.d.ts +32 -0
- package/dist/utils/gitignore.d.ts.map +1 -0
- package/dist/utils/gitignore.js +122 -0
- package/dist/utils/gitignore.js.map +1 -0
- package/dist/utils/hash.d.ts +11 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +14 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/ignore-annotation.d.ts +28 -0
- package/dist/utils/ignore-annotation.d.ts.map +1 -0
- package/dist/utils/ignore-annotation.js +46 -0
- package/dist/utils/ignore-annotation.js.map +1 -0
- package/dist/utils/llm-json.d.ts +2 -0
- package/dist/utils/llm-json.d.ts.map +1 -0
- package/dist/utils/llm-json.js +53 -0
- package/dist/utils/llm-json.js.map +1 -0
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +4 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/project-conventions.d.ts +2 -0
- package/dist/utils/project-conventions.d.ts.map +1 -0
- package/dist/utils/project-conventions.js +108 -0
- package/dist/utils/project-conventions.js.map +1 -0
- package/dist/utils/regex.d.ts +9 -0
- package/dist/utils/regex.d.ts.map +1 -0
- package/dist/utils/regex.js +11 -0
- package/dist/utils/regex.js.map +1 -0
- package/dist/utils/snippet.d.ts +20 -0
- package/dist/utils/snippet.d.ts.map +1 -0
- package/dist/utils/snippet.js +28 -0
- package/dist/utils/snippet.js.map +1 -0
- package/dist/utils/source-reader.d.ts +19 -0
- package/dist/utils/source-reader.d.ts.map +1 -0
- package/dist/utils/source-reader.js +32 -0
- package/dist/utils/source-reader.js.map +1 -0
- package/logo.png +0 -0
- package/package.json +92 -0
- package/scripts/start-llm.mjs +161 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { stableHash } from '../utils/hash.js';
|
|
3
|
+
import { trimSnippet, trimEnclosingSource } from '../utils/snippet.js';
|
|
4
|
+
import { Project, SyntaxKind, } from 'ts-morph';
|
|
5
|
+
const MUTATING_ARRAY_METHODS = new Set([
|
|
6
|
+
'push',
|
|
7
|
+
'pop',
|
|
8
|
+
'shift',
|
|
9
|
+
'unshift',
|
|
10
|
+
'splice',
|
|
11
|
+
'sort',
|
|
12
|
+
'reverse',
|
|
13
|
+
'fill',
|
|
14
|
+
'copyWithin',
|
|
15
|
+
]);
|
|
16
|
+
const ASSIGNMENT_OPERATORS = new Set([
|
|
17
|
+
'=',
|
|
18
|
+
'+=',
|
|
19
|
+
'-=',
|
|
20
|
+
'*=',
|
|
21
|
+
'/=',
|
|
22
|
+
'%=',
|
|
23
|
+
'**=',
|
|
24
|
+
'<<=',
|
|
25
|
+
'>>=',
|
|
26
|
+
'>>>=',
|
|
27
|
+
'&=',
|
|
28
|
+
'|=',
|
|
29
|
+
'^=',
|
|
30
|
+
'&&=',
|
|
31
|
+
'||=',
|
|
32
|
+
'??=',
|
|
33
|
+
]);
|
|
34
|
+
const IGNORE_ANNOTATION = 'rothunter:ignore-mutation';
|
|
35
|
+
// Parameter-mutation detector. Flags array-mutators, Object.assign target,
|
|
36
|
+
// delete, property assignment on params NOT typed Readonly/ReadonlyArray.
|
|
37
|
+
// `this.x = y` + constructor self-init skipped. `// rothunter:ignore-mutation`
|
|
38
|
+
// suppresses.
|
|
39
|
+
export function detectMutations(input) {
|
|
40
|
+
let project;
|
|
41
|
+
if (input.project) {
|
|
42
|
+
project = input.project;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
project = new Project({
|
|
46
|
+
skipAddingFilesFromTsConfig: true,
|
|
47
|
+
skipFileDependencyResolution: true,
|
|
48
|
+
});
|
|
49
|
+
for (const rel of input.files) {
|
|
50
|
+
project.addSourceFileAtPathIfExists(path.join(input.workspaceRoot, rel));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const candidates = [];
|
|
54
|
+
for (const sf of project.getSourceFiles()) {
|
|
55
|
+
const filePath = sf.getFilePath();
|
|
56
|
+
const relativeFile = path.relative(input.workspaceRoot, filePath);
|
|
57
|
+
// Tier 2 — module-scope mutable identifiers (`let`/`var` at top level).
|
|
58
|
+
// Their mutations from any function body are treated as shared-state writes.
|
|
59
|
+
const moduleMutables = collectModuleMutables(sf);
|
|
60
|
+
for (const fn of sf.getFunctions()) {
|
|
61
|
+
candidates.push(...analyzeCallable(fn, relativeFile, moduleMutables));
|
|
62
|
+
}
|
|
63
|
+
for (const cls of sf.getClasses()) {
|
|
64
|
+
for (const m of cls.getMethods())
|
|
65
|
+
candidates.push(...analyzeCallable(m, relativeFile, moduleMutables));
|
|
66
|
+
}
|
|
67
|
+
for (const arrow of sf.getDescendantsOfKind(SyntaxKind.ArrowFunction)) {
|
|
68
|
+
candidates.push(...analyzeCallable(arrow, relativeFile, moduleMutables));
|
|
69
|
+
}
|
|
70
|
+
for (const expr of sf.getDescendantsOfKind(SyntaxKind.FunctionExpression)) {
|
|
71
|
+
candidates.push(...analyzeCallable(expr, relativeFile, moduleMutables));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return candidates.map(toFinding);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Top-level `let` / `var` declarations are the canonical "shared mutable
|
|
78
|
+
* state" pattern. We deliberately exclude `const` because re-binding is
|
|
79
|
+
* blocked there — `const arr = []; arr.push()` still mutates contents, but
|
|
80
|
+
* the user has at least flagged the binding as fixed. For Tier 2 MVP we
|
|
81
|
+
* focus on `let`/`var`, which signal "intentionally mutable" most loudly.
|
|
82
|
+
*/
|
|
83
|
+
function collectModuleMutables(sf) {
|
|
84
|
+
const out = new Set();
|
|
85
|
+
const statements = sf.getVariableStatements();
|
|
86
|
+
for (const stmt of statements) {
|
|
87
|
+
const kind = stmt.getDeclarationKind();
|
|
88
|
+
if (kind !== 'let' && kind !== 'var')
|
|
89
|
+
continue;
|
|
90
|
+
for (const decl of stmt.getDeclarations()) {
|
|
91
|
+
if (decl.getNameNode().getKind() !== SyntaxKind.Identifier)
|
|
92
|
+
continue;
|
|
93
|
+
out.add(decl.getName());
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
function analyzeCallable(fn, file, moduleMutables) {
|
|
99
|
+
const params = new Map();
|
|
100
|
+
for (const p of fn.getParameters()) {
|
|
101
|
+
// Destructured params (`{a, b}: User`) — skip; mutation of `a`/`b` is
|
|
102
|
+
// mutation of locals, not the original argument.
|
|
103
|
+
if (p.getNameNode().getKind() !== SyntaxKind.Identifier)
|
|
104
|
+
continue;
|
|
105
|
+
const name = p.getName();
|
|
106
|
+
const typeNode = p.getTypeNode();
|
|
107
|
+
const typeText = typeNode ? typeNode.getText() : p.getType().getText();
|
|
108
|
+
params.set(name, { typeText });
|
|
109
|
+
}
|
|
110
|
+
const body = fn.getBody?.();
|
|
111
|
+
if (!body)
|
|
112
|
+
return [];
|
|
113
|
+
const enclosingSource = trimEnclosingSource(fn.getText());
|
|
114
|
+
const enclosingName = fn.getName?.();
|
|
115
|
+
// Pre-compute escape signals once per function: what identifiers leave the
|
|
116
|
+
// function via return / this.x assignment / call argument? Used to bump
|
|
117
|
+
// severity of param mutations that touch escaping bindings.
|
|
118
|
+
const escapingIdentifiers = collectEscapingIdentifiers(body);
|
|
119
|
+
const found = [];
|
|
120
|
+
const resolveScope = (name) => {
|
|
121
|
+
const param = params.get(name);
|
|
122
|
+
if (param)
|
|
123
|
+
return { scope: 'parameter', readonly: isReadonlyType(param.typeText) };
|
|
124
|
+
if (moduleMutables.has(name))
|
|
125
|
+
return { scope: 'module-state', readonly: false };
|
|
126
|
+
return null;
|
|
127
|
+
};
|
|
128
|
+
// --- Array mutator and Object.assign call expressions -----------------------
|
|
129
|
+
for (const call of body.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
130
|
+
if (hasIgnoreAnnotation(call))
|
|
131
|
+
continue;
|
|
132
|
+
const callee = call.getExpression();
|
|
133
|
+
if (callee.getKind() !== SyntaxKind.PropertyAccessExpression)
|
|
134
|
+
continue;
|
|
135
|
+
const pa = callee.asKindOrThrow(SyntaxKind.PropertyAccessExpression);
|
|
136
|
+
const head = pa.getExpression().getText();
|
|
137
|
+
const member = pa.getName();
|
|
138
|
+
// Object.assign(target, ...) — flag when target is a parameter or module mutable.
|
|
139
|
+
if (head === 'Object' && member === 'assign') {
|
|
140
|
+
const [first] = call.getArguments();
|
|
141
|
+
if (!first || first.getKind() !== SyntaxKind.Identifier)
|
|
142
|
+
continue;
|
|
143
|
+
const targetName = first.getText();
|
|
144
|
+
const scope = resolveScope(targetName);
|
|
145
|
+
if (!scope || scope.readonly)
|
|
146
|
+
continue;
|
|
147
|
+
found.push({
|
|
148
|
+
file,
|
|
149
|
+
line: call.getStartLineNumber(),
|
|
150
|
+
endLine: call.getEndLineNumber(),
|
|
151
|
+
pattern: scope.scope === 'module-state' ? 'shared-state-write' : 'object-assign',
|
|
152
|
+
scope: scope.scope,
|
|
153
|
+
escapes: scope.scope === 'parameter' && escapingIdentifiers.has(targetName),
|
|
154
|
+
target: targetName,
|
|
155
|
+
snippet: trimSnippet(call.getText()),
|
|
156
|
+
source: call.getText(),
|
|
157
|
+
enclosingSource,
|
|
158
|
+
enclosingName,
|
|
159
|
+
});
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
// Array mutator: identifier.push/...
|
|
163
|
+
if (MUTATING_ARRAY_METHODS.has(member)) {
|
|
164
|
+
const scope = resolveScope(head);
|
|
165
|
+
if (!scope || scope.readonly)
|
|
166
|
+
continue;
|
|
167
|
+
found.push({
|
|
168
|
+
file,
|
|
169
|
+
line: call.getStartLineNumber(),
|
|
170
|
+
endLine: call.getEndLineNumber(),
|
|
171
|
+
pattern: scope.scope === 'module-state' ? 'shared-state-write' : 'array-mutator',
|
|
172
|
+
scope: scope.scope,
|
|
173
|
+
escapes: scope.scope === 'parameter' && escapingIdentifiers.has(head),
|
|
174
|
+
target: head,
|
|
175
|
+
method: member,
|
|
176
|
+
snippet: trimSnippet(call.getText()),
|
|
177
|
+
source: call.getText(),
|
|
178
|
+
enclosingSource,
|
|
179
|
+
enclosingName,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// --- delete <expr> ----------------------------------------------------------
|
|
184
|
+
for (const del of body.getDescendantsOfKind(SyntaxKind.DeleteExpression)) {
|
|
185
|
+
if (hasIgnoreAnnotation(del))
|
|
186
|
+
continue;
|
|
187
|
+
const operand = del.getExpression();
|
|
188
|
+
const root = identifierRoot(operand);
|
|
189
|
+
if (!root)
|
|
190
|
+
continue;
|
|
191
|
+
const scope = resolveScope(root);
|
|
192
|
+
if (!scope || scope.readonly)
|
|
193
|
+
continue;
|
|
194
|
+
found.push({
|
|
195
|
+
file,
|
|
196
|
+
line: del.getStartLineNumber(),
|
|
197
|
+
endLine: del.getEndLineNumber(),
|
|
198
|
+
pattern: scope.scope === 'module-state' ? 'shared-state-write' : 'delete-property',
|
|
199
|
+
scope: scope.scope,
|
|
200
|
+
escapes: scope.scope === 'parameter' && escapingIdentifiers.has(root),
|
|
201
|
+
target: root,
|
|
202
|
+
snippet: trimSnippet(del.getText()),
|
|
203
|
+
source: del.getText(),
|
|
204
|
+
enclosingSource,
|
|
205
|
+
enclosingName,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
// --- <param>.x = y / += y / ... ---------------------------------------------
|
|
209
|
+
for (const bin of body.getDescendantsOfKind(SyntaxKind.BinaryExpression)) {
|
|
210
|
+
const opText = bin.getOperatorToken().getText();
|
|
211
|
+
if (!ASSIGNMENT_OPERATORS.has(opText))
|
|
212
|
+
continue;
|
|
213
|
+
if (hasIgnoreAnnotation(bin))
|
|
214
|
+
continue;
|
|
215
|
+
const lhs = bin.getLeft();
|
|
216
|
+
if (lhs.getKind() !== SyntaxKind.PropertyAccessExpression && lhs.getKind() !== SyntaxKind.ElementAccessExpression) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
const root = identifierRoot(lhs);
|
|
220
|
+
if (!root)
|
|
221
|
+
continue;
|
|
222
|
+
if (root === 'this')
|
|
223
|
+
continue; // class method self-init — almost always intentional
|
|
224
|
+
const scope = resolveScope(root);
|
|
225
|
+
if (!scope || scope.readonly)
|
|
226
|
+
continue;
|
|
227
|
+
found.push({
|
|
228
|
+
file,
|
|
229
|
+
line: bin.getStartLineNumber(),
|
|
230
|
+
endLine: bin.getEndLineNumber(),
|
|
231
|
+
pattern: scope.scope === 'module-state' ? 'shared-state-write' : 'property-assignment',
|
|
232
|
+
scope: scope.scope,
|
|
233
|
+
escapes: scope.scope === 'parameter' && escapingIdentifiers.has(root),
|
|
234
|
+
target: root,
|
|
235
|
+
snippet: trimSnippet(bin.getText()),
|
|
236
|
+
source: bin.getText(),
|
|
237
|
+
enclosingSource,
|
|
238
|
+
enclosingName,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
return found;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Collect identifier names that LEAVE the function via a return value,
|
|
245
|
+
* via `this.x = ident`, or by being passed as an argument to another call.
|
|
246
|
+
*
|
|
247
|
+
* Used to bump severity of param mutations: if a mutated param also escapes,
|
|
248
|
+
* the mutation is more dangerous (the caller's state is now corrupted).
|
|
249
|
+
*
|
|
250
|
+
* Heuristic — we don't follow `const copy = arg; return copy;` (that needs
|
|
251
|
+
* data-flow analysis). Direct uses only.
|
|
252
|
+
*/
|
|
253
|
+
function collectEscapingIdentifiers(body) {
|
|
254
|
+
const out = new Set();
|
|
255
|
+
for (const ret of body.getDescendantsOfKind(SyntaxKind.ReturnStatement)) {
|
|
256
|
+
const expr = ret.getExpression();
|
|
257
|
+
if (!expr)
|
|
258
|
+
continue;
|
|
259
|
+
collectIdentifiers(expr, out);
|
|
260
|
+
}
|
|
261
|
+
for (const bin of body.getDescendantsOfKind(SyntaxKind.BinaryExpression)) {
|
|
262
|
+
if (bin.getOperatorToken().getText() !== '=')
|
|
263
|
+
continue;
|
|
264
|
+
const lhs = bin.getLeft().asKind(SyntaxKind.PropertyAccessExpression);
|
|
265
|
+
if (!lhs)
|
|
266
|
+
continue;
|
|
267
|
+
if (lhs.getExpression().getKind() !== SyntaxKind.ThisKeyword)
|
|
268
|
+
continue;
|
|
269
|
+
collectIdentifiers(bin.getRight(), out);
|
|
270
|
+
}
|
|
271
|
+
for (const call of body.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
272
|
+
// Skip the calls we already flag ourselves (Object.assign / array mutators)
|
|
273
|
+
// to avoid counting the mutator's first argument as "escaping".
|
|
274
|
+
for (const arg of call.getArguments()) {
|
|
275
|
+
collectIdentifiers(arg, out);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return out;
|
|
279
|
+
}
|
|
280
|
+
function collectIdentifiers(node, into) {
|
|
281
|
+
if (node.getKind() === SyntaxKind.Identifier) {
|
|
282
|
+
into.add(node.getText());
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
for (const child of node.getDescendantsOfKind(SyntaxKind.Identifier)) {
|
|
286
|
+
into.add(child.getText());
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/** Walk a PropertyAccess / ElementAccess chain to the leftmost identifier. */
|
|
290
|
+
function identifierRoot(node) {
|
|
291
|
+
let cur = node;
|
|
292
|
+
while (true) {
|
|
293
|
+
const prop = cur.asKind(SyntaxKind.PropertyAccessExpression);
|
|
294
|
+
const elem = cur.asKind(SyntaxKind.ElementAccessExpression);
|
|
295
|
+
if (!prop && !elem)
|
|
296
|
+
break;
|
|
297
|
+
cur = (prop ?? elem).getExpression();
|
|
298
|
+
}
|
|
299
|
+
if (cur.getKind() === SyntaxKind.Identifier)
|
|
300
|
+
return cur.getText();
|
|
301
|
+
if (cur.getKind() === SyntaxKind.ThisKeyword)
|
|
302
|
+
return 'this';
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
function isReadonlyType(text) {
|
|
306
|
+
const compact = text.replace(/\s+/g, ' ').trim();
|
|
307
|
+
if (/^Readonly<.*>$/.test(compact))
|
|
308
|
+
return true;
|
|
309
|
+
if (/^ReadonlyArray<.*>$/.test(compact))
|
|
310
|
+
return true;
|
|
311
|
+
if (/^readonly\s/.test(compact))
|
|
312
|
+
return true;
|
|
313
|
+
if (/^readonly\s+[A-Za-z_$][\w$]*\[\]$/.test(compact))
|
|
314
|
+
return true;
|
|
315
|
+
if (/^\(\s*readonly\s/.test(compact))
|
|
316
|
+
return true;
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
function hasIgnoreAnnotation(node) {
|
|
320
|
+
const trivia = node.getLeadingCommentRanges();
|
|
321
|
+
for (const range of trivia) {
|
|
322
|
+
if (range.getText().includes(IGNORE_ANNOTATION))
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
// Check trailing or preceding statement-level comment.
|
|
326
|
+
const fullStart = node.getFullStart();
|
|
327
|
+
const sf = node.getSourceFile();
|
|
328
|
+
const text = sf.getFullText().slice(Math.max(0, fullStart - 200), fullStart);
|
|
329
|
+
return text.includes(IGNORE_ANNOTATION);
|
|
330
|
+
}
|
|
331
|
+
function toFinding(c) {
|
|
332
|
+
// Severity matrix (per ROADMAP):
|
|
333
|
+
// mutate-and-escape > mutate-shared-module > mutate-local
|
|
334
|
+
const severity = c.escapes
|
|
335
|
+
? 'high'
|
|
336
|
+
: c.scope === 'module-state'
|
|
337
|
+
? 'high'
|
|
338
|
+
: c.pattern === 'delete-property'
|
|
339
|
+
? 'high'
|
|
340
|
+
: 'medium';
|
|
341
|
+
const subject = c.scope === 'module-state' ? 'Shared module state' : 'Parameter';
|
|
342
|
+
const action = c.pattern === 'array-mutator'
|
|
343
|
+
? `array mutation: ${c.target}.${c.method ?? '?'}()`
|
|
344
|
+
: c.pattern === 'object-assign'
|
|
345
|
+
? `mutation via Object.assign: ${c.target}`
|
|
346
|
+
: c.pattern === 'delete-property'
|
|
347
|
+
? `property deletion: delete ${c.target}.…`
|
|
348
|
+
: c.pattern === 'property-assignment'
|
|
349
|
+
? `property assignment: ${c.target}.…`
|
|
350
|
+
: `write: ${c.target}`;
|
|
351
|
+
const escapeTag = c.escapes ? ' [ESCAPES]' : '';
|
|
352
|
+
const title = `${subject} ${action}${escapeTag}`;
|
|
353
|
+
const description = [
|
|
354
|
+
`${title} (pattern \`${c.pattern}\`, scope \`${c.scope}\`${c.escapes ? ', escapes' : ''}).`,
|
|
355
|
+
`Locations:`,
|
|
356
|
+
`- ${c.file}:${c.line} \`${c.snippet}\``,
|
|
357
|
+
'',
|
|
358
|
+
c.escapes
|
|
359
|
+
? 'The mutated value also escapes this function (returned, assigned to `this.x`, or passed onward). Callers will observe the mutation, which is the most damaging shape of this pattern.'
|
|
360
|
+
: c.scope === 'module-state'
|
|
361
|
+
? 'This writes to a module-scope `let`/`var` binding. Any other function that imports or closes over the binding sees the change — surface-area for accidental shared state.'
|
|
362
|
+
: "If the mutation is part of this function's contract (builder pattern, in-place transform), add a `// rothunter:ignore-mutation` comment above the call OR tighten the parameter type to `Readonly<...>` to document intent.",
|
|
363
|
+
].join('\n');
|
|
364
|
+
return {
|
|
365
|
+
detectorId: 'mutation',
|
|
366
|
+
severity,
|
|
367
|
+
confidence: c.escapes ? 0.82 : c.scope === 'module-state' ? 0.78 : 0.7,
|
|
368
|
+
layer: 1,
|
|
369
|
+
title,
|
|
370
|
+
description,
|
|
371
|
+
evidence: [
|
|
372
|
+
{
|
|
373
|
+
file: c.file,
|
|
374
|
+
range: { startLine: c.line, endLine: c.endLine },
|
|
375
|
+
snippet: c.source,
|
|
376
|
+
// `note` carries the enclosing-function source for the LLM
|
|
377
|
+
// confirmer. The markdown reporter intentionally does not render it.
|
|
378
|
+
note: JSON.stringify({
|
|
379
|
+
enclosingSource: c.enclosingSource,
|
|
380
|
+
enclosingName: c.enclosingName ?? '',
|
|
381
|
+
pattern: c.pattern,
|
|
382
|
+
escapes: c.escapes,
|
|
383
|
+
}),
|
|
384
|
+
},
|
|
385
|
+
],
|
|
386
|
+
suggestion: c.scope === 'module-state'
|
|
387
|
+
? 'Move the state into a module-scope `const` (with internal mutation hidden behind a small API) or into a class so the surface area is explicit.'
|
|
388
|
+
: 'Return a new value instead of mutating the parameter, or type the parameter as Readonly<...>.',
|
|
389
|
+
fingerprint: `mutation:${c.pattern}:${stableHash(`${c.file}::${c.line}::${c.target}`)}`,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Compress the enclosing function source so the LLM prompt stays in budget.
|
|
394
|
+
* We keep the full signature line plus up to ~40 lines of body — enough to
|
|
395
|
+
* judge intent without paying for the model to read a 200-line method.
|
|
396
|
+
*/
|
|
397
|
+
//# sourceMappingURL=mutation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutation.js","sourceRoot":"","sources":["../../src/detectors/mutation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Finding, SymbolRecord } from '../types.js';
|
|
2
|
+
export interface PublicAnyDetectorInput {
|
|
3
|
+
symbols: ReadonlyArray<SymbolRecord>;
|
|
4
|
+
}
|
|
5
|
+
export declare function detectPublicAny(input: PublicAnyDetectorInput): Finding[];
|
|
6
|
+
//# sourceMappingURL=public-any.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-any.d.ts","sourceRoot":"","sources":["../../src/detectors/public-any.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAqB,MAAM,aAAa,CAAC;AAG5E,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACtC;AAID,wBAAgB,eAAe,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,EAAE,CAmCxE"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { stableHash } from '../utils/hash.js';
|
|
2
|
+
// Exported function with `any` in params or return. HIGH — caller-visible
|
|
3
|
+
// type hole. `any[]`, `Record<string, any>`, `any | T` count. `unknown` doesn't.
|
|
4
|
+
export function detectPublicAny(input) {
|
|
5
|
+
const findings = [];
|
|
6
|
+
for (const sym of input.symbols) {
|
|
7
|
+
if (sym.kind !== 'function')
|
|
8
|
+
continue;
|
|
9
|
+
if (!sym.exported)
|
|
10
|
+
continue;
|
|
11
|
+
const fn = sym.structure;
|
|
12
|
+
if (!fn || fn.kind !== 'function')
|
|
13
|
+
continue;
|
|
14
|
+
const offendingParams = fn.params.filter((p) => containsAny(p.type)).map((p) => p.name);
|
|
15
|
+
const returnAny = containsAny(fn.returnType);
|
|
16
|
+
if (offendingParams.length === 0 && !returnAny)
|
|
17
|
+
continue;
|
|
18
|
+
const where = [
|
|
19
|
+
...offendingParams.map((n) => `param \`${n}: any\``),
|
|
20
|
+
...(returnAny ? [`return \`${fn.returnType}\``] : []),
|
|
21
|
+
];
|
|
22
|
+
findings.push({
|
|
23
|
+
detectorId: 'public-any',
|
|
24
|
+
severity: 'high',
|
|
25
|
+
confidence: 0.97,
|
|
26
|
+
layer: 1,
|
|
27
|
+
title: `Public \`any\` in \`${sym.name}\` (${sym.file})`,
|
|
28
|
+
description: `Exported function \`${sym.name}\` exposes \`any\` in its signature (${where.join(', ')}). Every caller silently loses type-safety on this boundary.`,
|
|
29
|
+
evidence: [
|
|
30
|
+
{
|
|
31
|
+
file: sym.file,
|
|
32
|
+
range: { startLine: sym.range.startLine, endLine: sym.range.endLine },
|
|
33
|
+
snippet: sym.source.split('\n').slice(0, 4).join('\n'),
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
suggestion: 'Replace `any` with the narrowest type that actually works (`unknown` + a type guard, a discriminated union, or a real interface). Prefer `unknown` over `any` when you only need "I do not know the shape yet".',
|
|
37
|
+
fingerprint: `public-any:${stableHash(`${sym.file}:${sym.name}`)}`,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return findings;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* True iff the rendered type includes a standalone `any` (not part of a
|
|
44
|
+
* larger identifier like `Many`). Looks at top-level + nested generics.
|
|
45
|
+
*/
|
|
46
|
+
function containsAny(type) {
|
|
47
|
+
if (!type)
|
|
48
|
+
return false;
|
|
49
|
+
// Word-boundary match; excludes `Many`, `anyway`, `JsonAny` etc.
|
|
50
|
+
return /\bany\b/.test(type);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=public-any.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-any.js","sourceRoot":"","sources":["../../src/detectors/public-any.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAM9C,0EAA0E;AAC1E,iFAAiF;AACjF,MAAM,UAAU,eAAe,CAAC,KAA6B;IAC3D,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAChC,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU;YAAE,SAAS;QACtC,IAAI,CAAC,GAAG,CAAC,QAAQ;YAAE,SAAS;QAC5B,MAAM,EAAE,GAAG,GAAG,CAAC,SAA0C,CAAC;QAC1D,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,IAAI,KAAK,UAAU;YAAE,SAAS;QAC5C,MAAM,eAAe,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACxF,MAAM,SAAS,GAAG,WAAW,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,SAAS;YAAE,SAAS;QACzD,MAAM,KAAK,GAAG;YACZ,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC;YACpD,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SACtD,CAAC;QACF,QAAQ,CAAC,IAAI,CAAC;YACZ,UAAU,EAAE,YAAY;YACxB,QAAQ,EAAE,MAAM;YAChB,UAAU,EAAE,IAAI;YAChB,KAAK,EAAE,CAAC;YACR,KAAK,EAAE,uBAAuB,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,IAAI,GAAG;YACxD,WAAW,EACT,uBAAuB,GAAG,CAAC,IAAI,wCAAwC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,8DAA8D;YACvJ,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,KAAK,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE;oBACrE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;iBACvD;aACF;YACD,UAAU,EACR,iNAAiN;YACnN,WAAW,EAAE,cAAc,UAAU,CAAC,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE;SACnE,CAAC,CAAC;IACL,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,IAAY;IAC/B,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,iEAAiE;IACjE,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Finding } from '../types.js';
|
|
2
|
+
import type { FileWalkingDetectorInput } from '../types/detector-input.js';
|
|
3
|
+
export interface RaceConditionDetectorInput extends FileWalkingDetectorInput {
|
|
4
|
+
}
|
|
5
|
+
export declare function detectRaceConditions(input: RaceConditionDetectorInput): Finding[];
|
|
6
|
+
//# sourceMappingURL=race-condition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"race-condition.d.ts","sourceRoot":"","sources":["../../src/detectors/race-condition.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAE3E,MAAM,WAAW,0BAA2B,SAAQ,wBAAwB;CAAG;AAsD/E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,EAAE,CAuEjF"}
|