entkapp 4.5.1 β†’ 5.1.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.
package/src/index.js CHANGED
@@ -1,14 +1,16 @@
1
- import { DeadCodeDetector } from "./ast/DeadCodeDetector.js";
2
1
  import { OxcAnalyzer } from "./ast/OxcAnalyzer.js";
3
2
  import { SecretScanner } from './ast/SecretScanner.js';
4
3
  import { AdvancedAnalysis } from './ast/AdvancedAnalysis.js';
5
4
  import { WorkspaceDiagnostic } from './resolution/WorkspaceDiagnostic.js';
5
+ import { DeadCodeDetector } from './ast/DeadCodeDetector.js';
6
+
6
7
  /**
7
8
  * ============================================================================
8
- * πŸ“¦ entkapp v4.1.0: Unified Architectural Refactoring Orchestrator
9
+ * πŸ“¦ entkapp v4.6.0: Unified Architectural Refactoring Orchestrator
9
10
  * ============================================================================
10
11
  * Main execution bridge managing multi-pass compilation cycles, semantic cross-linking,
11
12
  * supply-chain validation audits, and automated structural healing rollbacks.
13
+ * Production-ready linear orchestration engine.
12
14
  */
13
15
 
14
16
  import fs from 'fs/promises';
@@ -54,6 +56,7 @@ export class RefactoringEngine {
54
56
  this.context.entryPoints = options.entryPoints || [];
55
57
  this.context.exclude = options.exclude || [];
56
58
  this.context.rules = options.rules || {};
59
+
57
60
  // Stage 2: Initialize File Mappers and Multi-Package Graph Networks
58
61
  this.pathMapper = new PathMapper(this.context);
59
62
  this.workspaceGraph = new WorkspaceGraph(this.context);
@@ -78,10 +81,12 @@ export class RefactoringEngine {
78
81
  this.workerPool = new WorkerPool(this.context);
79
82
  this.gitSandbox = new GitSandbox(this.context);
80
83
  this.selfHealer = new SelfHealer(this.context, this.txManager, this.gitSandbox);
84
+
81
85
  // Stage 6: Secret / hardcoded credential scanner
82
86
  this.secretScanner = new SecretScanner();
83
87
  this.advancedAnalysis = new AdvancedAnalysis(this.context);
84
88
  this.workspaceDiagnostic = new WorkspaceDiagnostic(this.context);
89
+ this.deadCodeDetector = new DeadCodeDetector(this.context);
85
90
  }
86
91
 
87
92
  /**
@@ -103,20 +108,34 @@ export class RefactoringEngine {
103
108
  await this.oxcAnalyzer.init();
104
109
  await this.pathMapper.loadMappings(this.context.tsconfigFilename);
105
110
 
106
- // Always attempt workspace mesh initialization – it will auto-detect workspace
107
- // configuration and flip `context.isWorkspaceEnabled` when found.
111
+ // Always attempt workspace mesh initialization
108
112
  console.log(ansis.dim('🌐 Probing for monorepo workspace configuration...'));
109
113
  await this.workspaceGraph.initializeWorkspaceMesh();
110
114
  if (this.context.isWorkspaceEnabled) {
111
115
  console.log(ansis.dim('🌐 Monorepo workspace detected – mapping package mesh layers...'));
112
116
  }
113
117
 
118
+ // UPGRADE: Always clear cache for fresh analysis run
119
+ await this.cacheManager.clearCache();
120
+
114
121
  // Load asset fingerprints from disk cache to maximize cold-start performance
115
122
  const cacheManifest = await this.cacheManager.loadCacheManifest();
116
123
 
117
124
  // Pass 2: Recursively crawl directories to compile target codebase files list
118
- const fileList = [];
119
- await this.discoverSourceFiles(this.context.cwd, fileList);
125
+ const rawFileList = [];
126
+ await this.discoverSourceFiles(this.context.cwd, rawFileList);
127
+
128
+ // UPGRADE: De-duplicate and normalize file list to prevent massive count inflation
129
+ const slashifyInternal = (p) => {
130
+ let abs = path.resolve(this.context.cwd, p).replace(/\\/g, '/');
131
+ if (/^[a-z]:\//i.test(abs)) {
132
+ abs = abs.charAt(0).toUpperCase() + abs.slice(1);
133
+ }
134
+ return abs;
135
+ };
136
+ const uniqueFiles = new Set(rawFileList.map(f => slashifyInternal(f)));
137
+ const fileList = Array.from(uniqueFiles);
138
+
120
139
  this.context.metrics.totalFilesScanned = fileList.length;
121
140
 
122
141
  // Identify meta-framework setups (Next.js, Remix, Nuxt, etc.)
@@ -138,17 +157,42 @@ export class RefactoringEngine {
138
157
  parallelParseCompleted = await this.workerPool.parallelAnalyzeCodebase(sourceCodeFilesList, this);
139
158
  }
140
159
 
160
+ // =========================================================================
161
+ // πŸ’Ž GESETZ 1: EXKLUSIVE WURZEL-GARANTIE AUS MANIFEST EXTRAHIEREN
162
+ // =========================================================================
163
+ let localManifestMainEntryPoint = null;
164
+ try {
165
+ const pkgPath = path.join(this.context.cwd, 'package.json');
166
+ if (existsSync(pkgPath)) {
167
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
168
+ if (pkg.main) {
169
+ localManifestMainEntryPoint = path.resolve(this.context.cwd, pkg.main).replace(/\\/g, '/');
170
+ }
171
+ }
172
+ } catch (e) {}
173
+
174
+ // =========================================================================
175
+ // πŸ”„ PRIMARY FILE-PARSING LIFECYCLE LOOP
176
+ // =========================================================================
141
177
  for (const filePath of sourceCodeFilesList) {
142
- const node = this.context.getOrCreateNode(filePath);
143
- const currentHash = await this.cacheManager.computeHash(filePath);
178
+ const absFilePath = slashifyInternal(filePath);
179
+ const node = this.context.getOrCreateNode(absFilePath);
180
+ const currentHash = await this.cacheManager.computeHash(absFilePath);
144
181
  node.contentHash = currentHash;
145
182
 
183
+ // --- MANIFEST ENTRY ABSOLUTE IMMUNITΓ„T ---
184
+ if (localManifestMainEntryPoint && absFilePath === localManifestMainEntryPoint) {
185
+ node.isEntry = true;
186
+ if (this.context.verbose) {
187
+ console.log(ansis.bold.green(`[WURZEL-GARANTIE] ${filePath} sofort als unantastbarer Entry Point verankert.`));
188
+ }
189
+ }
190
+
146
191
  const isFileCached = cacheManifest[filePath] && cacheManifest[filePath].hash === currentHash;
147
192
 
148
193
  if (isFileCached) {
149
194
  this.context.metrics.cacheHits++;
150
195
  this.hydrateNodeFromCache(node, cacheManifest[filePath]);
151
- // Re-run secret scan even on cached files (secrets may change without AST change)
152
196
  try {
153
197
  const cachedContent = await fs.readFile(filePath, 'utf8');
154
198
  const secretFindings = this.secretScanner.scanFileContent(filePath, cachedContent);
@@ -159,10 +203,23 @@ export class RefactoringEngine {
159
203
  } catch { /* unreadable file – skip */ }
160
204
  } else if (!parallelParseCompleted) {
161
205
  this.context.metrics.cacheMisses++;
162
- const fileContent = await fs.readFile(filePath, 'utf8'); // Read file content here
206
+ const fileContent = await fs.readFile(filePath, 'utf8');
207
+
208
+ let success = false;
163
209
  if (this.oxcAnalyzer.isAvailable) {
164
- await this.oxcAnalyzer.parseFile(filePath, fileContent, node);
165
- } else {
210
+ success = await this.oxcAnalyzer.parseFile(filePath, fileContent, node);
211
+ }
212
+
213
+ // UPGRADE: Improved fallback logic for CommonJS files
214
+ const hasImportExportKeywords = fileContent.includes('import') || fileContent.includes('export');
215
+ const hasCommonJSKeywords = fileContent.includes('require') || fileContent.includes('module.exports') || fileContent.includes('exports.');
216
+ const oxcFailedToFindDependencies = node.explicitImports.size === 0 && node.internalExports.size === 0;
217
+
218
+ // Fallback to TS parser if:
219
+ // 1. OXC failed completely, OR
220
+ // 2. OXC found no dependencies but file has import/export keywords, OR
221
+ // 3. OXC found no dependencies but file has CommonJS keywords
222
+ if (!success || (oxcFailedToFindDependencies && (hasImportExportKeywords || hasCommonJSKeywords))) {
166
223
  await this.analyzer.parseFile(filePath, fileContent, node);
167
224
  }
168
225
  // Secret scan on freshly parsed content
@@ -176,20 +233,18 @@ export class RefactoringEngine {
176
233
  await this.magicDetector.injectVirtualConsumerEdges(filePath, node, activeFrameworkEcosystems);
177
234
 
178
235
  // Fix: Explicitly protect entry points defined in local configuration
179
- const slashify = (p) => path.resolve(this.context.cwd, p).replace(/\\/g, '/');
236
+ const slashifyLocal = (p) => path.resolve(this.context.cwd, p).replace(/\\/g, '/');
180
237
  if (this.context.entryPoints && this.context.entryPoints.some(ep => {
181
- const absEp = slashify(ep);
238
+ const absEp = slashifyLocal(ep);
182
239
  const cleanAbsEp = absEp.replace(/\.[^/.]+$/, "");
183
- const cleanFilePath = slashify(filePath).replace(/\.[^/.]+$/, "");
184
- return absEp === slashify(filePath) || cleanAbsEp === cleanFilePath;
240
+ const cleanFilePath = slashifyLocal(filePath).replace(/\.[^/.]+$/, "");
241
+ return absEp === slashifyLocal(filePath) || cleanAbsEp === cleanFilePath;
185
242
  })) {
186
- node.isLibraryEntry = true;
243
+ node.isEntry = true;
187
244
  }
188
- // node.externalPackageUsage.forEach(pkg => this.context.usedExternalPackages.add(pkg));
189
- }
245
+ } // πŸ’Ž HIER SCHLIESST DIE DATEI-SCHLEIFE JETZT SAUBER UND KORREKT!
190
246
 
191
247
  // Fix: Automatically mark active ecosystem packages as used.
192
- // Maps internal plugin names to their canonical npm package names.
193
248
  const pluginToPackageMap = {
194
249
  'typescript': 'typescript',
195
250
  'vitest': 'vitest',
@@ -215,18 +270,35 @@ export class RefactoringEngine {
215
270
  }
216
271
  });
217
272
 
218
- // Ensure all workspace package names are pre-marked as used so they are
219
- // never reported as unused dependencies in the manifest audit.
273
+ // =========================================================================
274
+ // πŸš€ ZWEITER DEBUG SENSOR: ImmunitΓ€ts-Verifikation nach dem Parsen
275
+ // =========================================================================
276
+ console.log(ansis.bold.magenta('\nπŸ” [DEBUG] Zustand der Entry-Points nach dem Parsen:'));
277
+ let entryCount = 0;
278
+ for (const [filePath, node] of this.context.projectGraph.entries()) {
279
+ if (node.isEntry) {
280
+ entryCount++;
281
+ const rel = path.relative(this.context.cwd, filePath).replace(/\\/g, '/');
282
+ console.log(ansis.green(` β€’ πŸ’Ž ERKANNT ALS ENTRY: ${rel}`));
283
+ }
284
+ }
285
+ if (entryCount === 0) {
286
+ console.log(ansis.bold.red(' 🚨 ALARM: Keine einzige Datei wurde als Entry Point markiert!'));
287
+ }
288
+
289
+ // Markiere Workspace-Pakete als genutzt, um Fehlalarme im Manifest-Auditor zu blockieren
220
290
  if (this.context.isWorkspaceEnabled) {
221
291
  this.workspaceGraph.markWorkspacePackagesAsUsed();
222
292
  }
223
293
 
224
- // Pass 4: Evaluate graph edges and link connections across the codebase mesh
294
+ // Pass 4: Berechne Graph-Kanten und verknΓΌpfe Import-Verbindungen
225
295
  console.log(ansis.dim('πŸ”— Linking graph edges and checking structural usage paths...'));
226
296
  if (this.context.verbose) {
227
297
  console.log(`[Linker] Starting dependency graph linkage for ${this.context.projectGraph.size} nodes.`);
228
298
  }
229
- await this.linkDependencyGraph();
299
+
300
+ await this.linkDependencyGraph(); // πŸ’Ž Wird jetzt exakt ein einziges Mal gestartet!
301
+
230
302
  if (this.context.verbose) {
231
303
  const totalEdges = Array.from(this.context.projectGraph.values()).reduce((sum, node) => sum + node.outgoingEdges.size, 0);
232
304
  console.log(`[Linker] Completed linkage. Total edges created: ${totalEdges}`);
@@ -243,51 +315,25 @@ export class RefactoringEngine {
243
315
  console.warn(ansis.bold.yellow(`\n⚠️ Detected ${cyclesResult.length} circular dependencies:`));
244
316
  this.circularDetector.formatCycles().forEach(c => console.log(ansis.dim(` β€’ ${c}`)));
245
317
  }
246
-
318
+
247
319
  // Pass 4b: Report hardcoded secrets
248
320
  console.log(ansis.dim("πŸ” Scanning for hardcoded secrets..."));
249
321
  const allSecrets = this.context.allSecretFindings || [];
250
- if (allSecrets.length > 0) {
322
+
323
+ if (this.context.allSecretFindings) {
324
+ const uniqueSecrets = new Map();
325
+ this.context.allSecretFindings.forEach(s => {
326
+ const key = `${s.file}:${s.line}:${s.type}`;
327
+ if (!uniqueSecrets.has(key)) uniqueSecrets.set(key, s);
328
+ });
329
+ this.context.allSecretFindings = Array.from(uniqueSecrets.values());
251
330
  }
252
331
 
253
332
  // NEW: Advanced Program Analysis (CFG, Data Flow, Taint Tracking)
254
333
  console.log(ansis.dim("🧠 Performing advanced program analysis..."));
255
334
  for (const [filePath, fileNode] of this.context.projectGraph.entries()) {
256
- // Placeholder for actual AST access, assuming oxcAnalyzer.parseFile populates fileNode.ast
257
- // In a real scenario, we'd need the actual AST object here.
258
- const ast = fileNode.ast || {}; // Assuming AST is stored in fileNode
335
+ const ast = fileNode.ast || {};
259
336
  this.advancedAnalysis.buildCFG(filePath, ast);
260
- // this.advancedAnalysis.handleComputedExports(fileNode, ast);
261
- // const unreachableCode = this.advancedAnalysis.analyzeReachability(filePath);
262
- // if (unreachableCode.length > 0) {
263
- // console.log(ansis.yellow(` ⚠️ Unreachable code detected in ${path.relative(this.context.cwd, filePath)}: ${unreachableCode.length} blocks`));
264
- // }
265
- // const taintFindings = this.advancedAnalysis.performTaintAnalysis(filePath, ast);
266
- // if (taintFindings.length > 0) {
267
- // console.log(ansis.red(` 🚨 Taint violations detected in ${path.relative(this.context.cwd, filePath)}: ${taintFindings.length} findings`));
268
- // taintFindings.forEach(f => console.log(ansis.dim(` β€’ ${f.type} at ${f.file}:${f.line} (Sink: ${f.sink})`)));
269
- // this.context.allSecretFindings.push(...taintFindings); // Add taint findings to overall secrets
270
- // }
271
-
272
- // Detect unused members
273
- // const unusedMembers = this.advancedAnalysis.detectUnusedMembers(fileNode);
274
- // if (unusedMembers.length > 0) {
275
- // console.log(ansis.yellow(` ⚠️ Unused members detected in ${path.relative(this.context.cwd, filePath)}:`));
276
- // unusedMembers.forEach(m => console.log(ansis.dim(` β€’ ${m.member}: ${m.message}`)));
277
- // }
278
-
279
- // NEW: Type-Jail Analysis
280
- // const typeJailViolations = this.workspaceDiagnostic.analyzeTypeJail(fileNode);
281
- // if (typeJailViolations.length > 0) {
282
- // console.log(ansis.yellow(` ⚠️ Type-Jail violations in ${path.relative(this.context.cwd, filePath)}:`));
283
- // typeJailViolations.forEach(v => console.log(ansis.dim(` β€’ ${v.message}`)));
284
- // }
285
-
286
- // NEW: Ghost Code Detection
287
- // const ghostExports = this.advancedAnalysis.detectGhostCode(fileNode, this.context.projectGraph);
288
- // if (ghostExports.length > 0) {
289
- // console.log(ansis.yellow(` πŸ‘» Ghost exports detected in ${path.relative(this.context.cwd, filePath)}: [${ghostExports.join(', ')}]`));
290
- // }
291
337
  }
292
338
 
293
339
  // NEW: Workspace Diagnostic & Architecture Enforcement
@@ -298,8 +344,6 @@ export class RefactoringEngine {
298
344
  workspaceHealthFindings.forEach(f => console.log(ansis.dim(` β€’ ${f.message}`)));
299
345
  }
300
346
 
301
- // Placeholder for boundary enforcement, would need to pass actual import data
302
- // For each file, iterate its imports and check against rules
303
347
  for (const [filePath, fileNode] of this.context.projectGraph.entries()) {
304
348
  const boundaryViolations = this.workspaceDiagnostic.enforceBoundaries(filePath, Array.from(fileNode.explicitImports));
305
349
  if (boundaryViolations.length > 0) {
@@ -308,15 +352,6 @@ export class RefactoringEngine {
308
352
  }
309
353
  }
310
354
 
311
- // Placeholder for Hotspot analysis
312
- // const hotspots = await this.workspaceDiagnostic.identifyHotspots(this.context.projectGraph, /* git history data */);
313
- // if (hotspots.length > 0) {
314
- // console.log(ansis.bold.magenta(`\nπŸ”₯ Code Hotspots identified:`));
315
- // hotspots.forEach(h => console.log(ansis.dim(` β€’ ${h.file}: Complexity ${h.complexity}, Change Frequency ${h.changeFrequency}`)));
316
- // }
317
-
318
- // End of new advanced analysis integrations
319
-
320
355
  if (allSecrets.length > 0) {
321
356
  const criticalSecrets = allSecrets.filter(s => s.severity === 'CRITICAL');
322
357
  const otherSecrets = allSecrets.filter(s => s.severity !== 'CRITICAL');
@@ -339,17 +374,12 @@ export class RefactoringEngine {
339
374
  }
340
375
 
341
376
  // Pass 5: Compile metrics summary and print diagnostics report
342
-
343
- // =========================================================================
344
- // πŸ›‘οΈ UNIFORM SLASH GRAPH EDGE RECONCILIATION LAYER (FINAL STABLE PRODUCTION)
345
- // =========================================================================
346
377
  if (!this.context.exportRegistry) this.context.exportRegistry = new Map();
347
378
  if (!this.context.importUsageRegistry) this.context.importUsageRegistry = new Set();
348
379
  if (!this.context.consumedRootPackages) this.context.consumedRootPackages = new Set();
349
380
  if (!this.context.consumedWorkspacePackages) this.context.consumedWorkspacePackages = new Set();
350
381
  if (!this.context.unlistedDependencies) this.context.unlistedDependencies = [];
351
382
 
352
- // Simple internal helper to guarantee matching forward slash strings across all platforms
353
383
  const slashify = (p) => path.resolve(this.context.cwd, p).replace(/\\/g, '/');
354
384
 
355
385
  if (this.context.projectGraph && typeof this.context.projectGraph.entries === 'function') {
@@ -358,9 +388,6 @@ export class RefactoringEngine {
358
388
 
359
389
  const cleanFilePath = slashify(filePath);
360
390
 
361
- // πŸš€ ROOT DEPS HARVESTER SHADOW TRACKING:
362
- // If external packages are parsed from ANY file node in the monorepo,
363
- // ensure the auditor registries register their footprint so root checking works!
364
391
  if (fileNode.externalPackageUsage) {
365
392
  fileNode.externalPackageUsage.forEach(pkg => {
366
393
  const relativeToRoot = path.relative(this.context.cwd, filePath);
@@ -372,7 +399,6 @@ export class RefactoringEngine {
372
399
  });
373
400
  }
374
401
 
375
- // 1. Gather all file exports using unified slashes
376
402
  if (fileNode.internalExports) {
377
403
  const exportKeys = typeof fileNode.internalExports.keys === 'function'
378
404
  ? Array.from(fileNode.internalExports.keys())
@@ -386,7 +412,6 @@ export class RefactoringEngine {
386
412
  }
387
413
  }
388
414
 
389
- // 2. Gather cross-file usage tokens using unified slashes
390
415
  if (fileNode.explicitImports && fileNode.importedSymbols) {
391
416
  const symbolsArray = typeof fileNode.importedSymbols.forEach === 'function'
392
417
  ? Array.from(fileNode.importedSymbols)
@@ -395,16 +420,7 @@ export class RefactoringEngine {
395
420
  for (const symbolToken of symbolsArray) {
396
421
  if (typeof symbolToken !== 'string') continue;
397
422
 
398
- // Fix: Use lastIndexOf for Windows paths (C:/path:symbol)
399
- // We need to be careful with C:\path... where the second colon is the separator
400
423
  let splitIndex = symbolToken.lastIndexOf(':');
401
-
402
- // If the colon is part of a Windows drive (e.g., index 1), look for another one
403
- if (splitIndex === 1 && symbolToken.length > 2 && /^[a-zA-Z]$/.test(symbolToken[0])) {
404
- // This is a drive letter, the actual separator must be elsewhere (though unlikely in this format)
405
- // But in 'C:/path:symbol', lastIndexOf(':') should already point to the correct one.
406
- }
407
-
408
424
  if (splitIndex === -1) continue;
409
425
 
410
426
  const specifier = symbolToken.slice(0, splitIndex);
@@ -419,9 +435,6 @@ export class RefactoringEngine {
419
435
  } else if (specifier.startsWith('.')) {
420
436
  targetFile = path.resolve(path.dirname(filePath), specifier);
421
437
 
422
- // πŸš€ COMPILE-TO-SOURCE EXTENSION SWAP:
423
- // If a barrel file imports relative paths using compiled targets (like './used-fn.js'),
424
- // replace the extension to check for active source components directly ('.ts' / '.tsx')
425
438
  if (targetFile.endsWith('.js')) {
426
439
  targetFile = targetFile.slice(0, -3);
427
440
  }
@@ -434,7 +447,6 @@ export class RefactoringEngine {
434
447
  }
435
448
 
436
449
  if (targetFile) {
437
- // Enforce uniform forward slash formats on targets
438
450
  const cleanTargetFile = Array.isArray(targetFile) ? slashify(targetFile[0]) : slashify(targetFile);
439
451
  this.context.importUsageRegistry.add(`${cleanTargetFile}:${symbolName}`);
440
452
  }
@@ -443,17 +455,14 @@ export class RefactoringEngine {
443
455
  }
444
456
  }
445
457
 
446
- // πŸš€ UNLISTED AUDITOR FALLBACK REMAPPING LAYER
447
458
  if (this.workspaceGraph && this.workspaceGraph.packageManifests) {
448
459
  for (const [_, metadata] of this.workspaceGraph.packageManifests.entries()) {
449
460
  if (this.context.projectGraph) {
450
461
  for (const [filePath, fileNode] of this.context.projectGraph.entries()) {
451
-
452
462
  const cleanRelative = path.relative(metadata.rootDirectory, filePath).replace(/\\/g, '/');
453
463
 
454
464
  if (!cleanRelative.startsWith('..') && !cleanRelative.startsWith('/') && fileNode.explicitImports) {
455
465
  try {
456
- // Switched to native sync token
457
466
  const localManifest = JSON.parse(readFileSync(metadata.manifestPath, 'utf8'));
458
467
  const localDeps = new Set([
459
468
  ...Object.keys(localManifest.dependencies || {}),
@@ -465,7 +474,6 @@ export class RefactoringEngine {
465
474
  if (specifier.startsWith('.') || specifier.startsWith('/')) return;
466
475
  const basePkg = specifier.startsWith('@') ? specifier.split('/').slice(0, 2).join('/') : specifier.split('/')[0];
467
476
 
468
- // Ensure lookups scan local package configurations only
469
477
  if (!localDeps.has(basePkg)) {
470
478
  const alreadyFlagged = this.context.unlistedDependencies.some(u => u.package === basePkg && u.file === filePath);
471
479
  if (!alreadyFlagged) {
@@ -478,7 +486,6 @@ export class RefactoringEngine {
478
486
  }
479
487
  });
480
488
  } catch (error) {
481
- // Dev logging fallback just in case JSON.parse hits bad layout characters
482
489
  if (this.context.options.verbose) {
483
490
  console.error(ansis.red(` ❌ Manifest Parsing Exception: ${error.message}`));
484
491
  }
@@ -489,12 +496,8 @@ export class RefactoringEngine {
489
496
  }
490
497
  }
491
498
 
492
- // =========================================================================
493
- // πŸ›‘οΈ ROOT GRAPH EDGE RECONCILIATION: Filter entry points directly inside context
494
- // =========================================================================
495
499
  if (this.workspaceGraph && this.workspaceGraph.packageManifests && this.context.orphanedFiles) {
496
500
  const verifiedSeeds = new Set();
497
-
498
501
  for (const [_, metadata] of this.workspaceGraph.packageManifests.entries()) {
499
502
  if (metadata.entryPoints) {
500
503
  metadata.entryPoints.forEach(absolutePath => {
@@ -505,148 +508,77 @@ export class RefactoringEngine {
505
508
 
506
509
  this.context.orphanedFiles = this.context.orphanedFiles.filter(flaggedFile => {
507
510
  const absoluteFlaggedPath = slashify(flaggedFile);
508
- const isAGraphSeed = verifiedSeeds.has(absoluteFlaggedPath);
509
- return !isAGraphSeed;
511
+ return !verifiedSeeds.has(absoluteFlaggedPath);
510
512
  });
511
513
  }
512
514
 
513
- // =========================================================================
514
- // πŸš€ PERMANENT COMPREHENSIVE ENGINE TELEMETRY DEBUG SENSOR
515
- // =========================================================================
516
515
  if (this.context?.options?.debug || this.context?.options?.verbose) {
517
516
  console.log('\nπŸ” [DEBUG METRICS] Evaluating Analyzer State Matrix:');
518
517
  console.log(` β€’ OXC Analyzer available & active: ${!!this.oxcAnalyzer?.isAvailable}`);
519
- if (!this.oxcAnalyzer?.isAvailable && this.context?.options?.verbose) {
520
- console.log(ansis.yellow(` ⚠️ OXC could not be initialized. Check if 'oxc-parser' is installed.`));
521
- }
522
518
  console.log(` β€’ Fast Mode execution flag state: ${!!this.context?.options?.fastMode}`);
523
519
  console.log(` β€’ Total files logged in exportRegistry: ${this.context?.exportRegistry ? this.context.exportRegistry.size : 0}`);
524
520
  console.log(` β€’ Total tracking tokens inside importUsageRegistry: ${this.context?.importUsageRegistry ? this.context.importUsageRegistry.size : 0}`);
525
521
  console.log(` β€’ Total unlisted dependencies intercepted: ${this.context?.unlistedDependencies ? this.context.unlistedDependencies.length : 0}`);
526
- console.log(` β€’ Consumed root external package names: [${Array.from(this.context?.consumedRootPackages || []).join(', ')}]`);
527
- console.log(` β€’ Consumed workspace package names: [${Array.from(this.context?.consumedWorkspacePackages || []).join(', ')}]`);
528
522
  console.log('------------------------------------------------------------\n');
529
523
  }
530
524
 
531
525
  const analysisSummary = await this.context.generateSummaryReport();
532
526
  analysisSummary.hardcodedSecrets = allSecrets;
533
527
 
534
- // 🚨 TARGET BUG 1: Detect Shadowed / Unused Root Dependencies
528
+ // const reachabilityResults = this.deadCodeDetector.detectDeadCode(this.context.projectGraph);
529
+ // analysisSummary.orphanedFiles = reachabilityResults.deadFiles.map(f => path.relative(this.context.cwd, f));
530
+
531
+ /* if (reachabilityResults.deadExports.length > 0) {
532
+ analysisSummary.deadExports = analysisSummary.deadExports.filter(de => {
533
+ return !analysisSummary.orphanedFiles.includes(de.file);
534
+ });
535
+ }*/
536
+
535
537
  try {
536
538
  const rootPkgPath = path.join(this.context.cwd, 'package.json');
537
539
  const rootPkg = JSON.parse(readFileSync(rootPkgPath, 'utf8'));
538
540
  const rootDeps = Object.keys(rootPkg.dependencies || {});
539
541
 
540
542
  for (const dep of rootDeps) {
541
- // Fix: Evaluate both root and workspace tracking sets to find shadowed root dependencies
542
543
  const usedInRoot = this.context.consumedRootPackages?.has(dep);
543
544
  const usedInWorkspaces = this.context.consumedWorkspacePackages?.has(dep);
544
545
 
545
546
  if (!usedInRoot && usedInWorkspaces) {
546
- const structuralViolation = {
547
- package: dep,
548
- type: 'dependency',
549
- manifest: 'package.json'
550
- };
551
-
547
+ const structuralViolation = { package: dep, type: 'dependency', manifest: 'package.json' };
552
548
  if (!analysisSummary.unusedDependencies) analysisSummary.unusedDependencies = [];
553
549
  const alreadyLogged = analysisSummary.unusedDependencies.some(d => d.package === dep);
554
- if (!alreadyLogged) {
555
- analysisSummary.unusedDependencies.push(structuralViolation);
556
- }
550
+ if (!alreadyLogged) analysisSummary.unusedDependencies.push(structuralViolation);
557
551
  }
558
552
  }
559
553
  } catch (e) {}
560
554
 
561
- // 🚨 TARGET BUG 3: Calculate and Append Unused Named Exports
562
- analysisSummary.deadExports = [];
555
+ const slashifyLocal = (p) => p ? path.resolve(this.context.cwd, p).replace(/\\/g, '/') : '';
563
556
  if (this.context.exportRegistry && this.workspaceGraph) {
564
- for (const [exportedFile, exportsSet] of this.context.exportRegistry.entries()) {
565
- const cleanExportedFile = slashify(exportedFile);
566
- const relativeExportedFile = path.relative(this.context.cwd, cleanExportedFile);
567
-
568
- if (analysisSummary.orphanedFiles.includes(relativeExportedFile)) {
569
- continue;
570
- }
557
+ analysisSummary.deadExports = (analysisSummary.deadExports || []).filter(de => {
558
+ if (!de || !de.file) return false;
559
+ const absPath = path.resolve(this.context.cwd, de.file);
560
+ const cleanAbsPath = slashifyLocal(absPath);
561
+ const node = this.context.projectGraph.get(cleanAbsPath);
571
562
 
563
+ if (node && (node.isLibraryEntry || node.isEntry)) return false;
564
+
572
565
  let isPackageEntryPoint = false;
573
- for (const [_, metadata] of this.workspaceGraph.packageManifests.entries()) {
574
- if (metadata.entryPoints.map(p => slashify(p)).includes(cleanExportedFile)) {
575
- isPackageEntryPoint = true;
576
- break;
577
- }
578
- }
579
- if (isPackageEntryPoint) continue;
580
-
581
- for (const symbol of exportsSet) {
582
- const consumptionToken = `${cleanExportedFile}:${symbol}`;
583
- if (!this.context.importUsageRegistry?.has(consumptionToken)) {
584
- analysisSummary.deadExports.push({
585
- symbol: symbol,
586
- file: relativeExportedFile,
587
- line: 7
588
- });
566
+ const manifests = this.workspaceGraph?.packageManifests;
567
+ if (manifests) {
568
+ for (const [_, metadata] of manifests.entries()) {
569
+ if (metadata.entryPoints && metadata.entryPoints.map(p => slashifyLocal(p)).includes(cleanAbsPath)) {
570
+ isPackageEntryPoint = true;
571
+ break;
572
+ }
589
573
  }
590
574
  }
591
- }
575
+ return !isPackageEntryPoint;
576
+ });
592
577
  }
593
578
  analysisSummary.unlistedDependencies = this.context.unlistedDependencies || [];
594
579
 
595
- // NEW: Advanced Program Analysis v4.5.0 (JIT, Topology, SAST)
596
- const advancedResults = this.advancedAnalysis.runAll(this.context.projectGraph, this.workspaceGraph.packageManifests);
597
-
598
- if (this.context.options.verbose) {
599
- if (advancedResults.jitWarnings.length > 0) {
600
- console.log(ansis.bold.yellow(`\n⚑ JIT Optimization Warnings (${advancedResults.jitWarnings.length}):`));
601
- advancedResults.jitWarnings.forEach(w => console.log(ansis.yellow(` β€’ [${w.type}] ${w.message} (${w.file}:${w.line})`)));
602
- }
603
- if (advancedResults.securityFindings.length > 0) {
604
- console.log(ansis.bold.red(`\nπŸ›‘οΈ Security Vulnerabilities (${advancedResults.securityFindings.length}):`));
605
- advancedResults.securityFindings.forEach(w => console.log(ansis.red(` β€’ [${w.type}] ${w.message} (${w.file}:${w.line})`)));
606
- }
607
- if (advancedResults.topologyWarnings.length > 0) {
608
- console.log(ansis.bold.blue(`\n🌐 Topology & Reachability Warnings (${advancedResults.topologyWarnings.length}):`));
609
- advancedResults.topologyWarnings.forEach(w => console.log(ansis.blue(` β€’ [${w.type}] ${w.message} (${w.file})`)));
610
- }
611
- if (advancedResults.integrityWarnings.length > 0) {
612
- console.log(ansis.bold.cyan(`\nπŸ“Š Config Integrity Checks (${advancedResults.integrityWarnings.length}):`));
613
- advancedResults.integrityWarnings.forEach(w => console.log(ansis.cyan(` β€’ [${w.type}] ${w.message} (${w.file})`)));
614
- }
615
- if (advancedResults.leakWarnings.length > 0) {
616
- console.log(ansis.bold.magenta(`\nπŸ’§ Event Leak Risk Analysis (${advancedResults.leakWarnings.length}):`));
617
- advancedResults.leakWarnings.forEach(w => console.log(ansis.magenta(` β€’ [${w.type}] ${w.message} (${w.file}:${w.line})`)));
618
- }
619
- if (advancedResults.binaryWarnings.length > 0) {
620
- console.log(ansis.bold.white(`\nπŸ—οΈ Binary Shaking Audit (${advancedResults.binaryWarnings.length}):`));
621
- advancedResults.binaryWarnings.forEach(w => console.log(ansis.white(` β€’ [${w.type}] ${w.message} (${w.file}:${w.line})`)));
622
- }
623
- if (advancedResults.sandboxViolations.length > 0) {
624
- console.log(ansis.bold.bgRed.white(`\n🧱 Architectural Sandbox Violations (${advancedResults.sandboxViolations.length}):`));
625
- advancedResults.sandboxViolations.forEach(w => console.log(ansis.red(` β€’ [${w.type}] ${w.message} (${w.file})`)));
626
- }
627
- if (advancedResults.dereferenceWarnings.length > 0) {
628
- console.log(ansis.bold.bgBlack.red(`\nπŸ’₯ Guaranteed Runtime Exceptions (${advancedResults.dereferenceWarnings.length}):`));
629
- advancedResults.dereferenceWarnings.forEach(w => console.log(ansis.red(` β€’ [${w.type}] ${w.message} (${w.file}:${w.line})`)));
630
- }
631
- if (advancedResults.cloneFindings.length > 0) {
632
- console.log(ansis.bold.bgCyan.black(`\nπŸ‘― Structural AST Clones Detected (${advancedResults.cloneFindings.length}):`));
633
- advancedResults.cloneFindings.forEach(w => console.log(ansis.cyan(` β€’ [${w.type}] ${w.message} (${w.file}:${w.line})`)));
634
- }
635
- if (advancedResults.scopeWarnings.length > 0) {
636
- console.log(ansis.bold.bgGreen.black(`\nπŸ”­ Scope Minimization Suggestions (${advancedResults.scopeWarnings.length}):`));
637
- advancedResults.scopeWarnings.forEach(w => console.log(ansis.green(` β€’ [${w.type}] ${w.message} (${w.file}:${w.line})`)));
638
- }
639
- if (advancedResults.loopWarnings.length > 0) {
640
- console.log(ansis.bold.bgYellow.black(`\nπŸ”„ Infinite Execution Proofs (${advancedResults.loopWarnings.length}):`));
641
- advancedResults.loopWarnings.forEach(w => console.log(ansis.yellow(` β€’ [${w.type}] ${w.message} (${w.file}:${w.line})`)));
642
- }
643
- if (advancedResults.configWarnings.length > 0) {
644
- console.log(ansis.bold.bgBlue.white(`\n🧼 Configuration Sanitizer (${advancedResults.configWarnings.length}):`));
645
- advancedResults.configWarnings.forEach(w => console.log(ansis.blue(` β€’ [${w.type}] ${w.message} (${w.file}:${w.line})`)));
646
- }
647
- }
648
-
649
- const cycles = cyclesResult; // Use the already detected cycles
580
+ const advancedResults = this.advancedAnalysis.runAll(this.context.projectGraph, this.workspaceGraph?.packageManifests || new Map());
581
+ const cycles = cyclesResult;
650
582
 
651
583
  const structuralModificationsStaged =
652
584
  analysisSummary.orphanedFiles.length > 0 ||
@@ -672,31 +604,25 @@ export class RefactoringEngine {
672
604
 
673
605
  if (analysisSummary.unusedDependencies && analysisSummary.unusedDependencies.length > 0) {
674
606
  console.log(ansis.bold(` πŸ“¦ Remove ${analysisSummary.unusedDependencies.length} unused dependencies:`));
675
- analysisSummary.unusedDependencies.forEach(d => {
676
- console.log(ansis.dim(` β€’ ${d.package} (${d.type} in ${d.manifest})`));
677
- });
607
+ analysisSummary.unusedDependencies.forEach(d => console.log(ansis.dim(` β€’ ${d.package} (${d.type} in ${d.manifest})`)));
608
+ }
609
+
610
+ if (analysisSummary.missingDependencies && analysisSummary.missingDependencies.length > 0) {
611
+ console.log(ansis.bold(` πŸ“¦ Add ${analysisSummary.missingDependencies.length} missing dependencies:`));
612
+ analysisSummary.missingDependencies.forEach(dep => console.log(ansis.dim(` β€’ ${dep} (detected via source usage)`)));
678
613
  }
679
614
 
680
- // 🚨 TARGET BUG 2: Print Alert layout warning for your unlisted package detections!
681
615
  if (analysisSummary.unlistedDependencies && analysisSummary.unlistedDependencies.length > 0) {
682
616
  console.log(ansis.bold.red(` ⚠️ Missing Declarations (Unlisted Packages Detected):`));
683
- analysisSummary.unlistedDependencies.forEach(u => {
684
- console.log(ansis.dim(` β€’ ${u.package} is imported in ${u.file} but missing from ${u.manifest}`));
685
- });
617
+ analysisSummary.unlistedDependencies.forEach(u => console.log(ansis.dim(` β€’ ${u.package} is imported in ${u.file} but missing from ${u.manifest}`)));
686
618
  }
687
619
 
688
620
  if (cycles.length > 0) {
689
621
  console.log(ansis.bold.magenta(` πŸ”„ Circular Dependencies Detected (${cycles.length}):`));
690
622
  cycles.forEach((cycle, idx) => {
691
- // Make paths relative to cwd for cleaner output
692
- const relativeCycle = cycle.map(p => path.relative(this.context.cwd, p));
693
- // Close the cycle visually by adding the first element at the end
694
- if (relativeCycle.length > 0) {
695
- relativeCycle.push(relativeCycle[0]);
696
- }
623
+ const relativeCycle = cycle.map(p => path.relative(this.context.cwd, p).replace(/\\/g, '/'));
697
624
  console.log(ansis.dim(` β€’ Cycle #${idx + 1}: ${relativeCycle.join(' -> ')}`));
698
625
  });
699
- console.log(ansis.italic.gray(' (Note: Automated resolution for circular dependencies is disabled to prevent structural damage.)'));
700
626
  }
701
627
 
702
628
  console.log(ansis.dim('------------------------------------------------------------'));
@@ -709,7 +635,6 @@ export class RefactoringEngine {
709
635
  }
710
636
 
711
637
  if (proceed) {
712
- // Execute healing lifecycle (git-state-capture -> apply -> verify -> commit/rollback)
713
638
  await this.selfHealer.runSelfHealingLifecycle(async () => {
714
639
  for (const relPath of analysisSummary.orphanedFiles) {
715
640
  const absPath = path.resolve(this.context.cwd, relPath);
@@ -729,8 +654,6 @@ export class RefactoringEngine {
729
654
  const nextText = await this.sourceRewriter.stripNamedExportSignature(absPath, unusedExport.symbol, meta);
730
655
  await this.txManager.stageWrite(absPath, nextText);
731
656
  await this.typeIntegrity.synchronizeDeclarationFile(absPath, unusedExport.symbol);
732
- } else if (this.context.verbose) {
733
- console.log(ansis.gray(`πŸ›‘οΈ Preserving symbol export [${unusedExport.symbol}] due to: ${safetyVerdict.blockReason}`));
734
657
  }
735
658
  }
736
659
  });
@@ -753,7 +676,6 @@ export class RefactoringEngine {
753
676
 
754
677
  async _generateVisualization(graph) {
755
678
  console.log(ansis.bold.green('\n🌐 [VISUALIZER] Generating Interactive Execution Graph...'));
756
-
757
679
  const nodes = [];
758
680
  const edges = [];
759
681
  const fileToIndex = new Map();
@@ -776,57 +698,11 @@ export class RefactoringEngine {
776
698
  const fromId = fileToIndex.get(file);
777
699
  node.outgoingEdges.forEach(edgeFile => {
778
700
  const toId = fileToIndex.get(edgeFile);
779
- if (toId) {
780
- edges.push({ from: fromId, to: toId, arrows: 'to' });
781
- }
701
+ if (toId) edges.push({ from: fromId, to: toId, arrows: 'to' });
782
702
  });
783
703
  }
784
704
 
785
- const html = `
786
- <!DOCTYPE html>
787
- <html>
788
- <head>
789
- <title>entkapp Execution Graph</title>
790
- <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
791
- <style type="text/css">
792
- body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background-color: #2d3436; color: #dfe6e9; font-family: sans-serif; }
793
- #network { width: 100%; height: 100%; }
794
- #header { position: absolute; top: 10px; left: 10px; z-index: 10; pointer-events: none; }
795
- h1 { margin: 0; font-size: 1.5rem; color: #fab1a0; }
796
- .legend { margin-top: 5px; font-size: 0.9rem; }
797
- .legend-item { display: inline-block; margin-right: 15px; }
798
- .dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 5px; }
799
- </style>
800
- </head>
801
- <body>
802
- <div id="header">
803
- <h1>entkapp Execution Graph</h1>
804
- <div class="legend">
805
- <div class="legend-item"><span class="dot" style="background-color: #ff7675; border-radius: 0; transform: rotate(45deg);"></span> Entry Point</div>
806
- <div class="legend-item"><span class="dot" style="background-color: #74b9ff;"></span> Module</div>
807
- </div>
808
- </div>
809
- <div id="network"></div>
810
- <script type="text/javascript">
811
- const nodes = new vis.DataSet(${JSON.stringify(nodes)});
812
- const edges = new vis.DataSet(${JSON.stringify(edges)});
813
- const container = document.getElementById('network');
814
- const data = { nodes, edges };
815
- const options = {
816
- nodes: { font: { color: '#dfe6e9', size: 14 } },
817
- edges: { color: { color: '#636e72', highlight: '#fab1a0' }, width: 2 },
818
- physics: {
819
- forceAtlas2Based: { gravitationalConstant: -50, centralGravity: 0.01, springLength: 100, springConstant: 0.08 },
820
- maxVelocity: 50,
821
- solver: 'forceAtlas2Based',
822
- timestep: 0.35,
823
- stabilization: { iterations: 150 }
824
- }
825
- };
826
- new vis.Network(container, data, options);
827
- </script>
828
- </body>
829
- </html>`;
705
+ const html = `<!DOCTYPE html><html><head><title>entkapp Execution Graph</title><script src="https://unpkg.com"></script><style>body,html{margin:0;padding:0;width:100%;height:100%;overflow:hidden;background-color:#2d3436;color:#dfe6e9;font-family:sans-serif;}#network{width:100%;height:100%;}#header{position:absolute;top:10px;left:10px;z-index:10;pointer-events:none;}h1{margin:0;font-size:1.5rem;color:#fab1a0;}.legend{margin-top:5px;font-size:0.9rem;}.legend-item{display:inline-block;margin-right:15px;}.dot{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:5px;}</style></head><body><div id="header"><h1>entkapp Execution Graph</h1><div class="legend"><div class="legend-item"><span class="dot" style="background-color:#ff7675;border-radius:0;transform:rotate(45deg);"></span> Entry Point</div><div class="legend-item"><span class="dot" style="background-color:#74b9ff;"></span> Module</div></div></div><div id="network"></div><script>const nodes=new vis.DataSet(${JSON.stringify(nodes)});const edges=new vis.DataSet(${JSON.stringify(edges)});const container=document.getElementById('network');const data={nodes,edges};const options={nodes:{font:{color:'#dfe6e9',size:14}},edges:{color:{color:'#636e72',highlight:#fab1a0},width:2},physics:{forceAtlas2Based:{gravitationalConstant:-50,centralGravity:0.01,springLength:100,springConstant:0.08},maxVelocity:50,solver:'forceAtlas2Based',timestep:0.35,stabilization:{iterations:150}}};new vis.Network(container,data,options);</script></body></html>`;
830
706
 
831
707
  const http = await import('http');
832
708
  const server = http.createServer((req, res) => {
@@ -837,13 +713,11 @@ export class RefactoringEngine {
837
713
  const port = 3000;
838
714
  server.listen(port, () => {
839
715
  console.log(ansis.bold.cyan(`\nπŸš€ Web Viewer active at: http://localhost:${port}`));
840
- console.log(ansis.yellow('Press Ctrl+C to terminate the session and continue...'));
841
716
  });
842
717
 
843
718
  return new Promise((resolve) => {
844
719
  process.on('SIGINT', () => {
845
720
  server.close();
846
- console.log(ansis.bold.red('\nπŸ›‘ Web Viewer stopped.'));
847
721
  resolve();
848
722
  });
849
723
  });
@@ -855,7 +729,6 @@ export class RefactoringEngine {
855
729
  const res = path.resolve(dir, entry.name);
856
730
  if (entry.isDirectory()) {
857
731
  if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === '.entkapp-cache') continue;
858
- if (this.context.verbose) console.log(ansis.dim(`πŸ“‚ Scanning deep folder: ${res}`));
859
732
  await this.discoverSourceFiles(res, fileList);
860
733
  } else {
861
734
  const ext = path.extname(entry.name);
@@ -867,75 +740,146 @@ export class RefactoringEngine {
867
740
  }
868
741
 
869
742
  async linkDependencyGraph() {
870
- for (const [filePath, node] of this.context.projectGraph.entries()) {
871
- if (this.context.verbose && node.explicitImports.size > 0) {
872
- console.log(`[Linker] Linking ${node.explicitImports.size} imports from ${path.relative(this.context.cwd, filePath)}`);
743
+ const slashify = (p) => {
744
+ let abs = path.resolve(this.context.cwd, p).replace(/\\/g, '/');
745
+ if (/^[a-z]:\//i.test(abs)) {
746
+ abs = abs.charAt(0).toUpperCase() + abs.slice(1);
873
747
  }
874
- // Pass A: Link all explicit imports (static + dynamic + re-export sources)
875
- for (const specifier of node.explicitImports) {
876
- const resolvedPath = this.resolver.resolveModulePath(filePath, specifier);
877
- if (resolvedPath && this.context.projectGraph.has(resolvedPath)) {
878
- if (this.context.verbose) {
879
- console.log(` -> Resolved ${specifier} to ${path.relative(this.context.cwd, resolvedPath)}`);
748
+ return abs;
749
+ };
750
+
751
+ // =========================================================================
752
+ // πŸ”„ PHASE 1: SAUBERE KANTEN-VERKNÜPFUNG (Zuerst das Netz spinnen!)
753
+ // =========================================================================
754
+ for (const [filePath, node] of this.context.projectGraph.entries()) {
755
+ const cleanSrcPath = slashify(filePath);
756
+
757
+ // Pass A: Link all explicit and dynamic imports safely
758
+ const allDirectImports = new Set([...node.explicitImports, ...node.dynamicImports]);
759
+ for (const specifier of allDirectImports) {
760
+ if (specifier.startsWith('__DYNAMIC_PATTERN__:')) continue;
761
+ if (this.context.verbose) console.log(`[Linker-DEBUG] Attempting to resolve ${specifier} from ${cleanSrcPath}`);
762
+ try {
763
+ const resolvedPath = this.resolver.resolveModulePath(cleanSrcPath, specifier);
764
+ if (this.context.verbose) console.log(`[Linker-DEBUG] Resolved to: ${resolvedPath}`);
765
+ if (resolvedPath && this.context.projectGraph.has(resolvedPath)) {
766
+ const cleanTargetPath = slashify(resolvedPath);
767
+ this.context.projectGraph.get(cleanTargetPath).incomingEdges.add(cleanSrcPath);
768
+ node.outgoingEdges.add(cleanTargetPath);
880
769
  }
881
- this.context.projectGraph.get(resolvedPath).incomingEdges.add(filePath);
882
- node.outgoingEdges.add(resolvedPath);
770
+ } catch (e) {}
771
+ }
772
+
773
+ // Pass B: Link named-symbol imports through barrel/re-export chains safely
774
+ for (const specToken of node.importedSymbols) {
775
+ try {
776
+ const delimiterIndex = specToken.lastIndexOf(':');
777
+ if (delimiterIndex === -1) continue;
778
+ const specifier = specToken.slice(0, delimiterIndex);
779
+ const symbol = specToken.slice(delimiterIndex + 1);
883
780
 
884
- // Fix: Ensure all internal exports from a re-exported source are marked as used
885
- // so the source file itself is never considered orphaned.
886
- const targetNode = this.context.projectGraph.get(resolvedPath);
887
- const isReExport = Array.from(node.internalExports.values()).some(exp => exp.source === specifier);
888
- if (isReExport) {
889
- targetNode.isLibraryEntry = true; // Protect re-exported internal files
781
+ const resolvedPath = this.resolver.resolveModulePath(cleanSrcPath, specifier);
782
+ if (!resolvedPath) continue;
783
+ const cleanResolvedPath = slashify(resolvedPath);
784
+
785
+ if (symbol === '*') {
786
+ if (this.context.projectGraph.has(cleanResolvedPath)) {
787
+ this.context.projectGraph.get(cleanResolvedPath).incomingEdges.add(cleanSrcPath);
788
+ node.outgoingEdges.add(cleanResolvedPath);
789
+
790
+ const targetNode = this.context.projectGraph.get(cleanResolvedPath);
791
+ for (const [expName, expMeta] of targetNode.internalExports.entries()) {
792
+ if (expMeta.type === 're-export-all' || expMeta.type === 're-export') {
793
+ const nestedPath = this.resolver.resolveModulePath(cleanResolvedPath, expMeta.source || expMeta.originalName);
794
+ if (nestedPath && this.context.projectGraph.has(slashify(nestedPath))) {
795
+ this.context.projectGraph.get(slashify(nestedPath)).incomingEdges.add(cleanResolvedPath);
796
+ }
797
+ }
798
+ }
799
+ }
800
+ } else {
801
+ const traceResolution = await this.barrelParser.determineSymbolDeclarationOrigin(cleanResolvedPath, symbol, this.context.projectGraph);
802
+ if (traceResolution && traceResolution.originFile) {
803
+ const cleanOriginFile = slashify(traceResolution.originFile);
804
+ if (this.context.projectGraph.has(cleanOriginFile)) {
805
+ this.context.projectGraph.get(cleanOriginFile).incomingEdges.add(cleanSrcPath);
806
+ node.outgoingEdges.add(cleanOriginFile);
807
+ if (!this.context.importUsageRegistry) this.context.importUsageRegistry = new Set();
808
+ this.context.importUsageRegistry.add(`${cleanOriginFile}:${traceResolution.originSymbol}`);
809
+ }
810
+ }
890
811
  }
891
- }
812
+ } catch (e) {}
892
813
  }
814
+ }
815
+
816
+ // =========================================================================
817
+ // πŸ”„ PHASE 2: SELEKTIVE ENTRY POINT IMMUNITΓ„T (Erst jetzt Heuristik prΓΌfen!)
818
+ // =========================================================================
819
+ const potentialEntryPoints = new Set();
820
+ let hasManifestEntries = false;
893
821
 
894
- // Pass A.2: Mark package entry points as library entries
895
- for (const pkg of this.workspaceGraph.packageManifests.values()) {
896
- for (const entryPath of pkg.entryPoints) {
897
- if (this.context.projectGraph.has(entryPath)) {
898
- this.context.projectGraph.get(entryPath).isLibraryEntry = true;
822
+ try {
823
+ const pkgPath = path.join(this.context.cwd, 'package.json');
824
+ if (existsSync(pkgPath)) {
825
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
826
+ const packageEntries = this.workspaceGraph.calculatePackageExportsEntries(pkg, this.context.cwd);
827
+ if (packageEntries.length > 0) {
828
+ hasManifestEntries = true;
829
+ for (const absEntry of packageEntries) {
830
+ potentialEntryPoints.add(slashify(absEntry));
899
831
  }
900
832
  }
901
833
  }
902
- // Auto-detect root index as entry point if no entry points are defined
903
- const rootIndexFiles = ['src/index.ts', 'src/index.js', 'index.ts', 'index.js'];
904
- const slashify = (p) => path.resolve(this.context.cwd, p).replace(/\\/g, '/');
905
- for (const indexFile of rootIndexFiles) {
906
- const absIndex = slashify(indexFile);
907
- if (this.context.projectGraph.has(absIndex)) {
908
- this.context.projectGraph.get(absIndex).isLibraryEntry = true;
909
- }
834
+ } catch (e) {}
835
+
836
+ if (!hasManifestEntries && this.context.entryPoints.length === 0) {
837
+ const rootIndexFiles = ['src/index.ts', 'src/index.js', 'src/index.tsx', 'src/index.jsx', 'index.ts', 'index.js', 'src/main.ts', 'src/main.js', 'main.ts', 'main.js'];
838
+ for (const indexFile of rootIndexFiles) potentialEntryPoints.add(slashify(indexFile));
839
+ }
840
+
841
+ const protectedFiles = new Set();
842
+ for (const [filePath] of this.context.projectGraph.entries()) {
843
+ const fileName = path.basename(filePath);
844
+ if (/\.(test|spec|e2e)\.(ts|js|tsx|jsx)$/.test(fileName) || filePath.includes('/__tests__/') || filePath.includes('/tests/') || /\.(config|rc)\.(ts|js|mjs|cjs)$/.test(fileName)) {
845
+ protectedFiles.add(slashify(filePath));
910
846
  }
847
+ }
911
848
 
912
- // Pass B: Link named-symbol imports through barrel/re-export chains
913
- for (const specToken of node.importedSymbols) {
914
- // Fix: Use lastIndexOf for Windows paths (C:/path:symbol)
915
- const delimiterIndex = specToken.lastIndexOf(':');
916
- if (delimiterIndex === -1) continue;
917
- const specifier = specToken.slice(0, delimiterIndex);
918
- const symbol = specToken.slice(delimiterIndex + 1);
919
- const resolvedPath = this.resolver.resolveModulePath(filePath, specifier);
920
-
921
- if (!resolvedPath) continue;
922
-
923
- if (symbol === '*') {
924
- // Wildcard import / re-export-all: add a direct edge to the resolved file.
925
- if (this.context.projectGraph.has(resolvedPath)) {
926
- this.context.projectGraph.get(resolvedPath).incomingEdges.add(filePath);
927
- node.outgoingEdges.add(resolvedPath);
928
- }
929
- } else {
930
- // Named import: trace through barrel files to the actual declaration origin.
931
- const traceResolution = await this.barrelParser.determineSymbolDeclarationOrigin(resolvedPath, symbol, this.context.projectGraph);
932
- if (traceResolution && this.context.projectGraph.has(traceResolution.originFile)) {
933
- this.context.projectGraph.get(traceResolution.originFile).incomingEdges.add(filePath);
934
- node.outgoingEdges.add(traceResolution.originFile);
935
- node.importedSymbols.add(`${traceResolution.originFile}:${traceResolution.symbolName}`);
849
+ // JETZT haben die Kanten verlÀssliche incomingEdges-Grâßen!
850
+ for (const [filePath, node] of this.context.projectGraph.entries()) {
851
+ const absPath = slashify(filePath);
852
+ const isProtected = protectedFiles.has(absPath);
853
+ const isManifestEntry = potentialEntryPoints.has(absPath);
854
+
855
+ const hasNoIncoming = node.incomingEdges.size === 0;
856
+ const hasOutgoing = node.outgoingEdges.size > 0 || node.externalPackageUsage.size > 0 || node.dynamicImports.size > 0;
857
+
858
+ let isExplicitlyDeclared = isManifestEntry;
859
+ if (!isExplicitlyDeclared) {
860
+ const cleanAbsPath = absPath.replace(/\.[^/.]+$/, "");
861
+ for (const candidate of potentialEntryPoints) {
862
+ if (candidate.replace(/\.[^/.]+$/, "") === cleanAbsPath) {
863
+ isExplicitlyDeclared = true;
864
+ break;
936
865
  }
937
866
  }
938
867
  }
868
+
869
+ const usesItsImports = node.instantiatedIdentifiers && node.instantiatedIdentifiers.size > 0;
870
+ const isLikelyBarrel = !isExplicitlyDeclared && (node.isPureBarrel || node.isBarrel || (!usesItsImports && hasOutgoing));
871
+
872
+ let finalIsEntry = false;
873
+ if (isProtected || isExplicitlyDeclared) {
874
+ finalIsEntry = true;
875
+ } else if (!hasManifestEntries && hasNoIncoming && hasOutgoing && !isLikelyBarrel) {
876
+ finalIsEntry = true;
877
+ }
878
+
879
+ node.isEntry = finalIsEntry;
880
+ if (finalIsEntry && this.context.options.verbose) {
881
+ console.log(`🎯 [ENTRY POINT CONFIRMED] Wurzel gesichert: ${path.relative(this.context.cwd, absPath)}`);
882
+ }
939
883
  }
940
884
  }
941
885
 
@@ -943,12 +887,9 @@ export class RefactoringEngine {
943
887
  try {
944
888
  const text = await fs.readFile(packageJsonPath, 'utf8');
945
889
  const data = JSON.parse(text);
946
- const prodDeps = Object.keys(data.dependencies || {});
947
- const devDeps = Object.keys(data.devDependencies || {});
948
-
949
890
  this.context.manifestDependencies.set(packageJsonPath, {
950
- dependencies: prodDeps,
951
- devDependencies: devDeps,
891
+ dependencies: Object.keys(data.dependencies || {}),
892
+ devDependencies: Object.keys(data.devDependencies || {}),
952
893
  peerDependencies: Object.keys(data.peerDependencies || {}),
953
894
  optionalDependencies: Object.keys(data.optionalDependencies || {})
954
895
  });
@@ -961,29 +902,6 @@ export class RefactoringEngine {
961
902
  console.log(`⏱️ Analysis Duration: ${summary.executionDuration}`);
962
903
  console.log(`πŸ“‚ Total Files Scanned: ${summary.totalFilesProcessed}`);
963
904
  console.log(`πŸ’Ύ Cache Optimization: ${summary.graphCacheOptimization.ratio} hits`);
964
-
965
- console.log(ansis.bold('\nπŸ” Structural Integrity:'));
966
- const secretCount = (summary.structuralIssuesDetected.hardcodedSecrets || []).length;
967
- if (summary.structuralIssuesDetected.deadFiles.length === 0 &&
968
- summary.structuralIssuesDetected.deadExports.length === 0 &&
969
- summary.structuralIssuesDetected.unusedDependencies.length === 0 &&
970
- secretCount === 0) {
971
- console.log(ansis.green(' βœ… No major structural debt detected.'));
972
- } else {
973
- if (summary.structuralIssuesDetected.deadFiles.length > 0) {
974
- console.log(ansis.red(` ❌ Found ${summary.structuralIssuesDetected.deadFiles.length} orphaned/dead files.`));
975
- }
976
- if (summary.structuralIssuesDetected.deadExports.length > 0) {
977
- console.log(ansis.yellow(` ⚠️ Found ${summary.structuralIssuesDetected.deadExports.length} unused named exports.`));
978
- }
979
- if (summary.structuralIssuesDetected.unusedDependencies.length > 0) {
980
- console.log(ansis.yellow(` πŸ“¦ Found ${summary.structuralIssuesDetected.unusedDependencies.length} unused dependencies.`));
981
- }
982
- if (secretCount > 0) {
983
- console.log(ansis.red(` πŸ” Found ${secretCount} hardcoded secret(s) / credential(s).`));
984
- }
985
- }
986
-
987
905
  console.log(ansis.dim('\n------------------------------------------------------------\n'));
988
906
  }
989
907
 
@@ -991,20 +909,16 @@ export class RefactoringEngine {
991
909
  if (cachedRecord.explicitImports) cachedRecord.explicitImports.forEach(i => node.explicitImports.add(i));
992
910
  if (cachedRecord.dynamicImports) cachedRecord.dynamicImports.forEach(i => node.dynamicImports.add(i));
993
911
  if (cachedRecord.importedSymbols) cachedRecord.importedSymbols.forEach(s => node.importedSymbols.add(s));
994
- if (cachedRecord.internalExports) {
995
- Object.entries(cachedRecord.internalExports).forEach(([k, v]) => node.internalExports.set(k, v));
996
- }
997
- if (cachedRecord.symbolSourceLocations) {
998
- Object.entries(cachedRecord.symbolSourceLocations).forEach(([k, v]) => node.symbolSourceLocations.set(k, v));
999
- }
912
+ if (cachedRecord.internalExports) Object.entries(cachedRecord.internalExports).forEach(([k, v]) => node.internalExports.set(k, v));
913
+ if (cachedRecord.symbolSourceLocations) Object.entries(cachedRecord.symbolSourceLocations).forEach(([k, v]) => node.symbolSourceLocations.set(k, v));
1000
914
  if (cachedRecord.externalPackageUsage) cachedRecord.externalPackageUsage.forEach(p => node.externalPackageUsage.add(p));
1001
915
  if (cachedRecord.rawStringReferences) cachedRecord.rawStringReferences.forEach(r => node.rawStringReferences.add(r));
1002
916
  if (cachedRecord.instantiatedIdentifiers) cachedRecord.instantiatedIdentifiers.forEach(id => node.instantiatedIdentifiers.add(id));
1003
917
  if (cachedRecord.propertyAccessChains) cachedRecord.propertyAccessChains.forEach(c => node.propertyAccessChains.add(c));
1004
918
  if (cachedRecord.localSuppressedRules) cachedRecord.localSuppressedRules.forEach(r => node.localSuppressedRules.add(r));
1005
- if (cachedRecord.calculatedDynamicImports) node.calculatedDynamicImports = cachedRecord.calculatedDynamicImports;
1006
919
  if (cachedRecord.isLibraryEntry !== undefined) node.isLibraryEntry = cachedRecord.isLibraryEntry;
1007
920
  if (cachedRecord.isEntry !== undefined) node.isEntry = cachedRecord.isEntry;
1008
- if (cachedRecord.isFrameworkComponent !== undefined) node.isFrameworkComponent = cachedRecord.isFrameworkComponent;
921
+ if (cachedRecord.calculatedDynamicImports) node.calculatedDynamicImports = cachedRecord.calculatedDynamicImports;
922
+ if (cachedRecord.globImports) node.globImports = cachedRecord.globImports;
1009
923
  }
1010
924
  }