@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,458 @@
1
+ /**
2
+ * Trace Narrative Renderer
3
+ *
4
+ * Transforms flat TraceDataflowResult[] into module-grouped,
5
+ * operator-annotated narrative using Grafema DSL operators.
6
+ *
7
+ * Same renderer for CLI and MCP — the output is a plain string.
8
+ *
9
+ * LOD levels:
10
+ * summary — shape + per-file type counts only (always fits on screen)
11
+ * normal — auto-tiered compression (default, max 35 lines)
12
+ * full — every node listed, grouped by file, no budget
13
+ *
14
+ * Operators are inferred from node type + direction. Since TraceDataflowResult
15
+ * has no edge data, these are approximate — use `describe` for precise
16
+ * edge-based operators.
17
+ *
18
+ * @module notation/traceRenderer
19
+ */
20
+
21
+ import type { TraceDataflowResult, DataflowNode } from '../queries/traceDataflow.js';
22
+ import { generateLegend } from './archetypes.js';
23
+
24
+ // === PUBLIC TYPES ===
25
+
26
+ export type TraceDetail = 'summary' | 'normal' | 'full';
27
+
28
+ export interface TraceNarrativeOptions {
29
+ /** Level of detail: summary, normal (default), full */
30
+ detail?: TraceDetail;
31
+ }
32
+
33
+ // === CONSTANTS ===
34
+
35
+ /** Node types to skip — internal graph plumbing, not meaningful to users. */
36
+ const NOISE_TYPES = new Set(['REFERENCE', 'EXPRESSION', 'LITERAL']);
37
+
38
+ /** Hard budget for normal detail: maximum lines in body. */
39
+ const MAX_LINES = 35;
40
+
41
+ // === LEGEND (from single source of truth) ===
42
+
43
+ function lodHint(currentDetail: TraceDetail): string {
44
+ switch (currentDetail) {
45
+ case 'summary':
46
+ return 'Use detail="normal" for node list, detail="full" for complete chain';
47
+ case 'normal':
48
+ return 'Use detail="full" for complete chain, detail="summary" for overview';
49
+ case 'full':
50
+ return 'Use detail="summary" for overview, detail="normal" for compressed view';
51
+ }
52
+ }
53
+
54
+ // === OPERATOR MAPPING ===
55
+
56
+ /**
57
+ * Unified operator vocabulary — uses the same operators as `describe` tool.
58
+ * Since trace has no edge data, operators are inferred from node type + direction.
59
+ *
60
+ * Operators (from archetypes.ts):
61
+ * > flow_out (calls, passes, returns)
62
+ * < flow_in (reads, receives, assigned from)
63
+ * => write (persistent side effect)
64
+ * o- depends (imports)
65
+ * >x exception (throws)
66
+ * ?| gates (condition)
67
+ * {} contains (class membership)
68
+ */
69
+ function getOperator(nodeType: string, direction: 'forward' | 'backward'): string {
70
+ const isForward = direction === 'forward';
71
+
72
+ switch (nodeType) {
73
+ case 'PARAMETER':
74
+ case 'VARIABLE':
75
+ case 'CALL':
76
+ case 'RETURN':
77
+ case 'EXPORT':
78
+ return isForward ? '>' : '<';
79
+ case 'IMPORT':
80
+ return isForward ? '<' : '>';
81
+ case 'FUNCTION':
82
+ case 'METHOD':
83
+ return 'o-';
84
+ case 'CLASS':
85
+ return '{}';
86
+ case 'CONSTANT':
87
+ return '=>';
88
+ case 'BRANCH':
89
+ return '?|';
90
+ default:
91
+ return isForward ? '>' : '<';
92
+ }
93
+ }
94
+
95
+ // === SHAPE DETECTION ===
96
+
97
+ type TraceShape = 'chain' | 'fan-out' | 'fan-in' | 'diamond';
98
+
99
+ function detectShape(
100
+ startFile: string | undefined,
101
+ fileGroups: Map<string, DataflowNode[]>,
102
+ ): TraceShape {
103
+ const totalNodes = Array.from(fileGroups.values()).reduce((s, g) => s + g.length, 0);
104
+ const fileCount = fileGroups.size;
105
+
106
+ // Chain: ≤2 files, ≤10 nodes
107
+ if (fileCount <= 2 && totalNodes <= 10) return 'chain';
108
+
109
+ const startFileGroup = startFile ? fileGroups.get(startFile) : undefined;
110
+ const startFileNodeCount = startFileGroup?.length ?? 0;
111
+ const otherFiles = fileCount - (startFileGroup ? 1 : 0);
112
+
113
+ // Fan-out: most nodes from 1 source, 3+ target files
114
+ if (otherFiles >= 3 && startFileNodeCount <= totalNodes * 0.3) return 'fan-out';
115
+
116
+ // Fan-in: 3+ source files converging to 1 target
117
+ for (const [, nodes] of fileGroups) {
118
+ if (nodes.length > totalNodes * 0.5 && fileCount >= 3) return 'fan-in';
119
+ }
120
+
121
+ // Diamond: fan-out then fan-in (heuristic: 4+ files with spread distribution)
122
+ if (fileCount >= 4) return 'diamond';
123
+
124
+ return 'chain';
125
+ }
126
+
127
+ function shapeLabel(shape: TraceShape, fileCount: number, nodeCount: number): string {
128
+ switch (shape) {
129
+ case 'chain': return `chain (${nodeCount} nodes reached)`;
130
+ case 'fan-out': return `fan-out across ${fileCount} modules (${nodeCount} nodes reached)`;
131
+ case 'fan-in': return `fan-in from ${fileCount} modules (${nodeCount} nodes reached)`;
132
+ case 'diamond': return `diamond across ${fileCount} modules (${nodeCount} nodes reached)`;
133
+ }
134
+ }
135
+
136
+ // === COMPRESSION TIERS ===
137
+
138
+ function getTier(totalReached: number): number {
139
+ if (totalReached <= 5) return 1;
140
+ if (totalReached <= 30) return 2;
141
+ if (totalReached <= 100) return 3;
142
+ if (totalReached <= 300) return 4;
143
+ return 5;
144
+ }
145
+
146
+ // === GROUPING ===
147
+
148
+ function groupByFile(nodes: DataflowNode[]): Map<string, DataflowNode[]> {
149
+ const groups = new Map<string, DataflowNode[]>();
150
+ for (const node of nodes) {
151
+ const file = node.file || '<unknown>';
152
+ let group = groups.get(file);
153
+ if (!group) {
154
+ group = [];
155
+ groups.set(file, group);
156
+ }
157
+ group.push(node);
158
+ }
159
+ // Sort nodes within each group by line number
160
+ for (const [, group] of groups) {
161
+ group.sort((a, b) => (a.line ?? 0) - (b.line ?? 0));
162
+ }
163
+ return groups;
164
+ }
165
+
166
+ function groupByDirectory(nodes: DataflowNode[]): Map<string, DataflowNode[]> {
167
+ const groups = new Map<string, DataflowNode[]>();
168
+ for (const node of nodes) {
169
+ const file = node.file || '<unknown>';
170
+ const lastSlash = file.lastIndexOf('/');
171
+ const dir = lastSlash >= 0 ? file.substring(0, lastSlash) : '.';
172
+ let group = groups.get(dir);
173
+ if (!group) {
174
+ group = [];
175
+ groups.set(dir, group);
176
+ }
177
+ group.push(node);
178
+ }
179
+ return groups;
180
+ }
181
+
182
+ function filterNoise(nodes: DataflowNode[]): DataflowNode[] {
183
+ return nodes.filter(n => !NOISE_TYPES.has(n.type));
184
+ }
185
+
186
+ // === PER-FILE TYPE SUMMARY ===
187
+
188
+ function typeSummary(nodes: DataflowNode[]): string {
189
+ const types = new Map<string, number>();
190
+ for (const n of nodes) {
191
+ types.set(n.type, (types.get(n.type) || 0) + 1);
192
+ }
193
+ return Array.from(types.entries())
194
+ .map(([t, c]) => `${c} ${t}`)
195
+ .join(', ');
196
+ }
197
+
198
+ // === RENDERING ===
199
+
200
+ function renderNodeLine(node: DataflowNode, direction: 'forward' | 'backward'): string {
201
+ const operator = getOperator(node.type, direction);
202
+ const name = node.name || '(anonymous)';
203
+ return ` ${operator} ${name} (${node.type})`;
204
+ }
205
+
206
+ // --- detail="full": every node, grouped by file, no budget ---
207
+
208
+ function renderFull(
209
+ fileGroups: Map<string, DataflowNode[]>,
210
+ direction: 'forward' | 'backward',
211
+ lines: string[],
212
+ ): void {
213
+ for (const [file, nodes] of fileGroups) {
214
+ lines.push('');
215
+ lines.push(` ${file}`);
216
+ for (const node of nodes) {
217
+ lines.push(renderNodeLine(node, direction));
218
+ }
219
+ }
220
+ }
221
+
222
+ // --- detail="summary": per-file type counts only ---
223
+
224
+ function renderSummary(
225
+ fileGroups: Map<string, DataflowNode[]>,
226
+ lines: string[],
227
+ ): void {
228
+ for (const [file, nodes] of fileGroups) {
229
+ lines.push(` ${file} — ${nodes.length} nodes (${typeSummary(nodes)})`);
230
+ }
231
+ }
232
+
233
+ // --- detail="normal": auto-tiered ---
234
+
235
+ function renderTier1(
236
+ nodes: DataflowNode[],
237
+ direction: 'forward' | 'backward',
238
+ lines: string[],
239
+ ): void {
240
+ for (const node of nodes) {
241
+ lines.push(renderNodeLine(node, direction));
242
+ }
243
+ }
244
+
245
+ function renderTier2(
246
+ fileGroups: Map<string, DataflowNode[]>,
247
+ direction: 'forward' | 'backward',
248
+ lines: string[],
249
+ ): void {
250
+ const budgetForContent = MAX_LINES - 3;
251
+ let remaining = 0;
252
+ let remainingModules = 0;
253
+
254
+ for (const [file, nodes] of fileGroups) {
255
+ if (lines.length >= budgetForContent) {
256
+ remaining += nodes.length;
257
+ remainingModules++;
258
+ continue;
259
+ }
260
+ lines.push('');
261
+ lines.push(` ${file}`);
262
+ for (const node of nodes) {
263
+ if (lines.length >= budgetForContent) {
264
+ remaining += 1;
265
+ continue;
266
+ }
267
+ lines.push(renderNodeLine(node, direction));
268
+ }
269
+ }
270
+
271
+ if (remaining > 0) {
272
+ lines.push('');
273
+ lines.push(` ... and ${remainingModules} more modules (${remaining} nodes)`);
274
+ }
275
+ }
276
+
277
+ function renderTier3(
278
+ fileGroups: Map<string, DataflowNode[]>,
279
+ direction: 'forward' | 'backward',
280
+ lines: string[],
281
+ ): void {
282
+ const MAX_PER_FILE = 3;
283
+ const budgetForContent = MAX_LINES - 3;
284
+ let remaining = 0;
285
+ let remainingModules = 0;
286
+
287
+ for (const [file, nodes] of fileGroups) {
288
+ if (lines.length >= budgetForContent) {
289
+ remaining += nodes.length;
290
+ remainingModules++;
291
+ continue;
292
+ }
293
+ lines.push('');
294
+ lines.push(` ${file}`);
295
+ const show = nodes.slice(0, MAX_PER_FILE);
296
+ const hidden = nodes.length - show.length;
297
+ for (const node of show) {
298
+ if (lines.length >= budgetForContent) {
299
+ remaining++;
300
+ continue;
301
+ }
302
+ lines.push(renderNodeLine(node, direction));
303
+ }
304
+ if (hidden > 0 && lines.length < budgetForContent) {
305
+ lines.push(` ... +${hidden} more`);
306
+ }
307
+ }
308
+
309
+ if (remaining > 0) {
310
+ lines.push('');
311
+ lines.push(` ... and ${remainingModules} more modules (${remaining} nodes)`);
312
+ }
313
+ }
314
+
315
+ function renderTier4(
316
+ fileGroups: Map<string, DataflowNode[]>,
317
+ lines: string[],
318
+ ): void {
319
+ let remainingModules = 0;
320
+ let remainingNodes = 0;
321
+
322
+ for (const [file, nodes] of fileGroups) {
323
+ if (lines.length >= MAX_LINES - 2) {
324
+ remainingModules++;
325
+ remainingNodes += nodes.length;
326
+ continue;
327
+ }
328
+ lines.push(` ${file} — ${nodes.length} nodes (${typeSummary(nodes)})`);
329
+ }
330
+
331
+ if (remainingModules > 0) {
332
+ lines.push(` ... and ${remainingModules} more modules (${remainingNodes} nodes)`);
333
+ }
334
+ }
335
+
336
+ function renderTier5(
337
+ nodes: DataflowNode[],
338
+ lines: string[],
339
+ ): void {
340
+ const dirGroups = groupByDirectory(nodes);
341
+
342
+ let remainingDirs = 0;
343
+ let remainingNodes = 0;
344
+
345
+ for (const [dir, dirNodes] of dirGroups) {
346
+ if (lines.length >= MAX_LINES - 2) {
347
+ remainingDirs++;
348
+ remainingNodes += dirNodes.length;
349
+ continue;
350
+ }
351
+ const files = new Set(dirNodes.map(n => n.file || '<unknown>'));
352
+ lines.push(` ${dir}/ — ${files.size} files, ${dirNodes.length} nodes (${typeSummary(dirNodes)})`);
353
+ }
354
+
355
+ if (remainingDirs > 0) {
356
+ lines.push(` ... and ${remainingDirs} more directories (${remainingNodes} nodes)`);
357
+ }
358
+ }
359
+
360
+ // === MAIN EXPORT ===
361
+
362
+ /**
363
+ * Render trace dataflow results as a human-readable narrative.
364
+ *
365
+ * Transforms flat BFS results into a module-grouped, operator-annotated
366
+ * narrative using Grafema DSL operators. Infers operators from node type
367
+ * and trace direction since edge info is not available in TraceDataflowResult.
368
+ *
369
+ * @param results - Array of TraceDataflowResult from traceDataflow()
370
+ * @param sourceName - Display name for the seed/source node
371
+ * @param options - Rendering options (detail level)
372
+ * @returns Formatted narrative string with legend and LOD hints
373
+ */
374
+ export function renderTraceNarrative(
375
+ results: TraceDataflowResult[],
376
+ sourceName: string,
377
+ options: TraceNarrativeOptions = {},
378
+ ): string {
379
+ const detail = options.detail ?? 'normal';
380
+
381
+ if (results.length === 0) return `No dataflow results for "${sourceName}"`;
382
+
383
+ const output: string[] = [];
384
+
385
+ for (const result of results) {
386
+ const arrow = result.direction === 'forward' ? '→' : '←';
387
+
388
+ // detail="full" shows everything BFS found — no noise filtering
389
+ const displayNodes = detail === 'full' ? result.reached : filterNoise(result.reached);
390
+
391
+ if (displayNodes.length === 0) {
392
+ if (result.reached.length > 0) {
393
+ // BFS found nodes but all were noise types — inform the user
394
+ output.push(
395
+ `"${sourceName}" ${arrow} ${result.reached.length} nodes reached (all internal references — use detail="full" to see)`
396
+ );
397
+ } else {
398
+ output.push(
399
+ `"${sourceName}" ${arrow} no reachable nodes`
400
+ );
401
+ }
402
+ output.push('');
403
+ continue;
404
+ }
405
+
406
+ const fileGroups = groupByFile(displayNodes);
407
+ const shape = detectShape(result.startNode.file, fileGroups);
408
+ // Use display count in header — matches what user sees in body
409
+ const displayCount = displayNodes.length;
410
+
411
+ output.push(
412
+ `"${sourceName}" ${arrow} ${shapeLabel(shape, fileGroups.size, displayCount)}`
413
+ );
414
+
415
+ const lines: string[] = [];
416
+
417
+ if (detail === 'full') {
418
+ renderFull(fileGroups, result.direction, lines);
419
+ } else if (detail === 'summary') {
420
+ renderSummary(fileGroups, lines);
421
+ } else {
422
+ // normal: auto-tiered
423
+ const tier = getTier(displayCount);
424
+ switch (tier) {
425
+ case 1:
426
+ renderTier1(displayNodes, result.direction, lines);
427
+ break;
428
+ case 2:
429
+ renderTier2(fileGroups, result.direction, lines);
430
+ break;
431
+ case 3:
432
+ renderTier3(fileGroups, result.direction, lines);
433
+ break;
434
+ case 4:
435
+ renderTier4(fileGroups, lines);
436
+ break;
437
+ case 5:
438
+ renderTier5(displayNodes, lines);
439
+ break;
440
+ }
441
+ }
442
+
443
+ output.push(...lines);
444
+ output.push('');
445
+ }
446
+
447
+ // Trim trailing empty line
448
+ while (output.length > 0 && output[output.length - 1] === '') {
449
+ output.pop();
450
+ }
451
+
452
+ // Append legend (from archetypes.ts — single source of truth) + LOD hint
453
+ output.push('');
454
+ output.push(generateLegend());
455
+ output.push(lodHint(detail));
456
+
457
+ return output.join('\n');
458
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Notation Types — interfaces for the Grafema DSL rendering engine
3
+ *
4
+ * The DSL is output-only: graph data → compact visual notation.
5
+ * Datalog remains the query language.
6
+ *
7
+ * @module notation/types
8
+ */
9
+
10
+ import type { BaseNodeRecord, EdgeRecord } from '@grafema/types';
11
+
12
+ export type Archetype =
13
+ | 'contains'
14
+ | 'flow_out'
15
+ | 'flow_in'
16
+ | 'write'
17
+ | 'exception'
18
+ | 'depends'
19
+ | 'publishes'
20
+ | 'gates'
21
+ | 'governs';
22
+
23
+ export interface EdgeMapping {
24
+ archetype: Archetype;
25
+ /** ASCII operator: >, <, =>, >x, o-, ~>>, ?|, |=, '' for containment */
26
+ operator: string;
27
+ /** Human-readable verb: "calls", "reads", "writes"... */
28
+ verb: string;
29
+ /** Rendering order within blocks */
30
+ sortOrder: number;
31
+ }
32
+
33
+ export interface DescribeOptions {
34
+ /** LOD: 0=names, 1=edges (default), 2=nested+fold, 3=nested (exact) */
35
+ depth?: number;
36
+ /** Only show these archetypes */
37
+ archetypeFilter?: Archetype[];
38
+ /** Max items before summarization (default 7) */
39
+ budget?: number;
40
+ /** Append file:line to node headers */
41
+ includeLocations?: boolean;
42
+ }
43
+
44
+ export interface SubgraphData {
45
+ rootNodes: BaseNodeRecord[];
46
+ edges: EdgeRecord[];
47
+ nodeMap: Map<string, BaseNodeRecord>;
48
+ }
49
+
50
+ export interface FoldMeta {
51
+ kind: 'fold' | 'chain' | 'dispatch' | 'leaf-repeat' | 'trivial-group' | 'datum-inline';
52
+ count: number;
53
+ label?: string;
54
+ sourceSummary?: string;
55
+ chainSteps?: string[];
56
+ chainSource?: string;
57
+ branches?: Array<{ pattern: string; handler: string }>;
58
+ names?: string[];
59
+ }
60
+
61
+ export interface NotationBlock {
62
+ nodeId: string;
63
+ displayName: string;
64
+ nodeType: string;
65
+ lines: NotationLine[];
66
+ children: NotationBlock[];
67
+ location?: string;
68
+ /** Set by fold engine — serializer renders fold summaries differently */
69
+ foldMeta?: FoldMeta;
70
+ }
71
+
72
+ export interface NotationLine {
73
+ operator: string;
74
+ verb: string;
75
+ targets: string[];
76
+ sortOrder: number;
77
+ /** Optional prefix modifier, e.g. '[]' for edges inside loops */
78
+ modifier?: string;
79
+ }