@danielsimonjr/memory-mcp 11.0.1 → 11.1.1

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 (172) hide show
  1. package/LICENSE +22 -22
  2. package/dist/core/EntityManager.d.ts +10 -15
  3. package/dist/core/EntityManager.d.ts.map +1 -1
  4. package/dist/core/EntityManager.js +21 -54
  5. package/dist/core/GraphStorage.d.ts +0 -51
  6. package/dist/core/GraphStorage.d.ts.map +1 -1
  7. package/dist/core/GraphStorage.js +2 -79
  8. package/dist/core/GraphTraversal.d.ts +2 -7
  9. package/dist/core/GraphTraversal.d.ts.map +1 -1
  10. package/dist/core/GraphTraversal.js +2 -19
  11. package/dist/core/ManagerContext.d.ts +0 -4
  12. package/dist/core/ManagerContext.d.ts.map +1 -1
  13. package/dist/core/ManagerContext.js +2 -12
  14. package/dist/core/RelationManager.d.ts.map +1 -1
  15. package/dist/core/RelationManager.js +4 -5
  16. package/dist/core/SQLiteStorage.d.ts.map +1 -1
  17. package/dist/core/SQLiteStorage.js +2 -3
  18. package/dist/core/TransactionManager.d.ts +2 -207
  19. package/dist/core/TransactionManager.d.ts.map +1 -1
  20. package/dist/core/TransactionManager.js +6 -482
  21. package/dist/core/index.d.ts +1 -2
  22. package/dist/core/index.d.ts.map +1 -1
  23. package/dist/core/index.js +1 -3
  24. package/dist/features/ArchiveManager.d.ts +2 -14
  25. package/dist/features/ArchiveManager.d.ts.map +1 -1
  26. package/dist/features/ArchiveManager.js +3 -44
  27. package/dist/features/CompressionManager.d.ts +4 -14
  28. package/dist/features/CompressionManager.d.ts.map +1 -1
  29. package/dist/features/CompressionManager.js +9 -74
  30. package/dist/features/IOManager.d.ts +2 -6
  31. package/dist/features/IOManager.d.ts.map +1 -1
  32. package/dist/features/IOManager.js +10 -105
  33. package/dist/features/StreamingExporter.d.ts +4 -27
  34. package/dist/features/StreamingExporter.d.ts.map +1 -1
  35. package/dist/features/StreamingExporter.js +4 -65
  36. package/dist/features/index.d.ts +0 -2
  37. package/dist/features/index.d.ts.map +1 -1
  38. package/dist/features/index.js +0 -3
  39. package/dist/search/EmbeddingService.d.ts +9 -108
  40. package/dist/search/EmbeddingService.d.ts.map +1 -1
  41. package/dist/search/EmbeddingService.js +15 -187
  42. package/dist/search/FuzzySearch.js +1 -1
  43. package/dist/search/SavedSearchManager.d.ts.map +1 -1
  44. package/dist/search/SavedSearchManager.js +2 -3
  45. package/dist/search/SearchManager.d.ts +1 -42
  46. package/dist/search/SearchManager.d.ts.map +1 -1
  47. package/dist/search/SearchManager.js +0 -115
  48. package/dist/search/SemanticSearch.d.ts +1 -4
  49. package/dist/search/SemanticSearch.d.ts.map +1 -1
  50. package/dist/search/SemanticSearch.js +2 -12
  51. package/dist/search/TFIDFIndexManager.d.ts +0 -88
  52. package/dist/search/TFIDFIndexManager.d.ts.map +1 -1
  53. package/dist/search/TFIDFIndexManager.js +0 -217
  54. package/dist/search/index.d.ts +1 -18
  55. package/dist/search/index.d.ts.map +1 -1
  56. package/dist/search/index.js +1 -32
  57. package/dist/server/MCPServer.d.ts.map +1 -1
  58. package/dist/server/MCPServer.js +4 -1
  59. package/dist/server/responseCompressor.js +5 -5
  60. package/dist/server/toolDefinitions.d.ts.map +1 -1
  61. package/dist/server/toolDefinitions.js +5 -1
  62. package/dist/server/toolHandlers.d.ts +9 -5
  63. package/dist/server/toolHandlers.d.ts.map +1 -1
  64. package/dist/server/toolHandlers.js +23 -8
  65. package/dist/types/index.d.ts +1 -1
  66. package/dist/types/index.d.ts.map +1 -1
  67. package/dist/types/types.d.ts +2 -579
  68. package/dist/types/types.d.ts.map +1 -1
  69. package/dist/utils/compressedCache.d.ts +0 -29
  70. package/dist/utils/compressedCache.d.ts.map +1 -1
  71. package/dist/utils/compressedCache.js +0 -39
  72. package/dist/utils/entityUtils.d.ts +1 -59
  73. package/dist/utils/entityUtils.d.ts.map +1 -1
  74. package/dist/utils/entityUtils.js +3 -113
  75. package/dist/utils/errors.d.ts +0 -18
  76. package/dist/utils/errors.d.ts.map +1 -1
  77. package/dist/utils/errors.js +0 -24
  78. package/dist/utils/index.d.ts +2 -6
  79. package/dist/utils/index.d.ts.map +1 -1
  80. package/dist/utils/index.js +2 -14
  81. package/dist/utils/logger.d.ts +0 -7
  82. package/dist/utils/logger.d.ts.map +1 -1
  83. package/dist/utils/logger.js +2 -9
  84. package/dist/utils/parallelUtils.d.ts +1 -5
  85. package/dist/utils/parallelUtils.d.ts.map +1 -1
  86. package/dist/utils/parallelUtils.js +1 -23
  87. package/dist/utils/schemas.d.ts +16 -16
  88. package/dist/utils/schemas.d.ts.map +1 -1
  89. package/dist/utils/schemas.js +12 -12
  90. package/dist/utils/taskScheduler.d.ts +0 -4
  91. package/dist/utils/taskScheduler.d.ts.map +1 -1
  92. package/dist/utils/taskScheduler.js +1 -21
  93. package/dist/workers/WorkerPool.d.ts +81 -0
  94. package/dist/workers/WorkerPool.d.ts.map +1 -0
  95. package/dist/workers/WorkerPool.js +121 -0
  96. package/dist/workers/index.d.ts +1 -1
  97. package/dist/workers/index.d.ts.map +1 -1
  98. package/dist/workers/levenshteinWorker.js +1 -1
  99. package/package.json +1 -4
  100. package/dist/__tests__/file-path.test.js +0 -119
  101. package/dist/__tests__/knowledge-graph.test.js +0 -318
  102. package/dist/core/GraphEventEmitter.d.ts +0 -202
  103. package/dist/core/GraphEventEmitter.d.ts.map +0 -1
  104. package/dist/core/GraphEventEmitter.js +0 -346
  105. package/dist/features/KeywordExtractor.d.ts +0 -61
  106. package/dist/features/KeywordExtractor.d.ts.map +0 -1
  107. package/dist/features/KeywordExtractor.js +0 -126
  108. package/dist/features/ObservationNormalizer.d.ts +0 -90
  109. package/dist/features/ObservationNormalizer.d.ts.map +0 -1
  110. package/dist/features/ObservationNormalizer.js +0 -193
  111. package/dist/memory.jsonl +0 -1
  112. package/dist/search/BM25Search.d.ts +0 -148
  113. package/dist/search/BM25Search.d.ts.map +0 -1
  114. package/dist/search/BM25Search.js +0 -339
  115. package/dist/search/EarlyTerminationManager.d.ts +0 -140
  116. package/dist/search/EarlyTerminationManager.d.ts.map +0 -1
  117. package/dist/search/EarlyTerminationManager.js +0 -279
  118. package/dist/search/EmbeddingCache.d.ts +0 -175
  119. package/dist/search/EmbeddingCache.d.ts.map +0 -1
  120. package/dist/search/EmbeddingCache.js +0 -246
  121. package/dist/search/HybridScorer.d.ts +0 -181
  122. package/dist/search/HybridScorer.d.ts.map +0 -1
  123. package/dist/search/HybridScorer.js +0 -257
  124. package/dist/search/HybridSearchManager.d.ts +0 -80
  125. package/dist/search/HybridSearchManager.d.ts.map +0 -1
  126. package/dist/search/HybridSearchManager.js +0 -187
  127. package/dist/search/IncrementalIndexer.d.ts +0 -201
  128. package/dist/search/IncrementalIndexer.d.ts.map +0 -1
  129. package/dist/search/IncrementalIndexer.js +0 -342
  130. package/dist/search/OptimizedInvertedIndex.d.ts +0 -163
  131. package/dist/search/OptimizedInvertedIndex.d.ts.map +0 -1
  132. package/dist/search/OptimizedInvertedIndex.js +0 -358
  133. package/dist/search/ParallelSearchExecutor.d.ts +0 -172
  134. package/dist/search/ParallelSearchExecutor.d.ts.map +0 -1
  135. package/dist/search/ParallelSearchExecutor.js +0 -309
  136. package/dist/search/QuantizedVectorStore.d.ts +0 -171
  137. package/dist/search/QuantizedVectorStore.d.ts.map +0 -1
  138. package/dist/search/QuantizedVectorStore.js +0 -307
  139. package/dist/search/QueryAnalyzer.d.ts +0 -76
  140. package/dist/search/QueryAnalyzer.d.ts.map +0 -1
  141. package/dist/search/QueryAnalyzer.js +0 -227
  142. package/dist/search/QueryCostEstimator.d.ts +0 -244
  143. package/dist/search/QueryCostEstimator.d.ts.map +0 -1
  144. package/dist/search/QueryCostEstimator.js +0 -652
  145. package/dist/search/QueryPlanCache.d.ts +0 -220
  146. package/dist/search/QueryPlanCache.d.ts.map +0 -1
  147. package/dist/search/QueryPlanCache.js +0 -379
  148. package/dist/search/QueryPlanner.d.ts +0 -58
  149. package/dist/search/QueryPlanner.d.ts.map +0 -1
  150. package/dist/search/QueryPlanner.js +0 -137
  151. package/dist/search/ReflectionManager.d.ts +0 -120
  152. package/dist/search/ReflectionManager.d.ts.map +0 -1
  153. package/dist/search/ReflectionManager.js +0 -231
  154. package/dist/search/SymbolicSearch.d.ts +0 -61
  155. package/dist/search/SymbolicSearch.d.ts.map +0 -1
  156. package/dist/search/SymbolicSearch.js +0 -163
  157. package/dist/search/TFIDFEventSync.d.ts +0 -85
  158. package/dist/search/TFIDFEventSync.d.ts.map +0 -1
  159. package/dist/search/TFIDFEventSync.js +0 -133
  160. package/dist/utils/BatchProcessor.d.ts +0 -271
  161. package/dist/utils/BatchProcessor.d.ts.map +0 -1
  162. package/dist/utils/BatchProcessor.js +0 -376
  163. package/dist/utils/MemoryMonitor.d.ts +0 -176
  164. package/dist/utils/MemoryMonitor.d.ts.map +0 -1
  165. package/dist/utils/MemoryMonitor.js +0 -305
  166. package/dist/utils/WorkerPoolManager.d.ts +0 -233
  167. package/dist/utils/WorkerPoolManager.d.ts.map +0 -1
  168. package/dist/utils/WorkerPoolManager.js +0 -420
  169. package/dist/utils/operationUtils.d.ts +0 -124
  170. package/dist/utils/operationUtils.d.ts.map +0 -1
  171. package/dist/utils/operationUtils.js +0 -175
  172. package/dist/vitest.config.js +0 -13
@@ -1,119 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { promises as fs } from 'fs';
3
- import path from 'path';
4
- import { fileURLToPath } from 'url';
5
- import { ensureMemoryFilePath, defaultMemoryPath } from '../index.js';
6
- describe('ensureMemoryFilePath', () => {
7
- const testDir = path.dirname(fileURLToPath(import.meta.url));
8
- const oldMemoryPath = path.join(testDir, '..', 'memory.json');
9
- const newMemoryPath = path.join(testDir, '..', 'memory.jsonl');
10
- let originalEnv;
11
- beforeEach(() => {
12
- // Save original environment variable
13
- originalEnv = process.env.MEMORY_FILE_PATH;
14
- // Delete environment variable
15
- delete process.env.MEMORY_FILE_PATH;
16
- });
17
- afterEach(async () => {
18
- // Restore original environment variable
19
- if (originalEnv !== undefined) {
20
- process.env.MEMORY_FILE_PATH = originalEnv;
21
- }
22
- else {
23
- delete process.env.MEMORY_FILE_PATH;
24
- }
25
- // Clean up test files
26
- try {
27
- await fs.unlink(oldMemoryPath);
28
- }
29
- catch {
30
- // Ignore if file doesn't exist
31
- }
32
- try {
33
- await fs.unlink(newMemoryPath);
34
- }
35
- catch {
36
- // Ignore if file doesn't exist
37
- }
38
- });
39
- describe('with MEMORY_FILE_PATH environment variable', () => {
40
- it('should return absolute path when MEMORY_FILE_PATH is absolute', async () => {
41
- const absolutePath = '/tmp/custom-memory.jsonl';
42
- process.env.MEMORY_FILE_PATH = absolutePath;
43
- const result = await ensureMemoryFilePath();
44
- expect(result).toBe(absolutePath);
45
- });
46
- it('should convert relative path to absolute when MEMORY_FILE_PATH is relative', async () => {
47
- const relativePath = 'custom-memory.jsonl';
48
- process.env.MEMORY_FILE_PATH = relativePath;
49
- const result = await ensureMemoryFilePath();
50
- expect(path.isAbsolute(result)).toBe(true);
51
- expect(result).toContain('custom-memory.jsonl');
52
- });
53
- it('should handle Windows absolute paths', async () => {
54
- const windowsPath = 'C:\\temp\\memory.jsonl';
55
- process.env.MEMORY_FILE_PATH = windowsPath;
56
- const result = await ensureMemoryFilePath();
57
- // On Windows, should return as-is; on Unix, will be treated as relative
58
- if (process.platform === 'win32') {
59
- expect(result).toBe(windowsPath);
60
- }
61
- else {
62
- expect(path.isAbsolute(result)).toBe(true);
63
- }
64
- });
65
- });
66
- describe('without MEMORY_FILE_PATH environment variable', () => {
67
- it('should return default path when no files exist', async () => {
68
- const result = await ensureMemoryFilePath();
69
- expect(result).toBe(defaultMemoryPath);
70
- });
71
- it('should migrate from memory.json to memory.jsonl when only old file exists', async () => {
72
- // Create old memory.json file
73
- await fs.writeFile(oldMemoryPath, '{"test":"data"}');
74
- const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
75
- const result = await ensureMemoryFilePath();
76
- expect(result).toBe(defaultMemoryPath);
77
- // Verify migration happened
78
- const newFileExists = await fs.access(newMemoryPath).then(() => true).catch(() => false);
79
- const oldFileExists = await fs.access(oldMemoryPath).then(() => true).catch(() => false);
80
- expect(newFileExists).toBe(true);
81
- expect(oldFileExists).toBe(false);
82
- // Verify console messages
83
- expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('DETECTED: Found legacy memory.json file'));
84
- expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('COMPLETED: Successfully migrated'));
85
- consoleErrorSpy.mockRestore();
86
- });
87
- it('should use new file when both old and new files exist', async () => {
88
- // Create both files
89
- await fs.writeFile(oldMemoryPath, '{"old":"data"}');
90
- await fs.writeFile(newMemoryPath, '{"new":"data"}');
91
- const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
92
- const result = await ensureMemoryFilePath();
93
- expect(result).toBe(defaultMemoryPath);
94
- // Verify no migration happened (both files should still exist)
95
- const newFileExists = await fs.access(newMemoryPath).then(() => true).catch(() => false);
96
- const oldFileExists = await fs.access(oldMemoryPath).then(() => true).catch(() => false);
97
- expect(newFileExists).toBe(true);
98
- expect(oldFileExists).toBe(true);
99
- // Verify no console messages about migration
100
- expect(consoleErrorSpy).not.toHaveBeenCalled();
101
- consoleErrorSpy.mockRestore();
102
- });
103
- it('should preserve file content during migration', async () => {
104
- const testContent = '{"entities": [{"name": "test", "type": "person"}]}';
105
- await fs.writeFile(oldMemoryPath, testContent);
106
- await ensureMemoryFilePath();
107
- const migratedContent = await fs.readFile(newMemoryPath, 'utf-8');
108
- expect(migratedContent).toBe(testContent);
109
- });
110
- });
111
- describe('defaultMemoryPath', () => {
112
- it('should end with memory.jsonl', () => {
113
- expect(defaultMemoryPath).toMatch(/memory\.jsonl$/);
114
- });
115
- it('should be an absolute path', () => {
116
- expect(path.isAbsolute(defaultMemoryPath)).toBe(true);
117
- });
118
- });
119
- });
@@ -1,318 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { promises as fs } from 'fs';
3
- import path from 'path';
4
- import { fileURLToPath } from 'url';
5
- import { KnowledgeGraphManager } from '../index.js';
6
- describe('KnowledgeGraphManager', () => {
7
- let manager;
8
- let testFilePath;
9
- beforeEach(async () => {
10
- // Create a temporary test file path
11
- testFilePath = path.join(path.dirname(fileURLToPath(import.meta.url)), `test-memory-${Date.now()}.jsonl`);
12
- manager = new KnowledgeGraphManager(testFilePath);
13
- });
14
- afterEach(async () => {
15
- // Clean up test file
16
- try {
17
- await fs.unlink(testFilePath);
18
- }
19
- catch (error) {
20
- // Ignore errors if file doesn't exist
21
- }
22
- });
23
- describe('createEntities', () => {
24
- it('should create new entities', async () => {
25
- const entities = [
26
- { name: 'Alice', entityType: 'person', observations: ['works at Acme Corp'] },
27
- { name: 'Bob', entityType: 'person', observations: ['likes programming'] },
28
- ];
29
- const newEntities = await manager.createEntities(entities);
30
- expect(newEntities).toHaveLength(2);
31
- expect(newEntities).toEqual(entities);
32
- const graph = await manager.readGraph();
33
- expect(graph.entities).toHaveLength(2);
34
- });
35
- it('should not create duplicate entities', async () => {
36
- const entities = [
37
- { name: 'Alice', entityType: 'person', observations: ['works at Acme Corp'] },
38
- ];
39
- await manager.createEntities(entities);
40
- const newEntities = await manager.createEntities(entities);
41
- expect(newEntities).toHaveLength(0);
42
- const graph = await manager.readGraph();
43
- expect(graph.entities).toHaveLength(1);
44
- });
45
- it('should handle empty entity arrays', async () => {
46
- const newEntities = await manager.createEntities([]);
47
- expect(newEntities).toHaveLength(0);
48
- });
49
- });
50
- describe('createRelations', () => {
51
- it('should create new relations', async () => {
52
- await manager.createEntities([
53
- { name: 'Alice', entityType: 'person', observations: [] },
54
- { name: 'Bob', entityType: 'person', observations: [] },
55
- ]);
56
- const relations = [
57
- { from: 'Alice', to: 'Bob', relationType: 'knows' },
58
- ];
59
- const newRelations = await manager.createRelations(relations);
60
- expect(newRelations).toHaveLength(1);
61
- expect(newRelations).toEqual(relations);
62
- const graph = await manager.readGraph();
63
- expect(graph.relations).toHaveLength(1);
64
- });
65
- it('should not create duplicate relations', async () => {
66
- await manager.createEntities([
67
- { name: 'Alice', entityType: 'person', observations: [] },
68
- { name: 'Bob', entityType: 'person', observations: [] },
69
- ]);
70
- const relations = [
71
- { from: 'Alice', to: 'Bob', relationType: 'knows' },
72
- ];
73
- await manager.createRelations(relations);
74
- const newRelations = await manager.createRelations(relations);
75
- expect(newRelations).toHaveLength(0);
76
- const graph = await manager.readGraph();
77
- expect(graph.relations).toHaveLength(1);
78
- });
79
- it('should handle empty relation arrays', async () => {
80
- const newRelations = await manager.createRelations([]);
81
- expect(newRelations).toHaveLength(0);
82
- });
83
- });
84
- describe('addObservations', () => {
85
- it('should add observations to existing entities', async () => {
86
- await manager.createEntities([
87
- { name: 'Alice', entityType: 'person', observations: ['works at Acme Corp'] },
88
- ]);
89
- const results = await manager.addObservations([
90
- { entityName: 'Alice', contents: ['likes coffee', 'has a dog'] },
91
- ]);
92
- expect(results).toHaveLength(1);
93
- expect(results[0].entityName).toBe('Alice');
94
- expect(results[0].addedObservations).toHaveLength(2);
95
- const graph = await manager.readGraph();
96
- const alice = graph.entities.find(e => e.name === 'Alice');
97
- expect(alice?.observations).toHaveLength(3);
98
- });
99
- it('should not add duplicate observations', async () => {
100
- await manager.createEntities([
101
- { name: 'Alice', entityType: 'person', observations: ['works at Acme Corp'] },
102
- ]);
103
- await manager.addObservations([
104
- { entityName: 'Alice', contents: ['likes coffee'] },
105
- ]);
106
- const results = await manager.addObservations([
107
- { entityName: 'Alice', contents: ['likes coffee', 'has a dog'] },
108
- ]);
109
- expect(results[0].addedObservations).toHaveLength(1);
110
- expect(results[0].addedObservations).toContain('has a dog');
111
- const graph = await manager.readGraph();
112
- const alice = graph.entities.find(e => e.name === 'Alice');
113
- expect(alice?.observations).toHaveLength(3);
114
- });
115
- it('should throw error for non-existent entity', async () => {
116
- await expect(manager.addObservations([
117
- { entityName: 'NonExistent', contents: ['some observation'] },
118
- ])).rejects.toThrow('Entity with name NonExistent not found');
119
- });
120
- });
121
- describe('deleteEntities', () => {
122
- it('should delete entities', async () => {
123
- await manager.createEntities([
124
- { name: 'Alice', entityType: 'person', observations: [] },
125
- { name: 'Bob', entityType: 'person', observations: [] },
126
- ]);
127
- await manager.deleteEntities(['Alice']);
128
- const graph = await manager.readGraph();
129
- expect(graph.entities).toHaveLength(1);
130
- expect(graph.entities[0].name).toBe('Bob');
131
- });
132
- it('should cascade delete relations when deleting entities', async () => {
133
- await manager.createEntities([
134
- { name: 'Alice', entityType: 'person', observations: [] },
135
- { name: 'Bob', entityType: 'person', observations: [] },
136
- { name: 'Charlie', entityType: 'person', observations: [] },
137
- ]);
138
- await manager.createRelations([
139
- { from: 'Alice', to: 'Bob', relationType: 'knows' },
140
- { from: 'Bob', to: 'Charlie', relationType: 'knows' },
141
- ]);
142
- await manager.deleteEntities(['Bob']);
143
- const graph = await manager.readGraph();
144
- expect(graph.entities).toHaveLength(2);
145
- expect(graph.relations).toHaveLength(0);
146
- });
147
- it('should handle deleting non-existent entities', async () => {
148
- await manager.deleteEntities(['NonExistent']);
149
- const graph = await manager.readGraph();
150
- expect(graph.entities).toHaveLength(0);
151
- });
152
- });
153
- describe('deleteObservations', () => {
154
- it('should delete observations from entities', async () => {
155
- await manager.createEntities([
156
- { name: 'Alice', entityType: 'person', observations: ['works at Acme Corp', 'likes coffee'] },
157
- ]);
158
- await manager.deleteObservations([
159
- { entityName: 'Alice', observations: ['likes coffee'] },
160
- ]);
161
- const graph = await manager.readGraph();
162
- const alice = graph.entities.find(e => e.name === 'Alice');
163
- expect(alice?.observations).toHaveLength(1);
164
- expect(alice?.observations).toContain('works at Acme Corp');
165
- });
166
- it('should handle deleting from non-existent entities', async () => {
167
- await manager.deleteObservations([
168
- { entityName: 'NonExistent', observations: ['some observation'] },
169
- ]);
170
- // Should not throw error
171
- const graph = await manager.readGraph();
172
- expect(graph.entities).toHaveLength(0);
173
- });
174
- });
175
- describe('deleteRelations', () => {
176
- it('should delete specific relations', async () => {
177
- await manager.createEntities([
178
- { name: 'Alice', entityType: 'person', observations: [] },
179
- { name: 'Bob', entityType: 'person', observations: [] },
180
- ]);
181
- await manager.createRelations([
182
- { from: 'Alice', to: 'Bob', relationType: 'knows' },
183
- { from: 'Alice', to: 'Bob', relationType: 'works_with' },
184
- ]);
185
- await manager.deleteRelations([
186
- { from: 'Alice', to: 'Bob', relationType: 'knows' },
187
- ]);
188
- const graph = await manager.readGraph();
189
- expect(graph.relations).toHaveLength(1);
190
- expect(graph.relations[0].relationType).toBe('works_with');
191
- });
192
- });
193
- describe('readGraph', () => {
194
- it('should return empty graph when file does not exist', async () => {
195
- const graph = await manager.readGraph();
196
- expect(graph.entities).toHaveLength(0);
197
- expect(graph.relations).toHaveLength(0);
198
- });
199
- it('should return complete graph with entities and relations', async () => {
200
- await manager.createEntities([
201
- { name: 'Alice', entityType: 'person', observations: ['works at Acme Corp'] },
202
- ]);
203
- await manager.createRelations([
204
- { from: 'Alice', to: 'Alice', relationType: 'self' },
205
- ]);
206
- const graph = await manager.readGraph();
207
- expect(graph.entities).toHaveLength(1);
208
- expect(graph.relations).toHaveLength(1);
209
- });
210
- });
211
- describe('searchNodes', () => {
212
- beforeEach(async () => {
213
- await manager.createEntities([
214
- { name: 'Alice', entityType: 'person', observations: ['works at Acme Corp', 'likes programming'] },
215
- { name: 'Bob', entityType: 'person', observations: ['works at TechCo'] },
216
- { name: 'Acme Corp', entityType: 'company', observations: ['tech company'] },
217
- ]);
218
- await manager.createRelations([
219
- { from: 'Alice', to: 'Acme Corp', relationType: 'works_at' },
220
- { from: 'Bob', to: 'Acme Corp', relationType: 'competitor' },
221
- ]);
222
- });
223
- it('should search by entity name', async () => {
224
- const result = await manager.searchNodes('Alice');
225
- expect(result.entities).toHaveLength(1);
226
- expect(result.entities[0].name).toBe('Alice');
227
- });
228
- it('should search by entity type', async () => {
229
- const result = await manager.searchNodes('company');
230
- expect(result.entities).toHaveLength(1);
231
- expect(result.entities[0].name).toBe('Acme Corp');
232
- });
233
- it('should search by observation content', async () => {
234
- const result = await manager.searchNodes('programming');
235
- expect(result.entities).toHaveLength(1);
236
- expect(result.entities[0].name).toBe('Alice');
237
- });
238
- it('should be case insensitive', async () => {
239
- const result = await manager.searchNodes('ALICE');
240
- expect(result.entities).toHaveLength(1);
241
- expect(result.entities[0].name).toBe('Alice');
242
- });
243
- it('should include relations between matched entities', async () => {
244
- const result = await manager.searchNodes('Acme');
245
- expect(result.entities).toHaveLength(2); // Alice and Acme Corp
246
- expect(result.relations).toHaveLength(1); // Only Alice -> Acme Corp relation
247
- });
248
- it('should return empty graph for no matches', async () => {
249
- const result = await manager.searchNodes('NonExistent');
250
- expect(result.entities).toHaveLength(0);
251
- expect(result.relations).toHaveLength(0);
252
- });
253
- });
254
- describe('openNodes', () => {
255
- beforeEach(async () => {
256
- await manager.createEntities([
257
- { name: 'Alice', entityType: 'person', observations: [] },
258
- { name: 'Bob', entityType: 'person', observations: [] },
259
- { name: 'Charlie', entityType: 'person', observations: [] },
260
- ]);
261
- await manager.createRelations([
262
- { from: 'Alice', to: 'Bob', relationType: 'knows' },
263
- { from: 'Bob', to: 'Charlie', relationType: 'knows' },
264
- ]);
265
- });
266
- it('should open specific nodes by name', async () => {
267
- const result = await manager.openNodes(['Alice', 'Bob']);
268
- expect(result.entities).toHaveLength(2);
269
- expect(result.entities.map(e => e.name)).toContain('Alice');
270
- expect(result.entities.map(e => e.name)).toContain('Bob');
271
- });
272
- it('should include relations between opened nodes', async () => {
273
- const result = await manager.openNodes(['Alice', 'Bob']);
274
- expect(result.relations).toHaveLength(1);
275
- expect(result.relations[0].from).toBe('Alice');
276
- expect(result.relations[0].to).toBe('Bob');
277
- });
278
- it('should exclude relations to unopened nodes', async () => {
279
- const result = await manager.openNodes(['Bob']);
280
- expect(result.relations).toHaveLength(0);
281
- });
282
- it('should handle opening non-existent nodes', async () => {
283
- const result = await manager.openNodes(['NonExistent']);
284
- expect(result.entities).toHaveLength(0);
285
- });
286
- it('should handle empty node list', async () => {
287
- const result = await manager.openNodes([]);
288
- expect(result.entities).toHaveLength(0);
289
- expect(result.relations).toHaveLength(0);
290
- });
291
- });
292
- describe('file persistence', () => {
293
- it('should persist data across manager instances', async () => {
294
- await manager.createEntities([
295
- { name: 'Alice', entityType: 'person', observations: ['persistent data'] },
296
- ]);
297
- // Create new manager instance with same file path
298
- const manager2 = new KnowledgeGraphManager(testFilePath);
299
- const graph = await manager2.readGraph();
300
- expect(graph.entities).toHaveLength(1);
301
- expect(graph.entities[0].name).toBe('Alice');
302
- });
303
- it('should handle JSONL format correctly', async () => {
304
- await manager.createEntities([
305
- { name: 'Alice', entityType: 'person', observations: [] },
306
- ]);
307
- await manager.createRelations([
308
- { from: 'Alice', to: 'Alice', relationType: 'self' },
309
- ]);
310
- // Read file directly
311
- const fileContent = await fs.readFile(testFilePath, 'utf-8');
312
- const lines = fileContent.split('\n').filter(line => line.trim());
313
- expect(lines).toHaveLength(2);
314
- expect(JSON.parse(lines[0])).toHaveProperty('type', 'entity');
315
- expect(JSON.parse(lines[1])).toHaveProperty('type', 'relation');
316
- });
317
- });
318
- });
@@ -1,202 +0,0 @@
1
- /**
2
- * Graph Event Emitter
3
- *
4
- * Phase 10 Sprint 2: Provides event-based notifications for graph changes.
5
- * Enables loose coupling between graph operations and dependent systems
6
- * like search indexes, analytics, and external integrations.
7
- *
8
- * @module core/GraphEventEmitter
9
- */
10
- import type { GraphEventType, GraphEvent, GraphEventListener, GraphEventMap, Entity, Relation } from '../types/index.js';
11
- /**
12
- * Phase 10 Sprint 2: Event emitter for graph change notifications.
13
- *
14
- * Provides a type-safe event system for subscribing to and emitting
15
- * graph change events. Supports wildcard listeners for all events.
16
- *
17
- * @example
18
- * ```typescript
19
- * const emitter = new GraphEventEmitter();
20
- *
21
- * // Listen to specific event types
22
- * emitter.on('entity:created', (event) => {
23
- * console.log(`Entity ${event.entity.name} created`);
24
- * });
25
- *
26
- * // Listen to all events
27
- * emitter.onAny((event) => {
28
- * console.log(`Event: ${event.type}`);
29
- * });
30
- *
31
- * // Emit an event
32
- * emitter.emitEntityCreated(entity);
33
- *
34
- * // Remove listener
35
- * const unsubscribe = emitter.on('entity:deleted', handler);
36
- * unsubscribe();
37
- * ```
38
- */
39
- export declare class GraphEventEmitter {
40
- /**
41
- * Map of event types to their registered listeners.
42
- */
43
- private listeners;
44
- /**
45
- * Listeners that receive all events regardless of type.
46
- */
47
- private wildcardListeners;
48
- /**
49
- * Whether to suppress errors from listeners (default: true).
50
- * When true, listener errors are logged but don't stop event propagation.
51
- */
52
- private suppressListenerErrors;
53
- /**
54
- * Create a new GraphEventEmitter instance.
55
- *
56
- * @param options - Optional configuration
57
- */
58
- constructor(options?: {
59
- suppressListenerErrors?: boolean;
60
- });
61
- /**
62
- * Subscribe to a specific event type.
63
- *
64
- * @template K - The event type key
65
- * @param eventType - The event type to listen for
66
- * @param listener - Callback function to invoke when event occurs
67
- * @returns Unsubscribe function to remove the listener
68
- *
69
- * @example
70
- * ```typescript
71
- * const unsubscribe = emitter.on('entity:created', (event) => {
72
- * console.log(`Created: ${event.entity.name}`);
73
- * });
74
- *
75
- * // Later: unsubscribe();
76
- * ```
77
- */
78
- on<K extends GraphEventType>(eventType: K, listener: GraphEventListener<GraphEventMap[K]>): () => void;
79
- /**
80
- * Unsubscribe from a specific event type.
81
- *
82
- * @template K - The event type key
83
- * @param eventType - The event type to unsubscribe from
84
- * @param listener - The listener function to remove
85
- */
86
- off<K extends GraphEventType>(eventType: K, listener: GraphEventListener<GraphEventMap[K]>): void;
87
- /**
88
- * Subscribe to all event types.
89
- *
90
- * @param listener - Callback function to invoke for any event
91
- * @returns Unsubscribe function to remove the listener
92
- *
93
- * @example
94
- * ```typescript
95
- * emitter.onAny((event) => {
96
- * console.log(`Event: ${event.type} at ${event.timestamp}`);
97
- * });
98
- * ```
99
- */
100
- onAny(listener: GraphEventListener<GraphEvent>): () => void;
101
- /**
102
- * Unsubscribe from all events.
103
- *
104
- * @param listener - The listener function to remove
105
- */
106
- offAny(listener: GraphEventListener<GraphEvent>): void;
107
- /**
108
- * Subscribe to an event type, but only receive the first occurrence.
109
- *
110
- * @template K - The event type key
111
- * @param eventType - The event type to listen for once
112
- * @param listener - Callback function to invoke once
113
- * @returns Unsubscribe function to cancel before event occurs
114
- */
115
- once<K extends GraphEventType>(eventType: K, listener: GraphEventListener<GraphEventMap[K]>): () => void;
116
- /**
117
- * Remove all listeners for all event types.
118
- */
119
- removeAllListeners(): void;
120
- /**
121
- * Get the count of listeners for a specific event type.
122
- *
123
- * @param eventType - The event type to count listeners for
124
- * @returns Number of listeners registered
125
- */
126
- listenerCount(eventType?: GraphEventType): number;
127
- /**
128
- * Emit an event to all registered listeners.
129
- *
130
- * @param event - The event to emit
131
- */
132
- emit(event: GraphEvent): void;
133
- /**
134
- * Emit an entity:created event.
135
- *
136
- * @param entity - The entity that was created
137
- */
138
- emitEntityCreated(entity: Entity): void;
139
- /**
140
- * Emit an entity:updated event.
141
- *
142
- * @param entityName - Name of the updated entity
143
- * @param changes - The changes that were applied
144
- * @param previousValues - Optional previous values before update
145
- */
146
- emitEntityUpdated(entityName: string, changes: Partial<Entity>, previousValues?: Partial<Entity>): void;
147
- /**
148
- * Emit an entity:deleted event.
149
- *
150
- * @param entityName - Name of the deleted entity
151
- * @param entity - Optional entity data before deletion
152
- */
153
- emitEntityDeleted(entityName: string, entity?: Entity): void;
154
- /**
155
- * Emit a relation:created event.
156
- *
157
- * @param relation - The relation that was created
158
- */
159
- emitRelationCreated(relation: Relation): void;
160
- /**
161
- * Emit a relation:deleted event.
162
- *
163
- * @param from - Source entity name
164
- * @param to - Target entity name
165
- * @param relationType - Type of the deleted relation
166
- */
167
- emitRelationDeleted(from: string, to: string, relationType: string): void;
168
- /**
169
- * Emit an observation:added event.
170
- *
171
- * @param entityName - Name of the entity
172
- * @param observations - Observations that were added
173
- */
174
- emitObservationAdded(entityName: string, observations: string[]): void;
175
- /**
176
- * Emit an observation:deleted event.
177
- *
178
- * @param entityName - Name of the entity
179
- * @param observations - Observations that were deleted
180
- */
181
- emitObservationDeleted(entityName: string, observations: string[]): void;
182
- /**
183
- * Emit a graph:saved event.
184
- *
185
- * @param entityCount - Number of entities in the saved graph
186
- * @param relationCount - Number of relations in the saved graph
187
- */
188
- emitGraphSaved(entityCount: number, relationCount: number): void;
189
- /**
190
- * Emit a graph:loaded event.
191
- *
192
- * @param entityCount - Number of entities in the loaded graph
193
- * @param relationCount - Number of relations in the loaded graph
194
- */
195
- emitGraphLoaded(entityCount: number, relationCount: number): void;
196
- /**
197
- * Safely invoke a listener, optionally catching errors.
198
- * @private
199
- */
200
- private invokeListener;
201
- }
202
- //# sourceMappingURL=GraphEventEmitter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"GraphEventEmitter.d.ts","sourceRoot":"","sources":["../../src/core/GraphEventEmitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,MAAM,EACN,QAAQ,EAUT,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,iBAAiB;IAC5B;;OAEG;IACH,OAAO,CAAC,SAAS,CAAgE;IAEjF;;OAEG;IACH,OAAO,CAAC,iBAAiB,CAAkD;IAE3E;;;OAGG;IACH,OAAO,CAAC,sBAAsB,CAAiB;IAE/C;;;;OAIG;gBACS,OAAO,CAAC,EAAE;QAAE,sBAAsB,CAAC,EAAE,OAAO,CAAA;KAAE;IAQ1D;;;;;;;;;;;;;;;;OAgBG;IACH,EAAE,CAAC,CAAC,SAAS,cAAc,EACzB,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,kBAAkB,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAC7C,MAAM,IAAI;IAYb;;;;;;OAMG;IACH,GAAG,CAAC,CAAC,SAAS,cAAc,EAC1B,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,kBAAkB,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAC7C,IAAI;IAOP;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,QAAQ,EAAE,kBAAkB,CAAC,UAAU,CAAC,GAAG,MAAM,IAAI;IAO3D;;;;OAIG;IACH,MAAM,CAAC,QAAQ,EAAE,kBAAkB,CAAC,UAAU,CAAC,GAAG,IAAI;IAItD;;;;;;;OAOG;IACH,IAAI,CAAC,CAAC,SAAS,cAAc,EAC3B,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,kBAAkB,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAC7C,MAAM,IAAI;IASb;;OAEG;IACH,kBAAkB,IAAI,IAAI;IAK1B;;;;;OAKG;IACH,aAAa,CAAC,SAAS,CAAC,EAAE,cAAc,GAAG,MAAM;IAcjD;;;;OAIG;IACH,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAe7B;;;;OAIG;IACH,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IASvC;;;;;;OAMG;IACH,iBAAiB,CACf,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,EACxB,cAAc,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,GAC/B,IAAI;IAWP;;;;;OAKG;IACH,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAU5D;;;;OAIG;IACH,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAS7C;;;;;;OAMG;IACH,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAWzE;;;;;OAKG;IACH,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI;IAYtE;;;;;OAKG;IACH,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI;IAYxE;;;;;OAKG;IACH,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI;IAUhE;;;;;OAKG;IACH,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI;IAYjE;;;OAGG;IACH,OAAO,CAAC,cAAc;CAYvB"}