@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
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* - CALL -> ALIAS_OF -> EXPRESSION (для трассировки)
|
|
14
14
|
*/
|
|
15
15
|
import { Plugin, createSuccessResult } from '../Plugin.js';
|
|
16
|
+
import { StrictModeError } from '../../errors/GrafemaError.js';
|
|
16
17
|
export class AliasTracker extends Plugin {
|
|
17
18
|
static MAX_DEPTH = 10;
|
|
18
19
|
depthExceeded = [];
|
|
@@ -35,6 +36,7 @@ export class AliasTracker extends Plugin {
|
|
|
35
36
|
let aliasesFound = 0;
|
|
36
37
|
let edgesCreated = 0;
|
|
37
38
|
let resolvedToMethod = 0;
|
|
39
|
+
const errors = [];
|
|
38
40
|
// Трекинг превышений глубины
|
|
39
41
|
this.depthExceeded = [];
|
|
40
42
|
// 1. Найти все CALL без object (call sites) которые ещё не резолвлены
|
|
@@ -118,9 +120,22 @@ export class AliasTracker extends Plugin {
|
|
|
118
120
|
chain: info.chain.join(' → ')
|
|
119
121
|
}))
|
|
120
122
|
});
|
|
123
|
+
// In strict mode, report as errors
|
|
124
|
+
if (context.strictMode) {
|
|
125
|
+
for (const info of this.depthExceeded) {
|
|
126
|
+
const error = new StrictModeError(`Alias chain exceeded max depth (${info.depth}): ${info.name}`, 'STRICT_ALIAS_DEPTH_EXCEEDED', {
|
|
127
|
+
filePath: info.file,
|
|
128
|
+
phase: 'ENRICHMENT',
|
|
129
|
+
plugin: 'AliasTracker',
|
|
130
|
+
aliasName: info.name,
|
|
131
|
+
chainLength: info.depth,
|
|
132
|
+
}, `Possible circular alias reference. Chain: ${info.chain.slice(0, 3).join(' -> ')}...`);
|
|
133
|
+
errors.push(error);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
121
136
|
}
|
|
122
137
|
logger.info('Summary', summary);
|
|
123
|
-
return createSuccessResult({ nodes: 0, edges: edgesCreated }, summary);
|
|
138
|
+
return createSuccessResult({ nodes: 0, edges: edgesCreated }, summary, errors);
|
|
124
139
|
}
|
|
125
140
|
/**
|
|
126
141
|
* Строит индекс алиасов: file:variableName -> EXPRESSION info
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArgumentParameterLinker - creates RECEIVES_ARGUMENT edges connecting
|
|
3
|
+
* function parameters to call arguments.
|
|
4
|
+
*
|
|
5
|
+
* RECEIVES_ARGUMENT edges connect:
|
|
6
|
+
* PARAMETER node -> RECEIVES_ARGUMENT -> argument source (VARIABLE, LITERAL, CALL, etc.)
|
|
7
|
+
*
|
|
8
|
+
* This is the inverse of PASSES_ARGUMENT:
|
|
9
|
+
* - PASSES_ARGUMENT: CALL -> argument (call site perspective)
|
|
10
|
+
* - RECEIVES_ARGUMENT: PARAMETER -> argument (function perspective)
|
|
11
|
+
*
|
|
12
|
+
* Edge attributes:
|
|
13
|
+
* - argIndex: position of the argument (0-based)
|
|
14
|
+
* - callId: ID of the CALL node that passed this argument
|
|
15
|
+
*
|
|
16
|
+
* Algorithm:
|
|
17
|
+
* For each CALL node with PASSES_ARGUMENT edges:
|
|
18
|
+
* 1. Get outgoing CALLS edge to find target function
|
|
19
|
+
* 2. If no CALLS edge -> skip (unresolved call)
|
|
20
|
+
* 3. Get target function's PARAMETER nodes via HAS_PARAMETER edges
|
|
21
|
+
* 4. For each PASSES_ARGUMENT edge:
|
|
22
|
+
* a. Get argIndex from edge metadata
|
|
23
|
+
* b. Find PARAMETER with matching index
|
|
24
|
+
* c. Create RECEIVES_ARGUMENT edge: PARAMETER -> argument_source
|
|
25
|
+
*/
|
|
26
|
+
import { Plugin } from '../Plugin.js';
|
|
27
|
+
import type { PluginContext, PluginResult, PluginMetadata } from '../Plugin.js';
|
|
28
|
+
export declare class ArgumentParameterLinker extends Plugin {
|
|
29
|
+
get metadata(): PluginMetadata;
|
|
30
|
+
execute(context: PluginContext): Promise<PluginResult>;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=ArgumentParameterLinker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ArgumentParameterLinker.d.ts","sourceRoot":"","sources":["../../../src/plugins/enrichment/ArgumentParameterLinker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,MAAM,EAAuB,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAiChF,qBAAa,uBAAwB,SAAQ,MAAM;IACjD,IAAI,QAAQ,IAAI,cAAc,CAW7B;IAEK,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CAqK7D"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArgumentParameterLinker - creates RECEIVES_ARGUMENT edges connecting
|
|
3
|
+
* function parameters to call arguments.
|
|
4
|
+
*
|
|
5
|
+
* RECEIVES_ARGUMENT edges connect:
|
|
6
|
+
* PARAMETER node -> RECEIVES_ARGUMENT -> argument source (VARIABLE, LITERAL, CALL, etc.)
|
|
7
|
+
*
|
|
8
|
+
* This is the inverse of PASSES_ARGUMENT:
|
|
9
|
+
* - PASSES_ARGUMENT: CALL -> argument (call site perspective)
|
|
10
|
+
* - RECEIVES_ARGUMENT: PARAMETER -> argument (function perspective)
|
|
11
|
+
*
|
|
12
|
+
* Edge attributes:
|
|
13
|
+
* - argIndex: position of the argument (0-based)
|
|
14
|
+
* - callId: ID of the CALL node that passed this argument
|
|
15
|
+
*
|
|
16
|
+
* Algorithm:
|
|
17
|
+
* For each CALL node with PASSES_ARGUMENT edges:
|
|
18
|
+
* 1. Get outgoing CALLS edge to find target function
|
|
19
|
+
* 2. If no CALLS edge -> skip (unresolved call)
|
|
20
|
+
* 3. Get target function's PARAMETER nodes via HAS_PARAMETER edges
|
|
21
|
+
* 4. For each PASSES_ARGUMENT edge:
|
|
22
|
+
* a. Get argIndex from edge metadata
|
|
23
|
+
* b. Find PARAMETER with matching index
|
|
24
|
+
* c. Create RECEIVES_ARGUMENT edge: PARAMETER -> argument_source
|
|
25
|
+
*/
|
|
26
|
+
import { Plugin, createSuccessResult } from '../Plugin.js';
|
|
27
|
+
import { StrictModeError } from '../../errors/GrafemaError.js';
|
|
28
|
+
export class ArgumentParameterLinker extends Plugin {
|
|
29
|
+
get metadata() {
|
|
30
|
+
return {
|
|
31
|
+
name: 'ArgumentParameterLinker',
|
|
32
|
+
phase: 'ENRICHMENT',
|
|
33
|
+
priority: 45, // Runs AFTER MethodCallResolver (50) which creates required CALLS edges
|
|
34
|
+
creates: {
|
|
35
|
+
nodes: [],
|
|
36
|
+
edges: ['RECEIVES_ARGUMENT']
|
|
37
|
+
},
|
|
38
|
+
dependencies: ['JSASTAnalyzer', 'MethodCallResolver'] // Requires CALLS edges
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async execute(context) {
|
|
42
|
+
const { graph, onProgress } = context;
|
|
43
|
+
const logger = this.log(context);
|
|
44
|
+
logger.info('Starting argument-parameter linking');
|
|
45
|
+
const startTime = Date.now();
|
|
46
|
+
let callsProcessed = 0;
|
|
47
|
+
let edgesCreated = 0;
|
|
48
|
+
let unresolvedCalls = 0;
|
|
49
|
+
let noParams = 0;
|
|
50
|
+
const errors = [];
|
|
51
|
+
// Collect all CALL nodes (both CALL and METHOD_CALL via CALL type check)
|
|
52
|
+
const callNodes = [];
|
|
53
|
+
for await (const node of graph.queryNodes({ nodeType: 'CALL' })) {
|
|
54
|
+
callNodes.push(node);
|
|
55
|
+
}
|
|
56
|
+
logger.info('Found calls to process', { count: callNodes.length });
|
|
57
|
+
// Build a Set of existing RECEIVES_ARGUMENT edges to avoid duplicates
|
|
58
|
+
// Key: `${paramId}:${dstId}:${callId}`
|
|
59
|
+
const existingEdges = new Set();
|
|
60
|
+
for await (const node of graph.queryNodes({ nodeType: 'PARAMETER' })) {
|
|
61
|
+
const edges = await graph.getOutgoingEdges(node.id, ['RECEIVES_ARGUMENT']);
|
|
62
|
+
for (const edge of edges) {
|
|
63
|
+
const callId = edge.callId ?? edge.metadata?.callId ?? '';
|
|
64
|
+
existingEdges.add(`${node.id}:${edge.dst}:${callId}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
logger.debug('Found existing RECEIVES_ARGUMENT edges', { count: existingEdges.size });
|
|
68
|
+
for (const callNode of callNodes) {
|
|
69
|
+
callsProcessed++;
|
|
70
|
+
// Report progress every 100 calls
|
|
71
|
+
if (onProgress && callsProcessed % 100 === 0) {
|
|
72
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
73
|
+
onProgress({
|
|
74
|
+
phase: 'enrichment',
|
|
75
|
+
currentPlugin: 'ArgumentParameterLinker',
|
|
76
|
+
message: `Linking arguments ${callsProcessed}/${callNodes.length} (${elapsed}s)`,
|
|
77
|
+
totalFiles: callNodes.length,
|
|
78
|
+
processedFiles: callsProcessed
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// 1. Get PASSES_ARGUMENT edges from this call
|
|
82
|
+
const passesArgumentEdges = await graph.getOutgoingEdges(callNode.id, ['PASSES_ARGUMENT']);
|
|
83
|
+
if (passesArgumentEdges.length === 0) {
|
|
84
|
+
continue; // No arguments passed, skip
|
|
85
|
+
}
|
|
86
|
+
// 2. Get CALLS edge to find target function
|
|
87
|
+
const callsEdges = await graph.getOutgoingEdges(callNode.id, ['CALLS']);
|
|
88
|
+
if (callsEdges.length === 0) {
|
|
89
|
+
unresolvedCalls++;
|
|
90
|
+
// In strict mode, report unresolved calls that have arguments
|
|
91
|
+
if (context.strictMode) {
|
|
92
|
+
const error = new StrictModeError(`Call with arguments has no resolved target: ${callNode.name || callNode.id}`, 'STRICT_UNRESOLVED_ARGUMENT', {
|
|
93
|
+
filePath: callNode.file,
|
|
94
|
+
lineNumber: callNode.line,
|
|
95
|
+
phase: 'ENRICHMENT',
|
|
96
|
+
plugin: 'ArgumentParameterLinker',
|
|
97
|
+
callId: callNode.id,
|
|
98
|
+
}, `Ensure the called function is imported or defined`);
|
|
99
|
+
errors.push(error);
|
|
100
|
+
}
|
|
101
|
+
continue; // Unresolved call, skip
|
|
102
|
+
}
|
|
103
|
+
// Get target function node
|
|
104
|
+
const targetFunctionId = callsEdges[0].dst;
|
|
105
|
+
const targetFunction = await graph.getNode(targetFunctionId);
|
|
106
|
+
if (!targetFunction) {
|
|
107
|
+
unresolvedCalls++;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
// 3. Get target function's PARAMETER nodes via HAS_PARAMETER edges
|
|
111
|
+
const hasParameterEdges = await graph.getOutgoingEdges(targetFunctionId, ['HAS_PARAMETER']);
|
|
112
|
+
if (hasParameterEdges.length === 0) {
|
|
113
|
+
noParams++;
|
|
114
|
+
continue; // Function has no parameters
|
|
115
|
+
}
|
|
116
|
+
// Build parameter index map: argIndex -> paramNode
|
|
117
|
+
const paramsByIndex = new Map();
|
|
118
|
+
for (const paramEdge of hasParameterEdges) {
|
|
119
|
+
const paramNode = await graph.getNode(paramEdge.dst);
|
|
120
|
+
if (paramNode && typeof paramNode.index === 'number') {
|
|
121
|
+
paramsByIndex.set(paramNode.index, paramNode);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (paramsByIndex.size === 0) {
|
|
125
|
+
noParams++;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
// 4. For each PASSES_ARGUMENT edge, create RECEIVES_ARGUMENT edge
|
|
129
|
+
for (const passesEdge of passesArgumentEdges) {
|
|
130
|
+
// Get argIndex from edge (can be top-level or in metadata)
|
|
131
|
+
const argIndex = passesEdge.argIndex ?? passesEdge.metadata?.argIndex;
|
|
132
|
+
if (argIndex === undefined) {
|
|
133
|
+
continue; // No argIndex, skip
|
|
134
|
+
}
|
|
135
|
+
// Find matching parameter
|
|
136
|
+
const paramNode = paramsByIndex.get(argIndex);
|
|
137
|
+
if (!paramNode) {
|
|
138
|
+
continue; // No parameter for this argument index (extra arg)
|
|
139
|
+
}
|
|
140
|
+
// Check for duplicate
|
|
141
|
+
const edgeKey = `${paramNode.id}:${passesEdge.dst}:${callNode.id}`;
|
|
142
|
+
if (existingEdges.has(edgeKey)) {
|
|
143
|
+
continue; // Already exists
|
|
144
|
+
}
|
|
145
|
+
// Create RECEIVES_ARGUMENT edge: PARAMETER -> argument_source
|
|
146
|
+
await graph.addEdge({
|
|
147
|
+
type: 'RECEIVES_ARGUMENT',
|
|
148
|
+
src: paramNode.id,
|
|
149
|
+
dst: passesEdge.dst,
|
|
150
|
+
metadata: {
|
|
151
|
+
argIndex,
|
|
152
|
+
callId: callNode.id
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
existingEdges.add(edgeKey);
|
|
156
|
+
edgesCreated++;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const totalTime = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
160
|
+
logger.info('Complete', {
|
|
161
|
+
callsProcessed,
|
|
162
|
+
edgesCreated,
|
|
163
|
+
unresolvedCalls,
|
|
164
|
+
noParams,
|
|
165
|
+
time: `${totalTime}s`
|
|
166
|
+
});
|
|
167
|
+
return createSuccessResult({ nodes: 0, edges: edgesCreated }, {
|
|
168
|
+
callsProcessed,
|
|
169
|
+
edgesCreated,
|
|
170
|
+
unresolvedCalls,
|
|
171
|
+
noParams,
|
|
172
|
+
timeMs: Date.now() - startTime
|
|
173
|
+
}, errors);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClosureCaptureEnricher - tracks transitive closure captures
|
|
3
|
+
*
|
|
4
|
+
* Problem: CAPTURES edges only exist for immediate parent scope (depth=1).
|
|
5
|
+
* Multi-level captures (grandparent, great-grandparent) are not tracked.
|
|
6
|
+
*
|
|
7
|
+
* Solution: Walk scope chains upward to find ALL captured variables,
|
|
8
|
+
* creating CAPTURES edges with depth metadata.
|
|
9
|
+
*
|
|
10
|
+
* USES:
|
|
11
|
+
* - SCOPE nodes with scopeType='closure'
|
|
12
|
+
* - SCOPE.parentScopeId for scope chain navigation
|
|
13
|
+
* - VARIABLE nodes with parentScopeId
|
|
14
|
+
* - CONSTANT nodes with parentScopeId
|
|
15
|
+
* - PARAMETER nodes with parentFunctionId (resolved via HAS_SCOPE)
|
|
16
|
+
*
|
|
17
|
+
* CREATES:
|
|
18
|
+
* - SCOPE -> CAPTURES -> VARIABLE/CONSTANT/PARAMETER (with metadata: { depth: N })
|
|
19
|
+
*
|
|
20
|
+
* NOTE: Depth=1 edges are created by JSASTAnalyzer without depth metadata.
|
|
21
|
+
* This enricher only creates edges for depth > 1.
|
|
22
|
+
*/
|
|
23
|
+
import { Plugin } from '../Plugin.js';
|
|
24
|
+
import type { PluginContext, PluginResult, PluginMetadata } from '../Plugin.js';
|
|
25
|
+
export declare class ClosureCaptureEnricher extends Plugin {
|
|
26
|
+
static MAX_DEPTH: number;
|
|
27
|
+
get metadata(): PluginMetadata;
|
|
28
|
+
execute(context: PluginContext): Promise<PluginResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Build index: scopeId -> ScopeNode
|
|
31
|
+
*/
|
|
32
|
+
private buildScopeIndex;
|
|
33
|
+
/**
|
|
34
|
+
* Build index: scopeId -> VariableNode[]
|
|
35
|
+
* Includes VARIABLE, CONSTANT, and PARAMETER nodes
|
|
36
|
+
*/
|
|
37
|
+
private buildVariablesByScopeIndex;
|
|
38
|
+
/**
|
|
39
|
+
* Build set of existing CAPTURES edges: "srcId:dstId"
|
|
40
|
+
*/
|
|
41
|
+
private buildExistingCapturesSet;
|
|
42
|
+
/**
|
|
43
|
+
* Walk scope chain upward from startScopeId
|
|
44
|
+
* Returns ancestor scopes with depth (1 = immediate parent, 2 = grandparent, etc.)
|
|
45
|
+
*
|
|
46
|
+
* Walks ALL scopes in the chain (including if/for/while blocks),
|
|
47
|
+
* not just closures.
|
|
48
|
+
*/
|
|
49
|
+
private walkScopeChain;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=ClosureCaptureEnricher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClosureCaptureEnricher.d.ts","sourceRoot":"","sources":["../../../src/plugins/enrichment/ClosureCaptureEnricher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,MAAM,EAAuB,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAsBhF,qBAAa,sBAAuB,SAAQ,MAAM;IAChD,MAAM,CAAC,SAAS,SAAM;IAEtB,IAAI,QAAQ,IAAI,cAAc,CAW7B;IAEK,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAyF5D;;OAEG;YACW,eAAe;IAU7B;;;OAGG;YACW,0BAA0B;IA0CxC;;OAEG;YACW,wBAAwB;IActC;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;CAgCvB"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClosureCaptureEnricher - tracks transitive closure captures
|
|
3
|
+
*
|
|
4
|
+
* Problem: CAPTURES edges only exist for immediate parent scope (depth=1).
|
|
5
|
+
* Multi-level captures (grandparent, great-grandparent) are not tracked.
|
|
6
|
+
*
|
|
7
|
+
* Solution: Walk scope chains upward to find ALL captured variables,
|
|
8
|
+
* creating CAPTURES edges with depth metadata.
|
|
9
|
+
*
|
|
10
|
+
* USES:
|
|
11
|
+
* - SCOPE nodes with scopeType='closure'
|
|
12
|
+
* - SCOPE.parentScopeId for scope chain navigation
|
|
13
|
+
* - VARIABLE nodes with parentScopeId
|
|
14
|
+
* - CONSTANT nodes with parentScopeId
|
|
15
|
+
* - PARAMETER nodes with parentFunctionId (resolved via HAS_SCOPE)
|
|
16
|
+
*
|
|
17
|
+
* CREATES:
|
|
18
|
+
* - SCOPE -> CAPTURES -> VARIABLE/CONSTANT/PARAMETER (with metadata: { depth: N })
|
|
19
|
+
*
|
|
20
|
+
* NOTE: Depth=1 edges are created by JSASTAnalyzer without depth metadata.
|
|
21
|
+
* This enricher only creates edges for depth > 1.
|
|
22
|
+
*/
|
|
23
|
+
import { Plugin, createSuccessResult } from '../Plugin.js';
|
|
24
|
+
export class ClosureCaptureEnricher extends Plugin {
|
|
25
|
+
static MAX_DEPTH = 10;
|
|
26
|
+
get metadata() {
|
|
27
|
+
return {
|
|
28
|
+
name: 'ClosureCaptureEnricher',
|
|
29
|
+
phase: 'ENRICHMENT',
|
|
30
|
+
priority: 40, // Lower number = runs later. Runs after ImportExportLinker (90)
|
|
31
|
+
creates: {
|
|
32
|
+
nodes: [],
|
|
33
|
+
edges: ['CAPTURES']
|
|
34
|
+
},
|
|
35
|
+
dependencies: ['JSASTAnalyzer'] // Requires SCOPE and VARIABLE nodes
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async execute(context) {
|
|
39
|
+
const { graph, onProgress } = context;
|
|
40
|
+
const logger = this.log(context);
|
|
41
|
+
logger.info('Starting transitive capture resolution');
|
|
42
|
+
let closuresProcessed = 0;
|
|
43
|
+
let capturesCreated = 0;
|
|
44
|
+
let existingCapturesSkipped = 0;
|
|
45
|
+
// Step 1: Build scope index for fast lookup
|
|
46
|
+
const scopeIndex = await this.buildScopeIndex(graph);
|
|
47
|
+
logger.debug('Indexed scopes', { count: scopeIndex.size });
|
|
48
|
+
// Step 2: Build variable index (scopeId -> variables/constants/parameters)
|
|
49
|
+
const variablesByScopeIndex = await this.buildVariablesByScopeIndex(graph);
|
|
50
|
+
logger.debug('Indexed variables by scope', { scopes: variablesByScopeIndex.size });
|
|
51
|
+
// Step 3: Find all closure scopes
|
|
52
|
+
const closureScopes = [];
|
|
53
|
+
for await (const node of graph.queryNodes({ type: 'SCOPE' })) {
|
|
54
|
+
const scope = node;
|
|
55
|
+
if (scope.scopeType === 'closure') {
|
|
56
|
+
closureScopes.push(scope);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
logger.info('Found closure scopes', { count: closureScopes.length });
|
|
60
|
+
// Step 4: Build existing CAPTURES edge set to avoid duplicates
|
|
61
|
+
const existingCaptures = await this.buildExistingCapturesSet(graph);
|
|
62
|
+
logger.debug('Existing CAPTURES edges', { count: existingCaptures.size });
|
|
63
|
+
// Step 5: Process each closure
|
|
64
|
+
for (const closure of closureScopes) {
|
|
65
|
+
closuresProcessed++;
|
|
66
|
+
// Progress reporting
|
|
67
|
+
if (onProgress && closuresProcessed % 50 === 0) {
|
|
68
|
+
onProgress({
|
|
69
|
+
phase: 'enrichment',
|
|
70
|
+
currentPlugin: 'ClosureCaptureEnricher',
|
|
71
|
+
message: `Processing closures ${closuresProcessed}/${closureScopes.length}`,
|
|
72
|
+
totalFiles: closureScopes.length,
|
|
73
|
+
processedFiles: closuresProcessed
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
// Walk scope chain upward
|
|
77
|
+
const ancestors = this.walkScopeChain(closure.id, scopeIndex);
|
|
78
|
+
// For each ancestor scope (depth > 1), find variables and create edges
|
|
79
|
+
for (const ancestor of ancestors) {
|
|
80
|
+
if (ancestor.depth <= 1)
|
|
81
|
+
continue; // Skip immediate parent (already handled by JSASTAnalyzer)
|
|
82
|
+
const variables = variablesByScopeIndex.get(ancestor.scopeId) || [];
|
|
83
|
+
for (const variable of variables) {
|
|
84
|
+
const edgeKey = `${closure.id}:${variable.id}`;
|
|
85
|
+
if (existingCaptures.has(edgeKey)) {
|
|
86
|
+
existingCapturesSkipped++;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
await graph.addEdge({
|
|
90
|
+
src: closure.id,
|
|
91
|
+
dst: variable.id,
|
|
92
|
+
type: 'CAPTURES',
|
|
93
|
+
metadata: { depth: ancestor.depth }
|
|
94
|
+
});
|
|
95
|
+
capturesCreated++;
|
|
96
|
+
existingCaptures.add(edgeKey); // Track to avoid duplicates
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const summary = {
|
|
101
|
+
closuresProcessed,
|
|
102
|
+
capturesCreated,
|
|
103
|
+
existingCapturesSkipped
|
|
104
|
+
};
|
|
105
|
+
logger.info('Summary', summary);
|
|
106
|
+
return createSuccessResult({ nodes: 0, edges: capturesCreated }, summary);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Build index: scopeId -> ScopeNode
|
|
110
|
+
*/
|
|
111
|
+
async buildScopeIndex(graph) {
|
|
112
|
+
const index = new Map();
|
|
113
|
+
for await (const node of graph.queryNodes({ type: 'SCOPE' })) {
|
|
114
|
+
index.set(node.id, node);
|
|
115
|
+
}
|
|
116
|
+
return index;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Build index: scopeId -> VariableNode[]
|
|
120
|
+
* Includes VARIABLE, CONSTANT, and PARAMETER nodes
|
|
121
|
+
*/
|
|
122
|
+
async buildVariablesByScopeIndex(graph) {
|
|
123
|
+
const index = new Map();
|
|
124
|
+
// Index VARIABLE nodes via parentScopeId
|
|
125
|
+
for await (const node of graph.queryNodes({ type: 'VARIABLE' })) {
|
|
126
|
+
const variable = node;
|
|
127
|
+
if (!variable.parentScopeId)
|
|
128
|
+
continue;
|
|
129
|
+
const vars = index.get(variable.parentScopeId) || [];
|
|
130
|
+
vars.push(variable);
|
|
131
|
+
index.set(variable.parentScopeId, vars);
|
|
132
|
+
}
|
|
133
|
+
// Index CONSTANT nodes via parentScopeId
|
|
134
|
+
for await (const node of graph.queryNodes({ type: 'CONSTANT' })) {
|
|
135
|
+
const constant = node;
|
|
136
|
+
if (!constant.parentScopeId)
|
|
137
|
+
continue;
|
|
138
|
+
const vars = index.get(constant.parentScopeId) || [];
|
|
139
|
+
vars.push(constant);
|
|
140
|
+
index.set(constant.parentScopeId, vars);
|
|
141
|
+
}
|
|
142
|
+
// Index PARAMETER nodes via parentFunctionId -> HAS_SCOPE lookup
|
|
143
|
+
for await (const node of graph.queryNodes({ type: 'PARAMETER' })) {
|
|
144
|
+
const param = node;
|
|
145
|
+
if (!param.parentFunctionId)
|
|
146
|
+
continue;
|
|
147
|
+
// Find the function's scope via HAS_SCOPE edge
|
|
148
|
+
// FUNCTION -[HAS_SCOPE]-> SCOPE
|
|
149
|
+
const scopeEdges = await graph.getOutgoingEdges(param.parentFunctionId, ['HAS_SCOPE']);
|
|
150
|
+
if (scopeEdges.length === 0)
|
|
151
|
+
continue;
|
|
152
|
+
const scopeId = scopeEdges[0].dst;
|
|
153
|
+
const params = index.get(scopeId) || [];
|
|
154
|
+
params.push(param);
|
|
155
|
+
index.set(scopeId, params);
|
|
156
|
+
}
|
|
157
|
+
return index;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Build set of existing CAPTURES edges: "srcId:dstId"
|
|
161
|
+
*/
|
|
162
|
+
async buildExistingCapturesSet(graph) {
|
|
163
|
+
const set = new Set();
|
|
164
|
+
// Query all SCOPE nodes and get their CAPTURES edges
|
|
165
|
+
for await (const node of graph.queryNodes({ type: 'SCOPE' })) {
|
|
166
|
+
const edges = await graph.getOutgoingEdges(node.id, ['CAPTURES']);
|
|
167
|
+
for (const edge of edges) {
|
|
168
|
+
set.add(`${edge.src}:${edge.dst}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return set;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Walk scope chain upward from startScopeId
|
|
175
|
+
* Returns ancestor scopes with depth (1 = immediate parent, 2 = grandparent, etc.)
|
|
176
|
+
*
|
|
177
|
+
* Walks ALL scopes in the chain (including if/for/while blocks),
|
|
178
|
+
* not just closures.
|
|
179
|
+
*/
|
|
180
|
+
walkScopeChain(startScopeId, scopeIndex) {
|
|
181
|
+
const result = [];
|
|
182
|
+
const visited = new Set();
|
|
183
|
+
const currentScope = scopeIndex.get(startScopeId);
|
|
184
|
+
if (!currentScope)
|
|
185
|
+
return result;
|
|
186
|
+
// Start walking from the closure's capturesFrom (immediate parent) or parentScopeId
|
|
187
|
+
let parentId = currentScope.capturesFrom || currentScope.parentScopeId;
|
|
188
|
+
let depth = 1;
|
|
189
|
+
while (parentId && depth <= ClosureCaptureEnricher.MAX_DEPTH) {
|
|
190
|
+
// Cycle protection
|
|
191
|
+
if (visited.has(parentId))
|
|
192
|
+
break;
|
|
193
|
+
visited.add(parentId);
|
|
194
|
+
result.push({ scopeId: parentId, depth });
|
|
195
|
+
// Get parent scope
|
|
196
|
+
const parentScope = scopeIndex.get(parentId);
|
|
197
|
+
if (!parentScope)
|
|
198
|
+
break;
|
|
199
|
+
// Move up the chain via parentScopeId
|
|
200
|
+
parentId = parentScope.parentScopeId;
|
|
201
|
+
depth++;
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
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 } from '../Plugin.js';
|
|
24
|
+
import type { PluginContext, PluginResult, PluginMetadata } from '../Plugin.js';
|
|
25
|
+
export declare class ExternalCallResolver extends Plugin {
|
|
26
|
+
get metadata(): PluginMetadata;
|
|
27
|
+
execute(context: PluginContext): Promise<PluginResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Extract package name from import source.
|
|
30
|
+
*
|
|
31
|
+
* Handles:
|
|
32
|
+
* - Simple packages: 'lodash' -> 'lodash'
|
|
33
|
+
* - Scoped packages: '@tanstack/react-query' -> '@tanstack/react-query'
|
|
34
|
+
* - Subpath imports: 'lodash/map' -> 'lodash'
|
|
35
|
+
* - Scoped subpath: '@scope/pkg/sub' -> '@scope/pkg'
|
|
36
|
+
*
|
|
37
|
+
* @param source - Import source string
|
|
38
|
+
* @returns Package name or null if invalid
|
|
39
|
+
*/
|
|
40
|
+
private extractPackageName;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=ExternalCallResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExternalCallResolver.d.ts","sourceRoot":"","sources":["../../../src/plugins/enrichment/ExternalCallResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,MAAM,EAAuB,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAoBhF,qBAAa,oBAAqB,SAAQ,MAAM;IAC9C,IAAI,QAAQ,IAAI,cAAc,CAW7B;IAEK,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IA0K5D;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,kBAAkB;CAqB3B"}
|