@grafema/core 0.1.0-alpha.1
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/LICENSE +190 -0
- package/README.md +76 -0
- package/dist/Orchestrator.d.ts +142 -0
- package/dist/Orchestrator.d.ts.map +1 -0
- package/dist/Orchestrator.js +481 -0
- package/dist/api/GraphAPI.d.ts +87 -0
- package/dist/api/GraphAPI.d.ts.map +1 -0
- package/dist/api/GraphAPI.js +210 -0
- package/dist/api/GuaranteeAPI.d.ts +147 -0
- package/dist/api/GuaranteeAPI.d.ts.map +1 -0
- package/dist/api/GuaranteeAPI.js +288 -0
- package/dist/core/ASTWorker.d.ts +133 -0
- package/dist/core/ASTWorker.d.ts.map +1 -0
- package/dist/core/ASTWorker.js +352 -0
- package/dist/core/ASTWorkerPool.d.ts +85 -0
- package/dist/core/ASTWorkerPool.d.ts.map +1 -0
- package/dist/core/ASTWorkerPool.js +207 -0
- package/dist/core/AnalysisQueue.d.ts +104 -0
- package/dist/core/AnalysisQueue.d.ts.map +1 -0
- package/dist/core/AnalysisQueue.js +299 -0
- package/dist/core/AnalysisWorker.d.ts +14 -0
- package/dist/core/AnalysisWorker.d.ts.map +1 -0
- package/dist/core/AnalysisWorker.js +307 -0
- package/dist/core/GraphBackend.d.ts +156 -0
- package/dist/core/GraphBackend.d.ts.map +1 -0
- package/dist/core/GraphBackend.js +85 -0
- package/dist/core/GuaranteeManager.d.ts +230 -0
- package/dist/core/GuaranteeManager.d.ts.map +1 -0
- package/dist/core/GuaranteeManager.js +352 -0
- package/dist/core/ManifestStore.d.ts +71 -0
- package/dist/core/ManifestStore.d.ts.map +1 -0
- package/dist/core/ManifestStore.js +146 -0
- package/dist/core/NodeFactory.d.ts +160 -0
- package/dist/core/NodeFactory.d.ts.map +1 -0
- package/dist/core/NodeFactory.js +137 -0
- package/dist/core/NodeId.d.ts +88 -0
- package/dist/core/NodeId.d.ts.map +1 -0
- package/dist/core/NodeId.js +170 -0
- package/dist/core/ParallelAnalyzer.d.ts +120 -0
- package/dist/core/ParallelAnalyzer.d.ts.map +1 -0
- package/dist/core/ParallelAnalyzer.js +331 -0
- package/dist/core/PriorityQueue.d.ts +106 -0
- package/dist/core/PriorityQueue.d.ts.map +1 -0
- package/dist/core/PriorityQueue.js +168 -0
- package/dist/core/Profiler.d.ts +75 -0
- package/dist/core/Profiler.d.ts.map +1 -0
- package/dist/core/Profiler.js +149 -0
- package/dist/core/QueueWorker.d.ts +12 -0
- package/dist/core/QueueWorker.d.ts.map +1 -0
- package/dist/core/QueueWorker.js +567 -0
- package/dist/core/RFDBClient.d.ts +179 -0
- package/dist/core/RFDBClient.d.ts.map +1 -0
- package/dist/core/RFDBClient.js +429 -0
- package/dist/core/Task.d.ts +56 -0
- package/dist/core/Task.d.ts.map +1 -0
- package/dist/core/Task.js +85 -0
- package/dist/core/TaskTypes.d.ts +20 -0
- package/dist/core/TaskTypes.d.ts.map +1 -0
- package/dist/core/TaskTypes.js +10 -0
- package/dist/core/VersionManager.d.ts +166 -0
- package/dist/core/VersionManager.d.ts.map +1 -0
- package/dist/core/VersionManager.js +237 -0
- package/dist/core/WorkerPool.d.ts +82 -0
- package/dist/core/WorkerPool.d.ts.map +1 -0
- package/dist/core/WorkerPool.js +109 -0
- package/dist/core/nodes/CallSiteNode.d.ts +26 -0
- package/dist/core/nodes/CallSiteNode.d.ts.map +1 -0
- package/dist/core/nodes/CallSiteNode.js +44 -0
- package/dist/core/nodes/ClassNode.d.ts +25 -0
- package/dist/core/nodes/ClassNode.d.ts.map +1 -0
- package/dist/core/nodes/ClassNode.js +40 -0
- package/dist/core/nodes/ConstantNode.d.ts +24 -0
- package/dist/core/nodes/ConstantNode.d.ts.map +1 -0
- package/dist/core/nodes/ConstantNode.js +39 -0
- package/dist/core/nodes/DatabaseQueryNode.d.ts +22 -0
- package/dist/core/nodes/DatabaseQueryNode.d.ts.map +1 -0
- package/dist/core/nodes/DatabaseQueryNode.js +37 -0
- package/dist/core/nodes/EntrypointNode.d.ts +102 -0
- package/dist/core/nodes/EntrypointNode.d.ts.map +1 -0
- package/dist/core/nodes/EntrypointNode.js +119 -0
- package/dist/core/nodes/EventListenerNode.d.ts +25 -0
- package/dist/core/nodes/EventListenerNode.d.ts.map +1 -0
- package/dist/core/nodes/EventListenerNode.js +39 -0
- package/dist/core/nodes/ExportNode.d.ts +26 -0
- package/dist/core/nodes/ExportNode.d.ts.map +1 -0
- package/dist/core/nodes/ExportNode.js +40 -0
- package/dist/core/nodes/ExternalStdioNode.d.ts +17 -0
- package/dist/core/nodes/ExternalStdioNode.d.ts.map +1 -0
- package/dist/core/nodes/ExternalStdioNode.js +26 -0
- package/dist/core/nodes/FunctionNode.d.ts +27 -0
- package/dist/core/nodes/FunctionNode.d.ts.map +1 -0
- package/dist/core/nodes/FunctionNode.js +53 -0
- package/dist/core/nodes/GuaranteeNode.d.ts +76 -0
- package/dist/core/nodes/GuaranteeNode.d.ts.map +1 -0
- package/dist/core/nodes/GuaranteeNode.js +117 -0
- package/dist/core/nodes/HttpRequestNode.d.ts +24 -0
- package/dist/core/nodes/HttpRequestNode.d.ts.map +1 -0
- package/dist/core/nodes/HttpRequestNode.js +38 -0
- package/dist/core/nodes/ImportNode.d.ts +27 -0
- package/dist/core/nodes/ImportNode.d.ts.map +1 -0
- package/dist/core/nodes/ImportNode.js +43 -0
- package/dist/core/nodes/LiteralNode.d.ts +26 -0
- package/dist/core/nodes/LiteralNode.d.ts.map +1 -0
- package/dist/core/nodes/LiteralNode.js +40 -0
- package/dist/core/nodes/MethodCallNode.d.ts +29 -0
- package/dist/core/nodes/MethodCallNode.d.ts.map +1 -0
- package/dist/core/nodes/MethodCallNode.js +47 -0
- package/dist/core/nodes/MethodNode.d.ts +29 -0
- package/dist/core/nodes/MethodNode.d.ts.map +1 -0
- package/dist/core/nodes/MethodNode.js +44 -0
- package/dist/core/nodes/ModuleNode.d.ts +29 -0
- package/dist/core/nodes/ModuleNode.d.ts.map +1 -0
- package/dist/core/nodes/ModuleNode.js +49 -0
- package/dist/core/nodes/NodeKind.d.ts +91 -0
- package/dist/core/nodes/NodeKind.d.ts.map +1 -0
- package/dist/core/nodes/NodeKind.js +146 -0
- package/dist/core/nodes/ParameterNode.d.ts +26 -0
- package/dist/core/nodes/ParameterNode.d.ts.map +1 -0
- package/dist/core/nodes/ParameterNode.js +43 -0
- package/dist/core/nodes/ScopeNode.d.ts +32 -0
- package/dist/core/nodes/ScopeNode.d.ts.map +1 -0
- package/dist/core/nodes/ScopeNode.js +47 -0
- package/dist/core/nodes/ServiceNode.d.ts +44 -0
- package/dist/core/nodes/ServiceNode.d.ts.map +1 -0
- package/dist/core/nodes/ServiceNode.js +49 -0
- package/dist/core/nodes/VariableDeclarationNode.d.ts +22 -0
- package/dist/core/nodes/VariableDeclarationNode.d.ts.map +1 -0
- package/dist/core/nodes/VariableDeclarationNode.js +38 -0
- package/dist/core/nodes/index.d.ts +25 -0
- package/dist/core/nodes/index.d.ts.map +1 -0
- package/dist/core/nodes/index.js +30 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/plugins/Plugin.d.ts +44 -0
- package/dist/plugins/Plugin.d.ts.map +1 -0
- package/dist/plugins/Plugin.js +46 -0
- package/dist/plugins/analysis/DatabaseAnalyzer.d.ts +23 -0
- package/dist/plugins/analysis/DatabaseAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/DatabaseAnalyzer.js +260 -0
- package/dist/plugins/analysis/ExpressAnalyzer.d.ts +19 -0
- package/dist/plugins/analysis/ExpressAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/ExpressAnalyzer.js +306 -0
- package/dist/plugins/analysis/ExpressRouteAnalyzer.d.ts +17 -0
- package/dist/plugins/analysis/ExpressRouteAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/ExpressRouteAnalyzer.js +308 -0
- package/dist/plugins/analysis/FetchAnalyzer.d.ts +38 -0
- package/dist/plugins/analysis/FetchAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/FetchAnalyzer.js +344 -0
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts +65 -0
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts.map +1 -0
- package/dist/plugins/analysis/IncrementalAnalysisPlugin.js +472 -0
- package/dist/plugins/analysis/JSASTAnalyzer.d.ts +84 -0
- package/dist/plugins/analysis/JSASTAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/JSASTAnalyzer.js +1378 -0
- package/dist/plugins/analysis/ReactAnalyzer.d.ts +90 -0
- package/dist/plugins/analysis/ReactAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/ReactAnalyzer.js +1153 -0
- package/dist/plugins/analysis/RustAnalyzer.d.ts +13 -0
- package/dist/plugins/analysis/RustAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/RustAnalyzer.js +259 -0
- package/dist/plugins/analysis/SQLiteAnalyzer.d.ts +21 -0
- package/dist/plugins/analysis/SQLiteAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/SQLiteAnalyzer.js +317 -0
- package/dist/plugins/analysis/ServiceLayerAnalyzer.d.ts +35 -0
- package/dist/plugins/analysis/ServiceLayerAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/ServiceLayerAnalyzer.js +303 -0
- package/dist/plugins/analysis/SocketIOAnalyzer.d.ts +33 -0
- package/dist/plugins/analysis/SocketIOAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/SocketIOAnalyzer.js +283 -0
- package/dist/plugins/analysis/SystemDbAnalyzer.d.ts +27 -0
- package/dist/plugins/analysis/SystemDbAnalyzer.d.ts.map +1 -0
- package/dist/plugins/analysis/SystemDbAnalyzer.js +211 -0
- package/dist/plugins/analysis/ast/ConditionParser.d.ts +85 -0
- package/dist/plugins/analysis/ast/ConditionParser.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/ConditionParser.js +277 -0
- package/dist/plugins/analysis/ast/ExpressionEvaluator.d.ts +15 -0
- package/dist/plugins/analysis/ast/ExpressionEvaluator.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/ExpressionEvaluator.js +91 -0
- package/dist/plugins/analysis/ast/GraphBuilder.d.ts +77 -0
- package/dist/plugins/analysis/ast/GraphBuilder.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/GraphBuilder.js +1077 -0
- package/dist/plugins/analysis/ast/OxcAdapter.d.ts +41 -0
- package/dist/plugins/analysis/ast/OxcAdapter.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/OxcAdapter.js +40 -0
- package/dist/plugins/analysis/ast/types.d.ts +346 -0
- package/dist/plugins/analysis/ast/types.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/types.js +4 -0
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts +93 -0
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/ASTVisitor.js +24 -0
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts +77 -0
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js +377 -0
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts +27 -0
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/ClassVisitor.js +232 -0
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts +25 -0
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js +172 -0
- package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.d.ts +29 -0
- package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.js +180 -0
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts +14 -0
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.js +200 -0
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts +45 -0
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/VariableVisitor.js +150 -0
- package/dist/plugins/analysis/ast/visitors/index.d.ts +17 -0
- package/dist/plugins/analysis/ast/visitors/index.d.ts.map +1 -0
- package/dist/plugins/analysis/ast/visitors/index.js +13 -0
- package/dist/plugins/discovery/DiscoveryPlugin.d.ts +34 -0
- package/dist/plugins/discovery/DiscoveryPlugin.d.ts.map +1 -0
- package/dist/plugins/discovery/DiscoveryPlugin.js +26 -0
- package/dist/plugins/discovery/MonorepoServiceDiscovery.d.ts +26 -0
- package/dist/plugins/discovery/MonorepoServiceDiscovery.d.ts.map +1 -0
- package/dist/plugins/discovery/MonorepoServiceDiscovery.js +79 -0
- package/dist/plugins/discovery/SimpleProjectDiscovery.d.ts +14 -0
- package/dist/plugins/discovery/SimpleProjectDiscovery.d.ts.map +1 -0
- package/dist/plugins/discovery/SimpleProjectDiscovery.js +65 -0
- package/dist/plugins/discovery/ZonServiceDiscovery.d.ts +19 -0
- package/dist/plugins/discovery/ZonServiceDiscovery.d.ts.map +1 -0
- package/dist/plugins/discovery/ZonServiceDiscovery.js +204 -0
- package/dist/plugins/enrichment/AliasTracker.d.ts +40 -0
- package/dist/plugins/enrichment/AliasTracker.d.ts.map +1 -0
- package/dist/plugins/enrichment/AliasTracker.js +290 -0
- package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts +30 -0
- package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts.map +1 -0
- package/dist/plugins/enrichment/HTTPConnectionEnricher.js +135 -0
- package/dist/plugins/enrichment/ImportExportLinker.d.ts +30 -0
- package/dist/plugins/enrichment/ImportExportLinker.d.ts.map +1 -0
- package/dist/plugins/enrichment/ImportExportLinker.js +176 -0
- package/dist/plugins/enrichment/InstanceOfResolver.d.ts +21 -0
- package/dist/plugins/enrichment/InstanceOfResolver.d.ts.map +1 -0
- package/dist/plugins/enrichment/InstanceOfResolver.js +117 -0
- package/dist/plugins/enrichment/MethodCallResolver.d.ts +41 -0
- package/dist/plugins/enrichment/MethodCallResolver.d.ts.map +1 -0
- package/dist/plugins/enrichment/MethodCallResolver.js +252 -0
- package/dist/plugins/enrichment/MountPointResolver.d.ts +26 -0
- package/dist/plugins/enrichment/MountPointResolver.d.ts.map +1 -0
- package/dist/plugins/enrichment/MountPointResolver.js +189 -0
- package/dist/plugins/enrichment/PrefixEvaluator.d.ts +89 -0
- package/dist/plugins/enrichment/PrefixEvaluator.d.ts.map +1 -0
- package/dist/plugins/enrichment/PrefixEvaluator.js +415 -0
- package/dist/plugins/enrichment/RustFFIEnricher.d.ts +25 -0
- package/dist/plugins/enrichment/RustFFIEnricher.d.ts.map +1 -0
- package/dist/plugins/enrichment/RustFFIEnricher.js +170 -0
- package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts +114 -0
- package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts.map +1 -0
- package/dist/plugins/enrichment/ValueDomainAnalyzer.js +464 -0
- package/dist/plugins/indexing/IncrementalModuleIndexer.d.ts +27 -0
- package/dist/plugins/indexing/IncrementalModuleIndexer.d.ts.map +1 -0
- package/dist/plugins/indexing/IncrementalModuleIndexer.js +238 -0
- package/dist/plugins/indexing/JSModuleIndexer.d.ts +33 -0
- package/dist/plugins/indexing/JSModuleIndexer.d.ts.map +1 -0
- package/dist/plugins/indexing/JSModuleIndexer.js +299 -0
- package/dist/plugins/indexing/RustModuleIndexer.d.ts +28 -0
- package/dist/plugins/indexing/RustModuleIndexer.d.ts.map +1 -0
- package/dist/plugins/indexing/RustModuleIndexer.js +140 -0
- package/dist/plugins/indexing/ServiceDetector.d.ts +46 -0
- package/dist/plugins/indexing/ServiceDetector.d.ts.map +1 -0
- package/dist/plugins/indexing/ServiceDetector.js +164 -0
- package/dist/plugins/validation/CallResolverValidator.d.ts +23 -0
- package/dist/plugins/validation/CallResolverValidator.d.ts.map +1 -0
- package/dist/plugins/validation/CallResolverValidator.js +108 -0
- package/dist/plugins/validation/DataFlowValidator.d.ts +24 -0
- package/dist/plugins/validation/DataFlowValidator.d.ts.map +1 -0
- package/dist/plugins/validation/DataFlowValidator.js +148 -0
- package/dist/plugins/validation/EvalBanValidator.d.ts +25 -0
- package/dist/plugins/validation/EvalBanValidator.d.ts.map +1 -0
- package/dist/plugins/validation/EvalBanValidator.js +123 -0
- package/dist/plugins/validation/GraphConnectivityValidator.d.ts +11 -0
- package/dist/plugins/validation/GraphConnectivityValidator.d.ts.map +1 -0
- package/dist/plugins/validation/GraphConnectivityValidator.js +135 -0
- package/dist/plugins/validation/SQLInjectionValidator.d.ts +43 -0
- package/dist/plugins/validation/SQLInjectionValidator.d.ts.map +1 -0
- package/dist/plugins/validation/SQLInjectionValidator.js +251 -0
- package/dist/plugins/validation/ShadowingDetector.d.ts +26 -0
- package/dist/plugins/validation/ShadowingDetector.d.ts.map +1 -0
- package/dist/plugins/validation/ShadowingDetector.js +119 -0
- package/dist/plugins/validation/TypeScriptDeadCodeValidator.d.ts +21 -0
- package/dist/plugins/validation/TypeScriptDeadCodeValidator.d.ts.map +1 -0
- package/dist/plugins/validation/TypeScriptDeadCodeValidator.js +151 -0
- package/dist/plugins/vcs/GitPlugin.d.ts +84 -0
- package/dist/plugins/vcs/GitPlugin.d.ts.map +1 -0
- package/dist/plugins/vcs/GitPlugin.js +295 -0
- package/dist/plugins/vcs/VCSPlugin.d.ts +133 -0
- package/dist/plugins/vcs/VCSPlugin.d.ts.map +1 -0
- package/dist/plugins/vcs/VCSPlugin.js +82 -0
- package/dist/plugins/vcs/index.d.ts +10 -0
- package/dist/plugins/vcs/index.d.ts.map +1 -0
- package/dist/plugins/vcs/index.js +18 -0
- package/dist/storage/backends/RFDBServerBackend.d.ts +258 -0
- package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -0
- package/dist/storage/backends/RFDBServerBackend.js +565 -0
- package/dist/storage/backends/typeValidation.d.ts +47 -0
- package/dist/storage/backends/typeValidation.d.ts.map +1 -0
- package/dist/storage/backends/typeValidation.js +137 -0
- package/dist/validation/PathValidator.d.ts +81 -0
- package/dist/validation/PathValidator.d.ts.map +1 -0
- package/dist/validation/PathValidator.js +251 -0
- package/package.json +57 -0
- package/src/.rfguard/current-session.txt +1 -0
- package/src/Orchestrator.ts +673 -0
- package/src/api/GraphAPI.ts +305 -0
- package/src/api/GuaranteeAPI.ts +401 -0
- package/src/core/ASTWorker.ts +567 -0
- package/src/core/ASTWorkerPool.ts +299 -0
- package/src/core/AnalysisQueue.ts +447 -0
- package/src/core/AnalysisWorker.ts +410 -0
- package/src/core/GraphBackend.ts +265 -0
- package/src/core/GuaranteeManager.ts +581 -0
- package/src/core/ManifestStore.ts +196 -0
- package/src/core/NodeFactory.ts +274 -0
- package/src/core/NodeId.ts +257 -0
- package/src/core/ParallelAnalyzer.ts +476 -0
- package/src/core/PriorityQueue.ts +227 -0
- package/src/core/Profiler.ts +188 -0
- package/src/core/QueueWorker.ts +780 -0
- package/src/core/Task.ts +107 -0
- package/src/core/TaskTypes.ts +40 -0
- package/src/core/VersionManager.ts +404 -0
- package/src/core/WorkerPool.ts +180 -0
- package/src/core/nodes/CallSiteNode.ts +72 -0
- package/src/core/nodes/ClassNode.ts +69 -0
- package/src/core/nodes/ConstantNode.ts +63 -0
- package/src/core/nodes/DatabaseQueryNode.ts +60 -0
- package/src/core/nodes/EntrypointNode.ts +164 -0
- package/src/core/nodes/EventListenerNode.ts +64 -0
- package/src/core/nodes/ExportNode.ts +71 -0
- package/src/core/nodes/ExternalStdioNode.ts +36 -0
- package/src/core/nodes/FunctionNode.ts +78 -0
- package/src/core/nodes/GuaranteeNode.ts +162 -0
- package/src/core/nodes/HttpRequestNode.ts +63 -0
- package/src/core/nodes/ImportNode.ts +75 -0
- package/src/core/nodes/LiteralNode.ts +67 -0
- package/src/core/nodes/MethodCallNode.ts +79 -0
- package/src/core/nodes/MethodNode.ts +78 -0
- package/src/core/nodes/ModuleNode.ts +74 -0
- package/src/core/nodes/NodeKind.ts +171 -0
- package/src/core/nodes/ParameterNode.ts +73 -0
- package/src/core/nodes/ScopeNode.ts +80 -0
- package/src/core/nodes/ServiceNode.ts +86 -0
- package/src/core/nodes/VariableDeclarationNode.ts +60 -0
- package/src/core/nodes/index.ts +49 -0
- package/src/index.ts +93 -0
- package/src/plugins/Plugin.ts +74 -0
- package/src/plugins/analysis/DatabaseAnalyzer.ts +322 -0
- package/src/plugins/analysis/ExpressAnalyzer.ts +401 -0
- package/src/plugins/analysis/ExpressRouteAnalyzer.ts +414 -0
- package/src/plugins/analysis/FetchAnalyzer.ts +441 -0
- package/src/plugins/analysis/IncrementalAnalysisPlugin.ts +686 -0
- package/src/plugins/analysis/JSASTAnalyzer.ts +1680 -0
- package/src/plugins/analysis/ReactAnalyzer.ts +1368 -0
- package/src/plugins/analysis/RustAnalyzer.ts +438 -0
- package/src/plugins/analysis/SQLiteAnalyzer.ts +388 -0
- package/src/plugins/analysis/ServiceLayerAnalyzer.ts +429 -0
- package/src/plugins/analysis/SocketIOAnalyzer.ts +395 -0
- package/src/plugins/analysis/SystemDbAnalyzer.ts +284 -0
- package/src/plugins/analysis/ast/ConditionParser.ts +333 -0
- package/src/plugins/analysis/ast/ExpressionEvaluator.ts +117 -0
- package/src/plugins/analysis/ast/GraphBuilder.ts +1371 -0
- package/src/plugins/analysis/ast/OxcAdapter.ts +63 -0
- package/src/plugins/analysis/ast/types.ts +400 -0
- package/src/plugins/analysis/ast/visitors/ASTVisitor.ts +137 -0
- package/src/plugins/analysis/ast/visitors/CallExpressionVisitor.ts +528 -0
- package/src/plugins/analysis/ast/visitors/ClassVisitor.ts +339 -0
- package/src/plugins/analysis/ast/visitors/FunctionVisitor.ts +273 -0
- package/src/plugins/analysis/ast/visitors/ImportExportVisitor.ts +259 -0
- package/src/plugins/analysis/ast/visitors/TypeScriptVisitor.ts +235 -0
- package/src/plugins/analysis/ast/visitors/VariableVisitor.ts +268 -0
- package/src/plugins/analysis/ast/visitors/index.ts +36 -0
- package/src/plugins/discovery/DiscoveryPlugin.ts +50 -0
- package/src/plugins/discovery/MonorepoServiceDiscovery.ts +117 -0
- package/src/plugins/discovery/SimpleProjectDiscovery.ts +102 -0
- package/src/plugins/enrichment/AliasTracker.ts +399 -0
- package/src/plugins/enrichment/HTTPConnectionEnricher.ts +192 -0
- package/src/plugins/enrichment/ImportExportLinker.ts +221 -0
- package/src/plugins/enrichment/InstanceOfResolver.ts +165 -0
- package/src/plugins/enrichment/MethodCallResolver.ts +333 -0
- package/src/plugins/enrichment/MountPointResolver.ts +264 -0
- package/src/plugins/enrichment/PrefixEvaluator.ts +527 -0
- package/src/plugins/enrichment/RustFFIEnricher.ts +218 -0
- package/src/plugins/enrichment/ValueDomainAnalyzer.ts +682 -0
- package/src/plugins/indexing/IncrementalModuleIndexer.ts +287 -0
- package/src/plugins/indexing/JSModuleIndexer.ts +374 -0
- package/src/plugins/indexing/RustModuleIndexer.ts +160 -0
- package/src/plugins/indexing/ServiceDetector.ts +230 -0
- package/src/plugins/validation/CallResolverValidator.ts +170 -0
- package/src/plugins/validation/DataFlowValidator.ts +233 -0
- package/src/plugins/validation/EvalBanValidator.ts +175 -0
- package/src/plugins/validation/GraphConnectivityValidator.ts +201 -0
- package/src/plugins/validation/SQLInjectionValidator.ts +363 -0
- package/src/plugins/validation/ShadowingDetector.ts +173 -0
- package/src/plugins/validation/TypeScriptDeadCodeValidator.ts +203 -0
- package/src/plugins/vcs/GitPlugin.ts +344 -0
- package/src/plugins/vcs/VCSPlugin.ts +190 -0
- package/src/plugins/vcs/index.ts +32 -0
- package/src/storage/backends/RFDBServerBackend.ts +687 -0
- package/src/storage/backends/typeValidation.ts +151 -0
- package/src/validation/PathValidator.ts +342 -0
|
@@ -0,0 +1,1371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphBuilder - создание узлов и рёбер графа из собранных AST данных
|
|
3
|
+
* OPTIMIZED: Uses batched writes to reduce FFI overhead
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { dirname, resolve } from 'path';
|
|
7
|
+
import type { GraphBackend } from '@grafema/types';
|
|
8
|
+
import type {
|
|
9
|
+
ModuleNode,
|
|
10
|
+
FunctionInfo,
|
|
11
|
+
ParameterInfo,
|
|
12
|
+
ScopeInfo,
|
|
13
|
+
VariableDeclarationInfo,
|
|
14
|
+
CallSiteInfo,
|
|
15
|
+
MethodCallInfo,
|
|
16
|
+
EventListenerInfo,
|
|
17
|
+
ClassInstantiationInfo,
|
|
18
|
+
ClassDeclarationInfo,
|
|
19
|
+
MethodCallbackInfo,
|
|
20
|
+
CallArgumentInfo,
|
|
21
|
+
ImportInfo,
|
|
22
|
+
ExportInfo,
|
|
23
|
+
HttpRequestInfo,
|
|
24
|
+
LiteralInfo,
|
|
25
|
+
VariableAssignmentInfo,
|
|
26
|
+
InterfaceDeclarationInfo,
|
|
27
|
+
TypeAliasInfo,
|
|
28
|
+
EnumDeclarationInfo,
|
|
29
|
+
DecoratorInfo,
|
|
30
|
+
ASTCollections,
|
|
31
|
+
GraphNode,
|
|
32
|
+
GraphEdge,
|
|
33
|
+
BuildResult,
|
|
34
|
+
} from './types.js';
|
|
35
|
+
|
|
36
|
+
export class GraphBuilder {
|
|
37
|
+
// Track singleton nodes to avoid duplicates (net:stdio, net:request, etc.)
|
|
38
|
+
private _createdSingletons: Set<string> = new Set();
|
|
39
|
+
|
|
40
|
+
// Batching buffers for optimized writes
|
|
41
|
+
private _nodeBuffer: GraphNode[] = [];
|
|
42
|
+
private _edgeBuffer: GraphEdge[] = [];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Buffer a node for batched writing
|
|
46
|
+
*/
|
|
47
|
+
private _bufferNode(node: GraphNode): void {
|
|
48
|
+
this._nodeBuffer.push(node);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Buffer an edge for batched writing
|
|
53
|
+
*/
|
|
54
|
+
private _bufferEdge(edge: GraphEdge): void {
|
|
55
|
+
this._edgeBuffer.push(edge);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Flush all buffered nodes to the graph
|
|
60
|
+
*/
|
|
61
|
+
private async _flushNodes(graph: GraphBackend): Promise<number> {
|
|
62
|
+
if (this._nodeBuffer.length > 0) {
|
|
63
|
+
// Cast to unknown first since GraphNode is more permissive than NodeRecord
|
|
64
|
+
await graph.addNodes(this._nodeBuffer as unknown as import('@grafema/types').NodeRecord[]);
|
|
65
|
+
const count = this._nodeBuffer.length;
|
|
66
|
+
this._nodeBuffer = [];
|
|
67
|
+
return count;
|
|
68
|
+
}
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Flush all buffered edges to the graph
|
|
74
|
+
* Note: skip_validation=true because nodes were just flushed
|
|
75
|
+
*/
|
|
76
|
+
private async _flushEdges(graph: GraphBackend): Promise<number> {
|
|
77
|
+
if (this._edgeBuffer.length > 0) {
|
|
78
|
+
await (graph as GraphBackend & { addEdges(e: GraphEdge[], skip?: boolean): Promise<void> }).addEdges(this._edgeBuffer, true /* skip_validation */);
|
|
79
|
+
const count = this._edgeBuffer.length;
|
|
80
|
+
this._edgeBuffer = [];
|
|
81
|
+
return count;
|
|
82
|
+
}
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Создаёт ноды и рёбра в графе (BATCHED VERSION)
|
|
88
|
+
*/
|
|
89
|
+
async build(module: ModuleNode, graph: GraphBackend, projectPath: string, data: ASTCollections): Promise<BuildResult> {
|
|
90
|
+
const {
|
|
91
|
+
functions,
|
|
92
|
+
parameters = [],
|
|
93
|
+
scopes,
|
|
94
|
+
variableDeclarations,
|
|
95
|
+
callSites,
|
|
96
|
+
methodCalls = [],
|
|
97
|
+
eventListeners = [],
|
|
98
|
+
classInstantiations = [],
|
|
99
|
+
classDeclarations = [],
|
|
100
|
+
methodCallbacks = [],
|
|
101
|
+
callArguments = [],
|
|
102
|
+
imports = [],
|
|
103
|
+
exports = [],
|
|
104
|
+
httpRequests = [],
|
|
105
|
+
literals = [],
|
|
106
|
+
variableAssignments = [],
|
|
107
|
+
// TypeScript-specific collections
|
|
108
|
+
interfaces = [],
|
|
109
|
+
typeAliases = [],
|
|
110
|
+
enums = [],
|
|
111
|
+
decorators = []
|
|
112
|
+
} = data;
|
|
113
|
+
|
|
114
|
+
// Reset buffers for this build
|
|
115
|
+
this._nodeBuffer = [];
|
|
116
|
+
this._edgeBuffer = [];
|
|
117
|
+
|
|
118
|
+
// 1. Buffer all functions (without edges)
|
|
119
|
+
for (const func of functions) {
|
|
120
|
+
const { parentScopeId, ...funcData } = func;
|
|
121
|
+
this._bufferNode(funcData as GraphNode);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 2. Buffer all SCOPE (without edges)
|
|
125
|
+
for (const scope of scopes) {
|
|
126
|
+
const { parentFunctionId, parentScopeId, capturesFrom, modifies, ...scopeData } = scope;
|
|
127
|
+
this._bufferNode(scopeData as GraphNode);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 3. Buffer variables
|
|
131
|
+
for (const varDecl of variableDeclarations) {
|
|
132
|
+
const { parentScopeId, ...varData } = varDecl;
|
|
133
|
+
this._bufferNode(varData as GraphNode);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 3.5. Buffer PARAMETER nodes and HAS_PARAMETER edges
|
|
137
|
+
for (const param of parameters) {
|
|
138
|
+
const { functionId, ...paramData } = param;
|
|
139
|
+
// Keep parentFunctionId on the node for queries
|
|
140
|
+
this._bufferNode(paramData as GraphNode);
|
|
141
|
+
|
|
142
|
+
if (param.parentFunctionId) {
|
|
143
|
+
this._bufferEdge({
|
|
144
|
+
type: 'HAS_PARAMETER',
|
|
145
|
+
src: param.parentFunctionId,
|
|
146
|
+
dst: param.id,
|
|
147
|
+
index: param.index
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 4. Buffer CALL_SITE
|
|
153
|
+
for (const callSite of callSites) {
|
|
154
|
+
const { parentScopeId, targetFunctionName, ...callData } = callSite;
|
|
155
|
+
this._bufferNode(callData as GraphNode);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 5. Buffer edges for functions
|
|
159
|
+
this.bufferFunctionEdges(module, functions);
|
|
160
|
+
|
|
161
|
+
// 6. Buffer edges for SCOPE
|
|
162
|
+
this.bufferScopeEdges(scopes, variableDeclarations);
|
|
163
|
+
|
|
164
|
+
// 7. Buffer edges for variables
|
|
165
|
+
this.bufferVariableEdges(variableDeclarations);
|
|
166
|
+
|
|
167
|
+
// 8. Buffer edges for CALL_SITE
|
|
168
|
+
this.bufferCallSiteEdges(callSites, functions);
|
|
169
|
+
|
|
170
|
+
// 9. Buffer METHOD_CALL nodes and CONTAINS edges
|
|
171
|
+
this.bufferMethodCalls(methodCalls);
|
|
172
|
+
|
|
173
|
+
// 10. Buffer net:stdio and WRITES_TO edges for console.log/error
|
|
174
|
+
this.bufferStdioNodes(methodCalls);
|
|
175
|
+
|
|
176
|
+
// 11. Buffer CLASS nodes for class declarations and CONTAINS edges
|
|
177
|
+
this.bufferClassDeclarationNodes(classDeclarations);
|
|
178
|
+
|
|
179
|
+
// 12. Buffer CLASS nodes and INSTANCE_OF edges for NewExpression
|
|
180
|
+
this.bufferClassNodes(module, classInstantiations, classDeclarations);
|
|
181
|
+
|
|
182
|
+
// 13. Buffer PASSES_ARGUMENT edges (METHOD_CALL -> FUNCTION)
|
|
183
|
+
this.bufferCallbackEdges(methodCallbacks, functions);
|
|
184
|
+
|
|
185
|
+
// 14. Buffer IMPORT nodes
|
|
186
|
+
this.bufferImportNodes(module, imports);
|
|
187
|
+
|
|
188
|
+
// 15. Buffer EXPORT nodes
|
|
189
|
+
this.bufferExportNodes(module, exports);
|
|
190
|
+
|
|
191
|
+
// 16. Buffer EVENT_LISTENER nodes and HANDLED_BY edges
|
|
192
|
+
this.bufferEventListeners(eventListeners, functions);
|
|
193
|
+
|
|
194
|
+
// 17. Buffer HTTP requests
|
|
195
|
+
this.bufferHttpRequests(httpRequests, functions);
|
|
196
|
+
|
|
197
|
+
// 18. Buffer LITERAL nodes
|
|
198
|
+
this.bufferLiterals(literals);
|
|
199
|
+
|
|
200
|
+
// 19. Buffer ASSIGNED_FROM edges for data flow (some need to create EXPRESSION nodes)
|
|
201
|
+
this.bufferAssignmentEdges(variableAssignments, variableDeclarations, callSites, methodCalls, functions, classInstantiations, parameters);
|
|
202
|
+
|
|
203
|
+
// 20. Buffer PASSES_ARGUMENT edges (CALL -> argument)
|
|
204
|
+
this.bufferArgumentEdges(callArguments, variableDeclarations, functions, callSites, methodCalls);
|
|
205
|
+
|
|
206
|
+
// 21. Buffer INTERFACE nodes and EXTENDS edges
|
|
207
|
+
this.bufferInterfaceNodes(module, interfaces);
|
|
208
|
+
|
|
209
|
+
// 22. Buffer TYPE nodes
|
|
210
|
+
this.bufferTypeAliasNodes(module, typeAliases);
|
|
211
|
+
|
|
212
|
+
// 23. Buffer ENUM nodes
|
|
213
|
+
this.bufferEnumNodes(module, enums);
|
|
214
|
+
|
|
215
|
+
// 24. Buffer DECORATOR nodes and DECORATED_BY edges
|
|
216
|
+
this.bufferDecoratorNodes(decorators);
|
|
217
|
+
|
|
218
|
+
// 25. Buffer IMPLEMENTS edges (CLASS -> INTERFACE)
|
|
219
|
+
this.bufferImplementsEdges(classDeclarations, interfaces);
|
|
220
|
+
|
|
221
|
+
// FLUSH: Write all nodes first, then edges in single batch calls
|
|
222
|
+
const nodesCreated = await this._flushNodes(graph);
|
|
223
|
+
const edgesCreated = await this._flushEdges(graph);
|
|
224
|
+
|
|
225
|
+
// Handle async operations that need graph queries (IMPORTS_FROM edges)
|
|
226
|
+
const importExportEdges = await this.createImportExportEdges(module, imports, exports, graph, projectPath);
|
|
227
|
+
|
|
228
|
+
// Handle async operations for ASSIGNED_FROM with CLASS lookups
|
|
229
|
+
const classAssignmentEdges = await this.createClassAssignmentEdges(variableAssignments, graph);
|
|
230
|
+
|
|
231
|
+
return { nodes: nodesCreated, edges: edgesCreated + importExportEdges + classAssignmentEdges };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ============= BUFFERED METHODS (synchronous, no awaits) =============
|
|
235
|
+
|
|
236
|
+
private bufferFunctionEdges(module: ModuleNode, functions: FunctionInfo[]): void {
|
|
237
|
+
for (const func of functions) {
|
|
238
|
+
const { parentScopeId, ...funcData } = func;
|
|
239
|
+
|
|
240
|
+
// MODULE -> CONTAINS -> FUNCTION (для функций верхнего уровня)
|
|
241
|
+
// или SCOPE -> CONTAINS -> FUNCTION (для вложенных функций)
|
|
242
|
+
if (parentScopeId) {
|
|
243
|
+
this._bufferEdge({
|
|
244
|
+
type: 'CONTAINS',
|
|
245
|
+
src: parentScopeId,
|
|
246
|
+
dst: funcData.id
|
|
247
|
+
});
|
|
248
|
+
} else {
|
|
249
|
+
this._bufferEdge({
|
|
250
|
+
type: 'CONTAINS',
|
|
251
|
+
src: module.id,
|
|
252
|
+
dst: funcData.id
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private bufferScopeEdges(scopes: ScopeInfo[], variableDeclarations: VariableDeclarationInfo[]): void {
|
|
259
|
+
for (const scope of scopes) {
|
|
260
|
+
const { parentFunctionId, parentScopeId, capturesFrom, modifies, ...scopeData } = scope;
|
|
261
|
+
|
|
262
|
+
// FUNCTION -> HAS_SCOPE -> SCOPE (для function_body)
|
|
263
|
+
if (parentFunctionId) {
|
|
264
|
+
this._bufferEdge({
|
|
265
|
+
type: 'HAS_SCOPE',
|
|
266
|
+
src: parentFunctionId,
|
|
267
|
+
dst: scopeData.id
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// SCOPE -> CONTAINS -> SCOPE (для вложенных scope, типа if внутри function)
|
|
272
|
+
if (parentScopeId) {
|
|
273
|
+
this._bufferEdge({
|
|
274
|
+
type: 'CONTAINS',
|
|
275
|
+
src: parentScopeId,
|
|
276
|
+
dst: scopeData.id
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// CAPTURES - замыкания захватывают переменные из родительского scope
|
|
281
|
+
if (capturesFrom && scopeData.scopeType === 'closure') {
|
|
282
|
+
const parentVars = variableDeclarations.filter(v => v.parentScopeId === capturesFrom);
|
|
283
|
+
for (const parentVar of parentVars) {
|
|
284
|
+
this._bufferEdge({
|
|
285
|
+
type: 'CAPTURES',
|
|
286
|
+
src: scopeData.id,
|
|
287
|
+
dst: parentVar.id
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// MODIFIES - scope модифицирует переменные (count++)
|
|
293
|
+
if (modifies && modifies.length > 0) {
|
|
294
|
+
for (const mod of modifies) {
|
|
295
|
+
this._bufferEdge({
|
|
296
|
+
type: 'MODIFIES',
|
|
297
|
+
src: scopeData.id,
|
|
298
|
+
dst: mod.variableId
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private bufferVariableEdges(variableDeclarations: VariableDeclarationInfo[]): void {
|
|
306
|
+
for (const varDecl of variableDeclarations) {
|
|
307
|
+
const { parentScopeId, ...varData } = varDecl;
|
|
308
|
+
|
|
309
|
+
// SCOPE -> DECLARES -> VARIABLE
|
|
310
|
+
this._bufferEdge({
|
|
311
|
+
type: 'DECLARES',
|
|
312
|
+
src: parentScopeId as string,
|
|
313
|
+
dst: varData.id
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
private bufferCallSiteEdges(callSites: CallSiteInfo[], functions: FunctionInfo[]): void {
|
|
319
|
+
for (const callSite of callSites) {
|
|
320
|
+
const { parentScopeId, targetFunctionName, ...callData } = callSite;
|
|
321
|
+
|
|
322
|
+
// SCOPE -> CONTAINS -> CALL_SITE
|
|
323
|
+
this._bufferEdge({
|
|
324
|
+
type: 'CONTAINS',
|
|
325
|
+
src: parentScopeId as string,
|
|
326
|
+
dst: callData.id
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// CALL_SITE -> CALLS -> FUNCTION
|
|
330
|
+
const targetFunction = functions.find(f => f.name === targetFunctionName);
|
|
331
|
+
if (targetFunction) {
|
|
332
|
+
this._bufferEdge({
|
|
333
|
+
type: 'CALLS',
|
|
334
|
+
src: callData.id,
|
|
335
|
+
dst: targetFunction.id
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private bufferMethodCalls(methodCalls: MethodCallInfo[]): void {
|
|
342
|
+
for (const methodCall of methodCalls) {
|
|
343
|
+
const { parentScopeId, ...methodData } = methodCall;
|
|
344
|
+
|
|
345
|
+
// Buffer METHOD_CALL node
|
|
346
|
+
this._bufferNode(methodData as GraphNode);
|
|
347
|
+
|
|
348
|
+
// SCOPE -> CONTAINS -> METHOD_CALL
|
|
349
|
+
this._bufferEdge({
|
|
350
|
+
type: 'CONTAINS',
|
|
351
|
+
src: parentScopeId as string,
|
|
352
|
+
dst: methodData.id
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private bufferStdioNodes(methodCalls: MethodCallInfo[]): void {
|
|
358
|
+
const consoleIOMethods = methodCalls.filter(mc =>
|
|
359
|
+
(mc.object === 'console' && (mc.method === 'log' || mc.method === 'error'))
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
if (consoleIOMethods.length > 0) {
|
|
363
|
+
const stdioId = 'net:stdio#__stdio__';
|
|
364
|
+
// Buffer net:stdio node only once (singleton)
|
|
365
|
+
if (!this._createdSingletons.has(stdioId)) {
|
|
366
|
+
this._bufferNode({
|
|
367
|
+
id: stdioId,
|
|
368
|
+
type: 'net:stdio',
|
|
369
|
+
name: '__stdio__',
|
|
370
|
+
description: 'Standard input/output stream'
|
|
371
|
+
});
|
|
372
|
+
this._createdSingletons.add(stdioId);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Buffer WRITES_TO edges for console.log/error
|
|
376
|
+
for (const methodCall of consoleIOMethods) {
|
|
377
|
+
this._bufferEdge({
|
|
378
|
+
type: 'WRITES_TO',
|
|
379
|
+
src: methodCall.id,
|
|
380
|
+
dst: stdioId
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private bufferClassDeclarationNodes(classDeclarations: ClassDeclarationInfo[]): void {
|
|
387
|
+
for (const classDecl of classDeclarations) {
|
|
388
|
+
const { id, type, name, file, line, column, superClass, methods } = classDecl;
|
|
389
|
+
|
|
390
|
+
// Buffer CLASS node
|
|
391
|
+
this._bufferNode({
|
|
392
|
+
id,
|
|
393
|
+
type,
|
|
394
|
+
name,
|
|
395
|
+
file,
|
|
396
|
+
line,
|
|
397
|
+
column,
|
|
398
|
+
superClass
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Buffer CONTAINS edges: CLASS -> METHOD
|
|
402
|
+
for (const methodId of methods) {
|
|
403
|
+
this._bufferEdge({
|
|
404
|
+
type: 'CONTAINS',
|
|
405
|
+
src: id,
|
|
406
|
+
dst: methodId
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// If superClass, buffer DERIVES_FROM edge
|
|
411
|
+
if (superClass) {
|
|
412
|
+
const superClassId = `CLASS#${superClass}#${file}`;
|
|
413
|
+
this._bufferEdge({
|
|
414
|
+
type: 'DERIVES_FROM',
|
|
415
|
+
src: id,
|
|
416
|
+
dst: superClassId
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
private bufferClassNodes(module: ModuleNode, classInstantiations: ClassInstantiationInfo[], classDeclarations: ClassDeclarationInfo[]): void {
|
|
423
|
+
// Create lookup map: className → declaration ID
|
|
424
|
+
const declarationMap = new Map<string, string>();
|
|
425
|
+
for (const decl of classDeclarations) {
|
|
426
|
+
if (decl.file === module.file) {
|
|
427
|
+
declarationMap.set(decl.name, decl.id);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
for (const instantiation of classInstantiations) {
|
|
432
|
+
const { variableId, className, line } = instantiation;
|
|
433
|
+
|
|
434
|
+
let classId = declarationMap.get(className);
|
|
435
|
+
|
|
436
|
+
if (!classId) {
|
|
437
|
+
// External class - buffer CLASS node
|
|
438
|
+
classId = `${module.file}:CLASS:${className}:${line}`;
|
|
439
|
+
this._bufferNode({
|
|
440
|
+
id: classId,
|
|
441
|
+
type: 'CLASS',
|
|
442
|
+
name: className,
|
|
443
|
+
file: module.file,
|
|
444
|
+
line,
|
|
445
|
+
isInstantiationRef: true
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Buffer INSTANCE_OF edge
|
|
450
|
+
this._bufferEdge({
|
|
451
|
+
type: 'INSTANCE_OF',
|
|
452
|
+
src: variableId,
|
|
453
|
+
dst: classId
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private bufferCallbackEdges(methodCallbacks: MethodCallbackInfo[], functions: FunctionInfo[]): void {
|
|
459
|
+
for (const callback of methodCallbacks) {
|
|
460
|
+
const { methodCallId, callbackLine, callbackColumn } = callback;
|
|
461
|
+
|
|
462
|
+
const callbackFunction = functions.find(f =>
|
|
463
|
+
f.line === callbackLine && f.column === callbackColumn
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
if (callbackFunction) {
|
|
467
|
+
this._bufferEdge({
|
|
468
|
+
type: 'HAS_CALLBACK',
|
|
469
|
+
src: methodCallId,
|
|
470
|
+
dst: callbackFunction.id
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
private bufferImportNodes(module: ModuleNode, imports: ImportInfo[]): void {
|
|
477
|
+
for (const imp of imports) {
|
|
478
|
+
const { source, specifiers, line } = imp;
|
|
479
|
+
|
|
480
|
+
for (const spec of specifiers) {
|
|
481
|
+
const importType = spec.imported === 'default' ? 'default' :
|
|
482
|
+
spec.imported === '*' ? 'namespace' : 'named';
|
|
483
|
+
|
|
484
|
+
const importId = `${module.file}:IMPORT:${source}:${spec.local}:${line}`;
|
|
485
|
+
|
|
486
|
+
this._bufferNode({
|
|
487
|
+
id: importId,
|
|
488
|
+
type: 'IMPORT',
|
|
489
|
+
source: source,
|
|
490
|
+
importType: importType,
|
|
491
|
+
imported: spec.imported,
|
|
492
|
+
local: spec.local,
|
|
493
|
+
file: module.file,
|
|
494
|
+
line: line
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// MODULE -> CONTAINS -> IMPORT
|
|
498
|
+
this._bufferEdge({
|
|
499
|
+
type: 'CONTAINS',
|
|
500
|
+
src: module.id,
|
|
501
|
+
dst: importId
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
// Create EXTERNAL_MODULE node for external modules
|
|
505
|
+
const isRelative = source.startsWith('./') || source.startsWith('../');
|
|
506
|
+
if (!isRelative) {
|
|
507
|
+
const externalModuleId = `EXTERNAL_MODULE:${source}`;
|
|
508
|
+
|
|
509
|
+
this._bufferNode({
|
|
510
|
+
id: externalModuleId,
|
|
511
|
+
type: 'EXTERNAL_MODULE',
|
|
512
|
+
name: source,
|
|
513
|
+
file: module.file,
|
|
514
|
+
line: line
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
this._bufferEdge({
|
|
518
|
+
type: 'IMPORTS',
|
|
519
|
+
src: module.id,
|
|
520
|
+
dst: externalModuleId
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
private bufferExportNodes(module: ModuleNode, exports: ExportInfo[]): void {
|
|
528
|
+
for (const exp of exports) {
|
|
529
|
+
const { type, line, name, specifiers, source } = exp;
|
|
530
|
+
|
|
531
|
+
if (type === 'default') {
|
|
532
|
+
const exportId = `${module.file}:EXPORT:default:${line}`;
|
|
533
|
+
|
|
534
|
+
this._bufferNode({
|
|
535
|
+
id: exportId,
|
|
536
|
+
type: 'EXPORT',
|
|
537
|
+
exportType: 'default',
|
|
538
|
+
name: 'default',
|
|
539
|
+
file: module.file,
|
|
540
|
+
line: line
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
this._bufferEdge({
|
|
544
|
+
type: 'CONTAINS',
|
|
545
|
+
src: module.id,
|
|
546
|
+
dst: exportId
|
|
547
|
+
});
|
|
548
|
+
} else if (type === 'named') {
|
|
549
|
+
if (specifiers) {
|
|
550
|
+
for (const spec of specifiers) {
|
|
551
|
+
const exportId = `${module.file}:EXPORT:${spec.exported}:${line}`;
|
|
552
|
+
|
|
553
|
+
this._bufferNode({
|
|
554
|
+
id: exportId,
|
|
555
|
+
type: 'EXPORT',
|
|
556
|
+
exportType: 'named',
|
|
557
|
+
name: spec.exported,
|
|
558
|
+
local: spec.local,
|
|
559
|
+
file: module.file,
|
|
560
|
+
line: line,
|
|
561
|
+
source: source
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
this._bufferEdge({
|
|
565
|
+
type: 'CONTAINS',
|
|
566
|
+
src: module.id,
|
|
567
|
+
dst: exportId
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
} else if (name) {
|
|
571
|
+
const exportId = `${module.file}:EXPORT:${name}:${line}`;
|
|
572
|
+
|
|
573
|
+
this._bufferNode({
|
|
574
|
+
id: exportId,
|
|
575
|
+
type: 'EXPORT',
|
|
576
|
+
exportType: 'named',
|
|
577
|
+
name: name,
|
|
578
|
+
file: module.file,
|
|
579
|
+
line: line
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
this._bufferEdge({
|
|
583
|
+
type: 'CONTAINS',
|
|
584
|
+
src: module.id,
|
|
585
|
+
dst: exportId
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
} else if (type === 'all') {
|
|
589
|
+
const exportId = `${module.file}:EXPORT:*:${line}`;
|
|
590
|
+
|
|
591
|
+
this._bufferNode({
|
|
592
|
+
id: exportId,
|
|
593
|
+
type: 'EXPORT',
|
|
594
|
+
exportType: 'all',
|
|
595
|
+
name: '*',
|
|
596
|
+
file: module.file,
|
|
597
|
+
line: line,
|
|
598
|
+
source: source
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
this._bufferEdge({
|
|
602
|
+
type: 'CONTAINS',
|
|
603
|
+
src: module.id,
|
|
604
|
+
dst: exportId
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
private bufferEventListeners(eventListeners: EventListenerInfo[], functions: FunctionInfo[]): void {
|
|
611
|
+
for (const eventListener of eventListeners) {
|
|
612
|
+
const { parentScopeId, callbackArg, ...listenerData } = eventListener;
|
|
613
|
+
|
|
614
|
+
this._bufferNode(listenerData as GraphNode);
|
|
615
|
+
|
|
616
|
+
this._bufferEdge({
|
|
617
|
+
type: 'CONTAINS',
|
|
618
|
+
src: parentScopeId as string,
|
|
619
|
+
dst: listenerData.id
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
if (callbackArg && callbackArg.type === 'ArrowFunctionExpression') {
|
|
623
|
+
const callbackLine = (callbackArg.loc as { start: { line: number } }).start.line;
|
|
624
|
+
const callbackFunction = functions.find(f =>
|
|
625
|
+
f.line === callbackLine && f.arrowFunction
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
if (callbackFunction) {
|
|
629
|
+
this._bufferEdge({
|
|
630
|
+
type: 'HANDLED_BY',
|
|
631
|
+
src: listenerData.id,
|
|
632
|
+
dst: callbackFunction.id
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
private bufferHttpRequests(httpRequests: HttpRequestInfo[], functions: FunctionInfo[]): void {
|
|
640
|
+
if (httpRequests.length > 0) {
|
|
641
|
+
const networkId = 'net:request#__network__';
|
|
642
|
+
|
|
643
|
+
if (!this._createdSingletons.has(networkId)) {
|
|
644
|
+
this._bufferNode({
|
|
645
|
+
id: networkId,
|
|
646
|
+
type: 'net:request',
|
|
647
|
+
name: '__network__'
|
|
648
|
+
});
|
|
649
|
+
this._createdSingletons.add(networkId);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
for (const request of httpRequests) {
|
|
653
|
+
const { parentScopeId, ...requestData } = request;
|
|
654
|
+
|
|
655
|
+
this._bufferNode(requestData as GraphNode);
|
|
656
|
+
|
|
657
|
+
this._bufferEdge({
|
|
658
|
+
type: 'CALLS',
|
|
659
|
+
src: request.id,
|
|
660
|
+
dst: networkId
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
if (parentScopeId) {
|
|
664
|
+
const scopeParts = parentScopeId.split(':');
|
|
665
|
+
if (scopeParts.length >= 3 && scopeParts[1] === 'SCOPE') {
|
|
666
|
+
const functionName = scopeParts[2];
|
|
667
|
+
const file = scopeParts[0];
|
|
668
|
+
|
|
669
|
+
const parentFunction = functions.find(f =>
|
|
670
|
+
f.file === file && f.name === functionName
|
|
671
|
+
);
|
|
672
|
+
|
|
673
|
+
if (parentFunction) {
|
|
674
|
+
this._bufferEdge({
|
|
675
|
+
type: 'MAKES_REQUEST',
|
|
676
|
+
src: parentFunction.id,
|
|
677
|
+
dst: request.id
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
private bufferLiterals(literals: LiteralInfo[]): void {
|
|
687
|
+
for (const literal of literals) {
|
|
688
|
+
const { parentCallId, argIndex, ...literalData } = literal;
|
|
689
|
+
this._bufferNode(literalData as GraphNode);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
private bufferAssignmentEdges(
|
|
694
|
+
variableAssignments: VariableAssignmentInfo[],
|
|
695
|
+
variableDeclarations: VariableDeclarationInfo[],
|
|
696
|
+
callSites: CallSiteInfo[],
|
|
697
|
+
methodCalls: MethodCallInfo[],
|
|
698
|
+
functions: FunctionInfo[],
|
|
699
|
+
classInstantiations: ClassInstantiationInfo[],
|
|
700
|
+
parameters: ParameterInfo[]
|
|
701
|
+
): void {
|
|
702
|
+
for (const assignment of variableAssignments) {
|
|
703
|
+
const {
|
|
704
|
+
variableId,
|
|
705
|
+
sourceId,
|
|
706
|
+
sourceType,
|
|
707
|
+
sourceName,
|
|
708
|
+
sourceLine,
|
|
709
|
+
sourceColumn,
|
|
710
|
+
sourceFile,
|
|
711
|
+
functionName,
|
|
712
|
+
line,
|
|
713
|
+
className
|
|
714
|
+
} = assignment;
|
|
715
|
+
|
|
716
|
+
// Skip CLASS sourceType - handled async in createClassAssignmentEdges
|
|
717
|
+
if (sourceType === 'CLASS') {
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Direct LITERAL assignment
|
|
722
|
+
if (sourceId && sourceType !== 'EXPRESSION') {
|
|
723
|
+
this._bufferEdge({
|
|
724
|
+
type: 'ASSIGNED_FROM',
|
|
725
|
+
src: variableId,
|
|
726
|
+
dst: sourceId
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
// METHOD_CALL by coordinates
|
|
730
|
+
else if (sourceType === 'METHOD_CALL' && sourceLine && sourceColumn) {
|
|
731
|
+
const methodCall = methodCalls.find(mc =>
|
|
732
|
+
mc.line === sourceLine &&
|
|
733
|
+
mc.column === sourceColumn &&
|
|
734
|
+
mc.file === sourceFile
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
if (methodCall) {
|
|
738
|
+
this._bufferEdge({
|
|
739
|
+
type: 'ASSIGNED_FROM',
|
|
740
|
+
src: variableId,
|
|
741
|
+
dst: methodCall.id
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
// CALL_SITE by coordinates
|
|
746
|
+
else if (sourceType === 'CALL_SITE') {
|
|
747
|
+
const searchLine = sourceLine || assignment.callLine;
|
|
748
|
+
const searchColumn = sourceColumn || assignment.callColumn;
|
|
749
|
+
const searchName = assignment.callName;
|
|
750
|
+
|
|
751
|
+
if (searchLine && searchColumn) {
|
|
752
|
+
const callSite = callSites.find(cs =>
|
|
753
|
+
cs.line === searchLine &&
|
|
754
|
+
cs.column === searchColumn &&
|
|
755
|
+
(searchName ? cs.name === searchName : true)
|
|
756
|
+
);
|
|
757
|
+
|
|
758
|
+
if (callSite) {
|
|
759
|
+
this._bufferEdge({
|
|
760
|
+
type: 'ASSIGNED_FROM',
|
|
761
|
+
src: variableId,
|
|
762
|
+
dst: callSite.id
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
// VARIABLE by name
|
|
768
|
+
else if (sourceType === 'VARIABLE' && sourceName) {
|
|
769
|
+
const varIdParts = variableId.split('#');
|
|
770
|
+
const varFile = varIdParts.length >= 3 ? varIdParts[2] : null;
|
|
771
|
+
const sourceVariable = variableDeclarations.find(v =>
|
|
772
|
+
v.name === sourceName && v.file === varFile
|
|
773
|
+
);
|
|
774
|
+
|
|
775
|
+
if (sourceVariable) {
|
|
776
|
+
this._bufferEdge({
|
|
777
|
+
type: 'ASSIGNED_FROM',
|
|
778
|
+
src: variableId,
|
|
779
|
+
dst: sourceVariable.id
|
|
780
|
+
});
|
|
781
|
+
} else {
|
|
782
|
+
const sourceParam = parameters.find(p =>
|
|
783
|
+
p.name === sourceName && p.file === varFile
|
|
784
|
+
);
|
|
785
|
+
|
|
786
|
+
if (sourceParam) {
|
|
787
|
+
this._bufferEdge({
|
|
788
|
+
type: 'DERIVES_FROM',
|
|
789
|
+
src: variableId,
|
|
790
|
+
dst: sourceParam.id
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
// FUNCTION (arrow function assigned to variable)
|
|
796
|
+
else if (sourceType === 'FUNCTION' && functionName && line) {
|
|
797
|
+
const sourceFunction = functions.find(f =>
|
|
798
|
+
f.name === functionName && f.line === line
|
|
799
|
+
);
|
|
800
|
+
|
|
801
|
+
if (sourceFunction) {
|
|
802
|
+
this._bufferEdge({
|
|
803
|
+
type: 'ASSIGNED_FROM',
|
|
804
|
+
src: variableId,
|
|
805
|
+
dst: sourceFunction.id
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
// EXPRESSION node creation
|
|
810
|
+
else if (sourceType === 'EXPRESSION' && sourceId) {
|
|
811
|
+
const {
|
|
812
|
+
expressionType,
|
|
813
|
+
object,
|
|
814
|
+
property,
|
|
815
|
+
computed,
|
|
816
|
+
computedPropertyVar,
|
|
817
|
+
operator,
|
|
818
|
+
objectSourceName,
|
|
819
|
+
leftSourceName,
|
|
820
|
+
rightSourceName,
|
|
821
|
+
consequentSourceName,
|
|
822
|
+
alternateSourceName,
|
|
823
|
+
file: exprFile,
|
|
824
|
+
line: exprLine
|
|
825
|
+
} = assignment;
|
|
826
|
+
|
|
827
|
+
const expressionNode: GraphNode = {
|
|
828
|
+
id: sourceId,
|
|
829
|
+
type: 'EXPRESSION',
|
|
830
|
+
expressionType,
|
|
831
|
+
file: exprFile,
|
|
832
|
+
line: exprLine
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
if (expressionType === 'MemberExpression') {
|
|
836
|
+
expressionNode.object = object;
|
|
837
|
+
expressionNode.property = property;
|
|
838
|
+
expressionNode.computed = computed;
|
|
839
|
+
if (computedPropertyVar) {
|
|
840
|
+
expressionNode.computedPropertyVar = computedPropertyVar;
|
|
841
|
+
}
|
|
842
|
+
expressionNode.name = `${object}.${property}`;
|
|
843
|
+
} else if (expressionType === 'BinaryExpression' || expressionType === 'LogicalExpression') {
|
|
844
|
+
expressionNode.operator = operator;
|
|
845
|
+
expressionNode.name = `<${expressionType}>`;
|
|
846
|
+
} else if (expressionType === 'ConditionalExpression') {
|
|
847
|
+
expressionNode.name = '<ternary>';
|
|
848
|
+
} else if (expressionType === 'TemplateLiteral') {
|
|
849
|
+
expressionNode.name = '<template>';
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
this._bufferNode(expressionNode);
|
|
853
|
+
|
|
854
|
+
this._bufferEdge({
|
|
855
|
+
type: 'ASSIGNED_FROM',
|
|
856
|
+
src: variableId,
|
|
857
|
+
dst: sourceId
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
// Buffer DERIVES_FROM edges
|
|
861
|
+
const varParts = variableId.split('#');
|
|
862
|
+
const varFile = varParts.length >= 3 ? varParts[2] : null;
|
|
863
|
+
|
|
864
|
+
if (expressionType === 'MemberExpression' && objectSourceName) {
|
|
865
|
+
const objectVar = variableDeclarations.find(v =>
|
|
866
|
+
v.name === objectSourceName && (!varFile || v.file === varFile)
|
|
867
|
+
);
|
|
868
|
+
if (objectVar) {
|
|
869
|
+
this._bufferEdge({
|
|
870
|
+
type: 'DERIVES_FROM',
|
|
871
|
+
src: sourceId,
|
|
872
|
+
dst: objectVar.id
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
if ((expressionType === 'BinaryExpression' || expressionType === 'LogicalExpression')) {
|
|
878
|
+
if (leftSourceName) {
|
|
879
|
+
const leftVar = variableDeclarations.find(v =>
|
|
880
|
+
v.name === leftSourceName && (!varFile || v.file === varFile)
|
|
881
|
+
);
|
|
882
|
+
if (leftVar) {
|
|
883
|
+
this._bufferEdge({
|
|
884
|
+
type: 'DERIVES_FROM',
|
|
885
|
+
src: sourceId,
|
|
886
|
+
dst: leftVar.id
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
if (rightSourceName) {
|
|
891
|
+
const rightVar = variableDeclarations.find(v =>
|
|
892
|
+
v.name === rightSourceName && (!varFile || v.file === varFile)
|
|
893
|
+
);
|
|
894
|
+
if (rightVar) {
|
|
895
|
+
this._bufferEdge({
|
|
896
|
+
type: 'DERIVES_FROM',
|
|
897
|
+
src: sourceId,
|
|
898
|
+
dst: rightVar.id
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
if (expressionType === 'ConditionalExpression') {
|
|
905
|
+
if (consequentSourceName) {
|
|
906
|
+
const consequentVar = variableDeclarations.find(v =>
|
|
907
|
+
v.name === consequentSourceName && (!varFile || v.file === varFile)
|
|
908
|
+
);
|
|
909
|
+
if (consequentVar) {
|
|
910
|
+
this._bufferEdge({
|
|
911
|
+
type: 'DERIVES_FROM',
|
|
912
|
+
src: sourceId,
|
|
913
|
+
dst: consequentVar.id
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
if (alternateSourceName) {
|
|
918
|
+
const alternateVar = variableDeclarations.find(v =>
|
|
919
|
+
v.name === alternateSourceName && (!varFile || v.file === varFile)
|
|
920
|
+
);
|
|
921
|
+
if (alternateVar) {
|
|
922
|
+
this._bufferEdge({
|
|
923
|
+
type: 'DERIVES_FROM',
|
|
924
|
+
src: sourceId,
|
|
925
|
+
dst: alternateVar.id
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
if (expressionType === 'TemplateLiteral') {
|
|
932
|
+
const { expressionSourceNames } = assignment;
|
|
933
|
+
if (expressionSourceNames && expressionSourceNames.length > 0) {
|
|
934
|
+
for (const exprSourceName of expressionSourceNames) {
|
|
935
|
+
const sourceVar = variableDeclarations.find(v =>
|
|
936
|
+
v.name === exprSourceName && (!varFile || v.file === varFile)
|
|
937
|
+
);
|
|
938
|
+
if (sourceVar) {
|
|
939
|
+
this._bufferEdge({
|
|
940
|
+
type: 'DERIVES_FROM',
|
|
941
|
+
src: sourceId,
|
|
942
|
+
dst: sourceVar.id
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
// DERIVES_FROM_VARIABLE
|
|
950
|
+
else if (sourceType === 'DERIVES_FROM_VARIABLE' && sourceName) {
|
|
951
|
+
const expressionId = variableId;
|
|
952
|
+
const exprParts = expressionId.split('#');
|
|
953
|
+
const exprFile = exprParts.length >= 3 ? exprParts[2] : assignment.file;
|
|
954
|
+
|
|
955
|
+
const sourceVariable = variableDeclarations.find(v =>
|
|
956
|
+
v.name === sourceName && v.file === exprFile
|
|
957
|
+
);
|
|
958
|
+
|
|
959
|
+
if (sourceVariable) {
|
|
960
|
+
this._bufferEdge({
|
|
961
|
+
type: 'DERIVES_FROM',
|
|
962
|
+
src: expressionId,
|
|
963
|
+
dst: sourceVariable.id
|
|
964
|
+
});
|
|
965
|
+
} else {
|
|
966
|
+
const sourceParam = parameters.find(p =>
|
|
967
|
+
p.name === sourceName && p.file === exprFile
|
|
968
|
+
);
|
|
969
|
+
|
|
970
|
+
if (sourceParam) {
|
|
971
|
+
this._bufferEdge({
|
|
972
|
+
type: 'DERIVES_FROM',
|
|
973
|
+
src: expressionId,
|
|
974
|
+
dst: sourceParam.id
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
private bufferArgumentEdges(
|
|
983
|
+
callArguments: CallArgumentInfo[],
|
|
984
|
+
variableDeclarations: VariableDeclarationInfo[],
|
|
985
|
+
functions: FunctionInfo[],
|
|
986
|
+
callSites: CallSiteInfo[],
|
|
987
|
+
methodCalls: MethodCallInfo[]
|
|
988
|
+
): void {
|
|
989
|
+
for (const arg of callArguments) {
|
|
990
|
+
const {
|
|
991
|
+
callId,
|
|
992
|
+
argIndex,
|
|
993
|
+
targetType,
|
|
994
|
+
targetId,
|
|
995
|
+
targetName,
|
|
996
|
+
file,
|
|
997
|
+
isSpread,
|
|
998
|
+
functionLine,
|
|
999
|
+
functionColumn,
|
|
1000
|
+
nestedCallLine,
|
|
1001
|
+
nestedCallColumn
|
|
1002
|
+
} = arg;
|
|
1003
|
+
|
|
1004
|
+
let targetNodeId = targetId;
|
|
1005
|
+
|
|
1006
|
+
if (targetType === 'VARIABLE' && targetName) {
|
|
1007
|
+
const varNode = variableDeclarations.find(v =>
|
|
1008
|
+
v.name === targetName && v.file === file
|
|
1009
|
+
);
|
|
1010
|
+
if (varNode) {
|
|
1011
|
+
targetNodeId = varNode.id;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
else if (targetType === 'FUNCTION' && functionLine && functionColumn) {
|
|
1015
|
+
const funcNode = functions.find(f =>
|
|
1016
|
+
f.file === file && f.line === functionLine && f.column === functionColumn
|
|
1017
|
+
);
|
|
1018
|
+
if (funcNode) {
|
|
1019
|
+
targetNodeId = funcNode.id;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
else if (targetType === 'CALL' && nestedCallLine && nestedCallColumn) {
|
|
1023
|
+
const nestedCall = callSites.find(c =>
|
|
1024
|
+
c.file === file && c.line === nestedCallLine && c.column === nestedCallColumn
|
|
1025
|
+
) || methodCalls.find(c =>
|
|
1026
|
+
c.file === file && c.line === nestedCallLine && c.column === nestedCallColumn
|
|
1027
|
+
);
|
|
1028
|
+
if (nestedCall) {
|
|
1029
|
+
targetNodeId = nestedCall.id;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
if (targetNodeId) {
|
|
1034
|
+
const edgeData: GraphEdge = {
|
|
1035
|
+
type: 'PASSES_ARGUMENT',
|
|
1036
|
+
src: callId,
|
|
1037
|
+
dst: targetNodeId,
|
|
1038
|
+
metadata: { argIndex }
|
|
1039
|
+
};
|
|
1040
|
+
|
|
1041
|
+
if (isSpread) {
|
|
1042
|
+
edgeData.metadata = { ...edgeData.metadata, isSpread: true };
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
this._bufferEdge(edgeData);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// ============= TypeScript-specific buffer methods =============
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* Buffer INTERFACE nodes and EXTENDS edges
|
|
1054
|
+
*/
|
|
1055
|
+
private bufferInterfaceNodes(module: ModuleNode, interfaces: InterfaceDeclarationInfo[]): void {
|
|
1056
|
+
for (const iface of interfaces) {
|
|
1057
|
+
// Buffer INTERFACE node
|
|
1058
|
+
this._bufferNode({
|
|
1059
|
+
id: iface.id,
|
|
1060
|
+
type: 'INTERFACE',
|
|
1061
|
+
name: iface.name,
|
|
1062
|
+
file: iface.file,
|
|
1063
|
+
line: iface.line,
|
|
1064
|
+
column: iface.column,
|
|
1065
|
+
properties: iface.properties,
|
|
1066
|
+
extends: iface.extends
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
// MODULE -> CONTAINS -> INTERFACE
|
|
1070
|
+
this._bufferEdge({
|
|
1071
|
+
type: 'CONTAINS',
|
|
1072
|
+
src: module.id,
|
|
1073
|
+
dst: iface.id
|
|
1074
|
+
});
|
|
1075
|
+
|
|
1076
|
+
// INTERFACE -> EXTENDS -> INTERFACE (for each parent interface)
|
|
1077
|
+
if (iface.extends && iface.extends.length > 0) {
|
|
1078
|
+
for (const parentName of iface.extends) {
|
|
1079
|
+
// Try to find the parent interface in the same file
|
|
1080
|
+
const parentInterface = interfaces.find(i => i.name === parentName);
|
|
1081
|
+
if (parentInterface) {
|
|
1082
|
+
this._bufferEdge({
|
|
1083
|
+
type: 'EXTENDS',
|
|
1084
|
+
src: iface.id,
|
|
1085
|
+
dst: parentInterface.id
|
|
1086
|
+
});
|
|
1087
|
+
} else {
|
|
1088
|
+
// External interface - create a reference node
|
|
1089
|
+
const externalId = `INTERFACE#${parentName}#${iface.file}#external`;
|
|
1090
|
+
this._bufferNode({
|
|
1091
|
+
id: externalId,
|
|
1092
|
+
type: 'INTERFACE',
|
|
1093
|
+
name: parentName,
|
|
1094
|
+
file: iface.file,
|
|
1095
|
+
line: iface.line,
|
|
1096
|
+
isExternal: true
|
|
1097
|
+
});
|
|
1098
|
+
this._bufferEdge({
|
|
1099
|
+
type: 'EXTENDS',
|
|
1100
|
+
src: iface.id,
|
|
1101
|
+
dst: externalId
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* Buffer TYPE alias nodes
|
|
1111
|
+
*/
|
|
1112
|
+
private bufferTypeAliasNodes(module: ModuleNode, typeAliases: TypeAliasInfo[]): void {
|
|
1113
|
+
for (const typeAlias of typeAliases) {
|
|
1114
|
+
// Buffer TYPE node
|
|
1115
|
+
this._bufferNode({
|
|
1116
|
+
id: typeAlias.id,
|
|
1117
|
+
type: 'TYPE',
|
|
1118
|
+
name: typeAlias.name,
|
|
1119
|
+
file: typeAlias.file,
|
|
1120
|
+
line: typeAlias.line,
|
|
1121
|
+
column: typeAlias.column,
|
|
1122
|
+
aliasOf: typeAlias.aliasOf
|
|
1123
|
+
});
|
|
1124
|
+
|
|
1125
|
+
// MODULE -> CONTAINS -> TYPE
|
|
1126
|
+
this._bufferEdge({
|
|
1127
|
+
type: 'CONTAINS',
|
|
1128
|
+
src: module.id,
|
|
1129
|
+
dst: typeAlias.id
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
/**
|
|
1135
|
+
* Buffer ENUM nodes
|
|
1136
|
+
*/
|
|
1137
|
+
private bufferEnumNodes(module: ModuleNode, enums: EnumDeclarationInfo[]): void {
|
|
1138
|
+
for (const enumDecl of enums) {
|
|
1139
|
+
// Buffer ENUM node
|
|
1140
|
+
this._bufferNode({
|
|
1141
|
+
id: enumDecl.id,
|
|
1142
|
+
type: 'ENUM',
|
|
1143
|
+
name: enumDecl.name,
|
|
1144
|
+
file: enumDecl.file,
|
|
1145
|
+
line: enumDecl.line,
|
|
1146
|
+
column: enumDecl.column,
|
|
1147
|
+
isConst: enumDecl.isConst,
|
|
1148
|
+
members: enumDecl.members
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
// MODULE -> CONTAINS -> ENUM
|
|
1152
|
+
this._bufferEdge({
|
|
1153
|
+
type: 'CONTAINS',
|
|
1154
|
+
src: module.id,
|
|
1155
|
+
dst: enumDecl.id
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
/**
|
|
1161
|
+
* Buffer DECORATOR nodes and DECORATED_BY edges
|
|
1162
|
+
*/
|
|
1163
|
+
private bufferDecoratorNodes(decorators: DecoratorInfo[]): void {
|
|
1164
|
+
for (const decorator of decorators) {
|
|
1165
|
+
// Buffer DECORATOR node
|
|
1166
|
+
this._bufferNode({
|
|
1167
|
+
id: decorator.id,
|
|
1168
|
+
type: 'DECORATOR',
|
|
1169
|
+
name: decorator.name,
|
|
1170
|
+
file: decorator.file,
|
|
1171
|
+
line: decorator.line,
|
|
1172
|
+
column: decorator.column,
|
|
1173
|
+
arguments: decorator.arguments,
|
|
1174
|
+
targetType: decorator.targetType
|
|
1175
|
+
});
|
|
1176
|
+
|
|
1177
|
+
// TARGET -> DECORATED_BY -> DECORATOR
|
|
1178
|
+
this._bufferEdge({
|
|
1179
|
+
type: 'DECORATED_BY',
|
|
1180
|
+
src: decorator.targetId,
|
|
1181
|
+
dst: decorator.id
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* Buffer IMPLEMENTS edges (CLASS -> INTERFACE)
|
|
1188
|
+
*/
|
|
1189
|
+
private bufferImplementsEdges(classDeclarations: ClassDeclarationInfo[], interfaces: InterfaceDeclarationInfo[]): void {
|
|
1190
|
+
for (const classDecl of classDeclarations) {
|
|
1191
|
+
if (classDecl.implements && classDecl.implements.length > 0) {
|
|
1192
|
+
for (const ifaceName of classDecl.implements) {
|
|
1193
|
+
// Try to find the interface in the same file
|
|
1194
|
+
const iface = interfaces.find(i => i.name === ifaceName);
|
|
1195
|
+
if (iface) {
|
|
1196
|
+
this._bufferEdge({
|
|
1197
|
+
type: 'IMPLEMENTS',
|
|
1198
|
+
src: classDecl.id,
|
|
1199
|
+
dst: iface.id
|
|
1200
|
+
});
|
|
1201
|
+
} else {
|
|
1202
|
+
// External interface - create a reference node
|
|
1203
|
+
const externalId = `INTERFACE#${ifaceName}#${classDecl.file}#external`;
|
|
1204
|
+
this._bufferNode({
|
|
1205
|
+
id: externalId,
|
|
1206
|
+
type: 'INTERFACE',
|
|
1207
|
+
name: ifaceName,
|
|
1208
|
+
file: classDecl.file,
|
|
1209
|
+
line: classDecl.line,
|
|
1210
|
+
isExternal: true
|
|
1211
|
+
});
|
|
1212
|
+
this._bufferEdge({
|
|
1213
|
+
type: 'IMPLEMENTS',
|
|
1214
|
+
src: classDecl.id,
|
|
1215
|
+
dst: externalId
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
/**
|
|
1224
|
+
* Handle CLASS ASSIGNED_FROM edges asynchronously (needs graph queries)
|
|
1225
|
+
*/
|
|
1226
|
+
private async createClassAssignmentEdges(variableAssignments: VariableAssignmentInfo[], graph: GraphBackend): Promise<number> {
|
|
1227
|
+
let edgesCreated = 0;
|
|
1228
|
+
|
|
1229
|
+
for (const assignment of variableAssignments) {
|
|
1230
|
+
const { variableId, sourceType, className } = assignment;
|
|
1231
|
+
|
|
1232
|
+
if (sourceType === 'CLASS' && className) {
|
|
1233
|
+
const parts = variableId.split('#');
|
|
1234
|
+
const file = parts.length >= 3 ? parts[2] : null;
|
|
1235
|
+
|
|
1236
|
+
let classNode: { id: string; name: string; file?: string } | null = null;
|
|
1237
|
+
for await (const node of graph.queryNodes({ type: 'CLASS' })) {
|
|
1238
|
+
if (node.name === className && (!file || node.file === file)) {
|
|
1239
|
+
classNode = node as { id: string; name: string; file?: string };
|
|
1240
|
+
break;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
if (classNode) {
|
|
1245
|
+
await graph.addEdge({
|
|
1246
|
+
type: 'ASSIGNED_FROM',
|
|
1247
|
+
src: variableId,
|
|
1248
|
+
dst: classNode.id
|
|
1249
|
+
});
|
|
1250
|
+
edgesCreated++;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
return edgesCreated;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/**
|
|
1259
|
+
* Create IMPORTS_FROM edges linking imports to their target exports
|
|
1260
|
+
*/
|
|
1261
|
+
private async createImportExportEdges(
|
|
1262
|
+
module: ModuleNode,
|
|
1263
|
+
imports: ImportInfo[],
|
|
1264
|
+
_exports: ExportInfo[],
|
|
1265
|
+
graph: GraphBackend,
|
|
1266
|
+
_projectPath: string
|
|
1267
|
+
): Promise<number> {
|
|
1268
|
+
let edgesCreated = 0;
|
|
1269
|
+
|
|
1270
|
+
for (const imp of imports) {
|
|
1271
|
+
const { source, specifiers, line } = imp;
|
|
1272
|
+
|
|
1273
|
+
// Только для относительных импортов
|
|
1274
|
+
const isRelative = source.startsWith('./') || source.startsWith('../');
|
|
1275
|
+
if (!isRelative) {
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// Резолвим целевой модуль
|
|
1280
|
+
const currentDir = dirname(module.file);
|
|
1281
|
+
let targetPath = resolve(currentDir, source);
|
|
1282
|
+
|
|
1283
|
+
// Пытаемся найти файл с расширениями .js, .ts, .jsx, .tsx
|
|
1284
|
+
const extensions = ['', '.js', '.ts', '.jsx', '.tsx', '/index.js', '/index.ts'];
|
|
1285
|
+
let targetModule: { id: string; file: string } | null = null;
|
|
1286
|
+
|
|
1287
|
+
// Ищем MODULE ноду по file атрибуту (не по ID, т.к. формат ID изменился)
|
|
1288
|
+
for (const ext of extensions) {
|
|
1289
|
+
const testPath = targetPath + ext;
|
|
1290
|
+
|
|
1291
|
+
// Ищем MODULE с этим file path
|
|
1292
|
+
for await (const node of graph.queryNodes({ type: 'MODULE' })) {
|
|
1293
|
+
if (node.file === testPath) {
|
|
1294
|
+
targetModule = node as { id: string; file: string };
|
|
1295
|
+
targetPath = testPath;
|
|
1296
|
+
break;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
if (targetModule) break;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
if (!targetModule) {
|
|
1303
|
+
// Целевой модуль не найден в графе
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
// Создаём IMPORTS edge от MODULE к MODULE (для совместимости с тестами)
|
|
1308
|
+
await graph.addEdge({
|
|
1309
|
+
type: 'IMPORTS',
|
|
1310
|
+
src: module.id,
|
|
1311
|
+
dst: targetModule.id
|
|
1312
|
+
});
|
|
1313
|
+
edgesCreated++;
|
|
1314
|
+
|
|
1315
|
+
// Для каждого импортированного идентификатора создаём ребро к соответствующему EXPORT
|
|
1316
|
+
for (const spec of specifiers) {
|
|
1317
|
+
const importId = `${module.file}:IMPORT:${source}:${spec.local}:${line}`;
|
|
1318
|
+
const importType = spec.imported === 'default' ? 'default' :
|
|
1319
|
+
spec.imported === '*' ? 'namespace' : 'named';
|
|
1320
|
+
|
|
1321
|
+
if (importType === 'namespace') {
|
|
1322
|
+
// import * as foo - связываем со всем модулем
|
|
1323
|
+
await graph.addEdge({
|
|
1324
|
+
type: 'IMPORTS_FROM',
|
|
1325
|
+
src: importId,
|
|
1326
|
+
dst: targetModule.id
|
|
1327
|
+
});
|
|
1328
|
+
edgesCreated++;
|
|
1329
|
+
} else if (importType === 'default') {
|
|
1330
|
+
// Находим EXPORT default в целевом модуле
|
|
1331
|
+
const targetExports: { id: string }[] = [];
|
|
1332
|
+
for await (const node of graph.queryNodes({ type: 'EXPORT' })) {
|
|
1333
|
+
const exportNode = node as { id: string; file?: string; exportType?: string };
|
|
1334
|
+
if (exportNode.file === targetPath && exportNode.exportType === 'default') {
|
|
1335
|
+
targetExports.push(exportNode);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
if (targetExports.length > 0) {
|
|
1340
|
+
await graph.addEdge({
|
|
1341
|
+
type: 'IMPORTS_FROM',
|
|
1342
|
+
src: importId,
|
|
1343
|
+
dst: targetExports[0].id
|
|
1344
|
+
});
|
|
1345
|
+
edgesCreated++;
|
|
1346
|
+
}
|
|
1347
|
+
} else {
|
|
1348
|
+
// Named import - находим соответствующий named export
|
|
1349
|
+
const targetExports: { id: string }[] = [];
|
|
1350
|
+
for await (const node of graph.queryNodes({ type: 'EXPORT' })) {
|
|
1351
|
+
const exportNode = node as { id: string; file?: string; exportType?: string; name?: string };
|
|
1352
|
+
if (exportNode.file === targetPath && exportNode.exportType === 'named' && exportNode.name === spec.imported) {
|
|
1353
|
+
targetExports.push(exportNode);
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
if (targetExports.length > 0) {
|
|
1358
|
+
await graph.addEdge({
|
|
1359
|
+
type: 'IMPORTS_FROM',
|
|
1360
|
+
src: importId,
|
|
1361
|
+
dst: targetExports[0].id
|
|
1362
|
+
});
|
|
1363
|
+
edgesCreated++;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
return edgesCreated;
|
|
1370
|
+
}
|
|
1371
|
+
}
|