@soulcraft/brainy 5.0.0 → 5.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.
- package/CHANGELOG.md +148 -0
- package/README.md +1 -1
- package/dist/augmentations/cacheAugmentation.js +9 -4
- package/dist/brainy.d.ts +87 -15
- package/dist/brainy.js +279 -33
- package/dist/cli/commands/import.js +1 -1
- package/dist/cli/commands/vfs.js +24 -40
- package/dist/import/ImportHistory.js +3 -3
- package/dist/importers/VFSStructureGenerator.d.ts +1 -1
- package/dist/importers/VFSStructureGenerator.js +3 -3
- package/dist/storage/adapters/memoryStorage.d.ts +6 -0
- package/dist/storage/adapters/memoryStorage.js +39 -14
- package/dist/storage/adapters/typeAwareStorageAdapter.d.ts +31 -1
- package/dist/storage/adapters/typeAwareStorageAdapter.js +272 -43
- package/dist/storage/baseStorage.d.ts +44 -1
- package/dist/storage/baseStorage.js +148 -16
- package/dist/storage/cow/BlobStorage.d.ts +1 -0
- package/dist/storage/cow/BlobStorage.js +5 -3
- package/dist/storage/storageFactory.d.ts +1 -2
- package/dist/storage/storageFactory.js +7 -6
- package/dist/types/brainy.types.d.ts +0 -1
- package/dist/vfs/FSCompat.d.ts +1 -1
- package/dist/vfs/FSCompat.js +1 -1
- package/dist/vfs/VirtualFileSystem.js +5 -6
- package/package.json +1 -1
|
@@ -124,22 +124,41 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
124
124
|
await this.init();
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Lightweight COW enablement - just enables branch-scoped paths
|
|
129
|
+
* Called during init() to ensure all data is stored with branch prefixes from the start
|
|
130
|
+
* RefManager/BlobStorage/CommitLog are lazy-initialized on first fork()
|
|
131
|
+
* @param branch - Branch name to use (default: 'main')
|
|
132
|
+
*/
|
|
133
|
+
enableCOWLightweight(branch = 'main') {
|
|
134
|
+
if (this.cowEnabled) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
this.currentBranch = branch;
|
|
138
|
+
this.cowEnabled = true;
|
|
139
|
+
// RefManager/BlobStorage/CommitLog remain undefined until first fork()
|
|
140
|
+
}
|
|
127
141
|
/**
|
|
128
142
|
* Initialize COW (Copy-on-Write) support
|
|
129
143
|
* Creates RefManager and BlobStorage for instant fork() capability
|
|
130
144
|
*
|
|
145
|
+
* v5.0.1: Now called automatically by storageFactory (zero-config)
|
|
146
|
+
*
|
|
131
147
|
* @param options - COW initialization options
|
|
132
148
|
* @param options.branch - Initial branch name (default: 'main')
|
|
133
149
|
* @param options.enableCompression - Enable zstd compression for blobs (default: true)
|
|
134
150
|
* @returns Promise that resolves when COW is initialized
|
|
135
151
|
*/
|
|
136
152
|
async initializeCOW(options) {
|
|
137
|
-
if (
|
|
138
|
-
|
|
153
|
+
// Check if RefManager already initialized (full COW setup complete)
|
|
154
|
+
if (this.refManager) {
|
|
139
155
|
return;
|
|
140
156
|
}
|
|
141
|
-
//
|
|
142
|
-
this.
|
|
157
|
+
// Enable lightweight COW if not already enabled
|
|
158
|
+
if (!this.cowEnabled) {
|
|
159
|
+
this.currentBranch = options?.branch || 'main';
|
|
160
|
+
this.cowEnabled = true;
|
|
161
|
+
}
|
|
143
162
|
// Create COWStorageAdapter bridge
|
|
144
163
|
// This adapts BaseStorage's methods to the simple key-value interface
|
|
145
164
|
const cowAdapter = {
|
|
@@ -227,6 +246,119 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
227
246
|
}
|
|
228
247
|
this.cowEnabled = true;
|
|
229
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Resolve branch-scoped path for COW isolation
|
|
251
|
+
* @protected - Available to subclasses for COW implementation
|
|
252
|
+
*/
|
|
253
|
+
resolveBranchPath(basePath, branch) {
|
|
254
|
+
if (!this.cowEnabled) {
|
|
255
|
+
return basePath; // COW disabled, use direct path
|
|
256
|
+
}
|
|
257
|
+
const targetBranch = branch || this.currentBranch || 'main';
|
|
258
|
+
// Branch-scoped path: branches/<branch>/<basePath>
|
|
259
|
+
return `branches/${targetBranch}/${basePath}`;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Write object to branch-specific path (COW layer)
|
|
263
|
+
* @protected - Available to subclasses for COW implementation
|
|
264
|
+
*/
|
|
265
|
+
async writeObjectToBranch(path, data, branch) {
|
|
266
|
+
const branchPath = this.resolveBranchPath(path, branch);
|
|
267
|
+
return this.writeObjectToPath(branchPath, data);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Read object with inheritance from parent branches (COW layer)
|
|
271
|
+
* Tries current branch first, then walks commit history
|
|
272
|
+
* @protected - Available to subclasses for COW implementation
|
|
273
|
+
*/
|
|
274
|
+
async readWithInheritance(path, branch) {
|
|
275
|
+
if (!this.cowEnabled) {
|
|
276
|
+
// COW disabled, direct read
|
|
277
|
+
return this.readObjectFromPath(path);
|
|
278
|
+
}
|
|
279
|
+
const targetBranch = branch || this.currentBranch || 'main';
|
|
280
|
+
// Try current branch first
|
|
281
|
+
const branchPath = this.resolveBranchPath(path, targetBranch);
|
|
282
|
+
let data = await this.readObjectFromPath(branchPath);
|
|
283
|
+
if (data !== null) {
|
|
284
|
+
return data; // Found in current branch
|
|
285
|
+
}
|
|
286
|
+
// Not in branch, check if we're on main (no inheritance needed)
|
|
287
|
+
if (targetBranch === 'main') {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
// Not in branch, walk commit history to find in parent
|
|
291
|
+
if (this.refManager && this.commitLog) {
|
|
292
|
+
try {
|
|
293
|
+
const commitHash = await this.refManager.resolveRef(targetBranch);
|
|
294
|
+
if (commitHash) {
|
|
295
|
+
// Walk parent commits until we find the data
|
|
296
|
+
for await (const commit of this.commitLog.walk(commitHash)) {
|
|
297
|
+
// Try reading from parent's branch path
|
|
298
|
+
const parentBranch = commit.metadata?.branch || 'main';
|
|
299
|
+
if (parentBranch === targetBranch)
|
|
300
|
+
continue; // Skip self
|
|
301
|
+
const parentPath = this.resolveBranchPath(path, parentBranch);
|
|
302
|
+
data = await this.readObjectFromPath(parentPath);
|
|
303
|
+
if (data !== null) {
|
|
304
|
+
return data; // Found in ancestor
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
// Commit walk failed, fall back to main
|
|
311
|
+
const mainPath = this.resolveBranchPath(path, 'main');
|
|
312
|
+
return this.readObjectFromPath(mainPath);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Last fallback: try main branch
|
|
316
|
+
const mainPath = this.resolveBranchPath(path, 'main');
|
|
317
|
+
return this.readObjectFromPath(mainPath);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Delete object from branch-specific path (COW layer)
|
|
321
|
+
* @protected - Available to subclasses for COW implementation
|
|
322
|
+
*/
|
|
323
|
+
async deleteObjectFromBranch(path, branch) {
|
|
324
|
+
const branchPath = this.resolveBranchPath(path, branch);
|
|
325
|
+
return this.deleteObjectFromPath(branchPath);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* List objects under path in branch (COW layer)
|
|
329
|
+
* @protected - Available to subclasses for COW implementation
|
|
330
|
+
*/
|
|
331
|
+
async listObjectsInBranch(prefix, branch) {
|
|
332
|
+
const branchPrefix = this.resolveBranchPath(prefix, branch);
|
|
333
|
+
const paths = await this.listObjectsUnderPath(branchPrefix);
|
|
334
|
+
// Remove branch prefix from results
|
|
335
|
+
const targetBranch = branch || this.currentBranch || 'main';
|
|
336
|
+
const prefixToRemove = `branches/${targetBranch}/`;
|
|
337
|
+
return paths.map(p => p.startsWith(prefixToRemove) ? p.substring(prefixToRemove.length) : p);
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* List objects with inheritance (v5.0.1)
|
|
341
|
+
* Lists objects from current branch AND main branch, returns unique paths
|
|
342
|
+
* This enables fork to see parent's data in pagination operations
|
|
343
|
+
*
|
|
344
|
+
* Simplified approach: All branches inherit from main
|
|
345
|
+
*/
|
|
346
|
+
async listObjectsWithInheritance(prefix, branch) {
|
|
347
|
+
if (!this.cowEnabled) {
|
|
348
|
+
return this.listObjectsInBranch(prefix, branch);
|
|
349
|
+
}
|
|
350
|
+
const targetBranch = branch || this.currentBranch || 'main';
|
|
351
|
+
// Collect paths from current branch
|
|
352
|
+
const pathsSet = new Set();
|
|
353
|
+
const currentBranchPaths = await this.listObjectsInBranch(prefix, targetBranch);
|
|
354
|
+
currentBranchPaths.forEach(p => pathsSet.add(p));
|
|
355
|
+
// If not on main, also list from main (all branches inherit from main)
|
|
356
|
+
if (targetBranch !== 'main') {
|
|
357
|
+
const mainPaths = await this.listObjectsInBranch(prefix, 'main');
|
|
358
|
+
mainPaths.forEach(p => pathsSet.add(p));
|
|
359
|
+
}
|
|
360
|
+
return Array.from(pathsSet);
|
|
361
|
+
}
|
|
230
362
|
/**
|
|
231
363
|
* Save a noun to storage (v4.0.0: vector only, metadata saved separately)
|
|
232
364
|
* @param noun Pure HNSW vector data (no metadata)
|
|
@@ -839,7 +971,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
839
971
|
async saveMetadata(id, metadata) {
|
|
840
972
|
await this.ensureInitialized();
|
|
841
973
|
const keyInfo = this.analyzeKey(id, 'system');
|
|
842
|
-
return this.
|
|
974
|
+
return this.writeObjectToBranch(keyInfo.fullPath, metadata);
|
|
843
975
|
}
|
|
844
976
|
/**
|
|
845
977
|
* Get metadata from storage (v4.0.0: now typed)
|
|
@@ -848,7 +980,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
848
980
|
async getMetadata(id) {
|
|
849
981
|
await this.ensureInitialized();
|
|
850
982
|
const keyInfo = this.analyzeKey(id, 'system');
|
|
851
|
-
return this.
|
|
983
|
+
return this.readWithInheritance(keyInfo.fullPath);
|
|
852
984
|
}
|
|
853
985
|
/**
|
|
854
986
|
* Save noun metadata to storage (v4.0.0: now typed)
|
|
@@ -873,10 +1005,10 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
873
1005
|
await this.ensureInitialized();
|
|
874
1006
|
// Determine if this is a new entity by checking if metadata already exists
|
|
875
1007
|
const keyInfo = this.analyzeKey(id, 'noun-metadata');
|
|
876
|
-
const existingMetadata = await this.
|
|
1008
|
+
const existingMetadata = await this.readWithInheritance(keyInfo.fullPath);
|
|
877
1009
|
const isNew = !existingMetadata;
|
|
878
|
-
// Save the metadata
|
|
879
|
-
await this.
|
|
1010
|
+
// Save the metadata (COW-aware - writes to branch-specific path)
|
|
1011
|
+
await this.writeObjectToBranch(keyInfo.fullPath, metadata);
|
|
880
1012
|
// CRITICAL FIX (v4.1.2): Increment count for new entities
|
|
881
1013
|
// This runs AFTER metadata is saved, guaranteeing type information is available
|
|
882
1014
|
// Uses synchronous increment since storage operations are already serialized
|
|
@@ -896,7 +1028,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
896
1028
|
async getNounMetadata(id) {
|
|
897
1029
|
await this.ensureInitialized();
|
|
898
1030
|
const keyInfo = this.analyzeKey(id, 'noun-metadata');
|
|
899
|
-
return this.
|
|
1031
|
+
return this.readWithInheritance(keyInfo.fullPath);
|
|
900
1032
|
}
|
|
901
1033
|
/**
|
|
902
1034
|
* Delete noun metadata from storage
|
|
@@ -905,7 +1037,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
905
1037
|
async deleteNounMetadata(id) {
|
|
906
1038
|
await this.ensureInitialized();
|
|
907
1039
|
const keyInfo = this.analyzeKey(id, 'noun-metadata');
|
|
908
|
-
return this.
|
|
1040
|
+
return this.deleteObjectFromBranch(keyInfo.fullPath);
|
|
909
1041
|
}
|
|
910
1042
|
/**
|
|
911
1043
|
* Save verb metadata to storage (v4.0.0: now typed)
|
|
@@ -931,10 +1063,10 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
931
1063
|
await this.ensureInitialized();
|
|
932
1064
|
// Determine if this is a new verb by checking if metadata already exists
|
|
933
1065
|
const keyInfo = this.analyzeKey(id, 'verb-metadata');
|
|
934
|
-
const existingMetadata = await this.
|
|
1066
|
+
const existingMetadata = await this.readWithInheritance(keyInfo.fullPath);
|
|
935
1067
|
const isNew = !existingMetadata;
|
|
936
|
-
// Save the metadata
|
|
937
|
-
await this.
|
|
1068
|
+
// Save the metadata (COW-aware - writes to branch-specific path)
|
|
1069
|
+
await this.writeObjectToBranch(keyInfo.fullPath, metadata);
|
|
938
1070
|
// CRITICAL FIX (v4.1.2): Increment verb count for new relationships
|
|
939
1071
|
// This runs AFTER metadata is saved
|
|
940
1072
|
// Verb type is now stored in metadata (as of v4.1.2) to avoid loading HNSWVerb
|
|
@@ -955,7 +1087,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
955
1087
|
async getVerbMetadata(id) {
|
|
956
1088
|
await this.ensureInitialized();
|
|
957
1089
|
const keyInfo = this.analyzeKey(id, 'verb-metadata');
|
|
958
|
-
return this.
|
|
1090
|
+
return this.readWithInheritance(keyInfo.fullPath);
|
|
959
1091
|
}
|
|
960
1092
|
/**
|
|
961
1093
|
* Delete verb metadata from storage
|
|
@@ -964,7 +1096,7 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
964
1096
|
async deleteVerbMetadata(id) {
|
|
965
1097
|
await this.ensureInitialized();
|
|
966
1098
|
const keyInfo = this.analyzeKey(id, 'verb-metadata');
|
|
967
|
-
return this.
|
|
1099
|
+
return this.deleteObjectFromBranch(keyInfo.fullPath);
|
|
968
1100
|
}
|
|
969
1101
|
/**
|
|
970
1102
|
* Helper method to convert a Map to a plain object for serialization
|
|
@@ -189,11 +189,13 @@ export class BlobStorage {
|
|
|
189
189
|
finalData = await this.zstdDecompress(data);
|
|
190
190
|
}
|
|
191
191
|
// Verify hash (optional, expensive)
|
|
192
|
-
if (!options.
|
|
192
|
+
if (!options.skipVerification && BlobStorage.hash(finalData) !== hash) {
|
|
193
193
|
throw new Error(`Blob integrity check failed: ${hash}`);
|
|
194
194
|
}
|
|
195
|
-
// Add to cache
|
|
196
|
-
|
|
195
|
+
// Add to cache (only if not skipped)
|
|
196
|
+
if (!options.skipCache) {
|
|
197
|
+
this.addToCache(hash, finalData, metadata);
|
|
198
|
+
}
|
|
197
199
|
return finalData;
|
|
198
200
|
}
|
|
199
201
|
/**
|
|
@@ -292,10 +292,9 @@ export interface StorageOptions {
|
|
|
292
292
|
};
|
|
293
293
|
/**
|
|
294
294
|
* COW (Copy-on-Write) configuration for instant fork() capability
|
|
295
|
-
* v5.0.
|
|
295
|
+
* v5.0.1: COW is now always enabled (automatic, zero-config)
|
|
296
296
|
*/
|
|
297
297
|
branch?: string;
|
|
298
|
-
enableCOW?: boolean;
|
|
299
298
|
enableCompression?: boolean;
|
|
300
299
|
}
|
|
301
300
|
/**
|
|
@@ -39,12 +39,13 @@ async function wrapWithTypeAware(underlying, options, verbose = false) {
|
|
|
39
39
|
underlyingStorage: underlying,
|
|
40
40
|
verbose
|
|
41
41
|
});
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
// v5.0.1: COW will be initialized AFTER storage.init() in Brainy
|
|
43
|
+
// Store COW options for later initialization
|
|
44
|
+
if (typeof wrapped.initializeCOW === 'function') {
|
|
45
|
+
wrapped._cowOptions = {
|
|
46
|
+
branch: options?.branch || 'main',
|
|
47
|
+
enableCompression: options?.enableCompression !== false
|
|
48
|
+
};
|
|
48
49
|
}
|
|
49
50
|
return wrapped;
|
|
50
51
|
}
|
package/dist/vfs/FSCompat.d.ts
CHANGED
package/dist/vfs/FSCompat.js
CHANGED
|
@@ -42,10 +42,9 @@ export class VirtualFileSystem {
|
|
|
42
42
|
return;
|
|
43
43
|
// Merge config with defaults
|
|
44
44
|
this.config = { ...this.getDefaultConfig(), ...config };
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
45
|
+
// v5.0.1: VFS is now auto-initialized during brain.init()
|
|
46
|
+
// Brain is guaranteed to be initialized when this is called
|
|
47
|
+
// Removed brain.init() check to prevent infinite recursion
|
|
49
48
|
// Create or find root entity
|
|
50
49
|
this.rootEntityId = await this.initializeRoot();
|
|
51
50
|
// Initialize projection registry with auto-discovery of built-in projections
|
|
@@ -847,11 +846,11 @@ export class VirtualFileSystem {
|
|
|
847
846
|
throw new VFSError(VFSErrorCode.EINVAL, 'VFS not initialized. Call await vfs.init() before using VFS operations.\n\n' +
|
|
848
847
|
'✅ After brain.import():\n' +
|
|
849
848
|
' await brain.import(file, { vfsPath: "/imports/data" })\n' +
|
|
850
|
-
' const vfs = brain.vfs
|
|
849
|
+
' const vfs = brain.vfs\n' +
|
|
851
850
|
' await vfs.init() // ← Required! Safe to call multiple times\n' +
|
|
852
851
|
' const files = await vfs.readdir("/imports/data")\n\n' +
|
|
853
852
|
'✅ Direct VFS usage:\n' +
|
|
854
|
-
' const vfs = brain.vfs
|
|
853
|
+
' const vfs = brain.vfs\n' +
|
|
855
854
|
' await vfs.init() // ← Always required before first use\n' +
|
|
856
855
|
' await vfs.writeFile("/docs/readme.md", "Hello")\n\n' +
|
|
857
856
|
'📖 Docs: https://github.com/soulcraftlabs/brainy/blob/main/docs/vfs/QUICK_START.md', '<unknown>', 'VFS');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.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",
|