@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.
Files changed (402) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +76 -0
  3. package/dist/Orchestrator.d.ts +142 -0
  4. package/dist/Orchestrator.d.ts.map +1 -0
  5. package/dist/Orchestrator.js +481 -0
  6. package/dist/api/GraphAPI.d.ts +87 -0
  7. package/dist/api/GraphAPI.d.ts.map +1 -0
  8. package/dist/api/GraphAPI.js +210 -0
  9. package/dist/api/GuaranteeAPI.d.ts +147 -0
  10. package/dist/api/GuaranteeAPI.d.ts.map +1 -0
  11. package/dist/api/GuaranteeAPI.js +288 -0
  12. package/dist/core/ASTWorker.d.ts +133 -0
  13. package/dist/core/ASTWorker.d.ts.map +1 -0
  14. package/dist/core/ASTWorker.js +352 -0
  15. package/dist/core/ASTWorkerPool.d.ts +85 -0
  16. package/dist/core/ASTWorkerPool.d.ts.map +1 -0
  17. package/dist/core/ASTWorkerPool.js +207 -0
  18. package/dist/core/AnalysisQueue.d.ts +104 -0
  19. package/dist/core/AnalysisQueue.d.ts.map +1 -0
  20. package/dist/core/AnalysisQueue.js +299 -0
  21. package/dist/core/AnalysisWorker.d.ts +14 -0
  22. package/dist/core/AnalysisWorker.d.ts.map +1 -0
  23. package/dist/core/AnalysisWorker.js +307 -0
  24. package/dist/core/GraphBackend.d.ts +156 -0
  25. package/dist/core/GraphBackend.d.ts.map +1 -0
  26. package/dist/core/GraphBackend.js +85 -0
  27. package/dist/core/GuaranteeManager.d.ts +230 -0
  28. package/dist/core/GuaranteeManager.d.ts.map +1 -0
  29. package/dist/core/GuaranteeManager.js +352 -0
  30. package/dist/core/ManifestStore.d.ts +71 -0
  31. package/dist/core/ManifestStore.d.ts.map +1 -0
  32. package/dist/core/ManifestStore.js +146 -0
  33. package/dist/core/NodeFactory.d.ts +160 -0
  34. package/dist/core/NodeFactory.d.ts.map +1 -0
  35. package/dist/core/NodeFactory.js +137 -0
  36. package/dist/core/NodeId.d.ts +88 -0
  37. package/dist/core/NodeId.d.ts.map +1 -0
  38. package/dist/core/NodeId.js +170 -0
  39. package/dist/core/ParallelAnalyzer.d.ts +120 -0
  40. package/dist/core/ParallelAnalyzer.d.ts.map +1 -0
  41. package/dist/core/ParallelAnalyzer.js +331 -0
  42. package/dist/core/PriorityQueue.d.ts +106 -0
  43. package/dist/core/PriorityQueue.d.ts.map +1 -0
  44. package/dist/core/PriorityQueue.js +168 -0
  45. package/dist/core/Profiler.d.ts +75 -0
  46. package/dist/core/Profiler.d.ts.map +1 -0
  47. package/dist/core/Profiler.js +149 -0
  48. package/dist/core/QueueWorker.d.ts +12 -0
  49. package/dist/core/QueueWorker.d.ts.map +1 -0
  50. package/dist/core/QueueWorker.js +567 -0
  51. package/dist/core/RFDBClient.d.ts +179 -0
  52. package/dist/core/RFDBClient.d.ts.map +1 -0
  53. package/dist/core/RFDBClient.js +429 -0
  54. package/dist/core/Task.d.ts +56 -0
  55. package/dist/core/Task.d.ts.map +1 -0
  56. package/dist/core/Task.js +85 -0
  57. package/dist/core/TaskTypes.d.ts +20 -0
  58. package/dist/core/TaskTypes.d.ts.map +1 -0
  59. package/dist/core/TaskTypes.js +10 -0
  60. package/dist/core/VersionManager.d.ts +166 -0
  61. package/dist/core/VersionManager.d.ts.map +1 -0
  62. package/dist/core/VersionManager.js +237 -0
  63. package/dist/core/WorkerPool.d.ts +82 -0
  64. package/dist/core/WorkerPool.d.ts.map +1 -0
  65. package/dist/core/WorkerPool.js +109 -0
  66. package/dist/core/nodes/CallSiteNode.d.ts +26 -0
  67. package/dist/core/nodes/CallSiteNode.d.ts.map +1 -0
  68. package/dist/core/nodes/CallSiteNode.js +44 -0
  69. package/dist/core/nodes/ClassNode.d.ts +25 -0
  70. package/dist/core/nodes/ClassNode.d.ts.map +1 -0
  71. package/dist/core/nodes/ClassNode.js +40 -0
  72. package/dist/core/nodes/ConstantNode.d.ts +24 -0
  73. package/dist/core/nodes/ConstantNode.d.ts.map +1 -0
  74. package/dist/core/nodes/ConstantNode.js +39 -0
  75. package/dist/core/nodes/DatabaseQueryNode.d.ts +22 -0
  76. package/dist/core/nodes/DatabaseQueryNode.d.ts.map +1 -0
  77. package/dist/core/nodes/DatabaseQueryNode.js +37 -0
  78. package/dist/core/nodes/EntrypointNode.d.ts +102 -0
  79. package/dist/core/nodes/EntrypointNode.d.ts.map +1 -0
  80. package/dist/core/nodes/EntrypointNode.js +119 -0
  81. package/dist/core/nodes/EventListenerNode.d.ts +25 -0
  82. package/dist/core/nodes/EventListenerNode.d.ts.map +1 -0
  83. package/dist/core/nodes/EventListenerNode.js +39 -0
  84. package/dist/core/nodes/ExportNode.d.ts +26 -0
  85. package/dist/core/nodes/ExportNode.d.ts.map +1 -0
  86. package/dist/core/nodes/ExportNode.js +40 -0
  87. package/dist/core/nodes/ExternalStdioNode.d.ts +17 -0
  88. package/dist/core/nodes/ExternalStdioNode.d.ts.map +1 -0
  89. package/dist/core/nodes/ExternalStdioNode.js +26 -0
  90. package/dist/core/nodes/FunctionNode.d.ts +27 -0
  91. package/dist/core/nodes/FunctionNode.d.ts.map +1 -0
  92. package/dist/core/nodes/FunctionNode.js +53 -0
  93. package/dist/core/nodes/GuaranteeNode.d.ts +76 -0
  94. package/dist/core/nodes/GuaranteeNode.d.ts.map +1 -0
  95. package/dist/core/nodes/GuaranteeNode.js +117 -0
  96. package/dist/core/nodes/HttpRequestNode.d.ts +24 -0
  97. package/dist/core/nodes/HttpRequestNode.d.ts.map +1 -0
  98. package/dist/core/nodes/HttpRequestNode.js +38 -0
  99. package/dist/core/nodes/ImportNode.d.ts +27 -0
  100. package/dist/core/nodes/ImportNode.d.ts.map +1 -0
  101. package/dist/core/nodes/ImportNode.js +43 -0
  102. package/dist/core/nodes/LiteralNode.d.ts +26 -0
  103. package/dist/core/nodes/LiteralNode.d.ts.map +1 -0
  104. package/dist/core/nodes/LiteralNode.js +40 -0
  105. package/dist/core/nodes/MethodCallNode.d.ts +29 -0
  106. package/dist/core/nodes/MethodCallNode.d.ts.map +1 -0
  107. package/dist/core/nodes/MethodCallNode.js +47 -0
  108. package/dist/core/nodes/MethodNode.d.ts +29 -0
  109. package/dist/core/nodes/MethodNode.d.ts.map +1 -0
  110. package/dist/core/nodes/MethodNode.js +44 -0
  111. package/dist/core/nodes/ModuleNode.d.ts +29 -0
  112. package/dist/core/nodes/ModuleNode.d.ts.map +1 -0
  113. package/dist/core/nodes/ModuleNode.js +49 -0
  114. package/dist/core/nodes/NodeKind.d.ts +91 -0
  115. package/dist/core/nodes/NodeKind.d.ts.map +1 -0
  116. package/dist/core/nodes/NodeKind.js +146 -0
  117. package/dist/core/nodes/ParameterNode.d.ts +26 -0
  118. package/dist/core/nodes/ParameterNode.d.ts.map +1 -0
  119. package/dist/core/nodes/ParameterNode.js +43 -0
  120. package/dist/core/nodes/ScopeNode.d.ts +32 -0
  121. package/dist/core/nodes/ScopeNode.d.ts.map +1 -0
  122. package/dist/core/nodes/ScopeNode.js +47 -0
  123. package/dist/core/nodes/ServiceNode.d.ts +44 -0
  124. package/dist/core/nodes/ServiceNode.d.ts.map +1 -0
  125. package/dist/core/nodes/ServiceNode.js +49 -0
  126. package/dist/core/nodes/VariableDeclarationNode.d.ts +22 -0
  127. package/dist/core/nodes/VariableDeclarationNode.d.ts.map +1 -0
  128. package/dist/core/nodes/VariableDeclarationNode.js +38 -0
  129. package/dist/core/nodes/index.d.ts +25 -0
  130. package/dist/core/nodes/index.d.ts.map +1 -0
  131. package/dist/core/nodes/index.js +30 -0
  132. package/dist/index.d.ts +57 -0
  133. package/dist/index.d.ts.map +1 -0
  134. package/dist/index.js +63 -0
  135. package/dist/plugins/Plugin.d.ts +44 -0
  136. package/dist/plugins/Plugin.d.ts.map +1 -0
  137. package/dist/plugins/Plugin.js +46 -0
  138. package/dist/plugins/analysis/DatabaseAnalyzer.d.ts +23 -0
  139. package/dist/plugins/analysis/DatabaseAnalyzer.d.ts.map +1 -0
  140. package/dist/plugins/analysis/DatabaseAnalyzer.js +260 -0
  141. package/dist/plugins/analysis/ExpressAnalyzer.d.ts +19 -0
  142. package/dist/plugins/analysis/ExpressAnalyzer.d.ts.map +1 -0
  143. package/dist/plugins/analysis/ExpressAnalyzer.js +306 -0
  144. package/dist/plugins/analysis/ExpressRouteAnalyzer.d.ts +17 -0
  145. package/dist/plugins/analysis/ExpressRouteAnalyzer.d.ts.map +1 -0
  146. package/dist/plugins/analysis/ExpressRouteAnalyzer.js +308 -0
  147. package/dist/plugins/analysis/FetchAnalyzer.d.ts +38 -0
  148. package/dist/plugins/analysis/FetchAnalyzer.d.ts.map +1 -0
  149. package/dist/plugins/analysis/FetchAnalyzer.js +344 -0
  150. package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts +65 -0
  151. package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts.map +1 -0
  152. package/dist/plugins/analysis/IncrementalAnalysisPlugin.js +472 -0
  153. package/dist/plugins/analysis/JSASTAnalyzer.d.ts +84 -0
  154. package/dist/plugins/analysis/JSASTAnalyzer.d.ts.map +1 -0
  155. package/dist/plugins/analysis/JSASTAnalyzer.js +1378 -0
  156. package/dist/plugins/analysis/ReactAnalyzer.d.ts +90 -0
  157. package/dist/plugins/analysis/ReactAnalyzer.d.ts.map +1 -0
  158. package/dist/plugins/analysis/ReactAnalyzer.js +1153 -0
  159. package/dist/plugins/analysis/RustAnalyzer.d.ts +13 -0
  160. package/dist/plugins/analysis/RustAnalyzer.d.ts.map +1 -0
  161. package/dist/plugins/analysis/RustAnalyzer.js +259 -0
  162. package/dist/plugins/analysis/SQLiteAnalyzer.d.ts +21 -0
  163. package/dist/plugins/analysis/SQLiteAnalyzer.d.ts.map +1 -0
  164. package/dist/plugins/analysis/SQLiteAnalyzer.js +317 -0
  165. package/dist/plugins/analysis/ServiceLayerAnalyzer.d.ts +35 -0
  166. package/dist/plugins/analysis/ServiceLayerAnalyzer.d.ts.map +1 -0
  167. package/dist/plugins/analysis/ServiceLayerAnalyzer.js +303 -0
  168. package/dist/plugins/analysis/SocketIOAnalyzer.d.ts +33 -0
  169. package/dist/plugins/analysis/SocketIOAnalyzer.d.ts.map +1 -0
  170. package/dist/plugins/analysis/SocketIOAnalyzer.js +283 -0
  171. package/dist/plugins/analysis/SystemDbAnalyzer.d.ts +27 -0
  172. package/dist/plugins/analysis/SystemDbAnalyzer.d.ts.map +1 -0
  173. package/dist/plugins/analysis/SystemDbAnalyzer.js +211 -0
  174. package/dist/plugins/analysis/ast/ConditionParser.d.ts +85 -0
  175. package/dist/plugins/analysis/ast/ConditionParser.d.ts.map +1 -0
  176. package/dist/plugins/analysis/ast/ConditionParser.js +277 -0
  177. package/dist/plugins/analysis/ast/ExpressionEvaluator.d.ts +15 -0
  178. package/dist/plugins/analysis/ast/ExpressionEvaluator.d.ts.map +1 -0
  179. package/dist/plugins/analysis/ast/ExpressionEvaluator.js +91 -0
  180. package/dist/plugins/analysis/ast/GraphBuilder.d.ts +77 -0
  181. package/dist/plugins/analysis/ast/GraphBuilder.d.ts.map +1 -0
  182. package/dist/plugins/analysis/ast/GraphBuilder.js +1077 -0
  183. package/dist/plugins/analysis/ast/OxcAdapter.d.ts +41 -0
  184. package/dist/plugins/analysis/ast/OxcAdapter.d.ts.map +1 -0
  185. package/dist/plugins/analysis/ast/OxcAdapter.js +40 -0
  186. package/dist/plugins/analysis/ast/types.d.ts +346 -0
  187. package/dist/plugins/analysis/ast/types.d.ts.map +1 -0
  188. package/dist/plugins/analysis/ast/types.js +4 -0
  189. package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts +93 -0
  190. package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts.map +1 -0
  191. package/dist/plugins/analysis/ast/visitors/ASTVisitor.js +24 -0
  192. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts +77 -0
  193. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts.map +1 -0
  194. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js +377 -0
  195. package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts +27 -0
  196. package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts.map +1 -0
  197. package/dist/plugins/analysis/ast/visitors/ClassVisitor.js +232 -0
  198. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts +25 -0
  199. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts.map +1 -0
  200. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js +172 -0
  201. package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.d.ts +29 -0
  202. package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.d.ts.map +1 -0
  203. package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.js +180 -0
  204. package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts +14 -0
  205. package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts.map +1 -0
  206. package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.js +200 -0
  207. package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts +45 -0
  208. package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts.map +1 -0
  209. package/dist/plugins/analysis/ast/visitors/VariableVisitor.js +150 -0
  210. package/dist/plugins/analysis/ast/visitors/index.d.ts +17 -0
  211. package/dist/plugins/analysis/ast/visitors/index.d.ts.map +1 -0
  212. package/dist/plugins/analysis/ast/visitors/index.js +13 -0
  213. package/dist/plugins/discovery/DiscoveryPlugin.d.ts +34 -0
  214. package/dist/plugins/discovery/DiscoveryPlugin.d.ts.map +1 -0
  215. package/dist/plugins/discovery/DiscoveryPlugin.js +26 -0
  216. package/dist/plugins/discovery/MonorepoServiceDiscovery.d.ts +26 -0
  217. package/dist/plugins/discovery/MonorepoServiceDiscovery.d.ts.map +1 -0
  218. package/dist/plugins/discovery/MonorepoServiceDiscovery.js +79 -0
  219. package/dist/plugins/discovery/SimpleProjectDiscovery.d.ts +14 -0
  220. package/dist/plugins/discovery/SimpleProjectDiscovery.d.ts.map +1 -0
  221. package/dist/plugins/discovery/SimpleProjectDiscovery.js +65 -0
  222. package/dist/plugins/discovery/ZonServiceDiscovery.d.ts +19 -0
  223. package/dist/plugins/discovery/ZonServiceDiscovery.d.ts.map +1 -0
  224. package/dist/plugins/discovery/ZonServiceDiscovery.js +204 -0
  225. package/dist/plugins/enrichment/AliasTracker.d.ts +40 -0
  226. package/dist/plugins/enrichment/AliasTracker.d.ts.map +1 -0
  227. package/dist/plugins/enrichment/AliasTracker.js +290 -0
  228. package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts +30 -0
  229. package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts.map +1 -0
  230. package/dist/plugins/enrichment/HTTPConnectionEnricher.js +135 -0
  231. package/dist/plugins/enrichment/ImportExportLinker.d.ts +30 -0
  232. package/dist/plugins/enrichment/ImportExportLinker.d.ts.map +1 -0
  233. package/dist/plugins/enrichment/ImportExportLinker.js +176 -0
  234. package/dist/plugins/enrichment/InstanceOfResolver.d.ts +21 -0
  235. package/dist/plugins/enrichment/InstanceOfResolver.d.ts.map +1 -0
  236. package/dist/plugins/enrichment/InstanceOfResolver.js +117 -0
  237. package/dist/plugins/enrichment/MethodCallResolver.d.ts +41 -0
  238. package/dist/plugins/enrichment/MethodCallResolver.d.ts.map +1 -0
  239. package/dist/plugins/enrichment/MethodCallResolver.js +252 -0
  240. package/dist/plugins/enrichment/MountPointResolver.d.ts +26 -0
  241. package/dist/plugins/enrichment/MountPointResolver.d.ts.map +1 -0
  242. package/dist/plugins/enrichment/MountPointResolver.js +189 -0
  243. package/dist/plugins/enrichment/PrefixEvaluator.d.ts +89 -0
  244. package/dist/plugins/enrichment/PrefixEvaluator.d.ts.map +1 -0
  245. package/dist/plugins/enrichment/PrefixEvaluator.js +415 -0
  246. package/dist/plugins/enrichment/RustFFIEnricher.d.ts +25 -0
  247. package/dist/plugins/enrichment/RustFFIEnricher.d.ts.map +1 -0
  248. package/dist/plugins/enrichment/RustFFIEnricher.js +170 -0
  249. package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts +114 -0
  250. package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts.map +1 -0
  251. package/dist/plugins/enrichment/ValueDomainAnalyzer.js +464 -0
  252. package/dist/plugins/indexing/IncrementalModuleIndexer.d.ts +27 -0
  253. package/dist/plugins/indexing/IncrementalModuleIndexer.d.ts.map +1 -0
  254. package/dist/plugins/indexing/IncrementalModuleIndexer.js +238 -0
  255. package/dist/plugins/indexing/JSModuleIndexer.d.ts +33 -0
  256. package/dist/plugins/indexing/JSModuleIndexer.d.ts.map +1 -0
  257. package/dist/plugins/indexing/JSModuleIndexer.js +299 -0
  258. package/dist/plugins/indexing/RustModuleIndexer.d.ts +28 -0
  259. package/dist/plugins/indexing/RustModuleIndexer.d.ts.map +1 -0
  260. package/dist/plugins/indexing/RustModuleIndexer.js +140 -0
  261. package/dist/plugins/indexing/ServiceDetector.d.ts +46 -0
  262. package/dist/plugins/indexing/ServiceDetector.d.ts.map +1 -0
  263. package/dist/plugins/indexing/ServiceDetector.js +164 -0
  264. package/dist/plugins/validation/CallResolverValidator.d.ts +23 -0
  265. package/dist/plugins/validation/CallResolverValidator.d.ts.map +1 -0
  266. package/dist/plugins/validation/CallResolverValidator.js +108 -0
  267. package/dist/plugins/validation/DataFlowValidator.d.ts +24 -0
  268. package/dist/plugins/validation/DataFlowValidator.d.ts.map +1 -0
  269. package/dist/plugins/validation/DataFlowValidator.js +148 -0
  270. package/dist/plugins/validation/EvalBanValidator.d.ts +25 -0
  271. package/dist/plugins/validation/EvalBanValidator.d.ts.map +1 -0
  272. package/dist/plugins/validation/EvalBanValidator.js +123 -0
  273. package/dist/plugins/validation/GraphConnectivityValidator.d.ts +11 -0
  274. package/dist/plugins/validation/GraphConnectivityValidator.d.ts.map +1 -0
  275. package/dist/plugins/validation/GraphConnectivityValidator.js +135 -0
  276. package/dist/plugins/validation/SQLInjectionValidator.d.ts +43 -0
  277. package/dist/plugins/validation/SQLInjectionValidator.d.ts.map +1 -0
  278. package/dist/plugins/validation/SQLInjectionValidator.js +251 -0
  279. package/dist/plugins/validation/ShadowingDetector.d.ts +26 -0
  280. package/dist/plugins/validation/ShadowingDetector.d.ts.map +1 -0
  281. package/dist/plugins/validation/ShadowingDetector.js +119 -0
  282. package/dist/plugins/validation/TypeScriptDeadCodeValidator.d.ts +21 -0
  283. package/dist/plugins/validation/TypeScriptDeadCodeValidator.d.ts.map +1 -0
  284. package/dist/plugins/validation/TypeScriptDeadCodeValidator.js +151 -0
  285. package/dist/plugins/vcs/GitPlugin.d.ts +84 -0
  286. package/dist/plugins/vcs/GitPlugin.d.ts.map +1 -0
  287. package/dist/plugins/vcs/GitPlugin.js +295 -0
  288. package/dist/plugins/vcs/VCSPlugin.d.ts +133 -0
  289. package/dist/plugins/vcs/VCSPlugin.d.ts.map +1 -0
  290. package/dist/plugins/vcs/VCSPlugin.js +82 -0
  291. package/dist/plugins/vcs/index.d.ts +10 -0
  292. package/dist/plugins/vcs/index.d.ts.map +1 -0
  293. package/dist/plugins/vcs/index.js +18 -0
  294. package/dist/storage/backends/RFDBServerBackend.d.ts +258 -0
  295. package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -0
  296. package/dist/storage/backends/RFDBServerBackend.js +565 -0
  297. package/dist/storage/backends/typeValidation.d.ts +47 -0
  298. package/dist/storage/backends/typeValidation.d.ts.map +1 -0
  299. package/dist/storage/backends/typeValidation.js +137 -0
  300. package/dist/validation/PathValidator.d.ts +81 -0
  301. package/dist/validation/PathValidator.d.ts.map +1 -0
  302. package/dist/validation/PathValidator.js +251 -0
  303. package/package.json +57 -0
  304. package/src/.rfguard/current-session.txt +1 -0
  305. package/src/Orchestrator.ts +673 -0
  306. package/src/api/GraphAPI.ts +305 -0
  307. package/src/api/GuaranteeAPI.ts +401 -0
  308. package/src/core/ASTWorker.ts +567 -0
  309. package/src/core/ASTWorkerPool.ts +299 -0
  310. package/src/core/AnalysisQueue.ts +447 -0
  311. package/src/core/AnalysisWorker.ts +410 -0
  312. package/src/core/GraphBackend.ts +265 -0
  313. package/src/core/GuaranteeManager.ts +581 -0
  314. package/src/core/ManifestStore.ts +196 -0
  315. package/src/core/NodeFactory.ts +274 -0
  316. package/src/core/NodeId.ts +257 -0
  317. package/src/core/ParallelAnalyzer.ts +476 -0
  318. package/src/core/PriorityQueue.ts +227 -0
  319. package/src/core/Profiler.ts +188 -0
  320. package/src/core/QueueWorker.ts +780 -0
  321. package/src/core/Task.ts +107 -0
  322. package/src/core/TaskTypes.ts +40 -0
  323. package/src/core/VersionManager.ts +404 -0
  324. package/src/core/WorkerPool.ts +180 -0
  325. package/src/core/nodes/CallSiteNode.ts +72 -0
  326. package/src/core/nodes/ClassNode.ts +69 -0
  327. package/src/core/nodes/ConstantNode.ts +63 -0
  328. package/src/core/nodes/DatabaseQueryNode.ts +60 -0
  329. package/src/core/nodes/EntrypointNode.ts +164 -0
  330. package/src/core/nodes/EventListenerNode.ts +64 -0
  331. package/src/core/nodes/ExportNode.ts +71 -0
  332. package/src/core/nodes/ExternalStdioNode.ts +36 -0
  333. package/src/core/nodes/FunctionNode.ts +78 -0
  334. package/src/core/nodes/GuaranteeNode.ts +162 -0
  335. package/src/core/nodes/HttpRequestNode.ts +63 -0
  336. package/src/core/nodes/ImportNode.ts +75 -0
  337. package/src/core/nodes/LiteralNode.ts +67 -0
  338. package/src/core/nodes/MethodCallNode.ts +79 -0
  339. package/src/core/nodes/MethodNode.ts +78 -0
  340. package/src/core/nodes/ModuleNode.ts +74 -0
  341. package/src/core/nodes/NodeKind.ts +171 -0
  342. package/src/core/nodes/ParameterNode.ts +73 -0
  343. package/src/core/nodes/ScopeNode.ts +80 -0
  344. package/src/core/nodes/ServiceNode.ts +86 -0
  345. package/src/core/nodes/VariableDeclarationNode.ts +60 -0
  346. package/src/core/nodes/index.ts +49 -0
  347. package/src/index.ts +93 -0
  348. package/src/plugins/Plugin.ts +74 -0
  349. package/src/plugins/analysis/DatabaseAnalyzer.ts +322 -0
  350. package/src/plugins/analysis/ExpressAnalyzer.ts +401 -0
  351. package/src/plugins/analysis/ExpressRouteAnalyzer.ts +414 -0
  352. package/src/plugins/analysis/FetchAnalyzer.ts +441 -0
  353. package/src/plugins/analysis/IncrementalAnalysisPlugin.ts +686 -0
  354. package/src/plugins/analysis/JSASTAnalyzer.ts +1680 -0
  355. package/src/plugins/analysis/ReactAnalyzer.ts +1368 -0
  356. package/src/plugins/analysis/RustAnalyzer.ts +438 -0
  357. package/src/plugins/analysis/SQLiteAnalyzer.ts +388 -0
  358. package/src/plugins/analysis/ServiceLayerAnalyzer.ts +429 -0
  359. package/src/plugins/analysis/SocketIOAnalyzer.ts +395 -0
  360. package/src/plugins/analysis/SystemDbAnalyzer.ts +284 -0
  361. package/src/plugins/analysis/ast/ConditionParser.ts +333 -0
  362. package/src/plugins/analysis/ast/ExpressionEvaluator.ts +117 -0
  363. package/src/plugins/analysis/ast/GraphBuilder.ts +1371 -0
  364. package/src/plugins/analysis/ast/OxcAdapter.ts +63 -0
  365. package/src/plugins/analysis/ast/types.ts +400 -0
  366. package/src/plugins/analysis/ast/visitors/ASTVisitor.ts +137 -0
  367. package/src/plugins/analysis/ast/visitors/CallExpressionVisitor.ts +528 -0
  368. package/src/plugins/analysis/ast/visitors/ClassVisitor.ts +339 -0
  369. package/src/plugins/analysis/ast/visitors/FunctionVisitor.ts +273 -0
  370. package/src/plugins/analysis/ast/visitors/ImportExportVisitor.ts +259 -0
  371. package/src/plugins/analysis/ast/visitors/TypeScriptVisitor.ts +235 -0
  372. package/src/plugins/analysis/ast/visitors/VariableVisitor.ts +268 -0
  373. package/src/plugins/analysis/ast/visitors/index.ts +36 -0
  374. package/src/plugins/discovery/DiscoveryPlugin.ts +50 -0
  375. package/src/plugins/discovery/MonorepoServiceDiscovery.ts +117 -0
  376. package/src/plugins/discovery/SimpleProjectDiscovery.ts +102 -0
  377. package/src/plugins/enrichment/AliasTracker.ts +399 -0
  378. package/src/plugins/enrichment/HTTPConnectionEnricher.ts +192 -0
  379. package/src/plugins/enrichment/ImportExportLinker.ts +221 -0
  380. package/src/plugins/enrichment/InstanceOfResolver.ts +165 -0
  381. package/src/plugins/enrichment/MethodCallResolver.ts +333 -0
  382. package/src/plugins/enrichment/MountPointResolver.ts +264 -0
  383. package/src/plugins/enrichment/PrefixEvaluator.ts +527 -0
  384. package/src/plugins/enrichment/RustFFIEnricher.ts +218 -0
  385. package/src/plugins/enrichment/ValueDomainAnalyzer.ts +682 -0
  386. package/src/plugins/indexing/IncrementalModuleIndexer.ts +287 -0
  387. package/src/plugins/indexing/JSModuleIndexer.ts +374 -0
  388. package/src/plugins/indexing/RustModuleIndexer.ts +160 -0
  389. package/src/plugins/indexing/ServiceDetector.ts +230 -0
  390. package/src/plugins/validation/CallResolverValidator.ts +170 -0
  391. package/src/plugins/validation/DataFlowValidator.ts +233 -0
  392. package/src/plugins/validation/EvalBanValidator.ts +175 -0
  393. package/src/plugins/validation/GraphConnectivityValidator.ts +201 -0
  394. package/src/plugins/validation/SQLInjectionValidator.ts +363 -0
  395. package/src/plugins/validation/ShadowingDetector.ts +173 -0
  396. package/src/plugins/validation/TypeScriptDeadCodeValidator.ts +203 -0
  397. package/src/plugins/vcs/GitPlugin.ts +344 -0
  398. package/src/plugins/vcs/VCSPlugin.ts +190 -0
  399. package/src/plugins/vcs/index.ts +32 -0
  400. package/src/storage/backends/RFDBServerBackend.ts +687 -0
  401. package/src/storage/backends/typeValidation.ts +151 -0
  402. package/src/validation/PathValidator.ts +342 -0
@@ -0,0 +1,1680 @@
1
+ /**
2
+ * JSASTAnalyzer - плагин для парсинга JavaScript AST
3
+ * Создаёт ноды: FUNCTION, CLASS, METHOD и т.д.
4
+ */
5
+
6
+ import { readFileSync } from 'fs';
7
+ import { createHash } from 'crypto';
8
+ import { parse } from '@babel/parser';
9
+ import traverseModule from '@babel/traverse';
10
+ import type { NodePath, TraverseOptions } from '@babel/traverse';
11
+ import * as t from '@babel/types';
12
+
13
+ // Type for CJS/ESM interop - @babel/traverse exports a function but @types defines it as namespace
14
+ type TraverseFn = (ast: t.Node, opts: TraverseOptions) => void;
15
+ const rawModule = traverseModule as unknown as TraverseFn | { default: TraverseFn };
16
+ const traverse: TraverseFn = typeof rawModule === 'function' ? rawModule : rawModule.default;
17
+
18
+ // Type guard for analysis result
19
+ interface AnalysisResult {
20
+ nodes: number;
21
+ edges: number;
22
+ }
23
+
24
+ function isAnalysisResult(value: unknown): value is AnalysisResult {
25
+ if (typeof value !== 'object' || value === null) return false;
26
+ if (!('nodes' in value) || !('edges' in value)) return false;
27
+ // After 'in' checks, TS knows properties exist; widening to unknown is safe
28
+ const { nodes, edges } = value as { nodes: unknown; edges: unknown };
29
+ return typeof nodes === 'number' && typeof edges === 'number';
30
+ }
31
+
32
+ import { Plugin, createSuccessResult, createErrorResult } from '../Plugin.js';
33
+ import { ExpressionEvaluator } from './ast/ExpressionEvaluator.js';
34
+ import { GraphBuilder } from './ast/GraphBuilder.js';
35
+ import {
36
+ ImportExportVisitor,
37
+ VariableVisitor,
38
+ FunctionVisitor,
39
+ ClassVisitor,
40
+ CallExpressionVisitor,
41
+ TypeScriptVisitor,
42
+ type VisitorModule,
43
+ type VisitorCollections,
44
+ type TrackVariableAssignmentCallback
45
+ } from './ast/visitors/index.js';
46
+ import { Task } from '../../core/Task.js';
47
+ import { PriorityQueue } from '../../core/PriorityQueue.js';
48
+ import { WorkerPool } from '../../core/WorkerPool.js';
49
+ import { ConditionParser } from './ast/ConditionParser.js';
50
+ import { Profiler } from '../../core/Profiler.js';
51
+ import type { PluginContext, PluginResult, PluginMetadata, GraphBackend } from '@grafema/types';
52
+ import type {
53
+ ModuleNode,
54
+ FunctionInfo,
55
+ ParameterInfo,
56
+ ScopeInfo,
57
+ VariableDeclarationInfo,
58
+ CallSiteInfo,
59
+ MethodCallInfo,
60
+ EventListenerInfo,
61
+ ClassInstantiationInfo,
62
+ ClassDeclarationInfo,
63
+ MethodCallbackInfo,
64
+ CallArgumentInfo,
65
+ ImportInfo,
66
+ ExportInfo,
67
+ HttpRequestInfo,
68
+ LiteralInfo,
69
+ VariableAssignmentInfo,
70
+ InterfaceDeclarationInfo,
71
+ TypeAliasInfo,
72
+ EnumDeclarationInfo,
73
+ DecoratorInfo,
74
+ CounterRef,
75
+ ProcessedNodes,
76
+ ASTCollections,
77
+ ExtractedVariable,
78
+ } from './ast/types.js';
79
+
80
+ // === LOCAL TYPES ===
81
+
82
+ /**
83
+ * Context for tracking semantic IDs within a scope hierarchy.
84
+ * Used to generate stable, line-number-independent IDs for SCOPE nodes.
85
+ */
86
+ interface ScopeContext {
87
+ semanticPath: string; // "ClassName.method" or "funcName"
88
+ siblingCounters: Map<string, number>; // scopeType → count for this level
89
+ }
90
+
91
+ // Internal Collections with required fields (ASTCollections has optional for GraphBuilder)
92
+ interface Collections {
93
+ functions: FunctionInfo[];
94
+ parameters: ParameterInfo[];
95
+ scopes: ScopeInfo[];
96
+ variableDeclarations: VariableDeclarationInfo[];
97
+ callSites: CallSiteInfo[];
98
+ methodCalls: MethodCallInfo[];
99
+ eventListeners: EventListenerInfo[];
100
+ classInstantiations: ClassInstantiationInfo[];
101
+ classDeclarations: ClassDeclarationInfo[];
102
+ methodCallbacks: MethodCallbackInfo[];
103
+ callArguments: CallArgumentInfo[];
104
+ imports: ImportInfo[];
105
+ exports: ExportInfo[];
106
+ httpRequests: HttpRequestInfo[];
107
+ literals: LiteralInfo[];
108
+ variableAssignments: VariableAssignmentInfo[];
109
+ // TypeScript-specific collections
110
+ interfaces: InterfaceDeclarationInfo[];
111
+ typeAliases: TypeAliasInfo[];
112
+ enums: EnumDeclarationInfo[];
113
+ decorators: DecoratorInfo[];
114
+ ifScopeCounterRef: CounterRef;
115
+ scopeCounterRef: CounterRef;
116
+ varDeclCounterRef: CounterRef;
117
+ callSiteCounterRef: CounterRef;
118
+ functionCounterRef: CounterRef;
119
+ httpRequestCounterRef: CounterRef;
120
+ literalCounterRef: CounterRef;
121
+ anonymousFunctionCounterRef: CounterRef;
122
+ processedNodes: ProcessedNodes;
123
+ moduleScopeCtx?: ScopeContext;
124
+ code?: string;
125
+ // VisitorCollections compatibility
126
+ classes: ClassDeclarationInfo[];
127
+ methods: FunctionInfo[];
128
+ variables: VariableDeclarationInfo[];
129
+ sideEffects: unknown[]; // TODO: define SideEffectInfo
130
+ variableCounterRef: CounterRef;
131
+ [key: string]: unknown;
132
+ }
133
+
134
+ interface AnalysisManifest {
135
+ projectPath: string;
136
+ [key: string]: unknown;
137
+ }
138
+
139
+ interface AnalyzeContext extends PluginContext {
140
+ manifest?: AnalysisManifest;
141
+ forceAnalysis?: boolean;
142
+ workerCount?: number;
143
+ // Use base onProgress type for compatibility
144
+ onProgress?: (info: Record<string, unknown>) => void;
145
+ }
146
+
147
+ export class JSASTAnalyzer extends Plugin {
148
+ private graphBuilder: GraphBuilder;
149
+ private analyzedModules: Set<string>;
150
+ private profiler: Profiler;
151
+ private _cacheCleared: boolean;
152
+
153
+ constructor() {
154
+ super();
155
+ this.graphBuilder = new GraphBuilder();
156
+ this.analyzedModules = new Set();
157
+ this.profiler = new Profiler('JSASTAnalyzer');
158
+ this._cacheCleared = false;
159
+ }
160
+
161
+ get metadata(): PluginMetadata {
162
+ return {
163
+ name: 'JSASTAnalyzer',
164
+ phase: 'ANALYSIS',
165
+ priority: 80,
166
+ creates: {
167
+ nodes: [
168
+ 'FUNCTION', 'CLASS', 'METHOD', 'VARIABLE', 'CONSTANT', 'SCOPE',
169
+ 'CALL', 'IMPORT', 'EXPORT', 'LITERAL', 'EXTERNAL_MODULE',
170
+ 'net:stdio', 'net:request', 'event:listener', 'http:request',
171
+ // TypeScript-specific nodes
172
+ 'INTERFACE', 'TYPE', 'ENUM', 'DECORATOR'
173
+ ],
174
+ edges: [
175
+ 'CONTAINS', 'DECLARES', 'CALLS', 'HAS_SCOPE', 'CAPTURES', 'MODIFIES',
176
+ 'WRITES_TO', 'IMPORTS', 'INSTANCE_OF', 'HANDLED_BY', 'HAS_CALLBACK',
177
+ 'PASSES_ARGUMENT', 'MAKES_REQUEST', 'IMPORTS_FROM', 'EXPORTS_TO', 'ASSIGNED_FROM',
178
+ // TypeScript-specific edges
179
+ 'IMPLEMENTS', 'EXTENDS', 'DECORATED_BY'
180
+ ]
181
+ },
182
+ dependencies: ['JSModuleIndexer']
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Вычисляет хеш содержимого файла
188
+ */
189
+ calculateFileHash(filePath: string): string | null {
190
+ try {
191
+ const content = readFileSync(filePath, 'utf-8');
192
+ return createHash('sha256').update(content).digest('hex');
193
+ } catch {
194
+ return null;
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Проверяет нужно ли анализировать модуль (сравнивает хеши)
200
+ */
201
+ async shouldAnalyzeModule(module: ModuleNode, graph: GraphBackend, forceAnalysis: boolean): Promise<boolean> {
202
+ if (forceAnalysis) {
203
+ return true;
204
+ }
205
+
206
+ if (!module.contentHash) {
207
+ return true;
208
+ }
209
+
210
+ const currentHash = this.calculateFileHash(module.file);
211
+ if (!currentHash) {
212
+ return true;
213
+ }
214
+
215
+ if (currentHash !== module.contentHash) {
216
+ await graph.addNode({
217
+ id: module.id,
218
+ type: 'MODULE',
219
+ name: module.name,
220
+ file: module.file,
221
+ contentHash: currentHash
222
+ });
223
+ return true;
224
+ }
225
+
226
+ // Hash matches - check if module was actually analyzed (has FUNCTION nodes)
227
+ if (graph.queryNodes) {
228
+ for await (const _node of graph.queryNodes({ type: 'FUNCTION', file: module.file })) {
229
+ // Found at least one function - module was analyzed, skip
230
+ return false;
231
+ }
232
+ }
233
+ // No functions found - need to analyze
234
+ return true;
235
+ }
236
+
237
+ async execute(context: AnalyzeContext): Promise<PluginResult> {
238
+ try {
239
+ const { manifest, graph, forceAnalysis = false } = context;
240
+ const projectPath = manifest?.projectPath ?? '';
241
+
242
+ if (forceAnalysis && !this._cacheCleared) {
243
+ this.analyzedModules.clear();
244
+ this._cacheCleared = true;
245
+ }
246
+
247
+ const allModules = await this.getModuleNodes(graph);
248
+
249
+ const modulesToAnalyze: ModuleNode[] = [];
250
+ let skippedCount = 0;
251
+
252
+ for (const module of allModules) {
253
+ if (this.analyzedModules.has(module.id)) {
254
+ skippedCount++;
255
+ continue;
256
+ }
257
+
258
+ if (await this.shouldAnalyzeModule(module, graph, forceAnalysis)) {
259
+ modulesToAnalyze.push(module);
260
+ } else {
261
+ skippedCount++;
262
+ }
263
+ }
264
+
265
+ console.log(`[JSASTAnalyzer] Starting parallel analysis of ${modulesToAnalyze.length} modules (${skippedCount} cached)...`);
266
+
267
+ if (modulesToAnalyze.length === 0) {
268
+ console.log(`[JSASTAnalyzer] All modules are up-to-date, skipping analysis`);
269
+ return createSuccessResult({ nodes: 0, edges: 0 });
270
+ }
271
+
272
+ const queue = new PriorityQueue();
273
+ const pool = new WorkerPool(context.workerCount || 10);
274
+
275
+ pool.registerHandler('ANALYZE_MODULE', async (task) => {
276
+ return await this.analyzeModule(task.data.module, graph, projectPath);
277
+ });
278
+
279
+ for (const module of modulesToAnalyze) {
280
+ this.analyzedModules.add(module.id);
281
+
282
+ const task = new Task({
283
+ id: `analyze:${module.id}`,
284
+ type: 'ANALYZE_MODULE',
285
+ priority: 80,
286
+ data: { module }
287
+ });
288
+ queue.add(task);
289
+ }
290
+
291
+ let completed = 0;
292
+ let currentFile = '';
293
+
294
+ const progressInterval = setInterval(() => {
295
+ if (context.onProgress && completed > 0) {
296
+ context.onProgress({
297
+ phase: 'analysis',
298
+ currentPlugin: 'JSASTAnalyzer',
299
+ message: `Analyzing ${currentFile} (${completed}/${modulesToAnalyze.length})`,
300
+ totalFiles: modulesToAnalyze.length,
301
+ processedFiles: completed
302
+ });
303
+ }
304
+ }, 500);
305
+
306
+ pool.on('worker:task:started', (task: Task) => {
307
+ currentFile = task.data.module.file?.replace(projectPath, '') || task.data.module.id;
308
+ });
309
+
310
+ pool.on('worker:task:completed', () => {
311
+ completed++;
312
+
313
+ if (completed % 10 === 0 || completed === modulesToAnalyze.length) {
314
+ console.log(`[JSASTAnalyzer] Progress: ${completed}/${modulesToAnalyze.length}`);
315
+ }
316
+ });
317
+
318
+ await pool.processQueue(queue);
319
+
320
+ clearInterval(progressInterval);
321
+
322
+ if (context.onProgress) {
323
+ context.onProgress({
324
+ phase: 'analysis',
325
+ currentPlugin: 'JSASTAnalyzer',
326
+ totalFiles: modulesToAnalyze.length,
327
+ processedFiles: completed
328
+ });
329
+ }
330
+
331
+ const stats = queue.getStats();
332
+ let nodesCreated = 0;
333
+ let edgesCreated = 0;
334
+
335
+ for (const task of queue.getCompletedTasks()) {
336
+ if (isAnalysisResult(task.result)) {
337
+ nodesCreated += task.result.nodes;
338
+ edgesCreated += task.result.edges;
339
+ }
340
+ }
341
+
342
+ console.log(`[JSASTAnalyzer] Analyzed ${modulesToAnalyze.length} modules, created ${nodesCreated} nodes`);
343
+ console.log(`[JSASTAnalyzer] Stats:`, stats);
344
+
345
+ this.profiler.printSummary();
346
+
347
+ return createSuccessResult(
348
+ { nodes: nodesCreated, edges: edgesCreated },
349
+ { modulesAnalyzed: modulesToAnalyze.length, workerStats: stats }
350
+ );
351
+
352
+ } catch (error) {
353
+ console.error(`[JSASTAnalyzer] Error:`, error);
354
+ const err = error instanceof Error ? error : new Error(String(error));
355
+ return createErrorResult(err);
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Extract variable names from destructuring patterns
361
+ * Uses t.isX() type guards to avoid casts
362
+ */
363
+ extractVariableNamesFromPattern(pattern: t.Node | null | undefined, variables: ExtractedVariable[] = [], propertyPath: string[] = []): ExtractedVariable[] {
364
+ if (!pattern) return variables;
365
+
366
+ if (t.isIdentifier(pattern)) {
367
+ variables.push({
368
+ name: pattern.name,
369
+ loc: pattern.loc?.start ? { start: pattern.loc.start } : { start: { line: 0, column: 0 } },
370
+ propertyPath: propertyPath.length > 0 ? [...propertyPath] : undefined
371
+ });
372
+ } else if (t.isObjectPattern(pattern)) {
373
+ pattern.properties.forEach((prop) => {
374
+ if (t.isRestElement(prop)) {
375
+ const restVars = this.extractVariableNamesFromPattern(prop.argument, [], []);
376
+ restVars.forEach(v => {
377
+ v.isRest = true;
378
+ v.propertyPath = propertyPath.length > 0 ? [...propertyPath] : undefined;
379
+ variables.push(v);
380
+ });
381
+ } else if (t.isObjectProperty(prop) && prop.value) {
382
+ const key = t.isIdentifier(prop.key) ? prop.key.name :
383
+ (t.isStringLiteral(prop.key) || t.isNumericLiteral(prop.key) ? String(prop.key.value) : null);
384
+
385
+ if (key !== null) {
386
+ const newPath = [...propertyPath, key];
387
+ this.extractVariableNamesFromPattern(prop.value, variables, newPath);
388
+ } else {
389
+ this.extractVariableNamesFromPattern(prop.value, variables, propertyPath);
390
+ }
391
+ }
392
+ });
393
+ } else if (t.isArrayPattern(pattern)) {
394
+ pattern.elements.forEach((element, index) => {
395
+ if (element) {
396
+ if (t.isRestElement(element)) {
397
+ const restVars = this.extractVariableNamesFromPattern(element.argument, [], []);
398
+ restVars.forEach(v => {
399
+ v.isRest = true;
400
+ v.arrayIndex = index;
401
+ v.propertyPath = propertyPath.length > 0 ? [...propertyPath] : undefined;
402
+ variables.push(v);
403
+ });
404
+ } else {
405
+ const extracted = this.extractVariableNamesFromPattern(element, [], propertyPath);
406
+ extracted.forEach(v => {
407
+ v.arrayIndex = index;
408
+ variables.push(v);
409
+ });
410
+ }
411
+ }
412
+ });
413
+ } else if (t.isRestElement(pattern)) {
414
+ const restVars = this.extractVariableNamesFromPattern(pattern.argument, [], propertyPath);
415
+ restVars.forEach(v => {
416
+ v.isRest = true;
417
+ variables.push(v);
418
+ });
419
+ } else if (t.isAssignmentPattern(pattern)) {
420
+ this.extractVariableNamesFromPattern(pattern.left, variables, propertyPath);
421
+ }
422
+
423
+ return variables;
424
+ }
425
+
426
+ /**
427
+ * Отслеживает присваивание переменной для data flow анализа
428
+ */
429
+ trackVariableAssignment(
430
+ initNode: t.Expression | null | undefined,
431
+ variableId: string,
432
+ variableName: string,
433
+ module: VisitorModule,
434
+ line: number,
435
+ literals: LiteralInfo[],
436
+ variableAssignments: VariableAssignmentInfo[],
437
+ literalCounterRef: CounterRef
438
+ ): void {
439
+ if (!initNode) return;
440
+ // initNode is already typed as t.Expression
441
+ const initExpression = initNode;
442
+
443
+ // 0. AwaitExpression
444
+ if (initExpression.type === 'AwaitExpression') {
445
+ return this.trackVariableAssignment(initExpression.argument, variableId, variableName, module, line, literals, variableAssignments, literalCounterRef);
446
+ }
447
+
448
+ // 1. Literal
449
+ const literalValue = ExpressionEvaluator.extractLiteralValue(initExpression);
450
+ if (literalValue !== null) {
451
+ const literalId = `LITERAL#${line}:${initExpression.start}#${module.file}`;
452
+ literals.push({
453
+ id: literalId,
454
+ type: 'LITERAL',
455
+ value: literalValue,
456
+ valueType: typeof literalValue,
457
+ file: module.file,
458
+ line: line
459
+ });
460
+
461
+ variableAssignments.push({
462
+ variableId,
463
+ sourceId: literalId,
464
+ sourceType: 'LITERAL'
465
+ });
466
+ return;
467
+ }
468
+
469
+ // 2. CallExpression with Identifier
470
+ if (initExpression.type === 'CallExpression' && initExpression.callee.type === 'Identifier') {
471
+ variableAssignments.push({
472
+ variableId,
473
+ sourceId: null,
474
+ sourceType: 'CALL_SITE',
475
+ callName: initExpression.callee.name,
476
+ callLine: initExpression.loc!.start.line,
477
+ callColumn: initExpression.loc!.start.column
478
+ });
479
+ return;
480
+ }
481
+
482
+ // 3. MemberExpression call (e.g., arr.map())
483
+ if (initExpression.type === 'CallExpression' && initExpression.callee.type === 'MemberExpression') {
484
+ const callee = initExpression.callee;
485
+ const objectName = callee.object.type === 'Identifier' ? callee.object.name : (callee.object.type === 'ThisExpression' ? 'this' : 'unknown');
486
+ const methodName = callee.property.type === 'Identifier' ? callee.property.name : 'unknown';
487
+
488
+ const fullName = `${objectName}.${methodName}`;
489
+ const methodCallId = `CALL#${fullName}#${module.file}#${initExpression.loc!.start.line}:${initExpression.loc!.start.column}:inline`;
490
+
491
+ const existing = variableAssignments.find(a => a.sourceId === methodCallId);
492
+ if (!existing) {
493
+ const extractedArgs: unknown[] = [];
494
+ initExpression.arguments.forEach((arg, index) => {
495
+ if (arg.type !== 'SpreadElement') {
496
+ const argLiteralValue = ExpressionEvaluator.extractLiteralValue(arg);
497
+ if (argLiteralValue !== null) {
498
+ const literalId = `LITERAL#arg${index}#${module.file}#${initExpression.loc!.start.line}:${initExpression.loc!.start.column}:${literalCounterRef.value++}`;
499
+ literals.push({
500
+ id: literalId,
501
+ type: 'LITERAL',
502
+ value: argLiteralValue,
503
+ valueType: typeof argLiteralValue,
504
+ file: module.file,
505
+ line: arg.loc?.start.line || initExpression.loc!.start.line,
506
+ column: arg.loc?.start.column || initExpression.loc!.start.column,
507
+ parentCallId: methodCallId,
508
+ argIndex: index
509
+ });
510
+ extractedArgs.push(argLiteralValue);
511
+ } else {
512
+ extractedArgs.push(undefined);
513
+ }
514
+ }
515
+ });
516
+
517
+ literals.push({
518
+ id: methodCallId,
519
+ type: 'CALL',
520
+ name: fullName,
521
+ object: objectName,
522
+ method: methodName,
523
+ file: module.file,
524
+ arguments: extractedArgs,
525
+ line: initExpression.loc!.start.line,
526
+ column: initExpression.loc!.start.column
527
+ });
528
+ }
529
+
530
+ variableAssignments.push({
531
+ variableId,
532
+ sourceId: methodCallId,
533
+ sourceType: 'CALL'
534
+ });
535
+ return;
536
+ }
537
+
538
+ // 4. Identifier
539
+ if (initExpression.type === 'Identifier') {
540
+ variableAssignments.push({
541
+ variableId,
542
+ sourceType: 'VARIABLE',
543
+ sourceName: initExpression.name,
544
+ line: line
545
+ });
546
+ return;
547
+ }
548
+
549
+ // 5. NewExpression
550
+ if (initExpression.type === 'NewExpression') {
551
+ const callee = initExpression.callee;
552
+ if (callee.type === 'Identifier') {
553
+ variableAssignments.push({
554
+ variableId,
555
+ sourceType: 'CLASS',
556
+ className: callee.name,
557
+ line: line
558
+ });
559
+ }
560
+ return;
561
+ }
562
+
563
+ // 6. ArrowFunctionExpression or FunctionExpression
564
+ if (initExpression.type === 'ArrowFunctionExpression' || initExpression.type === 'FunctionExpression') {
565
+ variableAssignments.push({
566
+ variableId,
567
+ sourceType: 'FUNCTION',
568
+ functionName: variableName,
569
+ line: line
570
+ });
571
+ return;
572
+ }
573
+
574
+ // 7. MemberExpression (без вызова)
575
+ if (initExpression.type === 'MemberExpression') {
576
+ const objectName = initExpression.object.type === 'Identifier'
577
+ ? initExpression.object.name
578
+ : '<complex>';
579
+ const propertyName = initExpression.computed
580
+ ? '<computed>'
581
+ : (initExpression.property.type === 'Identifier' ? initExpression.property.name : '<unknown>');
582
+
583
+ const computedPropertyVar = initExpression.computed && initExpression.property.type === 'Identifier'
584
+ ? initExpression.property.name
585
+ : null;
586
+
587
+ const expressionId = `EXPRESSION#${objectName}.${propertyName}#${module.file}#${line}:${initExpression.start}`;
588
+
589
+ variableAssignments.push({
590
+ variableId,
591
+ sourceType: 'EXPRESSION',
592
+ sourceId: expressionId,
593
+ expressionType: 'MemberExpression',
594
+ object: objectName,
595
+ property: propertyName,
596
+ computed: initExpression.computed,
597
+ computedPropertyVar,
598
+ objectSourceName: initExpression.object.type === 'Identifier' ? initExpression.object.name : null,
599
+ file: module.file,
600
+ line: line
601
+ });
602
+ return;
603
+ }
604
+
605
+ // 8. BinaryExpression
606
+ if (initExpression.type === 'BinaryExpression') {
607
+ const expressionId = `EXPRESSION#binary#${module.file}#${line}:${initExpression.start}`;
608
+
609
+ variableAssignments.push({
610
+ variableId,
611
+ sourceType: 'EXPRESSION',
612
+ sourceId: expressionId,
613
+ expressionType: 'BinaryExpression',
614
+ operator: initExpression.operator,
615
+ leftSourceName: initExpression.left.type === 'Identifier' ? initExpression.left.name : null,
616
+ rightSourceName: initExpression.right.type === 'Identifier' ? initExpression.right.name : null,
617
+ file: module.file,
618
+ line: line
619
+ });
620
+ return;
621
+ }
622
+
623
+ // 9. ConditionalExpression
624
+ if (initExpression.type === 'ConditionalExpression') {
625
+ const expressionId = `EXPRESSION#conditional#${module.file}#${line}:${initExpression.start}`;
626
+
627
+ variableAssignments.push({
628
+ variableId,
629
+ sourceType: 'EXPRESSION',
630
+ sourceId: expressionId,
631
+ expressionType: 'ConditionalExpression',
632
+ consequentSourceName: initExpression.consequent.type === 'Identifier' ? initExpression.consequent.name : null,
633
+ alternateSourceName: initExpression.alternate.type === 'Identifier' ? initExpression.alternate.name : null,
634
+ file: module.file,
635
+ line: line
636
+ });
637
+
638
+ this.trackVariableAssignment(initExpression.consequent, variableId, variableName, module, line, literals, variableAssignments, literalCounterRef);
639
+ this.trackVariableAssignment(initExpression.alternate, variableId, variableName, module, line, literals, variableAssignments, literalCounterRef);
640
+ return;
641
+ }
642
+
643
+ // 10. LogicalExpression
644
+ if (initExpression.type === 'LogicalExpression') {
645
+ const expressionId = `EXPRESSION#logical#${module.file}#${line}:${initExpression.start}`;
646
+
647
+ variableAssignments.push({
648
+ variableId,
649
+ sourceType: 'EXPRESSION',
650
+ sourceId: expressionId,
651
+ expressionType: 'LogicalExpression',
652
+ operator: initExpression.operator,
653
+ leftSourceName: initExpression.left.type === 'Identifier' ? initExpression.left.name : null,
654
+ rightSourceName: initExpression.right.type === 'Identifier' ? initExpression.right.name : null,
655
+ file: module.file,
656
+ line: line
657
+ });
658
+
659
+ this.trackVariableAssignment(initExpression.left, variableId, variableName, module, line, literals, variableAssignments, literalCounterRef);
660
+ this.trackVariableAssignment(initExpression.right, variableId, variableName, module, line, literals, variableAssignments, literalCounterRef);
661
+ return;
662
+ }
663
+
664
+ // 11. TemplateLiteral
665
+ if (initExpression.type === 'TemplateLiteral' && initExpression.expressions.length > 0) {
666
+ const expressionId = `EXPRESSION#template#${module.file}#${line}:${initExpression.start}`;
667
+
668
+ const expressionSourceNames = initExpression.expressions
669
+ .filter((expr): expr is t.Identifier => expr.type === 'Identifier')
670
+ .map(expr => expr.name);
671
+
672
+ variableAssignments.push({
673
+ variableId,
674
+ sourceType: 'EXPRESSION',
675
+ sourceId: expressionId,
676
+ expressionType: 'TemplateLiteral',
677
+ expressionSourceNames,
678
+ file: module.file,
679
+ line: line
680
+ });
681
+
682
+ for (const expr of initExpression.expressions) {
683
+ // Filter out TSType nodes (only in TypeScript code)
684
+ if (t.isExpression(expr)) {
685
+ this.trackVariableAssignment(expr, variableId, variableName, module, line, literals, variableAssignments, literalCounterRef);
686
+ }
687
+ }
688
+ return;
689
+ }
690
+ }
691
+
692
+ /**
693
+ * Получить все MODULE ноды из графа
694
+ */
695
+ private async getModuleNodes(graph: GraphBackend): Promise<ModuleNode[]> {
696
+ const modules: ModuleNode[] = [];
697
+ for await (const node of graph.queryNodes({ type: 'MODULE' })) {
698
+ modules.push(node as unknown as ModuleNode);
699
+ }
700
+ return modules;
701
+ }
702
+
703
+ /**
704
+ * Анализировать один модуль
705
+ */
706
+ async analyzeModule(module: ModuleNode, graph: GraphBackend, projectPath: string): Promise<{ nodes: number; edges: number }> {
707
+ let nodesCreated = 0;
708
+ let edgesCreated = 0;
709
+
710
+ try {
711
+ this.profiler.start('file_read');
712
+ const code = readFileSync(module.file, 'utf-8');
713
+ this.profiler.end('file_read');
714
+
715
+ this.profiler.start('babel_parse');
716
+ const ast = parse(code, {
717
+ sourceType: 'module',
718
+ plugins: ['jsx', 'typescript']
719
+ });
720
+ this.profiler.end('babel_parse');
721
+
722
+ const functions: FunctionInfo[] = [];
723
+ const parameters: ParameterInfo[] = [];
724
+ const scopes: ScopeInfo[] = [];
725
+ const variableDeclarations: VariableDeclarationInfo[] = [];
726
+ const callSites: CallSiteInfo[] = [];
727
+ const methodCalls: MethodCallInfo[] = [];
728
+ const eventListeners: EventListenerInfo[] = [];
729
+ const classInstantiations: ClassInstantiationInfo[] = [];
730
+ const classDeclarations: ClassDeclarationInfo[] = [];
731
+ const methodCallbacks: MethodCallbackInfo[] = [];
732
+ const callArguments: CallArgumentInfo[] = [];
733
+ const imports: ImportInfo[] = [];
734
+ const exports: ExportInfo[] = [];
735
+ const httpRequests: HttpRequestInfo[] = [];
736
+ const literals: LiteralInfo[] = [];
737
+ const variableAssignments: VariableAssignmentInfo[] = [];
738
+ // TypeScript-specific collections
739
+ const interfaces: InterfaceDeclarationInfo[] = [];
740
+ const typeAliases: TypeAliasInfo[] = [];
741
+ const enums: EnumDeclarationInfo[] = [];
742
+ const decorators: DecoratorInfo[] = [];
743
+
744
+ const ifScopeCounterRef: CounterRef = { value: 0 };
745
+ const scopeCounterRef: CounterRef = { value: 0 };
746
+ const varDeclCounterRef: CounterRef = { value: 0 };
747
+ const callSiteCounterRef: CounterRef = { value: 0 };
748
+ const functionCounterRef: CounterRef = { value: 0 };
749
+ const httpRequestCounterRef: CounterRef = { value: 0 };
750
+ const literalCounterRef: CounterRef = { value: 0 };
751
+ const anonymousFunctionCounterRef: CounterRef = { value: 0 };
752
+
753
+ const processedNodes: ProcessedNodes = {
754
+ functions: new Set(),
755
+ classes: new Set(),
756
+ imports: new Set(),
757
+ exports: new Set(),
758
+ variables: new Set(),
759
+ callSites: new Set(),
760
+ methodCalls: new Set(),
761
+ varDecls: new Set(),
762
+ eventListeners: new Set()
763
+ };
764
+
765
+ // Imports/Exports
766
+ this.profiler.start('traverse_imports');
767
+ const importExportVisitor = new ImportExportVisitor(
768
+ module,
769
+ { imports, exports },
770
+ this.extractVariableNamesFromPattern.bind(this)
771
+ );
772
+ traverse(ast, importExportVisitor.getImportHandlers());
773
+ traverse(ast, importExportVisitor.getExportHandlers());
774
+ this.profiler.end('traverse_imports');
775
+
776
+ // Variables
777
+ this.profiler.start('traverse_variables');
778
+ const variableVisitor = new VariableVisitor(
779
+ module,
780
+ { variableDeclarations, classInstantiations, literals, variableAssignments, varDeclCounterRef, literalCounterRef },
781
+ this.extractVariableNamesFromPattern.bind(this),
782
+ this.trackVariableAssignment.bind(this) as TrackVariableAssignmentCallback
783
+ );
784
+ traverse(ast, variableVisitor.getHandlers());
785
+ this.profiler.end('traverse_variables');
786
+
787
+ // Module-level scope context for consistent anonymous naming
788
+ const moduleScopeCtx: ScopeContext = {
789
+ semanticPath: module.name!,
790
+ siblingCounters: new Map()
791
+ };
792
+
793
+ const allCollections: Collections = {
794
+ functions, parameters, scopes, variableDeclarations, callSites, methodCalls,
795
+ eventListeners, methodCallbacks, callArguments, classInstantiations, classDeclarations,
796
+ httpRequests, literals, variableAssignments,
797
+ // TypeScript-specific collections
798
+ interfaces, typeAliases, enums, decorators,
799
+ ifScopeCounterRef, scopeCounterRef, varDeclCounterRef,
800
+ callSiteCounterRef, functionCounterRef, httpRequestCounterRef,
801
+ literalCounterRef, anonymousFunctionCounterRef, processedNodes,
802
+ imports, exports, moduleScopeCtx, code,
803
+ // VisitorCollections compatibility
804
+ classes: classDeclarations,
805
+ methods: [],
806
+ variables: variableDeclarations,
807
+ sideEffects: [],
808
+ variableCounterRef: varDeclCounterRef
809
+ };
810
+
811
+ // Functions
812
+ this.profiler.start('traverse_functions');
813
+ const functionVisitor = new FunctionVisitor(
814
+ module,
815
+ allCollections,
816
+ this.analyzeFunctionBody.bind(this)
817
+ );
818
+ traverse(ast, functionVisitor.getHandlers());
819
+ this.profiler.end('traverse_functions');
820
+
821
+ // AssignmentExpression (module-level function assignments)
822
+ this.profiler.start('traverse_assignments');
823
+ traverse(ast, {
824
+ AssignmentExpression: (assignPath: NodePath<t.AssignmentExpression>) => {
825
+ const assignNode = assignPath.node;
826
+ const functionParent = assignPath.getFunctionParent();
827
+ if (functionParent) return;
828
+
829
+ if (assignNode.right &&
830
+ (assignNode.right.type === 'FunctionExpression' ||
831
+ assignNode.right.type === 'ArrowFunctionExpression')) {
832
+
833
+ let functionName = 'anonymous';
834
+ if (assignNode.left.type === 'MemberExpression') {
835
+ const prop = assignNode.left.property;
836
+ if (t.isIdentifier(prop)) {
837
+ functionName = prop.name;
838
+ }
839
+ } else if (assignNode.left.type === 'Identifier') {
840
+ functionName = assignNode.left.name;
841
+ }
842
+
843
+ const funcNode = assignNode.right;
844
+ const functionId = `FUNCTION#${functionName}#${module.file}#${assignNode.loc!.start.line}:${assignNode.loc!.start.column}`;
845
+
846
+ functions.push({
847
+ id: functionId,
848
+ stableId: functionId,
849
+ type: 'FUNCTION',
850
+ name: functionName,
851
+ file: module.file,
852
+ line: assignNode.loc!.start.line,
853
+ column: assignNode.loc!.start.column,
854
+ async: funcNode.async || false,
855
+ generator: funcNode.type === 'FunctionExpression' ? funcNode.generator : false,
856
+ isAssignment: true
857
+ });
858
+
859
+ const funcBodyScopeId = `SCOPE#${functionName}:body#${module.file}#${assignNode.loc!.start.line}`;
860
+ scopes.push({
861
+ id: funcBodyScopeId,
862
+ type: 'SCOPE',
863
+ scopeType: 'function_body',
864
+ name: `${functionName}:body`,
865
+ semanticId: `${functionName}:function_body[0]`,
866
+ conditional: false,
867
+ file: module.file,
868
+ line: assignNode.loc!.start.line,
869
+ parentFunctionId: functionId
870
+ });
871
+
872
+ const funcPath = assignPath.get('right') as NodePath<t.FunctionExpression | t.ArrowFunctionExpression>;
873
+ // Create scope context for analyzing the function body
874
+ const funcScopeCtx: ScopeContext = {
875
+ semanticPath: functionName,
876
+ siblingCounters: new Map()
877
+ };
878
+ this.analyzeFunctionBody(funcPath, funcBodyScopeId, module, allCollections, funcScopeCtx);
879
+ }
880
+ }
881
+ });
882
+ this.profiler.end('traverse_assignments');
883
+
884
+ // Classes
885
+ this.profiler.start('traverse_classes');
886
+ const classVisitor = new ClassVisitor(
887
+ module,
888
+ allCollections,
889
+ this.analyzeFunctionBody.bind(this)
890
+ );
891
+ traverse(ast, classVisitor.getHandlers());
892
+ this.profiler.end('traverse_classes');
893
+
894
+ // TypeScript-specific constructs (interfaces, type aliases, enums)
895
+ this.profiler.start('traverse_typescript');
896
+ const typescriptVisitor = new TypeScriptVisitor(module, allCollections);
897
+ traverse(ast, typescriptVisitor.getHandlers());
898
+ this.profiler.end('traverse_typescript');
899
+
900
+ // Module-level callbacks
901
+ this.profiler.start('traverse_callbacks');
902
+ traverse(ast, {
903
+ FunctionExpression: (funcPath: NodePath<t.FunctionExpression>) => {
904
+ const funcNode = funcPath.node;
905
+ const functionParent = funcPath.getFunctionParent();
906
+ if (functionParent) return;
907
+
908
+ if (funcPath.parent && funcPath.parent.type === 'CallExpression') {
909
+ const funcName = funcNode.id ? funcNode.id.name : this.generateAnonymousName(moduleScopeCtx);
910
+ const functionId = `FUNCTION#${funcName}#${module.file}#${funcNode.loc!.start.line}:${funcNode.loc!.start.column}`;
911
+
912
+ functions.push({
913
+ id: functionId,
914
+ stableId: functionId,
915
+ type: 'FUNCTION',
916
+ name: funcName,
917
+ file: module.file,
918
+ line: funcNode.loc!.start.line,
919
+ column: funcNode.loc!.start.column,
920
+ async: funcNode.async || false,
921
+ generator: funcNode.generator || false,
922
+ isCallback: true,
923
+ parentScopeId: module.id
924
+ });
925
+
926
+ const callbackScopeId = `SCOPE#${funcName}:body#${module.file}#${funcNode.loc!.start.line}`;
927
+ scopes.push({
928
+ id: callbackScopeId,
929
+ type: 'SCOPE',
930
+ scopeType: 'callback_body',
931
+ name: `${funcName}:body`,
932
+ semanticId: `${funcName}:callback_body[0]`,
933
+ conditional: false,
934
+ file: module.file,
935
+ line: funcNode.loc!.start.line,
936
+ parentFunctionId: functionId
937
+ });
938
+
939
+ // Create scope context for analyzing the callback body
940
+ const callbackScopeCtx: ScopeContext = {
941
+ semanticPath: funcName,
942
+ siblingCounters: new Map()
943
+ };
944
+ this.analyzeFunctionBody(funcPath, callbackScopeId, module, allCollections, callbackScopeCtx);
945
+ funcPath.skip();
946
+ }
947
+ }
948
+ });
949
+ this.profiler.end('traverse_callbacks');
950
+
951
+ // Call expressions
952
+ this.profiler.start('traverse_calls');
953
+ const callExpressionVisitor = new CallExpressionVisitor(module, allCollections);
954
+ traverse(ast, callExpressionVisitor.getHandlers());
955
+ this.profiler.end('traverse_calls');
956
+
957
+ // Module-level IfStatements
958
+ this.profiler.start('traverse_ifs');
959
+ traverse(ast, {
960
+ IfStatement: (ifPath: NodePath<t.IfStatement>) => {
961
+ const functionParent = ifPath.getFunctionParent();
962
+ if (functionParent) return;
963
+
964
+ const ifNode = ifPath.node;
965
+ const condition = code.substring(ifNode.test.start!, ifNode.test.end!) || 'condition';
966
+ const counterId = ifScopeCounterRef.value++;
967
+ const ifScopeId = `SCOPE#if#${module.file}#${ifNode.loc!.start.line}:${ifNode.loc!.start.column}:${counterId}`;
968
+
969
+ const constraints = ConditionParser.parse(ifNode.test);
970
+ const ifSemanticId = this.generateSemanticId('if_statement', moduleScopeCtx);
971
+
972
+ scopes.push({
973
+ id: ifScopeId,
974
+ type: 'SCOPE',
975
+ scopeType: 'if_statement',
976
+ name: `if:${ifNode.loc!.start.line}:${ifNode.loc!.start.column}:${counterId}`,
977
+ semanticId: ifSemanticId,
978
+ conditional: true,
979
+ condition,
980
+ constraints: constraints.length > 0 ? constraints : undefined,
981
+ file: module.file,
982
+ line: ifNode.loc!.start.line,
983
+ parentScopeId: module.id
984
+ });
985
+
986
+ if (ifNode.alternate && ifNode.alternate.type !== 'IfStatement') {
987
+ const elseCounterId = ifScopeCounterRef.value++;
988
+ const elseScopeId = `SCOPE#else#${module.file}#${ifNode.alternate.loc!.start.line}:${ifNode.alternate.loc!.start.column}:${elseCounterId}`;
989
+
990
+ const negatedConstraints = constraints.length > 0 ? ConditionParser.negate(constraints) : undefined;
991
+ const elseSemanticId = this.generateSemanticId('else_statement', moduleScopeCtx);
992
+
993
+ scopes.push({
994
+ id: elseScopeId,
995
+ type: 'SCOPE',
996
+ scopeType: 'else_statement',
997
+ name: `else:${ifNode.alternate.loc!.start.line}:${ifNode.alternate.loc!.start.column}:${elseCounterId}`,
998
+ semanticId: elseSemanticId,
999
+ conditional: true,
1000
+ constraints: negatedConstraints,
1001
+ file: module.file,
1002
+ line: ifNode.alternate.loc!.start.line,
1003
+ parentScopeId: module.id
1004
+ });
1005
+ }
1006
+ }
1007
+ });
1008
+ this.profiler.end('traverse_ifs');
1009
+
1010
+ // Build graph
1011
+ this.profiler.start('graph_build');
1012
+ const result = await this.graphBuilder.build(module, graph, projectPath, {
1013
+ functions,
1014
+ scopes,
1015
+ variableDeclarations,
1016
+ callSites,
1017
+ methodCalls,
1018
+ eventListeners,
1019
+ classInstantiations,
1020
+ classDeclarations,
1021
+ methodCallbacks,
1022
+ callArguments,
1023
+ imports,
1024
+ exports,
1025
+ httpRequests,
1026
+ literals,
1027
+ variableAssignments,
1028
+ parameters,
1029
+ // TypeScript-specific collections
1030
+ interfaces,
1031
+ typeAliases,
1032
+ enums,
1033
+ decorators
1034
+ });
1035
+ this.profiler.end('graph_build');
1036
+
1037
+ nodesCreated = result.nodes;
1038
+ edgesCreated = result.edges;
1039
+
1040
+ } catch (error) {
1041
+ const err = error instanceof Error ? error : new Error(String(error));
1042
+ console.error(`[JSASTAnalyzer] Error analyzing ${module.file}:`, err.message);
1043
+ console.error(err.stack);
1044
+ }
1045
+
1046
+ return { nodes: nodesCreated, edges: edgesCreated };
1047
+ }
1048
+
1049
+ /**
1050
+ * Helper to generate semantic ID for a scope and update counters
1051
+ */
1052
+ private generateSemanticId(
1053
+ scopeType: string,
1054
+ scopeCtx: ScopeContext | undefined
1055
+ ): string | undefined {
1056
+ if (!scopeCtx) return undefined;
1057
+
1058
+ const siblingIndex = scopeCtx.siblingCounters.get(scopeType) || 0;
1059
+ scopeCtx.siblingCounters.set(scopeType, siblingIndex + 1);
1060
+ return `${scopeCtx.semanticPath}:${scopeType}[${siblingIndex}]`;
1061
+ }
1062
+
1063
+ /**
1064
+ * Helper to create child scope context from a semantic ID
1065
+ */
1066
+ private createChildScopeContext(semanticId: string | undefined): ScopeContext | undefined {
1067
+ if (!semanticId) return undefined;
1068
+ return {
1069
+ semanticPath: semanticId,
1070
+ siblingCounters: new Map()
1071
+ };
1072
+ }
1073
+
1074
+ /**
1075
+ * Generate a unique anonymous function name within the current scope
1076
+ * Uses scopeCtx.siblingCounters to ensure stability across JS/TS versions
1077
+ */
1078
+ private generateAnonymousName(scopeCtx: ScopeContext | undefined): string {
1079
+ if (!scopeCtx) return 'anonymous';
1080
+ const index = scopeCtx.siblingCounters.get('anonymous') || 0;
1081
+ scopeCtx.siblingCounters.set('anonymous', index + 1);
1082
+ return `anonymous[${index}]`;
1083
+ }
1084
+
1085
+ /**
1086
+ * Анализирует тело функции и извлекает переменные, вызовы, условные блоки
1087
+ */
1088
+ analyzeFunctionBody(
1089
+ funcPath: NodePath<t.Function>,
1090
+ parentScopeId: string,
1091
+ module: VisitorModule,
1092
+ collections: VisitorCollections,
1093
+ scopeCtx?: ScopeContext
1094
+ ): void {
1095
+ // Extract with defaults for optional properties
1096
+ const functions = (collections.functions ?? []) as FunctionInfo[];
1097
+ const scopes = (collections.scopes ?? []) as ScopeInfo[];
1098
+ const variableDeclarations = (collections.variableDeclarations ?? []) as VariableDeclarationInfo[];
1099
+ const callSites = (collections.callSites ?? []) as CallSiteInfo[];
1100
+ const methodCalls = (collections.methodCalls ?? []) as MethodCallInfo[];
1101
+ const eventListeners = (collections.eventListeners ?? []) as EventListenerInfo[];
1102
+ const methodCallbacks = (collections.methodCallbacks ?? []) as MethodCallbackInfo[];
1103
+ const classInstantiations = (collections.classInstantiations ?? []) as ClassInstantiationInfo[];
1104
+ const httpRequests = (collections.httpRequests ?? []) as HttpRequestInfo[];
1105
+ const literals = (collections.literals ?? []) as LiteralInfo[];
1106
+ const variableAssignments = (collections.variableAssignments ?? []) as VariableAssignmentInfo[];
1107
+ const ifScopeCounterRef = (collections.ifScopeCounterRef ?? { value: 0 }) as CounterRef;
1108
+ const scopeCounterRef = (collections.scopeCounterRef ?? { value: 0 }) as CounterRef;
1109
+ const varDeclCounterRef = (collections.varDeclCounterRef ?? { value: 0 }) as CounterRef;
1110
+ const callSiteCounterRef = (collections.callSiteCounterRef ?? { value: 0 }) as CounterRef;
1111
+ const functionCounterRef = (collections.functionCounterRef ?? { value: 0 }) as CounterRef;
1112
+ const httpRequestCounterRef = (collections.httpRequestCounterRef ?? { value: 0 }) as CounterRef;
1113
+ const literalCounterRef = (collections.literalCounterRef ?? { value: 0 }) as CounterRef;
1114
+ const anonymousFunctionCounterRef = (collections.anonymousFunctionCounterRef ?? { value: 0 }) as CounterRef;
1115
+ const processedNodes = collections.processedNodes ?? {
1116
+ functions: new Set<string>(),
1117
+ classes: new Set<string>(),
1118
+ imports: new Set<string>(),
1119
+ exports: new Set<string>(),
1120
+ variables: new Set<string>(),
1121
+ callSites: new Set<string>(),
1122
+ methodCalls: new Set<string>(),
1123
+ varDecls: new Set<string>(),
1124
+ eventListeners: new Set<string>()
1125
+ };
1126
+
1127
+ const parentScopeVariables = new Set<{ name: string; id: string; scopeId: string }>();
1128
+
1129
+ const processedCallSites = processedNodes.callSites;
1130
+ const processedVarDecls = processedNodes.varDecls;
1131
+ const processedMethodCalls = processedNodes.methodCalls;
1132
+ const processedEventListeners = processedNodes.eventListeners;
1133
+
1134
+ funcPath.traverse({
1135
+ VariableDeclaration: (varPath: NodePath<t.VariableDeclaration>) => {
1136
+ const varNode = varPath.node;
1137
+ const isConst = varNode.kind === 'const';
1138
+
1139
+ varNode.declarations.forEach(declarator => {
1140
+ const variables = this.extractVariableNamesFromPattern(declarator.id);
1141
+
1142
+ variables.forEach(varInfo => {
1143
+ const literalValue = declarator.init ? ExpressionEvaluator.extractLiteralValue(declarator.init) : null;
1144
+ const isLiteral = literalValue !== null;
1145
+ const isNewExpression = declarator.init && declarator.init.type === 'NewExpression';
1146
+
1147
+ const shouldBeConstant = isConst && (isLiteral || isNewExpression);
1148
+
1149
+ const varId = shouldBeConstant
1150
+ ? `CONSTANT#${varInfo.name}#${module.file}#${varInfo.loc.start.line}:${varInfo.loc.start.column}:${varDeclCounterRef.value++}`
1151
+ : `VARIABLE#${varInfo.name}#${module.file}#${varInfo.loc.start.line}:${varInfo.loc.start.column}:${varDeclCounterRef.value++}`;
1152
+
1153
+ parentScopeVariables.add({
1154
+ name: varInfo.name,
1155
+ id: varId,
1156
+ scopeId: parentScopeId
1157
+ });
1158
+
1159
+ if (shouldBeConstant) {
1160
+ const constantData: VariableDeclarationInfo = {
1161
+ id: varId,
1162
+ type: 'CONSTANT',
1163
+ name: varInfo.name,
1164
+ file: module.file,
1165
+ line: varInfo.loc.start.line,
1166
+ parentScopeId
1167
+ };
1168
+
1169
+ if (isLiteral) {
1170
+ constantData.value = literalValue;
1171
+ }
1172
+
1173
+ variableDeclarations.push(constantData);
1174
+
1175
+ const init = declarator.init;
1176
+ if (isNewExpression && t.isNewExpression(init) && t.isIdentifier(init.callee)) {
1177
+ const className = init.callee.name;
1178
+ classInstantiations.push({
1179
+ variableId: varId,
1180
+ variableName: varInfo.name,
1181
+ className: className,
1182
+ line: varInfo.loc.start.line,
1183
+ parentScopeId
1184
+ });
1185
+ }
1186
+ } else {
1187
+ variableDeclarations.push({
1188
+ id: varId,
1189
+ type: 'VARIABLE',
1190
+ name: varInfo.name,
1191
+ file: module.file,
1192
+ line: varInfo.loc.start.line,
1193
+ parentScopeId
1194
+ });
1195
+ }
1196
+
1197
+ if (declarator.init) {
1198
+ this.trackVariableAssignment(declarator.init, varId, varInfo.name, module, varInfo.loc.start.line, literals, variableAssignments, literalCounterRef);
1199
+ }
1200
+ });
1201
+ });
1202
+ },
1203
+
1204
+ ForStatement: (forPath: NodePath<t.ForStatement>) => {
1205
+ const forNode = forPath.node;
1206
+ const scopeId = `SCOPE#for-loop#${module.file}#${forNode.loc!.start.line}:${scopeCounterRef.value++}`;
1207
+ const semanticId = this.generateSemanticId('for-loop', scopeCtx);
1208
+ scopes.push({
1209
+ id: scopeId,
1210
+ type: 'SCOPE',
1211
+ scopeType: 'for-loop',
1212
+ semanticId,
1213
+ file: module.file,
1214
+ line: forNode.loc!.start.line,
1215
+ parentScopeId
1216
+ });
1217
+ },
1218
+
1219
+ ForInStatement: (forPath: NodePath<t.ForInStatement>) => {
1220
+ const forNode = forPath.node;
1221
+ const scopeId = `SCOPE#for-in-loop#${module.file}#${forNode.loc!.start.line}:${scopeCounterRef.value++}`;
1222
+ const semanticId = this.generateSemanticId('for-in-loop', scopeCtx);
1223
+ scopes.push({
1224
+ id: scopeId,
1225
+ type: 'SCOPE',
1226
+ scopeType: 'for-in-loop',
1227
+ semanticId,
1228
+ file: module.file,
1229
+ line: forNode.loc!.start.line,
1230
+ parentScopeId
1231
+ });
1232
+ },
1233
+
1234
+ ForOfStatement: (forPath: NodePath<t.ForOfStatement>) => {
1235
+ const forNode = forPath.node;
1236
+ const scopeId = `SCOPE#for-of-loop#${module.file}#${forNode.loc!.start.line}:${scopeCounterRef.value++}`;
1237
+ const semanticId = this.generateSemanticId('for-of-loop', scopeCtx);
1238
+ scopes.push({
1239
+ id: scopeId,
1240
+ type: 'SCOPE',
1241
+ scopeType: 'for-of-loop',
1242
+ semanticId,
1243
+ file: module.file,
1244
+ line: forNode.loc!.start.line,
1245
+ parentScopeId
1246
+ });
1247
+ },
1248
+
1249
+ WhileStatement: (whilePath: NodePath<t.WhileStatement>) => {
1250
+ const whileNode = whilePath.node;
1251
+ const scopeId = `SCOPE#while-loop#${module.file}#${whileNode.loc!.start.line}:${scopeCounterRef.value++}`;
1252
+ const semanticId = this.generateSemanticId('while-loop', scopeCtx);
1253
+ scopes.push({
1254
+ id: scopeId,
1255
+ type: 'SCOPE',
1256
+ scopeType: 'while-loop',
1257
+ semanticId,
1258
+ file: module.file,
1259
+ line: whileNode.loc!.start.line,
1260
+ parentScopeId
1261
+ });
1262
+ },
1263
+
1264
+ DoWhileStatement: (doPath: NodePath<t.DoWhileStatement>) => {
1265
+ const doNode = doPath.node;
1266
+ const scopeId = `SCOPE#do-while-loop#${module.file}#${doNode.loc!.start.line}:${scopeCounterRef.value++}`;
1267
+ const semanticId = this.generateSemanticId('do-while-loop', scopeCtx);
1268
+ scopes.push({
1269
+ id: scopeId,
1270
+ type: 'SCOPE',
1271
+ scopeType: 'do-while-loop',
1272
+ semanticId,
1273
+ file: module.file,
1274
+ line: doNode.loc!.start.line,
1275
+ parentScopeId
1276
+ });
1277
+ },
1278
+
1279
+ TryStatement: (tryPath: NodePath<t.TryStatement>) => {
1280
+ const tryNode = tryPath.node;
1281
+
1282
+ const tryScopeId = `SCOPE#try-block#${module.file}#${tryNode.loc!.start.line}:${scopeCounterRef.value++}`;
1283
+ const trySemanticId = this.generateSemanticId('try-block', scopeCtx);
1284
+ scopes.push({
1285
+ id: tryScopeId,
1286
+ type: 'SCOPE',
1287
+ scopeType: 'try-block',
1288
+ semanticId: trySemanticId,
1289
+ file: module.file,
1290
+ line: tryNode.loc!.start.line,
1291
+ parentScopeId
1292
+ });
1293
+
1294
+ if (tryNode.handler) {
1295
+ const catchBlock = tryNode.handler;
1296
+ const catchScopeId = `SCOPE#catch-block#${module.file}#${catchBlock.loc!.start.line}:${scopeCounterRef.value++}`;
1297
+ const catchSemanticId = this.generateSemanticId('catch-block', scopeCtx);
1298
+
1299
+ scopes.push({
1300
+ id: catchScopeId,
1301
+ type: 'SCOPE',
1302
+ scopeType: 'catch-block',
1303
+ semanticId: catchSemanticId,
1304
+ file: module.file,
1305
+ line: catchBlock.loc!.start.line,
1306
+ parentScopeId
1307
+ });
1308
+
1309
+ if (catchBlock.param) {
1310
+ const errorVarInfo = this.extractVariableNamesFromPattern(catchBlock.param);
1311
+
1312
+ errorVarInfo.forEach(varInfo => {
1313
+ const varId = `VARIABLE#${varInfo.name}#${module.file}#${varInfo.loc.start.line}:${varInfo.loc.start.column}:${varDeclCounterRef.value++}`;
1314
+
1315
+ variableDeclarations.push({
1316
+ id: varId,
1317
+ type: 'VARIABLE',
1318
+ name: varInfo.name,
1319
+ file: module.file,
1320
+ line: varInfo.loc.start.line,
1321
+ parentScopeId: catchScopeId
1322
+ });
1323
+ });
1324
+ }
1325
+ }
1326
+
1327
+ if (tryNode.finalizer) {
1328
+ const finallyScopeId = `SCOPE#finally-block#${module.file}#${tryNode.finalizer.loc!.start.line}:${scopeCounterRef.value++}`;
1329
+ const finallySemanticId = this.generateSemanticId('finally-block', scopeCtx);
1330
+
1331
+ scopes.push({
1332
+ id: finallyScopeId,
1333
+ type: 'SCOPE',
1334
+ scopeType: 'finally-block',
1335
+ semanticId: finallySemanticId,
1336
+ file: module.file,
1337
+ line: tryNode.finalizer.loc!.start.line,
1338
+ parentScopeId
1339
+ });
1340
+ }
1341
+ },
1342
+
1343
+ SwitchStatement: (switchPath: NodePath<t.SwitchStatement>) => {
1344
+ const switchNode = switchPath.node;
1345
+ const scopeId = `SCOPE#switch-case#${module.file}#${switchNode.loc!.start.line}:${scopeCounterRef.value++}`;
1346
+ const semanticId = this.generateSemanticId('switch-case', scopeCtx);
1347
+
1348
+ scopes.push({
1349
+ id: scopeId,
1350
+ type: 'SCOPE',
1351
+ scopeType: 'switch-case',
1352
+ semanticId,
1353
+ file: module.file,
1354
+ line: switchNode.loc!.start.line,
1355
+ parentScopeId
1356
+ });
1357
+ },
1358
+
1359
+ FunctionExpression: (funcPath: NodePath<t.FunctionExpression>) => {
1360
+ const node = funcPath.node;
1361
+ const funcName = node.id ? node.id.name : this.generateAnonymousName(scopeCtx);
1362
+ const functionId = `FUNCTION#${funcName}#${module.file}#${node.loc!.start.line}:${node.loc!.start.column}:${functionCounterRef.value++}`;
1363
+
1364
+ functions.push({
1365
+ id: functionId,
1366
+ stableId: functionId,
1367
+ type: 'FUNCTION',
1368
+ name: funcName,
1369
+ file: module.file,
1370
+ line: node.loc!.start.line,
1371
+ column: node.loc!.start.column,
1372
+ async: node.async || false,
1373
+ generator: node.generator || false,
1374
+ parentScopeId
1375
+ });
1376
+
1377
+ const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${node.loc!.start.line}`;
1378
+ const closureSemanticId = this.generateSemanticId('closure', scopeCtx);
1379
+ scopes.push({
1380
+ id: nestedScopeId,
1381
+ type: 'SCOPE',
1382
+ scopeType: 'closure',
1383
+ name: `${funcName}:body`,
1384
+ semanticId: closureSemanticId,
1385
+ conditional: false,
1386
+ file: module.file,
1387
+ line: node.loc!.start.line,
1388
+ parentFunctionId: functionId,
1389
+ capturesFrom: parentScopeId
1390
+ });
1391
+
1392
+ // For nested function, create new context with function name as semantic path
1393
+ const nestedFuncCtx: ScopeContext = {
1394
+ semanticPath: scopeCtx ? `${scopeCtx.semanticPath}.${funcName}` : funcName,
1395
+ siblingCounters: new Map()
1396
+ };
1397
+ this.analyzeFunctionBody(funcPath, nestedScopeId, module, collections, nestedFuncCtx);
1398
+ funcPath.skip();
1399
+ },
1400
+
1401
+ ArrowFunctionExpression: (arrowPath: NodePath<t.ArrowFunctionExpression>) => {
1402
+ const node = arrowPath.node;
1403
+ const line = node.loc!.start.line;
1404
+ const column = node.loc!.start.column;
1405
+
1406
+ // Определяем имя (anonymous если не присвоено переменной)
1407
+ const parent = arrowPath.parent;
1408
+ let funcName: string;
1409
+ if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id)) {
1410
+ funcName = parent.id.name;
1411
+ } else {
1412
+ // Используем scope-level счётчик для стабильного semanticId
1413
+ funcName = this.generateAnonymousName(scopeCtx);
1414
+ }
1415
+
1416
+ const functionId = `FUNCTION#${funcName}:${line}:${column}:${functionCounterRef.value++}`;
1417
+
1418
+ functions.push({
1419
+ id: functionId,
1420
+ stableId: functionId,
1421
+ type: 'FUNCTION',
1422
+ name: funcName,
1423
+ file: module.file,
1424
+ line,
1425
+ column,
1426
+ async: node.async || false,
1427
+ arrowFunction: true,
1428
+ parentScopeId
1429
+ });
1430
+
1431
+ if (node.body.type === 'BlockStatement') {
1432
+ const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${line}`;
1433
+ const arrowSemanticId = this.generateSemanticId('arrow_body', scopeCtx);
1434
+ scopes.push({
1435
+ id: nestedScopeId,
1436
+ type: 'SCOPE',
1437
+ scopeType: 'arrow_body',
1438
+ name: `${funcName}:body`,
1439
+ semanticId: arrowSemanticId,
1440
+ conditional: false,
1441
+ file: module.file,
1442
+ line,
1443
+ parentFunctionId: functionId,
1444
+ capturesFrom: parentScopeId
1445
+ });
1446
+
1447
+ // For arrow function, create new context with function name as semantic path
1448
+ const arrowFuncCtx: ScopeContext = {
1449
+ semanticPath: scopeCtx ? `${scopeCtx.semanticPath}.${funcName}` : funcName,
1450
+ siblingCounters: new Map()
1451
+ };
1452
+ this.analyzeFunctionBody(arrowPath, nestedScopeId, module, collections, arrowFuncCtx);
1453
+ }
1454
+
1455
+ arrowPath.skip();
1456
+ },
1457
+
1458
+ UpdateExpression: (updatePath: NodePath<t.UpdateExpression>) => {
1459
+ const updateNode = updatePath.node;
1460
+ if (updateNode.argument.type === 'Identifier') {
1461
+ const varName = updateNode.argument.name;
1462
+
1463
+ // Find variable by name - could be from parent scope or declarations
1464
+ const fromParentScope = Array.from(parentScopeVariables).find(v => v.name === varName);
1465
+ const fromDeclarations = variableDeclarations.find(v => v.name === varName);
1466
+ const variable = fromParentScope ?? fromDeclarations;
1467
+
1468
+ if (variable) {
1469
+ const scope = scopes.find(s => s.id === parentScopeId);
1470
+ if (scope) {
1471
+ if (!scope.modifies) scope.modifies = [];
1472
+ scope.modifies.push({
1473
+ variableId: variable.id,
1474
+ variableName: varName,
1475
+ line: updateNode.loc!.start.line
1476
+ });
1477
+ }
1478
+ }
1479
+ }
1480
+ },
1481
+
1482
+ // IF statements - создаём условные scope и обходим содержимое для CALL узлов
1483
+ IfStatement: (ifPath: NodePath<t.IfStatement>) => {
1484
+ const ifNode = ifPath.node;
1485
+ const sourceCode = collections.code ?? '';
1486
+ const condition = sourceCode.substring(ifNode.test.start!, ifNode.test.end!) || 'condition';
1487
+ const counterId = ifScopeCounterRef.value++;
1488
+ const ifScopeId = `SCOPE#if#${module.file}#${ifNode.loc!.start.line}:${ifNode.loc!.start.column}:${counterId}`;
1489
+
1490
+ // Parse condition to extract constraints
1491
+ const constraints = ConditionParser.parse(ifNode.test);
1492
+ const ifSemanticId = this.generateSemanticId('if_statement', scopeCtx);
1493
+
1494
+ scopes.push({
1495
+ id: ifScopeId,
1496
+ type: 'SCOPE',
1497
+ scopeType: 'if_statement',
1498
+ name: `if:${ifNode.loc!.start.line}:${ifNode.loc!.start.column}:${counterId}`,
1499
+ semanticId: ifSemanticId,
1500
+ conditional: true,
1501
+ condition,
1502
+ constraints: constraints.length > 0 ? constraints : undefined,
1503
+ file: module.file,
1504
+ line: ifNode.loc!.start.line,
1505
+ parentScopeId
1506
+ });
1507
+
1508
+ // Обходим содержимое if-блока (consequent)
1509
+ ifPath.get('consequent').traverse({
1510
+ CallExpression: (callPath: NodePath<t.CallExpression>) => {
1511
+ const callNode = callPath.node;
1512
+ if (callNode.callee.type === 'Identifier') {
1513
+ const nodeKey = `${callNode.start}:${callNode.end}`;
1514
+ if (processedCallSites.has(nodeKey)) {
1515
+ return;
1516
+ }
1517
+ processedCallSites.add(nodeKey);
1518
+
1519
+ callSites.push({
1520
+ id: `CALL#${callNode.callee.name}#${module.file}#${callNode.loc!.start.line}:${callNode.loc!.start.column}:${callSiteCounterRef.value++}`,
1521
+ type: 'CALL',
1522
+ name: callNode.callee.name,
1523
+ file: module.file,
1524
+ line: callNode.loc!.start.line,
1525
+ parentScopeId: ifScopeId,
1526
+ targetFunctionName: callNode.callee.name
1527
+ });
1528
+ }
1529
+ },
1530
+ NewExpression: (newPath: NodePath<t.NewExpression>) => {
1531
+ const newNode = newPath.node;
1532
+ if (newNode.callee.type === 'Identifier') {
1533
+ const nodeKey = `new:${newNode.start}:${newNode.end}`;
1534
+ if (processedCallSites.has(nodeKey)) {
1535
+ return;
1536
+ }
1537
+ processedCallSites.add(nodeKey);
1538
+
1539
+ callSites.push({
1540
+ id: `CALL#new:${newNode.callee.name}#${module.file}#${newNode.loc!.start.line}:${newNode.loc!.start.column}:${callSiteCounterRef.value++}`,
1541
+ type: 'CALL',
1542
+ name: newNode.callee.name,
1543
+ file: module.file,
1544
+ line: newNode.loc!.start.line,
1545
+ parentScopeId: ifScopeId,
1546
+ targetFunctionName: newNode.callee.name,
1547
+ isNew: true
1548
+ });
1549
+ }
1550
+ }
1551
+ });
1552
+
1553
+ // Handle else branch if present
1554
+ if (ifNode.alternate) {
1555
+ const elseCounterId = ifScopeCounterRef.value++;
1556
+ const elseScopeId = `SCOPE#else#${module.file}#${ifNode.alternate.loc!.start.line}:${ifNode.alternate.loc!.start.column}:${elseCounterId}`;
1557
+
1558
+ // Negate constraints for else branch
1559
+ const negatedConstraints = constraints.length > 0 ? ConditionParser.negate(constraints) : undefined;
1560
+ const elseSemanticId = this.generateSemanticId('else_statement', scopeCtx);
1561
+
1562
+ scopes.push({
1563
+ id: elseScopeId,
1564
+ type: 'SCOPE',
1565
+ scopeType: 'else_statement',
1566
+ name: `else:${ifNode.alternate.loc!.start.line}:${ifNode.alternate.loc!.start.column}:${elseCounterId}`,
1567
+ semanticId: elseSemanticId,
1568
+ conditional: true,
1569
+ constraints: negatedConstraints,
1570
+ file: module.file,
1571
+ line: ifNode.alternate.loc!.start.line,
1572
+ parentScopeId
1573
+ });
1574
+
1575
+ // Traverse else block
1576
+ ifPath.get('alternate').traverse({
1577
+ CallExpression: (callPath: NodePath<t.CallExpression>) => {
1578
+ const callNode = callPath.node;
1579
+ if (callNode.callee.type === 'Identifier') {
1580
+ const nodeKey = `${callNode.start}:${callNode.end}`;
1581
+ if (processedCallSites.has(nodeKey)) {
1582
+ return;
1583
+ }
1584
+ processedCallSites.add(nodeKey);
1585
+
1586
+ callSites.push({
1587
+ id: `CALL#${callNode.callee.name}#${module.file}#${callNode.loc!.start.line}:${callNode.loc!.start.column}:${callSiteCounterRef.value++}`,
1588
+ type: 'CALL',
1589
+ name: callNode.callee.name,
1590
+ file: module.file,
1591
+ line: callNode.loc!.start.line,
1592
+ parentScopeId: elseScopeId,
1593
+ targetFunctionName: callNode.callee.name
1594
+ });
1595
+ }
1596
+ },
1597
+ NewExpression: (newPath: NodePath<t.NewExpression>) => {
1598
+ const newNode = newPath.node;
1599
+ if (newNode.callee.type === 'Identifier') {
1600
+ const nodeKey = `new:${newNode.start}:${newNode.end}`;
1601
+ if (processedCallSites.has(nodeKey)) {
1602
+ return;
1603
+ }
1604
+ processedCallSites.add(nodeKey);
1605
+
1606
+ callSites.push({
1607
+ id: `CALL#new:${newNode.callee.name}#${module.file}#${newNode.loc!.start.line}:${newNode.loc!.start.column}:${callSiteCounterRef.value++}`,
1608
+ type: 'CALL',
1609
+ name: newNode.callee.name,
1610
+ file: module.file,
1611
+ line: newNode.loc!.start.line,
1612
+ parentScopeId: elseScopeId,
1613
+ targetFunctionName: newNode.callee.name,
1614
+ isNew: true
1615
+ });
1616
+ }
1617
+ }
1618
+ });
1619
+ }
1620
+
1621
+ // Останавливаем дальнейший обход, чтобы не обрабатывать вызовы дважды
1622
+ ifPath.skip();
1623
+ },
1624
+
1625
+ // Вызовы функций на безусловном уровне (вне if/for/while)
1626
+ CallExpression: (callPath: NodePath<t.CallExpression>) => {
1627
+ // Проверяем что вызов не внутри if/for/while (их мы обрабатываем отдельно)
1628
+ const parent = callPath.parent;
1629
+ if (parent.type !== 'IfStatement' && parent.type !== 'ForStatement' && parent.type !== 'WhileStatement') {
1630
+ const callNode = callPath.node;
1631
+
1632
+ // Обычные вызовы функций (greet(), main())
1633
+ if (callNode.callee.type === 'Identifier') {
1634
+ const nodeKey = `${callNode.start}:${callNode.end}`;
1635
+ if (processedCallSites.has(nodeKey)) {
1636
+ return;
1637
+ }
1638
+ processedCallSites.add(nodeKey);
1639
+
1640
+ callSites.push({
1641
+ id: `CALL#${callNode.callee.name}#${module.file}#${callNode.loc!.start.line}:${callNode.loc!.start.column}:${callSiteCounterRef.value++}`,
1642
+ type: 'CALL',
1643
+ name: callNode.callee.name,
1644
+ file: module.file,
1645
+ line: callNode.loc!.start.line,
1646
+ parentScopeId,
1647
+ targetFunctionName: callNode.callee.name
1648
+ });
1649
+ }
1650
+ }
1651
+ },
1652
+
1653
+ // NewExpression на безусловном уровне
1654
+ NewExpression: (newPath: NodePath<t.NewExpression>) => {
1655
+ const parent = newPath.parent;
1656
+ if (parent.type !== 'IfStatement' && parent.type !== 'ForStatement' && parent.type !== 'WhileStatement') {
1657
+ const newNode = newPath.node;
1658
+ if (newNode.callee.type === 'Identifier') {
1659
+ const nodeKey = `new:${newNode.start}:${newNode.end}`;
1660
+ if (processedCallSites.has(nodeKey)) {
1661
+ return;
1662
+ }
1663
+ processedCallSites.add(nodeKey);
1664
+
1665
+ callSites.push({
1666
+ id: `CALL#new:${newNode.callee.name}#${module.file}#${newNode.loc!.start.line}:${newNode.loc!.start.column}:${callSiteCounterRef.value++}`,
1667
+ type: 'CALL',
1668
+ name: newNode.callee.name,
1669
+ file: module.file,
1670
+ line: newNode.loc!.start.line,
1671
+ parentScopeId,
1672
+ targetFunctionName: newNode.callee.name,
1673
+ isNew: true
1674
+ });
1675
+ }
1676
+ }
1677
+ }
1678
+ });
1679
+ }
1680
+ }