@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,684 @@
1
+ /**
2
+ * GuaranteeManager - управление гарантиями/инвариантами кода
3
+ *
4
+ * GUARANTEE ноды хранят Datalog правила, которые код должен соблюдать.
5
+ * GOVERNS edges связывают гарантии с модулями, к которым они применяются.
6
+ *
7
+ * Workflow:
8
+ * 1. Создать гарантию (create) → GUARANTEE нода + GOVERNS edges
9
+ * 2. Проверить (check) → выполнить Datalog rule, найти нарушения
10
+ * 3. Экспортировать (export) → сохранить в YAML для version control
11
+ * 4. Импортировать (import) → загрузить из YAML в граф
12
+ * 5. Drift detection → сравнить граф с файлом
13
+ */
14
+
15
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
16
+ import { join, isAbsolute, relative } from 'path';
17
+ import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
18
+ import { minimatch } from 'minimatch';
19
+ import { brandNodeInternal } from './brandNodeInternal.js';
20
+ import type { BaseNodeRecord } from '@grafema/types';
21
+
22
+ /**
23
+ * Severity level for guarantees
24
+ */
25
+ export type GuaranteeSeverity = 'error' | 'warning' | 'info';
26
+
27
+ /**
28
+ * Guarantee definition
29
+ */
30
+ export interface GuaranteeDefinition {
31
+ id: string;
32
+ name?: string;
33
+ rule: string;
34
+ severity?: GuaranteeSeverity;
35
+ governs?: string[];
36
+ }
37
+
38
+ /**
39
+ * Guarantee node structure
40
+ */
41
+ export interface GuaranteeNode {
42
+ id: string;
43
+ type: 'GUARANTEE';
44
+ name: string;
45
+ rule: string;
46
+ severity: GuaranteeSeverity;
47
+ governs: string[];
48
+ version: 'meta';
49
+ createdAt: number;
50
+ governedModules?: string[];
51
+ }
52
+
53
+ /**
54
+ * Edge structure
55
+ */
56
+ export interface GraphEdge {
57
+ type: string;
58
+ src: string;
59
+ dst: string;
60
+ }
61
+
62
+ /**
63
+ * Module node
64
+ */
65
+ export interface ModuleNode {
66
+ id: string;
67
+ type: string;
68
+ file?: string;
69
+ }
70
+
71
+ /**
72
+ * Violation binding
73
+ */
74
+ export interface ViolationBinding {
75
+ name: string;
76
+ value: string;
77
+ }
78
+
79
+ /**
80
+ * Violation result from Datalog
81
+ */
82
+ export interface ViolationResult {
83
+ bindings?: ViolationBinding[];
84
+ }
85
+
86
+ /**
87
+ * Enriched violation info
88
+ */
89
+ export interface EnrichedViolation {
90
+ nodeId: string;
91
+ type: string;
92
+ name?: string;
93
+ file?: string;
94
+ line?: number;
95
+ }
96
+
97
+ /**
98
+ * Check result for a single guarantee
99
+ */
100
+ export interface GuaranteeCheckResult {
101
+ guaranteeId: string;
102
+ name: string;
103
+ severity: GuaranteeSeverity;
104
+ passed: boolean;
105
+ violationCount: number;
106
+ violations: EnrichedViolation[];
107
+ error: string | null;
108
+ checkDurationMs: number;
109
+ }
110
+
111
+ /**
112
+ * Check all result
113
+ */
114
+ export interface CheckAllResult {
115
+ total: number;
116
+ passed: number;
117
+ failed: number;
118
+ errors: number;
119
+ results: GuaranteeCheckResult[];
120
+ }
121
+
122
+ /**
123
+ * Import options
124
+ */
125
+ export interface ImportOptions {
126
+ clearExisting?: boolean;
127
+ }
128
+
129
+ /**
130
+ * Import result
131
+ */
132
+ export interface ImportResult {
133
+ imported: number;
134
+ skipped: number;
135
+ importedIds: string[];
136
+ skippedIds: string[];
137
+ }
138
+
139
+ /**
140
+ * Modified guarantee in drift
141
+ */
142
+ export interface ModifiedGuarantee {
143
+ id: string;
144
+ changes: string[];
145
+ }
146
+
147
+ /**
148
+ * Drift summary
149
+ */
150
+ export interface DriftSummary {
151
+ onlyInGraph: number;
152
+ onlyInFile: number;
153
+ modified: number;
154
+ unchanged: number;
155
+ }
156
+
157
+ /**
158
+ * Drift result
159
+ */
160
+ export interface DriftResult {
161
+ hasDrift: boolean;
162
+ summary: DriftSummary;
163
+ onlyInGraph: string[];
164
+ onlyInFile: string[];
165
+ modified: ModifiedGuarantee[];
166
+ unchanged: string[];
167
+ }
168
+
169
+ /**
170
+ * Export data format
171
+ */
172
+ export interface ExportData {
173
+ version: number;
174
+ exportedAt: string;
175
+ guarantees: Array<{
176
+ id: string;
177
+ name: string;
178
+ rule: string;
179
+ severity: GuaranteeSeverity;
180
+ governs: string[];
181
+ }>;
182
+ }
183
+
184
+ /**
185
+ * Graph interface for GuaranteeManager
186
+ */
187
+ export interface GuaranteeGraph {
188
+ addNode(node: GuaranteeNode): Promise<void>;
189
+ getNode(id: string): Promise<GuaranteeNode | ModuleNode | null>;
190
+ deleteNode(id: string): Promise<void>;
191
+ queryNodes(filter: { type: string }): AsyncIterable<GuaranteeNode | ModuleNode>;
192
+ addEdge(edge: GraphEdge): Promise<void>;
193
+ deleteEdge(src: string, dst: string, type: string): Promise<void>;
194
+ getOutgoingEdges(nodeId: string, types: string[]): Promise<GraphEdge[]>;
195
+ getIncomingEdges(nodeId: string, types: string[]): Promise<GraphEdge[]>;
196
+ checkGuarantee(rule: string): Promise<ViolationResult[]>;
197
+ }
198
+
199
+ export class GuaranteeManager {
200
+ private graph: GuaranteeGraph;
201
+ private projectPath: string;
202
+ private guaranteesFile: string;
203
+
204
+ constructor(graph: GuaranteeGraph, projectPath: string) {
205
+ this.graph = graph;
206
+ this.projectPath = projectPath;
207
+ this.guaranteesFile = join(projectPath, '.grafema', 'guarantees.yaml');
208
+ }
209
+
210
+ /**
211
+ * Создать новую гарантию
212
+ */
213
+ async create(guarantee: GuaranteeDefinition): Promise<GuaranteeNode> {
214
+ const { id, name, rule, severity = 'warning', governs = ['**/*.js'] } = guarantee;
215
+
216
+ if (!id || !rule) {
217
+ throw new Error('Guarantee must have id and rule');
218
+ }
219
+
220
+ // Создаём GUARANTEE ноду
221
+ const guaranteeNode = brandNodeInternal({
222
+ id: `GUARANTEE:${id}`,
223
+ type: 'GUARANTEE',
224
+ name: name || id,
225
+ rule,
226
+ severity,
227
+ governs,
228
+ version: 'meta',
229
+ createdAt: Date.now(),
230
+ } as BaseNodeRecord) as unknown as GuaranteeNode;
231
+
232
+ await this.graph.addNode(guaranteeNode);
233
+
234
+ // Создаём GOVERNS edges к matching модулям
235
+ await this._createGovernsEdges(guaranteeNode.id, governs);
236
+
237
+ return guaranteeNode;
238
+ }
239
+
240
+ /**
241
+ * Получить все гарантии из графа
242
+ */
243
+ async list(): Promise<GuaranteeNode[]> {
244
+ const guarantees: GuaranteeNode[] = [];
245
+ for await (const node of this.graph.queryNodes({ type: 'GUARANTEE' })) {
246
+ const guaranteeNode = node as GuaranteeNode;
247
+ // Получаем GOVERNS edges для этой гарантии
248
+ const governsEdges = await this.graph.getOutgoingEdges(guaranteeNode.id, ['GOVERNS']);
249
+ const governedModules: string[] = [];
250
+ for (const edge of governsEdges) {
251
+ const targetNode = await this.graph.getNode(edge.dst);
252
+ if (targetNode) {
253
+ governedModules.push((targetNode as ModuleNode).file || targetNode.id);
254
+ }
255
+ }
256
+
257
+ guarantees.push({
258
+ ...guaranteeNode,
259
+ governedModules
260
+ });
261
+ }
262
+ return guarantees;
263
+ }
264
+
265
+ /**
266
+ * Проверить гарантию
267
+ */
268
+ async check(guaranteeId: string): Promise<GuaranteeCheckResult> {
269
+ const fullId = guaranteeId.startsWith('GUARANTEE:') ? guaranteeId : `GUARANTEE:${guaranteeId}`;
270
+ const node = (await this.graph.getNode(fullId)) as GuaranteeNode | null;
271
+
272
+ if (!node) {
273
+ throw new Error(`Guarantee not found: ${guaranteeId}`);
274
+ }
275
+
276
+ const startTime = Date.now();
277
+ let violations: ViolationResult[] = [];
278
+ let error: string | null = null;
279
+
280
+ try {
281
+ violations = await this.graph.checkGuarantee(node.rule);
282
+ } catch (e) {
283
+ error = e instanceof Error ? e.message : String(e);
284
+ }
285
+
286
+ // Обогащаем violations информацией о нодах
287
+ const enrichedViolations: EnrichedViolation[] = [];
288
+ for (const v of violations) {
289
+ const nodeId = v.bindings?.find(b => b.name === 'X')?.value;
290
+ if (nodeId) {
291
+ const violatingNode = await this.graph.getNode(nodeId);
292
+ if (violatingNode) {
293
+ enrichedViolations.push({
294
+ nodeId,
295
+ type: violatingNode.type,
296
+ name: (violatingNode as GuaranteeNode).name,
297
+ file: (violatingNode as ModuleNode).file,
298
+ line: (violatingNode as { line?: number }).line
299
+ });
300
+ } else {
301
+ // Non-node-ID binding (e.g. attr() string value) — return raw binding as violation
302
+ enrichedViolations.push({ nodeId, type: 'raw_binding', name: nodeId });
303
+ }
304
+ }
305
+ }
306
+
307
+ return {
308
+ guaranteeId: guaranteeId,
309
+ name: node.name,
310
+ severity: node.severity,
311
+ passed: violations.length === 0 && !error,
312
+ violationCount: violations.length,
313
+ violations: enrichedViolations,
314
+ error,
315
+ checkDurationMs: Date.now() - startTime
316
+ };
317
+ }
318
+
319
+ /**
320
+ * Проверить все гарантии
321
+ */
322
+ async checkAll(): Promise<CheckAllResult> {
323
+ const guarantees = await this.list();
324
+ const results: GuaranteeCheckResult[] = [];
325
+ let passedCount = 0;
326
+ let failedCount = 0;
327
+ let errorCount = 0;
328
+
329
+ for (const g of guarantees) {
330
+ const result = await this.check(g.id);
331
+ results.push(result);
332
+
333
+ if (result.error) {
334
+ errorCount++;
335
+ } else if (result.passed) {
336
+ passedCount++;
337
+ } else {
338
+ failedCount++;
339
+ }
340
+ }
341
+
342
+ return {
343
+ total: guarantees.length,
344
+ passed: passedCount,
345
+ failed: failedCount,
346
+ errors: errorCount,
347
+ results
348
+ };
349
+ }
350
+
351
+ /**
352
+ * Extract type references from a Datalog rule.
353
+ * Matches node(X, "TYPE") and edge(X, Y, "TYPE") patterns.
354
+ * Returns unique types array. If nothing parseable, returns empty array.
355
+ */
356
+ extractRelevantTypes(rule: string): string[] {
357
+ const types = new Set<string>();
358
+
359
+ // Match node(X, "TYPE") or type(X, "TYPE") patterns
360
+ const nodePattern = /(?:node|type)\(\s*\w+\s*,\s*"([^"]+)"\s*\)/g;
361
+ let match;
362
+ while ((match = nodePattern.exec(rule)) !== null) {
363
+ types.add(match[1]);
364
+ }
365
+
366
+ // Match edge(X, Y, "TYPE") patterns
367
+ const edgePattern = /edge\(\s*\w+\s*,\s*\w+\s*,\s*"([^"]+)"\s*\)/g;
368
+ while ((match = edgePattern.exec(rule)) !== null) {
369
+ types.add(match[1]);
370
+ }
371
+
372
+ return [...types];
373
+ }
374
+
375
+ /**
376
+ * Selectively check guarantees whose relevant types overlap with changedTypes.
377
+ * Guarantees with no parseable types are always checked (conservative).
378
+ * Returns CheckAllResult with total = all guarantees count,
379
+ * but results only for the checked subset.
380
+ */
381
+ async checkSelective(changedTypes: Set<string>): Promise<CheckAllResult> {
382
+ const guarantees = await this.list();
383
+ const results: GuaranteeCheckResult[] = [];
384
+ let passedCount = 0;
385
+ let failedCount = 0;
386
+ let errorCount = 0;
387
+
388
+ for (const g of guarantees) {
389
+ const relevantTypes = this.extractRelevantTypes(g.rule);
390
+ // If no types parseable, check conservatively; otherwise check only if overlap
391
+ const shouldCheck = relevantTypes.length === 0 ||
392
+ relevantTypes.some(t => changedTypes.has(t));
393
+
394
+ if (!shouldCheck) continue;
395
+
396
+ const result = await this.check(g.id);
397
+ results.push(result);
398
+
399
+ if (result.error) {
400
+ errorCount++;
401
+ } else if (result.passed) {
402
+ passedCount++;
403
+ } else {
404
+ failedCount++;
405
+ }
406
+ }
407
+
408
+ return {
409
+ total: guarantees.length,
410
+ passed: passedCount,
411
+ failed: failedCount,
412
+ errors: errorCount,
413
+ results
414
+ };
415
+ }
416
+
417
+ /**
418
+ * Load guarantees from the project's YAML file (.grafema/guarantees.yaml).
419
+ *
420
+ * Reads the YAML file, skips non-datalog guarantees (e.g., integration-test),
421
+ * and creates GUARANTEE nodes in the graph for each datalog guarantee.
422
+ * Idempotent: skips guarantees that already exist in the graph.
423
+ * Gracefully handles missing file (no error, just returns).
424
+ *
425
+ * @returns ImportResult with counts of imported and skipped guarantees
426
+ */
427
+ async loadFromYaml(): Promise<ImportResult> {
428
+ if (!existsSync(this.guaranteesFile)) {
429
+ return { imported: 0, skipped: 0, importedIds: [], skippedIds: [] };
430
+ }
431
+
432
+ return this.import(this.guaranteesFile);
433
+ }
434
+
435
+ /**
436
+ * Удалить гарантию
437
+ */
438
+ async delete(guaranteeId: string): Promise<void> {
439
+ const fullId = guaranteeId.startsWith('GUARANTEE:') ? guaranteeId : `GUARANTEE:${guaranteeId}`;
440
+
441
+ // Удаляем GOVERNS edges
442
+ const edges = await this.graph.getOutgoingEdges(fullId, ['GOVERNS']);
443
+ for (const edge of edges) {
444
+ await this.graph.deleteEdge(edge.src, edge.dst, 'GOVERNS');
445
+ }
446
+
447
+ // Удаляем ноду
448
+ await this.graph.deleteNode(fullId);
449
+ }
450
+
451
+ /**
452
+ * Экспортировать гарантии в YAML файл
453
+ */
454
+ async export(filePath: string = this.guaranteesFile): Promise<string> {
455
+ const guarantees = await this.list();
456
+
457
+ const exportData: ExportData = {
458
+ version: 1,
459
+ exportedAt: new Date().toISOString(),
460
+ guarantees: guarantees.map(g => ({
461
+ id: g.id.replace('GUARANTEE:', ''),
462
+ name: g.name,
463
+ rule: g.rule,
464
+ severity: g.severity,
465
+ governs: g.governs || ['**/*.js']
466
+ }))
467
+ };
468
+
469
+ const yaml = stringifyYaml(exportData, { lineWidth: 0 });
470
+ writeFileSync(filePath, yaml, 'utf-8');
471
+
472
+ return filePath;
473
+ }
474
+
475
+ /**
476
+ * Импортировать гарантии из YAML файла
477
+ */
478
+ async import(filePath: string = this.guaranteesFile, options: ImportOptions = {}): Promise<ImportResult> {
479
+ const { clearExisting = false } = options;
480
+
481
+ if (!existsSync(filePath)) {
482
+ throw new Error(`Guarantees file not found: ${filePath}`);
483
+ }
484
+
485
+ const content = readFileSync(filePath, 'utf-8');
486
+ const data = parseYaml(content) as ExportData;
487
+
488
+ if (!data.guarantees || !Array.isArray(data.guarantees)) {
489
+ throw new Error('Invalid guarantees file format');
490
+ }
491
+
492
+ // Удаляем существующие если нужно
493
+ if (clearExisting) {
494
+ const existing = await this.list();
495
+ for (const g of existing) {
496
+ await this.delete(g.id);
497
+ }
498
+ }
499
+
500
+ // Импортируем
501
+ const imported: string[] = [];
502
+ const skipped: string[] = [];
503
+
504
+ for (const g of data.guarantees) {
505
+ // Normalize: YAML uses 'name', code expects 'id'
506
+ if (!g.id && g.name) g.id = g.name;
507
+ // Skip non-Datalog guarantees (integration-test entries have no rule)
508
+ if (!g.rule) {
509
+ skipped.push(g.id || g.name || 'unknown');
510
+ continue;
511
+ }
512
+
513
+ const fullId = `GUARANTEE:${g.id}`;
514
+ const existing = await this.graph.getNode(fullId);
515
+
516
+ if (existing && !clearExisting) {
517
+ skipped.push(g.id);
518
+ continue;
519
+ }
520
+
521
+ // Default governs to all files if not specified in YAML
522
+ if (!g.governs) {
523
+ g.governs = ['**/*'];
524
+ }
525
+ await this.create(g);
526
+ imported.push(g.id);
527
+ }
528
+
529
+ return {
530
+ imported: imported.length,
531
+ skipped: skipped.length,
532
+ importedIds: imported,
533
+ skippedIds: skipped
534
+ };
535
+ }
536
+
537
+ /**
538
+ * Показать drift между графом и файлом
539
+ */
540
+ async drift(filePath: string = this.guaranteesFile): Promise<DriftResult> {
541
+ const graphGuarantees = await this.list();
542
+ const graphMap = new Map<string, GuaranteeNode>(
543
+ graphGuarantees.map(g => [g.id.replace('GUARANTEE:', ''), g])
544
+ );
545
+
546
+ let fileGuarantees: Array<{ id: string; name: string; rule: string; severity: GuaranteeSeverity; governs: string[] }> = [];
547
+ if (existsSync(filePath)) {
548
+ const content = readFileSync(filePath, 'utf-8');
549
+ const data = parseYaml(content) as ExportData;
550
+ fileGuarantees = data.guarantees || [];
551
+ }
552
+ const fileMap = new Map(fileGuarantees.map(g => [g.id, g]));
553
+
554
+ const onlyInGraph: string[] = [];
555
+ const onlyInFile: string[] = [];
556
+ const modified: ModifiedGuarantee[] = [];
557
+ const unchanged: string[] = [];
558
+
559
+ // Проверяем гарантии в графе
560
+ for (const [id, graphG] of graphMap) {
561
+ const fileG = fileMap.get(id);
562
+ if (!fileG) {
563
+ onlyInGraph.push(id);
564
+ } else if (this._hasChanges(graphG, fileG)) {
565
+ modified.push({
566
+ id,
567
+ changes: this._describeChanges(graphG, fileG)
568
+ });
569
+ } else {
570
+ unchanged.push(id);
571
+ }
572
+ }
573
+
574
+ // Проверяем гарантии только в файле
575
+ for (const [id] of fileMap) {
576
+ if (!graphMap.has(id)) {
577
+ onlyInFile.push(id);
578
+ }
579
+ }
580
+
581
+ return {
582
+ hasDrift: onlyInGraph.length > 0 || onlyInFile.length > 0 || modified.length > 0,
583
+ summary: {
584
+ onlyInGraph: onlyInGraph.length,
585
+ onlyInFile: onlyInFile.length,
586
+ modified: modified.length,
587
+ unchanged: unchanged.length
588
+ },
589
+ onlyInGraph,
590
+ onlyInFile,
591
+ modified,
592
+ unchanged
593
+ };
594
+ }
595
+
596
+ /**
597
+ * Найти гарантии затронутые изменением ноды
598
+ */
599
+ async findAffectedGuarantees(nodeId: string): Promise<string[]> {
600
+ const node = await this.graph.getNode(nodeId);
601
+ if (!node) return [];
602
+
603
+ // Поднимаемся до MODULE
604
+ let moduleId: string | null = null;
605
+ if (node.type === 'MODULE') {
606
+ moduleId = node.id;
607
+ } else if ((node as ModuleNode).file) {
608
+ // Ищем MODULE по file
609
+ for await (const m of this.graph.queryNodes({ type: 'MODULE' })) {
610
+ if ((m as ModuleNode).file === (node as ModuleNode).file) {
611
+ moduleId = m.id;
612
+ break;
613
+ }
614
+ }
615
+ }
616
+
617
+ if (!moduleId) return [];
618
+
619
+ // Находим GOVERNS edges к этому модулю
620
+ const incomingEdges = await this.graph.getIncomingEdges(moduleId, ['GOVERNS']);
621
+ return incomingEdges.map(e => e.src);
622
+ }
623
+
624
+ // ============ Private methods ============
625
+
626
+ /**
627
+ * Создать GOVERNS edges к модулям по glob patterns
628
+ */
629
+ private async _createGovernsEdges(guaranteeId: string, patterns: string[]): Promise<void> {
630
+ // Получаем все MODULE ноды
631
+ const modules: ModuleNode[] = [];
632
+ for await (const node of this.graph.queryNodes({ type: 'MODULE' })) {
633
+ modules.push(node as ModuleNode);
634
+ }
635
+
636
+ // Матчим patterns
637
+ for (const module of modules) {
638
+ const relativePath = module.file
639
+ ? (isAbsolute(module.file) ? relative(this.projectPath, module.file) : module.file)
640
+ : '';
641
+
642
+ for (const pattern of patterns) {
643
+ if (minimatch(relativePath, pattern) || minimatch(module.file || '', pattern)) {
644
+ await this.graph.addEdge({
645
+ type: 'GOVERNS',
646
+ src: guaranteeId,
647
+ dst: module.id
648
+ });
649
+ break; // Один edge на модуль
650
+ }
651
+ }
652
+ }
653
+ }
654
+
655
+ /**
656
+ * Проверить есть ли изменения между версиями гарантии
657
+ */
658
+ private _hasChanges(
659
+ graphG: GuaranteeNode,
660
+ fileG: { rule: string; severity: GuaranteeSeverity; name: string; governs: string[] }
661
+ ): boolean {
662
+ return (
663
+ graphG.rule !== fileG.rule ||
664
+ graphG.severity !== fileG.severity ||
665
+ graphG.name !== fileG.name ||
666
+ JSON.stringify(graphG.governs) !== JSON.stringify(fileG.governs)
667
+ );
668
+ }
669
+
670
+ /**
671
+ * Описать изменения между версиями
672
+ */
673
+ private _describeChanges(
674
+ graphG: GuaranteeNode,
675
+ fileG: { rule: string; severity: GuaranteeSeverity; name: string; governs: string[] }
676
+ ): string[] {
677
+ const changes: string[] = [];
678
+ if (graphG.rule !== fileG.rule) changes.push('rule');
679
+ if (graphG.severity !== fileG.severity) changes.push('severity');
680
+ if (graphG.name !== fileG.name) changes.push('name');
681
+ if (JSON.stringify(graphG.governs) !== JSON.stringify(fileG.governs)) changes.push('governs');
682
+ return changes;
683
+ }
684
+ }