pkg-scaffold 3.3.0 ā 3.3.2
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 +2 -2
- package/NOTICE +1 -1
- package/bin/cli.js +52 -59
- package/package.json +1 -1
- package/src/EngineContext.js +24 -20
- package/src/api/HeadlessAPI.js +11 -0
- package/src/ast/ASTAnalyzer.js +3 -2
- package/src/ast/OxcAnalyzer.js +17 -7
- package/src/index.js +119 -148
- package/src/performance/SecretDetector.js +378 -0
- package/src/resolution/CircularDetector.js +81 -30
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ============================================================================
|
|
3
|
-
* š¦ pkg-scaffold v3.
|
|
3
|
+
* š¦ pkg-scaffold v3.3.2: Unified Architectural Refactoring Orchestrator
|
|
4
4
|
* ============================================================================
|
|
5
5
|
* Main execution bridge managing multi-pass compilation cycles, semantic cross-linking,
|
|
6
6
|
* supply-chain validation audits, and automated git self-healing rollbacks.
|
|
@@ -19,6 +19,7 @@ import { MagicDetector } from './ast/MagicDetector.js';
|
|
|
19
19
|
import { PathMapper } from './resolution/PathMapper.js';
|
|
20
20
|
import { WorkspaceGraph } from './resolution/WorkSpaceGraph.js';
|
|
21
21
|
import { DependencyResolver } from './resolution/DepencyResolver.js';
|
|
22
|
+
import { CircularDetector } from './resolution/CircularDetector.js';
|
|
22
23
|
import { TransactionManager } from './refractor/TransactionManager.js';
|
|
23
24
|
import { ImpactAnalyzer } from './refractor/ImpactAnalyzer.js';
|
|
24
25
|
import { SourceRewriter } from './refractor/SourceRewriter.js';
|
|
@@ -28,6 +29,7 @@ import { SelfHealer } from './healing/SelfHealer.js';
|
|
|
28
29
|
import { IncrementalCacheManager } from './performance/GraphCache.js';
|
|
29
30
|
import { WorkerPool } from './performance/WorkerPool.js';
|
|
30
31
|
import { SupplyChainGuard } from './performance/SupplyChainGuard.js';
|
|
32
|
+
import { SecretDetector } from './performance/SecretDetector.js';
|
|
31
33
|
|
|
32
34
|
/**
|
|
33
35
|
* Primary Refactoring Engine Core Coordination Controller
|
|
@@ -41,6 +43,7 @@ export class RefactoringEngine {
|
|
|
41
43
|
this.pathMapper = new PathMapper(this.context);
|
|
42
44
|
this.workspaceGraph = new WorkspaceGraph(this.context);
|
|
43
45
|
this.resolver = new DependencyResolver(this.context, this.pathMapper, this.workspaceGraph);
|
|
46
|
+
this.circularDetector = new CircularDetector(this.context);
|
|
44
47
|
|
|
45
48
|
// Stage 3: Wire official AST Syntax parsers and framework processors
|
|
46
49
|
this.analyzer = new ASTAnalyzer(this.context);
|
|
@@ -55,6 +58,7 @@ export class RefactoringEngine {
|
|
|
55
58
|
|
|
56
59
|
// Stage 5: Bind security audit utilities and performance cache rings
|
|
57
60
|
this.supplyChainGuard = new SupplyChainGuard(this.context);
|
|
61
|
+
this.secretDetector = new SecretDetector(this.context);
|
|
58
62
|
this.cacheManager = new IncrementalCacheManager(this.context);
|
|
59
63
|
this.workerPool = new WorkerPool(this.context);
|
|
60
64
|
this.gitSandbox = new GitSandbox(this.context);
|
|
@@ -68,10 +72,13 @@ export class RefactoringEngine {
|
|
|
68
72
|
try {
|
|
69
73
|
console.log(ansis.bold.green('šÆ Starting pkg-scaffold Operational Optimization Cycle...'));
|
|
70
74
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
let rl;
|
|
76
|
+
if (!this.context.skipConfirm) {
|
|
77
|
+
rl = readline.createInterface({
|
|
78
|
+
input: process.stdin,
|
|
79
|
+
output: process.stdout
|
|
80
|
+
});
|
|
81
|
+
}
|
|
75
82
|
|
|
76
83
|
// Pass 1: Boot environment contexts and load alias configuration maps
|
|
77
84
|
await this.context.initialize();
|
|
@@ -103,37 +110,39 @@ export class RefactoringEngine {
|
|
|
103
110
|
}
|
|
104
111
|
}
|
|
105
112
|
|
|
113
|
+
// Initialize TypeScript program for AST analysis (required before processFile)
|
|
114
|
+
if (sourceCodeFilesList.length > 0) {
|
|
115
|
+
try {
|
|
116
|
+
this.analyzer.initProgram(sourceCodeFilesList);
|
|
117
|
+
} catch (e) {
|
|
118
|
+
if (this.context.verbose) {
|
|
119
|
+
console.warn('Warning: Failed to initialize TypeScript program:', e.message);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
106
124
|
// Pass 3: Process source file tokens using high-performance concurrent workers
|
|
107
125
|
let parallelParseCompleted = false;
|
|
108
126
|
if (sourceCodeFilesList.length > 10) {
|
|
109
127
|
parallelParseCompleted = await this.workerPool.parallelAnalyzeCodebase(sourceCodeFilesList, this);
|
|
110
128
|
}
|
|
111
129
|
|
|
112
|
-
// Synchronous fallback loop to parse remaining files or cache misses.
|
|
113
|
-
// IMPORTANT: The `parallelParseCompleted` flag only controls whether the *worker pool*
|
|
114
|
-
// already processed all files. It must NOT be set per-file inside this loop ā doing so
|
|
115
|
-
// would cause all files after the first cache-hit to be silently skipped.
|
|
116
130
|
for (const filePath of sourceCodeFilesList) {
|
|
117
131
|
const node = this.context.createNode(filePath);
|
|
118
132
|
const currentHash = await this.cacheManager.computeHash(filePath);
|
|
119
133
|
node.contentHash = currentHash;
|
|
120
134
|
|
|
121
|
-
// Check if this individual file is already up-to-date in the cache
|
|
122
135
|
const isFileCached = cacheManifest[filePath] && cacheManifest[filePath].hash === currentHash;
|
|
123
136
|
|
|
124
137
|
if (isFileCached) {
|
|
125
138
|
this.context.metrics.cacheHits++;
|
|
126
139
|
this.hydrateNodeFromCache(node, cacheManifest[filePath]);
|
|
127
140
|
} else if (!parallelParseCompleted) {
|
|
128
|
-
// Only parse if the worker pool did not already handle this file
|
|
129
141
|
this.context.metrics.cacheMisses++;
|
|
130
142
|
await this.analyzer.processFile(filePath, node);
|
|
131
143
|
}
|
|
132
144
|
|
|
133
|
-
// Apply ecosystem overrides directly to our parsed memory maps
|
|
134
145
|
this.magicDetector.injectVirtualConsumerEdges(filePath, node, activeFrameworkEcosystems);
|
|
135
|
-
|
|
136
|
-
// Aggregate used external packages for the dependency audit
|
|
137
146
|
node.externalPackageUsage.forEach(pkg => this.context.usedExternalPackages.add(pkg));
|
|
138
147
|
}
|
|
139
148
|
|
|
@@ -141,15 +150,31 @@ export class RefactoringEngine {
|
|
|
141
150
|
console.log(ansis.dim('š Linking graph edges and checking structural usage paths...'));
|
|
142
151
|
await this.linkDependencyGraph();
|
|
143
152
|
|
|
153
|
+
// NEW: Circular Dependency Detection
|
|
154
|
+
console.log(ansis.dim('š Detecting circular dependencies...'));
|
|
155
|
+
const cycles = this.circularDetector.detectCycles(this.context.graph, this.context);
|
|
156
|
+
if (cycles.length > 0) {
|
|
157
|
+
console.warn(ansis.bold.yellow(`\nā ļø Detected ${cycles.length} circular dependencies:`));
|
|
158
|
+
this.circularDetector.formatCycles().forEach(c => console.log(ansis.dim(` ⢠${c}`)));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// NEW: Secret Detection
|
|
162
|
+
console.log(ansis.dim('š Scanning for hardcoded secrets...'));
|
|
163
|
+
const detectedSecrets = await this.secretDetector.scanCodebaseForSecrets(this.context);
|
|
164
|
+
const secretStats = this.secretDetector.getSecretStats();
|
|
165
|
+
|
|
144
166
|
// Pass 5: Compile metrics summary and print diagnostics report
|
|
145
167
|
const analysisSummary = this.context.generateSummaryReport();
|
|
168
|
+
analysisSummary.detectedSecrets = detectedSecrets;
|
|
169
|
+
analysisSummary.secretStats = secretStats;
|
|
146
170
|
this.displayConsoleDiagnostics(analysisSummary);
|
|
147
171
|
|
|
148
172
|
// Pass 6: Run self-healing automated transformations if --fix is set
|
|
149
173
|
if (this.context.allowAutoFix) {
|
|
150
174
|
const structuralModificationsStaged =
|
|
151
175
|
analysisSummary.structuralIssuesDetected.deadFiles.length > 0 ||
|
|
152
|
-
analysisSummary.structuralIssuesDetected.deadExports.length > 0
|
|
176
|
+
analysisSummary.structuralIssuesDetected.deadExports.length > 0 ||
|
|
177
|
+
analysisSummary.structuralIssuesDetected.unusedDependencies.length > 0;
|
|
153
178
|
|
|
154
179
|
if (structuralModificationsStaged) {
|
|
155
180
|
console.log(ansis.bold.yellow('\nš Proposed Optimization Plan:'));
|
|
@@ -167,7 +192,7 @@ export class RefactoringEngine {
|
|
|
167
192
|
|
|
168
193
|
if (analysisSummary.structuralIssuesDetected.unusedDependencies.length > 0) {
|
|
169
194
|
console.log(ansis.bold(` š¦ Remove ${analysisSummary.structuralIssuesDetected.unusedDependencies.length} unused dependencies:`));
|
|
170
|
-
analysisSummary.structuralIssuesDetected.unusedDependencies.forEach(d => console.log(ansis.dim(` ⢠${d.package} (in ${d.manifest})`)));
|
|
195
|
+
analysisSummary.structuralIssuesDetected.unusedDependencies.forEach(d => console.log(ansis.dim(` ⢠${d.package} (${d.type} in ${d.manifest})`)));
|
|
171
196
|
}
|
|
172
197
|
console.log(ansis.dim('------------------------------------------------------------'));
|
|
173
198
|
|
|
@@ -179,31 +204,23 @@ export class RefactoringEngine {
|
|
|
179
204
|
|
|
180
205
|
if (proceed) {
|
|
181
206
|
await this.selfHealer.runSelfHealingLifecycle(async () => {
|
|
182
|
-
// Sub-Task A: Purge completely unreferenced dangling components
|
|
183
207
|
for (const relPath of analysisSummary.structuralIssuesDetected.deadFiles) {
|
|
184
208
|
const absPath = path.resolve(this.context.cwd, relPath);
|
|
185
209
|
console.log(ansis.red(`āļø Removing unreferenced file: ${relPath}`));
|
|
186
210
|
await this.txManager.stageDeletion(absPath);
|
|
187
211
|
}
|
|
188
212
|
|
|
189
|
-
// Sub-Task B: Surgically remove unused named export blocks from active files
|
|
190
213
|
for (const unusedExport of analysisSummary.structuralIssuesDetected.deadExports) {
|
|
191
214
|
const absPath = path.resolve(this.context.cwd, unusedExport.file);
|
|
192
215
|
const node = this.context.graph.get(absPath);
|
|
193
|
-
|
|
194
216
|
if (!node) continue;
|
|
195
217
|
const meta = node.internalExports.get(unusedExport.symbol);
|
|
196
218
|
|
|
197
|
-
// Perform safety analysis to ensure the token isn't called via dynamic runtime methods
|
|
198
219
|
const safetyVerdict = await this.impactAnalyzer.verifyRefactorSafety(absPath, unusedExport.symbol, this.context.graph);
|
|
199
|
-
|
|
200
220
|
if (safetyVerdict.isSafeToPrune) {
|
|
201
221
|
console.log(ansis.yellow(`ā” Stripping unused export [${unusedExport.symbol}] from: ${unusedExport.file}:${unusedExport.line}`));
|
|
202
222
|
const nextText = await this.sourceRewriter.stripNamedExportSignature(absPath, unusedExport.symbol, meta);
|
|
203
|
-
|
|
204
223
|
await this.txManager.stageWrite(absPath, nextText);
|
|
205
|
-
|
|
206
|
-
// Align matching type declaration boundaries (.d.ts) to prevent compilation errors
|
|
207
224
|
await this.typeIntegrity.synchronizeDeclarationFile(absPath, unusedExport.symbol);
|
|
208
225
|
} else if (this.context.verbose) {
|
|
209
226
|
console.log(ansis.gray(`š”ļø Preserving symbol export [${unusedExport.symbol}] due to: ${safetyVerdict.blockReason}`));
|
|
@@ -216,9 +233,8 @@ export class RefactoringEngine {
|
|
|
216
233
|
}
|
|
217
234
|
}
|
|
218
235
|
|
|
219
|
-
// Pass 7: Save optimized graph footprints back to the cache directory
|
|
220
236
|
await this.cacheManager.saveCacheManifest(this.context.graph);
|
|
221
|
-
rl.close();
|
|
237
|
+
if (rl) rl.close();
|
|
222
238
|
console.log(ansis.bold.green('\n⨠Core optimization cycle completed smoothly. Codebase workspace is healthy.'));
|
|
223
239
|
|
|
224
240
|
} catch (criticalFault) {
|
|
@@ -228,13 +244,24 @@ export class RefactoringEngine {
|
|
|
228
244
|
}
|
|
229
245
|
}
|
|
230
246
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
247
|
+
async discoverSourceFiles(dir, fileList) {
|
|
248
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
249
|
+
for (const entry of entries) {
|
|
250
|
+
const res = path.resolve(dir, entry.name);
|
|
251
|
+
if (entry.isDirectory()) {
|
|
252
|
+
if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === '.scaffold-cache') continue;
|
|
253
|
+
await this.discoverSourceFiles(res, fileList);
|
|
254
|
+
} else {
|
|
255
|
+
const ext = path.extname(entry.name);
|
|
256
|
+
if (['.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte'].includes(ext) || entry.name === 'package.json') {
|
|
257
|
+
fileList.push(res);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
234
263
|
async linkDependencyGraph() {
|
|
235
264
|
for (const [filePath, node] of this.context.graph.entries()) {
|
|
236
|
-
|
|
237
|
-
// Connect standard module imports
|
|
238
265
|
for (const specifier of node.explicitImports) {
|
|
239
266
|
const resolvedPath = this.resolver.resolveModulePath(filePath, specifier);
|
|
240
267
|
if (resolvedPath && this.context.graph.has(resolvedPath)) {
|
|
@@ -243,23 +270,14 @@ export class RefactoringEngine {
|
|
|
243
270
|
}
|
|
244
271
|
}
|
|
245
272
|
|
|
246
|
-
// Unroll re-exports and unwrap barrel file links recursively
|
|
247
273
|
for (const specToken of node.importedSymbols) {
|
|
248
274
|
const delimiterIndex = specToken.indexOf(':');
|
|
249
275
|
if (delimiterIndex === -1) continue;
|
|
250
|
-
|
|
251
276
|
const specifier = specToken.slice(0, delimiterIndex);
|
|
252
277
|
const symbol = specToken.slice(delimiterIndex + 1);
|
|
253
|
-
|
|
254
278
|
const resolvedPath = this.resolver.resolveModulePath(filePath, specifier);
|
|
255
|
-
|
|
256
279
|
if (resolvedPath && symbol !== '*') {
|
|
257
|
-
const traceResolution = await this.barrelParser.determineSymbolDeclarationOrigin(
|
|
258
|
-
resolvedPath,
|
|
259
|
-
symbol,
|
|
260
|
-
this.context.graph
|
|
261
|
-
);
|
|
262
|
-
|
|
280
|
+
const traceResolution = await this.barrelParser.determineSymbolDeclarationOrigin(resolvedPath, symbol, this.context.graph);
|
|
263
281
|
if (traceResolution && this.context.graph.has(traceResolution.originFile)) {
|
|
264
282
|
this.context.graph.get(traceResolution.originFile).incomingEdges.add(filePath);
|
|
265
283
|
node.outgoingEdges.add(traceResolution.originFile);
|
|
@@ -269,142 +287,95 @@ export class RefactoringEngine {
|
|
|
269
287
|
}
|
|
270
288
|
}
|
|
271
289
|
|
|
272
|
-
/**
|
|
273
|
-
* Audits package json files using token equality checks instead of fragile regex searches.
|
|
274
|
-
*/
|
|
275
290
|
async auditManifestSupplyChain(packageJsonPath) {
|
|
276
291
|
try {
|
|
277
292
|
const text = await fs.readFile(packageJsonPath, 'utf8');
|
|
278
293
|
const data = JSON.parse(text);
|
|
279
|
-
|
|
280
294
|
const prodDeps = Object.keys(data.dependencies || {});
|
|
281
295
|
const devDeps = Object.keys(data.devDependencies || {});
|
|
282
296
|
const totalDependencies = [...prodDeps, ...devDeps];
|
|
283
297
|
|
|
284
|
-
// Register dependencies for later audit
|
|
285
298
|
this.context.manifestDependencies.set(packageJsonPath, {
|
|
286
299
|
dependencies: prodDeps,
|
|
287
|
-
devDependencies: devDeps
|
|
300
|
+
devDependencies: devDeps,
|
|
301
|
+
peerDependencies: Object.keys(data.peerDependencies || {}),
|
|
302
|
+
optionalDependencies: Object.keys(data.optionalDependencies || {})
|
|
288
303
|
});
|
|
289
304
|
|
|
290
|
-
// Pass dependencies down to our Levenshtein string-distance guard to find typosquat targets
|
|
291
305
|
const supplyChainThreats = this.supplyChainGuard.detectTyposquattingAnomalies(totalDependencies);
|
|
292
|
-
|
|
293
306
|
for (const anomaly of supplyChainThreats) {
|
|
294
307
|
console.warn(ansis.bold.red(`šØ Supply Chain Alert: Malicious package masking candidate discovered [${anomaly.maliciousCandidate}]. Mimics trusted library [${anomaly.targetMimicked}].`));
|
|
295
308
|
}
|
|
296
|
-
|
|
297
|
-
// Verify lockfile hash configurations to catch poisoned manifests
|
|
298
309
|
await this.supplyChainGuard.verifyIntegrityLockfileHashes(packageJsonPath);
|
|
299
|
-
} catch {
|
|
300
|
-
// Manifest unreadable or locked; skip gracefully
|
|
301
|
-
}
|
|
310
|
+
} catch {}
|
|
302
311
|
}
|
|
303
312
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (cachedRecord.internalExports) {
|
|
313
|
-
Object.entries(cachedRecord.internalExports).forEach(([k, v]) => {
|
|
314
|
-
node.internalExports.set(k, v);
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
node.isLibraryEntry = cachedRecord.isLibraryEntry || false;
|
|
318
|
-
node.securityThreats = cachedRecord.securityThreats || [];
|
|
319
|
-
if (cachedRecord.localSuppressedRules) {
|
|
320
|
-
cachedRecord.localSuppressedRules.forEach(r => node.localSuppressedRules.add(r));
|
|
321
|
-
}
|
|
322
|
-
if (cachedRecord.symbolSourceLocations) {
|
|
323
|
-
Object.entries(cachedRecord.symbolSourceLocations).forEach(([k, v]) => {
|
|
324
|
-
node.symbolSourceLocations.set(k, v);
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
if (cachedRecord.externalPackageUsage) {
|
|
328
|
-
cachedRecord.externalPackageUsage.forEach(p => node.externalPackageUsage.add(p));
|
|
329
|
-
}
|
|
330
|
-
}
|
|
313
|
+
displayConsoleDiagnostics(summary) {
|
|
314
|
+
console.log(ansis.bold.cyan('\nš Operational Diagnostics Summary:'));
|
|
315
|
+
console.log(ansis.dim('------------------------------------------------------------'));
|
|
316
|
+
console.log(`ā±ļø Duration : ${summary.executionDuration}`);
|
|
317
|
+
console.log(`š Files Processed : ${summary.totalFilesProcessed}`);
|
|
318
|
+
console.log(`š§ Cache Hit Ratio : ${summary.graphCacheOptimization.ratio}`);
|
|
319
|
+
console.log(ansis.dim('------------------------------------------------------------'));
|
|
331
320
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
async discoverSourceFiles(currentDirectory, fileAccumulator) {
|
|
336
|
-
try {
|
|
337
|
-
const entries = await fs.readdir(currentDirectory, { withFileTypes: true });
|
|
338
|
-
|
|
339
|
-
for (const entry of entries) {
|
|
340
|
-
const absolutePath = path.join(currentDirectory, entry.name);
|
|
341
|
-
|
|
342
|
-
if (entry.isDirectory()) {
|
|
343
|
-
// Standard systemic exclusions filters
|
|
344
|
-
if (entry.name === 'node_modules' ||
|
|
345
|
-
entry.name === '.git' ||
|
|
346
|
-
entry.name === '.scaffold-cache' ||
|
|
347
|
-
entry.name === 'dist' ||
|
|
348
|
-
entry.name === 'build') {
|
|
349
|
-
continue;
|
|
350
|
-
}
|
|
351
|
-
await this.discoverSourceFiles(absolutePath, fileAccumulator);
|
|
352
|
-
} else if (entry.isFile()) {
|
|
353
|
-
const extension = path.extname(entry.name);
|
|
354
|
-
if (extension === '.js' ||
|
|
355
|
-
extension === '.ts' ||
|
|
356
|
-
extension === '.tsx' ||
|
|
357
|
-
extension === '.jsx' ||
|
|
358
|
-
entry.name === 'package.json') {
|
|
359
|
-
fileAccumulator.push(absolutePath);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
} catch {
|
|
364
|
-
// Path unreadable or access locked; close loop gracefully
|
|
321
|
+
if (summary.structuralIssuesDetected.deadFiles.length > 0) {
|
|
322
|
+
console.log(ansis.bold.red(`\nš Dead Files Detected (${summary.structuralIssuesDetected.deadFiles.length}):`));
|
|
323
|
+
summary.structuralIssuesDetected.deadFiles.forEach(f => console.log(ansis.dim(` ⢠${f}`)));
|
|
365
324
|
}
|
|
366
|
-
}
|
|
367
325
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
console.log(`${ansis.bold('Processing Cycle Wall Duration:')} ${ansis.cyan(report.executionDuration)}`);
|
|
372
|
-
console.log(`${ansis.bold('Total Files Indexed Globally :')} ${ansis.white(report.totalFilesProcessed)}`);
|
|
373
|
-
console.log(`${ansis.bold('Delta Cache Optimization Ratio:')} ${ansis.green(report.graphCacheOptimization.ratio)} (Hits: ${report.graphCacheOptimization.hits} / Misses: ${report.graphCacheOptimization.misses})`);
|
|
374
|
-
console.log(ansis.dim('------------------------------------------------------------'));
|
|
375
|
-
|
|
376
|
-
if (report.structuralIssuesDetected.deadFiles.length > 0) {
|
|
377
|
-
console.log(ansis.bold.red(`\nOrphaned Files Flagged (${report.structuralIssuesDetected.deadFiles.length}):`));
|
|
378
|
-
report.structuralIssuesDetected.deadFiles.forEach(f => console.log(ansis.dim(` ⢠${f}`)));
|
|
379
|
-
} else {
|
|
380
|
-
console.log(ansis.green('\n⨠No orphaned or unreferenced component files found.'));
|
|
326
|
+
if (summary.structuralIssuesDetected.deadExports.length > 0) {
|
|
327
|
+
console.log(ansis.bold.red(`\nāļø Dead Exports Detected (${summary.structuralIssuesDetected.deadExports.length}):`));
|
|
328
|
+
summary.structuralIssuesDetected.deadExports.forEach(e => console.log(ansis.dim(` ⢠${e.symbol} in ${e.file}:${e.line}`)));
|
|
381
329
|
}
|
|
382
330
|
|
|
383
|
-
if (
|
|
384
|
-
console.log(ansis.bold.
|
|
385
|
-
|
|
386
|
-
console.log(` ⢠${ansis.dim(e.file)}:${e.line}:${e.column} -> [${ansis.yellow(e.symbol)}] (Type: ${e.type})`);
|
|
387
|
-
});
|
|
388
|
-
} else {
|
|
389
|
-
console.log(ansis.green('⨠No dead or unused named symbols exported across components.'));
|
|
331
|
+
if (summary.structuralIssuesDetected.unusedDependencies.length > 0) {
|
|
332
|
+
console.log(ansis.bold.red(`\nš¦ Unused Dependencies (${summary.structuralIssuesDetected.unusedDependencies.length}):`));
|
|
333
|
+
summary.structuralIssuesDetected.unusedDependencies.forEach(d => console.log(ansis.dim(` ⢠${d.package} (${d.type} in ${d.manifest})`)));
|
|
390
334
|
}
|
|
391
335
|
|
|
392
|
-
if (
|
|
393
|
-
console.log(ansis.bold.red(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
});
|
|
336
|
+
if (summary.structuralIssuesDetected.securityThreats.length > 0) {
|
|
337
|
+
console.log(ansis.bold.red(`
|
|
338
|
+
š”ļø Security Threats (${summary.structuralIssuesDetected.securityThreats.length}):`));
|
|
339
|
+
summary.structuralIssuesDetected.securityThreats.forEach(t => console.log(ansis.dim(` ⢠${t.identifier} in ${t.file}:${t.line} (Risk: ${t.riskCode})`)));
|
|
397
340
|
}
|
|
398
341
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
342
|
+
// Display detected secrets
|
|
343
|
+
if (summary.detectedSecrets && summary.detectedSecrets.length > 0) {
|
|
344
|
+
const criticalSecrets = summary.detectedSecrets.filter(s => s.severity === 'CRITICAL');
|
|
345
|
+
const highSecrets = summary.detectedSecrets.filter(s => s.severity === 'HIGH');
|
|
346
|
+
|
|
347
|
+
console.log(ansis.bold.red(`
|
|
348
|
+
š Hardcoded Secrets Detected (${summary.detectedSecrets.length}):`));
|
|
349
|
+
|
|
350
|
+
if (criticalSecrets.length > 0) {
|
|
351
|
+
console.log(ansis.bold.red(` CRITICAL (${criticalSecrets.length}):`));
|
|
352
|
+
criticalSecrets.forEach(s => {
|
|
353
|
+
const relPath = s.file.split(/[\\/]/).slice(-2).join('/');
|
|
354
|
+
console.log(ansis.dim(` ⢠${s.type} in ${relPath}:${s.line} [${s.variable}]`));
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (highSecrets.length > 0) {
|
|
359
|
+
console.log(ansis.bold.yellow(` HIGH (${highSecrets.length}):`));
|
|
360
|
+
highSecrets.forEach(s => {
|
|
361
|
+
const relPath = s.file.split(/[\\/]/).slice(-2).join('/');
|
|
362
|
+
console.log(ansis.dim(` ⢠${s.type} in ${relPath}:${s.line} [${s.variable}]`));
|
|
363
|
+
});
|
|
364
|
+
}
|
|
406
365
|
}
|
|
407
366
|
|
|
408
|
-
console.log(ansis.dim('\n
|
|
367
|
+
console.log(ansis.dim('\n------------------------------------------------------------\n'));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
hydrateNodeFromCache(node, cachedRecord) {
|
|
371
|
+
if (cachedRecord.explicitImports) cachedRecord.explicitImports.forEach(i => node.explicitImports.add(i));
|
|
372
|
+
if (cachedRecord.dynamicImports) cachedRecord.dynamicImports.forEach(i => node.dynamicImports.add(i));
|
|
373
|
+
if (cachedRecord.importedSymbols) cachedRecord.importedSymbols.forEach(s => node.importedSymbols.add(s));
|
|
374
|
+
if (cachedRecord.internalExports) {
|
|
375
|
+
Object.entries(cachedRecord.internalExports).forEach(([k, v]) => node.internalExports.set(k, v));
|
|
376
|
+
}
|
|
377
|
+
if (cachedRecord.symbolSourceLocations) {
|
|
378
|
+
Object.entries(cachedRecord.symbolSourceLocations).forEach(([k, v]) => node.symbolSourceLocations.set(k, v));
|
|
379
|
+
}
|
|
409
380
|
}
|
|
410
381
|
}
|