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,221 @@
1
+ import ts from 'typescript';
2
+ import fs from 'fs/promises';
3
+
4
+ /**
5
+ * Enterprise Wildcard Optimization & Flattening Layer for Barrel Operations
6
+ * Traces unreferenced paths through redistribution entries.
7
+ */
8
+ export class BarrelParser {
9
+ constructor(context, resolver) {
10
+ this.context = context;
11
+ this.resolver = resolver;
12
+ this.cachedSpecifications = new Map();
13
+ }
14
+
15
+ /**
16
+ * Compiles the structural export layout for a specific index or barrel target.
17
+ * @param {string} filePath - Absolute path to on-disk target element
18
+ */
19
+ async parseBarrelSpecification(filePath) {
20
+ if (this.cachedSpecifications.has(filePath)) {
21
+ return this.cachedSpecifications.get(filePath);
22
+ }
23
+
24
+ const specification = {
25
+ isBarrelInstance: false,
26
+ wildcardExports: new Set(), // export * from './module';
27
+ namespacedWildcardExports: new Map(), // export * as Utils from './module';
28
+ forwardedNamedExports: new Map(), // export { token as alias } from './module';
29
+ declaredLocalExports: new Set() // export const a = 1;
30
+ };
31
+
32
+ try {
33
+ const code = await fs.readFile(filePath, 'utf8');
34
+ const sourceFile = ts.createSourceFile(filePath, code, ts.ScriptTarget.Latest, true);
35
+
36
+ this.harvestExportSignatures(sourceFile, specification);
37
+
38
+ if (specification.wildcardExports.size > 0 ||
39
+ specification.namespacedWildcardExports.size > 0 ||
40
+ specification.forwardedNamedExports.size > 0) {
41
+ specification.isBarrelInstance = true;
42
+ }
43
+
44
+ this.cachedSpecifications.set(filePath, specification);
45
+ return specification;
46
+ } catch {
47
+ return specification; // Error state defaults to safe boundaries layout manifest
48
+ }
49
+ }
50
+
51
+ harvestExportSignatures(node, spec) {
52
+ if (!node) return;
53
+
54
+ switch (node.kind) {
55
+ case ts.SyntaxKind.ExportDeclaration: {
56
+ const targetSpecifier = node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)
57
+ ? node.moduleSpecifier.text
58
+ : null;
59
+
60
+ if (targetSpecifier) {
61
+ if (!node.exportClause) {
62
+ // Standard Wildcard Transfer: export * from './module';
63
+ spec.wildcardExports.add(targetSpecifier);
64
+ } else if (ts.isNamespaceExport(node.exportClause)) {
65
+ // Namespaced Wildcard Transfer: export * as Core from './module';
66
+ const namespaceAlias = node.exportClause.name.text;
67
+ spec.namespacedWildcardExports.set(namespaceAlias, targetSpecifier);
68
+ } else if (ts.isNamedExports(node.exportClause)) {
69
+ // Selective Re-export Forwarding: export { x, y as z } from './module';
70
+ node.exportClause.elements.forEach(element => {
71
+ const originalSymbol = element.propertyName ? element.propertyName.text : element.name.text;
72
+ const exposedAlias = element.name.text;
73
+ spec.forwardedNamedExports.set(exposedAlias, {
74
+ targetModule: targetSpecifier,
75
+ sourceSymbol: originalSymbol
76
+ });
77
+ });
78
+ }
79
+ }
80
+ break;
81
+ }
82
+
83
+ // Track items declared directly within the file boundary
84
+ case ts.SyntaxKind.FunctionDeclaration:
85
+ case ts.SyntaxKind.ClassDeclaration:
86
+ case ts.SyntaxKind.InterfaceDeclaration:
87
+ case ts.SyntaxKind.TypeAliasDeclaration:
88
+ case ts.SyntaxKind.EnumDeclaration: {
89
+ if (node.modifiers && node.modifiers.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) {
90
+ if (node.name && ts.isIdentifier(node.name)) {
91
+ spec.declaredLocalExports.add(node.name.text);
92
+ }
93
+ }
94
+ break;
95
+ }
96
+
97
+ case ts.SyntaxKind.VariableStatement: {
98
+ if (node.modifiers && node.modifiers.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) {
99
+ node.declarationList.declarations.forEach(decl => {
100
+ if (ts.isIdentifier(decl.name)) {
101
+ spec.declaredLocalExports.add(decl.name.text);
102
+ } else if (ts.isObjectBindingPattern(decl.name)) {
103
+ decl.name.elements.forEach(element => {
104
+ if (element.name && ts.isIdentifier(element.name)) {
105
+ spec.declaredLocalExports.add(element.name.text);
106
+ }
107
+ });
108
+ } else if (ts.isArrayBindingPattern(decl.name)) {
109
+ decl.name.elements.forEach(element => {
110
+ if (ts.isBindingElement(element) && element.name && ts.isIdentifier(element.name)) {
111
+ spec.declaredLocalExports.add(element.name.text);
112
+ }
113
+ });
114
+ }
115
+ });
116
+ }
117
+ break;
118
+ }
119
+
120
+ // Track default exports declared directly within the file boundary:
121
+ // Handles both `export default function foo()` and `export default expression`.
122
+ // The canonical symbol name stored is 'default' so that forwardedNamedExports
123
+ // entries whose sourceSymbol is 'default' can resolve correctly via Rule A.
124
+ case ts.SyntaxKind.ExportAssignment: {
125
+ // ExportAssignment covers `export default <expr>` (isExportEquals === false)
126
+ // as well as `export = <expr>` (isExportEquals === true, CommonJS style).
127
+ // We register 'default' for both to ensure the barrel tracer can settle here.
128
+ spec.declaredLocalExports.add('default');
129
+ break;
130
+ }
131
+ }
132
+
133
+ ts.forEachChild(node, child => this.harvestExportSignatures(child, spec));
134
+ }
135
+
136
+ /**
137
+ * Challenge #3 Resolution Logic. Unwraps nested redistribution links to find origin source nodes.
138
+ * @param {string} contextFilePath - Current position filename vector pointer
139
+ * @param {string} targetSymbolName - Targeted semantic variable signature name
140
+ * @param {Map} activeProjectGraph - Global active module maps directory registry
141
+ * @param {Set} [protectionStack] - Avoids cyclic validation traps inside self-referencing links
142
+ */
143
+ async determineSymbolDeclarationOrigin(contextFilePath, targetSymbolName, activeProjectGraph, protectionStack = new Set()) {
144
+ if (protectionStack.has(contextFilePath)) {
145
+ return { originFile: contextFilePath, originSymbol: targetSymbolName };
146
+ }
147
+ protectionStack.add(contextFilePath);
148
+
149
+ const spec = await this.parseBarrelSpecification(contextFilePath);
150
+ if (!spec.isBarrelInstance) {
151
+ return { originFile: contextFilePath, originSymbol: targetSymbolName };
152
+ }
153
+
154
+ // Rule A: Settle boundary immediately if local declaration matches token signature name
155
+ if (spec.declaredLocalExports.has(targetSymbolName)) {
156
+ return { originFile: contextFilePath, originSymbol: targetSymbolName };
157
+ }
158
+
159
+ // Rule B: Evaluate explicit named re-export mappings
160
+ if (spec.forwardedNamedExports.has(targetSymbolName)) {
161
+ const routingRule = spec.forwardedNamedExports.get(targetSymbolName);
162
+ const fullyResolvedPath = this.resolver.resolveModulePath(contextFilePath, routingRule.targetModule);
163
+ if (fullyResolvedPath) {
164
+ return this.determineSymbolDeclarationOrigin(fullyResolvedPath, routingRule.sourceSymbol, activeProjectGraph, protectionStack);
165
+ }
166
+ }
167
+
168
+ // Rule C: Evaluate structural namespace alias groupings (export * as name from 'module')
169
+ for (const [namespaceAlias, relativeModule] of spec.namespacedWildcardExports.entries()) {
170
+ // If the targetSymbolName is prefixed with the namespaceAlias, then it's a member of this namespace re-export
171
+ if (targetSymbolName.startsWith(`${namespaceAlias}.`)) {
172
+ const originalSymbol = targetSymbolName.substring(namespaceAlias.length + 1);
173
+ const fullyResolvedPath = this.resolver.resolveModulePath(contextFilePath, relativeModule);
174
+ if (fullyResolvedPath) {
175
+ return this.determineSymbolDeclarationOrigin(fullyResolvedPath, originalSymbol, activeProjectGraph, protectionStack);
176
+ }
177
+ }
178
+ }
179
+
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
+ for (const relativePath of spec.wildcardExports) {
191
+ const fullyResolvedPath = this.resolver.resolveModulePath(contextFilePath, relativePath);
192
+
193
+ if (fullyResolvedPath) {
194
+ const continuousResolutionTrace = await this.determineSymbolDeclarationOrigin(
195
+ fullyResolvedPath,
196
+ targetSymbolName,
197
+ activeProjectGraph,
198
+ new Set(protectionStack) // Use a copy so sibling branches don't block each other
199
+ );
200
+
201
+ if (!continuousResolutionTrace) continue;
202
+ if (continuousResolutionTrace.originFile === contextFilePath) continue;
203
+
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
+ const originSpec = await this.parseBarrelSpecification(continuousResolutionTrace.originFile);
208
+ if (originSpec.declaredLocalExports.has(continuousResolutionTrace.originSymbol)) {
209
+ return continuousResolutionTrace;
210
+ }
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
+ }
217
+ }
218
+
219
+ return { originFile: contextFilePath, originSymbol: targetSymbolName };
220
+ }
221
+ }
@@ -0,0 +1,73 @@
1
+ export class DeadCodeDetector {
2
+ constructor(context) {
3
+ this.context = context;
4
+ }
5
+
6
+ detectDeadCode(graph) {
7
+ const deadFiles = [];
8
+ const deadExports = [];
9
+
10
+ // Find all entry points
11
+ const entryPoints = new Set();
12
+ for (const [filePath, node] of graph.entries()) {
13
+ if (node.isEntry || node.isNextJsRoute || node.isSvelteComponent || node.isAstroPage) {
14
+ entryPoints.add(filePath);
15
+ }
16
+ }
17
+
18
+ // Traverse from entry points to find used files
19
+ const usedFiles = new Set();
20
+ const queue = Array.from(entryPoints);
21
+
22
+ while (queue.length > 0) {
23
+ const current = queue.shift();
24
+ if (!usedFiles.has(current)) {
25
+ usedFiles.add(current);
26
+ const node = graph.get(current);
27
+ if (node && node.outgoingEdges) {
28
+ for (const edge of node.outgoingEdges) {
29
+ queue.push(edge);
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ // Identify dead files
36
+ for (const filePath of graph.keys()) {
37
+ if (!usedFiles.has(filePath)) {
38
+ deadFiles.push(filePath);
39
+ }
40
+ }
41
+
42
+ // Identify dead exports in used files
43
+ for (const filePath of usedFiles) {
44
+ const node = graph.get(filePath);
45
+ if (!node) continue;
46
+
47
+ // If it's an entry point, we consider its exports used (unless strictly configured otherwise)
48
+ if (node.isEntry) continue;
49
+
50
+ for (const [exportName, exportInfo] of node.internalExports.entries()) {
51
+ if (exportName === '*' || exportName === 'default') continue; // Skip wildcards for now
52
+
53
+ let isUsed = false;
54
+ // Check incoming edges if they import this specific symbol
55
+ for (const [otherPath, otherNode] of graph.entries()) {
56
+ if (otherNode.outgoingEdges.has(filePath)) {
57
+ if (otherNode.importedSymbols.has(`${filePath}:${exportName}`) ||
58
+ otherNode.importedSymbols.has(`${filePath}:*`)) {
59
+ isUsed = true;
60
+ break;
61
+ }
62
+ }
63
+ }
64
+
65
+ if (!isUsed) {
66
+ deadExports.push({ file: filePath, symbol: exportName, line: exportInfo.start });
67
+ }
68
+ }
69
+ }
70
+
71
+ return { deadFiles, deadExports };
72
+ }
73
+ }
@@ -0,0 +1,203 @@
1
+ import path from 'path';
2
+ import fs from 'fs/promises';
3
+ import { PluginRegistry } from '../plugins/PluginRegistry.js';
4
+
5
+ /**
6
+ * Ecosystem Entry Point Manifest & Dynamic Framework Router Heuristic Validator
7
+ * Intercepts implicit conventions to handle cases where direct import statements are absent.
8
+ * Now refactored to use a pluggable architecture.
9
+ *
10
+ * Improvements over v1:
11
+ * - Extended config-file detection list (Biome, Oxlint, tsup, unbuild, etc.)
12
+ * - Next.js App Router conventions (page.tsx, layout.tsx, loading.tsx, error.tsx, etc.)
13
+ * - Remix conventions (route files under app/routes/)
14
+ * - SvelteKit conventions (+page.svelte, +layout.svelte, etc.)
15
+ * - Astro page/layout conventions
16
+ * - Common entry-point patterns (bin/, cli.ts, server.ts, main.ts, app.ts)
17
+ * - Test file patterns extended to cover Vitest workspace files
18
+ */
19
+ export class MagicDetector {
20
+ constructor(context) {
21
+ this.context = context;
22
+ this.registry = new PluginRegistry(context);
23
+ this.isInitialized = false;
24
+ }
25
+
26
+ async ensureInitialized(baseDir) {
27
+ if (this.isInitialized) return;
28
+ await this.registry.init(baseDir || process.cwd());
29
+ this.isInitialized = true;
30
+ }
31
+
32
+ /**
33
+ * Audits the project context to map active micro-framework ecosystems.
34
+ * @param {string} baseContextDirectory - Root file workspace context execution vector path
35
+ */
36
+ async identifyActiveProjectEcosystems(baseContextDirectory) {
37
+ await this.ensureInitialized(baseContextDirectory);
38
+ const activePlugins = await this.registry.getActivePlugins(baseContextDirectory);
39
+ const activeFrameworkFlags = activePlugins.map(p => p.name);
40
+
41
+ // Universal infrastructure overrides (testing platforms and common bundlers)
42
+ activeFrameworkFlags.push('universal-tooling-vectors');
43
+ return activeFrameworkFlags;
44
+ }
45
+
46
+ /**
47
+ * Assesses if a file path acts as an implicit route entry point.
48
+ */
49
+ async isImplicitlyRequiredByEcosystem(absolutePath, activeFrameworks, baseDir) {
50
+ await this.ensureInitialized();
51
+ const normalizedSystemPath = absolutePath.replace(/\\/g, '/');
52
+
53
+ const plugins = this.registry.getPlugins();
54
+ for (const plugin of plugins) {
55
+ if (activeFrameworks.includes(plugin.name)) {
56
+ const patterns = plugin.getRoutePatterns();
57
+ if (patterns.some(regex => regex.test(normalizedSystemPath))) {
58
+ return true;
59
+ }
60
+ }
61
+ }
62
+
63
+ // Apply baseline platform rules (Test suites, lint parameters, continuous integration files)
64
+ if (this.isCoreToolingSuiteElement(normalizedSystemPath)) {
65
+ return true;
66
+ }
67
+
68
+ return false;
69
+ }
70
+
71
+ isCoreToolingSuiteElement(normalizedPath) {
72
+ // ── Test / spec files ──────────────────────────────────────────────────────
73
+ if (/\.(test|spec|e2e|cy)\.(js|ts|tsx|jsx)$/i.test(normalizedPath)) return true;
74
+ if (/\.stories\.(js|ts|tsx|jsx)$/i.test(normalizedPath)) return true;
75
+
76
+ // ── Build / bundler config files ───────────────────────────────────────────
77
+ const configFragments = [
78
+ // Test runners
79
+ 'jest.config.', 'vitest.config.', 'vitest.workspace.',
80
+ 'playwright.config.', 'cypress.config.',
81
+ // Bundlers
82
+ 'webpack.config.', 'vite.config.', 'rollup.config.',
83
+ 'esbuild.config.', 'parcel.config.',
84
+ 'tsup.config.', 'unbuild.config.', 'pkgroll.config.',
85
+ // CSS / styling
86
+ 'tailwind.config.', 'postcss.config.', '.postcssrc.',
87
+ // Linters / formatters
88
+ '.eslintrc.', 'eslint.config.', 'prettier.config.', '.prettierrc.',
89
+ '.stylelintrc.', 'stylelint.config.',
90
+ 'biome.json', '.oxlintrc.',
91
+ // Babel / transpilation
92
+ '.babelrc.', 'babel.config.',
93
+ // Commit / git hooks
94
+ '.commitlintrc.', 'commitlint.config.',
95
+ '.lintstagedrc.', 'lint-staged.config.',
96
+ // Documentation
97
+ 'typedoc.config.', 'typedoc.json',
98
+ // Monorepo tools
99
+ 'turbo.json', 'nx.json', 'lerna.json',
100
+ // Misc tooling
101
+ 'knip.config.', 'knip.json',
102
+ 'syncpack.config.',
103
+ // Internal worker
104
+ 'WorkerTaskRunner.js'
105
+ ];
106
+ if (configFragments.some(f => normalizedPath.includes(f))) return true;
107
+
108
+ // ── Common application entry points ───────────────────────────────────────
109
+ const entryPatterns = [
110
+ // CLI binaries
111
+ '/bin/cli.js', '/bin/cli.ts', '/bin/cli.mjs',
112
+ '/bin/index.js', '/bin/index.ts',
113
+ // Server / app entry points (Reduced in v3.3.8 to avoid false positives in libraries)
114
+ '/src/main.ts', '/src/main.js',
115
+ '/src/app.ts', '/src/app.js',
116
+ '/src/api/HeadlessAPI.js', '/src/api/PluginSDK.js',
117
+ '/main.ts', '/main.js',
118
+ '/app.ts', '/app.js',
119
+ ];
120
+ if (entryPatterns.some(p => normalizedPath.endsWith(p))) return true;
121
+
122
+ // ── Next.js App Router conventions ────────────────────────────────────────
123
+ // Files under app/ directory with Next.js special names
124
+ if (/\/app\/(page|layout|loading|error|not-found|template|default|route|middleware)\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
125
+ // Next.js Pages Router
126
+ if (/\/pages\/.*\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
127
+ // Next.js API routes
128
+ if (/\/pages\/api\/.*\.(js|ts)$/.test(normalizedPath)) return true;
129
+ // Next.js middleware
130
+ if (/\/middleware\.(js|ts)$/.test(normalizedPath)) return true;
131
+ // Next.js config
132
+ if (/\/next\.config\.(js|ts|mjs|cjs)$/.test(normalizedPath)) return true;
133
+
134
+ // ── Remix conventions ─────────────────────────────────────────────────────
135
+ if (/\/app\/routes\/.*\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
136
+ if (/\/app\/root\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
137
+ if (/\/app\/entry\.(client|server)\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
138
+
139
+ // ── SvelteKit conventions ─────────────────────────────────────────────────
140
+ if (/\/\+page(\.(server|client))?\.(svelte|js|ts)$/.test(normalizedPath)) return true;
141
+ if (/\/\+layout(\.(server|client))?\.(svelte|js|ts)$/.test(normalizedPath)) return true;
142
+ if (/\/\+error\.(svelte|js|ts)$/.test(normalizedPath)) return true;
143
+ if (/\/\+server\.(js|ts)$/.test(normalizedPath)) return true;
144
+ if (/\/svelte\.config\.(js|ts|mjs)$/.test(normalizedPath)) return true;
145
+
146
+ // ── Astro conventions ─────────────────────────────────────────────────────
147
+ if (/\/src\/pages\/.*\.astro$/.test(normalizedPath)) return true;
148
+ if (/\/src\/layouts\/.*\.astro$/.test(normalizedPath)) return true;
149
+ if (/\/astro\.config\.(mjs|js|ts)$/.test(normalizedPath)) return true;
150
+
151
+ // ── Nuxt conventions ──────────────────────────────────────────────────────
152
+ if (/\/pages\/.*\.vue$/.test(normalizedPath)) return true;
153
+ if (/\/layouts\/.*\.vue$/.test(normalizedPath)) return true;
154
+ if (/\/server\/api\/.*\.(js|ts)$/.test(normalizedPath)) return true;
155
+ if (/\/nuxt\.config\.(js|ts|mjs)$/.test(normalizedPath)) return true;
156
+
157
+ // ── React / Vite entry points ─────────────────────────────────────────────
158
+ if (/\/vite\.config\.(js|ts|mjs)$/.test(normalizedPath)) return true;
159
+
160
+ // ── Angular conventions ───────────────────────────────────────────────────
161
+ if (/\/main\.(ts|js)$/.test(normalizedPath)) return true;
162
+ if (/\/app\.module\.(ts|js)$/.test(normalizedPath)) return true;
163
+ if (/\/angular\.json$/.test(normalizedPath)) return true;
164
+
165
+ // ── Expo / React Native ───────────────────────────────────────────────────
166
+ if (/\/app\/_layout\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
167
+ if (/\/app\/index\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
168
+
169
+ return false;
170
+ }
171
+
172
+ /**
173
+ * Challenge #4 Framework Overrides. Protects interface boundaries from false positive report flags.
174
+ */
175
+ async injectVirtualConsumerEdges(filePath, fileNode, activeFrameworks) {
176
+ await this.ensureInitialized();
177
+ if (!await this.isImplicitlyRequiredByEcosystem(filePath, activeFrameworks)) return;
178
+
179
+ // Retain entry point elements within memory to keep verification safe
180
+ fileNode.isLibraryEntry = true;
181
+
182
+ // Apply dynamic exports coverage metrics based on active platform contracts
183
+ const normalizedPath = filePath.replace(/\\/g, '/');
184
+ const plugins = this.registry.getPlugins();
185
+
186
+ for (const plugin of plugins) {
187
+ if (activeFrameworks.includes(plugin.name)) {
188
+ const patterns = plugin.getRoutePatterns();
189
+ const appliesToFramework = patterns.some(regex => regex.test(normalizedPath));
190
+
191
+ if (appliesToFramework) {
192
+ const contracts = plugin.getRequiredSystemContracts();
193
+ contracts.forEach(contractMethodToken => {
194
+ if (fileNode.internalExports.has(contractMethodToken)) {
195
+ // Emulate active local reference linkages to protect the export
196
+ fileNode.instantiatedIdentifiers.add(contractMethodToken);
197
+ }
198
+ });
199
+ }
200
+ }
201
+ }
202
+ }
203
+ }