@soulcraft/brainy 0.40.0 → 0.43.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 (152) hide show
  1. package/README.md +605 -194
  2. package/dist/augmentations/conduitAugmentations.js +1158 -0
  3. package/dist/augmentations/conduitAugmentations.js.map +1 -0
  4. package/dist/augmentations/memoryAugmentations.d.ts +2 -0
  5. package/dist/augmentations/memoryAugmentations.d.ts.map +1 -1
  6. package/dist/augmentations/memoryAugmentations.js +270 -0
  7. package/dist/augmentations/memoryAugmentations.js.map +1 -0
  8. package/dist/augmentations/serverSearchAugmentations.js +531 -0
  9. package/dist/augmentations/serverSearchAugmentations.js.map +1 -0
  10. package/dist/browserFramework.d.ts +15 -0
  11. package/dist/demo.d.ts +106 -0
  12. package/dist/examples/basicUsage.js +118 -0
  13. package/dist/examples/basicUsage.js.map +1 -0
  14. package/dist/hnsw/distributedSearch.js +452 -0
  15. package/dist/hnsw/distributedSearch.js.map +1 -0
  16. package/dist/hnsw/hnswIndex.js +602 -0
  17. package/dist/hnsw/hnswIndex.js.map +1 -0
  18. package/dist/hnsw/hnswIndexOptimized.js +471 -0
  19. package/dist/hnsw/hnswIndexOptimized.js.map +1 -0
  20. package/dist/hnsw/optimizedHNSWIndex.js +313 -0
  21. package/dist/hnsw/optimizedHNSWIndex.js.map +1 -0
  22. package/dist/hnsw/partitionedHNSWIndex.js +304 -0
  23. package/dist/hnsw/partitionedHNSWIndex.js.map +1 -0
  24. package/dist/hnsw/scaledHNSWSystem.js +559 -0
  25. package/dist/hnsw/scaledHNSWSystem.js.map +1 -0
  26. package/dist/index.d.ts +5 -3
  27. package/dist/index.js +81 -0
  28. package/dist/mcp/brainyMCPAdapter.js +142 -0
  29. package/dist/mcp/brainyMCPAdapter.js.map +1 -0
  30. package/dist/mcp/brainyMCPService.js +248 -0
  31. package/dist/mcp/brainyMCPService.js.map +1 -0
  32. package/dist/mcp/index.js +17 -0
  33. package/dist/mcp/index.js.map +1 -0
  34. package/dist/mcp/mcpAugmentationToolset.js +180 -0
  35. package/dist/mcp/mcpAugmentationToolset.js.map +1 -0
  36. package/dist/storage/adapters/baseStorageAdapter.js +349 -0
  37. package/dist/storage/adapters/baseStorageAdapter.js.map +1 -0
  38. package/dist/storage/adapters/batchS3Operations.js +287 -0
  39. package/dist/storage/adapters/batchS3Operations.js.map +1 -0
  40. package/dist/storage/adapters/fileSystemStorage.js +846 -0
  41. package/dist/storage/adapters/fileSystemStorage.js.map +1 -0
  42. package/dist/storage/adapters/memoryStorage.js +532 -0
  43. package/dist/storage/adapters/memoryStorage.js.map +1 -0
  44. package/dist/storage/adapters/opfsStorage.d.ts.map +1 -1
  45. package/dist/storage/adapters/opfsStorage.js +1118 -0
  46. package/dist/storage/adapters/opfsStorage.js.map +1 -0
  47. package/dist/storage/adapters/optimizedS3Search.d.ts +79 -0
  48. package/dist/storage/adapters/optimizedS3Search.d.ts.map +1 -0
  49. package/dist/storage/adapters/optimizedS3Search.js +248 -0
  50. package/dist/storage/adapters/optimizedS3Search.js.map +1 -0
  51. package/dist/storage/adapters/s3CompatibleStorage.d.ts +21 -0
  52. package/dist/storage/adapters/s3CompatibleStorage.d.ts.map +1 -1
  53. package/dist/storage/adapters/s3CompatibleStorage.js +2026 -0
  54. package/dist/storage/adapters/s3CompatibleStorage.js.map +1 -0
  55. package/dist/storage/baseStorage.d.ts +1 -0
  56. package/dist/storage/baseStorage.d.ts.map +1 -1
  57. package/dist/storage/baseStorage.js +603 -0
  58. package/dist/storage/baseStorage.js.map +1 -0
  59. package/dist/storage/cacheManager.js +1306 -0
  60. package/dist/storage/cacheManager.js.map +1 -0
  61. package/dist/storage/enhancedCacheManager.js +520 -0
  62. package/dist/storage/enhancedCacheManager.js.map +1 -0
  63. package/dist/storage/readOnlyOptimizations.js +425 -0
  64. package/dist/storage/readOnlyOptimizations.js.map +1 -0
  65. package/dist/storage/storageFactory.d.ts +0 -1
  66. package/dist/storage/storageFactory.d.ts.map +1 -1
  67. package/dist/storage/storageFactory.js +227 -0
  68. package/dist/storage/storageFactory.js.map +1 -0
  69. package/dist/types/augmentations.js +16 -0
  70. package/dist/types/augmentations.js.map +1 -0
  71. package/dist/types/brainyDataInterface.js +8 -0
  72. package/dist/types/brainyDataInterface.js.map +1 -0
  73. package/dist/types/distributedTypes.js +6 -0
  74. package/dist/types/distributedTypes.js.map +1 -0
  75. package/dist/types/fileSystemTypes.js +8 -0
  76. package/dist/types/fileSystemTypes.js.map +1 -0
  77. package/dist/types/graphTypes.js +247 -0
  78. package/dist/types/graphTypes.js.map +1 -0
  79. package/dist/types/mcpTypes.js +22 -0
  80. package/dist/types/mcpTypes.js.map +1 -0
  81. package/dist/types/paginationTypes.js +5 -0
  82. package/dist/types/paginationTypes.js.map +1 -0
  83. package/dist/types/pipelineTypes.js +7 -0
  84. package/dist/types/pipelineTypes.js.map +1 -0
  85. package/dist/types/tensorflowTypes.js +6 -0
  86. package/dist/types/tensorflowTypes.js.map +1 -0
  87. package/dist/unified.js +52 -128048
  88. package/dist/utils/autoConfiguration.js +341 -0
  89. package/dist/utils/autoConfiguration.js.map +1 -0
  90. package/dist/utils/cacheAutoConfig.js +261 -0
  91. package/dist/utils/cacheAutoConfig.js.map +1 -0
  92. package/dist/utils/crypto.js +45 -0
  93. package/dist/utils/crypto.js.map +1 -0
  94. package/dist/utils/distance.js +239 -0
  95. package/dist/utils/distance.js.map +1 -0
  96. package/dist/utils/embedding.d.ts.map +1 -1
  97. package/dist/utils/embedding.js +702 -0
  98. package/dist/utils/embedding.js.map +1 -0
  99. package/dist/utils/environment.js +75 -0
  100. package/dist/utils/environment.js.map +1 -0
  101. package/dist/utils/fieldNameTracking.js +90 -0
  102. package/dist/utils/fieldNameTracking.js.map +1 -0
  103. package/dist/utils/index.d.ts +1 -0
  104. package/dist/utils/index.d.ts.map +1 -1
  105. package/dist/utils/index.js +8 -0
  106. package/dist/utils/index.js.map +1 -0
  107. package/dist/utils/jsonProcessing.js +179 -0
  108. package/dist/utils/jsonProcessing.js.map +1 -0
  109. package/dist/utils/logger.d.ts +45 -92
  110. package/dist/utils/logger.d.ts.map +1 -1
  111. package/dist/utils/logger.js +129 -0
  112. package/dist/utils/logger.js.map +1 -0
  113. package/dist/utils/operationUtils.js +126 -0
  114. package/dist/utils/operationUtils.js.map +1 -0
  115. package/dist/utils/robustModelLoader.d.ts +14 -0
  116. package/dist/utils/robustModelLoader.d.ts.map +1 -1
  117. package/dist/utils/robustModelLoader.js +537 -0
  118. package/dist/utils/robustModelLoader.js.map +1 -0
  119. package/dist/utils/searchCache.js +248 -0
  120. package/dist/utils/searchCache.js.map +1 -0
  121. package/dist/utils/statistics.js +25 -0
  122. package/dist/utils/statistics.js.map +1 -0
  123. package/dist/utils/statisticsCollector.js +224 -0
  124. package/dist/utils/statisticsCollector.js.map +1 -0
  125. package/dist/utils/textEncoding.js +309 -0
  126. package/dist/utils/textEncoding.js.map +1 -0
  127. package/dist/utils/typeUtils.js +40 -0
  128. package/dist/utils/typeUtils.js.map +1 -0
  129. package/dist/utils/version.d.ts +15 -3
  130. package/dist/utils/version.d.ts.map +1 -1
  131. package/dist/utils/version.js +24 -0
  132. package/dist/utils/version.js.map +1 -0
  133. package/dist/utils/workerUtils.js +458 -0
  134. package/dist/utils/workerUtils.js.map +1 -0
  135. package/package.json +23 -15
  136. package/dist/brainy.js +0 -90220
  137. package/dist/brainy.min.js +0 -12511
  138. package/dist/patched-platform-node.d.ts +0 -17
  139. package/dist/statistics/statisticsManager.d.ts +0 -121
  140. package/dist/storage/fileSystemStorage.d.ts +0 -73
  141. package/dist/storage/fileSystemStorage.d.ts.map +0 -1
  142. package/dist/storage/opfsStorage.d.ts +0 -236
  143. package/dist/storage/opfsStorage.d.ts.map +0 -1
  144. package/dist/storage/s3CompatibleStorage.d.ts +0 -157
  145. package/dist/storage/s3CompatibleStorage.d.ts.map +0 -1
  146. package/dist/testing/prettyReporter.d.ts +0 -23
  147. package/dist/testing/prettySummaryReporter.d.ts +0 -22
  148. package/dist/unified.min.js +0 -16153
  149. package/dist/utils/environmentDetection.d.ts +0 -47
  150. package/dist/utils/environmentDetection.d.ts.map +0 -1
  151. package/dist/utils/tensorflowUtils.d.ts +0 -17
  152. package/dist/utils/tensorflowUtils.d.ts.map +0 -1
@@ -0,0 +1,846 @@
1
+ /**
2
+ * File System Storage Adapter
3
+ * File system storage adapter for Node.js environments
4
+ */
5
+ import { BaseStorage, NOUNS_DIR, VERBS_DIR, METADATA_DIR, NOUN_METADATA_DIR, VERB_METADATA_DIR, INDEX_DIR, STATISTICS_KEY } from '../baseStorage.js';
6
+ // Node.js modules - dynamically imported to avoid issues in browser environments
7
+ let fs;
8
+ let path;
9
+ let moduleLoadingPromise = null;
10
+ // Try to load Node.js modules
11
+ try {
12
+ // Using dynamic imports to avoid issues in browser environments
13
+ const fsPromise = import('fs');
14
+ const pathPromise = import('path');
15
+ moduleLoadingPromise = Promise.all([fsPromise, pathPromise])
16
+ .then(([fsModule, pathModule]) => {
17
+ fs = fsModule;
18
+ path = pathModule.default;
19
+ })
20
+ .catch((error) => {
21
+ console.error('Failed to load Node.js modules:', error);
22
+ throw error;
23
+ });
24
+ }
25
+ catch (error) {
26
+ console.error('FileSystemStorage: Failed to load Node.js modules. This adapter is not supported in this environment.', error);
27
+ }
28
+ /**
29
+ * File system storage adapter for Node.js environments
30
+ * Uses the file system to store data in the specified directory structure
31
+ */
32
+ export class FileSystemStorage extends BaseStorage {
33
+ /**
34
+ * Initialize the storage adapter
35
+ * @param rootDirectory The root directory for storage
36
+ */
37
+ constructor(rootDirectory) {
38
+ super();
39
+ this.activeLocks = new Set();
40
+ this.rootDir = rootDirectory;
41
+ // Defer path operations until init() when path module is guaranteed to be loaded
42
+ }
43
+ /**
44
+ * Initialize the storage adapter
45
+ */
46
+ async init() {
47
+ if (this.isInitialized) {
48
+ return;
49
+ }
50
+ // Wait for module loading to complete
51
+ if (moduleLoadingPromise) {
52
+ try {
53
+ await moduleLoadingPromise;
54
+ }
55
+ catch (error) {
56
+ throw new Error('FileSystemStorage requires a Node.js environment, but `fs` and `path` modules could not be loaded.');
57
+ }
58
+ }
59
+ // Check if Node.js modules are available
60
+ if (!fs || !path) {
61
+ throw new Error('FileSystemStorage requires a Node.js environment, but `fs` and `path` modules could not be loaded.');
62
+ }
63
+ try {
64
+ // Initialize directory paths now that path module is loaded
65
+ this.nounsDir = path.join(this.rootDir, NOUNS_DIR);
66
+ this.verbsDir = path.join(this.rootDir, VERBS_DIR);
67
+ this.metadataDir = path.join(this.rootDir, METADATA_DIR);
68
+ this.nounMetadataDir = path.join(this.rootDir, NOUN_METADATA_DIR);
69
+ this.verbMetadataDir = path.join(this.rootDir, VERB_METADATA_DIR);
70
+ this.indexDir = path.join(this.rootDir, INDEX_DIR);
71
+ this.lockDir = path.join(this.rootDir, 'locks');
72
+ // Create the root directory if it doesn't exist
73
+ await this.ensureDirectoryExists(this.rootDir);
74
+ // Create the nouns directory if it doesn't exist
75
+ await this.ensureDirectoryExists(this.nounsDir);
76
+ // Create the verbs directory if it doesn't exist
77
+ await this.ensureDirectoryExists(this.verbsDir);
78
+ // Create the metadata directory if it doesn't exist
79
+ await this.ensureDirectoryExists(this.metadataDir);
80
+ // Create the noun metadata directory if it doesn't exist
81
+ await this.ensureDirectoryExists(this.nounMetadataDir);
82
+ // Create the verb metadata directory if it doesn't exist
83
+ await this.ensureDirectoryExists(this.verbMetadataDir);
84
+ // Create the index directory if it doesn't exist
85
+ await this.ensureDirectoryExists(this.indexDir);
86
+ // Create the locks directory if it doesn't exist
87
+ await this.ensureDirectoryExists(this.lockDir);
88
+ this.isInitialized = true;
89
+ }
90
+ catch (error) {
91
+ console.error('Error initializing FileSystemStorage:', error);
92
+ throw error;
93
+ }
94
+ }
95
+ /**
96
+ * Ensure a directory exists, creating it if necessary
97
+ */
98
+ async ensureDirectoryExists(dirPath) {
99
+ try {
100
+ await fs.promises.mkdir(dirPath, { recursive: true });
101
+ }
102
+ catch (error) {
103
+ // Ignore EEXIST error, which means the directory already exists
104
+ if (error.code !== 'EEXIST') {
105
+ throw error;
106
+ }
107
+ }
108
+ }
109
+ /**
110
+ * Save a node to storage
111
+ */
112
+ async saveNode(node) {
113
+ await this.ensureInitialized();
114
+ // Convert connections Map to a serializable format
115
+ const serializableNode = {
116
+ ...node,
117
+ connections: this.mapToObject(node.connections, (set) => Array.from(set))
118
+ };
119
+ const filePath = path.join(this.nounsDir, `${node.id}.json`);
120
+ await fs.promises.writeFile(filePath, JSON.stringify(serializableNode, null, 2));
121
+ }
122
+ /**
123
+ * Get a node from storage
124
+ */
125
+ async getNode(id) {
126
+ await this.ensureInitialized();
127
+ const filePath = path.join(this.nounsDir, `${id}.json`);
128
+ try {
129
+ const data = await fs.promises.readFile(filePath, 'utf-8');
130
+ const parsedNode = JSON.parse(data);
131
+ // Convert serialized connections back to Map<number, Set<string>>
132
+ const connections = new Map();
133
+ for (const [level, nodeIds] of Object.entries(parsedNode.connections)) {
134
+ connections.set(Number(level), new Set(nodeIds));
135
+ }
136
+ return {
137
+ id: parsedNode.id,
138
+ vector: parsedNode.vector,
139
+ connections,
140
+ level: parsedNode.level || 0
141
+ };
142
+ }
143
+ catch (error) {
144
+ if (error.code !== 'ENOENT') {
145
+ console.error(`Error reading node ${id}:`, error);
146
+ }
147
+ return null;
148
+ }
149
+ }
150
+ /**
151
+ * Get all nodes from storage
152
+ */
153
+ async getAllNodes() {
154
+ await this.ensureInitialized();
155
+ const allNodes = [];
156
+ try {
157
+ const files = await fs.promises.readdir(this.nounsDir);
158
+ for (const file of files) {
159
+ if (file.endsWith('.json')) {
160
+ const filePath = path.join(this.nounsDir, file);
161
+ const data = await fs.promises.readFile(filePath, 'utf-8');
162
+ const parsedNode = JSON.parse(data);
163
+ // Convert serialized connections back to Map<number, Set<string>>
164
+ const connections = new Map();
165
+ for (const [level, nodeIds] of Object.entries(parsedNode.connections)) {
166
+ connections.set(Number(level), new Set(nodeIds));
167
+ }
168
+ allNodes.push({
169
+ id: parsedNode.id,
170
+ vector: parsedNode.vector,
171
+ connections,
172
+ level: parsedNode.level || 0
173
+ });
174
+ }
175
+ }
176
+ }
177
+ catch (error) {
178
+ if (error.code !== 'ENOENT') {
179
+ console.error(`Error reading directory ${this.nounsDir}:`, error);
180
+ }
181
+ }
182
+ return allNodes;
183
+ }
184
+ /**
185
+ * Get nodes by noun type
186
+ * @param nounType The noun type to filter by
187
+ * @returns Promise that resolves to an array of nodes of the specified noun type
188
+ */
189
+ async getNodesByNounType(nounType) {
190
+ await this.ensureInitialized();
191
+ const nouns = [];
192
+ try {
193
+ const files = await fs.promises.readdir(this.nounsDir);
194
+ for (const file of files) {
195
+ if (file.endsWith('.json')) {
196
+ const filePath = path.join(this.nounsDir, file);
197
+ const data = await fs.promises.readFile(filePath, 'utf-8');
198
+ const parsedNode = JSON.parse(data);
199
+ // Filter by noun type using metadata
200
+ const nodeId = parsedNode.id;
201
+ const metadata = await this.getMetadata(nodeId);
202
+ if (metadata && metadata.noun === nounType) {
203
+ // Convert serialized connections back to Map<number, Set<string>>
204
+ const connections = new Map();
205
+ for (const [level, nodeIds] of Object.entries(parsedNode.connections)) {
206
+ connections.set(Number(level), new Set(nodeIds));
207
+ }
208
+ nouns.push({
209
+ id: parsedNode.id,
210
+ vector: parsedNode.vector,
211
+ connections,
212
+ level: parsedNode.level || 0
213
+ });
214
+ }
215
+ }
216
+ }
217
+ }
218
+ catch (error) {
219
+ if (error.code !== 'ENOENT') {
220
+ console.error(`Error reading directory ${this.nounsDir}:`, error);
221
+ }
222
+ }
223
+ return nouns;
224
+ }
225
+ /**
226
+ * Delete a node from storage
227
+ */
228
+ async deleteNode(id) {
229
+ await this.ensureInitialized();
230
+ const filePath = path.join(this.nounsDir, `${id}.json`);
231
+ try {
232
+ await fs.promises.unlink(filePath);
233
+ }
234
+ catch (error) {
235
+ if (error.code !== 'ENOENT') {
236
+ console.error(`Error deleting node file ${filePath}:`, error);
237
+ throw error;
238
+ }
239
+ }
240
+ }
241
+ /**
242
+ * Save an edge to storage
243
+ */
244
+ async saveEdge(edge) {
245
+ await this.ensureInitialized();
246
+ // Convert connections Map to a serializable format
247
+ const serializableEdge = {
248
+ ...edge,
249
+ connections: this.mapToObject(edge.connections, (set) => Array.from(set))
250
+ };
251
+ const filePath = path.join(this.verbsDir, `${edge.id}.json`);
252
+ await fs.promises.writeFile(filePath, JSON.stringify(serializableEdge, null, 2));
253
+ }
254
+ /**
255
+ * Get an edge from storage
256
+ */
257
+ async getEdge(id) {
258
+ await this.ensureInitialized();
259
+ const filePath = path.join(this.verbsDir, `${id}.json`);
260
+ try {
261
+ const data = await fs.promises.readFile(filePath, 'utf-8');
262
+ const parsedEdge = JSON.parse(data);
263
+ // Convert serialized connections back to Map<number, Set<string>>
264
+ const connections = new Map();
265
+ for (const [level, nodeIds] of Object.entries(parsedEdge.connections)) {
266
+ connections.set(Number(level), new Set(nodeIds));
267
+ }
268
+ return {
269
+ id: parsedEdge.id,
270
+ vector: parsedEdge.vector,
271
+ connections
272
+ };
273
+ }
274
+ catch (error) {
275
+ if (error.code !== 'ENOENT') {
276
+ console.error(`Error reading edge ${id}:`, error);
277
+ }
278
+ return null;
279
+ }
280
+ }
281
+ /**
282
+ * Get all edges from storage
283
+ */
284
+ async getAllEdges() {
285
+ await this.ensureInitialized();
286
+ const allEdges = [];
287
+ try {
288
+ const files = await fs.promises.readdir(this.verbsDir);
289
+ for (const file of files) {
290
+ if (file.endsWith('.json')) {
291
+ const filePath = path.join(this.verbsDir, file);
292
+ const data = await fs.promises.readFile(filePath, 'utf-8');
293
+ const parsedEdge = JSON.parse(data);
294
+ // Convert serialized connections back to Map<number, Set<string>>
295
+ const connections = new Map();
296
+ for (const [level, nodeIds] of Object.entries(parsedEdge.connections)) {
297
+ connections.set(Number(level), new Set(nodeIds));
298
+ }
299
+ allEdges.push({
300
+ id: parsedEdge.id,
301
+ vector: parsedEdge.vector,
302
+ connections
303
+ });
304
+ }
305
+ }
306
+ }
307
+ catch (error) {
308
+ if (error.code !== 'ENOENT') {
309
+ console.error(`Error reading directory ${this.verbsDir}:`, error);
310
+ }
311
+ }
312
+ return allEdges;
313
+ }
314
+ /**
315
+ * Get edges by source
316
+ */
317
+ async getEdgesBySource(sourceId) {
318
+ // This method is deprecated and would require loading metadata for each edge
319
+ // For now, return empty array since this is not efficiently implementable with new storage pattern
320
+ console.warn('getEdgesBySource is deprecated and not efficiently supported in new storage pattern');
321
+ return [];
322
+ }
323
+ /**
324
+ * Get edges by target
325
+ */
326
+ async getEdgesByTarget(targetId) {
327
+ // This method is deprecated and would require loading metadata for each edge
328
+ // For now, return empty array since this is not efficiently implementable with new storage pattern
329
+ console.warn('getEdgesByTarget is deprecated and not efficiently supported in new storage pattern');
330
+ return [];
331
+ }
332
+ /**
333
+ * Get edges by type
334
+ */
335
+ async getEdgesByType(type) {
336
+ // This method is deprecated and would require loading metadata for each edge
337
+ // For now, return empty array since this is not efficiently implementable with new storage pattern
338
+ console.warn('getEdgesByType is deprecated and not efficiently supported in new storage pattern');
339
+ return [];
340
+ }
341
+ /**
342
+ * Delete an edge from storage
343
+ */
344
+ async deleteEdge(id) {
345
+ await this.ensureInitialized();
346
+ const filePath = path.join(this.verbsDir, `${id}.json`);
347
+ try {
348
+ await fs.promises.unlink(filePath);
349
+ }
350
+ catch (error) {
351
+ if (error.code !== 'ENOENT') {
352
+ console.error(`Error deleting edge file ${filePath}:`, error);
353
+ throw error;
354
+ }
355
+ }
356
+ }
357
+ /**
358
+ * Save metadata to storage
359
+ */
360
+ async saveMetadata(id, metadata) {
361
+ await this.ensureInitialized();
362
+ const filePath = path.join(this.metadataDir, `${id}.json`);
363
+ await fs.promises.writeFile(filePath, JSON.stringify(metadata, null, 2));
364
+ }
365
+ /**
366
+ * Get metadata from storage
367
+ */
368
+ async getMetadata(id) {
369
+ await this.ensureInitialized();
370
+ const filePath = path.join(this.metadataDir, `${id}.json`);
371
+ try {
372
+ const data = await fs.promises.readFile(filePath, 'utf-8');
373
+ return JSON.parse(data);
374
+ }
375
+ catch (error) {
376
+ if (error.code !== 'ENOENT') {
377
+ console.error(`Error reading metadata ${id}:`, error);
378
+ }
379
+ return null;
380
+ }
381
+ }
382
+ /**
383
+ * Save noun metadata to storage
384
+ */
385
+ async saveNounMetadata(id, metadata) {
386
+ await this.ensureInitialized();
387
+ const filePath = path.join(this.nounMetadataDir, `${id}.json`);
388
+ await fs.promises.writeFile(filePath, JSON.stringify(metadata, null, 2));
389
+ }
390
+ /**
391
+ * Get noun metadata from storage
392
+ */
393
+ async getNounMetadata(id) {
394
+ await this.ensureInitialized();
395
+ const filePath = path.join(this.nounMetadataDir, `${id}.json`);
396
+ try {
397
+ const data = await fs.promises.readFile(filePath, 'utf-8');
398
+ return JSON.parse(data);
399
+ }
400
+ catch (error) {
401
+ if (error.code !== 'ENOENT') {
402
+ console.error(`Error reading noun metadata ${id}:`, error);
403
+ }
404
+ return null;
405
+ }
406
+ }
407
+ /**
408
+ * Save verb metadata to storage
409
+ */
410
+ async saveVerbMetadata(id, metadata) {
411
+ await this.ensureInitialized();
412
+ const filePath = path.join(this.verbMetadataDir, `${id}.json`);
413
+ await fs.promises.writeFile(filePath, JSON.stringify(metadata, null, 2));
414
+ }
415
+ /**
416
+ * Get verb metadata from storage
417
+ */
418
+ async getVerbMetadata(id) {
419
+ await this.ensureInitialized();
420
+ const filePath = path.join(this.verbMetadataDir, `${id}.json`);
421
+ try {
422
+ const data = await fs.promises.readFile(filePath, 'utf-8');
423
+ return JSON.parse(data);
424
+ }
425
+ catch (error) {
426
+ if (error.code !== 'ENOENT') {
427
+ console.error(`Error reading verb metadata ${id}:`, error);
428
+ }
429
+ return null;
430
+ }
431
+ }
432
+ /**
433
+ * Clear all data from storage
434
+ */
435
+ async clear() {
436
+ await this.ensureInitialized();
437
+ // Check if fs module is available
438
+ if (!fs || !fs.promises) {
439
+ console.warn('FileSystemStorage.clear: fs module not available, skipping clear operation');
440
+ return;
441
+ }
442
+ // Helper function to remove all files in a directory
443
+ const removeDirectoryContents = async (dirPath) => {
444
+ try {
445
+ const files = await fs.promises.readdir(dirPath);
446
+ for (const file of files) {
447
+ const filePath = path.join(dirPath, file);
448
+ const stats = await fs.promises.stat(filePath);
449
+ if (stats.isDirectory()) {
450
+ await removeDirectoryContents(filePath);
451
+ await fs.promises.rmdir(filePath);
452
+ }
453
+ else {
454
+ await fs.promises.unlink(filePath);
455
+ }
456
+ }
457
+ }
458
+ catch (error) {
459
+ if (error.code !== 'ENOENT') {
460
+ console.error(`Error removing directory contents ${dirPath}:`, error);
461
+ throw error;
462
+ }
463
+ }
464
+ };
465
+ // Remove all files in the nouns directory
466
+ await removeDirectoryContents(this.nounsDir);
467
+ // Remove all files in the verbs directory
468
+ await removeDirectoryContents(this.verbsDir);
469
+ // Remove all files in the metadata directory
470
+ await removeDirectoryContents(this.metadataDir);
471
+ // Remove all files in the noun metadata directory
472
+ await removeDirectoryContents(this.nounMetadataDir);
473
+ // Remove all files in the verb metadata directory
474
+ await removeDirectoryContents(this.verbMetadataDir);
475
+ // Remove all files in the index directory
476
+ await removeDirectoryContents(this.indexDir);
477
+ // Clear the statistics cache
478
+ this.statisticsCache = null;
479
+ this.statisticsModified = false;
480
+ }
481
+ /**
482
+ * Get information about storage usage and capacity
483
+ */
484
+ async getStorageStatus() {
485
+ await this.ensureInitialized();
486
+ // Check if fs module is available
487
+ if (!fs || !fs.promises) {
488
+ console.warn('FileSystemStorage.getStorageStatus: fs module not available, returning default values');
489
+ return {
490
+ type: 'filesystem',
491
+ used: 0,
492
+ quota: null,
493
+ details: {
494
+ nounsCount: 0,
495
+ verbsCount: 0,
496
+ metadataCount: 0,
497
+ directorySizes: {
498
+ nouns: 0,
499
+ verbs: 0,
500
+ metadata: 0,
501
+ index: 0
502
+ }
503
+ }
504
+ };
505
+ }
506
+ try {
507
+ // Calculate the total size of all files in the storage directories
508
+ let totalSize = 0;
509
+ // Helper function to calculate directory size
510
+ const calculateSize = async (dirPath) => {
511
+ let size = 0;
512
+ try {
513
+ const files = await fs.promises.readdir(dirPath);
514
+ for (const file of files) {
515
+ const filePath = path.join(dirPath, file);
516
+ const stats = await fs.promises.stat(filePath);
517
+ if (stats.isDirectory()) {
518
+ size += await calculateSize(filePath);
519
+ }
520
+ else {
521
+ size += stats.size;
522
+ }
523
+ }
524
+ }
525
+ catch (error) {
526
+ if (error.code !== 'ENOENT') {
527
+ console.error(`Error calculating size for directory ${dirPath}:`, error);
528
+ }
529
+ }
530
+ return size;
531
+ };
532
+ // Calculate size for each directory
533
+ const nounsDirSize = await calculateSize(this.nounsDir);
534
+ const verbsDirSize = await calculateSize(this.verbsDir);
535
+ const metadataDirSize = await calculateSize(this.metadataDir);
536
+ const indexDirSize = await calculateSize(this.indexDir);
537
+ totalSize = nounsDirSize + verbsDirSize + metadataDirSize + indexDirSize;
538
+ // Count files in each directory
539
+ const nounsCount = (await fs.promises.readdir(this.nounsDir)).filter((file) => file.endsWith('.json')).length;
540
+ const verbsCount = (await fs.promises.readdir(this.verbsDir)).filter((file) => file.endsWith('.json')).length;
541
+ const metadataCount = (await fs.promises.readdir(this.metadataDir)).filter((file) => file.endsWith('.json')).length;
542
+ // Count nouns by type using metadata
543
+ const nounTypeCounts = {};
544
+ const metadataFiles = await fs.promises.readdir(this.metadataDir);
545
+ for (const file of metadataFiles) {
546
+ if (file.endsWith('.json')) {
547
+ try {
548
+ const filePath = path.join(this.metadataDir, file);
549
+ const data = await fs.promises.readFile(filePath, 'utf-8');
550
+ const metadata = JSON.parse(data);
551
+ if (metadata.noun) {
552
+ nounTypeCounts[metadata.noun] =
553
+ (nounTypeCounts[metadata.noun] || 0) + 1;
554
+ }
555
+ }
556
+ catch (error) {
557
+ console.error(`Error reading metadata file ${file}:`, error);
558
+ }
559
+ }
560
+ }
561
+ return {
562
+ type: 'filesystem',
563
+ used: totalSize,
564
+ quota: null, // File system doesn't provide quota information
565
+ details: {
566
+ rootDirectory: this.rootDir,
567
+ nounsCount,
568
+ verbsCount,
569
+ metadataCount,
570
+ nounsDirSize,
571
+ verbsDirSize,
572
+ metadataDirSize,
573
+ indexDirSize,
574
+ nounTypes: nounTypeCounts
575
+ }
576
+ };
577
+ }
578
+ catch (error) {
579
+ console.error('Failed to get storage status:', error);
580
+ return {
581
+ type: 'filesystem',
582
+ used: 0,
583
+ quota: null,
584
+ details: { error: String(error) }
585
+ };
586
+ }
587
+ }
588
+ /**
589
+ * Implementation of abstract methods from BaseStorage
590
+ */
591
+ /**
592
+ * Save a noun to storage
593
+ */
594
+ async saveNoun_internal(noun) {
595
+ return this.saveNode(noun);
596
+ }
597
+ /**
598
+ * Get a noun from storage
599
+ */
600
+ async getNoun_internal(id) {
601
+ return this.getNode(id);
602
+ }
603
+ /**
604
+ * Get all nouns from storage
605
+ */
606
+ async getAllNouns_internal() {
607
+ return this.getAllNodes();
608
+ }
609
+ /**
610
+ * Get nouns by noun type
611
+ */
612
+ async getNounsByNounType_internal(nounType) {
613
+ return this.getNodesByNounType(nounType);
614
+ }
615
+ /**
616
+ * Delete a noun from storage
617
+ */
618
+ async deleteNoun_internal(id) {
619
+ return this.deleteNode(id);
620
+ }
621
+ /**
622
+ * Save a verb to storage
623
+ */
624
+ async saveVerb_internal(verb) {
625
+ return this.saveEdge(verb);
626
+ }
627
+ /**
628
+ * Get a verb from storage
629
+ */
630
+ async getVerb_internal(id) {
631
+ return this.getEdge(id);
632
+ }
633
+ /**
634
+ * Get all verbs from storage
635
+ */
636
+ async getAllVerbs_internal() {
637
+ return this.getAllEdges();
638
+ }
639
+ /**
640
+ * Get verbs by source
641
+ */
642
+ async getVerbsBySource_internal(sourceId) {
643
+ // This method is deprecated and would require loading metadata for each edge
644
+ // For now, return empty array since this is not efficiently implementable with new storage pattern
645
+ console.warn('getVerbsBySource_internal is deprecated and not efficiently supported in new storage pattern');
646
+ return [];
647
+ }
648
+ /**
649
+ * Get verbs by target
650
+ */
651
+ async getVerbsByTarget_internal(targetId) {
652
+ // This method is deprecated and would require loading metadata for each edge
653
+ // For now, return empty array since this is not efficiently implementable with new storage pattern
654
+ console.warn('getVerbsByTarget_internal is deprecated and not efficiently supported in new storage pattern');
655
+ return [];
656
+ }
657
+ /**
658
+ * Get verbs by type
659
+ */
660
+ async getVerbsByType_internal(type) {
661
+ // This method is deprecated and would require loading metadata for each edge
662
+ // For now, return empty array since this is not efficiently implementable with new storage pattern
663
+ console.warn('getVerbsByType_internal is deprecated and not efficiently supported in new storage pattern');
664
+ return [];
665
+ }
666
+ /**
667
+ * Delete a verb from storage
668
+ */
669
+ async deleteVerb_internal(id) {
670
+ return this.deleteEdge(id);
671
+ }
672
+ /**
673
+ * Acquire a file-based lock for coordinating operations across multiple processes
674
+ * @param lockKey The key to lock on
675
+ * @param ttl Time to live for the lock in milliseconds (default: 30 seconds)
676
+ * @returns Promise that resolves to true if lock was acquired, false otherwise
677
+ */
678
+ async acquireLock(lockKey, ttl = 30000) {
679
+ await this.ensureInitialized();
680
+ const lockFile = path.join(this.lockDir, `${lockKey}.lock`);
681
+ const lockValue = `${Date.now()}_${Math.random()}_${process.pid || 'unknown'}`;
682
+ const expiresAt = Date.now() + ttl;
683
+ try {
684
+ // Check if lock file already exists and is still valid
685
+ try {
686
+ const lockData = await fs.promises.readFile(lockFile, 'utf-8');
687
+ const lockInfo = JSON.parse(lockData);
688
+ if (lockInfo.expiresAt > Date.now()) {
689
+ // Lock exists and is still valid
690
+ return false;
691
+ }
692
+ }
693
+ catch (error) {
694
+ // If file doesn't exist or can't be read, we can proceed to create the lock
695
+ if (error.code !== 'ENOENT') {
696
+ console.warn(`Error reading lock file ${lockFile}:`, error);
697
+ }
698
+ }
699
+ // Try to create the lock file
700
+ const lockInfo = {
701
+ lockValue,
702
+ expiresAt,
703
+ pid: process.pid || 'unknown',
704
+ timestamp: Date.now()
705
+ };
706
+ await fs.promises.writeFile(lockFile, JSON.stringify(lockInfo, null, 2));
707
+ // Add to active locks for cleanup
708
+ this.activeLocks.add(lockKey);
709
+ // Schedule automatic cleanup when lock expires
710
+ setTimeout(() => {
711
+ this.releaseLock(lockKey, lockValue).catch((error) => {
712
+ console.warn(`Failed to auto-release expired lock ${lockKey}:`, error);
713
+ });
714
+ }, ttl);
715
+ return true;
716
+ }
717
+ catch (error) {
718
+ console.warn(`Failed to acquire lock ${lockKey}:`, error);
719
+ return false;
720
+ }
721
+ }
722
+ /**
723
+ * Release a file-based lock
724
+ * @param lockKey The key to unlock
725
+ * @param lockValue The value used when acquiring the lock (for verification)
726
+ * @returns Promise that resolves when lock is released
727
+ */
728
+ async releaseLock(lockKey, lockValue) {
729
+ await this.ensureInitialized();
730
+ const lockFile = path.join(this.lockDir, `${lockKey}.lock`);
731
+ try {
732
+ // If lockValue is provided, verify it matches before releasing
733
+ if (lockValue) {
734
+ try {
735
+ const lockData = await fs.promises.readFile(lockFile, 'utf-8');
736
+ const lockInfo = JSON.parse(lockData);
737
+ if (lockInfo.lockValue !== lockValue) {
738
+ // Lock was acquired by someone else, don't release it
739
+ return;
740
+ }
741
+ }
742
+ catch (error) {
743
+ // If lock file doesn't exist, that's fine
744
+ if (error.code === 'ENOENT') {
745
+ return;
746
+ }
747
+ throw error;
748
+ }
749
+ }
750
+ // Delete the lock file
751
+ await fs.promises.unlink(lockFile);
752
+ // Remove from active locks
753
+ this.activeLocks.delete(lockKey);
754
+ }
755
+ catch (error) {
756
+ if (error.code !== 'ENOENT') {
757
+ console.warn(`Failed to release lock ${lockKey}:`, error);
758
+ }
759
+ }
760
+ }
761
+ /**
762
+ * Clean up expired lock files
763
+ */
764
+ async cleanupExpiredLocks() {
765
+ await this.ensureInitialized();
766
+ try {
767
+ const lockFiles = await fs.promises.readdir(this.lockDir);
768
+ const now = Date.now();
769
+ for (const lockFile of lockFiles) {
770
+ if (!lockFile.endsWith('.lock'))
771
+ continue;
772
+ const lockPath = path.join(this.lockDir, lockFile);
773
+ try {
774
+ const lockData = await fs.promises.readFile(lockPath, 'utf-8');
775
+ const lockInfo = JSON.parse(lockData);
776
+ if (lockInfo.expiresAt <= now) {
777
+ await fs.promises.unlink(lockPath);
778
+ const lockKey = lockFile.replace('.lock', '');
779
+ this.activeLocks.delete(lockKey);
780
+ }
781
+ }
782
+ catch (error) {
783
+ // If we can't read or parse the lock file, remove it
784
+ try {
785
+ await fs.promises.unlink(lockPath);
786
+ }
787
+ catch (unlinkError) {
788
+ console.warn(`Failed to cleanup invalid lock file ${lockPath}:`, unlinkError);
789
+ }
790
+ }
791
+ }
792
+ }
793
+ catch (error) {
794
+ console.warn('Failed to cleanup expired locks:', error);
795
+ }
796
+ }
797
+ /**
798
+ * Save statistics data to storage with file-based locking
799
+ */
800
+ async saveStatisticsData(statistics) {
801
+ const lockKey = 'statistics';
802
+ const lockAcquired = await this.acquireLock(lockKey, 10000); // 10 second timeout
803
+ if (!lockAcquired) {
804
+ console.warn('Failed to acquire lock for statistics update, proceeding without lock');
805
+ }
806
+ try {
807
+ // Get existing statistics to merge with new data
808
+ const existingStats = (await this.getMetadata(STATISTICS_KEY));
809
+ if (existingStats) {
810
+ // Merge statistics data
811
+ const mergedStats = {
812
+ totalNodes: Math.max(statistics.totalNodes || 0, existingStats.totalNodes || 0),
813
+ totalEdges: Math.max(statistics.totalEdges || 0, existingStats.totalEdges || 0),
814
+ totalMetadata: Math.max(statistics.totalMetadata || 0, existingStats.totalMetadata || 0),
815
+ // Preserve any additional fields from existing stats
816
+ ...existingStats,
817
+ // Override with new values where provided
818
+ ...statistics,
819
+ // Always update lastUpdated to current time
820
+ lastUpdated: new Date().toISOString()
821
+ };
822
+ await this.saveMetadata(STATISTICS_KEY, mergedStats);
823
+ }
824
+ else {
825
+ // No existing statistics, save new ones
826
+ const newStats = {
827
+ ...statistics,
828
+ lastUpdated: new Date().toISOString()
829
+ };
830
+ await this.saveMetadata(STATISTICS_KEY, newStats);
831
+ }
832
+ }
833
+ finally {
834
+ if (lockAcquired) {
835
+ await this.releaseLock(lockKey);
836
+ }
837
+ }
838
+ }
839
+ /**
840
+ * Get statistics data from storage
841
+ */
842
+ async getStatisticsData() {
843
+ return this.getMetadata(STATISTICS_KEY);
844
+ }
845
+ }
846
+ //# sourceMappingURL=fileSystemStorage.js.map