@grafema/core 0.1.1-alpha → 0.2.1-beta

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 (319) hide show
  1. package/dist/Orchestrator.d.ts +7 -0
  2. package/dist/Orchestrator.d.ts.map +1 -1
  3. package/dist/Orchestrator.js +25 -3
  4. package/dist/config/ConfigLoader.d.ts +18 -0
  5. package/dist/config/ConfigLoader.d.ts.map +1 -1
  6. package/dist/config/ConfigLoader.js +65 -3
  7. package/dist/core/FileExplainer.d.ts +101 -0
  8. package/dist/core/FileExplainer.d.ts.map +1 -0
  9. package/dist/core/FileExplainer.js +139 -0
  10. package/dist/core/NodeFactory.d.ts +44 -5
  11. package/dist/core/NodeFactory.d.ts.map +1 -1
  12. package/dist/core/NodeFactory.js +52 -7
  13. package/dist/core/nodes/ArrayLiteralNode.d.ts.map +1 -1
  14. package/dist/core/nodes/ArrayLiteralNode.js +4 -2
  15. package/dist/core/nodes/BranchNode.d.ts +41 -0
  16. package/dist/core/nodes/BranchNode.d.ts.map +1 -0
  17. package/dist/core/nodes/BranchNode.js +82 -0
  18. package/dist/core/nodes/CallSiteNode.d.ts +2 -2
  19. package/dist/core/nodes/CallSiteNode.d.ts.map +1 -1
  20. package/dist/core/nodes/CallSiteNode.js +9 -5
  21. package/dist/core/nodes/CaseNode.d.ts +43 -0
  22. package/dist/core/nodes/CaseNode.d.ts.map +1 -0
  23. package/dist/core/nodes/CaseNode.js +81 -0
  24. package/dist/core/nodes/ClassNode.d.ts +2 -2
  25. package/dist/core/nodes/ClassNode.d.ts.map +1 -1
  26. package/dist/core/nodes/ClassNode.js +8 -4
  27. package/dist/core/nodes/ConstantNode.d.ts +2 -2
  28. package/dist/core/nodes/ConstantNode.d.ts.map +1 -1
  29. package/dist/core/nodes/ConstantNode.js +6 -4
  30. package/dist/core/nodes/ConstructorCallNode.d.ts +51 -0
  31. package/dist/core/nodes/ConstructorCallNode.d.ts.map +1 -0
  32. package/dist/core/nodes/ConstructorCallNode.js +171 -0
  33. package/dist/core/nodes/DatabaseQueryNode.d.ts +3 -2
  34. package/dist/core/nodes/DatabaseQueryNode.d.ts.map +1 -1
  35. package/dist/core/nodes/DatabaseQueryNode.js +5 -2
  36. package/dist/core/nodes/DecoratorNode.d.ts +2 -2
  37. package/dist/core/nodes/DecoratorNode.d.ts.map +1 -1
  38. package/dist/core/nodes/DecoratorNode.js +5 -3
  39. package/dist/core/nodes/EnumNode.d.ts +2 -2
  40. package/dist/core/nodes/EnumNode.d.ts.map +1 -1
  41. package/dist/core/nodes/EnumNode.js +5 -3
  42. package/dist/core/nodes/EventListenerNode.d.ts +4 -4
  43. package/dist/core/nodes/EventListenerNode.d.ts.map +1 -1
  44. package/dist/core/nodes/EventListenerNode.js +7 -4
  45. package/dist/core/nodes/ExportNode.d.ts +2 -2
  46. package/dist/core/nodes/ExportNode.d.ts.map +1 -1
  47. package/dist/core/nodes/ExportNode.js +8 -4
  48. package/dist/core/nodes/ExpressionNode.d.ts +2 -2
  49. package/dist/core/nodes/ExpressionNode.d.ts.map +1 -1
  50. package/dist/core/nodes/ExpressionNode.js +6 -4
  51. package/dist/core/nodes/ExternalModuleNode.d.ts +4 -0
  52. package/dist/core/nodes/ExternalModuleNode.d.ts.map +1 -1
  53. package/dist/core/nodes/ExternalModuleNode.js +10 -2
  54. package/dist/core/nodes/HttpRequestNode.d.ts +4 -4
  55. package/dist/core/nodes/HttpRequestNode.d.ts.map +1 -1
  56. package/dist/core/nodes/HttpRequestNode.js +7 -4
  57. package/dist/core/nodes/ImportNode.d.ts +10 -2
  58. package/dist/core/nodes/ImportNode.d.ts.map +1 -1
  59. package/dist/core/nodes/ImportNode.js +21 -4
  60. package/dist/core/nodes/InterfaceNode.d.ts +2 -2
  61. package/dist/core/nodes/InterfaceNode.d.ts.map +1 -1
  62. package/dist/core/nodes/InterfaceNode.js +5 -3
  63. package/dist/core/nodes/LiteralNode.d.ts +2 -2
  64. package/dist/core/nodes/LiteralNode.d.ts.map +1 -1
  65. package/dist/core/nodes/LiteralNode.js +6 -4
  66. package/dist/core/nodes/MethodCallNode.d.ts +2 -2
  67. package/dist/core/nodes/MethodCallNode.d.ts.map +1 -1
  68. package/dist/core/nodes/MethodCallNode.js +9 -5
  69. package/dist/core/nodes/MethodNode.d.ts +2 -2
  70. package/dist/core/nodes/MethodNode.d.ts.map +1 -1
  71. package/dist/core/nodes/MethodNode.js +8 -4
  72. package/dist/core/nodes/ObjectLiteralNode.d.ts.map +1 -1
  73. package/dist/core/nodes/ObjectLiteralNode.js +4 -2
  74. package/dist/core/nodes/ParameterNode.d.ts +2 -2
  75. package/dist/core/nodes/ParameterNode.d.ts.map +1 -1
  76. package/dist/core/nodes/ParameterNode.js +5 -3
  77. package/dist/core/nodes/TypeNode.d.ts +2 -2
  78. package/dist/core/nodes/TypeNode.d.ts.map +1 -1
  79. package/dist/core/nodes/TypeNode.js +5 -3
  80. package/dist/core/nodes/VariableDeclarationNode.d.ts +2 -2
  81. package/dist/core/nodes/VariableDeclarationNode.d.ts.map +1 -1
  82. package/dist/core/nodes/VariableDeclarationNode.js +9 -5
  83. package/dist/core/nodes/index.d.ts +3 -0
  84. package/dist/core/nodes/index.d.ts.map +1 -1
  85. package/dist/core/nodes/index.js +3 -0
  86. package/dist/data/builtins/BuiltinRegistry.d.ts +78 -0
  87. package/dist/data/builtins/BuiltinRegistry.d.ts.map +1 -0
  88. package/dist/data/builtins/BuiltinRegistry.js +110 -0
  89. package/dist/data/builtins/definitions.d.ts +28 -0
  90. package/dist/data/builtins/definitions.d.ts.map +1 -0
  91. package/dist/data/builtins/definitions.js +250 -0
  92. package/dist/data/builtins/index.d.ts +10 -0
  93. package/dist/data/builtins/index.d.ts.map +1 -0
  94. package/dist/data/builtins/index.js +8 -0
  95. package/dist/data/builtins/jsGlobals.d.ts +18 -0
  96. package/dist/data/builtins/jsGlobals.d.ts.map +1 -0
  97. package/dist/data/builtins/jsGlobals.js +26 -0
  98. package/dist/data/builtins/types.d.ts +34 -0
  99. package/dist/data/builtins/types.d.ts.map +1 -0
  100. package/dist/data/builtins/types.js +7 -0
  101. package/dist/data/globals/definitions.d.ts +27 -0
  102. package/dist/data/globals/definitions.d.ts.map +1 -0
  103. package/dist/data/globals/definitions.js +117 -0
  104. package/dist/data/globals/index.d.ts +36 -0
  105. package/dist/data/globals/index.d.ts.map +1 -0
  106. package/dist/data/globals/index.js +52 -0
  107. package/dist/diagnostics/DiagnosticReporter.d.ts +23 -0
  108. package/dist/diagnostics/DiagnosticReporter.d.ts.map +1 -1
  109. package/dist/diagnostics/DiagnosticReporter.js +88 -0
  110. package/dist/diagnostics/index.d.ts +1 -1
  111. package/dist/diagnostics/index.d.ts.map +1 -1
  112. package/dist/errors/GrafemaError.d.ts +43 -0
  113. package/dist/errors/GrafemaError.d.ts.map +1 -1
  114. package/dist/errors/GrafemaError.js +50 -0
  115. package/dist/index.d.ts +17 -1
  116. package/dist/index.d.ts.map +1 -1
  117. package/dist/index.js +17 -1
  118. package/dist/plugins/analysis/DatabaseAnalyzer.d.ts.map +1 -1
  119. package/dist/plugins/analysis/DatabaseAnalyzer.js +3 -2
  120. package/dist/plugins/analysis/ExpressAnalyzer.d.ts.map +1 -1
  121. package/dist/plugins/analysis/ExpressAnalyzer.js +3 -1
  122. package/dist/plugins/analysis/ExpressResponseAnalyzer.d.ts +148 -0
  123. package/dist/plugins/analysis/ExpressResponseAnalyzer.d.ts.map +1 -0
  124. package/dist/plugins/analysis/ExpressResponseAnalyzer.js +495 -0
  125. package/dist/plugins/analysis/ExpressRouteAnalyzer.d.ts.map +1 -1
  126. package/dist/plugins/analysis/ExpressRouteAnalyzer.js +53 -18
  127. package/dist/plugins/analysis/FetchAnalyzer.d.ts +40 -0
  128. package/dist/plugins/analysis/FetchAnalyzer.d.ts.map +1 -1
  129. package/dist/plugins/analysis/FetchAnalyzer.js +163 -15
  130. package/dist/plugins/analysis/JSASTAnalyzer.d.ts +157 -26
  131. package/dist/plugins/analysis/JSASTAnalyzer.d.ts.map +1 -1
  132. package/dist/plugins/analysis/JSASTAnalyzer.js +2418 -191
  133. package/dist/plugins/analysis/RustAnalyzer.js +4 -4
  134. package/dist/plugins/analysis/SQLiteAnalyzer.d.ts.map +1 -1
  135. package/dist/plugins/analysis/SQLiteAnalyzer.js +4 -2
  136. package/dist/plugins/analysis/SocketIOAnalyzer.d.ts +9 -0
  137. package/dist/plugins/analysis/SocketIOAnalyzer.d.ts.map +1 -1
  138. package/dist/plugins/analysis/SocketIOAnalyzer.js +91 -7
  139. package/dist/plugins/analysis/ast/GraphBuilder.d.ts +173 -0
  140. package/dist/plugins/analysis/ast/GraphBuilder.d.ts.map +1 -1
  141. package/dist/plugins/analysis/ast/GraphBuilder.js +1256 -65
  142. package/dist/plugins/analysis/ast/types.d.ts +294 -0
  143. package/dist/plugins/analysis/ast/types.d.ts.map +1 -1
  144. package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts +5 -1
  145. package/dist/plugins/analysis/ast/visitors/ASTVisitor.d.ts.map +1 -1
  146. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts +1 -0
  147. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.d.ts.map +1 -1
  148. package/dist/plugins/analysis/ast/visitors/CallExpressionVisitor.js +12 -1
  149. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts +10 -0
  150. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.d.ts.map +1 -1
  151. package/dist/plugins/analysis/ast/visitors/FunctionVisitor.js +62 -0
  152. package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.d.ts +4 -0
  153. package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.d.ts.map +1 -1
  154. package/dist/plugins/analysis/ast/visitors/ImportExportVisitor.js +101 -0
  155. package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts +16 -1
  156. package/dist/plugins/analysis/ast/visitors/VariableVisitor.d.ts.map +1 -1
  157. package/dist/plugins/analysis/ast/visitors/VariableVisitor.js +233 -39
  158. package/dist/plugins/discovery/WorkspaceDiscovery.d.ts.map +1 -1
  159. package/dist/plugins/discovery/WorkspaceDiscovery.js +9 -4
  160. package/dist/plugins/enrichment/AliasTracker.d.ts.map +1 -1
  161. package/dist/plugins/enrichment/AliasTracker.js +16 -1
  162. package/dist/plugins/enrichment/ArgumentParameterLinker.d.ts +32 -0
  163. package/dist/plugins/enrichment/ArgumentParameterLinker.d.ts.map +1 -0
  164. package/dist/plugins/enrichment/ArgumentParameterLinker.js +175 -0
  165. package/dist/plugins/enrichment/ClosureCaptureEnricher.d.ts +51 -0
  166. package/dist/plugins/enrichment/ClosureCaptureEnricher.d.ts.map +1 -0
  167. package/dist/plugins/enrichment/ClosureCaptureEnricher.js +205 -0
  168. package/dist/plugins/enrichment/ExternalCallResolver.d.ts +42 -0
  169. package/dist/plugins/enrichment/ExternalCallResolver.d.ts.map +1 -0
  170. package/dist/plugins/enrichment/ExternalCallResolver.js +213 -0
  171. package/dist/plugins/enrichment/FunctionCallResolver.d.ts +58 -0
  172. package/dist/plugins/enrichment/FunctionCallResolver.d.ts.map +1 -0
  173. package/dist/plugins/enrichment/FunctionCallResolver.js +340 -0
  174. package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts +16 -3
  175. package/dist/plugins/enrichment/HTTPConnectionEnricher.d.ts.map +1 -1
  176. package/dist/plugins/enrichment/HTTPConnectionEnricher.js +64 -20
  177. package/dist/plugins/enrichment/MethodCallResolver.d.ts.map +1 -1
  178. package/dist/plugins/enrichment/MethodCallResolver.js +15 -1
  179. package/dist/plugins/enrichment/MountPointResolver.d.ts +14 -12
  180. package/dist/plugins/enrichment/MountPointResolver.d.ts.map +1 -1
  181. package/dist/plugins/enrichment/MountPointResolver.js +172 -151
  182. package/dist/plugins/enrichment/NodejsBuiltinsResolver.d.ts +44 -0
  183. package/dist/plugins/enrichment/NodejsBuiltinsResolver.d.ts.map +1 -0
  184. package/dist/plugins/enrichment/NodejsBuiltinsResolver.js +271 -0
  185. package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts +5 -27
  186. package/dist/plugins/enrichment/ValueDomainAnalyzer.d.ts.map +1 -1
  187. package/dist/plugins/enrichment/ValueDomainAnalyzer.js +62 -139
  188. package/dist/plugins/indexing/JSModuleIndexer.d.ts +15 -0
  189. package/dist/plugins/indexing/JSModuleIndexer.d.ts.map +1 -1
  190. package/dist/plugins/indexing/JSModuleIndexer.js +58 -0
  191. package/dist/plugins/indexing/RustModuleIndexer.d.ts +1 -1
  192. package/dist/plugins/indexing/RustModuleIndexer.js +4 -4
  193. package/dist/plugins/validation/BrokenImportValidator.d.ts +31 -0
  194. package/dist/plugins/validation/BrokenImportValidator.d.ts.map +1 -0
  195. package/dist/plugins/validation/BrokenImportValidator.js +249 -0
  196. package/dist/plugins/validation/CallResolverValidator.d.ts +21 -10
  197. package/dist/plugins/validation/CallResolverValidator.d.ts.map +1 -1
  198. package/dist/plugins/validation/CallResolverValidator.js +101 -76
  199. package/dist/plugins/validation/DataFlowValidator.d.ts.map +1 -1
  200. package/dist/plugins/validation/DataFlowValidator.js +49 -41
  201. package/dist/plugins/validation/GraphConnectivityValidator.d.ts.map +1 -1
  202. package/dist/plugins/validation/GraphConnectivityValidator.js +25 -1
  203. package/dist/plugins/validation/SQLInjectionValidator.d.ts.map +1 -1
  204. package/dist/plugins/validation/SQLInjectionValidator.js +2 -3
  205. package/dist/queries/findCallsInFunction.d.ts +52 -0
  206. package/dist/queries/findCallsInFunction.d.ts.map +1 -0
  207. package/dist/queries/findCallsInFunction.js +135 -0
  208. package/dist/queries/findContainingFunction.d.ts +45 -0
  209. package/dist/queries/findContainingFunction.d.ts.map +1 -0
  210. package/dist/queries/findContainingFunction.js +54 -0
  211. package/dist/queries/index.d.ts +14 -0
  212. package/dist/queries/index.d.ts.map +1 -0
  213. package/dist/queries/index.js +11 -0
  214. package/dist/queries/traceValues.d.ts +70 -0
  215. package/dist/queries/traceValues.d.ts.map +1 -0
  216. package/dist/queries/traceValues.js +299 -0
  217. package/dist/queries/types.d.ts +163 -0
  218. package/dist/queries/types.d.ts.map +1 -0
  219. package/dist/queries/types.js +9 -0
  220. package/dist/schema/GraphSchemaExtractor.d.ts +53 -0
  221. package/dist/schema/GraphSchemaExtractor.d.ts.map +1 -0
  222. package/dist/schema/GraphSchemaExtractor.js +124 -0
  223. package/dist/schema/InterfaceSchemaExtractor.d.ts +73 -0
  224. package/dist/schema/InterfaceSchemaExtractor.d.ts.map +1 -0
  225. package/dist/schema/InterfaceSchemaExtractor.js +112 -0
  226. package/dist/schema/index.d.ts +5 -0
  227. package/dist/schema/index.d.ts.map +1 -0
  228. package/dist/schema/index.js +2 -0
  229. package/dist/storage/backends/RFDBServerBackend.d.ts +13 -18
  230. package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -1
  231. package/dist/storage/backends/RFDBServerBackend.js +47 -51
  232. package/dist/storage/backends/typeValidation.d.ts.map +1 -1
  233. package/dist/storage/backends/typeValidation.js +1 -0
  234. package/package.json +3 -3
  235. package/src/Orchestrator.ts +35 -3
  236. package/src/config/ConfigLoader.ts +94 -3
  237. package/src/core/FileExplainer.ts +179 -0
  238. package/src/core/NodeFactory.ts +72 -8
  239. package/src/core/nodes/ArrayLiteralNode.ts +3 -2
  240. package/src/core/nodes/BranchNode.ts +113 -0
  241. package/src/core/nodes/CallSiteNode.ts +7 -5
  242. package/src/core/nodes/CaseNode.ts +123 -0
  243. package/src/core/nodes/ClassNode.ts +6 -4
  244. package/src/core/nodes/ConstantNode.ts +5 -4
  245. package/src/core/nodes/ConstructorCallNode.ts +217 -0
  246. package/src/core/nodes/DatabaseQueryNode.ts +5 -1
  247. package/src/core/nodes/DecoratorNode.ts +4 -3
  248. package/src/core/nodes/EnumNode.ts +4 -3
  249. package/src/core/nodes/EventListenerNode.ts +7 -4
  250. package/src/core/nodes/ExportNode.ts +6 -4
  251. package/src/core/nodes/ExpressionNode.ts +5 -4
  252. package/src/core/nodes/ExternalModuleNode.ts +11 -2
  253. package/src/core/nodes/HttpRequestNode.ts +7 -4
  254. package/src/core/nodes/ImportNode.ts +31 -4
  255. package/src/core/nodes/InterfaceNode.ts +4 -3
  256. package/src/core/nodes/LiteralNode.ts +5 -4
  257. package/src/core/nodes/MethodCallNode.ts +7 -5
  258. package/src/core/nodes/MethodNode.ts +6 -4
  259. package/src/core/nodes/ObjectLiteralNode.ts +3 -2
  260. package/src/core/nodes/ParameterNode.ts +4 -3
  261. package/src/core/nodes/TypeNode.ts +4 -3
  262. package/src/core/nodes/VariableDeclarationNode.ts +7 -5
  263. package/src/core/nodes/index.ts +3 -0
  264. package/src/data/builtins/BuiltinRegistry.ts +124 -0
  265. package/src/data/builtins/definitions.ts +267 -0
  266. package/src/data/builtins/index.ts +10 -0
  267. package/src/data/builtins/jsGlobals.ts +28 -0
  268. package/src/data/builtins/types.ts +36 -0
  269. package/src/data/globals/definitions.ts +156 -0
  270. package/src/data/globals/index.ts +66 -0
  271. package/src/diagnostics/DiagnosticReporter.ts +120 -0
  272. package/src/diagnostics/index.ts +1 -1
  273. package/src/errors/GrafemaError.ts +65 -0
  274. package/src/index.ts +45 -0
  275. package/src/plugins/analysis/DatabaseAnalyzer.ts +4 -2
  276. package/src/plugins/analysis/ExpressAnalyzer.ts +5 -1
  277. package/src/plugins/analysis/ExpressResponseAnalyzer.ts +636 -0
  278. package/src/plugins/analysis/ExpressRouteAnalyzer.ts +57 -18
  279. package/src/plugins/analysis/FetchAnalyzer.ts +204 -16
  280. package/src/plugins/analysis/JSASTAnalyzer.ts +2958 -260
  281. package/src/plugins/analysis/RustAnalyzer.ts +4 -4
  282. package/src/plugins/analysis/SQLiteAnalyzer.ts +5 -2
  283. package/src/plugins/analysis/SocketIOAnalyzer.ts +121 -7
  284. package/src/plugins/analysis/ast/GraphBuilder.ts +1578 -70
  285. package/src/plugins/analysis/ast/types.ts +387 -0
  286. package/src/plugins/analysis/ast/visitors/ASTVisitor.ts +8 -0
  287. package/src/plugins/analysis/ast/visitors/CallExpressionVisitor.ts +16 -1
  288. package/src/plugins/analysis/ast/visitors/FunctionVisitor.ts +77 -2
  289. package/src/plugins/analysis/ast/visitors/ImportExportVisitor.ts +112 -1
  290. package/src/plugins/analysis/ast/visitors/VariableVisitor.ts +272 -47
  291. package/src/plugins/discovery/WorkspaceDiscovery.ts +11 -4
  292. package/src/plugins/enrichment/AliasTracker.ts +22 -1
  293. package/src/plugins/enrichment/ArgumentParameterLinker.ts +240 -0
  294. package/src/plugins/enrichment/ClosureCaptureEnricher.ts +267 -0
  295. package/src/plugins/enrichment/ExternalCallResolver.ts +262 -0
  296. package/src/plugins/enrichment/FunctionCallResolver.ts +456 -0
  297. package/src/plugins/enrichment/HTTPConnectionEnricher.ts +70 -20
  298. package/src/plugins/enrichment/MethodCallResolver.ts +21 -1
  299. package/src/plugins/enrichment/MountPointResolver.ts +206 -198
  300. package/src/plugins/enrichment/NodejsBuiltinsResolver.ts +365 -0
  301. package/src/plugins/enrichment/ValueDomainAnalyzer.ts +67 -184
  302. package/src/plugins/indexing/JSModuleIndexer.ts +66 -0
  303. package/src/plugins/indexing/RustModuleIndexer.ts +4 -4
  304. package/src/plugins/validation/BrokenImportValidator.ts +325 -0
  305. package/src/plugins/validation/CallResolverValidator.ts +129 -109
  306. package/src/plugins/validation/DataFlowValidator.ts +75 -58
  307. package/src/plugins/validation/GraphConnectivityValidator.ts +39 -1
  308. package/src/plugins/validation/SQLInjectionValidator.ts +2 -5
  309. package/src/queries/README.md +46 -0
  310. package/src/queries/findCallsInFunction.ts +206 -0
  311. package/src/queries/findContainingFunction.ts +83 -0
  312. package/src/queries/index.ts +23 -0
  313. package/src/queries/traceValues.ts +398 -0
  314. package/src/queries/types.ts +187 -0
  315. package/src/schema/GraphSchemaExtractor.ts +177 -0
  316. package/src/schema/InterfaceSchemaExtractor.ts +173 -0
  317. package/src/schema/index.ts +5 -0
  318. package/src/storage/backends/RFDBServerBackend.ts +64 -68
  319. package/src/storage/backends/typeValidation.ts +1 -0
@@ -0,0 +1,495 @@
1
+ /**
2
+ * ExpressResponseAnalyzer - detects Express response patterns
3
+ *
4
+ * For each http:route node:
5
+ * 1. Follow HANDLED_BY edge to get handler function
6
+ * 2. Traverse handler AST for res.json(...), res.send(...) patterns
7
+ * 3. Create RESPONDS_WITH edge from http:route to response argument node
8
+ *
9
+ * Patterns:
10
+ * - res.json({ data })
11
+ * - res.send(variable)
12
+ * - res.status(200).json(data)
13
+ */
14
+ import { readFileSync } from 'fs';
15
+ import { parse } from '@babel/parser';
16
+ import traverseModule from '@babel/traverse';
17
+ import { Plugin, createSuccessResult, createErrorResult } from '../Plugin.js';
18
+ import { getLine } from './ast/utils/location.js';
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ const traverse = traverseModule.default || traverseModule;
21
+ const RESPONSE_METHODS = ['json', 'send'];
22
+ export class ExpressResponseAnalyzer extends Plugin {
23
+ responseNodeCounter = 0;
24
+ get metadata() {
25
+ return {
26
+ name: 'ExpressResponseAnalyzer',
27
+ phase: 'ANALYSIS',
28
+ priority: 74, // After ExpressRouteAnalyzer (75)
29
+ creates: {
30
+ nodes: [],
31
+ edges: ['RESPONDS_WITH']
32
+ },
33
+ dependencies: ['ExpressRouteAnalyzer', 'JSASTAnalyzer']
34
+ };
35
+ }
36
+ async execute(context) {
37
+ const logger = this.log(context);
38
+ try {
39
+ const { graph } = context;
40
+ // Get all http:route nodes
41
+ const routes = [];
42
+ for await (const node of graph.queryNodes({ type: 'http:route' })) {
43
+ routes.push(node);
44
+ }
45
+ logger.info('Processing routes', { count: routes.length });
46
+ let edgesCreated = 0;
47
+ for (const route of routes) {
48
+ const result = await this.analyzeRouteResponses(route, graph);
49
+ edgesCreated += result;
50
+ }
51
+ logger.info('Analysis complete', { edgesCreated });
52
+ return createSuccessResult({ nodes: 0, edges: edgesCreated }, { routesAnalyzed: routes.length });
53
+ }
54
+ catch (error) {
55
+ logger.error('Analysis failed', { error });
56
+ return createErrorResult(error);
57
+ }
58
+ }
59
+ /**
60
+ * Analyze a single http:route for response patterns
61
+ */
62
+ async analyzeRouteResponses(route, graph) {
63
+ let edgesCreated = 0;
64
+ try {
65
+ // Get HANDLED_BY edges to find handler function
66
+ const handledByEdges = await graph.getOutgoingEdges(route.id, ['HANDLED_BY']);
67
+ if (handledByEdges.length === 0) {
68
+ return 0;
69
+ }
70
+ // Get handler function node
71
+ const handlerEdge = handledByEdges[0];
72
+ const handlerNode = await graph.getNode(handlerEdge.dst);
73
+ if (!handlerNode || !handlerNode.file) {
74
+ return 0;
75
+ }
76
+ // Parse the file and find response calls in handler
77
+ const code = readFileSync(handlerNode.file, 'utf-8');
78
+ const ast = parse(code, {
79
+ sourceType: 'module',
80
+ plugins: ['jsx', 'typescript']
81
+ });
82
+ // Find response calls within the handler's line range
83
+ const responseCalls = this.findResponseCalls(ast, handlerNode.file, handlerNode.line);
84
+ // Create RESPONDS_WITH edges
85
+ for (const call of responseCalls) {
86
+ // Try to resolve to existing variable, or create a unique response node
87
+ const dstNodeId = await this.resolveOrCreateResponseNode(graph, handlerNode.file, call, route.id, handlerNode.id // Handler's semantic ID for scope resolution
88
+ );
89
+ await graph.addEdge({
90
+ type: 'RESPONDS_WITH',
91
+ src: route.id,
92
+ dst: dstNodeId,
93
+ metadata: {
94
+ responseMethod: call.method
95
+ }
96
+ });
97
+ edgesCreated++;
98
+ }
99
+ }
100
+ catch {
101
+ // Silent - per-route errors shouldn't spam logs
102
+ }
103
+ return edgesCreated;
104
+ }
105
+ /**
106
+ * Find res.json/res.send calls within a function at given line
107
+ */
108
+ findResponseCalls(ast, file, handlerLine) {
109
+ const calls = [];
110
+ // Using object wrapper to satisfy TypeScript's control flow analysis
111
+ const found = { path: null };
112
+ // First pass: find the handler function at the specified line
113
+ traverse(ast, {
114
+ 'ArrowFunctionExpression|FunctionExpression|FunctionDeclaration': (path) => {
115
+ const node = path.node;
116
+ const line = getLine(node);
117
+ if (line === handlerLine) {
118
+ found.path = path;
119
+ path.stop();
120
+ }
121
+ }
122
+ });
123
+ if (!found.path) {
124
+ return calls;
125
+ }
126
+ const handlerPath = found.path;
127
+ // Second pass: traverse only within the handler to find response calls
128
+ const handlerNode = handlerPath.node;
129
+ // Get parameter names to identify 'res'
130
+ const resParamName = this.getResponseParamName(handlerNode);
131
+ if (!resParamName) {
132
+ return calls;
133
+ }
134
+ // Traverse the handler body for res.json/res.send calls
135
+ handlerPath.traverse({
136
+ CallExpression: (callPath) => {
137
+ const callNode = callPath.node;
138
+ const callee = callNode.callee;
139
+ // Check for res.json() or res.send() or res.status().json() patterns
140
+ const responseInfo = this.extractResponseInfo(callee, resParamName);
141
+ if (!responseInfo) {
142
+ return;
143
+ }
144
+ // Get the argument being sent
145
+ if (callNode.arguments.length === 0) {
146
+ return;
147
+ }
148
+ const arg = callNode.arguments[0];
149
+ const argLine = getLine(arg);
150
+ const argColumn = arg.loc?.start.column ?? 0;
151
+ calls.push({
152
+ method: responseInfo.method,
153
+ argLine,
154
+ argColumn,
155
+ argType: arg.type,
156
+ line: getLine(callNode),
157
+ identifierName: arg.type === 'Identifier' ? arg.name : undefined
158
+ });
159
+ }
160
+ });
161
+ return calls;
162
+ }
163
+ /**
164
+ * Get response parameter name from function params (typically 'res')
165
+ */
166
+ getResponseParamName(func) {
167
+ const params = func.params;
168
+ // Express handlers: (req, res) or (req, res, next)
169
+ if (params.length >= 2) {
170
+ const resParam = params[1];
171
+ if (resParam.type === 'Identifier') {
172
+ return resParam.name;
173
+ }
174
+ }
175
+ return null;
176
+ }
177
+ /**
178
+ * Extract response method info from callee
179
+ * Handles: res.json(), res.send(), res.status(200).json()
180
+ */
181
+ extractResponseInfo(callee, resParamName) {
182
+ // Direct call: res.json() or res.send()
183
+ if (callee.type === 'MemberExpression') {
184
+ const memberExpr = callee;
185
+ const property = memberExpr.property;
186
+ if (property.type !== 'Identifier') {
187
+ return null;
188
+ }
189
+ const methodName = property.name;
190
+ // Check for res.json() or res.send()
191
+ if (memberExpr.object.type === 'Identifier' &&
192
+ memberExpr.object.name === resParamName &&
193
+ RESPONSE_METHODS.includes(methodName)) {
194
+ return { method: methodName };
195
+ }
196
+ // Check for res.status(200).json() chain
197
+ if (memberExpr.object.type === 'CallExpression' &&
198
+ RESPONSE_METHODS.includes(methodName)) {
199
+ const chainedCall = memberExpr.object;
200
+ if (this.isResMethodCall(chainedCall.callee, resParamName, 'status')) {
201
+ return { method: methodName };
202
+ }
203
+ }
204
+ }
205
+ return null;
206
+ }
207
+ /**
208
+ * Check if callee is res.methodName()
209
+ */
210
+ isResMethodCall(callee, resParamName, methodName) {
211
+ if (callee.type !== 'MemberExpression') {
212
+ return false;
213
+ }
214
+ const memberExpr = callee;
215
+ return (memberExpr.object.type === 'Identifier' &&
216
+ memberExpr.object.name === resParamName &&
217
+ memberExpr.property.type === 'Identifier' &&
218
+ memberExpr.property.name === methodName);
219
+ }
220
+ /**
221
+ * Resolve response node: find existing variable or create stub.
222
+ *
223
+ * For Identifier arguments (e.g., res.json(statusData)):
224
+ * 1. Try to find existing VARIABLE/PARAMETER/CONSTANT with same name in handler scope
225
+ * 2. If found, return existing node ID (no stub needed)
226
+ * 3. If not found, fall back to creating stub (external/global variables)
227
+ *
228
+ * For non-Identifier arguments (ObjectExpression, CallExpression, etc.):
229
+ * - Always create stub node (existing behavior)
230
+ *
231
+ * @param graph - Graph backend
232
+ * @param file - Handler file path
233
+ * @param call - Response call info (includes identifierName)
234
+ * @param routeId - Route ID (for metadata)
235
+ * @param handlerSemanticId - Handler function's semantic ID (for scope matching)
236
+ * @returns Node ID (existing or newly created)
237
+ */
238
+ async resolveOrCreateResponseNode(graph, file, call, routeId, handlerSemanticId) {
239
+ const { argLine, argColumn, argType, identifierName } = call;
240
+ // For Identifier arguments, try to find existing variable/parameter
241
+ if (argType === 'Identifier' && identifierName) {
242
+ const existingNodeId = await this.findIdentifierInScope(graph, file, identifierName, handlerSemanticId, argLine);
243
+ if (existingNodeId) {
244
+ return existingNodeId; // Use existing node, no stub needed
245
+ }
246
+ // Fall through to create stub if not found (external/global variables)
247
+ }
248
+ // For non-Identifier or not-found, create stub node (existing logic)
249
+ return this.createResponseArgumentNode(graph, file, argLine, argColumn, argType, routeId);
250
+ }
251
+ /**
252
+ * Find existing VARIABLE/CONSTANT/PARAMETER node in handler scope.
253
+ *
254
+ * Strategy:
255
+ * 1. Parse handler semantic ID to extract scope prefix
256
+ * 2. Query VARIABLE/CONSTANT nodes: match by name, file, scope prefix, and line <= useLine
257
+ * 3. Query PARAMETER nodes: match by name, file, parentFunctionId === handlerSemanticId
258
+ *
259
+ * Scope matching:
260
+ * - Handler ID: "routes.js->anonymous[1]->FUNCTION->anonymous[1]"
261
+ * - Scope prefix: "routes.js->anonymous[1]->"
262
+ * - Variable ID: "routes.js->anonymous[1]->VARIABLE->statusData" (matches prefix)
263
+ * - External ID: "utils.js->VARIABLE->config" (different file)
264
+ *
265
+ * @param graph - Graph backend
266
+ * @param file - File path
267
+ * @param name - Variable name to find
268
+ * @param handlerSemanticId - Handler function's semantic ID
269
+ * @param useLine - Line where identifier is used (variable must be declared before this)
270
+ * @returns Node ID if found, null otherwise
271
+ */
272
+ async findIdentifierInScope(graph, file, name, handlerSemanticId, useLine) {
273
+ // Extract scope prefix from handler semantic ID
274
+ const handlerScopePrefix = this.extractScopePrefix(handlerSemanticId);
275
+ // Query VARIABLE nodes
276
+ for await (const node of graph.queryNodes({ type: 'VARIABLE' })) {
277
+ if (node.name === name && node.file === file) {
278
+ // Check if in handler scope and declared before usage
279
+ if (node.id.startsWith(handlerScopePrefix) && node.line <= useLine) {
280
+ return node.id;
281
+ }
282
+ }
283
+ }
284
+ // Query CONSTANT nodes
285
+ for await (const node of graph.queryNodes({ type: 'CONSTANT' })) {
286
+ if (node.name === name && node.file === file) {
287
+ if (node.id.startsWith(handlerScopePrefix) && node.line <= useLine) {
288
+ return node.id;
289
+ }
290
+ }
291
+ }
292
+ // Query PARAMETER nodes
293
+ for await (const node of graph.queryNodes({ type: 'PARAMETER' })) {
294
+ if (node.name === name && node.file === file) {
295
+ // Parameters belong to the function directly
296
+ const parentFunctionId = node.parentFunctionId;
297
+ if (parentFunctionId === handlerSemanticId) {
298
+ return node.id;
299
+ }
300
+ }
301
+ }
302
+ // Also check module-level variables (scope prefix would be just "file.js->")
303
+ // For module-level constants, they should be accessible from any function in the file
304
+ const modulePrefix = this.extractModulePrefix(handlerSemanticId);
305
+ if (modulePrefix) {
306
+ // Check module-level VARIABLE
307
+ for await (const node of graph.queryNodes({ type: 'VARIABLE' })) {
308
+ if (node.name === name && node.file === file) {
309
+ // Module-level variables have IDs like "file.js->VARIABLE->name" (3 parts)
310
+ // Function-local variables have IDs like "file.js->funcName->VARIABLE->name" (4+ parts)
311
+ // Only match true module-level variables by checking structure
312
+ if (this.isModuleLevelId(node.id, modulePrefix) && node.line <= useLine) {
313
+ return node.id;
314
+ }
315
+ }
316
+ }
317
+ // Check module-level CONSTANT
318
+ for await (const node of graph.queryNodes({ type: 'CONSTANT' })) {
319
+ if (node.name === name && node.file === file) {
320
+ if (this.isModuleLevelId(node.id, modulePrefix) && node.line <= useLine) {
321
+ return node.id;
322
+ }
323
+ }
324
+ }
325
+ }
326
+ return null; // Not found - will create stub
327
+ }
328
+ /**
329
+ * Extract scope prefix from handler function's semantic ID.
330
+ *
331
+ * Handler function semantic IDs follow the pattern:
332
+ * {file}->{scope_path}->{type}->{name}
333
+ *
334
+ * Variables declared INSIDE the handler have IDs where the handler's NAME
335
+ * becomes part of THEIR scope path:
336
+ * {file}->{handler_name}->{type}->{var_name}
337
+ *
338
+ * Examples:
339
+ * - Handler: "index.js->global->FUNCTION->anonymous[0]"
340
+ * -> Variables inside: "index.js->anonymous[0]->CONSTANT->statusData"
341
+ * -> Scope prefix: "index.js->anonymous[0]->"
342
+ *
343
+ * - Handler: "routes.js->anonymous[1]->FUNCTION->anonymous[1]"
344
+ * -> Variables inside: "routes.js->anonymous[1]->VARIABLE->data"
345
+ * -> Scope prefix: "routes.js->anonymous[1]->"
346
+ *
347
+ * - Handler: "app.js->global->FUNCTION->handleRequest"
348
+ * -> Variables inside: "app.js->handleRequest->VARIABLE->result"
349
+ * -> Scope prefix: "app.js->handleRequest->"
350
+ *
351
+ * Algorithm:
352
+ * 1. Split by "->"
353
+ * 2. Take file (first part) and handler name (last part)
354
+ * 3. Join with "->" and add trailing "->"
355
+ *
356
+ * @param semanticId - Handler function's semantic ID
357
+ * @returns Scope prefix for matching variables declared inside the handler
358
+ */
359
+ extractScopePrefix(semanticId) {
360
+ const parts = semanticId.split('->');
361
+ // Semantic ID format: file->scope->TYPE->name
362
+ // We need file + function name (last part) to match variables inside the function
363
+ if (parts.length >= 4) {
364
+ const file = parts[0];
365
+ const functionName = parts[parts.length - 1]; // Function name is the last part
366
+ return `${file}->${functionName}->`;
367
+ }
368
+ // Fallback: use first two parts (shouldn't happen for well-formed IDs)
369
+ if (parts.length >= 2) {
370
+ return `${parts[0]}->${parts[1]}->`;
371
+ }
372
+ return semanticId;
373
+ }
374
+ /**
375
+ * Extract module prefix from semantic ID (for module-level variable access).
376
+ *
377
+ * Examples:
378
+ * - "routes.js->anonymous[1]->FUNCTION->anonymous[1]" -> "routes.js->"
379
+ * - "app.js->startServer->FUNCTION->startServer" -> "app.js->"
380
+ *
381
+ * @param semanticId - Handler function's semantic ID
382
+ * @returns Module prefix for matching module-level variables
383
+ */
384
+ extractModulePrefix(semanticId) {
385
+ const parts = semanticId.split('->');
386
+ if (parts.length >= 1 && parts[0]) {
387
+ return `${parts[0]}->`;
388
+ }
389
+ return null;
390
+ }
391
+ /**
392
+ * Check if a semantic ID represents a true module-level variable.
393
+ *
394
+ * Semantic IDs have format: file->scope->TYPE->name
395
+ * - Module-level variables have "global" as the scope: "file.js->global->TYPE->name"
396
+ * - Function-local variables have function name as scope: "file.js->funcName->TYPE->name"
397
+ *
398
+ * Examples:
399
+ * - "index.js->global->CONSTANT->CONFIG" -> true (module-level)
400
+ * - "index.js->global->VARIABLE->counter" -> true (module-level)
401
+ * - "index.js->anonymous[0]->CONSTANT->data" -> false (function-local)
402
+ * - "routes.js->handler->VARIABLE->result" -> false (function-local)
403
+ *
404
+ * @param nodeId - The node's semantic ID
405
+ * @param modulePrefix - The module prefix (e.g., "index.js->")
406
+ * @returns true if this is a module-level variable
407
+ */
408
+ isModuleLevelId(nodeId, modulePrefix) {
409
+ if (!nodeId.startsWith(modulePrefix)) {
410
+ return false;
411
+ }
412
+ // Check if the scope part (second component) is "global"
413
+ const parts = nodeId.split('->');
414
+ // Expected format: ["file.js", "global", "TYPE", "name"]
415
+ // Check that second part is "global" (module scope)
416
+ return parts.length >= 4 && parts[1] === 'global';
417
+ }
418
+ /**
419
+ * Create a node for the response argument
420
+ */
421
+ async createResponseArgumentNode(graph, file, line, column, astType, routeId) {
422
+ // Map AST type to node type and create appropriate node
423
+ switch (astType) {
424
+ case 'ObjectExpression': {
425
+ // Include counter to make the node unique even for same location
426
+ const counter = this.responseNodeCounter++;
427
+ const id = `OBJECT_LITERAL#response:${counter}#${file}#${line}:${column}`;
428
+ await graph.addNode({
429
+ id,
430
+ type: 'OBJECT_LITERAL',
431
+ name: '<response>',
432
+ file,
433
+ line,
434
+ column,
435
+ parentRouteId: routeId
436
+ });
437
+ return id;
438
+ }
439
+ case 'Identifier': {
440
+ // For identifiers, we link to the variable that's being returned
441
+ const counter = this.responseNodeCounter++;
442
+ const id = `VARIABLE#response:${counter}#${file}#${line}:${column}`;
443
+ await graph.addNode({
444
+ id,
445
+ type: 'VARIABLE',
446
+ name: '<response>',
447
+ file,
448
+ line,
449
+ column
450
+ });
451
+ return id;
452
+ }
453
+ case 'CallExpression': {
454
+ const counter = this.responseNodeCounter++;
455
+ const id = `CALL#response:${counter}#${file}#${line}:${column}`;
456
+ await graph.addNode({
457
+ id,
458
+ type: 'CALL',
459
+ name: '<response>',
460
+ file,
461
+ line,
462
+ column
463
+ });
464
+ return id;
465
+ }
466
+ case 'ArrayExpression': {
467
+ const counter = this.responseNodeCounter++;
468
+ const id = `ARRAY_LITERAL#response:${counter}#${file}#${line}:${column}`;
469
+ await graph.addNode({
470
+ id,
471
+ type: 'ARRAY_LITERAL',
472
+ name: '<response>',
473
+ file,
474
+ line,
475
+ column
476
+ });
477
+ return id;
478
+ }
479
+ default: {
480
+ // Generic expression node
481
+ const counter = this.responseNodeCounter++;
482
+ const id = `EXPRESSION#response:${counter}#${file}#${line}:${column}`;
483
+ await graph.addNode({
484
+ id,
485
+ type: 'EXPRESSION',
486
+ name: '<response>',
487
+ file,
488
+ line,
489
+ column
490
+ });
491
+ return id;
492
+ }
493
+ }
494
+ }
495
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"ExpressRouteAnalyzer.d.ts","sourceRoot":"","sources":["../../../src/plugins/analysis/ExpressRouteAnalyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,EAAE,MAAM,EAA0C,MAAM,cAAc,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AA+ChF,qBAAa,oBAAqB,SAAQ,MAAM;IAC9C,IAAI,QAAQ,IAAI,cAAc,CAW7B;IAEK,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;YAmD9C,aAAa;CA8R5B"}
1
+ {"version":3,"file":"ExpressRouteAnalyzer.d.ts","sourceRoot":"","sources":["../../../src/plugins/analysis/ExpressRouteAnalyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,EAAE,MAAM,EAA0C,MAAM,cAAc,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAkDhF,qBAAa,oBAAqB,SAAQ,MAAM;IAC9C,IAAI,QAAQ,IAAI,cAAc,CAW7B;IAEK,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;YAmD9C,aAAa;CAkU5B"}
@@ -11,7 +11,7 @@ import { readFileSync } from 'fs';
11
11
  import { parse } from '@babel/parser';
12
12
  import traverseModule from '@babel/traverse';
13
13
  import { Plugin, createSuccessResult, createErrorResult } from '../Plugin.js';
14
- import { getLine } from './ast/utils/location.js';
14
+ import { getLine, getColumn } from './ast/utils/location.js';
15
15
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
16
  const traverse = traverseModule.default || traverseModule;
17
17
  const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'options', 'head'];
@@ -140,6 +140,32 @@ export class ExpressRouteAnalyzer extends Plugin {
140
140
  const handlers = args.slice(1);
141
141
  // Последний handler - это route handler
142
142
  const mainHandler = handlers[handlers.length - 1];
143
+ // Unwrap wrapper functions (asyncHandler, catchAsync, etc.)
144
+ // Pattern: wrapper(async (req, res) => {...}) -> extract inner function
145
+ // Also handles nested wrappers: outer(inner(handler))
146
+ let actualHandler = mainHandler;
147
+ while (actualHandler.type === 'CallExpression') {
148
+ const callExpr = actualHandler;
149
+ const firstArg = callExpr.arguments[0];
150
+ if (!firstArg) {
151
+ // No arguments - not a wrapper pattern
152
+ break;
153
+ }
154
+ if (firstArg.type === 'ArrowFunctionExpression' ||
155
+ firstArg.type === 'FunctionExpression') {
156
+ // Found the actual handler function
157
+ actualHandler = firstArg;
158
+ break;
159
+ }
160
+ else if (firstArg.type === 'CallExpression') {
161
+ // Nested wrapper: outer(inner(...)) - continue unwrapping
162
+ actualHandler = firstArg;
163
+ }
164
+ else {
165
+ // First arg is not a function or CallExpression - not a wrapper pattern
166
+ break;
167
+ }
168
+ }
143
169
  // Все предыдущие - middleware
144
170
  const middlewareHandlers = handlers.slice(0, -1);
145
171
  // Создаём http:route
@@ -151,10 +177,14 @@ export class ExpressRouteAnalyzer extends Plugin {
151
177
  path: routePath,
152
178
  file: module.file,
153
179
  line: getLine(node),
180
+ column: getColumn(node),
154
181
  routerName: objectName,
155
- handlerLine: mainHandler.loc
156
- ? getLine(mainHandler)
157
- : getLine(node)
182
+ handlerLine: actualHandler.loc
183
+ ? getLine(actualHandler)
184
+ : getLine(node),
185
+ handlerColumn: actualHandler.loc
186
+ ? getColumn(actualHandler)
187
+ : getColumn(node)
158
188
  });
159
189
  // Обрабатываем middleware
160
190
  middlewareHandlers.forEach((mw, index) => {
@@ -181,6 +211,7 @@ export class ExpressRouteAnalyzer extends Plugin {
181
211
  name: middlewareName,
182
212
  file: module.file,
183
213
  line: mwNode.loc ? getLine(mwNode) : getLine(node),
214
+ column: mwNode.loc ? getColumn(mwNode) : getColumn(node),
184
215
  endpointId: endpointId,
185
216
  order: index // Порядок в цепочке
186
217
  });
@@ -218,6 +249,7 @@ export class ExpressRouteAnalyzer extends Plugin {
218
249
  name: middlewareName,
219
250
  file: module.file,
220
251
  line: getLine(node),
252
+ column: getColumn(node),
221
253
  mountPath: mountPath,
222
254
  isGlobal: mountPath === '/' // Global middleware если нет path
223
255
  });
@@ -230,10 +262,11 @@ export class ExpressRouteAnalyzer extends Plugin {
230
262
  });
231
263
  // Создаём ENDPOINT ноды
232
264
  for (const endpoint of endpoints) {
233
- // Сохраняем handlerLine ПЕРЕД destructuring
265
+ // Сохраняем handler location ПЕРЕД destructuring
234
266
  const handlerLine = endpoint.handlerLine;
267
+ const handlerColumn = endpoint.handlerColumn;
235
268
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
236
- const { handlerLine: _, routerName, ...endpointData } = endpoint;
269
+ const { handlerLine: _hl, handlerColumn: _hc, routerName, ...endpointData } = endpoint;
237
270
  await graph.addNode(endpointData);
238
271
  endpointsCreated++;
239
272
  // MODULE -> CONTAINS -> ENDPOINT
@@ -243,22 +276,24 @@ export class ExpressRouteAnalyzer extends Plugin {
243
276
  dst: endpoint.id
244
277
  });
245
278
  edgesCreated++;
246
- // Ищем FUNCTION ноду для handler (arrow function на той же строке)
279
+ // Ищем FUNCTION ноду для handler по line+column
280
+ // NOTE: queryNodes не поддерживает line/column фильтрацию, поэтому фильтруем вручную
247
281
  if (handlerLine) {
248
- // Используем queryNodes вместо прямого доступа к graph.nodes
249
282
  for await (const fn of graph.queryNodes({
250
283
  type: 'FUNCTION',
251
- file: module.file,
252
- line: handlerLine
284
+ file: module.file
253
285
  })) {
254
- // ENDPOINT -> HANDLED_BY -> FUNCTION
255
- await graph.addEdge({
256
- type: 'HANDLED_BY',
257
- src: endpoint.id,
258
- dst: fn.id
259
- });
260
- edgesCreated++;
261
- break; // Берём только первую найденную функцию
286
+ // Проверяем точное совпадение line и column
287
+ if (fn.line === handlerLine && fn.column === handlerColumn) {
288
+ // ENDPOINT -> HANDLED_BY -> FUNCTION
289
+ await graph.addEdge({
290
+ type: 'HANDLED_BY',
291
+ src: endpoint.id,
292
+ dst: fn.id
293
+ });
294
+ edgesCreated++;
295
+ break;
296
+ }
262
297
  }
263
298
  }
264
299
  }