entkapp 4.1.0 → 4.3.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/README.md CHANGED
@@ -28,6 +28,19 @@ It represents the process of stripping away the unnecessary layers of a codebase
28
28
  ## šŸ“¦ Installation
29
29
 
30
30
  ```bash
31
- npm install -D entkapp
31
+ npm install -g entkapp
32
32
  # or
33
- pnpm add -D entkapp
33
+ pnpm add -g entkapp
34
+ ```
35
+
36
+ ## Usage
37
+ ```bash
38
+ npx entkapp
39
+ # or
40
+ entkapp
41
+ # and then
42
+ npx entkapp -r
43
+ # or
44
+ entkapp -r
45
+ # It is recommended to run with --no-fix tag on your first run!
46
+ ```
package/bin/cli.js CHANGED
@@ -28,7 +28,7 @@ async function bootstrap() {
28
28
  program
29
29
  .name('entkapp')
30
30
  .description(ansis.cyan('Enterprise-Grade AST Syntax Refactoring & Self-Healing Engine'))
31
- .version(packageJsonContent.version || '4.0.0');
31
+ .version(packageJsonContent.version || '4.3.0');
32
32
 
33
33
  program
34
34
  .option('-c, --cwd <path>', 'Specify the execution context root directory', process.cwd())
@@ -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 || '4.0.0'} Engine Activation`));
135
+ console.log(ansis.bold.green(`\nšŸ“¦ entkapp v${packageJsonContent.version || '4.3.0'} 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/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * ============================================================================
5
- * šŸ“¦ entkapp v4.1.0: Ultimate Enterprise Codebase Janitor & Self-Healing Engine
5
+ * šŸ“¦ entkapp v4.2.0: Ultimate Enterprise Codebase Janitor & Self-Healing Engine
6
6
  * ============================================================================
7
7
  * * Eine hochgradig integrierte Code-Analyse- und Projektbootstrapping-Engine.
8
8
  * Kombiniert rekursive Erreichbarkeitsanalysen (Reachability Graphs) auf
@@ -648,7 +648,7 @@ function smartPrepend(originalCode, declarationBlock) {
648
648
  async function inspectNpmPackage(pkgName) {
649
649
  try {
650
650
  const response = await fetch(`https://registry.npmjs.org/${pkgName}/latest`, {
651
- headers: { 'User-Agent': 'entkapp-dx-client/4.1.0' },
651
+ headers: { 'User-Agent': 'entkapp-dx-client/4.2.0' },
652
652
  signal: AbortSignal.timeout(4000)
653
653
  });
654
654
  if (response.status === 200) {
@@ -682,7 +682,7 @@ async function inspectNpmPackage(pkgName) {
682
682
  async function fetchRemoteLicense(licenseKey) {
683
683
  try {
684
684
  const response = await fetch(`https://api.github.com/licenses/${licenseKey.toLowerCase()}`, {
685
- headers: { 'User-Agent': 'entkapp-dx-client/4.1.0' },
685
+ headers: { 'User-Agent': 'entkapp-dx-client/4.2.0' },
686
686
  signal: AbortSignal.timeout(5000)
687
687
  });
688
688
  if (response.status === 200) {
@@ -1681,7 +1681,7 @@ function postProcessAnalysis(stats, graphEngine) {
1681
1681
  // ============================================================
1682
1682
  async function main() {
1683
1683
  if (process.argv.includes('--help') || process.argv.includes('-h')) {
1684
- console.log(`\nšŸ“¦ entkapp v4.1.0: Advanced Dependency Intelligence Engine\n`);
1684
+ console.log(`\nšŸ“¦ entkapp v4.2.0: Advanced Dependency Intelligence Engine\n`);
1685
1685
  console.log(`Usage: npx entkapp [options]\n`);
1686
1686
  console.log(`Options:`);
1687
1687
  console.log(` -h, --help Show this comprehensive workspace helper panel`);
@@ -1766,7 +1766,7 @@ async function main() {
1766
1766
  let detectedFrameworks = [];
1767
1767
 
1768
1768
  console.log(`\n${'═'.repeat(67)}`);
1769
- console.log(`šŸš€ entkapp v4.1.0: Enterprise Graph Intelligence Analyzer`);
1769
+ console.log(`šŸš€ entkapp v4.2.0: Enterprise Graph Intelligence Analyzer`);
1770
1770
  console.log(`${'═'.repeat(67)}\n`);
1771
1771
 
1772
1772
  const topLevelItems = fs.readdirSync(targetDir);
package/logo.png CHANGED
Binary file
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": "4.1.0",
4
+ "version": "4.3.0",
5
5
  "description": "The Ultimate Enterprise Codebase Janitor. Faster than Knip with OXC integration, type-aware analysis, and automated structural healing. Fully standalone - solving what Knip cannot.",
6
6
  "type": "module",
7
7
  "main": "index.js",
@@ -64,7 +64,12 @@
64
64
  "unresolved",
65
65
  "unused",
66
66
  "unused-packages",
67
- "workspace"
67
+ "workspace",
68
+ "entkapp",
69
+ "cut",
70
+ "kapp",
71
+ "dreamlong",
72
+ "DreamLong"
68
73
  ],
69
74
  "author": "DreamLongYT",
70
75
  "license": "Apache-2.0",
@@ -86,7 +91,6 @@
86
91
  },
87
92
  "devDependencies": {
88
93
  "@types/node": "^25.9.3",
89
- "knip": "^6.17.1",
90
94
  "vitepress": "^1.6.4",
91
95
  "vue": ">=3.5.0"
92
96
  },
@@ -31,8 +31,16 @@ export class GraphNode {
31
31
  this.localSuppressedRules = new Set();
32
32
  }
33
33
 
34
- isSymbolReferencedExternally(symbolName, projectGraph) {
34
+ isSymbolReferencedExternally(symbolName, projectGraph, context) {
35
35
  if (this.isLibraryEntry) return true;
36
+
37
+ // Check global registry if provided
38
+ if (context && context.importUsageRegistry) {
39
+ if (context.importUsageRegistry.has(`${this.filePath}:${symbolName}`) ||
40
+ context.importUsageRegistry.has(`${this.filePath}:*`)) {
41
+ return true;
42
+ }
43
+ }
36
44
 
37
45
  // --- NEW: Self-reference check (Fix for SecretSeverity bug) ---
38
46
  // If the symbol is used within the same file, it is NOT dead.
@@ -137,12 +145,14 @@ export class EngineContext {
137
145
  report.orphanedFiles.push(path.relative(this.cwd, filePath));
138
146
  }
139
147
 
148
+ if (node.isEntry || node.isLibraryEntry) continue;
149
+
140
150
  for (const [symbol, meta] of node.internalExports.entries()) {
141
151
  if (symbol === '*' || symbol === 'default' || node.localSuppressedRules.has('unused-export') || node.localSuppressedRules.has(`unused-export:${symbol}`)) {
142
152
  continue;
143
153
  }
144
154
 
145
- if (!node.isSymbolReferencedExternally(symbol, this.projectGraph)) {
155
+ if (!node.isSymbolReferencedExternally(symbol, this.projectGraph, this)) {
146
156
  const loc = node.symbolSourceLocations.get(symbol) || { line: 0, column: 0 };
147
157
  report.deadExports.push({
148
158
  symbol,
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ============================================================================
3
- * Headless API for entkapp v4.1.0
3
+ * Headless API for entkapp v4.2.0
4
4
  * ============================================================================
5
5
  * Provides a programmatic interface for integrating entkapp into
6
6
  * custom workflows, CI/CD pipelines, and third-party tools.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ============================================================================
3
- * Plugin SDK for entkapp v4.0.0
3
+ * Plugin SDK for entkapp v4.2.0
4
4
  * ============================================================================
5
5
  * Provides utilities and helpers for developing custom plugins that extend
6
6
  * entkapp's analysis and healing capabilities.
@@ -1,6 +1,7 @@
1
1
  import ts from 'typescript';
2
2
  import path from 'path';
3
3
  import fs from 'fs';
4
+ import ansis from 'ansis';
4
5
 
5
6
  export class ASTAnalyzer {
6
7
  constructor(context) {
@@ -398,6 +399,14 @@ export class ASTAnalyzer {
398
399
  if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
399
400
  const arg = node.arguments[0];
400
401
  if (arg) {
402
+ if (!fileNode.calculatedDynamicImports) fileNode.calculatedDynamicImports = [];
403
+ fileNode.calculatedDynamicImports.push({ kind: ts.SyntaxKind[arg.kind], start: arg.getStart(sourceFile) });
404
+
405
+ // Ensure the argument text is also in rawStringReferences if it's a literal
406
+ if (ts.isStringLiteral(arg) || ts.isNoSubstitutionTemplateLiteral(arg)) {
407
+ fileNode.rawStringReferences.add(arg.text);
408
+ }
409
+
401
410
  if (ts.isStringLiteral(arg)) {
402
411
  fileNode.explicitImports.add(arg.text);
403
412
  fileNode.dynamicImports.add(arg.text);
@@ -405,11 +414,6 @@ export class ASTAnalyzer {
405
414
  if (!arg.text.startsWith('.') && !arg.text.startsWith('/')) {
406
415
  fileNode.externalPackageUsage.add(this._extractPackageName(arg.text));
407
416
  }
408
- } else {
409
- // Dynamic import with a non-literal expression (e.g., variable or template literal).
410
- if (fileNode.calculatedDynamicImports) {
411
- fileNode.calculatedDynamicImports.push({ kind: ts.SyntaxKind[arg.kind], start: arg.getStart(sourceFile) });
412
- }
413
417
  }
414
418
  }
415
419
  } else if (ts.isIdentifier(node.expression) && node.expression.text === 'require') {
@@ -178,15 +178,6 @@ export class BarrelParser {
178
178
  }
179
179
 
180
180
  // Rule D: Sweep through anonymous star re-exports vectors (export * from 'module')
181
- //
182
- // Algorithm:
183
- // 1. For each `export * from './child'`, recursively resolve the symbol in the child.
184
- // 2. A non-barrel child immediately returns `{ originFile: childPath }` regardless of
185
- // whether it actually declares the symbol. We therefore must verify that the
186
- // returned origin file actually contains the symbol in its declaredLocalExports
187
- // before accepting the result.
188
- // 3. If the child is itself a barrel, the recursive call already performs the full
189
- // chain walk, so we only need to verify the final origin.
190
181
  for (const relativePath of spec.wildcardExports) {
191
182
  const fullyResolvedPath = this.resolver.resolveModulePath(contextFilePath, relativePath);
192
183
 
@@ -195,24 +186,15 @@ export class BarrelParser {
195
186
  fullyResolvedPath,
196
187
  targetSymbolName,
197
188
  activeProjectGraph,
198
- new Set(protectionStack) // Use a copy so sibling branches don't block each other
189
+ new Set(protectionStack)
199
190
  );
200
191
 
201
192
  if (!continuousResolutionTrace) continue;
202
- if (continuousResolutionTrace.originFile === contextFilePath) continue;
203
193
 
204
- // Verify that the resolved origin actually declares the symbol.
205
- // This prevents a non-barrel sibling (e.g. constants.ts) from being
206
- // incorrectly returned for a symbol it does not export (e.g. formatData).
207
194
  const originSpec = await this.parseBarrelSpecification(continuousResolutionTrace.originFile);
208
195
  if (originSpec.declaredLocalExports.has(continuousResolutionTrace.originSymbol)) {
209
196
  return continuousResolutionTrace;
210
197
  }
211
- // The origin spec is itself a barrel (isBarrelInstance = true) and the
212
- // recursive call already resolved through it – accept the result.
213
- if (originSpec.isBarrelInstance) {
214
- return continuousResolutionTrace;
215
- }
216
198
  }
217
199
  }
218
200
 
@@ -10,7 +10,7 @@ export class DeadCodeDetector {
10
10
  // Find all entry points
11
11
  const entryPoints = new Set();
12
12
  for (const [filePath, node] of graph.entries()) {
13
- if (node.isEntry || node.isNextJsRoute || node.isSvelteComponent || node.isAstroPage) {
13
+ if (node.isEntry || node.isLibraryEntry || node.isNextJsRoute || node.isSvelteComponent || node.isAstroPage) {
14
14
  entryPoints.add(filePath);
15
15
  }
16
16
  }
@@ -45,7 +45,7 @@ export class DeadCodeDetector {
45
45
  if (!node) continue;
46
46
 
47
47
  // If it's an entry point, we consider its exports used (unless strictly configured otherwise)
48
- if (node.isEntry) continue;
48
+ if (node.isEntry || node.isLibraryEntry) continue;
49
49
 
50
50
  for (const [exportName, exportInfo] of node.internalExports.entries()) {
51
51
  if (exportName === '*' || exportName === 'default') continue; // Skip wildcards for now
@@ -3,20 +3,17 @@ export class OxcAnalyzer {
3
3
  this.context = context;
4
4
  this.oxc = null;
5
5
  this.isAvailable = false;
6
- // Initialization is handled via init() or lazily during parseFile
7
6
  }
8
7
 
9
8
  async init() {
10
9
  if (this.isAvailable) return true;
11
10
  try {
12
- // In ESM, we use dynamic import()
13
11
  const oxc = await import("oxc-parser");
14
12
  this.oxc = oxc;
15
13
  this.isAvailable = true;
16
14
  return true;
17
15
  } catch (e) {
18
16
  try {
19
- // Fallback for older node versions or specific bundling setups
20
17
  const { createRequire } = await import('module');
21
18
  const require = createRequire(import.meta.url);
22
19
  this.oxc = require("oxc-parser");
@@ -25,7 +22,7 @@ export class OxcAnalyzer {
25
22
  } catch (err) {
26
23
  this.isAvailable = false;
27
24
  if (this.context.verbose) {
28
- console.warn("[OxcAnalyzer] oxc-parser not found or failed to load, falling back to TypeScript compiler API.");
25
+ console.warn("[OxcAnalyzer] oxc-parser not found or failed to load.");
29
26
  }
30
27
  return false;
31
28
  }
@@ -39,24 +36,18 @@ export class OxcAnalyzer {
39
36
  }
40
37
 
41
38
  try {
42
- if (this.context.verbose) {
43
- console.log(`[OXC] Fast-parsing: ${filePath}`);
44
- }
45
-
46
39
  const ast = this.oxc.parseSync(content, {
47
40
  sourceType: "module",
48
41
  sourceFilename: filePath,
49
42
  ecmaVersion: "latest",
50
43
  });
51
44
 
52
- // Initialize new properties for JSX and Decorator analysis
53
45
  fileNode.jsxComponents = new Set();
54
46
  fileNode.jsxProps = new Set();
55
47
  fileNode.decorators = new Set();
56
48
 
57
49
  this.walkOxcAst(ast.program, fileNode, content);
58
50
 
59
- // Compute line/column for each export start position
60
51
  const lines = content.split('\n');
61
52
  const getLineCol = (pos) => {
62
53
  let count = 0;
@@ -100,19 +91,17 @@ export class OxcAnalyzer {
100
91
  this.handleCallExpression(node, fileNode);
101
92
  break;
102
93
  case "JSXElement":
103
- case "JSXFragment": // Consider fragments as well
94
+ case "JSXFragment":
104
95
  this.handleJsxElement(node, fileNode);
105
96
  break;
106
97
  case "Decorator":
107
98
  this.handleDecorator(node, fileNode);
108
99
  break;
109
100
  case "StringLiteral":
110
- // Track for secret scanning if it looks like a secret
111
101
  fileNode.rawStringReferences.add(node.value);
112
102
  break;
113
103
  }
114
104
 
115
- // Traverse children
116
105
  for (const key in node) {
117
106
  if (node[key] && typeof node[key] === "object") {
118
107
  if (Array.isArray(node[key])) {
@@ -128,7 +117,6 @@ export class OxcAnalyzer {
128
117
  const specifier = node.source.value;
129
118
  fileNode.explicitImports.add(specifier);
130
119
 
131
- // Track external package usage for dependency analysis
132
120
  if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
133
121
  fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
134
122
  }
@@ -136,7 +124,7 @@ export class OxcAnalyzer {
136
124
  if (node.specifiers) {
137
125
  node.specifiers.forEach((spec) => {
138
126
  if (spec.type === "ImportSpecifier") {
139
- const importedName = spec.imported.name;
127
+ const importedName = spec.imported.name || (spec.imported.type === "Identifier" ? spec.imported.name : spec.imported.value);
140
128
  fileNode.importedSymbols.add(`${specifier}:${importedName}`);
141
129
  } else if (spec.type === "ImportDefaultSpecifier") {
142
130
  fileNode.importedSymbols.add(`${specifier}:default`);
@@ -148,86 +136,83 @@ export class OxcAnalyzer {
148
136
  }
149
137
 
150
138
  handleExportDeclaration(node, fileNode, content) {
139
+ // 1. Default Exports
151
140
  if (node.type === "ExportDefaultDeclaration") {
152
- fileNode.internalExports.set("default", { type: "default", start: node.start, end: node.end });
141
+ fileNode.internalExports.set("default", {
142
+ type: "default",
143
+ start: node.start,
144
+ end: node.end
145
+ });
153
146
  return;
154
147
  }
155
148
 
149
+ // 2. Re-export All: export * from 'mod' or export * as ns from 'mod'
156
150
  if (node.type === "ExportAllDeclaration") {
157
- const sourceSpecifier = node.source ? node.source.value : null;
158
- if (sourceSpecifier) {
159
- // Register re-export source as an explicit import so the graph linker
160
- // creates an incomingEdge on the re-exported file.
161
- fileNode.explicitImports.add(sourceSpecifier);
162
-
163
- // Track external package usage from re-exports
164
- if (!sourceSpecifier.startsWith('.') && !sourceSpecifier.startsWith('/')) {
165
- fileNode.externalPackageUsage.add(this._extractPackageName(sourceSpecifier));
166
- }
151
+ const sourceSpecifier = node.source.value;
152
+ fileNode.explicitImports.add(sourceSpecifier);
153
+ if (!sourceSpecifier.startsWith('.') && !sourceSpecifier.startsWith('/')) {
154
+ fileNode.externalPackageUsage.add(this._extractPackageName(sourceSpecifier));
155
+ }
167
156
 
168
- if (node.exported) {
169
- // export * as name from 'module'
170
- const name = node.exported.name || (node.exported.type === "Identifier" ? node.exported.name : null);
171
- if (name) {
172
- fileNode.internalExports.set(name, { type: "re-export-namespace", source: sourceSpecifier, originalName: "*", start: node.start, end: node.end });
173
- fileNode.importedSymbols.add(`${sourceSpecifier}:*`);
174
- }
175
- } else {
176
- // export * from 'module'
177
- fileNode.internalExports.set("*", { type: "re-export-all", source: sourceSpecifier });
178
- // Register as wildcard importedSymbol so graph linker creates incomingEdge
157
+ if (node.exported) {
158
+ // export * as ns from 'mod'
159
+ const name = node.exported.name || (node.exported.type === "Identifier" ? node.exported.name : null);
160
+ if (name) {
161
+ fileNode.internalExports.set(name, {
162
+ type: "re-export-namespace",
163
+ source: sourceSpecifier,
164
+ originalName: "*",
165
+ start: node.start,
166
+ end: node.end
167
+ });
179
168
  fileNode.importedSymbols.add(`${sourceSpecifier}:*`);
180
169
  }
170
+ } else {
171
+ // export * from 'mod'
172
+ fileNode.internalExports.set("*", {
173
+ type: "re-export-all",
174
+ source: sourceSpecifier
175
+ });
176
+ fileNode.importedSymbols.add(`${sourceSpecifier}:*`);
181
177
  }
182
178
  return;
183
179
  }
184
180
 
181
+ // 3. Named Exports & Re-exports with specifiers
185
182
  if (node.source) {
186
- // Re-export with source: export { x } from 'module'
183
+ // Re-export: export { x } from 'mod'
187
184
  const specifier = node.source.value;
188
- // Register re-export source as an explicit import
189
185
  fileNode.explicitImports.add(specifier);
190
-
191
- // Track external package usage from re-exports
192
186
  if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
193
187
  fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
194
188
  }
195
189
 
196
190
  if (node.specifiers) {
197
191
  node.specifiers.forEach((spec) => {
198
- const exportedName = spec.exported.name;
199
- const localName = spec.local.name;
192
+ const exportedName = spec.exported.name || (spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value);
193
+ const localName = spec.local.name || (spec.local.type === "Identifier" ? spec.local.name : spec.local.value);
200
194
  fileNode.internalExports.set(exportedName, {
201
195
  type: "re-export",
202
196
  source: specifier,
203
197
  originalName: localName,
204
- start: node.start,
205
- end: node.end,
198
+ start: spec.start,
199
+ end: spec.end,
206
200
  });
207
- // Register as importedSymbol so barrel-tracer can resolve origin file
208
201
  fileNode.importedSymbols.add(`${specifier}:${localName}`);
209
202
  });
210
203
  }
211
204
  } else if (node.declaration) {
212
- // Direct export
205
+ // Direct declaration export: export const x = 1, export function f() {}
213
206
  const decl = node.declaration;
214
207
  if (decl.type === "VariableDeclaration") {
215
208
  decl.declarations.forEach((d) => {
216
- if (d.id.type === "Identifier") {
217
- fileNode.internalExports.set(d.id.name, { type: "variable", start: d.start, end: d.end });
218
- } else if (d.id.type === "ObjectPattern") {
219
- d.id.properties.forEach((p) => {
220
- if (p.type === "Property" && p.value.type === "Identifier") {
221
- fileNode.internalExports.set(p.value.name, { type: "variable", start: p.start, end: p.end });
222
- }
223
- });
224
- } else if (d.id.type === "ArrayPattern") {
225
- d.id.elements.forEach((e) => {
226
- if (e && e.type === "Identifier") {
227
- fileNode.internalExports.set(e.name, { type: "variable", start: e.start, end: e.end });
228
- }
209
+ this._extractNamesFromPattern(d.id, (name) => {
210
+ fileNode.internalExports.set(name, {
211
+ type: "variable",
212
+ start: d.start,
213
+ end: d.end
229
214
  });
230
- }
215
+ });
231
216
  });
232
217
  } else if (decl.id && decl.id.name) {
233
218
  let type = "unknown";
@@ -238,49 +223,70 @@ export class OxcAnalyzer {
238
223
  else if (decl.type === "TSTypeAliasDeclaration") type = "type";
239
224
  else if (decl.type === "TSModuleDeclaration") type = "namespace";
240
225
 
241
- const exportInfo = { type, start: decl.start, end: decl.end };
242
- fileNode.internalExports.set(decl.id.name, exportInfo);
243
-
244
- if (decl.type === "TSEnumDeclaration") {
245
- exportInfo.members = decl.members.map((m) => m.id.name || (m.id.type === "Identifier" ? m.id.name : ""));
246
- } else if (decl.type === "TSInterfaceDeclaration" || decl.type === "ClassDeclaration") {
247
- exportInfo.members = decl.body.body.filter((m) => m.key && m.key.name).map((m) => m.key.name);
248
- }
226
+ fileNode.internalExports.set(decl.id.name, {
227
+ type,
228
+ start: decl.start,
229
+ end: decl.end
230
+ });
249
231
  }
250
232
  } else if (node.specifiers) {
233
+ // Export existing locals: export { x, y as z }
251
234
  node.specifiers.forEach((spec) => {
252
- const exportedName = spec.exported.name;
253
- const localName = spec.local.name;
235
+ const exportedName = spec.exported.name || (spec.exported.type === "Identifier" ? spec.exported.name : spec.exported.value);
236
+ const localName = spec.local.name || (spec.local.type === "Identifier" ? spec.local.name : spec.local.value);
254
237
  fileNode.internalExports.set(exportedName, {
255
238
  type: "export",
256
239
  originalName: localName,
257
- start: node.start,
258
- end: node.end,
240
+ start: spec.start,
241
+ end: spec.end,
259
242
  });
260
243
  });
261
244
  }
262
245
  }
263
246
 
247
+ _extractNamesFromPattern(node, callback) {
248
+ if (!node) return;
249
+ if (node.type === "Identifier") {
250
+ callback(node.name);
251
+ } else if (node.type === "ObjectPattern") {
252
+ node.properties.forEach(p => {
253
+ if (p.type === "Property") {
254
+ this._extractNamesFromPattern(p.value, callback);
255
+ } else if (p.type === "RestElement") {
256
+ this._extractNamesFromPattern(p.argument, callback);
257
+ }
258
+ });
259
+ } else if (node.type === "ArrayPattern") {
260
+ node.elements.forEach(e => {
261
+ if (e) this._extractNamesFromPattern(e, callback);
262
+ });
263
+ } else if (node.type === "AssignmentPattern") {
264
+ this._extractNamesFromPattern(node.left, callback);
265
+ } else if (node.type === "RestElement") {
266
+ this._extractNamesFromPattern(node.argument, callback);
267
+ }
268
+ }
269
+
264
270
  handleCallExpression(node, fileNode) {
265
271
  // Dynamic import(): import('./module')
266
272
  if (node.callee.type === "Import" && node.arguments.length > 0) {
267
273
  const arg = node.arguments[0];
274
+ if (!fileNode.calculatedDynamicImports) fileNode.calculatedDynamicImports = [];
275
+ fileNode.calculatedDynamicImports.push({ kind: arg.type, start: arg.start });
276
+
268
277
  if (arg.type === "StringLiteral") {
269
278
  const specifier = arg.value;
270
279
  fileNode.explicitImports.add(specifier);
271
280
  fileNode.dynamicImports.add(specifier);
281
+ fileNode.importedSymbols.add(`${specifier}:*`); // Dynamic import usually consumes the whole namespace
272
282
  if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
273
283
  fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
274
284
  }
275
- } else {
276
- // Non-literal dynamic import: record as calculated
277
- if (fileNode.calculatedDynamicImports) {
278
- fileNode.calculatedDynamicImports.push({ kind: arg.type, start: arg.start });
279
- }
280
285
  }
281
286
  } else if (node.callee.type === "Identifier" && node.callee.name === "require" && node.arguments.length > 0 && node.arguments[0].type === "StringLiteral") {
282
287
  const specifier = node.arguments[0].value;
283
288
  fileNode.explicitImports.add(specifier);
289
+ fileNode.importedSymbols.add(`${specifier}:*`);
284
290
  if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
285
291
  fileNode.externalPackageUsage.add(this._extractPackageName(specifier));
286
292
  }
@@ -291,6 +297,7 @@ export class OxcAnalyzer {
291
297
  const getElementName = (nameNode) => {
292
298
  if (nameNode.type === "JSXIdentifier") return nameNode.name;
293
299
  if (nameNode.type === "JSXMemberExpression") return `${getElementName(nameNode.object)}.${nameNode.property.name}`;
300
+ if (nameNode.type === "JSXNamespacedName") return `${nameNode.namespace.name}:${nameNode.name.name}`;
294
301
  return "unknown";
295
302
  };
296
303
 
@@ -311,19 +318,17 @@ export class OxcAnalyzer {
311
318
  handleDecorator(node, fileNode) {
312
319
  const getDecoratorName = (expr) => {
313
320
  if (expr.type === "Identifier") return expr.name;
314
- if (expr.type === "CallExpression" && expr.callee.type === "Identifier") return expr.callee.name;
315
- if (expr.type === "CallExpression" && expr.callee.type === "MemberExpression" && expr.callee.property.type === "Identifier") return expr.callee.property.name;
321
+ if (expr.type === "CallExpression") return getDecoratorName(expr.callee);
322
+ if (expr.type === "MemberExpression") {
323
+ const prop = expr.property.name || expr.property.value;
324
+ return prop || "unknown";
325
+ }
316
326
  return "unknown";
317
327
  };
318
328
 
319
329
  const decoratorName = getDecoratorName(node.expression);
320
- fileNode.decorators.add(decoratorName);
321
-
322
- // Optionally, extract decorator arguments
323
- if (node.expression.type === "CallExpression") {
324
- node.expression.arguments.forEach(arg => {
325
- // Further analysis of arguments can be done here if needed
326
- });
330
+ if (decoratorName !== "unknown") {
331
+ fileNode.decorators.add(decoratorName);
327
332
  }
328
333
  }
329
334
 
package/src/index.js CHANGED
@@ -3,7 +3,7 @@ import { OxcAnalyzer } from "./ast/OxcAnalyzer.js";
3
3
  import { SecretScanner } from './ast/SecretScanner.js';
4
4
  /**
5
5
  * ============================================================================
6
- * šŸ“¦ entkapp v4.1.0: Unified Architectural Refactoring Orchestrator
6
+ * šŸ“¦ entkapp v4.2.0: Unified Architectural Refactoring Orchestrator
7
7
  * ============================================================================
8
8
  * Main execution bridge managing multi-pass compilation cycles, semantic cross-linking,
9
9
  * supply-chain validation audits, and automated structural healing rollbacks.
@@ -87,6 +87,8 @@ export class RefactoringEngine {
87
87
  try {
88
88
  console.log(ansis.bold.green('šŸŽÆ Starting entkapp Operational Optimization Cycle...'));
89
89
 
90
+ if (!this.context.importUsageRegistry) this.context.importUsageRegistry = new Set();
91
+
90
92
  let rl;
91
93
  if (!this.context.skipConfirm) {
92
94
  rl = readline.createInterface({
@@ -136,6 +138,7 @@ export class RefactoringEngine {
136
138
 
137
139
  for (const filePath of sourceCodeFilesList) {
138
140
  const node = this.context.getOrCreateNode(filePath);
141
+ if (!this.context.importUsageRegistry) this.context.importUsageRegistry = new Set();
139
142
  const currentHash = await this.cacheManager.computeHash(filePath);
140
143
  node.contentHash = currentHash;
141
144
 
@@ -217,6 +220,21 @@ export class RefactoringEngine {
217
220
  // Pass 4: Evaluate graph edges and link connections across the codebase mesh
218
221
  console.log(ansis.dim('šŸ”— Linking graph edges and checking structural usage paths...'));
219
222
  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
+ }
220
238
 
221
239
  // NEW: Circular Dependency Detection
222
240
  console.log(ansis.dim('šŸ”„ Detecting circular dependencies...'));
@@ -262,7 +280,11 @@ export class RefactoringEngine {
262
280
  if (!this.context.unlistedDependencies) this.context.unlistedDependencies = [];
263
281
 
264
282
  // Simple internal helper to guarantee matching forward slash strings across all platforms
265
- const slashify = (p) => path.resolve(this.context.cwd, p).replace(/\\/g, '/');
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
+ };
266
288
 
267
289
  if (this.context.projectGraph && typeof this.context.projectGraph.entries === 'function') {
268
290
  for (const [filePath, fileNode] of this.context.projectGraph.entries()) {
@@ -271,12 +293,10 @@ export class RefactoringEngine {
271
293
  const cleanFilePath = slashify(filePath);
272
294
 
273
295
  // šŸš€ ROOT DEPS HARVESTER SHADOW TRACKING:
274
- // If external packages are parsed from ANY file node in the monorepo,
275
- // ensure the auditor registries register their footprint so root checking works!
276
296
  if (fileNode.externalPackageUsage) {
277
297
  fileNode.externalPackageUsage.forEach(pkg => {
278
- const relativeToRoot = path.relative(this.context.cwd, filePath);
279
- if (relativeToRoot.startsWith('packages' + path.sep) || relativeToRoot.startsWith('packages/')) {
298
+ const relativeToRoot = path.relative(this.context.cwd, filePath).replace(/\\/g, '/');
299
+ if (relativeToRoot.startsWith('packages/')) {
280
300
  this.context.consumedWorkspacePackages.add(pkg);
281
301
  } else {
282
302
  this.context.consumedRootPackages.add(pkg);
@@ -294,17 +314,17 @@ export class RefactoringEngine {
294
314
  if (!this.context.exportRegistry.has(cleanFilePath)) {
295
315
  this.context.exportRegistry.set(cleanFilePath, new Set());
296
316
  }
297
- exportKeys.forEach(key => this.context.exportRegistry.get(cleanFilePath).add(key));
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
+ });
298
322
  }
299
323
  }
300
324
 
301
325
  // 2. Gather cross-file usage tokens using unified slashes
302
- if (fileNode.explicitImports && fileNode.importedSymbols) {
303
- const symbolsArray = typeof fileNode.importedSymbols.forEach === 'function'
304
- ? Array.from(fileNode.importedSymbols)
305
- : (Array.isArray(fileNode.importedSymbols) ? fileNode.importedSymbols : []);
306
-
307
- for (const symbolToken of symbolsArray) {
326
+ if (fileNode.importedSymbols) {
327
+ for (const symbolToken of fileNode.importedSymbols) {
308
328
  if (typeof symbolToken !== 'string') continue;
309
329
 
310
330
  const splitIndex = symbolToken.indexOf(':');
@@ -314,31 +334,20 @@ export class RefactoringEngine {
314
334
  const symbolName = symbolToken.slice(splitIndex + 1);
315
335
 
316
336
  let targetFile = null;
317
- if (this.workspaceGraph && typeof this.workspaceGraph.isLocalWorkspaceSpecifier === 'function' && this.workspaceGraph.isLocalWorkspaceSpecifier(specifier)) {
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)) {
318
341
  const match = this.workspaceGraph.getWorkspacePackageMatch(specifier);
319
342
  if (match && match.entryPoints && match.entryPoints.length > 0) {
320
- targetFile = Array.isArray(match.entryPoints) ? match.entryPoints : match.entryPoints;
343
+ targetFile = match.entryPoints[0];
321
344
  }
322
345
  } else if (specifier.startsWith('.')) {
323
- targetFile = path.resolve(path.dirname(filePath), specifier);
324
-
325
- // šŸš€ COMPILE-TO-SOURCE EXTENSION SWAP:
326
- // If a barrel file imports relative paths using compiled targets (like './used-fn.js'),
327
- // replace the extension to check for active source components directly ('.ts' / '.tsx')
328
- if (targetFile.endsWith('.js')) {
329
- targetFile = targetFile.slice(0, -3);
330
- }
331
-
332
- if (!path.extname(targetFile)) {
333
- if (existsSync(targetFile + '.ts')) targetFile += '.ts';
334
- else if (existsSync(targetFile + '.tsx')) targetFile += '.tsx';
335
- else if (existsSync(targetFile + '.js')) targetFile += '.js';
336
- }
346
+ targetFile = this.resolver.resolveModulePath(filePath, specifier);
337
347
  }
338
348
 
339
349
  if (targetFile) {
340
- // Enforce uniform forward slash formats on targets
341
- const cleanTargetFile = Array.isArray(targetFile) ? slashify(targetFile[0]) : slashify(targetFile);
350
+ const cleanTargetFile = slashify(targetFile);
342
351
  this.context.importUsageRegistry.add(`${cleanTargetFile}:${symbolName}`);
343
352
  }
344
353
  }
@@ -478,16 +487,35 @@ export class RefactoringEngine {
478
487
  }
479
488
  if (isPackageEntryPoint) continue;
480
489
 
490
+ const originalNode = this.context.projectGraph.get(cleanExportedFile);
491
+ if (originalNode && originalNode.isLibraryEntry) continue;
492
+
493
+ const unusedExportsInThisFile = [];
494
+
481
495
  for (const symbol of exportsSet) {
482
496
  const consumptionToken = `${cleanExportedFile}:${symbol}`;
483
497
  if (!this.context.importUsageRegistry?.has(consumptionToken)) {
484
- analysisSummary.deadExports.push({
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({
485
501
  symbol: symbol,
486
502
  file: relativeExportedFile,
487
- line: 7
503
+ line: loc.line
488
504
  });
489
505
  }
490
506
  }
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
+ }
491
519
  }
492
520
  }
493
521
  analysisSummary.unlistedDependencies = this.context.unlistedDependencies || [];
@@ -592,40 +620,68 @@ export class RefactoringEngine {
592
620
  const ext = path.extname(entry.name);
593
621
  if (['.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte'].includes(ext) || entry.name === 'package.json') {
594
622
  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
+ }
595
631
  }
596
632
  }
597
633
  }
598
634
  }
599
635
 
600
636
  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
601
664
  for (const [filePath, node] of this.context.projectGraph.entries()) {
602
- // Pass A: Link all explicit imports (static + dynamic + re-export sources)
665
+ // A. Explicit Imports (Files)
603
666
  for (const specifier of node.explicitImports) {
604
667
  const resolvedPath = this.resolver.resolveModulePath(filePath, specifier);
605
668
  if (resolvedPath && this.context.projectGraph.has(resolvedPath)) {
606
- this.context.projectGraph.get(resolvedPath).incomingEdges.add(filePath);
669
+ const targetNode = this.context.projectGraph.get(resolvedPath);
670
+ targetNode.incomingEdges.add(filePath);
607
671
  node.outgoingEdges.add(resolvedPath);
608
672
 
609
- // Fix: Ensure all internal exports from a re-exported source are marked as used
610
- // so the source file itself is never considered orphaned.
611
- const targetNode = this.context.projectGraph.get(resolvedPath);
612
- const isReExport = Array.from(node.internalExports.values()).some(exp => exp.source === specifier);
613
- if (isReExport) {
614
- targetNode.isLibraryEntry = true; // Protect re-exported internal files
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;
615
680
  }
616
681
  }
617
682
  }
618
683
 
619
- // Pass A.2: Mark package entry points as library entries
620
- for (const pkg of this.workspaceGraph.packageManifests.values()) {
621
- for (const entryPath of pkg.entryPoints) {
622
- if (this.context.projectGraph.has(entryPath)) {
623
- this.context.projectGraph.get(entryPath).isLibraryEntry = true;
624
- }
625
- }
626
- }
627
-
628
- // Pass B: Link named-symbol imports through barrel/re-export chains
684
+ // B. Symbol-level Linking (Tracing through barrels)
629
685
  for (const specToken of node.importedSymbols) {
630
686
  const delimiterIndex = specToken.indexOf(':');
631
687
  if (delimiterIndex === -1) continue;
@@ -636,18 +692,55 @@ export class RefactoringEngine {
636
692
  if (!resolvedPath) continue;
637
693
 
638
694
  if (symbol === '*') {
639
- // Wildcard import / re-export-all: add a direct edge to the resolved file.
640
695
  if (this.context.projectGraph.has(resolvedPath)) {
641
696
  this.context.projectGraph.get(resolvedPath).incomingEdges.add(filePath);
642
697
  node.outgoingEdges.add(resolvedPath);
643
698
  }
644
699
  } else {
645
- // Named import: trace through barrel files to the actual declaration origin.
646
700
  const traceResolution = await this.barrelParser.determineSymbolDeclarationOrigin(resolvedPath, symbol, this.context.projectGraph);
647
701
  if (traceResolution && this.context.projectGraph.has(traceResolution.originFile)) {
648
- this.context.projectGraph.get(traceResolution.originFile).incomingEdges.add(filePath);
702
+ const originNode = this.context.projectGraph.get(traceResolution.originFile);
703
+ originNode.incomingEdges.add(filePath);
649
704
  node.outgoingEdges.add(traceResolution.originFile);
650
- node.importedSymbols.add(`${traceResolution.originFile}:${traceResolution.symbolName}`);
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
+ }
651
744
  }
652
745
  }
653
746
  }
@@ -0,0 +1,87 @@
1
+ import { parentPort, workerData } from 'worker_threads';
2
+ import fs from 'fs/promises';
3
+ import { ASTAnalyzer } from '../ast/ASTAnalyzer.js';
4
+ import { OxcAnalyzer } from '../ast/OxcAnalyzer.js';
5
+
6
+ /**
7
+ * Worker Thread Execution Script
8
+ * Handles parallel AST parsing for a chunk of files and returns serialized results.
9
+ */
10
+ async function runTask() {
11
+ const { files, contextOptions } = workerData;
12
+ const results = [];
13
+
14
+ // Create a minimal context for analyzers
15
+ const mockContext = {
16
+ verbose: contextOptions.verbose,
17
+ projectGraph: new Map(),
18
+ getOrCreateNode: (path) => ({
19
+ filePath: path,
20
+ explicitImports: new Set(),
21
+ dynamicImports: new Set(),
22
+ importedSymbols: new Set(),
23
+ rawStringReferences: new Set(),
24
+ instantiatedIdentifiers: new Set(),
25
+ propertyAccessChains: new Set(),
26
+ internalExports: new Map(),
27
+ securityThreats: [],
28
+ localSuppressedRules: new Set(),
29
+ externalPackageUsage: new Set(),
30
+ symbolSourceLocations: new Map(),
31
+ jsxComponents: new Set(),
32
+ jsxProps: new Set(),
33
+ decorators: new Set(),
34
+ isFrameworkContract: false
35
+ })
36
+ };
37
+
38
+ const astAnalyzer = new ASTAnalyzer(mockContext);
39
+ const oxcAnalyzer = new OxcAnalyzer(mockContext);
40
+
41
+ for (const filePath of files) {
42
+ try {
43
+ const content = await fs.readFile(filePath, 'utf8');
44
+ const node = mockContext.getOrCreateNode(filePath);
45
+
46
+ // Use OXC if available, fallback to TS AST
47
+ if (oxcAnalyzer.isAvailable) {
48
+ await oxcAnalyzer.parseFile(filePath, content, node);
49
+ } else {
50
+ await astAnalyzer.parseFile(filePath, content, node);
51
+ }
52
+
53
+ // Serialize the node data for transfer back to main thread
54
+ results.push({
55
+ filePath: node.filePath,
56
+ explicitImports: Array.from(node.explicitImports),
57
+ dynamicImports: Array.from(node.dynamicImports),
58
+ importedSymbols: Array.from(node.importedSymbols),
59
+ rawStringReferences: Array.from(node.rawStringReferences),
60
+ instantiatedIdentifiers: Array.from(node.instantiatedIdentifiers),
61
+ propertyAccessChains: Array.from(node.propertyAccessChains),
62
+ internalExports: Object.fromEntries(node.internalExports),
63
+ securityThreats: node.securityThreats,
64
+ localSuppressedRules: Array.from(node.localSuppressedRules),
65
+ externalPackageUsage: Array.from(node.externalPackageUsage),
66
+ symbolSourceLocations: Object.fromEntries(node.symbolSourceLocations),
67
+ jsxComponents: Array.from(node.jsxComponents),
68
+ jsxProps: Array.from(node.jsxProps),
69
+ decorators: Array.from(node.decorators),
70
+ isFrameworkContract: node.isFrameworkContract
71
+ });
72
+ } catch (err) {
73
+ if (contextOptions.verbose) {
74
+ console.error(`[Worker] Failed to parse ${filePath}: ${err.message}`);
75
+ }
76
+ // Push null to keep array indices if needed, or just skip
77
+ results.push(null);
78
+ }
79
+ }
80
+
81
+ parentPort.postMessage(results);
82
+ }
83
+
84
+ runTask().catch(err => {
85
+ console.error(`[Worker Critical Fault] ${err.stack}`);
86
+ process.exit(1);
87
+ });
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ============================================================================
3
- * Backend Services Plugins for entkapp v4.1.0
3
+ * Backend Services Plugins for entkapp v4.2.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.0.0
3
+ * Modern Frameworks Plugins for entkapp v4.2.0
4
4
  * ============================================================================
5
5
  * Built-in support for React, Vue, Svelte, and Angular.
6
6
  */