@grafema/core 0.1.1-alpha → 0.2.0-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 +12 -18
- package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -1
- package/dist/storage/backends/RFDBServerBackend.js +41 -52
- 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 +58 -70
- package/src/storage/backends/typeValidation.ts +1 -0
|
@@ -16,7 +16,7 @@ import type { NodePath } from '@babel/traverse';
|
|
|
16
16
|
import { Plugin, createSuccessResult, createErrorResult } from '../Plugin.js';
|
|
17
17
|
import type { PluginContext, PluginResult, PluginMetadata } from '../Plugin.js';
|
|
18
18
|
import type { NodeRecord } from '@grafema/types';
|
|
19
|
-
import { getLine } from './ast/utils/location.js';
|
|
19
|
+
import { getLine, getColumn } from './ast/utils/location.js';
|
|
20
20
|
|
|
21
21
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
22
|
const traverse = (traverseModule as any).default || traverseModule;
|
|
@@ -31,8 +31,10 @@ interface EndpointNode {
|
|
|
31
31
|
path: string;
|
|
32
32
|
file: string;
|
|
33
33
|
line: number;
|
|
34
|
+
column: number;
|
|
34
35
|
routerName: string;
|
|
35
36
|
handlerLine: number;
|
|
37
|
+
handlerColumn: number;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
/**
|
|
@@ -44,6 +46,7 @@ interface MiddlewareNode {
|
|
|
44
46
|
name: string;
|
|
45
47
|
file: string;
|
|
46
48
|
line: number;
|
|
49
|
+
column: number;
|
|
47
50
|
endpointId?: string;
|
|
48
51
|
order?: number;
|
|
49
52
|
mountPath?: string;
|
|
@@ -216,6 +219,33 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
216
219
|
// Последний handler - это route handler
|
|
217
220
|
const mainHandler = handlers[handlers.length - 1];
|
|
218
221
|
|
|
222
|
+
// Unwrap wrapper functions (asyncHandler, catchAsync, etc.)
|
|
223
|
+
// Pattern: wrapper(async (req, res) => {...}) -> extract inner function
|
|
224
|
+
// Also handles nested wrappers: outer(inner(handler))
|
|
225
|
+
let actualHandler = mainHandler as Node;
|
|
226
|
+
while (actualHandler.type === 'CallExpression') {
|
|
227
|
+
const callExpr = actualHandler as CallExpression;
|
|
228
|
+
const firstArg = callExpr.arguments[0] as Node | undefined;
|
|
229
|
+
if (!firstArg) {
|
|
230
|
+
// No arguments - not a wrapper pattern
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
if (
|
|
234
|
+
firstArg.type === 'ArrowFunctionExpression' ||
|
|
235
|
+
firstArg.type === 'FunctionExpression'
|
|
236
|
+
) {
|
|
237
|
+
// Found the actual handler function
|
|
238
|
+
actualHandler = firstArg;
|
|
239
|
+
break;
|
|
240
|
+
} else if (firstArg.type === 'CallExpression') {
|
|
241
|
+
// Nested wrapper: outer(inner(...)) - continue unwrapping
|
|
242
|
+
actualHandler = firstArg;
|
|
243
|
+
} else {
|
|
244
|
+
// First arg is not a function or CallExpression - not a wrapper pattern
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
219
249
|
// Все предыдущие - middleware
|
|
220
250
|
const middlewareHandlers = handlers.slice(0, -1);
|
|
221
251
|
|
|
@@ -229,10 +259,14 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
229
259
|
path: routePath,
|
|
230
260
|
file: module.file!,
|
|
231
261
|
line: getLine(node),
|
|
262
|
+
column: getColumn(node),
|
|
232
263
|
routerName: objectName,
|
|
233
|
-
handlerLine:
|
|
234
|
-
? getLine(
|
|
235
|
-
: getLine(node)
|
|
264
|
+
handlerLine: actualHandler.loc
|
|
265
|
+
? getLine(actualHandler)
|
|
266
|
+
: getLine(node),
|
|
267
|
+
handlerColumn: actualHandler.loc
|
|
268
|
+
? getColumn(actualHandler)
|
|
269
|
+
: getColumn(node)
|
|
236
270
|
});
|
|
237
271
|
|
|
238
272
|
// Обрабатываем middleware
|
|
@@ -265,6 +299,7 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
265
299
|
name: middlewareName,
|
|
266
300
|
file: module.file!,
|
|
267
301
|
line: mwNode.loc ? getLine(mwNode) : getLine(node),
|
|
302
|
+
column: mwNode.loc ? getColumn(mwNode) : getColumn(node),
|
|
268
303
|
endpointId: endpointId,
|
|
269
304
|
order: index // Порядок в цепочке
|
|
270
305
|
});
|
|
@@ -309,6 +344,7 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
309
344
|
name: middlewareName,
|
|
310
345
|
file: module.file!,
|
|
311
346
|
line: getLine(node),
|
|
347
|
+
column: getColumn(node),
|
|
312
348
|
mountPath: mountPath,
|
|
313
349
|
isGlobal: mountPath === '/' // Global middleware если нет path
|
|
314
350
|
});
|
|
@@ -322,10 +358,11 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
322
358
|
|
|
323
359
|
// Создаём ENDPOINT ноды
|
|
324
360
|
for (const endpoint of endpoints) {
|
|
325
|
-
// Сохраняем
|
|
361
|
+
// Сохраняем handler location ПЕРЕД destructuring
|
|
326
362
|
const handlerLine = endpoint.handlerLine;
|
|
363
|
+
const handlerColumn = endpoint.handlerColumn;
|
|
327
364
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
328
|
-
const { handlerLine:
|
|
365
|
+
const { handlerLine: _hl, handlerColumn: _hc, routerName, ...endpointData } = endpoint;
|
|
329
366
|
|
|
330
367
|
await graph.addNode(endpointData as unknown as NodeRecord);
|
|
331
368
|
endpointsCreated++;
|
|
@@ -338,22 +375,24 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
338
375
|
});
|
|
339
376
|
edgesCreated++;
|
|
340
377
|
|
|
341
|
-
// Ищем FUNCTION ноду для handler
|
|
378
|
+
// Ищем FUNCTION ноду для handler по line+column
|
|
379
|
+
// NOTE: queryNodes не поддерживает line/column фильтрацию, поэтому фильтруем вручную
|
|
342
380
|
if (handlerLine) {
|
|
343
|
-
// Используем queryNodes вместо прямого доступа к graph.nodes
|
|
344
381
|
for await (const fn of graph.queryNodes({
|
|
345
382
|
type: 'FUNCTION',
|
|
346
|
-
file: module.file
|
|
347
|
-
line: handlerLine
|
|
383
|
+
file: module.file
|
|
348
384
|
})) {
|
|
349
|
-
//
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
385
|
+
// Проверяем точное совпадение line и column
|
|
386
|
+
if (fn.line === handlerLine && fn.column === handlerColumn) {
|
|
387
|
+
// ENDPOINT -> HANDLED_BY -> FUNCTION
|
|
388
|
+
await graph.addEdge({
|
|
389
|
+
type: 'HANDLED_BY',
|
|
390
|
+
src: endpoint.id,
|
|
391
|
+
dst: fn.id
|
|
392
|
+
});
|
|
393
|
+
edgesCreated++;
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
357
396
|
}
|
|
358
397
|
}
|
|
359
398
|
}
|
|
@@ -18,7 +18,7 @@ import { Plugin, createSuccessResult, createErrorResult } from '../Plugin.js';
|
|
|
18
18
|
import type { PluginContext, PluginResult, PluginMetadata } from '../Plugin.js';
|
|
19
19
|
import type { NodeRecord } from '@grafema/types';
|
|
20
20
|
import { NetworkRequestNode } from '../../core/nodes/NetworkRequestNode.js';
|
|
21
|
-
import { getLine } from './ast/utils/location.js';
|
|
21
|
+
import { getLine, getColumn } from './ast/utils/location.js';
|
|
22
22
|
|
|
23
23
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
24
|
const traverse = (traverseModule as any).default || traverseModule;
|
|
@@ -29,12 +29,28 @@ const traverse = (traverseModule as any).default || traverseModule;
|
|
|
29
29
|
interface HttpRequestNode {
|
|
30
30
|
id: string;
|
|
31
31
|
type: 'http:request';
|
|
32
|
+
name: string; // Human-readable name like "GET /api/users"
|
|
32
33
|
method: string;
|
|
33
34
|
url: string;
|
|
34
35
|
library: string;
|
|
35
36
|
file: string;
|
|
36
37
|
line: number;
|
|
38
|
+
column: number;
|
|
37
39
|
staticUrl: 'yes' | 'no';
|
|
40
|
+
responseDataNode?: string | null; // ID of CALL node for response.json(), response.text(), etc.
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Response consumption methods for fetch API
|
|
45
|
+
*/
|
|
46
|
+
const RESPONSE_CONSUMPTION_METHODS = ['json', 'text', 'blob', 'arrayBuffer', 'formData'];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Fetch call info with response variable name (for responseDataNode tracking)
|
|
50
|
+
*/
|
|
51
|
+
interface FetchCallInfo {
|
|
52
|
+
request: HttpRequestNode;
|
|
53
|
+
responseVarName: string | null;
|
|
38
54
|
}
|
|
39
55
|
|
|
40
56
|
/**
|
|
@@ -47,6 +63,7 @@ interface AnalysisResult {
|
|
|
47
63
|
|
|
48
64
|
export class FetchAnalyzer extends Plugin {
|
|
49
65
|
private networkNodeCreated = false;
|
|
66
|
+
private networkNodeId: string | null = null;
|
|
50
67
|
|
|
51
68
|
get metadata(): PluginMetadata {
|
|
52
69
|
return {
|
|
@@ -67,10 +84,7 @@ export class FetchAnalyzer extends Plugin {
|
|
|
67
84
|
try {
|
|
68
85
|
const { graph } = context;
|
|
69
86
|
|
|
70
|
-
//
|
|
71
|
-
const networkNode = NetworkRequestNode.create();
|
|
72
|
-
await graph.addNode(networkNode);
|
|
73
|
-
this.networkNodeCreated = true;
|
|
87
|
+
// net:request singleton created lazily in analyzeModule when first request found
|
|
74
88
|
|
|
75
89
|
// Получаем все модули
|
|
76
90
|
const modules = await this.getModules(graph);
|
|
@@ -82,7 +96,7 @@ export class FetchAnalyzer extends Plugin {
|
|
|
82
96
|
|
|
83
97
|
for (let i = 0; i < modules.length; i++) {
|
|
84
98
|
const module = modules[i];
|
|
85
|
-
const result = await this.analyzeModule(module, graph
|
|
99
|
+
const result = await this.analyzeModule(module, graph);
|
|
86
100
|
requestsCount += result.requests;
|
|
87
101
|
apisCount += result.apis;
|
|
88
102
|
|
|
@@ -118,10 +132,23 @@ export class FetchAnalyzer extends Plugin {
|
|
|
118
132
|
}
|
|
119
133
|
}
|
|
120
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Ensures net:request singleton exists, creating it if necessary.
|
|
137
|
+
* Called lazily when first HTTP request is detected.
|
|
138
|
+
*/
|
|
139
|
+
private async ensureNetworkNode(graph: PluginContext['graph']): Promise<string> {
|
|
140
|
+
if (!this.networkNodeId) {
|
|
141
|
+
const networkNode = NetworkRequestNode.create();
|
|
142
|
+
await graph.addNode(networkNode);
|
|
143
|
+
this.networkNodeCreated = true;
|
|
144
|
+
this.networkNodeId = networkNode.id;
|
|
145
|
+
}
|
|
146
|
+
return this.networkNodeId;
|
|
147
|
+
}
|
|
148
|
+
|
|
121
149
|
private async analyzeModule(
|
|
122
150
|
module: NodeRecord,
|
|
123
|
-
graph: PluginContext['graph']
|
|
124
|
-
networkId: string
|
|
151
|
+
graph: PluginContext['graph']
|
|
125
152
|
): Promise<AnalysisResult> {
|
|
126
153
|
try {
|
|
127
154
|
const code = readFileSync(module.file!, 'utf-8');
|
|
@@ -140,7 +167,7 @@ export class FetchAnalyzer extends Plugin {
|
|
|
140
167
|
] as ParserPlugin[]
|
|
141
168
|
});
|
|
142
169
|
|
|
143
|
-
const
|
|
170
|
+
const fetchCalls: FetchCallInfo[] = [];
|
|
144
171
|
const externalAPIs = new Set<string>();
|
|
145
172
|
|
|
146
173
|
// Детект HTTP request паттернов
|
|
@@ -159,15 +186,19 @@ export class FetchAnalyzer extends Plugin {
|
|
|
159
186
|
const request: HttpRequestNode = {
|
|
160
187
|
id: `http:request#${method}:${url}#${module.file}#${line}`,
|
|
161
188
|
type: 'http:request',
|
|
189
|
+
name: `${method} ${url}`,
|
|
162
190
|
method: method,
|
|
163
191
|
url: url,
|
|
164
192
|
library: 'fetch',
|
|
165
193
|
file: module.file!,
|
|
166
194
|
line: line,
|
|
195
|
+
column: getColumn(node),
|
|
167
196
|
staticUrl: url !== 'dynamic' && url !== 'unknown' ? 'yes' : 'no'
|
|
168
197
|
};
|
|
169
198
|
|
|
170
|
-
|
|
199
|
+
// Extract response variable name for responseDataNode tracking
|
|
200
|
+
const responseVarName = this.getResponseVariableName(path);
|
|
201
|
+
fetchCalls.push({ request, responseVarName });
|
|
171
202
|
|
|
172
203
|
// Определяем внешний ли это API
|
|
173
204
|
if (this.isExternalAPI(url)) {
|
|
@@ -190,15 +221,18 @@ export class FetchAnalyzer extends Plugin {
|
|
|
190
221
|
const request: HttpRequestNode = {
|
|
191
222
|
id: `http:request#${method}:${url}#${module.file}#${line}`,
|
|
192
223
|
type: 'http:request',
|
|
224
|
+
name: `${method} ${url}`,
|
|
193
225
|
method: method,
|
|
194
226
|
url: url,
|
|
195
227
|
library: 'axios',
|
|
196
228
|
file: module.file!,
|
|
197
229
|
line: line,
|
|
230
|
+
column: getColumn(node),
|
|
198
231
|
staticUrl: url !== 'dynamic' && url !== 'unknown' ? 'yes' : 'no'
|
|
199
232
|
};
|
|
200
233
|
|
|
201
|
-
|
|
234
|
+
// Axios uses response.data, not response.json() - out of scope for v1.0
|
|
235
|
+
fetchCalls.push({ request, responseVarName: null });
|
|
202
236
|
|
|
203
237
|
if (this.isExternalAPI(url)) {
|
|
204
238
|
externalAPIs.add(this.extractDomain(url));
|
|
@@ -234,15 +268,18 @@ export class FetchAnalyzer extends Plugin {
|
|
|
234
268
|
const request: HttpRequestNode = {
|
|
235
269
|
id: `http:request#${method.toUpperCase()}:${url}#${module.file}#${line}`,
|
|
236
270
|
type: 'http:request',
|
|
271
|
+
name: `${method.toUpperCase()} ${url}`,
|
|
237
272
|
method: method.toUpperCase(),
|
|
238
273
|
url: url,
|
|
239
274
|
library: 'axios',
|
|
240
275
|
file: module.file!,
|
|
241
276
|
line: line,
|
|
277
|
+
column: getColumn(node),
|
|
242
278
|
staticUrl: url !== 'dynamic' && url !== 'unknown' ? 'yes' : 'no'
|
|
243
279
|
};
|
|
244
280
|
|
|
245
|
-
|
|
281
|
+
// Axios uses response.data, not response.json() - out of scope for v1.0
|
|
282
|
+
fetchCalls.push({ request, responseVarName: null });
|
|
246
283
|
|
|
247
284
|
if (this.isExternalAPI(url)) {
|
|
248
285
|
externalAPIs.add(this.extractDomain(url));
|
|
@@ -266,15 +303,19 @@ export class FetchAnalyzer extends Plugin {
|
|
|
266
303
|
const request: HttpRequestNode = {
|
|
267
304
|
id: `http:request#${method}:${url}#${module.file}#${line}`,
|
|
268
305
|
type: 'http:request',
|
|
306
|
+
name: `${method} ${url}`,
|
|
269
307
|
method: method,
|
|
270
308
|
url: url,
|
|
271
309
|
library: calleeName,
|
|
272
310
|
file: module.file!,
|
|
273
311
|
line: line,
|
|
312
|
+
column: getColumn(node),
|
|
274
313
|
staticUrl: url !== 'dynamic' && url !== 'unknown' ? 'yes' : 'no'
|
|
275
314
|
};
|
|
276
315
|
|
|
277
|
-
|
|
316
|
+
// Custom wrappers may use fetch-like response.json() pattern
|
|
317
|
+
const responseVarName = this.getResponseVariableName(path);
|
|
318
|
+
fetchCalls.push({ request, responseVarName });
|
|
278
319
|
|
|
279
320
|
if (this.isExternalAPI(url)) {
|
|
280
321
|
externalAPIs.add(this.extractDomain(url));
|
|
@@ -284,8 +325,26 @@ export class FetchAnalyzer extends Plugin {
|
|
|
284
325
|
}
|
|
285
326
|
});
|
|
286
327
|
|
|
328
|
+
// Extract httpRequests for external API handling
|
|
329
|
+
const httpRequests = fetchCalls.map(fc => fc.request);
|
|
330
|
+
|
|
287
331
|
// Создаём HTTP_REQUEST ноды
|
|
288
|
-
for (const
|
|
332
|
+
for (const fetchCall of fetchCalls) {
|
|
333
|
+
const request = fetchCall.request;
|
|
334
|
+
|
|
335
|
+
// Find responseDataNode if we have a response variable name
|
|
336
|
+
if (fetchCall.responseVarName) {
|
|
337
|
+
const responseDataNodeId = await this.findResponseJsonCall(
|
|
338
|
+
graph,
|
|
339
|
+
request.file,
|
|
340
|
+
fetchCall.responseVarName,
|
|
341
|
+
request.line
|
|
342
|
+
);
|
|
343
|
+
if (responseDataNodeId) {
|
|
344
|
+
request.responseDataNode = responseDataNodeId;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
289
348
|
await graph.addNode(request as unknown as NodeRecord);
|
|
290
349
|
|
|
291
350
|
// Создаём ребро от модуля к request
|
|
@@ -295,7 +354,8 @@ export class FetchAnalyzer extends Plugin {
|
|
|
295
354
|
dst: request.id
|
|
296
355
|
});
|
|
297
356
|
|
|
298
|
-
// http:request --CALLS--> net:request singleton
|
|
357
|
+
// http:request --CALLS--> net:request singleton (created lazily)
|
|
358
|
+
const networkId = await this.ensureNetworkNode(graph);
|
|
299
359
|
await graph.addEdge({
|
|
300
360
|
type: 'CALLS',
|
|
301
361
|
src: request.id,
|
|
@@ -328,6 +388,25 @@ export class FetchAnalyzer extends Plugin {
|
|
|
328
388
|
dst: request.id
|
|
329
389
|
});
|
|
330
390
|
}
|
|
391
|
+
|
|
392
|
+
// Find CALL node that makes this request (same file, same line)
|
|
393
|
+
// Determine expected call name based on library
|
|
394
|
+
const expectedCallNames = this.getExpectedCallNames(request.library, request.method);
|
|
395
|
+
|
|
396
|
+
for await (const callNode of graph.queryNodes({ type: 'CALL' })) {
|
|
397
|
+
if (
|
|
398
|
+
callNode.file === request.file &&
|
|
399
|
+
callNode.line === request.line &&
|
|
400
|
+
expectedCallNames.includes(callNode.name as string)
|
|
401
|
+
) {
|
|
402
|
+
await graph.addEdge({
|
|
403
|
+
type: 'MAKES_REQUEST',
|
|
404
|
+
src: callNode.id,
|
|
405
|
+
dst: request.id
|
|
406
|
+
});
|
|
407
|
+
break; // Only link to first matching CALL node
|
|
408
|
+
}
|
|
409
|
+
}
|
|
331
410
|
}
|
|
332
411
|
|
|
333
412
|
// Создаём EXTERNAL ноды для внешних API
|
|
@@ -358,7 +437,7 @@ export class FetchAnalyzer extends Plugin {
|
|
|
358
437
|
}
|
|
359
438
|
|
|
360
439
|
return {
|
|
361
|
-
requests:
|
|
440
|
+
requests: fetchCalls.length,
|
|
362
441
|
apis: externalAPIs.size
|
|
363
442
|
};
|
|
364
443
|
} catch (error) {
|
|
@@ -436,6 +515,28 @@ export class FetchAnalyzer extends Plugin {
|
|
|
436
515
|
return null;
|
|
437
516
|
}
|
|
438
517
|
|
|
518
|
+
/**
|
|
519
|
+
* Returns expected CALL node names based on library and HTTP method.
|
|
520
|
+
* Used to match http:request with corresponding CALL node.
|
|
521
|
+
*
|
|
522
|
+
* @param library - Library name (fetch, axios, or custom wrapper name)
|
|
523
|
+
* @param method - HTTP method (GET, POST, etc.)
|
|
524
|
+
* @returns Array of possible call names
|
|
525
|
+
*/
|
|
526
|
+
private getExpectedCallNames(library: string, method: string): string[] {
|
|
527
|
+
if (library === 'fetch') {
|
|
528
|
+
return ['fetch'];
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (library === 'axios') {
|
|
532
|
+
// axios.get(), axios.post(), etc. or axios() config call
|
|
533
|
+
return [`axios.${method.toLowerCase()}`, 'axios'];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Custom wrapper (authFetch, apiFetch, etc.)
|
|
537
|
+
return [library];
|
|
538
|
+
}
|
|
539
|
+
|
|
439
540
|
/**
|
|
440
541
|
* Проверяет является ли URL внешним API
|
|
441
542
|
*/
|
|
@@ -461,4 +562,91 @@ export class FetchAnalyzer extends Plugin {
|
|
|
461
562
|
return match ? match[1] : url;
|
|
462
563
|
}
|
|
463
564
|
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Extracts the variable name that holds the fetch response.
|
|
568
|
+
* Handles patterns like:
|
|
569
|
+
* - const response = await fetch(url)
|
|
570
|
+
* - let res = await fetch(url)
|
|
571
|
+
* - response = await fetch(url)
|
|
572
|
+
*
|
|
573
|
+
* @param path - NodePath of the CallExpression
|
|
574
|
+
* @returns Variable name or null if not found
|
|
575
|
+
*/
|
|
576
|
+
private getResponseVariableName(path: NodePath<CallExpression>): string | null {
|
|
577
|
+
// Walk up the AST to find the variable assignment
|
|
578
|
+
let current: NodePath | null = path.parentPath;
|
|
579
|
+
|
|
580
|
+
// Pattern: await fetch()
|
|
581
|
+
if (current && current.node.type === 'AwaitExpression') {
|
|
582
|
+
current = current.parentPath;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (!current) return null;
|
|
586
|
+
|
|
587
|
+
// Pattern: const response = await fetch() / let response = await fetch()
|
|
588
|
+
if (current.node.type === 'VariableDeclarator') {
|
|
589
|
+
const declarator = current.node as { id: Node };
|
|
590
|
+
if (declarator.id.type === 'Identifier') {
|
|
591
|
+
return (declarator.id as Identifier).name;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Pattern: response = await fetch() (reassignment)
|
|
596
|
+
if (current.node.type === 'AssignmentExpression') {
|
|
597
|
+
const assignment = current.node as { left: Node };
|
|
598
|
+
if (assignment.left.type === 'Identifier') {
|
|
599
|
+
return (assignment.left as Identifier).name;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Finds the response consumption CALL node (response.json(), response.text(), etc.)
|
|
608
|
+
* by querying the graph for CALL nodes in the same file with matching object name.
|
|
609
|
+
*
|
|
610
|
+
* Important: Filters to find the response.json() call AFTER the fetch line and
|
|
611
|
+
* closest to it, ensuring we match the correct call in the same function scope.
|
|
612
|
+
*
|
|
613
|
+
* @param graph - Graph backend
|
|
614
|
+
* @param file - File path where fetch call is located
|
|
615
|
+
* @param responseVarName - Variable name holding the response
|
|
616
|
+
* @param fetchLine - Line number of the fetch call (response.json must be after this)
|
|
617
|
+
* @returns CALL node ID or null if not found
|
|
618
|
+
*/
|
|
619
|
+
private async findResponseJsonCall(
|
|
620
|
+
graph: PluginContext['graph'],
|
|
621
|
+
file: string,
|
|
622
|
+
responseVarName: string,
|
|
623
|
+
fetchLine: number
|
|
624
|
+
): Promise<string | null> {
|
|
625
|
+
// Collect all matching CALL nodes
|
|
626
|
+
const candidates: Array<{ id: string; line: number }> = [];
|
|
627
|
+
|
|
628
|
+
for await (const node of graph.queryNodes({ type: 'CALL' })) {
|
|
629
|
+
// Check if it's in the same file
|
|
630
|
+
if (node.file !== file) continue;
|
|
631
|
+
|
|
632
|
+
// Check if object matches response variable name
|
|
633
|
+
const callNode = node as unknown as { object?: string; method?: string };
|
|
634
|
+
if (callNode.object !== responseVarName) continue;
|
|
635
|
+
|
|
636
|
+
// Check if method is a response consumption method
|
|
637
|
+
if (callNode.method && RESPONSE_CONSUMPTION_METHODS.includes(callNode.method)) {
|
|
638
|
+
const nodeLine = node.line ?? 0;
|
|
639
|
+
// Only consider calls AFTER the fetch line (response.json comes after fetch)
|
|
640
|
+
if (nodeLine > fetchLine) {
|
|
641
|
+
candidates.push({ id: node.id, line: nodeLine });
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (candidates.length === 0) return null;
|
|
647
|
+
|
|
648
|
+
// Return the closest match (smallest line number after fetch)
|
|
649
|
+
candidates.sort((a, b) => a.line - b.line);
|
|
650
|
+
return candidates[0].id;
|
|
651
|
+
}
|
|
464
652
|
}
|