entkapp 5.7.0 β†’ 5.7.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # πŸ•ΈοΈ entkapp Ultimate v5.6.1
1
+ # πŸ•ΈοΈ entkapp Ultimate v5.7.1
2
2
 
3
3
  > **The Ultimate Enterprise Codebase Janitor.** High-speed OXC integration, type-aware analysis, and automated structural healing. Fully standalone architectural orchestrator.
4
4
 
package/bin/cli.js CHANGED
@@ -132,7 +132,7 @@ async function bootstrap() {
132
132
  }, timeoutMs);
133
133
  timeoutTimer.unref(); // Allow process to exit if work finishes
134
134
 
135
- console.log(ansis.bold.green(`\nπŸ“¦ entkapp v${packageJsonContent.version || '5.6.0'} Engine Activation`));
135
+ console.log(ansis.bold.green(`\nπŸ“¦ entkapp v${packageJsonContent.version || '5.7.1'} Engine Activation`));
136
136
  console.log(ansis.dim('------------------------------------------------------------'));
137
137
  console.log(`${ansis.bold('Target Workspace Root :')} ${ansis.blue(targetCwd)}`);
138
138
  console.log(`${ansis.bold('Refactoring Mode :')} ${options.fix ? ansis.yellow('Active Fixing & Self-Healing Enabled') : ansis.gray('Dry-Run Reporting Only')}`);
package/bin/cli.mjs CHANGED
@@ -28,7 +28,7 @@ async function bootstrap() {
28
28
  program
29
29
  .name('entkapp')
30
30
  .description(ansis.cyan('The Ultimate Enterprise Codebase Janitor with OXC integration, type-aware analysis, and automated structural healing.'))
31
- .version(packageJsonContent.version || '5.7.0');
31
+ .version(packageJsonContent.version || '5.7.1');
32
32
 
33
33
  program
34
34
  .option('-c, --cwd <path>', 'Specify the execution context root directory', process.cwd())
@@ -123,7 +123,7 @@ async function bootstrap() {
123
123
  }, timeoutMs);
124
124
  timeoutTimer.unref();
125
125
 
126
- console.log(ansis.bold.green(`\nπŸ“¦ entkapp v${packageJsonContent.version || '5.7.0'} Engine Activation`));
126
+ console.log(ansis.bold.green(`\nπŸ“¦ entkapp v${packageJsonContent.version || '5.7.1'} Engine Activation`));
127
127
  console.log(ansis.dim('------------------------------------------------------------'));
128
128
  console.log(`${ansis.bold('Target Workspace Root :')} ${ansis.blue(targetCwd)}`);
129
129
  console.log(`${ansis.bold('Refactoring Mode :')} ${options.fix ? ansis.yellow('Active Fixing & Self-Healing Enabled') : ansis.gray('Dry-Run Reporting Only')}`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "name": "entkapp",
4
- "version": "5.7.0",
4
+ "version": "5.7.1",
5
5
  "description": "The Ultimate Enterprise Codebase Janitor. High-speed OXC integration, type-aware analysis, and automated structural healing. Fully standalone architectural orchestrator.",
6
6
  "type": "module",
7
7
  "main": "index.js",
@@ -214,6 +214,44 @@ export class EngineContext {
214
214
  } catch (e) {}
215
215
  }
216
216
 
217
+ // FIX (Bug 2): Fallback Entry-Point Seeding for standard TypeScript/JavaScript projects.
218
+ // If no entry points have been discovered via node.isEntry flags or manifest fields
219
+ // (e.g. because EntryPointDetector was not wired in the JS build), we fall back to
220
+ // well-known convention paths so that the BFS traversal is never empty and all
221
+ // reachable files – and their dependencies – are correctly included in the analysis.
222
+ if (reachableFiles.size === 0 && this.projectGraph.size > 0) {
223
+ const conventionEntries = [
224
+ 'src/index.ts', 'src/index.tsx', 'src/index.js', 'src/index.jsx',
225
+ 'src/main.ts', 'src/main.js',
226
+ 'index.ts', 'index.tsx', 'index.js',
227
+ 'src/app.ts', 'src/app.js'
228
+ ];
229
+ const normalizeCwd = (p) => {
230
+ let n = path.resolve(this.cwd, p).replace(/\\/g, '/');
231
+ if (/^[a-z]:\//.test(n)) n = n.charAt(0).toUpperCase() + n.slice(1);
232
+ return n;
233
+ };
234
+ for (const rel of conventionEntries) {
235
+ const abs = normalizeCwd(rel);
236
+ if (this.projectGraph.has(abs) && !reachableFiles.has(abs)) {
237
+ reachableFiles.add(abs);
238
+ queue.push(abs);
239
+ this.projectGraph.get(abs).isEntry = true;
240
+ if (this.verbose) console.log(`[Reachability] Fallback entry point seeded: ${rel}`);
241
+ }
242
+ }
243
+ // Last resort: if still empty, treat every graph node that has no incoming edges
244
+ // as a potential root so that the dependency audit is never skipped entirely.
245
+ if (reachableFiles.size === 0) {
246
+ for (const [fp, node] of this.projectGraph.entries()) {
247
+ if (node.incomingEdges.size === 0) {
248
+ reachableFiles.add(fp);
249
+ queue.push(fp);
250
+ }
251
+ }
252
+ }
253
+ }
254
+
217
255
  // 2. BFS Traversal
218
256
  while (queue.length > 0) {
219
257
  const currentPath = queue.shift();
@@ -362,11 +400,33 @@ export class EngineContext {
362
400
 
363
401
  // --- DEPENDENCY ANALYSIS ---
364
402
  const usedByReachableFiles = new Set();
403
+
404
+ // FIX (Bug 3): Helper to extract the base package name from an import specifier.
405
+ // Handles scoped packages (@org/pkg), sub-path imports (pkg/sub), and node: prefix.
406
+ const extractBasePkg = (specifier) => {
407
+ if (!specifier || specifier.startsWith('.') || specifier.startsWith('/')) return null;
408
+ const s = specifier.startsWith('node:') ? specifier.slice(5) : specifier;
409
+ return s.startsWith('@') ? s.split('/').slice(0, 2).join('/') : s.split('/')[0];
410
+ };
411
+
365
412
  reachableFiles.forEach(filePath => {
366
413
  const node = this.projectGraph.get(filePath);
367
414
  if (node) {
415
+ // Primary source: externalPackageUsage populated by ASTAnalyzer / OxcAnalyzer
368
416
  node.externalPackageUsage.forEach(pkg => usedByReachableFiles.add(pkg));
369
-
417
+
418
+ // FIX (Bug 3): Also derive package names from explicitImports.
419
+ // ASTAnalyzer populates node.explicitImports with raw import specifiers
420
+ // (e.g. "lodash", "axios/lib/core") but these are not always mirrored into
421
+ // externalPackageUsage when the AST pass runs in worker threads or when the
422
+ // OXC parser is unavailable. Extracting them here closes the gap.
423
+ if (node.explicitImports) {
424
+ node.explicitImports.forEach(specifier => {
425
+ const pkg = extractBasePkg(specifier);
426
+ if (pkg) usedByReachableFiles.add(pkg);
427
+ });
428
+ }
429
+
370
430
  // Conservative check for dynamic imports in reachable files
371
431
  if (node.calculatedDynamicImports) {
372
432
  node.calculatedDynamicImports.forEach(entry => {
@@ -397,11 +457,21 @@ export class EngineContext {
397
457
  }
398
458
 
399
459
  // UPGRADE: Be more aggressive in detecting unused dependencies.
400
- // Check both externalPackageUsage and rawStringReferences.
460
+ // FIX (Bug 3): Check externalPackageUsage, rawStringReferences AND explicitImports
461
+ // to ensure packages imported via static import statements are never false-positives.
401
462
  const isUsed = usedByReachableFiles.has(dep) ||
402
463
  Array.from(reachableFiles).some(f => {
403
464
  const node = this.projectGraph.get(f);
404
- return node && (node.externalPackageUsage.has(dep) || node.rawStringReferences.has(dep));
465
+ if (!node) return false;
466
+ if (node.externalPackageUsage.has(dep) || node.rawStringReferences.has(dep)) return true;
467
+ // FIX: check explicitImports for base package name match
468
+ if (node.explicitImports) {
469
+ for (const specifier of node.explicitImports) {
470
+ const base = extractBasePkg(specifier);
471
+ if (base === dep) return true;
472
+ }
473
+ }
474
+ return false;
405
475
  });
406
476
 
407
477
  if (!isUsed) {
@@ -218,6 +218,38 @@ export class EngineContext {
218
218
  } catch (e) {}
219
219
  }
220
220
 
221
+ // FIX (Bug 2): Fallback Entry-Point Seeding for standard TypeScript/JavaScript projects.
222
+ if (reachableFiles.size === 0 && this.projectGraph.size > 0) {
223
+ const conventionEntries = [
224
+ 'src/index.ts', 'src/index.tsx', 'src/index.js', 'src/index.jsx',
225
+ 'src/main.ts', 'src/main.js',
226
+ 'index.ts', 'index.tsx', 'index.js',
227
+ 'src/app.ts', 'src/app.js'
228
+ ];
229
+ const normalizeCwd = (p) => {
230
+ let n = path.resolve(this.cwd, p).replace(/\\/g, '/');
231
+ if (/^[a-z]:\//.test(n)) n = n.charAt(0).toUpperCase() + n.slice(1);
232
+ return n;
233
+ };
234
+ for (const rel of conventionEntries) {
235
+ const abs = normalizeCwd(rel);
236
+ if (this.projectGraph.has(abs) && !reachableFiles.has(abs)) {
237
+ reachableFiles.add(abs);
238
+ queue.push(abs);
239
+ this.projectGraph.get(abs).isEntry = true;
240
+ if (this.verbose) console.log(`[Reachability] Fallback entry point seeded: ${rel}`);
241
+ }
242
+ }
243
+ if (reachableFiles.size === 0) {
244
+ for (const [fp, node] of this.projectGraph.entries()) {
245
+ if (node.incomingEdges.size === 0) {
246
+ reachableFiles.add(fp);
247
+ queue.push(fp);
248
+ }
249
+ }
250
+ }
251
+ }
252
+
221
253
  // 2. BFS Traversal
222
254
  while (queue.length > 0) {
223
255
  const currentPath = queue.shift();
@@ -366,10 +398,24 @@ export class EngineContext {
366
398
 
367
399
  // --- DEPENDENCY ANALYSIS ---
368
400
  const usedByReachableFiles = new Set();
401
+
402
+ const extractBasePkg = (specifier) => {
403
+ if (!specifier || specifier.startsWith('.') || specifier.startsWith('/')) return null;
404
+ const s = specifier.startsWith('node:') ? specifier.slice(5) : specifier;
405
+ return s.startsWith('@') ? s.split('/').slice(0, 2).join('/') : s.split('/')[0];
406
+ };
407
+
369
408
  reachableFiles.forEach(filePath => {
370
409
  const node = this.projectGraph.get(filePath);
371
410
  if (node) {
372
411
  node.externalPackageUsage.forEach(pkg => usedByReachableFiles.add(pkg));
412
+
413
+ if (node.explicitImports) {
414
+ node.explicitImports.forEach(specifier => {
415
+ const pkg = extractBasePkg(specifier);
416
+ if (pkg) usedByReachableFiles.add(pkg);
417
+ });
418
+ }
373
419
 
374
420
  // Conservative check for dynamic imports in reachable files
375
421
  if (node.calculatedDynamicImports) {
@@ -401,11 +447,18 @@ export class EngineContext {
401
447
  }
402
448
 
403
449
  // UPGRADE: Be more aggressive in detecting unused dependencies.
404
- // Check both externalPackageUsage and rawStringReferences.
405
450
  const isUsed = usedByReachableFiles.has(dep) ||
406
451
  Array.from(reachableFiles).some(f => {
407
452
  const node = this.projectGraph.get(f);
408
- return node && (node.externalPackageUsage.has(dep) || node.rawStringReferences.has(dep));
453
+ if (!node) return false;
454
+ if (node.externalPackageUsage.has(dep) || node.rawStringReferences.has(dep)) return true;
455
+ if (node.explicitImports) {
456
+ for (const specifier of node.explicitImports) {
457
+ const base = extractBasePkg(specifier);
458
+ if (base === dep) return true;
459
+ }
460
+ }
461
+ return false;
409
462
  });
410
463
 
411
464
  if (!isUsed) {
package/src/index.js CHANGED
@@ -64,6 +64,24 @@ export class RefactoringEngine {
64
64
  this.resolver = new DependencyResolver(this.context, this.pathMapper, this.workspaceGraph);
65
65
  this.circularDetector = new CircularDetector(this.context);
66
66
 
67
+ // FIX (Bug 2): Initialize packageJson and EntryPointDetector, mirroring the .mjs twin.
68
+ // Without this, src/index.ts is never registered as an entry point in the JS build,
69
+ // causing all files to be treated as unreachable and suppressing the dependency audit.
70
+ try {
71
+ const pkgPath = path.join(this.context.cwd, 'package.json');
72
+ if (existsSync(pkgPath)) {
73
+ this.context.packageJson = JSON.parse(readFileSync(pkgPath, 'utf8'));
74
+ }
75
+ } catch (e) {}
76
+
77
+ import('./resolution/EntryPointDetector.js').then(({ EntryPointDetector }) => {
78
+ this.entryPointDetector = new EntryPointDetector(
79
+ this.context.cwd,
80
+ this.context.packageJson,
81
+ this.context.metrics
82
+ );
83
+ }).catch(() => {});
84
+
67
85
  // Lazy import DependencyProfiler
68
86
  import('./resolution/DependencyProfiler.js').then(({ DependencyProfiler }) => {
69
87
  this.dependencyProfiler = new DependencyProfiler(this.context);
@@ -347,16 +365,33 @@ export class RefactoringEngine {
347
365
  }
348
366
  }
349
367
  if (entryCount === 0) {
350
- console.log(ansis.bold.yellow(' 🚨 ALARM: Keine einzige Datei wurde als Entry Point markiert! Greife auf Fallbacks zurück...'));
351
- const fallbackFiles = ['index.js', 'index.mjs', 'src/index.js', 'src/index.mjs', 'main.js', 'src/main.js'];
352
- for (const fallback of fallbackFiles) {
353
- const absFallback = path.resolve(this.context.cwd, fallback).replace(/\\/g, '/');
354
- if (this.context.projectGraph.has(absFallback)) {
355
- const node = this.context.projectGraph.get(absFallback);
356
- node.isEntry = true;
357
- console.log(ansis.green(` β€’ πŸ’Ž FALLBACK ENTRY: ${fallback}`));
358
- entryCount++;
359
- break;
368
+ console.log(ansis.bold.yellow(' 🚨 ALARM: Keine einzige Datei wurde als Entry Point markiert! Starte Deep Detection...'));
369
+
370
+ // FIX (Bug 2): Use EntryPointDetector first (mirrors .mjs twin behavior).
371
+ // This ensures src/index.ts and other convention-based roots are found reliably.
372
+ if (this.entryPointDetector) {
373
+ const detected = this.entryPointDetector.detect();
374
+ detected.forEach(absPath => {
375
+ const cleanPath = absPath.replace(/\\/g, '/');
376
+ if (this.context.projectGraph.has(cleanPath)) {
377
+ this.context.projectGraph.get(cleanPath).isEntry = true;
378
+ console.log(ansis.green(` β€’ πŸ’Ž DETECTED ENTRY: ${path.relative(this.context.cwd, cleanPath)}`));
379
+ entryCount++;
380
+ }
381
+ });
382
+ }
383
+
384
+ if (entryCount === 0) {
385
+ const fallbackFiles = ['index.js', 'index.mjs', 'src/index.js', 'src/index.mjs', 'src/index.ts', 'src/index.tsx', 'main.js', 'src/main.js', 'src/main.ts'];
386
+ for (const fallback of fallbackFiles) {
387
+ const absFallback = path.resolve(this.context.cwd, fallback).replace(/\\/g, '/');
388
+ if (this.context.projectGraph.has(absFallback)) {
389
+ const node = this.context.projectGraph.get(absFallback);
390
+ node.isEntry = true;
391
+ console.log(ansis.green(` β€’ πŸ’Ž FALLBACK ENTRY: ${fallback}`));
392
+ entryCount++;
393
+ break;
394
+ }
360
395
  }
361
396
  }
362
397
  }
@@ -547,10 +582,22 @@ export class RefactoringEngine {
547
582
  }
548
583
  }
549
584
 
585
+ // FIX: Robust path normalization helper for cross-platform path comparison.
586
+ // Mirrors the normalizePath() logic in OxcAnalyzer.js to handle Windows
587
+ // drive-letter case differences and mixed slash types before path.relative().
588
+ const normalizeForAudit = (p) => {
589
+ if (!p) return '';
590
+ let n = path.resolve(p).replace(/\\/g, '/');
591
+ if (/^[a-z]:\//.test(n)) n = n.charAt(0).toUpperCase() + n.slice(1);
592
+ return n.replace(/\/+/g, '/').replace(/\/$/, '');
593
+ };
594
+
550
595
  for (const metadata of auditManifests) {
551
596
  if (this.context.projectGraph) {
597
+ const normRoot = normalizeForAudit(metadata.rootDirectory);
552
598
  for (const [filePath, fileNode] of this.context.projectGraph.entries()) {
553
- const cleanRelative = path.relative(metadata.rootDirectory, filePath).replace(/\\/g, '/');
599
+ const normFilePath = normalizeForAudit(filePath);
600
+ const cleanRelative = path.relative(normRoot, normFilePath).replace(/\\/g, '/');
554
601
 
555
602
  // Check if file belongs to this manifest's directory scope
556
603
  if (!cleanRelative.startsWith('..') && !cleanRelative.startsWith('/') && fileNode.explicitImports) {