entkapp 4.2.0 → 4.4.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,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
  /**
@@ -96,7 +100,7 @@ export class RefactoringEngine {
96
100
  }
97
101
 
98
102
  // Pass 1: Boot environment contexts and load alias configuration maps
99
-
103
+ await this.oxcAnalyzer.init();
100
104
  await this.pathMapper.loadMappings(this.context.tsconfigFilename);
101
105
 
102
106
  // Always attempt workspace mesh initialization – it will auto-detect workspace
@@ -220,15 +224,85 @@ export class RefactoringEngine {
220
224
 
221
225
  // NEW: Circular Dependency Detection
222
226
  console.log(ansis.dim('šŸ”„ Detecting circular dependencies...'));
223
- const cycles = this.circularDetector.detectCycles(this.context.projectGraph, this.context);
224
- if (cycles.length > 0) {
225
- 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:`));
226
230
  this.circularDetector.formatCycles().forEach(c => console.log(ansis.dim(` • ${c}`)));
227
231
  }
228
232
 
229
233
  // Pass 4b: Report hardcoded secrets
230
- console.log(ansis.dim('šŸ” Scanning for hardcoded secrets...'));
234
+ console.log(ansis.dim("šŸ” Scanning for hardcoded secrets..."));
231
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
+
232
306
  if (allSecrets.length > 0) {
233
307
  const criticalSecrets = allSecrets.filter(s => s.severity === 'CRITICAL');
234
308
  const otherSecrets = allSecrets.filter(s => s.severity !== 'CRITICAL');
@@ -262,11 +336,7 @@ export class RefactoringEngine {
262
336
  if (!this.context.unlistedDependencies) this.context.unlistedDependencies = [];
263
337
 
264
338
  // Simple internal helper to guarantee matching forward slash strings across all platforms
265
- const slashify = (p) => {
266
- if (!p) return p;
267
- if (Array.isArray(p)) p = p[0];
268
- return path.resolve(this.context.cwd, p).replace(/\\/g, '/');
269
- };
339
+ const slashify = (p) => path.resolve(this.context.cwd, p).replace(/\\/g, '/');
270
340
 
271
341
  if (this.context.projectGraph && typeof this.context.projectGraph.entries === 'function') {
272
342
  for (const [filePath, fileNode] of this.context.projectGraph.entries()) {
@@ -275,10 +345,12 @@ export class RefactoringEngine {
275
345
  const cleanFilePath = slashify(filePath);
276
346
 
277
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!
278
350
  if (fileNode.externalPackageUsage) {
279
351
  fileNode.externalPackageUsage.forEach(pkg => {
280
- const relativeToRoot = path.relative(this.context.cwd, filePath).replace(/\\/g, '/');
281
- if (relativeToRoot.startsWith('packages/')) {
352
+ const relativeToRoot = path.relative(this.context.cwd, filePath);
353
+ if (relativeToRoot.startsWith('packages' + path.sep) || relativeToRoot.startsWith('packages/')) {
282
354
  this.context.consumedWorkspacePackages.add(pkg);
283
355
  } else {
284
356
  this.context.consumedRootPackages.add(pkg);
@@ -296,40 +368,60 @@ export class RefactoringEngine {
296
368
  if (!this.context.exportRegistry.has(cleanFilePath)) {
297
369
  this.context.exportRegistry.set(cleanFilePath, new Set());
298
370
  }
299
- exportKeys.forEach(key => {
300
- if (key !== '*') { // Don't track wildcard as a dead export symbol
301
- this.context.exportRegistry.get(cleanFilePath).add(key);
302
- }
303
- });
371
+ exportKeys.forEach(key => this.context.exportRegistry.get(cleanFilePath).add(key));
304
372
  }
305
373
  }
306
374
 
307
375
  // 2. Gather cross-file usage tokens using unified slashes
308
- if (fileNode.importedSymbols) {
309
- 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) {
310
382
  if (typeof symbolToken !== 'string') continue;
311
383
 
312
- 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
+
313
394
  if (splitIndex === -1) continue;
314
395
 
315
396
  const specifier = symbolToken.slice(0, splitIndex);
316
397
  const symbolName = symbolToken.slice(splitIndex + 1);
317
398
 
318
399
  let targetFile = null;
319
- // If specifier is already an absolute path (resolved during linkDependencyGraph)
320
- if (path.isAbsolute(specifier)) {
321
- targetFile = specifier;
322
- } else if (this.workspaceGraph && this.workspaceGraph.isLocalWorkspaceSpecifier(specifier)) {
400
+ if (this.workspaceGraph && typeof this.workspaceGraph.isLocalWorkspaceSpecifier === 'function' && this.workspaceGraph.isLocalWorkspaceSpecifier(specifier)) {
323
401
  const match = this.workspaceGraph.getWorkspacePackageMatch(specifier);
324
402
  if (match && match.entryPoints && match.entryPoints.length > 0) {
325
- targetFile = match.entryPoints[0];
403
+ targetFile = Array.isArray(match.entryPoints) ? match.entryPoints : match.entryPoints;
326
404
  }
327
405
  } else if (specifier.startsWith('.')) {
328
- 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
+ }
329
420
  }
330
421
 
331
422
  if (targetFile) {
332
- const cleanTargetFile = slashify(targetFile);
423
+ // Enforce uniform forward slash formats on targets
424
+ const cleanTargetFile = Array.isArray(targetFile) ? slashify(targetFile[0]) : slashify(targetFile);
333
425
  this.context.importUsageRegistry.add(`${cleanTargetFile}:${symbolName}`);
334
426
  }
335
427
  }
@@ -410,6 +502,9 @@ export class RefactoringEngine {
410
502
  if (this.context?.options?.debug || this.context?.options?.verbose) {
411
503
  console.log('\nšŸ” [DEBUG METRICS] Evaluating Analyzer State Matrix:');
412
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
+ }
413
508
  console.log(` • Fast Mode execution flag state: ${!!this.context?.options?.fastMode}`);
414
509
  console.log(` • Total files logged in exportRegistry: ${this.context?.exportRegistry ? this.context.exportRegistry.size : 0}`);
415
510
  console.log(` • Total tracking tokens inside importUsageRegistry: ${this.context?.importUsageRegistry ? this.context.importUsageRegistry.size : 0}`);
@@ -469,42 +564,28 @@ export class RefactoringEngine {
469
564
  }
470
565
  if (isPackageEntryPoint) continue;
471
566
 
472
- const originalNode = this.context.projectGraph.get(cleanExportedFile);
473
- const unusedExportsInThisFile = [];
474
-
475
567
  for (const symbol of exportsSet) {
476
568
  const consumptionToken = `${cleanExportedFile}:${symbol}`;
477
569
  if (!this.context.importUsageRegistry?.has(consumptionToken)) {
478
- // Retrieve the real source location if available
479
- const loc = (originalNode && originalNode.symbolSourceLocations) ? originalNode.symbolSourceLocations.get(symbol) || { line: 0 } : { line: 0 };
480
- unusedExportsInThisFile.push({
570
+ analysisSummary.deadExports.push({
481
571
  symbol: symbol,
482
572
  file: relativeExportedFile,
483
- line: loc.line
573
+ line: 7
484
574
  });
485
575
  }
486
576
  }
487
-
488
- // --- NEW: Orphaned File Detection based on Export Coverage ---
489
- // If every single export in this file is unused, and it's not an entry point,
490
- // we suggest deleting the entire file instead of pruning individual exports.
491
- if (unusedExportsInThisFile.length > 0 && unusedExportsInThisFile.length === exportsSet.size) {
492
- if (!analysisSummary.orphanedFiles.includes(relativeExportedFile)) {
493
- analysisSummary.orphanedFiles.push(relativeExportedFile);
494
- }
495
- } else {
496
- // Otherwise, just append the individual dead exports as usual
497
- analysisSummary.deadExports.push(...unusedExportsInThisFile);
498
- }
499
577
  }
500
578
  }
501
579
  analysisSummary.unlistedDependencies = this.context.unlistedDependencies || [];
502
580
 
581
+ const cycles = cyclesResult; // Use the already detected cycles
582
+
503
583
  const structuralModificationsStaged =
504
584
  analysisSummary.orphanedFiles.length > 0 ||
505
585
  analysisSummary.deadExports.length > 0 ||
506
586
  analysisSummary.unusedDependencies.length > 0 ||
507
- analysisSummary.unlistedDependencies.length > 0;
587
+ analysisSummary.unlistedDependencies.length > 0 ||
588
+ (cycles && cycles.length > 0);
508
589
 
509
590
  // Pass 6: Display Optimization Plan and Run Automated Structural Healing
510
591
  if (structuralModificationsStaged) {
@@ -536,6 +617,20 @@ export class RefactoringEngine {
536
617
  });
537
618
  }
538
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
+
539
634
  console.log(ansis.dim('------------------------------------------------------------'));
540
635
 
541
636
  if (this.context.options.fix) {
@@ -606,52 +701,34 @@ export class RefactoringEngine {
606
701
  }
607
702
 
608
703
  async linkDependencyGraph() {
609
- // Pass 1: Global Entry-Point Protection (Seeds)
610
- // Mark all package entry points and explicit config entry points as library entries first.
611
- const seeds = new Set();
612
-
613
- // Add workspace entry points
614
- if (this.workspaceGraph && this.workspaceGraph.packageManifests) {
615
- for (const pkg of this.workspaceGraph.packageManifests.values()) {
616
- if (pkg.entryPoints) {
617
- pkg.entryPoints.forEach(ep => seeds.add(path.resolve(ep)));
618
- }
619
- }
620
- }
621
-
622
- // Add explicit config entry points
623
- if (this.context.entryPoints) {
624
- this.context.entryPoints.forEach(ep => seeds.add(path.resolve(this.context.cwd, ep)));
625
- }
626
-
627
- for (const seedPath of seeds) {
628
- if (this.context.projectGraph.has(seedPath)) {
629
- this.context.projectGraph.get(seedPath).isLibraryEntry = true;
630
- }
631
- }
632
-
633
- // Pass 2: Edge Linking
634
704
  for (const [filePath, node] of this.context.projectGraph.entries()) {
635
- // A. Explicit Imports (Files)
705
+ // Pass A: Link all explicit imports (static + dynamic + re-export sources)
636
706
  for (const specifier of node.explicitImports) {
637
707
  const resolvedPath = this.resolver.resolveModulePath(filePath, specifier);
638
708
  if (resolvedPath && this.context.projectGraph.has(resolvedPath)) {
639
- const targetNode = this.context.projectGraph.get(resolvedPath);
640
- targetNode.incomingEdges.add(filePath);
709
+ this.context.projectGraph.get(resolvedPath).incomingEdges.add(filePath);
641
710
  node.outgoingEdges.add(resolvedPath);
642
711
 
643
- // Re-export protection: If this file re-exports everything from another file,
644
- // the target file should be treated as an entry point for its own exports.
645
- const isReExportAll = Array.from(node.internalExports.values()).some(exp =>
646
- (exp.type === "re-export-all" || exp.type === "re-export-namespace") && exp.source === specifier
647
- );
648
- if (isReExportAll) {
649
- 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;
650
727
  }
651
728
  }
652
729
  }
653
730
 
654
- // B. Symbol-level Linking (Tracing through barrels)
731
+ // Pass B: Link named-symbol imports through barrel/re-export chains
655
732
  for (const specToken of node.importedSymbols) {
656
733
  const delimiterIndex = specToken.indexOf(':');
657
734
  if (delimiterIndex === -1) continue;
@@ -662,18 +739,18 @@ export class RefactoringEngine {
662
739
  if (!resolvedPath) continue;
663
740
 
664
741
  if (symbol === '*') {
742
+ // Wildcard import / re-export-all: add a direct edge to the resolved file.
665
743
  if (this.context.projectGraph.has(resolvedPath)) {
666
744
  this.context.projectGraph.get(resolvedPath).incomingEdges.add(filePath);
667
745
  node.outgoingEdges.add(resolvedPath);
668
746
  }
669
747
  } else {
748
+ // Named import: trace through barrel files to the actual declaration origin.
670
749
  const traceResolution = await this.barrelParser.determineSymbolDeclarationOrigin(resolvedPath, symbol, this.context.projectGraph);
671
750
  if (traceResolution && this.context.projectGraph.has(traceResolution.originFile)) {
672
- const originNode = this.context.projectGraph.get(traceResolution.originFile);
673
- originNode.incomingEdges.add(filePath);
751
+ this.context.projectGraph.get(traceResolution.originFile).incomingEdges.add(filePath);
674
752
  node.outgoingEdges.add(traceResolution.originFile);
675
- // Register the actual resolved symbol for dead export detection
676
- node.importedSymbols.add(`${traceResolution.originFile}:${traceResolution.originSymbol}`);
753
+ node.importedSymbols.add(`${traceResolution.originFile}:${traceResolution.symbolName}`);
677
754
  }
678
755
  }
679
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
+ }