pkg-scaffold 3.3.4 ā 3.3.6
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/README.md +7 -5
- package/bin/cli.js +4 -4
- package/package.json +10 -4
- package/src/EngineContext.js +52 -27
- package/src/ast/ASTAnalyzer.js +111 -77
- package/src/ast/BarrelParser.js +24 -4
- package/src/ast/MagicDetector.js +106 -13
- package/src/ast/OxcAnalyzer.js +121 -20
- package/src/ast/SecretScanner.js +304 -0
- package/src/healing/GitSandbox.js +44 -122
- package/src/healing/SelfHealer.js +29 -130
- package/src/index.js +175 -97
- package/src/performance/WorkerPool.js +6 -3
- package/src/plugins/PluginRegistry.js +27 -1
- package/src/resolution/DependencyProfiler.js +261 -9
- package/src/resolution/WorkSpaceGraph.js +142 -34
- package/src/performance/SecretDetector.js +0 -378
- package/src/performance/WorkerTaskRunner.js +0 -71
package/src/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { DeadCodeDetector } from "./ast/DeadCodeDetector.js";
|
|
2
2
|
import { OxcAnalyzer } from "./ast/OxcAnalyzer.js";
|
|
3
|
+
import { SecretScanner } from './ast/SecretScanner.js';
|
|
3
4
|
/**
|
|
4
5
|
* ============================================================================
|
|
5
|
-
* š¦ pkg-scaffold v3.3.
|
|
6
|
+
* š¦ pkg-scaffold v3.3.6: Unified Architectural Refactoring Orchestrator
|
|
6
7
|
* ============================================================================
|
|
7
8
|
* Main execution bridge managing multi-pass compilation cycles, semantic cross-linking,
|
|
8
|
-
* supply-chain validation audits, and automated
|
|
9
|
+
* supply-chain validation audits, and automated structural healing rollbacks.
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import fs from 'fs/promises';
|
|
@@ -31,7 +32,6 @@ import { SelfHealer } from './healing/SelfHealer.js';
|
|
|
31
32
|
import { IncrementalCacheManager } from './performance/GraphCache.js';
|
|
32
33
|
import { WorkerPool } from './performance/WorkerPool.js';
|
|
33
34
|
import { SupplyChainGuard } from './performance/SupplyChainGuard.js';
|
|
34
|
-
import { SecretDetector } from './performance/SecretDetector.js';
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Primary Refactoring Engine Core Coordination Controller
|
|
@@ -61,11 +61,12 @@ export class RefactoringEngine {
|
|
|
61
61
|
|
|
62
62
|
// Stage 5: Bind security audit utilities and performance cache rings
|
|
63
63
|
this.supplyChainGuard = new SupplyChainGuard(this.context);
|
|
64
|
-
this.secretDetector = new SecretDetector(this.context);
|
|
65
64
|
this.cacheManager = new IncrementalCacheManager(this.context);
|
|
66
65
|
this.workerPool = new WorkerPool(this.context);
|
|
67
66
|
this.gitSandbox = new GitSandbox(this.context);
|
|
68
67
|
this.selfHealer = new SelfHealer(this.context, this.txManager, this.gitSandbox);
|
|
68
|
+
// Stage 6: Secret / hardcoded credential scanner
|
|
69
|
+
this.secretScanner = new SecretScanner();
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
/**
|
|
@@ -87,9 +88,12 @@ export class RefactoringEngine {
|
|
|
87
88
|
await this.context.initialize();
|
|
88
89
|
await this.pathMapper.loadMappings(this.context.tsconfigFilename);
|
|
89
90
|
|
|
91
|
+
// Always attempt workspace mesh initialization ā it will auto-detect workspace
|
|
92
|
+
// configuration and flip `context.isWorkspaceEnabled` when found.
|
|
93
|
+
console.log(ansis.dim('š Probing for monorepo workspace configuration...'));
|
|
94
|
+
await this.workspaceGraph.initializeWorkspaceMesh();
|
|
90
95
|
if (this.context.isWorkspaceEnabled) {
|
|
91
|
-
console.log(ansis.dim('š
|
|
92
|
-
await this.workspaceGraph.initializeWorkspaceMesh();
|
|
96
|
+
console.log(ansis.dim('š Monorepo workspace detected ā mapping package mesh layers...'));
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
// Load asset fingerprints from disk cache to maximize cold-start performance
|
|
@@ -113,8 +117,6 @@ export class RefactoringEngine {
|
|
|
113
117
|
}
|
|
114
118
|
}
|
|
115
119
|
|
|
116
|
-
|
|
117
|
-
|
|
118
120
|
// Pass 3: Process source file tokens using high-performance concurrent workers
|
|
119
121
|
let parallelParseCompleted = false;
|
|
120
122
|
if (sourceCodeFilesList.length > 10) {
|
|
@@ -131,6 +133,15 @@ export class RefactoringEngine {
|
|
|
131
133
|
if (isFileCached) {
|
|
132
134
|
this.context.metrics.cacheHits++;
|
|
133
135
|
this.hydrateNodeFromCache(node, cacheManifest[filePath]);
|
|
136
|
+
// Re-run secret scan even on cached files (secrets may change without AST change)
|
|
137
|
+
try {
|
|
138
|
+
const cachedContent = await fs.readFile(filePath, 'utf8');
|
|
139
|
+
const secretFindings = this.secretScanner.scanFileContent(filePath, cachedContent);
|
|
140
|
+
if (secretFindings.length > 0) {
|
|
141
|
+
node.securityThreats = (node.securityThreats || []).concat(secretFindings);
|
|
142
|
+
secretFindings.forEach(f => this.context.allSecretFindings.push(f));
|
|
143
|
+
}
|
|
144
|
+
} catch { /* unreadable file ā skip */ }
|
|
134
145
|
} else if (!parallelParseCompleted) {
|
|
135
146
|
this.context.metrics.cacheMisses++;
|
|
136
147
|
const fileContent = await fs.readFile(filePath, 'utf8'); // Read file content here
|
|
@@ -139,12 +150,51 @@ export class RefactoringEngine {
|
|
|
139
150
|
} else {
|
|
140
151
|
this.analyzer.parseFile(filePath, fileContent, node);
|
|
141
152
|
}
|
|
153
|
+
// Secret scan on freshly parsed content
|
|
154
|
+
const secretFindings = this.secretScanner.scanFileContent(filePath, fileContent);
|
|
155
|
+
if (secretFindings.length > 0) {
|
|
156
|
+
node.securityThreats = (node.securityThreats || []).concat(secretFindings);
|
|
157
|
+
secretFindings.forEach(f => this.context.allSecretFindings.push(f));
|
|
158
|
+
}
|
|
142
159
|
}
|
|
143
160
|
|
|
144
161
|
this.magicDetector.injectVirtualConsumerEdges(filePath, node, activeFrameworkEcosystems);
|
|
145
162
|
node.externalPackageUsage.forEach(pkg => this.context.usedExternalPackages.add(pkg));
|
|
146
163
|
}
|
|
147
164
|
|
|
165
|
+
// Fix: Automatically mark active ecosystem packages as used.
|
|
166
|
+
// Maps internal plugin names to their canonical npm package names.
|
|
167
|
+
const pluginToPackageMap = {
|
|
168
|
+
'typescript': 'typescript',
|
|
169
|
+
'vitest': 'vitest',
|
|
170
|
+
'eslint': 'eslint',
|
|
171
|
+
'prettier': 'prettier',
|
|
172
|
+
'tailwindcss': 'tailwindcss',
|
|
173
|
+
'postcss': 'postcss',
|
|
174
|
+
'jest': 'jest',
|
|
175
|
+
'playwright': '@playwright/test',
|
|
176
|
+
'cypress': 'cypress',
|
|
177
|
+
'storybook': 'storybook',
|
|
178
|
+
'nextjs': 'next',
|
|
179
|
+
'nuxt': 'nuxt',
|
|
180
|
+
'remix': '@remix-run/dev',
|
|
181
|
+
'sveltekit': '@sveltejs/kit',
|
|
182
|
+
'astro': 'astro'
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
activeFrameworkEcosystems.forEach(ecosystem => {
|
|
186
|
+
if (ecosystem !== 'universal-tooling-vectors') {
|
|
187
|
+
const pkgName = pluginToPackageMap[ecosystem] || ecosystem;
|
|
188
|
+
this.context.usedExternalPackages.add(pkgName);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Ensure all workspace package names are pre-marked as used so they are
|
|
193
|
+
// never reported as unused dependencies in the manifest audit.
|
|
194
|
+
if (this.context.isWorkspaceEnabled) {
|
|
195
|
+
this.workspaceGraph.markWorkspacePackagesAsUsed();
|
|
196
|
+
}
|
|
197
|
+
|
|
148
198
|
// Pass 4: Evaluate graph edges and link connections across the codebase mesh
|
|
149
199
|
console.log(ansis.dim('š Linking graph edges and checking structural usage paths...'));
|
|
150
200
|
await this.linkDependencyGraph();
|
|
@@ -157,44 +207,62 @@ export class RefactoringEngine {
|
|
|
157
207
|
this.circularDetector.formatCycles().forEach(c => console.log(ansis.dim(` ⢠${c}`)));
|
|
158
208
|
}
|
|
159
209
|
|
|
160
|
-
//
|
|
161
|
-
console.log(ansis.dim('
|
|
162
|
-
const
|
|
163
|
-
|
|
210
|
+
// Pass 4b: Report hardcoded secrets
|
|
211
|
+
console.log(ansis.dim('š Scanning for hardcoded secrets...'));
|
|
212
|
+
const allSecrets = this.context.allSecretFindings || [];
|
|
213
|
+
if (allSecrets.length > 0) {
|
|
214
|
+
const criticalSecrets = allSecrets.filter(s => s.severity === 'CRITICAL');
|
|
215
|
+
const otherSecrets = allSecrets.filter(s => s.severity !== 'CRITICAL');
|
|
216
|
+
console.log(ansis.bold.red(`\nš Hardcoded Secrets Detected (${allSecrets.length}):`) );
|
|
217
|
+
if (criticalSecrets.length > 0) {
|
|
218
|
+
console.log(ansis.red(` CRITICAL (${criticalSecrets.length}):`));
|
|
219
|
+
criticalSecrets.forEach(s => {
|
|
220
|
+
const relPath = path.relative(this.context.cwd, s.file);
|
|
221
|
+
const varInfo = s.variableName ? ` [${s.label}]` : ` [${s.label}]`;
|
|
222
|
+
console.log(ansis.dim(` ⢠${s.variableName || '<literal>'} in ${relPath}:${s.line}${varInfo}`));
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
if (otherSecrets.length > 0) {
|
|
226
|
+
console.log(ansis.yellow(` HIGH/MEDIUM (${otherSecrets.length}):`));
|
|
227
|
+
otherSecrets.forEach(s => {
|
|
228
|
+
const relPath = path.relative(this.context.cwd, s.file);
|
|
229
|
+
console.log(ansis.dim(` ⢠${s.variableName || '<literal>'} in ${relPath}:${s.line} [${s.label}]`));
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
164
233
|
|
|
165
234
|
// Pass 5: Compile metrics summary and print diagnostics report
|
|
166
|
-
const analysisSummary = this.context.generateSummaryReport();
|
|
167
|
-
analysisSummary.
|
|
168
|
-
analysisSummary.secretStats = secretStats;
|
|
235
|
+
const analysisSummary = await this.context.generateSummaryReport();
|
|
236
|
+
analysisSummary.structuralIssuesDetected.hardcodedSecrets = allSecrets;
|
|
169
237
|
this.displayConsoleDiagnostics(analysisSummary);
|
|
170
238
|
|
|
171
|
-
// Pass 6:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
239
|
+
// Pass 6: Display Optimization Plan and Run Automated Structural Healing
|
|
240
|
+
const structuralModificationsStaged =
|
|
241
|
+
analysisSummary.structuralIssuesDetected.deadFiles.length > 0 ||
|
|
242
|
+
analysisSummary.structuralIssuesDetected.deadExports.length > 0 ||
|
|
243
|
+
analysisSummary.structuralIssuesDetected.unusedDependencies.length > 0;
|
|
244
|
+
|
|
245
|
+
if (structuralModificationsStaged) {
|
|
246
|
+
console.log(ansis.bold.yellow('\nš Proposed Optimization Plan:'));
|
|
247
|
+
console.log(ansis.dim('------------------------------------------------------------'));
|
|
248
|
+
|
|
249
|
+
if (analysisSummary.structuralIssuesDetected.deadFiles.length > 0) {
|
|
250
|
+
console.log(ansis.bold(` šļø Delete ${analysisSummary.structuralIssuesDetected.deadFiles.length} orphaned files:`));
|
|
251
|
+
analysisSummary.structuralIssuesDetected.deadFiles.forEach(f => console.log(ansis.dim(` ⢠${f}`)));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (analysisSummary.structuralIssuesDetected.deadExports.length > 0) {
|
|
255
|
+
console.log(ansis.bold(` āļø Prune ${analysisSummary.structuralIssuesDetected.deadExports.length} unused named exports:`));
|
|
256
|
+
analysisSummary.structuralIssuesDetected.deadExports.forEach(e => console.log(ansis.dim(` ⢠${e.symbol} in ${e.file}:${e.line}`)));
|
|
257
|
+
}
|
|
191
258
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
259
|
+
if (analysisSummary.structuralIssuesDetected.unusedDependencies.length > 0) {
|
|
260
|
+
console.log(ansis.bold(` š¦ Remove ${analysisSummary.structuralIssuesDetected.unusedDependencies.length} unused dependencies:`));
|
|
261
|
+
analysisSummary.structuralIssuesDetected.unusedDependencies.forEach(d => console.log(ansis.dim(` ⢠${d.package} (${d.type} in ${d.manifest})`)));
|
|
262
|
+
}
|
|
263
|
+
console.log(ansis.dim('------------------------------------------------------------'));
|
|
197
264
|
|
|
265
|
+
if (this.context.allowAutoFix) {
|
|
198
266
|
let proceed = this.context.skipConfirm;
|
|
199
267
|
if (!proceed) {
|
|
200
268
|
const answer = await rl.question(ansis.bold.cyan('\nā Apply these structural modifications? (y/N): '));
|
|
@@ -202,6 +270,7 @@ export class RefactoringEngine {
|
|
|
202
270
|
}
|
|
203
271
|
|
|
204
272
|
if (proceed) {
|
|
273
|
+
// Execute healing lifecycle (git-state-capture -> apply -> verify -> commit/rollback)
|
|
205
274
|
await this.selfHealer.runSelfHealingLifecycle(async () => {
|
|
206
275
|
for (const relPath of analysisSummary.structuralIssuesDetected.deadFiles) {
|
|
207
276
|
const absPath = path.resolve(this.context.cwd, relPath);
|
|
@@ -249,6 +318,7 @@ export class RefactoringEngine {
|
|
|
249
318
|
const res = path.resolve(dir, entry.name);
|
|
250
319
|
if (entry.isDirectory()) {
|
|
251
320
|
if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === '.scaffold-cache') continue;
|
|
321
|
+
if (this.context.verbose) console.log(ansis.dim(`š Scanning deep folder: ${res}`));
|
|
252
322
|
await this.discoverSourceFiles(res, fileList);
|
|
253
323
|
} else {
|
|
254
324
|
const ext = path.extname(entry.name);
|
|
@@ -261,25 +331,57 @@ export class RefactoringEngine {
|
|
|
261
331
|
|
|
262
332
|
async linkDependencyGraph() {
|
|
263
333
|
for (const [filePath, node] of this.context.graph.entries()) {
|
|
334
|
+
// Pass A: Link all explicit imports (static + dynamic + re-export sources)
|
|
264
335
|
for (const specifier of node.explicitImports) {
|
|
265
336
|
const resolvedPath = this.resolver.resolveModulePath(filePath, specifier);
|
|
266
337
|
if (resolvedPath && this.context.graph.has(resolvedPath)) {
|
|
267
338
|
this.context.graph.get(resolvedPath).incomingEdges.add(filePath);
|
|
268
339
|
node.outgoingEdges.add(resolvedPath);
|
|
340
|
+
|
|
341
|
+
// Fix: Ensure all internal exports from a re-exported source are marked as used
|
|
342
|
+
// so the source file itself is never considered orphaned.
|
|
343
|
+
const targetNode = this.context.graph.get(resolvedPath);
|
|
344
|
+
const isReExport = Array.from(node.internalExports.values()).some(exp => exp.source === specifier);
|
|
345
|
+
if (isReExport) {
|
|
346
|
+
targetNode.isLibraryEntry = true; // Protect re-exported internal files
|
|
347
|
+
}
|
|
269
348
|
}
|
|
270
349
|
}
|
|
271
350
|
|
|
351
|
+
// Pass A.2: Mark package entry points as library entries
|
|
352
|
+
for (const pkg of this.workspaceGraph.packageManifests.values()) {
|
|
353
|
+
for (const entryPath of pkg.entryPoints) {
|
|
354
|
+
if (this.context.graph.has(entryPath)) {
|
|
355
|
+
this.context.graph.get(entryPath).isLibraryEntry = true;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Pass B: Link named-symbol imports through barrel/re-export chains
|
|
272
361
|
for (const specToken of node.importedSymbols) {
|
|
273
362
|
const delimiterIndex = specToken.indexOf(':');
|
|
274
363
|
if (delimiterIndex === -1) continue;
|
|
275
364
|
const specifier = specToken.slice(0, delimiterIndex);
|
|
276
365
|
const symbol = specToken.slice(delimiterIndex + 1);
|
|
277
366
|
const resolvedPath = this.resolver.resolveModulePath(filePath, specifier);
|
|
278
|
-
|
|
367
|
+
|
|
368
|
+
if (!resolvedPath) continue;
|
|
369
|
+
|
|
370
|
+
if (symbol === '*') {
|
|
371
|
+
// Wildcard import / re-export-all: add a direct edge to the resolved file.
|
|
372
|
+
if (this.context.graph.has(resolvedPath)) {
|
|
373
|
+
this.context.graph.get(resolvedPath).incomingEdges.add(filePath);
|
|
374
|
+
node.outgoingEdges.add(resolvedPath);
|
|
375
|
+
}
|
|
376
|
+
} else {
|
|
377
|
+
// Named import: trace through barrel files to the actual declaration origin.
|
|
279
378
|
const traceResolution = await this.barrelParser.determineSymbolDeclarationOrigin(resolvedPath, symbol, this.context.graph);
|
|
280
379
|
if (traceResolution && this.context.graph.has(traceResolution.originFile)) {
|
|
281
380
|
this.context.graph.get(traceResolution.originFile).incomingEdges.add(filePath);
|
|
282
381
|
node.outgoingEdges.add(traceResolution.originFile);
|
|
382
|
+
// Fix: Store the absolute resolution in importedSymbols so isSymbolReferencedExternally can find it
|
|
383
|
+
// Use the traced symbol name (in case of re-exports with renaming)
|
|
384
|
+
node.importedSymbols.add(`${traceResolution.originFile}:${traceResolution.symbolName}`);
|
|
283
385
|
}
|
|
284
386
|
}
|
|
285
387
|
}
|
|
@@ -292,7 +394,6 @@ export class RefactoringEngine {
|
|
|
292
394
|
const data = JSON.parse(text);
|
|
293
395
|
const prodDeps = Object.keys(data.dependencies || {});
|
|
294
396
|
const devDeps = Object.keys(data.devDependencies || {});
|
|
295
|
-
const totalDependencies = [...prodDeps, ...devDeps];
|
|
296
397
|
|
|
297
398
|
this.context.manifestDependencies.set(packageJsonPath, {
|
|
298
399
|
dependencies: prodDeps,
|
|
@@ -300,66 +401,35 @@ export class RefactoringEngine {
|
|
|
300
401
|
peerDependencies: Object.keys(data.peerDependencies || {}),
|
|
301
402
|
optionalDependencies: Object.keys(data.optionalDependencies || {})
|
|
302
403
|
});
|
|
303
|
-
|
|
304
|
-
const supplyChainThreats = this.supplyChainGuard.detectTyposquattingAnomalies(totalDependencies);
|
|
305
|
-
for (const anomaly of supplyChainThreats) {
|
|
306
|
-
console.warn(ansis.bold.red(`šØ Supply Chain Alert: Malicious package masking candidate discovered [${anomaly.maliciousCandidate}]. Mimics trusted library [${anomaly.targetMimicked}].`));
|
|
307
|
-
}
|
|
308
|
-
await this.supplyChainGuard.verifyIntegrityLockfileHashes(packageJsonPath);
|
|
309
|
-
} catch {}
|
|
404
|
+
} catch (e) {}
|
|
310
405
|
}
|
|
311
406
|
|
|
312
407
|
displayConsoleDiagnostics(summary) {
|
|
313
|
-
console.log(ansis.bold.cyan('\nš
|
|
408
|
+
console.log(ansis.bold.cyan('\nš Codebase Optimization Summary Report'));
|
|
314
409
|
console.log(ansis.dim('------------------------------------------------------------'));
|
|
315
|
-
console.log(`ā±ļø Duration
|
|
316
|
-
console.log(
|
|
317
|
-
console.log(
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
summary.structuralIssuesDetected.
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (summary.structuralIssuesDetected.unusedDependencies.length > 0) {
|
|
331
|
-
console.log(ansis.bold.red(`\nš¦ Unused Dependencies (${summary.structuralIssuesDetected.unusedDependencies.length}):`));
|
|
332
|
-
summary.structuralIssuesDetected.unusedDependencies.forEach(d => console.log(ansis.dim(` ⢠${d.package} (${d.type} in ${d.manifest})`)));
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (summary.structuralIssuesDetected.securityThreats.length > 0) {
|
|
336
|
-
console.log(ansis.bold.red(`
|
|
337
|
-
š”ļø Security Threats (${summary.structuralIssuesDetected.securityThreats.length}):`));
|
|
338
|
-
summary.structuralIssuesDetected.securityThreats.forEach(t => console.log(ansis.dim(` ⢠${t.identifier} in ${t.file}:${t.line} (Risk: ${t.riskCode})`)));
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Display detected secrets
|
|
342
|
-
if (summary.detectedSecrets && summary.detectedSecrets.length > 0) {
|
|
343
|
-
const criticalSecrets = summary.detectedSecrets.filter(s => s.severity === 'CRITICAL');
|
|
344
|
-
const highSecrets = summary.detectedSecrets.filter(s => s.severity === 'HIGH');
|
|
345
|
-
|
|
346
|
-
console.log(ansis.bold.red(`
|
|
347
|
-
š Hardcoded Secrets Detected (${summary.detectedSecrets.length}):`));
|
|
348
|
-
|
|
349
|
-
if (criticalSecrets.length > 0) {
|
|
350
|
-
console.log(ansis.bold.red(` CRITICAL (${criticalSecrets.length}):`));
|
|
351
|
-
criticalSecrets.forEach(s => {
|
|
352
|
-
const relPath = s.file.split(/[\\/]/).slice(-2).join('/');
|
|
353
|
-
console.log(ansis.dim(` ⢠${s.type} in ${relPath}:${s.line} [${s.variable}]`));
|
|
354
|
-
});
|
|
410
|
+
console.log(`ā±ļø Analysis Duration: ${summary.executionDuration}`);
|
|
411
|
+
console.log(`š Total Files Scanned: ${summary.totalFilesProcessed}`);
|
|
412
|
+
console.log(`š¾ Cache Optimization: ${summary.graphCacheOptimization.ratio} hits`);
|
|
413
|
+
|
|
414
|
+
console.log(ansis.bold('\nš Structural Integrity:'));
|
|
415
|
+
const secretCount = (summary.structuralIssuesDetected.hardcodedSecrets || []).length;
|
|
416
|
+
if (summary.structuralIssuesDetected.deadFiles.length === 0 &&
|
|
417
|
+
summary.structuralIssuesDetected.deadExports.length === 0 &&
|
|
418
|
+
summary.structuralIssuesDetected.unusedDependencies.length === 0 &&
|
|
419
|
+
secretCount === 0) {
|
|
420
|
+
console.log(ansis.green(' ā
No major structural debt detected.'));
|
|
421
|
+
} else {
|
|
422
|
+
if (summary.structuralIssuesDetected.deadFiles.length > 0) {
|
|
423
|
+
console.log(ansis.red(` ā Found ${summary.structuralIssuesDetected.deadFiles.length} orphaned/dead files.`));
|
|
355
424
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
425
|
+
if (summary.structuralIssuesDetected.deadExports.length > 0) {
|
|
426
|
+
console.log(ansis.yellow(` ā ļø Found ${summary.structuralIssuesDetected.deadExports.length} unused named exports.`));
|
|
427
|
+
}
|
|
428
|
+
if (summary.structuralIssuesDetected.unusedDependencies.length > 0) {
|
|
429
|
+
console.log(ansis.yellow(` š¦ Found ${summary.structuralIssuesDetected.unusedDependencies.length} unused dependencies.`));
|
|
430
|
+
}
|
|
431
|
+
if (secretCount > 0) {
|
|
432
|
+
console.log(ansis.red(` š Found ${secretCount} hardcoded secret(s) / credential(s).`));
|
|
363
433
|
}
|
|
364
434
|
}
|
|
365
435
|
|
|
@@ -376,5 +446,13 @@ export class RefactoringEngine {
|
|
|
376
446
|
if (cachedRecord.symbolSourceLocations) {
|
|
377
447
|
Object.entries(cachedRecord.symbolSourceLocations).forEach(([k, v]) => node.symbolSourceLocations.set(k, v));
|
|
378
448
|
}
|
|
449
|
+
// Restore fields that were previously missing from cache hydration
|
|
450
|
+
if (cachedRecord.externalPackageUsage) cachedRecord.externalPackageUsage.forEach(p => node.externalPackageUsage.add(p));
|
|
451
|
+
if (cachedRecord.rawStringReferences) cachedRecord.rawStringReferences.forEach(r => node.rawStringReferences.add(r));
|
|
452
|
+
if (cachedRecord.instantiatedIdentifiers) cachedRecord.instantiatedIdentifiers.forEach(id => node.instantiatedIdentifiers.add(id));
|
|
453
|
+
if (cachedRecord.propertyAccessChains) cachedRecord.propertyAccessChains.forEach(c => node.propertyAccessChains.add(c));
|
|
454
|
+
if (cachedRecord.localSuppressedRules) cachedRecord.localSuppressedRules.forEach(r => node.localSuppressedRules.add(r));
|
|
455
|
+
if (cachedRecord.calculatedDynamicImports) node.calculatedDynamicImports = cachedRecord.calculatedDynamicImports;
|
|
456
|
+
if (cachedRecord.isLibraryEntry !== undefined) node.isLibraryEntry = cachedRecord.isLibraryEntry;
|
|
379
457
|
}
|
|
380
458
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Worker
|
|
1
|
+
import { Worker } from 'worker_threads';
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Host CPU Thread-Distribution Pipeline Supervisor
|
|
@@ -11,7 +12,9 @@ export class WorkerPool {
|
|
|
11
12
|
this.context = context;
|
|
12
13
|
// Dynamically query host specs; default down to 1 if threading channels are choked
|
|
13
14
|
this.hardwareConcurrencyCoreCount = maximumConcurrencyLimit || os.availableParallelism?.() || os.cpus().length || 2;
|
|
14
|
-
|
|
15
|
+
// Resolve worker script path relative to this module
|
|
16
|
+
const __dir = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
this.workerScriptPath = path.resolve(__dir, 'WorkerTaskRunner.js');
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
/**
|
|
@@ -86,7 +89,7 @@ export class WorkerPool {
|
|
|
86
89
|
|
|
87
90
|
executeChunkInsideThread(fileChunkSubset) {
|
|
88
91
|
return new Promise((resolve, reject) => {
|
|
89
|
-
const workerInstance = new Worker(this.workerScriptPath, {
|
|
92
|
+
const workerInstance = new Worker(this.workerScriptPath, { type: 'module',
|
|
90
93
|
workerData: { files: fileChunkSubset, contextOptions: { verbose: this.context.verbose } }
|
|
91
94
|
});
|
|
92
95
|
|
|
@@ -54,6 +54,15 @@ export class PluginRegistry {
|
|
|
54
54
|
// Backend Services (New in v4.0)
|
|
55
55
|
const { GraphQLPlugin, DatabasePlugin } = await import('./ecosystems/BackendServices.js');
|
|
56
56
|
|
|
57
|
+
// Extended tooling plugins (New in v4.1)
|
|
58
|
+
const {
|
|
59
|
+
TailwindPlugin, PostcssPlugin, JestPlugin, VitestPlugin,
|
|
60
|
+
PlaywrightPlugin, CypressPlugin, StorybookPlugin,
|
|
61
|
+
EslintPlugin, PrettierPlugin, HuskyPlugin, LintStagedPlugin,
|
|
62
|
+
CommitlintPlugin, BabelPlugin, RollupPlugin, WebpackPlugin,
|
|
63
|
+
GithubActionsPlugin
|
|
64
|
+
} = await import('./ecosystems/MorePlugins.js');
|
|
65
|
+
|
|
57
66
|
const builtins = [
|
|
58
67
|
new NextJsPlugin(this.context),
|
|
59
68
|
new NuxtPlugin(this.context),
|
|
@@ -66,7 +75,24 @@ export class PluginRegistry {
|
|
|
66
75
|
new SveltePlugin(this.context),
|
|
67
76
|
new AngularPlugin(this.context),
|
|
68
77
|
new GraphQLPlugin(this.context),
|
|
69
|
-
new DatabasePlugin(this.context)
|
|
78
|
+
new DatabasePlugin(this.context),
|
|
79
|
+
// Extended tooling plugins
|
|
80
|
+
new TailwindPlugin(this.context),
|
|
81
|
+
new PostcssPlugin(this.context),
|
|
82
|
+
new JestPlugin(this.context),
|
|
83
|
+
new VitestPlugin(this.context),
|
|
84
|
+
new PlaywrightPlugin(this.context),
|
|
85
|
+
new CypressPlugin(this.context),
|
|
86
|
+
new StorybookPlugin(this.context),
|
|
87
|
+
new EslintPlugin(this.context),
|
|
88
|
+
new PrettierPlugin(this.context),
|
|
89
|
+
new HuskyPlugin(this.context),
|
|
90
|
+
new LintStagedPlugin(this.context),
|
|
91
|
+
new CommitlintPlugin(this.context),
|
|
92
|
+
new BabelPlugin(this.context),
|
|
93
|
+
new RollupPlugin(this.context),
|
|
94
|
+
new WebpackPlugin(this.context),
|
|
95
|
+
new GithubActionsPlugin(this.context)
|
|
70
96
|
];
|
|
71
97
|
|
|
72
98
|
builtins.forEach(p => {
|