@soulcraft/brainy 4.11.0 → 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,36 @@
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
+
5
35
  ## [4.11.0](https://github.com/soulcraftlabs/brainy/compare/v4.10.4...v4.11.0) (2025-10-30)
6
36
 
7
37
  ### 🚨 CRITICAL BUG FIX
@@ -100,6 +100,7 @@ export declare class DataAPI {
100
100
  }): Promise<{
101
101
  entitiesRestored: number;
102
102
  relationshipsRestored: number;
103
+ relationshipsSkipped: number;
103
104
  errors: Array<{
104
105
  type: 'entity' | 'relation';
105
106
  id: string;
@@ -90,6 +90,7 @@ export class DataAPI {
90
90
  const result = {
91
91
  entitiesRestored: 0,
92
92
  relationshipsRestored: 0,
93
+ relationshipsSkipped: 0,
93
94
  errors: []
94
95
  };
95
96
  // Validate backup format
@@ -139,6 +140,8 @@ export class DataAPI {
139
140
  };
140
141
  });
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();
142
145
  if (entityParams.length > 0) {
143
146
  try {
144
147
  const addResult = await this.brain.addMany({
@@ -149,6 +152,10 @@ export class DataAPI {
149
152
  }
150
153
  });
151
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
+ });
152
159
  // Track errors
153
160
  addResult.failed.forEach((failure) => {
154
161
  result.errors.push({
@@ -164,9 +171,9 @@ export class DataAPI {
164
171
  }
165
172
  // ============================================
166
173
  // Phase 2: Restore relationships using relateMany()
167
- // v4.11.1: Uses proper persistence path through brain.relateMany()
174
+ // v4.11.1: CRITICAL FIX - Filter orphaned relationships
168
175
  // ============================================
169
- // Prepare relationship parameters for relateMany()
176
+ // Prepare relationship parameters - filter out orphaned references
170
177
  const relationParams = backup.relations
171
178
  .filter(relation => {
172
179
  // Skip existing relations when merging without overwrite
@@ -174,6 +181,20 @@ export class DataAPI {
174
181
  // Note: We'll rely on relateMany's internal duplicate handling
175
182
  return true;
176
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'})`);
195
+ }
196
+ return false;
197
+ }
177
198
  return true;
178
199
  })
179
200
  .map(relation => ({
@@ -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.11.0",
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",