@aiready/visualizer 0.7.3 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +284 -239
- package/dist/cli.js.map +1 -1
- package/dist/graph/index.d.ts +11 -26
- package/dist/graph/index.js +284 -239
- package/dist/graph/index.js.map +1 -1
- package/dist/index.js +284 -239
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/web/dist/assets/index-C8KyAlJg.js.map +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,79 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
-
import
|
|
3
|
-
import { UnifiedReportSchema, ToolName,
|
|
2
|
+
import path2 from 'path';
|
|
3
|
+
import { Severity, UnifiedReportSchema, ToolName, normalizeAnalysisResult } from '@aiready/core';
|
|
4
|
+
|
|
5
|
+
// src/graph/builder.ts
|
|
6
|
+
var GRAPH_CONSTANTS = {
|
|
7
|
+
DEFAULT_NODE_SIZE: 1,
|
|
8
|
+
DEFAULT_REFERENCE_SIZE: 5,
|
|
9
|
+
DEFAULT_DEPENDENCY_SIZE: 2,
|
|
10
|
+
DEFAULT_CONTEXT_SIZE: 10,
|
|
11
|
+
FUZZY_MATCH_THRESHOLD: 50,
|
|
12
|
+
FUZZY_MATCH_HIGH_THRESHOLD: 80,
|
|
13
|
+
COLORS: {
|
|
14
|
+
CRITICAL: "#ff4d4f",
|
|
15
|
+
MAJOR: "#ff9900",
|
|
16
|
+
MINOR: "#ffd666",
|
|
17
|
+
INFO: "#91d5ff",
|
|
18
|
+
DEFAULT: "#97c2fc"
|
|
19
|
+
},
|
|
20
|
+
SEVERITY_ORDER: {
|
|
21
|
+
[Severity.Critical]: 3,
|
|
22
|
+
[Severity.Major]: 2,
|
|
23
|
+
[Severity.Minor]: 1,
|
|
24
|
+
[Severity.Info]: 0
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
function normalizeLabel(filePath, rootDir) {
|
|
28
|
+
try {
|
|
29
|
+
return path2.relative(rootDir, filePath);
|
|
30
|
+
} catch {
|
|
31
|
+
return filePath;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function extractReferencedPaths(message) {
|
|
35
|
+
if (!message || typeof message !== "string") return [];
|
|
36
|
+
const reAbs = /\/(?:[\w\-.]+\/)+[\w\-.]+\.(?:ts|tsx|js|jsx|py|java|go)/g;
|
|
37
|
+
const reRel = /(?:\.\/|\.\.\/)(?:[\w\-.]+\/)+[\w\-.]+\.(?:ts|tsx|js|jsx|py|java|go)/g;
|
|
38
|
+
const abs = message.match(reAbs) ?? [];
|
|
39
|
+
const rel = message.match(reRel) ?? [];
|
|
40
|
+
return abs.concat(rel);
|
|
41
|
+
}
|
|
42
|
+
function getPackageGroup(fp) {
|
|
43
|
+
if (!fp) return void 0;
|
|
44
|
+
const parts = fp.split(path2.sep);
|
|
45
|
+
const pkgIdx = parts.indexOf("packages");
|
|
46
|
+
if (pkgIdx >= 0 && parts.length > pkgIdx + 1)
|
|
47
|
+
return `packages/${parts[pkgIdx + 1]}`;
|
|
48
|
+
const landingIdx = parts.indexOf("landing");
|
|
49
|
+
if (landingIdx >= 0) return "landing";
|
|
50
|
+
const scriptsIdx = parts.indexOf("scripts");
|
|
51
|
+
if (scriptsIdx >= 0) return "scripts";
|
|
52
|
+
return parts.length > 1 ? parts[1] : parts[0];
|
|
53
|
+
}
|
|
54
|
+
function rankSeverity(s) {
|
|
55
|
+
if (!s) return null;
|
|
56
|
+
const ss = String(s).toLowerCase();
|
|
57
|
+
if (ss.includes("critical")) return Severity.Critical;
|
|
58
|
+
if (ss.includes("major")) return Severity.Major;
|
|
59
|
+
if (ss.includes("minor")) return Severity.Minor;
|
|
60
|
+
if (ss.includes("info")) return Severity.Info;
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function getColorForSeverity(sev) {
|
|
64
|
+
switch (sev) {
|
|
65
|
+
case Severity.Critical:
|
|
66
|
+
return GRAPH_CONSTANTS.COLORS.CRITICAL;
|
|
67
|
+
case Severity.Major:
|
|
68
|
+
return GRAPH_CONSTANTS.COLORS.MAJOR;
|
|
69
|
+
case Severity.Minor:
|
|
70
|
+
return GRAPH_CONSTANTS.COLORS.MINOR;
|
|
71
|
+
case Severity.Info:
|
|
72
|
+
return GRAPH_CONSTANTS.COLORS.INFO;
|
|
73
|
+
default:
|
|
74
|
+
return GRAPH_CONSTANTS.COLORS.DEFAULT;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
4
77
|
|
|
5
78
|
// src/graph/builder.ts
|
|
6
79
|
var GraphBuilder = class _GraphBuilder {
|
|
@@ -10,97 +83,59 @@ var GraphBuilder = class _GraphBuilder {
|
|
|
10
83
|
this.edges = [];
|
|
11
84
|
this.edgesSet = /* @__PURE__ */ new Set();
|
|
12
85
|
}
|
|
13
|
-
normalizeLabel(filePath) {
|
|
14
|
-
try {
|
|
15
|
-
return path.relative(this.rootDir, filePath);
|
|
16
|
-
} catch {
|
|
17
|
-
return filePath;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
extractReferencedPaths(message) {
|
|
21
|
-
if (!message || typeof message !== "string") return [];
|
|
22
|
-
const reAbs = /\/(?:[\w\-.]+\/)+[\w\-.]+\.(?:ts|tsx|js|jsx|py|java|go)/g;
|
|
23
|
-
const reRel = /(?:\.\/|\.\.\/)(?:[\w\-.]+\/)+[\w\-.]+\.(?:ts|tsx|js|jsx|py|java|go)/g;
|
|
24
|
-
const abs = message.match(reAbs) ?? [];
|
|
25
|
-
const rel = message.match(reRel) ?? [];
|
|
26
|
-
return abs.concat(rel);
|
|
27
|
-
}
|
|
28
|
-
getPackageGroup(fp) {
|
|
29
|
-
if (!fp) return null;
|
|
30
|
-
const parts = fp.split(path.sep);
|
|
31
|
-
const pkgIdx = parts.indexOf("packages");
|
|
32
|
-
if (pkgIdx >= 0 && parts.length > pkgIdx + 1)
|
|
33
|
-
return `packages/${parts[pkgIdx + 1]}`;
|
|
34
|
-
const landingIdx = parts.indexOf("landing");
|
|
35
|
-
if (landingIdx >= 0) return "landing";
|
|
36
|
-
const scriptsIdx = parts.indexOf("scripts");
|
|
37
|
-
if (scriptsIdx >= 0) return "scripts";
|
|
38
|
-
return parts.length > 1 ? parts[1] : parts[0];
|
|
39
|
-
}
|
|
40
86
|
/**
|
|
41
87
|
* Add a new node to the graph or update an existing one.
|
|
42
|
-
*
|
|
43
|
-
* @param file - Unique identifier for the file (node ID).
|
|
44
|
-
* @param title - Optional title or description for the node.
|
|
45
|
-
* @param value - Numerical value representing the node size/weight.
|
|
46
88
|
*/
|
|
47
|
-
addNode(file, title = "",
|
|
89
|
+
addNode(file, title = "", size = GRAPH_CONSTANTS.DEFAULT_NODE_SIZE) {
|
|
48
90
|
if (!file) return;
|
|
49
|
-
const id =
|
|
50
|
-
|
|
91
|
+
const id = path2.resolve(this.rootDir, file);
|
|
92
|
+
const existingNode = this.nodesMap.get(id);
|
|
93
|
+
if (!existingNode) {
|
|
51
94
|
const node = {
|
|
52
95
|
id,
|
|
53
96
|
path: id,
|
|
54
|
-
label:
|
|
97
|
+
label: normalizeLabel(id, this.rootDir),
|
|
55
98
|
title,
|
|
56
|
-
size
|
|
99
|
+
size
|
|
57
100
|
};
|
|
58
101
|
this.nodesMap.set(id, node);
|
|
59
102
|
} else {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
103
|
+
if (title && (!existingNode.title || !existingNode.title.includes(title))) {
|
|
104
|
+
existingNode.title = (existingNode.title ? existingNode.title + "\n" : "") + title;
|
|
105
|
+
}
|
|
106
|
+
if (size > (existingNode.size ?? 0)) {
|
|
107
|
+
existingNode.size = size;
|
|
63
108
|
}
|
|
64
|
-
if (value > (node.size ?? 0)) node.size = value;
|
|
65
109
|
}
|
|
66
110
|
}
|
|
67
111
|
/**
|
|
68
112
|
* Add a directed edge between two nodes in the graph.
|
|
69
|
-
*
|
|
70
|
-
* @param from - Source node ID (file path).
|
|
71
|
-
* @param to - Target node ID (file path).
|
|
72
|
-
* @param type - Type of relationship (e.g., 'dependency', 'reference').
|
|
73
113
|
*/
|
|
74
114
|
addEdge(from, to, type = "link") {
|
|
75
115
|
if (!from || !to) return;
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
79
|
-
const key = `${
|
|
116
|
+
const source = path2.resolve(this.rootDir, from);
|
|
117
|
+
const target = path2.resolve(this.rootDir, to);
|
|
118
|
+
if (source === target) return;
|
|
119
|
+
const key = `${source}->${target}`;
|
|
80
120
|
if (!this.edgesSet.has(key)) {
|
|
81
|
-
this.edges.push({ source
|
|
121
|
+
this.edges.push({ source, target, type });
|
|
82
122
|
this.edgesSet.add(key);
|
|
83
123
|
}
|
|
84
124
|
}
|
|
85
125
|
/**
|
|
86
126
|
* Build the final GraphData object from collected nodes and edges.
|
|
87
|
-
*
|
|
88
|
-
* @returns Consolidated graph data structure.
|
|
89
127
|
*/
|
|
90
128
|
build() {
|
|
91
129
|
const nodes = Array.from(this.nodesMap.values());
|
|
92
|
-
const edges = this.edges.map(
|
|
93
|
-
(e) => ({ source: e.source, target: e.target, type: e.type })
|
|
94
|
-
);
|
|
95
130
|
return {
|
|
96
131
|
nodes,
|
|
97
|
-
edges,
|
|
132
|
+
edges: [...this.edges],
|
|
98
133
|
clusters: [],
|
|
99
134
|
issues: [],
|
|
100
135
|
metadata: {
|
|
101
136
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
102
137
|
totalFiles: nodes.length,
|
|
103
|
-
totalDependencies: edges.length,
|
|
138
|
+
totalDependencies: this.edges.length,
|
|
104
139
|
analysisTypes: [],
|
|
105
140
|
criticalIssues: 0,
|
|
106
141
|
majorIssues: 0,
|
|
@@ -115,10 +150,6 @@ var GraphBuilder = class _GraphBuilder {
|
|
|
115
150
|
}
|
|
116
151
|
/**
|
|
117
152
|
* Static helper to build graph from an AIReady report JSON.
|
|
118
|
-
*
|
|
119
|
-
* @param report - Unified AIReady report object.
|
|
120
|
-
* @param rootDir - Root directory for path resolution.
|
|
121
|
-
* @returns Fully populated GraphData for visualization.
|
|
122
153
|
*/
|
|
123
154
|
static buildFromReport(report, rootDir = process.cwd()) {
|
|
124
155
|
const validation = UnifiedReportSchema.safeParse(report);
|
|
@@ -129,31 +160,18 @@ var GraphBuilder = class _GraphBuilder {
|
|
|
129
160
|
}
|
|
130
161
|
const builder = new _GraphBuilder(rootDir);
|
|
131
162
|
const fileIssues = /* @__PURE__ */ new Map();
|
|
132
|
-
const
|
|
133
|
-
if (!s) return null;
|
|
134
|
-
const ss = String(s).toLowerCase();
|
|
135
|
-
if (ss.includes("critical")) return Severity.Critical;
|
|
136
|
-
if (ss.includes("major")) return Severity.Major;
|
|
137
|
-
if (ss.includes("minor")) return Severity.Minor;
|
|
138
|
-
if (ss.includes("info")) return Severity.Info;
|
|
139
|
-
return null;
|
|
140
|
-
};
|
|
141
|
-
const bumpIssue = (file, sev) => {
|
|
163
|
+
const bumpIssue = (file, severity) => {
|
|
142
164
|
if (!file) return;
|
|
143
|
-
const id =
|
|
144
|
-
if (!fileIssues.has(id))
|
|
165
|
+
const id = path2.resolve(rootDir, file);
|
|
166
|
+
if (!fileIssues.has(id)) {
|
|
145
167
|
fileIssues.set(id, { count: 0, maxSeverity: null, duplicates: 0 });
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
[Severity.Info]: 0
|
|
154
|
-
};
|
|
155
|
-
if (!rec.maxSeverity || order[sev] > order[rec.maxSeverity])
|
|
156
|
-
rec.maxSeverity = sev;
|
|
168
|
+
}
|
|
169
|
+
const record = fileIssues.get(id);
|
|
170
|
+
record.count += 1;
|
|
171
|
+
if (severity) {
|
|
172
|
+
if (!record.maxSeverity || GRAPH_CONSTANTS.SEVERITY_ORDER[severity] > GRAPH_CONSTANTS.SEVERITY_ORDER[record.maxSeverity]) {
|
|
173
|
+
record.maxSeverity = severity;
|
|
174
|
+
}
|
|
157
175
|
}
|
|
158
176
|
};
|
|
159
177
|
const getResults = (toolKey, legacyKey) => {
|
|
@@ -163,120 +181,183 @@ var GraphBuilder = class _GraphBuilder {
|
|
|
163
181
|
if (Array.isArray(toolData)) return toolData;
|
|
164
182
|
return toolData.results ?? toolData.issues ?? [];
|
|
165
183
|
};
|
|
184
|
+
this.processPatterns(
|
|
185
|
+
builder,
|
|
186
|
+
getResults(ToolName.PatternDetect, "patterns"),
|
|
187
|
+
rootDir,
|
|
188
|
+
bumpIssue
|
|
189
|
+
);
|
|
190
|
+
this.processDuplicates(builder, report, rootDir, fileIssues);
|
|
191
|
+
this.processContext(
|
|
192
|
+
builder,
|
|
193
|
+
getResults(ToolName.ContextAnalyzer, "context"),
|
|
194
|
+
rootDir,
|
|
195
|
+
bumpIssue
|
|
196
|
+
);
|
|
197
|
+
this.processToolResults(
|
|
198
|
+
builder,
|
|
199
|
+
ToolName.DocDrift,
|
|
200
|
+
"docDrift",
|
|
201
|
+
report,
|
|
202
|
+
bumpIssue,
|
|
203
|
+
"Doc-Drift Issue"
|
|
204
|
+
);
|
|
205
|
+
this.processToolResults(
|
|
206
|
+
builder,
|
|
207
|
+
ToolName.DependencyHealth,
|
|
208
|
+
"dependencyHealth",
|
|
209
|
+
report,
|
|
210
|
+
bumpIssue,
|
|
211
|
+
"Dependency Issue"
|
|
212
|
+
);
|
|
213
|
+
this.processToolResults(
|
|
214
|
+
builder,
|
|
215
|
+
ToolName.ContractEnforcement,
|
|
216
|
+
"contractEnforcement",
|
|
217
|
+
report,
|
|
218
|
+
bumpIssue,
|
|
219
|
+
"Contract Gap"
|
|
220
|
+
);
|
|
221
|
+
return this.finalizeGraph(builder, fileIssues, report);
|
|
222
|
+
}
|
|
223
|
+
static processPatterns(builder, results, rootDir, bumpIssue) {
|
|
166
224
|
const basenameMap = /* @__PURE__ */ new Map();
|
|
167
|
-
|
|
168
|
-
patternResults.forEach((p) => {
|
|
225
|
+
results.forEach((p) => {
|
|
169
226
|
const fileName = p.fileName ?? p.file;
|
|
170
227
|
if (fileName) {
|
|
171
|
-
const base =
|
|
228
|
+
const base = path2.basename(fileName);
|
|
172
229
|
if (!basenameMap.has(base)) basenameMap.set(base, /* @__PURE__ */ new Set());
|
|
173
230
|
basenameMap.get(base).add(fileName);
|
|
174
231
|
}
|
|
175
232
|
});
|
|
176
|
-
|
|
177
|
-
const
|
|
233
|
+
results.forEach((entry) => {
|
|
234
|
+
const normalized = normalizeAnalysisResult(entry);
|
|
235
|
+
const file = normalized.fileName;
|
|
178
236
|
if (!file) return;
|
|
179
237
|
builder.addNode(
|
|
180
238
|
file,
|
|
181
|
-
`Issues: ${
|
|
182
|
-
|
|
239
|
+
`Issues: ${normalized.issues.length}`,
|
|
240
|
+
normalized.metrics.tokenCost || GRAPH_CONSTANTS.DEFAULT_REFERENCE_SIZE
|
|
183
241
|
);
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
242
|
+
const rawIssues = Array.isArray(entry.issues) ? entry.issues : [];
|
|
243
|
+
if (rawIssues.length > 0) {
|
|
244
|
+
rawIssues.forEach((issue) => {
|
|
245
|
+
bumpIssue(file, rankSeverity(issue.severity));
|
|
246
|
+
});
|
|
247
|
+
} else {
|
|
248
|
+
normalized.issues.forEach((issue) => {
|
|
249
|
+
bumpIssue(file, issue.severity);
|
|
190
250
|
});
|
|
191
251
|
}
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
const refs = builder.extractReferencedPaths(message);
|
|
252
|
+
normalized.issues.forEach((issue) => {
|
|
253
|
+
const refs = extractReferencedPaths(issue.message);
|
|
195
254
|
refs.forEach((ref) => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
target
|
|
199
|
-
|
|
200
|
-
|
|
255
|
+
const target = path2.isAbsolute(ref) ? ref : path2.resolve(path2.dirname(file), ref);
|
|
256
|
+
builder.addNode(
|
|
257
|
+
target,
|
|
258
|
+
"Referenced file",
|
|
259
|
+
GRAPH_CONSTANTS.DEFAULT_REFERENCE_SIZE
|
|
260
|
+
);
|
|
201
261
|
builder.addEdge(file, target, "reference");
|
|
202
262
|
});
|
|
203
|
-
const percMatch = (message.match(/(\d+)%/) || [])[1];
|
|
263
|
+
const percMatch = (issue.message.match(/(\d+)%/) || [])[1];
|
|
204
264
|
const perc = percMatch ? parseInt(percMatch, 10) : null;
|
|
205
|
-
const wantFuzzy = issue.type === "duplicate-pattern" || /similar/i.test(message) || perc && perc >=
|
|
265
|
+
const wantFuzzy = issue.type === "duplicate-pattern" || /similar/i.test(issue.message) || perc !== null && perc >= GRAPH_CONSTANTS.FUZZY_MATCH_THRESHOLD;
|
|
206
266
|
if (wantFuzzy) {
|
|
207
|
-
const fileGroup =
|
|
267
|
+
const fileGroup = getPackageGroup(file);
|
|
208
268
|
for (const [base, pathsSet] of basenameMap.entries()) {
|
|
209
|
-
if (!message.includes(base) || base ===
|
|
269
|
+
if (!issue.message.includes(base) || base === path2.basename(file))
|
|
210
270
|
continue;
|
|
211
271
|
for (const target of pathsSet) {
|
|
212
|
-
const targetGroup =
|
|
213
|
-
if (fileGroup !== targetGroup && !(perc && perc >=
|
|
214
|
-
|
|
272
|
+
const targetGroup = getPackageGroup(target);
|
|
273
|
+
if (fileGroup !== targetGroup && !(perc !== null && perc >= GRAPH_CONSTANTS.FUZZY_MATCH_HIGH_THRESHOLD))
|
|
274
|
+
continue;
|
|
275
|
+
builder.addNode(
|
|
276
|
+
target,
|
|
277
|
+
"Fuzzy match",
|
|
278
|
+
GRAPH_CONSTANTS.DEFAULT_REFERENCE_SIZE
|
|
279
|
+
);
|
|
215
280
|
builder.addEdge(file, target, "similarity");
|
|
216
281
|
}
|
|
217
282
|
}
|
|
218
283
|
}
|
|
219
284
|
});
|
|
220
285
|
});
|
|
286
|
+
}
|
|
287
|
+
static processDuplicates(builder, report, rootDir, fileIssues) {
|
|
221
288
|
const patternData = report[ToolName.PatternDetect] || report.patternDetect || report.patterns || {};
|
|
222
289
|
const duplicates = (Array.isArray(patternData.duplicates) ? patternData.duplicates : null) || (patternData.summary && Array.isArray(patternData.summary.duplicates) ? patternData.summary.duplicates : null) || (Array.isArray(report.duplicates) ? report.duplicates : []);
|
|
223
290
|
duplicates.forEach((dup) => {
|
|
224
|
-
builder.addNode(
|
|
225
|
-
|
|
291
|
+
builder.addNode(
|
|
292
|
+
dup.file1,
|
|
293
|
+
"Similarity target",
|
|
294
|
+
GRAPH_CONSTANTS.DEFAULT_REFERENCE_SIZE
|
|
295
|
+
);
|
|
296
|
+
builder.addNode(
|
|
297
|
+
dup.file2,
|
|
298
|
+
"Similarity target",
|
|
299
|
+
GRAPH_CONSTANTS.DEFAULT_REFERENCE_SIZE
|
|
300
|
+
);
|
|
226
301
|
builder.addEdge(dup.file1, dup.file2, "similarity");
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
fileIssues.
|
|
233
|
-
|
|
234
|
-
fileIssues.get(f2).duplicates += 1;
|
|
302
|
+
[dup.file1, dup.file2].forEach((file) => {
|
|
303
|
+
const id = path2.resolve(rootDir, file);
|
|
304
|
+
if (!fileIssues.has(id)) {
|
|
305
|
+
fileIssues.set(id, { count: 0, maxSeverity: null, duplicates: 0 });
|
|
306
|
+
}
|
|
307
|
+
fileIssues.get(id).duplicates += 1;
|
|
308
|
+
});
|
|
235
309
|
});
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
310
|
+
}
|
|
311
|
+
static processContext(builder, results, rootDir, bumpIssue) {
|
|
312
|
+
results.forEach((ctx) => {
|
|
313
|
+
const normalized = normalizeAnalysisResult(ctx);
|
|
314
|
+
const file = normalized.fileName;
|
|
239
315
|
if (!file) return;
|
|
240
|
-
builder.addNode(
|
|
241
|
-
|
|
242
|
-
ctx.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
316
|
+
builder.addNode(
|
|
317
|
+
file,
|
|
318
|
+
`Deps: ${ctx.dependencyCount || 0}`,
|
|
319
|
+
GRAPH_CONSTANTS.DEFAULT_CONTEXT_SIZE
|
|
320
|
+
);
|
|
321
|
+
normalized.issues.forEach((issue) => {
|
|
322
|
+
bumpIssue(file, issue.severity);
|
|
323
|
+
});
|
|
249
324
|
(ctx.relatedFiles ?? []).forEach((rel) => {
|
|
250
|
-
const resolvedRel =
|
|
251
|
-
const
|
|
252
|
-
const
|
|
253
|
-
|
|
325
|
+
const resolvedRel = path2.isAbsolute(rel) ? rel : path2.resolve(path2.dirname(file), rel);
|
|
326
|
+
const resolvedFile = path2.resolve(builder.rootDir, file);
|
|
327
|
+
const resolvedTarget = path2.resolve(builder.rootDir, resolvedRel);
|
|
328
|
+
const keyA = `${resolvedFile}->${resolvedTarget}`;
|
|
329
|
+
const keyB = `${resolvedTarget}->${resolvedFile}`;
|
|
330
|
+
if (builder["edgesSet"].has(keyA) || builder["edgesSet"].has(keyB))
|
|
254
331
|
return;
|
|
255
|
-
builder.addNode(
|
|
256
|
-
|
|
257
|
-
|
|
332
|
+
builder.addNode(
|
|
333
|
+
resolvedRel,
|
|
334
|
+
"Related file",
|
|
335
|
+
GRAPH_CONSTANTS.DEFAULT_REFERENCE_SIZE
|
|
258
336
|
);
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
} catch {
|
|
337
|
+
const node = builder["nodesMap"].get(resolvedTarget);
|
|
338
|
+
if (node) {
|
|
339
|
+
node.size = (node.size || 1) + 2;
|
|
263
340
|
}
|
|
341
|
+
builder.addEdge(file, resolvedRel, "related");
|
|
264
342
|
});
|
|
265
|
-
const
|
|
266
|
-
const fileDir = path.dirname(absoluteFile);
|
|
343
|
+
const fileDir = path2.dirname(path2.resolve(builder.rootDir, file));
|
|
267
344
|
(ctx.dependencyList ?? []).forEach((dep) => {
|
|
268
345
|
if (dep.startsWith(".") || dep.startsWith("/")) {
|
|
269
346
|
const possiblePaths = [
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
347
|
+
path2.resolve(fileDir, dep),
|
|
348
|
+
path2.resolve(fileDir, dep + ".ts"),
|
|
349
|
+
path2.resolve(fileDir, dep + ".tsx"),
|
|
350
|
+
path2.resolve(fileDir, dep + ".js"),
|
|
351
|
+
path2.resolve(fileDir, dep, "index.ts"),
|
|
352
|
+
path2.resolve(fileDir, dep, "index.tsx")
|
|
276
353
|
];
|
|
277
354
|
for (const p of possiblePaths) {
|
|
278
355
|
if (fs.existsSync(p)) {
|
|
279
|
-
builder.addNode(
|
|
356
|
+
builder.addNode(
|
|
357
|
+
p,
|
|
358
|
+
"Dependency",
|
|
359
|
+
GRAPH_CONSTANTS.DEFAULT_DEPENDENCY_SIZE
|
|
360
|
+
);
|
|
280
361
|
builder.addEdge(file, p, "dependency");
|
|
281
362
|
break;
|
|
282
363
|
}
|
|
@@ -284,100 +365,64 @@ var GraphBuilder = class _GraphBuilder {
|
|
|
284
365
|
}
|
|
285
366
|
});
|
|
286
367
|
});
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const file = issue.fileName ?? issue.location?.file;
|
|
302
|
-
if (file) {
|
|
303
|
-
builder.addNode(file, "Dependency Issue", 5);
|
|
304
|
-
const sev = rankSeverity(issue.severity ?? null);
|
|
305
|
-
bumpIssue(file, sev);
|
|
368
|
+
}
|
|
369
|
+
static processToolResults(builder, toolName, legacyKey, report, bumpIssue, title) {
|
|
370
|
+
const camelKey = toolName.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
371
|
+
const toolData = report[toolName] ?? report[camelKey] ?? report[legacyKey];
|
|
372
|
+
if (!toolData) return;
|
|
373
|
+
const results = Array.isArray(toolData) ? toolData : toolData.results ?? toolData.issues ?? [];
|
|
374
|
+
results.forEach((item) => {
|
|
375
|
+
if (!Array.isArray(item.issues) && (item.severity || item.message)) {
|
|
376
|
+
const file2 = item.fileName ?? item.file ?? item.location?.file;
|
|
377
|
+
if (file2) {
|
|
378
|
+
builder.addNode(file2, title, GRAPH_CONSTANTS.DEFAULT_REFERENCE_SIZE);
|
|
379
|
+
bumpIssue(file2, rankSeverity(item.severity));
|
|
380
|
+
}
|
|
381
|
+
return;
|
|
306
382
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
ToolName.ContractEnforcement,
|
|
310
|
-
"contractEnforcement"
|
|
311
|
-
);
|
|
312
|
-
contractEnforcementResults.forEach((issue) => {
|
|
313
|
-
const file = issue.fileName ?? issue.location?.file;
|
|
383
|
+
const normalized = normalizeAnalysisResult(item);
|
|
384
|
+
const file = normalized.fileName;
|
|
314
385
|
if (file) {
|
|
315
|
-
builder.addNode(file,
|
|
316
|
-
|
|
317
|
-
|
|
386
|
+
builder.addNode(file, title, GRAPH_CONSTANTS.DEFAULT_REFERENCE_SIZE);
|
|
387
|
+
normalized.issues.forEach((issue) => {
|
|
388
|
+
bumpIssue(file, issue.severity);
|
|
389
|
+
});
|
|
318
390
|
}
|
|
319
391
|
});
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const
|
|
323
|
-
switch (sev) {
|
|
324
|
-
case Severity.Critical:
|
|
325
|
-
return "#ff4d4f";
|
|
326
|
-
// red
|
|
327
|
-
case Severity.Major:
|
|
328
|
-
return "#ff9900";
|
|
329
|
-
// orange
|
|
330
|
-
case Severity.Minor:
|
|
331
|
-
return "#ffd666";
|
|
332
|
-
// yellow
|
|
333
|
-
case Severity.Info:
|
|
334
|
-
return "#91d5ff";
|
|
335
|
-
// light blue
|
|
336
|
-
default:
|
|
337
|
-
return "#97c2fc";
|
|
338
|
-
}
|
|
339
|
-
};
|
|
392
|
+
}
|
|
393
|
+
static finalizeGraph(builder, fileIssues, report) {
|
|
394
|
+
const graph = builder.build();
|
|
340
395
|
let criticalIssues = 0;
|
|
341
396
|
let majorIssues = 0;
|
|
342
397
|
let minorIssues = 0;
|
|
343
398
|
let infoIssues = 0;
|
|
344
|
-
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
else if (
|
|
353
|
-
|
|
354
|
-
else if (
|
|
399
|
+
graph.nodes.forEach((node) => {
|
|
400
|
+
const record = fileIssues.get(node.id);
|
|
401
|
+
if (record) {
|
|
402
|
+
node.duplicates = record.duplicates || 0;
|
|
403
|
+
node.color = getColorForSeverity(record.maxSeverity);
|
|
404
|
+
node.group = getPackageGroup(node.id);
|
|
405
|
+
if (record.maxSeverity === Severity.Critical)
|
|
406
|
+
criticalIssues += record.count;
|
|
407
|
+
else if (record.maxSeverity === Severity.Major)
|
|
408
|
+
majorIssues += record.count;
|
|
409
|
+
else if (record.maxSeverity === Severity.Minor)
|
|
410
|
+
minorIssues += record.count;
|
|
411
|
+
else if (record.maxSeverity === Severity.Info)
|
|
412
|
+
infoIssues += record.count;
|
|
355
413
|
} else {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
const graph = {
|
|
362
|
-
nodes,
|
|
363
|
-
edges,
|
|
364
|
-
clusters: [],
|
|
365
|
-
issues: [],
|
|
366
|
-
metadata: {
|
|
367
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
368
|
-
totalFiles: nodes.length,
|
|
369
|
-
totalDependencies: edges.length,
|
|
370
|
-
analysisTypes: [],
|
|
371
|
-
criticalIssues,
|
|
372
|
-
majorIssues,
|
|
373
|
-
minorIssues,
|
|
374
|
-
infoIssues,
|
|
375
|
-
tokenBudget: report.scoring?.tokenBudget
|
|
376
|
-
},
|
|
377
|
-
truncated: {
|
|
378
|
-
nodes: false,
|
|
379
|
-
edges: false
|
|
414
|
+
node.color = getColorForSeverity(null);
|
|
415
|
+
node.group = getPackageGroup(node.id);
|
|
416
|
+
node.duplicates = 0;
|
|
380
417
|
}
|
|
418
|
+
});
|
|
419
|
+
graph.metadata = {
|
|
420
|
+
...graph.metadata,
|
|
421
|
+
criticalIssues,
|
|
422
|
+
majorIssues,
|
|
423
|
+
minorIssues,
|
|
424
|
+
infoIssues,
|
|
425
|
+
tokenBudget: report.scoring?.tokenBudget
|
|
381
426
|
};
|
|
382
427
|
return graph;
|
|
383
428
|
}
|