@soulcraft/brainy 4.10.4 β†’ 4.11.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,131 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [4.11.1](https://github.com/soulcraftlabs/brainy/compare/v4.11.0...v4.11.1) (2025-10-30)
6
+
7
+ - fix: prevent orphaned relationships in restore() and add VFS progress tracking (549d773)
8
+
9
+
10
+ ## [4.11.1](https://github.com/soulcraftlabs/brainy/compare/v4.11.0...v4.11.1) (2025-10-30)
11
+
12
+ ### πŸ› Bug Fixes
13
+
14
+ * **fix(api)**: DataAPI.restore() now filters orphaned relationships (P0 Critical)
15
+ - **Issue**: restore() created relationships to entities that failed to restore, causing "Entity not found" errors
16
+ - **Root Cause**: Relationships were not filtered based on successfully restored entities
17
+ - **Fix**: Now builds Set of successful entity IDs and filters relationships accordingly
18
+ - **New Tracking**: Added `relationshipsSkipped` to return type for visibility
19
+ - **Impact**: Prevents complete data corruption when some entities fail to restore
20
+
21
+ * **fix(import)**: VFS creation now reports progress during import (P1 High)
22
+ - **Issue**: 3-5 minute VFS creation showed no progress (stuck at 0%), causing users to think import froze
23
+ - **Root Cause**: VFSStructureGenerator.generate() had no progress callback parameter
24
+ - **Fix**: Added onProgress callback to VFSStructureOptions interface
25
+ - **Progress Stages**: Reports 'directories', 'entities', 'metadata' with detailed messages
26
+ - **Frequency**: Reports every 10 entity files to avoid excessive updates
27
+ - **Integration**: Wired through ImportCoordinator to main progress callback
28
+
29
+ ### πŸ“ Files Modified
30
+
31
+ * `src/api/DataAPI.ts` (lines 173-350) - Added orphaned relationship filtering
32
+ * `src/importers/VFSStructureGenerator.ts` (lines 18-53, 110-347) - Added progress callback
33
+ * `src/import/ImportCoordinator.ts` (lines 438-459) - Wired progress callback
34
+
35
+ ## [4.11.0](https://github.com/soulcraftlabs/brainy/compare/v4.10.4...v4.11.0) (2025-10-30)
36
+
37
+ ### 🚨 CRITICAL BUG FIX
38
+
39
+ **DataAPI.restore() Complete Data Loss Bug Fixed**
40
+
41
+ Previous versions (v4.10.4 and earlier) had a critical bug where `DataAPI.restore()` did NOT persist data to storage, causing complete data loss after instance restart or cache clear. **If you used backup/restore in v4.10.4 or earlier, your restored data was NOT saved.**
42
+
43
+ ### πŸ”§ What Was Fixed
44
+
45
+ * **fix(api)**: DataAPI.restore() now properly persists data to all storage adapters
46
+ - **Root Cause**: restore() called `storage.saveNoun()` directly, bypassing all indexes and proper persistence
47
+ - **Fix**: Now uses `brain.addMany()` and `brain.relateMany()` (proper persistence path)
48
+ - **Result**: Data now survives instance restart and is fully indexed/searchable
49
+
50
+ ### ✨ Improvements
51
+
52
+ * **feat(api)**: Enhanced restore() with progress reporting and error tracking
53
+ - **New Return Type**: Returns `{ entitiesRestored, relationshipsRestored, errors }` instead of `void`
54
+ - **Progress Callback**: Optional `onProgress(completed, total)` parameter for UI updates
55
+ - **Error Details**: Returns array of failed entities/relations with error messages
56
+ - **Verification**: Automatically verifies first entity is retrievable after restore
57
+
58
+ * **feat(api)**: Cross-storage restore support
59
+ - Backup from any storage adapter, restore to any other
60
+ - Example: Backup from GCS β†’ Restore to Filesystem
61
+ - Automatically uses target storage's optimal batch configuration
62
+
63
+ * **perf(api)**: Storage-aware batching for restore operations
64
+ - Leverages v4.10.4's storage-aware batching (10-100x faster on cloud storage)
65
+ - Automatic backpressure management prevents circuit breaker activation
66
+ - Separate read/write circuit breakers (backup can run during restore throttling)
67
+
68
+ ### πŸ“Š What's Now Guaranteed
69
+
70
+ | Feature | v4.10.4 | v4.11.0 |
71
+ |---------|---------|---------|
72
+ | Data Persists to Storage | ❌ No | βœ… Yes |
73
+ | Data Survives Restart | ❌ No | βœ… Yes |
74
+ | HNSW Index Updated | ❌ No | βœ… Yes |
75
+ | Metadata Index Updated | ❌ No | βœ… Yes |
76
+ | Searchable After Restore | ❌ No | βœ… Yes |
77
+ | Progress Reporting | ❌ No | βœ… Yes |
78
+ | Error Tracking | ❌ Silent | βœ… Detailed |
79
+ | Cross-Storage Support | ❌ No | βœ… Yes |
80
+
81
+ ### πŸ”„ Migration Guide
82
+
83
+ **No code changes required!** The fix is backward compatible:
84
+
85
+ ```typescript
86
+ // Old code (still works)
87
+ await brain.data().restore({ backup, overwrite: true })
88
+
89
+ // New code (with progress tracking)
90
+ const result = await brain.data().restore({
91
+ backup,
92
+ overwrite: true,
93
+ onProgress: (done, total) => {
94
+ console.log(`Restoring... ${done}/${total}`)
95
+ }
96
+ })
97
+
98
+ console.log(`βœ… Restored ${result.entitiesRestored} entities`)
99
+ if (result.errors.length > 0) {
100
+ console.warn(`⚠️ ${result.errors.length} failures`)
101
+ }
102
+ ```
103
+
104
+ ### ⚠️ Breaking Changes (Minor API Change)
105
+
106
+ * **DataAPI.restore()** return type changed from `Promise<void>` to `Promise<{ entitiesRestored, relationshipsRestored, errors }>`
107
+ - Impact: Minimal - most code doesn't use the return value
108
+ - Fix: Remove explicit `Promise<void>` type annotations if present
109
+
110
+ ### πŸ“ Files Modified
111
+
112
+ * `src/api/DataAPI.ts` - Complete rewrite of restore() method (lines 161-338)
113
+
114
+ ### [4.10.4](https://github.com/soulcraftlabs/brainy/compare/v4.10.3...v4.10.4) (2025-10-30)
115
+
116
+ * fix: prevent circuit breaker activation and data loss during bulk imports
117
+ - Storage-aware batching system prevents rate limiting on cloud storage (GCS, S3, R2, Azure)
118
+ - Separate read/write circuit breakers prevent read lockouts during write throttling
119
+ - ImportCoordinator uses addMany()/relateMany() for 10-100x performance improvement
120
+ - Fixes silent data loss and 30+ second lockouts on 1000+ row imports
121
+
122
+ ### [4.10.3](https://github.com/soulcraftlabs/brainy/compare/v4.10.2...v4.10.3) (2025-10-29)
123
+
124
+ * fix: add atomic writes to ALL file operations to prevent concurrent write corruption
125
+
126
+ ### [4.10.2](https://github.com/soulcraftlabs/brainy/compare/v4.10.1...v4.10.2) (2025-10-29)
127
+
128
+ * fix: VFS not initialized during Excel import, causing 0 files accessible
129
+
5
130
  ### [4.10.1](https://github.com/soulcraftlabs/brainy/compare/v4.10.0...v4.10.1) (2025-10-29)
6
131
 
7
132
  - fix: add mutex locks to FileSystemStorage for HNSW concurrency (CRITICAL) (ff86e88)
@@ -81,13 +81,32 @@ export declare class DataAPI {
81
81
  }>;
82
82
  /**
83
83
  * Restore data from a backup
84
+ *
85
+ * v4.11.1: CRITICAL FIX - Now uses brain.addMany() and brain.relateMany()
86
+ * Previous implementation only wrote to storage cache without updating indexes,
87
+ * causing complete data loss on restart. This fix ensures:
88
+ * - All 5 indexes updated (HNSW, metadata, adjacency, sparse, type-aware)
89
+ * - Proper persistence to disk/cloud storage
90
+ * - Storage-aware batching for optimal performance
91
+ * - Atomic writes to prevent corruption
92
+ * - Data survives instance restart
84
93
  */
85
94
  restore(params: {
86
95
  backup: BackupData;
87
96
  merge?: boolean;
88
97
  overwrite?: boolean;
89
98
  validate?: boolean;
90
- }): Promise<void>;
99
+ onProgress?: (completed: number, total: number) => void;
100
+ }): Promise<{
101
+ entitiesRestored: number;
102
+ relationshipsRestored: number;
103
+ relationshipsSkipped: number;
104
+ errors: Array<{
105
+ type: 'entity' | 'relation';
106
+ id: string;
107
+ error: string;
108
+ }>;
109
+ }>;
91
110
  /**
92
111
  * Clear data
93
112
  */
@@ -75,89 +75,171 @@ export class DataAPI {
75
75
  }
76
76
  /**
77
77
  * Restore data from a backup
78
+ *
79
+ * v4.11.1: CRITICAL FIX - Now uses brain.addMany() and brain.relateMany()
80
+ * Previous implementation only wrote to storage cache without updating indexes,
81
+ * causing complete data loss on restart. This fix ensures:
82
+ * - All 5 indexes updated (HNSW, metadata, adjacency, sparse, type-aware)
83
+ * - Proper persistence to disk/cloud storage
84
+ * - Storage-aware batching for optimal performance
85
+ * - Atomic writes to prevent corruption
86
+ * - Data survives instance restart
78
87
  */
79
88
  async restore(params) {
80
- const { backup, merge = false, overwrite = false, validate = true } = params;
89
+ const { backup, merge = false, overwrite = false, validate = true, onProgress } = params;
90
+ const result = {
91
+ entitiesRestored: 0,
92
+ relationshipsRestored: 0,
93
+ relationshipsSkipped: 0,
94
+ errors: []
95
+ };
81
96
  // Validate backup format
82
97
  if (validate) {
83
98
  if (!backup.version || !backup.entities || !backup.relations) {
84
- throw new Error('Invalid backup format');
99
+ throw new Error('Invalid backup format: missing version, entities, or relations');
85
100
  }
86
101
  }
102
+ // Validate brain instance is available (required for v4.11.1+ restore)
103
+ if (!this.brain) {
104
+ throw new Error('Restore requires brain instance. DataAPI must be initialized with brain reference. ' +
105
+ 'Use: await brain.data() instead of constructing DataAPI directly.');
106
+ }
87
107
  // Clear existing data if not merging
88
108
  if (!merge && overwrite) {
89
109
  await this.clear({ entities: true, relations: true });
90
110
  }
91
- // Restore entities
92
- for (const entity of backup.entities) {
111
+ // ============================================
112
+ // Phase 1: Restore entities using addMany()
113
+ // v4.11.1: Uses proper persistence path through brain.addMany()
114
+ // ============================================
115
+ // Prepare entity parameters for addMany()
116
+ const entityParams = backup.entities
117
+ .filter(entity => {
118
+ // Skip existing entities when merging without overwrite
119
+ if (merge && !overwrite) {
120
+ // Note: We'll rely on addMany's internal duplicate handling
121
+ // rather than checking each entity individually (performance)
122
+ return true;
123
+ }
124
+ return true;
125
+ })
126
+ .map(entity => {
127
+ // Extract data field from metadata (backup format compatibility)
128
+ // Backup stores the original data in metadata.data
129
+ const data = entity.metadata?.data || entity.id;
130
+ return {
131
+ id: entity.id,
132
+ data, // Required field for brainy.add()
133
+ type: entity.type,
134
+ metadata: entity.metadata || {},
135
+ vector: entity.vector, // Preserve original vectors from backup
136
+ service: entity.service,
137
+ // Preserve confidence and weight if available
138
+ confidence: entity.metadata?.confidence,
139
+ weight: entity.metadata?.weight
140
+ };
141
+ });
142
+ // Restore entities in batches using storage-aware batching (v4.11.0)
143
+ // v4.11.1: Track successful entity IDs to prevent orphaned relationships
144
+ const successfulEntityIds = new Set();
145
+ if (entityParams.length > 0) {
93
146
  try {
94
- // v4.0.0: Prepare noun and metadata separately
95
- const noun = {
96
- id: entity.id,
97
- vector: entity.vector || new Array(384).fill(0), // Default vector if missing
98
- connections: new Map(),
99
- level: 0
100
- };
101
- const metadata = {
102
- ...entity.metadata,
103
- noun: entity.type,
104
- service: entity.service,
105
- createdAt: Date.now()
106
- };
107
- // Check if entity exists when merging
108
- if (merge) {
109
- const existing = await this.storage.getNoun(entity.id);
110
- if (existing && !overwrite) {
111
- continue; // Skip existing entities unless overwriting
147
+ const addResult = await this.brain.addMany({
148
+ items: entityParams,
149
+ continueOnError: true,
150
+ onProgress: (done, total) => {
151
+ onProgress?.(done, backup.entities.length + backup.relations.length);
112
152
  }
113
- }
114
- await this.storage.saveNoun(noun);
115
- await this.storage.saveNounMetadata(entity.id, metadata);
153
+ });
154
+ result.entitiesRestored = addResult.successful.length;
155
+ // Build Set of successfully restored entity IDs (v4.11.1 Bug Fix)
156
+ addResult.successful.forEach((entityId) => {
157
+ successfulEntityIds.add(entityId);
158
+ });
159
+ // Track errors
160
+ addResult.failed.forEach((failure) => {
161
+ result.errors.push({
162
+ type: 'entity',
163
+ id: failure.item?.id || 'unknown',
164
+ error: failure.error || 'Unknown error'
165
+ });
166
+ });
116
167
  }
117
168
  catch (error) {
118
- console.error(`Failed to restore entity ${entity.id}:`, error);
169
+ throw new Error(`Failed to restore entities: ${error.message}`);
119
170
  }
120
171
  }
121
- // Restore relations
122
- for (const relation of backup.relations) {
123
- try {
124
- // Get source and target entities to compute relation vector
125
- const sourceNoun = await this.storage.getNoun(relation.from);
126
- const targetNoun = await this.storage.getNoun(relation.to);
127
- if (!sourceNoun || !targetNoun) {
128
- console.warn(`Skipping relation ${relation.id}: missing entities`);
129
- continue;
130
- }
131
- // Compute relation vector as average of source and target
132
- const relationVector = sourceNoun.vector.map((v, i) => (v + targetNoun.vector[i]) / 2);
133
- // v4.0.0: Prepare verb and metadata separately
134
- const verb = {
135
- id: relation.id,
136
- vector: relationVector,
137
- connections: new Map(),
138
- verb: relation.type,
139
- sourceId: relation.from,
140
- targetId: relation.to
141
- };
142
- const verbMetadata = {
143
- weight: relation.weight,
144
- ...relation.metadata,
145
- createdAt: Date.now()
146
- };
147
- // Check if relation exists when merging
148
- if (merge) {
149
- const existing = await this.storage.getVerb(relation.id);
150
- if (existing && !overwrite) {
151
- continue;
152
- }
172
+ // ============================================
173
+ // Phase 2: Restore relationships using relateMany()
174
+ // v4.11.1: CRITICAL FIX - Filter orphaned relationships
175
+ // ============================================
176
+ // Prepare relationship parameters - filter out orphaned references
177
+ const relationParams = backup.relations
178
+ .filter(relation => {
179
+ // Skip existing relations when merging without overwrite
180
+ if (merge && !overwrite) {
181
+ // Note: We'll rely on relateMany's internal duplicate handling
182
+ return true;
183
+ }
184
+ // v4.11.1 CRITICAL BUG FIX: Skip relationships where source or target entity failed to restore
185
+ // This prevents creating orphaned references that cause "Entity not found" errors
186
+ const sourceExists = successfulEntityIds.has(relation.from);
187
+ const targetExists = successfulEntityIds.has(relation.to);
188
+ if (!sourceExists || !targetExists) {
189
+ // Track skipped relationship
190
+ result.relationshipsSkipped++;
191
+ // Optionally log for debugging (can be removed in production)
192
+ if (process.env.NODE_ENV !== 'production') {
193
+ console.debug(`Skipping orphaned relationship: ${relation.from} -> ${relation.to} ` +
194
+ `(${!sourceExists ? 'missing source' : 'missing target'})`);
153
195
  }
154
- await this.storage.saveVerb(verb);
155
- await this.storage.saveVerbMetadata(relation.id, verbMetadata);
196
+ return false;
197
+ }
198
+ return true;
199
+ })
200
+ .map(relation => ({
201
+ from: relation.from,
202
+ to: relation.to,
203
+ type: relation.type,
204
+ metadata: relation.metadata || {},
205
+ weight: relation.weight || 1.0
206
+ // Note: relation.id is ignored - brain.relate() generates new IDs
207
+ // This is intentional to avoid ID conflicts
208
+ }));
209
+ // Restore relationships in batches using storage-aware batching (v4.11.0)
210
+ if (relationParams.length > 0) {
211
+ try {
212
+ const relateResult = await this.brain.relateMany({
213
+ items: relationParams,
214
+ continueOnError: true
215
+ });
216
+ result.relationshipsRestored = relateResult.successful.length;
217
+ // Track errors
218
+ relateResult.failed.forEach((failure) => {
219
+ result.errors.push({
220
+ type: 'relation',
221
+ id: failure.item?.from + '->' + failure.item?.to || 'unknown',
222
+ error: failure.error || 'Unknown error'
223
+ });
224
+ });
156
225
  }
157
226
  catch (error) {
158
- console.error(`Failed to restore relation ${relation.id}:`, error);
227
+ throw new Error(`Failed to restore relationships: ${error.message}`);
228
+ }
229
+ }
230
+ // ============================================
231
+ // Phase 3: Verify restoration succeeded
232
+ // ============================================
233
+ // Sample verification: Check that first entity is actually retrievable
234
+ if (backup.entities.length > 0 && result.entitiesRestored > 0) {
235
+ const firstEntityId = backup.entities[0].id;
236
+ const verified = await this.brain.get(firstEntityId);
237
+ if (!verified) {
238
+ console.warn(`⚠️ Restore completed but verification failed - entity ${firstEntityId} not retrievable. ` +
239
+ `This may indicate a persistence issue with the storage adapter.`);
159
240
  }
160
241
  }
242
+ return result;
161
243
  }
162
244
  /**
163
245
  * Clear data
@@ -137,7 +137,16 @@ export class ImportCoordinator {
137
137
  sourceFilename: normalizedSource.filename || `import.${detection.format}`,
138
138
  createRelationshipFile: true,
139
139
  createMetadataFile: true,
140
- trackingContext // v4.10.0: Pass tracking metadata to VFS
140
+ trackingContext, // v4.10.0: Pass tracking metadata to VFS
141
+ // v4.11.1: Pass progress callback for VFS creation updates
142
+ onProgress: (vfsProgress) => {
143
+ options.onProgress?.({
144
+ stage: 'storing-vfs',
145
+ message: vfsProgress.message,
146
+ processed: vfsProgress.processed,
147
+ total: vfsProgress.total
148
+ });
149
+ }
141
150
  });
142
151
  // Report graph storage stage
143
152
  options.onProgress?.({
@@ -30,6 +30,13 @@ export interface VFSStructureOptions {
30
30
  createMetadataFile?: boolean;
31
31
  /** Import tracking context (v4.10.0) */
32
32
  trackingContext?: TrackingContext;
33
+ /** Progress callback (v4.11.1) - Reports VFS creation progress */
34
+ onProgress?: (progress: {
35
+ stage: 'directories' | 'entities' | 'metadata';
36
+ message: string;
37
+ processed: number;
38
+ total: number;
39
+ }) => void;
33
40
  }
34
41
  export interface VFSStructureResult {
35
42
  /** Root path created */
@@ -48,6 +48,26 @@ export class VFSStructureGenerator {
48
48
  };
49
49
  // Ensure VFS is initialized
50
50
  await this.init();
51
+ // v4.11.1: Calculate total operations for progress tracking
52
+ const groups = this.groupEntities(importResult, options);
53
+ const totalEntities = Array.from(groups.values()).reduce((sum, entities) => sum + entities.length, 0);
54
+ const totalOperations = 1 + // root directory
55
+ (options.preserveSource ? 1 : 0) + // source file
56
+ groups.size + // group directories
57
+ totalEntities + // entity files
58
+ (options.createRelationshipFile !== false ? 1 : 0) + // relationships file
59
+ (options.createMetadataFile !== false ? 1 : 0); // metadata file
60
+ let completedOperations = 0;
61
+ // Helper to report progress
62
+ const reportProgress = (stage, message) => {
63
+ completedOperations++;
64
+ options.onProgress?.({
65
+ stage,
66
+ message,
67
+ processed: completedOperations,
68
+ total: totalOperations
69
+ });
70
+ };
51
71
  // Extract tracking metadata if provided
52
72
  const trackingMetadata = options.trackingContext ? {
53
73
  importIds: [options.trackingContext.importId],
@@ -65,6 +85,7 @@ export class VFSStructureGenerator {
65
85
  });
66
86
  result.directories.push(options.rootPath);
67
87
  result.operations++;
88
+ reportProgress('directories', `Created root directory: ${options.rootPath}`);
68
89
  }
69
90
  catch (error) {
70
91
  // Directory might already exist, that's fine
@@ -72,6 +93,7 @@ export class VFSStructureGenerator {
72
93
  throw error;
73
94
  }
74
95
  result.directories.push(options.rootPath);
96
+ reportProgress('directories', `Root directory exists: ${options.rootPath}`);
75
97
  }
76
98
  // Preserve source file if requested
77
99
  if (options.preserveSource && options.sourceBuffer && options.sourceFilename) {
@@ -84,9 +106,10 @@ export class VFSStructureGenerator {
84
106
  type: 'source'
85
107
  });
86
108
  result.operations++;
109
+ reportProgress('metadata', `Preserved source file: ${options.sourceFilename}`);
87
110
  }
88
- // Group entities
89
- const groups = this.groupEntities(importResult, options);
111
+ // Note: groups already calculated above for progress tracking
112
+ // const groups = this.groupEntities(importResult, options)
90
113
  // Create directories and files for each group
91
114
  for (const [groupName, entities] of groups.entries()) {
92
115
  const groupPath = `${options.rootPath}/${groupName}`;
@@ -98,6 +121,7 @@ export class VFSStructureGenerator {
98
121
  });
99
122
  result.directories.push(groupPath);
100
123
  result.operations++;
124
+ reportProgress('directories', `Created directory: ${groupName} (${entities.length} entities)`);
101
125
  }
102
126
  catch (error) {
103
127
  // Directory might already exist
@@ -105,9 +129,12 @@ export class VFSStructureGenerator {
105
129
  throw error;
106
130
  }
107
131
  result.directories.push(groupPath);
132
+ reportProgress('directories', `Directory exists: ${groupName}`);
108
133
  }
109
134
  // Create entity files
135
+ let entityCount = 0;
110
136
  for (const extracted of entities) {
137
+ entityCount++;
111
138
  const sanitizedName = this.sanitizeFilename(extracted.entity.name);
112
139
  const entityPath = `${groupPath}/${sanitizedName}.json`;
113
140
  // Create entity JSON
@@ -140,6 +167,10 @@ export class VFSStructureGenerator {
140
167
  type: 'entity'
141
168
  });
142
169
  result.operations++;
170
+ // v4.11.1: Report progress every 10 entities (or on last entity)
171
+ if (entityCount % 10 === 0 || entityCount === entities.length) {
172
+ reportProgress('entities', `Created ${entityCount}/${entities.length} ${groupName} files`);
173
+ }
143
174
  }
144
175
  }
145
176
  // Create relationships file
@@ -167,6 +198,7 @@ export class VFSStructureGenerator {
167
198
  type: 'relationships'
168
199
  });
169
200
  result.operations++;
201
+ reportProgress('metadata', `Created relationships file (${allRelationships.length} relationships)`);
170
202
  }
171
203
  // Create metadata file
172
204
  if (options.createMetadataFile !== false) {
@@ -206,6 +238,16 @@ export class VFSStructureGenerator {
206
238
  type: 'metadata'
207
239
  });
208
240
  result.operations++;
241
+ reportProgress('metadata', 'Created metadata file');
242
+ }
243
+ // v4.11.1: Final progress update
244
+ if (options.onProgress) {
245
+ options.onProgress({
246
+ stage: 'metadata',
247
+ message: `VFS structure created successfully (${result.files.length} files, ${result.directories.length} directories)`,
248
+ processed: totalOperations,
249
+ total: totalOperations
250
+ });
209
251
  }
210
252
  result.duration = Date.now() - startTime;
211
253
  return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulcraft/brainy",
3
- "version": "4.10.4",
3
+ "version": "4.11.1",
4
4
  "description": "Universal Knowledge Protocolβ„’ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. 31 nouns Γ— 40 verbs for infinite expressiveness.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",