@grafema/core 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (402) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +76 -0
  3. package/dist/Orchestrator.d.ts +142 -0
  4. package/dist/Orchestrator.d.ts.map +1 -0
  5. package/dist/Orchestrator.js +481 -0
  6. package/dist/api/GraphAPI.d.ts +87 -0
  7. package/dist/api/GraphAPI.d.ts.map +1 -0
  8. package/dist/api/GraphAPI.js +210 -0
  9. package/dist/api/GuaranteeAPI.d.ts +147 -0
  10. package/dist/api/GuaranteeAPI.d.ts.map +1 -0
  11. package/dist/api/GuaranteeAPI.js +288 -0
  12. package/dist/core/ASTWorker.d.ts +133 -0
  13. package/dist/core/ASTWorker.d.ts.map +1 -0
  14. package/dist/core/ASTWorker.js +352 -0
  15. package/dist/core/ASTWorkerPool.d.ts +85 -0
  16. package/dist/core/ASTWorkerPool.d.ts.map +1 -0
  17. package/dist/core/ASTWorkerPool.js +207 -0
  18. package/dist/core/AnalysisQueue.d.ts +104 -0
  19. package/dist/core/AnalysisQueue.d.ts.map +1 -0
  20. package/dist/core/AnalysisQueue.js +299 -0
  21. package/dist/core/AnalysisWorker.d.ts +14 -0
  22. package/dist/core/AnalysisWorker.d.ts.map +1 -0
  23. package/dist/core/AnalysisWorker.js +307 -0
  24. package/dist/core/GraphBackend.d.ts +156 -0
  25. package/dist/core/GraphBackend.d.ts.map +1 -0
  26. package/dist/core/GraphBackend.js +85 -0
  27. package/dist/core/GuaranteeManager.d.ts +230 -0
  28. package/dist/core/GuaranteeManager.d.ts.map +1 -0
  29. package/dist/core/GuaranteeManager.js +352 -0
  30. package/dist/core/ManifestStore.d.ts +71 -0
  31. package/dist/core/ManifestStore.d.ts.map +1 -0
  32. package/dist/core/ManifestStore.js +146 -0
  33. package/dist/core/NodeFactory.d.ts +160 -0
  34. package/dist/core/NodeFactory.d.ts.map +1 -0
  35. package/dist/core/NodeFactory.js +137 -0
  36. package/dist/core/NodeId.d.ts +88 -0
  37. package/dist/core/NodeId.d.ts.map +1 -0
  38. package/dist/core/NodeId.js +170 -0
  39. package/dist/core/ParallelAnalyzer.d.ts +120 -0
  40. package/dist/core/ParallelAnalyzer.d.ts.map +1 -0
  41. package/dist/core/ParallelAnalyzer.js +331 -0
  42. package/dist/core/PriorityQueue.d.ts +106 -0
  43. package/dist/core/PriorityQueue.d.ts.map +1 -0
  44. package/dist/core/PriorityQueue.js +168 -0
  45. package/dist/core/Profiler.d.ts +75 -0
  46. package/dist/core/Profiler.d.ts.map +1 -0
  47. package/dist/core/Profiler.js +149 -0
  48. package/dist/core/QueueWorker.d.ts +12 -0
  49. package/dist/core/QueueWorker.d.ts.map +1 -0
  50. package/dist/core/QueueWorker.js +567 -0
  51. package/dist/core/RFDBClient.d.ts +179 -0
  52. package/dist/core/RFDBClient.d.ts.map +1 -0
  53. package/dist/core/RFDBClient.js +429 -0
  54. package/dist/core/Task.d.ts +56 -0
  55. package/dist/core/Task.d.ts.map +1 -0
  56. package/dist/core/Task.js +85 -0
  57. package/dist/core/TaskTypes.d.ts +20 -0
  58. package/dist/core/TaskTypes.d.ts.map +1 -0
  59. package/dist/core/TaskTypes.js +10 -0
  60. package/dist/core/VersionManager.d.ts +166 -0
  61. package/dist/core/VersionManager.d.ts.map +1 -0
  62. package/dist/core/VersionManager.js +237 -0
  63. package/dist/core/WorkerPool.d.ts +82 -0
  64. package/dist/core/WorkerPool.d.ts.map +1 -0
  65. package/dist/core/WorkerPool.js +109 -0
  66. package/dist/core/nodes/CallSiteNode.d.ts +26 -0
  67. package/dist/core/nodes/CallSiteNode.d.ts.map +1 -0
  68. package/dist/core/nodes/CallSiteNode.js +44 -0
  69. package/dist/core/nodes/ClassNode.d.ts +25 -0
  70. package/dist/core/nodes/ClassNode.d.ts.map +1 -0
  71. package/dist/core/nodes/ClassNode.js +40 -0
  72. package/dist/core/nodes/ConstantNode.d.ts +24 -0
  73. package/dist/core/nodes/ConstantNode.d.ts.map +1 -0
  74. package/dist/core/nodes/ConstantNode.js +39 -0
  75. package/dist/core/nodes/DatabaseQueryNode.d.ts +22 -0
  76. package/dist/core/nodes/DatabaseQueryNode.d.ts.map +1 -0
  77. package/dist/core/nodes/DatabaseQueryNode.js +37 -0
  78. package/dist/core/nodes/EntrypointNode.d.ts +102 -0
  79. package/dist/core/nodes/EntrypointNode.d.ts.map +1 -0
  80. package/dist/core/nodes/EntrypointNode.js +119 -0
  81. package/dist/core/nodes/EventListenerNode.d.ts +25 -0
  82. package/dist/core/nodes/EventListenerNode.d.ts.map +1 -0
  83. package/dist/core/nodes/EventListenerNode.js +39 -0
  84. package/dist/core/nodes/ExportNode.d.ts +26 -0
  85. package/dist/core/nodes/ExportNode.d.ts.map +1 -0
  86. package/dist/core/nodes/ExportNode.js +40 -0
  87. package/dist/core/nodes/ExternalStdioNode.d.ts +17 -0
  88. package/dist/core/nodes/ExternalStdioNode.d.ts.map +1 -0
  89. package/dist/core/nodes/ExternalStdioNode.js +26 -0
  90. package/dist/core/nodes/FunctionNode.d.ts +27 -0
  91. package/dist/core/nodes/FunctionNode.d.ts.map +1 -0
  92. package/dist/core/nodes/FunctionNode.js +53 -0
  93. package/dist/core/nodes/GuaranteeNode.d.ts +76 -0
  94. package/dist/core/nodes/GuaranteeNode.d.ts.map +1 -0
  95. package/dist/core/nodes/GuaranteeNode.js +117 -0
  96. package/dist/core/nodes/HttpRequestNode.d.ts +24 -0
  97. package/dist/core/nodes/HttpRequestNode.d.ts.map +1 -0
  98. package/dist/core/nodes/HttpRequestNode.js +38 -0
  99. package/dist/core/nodes/ImportNode.d.ts +27 -0
  100. package/dist/core/nodes/ImportNode.d.ts.map +1 -0
  101. package/dist/core/nodes/ImportNode.js +43 -0
  102. package/dist/core/nodes/LiteralNode.d.ts +26 -0
  103. package/dist/core/nodes/LiteralNode.d.ts.map +1 -0
  104. package/dist/core/nodes/LiteralNode.js +40 -0
  105. package/dist/core/nodes/MethodCallNode.d.ts +29 -0
  106. package/dist/core/nodes/MethodCallNode.d.ts.map +1 -0
  107. package/dist/core/nodes/MethodCallNode.js +47 -0
  108. package/dist/core/nodes/MethodNode.d.ts +29 -0
  109. package/dist/core/nodes/MethodNode.d.ts.map +1 -0
  110. package/dist/core/nodes/MethodNode.js +44 -0
  111. package/dist/core/nodes/ModuleNode.d.ts +29 -0
  112. package/dist/core/nodes/ModuleNode.d.ts.map +1 -0
  113. package/dist/core/nodes/ModuleNode.js +49 -0
  114. package/dist/core/nodes/NodeKind.d.ts +91 -0
  115. package/dist/core/nodes/NodeKind.d.ts.map +1 -0
  116. package/dist/core/nodes/NodeKind.js +146 -0
  117. package/dist/core/nodes/ParameterNode.d.ts +26 -0
  118. package/dist/core/nodes/ParameterNode.d.ts.map +1 -0
  119. package/dist/core/nodes/ParameterNode.js +43 -0
  120. package/dist/core/nodes/ScopeNode.d.ts +32 -0
  121. package/dist/core/nodes/ScopeNode.d.ts.map +1 -0
  122. package/dist/core/nodes/ScopeNode.js +47 -0
  123. package/dist/core/nodes/ServiceNode.d.ts +44 -0
  124. package/dist/core/nodes/ServiceNode.d.ts.map +1 -0
  125. package/dist/core/nodes/ServiceNode.js +49 -0
  126. package/dist/core/nodes/VariableDeclarationNode.d.ts +22 -0
  127. package/dist/core/nodes/VariableDeclarationNode.d.ts.map +1 -0
  128. package/dist/core/nodes/VariableDeclarationNode.js +38 -0
  129. package/dist/core/nodes/index.d.ts +25 -0
  130. package/dist/core/nodes/index.d.ts.map +1 -0
  131. package/dist/core/nodes/index.js +30 -0
  132. package/dist/index.d.ts +57 -0
  133. package/dist/index.d.ts.map +1 -0
  134. package/dist/index.js +63 -0
  135. package/dist/plugins/Plugin.d.ts +44 -0
  136. package/dist/plugins/Plugin.d.ts.map +1 -0
  137. package/dist/plugins/Plugin.js +46 -0
  138. package/dist/plugins/analysis/DatabaseAnalyzer.d.ts +23 -0
  139. package/dist/plugins/analysis/DatabaseAnalyzer.d.ts.map +1 -0
  140. package/dist/plugins/analysis/DatabaseAnalyzer.js +260 -0
  141. package/dist/plugins/analysis/ExpressAnalyzer.d.ts +19 -0
  142. package/dist/plugins/analysis/ExpressAnalyzer.d.ts.map +1 -0
  143. package/dist/plugins/analysis/ExpressAnalyzer.js +306 -0
  144. package/dist/plugins/analysis/ExpressRouteAnalyzer.d.ts +17 -0
  145. package/dist/plugins/analysis/ExpressRouteAnalyzer.d.ts.map +1 -0
  146. package/dist/plugins/analysis/ExpressRouteAnalyzer.js +308 -0
  147. package/dist/plugins/analysis/FetchAnalyzer.d.ts +38 -0
  148. package/dist/plugins/analysis/FetchAnalyzer.d.ts.map +1 -0
  149. package/dist/plugins/analysis/FetchAnalyzer.js +344 -0
  150. package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts +65 -0
  151. package/dist/plugins/analysis/IncrementalAnalysisPlugin.d.ts.map +1 -0
  152. package/dist/plugins/analysis/IncrementalAnalysisPlugin.js +472 -0
  153. package/dist/plugins/analysis/JSASTAnalyzer.d.ts +84 -0
  154. package/dist/plugins/analysis/JSASTAnalyzer.d.ts.map +1 -0
  155. package/dist/plugins/analysis/JSASTAnalyzer.js +1378 -0
  156. package/dist/plugins/analysis/ReactAnalyzer.d.ts +90 -0
  157. package/dist/plugins/analysis/ReactAnalyzer.d.ts.map +1 -0
  158. package/dist/plugins/analysis/ReactAnalyzer.js +1153 -0
  159. package/dist/plugins/analysis/RustAnalyzer.d.ts +13 -0
  160. package/dist/plugins/analysis/RustAnalyzer.d.ts.map +1 -0
  161. package/dist/plugins/analysis/RustAnalyzer.js +259 -0
  162. package/dist/plugins/analysis/SQLiteAnalyzer.d.ts +21 -0
  163. package/dist/plugins/analysis/SQLiteAnalyzer.d.ts.map +1 -0
  164. package/dist/plugins/analysis/SQLiteAnalyzer.js +317 -0
  165. package/dist/plugins/analysis/ServiceLayerAnalyzer.d.ts +35 -0
  166. package/dist/plugins/analysis/ServiceLayerAnalyzer.d.ts.map +1 -0
  167. package/dist/plugins/analysis/ServiceLayerAnalyzer.js +303 -0
  168. package/dist/plugins/analysis/SocketIOAnalyzer.d.ts +33 -0
  169. package/dist/plugins/analysis/SocketIOAnalyzer.d.ts.map +1 -0
  170. package/dist/plugins/analysis/SocketIOAnalyzer.js +283 -0
  171. package/dist/plugins/analysis/SystemDbAnalyzer.d.ts +27 -0
  172. package/dist/plugins/analysis/SystemDbAnalyzer.d.ts.map +1 -0
  173. package/dist/plugins/analysis/SystemDbAnalyzer.js +211 -0
  174. package/dist/plugins/analysis/ast/ConditionParser.d.ts +85 -0
  175. package/dist/plugins/analysis/ast/ConditionParser.d.ts.map +1 -0
  176. package/dist/plugins/analysis/ast/ConditionParser.js +277 -0
  177. package/dist/plugins/analysis/ast/ExpressionEvaluator.d.ts +15 -0
  178. package/dist/plugins/analysis/ast/ExpressionEvaluator.d.ts.map +1 -0
  179. package/dist/plugins/analysis/ast/ExpressionEvaluator.js +91 -0
  180. package/dist/plugins/analysis/ast/GraphBuilder.d.ts +77 -0
  181. package/dist/plugins/analysis/ast/GraphBuilder.d.ts.map +1 -0
  182. package/dist/plugins/analysis/ast/GraphBuilder.js +1077 -0
  183. package/dist/plugins/analysis/ast/OxcAdapter.d.ts +41 -0
  184. package/dist/plugins/analysis/ast/OxcAdapter.d.ts.map +1 -0
  185. package/dist/plugins/analysis/ast/OxcAdapter.js +40 -0
  186. package/dist/plugins/analysis/ast/types.d.ts +346 -0
  187. package/dist/plugins/analysis/ast/types.d.ts.map +1 -0
  188. package/dist/plugins/analysis/ast/types.js +4 -0
  189. package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts +93 -0
  190. package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts.map +1 -0
  191. package/dist/plugins/analysis/ast/visitors/ASTVisitor.js +24 -0
  192. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts +77 -0
  193. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts.map +1 -0
  194. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js +377 -0
  195. package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts +27 -0
  196. package/dist/plugins/analysis/ast/visitors/ClassVisitor.d.ts.map +1 -0
  197. package/dist/plugins/analysis/ast/visitors/ClassVisitor.js +232 -0
  198. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts +25 -0
  199. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts.map +1 -0
  200. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js +172 -0
  201. package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.d.ts +29 -0
  202. package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.d.ts.map +1 -0
  203. package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.js +180 -0
  204. package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts +14 -0
  205. package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.d.ts.map +1 -0
  206. package/dist/plugins/analysis/ast/visitors/TypeScriptVisitor.js +200 -0
  207. package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts +45 -0
  208. package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts.map +1 -0
  209. package/dist/plugins/analysis/ast/visitors/VariableVisitor.js +150 -0
  210. package/dist/plugins/analysis/ast/visitors/index.d.ts +17 -0
  211. package/dist/plugins/analysis/ast/visitors/index.d.ts.map +1 -0
  212. package/dist/plugins/analysis/ast/visitors/index.js +13 -0
  213. package/dist/plugins/discovery/DiscoveryPlugin.d.ts +34 -0
  214. package/dist/plugins/discovery/DiscoveryPlugin.d.ts.map +1 -0
  215. package/dist/plugins/discovery/DiscoveryPlugin.js +26 -0
  216. package/dist/plugins/discovery/MonorepoServiceDiscovery.d.ts +26 -0
  217. package/dist/plugins/discovery/MonorepoServiceDiscovery.d.ts.map +1 -0
  218. package/dist/plugins/discovery/MonorepoServiceDiscovery.js +79 -0
  219. package/dist/plugins/discovery/SimpleProjectDiscovery.d.ts +14 -0
  220. package/dist/plugins/discovery/SimpleProjectDiscovery.d.ts.map +1 -0
  221. package/dist/plugins/discovery/SimpleProjectDiscovery.js +65 -0
  222. package/dist/plugins/discovery/ZonServiceDiscovery.d.ts +19 -0
  223. package/dist/plugins/discovery/ZonServiceDiscovery.d.ts.map +1 -0
  224. package/dist/plugins/discovery/ZonServiceDiscovery.js +204 -0
  225. package/dist/plugins/enrichment/AliasTracker.d.ts +40 -0
  226. package/dist/plugins/enrichment/AliasTracker.d.ts.map +1 -0
  227. package/dist/plugins/enrichment/AliasTracker.js +290 -0
  228. package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts +30 -0
  229. package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts.map +1 -0
  230. package/dist/plugins/enrichment/HTTPConnectionEnricher.js +135 -0
  231. package/dist/plugins/enrichment/ImportExportLinker.d.ts +30 -0
  232. package/dist/plugins/enrichment/ImportExportLinker.d.ts.map +1 -0
  233. package/dist/plugins/enrichment/ImportExportLinker.js +176 -0
  234. package/dist/plugins/enrichment/InstanceOfResolver.d.ts +21 -0
  235. package/dist/plugins/enrichment/InstanceOfResolver.d.ts.map +1 -0
  236. package/dist/plugins/enrichment/InstanceOfResolver.js +117 -0
  237. package/dist/plugins/enrichment/MethodCallResolver.d.ts +41 -0
  238. package/dist/plugins/enrichment/MethodCallResolver.d.ts.map +1 -0
  239. package/dist/plugins/enrichment/MethodCallResolver.js +252 -0
  240. package/dist/plugins/enrichment/MountPointResolver.d.ts +26 -0
  241. package/dist/plugins/enrichment/MountPointResolver.d.ts.map +1 -0
  242. package/dist/plugins/enrichment/MountPointResolver.js +189 -0
  243. package/dist/plugins/enrichment/PrefixEvaluator.d.ts +89 -0
  244. package/dist/plugins/enrichment/PrefixEvaluator.d.ts.map +1 -0
  245. package/dist/plugins/enrichment/PrefixEvaluator.js +415 -0
  246. package/dist/plugins/enrichment/RustFFIEnricher.d.ts +25 -0
  247. package/dist/plugins/enrichment/RustFFIEnricher.d.ts.map +1 -0
  248. package/dist/plugins/enrichment/RustFFIEnricher.js +170 -0
  249. package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts +114 -0
  250. package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts.map +1 -0
  251. package/dist/plugins/enrichment/ValueDomainAnalyzer.js +464 -0
  252. package/dist/plugins/indexing/IncrementalModuleIndexer.d.ts +27 -0
  253. package/dist/plugins/indexing/IncrementalModuleIndexer.d.ts.map +1 -0
  254. package/dist/plugins/indexing/IncrementalModuleIndexer.js +238 -0
  255. package/dist/plugins/indexing/JSModuleIndexer.d.ts +33 -0
  256. package/dist/plugins/indexing/JSModuleIndexer.d.ts.map +1 -0
  257. package/dist/plugins/indexing/JSModuleIndexer.js +299 -0
  258. package/dist/plugins/indexing/RustModuleIndexer.d.ts +28 -0
  259. package/dist/plugins/indexing/RustModuleIndexer.d.ts.map +1 -0
  260. package/dist/plugins/indexing/RustModuleIndexer.js +140 -0
  261. package/dist/plugins/indexing/ServiceDetector.d.ts +46 -0
  262. package/dist/plugins/indexing/ServiceDetector.d.ts.map +1 -0
  263. package/dist/plugins/indexing/ServiceDetector.js +164 -0
  264. package/dist/plugins/validation/CallResolverValidator.d.ts +23 -0
  265. package/dist/plugins/validation/CallResolverValidator.d.ts.map +1 -0
  266. package/dist/plugins/validation/CallResolverValidator.js +108 -0
  267. package/dist/plugins/validation/DataFlowValidator.d.ts +24 -0
  268. package/dist/plugins/validation/DataFlowValidator.d.ts.map +1 -0
  269. package/dist/plugins/validation/DataFlowValidator.js +148 -0
  270. package/dist/plugins/validation/EvalBanValidator.d.ts +25 -0
  271. package/dist/plugins/validation/EvalBanValidator.d.ts.map +1 -0
  272. package/dist/plugins/validation/EvalBanValidator.js +123 -0
  273. package/dist/plugins/validation/GraphConnectivityValidator.d.ts +11 -0
  274. package/dist/plugins/validation/GraphConnectivityValidator.d.ts.map +1 -0
  275. package/dist/plugins/validation/GraphConnectivityValidator.js +135 -0
  276. package/dist/plugins/validation/SQLInjectionValidator.d.ts +43 -0
  277. package/dist/plugins/validation/SQLInjectionValidator.d.ts.map +1 -0
  278. package/dist/plugins/validation/SQLInjectionValidator.js +251 -0
  279. package/dist/plugins/validation/ShadowingDetector.d.ts +26 -0
  280. package/dist/plugins/validation/ShadowingDetector.d.ts.map +1 -0
  281. package/dist/plugins/validation/ShadowingDetector.js +119 -0
  282. package/dist/plugins/validation/TypeScriptDeadCodeValidator.d.ts +21 -0
  283. package/dist/plugins/validation/TypeScriptDeadCodeValidator.d.ts.map +1 -0
  284. package/dist/plugins/validation/TypeScriptDeadCodeValidator.js +151 -0
  285. package/dist/plugins/vcs/GitPlugin.d.ts +84 -0
  286. package/dist/plugins/vcs/GitPlugin.d.ts.map +1 -0
  287. package/dist/plugins/vcs/GitPlugin.js +295 -0
  288. package/dist/plugins/vcs/VCSPlugin.d.ts +133 -0
  289. package/dist/plugins/vcs/VCSPlugin.d.ts.map +1 -0
  290. package/dist/plugins/vcs/VCSPlugin.js +82 -0
  291. package/dist/plugins/vcs/index.d.ts +10 -0
  292. package/dist/plugins/vcs/index.d.ts.map +1 -0
  293. package/dist/plugins/vcs/index.js +18 -0
  294. package/dist/storage/backends/RFDBServerBackend.d.ts +258 -0
  295. package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -0
  296. package/dist/storage/backends/RFDBServerBackend.js +565 -0
  297. package/dist/storage/backends/typeValidation.d.ts +47 -0
  298. package/dist/storage/backends/typeValidation.d.ts.map +1 -0
  299. package/dist/storage/backends/typeValidation.js +137 -0
  300. package/dist/validation/PathValidator.d.ts +81 -0
  301. package/dist/validation/PathValidator.d.ts.map +1 -0
  302. package/dist/validation/PathValidator.js +251 -0
  303. package/package.json +57 -0
  304. package/src/.rfguard/current-session.txt +1 -0
  305. package/src/Orchestrator.ts +673 -0
  306. package/src/api/GraphAPI.ts +305 -0
  307. package/src/api/GuaranteeAPI.ts +401 -0
  308. package/src/core/ASTWorker.ts +567 -0
  309. package/src/core/ASTWorkerPool.ts +299 -0
  310. package/src/core/AnalysisQueue.ts +447 -0
  311. package/src/core/AnalysisWorker.ts +410 -0
  312. package/src/core/GraphBackend.ts +265 -0
  313. package/src/core/GuaranteeManager.ts +581 -0
  314. package/src/core/ManifestStore.ts +196 -0
  315. package/src/core/NodeFactory.ts +274 -0
  316. package/src/core/NodeId.ts +257 -0
  317. package/src/core/ParallelAnalyzer.ts +476 -0
  318. package/src/core/PriorityQueue.ts +227 -0
  319. package/src/core/Profiler.ts +188 -0
  320. package/src/core/QueueWorker.ts +780 -0
  321. package/src/core/Task.ts +107 -0
  322. package/src/core/TaskTypes.ts +40 -0
  323. package/src/core/VersionManager.ts +404 -0
  324. package/src/core/WorkerPool.ts +180 -0
  325. package/src/core/nodes/CallSiteNode.ts +72 -0
  326. package/src/core/nodes/ClassNode.ts +69 -0
  327. package/src/core/nodes/ConstantNode.ts +63 -0
  328. package/src/core/nodes/DatabaseQueryNode.ts +60 -0
  329. package/src/core/nodes/EntrypointNode.ts +164 -0
  330. package/src/core/nodes/EventListenerNode.ts +64 -0
  331. package/src/core/nodes/ExportNode.ts +71 -0
  332. package/src/core/nodes/ExternalStdioNode.ts +36 -0
  333. package/src/core/nodes/FunctionNode.ts +78 -0
  334. package/src/core/nodes/GuaranteeNode.ts +162 -0
  335. package/src/core/nodes/HttpRequestNode.ts +63 -0
  336. package/src/core/nodes/ImportNode.ts +75 -0
  337. package/src/core/nodes/LiteralNode.ts +67 -0
  338. package/src/core/nodes/MethodCallNode.ts +79 -0
  339. package/src/core/nodes/MethodNode.ts +78 -0
  340. package/src/core/nodes/ModuleNode.ts +74 -0
  341. package/src/core/nodes/NodeKind.ts +171 -0
  342. package/src/core/nodes/ParameterNode.ts +73 -0
  343. package/src/core/nodes/ScopeNode.ts +80 -0
  344. package/src/core/nodes/ServiceNode.ts +86 -0
  345. package/src/core/nodes/VariableDeclarationNode.ts +60 -0
  346. package/src/core/nodes/index.ts +49 -0
  347. package/src/index.ts +93 -0
  348. package/src/plugins/Plugin.ts +74 -0
  349. package/src/plugins/analysis/DatabaseAnalyzer.ts +322 -0
  350. package/src/plugins/analysis/ExpressAnalyzer.ts +401 -0
  351. package/src/plugins/analysis/ExpressRouteAnalyzer.ts +414 -0
  352. package/src/plugins/analysis/FetchAnalyzer.ts +441 -0
  353. package/src/plugins/analysis/IncrementalAnalysisPlugin.ts +686 -0
  354. package/src/plugins/analysis/JSASTAnalyzer.ts +1680 -0
  355. package/src/plugins/analysis/ReactAnalyzer.ts +1368 -0
  356. package/src/plugins/analysis/RustAnalyzer.ts +438 -0
  357. package/src/plugins/analysis/SQLiteAnalyzer.ts +388 -0
  358. package/src/plugins/analysis/ServiceLayerAnalyzer.ts +429 -0
  359. package/src/plugins/analysis/SocketIOAnalyzer.ts +395 -0
  360. package/src/plugins/analysis/SystemDbAnalyzer.ts +284 -0
  361. package/src/plugins/analysis/ast/ConditionParser.ts +333 -0
  362. package/src/plugins/analysis/ast/ExpressionEvaluator.ts +117 -0
  363. package/src/plugins/analysis/ast/GraphBuilder.ts +1371 -0
  364. package/src/plugins/analysis/ast/OxcAdapter.ts +63 -0
  365. package/src/plugins/analysis/ast/types.ts +400 -0
  366. package/src/plugins/analysis/ast/visitors/ASTVisitor.ts +137 -0
  367. package/src/plugins/analysis/ast/visitors/CallExpressionVisitor.ts +528 -0
  368. package/src/plugins/analysis/ast/visitors/ClassVisitor.ts +339 -0
  369. package/src/plugins/analysis/ast/visitors/FunctionVisitor.ts +273 -0
  370. package/src/plugins/analysis/ast/visitors/ImportExportVisitor.ts +259 -0
  371. package/src/plugins/analysis/ast/visitors/TypeScriptVisitor.ts +235 -0
  372. package/src/plugins/analysis/ast/visitors/VariableVisitor.ts +268 -0
  373. package/src/plugins/analysis/ast/visitors/index.ts +36 -0
  374. package/src/plugins/discovery/DiscoveryPlugin.ts +50 -0
  375. package/src/plugins/discovery/MonorepoServiceDiscovery.ts +117 -0
  376. package/src/plugins/discovery/SimpleProjectDiscovery.ts +102 -0
  377. package/src/plugins/enrichment/AliasTracker.ts +399 -0
  378. package/src/plugins/enrichment/HTTPConnectionEnricher.ts +192 -0
  379. package/src/plugins/enrichment/ImportExportLinker.ts +221 -0
  380. package/src/plugins/enrichment/InstanceOfResolver.ts +165 -0
  381. package/src/plugins/enrichment/MethodCallResolver.ts +333 -0
  382. package/src/plugins/enrichment/MountPointResolver.ts +264 -0
  383. package/src/plugins/enrichment/PrefixEvaluator.ts +527 -0
  384. package/src/plugins/enrichment/RustFFIEnricher.ts +218 -0
  385. package/src/plugins/enrichment/ValueDomainAnalyzer.ts +682 -0
  386. package/src/plugins/indexing/IncrementalModuleIndexer.ts +287 -0
  387. package/src/plugins/indexing/JSModuleIndexer.ts +374 -0
  388. package/src/plugins/indexing/RustModuleIndexer.ts +160 -0
  389. package/src/plugins/indexing/ServiceDetector.ts +230 -0
  390. package/src/plugins/validation/CallResolverValidator.ts +170 -0
  391. package/src/plugins/validation/DataFlowValidator.ts +233 -0
  392. package/src/plugins/validation/EvalBanValidator.ts +175 -0
  393. package/src/plugins/validation/GraphConnectivityValidator.ts +201 -0
  394. package/src/plugins/validation/SQLInjectionValidator.ts +363 -0
  395. package/src/plugins/validation/ShadowingDetector.ts +173 -0
  396. package/src/plugins/validation/TypeScriptDeadCodeValidator.ts +203 -0
  397. package/src/plugins/vcs/GitPlugin.ts +344 -0
  398. package/src/plugins/vcs/VCSPlugin.ts +190 -0
  399. package/src/plugins/vcs/index.ts +32 -0
  400. package/src/storage/backends/RFDBServerBackend.ts +687 -0
  401. package/src/storage/backends/typeValidation.ts +151 -0
  402. package/src/validation/PathValidator.ts +342 -0
@@ -0,0 +1,1153 @@
1
+ /**
2
+ * ReactAnalyzer - React/Browser domain-specific analysis
3
+ *
4
+ * Detects React patterns:
5
+ * - Components and rendering tree
6
+ * - Props flow between components
7
+ * - Event handlers (onClick, onSubmit, etc.)
8
+ * - Hooks (useState, useEffect, useCallback, useMemo, useRef, useReducer, useContext)
9
+ * - Browser APIs (localStorage, timers, DOM, observers)
10
+ * - Edge cases (stale closures, missing cleanup, RAF bugs)
11
+ */
12
+ import { readFileSync } from 'fs';
13
+ import { parse } from '@babel/parser';
14
+ import traverseModule from '@babel/traverse';
15
+ import { Plugin, createSuccessResult, createErrorResult } from '../Plugin.js';
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ const traverse = traverseModule.default || traverseModule;
18
+ // React event handlers mapping
19
+ const REACT_EVENTS = {
20
+ // Mouse events
21
+ onClick: 'click', onDoubleClick: 'dblclick', onContextMenu: 'contextmenu',
22
+ onMouseDown: 'mousedown', onMouseUp: 'mouseup', onMouseEnter: 'mouseenter',
23
+ onMouseLeave: 'mouseleave', onMouseMove: 'mousemove', onMouseOver: 'mouseover',
24
+ onMouseOut: 'mouseout',
25
+ // Keyboard events
26
+ onKeyDown: 'keydown', onKeyUp: 'keyup', onKeyPress: 'keypress',
27
+ // Focus events
28
+ onFocus: 'focus', onBlur: 'blur', onFocusCapture: 'focus:capture',
29
+ // Form events
30
+ onSubmit: 'submit', onReset: 'reset', onChange: 'change', onInput: 'input',
31
+ onInvalid: 'invalid',
32
+ // Touch events
33
+ onTouchStart: 'touchstart', onTouchMove: 'touchmove', onTouchEnd: 'touchend',
34
+ onTouchCancel: 'touchcancel',
35
+ // Drag events
36
+ onDragStart: 'dragstart', onDrag: 'drag', onDragEnd: 'dragend',
37
+ onDragEnter: 'dragenter', onDragOver: 'dragover', onDragLeave: 'dragleave',
38
+ onDrop: 'drop',
39
+ // Scroll/Wheel events
40
+ onScroll: 'scroll', onWheel: 'wheel',
41
+ // Clipboard events
42
+ onCopy: 'copy', onCut: 'cut', onPaste: 'paste',
43
+ // Composition events
44
+ onCompositionStart: 'compositionstart', onCompositionUpdate: 'compositionupdate',
45
+ onCompositionEnd: 'compositionend',
46
+ // Media events
47
+ onPlay: 'play', onPause: 'pause', onEnded: 'ended', onTimeUpdate: 'timeupdate',
48
+ onLoadedData: 'loadeddata', onLoadedMetadata: 'loadedmetadata',
49
+ onCanPlay: 'canplay', onWaiting: 'waiting', onSeeking: 'seeking',
50
+ onSeeked: 'seeked', onError: 'error', onVolumeChange: 'volumechange',
51
+ // Image events
52
+ onLoad: 'load',
53
+ // Animation events
54
+ onAnimationStart: 'animationstart', onAnimationEnd: 'animationend',
55
+ onAnimationIteration: 'animationiteration',
56
+ // Transition events
57
+ onTransitionEnd: 'transitionend',
58
+ // Pointer events
59
+ onPointerDown: 'pointerdown', onPointerUp: 'pointerup', onPointerMove: 'pointermove',
60
+ onPointerEnter: 'pointerenter', onPointerLeave: 'pointerleave',
61
+ onPointerCancel: 'pointercancel', onGotPointerCapture: 'gotpointercapture',
62
+ onLostPointerCapture: 'lostpointercapture'
63
+ };
64
+ // React hooks that need tracking
65
+ const REACT_HOOKS = [
66
+ 'useState', 'useEffect', 'useLayoutEffect', 'useInsertionEffect',
67
+ 'useCallback', 'useMemo', 'useRef', 'useReducer', 'useContext',
68
+ 'useImperativeHandle', 'useDebugValue', 'useDeferredValue',
69
+ 'useTransition', 'useId', 'useSyncExternalStore'
70
+ ];
71
+ // Browser APIs that create side effects
72
+ const BROWSER_APIS = {
73
+ timers: ['setTimeout', 'setInterval', 'requestAnimationFrame', 'requestIdleCallback'],
74
+ cleanup: {
75
+ setTimeout: 'clearTimeout',
76
+ setInterval: 'clearInterval',
77
+ requestAnimationFrame: 'cancelAnimationFrame',
78
+ requestIdleCallback: 'cancelIdleCallback'
79
+ },
80
+ observers: ['IntersectionObserver', 'ResizeObserver', 'MutationObserver', 'PerformanceObserver'],
81
+ storage: ['localStorage', 'sessionStorage'],
82
+ async: ['fetch', 'XMLHttpRequest', 'WebSocket', 'EventSource'],
83
+ dom: ['document', 'getElementById', 'querySelector', 'querySelectorAll'],
84
+ workers: ['Worker', 'SharedWorker', 'ServiceWorker'],
85
+ geolocation: ['grafemagator.geolocation'],
86
+ notifications: ['Notification'],
87
+ fullscreen: ['requestFullscreen', 'exitFullscreen'],
88
+ clipboard: ['grafemagator.clipboard'],
89
+ history: ['history.pushState', 'history.replaceState'],
90
+ blocking: ['alert', 'confirm', 'prompt']
91
+ };
92
+ export class ReactAnalyzer extends Plugin {
93
+ get metadata() {
94
+ return {
95
+ name: 'ReactAnalyzer',
96
+ phase: 'ANALYSIS',
97
+ priority: 70, // After JSASTAnalyzer and ExpressAnalyzer
98
+ creates: {
99
+ nodes: [
100
+ 'react:component', 'react:state', 'react:effect', 'react:callback',
101
+ 'react:memo', 'react:ref', 'react:reducer', 'react:context',
102
+ 'dom:event', 'browser:storage', 'browser:timer', 'browser:observer',
103
+ 'browser:async', 'browser:worker', 'browser:api',
104
+ 'canvas:context', 'canvas:draw',
105
+ 'issue:stale-closure', 'issue:missing-cleanup', 'issue:raf-leak',
106
+ 'issue:canvas-leak', 'issue:state-after-unmount'
107
+ ],
108
+ edges: [
109
+ 'RENDERS', 'PASSES_PROP', 'HANDLES_EVENT', 'UPDATES_STATE',
110
+ 'DEPENDS_ON', 'SCHEDULES', 'CLEANS_UP', 'DISPATCHES',
111
+ 'PROVIDES', 'CONSUMES', 'FORWARDS_REF', 'OBSERVES'
112
+ ]
113
+ },
114
+ dependencies: ['JSASTAnalyzer']
115
+ };
116
+ }
117
+ async execute(context) {
118
+ try {
119
+ const { graph } = context;
120
+ const modules = await this.getModules(graph);
121
+ const stats = {
122
+ components: 0,
123
+ hooks: 0,
124
+ events: 0,
125
+ browserAPIs: 0,
126
+ issues: 0,
127
+ edges: 0
128
+ };
129
+ for (const module of modules) {
130
+ // Only analyze .jsx, .tsx, or files that import React
131
+ if (!this.isReactFile(module.file)) {
132
+ continue;
133
+ }
134
+ try {
135
+ const result = await this.analyzeModule(module, graph);
136
+ stats.components += result.components;
137
+ stats.hooks += result.hooks;
138
+ stats.events += result.events;
139
+ stats.browserAPIs += result.browserAPIs;
140
+ stats.issues += result.issues;
141
+ stats.edges += result.edges;
142
+ }
143
+ catch (err) {
144
+ console.error(`[ReactAnalyzer] Error analyzing ${module.file}:`, err.message);
145
+ }
146
+ }
147
+ console.log(`[ReactAnalyzer] Found ${stats.components} components, ${stats.hooks} hooks, ${stats.events} events, ${stats.issues} issues`);
148
+ return createSuccessResult({
149
+ nodes: stats.components + stats.hooks + stats.events + stats.browserAPIs + stats.issues,
150
+ edges: stats.edges
151
+ }, stats);
152
+ }
153
+ catch (error) {
154
+ console.error(`[ReactAnalyzer] Error:`, error);
155
+ return createErrorResult(error);
156
+ }
157
+ }
158
+ isReactFile(filePath) {
159
+ if (filePath.endsWith('.jsx') || filePath.endsWith('.tsx')) {
160
+ return true;
161
+ }
162
+ // Could also check for React import in .js/.ts files
163
+ return false;
164
+ }
165
+ async analyzeModule(module, graph) {
166
+ const code = readFileSync(module.file, 'utf-8');
167
+ const ast = parse(code, {
168
+ sourceType: 'module',
169
+ plugins: ['jsx', 'typescript']
170
+ });
171
+ return this.analyzeAST(ast, module.file, graph, module.id);
172
+ }
173
+ /**
174
+ * Main AST analysis - can be called directly for testing
175
+ */
176
+ async analyzeAST(ast, filePath, graph, moduleId = null) {
177
+ const analysis = {
178
+ components: [],
179
+ hooks: [],
180
+ events: [],
181
+ browserAPIs: [],
182
+ issues: [],
183
+ edges: []
184
+ };
185
+ const importedIdentifiers = new Set();
186
+ // Collect imported identifiers (stable references)
187
+ traverse(ast, {
188
+ ImportDeclaration: (path) => {
189
+ const node = path.node;
190
+ for (const specifier of node.specifiers) {
191
+ if (specifier.local?.name) {
192
+ importedIdentifiers.add(specifier.local.name);
193
+ }
194
+ }
195
+ }
196
+ });
197
+ // First pass: collect component definitions
198
+ traverse(ast, {
199
+ // Arrow function components: const App = () => ...
200
+ VariableDeclarator: (path) => {
201
+ if (this.isReactComponent(path)) {
202
+ const node = path.node;
203
+ const name = node.id.name;
204
+ const loc = node.loc;
205
+ const component = {
206
+ id: `react:component#${name}#${filePath}:${loc.start.line}`,
207
+ type: 'react:component',
208
+ name,
209
+ file: filePath,
210
+ line: loc.start.line,
211
+ column: loc.start.column,
212
+ kind: 'arrow'
213
+ };
214
+ analysis.components.push(component);
215
+ }
216
+ },
217
+ // Function declaration components: function App() {...}
218
+ FunctionDeclaration: (path) => {
219
+ if (this.isReactComponent(path)) {
220
+ const name = path.node.id?.name;
221
+ if (!name)
222
+ return;
223
+ const loc = path.node.loc;
224
+ const component = {
225
+ id: `react:component#${name}#${filePath}:${loc.start.line}`,
226
+ type: 'react:component',
227
+ name,
228
+ file: filePath,
229
+ line: loc.start.line,
230
+ column: loc.start.column,
231
+ kind: 'function'
232
+ };
233
+ analysis.components.push(component);
234
+ }
235
+ }
236
+ });
237
+ // Second pass: analyze hooks, events, JSX
238
+ traverse(ast, {
239
+ CallExpression: (path) => {
240
+ const callee = path.node.callee;
241
+ // Detect React hooks
242
+ if (callee.type === 'Identifier' && REACT_HOOKS.includes(callee.name)) {
243
+ const hookData = this.analyzeHook(path, filePath);
244
+ if (hookData) {
245
+ analysis.hooks.push(hookData);
246
+ // Check for issues in hooks
247
+ if (callee.name === 'useEffect' || callee.name === 'useLayoutEffect') {
248
+ this.checkEffectIssues(path, filePath, analysis, hookData, importedIdentifiers);
249
+ }
250
+ }
251
+ }
252
+ // Detect forwardRef
253
+ if (callee.type === 'Identifier' && callee.name === 'forwardRef') {
254
+ this.analyzeForwardRef(path, filePath, analysis);
255
+ }
256
+ // Detect createContext
257
+ if (callee.type === 'Identifier' && callee.name === 'createContext') {
258
+ this.analyzeCreateContext(path, filePath, analysis);
259
+ }
260
+ // Detect browser APIs
261
+ this.analyzeBrowserAPI(path, filePath, analysis);
262
+ },
263
+ // JSX elements
264
+ JSXElement: (path) => {
265
+ this.analyzeJSXElement(path, filePath, analysis);
266
+ },
267
+ // JSX attributes (for event handlers and props)
268
+ JSXAttribute: (path) => {
269
+ this.analyzeJSXAttribute(path, filePath, analysis);
270
+ }
271
+ });
272
+ // Add all nodes and edges to graph
273
+ await this.addToGraph(analysis, graph, moduleId);
274
+ return {
275
+ components: analysis.components.length,
276
+ hooks: analysis.hooks.length,
277
+ events: analysis.events.length,
278
+ browserAPIs: analysis.browserAPIs.length,
279
+ issues: analysis.issues.length,
280
+ edges: analysis.edges.length
281
+ };
282
+ }
283
+ /**
284
+ * Check if a function is a React component (returns JSX)
285
+ */
286
+ isReactComponent(path) {
287
+ let hasJSXReturn = false;
288
+ // Check for arrow function or function
289
+ const node = path.node;
290
+ const func = node.init || path.node;
291
+ if (!func)
292
+ return false;
293
+ // Must be a function
294
+ if (func.type !== 'ArrowFunctionExpression' &&
295
+ func.type !== 'FunctionExpression' &&
296
+ func.type !== 'FunctionDeclaration') {
297
+ return false;
298
+ }
299
+ // Name must start with uppercase (React component convention)
300
+ const pathNode = path.node;
301
+ const name = pathNode.id?.name;
302
+ if (!name || !/^[A-Z]/.test(name)) {
303
+ return false;
304
+ }
305
+ // Check if body contains JSX
306
+ path.traverse({
307
+ JSXElement: () => { hasJSXReturn = true; },
308
+ JSXFragment: () => { hasJSXReturn = true; }
309
+ });
310
+ return hasJSXReturn;
311
+ }
312
+ /**
313
+ * Analyze React hooks
314
+ */
315
+ analyzeHook(path, filePath) {
316
+ const callee = path.node.callee;
317
+ const hookName = callee.name;
318
+ const loc = path.node.loc;
319
+ const args = path.node.arguments;
320
+ const hookBase = {
321
+ file: filePath,
322
+ line: loc.start.line,
323
+ column: loc.start.column,
324
+ hookName
325
+ };
326
+ switch (hookName) {
327
+ case 'useState': {
328
+ // const [state, setState] = useState(initialValue)
329
+ const parent = path.parent;
330
+ if (parent.type === 'VariableDeclarator' &&
331
+ parent.id?.type === 'ArrayPattern' &&
332
+ parent.id.elements?.length === 2) {
333
+ const stateName = parent.id.elements[0]?.name;
334
+ const setterName = parent.id.elements[1]?.name;
335
+ const initialValue = args[0];
336
+ return {
337
+ id: `react:state#${stateName}#${filePath}:${loc.start.line}`,
338
+ type: 'react:state',
339
+ ...hookBase,
340
+ stateName,
341
+ setterName,
342
+ initialValue: this.getExpressionValue(initialValue)
343
+ };
344
+ }
345
+ break;
346
+ }
347
+ case 'useEffect':
348
+ case 'useLayoutEffect':
349
+ case 'useInsertionEffect': {
350
+ // useEffect(() => {...}, [deps])
351
+ const callback = args[0];
352
+ const depsArg = args[1];
353
+ const deps = this.extractDeps(depsArg);
354
+ const hasCleanup = this.hasCleanupReturn(callback);
355
+ const effectType = hookName === 'useEffect' ? 'react:effect' :
356
+ hookName === 'useLayoutEffect' ? 'react:layout-effect' :
357
+ 'react:insertion-effect';
358
+ return {
359
+ id: `${effectType}#${filePath}:${loc.start.line}`,
360
+ type: effectType,
361
+ ...hookBase,
362
+ deps,
363
+ hasCleanup,
364
+ depsType: !depsArg ? 'none' : (deps?.length === 0 ? 'empty' : 'array')
365
+ };
366
+ }
367
+ case 'useCallback': {
368
+ // const fn = useCallback(() => {...}, [deps])
369
+ const parent = path.parent;
370
+ const callbackName = parent.type === 'VariableDeclarator' ? parent.id?.name : null;
371
+ const depsArg = args[1];
372
+ const deps = this.extractDeps(depsArg);
373
+ return {
374
+ id: `react:callback#${callbackName || 'anonymous'}#${filePath}:${loc.start.line}`,
375
+ type: 'react:callback',
376
+ ...hookBase,
377
+ callbackName,
378
+ deps
379
+ };
380
+ }
381
+ case 'useMemo': {
382
+ // const value = useMemo(() => computation, [deps])
383
+ const parent = path.parent;
384
+ const memoName = parent.type === 'VariableDeclarator' ? parent.id?.name : null;
385
+ const depsArg = args[1];
386
+ const deps = this.extractDeps(depsArg);
387
+ return {
388
+ id: `react:memo#${memoName || 'anonymous'}#${filePath}:${loc.start.line}`,
389
+ type: 'react:memo',
390
+ ...hookBase,
391
+ memoName,
392
+ deps
393
+ };
394
+ }
395
+ case 'useRef': {
396
+ // const ref = useRef(initialValue)
397
+ const parent = path.parent;
398
+ const refName = parent.type === 'VariableDeclarator' ? parent.id?.name : null;
399
+ const initialValue = args[0];
400
+ return {
401
+ id: `react:ref#${refName || 'anonymous'}#${filePath}:${loc.start.line}`,
402
+ type: 'react:ref',
403
+ ...hookBase,
404
+ refName,
405
+ initialValue: this.getExpressionValue(initialValue)
406
+ };
407
+ }
408
+ case 'useReducer': {
409
+ // const [state, dispatch] = useReducer(reducer, initialState)
410
+ const parent = path.parent;
411
+ if (parent.type === 'VariableDeclarator' &&
412
+ parent.id?.type === 'ArrayPattern' &&
413
+ parent.id.elements && parent.id.elements.length >= 2) {
414
+ const stateName = parent.id.elements[0]?.name;
415
+ const dispatchName = parent.id.elements[1]?.name;
416
+ const reducerArg = args[0];
417
+ const reducerName = reducerArg?.type === 'Identifier' ? reducerArg.name : null;
418
+ return {
419
+ id: `react:reducer#${stateName}#${filePath}:${loc.start.line}`,
420
+ type: 'react:reducer',
421
+ ...hookBase,
422
+ stateName,
423
+ dispatchName,
424
+ reducerName
425
+ };
426
+ }
427
+ break;
428
+ }
429
+ case 'useContext': {
430
+ // const value = useContext(Context)
431
+ const parent = path.parent;
432
+ const valueName = parent.type === 'VariableDeclarator' ? parent.id?.name : null;
433
+ const contextArg = args[0];
434
+ const contextName = contextArg?.type === 'Identifier' ? contextArg.name : null;
435
+ return {
436
+ id: `react:context-use#${contextName || 'unknown'}#${filePath}:${loc.start.line}`,
437
+ type: 'react:context-use',
438
+ ...hookBase,
439
+ valueName,
440
+ contextName
441
+ };
442
+ }
443
+ case 'useImperativeHandle': {
444
+ // useImperativeHandle(ref, () => ({ method1, method2 }), [deps])
445
+ const refArg = args[0];
446
+ const refName = refArg?.type === 'Identifier' ? refArg.name : null;
447
+ const createHandle = args[1];
448
+ // Extract exposed methods
449
+ const exposedMethods = [];
450
+ if (createHandle?.type === 'ArrowFunctionExpression' ||
451
+ createHandle?.type === 'FunctionExpression') {
452
+ const body = createHandle.body;
453
+ if (body.type === 'ObjectExpression') {
454
+ const objExpr = body;
455
+ for (const prop of objExpr.properties) {
456
+ if (prop.key?.name) {
457
+ exposedMethods.push(prop.key.name);
458
+ }
459
+ }
460
+ }
461
+ }
462
+ return {
463
+ id: `react:imperative-handle#${filePath}:${loc.start.line}`,
464
+ type: 'react:imperative-handle',
465
+ ...hookBase,
466
+ refName,
467
+ exposedMethods
468
+ };
469
+ }
470
+ }
471
+ return null;
472
+ }
473
+ /**
474
+ * Extract dependency array from hook
475
+ */
476
+ extractDeps(depsArg) {
477
+ if (!depsArg)
478
+ return null; // No deps argument
479
+ if (depsArg.type !== 'ArrayExpression')
480
+ return ['<dynamic>'];
481
+ const arrExpr = depsArg;
482
+ return arrExpr.elements.map(el => {
483
+ if (!el)
484
+ return '<empty>';
485
+ if (el.type === 'Identifier')
486
+ return el.name;
487
+ if (el.type === 'MemberExpression') {
488
+ return this.getMemberExpressionName(el);
489
+ }
490
+ return '<expression>';
491
+ });
492
+ }
493
+ getMemberExpressionName(node) {
494
+ if (node.type !== 'MemberExpression') {
495
+ return node.name || '<unknown>';
496
+ }
497
+ const memExpr = node;
498
+ const object = this.getMemberExpressionName(memExpr.object);
499
+ const property = memExpr.property.name || memExpr.property.value || '<computed>';
500
+ return `${object}.${property}`;
501
+ }
502
+ /**
503
+ * Check if effect callback has cleanup return
504
+ */
505
+ hasCleanupReturn(callback) {
506
+ if (!callback)
507
+ return false;
508
+ if (callback.type !== 'ArrowFunctionExpression' &&
509
+ callback.type !== 'FunctionExpression') {
510
+ return false;
511
+ }
512
+ // Simple AST traversal without using babel traverse
513
+ const checkForCleanupReturn = (node) => {
514
+ if (!node)
515
+ return false;
516
+ if (node.type === 'ReturnStatement') {
517
+ const retStmt = node;
518
+ const arg = retStmt.argument;
519
+ if (arg && (arg.type === 'ArrowFunctionExpression' ||
520
+ arg.type === 'FunctionExpression')) {
521
+ return true;
522
+ }
523
+ }
524
+ // Check body
525
+ const nodeWithBody = node;
526
+ if (nodeWithBody.body) {
527
+ if (Array.isArray(nodeWithBody.body)) {
528
+ return nodeWithBody.body.some(n => checkForCleanupReturn(n));
529
+ }
530
+ else if (nodeWithBody.body.type === 'BlockStatement') {
531
+ const blockStmt = nodeWithBody.body;
532
+ if (blockStmt.body) {
533
+ return blockStmt.body.some(n => checkForCleanupReturn(n));
534
+ }
535
+ }
536
+ else {
537
+ return checkForCleanupReturn(nodeWithBody.body);
538
+ }
539
+ }
540
+ return false;
541
+ };
542
+ return checkForCleanupReturn(callback);
543
+ }
544
+ /**
545
+ * Check for issues in useEffect/useLayoutEffect
546
+ */
547
+ checkEffectIssues(path, filePath, analysis, hookData, importedIdentifiers = new Set()) {
548
+ const callback = path.node.arguments[0];
549
+ if (!callback)
550
+ return;
551
+ const deps = hookData.deps;
552
+ const usedVars = new Set();
553
+ const setterCalls = new Set();
554
+ const callbackParams = new Set(); // Track parameters of nested callback functions
555
+ // Simple recursive AST walker to collect identifiers
556
+ const collectIdentifiers = (node, parentType = null, isPropertyKey = false) => {
557
+ if (!node)
558
+ return;
559
+ if (node.type === 'Identifier') {
560
+ const id = node;
561
+ // Skip if it's a property access key
562
+ if (!isPropertyKey && !callbackParams.has(id.name)) {
563
+ usedVars.add(id.name);
564
+ }
565
+ return;
566
+ }
567
+ if (node.type === 'MemberExpression') {
568
+ const memExpr = node;
569
+ // Check for ref.current pattern (valid for stable refs)
570
+ if (memExpr.property?.type === 'Identifier' && memExpr.property.name === 'current') {
571
+ // This is likely a ref.current access - collect only the ref name, not 'current'
572
+ collectIdentifiers(memExpr.object, 'MemberExpression', false);
573
+ return;
574
+ }
575
+ collectIdentifiers(memExpr.object, 'MemberExpression', false);
576
+ // Skip property name
577
+ return;
578
+ }
579
+ if (node.type === 'CallExpression') {
580
+ const callExpr = node;
581
+ const callee = callExpr.callee;
582
+ if (callee.type === 'Identifier' && callee.name?.startsWith('set')) {
583
+ const arg = callExpr.arguments[0];
584
+ if (arg?.type === 'ArrowFunctionExpression' ||
585
+ arg?.type === 'FunctionExpression') {
586
+ setterCalls.add(callee.name);
587
+ }
588
+ }
589
+ collectIdentifiers(callee, 'CallExpression', false);
590
+ callExpr.arguments?.forEach(arg => collectIdentifiers(arg, 'CallExpression', false));
591
+ return;
592
+ }
593
+ if (node.type === 'ObjectProperty') {
594
+ const prop = node;
595
+ // Skip key, process value
596
+ collectIdentifiers(prop.value, 'ObjectProperty', false);
597
+ return;
598
+ }
599
+ // Handle function parameters - skip them (AC-1: callback parameters are not external deps)
600
+ if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionExpression') {
601
+ const func = node;
602
+ // Collect parameter names from nested callbacks
603
+ func.params?.forEach(p => {
604
+ if (p.type === 'Identifier' && p.name) {
605
+ callbackParams.add(p.name); // Add to global tracking
606
+ }
607
+ });
608
+ const collectInBody = (bodyNode) => {
609
+ if (!bodyNode)
610
+ return;
611
+ if (bodyNode.type === 'Identifier') {
612
+ const id = bodyNode;
613
+ if (!callbackParams.has(id.name)) {
614
+ usedVars.add(id.name);
615
+ }
616
+ }
617
+ else if (typeof bodyNode === 'object') {
618
+ Object.values(bodyNode).forEach(child => {
619
+ if (child && typeof child === 'object') {
620
+ if (Array.isArray(child)) {
621
+ child.forEach(c => collectInBody(c));
622
+ }
623
+ else {
624
+ collectInBody(child);
625
+ }
626
+ }
627
+ });
628
+ }
629
+ };
630
+ collectInBody(func.body);
631
+ return;
632
+ }
633
+ // Recurse into child nodes
634
+ if (typeof node === 'object') {
635
+ Object.entries(node).forEach(([key, child]) => {
636
+ if (key === 'loc' || key === 'start' || key === 'end' || key === 'type')
637
+ return;
638
+ if (child && typeof child === 'object') {
639
+ if (Array.isArray(child)) {
640
+ child.forEach(c => collectIdentifiers(c, node.type, false));
641
+ }
642
+ else {
643
+ collectIdentifiers(child, node.type, false);
644
+ }
645
+ }
646
+ });
647
+ }
648
+ };
649
+ collectIdentifiers(callback);
650
+ // Check for stale closures
651
+ if (deps !== null && deps.length >= 0) { // Has deps array
652
+ const depsSet = new Set(deps);
653
+ // Hooks that don't cause stale closure issues
654
+ const safeHooks = ['useState', 'useReducer', 'useRef', 'useCallback', 'useMemo',
655
+ 'useEffect', 'useLayoutEffect', 'useContext'];
656
+ const setterPrefixes = ['set'];
657
+ for (const used of usedVars) {
658
+ // Skip safe identifiers
659
+ if (safeHooks.includes(used))
660
+ continue;
661
+ if (setterPrefixes.some(p => used.startsWith(p)))
662
+ continue;
663
+ if (depsSet.has(used))
664
+ continue;
665
+ if (used === 'console' || used === 'window' || used === 'document')
666
+ continue;
667
+ if (used === 'Math' || used === 'JSON' || used === 'Date')
668
+ continue;
669
+ if (used === 'undefined' || used === 'null' || used === 'true' || used === 'false')
670
+ continue;
671
+ // AC-2: Skip imported identifiers (stable references)
672
+ if (importedIdentifiers.has(used))
673
+ continue;
674
+ // This variable is used but not in deps - potential stale closure
675
+ const issue = {
676
+ id: `issue:stale-closure#${used}#${filePath}:${hookData.line}`,
677
+ type: 'issue:stale-closure',
678
+ file: filePath,
679
+ line: hookData.line,
680
+ variable: used,
681
+ hookType: hookData.hookName,
682
+ deps: deps,
683
+ message: `Variable '${used}' is used in ${hookData.hookName} but not listed in dependencies`
684
+ };
685
+ analysis.issues.push(issue);
686
+ }
687
+ }
688
+ // Check for missing cleanup
689
+ this.checkMissingCleanup(callback, filePath, analysis, hookData);
690
+ }
691
+ /**
692
+ * Check for missing cleanup in effect callback
693
+ */
694
+ checkMissingCleanup(callback, filePath, analysis, hookData) {
695
+ const hasCleanup = hookData.hasCleanup;
696
+ // Simple recursive AST walker
697
+ const checkNode = (node, parent = null) => {
698
+ if (!node || typeof node !== 'object')
699
+ return;
700
+ if (node.type === 'CallExpression') {
701
+ const callExpr = node;
702
+ const callee = callExpr.callee;
703
+ const loc = callExpr.loc;
704
+ // Check for timer APIs
705
+ if (callee?.type === 'Identifier') {
706
+ const api = callee.name;
707
+ if (api && BROWSER_APIS.timers.includes(api)) {
708
+ // Timer called without storing reference
709
+ if (!hasCleanup && api === 'requestAnimationFrame') {
710
+ analysis.issues.push({
711
+ id: `issue:raf-leak#${filePath}:${loc?.start?.line || 0}`,
712
+ type: 'issue:raf-leak',
713
+ file: filePath,
714
+ line: loc?.start?.line || 0,
715
+ message: `requestAnimationFrame called without cleanup - will leak on unmount`
716
+ });
717
+ }
718
+ }
719
+ }
720
+ }
721
+ if (node.type === 'NewExpression') {
722
+ const newExpr = node;
723
+ const callee = newExpr.callee;
724
+ const loc = newExpr.loc;
725
+ // Check for WebSocket without cleanup
726
+ if (callee?.type === 'Identifier' && callee.name === 'WebSocket') {
727
+ if (!hasCleanup) {
728
+ analysis.issues.push({
729
+ id: `issue:missing-cleanup#websocket#${filePath}:${loc?.start?.line || 0}`,
730
+ type: 'issue:missing-cleanup',
731
+ file: filePath,
732
+ line: loc?.start?.line || 0,
733
+ api: 'WebSocket',
734
+ message: `WebSocket created without cleanup - connection will leak on unmount`
735
+ });
736
+ }
737
+ }
738
+ // Check for observers without cleanup
739
+ if (callee?.type === 'Identifier' && callee.name && BROWSER_APIS.observers.includes(callee.name)) {
740
+ if (!hasCleanup) {
741
+ analysis.issues.push({
742
+ id: `issue:missing-cleanup#${callee.name}#${filePath}:${loc?.start?.line || 0}`,
743
+ type: 'issue:missing-cleanup',
744
+ file: filePath,
745
+ line: loc?.start?.line || 0,
746
+ api: callee.name,
747
+ message: `${callee.name} created without disconnect in cleanup`
748
+ });
749
+ }
750
+ }
751
+ }
752
+ // Recurse into child nodes
753
+ Object.entries(node).forEach(([key, child]) => {
754
+ if (key === 'loc' || key === 'start' || key === 'end' || key === 'type')
755
+ return;
756
+ if (child && typeof child === 'object') {
757
+ if (Array.isArray(child)) {
758
+ child.forEach(c => checkNode(c, node));
759
+ }
760
+ else {
761
+ checkNode(child, node);
762
+ }
763
+ }
764
+ });
765
+ };
766
+ checkNode(callback);
767
+ }
768
+ /**
769
+ * Analyze JSX element for component rendering
770
+ */
771
+ analyzeJSXElement(path, filePath, analysis) {
772
+ const openingElement = path.node.openingElement;
773
+ const elementName = this.getJSXElementName(openingElement.name);
774
+ // Skip native HTML elements (lowercase)
775
+ if (/^[a-z]/.test(elementName)) {
776
+ return;
777
+ }
778
+ // This is a React component being rendered
779
+ const loc = openingElement.loc;
780
+ // Find parent component
781
+ let parentComponent = null;
782
+ let parentPath = path.parentPath;
783
+ while (parentPath) {
784
+ if (parentPath.node.type === 'FunctionDeclaration' ||
785
+ parentPath.node.type === 'ArrowFunctionExpression' ||
786
+ parentPath.node.type === 'FunctionExpression') {
787
+ // Check if this function is a component
788
+ const funcName = this.getFunctionName(parentPath);
789
+ if (funcName && /^[A-Z]/.test(funcName)) {
790
+ parentComponent = funcName;
791
+ break;
792
+ }
793
+ }
794
+ parentPath = parentPath.parentPath;
795
+ }
796
+ if (parentComponent) {
797
+ analysis.edges.push({
798
+ edgeType: 'RENDERS',
799
+ src: `react:component#${parentComponent}`,
800
+ dst: `react:component#${elementName}`,
801
+ file: filePath,
802
+ line: loc.start.line
803
+ });
804
+ }
805
+ }
806
+ getJSXElementName(nameNode) {
807
+ if (nameNode.type === 'JSXIdentifier') {
808
+ return nameNode.name;
809
+ }
810
+ if (nameNode.type === 'JSXMemberExpression') {
811
+ const memExpr = nameNode;
812
+ return `${this.getJSXElementName(memExpr.object)}.${memExpr.property.name}`;
813
+ }
814
+ return '<unknown>';
815
+ }
816
+ getFunctionName(path) {
817
+ // Arrow function assigned to variable
818
+ const parent = path.parent;
819
+ if (parent?.type === 'VariableDeclarator') {
820
+ return parent.id?.name || null;
821
+ }
822
+ // Function declaration
823
+ const node = path.node;
824
+ if (node.id?.name) {
825
+ return node.id.name;
826
+ }
827
+ return null;
828
+ }
829
+ /**
830
+ * Analyze JSX attribute for props and event handlers
831
+ */
832
+ analyzeJSXAttribute(path, filePath, analysis) {
833
+ const attr = path.node;
834
+ if (!attr.name || attr.name.type !== 'JSXIdentifier')
835
+ return;
836
+ const attrName = attr.name.name;
837
+ const loc = attr.loc;
838
+ // Get parent JSX element info first
839
+ const jsxOpeningElement = path.parent;
840
+ let componentName = null;
841
+ let isReactComponent = false;
842
+ if (jsxOpeningElement?.type === 'JSXOpeningElement' && jsxOpeningElement.name) {
843
+ componentName = this.getJSXElementName(jsxOpeningElement.name);
844
+ isReactComponent = /^[A-Z]/.test(componentName);
845
+ }
846
+ // Check if it's an event handler
847
+ if (REACT_EVENTS[attrName]) {
848
+ const eventType = REACT_EVENTS[attrName];
849
+ const handler = attr.value;
850
+ let handlerName = '<inline>';
851
+ if (handler?.type === 'JSXExpressionContainer') {
852
+ const expr = handler.expression;
853
+ if (expr?.type === 'Identifier') {
854
+ handlerName = expr.name;
855
+ }
856
+ else if (expr?.type === 'MemberExpression') {
857
+ handlerName = this.getMemberExpressionName(expr);
858
+ }
859
+ }
860
+ const event = {
861
+ id: `dom:event#${eventType}#${filePath}:${loc.start.line}`,
862
+ type: 'dom:event',
863
+ eventType,
864
+ reactProp: attrName,
865
+ handler: handlerName,
866
+ file: filePath,
867
+ line: loc.start.line
868
+ };
869
+ analysis.events.push(event);
870
+ }
871
+ // For React components (uppercase), create PASSES_PROP edges for all props
872
+ if (isReactComponent && componentName && attrName !== 'key' && attrName !== 'ref' && attrName !== 'children') {
873
+ let propValue = '<expression>';
874
+ const value = attr.value;
875
+ if (value?.type === 'StringLiteral') {
876
+ propValue = value.value || '';
877
+ }
878
+ else if (value?.type === 'JSXExpressionContainer') {
879
+ propValue = this.getExpressionValue(value.expression);
880
+ }
881
+ else if (value === null) {
882
+ propValue = 'true'; // Boolean shorthand
883
+ }
884
+ // Find parent component
885
+ let parentComponent = null;
886
+ let parentPath = path.parentPath;
887
+ while (parentPath) {
888
+ const node = parentPath.node;
889
+ if (node.type === 'FunctionDeclaration' && node.id?.name) {
890
+ if (/^[A-Z]/.test(node.id.name)) {
891
+ parentComponent = node.id.name;
892
+ break;
893
+ }
894
+ }
895
+ else if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionExpression') {
896
+ const funcName = this.getFunctionName(parentPath);
897
+ if (funcName && /^[A-Z]/.test(funcName)) {
898
+ parentComponent = funcName;
899
+ break;
900
+ }
901
+ }
902
+ parentPath = parentPath.parentPath;
903
+ }
904
+ if (parentComponent) {
905
+ analysis.edges.push({
906
+ edgeType: 'PASSES_PROP',
907
+ src: `react:component#${parentComponent}`,
908
+ dst: `react:component#${componentName}`,
909
+ propName: attrName,
910
+ propValue,
911
+ file: filePath,
912
+ line: loc.start.line
913
+ });
914
+ }
915
+ }
916
+ }
917
+ /**
918
+ * Analyze forwardRef usage
919
+ */
920
+ analyzeForwardRef(path, filePath, analysis) {
921
+ const loc = path.node.loc;
922
+ const parent = path.parent;
923
+ const componentName = parent.type === 'VariableDeclarator' ? parent.id?.name : null;
924
+ if (componentName) {
925
+ analysis.components.push({
926
+ id: `react:component#${componentName}#${filePath}:${loc.start.line}`,
927
+ type: 'react:component',
928
+ name: componentName,
929
+ file: filePath,
930
+ line: loc.start.line,
931
+ column: loc.start.column,
932
+ kind: 'forwardRef'
933
+ });
934
+ }
935
+ }
936
+ /**
937
+ * Analyze createContext usage
938
+ */
939
+ analyzeCreateContext(path, filePath, analysis) {
940
+ const loc = path.node.loc;
941
+ const parent = path.parent;
942
+ const contextName = parent.type === 'VariableDeclarator' ? parent.id?.name : null;
943
+ if (contextName) {
944
+ const defaultValue = path.node.arguments[0];
945
+ analysis.hooks.push({
946
+ id: `react:context#${contextName}#${filePath}:${loc.start.line}`,
947
+ type: 'react:context',
948
+ contextName,
949
+ file: filePath,
950
+ line: loc.start.line,
951
+ column: loc.start.column,
952
+ hookName: 'createContext',
953
+ defaultValue: this.getExpressionValue(defaultValue)
954
+ });
955
+ }
956
+ }
957
+ /**
958
+ * Analyze browser API calls
959
+ */
960
+ analyzeBrowserAPI(path, filePath, analysis) {
961
+ const callee = path.node.callee;
962
+ const loc = path.node.loc;
963
+ // Direct function call: setTimeout, fetch, alert
964
+ if (callee.type === 'Identifier') {
965
+ const name = callee.name;
966
+ // Timers
967
+ if (BROWSER_APIS.timers.includes(name)) {
968
+ analysis.browserAPIs.push({
969
+ id: `browser:timer#${name}#${filePath}:${loc.start.line}`,
970
+ type: 'browser:timer',
971
+ api: name,
972
+ file: filePath,
973
+ line: loc.start.line
974
+ });
975
+ return;
976
+ }
977
+ // Blocking APIs
978
+ if (BROWSER_APIS.blocking.includes(name)) {
979
+ analysis.browserAPIs.push({
980
+ id: `browser:blocking#${name}#${filePath}:${loc.start.line}`,
981
+ type: 'browser:blocking',
982
+ api: name,
983
+ file: filePath,
984
+ line: loc.start.line
985
+ });
986
+ return;
987
+ }
988
+ // Fetch
989
+ if (name === 'fetch') {
990
+ analysis.browserAPIs.push({
991
+ id: `browser:async#fetch#${filePath}:${loc.start.line}`,
992
+ type: 'browser:async',
993
+ api: 'fetch',
994
+ file: filePath,
995
+ line: loc.start.line
996
+ });
997
+ return;
998
+ }
999
+ }
1000
+ // Member expression: localStorage.setItem, document.querySelector
1001
+ if (callee.type === 'MemberExpression') {
1002
+ const fullName = this.getMemberExpressionName(callee);
1003
+ // localStorage/sessionStorage
1004
+ if (fullName.startsWith('localStorage.') || fullName.startsWith('sessionStorage.')) {
1005
+ const [storage, method] = fullName.split('.');
1006
+ const operation = method === 'getItem' ? 'read' :
1007
+ method === 'setItem' ? 'write' :
1008
+ method === 'removeItem' ? 'delete' : method;
1009
+ analysis.browserAPIs.push({
1010
+ id: `browser:storage#${storage}:${operation}#${filePath}:${loc.start.line}`,
1011
+ type: 'browser:storage',
1012
+ storage,
1013
+ operation,
1014
+ file: filePath,
1015
+ line: loc.start.line
1016
+ });
1017
+ return;
1018
+ }
1019
+ // DOM queries
1020
+ if (fullName.startsWith('document.') &&
1021
+ (fullName.includes('querySelector') || fullName.includes('getElementById'))) {
1022
+ analysis.browserAPIs.push({
1023
+ id: `browser:dom#query#${filePath}:${loc.start.line}`,
1024
+ type: 'browser:dom',
1025
+ operation: 'query',
1026
+ api: fullName,
1027
+ file: filePath,
1028
+ line: loc.start.line
1029
+ });
1030
+ return;
1031
+ }
1032
+ // History API
1033
+ if (fullName.startsWith('history.') || fullName.startsWith('window.history.')) {
1034
+ analysis.browserAPIs.push({
1035
+ id: `browser:history#${filePath}:${loc.start.line}`,
1036
+ type: 'browser:history',
1037
+ api: fullName,
1038
+ file: filePath,
1039
+ line: loc.start.line
1040
+ });
1041
+ return;
1042
+ }
1043
+ // Clipboard API
1044
+ if (fullName.includes('clipboard')) {
1045
+ analysis.browserAPIs.push({
1046
+ id: `browser:clipboard#${filePath}:${loc.start.line}`,
1047
+ type: 'browser:clipboard',
1048
+ api: fullName,
1049
+ file: filePath,
1050
+ line: loc.start.line
1051
+ });
1052
+ return;
1053
+ }
1054
+ // Geolocation
1055
+ if (fullName.includes('geolocation')) {
1056
+ analysis.browserAPIs.push({
1057
+ id: `browser:geolocation#${filePath}:${loc.start.line}`,
1058
+ type: 'browser:geolocation',
1059
+ api: fullName,
1060
+ file: filePath,
1061
+ line: loc.start.line
1062
+ });
1063
+ return;
1064
+ }
1065
+ // Canvas context
1066
+ if (fullName.match(/\.(fillRect|strokeRect|fillText|strokeText|beginPath|closePath|moveTo|lineTo|arc|fill|stroke|clearRect|drawImage|save|restore|translate|rotate|scale)$/)) {
1067
+ const method = fullName.split('.').pop();
1068
+ analysis.browserAPIs.push({
1069
+ id: `canvas:draw#${method}#${filePath}:${loc.start.line}`,
1070
+ type: 'canvas:draw',
1071
+ method,
1072
+ file: filePath,
1073
+ line: loc.start.line
1074
+ });
1075
+ return;
1076
+ }
1077
+ // matchMedia
1078
+ if (fullName === 'window.matchMedia' || fullName === 'matchMedia') {
1079
+ analysis.browserAPIs.push({
1080
+ id: `browser:media-query#${filePath}:${loc.start.line}`,
1081
+ type: 'browser:media-query',
1082
+ api: 'matchMedia',
1083
+ file: filePath,
1084
+ line: loc.start.line
1085
+ });
1086
+ return;
1087
+ }
1088
+ }
1089
+ }
1090
+ getExpressionValue(expr) {
1091
+ if (!expr)
1092
+ return 'undefined';
1093
+ if (expr.type === 'StringLiteral')
1094
+ return `"${expr.value}"`;
1095
+ if (expr.type === 'NumericLiteral')
1096
+ return String(expr.value);
1097
+ if (expr.type === 'BooleanLiteral')
1098
+ return String(expr.value);
1099
+ if (expr.type === 'NullLiteral')
1100
+ return 'null';
1101
+ if (expr.type === 'Identifier')
1102
+ return expr.name;
1103
+ if (expr.type === 'ObjectExpression')
1104
+ return '{...}';
1105
+ if (expr.type === 'ArrayExpression')
1106
+ return '[...]';
1107
+ if (expr.type === 'ArrowFunctionExpression')
1108
+ return '() => {...}';
1109
+ if (expr.type === 'FunctionExpression')
1110
+ return 'function() {...}';
1111
+ return '<expression>';
1112
+ }
1113
+ /**
1114
+ * Add all analysis results to graph
1115
+ */
1116
+ async addToGraph(analysis, graph, moduleId) {
1117
+ // Add component nodes
1118
+ for (const component of analysis.components) {
1119
+ await graph.addNode(component);
1120
+ if (moduleId) {
1121
+ await graph.addEdge({
1122
+ type: 'DEFINES',
1123
+ src: moduleId,
1124
+ dst: component.id
1125
+ });
1126
+ }
1127
+ }
1128
+ // Add hook nodes
1129
+ for (const hook of analysis.hooks) {
1130
+ await graph.addNode(hook);
1131
+ }
1132
+ // Add event nodes
1133
+ for (const event of analysis.events) {
1134
+ await graph.addNode(event);
1135
+ }
1136
+ // Add browser API nodes
1137
+ for (const api of analysis.browserAPIs) {
1138
+ await graph.addNode(api);
1139
+ }
1140
+ // Add issue nodes
1141
+ for (const issue of analysis.issues) {
1142
+ await graph.addNode(issue);
1143
+ }
1144
+ // Add edges
1145
+ for (const edge of analysis.edges) {
1146
+ const { edgeType, ...rest } = edge;
1147
+ await graph.addEdge({
1148
+ type: edgeType,
1149
+ ...rest
1150
+ });
1151
+ }
1152
+ }
1153
+ }