@grafema/core 0.1.0-alpha.5 → 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/README.md +0 -1
- package/dist/Orchestrator.d.ts +31 -2
- package/dist/Orchestrator.d.ts.map +1 -1
- package/dist/Orchestrator.js +222 -27
- package/dist/config/ConfigLoader.d.ts +90 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -0
- package/dist/config/ConfigLoader.js +249 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/core/ASTWorker.d.ts +11 -36
- package/dist/core/ASTWorker.d.ts.map +1 -1
- package/dist/core/ASTWorker.js +93 -99
- package/dist/core/CoverageAnalyzer.d.ts +65 -0
- package/dist/core/CoverageAnalyzer.d.ts.map +1 -0
- package/dist/core/CoverageAnalyzer.js +198 -0
- 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/FileNodeManager.d.ts +40 -0
- package/dist/core/FileNodeManager.d.ts.map +1 -0
- package/dist/core/FileNodeManager.js +84 -0
- package/dist/core/GraphFreshnessChecker.d.ts +33 -0
- package/dist/core/GraphFreshnessChecker.d.ts.map +1 -0
- package/dist/core/GraphFreshnessChecker.js +101 -0
- package/dist/core/HashUtils.d.ts +24 -0
- package/dist/core/HashUtils.d.ts.map +1 -0
- package/dist/core/HashUtils.js +45 -0
- package/dist/core/IncrementalReanalyzer.d.ts +36 -0
- package/dist/core/IncrementalReanalyzer.d.ts.map +1 -0
- package/dist/core/IncrementalReanalyzer.js +132 -0
- package/dist/core/NodeFactory.d.ts +266 -19
- package/dist/core/NodeFactory.d.ts.map +1 -1
- package/dist/core/NodeFactory.js +256 -21
- package/dist/core/ScopeTracker.d.ts +84 -0
- package/dist/core/ScopeTracker.d.ts.map +1 -0
- package/dist/core/ScopeTracker.js +116 -0
- package/dist/core/SemanticId.d.ts +90 -0
- package/dist/core/SemanticId.d.ts.map +1 -0
- package/dist/core/SemanticId.js +115 -0
- package/dist/core/VersionManager.d.ts.map +1 -1
- package/dist/core/VersionManager.js +3 -2
- package/dist/core/nodes/ArgumentExpressionNode.d.ts +43 -0
- package/dist/core/nodes/ArgumentExpressionNode.d.ts.map +1 -0
- package/dist/core/nodes/ArgumentExpressionNode.js +60 -0
- package/dist/core/nodes/ArrayLiteralNode.d.ts +27 -0
- package/dist/core/nodes/ArrayLiteralNode.d.ts.map +1 -0
- package/dist/core/nodes/ArrayLiteralNode.js +43 -0
- 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 +30 -2
- package/dist/core/nodes/CallSiteNode.d.ts.map +1 -1
- package/dist/core/nodes/CallSiteNode.js +54 -4
- 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 +34 -2
- package/dist/core/nodes/ClassNode.d.ts.map +1 -1
- package/dist/core/nodes/ClassNode.js +52 -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 +42 -0
- package/dist/core/nodes/DecoratorNode.d.ts.map +1 -0
- package/dist/core/nodes/DecoratorNode.js +64 -0
- package/dist/core/nodes/EnumNode.d.ts +42 -0
- package/dist/core/nodes/EnumNode.d.ts.map +1 -0
- package/dist/core/nodes/EnumNode.js +56 -0
- 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 +38 -2
- package/dist/core/nodes/ExportNode.d.ts.map +1 -1
- package/dist/core/nodes/ExportNode.js +54 -4
- package/dist/core/nodes/ExpressionNode.d.ts +97 -0
- package/dist/core/nodes/ExpressionNode.d.ts.map +1 -0
- package/dist/core/nodes/ExpressionNode.js +180 -0
- package/dist/core/nodes/ExternalModuleNode.d.ts +32 -0
- package/dist/core/nodes/ExternalModuleNode.d.ts.map +1 -0
- package/dist/core/nodes/ExternalModuleNode.js +49 -0
- package/dist/core/nodes/ExternalStdioNode.d.ts +13 -6
- package/dist/core/nodes/ExternalStdioNode.d.ts.map +1 -1
- package/dist/core/nodes/ExternalStdioNode.js +15 -8
- package/dist/core/nodes/FunctionNode.d.ts +36 -0
- package/dist/core/nodes/FunctionNode.d.ts.map +1 -1
- package/dist/core/nodes/FunctionNode.js +80 -1
- 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 +28 -6
- package/dist/core/nodes/ImportNode.d.ts.map +1 -1
- package/dist/core/nodes/ImportNode.js +43 -8
- package/dist/core/nodes/InterfaceNode.d.ts +46 -0
- package/dist/core/nodes/InterfaceNode.d.ts.map +1 -0
- package/dist/core/nodes/InterfaceNode.js +57 -0
- package/dist/core/nodes/IssueNode.d.ts +73 -0
- package/dist/core/nodes/IssueNode.d.ts.map +1 -0
- package/dist/core/nodes/IssueNode.js +129 -0
- 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 +32 -2
- package/dist/core/nodes/MethodCallNode.d.ts.map +1 -1
- package/dist/core/nodes/MethodCallNode.js +57 -4
- package/dist/core/nodes/MethodNode.d.ts +34 -2
- package/dist/core/nodes/MethodNode.d.ts.map +1 -1
- package/dist/core/nodes/MethodNode.js +55 -3
- package/dist/core/nodes/ModuleNode.d.ts +31 -0
- package/dist/core/nodes/ModuleNode.d.ts.map +1 -1
- package/dist/core/nodes/ModuleNode.js +37 -0
- package/dist/core/nodes/NetworkRequestNode.d.ts +54 -0
- package/dist/core/nodes/NetworkRequestNode.d.ts.map +1 -0
- package/dist/core/nodes/NetworkRequestNode.js +65 -0
- package/dist/core/nodes/ObjectLiteralNode.d.ts +27 -0
- package/dist/core/nodes/ObjectLiteralNode.d.ts.map +1 -0
- package/dist/core/nodes/ObjectLiteralNode.js +43 -0
- 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/ScopeNode.d.ts +31 -0
- package/dist/core/nodes/ScopeNode.d.ts.map +1 -1
- package/dist/core/nodes/ScopeNode.js +49 -0
- package/dist/core/nodes/TypeNode.d.ts +36 -0
- package/dist/core/nodes/TypeNode.d.ts.map +1 -0
- package/dist/core/nodes/TypeNode.js +55 -0
- package/dist/core/nodes/VariableDeclarationNode.d.ts +29 -2
- package/dist/core/nodes/VariableDeclarationNode.d.ts.map +1 -1
- package/dist/core/nodes/VariableDeclarationNode.js +48 -4
- package/dist/core/nodes/index.d.ts +15 -1
- package/dist/core/nodes/index.d.ts.map +1 -1
- package/dist/core/nodes/index.js +17 -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/DiagnosticCollector.d.ts +98 -0
- package/dist/diagnostics/DiagnosticCollector.d.ts.map +1 -0
- package/dist/diagnostics/DiagnosticCollector.js +129 -0
- package/dist/diagnostics/DiagnosticReporter.d.ts +100 -0
- package/dist/diagnostics/DiagnosticReporter.d.ts.map +1 -0
- package/dist/diagnostics/DiagnosticReporter.js +247 -0
- package/dist/diagnostics/DiagnosticWriter.d.ts +31 -0
- package/dist/diagnostics/DiagnosticWriter.d.ts.map +1 -0
- package/dist/diagnostics/DiagnosticWriter.js +43 -0
- package/dist/diagnostics/index.d.ts +14 -0
- package/dist/diagnostics/index.d.ts.map +1 -0
- package/dist/diagnostics/index.js +11 -0
- package/dist/errors/GrafemaError.d.ts +161 -0
- package/dist/errors/GrafemaError.d.ts.map +1 -0
- package/dist/errors/GrafemaError.js +181 -0
- package/dist/index.d.ts +73 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +70 -1
- package/dist/logging/Logger.d.ts +48 -0
- package/dist/logging/Logger.d.ts.map +1 -0
- package/dist/logging/Logger.js +134 -0
- package/dist/plugins/Plugin.d.ts +5 -1
- package/dist/plugins/Plugin.d.ts.map +1 -1
- package/dist/plugins/Plugin.js +33 -0
- package/dist/plugins/analysis/DatabaseAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/DatabaseAnalyzer.js +14 -6
- package/dist/plugins/analysis/ExpressAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ExpressAnalyzer.js +29 -19
- 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 +71 -29
- package/dist/plugins/analysis/FetchAnalyzer.d.ts +41 -0
- package/dist/plugins/analysis/FetchAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/FetchAnalyzer.js +187 -19
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts +6 -3
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts.map +1 -1
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.js +76 -80
- package/dist/plugins/analysis/JSASTAnalyzer.d.ts +313 -19
- package/dist/plugins/analysis/JSASTAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/JSASTAnalyzer.js +3430 -503
- package/dist/plugins/analysis/ReactAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ReactAnalyzer.js +56 -57
- package/dist/plugins/analysis/RustAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/RustAnalyzer.js +16 -11
- package/dist/plugins/analysis/SQLiteAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/SQLiteAnalyzer.js +11 -7
- package/dist/plugins/analysis/ServiceLayerAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/ServiceLayerAnalyzer.js +21 -9
- 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 +117 -21
- package/dist/plugins/analysis/SystemDbAnalyzer.d.ts.map +1 -1
- package/dist/plugins/analysis/SystemDbAnalyzer.js +15 -5
- package/dist/plugins/analysis/ast/GraphBuilder.d.ts +207 -4
- package/dist/plugins/analysis/ast/GraphBuilder.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/GraphBuilder.js +1527 -316
- package/dist/plugins/analysis/ast/IdGenerator.d.ts +105 -0
- package/dist/plugins/analysis/ast/IdGenerator.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/IdGenerator.js +116 -0
- package/dist/plugins/analysis/ast/types.d.ts +470 -5
- package/dist/plugins/analysis/ast/types.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/utils/createParameterNodes.d.ts +33 -0
- package/dist/plugins/analysis/ast/utils/createParameterNodes.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/utils/createParameterNodes.js +89 -0
- package/dist/plugins/analysis/ast/utils/index.d.ts +6 -0
- package/dist/plugins/analysis/ast/utils/index.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/utils/index.js +5 -0
- package/dist/plugins/analysis/ast/utils/location.d.ts +87 -0
- package/dist/plugins/analysis/ast/utils/location.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/utils/location.js +78 -0
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts +14 -5
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.js +6 -5
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts +100 -9
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js +674 -125
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts +4 -1
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.js +72 -32
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts +14 -1
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js +190 -63
- 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 +112 -8
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts +12 -1
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.js +36 -14
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts +20 -2
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts.map +1 -1
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.js +243 -45
- package/dist/plugins/discovery/MonorepoServiceDiscovery.d.ts.map +1 -1
- package/dist/plugins/discovery/MonorepoServiceDiscovery.js +3 -2
- package/dist/plugins/discovery/SimpleProjectDiscovery.d.ts.map +1 -1
- package/dist/plugins/discovery/SimpleProjectDiscovery.js +5 -1
- package/dist/plugins/discovery/WorkspaceDiscovery.d.ts +22 -0
- package/dist/plugins/discovery/WorkspaceDiscovery.d.ts.map +1 -0
- package/dist/plugins/discovery/WorkspaceDiscovery.js +141 -0
- package/dist/plugins/discovery/resolveSourceEntrypoint.d.ts +46 -0
- package/dist/plugins/discovery/resolveSourceEntrypoint.d.ts.map +1 -0
- package/dist/plugins/discovery/resolveSourceEntrypoint.js +86 -0
- package/dist/plugins/discovery/workspaces/detector.d.ts +21 -0
- package/dist/plugins/discovery/workspaces/detector.d.ts.map +1 -0
- package/dist/plugins/discovery/workspaces/detector.js +49 -0
- package/dist/plugins/discovery/workspaces/globResolver.d.ts +35 -0
- package/dist/plugins/discovery/workspaces/globResolver.d.ts.map +1 -0
- package/dist/plugins/discovery/workspaces/globResolver.js +184 -0
- package/dist/plugins/discovery/workspaces/index.d.ts +9 -0
- package/dist/plugins/discovery/workspaces/index.d.ts.map +1 -0
- package/dist/plugins/discovery/workspaces/index.js +8 -0
- package/dist/plugins/discovery/workspaces/parsers.d.ts +38 -0
- package/dist/plugins/discovery/workspaces/parsers.d.ts.map +1 -0
- package/dist/plugins/discovery/workspaces/parsers.js +80 -0
- package/dist/plugins/enrichment/AliasTracker.d.ts.map +1 -1
- package/dist/plugins/enrichment/AliasTracker.js +29 -8
- 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 +78 -27
- package/dist/plugins/enrichment/ImportExportLinker.d.ts.map +1 -1
- package/dist/plugins/enrichment/ImportExportLinker.js +23 -6
- package/dist/plugins/enrichment/MethodCallResolver.d.ts.map +1 -1
- package/dist/plugins/enrichment/MethodCallResolver.js +33 -13
- 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 +173 -147
- 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/PrefixEvaluator.d.ts.map +1 -1
- package/dist/plugins/enrichment/PrefixEvaluator.js +16 -7
- package/dist/plugins/enrichment/RustFFIEnricher.d.ts.map +1 -1
- package/dist/plugins/enrichment/RustFFIEnricher.js +6 -5
- package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts +22 -27
- package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts.map +1 -1
- package/dist/plugins/enrichment/ValueDomainAnalyzer.js +185 -143
- package/dist/plugins/indexing/IncrementalModuleIndexer.d.ts.map +1 -1
- package/dist/plugins/indexing/IncrementalModuleIndexer.js +23 -14
- 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 +121 -31
- package/dist/plugins/indexing/RustModuleIndexer.d.ts +1 -1
- package/dist/plugins/indexing/RustModuleIndexer.d.ts.map +1 -1
- package/dist/plugins/indexing/RustModuleIndexer.js +8 -7
- package/dist/plugins/indexing/ServiceDetector.d.ts +10 -0
- package/dist/plugins/indexing/ServiceDetector.d.ts.map +1 -1
- package/dist/plugins/indexing/ServiceDetector.js +28 -15
- 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 +103 -77
- package/dist/plugins/validation/DataFlowValidator.d.ts.map +1 -1
- package/dist/plugins/validation/DataFlowValidator.js +62 -49
- package/dist/plugins/validation/EvalBanValidator.d.ts.map +1 -1
- package/dist/plugins/validation/EvalBanValidator.js +17 -16
- package/dist/plugins/validation/GraphConnectivityValidator.d.ts.map +1 -1
- package/dist/plugins/validation/GraphConnectivityValidator.js +44 -24
- package/dist/plugins/validation/NodeCreationValidator.d.ts +85 -0
- package/dist/plugins/validation/NodeCreationValidator.d.ts.map +1 -0
- package/dist/plugins/validation/NodeCreationValidator.js +415 -0
- package/dist/plugins/validation/SQLInjectionValidator.d.ts.map +1 -1
- package/dist/plugins/validation/SQLInjectionValidator.js +61 -19
- package/dist/plugins/validation/ShadowingDetector.d.ts.map +1 -1
- package/dist/plugins/validation/ShadowingDetector.js +6 -5
- package/dist/plugins/validation/TypeScriptDeadCodeValidator.d.ts.map +1 -1
- package/dist/plugins/validation/TypeScriptDeadCodeValidator.js +12 -11
- package/dist/plugins/vcs/GitPlugin.d.ts.map +1 -1
- package/dist/plugins/vcs/GitPlugin.js +10 -12
- package/dist/plugins/vcs/VCSPlugin.d.ts +3 -2
- package/dist/plugins/vcs/VCSPlugin.d.ts.map +1 -1
- package/dist/plugins/vcs/VCSPlugin.js +5 -5
- 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 +21 -34
- package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -1
- package/dist/storage/backends/RFDBServerBackend.js +72 -62
- package/dist/storage/backends/typeValidation.d.ts.map +1 -1
- package/dist/storage/backends/typeValidation.js +1 -0
- package/dist/validation/PathValidator.d.ts +1 -2
- package/dist/validation/PathValidator.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/Orchestrator.ts +272 -27
- package/src/config/ConfigLoader.ts +354 -0
- package/src/config/index.ts +5 -0
- package/src/core/ASTWorker.ts +143 -139
- package/src/core/CoverageAnalyzer.ts +243 -0
- package/src/core/FileExplainer.ts +179 -0
- package/src/core/FileNodeManager.ts +100 -0
- package/src/core/GraphFreshnessChecker.ts +143 -0
- package/src/core/HashUtils.ts +48 -0
- package/src/core/IncrementalReanalyzer.ts +192 -0
- package/src/core/NodeFactory.ts +470 -23
- package/src/core/ScopeTracker.ts +154 -0
- package/src/core/SemanticId.ts +192 -0
- package/src/core/VersionManager.ts +3 -2
- package/src/core/nodes/ArgumentExpressionNode.ts +89 -0
- package/src/core/nodes/ArrayLiteralNode.ts +66 -0
- package/src/core/nodes/BranchNode.ts +113 -0
- package/src/core/nodes/CallSiteNode.ts +64 -4
- package/src/core/nodes/CaseNode.ts +123 -0
- package/src/core/nodes/ClassNode.ts +67 -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 +92 -0
- package/src/core/nodes/EnumNode.ts +87 -0
- package/src/core/nodes/EventListenerNode.ts +7 -4
- package/src/core/nodes/ExportNode.ts +74 -4
- package/src/core/nodes/ExpressionNode.ts +232 -0
- package/src/core/nodes/ExternalModuleNode.ts +65 -0
- package/src/core/nodes/ExternalStdioNode.ts +17 -9
- package/src/core/nodes/FunctionNode.ts +101 -1
- package/src/core/nodes/HttpRequestNode.ts +7 -4
- package/src/core/nodes/ImportNode.ts +62 -13
- package/src/core/nodes/InterfaceNode.ts +92 -0
- package/src/core/nodes/IssueNode.ts +177 -0
- package/src/core/nodes/LiteralNode.ts +5 -4
- package/src/core/nodes/MethodCallNode.ts +70 -4
- package/src/core/nodes/MethodNode.ts +68 -3
- package/src/core/nodes/ModuleNode.ts +50 -0
- package/src/core/nodes/NetworkRequestNode.ts +77 -0
- package/src/core/nodes/ObjectLiteralNode.ts +66 -0
- package/src/core/nodes/ParameterNode.ts +4 -3
- package/src/core/nodes/ScopeNode.ts +65 -0
- package/src/core/nodes/TypeNode.ts +79 -0
- package/src/core/nodes/VariableDeclarationNode.ts +58 -4
- package/src/core/nodes/index.ts +21 -1
- 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/DiagnosticCollector.ts +163 -0
- package/src/diagnostics/DiagnosticReporter.ts +324 -0
- package/src/diagnostics/DiagnosticWriter.ts +50 -0
- package/src/diagnostics/index.ts +16 -0
- package/src/errors/GrafemaError.ts +239 -0
- package/src/index.ts +193 -1
- package/src/logging/Logger.ts +152 -0
- package/src/plugins/Plugin.ts +42 -0
- package/src/plugins/analysis/DatabaseAnalyzer.ts +16 -8
- package/src/plugins/analysis/ExpressAnalyzer.ts +33 -19
- package/src/plugins/analysis/ExpressResponseAnalyzer.ts +636 -0
- package/src/plugins/analysis/ExpressRouteAnalyzer.ts +76 -36
- package/src/plugins/analysis/FetchAnalyzer.ts +232 -21
- package/src/plugins/analysis/IncrementalAnalysisPlugin.ts +84 -101
- package/src/plugins/analysis/JSASTAnalyzer.ts +4265 -587
- package/src/plugins/analysis/ReactAnalyzer.ts +57 -57
- package/src/plugins/analysis/RustAnalyzer.ts +16 -11
- package/src/plugins/analysis/SQLiteAnalyzer.ts +13 -7
- package/src/plugins/analysis/ServiceLayerAnalyzer.ts +22 -16
- package/src/plugins/analysis/SocketIOAnalyzer.ts +151 -28
- package/src/plugins/analysis/SystemDbAnalyzer.ts +16 -11
- package/src/plugins/analysis/ast/GraphBuilder.ts +1947 -327
- package/src/plugins/analysis/ast/IdGenerator.ts +177 -0
- package/src/plugins/analysis/ast/types.ts +596 -6
- package/src/plugins/analysis/ast/utils/createParameterNodes.ts +104 -0
- package/src/plugins/analysis/ast/utils/index.ts +12 -0
- package/src/plugins/analysis/ast/utils/location.ts +103 -0
- package/src/plugins/analysis/ast/visitors/ASTVisitor.ts +19 -8
- package/src/plugins/analysis/ast/visitors/CallExpressionVisitor.ts +924 -83
- package/src/plugins/analysis/ast/visitors/ClassVisitor.ts +97 -44
- package/src/plugins/analysis/ast/visitors/FunctionVisitor.ts +234 -93
- package/src/plugins/analysis/ast/visitors/ImportExportVisitor.ts +124 -9
- package/src/plugins/analysis/ast/visitors/TypeScriptVisitor.ts +41 -14
- package/src/plugins/analysis/ast/visitors/VariableVisitor.ts +294 -49
- package/src/plugins/discovery/MonorepoServiceDiscovery.ts +3 -2
- package/src/plugins/discovery/SimpleProjectDiscovery.ts +6 -1
- package/src/plugins/discovery/WorkspaceDiscovery.ts +184 -0
- package/src/plugins/discovery/resolveSourceEntrypoint.ts +103 -0
- package/src/plugins/discovery/workspaces/detector.ts +63 -0
- package/src/plugins/discovery/workspaces/globResolver.ts +229 -0
- package/src/plugins/discovery/workspaces/index.ts +23 -0
- package/src/plugins/discovery/workspaces/parsers.ts +99 -0
- package/src/plugins/enrichment/AliasTracker.ts +35 -8
- 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 +84 -27
- package/src/plugins/enrichment/ImportExportLinker.ts +24 -6
- package/src/plugins/enrichment/MethodCallResolver.ts +39 -13
- package/src/plugins/enrichment/MountPointResolver.ts +208 -195
- package/src/plugins/enrichment/NodejsBuiltinsResolver.ts +365 -0
- package/src/plugins/enrichment/PrefixEvaluator.ts +16 -7
- package/src/plugins/enrichment/RustFFIEnricher.ts +6 -5
- package/src/plugins/enrichment/ValueDomainAnalyzer.ts +209 -189
- package/src/plugins/indexing/IncrementalModuleIndexer.ts +23 -14
- package/src/plugins/indexing/JSModuleIndexer.ts +140 -34
- package/src/plugins/indexing/RustModuleIndexer.ts +8 -7
- package/src/plugins/validation/BrokenImportValidator.ts +325 -0
- package/src/plugins/validation/CallResolverValidator.ts +131 -110
- package/src/plugins/validation/DataFlowValidator.ts +88 -67
- package/src/plugins/validation/EvalBanValidator.ts +17 -16
- package/src/plugins/validation/GraphConnectivityValidator.ts +58 -24
- package/src/plugins/validation/NodeCreationValidator.ts +554 -0
- package/src/plugins/validation/SQLInjectionValidator.ts +63 -20
- package/src/plugins/validation/ShadowingDetector.ts +6 -5
- package/src/plugins/validation/TypeScriptDeadCodeValidator.ts +12 -11
- package/src/plugins/vcs/GitPlugin.ts +40 -12
- package/src/plugins/vcs/VCSPlugin.ts +7 -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 +100 -98
- package/src/storage/backends/typeValidation.ts +1 -0
- package/src/validation/PathValidator.ts +1 -1
- package/dist/core/AnalysisWorker.d.ts +0 -14
- package/dist/core/AnalysisWorker.d.ts.map +0 -1
- package/dist/core/AnalysisWorker.js +0 -307
- package/dist/core/ParallelAnalyzer.d.ts +0 -120
- package/dist/core/ParallelAnalyzer.d.ts.map +0 -1
- package/dist/core/ParallelAnalyzer.js +0 -331
- package/dist/core/QueueWorker.d.ts +0 -12
- package/dist/core/QueueWorker.d.ts.map +0 -1
- package/dist/core/QueueWorker.js +0 -567
- package/dist/core/RFDBClient.d.ts +0 -179
- package/dist/core/RFDBClient.d.ts.map +0 -1
- package/dist/core/RFDBClient.js +0 -429
- package/dist/plugins/discovery/ZonServiceDiscovery.d.ts +0 -19
- package/dist/plugins/discovery/ZonServiceDiscovery.d.ts.map +0 -1
- package/dist/plugins/discovery/ZonServiceDiscovery.js +0 -204
- package/src/core/AnalysisWorker.ts +0 -410
- package/src/core/ParallelAnalyzer.ts +0 -476
- package/src/core/QueueWorker.ts +0 -780
- package/src/plugins/indexing/ServiceDetector.ts +0 -230
|
@@ -16,6 +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, getColumn } from './ast/utils/location.js';
|
|
19
20
|
|
|
20
21
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
22
|
const traverse = (traverseModule as any).default || traverseModule;
|
|
@@ -30,8 +31,10 @@ interface EndpointNode {
|
|
|
30
31
|
path: string;
|
|
31
32
|
file: string;
|
|
32
33
|
line: number;
|
|
34
|
+
column: number;
|
|
33
35
|
routerName: string;
|
|
34
36
|
handlerLine: number;
|
|
37
|
+
handlerColumn: number;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
/**
|
|
@@ -43,6 +46,7 @@ interface MiddlewareNode {
|
|
|
43
46
|
name: string;
|
|
44
47
|
file: string;
|
|
45
48
|
line: number;
|
|
49
|
+
column: number;
|
|
46
50
|
endpointId?: string;
|
|
47
51
|
order?: number;
|
|
48
52
|
mountPath?: string;
|
|
@@ -75,12 +79,14 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
async execute(context: PluginContext): Promise<PluginResult> {
|
|
82
|
+
const logger = this.log(context);
|
|
83
|
+
|
|
78
84
|
try {
|
|
79
85
|
const { graph } = context;
|
|
80
86
|
|
|
81
87
|
// Получаем все MODULE ноды
|
|
82
88
|
const modules = await this.getModules(graph);
|
|
83
|
-
|
|
89
|
+
logger.info('Processing modules', { count: modules.length });
|
|
84
90
|
|
|
85
91
|
let endpointsCreated = 0;
|
|
86
92
|
let middlewareCreated = 0;
|
|
@@ -99,15 +105,16 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
99
105
|
if ((i + 1) % 20 === 0 || i === modules.length - 1) {
|
|
100
106
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
101
107
|
const avgTime = ((Date.now() - startTime) / (i + 1)).toFixed(0);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
logger.debug('Progress', {
|
|
109
|
+
current: i + 1,
|
|
110
|
+
total: modules.length,
|
|
111
|
+
elapsed: `${elapsed}s`,
|
|
112
|
+
avgTime: `${avgTime}ms/module`
|
|
113
|
+
});
|
|
105
114
|
}
|
|
106
115
|
}
|
|
107
116
|
|
|
108
|
-
|
|
109
|
-
`[ExpressRouteAnalyzer] Found ${endpointsCreated} endpoints, ${middlewareCreated} middleware`
|
|
110
|
-
);
|
|
117
|
+
logger.info('Analysis complete', { endpointsCreated, middlewareCreated });
|
|
111
118
|
|
|
112
119
|
return createSuccessResult(
|
|
113
120
|
{
|
|
@@ -117,7 +124,7 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
117
124
|
{ modulesAnalyzed: modules.length }
|
|
118
125
|
);
|
|
119
126
|
} catch (error) {
|
|
120
|
-
|
|
127
|
+
logger.error('Analysis failed', { error });
|
|
121
128
|
return createErrorResult(error as Error);
|
|
122
129
|
}
|
|
123
130
|
}
|
|
@@ -212,11 +219,38 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
212
219
|
// Последний handler - это route handler
|
|
213
220
|
const mainHandler = handlers[handlers.length - 1];
|
|
214
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
|
+
|
|
215
249
|
// Все предыдущие - middleware
|
|
216
250
|
const middlewareHandlers = handlers.slice(0, -1);
|
|
217
251
|
|
|
218
252
|
// Создаём http:route
|
|
219
|
-
const endpointId = `http:route#${method.toUpperCase()}:${routePath}#${module.file}#${node
|
|
253
|
+
const endpointId = `http:route#${method.toUpperCase()}:${routePath}#${module.file}#${getLine(node)}`;
|
|
220
254
|
|
|
221
255
|
endpoints.push({
|
|
222
256
|
id: endpointId,
|
|
@@ -224,11 +258,15 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
224
258
|
method: method.toUpperCase(),
|
|
225
259
|
path: routePath,
|
|
226
260
|
file: module.file!,
|
|
227
|
-
line: node
|
|
261
|
+
line: getLine(node),
|
|
262
|
+
column: getColumn(node),
|
|
228
263
|
routerName: objectName,
|
|
229
|
-
handlerLine:
|
|
230
|
-
? (
|
|
231
|
-
: node
|
|
264
|
+
handlerLine: actualHandler.loc
|
|
265
|
+
? getLine(actualHandler)
|
|
266
|
+
: getLine(node),
|
|
267
|
+
handlerColumn: actualHandler.loc
|
|
268
|
+
? getColumn(actualHandler)
|
|
269
|
+
: getColumn(node)
|
|
232
270
|
});
|
|
233
271
|
|
|
234
272
|
// Обрабатываем middleware
|
|
@@ -249,18 +287,19 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
249
287
|
mwNode.type === 'ArrowFunctionExpression' ||
|
|
250
288
|
mwNode.type === 'FunctionExpression'
|
|
251
289
|
) {
|
|
252
|
-
middlewareName = `inline:${mwNode
|
|
290
|
+
middlewareName = `inline:${getLine(mwNode)}`;
|
|
253
291
|
}
|
|
254
292
|
|
|
255
293
|
if (middlewareName) {
|
|
256
|
-
const middlewareId = `express:middleware#${middlewareName}#${module.file}#${mwNode
|
|
294
|
+
const middlewareId = `express:middleware#${middlewareName}#${module.file}#${getLine(mwNode)}`;
|
|
257
295
|
|
|
258
296
|
middlewares.push({
|
|
259
297
|
id: middlewareId,
|
|
260
298
|
type: 'express:middleware',
|
|
261
299
|
name: middlewareName,
|
|
262
300
|
file: module.file!,
|
|
263
|
-
line: mwNode.loc ? mwNode
|
|
301
|
+
line: mwNode.loc ? getLine(mwNode) : getLine(node),
|
|
302
|
+
column: mwNode.loc ? getColumn(mwNode) : getColumn(node),
|
|
264
303
|
endpointId: endpointId,
|
|
265
304
|
order: index // Порядок в цепочке
|
|
266
305
|
});
|
|
@@ -297,14 +336,15 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
297
336
|
}
|
|
298
337
|
|
|
299
338
|
if (middlewareName) {
|
|
300
|
-
const middlewareId = `express:middleware#${middlewareName}#${module.file}#${node
|
|
339
|
+
const middlewareId = `express:middleware#${middlewareName}#${module.file}#${getLine(node)}`;
|
|
301
340
|
|
|
302
341
|
middlewares.push({
|
|
303
342
|
id: middlewareId,
|
|
304
343
|
type: 'express:middleware',
|
|
305
344
|
name: middlewareName,
|
|
306
345
|
file: module.file!,
|
|
307
|
-
line: node
|
|
346
|
+
line: getLine(node),
|
|
347
|
+
column: getColumn(node),
|
|
308
348
|
mountPath: mountPath,
|
|
309
349
|
isGlobal: mountPath === '/' // Global middleware если нет path
|
|
310
350
|
});
|
|
@@ -318,10 +358,11 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
318
358
|
|
|
319
359
|
// Создаём ENDPOINT ноды
|
|
320
360
|
for (const endpoint of endpoints) {
|
|
321
|
-
// Сохраняем
|
|
361
|
+
// Сохраняем handler location ПЕРЕД destructuring
|
|
322
362
|
const handlerLine = endpoint.handlerLine;
|
|
363
|
+
const handlerColumn = endpoint.handlerColumn;
|
|
323
364
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
324
|
-
const { handlerLine:
|
|
365
|
+
const { handlerLine: _hl, handlerColumn: _hc, routerName, ...endpointData } = endpoint;
|
|
325
366
|
|
|
326
367
|
await graph.addNode(endpointData as unknown as NodeRecord);
|
|
327
368
|
endpointsCreated++;
|
|
@@ -334,22 +375,24 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
334
375
|
});
|
|
335
376
|
edgesCreated++;
|
|
336
377
|
|
|
337
|
-
// Ищем FUNCTION ноду для handler
|
|
378
|
+
// Ищем FUNCTION ноду для handler по line+column
|
|
379
|
+
// NOTE: queryNodes не поддерживает line/column фильтрацию, поэтому фильтруем вручную
|
|
338
380
|
if (handlerLine) {
|
|
339
|
-
// Используем queryNodes вместо прямого доступа к graph.nodes
|
|
340
381
|
for await (const fn of graph.queryNodes({
|
|
341
382
|
type: 'FUNCTION',
|
|
342
|
-
file: module.file
|
|
343
|
-
line: handlerLine
|
|
383
|
+
file: module.file
|
|
344
384
|
})) {
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
+
}
|
|
353
396
|
}
|
|
354
397
|
}
|
|
355
398
|
}
|
|
@@ -399,10 +442,7 @@ export class ExpressRouteAnalyzer extends Plugin {
|
|
|
399
442
|
}
|
|
400
443
|
}
|
|
401
444
|
} catch (error) {
|
|
402
|
-
|
|
403
|
-
`[ExpressRouteAnalyzer] Error analyzing ${module.file}:`,
|
|
404
|
-
(error as Error).message
|
|
405
|
-
);
|
|
445
|
+
// Silent - per-module errors shouldn't spam logs
|
|
406
446
|
}
|
|
407
447
|
|
|
408
448
|
return {
|
|
@@ -17,6 +17,8 @@ import type { NodePath } from '@babel/traverse';
|
|
|
17
17
|
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
|
+
import { NetworkRequestNode } from '../../core/nodes/NetworkRequestNode.js';
|
|
21
|
+
import { getLine, getColumn } from './ast/utils/location.js';
|
|
20
22
|
|
|
21
23
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
24
|
const traverse = (traverseModule as any).default || traverseModule;
|
|
@@ -27,12 +29,28 @@ const traverse = (traverseModule as any).default || traverseModule;
|
|
|
27
29
|
interface HttpRequestNode {
|
|
28
30
|
id: string;
|
|
29
31
|
type: 'http:request';
|
|
32
|
+
name: string; // Human-readable name like "GET /api/users"
|
|
30
33
|
method: string;
|
|
31
34
|
url: string;
|
|
32
35
|
library: string;
|
|
33
36
|
file: string;
|
|
34
37
|
line: number;
|
|
38
|
+
column: number;
|
|
35
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;
|
|
36
54
|
}
|
|
37
55
|
|
|
38
56
|
/**
|
|
@@ -44,6 +62,9 @@ interface AnalysisResult {
|
|
|
44
62
|
}
|
|
45
63
|
|
|
46
64
|
export class FetchAnalyzer extends Plugin {
|
|
65
|
+
private networkNodeCreated = false;
|
|
66
|
+
private networkNodeId: string | null = null;
|
|
67
|
+
|
|
47
68
|
get metadata(): PluginMetadata {
|
|
48
69
|
return {
|
|
49
70
|
name: 'FetchAnalyzer',
|
|
@@ -58,12 +79,16 @@ export class FetchAnalyzer extends Plugin {
|
|
|
58
79
|
}
|
|
59
80
|
|
|
60
81
|
async execute(context: PluginContext): Promise<PluginResult> {
|
|
82
|
+
const logger = this.log(context);
|
|
83
|
+
|
|
61
84
|
try {
|
|
62
85
|
const { graph } = context;
|
|
63
86
|
|
|
87
|
+
// net:request singleton created lazily in analyzeModule when first request found
|
|
88
|
+
|
|
64
89
|
// Получаем все модули
|
|
65
90
|
const modules = await this.getModules(graph);
|
|
66
|
-
|
|
91
|
+
logger.info('Processing modules', { count: modules.length });
|
|
67
92
|
|
|
68
93
|
let requestsCount = 0;
|
|
69
94
|
let apisCount = 0;
|
|
@@ -79,30 +104,48 @@ export class FetchAnalyzer extends Plugin {
|
|
|
79
104
|
if ((i + 1) % 20 === 0 || i === modules.length - 1) {
|
|
80
105
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
81
106
|
const avgTime = ((Date.now() - startTime) / (i + 1)).toFixed(0);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
107
|
+
logger.debug('Progress', {
|
|
108
|
+
current: i + 1,
|
|
109
|
+
total: modules.length,
|
|
110
|
+
elapsed: `${elapsed}s`,
|
|
111
|
+
avgTime: `${avgTime}ms/module`
|
|
112
|
+
});
|
|
85
113
|
}
|
|
86
114
|
}
|
|
87
115
|
|
|
88
|
-
|
|
116
|
+
logger.info('Analysis complete', { requestsCount, apisCount });
|
|
89
117
|
|
|
90
118
|
return createSuccessResult(
|
|
91
119
|
{
|
|
92
|
-
nodes: requestsCount + apisCount,
|
|
93
|
-
edges:
|
|
120
|
+
nodes: requestsCount + apisCount + (this.networkNodeCreated ? 1 : 0),
|
|
121
|
+
edges: requestsCount // CALLS edges from http:request to net:request
|
|
94
122
|
},
|
|
95
123
|
{
|
|
96
124
|
requestsCount,
|
|
97
|
-
apisCount
|
|
125
|
+
apisCount,
|
|
126
|
+
networkSingletonCreated: this.networkNodeCreated
|
|
98
127
|
}
|
|
99
128
|
);
|
|
100
129
|
} catch (error) {
|
|
101
|
-
|
|
130
|
+
logger.error('Analysis failed', { error });
|
|
102
131
|
return createErrorResult(error as Error);
|
|
103
132
|
}
|
|
104
133
|
}
|
|
105
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
|
+
|
|
106
149
|
private async analyzeModule(
|
|
107
150
|
module: NodeRecord,
|
|
108
151
|
graph: PluginContext['graph']
|
|
@@ -124,7 +167,7 @@ export class FetchAnalyzer extends Plugin {
|
|
|
124
167
|
] as ParserPlugin[]
|
|
125
168
|
});
|
|
126
169
|
|
|
127
|
-
const
|
|
170
|
+
const fetchCalls: FetchCallInfo[] = [];
|
|
128
171
|
const externalAPIs = new Set<string>();
|
|
129
172
|
|
|
130
173
|
// Детект HTTP request паттернов
|
|
@@ -138,20 +181,24 @@ export class FetchAnalyzer extends Plugin {
|
|
|
138
181
|
const urlArg = node.arguments[0];
|
|
139
182
|
const url = this.extractURL(urlArg);
|
|
140
183
|
const method = this.extractMethod(node.arguments[1]) || 'GET';
|
|
141
|
-
const line = node
|
|
184
|
+
const line = getLine(node);
|
|
142
185
|
|
|
143
186
|
const request: HttpRequestNode = {
|
|
144
187
|
id: `http:request#${method}:${url}#${module.file}#${line}`,
|
|
145
188
|
type: 'http:request',
|
|
189
|
+
name: `${method} ${url}`,
|
|
146
190
|
method: method,
|
|
147
191
|
url: url,
|
|
148
192
|
library: 'fetch',
|
|
149
193
|
file: module.file!,
|
|
150
194
|
line: line,
|
|
195
|
+
column: getColumn(node),
|
|
151
196
|
staticUrl: url !== 'dynamic' && url !== 'unknown' ? 'yes' : 'no'
|
|
152
197
|
};
|
|
153
198
|
|
|
154
|
-
|
|
199
|
+
// Extract response variable name for responseDataNode tracking
|
|
200
|
+
const responseVarName = this.getResponseVariableName(path);
|
|
201
|
+
fetchCalls.push({ request, responseVarName });
|
|
155
202
|
|
|
156
203
|
// Определяем внешний ли это API
|
|
157
204
|
if (this.isExternalAPI(url)) {
|
|
@@ -169,20 +216,23 @@ export class FetchAnalyzer extends Plugin {
|
|
|
169
216
|
const method = ((callee as MemberExpression).property as Identifier).name.toUpperCase();
|
|
170
217
|
const urlArg = node.arguments[0];
|
|
171
218
|
const url = this.extractURL(urlArg);
|
|
172
|
-
const line = node
|
|
219
|
+
const line = getLine(node);
|
|
173
220
|
|
|
174
221
|
const request: HttpRequestNode = {
|
|
175
222
|
id: `http:request#${method}:${url}#${module.file}#${line}`,
|
|
176
223
|
type: 'http:request',
|
|
224
|
+
name: `${method} ${url}`,
|
|
177
225
|
method: method,
|
|
178
226
|
url: url,
|
|
179
227
|
library: 'axios',
|
|
180
228
|
file: module.file!,
|
|
181
229
|
line: line,
|
|
230
|
+
column: getColumn(node),
|
|
182
231
|
staticUrl: url !== 'dynamic' && url !== 'unknown' ? 'yes' : 'no'
|
|
183
232
|
};
|
|
184
233
|
|
|
185
|
-
|
|
234
|
+
// Axios uses response.data, not response.json() - out of scope for v1.0
|
|
235
|
+
fetchCalls.push({ request, responseVarName: null });
|
|
186
236
|
|
|
187
237
|
if (this.isExternalAPI(url)) {
|
|
188
238
|
externalAPIs.add(this.extractDomain(url));
|
|
@@ -213,20 +263,23 @@ export class FetchAnalyzer extends Plugin {
|
|
|
213
263
|
const method = methodProp
|
|
214
264
|
? this.extractString((methodProp as { value: Node }).value) || 'GET'
|
|
215
265
|
: 'GET';
|
|
216
|
-
const line = node
|
|
266
|
+
const line = getLine(node);
|
|
217
267
|
|
|
218
268
|
const request: HttpRequestNode = {
|
|
219
269
|
id: `http:request#${method.toUpperCase()}:${url}#${module.file}#${line}`,
|
|
220
270
|
type: 'http:request',
|
|
271
|
+
name: `${method.toUpperCase()} ${url}`,
|
|
221
272
|
method: method.toUpperCase(),
|
|
222
273
|
url: url,
|
|
223
274
|
library: 'axios',
|
|
224
275
|
file: module.file!,
|
|
225
276
|
line: line,
|
|
277
|
+
column: getColumn(node),
|
|
226
278
|
staticUrl: url !== 'dynamic' && url !== 'unknown' ? 'yes' : 'no'
|
|
227
279
|
};
|
|
228
280
|
|
|
229
|
-
|
|
281
|
+
// Axios uses response.data, not response.json() - out of scope for v1.0
|
|
282
|
+
fetchCalls.push({ request, responseVarName: null });
|
|
230
283
|
|
|
231
284
|
if (this.isExternalAPI(url)) {
|
|
232
285
|
externalAPIs.add(this.extractDomain(url));
|
|
@@ -245,20 +298,24 @@ export class FetchAnalyzer extends Plugin {
|
|
|
245
298
|
const urlArg = node.arguments[0];
|
|
246
299
|
const url = this.extractURL(urlArg);
|
|
247
300
|
const method = this.extractMethod(node.arguments[1]) || 'GET';
|
|
248
|
-
const line = node
|
|
301
|
+
const line = getLine(node);
|
|
249
302
|
|
|
250
303
|
const request: HttpRequestNode = {
|
|
251
304
|
id: `http:request#${method}:${url}#${module.file}#${line}`,
|
|
252
305
|
type: 'http:request',
|
|
306
|
+
name: `${method} ${url}`,
|
|
253
307
|
method: method,
|
|
254
308
|
url: url,
|
|
255
309
|
library: calleeName,
|
|
256
310
|
file: module.file!,
|
|
257
311
|
line: line,
|
|
312
|
+
column: getColumn(node),
|
|
258
313
|
staticUrl: url !== 'dynamic' && url !== 'unknown' ? 'yes' : 'no'
|
|
259
314
|
};
|
|
260
315
|
|
|
261
|
-
|
|
316
|
+
// Custom wrappers may use fetch-like response.json() pattern
|
|
317
|
+
const responseVarName = this.getResponseVariableName(path);
|
|
318
|
+
fetchCalls.push({ request, responseVarName });
|
|
262
319
|
|
|
263
320
|
if (this.isExternalAPI(url)) {
|
|
264
321
|
externalAPIs.add(this.extractDomain(url));
|
|
@@ -268,8 +325,26 @@ export class FetchAnalyzer extends Plugin {
|
|
|
268
325
|
}
|
|
269
326
|
});
|
|
270
327
|
|
|
328
|
+
// Extract httpRequests for external API handling
|
|
329
|
+
const httpRequests = fetchCalls.map(fc => fc.request);
|
|
330
|
+
|
|
271
331
|
// Создаём HTTP_REQUEST ноды
|
|
272
|
-
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
|
+
|
|
273
348
|
await graph.addNode(request as unknown as NodeRecord);
|
|
274
349
|
|
|
275
350
|
// Создаём ребро от модуля к request
|
|
@@ -279,6 +354,14 @@ export class FetchAnalyzer extends Plugin {
|
|
|
279
354
|
dst: request.id
|
|
280
355
|
});
|
|
281
356
|
|
|
357
|
+
// http:request --CALLS--> net:request singleton (created lazily)
|
|
358
|
+
const networkId = await this.ensureNetworkNode(graph);
|
|
359
|
+
await graph.addEdge({
|
|
360
|
+
type: 'CALLS',
|
|
361
|
+
src: request.id,
|
|
362
|
+
dst: networkId
|
|
363
|
+
});
|
|
364
|
+
|
|
282
365
|
// Ищем FUNCTION node которая делает запрос
|
|
283
366
|
const functions: NodeRecord[] = [];
|
|
284
367
|
for await (const fn of graph.queryNodes({ type: 'FUNCTION' })) {
|
|
@@ -305,6 +388,25 @@ export class FetchAnalyzer extends Plugin {
|
|
|
305
388
|
dst: request.id
|
|
306
389
|
});
|
|
307
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
|
+
}
|
|
308
410
|
}
|
|
309
411
|
|
|
310
412
|
// Создаём EXTERNAL ноды для внешних API
|
|
@@ -335,11 +437,11 @@ export class FetchAnalyzer extends Plugin {
|
|
|
335
437
|
}
|
|
336
438
|
|
|
337
439
|
return {
|
|
338
|
-
requests:
|
|
440
|
+
requests: fetchCalls.length,
|
|
339
441
|
apis: externalAPIs.size
|
|
340
442
|
};
|
|
341
443
|
} catch (error) {
|
|
342
|
-
|
|
444
|
+
// Silent - per-module errors shouldn't spam logs
|
|
343
445
|
return { requests: 0, apis: 0 };
|
|
344
446
|
}
|
|
345
447
|
}
|
|
@@ -413,6 +515,28 @@ export class FetchAnalyzer extends Plugin {
|
|
|
413
515
|
return null;
|
|
414
516
|
}
|
|
415
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
|
+
|
|
416
540
|
/**
|
|
417
541
|
* Проверяет является ли URL внешним API
|
|
418
542
|
*/
|
|
@@ -438,4 +562,91 @@ export class FetchAnalyzer extends Plugin {
|
|
|
438
562
|
return match ? match[1] : url;
|
|
439
563
|
}
|
|
440
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
|
+
}
|
|
441
652
|
}
|