@grafema/core 0.1.1-alpha → 0.2.1-beta
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/dist/Orchestrator.d.ts +7 -0
- package/dist/Orchestrator.d.ts.map +1 -1
- package/dist/Orchestrator.js +25 -3
- package/dist/config/ConfigLoader.d.ts +18 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +65 -3
- package/dist/core/FileExplainer.d.ts +101 -0
- package/dist/core/FileExplainer.d.ts.map +1 -0
- package/dist/core/FileExplainer.js +139 -0
- package/dist/core/NodeFactory.d.ts +44 -5
- package/dist/core/NodeFactory.d.ts.map +1 -1
- package/dist/core/NodeFactory.js +52 -7
- package/dist/core/nodes/ArrayLiteralNode.d.ts.map +1 -1
- package/dist/core/nodes/ArrayLiteralNode.js +4 -2
- package/dist/core/nodes/BranchNode.d.ts +41 -0
- package/dist/core/nodes/BranchNode.d.ts.map +1 -0
- package/dist/core/nodes/BranchNode.js +82 -0
- package/dist/core/nodes/CallSiteNode.d.ts +2 -2
- package/dist/core/nodes/CallSiteNode.d.ts.map +1 -1
- package/dist/core/nodes/CallSiteNode.js +9 -5
- package/dist/core/nodes/CaseNode.d.ts +43 -0
- package/dist/core/nodes/CaseNode.d.ts.map +1 -0
- package/dist/core/nodes/CaseNode.js +81 -0
- package/dist/core/nodes/ClassNode.d.ts +2 -2
- package/dist/core/nodes/ClassNode.d.ts.map +1 -1
- package/dist/core/nodes/ClassNode.js +8 -4
- package/dist/core/nodes/ConstantNode.d.ts +2 -2
- package/dist/core/nodes/ConstantNode.d.ts.map +1 -1
- package/dist/core/nodes/ConstantNode.js +6 -4
- package/dist/core/nodes/ConstructorCallNode.d.ts +51 -0
- package/dist/core/nodes/ConstructorCallNode.d.ts.map +1 -0
- package/dist/core/nodes/ConstructorCallNode.js +171 -0
- package/dist/core/nodes/DatabaseQueryNode.d.ts +3 -2
- package/dist/core/nodes/DatabaseQueryNode.d.ts.map +1 -1
- package/dist/core/nodes/DatabaseQueryNode.js +5 -2
- package/dist/core/nodes/DecoratorNode.d.ts +2 -2
- package/dist/core/nodes/DecoratorNode.d.ts.map +1 -1
- package/dist/core/nodes/DecoratorNode.js +5 -3
- package/dist/core/nodes/EnumNode.d.ts +2 -2
- package/dist/core/nodes/EnumNode.d.ts.map +1 -1
- package/dist/core/nodes/EnumNode.js +5 -3
- package/dist/core/nodes/EventListenerNode.d.ts +4 -4
- package/dist/core/nodes/EventListenerNode.d.ts.map +1 -1
- package/dist/core/nodes/EventListenerNode.js +7 -4
- package/dist/core/nodes/ExportNode.d.ts +2 -2
- package/dist/core/nodes/ExportNode.d.ts.map +1 -1
- package/dist/core/nodes/ExportNode.js +8 -4
- package/dist/core/nodes/ExpressionNode.d.ts +2 -2
- package/dist/core/nodes/ExpressionNode.d.ts.map +1 -1
- package/dist/core/nodes/ExpressionNode.js +6 -4
- package/dist/core/nodes/ExternalModuleNode.d.ts +4 -0
- package/dist/core/nodes/ExternalModuleNode.d.ts.map +1 -1
- package/dist/core/nodes/ExternalModuleNode.js +10 -2
- package/dist/core/nodes/HttpRequestNode.d.ts +4 -4
- package/dist/core/nodes/HttpRequestNode.d.ts.map +1 -1
- package/dist/core/nodes/HttpRequestNode.js +7 -4
- package/dist/core/nodes/ImportNode.d.ts +10 -2
- package/dist/core/nodes/ImportNode.d.ts.map +1 -1
- package/dist/core/nodes/ImportNode.js +21 -4
- package/dist/core/nodes/InterfaceNode.d.ts +2 -2
- package/dist/core/nodes/InterfaceNode.d.ts.map +1 -1
- package/dist/core/nodes/InterfaceNode.js +5 -3
- package/dist/core/nodes/LiteralNode.d.ts +2 -2
- package/dist/core/nodes/LiteralNode.d.ts.map +1 -1
- package/dist/core/nodes/LiteralNode.js +6 -4
- package/dist/core/nodes/MethodCallNode.d.ts +2 -2
- package/dist/core/nodes/MethodCallNode.d.ts.map +1 -1
- package/dist/core/nodes/MethodCallNode.js +9 -5
- package/dist/core/nodes/MethodNode.d.ts +2 -2
- package/dist/core/nodes/MethodNode.d.ts.map +1 -1
- package/dist/core/nodes/MethodNode.js +8 -4
- package/dist/core/nodes/ObjectLiteralNode.d.ts.map +1 -1
- package/dist/core/nodes/ObjectLiteralNode.js +4 -2
- package/dist/core/nodes/ParameterNode.d.ts +2 -2
- package/dist/core/nodes/ParameterNode.d.ts.map +1 -1
- package/dist/core/nodes/ParameterNode.js +5 -3
- package/dist/core/nodes/TypeNode.d.ts +2 -2
- package/dist/core/nodes/TypeNode.d.ts.map +1 -1
- package/dist/core/nodes/TypeNode.js +5 -3
- package/dist/core/nodes/VariableDeclarationNode.d.ts +2 -2
- package/dist/core/nodes/VariableDeclarationNode.d.ts.map +1 -1
- package/dist/core/nodes/VariableDeclarationNode.js +9 -5
- package/dist/core/nodes/index.d.ts +3 -0
- package/dist/core/nodes/index.d.ts.map +1 -1
- package/dist/core/nodes/index.js +3 -0
- package/dist/data/builtins/BuiltinRegistry.d.ts +78 -0
- package/dist/data/builtins/BuiltinRegistry.d.ts.map +1 -0
- package/dist/data/builtins/BuiltinRegistry.js +110 -0
- package/dist/data/builtins/definitions.d.ts +28 -0
- package/dist/data/builtins/definitions.d.ts.map +1 -0
- package/dist/data/builtins/definitions.js +250 -0
- package/dist/data/builtins/index.d.ts +10 -0
- package/dist/data/builtins/index.d.ts.map +1 -0
- package/dist/data/builtins/index.js +8 -0
- package/dist/data/builtins/jsGlobals.d.ts +18 -0
- package/dist/data/builtins/jsGlobals.d.ts.map +1 -0
- package/dist/data/builtins/jsGlobals.js +26 -0
- package/dist/data/builtins/types.d.ts +34 -0
- package/dist/data/builtins/types.d.ts.map +1 -0
- package/dist/data/builtins/types.js +7 -0
- package/dist/data/globals/definitions.d.ts +27 -0
- package/dist/data/globals/definitions.d.ts.map +1 -0
- package/dist/data/globals/definitions.js +117 -0
- package/dist/data/globals/index.d.ts +36 -0
- package/dist/data/globals/index.d.ts.map +1 -0
- package/dist/data/globals/index.js +52 -0
- package/dist/diagnostics/DiagnosticReporter.d.ts +23 -0
- package/dist/diagnostics/DiagnosticReporter.d.ts.map +1 -1
- package/dist/diagnostics/DiagnosticReporter.js +88 -0
- package/dist/diagnostics/index.d.ts +1 -1
- package/dist/diagnostics/index.d.ts.map +1 -1
- package/dist/errors/GrafemaError.d.ts +43 -0
- package/dist/errors/GrafemaError.d.ts.map +1 -1
- package/dist/errors/GrafemaError.js +50 -0
- package/dist/index.d.ts +17 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/plugins/analysis/DatabaseAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/DatabaseAnalyzer.js +3 -2
- package/dist/plugins/analysis/ExpressAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ExpressAnalyzer.js +3 -1
- package/dist/plugins/analysis/ExpressResponseAnalyzer.d.ts +148 -0
- package/dist/plugins/analysis/ExpressResponseAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/ExpressResponseAnalyzer.js +495 -0
- package/dist/plugins/analysis/ExpressRouteAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ExpressRouteAnalyzer.js +53 -18
- package/dist/plugins/analysis/FetchAnalyzer.d.ts +40 -0
- package/dist/plugins/analysis/FetchAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/FetchAnalyzer.js +163 -15
- package/dist/plugins/analysis/JSASTAnalyzer.d.ts +157 -26
- package/dist/plugins/analysis/JSASTAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/JSASTAnalyzer.js +2418 -191
- package/dist/plugins/analysis/RustAnalyzer.js +4 -4
- package/dist/plugins/analysis/SQLiteAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/SQLiteAnalyzer.js +4 -2
- package/dist/plugins/analysis/SocketIOAnalyzer.d.ts +9 -0
- package/dist/plugins/analysis/SocketIOAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/SocketIOAnalyzer.js +91 -7
- package/dist/plugins/analysis/ast/GraphBuilder.d.ts +173 -0
- package/dist/plugins/analysis/ast/GraphBuilder.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/GraphBuilder.js +1256 -65
- package/dist/plugins/analysis/ast/types.d.ts +294 -0
- package/dist/plugins/analysis/ast/types.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts +5 -1
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts +1 -0
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js +12 -1
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts +10 -0
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js +62 -0
- package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.d.ts +4 -0
- package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.js +101 -0
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts +16 -1
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.js +233 -39
- package/dist/plugins/discovery/WorkspaceDiscovery.d.ts.map +1 -1
- package/dist/plugins/discovery/WorkspaceDiscovery.js +9 -4
- package/dist/plugins/enrichment/AliasTracker.d.ts.map +1 -1
- package/dist/plugins/enrichment/AliasTracker.js +16 -1
- package/dist/plugins/enrichment/ArgumentParameterLinker.d.ts +32 -0
- package/dist/plugins/enrichment/ArgumentParameterLinker.d.ts.map +1 -0
- package/dist/plugins/enrichment/ArgumentParameterLinker.js +175 -0
- package/dist/plugins/enrichment/ClosureCaptureEnricher.d.ts +51 -0
- package/dist/plugins/enrichment/ClosureCaptureEnricher.d.ts.map +1 -0
- package/dist/plugins/enrichment/ClosureCaptureEnricher.js +205 -0
- package/dist/plugins/enrichment/ExternalCallResolver.d.ts +42 -0
- package/dist/plugins/enrichment/ExternalCallResolver.d.ts.map +1 -0
- package/dist/plugins/enrichment/ExternalCallResolver.js +213 -0
- package/dist/plugins/enrichment/FunctionCallResolver.d.ts +58 -0
- package/dist/plugins/enrichment/FunctionCallResolver.d.ts.map +1 -0
- package/dist/plugins/enrichment/FunctionCallResolver.js +340 -0
- package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts +16 -3
- package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts.map +1 -1
- package/dist/plugins/enrichment/HTTPConnectionEnricher.js +64 -20
- package/dist/plugins/enrichment/MethodCallResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/MethodCallResolver.js +15 -1
- package/dist/plugins/enrichment/MountPointResolver.d.ts +14 -12
- package/dist/plugins/enrichment/MountPointResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/MountPointResolver.js +172 -151
- package/dist/plugins/enrichment/NodejsBuiltinsResolver.d.ts +44 -0
- package/dist/plugins/enrichment/NodejsBuiltinsResolver.d.ts.map +1 -0
- package/dist/plugins/enrichment/NodejsBuiltinsResolver.js +271 -0
- package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts +5 -27
- package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts.map +1 -1
- package/dist/plugins/enrichment/ValueDomainAnalyzer.js +62 -139
- package/dist/plugins/indexing/JSModuleIndexer.d.ts +15 -0
- package/dist/plugins/indexing/JSModuleIndexer.d.ts.map +1 -1
- package/dist/plugins/indexing/JSModuleIndexer.js +58 -0
- package/dist/plugins/indexing/RustModuleIndexer.d.ts +1 -1
- package/dist/plugins/indexing/RustModuleIndexer.js +4 -4
- package/dist/plugins/validation/BrokenImportValidator.d.ts +31 -0
- package/dist/plugins/validation/BrokenImportValidator.d.ts.map +1 -0
- package/dist/plugins/validation/BrokenImportValidator.js +249 -0
- package/dist/plugins/validation/CallResolverValidator.d.ts +21 -10
- package/dist/plugins/validation/CallResolverValidator.d.ts.map +1 -1
- package/dist/plugins/validation/CallResolverValidator.js +101 -76
- package/dist/plugins/validation/DataFlowValidator.d.ts.map +1 -1
- package/dist/plugins/validation/DataFlowValidator.js +49 -41
- package/dist/plugins/validation/GraphConnectivityValidator.d.ts.map +1 -1
- package/dist/plugins/validation/GraphConnectivityValidator.js +25 -1
- package/dist/plugins/validation/SQLInjectionValidator.d.ts.map +1 -1
- package/dist/plugins/validation/SQLInjectionValidator.js +2 -3
- package/dist/queries/findCallsInFunction.d.ts +52 -0
- package/dist/queries/findCallsInFunction.d.ts.map +1 -0
- package/dist/queries/findCallsInFunction.js +135 -0
- package/dist/queries/findContainingFunction.d.ts +45 -0
- package/dist/queries/findContainingFunction.d.ts.map +1 -0
- package/dist/queries/findContainingFunction.js +54 -0
- package/dist/queries/index.d.ts +14 -0
- package/dist/queries/index.d.ts.map +1 -0
- package/dist/queries/index.js +11 -0
- package/dist/queries/traceValues.d.ts +70 -0
- package/dist/queries/traceValues.d.ts.map +1 -0
- package/dist/queries/traceValues.js +299 -0
- package/dist/queries/types.d.ts +163 -0
- package/dist/queries/types.d.ts.map +1 -0
- package/dist/queries/types.js +9 -0
- package/dist/schema/GraphSchemaExtractor.d.ts +53 -0
- package/dist/schema/GraphSchemaExtractor.d.ts.map +1 -0
- package/dist/schema/GraphSchemaExtractor.js +124 -0
- package/dist/schema/InterfaceSchemaExtractor.d.ts +73 -0
- package/dist/schema/InterfaceSchemaExtractor.d.ts.map +1 -0
- package/dist/schema/InterfaceSchemaExtractor.js +112 -0
- package/dist/schema/index.d.ts +5 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +2 -0
- package/dist/storage/backends/RFDBServerBackend.d.ts +13 -18
- package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -1
- package/dist/storage/backends/RFDBServerBackend.js +47 -51
- package/dist/storage/backends/typeValidation.d.ts.map +1 -1
- package/dist/storage/backends/typeValidation.js +1 -0
- package/package.json +3 -3
- package/src/Orchestrator.ts +35 -3
- package/src/config/ConfigLoader.ts +94 -3
- package/src/core/FileExplainer.ts +179 -0
- package/src/core/NodeFactory.ts +72 -8
- package/src/core/nodes/ArrayLiteralNode.ts +3 -2
- package/src/core/nodes/BranchNode.ts +113 -0
- package/src/core/nodes/CallSiteNode.ts +7 -5
- package/src/core/nodes/CaseNode.ts +123 -0
- package/src/core/nodes/ClassNode.ts +6 -4
- package/src/core/nodes/ConstantNode.ts +5 -4
- package/src/core/nodes/ConstructorCallNode.ts +217 -0
- package/src/core/nodes/DatabaseQueryNode.ts +5 -1
- package/src/core/nodes/DecoratorNode.ts +4 -3
- package/src/core/nodes/EnumNode.ts +4 -3
- package/src/core/nodes/EventListenerNode.ts +7 -4
- package/src/core/nodes/ExportNode.ts +6 -4
- package/src/core/nodes/ExpressionNode.ts +5 -4
- package/src/core/nodes/ExternalModuleNode.ts +11 -2
- package/src/core/nodes/HttpRequestNode.ts +7 -4
- package/src/core/nodes/ImportNode.ts +31 -4
- package/src/core/nodes/InterfaceNode.ts +4 -3
- package/src/core/nodes/LiteralNode.ts +5 -4
- package/src/core/nodes/MethodCallNode.ts +7 -5
- package/src/core/nodes/MethodNode.ts +6 -4
- package/src/core/nodes/ObjectLiteralNode.ts +3 -2
- package/src/core/nodes/ParameterNode.ts +4 -3
- package/src/core/nodes/TypeNode.ts +4 -3
- package/src/core/nodes/VariableDeclarationNode.ts +7 -5
- package/src/core/nodes/index.ts +3 -0
- package/src/data/builtins/BuiltinRegistry.ts +124 -0
- package/src/data/builtins/definitions.ts +267 -0
- package/src/data/builtins/index.ts +10 -0
- package/src/data/builtins/jsGlobals.ts +28 -0
- package/src/data/builtins/types.ts +36 -0
- package/src/data/globals/definitions.ts +156 -0
- package/src/data/globals/index.ts +66 -0
- package/src/diagnostics/DiagnosticReporter.ts +120 -0
- package/src/diagnostics/index.ts +1 -1
- package/src/errors/GrafemaError.ts +65 -0
- package/src/index.ts +45 -0
- package/src/plugins/analysis/DatabaseAnalyzer.ts +4 -2
- package/src/plugins/analysis/ExpressAnalyzer.ts +5 -1
- package/src/plugins/analysis/ExpressResponseAnalyzer.ts +636 -0
- package/src/plugins/analysis/ExpressRouteAnalyzer.ts +57 -18
- package/src/plugins/analysis/FetchAnalyzer.ts +204 -16
- package/src/plugins/analysis/JSASTAnalyzer.ts +2958 -260
- package/src/plugins/analysis/RustAnalyzer.ts +4 -4
- package/src/plugins/analysis/SQLiteAnalyzer.ts +5 -2
- package/src/plugins/analysis/SocketIOAnalyzer.ts +121 -7
- package/src/plugins/analysis/ast/GraphBuilder.ts +1578 -70
- package/src/plugins/analysis/ast/types.ts +387 -0
- package/src/plugins/analysis/ast/visitors/ASTVisitor.ts +8 -0
- package/src/plugins/analysis/ast/visitors/CallExpressionVisitor.ts +16 -1
- package/src/plugins/analysis/ast/visitors/FunctionVisitor.ts +77 -2
- package/src/plugins/analysis/ast/visitors/ImportExportVisitor.ts +112 -1
- package/src/plugins/analysis/ast/visitors/VariableVisitor.ts +272 -47
- package/src/plugins/discovery/WorkspaceDiscovery.ts +11 -4
- package/src/plugins/enrichment/AliasTracker.ts +22 -1
- package/src/plugins/enrichment/ArgumentParameterLinker.ts +240 -0
- package/src/plugins/enrichment/ClosureCaptureEnricher.ts +267 -0
- package/src/plugins/enrichment/ExternalCallResolver.ts +262 -0
- package/src/plugins/enrichment/FunctionCallResolver.ts +456 -0
- package/src/plugins/enrichment/HTTPConnectionEnricher.ts +70 -20
- package/src/plugins/enrichment/MethodCallResolver.ts +21 -1
- package/src/plugins/enrichment/MountPointResolver.ts +206 -198
- package/src/plugins/enrichment/NodejsBuiltinsResolver.ts +365 -0
- package/src/plugins/enrichment/ValueDomainAnalyzer.ts +67 -184
- package/src/plugins/indexing/JSModuleIndexer.ts +66 -0
- package/src/plugins/indexing/RustModuleIndexer.ts +4 -4
- package/src/plugins/validation/BrokenImportValidator.ts +325 -0
- package/src/plugins/validation/CallResolverValidator.ts +129 -109
- package/src/plugins/validation/DataFlowValidator.ts +75 -58
- package/src/plugins/validation/GraphConnectivityValidator.ts +39 -1
- package/src/plugins/validation/SQLInjectionValidator.ts +2 -5
- package/src/queries/README.md +46 -0
- package/src/queries/findCallsInFunction.ts +206 -0
- package/src/queries/findContainingFunction.ts +83 -0
- package/src/queries/index.ts +23 -0
- package/src/queries/traceValues.ts +398 -0
- package/src/queries/types.ts +187 -0
- package/src/schema/GraphSchemaExtractor.ts +177 -0
- package/src/schema/InterfaceSchemaExtractor.ts +173 -0
- package/src/schema/index.ts +5 -0
- package/src/storage/backends/RFDBServerBackend.ts +64 -68
- package/src/storage/backends/typeValidation.ts +1 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExternalCallResolver - creates CALLS edges for external package calls (REG-226)
|
|
3
|
+
*
|
|
4
|
+
* This enrichment plugin runs AFTER FunctionCallResolver (priority 70 vs 80) and:
|
|
5
|
+
* 1. Finds CALL nodes without CALLS edges (excluding method calls)
|
|
6
|
+
* 2. For each, looks for IMPORT with matching local name in same file
|
|
7
|
+
* 3. If import source is non-relative (external package), creates CALLS edge to EXTERNAL_MODULE
|
|
8
|
+
* 4. Recognizes JS built-in global functions (no edge needed, just counts them)
|
|
9
|
+
*
|
|
10
|
+
* CREATES NODES:
|
|
11
|
+
* - EXTERNAL_MODULE (for external packages like lodash, @tanstack/react-query)
|
|
12
|
+
*
|
|
13
|
+
* CREATES EDGES:
|
|
14
|
+
* - CALL -> CALLS -> EXTERNAL_MODULE (for imported external functions)
|
|
15
|
+
*
|
|
16
|
+
* Architecture:
|
|
17
|
+
* - Runs after FunctionCallResolver handles relative imports
|
|
18
|
+
* - Skips method calls (have 'object' attribute)
|
|
19
|
+
* - Skips already resolved calls (have CALLS edge)
|
|
20
|
+
* - Creates EXTERNAL_MODULE nodes lazily (only when needed)
|
|
21
|
+
* - Uses import index for O(1) lookups
|
|
22
|
+
*/
|
|
23
|
+
import { Plugin, createSuccessResult } from '../Plugin.js';
|
|
24
|
+
import { JS_GLOBAL_FUNCTIONS } from '../../data/builtins/index.js';
|
|
25
|
+
// === PLUGIN CLASS ===
|
|
26
|
+
export class ExternalCallResolver extends Plugin {
|
|
27
|
+
get metadata() {
|
|
28
|
+
return {
|
|
29
|
+
name: 'ExternalCallResolver',
|
|
30
|
+
phase: 'ENRICHMENT',
|
|
31
|
+
priority: 70, // After FunctionCallResolver (80)
|
|
32
|
+
creates: {
|
|
33
|
+
nodes: ['EXTERNAL_MODULE'],
|
|
34
|
+
edges: ['CALLS']
|
|
35
|
+
},
|
|
36
|
+
dependencies: ['FunctionCallResolver'] // Requires relative imports to be resolved first
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async execute(context) {
|
|
40
|
+
const { graph, onProgress } = context;
|
|
41
|
+
const logger = this.log(context);
|
|
42
|
+
logger.info('Starting external call resolution');
|
|
43
|
+
const startTime = Date.now();
|
|
44
|
+
// Step 1: Build Import Index - Map<file:local, ImportNode>
|
|
45
|
+
// Only index non-relative imports (external packages)
|
|
46
|
+
const importIndex = new Map();
|
|
47
|
+
for await (const node of graph.queryNodes({ nodeType: 'IMPORT' })) {
|
|
48
|
+
const imp = node;
|
|
49
|
+
if (!imp.file || !imp.local || !imp.source)
|
|
50
|
+
continue;
|
|
51
|
+
// Only index external imports (non-relative)
|
|
52
|
+
const isRelative = imp.source.startsWith('./') || imp.source.startsWith('../');
|
|
53
|
+
if (isRelative)
|
|
54
|
+
continue;
|
|
55
|
+
const key = `${imp.file}:${imp.local}`;
|
|
56
|
+
importIndex.set(key, imp);
|
|
57
|
+
}
|
|
58
|
+
logger.debug('Indexed external imports', { count: importIndex.size });
|
|
59
|
+
// Step 2: Collect unresolved CALL nodes (excluding method calls)
|
|
60
|
+
const callsToProcess = [];
|
|
61
|
+
for await (const node of graph.queryNodes({ nodeType: 'CALL' })) {
|
|
62
|
+
const call = node;
|
|
63
|
+
// Skip method calls (have object attribute)
|
|
64
|
+
if (call.object)
|
|
65
|
+
continue;
|
|
66
|
+
// Skip if already has CALLS edge
|
|
67
|
+
const existingEdges = await graph.getOutgoingEdges(call.id, ['CALLS']);
|
|
68
|
+
if (existingEdges.length > 0)
|
|
69
|
+
continue;
|
|
70
|
+
callsToProcess.push(call);
|
|
71
|
+
}
|
|
72
|
+
logger.info('Found calls to process', { count: callsToProcess.length });
|
|
73
|
+
// Step 3: Track created EXTERNAL_MODULE nodes to avoid duplicates
|
|
74
|
+
const createdExternalModules = new Set();
|
|
75
|
+
// Pre-check existing EXTERNAL_MODULE nodes
|
|
76
|
+
for await (const node of graph.queryNodes({ nodeType: 'EXTERNAL_MODULE' })) {
|
|
77
|
+
createdExternalModules.add(node.id);
|
|
78
|
+
}
|
|
79
|
+
logger.debug('Existing EXTERNAL_MODULE nodes', { count: createdExternalModules.size });
|
|
80
|
+
// Step 4: Resolution
|
|
81
|
+
let nodesCreated = 0;
|
|
82
|
+
let edgesCreated = 0;
|
|
83
|
+
let callsProcessed = 0;
|
|
84
|
+
let externalResolved = 0;
|
|
85
|
+
let builtinResolved = 0;
|
|
86
|
+
const unresolvedByReason = {
|
|
87
|
+
unknown: 0,
|
|
88
|
+
dynamic: 0
|
|
89
|
+
};
|
|
90
|
+
for (const callNode of callsToProcess) {
|
|
91
|
+
callsProcessed++;
|
|
92
|
+
// Progress reporting
|
|
93
|
+
if (onProgress && callsProcessed % 100 === 0) {
|
|
94
|
+
onProgress({
|
|
95
|
+
phase: 'enrichment',
|
|
96
|
+
currentPlugin: 'ExternalCallResolver',
|
|
97
|
+
message: `Processing calls ${callsProcessed}/${callsToProcess.length}`,
|
|
98
|
+
totalFiles: callsToProcess.length,
|
|
99
|
+
processedFiles: callsProcessed
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const calledName = callNode.name;
|
|
103
|
+
const file = callNode.file;
|
|
104
|
+
if (!calledName || !file) {
|
|
105
|
+
unresolvedByReason.unknown++;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
// Step 4.1: Check if this is a JS builtin
|
|
109
|
+
if (JS_GLOBAL_FUNCTIONS.has(calledName)) {
|
|
110
|
+
builtinResolved++;
|
|
111
|
+
continue; // No edge needed, just count it
|
|
112
|
+
}
|
|
113
|
+
// Step 4.2: Check if this is a dynamic call
|
|
114
|
+
if (callNode.isDynamic) {
|
|
115
|
+
unresolvedByReason.dynamic++;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
// Step 4.3: Find matching import in same file
|
|
119
|
+
const importKey = `${file}:${calledName}`;
|
|
120
|
+
const imp = importIndex.get(importKey);
|
|
121
|
+
if (!imp) {
|
|
122
|
+
// No import found - this is an unknown call
|
|
123
|
+
unresolvedByReason.unknown++;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
// Step 4.4: Extract package name from import source
|
|
127
|
+
const packageName = this.extractPackageName(imp.source);
|
|
128
|
+
if (!packageName) {
|
|
129
|
+
unresolvedByReason.unknown++;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
// Step 4.5: Create or reuse EXTERNAL_MODULE node
|
|
133
|
+
const externalModuleId = `EXTERNAL_MODULE:${packageName}`;
|
|
134
|
+
if (!createdExternalModules.has(externalModuleId)) {
|
|
135
|
+
// Check if node already exists in graph
|
|
136
|
+
const existingNode = await graph.getNode(externalModuleId);
|
|
137
|
+
if (!existingNode) {
|
|
138
|
+
await graph.addNode({
|
|
139
|
+
id: externalModuleId,
|
|
140
|
+
type: 'EXTERNAL_MODULE',
|
|
141
|
+
name: packageName,
|
|
142
|
+
file: '',
|
|
143
|
+
line: 0
|
|
144
|
+
});
|
|
145
|
+
nodesCreated++;
|
|
146
|
+
}
|
|
147
|
+
createdExternalModules.add(externalModuleId);
|
|
148
|
+
}
|
|
149
|
+
// Step 4.6: Create CALLS edge with metadata
|
|
150
|
+
// Use 'imported' field for exportedName (the original name in source module)
|
|
151
|
+
// For default imports, 'imported' is 'default'
|
|
152
|
+
// For named imports with alias, 'imported' is the original name
|
|
153
|
+
const exportedName = imp.imported || calledName;
|
|
154
|
+
await graph.addEdge({
|
|
155
|
+
type: 'CALLS',
|
|
156
|
+
src: callNode.id,
|
|
157
|
+
dst: externalModuleId,
|
|
158
|
+
metadata: { exportedName }
|
|
159
|
+
});
|
|
160
|
+
edgesCreated++;
|
|
161
|
+
externalResolved++;
|
|
162
|
+
}
|
|
163
|
+
const totalTime = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
164
|
+
logger.info('Complete', {
|
|
165
|
+
nodesCreated,
|
|
166
|
+
edgesCreated,
|
|
167
|
+
callsProcessed,
|
|
168
|
+
externalResolved,
|
|
169
|
+
builtinResolved,
|
|
170
|
+
unresolvedByReason,
|
|
171
|
+
time: `${totalTime}s`
|
|
172
|
+
});
|
|
173
|
+
return createSuccessResult({ nodes: nodesCreated, edges: edgesCreated }, {
|
|
174
|
+
callsProcessed,
|
|
175
|
+
externalResolved,
|
|
176
|
+
builtinResolved,
|
|
177
|
+
unresolvedByReason,
|
|
178
|
+
timeMs: Date.now() - startTime
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Extract package name from import source.
|
|
183
|
+
*
|
|
184
|
+
* Handles:
|
|
185
|
+
* - Simple packages: 'lodash' -> 'lodash'
|
|
186
|
+
* - Scoped packages: '@tanstack/react-query' -> '@tanstack/react-query'
|
|
187
|
+
* - Subpath imports: 'lodash/map' -> 'lodash'
|
|
188
|
+
* - Scoped subpath: '@scope/pkg/sub' -> '@scope/pkg'
|
|
189
|
+
*
|
|
190
|
+
* @param source - Import source string
|
|
191
|
+
* @returns Package name or null if invalid
|
|
192
|
+
*/
|
|
193
|
+
extractPackageName(source) {
|
|
194
|
+
if (!source)
|
|
195
|
+
return null;
|
|
196
|
+
// Handle scoped packages (@scope/package)
|
|
197
|
+
if (source.startsWith('@')) {
|
|
198
|
+
// @scope/package or @scope/package/subpath
|
|
199
|
+
const parts = source.split('/');
|
|
200
|
+
if (parts.length >= 2) {
|
|
201
|
+
// Return @scope/package (first two parts)
|
|
202
|
+
return `${parts[0]}/${parts[1]}`;
|
|
203
|
+
}
|
|
204
|
+
return null; // Invalid scoped package
|
|
205
|
+
}
|
|
206
|
+
// Non-scoped package: lodash or lodash/map
|
|
207
|
+
const slashIndex = source.indexOf('/');
|
|
208
|
+
if (slashIndex === -1) {
|
|
209
|
+
return source; // Simple package name
|
|
210
|
+
}
|
|
211
|
+
return source.substring(0, slashIndex); // Package name before subpath
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FunctionCallResolver - creates CALLS edges for imported function calls
|
|
3
|
+
*
|
|
4
|
+
* This enrichment plugin runs AFTER ImportExportLinker (priority 80 vs 90) and:
|
|
5
|
+
* 1. Finds CALL_SITE nodes without CALLS edges (excluding method calls)
|
|
6
|
+
* 2. For each, looks for IMPORT with matching local name in same file
|
|
7
|
+
* 3. Follows IMPORTS_FROM -> EXPORT -> FUNCTION chain
|
|
8
|
+
* 4. Creates CALLS edge to target FUNCTION
|
|
9
|
+
*
|
|
10
|
+
* CREATES EDGES:
|
|
11
|
+
* - CALL_SITE -> CALLS -> FUNCTION (for imported functions)
|
|
12
|
+
*/
|
|
13
|
+
import { Plugin } from '../Plugin.js';
|
|
14
|
+
import type { PluginContext, PluginResult, PluginMetadata } from '../Plugin.js';
|
|
15
|
+
export declare class FunctionCallResolver extends Plugin {
|
|
16
|
+
get metadata(): PluginMetadata;
|
|
17
|
+
execute(context: PluginContext): Promise<PluginResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Build a key string for export index lookup.
|
|
20
|
+
*
|
|
21
|
+
* @param exp - Export node to build key for
|
|
22
|
+
* @returns Key in format "default" or "named:name"
|
|
23
|
+
*/
|
|
24
|
+
private buildExportKey;
|
|
25
|
+
/**
|
|
26
|
+
* Extract package name from import source.
|
|
27
|
+
* Handles: 'lodash', '@tanstack/react-query', 'lodash/map', '@scope/pkg/sub'
|
|
28
|
+
*/
|
|
29
|
+
private extractPackageName;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve module specifier to actual file path using extension fallbacks.
|
|
32
|
+
* Pattern reused from ImportExportLinker (lines 101-122).
|
|
33
|
+
*
|
|
34
|
+
* @param currentDir - Directory of the file containing the import/re-export
|
|
35
|
+
* @param specifier - The module specifier (e.g., "./utils", "../lib/helpers")
|
|
36
|
+
* @param fileIndex - Set or Map of known file paths for existence checking
|
|
37
|
+
* @returns Resolved file path or null if not found
|
|
38
|
+
*/
|
|
39
|
+
private resolveModulePath;
|
|
40
|
+
/**
|
|
41
|
+
* Follow re-export chain to find the final EXPORT node (without source field).
|
|
42
|
+
*
|
|
43
|
+
* Algorithm:
|
|
44
|
+
* 1. If current export has no source -> return it (base case)
|
|
45
|
+
* 2. Resolve source path to file
|
|
46
|
+
* 3. Find matching export in that file
|
|
47
|
+
* 4. Recurse (with cycle detection)
|
|
48
|
+
*
|
|
49
|
+
* @param exportNode - Starting export node (may be re-export)
|
|
50
|
+
* @param exportIndex - Pre-built export index for O(1) lookups
|
|
51
|
+
* @param knownFiles - Set of known file paths
|
|
52
|
+
* @param visited - Set of visited export IDs for cycle detection
|
|
53
|
+
* @param maxDepth - Maximum chain depth (safety limit)
|
|
54
|
+
* @returns Final export node (without source), external module result, or null if chain broken/circular
|
|
55
|
+
*/
|
|
56
|
+
private resolveExportChain;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=FunctionCallResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FunctionCallResolver.d.ts","sourceRoot":"","sources":["../../../src/plugins/enrichment/FunctionCallResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,EAAuB,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAyChF,qBAAa,oBAAqB,SAAQ,MAAM;IAC9C,IAAI,QAAQ,IAAI,cAAc,CAW7B;IAEK,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAgP5D;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAOtB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAoB1B;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB;IAkBzB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,kBAAkB;CAkE3B"}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FunctionCallResolver - creates CALLS edges for imported function calls
|
|
3
|
+
*
|
|
4
|
+
* This enrichment plugin runs AFTER ImportExportLinker (priority 80 vs 90) and:
|
|
5
|
+
* 1. Finds CALL_SITE nodes without CALLS edges (excluding method calls)
|
|
6
|
+
* 2. For each, looks for IMPORT with matching local name in same file
|
|
7
|
+
* 3. Follows IMPORTS_FROM -> EXPORT -> FUNCTION chain
|
|
8
|
+
* 4. Creates CALLS edge to target FUNCTION
|
|
9
|
+
*
|
|
10
|
+
* CREATES EDGES:
|
|
11
|
+
* - CALL_SITE -> CALLS -> FUNCTION (for imported functions)
|
|
12
|
+
*/
|
|
13
|
+
import { Plugin, createSuccessResult } from '../Plugin.js';
|
|
14
|
+
import { dirname, resolve } from 'path';
|
|
15
|
+
import { StrictModeError } from '../../errors/GrafemaError.js';
|
|
16
|
+
// === PLUGIN CLASS ===
|
|
17
|
+
export class FunctionCallResolver extends Plugin {
|
|
18
|
+
get metadata() {
|
|
19
|
+
return {
|
|
20
|
+
name: 'FunctionCallResolver',
|
|
21
|
+
phase: 'ENRICHMENT',
|
|
22
|
+
priority: 80, // After ImportExportLinker (90)
|
|
23
|
+
creates: {
|
|
24
|
+
nodes: ['EXTERNAL_MODULE'],
|
|
25
|
+
edges: ['CALLS']
|
|
26
|
+
},
|
|
27
|
+
dependencies: ['ImportExportLinker'] // Requires IMPORTS_FROM edges
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async execute(context) {
|
|
31
|
+
const { graph } = context;
|
|
32
|
+
const logger = this.log(context);
|
|
33
|
+
logger.info('Starting function call resolution');
|
|
34
|
+
const startTime = Date.now();
|
|
35
|
+
// Step 1: Build Import Index - Map<file:local, ImportNode>
|
|
36
|
+
const importIndex = new Map();
|
|
37
|
+
for await (const node of graph.queryNodes({ nodeType: 'IMPORT' })) {
|
|
38
|
+
const imp = node;
|
|
39
|
+
if (!imp.file || !imp.local)
|
|
40
|
+
continue;
|
|
41
|
+
// Skip external imports (non-relative)
|
|
42
|
+
const isRelative = imp.source && (imp.source.startsWith('./') || imp.source.startsWith('../'));
|
|
43
|
+
if (!isRelative)
|
|
44
|
+
continue;
|
|
45
|
+
const key = `${imp.file}:${imp.local}`;
|
|
46
|
+
importIndex.set(key, imp);
|
|
47
|
+
}
|
|
48
|
+
logger.debug('Indexed imports', { count: importIndex.size });
|
|
49
|
+
// Step 2: Build Function Index - Map<file, Map<name, FunctionNode>>
|
|
50
|
+
const functionIndex = new Map();
|
|
51
|
+
for await (const node of graph.queryNodes({ nodeType: 'FUNCTION' })) {
|
|
52
|
+
const func = node;
|
|
53
|
+
if (!func.file || !func.name)
|
|
54
|
+
continue;
|
|
55
|
+
if (!functionIndex.has(func.file)) {
|
|
56
|
+
functionIndex.set(func.file, new Map());
|
|
57
|
+
}
|
|
58
|
+
functionIndex.get(func.file).set(func.name, func);
|
|
59
|
+
}
|
|
60
|
+
logger.debug('Indexed functions', { files: functionIndex.size });
|
|
61
|
+
// Step 2.5: Build Export Index - Map<file, Map<exportKey, ExportNode>>
|
|
62
|
+
// This enables O(1) lookup when following re-export chains
|
|
63
|
+
const exportIndex = new Map();
|
|
64
|
+
for await (const node of graph.queryNodes({ nodeType: 'EXPORT' })) {
|
|
65
|
+
const exp = node;
|
|
66
|
+
if (!exp.file)
|
|
67
|
+
continue;
|
|
68
|
+
if (!exportIndex.has(exp.file)) {
|
|
69
|
+
exportIndex.set(exp.file, new Map());
|
|
70
|
+
}
|
|
71
|
+
const fileExports = exportIndex.get(exp.file);
|
|
72
|
+
fileExports.set(this.buildExportKey(exp), exp);
|
|
73
|
+
}
|
|
74
|
+
logger.debug('Indexed exports', { files: exportIndex.size });
|
|
75
|
+
// Step 2.6: Build set of known files for path resolution
|
|
76
|
+
const knownFiles = new Set();
|
|
77
|
+
for (const file of exportIndex.keys()) {
|
|
78
|
+
knownFiles.add(file);
|
|
79
|
+
}
|
|
80
|
+
for (const file of functionIndex.keys()) {
|
|
81
|
+
knownFiles.add(file);
|
|
82
|
+
}
|
|
83
|
+
logger.debug('Indexed known files', { count: knownFiles.size });
|
|
84
|
+
// Step 3: Collect unresolved CALL_SITE nodes
|
|
85
|
+
const callSitesToResolve = [];
|
|
86
|
+
for await (const node of graph.queryNodes({ nodeType: 'CALL' })) {
|
|
87
|
+
const call = node;
|
|
88
|
+
// Skip method calls (have object attribute)
|
|
89
|
+
if (call.object)
|
|
90
|
+
continue;
|
|
91
|
+
// Skip if already has CALLS edge
|
|
92
|
+
const existingEdges = await graph.getOutgoingEdges(call.id, ['CALLS']);
|
|
93
|
+
if (existingEdges.length > 0)
|
|
94
|
+
continue;
|
|
95
|
+
callSitesToResolve.push(call);
|
|
96
|
+
}
|
|
97
|
+
logger.info('Found call sites to resolve', { count: callSitesToResolve.length });
|
|
98
|
+
// Step 4: Resolution
|
|
99
|
+
let edgesCreated = 0;
|
|
100
|
+
const skipped = {
|
|
101
|
+
alreadyResolved: 0,
|
|
102
|
+
methodCalls: 0,
|
|
103
|
+
external: 0,
|
|
104
|
+
missingImport: 0,
|
|
105
|
+
missingImportsFrom: 0,
|
|
106
|
+
reExportsBroken: 0 // Re-export chain broken (missing export, file not found, or circular)
|
|
107
|
+
};
|
|
108
|
+
let reExportsResolved = 0; // Counter for successfully resolved re-export chains
|
|
109
|
+
const errors = [];
|
|
110
|
+
for (const callSite of callSitesToResolve) {
|
|
111
|
+
const calledName = callSite.name;
|
|
112
|
+
const file = callSite.file;
|
|
113
|
+
if (!calledName || !file)
|
|
114
|
+
continue;
|
|
115
|
+
// Step 4.1: Find matching import in same file
|
|
116
|
+
const importKey = `${file}:${calledName}`;
|
|
117
|
+
const imp = importIndex.get(importKey);
|
|
118
|
+
if (!imp) {
|
|
119
|
+
skipped.missingImport++;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
// Step 4.2: Follow IMPORTS_FROM edge to find EXPORT
|
|
123
|
+
const importsFromEdges = await graph.getOutgoingEdges(imp.id, ['IMPORTS_FROM']);
|
|
124
|
+
if (importsFromEdges.length === 0) {
|
|
125
|
+
skipped.missingImportsFrom++;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const exportNodeId = importsFromEdges[0].dst;
|
|
129
|
+
const exportNode = await graph.getNode(exportNodeId);
|
|
130
|
+
if (!exportNode) {
|
|
131
|
+
skipped.missingImportsFrom++;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
// Step 4.3: Resolve re-export chain (if applicable)
|
|
135
|
+
let finalExport = exportNode;
|
|
136
|
+
if (exportNode.source) {
|
|
137
|
+
// This is a re-export - follow the chain
|
|
138
|
+
const resolved = this.resolveExportChain(exportNode, exportIndex, knownFiles);
|
|
139
|
+
if (!resolved) {
|
|
140
|
+
// Chain broken or circular
|
|
141
|
+
// Distinguish: if visited set would show cycle, it's circular
|
|
142
|
+
// For simplicity, count as broken (can add nuance later)
|
|
143
|
+
skipped.reExportsBroken++;
|
|
144
|
+
// In strict mode, collect error
|
|
145
|
+
if (context.strictMode) {
|
|
146
|
+
const error = new StrictModeError(`Cannot resolve re-export chain for: ${calledName}`, 'STRICT_BROKEN_IMPORT', {
|
|
147
|
+
filePath: file,
|
|
148
|
+
lineNumber: callSite.line,
|
|
149
|
+
phase: 'ENRICHMENT',
|
|
150
|
+
plugin: 'FunctionCallResolver',
|
|
151
|
+
calledFunction: calledName,
|
|
152
|
+
importSource: imp.source,
|
|
153
|
+
}, `Check if the module "${imp.source}" exists and exports "${calledName}"`);
|
|
154
|
+
errors.push(error);
|
|
155
|
+
}
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
// Check if resolved to external module
|
|
159
|
+
if ('type' in resolved && resolved.type === 'external') {
|
|
160
|
+
// Type narrowing: resolved is ExternalModuleResult
|
|
161
|
+
const externalResult = resolved;
|
|
162
|
+
// Find or create EXTERNAL_MODULE node and create CALLS edge
|
|
163
|
+
const externalModuleId = `EXTERNAL_MODULE:${externalResult.packageName}`;
|
|
164
|
+
// Check if node exists
|
|
165
|
+
let externalNode = await graph.getNode(externalModuleId);
|
|
166
|
+
if (!externalNode) {
|
|
167
|
+
// Create EXTERNAL_MODULE node
|
|
168
|
+
await graph.addNode({
|
|
169
|
+
id: externalModuleId,
|
|
170
|
+
type: 'EXTERNAL_MODULE',
|
|
171
|
+
name: externalResult.packageName,
|
|
172
|
+
file: '',
|
|
173
|
+
line: 0
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
// Create CALLS edge with metadata
|
|
177
|
+
await graph.addEdge({
|
|
178
|
+
type: 'CALLS',
|
|
179
|
+
src: callSite.id,
|
|
180
|
+
dst: externalModuleId,
|
|
181
|
+
metadata: { exportedName: externalResult.exportedName }
|
|
182
|
+
});
|
|
183
|
+
edgesCreated++;
|
|
184
|
+
reExportsResolved++;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
// At this point, resolved must be ExportNode (not external)
|
|
188
|
+
finalExport = resolved;
|
|
189
|
+
reExportsResolved++;
|
|
190
|
+
}
|
|
191
|
+
// Step 4.4: Find target FUNCTION via final export's local name
|
|
192
|
+
const targetFile = finalExport.file;
|
|
193
|
+
const targetFunctionName = finalExport.local || finalExport.name;
|
|
194
|
+
if (!targetFile || !targetFunctionName)
|
|
195
|
+
continue;
|
|
196
|
+
const fileFunctions = functionIndex.get(targetFile);
|
|
197
|
+
if (!fileFunctions)
|
|
198
|
+
continue;
|
|
199
|
+
const targetFunction = fileFunctions.get(targetFunctionName);
|
|
200
|
+
if (!targetFunction)
|
|
201
|
+
continue;
|
|
202
|
+
// Step 4.5: Create CALLS edge
|
|
203
|
+
await graph.addEdge({
|
|
204
|
+
type: 'CALLS',
|
|
205
|
+
src: callSite.id,
|
|
206
|
+
dst: targetFunction.id
|
|
207
|
+
});
|
|
208
|
+
edgesCreated++;
|
|
209
|
+
}
|
|
210
|
+
const totalTime = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
211
|
+
logger.info('Complete', {
|
|
212
|
+
edgesCreated,
|
|
213
|
+
skipped,
|
|
214
|
+
time: `${totalTime}s`
|
|
215
|
+
});
|
|
216
|
+
return createSuccessResult({ nodes: 0, edges: edgesCreated }, {
|
|
217
|
+
callSitesProcessed: callSitesToResolve.length,
|
|
218
|
+
edgesCreated,
|
|
219
|
+
reExportsResolved,
|
|
220
|
+
skipped,
|
|
221
|
+
timeMs: Date.now() - startTime
|
|
222
|
+
}, errors);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Build a key string for export index lookup.
|
|
226
|
+
*
|
|
227
|
+
* @param exp - Export node to build key for
|
|
228
|
+
* @returns Key in format "default" or "named:name"
|
|
229
|
+
*/
|
|
230
|
+
buildExportKey(exp) {
|
|
231
|
+
if (exp.exportType === 'default') {
|
|
232
|
+
return 'default';
|
|
233
|
+
}
|
|
234
|
+
return `named:${exp.name || exp.local || 'anonymous'}`;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Extract package name from import source.
|
|
238
|
+
* Handles: 'lodash', '@tanstack/react-query', 'lodash/map', '@scope/pkg/sub'
|
|
239
|
+
*/
|
|
240
|
+
extractPackageName(source) {
|
|
241
|
+
if (!source)
|
|
242
|
+
return null;
|
|
243
|
+
// Handle scoped packages (@scope/package)
|
|
244
|
+
if (source.startsWith('@')) {
|
|
245
|
+
const parts = source.split('/');
|
|
246
|
+
if (parts.length >= 2) {
|
|
247
|
+
return `${parts[0]}/${parts[1]}`;
|
|
248
|
+
}
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
// Non-scoped package: lodash or lodash/map
|
|
252
|
+
const slashIndex = source.indexOf('/');
|
|
253
|
+
if (slashIndex === -1) {
|
|
254
|
+
return source;
|
|
255
|
+
}
|
|
256
|
+
return source.substring(0, slashIndex);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Resolve module specifier to actual file path using extension fallbacks.
|
|
260
|
+
* Pattern reused from ImportExportLinker (lines 101-122).
|
|
261
|
+
*
|
|
262
|
+
* @param currentDir - Directory of the file containing the import/re-export
|
|
263
|
+
* @param specifier - The module specifier (e.g., "./utils", "../lib/helpers")
|
|
264
|
+
* @param fileIndex - Set or Map of known file paths for existence checking
|
|
265
|
+
* @returns Resolved file path or null if not found
|
|
266
|
+
*/
|
|
267
|
+
resolveModulePath(currentDir, specifier, fileIndex) {
|
|
268
|
+
const basePath = resolve(currentDir, specifier);
|
|
269
|
+
const extensions = ['', '.js', '.ts', '.jsx', '.tsx', '/index.js', '/index.ts'];
|
|
270
|
+
for (const ext of extensions) {
|
|
271
|
+
const testPath = basePath + ext;
|
|
272
|
+
if (fileIndex.has(testPath)) {
|
|
273
|
+
return testPath;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Follow re-export chain to find the final EXPORT node (without source field).
|
|
280
|
+
*
|
|
281
|
+
* Algorithm:
|
|
282
|
+
* 1. If current export has no source -> return it (base case)
|
|
283
|
+
* 2. Resolve source path to file
|
|
284
|
+
* 3. Find matching export in that file
|
|
285
|
+
* 4. Recurse (with cycle detection)
|
|
286
|
+
*
|
|
287
|
+
* @param exportNode - Starting export node (may be re-export)
|
|
288
|
+
* @param exportIndex - Pre-built export index for O(1) lookups
|
|
289
|
+
* @param knownFiles - Set of known file paths
|
|
290
|
+
* @param visited - Set of visited export IDs for cycle detection
|
|
291
|
+
* @param maxDepth - Maximum chain depth (safety limit)
|
|
292
|
+
* @returns Final export node (without source), external module result, or null if chain broken/circular
|
|
293
|
+
*/
|
|
294
|
+
resolveExportChain(exportNode, exportIndex, knownFiles, visited = new Set(), maxDepth = 10) {
|
|
295
|
+
// Safety: max depth exceeded
|
|
296
|
+
if (maxDepth <= 0) {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
// Cycle detection
|
|
300
|
+
if (visited.has(exportNode.id)) {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
visited.add(exportNode.id);
|
|
304
|
+
// Base case: not a re-export
|
|
305
|
+
if (!exportNode.source) {
|
|
306
|
+
return exportNode;
|
|
307
|
+
}
|
|
308
|
+
// Recursive case: follow re-export
|
|
309
|
+
const currentDir = dirname(exportNode.file);
|
|
310
|
+
const targetFile = this.resolveModulePath(currentDir, exportNode.source, knownFiles);
|
|
311
|
+
if (!targetFile) {
|
|
312
|
+
// Check if this is an external module (non-relative source)
|
|
313
|
+
const source = exportNode.source;
|
|
314
|
+
const isExternal = !source.startsWith('./') && !source.startsWith('../');
|
|
315
|
+
if (isExternal) {
|
|
316
|
+
const packageName = this.extractPackageName(source);
|
|
317
|
+
if (packageName) {
|
|
318
|
+
return {
|
|
319
|
+
type: 'external',
|
|
320
|
+
packageName,
|
|
321
|
+
exportedName: exportNode.local || exportNode.name || ''
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return null; // Source file not found and not external
|
|
326
|
+
}
|
|
327
|
+
const targetExports = exportIndex.get(targetFile);
|
|
328
|
+
if (!targetExports) {
|
|
329
|
+
return null; // No exports in target file
|
|
330
|
+
}
|
|
331
|
+
// Find matching export by name
|
|
332
|
+
// Re-export: export { foo } from './other' - look for named:foo
|
|
333
|
+
// Re-export default: export { default } from './other' - look for default
|
|
334
|
+
const nextExport = targetExports.get(this.buildExportKey(exportNode));
|
|
335
|
+
if (!nextExport) {
|
|
336
|
+
return null; // Export not found in target
|
|
337
|
+
}
|
|
338
|
+
return this.resolveExportChain(nextExport, exportIndex, knownFiles, visited, maxDepth - 1);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
@@ -14,12 +14,25 @@ export declare class HTTPConnectionEnricher extends Plugin {
|
|
|
14
14
|
get metadata(): PluginMetadata;
|
|
15
15
|
execute(context: PluginContext): Promise<PluginResult>;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Normalize URL to canonical form for comparison.
|
|
18
|
+
* Converts both Express params (:id) and template literals (${...}) to {param}.
|
|
19
|
+
*/
|
|
20
|
+
private normalizeUrl;
|
|
21
|
+
/**
|
|
22
|
+
* Check if URL has any parameter placeholders (after normalization)
|
|
23
|
+
*/
|
|
24
|
+
private hasParamsNormalized;
|
|
25
|
+
/**
|
|
26
|
+
* Check if request URL matches route path.
|
|
27
|
+
* Supports:
|
|
28
|
+
* - Exact match
|
|
29
|
+
* - Express params (:id)
|
|
30
|
+
* - Template literals (${...})
|
|
31
|
+
* - Concrete values matching params (/users/123 matches /users/:id)
|
|
19
32
|
*/
|
|
20
33
|
private pathsMatch;
|
|
21
34
|
/**
|
|
22
|
-
*
|
|
35
|
+
* Check if path has parameters (for edge matchType metadata)
|
|
23
36
|
*/
|
|
24
37
|
private hasParams;
|
|
25
38
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HTTPConnectionEnricher.d.ts","sourceRoot":"","sources":["../../../src/plugins/enrichment/HTTPConnectionEnricher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAA0C,MAAM,cAAc,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"HTTPConnectionEnricher.d.ts","sourceRoot":"","sources":["../../../src/plugins/enrichment/HTTPConnectionEnricher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAA0C,MAAM,cAAc,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAgChF,qBAAa,sBAAuB,SAAQ,MAAM;IAChD,IAAI,QAAQ,IAAI,cAAc,CAW7B;IAEK,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAoH5D;;;OAGG;IACH,OAAO,CAAC,YAAY;IAMpB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;;;;;;OAOG;IACH,OAAO,CAAC,UAAU;IAwBlB;;OAEG;IACH,OAAO,CAAC,SAAS;IAMjB;;OAEG;IACH,OAAO,CAAC,eAAe;CAaxB"}
|