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 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
- return findHighestBlastRadius(blastRadiusData.reduce((acc, item) => {
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 blastRadiusInfo = analyzeBlastRadius(map);
56820
- if (blastRadiusInfo && blastRadiusInfo.blast_radius > 0) {
56821
- const totalFiles = map.nodes.length;
56822
- console.log(`
56823
- \u26A0\uFE0F ${blastRadiusInfo.file} at the center of the system (blast radius: ${blastRadiusInfo.blast_radius} out of ${totalFiles}). This file represents a single point of systemic failure. Changes here may affect many parts of the system.`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcvision",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "Architecture scanner for modern codebases",
5
5
  "bin": {
6
6
  "arcvision": "./dist/index.js"
@@ -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
  };
@@ -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
- // Find the file with the highest blast radius
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 blastRadiusInfo = analyzeBlastRadius(map);
224
- if (blastRadiusInfo && blastRadiusInfo.blast_radius > 0) {
225
- const totalFiles = map.nodes.length;
226
- console.log(`\n⚠️ ${blastRadiusInfo.file} at the center of the system (blast radius: ${blastRadiusInfo.blast_radius} out of ${totalFiles}). This file represents a single point of systemic failure. Changes here may affect many parts of the system.`);
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
  }