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