depwire-cli 0.9.24 → 0.9.26
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/{chunk-ORGAO3HT.js → chunk-B2KGFBZL.js} +59 -1
- package/dist/{chunk-QHVWDUSX.js → chunk-YYY5TNG7.js} +1446 -1
- package/dist/index.js +482 -36
- package/dist/mcpb-entry.js +2 -2
- package/dist/sdk.d.ts +49 -1
- package/dist/sdk.js +3 -1
- package/dist/viz/public/arc.js +185 -7
- package/package.json +1 -1
package/dist/sdk.d.ts
CHANGED
|
@@ -222,6 +222,54 @@ declare class SimulationEngine {
|
|
|
222
222
|
private computeHealthScore;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
type Severity = 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
226
|
+
type VulnerabilityClass = 'dependency-cve' | 'shell-injection' | 'code-injection' | 'secrets' | 'path-traversal' | 'auth' | 'input-validation' | 'information-disclosure' | 'architecture' | 'cryptography' | 'supply-chain' | 'frontend-xss';
|
|
227
|
+
interface SecurityFinding {
|
|
228
|
+
id: string;
|
|
229
|
+
severity: Severity;
|
|
230
|
+
vulnerabilityClass: VulnerabilityClass;
|
|
231
|
+
file: string;
|
|
232
|
+
line?: number;
|
|
233
|
+
symbol?: string;
|
|
234
|
+
title: string;
|
|
235
|
+
description: string;
|
|
236
|
+
attackScenario: string;
|
|
237
|
+
suggestedFix: string;
|
|
238
|
+
graphReachability?: {
|
|
239
|
+
entryPoints: string[];
|
|
240
|
+
reachableFrom: number;
|
|
241
|
+
elevatedBy: string;
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
interface SecurityScanResult {
|
|
245
|
+
scannedAt: string;
|
|
246
|
+
projectRoot: string;
|
|
247
|
+
filesScanned: number;
|
|
248
|
+
findings: SecurityFinding[];
|
|
249
|
+
summary: {
|
|
250
|
+
critical: number;
|
|
251
|
+
high: number;
|
|
252
|
+
medium: number;
|
|
253
|
+
low: number;
|
|
254
|
+
info: number;
|
|
255
|
+
total: number;
|
|
256
|
+
};
|
|
257
|
+
dependencyAudit: {
|
|
258
|
+
ran: boolean;
|
|
259
|
+
packageManager: string | null;
|
|
260
|
+
rawOutput: string;
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
interface SecurityScanOptions {
|
|
264
|
+
target?: string;
|
|
265
|
+
classes?: VulnerabilityClass[];
|
|
266
|
+
format?: 'table' | 'json' | 'sarif';
|
|
267
|
+
failOn?: Severity;
|
|
268
|
+
graphAware?: boolean;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
declare function scanSecurity(projectRoot: string, graph: DirectedGraph, options?: SecurityScanOptions): Promise<SecurityScanResult>;
|
|
272
|
+
|
|
225
273
|
/**
|
|
226
274
|
* depwire-cli SDK — Public API Surface
|
|
227
275
|
*
|
|
@@ -234,4 +282,4 @@ declare class SimulationEngine {
|
|
|
234
282
|
/** Current SDK version — matches depwire-cli npm version */
|
|
235
283
|
declare const DepwireSDKVersion: string;
|
|
236
284
|
|
|
237
|
-
export { type BrokenImport, DepwireSDKVersion, type GraphDiff, type HealthDelta, type SimulationAction, SimulationEngine, type SimulationResult, analyzeDeadCode, buildGraph, calculateHealthScore, generateDocs, getArchitectureSummary, getImpact, parseProject, searchSymbols };
|
|
285
|
+
export { type BrokenImport, DepwireSDKVersion, type GraphDiff, type HealthDelta, type SecurityFinding, type SecurityScanOptions, type SecurityScanResult, type Severity, type SimulationAction, SimulationEngine, type SimulationResult, type VulnerabilityClass, analyzeDeadCode, buildGraph, calculateHealthScore, generateDocs, getArchitectureSummary, getImpact, parseProject, scanSecurity, searchSymbols };
|
package/dist/sdk.js
CHANGED
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
getArchitectureSummary,
|
|
8
8
|
getImpact,
|
|
9
9
|
parseProject,
|
|
10
|
+
scanSecurity,
|
|
10
11
|
searchSymbols
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-YYY5TNG7.js";
|
|
12
13
|
|
|
13
14
|
// src/sdk.ts
|
|
14
15
|
import { readFileSync } from "fs";
|
|
@@ -28,5 +29,6 @@ export {
|
|
|
28
29
|
getArchitectureSummary,
|
|
29
30
|
getImpact,
|
|
30
31
|
parseProject,
|
|
32
|
+
scanSecurity,
|
|
31
33
|
searchSymbols
|
|
32
34
|
};
|
package/dist/viz/public/arc.js
CHANGED
|
@@ -1,4 +1,176 @@
|
|
|
1
1
|
// Arc Diagram Renderer using D3.js
|
|
2
|
+
|
|
3
|
+
// ── Factory function for creating isolated arc diagram instances ──
|
|
4
|
+
window.createArcDiagram = function(containerId, svgId, tooltipId, data) {
|
|
5
|
+
let _data = data;
|
|
6
|
+
let _svg = null;
|
|
7
|
+
let _g = null;
|
|
8
|
+
let _filePositions = new Map();
|
|
9
|
+
let _selectedFile = null;
|
|
10
|
+
let _selectedArc = null;
|
|
11
|
+
|
|
12
|
+
function _showTooltip(event, content) {
|
|
13
|
+
const el = document.getElementById(tooltipId);
|
|
14
|
+
if (!el) return;
|
|
15
|
+
el.innerHTML = content;
|
|
16
|
+
el.style.left = (event.pageX + 10) + 'px';
|
|
17
|
+
el.style.top = (event.pageY + 10) + 'px';
|
|
18
|
+
el.classList.add('show');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function _hideTooltip() {
|
|
22
|
+
const el = document.getElementById(tooltipId);
|
|
23
|
+
if (!el) return;
|
|
24
|
+
el.classList.remove('show');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function _clearSelection() {
|
|
28
|
+
_selectedFile = null;
|
|
29
|
+
_selectedArc = null;
|
|
30
|
+
const ctr = d3.select('#' + containerId);
|
|
31
|
+
ctr.selectAll('.arc').classed('highlighted', false).classed('dimmed', false);
|
|
32
|
+
ctr.selectAll('.file-bar').classed('highlighted', false).classed('dimmed', false);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function render() {
|
|
36
|
+
const container = document.getElementById(containerId);
|
|
37
|
+
if (!container || !_data) return;
|
|
38
|
+
|
|
39
|
+
const width = container.clientWidth;
|
|
40
|
+
const height = container.clientHeight;
|
|
41
|
+
_filePositions.clear();
|
|
42
|
+
_selectedFile = null;
|
|
43
|
+
_selectedArc = null;
|
|
44
|
+
|
|
45
|
+
const svgEl = d3.select('#' + svgId);
|
|
46
|
+
svgEl.selectAll('*').remove();
|
|
47
|
+
_svg = svgEl.attr('width', width).attr('height', height);
|
|
48
|
+
_g = _svg.append('g');
|
|
49
|
+
|
|
50
|
+
const zoom = d3.zoom().scaleExtent([0.5, 4]).on('zoom', (event) => {
|
|
51
|
+
_g.attr('transform', event.transform);
|
|
52
|
+
});
|
|
53
|
+
_svg.call(zoom);
|
|
54
|
+
|
|
55
|
+
const margin = { top: 60, right: 40, bottom: 120, left: 40 };
|
|
56
|
+
const plotWidth = width - margin.left - margin.right;
|
|
57
|
+
const plotHeight = height - margin.top - margin.bottom;
|
|
58
|
+
const baseline = margin.top + plotHeight;
|
|
59
|
+
|
|
60
|
+
const totalSymbols = d3.sum(_data.files, d => d.symbolCount);
|
|
61
|
+
const minBarWidth = 4;
|
|
62
|
+
const gap = 2;
|
|
63
|
+
let x = margin.left;
|
|
64
|
+
|
|
65
|
+
_data.files.forEach(file => {
|
|
66
|
+
const barWidth = Math.max(minBarWidth, (file.symbolCount / totalSymbols) * plotWidth * 0.8);
|
|
67
|
+
_filePositions.set(file.path, { x: x + barWidth / 2, width: barWidth, file: file });
|
|
68
|
+
x += barWidth + gap;
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const directories = [...new Set(_data.files.map(f => f.directory))];
|
|
72
|
+
const colorScale = d3.scaleOrdinal().domain(directories)
|
|
73
|
+
.range(['#4a9eff', '#7c3aed', '#ec4899', '#f59e0b', '#10b981', '#06b6d4']);
|
|
74
|
+
|
|
75
|
+
const maxDistance = d3.max(_data.arcs, arc => {
|
|
76
|
+
const s = _filePositions.get(arc.sourceFile);
|
|
77
|
+
const t = _filePositions.get(arc.targetFile);
|
|
78
|
+
return (s && t) ? Math.abs(t.x - s.x) : 0;
|
|
79
|
+
}) || 1;
|
|
80
|
+
|
|
81
|
+
const ctr = d3.select('#' + containerId);
|
|
82
|
+
|
|
83
|
+
_g.selectAll('.arc').data(_data.arcs).enter().append('path')
|
|
84
|
+
.attr('class', 'arc')
|
|
85
|
+
.attr('d', d => {
|
|
86
|
+
const s = _filePositions.get(d.sourceFile);
|
|
87
|
+
const t = _filePositions.get(d.targetFile);
|
|
88
|
+
if (!s || !t) return null;
|
|
89
|
+
const x1 = s.x, x2 = t.x, dist = Math.abs(x2 - x1), midX = (x1 + x2) / 2;
|
|
90
|
+
return `M ${x1} ${baseline} Q ${midX} ${baseline - dist * 0.4} ${x2} ${baseline}`;
|
|
91
|
+
})
|
|
92
|
+
.attr('stroke', d => {
|
|
93
|
+
const s = _filePositions.get(d.sourceFile);
|
|
94
|
+
const t = _filePositions.get(d.targetFile);
|
|
95
|
+
if (!s || !t) return '#4a9eff';
|
|
96
|
+
return d3.interpolateRainbow(Math.abs(t.x - s.x) / maxDistance);
|
|
97
|
+
})
|
|
98
|
+
.attr('stroke-width', d => Math.min(4, 1 + Math.log(d.edgeCount)))
|
|
99
|
+
.on('mouseover', function(event, d) {
|
|
100
|
+
if (_selectedArc) return;
|
|
101
|
+
d3.select(this).classed('highlighted', true);
|
|
102
|
+
ctr.selectAll('.arc').filter(a => a !== d).classed('dimmed', true);
|
|
103
|
+
ctr.selectAll('.file-bar').each(function(f) {
|
|
104
|
+
const match = f.path === d.sourceFile || f.path === d.targetFile;
|
|
105
|
+
d3.select(this).classed('highlighted', match).classed('dimmed', !match);
|
|
106
|
+
});
|
|
107
|
+
_showTooltip(event, `<div class="tooltip-line"><strong>${d.sourceFile}</strong> → <strong>${d.targetFile}</strong></div><div class="tooltip-line"><span class="tooltip-label">Edges:</span> ${d.edgeCount}</div>`);
|
|
108
|
+
})
|
|
109
|
+
.on('mouseout', function() {
|
|
110
|
+
if (_selectedArc) return;
|
|
111
|
+
d3.select(this).classed('highlighted', false);
|
|
112
|
+
ctr.selectAll('.arc').classed('dimmed', false);
|
|
113
|
+
ctr.selectAll('.file-bar').classed('highlighted', false).classed('dimmed', false);
|
|
114
|
+
_hideTooltip();
|
|
115
|
+
})
|
|
116
|
+
.on('click', function(event, d) {
|
|
117
|
+
event.stopPropagation();
|
|
118
|
+
if (_selectedArc === d) { _selectedArc = null; ctr.selectAll('.arc,.file-bar').classed('highlighted', false).classed('dimmed', false); _hideTooltip(); return; }
|
|
119
|
+
_selectedArc = d; _selectedFile = null;
|
|
120
|
+
ctr.selectAll('.arc').classed('highlighted', false).classed('dimmed', true);
|
|
121
|
+
d3.select(this).classed('highlighted', true).classed('dimmed', false);
|
|
122
|
+
ctr.selectAll('.file-bar').each(function(f) {
|
|
123
|
+
const match = f.path === d.sourceFile || f.path === d.targetFile;
|
|
124
|
+
d3.select(this).classed('highlighted', match).classed('dimmed', !match);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
_g.selectAll('.file-bar').data(_data.files).enter().append('rect')
|
|
129
|
+
.attr('class', 'file-bar')
|
|
130
|
+
.attr('x', d => { const p = _filePositions.get(d.path); return p.x - p.width / 2; })
|
|
131
|
+
.attr('y', baseline).attr('width', d => _filePositions.get(d.path).width).attr('height', 8)
|
|
132
|
+
.attr('fill', d => colorScale(d.directory))
|
|
133
|
+
.on('mouseover', function(event, d) {
|
|
134
|
+
if (_selectedFile) return;
|
|
135
|
+
d3.select(this).classed('highlighted', true);
|
|
136
|
+
const connected = _data.arcs.filter(a => a.sourceFile === d.path || a.targetFile === d.path);
|
|
137
|
+
ctr.selectAll('.arc').classed('highlighted', a => connected.includes(a)).classed('dimmed', a => !connected.includes(a));
|
|
138
|
+
ctr.selectAll('.file-bar').filter(f => f !== d).classed('dimmed', true);
|
|
139
|
+
_showTooltip(event, `<div class="tooltip-line"><strong>${d.path}</strong></div><div class="tooltip-line"><span class="tooltip-label">Symbols:</span> ${d.symbolCount} | In: ${d.incomingCount} | Out: ${d.outgoingCount}</div>`);
|
|
140
|
+
})
|
|
141
|
+
.on('mouseout', function() {
|
|
142
|
+
if (_selectedFile) return;
|
|
143
|
+
d3.select(this).classed('highlighted', false);
|
|
144
|
+
ctr.selectAll('.arc').classed('highlighted', false).classed('dimmed', false);
|
|
145
|
+
ctr.selectAll('.file-bar').classed('dimmed', false);
|
|
146
|
+
_hideTooltip();
|
|
147
|
+
})
|
|
148
|
+
.on('click', function(event, d) {
|
|
149
|
+
event.stopPropagation();
|
|
150
|
+
if (_selectedFile === d) { _selectedFile = null; ctr.selectAll('.arc,.file-bar').classed('highlighted', false).classed('dimmed', false); return; }
|
|
151
|
+
_selectedFile = d; _selectedArc = null;
|
|
152
|
+
const connected = _data.arcs.filter(a => a.sourceFile === d.path || a.targetFile === d.path);
|
|
153
|
+
ctr.selectAll('.arc').classed('highlighted', a => connected.includes(a)).classed('dimmed', a => !connected.includes(a));
|
|
154
|
+
ctr.selectAll('.file-bar').classed('highlighted', f => f === d).classed('dimmed', f => f !== d);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
_g.selectAll('.file-label').data(_data.files).enter().append('text')
|
|
158
|
+
.attr('class', 'file-label')
|
|
159
|
+
.attr('x', d => _filePositions.get(d.path).x)
|
|
160
|
+
.attr('y', baseline + 20)
|
|
161
|
+
.attr('transform', d => `rotate(-45, ${_filePositions.get(d.path).x}, ${baseline + 20})`)
|
|
162
|
+
.attr('text-anchor', 'end')
|
|
163
|
+
.text(d => d.path.split('/').pop());
|
|
164
|
+
|
|
165
|
+
_svg.append('text').attr('x', 10).attr('y', 20).attr('fill', '#4a9eff')
|
|
166
|
+
.attr('font-size', '12px').attr('cursor', 'pointer').text('↺ Reset View')
|
|
167
|
+
.on('click', () => { _svg.transition().duration(750).call(zoom.transform, d3.zoomIdentity); _clearSelection(); });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return { render };
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// ── Legacy global state and functions (used by depwire viz) ──
|
|
2
174
|
let graphData = null;
|
|
3
175
|
let svg = null;
|
|
4
176
|
let g = null;
|
|
@@ -23,12 +195,16 @@ async function init() {
|
|
|
23
195
|
// Render diagram
|
|
24
196
|
renderArcDiagram();
|
|
25
197
|
|
|
26
|
-
// Setup interactions
|
|
27
|
-
|
|
28
|
-
|
|
198
|
+
// Setup interactions (not in whatif mode)
|
|
199
|
+
if (!window.__depwireWhatIf) {
|
|
200
|
+
setupSearch();
|
|
201
|
+
setupExport();
|
|
202
|
+
}
|
|
29
203
|
|
|
30
|
-
// Setup WebSocket for live updates
|
|
31
|
-
|
|
204
|
+
// Setup WebSocket for live updates (not in whatif mode)
|
|
205
|
+
if (!window.__depwireWhatIf) {
|
|
206
|
+
setupWebSocket();
|
|
207
|
+
}
|
|
32
208
|
|
|
33
209
|
// Handle window resize
|
|
34
210
|
window.addEventListener('resize', () => {
|
|
@@ -575,5 +751,7 @@ d3.select('body').on('click', () => {
|
|
|
575
751
|
}
|
|
576
752
|
});
|
|
577
753
|
|
|
578
|
-
// Initialize on page load
|
|
579
|
-
|
|
754
|
+
// Initialize on page load — only if NOT in What If context
|
|
755
|
+
if (!window.__depwireWhatIf) {
|
|
756
|
+
init();
|
|
757
|
+
}
|