@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,177 @@
1
+ /**
2
+ * IssueNode - contract for issue:* nodes
3
+ *
4
+ * Types: issue:security, issue:performance, issue:style, issue:smell
5
+ * ID format: issue:<category>#<hash>
6
+ *
7
+ * Issues represent detected problems in the codebase.
8
+ * They connect to affected code via AFFECTS edges.
9
+ */
10
+
11
+ import { createHash } from 'crypto';
12
+ import type { BaseNodeRecord } from '@grafema/types';
13
+ import { getNamespace } from './NodeKind.js';
14
+
15
+ // Severity type
16
+ export type IssueSeverity = 'error' | 'warning' | 'info';
17
+
18
+ // Issue types
19
+ export type IssueType = `issue:${string}`;
20
+
21
+ export interface IssueNodeRecord extends BaseNodeRecord {
22
+ type: IssueType;
23
+ severity: IssueSeverity;
24
+ category: string;
25
+ message: string;
26
+ plugin: string;
27
+ targetNodeId?: string;
28
+ createdAt: number;
29
+ context?: Record<string, unknown>;
30
+ }
31
+
32
+ export interface IssueNodeOptions {
33
+ context?: Record<string, unknown>;
34
+ }
35
+
36
+ // Valid severity levels
37
+ const VALID_SEVERITIES = ['error', 'warning', 'info'] as const;
38
+
39
+ export class IssueNode {
40
+ static readonly REQUIRED = ['category', 'severity', 'message', 'plugin', 'file'] as const;
41
+ static readonly OPTIONAL = ['targetNodeId', 'context'] as const;
42
+
43
+ /**
44
+ * Generate deterministic issue ID
45
+ * Format: issue:<category>#<hash12>
46
+ *
47
+ * Hash is based on plugin + file + line + column + message
48
+ * This ensures same issue = same ID across analysis runs
49
+ */
50
+ static generateId(
51
+ category: string,
52
+ plugin: string,
53
+ file: string,
54
+ line: number,
55
+ column: number,
56
+ message: string
57
+ ): string {
58
+ const hashInput = `${plugin}|${file}|${line}|${column}|${message}`;
59
+ const hash = createHash('sha256').update(hashInput).digest('hex').substring(0, 12);
60
+ return `issue:${category}#${hash}`;
61
+ }
62
+
63
+ /**
64
+ * Create issue node
65
+ *
66
+ * @param category - Issue category (security, performance, style, smell, or custom)
67
+ * @param severity - error | warning | info
68
+ * @param message - Human-readable description
69
+ * @param plugin - Plugin name that detected this issue
70
+ * @param file - File where issue was detected
71
+ * @param line - Line number
72
+ * @param column - Column number (optional, defaults to 0)
73
+ * @param options - Optional fields (context)
74
+ */
75
+ static create(
76
+ category: string,
77
+ severity: IssueSeverity,
78
+ message: string,
79
+ plugin: string,
80
+ file: string,
81
+ line: number,
82
+ column: number = 0,
83
+ options: IssueNodeOptions = {}
84
+ ): IssueNodeRecord {
85
+ if (!category) throw new Error('IssueNode.create: category is required');
86
+ if (!severity) throw new Error('IssueNode.create: severity is required');
87
+ if (!VALID_SEVERITIES.includes(severity)) {
88
+ throw new Error(`IssueNode.create: invalid severity "${severity}". Valid: ${VALID_SEVERITIES.join(', ')}`);
89
+ }
90
+ if (!message) throw new Error('IssueNode.create: message is required');
91
+ if (!plugin) throw new Error('IssueNode.create: plugin is required');
92
+ if (!file) throw new Error('IssueNode.create: file is required');
93
+
94
+ const type = `issue:${category}` as IssueType;
95
+ const id = this.generateId(category, plugin, file, line, column, message);
96
+ const now = Date.now();
97
+
98
+ return {
99
+ id,
100
+ type,
101
+ name: message.substring(0, 100), // Truncate for display
102
+ file,
103
+ line,
104
+ column,
105
+ severity,
106
+ category,
107
+ message,
108
+ plugin,
109
+ createdAt: now,
110
+ context: options.context,
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Validate issue node
116
+ * @returns array of error messages, empty if valid
117
+ */
118
+ static validate(node: IssueNodeRecord): string[] {
119
+ const errors: string[] = [];
120
+
121
+ if (!IssueNode.isIssueType(node.type)) {
122
+ errors.push(`Expected issue:* type, got ${node.type}`);
123
+ }
124
+
125
+ if (!node.category) {
126
+ errors.push('Missing required field: category');
127
+ }
128
+
129
+ if (!node.severity) {
130
+ errors.push('Missing required field: severity');
131
+ } else if (!VALID_SEVERITIES.includes(node.severity as IssueSeverity)) {
132
+ errors.push(`Invalid severity: ${node.severity}. Valid: ${VALID_SEVERITIES.join(', ')}`);
133
+ }
134
+
135
+ if (!node.message) {
136
+ errors.push('Missing required field: message');
137
+ }
138
+
139
+ if (!node.plugin) {
140
+ errors.push('Missing required field: plugin');
141
+ }
142
+
143
+ return errors;
144
+ }
145
+
146
+ /**
147
+ * Parse issue ID into components
148
+ * @param id - full ID (e.g., 'issue:security#a3f2b1c4d5e6')
149
+ * @returns { category, hash } or null if invalid
150
+ */
151
+ static parseId(id: string): { category: string; hash: string } | null {
152
+ if (!id) return null;
153
+
154
+ const match = id.match(/^issue:([^#]+)#(.+)$/);
155
+ if (!match) return null;
156
+
157
+ return {
158
+ category: match[1],
159
+ hash: match[2],
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Check if type is an issue type
165
+ */
166
+ static isIssueType(type: string): boolean {
167
+ if (!type) return false;
168
+ return getNamespace(type) === 'issue';
169
+ }
170
+
171
+ /**
172
+ * Get all known issue categories
173
+ */
174
+ static getCategories(): string[] {
175
+ return ['security', 'performance', 'style', 'smell'];
176
+ }
177
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Node Types - string type system for nodes
3
+ *
4
+ * Base types: FUNCTION, CLASS, METHOD, etc.
5
+ * Namespaced types: http:route, socketio:emit, express:router, etc.
6
+ *
7
+ * Namespace convention:
8
+ * - http:* - HTTP endpoints and requests
9
+ * - express:* - Express.js specifics
10
+ * - socketio:* - Socket.IO
11
+ * - db:* - Database queries
12
+ * - fs:* - Filesystem operations
13
+ */
14
+
15
+ // === BASE TYPES ===
16
+ // Abstract types, not tied to language or framework
17
+
18
+ export const NODE_TYPE = {
19
+ // Core code entities
20
+ FUNCTION: 'FUNCTION',
21
+ CLASS: 'CLASS',
22
+ METHOD: 'METHOD',
23
+ VARIABLE: 'VARIABLE',
24
+ PARAMETER: 'PARAMETER',
25
+ CONSTANT: 'CONSTANT',
26
+ LITERAL: 'LITERAL',
27
+ EXPRESSION: 'EXPRESSION', // Generic expression node for data flow tracking
28
+ TYPE_PARAMETER: 'TYPE_PARAMETER',
29
+
30
+ // Module system
31
+ MODULE: 'MODULE',
32
+ IMPORT: 'IMPORT',
33
+ EXPORT: 'EXPORT',
34
+
35
+ // Call graph
36
+ CALL: 'CALL', // unified METHOD_CALL + CALL_SITE
37
+
38
+ // Project structure
39
+ PROJECT: 'PROJECT',
40
+ SERVICE: 'SERVICE',
41
+ FILE: 'FILE',
42
+ SCOPE: 'SCOPE',
43
+
44
+ // External dependencies
45
+ EXTERNAL: 'EXTERNAL',
46
+ EXTERNAL_MODULE: 'EXTERNAL_MODULE',
47
+
48
+ // Generic side effects
49
+ SIDE_EFFECT: 'SIDE_EFFECT',
50
+ } as const;
51
+
52
+ export type BaseNodeType = typeof NODE_TYPE[keyof typeof NODE_TYPE];
53
+
54
+ // === NAMESPACED TYPES ===
55
+ // Types specific to frameworks and libraries
56
+
57
+ export const NAMESPACED_TYPE = {
58
+ // HTTP (generic)
59
+ HTTP_ROUTE: 'http:route',
60
+ HTTP_REQUEST: 'http:request',
61
+
62
+ // Express.js
63
+ EXPRESS_ROUTER: 'express:router',
64
+ EXPRESS_MIDDLEWARE: 'express:middleware',
65
+ EXPRESS_MOUNT: 'express:mount',
66
+
67
+ // Socket.IO
68
+ SOCKETIO_EMIT: 'socketio:emit',
69
+ SOCKETIO_ON: 'socketio:on',
70
+ SOCKETIO_NAMESPACE: 'socketio:namespace',
71
+
72
+ // Database
73
+ DB_QUERY: 'db:query',
74
+ DB_CONNECTION: 'db:connection',
75
+
76
+ // Redis
77
+ REDIS_READ: 'redis:read',
78
+ REDIS_WRITE: 'redis:write',
79
+ REDIS_DELETE: 'redis:delete',
80
+ REDIS_PUBLISH: 'redis:publish',
81
+ REDIS_SUBSCRIBE: 'redis:subscribe',
82
+ REDIS_TRANSACTION: 'redis:transaction',
83
+ REDIS_CONNECTION: 'redis:connection',
84
+
85
+ // Filesystem
86
+ FS_READ: 'fs:read',
87
+ FS_WRITE: 'fs:write',
88
+ FS_OPERATION: 'fs:operation',
89
+
90
+ // Network
91
+ NET_REQUEST: 'net:request',
92
+ NET_STDIO: 'net:stdio',
93
+
94
+ // Events
95
+ EVENT_LISTENER: 'event:listener',
96
+ EVENT_EMIT: 'event:emit',
97
+
98
+ // Guarantees (contract-based)
99
+ GUARANTEE_QUEUE: 'guarantee:queue',
100
+ GUARANTEE_API: 'guarantee:api',
101
+ GUARANTEE_PERMISSION: 'guarantee:permission',
102
+
103
+ // Grafema internal (self-describing pipeline)
104
+ GRAFEMA_PLUGIN: 'grafema:plugin',
105
+ } as const;
106
+
107
+ export type NamespacedNodeType = typeof NAMESPACED_TYPE[keyof typeof NAMESPACED_TYPE];
108
+
109
+ // Combined node type
110
+ export type NodeType = BaseNodeType | NamespacedNodeType | string;
111
+
112
+ // === HELPERS ===
113
+
114
+ /**
115
+ * Check if type is namespaced (contains :)
116
+ */
117
+ export function isNamespacedType(nodeType: string): boolean {
118
+ return nodeType !== undefined && nodeType !== null && nodeType.includes(':');
119
+ }
120
+
121
+ /**
122
+ * Get namespace from type
123
+ * @returns namespace or null for base types
124
+ */
125
+ export function getNamespace(nodeType: string): string | null {
126
+ if (!nodeType || !nodeType.includes(':')) return null;
127
+ return nodeType.split(':')[0];
128
+ }
129
+
130
+ /**
131
+ * Get base name from namespaced type
132
+ */
133
+ export function getBaseName(nodeType: string): string {
134
+ if (!nodeType) return '';
135
+ if (!nodeType.includes(':')) return nodeType;
136
+ return nodeType.split(':').slice(1).join(':');
137
+ }
138
+
139
+ /**
140
+ * Check if type is an endpoint (HTTP route, WebSocket handler, etc.)
141
+ */
142
+ export function isEndpointType(nodeType: string): boolean {
143
+ if (!nodeType) return false;
144
+ const ns = getNamespace(nodeType);
145
+ return ns === 'http' || ns === 'express' || ns === 'socketio';
146
+ }
147
+
148
+ /**
149
+ * Check if type is a side effect (DB, FS, Network)
150
+ */
151
+ export function isSideEffectType(nodeType: string): boolean {
152
+ if (!nodeType) return false;
153
+ if (nodeType === NODE_TYPE.SIDE_EFFECT) return true;
154
+ const ns = getNamespace(nodeType);
155
+ return ns === 'db' || ns === 'fs' || ns === 'net' || ns === 'redis';
156
+ }
157
+
158
+ /**
159
+ * Check if type matches a pattern with wildcard
160
+ * @param nodeType - node type
161
+ * @param pattern - pattern (e.g., "http:*", "FUNCTION")
162
+ */
163
+ export function matchesTypePattern(nodeType: string, pattern: string): boolean {
164
+ if (!nodeType || !pattern) return false;
165
+
166
+ // Exact match
167
+ if (nodeType === pattern) return true;
168
+
169
+ // Wildcard match (e.g., "http:*" matches "http:route")
170
+ if (pattern.endsWith(':*')) {
171
+ const patternNs = pattern.slice(0, -2);
172
+ return getNamespace(nodeType) === patternNs;
173
+ }
174
+
175
+ return false;
176
+ }
177
+
178
+ /**
179
+ * Check if type is a guarantee type (guarantee:queue, guarantee:api, etc.)
180
+ */
181
+ export function isGuaranteeType(nodeType: string): boolean {
182
+ if (!nodeType) return false;
183
+ return getNamespace(nodeType) === 'guarantee';
184
+ }
185
+
186
+ /**
187
+ * Check if type is a grafema internal type (grafema:plugin, etc.)
188
+ */
189
+ export function isGrafemaType(nodeType: string): boolean {
190
+ if (!nodeType) return false;
191
+ return getNamespace(nodeType) === 'grafema';
192
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * DiagnosticCollector - Collects and filters diagnostics from plugin execution
3
+ *
4
+ * The DiagnosticCollector aggregates errors from PluginResult.errors[],
5
+ * converting both GrafemaError instances (with rich info) and plain Error
6
+ * instances (treated as generic errors) into unified Diagnostic entries.
7
+ *
8
+ * Usage:
9
+ * const collector = new DiagnosticCollector();
10
+ * collector.addFromPluginResult('INDEXING', 'JSModuleIndexer', result);
11
+ *
12
+ * if (collector.hasFatal()) {
13
+ * throw new Error('Fatal error detected');
14
+ * }
15
+ *
16
+ * console.log(collector.toDiagnosticsLog());
17
+ */
18
+
19
+ import type { PluginPhase, PluginResult } from '@grafema/types';
20
+ import { GrafemaError, type ResolutionStep, type ResolutionFailureReason } from '../errors/GrafemaError.js';
21
+
22
+ /**
23
+ * Diagnostic entry - unified format for all errors/warnings
24
+ */
25
+ export interface Diagnostic {
26
+ code: string;
27
+ severity: 'fatal' | 'error' | 'warning' | 'info';
28
+ message: string;
29
+ file?: string;
30
+ line?: number;
31
+ phase: PluginPhase;
32
+ plugin: string;
33
+ timestamp: number;
34
+ suggestion?: string;
35
+ /** Resolution chain for context (REG-332) */
36
+ resolutionChain?: ResolutionStep[];
37
+ /** Failure reason for context-aware suggestions (REG-332) */
38
+ failureReason?: ResolutionFailureReason;
39
+ }
40
+
41
+ /**
42
+ * Diagnostic input (without timestamp, which is auto-generated)
43
+ */
44
+ export type DiagnosticInput = Omit<Diagnostic, 'timestamp'>;
45
+
46
+ /**
47
+ * DiagnosticCollector - collects, filters, and formats diagnostics
48
+ */
49
+ export class DiagnosticCollector {
50
+ private diagnostics: Diagnostic[] = [];
51
+
52
+ /**
53
+ * Extract errors from PluginResult and add as diagnostics.
54
+ *
55
+ * GrafemaError instances provide rich info (code, severity, context, suggestion).
56
+ * Plain Error instances are treated as generic errors with code 'ERR_UNKNOWN'.
57
+ */
58
+ addFromPluginResult(phase: PluginPhase, plugin: string, result: PluginResult): void {
59
+ for (const error of result.errors) {
60
+ if (error instanceof GrafemaError) {
61
+ this.add({
62
+ code: error.code,
63
+ severity: error.severity,
64
+ message: error.message,
65
+ file: error.context.filePath,
66
+ line: error.context.lineNumber,
67
+ phase,
68
+ plugin,
69
+ suggestion: error.suggestion,
70
+ // REG-332: Pass through resolution context
71
+ resolutionChain: error.context.resolutionChain as ResolutionStep[] | undefined,
72
+ failureReason: error.context.failureReason as ResolutionFailureReason | undefined,
73
+ });
74
+ } else {
75
+ // Plain Error - treat as generic error
76
+ this.add({
77
+ code: 'ERR_UNKNOWN',
78
+ severity: 'error',
79
+ message: error.message,
80
+ phase,
81
+ plugin,
82
+ });
83
+ }
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Add a diagnostic directly.
89
+ * Timestamp is set automatically.
90
+ */
91
+ add(diagnostic: DiagnosticInput): void {
92
+ this.diagnostics.push({
93
+ ...diagnostic,
94
+ timestamp: Date.now(),
95
+ });
96
+ }
97
+
98
+ /**
99
+ * Get all diagnostics.
100
+ * Returns a copy to prevent external modification.
101
+ */
102
+ getAll(): Diagnostic[] {
103
+ return [...this.diagnostics];
104
+ }
105
+
106
+ /**
107
+ * Get diagnostics filtered by phase.
108
+ */
109
+ getByPhase(phase: PluginPhase): Diagnostic[] {
110
+ return this.diagnostics.filter(d => d.phase === phase);
111
+ }
112
+
113
+ /**
114
+ * Get diagnostics filtered by plugin name (case-sensitive).
115
+ */
116
+ getByPlugin(plugin: string): Diagnostic[] {
117
+ return this.diagnostics.filter(d => d.plugin === plugin);
118
+ }
119
+
120
+ /**
121
+ * Get diagnostics filtered by error code.
122
+ */
123
+ getByCode(code: string): Diagnostic[] {
124
+ return this.diagnostics.filter(d => d.code === code);
125
+ }
126
+
127
+ /**
128
+ * Check if any fatal diagnostic exists.
129
+ * Fatal errors require immediate stop of analysis.
130
+ */
131
+ hasFatal(): boolean {
132
+ return this.diagnostics.some(d => d.severity === 'fatal');
133
+ }
134
+
135
+ /**
136
+ * Check if any error (including fatal) exists.
137
+ */
138
+ hasErrors(): boolean {
139
+ return this.diagnostics.some(d => d.severity === 'error' || d.severity === 'fatal');
140
+ }
141
+
142
+ /**
143
+ * Check if any warning exists.
144
+ */
145
+ hasWarnings(): boolean {
146
+ return this.diagnostics.some(d => d.severity === 'warning');
147
+ }
148
+
149
+ /**
150
+ * Get total count of diagnostics.
151
+ */
152
+ count(): number {
153
+ return this.diagnostics.length;
154
+ }
155
+
156
+ /**
157
+ * Format diagnostics as JSON lines (one JSON object per line).
158
+ * Suitable for .grafema/diagnostics.log file.
159
+ */
160
+ toDiagnosticsLog(): string {
161
+ return this.diagnostics.map(d => JSON.stringify(d)).join('\n');
162
+ }
163
+
164
+ /**
165
+ * Clear all diagnostics.
166
+ */
167
+ clear(): void {
168
+ this.diagnostics = [];
169
+ }
170
+ }