@danielsimonjr/memory-mcp 0.47.1 → 9.8.0

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 (207) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +2000 -194
  3. package/dist/__tests__/file-path.test.js +5 -5
  4. package/dist/__tests__/knowledge-graph.test.js +3 -8
  5. package/dist/core/EntityManager.d.ts +266 -0
  6. package/dist/core/EntityManager.d.ts.map +1 -0
  7. package/dist/core/EntityManager.js +85 -133
  8. package/dist/core/GraphEventEmitter.d.ts +202 -0
  9. package/dist/core/GraphEventEmitter.d.ts.map +1 -0
  10. package/dist/core/GraphEventEmitter.js +346 -0
  11. package/dist/core/GraphStorage.d.ts +395 -0
  12. package/dist/core/GraphStorage.d.ts.map +1 -0
  13. package/dist/core/GraphStorage.js +643 -31
  14. package/dist/core/GraphTraversal.d.ts +141 -0
  15. package/dist/core/GraphTraversal.d.ts.map +1 -0
  16. package/dist/core/GraphTraversal.js +573 -0
  17. package/dist/core/HierarchyManager.d.ts +111 -0
  18. package/dist/core/HierarchyManager.d.ts.map +1 -0
  19. package/dist/{features → core}/HierarchyManager.js +14 -9
  20. package/dist/core/ManagerContext.d.ts +72 -0
  21. package/dist/core/ManagerContext.d.ts.map +1 -0
  22. package/dist/core/ManagerContext.js +118 -0
  23. package/dist/core/ObservationManager.d.ts +85 -0
  24. package/dist/core/ObservationManager.d.ts.map +1 -0
  25. package/dist/core/ObservationManager.js +51 -57
  26. package/dist/core/RelationManager.d.ts +131 -0
  27. package/dist/core/RelationManager.d.ts.map +1 -0
  28. package/dist/core/RelationManager.js +31 -7
  29. package/dist/core/SQLiteStorage.d.ts +354 -0
  30. package/dist/core/SQLiteStorage.d.ts.map +1 -0
  31. package/dist/core/SQLiteStorage.js +917 -0
  32. package/dist/core/StorageFactory.d.ts +45 -0
  33. package/dist/core/StorageFactory.d.ts.map +1 -0
  34. package/dist/core/StorageFactory.js +64 -0
  35. package/dist/core/TransactionManager.d.ts +464 -0
  36. package/dist/core/TransactionManager.d.ts.map +1 -0
  37. package/dist/core/TransactionManager.js +490 -13
  38. package/dist/core/index.d.ts +17 -0
  39. package/dist/core/index.d.ts.map +1 -0
  40. package/dist/core/index.js +12 -2
  41. package/dist/features/AnalyticsManager.d.ts +44 -0
  42. package/dist/features/AnalyticsManager.d.ts.map +1 -0
  43. package/dist/features/AnalyticsManager.js +14 -13
  44. package/dist/features/ArchiveManager.d.ts +133 -0
  45. package/dist/features/ArchiveManager.d.ts.map +1 -0
  46. package/dist/features/ArchiveManager.js +221 -14
  47. package/dist/features/CompressionManager.d.ts +117 -0
  48. package/dist/features/CompressionManager.d.ts.map +1 -0
  49. package/dist/features/CompressionManager.js +189 -20
  50. package/dist/features/IOManager.d.ts +225 -0
  51. package/dist/features/IOManager.d.ts.map +1 -0
  52. package/dist/features/IOManager.js +1041 -0
  53. package/dist/features/StreamingExporter.d.ts +123 -0
  54. package/dist/features/StreamingExporter.d.ts.map +1 -0
  55. package/dist/features/StreamingExporter.js +203 -0
  56. package/dist/features/TagManager.d.ts +147 -0
  57. package/dist/features/TagManager.d.ts.map +1 -0
  58. package/dist/features/index.d.ts +12 -0
  59. package/dist/features/index.d.ts.map +1 -0
  60. package/dist/features/index.js +5 -6
  61. package/dist/index.d.ts +9 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +12 -45
  64. package/dist/memory.jsonl +1 -18
  65. package/dist/search/BasicSearch.d.ts +51 -0
  66. package/dist/search/BasicSearch.d.ts.map +1 -0
  67. package/dist/search/BasicSearch.js +9 -3
  68. package/dist/search/BooleanSearch.d.ts +98 -0
  69. package/dist/search/BooleanSearch.d.ts.map +1 -0
  70. package/dist/search/BooleanSearch.js +156 -9
  71. package/dist/search/EmbeddingService.d.ts +178 -0
  72. package/dist/search/EmbeddingService.d.ts.map +1 -0
  73. package/dist/search/EmbeddingService.js +358 -0
  74. package/dist/search/FuzzySearch.d.ts +118 -0
  75. package/dist/search/FuzzySearch.d.ts.map +1 -0
  76. package/dist/search/FuzzySearch.js +241 -25
  77. package/dist/search/QueryCostEstimator.d.ts +111 -0
  78. package/dist/search/QueryCostEstimator.d.ts.map +1 -0
  79. package/dist/search/QueryCostEstimator.js +355 -0
  80. package/dist/search/RankedSearch.d.ts +71 -0
  81. package/dist/search/RankedSearch.d.ts.map +1 -0
  82. package/dist/search/RankedSearch.js +54 -6
  83. package/dist/search/SavedSearchManager.d.ts +79 -0
  84. package/dist/search/SavedSearchManager.d.ts.map +1 -0
  85. package/dist/search/SearchFilterChain.d.ts +120 -0
  86. package/dist/search/SearchFilterChain.d.ts.map +1 -0
  87. package/dist/search/SearchFilterChain.js +2 -4
  88. package/dist/search/SearchManager.d.ts +326 -0
  89. package/dist/search/SearchManager.d.ts.map +1 -0
  90. package/dist/search/SearchManager.js +148 -0
  91. package/dist/search/SearchSuggestions.d.ts +27 -0
  92. package/dist/search/SearchSuggestions.d.ts.map +1 -0
  93. package/dist/search/SearchSuggestions.js +1 -1
  94. package/dist/search/SemanticSearch.d.ts +149 -0
  95. package/dist/search/SemanticSearch.d.ts.map +1 -0
  96. package/dist/search/SemanticSearch.js +323 -0
  97. package/dist/search/TFIDFEventSync.d.ts +85 -0
  98. package/dist/search/TFIDFEventSync.d.ts.map +1 -0
  99. package/dist/search/TFIDFEventSync.js +133 -0
  100. package/dist/search/TFIDFIndexManager.d.ts +151 -0
  101. package/dist/search/TFIDFIndexManager.d.ts.map +1 -0
  102. package/dist/search/TFIDFIndexManager.js +232 -17
  103. package/dist/search/VectorStore.d.ts +235 -0
  104. package/dist/search/VectorStore.d.ts.map +1 -0
  105. package/dist/search/VectorStore.js +311 -0
  106. package/dist/search/index.d.ts +21 -0
  107. package/dist/search/index.d.ts.map +1 -0
  108. package/dist/search/index.js +12 -0
  109. package/dist/server/MCPServer.d.ts +21 -0
  110. package/dist/server/MCPServer.d.ts.map +1 -0
  111. package/dist/server/MCPServer.js +4 -4
  112. package/dist/server/responseCompressor.d.ts +94 -0
  113. package/dist/server/responseCompressor.d.ts.map +1 -0
  114. package/dist/server/responseCompressor.js +127 -0
  115. package/dist/server/toolDefinitions.d.ts +27 -0
  116. package/dist/server/toolDefinitions.d.ts.map +1 -0
  117. package/dist/server/toolDefinitions.js +189 -18
  118. package/dist/server/toolHandlers.d.ts +41 -0
  119. package/dist/server/toolHandlers.d.ts.map +1 -0
  120. package/dist/server/toolHandlers.js +467 -75
  121. package/dist/types/index.d.ts +13 -0
  122. package/dist/types/index.d.ts.map +1 -0
  123. package/dist/types/index.js +1 -1
  124. package/dist/types/types.d.ts +1654 -0
  125. package/dist/types/types.d.ts.map +1 -0
  126. package/dist/types/types.js +9 -0
  127. package/dist/utils/compressedCache.d.ts +192 -0
  128. package/dist/utils/compressedCache.d.ts.map +1 -0
  129. package/dist/utils/compressedCache.js +309 -0
  130. package/dist/utils/compressionUtil.d.ts +214 -0
  131. package/dist/utils/compressionUtil.d.ts.map +1 -0
  132. package/dist/utils/compressionUtil.js +247 -0
  133. package/dist/utils/constants.d.ts +245 -0
  134. package/dist/utils/constants.d.ts.map +1 -0
  135. package/dist/utils/constants.js +124 -0
  136. package/dist/utils/entityUtils.d.ts +321 -0
  137. package/dist/utils/entityUtils.d.ts.map +1 -0
  138. package/dist/utils/entityUtils.js +434 -4
  139. package/dist/utils/errors.d.ts +95 -0
  140. package/dist/utils/errors.d.ts.map +1 -0
  141. package/dist/utils/errors.js +24 -0
  142. package/dist/utils/formatters.d.ts +145 -0
  143. package/dist/utils/formatters.d.ts.map +1 -0
  144. package/dist/utils/{paginationUtils.js → formatters.js} +54 -3
  145. package/dist/utils/index.d.ts +23 -0
  146. package/dist/utils/index.d.ts.map +1 -0
  147. package/dist/utils/index.js +69 -31
  148. package/dist/utils/indexes.d.ts +270 -0
  149. package/dist/utils/indexes.d.ts.map +1 -0
  150. package/dist/utils/indexes.js +526 -0
  151. package/dist/utils/logger.d.ts +24 -0
  152. package/dist/utils/logger.d.ts.map +1 -0
  153. package/dist/utils/operationUtils.d.ts +124 -0
  154. package/dist/utils/operationUtils.d.ts.map +1 -0
  155. package/dist/utils/operationUtils.js +175 -0
  156. package/dist/utils/parallelUtils.d.ts +72 -0
  157. package/dist/utils/parallelUtils.d.ts.map +1 -0
  158. package/dist/utils/parallelUtils.js +169 -0
  159. package/dist/utils/schemas.d.ts +374 -0
  160. package/dist/utils/schemas.d.ts.map +1 -0
  161. package/dist/utils/schemas.js +302 -2
  162. package/dist/utils/searchAlgorithms.d.ts +99 -0
  163. package/dist/utils/searchAlgorithms.d.ts.map +1 -0
  164. package/dist/utils/searchAlgorithms.js +167 -0
  165. package/dist/utils/searchCache.d.ts +108 -0
  166. package/dist/utils/searchCache.d.ts.map +1 -0
  167. package/dist/utils/taskScheduler.d.ts +290 -0
  168. package/dist/utils/taskScheduler.d.ts.map +1 -0
  169. package/dist/utils/taskScheduler.js +466 -0
  170. package/dist/workers/index.d.ts +12 -0
  171. package/dist/workers/index.d.ts.map +1 -0
  172. package/dist/workers/index.js +9 -0
  173. package/dist/workers/levenshteinWorker.d.ts +60 -0
  174. package/dist/workers/levenshteinWorker.d.ts.map +1 -0
  175. package/dist/workers/levenshteinWorker.js +98 -0
  176. package/package.json +17 -4
  177. package/dist/__tests__/edge-cases/edge-cases.test.js +0 -406
  178. package/dist/__tests__/integration/workflows.test.js +0 -449
  179. package/dist/__tests__/performance/benchmarks.test.js +0 -413
  180. package/dist/__tests__/unit/core/EntityManager.test.js +0 -334
  181. package/dist/__tests__/unit/core/GraphStorage.test.js +0 -205
  182. package/dist/__tests__/unit/core/RelationManager.test.js +0 -274
  183. package/dist/__tests__/unit/features/CompressionManager.test.js +0 -350
  184. package/dist/__tests__/unit/search/BasicSearch.test.js +0 -311
  185. package/dist/__tests__/unit/search/BooleanSearch.test.js +0 -432
  186. package/dist/__tests__/unit/search/FuzzySearch.test.js +0 -448
  187. package/dist/__tests__/unit/search/RankedSearch.test.js +0 -379
  188. package/dist/__tests__/unit/utils/levenshtein.test.js +0 -77
  189. package/dist/core/KnowledgeGraphManager.js +0 -423
  190. package/dist/features/BackupManager.js +0 -311
  191. package/dist/features/ExportManager.js +0 -305
  192. package/dist/features/ImportExportManager.js +0 -50
  193. package/dist/features/ImportManager.js +0 -328
  194. package/dist/types/analytics.types.js +0 -6
  195. package/dist/types/entity.types.js +0 -7
  196. package/dist/types/import-export.types.js +0 -7
  197. package/dist/types/search.types.js +0 -7
  198. package/dist/types/tag.types.js +0 -6
  199. package/dist/utils/dateUtils.js +0 -89
  200. package/dist/utils/filterUtils.js +0 -155
  201. package/dist/utils/levenshtein.js +0 -62
  202. package/dist/utils/pathUtils.js +0 -115
  203. package/dist/utils/responseFormatter.js +0 -55
  204. package/dist/utils/tagUtils.js +0 -107
  205. package/dist/utils/tfidf.js +0 -90
  206. package/dist/utils/validationHelper.js +0 -99
  207. package/dist/utils/validationUtils.js +0 -109
@@ -0,0 +1,573 @@
1
+ /**
2
+ * Graph Traversal
3
+ *
4
+ * Phase 4 Sprints 6-8: Graph traversal algorithms for knowledge graph analysis.
5
+ * Includes BFS, DFS, shortest path, all paths, connected components, and centrality.
6
+ *
7
+ * @module core/GraphTraversal
8
+ */
9
+ import { checkCancellation } from '../utils/index.js';
10
+ /**
11
+ * Phase 4 Sprint 6: Default traversal options.
12
+ */
13
+ const DEFAULT_OPTIONS = {
14
+ direction: 'both',
15
+ maxDepth: Infinity,
16
+ relationTypes: [],
17
+ entityTypes: [],
18
+ };
19
+ /**
20
+ * Graph traversal algorithms for knowledge graph analysis.
21
+ *
22
+ * Provides BFS, DFS, shortest path finding, connected component detection,
23
+ * and centrality metrics for analyzing graph structure.
24
+ */
25
+ export class GraphTraversal {
26
+ storage;
27
+ constructor(storage) {
28
+ this.storage = storage;
29
+ }
30
+ // ==================== Sprint 6: BFS and DFS Traversal ====================
31
+ /**
32
+ * Get neighbors of a node based on traversal direction and filters.
33
+ *
34
+ * @param entityName - Entity to get neighbors for
35
+ * @param options - Traversal options
36
+ * @returns Array of neighbor entity names with their relations
37
+ */
38
+ getNeighborsWithRelations(entityName, options = {}) {
39
+ // Filter out undefined values before merging with defaults
40
+ const definedOptions = Object.fromEntries(Object.entries(options).filter(([, v]) => v !== undefined));
41
+ const opts = { ...DEFAULT_OPTIONS, ...definedOptions };
42
+ const neighbors = [];
43
+ // Get relations based on direction
44
+ let relations = [];
45
+ if (opts.direction === 'outgoing' || opts.direction === 'both') {
46
+ relations = relations.concat(this.storage.getRelationsFrom(entityName));
47
+ }
48
+ if (opts.direction === 'incoming' || opts.direction === 'both') {
49
+ relations = relations.concat(this.storage.getRelationsTo(entityName));
50
+ }
51
+ // Filter by relation types if specified
52
+ if (opts.relationTypes && opts.relationTypes.length > 0) {
53
+ const typeSet = new Set(opts.relationTypes.map(t => t.toLowerCase()));
54
+ relations = relations.filter(r => typeSet.has(r.relationType.toLowerCase()));
55
+ }
56
+ // Process relations to get neighbors
57
+ for (const relation of relations) {
58
+ const neighbor = relation.from === entityName ? relation.to : relation.from;
59
+ // Skip self-loops
60
+ if (neighbor === entityName)
61
+ continue;
62
+ // Filter by entity types if specified
63
+ if (opts.entityTypes && opts.entityTypes.length > 0) {
64
+ const entity = this.storage.getEntityByName(neighbor);
65
+ if (!entity)
66
+ continue;
67
+ const typeSet = new Set(opts.entityTypes.map(t => t.toLowerCase()));
68
+ if (!typeSet.has(entity.entityType.toLowerCase()))
69
+ continue;
70
+ }
71
+ neighbors.push({ neighbor, relation });
72
+ }
73
+ return neighbors;
74
+ }
75
+ /**
76
+ * Breadth-First Search traversal starting from a given entity.
77
+ *
78
+ * @param startEntity - Entity name to start traversal from
79
+ * @param options - Traversal options
80
+ * @returns Traversal result with visited nodes, depths, and parent pointers
81
+ */
82
+ bfs(startEntity, options = {}) {
83
+ const opts = { ...DEFAULT_OPTIONS, ...options };
84
+ // Validate start entity exists
85
+ if (!this.storage.hasEntity(startEntity)) {
86
+ return { nodes: [], depths: new Map(), parents: new Map() };
87
+ }
88
+ const visited = new Set();
89
+ const queue = [{ node: startEntity, depth: 0 }];
90
+ const nodes = [];
91
+ const depths = new Map();
92
+ const parents = new Map();
93
+ visited.add(startEntity);
94
+ parents.set(startEntity, null);
95
+ while (queue.length > 0) {
96
+ const { node, depth } = queue.shift();
97
+ // Respect maxDepth limit
98
+ if (depth > opts.maxDepth)
99
+ continue;
100
+ nodes.push(node);
101
+ depths.set(node, depth);
102
+ // Get neighbors and add unvisited ones to queue
103
+ const neighbors = this.getNeighborsWithRelations(node, opts);
104
+ for (const { neighbor } of neighbors) {
105
+ if (!visited.has(neighbor)) {
106
+ visited.add(neighbor);
107
+ queue.push({ node: neighbor, depth: depth + 1 });
108
+ parents.set(neighbor, node);
109
+ }
110
+ }
111
+ }
112
+ return { nodes, depths, parents };
113
+ }
114
+ /**
115
+ * Depth-First Search traversal starting from a given entity.
116
+ *
117
+ * @param startEntity - Entity name to start traversal from
118
+ * @param options - Traversal options
119
+ * @returns Traversal result with visited nodes, depths, and parent pointers
120
+ */
121
+ dfs(startEntity, options = {}) {
122
+ const opts = { ...DEFAULT_OPTIONS, ...options };
123
+ // Validate start entity exists
124
+ if (!this.storage.hasEntity(startEntity)) {
125
+ return { nodes: [], depths: new Map(), parents: new Map() };
126
+ }
127
+ const visited = new Set();
128
+ const stack = [{ node: startEntity, depth: 0 }];
129
+ const nodes = [];
130
+ const depths = new Map();
131
+ const parents = new Map();
132
+ parents.set(startEntity, null);
133
+ while (stack.length > 0) {
134
+ const { node, depth } = stack.pop();
135
+ // Skip if already visited
136
+ if (visited.has(node))
137
+ continue;
138
+ // Respect maxDepth limit
139
+ if (depth > opts.maxDepth)
140
+ continue;
141
+ visited.add(node);
142
+ nodes.push(node);
143
+ depths.set(node, depth);
144
+ // Get neighbors and add unvisited ones to stack
145
+ const neighbors = this.getNeighborsWithRelations(node, opts);
146
+ for (const { neighbor } of neighbors) {
147
+ if (!visited.has(neighbor)) {
148
+ stack.push({ node: neighbor, depth: depth + 1 });
149
+ if (!parents.has(neighbor)) {
150
+ parents.set(neighbor, node);
151
+ }
152
+ }
153
+ }
154
+ }
155
+ return { nodes, depths, parents };
156
+ }
157
+ // ==================== Sprint 7: Path Finding Algorithms ====================
158
+ /**
159
+ * Find the shortest path between two entities using BFS.
160
+ *
161
+ * @param source - Source entity name
162
+ * @param target - Target entity name
163
+ * @param options - Traversal options
164
+ * @returns PathResult if path exists, null otherwise
165
+ */
166
+ async findShortestPath(source, target, options = {}) {
167
+ // Ensure graph is loaded to populate indexes
168
+ await this.storage.loadGraph();
169
+ // Validate entities exist
170
+ if (!this.storage.hasEntity(source) || !this.storage.hasEntity(target)) {
171
+ return null;
172
+ }
173
+ // Same source and target
174
+ if (source === target) {
175
+ return { path: [source], length: 0, relations: [] };
176
+ }
177
+ const opts = { ...DEFAULT_OPTIONS, ...options };
178
+ const visited = new Set();
179
+ const queue = [source];
180
+ const parents = new Map();
181
+ visited.add(source);
182
+ parents.set(source, null);
183
+ while (queue.length > 0) {
184
+ const current = queue.shift();
185
+ // Found target, reconstruct path
186
+ if (current === target) {
187
+ return this.reconstructPath(source, target, parents);
188
+ }
189
+ // Get neighbors
190
+ const neighbors = this.getNeighborsWithRelations(current, opts);
191
+ for (const { neighbor, relation } of neighbors) {
192
+ if (!visited.has(neighbor)) {
193
+ visited.add(neighbor);
194
+ queue.push(neighbor);
195
+ parents.set(neighbor, { parent: current, relation });
196
+ }
197
+ }
198
+ }
199
+ // No path found
200
+ return null;
201
+ }
202
+ /**
203
+ * Reconstruct path from parent pointers.
204
+ */
205
+ reconstructPath(_source, target, parents) {
206
+ const path = [];
207
+ const relations = [];
208
+ let current = target;
209
+ while (current !== null) {
210
+ path.unshift(current);
211
+ const parentInfo = parents.get(current);
212
+ if (parentInfo) {
213
+ relations.unshift(parentInfo.relation);
214
+ current = parentInfo.parent;
215
+ }
216
+ else {
217
+ current = null;
218
+ }
219
+ }
220
+ return {
221
+ path,
222
+ length: path.length - 1,
223
+ relations,
224
+ };
225
+ }
226
+ /**
227
+ * Find all paths between two entities up to a maximum depth.
228
+ *
229
+ * Phase 9B: Supports cancellation via AbortSignal in options.
230
+ *
231
+ * @param source - Source entity name
232
+ * @param target - Target entity name
233
+ * @param maxDepth - Maximum path length (default: 5)
234
+ * @param options - Traversal options (includes signal for cancellation)
235
+ * @returns Array of PathResult objects for all found paths
236
+ * @throws {OperationCancelledError} If operation is cancelled via signal (Phase 9B)
237
+ */
238
+ async findAllPaths(source, target, maxDepth = 5, options = {}) {
239
+ // Check for early cancellation
240
+ const { signal, ...traversalOptions } = options;
241
+ checkCancellation(signal, 'findAllPaths');
242
+ // Ensure graph is loaded to populate indexes
243
+ await this.storage.loadGraph();
244
+ // Check for cancellation after load
245
+ checkCancellation(signal, 'findAllPaths');
246
+ // Validate entities exist
247
+ if (!this.storage.hasEntity(source) || !this.storage.hasEntity(target)) {
248
+ return [];
249
+ }
250
+ const opts = { ...DEFAULT_OPTIONS, ...traversalOptions };
251
+ const allPaths = [];
252
+ const currentPath = [source];
253
+ const currentRelations = [];
254
+ const visited = new Set([source]);
255
+ // Track iterations for periodic cancellation checks
256
+ let iterationCount = 0;
257
+ const CANCELLATION_CHECK_INTERVAL = 100;
258
+ const dfsAllPaths = (current, depth) => {
259
+ // Periodic cancellation check
260
+ iterationCount++;
261
+ if (iterationCount % CANCELLATION_CHECK_INTERVAL === 0) {
262
+ checkCancellation(signal, 'findAllPaths');
263
+ }
264
+ if (depth > maxDepth)
265
+ return;
266
+ if (current === target && depth > 0) {
267
+ allPaths.push({
268
+ path: [...currentPath],
269
+ length: currentPath.length - 1,
270
+ relations: [...currentRelations],
271
+ });
272
+ return;
273
+ }
274
+ const neighbors = this.getNeighborsWithRelations(current, opts);
275
+ for (const { neighbor, relation } of neighbors) {
276
+ if (!visited.has(neighbor)) {
277
+ visited.add(neighbor);
278
+ currentPath.push(neighbor);
279
+ currentRelations.push(relation);
280
+ dfsAllPaths(neighbor, depth + 1);
281
+ currentPath.pop();
282
+ currentRelations.pop();
283
+ visited.delete(neighbor);
284
+ }
285
+ }
286
+ };
287
+ dfsAllPaths(source, 0);
288
+ return allPaths;
289
+ }
290
+ // ==================== Sprint 8: Connected Components ====================
291
+ /**
292
+ * Find all connected components in the graph.
293
+ *
294
+ * Uses BFS to find all weakly connected components (treating the graph as undirected).
295
+ *
296
+ * @returns ConnectedComponentsResult with all components
297
+ */
298
+ async findConnectedComponents() {
299
+ const graph = await this.storage.loadGraph();
300
+ const visited = new Set();
301
+ const components = [];
302
+ for (const entity of graph.entities) {
303
+ if (!visited.has(entity.name)) {
304
+ // BFS to find all nodes in this component
305
+ const component = [];
306
+ const queue = [entity.name];
307
+ visited.add(entity.name);
308
+ while (queue.length > 0) {
309
+ const current = queue.shift();
310
+ component.push(current);
311
+ // Get all neighbors (both directions for weakly connected)
312
+ const neighbors = this.getNeighborsWithRelations(current, { direction: 'both' });
313
+ for (const { neighbor } of neighbors) {
314
+ if (!visited.has(neighbor)) {
315
+ visited.add(neighbor);
316
+ queue.push(neighbor);
317
+ }
318
+ }
319
+ }
320
+ components.push(component);
321
+ }
322
+ }
323
+ // Sort components by size (largest first)
324
+ components.sort((a, b) => b.length - a.length);
325
+ return {
326
+ components,
327
+ count: components.length,
328
+ largestComponentSize: components.length > 0 ? components[0].length : 0,
329
+ };
330
+ }
331
+ // ==================== Sprint 8: Centrality Algorithms ====================
332
+ /**
333
+ * Calculate degree centrality for all entities.
334
+ *
335
+ * Degree centrality is the number of connections an entity has,
336
+ * normalized by the maximum possible connections.
337
+ *
338
+ * @param direction - Direction to count: 'in', 'out', or 'both' (default)
339
+ * @param topN - Number of top entities to return (default: 10)
340
+ * @returns CentralityResult with scores and top entities
341
+ */
342
+ async calculateDegreeCentrality(direction = 'both', topN = 10) {
343
+ const graph = await this.storage.loadGraph();
344
+ const scores = new Map();
345
+ const n = graph.entities.length;
346
+ // Calculate degree for each entity
347
+ for (const entity of graph.entities) {
348
+ let degree = 0;
349
+ if (direction === 'in' || direction === 'both') {
350
+ degree += this.storage.getRelationsTo(entity.name).length;
351
+ }
352
+ if (direction === 'out' || direction === 'both') {
353
+ degree += this.storage.getRelationsFrom(entity.name).length;
354
+ }
355
+ // Normalize by maximum possible degree
356
+ const normalizedDegree = n > 1 ? degree / (n - 1) : 0;
357
+ scores.set(entity.name, normalizedDegree);
358
+ }
359
+ // Get top N entities
360
+ const topEntities = this.getTopEntities(scores, topN);
361
+ return {
362
+ scores,
363
+ topEntities,
364
+ algorithm: 'degree',
365
+ };
366
+ }
367
+ /**
368
+ * Calculate betweenness centrality for all entities.
369
+ *
370
+ * Betweenness centrality measures how often a node appears on shortest paths
371
+ * between other nodes. Uses Brandes' algorithm for efficiency.
372
+ *
373
+ * @param options - Configuration options
374
+ * @param options.topN - Number of top entities to return (default: 10)
375
+ * @param options.chunkSize - Yield control every N vertices (default: 50)
376
+ * @param options.onProgress - Progress callback (0.0 to 1.0)
377
+ * @param options.approximate - Use approximation for faster results (default: false)
378
+ * @param options.sampleRate - Sample rate for approximation (default: 0.2)
379
+ * @returns CentralityResult with scores and top entities
380
+ */
381
+ async calculateBetweennessCentrality(options = {}) {
382
+ const { topN = 10, chunkSize = 50, onProgress, approximate = false, sampleRate = 0.2 } = options;
383
+ const graph = await this.storage.loadGraph();
384
+ const scores = new Map();
385
+ // Initialize scores
386
+ for (const entity of graph.entities) {
387
+ scores.set(entity.name, 0);
388
+ }
389
+ // Determine which sources to process (full or sampled)
390
+ let sourcesToProcess = graph.entities;
391
+ if (approximate && graph.entities.length > 100) {
392
+ const sampleSize = Math.max(10, Math.floor(graph.entities.length * sampleRate));
393
+ sourcesToProcess = this.sampleEntities(graph.entities, sampleSize);
394
+ }
395
+ // Brandes' algorithm with chunked processing
396
+ let processed = 0;
397
+ for (const source of sourcesToProcess) {
398
+ const stack = [];
399
+ const predecessors = new Map();
400
+ const sigma = new Map(); // Number of shortest paths
401
+ const distance = new Map(); // Distance from source
402
+ const delta = new Map(); // Dependency
403
+ // Initialize
404
+ for (const entity of graph.entities) {
405
+ predecessors.set(entity.name, []);
406
+ sigma.set(entity.name, 0);
407
+ distance.set(entity.name, -1);
408
+ delta.set(entity.name, 0);
409
+ }
410
+ sigma.set(source.name, 1);
411
+ distance.set(source.name, 0);
412
+ // BFS
413
+ const queue = [source.name];
414
+ while (queue.length > 0) {
415
+ const v = queue.shift();
416
+ stack.push(v);
417
+ const neighbors = this.getNeighborsWithRelations(v, { direction: 'both' });
418
+ for (const { neighbor: w } of neighbors) {
419
+ // First time w is discovered
420
+ if (distance.get(w) === -1) {
421
+ distance.set(w, distance.get(v) + 1);
422
+ queue.push(w);
423
+ }
424
+ // w is on a shortest path from source via v
425
+ if (distance.get(w) === distance.get(v) + 1) {
426
+ sigma.set(w, sigma.get(w) + sigma.get(v));
427
+ predecessors.get(w).push(v);
428
+ }
429
+ }
430
+ }
431
+ // Accumulation
432
+ while (stack.length > 0) {
433
+ const w = stack.pop();
434
+ for (const v of predecessors.get(w)) {
435
+ const contribution = (sigma.get(v) / sigma.get(w)) * (1 + delta.get(w));
436
+ delta.set(v, delta.get(v) + contribution);
437
+ }
438
+ if (w !== source.name) {
439
+ scores.set(w, scores.get(w) + delta.get(w));
440
+ }
441
+ }
442
+ // Yield control periodically to prevent blocking event loop
443
+ processed++;
444
+ if (processed % chunkSize === 0) {
445
+ // Yield control to allow event loop to process other events
446
+ await new Promise(resolve => setImmediate(resolve));
447
+ // Report progress
448
+ if (onProgress) {
449
+ onProgress(processed / sourcesToProcess.length);
450
+ }
451
+ }
452
+ }
453
+ // Final progress update
454
+ if (onProgress) {
455
+ onProgress(1);
456
+ }
457
+ // Scale scores if using approximation
458
+ if (approximate && sampleRate < 1.0) {
459
+ const scaleFactor = 1 / sampleRate;
460
+ for (const [entity, score] of scores) {
461
+ scores.set(entity, score * scaleFactor);
462
+ }
463
+ }
464
+ // Normalize scores
465
+ const n = graph.entities.length;
466
+ const normalization = n > 2 ? 2 / ((n - 1) * (n - 2)) : 1;
467
+ for (const [name, score] of scores) {
468
+ scores.set(name, score * normalization);
469
+ }
470
+ const topEntities = this.getTopEntities(scores, topN);
471
+ return {
472
+ scores,
473
+ topEntities,
474
+ algorithm: 'betweenness',
475
+ };
476
+ }
477
+ /**
478
+ * Sample entities randomly for approximation algorithms.
479
+ *
480
+ * @param entities - Array of entities to sample from
481
+ * @param sampleSize - Number of entities to sample
482
+ * @returns Array of sampled entities
483
+ */
484
+ sampleEntities(entities, sampleSize) {
485
+ const shuffled = [...entities];
486
+ // Fisher-Yates shuffle
487
+ for (let i = shuffled.length - 1; i > 0; i--) {
488
+ const j = Math.floor(Math.random() * (i + 1));
489
+ [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
490
+ }
491
+ return shuffled.slice(0, sampleSize);
492
+ }
493
+ /**
494
+ * Calculate PageRank centrality for all entities.
495
+ *
496
+ * PageRank measures importance based on incoming connections from
497
+ * other important nodes. Uses iterative power method.
498
+ *
499
+ * @param dampingFactor - Damping factor (default: 0.85)
500
+ * @param maxIterations - Maximum iterations (default: 100)
501
+ * @param tolerance - Convergence tolerance (default: 1e-6)
502
+ * @param topN - Number of top entities to return (default: 10)
503
+ * @returns CentralityResult with scores and top entities
504
+ */
505
+ async calculatePageRank(dampingFactor = 0.85, maxIterations = 100, tolerance = 1e-6, topN = 10) {
506
+ const graph = await this.storage.loadGraph();
507
+ const n = graph.entities.length;
508
+ if (n === 0) {
509
+ return { scores: new Map(), topEntities: [], algorithm: 'pagerank' };
510
+ }
511
+ // Initialize PageRank scores
512
+ const scores = new Map();
513
+ const initialScore = 1 / n;
514
+ for (const entity of graph.entities) {
515
+ scores.set(entity.name, initialScore);
516
+ }
517
+ // Build outgoing links map
518
+ const outLinks = new Map();
519
+ for (const entity of graph.entities) {
520
+ const outgoing = this.storage.getRelationsFrom(entity.name);
521
+ outLinks.set(entity.name, outgoing.map(r => r.to));
522
+ }
523
+ // Power iteration
524
+ for (let iteration = 0; iteration < maxIterations; iteration++) {
525
+ const newScores = new Map();
526
+ let totalDiff = 0;
527
+ // Calculate dangling node contribution (nodes with no outgoing links)
528
+ let danglingSum = 0;
529
+ for (const entity of graph.entities) {
530
+ if (outLinks.get(entity.name).length === 0) {
531
+ danglingSum += scores.get(entity.name);
532
+ }
533
+ }
534
+ const danglingContribution = (dampingFactor * danglingSum) / n;
535
+ // Calculate new scores
536
+ for (const entity of graph.entities) {
537
+ let incomingScore = 0;
538
+ const incoming = this.storage.getRelationsTo(entity.name);
539
+ for (const relation of incoming) {
540
+ const source = relation.from;
541
+ const sourceOutCount = outLinks.get(source)?.length || 1;
542
+ incomingScore += scores.get(source) / sourceOutCount;
543
+ }
544
+ const newScore = (1 - dampingFactor) / n + dampingFactor * incomingScore + danglingContribution;
545
+ newScores.set(entity.name, newScore);
546
+ totalDiff += Math.abs(newScore - scores.get(entity.name));
547
+ }
548
+ // Update scores
549
+ for (const [name, score] of newScores) {
550
+ scores.set(name, score);
551
+ }
552
+ // Check convergence
553
+ if (totalDiff < tolerance) {
554
+ break;
555
+ }
556
+ }
557
+ const topEntities = this.getTopEntities(scores, topN);
558
+ return {
559
+ scores,
560
+ topEntities,
561
+ algorithm: 'pagerank',
562
+ };
563
+ }
564
+ /**
565
+ * Get top N entities from a scores map.
566
+ */
567
+ getTopEntities(scores, topN) {
568
+ return Array.from(scores.entries())
569
+ .sort((a, b) => b[1] - a[1])
570
+ .slice(0, topN)
571
+ .map(([name, score]) => ({ name, score }));
572
+ }
573
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Hierarchy Manager
3
+ *
4
+ * Handles entity hierarchy operations (parent-child relationships).
5
+ * Extracted from EntityManager (Phase 4: Consolidate God Objects).
6
+ *
7
+ * @module core/HierarchyManager
8
+ */
9
+ import type { Entity, KnowledgeGraph } from '../types/index.js';
10
+ import type { GraphStorage } from './GraphStorage.js';
11
+ /**
12
+ * Manages hierarchical relationships between entities.
13
+ */
14
+ export declare class HierarchyManager {
15
+ private storage;
16
+ constructor(storage: GraphStorage);
17
+ /**
18
+ * Set the parent of an entity.
19
+ *
20
+ * Validates:
21
+ * - Entity and parent exist
22
+ * - Setting parent won't create a cycle
23
+ *
24
+ * @param entityName - Entity to set parent for
25
+ * @param parentName - Parent entity name (null to remove parent)
26
+ * @returns Updated entity
27
+ * @throws {EntityNotFoundError} If entity or parent not found
28
+ * @throws {CycleDetectedError} If setting parent would create a cycle
29
+ */
30
+ setEntityParent(entityName: string, parentName: string | null): Promise<Entity>;
31
+ /**
32
+ * Check if setting a parent would create a cycle in the hierarchy.
33
+ *
34
+ * @param graph - Knowledge graph
35
+ * @param entityName - Entity to set parent for
36
+ * @param parentName - Proposed parent
37
+ * @returns True if cycle would be created
38
+ */
39
+ private wouldCreateCycle;
40
+ /**
41
+ * Get the immediate children of an entity.
42
+ *
43
+ * @param entityName - Parent entity name
44
+ * @returns Array of child entities
45
+ * @throws {EntityNotFoundError} If entity not found
46
+ */
47
+ getChildren(entityName: string): Promise<Entity[]>;
48
+ /**
49
+ * Get the parent of an entity.
50
+ *
51
+ * @param entityName - Entity name
52
+ * @returns Parent entity or null if no parent
53
+ * @throws {EntityNotFoundError} If entity not found
54
+ */
55
+ getParent(entityName: string): Promise<Entity | null>;
56
+ /**
57
+ * Get all ancestors of an entity (parent, grandparent, etc.).
58
+ *
59
+ * @param entityName - Entity name
60
+ * @returns Array of ancestor entities (ordered from immediate parent to root)
61
+ * @throws {EntityNotFoundError} If entity not found
62
+ */
63
+ getAncestors(entityName: string): Promise<Entity[]>;
64
+ /**
65
+ * Get all descendants of an entity (children, grandchildren, etc.).
66
+ *
67
+ * Uses breadth-first traversal.
68
+ *
69
+ * @param entityName - Entity name
70
+ * @returns Array of descendant entities
71
+ * @throws {EntityNotFoundError} If entity not found
72
+ */
73
+ getDescendants(entityName: string): Promise<Entity[]>;
74
+ /**
75
+ * Get the entire subtree rooted at an entity (entity + all descendants).
76
+ *
77
+ * Includes relations between entities in the subtree.
78
+ *
79
+ * @param entityName - Root entity name
80
+ * @returns Knowledge graph containing subtree
81
+ * @throws {EntityNotFoundError} If entity not found
82
+ */
83
+ getSubtree(entityName: string): Promise<KnowledgeGraph>;
84
+ /**
85
+ * Get root entities (entities with no parent).
86
+ *
87
+ * @returns Array of root entities
88
+ */
89
+ getRootEntities(): Promise<Entity[]>;
90
+ /**
91
+ * Get the depth of an entity in the hierarchy.
92
+ *
93
+ * Root entities have depth 0, their children depth 1, etc.
94
+ *
95
+ * @param entityName - Entity name
96
+ * @returns Depth (number of ancestors)
97
+ * @throws {EntityNotFoundError} If entity not found
98
+ */
99
+ getEntityDepth(entityName: string): Promise<number>;
100
+ /**
101
+ * Move an entity to a new parent (maintaining its descendants).
102
+ *
103
+ * Alias for setEntityParent.
104
+ *
105
+ * @param entityName - Entity to move
106
+ * @param newParentName - New parent name (null to make root)
107
+ * @returns Updated entity
108
+ */
109
+ moveEntity(entityName: string, newParentName: string | null): Promise<Entity>;
110
+ }
111
+ //# sourceMappingURL=HierarchyManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HierarchyManager.d.ts","sourceRoot":"","sources":["../../src/core/HierarchyManager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAA0B,MAAM,mBAAmB,CAAC;AACxF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD;;GAEG;AACH,qBAAa,gBAAgB;IACf,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,YAAY;IAEzC;;;;;;;;;;;;OAYG;IACG,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IAgCrF;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IAwBxB;;;;;;OAMG;IACG,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAWxD;;;;;;OAMG;IACG,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAgB3D;;;;;;OAMG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAmBzD;;;;;;;;OAQG;IACG,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAwB3D;;;;;;;;OAQG;IACG,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAuB7D;;;;OAIG;IACG,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAK1C;;;;;;;;OAQG;IACG,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKzD;;;;;;;;OAQG;IACG,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;CAGpF"}