@grafema/util 0.3.0-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 (324) hide show
  1. package/LICENSE +190 -0
  2. package/dist/api/GraphAPI.d.ts +87 -0
  3. package/dist/api/GraphAPI.d.ts.map +1 -0
  4. package/dist/api/GraphAPI.js +212 -0
  5. package/dist/api/GraphAPI.js.map +1 -0
  6. package/dist/api/GuaranteeAPI.d.ts +147 -0
  7. package/dist/api/GuaranteeAPI.d.ts.map +1 -0
  8. package/dist/api/GuaranteeAPI.js +290 -0
  9. package/dist/api/GuaranteeAPI.js.map +1 -0
  10. package/dist/config/ConfigLoader.d.ts +214 -0
  11. package/dist/config/ConfigLoader.d.ts.map +1 -0
  12. package/dist/config/ConfigLoader.js +441 -0
  13. package/dist/config/ConfigLoader.js.map +1 -0
  14. package/dist/config/index.d.ts +6 -0
  15. package/dist/config/index.d.ts.map +1 -0
  16. package/dist/config/index.js +5 -0
  17. package/dist/config/index.js.map +1 -0
  18. package/dist/core/CoverageAnalyzer.d.ts +65 -0
  19. package/dist/core/CoverageAnalyzer.d.ts.map +1 -0
  20. package/dist/core/CoverageAnalyzer.js +199 -0
  21. package/dist/core/CoverageAnalyzer.js.map +1 -0
  22. package/dist/core/FileExplainer.d.ts +101 -0
  23. package/dist/core/FileExplainer.d.ts.map +1 -0
  24. package/dist/core/FileExplainer.js +140 -0
  25. package/dist/core/FileExplainer.js.map +1 -0
  26. package/dist/core/FileOverview.d.ts +124 -0
  27. package/dist/core/FileOverview.d.ts.map +1 -0
  28. package/dist/core/FileOverview.js +279 -0
  29. package/dist/core/FileOverview.js.map +1 -0
  30. package/dist/core/GrafemaUri.d.ts +66 -0
  31. package/dist/core/GrafemaUri.d.ts.map +1 -0
  32. package/dist/core/GrafemaUri.js +191 -0
  33. package/dist/core/GrafemaUri.js.map +1 -0
  34. package/dist/core/GraphBackend.d.ts +158 -0
  35. package/dist/core/GraphBackend.d.ts.map +1 -0
  36. package/dist/core/GraphBackend.js +85 -0
  37. package/dist/core/GraphBackend.js.map +1 -0
  38. package/dist/core/GraphFreshnessChecker.d.ts +33 -0
  39. package/dist/core/GraphFreshnessChecker.d.ts.map +1 -0
  40. package/dist/core/GraphFreshnessChecker.js +104 -0
  41. package/dist/core/GraphFreshnessChecker.js.map +1 -0
  42. package/dist/core/GuaranteeManager.d.ts +254 -0
  43. package/dist/core/GuaranteeManager.d.ts.map +1 -0
  44. package/dist/core/GuaranteeManager.js +447 -0
  45. package/dist/core/GuaranteeManager.js.map +1 -0
  46. package/dist/core/HashUtils.d.ts +24 -0
  47. package/dist/core/HashUtils.d.ts.map +1 -0
  48. package/dist/core/HashUtils.js +46 -0
  49. package/dist/core/HashUtils.js.map +1 -0
  50. package/dist/core/IncrementalReanalyzer.d.ts +33 -0
  51. package/dist/core/IncrementalReanalyzer.d.ts.map +1 -0
  52. package/dist/core/IncrementalReanalyzer.js +67 -0
  53. package/dist/core/IncrementalReanalyzer.js.map +1 -0
  54. package/dist/core/ResourceRegistry.d.ts +17 -0
  55. package/dist/core/ResourceRegistry.d.ts.map +1 -0
  56. package/dist/core/ResourceRegistry.js +32 -0
  57. package/dist/core/ResourceRegistry.js.map +1 -0
  58. package/dist/core/SemanticId.d.ts +159 -0
  59. package/dist/core/SemanticId.d.ts.map +1 -0
  60. package/dist/core/SemanticId.js +291 -0
  61. package/dist/core/SemanticId.js.map +1 -0
  62. package/dist/core/VersionManager.d.ts +166 -0
  63. package/dist/core/VersionManager.d.ts.map +1 -0
  64. package/dist/core/VersionManager.js +239 -0
  65. package/dist/core/VersionManager.js.map +1 -0
  66. package/dist/core/brandNodeInternal.d.ts +14 -0
  67. package/dist/core/brandNodeInternal.d.ts.map +1 -0
  68. package/dist/core/brandNodeInternal.js +4 -0
  69. package/dist/core/brandNodeInternal.js.map +1 -0
  70. package/dist/core/nodes/GuaranteeNode.d.ts +76 -0
  71. package/dist/core/nodes/GuaranteeNode.d.ts.map +1 -0
  72. package/dist/core/nodes/GuaranteeNode.js +118 -0
  73. package/dist/core/nodes/GuaranteeNode.js.map +1 -0
  74. package/dist/core/nodes/IssueNode.d.ts +73 -0
  75. package/dist/core/nodes/IssueNode.d.ts.map +1 -0
  76. package/dist/core/nodes/IssueNode.js +130 -0
  77. package/dist/core/nodes/IssueNode.js.map +1 -0
  78. package/dist/core/nodes/NodeKind.d.ts +104 -0
  79. package/dist/core/nodes/NodeKind.d.ts.map +1 -0
  80. package/dist/core/nodes/NodeKind.js +166 -0
  81. package/dist/core/nodes/NodeKind.js.map +1 -0
  82. package/dist/diagnostics/DiagnosticCollector.d.ts +103 -0
  83. package/dist/diagnostics/DiagnosticCollector.d.ts.map +1 -0
  84. package/dist/diagnostics/DiagnosticCollector.js +133 -0
  85. package/dist/diagnostics/DiagnosticCollector.js.map +1 -0
  86. package/dist/diagnostics/DiagnosticReporter.d.ts +122 -0
  87. package/dist/diagnostics/DiagnosticReporter.d.ts.map +1 -0
  88. package/dist/diagnostics/DiagnosticReporter.js +300 -0
  89. package/dist/diagnostics/DiagnosticReporter.js.map +1 -0
  90. package/dist/diagnostics/DiagnosticWriter.d.ts +31 -0
  91. package/dist/diagnostics/DiagnosticWriter.d.ts.map +1 -0
  92. package/dist/diagnostics/DiagnosticWriter.js +44 -0
  93. package/dist/diagnostics/DiagnosticWriter.js.map +1 -0
  94. package/dist/diagnostics/categories.d.ts +57 -0
  95. package/dist/diagnostics/categories.d.ts.map +1 -0
  96. package/dist/diagnostics/categories.js +71 -0
  97. package/dist/diagnostics/categories.js.map +1 -0
  98. package/dist/diagnostics/index.d.ts +17 -0
  99. package/dist/diagnostics/index.d.ts.map +1 -0
  100. package/dist/diagnostics/index.js +15 -0
  101. package/dist/diagnostics/index.js.map +1 -0
  102. package/dist/errors/GrafemaError.d.ts +200 -0
  103. package/dist/errors/GrafemaError.d.ts.map +1 -0
  104. package/dist/errors/GrafemaError.js +209 -0
  105. package/dist/errors/GrafemaError.js.map +1 -0
  106. package/dist/index.d.ts +75 -0
  107. package/dist/index.d.ts.map +1 -0
  108. package/dist/index.js +76 -0
  109. package/dist/index.js.map +1 -0
  110. package/dist/instructions/index.d.ts +8 -0
  111. package/dist/instructions/index.d.ts.map +1 -0
  112. package/dist/instructions/index.js +20 -0
  113. package/dist/instructions/index.js.map +1 -0
  114. package/dist/instructions/onboarding.md +133 -0
  115. package/dist/knowledge/KnowledgeBase.d.ts +113 -0
  116. package/dist/knowledge/KnowledgeBase.d.ts.map +1 -0
  117. package/dist/knowledge/KnowledgeBase.js +420 -0
  118. package/dist/knowledge/KnowledgeBase.js.map +1 -0
  119. package/dist/knowledge/SemanticAddressResolver.d.ts +59 -0
  120. package/dist/knowledge/SemanticAddressResolver.d.ts.map +1 -0
  121. package/dist/knowledge/SemanticAddressResolver.js +160 -0
  122. package/dist/knowledge/SemanticAddressResolver.js.map +1 -0
  123. package/dist/knowledge/git-ingest.d.ts +58 -0
  124. package/dist/knowledge/git-ingest.d.ts.map +1 -0
  125. package/dist/knowledge/git-ingest.js +301 -0
  126. package/dist/knowledge/git-ingest.js.map +1 -0
  127. package/dist/knowledge/git-queries.d.ts +86 -0
  128. package/dist/knowledge/git-queries.d.ts.map +1 -0
  129. package/dist/knowledge/git-queries.js +177 -0
  130. package/dist/knowledge/git-queries.js.map +1 -0
  131. package/dist/knowledge/index.d.ts +14 -0
  132. package/dist/knowledge/index.d.ts.map +1 -0
  133. package/dist/knowledge/index.js +10 -0
  134. package/dist/knowledge/index.js.map +1 -0
  135. package/dist/knowledge/parser.d.ts +39 -0
  136. package/dist/knowledge/parser.d.ts.map +1 -0
  137. package/dist/knowledge/parser.js +292 -0
  138. package/dist/knowledge/parser.js.map +1 -0
  139. package/dist/knowledge/types.d.ts +133 -0
  140. package/dist/knowledge/types.d.ts.map +1 -0
  141. package/dist/knowledge/types.js +8 -0
  142. package/dist/knowledge/types.js.map +1 -0
  143. package/dist/logging/Logger.d.ts +98 -0
  144. package/dist/logging/Logger.d.ts.map +1 -0
  145. package/dist/logging/Logger.js +274 -0
  146. package/dist/logging/Logger.js.map +1 -0
  147. package/dist/notation/archetypes.d.ts +36 -0
  148. package/dist/notation/archetypes.d.ts.map +1 -0
  149. package/dist/notation/archetypes.js +173 -0
  150. package/dist/notation/archetypes.js.map +1 -0
  151. package/dist/notation/fold.d.ts +25 -0
  152. package/dist/notation/fold.d.ts.map +1 -0
  153. package/dist/notation/fold.js +598 -0
  154. package/dist/notation/fold.js.map +1 -0
  155. package/dist/notation/index.d.ts +18 -0
  156. package/dist/notation/index.d.ts.map +1 -0
  157. package/dist/notation/index.js +16 -0
  158. package/dist/notation/index.js.map +1 -0
  159. package/dist/notation/lodExtractor.d.ts +32 -0
  160. package/dist/notation/lodExtractor.d.ts.map +1 -0
  161. package/dist/notation/lodExtractor.js +149 -0
  162. package/dist/notation/lodExtractor.js.map +1 -0
  163. package/dist/notation/nameShortener.d.ts +22 -0
  164. package/dist/notation/nameShortener.d.ts.map +1 -0
  165. package/dist/notation/nameShortener.js +24 -0
  166. package/dist/notation/nameShortener.js.map +1 -0
  167. package/dist/notation/perspectives.d.ts +11 -0
  168. package/dist/notation/perspectives.d.ts.map +1 -0
  169. package/dist/notation/perspectives.js +16 -0
  170. package/dist/notation/perspectives.js.map +1 -0
  171. package/dist/notation/renderer.d.ts +31 -0
  172. package/dist/notation/renderer.d.ts.map +1 -0
  173. package/dist/notation/renderer.js +315 -0
  174. package/dist/notation/renderer.js.map +1 -0
  175. package/dist/notation/traceRenderer.d.ts +39 -0
  176. package/dist/notation/traceRenderer.d.ts.map +1 -0
  177. package/dist/notation/traceRenderer.js +358 -0
  178. package/dist/notation/traceRenderer.js.map +1 -0
  179. package/dist/notation/types.d.ts +66 -0
  180. package/dist/notation/types.d.ts.map +1 -0
  181. package/dist/notation/types.js +10 -0
  182. package/dist/notation/types.js.map +1 -0
  183. package/dist/queries/NodeContext.d.ts +81 -0
  184. package/dist/queries/NodeContext.d.ts.map +1 -0
  185. package/dist/queries/NodeContext.js +196 -0
  186. package/dist/queries/NodeContext.js.map +1 -0
  187. package/dist/queries/findCallsInFunction.d.ts +62 -0
  188. package/dist/queries/findCallsInFunction.d.ts.map +1 -0
  189. package/dist/queries/findCallsInFunction.js +169 -0
  190. package/dist/queries/findCallsInFunction.js.map +1 -0
  191. package/dist/queries/findContainingFunction.d.ts +57 -0
  192. package/dist/queries/findContainingFunction.d.ts.map +1 -0
  193. package/dist/queries/findContainingFunction.js +91 -0
  194. package/dist/queries/findContainingFunction.js.map +1 -0
  195. package/dist/queries/index.d.ts +18 -0
  196. package/dist/queries/index.d.ts.map +1 -0
  197. package/dist/queries/index.js +14 -0
  198. package/dist/queries/index.js.map +1 -0
  199. package/dist/queries/traceDataflow.d.ts +65 -0
  200. package/dist/queries/traceDataflow.d.ts.map +1 -0
  201. package/dist/queries/traceDataflow.js +754 -0
  202. package/dist/queries/traceDataflow.js.map +1 -0
  203. package/dist/queries/traceValues.d.ts +70 -0
  204. package/dist/queries/traceValues.d.ts.map +1 -0
  205. package/dist/queries/traceValues.js +373 -0
  206. package/dist/queries/traceValues.js.map +1 -0
  207. package/dist/queries/types.d.ts +166 -0
  208. package/dist/queries/types.d.ts.map +1 -0
  209. package/dist/queries/types.js +10 -0
  210. package/dist/queries/types.js.map +1 -0
  211. package/dist/schema/GraphSchemaExtractor.d.ts +53 -0
  212. package/dist/schema/GraphSchemaExtractor.d.ts.map +1 -0
  213. package/dist/schema/GraphSchemaExtractor.js +125 -0
  214. package/dist/schema/GraphSchemaExtractor.js.map +1 -0
  215. package/dist/schema/InterfaceSchemaExtractor.d.ts +73 -0
  216. package/dist/schema/InterfaceSchemaExtractor.d.ts.map +1 -0
  217. package/dist/schema/InterfaceSchemaExtractor.js +113 -0
  218. package/dist/schema/InterfaceSchemaExtractor.js.map +1 -0
  219. package/dist/schema/index.d.ts +5 -0
  220. package/dist/schema/index.d.ts.map +1 -0
  221. package/dist/schema/index.js +3 -0
  222. package/dist/schema/index.js.map +1 -0
  223. package/dist/storage/backends/RFDBServerBackend.d.ts +356 -0
  224. package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -0
  225. package/dist/storage/backends/RFDBServerBackend.js +748 -0
  226. package/dist/storage/backends/RFDBServerBackend.js.map +1 -0
  227. package/dist/storage/backends/typeValidation.d.ts +47 -0
  228. package/dist/storage/backends/typeValidation.d.ts.map +1 -0
  229. package/dist/storage/backends/typeValidation.js +141 -0
  230. package/dist/storage/backends/typeValidation.js.map +1 -0
  231. package/dist/utils/findRfdbBinary.d.ts +67 -0
  232. package/dist/utils/findRfdbBinary.d.ts.map +1 -0
  233. package/dist/utils/findRfdbBinary.js +261 -0
  234. package/dist/utils/findRfdbBinary.js.map +1 -0
  235. package/dist/utils/lazyDownload.d.ts +43 -0
  236. package/dist/utils/lazyDownload.d.ts.map +1 -0
  237. package/dist/utils/lazyDownload.js +175 -0
  238. package/dist/utils/lazyDownload.js.map +1 -0
  239. package/dist/utils/moduleResolution.d.ts +134 -0
  240. package/dist/utils/moduleResolution.d.ts.map +1 -0
  241. package/dist/utils/moduleResolution.js +189 -0
  242. package/dist/utils/moduleResolution.js.map +1 -0
  243. package/dist/utils/resolveNodeFile.d.ts +13 -0
  244. package/dist/utils/resolveNodeFile.d.ts.map +1 -0
  245. package/dist/utils/resolveNodeFile.js +18 -0
  246. package/dist/utils/resolveNodeFile.js.map +1 -0
  247. package/dist/utils/startRfdbServer.d.ts +63 -0
  248. package/dist/utils/startRfdbServer.d.ts.map +1 -0
  249. package/dist/utils/startRfdbServer.js +142 -0
  250. package/dist/utils/startRfdbServer.js.map +1 -0
  251. package/dist/validation/PathValidator.d.ts +80 -0
  252. package/dist/validation/PathValidator.d.ts.map +1 -0
  253. package/dist/validation/PathValidator.js +252 -0
  254. package/dist/validation/PathValidator.js.map +1 -0
  255. package/dist/version.d.ts +11 -0
  256. package/dist/version.d.ts.map +1 -0
  257. package/dist/version.js +26 -0
  258. package/dist/version.js.map +1 -0
  259. package/package.json +50 -0
  260. package/src/api/GraphAPI.ts +307 -0
  261. package/src/api/GuaranteeAPI.ts +402 -0
  262. package/src/config/ConfigLoader.ts +653 -0
  263. package/src/config/index.ts +13 -0
  264. package/src/core/CoverageAnalyzer.ts +243 -0
  265. package/src/core/FileExplainer.ts +179 -0
  266. package/src/core/FileOverview.ts +397 -0
  267. package/src/core/GrafemaUri.ts +216 -0
  268. package/src/core/GraphBackend.ts +266 -0
  269. package/src/core/GraphFreshnessChecker.ts +145 -0
  270. package/src/core/GuaranteeManager.ts +684 -0
  271. package/src/core/HashUtils.ts +48 -0
  272. package/src/core/IncrementalReanalyzer.ts +106 -0
  273. package/src/core/ResourceRegistry.ts +39 -0
  274. package/src/core/SemanticId.ts +423 -0
  275. package/src/core/VersionManager.ts +405 -0
  276. package/src/core/brandNodeInternal.ts +16 -0
  277. package/src/core/nodes/GuaranteeNode.ts +162 -0
  278. package/src/core/nodes/IssueNode.ts +177 -0
  279. package/src/core/nodes/NodeKind.ts +192 -0
  280. package/src/diagnostics/DiagnosticCollector.ts +170 -0
  281. package/src/diagnostics/DiagnosticReporter.ts +395 -0
  282. package/src/diagnostics/DiagnosticWriter.ts +50 -0
  283. package/src/diagnostics/categories.ts +104 -0
  284. package/src/diagnostics/index.ts +30 -0
  285. package/src/errors/GrafemaError.ts +297 -0
  286. package/src/index.ts +261 -0
  287. package/src/instructions/index.ts +21 -0
  288. package/src/instructions/onboarding.md +133 -0
  289. package/src/knowledge/KnowledgeBase.ts +486 -0
  290. package/src/knowledge/SemanticAddressResolver.ts +191 -0
  291. package/src/knowledge/git-ingest.ts +402 -0
  292. package/src/knowledge/git-queries.ts +269 -0
  293. package/src/knowledge/index.ts +29 -0
  294. package/src/knowledge/parser.ts +294 -0
  295. package/src/knowledge/types.ts +146 -0
  296. package/src/logging/Logger.ts +303 -0
  297. package/src/notation/archetypes.ts +189 -0
  298. package/src/notation/fold.ts +696 -0
  299. package/src/notation/index.ts +27 -0
  300. package/src/notation/lodExtractor.ts +177 -0
  301. package/src/notation/nameShortener.ts +24 -0
  302. package/src/notation/perspectives.ts +18 -0
  303. package/src/notation/renderer.ts +394 -0
  304. package/src/notation/traceRenderer.ts +458 -0
  305. package/src/notation/types.ts +79 -0
  306. package/src/queries/NodeContext.ts +280 -0
  307. package/src/queries/findCallsInFunction.ts +249 -0
  308. package/src/queries/findContainingFunction.ts +124 -0
  309. package/src/queries/index.ts +44 -0
  310. package/src/queries/traceDataflow.ts +838 -0
  311. package/src/queries/traceValues.ts +531 -0
  312. package/src/queries/types.ts +191 -0
  313. package/src/schema/GraphSchemaExtractor.ts +177 -0
  314. package/src/schema/InterfaceSchemaExtractor.ts +173 -0
  315. package/src/schema/index.ts +5 -0
  316. package/src/storage/backends/RFDBServerBackend.ts +895 -0
  317. package/src/storage/backends/typeValidation.ts +154 -0
  318. package/src/utils/findRfdbBinary.ts +288 -0
  319. package/src/utils/lazyDownload.ts +206 -0
  320. package/src/utils/moduleResolution.ts +271 -0
  321. package/src/utils/resolveNodeFile.ts +18 -0
  322. package/src/utils/startRfdbServer.ts +197 -0
  323. package/src/validation/PathValidator.ts +334 -0
  324. package/src/version.ts +28 -0
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Node Context — shared data logic for building node neighborhood context
3
+ *
4
+ * Extracts source code preview and all incoming/outgoing edges
5
+ * for a given node, grouped by edge type.
6
+ *
7
+ * Used by CLI (grafema context) and MCP (get_context tool).
8
+ * Consumers handle their own formatting; this module provides data only.
9
+ *
10
+ * @module queries/NodeContext
11
+ */
12
+
13
+ import { existsSync, readFileSync } from 'fs';
14
+ import type { BaseNodeRecord, EdgeRecord } from '@grafema/types';
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Minimal backend interface (matches project pattern — loose coupling)
18
+ // ---------------------------------------------------------------------------
19
+
20
+ interface GraphBackend {
21
+ getNode(id: string): Promise<BaseNodeRecord | null>;
22
+ getOutgoingEdges(
23
+ nodeId: string,
24
+ edgeTypes?: string[] | null,
25
+ ): Promise<EdgeRecord[]>;
26
+ getIncomingEdges(
27
+ nodeId: string,
28
+ edgeTypes?: string[] | null,
29
+ ): Promise<EdgeRecord[]>;
30
+ }
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Constants
34
+ // ---------------------------------------------------------------------------
35
+
36
+ /**
37
+ * Edge types that are structural/containment — shown in compact form.
38
+ * These describe HOW code is nested, not WHAT it does.
39
+ */
40
+ export const STRUCTURAL_EDGE_TYPES = new Set([
41
+ 'CONTAINS',
42
+ 'HAS_SCOPE',
43
+ 'DECLARES',
44
+ 'DEFINES',
45
+ 'HAS_CONDITION',
46
+ 'HAS_CASE',
47
+ 'HAS_DEFAULT',
48
+ 'HAS_CONSEQUENT',
49
+ 'HAS_ALTERNATE',
50
+ 'HAS_BODY',
51
+ 'HAS_INIT',
52
+ 'HAS_UPDATE',
53
+ 'HAS_CATCH',
54
+ 'HAS_FINALLY',
55
+ 'HAS_PARAMETER',
56
+ 'HAS_PROPERTY',
57
+ 'HAS_ELEMENT',
58
+ 'USES',
59
+ 'GOVERNS',
60
+ 'VIOLATES',
61
+ 'AFFECTS',
62
+ 'UNKNOWN',
63
+ ]);
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Types
67
+ // ---------------------------------------------------------------------------
68
+
69
+ export interface EdgeWithNode {
70
+ edge: EdgeRecord;
71
+ node: BaseNodeRecord | null;
72
+ }
73
+
74
+ export interface EdgeGroup {
75
+ edgeType: string;
76
+ edges: EdgeWithNode[];
77
+ }
78
+
79
+ export interface SourcePreview {
80
+ file: string;
81
+ startLine: number;
82
+ endLine: number;
83
+ lines: string[];
84
+ }
85
+
86
+ export interface NodeContext {
87
+ node: BaseNodeRecord;
88
+ source: SourcePreview | null;
89
+ outgoing: EdgeGroup[];
90
+ incoming: EdgeGroup[];
91
+ }
92
+
93
+ export interface BuildNodeContextOptions {
94
+ /** Lines of context before the highlighted line (default: 3) */
95
+ contextLines?: number;
96
+ /** Filter to only these edge types (null = show all) */
97
+ edgeTypeFilter?: Set<string> | null;
98
+ /**
99
+ * DI callback for reading file content.
100
+ * Return file content as string, or null if file can't be read.
101
+ * Default uses fs.readFileSync.
102
+ */
103
+ readFileContent?: (filePath: string) => string | null;
104
+ }
105
+
106
+ // ---------------------------------------------------------------------------
107
+ // Default file reader
108
+ // ---------------------------------------------------------------------------
109
+
110
+ function defaultReadFileContent(filePath: string): string | null {
111
+ if (!existsSync(filePath)) return null;
112
+ try {
113
+ return readFileSync(filePath, 'utf-8');
114
+ } catch {
115
+ return null;
116
+ }
117
+ }
118
+
119
+ // ---------------------------------------------------------------------------
120
+ // Core function
121
+ // ---------------------------------------------------------------------------
122
+
123
+ /**
124
+ * Build full node context: source preview + all edges with connected nodes.
125
+ *
126
+ * @param backend - Graph backend for queries
127
+ * @param node - The node to build context for (already looked up by caller)
128
+ * @param options - Context options
129
+ * @returns NodeContext with source preview and grouped edges
130
+ */
131
+ export async function buildNodeContext(
132
+ backend: GraphBackend,
133
+ node: BaseNodeRecord,
134
+ options: BuildNodeContextOptions = {},
135
+ ): Promise<NodeContext> {
136
+ const {
137
+ contextLines = 3,
138
+ edgeTypeFilter = null,
139
+ readFileContent = defaultReadFileContent,
140
+ } = options;
141
+
142
+ // Source code preview
143
+ let source: SourcePreview | null = null;
144
+ if (node.file && node.line) {
145
+ const content = readFileContent(node.file);
146
+ if (content) {
147
+ const allLines = content.split('\n');
148
+ const line = node.line as number;
149
+ const startLine = Math.max(1, line - contextLines);
150
+ const endLine = Math.min(allLines.length, line + contextLines + 12);
151
+ source = {
152
+ file: node.file,
153
+ startLine,
154
+ endLine,
155
+ lines: allLines.slice(startLine - 1, endLine),
156
+ };
157
+ }
158
+ }
159
+
160
+ // Outgoing edges
161
+ const rawOutgoing = await backend.getOutgoingEdges(node.id);
162
+ const outgoing = await groupEdges(backend, rawOutgoing, 'dst', edgeTypeFilter);
163
+
164
+ // Incoming edges
165
+ const rawIncoming = await backend.getIncomingEdges(node.id);
166
+ const incoming = await groupEdges(backend, rawIncoming, 'src', edgeTypeFilter);
167
+
168
+ return { node, source, outgoing, incoming };
169
+ }
170
+
171
+ // ---------------------------------------------------------------------------
172
+ // Edge grouping
173
+ // ---------------------------------------------------------------------------
174
+
175
+ /**
176
+ * Group edges by type and resolve connected nodes.
177
+ *
178
+ * Sort order: non-structural edges first (alphabetical),
179
+ * then structural edges (alphabetical).
180
+ */
181
+ async function groupEdges(
182
+ backend: GraphBackend,
183
+ edges: EdgeRecord[],
184
+ nodeField: 'src' | 'dst',
185
+ edgeTypeFilter: Set<string> | null,
186
+ ): Promise<EdgeGroup[]> {
187
+ const groups = new Map<string, EdgeWithNode[]>();
188
+
189
+ for (const edge of edges) {
190
+ const edgeType = edge.type || 'UNKNOWN';
191
+
192
+ // Apply edge type filter
193
+ if (edgeTypeFilter && !edgeTypeFilter.has(edgeType)) continue;
194
+
195
+ const connectedId = edge[nodeField];
196
+ const connectedNode = await backend.getNode(connectedId);
197
+
198
+ if (!groups.has(edgeType)) {
199
+ groups.set(edgeType, []);
200
+ }
201
+ groups.get(edgeType)!.push({ edge, node: connectedNode });
202
+ }
203
+
204
+ // Sort groups: primary edges first, then structural
205
+ return Array.from(groups.entries())
206
+ .sort(([a], [b]) => {
207
+ const aStructural = STRUCTURAL_EDGE_TYPES.has(a);
208
+ const bStructural = STRUCTURAL_EDGE_TYPES.has(b);
209
+ if (aStructural !== bStructural) return aStructural ? 1 : -1;
210
+ return a.localeCompare(b);
211
+ })
212
+ .map(([edgeType, edgeList]) => ({ edgeType, edges: edgeList }));
213
+ }
214
+
215
+ // ---------------------------------------------------------------------------
216
+ // Display helpers
217
+ // ---------------------------------------------------------------------------
218
+
219
+ /**
220
+ * Get display name for a node based on its type.
221
+ *
222
+ * Special cases:
223
+ * - HTTP routes: "METHOD /path"
224
+ * - HTTP requests: "METHOD url"
225
+ * - Socket.IO: event name
226
+ * - Default: node.name or node.id
227
+ */
228
+ export function getNodeDisplayName(node: BaseNodeRecord): string {
229
+ // HTTP nodes: method + path/url
230
+ if (node.type === 'http:route') {
231
+ const method = node.method as string | undefined;
232
+ const path = node.path as string | undefined;
233
+ if (method && path) return `${method} ${path}`;
234
+ }
235
+ if (node.type === 'http:request') {
236
+ const method = node.method as string | undefined;
237
+ const url = node.url as string | undefined;
238
+ if (method && url) return `${method} ${url}`;
239
+ }
240
+
241
+ // Socket.IO: event name
242
+ if (node.type === 'socketio:emit' || node.type === 'socketio:on') {
243
+ const event = node.event as string | undefined;
244
+ if (event) return event;
245
+ }
246
+
247
+ // Anonymous arrows/expressions → λ
248
+ if (node.name === '<arrow>' || node.name === '<expression>') return 'λ';
249
+
250
+ // Default: name or ID fallback
251
+ if (node.name && !node.name.startsWith('{')) return node.name;
252
+ return node.id;
253
+ }
254
+
255
+ /**
256
+ * Format edge metadata for inline display (only meaningful fields).
257
+ *
258
+ * Returns a string like " [arg0]" or "" if no relevant metadata.
259
+ */
260
+ export function formatEdgeMetadata(edge: EdgeRecord): string {
261
+ const parts: string[] = [];
262
+ const meta = edge.metadata || {};
263
+
264
+ if (edge.type === 'PASSES_ARGUMENT' || edge.type === 'RECEIVES_ARGUMENT') {
265
+ if ('argIndex' in meta) {
266
+ parts.push(`arg${meta.argIndex}`);
267
+ }
268
+ }
269
+ if (edge.type === 'FLOWS_INTO') {
270
+ if ('mutationMethod' in meta) parts.push(`via ${meta.mutationMethod}`);
271
+ }
272
+ if (edge.type === 'HAS_PROPERTY') {
273
+ if ('propertyName' in meta) parts.push(`key: ${meta.propertyName}`);
274
+ }
275
+ if (edge.type === 'ITERATES_OVER') {
276
+ if ('iterates' in meta) parts.push(`${meta.iterates}`);
277
+ }
278
+
279
+ return parts.length > 0 ? ` [${parts.join(', ')}]` : '';
280
+ }
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Find all CALL and METHOD_CALL nodes inside a function.
3
+ *
4
+ * Supports two graph layouts:
5
+ *
6
+ * Layout A (scope chain — legacy JS analyzer):
7
+ * ```
8
+ * FUNCTION -[HAS_SCOPE]-> SCOPE -[CONTAINS]-> CALL
9
+ * SCOPE -[CONTAINS]-> METHOD_CALL
10
+ * SCOPE -[CONTAINS]-> SCOPE (nested blocks)
11
+ * ```
12
+ *
13
+ * Layout B (direct edges — Rust orchestrator):
14
+ * ```
15
+ * FUNCTION -[AWAITS|RETURNS|THROWS|...]--> CALL / METHOD_CALL
16
+ * ```
17
+ * The orchestrator links functions directly to their call nodes via
18
+ * semantic edge types (AWAITS for async calls, RETURNS for return
19
+ * expressions, THROWS for throw expressions, etc.).
20
+ *
21
+ * Algorithm:
22
+ * 1. Try HAS_SCOPE path first (Layout A)
23
+ * 2. If no HAS_SCOPE edges, fall back to direct edges (Layout B):
24
+ * collect all outgoing edges, keep those targeting CALL/METHOD_CALL nodes
25
+ * 3. For each call, check CALLS edge to determine if resolved
26
+ * 4. If transitive=true, recursively follow resolved CALLS edges
27
+ *
28
+ * Performance: O(S + C) where S = scopes, C = calls
29
+ * For functions with 100 calls, expect ~200 DB operations.
30
+ *
31
+ * @module queries/findCallsInFunction
32
+ */
33
+
34
+ import type { CallInfo, FindCallsOptions } from './types.js';
35
+
36
+ /**
37
+ * Graph backend interface (minimal surface)
38
+ */
39
+ interface GraphBackend {
40
+ getNode(id: string): Promise<{
41
+ id: string;
42
+ type: string;
43
+ name?: string;
44
+ file?: string;
45
+ line?: number;
46
+ object?: string;
47
+ } | null>;
48
+ getOutgoingEdges(
49
+ nodeId: string,
50
+ edgeTypes: string[] | null
51
+ ): Promise<Array<{ src: string; dst: string; type: string }>>;
52
+ }
53
+
54
+ /**
55
+ * Maximum BFS depth for downward scope traversal.
56
+ *
57
+ * Each depth level = one CONTAINS hop through nested scopes.
58
+ * Typical function bodies: 2-5 scope levels (if → loop → try → ...).
59
+ * Set to 10 to cover deep nesting while bounding traversal in malformed graphs.
60
+ */
61
+ const DEFAULT_MAX_SCOPE_DEPTH = 10;
62
+
63
+ /**
64
+ * Find all CALL and METHOD_CALL nodes inside a function.
65
+ *
66
+ * @param backend - Graph backend for queries
67
+ * @param functionId - ID of the FUNCTION node
68
+ * @param options - Options for traversal
69
+ * @returns Array of CallInfo objects
70
+ */
71
+ export async function findCallsInFunction(
72
+ backend: GraphBackend,
73
+ functionId: string,
74
+ options: FindCallsOptions = {}
75
+ ): Promise<CallInfo[]> {
76
+ const {
77
+ maxDepth = DEFAULT_MAX_SCOPE_DEPTH,
78
+ transitive = false,
79
+ transitiveDepth = 5,
80
+ } = options;
81
+
82
+ const calls: CallInfo[] = [];
83
+ const visited = new Set<string>();
84
+ const seenTargets = new Set<string>(); // For deduplication in transitive mode
85
+
86
+ // Add the starting function to seenTargets to prevent cycles back to it
87
+ if (transitive) {
88
+ seenTargets.add(functionId);
89
+ }
90
+
91
+ // Step 1: Try HAS_SCOPE path (Layout A — scope chain)
92
+ const hasScopeEdges = await backend.getOutgoingEdges(functionId, ['HAS_SCOPE']);
93
+
94
+ if (hasScopeEdges.length > 0) {
95
+ // Layout A: BFS through scope chain
96
+ const queue: Array<{ id: string; depth: number }> = [];
97
+
98
+ for (const edge of hasScopeEdges) {
99
+ queue.push({ id: edge.dst, depth: 0 });
100
+ }
101
+
102
+ while (queue.length > 0) {
103
+ const { id, depth } = queue.shift()!;
104
+
105
+ if (visited.has(id) || depth > maxDepth) continue;
106
+ visited.add(id);
107
+
108
+ const containsEdges = await backend.getOutgoingEdges(id, ['CONTAINS']);
109
+
110
+ for (const edge of containsEdges) {
111
+ const child = await backend.getNode(edge.dst);
112
+ if (!child) continue;
113
+
114
+ if (child.type === 'CALL' || child.type === 'METHOD_CALL') {
115
+ const callInfo = await buildCallInfo(backend, child, 0);
116
+ calls.push(callInfo);
117
+
118
+ if (transitive && callInfo.resolved && callInfo.target) {
119
+ await collectTransitiveCalls(
120
+ backend,
121
+ callInfo.target.id,
122
+ 1,
123
+ transitiveDepth,
124
+ calls,
125
+ seenTargets
126
+ );
127
+ }
128
+ }
129
+
130
+ // Continue into nested scopes, but NOT into nested functions/classes
131
+ if (child.type === 'SCOPE') {
132
+ queue.push({ id: child.id, depth: depth + 1 });
133
+ }
134
+ }
135
+ }
136
+ } else {
137
+ // Layout B: Direct edges from FUNCTION to CALL/METHOD_CALL nodes
138
+ // The Rust orchestrator links functions to calls via semantic edge types
139
+ // (AWAITS, RETURNS, THROWS, etc.) instead of HAS_SCOPE -> CONTAINS.
140
+ const allOutgoing = await backend.getOutgoingEdges(functionId, null);
141
+
142
+ for (const edge of allOutgoing) {
143
+ const child = await backend.getNode(edge.dst);
144
+ if (!child) continue;
145
+
146
+ if (child.type === 'CALL' || child.type === 'METHOD_CALL') {
147
+ const callInfo = await buildCallInfo(backend, child, 0);
148
+ calls.push(callInfo);
149
+
150
+ if (transitive && callInfo.resolved && callInfo.target) {
151
+ await collectTransitiveCalls(
152
+ backend,
153
+ callInfo.target.id,
154
+ 1,
155
+ transitiveDepth,
156
+ calls,
157
+ seenTargets
158
+ );
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ return calls;
165
+ }
166
+
167
+ /**
168
+ * Build CallInfo from a call node
169
+ */
170
+ async function buildCallInfo(
171
+ backend: GraphBackend,
172
+ callNode: { id: string; type: string; name?: string; file?: string; line?: number; object?: string },
173
+ depth: number
174
+ ): Promise<CallInfo> {
175
+ // Check for CALLS edge (resolved target)
176
+ const callsEdges = await backend.getOutgoingEdges(callNode.id, ['CALLS']);
177
+ const isResolved = callsEdges.length > 0;
178
+
179
+ let target = undefined;
180
+ if (isResolved) {
181
+ const targetNode = await backend.getNode(callsEdges[0].dst);
182
+ if (targetNode) {
183
+ target = {
184
+ id: targetNode.id,
185
+ name: targetNode.name ?? '<anonymous>',
186
+ file: targetNode.file,
187
+ line: targetNode.line,
188
+ };
189
+ }
190
+ }
191
+
192
+ return {
193
+ id: callNode.id,
194
+ name: callNode.name ?? '<unknown>',
195
+ type: callNode.type as 'CALL' | 'METHOD_CALL',
196
+ object: callNode.object,
197
+ resolved: isResolved,
198
+ target,
199
+ file: callNode.file,
200
+ line: callNode.line,
201
+ depth,
202
+ };
203
+ }
204
+
205
+ /**
206
+ * Recursively collect transitive calls
207
+ *
208
+ * Infinite loop prevention:
209
+ * - Track seen function IDs in seenTargets
210
+ * - Stop when we've seen a function before (handles recursion)
211
+ * - Stop at transitiveDepth limit
212
+ */
213
+ async function collectTransitiveCalls(
214
+ backend: GraphBackend,
215
+ functionId: string,
216
+ currentDepth: number,
217
+ maxTransitiveDepth: number,
218
+ calls: CallInfo[],
219
+ seenTargets: Set<string>
220
+ ): Promise<void> {
221
+ // Prevent infinite loops and limit depth
222
+ if (seenTargets.has(functionId) || currentDepth > maxTransitiveDepth) {
223
+ return;
224
+ }
225
+ seenTargets.add(functionId);
226
+
227
+ // Find calls in this function (non-transitive to avoid recursion)
228
+ const innerCalls = await findCallsInFunction(backend, functionId, {
229
+ maxDepth: DEFAULT_MAX_SCOPE_DEPTH,
230
+ transitive: false,
231
+ });
232
+
233
+ for (const call of innerCalls) {
234
+ // Add with updated depth
235
+ calls.push({ ...call, depth: currentDepth });
236
+
237
+ // Continue transitively if resolved
238
+ if (call.resolved && call.target) {
239
+ await collectTransitiveCalls(
240
+ backend,
241
+ call.target.id,
242
+ currentDepth + 1,
243
+ maxTransitiveDepth,
244
+ calls,
245
+ seenTargets
246
+ );
247
+ }
248
+ }
249
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Find the FUNCTION, CLASS, or MODULE that contains a node.
3
+ *
4
+ * Supports two graph layouts:
5
+ *
6
+ * Layout A (scope chain — legacy JS analyzer):
7
+ * ```
8
+ * CALL <- CONTAINS <- SCOPE <- ... <- SCOPE <- HAS_SCOPE <- FUNCTION
9
+ * VARIABLE <- DECLARES <- SCOPE <- ... <- SCOPE <- HAS_SCOPE <- FUNCTION
10
+ * ```
11
+ *
12
+ * Layout B (direct edges — Rust orchestrator):
13
+ * ```
14
+ * CALL <- AWAITS|RETURNS|THROWS <- FUNCTION
15
+ * ```
16
+ * The orchestrator links functions directly to their call nodes via
17
+ * semantic edge types. These incoming edges on a CALL node point
18
+ * directly back to the containing FUNCTION.
19
+ *
20
+ * Algorithm:
21
+ * 1. BFS up via CONTAINS, HAS_SCOPE, DECLARES (Layout A)
22
+ * 2. Also follow AWAITS, RETURNS, THROWS incoming edges (Layout B)
23
+ * 3. Stop when we find FUNCTION, CLASS, or MODULE
24
+ * 4. Prefer FUNCTION over MODULE (FUNCTION is more specific)
25
+ *
26
+ * @module queries/findContainingFunction
27
+ */
28
+
29
+ import type { CallerInfo } from './types.js';
30
+
31
+ /**
32
+ * Graph backend interface (minimal surface)
33
+ */
34
+ interface GraphBackend {
35
+ getNode(id: string): Promise<{
36
+ id: string;
37
+ type: string;
38
+ name?: string;
39
+ file?: string;
40
+ line?: number;
41
+ } | null>;
42
+ getIncomingEdges(
43
+ nodeId: string,
44
+ edgeTypes: string[] | null
45
+ ): Promise<Array<{ src: string; dst: string; type: string }>>;
46
+ }
47
+
48
+ /**
49
+ * Maximum BFS depth for upward containment traversal.
50
+ *
51
+ * Each depth level = one CONTAINS/HAS_SCOPE/DECLARES hop.
52
+ * Typical real-world nesting: 3-7 levels (function body → if → loop → try → ...).
53
+ * Set to 15 to handle pathological cases (deeply nested callbacks, complex control flow)
54
+ * while still bounding traversal in malformed graphs.
55
+ */
56
+ const DEFAULT_MAX_DEPTH = 15;
57
+
58
+ /**
59
+ * Find the FUNCTION, CLASS, or MODULE that contains a node.
60
+ *
61
+ * @param backend - Graph backend for queries
62
+ * @param nodeId - ID of the node to find container for
63
+ * @param maxDepth - Maximum traversal depth (default: {@link DEFAULT_MAX_DEPTH}).
64
+ * Traversal visits depths 0 through maxDepth inclusive.
65
+ * @returns CallerInfo or null if no container found within maxDepth hops
66
+ */
67
+ export async function findContainingFunction(
68
+ backend: GraphBackend,
69
+ nodeId: string,
70
+ maxDepth: number = DEFAULT_MAX_DEPTH
71
+ ): Promise<CallerInfo | null> {
72
+ const visited = new Set<string>();
73
+ const queue: Array<{ id: string; depth: number }> = [{ id: nodeId, depth: 0 }];
74
+
75
+ // Collect all candidates so we can prefer FUNCTION over MODULE
76
+ let bestCandidate: CallerInfo | null = null;
77
+
78
+ while (queue.length > 0) {
79
+ const { id, depth } = queue.shift()!;
80
+
81
+ if (visited.has(id) || depth > maxDepth) continue;
82
+ visited.add(id);
83
+
84
+ // Layout A edges: CONTAINS, HAS_SCOPE, DECLARES (scope chain traversal)
85
+ // Layout B edges: AWAITS, RETURNS, THROWS (direct function-to-call edges)
86
+ const edges = await backend.getIncomingEdges(id, [
87
+ 'CONTAINS', 'HAS_SCOPE', 'DECLARES',
88
+ 'AWAITS', 'RETURNS', 'THROWS',
89
+ ]);
90
+
91
+ for (const edge of edges) {
92
+ const parentNode = await backend.getNode(edge.src);
93
+ if (!parentNode || visited.has(parentNode.id)) continue;
94
+
95
+ // Found container!
96
+ if (parentNode.type === 'FUNCTION' || parentNode.type === 'CLASS' || parentNode.type === 'MODULE') {
97
+ const candidate: CallerInfo = {
98
+ id: parentNode.id,
99
+ name: parentNode.name || '<anonymous>',
100
+ type: parentNode.type,
101
+ file: parentNode.file,
102
+ line: parentNode.line,
103
+ };
104
+
105
+ // FUNCTION/CLASS is most specific — return immediately
106
+ if (parentNode.type === 'FUNCTION' || parentNode.type === 'CLASS') {
107
+ return candidate;
108
+ }
109
+
110
+ // MODULE is less specific — keep as fallback, continue searching
111
+ if (!bestCandidate) {
112
+ bestCandidate = candidate;
113
+ }
114
+
115
+ continue;
116
+ }
117
+
118
+ // Continue searching
119
+ queue.push({ id: parentNode.id, depth: depth + 1 });
120
+ }
121
+ }
122
+
123
+ return bestCandidate;
124
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Graph Query Utilities
3
+ *
4
+ * Shared utilities for querying the code graph.
5
+ * Used by MCP, CLI, and other tools.
6
+ *
7
+ * @module queries
8
+ */
9
+
10
+ export { findCallsInFunction } from './findCallsInFunction.js';
11
+ export { findContainingFunction } from './findContainingFunction.js';
12
+ export { traceValues, aggregateValues, NONDETERMINISTIC_PATTERNS, NONDETERMINISTIC_OBJECTS } from './traceValues.js';
13
+ export { traceDataflow, traceForwardBFS, traceBackwardBFS } from './traceDataflow.js';
14
+ export {
15
+ buildNodeContext,
16
+ getNodeDisplayName,
17
+ formatEdgeMetadata,
18
+ STRUCTURAL_EDGE_TYPES,
19
+ } from './NodeContext.js';
20
+
21
+ export type { CallInfo, CallerInfo, FindCallsOptions } from './types.js';
22
+ export type {
23
+ EdgeWithNode,
24
+ EdgeGroup,
25
+ SourcePreview,
26
+ NodeContext,
27
+ BuildNodeContextOptions,
28
+ } from './NodeContext.js';
29
+ export type {
30
+ TracedValue,
31
+ ValueSource,
32
+ UnknownReason,
33
+ TraceValuesOptions,
34
+ ValueSetResult,
35
+ TraceValuesGraphBackend,
36
+ NondeterministicPattern,
37
+ } from './types.js';
38
+ export type {
39
+ DataflowNode,
40
+ DataflowEdge,
41
+ DataflowBackend,
42
+ TraceDataflowOptions,
43
+ TraceDataflowResult,
44
+ } from './traceDataflow.js';