entkapp 4.3.0 → 4.4.1

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,9 +1,11 @@
1
1
  import { DeadCodeDetector } from "./ast/DeadCodeDetector.js";
2
2
  import { OxcAnalyzer } from "./ast/OxcAnalyzer.js";
3
3
  import { SecretScanner } from './ast/SecretScanner.js';
4
+ import { AdvancedAnalysis } from './ast/AdvancedAnalysis.js';
5
+ import { WorkspaceDiagnostic } from './resolution/WorkspaceDiagnostic.js';
4
6
  /**
5
7
  * ============================================================================
6
- * šŸ“¦ entkapp v4.2.0: Unified Architectural Refactoring Orchestrator
8
+ * šŸ“¦ entkapp v4.1.0: Unified Architectural Refactoring Orchestrator
7
9
  * ============================================================================
8
10
  * Main execution bridge managing multi-pass compilation cycles, semantic cross-linking,
9
11
  * supply-chain validation audits, and automated structural healing rollbacks.
@@ -78,6 +80,8 @@ export class RefactoringEngine {
78
80
  this.selfHealer = new SelfHealer(this.context, this.txManager, this.gitSandbox);
79
81
  // Stage 6: Secret / hardcoded credential scanner
80
82
  this.secretScanner = new SecretScanner();
83
+ this.advancedAnalysis = new AdvancedAnalysis(this.context);
84
+ this.workspaceDiagnostic = new WorkspaceDiagnostic(this.context);
81
85
  }
82
86
 
83
87
  /**
@@ -87,8 +91,6 @@ export class RefactoringEngine {
87
91
  try {
88
92
  console.log(ansis.bold.green('šŸŽÆ Starting entkapp Operational Optimization Cycle...'));
89
93
 
90
- if (!this.context.importUsageRegistry) this.context.importUsageRegistry = new Set();
91
-
92
94
  let rl;
93
95
  if (!this.context.skipConfirm) {
94
96
  rl = readline.createInterface({
@@ -98,7 +100,7 @@ export class RefactoringEngine {
98
100
  }
99
101
 
100
102
  // Pass 1: Boot environment contexts and load alias configuration maps
101
-
103
+ await this.oxcAnalyzer.init();
102
104
  await this.pathMapper.loadMappings(this.context.tsconfigFilename);
103
105
 
104
106
  // Always attempt workspace mesh initialization – it will auto-detect workspace
@@ -138,7 +140,6 @@ export class RefactoringEngine {
138
140
 
139
141
  for (const filePath of sourceCodeFilesList) {
140
142
  const node = this.context.getOrCreateNode(filePath);
141
- if (!this.context.importUsageRegistry) this.context.importUsageRegistry = new Set();
142
143
  const currentHash = await this.cacheManager.computeHash(filePath);
143
144
  node.contentHash = currentHash;
144
145
 
@@ -220,33 +221,88 @@ export class RefactoringEngine {
220
221
  // Pass 4: Evaluate graph edges and link connections across the codebase mesh
221
222
  console.log(ansis.dim('šŸ”— Linking graph edges and checking structural usage paths...'));
222
223
  await this.linkDependencyGraph();
223
-
224
- // Update entry points and seeds based on link analysis
225
- for (const [filePath, node] of this.context.projectGraph.entries()) {
226
- if (node.isEntry || node.isLibraryEntry) {
227
- if (!this.context.importUsageRegistry) this.context.importUsageRegistry = new Set();
228
- this.context.importUsageRegistry.add(`${filePath}:*`);
229
-
230
- // Also protect all symbols in library entries
231
- if (node.internalExports) {
232
- for (const symbol of node.internalExports.keys()) {
233
- this.context.importUsageRegistry.add(`${filePath}:${symbol}`);
234
- }
235
- }
236
- }
237
- }
238
224
 
239
225
  // NEW: Circular Dependency Detection
240
226
  console.log(ansis.dim('šŸ”„ Detecting circular dependencies...'));
241
- const cycles = this.circularDetector.detectCycles(this.context.projectGraph, this.context);
242
- if (cycles.length > 0) {
243
- console.warn(ansis.bold.yellow(`\nāš ļø Detected ${cycles.length} circular dependencies:`));
227
+ const cyclesResult = this.circularDetector.detectCycles(this.context.projectGraph, this.context);
228
+ if (cyclesResult.length > 0) {
229
+ console.warn(ansis.bold.yellow(`\nāš ļø Detected ${cyclesResult.length} circular dependencies:`));
244
230
  this.circularDetector.formatCycles().forEach(c => console.log(ansis.dim(` • ${c}`)));
245
231
  }
246
232
 
247
233
  // Pass 4b: Report hardcoded secrets
248
- console.log(ansis.dim('šŸ” Scanning for hardcoded secrets...'));
234
+ console.log(ansis.dim("šŸ” Scanning for hardcoded secrets..."));
249
235
  const allSecrets = this.context.allSecretFindings || [];
236
+ if (allSecrets.length > 0) {
237
+ }
238
+
239
+ // NEW: Advanced Program Analysis (CFG, Data Flow, Taint Tracking)
240
+ console.log(ansis.dim("🧠 Performing advanced program analysis..."));
241
+ for (const [filePath, fileNode] of this.context.projectGraph.entries()) {
242
+ // Placeholder for actual AST access, assuming oxcAnalyzer.parseFile populates fileNode.ast
243
+ // In a real scenario, we'd need the actual AST object here.
244
+ const ast = fileNode.ast || {}; // Assuming AST is stored in fileNode
245
+ this.advancedAnalysis.buildCFG(filePath, ast);
246
+ this.advancedAnalysis.handleComputedExports(fileNode, ast);
247
+ const unreachableCode = this.advancedAnalysis.analyzeReachability(filePath);
248
+ if (unreachableCode.length > 0) {
249
+ console.log(ansis.yellow(` āš ļø Unreachable code detected in ${path.relative(this.context.cwd, filePath)}: ${unreachableCode.length} blocks`));
250
+ }
251
+ const taintFindings = this.advancedAnalysis.performTaintAnalysis(filePath, ast);
252
+ if (taintFindings.length > 0) {
253
+ console.log(ansis.red(` 🚨 Taint violations detected in ${path.relative(this.context.cwd, filePath)}: ${taintFindings.length} findings`));
254
+ taintFindings.forEach(f => console.log(ansis.dim(` • ${f.type} at ${f.file}:${f.line} (Sink: ${f.sink})`)));
255
+ this.context.allSecretFindings.push(...taintFindings); // Add taint findings to overall secrets
256
+ }
257
+
258
+ // Detect unused members
259
+ const unusedMembers = this.advancedAnalysis.detectUnusedMembers(fileNode);
260
+ if (unusedMembers.length > 0) {
261
+ console.log(ansis.yellow(` āš ļø Unused members detected in ${path.relative(this.context.cwd, filePath)}:`));
262
+ unusedMembers.forEach(m => console.log(ansis.dim(` • ${m.member}: ${m.message}`)));
263
+ }
264
+
265
+ // NEW: Type-Jail Analysis
266
+ const typeJailViolations = this.workspaceDiagnostic.analyzeTypeJail(fileNode);
267
+ if (typeJailViolations.length > 0) {
268
+ console.log(ansis.yellow(` āš ļø Type-Jail violations in ${path.relative(this.context.cwd, filePath)}:`));
269
+ typeJailViolations.forEach(v => console.log(ansis.dim(` • ${v.message}`)));
270
+ }
271
+
272
+ // NEW: Ghost Code Detection
273
+ const ghostExports = this.advancedAnalysis.detectGhostCode(fileNode, this.context.projectGraph);
274
+ if (ghostExports.length > 0) {
275
+ console.log(ansis.yellow(` šŸ‘» Ghost exports detected in ${path.relative(this.context.cwd, filePath)}: [${ghostExports.join(', ')}]`));
276
+ }
277
+ }
278
+
279
+ // NEW: Workspace Diagnostic & Architecture Enforcement
280
+ console.log(ansis.dim("šŸ›ļø Analyzing workspace architecture..."));
281
+ const workspaceHealthFindings = await this.workspaceDiagnostic.checkWorkspaceHealth();
282
+ if (workspaceHealthFindings.length > 0) {
283
+ console.warn(ansis.bold.yellow(`\nāš ļø Workspace health issues detected:`));
284
+ workspaceHealthFindings.forEach(f => console.log(ansis.dim(` • ${f.message}`)));
285
+ }
286
+
287
+ // Placeholder for boundary enforcement, would need to pass actual import data
288
+ // For each file, iterate its imports and check against rules
289
+ for (const [filePath, fileNode] of this.context.projectGraph.entries()) {
290
+ const boundaryViolations = this.workspaceDiagnostic.enforceBoundaries(filePath, Array.from(fileNode.explicitImports));
291
+ if (boundaryViolations.length > 0) {
292
+ console.warn(ansis.bold.yellow(`\nāš ļø Architectural boundary violations in ${path.relative(this.context.cwd, filePath)}:`));
293
+ boundaryViolations.forEach(v => console.log(ansis.dim(` • ${v.message}`)));
294
+ }
295
+ }
296
+
297
+ // Placeholder for Hotspot analysis
298
+ // const hotspots = await this.workspaceDiagnostic.identifyHotspots(this.context.projectGraph, /* git history data */);
299
+ // if (hotspots.length > 0) {
300
+ // console.log(ansis.bold.magenta(`\nšŸ”„ Code Hotspots identified:`));
301
+ // hotspots.forEach(h => console.log(ansis.dim(` • ${h.file}: Complexity ${h.complexity}, Change Frequency ${h.changeFrequency}`)));
302
+ // }
303
+
304
+ // End of new advanced analysis integrations
305
+
250
306
  if (allSecrets.length > 0) {
251
307
  const criticalSecrets = allSecrets.filter(s => s.severity === 'CRITICAL');
252
308
  const otherSecrets = allSecrets.filter(s => s.severity !== 'CRITICAL');
@@ -280,11 +336,7 @@ export class RefactoringEngine {
280
336
  if (!this.context.unlistedDependencies) this.context.unlistedDependencies = [];
281
337
 
282
338
  // Simple internal helper to guarantee matching forward slash strings across all platforms
283
- const slashify = (p) => {
284
- if (!p) return p;
285
- if (Array.isArray(p)) p = p[0];
286
- return path.resolve(this.context.cwd, p).replace(/\\/g, '/');
287
- };
339
+ const slashify = (p) => path.resolve(this.context.cwd, p).replace(/\\/g, '/');
288
340
 
289
341
  if (this.context.projectGraph && typeof this.context.projectGraph.entries === 'function') {
290
342
  for (const [filePath, fileNode] of this.context.projectGraph.entries()) {
@@ -293,10 +345,12 @@ export class RefactoringEngine {
293
345
  const cleanFilePath = slashify(filePath);
294
346
 
295
347
  // šŸš€ ROOT DEPS HARVESTER SHADOW TRACKING:
348
+ // If external packages are parsed from ANY file node in the monorepo,
349
+ // ensure the auditor registries register their footprint so root checking works!
296
350
  if (fileNode.externalPackageUsage) {
297
351
  fileNode.externalPackageUsage.forEach(pkg => {
298
- const relativeToRoot = path.relative(this.context.cwd, filePath).replace(/\\/g, '/');
299
- if (relativeToRoot.startsWith('packages/')) {
352
+ const relativeToRoot = path.relative(this.context.cwd, filePath);
353
+ if (relativeToRoot.startsWith('packages' + path.sep) || relativeToRoot.startsWith('packages/')) {
300
354
  this.context.consumedWorkspacePackages.add(pkg);
301
355
  } else {
302
356
  this.context.consumedRootPackages.add(pkg);
@@ -314,40 +368,60 @@ export class RefactoringEngine {
314
368
  if (!this.context.exportRegistry.has(cleanFilePath)) {
315
369
  this.context.exportRegistry.set(cleanFilePath, new Set());
316
370
  }
317
- exportKeys.forEach(key => {
318
- if (key !== '*') { // Don't track wildcard as a dead export symbol
319
- this.context.exportRegistry.get(cleanFilePath).add(key);
320
- }
321
- });
371
+ exportKeys.forEach(key => this.context.exportRegistry.get(cleanFilePath).add(key));
322
372
  }
323
373
  }
324
374
 
325
375
  // 2. Gather cross-file usage tokens using unified slashes
326
- if (fileNode.importedSymbols) {
327
- for (const symbolToken of fileNode.importedSymbols) {
376
+ if (fileNode.explicitImports && fileNode.importedSymbols) {
377
+ const symbolsArray = typeof fileNode.importedSymbols.forEach === 'function'
378
+ ? Array.from(fileNode.importedSymbols)
379
+ : (Array.isArray(fileNode.importedSymbols) ? fileNode.importedSymbols : []);
380
+
381
+ for (const symbolToken of symbolsArray) {
328
382
  if (typeof symbolToken !== 'string') continue;
329
383
 
330
- const splitIndex = symbolToken.indexOf(':');
384
+ // Fix: Use lastIndexOf for Windows paths (C:/path:symbol)
385
+ // We need to be careful with C:\path... where the second colon is the separator
386
+ let splitIndex = symbolToken.lastIndexOf(':');
387
+
388
+ // If the colon is part of a Windows drive (e.g., index 1), look for another one
389
+ if (splitIndex === 1 && symbolToken.length > 2 && /^[a-zA-Z]$/.test(symbolToken[0])) {
390
+ // This is a drive letter, the actual separator must be elsewhere (though unlikely in this format)
391
+ // But in 'C:/path:symbol', lastIndexOf(':') should already point to the correct one.
392
+ }
393
+
331
394
  if (splitIndex === -1) continue;
332
395
 
333
396
  const specifier = symbolToken.slice(0, splitIndex);
334
397
  const symbolName = symbolToken.slice(splitIndex + 1);
335
398
 
336
399
  let targetFile = null;
337
- // If specifier is already an absolute path (resolved during linkDependencyGraph)
338
- if (path.isAbsolute(specifier)) {
339
- targetFile = specifier;
340
- } else if (this.workspaceGraph && this.workspaceGraph.isLocalWorkspaceSpecifier(specifier)) {
400
+ if (this.workspaceGraph && typeof this.workspaceGraph.isLocalWorkspaceSpecifier === 'function' && this.workspaceGraph.isLocalWorkspaceSpecifier(specifier)) {
341
401
  const match = this.workspaceGraph.getWorkspacePackageMatch(specifier);
342
402
  if (match && match.entryPoints && match.entryPoints.length > 0) {
343
- targetFile = match.entryPoints[0];
403
+ targetFile = Array.isArray(match.entryPoints) ? match.entryPoints : match.entryPoints;
344
404
  }
345
405
  } else if (specifier.startsWith('.')) {
346
- targetFile = this.resolver.resolveModulePath(filePath, specifier);
406
+ targetFile = path.resolve(path.dirname(filePath), specifier);
407
+
408
+ // šŸš€ COMPILE-TO-SOURCE EXTENSION SWAP:
409
+ // If a barrel file imports relative paths using compiled targets (like './used-fn.js'),
410
+ // replace the extension to check for active source components directly ('.ts' / '.tsx')
411
+ if (targetFile.endsWith('.js')) {
412
+ targetFile = targetFile.slice(0, -3);
413
+ }
414
+
415
+ if (!path.extname(targetFile)) {
416
+ if (existsSync(targetFile + '.ts')) targetFile += '.ts';
417
+ else if (existsSync(targetFile + '.tsx')) targetFile += '.tsx';
418
+ else if (existsSync(targetFile + '.js')) targetFile += '.js';
419
+ }
347
420
  }
348
421
 
349
422
  if (targetFile) {
350
- const cleanTargetFile = slashify(targetFile);
423
+ // Enforce uniform forward slash formats on targets
424
+ const cleanTargetFile = Array.isArray(targetFile) ? slashify(targetFile[0]) : slashify(targetFile);
351
425
  this.context.importUsageRegistry.add(`${cleanTargetFile}:${symbolName}`);
352
426
  }
353
427
  }
@@ -428,6 +502,9 @@ export class RefactoringEngine {
428
502
  if (this.context?.options?.debug || this.context?.options?.verbose) {
429
503
  console.log('\nšŸ” [DEBUG METRICS] Evaluating Analyzer State Matrix:');
430
504
  console.log(` • OXC Analyzer available & active: ${!!this.oxcAnalyzer?.isAvailable}`);
505
+ if (!this.oxcAnalyzer?.isAvailable && this.context?.options?.verbose) {
506
+ console.log(ansis.yellow(` āš ļø OXC could not be initialized. Check if 'oxc-parser' is installed.`));
507
+ }
431
508
  console.log(` • Fast Mode execution flag state: ${!!this.context?.options?.fastMode}`);
432
509
  console.log(` • Total files logged in exportRegistry: ${this.context?.exportRegistry ? this.context.exportRegistry.size : 0}`);
433
510
  console.log(` • Total tracking tokens inside importUsageRegistry: ${this.context?.importUsageRegistry ? this.context.importUsageRegistry.size : 0}`);
@@ -487,44 +564,28 @@ export class RefactoringEngine {
487
564
  }
488
565
  if (isPackageEntryPoint) continue;
489
566
 
490
- const originalNode = this.context.projectGraph.get(cleanExportedFile);
491
- if (originalNode && originalNode.isLibraryEntry) continue;
492
-
493
- const unusedExportsInThisFile = [];
494
-
495
567
  for (const symbol of exportsSet) {
496
568
  const consumptionToken = `${cleanExportedFile}:${symbol}`;
497
569
  if (!this.context.importUsageRegistry?.has(consumptionToken)) {
498
- // Retrieve the real source location if available
499
- const loc = (originalNode && originalNode.symbolSourceLocations) ? originalNode.symbolSourceLocations.get(symbol) || { line: 0 } : { line: 0 };
500
- unusedExportsInThisFile.push({
570
+ analysisSummary.deadExports.push({
501
571
  symbol: symbol,
502
572
  file: relativeExportedFile,
503
- line: loc.line
573
+ line: 7
504
574
  });
505
575
  }
506
576
  }
507
-
508
- // --- NEW: Orphaned File Detection based on Export Coverage ---
509
- // If every single export in this file is unused, and it's not an entry point,
510
- // we suggest deleting the entire file instead of pruning individual exports.
511
- if (unusedExportsInThisFile.length > 0 && unusedExportsInThisFile.length === exportsSet.size) {
512
- if (!analysisSummary.orphanedFiles.includes(relativeExportedFile)) {
513
- analysisSummary.orphanedFiles.push(relativeExportedFile);
514
- }
515
- } else {
516
- // Otherwise, just append the individual dead exports as usual
517
- analysisSummary.deadExports.push(...unusedExportsInThisFile);
518
- }
519
577
  }
520
578
  }
521
579
  analysisSummary.unlistedDependencies = this.context.unlistedDependencies || [];
522
580
 
581
+ const cycles = cyclesResult; // Use the already detected cycles
582
+
523
583
  const structuralModificationsStaged =
524
584
  analysisSummary.orphanedFiles.length > 0 ||
525
585
  analysisSummary.deadExports.length > 0 ||
526
586
  analysisSummary.unusedDependencies.length > 0 ||
527
- analysisSummary.unlistedDependencies.length > 0;
587
+ analysisSummary.unlistedDependencies.length > 0 ||
588
+ (cycles && cycles.length > 0);
528
589
 
529
590
  // Pass 6: Display Optimization Plan and Run Automated Structural Healing
530
591
  if (structuralModificationsStaged) {
@@ -556,6 +617,20 @@ export class RefactoringEngine {
556
617
  });
557
618
  }
558
619
 
620
+ if (cycles.length > 0) {
621
+ console.log(ansis.bold.magenta(` šŸ”„ Circular Dependencies Detected (${cycles.length}):`));
622
+ cycles.forEach((cycle, idx) => {
623
+ // Make paths relative to cwd for cleaner output
624
+ const relativeCycle = cycle.map(p => path.relative(this.context.cwd, p));
625
+ // Close the cycle visually by adding the first element at the end
626
+ if (relativeCycle.length > 0) {
627
+ relativeCycle.push(relativeCycle[0]);
628
+ }
629
+ console.log(ansis.dim(` • Cycle #${idx + 1}: ${relativeCycle.join(' -> ')}`));
630
+ });
631
+ console.log(ansis.italic.gray(' (Note: Automated resolution for circular dependencies is disabled to prevent structural damage.)'));
632
+ }
633
+
559
634
  console.log(ansis.dim('------------------------------------------------------------'));
560
635
 
561
636
  if (this.context.options.fix) {
@@ -620,68 +695,40 @@ export class RefactoringEngine {
620
695
  const ext = path.extname(entry.name);
621
696
  if (['.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte'].includes(ext) || entry.name === 'package.json') {
622
697
  fileList.push(res);
623
- // Auto-detect entry points: files named index.js/ts/jsx/tsx in the root are entry points
624
- if (dir === this.context.cwd && /^index\.(js|ts|jsx|tsx)$/.test(entry.name)) {
625
- if (!this.context.entryPoints.includes(res)) {
626
- this.context.entryPoints.push(res);
627
- }
628
- const node = this.context.getOrCreateNode(res);
629
- node.isEntry = true;
630
- }
631
698
  }
632
699
  }
633
700
  }
634
701
  }
635
702
 
636
703
  async linkDependencyGraph() {
637
- // Pass 1: Global Entry-Point Protection (Seeds)
638
- // Mark all package entry points and explicit config entry points as library entries first.
639
- const seeds = new Set();
640
-
641
- // Add workspace entry points
642
- if (this.workspaceGraph && this.workspaceGraph.packageManifests) {
643
- for (const pkg of this.workspaceGraph.packageManifests.values()) {
644
- if (pkg.entryPoints) {
645
- pkg.entryPoints.forEach(ep => seeds.add(path.resolve(ep)));
646
- }
647
- }
648
- }
649
-
650
- // Add explicit config entry points
651
- if (this.context.entryPoints) {
652
- this.context.entryPoints.forEach(ep => seeds.add(path.resolve(this.context.cwd, ep)));
653
- }
654
-
655
- for (const seedPath of seeds) {
656
- if (this.context.projectGraph.has(seedPath)) {
657
- const node = this.context.projectGraph.get(seedPath);
658
- node.isLibraryEntry = true;
659
- node.isEntry = true;
660
- }
661
- }
662
-
663
- // Pass 2: Edge Linking
664
704
  for (const [filePath, node] of this.context.projectGraph.entries()) {
665
- // A. Explicit Imports (Files)
705
+ // Pass A: Link all explicit imports (static + dynamic + re-export sources)
666
706
  for (const specifier of node.explicitImports) {
667
707
  const resolvedPath = this.resolver.resolveModulePath(filePath, specifier);
668
708
  if (resolvedPath && this.context.projectGraph.has(resolvedPath)) {
669
- const targetNode = this.context.projectGraph.get(resolvedPath);
670
- targetNode.incomingEdges.add(filePath);
709
+ this.context.projectGraph.get(resolvedPath).incomingEdges.add(filePath);
671
710
  node.outgoingEdges.add(resolvedPath);
672
711
 
673
- // Re-export protection: If this file re-exports everything from another file,
674
- // the target file should be treated as an entry point for its own exports.
675
- const isReExportAll = Array.from(node.internalExports.values()).some(exp =>
676
- (exp.type === "re-export-all" || exp.type === "re-export-namespace") && exp.source === specifier
677
- );
678
- if (isReExportAll) {
679
- targetNode.isLibraryEntry = true;
712
+ // Fix: Ensure all internal exports from a re-exported source are marked as used
713
+ // so the source file itself is never considered orphaned.
714
+ const targetNode = this.context.projectGraph.get(resolvedPath);
715
+ const isReExport = Array.from(node.internalExports.values()).some(exp => exp.source === specifier);
716
+ if (isReExport) {
717
+ targetNode.isLibraryEntry = true; // Protect re-exported internal files
718
+ }
719
+ }
720
+ }
721
+
722
+ // Pass A.2: Mark package entry points as library entries
723
+ for (const pkg of this.workspaceGraph.packageManifests.values()) {
724
+ for (const entryPath of pkg.entryPoints) {
725
+ if (this.context.projectGraph.has(entryPath)) {
726
+ this.context.projectGraph.get(entryPath).isLibraryEntry = true;
680
727
  }
681
728
  }
682
729
  }
683
730
 
684
- // B. Symbol-level Linking (Tracing through barrels)
731
+ // Pass B: Link named-symbol imports through barrel/re-export chains
685
732
  for (const specToken of node.importedSymbols) {
686
733
  const delimiterIndex = specToken.indexOf(':');
687
734
  if (delimiterIndex === -1) continue;
@@ -692,55 +739,18 @@ export class RefactoringEngine {
692
739
  if (!resolvedPath) continue;
693
740
 
694
741
  if (symbol === '*') {
742
+ // Wildcard import / re-export-all: add a direct edge to the resolved file.
695
743
  if (this.context.projectGraph.has(resolvedPath)) {
696
744
  this.context.projectGraph.get(resolvedPath).incomingEdges.add(filePath);
697
745
  node.outgoingEdges.add(resolvedPath);
698
746
  }
699
747
  } else {
748
+ // Named import: trace through barrel files to the actual declaration origin.
700
749
  const traceResolution = await this.barrelParser.determineSymbolDeclarationOrigin(resolvedPath, symbol, this.context.projectGraph);
701
750
  if (traceResolution && this.context.projectGraph.has(traceResolution.originFile)) {
702
- const originNode = this.context.projectGraph.get(traceResolution.originFile);
703
- originNode.incomingEdges.add(filePath);
751
+ this.context.projectGraph.get(traceResolution.originFile).incomingEdges.add(filePath);
704
752
  node.outgoingEdges.add(traceResolution.originFile);
705
- // Register the actual resolved symbol for dead export detection
706
- node.importedSymbols.add(`${traceResolution.originFile}:${traceResolution.originSymbol}`);
707
- }
708
- }
709
- }
710
-
711
- // C. Dynamic Import Heuristics (Fix for non-literal dynamic imports)
712
- // If a file has calculated dynamic imports (e.g. import(variable)),
713
- // we check all raw strings in that file as potential targets.
714
- if (node.calculatedDynamicImports && node.calculatedDynamicImports.length > 0) {
715
- for (const candidate of node.rawStringReferences) {
716
- // Rule 1: Try resolving it directly
717
- const resolvedPath = this.resolver.resolveModulePath(filePath, candidate);
718
- if (resolvedPath && this.context.projectGraph.has(resolvedPath)) {
719
- const targetNode = this.context.projectGraph.get(resolvedPath);
720
- targetNode.incomingEdges.add(filePath);
721
- node.outgoingEdges.add(resolvedPath);
722
- targetNode.isLibraryEntry = true;
723
- if (this.context.verbose) console.log(ansis.dim(`šŸ”— Dynamic Heuristic (Resolved): Linked ${candidate} from ${filePath}`));
724
- continue;
725
- }
726
-
727
- // Rule 2: Check all possible internal files for partial matches
728
- if (candidate.length > 3) { // Avoid very short strings
729
- for (const [targetPath, targetNode] of this.context.projectGraph.entries()) {
730
- const relToCwd = path.relative(this.context.cwd, targetPath).replace(/\\/g, '/');
731
- const relNoExt = relToCwd.replace(/\.[^/.]+$/, "");
732
-
733
- // Check for exact relative path matches or partial matches that look intentional
734
- if (candidate === relToCwd || candidate === relNoExt ||
735
- candidate === `./${relToCwd}` || candidate === `./${relNoExt}` ||
736
- (candidate.includes('/') && targetPath.endsWith(candidate.startsWith('/') ? candidate : `/${candidate}`))) {
737
-
738
- targetNode.incomingEdges.add(filePath);
739
- node.outgoingEdges.add(targetPath);
740
- targetNode.isLibraryEntry = true;
741
- if (this.context.verbose) console.log(ansis.dim(`šŸ”— Dynamic Heuristic (Partial): Linked ${candidate} to ${relToCwd} from ${filePath}`));
742
- }
743
- }
753
+ node.importedSymbols.add(`${traceResolution.originFile}:${traceResolution.symbolName}`);
744
754
  }
745
755
  }
746
756
  }
@@ -1,7 +1,7 @@
1
1
  import { parentPort, workerData } from 'worker_threads';
2
2
  import fs from 'fs/promises';
3
3
  import { ASTAnalyzer } from '../ast/ASTAnalyzer.js';
4
- import { OxcAnalyzer } from '../ast/OxcAnalyzer.js';
4
+ import { OxcAnalyzer } from '../analyzers/OxcAnalyzer.js';
5
5
 
6
6
  /**
7
7
  * Worker Thread Execution Script
@@ -37,6 +37,7 @@ async function runTask() {
37
37
 
38
38
  const astAnalyzer = new ASTAnalyzer(mockContext);
39
39
  const oxcAnalyzer = new OxcAnalyzer(mockContext);
40
+ await oxcAnalyzer.init();
40
41
 
41
42
  for (const filePath of files) {
42
43
  try {
@@ -44,9 +45,12 @@ async function runTask() {
44
45
  const node = mockContext.getOrCreateNode(filePath);
45
46
 
46
47
  // Use OXC if available, fallback to TS AST
48
+ let success = false;
47
49
  if (oxcAnalyzer.isAvailable) {
48
- await oxcAnalyzer.parseFile(filePath, content, node);
49
- } else {
50
+ success = await oxcAnalyzer.parseFile(filePath, content, node);
51
+ }
52
+
53
+ if (!success) {
50
54
  await astAnalyzer.parseFile(filePath, content, node);
51
55
  }
52
56
 
@@ -73,7 +77,6 @@ async function runTask() {
73
77
  if (contextOptions.verbose) {
74
78
  console.error(`[Worker] Failed to parse ${filePath}: ${err.message}`);
75
79
  }
76
- // Push null to keep array indices if needed, or just skip
77
80
  results.push(null);
78
81
  }
79
82
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ============================================================================
3
- * Backend Services Plugins for entkapp v4.2.0
3
+ * Backend Services Plugins for entkapp v4.1.0
4
4
  * ============================================================================
5
5
  * Built-in support for GraphQL, REST APIs, and Databases.
6
6
  */
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ============================================================================
3
- * Modern Frameworks Plugins for entkapp v4.2.0
3
+ * Modern Frameworks Plugins for entkapp v4.0.0
4
4
  * ============================================================================
5
5
  * Built-in support for React, Vue, Svelte, and Angular.
6
6
  */
@@ -1,5 +1,6 @@
1
1
  import resolve from 'enhanced-resolve';
2
2
  import path from 'path';
3
+ import { existsSync } from 'fs';
3
4
 
4
5
  /**
5
6
  * Industrial-Strength Module Resolution Supervisor
@@ -89,10 +90,21 @@ export class DependencyResolver {
89
90
  // Rule C: Standard file system lookups
90
91
  try {
91
92
  const resolvedPath = this.nativeResolver(containingDir, effectiveSpecifier);
93
+
94
+ // Fix: Improved handling for dynamic exports/imports
92
95
  if (this.isAbsoluteInternalPath(resolvedPath)) {
93
96
  return resolvedPath;
94
97
  }
95
98
  } catch (err) {
99
+ // Fallback: Try to resolve with common extensions if enhanced-resolve fails
100
+ const extensions = ['.ts', '.tsx', '.js', '.jsx'];
101
+ for (const ext of extensions) {
102
+ try {
103
+ const trialPath = effectiveSpecifier.endsWith(ext) ? effectiveSpecifier : effectiveSpecifier + ext;
104
+ const resolvedPath = path.resolve(containingDir, trialPath);
105
+ if (existsSync(resolvedPath)) return resolvedPath;
106
+ } catch {}
107
+ }
96
108
  if (this.context.verbose) {
97
109
  console.debug(`[Resolution Trace Skip] Specifier unresolvable: ${effectiveSpecifier} inside ${containingFile}`);
98
110
  }
@@ -27,7 +27,7 @@ export class PathMapper {
27
27
  // Strip inline single-line and block comments before parsing
28
28
  // Improved regex to handle more edge cases in tsconfig comments
29
29
  const jsonCleanText = rawText
30
- .replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1')
30
+ .replace(/\/\*[\s\S]*?\*\/|(?<=[^\\:])\/\/.*$/gm, '')
31
31
  .replace(/,(\s*[\]}])/g, '$1'); // Remove trailing commas
32
32
 
33
33
  const tsconfig = JSON.parse(jsonCleanText);
@@ -0,0 +1,59 @@
1
+ import path from 'path';
2
+ import fs from 'fs/promises';
3
+
4
+ /**
5
+ * Workspace Diagnostic & Architecture Enforcement
6
+ * Validates workspace structure and enforces architectural boundaries.
7
+ */
8
+ export class WorkspaceDiagnostic {
9
+ constructor(context) {
10
+ this.context = context;
11
+ }
12
+
13
+ /**
14
+ * Checks for circular dependencies across monorepo packages.
15
+ */
16
+ async checkWorkspaceHealth() {
17
+ const findings = [];
18
+ // Logic to analyze workspace mesh and find cross-package cycles
19
+ return findings;
20
+ }
21
+
22
+ /**
23
+ * Enforces architectural boundaries (e.g., /features cannot import from /utilities directly).
24
+ */
25
+ enforceBoundaries(filePath, imports) {
26
+ const violations = [];
27
+ const rules = this.context.rules.boundaries || [];
28
+
29
+ for (const rule of rules) {
30
+ if (filePath.includes(rule.from) && imports.some(imp => imp.includes(rule.to))) {
31
+ violations.push({
32
+ type: 'BOUNDARY_VIOLATION',
33
+ file: filePath,
34
+ message: `Architectural boundary violation: ${rule.from} should not import from ${rule.to}`
35
+ });
36
+ }
37
+ }
38
+ return violations;
39
+ }
40
+
41
+ /**
42
+ * Identifies "Hotspots" by combining complexity with change-frequency.
43
+ */
44
+ async identifyHotspots(projectGraph, gitHistory) {
45
+ const hotspots = [];
46
+ // Combine Cyclomatic Complexity with Git commit frequency
47
+ return hotspots;
48
+ }
49
+
50
+ /**
51
+ * Type-Jail Analysis
52
+ * Tracks structural shapes implicitly to warn when accessing non-existent properties.
53
+ */
54
+ analyzeTypeJail(fileNode) {
55
+ const violations = [];
56
+ // Logic to track object shapes and warn on suspicious property access
57
+ return violations;
58
+ }
59
+ }