pkg-scaffold 2.3.0 → 3.0.0

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.
@@ -0,0 +1,160 @@
1
+ import { execSync } from 'child_process';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * Ephemeral Git Sandbox & Version Control Islolation Gateway
6
+ * Manages atomic branch switching, working-tree stashing, and systemic rollbacks.
7
+ */
8
+ export class GitSandbox {
9
+ constructor(context) {
10
+ this.context = context;
11
+ this.originalBranch = null;
12
+ this.sandboxBranch = null;
13
+ this.hasStashedChanges = false;
14
+ this.isGitRepository = false;
15
+ }
16
+
17
+ /**
18
+ * Validates the active environment and isolates the working directory.
19
+ */
20
+ async establishIsolationCheckpoint() {
21
+ this.verifyGitRepositoryPresence();
22
+ if (!this.isGitRepository) return;
23
+
24
+ try {
25
+ // Capture the current branch name
26
+ this.originalBranch = execSync('git rev-parse --abbrev-ref HEAD', {
27
+ cwd: this.context.cwd,
28
+ encoding: 'utf8'
29
+ }).trim();
30
+
31
+ // Check for uncommitted working directory changes
32
+ const statusOutput = execSync('git status --porcelain', {
33
+ cwd: this.context.cwd,
34
+ encoding: 'utf8'
35
+ }).trim();
36
+
37
+ if (statusOutput.length > 0) {
38
+ if (this.context.verbose) {
39
+ console.log('📦 Working tree contains uncommitted changes. Stashing before running pipeline...');
40
+ }
41
+ execSync('git stash save "pkg-scaffold: Ephemeral Checkpoint Stash"', {
42
+ cwd: this.context.cwd,
43
+ stdio: 'ignore'
44
+ });
45
+ this.hasStashedChanges = true;
46
+ }
47
+
48
+ // Generate an isolated tracking branch name
49
+ this.sandboxBranch = `scaffold-heal-${Date.now()}`;
50
+ execSync(`git checkout -b ${this.sandboxBranch}`, {
51
+ cwd: this.context.cwd,
52
+ stdio: 'ignore'
53
+ });
54
+
55
+ if (this.context.verbose) {
56
+ console.log(`🌿 Isolated sandbox branch successfully initialized: [${this.sandboxBranch}]`);
57
+ }
58
+ } catch (error) {
59
+ throw new Error(`Git Sandbox separation failure: ${error.message}`);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Commits all modifications within the sandbox branch for validation testing.
65
+ */
66
+ async stageAndCheckpointChanges(commitMessage = 'refactor(scaffold): apply structural optimizations') {
67
+ if (!this.isGitRepository) return;
68
+ this.assertActiveIsolation();
69
+
70
+ try {
71
+ execSync('git add .', { cwd: this.context.cwd, stdio: 'ignore' });
72
+
73
+ // Check if any mutations were actually staged
74
+ const diffIndex = execSync('git diff --cached --name-only', {
75
+ cwd: this.context.cwd,
76
+ encoding: 'utf8'
77
+ }).trim();
78
+
79
+ if (diffIndex.length === 0) return; // No modifications to verify
80
+
81
+ execSync(`git commit -m "${commitMessage}" --no-verify`, {
82
+ cwd: this.context.cwd,
83
+ stdio: 'ignore'
84
+ });
85
+ } catch (error) {
86
+ throw new Error(`Failed to stage changes inside the sandbox: ${error.message}`);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Merges modifications back into the mainline branch if all validation test suites pass.
92
+ */
93
+ async acceptAndMergeOptimizations() {
94
+ if (!this.isGitRepository) return;
95
+ this.assertActiveIsolation();
96
+
97
+ try {
98
+ // Return to the original project branch boundary
99
+ execSync(`git checkout ${this.originalBranch}`, { cwd: this.context.cwd, stdio: 'ignore' });
100
+
101
+ // Merge the verified changes without creating a fast-forward bottleneck
102
+ execSync(`git merge --squash ${this.sandboxBranch}`, { cwd: this.context.cwd, stdio: 'ignore' });
103
+ execSync('git commit -m "chore(refactor): apply verified dead-code removals" --no-verify', {
104
+ cwd: this.context.cwd,
105
+ stdio: 'ignore'
106
+ });
107
+
108
+ // Purge the temporary branch tracking reference
109
+ execSync(`git branch -D ${this.sandboxBranch}`, { cwd: this.context.cwd, stdio: 'ignore' });
110
+ this.restorePreviousWorkingState();
111
+ } catch (error) {
112
+ throw new Error(`Failed to merge sandbox modifications into mainline branch: ${error.message}`);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Reverts all disk changes instantly if testing suites flag an error.
118
+ */
119
+ async rejectAndAbortOptimizations() {
120
+ if (!this.isGitRepository) return;
121
+ if (!this.sandboxBranch) return;
122
+
123
+ try {
124
+ if (this.context.verbose) {
125
+ console.log('🔄 Aborting transaction loop. Resetting file systems...');
126
+ }
127
+
128
+ execSync('git add . && git reset --hard HEAD', { cwd: this.context.cwd, stdio: 'ignore' });
129
+ execSync(`git checkout ${this.originalBranch}`, { cwd: this.context.cwd, stdio: 'ignore' });
130
+ execSync(`git branch -D ${this.sandboxBranch}`, { cwd: this.context.cwd, stdio: 'ignore' });
131
+
132
+ this.restorePreviousWorkingState();
133
+ } catch (error) {
134
+ console.error(`🚨 Critical Recovery Error: Failed to drop sandbox branch cleanly: ${error.message}`);
135
+ }
136
+ }
137
+
138
+ restorePreviousWorkingState() {
139
+ if (this.hasStashedChanges) {
140
+ execSync('git stash pop', { cwd: this.context.cwd, stdio: 'ignore' });
141
+ this.hasStashedChanges = false;
142
+ }
143
+ this.sandboxBranch = null;
144
+ }
145
+
146
+ verifyGitRepositoryPresence() {
147
+ try {
148
+ execSync('git rev-parse --is-inside-work-tree', { cwd: this.context.cwd, stdio: 'ignore' });
149
+ this.isGitRepository = true;
150
+ } catch {
151
+ this.isGitRepository = false;
152
+ }
153
+ }
154
+
155
+ assertActiveIsolation() {
156
+ if (!this.sandboxBranch) {
157
+ throw new Error('Git Sandbox operation error: No active sandbox tracking context exists.');
158
+ }
159
+ }
160
+ }
@@ -0,0 +1,150 @@
1
+ import { spawn } from 'child_process';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * Self-Healing Test Orchestration & Regression Scanner
6
+ * Runs validation suites, intercepts stack traces, and controls transaction rollbacks.
7
+ */
8
+ export class SelfHealer {
9
+ constructor(context, transactionManager, gitSandbox) {
10
+ this.context = context;
11
+ this.transactionManager = transactionManager;
12
+ this.gitSandbox = gitSandbox;
13
+ this.executionTimeout = 45000; // 45-second execution timeout wall
14
+ }
15
+
16
+ /**
17
+ * Validates the stability of staged code changes by running your project's test suite.
18
+ * @param {Function} refactorTask - The function containing code changes to evaluate
19
+ */
20
+ async runSelfHealingLifecycle(refactorTask) {
21
+ console.log('🛡️ Initializing transaction safety sandbox...');
22
+
23
+ // Step 1: Open the transaction boundaries
24
+ await this.gitSandbox.establishIsolationCheckpoint();
25
+ await this.transactionManager.begin();
26
+
27
+ try {
28
+ // Step 2: Execute the codebase pruning transformations
29
+ await refactorTask();
30
+
31
+ // Step 3: Stage changes inside our Git tracking context
32
+ await this.gitSandbox.stageAndCheckpointChanges();
33
+
34
+ console.log('🧪 Running testing suites to verify workspace integrity...');
35
+ const validationPassed = await this.verifyProjectHealthStatus();
36
+
37
+ if (validationPassed) {
38
+ console.log('✨ Build stable. Merging verified code improvements.');
39
+ await this.transactionManager.commit();
40
+ await this.gitSandbox.acceptAndMergeOptimizations();
41
+ return true;
42
+ } else {
43
+ console.warn('⚠️ Regression flagged during validation. Triggering self-healing rollback...');
44
+ await this.revertWorkspaceToSafeState();
45
+ return false;
46
+ }
47
+ } catch (pipelineError) {
48
+ console.error(`🚨 Critical Exception Intercepted: ${pipelineError.message}`);
49
+ await this.revertWorkspaceToSafeState();
50
+ return false;
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Spawns testing processes to detect exit code alerts or compiler trace metrics.
56
+ * @returns {Promise<boolean>} True if the test suite runs with an exit code of 0
57
+ */
58
+ verifyProjectHealthStatus() {
59
+ return new Promise((resolve) => {
60
+ const commandTokens = this.context.testCommand.split(' ');
61
+ const executionBinary = commandTokens.shift();
62
+
63
+ const processBuffer = [];
64
+ const testProcess = spawn(executionBinary, commandTokens, {
65
+ cwd: this.context.cwd,
66
+ shell: true
67
+ });
68
+
69
+ // Implement an execution timeout monitor to prevent hanging test suites from blocking the pipeline
70
+ const timeoutWatch = setTimeout(() => {
71
+ console.warn('⏱️ Test execution exceeded timeout limit. Terminating subprocess...');
72
+ testProcess.kill('SIGKILL');
73
+ }, this.executionTimeout);
74
+
75
+ testProcess.stdout.on('data', (data) => {
76
+ processBuffer.push(data);
77
+ if (this.context.verbose) {
78
+ process.stdout.write(data);
79
+ }
80
+ });
81
+
82
+ testProcess.stderr.on('data', (data) => {
83
+ processBuffer.push(data);
84
+ if (this.context.verbose) {
85
+ process.stderr.write(data);
86
+ }
87
+ });
88
+
89
+ testProcess.on('close', (exitCode) => {
90
+ clearTimeout(timeoutWatch);
91
+
92
+ if (exitCode !== 0) {
93
+ const fullConsoleOutput = Buffer.concat(processBuffer).toString('utf8');
94
+ this.diagnoseErrorOutput(fullConsoleOutput);
95
+ resolve(false);
96
+ } else {
97
+ resolve(true);
98
+ }
99
+ });
100
+
101
+ testProcess.on('error', (err) => {
102
+ clearTimeout(timeoutWatch);
103
+ if (this.context.verbose) {
104
+ console.error(`Subprocess execution error: ${err.message}`);
105
+ }
106
+ resolve(false);
107
+ });
108
+ });
109
+ }
110
+
111
+ /**
112
+ * Traces error codes and console log outputs to identify the root cause of a broken build.
113
+ */
114
+ diagnoseErrorOutput(consoleLogOutput) {
115
+ console.log('\n🔍 [Diagnostics] Reviewing error output...');
116
+
117
+ // Scan for standard TypeScript Compilation errors
118
+ const tsErrorPattern = /error\s+TS(\d+):\s+([\s\S]*?)$/m;
119
+ const tsMatches = consoleLogOutput.match(tsErrorPattern);
120
+
121
+ if (tsMatches) {
122
+ console.error(`❌ Mapped Type Error Code [TS${tsMatches[1]}]: ${tsMatches[2].trim()}`);
123
+ return;
124
+ }
125
+
126
+ // Scan for missing file imports or runtime resolution exceptions
127
+ const missingModulePattern = /Cannot find module '([^']+)'/i;
128
+ const moduleMatches = consoleLogOutput.match(missingModulePattern);
129
+
130
+ if (moduleMatches) {
131
+ console.error(`❌ Missing Dependency Reference Pointer: Cannot locate element [${moduleMatches[1]}]`);
132
+ return;
133
+ }
134
+
135
+ console.error('❌ Test suite execution failed. Check standard console output for details.');
136
+ }
137
+
138
+ /**
139
+ * Automatically executes a rollback event, restoring both file states and Git status.
140
+ */
141
+ async revertWorkspaceToSafeState() {
142
+ try {
143
+ await this.transactionManager.rollback();
144
+ await this.gitSandbox.rejectAndAbortOptimizations();
145
+ console.log('🔄 [Self-Healing] Rollback complete. Original project states restored.');
146
+ } catch (recoveryError) {
147
+ console.error(`🚨 Fatal Recovery Error: Failed to restore prior workspace configuration: ${recoveryError.message}`);
148
+ }
149
+ }
150
+ }
package/src/index.js ADDED
@@ -0,0 +1,384 @@
1
+ /**
2
+ * ============================================================================
3
+ * 📦 pkg-scaffold v3.0.0: Unified Architectural Refactoring Orchestrator
4
+ * ============================================================================
5
+ * Main execution bridge managing multi-pass compilation cycles, semantic cross-linking,
6
+ * supply-chain validation audits, and automated git self-healing rollbacks.
7
+ */
8
+
9
+ import fs from 'fs/promises';
10
+ import path from 'path';
11
+ import ansis from 'ansis';
12
+ import readline from 'readline/promises';
13
+
14
+ // Import local domain architecture sub-systems
15
+ import { EngineContext } from './EngineContext.js';
16
+ import { ASTAnalyzer } from './ast/ASTAnalyzer.js';
17
+ import { BarrelParser } from './ast/BarrelParser.js';
18
+ import { MagicDetector } from './ast/MagicDetector.js';
19
+ import { PathMapper } from './resolution/PathMapper.js';
20
+ import { WorkspaceGraph } from './resolution/WorkSpaceGraph.js';
21
+ import { DependencyResolver } from './resolution/DepencyResolver.js';
22
+ import { TransactionManager } from './refractor/TransactionManager.js';
23
+ import { ImpactAnalyzer } from './refractor/ImpactAnalyzer.js';
24
+ import { SourceRewriter } from './refractor/SourceRewriter.js';
25
+ import { TypeIntegrity } from './refractor/TypeIntegrity.js';
26
+ import { GitSandbox } from './healing/GitSandbox.js';
27
+ import { SelfHealer } from './healing/SelfHealer.js';
28
+ import { IncrementalCacheManager } from './performance/GraphCache.js';
29
+ import { WorkerPool } from './performance/WorkerPool.js';
30
+ import { SupplyChainGuard } from './performance/SupplyChainGuard.js';
31
+
32
+ /**
33
+ * Primary Refactoring Engine Core Coordination Controller
34
+ */
35
+ export class RefactoringEngine {
36
+ constructor(options = {}) {
37
+ // Stage 1: Instantiate State Registers and Global Variables context
38
+ this.context = new EngineContext(options);
39
+
40
+ // Stage 2: Initialize File Mappers and Multi-Package Graph Networks
41
+ this.pathMapper = new PathMapper(this.context);
42
+ this.workspaceGraph = new WorkspaceGraph(this.context);
43
+ this.resolver = new DependencyResolver(this.context, this.pathMapper, this.workspaceGraph);
44
+
45
+ // Stage 3: Wire official AST Syntax parsers and framework processors
46
+ this.analyzer = new ASTAnalyzer(this.context);
47
+ this.barrelParser = new BarrelParser(this.context, this.resolver);
48
+ this.magicDetector = new MagicDetector(this.context);
49
+
50
+ // Stage 4: Connect Transaction managers and surgical code generation scripts
51
+ this.txManager = new TransactionManager(this.context);
52
+ this.impactAnalyzer = new ImpactAnalyzer(this.context);
53
+ this.sourceRewriter = new SourceRewriter(this.context);
54
+ this.typeIntegrity = new TypeIntegrity(this.context);
55
+
56
+ // Stage 5: Bind security audit utilities and performance cache rings
57
+ this.supplyChainGuard = new SupplyChainGuard(this.context);
58
+ this.cacheManager = new IncrementalCacheManager(this.context);
59
+ this.workerPool = new WorkerPool(this.context);
60
+ this.gitSandbox = new GitSandbox(this.context);
61
+ this.selfHealer = new SelfHealer(this.context, this.txManager, this.gitSandbox);
62
+ }
63
+
64
+ /**
65
+ * Main Operational Loop executing multi-stage analysis passes across files.
66
+ */
67
+ async run() {
68
+ try {
69
+ console.log(ansis.bold.green('🎯 Starting pkg-scaffold Operational Optimization Cycle...'));
70
+
71
+ const rl = readline.createInterface({
72
+ input: process.stdin,
73
+ output: process.stdout
74
+ });
75
+
76
+ // Pass 1: Boot environment contexts and load alias configuration maps
77
+ await this.context.initialize();
78
+ await this.pathMapper.loadMappings(this.context.tsconfigFilename);
79
+
80
+ if (this.context.isWorkspaceEnabled) {
81
+ console.log(ansis.dim('🌐 Mapping local monorepo workspaces and package mesh layers...'));
82
+ await this.workspaceGraph.initializeWorkspaceMesh();
83
+ }
84
+
85
+ // Load asset fingerprints from disk cache to maximize cold-start performance
86
+ const cacheManifest = await this.cacheManager.loadCacheManifest();
87
+
88
+ // Pass 2: Recursively crawl directories to compile target codebase files list
89
+ const fileList = [];
90
+ await this.discoverSourceFiles(this.context.cwd, fileList);
91
+ this.context.metrics.totalFilesScanned = fileList.length;
92
+
93
+ // Identify meta-framework setups (Next.js, Remix, Nuxt, etc.)
94
+ const activeFrameworkEcosystems = await this.magicDetector.identifyActiveProjectEcosystems(this.context.cwd);
95
+
96
+ // Separate explicit configuration packages out for targeted supply chain security checks
97
+ const sourceCodeFilesList = [];
98
+ for (const file of fileList) {
99
+ if (file.endsWith('package.json')) {
100
+ await this.auditManifestSupplyChain(file);
101
+ } else {
102
+ sourceCodeFilesList.push(file);
103
+ }
104
+ }
105
+
106
+ // Pass 3: Process source file tokens using high-performance concurrent workers
107
+ let parallelParseCompleted = false;
108
+ if (sourceCodeFilesList.length > 10) {
109
+ parallelParseCompleted = await this.workerPool.parallelAnalyzeCodebase(sourceCodeFilesList, this);
110
+ }
111
+
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
+ for (const filePath of sourceCodeFilesList) {
117
+ const node = this.context.createNode(filePath);
118
+ const currentHash = await this.cacheManager.computeHash(filePath);
119
+ node.contentHash = currentHash;
120
+
121
+ // Check if this individual file is already up-to-date in the cache
122
+ const isFileCached = cacheManifest[filePath] && cacheManifest[filePath].hash === currentHash;
123
+
124
+ if (isFileCached) {
125
+ this.context.metrics.cacheHits++;
126
+ this.hydrateNodeFromCache(node, cacheManifest[filePath]);
127
+ } else if (!parallelParseCompleted) {
128
+ // Only parse if the worker pool did not already handle this file
129
+ this.context.metrics.cacheMisses++;
130
+ await this.analyzer.processFile(filePath, node);
131
+ }
132
+
133
+ // Apply ecosystem overrides directly to our parsed memory maps
134
+ this.magicDetector.injectVirtualConsumerEdges(filePath, node, activeFrameworkEcosystems);
135
+ }
136
+
137
+ // Pass 4: Evaluate graph edges and link connections across the codebase mesh
138
+ console.log(ansis.dim('🔗 Linking graph edges and checking structural usage paths...'));
139
+ await this.linkDependencyGraph();
140
+
141
+ // Pass 5: Compile metrics summary and print diagnostics report
142
+ const analysisSummary = this.context.generateSummaryReport();
143
+ this.displayConsoleDiagnostics(analysisSummary);
144
+
145
+ // Pass 6: Run self-healing automated transformations if --fix is set
146
+ if (this.context.allowAutoFix) {
147
+ const structuralModificationsStaged =
148
+ analysisSummary.structuralIssuesDetected.deadFiles.length > 0 ||
149
+ analysisSummary.structuralIssuesDetected.deadExports.length > 0;
150
+
151
+ if (structuralModificationsStaged) {
152
+ console.log(ansis.bold.yellow('\n📋 Proposed Optimization Plan:'));
153
+ console.log(ansis.dim('------------------------------------------------------------'));
154
+
155
+ if (analysisSummary.structuralIssuesDetected.deadFiles.length > 0) {
156
+ console.log(ansis.bold(` 🗑️ Delete ${analysisSummary.structuralIssuesDetected.deadFiles.length} orphaned files:`));
157
+ analysisSummary.structuralIssuesDetected.deadFiles.forEach(f => console.log(ansis.dim(` • ${f}`)));
158
+ }
159
+
160
+ if (analysisSummary.structuralIssuesDetected.deadExports.length > 0) {
161
+ console.log(ansis.bold(` ✂️ Prune ${analysisSummary.structuralIssuesDetected.deadExports.length} unused named exports:`));
162
+ analysisSummary.structuralIssuesDetected.deadExports.forEach(e => console.log(ansis.dim(` • ${e.symbol} in ${e.file}:${e.line}`)));
163
+ }
164
+ console.log(ansis.dim('------------------------------------------------------------'));
165
+
166
+ let proceed = this.context.skipConfirm;
167
+ if (!proceed) {
168
+ const answer = await rl.question(ansis.bold.cyan('\n❓ Apply these structural modifications? (y/N): '));
169
+ proceed = answer.toLowerCase() === 'y';
170
+ }
171
+
172
+ if (proceed) {
173
+ await this.selfHealer.runSelfHealingLifecycle(async () => {
174
+ // Sub-Task A: Purge completely unreferenced dangling components
175
+ for (const relPath of analysisSummary.structuralIssuesDetected.deadFiles) {
176
+ const absPath = path.resolve(this.context.cwd, relPath);
177
+ console.log(ansis.red(`✂️ Removing unreferenced file: ${relPath}`));
178
+ await this.txManager.stageDeletion(absPath);
179
+ }
180
+
181
+ // Sub-Task B: Surgically remove unused named export blocks from active files
182
+ for (const unusedExport of analysisSummary.structuralIssuesDetected.deadExports) {
183
+ const absPath = path.resolve(this.context.cwd, unusedExport.file);
184
+ const node = this.context.graph.get(absPath);
185
+
186
+ if (!node) continue;
187
+ const meta = node.internalExports.get(unusedExport.symbol);
188
+
189
+ // Perform safety analysis to ensure the token isn't called via dynamic runtime methods
190
+ const safetyVerdict = await this.impactAnalyzer.verifyRefactorSafety(absPath, unusedExport.symbol, this.context.graph);
191
+
192
+ if (safetyVerdict.isSafeToPrune) {
193
+ console.log(ansis.yellow(`⚡ Stripping unused export [${unusedExport.symbol}] from: ${unusedExport.file}:${unusedExport.line}`));
194
+ const nextText = await this.sourceRewriter.stripNamedExportSignature(absPath, unusedExport.symbol, meta);
195
+
196
+ await this.txManager.stageWrite(absPath, nextText);
197
+
198
+ // Align matching type declaration boundaries (.d.ts) to prevent compilation errors
199
+ await this.typeIntegrity.synchronizeDeclarationFile(absPath, unusedExport.symbol);
200
+ } else if (this.context.verbose) {
201
+ console.log(ansis.gray(`🛡️ Preserving symbol export [${unusedExport.symbol}] due to: ${safetyVerdict.blockReason}`));
202
+ }
203
+ }
204
+ });
205
+ } else {
206
+ console.log(ansis.bold.yellow('\n⚠️ Optimization plan aborted by user. No changes applied.'));
207
+ }
208
+ }
209
+ }
210
+
211
+ // Pass 7: Save optimized graph footprints back to the cache directory
212
+ await this.cacheManager.saveCacheManifest(this.context.graph);
213
+ rl.close();
214
+ console.log(ansis.bold.green('\n✨ Core optimization cycle completed smoothly. Codebase workspace is healthy.'));
215
+
216
+ } catch (criticalFault) {
217
+ console.error(ansis.bold.red(`\n🚨 Critical Operational Pipeline Failure: ${criticalFault.message}`));
218
+ if (criticalFault.stack) console.error(ansis.dim(criticalFault.stack));
219
+ process.exit(1);
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Links nodes and processes barrel files recursively without regular expressions.
225
+ */
226
+ async linkDependencyGraph() {
227
+ for (const [filePath, node] of this.context.graph.entries()) {
228
+
229
+ // Connect standard module imports
230
+ for (const specifier of node.explicitImports) {
231
+ const resolvedPath = this.resolver.resolveModulePath(filePath, specifier);
232
+ if (resolvedPath && this.context.graph.has(resolvedPath)) {
233
+ this.context.graph.get(resolvedPath).incomingEdges.add(filePath);
234
+ node.outgoingEdges.add(resolvedPath);
235
+ }
236
+ }
237
+
238
+ // Unroll re-exports and unwrap barrel file links recursively
239
+ for (const specToken of node.importedSymbols) {
240
+ const delimiterIndex = specToken.indexOf(':');
241
+ if (delimiterIndex === -1) continue;
242
+
243
+ const specifier = specToken.slice(0, delimiterIndex);
244
+ const symbol = specToken.slice(delimiterIndex + 1);
245
+
246
+ const resolvedPath = this.resolver.resolveModulePath(filePath, specifier);
247
+
248
+ if (resolvedPath && symbol !== '*') {
249
+ const traceResolution = await this.barrelParser.determineSymbolDeclarationOrigin(
250
+ resolvedPath,
251
+ symbol,
252
+ this.context.graph
253
+ );
254
+
255
+ if (traceResolution && this.context.graph.has(traceResolution.originFile)) {
256
+ this.context.graph.get(traceResolution.originFile).incomingEdges.add(filePath);
257
+ node.outgoingEdges.add(traceResolution.originFile);
258
+ }
259
+ }
260
+ }
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Audits package json files using token equality checks instead of fragile regex searches.
266
+ */
267
+ async auditManifestSupplyChain(packageJsonPath) {
268
+ try {
269
+ const text = await fs.readFile(packageJsonPath, 'utf8');
270
+ const data = JSON.parse(text);
271
+
272
+ const prodDeps = Object.keys(data.dependencies || {});
273
+ const devDeps = Object.keys(data.devDependencies || {});
274
+ const totalDependencies = [...prodDeps, ...devDeps];
275
+
276
+ // Pass dependencies down to our Levenshtein string-distance guard to find typosquat targets
277
+ const supplyChainThreats = this.supplyChainGuard.detectTyposquattingAnomalies(totalDependencies);
278
+
279
+ for (const anomaly of supplyChainThreats) {
280
+ console.warn(ansis.bold.red(`🚨 Supply Chain Alert: Malicious package masking candidate discovered [${anomaly.maliciousCandidate}]. Mimics trusted library [${anomaly.targetMimicked}].`));
281
+ }
282
+
283
+ // Verify lockfile hash configurations to catch poisoned manifests
284
+ await this.supplyChainGuard.verifyIntegrityLockfileHashes(packageJsonPath);
285
+ } catch {
286
+ // Manifest unreadable or locked; skip gracefully
287
+ }
288
+ }
289
+
290
+ hydrateNodeFromCache(node, cachedRecord) {
291
+ cachedRecord.explicitImports.forEach(i => node.explicitImports.add(i));
292
+ cachedRecord.dynamicImports.forEach(i => node.dynamicImports.add(i));
293
+ cachedRecord.importedSymbols.forEach(s => node.importedSymbols.add(s));
294
+ cachedRecord.rawStringReferences.forEach(r => node.rawStringReferences.add(r));
295
+ cachedRecord.instantiatedIdentifiers.forEach(i => node.instantiatedIdentifiers.add(i));
296
+ cachedRecord.propertyAccessChains.forEach(c => node.propertyAccessChains.add(c));
297
+
298
+ if (cachedRecord.internalExports) {
299
+ Object.entries(cachedRecord.internalExports).forEach(([k, v]) => {
300
+ node.internalExports.set(k, v);
301
+ });
302
+ }
303
+ node.isLibraryEntry = cachedRecord.isLibraryEntry || false;
304
+ node.securityThreats = cachedRecord.securityThreats || [];
305
+ if (cachedRecord.localSuppressedRules) {
306
+ cachedRecord.localSuppressedRules.forEach(r => node.localSuppressedRules.add(r));
307
+ }
308
+ if (cachedRecord.symbolSourceLocations) {
309
+ Object.entries(cachedRecord.symbolSourceLocations).forEach(([k, v]) => {
310
+ node.symbolSourceLocations.set(k, v);
311
+ });
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Crawls project roots using precise token extension checking instead of high-risk text matching.
317
+ */
318
+ async discoverSourceFiles(currentDirectory, fileAccumulator) {
319
+ try {
320
+ const entries = await fs.readdir(currentDirectory, { withFileTypes: true });
321
+
322
+ for (const entry of entries) {
323
+ const absolutePath = path.join(currentDirectory, entry.name);
324
+
325
+ if (entry.isDirectory()) {
326
+ // Standard systemic exclusions filters
327
+ if (entry.name === 'node_modules' ||
328
+ entry.name === '.git' ||
329
+ entry.name === '.scaffold-cache' ||
330
+ entry.name === 'dist' ||
331
+ entry.name === 'build') {
332
+ continue;
333
+ }
334
+ await this.discoverSourceFiles(absolutePath, fileAccumulator);
335
+ } else if (entry.isFile()) {
336
+ const extension = path.extname(entry.name);
337
+ if (extension === '.js' ||
338
+ extension === '.ts' ||
339
+ extension === '.tsx' ||
340
+ extension === '.jsx' ||
341
+ entry.name === 'package.json') {
342
+ fileAccumulator.push(absolutePath);
343
+ }
344
+ }
345
+ }
346
+ } catch {
347
+ // Path unreadable or access locked; close loop gracefully
348
+ }
349
+ }
350
+
351
+ displayConsoleDiagnostics(report) {
352
+ console.log(ansis.bold.green('\n📊 Codebase Structural Diagnostics Summary'));
353
+ console.log(ansis.dim('============================================================'));
354
+ console.log(`${ansis.bold('Processing Cycle Wall Duration:')} ${ansis.cyan(report.executionDuration)}`);
355
+ console.log(`${ansis.bold('Total Files Indexed Globally :')} ${ansis.white(report.totalFilesProcessed)}`);
356
+ console.log(`${ansis.bold('Delta Cache Optimization Ratio:')} ${ansis.green(report.graphCacheOptimization.ratio)} (Hits: ${report.graphCacheOptimization.hits} / Misses: ${report.graphCacheOptimization.misses})`);
357
+ console.log(ansis.dim('------------------------------------------------------------'));
358
+
359
+ if (report.structuralIssuesDetected.deadFiles.length > 0) {
360
+ console.log(ansis.bold.red(`\nOrphaned Files Flagged (${report.structuralIssuesDetected.deadFiles.length}):`));
361
+ report.structuralIssuesDetected.deadFiles.forEach(f => console.log(ansis.dim(` • ${f}`)));
362
+ } else {
363
+ console.log(ansis.green('\n✨ No orphaned or unreferenced component files found.'));
364
+ }
365
+
366
+ if (report.structuralIssuesDetected.deadExports.length > 0) {
367
+ console.log(ansis.bold.yellow(`\nUnused Named Symbol Exports Flagged (${report.structuralIssuesDetected.deadExports.length}):`));
368
+ report.structuralIssuesDetected.deadExports.forEach(e => {
369
+ console.log(` • ${ansis.dim(e.file)}:${e.line}:${e.column} -> [${ansis.yellow(e.symbol)}] (Type: ${e.type})`);
370
+ });
371
+ } else {
372
+ console.log(ansis.green('✨ No dead or unused named symbols exported across components.'));
373
+ }
374
+
375
+ if (report.structuralIssuesDetected.securityThreats.length > 0) {
376
+ console.log(ansis.bold.red(`\n⚠️ High-Risk Variable Assignment Alerts (${report.structuralIssuesDetected.securityThreats.length}):`));
377
+ report.structuralIssuesDetected.securityThreats.forEach(threat => {
378
+ console.log(` • ${ansis.red(threat.file)}:${threat.line} -> Variable [${ansis.bold(threat.identifier)}] contains a high-entropy secret (Shannon Score: ${threat.entropy})`);
379
+ });
380
+ }
381
+
382
+ console.log(ansis.dim('\n============================================================\n'));
383
+ }
384
+ }