entkapp 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/.scaffold-ignore +22 -0
  2. package/LICENSE +211 -0
  3. package/NOTICE +13 -0
  4. package/README.md +33 -0
  5. package/bin/cli.js +174 -0
  6. package/entkapp/config.json +42 -0
  7. package/entkapp/plugins/README.md +19 -0
  8. package/index.js +2254 -0
  9. package/logo.png +0 -0
  10. package/package.json +96 -0
  11. package/src/EngineContext.js +185 -0
  12. package/src/api/HeadlessAPI.js +376 -0
  13. package/src/api/PluginSDK.js +299 -0
  14. package/src/ast/ASTAnalyzer.js +492 -0
  15. package/src/ast/BarrelParser.js +221 -0
  16. package/src/ast/DeadCodeDetector.js +73 -0
  17. package/src/ast/MagicDetector.js +203 -0
  18. package/src/ast/OxcAnalyzer.js +337 -0
  19. package/src/ast/SecretScanner.js +304 -0
  20. package/src/healing/GitSandbox.js +82 -0
  21. package/src/healing/SelfHealer.js +52 -0
  22. package/src/index.js +723 -0
  23. package/src/performance/GraphCache.js +87 -0
  24. package/src/performance/SupplyChainGuard.js +92 -0
  25. package/src/performance/WorkerPool.js +109 -0
  26. package/src/plugins/BasePlugin.js +71 -0
  27. package/src/plugins/KnipAdapter.js +106 -0
  28. package/src/plugins/PluginRegistry.js +197 -0
  29. package/src/plugins/ecosystems/BackendServices.js +61 -0
  30. package/src/plugins/ecosystems/GenericPlugins.js +64 -0
  31. package/src/plugins/ecosystems/ModernFrameworks.js +159 -0
  32. package/src/plugins/ecosystems/MorePlugins.js +184 -0
  33. package/src/plugins/ecosystems/NextJsPlugin.js +33 -0
  34. package/src/plugins/ecosystems/PluginLoader.js +20 -0
  35. package/src/plugins/ecosystems/TypeScriptPlugin.js +56 -0
  36. package/src/refractor/ImpactAnalyzer.js +92 -0
  37. package/src/refractor/SourceRewriter.js +86 -0
  38. package/src/refractor/TransactionManager.js +138 -0
  39. package/src/refractor/TypeIntegrity.js +75 -0
  40. package/src/resolution/CircularDetector.js +91 -0
  41. package/src/resolution/ConfigLoader.js +87 -0
  42. package/src/resolution/DepencyResolver.js +133 -0
  43. package/src/resolution/DependencyProfiler.js +268 -0
  44. package/src/resolution/PathMapper.js +125 -0
  45. package/src/resolution/WorkSpaceGraph.js +474 -0
  46. package/tsconfig.json +26 -0
@@ -0,0 +1,86 @@
1
+ import ts from 'typescript';
2
+ import fs from 'fs/promises';
3
+
4
+ /**
5
+ * Format-Preserving Abstract Syntax Tree Source Mutation Engine
6
+ * Executes targeted updates using token position logic while preserving trivia (comments/JSDoc).
7
+ */
8
+ export class SourceRewriter {
9
+ constructor(context) {
10
+ this.context = context;
11
+ }
12
+
13
+ /**
14
+ * Removes a specific named export symbol declaration block from code text.
15
+ * @param {string} filePath - Absolute path to on-disk code component
16
+ * @param {string} symbolName - Target export key to prune
17
+ * @param {Object} exportMeta - Mapped token offset indices (start/end offsets)
18
+ */
19
+ async stripNamedExportSignature(filePath, symbolName, exportMeta) {
20
+ const rawSource = await fs.readFile(filePath, 'utf8');
21
+
22
+ // Case A: Token is grouped inside a multi-export destructured statement block: export { a, b, c };
23
+ if (exportMeta.type === 'named') {
24
+ const updatedText = this.pruneFromSharedExportClause(rawSource, symbolName, exportMeta);
25
+ if (updatedText) return updatedText;
26
+ }
27
+
28
+ // Case B: Isolated node removal. Calculate indices from start to end while preserving comments.
29
+ const startOffset = exportMeta.start;
30
+ const endOffset = exportMeta.end;
31
+
32
+ // Split source without dropping trailing line structural punctuation or text formatting configurations
33
+ const prefix = rawSource.slice(0, startOffset);
34
+ let suffix = rawSource.slice(endOffset);
35
+
36
+ // Clean up trailing commas or semicolons to prevent syntax errors
37
+ if (suffix.startsWith(';') || suffix.startsWith(',')) {
38
+ suffix = suffix.slice(1);
39
+ }
40
+
41
+ return prefix + suffix;
42
+ }
43
+
44
+ /**
45
+ * Handles selective pruning of export elements within inline groupings like `export { x, y as z }`.
46
+ */
47
+ pruneFromSharedExportClause(sourceText, symbolName, meta) {
48
+ // Find the enclosing export declaration node using structural boundaries
49
+ const sourceFile = ts.createSourceFile(
50
+ 'temp.ts',
51
+ sourceText,
52
+ ts.ScriptTarget.Latest,
53
+ true
54
+ );
55
+
56
+ let targetText = null;
57
+
58
+ const findAndPrune = (node) => {
59
+ if (ts.isExportDeclaration(node) && node.exportClause && ts.isNamedExports(node.exportClause)) {
60
+ const start = node.getStart(sourceFile);
61
+ const end = node.getEnd();
62
+
63
+ // Check if the target node spans the exact offset coordinates of the dead symbol
64
+ if (meta.start >= start && meta.end <= end) {
65
+ const activeElements = node.exportClause.elements;
66
+ const keptSymbols = activeElements
67
+ .filter(el => el.name.text !== symbolName)
68
+ .map(el => el.getText(sourceFile));
69
+
70
+ if (keptSymbols.length === 0) {
71
+ // Drop the entire declaration block if no active exports remain inside it
72
+ targetText = sourceText.slice(0, start) + sourceText.slice(end);
73
+ } else {
74
+ // Rewrite the export group inline, preserving formatting and comments
75
+ const reconstructedClause = `export { ${keptSymbols.join(', ')} };`;
76
+ targetText = sourceText.slice(0, start) + reconstructedClause + sourceText.slice(end);
77
+ }
78
+ }
79
+ }
80
+ ts.forEachChild(node, findAndPrune);
81
+ };
82
+
83
+ findAndPrune(sourceFile);
84
+ return targetText;
85
+ }
86
+ }
@@ -0,0 +1,138 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import crypto from 'crypto';
4
+
5
+ /**
6
+ * Transactional File System Operations Supervisor
7
+ * Guarantees atomicity across multi-file transformations by tracking rolling backups.
8
+ */
9
+ export class TransactionManager {
10
+ constructor(context) {
11
+ this.context = context;
12
+ // FIX: Ensure context.cwd is always defined, fallback to process.cwd()
13
+ const cwd = context?.cwd || process.cwd();
14
+ this.backupDirectory = path.join(cwd, 'backups');
15
+ this.journal = [];
16
+ this.isLocked = false;
17
+ }
18
+
19
+ /**
20
+ * Begins a new transaction lifecycle loop, initializing isolation vectors.
21
+ */
22
+ async begin() {
23
+ if (this.isLocked) {
24
+ throw new Error('Transaction Manager concurrency fault: Another transaction is already staged.');
25
+ }
26
+ this.isLocked = true;
27
+ this.journal = [];
28
+ await fs.mkdir(this.backupDirectory, { recursive: true });
29
+ }
30
+
31
+ /**
32
+ * Stages a destructive file modification or creation sequence.
33
+ * @param {string} filePath - Absolute system file target location
34
+ * @param {string} nextContent - The completely rewritten proposed source structure
35
+ */
36
+ async stageWrite(filePath, nextContent) {
37
+ this.assertLock();
38
+ let originalContent = null;
39
+ let backupPath = null;
40
+ let operationType = 'UPDATE';
41
+
42
+ try {
43
+ await fs.access(filePath);
44
+ originalContent = await fs.readFile(filePath, 'utf8');
45
+
46
+ const fileId = crypto.createHash('sha256').update(filePath).digest('hex');
47
+ backupPath = path.join(this.backupDirectory, `${fileId}.bak`);
48
+ await fs.writeFile(backupPath, originalContent, 'utf8');
49
+ } catch {
50
+ operationType = 'CREATE';
51
+ }
52
+
53
+ // Attempt target file mutation write directly to disk
54
+ await fs.writeFile(filePath, nextContent, 'utf8');
55
+
56
+ this.journal.push({
57
+ type: operationType,
58
+ targetFile: filePath,
59
+ backupLocation: backupPath
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Stages an element deletion sequence safely.
65
+ * @param {string} filePath - Target component being evaluated as dead
66
+ */
67
+ async stageDeletion(filePath) {
68
+ this.assertLock();
69
+
70
+ // Read previous state data for archiving before dropping linkage
71
+ const originalContent = await fs.readFile(filePath, 'utf8');
72
+ const fileId = crypto.createHash('sha256').update(filePath).digest('hex');
73
+ const backupPath = path.join(this.backupDirectory, `${fileId}.bak`);
74
+
75
+ await fs.writeFile(backupPath, originalContent, 'utf8');
76
+ await fs.unlink(filePath);
77
+
78
+ this.journal.push({
79
+ type: 'DELETE',
80
+ targetFile: filePath,
81
+ backupLocation: backupPath
82
+ });
83
+ }
84
+
85
+ /**
86
+ * Finalizes the transaction sequence, cleaning up local transaction cache points.
87
+ */
88
+ async commit() {
89
+ this.assertLock();
90
+ try {
91
+ for (const record of this.journal) {
92
+ if (record.backupLocation) {
93
+ await fs.unlink(record.backupLocation).catch(() => {});
94
+ }
95
+ }
96
+ } finally {
97
+ this.releaseLock();
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Reverts changes to their original states if any error or verification failure is flagged.
103
+ */
104
+ async rollback() {
105
+ this.assertLock();
106
+ try {
107
+ // Process journal logs in reverse execution order to preserve dependencies
108
+ for (let i = this.journal.length - 1; i >= 0; i--) {
109
+ const record = this.journal[i];
110
+
111
+ if (record.type === 'CREATE') {
112
+ await fs.unlink(record.targetFile).catch(() => {});
113
+ } else if (record.type === 'UPDATE' || record.type === 'DELETE') {
114
+ try {
115
+ const originalContent = await fs.readFile(record.backupLocation, 'utf8');
116
+ await fs.writeFile(record.targetFile, originalContent, 'utf8');
117
+ await fs.unlink(record.backupLocation).catch(() => {});
118
+ } catch (e) {
119
+ console.warn(`[Transaction] Rollback failed for ${record.targetFile}: ${e.message}`);
120
+ }
121
+ }
122
+ }
123
+ } finally {
124
+ this.releaseLock();
125
+ }
126
+ }
127
+
128
+ assertLock() {
129
+ if (!this.isLocked) {
130
+ throw new Error('Transaction Manager boundary violation: No active tracking block exists.');
131
+ }
132
+ }
133
+
134
+ releaseLock() {
135
+ this.isLocked = false;
136
+ this.journal = [];
137
+ }
138
+ }
@@ -0,0 +1,75 @@
1
+ import ts from 'typescript';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+
5
+ /**
6
+ * Ambient Type Declaration (`.d.ts`) Alignment Supervisor
7
+ * Updates declaration mapping entries to keep compiler checks stable.
8
+ */
9
+ export class TypeIntegrity {
10
+ constructor(context) {
11
+ this.context = context;
12
+ }
13
+
14
+ /**
15
+ * Re-evaluates ambient files to verify type alignment after optimization changes.
16
+ * @param {string} sourceFilePath - Absolute filename target context location
17
+ * @param {string} prunedSymbolName - Target variable token removed from active source graph
18
+ */
19
+ async synchronizeDeclarationFile(sourceFilePath, prunedSymbolName) {
20
+ const fileDirectory = path.dirname(sourceFilePath);
21
+ const baselineName = path.basename(sourceFilePath, path.extname(sourceFilePath));
22
+
23
+ // Map standard ambient types build output combinations
24
+ const declarationTargets = [
25
+ path.join(fileDirectory, `${baselineName}.d.ts`),
26
+ path.join(this.context.cwd, 'dist', `${baselineName}.d.ts`),
27
+ path.join(this.context.cwd, 'types', `${baselineName}.d.ts`)
28
+ ];
29
+
30
+ for (const dtsPath of declarationTargets) {
31
+ try {
32
+ await fs.access(dtsPath);
33
+ const code = await fs.readFile(dtsPath, 'utf8');
34
+
35
+ const sourceFile = ts.createSourceFile(
36
+ dtsPath,
37
+ code,
38
+ ts.ScriptTarget.Latest,
39
+ true,
40
+ ts.ScriptKind.TS
41
+ );
42
+
43
+ const replacementIntervals = [];
44
+ this.inspectDtsNodes(sourceFile, prunedSymbolName, replacementIntervals);
45
+
46
+ if (replacementIntervals.length > 0) {
47
+ let updatedDtsText = code;
48
+ // Apply substring text deletions from back to front to keep offset indexes accurate
49
+ replacementIntervals.sort((a, b) => b.start - a.start);
50
+
51
+ for (const interval of replacementIntervals) {
52
+ updatedDtsText = updatedDtsText.slice(0, interval.start) + updatedDtsText.slice(interval.end);
53
+ }
54
+
55
+ await fs.writeFile(dtsPath, updatedDtsText, 'utf8');
56
+ }
57
+ } catch {
58
+ // Declaration file variation target absent; proceed to fallback check routes
59
+ }
60
+ }
61
+ }
62
+
63
+ inspectDtsNodes(node, matchSymbol, intervals) {
64
+ if (!node) return;
65
+
66
+ // Match type mutations inside ambient structural parameters
67
+ if (ts.isExportSpecifier(node) && node.name.text === matchSymbol) {
68
+ intervals.push({ start: node.getStart(), end: node.getEnd() });
69
+ } else if ((ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isClassDeclaration(node) || ts.isFunctionDeclaration(node)) && node.name && node.name.text === matchSymbol) {
70
+ intervals.push({ start: node.getStart(), end: node.getEnd() });
71
+ }
72
+
73
+ ts.forEachChild(node, child => this.inspectDtsNodes(child, matchSymbol, intervals));
74
+ }
75
+ }
@@ -0,0 +1,91 @@
1
+ export class CircularDetector {
2
+ constructor(context) {
3
+ this.context = context;
4
+ this.cycles = [];
5
+ }
6
+
7
+ detectCycles(graph, context = null) {
8
+ if (context) this.context = context;
9
+ this.cwd = context?.cwd || this.context?.cwd || process.cwd();
10
+ this.cycles = [];
11
+
12
+ let index = 0;
13
+ const stack = [];
14
+ const indices = new Map();
15
+ const lowlink = new Map();
16
+ const onStack = new Set();
17
+
18
+ const strongconnect = (v) => {
19
+ indices.set(v, index);
20
+ lowlink.set(v, index);
21
+ index++;
22
+ stack.push(v);
23
+ onStack.add(v);
24
+
25
+ const node = graph.get(v);
26
+ if (node && node.outgoingEdges) {
27
+ for (const w of node.outgoingEdges) {
28
+ if (!indices.has(w)) {
29
+ strongconnect(w);
30
+ lowlink.set(v, Math.min(lowlink.get(v), lowlink.get(w)));
31
+ } else if (onStack.has(w)) {
32
+ lowlink.set(v, Math.min(lowlink.get(v), indices.get(w)));
33
+ }
34
+ }
35
+ }
36
+
37
+ if (lowlink.get(v) === indices.get(v)) {
38
+ const component = [];
39
+ let w;
40
+ do {
41
+ w = stack.pop();
42
+ onStack.delete(w);
43
+ component.push(w);
44
+ } while (w !== v);
45
+
46
+ if (component.length > 1) {
47
+ this.cycles.push(component.reverse());
48
+ } else {
49
+ const node = graph.get(v);
50
+ if (node && node.outgoingEdges && node.outgoingEdges.has(v)) {
51
+ this.cycles.push([v]);
52
+ }
53
+ }
54
+ }
55
+ };
56
+
57
+ for (const v of graph.keys()) {
58
+ if (!indices.has(v)) {
59
+ strongconnect(v);
60
+ }
61
+ }
62
+
63
+ return this.cycles;
64
+ }
65
+
66
+ formatCycles() {
67
+ return this.cycles.map(cycle => {
68
+ const paths = cycle.map(p => {
69
+ let rel = p.replace(this.context.cwd, '').replace(/^\//, '');
70
+ if (rel.includes(':\\')) rel = rel.split(':\\')[1] || rel;
71
+ return rel;
72
+ });
73
+ if (cycle.length === 1) return `${paths[0]} -> (self-loop)`;
74
+ return paths.join(' -> ') + ' -> ' + paths[0];
75
+ });
76
+ }
77
+
78
+ getCycleDetails() {
79
+ return this.cycles.map((cycle, idx) => ({
80
+ cycleId: idx + 1,
81
+ files: cycle.map(p => {
82
+ let rel = p.replace(this.context.cwd, '').replace(/^\//, '');
83
+ if (rel.includes(':\\')) rel = rel.split(':\\')[1] || rel;
84
+ return rel;
85
+ }),
86
+ length: cycle.length,
87
+ isSelfLoop: cycle.length === 1
88
+ }));
89
+ }
90
+ }
91
+ export default CircularDetector;
@@ -0,0 +1,87 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { pathToFileURL } from 'url';
4
+
5
+ /**
6
+ * Loads and parses entkapp configuration files.
7
+ * Supports scaffold.config.js, .scaffoldrc.json, and .scaffoldrc.
8
+ */
9
+ export class ConfigLoader {
10
+ constructor(context) {
11
+ this.context = context;
12
+ }
13
+
14
+ async loadConfig(projectRoot) {
15
+ const searchPaths = [
16
+ 'entkapp.json',
17
+ 'entkapp.jsonc',
18
+ '.entkapp.json',
19
+ '.entkapp.jsonc',
20
+ 'entkapp.js',
21
+ 'entkapp.ts',
22
+ 'entkapp/config.json',
23
+ 'scaffold.config.js',
24
+ 'scaffold.config.mjs',
25
+ '.scaffoldrc.json',
26
+ '.scaffoldrc'
27
+ ];
28
+
29
+ let config = this.getDefaultConfig();
30
+
31
+ // Try package.json first
32
+ try {
33
+ const pkgPath = path.join(projectRoot, 'package.json');
34
+ const pkgContent = await fs.readFile(pkgPath, 'utf8');
35
+ const pkg = JSON.parse(pkgContent);
36
+ if (pkg['entkapp']) {
37
+ Object.assign(config, pkg['entkapp']);
38
+ }
39
+ } catch (e) {
40
+ // ignore
41
+ }
42
+
43
+ for (const fileName of searchPaths) {
44
+ const fullPath = path.join(projectRoot, fileName);
45
+ try {
46
+ await fs.access(fullPath);
47
+
48
+ if (fileName.endsWith('.js') || fileName.endsWith('.mjs') || fileName.endsWith('.ts')) {
49
+ const module = await import(pathToFileURL(fullPath).href);
50
+ Object.assign(config, module.default || module);
51
+ break;
52
+ } else {
53
+ const content = await fs.readFile(fullPath, 'utf8');
54
+ // Very basic JSONC parsing (strip comments)
55
+ const stripped = content.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');
56
+ Object.assign(config, JSON.parse(stripped));
57
+ break;
58
+ }
59
+ } catch (e) {
60
+ continue;
61
+ }
62
+ }
63
+
64
+ return config;
65
+ }
66
+
67
+ getDefaultConfig() {
68
+ return {
69
+ entryPoints: ['src/index.ts', 'index.js', 'src/index.js', 'src/main.ts', 'src/main.js'],
70
+ exclude: [
71
+ 'node_modules/**',
72
+ 'dist/**',
73
+ 'build/**',
74
+ '**/*.test.ts',
75
+ '**/*.spec.ts',
76
+ '**/*.test.js',
77
+ '**/*.spec.js'
78
+ ],
79
+ plugins: [],
80
+ rules: {
81
+ 'no-unused-exports': 'error',
82
+ 'no-unused-vars': 'warn',
83
+ 'no-dead-code': 'error'
84
+ }
85
+ };
86
+ }
87
+ }
@@ -0,0 +1,133 @@
1
+ import resolve from 'enhanced-resolve';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * Industrial-Strength Module Resolution Supervisor
6
+ * Integrates path mapping and workspace topologies using enhanced-resolve.
7
+ */
8
+ export class DependencyResolver {
9
+ constructor(context, pathMapper, workspaceGraph) {
10
+ this.context = context;
11
+ this.pathMapper = pathMapper;
12
+ this.workspaceGraph = workspaceGraph;
13
+
14
+ // Instantiate production-grade enhanced-resolve workspace parameters
15
+ this.nativeResolver = resolve.create.sync({
16
+ conditionNames: ['import', 'module', 'require', 'node', 'types'],
17
+ // Extensions prioritized: TS then JS
18
+ extensions: ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs', '.json', '.vue'],
19
+ mainFields: ['module', 'main', 'types'],
20
+ exportsFields: ['exports'],
21
+ symlinks: true
22
+ });
23
+ }
24
+
25
+ /**
26
+ * Resolves a raw import string from a source file into an absolute file path.
27
+ * Handles the "trap" where .ts files import .js files that should resolve to .ts.
28
+ */
29
+ resolveModulePath(containingFile, importSpecifier) {
30
+ if (importSpecifier.startsWith('node:') || [
31
+ 'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'console', 'constants',
32
+ 'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'fs/promises', 'http', 'http2',
33
+ 'https', 'inspector', 'module', 'net', 'os', 'path', 'perf_hooks', 'process',
34
+ 'punycode', 'querystring', 'readline', 'repl', 'stream', 'string_decoder',
35
+ 'timers', 'tls', 'trace_events', 'tty', 'url', 'util', 'v8', 'vm', 'worker_threads', 'zlib'
36
+ ].includes(importSpecifier)) {
37
+ return null;
38
+ }
39
+
40
+ const containingDir = path.dirname(containingFile);
41
+
42
+ // --- NEW: Fix for JS-in-TS import trap ---
43
+ // If we are in a TS file and importing something ending in .js,
44
+ // we should try to resolve the .ts version first.
45
+ let effectiveSpecifier = importSpecifier;
46
+ const isTsFile = /\.(ts|tsx|mts|cts)$/.test(containingFile);
47
+ if (isTsFile && importSpecifier.endsWith('.js')) {
48
+ // Try replacing .js with .ts
49
+ const tsSpecifier = importSpecifier.replace(/\.js$/, '.ts');
50
+ try {
51
+ const resolvedTs = this.nativeResolver(containingDir, tsSpecifier);
52
+ if (this.isAbsoluteInternalPath(resolvedTs)) return resolvedTs;
53
+ } catch (e) {
54
+ // Fall back to original specifier if .ts version doesn't exist
55
+ }
56
+ }
57
+ // -----------------------------------------
58
+
59
+ // Rule A: Intercept and resolve local monorepo workspace cross-links
60
+ if (this.workspaceGraph.isLocalWorkspaceSpecifier(effectiveSpecifier)) {
61
+ const match = this.workspaceGraph.getWorkspacePackageMatch(effectiveSpecifier);
62
+ if (match) {
63
+ if (effectiveSpecifier === match.packageName) {
64
+ return match.entryPoints[0] || null;
65
+ }
66
+
67
+ const subPathOffset = effectiveSpecifier.slice(match.packageName.length + 1);
68
+ try {
69
+ return this.nativeResolver(match.rootDirectory, `./${subPathOffset}`);
70
+ } catch {
71
+ }
72
+ }
73
+ }
74
+
75
+ // Rule B: Intercept and expand path mapping aliases (@/*)
76
+ const aliasedCandidates = this.pathMapper.resolveCandidatePaths(effectiveSpecifier);
77
+ if (aliasedCandidates.length > 0) {
78
+ for (const candidate of aliasedCandidates) {
79
+ try {
80
+ const resolvedPath = this.nativeResolver(containingDir, candidate);
81
+ if (this.isAbsoluteInternalPath(resolvedPath)) {
82
+ return resolvedPath;
83
+ }
84
+ } catch {
85
+ }
86
+ }
87
+ }
88
+
89
+ // Rule C: Standard file system lookups
90
+ try {
91
+ const resolvedPath = this.nativeResolver(containingDir, effectiveSpecifier);
92
+ if (this.isAbsoluteInternalPath(resolvedPath)) {
93
+ return resolvedPath;
94
+ }
95
+ } catch (err) {
96
+ if (this.context.verbose) {
97
+ console.debug(`[Resolution Trace Skip] Specifier unresolvable: ${effectiveSpecifier} inside ${containingFile}`);
98
+ }
99
+ }
100
+
101
+ return null;
102
+ }
103
+
104
+ isAbsoluteInternalPath(resolvedPath) {
105
+ if (!resolvedPath) return false;
106
+ const normalized = resolvedPath.replace(/\\/g, '/');
107
+
108
+ if (normalized.includes('/node_modules/')) {
109
+ for (const [name, meta] of this.workspaceGraph.packageManifests.entries()) {
110
+ if (normalized.startsWith(meta.rootDirectory.replace(/\\/g, '/'))) {
111
+ return true;
112
+ }
113
+ }
114
+ return false;
115
+ }
116
+
117
+ return path.isAbsolute(resolvedPath);
118
+ }
119
+
120
+ determineIntentProfile(filePath, declaredExportsManifest) {
121
+ const fileName = path.basename(filePath);
122
+ const isPublicContractFile = /(^index|^public\-api|^entry)\.(ts|js|tsx|jsx)$/i.test(fileName);
123
+
124
+ if (isPublicContractFile) {
125
+ for (const [symbolKey, metadata] of declaredExportsManifest.entries()) {
126
+ metadata.isLibraryContract = true;
127
+ }
128
+ return 'LIBRARY_CONSUMPTION_TARGET';
129
+ }
130
+
131
+ return 'INTERNAL_CODEBASE_ELEMENT';
132
+ }
133
+ }