@grafema/core 0.1.0-alpha.5 → 0.1.1-alpha

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 (391) hide show
  1. package/README.md +0 -1
  2. package/dist/Orchestrator.d.ts +24 -2
  3. package/dist/Orchestrator.d.ts.map +1 -1
  4. package/dist/Orchestrator.js +197 -24
  5. package/dist/config/ConfigLoader.d.ts +72 -0
  6. package/dist/config/ConfigLoader.d.ts.map +1 -0
  7. package/dist/config/ConfigLoader.js +187 -0
  8. package/dist/config/index.d.ts +6 -0
  9. package/dist/config/index.d.ts.map +1 -0
  10. package/dist/config/index.js +4 -0
  11. package/dist/core/ASTWorker.d.ts +11 -36
  12. package/dist/core/ASTWorker.d.ts.map +1 -1
  13. package/dist/core/ASTWorker.js +93 -99
  14. package/dist/core/CoverageAnalyzer.d.ts +65 -0
  15. package/dist/core/CoverageAnalyzer.d.ts.map +1 -0
  16. package/dist/core/CoverageAnalyzer.js +198 -0
  17. package/dist/core/FileNodeManager.d.ts +40 -0
  18. package/dist/core/FileNodeManager.d.ts.map +1 -0
  19. package/dist/core/FileNodeManager.js +84 -0
  20. package/dist/core/GraphFreshnessChecker.d.ts +33 -0
  21. package/dist/core/GraphFreshnessChecker.d.ts.map +1 -0
  22. package/dist/core/GraphFreshnessChecker.js +101 -0
  23. package/dist/core/HashUtils.d.ts +24 -0
  24. package/dist/core/HashUtils.d.ts.map +1 -0
  25. package/dist/core/HashUtils.js +45 -0
  26. package/dist/core/IncrementalReanalyzer.d.ts +36 -0
  27. package/dist/core/IncrementalReanalyzer.d.ts.map +1 -0
  28. package/dist/core/IncrementalReanalyzer.js +132 -0
  29. package/dist/core/NodeFactory.d.ts +225 -17
  30. package/dist/core/NodeFactory.d.ts.map +1 -1
  31. package/dist/core/NodeFactory.js +208 -18
  32. package/dist/core/ScopeTracker.d.ts +84 -0
  33. package/dist/core/ScopeTracker.d.ts.map +1 -0
  34. package/dist/core/ScopeTracker.js +116 -0
  35. package/dist/core/SemanticId.d.ts +90 -0
  36. package/dist/core/SemanticId.d.ts.map +1 -0
  37. package/dist/core/SemanticId.js +115 -0
  38. package/dist/core/VersionManager.d.ts.map +1 -1
  39. package/dist/core/VersionManager.js +3 -2
  40. package/dist/core/nodes/ArgumentExpressionNode.d.ts +43 -0
  41. package/dist/core/nodes/ArgumentExpressionNode.d.ts.map +1 -0
  42. package/dist/core/nodes/ArgumentExpressionNode.js +60 -0
  43. package/dist/core/nodes/ArrayLiteralNode.d.ts +27 -0
  44. package/dist/core/nodes/ArrayLiteralNode.d.ts.map +1 -0
  45. package/dist/core/nodes/ArrayLiteralNode.js +41 -0
  46. package/dist/core/nodes/CallSiteNode.d.ts +28 -0
  47. package/dist/core/nodes/CallSiteNode.d.ts.map +1 -1
  48. package/dist/core/nodes/CallSiteNode.js +46 -0
  49. package/dist/core/nodes/ClassNode.d.ts +33 -1
  50. package/dist/core/nodes/ClassNode.d.ts.map +1 -1
  51. package/dist/core/nodes/ClassNode.js +46 -2
  52. package/dist/core/nodes/DecoratorNode.d.ts +42 -0
  53. package/dist/core/nodes/DecoratorNode.d.ts.map +1 -0
  54. package/dist/core/nodes/DecoratorNode.js +62 -0
  55. package/dist/core/nodes/EnumNode.d.ts +42 -0
  56. package/dist/core/nodes/EnumNode.d.ts.map +1 -0
  57. package/dist/core/nodes/EnumNode.js +54 -0
  58. package/dist/core/nodes/ExportNode.d.ts +37 -1
  59. package/dist/core/nodes/ExportNode.d.ts.map +1 -1
  60. package/dist/core/nodes/ExportNode.js +48 -2
  61. package/dist/core/nodes/ExpressionNode.d.ts +97 -0
  62. package/dist/core/nodes/ExpressionNode.d.ts.map +1 -0
  63. package/dist/core/nodes/ExpressionNode.js +178 -0
  64. package/dist/core/nodes/ExternalModuleNode.d.ts +28 -0
  65. package/dist/core/nodes/ExternalModuleNode.d.ts.map +1 -0
  66. package/dist/core/nodes/ExternalModuleNode.js +41 -0
  67. package/dist/core/nodes/ExternalStdioNode.d.ts +13 -6
  68. package/dist/core/nodes/ExternalStdioNode.d.ts.map +1 -1
  69. package/dist/core/nodes/ExternalStdioNode.js +15 -8
  70. package/dist/core/nodes/FunctionNode.d.ts +36 -0
  71. package/dist/core/nodes/FunctionNode.d.ts.map +1 -1
  72. package/dist/core/nodes/FunctionNode.js +80 -1
  73. package/dist/core/nodes/ImportNode.d.ts +19 -5
  74. package/dist/core/nodes/ImportNode.d.ts.map +1 -1
  75. package/dist/core/nodes/ImportNode.js +23 -5
  76. package/dist/core/nodes/InterfaceNode.d.ts +46 -0
  77. package/dist/core/nodes/InterfaceNode.d.ts.map +1 -0
  78. package/dist/core/nodes/InterfaceNode.js +55 -0
  79. package/dist/core/nodes/IssueNode.d.ts +73 -0
  80. package/dist/core/nodes/IssueNode.d.ts.map +1 -0
  81. package/dist/core/nodes/IssueNode.js +129 -0
  82. package/dist/core/nodes/MethodCallNode.d.ts +30 -0
  83. package/dist/core/nodes/MethodCallNode.d.ts.map +1 -1
  84. package/dist/core/nodes/MethodCallNode.js +49 -0
  85. package/dist/core/nodes/MethodNode.d.ts +32 -0
  86. package/dist/core/nodes/MethodNode.d.ts.map +1 -1
  87. package/dist/core/nodes/MethodNode.js +48 -0
  88. package/dist/core/nodes/ModuleNode.d.ts +31 -0
  89. package/dist/core/nodes/ModuleNode.d.ts.map +1 -1
  90. package/dist/core/nodes/ModuleNode.js +37 -0
  91. package/dist/core/nodes/NetworkRequestNode.d.ts +54 -0
  92. package/dist/core/nodes/NetworkRequestNode.d.ts.map +1 -0
  93. package/dist/core/nodes/NetworkRequestNode.js +65 -0
  94. package/dist/core/nodes/ObjectLiteralNode.d.ts +27 -0
  95. package/dist/core/nodes/ObjectLiteralNode.d.ts.map +1 -0
  96. package/dist/core/nodes/ObjectLiteralNode.js +41 -0
  97. package/dist/core/nodes/ScopeNode.d.ts +31 -0
  98. package/dist/core/nodes/ScopeNode.d.ts.map +1 -1
  99. package/dist/core/nodes/ScopeNode.js +49 -0
  100. package/dist/core/nodes/TypeNode.d.ts +36 -0
  101. package/dist/core/nodes/TypeNode.d.ts.map +1 -0
  102. package/dist/core/nodes/TypeNode.js +53 -0
  103. package/dist/core/nodes/VariableDeclarationNode.d.ts +27 -0
  104. package/dist/core/nodes/VariableDeclarationNode.d.ts.map +1 -1
  105. package/dist/core/nodes/VariableDeclarationNode.js +40 -0
  106. package/dist/core/nodes/index.d.ts +12 -1
  107. package/dist/core/nodes/index.d.ts.map +1 -1
  108. package/dist/core/nodes/index.js +14 -0
  109. package/dist/diagnostics/DiagnosticCollector.d.ts +98 -0
  110. package/dist/diagnostics/DiagnosticCollector.d.ts.map +1 -0
  111. package/dist/diagnostics/DiagnosticCollector.js +129 -0
  112. package/dist/diagnostics/DiagnosticReporter.d.ts +77 -0
  113. package/dist/diagnostics/DiagnosticReporter.d.ts.map +1 -0
  114. package/dist/diagnostics/DiagnosticReporter.js +159 -0
  115. package/dist/diagnostics/DiagnosticWriter.d.ts +31 -0
  116. package/dist/diagnostics/DiagnosticWriter.d.ts.map +1 -0
  117. package/dist/diagnostics/DiagnosticWriter.js +43 -0
  118. package/dist/diagnostics/index.d.ts +14 -0
  119. package/dist/diagnostics/index.d.ts.map +1 -0
  120. package/dist/diagnostics/index.js +11 -0
  121. package/dist/errors/GrafemaError.d.ts +118 -0
  122. package/dist/errors/GrafemaError.d.ts.map +1 -0
  123. package/dist/errors/GrafemaError.js +131 -0
  124. package/dist/index.d.ts +57 -1
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/index.js +54 -1
  127. package/dist/logging/Logger.d.ts +48 -0
  128. package/dist/logging/Logger.d.ts.map +1 -0
  129. package/dist/logging/Logger.js +134 -0
  130. package/dist/plugins/Plugin.d.ts +5 -1
  131. package/dist/plugins/Plugin.d.ts.map +1 -1
  132. package/dist/plugins/Plugin.js +33 -0
  133. package/dist/plugins/analysis/DatabaseAnalyzer.d.ts.map +1 -1
  134. package/dist/plugins/analysis/DatabaseAnalyzer.js +13 -6
  135. package/dist/plugins/analysis/ExpressAnalyzer.d.ts.map +1 -1
  136. package/dist/plugins/analysis/ExpressAnalyzer.js +27 -19
  137. package/dist/plugins/analysis/ExpressRouteAnalyzer.d.ts.map +1 -1
  138. package/dist/plugins/analysis/ExpressRouteAnalyzer.js +21 -14
  139. package/dist/plugins/analysis/FetchAnalyzer.d.ts +1 -0
  140. package/dist/plugins/analysis/FetchAnalyzer.d.ts.map +1 -1
  141. package/dist/plugins/analysis/FetchAnalyzer.js +34 -14
  142. package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts +6 -3
  143. package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts.map +1 -1
  144. package/dist/plugins/analysis/IncrementalAnalysisPlugin.js +76 -80
  145. package/dist/plugins/analysis/JSASTAnalyzer.d.ts +180 -17
  146. package/dist/plugins/analysis/JSASTAnalyzer.d.ts.map +1 -1
  147. package/dist/plugins/analysis/JSASTAnalyzer.js +1171 -471
  148. package/dist/plugins/analysis/ReactAnalyzer.d.ts.map +1 -1
  149. package/dist/plugins/analysis/ReactAnalyzer.js +56 -57
  150. package/dist/plugins/analysis/RustAnalyzer.d.ts.map +1 -1
  151. package/dist/plugins/analysis/RustAnalyzer.js +15 -10
  152. package/dist/plugins/analysis/SQLiteAnalyzer.d.ts.map +1 -1
  153. package/dist/plugins/analysis/SQLiteAnalyzer.js +9 -7
  154. package/dist/plugins/analysis/ServiceLayerAnalyzer.d.ts.map +1 -1
  155. package/dist/plugins/analysis/ServiceLayerAnalyzer.js +21 -9
  156. package/dist/plugins/analysis/SocketIOAnalyzer.d.ts.map +1 -1
  157. package/dist/plugins/analysis/SocketIOAnalyzer.js +27 -15
  158. package/dist/plugins/analysis/SystemDbAnalyzer.d.ts.map +1 -1
  159. package/dist/plugins/analysis/SystemDbAnalyzer.js +15 -5
  160. package/dist/plugins/analysis/ast/GraphBuilder.d.ts +34 -4
  161. package/dist/plugins/analysis/ast/GraphBuilder.d.ts.map +1 -1
  162. package/dist/plugins/analysis/ast/GraphBuilder.js +318 -298
  163. package/dist/plugins/analysis/ast/IdGenerator.d.ts +105 -0
  164. package/dist/plugins/analysis/ast/IdGenerator.d.ts.map +1 -0
  165. package/dist/plugins/analysis/ast/IdGenerator.js +116 -0
  166. package/dist/plugins/analysis/ast/types.d.ts +176 -5
  167. package/dist/plugins/analysis/ast/types.d.ts.map +1 -1
  168. package/dist/plugins/analysis/ast/utils/createParameterNodes.d.ts +33 -0
  169. package/dist/plugins/analysis/ast/utils/createParameterNodes.d.ts.map +1 -0
  170. package/dist/plugins/analysis/ast/utils/createParameterNodes.js +89 -0
  171. package/dist/plugins/analysis/ast/utils/index.d.ts +6 -0
  172. package/dist/plugins/analysis/ast/utils/index.d.ts.map +1 -0
  173. package/dist/plugins/analysis/ast/utils/index.js +5 -0
  174. package/dist/plugins/analysis/ast/utils/location.d.ts +87 -0
  175. package/dist/plugins/analysis/ast/utils/location.d.ts.map +1 -0
  176. package/dist/plugins/analysis/ast/utils/location.js +78 -0
  177. package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts +9 -4
  178. package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts.map +1 -1
  179. package/dist/plugins/analysis/ast/visitors/ASTVisitor.js +6 -5
  180. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts +99 -9
  181. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts.map +1 -1
  182. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js +663 -125
  183. package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts +4 -1
  184. package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts.map +1 -1
  185. package/dist/plugins/analysis/ast/visitors/ClassVisitor.js +72 -32
  186. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts +4 -1
  187. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts.map +1 -1
  188. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js +128 -63
  189. package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.d.ts.map +1 -1
  190. package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.js +11 -8
  191. package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts +12 -1
  192. package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts.map +1 -1
  193. package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.js +36 -14
  194. package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts +4 -1
  195. package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts.map +1 -1
  196. package/dist/plugins/analysis/ast/visitors/VariableVisitor.js +17 -13
  197. package/dist/plugins/discovery/MonorepoServiceDiscovery.d.ts.map +1 -1
  198. package/dist/plugins/discovery/MonorepoServiceDiscovery.js +3 -2
  199. package/dist/plugins/discovery/SimpleProjectDiscovery.d.ts.map +1 -1
  200. package/dist/plugins/discovery/SimpleProjectDiscovery.js +5 -1
  201. package/dist/plugins/discovery/WorkspaceDiscovery.d.ts +22 -0
  202. package/dist/plugins/discovery/WorkspaceDiscovery.d.ts.map +1 -0
  203. package/dist/plugins/discovery/WorkspaceDiscovery.js +136 -0
  204. package/dist/plugins/discovery/resolveSourceEntrypoint.d.ts +46 -0
  205. package/dist/plugins/discovery/resolveSourceEntrypoint.d.ts.map +1 -0
  206. package/dist/plugins/discovery/resolveSourceEntrypoint.js +86 -0
  207. package/dist/plugins/discovery/workspaces/detector.d.ts +21 -0
  208. package/dist/plugins/discovery/workspaces/detector.d.ts.map +1 -0
  209. package/dist/plugins/discovery/workspaces/detector.js +49 -0
  210. package/dist/plugins/discovery/workspaces/globResolver.d.ts +35 -0
  211. package/dist/plugins/discovery/workspaces/globResolver.d.ts.map +1 -0
  212. package/dist/plugins/discovery/workspaces/globResolver.js +184 -0
  213. package/dist/plugins/discovery/workspaces/index.d.ts +9 -0
  214. package/dist/plugins/discovery/workspaces/index.d.ts.map +1 -0
  215. package/dist/plugins/discovery/workspaces/index.js +8 -0
  216. package/dist/plugins/discovery/workspaces/parsers.d.ts +38 -0
  217. package/dist/plugins/discovery/workspaces/parsers.d.ts.map +1 -0
  218. package/dist/plugins/discovery/workspaces/parsers.js +80 -0
  219. package/dist/plugins/enrichment/AliasTracker.d.ts.map +1 -1
  220. package/dist/plugins/enrichment/AliasTracker.js +14 -8
  221. package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts.map +1 -1
  222. package/dist/plugins/enrichment/HTTPConnectionEnricher.js +14 -7
  223. package/dist/plugins/enrichment/ImportExportLinker.d.ts.map +1 -1
  224. package/dist/plugins/enrichment/ImportExportLinker.js +23 -6
  225. package/dist/plugins/enrichment/MethodCallResolver.d.ts.map +1 -1
  226. package/dist/plugins/enrichment/MethodCallResolver.js +18 -12
  227. package/dist/plugins/enrichment/MountPointResolver.d.ts.map +1 -1
  228. package/dist/plugins/enrichment/MountPointResolver.js +8 -3
  229. package/dist/plugins/enrichment/PrefixEvaluator.d.ts.map +1 -1
  230. package/dist/plugins/enrichment/PrefixEvaluator.js +16 -7
  231. package/dist/plugins/enrichment/RustFFIEnricher.d.ts.map +1 -1
  232. package/dist/plugins/enrichment/RustFFIEnricher.js +6 -5
  233. package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts +17 -0
  234. package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts.map +1 -1
  235. package/dist/plugins/enrichment/ValueDomainAnalyzer.js +129 -10
  236. package/dist/plugins/indexing/IncrementalModuleIndexer.d.ts.map +1 -1
  237. package/dist/plugins/indexing/IncrementalModuleIndexer.js +23 -14
  238. package/dist/plugins/indexing/JSModuleIndexer.d.ts.map +1 -1
  239. package/dist/plugins/indexing/JSModuleIndexer.js +63 -31
  240. package/dist/plugins/indexing/RustModuleIndexer.d.ts.map +1 -1
  241. package/dist/plugins/indexing/RustModuleIndexer.js +5 -4
  242. package/dist/plugins/indexing/ServiceDetector.d.ts +10 -0
  243. package/dist/plugins/indexing/ServiceDetector.d.ts.map +1 -1
  244. package/dist/plugins/indexing/ServiceDetector.js +28 -15
  245. package/dist/plugins/validation/CallResolverValidator.d.ts.map +1 -1
  246. package/dist/plugins/validation/CallResolverValidator.js +8 -7
  247. package/dist/plugins/validation/DataFlowValidator.d.ts.map +1 -1
  248. package/dist/plugins/validation/DataFlowValidator.js +17 -12
  249. package/dist/plugins/validation/EvalBanValidator.d.ts.map +1 -1
  250. package/dist/plugins/validation/EvalBanValidator.js +17 -16
  251. package/dist/plugins/validation/GraphConnectivityValidator.d.ts.map +1 -1
  252. package/dist/plugins/validation/GraphConnectivityValidator.js +19 -23
  253. package/dist/plugins/validation/NodeCreationValidator.d.ts +85 -0
  254. package/dist/plugins/validation/NodeCreationValidator.d.ts.map +1 -0
  255. package/dist/plugins/validation/NodeCreationValidator.js +415 -0
  256. package/dist/plugins/validation/SQLInjectionValidator.d.ts.map +1 -1
  257. package/dist/plugins/validation/SQLInjectionValidator.js +59 -16
  258. package/dist/plugins/validation/ShadowingDetector.d.ts.map +1 -1
  259. package/dist/plugins/validation/ShadowingDetector.js +6 -5
  260. package/dist/plugins/validation/TypeScriptDeadCodeValidator.d.ts.map +1 -1
  261. package/dist/plugins/validation/TypeScriptDeadCodeValidator.js +12 -11
  262. package/dist/plugins/vcs/GitPlugin.d.ts.map +1 -1
  263. package/dist/plugins/vcs/GitPlugin.js +10 -12
  264. package/dist/plugins/vcs/VCSPlugin.d.ts +3 -2
  265. package/dist/plugins/vcs/VCSPlugin.d.ts.map +1 -1
  266. package/dist/plugins/vcs/VCSPlugin.js +5 -5
  267. package/dist/storage/backends/RFDBServerBackend.d.ts +10 -17
  268. package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -1
  269. package/dist/storage/backends/RFDBServerBackend.js +31 -10
  270. package/dist/validation/PathValidator.d.ts +1 -2
  271. package/dist/validation/PathValidator.d.ts.map +1 -1
  272. package/package.json +3 -3
  273. package/src/Orchestrator.ts +237 -24
  274. package/src/config/ConfigLoader.ts +263 -0
  275. package/src/config/index.ts +5 -0
  276. package/src/core/ASTWorker.ts +143 -139
  277. package/src/core/CoverageAnalyzer.ts +243 -0
  278. package/src/core/FileNodeManager.ts +100 -0
  279. package/src/core/GraphFreshnessChecker.ts +143 -0
  280. package/src/core/HashUtils.ts +48 -0
  281. package/src/core/IncrementalReanalyzer.ts +192 -0
  282. package/src/core/NodeFactory.ts +401 -18
  283. package/src/core/ScopeTracker.ts +154 -0
  284. package/src/core/SemanticId.ts +192 -0
  285. package/src/core/VersionManager.ts +3 -2
  286. package/src/core/nodes/ArgumentExpressionNode.ts +89 -0
  287. package/src/core/nodes/ArrayLiteralNode.ts +65 -0
  288. package/src/core/nodes/CallSiteNode.ts +58 -0
  289. package/src/core/nodes/ClassNode.ts +63 -2
  290. package/src/core/nodes/DecoratorNode.ts +91 -0
  291. package/src/core/nodes/EnumNode.ts +86 -0
  292. package/src/core/nodes/ExportNode.ts +70 -2
  293. package/src/core/nodes/ExpressionNode.ts +231 -0
  294. package/src/core/nodes/ExternalModuleNode.ts +56 -0
  295. package/src/core/nodes/ExternalStdioNode.ts +17 -9
  296. package/src/core/nodes/FunctionNode.ts +101 -1
  297. package/src/core/nodes/ImportNode.ts +32 -10
  298. package/src/core/nodes/InterfaceNode.ts +91 -0
  299. package/src/core/nodes/IssueNode.ts +177 -0
  300. package/src/core/nodes/MethodCallNode.ts +64 -0
  301. package/src/core/nodes/MethodNode.ts +63 -0
  302. package/src/core/nodes/ModuleNode.ts +50 -0
  303. package/src/core/nodes/NetworkRequestNode.ts +77 -0
  304. package/src/core/nodes/ObjectLiteralNode.ts +65 -0
  305. package/src/core/nodes/ScopeNode.ts +65 -0
  306. package/src/core/nodes/TypeNode.ts +78 -0
  307. package/src/core/nodes/VariableDeclarationNode.ts +52 -0
  308. package/src/core/nodes/index.ts +18 -1
  309. package/src/diagnostics/DiagnosticCollector.ts +163 -0
  310. package/src/diagnostics/DiagnosticReporter.ts +204 -0
  311. package/src/diagnostics/DiagnosticWriter.ts +50 -0
  312. package/src/diagnostics/index.ts +16 -0
  313. package/src/errors/GrafemaError.ts +174 -0
  314. package/src/index.ts +148 -1
  315. package/src/logging/Logger.ts +152 -0
  316. package/src/plugins/Plugin.ts +42 -0
  317. package/src/plugins/analysis/DatabaseAnalyzer.ts +14 -8
  318. package/src/plugins/analysis/ExpressAnalyzer.ts +29 -19
  319. package/src/plugins/analysis/ExpressRouteAnalyzer.ts +22 -21
  320. package/src/plugins/analysis/FetchAnalyzer.ts +39 -16
  321. package/src/plugins/analysis/IncrementalAnalysisPlugin.ts +84 -101
  322. package/src/plugins/analysis/JSASTAnalyzer.ts +1483 -503
  323. package/src/plugins/analysis/ReactAnalyzer.ts +57 -57
  324. package/src/plugins/analysis/RustAnalyzer.ts +15 -10
  325. package/src/plugins/analysis/SQLiteAnalyzer.ts +10 -7
  326. package/src/plugins/analysis/ServiceLayerAnalyzer.ts +22 -16
  327. package/src/plugins/analysis/SocketIOAnalyzer.ts +31 -22
  328. package/src/plugins/analysis/SystemDbAnalyzer.ts +16 -11
  329. package/src/plugins/analysis/ast/GraphBuilder.ts +439 -327
  330. package/src/plugins/analysis/ast/IdGenerator.ts +177 -0
  331. package/src/plugins/analysis/ast/types.ts +209 -6
  332. package/src/plugins/analysis/ast/utils/createParameterNodes.ts +104 -0
  333. package/src/plugins/analysis/ast/utils/index.ts +12 -0
  334. package/src/plugins/analysis/ast/utils/location.ts +103 -0
  335. package/src/plugins/analysis/ast/visitors/ASTVisitor.ts +11 -8
  336. package/src/plugins/analysis/ast/visitors/CallExpressionVisitor.ts +909 -83
  337. package/src/plugins/analysis/ast/visitors/ClassVisitor.ts +97 -44
  338. package/src/plugins/analysis/ast/visitors/FunctionVisitor.ts +159 -93
  339. package/src/plugins/analysis/ast/visitors/ImportExportVisitor.ts +12 -8
  340. package/src/plugins/analysis/ast/visitors/TypeScriptVisitor.ts +41 -14
  341. package/src/plugins/analysis/ast/visitors/VariableVisitor.ts +37 -17
  342. package/src/plugins/discovery/MonorepoServiceDiscovery.ts +3 -2
  343. package/src/plugins/discovery/SimpleProjectDiscovery.ts +6 -1
  344. package/src/plugins/discovery/WorkspaceDiscovery.ts +177 -0
  345. package/src/plugins/discovery/resolveSourceEntrypoint.ts +103 -0
  346. package/src/plugins/discovery/workspaces/detector.ts +63 -0
  347. package/src/plugins/discovery/workspaces/globResolver.ts +229 -0
  348. package/src/plugins/discovery/workspaces/index.ts +23 -0
  349. package/src/plugins/discovery/workspaces/parsers.ts +99 -0
  350. package/src/plugins/enrichment/AliasTracker.ts +14 -8
  351. package/src/plugins/enrichment/HTTPConnectionEnricher.ts +14 -7
  352. package/src/plugins/enrichment/ImportExportLinker.ts +24 -6
  353. package/src/plugins/enrichment/MethodCallResolver.ts +18 -12
  354. package/src/plugins/enrichment/MountPointResolver.ts +8 -3
  355. package/src/plugins/enrichment/PrefixEvaluator.ts +16 -7
  356. package/src/plugins/enrichment/RustFFIEnricher.ts +6 -5
  357. package/src/plugins/enrichment/ValueDomainAnalyzer.ts +149 -12
  358. package/src/plugins/indexing/IncrementalModuleIndexer.ts +23 -14
  359. package/src/plugins/indexing/JSModuleIndexer.ts +74 -34
  360. package/src/plugins/indexing/RustModuleIndexer.ts +5 -4
  361. package/src/plugins/validation/CallResolverValidator.ts +8 -7
  362. package/src/plugins/validation/DataFlowValidator.ts +16 -12
  363. package/src/plugins/validation/EvalBanValidator.ts +17 -16
  364. package/src/plugins/validation/GraphConnectivityValidator.ts +19 -23
  365. package/src/plugins/validation/NodeCreationValidator.ts +554 -0
  366. package/src/plugins/validation/SQLInjectionValidator.ts +61 -15
  367. package/src/plugins/validation/ShadowingDetector.ts +6 -5
  368. package/src/plugins/validation/TypeScriptDeadCodeValidator.ts +12 -11
  369. package/src/plugins/vcs/GitPlugin.ts +40 -12
  370. package/src/plugins/vcs/VCSPlugin.ts +7 -5
  371. package/src/storage/backends/RFDBServerBackend.ts +43 -29
  372. package/src/validation/PathValidator.ts +1 -1
  373. package/dist/core/AnalysisWorker.d.ts +0 -14
  374. package/dist/core/AnalysisWorker.d.ts.map +0 -1
  375. package/dist/core/AnalysisWorker.js +0 -307
  376. package/dist/core/ParallelAnalyzer.d.ts +0 -120
  377. package/dist/core/ParallelAnalyzer.d.ts.map +0 -1
  378. package/dist/core/ParallelAnalyzer.js +0 -331
  379. package/dist/core/QueueWorker.d.ts +0 -12
  380. package/dist/core/QueueWorker.d.ts.map +0 -1
  381. package/dist/core/QueueWorker.js +0 -567
  382. package/dist/core/RFDBClient.d.ts +0 -179
  383. package/dist/core/RFDBClient.d.ts.map +0 -1
  384. package/dist/core/RFDBClient.js +0 -429
  385. package/dist/plugins/discovery/ZonServiceDiscovery.d.ts +0 -19
  386. package/dist/plugins/discovery/ZonServiceDiscovery.d.ts.map +0 -1
  387. package/dist/plugins/discovery/ZonServiceDiscovery.js +0 -204
  388. package/src/core/AnalysisWorker.ts +0 -410
  389. package/src/core/ParallelAnalyzer.ts +0 -476
  390. package/src/core/QueueWorker.ts +0 -780
  391. package/src/plugins/indexing/ServiceDetector.ts +0 -230
@@ -8,10 +8,83 @@
8
8
  * - Constructor calls: new Foo(), new Function()
9
9
  */
10
10
 
11
- import type { Node, CallExpression, NewExpression, Identifier, MemberExpression } from '@babel/types';
11
+ import type { Node, CallExpression, NewExpression, Identifier, MemberExpression, ObjectExpression, ArrayExpression, ObjectProperty, SpreadElement } from '@babel/types';
12
12
  import type { NodePath } from '@babel/traverse';
13
13
  import { ASTVisitor, type VisitorModule, type VisitorCollections, type VisitorHandlers, type CounterRef } from './ASTVisitor.js';
14
14
  import { ExpressionEvaluator } from '../ExpressionEvaluator.js';
15
+ import type { ArrayMutationInfo, ArrayMutationArgument, ObjectMutationInfo, ObjectMutationValue } from '../types.js';
16
+ import { ScopeTracker } from '../../../../core/ScopeTracker.js';
17
+ import { computeSemanticId } from '../../../../core/SemanticId.js';
18
+ import { IdGenerator } from '../IdGenerator.js';
19
+ import { NodeFactory } from '../../../../core/NodeFactory.js';
20
+ import { ObjectLiteralNode } from '../../../../core/nodes/ObjectLiteralNode.js';
21
+ import { ArrayLiteralNode } from '../../../../core/nodes/ArrayLiteralNode.js';
22
+ import { getLine, getColumn } from '../utils/location.js';
23
+
24
+ /**
25
+ * Object literal info for OBJECT_LITERAL nodes
26
+ */
27
+ interface ObjectLiteralInfo {
28
+ id: string;
29
+ type: 'OBJECT_LITERAL';
30
+ file: string;
31
+ line: number;
32
+ column: number;
33
+ parentCallId?: string;
34
+ argIndex?: number;
35
+ isSpread?: boolean;
36
+ }
37
+
38
+ /**
39
+ * Object property info for HAS_PROPERTY edges
40
+ */
41
+ interface ObjectPropertyInfo {
42
+ objectId: string;
43
+ propertyName: string;
44
+ valueNodeId?: string;
45
+ valueType: 'LITERAL' | 'VARIABLE' | 'CALL' | 'EXPRESSION' | 'OBJECT_LITERAL' | 'ARRAY_LITERAL' | 'SPREAD';
46
+ valueName?: string;
47
+ literalValue?: unknown;
48
+ file: string;
49
+ line: number;
50
+ column: number;
51
+ callLine?: number;
52
+ callColumn?: number;
53
+ nestedObjectId?: string;
54
+ nestedArrayId?: string;
55
+ }
56
+
57
+ /**
58
+ * Array literal info for ARRAY_LITERAL nodes
59
+ */
60
+ interface ArrayLiteralInfo {
61
+ id: string;
62
+ type: 'ARRAY_LITERAL';
63
+ file: string;
64
+ line: number;
65
+ column: number;
66
+ parentCallId?: string;
67
+ argIndex?: number;
68
+ }
69
+
70
+ /**
71
+ * Array element info for HAS_ELEMENT edges
72
+ */
73
+ interface ArrayElementInfo {
74
+ arrayId: string;
75
+ index: number;
76
+ valueNodeId?: string;
77
+ valueType: 'LITERAL' | 'VARIABLE' | 'CALL' | 'EXPRESSION' | 'OBJECT_LITERAL' | 'ARRAY_LITERAL' | 'SPREAD';
78
+ valueName?: string;
79
+ literalValue?: unknown;
80
+ file: string;
81
+ line: number;
82
+ column: number;
83
+ callLine?: number;
84
+ callColumn?: number;
85
+ nestedObjectId?: string;
86
+ nestedArrayId?: string;
87
+ }
15
88
 
16
89
  /**
17
90
  * Argument info for PASSES_ARGUMENT edges
@@ -112,8 +185,16 @@ interface LiteralInfo {
112
185
  }
113
186
 
114
187
  export class CallExpressionVisitor extends ASTVisitor {
115
- constructor(module: VisitorModule, collections: VisitorCollections) {
188
+ private scopeTracker?: ScopeTracker;
189
+
190
+ /**
191
+ * @param module - Current module being analyzed
192
+ * @param collections - Must contain arrays and counter refs
193
+ * @param scopeTracker - Optional ScopeTracker for semantic ID generation
194
+ */
195
+ constructor(module: VisitorModule, collections: VisitorCollections, scopeTracker?: ScopeTracker) {
116
196
  super(module, collections);
197
+ this.scopeTracker = scopeTracker;
117
198
  }
118
199
 
119
200
  /**
@@ -143,27 +224,130 @@ export class CallExpressionVisitor extends ASTVisitor {
143
224
  actualArg = arg.argument; // Get the actual argument
144
225
  }
145
226
 
146
- // Literal value
147
- const literalValue = ExpressionEvaluator.extractLiteralValue(actualArg);
148
- if (literalValue !== null) {
149
- const literalId = `LITERAL#arg${index}#${module.file}#${argInfo.line}:${argInfo.column}:${literalCounterRef.value++}`;
150
- literals.push({
151
- id: literalId,
152
- type: 'LITERAL',
153
- value: literalValue,
154
- valueType: typeof literalValue,
155
- file: module.file,
156
- line: argInfo.line,
157
- column: argInfo.column,
158
- parentCallId: callId,
159
- argIndex: index
160
- });
161
- argInfo.targetType = 'LITERAL';
162
- argInfo.targetId = literalId;
163
- argInfo.literalValue = literalValue;
227
+ // Object literal - check BEFORE extractLiteralValue to handle object-typed args properly
228
+ if (actualArg.type === 'ObjectExpression') {
229
+ const objectExpr = actualArg as ObjectExpression;
230
+ // Initialize collections if not exist (must assign back to this.collections!)
231
+ if (!this.collections.objectLiteralCounterRef) {
232
+ this.collections.objectLiteralCounterRef = { value: 0 };
233
+ }
234
+ if (!this.collections.objectLiterals) {
235
+ this.collections.objectLiterals = [];
236
+ }
237
+ if (!this.collections.objectProperties) {
238
+ this.collections.objectProperties = [];
239
+ }
240
+ const objectLiteralCounterRef = this.collections.objectLiteralCounterRef as CounterRef;
241
+
242
+ // Use factory to create OBJECT_LITERAL node
243
+ const objectNode = ObjectLiteralNode.create(
244
+ module.file,
245
+ argInfo.line,
246
+ argInfo.column,
247
+ {
248
+ parentCallId: callId,
249
+ argIndex: index,
250
+ counter: objectLiteralCounterRef.value++
251
+ }
252
+ );
253
+ // Factory guarantees line is set, cast to ObjectLiteralInfo
254
+ (this.collections.objectLiterals as ObjectLiteralInfo[]).push(objectNode as unknown as ObjectLiteralInfo);
255
+ const objectId = objectNode.id;
256
+
257
+ // Extract properties
258
+ this.extractObjectProperties(
259
+ objectExpr,
260
+ objectId,
261
+ module,
262
+ this.collections.objectProperties as ObjectPropertyInfo[],
263
+ this.collections.objectLiterals as ObjectLiteralInfo[],
264
+ objectLiteralCounterRef,
265
+ literals as LiteralInfo[],
266
+ literalCounterRef
267
+ );
268
+
269
+ argInfo.targetType = 'OBJECT_LITERAL';
270
+ argInfo.targetId = objectId;
271
+ }
272
+ // Array literal - check BEFORE extractLiteralValue to handle array-typed args properly
273
+ else if (actualArg.type === 'ArrayExpression') {
274
+ const arrayExpr = actualArg as ArrayExpression;
275
+ // Initialize collections if not exist (must assign back to this.collections!)
276
+ if (!this.collections.arrayLiteralCounterRef) {
277
+ this.collections.arrayLiteralCounterRef = { value: 0 };
278
+ }
279
+ if (!this.collections.arrayLiterals) {
280
+ this.collections.arrayLiterals = [];
281
+ }
282
+ if (!this.collections.arrayElements) {
283
+ this.collections.arrayElements = [];
284
+ }
285
+ if (!this.collections.objectLiteralCounterRef) {
286
+ this.collections.objectLiteralCounterRef = { value: 0 };
287
+ }
288
+ if (!this.collections.objectLiterals) {
289
+ this.collections.objectLiterals = [];
290
+ }
291
+ if (!this.collections.objectProperties) {
292
+ this.collections.objectProperties = [];
293
+ }
294
+ const arrayLiteralCounterRef = this.collections.arrayLiteralCounterRef as CounterRef;
295
+
296
+ // Use factory to create ARRAY_LITERAL node
297
+ const arrayNode = ArrayLiteralNode.create(
298
+ module.file,
299
+ argInfo.line,
300
+ argInfo.column,
301
+ {
302
+ parentCallId: callId,
303
+ argIndex: index,
304
+ counter: arrayLiteralCounterRef.value++
305
+ }
306
+ );
307
+ // Factory guarantees line is set, cast to ArrayLiteralInfo
308
+ (this.collections.arrayLiterals as ArrayLiteralInfo[]).push(arrayNode as unknown as ArrayLiteralInfo);
309
+ const arrayId = arrayNode.id;
310
+
311
+ // Extract elements
312
+ this.extractArrayElements(
313
+ arrayExpr,
314
+ arrayId,
315
+ module,
316
+ this.collections.arrayElements as ArrayElementInfo[],
317
+ this.collections.arrayLiterals as ArrayLiteralInfo[],
318
+ arrayLiteralCounterRef,
319
+ this.collections.objectLiterals as ObjectLiteralInfo[],
320
+ this.collections.objectLiteralCounterRef as CounterRef,
321
+ this.collections.objectProperties as ObjectPropertyInfo[],
322
+ literals as LiteralInfo[],
323
+ literalCounterRef
324
+ );
325
+
326
+ argInfo.targetType = 'ARRAY_LITERAL';
327
+ argInfo.targetId = arrayId;
164
328
  }
165
- // Variable reference
166
- else if (actualArg.type === 'Identifier') {
329
+ // Literal value (primitives only - objects/arrays handled above)
330
+ else {
331
+ const literalValue = ExpressionEvaluator.extractLiteralValue(actualArg);
332
+ if (literalValue !== null) {
333
+ const literalId = `LITERAL#arg${index}#${module.file}#${argInfo.line}:${argInfo.column}:${literalCounterRef.value++}`;
334
+ literals.push({
335
+ id: literalId,
336
+ type: 'LITERAL',
337
+ value: literalValue,
338
+ valueType: typeof literalValue,
339
+ file: module.file,
340
+ line: argInfo.line,
341
+ column: argInfo.column,
342
+ parentCallId: callId,
343
+ argIndex: index
344
+ });
345
+ argInfo.targetType = 'LITERAL';
346
+ argInfo.targetId = literalId;
347
+ argInfo.literalValue = literalValue;
348
+ }
349
+ // Variable reference
350
+ else if (actualArg.type === 'Identifier') {
167
351
  argInfo.targetType = 'VARIABLE';
168
352
  argInfo.targetName = (actualArg as Identifier).name; // Will be resolved in GraphBuilder
169
353
  }
@@ -196,25 +380,26 @@ export class CallExpressionVisitor extends ASTVisitor {
196
380
  else if (actualArg.type === 'BinaryExpression' || actualArg.type === 'LogicalExpression') {
197
381
  const expr = actualArg as { operator?: string; type: string };
198
382
  const operator = expr.operator || '?';
199
- const exprName = `<${actualArg.type}:${operator}>`;
200
- const expressionId = `EXPRESSION#${exprName}#${module.file}#${argInfo.line}:${argInfo.column}:${literalCounterRef.value++}`;
201
-
202
- // Create EXPRESSION node
203
- literals.push({
204
- id: expressionId,
205
- type: 'EXPRESSION',
206
- expressionType: actualArg.type,
207
- operator: operator,
208
- name: exprName,
209
- file: module.file,
210
- line: argInfo.line,
211
- column: argInfo.column,
212
- parentCallId: callId,
213
- argIndex: index
214
- });
383
+ const counter = literalCounterRef.value++;
384
+
385
+ // Create EXPRESSION node via NodeFactory
386
+ const expressionNode = NodeFactory.createArgumentExpression(
387
+ actualArg.type,
388
+ module.file,
389
+ argInfo.line,
390
+ argInfo.column,
391
+ {
392
+ parentCallId: callId,
393
+ argIndex: index,
394
+ operator,
395
+ counter
396
+ }
397
+ );
398
+
399
+ literals.push(expressionNode as LiteralInfo);
215
400
 
216
401
  argInfo.targetType = 'EXPRESSION';
217
- argInfo.targetId = expressionId;
402
+ argInfo.targetId = expressionNode.id;
218
403
  argInfo.expressionType = actualArg.type;
219
404
 
220
405
  // Track DERIVES_FROM edges for identifiers in expression
@@ -223,7 +408,7 @@ export class CallExpressionVisitor extends ASTVisitor {
223
408
  if (variableAssignments) {
224
409
  for (const identName of identifiers) {
225
410
  variableAssignments.push({
226
- variableId: expressionId,
411
+ variableId: expressionNode.id,
227
412
  sourceId: null,
228
413
  sourceName: identName,
229
414
  sourceType: 'DERIVES_FROM_VARIABLE',
@@ -232,19 +417,12 @@ export class CallExpressionVisitor extends ASTVisitor {
232
417
  }
233
418
  }
234
419
  }
235
- // Object literal
236
- else if (actualArg.type === 'ObjectExpression') {
237
- argInfo.targetType = 'OBJECT_LITERAL';
238
- }
239
- // Array literal
240
- else if (actualArg.type === 'ArrayExpression') {
241
- argInfo.targetType = 'ARRAY_LITERAL';
242
- }
243
- // Other expression types
420
+ // Other expression types (fallback for unhandled expression types)
244
421
  else {
245
422
  argInfo.targetType = 'EXPRESSION';
246
423
  argInfo.expressionType = actualArg.type;
247
424
  }
425
+ }
248
426
 
249
427
  callArguments.push(argInfo);
250
428
  });
@@ -298,34 +476,572 @@ export class CallExpressionVisitor extends ASTVisitor {
298
476
  }
299
477
 
300
478
  /**
301
- * Get a stable scope ID for a function parent
302
- * Format must match what FunctionVisitor creates:
303
- * - FunctionDeclaration: FUNCTION#name#file#line
304
- * - ArrowFunctionExpression: FUNCTION#name#file#line:col:counter
479
+ * Extract object properties and create ObjectPropertyInfo records
480
+ */
481
+ extractObjectProperties(
482
+ objectExpr: ObjectExpression,
483
+ objectId: string,
484
+ module: VisitorModule,
485
+ objectProperties: ObjectPropertyInfo[],
486
+ objectLiterals: ObjectLiteralInfo[],
487
+ objectLiteralCounterRef: CounterRef,
488
+ literals: LiteralInfo[],
489
+ literalCounterRef: CounterRef
490
+ ): void {
491
+ for (const prop of objectExpr.properties) {
492
+ const propLine = prop.loc?.start.line || 0;
493
+ const propColumn = prop.loc?.start.column || 0;
494
+
495
+ // Handle spread properties: { ...other }
496
+ if (prop.type === 'SpreadElement') {
497
+ const spreadArg = prop.argument;
498
+ const propertyInfo: ObjectPropertyInfo = {
499
+ objectId,
500
+ propertyName: '<spread>',
501
+ valueType: 'SPREAD',
502
+ file: module.file,
503
+ line: propLine,
504
+ column: propColumn
505
+ };
506
+
507
+ if (spreadArg.type === 'Identifier') {
508
+ propertyInfo.valueName = spreadArg.name;
509
+ propertyInfo.valueType = 'VARIABLE';
510
+ }
511
+
512
+ objectProperties.push(propertyInfo);
513
+ continue;
514
+ }
515
+
516
+ // Handle regular properties
517
+ if (prop.type === 'ObjectProperty') {
518
+ const objProp = prop as ObjectProperty;
519
+ let propertyName: string;
520
+
521
+ // Get property name
522
+ if (objProp.key.type === 'Identifier') {
523
+ propertyName = objProp.key.name;
524
+ } else if (objProp.key.type === 'StringLiteral') {
525
+ propertyName = objProp.key.value;
526
+ } else if (objProp.key.type === 'NumericLiteral') {
527
+ propertyName = String(objProp.key.value);
528
+ } else {
529
+ propertyName = '<computed>';
530
+ }
531
+
532
+ const propertyInfo: ObjectPropertyInfo = {
533
+ objectId,
534
+ propertyName,
535
+ file: module.file,
536
+ line: propLine,
537
+ column: propColumn,
538
+ valueType: 'EXPRESSION'
539
+ };
540
+
541
+ const value = objProp.value;
542
+
543
+ // Nested object literal - check BEFORE extractLiteralValue
544
+ if (value.type === 'ObjectExpression') {
545
+ // Use factory - do NOT pass argIndex for nested literals (uses 'obj' suffix)
546
+ const nestedObjectNode = ObjectLiteralNode.create(
547
+ module.file,
548
+ value.loc?.start.line || 0,
549
+ value.loc?.start.column || 0,
550
+ {
551
+ counter: objectLiteralCounterRef.value++
552
+ }
553
+ );
554
+ objectLiterals.push(nestedObjectNode as unknown as ObjectLiteralInfo);
555
+ const nestedObjectId = nestedObjectNode.id;
556
+
557
+ // Recursively extract nested properties
558
+ this.extractObjectProperties(
559
+ value,
560
+ nestedObjectId,
561
+ module,
562
+ objectProperties,
563
+ objectLiterals,
564
+ objectLiteralCounterRef,
565
+ literals,
566
+ literalCounterRef
567
+ );
568
+
569
+ propertyInfo.valueType = 'OBJECT_LITERAL';
570
+ propertyInfo.nestedObjectId = nestedObjectId;
571
+ propertyInfo.valueNodeId = nestedObjectId;
572
+ }
573
+ // Nested array literal - check BEFORE extractLiteralValue
574
+ else if (value.type === 'ArrayExpression') {
575
+ const arrayLiteralCounterRef = (this.collections.arrayLiteralCounterRef ?? { value: 0 }) as CounterRef;
576
+ const arrayLiterals = this.collections.arrayLiterals ?? [];
577
+ const arrayElements = this.collections.arrayElements ?? [];
578
+
579
+ // Use factory - do NOT pass argIndex for nested literals (uses 'arr' suffix)
580
+ const nestedArrayNode = ArrayLiteralNode.create(
581
+ module.file,
582
+ value.loc?.start.line || 0,
583
+ value.loc?.start.column || 0,
584
+ {
585
+ counter: arrayLiteralCounterRef.value++
586
+ }
587
+ );
588
+ (arrayLiterals as ArrayLiteralInfo[]).push(nestedArrayNode as unknown as ArrayLiteralInfo);
589
+ const nestedArrayId = nestedArrayNode.id;
590
+
591
+ // Recursively extract array elements
592
+ this.extractArrayElements(
593
+ value,
594
+ nestedArrayId,
595
+ module,
596
+ arrayElements as ArrayElementInfo[],
597
+ arrayLiterals as ArrayLiteralInfo[],
598
+ arrayLiteralCounterRef,
599
+ objectLiterals,
600
+ objectLiteralCounterRef,
601
+ objectProperties,
602
+ literals,
603
+ literalCounterRef
604
+ );
605
+
606
+ propertyInfo.valueType = 'ARRAY_LITERAL';
607
+ propertyInfo.nestedArrayId = nestedArrayId;
608
+ propertyInfo.valueNodeId = nestedArrayId;
609
+ }
610
+ // Literal value (primitives only - objects/arrays handled above)
611
+ else {
612
+ const literalValue = ExpressionEvaluator.extractLiteralValue(value);
613
+ if (literalValue !== null) {
614
+ const literalId = `LITERAL#${propertyName}#${module.file}#${propLine}:${propColumn}:${literalCounterRef.value++}`;
615
+ literals.push({
616
+ id: literalId,
617
+ type: 'LITERAL',
618
+ value: literalValue,
619
+ valueType: typeof literalValue,
620
+ file: module.file,
621
+ line: propLine,
622
+ column: propColumn,
623
+ parentCallId: objectId,
624
+ argIndex: 0
625
+ });
626
+ propertyInfo.valueType = 'LITERAL';
627
+ propertyInfo.valueNodeId = literalId;
628
+ propertyInfo.literalValue = literalValue;
629
+ }
630
+ // Variable reference
631
+ else if (value.type === 'Identifier') {
632
+ propertyInfo.valueType = 'VARIABLE';
633
+ propertyInfo.valueName = value.name;
634
+ }
635
+ // Call expression
636
+ else if (value.type === 'CallExpression') {
637
+ propertyInfo.valueType = 'CALL';
638
+ propertyInfo.callLine = value.loc?.start.line;
639
+ propertyInfo.callColumn = value.loc?.start.column;
640
+ }
641
+ // Other expressions
642
+ else {
643
+ propertyInfo.valueType = 'EXPRESSION';
644
+ }
645
+ }
646
+
647
+ objectProperties.push(propertyInfo);
648
+ }
649
+ // Handle object methods: { foo() {} }
650
+ else if (prop.type === 'ObjectMethod') {
651
+ const propertyName = prop.key.type === 'Identifier' ? prop.key.name : '<computed>';
652
+ objectProperties.push({
653
+ objectId,
654
+ propertyName,
655
+ valueType: 'EXPRESSION',
656
+ file: module.file,
657
+ line: propLine,
658
+ column: propColumn
659
+ });
660
+ }
661
+ }
662
+ }
663
+
664
+ /**
665
+ * Extract array elements and create ArrayElementInfo records
666
+ */
667
+ extractArrayElements(
668
+ arrayExpr: ArrayExpression,
669
+ arrayId: string,
670
+ module: VisitorModule,
671
+ arrayElements: ArrayElementInfo[],
672
+ arrayLiterals: ArrayLiteralInfo[],
673
+ arrayLiteralCounterRef: CounterRef,
674
+ objectLiterals: ObjectLiteralInfo[],
675
+ objectLiteralCounterRef: CounterRef,
676
+ objectProperties: ObjectPropertyInfo[],
677
+ literals: LiteralInfo[],
678
+ literalCounterRef: CounterRef
679
+ ): void {
680
+ arrayExpr.elements.forEach((element, index) => {
681
+ if (!element) return; // Skip holes in arrays
682
+
683
+ const elemLine = element.loc?.start.line || 0;
684
+ const elemColumn = element.loc?.start.column || 0;
685
+
686
+ const elementInfo: ArrayElementInfo = {
687
+ arrayId,
688
+ index,
689
+ file: module.file,
690
+ line: elemLine,
691
+ column: elemColumn,
692
+ valueType: 'EXPRESSION'
693
+ };
694
+
695
+ // Handle spread elements: [...arr]
696
+ if (element.type === 'SpreadElement') {
697
+ const spreadArg = element.argument;
698
+ elementInfo.valueType = 'SPREAD';
699
+ if (spreadArg.type === 'Identifier') {
700
+ elementInfo.valueName = spreadArg.name;
701
+ }
702
+ arrayElements.push(elementInfo);
703
+ return;
704
+ }
705
+
706
+ // Nested object literal - check BEFORE extractLiteralValue
707
+ if (element.type === 'ObjectExpression') {
708
+ // Use factory - do NOT pass argIndex for nested literals (uses 'obj' suffix)
709
+ const nestedObjectNode = ObjectLiteralNode.create(
710
+ module.file,
711
+ elemLine,
712
+ elemColumn,
713
+ {
714
+ counter: objectLiteralCounterRef.value++
715
+ }
716
+ );
717
+ objectLiterals.push(nestedObjectNode as unknown as ObjectLiteralInfo);
718
+ const nestedObjectId = nestedObjectNode.id;
719
+
720
+ // Recursively extract properties
721
+ this.extractObjectProperties(
722
+ element,
723
+ nestedObjectId,
724
+ module,
725
+ objectProperties,
726
+ objectLiterals,
727
+ objectLiteralCounterRef,
728
+ literals,
729
+ literalCounterRef
730
+ );
731
+
732
+ elementInfo.valueType = 'OBJECT_LITERAL';
733
+ elementInfo.nestedObjectId = nestedObjectId;
734
+ elementInfo.valueNodeId = nestedObjectId;
735
+ }
736
+ // Nested array literal - check BEFORE extractLiteralValue
737
+ else if (element.type === 'ArrayExpression') {
738
+ // Use factory - do NOT pass argIndex for nested literals (uses 'arr' suffix)
739
+ const nestedArrayNode = ArrayLiteralNode.create(
740
+ module.file,
741
+ elemLine,
742
+ elemColumn,
743
+ {
744
+ counter: arrayLiteralCounterRef.value++
745
+ }
746
+ );
747
+ arrayLiterals.push(nestedArrayNode as unknown as ArrayLiteralInfo);
748
+ const nestedArrayId = nestedArrayNode.id;
749
+
750
+ // Recursively extract elements
751
+ this.extractArrayElements(
752
+ element,
753
+ nestedArrayId,
754
+ module,
755
+ arrayElements,
756
+ arrayLiterals,
757
+ arrayLiteralCounterRef,
758
+ objectLiterals,
759
+ objectLiteralCounterRef,
760
+ objectProperties,
761
+ literals,
762
+ literalCounterRef
763
+ );
764
+
765
+ elementInfo.valueType = 'ARRAY_LITERAL';
766
+ elementInfo.nestedArrayId = nestedArrayId;
767
+ elementInfo.valueNodeId = nestedArrayId;
768
+ }
769
+ // Literal value (primitives only - objects/arrays handled above)
770
+ else {
771
+ const literalValue = ExpressionEvaluator.extractLiteralValue(element);
772
+ if (literalValue !== null) {
773
+ const literalId = `LITERAL#elem${index}#${module.file}#${elemLine}:${elemColumn}:${literalCounterRef.value++}`;
774
+ literals.push({
775
+ id: literalId,
776
+ type: 'LITERAL',
777
+ value: literalValue,
778
+ valueType: typeof literalValue,
779
+ file: module.file,
780
+ line: elemLine,
781
+ column: elemColumn,
782
+ parentCallId: arrayId,
783
+ argIndex: index
784
+ });
785
+ elementInfo.valueType = 'LITERAL';
786
+ elementInfo.valueNodeId = literalId;
787
+ elementInfo.literalValue = literalValue;
788
+ }
789
+ // Variable reference
790
+ else if (element.type === 'Identifier') {
791
+ elementInfo.valueType = 'VARIABLE';
792
+ elementInfo.valueName = element.name;
793
+ }
794
+ // Call expression
795
+ else if (element.type === 'CallExpression') {
796
+ elementInfo.valueType = 'CALL';
797
+ elementInfo.callLine = element.loc?.start.line;
798
+ elementInfo.callColumn = element.loc?.start.column;
799
+ }
800
+ // Other expressions
801
+ else {
802
+ elementInfo.valueType = 'EXPRESSION';
803
+ }
804
+ }
805
+
806
+ arrayElements.push(elementInfo);
807
+ });
808
+ }
809
+
810
+ /**
811
+ * Detect array mutation calls (push, unshift, splice) and collect mutation info
812
+ * for later FLOWS_INTO edge creation in GraphBuilder
813
+ *
814
+ * REG-117: Added isNested, baseObjectName, propertyName for nested mutations
815
+ */
816
+ private detectArrayMutation(
817
+ callNode: CallExpression,
818
+ arrayName: string,
819
+ method: 'push' | 'unshift' | 'splice',
820
+ module: VisitorModule,
821
+ isNested?: boolean,
822
+ baseObjectName?: string,
823
+ propertyName?: string
824
+ ): void {
825
+ // Initialize collection if not exists
826
+ if (!this.collections.arrayMutations) {
827
+ this.collections.arrayMutations = [];
828
+ }
829
+ const arrayMutations = this.collections.arrayMutations as ArrayMutationInfo[];
830
+
831
+ const mutationArgs: ArrayMutationArgument[] = [];
832
+
833
+ // For splice, only arguments from index 2 onwards are insertions
834
+ // splice(start, deleteCount, item1, item2, ...)
835
+ callNode.arguments.forEach((arg, index) => {
836
+ // Skip start and deleteCount for splice
837
+ if (method === 'splice' && index < 2) return;
838
+
839
+ const argInfo: ArrayMutationArgument = {
840
+ argIndex: method === 'splice' ? index - 2 : index,
841
+ isSpread: arg.type === 'SpreadElement',
842
+ valueType: 'EXPRESSION' // Default
843
+ };
844
+
845
+ let actualArg = arg;
846
+ if (arg.type === 'SpreadElement') {
847
+ actualArg = arg.argument;
848
+ }
849
+
850
+ // Determine value type
851
+ const literalValue = ExpressionEvaluator.extractLiteralValue(actualArg);
852
+ if (literalValue !== null) {
853
+ argInfo.valueType = 'LITERAL';
854
+ argInfo.literalValue = literalValue;
855
+ } else if (actualArg.type === 'Identifier') {
856
+ argInfo.valueType = 'VARIABLE';
857
+ argInfo.valueName = actualArg.name;
858
+ } else if (actualArg.type === 'ObjectExpression') {
859
+ argInfo.valueType = 'OBJECT_LITERAL';
860
+ } else if (actualArg.type === 'ArrayExpression') {
861
+ argInfo.valueType = 'ARRAY_LITERAL';
862
+ } else if (actualArg.type === 'CallExpression') {
863
+ argInfo.valueType = 'CALL';
864
+ argInfo.callLine = actualArg.loc?.start.line;
865
+ argInfo.callColumn = actualArg.loc?.start.column;
866
+ }
867
+
868
+ mutationArgs.push(argInfo);
869
+ });
870
+
871
+ // Only record if there are actual insertions
872
+ if (mutationArgs.length > 0) {
873
+ const line = callNode.loc?.start.line ?? 0;
874
+ const column = callNode.loc?.start.column ?? 0;
875
+
876
+ // Generate semantic ID for array mutation if scopeTracker available
877
+ const scopeTracker = this.scopeTracker;
878
+ let mutationId: string | undefined;
879
+ if (scopeTracker) {
880
+ const discriminator = scopeTracker.getItemCounter(`ARRAY_MUTATION:${arrayName}.${method}`);
881
+ mutationId = computeSemanticId('ARRAY_MUTATION', `${arrayName}.${method}`, scopeTracker.getContext(), { discriminator });
882
+ }
883
+
884
+ arrayMutations.push({
885
+ id: mutationId,
886
+ arrayName,
887
+ mutationMethod: method,
888
+ file: module.file,
889
+ line,
890
+ column,
891
+ insertedValues: mutationArgs,
892
+ // REG-117: Nested mutation fields
893
+ isNested,
894
+ baseObjectName,
895
+ propertyName
896
+ });
897
+ }
898
+ }
899
+
900
+ /**
901
+ * Detect Object.assign(target, source1, source2, ...) calls
902
+ * Creates ObjectMutationInfo for FLOWS_INTO edge generation in GraphBuilder
903
+ *
904
+ * @param callNode - The call expression node
905
+ * @param module - Current module being analyzed
906
+ */
907
+ private detectObjectAssign(
908
+ callNode: CallExpression,
909
+ module: VisitorModule
910
+ ): void {
911
+ // Need at least 2 arguments: target and at least one source
912
+ if (callNode.arguments.length < 2) return;
913
+
914
+ // Initialize object mutations collection if not exists
915
+ if (!this.collections.objectMutations) {
916
+ this.collections.objectMutations = [];
917
+ }
918
+ const objectMutations = this.collections.objectMutations as ObjectMutationInfo[];
919
+
920
+ // First argument is target
921
+ const targetArg = callNode.arguments[0];
922
+ let targetName: string;
923
+
924
+ if (targetArg.type === 'Identifier') {
925
+ targetName = targetArg.name;
926
+ } else if (targetArg.type === 'ObjectExpression') {
927
+ targetName = '<anonymous>';
928
+ } else {
929
+ return;
930
+ }
931
+
932
+ const line = callNode.loc?.start.line ?? 0;
933
+ const column = callNode.loc?.start.column ?? 0;
934
+
935
+ for (let i = 1; i < callNode.arguments.length; i++) {
936
+ let arg = callNode.arguments[i];
937
+ let isSpread = false;
938
+
939
+ if (arg.type === 'SpreadElement') {
940
+ isSpread = true;
941
+ arg = arg.argument;
942
+ }
943
+
944
+ const valueInfo: ObjectMutationValue = {
945
+ valueType: 'EXPRESSION',
946
+ argIndex: i - 1,
947
+ isSpread
948
+ };
949
+
950
+ const literalValue = ExpressionEvaluator.extractLiteralValue(arg);
951
+ if (literalValue !== null) {
952
+ valueInfo.valueType = 'LITERAL';
953
+ valueInfo.literalValue = literalValue;
954
+ } else if (arg.type === 'Identifier') {
955
+ valueInfo.valueType = 'VARIABLE';
956
+ valueInfo.valueName = arg.name;
957
+ } else if (arg.type === 'ObjectExpression') {
958
+ valueInfo.valueType = 'OBJECT_LITERAL';
959
+ } else if (arg.type === 'ArrayExpression') {
960
+ valueInfo.valueType = 'ARRAY_LITERAL';
961
+ } else if (arg.type === 'CallExpression') {
962
+ valueInfo.valueType = 'CALL';
963
+ valueInfo.callLine = arg.loc?.start.line;
964
+ valueInfo.callColumn = arg.loc?.start.column;
965
+ }
966
+
967
+ let mutationId: string | undefined;
968
+ if (this.scopeTracker) {
969
+ const discriminator = this.scopeTracker.getItemCounter(`OBJECT_MUTATION:Object.assign:${targetName}`);
970
+ mutationId = computeSemanticId('OBJECT_MUTATION', `Object.assign:${targetName}`, this.scopeTracker.getContext(), { discriminator });
971
+ }
972
+
973
+ objectMutations.push({
974
+ id: mutationId,
975
+ objectName: targetName,
976
+ propertyName: '<assign>',
977
+ mutationType: 'assign',
978
+ file: module.file,
979
+ line,
980
+ column,
981
+ value: valueInfo
982
+ });
983
+ }
984
+ }
985
+
986
+ /**
987
+ * Get a stable scope ID for a function parent.
988
+ *
989
+ * Format must match what FunctionVisitor/ClassVisitor creates (semantic ID):
990
+ * - Module-level function: {file}->global->FUNCTION->{name}
991
+ * - Class method: {file}->{className}->FUNCTION->{methodName}
305
992
  *
306
- * NOTE: We don't have access to the counter here, so for arrow functions
307
- * we try to match by name+file+line:col. This may not always work for
308
- * multiple arrow functions on the same line.
993
+ * Reconstructs scope path by walking up the AST.
309
994
  */
310
995
  getFunctionScopeId(functionParent: NodePath, module: VisitorModule): string {
311
996
  const funcNode = functionParent.node as Node & {
312
997
  id?: { name: string } | null;
998
+ key?: { name?: string; type: string };
313
999
  loc?: { start: { line: number; column: number } };
314
1000
  type: string;
315
1001
  };
316
- const line = funcNode.loc?.start.line || 0;
317
- const col = funcNode.loc?.start.column || 0;
318
1002
 
319
- // FunctionDeclaration with name
1003
+ // Get function name
1004
+ let funcName: string | undefined;
320
1005
  if (funcNode.type === 'FunctionDeclaration' && funcNode.id?.name) {
321
- return `FUNCTION#${funcNode.id.name}#${module.file}#${line}`;
1006
+ funcName = funcNode.id.name;
1007
+ } else if (funcNode.type === 'ClassMethod' && funcNode.key?.type === 'Identifier') {
1008
+ funcName = funcNode.key.name;
1009
+ }
1010
+
1011
+ if (!funcName) {
1012
+ // Anonymous function - fall back to module scope
1013
+ return module.id;
1014
+ }
1015
+
1016
+ // Build scope path by walking up the AST
1017
+ const scopePath: string[] = [];
1018
+ let current: NodePath | null = functionParent.parentPath;
1019
+
1020
+ while (current) {
1021
+ const node = current.node as Node & {
1022
+ id?: { name: string } | null;
1023
+ type: string;
1024
+ };
1025
+
1026
+ if (node.type === 'ClassDeclaration' && node.id?.name) {
1027
+ scopePath.unshift(node.id.name);
1028
+ break; // Class is the outermost scope we need
1029
+ } else if (node.type === 'ClassBody') {
1030
+ // Continue up to ClassDeclaration
1031
+ } else if (node.type === 'Program') {
1032
+ break;
1033
+ }
1034
+
1035
+ current = current.parentPath;
1036
+ }
1037
+
1038
+ // If no class found, it's at module level (global scope)
1039
+ if (scopePath.length === 0) {
1040
+ scopePath.push('global');
322
1041
  }
323
1042
 
324
- // For arrow functions and other cases, we can't perfectly match the ID
325
- // because FunctionVisitor uses a counter. For now, use module.id as fallback
326
- // to avoid creating invalid edges. The CALL node will be connected to MODULE
327
- // instead of the specific function.
328
- return module.id;
1043
+ // Compute semantic ID: {file}->{scopePath}->FUNCTION->{name}
1044
+ return `${module.file}->${scopePath.join('->')}->FUNCTION->${funcName}`;
329
1045
  }
330
1046
 
331
1047
  getHandlers(): VisitorHandlers {
@@ -339,26 +1055,43 @@ export class CallExpressionVisitor extends ASTVisitor {
339
1055
  const callSiteCounterRef = (this.collections.callSiteCounterRef ?? { value: 0 }) as CounterRef;
340
1056
  const literalCounterRef = (this.collections.literalCounterRef ?? { value: 0 }) as CounterRef;
341
1057
  const processedNodes = this.collections.processedNodes ?? { callSites: new Set(), methodCalls: new Set(), eventListeners: new Set() };
1058
+ const scopeTracker = this.scopeTracker;
342
1059
 
343
1060
  return {
344
1061
  CallExpression: (path: NodePath) => {
345
1062
  const callNode = path.node as CallExpression;
346
1063
  const functionParent = path.getFunctionParent();
1064
+
347
1065
  // Determine parent scope - if inside a function, use function's scope, otherwise module
348
1066
  const parentScopeId = functionParent ? this.getFunctionScopeId(functionParent, module) : module.id;
349
1067
 
350
1068
  // Identifier calls (direct function calls)
1069
+ // Skip if inside function - they will be processed by analyzeFunctionBody with proper scope tracking
351
1070
  if (callNode.callee.type === 'Identifier') {
1071
+ if (functionParent) {
1072
+ return;
1073
+ }
352
1074
  const callee = callNode.callee as Identifier;
353
- const callId = `CALL#${callee.name}#${module.file}#${callNode.loc!.start.line}:${callNode.loc!.start.column}:${callSiteCounterRef.value++}`;
1075
+
1076
+ const line = getLine(callNode);
1077
+ const column = getColumn(callNode);
1078
+
1079
+ // Generate ID using centralized IdGenerator
1080
+ const idGenerator = new IdGenerator(scopeTracker);
1081
+ const callId = idGenerator.generate(
1082
+ 'CALL', callee.name, module.file,
1083
+ line, column,
1084
+ callSiteCounterRef,
1085
+ { useDiscriminator: true, discriminatorKey: `CALL:${callee.name}` }
1086
+ );
354
1087
 
355
1088
  (callSites as CallSiteInfo[]).push({
356
1089
  id: callId,
357
1090
  type: 'CALL',
358
1091
  name: callee.name,
359
1092
  file: module.file,
360
- line: callNode.loc!.start.line,
361
- column: callNode.loc!.start.column,
1093
+ line,
1094
+ column,
362
1095
  parentScopeId,
363
1096
  targetFunctionName: callee.name
364
1097
  });
@@ -375,8 +1108,12 @@ export class CallExpressionVisitor extends ASTVisitor {
375
1108
  );
376
1109
  }
377
1110
  }
378
- // MemberExpression calls (method calls at module level)
1111
+ // MemberExpression calls (method calls)
1112
+ // Skip if inside function - they will be processed by analyzeFunctionBody with proper scope tracking
379
1113
  else if (callNode.callee.type === 'MemberExpression') {
1114
+ if (functionParent) {
1115
+ return;
1116
+ }
380
1117
  const memberCallee = callNode.callee as MemberExpression;
381
1118
  const object = memberCallee.object;
382
1119
  const property = memberCallee.property;
@@ -403,13 +1140,16 @@ export class CallExpressionVisitor extends ASTVisitor {
403
1140
  }
404
1141
  processedNodes.eventListeners.add(nodeKey);
405
1142
 
1143
+ const eventLine = getLine(callNode);
1144
+ const eventColumn = getColumn(callNode);
1145
+
406
1146
  (eventListeners as EventListenerInfo[]).push({
407
- id: `event:listener#${eventName}#${module.file}#${callNode.loc!.start.line}:${callNode.loc!.start.column}:${callSiteCounterRef.value++}`,
1147
+ id: `event:listener#${eventName}#${module.file}#${eventLine}:${eventColumn}:${callSiteCounterRef.value++}`,
408
1148
  type: 'event:listener',
409
1149
  name: eventName,
410
1150
  object: objectName,
411
1151
  file: module.file,
412
- line: callNode.loc!.start.line,
1152
+ line: eventLine,
413
1153
  parentScopeId,
414
1154
  callbackArg: secondArg
415
1155
  });
@@ -423,7 +1163,17 @@ export class CallExpressionVisitor extends ASTVisitor {
423
1163
  processedNodes.methodCalls.add(nodeKey);
424
1164
 
425
1165
  const fullName = `${objectName}.${methodName}`;
426
- const methodCallId = `CALL#${fullName}#${module.file}#${callNode.loc!.start.line}:${callNode.loc!.start.column}:${callSiteCounterRef.value++}`;
1166
+ const methodLine = getLine(callNode);
1167
+ const methodColumn = getColumn(callNode);
1168
+
1169
+ // Generate ID using centralized IdGenerator
1170
+ const idGenerator = new IdGenerator(scopeTracker);
1171
+ const methodCallId = idGenerator.generate(
1172
+ 'CALL', fullName, module.file,
1173
+ methodLine, methodColumn,
1174
+ callSiteCounterRef,
1175
+ { useDiscriminator: true, discriminatorKey: `CALL:${fullName}` }
1176
+ );
427
1177
 
428
1178
  (methodCalls as MethodCallInfo[]).push({
429
1179
  id: methodCallId,
@@ -434,11 +1184,27 @@ export class CallExpressionVisitor extends ASTVisitor {
434
1184
  computed: isComputed,
435
1185
  computedPropertyVar, // Variable name used in obj[x]() calls
436
1186
  file: module.file,
437
- line: callNode.loc!.start.line,
438
- column: callNode.loc!.start.column,
1187
+ line: methodLine,
1188
+ column: methodColumn,
439
1189
  parentScopeId
440
1190
  });
441
1191
 
1192
+ // Check for array mutation methods (push, unshift, splice)
1193
+ const ARRAY_MUTATION_METHODS = ['push', 'unshift', 'splice'];
1194
+ if (ARRAY_MUTATION_METHODS.includes(methodName)) {
1195
+ this.detectArrayMutation(
1196
+ callNode,
1197
+ objectName,
1198
+ methodName as 'push' | 'unshift' | 'splice',
1199
+ module
1200
+ );
1201
+ }
1202
+
1203
+ // Check for Object.assign() calls
1204
+ if (objectName === 'Object' && methodName === 'assign') {
1205
+ this.detectObjectAssign(callNode, module);
1206
+ }
1207
+
442
1208
  // Extract arguments for PASSES_ARGUMENT edges
443
1209
  if (callNode.arguments.length > 0) {
444
1210
  this.extractArguments(
@@ -455,8 +1221,8 @@ export class CallExpressionVisitor extends ASTVisitor {
455
1221
  if (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression') {
456
1222
  (methodCallbacks as MethodCallbackInfo[]).push({
457
1223
  methodCallId,
458
- callbackLine: arg.loc!.start.line,
459
- callbackColumn: arg.loc!.start.column,
1224
+ callbackLine: getLine(arg),
1225
+ callbackColumn: getColumn(arg),
460
1226
  callbackType: arg.type
461
1227
  });
462
1228
  }
@@ -464,14 +1230,52 @@ export class CallExpressionVisitor extends ASTVisitor {
464
1230
  }
465
1231
  }
466
1232
  }
1233
+ // REG-117: Nested array mutations like obj.arr.push(item)
1234
+ // object is MemberExpression, property is the method name
1235
+ else if (object.type === 'MemberExpression' && property.type === 'Identifier') {
1236
+ const nestedMember = object as MemberExpression;
1237
+ const methodName = (property as Identifier).name;
1238
+ const ARRAY_MUTATION_METHODS = ['push', 'unshift', 'splice'];
1239
+
1240
+ if (ARRAY_MUTATION_METHODS.includes(methodName)) {
1241
+ // Extract base object and property from nested MemberExpression
1242
+ const base = nestedMember.object;
1243
+ const prop = nestedMember.property;
1244
+
1245
+ // Only handle single-level nesting: obj.arr.push() or this.items.push()
1246
+ if ((base.type === 'Identifier' || base.type === 'ThisExpression') &&
1247
+ !nestedMember.computed &&
1248
+ prop.type === 'Identifier') {
1249
+ const baseObjectName = base.type === 'Identifier' ? (base as Identifier).name : 'this';
1250
+ const propertyName = (prop as Identifier).name;
1251
+
1252
+ this.detectArrayMutation(
1253
+ callNode,
1254
+ `${baseObjectName}.${propertyName}`, // arrayName for ID purposes
1255
+ methodName as 'push' | 'unshift' | 'splice',
1256
+ module,
1257
+ true, // isNested
1258
+ baseObjectName,
1259
+ propertyName
1260
+ );
1261
+ }
1262
+ }
1263
+ }
467
1264
  }
468
1265
  },
469
1266
 
470
1267
  // NewExpression: new Foo(), new Function(), new Map(), etc.
1268
+ // Skip if inside function - they will be processed by analyzeFunctionBody with proper scope tracking
471
1269
  NewExpression: (path: NodePath) => {
472
1270
  const newNode = path.node as NewExpression;
473
1271
  const functionParent = path.getFunctionParent();
474
- const parentScopeId = functionParent ? this.getFunctionScopeId(functionParent, module) : module.id;
1272
+
1273
+ // Skip if inside function - handled by analyzeFunctionBody
1274
+ if (functionParent) {
1275
+ return;
1276
+ }
1277
+
1278
+ const parentScopeId = module.id;
475
1279
 
476
1280
  // Dedup check
477
1281
  const nodeKey = `new:${newNode.start}:${newNode.end}`;
@@ -484,14 +1288,25 @@ export class CallExpressionVisitor extends ASTVisitor {
484
1288
  if (newNode.callee.type === 'Identifier') {
485
1289
  const callee = newNode.callee as Identifier;
486
1290
  const constructorName = callee.name;
1291
+ const newLine = getLine(newNode);
1292
+ const newColumn = getColumn(newNode);
1293
+
1294
+ // Generate ID using centralized IdGenerator
1295
+ const idGenerator = new IdGenerator(scopeTracker);
1296
+ const newCallId = idGenerator.generate(
1297
+ 'CALL', `new:${constructorName}`, module.file,
1298
+ newLine, newColumn,
1299
+ callSiteCounterRef,
1300
+ { useDiscriminator: true, discriminatorKey: `CALL:new:${constructorName}` }
1301
+ );
487
1302
 
488
1303
  (callSites as CallSiteInfo[]).push({
489
- id: `CALL#new:${constructorName}#${module.file}#${newNode.loc!.start.line}:${newNode.loc!.start.column}:${callSiteCounterRef.value++}`,
1304
+ id: newCallId,
490
1305
  type: 'CALL',
491
1306
  name: constructorName,
492
1307
  file: module.file,
493
- line: newNode.loc!.start.line,
494
- column: newNode.loc!.start.column,
1308
+ line: newLine,
1309
+ column: newColumn,
495
1310
  parentScopeId,
496
1311
  targetFunctionName: constructorName,
497
1312
  isNew: true // Mark as constructor call
@@ -507,16 +1322,27 @@ export class CallExpressionVisitor extends ASTVisitor {
507
1322
  const objectName = (object as Identifier).name;
508
1323
  const constructorName = (property as Identifier).name;
509
1324
  const fullName = `${objectName}.${constructorName}`;
1325
+ const memberNewLine = getLine(newNode);
1326
+ const memberNewColumn = getColumn(newNode);
1327
+
1328
+ // Generate ID using centralized IdGenerator
1329
+ const idGenerator = new IdGenerator(scopeTracker);
1330
+ const newMethodCallId = idGenerator.generate(
1331
+ 'CALL', `new:${fullName}`, module.file,
1332
+ memberNewLine, memberNewColumn,
1333
+ callSiteCounterRef,
1334
+ { useDiscriminator: true, discriminatorKey: `CALL:new:${fullName}` }
1335
+ );
510
1336
 
511
1337
  (methodCalls as MethodCallInfo[]).push({
512
- id: `CALL#new:${fullName}#${module.file}#${newNode.loc!.start.line}:${newNode.loc!.start.column}:${callSiteCounterRef.value++}`,
1338
+ id: newMethodCallId,
513
1339
  type: 'CALL',
514
1340
  name: fullName,
515
1341
  object: objectName,
516
1342
  method: constructorName,
517
1343
  file: module.file,
518
- line: newNode.loc!.start.line,
519
- column: newNode.loc!.start.column,
1344
+ line: memberNewLine,
1345
+ column: memberNewColumn,
520
1346
  parentScopeId,
521
1347
  isNew: true // Mark as constructor call
522
1348
  });