@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,48 @@
1
+ /**
2
+ * HashUtils - unified hash computation for Grafema
3
+ *
4
+ * WHY THIS EXISTS:
5
+ * - 6 copies of the same hash computation existed across the codebase
6
+ * - Single source of truth ensures consistent hashing everywhere
7
+ * - Makes future algorithm changes (e.g., SHA-256 -> BLAKE3) trivial
8
+ */
9
+
10
+ import { createHash } from 'crypto';
11
+ import { readFileSync } from 'fs';
12
+ import { readFile } from 'fs/promises';
13
+
14
+ const HASH_ALGORITHM = 'sha256';
15
+
16
+ /**
17
+ * Calculate hash from a file path (synchronous).
18
+ * Returns null if file doesn't exist or is unreadable.
19
+ */
20
+ export function calculateFileHash(filePath: string): string | null {
21
+ try {
22
+ const content = readFileSync(filePath, 'utf-8');
23
+ return createHash(HASH_ALGORITHM).update(content).digest('hex');
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Calculate hash from a file path (asynchronous).
31
+ * Returns null if file doesn't exist or is unreadable.
32
+ */
33
+ export async function calculateFileHashAsync(filePath: string): Promise<string | null> {
34
+ try {
35
+ const content = await readFile(filePath, 'utf-8');
36
+ return createHash(HASH_ALGORITHM).update(content).digest('hex');
37
+ } catch {
38
+ return null;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Calculate hash from content string.
44
+ * Always returns a hash (never null).
45
+ */
46
+ export function calculateContentHash(content: string): string {
47
+ return createHash(HASH_ALGORITHM).update(content).digest('hex');
48
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * IncrementalReanalyzer - selective re-analysis of stale modules
3
+ *
4
+ * Shells out to grafema-orchestrator for reanalysis.
5
+ * The Rust orchestrator handles clearing, indexing, analysis, and enrichment.
6
+ */
7
+
8
+ import { spawn } from 'child_process';
9
+ import { join } from 'path';
10
+ import type { GraphBackend } from '@grafema/types';
11
+ import type { StaleModule } from './GraphFreshnessChecker.js';
12
+ import { findOrchestratorBinary } from '../utils/findRfdbBinary.js';
13
+
14
+ export interface ReanalysisOptions {
15
+ skipEnrichment?: boolean;
16
+ onProgress?: (info: ReanalysisProgress) => void;
17
+ }
18
+
19
+ export interface ReanalysisProgress {
20
+ phase: 'clearing' | 'indexing' | 'analysis' | 'enrichment';
21
+ processedFiles: number;
22
+ totalFiles: number;
23
+ currentService?: string;
24
+ }
25
+
26
+ export interface ReanalysisResult {
27
+ modulesReanalyzed: number;
28
+ modulesDeleted: number;
29
+ nodesCreated: number;
30
+ edgesCreated: number;
31
+ nodesCleared: number;
32
+ durationMs: number;
33
+ }
34
+
35
+ export class IncrementalReanalyzer {
36
+ private _graph: GraphBackend;
37
+ private projectPath: string;
38
+
39
+ constructor(graph: GraphBackend, projectPath: string) {
40
+ this._graph = graph;
41
+ this.projectPath = projectPath;
42
+ }
43
+
44
+ async reanalyze(
45
+ staleModules: StaleModule[],
46
+ _options: ReanalysisOptions = {}
47
+ ): Promise<ReanalysisResult> {
48
+ const startTime = Date.now();
49
+
50
+ if (staleModules.length === 0) {
51
+ return {
52
+ modulesReanalyzed: 0,
53
+ modulesDeleted: 0,
54
+ nodesCreated: 0,
55
+ edgesCreated: 0,
56
+ nodesCleared: 0,
57
+ durationMs: Date.now() - startTime,
58
+ };
59
+ }
60
+
61
+ const binary = findOrchestratorBinary();
62
+ if (!binary) {
63
+ throw new Error(
64
+ 'grafema-orchestrator binary not found. ' +
65
+ 'Build it with: cd packages/grafema-orchestrator && cargo build --release'
66
+ );
67
+ }
68
+
69
+ const socketPath = join(this.projectPath, '.grafema', 'rfdb.sock');
70
+
71
+ return new Promise<ReanalysisResult>((resolve, reject) => {
72
+ const child = spawn(binary, [
73
+ 'analyze',
74
+ '--project', this.projectPath,
75
+ '--socket', socketPath,
76
+ ], {
77
+ stdio: ['ignore', 'pipe', 'pipe'],
78
+ });
79
+
80
+ let stderr = '';
81
+ child.stderr?.on('data', (data: Buffer) => {
82
+ stderr += data.toString();
83
+ });
84
+
85
+ child.on('error', (err) => {
86
+ reject(new Error(`Failed to start grafema-orchestrator: ${err.message}`));
87
+ });
88
+
89
+ child.on('close', (code) => {
90
+ if (code !== 0) {
91
+ reject(new Error(`grafema-orchestrator exited with code ${code}: ${stderr}`));
92
+ return;
93
+ }
94
+
95
+ resolve({
96
+ modulesReanalyzed: staleModules.filter(m => m.currentHash !== null).length,
97
+ modulesDeleted: staleModules.filter(m => m.currentHash === null).length,
98
+ nodesCreated: 0,
99
+ edgesCreated: 0,
100
+ nodesCleared: 0,
101
+ durationMs: Date.now() - startTime,
102
+ });
103
+ });
104
+ });
105
+ }
106
+ }
@@ -0,0 +1,39 @@
1
+ import type { Resource, ResourceId, ResourceRegistry as IResourceRegistry } from '@grafema/types';
2
+
3
+ /**
4
+ * In-memory Resource registry for a single pipeline run.
5
+ * Created by Orchestrator at run start, cleared at run end.
6
+ *
7
+ * Thread safety: Not needed — Grafema runs plugins sequentially within
8
+ * each phase. Plugins are toposorted and run one at a time.
9
+ */
10
+ export class ResourceRegistryImpl implements IResourceRegistry {
11
+ private resources = new Map<ResourceId, Resource>();
12
+
13
+ getOrCreate<T extends Resource>(id: ResourceId, factory: () => T): T {
14
+ let resource = this.resources.get(id);
15
+ if (!resource) {
16
+ resource = factory();
17
+ if (resource.id !== id) {
18
+ throw new Error(
19
+ `Resource factory returned resource with id "${resource.id}" but expected "${id}"`
20
+ );
21
+ }
22
+ this.resources.set(id, resource);
23
+ }
24
+ return resource as T;
25
+ }
26
+
27
+ get<T extends Resource>(id: ResourceId): T | undefined {
28
+ return this.resources.get(id) as T | undefined;
29
+ }
30
+
31
+ has(id: ResourceId): boolean {
32
+ return this.resources.has(id);
33
+ }
34
+
35
+ /** Clear all Resources. Called by Orchestrator at the end of a run. */
36
+ clear(): void {
37
+ this.resources.clear();
38
+ }
39
+ }
@@ -0,0 +1,423 @@
1
+ /**
2
+ * SemanticId - Stable identifiers for code elements
3
+ *
4
+ * Semantic IDs provide stable identifiers for code elements that don't change
5
+ * when unrelated code is added/removed (no line numbers in IDs).
6
+ *
7
+ * Format: {file}->{scope_path}->{type}->{name}[#discriminator]
8
+ *
9
+ * Examples:
10
+ * src/app.js->global->FUNCTION->processData
11
+ * src/app.js->UserService->METHOD->login
12
+ * src/app.js->getUser->if#0->CALL->console.log#0
13
+ *
14
+ * Special formats:
15
+ * Singletons: net:stdio->__stdio__
16
+ * External modules: EXTERNAL_MODULE->lodash
17
+ */
18
+
19
+ /**
20
+ * Location in source file
21
+ */
22
+ export interface Location {
23
+ line: number;
24
+ column: number;
25
+ endLine?: number;
26
+ endColumn?: number;
27
+ }
28
+
29
+ /**
30
+ * Scope context for semantic ID generation
31
+ */
32
+ export interface ScopeContext {
33
+ /** Source file path */
34
+ file: string;
35
+ /** Array of scope names, e.g. ['MyClass', 'myMethod', 'if#1'] */
36
+ scopePath: string[];
37
+ }
38
+
39
+ /**
40
+ * Options for semantic ID generation
41
+ */
42
+ export interface SemanticIdOptions {
43
+ /** Counter for disambiguation (#N) */
44
+ discriminator?: number;
45
+ /** Context string for special cases ([context]) */
46
+ context?: string;
47
+ }
48
+
49
+ /**
50
+ * Parsed semantic ID components
51
+ */
52
+ export interface ParsedSemanticId {
53
+ file: string;
54
+ scopePath: string[];
55
+ type: string;
56
+ name: string;
57
+ discriminator?: number;
58
+ context?: string;
59
+ }
60
+
61
+ /**
62
+ * Item with name and location for discriminator computation
63
+ */
64
+ export interface LocatedItem {
65
+ name: string;
66
+ location: Location;
67
+ }
68
+
69
+ // =============================================================================
70
+ // Semantic ID v2
71
+ // =============================================================================
72
+
73
+ /**
74
+ * Parsed v2 semantic ID components.
75
+ *
76
+ * v2 format: file->TYPE->name[in:namedParent,h:xxxx]#N
77
+ *
78
+ * Key difference from v1: no scope path in ID. Only nearest named ancestor
79
+ * (namedParent) is encoded. Anonymous scopes (if, for, try) are invisible.
80
+ */
81
+ export interface ParsedSemanticIdV2 {
82
+ file: string;
83
+ type: string;
84
+ name: string;
85
+ namedParent?: string;
86
+ contentHash?: string;
87
+ counter?: number;
88
+ }
89
+
90
+ /**
91
+ * Content hint data for computing content hash.
92
+ * Each node type supplies different hints for disambiguation.
93
+ */
94
+ export interface ContentHashHints {
95
+ /** Number of arguments (CALL) or parameters (FUNCTION) */
96
+ arity?: number;
97
+ /** First literal argument value (CALL) */
98
+ firstLiteralArg?: string;
99
+ /** First parameter name (FUNCTION) */
100
+ firstParamName?: string;
101
+ /** RHS expression type (VARIABLE/CONSTANT) */
102
+ rhsType?: string;
103
+ /** First significant token of RHS (VARIABLE/CONSTANT) */
104
+ rhsToken?: string;
105
+ /** Object expression chain (PROPERTY_ACCESS) */
106
+ objectChain?: string;
107
+ }
108
+
109
+ /**
110
+ * Compute semantic ID for any node type.
111
+ *
112
+ * @param type - Node type (FUNCTION, CALL, VARIABLE, etc.)
113
+ * @param name - Node name
114
+ * @param context - Scope context from ScopeTracker
115
+ * @param options - Optional discriminator or context
116
+ * @returns Semantic ID string
117
+ */
118
+ export function computeSemanticId(
119
+ type: string,
120
+ name: string,
121
+ context: ScopeContext,
122
+ options?: SemanticIdOptions
123
+ ): string {
124
+ const { file, scopePath } = context;
125
+ const scope = scopePath.length > 0 ? scopePath.join('->') : 'global';
126
+
127
+ let id = `${file}->${scope}->${type}->${name}`;
128
+
129
+ if (options?.discriminator !== undefined) {
130
+ id += `#${options.discriminator}`;
131
+ } else if (options?.context) {
132
+ id += `[${options.context}]`;
133
+ }
134
+
135
+ return id;
136
+ }
137
+
138
+ /**
139
+ * Parse semantic ID back to components.
140
+ *
141
+ * @param id - Semantic ID to parse
142
+ * @returns Parsed components or null if invalid
143
+ */
144
+ export function parseSemanticId(id: string): ParsedSemanticId | null {
145
+ // Handle singletons
146
+ if (id.startsWith('net:stdio') || id.startsWith('net:request')) {
147
+ const [prefix, name] = id.split('->');
148
+ return {
149
+ file: '',
150
+ scopePath: [prefix],
151
+ type: 'SINGLETON',
152
+ name,
153
+ discriminator: undefined
154
+ };
155
+ }
156
+
157
+ if (id.startsWith('EXTERNAL_MODULE')) {
158
+ const [, name] = id.split('->');
159
+ return {
160
+ file: '',
161
+ scopePath: [],
162
+ type: 'EXTERNAL_MODULE',
163
+ name,
164
+ discriminator: undefined
165
+ };
166
+ }
167
+
168
+ const parts = id.split('->');
169
+ if (parts.length < 4) return null;
170
+
171
+ const file = parts[0];
172
+ const type = parts[parts.length - 2];
173
+ let name = parts[parts.length - 1];
174
+ const scopePath = parts.slice(1, -2);
175
+
176
+ // Parse discriminator or context
177
+ let discriminator: number | undefined;
178
+ let context: string | undefined;
179
+
180
+ const hashMatch = name.match(/^(.+)#(\d+)$/);
181
+ if (hashMatch) {
182
+ name = hashMatch[1];
183
+ discriminator = parseInt(hashMatch[2], 10);
184
+ }
185
+
186
+ const bracketMatch = name.match(/^(.+)\[(.+)\]$/);
187
+ if (bracketMatch) {
188
+ name = bracketMatch[1];
189
+ context = bracketMatch[2];
190
+ }
191
+
192
+ return { file, scopePath, type, name, discriminator, context };
193
+ }
194
+
195
+ /**
196
+ * Compute discriminator for items with same name in same scope.
197
+ * Uses line/column for stable ordering.
198
+ *
199
+ * @param items - All items in scope
200
+ * @param targetName - Name to find discriminator for
201
+ * @param targetLocation - Location of target item
202
+ * @returns Discriminator (0-based index among same-named items)
203
+ */
204
+ export function computeDiscriminator(
205
+ items: LocatedItem[],
206
+ targetName: string,
207
+ targetLocation: Location
208
+ ): number {
209
+ // Filter items with same name
210
+ const sameNameItems = items.filter(item => item.name === targetName);
211
+
212
+ if (sameNameItems.length <= 1) {
213
+ return 0;
214
+ }
215
+
216
+ // Sort by line, then by column for stable ordering
217
+ sameNameItems.sort((a, b) => {
218
+ if (a.location.line !== b.location.line) {
219
+ return a.location.line - b.location.line;
220
+ }
221
+ return a.location.column - b.location.column;
222
+ });
223
+
224
+ // Find index of target
225
+ const index = sameNameItems.findIndex(
226
+ item =>
227
+ item.location.line === targetLocation.line &&
228
+ item.location.column === targetLocation.column
229
+ );
230
+
231
+ return index >= 0 ? index : 0;
232
+ }
233
+
234
+ // =============================================================================
235
+ // Semantic ID v2 Functions
236
+ // =============================================================================
237
+
238
+ /**
239
+ * Compute v2 semantic ID.
240
+ *
241
+ * Format: file->TYPE->name (top-level, no collision)
242
+ * Format: file->TYPE->name[in:parent] (nested, no collision)
243
+ * Format: file->TYPE->name[in:parent,h:xxxx] (collision, hash disambiguates)
244
+ * Format: file->TYPE->name[in:parent,h:xxxx]#N (hash collision, counter)
245
+ *
246
+ * @param type - Node type (FUNCTION, CALL, VARIABLE, etc.)
247
+ * @param name - Node name
248
+ * @param file - Source file path
249
+ * @param namedParent - Nearest named ancestor (undefined for top-level)
250
+ * @param contentHash - 4-hex content hash for disambiguation
251
+ * @param counter - Counter for hash collisions
252
+ */
253
+ export function computeSemanticIdV2(
254
+ type: string,
255
+ name: string,
256
+ file: string,
257
+ namedParent?: string,
258
+ contentHash?: string,
259
+ counter?: number
260
+ ): string {
261
+ const brackets: string[] = [];
262
+ if (namedParent) brackets.push(`in:${namedParent}`);
263
+ if (contentHash) brackets.push(`h:${contentHash}`);
264
+
265
+ let id = `${file}->${type}->${name}`;
266
+ if (brackets.length > 0) {
267
+ id += `[${brackets.join(',')}]`;
268
+ }
269
+ if (counter !== undefined && counter > 0) {
270
+ id += `#${counter}`;
271
+ }
272
+ return id;
273
+ }
274
+
275
+ /**
276
+ * Parse v2 semantic ID back to components.
277
+ *
278
+ * Handles v2 format: file->TYPE->name[in:parent,h:xxxx]#N
279
+ * and special formats: net:stdio->__stdio__, EXTERNAL_MODULE->lodash
280
+ *
281
+ * @returns Parsed components or null if not a valid v2 ID
282
+ */
283
+ export function parseSemanticIdV2(id: string): ParsedSemanticIdV2 | null {
284
+ // Handle grafema:// URI input — convert to compact first
285
+ if (id.startsWith('grafema://')) {
286
+ const scheme = 'grafema://';
287
+ const rest = id.slice(scheme.length);
288
+
289
+ const slashUnderscore = rest.indexOf('/_/');
290
+ const hashPos = rest.indexOf('#');
291
+
292
+ if (slashUnderscore !== -1 && (hashPos === -1 || slashUnderscore < hashPos)) {
293
+ // Virtual node: decode and re-parse
294
+ const encodedId = rest.slice(slashUnderscore + 3);
295
+ const decoded = encodedId
296
+ .replaceAll('%3E', '>').replaceAll('%3e', '>')
297
+ .replaceAll('%5B', '[').replaceAll('%5b', '[')
298
+ .replaceAll('%5D', ']').replaceAll('%5d', ']')
299
+ .replaceAll('%23', '#');
300
+ return parseSemanticIdV2(decoded);
301
+ }
302
+
303
+ if (hashPos !== -1) {
304
+ const pathPart = rest.slice(0, hashPos);
305
+ const fragment = rest.slice(hashPos + 1);
306
+ const decoded = fragment
307
+ .replaceAll('%3E', '>').replaceAll('%3e', '>')
308
+ .replaceAll('%5B', '[').replaceAll('%5b', '[')
309
+ .replaceAll('%5D', ']').replaceAll('%5d', ']')
310
+ .replaceAll('%23', '#');
311
+
312
+ if (decoded === 'MODULE') {
313
+ const segments = pathPart.split('/');
314
+ const host = segments[0];
315
+ const skip = (host === 'github.com' || host === 'gitlab.com' || host === 'bitbucket.org') ? 3 : 2;
316
+ const filePath = segments.slice(skip).join('/');
317
+ return { file: filePath, type: 'MODULE', name: filePath };
318
+ }
319
+
320
+ // Standard node: extract file and reconstruct compact ID
321
+ const segments = pathPart.split('/');
322
+ const host = segments[0];
323
+ const skip = (host === 'github.com' || host === 'gitlab.com' || host === 'bitbucket.org') ? 3 : 2;
324
+ const filePath = segments.slice(skip).join('/');
325
+ const compactId = `${filePath}->${decoded}`;
326
+ return parseSemanticIdV2(compactId);
327
+ }
328
+
329
+ return null;
330
+ }
331
+
332
+ // Handle singletons
333
+ if (id.startsWith('net:stdio') || id.startsWith('net:request')) {
334
+ const arrowIdx = id.indexOf('->');
335
+ if (arrowIdx === -1) return null;
336
+ return { file: '', type: 'SINGLETON', name: id.slice(arrowIdx + 2) };
337
+ }
338
+
339
+ if (id.startsWith('EXTERNAL_MODULE')) {
340
+ const arrowIdx = id.indexOf('->');
341
+ if (arrowIdx === -1) return null;
342
+ return { file: '', type: 'EXTERNAL_MODULE', name: id.slice(arrowIdx + 2) };
343
+ }
344
+
345
+ // v2 format: file->TYPE->name[in:parent,h:xxxx]#N
346
+ // Exactly two '->' delimiters: file->TYPE->rest
347
+ const firstArrow = id.indexOf('->');
348
+ if (firstArrow === -1) return null;
349
+
350
+ const secondArrow = id.indexOf('->', firstArrow + 2);
351
+ if (secondArrow === -1) return null;
352
+
353
+ const file = id.slice(0, firstArrow);
354
+ const type = id.slice(firstArrow + 2, secondArrow);
355
+ let rest = id.slice(secondArrow + 2);
356
+
357
+ // Check this isn't a v1 ID (v1 has 4+ parts, meaning rest contains '->')
358
+ // v2 names don't contain '->' in practice
359
+ if (rest.includes('->')) return null;
360
+
361
+ // Parse counter suffix: #N (only at the very end, after brackets)
362
+ let counter: number | undefined;
363
+ const counterMatch = rest.match(/#(\d+)$/);
364
+ if (counterMatch) {
365
+ counter = parseInt(counterMatch[1], 10);
366
+ rest = rest.slice(0, -counterMatch[0].length);
367
+ }
368
+
369
+ // Parse bracket content: [in:parent,h:xxxx]
370
+ let namedParent: string | undefined;
371
+ let contentHash: string | undefined;
372
+ let name = rest;
373
+
374
+ const bracketStart = rest.indexOf('[');
375
+ const bracketEnd = rest.lastIndexOf(']');
376
+ if (bracketStart !== -1 && bracketEnd === rest.length - 1) {
377
+ name = rest.slice(0, bracketStart);
378
+ const bracketContent = rest.slice(bracketStart + 1, bracketEnd);
379
+
380
+ for (const part of bracketContent.split(',')) {
381
+ const colonIdx = part.indexOf(':');
382
+ if (colonIdx === -1) continue;
383
+ const key = part.slice(0, colonIdx);
384
+ const value = part.slice(colonIdx + 1);
385
+ if (key === 'in') namedParent = value;
386
+ else if (key === 'h') contentHash = value;
387
+ }
388
+ }
389
+
390
+ return { file, type, name, namedParent, contentHash, counter };
391
+ }
392
+
393
+ /**
394
+ * FNV-1a hash function, returns 4-hex-char string.
395
+ *
396
+ * FNV-1a is simple, fast, and has good distribution for short strings.
397
+ * 4 hex chars = 16 bits = 65536 buckets.
398
+ *
399
+ * @param hints - Content data to hash
400
+ * @returns 4-char hex string (e.g., "a1b2")
401
+ */
402
+ export function computeContentHash(hints: ContentHashHints): string {
403
+ const parts: string[] = [];
404
+ if (hints.arity !== undefined) parts.push(`a:${hints.arity}`);
405
+ if (hints.firstLiteralArg !== undefined) parts.push(`l:${hints.firstLiteralArg}`);
406
+ if (hints.firstParamName !== undefined) parts.push(`p:${hints.firstParamName}`);
407
+ if (hints.rhsType !== undefined) parts.push(`r:${hints.rhsType}`);
408
+ if (hints.rhsToken !== undefined) parts.push(`t:${hints.rhsToken}`);
409
+ if (hints.objectChain !== undefined) parts.push(`o:${hints.objectChain}`);
410
+
411
+ const input = parts.join('|');
412
+
413
+ // FNV-1a 32-bit
414
+ let hash = 0x811c9dc5; // FNV offset basis
415
+ for (let i = 0; i < input.length; i++) {
416
+ hash ^= input.charCodeAt(i);
417
+ hash = Math.imul(hash, 0x01000193); // FNV prime
418
+ }
419
+
420
+ // Truncate to 16 bits, format as 4-hex
421
+ const truncated = (hash >>> 0) & 0xffff;
422
+ return truncated.toString(16).padStart(4, '0');
423
+ }