arcvision 0.1.17 → 0.1.18
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/index.js +55 -9
- package/package.json +1 -1
- package/src/core/blastRadius.js +45 -1
- package/src/core/scanner.js +9 -0
- package/src/index.js +31 -8
package/dist/index.js
CHANGED
|
@@ -56558,10 +56558,32 @@ var require_blastRadius = __commonJS({
|
|
|
56558
56558
|
}
|
|
56559
56559
|
return maxFile;
|
|
56560
56560
|
}
|
|
56561
|
+
function getTopBlastRadiusFiles2(blastRadiusMap, limit = 3) {
|
|
56562
|
+
if (Object.keys(blastRadiusMap).length === 0) {
|
|
56563
|
+
return [];
|
|
56564
|
+
}
|
|
56565
|
+
const sortedFiles = Object.entries(blastRadiusMap).map(([file, blast_radius]) => ({ file, blast_radius })).sort((a, b) => b.blast_radius - a.blast_radius);
|
|
56566
|
+
return sortedFiles.slice(0, limit);
|
|
56567
|
+
}
|
|
56568
|
+
function computeBlastRadiusWithPercentage2(blastRadiusMap, totalFiles) {
|
|
56569
|
+
if (Object.keys(blastRadiusMap).length === 0 || totalFiles === 0) {
|
|
56570
|
+
return [];
|
|
56571
|
+
}
|
|
56572
|
+
return Object.entries(blastRadiusMap).map(([file, blastRadius]) => {
|
|
56573
|
+
const percentOfGraph = Number((blastRadius / totalFiles * 100).toFixed(2));
|
|
56574
|
+
return {
|
|
56575
|
+
file,
|
|
56576
|
+
blastRadius,
|
|
56577
|
+
percentOfGraph
|
|
56578
|
+
};
|
|
56579
|
+
}).sort((a, b) => b.blastRadius - a.blastRadius);
|
|
56580
|
+
}
|
|
56561
56581
|
module2.exports = {
|
|
56562
56582
|
buildReverseDependencyGraph,
|
|
56563
56583
|
computeBlastRadius,
|
|
56564
|
-
findHighestBlastRadius: findHighestBlastRadius2
|
|
56584
|
+
findHighestBlastRadius: findHighestBlastRadius2,
|
|
56585
|
+
getTopBlastRadiusFiles: getTopBlastRadiusFiles2,
|
|
56586
|
+
computeBlastRadiusWithPercentage: computeBlastRadiusWithPercentage2
|
|
56565
56587
|
};
|
|
56566
56588
|
}
|
|
56567
56589
|
});
|
|
@@ -56638,6 +56660,12 @@ var require_scanner = __commonJS({
|
|
|
56638
56660
|
architectureMap.nodes.forEach((node) => {
|
|
56639
56661
|
node.metadata.blast_radius = blastRadiusMap[node.id] || 0;
|
|
56640
56662
|
});
|
|
56663
|
+
const totalFiles = architectureMap.nodes.length;
|
|
56664
|
+
const { computeBlastRadiusWithPercentage: computeBlastRadiusWithPercentage2 } = require_blastRadius();
|
|
56665
|
+
const topFiles = computeBlastRadiusWithPercentage2(blastRadiusMap, totalFiles);
|
|
56666
|
+
architectureMap.riskSurface = {
|
|
56667
|
+
topBlastRadiusFiles: topFiles.slice(0, 3)
|
|
56668
|
+
};
|
|
56641
56669
|
return architectureMap;
|
|
56642
56670
|
} catch (err) {
|
|
56643
56671
|
throw err;
|
|
@@ -56664,7 +56692,7 @@ try {
|
|
|
56664
56692
|
}
|
|
56665
56693
|
var CONFIG_FILE = path.join(os.homedir(), ".arcvisionrc");
|
|
56666
56694
|
var API_URL = process.env.ARCVISION_API_URL || "https://arcvisiondev.vercel.app";
|
|
56667
|
-
var { findHighestBlastRadius } = require_blastRadius();
|
|
56695
|
+
var { findHighestBlastRadius, getTopBlastRadiusFiles, computeBlastRadiusWithPercentage } = require_blastRadius();
|
|
56668
56696
|
function analyzeBlastRadius(architectureMap) {
|
|
56669
56697
|
const blastRadiusData = [];
|
|
56670
56698
|
architectureMap.nodes.forEach((node) => {
|
|
@@ -56673,10 +56701,16 @@ function analyzeBlastRadius(architectureMap) {
|
|
|
56673
56701
|
blast_radius: node.metadata.blast_radius || 0
|
|
56674
56702
|
});
|
|
56675
56703
|
});
|
|
56676
|
-
|
|
56704
|
+
const blastRadiusMap = blastRadiusData.reduce((acc, item) => {
|
|
56677
56705
|
acc[item.file] = item.blast_radius;
|
|
56678
56706
|
return acc;
|
|
56679
|
-
}, {})
|
|
56707
|
+
}, {});
|
|
56708
|
+
const totalFiles = architectureMap.nodes.length;
|
|
56709
|
+
const topFiles = computeBlastRadiusWithPercentage(blastRadiusMap, totalFiles);
|
|
56710
|
+
return {
|
|
56711
|
+
topFiles: topFiles.slice(0, 3),
|
|
56712
|
+
totalFiles
|
|
56713
|
+
};
|
|
56680
56714
|
}
|
|
56681
56715
|
function saveToken(token) {
|
|
56682
56716
|
try {
|
|
@@ -56816,11 +56850,23 @@ program.command("scan").description("Scan the current directory and generate arc
|
|
|
56816
56850
|
try {
|
|
56817
56851
|
const map = await scanner.scan(targetDir);
|
|
56818
56852
|
console.log(chalk.green("Scan complete!"));
|
|
56819
|
-
const
|
|
56820
|
-
if (
|
|
56821
|
-
|
|
56822
|
-
|
|
56823
|
-
|
|
56853
|
+
const blastRadiusAnalysis = analyzeBlastRadius(map);
|
|
56854
|
+
if (blastRadiusAnalysis && blastRadiusAnalysis.topFiles && blastRadiusAnalysis.topFiles.length > 0) {
|
|
56855
|
+
console.log("\n\u26A0\uFE0F Top Structural Risk Hubs Detected:\n");
|
|
56856
|
+
blastRadiusAnalysis.topFiles.forEach((item, index) => {
|
|
56857
|
+
let warningMessage = "";
|
|
56858
|
+
if (index === 0) {
|
|
56859
|
+
warningMessage = "Changes here may silently propagate across the system.";
|
|
56860
|
+
} else if (index === 1) {
|
|
56861
|
+
warningMessage = "Acts as a coordination layer between components.";
|
|
56862
|
+
} else {
|
|
56863
|
+
warningMessage = "Modifications can cause widespread inconsistencies.";
|
|
56864
|
+
}
|
|
56865
|
+
console.log(`${index + 1}. ${item.file}`);
|
|
56866
|
+
console.log(` Blast Radius: ${item.blastRadius} files (${item.percentOfGraph}%)`);
|
|
56867
|
+
console.log(` Warning: ${warningMessage}
|
|
56868
|
+
`);
|
|
56869
|
+
});
|
|
56824
56870
|
} else {
|
|
56825
56871
|
console.log("\nNo high-risk files detected based on import dependencies.");
|
|
56826
56872
|
}
|
package/package.json
CHANGED
package/src/core/blastRadius.js
CHANGED
|
@@ -77,8 +77,52 @@ function findHighestBlastRadius(blastRadiusMap) {
|
|
|
77
77
|
return maxFile;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Gets the top N files by blast radius
|
|
82
|
+
* @param {Object} blastRadiusMap - Map of file paths to their blast radius
|
|
83
|
+
* @param {number} limit - Number of top files to return (default: 3)
|
|
84
|
+
* @returns {Array} Array of top N files with their blast radius
|
|
85
|
+
*/
|
|
86
|
+
function getTopBlastRadiusFiles(blastRadiusMap, limit = 3) {
|
|
87
|
+
if (Object.keys(blastRadiusMap).length === 0) {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Convert to array and sort by blast radius descending
|
|
92
|
+
const sortedFiles = Object.entries(blastRadiusMap)
|
|
93
|
+
.map(([file, blast_radius]) => ({ file, blast_radius }))
|
|
94
|
+
.sort((a, b) => b.blast_radius - a.blast_radius);
|
|
95
|
+
|
|
96
|
+
return sortedFiles.slice(0, limit);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Computes blast radius results with percentage of total files
|
|
101
|
+
* @param {Object} blastRadiusMap - Map of file paths to their blast radius
|
|
102
|
+
* @param {number} totalFiles - Total number of files in the project
|
|
103
|
+
* @returns {Array} Array of files with blast radius and percentage
|
|
104
|
+
*/
|
|
105
|
+
function computeBlastRadiusWithPercentage(blastRadiusMap, totalFiles) {
|
|
106
|
+
if (Object.keys(blastRadiusMap).length === 0 || totalFiles === 0) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return Object.entries(blastRadiusMap)
|
|
111
|
+
.map(([file, blastRadius]) => {
|
|
112
|
+
const percentOfGraph = Number(((blastRadius / totalFiles) * 100).toFixed(2));
|
|
113
|
+
return {
|
|
114
|
+
file,
|
|
115
|
+
blastRadius,
|
|
116
|
+
percentOfGraph
|
|
117
|
+
};
|
|
118
|
+
})
|
|
119
|
+
.sort((a, b) => b.blastRadius - a.blastRadius);
|
|
120
|
+
}
|
|
121
|
+
|
|
80
122
|
module.exports = {
|
|
81
123
|
buildReverseDependencyGraph,
|
|
82
124
|
computeBlastRadius,
|
|
83
|
-
findHighestBlastRadius
|
|
125
|
+
findHighestBlastRadius,
|
|
126
|
+
getTopBlastRadiusFiles,
|
|
127
|
+
computeBlastRadiusWithPercentage
|
|
84
128
|
};
|
package/src/core/scanner.js
CHANGED
|
@@ -97,6 +97,15 @@ async function scan(directory) {
|
|
|
97
97
|
node.metadata.blast_radius = blastRadiusMap[node.id] || 0;
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
+
// Add top blast radius files to the architecture map
|
|
101
|
+
const totalFiles = architectureMap.nodes.length;
|
|
102
|
+
const { computeBlastRadiusWithPercentage } = require('./blastRadius');
|
|
103
|
+
const topFiles = computeBlastRadiusWithPercentage(blastRadiusMap, totalFiles);
|
|
104
|
+
|
|
105
|
+
architectureMap.riskSurface = {
|
|
106
|
+
topBlastRadiusFiles: topFiles.slice(0, 3)
|
|
107
|
+
};
|
|
108
|
+
|
|
100
109
|
return architectureMap;
|
|
101
110
|
|
|
102
111
|
} catch (err) {
|
package/src/index.js
CHANGED
|
@@ -25,7 +25,7 @@ const CONFIG_FILE = path.join(os.homedir(), '.arcvisionrc');
|
|
|
25
25
|
const API_URL = process.env.ARCVISION_API_URL || 'https://arcvisiondev.vercel.app';
|
|
26
26
|
|
|
27
27
|
// Blast radius analysis
|
|
28
|
-
const { findHighestBlastRadius } = require('./core/blastRadius');
|
|
28
|
+
const { findHighestBlastRadius, getTopBlastRadiusFiles, computeBlastRadiusWithPercentage } = require('./core/blastRadius');
|
|
29
29
|
|
|
30
30
|
function analyzeBlastRadius(architectureMap) {
|
|
31
31
|
// Extract blast radius information from nodes
|
|
@@ -38,11 +38,20 @@ function analyzeBlastRadius(architectureMap) {
|
|
|
38
38
|
});
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
return findHighestBlastRadius(blastRadiusData.reduce((acc, item) => {
|
|
41
|
+
const blastRadiusMap = blastRadiusData.reduce((acc, item) => {
|
|
43
42
|
acc[item.file] = item.blast_radius;
|
|
44
43
|
return acc;
|
|
45
|
-
}, {})
|
|
44
|
+
}, {});
|
|
45
|
+
|
|
46
|
+
const totalFiles = architectureMap.nodes.length;
|
|
47
|
+
|
|
48
|
+
// Get top 3 files by blast radius with percentages
|
|
49
|
+
const topFiles = computeBlastRadiusWithPercentage(blastRadiusMap, totalFiles);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
topFiles: topFiles.slice(0, 3),
|
|
53
|
+
totalFiles: totalFiles
|
|
54
|
+
};
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
function saveToken(token) {
|
|
@@ -220,10 +229,24 @@ program
|
|
|
220
229
|
console.log(chalk.green('Scan complete!'));
|
|
221
230
|
|
|
222
231
|
// Analyze and print blast radius insight
|
|
223
|
-
const
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
|
|
232
|
+
const blastRadiusAnalysis = analyzeBlastRadius(map);
|
|
233
|
+
if (blastRadiusAnalysis && blastRadiusAnalysis.topFiles && blastRadiusAnalysis.topFiles.length > 0) {
|
|
234
|
+
console.log('\n⚠️ Top Structural Risk Hubs Detected:\n');
|
|
235
|
+
|
|
236
|
+
blastRadiusAnalysis.topFiles.forEach((item, index) => {
|
|
237
|
+
let warningMessage = '';
|
|
238
|
+
if (index === 0) {
|
|
239
|
+
warningMessage = 'Changes here may silently propagate across the system.';
|
|
240
|
+
} else if (index === 1) {
|
|
241
|
+
warningMessage = 'Acts as a coordination layer between components.';
|
|
242
|
+
} else {
|
|
243
|
+
warningMessage = 'Modifications can cause widespread inconsistencies.';
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
console.log(`${index + 1}. ${item.file}`);
|
|
247
|
+
console.log(` Blast Radius: ${item.blastRadius} files (${item.percentOfGraph}%)`);
|
|
248
|
+
console.log(` Warning: ${warningMessage}\n`);
|
|
249
|
+
});
|
|
227
250
|
} else {
|
|
228
251
|
console.log('\nNo high-risk files detected based on import dependencies.');
|
|
229
252
|
}
|