@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
@@ -8,7 +8,7 @@ import { createHash } from 'crypto';
8
8
  import { basename } from 'path';
9
9
  import { parse } from '@babel/parser';
10
10
  import traverseModule from '@babel/traverse';
11
- import type { NodePath, TraverseOptions } from '@babel/traverse';
11
+ import type { NodePath, TraverseOptions, Visitor } from '@babel/traverse';
12
12
  import * as t from '@babel/types';
13
13
 
14
14
  // Type for CJS/ESM interop - @babel/traverse exports a function but @types defines it as namespace
@@ -54,11 +54,15 @@ import { getLine, getColumn } from './ast/utils/location.js';
54
54
  import { Profiler } from '../../core/Profiler.js';
55
55
  import { ScopeTracker } from '../../core/ScopeTracker.js';
56
56
  import { computeSemanticId } from '../../core/SemanticId.js';
57
+ import { IdGenerator } from './ast/IdGenerator.js';
58
+ import { CollisionResolver } from './ast/CollisionResolver.js';
57
59
  import { ExpressionNode } from '../../core/nodes/ExpressionNode.js';
58
60
  import { ConstructorCallNode } from '../../core/nodes/ConstructorCallNode.js';
59
61
  import { ObjectLiteralNode } from '../../core/nodes/ObjectLiteralNode.js';
60
62
  import { ArrayLiteralNode } from '../../core/nodes/ArrayLiteralNode.js';
61
63
  import { NodeFactory } from '../../core/NodeFactory.js';
64
+ import { brandNodeInternal } from '../../core/brandNodeInternal.js';
65
+ import { resolveNodeFile } from '../../utils/resolveNodeFile.js';
62
66
  import type { PluginContext, PluginResult, PluginMetadata, GraphBackend } from '@grafema/types';
63
67
  import type {
64
68
  ModuleNode,
@@ -106,11 +110,29 @@ import type {
106
110
  RejectionPatternInfo,
107
111
  CatchesFromInfo,
108
112
  PropertyAccessInfo,
113
+ TypeParameterInfo,
109
114
  CounterRef,
110
115
  ProcessedNodes,
111
116
  ASTCollections,
112
117
  ExtractedVariable,
113
118
  } from './ast/types.js';
119
+ import { extractNamesFromPattern } from './ast/utils/extractNamesFromPattern.js';
120
+ import { createFunctionBodyContext } from './ast/FunctionBodyContext.js';
121
+ import type { FunctionBodyContext } from './ast/FunctionBodyContext.js';
122
+ import {
123
+ VariableHandler,
124
+ ReturnYieldHandler,
125
+ ThrowHandler,
126
+ NestedFunctionHandler,
127
+ PropertyAccessHandler,
128
+ NewExpressionHandler,
129
+ CallExpressionHandler,
130
+ LoopHandler,
131
+ TryCatchHandler,
132
+ BranchHandler,
133
+ } from './ast/handlers/index.js';
134
+ import type { AnalyzerDelegate } from './ast/handlers/index.js';
135
+ import type { FunctionBodyHandler } from './ast/handlers/index.js';
114
136
 
115
137
  // === LOCAL TYPES ===
116
138
 
@@ -152,6 +174,8 @@ interface Collections {
152
174
  typeAliases: TypeAliasInfo[];
153
175
  enums: EnumDeclarationInfo[];
154
176
  decorators: DecoratorInfo[];
177
+ // Type parameter tracking for generics (REG-303)
178
+ typeParameters: TypeParameterInfo[];
155
179
  // Object/Array literal tracking
156
180
  objectLiterals: ObjectLiteralInfo[];
157
181
  objectProperties: ObjectPropertyInfo[];
@@ -201,35 +225,6 @@ interface Collections {
201
225
  [key: string]: unknown;
202
226
  }
203
227
 
204
- /**
205
- * Tracks try/catch/finally scope transitions during traversal.
206
- * Used by createTryStatementHandler and createBlockStatementHandler.
207
- */
208
- interface TryScopeInfo {
209
- tryScopeId: string;
210
- catchScopeId: string | null;
211
- finallyScopeId: string | null;
212
- currentBlock: 'try' | 'catch' | 'finally';
213
- // Phase 4: Control flow node IDs
214
- tryBlockId: string;
215
- catchBlockId: string | null;
216
- finallyBlockId: string | null;
217
- }
218
-
219
- /**
220
- * Tracks if/else scope transitions during traversal.
221
- * Used by createIfStatementHandler and createBlockStatementHandler.
222
- * Phase 3: Extended to include branchId for control flow BRANCH nodes.
223
- */
224
- interface IfElseScopeInfo {
225
- inElse: boolean;
226
- hasElse: boolean;
227
- ifScopeId: string;
228
- elseScopeId: string | null;
229
- // Phase 3: Control flow BRANCH node ID
230
- branchId: string;
231
- }
232
-
233
228
  interface AnalysisManifest {
234
229
  projectPath: string;
235
230
  [key: string]: unknown;
@@ -267,14 +262,14 @@ export class JSASTAnalyzer extends Plugin {
267
262
  'CALL', 'IMPORT', 'EXPORT', 'LITERAL', 'EXTERNAL_MODULE',
268
263
  'net:stdio', 'net:request', 'event:listener', 'http:request',
269
264
  // TypeScript-specific nodes
270
- 'INTERFACE', 'TYPE', 'ENUM', 'DECORATOR'
265
+ 'INTERFACE', 'TYPE', 'ENUM', 'DECORATOR', 'TYPE_PARAMETER'
271
266
  ],
272
267
  edges: [
273
268
  'CONTAINS', 'DECLARES', 'CALLS', 'HAS_SCOPE', 'CAPTURES', 'MODIFIES',
274
269
  'WRITES_TO', 'IMPORTS', 'INSTANCE_OF', 'HANDLED_BY', 'HAS_CALLBACK',
275
270
  'PASSES_ARGUMENT', 'MAKES_REQUEST', 'IMPORTS_FROM', 'EXPORTS_TO', 'ASSIGNED_FROM',
276
271
  // TypeScript-specific edges
277
- 'IMPLEMENTS', 'EXTENDS', 'DECORATED_BY',
272
+ 'IMPLEMENTS', 'EXTENDS', 'DECORATED_BY', 'HAS_TYPE_PARAMETER',
278
273
  // Promise data flow
279
274
  'RESOLVES_TO'
280
275
  ]
@@ -295,9 +290,9 @@ export class JSASTAnalyzer extends Plugin {
295
290
  /**
296
291
  * Вычисляет хеш содержимого файла
297
292
  */
298
- calculateFileHash(filePath: string): string | null {
293
+ calculateFileHash(filePath: string, projectPath: string = ''): string | null {
299
294
  try {
300
- const content = readFileSync(filePath, 'utf-8');
295
+ const content = readFileSync(resolveNodeFile(filePath, projectPath), 'utf-8');
301
296
  return createHash('sha256').update(content).digest('hex');
302
297
  } catch {
303
298
  return null;
@@ -307,7 +302,7 @@ export class JSASTAnalyzer extends Plugin {
307
302
  /**
308
303
  * Проверяет нужно ли анализировать модуль (сравнивает хеши)
309
304
  */
310
- async shouldAnalyzeModule(module: ModuleNode, graph: GraphBackend, forceAnalysis: boolean): Promise<boolean> {
305
+ async shouldAnalyzeModule(module: ModuleNode, graph: GraphBackend, forceAnalysis: boolean, projectPath: string = ""): Promise<boolean> {
311
306
  if (forceAnalysis) {
312
307
  return true;
313
308
  }
@@ -316,19 +311,19 @@ export class JSASTAnalyzer extends Plugin {
316
311
  return true;
317
312
  }
318
313
 
319
- const currentHash = this.calculateFileHash(module.file);
314
+ const currentHash = this.calculateFileHash(module.file, projectPath);
320
315
  if (!currentHash) {
321
316
  return true;
322
317
  }
323
318
 
324
319
  if (currentHash !== module.contentHash) {
325
- await graph.addNode({
320
+ await graph.addNode(brandNodeInternal({
326
321
  id: module.id,
327
- type: 'MODULE',
322
+ type: 'MODULE' as const,
328
323
  name: module.name,
329
324
  file: module.file,
330
325
  contentHash: currentHash
331
- });
326
+ }));
332
327
  return true;
333
328
  }
334
329
 
@@ -365,7 +360,7 @@ export class JSASTAnalyzer extends Plugin {
365
360
  continue;
366
361
  }
367
362
 
368
- if (await this.shouldAnalyzeModule(module, graph, forceAnalysis)) {
363
+ if (await this.shouldAnalyzeModule(module, graph, forceAnalysis, projectPath)) {
369
364
  modulesToAnalyze.push(module);
370
365
  } else {
371
366
  skippedCount++;
@@ -501,7 +496,7 @@ export class JSASTAnalyzer extends Plugin {
501
496
  // Convert ModuleNode to ASTModuleInfo format
502
497
  const moduleInfos: ASTModuleInfo[] = modules.map(m => ({
503
498
  id: m.id,
504
- file: m.file,
499
+ file: resolveNodeFile(m.file, projectPath),
505
500
  name: m.name
506
501
  }));
507
502
 
@@ -546,7 +541,7 @@ export class JSASTAnalyzer extends Plugin {
546
541
  context.onProgress({
547
542
  phase: 'analysis',
548
543
  currentPlugin: 'JSASTAnalyzer',
549
- message: `Processed ${result.module.file.replace(projectPath, '')}`,
544
+ message: `Processed ${result.module.name}`,
550
545
  totalFiles: modules.length,
551
546
  processedFiles: results.indexOf(result) + 1
552
547
  });
@@ -567,68 +562,13 @@ export class JSASTAnalyzer extends Plugin {
567
562
  /**
568
563
  * Extract variable names from destructuring patterns
569
564
  * Uses t.isX() type guards to avoid casts
565
+ *
566
+ * REG-399: Delegated to extractNamesFromPattern utility for code reuse with parameters.
567
+ * This method maintains the same API for backward compatibility.
570
568
  */
571
569
  extractVariableNamesFromPattern(pattern: t.Node | null | undefined, variables: ExtractedVariable[] = [], propertyPath: string[] = []): ExtractedVariable[] {
572
- if (!pattern) return variables;
573
-
574
- if (t.isIdentifier(pattern)) {
575
- variables.push({
576
- name: pattern.name,
577
- loc: pattern.loc?.start ? { start: pattern.loc.start } : { start: { line: 0, column: 0 } },
578
- propertyPath: propertyPath.length > 0 ? [...propertyPath] : undefined
579
- });
580
- } else if (t.isObjectPattern(pattern)) {
581
- pattern.properties.forEach((prop) => {
582
- if (t.isRestElement(prop)) {
583
- const restVars = this.extractVariableNamesFromPattern(prop.argument, [], []);
584
- restVars.forEach(v => {
585
- v.isRest = true;
586
- v.propertyPath = propertyPath.length > 0 ? [...propertyPath] : undefined;
587
- variables.push(v);
588
- });
589
- } else if (t.isObjectProperty(prop) && prop.value) {
590
- const key = t.isIdentifier(prop.key) ? prop.key.name :
591
- (t.isStringLiteral(prop.key) || t.isNumericLiteral(prop.key) ? String(prop.key.value) : null);
592
-
593
- if (key !== null) {
594
- const newPath = [...propertyPath, key];
595
- this.extractVariableNamesFromPattern(prop.value, variables, newPath);
596
- } else {
597
- this.extractVariableNamesFromPattern(prop.value, variables, propertyPath);
598
- }
599
- }
600
- });
601
- } else if (t.isArrayPattern(pattern)) {
602
- pattern.elements.forEach((element, index) => {
603
- if (element) {
604
- if (t.isRestElement(element)) {
605
- const restVars = this.extractVariableNamesFromPattern(element.argument, [], []);
606
- restVars.forEach(v => {
607
- v.isRest = true;
608
- v.arrayIndex = index;
609
- v.propertyPath = propertyPath.length > 0 ? [...propertyPath] : undefined;
610
- variables.push(v);
611
- });
612
- } else {
613
- const extracted = this.extractVariableNamesFromPattern(element, [], propertyPath);
614
- extracted.forEach(v => {
615
- v.arrayIndex = index;
616
- variables.push(v);
617
- });
618
- }
619
- }
620
- });
621
- } else if (t.isRestElement(pattern)) {
622
- const restVars = this.extractVariableNamesFromPattern(pattern.argument, [], propertyPath);
623
- restVars.forEach(v => {
624
- v.isRest = true;
625
- variables.push(v);
626
- });
627
- } else if (t.isAssignmentPattern(pattern)) {
628
- this.extractVariableNamesFromPattern(pattern.left, variables, propertyPath);
629
- }
630
-
631
- return variables;
570
+ // Delegate to the extracted utility function
571
+ return extractNamesFromPattern(pattern, variables, propertyPath);
632
572
  }
633
573
 
634
574
  /**
@@ -725,57 +665,15 @@ export class JSASTAnalyzer extends Plugin {
725
665
  }
726
666
 
727
667
  // 3. MemberExpression call (e.g., arr.map())
668
+ // Uses coordinate-based lookup to reference the standard CALL node created by CallExpressionVisitor
728
669
  if (initExpression.type === 'CallExpression' && initExpression.callee.type === 'MemberExpression') {
729
- const callee = initExpression.callee;
730
- const objectName = callee.object.type === 'Identifier' ? callee.object.name : (callee.object.type === 'ThisExpression' ? 'this' : 'unknown');
731
- const methodName = callee.property.type === 'Identifier' ? callee.property.name : 'unknown';
732
-
733
- const fullName = `${objectName}.${methodName}`;
734
- const methodCallId = `CALL#${fullName}#${module.file}#${getLine(initExpression)}:${getColumn(initExpression)}:inline`;
735
-
736
- const existing = variableAssignments.find(a => a.sourceId === methodCallId);
737
- if (!existing) {
738
- const extractedArgs: unknown[] = [];
739
- initExpression.arguments.forEach((arg, index) => {
740
- if (arg.type !== 'SpreadElement') {
741
- const argLiteralValue = ExpressionEvaluator.extractLiteralValue(arg);
742
- if (argLiteralValue !== null) {
743
- const literalId = `LITERAL#arg${index}#${module.file}#${getLine(initExpression)}:${getColumn(initExpression)}:${literalCounterRef.value++}`;
744
- literals.push({
745
- id: literalId,
746
- type: 'LITERAL',
747
- value: argLiteralValue,
748
- valueType: typeof argLiteralValue,
749
- file: module.file,
750
- line: arg.loc?.start.line || getLine(initExpression),
751
- column: arg.loc?.start.column || getColumn(initExpression),
752
- parentCallId: methodCallId,
753
- argIndex: index
754
- });
755
- extractedArgs.push(argLiteralValue);
756
- } else {
757
- extractedArgs.push(undefined);
758
- }
759
- }
760
- });
761
-
762
- literals.push({
763
- id: methodCallId,
764
- type: 'CALL',
765
- name: fullName,
766
- object: objectName,
767
- method: methodName,
768
- file: module.file,
769
- arguments: extractedArgs,
770
- line: getLine(initExpression),
771
- column: getColumn(initExpression)
772
- });
773
- }
774
-
775
670
  variableAssignments.push({
776
671
  variableId,
777
- sourceId: methodCallId,
778
- sourceType: 'CALL'
672
+ sourceType: 'METHOD_CALL',
673
+ sourceLine: getLine(initExpression),
674
+ sourceColumn: getColumn(initExpression),
675
+ sourceFile: module.file,
676
+ line: line
779
677
  });
780
678
  return;
781
679
  }
@@ -1417,13 +1315,13 @@ export class JSASTAnalyzer extends Plugin {
1417
1315
 
1418
1316
  try {
1419
1317
  this.profiler.start('file_read');
1420
- const code = readFileSync(module.file, 'utf-8');
1318
+ const code = readFileSync(resolveNodeFile(module.file, projectPath), 'utf-8');
1421
1319
  this.profiler.end('file_read');
1422
1320
 
1423
1321
  this.profiler.start('babel_parse');
1424
1322
  const ast = parse(code, {
1425
1323
  sourceType: 'module',
1426
- plugins: ['jsx', 'typescript']
1324
+ plugins: ['jsx', 'typescript', 'decorators-legacy']
1427
1325
  });
1428
1326
  this.profiler.end('babel_parse');
1429
1327
 
@@ -1431,6 +1329,9 @@ export class JSASTAnalyzer extends Plugin {
1431
1329
  // Use basename for shorter, more readable semantic IDs
1432
1330
  const scopeTracker = new ScopeTracker(basename(module.file));
1433
1331
 
1332
+ // REG-464: Shared IdGenerator for v2 collision resolution across visitors
1333
+ const sharedIdGenerator = new IdGenerator(scopeTracker);
1334
+
1434
1335
  const functions: FunctionInfo[] = [];
1435
1336
  const parameters: ParameterInfo[] = [];
1436
1337
  const scopes: ScopeInfo[] = [];
@@ -1458,6 +1359,8 @@ export class JSASTAnalyzer extends Plugin {
1458
1359
  const typeAliases: TypeAliasInfo[] = [];
1459
1360
  const enums: EnumDeclarationInfo[] = [];
1460
1361
  const decorators: DecoratorInfo[] = [];
1362
+ // Type parameter tracking for generics (REG-303)
1363
+ const typeParameters: TypeParameterInfo[] = [];
1461
1364
  // Object/Array literal tracking for data flow
1462
1365
  const objectLiterals: ObjectLiteralInfo[] = [];
1463
1366
  const objectProperties: ObjectPropertyInfo[] = [];
@@ -1545,6 +1448,8 @@ export class JSASTAnalyzer extends Plugin {
1545
1448
  httpRequests, literals, variableAssignments,
1546
1449
  // TypeScript-specific collections
1547
1450
  interfaces, typeAliases, enums, decorators,
1451
+ // Type parameter tracking for generics (REG-303)
1452
+ typeParameters,
1548
1453
  // Object/Array literal tracking
1549
1454
  objectLiterals, objectProperties, arrayLiterals, arrayElements,
1550
1455
  // Array mutation tracking
@@ -1759,10 +1664,30 @@ export class JSASTAnalyzer extends Plugin {
1759
1664
 
1760
1665
  // Call expressions
1761
1666
  this.profiler.start('traverse_calls');
1762
- const callExpressionVisitor = new CallExpressionVisitor(module, allCollections, scopeTracker);
1667
+ const callExpressionVisitor = new CallExpressionVisitor(module, allCollections, scopeTracker, sharedIdGenerator);
1763
1668
  traverse(ast, callExpressionVisitor.getHandlers());
1764
1669
  this.profiler.end('traverse_calls');
1765
1670
 
1671
+ // REG-297: Detect top-level await expressions
1672
+ this.profiler.start('traverse_top_level_await');
1673
+ let hasTopLevelAwait = false;
1674
+ traverse(ast, {
1675
+ AwaitExpression(awaitPath: NodePath<t.AwaitExpression>) {
1676
+ if (!awaitPath.getFunctionParent()) {
1677
+ hasTopLevelAwait = true;
1678
+ awaitPath.stop();
1679
+ }
1680
+ },
1681
+ // for-await-of uses ForOfStatement.await, not AwaitExpression
1682
+ ForOfStatement(forOfPath: NodePath<t.ForOfStatement>) {
1683
+ if (forOfPath.node.await && !forOfPath.getFunctionParent()) {
1684
+ hasTopLevelAwait = true;
1685
+ forOfPath.stop();
1686
+ }
1687
+ }
1688
+ });
1689
+ this.profiler.end('traverse_top_level_await');
1690
+
1766
1691
  // Property access expressions (REG-395)
1767
1692
  this.profiler.start('traverse_property_access');
1768
1693
  const propertyAccessVisitor = new PropertyAccessVisitor(module, allCollections, scopeTracker);
@@ -1897,6 +1822,39 @@ export class JSASTAnalyzer extends Plugin {
1897
1822
  });
1898
1823
  this.profiler.end('traverse_ifs');
1899
1824
 
1825
+ // REG-464: Resolve v2 ID collisions after all visitors complete
1826
+ const pendingNodes = sharedIdGenerator.getPendingNodes();
1827
+ if (pendingNodes.length > 0) {
1828
+ // Capture pre-resolution IDs to update callArguments afterward
1829
+ const preResolutionIds = new Map<{ id: string }, string>();
1830
+ for (const pn of pendingNodes) {
1831
+ preResolutionIds.set(pn.collectionRef, pn.collectionRef.id);
1832
+ }
1833
+
1834
+ const collisionResolver = new CollisionResolver();
1835
+ collisionResolver.resolve(pendingNodes);
1836
+
1837
+ // Update callArgument.callId references that became stale after resolution
1838
+ const idRemapping = new Map<string, string>();
1839
+ for (const pn of pendingNodes) {
1840
+ const oldId = preResolutionIds.get(pn.collectionRef)!;
1841
+ if (oldId !== pn.collectionRef.id) {
1842
+ idRemapping.set(oldId, pn.collectionRef.id);
1843
+ }
1844
+ }
1845
+ if (idRemapping.size > 0) {
1846
+ const callArgs = allCollections.callArguments as Array<{ callId: string }> | undefined;
1847
+ if (callArgs) {
1848
+ for (const arg of callArgs) {
1849
+ const resolved = idRemapping.get(arg.callId);
1850
+ if (resolved) {
1851
+ arg.callId = resolved;
1852
+ }
1853
+ }
1854
+ }
1855
+ }
1856
+ }
1857
+
1900
1858
  // Build graph
1901
1859
  this.profiler.start('graph_build');
1902
1860
  const result = await this.graphBuilder.build(module, graph, projectPath, {
@@ -1932,6 +1890,8 @@ export class JSASTAnalyzer extends Plugin {
1932
1890
  typeAliases,
1933
1891
  enums,
1934
1892
  decorators,
1893
+ // Type parameter tracking for generics (REG-303)
1894
+ typeParameters,
1935
1895
  // Array mutation tracking
1936
1896
  arrayMutations,
1937
1897
  // Object mutation tracking
@@ -1958,7 +1918,9 @@ export class JSASTAnalyzer extends Plugin {
1958
1918
  ? allCollections.catchesFromInfos as CatchesFromInfo[]
1959
1919
  : catchesFromInfos,
1960
1920
  // Property access tracking (REG-395)
1961
- propertyAccesses: allCollections.propertyAccesses || propertyAccesses
1921
+ propertyAccesses: allCollections.propertyAccesses || propertyAccesses,
1922
+ // REG-297: Top-level await tracking
1923
+ hasTopLevelAwait
1962
1924
  });
1963
1925
  this.profiler.end('graph_build');
1964
1926
 
@@ -2205,506 +2167,6 @@ export class JSASTAnalyzer extends Plugin {
2205
2167
  });
2206
2168
  }
2207
2169
 
2208
- private createLoopScopeHandler(
2209
- trackerScopeType: string,
2210
- scopeType: string,
2211
- loopType: 'for' | 'for-in' | 'for-of' | 'while' | 'do-while',
2212
- parentScopeId: string,
2213
- module: VisitorModule,
2214
- scopes: ScopeInfo[],
2215
- loops: LoopInfo[],
2216
- scopeCounterRef: CounterRef,
2217
- loopCounterRef: CounterRef,
2218
- scopeTracker: ScopeTracker | undefined,
2219
- scopeIdStack?: string[],
2220
- controlFlowState?: { loopCount: number }
2221
- ): { enter: (path: NodePath<t.Loop>) => void; exit: () => void } {
2222
- return {
2223
- enter: (path: NodePath<t.Loop>) => {
2224
- const node = path.node;
2225
-
2226
- // Phase 6 (REG-267): Increment loop count for cyclomatic complexity
2227
- if (controlFlowState) {
2228
- controlFlowState.loopCount++;
2229
- }
2230
-
2231
- // 1. Create LOOP node
2232
- const loopCounter = loopCounterRef.value++;
2233
- const legacyLoopId = `${module.file}:LOOP:${loopType}:${getLine(node)}:${loopCounter}`;
2234
- const loopId = scopeTracker
2235
- ? computeSemanticId('LOOP', loopType, scopeTracker.getContext(), { discriminator: loopCounter })
2236
- : legacyLoopId;
2237
-
2238
- // 2. Extract iteration target for for-in/for-of
2239
- let iteratesOverName: string | undefined;
2240
- let iteratesOverLine: number | undefined;
2241
- let iteratesOverColumn: number | undefined;
2242
-
2243
- if (loopType === 'for-in' || loopType === 'for-of') {
2244
- const loopNode = node as t.ForInStatement | t.ForOfStatement;
2245
- if (t.isIdentifier(loopNode.right)) {
2246
- iteratesOverName = loopNode.right.name;
2247
- iteratesOverLine = getLine(loopNode.right);
2248
- iteratesOverColumn = getColumn(loopNode.right);
2249
- } else if (t.isMemberExpression(loopNode.right)) {
2250
- iteratesOverName = this.memberExpressionToString(loopNode.right);
2251
- iteratesOverLine = getLine(loopNode.right);
2252
- iteratesOverColumn = getColumn(loopNode.right);
2253
- }
2254
- }
2255
-
2256
- // 2b. Extract init/test/update for classic for loops and test for while/do-while (REG-282)
2257
- let initVariableName: string | undefined;
2258
- let initLine: number | undefined;
2259
-
2260
- let testExpressionId: string | undefined;
2261
- let testExpressionType: string | undefined;
2262
- let testLine: number | undefined;
2263
- let testColumn: number | undefined;
2264
-
2265
- let updateExpressionId: string | undefined;
2266
- let updateExpressionType: string | undefined;
2267
- let updateLine: number | undefined;
2268
- let updateColumn: number | undefined;
2269
-
2270
- if (loopType === 'for') {
2271
- const forNode = node as t.ForStatement;
2272
-
2273
- // Extract init: let i = 0
2274
- if (forNode.init) {
2275
- initLine = getLine(forNode.init);
2276
- if (t.isVariableDeclaration(forNode.init)) {
2277
- // Get name of first declared variable
2278
- const firstDeclarator = forNode.init.declarations[0];
2279
- if (t.isIdentifier(firstDeclarator.id)) {
2280
- initVariableName = firstDeclarator.id.name;
2281
- }
2282
- }
2283
- }
2284
-
2285
- // Extract test: i < 10
2286
- if (forNode.test) {
2287
- testLine = getLine(forNode.test);
2288
- testColumn = getColumn(forNode.test);
2289
- testExpressionType = forNode.test.type;
2290
- testExpressionId = ExpressionNode.generateId(forNode.test.type, module.file, testLine, testColumn);
2291
- }
2292
-
2293
- // Extract update: i++
2294
- if (forNode.update) {
2295
- updateLine = getLine(forNode.update);
2296
- updateColumn = getColumn(forNode.update);
2297
- updateExpressionType = forNode.update.type;
2298
- updateExpressionId = ExpressionNode.generateId(forNode.update.type, module.file, updateLine, updateColumn);
2299
- }
2300
- }
2301
-
2302
- // Extract test condition for while and do-while loops
2303
- if (loopType === 'while' || loopType === 'do-while') {
2304
- const condLoop = node as t.WhileStatement | t.DoWhileStatement;
2305
- if (condLoop.test) {
2306
- testLine = getLine(condLoop.test);
2307
- testColumn = getColumn(condLoop.test);
2308
- testExpressionType = condLoop.test.type;
2309
- testExpressionId = ExpressionNode.generateId(condLoop.test.type, module.file, testLine, testColumn);
2310
- }
2311
- }
2312
-
2313
- // Extract async flag for for-await-of (REG-284)
2314
- let isAsync: boolean | undefined;
2315
- if (loopType === 'for-of') {
2316
- const forOfNode = node as t.ForOfStatement;
2317
- isAsync = forOfNode.await === true ? true : undefined;
2318
- }
2319
-
2320
- // 3. Determine actual parent - use stack for nested loops, otherwise original parentScopeId
2321
- const actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
2322
- ? scopeIdStack[scopeIdStack.length - 1]
2323
- : parentScopeId;
2324
-
2325
- // 3.5. Extract condition expression for while/do-while/for loops (REG-280)
2326
- // Note: for-in and for-of don't have test expressions (they use ITERATES_OVER instead)
2327
- let conditionExpressionId: string | undefined;
2328
- let conditionExpressionType: string | undefined;
2329
- let conditionLine: number | undefined;
2330
- let conditionColumn: number | undefined;
2331
-
2332
- if (loopType === 'while' || loopType === 'do-while') {
2333
- const testNode = (node as t.WhileStatement | t.DoWhileStatement).test;
2334
- if (testNode) {
2335
- const condResult = this.extractDiscriminantExpression(testNode, module);
2336
- conditionExpressionId = condResult.id;
2337
- conditionExpressionType = condResult.expressionType;
2338
- conditionLine = condResult.line;
2339
- conditionColumn = condResult.column;
2340
- }
2341
- } else if (loopType === 'for') {
2342
- const forNode = node as t.ForStatement;
2343
- // for loop test may be null (infinite loop: for(;;))
2344
- if (forNode.test) {
2345
- const condResult = this.extractDiscriminantExpression(forNode.test, module);
2346
- conditionExpressionId = condResult.id;
2347
- conditionExpressionType = condResult.expressionType;
2348
- conditionLine = condResult.line;
2349
- conditionColumn = condResult.column;
2350
- }
2351
- }
2352
-
2353
- // 4. Push LOOP info
2354
- loops.push({
2355
- id: loopId,
2356
- semanticId: loopId,
2357
- type: 'LOOP',
2358
- loopType,
2359
- file: module.file,
2360
- line: getLine(node),
2361
- column: getColumn(node),
2362
- parentScopeId: actualParentScopeId,
2363
- iteratesOverName,
2364
- iteratesOverLine,
2365
- iteratesOverColumn,
2366
- conditionExpressionId,
2367
- conditionExpressionType,
2368
- conditionLine,
2369
- conditionColumn,
2370
- // REG-282: init/test/update for classic for loops
2371
- initVariableName,
2372
- initLine,
2373
- testExpressionId,
2374
- testExpressionType,
2375
- testLine,
2376
- testColumn,
2377
- updateExpressionId,
2378
- updateExpressionType,
2379
- updateLine,
2380
- updateColumn,
2381
- // REG-284: async flag for for-await-of
2382
- async: isAsync
2383
- });
2384
-
2385
- // 5. Create body SCOPE (backward compatibility)
2386
- const scopeId = `SCOPE#${scopeType}#${module.file}#${getLine(node)}:${scopeCounterRef.value++}`;
2387
- const semanticId = this.generateSemanticId(scopeType, scopeTracker);
2388
- scopes.push({
2389
- id: scopeId,
2390
- type: 'SCOPE',
2391
- scopeType,
2392
- semanticId,
2393
- file: module.file,
2394
- line: getLine(node),
2395
- parentScopeId: loopId // Parent is LOOP, not original parentScopeId
2396
- });
2397
-
2398
- // 6. Push body SCOPE to scopeIdStack (for CONTAINS edges to nested items)
2399
- // The body scope is the container for nested loops, not the LOOP itself
2400
- if (scopeIdStack) {
2401
- scopeIdStack.push(scopeId);
2402
- }
2403
-
2404
- // Enter scope for semantic ID generation
2405
- if (scopeTracker) {
2406
- scopeTracker.enterCountedScope(trackerScopeType);
2407
- }
2408
- },
2409
- exit: () => {
2410
- // Pop loop scope from stack
2411
- if (scopeIdStack) {
2412
- scopeIdStack.pop();
2413
- }
2414
-
2415
- // Exit scope
2416
- if (scopeTracker) {
2417
- scopeTracker.exitScope();
2418
- }
2419
- }
2420
- };
2421
- }
2422
-
2423
- /**
2424
- * Factory method to create TryStatement handler.
2425
- * Creates TRY_BLOCK, CATCH_BLOCK, FINALLY_BLOCK nodes and body SCOPEs.
2426
- * Does NOT use skip() - allows normal traversal for CallExpression/NewExpression visitors.
2427
- *
2428
- * Phase 4 (REG-267): Creates control flow nodes with HAS_CATCH and HAS_FINALLY edges.
2429
- *
2430
- * @param parentScopeId - Parent scope ID for the scope nodes
2431
- * @param module - Module context
2432
- * @param scopes - Collection to push scope nodes to
2433
- * @param tryBlocks - Collection to push TRY_BLOCK nodes to
2434
- * @param catchBlocks - Collection to push CATCH_BLOCK nodes to
2435
- * @param finallyBlocks - Collection to push FINALLY_BLOCK nodes to
2436
- * @param scopeCounterRef - Counter for unique scope IDs
2437
- * @param tryBlockCounterRef - Counter for unique TRY_BLOCK IDs
2438
- * @param catchBlockCounterRef - Counter for unique CATCH_BLOCK IDs
2439
- * @param finallyBlockCounterRef - Counter for unique FINALLY_BLOCK IDs
2440
- * @param scopeTracker - Tracker for semantic ID generation
2441
- * @param tryScopeMap - Map to track try/catch/finally scope transitions
2442
- * @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
2443
- */
2444
- private createTryStatementHandler(
2445
- parentScopeId: string,
2446
- module: VisitorModule,
2447
- scopes: ScopeInfo[],
2448
- tryBlocks: TryBlockInfo[],
2449
- catchBlocks: CatchBlockInfo[],
2450
- finallyBlocks: FinallyBlockInfo[],
2451
- scopeCounterRef: CounterRef,
2452
- tryBlockCounterRef: CounterRef,
2453
- catchBlockCounterRef: CounterRef,
2454
- finallyBlockCounterRef: CounterRef,
2455
- scopeTracker: ScopeTracker | undefined,
2456
- tryScopeMap: Map<t.TryStatement, TryScopeInfo>,
2457
- scopeIdStack?: string[],
2458
- controlFlowState?: { hasTryCatch: boolean; tryBlockDepth: number }
2459
- ): { enter: (tryPath: NodePath<t.TryStatement>) => void; exit: (tryPath: NodePath<t.TryStatement>) => void } {
2460
- return {
2461
- enter: (tryPath: NodePath<t.TryStatement>) => {
2462
- const tryNode = tryPath.node;
2463
-
2464
- // Phase 6 (REG-267): Mark that this function has try/catch
2465
- if (controlFlowState) {
2466
- controlFlowState.hasTryCatch = true;
2467
- // REG-311: Increment try block depth for O(1) isInsideTry detection
2468
- controlFlowState.tryBlockDepth++;
2469
- }
2470
-
2471
- // Determine actual parent - use stack for nested structures, otherwise original parentScopeId
2472
- const actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
2473
- ? scopeIdStack[scopeIdStack.length - 1]
2474
- : parentScopeId;
2475
-
2476
- // 1. Create TRY_BLOCK node
2477
- const tryBlockCounter = tryBlockCounterRef.value++;
2478
- const legacyTryBlockId = `${module.file}:TRY_BLOCK:${getLine(tryNode)}:${tryBlockCounter}`;
2479
- const tryBlockId = scopeTracker
2480
- ? computeSemanticId('TRY_BLOCK', 'try', scopeTracker.getContext(), { discriminator: tryBlockCounter })
2481
- : legacyTryBlockId;
2482
-
2483
- tryBlocks.push({
2484
- id: tryBlockId,
2485
- semanticId: tryBlockId,
2486
- type: 'TRY_BLOCK',
2487
- file: module.file,
2488
- line: getLine(tryNode),
2489
- column: getColumn(tryNode),
2490
- parentScopeId: actualParentScopeId
2491
- });
2492
-
2493
- // 2. Create try-body SCOPE (backward compatibility)
2494
- // Parent is now TRY_BLOCK, not original parentScopeId
2495
- const tryScopeId = `SCOPE#try-block#${module.file}#${getLine(tryNode)}:${scopeCounterRef.value++}`;
2496
- const trySemanticId = this.generateSemanticId('try-block', scopeTracker);
2497
- scopes.push({
2498
- id: tryScopeId,
2499
- type: 'SCOPE',
2500
- scopeType: 'try-block',
2501
- semanticId: trySemanticId,
2502
- file: module.file,
2503
- line: getLine(tryNode),
2504
- parentScopeId: tryBlockId // Parent is TRY_BLOCK
2505
- });
2506
-
2507
- // 3. Create CATCH_BLOCK and catch-body SCOPE if handler exists
2508
- let catchBlockId: string | null = null;
2509
- let catchScopeId: string | null = null;
2510
- if (tryNode.handler) {
2511
- const catchClause = tryNode.handler;
2512
- const catchBlockCounter = catchBlockCounterRef.value++;
2513
- const legacyCatchBlockId = `${module.file}:CATCH_BLOCK:${getLine(catchClause)}:${catchBlockCounter}`;
2514
- catchBlockId = scopeTracker
2515
- ? computeSemanticId('CATCH_BLOCK', 'catch', scopeTracker.getContext(), { discriminator: catchBlockCounter })
2516
- : legacyCatchBlockId;
2517
-
2518
- // Extract parameter name if present
2519
- let parameterName: string | undefined;
2520
- if (catchClause.param && t.isIdentifier(catchClause.param)) {
2521
- parameterName = catchClause.param.name;
2522
- }
2523
-
2524
- catchBlocks.push({
2525
- id: catchBlockId,
2526
- semanticId: catchBlockId,
2527
- type: 'CATCH_BLOCK',
2528
- file: module.file,
2529
- line: getLine(catchClause),
2530
- column: getColumn(catchClause),
2531
- parentScopeId,
2532
- parentTryBlockId: tryBlockId,
2533
- parameterName
2534
- });
2535
-
2536
- // Create catch-body SCOPE (backward compatibility)
2537
- catchScopeId = `SCOPE#catch-block#${module.file}#${getLine(catchClause)}:${scopeCounterRef.value++}`;
2538
- const catchSemanticId = this.generateSemanticId('catch-block', scopeTracker);
2539
- scopes.push({
2540
- id: catchScopeId,
2541
- type: 'SCOPE',
2542
- scopeType: 'catch-block',
2543
- semanticId: catchSemanticId,
2544
- file: module.file,
2545
- line: getLine(catchClause),
2546
- parentScopeId: catchBlockId // Parent is CATCH_BLOCK
2547
- });
2548
- }
2549
-
2550
- // 4. Create FINALLY_BLOCK and finally-body SCOPE if finalizer exists
2551
- let finallyBlockId: string | null = null;
2552
- let finallyScopeId: string | null = null;
2553
- if (tryNode.finalizer) {
2554
- const finallyBlockCounter = finallyBlockCounterRef.value++;
2555
- const legacyFinallyBlockId = `${module.file}:FINALLY_BLOCK:${getLine(tryNode.finalizer)}:${finallyBlockCounter}`;
2556
- finallyBlockId = scopeTracker
2557
- ? computeSemanticId('FINALLY_BLOCK', 'finally', scopeTracker.getContext(), { discriminator: finallyBlockCounter })
2558
- : legacyFinallyBlockId;
2559
-
2560
- finallyBlocks.push({
2561
- id: finallyBlockId,
2562
- semanticId: finallyBlockId,
2563
- type: 'FINALLY_BLOCK',
2564
- file: module.file,
2565
- line: getLine(tryNode.finalizer),
2566
- column: getColumn(tryNode.finalizer),
2567
- parentScopeId,
2568
- parentTryBlockId: tryBlockId
2569
- });
2570
-
2571
- // Create finally-body SCOPE (backward compatibility)
2572
- finallyScopeId = `SCOPE#finally-block#${module.file}#${getLine(tryNode.finalizer)}:${scopeCounterRef.value++}`;
2573
- const finallySemanticId = this.generateSemanticId('finally-block', scopeTracker);
2574
- scopes.push({
2575
- id: finallyScopeId,
2576
- type: 'SCOPE',
2577
- scopeType: 'finally-block',
2578
- semanticId: finallySemanticId,
2579
- file: module.file,
2580
- line: getLine(tryNode.finalizer),
2581
- parentScopeId: finallyBlockId // Parent is FINALLY_BLOCK
2582
- });
2583
- }
2584
-
2585
- // 5. Push try scope onto stack for CONTAINS edges
2586
- if (scopeIdStack) {
2587
- scopeIdStack.push(tryScopeId);
2588
- }
2589
-
2590
- // Enter try scope for semantic ID generation
2591
- if (scopeTracker) {
2592
- scopeTracker.enterCountedScope('try');
2593
- }
2594
-
2595
- // 6. Store scope info for catch/finally transitions
2596
- tryScopeMap.set(tryNode, {
2597
- tryScopeId,
2598
- catchScopeId,
2599
- finallyScopeId,
2600
- currentBlock: 'try',
2601
- tryBlockId,
2602
- catchBlockId,
2603
- finallyBlockId
2604
- });
2605
- },
2606
- exit: (tryPath: NodePath<t.TryStatement>) => {
2607
- const tryNode = tryPath.node;
2608
- const _scopeInfo = tryScopeMap.get(tryNode);
2609
-
2610
- // REG-311: Only decrement try block depth if we're still in 'try' block
2611
- // (not transitioned to catch/finally, where we already decremented)
2612
- if (controlFlowState && _scopeInfo?.currentBlock === 'try') {
2613
- controlFlowState.tryBlockDepth--;
2614
- }
2615
-
2616
- // Pop the current scope from stack (could be try, catch, or finally)
2617
- if (scopeIdStack) {
2618
- scopeIdStack.pop();
2619
- }
2620
-
2621
- // Exit the current scope
2622
- if (scopeTracker) {
2623
- scopeTracker.exitScope();
2624
- }
2625
-
2626
- // Clean up
2627
- tryScopeMap.delete(tryNode);
2628
- }
2629
- };
2630
- }
2631
-
2632
- /**
2633
- * Factory method to create CatchClause handler.
2634
- * Handles scope transition from try to catch and processes catch parameter.
2635
- *
2636
- * @param module - Module context
2637
- * @param variableDeclarations - Collection to push variable declarations to
2638
- * @param varDeclCounterRef - Counter for unique variable declaration IDs
2639
- * @param scopeTracker - Tracker for semantic ID generation
2640
- * @param tryScopeMap - Map to track try/catch/finally scope transitions
2641
- * @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
2642
- */
2643
- private createCatchClauseHandler(
2644
- module: VisitorModule,
2645
- variableDeclarations: VariableDeclarationInfo[],
2646
- varDeclCounterRef: CounterRef,
2647
- scopeTracker: ScopeTracker | undefined,
2648
- tryScopeMap: Map<t.TryStatement, TryScopeInfo>,
2649
- scopeIdStack?: string[],
2650
- controlFlowState?: { hasTryCatch: boolean; tryBlockDepth: number }
2651
- ): { enter: (catchPath: NodePath<t.CatchClause>) => void } {
2652
- return {
2653
- enter: (catchPath: NodePath<t.CatchClause>) => {
2654
- const catchNode = catchPath.node;
2655
- const parent = catchPath.parent;
2656
-
2657
- if (!t.isTryStatement(parent)) return;
2658
-
2659
- const scopeInfo = tryScopeMap.get(parent);
2660
- if (!scopeInfo || !scopeInfo.catchScopeId) return;
2661
-
2662
- // Transition from try scope to catch scope
2663
- if (scopeInfo.currentBlock === 'try') {
2664
- // Pop try scope, push catch scope
2665
- if (scopeIdStack) {
2666
- scopeIdStack.pop();
2667
- scopeIdStack.push(scopeInfo.catchScopeId);
2668
- }
2669
-
2670
- // Exit try scope, enter catch scope for semantic ID
2671
- if (scopeTracker) {
2672
- scopeTracker.exitScope();
2673
- scopeTracker.enterCountedScope('catch');
2674
- }
2675
-
2676
- // REG-311: Decrement tryBlockDepth when leaving try block for catch
2677
- // Calls in catch block should NOT have isInsideTry=true
2678
- if (controlFlowState) {
2679
- controlFlowState.tryBlockDepth--;
2680
- }
2681
-
2682
- scopeInfo.currentBlock = 'catch';
2683
- }
2684
-
2685
- // Handle catch parameter (e.g., catch (e) or catch ({ message }))
2686
- if (catchNode.param) {
2687
- const errorVarInfo = this.extractVariableNamesFromPattern(catchNode.param);
2688
-
2689
- errorVarInfo.forEach(varInfo => {
2690
- const legacyId = `VARIABLE#${varInfo.name}#${module.file}#${varInfo.loc.start.line}:${varInfo.loc.start.column}:${varDeclCounterRef.value++}`;
2691
- const varId = scopeTracker
2692
- ? computeSemanticId('VARIABLE', varInfo.name, scopeTracker.getContext())
2693
- : legacyId;
2694
-
2695
- variableDeclarations.push({
2696
- id: varId,
2697
- type: 'VARIABLE',
2698
- name: varInfo.name,
2699
- file: module.file,
2700
- line: varInfo.loc.start.line,
2701
- parentScopeId: scopeInfo.catchScopeId!
2702
- });
2703
- });
2704
- }
2705
- }
2706
- };
2707
- }
2708
2170
 
2709
2171
  /**
2710
2172
  * Handles SwitchStatement nodes.
@@ -3219,1635 +2681,144 @@ export class JSASTAnalyzer extends Plugin {
3219
2681
  };
3220
2682
  }
3221
2683
 
2684
+
3222
2685
  /**
3223
- * Factory method to create IfStatement handler.
3224
- * Creates BRANCH node for if statement and SCOPE nodes for if/else bodies.
3225
- * Tracks if/else scope transitions via ifElseScopeMap.
3226
- *
3227
- * Phase 3 (REG-267): Creates BRANCH node with branchType='if' and
3228
- * HAS_CONSEQUENT/HAS_ALTERNATE edges to body SCOPEs.
2686
+ * Анализирует тело функции и извлекает переменные, вызовы, условные блоки.
2687
+ * Uses ScopeTracker from collections for semantic ID generation.
3229
2688
  *
3230
- * @param parentScopeId - Parent scope ID for the scope nodes
3231
- * @param module - Module context
3232
- * @param scopes - Collection to push scope nodes to
3233
- * @param branches - Collection to push BRANCH nodes to
3234
- * @param ifScopeCounterRef - Counter for unique if scope IDs
3235
- * @param branchCounterRef - Counter for unique BRANCH IDs
3236
- * @param scopeTracker - Tracker for semantic ID generation
3237
- * @param sourceCode - Source code for extracting condition text
3238
- * @param ifElseScopeMap - Map to track if/else scope transitions
3239
- * @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
2689
+ * REG-422: Delegates traversal to extracted handler classes.
2690
+ * Local state is encapsulated in FunctionBodyContext; each handler
2691
+ * contributes a Visitor fragment that is merged into a single traversal.
3240
2692
  */
3241
- private createIfStatementHandler(
2693
+ analyzeFunctionBody(
2694
+ funcPath: NodePath<t.Function | t.StaticBlock>,
3242
2695
  parentScopeId: string,
3243
2696
  module: VisitorModule,
3244
- scopes: ScopeInfo[],
3245
- branches: BranchInfo[],
3246
- ifScopeCounterRef: CounterRef,
3247
- branchCounterRef: CounterRef,
3248
- scopeTracker: ScopeTracker | undefined,
3249
- sourceCode: string,
3250
- ifElseScopeMap: Map<t.IfStatement, IfElseScopeInfo>,
3251
- scopeIdStack?: string[],
3252
- controlFlowState?: { branchCount: number; logicalOpCount: number },
3253
- countLogicalOperators?: (node: t.Expression) => number
3254
- ): { enter: (ifPath: NodePath<t.IfStatement>) => void; exit: (ifPath: NodePath<t.IfStatement>) => void } {
3255
- return {
3256
- enter: (ifPath: NodePath<t.IfStatement>) => {
3257
- const ifNode = ifPath.node;
3258
- const condition = sourceCode.substring(ifNode.test.start!, ifNode.test.end!) || 'condition';
3259
-
3260
- // Phase 6 (REG-267): Increment branch count and count logical operators
3261
- if (controlFlowState) {
3262
- controlFlowState.branchCount++;
3263
- if (countLogicalOperators) {
3264
- controlFlowState.logicalOpCount += countLogicalOperators(ifNode.test);
3265
- }
3266
- }
2697
+ collections: VisitorCollections
2698
+ ): void {
2699
+ // 1. Create context (replaces ~260 lines of local var declarations)
2700
+ const ctx = createFunctionBodyContext(
2701
+ funcPath, parentScopeId, module, collections,
2702
+ (collections.functions ?? []) as FunctionInfo[],
2703
+ extractNamesFromPattern
2704
+ );
2705
+
2706
+ // 2. Handle implicit return for THIS arrow function if it has an expression body
2707
+ // e.g., `const double = x => x * 2;` this needs this.extractReturnExpressionInfo,
2708
+ // so it stays here rather than in a handler.
2709
+ if (t.isArrowFunctionExpression(ctx.funcNode) && !t.isBlockStatement(ctx.funcNode.body) && ctx.currentFunctionId) {
2710
+ const bodyExpr = ctx.funcNode.body;
2711
+ const exprInfo = this.extractReturnExpressionInfo(
2712
+ bodyExpr, module, ctx.literals, ctx.literalCounterRef, ctx.funcLine, ctx.funcColumn, 'implicit_return'
2713
+ );
2714
+ ctx.returnStatements.push({
2715
+ parentFunctionId: ctx.currentFunctionId,
2716
+ file: module.file,
2717
+ line: getLine(bodyExpr),
2718
+ column: getColumn(bodyExpr),
2719
+ returnValueType: 'NONE',
2720
+ isImplicitReturn: true,
2721
+ ...exprInfo,
2722
+ });
2723
+ }
3267
2724
 
3268
- // Check if this if-statement is an else-if (alternate of parent IfStatement)
3269
- const isElseIf = t.isIfStatement(ifPath.parent) && ifPath.parentKey === 'alternate';
3270
-
3271
- // Determine actual parent scope
3272
- let actualParentScopeId: string;
3273
- if (isElseIf) {
3274
- // For else-if, parent should be the outer BRANCH (stored in ifElseScopeMap)
3275
- const parentIfInfo = ifElseScopeMap.get(ifPath.parent as t.IfStatement);
3276
- if (parentIfInfo) {
3277
- actualParentScopeId = parentIfInfo.branchId;
3278
- } else {
3279
- // Fallback to stack
3280
- actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
3281
- ? scopeIdStack[scopeIdStack.length - 1]
3282
- : parentScopeId;
3283
- }
3284
- } else {
3285
- // For regular if statements, use stack or original parentScopeId
3286
- actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
3287
- ? scopeIdStack[scopeIdStack.length - 1]
3288
- : parentScopeId;
3289
- }
2725
+ // 3. Create handlers and merge their visitors into a single traversal
2726
+ // Cast to AnalyzerDelegate the interface declares the same methods that exist
2727
+ // on this class as private. The cast is safe because the shape matches exactly.
2728
+ const delegate = this as unknown as AnalyzerDelegate;
2729
+ const handlers: FunctionBodyHandler[] = [
2730
+ new VariableHandler(ctx, delegate),
2731
+ new ReturnYieldHandler(ctx, delegate),
2732
+ new ThrowHandler(ctx, delegate),
2733
+ new NestedFunctionHandler(ctx, delegate),
2734
+ new PropertyAccessHandler(ctx, delegate),
2735
+ new NewExpressionHandler(ctx, delegate),
2736
+ new CallExpressionHandler(ctx, delegate),
2737
+ new LoopHandler(ctx, delegate),
2738
+ new TryCatchHandler(ctx, delegate),
2739
+ new BranchHandler(ctx, delegate),
2740
+ ];
2741
+
2742
+ const mergedVisitor: Visitor = {};
2743
+ for (const handler of handlers) {
2744
+ Object.assign(mergedVisitor, handler.getHandlers());
2745
+ }
2746
+
2747
+ // 4. Single traversal over the function body
2748
+ funcPath.traverse(mergedVisitor);
2749
+
2750
+ // 5. Post-traverse: collect CATCHES_FROM info for try/catch blocks
2751
+ if (ctx.functionPath) {
2752
+ this.collectCatchesFromInfo(
2753
+ ctx.functionPath,
2754
+ ctx.catchBlocks,
2755
+ ctx.callSites,
2756
+ ctx.methodCalls,
2757
+ ctx.constructorCalls,
2758
+ ctx.catchesFromInfos,
2759
+ module
2760
+ );
2761
+ }
3290
2762
 
3291
- // 1. Create BRANCH node for if statement
3292
- const branchCounter = branchCounterRef.value++;
3293
- const legacyBranchId = `${module.file}:BRANCH:if:${getLine(ifNode)}:${branchCounter}`;
3294
- const branchId = scopeTracker
3295
- ? computeSemanticId('BRANCH', 'if', scopeTracker.getContext(), { discriminator: branchCounter })
3296
- : legacyBranchId;
3297
-
3298
- // 2. Extract condition expression info for HAS_CONDITION edge
3299
- const conditionResult = this.extractDiscriminantExpression(ifNode.test, module);
3300
-
3301
- // For else-if, get the parent branch ID
3302
- const isAlternateOfBranchId = isElseIf
3303
- ? ifElseScopeMap.get(ifPath.parent as t.IfStatement)?.branchId
3304
- : undefined;
3305
-
3306
- branches.push({
3307
- id: branchId,
3308
- semanticId: branchId,
3309
- type: 'BRANCH',
3310
- branchType: 'if',
3311
- file: module.file,
3312
- line: getLine(ifNode),
3313
- parentScopeId: actualParentScopeId,
3314
- discriminantExpressionId: conditionResult.id,
3315
- discriminantExpressionType: conditionResult.expressionType,
3316
- discriminantLine: conditionResult.line,
3317
- discriminantColumn: conditionResult.column,
3318
- isAlternateOfBranchId
3319
- });
2763
+ // 6. Post-traverse: Attach control flow metadata to the function node
2764
+ this.attachControlFlowMetadata(ctx);
2765
+ }
3320
2766
 
3321
- // 3. Create if-body SCOPE (backward compatibility)
3322
- // Parent is now BRANCH, not original parentScopeId
3323
- const counterId = ifScopeCounterRef.value++;
3324
- const ifScopeId = `SCOPE#if#${module.file}#${getLine(ifNode)}:${getColumn(ifNode)}:${counterId}`;
3325
-
3326
- // Parse condition to extract constraints
3327
- const constraints = ConditionParser.parse(ifNode.test);
3328
- const ifSemanticId = this.generateSemanticId('if_statement', scopeTracker);
3329
-
3330
- scopes.push({
3331
- id: ifScopeId,
3332
- type: 'SCOPE',
3333
- scopeType: 'if_statement',
3334
- name: `if:${getLine(ifNode)}:${getColumn(ifNode)}:${counterId}`,
3335
- semanticId: ifSemanticId,
3336
- conditional: true,
3337
- condition,
3338
- constraints: constraints.length > 0 ? constraints : undefined,
3339
- file: module.file,
3340
- line: getLine(ifNode),
3341
- parentScopeId: branchId // Parent is BRANCH, not original parentScopeId
3342
- });
2767
+ /**
2768
+ * Attach control flow metadata (cyclomatic complexity, error tracking, HOF bindings)
2769
+ * to the matching function node after traversal completes.
2770
+ */
2771
+ private attachControlFlowMetadata(ctx: FunctionBodyContext): void {
2772
+ if (!ctx.matchingFunction) return;
2773
+
2774
+ const cyclomaticComplexity = 1 +
2775
+ ctx.controlFlowState.branchCount +
2776
+ ctx.controlFlowState.loopCount +
2777
+ ctx.controlFlowState.caseCount +
2778
+ ctx.controlFlowState.logicalOpCount;
2779
+
2780
+ // REG-311: Collect rejection info for this function
2781
+ const functionRejectionPatterns = ctx.rejectionPatterns.filter(p => p.functionId === ctx.matchingFunction!.id);
2782
+ const asyncPatterns = functionRejectionPatterns.filter(p => p.isAsync);
2783
+ const syncPatterns = functionRejectionPatterns.filter(p => !p.isAsync);
2784
+ const canReject = asyncPatterns.length > 0;
2785
+ const hasAsyncThrow = asyncPatterns.some(p => p.rejectionType === 'async_throw');
2786
+ const rejectedBuiltinErrors = [...new Set(
2787
+ asyncPatterns
2788
+ .filter(p => p.errorClassName !== null)
2789
+ .map(p => p.errorClassName!)
2790
+ )];
2791
+ // REG-286: Sync throw error tracking
2792
+ const thrownBuiltinErrors = [...new Set(
2793
+ syncPatterns
2794
+ .filter(p => p.errorClassName !== null)
2795
+ .map(p => p.errorClassName!)
2796
+ )];
2797
+
2798
+ ctx.matchingFunction.controlFlow = {
2799
+ hasBranches: ctx.controlFlowState.branchCount > 0,
2800
+ hasLoops: ctx.controlFlowState.loopCount > 0,
2801
+ hasTryCatch: ctx.controlFlowState.hasTryCatch,
2802
+ hasEarlyReturn: ctx.controlFlowState.hasEarlyReturn,
2803
+ hasThrow: ctx.controlFlowState.hasThrow,
2804
+ cyclomaticComplexity,
2805
+ // REG-311: Async error tracking
2806
+ canReject,
2807
+ hasAsyncThrow,
2808
+ rejectedBuiltinErrors: rejectedBuiltinErrors.length > 0 ? rejectedBuiltinErrors : undefined,
2809
+ // REG-286: Sync throw tracking
2810
+ thrownBuiltinErrors: thrownBuiltinErrors.length > 0 ? thrownBuiltinErrors : undefined
2811
+ };
3343
2812
 
3344
- // 4. Push if scope onto stack for CONTAINS edges
3345
- if (scopeIdStack) {
3346
- scopeIdStack.push(ifScopeId);
3347
- }
3348
-
3349
- // Enter scope for semantic ID generation
3350
- if (scopeTracker) {
3351
- scopeTracker.enterCountedScope('if');
3352
- }
3353
-
3354
- // 5. Handle else branch if present
3355
- let elseScopeId: string | null = null;
3356
- if (ifNode.alternate && !t.isIfStatement(ifNode.alternate)) {
3357
- // Only create else scope for actual else block, not else-if
3358
- const elseCounterId = ifScopeCounterRef.value++;
3359
- elseScopeId = `SCOPE#else#${module.file}#${getLine(ifNode.alternate)}:${getColumn(ifNode.alternate)}:${elseCounterId}`;
3360
-
3361
- const negatedConstraints = constraints.length > 0 ? ConditionParser.negate(constraints) : undefined;
3362
- const elseSemanticId = this.generateSemanticId('else_statement', scopeTracker);
3363
-
3364
- scopes.push({
3365
- id: elseScopeId,
3366
- type: 'SCOPE',
3367
- scopeType: 'else_statement',
3368
- name: `else:${getLine(ifNode.alternate)}:${getColumn(ifNode.alternate)}:${elseCounterId}`,
3369
- semanticId: elseSemanticId,
3370
- conditional: true,
3371
- constraints: negatedConstraints,
3372
- file: module.file,
3373
- line: getLine(ifNode.alternate),
3374
- parentScopeId: branchId // Parent is BRANCH, not original parentScopeId
3375
- });
3376
-
3377
- // Store info to switch to else scope when we enter alternate
3378
- ifElseScopeMap.set(ifNode, { inElse: false, hasElse: true, ifScopeId, elseScopeId, branchId });
3379
- } else {
3380
- ifElseScopeMap.set(ifNode, { inElse: false, hasElse: false, ifScopeId, elseScopeId: null, branchId });
3381
- }
3382
- },
3383
- exit: (ifPath: NodePath<t.IfStatement>) => {
3384
- const ifNode = ifPath.node;
3385
-
3386
- // Pop scope from stack (either if or else, depending on what we're exiting)
3387
- if (scopeIdStack) {
3388
- scopeIdStack.pop();
3389
- }
3390
-
3391
- // Exit the current scope (either if or else)
3392
- if (scopeTracker) {
3393
- scopeTracker.exitScope();
3394
- }
3395
-
3396
- // If we were in else, we already exited else scope
3397
- // If we only had if, we exit if scope (done above)
3398
- ifElseScopeMap.delete(ifNode);
3399
- }
3400
- };
3401
- }
3402
-
3403
- /**
3404
- * Factory method to create ConditionalExpression (ternary) handler.
3405
- * Creates BRANCH nodes with branchType='ternary' and increments branchCount for cyclomatic complexity.
3406
- *
3407
- * Key difference from IfStatement: ternary has EXPRESSIONS as branches, not SCOPE blocks.
3408
- * We store consequentExpressionId and alternateExpressionId in BranchInfo for HAS_CONSEQUENT/HAS_ALTERNATE edges.
3409
- *
3410
- * @param parentScopeId - Parent scope ID for the BRANCH node
3411
- * @param module - Module context
3412
- * @param branches - Collection to push BRANCH nodes to
3413
- * @param branchCounterRef - Counter for unique BRANCH IDs
3414
- * @param scopeTracker - Tracker for semantic ID generation
3415
- * @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
3416
- * @param controlFlowState - State for tracking control flow metrics (complexity)
3417
- * @param countLogicalOperators - Function to count logical operators in condition
3418
- */
3419
- private createConditionalExpressionHandler(
3420
- parentScopeId: string,
3421
- module: VisitorModule,
3422
- branches: BranchInfo[],
3423
- branchCounterRef: CounterRef,
3424
- scopeTracker: ScopeTracker | undefined,
3425
- scopeIdStack?: string[],
3426
- controlFlowState?: { branchCount: number; logicalOpCount: number },
3427
- countLogicalOperators?: (node: t.Expression) => number
3428
- ): (condPath: NodePath<t.ConditionalExpression>) => void {
3429
- return (condPath: NodePath<t.ConditionalExpression>) => {
3430
- const condNode = condPath.node;
3431
-
3432
- // Increment branch count for cyclomatic complexity
3433
- if (controlFlowState) {
3434
- controlFlowState.branchCount++;
3435
- // Count logical operators in the test condition (e.g., a && b ? x : y)
3436
- if (countLogicalOperators) {
3437
- controlFlowState.logicalOpCount += countLogicalOperators(condNode.test);
3438
- }
3439
- }
3440
-
3441
- // Determine parent scope from stack or fallback
3442
- const actualParentScopeId = (scopeIdStack && scopeIdStack.length > 0)
3443
- ? scopeIdStack[scopeIdStack.length - 1]
3444
- : parentScopeId;
3445
-
3446
- // Create BRANCH node with branchType='ternary'
3447
- const branchCounter = branchCounterRef.value++;
3448
- const legacyBranchId = `${module.file}:BRANCH:ternary:${getLine(condNode)}:${branchCounter}`;
3449
- const branchId = scopeTracker
3450
- ? computeSemanticId('BRANCH', 'ternary', scopeTracker.getContext(), { discriminator: branchCounter })
3451
- : legacyBranchId;
3452
-
3453
- // Extract condition expression info for HAS_CONDITION edge
3454
- const conditionResult = this.extractDiscriminantExpression(condNode.test, module);
3455
-
3456
- // Generate expression IDs for consequent and alternate
3457
- const consequentLine = getLine(condNode.consequent);
3458
- const consequentColumn = getColumn(condNode.consequent);
3459
- const consequentExpressionId = ExpressionNode.generateId(
3460
- condNode.consequent.type,
3461
- module.file,
3462
- consequentLine,
3463
- consequentColumn
3464
- );
3465
-
3466
- const alternateLine = getLine(condNode.alternate);
3467
- const alternateColumn = getColumn(condNode.alternate);
3468
- const alternateExpressionId = ExpressionNode.generateId(
3469
- condNode.alternate.type,
3470
- module.file,
3471
- alternateLine,
3472
- alternateColumn
3473
- );
3474
-
3475
- branches.push({
3476
- id: branchId,
3477
- semanticId: branchId,
3478
- type: 'BRANCH',
3479
- branchType: 'ternary',
3480
- file: module.file,
3481
- line: getLine(condNode),
3482
- parentScopeId: actualParentScopeId,
3483
- discriminantExpressionId: conditionResult.id,
3484
- discriminantExpressionType: conditionResult.expressionType,
3485
- discriminantLine: conditionResult.line,
3486
- discriminantColumn: conditionResult.column,
3487
- consequentExpressionId,
3488
- alternateExpressionId
3489
- });
3490
- };
3491
- }
3492
-
3493
- /**
3494
- * Factory method to create BlockStatement handler for tracking if/else and try/finally transitions.
3495
- * When entering an else block, switches scope from if to else.
3496
- * When entering a finally block, switches scope from try/catch to finally.
3497
- *
3498
- * @param scopeTracker - Tracker for semantic ID generation
3499
- * @param ifElseScopeMap - Map to track if/else scope transitions
3500
- * @param tryScopeMap - Map to track try/catch/finally scope transitions
3501
- * @param scopeIdStack - Stack for tracking current scope ID for CONTAINS edges
3502
- */
3503
- private createBlockStatementHandler(
3504
- scopeTracker: ScopeTracker | undefined,
3505
- ifElseScopeMap: Map<t.IfStatement, IfElseScopeInfo>,
3506
- tryScopeMap: Map<t.TryStatement, TryScopeInfo>,
3507
- scopeIdStack?: string[]
3508
- ): { enter: (blockPath: NodePath<t.BlockStatement>) => void } {
3509
- return {
3510
- enter: (blockPath: NodePath<t.BlockStatement>) => {
3511
- const parent = blockPath.parent;
3512
-
3513
- // Check if this block is the alternate of an IfStatement
3514
- if (t.isIfStatement(parent) && parent.alternate === blockPath.node) {
3515
- const scopeInfo = ifElseScopeMap.get(parent);
3516
- if (scopeInfo && scopeInfo.hasElse && !scopeInfo.inElse) {
3517
- // Swap if-scope for else-scope on the stack
3518
- if (scopeIdStack && scopeInfo.elseScopeId) {
3519
- scopeIdStack.pop(); // Remove if-scope
3520
- scopeIdStack.push(scopeInfo.elseScopeId); // Push else-scope
3521
- }
3522
-
3523
- // Exit if scope, enter else scope for semantic ID tracking
3524
- if (scopeTracker) {
3525
- scopeTracker.exitScope();
3526
- scopeTracker.enterCountedScope('else');
3527
- }
3528
- scopeInfo.inElse = true;
3529
- }
3530
- }
3531
-
3532
- // Check if this block is the finalizer of a TryStatement
3533
- if (t.isTryStatement(parent) && parent.finalizer === blockPath.node) {
3534
- const scopeInfo = tryScopeMap.get(parent);
3535
- if (scopeInfo && scopeInfo.finallyScopeId && scopeInfo.currentBlock !== 'finally') {
3536
- // Pop current scope (try or catch), push finally scope
3537
- if (scopeIdStack) {
3538
- scopeIdStack.pop();
3539
- scopeIdStack.push(scopeInfo.finallyScopeId);
3540
- }
3541
-
3542
- // Exit current scope, enter finally scope for semantic ID tracking
3543
- if (scopeTracker) {
3544
- scopeTracker.exitScope();
3545
- scopeTracker.enterCountedScope('finally');
3546
- }
3547
- scopeInfo.currentBlock = 'finally';
3548
- }
3549
- }
3550
- }
3551
- };
3552
- }
3553
-
3554
- /**
3555
- * Анализирует тело функции и извлекает переменные, вызовы, условные блоки.
3556
- * Uses ScopeTracker from collections for semantic ID generation.
3557
- */
3558
- analyzeFunctionBody(
3559
- funcPath: NodePath<t.Function | t.StaticBlock>,
3560
- parentScopeId: string,
3561
- module: VisitorModule,
3562
- collections: VisitorCollections
3563
- ): void {
3564
- // Extract with defaults for optional properties
3565
- const functions = (collections.functions ?? []) as FunctionInfo[];
3566
- const scopes = (collections.scopes ?? []) as ScopeInfo[];
3567
- const variableDeclarations = (collections.variableDeclarations ?? []) as VariableDeclarationInfo[];
3568
- const callSites = (collections.callSites ?? []) as CallSiteInfo[];
3569
- const methodCalls = (collections.methodCalls ?? []) as MethodCallInfo[];
3570
- const _eventListeners = (collections.eventListeners ?? []) as EventListenerInfo[];
3571
- const _methodCallbacks = (collections.methodCallbacks ?? []) as MethodCallbackInfo[];
3572
- const classInstantiations = (collections.classInstantiations ?? []) as ClassInstantiationInfo[];
3573
- const constructorCalls = (collections.constructorCalls ?? []) as ConstructorCallInfo[];
3574
- const _httpRequests = (collections.httpRequests ?? []) as HttpRequestInfo[];
3575
- const literals = (collections.literals ?? []) as LiteralInfo[];
3576
- const variableAssignments = (collections.variableAssignments ?? []) as VariableAssignmentInfo[];
3577
- const ifScopeCounterRef = (collections.ifScopeCounterRef ?? { value: 0 }) as CounterRef;
3578
- const scopeCounterRef = (collections.scopeCounterRef ?? { value: 0 }) as CounterRef;
3579
- const varDeclCounterRef = (collections.varDeclCounterRef ?? { value: 0 }) as CounterRef;
3580
- const callSiteCounterRef = (collections.callSiteCounterRef ?? { value: 0 }) as CounterRef;
3581
- const functionCounterRef = (collections.functionCounterRef ?? { value: 0 }) as CounterRef;
3582
- const _httpRequestCounterRef = (collections.httpRequestCounterRef ?? { value: 0 }) as CounterRef;
3583
- const literalCounterRef = (collections.literalCounterRef ?? { value: 0 }) as CounterRef;
3584
- const _anonymousFunctionCounterRef = (collections.anonymousFunctionCounterRef ?? { value: 0 }) as CounterRef;
3585
- const scopeTracker = collections.scopeTracker as ScopeTracker | undefined;
3586
- // Object literal tracking (REG-328)
3587
- if (!collections.objectLiterals) {
3588
- collections.objectLiterals = [];
3589
- }
3590
- if (!collections.objectProperties) {
3591
- collections.objectProperties = [];
3592
- }
3593
- if (!collections.objectLiteralCounterRef) {
3594
- collections.objectLiteralCounterRef = { value: 0 };
3595
- }
3596
- const objectLiterals = collections.objectLiterals as ObjectLiteralInfo[];
3597
- const objectProperties = collections.objectProperties as ObjectPropertyInfo[];
3598
- const objectLiteralCounterRef = collections.objectLiteralCounterRef as CounterRef;
3599
- const returnStatements = (collections.returnStatements ?? []) as ReturnStatementInfo[];
3600
- // Initialize yieldExpressions if not exist to ensure nested function calls share same array
3601
- if (!collections.yieldExpressions) {
3602
- collections.yieldExpressions = [];
3603
- }
3604
- const yieldExpressions = collections.yieldExpressions as YieldExpressionInfo[];
3605
- const _parameters = (collections.parameters ?? []) as ParameterInfo[];
3606
- // Control flow collections (Phase 2: LOOP nodes)
3607
- // Initialize if not exist to ensure nested function calls share same arrays
3608
- if (!collections.loops) {
3609
- collections.loops = [];
3610
- }
3611
- if (!collections.loopCounterRef) {
3612
- collections.loopCounterRef = { value: 0 };
3613
- }
3614
- const loops = collections.loops as LoopInfo[];
3615
- const loopCounterRef = collections.loopCounterRef as CounterRef;
3616
- const updateExpressions = (collections.updateExpressions ?? []) as UpdateExpressionInfo[];
3617
- const processedNodes = collections.processedNodes ?? {
3618
- functions: new Set<string>(),
3619
- classes: new Set<string>(),
3620
- imports: new Set<string>(),
3621
- exports: new Set<string>(),
3622
- variables: new Set<string>(),
3623
- callSites: new Set<string>(),
3624
- methodCalls: new Set<string>(),
3625
- varDecls: new Set<string>(),
3626
- eventListeners: new Set<string>()
3627
- };
3628
-
3629
- const parentScopeVariables = new Set<{ name: string; id: string; scopeId: string }>();
3630
-
3631
- const processedCallSites = processedNodes.callSites;
3632
- const _processedVarDecls = processedNodes.varDecls;
3633
- const processedMethodCalls = processedNodes.methodCalls;
3634
- const _processedEventListeners = processedNodes.eventListeners;
3635
-
3636
- // Track if/else scope transitions (Phase 3: extended with branchId)
3637
- const ifElseScopeMap = new Map<t.IfStatement, IfElseScopeInfo>();
3638
-
3639
- // Ensure branches and branchCounterRef are initialized (used by IfStatement and SwitchStatement)
3640
- if (!collections.branches) {
3641
- collections.branches = [];
3642
- }
3643
- if (!collections.branchCounterRef) {
3644
- collections.branchCounterRef = { value: 0 };
3645
- }
3646
- const branches = collections.branches as BranchInfo[];
3647
- const branchCounterRef = collections.branchCounterRef as CounterRef;
3648
-
3649
- // Phase 4: Initialize try/catch/finally collections and counters
3650
- if (!collections.tryBlocks) {
3651
- collections.tryBlocks = [];
3652
- }
3653
- if (!collections.catchBlocks) {
3654
- collections.catchBlocks = [];
3655
- }
3656
- if (!collections.finallyBlocks) {
3657
- collections.finallyBlocks = [];
3658
- }
3659
- if (!collections.tryBlockCounterRef) {
3660
- collections.tryBlockCounterRef = { value: 0 };
3661
- }
3662
- if (!collections.catchBlockCounterRef) {
3663
- collections.catchBlockCounterRef = { value: 0 };
3664
- }
3665
- if (!collections.finallyBlockCounterRef) {
3666
- collections.finallyBlockCounterRef = { value: 0 };
3667
- }
3668
- const tryBlocks = collections.tryBlocks as TryBlockInfo[];
3669
- const catchBlocks = collections.catchBlocks as CatchBlockInfo[];
3670
- const finallyBlocks = collections.finallyBlocks as FinallyBlockInfo[];
3671
- const tryBlockCounterRef = collections.tryBlockCounterRef as CounterRef;
3672
- const catchBlockCounterRef = collections.catchBlockCounterRef as CounterRef;
3673
- const finallyBlockCounterRef = collections.finallyBlockCounterRef as CounterRef;
3674
-
3675
- // Track try/catch/finally scope transitions
3676
- const tryScopeMap = new Map<t.TryStatement, TryScopeInfo>();
3677
-
3678
- // REG-334: Use shared Promise executor contexts from collections.
3679
- // These are populated by module-level NewExpression handler and function-level NewExpression handler.
3680
- if (!collections.promiseExecutorContexts) {
3681
- collections.promiseExecutorContexts = new Map<string, PromiseExecutorContext>();
3682
- }
3683
- const promiseExecutorContexts = collections.promiseExecutorContexts as Map<string, PromiseExecutorContext>;
3684
-
3685
- // Initialize promiseResolutions array if not exists
3686
- if (!collections.promiseResolutions) {
3687
- collections.promiseResolutions = [];
3688
- }
3689
- const promiseResolutions = collections.promiseResolutions as PromiseResolutionInfo[];
3690
-
3691
- // REG-311: Initialize rejectionPatterns and catchesFromInfos collections
3692
- if (!collections.rejectionPatterns) {
3693
- collections.rejectionPatterns = [];
3694
- }
3695
- if (!collections.catchesFromInfos) {
3696
- collections.catchesFromInfos = [];
3697
- }
3698
- const rejectionPatterns = collections.rejectionPatterns as RejectionPatternInfo[];
3699
- const catchesFromInfos = collections.catchesFromInfos as CatchesFromInfo[];
3700
-
3701
- // Dynamic scope ID stack for CONTAINS edges
3702
- // Starts with the function body scope, gets updated as we enter/exit conditional scopes
3703
- const scopeIdStack: string[] = [parentScopeId];
3704
- const getCurrentScopeId = (): string => scopeIdStack[scopeIdStack.length - 1];
3705
-
3706
- // Determine the ID of the function we're analyzing for RETURNS edges
3707
- // Find by matching file/line/column in functions collection (it was just added by the visitor)
3708
- // REG-271: Skip for StaticBlock (static blocks don't have RETURNS edges or control flow metadata)
3709
- const funcNode = funcPath.node;
3710
- const functionNode = t.isFunction(funcNode) ? funcNode : null;
3711
- const functionPath = functionNode ? (funcPath as NodePath<t.Function>) : null;
3712
- const funcLine = getLine(funcNode);
3713
- const funcColumn = getColumn(funcNode);
3714
- let currentFunctionId: string | null = null;
3715
-
3716
- // StaticBlock is not a function - skip function matching for RETURNS edges
3717
- // For StaticBlock, matchingFunction will be undefined
3718
- const matchingFunction = funcNode.type !== 'StaticBlock'
3719
- ? functions.find(f =>
3720
- f.file === module.file &&
3721
- f.line === funcLine &&
3722
- (f.column === undefined || f.column === funcColumn)
3723
- )
3724
- : undefined;
3725
-
3726
- if (matchingFunction) {
3727
- currentFunctionId = matchingFunction.id;
3728
- }
3729
-
3730
- // Phase 6 (REG-267): Control flow tracking state for cyclomatic complexity
3731
- const controlFlowState = {
3732
- branchCount: 0, // if/switch statements
3733
- loopCount: 0, // for/while/do-while/for-in/for-of
3734
- caseCount: 0, // switch cases (excluding default)
3735
- logicalOpCount: 0, // && and || in conditions
3736
- hasTryCatch: false,
3737
- hasEarlyReturn: false,
3738
- hasThrow: false,
3739
- returnCount: 0, // Track total return count for early return detection
3740
- totalStatements: 0, // Track if there are statements after returns
3741
- // REG-311: Try block depth counter for O(1) isInsideTry detection
3742
- tryBlockDepth: 0
3743
- };
3744
-
3745
- // Handle implicit return for THIS arrow function if it has an expression body
3746
- // e.g., `const double = x => x * 2;` - the function we're analyzing IS an arrow with expression body
3747
- if (t.isArrowFunctionExpression(funcNode) && !t.isBlockStatement(funcNode.body) && currentFunctionId) {
3748
- const bodyExpr = funcNode.body;
3749
- const bodyLine = getLine(bodyExpr);
3750
- const bodyColumn = getColumn(bodyExpr);
3751
-
3752
- // Extract expression-specific info using shared method
3753
- const exprInfo = this.extractReturnExpressionInfo(
3754
- bodyExpr, module, literals, literalCounterRef, funcLine, funcColumn, 'implicit_return'
3755
- );
3756
-
3757
- const returnInfo: ReturnStatementInfo = {
3758
- parentFunctionId: currentFunctionId,
3759
- file: module.file,
3760
- line: bodyLine,
3761
- column: bodyColumn,
3762
- returnValueType: 'NONE',
3763
- isImplicitReturn: true,
3764
- ...exprInfo,
3765
- };
3766
-
3767
- returnStatements.push(returnInfo);
3768
- }
3769
-
3770
- funcPath.traverse({
3771
- VariableDeclaration: (varPath: NodePath<t.VariableDeclaration>) => {
3772
- this.handleVariableDeclaration(
3773
- varPath,
3774
- getCurrentScopeId(),
3775
- module,
3776
- variableDeclarations,
3777
- classInstantiations,
3778
- literals,
3779
- variableAssignments,
3780
- varDeclCounterRef,
3781
- literalCounterRef,
3782
- scopeTracker,
3783
- parentScopeVariables,
3784
- objectLiterals,
3785
- objectProperties,
3786
- objectLiteralCounterRef
3787
- );
3788
- },
3789
-
3790
- // Detect indexed array assignments: arr[i] = value
3791
- AssignmentExpression: (assignPath: NodePath<t.AssignmentExpression>) => {
3792
- const assignNode = assignPath.node;
3793
-
3794
- // === VARIABLE REASSIGNMENT (REG-290) ===
3795
- // Check if LHS is simple identifier (not obj.prop, not arr[i])
3796
- // Must be checked FIRST before array/object mutation handlers
3797
- if (assignNode.left.type === 'Identifier') {
3798
- // Initialize collection if not exists
3799
- if (!collections.variableReassignments) {
3800
- collections.variableReassignments = [];
3801
- }
3802
- const variableReassignments = collections.variableReassignments as VariableReassignmentInfo[];
3803
-
3804
- this.detectVariableReassignment(assignNode, module, variableReassignments, scopeTracker);
3805
- }
3806
- // === END VARIABLE REASSIGNMENT ===
3807
-
3808
- // Initialize collection if not exists
3809
- if (!collections.arrayMutations) {
3810
- collections.arrayMutations = [];
3811
- }
3812
- const arrayMutations = collections.arrayMutations as ArrayMutationInfo[];
3813
-
3814
- // Check for indexed array assignment: arr[i] = value
3815
- this.detectIndexedArrayAssignment(assignNode, module, arrayMutations, scopeTracker, collections);
3816
-
3817
- // Initialize object mutations collection if not exists
3818
- if (!collections.objectMutations) {
3819
- collections.objectMutations = [];
3820
- }
3821
- const objectMutations = collections.objectMutations as ObjectMutationInfo[];
3822
-
3823
- // Check for object property assignment: obj.prop = value
3824
- this.detectObjectPropertyAssignment(assignNode, module, objectMutations, scopeTracker);
3825
- },
3826
-
3827
- // Handle return statements for RETURNS edges
3828
- ReturnStatement: (returnPath: NodePath<t.ReturnStatement>) => {
3829
- // Skip if we couldn't determine the function ID
3830
- if (!currentFunctionId) {
3831
- return;
3832
- }
3833
-
3834
- // Skip if this return is inside a nested function (not the function we're analyzing)
3835
- // Check if there's a function ancestor BETWEEN us and funcNode
3836
- // Stop checking once we reach funcNode - parents above funcNode are outside scope
3837
- let parent: NodePath | null = returnPath.parentPath;
3838
- let isInsideConditional = false;
3839
- while (parent) {
3840
- // If we've reached funcNode, we're done checking - this return belongs to funcNode
3841
- if (parent.node === funcNode) {
3842
- break;
3843
- }
3844
- if (t.isFunction(parent.node)) {
3845
- // Found a function between returnPath and funcNode - this return is inside a nested function
3846
- return;
3847
- }
3848
- // Track if return is inside a conditional block (if/else, switch case, loop, try/catch)
3849
- if (t.isIfStatement(parent.node) ||
3850
- t.isSwitchCase(parent.node) ||
3851
- t.isLoop(parent.node) ||
3852
- t.isTryStatement(parent.node) ||
3853
- t.isCatchClause(parent.node)) {
3854
- isInsideConditional = true;
3855
- }
3856
- parent = parent.parentPath;
3857
- }
3858
-
3859
- // Phase 6 (REG-267): Track return count and early return detection
3860
- controlFlowState.returnCount++;
3861
-
3862
- // A return is "early" if it's inside a conditional structure
3863
- // (More returns after this one indicate the function doesn't always end here)
3864
- if (isInsideConditional) {
3865
- controlFlowState.hasEarlyReturn = true;
3866
- }
3867
-
3868
- const returnNode = returnPath.node;
3869
- const returnLine = getLine(returnNode);
3870
- const returnColumn = getColumn(returnNode);
3871
-
3872
- // Handle bare return; (no value)
3873
- if (!returnNode.argument) {
3874
- // Skip - no data flow value
3875
- return;
3876
- }
3877
-
3878
- const arg = returnNode.argument;
3879
-
3880
- // Extract expression-specific info using shared method
3881
- const exprInfo = this.extractReturnExpressionInfo(
3882
- arg, module, literals, literalCounterRef, returnLine, returnColumn, 'return'
3883
- );
3884
-
3885
- const returnInfo: ReturnStatementInfo = {
3886
- parentFunctionId: currentFunctionId,
3887
- file: module.file,
3888
- line: returnLine,
3889
- column: returnColumn,
3890
- returnValueType: 'NONE',
3891
- ...exprInfo,
3892
- };
3893
-
3894
- returnStatements.push(returnInfo);
3895
- },
3896
-
3897
- // Phase 6 (REG-267): Track throw statements for control flow metadata
3898
- // REG-311: Also detect async_throw rejection patterns
3899
- ThrowStatement: (throwPath: NodePath<t.ThrowStatement>) => {
3900
- // Skip if this throw is inside a nested function (not the function we're analyzing)
3901
- let parent: NodePath | null = throwPath.parentPath;
3902
- while (parent) {
3903
- if (t.isFunction(parent.node) && parent.node !== funcNode) {
3904
- // This throw is inside a nested function - skip it
3905
- return;
3906
- }
3907
- parent = parent.parentPath;
3908
- }
3909
-
3910
- controlFlowState.hasThrow = true;
3911
-
3912
- // REG-311: Track rejection patterns for async functions
3913
- const isAsyncFunction = functionNode?.async === true;
3914
- if (isAsyncFunction && currentFunctionId && functionNode && functionPath) {
3915
- const throwNode = throwPath.node;
3916
- const arg = throwNode.argument;
3917
- const throwLine = getLine(throwNode);
3918
- const throwColumn = getColumn(throwNode);
3919
-
3920
- // Case 1: throw new Error() or throw new CustomError()
3921
- if (arg && t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
3922
- rejectionPatterns.push({
3923
- functionId: currentFunctionId,
3924
- errorClassName: arg.callee.name,
3925
- rejectionType: 'async_throw',
3926
- file: module.file,
3927
- line: throwLine,
3928
- column: throwColumn
3929
- });
3930
- }
3931
- // Case 2: throw identifier - needs micro-trace
3932
- else if (arg && t.isIdentifier(arg)) {
3933
- const varName = arg.name;
3934
-
3935
- // Check if it's a parameter
3936
- const isParameter = functionNode.params.some(param =>
3937
- t.isIdentifier(param) && param.name === varName
3938
- );
3939
-
3940
- if (isParameter) {
3941
- // Parameter forwarding - can't resolve statically
3942
- rejectionPatterns.push({
3943
- functionId: currentFunctionId,
3944
- errorClassName: null,
3945
- rejectionType: 'variable_parameter',
3946
- file: module.file,
3947
- line: throwLine,
3948
- column: throwColumn,
3949
- sourceVariableName: varName
3950
- });
3951
- } else {
3952
- // Try micro-trace
3953
- const { errorClassName, tracePath } = this.microTraceToErrorClass(
3954
- varName,
3955
- functionPath,
3956
- variableDeclarations
3957
- );
3958
-
3959
- rejectionPatterns.push({
3960
- functionId: currentFunctionId,
3961
- errorClassName,
3962
- rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
3963
- file: module.file,
3964
- line: throwLine,
3965
- column: throwColumn,
3966
- sourceVariableName: varName,
3967
- tracePath
3968
- });
3969
- }
3970
- }
3971
- }
3972
- },
3973
-
3974
- // Handle yield expressions for YIELDS/DELEGATES_TO edges (REG-270)
3975
- YieldExpression: (yieldPath: NodePath<t.YieldExpression>) => {
3976
- // Skip if we couldn't determine the function ID
3977
- if (!currentFunctionId) {
3978
- return;
3979
- }
3980
-
3981
- // Skip if this yield is inside a nested function (not the function we're analyzing)
3982
- // Check if there's a function ancestor BETWEEN us and funcNode
3983
- let parent: NodePath | null = yieldPath.parentPath;
3984
- while (parent) {
3985
- // If we've reached funcNode, we're done checking - this yield belongs to funcNode
3986
- if (parent.node === funcNode) {
3987
- break;
3988
- }
3989
- if (t.isFunction(parent.node)) {
3990
- // Found a function between yieldPath and funcNode - this yield is inside a nested function
3991
- return;
3992
- }
3993
- parent = parent.parentPath;
3994
- }
3995
-
3996
- const yieldNode = yieldPath.node;
3997
- const yieldLine = getLine(yieldNode);
3998
- const yieldColumn = getColumn(yieldNode);
3999
- const isDelegate = yieldNode.delegate ?? false;
4000
-
4001
- // Handle bare yield; (no value) - only valid for non-delegate yield
4002
- if (!yieldNode.argument && !isDelegate) {
4003
- // Skip - no data flow value
4004
- return;
4005
- }
4006
-
4007
- // For yield* without argument (syntax error in practice, but handle gracefully)
4008
- if (!yieldNode.argument) {
4009
- return;
4010
- }
4011
-
4012
- const arg = yieldNode.argument;
4013
-
4014
- // Extract expression-specific info using shared method
4015
- // Note: We reuse extractReturnExpressionInfo since yield values have identical semantics
4016
- const exprInfo = this.extractReturnExpressionInfo(
4017
- arg, module, literals, literalCounterRef, yieldLine, yieldColumn, 'yield'
4018
- );
4019
-
4020
- // Map ReturnStatementInfo fields to YieldExpressionInfo fields
4021
- const yieldInfo: YieldExpressionInfo = {
4022
- parentFunctionId: currentFunctionId,
4023
- file: module.file,
4024
- line: yieldLine,
4025
- column: yieldColumn,
4026
- isDelegate,
4027
- yieldValueType: exprInfo.returnValueType ?? 'NONE',
4028
- yieldValueName: exprInfo.returnValueName,
4029
- yieldValueId: exprInfo.returnValueId,
4030
- yieldValueLine: exprInfo.returnValueLine,
4031
- yieldValueColumn: exprInfo.returnValueColumn,
4032
- yieldValueCallName: exprInfo.returnValueCallName,
4033
- expressionType: exprInfo.expressionType,
4034
- operator: exprInfo.operator,
4035
- leftSourceName: exprInfo.leftSourceName,
4036
- rightSourceName: exprInfo.rightSourceName,
4037
- consequentSourceName: exprInfo.consequentSourceName,
4038
- alternateSourceName: exprInfo.alternateSourceName,
4039
- object: exprInfo.object,
4040
- property: exprInfo.property,
4041
- computed: exprInfo.computed,
4042
- objectSourceName: exprInfo.objectSourceName,
4043
- expressionSourceNames: exprInfo.expressionSourceNames,
4044
- unaryArgSourceName: exprInfo.unaryArgSourceName,
4045
- };
4046
-
4047
- yieldExpressions.push(yieldInfo);
4048
- },
4049
-
4050
- ForStatement: this.createLoopScopeHandler('for', 'for-loop', 'for', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
4051
- ForInStatement: this.createLoopScopeHandler('for-in', 'for-in-loop', 'for-in', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
4052
- ForOfStatement: this.createLoopScopeHandler('for-of', 'for-of-loop', 'for-of', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
4053
- WhileStatement: this.createLoopScopeHandler('while', 'while-loop', 'while', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
4054
- DoWhileStatement: this.createLoopScopeHandler('do-while', 'do-while-loop', 'do-while', parentScopeId, module, scopes, loops, scopeCounterRef, loopCounterRef, scopeTracker, scopeIdStack, controlFlowState),
4055
-
4056
- // Phase 4 (REG-267): Now creates TRY_BLOCK, CATCH_BLOCK, FINALLY_BLOCK nodes
4057
- TryStatement: this.createTryStatementHandler(
4058
- parentScopeId,
4059
- module,
4060
- scopes,
4061
- tryBlocks,
4062
- catchBlocks,
4063
- finallyBlocks,
4064
- scopeCounterRef,
4065
- tryBlockCounterRef,
4066
- catchBlockCounterRef,
4067
- finallyBlockCounterRef,
4068
- scopeTracker,
4069
- tryScopeMap,
4070
- scopeIdStack,
4071
- controlFlowState
4072
- ),
4073
-
4074
- CatchClause: this.createCatchClauseHandler(
4075
- module,
4076
- variableDeclarations,
4077
- varDeclCounterRef,
4078
- scopeTracker,
4079
- tryScopeMap,
4080
- scopeIdStack,
4081
- controlFlowState
4082
- ),
4083
-
4084
- SwitchStatement: (switchPath: NodePath<t.SwitchStatement>) => {
4085
- this.handleSwitchStatement(
4086
- switchPath,
4087
- parentScopeId,
4088
- module,
4089
- collections,
4090
- scopeTracker,
4091
- controlFlowState
4092
- );
4093
- },
4094
-
4095
- FunctionDeclaration: (funcDeclPath: NodePath<t.FunctionDeclaration>) => {
4096
- const node = funcDeclPath.node;
4097
- const funcName = node.id ? node.id.name : this.generateAnonymousName(scopeTracker);
4098
- // Use semantic ID as primary ID when scopeTracker available
4099
- const legacyId = `FUNCTION#${funcName}#${module.file}#${getLine(node)}:${getColumn(node)}:${functionCounterRef.value++}`;
4100
- const functionId = scopeTracker
4101
- ? computeSemanticId('FUNCTION', funcName, scopeTracker.getContext())
4102
- : legacyId;
4103
-
4104
- functions.push({
4105
- id: functionId,
4106
- type: 'FUNCTION',
4107
- name: funcName,
4108
- file: module.file,
4109
- line: getLine(node),
4110
- column: getColumn(node),
4111
- async: node.async || false,
4112
- generator: node.generator || false,
4113
- parentScopeId
4114
- });
4115
-
4116
- const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${getLine(node)}`;
4117
- const closureSemanticId = this.generateSemanticId('closure', scopeTracker);
4118
- scopes.push({
4119
- id: nestedScopeId,
4120
- type: 'SCOPE',
4121
- scopeType: 'closure',
4122
- name: `${funcName}:body`,
4123
- semanticId: closureSemanticId,
4124
- conditional: false,
4125
- file: module.file,
4126
- line: getLine(node),
4127
- parentFunctionId: functionId,
4128
- capturesFrom: parentScopeId
4129
- });
4130
-
4131
- // Enter nested function scope for semantic ID generation
4132
- if (scopeTracker) {
4133
- scopeTracker.enterScope(funcName, 'function');
4134
- }
4135
- this.analyzeFunctionBody(funcDeclPath, nestedScopeId, module, collections);
4136
- if (scopeTracker) {
4137
- scopeTracker.exitScope();
4138
- }
4139
- funcDeclPath.skip();
4140
- },
4141
-
4142
- FunctionExpression: (funcPath: NodePath<t.FunctionExpression>) => {
4143
- const node = funcPath.node;
4144
- const funcName = node.id ? node.id.name : this.generateAnonymousName(scopeTracker);
4145
- // Use semantic ID as primary ID when scopeTracker available
4146
- const legacyId = `FUNCTION#${funcName}#${module.file}#${getLine(node)}:${getColumn(node)}:${functionCounterRef.value++}`;
4147
- const functionId = scopeTracker
4148
- ? computeSemanticId('FUNCTION', funcName, scopeTracker.getContext())
4149
- : legacyId;
4150
-
4151
- functions.push({
4152
- id: functionId,
4153
- type: 'FUNCTION',
4154
- name: funcName,
4155
- file: module.file,
4156
- line: getLine(node),
4157
- column: getColumn(node),
4158
- async: node.async || false,
4159
- generator: node.generator || false,
4160
- parentScopeId
4161
- });
4162
-
4163
- const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${getLine(node)}`;
4164
- const closureSemanticId = this.generateSemanticId('closure', scopeTracker);
4165
- scopes.push({
4166
- id: nestedScopeId,
4167
- type: 'SCOPE',
4168
- scopeType: 'closure',
4169
- name: `${funcName}:body`,
4170
- semanticId: closureSemanticId,
4171
- conditional: false,
4172
- file: module.file,
4173
- line: getLine(node),
4174
- parentFunctionId: functionId,
4175
- capturesFrom: parentScopeId
4176
- });
4177
-
4178
- // Enter nested function scope for semantic ID generation
4179
- if (scopeTracker) {
4180
- scopeTracker.enterScope(funcName, 'function');
4181
- }
4182
- this.analyzeFunctionBody(funcPath, nestedScopeId, module, collections);
4183
- if (scopeTracker) {
4184
- scopeTracker.exitScope();
4185
- }
4186
- funcPath.skip();
4187
- },
4188
-
4189
- ArrowFunctionExpression: (arrowPath: NodePath<t.ArrowFunctionExpression>) => {
4190
- const node = arrowPath.node;
4191
- const line = getLine(node);
4192
- const column = getColumn(node);
4193
-
4194
- // Определяем имя (anonymous если не присвоено переменной)
4195
- const parent = arrowPath.parent;
4196
- let funcName: string;
4197
- if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id)) {
4198
- funcName = parent.id.name;
4199
- } else {
4200
- // Используем scope-level счётчик для стабильного semanticId
4201
- funcName = this.generateAnonymousName(scopeTracker);
4202
- }
4203
-
4204
- // Use semantic ID as primary ID when scopeTracker available
4205
- const legacyId = `FUNCTION#${funcName}:${line}:${column}:${functionCounterRef.value++}`;
4206
- const functionId = scopeTracker
4207
- ? computeSemanticId('FUNCTION', funcName, scopeTracker.getContext())
4208
- : legacyId;
4209
-
4210
- functions.push({
4211
- id: functionId,
4212
- type: 'FUNCTION',
4213
- name: funcName,
4214
- file: module.file,
4215
- line,
4216
- column,
4217
- async: node.async || false,
4218
- arrowFunction: true,
4219
- parentScopeId
4220
- });
4221
-
4222
- if (node.body.type === 'BlockStatement') {
4223
- const nestedScopeId = `SCOPE#${funcName}:body#${module.file}#${line}`;
4224
- const arrowSemanticId = this.generateSemanticId('arrow_body', scopeTracker);
4225
- scopes.push({
4226
- id: nestedScopeId,
4227
- type: 'SCOPE',
4228
- scopeType: 'arrow_body',
4229
- name: `${funcName}:body`,
4230
- semanticId: arrowSemanticId,
4231
- conditional: false,
4232
- file: module.file,
4233
- line,
4234
- parentFunctionId: functionId,
4235
- capturesFrom: parentScopeId
4236
- });
4237
-
4238
- // Enter arrow function scope for semantic ID generation
4239
- if (scopeTracker) {
4240
- scopeTracker.enterScope(funcName, 'arrow');
4241
- }
4242
- this.analyzeFunctionBody(arrowPath, nestedScopeId, module, collections);
4243
- if (scopeTracker) {
4244
- scopeTracker.exitScope();
4245
- }
4246
- } else {
4247
- // Arrow function with expression body (implicit return)
4248
- // e.g., x => x * 2, () => 42
4249
- const bodyExpr = node.body;
4250
- const bodyLine = getLine(bodyExpr);
4251
- const bodyColumn = getColumn(bodyExpr);
4252
-
4253
- // Extract expression-specific info using shared method
4254
- const exprInfo = this.extractReturnExpressionInfo(
4255
- bodyExpr, module, literals, literalCounterRef, line, column, 'implicit_return'
4256
- );
4257
-
4258
- const returnInfo: ReturnStatementInfo = {
4259
- parentFunctionId: functionId,
4260
- file: module.file,
4261
- line: bodyLine,
4262
- column: bodyColumn,
4263
- returnValueType: 'NONE',
4264
- isImplicitReturn: true,
4265
- ...exprInfo,
4266
- };
4267
-
4268
- returnStatements.push(returnInfo);
4269
- }
4270
-
4271
- arrowPath.skip();
4272
- },
4273
-
4274
- UpdateExpression: (updatePath: NodePath<t.UpdateExpression>) => {
4275
- const updateNode = updatePath.node;
4276
-
4277
- // REG-288/REG-312: Collect update expression info for graph building
4278
- this.collectUpdateExpression(updateNode, module, updateExpressions, getCurrentScopeId(), scopeTracker);
4279
-
4280
- // Legacy behavior: update scope.modifies for IDENTIFIER targets
4281
- if (updateNode.argument.type === 'Identifier') {
4282
- const varName = updateNode.argument.name;
4283
-
4284
- // Find variable by name - could be from parent scope or declarations
4285
- const fromParentScope = Array.from(parentScopeVariables).find(v => v.name === varName);
4286
- const fromDeclarations = variableDeclarations.find(v => v.name === varName);
4287
- const variable = fromParentScope ?? fromDeclarations;
4288
-
4289
- if (variable) {
4290
- const scope = scopes.find(s => s.id === parentScopeId);
4291
- if (scope) {
4292
- if (!scope.modifies) scope.modifies = [];
4293
- scope.modifies.push({
4294
- variableId: variable.id,
4295
- variableName: varName,
4296
- line: getLine(updateNode)
4297
- });
4298
- }
4299
- }
4300
- }
4301
- },
4302
-
4303
- // IF statements - создаём условные scope и обходим содержимое для CALL узлов
4304
- // Phase 3 (REG-267): Now creates BRANCH nodes with branchType='if'
4305
- IfStatement: this.createIfStatementHandler(
4306
- parentScopeId,
4307
- module,
4308
- scopes,
4309
- branches,
4310
- ifScopeCounterRef,
4311
- branchCounterRef,
4312
- scopeTracker,
4313
- collections.code ?? '',
4314
- ifElseScopeMap,
4315
- scopeIdStack,
4316
- controlFlowState,
4317
- this.countLogicalOperators.bind(this)
4318
- ),
4319
-
4320
- // Ternary expressions (REG-287): Creates BRANCH nodes with branchType='ternary'
4321
- ConditionalExpression: this.createConditionalExpressionHandler(
4322
- parentScopeId,
4323
- module,
4324
- branches,
4325
- branchCounterRef,
4326
- scopeTracker,
4327
- scopeIdStack,
4328
- controlFlowState,
4329
- this.countLogicalOperators.bind(this)
4330
- ),
4331
-
4332
- // Track when we enter the alternate (else) block of an IfStatement
4333
- BlockStatement: this.createBlockStatementHandler(scopeTracker, ifElseScopeMap, tryScopeMap, scopeIdStack),
4334
-
4335
- // Function call expressions
4336
- CallExpression: (callPath: NodePath<t.CallExpression>) => {
4337
- // REG-311: Detect isAwaited (parent is AwaitExpression)
4338
- const parent = callPath.parentPath;
4339
- const isAwaited = parent?.isAwaitExpression() ?? false;
4340
-
4341
- // REG-311: Detect isInsideTry (O(1) via depth counter)
4342
- const isInsideTry = controlFlowState.tryBlockDepth > 0;
4343
-
4344
- this.handleCallExpression(
4345
- callPath.node,
4346
- processedCallSites,
4347
- processedMethodCalls,
4348
- callSites,
4349
- methodCalls,
4350
- module,
4351
- callSiteCounterRef,
4352
- scopeTracker,
4353
- getCurrentScopeId(),
4354
- collections,
4355
- isAwaited,
4356
- isInsideTry
4357
- );
4358
-
4359
- // REG-334: Check for resolve/reject calls inside Promise executors
4360
- const callNode = callPath.node;
4361
- if (t.isIdentifier(callNode.callee)) {
4362
- const calleeName = callNode.callee.name;
4363
-
4364
- // Walk up function parents to find Promise executor context
4365
- // This handles nested callbacks like: new Promise((resolve) => { db.query((err, data) => { resolve(data); }); });
4366
- let funcParent = callPath.getFunctionParent();
4367
- while (funcParent) {
4368
- const funcNode = funcParent.node;
4369
- const funcKey = `${funcNode.start}:${funcNode.end}`;
4370
- const context = promiseExecutorContexts.get(funcKey);
4371
-
4372
- if (context) {
4373
- const isResolve = calleeName === context.resolveName;
4374
- const isReject = calleeName === context.rejectName;
4375
-
4376
- if (isResolve || isReject) {
4377
- // Find the CALL node ID for this resolve/reject call
4378
- // It was just added by handleCallExpression
4379
- const callLine = getLine(callNode);
4380
- const callColumn = getColumn(callNode);
4381
-
4382
- // Find matching call site that was just added
4383
- const resolveCall = callSites.find(cs =>
4384
- cs.name === calleeName &&
4385
- cs.file === module.file &&
4386
- cs.line === callLine &&
4387
- cs.column === callColumn
4388
- );
4389
-
4390
- if (resolveCall) {
4391
- promiseResolutions.push({
4392
- callId: resolveCall.id,
4393
- constructorCallId: context.constructorCallId,
4394
- isReject,
4395
- file: module.file,
4396
- line: callLine
4397
- });
4398
-
4399
- // REG-334: Collect arguments for resolve/reject calls
4400
- // This enables traceValues to follow PASSES_ARGUMENT edges
4401
- if (!collections.callArguments) {
4402
- collections.callArguments = [];
4403
- }
4404
- const callArgumentsArr = collections.callArguments as CallArgumentInfo[];
4405
-
4406
- // Process arguments (typically just one: resolve(value))
4407
- callNode.arguments.forEach((arg, argIndex) => {
4408
- const argInfo: CallArgumentInfo = {
4409
- callId: resolveCall.id,
4410
- argIndex,
4411
- file: module.file,
4412
- line: getLine(arg),
4413
- column: getColumn(arg)
4414
- };
4415
-
4416
- // Handle different argument types
4417
- if (t.isIdentifier(arg)) {
4418
- argInfo.targetType = 'VARIABLE';
4419
- argInfo.targetName = arg.name;
4420
- } else if (t.isLiteral(arg) && !t.isTemplateLiteral(arg)) {
4421
- // Create LITERAL node for the argument value
4422
- const literalValue = ExpressionEvaluator.extractLiteralValue(arg as t.Literal);
4423
- if (literalValue !== null) {
4424
- const argLine = getLine(arg);
4425
- const argColumn = getColumn(arg);
4426
- const literalId = `LITERAL#arg${argIndex}#${module.file}#${argLine}:${argColumn}:${literalCounterRef.value++}`;
4427
- literals.push({
4428
- id: literalId,
4429
- type: 'LITERAL',
4430
- value: literalValue,
4431
- valueType: typeof literalValue,
4432
- file: module.file,
4433
- line: argLine,
4434
- column: argColumn,
4435
- parentCallId: resolveCall.id,
4436
- argIndex
4437
- });
4438
- argInfo.targetType = 'LITERAL';
4439
- argInfo.targetId = literalId;
4440
- argInfo.literalValue = literalValue;
4441
- }
4442
- } else if (t.isCallExpression(arg)) {
4443
- argInfo.targetType = 'CALL';
4444
- argInfo.nestedCallLine = getLine(arg);
4445
- argInfo.nestedCallColumn = getColumn(arg);
4446
- } else {
4447
- argInfo.targetType = 'EXPRESSION';
4448
- argInfo.expressionType = arg.type;
4449
- }
4450
-
4451
- callArgumentsArr.push(argInfo);
4452
- });
4453
- }
4454
-
4455
- break; // Found context, stop searching
4456
- }
4457
- }
4458
-
4459
- funcParent = funcParent.getFunctionParent();
4460
- }
4461
-
4462
- // REG-311: Detect executor_reject pattern - reject(new Error()) inside Promise executor
4463
- // Walk up to find Promise executor context and check if this is reject call with NewExpression arg
4464
- funcParent = callPath.getFunctionParent();
4465
- while (funcParent && currentFunctionId) {
4466
- const funcNode = funcParent.node;
4467
- const funcKey = `${funcNode.start}:${funcNode.end}`;
4468
- const context = promiseExecutorContexts.get(funcKey);
4469
-
4470
- if (context && calleeName === context.rejectName && callNode.arguments.length > 0) {
4471
- // REG-311: Use the creator function's ID (the function that created the Promise),
4472
- // not the executor's ID
4473
- const targetFunctionId = context.creatorFunctionId || currentFunctionId;
4474
- const arg = callNode.arguments[0];
4475
- const callLine = getLine(callNode);
4476
- const callColumn = getColumn(callNode);
4477
-
4478
- // Case 1: reject(new Error())
4479
- if (t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
4480
- rejectionPatterns.push({
4481
- functionId: targetFunctionId,
4482
- errorClassName: arg.callee.name,
4483
- rejectionType: 'executor_reject',
4484
- file: module.file,
4485
- line: callLine,
4486
- column: callColumn
4487
- });
4488
- }
4489
- // Case 2: reject(err) where err is variable
4490
- else if (t.isIdentifier(arg)) {
4491
- const varName = arg.name;
4492
- // Check if it's a parameter of ANY containing function (executor, outer, etc.)
4493
- // Walk up the function chain to find if varName is a parameter
4494
- let isParameter = false;
4495
- let checkParent: NodePath<t.Node> | null = funcParent;
4496
- while (checkParent) {
4497
- if (t.isFunction(checkParent.node)) {
4498
- if (checkParent.node.params.some(p =>
4499
- t.isIdentifier(p) && p.name === varName
4500
- )) {
4501
- isParameter = true;
4502
- break;
4503
- }
4504
- }
4505
- checkParent = checkParent.getFunctionParent();
4506
- }
4507
-
4508
- if (isParameter) {
4509
- rejectionPatterns.push({
4510
- functionId: targetFunctionId,
4511
- errorClassName: null,
4512
- rejectionType: 'variable_parameter',
4513
- file: module.file,
4514
- line: callLine,
4515
- column: callColumn,
4516
- sourceVariableName: varName
4517
- });
4518
- } else {
4519
- // Try micro-trace
4520
- const { errorClassName, tracePath } = this.microTraceToErrorClass(
4521
- varName,
4522
- funcParent as NodePath<t.Function>,
4523
- variableDeclarations
4524
- );
4525
-
4526
- rejectionPatterns.push({
4527
- functionId: targetFunctionId,
4528
- errorClassName,
4529
- rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
4530
- file: module.file,
4531
- line: callLine,
4532
- column: callColumn,
4533
- sourceVariableName: varName,
4534
- tracePath
4535
- });
4536
- }
4537
- }
4538
- break;
4539
- }
4540
- funcParent = funcParent.getFunctionParent();
4541
- }
4542
- }
4543
-
4544
- // REG-311: Detect Promise.reject(new Error()) pattern
4545
- if (t.isMemberExpression(callNode.callee) && currentFunctionId) {
4546
- const memberCallee = callNode.callee;
4547
- if (t.isIdentifier(memberCallee.object) &&
4548
- memberCallee.object.name === 'Promise' &&
4549
- t.isIdentifier(memberCallee.property) &&
4550
- memberCallee.property.name === 'reject' &&
4551
- callNode.arguments.length > 0) {
4552
- const arg = callNode.arguments[0];
4553
- const callLine = getLine(callNode);
4554
- const callColumn = getColumn(callNode);
4555
-
4556
- // Case 1: Promise.reject(new Error())
4557
- if (t.isNewExpression(arg) && t.isIdentifier(arg.callee)) {
4558
- rejectionPatterns.push({
4559
- functionId: currentFunctionId,
4560
- errorClassName: arg.callee.name,
4561
- rejectionType: 'promise_reject',
4562
- file: module.file,
4563
- line: callLine,
4564
- column: callColumn
4565
- });
4566
- }
4567
- // Case 2: Promise.reject(err) where err is variable
4568
- else if (t.isIdentifier(arg)) {
4569
- const varName = arg.name;
4570
- // Check if it's a parameter of containing function
4571
- const isParameter = functionNode
4572
- ? functionNode.params.some(param => t.isIdentifier(param) && param.name === varName)
4573
- : false;
4574
-
4575
- if (isParameter) {
4576
- rejectionPatterns.push({
4577
- functionId: currentFunctionId,
4578
- errorClassName: null,
4579
- rejectionType: 'variable_parameter',
4580
- file: module.file,
4581
- line: callLine,
4582
- column: callColumn,
4583
- sourceVariableName: varName
4584
- });
4585
- } else {
4586
- // Try micro-trace
4587
- if (!functionPath) {
4588
- rejectionPatterns.push({
4589
- functionId: currentFunctionId,
4590
- errorClassName: null,
4591
- rejectionType: 'variable_unknown',
4592
- file: module.file,
4593
- line: callLine,
4594
- column: callColumn,
4595
- sourceVariableName: varName,
4596
- tracePath: [varName]
4597
- });
4598
- return;
4599
- }
4600
-
4601
- const { errorClassName, tracePath } = this.microTraceToErrorClass(
4602
- varName,
4603
- functionPath,
4604
- variableDeclarations
4605
- );
4606
-
4607
- rejectionPatterns.push({
4608
- functionId: currentFunctionId,
4609
- errorClassName,
4610
- rejectionType: errorClassName ? 'variable_traced' : 'variable_unknown',
4611
- file: module.file,
4612
- line: callLine,
4613
- column: callColumn,
4614
- sourceVariableName: varName,
4615
- tracePath
4616
- });
4617
- }
4618
- }
4619
- }
4620
- }
4621
- },
4622
-
4623
- // NewExpression (constructor calls)
4624
- NewExpression: (newPath: NodePath<t.NewExpression>) => {
4625
- const newNode = newPath.node;
4626
- const nodeKey = `new:${newNode.start}:${newNode.end}`;
4627
-
4628
- // Determine className from callee
4629
- let className: string | null = null;
4630
- if (newNode.callee.type === 'Identifier') {
4631
- className = newNode.callee.name;
4632
- } else if (newNode.callee.type === 'MemberExpression' && newNode.callee.property.type === 'Identifier') {
4633
- className = newNode.callee.property.name;
4634
- }
4635
-
4636
- // Create CONSTRUCTOR_CALL node (always, for all NewExpressions)
4637
- if (className) {
4638
- const constructorKey = `constructor:${nodeKey}`;
4639
- if (!processedCallSites.has(constructorKey)) {
4640
- processedCallSites.add(constructorKey);
4641
-
4642
- const line = getLine(newNode);
4643
- const column = getColumn(newNode);
4644
- const constructorCallId = ConstructorCallNode.generateId(className, module.file, line, column);
4645
- const isBuiltin = ConstructorCallNode.isBuiltinConstructor(className);
4646
-
4647
- constructorCalls.push({
4648
- id: constructorCallId,
4649
- type: 'CONSTRUCTOR_CALL',
4650
- className,
4651
- isBuiltin,
4652
- file: module.file,
4653
- line,
4654
- column
4655
- });
4656
-
4657
- // REG-334: If this is Promise constructor with executor callback,
4658
- // register the context for resolve/reject detection
4659
- if (className === 'Promise' && newNode.arguments.length > 0) {
4660
- const executorArg = newNode.arguments[0];
4661
-
4662
- // Only handle inline function expressions (not variable references)
4663
- if (t.isArrowFunctionExpression(executorArg) || t.isFunctionExpression(executorArg)) {
4664
- // Extract resolve/reject parameter names
4665
- let resolveName: string | undefined;
4666
- let rejectName: string | undefined;
4667
-
4668
- if (executorArg.params.length > 0 && t.isIdentifier(executorArg.params[0])) {
4669
- resolveName = executorArg.params[0].name;
4670
- }
4671
- if (executorArg.params.length > 1 && t.isIdentifier(executorArg.params[1])) {
4672
- rejectName = executorArg.params[1].name;
4673
- }
4674
-
4675
- if (resolveName) {
4676
- // Key by function node position to allow nested Promise detection
4677
- const funcKey = `${executorArg.start}:${executorArg.end}`;
4678
- promiseExecutorContexts.set(funcKey, {
4679
- constructorCallId,
4680
- resolveName,
4681
- rejectName,
4682
- file: module.file,
4683
- line,
4684
- // REG-311: Store the ID of the function that creates the Promise
4685
- creatorFunctionId: currentFunctionId || undefined
4686
- });
4687
- }
4688
- }
4689
- }
4690
- }
4691
- }
4692
-
4693
- // Handle simple constructor: new Foo()
4694
- if (newNode.callee.type === 'Identifier') {
4695
- if (processedCallSites.has(nodeKey)) {
4696
- return;
4697
- }
4698
- processedCallSites.add(nodeKey);
4699
-
4700
- // Generate semantic ID (primary) or legacy ID (fallback)
4701
- const constructorName = newNode.callee.name;
4702
- const legacyId = `CALL#new:${constructorName}#${module.file}#${getLine(newNode)}:${getColumn(newNode)}:${callSiteCounterRef.value++}`;
4703
-
4704
- let newCallId = legacyId;
4705
- if (scopeTracker) {
4706
- const discriminator = scopeTracker.getItemCounter(`CALL:new:${constructorName}`);
4707
- newCallId = computeSemanticId('CALL', `new:${constructorName}`, scopeTracker.getContext(), { discriminator });
4708
- }
4709
-
4710
- callSites.push({
4711
- id: newCallId,
4712
- type: 'CALL',
4713
- name: constructorName,
4714
- file: module.file,
4715
- line: getLine(newNode),
4716
- parentScopeId: getCurrentScopeId(),
4717
- targetFunctionName: constructorName,
4718
- isNew: true
4719
- });
4720
- }
4721
- // Handle namespaced constructor: new ns.Constructor()
4722
- else if (newNode.callee.type === 'MemberExpression') {
4723
- const memberCallee = newNode.callee;
4724
- const object = memberCallee.object;
4725
- const property = memberCallee.property;
4726
-
4727
- if (object.type === 'Identifier' && property.type === 'Identifier') {
4728
- if (processedMethodCalls.has(nodeKey)) {
4729
- return;
4730
- }
4731
- processedMethodCalls.add(nodeKey);
4732
-
4733
- const objectName = object.name;
4734
- const constructorName = property.name;
4735
- const fullName = `${objectName}.${constructorName}`;
4736
-
4737
- // Generate semantic ID for method-style constructor call
4738
- const legacyId = `CALL#new:${fullName}#${module.file}#${getLine(newNode)}:${getColumn(newNode)}:${callSiteCounterRef.value++}`;
4739
-
4740
- let newMethodCallId = legacyId;
4741
- if (scopeTracker) {
4742
- const discriminator = scopeTracker.getItemCounter(`CALL:new:${fullName}`);
4743
- newMethodCallId = computeSemanticId('CALL', `new:${fullName}`, scopeTracker.getContext(), { discriminator });
4744
- }
4745
-
4746
- methodCalls.push({
4747
- id: newMethodCallId,
4748
- type: 'CALL',
4749
- name: fullName,
4750
- object: objectName,
4751
- method: constructorName,
4752
- file: module.file,
4753
- line: getLine(newNode),
4754
- column: getColumn(newNode),
4755
- parentScopeId: getCurrentScopeId(),
4756
- isNew: true
4757
- });
4758
- }
4759
- }
4760
- },
4761
-
4762
- // Property access expressions (REG-395)
4763
- // Shared handler for both MemberExpression and OptionalMemberExpression
4764
- MemberExpression: (memberPath: NodePath<t.MemberExpression>) => {
4765
- // Initialize collections if needed
4766
- if (!collections.propertyAccesses) {
4767
- collections.propertyAccesses = [];
4768
- }
4769
- if (!collections.propertyAccessCounterRef) {
4770
- collections.propertyAccessCounterRef = { value: 0 };
4771
- }
4772
-
4773
- PropertyAccessVisitor.extractPropertyAccesses(
4774
- memberPath,
4775
- memberPath.node,
4776
- module,
4777
- collections.propertyAccesses as PropertyAccessInfo[],
4778
- collections.propertyAccessCounterRef as CounterRef,
4779
- scopeTracker,
4780
- currentFunctionId || getCurrentScopeId()
4781
- );
4782
- },
4783
- // OptionalMemberExpression: obj?.prop (same logic as MemberExpression)
4784
- OptionalMemberExpression: (memberPath: NodePath) => {
4785
- // Initialize collections if needed
4786
- if (!collections.propertyAccesses) {
4787
- collections.propertyAccesses = [];
4788
- }
4789
- if (!collections.propertyAccessCounterRef) {
4790
- collections.propertyAccessCounterRef = { value: 0 };
4791
- }
4792
-
4793
- PropertyAccessVisitor.extractPropertyAccesses(
4794
- memberPath,
4795
- memberPath.node as t.MemberExpression,
4796
- module,
4797
- collections.propertyAccesses as PropertyAccessInfo[],
4798
- collections.propertyAccessCounterRef as CounterRef,
4799
- scopeTracker,
4800
- currentFunctionId || getCurrentScopeId()
4801
- );
4802
- }
4803
- });
4804
-
4805
- // REG-311: Second pass - collect CATCHES_FROM info for try/catch blocks
4806
- // This links catch blocks to exception sources in their corresponding try blocks
4807
- if (functionPath) {
4808
- this.collectCatchesFromInfo(
4809
- functionPath,
4810
- catchBlocks,
4811
- callSites,
4812
- methodCalls,
4813
- constructorCalls,
4814
- catchesFromInfos,
4815
- module
4816
- );
4817
- }
4818
-
4819
- // Phase 6 (REG-267): Attach control flow metadata to the function node
4820
- if (matchingFunction) {
4821
- const cyclomaticComplexity = 1 +
4822
- controlFlowState.branchCount +
4823
- controlFlowState.loopCount +
4824
- controlFlowState.caseCount +
4825
- controlFlowState.logicalOpCount;
4826
-
4827
- // REG-311: Collect rejection info for this function
4828
- const functionRejectionPatterns = rejectionPatterns.filter(p => p.functionId === matchingFunction.id);
4829
- const canReject = functionRejectionPatterns.length > 0;
4830
- const hasAsyncThrow = functionRejectionPatterns.some(p => p.rejectionType === 'async_throw');
4831
- const rejectedBuiltinErrors = [...new Set(
4832
- functionRejectionPatterns
4833
- .filter(p => p.errorClassName !== null)
4834
- .map(p => p.errorClassName!)
4835
- )];
4836
-
4837
- matchingFunction.controlFlow = {
4838
- hasBranches: controlFlowState.branchCount > 0,
4839
- hasLoops: controlFlowState.loopCount > 0,
4840
- hasTryCatch: controlFlowState.hasTryCatch,
4841
- hasEarlyReturn: controlFlowState.hasEarlyReturn,
4842
- hasThrow: controlFlowState.hasThrow,
4843
- cyclomaticComplexity,
4844
- // REG-311: Async error tracking
4845
- canReject,
4846
- hasAsyncThrow,
4847
- rejectedBuiltinErrors: rejectedBuiltinErrors.length > 0 ? rejectedBuiltinErrors : undefined
4848
- };
4849
- }
4850
- }
2813
+ // REG-401: Store invoked parameter indexes for user-defined HOF detection
2814
+ if (ctx.invokedParamIndexes.size > 0) {
2815
+ ctx.matchingFunction.invokesParamIndexes = [...ctx.invokedParamIndexes];
2816
+ }
2817
+ // REG-417: Store property paths for destructured param bindings
2818
+ if (ctx.invokesParamBindings.length > 0) {
2819
+ ctx.matchingFunction.invokesParamBindings = ctx.invokesParamBindings;
2820
+ }
2821
+ }
4851
2822
 
4852
2823
  /**
4853
2824
  * Handle CallExpression nodes: direct function calls (greet(), main())
@@ -4885,7 +2856,8 @@ export class JSASTAnalyzer extends Plugin {
4885
2856
  parentScopeId: string,
4886
2857
  collections: VisitorCollections,
4887
2858
  isAwaited: boolean = false,
4888
- isInsideTry: boolean = false
2859
+ isInsideTry: boolean = false,
2860
+ isInsideLoop: boolean = false
4889
2861
  ): void {
4890
2862
  // Handle direct function calls (greet(), main())
4891
2863
  if (callNode.callee.type === 'Identifier') {
@@ -4916,7 +2888,9 @@ export class JSASTAnalyzer extends Plugin {
4916
2888
  targetFunctionName: calleeName,
4917
2889
  // REG-311: Async error tracking metadata
4918
2890
  isAwaited,
4919
- isInsideTry
2891
+ isInsideTry,
2892
+ // REG-298: Await-in-loop detection
2893
+ ...(isAwaited && isInsideLoop ? { isInsideLoop } : {})
4920
2894
  });
4921
2895
  }
4922
2896
  // Handle method calls (obj.method(), data.process())
@@ -4961,9 +2935,16 @@ export class JSASTAnalyzer extends Plugin {
4961
2935
  // REG-311: Async error tracking metadata
4962
2936
  isAwaited,
4963
2937
  isInsideTry,
2938
+ // REG-298: Await-in-loop detection
2939
+ ...(isAwaited && isInsideLoop ? { isInsideLoop } : {}),
4964
2940
  isMethodCall: true
4965
2941
  });
4966
2942
 
2943
+ // REG-400: Extract arguments for method calls (enables callback resolution)
2944
+ if (callNode.arguments.length > 0) {
2945
+ this.extractMethodCallArguments(callNode, methodCallId, module, collections);
2946
+ }
2947
+
4967
2948
  // Check for array mutation methods (push, unshift, splice)
4968
2949
  const ARRAY_MUTATION_METHODS = ['push', 'unshift', 'splice'];
4969
2950
  if (ARRAY_MUTATION_METHODS.includes(methodName)) {
@@ -5065,12 +3046,112 @@ export class JSASTAnalyzer extends Plugin {
5065
3046
  parentScopeId,
5066
3047
  isMethodCall: true
5067
3048
  });
3049
+
3050
+ // REG-400: Extract arguments for nested method calls (enables callback resolution)
3051
+ if (callNode.arguments.length > 0) {
3052
+ this.extractMethodCallArguments(callNode, methodCallId, module, collections);
3053
+ }
5068
3054
  }
5069
3055
  }
5070
3056
  }
5071
3057
  }
5072
3058
  }
5073
3059
 
3060
+ /**
3061
+ * REG-400: Extract arguments from method call nodes inside function bodies.
3062
+ * Populates callArguments collection so GraphBuilder.bufferArgumentEdges can
3063
+ * create PASSES_ARGUMENT and callback CALLS edges.
3064
+ *
3065
+ * This mirrors CallExpressionVisitor.extractArguments but is simplified —
3066
+ * handles Identifier, Literal, CallExpression, and Expression types.
3067
+ */
3068
+ private extractMethodCallArguments(
3069
+ callNode: t.CallExpression,
3070
+ methodCallId: string,
3071
+ module: VisitorModule,
3072
+ collections: VisitorCollections
3073
+ ): void {
3074
+ if (!collections.callArguments) {
3075
+ collections.callArguments = [];
3076
+ }
3077
+ const callArguments = collections.callArguments as CallArgumentInfo[];
3078
+ const literals = (collections.literals ?? []) as LiteralInfo[];
3079
+ const literalCounterRef = (collections.literalCounterRef ?? { value: 0 }) as CounterRef;
3080
+
3081
+ callNode.arguments.forEach((arg, argIndex) => {
3082
+ const argInfo: CallArgumentInfo = {
3083
+ callId: methodCallId,
3084
+ argIndex,
3085
+ file: module.file,
3086
+ line: getLine(arg),
3087
+ column: getColumn(arg)
3088
+ };
3089
+
3090
+ if (t.isSpreadElement(arg)) {
3091
+ const spreadArg = arg.argument;
3092
+ if (t.isIdentifier(spreadArg)) {
3093
+ argInfo.targetType = 'VARIABLE';
3094
+ argInfo.targetName = spreadArg.name;
3095
+ argInfo.isSpread = true;
3096
+ }
3097
+ } else if (t.isIdentifier(arg)) {
3098
+ argInfo.targetType = 'VARIABLE';
3099
+ argInfo.targetName = arg.name;
3100
+ } else if (t.isLiteral(arg) && !t.isTemplateLiteral(arg)) {
3101
+ const literalValue = ExpressionEvaluator.extractLiteralValue(arg as t.Literal);
3102
+ if (literalValue !== null) {
3103
+ const argLine = getLine(arg);
3104
+ const argColumn = getColumn(arg);
3105
+ const literalId = `LITERAL#arg${argIndex}#${module.file}#${argLine}:${argColumn}:${literalCounterRef.value++}`;
3106
+ literals.push({
3107
+ id: literalId,
3108
+ type: 'LITERAL',
3109
+ value: literalValue,
3110
+ valueType: typeof literalValue,
3111
+ file: module.file,
3112
+ line: argLine,
3113
+ column: argColumn,
3114
+ parentCallId: methodCallId,
3115
+ argIndex
3116
+ });
3117
+ argInfo.targetType = 'LITERAL';
3118
+ argInfo.targetId = literalId;
3119
+ argInfo.literalValue = literalValue;
3120
+ }
3121
+ } else if (t.isArrowFunctionExpression(arg) || t.isFunctionExpression(arg)) {
3122
+ argInfo.targetType = 'FUNCTION';
3123
+ argInfo.functionLine = getLine(arg);
3124
+ argInfo.functionColumn = getColumn(arg);
3125
+ } else if (t.isCallExpression(arg)) {
3126
+ argInfo.targetType = 'CALL';
3127
+ argInfo.nestedCallLine = getLine(arg);
3128
+ argInfo.nestedCallColumn = getColumn(arg);
3129
+ // REG-402: MemberExpression arguments (this.handler, obj.method)
3130
+ } else if (t.isMemberExpression(arg)) {
3131
+ argInfo.targetType = 'EXPRESSION';
3132
+ argInfo.expressionType = 'MemberExpression';
3133
+ if (t.isIdentifier(arg.object)) {
3134
+ argInfo.objectName = arg.object.name;
3135
+ } else if (t.isThisExpression(arg.object)) {
3136
+ argInfo.objectName = 'this';
3137
+ // Store enclosing class name for direct lookup in GraphBuilder
3138
+ const scopeTracker = collections.scopeTracker as ScopeTracker | undefined;
3139
+ if (scopeTracker) {
3140
+ argInfo.enclosingClassName = scopeTracker.getEnclosingScope('CLASS');
3141
+ }
3142
+ }
3143
+ if (!arg.computed && t.isIdentifier(arg.property)) {
3144
+ argInfo.propertyName = arg.property.name;
3145
+ }
3146
+ } else {
3147
+ argInfo.targetType = 'EXPRESSION';
3148
+ argInfo.expressionType = arg.type;
3149
+ }
3150
+
3151
+ callArguments.push(argInfo);
3152
+ });
3153
+ }
3154
+
5074
3155
  /**
5075
3156
  * REG-311: Micro-trace - follow variable assignments within function to find error source.
5076
3157
  * Used to resolve reject(err) or throw err where err is a variable.
@@ -5461,34 +3542,12 @@ export class JSASTAnalyzer extends Plugin {
5461
3542
  valueType: 'EXPRESSION'
5462
3543
  };
5463
3544
 
5464
- // Determine value type and create value nodes for non-variable types (REG-392)
5465
- const literalValue = ExpressionEvaluator.extractLiteralValue(value);
5466
- if (literalValue !== null) {
5467
- argInfo.valueType = 'LITERAL';
5468
- argInfo.literalValue = literalValue;
5469
- const valueLine = value.loc?.start.line ?? line;
5470
- const valueColumn = value.loc?.start.column ?? column;
5471
- // Create LITERAL node if collections available
5472
- if (collections?.literals && collections.literalCounterRef) {
5473
- const literalCounterRef = collections.literalCounterRef as CounterRef;
5474
- const literalId = `LITERAL#indexed#${module.file}#${valueLine}:${valueColumn}:${literalCounterRef.value++}`;
5475
- (collections.literals as LiteralInfo[]).push({
5476
- id: literalId,
5477
- type: 'LITERAL',
5478
- value: literalValue,
5479
- valueType: typeof literalValue,
5480
- file: module.file,
5481
- line: valueLine,
5482
- column: valueColumn,
5483
- parentCallId: undefined,
5484
- argIndex: 0
5485
- } as LiteralInfo);
5486
- argInfo.valueNodeId = literalId;
5487
- }
5488
- } else if (value.type === 'Identifier') {
5489
- argInfo.valueType = 'VARIABLE';
5490
- argInfo.valueName = value.name;
5491
- } else if (value.type === 'ObjectExpression') {
3545
+ // Determine value type and create value nodes for non-variable types
3546
+ // IMPORTANT: Check ObjectExpression/ArrayExpression BEFORE extractLiteralValue
3547
+ // to match the order in detectArrayMutation and extractArguments (REG-396).
3548
+ // extractLiteralValue returns objects/arrays with all-literal properties as
3549
+ // literal values, but we want OBJECT_LITERAL/ARRAY_LITERAL nodes instead.
3550
+ if (value.type === 'ObjectExpression') {
5492
3551
  argInfo.valueType = 'OBJECT_LITERAL';
5493
3552
  const valueLine = value.loc?.start.line ?? line;
5494
3553
  const valueColumn = value.loc?.start.column ?? column;
@@ -5518,10 +3577,38 @@ export class JSASTAnalyzer extends Plugin {
5518
3577
  (collections.arrayLiterals as ArrayLiteralInfo[]).push(arrayNode as unknown as ArrayLiteralInfo);
5519
3578
  argInfo.valueNodeId = arrayNode.id;
5520
3579
  }
3580
+ } else if (value.type === 'Identifier') {
3581
+ argInfo.valueType = 'VARIABLE';
3582
+ argInfo.valueName = value.name;
5521
3583
  } else if (value.type === 'CallExpression') {
5522
3584
  argInfo.valueType = 'CALL';
5523
3585
  argInfo.callLine = value.loc?.start.line;
5524
3586
  argInfo.callColumn = value.loc?.start.column;
3587
+ } else {
3588
+ const literalValue = ExpressionEvaluator.extractLiteralValue(value);
3589
+ if (literalValue !== null) {
3590
+ argInfo.valueType = 'LITERAL';
3591
+ argInfo.literalValue = literalValue;
3592
+ const valueLine = value.loc?.start.line ?? line;
3593
+ const valueColumn = value.loc?.start.column ?? column;
3594
+ // Create LITERAL node if collections available
3595
+ if (collections?.literals && collections.literalCounterRef) {
3596
+ const literalCounterRef = collections.literalCounterRef as CounterRef;
3597
+ const literalId = `LITERAL#indexed#${module.file}#${valueLine}:${valueColumn}:${literalCounterRef.value++}`;
3598
+ (collections.literals as LiteralInfo[]).push({
3599
+ id: literalId,
3600
+ type: 'LITERAL',
3601
+ value: literalValue,
3602
+ valueType: typeof literalValue,
3603
+ file: module.file,
3604
+ line: valueLine,
3605
+ column: valueColumn,
3606
+ parentCallId: undefined,
3607
+ argIndex: 0
3608
+ } as LiteralInfo);
3609
+ argInfo.valueNodeId = literalId;
3610
+ }
3611
+ }
5525
3612
  }
5526
3613
 
5527
3614
  // Capture scope path for scope-aware lookup (REG-309)