@grafema/core 0.2.5-beta → 0.2.7

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 (769) hide show
  1. package/README.md +61 -23
  2. package/dist/DiscoveryManager.d.ts +59 -0
  3. package/dist/DiscoveryManager.d.ts.map +1 -0
  4. package/dist/DiscoveryManager.js +249 -0
  5. package/dist/DiscoveryManager.js.map +1 -0
  6. package/dist/GraphInitializer.d.ts +44 -0
  7. package/dist/GraphInitializer.d.ts.map +1 -0
  8. package/dist/GraphInitializer.js +121 -0
  9. package/dist/GraphInitializer.js.map +1 -0
  10. package/dist/GuaranteeChecker.d.ts +35 -0
  11. package/dist/GuaranteeChecker.d.ts.map +1 -0
  12. package/dist/GuaranteeChecker.js +81 -0
  13. package/dist/GuaranteeChecker.js.map +1 -0
  14. package/dist/Orchestrator.d.ts +34 -151
  15. package/dist/Orchestrator.d.ts.map +1 -1
  16. package/dist/Orchestrator.js +173 -741
  17. package/dist/Orchestrator.js.map +1 -1
  18. package/dist/OrchestratorTypes.d.ts +115 -0
  19. package/dist/OrchestratorTypes.d.ts.map +1 -0
  20. package/dist/OrchestratorTypes.js +6 -0
  21. package/dist/OrchestratorTypes.js.map +1 -0
  22. package/dist/ParallelAnalysisRunner.d.ts +43 -0
  23. package/dist/ParallelAnalysisRunner.d.ts.map +1 -0
  24. package/dist/ParallelAnalysisRunner.js +161 -0
  25. package/dist/ParallelAnalysisRunner.js.map +1 -0
  26. package/dist/PhaseRunner.d.ts +94 -0
  27. package/dist/PhaseRunner.d.ts.map +1 -0
  28. package/dist/PhaseRunner.js +332 -0
  29. package/dist/PhaseRunner.js.map +1 -0
  30. package/dist/config/ConfigLoader.d.ts +51 -1
  31. package/dist/config/ConfigLoader.d.ts.map +1 -1
  32. package/dist/config/ConfigLoader.js +121 -1
  33. package/dist/config/ConfigLoader.js.map +1 -1
  34. package/dist/config/index.d.ts +1 -1
  35. package/dist/config/index.d.ts.map +1 -1
  36. package/dist/config/index.js +1 -1
  37. package/dist/config/index.js.map +1 -1
  38. package/dist/core/ASTWorker.d.ts +2 -0
  39. package/dist/core/ASTWorker.d.ts.map +1 -1
  40. package/dist/core/ASTWorker.js +6 -2
  41. package/dist/core/ASTWorker.js.map +1 -1
  42. package/dist/core/FileOverview.d.ts +124 -0
  43. package/dist/core/FileOverview.d.ts.map +1 -0
  44. package/dist/core/FileOverview.js +257 -0
  45. package/dist/core/FileOverview.js.map +1 -0
  46. package/dist/core/GraphFreshnessChecker.d.ts +1 -1
  47. package/dist/core/GraphFreshnessChecker.d.ts.map +1 -1
  48. package/dist/core/GraphFreshnessChecker.js +7 -5
  49. package/dist/core/GraphFreshnessChecker.js.map +1 -1
  50. package/dist/core/GuaranteeManager.d.ts +13 -0
  51. package/dist/core/GuaranteeManager.d.ts.map +1 -1
  52. package/dist/core/GuaranteeManager.js +63 -2
  53. package/dist/core/GuaranteeManager.js.map +1 -1
  54. package/dist/core/IncrementalReanalyzer.d.ts.map +1 -1
  55. package/dist/core/IncrementalReanalyzer.js +6 -3
  56. package/dist/core/IncrementalReanalyzer.js.map +1 -1
  57. package/dist/core/NodeFactory.d.ts +81 -415
  58. package/dist/core/NodeFactory.d.ts.map +1 -1
  59. package/dist/core/NodeFactory.js +139 -336
  60. package/dist/core/NodeFactory.js.map +1 -1
  61. package/dist/core/ResourceRegistry.d.ts +17 -0
  62. package/dist/core/ResourceRegistry.d.ts.map +1 -0
  63. package/dist/core/ResourceRegistry.js +32 -0
  64. package/dist/core/ResourceRegistry.js.map +1 -0
  65. package/dist/core/ScopeTracker.d.ts +12 -0
  66. package/dist/core/ScopeTracker.d.ts.map +1 -1
  67. package/dist/core/ScopeTracker.js +24 -0
  68. package/dist/core/ScopeTracker.js.map +1 -1
  69. package/dist/core/SemanticId.d.ts +69 -0
  70. package/dist/core/SemanticId.d.ts.map +1 -1
  71. package/dist/core/SemanticId.js +134 -0
  72. package/dist/core/SemanticId.js.map +1 -1
  73. package/dist/core/brandNodeInternal.d.ts +14 -0
  74. package/dist/core/brandNodeInternal.d.ts.map +1 -0
  75. package/dist/core/brandNodeInternal.js +4 -0
  76. package/dist/core/brandNodeInternal.js.map +1 -0
  77. package/dist/core/buildDependencyGraph.d.ts +36 -0
  78. package/dist/core/buildDependencyGraph.d.ts.map +1 -0
  79. package/dist/core/buildDependencyGraph.js +78 -0
  80. package/dist/core/buildDependencyGraph.js.map +1 -0
  81. package/dist/core/factories/CoreFactory.d.ts +223 -0
  82. package/dist/core/factories/CoreFactory.d.ts.map +1 -0
  83. package/dist/core/factories/CoreFactory.js +127 -0
  84. package/dist/core/factories/CoreFactory.js.map +1 -0
  85. package/dist/core/factories/DatabaseFactory.d.ts +29 -0
  86. package/dist/core/factories/DatabaseFactory.d.ts.map +1 -0
  87. package/dist/core/factories/DatabaseFactory.js +25 -0
  88. package/dist/core/factories/DatabaseFactory.js.map +1 -0
  89. package/dist/core/factories/ExternalFactory.d.ts +11 -0
  90. package/dist/core/factories/ExternalFactory.d.ts.map +1 -0
  91. package/dist/core/factories/ExternalFactory.js +16 -0
  92. package/dist/core/factories/ExternalFactory.js.map +1 -0
  93. package/dist/core/factories/HttpFactory.d.ts +22 -0
  94. package/dist/core/factories/HttpFactory.d.ts.map +1 -0
  95. package/dist/core/factories/HttpFactory.js +32 -0
  96. package/dist/core/factories/HttpFactory.js.map +1 -0
  97. package/dist/core/factories/ReactFactory.d.ts +14 -0
  98. package/dist/core/factories/ReactFactory.d.ts.map +1 -0
  99. package/dist/core/factories/ReactFactory.js +13 -0
  100. package/dist/core/factories/ReactFactory.js.map +1 -0
  101. package/dist/core/factories/RustFactory.d.ts +62 -0
  102. package/dist/core/factories/RustFactory.d.ts.map +1 -0
  103. package/dist/core/factories/RustFactory.js +32 -0
  104. package/dist/core/factories/RustFactory.js.map +1 -0
  105. package/dist/core/factories/ServiceFactory.d.ts +12 -0
  106. package/dist/core/factories/ServiceFactory.d.ts.map +1 -0
  107. package/dist/core/factories/ServiceFactory.js +22 -0
  108. package/dist/core/factories/ServiceFactory.js.map +1 -0
  109. package/dist/core/factories/SocketFactory.d.ts +31 -0
  110. package/dist/core/factories/SocketFactory.d.ts.map +1 -0
  111. package/dist/core/factories/SocketFactory.js +35 -0
  112. package/dist/core/factories/SocketFactory.js.map +1 -0
  113. package/dist/core/nodes/DatabaseNode.d.ts +85 -0
  114. package/dist/core/nodes/DatabaseNode.d.ts.map +1 -0
  115. package/dist/core/nodes/DatabaseNode.js +118 -0
  116. package/dist/core/nodes/DatabaseNode.js.map +1 -0
  117. package/dist/core/nodes/ExpressMiddlewareNode.d.ts +47 -0
  118. package/dist/core/nodes/ExpressMiddlewareNode.d.ts.map +1 -0
  119. package/dist/core/nodes/ExpressMiddlewareNode.js +63 -0
  120. package/dist/core/nodes/ExpressMiddlewareNode.js.map +1 -0
  121. package/dist/core/nodes/ExpressMountNode.d.ts +44 -0
  122. package/dist/core/nodes/ExpressMountNode.d.ts.map +1 -0
  123. package/dist/core/nodes/ExpressMountNode.js +61 -0
  124. package/dist/core/nodes/ExpressMountNode.js.map +1 -0
  125. package/dist/core/nodes/ExternalApiNode.d.ts +29 -0
  126. package/dist/core/nodes/ExternalApiNode.d.ts.map +1 -0
  127. package/dist/core/nodes/ExternalApiNode.js +41 -0
  128. package/dist/core/nodes/ExternalApiNode.js.map +1 -0
  129. package/dist/core/nodes/ExternalFunctionNode.d.ts +40 -0
  130. package/dist/core/nodes/ExternalFunctionNode.d.ts.map +1 -0
  131. package/dist/core/nodes/ExternalFunctionNode.js +54 -0
  132. package/dist/core/nodes/ExternalFunctionNode.js.map +1 -0
  133. package/dist/core/nodes/FetchRequestNode.d.ts +54 -0
  134. package/dist/core/nodes/FetchRequestNode.d.ts.map +1 -0
  135. package/dist/core/nodes/FetchRequestNode.js +67 -0
  136. package/dist/core/nodes/FetchRequestNode.js.map +1 -0
  137. package/dist/core/nodes/HttpRouteNode.d.ts +58 -0
  138. package/dist/core/nodes/HttpRouteNode.d.ts.map +1 -0
  139. package/dist/core/nodes/HttpRouteNode.js +72 -0
  140. package/dist/core/nodes/HttpRouteNode.js.map +1 -0
  141. package/dist/core/nodes/NodeKind.d.ts +1 -0
  142. package/dist/core/nodes/NodeKind.d.ts.map +1 -1
  143. package/dist/core/nodes/NodeKind.js +1 -0
  144. package/dist/core/nodes/NodeKind.js.map +1 -1
  145. package/dist/core/nodes/ReactNode.d.ts +53 -0
  146. package/dist/core/nodes/ReactNode.d.ts.map +1 -0
  147. package/dist/core/nodes/ReactNode.js +70 -0
  148. package/dist/core/nodes/ReactNode.js.map +1 -0
  149. package/dist/core/nodes/RustCallNode.d.ts +46 -0
  150. package/dist/core/nodes/RustCallNode.d.ts.map +1 -0
  151. package/dist/core/nodes/RustCallNode.js +62 -0
  152. package/dist/core/nodes/RustCallNode.js.map +1 -0
  153. package/dist/core/nodes/RustFunctionNode.d.ts +58 -0
  154. package/dist/core/nodes/RustFunctionNode.d.ts.map +1 -0
  155. package/dist/core/nodes/RustFunctionNode.js +67 -0
  156. package/dist/core/nodes/RustFunctionNode.js.map +1 -0
  157. package/dist/core/nodes/RustImplNode.d.ts +35 -0
  158. package/dist/core/nodes/RustImplNode.d.ts.map +1 -0
  159. package/dist/core/nodes/RustImplNode.js +55 -0
  160. package/dist/core/nodes/RustImplNode.js.map +1 -0
  161. package/dist/core/nodes/RustMethodNode.d.ts +64 -0
  162. package/dist/core/nodes/RustMethodNode.d.ts.map +1 -0
  163. package/dist/core/nodes/RustMethodNode.js +76 -0
  164. package/dist/core/nodes/RustMethodNode.js.map +1 -0
  165. package/dist/core/nodes/RustModuleNode.d.ts +40 -0
  166. package/dist/core/nodes/RustModuleNode.d.ts.map +1 -0
  167. package/dist/core/nodes/RustModuleNode.js +57 -0
  168. package/dist/core/nodes/RustModuleNode.js.map +1 -0
  169. package/dist/core/nodes/RustStructNode.d.ts +38 -0
  170. package/dist/core/nodes/RustStructNode.d.ts.map +1 -0
  171. package/dist/core/nodes/RustStructNode.js +54 -0
  172. package/dist/core/nodes/RustStructNode.js.map +1 -0
  173. package/dist/core/nodes/RustTraitNode.d.ts +40 -0
  174. package/dist/core/nodes/RustTraitNode.d.ts.map +1 -0
  175. package/dist/core/nodes/RustTraitNode.js +52 -0
  176. package/dist/core/nodes/RustTraitNode.js.map +1 -0
  177. package/dist/core/nodes/ServiceLayerNode.d.ts +85 -0
  178. package/dist/core/nodes/ServiceLayerNode.d.ts.map +1 -0
  179. package/dist/core/nodes/ServiceLayerNode.js +122 -0
  180. package/dist/core/nodes/ServiceLayerNode.js.map +1 -0
  181. package/dist/core/nodes/SocketIONode.d.ts +71 -0
  182. package/dist/core/nodes/SocketIONode.d.ts.map +1 -0
  183. package/dist/core/nodes/SocketIONode.js +111 -0
  184. package/dist/core/nodes/SocketIONode.js.map +1 -0
  185. package/dist/core/nodes/SocketNode.d.ts +87 -0
  186. package/dist/core/nodes/SocketNode.d.ts.map +1 -0
  187. package/dist/core/nodes/SocketNode.js +124 -0
  188. package/dist/core/nodes/SocketNode.js.map +1 -0
  189. package/dist/core/nodes/TypeNode.d.ts +26 -1
  190. package/dist/core/nodes/TypeNode.d.ts.map +1 -1
  191. package/dist/core/nodes/TypeNode.js +21 -3
  192. package/dist/core/nodes/TypeNode.js.map +1 -1
  193. package/dist/core/nodes/TypeParameterNode.d.ts +44 -0
  194. package/dist/core/nodes/TypeParameterNode.d.ts.map +1 -0
  195. package/dist/core/nodes/TypeParameterNode.js +64 -0
  196. package/dist/core/nodes/TypeParameterNode.js.map +1 -0
  197. package/dist/core/nodes/index.d.ts +19 -0
  198. package/dist/core/nodes/index.d.ts.map +1 -1
  199. package/dist/core/nodes/index.js +26 -0
  200. package/dist/core/nodes/index.js.map +1 -1
  201. package/dist/index.d.ts +33 -6
  202. package/dist/index.d.ts.map +1 -1
  203. package/dist/index.js +30 -3
  204. package/dist/index.js.map +1 -1
  205. package/dist/plugins/InfraAnalyzer.d.ts +110 -0
  206. package/dist/plugins/InfraAnalyzer.d.ts.map +1 -0
  207. package/dist/plugins/InfraAnalyzer.js +170 -0
  208. package/dist/plugins/InfraAnalyzer.js.map +1 -0
  209. package/dist/plugins/analysis/DatabaseAnalyzer.d.ts.map +1 -1
  210. package/dist/plugins/analysis/DatabaseAnalyzer.js +18 -15
  211. package/dist/plugins/analysis/DatabaseAnalyzer.js.map +1 -1
  212. package/dist/plugins/analysis/ExpressAnalyzer.d.ts.map +1 -1
  213. package/dist/plugins/analysis/ExpressAnalyzer.js +27 -26
  214. package/dist/plugins/analysis/ExpressAnalyzer.js.map +1 -1
  215. package/dist/plugins/analysis/ExpressResponseAnalyzer.d.ts.map +1 -1
  216. package/dist/plugins/analysis/ExpressResponseAnalyzer.js +5 -3
  217. package/dist/plugins/analysis/ExpressResponseAnalyzer.js.map +1 -1
  218. package/dist/plugins/analysis/ExpressRouteAnalyzer.d.ts.map +1 -1
  219. package/dist/plugins/analysis/ExpressRouteAnalyzer.js +36 -39
  220. package/dist/plugins/analysis/ExpressRouteAnalyzer.js.map +1 -1
  221. package/dist/plugins/analysis/FetchAnalyzer.d.ts.map +1 -1
  222. package/dist/plugins/analysis/FetchAnalyzer.js +23 -39
  223. package/dist/plugins/analysis/FetchAnalyzer.js.map +1 -1
  224. package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts.map +1 -1
  225. package/dist/plugins/analysis/IncrementalAnalysisPlugin.js +3 -2
  226. package/dist/plugins/analysis/IncrementalAnalysisPlugin.js.map +1 -1
  227. package/dist/plugins/analysis/JSASTAnalyzer.d.ts +23 -85
  228. package/dist/plugins/analysis/JSASTAnalyzer.d.ts.map +1 -1
  229. package/dist/plugins/analysis/JSASTAnalyzer.js +351 -1887
  230. package/dist/plugins/analysis/JSASTAnalyzer.js.map +1 -1
  231. package/dist/plugins/analysis/NestJSRouteAnalyzer.d.ts +28 -0
  232. package/dist/plugins/analysis/NestJSRouteAnalyzer.d.ts.map +1 -0
  233. package/dist/plugins/analysis/NestJSRouteAnalyzer.js +196 -0
  234. package/dist/plugins/analysis/NestJSRouteAnalyzer.js.map +1 -0
  235. package/dist/plugins/analysis/ReactAnalyzer.d.ts +1 -61
  236. package/dist/plugins/analysis/ReactAnalyzer.d.ts.map +1 -1
  237. package/dist/plugins/analysis/ReactAnalyzer.js +24 -915
  238. package/dist/plugins/analysis/ReactAnalyzer.js.map +1 -1
  239. package/dist/plugins/analysis/RustAnalyzer.d.ts.map +1 -1
  240. package/dist/plugins/analysis/RustAnalyzer.js +31 -66
  241. package/dist/plugins/analysis/RustAnalyzer.js.map +1 -1
  242. package/dist/plugins/analysis/SQLiteAnalyzer.d.ts.map +1 -1
  243. package/dist/plugins/analysis/SQLiteAnalyzer.js +13 -6
  244. package/dist/plugins/analysis/SQLiteAnalyzer.js.map +1 -1
  245. package/dist/plugins/analysis/ServiceLayerAnalyzer.d.ts.map +1 -1
  246. package/dist/plugins/analysis/ServiceLayerAnalyzer.js +10 -7
  247. package/dist/plugins/analysis/ServiceLayerAnalyzer.js.map +1 -1
  248. package/dist/plugins/analysis/SocketAnalyzer.d.ts +81 -0
  249. package/dist/plugins/analysis/SocketAnalyzer.d.ts.map +1 -0
  250. package/dist/plugins/analysis/SocketAnalyzer.js +475 -0
  251. package/dist/plugins/analysis/SocketAnalyzer.js.map +1 -0
  252. package/dist/plugins/analysis/SocketIOAnalyzer.d.ts.map +1 -1
  253. package/dist/plugins/analysis/SocketIOAnalyzer.js +13 -18
  254. package/dist/plugins/analysis/SocketIOAnalyzer.js.map +1 -1
  255. package/dist/plugins/analysis/SystemDbAnalyzer.d.ts.map +1 -1
  256. package/dist/plugins/analysis/SystemDbAnalyzer.js +8 -5
  257. package/dist/plugins/analysis/SystemDbAnalyzer.js.map +1 -1
  258. package/dist/plugins/analysis/ast/CollisionResolver.d.ts +65 -0
  259. package/dist/plugins/analysis/ast/CollisionResolver.d.ts.map +1 -0
  260. package/dist/plugins/analysis/ast/CollisionResolver.js +106 -0
  261. package/dist/plugins/analysis/ast/CollisionResolver.js.map +1 -0
  262. package/dist/plugins/analysis/ast/FunctionBodyContext.d.ts +124 -0
  263. package/dist/plugins/analysis/ast/FunctionBodyContext.d.ts.map +1 -0
  264. package/dist/plugins/analysis/ast/FunctionBodyContext.js +151 -0
  265. package/dist/plugins/analysis/ast/FunctionBodyContext.js.map +1 -0
  266. package/dist/plugins/analysis/ast/GraphBuilder.d.ts +26 -261
  267. package/dist/plugins/analysis/ast/GraphBuilder.d.ts.map +1 -1
  268. package/dist/plugins/analysis/ast/GraphBuilder.js +251 -2494
  269. package/dist/plugins/analysis/ast/GraphBuilder.js.map +1 -1
  270. package/dist/plugins/analysis/ast/IdGenerator.d.ts +42 -0
  271. package/dist/plugins/analysis/ast/IdGenerator.d.ts.map +1 -1
  272. package/dist/plugins/analysis/ast/IdGenerator.js +61 -1
  273. package/dist/plugins/analysis/ast/IdGenerator.js.map +1 -1
  274. package/dist/plugins/analysis/ast/builders/AssignmentBuilder.d.ts +15 -0
  275. package/dist/plugins/analysis/ast/builders/AssignmentBuilder.d.ts.map +1 -0
  276. package/dist/plugins/analysis/ast/builders/AssignmentBuilder.js +274 -0
  277. package/dist/plugins/analysis/ast/builders/AssignmentBuilder.js.map +1 -0
  278. package/dist/plugins/analysis/ast/builders/CallFlowBuilder.d.ts +22 -0
  279. package/dist/plugins/analysis/ast/builders/CallFlowBuilder.d.ts.map +1 -0
  280. package/dist/plugins/analysis/ast/builders/CallFlowBuilder.js +178 -0
  281. package/dist/plugins/analysis/ast/builders/CallFlowBuilder.js.map +1 -0
  282. package/dist/plugins/analysis/ast/builders/ControlFlowBuilder.d.ts +76 -0
  283. package/dist/plugins/analysis/ast/builders/ControlFlowBuilder.d.ts.map +1 -0
  284. package/dist/plugins/analysis/ast/builders/ControlFlowBuilder.js +387 -0
  285. package/dist/plugins/analysis/ast/builders/ControlFlowBuilder.js.map +1 -0
  286. package/dist/plugins/analysis/ast/builders/CoreBuilder.d.ts +38 -0
  287. package/dist/plugins/analysis/ast/builders/CoreBuilder.d.ts.map +1 -0
  288. package/dist/plugins/analysis/ast/builders/CoreBuilder.js +240 -0
  289. package/dist/plugins/analysis/ast/builders/CoreBuilder.js.map +1 -0
  290. package/dist/plugins/analysis/ast/builders/ModuleRuntimeBuilder.d.ts +53 -0
  291. package/dist/plugins/analysis/ast/builders/ModuleRuntimeBuilder.d.ts.map +1 -0
  292. package/dist/plugins/analysis/ast/builders/ModuleRuntimeBuilder.js +355 -0
  293. package/dist/plugins/analysis/ast/builders/ModuleRuntimeBuilder.js.map +1 -0
  294. package/dist/plugins/analysis/ast/builders/MutationBuilder.d.ts +46 -0
  295. package/dist/plugins/analysis/ast/builders/MutationBuilder.d.ts.map +1 -0
  296. package/dist/plugins/analysis/ast/builders/MutationBuilder.js +264 -0
  297. package/dist/plugins/analysis/ast/builders/MutationBuilder.js.map +1 -0
  298. package/dist/plugins/analysis/ast/builders/ReturnBuilder.d.ts +23 -0
  299. package/dist/plugins/analysis/ast/builders/ReturnBuilder.d.ts.map +1 -0
  300. package/dist/plugins/analysis/ast/builders/ReturnBuilder.js +206 -0
  301. package/dist/plugins/analysis/ast/builders/ReturnBuilder.js.map +1 -0
  302. package/dist/plugins/analysis/ast/builders/TypeSystemBuilder.d.ts +64 -0
  303. package/dist/plugins/analysis/ast/builders/TypeSystemBuilder.d.ts.map +1 -0
  304. package/dist/plugins/analysis/ast/builders/TypeSystemBuilder.js +370 -0
  305. package/dist/plugins/analysis/ast/builders/TypeSystemBuilder.js.map +1 -0
  306. package/dist/plugins/analysis/ast/builders/UpdateExpressionBuilder.d.ts +46 -0
  307. package/dist/plugins/analysis/ast/builders/UpdateExpressionBuilder.d.ts.map +1 -0
  308. package/dist/plugins/analysis/ast/builders/UpdateExpressionBuilder.js +191 -0
  309. package/dist/plugins/analysis/ast/builders/UpdateExpressionBuilder.js.map +1 -0
  310. package/dist/plugins/analysis/ast/builders/YieldBuilder.d.ts +30 -0
  311. package/dist/plugins/analysis/ast/builders/YieldBuilder.d.ts.map +1 -0
  312. package/dist/plugins/analysis/ast/builders/YieldBuilder.js +214 -0
  313. package/dist/plugins/analysis/ast/builders/YieldBuilder.js.map +1 -0
  314. package/dist/plugins/analysis/ast/builders/index.d.ts +12 -0
  315. package/dist/plugins/analysis/ast/builders/index.d.ts.map +1 -0
  316. package/dist/plugins/analysis/ast/builders/index.js +11 -0
  317. package/dist/plugins/analysis/ast/builders/index.js.map +1 -0
  318. package/dist/plugins/analysis/ast/builders/types.d.ts +30 -0
  319. package/dist/plugins/analysis/ast/builders/types.d.ts.map +1 -0
  320. package/dist/plugins/analysis/ast/builders/types.js +8 -0
  321. package/dist/plugins/analysis/ast/builders/types.js.map +1 -0
  322. package/dist/plugins/analysis/ast/handlers/AnalyzerDelegate.d.ts +50 -0
  323. package/dist/plugins/analysis/ast/handlers/AnalyzerDelegate.d.ts.map +1 -0
  324. package/dist/plugins/analysis/ast/handlers/AnalyzerDelegate.js +2 -0
  325. package/dist/plugins/analysis/ast/handlers/AnalyzerDelegate.js.map +1 -0
  326. package/dist/plugins/analysis/ast/handlers/BranchHandler.d.ts +18 -0
  327. package/dist/plugins/analysis/ast/handlers/BranchHandler.d.ts.map +1 -0
  328. package/dist/plugins/analysis/ast/handlers/BranchHandler.js +244 -0
  329. package/dist/plugins/analysis/ast/handlers/BranchHandler.js.map +1 -0
  330. package/dist/plugins/analysis/ast/handlers/CallExpressionHandler.d.ts +7 -0
  331. package/dist/plugins/analysis/ast/handlers/CallExpressionHandler.d.ts.map +1 -0
  332. package/dist/plugins/analysis/ast/handlers/CallExpressionHandler.js +295 -0
  333. package/dist/plugins/analysis/ast/handlers/CallExpressionHandler.js.map +1 -0
  334. package/dist/plugins/analysis/ast/handlers/FunctionBodyHandler.d.ts +22 -0
  335. package/dist/plugins/analysis/ast/handlers/FunctionBodyHandler.d.ts.map +1 -0
  336. package/dist/plugins/analysis/ast/handlers/FunctionBodyHandler.js +9 -0
  337. package/dist/plugins/analysis/ast/handlers/FunctionBodyHandler.js.map +1 -0
  338. package/dist/plugins/analysis/ast/handlers/LoopHandler.d.ts +13 -0
  339. package/dist/plugins/analysis/ast/handlers/LoopHandler.d.ts.map +1 -0
  340. package/dist/plugins/analysis/ast/handlers/LoopHandler.js +207 -0
  341. package/dist/plugins/analysis/ast/handlers/LoopHandler.js.map +1 -0
  342. package/dist/plugins/analysis/ast/handlers/NestedFunctionHandler.d.ts +13 -0
  343. package/dist/plugins/analysis/ast/handlers/NestedFunctionHandler.d.ts.map +1 -0
  344. package/dist/plugins/analysis/ast/handlers/NestedFunctionHandler.js +174 -0
  345. package/dist/plugins/analysis/ast/handlers/NestedFunctionHandler.js.map +1 -0
  346. package/dist/plugins/analysis/ast/handlers/NewExpressionHandler.d.ts +12 -0
  347. package/dist/plugins/analysis/ast/handlers/NewExpressionHandler.d.ts.map +1 -0
  348. package/dist/plugins/analysis/ast/handlers/NewExpressionHandler.js +135 -0
  349. package/dist/plugins/analysis/ast/handlers/NewExpressionHandler.js.map +1 -0
  350. package/dist/plugins/analysis/ast/handlers/PropertyAccessHandler.d.ts +13 -0
  351. package/dist/plugins/analysis/ast/handlers/PropertyAccessHandler.d.ts.map +1 -0
  352. package/dist/plugins/analysis/ast/handlers/PropertyAccessHandler.js +71 -0
  353. package/dist/plugins/analysis/ast/handlers/PropertyAccessHandler.js.map +1 -0
  354. package/dist/plugins/analysis/ast/handlers/ReturnYieldHandler.d.ts +12 -0
  355. package/dist/plugins/analysis/ast/handlers/ReturnYieldHandler.d.ts.map +1 -0
  356. package/dist/plugins/analysis/ast/handlers/ReturnYieldHandler.js +135 -0
  357. package/dist/plugins/analysis/ast/handlers/ReturnYieldHandler.js.map +1 -0
  358. package/dist/plugins/analysis/ast/handlers/ThrowHandler.d.ts +12 -0
  359. package/dist/plugins/analysis/ast/handlers/ThrowHandler.d.ts.map +1 -0
  360. package/dist/plugins/analysis/ast/handlers/ThrowHandler.js +82 -0
  361. package/dist/plugins/analysis/ast/handlers/ThrowHandler.js.map +1 -0
  362. package/dist/plugins/analysis/ast/handlers/TryCatchHandler.d.ts +14 -0
  363. package/dist/plugins/analysis/ast/handlers/TryCatchHandler.d.ts.map +1 -0
  364. package/dist/plugins/analysis/ast/handlers/TryCatchHandler.js +220 -0
  365. package/dist/plugins/analysis/ast/handlers/TryCatchHandler.js.map +1 -0
  366. package/dist/plugins/analysis/ast/handlers/VariableHandler.d.ts +12 -0
  367. package/dist/plugins/analysis/ast/handlers/VariableHandler.d.ts.map +1 -0
  368. package/dist/plugins/analysis/ast/handlers/VariableHandler.js +57 -0
  369. package/dist/plugins/analysis/ast/handlers/VariableHandler.js.map +1 -0
  370. package/dist/plugins/analysis/ast/handlers/index.d.ts +13 -0
  371. package/dist/plugins/analysis/ast/handlers/index.d.ts.map +1 -0
  372. package/dist/plugins/analysis/ast/handlers/index.js +12 -0
  373. package/dist/plugins/analysis/ast/handlers/index.js.map +1 -0
  374. package/dist/plugins/analysis/ast/types.d.ts +57 -6
  375. package/dist/plugins/analysis/ast/types.d.ts.map +1 -1
  376. package/dist/plugins/analysis/ast/utils/createParameterNodes.d.ts +5 -4
  377. package/dist/plugins/analysis/ast/utils/createParameterNodes.d.ts.map +1 -1
  378. package/dist/plugins/analysis/ast/utils/createParameterNodes.js +94 -13
  379. package/dist/plugins/analysis/ast/utils/createParameterNodes.js.map +1 -1
  380. package/dist/plugins/analysis/ast/utils/extractNamesFromPattern.d.ts +81 -0
  381. package/dist/plugins/analysis/ast/utils/extractNamesFromPattern.d.ts.map +1 -0
  382. package/dist/plugins/analysis/ast/utils/extractNamesFromPattern.js +140 -0
  383. package/dist/plugins/analysis/ast/utils/extractNamesFromPattern.js.map +1 -0
  384. package/dist/plugins/analysis/ast/utils/getExpressionValue.d.ts +22 -0
  385. package/dist/plugins/analysis/ast/utils/getExpressionValue.d.ts.map +1 -0
  386. package/dist/plugins/analysis/ast/utils/getExpressionValue.js +35 -0
  387. package/dist/plugins/analysis/ast/utils/getExpressionValue.js.map +1 -0
  388. package/dist/plugins/analysis/ast/utils/getMemberExpressionName.d.ts +25 -0
  389. package/dist/plugins/analysis/ast/utils/getMemberExpressionName.d.ts.map +1 -0
  390. package/dist/plugins/analysis/ast/utils/getMemberExpressionName.js +21 -0
  391. package/dist/plugins/analysis/ast/utils/getMemberExpressionName.js.map +1 -0
  392. package/dist/plugins/analysis/ast/utils/index.d.ts +2 -0
  393. package/dist/plugins/analysis/ast/utils/index.d.ts.map +1 -1
  394. package/dist/plugins/analysis/ast/utils/index.js +2 -0
  395. package/dist/plugins/analysis/ast/utils/index.js.map +1 -1
  396. package/dist/plugins/analysis/ast/visitors/ArgumentExtractor.d.ts +23 -0
  397. package/dist/plugins/analysis/ast/visitors/ArgumentExtractor.d.ts.map +1 -0
  398. package/dist/plugins/analysis/ast/visitors/ArgumentExtractor.js +241 -0
  399. package/dist/plugins/analysis/ast/visitors/ArgumentExtractor.js.map +1 -0
  400. package/dist/plugins/analysis/ast/visitors/ArrayElementExtractor.d.ts +20 -0
  401. package/dist/plugins/analysis/ast/visitors/ArrayElementExtractor.d.ts.map +1 -0
  402. package/dist/plugins/analysis/ast/visitors/ArrayElementExtractor.js +110 -0
  403. package/dist/plugins/analysis/ast/visitors/ArrayElementExtractor.js.map +1 -0
  404. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts +15 -142
  405. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts.map +1 -1
  406. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js +304 -937
  407. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js.map +1 -1
  408. package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts.map +1 -1
  409. package/dist/plugins/analysis/ast/visitors/ClassVisitor.js +26 -11
  410. package/dist/plugins/analysis/ast/visitors/ClassVisitor.js.map +1 -1
  411. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts.map +1 -1
  412. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js +21 -6
  413. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js.map +1 -1
  414. package/dist/plugins/analysis/ast/visitors/MutationDetector.d.ts +25 -0
  415. package/dist/plugins/analysis/ast/visitors/MutationDetector.d.ts.map +1 -0
  416. package/dist/plugins/analysis/ast/visitors/MutationDetector.js +181 -0
  417. package/dist/plugins/analysis/ast/visitors/MutationDetector.js.map +1 -0
  418. package/dist/plugins/analysis/ast/visitors/ObjectPropertyExtractor.d.ts +20 -0
  419. package/dist/plugins/analysis/ast/visitors/ObjectPropertyExtractor.d.ts.map +1 -0
  420. package/dist/plugins/analysis/ast/visitors/ObjectPropertyExtractor.js +155 -0
  421. package/dist/plugins/analysis/ast/visitors/ObjectPropertyExtractor.js.map +1 -0
  422. package/dist/plugins/analysis/ast/visitors/PropertyAccessVisitor.d.ts +9 -1
  423. package/dist/plugins/analysis/ast/visitors/PropertyAccessVisitor.d.ts.map +1 -1
  424. package/dist/plugins/analysis/ast/visitors/PropertyAccessVisitor.js +51 -3
  425. package/dist/plugins/analysis/ast/visitors/PropertyAccessVisitor.js.map +1 -1
  426. package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts +20 -0
  427. package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts.map +1 -1
  428. package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.js +188 -12
  429. package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.js.map +1 -1
  430. package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts.map +1 -1
  431. package/dist/plugins/analysis/ast/visitors/VariableVisitor.js +6 -4
  432. package/dist/plugins/analysis/ast/visitors/VariableVisitor.js.map +1 -1
  433. package/dist/plugins/analysis/ast/visitors/call-expression-helpers.d.ts +19 -0
  434. package/dist/plugins/analysis/ast/visitors/call-expression-helpers.d.ts.map +1 -0
  435. package/dist/plugins/analysis/ast/visitors/call-expression-helpers.js +57 -0
  436. package/dist/plugins/analysis/ast/visitors/call-expression-helpers.js.map +1 -0
  437. package/dist/plugins/analysis/ast/visitors/call-expression-types.d.ts +168 -0
  438. package/dist/plugins/analysis/ast/visitors/call-expression-types.d.ts.map +1 -0
  439. package/dist/plugins/analysis/ast/visitors/call-expression-types.js +7 -0
  440. package/dist/plugins/analysis/ast/visitors/call-expression-types.js.map +1 -0
  441. package/dist/plugins/analysis/react-internal/browser-api.d.ts +20 -0
  442. package/dist/plugins/analysis/react-internal/browser-api.d.ts.map +1 -0
  443. package/dist/plugins/analysis/react-internal/browser-api.js +140 -0
  444. package/dist/plugins/analysis/react-internal/browser-api.js.map +1 -0
  445. package/dist/plugins/analysis/react-internal/hooks.d.ts +31 -0
  446. package/dist/plugins/analysis/react-internal/hooks.d.ts.map +1 -0
  447. package/dist/plugins/analysis/react-internal/hooks.js +465 -0
  448. package/dist/plugins/analysis/react-internal/hooks.js.map +1 -0
  449. package/dist/plugins/analysis/react-internal/jsx.d.ts +43 -0
  450. package/dist/plugins/analysis/react-internal/jsx.d.ts.map +1 -0
  451. package/dist/plugins/analysis/react-internal/jsx.js +231 -0
  452. package/dist/plugins/analysis/react-internal/jsx.js.map +1 -0
  453. package/dist/plugins/analysis/react-internal/types.d.ts +116 -0
  454. package/dist/plugins/analysis/react-internal/types.d.ts.map +1 -0
  455. package/dist/plugins/analysis/react-internal/types.js +83 -0
  456. package/dist/plugins/analysis/react-internal/types.js.map +1 -0
  457. package/dist/plugins/discovery/MonorepoServiceDiscovery.d.ts.map +1 -1
  458. package/dist/plugins/discovery/MonorepoServiceDiscovery.js +6 -13
  459. package/dist/plugins/discovery/MonorepoServiceDiscovery.js.map +1 -1
  460. package/dist/plugins/enrichment/AliasTracker.d.ts.map +1 -1
  461. package/dist/plugins/enrichment/AliasTracker.js +3 -1
  462. package/dist/plugins/enrichment/AliasTracker.js.map +1 -1
  463. package/dist/plugins/enrichment/ArgumentParameterLinker.d.ts.map +1 -1
  464. package/dist/plugins/enrichment/ArgumentParameterLinker.js +3 -1
  465. package/dist/plugins/enrichment/ArgumentParameterLinker.js.map +1 -1
  466. package/dist/plugins/enrichment/CallbackCallResolver.d.ts +42 -0
  467. package/dist/plugins/enrichment/CallbackCallResolver.d.ts.map +1 -0
  468. package/dist/plugins/enrichment/CallbackCallResolver.js +311 -0
  469. package/dist/plugins/enrichment/CallbackCallResolver.js.map +1 -0
  470. package/dist/plugins/enrichment/ClosureCaptureEnricher.d.ts.map +1 -1
  471. package/dist/plugins/enrichment/ClosureCaptureEnricher.js +3 -1
  472. package/dist/plugins/enrichment/ClosureCaptureEnricher.js.map +1 -1
  473. package/dist/plugins/enrichment/ConfigRoutingMapBuilder.d.ts +17 -0
  474. package/dist/plugins/enrichment/ConfigRoutingMapBuilder.d.ts.map +1 -0
  475. package/dist/plugins/enrichment/ConfigRoutingMapBuilder.js +55 -0
  476. package/dist/plugins/enrichment/ConfigRoutingMapBuilder.js.map +1 -0
  477. package/dist/plugins/enrichment/ExpressHandlerLinker.d.ts.map +1 -1
  478. package/dist/plugins/enrichment/ExpressHandlerLinker.js +3 -1
  479. package/dist/plugins/enrichment/ExpressHandlerLinker.js.map +1 -1
  480. package/dist/plugins/enrichment/ExternalCallResolver.d.ts.map +1 -1
  481. package/dist/plugins/enrichment/ExternalCallResolver.js +5 -8
  482. package/dist/plugins/enrichment/ExternalCallResolver.js.map +1 -1
  483. package/dist/plugins/enrichment/FunctionCallResolver.d.ts.map +1 -1
  484. package/dist/plugins/enrichment/FunctionCallResolver.js +6 -9
  485. package/dist/plugins/enrichment/FunctionCallResolver.js.map +1 -1
  486. package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts.map +1 -1
  487. package/dist/plugins/enrichment/HTTPConnectionEnricher.js +3 -1
  488. package/dist/plugins/enrichment/HTTPConnectionEnricher.js.map +1 -1
  489. package/dist/plugins/enrichment/ImportExportLinker.d.ts.map +1 -1
  490. package/dist/plugins/enrichment/ImportExportLinker.js +5 -3
  491. package/dist/plugins/enrichment/ImportExportLinker.js.map +1 -1
  492. package/dist/plugins/enrichment/InstanceOfResolver.d.ts.map +1 -1
  493. package/dist/plugins/enrichment/InstanceOfResolver.js +3 -1
  494. package/dist/plugins/enrichment/InstanceOfResolver.js.map +1 -1
  495. package/dist/plugins/enrichment/MethodCallResolver.d.ts +17 -68
  496. package/dist/plugins/enrichment/MethodCallResolver.d.ts.map +1 -1
  497. package/dist/plugins/enrichment/MethodCallResolver.js +42 -517
  498. package/dist/plugins/enrichment/MethodCallResolver.js.map +1 -1
  499. package/dist/plugins/enrichment/MountPointResolver.d.ts.map +1 -1
  500. package/dist/plugins/enrichment/MountPointResolver.js +9 -2
  501. package/dist/plugins/enrichment/MountPointResolver.js.map +1 -1
  502. package/dist/plugins/enrichment/NodejsBuiltinsResolver.d.ts.map +1 -1
  503. package/dist/plugins/enrichment/NodejsBuiltinsResolver.js +7 -16
  504. package/dist/plugins/enrichment/NodejsBuiltinsResolver.js.map +1 -1
  505. package/dist/plugins/enrichment/PrefixEvaluator.d.ts.map +1 -1
  506. package/dist/plugins/enrichment/PrefixEvaluator.js +6 -2
  507. package/dist/plugins/enrichment/PrefixEvaluator.js.map +1 -1
  508. package/dist/plugins/enrichment/RejectionPropagationEnricher.d.ts.map +1 -1
  509. package/dist/plugins/enrichment/RejectionPropagationEnricher.js +3 -1
  510. package/dist/plugins/enrichment/RejectionPropagationEnricher.js.map +1 -1
  511. package/dist/plugins/enrichment/RustFFIEnricher.d.ts.map +1 -1
  512. package/dist/plugins/enrichment/RustFFIEnricher.js +3 -1
  513. package/dist/plugins/enrichment/RustFFIEnricher.js.map +1 -1
  514. package/dist/plugins/enrichment/ServiceConnectionEnricher.d.ts +76 -0
  515. package/dist/plugins/enrichment/ServiceConnectionEnricher.d.ts.map +1 -0
  516. package/dist/plugins/enrichment/ServiceConnectionEnricher.js +355 -0
  517. package/dist/plugins/enrichment/ServiceConnectionEnricher.js.map +1 -0
  518. package/dist/plugins/enrichment/SocketConnectionEnricher.d.ts +42 -0
  519. package/dist/plugins/enrichment/SocketConnectionEnricher.d.ts.map +1 -0
  520. package/dist/plugins/enrichment/SocketConnectionEnricher.js +166 -0
  521. package/dist/plugins/enrichment/SocketConnectionEnricher.js.map +1 -0
  522. package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts.map +1 -1
  523. package/dist/plugins/enrichment/ValueDomainAnalyzer.js +3 -1
  524. package/dist/plugins/enrichment/ValueDomainAnalyzer.js.map +1 -1
  525. package/dist/plugins/enrichment/method-call/MethodCallData.d.ts +68 -0
  526. package/dist/plugins/enrichment/method-call/MethodCallData.d.ts.map +1 -0
  527. package/dist/plugins/enrichment/method-call/MethodCallData.js +227 -0
  528. package/dist/plugins/enrichment/method-call/MethodCallData.js.map +1 -0
  529. package/dist/plugins/enrichment/method-call/MethodCallDetectors.d.ts +21 -0
  530. package/dist/plugins/enrichment/method-call/MethodCallDetectors.d.ts.map +1 -0
  531. package/dist/plugins/enrichment/method-call/MethodCallDetectors.js +52 -0
  532. package/dist/plugins/enrichment/method-call/MethodCallDetectors.js.map +1 -0
  533. package/dist/plugins/enrichment/method-call/MethodCallErrorAnalysis.d.ts +22 -0
  534. package/dist/plugins/enrichment/method-call/MethodCallErrorAnalysis.d.ts.map +1 -0
  535. package/dist/plugins/enrichment/method-call/MethodCallErrorAnalysis.js +105 -0
  536. package/dist/plugins/enrichment/method-call/MethodCallErrorAnalysis.js.map +1 -0
  537. package/dist/plugins/enrichment/method-call/MethodCallIndexers.d.ts +19 -0
  538. package/dist/plugins/enrichment/method-call/MethodCallIndexers.d.ts.map +1 -0
  539. package/dist/plugins/enrichment/method-call/MethodCallIndexers.js +63 -0
  540. package/dist/plugins/enrichment/method-call/MethodCallIndexers.js.map +1 -0
  541. package/dist/plugins/enrichment/method-call/MethodCallResolution.d.ts +30 -0
  542. package/dist/plugins/enrichment/method-call/MethodCallResolution.d.ts.map +1 -0
  543. package/dist/plugins/enrichment/method-call/MethodCallResolution.js +138 -0
  544. package/dist/plugins/enrichment/method-call/MethodCallResolution.js.map +1 -0
  545. package/dist/plugins/indexing/IncrementalModuleIndexer.d.ts.map +1 -1
  546. package/dist/plugins/indexing/IncrementalModuleIndexer.js +2 -8
  547. package/dist/plugins/indexing/IncrementalModuleIndexer.js.map +1 -1
  548. package/dist/plugins/indexing/JSModuleIndexer.d.ts.map +1 -1
  549. package/dist/plugins/indexing/JSModuleIndexer.js +13 -20
  550. package/dist/plugins/indexing/JSModuleIndexer.js.map +1 -1
  551. package/dist/plugins/indexing/RustModuleIndexer.d.ts.map +1 -1
  552. package/dist/plugins/indexing/RustModuleIndexer.js +4 -8
  553. package/dist/plugins/indexing/RustModuleIndexer.js.map +1 -1
  554. package/dist/plugins/validation/AwaitInLoopValidator.d.ts +24 -0
  555. package/dist/plugins/validation/AwaitInLoopValidator.d.ts.map +1 -0
  556. package/dist/plugins/validation/AwaitInLoopValidator.js +69 -0
  557. package/dist/plugins/validation/AwaitInLoopValidator.js.map +1 -0
  558. package/dist/plugins/validation/PackageCoverageValidator.d.ts +33 -0
  559. package/dist/plugins/validation/PackageCoverageValidator.d.ts.map +1 -0
  560. package/dist/plugins/validation/PackageCoverageValidator.js +149 -0
  561. package/dist/plugins/validation/PackageCoverageValidator.js.map +1 -0
  562. package/dist/plugins/validation/UnconnectedRouteValidator.d.ts +18 -0
  563. package/dist/plugins/validation/UnconnectedRouteValidator.d.ts.map +1 -0
  564. package/dist/plugins/validation/UnconnectedRouteValidator.js +68 -0
  565. package/dist/plugins/validation/UnconnectedRouteValidator.js.map +1 -0
  566. package/dist/queries/NodeContext.d.ts +81 -0
  567. package/dist/queries/NodeContext.d.ts.map +1 -0
  568. package/dist/queries/NodeContext.js +193 -0
  569. package/dist/queries/NodeContext.js.map +1 -0
  570. package/dist/queries/findCallsInFunction.d.ts.map +1 -1
  571. package/dist/queries/findCallsInFunction.js +10 -2
  572. package/dist/queries/findCallsInFunction.js.map +1 -1
  573. package/dist/queries/findContainingFunction.d.ts +3 -2
  574. package/dist/queries/findContainingFunction.d.ts.map +1 -1
  575. package/dist/queries/findContainingFunction.js +13 -3
  576. package/dist/queries/findContainingFunction.js.map +1 -1
  577. package/dist/queries/index.d.ts +2 -0
  578. package/dist/queries/index.d.ts.map +1 -1
  579. package/dist/queries/index.js +1 -0
  580. package/dist/queries/index.js.map +1 -1
  581. package/dist/resources/InfraResourceMapImpl.d.ts +31 -0
  582. package/dist/resources/InfraResourceMapImpl.d.ts.map +1 -0
  583. package/dist/resources/InfraResourceMapImpl.js +110 -0
  584. package/dist/resources/InfraResourceMapImpl.js.map +1 -0
  585. package/dist/resources/RoutingMapImpl.d.ts +33 -0
  586. package/dist/resources/RoutingMapImpl.d.ts.map +1 -0
  587. package/dist/resources/RoutingMapImpl.js +115 -0
  588. package/dist/resources/RoutingMapImpl.js.map +1 -0
  589. package/dist/storage/backends/RFDBServerBackend.d.ts +35 -6
  590. package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -1
  591. package/dist/storage/backends/RFDBServerBackend.js +102 -70
  592. package/dist/storage/backends/RFDBServerBackend.js.map +1 -1
  593. package/dist/utils/findRfdbBinary.d.ts +3 -2
  594. package/dist/utils/findRfdbBinary.d.ts.map +1 -1
  595. package/dist/utils/findRfdbBinary.js +22 -7
  596. package/dist/utils/findRfdbBinary.js.map +1 -1
  597. package/dist/utils/moduleResolution.d.ts.map +1 -1
  598. package/dist/utils/moduleResolution.js +26 -1
  599. package/dist/utils/moduleResolution.js.map +1 -1
  600. package/dist/utils/resolveNodeFile.d.ts +13 -0
  601. package/dist/utils/resolveNodeFile.d.ts.map +1 -0
  602. package/dist/utils/resolveNodeFile.js +18 -0
  603. package/dist/utils/resolveNodeFile.js.map +1 -0
  604. package/dist/version.d.ts +11 -0
  605. package/dist/version.d.ts.map +1 -0
  606. package/dist/version.js +26 -0
  607. package/dist/version.js.map +1 -0
  608. package/package.json +3 -3
  609. package/src/DiscoveryManager.ts +279 -0
  610. package/src/GraphInitializer.ts +131 -0
  611. package/src/GuaranteeChecker.ts +90 -0
  612. package/src/Orchestrator.ts +222 -963
  613. package/src/OrchestratorTypes.ts +122 -0
  614. package/src/ParallelAnalysisRunner.ts +188 -0
  615. package/src/PhaseRunner.ts +450 -0
  616. package/src/config/ConfigLoader.ts +176 -2
  617. package/src/config/index.ts +2 -0
  618. package/src/core/ASTWorker.ts +9 -2
  619. package/src/core/FileOverview.ts +374 -0
  620. package/src/core/GraphFreshnessChecker.ts +7 -5
  621. package/src/core/GuaranteeManager.ts +70 -2
  622. package/src/core/IncrementalReanalyzer.ts +6 -3
  623. package/src/core/NodeFactory.ts +173 -652
  624. package/src/core/ResourceRegistry.ts +39 -0
  625. package/src/core/ScopeTracker.ts +23 -0
  626. package/src/core/SemanticId.ts +183 -0
  627. package/src/core/brandNodeInternal.ts +16 -0
  628. package/src/core/buildDependencyGraph.ts +98 -0
  629. package/src/core/factories/CoreFactory.ts +489 -0
  630. package/src/core/factories/DatabaseFactory.ts +63 -0
  631. package/src/core/factories/ExternalFactory.ts +23 -0
  632. package/src/core/factories/HttpFactory.ts +57 -0
  633. package/src/core/factories/ReactFactory.ts +15 -0
  634. package/src/core/factories/RustFactory.ts +128 -0
  635. package/src/core/factories/ServiceFactory.ts +27 -0
  636. package/src/core/factories/SocketFactory.ts +94 -0
  637. package/src/core/nodes/DatabaseNode.ts +175 -0
  638. package/src/core/nodes/ExpressMiddlewareNode.ts +98 -0
  639. package/src/core/nodes/ExpressMountNode.ts +94 -0
  640. package/src/core/nodes/ExternalApiNode.ts +53 -0
  641. package/src/core/nodes/ExternalFunctionNode.ts +77 -0
  642. package/src/core/nodes/FetchRequestNode.ts +105 -0
  643. package/src/core/nodes/HttpRouteNode.ts +113 -0
  644. package/src/core/nodes/NodeKind.ts +1 -0
  645. package/src/core/nodes/ReactNode.ts +78 -0
  646. package/src/core/nodes/RustCallNode.ts +96 -0
  647. package/src/core/nodes/RustFunctionNode.ts +112 -0
  648. package/src/core/nodes/RustImplNode.ts +78 -0
  649. package/src/core/nodes/RustMethodNode.ts +125 -0
  650. package/src/core/nodes/RustModuleNode.ts +84 -0
  651. package/src/core/nodes/RustStructNode.ts +80 -0
  652. package/src/core/nodes/RustTraitNode.ts +82 -0
  653. package/src/core/nodes/ServiceLayerNode.ts +183 -0
  654. package/src/core/nodes/SocketIONode.ts +177 -0
  655. package/src/core/nodes/SocketNode.ts +206 -0
  656. package/src/core/nodes/TypeNode.ts +46 -3
  657. package/src/core/nodes/TypeParameterNode.ts +91 -0
  658. package/src/core/nodes/index.ts +57 -0
  659. package/src/index.ts +60 -4
  660. package/src/plugins/InfraAnalyzer.ts +208 -0
  661. package/src/plugins/analysis/DatabaseAnalyzer.ts +27 -17
  662. package/src/plugins/analysis/ExpressAnalyzer.ts +51 -38
  663. package/src/plugins/analysis/ExpressResponseAnalyzer.ts +15 -12
  664. package/src/plugins/analysis/ExpressRouteAnalyzer.ts +56 -56
  665. package/src/plugins/analysis/FetchAnalyzer.ts +42 -52
  666. package/src/plugins/analysis/IncrementalAnalysisPlugin.ts +3 -2
  667. package/src/plugins/analysis/JSASTAnalyzer.ts +391 -2304
  668. package/src/plugins/analysis/NestJSRouteAnalyzer.ts +241 -0
  669. package/src/plugins/analysis/ReactAnalyzer.ts +33 -1085
  670. package/src/plugins/analysis/RustAnalyzer.ts +112 -116
  671. package/src/plugins/analysis/SQLiteAnalyzer.ts +23 -9
  672. package/src/plugins/analysis/ServiceLayerAnalyzer.ts +32 -10
  673. package/src/plugins/analysis/SocketAnalyzer.ts +601 -0
  674. package/src/plugins/analysis/SocketIOAnalyzer.ts +25 -34
  675. package/src/plugins/analysis/SystemDbAnalyzer.ts +15 -12
  676. package/src/plugins/analysis/ast/CollisionResolver.ts +137 -0
  677. package/src/plugins/analysis/ast/FunctionBodyContext.ts +291 -0
  678. package/src/plugins/analysis/ast/GraphBuilder.ts +274 -3180
  679. package/src/plugins/analysis/ast/IdGenerator.ts +81 -1
  680. package/src/plugins/analysis/ast/builders/AssignmentBuilder.ts +407 -0
  681. package/src/plugins/analysis/ast/builders/CallFlowBuilder.ts +255 -0
  682. package/src/plugins/analysis/ast/builders/ControlFlowBuilder.ts +470 -0
  683. package/src/plugins/analysis/ast/builders/CoreBuilder.ts +306 -0
  684. package/src/plugins/analysis/ast/builders/ModuleRuntimeBuilder.ts +452 -0
  685. package/src/plugins/analysis/ast/builders/MutationBuilder.ts +372 -0
  686. package/src/plugins/analysis/ast/builders/ReturnBuilder.ts +279 -0
  687. package/src/plugins/analysis/ast/builders/TypeSystemBuilder.ts +475 -0
  688. package/src/plugins/analysis/ast/builders/UpdateExpressionBuilder.ts +262 -0
  689. package/src/plugins/analysis/ast/builders/YieldBuilder.ts +287 -0
  690. package/src/plugins/analysis/ast/builders/index.ts +11 -0
  691. package/src/plugins/analysis/ast/builders/types.ts +65 -0
  692. package/src/plugins/analysis/ast/handlers/AnalyzerDelegate.ts +183 -0
  693. package/src/plugins/analysis/ast/handlers/BranchHandler.ts +313 -0
  694. package/src/plugins/analysis/ast/handlers/CallExpressionHandler.ts +347 -0
  695. package/src/plugins/analysis/ast/handlers/FunctionBodyHandler.ts +24 -0
  696. package/src/plugins/analysis/ast/handlers/LoopHandler.ts +240 -0
  697. package/src/plugins/analysis/ast/handlers/NestedFunctionHandler.ts +201 -0
  698. package/src/plugins/analysis/ast/handlers/NewExpressionHandler.ts +159 -0
  699. package/src/plugins/analysis/ast/handlers/PropertyAccessHandler.ts +112 -0
  700. package/src/plugins/analysis/ast/handlers/ReturnYieldHandler.ts +166 -0
  701. package/src/plugins/analysis/ast/handlers/ThrowHandler.ts +101 -0
  702. package/src/plugins/analysis/ast/handlers/TryCatchHandler.ts +262 -0
  703. package/src/plugins/analysis/ast/handlers/VariableHandler.ts +93 -0
  704. package/src/plugins/analysis/ast/handlers/index.ts +12 -0
  705. package/src/plugins/analysis/ast/types.ts +68 -9
  706. package/src/plugins/analysis/ast/utils/createParameterNodes.ts +118 -13
  707. package/src/plugins/analysis/ast/utils/extractNamesFromPattern.ts +166 -0
  708. package/src/plugins/analysis/ast/utils/getExpressionValue.ts +34 -0
  709. package/src/plugins/analysis/ast/utils/getMemberExpressionName.ts +33 -0
  710. package/src/plugins/analysis/ast/utils/index.ts +2 -0
  711. package/src/plugins/analysis/ast/visitors/ArgumentExtractor.ts +307 -0
  712. package/src/plugins/analysis/ast/visitors/ArrayElementExtractor.ts +172 -0
  713. package/src/plugins/analysis/ast/visitors/CallExpressionVisitor.ts +425 -1374
  714. package/src/plugins/analysis/ast/visitors/ClassVisitor.ts +43 -12
  715. package/src/plugins/analysis/ast/visitors/FunctionVisitor.ts +39 -8
  716. package/src/plugins/analysis/ast/visitors/MutationDetector.ts +211 -0
  717. package/src/plugins/analysis/ast/visitors/ObjectPropertyExtractor.ts +217 -0
  718. package/src/plugins/analysis/ast/visitors/PropertyAccessVisitor.ts +69 -4
  719. package/src/plugins/analysis/ast/visitors/TypeScriptVisitor.ts +232 -13
  720. package/src/plugins/analysis/ast/visitors/VariableVisitor.ts +8 -11
  721. package/src/plugins/analysis/ast/visitors/call-expression-helpers.ts +65 -0
  722. package/src/plugins/analysis/ast/visitors/call-expression-types.ts +179 -0
  723. package/src/plugins/analysis/react-internal/browser-api.ts +168 -0
  724. package/src/plugins/analysis/react-internal/hooks.ts +517 -0
  725. package/src/plugins/analysis/react-internal/jsx.ts +279 -0
  726. package/src/plugins/analysis/react-internal/types.ts +183 -0
  727. package/src/plugins/discovery/MonorepoServiceDiscovery.ts +6 -14
  728. package/src/plugins/enrichment/AliasTracker.ts +3 -1
  729. package/src/plugins/enrichment/ArgumentParameterLinker.ts +3 -1
  730. package/src/plugins/enrichment/CallbackCallResolver.ts +398 -0
  731. package/src/plugins/enrichment/ClosureCaptureEnricher.ts +3 -1
  732. package/src/plugins/enrichment/ConfigRoutingMapBuilder.ts +67 -0
  733. package/src/plugins/enrichment/ExpressHandlerLinker.ts +3 -1
  734. package/src/plugins/enrichment/ExternalCallResolver.ts +5 -8
  735. package/src/plugins/enrichment/FunctionCallResolver.ts +6 -9
  736. package/src/plugins/enrichment/HTTPConnectionEnricher.ts +3 -1
  737. package/src/plugins/enrichment/ImportExportLinker.ts +5 -3
  738. package/src/plugins/enrichment/InstanceOfResolver.ts +3 -1
  739. package/src/plugins/enrichment/MethodCallResolver.ts +48 -659
  740. package/src/plugins/enrichment/MountPointResolver.ts +9 -2
  741. package/src/plugins/enrichment/NodejsBuiltinsResolver.ts +13 -18
  742. package/src/plugins/enrichment/PrefixEvaluator.ts +6 -2
  743. package/src/plugins/enrichment/RejectionPropagationEnricher.ts +3 -1
  744. package/src/plugins/enrichment/RustFFIEnricher.ts +3 -1
  745. package/src/plugins/enrichment/ServiceConnectionEnricher.ts +472 -0
  746. package/src/plugins/enrichment/SocketConnectionEnricher.ts +228 -0
  747. package/src/plugins/enrichment/ValueDomainAnalyzer.ts +3 -1
  748. package/src/plugins/enrichment/method-call/MethodCallData.ts +299 -0
  749. package/src/plugins/enrichment/method-call/MethodCallDetectors.ts +70 -0
  750. package/src/plugins/enrichment/method-call/MethodCallErrorAnalysis.ts +131 -0
  751. package/src/plugins/enrichment/method-call/MethodCallIndexers.ts +83 -0
  752. package/src/plugins/enrichment/method-call/MethodCallResolution.ts +181 -0
  753. package/src/plugins/indexing/IncrementalModuleIndexer.ts +5 -10
  754. package/src/plugins/indexing/JSModuleIndexer.ts +17 -21
  755. package/src/plugins/indexing/RustModuleIndexer.ts +14 -13
  756. package/src/plugins/validation/AwaitInLoopValidator.ts +91 -0
  757. package/src/plugins/validation/PackageCoverageValidator.ts +181 -0
  758. package/src/plugins/validation/UnconnectedRouteValidator.ts +93 -0
  759. package/src/queries/NodeContext.ts +277 -0
  760. package/src/queries/findCallsInFunction.ts +11 -2
  761. package/src/queries/findContainingFunction.ts +14 -3
  762. package/src/queries/index.ts +13 -0
  763. package/src/resources/InfraResourceMapImpl.ts +119 -0
  764. package/src/resources/RoutingMapImpl.ts +133 -0
  765. package/src/storage/backends/RFDBServerBackend.ts +106 -77
  766. package/src/utils/findRfdbBinary.ts +22 -7
  767. package/src/utils/moduleResolution.ts +28 -1
  768. package/src/utils/resolveNodeFile.ts +18 -0
  769. package/src/version.ts +28 -0
@@ -32,11 +32,18 @@ import { getLine, getColumn } from './ast/utils/location.js';
32
32
  import { Profiler } from '../../core/Profiler.js';
33
33
  import { ScopeTracker } from '../../core/ScopeTracker.js';
34
34
  import { computeSemanticId } from '../../core/SemanticId.js';
35
+ import { IdGenerator } from './ast/IdGenerator.js';
36
+ import { CollisionResolver } from './ast/CollisionResolver.js';
35
37
  import { ExpressionNode } from '../../core/nodes/ExpressionNode.js';
36
38
  import { ConstructorCallNode } from '../../core/nodes/ConstructorCallNode.js';
37
39
  import { ObjectLiteralNode } from '../../core/nodes/ObjectLiteralNode.js';
38
40
  import { ArrayLiteralNode } from '../../core/nodes/ArrayLiteralNode.js';
39
41
  import { NodeFactory } from '../../core/NodeFactory.js';
42
+ import { brandNodeInternal } from '../../core/brandNodeInternal.js';
43
+ import { resolveNodeFile } from '../../utils/resolveNodeFile.js';
44
+ import { extractNamesFromPattern } from './ast/utils/extractNamesFromPattern.js';
45
+ import { createFunctionBodyContext } from './ast/FunctionBodyContext.js';
46
+ import { VariableHandler, ReturnYieldHandler, ThrowHandler, NestedFunctionHandler, PropertyAccessHandler, NewExpressionHandler, CallExpressionHandler, LoopHandler, TryCatchHandler, BranchHandler, } from './ast/handlers/index.js';
40
47
  export class JSASTAnalyzer extends Plugin {
41
48
  graphBuilder;
42
49
  analyzedModules;
@@ -57,14 +64,14 @@ export class JSASTAnalyzer extends Plugin {
57
64
  'CALL', 'IMPORT', 'EXPORT', 'LITERAL', 'EXTERNAL_MODULE',
58
65
  'net:stdio', 'net:request', 'event:listener', 'http:request',
59
66
  // TypeScript-specific nodes
60
- 'INTERFACE', 'TYPE', 'ENUM', 'DECORATOR'
67
+ 'INTERFACE', 'TYPE', 'ENUM', 'DECORATOR', 'TYPE_PARAMETER'
61
68
  ],
62
69
  edges: [
63
70
  'CONTAINS', 'DECLARES', 'CALLS', 'HAS_SCOPE', 'CAPTURES', 'MODIFIES',
64
71
  'WRITES_TO', 'IMPORTS', 'INSTANCE_OF', 'HANDLED_BY', 'HAS_CALLBACK',
65
72
  'PASSES_ARGUMENT', 'MAKES_REQUEST', 'IMPORTS_FROM', 'EXPORTS_TO', 'ASSIGNED_FROM',
66
73
  // TypeScript-specific edges
67
- 'IMPLEMENTS', 'EXTENDS', 'DECORATED_BY',
74
+ 'IMPLEMENTS', 'EXTENDS', 'DECORATED_BY', 'HAS_TYPE_PARAMETER',
68
75
  // Promise data flow
69
76
  'RESOLVES_TO'
70
77
  ]
@@ -84,9 +91,9 @@ export class JSASTAnalyzer extends Plugin {
84
91
  /**
85
92
  * Вычисляет хеш содержимого файла
86
93
  */
87
- calculateFileHash(filePath) {
94
+ calculateFileHash(filePath, projectPath = '') {
88
95
  try {
89
- const content = readFileSync(filePath, 'utf-8');
96
+ const content = readFileSync(resolveNodeFile(filePath, projectPath), 'utf-8');
90
97
  return createHash('sha256').update(content).digest('hex');
91
98
  }
92
99
  catch {
@@ -96,25 +103,25 @@ export class JSASTAnalyzer extends Plugin {
96
103
  /**
97
104
  * Проверяет нужно ли анализировать модуль (сравнивает хеши)
98
105
  */
99
- async shouldAnalyzeModule(module, graph, forceAnalysis) {
106
+ async shouldAnalyzeModule(module, graph, forceAnalysis, projectPath = "") {
100
107
  if (forceAnalysis) {
101
108
  return true;
102
109
  }
103
110
  if (!module.contentHash) {
104
111
  return true;
105
112
  }
106
- const currentHash = this.calculateFileHash(module.file);
113
+ const currentHash = this.calculateFileHash(module.file, projectPath);
107
114
  if (!currentHash) {
108
115
  return true;
109
116
  }
110
117
  if (currentHash !== module.contentHash) {
111
- await graph.addNode({
118
+ await graph.addNode(brandNodeInternal({
112
119
  id: module.id,
113
120
  type: 'MODULE',
114
121
  name: module.name,
115
122
  file: module.file,
116
123
  contentHash: currentHash
117
- });
124
+ }));
118
125
  return true;
119
126
  }
120
127
  // Hash matches - check if module was actually analyzed (has FUNCTION nodes)
@@ -143,7 +150,7 @@ export class JSASTAnalyzer extends Plugin {
143
150
  skippedCount++;
144
151
  continue;
145
152
  }
146
- if (await this.shouldAnalyzeModule(module, graph, forceAnalysis)) {
153
+ if (await this.shouldAnalyzeModule(module, graph, forceAnalysis, projectPath)) {
147
154
  modulesToAnalyze.push(module);
148
155
  }
149
156
  else {
@@ -248,7 +255,7 @@ export class JSASTAnalyzer extends Plugin {
248
255
  // Convert ModuleNode to ASTModuleInfo format
249
256
  const moduleInfos = modules.map(m => ({
250
257
  id: m.id,
251
- file: m.file,
258
+ file: resolveNodeFile(m.file, projectPath),
252
259
  name: m.name
253
260
  }));
254
261
  // Parse all modules in parallel using worker threads
@@ -282,7 +289,7 @@ export class JSASTAnalyzer extends Plugin {
282
289
  context.onProgress({
283
290
  phase: 'analysis',
284
291
  currentPlugin: 'JSASTAnalyzer',
285
- message: `Processed ${result.module.file.replace(projectPath, '')}`,
292
+ message: `Processed ${result.module.name}`,
286
293
  totalFiles: modules.length,
287
294
  processedFiles: results.indexOf(result) + 1
288
295
  });
@@ -298,73 +305,13 @@ export class JSASTAnalyzer extends Plugin {
298
305
  /**
299
306
  * Extract variable names from destructuring patterns
300
307
  * Uses t.isX() type guards to avoid casts
308
+ *
309
+ * REG-399: Delegated to extractNamesFromPattern utility for code reuse with parameters.
310
+ * This method maintains the same API for backward compatibility.
301
311
  */
302
312
  extractVariableNamesFromPattern(pattern, variables = [], propertyPath = []) {
303
- if (!pattern)
304
- return variables;
305
- if (t.isIdentifier(pattern)) {
306
- variables.push({
307
- name: pattern.name,
308
- loc: pattern.loc?.start ? { start: pattern.loc.start } : { start: { line: 0, column: 0 } },
309
- propertyPath: propertyPath.length > 0 ? [...propertyPath] : undefined
310
- });
311
- }
312
- else if (t.isObjectPattern(pattern)) {
313
- pattern.properties.forEach((prop) => {
314
- if (t.isRestElement(prop)) {
315
- const restVars = this.extractVariableNamesFromPattern(prop.argument, [], []);
316
- restVars.forEach(v => {
317
- v.isRest = true;
318
- v.propertyPath = propertyPath.length > 0 ? [...propertyPath] : undefined;
319
- variables.push(v);
320
- });
321
- }
322
- else if (t.isObjectProperty(prop) && prop.value) {
323
- const key = t.isIdentifier(prop.key) ? prop.key.name :
324
- (t.isStringLiteral(prop.key) || t.isNumericLiteral(prop.key) ? String(prop.key.value) : null);
325
- if (key !== null) {
326
- const newPath = [...propertyPath, key];
327
- this.extractVariableNamesFromPattern(prop.value, variables, newPath);
328
- }
329
- else {
330
- this.extractVariableNamesFromPattern(prop.value, variables, propertyPath);
331
- }
332
- }
333
- });
334
- }
335
- else if (t.isArrayPattern(pattern)) {
336
- pattern.elements.forEach((element, index) => {
337
- if (element) {
338
- if (t.isRestElement(element)) {
339
- const restVars = this.extractVariableNamesFromPattern(element.argument, [], []);
340
- restVars.forEach(v => {
341
- v.isRest = true;
342
- v.arrayIndex = index;
343
- v.propertyPath = propertyPath.length > 0 ? [...propertyPath] : undefined;
344
- variables.push(v);
345
- });
346
- }
347
- else {
348
- const extracted = this.extractVariableNamesFromPattern(element, [], propertyPath);
349
- extracted.forEach(v => {
350
- v.arrayIndex = index;
351
- variables.push(v);
352
- });
353
- }
354
- }
355
- });
356
- }
357
- else if (t.isRestElement(pattern)) {
358
- const restVars = this.extractVariableNamesFromPattern(pattern.argument, [], propertyPath);
359
- restVars.forEach(v => {
360
- v.isRest = true;
361
- variables.push(v);
362
- });
363
- }
364
- else if (t.isAssignmentPattern(pattern)) {
365
- this.extractVariableNamesFromPattern(pattern.left, variables, propertyPath);
366
- }
367
- return variables;
313
+ // Delegate to the extracted utility function
314
+ return extractNamesFromPattern(pattern, variables, propertyPath);
368
315
  }
369
316
  /**
370
317
  * Отслеживает присваивание переменной для data flow анализа
@@ -426,54 +373,15 @@ export class JSASTAnalyzer extends Plugin {
426
373
  return;
427
374
  }
428
375
  // 3. MemberExpression call (e.g., arr.map())
376
+ // Uses coordinate-based lookup to reference the standard CALL node created by CallExpressionVisitor
429
377
  if (initExpression.type === 'CallExpression' && initExpression.callee.type === 'MemberExpression') {
430
- const callee = initExpression.callee;
431
- const objectName = callee.object.type === 'Identifier' ? callee.object.name : (callee.object.type === 'ThisExpression' ? 'this' : 'unknown');
432
- const methodName = callee.property.type === 'Identifier' ? callee.property.name : 'unknown';
433
- const fullName = `${objectName}.${methodName}`;
434
- const methodCallId = `CALL#${fullName}#${module.file}#${getLine(initExpression)}:${getColumn(initExpression)}:inline`;
435
- const existing = variableAssignments.find(a => a.sourceId === methodCallId);
436
- if (!existing) {
437
- const extractedArgs = [];
438
- initExpression.arguments.forEach((arg, index) => {
439
- if (arg.type !== 'SpreadElement') {
440
- const argLiteralValue = ExpressionEvaluator.extractLiteralValue(arg);
441
- if (argLiteralValue !== null) {
442
- const literalId = `LITERAL#arg${index}#${module.file}#${getLine(initExpression)}:${getColumn(initExpression)}:${literalCounterRef.value++}`;
443
- literals.push({
444
- id: literalId,
445
- type: 'LITERAL',
446
- value: argLiteralValue,
447
- valueType: typeof argLiteralValue,
448
- file: module.file,
449
- line: arg.loc?.start.line || getLine(initExpression),
450
- column: arg.loc?.start.column || getColumn(initExpression),
451
- parentCallId: methodCallId,
452
- argIndex: index
453
- });
454
- extractedArgs.push(argLiteralValue);
455
- }
456
- else {
457
- extractedArgs.push(undefined);
458
- }
459
- }
460
- });
461
- literals.push({
462
- id: methodCallId,
463
- type: 'CALL',
464
- name: fullName,
465
- object: objectName,
466
- method: methodName,
467
- file: module.file,
468
- arguments: extractedArgs,
469
- line: getLine(initExpression),
470
- column: getColumn(initExpression)
471
- });
472
- }
473
378
  variableAssignments.push({
474
379
  variableId,
475
- sourceId: methodCallId,
476
- sourceType: 'CALL'
380
+ sourceType: 'METHOD_CALL',
381
+ sourceLine: getLine(initExpression),
382
+ sourceColumn: getColumn(initExpression),
383
+ sourceFile: module.file,
384
+ line: line
477
385
  });
478
386
  return;
479
387
  }
@@ -1002,17 +910,19 @@ export class JSASTAnalyzer extends Plugin {
1002
910
  let edgesCreated = 0;
1003
911
  try {
1004
912
  this.profiler.start('file_read');
1005
- const code = readFileSync(module.file, 'utf-8');
913
+ const code = readFileSync(resolveNodeFile(module.file, projectPath), 'utf-8');
1006
914
  this.profiler.end('file_read');
1007
915
  this.profiler.start('babel_parse');
1008
916
  const ast = parse(code, {
1009
917
  sourceType: 'module',
1010
- plugins: ['jsx', 'typescript']
918
+ plugins: ['jsx', 'typescript', 'decorators-legacy']
1011
919
  });
1012
920
  this.profiler.end('babel_parse');
1013
921
  // Create ScopeTracker for semantic ID generation
1014
922
  // Use basename for shorter, more readable semantic IDs
1015
923
  const scopeTracker = new ScopeTracker(basename(module.file));
924
+ // REG-464: Shared IdGenerator for v2 collision resolution across visitors
925
+ const sharedIdGenerator = new IdGenerator(scopeTracker);
1016
926
  const functions = [];
1017
927
  const parameters = [];
1018
928
  const scopes = [];
@@ -1040,6 +950,8 @@ export class JSASTAnalyzer extends Plugin {
1040
950
  const typeAliases = [];
1041
951
  const enums = [];
1042
952
  const decorators = [];
953
+ // Type parameter tracking for generics (REG-303)
954
+ const typeParameters = [];
1043
955
  // Object/Array literal tracking for data flow
1044
956
  const objectLiterals = [];
1045
957
  const objectProperties = [];
@@ -1113,6 +1025,8 @@ export class JSASTAnalyzer extends Plugin {
1113
1025
  httpRequests, literals, variableAssignments,
1114
1026
  // TypeScript-specific collections
1115
1027
  interfaces, typeAliases, enums, decorators,
1028
+ // Type parameter tracking for generics (REG-303)
1029
+ typeParameters,
1116
1030
  // Object/Array literal tracking
1117
1031
  objectLiterals, objectProperties, arrayLiterals, arrayElements,
1118
1032
  // Array mutation tracking
@@ -1301,9 +1215,28 @@ export class JSASTAnalyzer extends Plugin {
1301
1215
  this.profiler.end('traverse_callbacks');
1302
1216
  // Call expressions
1303
1217
  this.profiler.start('traverse_calls');
1304
- const callExpressionVisitor = new CallExpressionVisitor(module, allCollections, scopeTracker);
1218
+ const callExpressionVisitor = new CallExpressionVisitor(module, allCollections, scopeTracker, sharedIdGenerator);
1305
1219
  traverse(ast, callExpressionVisitor.getHandlers());
1306
1220
  this.profiler.end('traverse_calls');
1221
+ // REG-297: Detect top-level await expressions
1222
+ this.profiler.start('traverse_top_level_await');
1223
+ let hasTopLevelAwait = false;
1224
+ traverse(ast, {
1225
+ AwaitExpression(awaitPath) {
1226
+ if (!awaitPath.getFunctionParent()) {
1227
+ hasTopLevelAwait = true;
1228
+ awaitPath.stop();
1229
+ }
1230
+ },
1231
+ // for-await-of uses ForOfStatement.await, not AwaitExpression
1232
+ ForOfStatement(forOfPath) {
1233
+ if (forOfPath.node.await && !forOfPath.getFunctionParent()) {
1234
+ hasTopLevelAwait = true;
1235
+ forOfPath.stop();
1236
+ }
1237
+ }
1238
+ });
1239
+ this.profiler.end('traverse_top_level_await');
1307
1240
  // Property access expressions (REG-395)
1308
1241
  this.profiler.start('traverse_property_access');
1309
1242
  const propertyAccessVisitor = new PropertyAccessVisitor(module, allCollections, scopeTracker);
@@ -1424,6 +1357,36 @@ export class JSASTAnalyzer extends Plugin {
1424
1357
  }
1425
1358
  });
1426
1359
  this.profiler.end('traverse_ifs');
1360
+ // REG-464: Resolve v2 ID collisions after all visitors complete
1361
+ const pendingNodes = sharedIdGenerator.getPendingNodes();
1362
+ if (pendingNodes.length > 0) {
1363
+ // Capture pre-resolution IDs to update callArguments afterward
1364
+ const preResolutionIds = new Map();
1365
+ for (const pn of pendingNodes) {
1366
+ preResolutionIds.set(pn.collectionRef, pn.collectionRef.id);
1367
+ }
1368
+ const collisionResolver = new CollisionResolver();
1369
+ collisionResolver.resolve(pendingNodes);
1370
+ // Update callArgument.callId references that became stale after resolution
1371
+ const idRemapping = new Map();
1372
+ for (const pn of pendingNodes) {
1373
+ const oldId = preResolutionIds.get(pn.collectionRef);
1374
+ if (oldId !== pn.collectionRef.id) {
1375
+ idRemapping.set(oldId, pn.collectionRef.id);
1376
+ }
1377
+ }
1378
+ if (idRemapping.size > 0) {
1379
+ const callArgs = allCollections.callArguments;
1380
+ if (callArgs) {
1381
+ for (const arg of callArgs) {
1382
+ const resolved = idRemapping.get(arg.callId);
1383
+ if (resolved) {
1384
+ arg.callId = resolved;
1385
+ }
1386
+ }
1387
+ }
1388
+ }
1389
+ }
1427
1390
  // Build graph
1428
1391
  this.profiler.start('graph_build');
1429
1392
  const result = await this.graphBuilder.build(module, graph, projectPath, {
@@ -1459,6 +1422,8 @@ export class JSASTAnalyzer extends Plugin {
1459
1422
  typeAliases,
1460
1423
  enums,
1461
1424
  decorators,
1425
+ // Type parameter tracking for generics (REG-303)
1426
+ typeParameters,
1462
1427
  // Array mutation tracking
1463
1428
  arrayMutations,
1464
1429
  // Object mutation tracking
@@ -1485,7 +1450,9 @@ export class JSASTAnalyzer extends Plugin {
1485
1450
  ? allCollections.catchesFromInfos
1486
1451
  : catchesFromInfos,
1487
1452
  // Property access tracking (REG-395)
1488
- propertyAccesses: allCollections.propertyAccesses || propertyAccesses
1453
+ propertyAccesses: allCollections.propertyAccesses || propertyAccesses,
1454
+ // REG-297: Top-level await tracking
1455
+ hasTopLevelAwait
1489
1456
  });
1490
1457
  this.profiler.end('graph_build');
1491
1458
  nodesCreated = result.nodes;
@@ -1663,423 +1630,6 @@ export class JSASTAnalyzer extends Plugin {
1663
1630
  }
1664
1631
  });
1665
1632
  }
1666
- createLoopScopeHandler(trackerScopeType, scopeType, loopType, parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState) {
1667
- return {
1668
- enter: (path) => {
1669
- const node = path.node;
1670
- // Phase 6 (REG-267): Increment loop count for cyclomatic complexity
1671
- if (controlFlowState) {
1672
- controlFlowState.loopCount++;
1673
- }
1674
- // 1. Create LOOP node
1675
- const loopCounter = loopCounterRef.value++;
1676
- const legacyLoopId = `${module.file}:LOOP:${loopType}:${getLine(node)}:${loopCounter}`;
1677
- const loopId = scopeTracker
1678
- ? computeSemanticId('LOOP', loopType, scopeTracker.getContext(), { discriminator: loopCounter })
1679
- : legacyLoopId;
1680
- // 2. Extract iteration target for for-in/for-of
1681
- let iteratesOverName;
1682
- let iteratesOverLine;
1683
- let iteratesOverColumn;
1684
- if (loopType === 'for-in' || loopType === 'for-of') {
1685
- const loopNode = node;
1686
- if (t.isIdentifier(loopNode.right)) {
1687
- iteratesOverName = loopNode.right.name;
1688
- iteratesOverLine = getLine(loopNode.right);
1689
- iteratesOverColumn = getColumn(loopNode.right);
1690
- }
1691
- else if (t.isMemberExpression(loopNode.right)) {
1692
- iteratesOverName = this.memberExpressionToString(loopNode.right);
1693
- iteratesOverLine = getLine(loopNode.right);
1694
- iteratesOverColumn = getColumn(loopNode.right);
1695
- }
1696
- }
1697
- // 2b. Extract init/test/update for classic for loops and test for while/do-while (REG-282)
1698
- let initVariableName;
1699
- let initLine;
1700
- let testExpressionId;
1701
- let testExpressionType;
1702
- let testLine;
1703
- let testColumn;
1704
- let updateExpressionId;
1705
- let updateExpressionType;
1706
- let updateLine;
1707
- let updateColumn;
1708
- if (loopType === 'for') {
1709
- const forNode = node;
1710
- // Extract init: let i = 0
1711
- if (forNode.init) {
1712
- initLine = getLine(forNode.init);
1713
- if (t.isVariableDeclaration(forNode.init)) {
1714
- // Get name of first declared variable
1715
- const firstDeclarator = forNode.init.declarations[0];
1716
- if (t.isIdentifier(firstDeclarator.id)) {
1717
- initVariableName = firstDeclarator.id.name;
1718
- }
1719
- }
1720
- }
1721
- // Extract test: i < 10
1722
- if (forNode.test) {
1723
- testLine = getLine(forNode.test);
1724
- testColumn = getColumn(forNode.test);
1725
- testExpressionType = forNode.test.type;
1726
- testExpressionId = ExpressionNode.generateId(forNode.test.type, module.file, testLine, testColumn);
1727
- }
1728
- // Extract update: i++
1729
- if (forNode.update) {
1730
- updateLine = getLine(forNode.update);
1731
- updateColumn = getColumn(forNode.update);
1732
- updateExpressionType = forNode.update.type;
1733
- updateExpressionId = ExpressionNode.generateId(forNode.update.type, module.file, updateLine, updateColumn);
1734
- }
1735
- }
1736
- // Extract test condition for while and do-while loops
1737
- if (loopType === 'while' || loopType === 'do-while') {
1738
- const condLoop = node;
1739
- if (condLoop.test) {
1740
- testLine = getLine(condLoop.test);
1741
- testColumn = getColumn(condLoop.test);
1742
- testExpressionType = condLoop.test.type;
1743
- testExpressionId = ExpressionNode.generateId(condLoop.test.type, module.file, testLine, testColumn);
1744
- }
1745
- }
1746
- // Extract async flag for for-await-of (REG-284)
1747
- let isAsync;
1748
- if (loopType === 'for-of') {
1749
- const forOfNode = node;
1750
- isAsync = forOfNode.await === true ? true : undefined;
1751
- }
1752
- // 3. Determine actual parent - use stack for nested loops, otherwise original parentScopeId
1753
- const actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
1754
- ? scopeIdStack[scopeIdStack.length - 1]
1755
- : parentScopeId;
1756
- // 3.5. Extract condition expression for while/do-while/for loops (REG-280)
1757
- // Note: for-in and for-of don't have test expressions (they use ITERATES_OVER instead)
1758
- let conditionExpressionId;
1759
- let conditionExpressionType;
1760
- let conditionLine;
1761
- let conditionColumn;
1762
- if (loopType === 'while' || loopType === 'do-while') {
1763
- const testNode = node.test;
1764
- if (testNode) {
1765
- const condResult = this.extractDiscriminantExpression(testNode, module);
1766
- conditionExpressionId = condResult.id;
1767
- conditionExpressionType = condResult.expressionType;
1768
- conditionLine = condResult.line;
1769
- conditionColumn = condResult.column;
1770
- }
1771
- }
1772
- else if (loopType === 'for') {
1773
- const forNode = node;
1774
- // for loop test may be null (infinite loop: for(;;))
1775
- if (forNode.test) {
1776
- const condResult = this.extractDiscriminantExpression(forNode.test, module);
1777
- conditionExpressionId = condResult.id;
1778
- conditionExpressionType = condResult.expressionType;
1779
- conditionLine = condResult.line;
1780
- conditionColumn = condResult.column;
1781
- }
1782
- }
1783
- // 4. Push LOOP info
1784
- loops.push({
1785
- id: loopId,
1786
- semanticId: loopId,
1787
- type: 'LOOP',
1788
- loopType,
1789
- file: module.file,
1790
- line: getLine(node),
1791
- column: getColumn(node),
1792
- parentScopeId: actualParentScopeId,
1793
- iteratesOverName,
1794
- iteratesOverLine,
1795
- iteratesOverColumn,
1796
- conditionExpressionId,
1797
- conditionExpressionType,
1798
- conditionLine,
1799
- conditionColumn,
1800
- // REG-282: init/test/update for classic for loops
1801
- initVariableName,
1802
- initLine,
1803
- testExpressionId,
1804
- testExpressionType,
1805
- testLine,
1806
- testColumn,
1807
- updateExpressionId,
1808
- updateExpressionType,
1809
- updateLine,
1810
- updateColumn,
1811
- // REG-284: async flag for for-await-of
1812
- async: isAsync
1813
- });
1814
- // 5. Create body SCOPE (backward compatibility)
1815
- const scopeId = `SCOPE#${scopeType}#${module.file}#${getLine(node)}:${scopeCounterRef.value++}`;
1816
- const semanticId = this.generateSemanticId(scopeType, scopeTracker);
1817
- scopes.push({
1818
- id: scopeId,
1819
- type: 'SCOPE',
1820
- scopeType,
1821
- semanticId,
1822
- file: module.file,
1823
- line: getLine(node),
1824
- parentScopeId: loopId // Parent is LOOP, not original parentScopeId
1825
- });
1826
- // 6. Push body SCOPE to scopeIdStack (for CONTAINS edges to nested items)
1827
- // The body scope is the container for nested loops, not the LOOP itself
1828
- if (scopeIdStack) {
1829
- scopeIdStack.push(scopeId);
1830
- }
1831
- // Enter scope for semantic ID generation
1832
- if (scopeTracker) {
1833
- scopeTracker.enterCountedScope(trackerScopeType);
1834
- }
1835
- },
1836
- exit: () => {
1837
- // Pop loop scope from stack
1838
- if (scopeIdStack) {
1839
- scopeIdStack.pop();
1840
- }
1841
- // Exit scope
1842
- if (scopeTracker) {
1843
- scopeTracker.exitScope();
1844
- }
1845
- }
1846
- };
1847
- }
1848
- /**
1849
- * Factory method to create TryStatement handler.
1850
- * Creates TRY_BLOCK, CATCH_BLOCK, FINALLY_BLOCK nodes and body SCOPEs.
1851
- * Does NOT use skip() - allows normal traversal for CallExpression/NewExpression visitors.
1852
- *
1853
- * Phase 4 (REG-267): Creates control flow nodes with HAS_CATCH and HAS_FINALLY edges.
1854
- *
1855
- * @param parentScopeId - Parent scope ID for the scope nodes
1856
- * @param module - Module context
1857
- * @param scopes - Collection to push scope nodes to
1858
- * @param tryBlocks - Collection to push TRY_BLOCK nodes to
1859
- * @param catchBlocks - Collection to push CATCH_BLOCK nodes to
1860
- * @param finallyBlocks - Collection to push FINALLY_BLOCK nodes to
1861
- * @param scopeCounterRef - Counter for unique scope IDs
1862
- * @param tryBlockCounterRef - Counter for unique TRY_BLOCK IDs
1863
- * @param catchBlockCounterRef - Counter for unique CATCH_BLOCK IDs
1864
- * @param finallyBlockCounterRef - Counter for unique FINALLY_BLOCK IDs
1865
- * @param scopeTracker - Tracker for semantic ID generation
1866
- * @param tryScopeMap - Map to track try/catch/finally scope transitions
1867
- * @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
1868
- */
1869
- createTryStatementHandler(parentScopeId, module, scopes, tryBlocks, catchBlocks, finallyBlocks, scopeCounterRef, tryBlockCounterRef, catchBlockCounterRef, finallyBlockCounterRef, scopeTracker, tryScopeMap, scopeIdStack, controlFlowState) {
1870
- return {
1871
- enter: (tryPath) => {
1872
- const tryNode = tryPath.node;
1873
- // Phase 6 (REG-267): Mark that this function has try/catch
1874
- if (controlFlowState) {
1875
- controlFlowState.hasTryCatch = true;
1876
- // REG-311: Increment try block depth for O(1) isInsideTry detection
1877
- controlFlowState.tryBlockDepth++;
1878
- }
1879
- // Determine actual parent - use stack for nested structures, otherwise original parentScopeId
1880
- const actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
1881
- ? scopeIdStack[scopeIdStack.length - 1]
1882
- : parentScopeId;
1883
- // 1. Create TRY_BLOCK node
1884
- const tryBlockCounter = tryBlockCounterRef.value++;
1885
- const legacyTryBlockId = `${module.file}:TRY_BLOCK:${getLine(tryNode)}:${tryBlockCounter}`;
1886
- const tryBlockId = scopeTracker
1887
- ? computeSemanticId('TRY_BLOCK', 'try', scopeTracker.getContext(), { discriminator: tryBlockCounter })
1888
- : legacyTryBlockId;
1889
- tryBlocks.push({
1890
- id: tryBlockId,
1891
- semanticId: tryBlockId,
1892
- type: 'TRY_BLOCK',
1893
- file: module.file,
1894
- line: getLine(tryNode),
1895
- column: getColumn(tryNode),
1896
- parentScopeId: actualParentScopeId
1897
- });
1898
- // 2. Create try-body SCOPE (backward compatibility)
1899
- // Parent is now TRY_BLOCK, not original parentScopeId
1900
- const tryScopeId = `SCOPE#try-block#${module.file}#${getLine(tryNode)}:${scopeCounterRef.value++}`;
1901
- const trySemanticId = this.generateSemanticId('try-block', scopeTracker);
1902
- scopes.push({
1903
- id: tryScopeId,
1904
- type: 'SCOPE',
1905
- scopeType: 'try-block',
1906
- semanticId: trySemanticId,
1907
- file: module.file,
1908
- line: getLine(tryNode),
1909
- parentScopeId: tryBlockId // Parent is TRY_BLOCK
1910
- });
1911
- // 3. Create CATCH_BLOCK and catch-body SCOPE if handler exists
1912
- let catchBlockId = null;
1913
- let catchScopeId = null;
1914
- if (tryNode.handler) {
1915
- const catchClause = tryNode.handler;
1916
- const catchBlockCounter = catchBlockCounterRef.value++;
1917
- const legacyCatchBlockId = `${module.file}:CATCH_BLOCK:${getLine(catchClause)}:${catchBlockCounter}`;
1918
- catchBlockId = scopeTracker
1919
- ? computeSemanticId('CATCH_BLOCK', 'catch', scopeTracker.getContext(), { discriminator: catchBlockCounter })
1920
- : legacyCatchBlockId;
1921
- // Extract parameter name if present
1922
- let parameterName;
1923
- if (catchClause.param && t.isIdentifier(catchClause.param)) {
1924
- parameterName = catchClause.param.name;
1925
- }
1926
- catchBlocks.push({
1927
- id: catchBlockId,
1928
- semanticId: catchBlockId,
1929
- type: 'CATCH_BLOCK',
1930
- file: module.file,
1931
- line: getLine(catchClause),
1932
- column: getColumn(catchClause),
1933
- parentScopeId,
1934
- parentTryBlockId: tryBlockId,
1935
- parameterName
1936
- });
1937
- // Create catch-body SCOPE (backward compatibility)
1938
- catchScopeId = `SCOPE#catch-block#${module.file}#${getLine(catchClause)}:${scopeCounterRef.value++}`;
1939
- const catchSemanticId = this.generateSemanticId('catch-block', scopeTracker);
1940
- scopes.push({
1941
- id: catchScopeId,
1942
- type: 'SCOPE',
1943
- scopeType: 'catch-block',
1944
- semanticId: catchSemanticId,
1945
- file: module.file,
1946
- line: getLine(catchClause),
1947
- parentScopeId: catchBlockId // Parent is CATCH_BLOCK
1948
- });
1949
- }
1950
- // 4. Create FINALLY_BLOCK and finally-body SCOPE if finalizer exists
1951
- let finallyBlockId = null;
1952
- let finallyScopeId = null;
1953
- if (tryNode.finalizer) {
1954
- const finallyBlockCounter = finallyBlockCounterRef.value++;
1955
- const legacyFinallyBlockId = `${module.file}:FINALLY_BLOCK:${getLine(tryNode.finalizer)}:${finallyBlockCounter}`;
1956
- finallyBlockId = scopeTracker
1957
- ? computeSemanticId('FINALLY_BLOCK', 'finally', scopeTracker.getContext(), { discriminator: finallyBlockCounter })
1958
- : legacyFinallyBlockId;
1959
- finallyBlocks.push({
1960
- id: finallyBlockId,
1961
- semanticId: finallyBlockId,
1962
- type: 'FINALLY_BLOCK',
1963
- file: module.file,
1964
- line: getLine(tryNode.finalizer),
1965
- column: getColumn(tryNode.finalizer),
1966
- parentScopeId,
1967
- parentTryBlockId: tryBlockId
1968
- });
1969
- // Create finally-body SCOPE (backward compatibility)
1970
- finallyScopeId = `SCOPE#finally-block#${module.file}#${getLine(tryNode.finalizer)}:${scopeCounterRef.value++}`;
1971
- const finallySemanticId = this.generateSemanticId('finally-block', scopeTracker);
1972
- scopes.push({
1973
- id: finallyScopeId,
1974
- type: 'SCOPE',
1975
- scopeType: 'finally-block',
1976
- semanticId: finallySemanticId,
1977
- file: module.file,
1978
- line: getLine(tryNode.finalizer),
1979
- parentScopeId: finallyBlockId // Parent is FINALLY_BLOCK
1980
- });
1981
- }
1982
- // 5. Push try scope onto stack for CONTAINS edges
1983
- if (scopeIdStack) {
1984
- scopeIdStack.push(tryScopeId);
1985
- }
1986
- // Enter try scope for semantic ID generation
1987
- if (scopeTracker) {
1988
- scopeTracker.enterCountedScope('try');
1989
- }
1990
- // 6. Store scope info for catch/finally transitions
1991
- tryScopeMap.set(tryNode, {
1992
- tryScopeId,
1993
- catchScopeId,
1994
- finallyScopeId,
1995
- currentBlock: 'try',
1996
- tryBlockId,
1997
- catchBlockId,
1998
- finallyBlockId
1999
- });
2000
- },
2001
- exit: (tryPath) => {
2002
- const tryNode = tryPath.node;
2003
- const _scopeInfo = tryScopeMap.get(tryNode);
2004
- // REG-311: Only decrement try block depth if we're still in 'try' block
2005
- // (not transitioned to catch/finally, where we already decremented)
2006
- if (controlFlowState && _scopeInfo?.currentBlock === 'try') {
2007
- controlFlowState.tryBlockDepth--;
2008
- }
2009
- // Pop the current scope from stack (could be try, catch, or finally)
2010
- if (scopeIdStack) {
2011
- scopeIdStack.pop();
2012
- }
2013
- // Exit the current scope
2014
- if (scopeTracker) {
2015
- scopeTracker.exitScope();
2016
- }
2017
- // Clean up
2018
- tryScopeMap.delete(tryNode);
2019
- }
2020
- };
2021
- }
2022
- /**
2023
- * Factory method to create CatchClause handler.
2024
- * Handles scope transition from try to catch and processes catch parameter.
2025
- *
2026
- * @param module - Module context
2027
- * @param variableDeclarations - Collection to push variable declarations to
2028
- * @param varDeclCounterRef - Counter for unique variable declaration IDs
2029
- * @param scopeTracker - Tracker for semantic ID generation
2030
- * @param tryScopeMap - Map to track try/catch/finally scope transitions
2031
- * @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
2032
- */
2033
- createCatchClauseHandler(module, variableDeclarations, varDeclCounterRef, scopeTracker, tryScopeMap, scopeIdStack, controlFlowState) {
2034
- return {
2035
- enter: (catchPath) => {
2036
- const catchNode = catchPath.node;
2037
- const parent = catchPath.parent;
2038
- if (!t.isTryStatement(parent))
2039
- return;
2040
- const scopeInfo = tryScopeMap.get(parent);
2041
- if (!scopeInfo || !scopeInfo.catchScopeId)
2042
- return;
2043
- // Transition from try scope to catch scope
2044
- if (scopeInfo.currentBlock === 'try') {
2045
- // Pop try scope, push catch scope
2046
- if (scopeIdStack) {
2047
- scopeIdStack.pop();
2048
- scopeIdStack.push(scopeInfo.catchScopeId);
2049
- }
2050
- // Exit try scope, enter catch scope for semantic ID
2051
- if (scopeTracker) {
2052
- scopeTracker.exitScope();
2053
- scopeTracker.enterCountedScope('catch');
2054
- }
2055
- // REG-311: Decrement tryBlockDepth when leaving try block for catch
2056
- // Calls in catch block should NOT have isInsideTry=true
2057
- if (controlFlowState) {
2058
- controlFlowState.tryBlockDepth--;
2059
- }
2060
- scopeInfo.currentBlock = 'catch';
2061
- }
2062
- // Handle catch parameter (e.g., catch (e) or catch ({ message }))
2063
- if (catchNode.param) {
2064
- const errorVarInfo = this.extractVariableNamesFromPattern(catchNode.param);
2065
- errorVarInfo.forEach(varInfo => {
2066
- const legacyId = `VARIABLE#${varInfo.name}#${module.file}#${varInfo.loc.start.line}:${varInfo.loc.start.column}:${varDeclCounterRef.value++}`;
2067
- const varId = scopeTracker
2068
- ? computeSemanticId('VARIABLE', varInfo.name, scopeTracker.getContext())
2069
- : legacyId;
2070
- variableDeclarations.push({
2071
- id: varId,
2072
- type: 'VARIABLE',
2073
- name: varInfo.name,
2074
- file: module.file,
2075
- line: varInfo.loc.start.line,
2076
- parentScopeId: scopeInfo.catchScopeId
2077
- });
2078
- });
2079
- }
2080
- }
2081
- };
2082
- }
2083
1633
  /**
2084
1634
  * Handles SwitchStatement nodes.
2085
1635
  * Creates BRANCH node for switch, CASE nodes for each case clause,
@@ -2533,1331 +2083,134 @@ export class JSASTAnalyzer extends Plugin {
2533
2083
  };
2534
2084
  }
2535
2085
  /**
2536
- * Factory method to create IfStatement handler.
2537
- * Creates BRANCH node for if statement and SCOPE nodes for if/else bodies.
2538
- * Tracks if/else scope transitions via ifElseScopeMap.
2539
- *
2540
- * Phase 3 (REG-267): Creates BRANCH node with branchType='if' and
2541
- * HAS_CONSEQUENT/HAS_ALTERNATE edges to body SCOPEs.
2542
- *
2543
- * @param parentScopeId - Parent scope ID for the scope nodes
2544
- * @param module - Module context
2545
- * @param scopes - Collection to push scope nodes to
2546
- * @param branches - Collection to push BRANCH nodes to
2547
- * @param ifScopeCounterRef - Counter for unique if scope IDs
2548
- * @param branchCounterRef - Counter for unique BRANCH IDs
2549
- * @param scopeTracker - Tracker for semantic ID generation
2550
- * @param sourceCode - Source code for extracting condition text
2551
- * @param ifElseScopeMap - Map to track if/else scope transitions
2552
- * @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
2553
- */
2554
- createIfStatementHandler(parentScopeId, module, scopes, branches, ifScopeCounterRef, branchCounterRef, scopeTracker, sourceCode, ifElseScopeMap, scopeIdStack, controlFlowState, countLogicalOperators) {
2555
- return {
2556
- enter: (ifPath) => {
2557
- const ifNode = ifPath.node;
2558
- const condition = sourceCode.substring(ifNode.test.start, ifNode.test.end) || 'condition';
2559
- // Phase 6 (REG-267): Increment branch count and count logical operators
2560
- if (controlFlowState) {
2561
- controlFlowState.branchCount++;
2562
- if (countLogicalOperators) {
2563
- controlFlowState.logicalOpCount += countLogicalOperators(ifNode.test);
2564
- }
2565
- }
2566
- // Check if this if-statement is an else-if (alternate of parent IfStatement)
2567
- const isElseIf = t.isIfStatement(ifPath.parent) && ifPath.parentKey === 'alternate';
2568
- // Determine actual parent scope
2569
- let actualParentScopeId;
2570
- if (isElseIf) {
2571
- // For else-if, parent should be the outer BRANCH (stored in ifElseScopeMap)
2572
- const parentIfInfo = ifElseScopeMap.get(ifPath.parent);
2573
- if (parentIfInfo) {
2574
- actualParentScopeId = parentIfInfo.branchId;
2575
- }
2576
- else {
2577
- // Fallback to stack
2578
- actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
2579
- ? scopeIdStack[scopeIdStack.length - 1]
2580
- : parentScopeId;
2581
- }
2582
- }
2583
- else {
2584
- // For regular if statements, use stack or original parentScopeId
2585
- actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
2586
- ? scopeIdStack[scopeIdStack.length - 1]
2587
- : parentScopeId;
2588
- }
2589
- // 1. Create BRANCH node for if statement
2590
- const branchCounter = branchCounterRef.value++;
2591
- const legacyBranchId = `${module.file}:BRANCH:if:${getLine(ifNode)}:${branchCounter}`;
2592
- const branchId = scopeTracker
2593
- ? computeSemanticId('BRANCH', 'if', scopeTracker.getContext(), { discriminator: branchCounter })
2594
- : legacyBranchId;
2595
- // 2. Extract condition expression info for HAS_CONDITION edge
2596
- const conditionResult = this.extractDiscriminantExpression(ifNode.test, module);
2597
- // For else-if, get the parent branch ID
2598
- const isAlternateOfBranchId = isElseIf
2599
- ? ifElseScopeMap.get(ifPath.parent)?.branchId
2600
- : undefined;
2601
- branches.push({
2602
- id: branchId,
2603
- semanticId: branchId,
2604
- type: 'BRANCH',
2605
- branchType: 'if',
2606
- file: module.file,
2607
- line: getLine(ifNode),
2608
- parentScopeId: actualParentScopeId,
2609
- discriminantExpressionId: conditionResult.id,
2610
- discriminantExpressionType: conditionResult.expressionType,
2611
- discriminantLine: conditionResult.line,
2612
- discriminantColumn: conditionResult.column,
2613
- isAlternateOfBranchId
2614
- });
2615
- // 3. Create if-body SCOPE (backward compatibility)
2616
- // Parent is now BRANCH, not original parentScopeId
2617
- const counterId = ifScopeCounterRef.value++;
2618
- const ifScopeId = `SCOPE#if#${module.file}#${getLine(ifNode)}:${getColumn(ifNode)}:${counterId}`;
2619
- // Parse condition to extract constraints
2620
- const constraints = ConditionParser.parse(ifNode.test);
2621
- const ifSemanticId = this.generateSemanticId('if_statement', scopeTracker);
2622
- scopes.push({
2623
- id: ifScopeId,
2624
- type: 'SCOPE',
2625
- scopeType: 'if_statement',
2626
- name: `if:${getLine(ifNode)}:${getColumn(ifNode)}:${counterId}`,
2627
- semanticId: ifSemanticId,
2628
- conditional: true,
2629
- condition,
2630
- constraints: constraints.length > 0 ? constraints : undefined,
2631
- file: module.file,
2632
- line: getLine(ifNode),
2633
- parentScopeId: branchId // Parent is BRANCH, not original parentScopeId
2634
- });
2635
- // 4. Push if scope onto stack for CONTAINS edges
2636
- if (scopeIdStack) {
2637
- scopeIdStack.push(ifScopeId);
2638
- }
2639
- // Enter scope for semantic ID generation
2640
- if (scopeTracker) {
2641
- scopeTracker.enterCountedScope('if');
2642
- }
2643
- // 5. Handle else branch if present
2644
- let elseScopeId = null;
2645
- if (ifNode.alternate && !t.isIfStatement(ifNode.alternate)) {
2646
- // Only create else scope for actual else block, not else-if
2647
- const elseCounterId = ifScopeCounterRef.value++;
2648
- elseScopeId = `SCOPE#else#${module.file}#${getLine(ifNode.alternate)}:${getColumn(ifNode.alternate)}:${elseCounterId}`;
2649
- const negatedConstraints = constraints.length > 0 ? ConditionParser.negate(constraints) : undefined;
2650
- const elseSemanticId = this.generateSemanticId('else_statement', scopeTracker);
2651
- scopes.push({
2652
- id: elseScopeId,
2653
- type: 'SCOPE',
2654
- scopeType: 'else_statement',
2655
- name: `else:${getLine(ifNode.alternate)}:${getColumn(ifNode.alternate)}:${elseCounterId}`,
2656
- semanticId: elseSemanticId,
2657
- conditional: true,
2658
- constraints: negatedConstraints,
2659
- file: module.file,
2660
- line: getLine(ifNode.alternate),
2661
- parentScopeId: branchId // Parent is BRANCH, not original parentScopeId
2662
- });
2663
- // Store info to switch to else scope when we enter alternate
2664
- ifElseScopeMap.set(ifNode, { inElse: false, hasElse: true, ifScopeId, elseScopeId, branchId });
2665
- }
2666
- else {
2667
- ifElseScopeMap.set(ifNode, { inElse: false, hasElse: false, ifScopeId, elseScopeId: null, branchId });
2668
- }
2669
- },
2670
- exit: (ifPath) => {
2671
- const ifNode = ifPath.node;
2672
- // Pop scope from stack (either if or else, depending on what we're exiting)
2673
- if (scopeIdStack) {
2674
- scopeIdStack.pop();
2675
- }
2676
- // Exit the current scope (either if or else)
2677
- if (scopeTracker) {
2678
- scopeTracker.exitScope();
2679
- }
2680
- // If we were in else, we already exited else scope
2681
- // If we only had if, we exit if scope (done above)
2682
- ifElseScopeMap.delete(ifNode);
2683
- }
2684
- };
2685
- }
2686
- /**
2687
- * Factory method to create ConditionalExpression (ternary) handler.
2688
- * Creates BRANCH nodes with branchType='ternary' and increments branchCount for cyclomatic complexity.
2689
- *
2690
- * Key difference from IfStatement: ternary has EXPRESSIONS as branches, not SCOPE blocks.
2691
- * We store consequentExpressionId and alternateExpressionId in BranchInfo for HAS_CONSEQUENT/HAS_ALTERNATE edges.
2086
+ * Анализирует тело функции и извлекает переменные, вызовы, условные блоки.
2087
+ * Uses ScopeTracker from collections for semantic ID generation.
2692
2088
  *
2693
- * @param parentScopeId - Parent scope ID for the BRANCH node
2694
- * @param module - Module context
2695
- * @param branches - Collection to push BRANCH nodes to
2696
- * @param branchCounterRef - Counter for unique BRANCH IDs
2697
- * @param scopeTracker - Tracker for semantic ID generation
2698
- * @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
2699
- * @param controlFlowState - State for tracking control flow metrics (complexity)
2700
- * @param countLogicalOperators - Function to count logical operators in condition
2089
+ * REG-422: Delegates traversal to extracted handler classes.
2090
+ * Local state is encapsulated in FunctionBodyContext; each handler
2091
+ * contributes a Visitor fragment that is merged into a single traversal.
2701
2092
  */
2702
- createConditionalExpressionHandler(parentScopeId, module, branches, branchCounterRef, scopeTracker, scopeIdStack, controlFlowState, countLogicalOperators) {
2703
- return (condPath) => {
2704
- const condNode = condPath.node;
2705
- // Increment branch count for cyclomatic complexity
2706
- if (controlFlowState) {
2707
- controlFlowState.branchCount++;
2708
- // Count logical operators in the test condition (e.g., a && b ? x : y)
2709
- if (countLogicalOperators) {
2710
- controlFlowState.logicalOpCount += countLogicalOperators(condNode.test);
2711
- }
2712
- }
2713
- // Determine parent scope from stack or fallback
2714
- const actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
2715
- ? scopeIdStack[scopeIdStack.length - 1]
2716
- : parentScopeId;
2717
- // Create BRANCH node with branchType='ternary'
2718
- const branchCounter = branchCounterRef.value++;
2719
- const legacyBranchId = `${module.file}:BRANCH:ternary:${getLine(condNode)}:${branchCounter}`;
2720
- const branchId = scopeTracker
2721
- ? computeSemanticId('BRANCH', 'ternary', scopeTracker.getContext(), { discriminator: branchCounter })
2722
- : legacyBranchId;
2723
- // Extract condition expression info for HAS_CONDITION edge
2724
- const conditionResult = this.extractDiscriminantExpression(condNode.test, module);
2725
- // Generate expression IDs for consequent and alternate
2726
- const consequentLine = getLine(condNode.consequent);
2727
- const consequentColumn = getColumn(condNode.consequent);
2728
- const consequentExpressionId = ExpressionNode.generateId(condNode.consequent.type, module.file, consequentLine, consequentColumn);
2729
- const alternateLine = getLine(condNode.alternate);
2730
- const alternateColumn = getColumn(condNode.alternate);
2731
- const alternateExpressionId = ExpressionNode.generateId(condNode.alternate.type, module.file, alternateLine, alternateColumn);
2732
- branches.push({
2733
- id: branchId,
2734
- semanticId: branchId,
2735
- type: 'BRANCH',
2736
- branchType: 'ternary',
2093
+ analyzeFunctionBody(funcPath, parentScopeId, module, collections) {
2094
+ // 1. Create context (replaces ~260 lines of local var declarations)
2095
+ const ctx = createFunctionBodyContext(funcPath, parentScopeId, module, collections, (collections.functions ?? []), extractNamesFromPattern);
2096
+ // 2. Handle implicit return for THIS arrow function if it has an expression body
2097
+ // e.g., `const double = x => x * 2;` — this needs this.extractReturnExpressionInfo,
2098
+ // so it stays here rather than in a handler.
2099
+ if (t.isArrowFunctionExpression(ctx.funcNode) && !t.isBlockStatement(ctx.funcNode.body) && ctx.currentFunctionId) {
2100
+ const bodyExpr = ctx.funcNode.body;
2101
+ const exprInfo = this.extractReturnExpressionInfo(bodyExpr, module, ctx.literals, ctx.literalCounterRef, ctx.funcLine, ctx.funcColumn, 'implicit_return');
2102
+ ctx.returnStatements.push({
2103
+ parentFunctionId: ctx.currentFunctionId,
2737
2104
  file: module.file,
2738
- line: getLine(condNode),
2739
- parentScopeId: actualParentScopeId,
2740
- discriminantExpressionId: conditionResult.id,
2741
- discriminantExpressionType: conditionResult.expressionType,
2742
- discriminantLine: conditionResult.line,
2743
- discriminantColumn: conditionResult.column,
2744
- consequentExpressionId,
2745
- alternateExpressionId
2105
+ line: getLine(bodyExpr),
2106
+ column: getColumn(bodyExpr),
2107
+ returnValueType: 'NONE',
2108
+ isImplicitReturn: true,
2109
+ ...exprInfo,
2746
2110
  });
2747
- };
2111
+ }
2112
+ // 3. Create handlers and merge their visitors into a single traversal
2113
+ // Cast to AnalyzerDelegate — the interface declares the same methods that exist
2114
+ // on this class as private. The cast is safe because the shape matches exactly.
2115
+ const delegate = this;
2116
+ const handlers = [
2117
+ new VariableHandler(ctx, delegate),
2118
+ new ReturnYieldHandler(ctx, delegate),
2119
+ new ThrowHandler(ctx, delegate),
2120
+ new NestedFunctionHandler(ctx, delegate),
2121
+ new PropertyAccessHandler(ctx, delegate),
2122
+ new NewExpressionHandler(ctx, delegate),
2123
+ new CallExpressionHandler(ctx, delegate),
2124
+ new LoopHandler(ctx, delegate),
2125
+ new TryCatchHandler(ctx, delegate),
2126
+ new BranchHandler(ctx, delegate),
2127
+ ];
2128
+ const mergedVisitor = {};
2129
+ for (const handler of handlers) {
2130
+ Object.assign(mergedVisitor, handler.getHandlers());
2131
+ }
2132
+ // 4. Single traversal over the function body
2133
+ funcPath.traverse(mergedVisitor);
2134
+ // 5. Post-traverse: collect CATCHES_FROM info for try/catch blocks
2135
+ if (ctx.functionPath) {
2136
+ this.collectCatchesFromInfo(ctx.functionPath, ctx.catchBlocks, ctx.callSites, ctx.methodCalls, ctx.constructorCalls, ctx.catchesFromInfos, module);
2137
+ }
2138
+ // 6. Post-traverse: Attach control flow metadata to the function node
2139
+ this.attachControlFlowMetadata(ctx);
2748
2140
  }
2749
2141
  /**
2750
- * Factory method to create BlockStatement handler for tracking if/else and try/finally transitions.
2751
- * When entering an else block, switches scope from if to else.
2752
- * When entering a finally block, switches scope from try/catch to finally.
2753
- *
2754
- * @param scopeTracker - Tracker for semantic ID generation
2755
- * @param ifElseScopeMap - Map to track if/else scope transitions
2756
- * @param tryScopeMap - Map to track try/catch/finally scope transitions
2757
- * @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
2142
+ * Attach control flow metadata (cyclomatic complexity, error tracking, HOF bindings)
2143
+ * to the matching function node after traversal completes.
2758
2144
  */
2759
- createBlockStatementHandler(scopeTracker, ifElseScopeMap, tryScopeMap, scopeIdStack) {
2760
- return {
2761
- enter: (blockPath) => {
2762
- const parent = blockPath.parent;
2763
- // Check if this block is the alternate of an IfStatement
2764
- if (t.isIfStatement(parent) && parent.alternate === blockPath.node) {
2765
- const scopeInfo = ifElseScopeMap.get(parent);
2766
- if (scopeInfo && scopeInfo.hasElse && !scopeInfo.inElse) {
2767
- // Swap if-scope for else-scope on the stack
2768
- if (scopeIdStack && scopeInfo.elseScopeId) {
2769
- scopeIdStack.pop(); // Remove if-scope
2770
- scopeIdStack.push(scopeInfo.elseScopeId); // Push else-scope
2771
- }
2772
- // Exit if scope, enter else scope for semantic ID tracking
2773
- if (scopeTracker) {
2774
- scopeTracker.exitScope();
2775
- scopeTracker.enterCountedScope('else');
2776
- }
2777
- scopeInfo.inElse = true;
2778
- }
2779
- }
2780
- // Check if this block is the finalizer of a TryStatement
2781
- if (t.isTryStatement(parent) && parent.finalizer === blockPath.node) {
2782
- const scopeInfo = tryScopeMap.get(parent);
2783
- if (scopeInfo && scopeInfo.finallyScopeId && scopeInfo.currentBlock !== 'finally') {
2784
- // Pop current scope (try or catch), push finally scope
2785
- if (scopeIdStack) {
2786
- scopeIdStack.pop();
2787
- scopeIdStack.push(scopeInfo.finallyScopeId);
2788
- }
2789
- // Exit current scope, enter finally scope for semantic ID tracking
2790
- if (scopeTracker) {
2791
- scopeTracker.exitScope();
2792
- scopeTracker.enterCountedScope('finally');
2793
- }
2794
- scopeInfo.currentBlock = 'finally';
2795
- }
2796
- }
2797
- }
2145
+ attachControlFlowMetadata(ctx) {
2146
+ if (!ctx.matchingFunction)
2147
+ return;
2148
+ const cyclomaticComplexity = 1 +
2149
+ ctx.controlFlowState.branchCount +
2150
+ ctx.controlFlowState.loopCount +
2151
+ ctx.controlFlowState.caseCount +
2152
+ ctx.controlFlowState.logicalOpCount;
2153
+ // REG-311: Collect rejection info for this function
2154
+ const functionRejectionPatterns = ctx.rejectionPatterns.filter(p => p.functionId === ctx.matchingFunction.id);
2155
+ const asyncPatterns = functionRejectionPatterns.filter(p => p.isAsync);
2156
+ const syncPatterns = functionRejectionPatterns.filter(p => !p.isAsync);
2157
+ const canReject = asyncPatterns.length > 0;
2158
+ const hasAsyncThrow = asyncPatterns.some(p => p.rejectionType === 'async_throw');
2159
+ const rejectedBuiltinErrors = [...new Set(asyncPatterns
2160
+ .filter(p => p.errorClassName !== null)
2161
+ .map(p => p.errorClassName))];
2162
+ // REG-286: Sync throw error tracking
2163
+ const thrownBuiltinErrors = [...new Set(syncPatterns
2164
+ .filter(p => p.errorClassName !== null)
2165
+ .map(p => p.errorClassName))];
2166
+ ctx.matchingFunction.controlFlow = {
2167
+ hasBranches: ctx.controlFlowState.branchCount > 0,
2168
+ hasLoops: ctx.controlFlowState.loopCount > 0,
2169
+ hasTryCatch: ctx.controlFlowState.hasTryCatch,
2170
+ hasEarlyReturn: ctx.controlFlowState.hasEarlyReturn,
2171
+ hasThrow: ctx.controlFlowState.hasThrow,
2172
+ cyclomaticComplexity,
2173
+ // REG-311: Async error tracking
2174
+ canReject,
2175
+ hasAsyncThrow,
2176
+ rejectedBuiltinErrors: rejectedBuiltinErrors.length > 0 ? rejectedBuiltinErrors : undefined,
2177
+ // REG-286: Sync throw tracking
2178
+ thrownBuiltinErrors: thrownBuiltinErrors.length > 0 ? thrownBuiltinErrors : undefined
2798
2179
  };
2180
+ // REG-401: Store invoked parameter indexes for user-defined HOF detection
2181
+ if (ctx.invokedParamIndexes.size > 0) {
2182
+ ctx.matchingFunction.invokesParamIndexes = [...ctx.invokedParamIndexes];
2183
+ }
2184
+ // REG-417: Store property paths for destructured param bindings
2185
+ if (ctx.invokesParamBindings.length > 0) {
2186
+ ctx.matchingFunction.invokesParamBindings = ctx.invokesParamBindings;
2187
+ }
2799
2188
  }
2800
2189
  /**
2801
- * Анализирует тело функции и извлекает переменные, вызовы, условные блоки.
2802
- * Uses ScopeTracker from collections for semantic ID generation.
2190
+ * Handle CallExpression nodes: direct function calls (greet(), main())
2191
+ * and method calls (obj.method(), data.process()).
2192
+ *
2193
+ * Handles:
2194
+ * - Direct function calls (Identifier callee) → callSites collection
2195
+ * - Method calls (MemberExpression callee) → methodCalls collection
2196
+ * - Array mutation detection (push, unshift, splice)
2197
+ * - Object.assign() detection
2198
+ * - REG-311: isAwaited and isInsideTry metadata on CALL nodes
2199
+ *
2200
+ * @param callNode - The call expression AST node
2201
+ * @param processedCallSites - Set of already processed call site keys to avoid duplicates
2202
+ * @param processedMethodCalls - Set of already processed method call keys to avoid duplicates
2203
+ * @param callSites - Collection for direct function calls
2204
+ * @param methodCalls - Collection for method calls
2205
+ * @param module - Current module being analyzed
2206
+ * @param callSiteCounterRef - Counter for legacy ID generation
2207
+ * @param scopeTracker - Optional scope tracker for semantic ID generation
2208
+ * @param parentScopeId - ID of the parent scope containing this call
2209
+ * @param collections - Full collections object for array/object mutations
2210
+ * @param isAwaited - REG-311: true if wrapped in await expression
2211
+ * @param isInsideTry - REG-311: true if inside try block
2803
2212
  */
2804
- analyzeFunctionBody(funcPath, parentScopeId, module, collections) {
2805
- // Extract with defaults for optional properties
2806
- const functions = (collections.functions ?? []);
2807
- const scopes = (collections.scopes ?? []);
2808
- const variableDeclarations = (collections.variableDeclarations ?? []);
2809
- const callSites = (collections.callSites ?? []);
2810
- const methodCalls = (collections.methodCalls ?? []);
2811
- const _eventListeners = (collections.eventListeners ?? []);
2812
- const _methodCallbacks = (collections.methodCallbacks ?? []);
2813
- const classInstantiations = (collections.classInstantiations ?? []);
2814
- const constructorCalls = (collections.constructorCalls ?? []);
2815
- const _httpRequests = (collections.httpRequests ?? []);
2816
- const literals = (collections.literals ?? []);
2817
- const variableAssignments = (collections.variableAssignments ?? []);
2818
- const ifScopeCounterRef = (collections.ifScopeCounterRef ?? { value: 0 });
2819
- const scopeCounterRef = (collections.scopeCounterRef ?? { value: 0 });
2820
- const varDeclCounterRef = (collections.varDeclCounterRef ?? { value: 0 });
2821
- const callSiteCounterRef = (collections.callSiteCounterRef ?? { value: 0 });
2822
- const functionCounterRef = (collections.functionCounterRef ?? { value: 0 });
2823
- const _httpRequestCounterRef = (collections.httpRequestCounterRef ?? { value: 0 });
2824
- const literalCounterRef = (collections.literalCounterRef ?? { value: 0 });
2825
- const _anonymousFunctionCounterRef = (collections.anonymousFunctionCounterRef ?? { value: 0 });
2826
- const scopeTracker = collections.scopeTracker;
2827
- // Object literal tracking (REG-328)
2828
- if (!collections.objectLiterals) {
2829
- collections.objectLiterals = [];
2830
- }
2831
- if (!collections.objectProperties) {
2832
- collections.objectProperties = [];
2833
- }
2834
- if (!collections.objectLiteralCounterRef) {
2835
- collections.objectLiteralCounterRef = { value: 0 };
2836
- }
2837
- const objectLiterals = collections.objectLiterals;
2838
- const objectProperties = collections.objectProperties;
2839
- const objectLiteralCounterRef = collections.objectLiteralCounterRef;
2840
- const returnStatements = (collections.returnStatements ?? []);
2841
- // Initialize yieldExpressions if not exist to ensure nested function calls share same array
2842
- if (!collections.yieldExpressions) {
2843
- collections.yieldExpressions = [];
2844
- }
2845
- const yieldExpressions = collections.yieldExpressions;
2846
- const _parameters = (collections.parameters ?? []);
2847
- // Control flow collections (Phase 2: LOOP nodes)
2848
- // Initialize if not exist to ensure nested function calls share same arrays
2849
- if (!collections.loops) {
2850
- collections.loops = [];
2851
- }
2852
- if (!collections.loopCounterRef) {
2853
- collections.loopCounterRef = { value: 0 };
2854
- }
2855
- const loops = collections.loops;
2856
- const loopCounterRef = collections.loopCounterRef;
2857
- const updateExpressions = (collections.updateExpressions ?? []);
2858
- const processedNodes = collections.processedNodes ?? {
2859
- functions: new Set(),
2860
- classes: new Set(),
2861
- imports: new Set(),
2862
- exports: new Set(),
2863
- variables: new Set(),
2864
- callSites: new Set(),
2865
- methodCalls: new Set(),
2866
- varDecls: new Set(),
2867
- eventListeners: new Set()
2868
- };
2869
- const parentScopeVariables = new Set();
2870
- const processedCallSites = processedNodes.callSites;
2871
- const _processedVarDecls = processedNodes.varDecls;
2872
- const processedMethodCalls = processedNodes.methodCalls;
2873
- const _processedEventListeners = processedNodes.eventListeners;
2874
- // Track if/else scope transitions (Phase 3: extended with branchId)
2875
- const ifElseScopeMap = new Map();
2876
- // Ensure branches and branchCounterRef are initialized (used by IfStatement and SwitchStatement)
2877
- if (!collections.branches) {
2878
- collections.branches = [];
2879
- }
2880
- if (!collections.branchCounterRef) {
2881
- collections.branchCounterRef = { value: 0 };
2882
- }
2883
- const branches = collections.branches;
2884
- const branchCounterRef = collections.branchCounterRef;
2885
- // Phase 4: Initialize try/catch/finally collections and counters
2886
- if (!collections.tryBlocks) {
2887
- collections.tryBlocks = [];
2888
- }
2889
- if (!collections.catchBlocks) {
2890
- collections.catchBlocks = [];
2891
- }
2892
- if (!collections.finallyBlocks) {
2893
- collections.finallyBlocks = [];
2894
- }
2895
- if (!collections.tryBlockCounterRef) {
2896
- collections.tryBlockCounterRef = { value: 0 };
2897
- }
2898
- if (!collections.catchBlockCounterRef) {
2899
- collections.catchBlockCounterRef = { value: 0 };
2900
- }
2901
- if (!collections.finallyBlockCounterRef) {
2902
- collections.finallyBlockCounterRef = { value: 0 };
2903
- }
2904
- const tryBlocks = collections.tryBlocks;
2905
- const catchBlocks = collections.catchBlocks;
2906
- const finallyBlocks = collections.finallyBlocks;
2907
- const tryBlockCounterRef = collections.tryBlockCounterRef;
2908
- const catchBlockCounterRef = collections.catchBlockCounterRef;
2909
- const finallyBlockCounterRef = collections.finallyBlockCounterRef;
2910
- // Track try/catch/finally scope transitions
2911
- const tryScopeMap = new Map();
2912
- // REG-334: Use shared Promise executor contexts from collections.
2913
- // These are populated by module-level NewExpression handler and function-level NewExpression handler.
2914
- if (!collections.promiseExecutorContexts) {
2915
- collections.promiseExecutorContexts = new Map();
2916
- }
2917
- const promiseExecutorContexts = collections.promiseExecutorContexts;
2918
- // Initialize promiseResolutions array if not exists
2919
- if (!collections.promiseResolutions) {
2920
- collections.promiseResolutions = [];
2921
- }
2922
- const promiseResolutions = collections.promiseResolutions;
2923
- // REG-311: Initialize rejectionPatterns and catchesFromInfos collections
2924
- if (!collections.rejectionPatterns) {
2925
- collections.rejectionPatterns = [];
2926
- }
2927
- if (!collections.catchesFromInfos) {
2928
- collections.catchesFromInfos = [];
2929
- }
2930
- const rejectionPatterns = collections.rejectionPatterns;
2931
- const catchesFromInfos = collections.catchesFromInfos;
2932
- // Dynamic scope ID stack for CONTAINS edges
2933
- // Starts with the function body scope, gets updated as we enter/exit conditional scopes
2934
- const scopeIdStack = [parentScopeId];
2935
- const getCurrentScopeId = () => scopeIdStack[scopeIdStack.length - 1];
2936
- // Determine the ID of the function we're analyzing for RETURNS edges
2937
- // Find by matching file/line/column in functions collection (it was just added by the visitor)
2938
- // REG-271: Skip for StaticBlock (static blocks don't have RETURNS edges or control flow metadata)
2939
- const funcNode = funcPath.node;
2940
- const functionNode = t.isFunction(funcNode) ? funcNode : null;
2941
- const functionPath = functionNode ? funcPath : null;
2942
- const funcLine = getLine(funcNode);
2943
- const funcColumn = getColumn(funcNode);
2944
- let currentFunctionId = null;
2945
- // StaticBlock is not a function - skip function matching for RETURNS edges
2946
- // For StaticBlock, matchingFunction will be undefined
2947
- const matchingFunction = funcNode.type !== 'StaticBlock'
2948
- ? functions.find(f => f.file === module.file &&
2949
- f.line === funcLine &&
2950
- (f.column === undefined || f.column === funcColumn))
2951
- : undefined;
2952
- if (matchingFunction) {
2953
- currentFunctionId = matchingFunction.id;
2954
- }
2955
- // Phase 6 (REG-267): Control flow tracking state for cyclomatic complexity
2956
- const controlFlowState = {
2957
- branchCount: 0, // if/switch statements
2958
- loopCount: 0, // for/while/do-while/for-in/for-of
2959
- caseCount: 0, // switch cases (excluding default)
2960
- logicalOpCount: 0, // && and || in conditions
2961
- hasTryCatch: false,
2962
- hasEarlyReturn: false,
2963
- hasThrow: false,
2964
- returnCount: 0, // Track total return count for early return detection
2965
- totalStatements: 0, // Track if there are statements after returns
2966
- // REG-311: Try block depth counter for O(1) isInsideTry detection
2967
- tryBlockDepth: 0
2968
- };
2969
- // Handle implicit return for THIS arrow function if it has an expression body
2970
- // e.g., `const double = x => x * 2;` - the function we're analyzing IS an arrow with expression body
2971
- if (t.isArrowFunctionExpression(funcNode) && !t.isBlockStatement(funcNode.body) && currentFunctionId) {
2972
- const bodyExpr = funcNode.body;
2973
- const bodyLine = getLine(bodyExpr);
2974
- const bodyColumn = getColumn(bodyExpr);
2975
- // Extract expression-specific info using shared method
2976
- const exprInfo = this.extractReturnExpressionInfo(bodyExpr, module, literals, literalCounterRef, funcLine, funcColumn, 'implicit_return');
2977
- const returnInfo = {
2978
- parentFunctionId: currentFunctionId,
2979
- file: module.file,
2980
- line: bodyLine,
2981
- column: bodyColumn,
2982
- returnValueType: 'NONE',
2983
- isImplicitReturn: true,
2984
- ...exprInfo,
2985
- };
2986
- returnStatements.push(returnInfo);
2987
- }
2988
- funcPath.traverse({
2989
- VariableDeclaration: (varPath) => {
2990
- this.handleVariableDeclaration(varPath, getCurrentScopeId(), module, variableDeclarations, classInstantiations, literals, variableAssignments, varDeclCounterRef, literalCounterRef, scopeTracker, parentScopeVariables, objectLiterals, objectProperties, objectLiteralCounterRef);
2991
- },
2992
- // Detect indexed array assignments: arr[i] = value
2993
- AssignmentExpression: (assignPath) => {
2994
- const assignNode = assignPath.node;
2995
- // === VARIABLE REASSIGNMENT (REG-290) ===
2996
- // Check if LHS is simple identifier (not obj.prop, not arr[i])
2997
- // Must be checked FIRST before array/object mutation handlers
2998
- if (assignNode.left.type === 'Identifier') {
2999
- // Initialize collection if not exists
3000
- if (!collections.variableReassignments) {
3001
- collections.variableReassignments = [];
3002
- }
3003
- const variableReassignments = collections.variableReassignments;
3004
- this.detectVariableReassignment(assignNode, module, variableReassignments, scopeTracker);
3005
- }
3006
- // === END VARIABLE REASSIGNMENT ===
3007
- // Initialize collection if not exists
3008
- if (!collections.arrayMutations) {
3009
- collections.arrayMutations = [];
3010
- }
3011
- const arrayMutations = collections.arrayMutations;
3012
- // Check for indexed array assignment: arr[i] = value
3013
- this.detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker, collections);
3014
- // Initialize object mutations collection if not exists
3015
- if (!collections.objectMutations) {
3016
- collections.objectMutations = [];
3017
- }
3018
- const objectMutations = collections.objectMutations;
3019
- // Check for object property assignment: obj.prop = value
3020
- this.detectObjectPropertyAssignment(assignNode, module, objectMutations, scopeTracker);
3021
- },
3022
- // Handle return statements for RETURNS edges
3023
- ReturnStatement: (returnPath) => {
3024
- // Skip if we couldn't determine the function ID
3025
- if (!currentFunctionId) {
3026
- return;
3027
- }
3028
- // Skip if this return is inside a nested function (not the function we're analyzing)
3029
- // Check if there's a function ancestor BETWEEN us and funcNode
3030
- // Stop checking once we reach funcNode - parents above funcNode are outside scope
3031
- let parent = returnPath.parentPath;
3032
- let isInsideConditional = false;
3033
- while (parent) {
3034
- // If we've reached funcNode, we're done checking - this return belongs to funcNode
3035
- if (parent.node === funcNode) {
3036
- break;
3037
- }
3038
- if (t.isFunction(parent.node)) {
3039
- // Found a function between returnPath and funcNode - this return is inside a nested function
3040
- return;
3041
- }
3042
- // Track if return is inside a conditional block (if/else, switch case, loop, try/catch)
3043
- if (t.isIfStatement(parent.node) ||
3044
- t.isSwitchCase(parent.node) ||
3045
- t.isLoop(parent.node) ||
3046
- t.isTryStatement(parent.node) ||
3047
- t.isCatchClause(parent.node)) {
3048
- isInsideConditional = true;
3049
- }
3050
- parent = parent.parentPath;
3051
- }
3052
- // Phase 6 (REG-267): Track return count and early return detection
3053
- controlFlowState.returnCount++;
3054
- // A return is "early" if it's inside a conditional structure
3055
- // (More returns after this one indicate the function doesn't always end here)
3056
- if (isInsideConditional) {
3057
- controlFlowState.hasEarlyReturn = true;
3058
- }
3059
- const returnNode = returnPath.node;
3060
- const returnLine = getLine(returnNode);
3061
- const returnColumn = getColumn(returnNode);
3062
- // Handle bare return; (no value)
3063
- if (!returnNode.argument) {
3064
- // Skip - no data flow value
3065
- return;
3066
- }
3067
- const arg = returnNode.argument;
3068
- // Extract expression-specific info using shared method
3069
- const exprInfo = this.extractReturnExpressionInfo(arg, module, literals, literalCounterRef, returnLine, returnColumn, 'return');
3070
- const returnInfo = {
3071
- parentFunctionId: currentFunctionId,
3072
- file: module.file,
3073
- line: returnLine,
3074
- column: returnColumn,
3075
- returnValueType: 'NONE',
3076
- ...exprInfo,
3077
- };
3078
- returnStatements.push(returnInfo);
3079
- },
3080
- // Phase 6 (REG-267): Track throw statements for control flow metadata
3081
- // REG-311: Also detect async_throw rejection patterns
3082
- ThrowStatement: (throwPath) => {
3083
- // Skip if this throw is inside a nested function (not the function we're analyzing)
3084
- let parent = throwPath.parentPath;
3085
- while (parent) {
3086
- if (t.isFunction(parent.node) && parent.node !== funcNode) {
3087
- // This throw is inside a nested function - skip it
3088
- return;
3089
- }
3090
- parent = parent.parentPath;
3091
- }
3092
- controlFlowState.hasThrow = true;
3093
- // REG-311: Track rejection patterns for async functions
3094
- const isAsyncFunction = functionNode?.async === true;
3095
- if (isAsyncFunction && currentFunctionId && functionNode && functionPath) {
3096
- const throwNode = throwPath.node;
3097
- const arg = throwNode.argument;
3098
- const throwLine = getLine(throwNode);
3099
- const throwColumn = getColumn(throwNode);
3100
- // Case 1: throw new Error() or throw new CustomError()
3101
- if (arg && t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
3102
- rejectionPatterns.push({
3103
- functionId: currentFunctionId,
3104
- errorClassName: arg.callee.name,
3105
- rejectionType: 'async_throw',
3106
- file: module.file,
3107
- line: throwLine,
3108
- column: throwColumn
3109
- });
3110
- }
3111
- // Case 2: throw identifier - needs micro-trace
3112
- else if (arg && t.isIdentifier(arg)) {
3113
- const varName = arg.name;
3114
- // Check if it's a parameter
3115
- const isParameter = functionNode.params.some(param => t.isIdentifier(param) && param.name === varName);
3116
- if (isParameter) {
3117
- // Parameter forwarding - can't resolve statically
3118
- rejectionPatterns.push({
3119
- functionId: currentFunctionId,
3120
- errorClassName: null,
3121
- rejectionType: 'variable_parameter',
3122
- file: module.file,
3123
- line: throwLine,
3124
- column: throwColumn,
3125
- sourceVariableName: varName
3126
- });
3127
- }
3128
- else {
3129
- // Try micro-trace
3130
- const { errorClassName, tracePath } = this.microTraceToErrorClass(varName, functionPath, variableDeclarations);
3131
- rejectionPatterns.push({
3132
- functionId: currentFunctionId,
3133
- errorClassName,
3134
- rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
3135
- file: module.file,
3136
- line: throwLine,
3137
- column: throwColumn,
3138
- sourceVariableName: varName,
3139
- tracePath
3140
- });
3141
- }
3142
- }
3143
- }
3144
- },
3145
- // Handle yield expressions for YIELDS/DELEGATES_TO edges (REG-270)
3146
- YieldExpression: (yieldPath) => {
3147
- // Skip if we couldn't determine the function ID
3148
- if (!currentFunctionId) {
3149
- return;
3150
- }
3151
- // Skip if this yield is inside a nested function (not the function we're analyzing)
3152
- // Check if there's a function ancestor BETWEEN us and funcNode
3153
- let parent = yieldPath.parentPath;
3154
- while (parent) {
3155
- // If we've reached funcNode, we're done checking - this yield belongs to funcNode
3156
- if (parent.node === funcNode) {
3157
- break;
3158
- }
3159
- if (t.isFunction(parent.node)) {
3160
- // Found a function between yieldPath and funcNode - this yield is inside a nested function
3161
- return;
3162
- }
3163
- parent = parent.parentPath;
3164
- }
3165
- const yieldNode = yieldPath.node;
3166
- const yieldLine = getLine(yieldNode);
3167
- const yieldColumn = getColumn(yieldNode);
3168
- const isDelegate = yieldNode.delegate ?? false;
3169
- // Handle bare yield; (no value) - only valid for non-delegate yield
3170
- if (!yieldNode.argument && !isDelegate) {
3171
- // Skip - no data flow value
3172
- return;
3173
- }
3174
- // For yield* without argument (syntax error in practice, but handle gracefully)
3175
- if (!yieldNode.argument) {
3176
- return;
3177
- }
3178
- const arg = yieldNode.argument;
3179
- // Extract expression-specific info using shared method
3180
- // Note: We reuse extractReturnExpressionInfo since yield values have identical semantics
3181
- const exprInfo = this.extractReturnExpressionInfo(arg, module, literals, literalCounterRef, yieldLine, yieldColumn, 'yield');
3182
- // Map ReturnStatementInfo fields to YieldExpressionInfo fields
3183
- const yieldInfo = {
3184
- parentFunctionId: currentFunctionId,
3185
- file: module.file,
3186
- line: yieldLine,
3187
- column: yieldColumn,
3188
- isDelegate,
3189
- yieldValueType: exprInfo.returnValueType ?? 'NONE',
3190
- yieldValueName: exprInfo.returnValueName,
3191
- yieldValueId: exprInfo.returnValueId,
3192
- yieldValueLine: exprInfo.returnValueLine,
3193
- yieldValueColumn: exprInfo.returnValueColumn,
3194
- yieldValueCallName: exprInfo.returnValueCallName,
3195
- expressionType: exprInfo.expressionType,
3196
- operator: exprInfo.operator,
3197
- leftSourceName: exprInfo.leftSourceName,
3198
- rightSourceName: exprInfo.rightSourceName,
3199
- consequentSourceName: exprInfo.consequentSourceName,
3200
- alternateSourceName: exprInfo.alternateSourceName,
3201
- object: exprInfo.object,
3202
- property: exprInfo.property,
3203
- computed: exprInfo.computed,
3204
- objectSourceName: exprInfo.objectSourceName,
3205
- expressionSourceNames: exprInfo.expressionSourceNames,
3206
- unaryArgSourceName: exprInfo.unaryArgSourceName,
3207
- };
3208
- yieldExpressions.push(yieldInfo);
3209
- },
3210
- ForStatement: this.createLoopScopeHandler('for', 'for-loop', 'for', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
3211
- ForInStatement: this.createLoopScopeHandler('for-in', 'for-in-loop', 'for-in', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
3212
- ForOfStatement: this.createLoopScopeHandler('for-of', 'for-of-loop', 'for-of', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
3213
- WhileStatement: this.createLoopScopeHandler('while', 'while-loop', 'while', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
3214
- DoWhileStatement: this.createLoopScopeHandler('do-while', 'do-while-loop', 'do-while', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
3215
- // Phase 4 (REG-267): Now creates TRY_BLOCK, CATCH_BLOCK, FINALLY_BLOCK nodes
3216
- TryStatement: this.createTryStatementHandler(parentScopeId, module, scopes, tryBlocks, catchBlocks, finallyBlocks, scopeCounterRef, tryBlockCounterRef, catchBlockCounterRef, finallyBlockCounterRef, scopeTracker, tryScopeMap, scopeIdStack, controlFlowState),
3217
- CatchClause: this.createCatchClauseHandler(module, variableDeclarations, varDeclCounterRef, scopeTracker, tryScopeMap, scopeIdStack, controlFlowState),
3218
- SwitchStatement: (switchPath) => {
3219
- this.handleSwitchStatement(switchPath, parentScopeId, module, collections, scopeTracker, controlFlowState);
3220
- },
3221
- FunctionDeclaration: (funcDeclPath) => {
3222
- const node = funcDeclPath.node;
3223
- const funcName = node.id ? node.id.name : this.generateAnonymousName(scopeTracker);
3224
- // Use semantic ID as primary ID when scopeTracker available
3225
- const legacyId = `FUNCTION#${funcName}#${module.file}#${getLine(node)}:${getColumn(node)}:${functionCounterRef.value++}`;
3226
- const functionId = scopeTracker
3227
- ? computeSemanticId('FUNCTION', funcName, scopeTracker.getContext())
3228
- : legacyId;
3229
- functions.push({
3230
- id: functionId,
3231
- type: 'FUNCTION',
3232
- name: funcName,
3233
- file: module.file,
3234
- line: getLine(node),
3235
- column: getColumn(node),
3236
- async: node.async || false,
3237
- generator: node.generator || false,
3238
- parentScopeId
3239
- });
3240
- const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${getLine(node)}`;
3241
- const closureSemanticId = this.generateSemanticId('closure', scopeTracker);
3242
- scopes.push({
3243
- id: nestedScopeId,
3244
- type: 'SCOPE',
3245
- scopeType: 'closure',
3246
- name: `${funcName}:body`,
3247
- semanticId: closureSemanticId,
3248
- conditional: false,
3249
- file: module.file,
3250
- line: getLine(node),
3251
- parentFunctionId: functionId,
3252
- capturesFrom: parentScopeId
3253
- });
3254
- // Enter nested function scope for semantic ID generation
3255
- if (scopeTracker) {
3256
- scopeTracker.enterScope(funcName, 'function');
3257
- }
3258
- this.analyzeFunctionBody(funcDeclPath, nestedScopeId, module, collections);
3259
- if (scopeTracker) {
3260
- scopeTracker.exitScope();
3261
- }
3262
- funcDeclPath.skip();
3263
- },
3264
- FunctionExpression: (funcPath) => {
3265
- const node = funcPath.node;
3266
- const funcName = node.id ? node.id.name : this.generateAnonymousName(scopeTracker);
3267
- // Use semantic ID as primary ID when scopeTracker available
3268
- const legacyId = `FUNCTION#${funcName}#${module.file}#${getLine(node)}:${getColumn(node)}:${functionCounterRef.value++}`;
3269
- const functionId = scopeTracker
3270
- ? computeSemanticId('FUNCTION', funcName, scopeTracker.getContext())
3271
- : legacyId;
3272
- functions.push({
3273
- id: functionId,
3274
- type: 'FUNCTION',
3275
- name: funcName,
3276
- file: module.file,
3277
- line: getLine(node),
3278
- column: getColumn(node),
3279
- async: node.async || false,
3280
- generator: node.generator || false,
3281
- parentScopeId
3282
- });
3283
- const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${getLine(node)}`;
3284
- const closureSemanticId = this.generateSemanticId('closure', scopeTracker);
3285
- scopes.push({
3286
- id: nestedScopeId,
3287
- type: 'SCOPE',
3288
- scopeType: 'closure',
3289
- name: `${funcName}:body`,
3290
- semanticId: closureSemanticId,
3291
- conditional: false,
3292
- file: module.file,
3293
- line: getLine(node),
3294
- parentFunctionId: functionId,
3295
- capturesFrom: parentScopeId
3296
- });
3297
- // Enter nested function scope for semantic ID generation
3298
- if (scopeTracker) {
3299
- scopeTracker.enterScope(funcName, 'function');
3300
- }
3301
- this.analyzeFunctionBody(funcPath, nestedScopeId, module, collections);
3302
- if (scopeTracker) {
3303
- scopeTracker.exitScope();
3304
- }
3305
- funcPath.skip();
3306
- },
3307
- ArrowFunctionExpression: (arrowPath) => {
3308
- const node = arrowPath.node;
3309
- const line = getLine(node);
3310
- const column = getColumn(node);
3311
- // Определяем имя (anonymous если не присвоено переменной)
3312
- const parent = arrowPath.parent;
3313
- let funcName;
3314
- if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id)) {
3315
- funcName = parent.id.name;
3316
- }
3317
- else {
3318
- // Используем scope-level счётчик для стабильного semanticId
3319
- funcName = this.generateAnonymousName(scopeTracker);
3320
- }
3321
- // Use semantic ID as primary ID when scopeTracker available
3322
- const legacyId = `FUNCTION#${funcName}:${line}:${column}:${functionCounterRef.value++}`;
3323
- const functionId = scopeTracker
3324
- ? computeSemanticId('FUNCTION', funcName, scopeTracker.getContext())
3325
- : legacyId;
3326
- functions.push({
3327
- id: functionId,
3328
- type: 'FUNCTION',
3329
- name: funcName,
3330
- file: module.file,
3331
- line,
3332
- column,
3333
- async: node.async || false,
3334
- arrowFunction: true,
3335
- parentScopeId
3336
- });
3337
- if (node.body.type === 'BlockStatement') {
3338
- const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${line}`;
3339
- const arrowSemanticId = this.generateSemanticId('arrow_body', scopeTracker);
3340
- scopes.push({
3341
- id: nestedScopeId,
3342
- type: 'SCOPE',
3343
- scopeType: 'arrow_body',
3344
- name: `${funcName}:body`,
3345
- semanticId: arrowSemanticId,
3346
- conditional: false,
3347
- file: module.file,
3348
- line,
3349
- parentFunctionId: functionId,
3350
- capturesFrom: parentScopeId
3351
- });
3352
- // Enter arrow function scope for semantic ID generation
3353
- if (scopeTracker) {
3354
- scopeTracker.enterScope(funcName, 'arrow');
3355
- }
3356
- this.analyzeFunctionBody(arrowPath, nestedScopeId, module, collections);
3357
- if (scopeTracker) {
3358
- scopeTracker.exitScope();
3359
- }
3360
- }
3361
- else {
3362
- // Arrow function with expression body (implicit return)
3363
- // e.g., x => x * 2, () => 42
3364
- const bodyExpr = node.body;
3365
- const bodyLine = getLine(bodyExpr);
3366
- const bodyColumn = getColumn(bodyExpr);
3367
- // Extract expression-specific info using shared method
3368
- const exprInfo = this.extractReturnExpressionInfo(bodyExpr, module, literals, literalCounterRef, line, column, 'implicit_return');
3369
- const returnInfo = {
3370
- parentFunctionId: functionId,
3371
- file: module.file,
3372
- line: bodyLine,
3373
- column: bodyColumn,
3374
- returnValueType: 'NONE',
3375
- isImplicitReturn: true,
3376
- ...exprInfo,
3377
- };
3378
- returnStatements.push(returnInfo);
3379
- }
3380
- arrowPath.skip();
3381
- },
3382
- UpdateExpression: (updatePath) => {
3383
- const updateNode = updatePath.node;
3384
- // REG-288/REG-312: Collect update expression info for graph building
3385
- this.collectUpdateExpression(updateNode, module, updateExpressions, getCurrentScopeId(), scopeTracker);
3386
- // Legacy behavior: update scope.modifies for IDENTIFIER targets
3387
- if (updateNode.argument.type === 'Identifier') {
3388
- const varName = updateNode.argument.name;
3389
- // Find variable by name - could be from parent scope or declarations
3390
- const fromParentScope = Array.from(parentScopeVariables).find(v => v.name === varName);
3391
- const fromDeclarations = variableDeclarations.find(v => v.name === varName);
3392
- const variable = fromParentScope ?? fromDeclarations;
3393
- if (variable) {
3394
- const scope = scopes.find(s => s.id === parentScopeId);
3395
- if (scope) {
3396
- if (!scope.modifies)
3397
- scope.modifies = [];
3398
- scope.modifies.push({
3399
- variableId: variable.id,
3400
- variableName: varName,
3401
- line: getLine(updateNode)
3402
- });
3403
- }
3404
- }
3405
- }
3406
- },
3407
- // IF statements - создаём условные scope и обходим содержимое для CALL узлов
3408
- // Phase 3 (REG-267): Now creates BRANCH nodes with branchType='if'
3409
- IfStatement: this.createIfStatementHandler(parentScopeId, module, scopes, branches, ifScopeCounterRef, branchCounterRef, scopeTracker, collections.code ?? '', ifElseScopeMap, scopeIdStack, controlFlowState, this.countLogicalOperators.bind(this)),
3410
- // Ternary expressions (REG-287): Creates BRANCH nodes with branchType='ternary'
3411
- ConditionalExpression: this.createConditionalExpressionHandler(parentScopeId, module, branches, branchCounterRef, scopeTracker, scopeIdStack, controlFlowState, this.countLogicalOperators.bind(this)),
3412
- // Track when we enter the alternate (else) block of an IfStatement
3413
- BlockStatement: this.createBlockStatementHandler(scopeTracker, ifElseScopeMap, tryScopeMap, scopeIdStack),
3414
- // Function call expressions
3415
- CallExpression: (callPath) => {
3416
- // REG-311: Detect isAwaited (parent is AwaitExpression)
3417
- const parent = callPath.parentPath;
3418
- const isAwaited = parent?.isAwaitExpression() ?? false;
3419
- // REG-311: Detect isInsideTry (O(1) via depth counter)
3420
- const isInsideTry = controlFlowState.tryBlockDepth > 0;
3421
- this.handleCallExpression(callPath.node, processedCallSites, processedMethodCalls, callSites, methodCalls, module, callSiteCounterRef, scopeTracker, getCurrentScopeId(), collections, isAwaited, isInsideTry);
3422
- // REG-334: Check for resolve/reject calls inside Promise executors
3423
- const callNode = callPath.node;
3424
- if (t.isIdentifier(callNode.callee)) {
3425
- const calleeName = callNode.callee.name;
3426
- // Walk up function parents to find Promise executor context
3427
- // This handles nested callbacks like: new Promise((resolve) => { db.query((err, data) => { resolve(data); }); });
3428
- let funcParent = callPath.getFunctionParent();
3429
- while (funcParent) {
3430
- const funcNode = funcParent.node;
3431
- const funcKey = `${funcNode.start}:${funcNode.end}`;
3432
- const context = promiseExecutorContexts.get(funcKey);
3433
- if (context) {
3434
- const isResolve = calleeName === context.resolveName;
3435
- const isReject = calleeName === context.rejectName;
3436
- if (isResolve || isReject) {
3437
- // Find the CALL node ID for this resolve/reject call
3438
- // It was just added by handleCallExpression
3439
- const callLine = getLine(callNode);
3440
- const callColumn = getColumn(callNode);
3441
- // Find matching call site that was just added
3442
- const resolveCall = callSites.find(cs => cs.name === calleeName &&
3443
- cs.file === module.file &&
3444
- cs.line === callLine &&
3445
- cs.column === callColumn);
3446
- if (resolveCall) {
3447
- promiseResolutions.push({
3448
- callId: resolveCall.id,
3449
- constructorCallId: context.constructorCallId,
3450
- isReject,
3451
- file: module.file,
3452
- line: callLine
3453
- });
3454
- // REG-334: Collect arguments for resolve/reject calls
3455
- // This enables traceValues to follow PASSES_ARGUMENT edges
3456
- if (!collections.callArguments) {
3457
- collections.callArguments = [];
3458
- }
3459
- const callArgumentsArr = collections.callArguments;
3460
- // Process arguments (typically just one: resolve(value))
3461
- callNode.arguments.forEach((arg, argIndex) => {
3462
- const argInfo = {
3463
- callId: resolveCall.id,
3464
- argIndex,
3465
- file: module.file,
3466
- line: getLine(arg),
3467
- column: getColumn(arg)
3468
- };
3469
- // Handle different argument types
3470
- if (t.isIdentifier(arg)) {
3471
- argInfo.targetType = 'VARIABLE';
3472
- argInfo.targetName = arg.name;
3473
- }
3474
- else if (t.isLiteral(arg) && !t.isTemplateLiteral(arg)) {
3475
- // Create LITERAL node for the argument value
3476
- const literalValue = ExpressionEvaluator.extractLiteralValue(arg);
3477
- if (literalValue !== null) {
3478
- const argLine = getLine(arg);
3479
- const argColumn = getColumn(arg);
3480
- const literalId = `LITERAL#arg${argIndex}#${module.file}#${argLine}:${argColumn}:${literalCounterRef.value++}`;
3481
- literals.push({
3482
- id: literalId,
3483
- type: 'LITERAL',
3484
- value: literalValue,
3485
- valueType: typeof literalValue,
3486
- file: module.file,
3487
- line: argLine,
3488
- column: argColumn,
3489
- parentCallId: resolveCall.id,
3490
- argIndex
3491
- });
3492
- argInfo.targetType = 'LITERAL';
3493
- argInfo.targetId = literalId;
3494
- argInfo.literalValue = literalValue;
3495
- }
3496
- }
3497
- else if (t.isCallExpression(arg)) {
3498
- argInfo.targetType = 'CALL';
3499
- argInfo.nestedCallLine = getLine(arg);
3500
- argInfo.nestedCallColumn = getColumn(arg);
3501
- }
3502
- else {
3503
- argInfo.targetType = 'EXPRESSION';
3504
- argInfo.expressionType = arg.type;
3505
- }
3506
- callArgumentsArr.push(argInfo);
3507
- });
3508
- }
3509
- break; // Found context, stop searching
3510
- }
3511
- }
3512
- funcParent = funcParent.getFunctionParent();
3513
- }
3514
- // REG-311: Detect executor_reject pattern - reject(new Error()) inside Promise executor
3515
- // Walk up to find Promise executor context and check if this is reject call with NewExpression arg
3516
- funcParent = callPath.getFunctionParent();
3517
- while (funcParent && currentFunctionId) {
3518
- const funcNode = funcParent.node;
3519
- const funcKey = `${funcNode.start}:${funcNode.end}`;
3520
- const context = promiseExecutorContexts.get(funcKey);
3521
- if (context && calleeName === context.rejectName && callNode.arguments.length > 0) {
3522
- // REG-311: Use the creator function's ID (the function that created the Promise),
3523
- // not the executor's ID
3524
- const targetFunctionId = context.creatorFunctionId || currentFunctionId;
3525
- const arg = callNode.arguments[0];
3526
- const callLine = getLine(callNode);
3527
- const callColumn = getColumn(callNode);
3528
- // Case 1: reject(new Error())
3529
- if (t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
3530
- rejectionPatterns.push({
3531
- functionId: targetFunctionId,
3532
- errorClassName: arg.callee.name,
3533
- rejectionType: 'executor_reject',
3534
- file: module.file,
3535
- line: callLine,
3536
- column: callColumn
3537
- });
3538
- }
3539
- // Case 2: reject(err) where err is variable
3540
- else if (t.isIdentifier(arg)) {
3541
- const varName = arg.name;
3542
- // Check if it's a parameter of ANY containing function (executor, outer, etc.)
3543
- // Walk up the function chain to find if varName is a parameter
3544
- let isParameter = false;
3545
- let checkParent = funcParent;
3546
- while (checkParent) {
3547
- if (t.isFunction(checkParent.node)) {
3548
- if (checkParent.node.params.some(p => t.isIdentifier(p) && p.name === varName)) {
3549
- isParameter = true;
3550
- break;
3551
- }
3552
- }
3553
- checkParent = checkParent.getFunctionParent();
3554
- }
3555
- if (isParameter) {
3556
- rejectionPatterns.push({
3557
- functionId: targetFunctionId,
3558
- errorClassName: null,
3559
- rejectionType: 'variable_parameter',
3560
- file: module.file,
3561
- line: callLine,
3562
- column: callColumn,
3563
- sourceVariableName: varName
3564
- });
3565
- }
3566
- else {
3567
- // Try micro-trace
3568
- const { errorClassName, tracePath } = this.microTraceToErrorClass(varName, funcParent, variableDeclarations);
3569
- rejectionPatterns.push({
3570
- functionId: targetFunctionId,
3571
- errorClassName,
3572
- rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
3573
- file: module.file,
3574
- line: callLine,
3575
- column: callColumn,
3576
- sourceVariableName: varName,
3577
- tracePath
3578
- });
3579
- }
3580
- }
3581
- break;
3582
- }
3583
- funcParent = funcParent.getFunctionParent();
3584
- }
3585
- }
3586
- // REG-311: Detect Promise.reject(new Error()) pattern
3587
- if (t.isMemberExpression(callNode.callee) && currentFunctionId) {
3588
- const memberCallee = callNode.callee;
3589
- if (t.isIdentifier(memberCallee.object) &&
3590
- memberCallee.object.name === 'Promise' &&
3591
- t.isIdentifier(memberCallee.property) &&
3592
- memberCallee.property.name === 'reject' &&
3593
- callNode.arguments.length > 0) {
3594
- const arg = callNode.arguments[0];
3595
- const callLine = getLine(callNode);
3596
- const callColumn = getColumn(callNode);
3597
- // Case 1: Promise.reject(new Error())
3598
- if (t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
3599
- rejectionPatterns.push({
3600
- functionId: currentFunctionId,
3601
- errorClassName: arg.callee.name,
3602
- rejectionType: 'promise_reject',
3603
- file: module.file,
3604
- line: callLine,
3605
- column: callColumn
3606
- });
3607
- }
3608
- // Case 2: Promise.reject(err) where err is variable
3609
- else if (t.isIdentifier(arg)) {
3610
- const varName = arg.name;
3611
- // Check if it's a parameter of containing function
3612
- const isParameter = functionNode
3613
- ? functionNode.params.some(param => t.isIdentifier(param) && param.name === varName)
3614
- : false;
3615
- if (isParameter) {
3616
- rejectionPatterns.push({
3617
- functionId: currentFunctionId,
3618
- errorClassName: null,
3619
- rejectionType: 'variable_parameter',
3620
- file: module.file,
3621
- line: callLine,
3622
- column: callColumn,
3623
- sourceVariableName: varName
3624
- });
3625
- }
3626
- else {
3627
- // Try micro-trace
3628
- if (!functionPath) {
3629
- rejectionPatterns.push({
3630
- functionId: currentFunctionId,
3631
- errorClassName: null,
3632
- rejectionType: 'variable_unknown',
3633
- file: module.file,
3634
- line: callLine,
3635
- column: callColumn,
3636
- sourceVariableName: varName,
3637
- tracePath: [varName]
3638
- });
3639
- return;
3640
- }
3641
- const { errorClassName, tracePath } = this.microTraceToErrorClass(varName, functionPath, variableDeclarations);
3642
- rejectionPatterns.push({
3643
- functionId: currentFunctionId,
3644
- errorClassName,
3645
- rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
3646
- file: module.file,
3647
- line: callLine,
3648
- column: callColumn,
3649
- sourceVariableName: varName,
3650
- tracePath
3651
- });
3652
- }
3653
- }
3654
- }
3655
- }
3656
- },
3657
- // NewExpression (constructor calls)
3658
- NewExpression: (newPath) => {
3659
- const newNode = newPath.node;
3660
- const nodeKey = `new:${newNode.start}:${newNode.end}`;
3661
- // Determine className from callee
3662
- let className = null;
3663
- if (newNode.callee.type === 'Identifier') {
3664
- className = newNode.callee.name;
3665
- }
3666
- else if (newNode.callee.type === 'MemberExpression' && newNode.callee.property.type === 'Identifier') {
3667
- className = newNode.callee.property.name;
3668
- }
3669
- // Create CONSTRUCTOR_CALL node (always, for all NewExpressions)
3670
- if (className) {
3671
- const constructorKey = `constructor:${nodeKey}`;
3672
- if (!processedCallSites.has(constructorKey)) {
3673
- processedCallSites.add(constructorKey);
3674
- const line = getLine(newNode);
3675
- const column = getColumn(newNode);
3676
- const constructorCallId = ConstructorCallNode.generateId(className, module.file, line, column);
3677
- const isBuiltin = ConstructorCallNode.isBuiltinConstructor(className);
3678
- constructorCalls.push({
3679
- id: constructorCallId,
3680
- type: 'CONSTRUCTOR_CALL',
3681
- className,
3682
- isBuiltin,
3683
- file: module.file,
3684
- line,
3685
- column
3686
- });
3687
- // REG-334: If this is Promise constructor with executor callback,
3688
- // register the context for resolve/reject detection
3689
- if (className === 'Promise' && newNode.arguments.length > 0) {
3690
- const executorArg = newNode.arguments[0];
3691
- // Only handle inline function expressions (not variable references)
3692
- if (t.isArrowFunctionExpression(executorArg) || t.isFunctionExpression(executorArg)) {
3693
- // Extract resolve/reject parameter names
3694
- let resolveName;
3695
- let rejectName;
3696
- if (executorArg.params.length > 0 && t.isIdentifier(executorArg.params[0])) {
3697
- resolveName = executorArg.params[0].name;
3698
- }
3699
- if (executorArg.params.length > 1 && t.isIdentifier(executorArg.params[1])) {
3700
- rejectName = executorArg.params[1].name;
3701
- }
3702
- if (resolveName) {
3703
- // Key by function node position to allow nested Promise detection
3704
- const funcKey = `${executorArg.start}:${executorArg.end}`;
3705
- promiseExecutorContexts.set(funcKey, {
3706
- constructorCallId,
3707
- resolveName,
3708
- rejectName,
3709
- file: module.file,
3710
- line,
3711
- // REG-311: Store the ID of the function that creates the Promise
3712
- creatorFunctionId: currentFunctionId || undefined
3713
- });
3714
- }
3715
- }
3716
- }
3717
- }
3718
- }
3719
- // Handle simple constructor: new Foo()
3720
- if (newNode.callee.type === 'Identifier') {
3721
- if (processedCallSites.has(nodeKey)) {
3722
- return;
3723
- }
3724
- processedCallSites.add(nodeKey);
3725
- // Generate semantic ID (primary) or legacy ID (fallback)
3726
- const constructorName = newNode.callee.name;
3727
- const legacyId = `CALL#new:${constructorName}#${module.file}#${getLine(newNode)}:${getColumn(newNode)}:${callSiteCounterRef.value++}`;
3728
- let newCallId = legacyId;
3729
- if (scopeTracker) {
3730
- const discriminator = scopeTracker.getItemCounter(`CALL:new:${constructorName}`);
3731
- newCallId = computeSemanticId('CALL', `new:${constructorName}`, scopeTracker.getContext(), { discriminator });
3732
- }
3733
- callSites.push({
3734
- id: newCallId,
3735
- type: 'CALL',
3736
- name: constructorName,
3737
- file: module.file,
3738
- line: getLine(newNode),
3739
- parentScopeId: getCurrentScopeId(),
3740
- targetFunctionName: constructorName,
3741
- isNew: true
3742
- });
3743
- }
3744
- // Handle namespaced constructor: new ns.Constructor()
3745
- else if (newNode.callee.type === 'MemberExpression') {
3746
- const memberCallee = newNode.callee;
3747
- const object = memberCallee.object;
3748
- const property = memberCallee.property;
3749
- if (object.type === 'Identifier' && property.type === 'Identifier') {
3750
- if (processedMethodCalls.has(nodeKey)) {
3751
- return;
3752
- }
3753
- processedMethodCalls.add(nodeKey);
3754
- const objectName = object.name;
3755
- const constructorName = property.name;
3756
- const fullName = `${objectName}.${constructorName}`;
3757
- // Generate semantic ID for method-style constructor call
3758
- const legacyId = `CALL#new:${fullName}#${module.file}#${getLine(newNode)}:${getColumn(newNode)}:${callSiteCounterRef.value++}`;
3759
- let newMethodCallId = legacyId;
3760
- if (scopeTracker) {
3761
- const discriminator = scopeTracker.getItemCounter(`CALL:new:${fullName}`);
3762
- newMethodCallId = computeSemanticId('CALL', `new:${fullName}`, scopeTracker.getContext(), { discriminator });
3763
- }
3764
- methodCalls.push({
3765
- id: newMethodCallId,
3766
- type: 'CALL',
3767
- name: fullName,
3768
- object: objectName,
3769
- method: constructorName,
3770
- file: module.file,
3771
- line: getLine(newNode),
3772
- column: getColumn(newNode),
3773
- parentScopeId: getCurrentScopeId(),
3774
- isNew: true
3775
- });
3776
- }
3777
- }
3778
- },
3779
- // Property access expressions (REG-395)
3780
- // Shared handler for both MemberExpression and OptionalMemberExpression
3781
- MemberExpression: (memberPath) => {
3782
- // Initialize collections if needed
3783
- if (!collections.propertyAccesses) {
3784
- collections.propertyAccesses = [];
3785
- }
3786
- if (!collections.propertyAccessCounterRef) {
3787
- collections.propertyAccessCounterRef = { value: 0 };
3788
- }
3789
- PropertyAccessVisitor.extractPropertyAccesses(memberPath, memberPath.node, module, collections.propertyAccesses, collections.propertyAccessCounterRef, scopeTracker, currentFunctionId || getCurrentScopeId());
3790
- },
3791
- // OptionalMemberExpression: obj?.prop (same logic as MemberExpression)
3792
- OptionalMemberExpression: (memberPath) => {
3793
- // Initialize collections if needed
3794
- if (!collections.propertyAccesses) {
3795
- collections.propertyAccesses = [];
3796
- }
3797
- if (!collections.propertyAccessCounterRef) {
3798
- collections.propertyAccessCounterRef = { value: 0 };
3799
- }
3800
- PropertyAccessVisitor.extractPropertyAccesses(memberPath, memberPath.node, module, collections.propertyAccesses, collections.propertyAccessCounterRef, scopeTracker, currentFunctionId || getCurrentScopeId());
3801
- }
3802
- });
3803
- // REG-311: Second pass - collect CATCHES_FROM info for try/catch blocks
3804
- // This links catch blocks to exception sources in their corresponding try blocks
3805
- if (functionPath) {
3806
- this.collectCatchesFromInfo(functionPath, catchBlocks, callSites, methodCalls, constructorCalls, catchesFromInfos, module);
3807
- }
3808
- // Phase 6 (REG-267): Attach control flow metadata to the function node
3809
- if (matchingFunction) {
3810
- const cyclomaticComplexity = 1 +
3811
- controlFlowState.branchCount +
3812
- controlFlowState.loopCount +
3813
- controlFlowState.caseCount +
3814
- controlFlowState.logicalOpCount;
3815
- // REG-311: Collect rejection info for this function
3816
- const functionRejectionPatterns = rejectionPatterns.filter(p => p.functionId === matchingFunction.id);
3817
- const canReject = functionRejectionPatterns.length > 0;
3818
- const hasAsyncThrow = functionRejectionPatterns.some(p => p.rejectionType === 'async_throw');
3819
- const rejectedBuiltinErrors = [...new Set(functionRejectionPatterns
3820
- .filter(p => p.errorClassName !== null)
3821
- .map(p => p.errorClassName))];
3822
- matchingFunction.controlFlow = {
3823
- hasBranches: controlFlowState.branchCount > 0,
3824
- hasLoops: controlFlowState.loopCount > 0,
3825
- hasTryCatch: controlFlowState.hasTryCatch,
3826
- hasEarlyReturn: controlFlowState.hasEarlyReturn,
3827
- hasThrow: controlFlowState.hasThrow,
3828
- cyclomaticComplexity,
3829
- // REG-311: Async error tracking
3830
- canReject,
3831
- hasAsyncThrow,
3832
- rejectedBuiltinErrors: rejectedBuiltinErrors.length > 0 ? rejectedBuiltinErrors : undefined
3833
- };
3834
- }
3835
- }
3836
- /**
3837
- * Handle CallExpression nodes: direct function calls (greet(), main())
3838
- * and method calls (obj.method(), data.process()).
3839
- *
3840
- * Handles:
3841
- * - Direct function calls (Identifier callee) → callSites collection
3842
- * - Method calls (MemberExpression callee) → methodCalls collection
3843
- * - Array mutation detection (push, unshift, splice)
3844
- * - Object.assign() detection
3845
- * - REG-311: isAwaited and isInsideTry metadata on CALL nodes
3846
- *
3847
- * @param callNode - The call expression AST node
3848
- * @param processedCallSites - Set of already processed call site keys to avoid duplicates
3849
- * @param processedMethodCalls - Set of already processed method call keys to avoid duplicates
3850
- * @param callSites - Collection for direct function calls
3851
- * @param methodCalls - Collection for method calls
3852
- * @param module - Current module being analyzed
3853
- * @param callSiteCounterRef - Counter for legacy ID generation
3854
- * @param scopeTracker - Optional scope tracker for semantic ID generation
3855
- * @param parentScopeId - ID of the parent scope containing this call
3856
- * @param collections - Full collections object for array/object mutations
3857
- * @param isAwaited - REG-311: true if wrapped in await expression
3858
- * @param isInsideTry - REG-311: true if inside try block
3859
- */
3860
- handleCallExpression(callNode, processedCallSites, processedMethodCalls, callSites, methodCalls, module, callSiteCounterRef, scopeTracker, parentScopeId, collections, isAwaited = false, isInsideTry = false) {
2213
+ handleCallExpression(callNode, processedCallSites, processedMethodCalls, callSites, methodCalls, module, callSiteCounterRef, scopeTracker, parentScopeId, collections, isAwaited = false, isInsideTry = false, isInsideLoop = false) {
3861
2214
  // Handle direct function calls (greet(), main())
3862
2215
  if (callNode.callee.type === 'Identifier') {
3863
2216
  const nodeKey = `${callNode.start}:${callNode.end}`;
@@ -3884,7 +2237,9 @@ export class JSASTAnalyzer extends Plugin {
3884
2237
  targetFunctionName: calleeName,
3885
2238
  // REG-311: Async error tracking metadata
3886
2239
  isAwaited,
3887
- isInsideTry
2240
+ isInsideTry,
2241
+ // REG-298: Await-in-loop detection
2242
+ ...(isAwaited && isInsideLoop ? { isInsideLoop } : {})
3888
2243
  });
3889
2244
  }
3890
2245
  // Handle method calls (obj.method(), data.process())
@@ -3924,8 +2279,14 @@ export class JSASTAnalyzer extends Plugin {
3924
2279
  // REG-311: Async error tracking metadata
3925
2280
  isAwaited,
3926
2281
  isInsideTry,
2282
+ // REG-298: Await-in-loop detection
2283
+ ...(isAwaited && isInsideLoop ? { isInsideLoop } : {}),
3927
2284
  isMethodCall: true
3928
2285
  });
2286
+ // REG-400: Extract arguments for method calls (enables callback resolution)
2287
+ if (callNode.arguments.length > 0) {
2288
+ this.extractMethodCallArguments(callNode, methodCallId, module, collections);
2289
+ }
3929
2290
  // Check for array mutation methods (push, unshift, splice)
3930
2291
  const ARRAY_MUTATION_METHODS = ['push', 'unshift', 'splice'];
3931
2292
  if (ARRAY_MUTATION_METHODS.includes(methodName)) {
@@ -3998,11 +2359,108 @@ export class JSASTAnalyzer extends Plugin {
3998
2359
  parentScopeId,
3999
2360
  isMethodCall: true
4000
2361
  });
2362
+ // REG-400: Extract arguments for nested method calls (enables callback resolution)
2363
+ if (callNode.arguments.length > 0) {
2364
+ this.extractMethodCallArguments(callNode, methodCallId, module, collections);
2365
+ }
4001
2366
  }
4002
2367
  }
4003
2368
  }
4004
2369
  }
4005
2370
  }
2371
+ /**
2372
+ * REG-400: Extract arguments from method call nodes inside function bodies.
2373
+ * Populates callArguments collection so GraphBuilder.bufferArgumentEdges can
2374
+ * create PASSES_ARGUMENT and callback CALLS edges.
2375
+ *
2376
+ * This mirrors CallExpressionVisitor.extractArguments but is simplified —
2377
+ * handles Identifier, Literal, CallExpression, and Expression types.
2378
+ */
2379
+ extractMethodCallArguments(callNode, methodCallId, module, collections) {
2380
+ if (!collections.callArguments) {
2381
+ collections.callArguments = [];
2382
+ }
2383
+ const callArguments = collections.callArguments;
2384
+ const literals = (collections.literals ?? []);
2385
+ const literalCounterRef = (collections.literalCounterRef ?? { value: 0 });
2386
+ callNode.arguments.forEach((arg, argIndex) => {
2387
+ const argInfo = {
2388
+ callId: methodCallId,
2389
+ argIndex,
2390
+ file: module.file,
2391
+ line: getLine(arg),
2392
+ column: getColumn(arg)
2393
+ };
2394
+ if (t.isSpreadElement(arg)) {
2395
+ const spreadArg = arg.argument;
2396
+ if (t.isIdentifier(spreadArg)) {
2397
+ argInfo.targetType = 'VARIABLE';
2398
+ argInfo.targetName = spreadArg.name;
2399
+ argInfo.isSpread = true;
2400
+ }
2401
+ }
2402
+ else if (t.isIdentifier(arg)) {
2403
+ argInfo.targetType = 'VARIABLE';
2404
+ argInfo.targetName = arg.name;
2405
+ }
2406
+ else if (t.isLiteral(arg) && !t.isTemplateLiteral(arg)) {
2407
+ const literalValue = ExpressionEvaluator.extractLiteralValue(arg);
2408
+ if (literalValue !== null) {
2409
+ const argLine = getLine(arg);
2410
+ const argColumn = getColumn(arg);
2411
+ const literalId = `LITERAL#arg${argIndex}#${module.file}#${argLine}:${argColumn}:${literalCounterRef.value++}`;
2412
+ literals.push({
2413
+ id: literalId,
2414
+ type: 'LITERAL',
2415
+ value: literalValue,
2416
+ valueType: typeof literalValue,
2417
+ file: module.file,
2418
+ line: argLine,
2419
+ column: argColumn,
2420
+ parentCallId: methodCallId,
2421
+ argIndex
2422
+ });
2423
+ argInfo.targetType = 'LITERAL';
2424
+ argInfo.targetId = literalId;
2425
+ argInfo.literalValue = literalValue;
2426
+ }
2427
+ }
2428
+ else if (t.isArrowFunctionExpression(arg) || t.isFunctionExpression(arg)) {
2429
+ argInfo.targetType = 'FUNCTION';
2430
+ argInfo.functionLine = getLine(arg);
2431
+ argInfo.functionColumn = getColumn(arg);
2432
+ }
2433
+ else if (t.isCallExpression(arg)) {
2434
+ argInfo.targetType = 'CALL';
2435
+ argInfo.nestedCallLine = getLine(arg);
2436
+ argInfo.nestedCallColumn = getColumn(arg);
2437
+ // REG-402: MemberExpression arguments (this.handler, obj.method)
2438
+ }
2439
+ else if (t.isMemberExpression(arg)) {
2440
+ argInfo.targetType = 'EXPRESSION';
2441
+ argInfo.expressionType = 'MemberExpression';
2442
+ if (t.isIdentifier(arg.object)) {
2443
+ argInfo.objectName = arg.object.name;
2444
+ }
2445
+ else if (t.isThisExpression(arg.object)) {
2446
+ argInfo.objectName = 'this';
2447
+ // Store enclosing class name for direct lookup in GraphBuilder
2448
+ const scopeTracker = collections.scopeTracker;
2449
+ if (scopeTracker) {
2450
+ argInfo.enclosingClassName = scopeTracker.getEnclosingScope('CLASS');
2451
+ }
2452
+ }
2453
+ if (!arg.computed && t.isIdentifier(arg.property)) {
2454
+ argInfo.propertyName = arg.property.name;
2455
+ }
2456
+ }
2457
+ else {
2458
+ argInfo.targetType = 'EXPRESSION';
2459
+ argInfo.expressionType = arg.type;
2460
+ }
2461
+ callArguments.push(argInfo);
2462
+ });
2463
+ }
4006
2464
  /**
4007
2465
  * REG-311: Micro-trace - follow variable assignments within function to find error source.
4008
2466
  * Used to resolve reject(err) or throw err where err is a variable.
@@ -4322,36 +2780,12 @@ export class JSASTAnalyzer extends Plugin {
4322
2780
  isSpread: false,
4323
2781
  valueType: 'EXPRESSION'
4324
2782
  };
4325
- // Determine value type and create value nodes for non-variable types (REG-392)
4326
- const literalValue = ExpressionEvaluator.extractLiteralValue(value);
4327
- if (literalValue !== null) {
4328
- argInfo.valueType = 'LITERAL';
4329
- argInfo.literalValue = literalValue;
4330
- const valueLine = value.loc?.start.line ?? line;
4331
- const valueColumn = value.loc?.start.column ?? column;
4332
- // Create LITERAL node if collections available
4333
- if (collections?.literals && collections.literalCounterRef) {
4334
- const literalCounterRef = collections.literalCounterRef;
4335
- const literalId = `LITERAL#indexed#${module.file}#${valueLine}:${valueColumn}:${literalCounterRef.value++}`;
4336
- collections.literals.push({
4337
- id: literalId,
4338
- type: 'LITERAL',
4339
- value: literalValue,
4340
- valueType: typeof literalValue,
4341
- file: module.file,
4342
- line: valueLine,
4343
- column: valueColumn,
4344
- parentCallId: undefined,
4345
- argIndex: 0
4346
- });
4347
- argInfo.valueNodeId = literalId;
4348
- }
4349
- }
4350
- else if (value.type === 'Identifier') {
4351
- argInfo.valueType = 'VARIABLE';
4352
- argInfo.valueName = value.name;
4353
- }
4354
- else if (value.type === 'ObjectExpression') {
2783
+ // Determine value type and create value nodes for non-variable types
2784
+ // IMPORTANT: Check ObjectExpression/ArrayExpression BEFORE extractLiteralValue
2785
+ // to match the order in detectArrayMutation and extractArguments (REG-396).
2786
+ // extractLiteralValue returns objects/arrays with all-literal properties as
2787
+ // literal values, but we want OBJECT_LITERAL/ARRAY_LITERAL nodes instead.
2788
+ if (value.type === 'ObjectExpression') {
4355
2789
  argInfo.valueType = 'OBJECT_LITERAL';
4356
2790
  const valueLine = value.loc?.start.line ?? line;
4357
2791
  const valueColumn = value.loc?.start.column ?? column;
@@ -4379,11 +2813,41 @@ export class JSASTAnalyzer extends Plugin {
4379
2813
  argInfo.valueNodeId = arrayNode.id;
4380
2814
  }
4381
2815
  }
2816
+ else if (value.type === 'Identifier') {
2817
+ argInfo.valueType = 'VARIABLE';
2818
+ argInfo.valueName = value.name;
2819
+ }
4382
2820
  else if (value.type === 'CallExpression') {
4383
2821
  argInfo.valueType = 'CALL';
4384
2822
  argInfo.callLine = value.loc?.start.line;
4385
2823
  argInfo.callColumn = value.loc?.start.column;
4386
2824
  }
2825
+ else {
2826
+ const literalValue = ExpressionEvaluator.extractLiteralValue(value);
2827
+ if (literalValue !== null) {
2828
+ argInfo.valueType = 'LITERAL';
2829
+ argInfo.literalValue = literalValue;
2830
+ const valueLine = value.loc?.start.line ?? line;
2831
+ const valueColumn = value.loc?.start.column ?? column;
2832
+ // Create LITERAL node if collections available
2833
+ if (collections?.literals && collections.literalCounterRef) {
2834
+ const literalCounterRef = collections.literalCounterRef;
2835
+ const literalId = `LITERAL#indexed#${module.file}#${valueLine}:${valueColumn}:${literalCounterRef.value++}`;
2836
+ collections.literals.push({
2837
+ id: literalId,
2838
+ type: 'LITERAL',
2839
+ value: literalValue,
2840
+ valueType: typeof literalValue,
2841
+ file: module.file,
2842
+ line: valueLine,
2843
+ column: valueColumn,
2844
+ parentCallId: undefined,
2845
+ argIndex: 0
2846
+ });
2847
+ argInfo.valueNodeId = literalId;
2848
+ }
2849
+ }
2850
+ }
4387
2851
  // Capture scope path for scope-aware lookup (REG-309)
4388
2852
  const scopePath = scopeTracker?.getContext().scopePath ?? [];
4389
2853
  arrayMutations.push({