@soulcraft/brainy 4.11.2 → 5.1.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.
- package/CHANGELOG.md +271 -0
- package/README.md +38 -1
- package/dist/augmentations/brainyAugmentation.d.ts +76 -0
- package/dist/augmentations/brainyAugmentation.js +126 -0
- package/dist/augmentations/cacheAugmentation.js +9 -4
- package/dist/brainy.d.ts +248 -15
- package/dist/brainy.js +707 -17
- package/dist/cli/commands/cow.d.ts +60 -0
- package/dist/cli/commands/cow.js +444 -0
- package/dist/cli/commands/import.js +1 -1
- package/dist/cli/commands/vfs.js +24 -40
- package/dist/cli/index.js +50 -0
- package/dist/hnsw/hnswIndex.d.ts +41 -0
- package/dist/hnsw/hnswIndex.js +96 -1
- package/dist/hnsw/typeAwareHNSWIndex.d.ts +9 -0
- package/dist/hnsw/typeAwareHNSWIndex.js +22 -0
- package/dist/import/ImportHistory.js +3 -3
- package/dist/importers/VFSStructureGenerator.d.ts +1 -1
- package/dist/importers/VFSStructureGenerator.js +3 -3
- package/dist/index.d.ts +6 -0
- package/dist/index.js +10 -0
- 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 +64 -0
- package/dist/storage/baseStorage.js +252 -12
- package/dist/storage/cow/BlobStorage.d.ts +232 -0
- package/dist/storage/cow/BlobStorage.js +437 -0
- package/dist/storage/cow/CommitLog.d.ts +199 -0
- package/dist/storage/cow/CommitLog.js +363 -0
- package/dist/storage/cow/CommitObject.d.ts +276 -0
- package/dist/storage/cow/CommitObject.js +431 -0
- package/dist/storage/cow/RefManager.d.ts +213 -0
- package/dist/storage/cow/RefManager.js +409 -0
- package/dist/storage/cow/TreeObject.d.ts +177 -0
- package/dist/storage/cow/TreeObject.js +293 -0
- package/dist/storage/storageFactory.d.ts +6 -0
- package/dist/storage/storageFactory.js +92 -74
- package/dist/types/brainy.types.d.ts +1 -0
- 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
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TreeObject: Directory structure for COW (Copy-on-Write)
|
|
3
|
+
*
|
|
4
|
+
* Similar to Git trees, represents the structure of a Brainy instance at a point in time.
|
|
5
|
+
* Trees contain entries mapping names to blob hashes.
|
|
6
|
+
*
|
|
7
|
+
* Structure:
|
|
8
|
+
* - entities/ → tree hash (entity blobs)
|
|
9
|
+
* - indexes/nouns → blob hash (HNSW noun index)
|
|
10
|
+
* - indexes/metadata → blob hash (metadata index)
|
|
11
|
+
* - indexes/graph → blob hash (graph adjacency index)
|
|
12
|
+
* - indexes/deleted → blob hash (deleted items index)
|
|
13
|
+
*
|
|
14
|
+
* @module storage/cow/TreeObject
|
|
15
|
+
*/
|
|
16
|
+
import { BlobStorage } from './BlobStorage.js';
|
|
17
|
+
/**
|
|
18
|
+
* TreeBuilder: Fluent API for building tree objects
|
|
19
|
+
*
|
|
20
|
+
* Example:
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const tree = await TreeBuilder.create(blobStorage)
|
|
23
|
+
* .addBlob('entities/abc123', entityHash, size)
|
|
24
|
+
* .addBlob('indexes/nouns', nounsHash, size)
|
|
25
|
+
* .build()
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class TreeBuilder {
|
|
29
|
+
constructor(blobStorage) {
|
|
30
|
+
this.entries = [];
|
|
31
|
+
this.blobStorage = blobStorage;
|
|
32
|
+
}
|
|
33
|
+
static create(blobStorage) {
|
|
34
|
+
return new TreeBuilder(blobStorage);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Add a blob entry to the tree
|
|
38
|
+
*
|
|
39
|
+
* @param name - Entry name (e.g., 'entities/abc123')
|
|
40
|
+
* @param hash - Blob hash
|
|
41
|
+
* @param size - Original blob size
|
|
42
|
+
*/
|
|
43
|
+
addBlob(name, hash, size) {
|
|
44
|
+
this.entries.push({
|
|
45
|
+
name,
|
|
46
|
+
hash,
|
|
47
|
+
type: 'blob',
|
|
48
|
+
size
|
|
49
|
+
});
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Add a subtree entry to the tree
|
|
54
|
+
*
|
|
55
|
+
* @param name - Subtree name (e.g., 'entities/')
|
|
56
|
+
* @param treeHash - Tree hash
|
|
57
|
+
* @param size - Total size of subtree
|
|
58
|
+
*/
|
|
59
|
+
addTree(name, treeHash, size) {
|
|
60
|
+
this.entries.push({
|
|
61
|
+
name,
|
|
62
|
+
hash: treeHash,
|
|
63
|
+
type: 'tree',
|
|
64
|
+
size
|
|
65
|
+
});
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build and persist the tree object
|
|
70
|
+
*
|
|
71
|
+
* @returns Tree hash
|
|
72
|
+
*/
|
|
73
|
+
async build() {
|
|
74
|
+
const tree = {
|
|
75
|
+
entries: this.entries.sort((a, b) => a.name.localeCompare(b.name)),
|
|
76
|
+
createdAt: Date.now()
|
|
77
|
+
};
|
|
78
|
+
return TreeObject.write(this.blobStorage, tree);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* TreeObject: Represents directory structure in COW storage
|
|
83
|
+
*/
|
|
84
|
+
export class TreeObject {
|
|
85
|
+
/**
|
|
86
|
+
* Serialize tree object to Buffer
|
|
87
|
+
*
|
|
88
|
+
* Format: JSON (simple, debuggable)
|
|
89
|
+
* Future: Could use protobuf for efficiency
|
|
90
|
+
*
|
|
91
|
+
* @param tree - Tree object
|
|
92
|
+
* @returns Serialized tree
|
|
93
|
+
*/
|
|
94
|
+
static serialize(tree) {
|
|
95
|
+
return Buffer.from(JSON.stringify(tree, null, 0)); // Compact JSON
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Deserialize tree object from Buffer
|
|
99
|
+
*
|
|
100
|
+
* @param data - Serialized tree
|
|
101
|
+
* @returns Tree object
|
|
102
|
+
*/
|
|
103
|
+
static deserialize(data) {
|
|
104
|
+
const tree = JSON.parse(data.toString());
|
|
105
|
+
// Validate structure
|
|
106
|
+
if (!tree.entries || !Array.isArray(tree.entries)) {
|
|
107
|
+
throw new Error('Invalid tree object: missing entries array');
|
|
108
|
+
}
|
|
109
|
+
if (typeof tree.createdAt !== 'number') {
|
|
110
|
+
throw new Error('Invalid tree object: missing or invalid createdAt');
|
|
111
|
+
}
|
|
112
|
+
// Validate entries
|
|
113
|
+
for (const entry of tree.entries) {
|
|
114
|
+
if (typeof entry.name !== 'string') {
|
|
115
|
+
throw new Error(`Invalid tree entry: name must be string`);
|
|
116
|
+
}
|
|
117
|
+
if (typeof entry.hash !== 'string' || entry.hash.length !== 64) {
|
|
118
|
+
throw new Error(`Invalid tree entry: hash must be 64-char SHA-256`);
|
|
119
|
+
}
|
|
120
|
+
if (entry.type !== 'blob' && entry.type !== 'tree') {
|
|
121
|
+
throw new Error(`Invalid tree entry: type must be 'blob' or 'tree'`);
|
|
122
|
+
}
|
|
123
|
+
if (typeof entry.size !== 'number' || entry.size < 0) {
|
|
124
|
+
throw new Error(`Invalid tree entry: size must be non-negative number`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return tree;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Compute hash of tree object
|
|
131
|
+
*
|
|
132
|
+
* @param tree - Tree object
|
|
133
|
+
* @returns SHA-256 hash
|
|
134
|
+
*/
|
|
135
|
+
static hash(tree) {
|
|
136
|
+
const data = TreeObject.serialize(tree);
|
|
137
|
+
return BlobStorage.hash(data);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Write tree object to blob storage
|
|
141
|
+
*
|
|
142
|
+
* @param blobStorage - Blob storage instance
|
|
143
|
+
* @param tree - Tree object
|
|
144
|
+
* @returns Tree hash
|
|
145
|
+
*/
|
|
146
|
+
static async write(blobStorage, tree) {
|
|
147
|
+
const data = TreeObject.serialize(tree);
|
|
148
|
+
return blobStorage.write(data, { type: 'tree', compression: 'auto' });
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Read tree object from blob storage
|
|
152
|
+
*
|
|
153
|
+
* @param blobStorage - Blob storage instance
|
|
154
|
+
* @param hash - Tree hash
|
|
155
|
+
* @returns Tree object
|
|
156
|
+
*/
|
|
157
|
+
static async read(blobStorage, hash) {
|
|
158
|
+
const data = await blobStorage.read(hash);
|
|
159
|
+
return TreeObject.deserialize(data);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get specific entry from tree
|
|
163
|
+
*
|
|
164
|
+
* @param tree - Tree object
|
|
165
|
+
* @param name - Entry name
|
|
166
|
+
* @returns Tree entry or undefined
|
|
167
|
+
*/
|
|
168
|
+
static getEntry(tree, name) {
|
|
169
|
+
return tree.entries.find(e => e.name === name);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get all blob entries from tree (non-recursive)
|
|
173
|
+
*
|
|
174
|
+
* @param tree - Tree object
|
|
175
|
+
* @returns Array of blob entries
|
|
176
|
+
*/
|
|
177
|
+
static getBlobs(tree) {
|
|
178
|
+
return tree.entries.filter(e => e.type === 'blob');
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Get all subtree entries from tree (non-recursive)
|
|
182
|
+
*
|
|
183
|
+
* @param tree - Tree object
|
|
184
|
+
* @returns Array of tree entries
|
|
185
|
+
*/
|
|
186
|
+
static getSubtrees(tree) {
|
|
187
|
+
return tree.entries.filter(e => e.type === 'tree');
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Walk tree recursively, yielding all blob entries
|
|
191
|
+
*
|
|
192
|
+
* @param blobStorage - Blob storage instance
|
|
193
|
+
* @param tree - Tree object
|
|
194
|
+
*/
|
|
195
|
+
static async *walk(blobStorage, tree) {
|
|
196
|
+
for (const entry of tree.entries) {
|
|
197
|
+
if (entry.type === 'blob') {
|
|
198
|
+
yield entry;
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// Recurse into subtree
|
|
202
|
+
const subtree = await TreeObject.read(blobStorage, entry.hash);
|
|
203
|
+
yield* TreeObject.walk(blobStorage, subtree);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Compute total size of tree (recursive)
|
|
209
|
+
*
|
|
210
|
+
* @param blobStorage - Blob storage instance
|
|
211
|
+
* @param tree - Tree object
|
|
212
|
+
* @returns Total size in bytes
|
|
213
|
+
*/
|
|
214
|
+
static async getTotalSize(blobStorage, tree) {
|
|
215
|
+
let totalSize = 0;
|
|
216
|
+
for await (const entry of TreeObject.walk(blobStorage, tree)) {
|
|
217
|
+
totalSize += entry.size;
|
|
218
|
+
}
|
|
219
|
+
return totalSize;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Create a new tree by updating a single entry
|
|
223
|
+
* (Copy-on-write: creates new tree, doesn't modify original)
|
|
224
|
+
*
|
|
225
|
+
* @param blobStorage - Blob storage instance
|
|
226
|
+
* @param tree - Original tree
|
|
227
|
+
* @param name - Entry name to update
|
|
228
|
+
* @param hash - New blob/tree hash
|
|
229
|
+
* @param size - New size
|
|
230
|
+
* @returns New tree hash
|
|
231
|
+
*/
|
|
232
|
+
static async updateEntry(blobStorage, tree, name, hash, size) {
|
|
233
|
+
const builder = TreeBuilder.create(blobStorage);
|
|
234
|
+
// Copy all entries except the one being updated
|
|
235
|
+
for (const entry of tree.entries) {
|
|
236
|
+
if (entry.name === name) {
|
|
237
|
+
// Replace with new entry
|
|
238
|
+
if (entry.type === 'blob') {
|
|
239
|
+
builder.addBlob(name, hash, size);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
builder.addTree(name, hash, size);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
// Keep existing entry
|
|
247
|
+
if (entry.type === 'blob') {
|
|
248
|
+
builder.addBlob(entry.name, entry.hash, entry.size);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
builder.addTree(entry.name, entry.hash, entry.size);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// If entry didn't exist, add it
|
|
256
|
+
if (!TreeObject.getEntry(tree, name)) {
|
|
257
|
+
builder.addBlob(name, hash, size);
|
|
258
|
+
}
|
|
259
|
+
return builder.build();
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Diff two trees, return changed/added/deleted entries
|
|
263
|
+
*
|
|
264
|
+
* @param tree1 - First tree (base)
|
|
265
|
+
* @param tree2 - Second tree (comparison)
|
|
266
|
+
* @returns Diff result
|
|
267
|
+
*/
|
|
268
|
+
static diff(tree1, tree2) {
|
|
269
|
+
const entries1 = new Map(tree1.entries.map(e => [e.name, e]));
|
|
270
|
+
const entries2 = new Map(tree2.entries.map(e => [e.name, e]));
|
|
271
|
+
const added = [];
|
|
272
|
+
const modified = [];
|
|
273
|
+
const deleted = [];
|
|
274
|
+
// Find added and modified
|
|
275
|
+
for (const [name, entry2] of entries2) {
|
|
276
|
+
const entry1 = entries1.get(name);
|
|
277
|
+
if (!entry1) {
|
|
278
|
+
added.push(entry2);
|
|
279
|
+
}
|
|
280
|
+
else if (entry1.hash !== entry2.hash) {
|
|
281
|
+
modified.push(entry2);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Find deleted
|
|
285
|
+
for (const [name, entry1] of entries1) {
|
|
286
|
+
if (!entries2.has(name)) {
|
|
287
|
+
deleted.push(entry1);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return { added, modified, deleted };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=TreeObject.js.map
|
|
@@ -290,6 +290,12 @@ export interface StorageOptions {
|
|
|
290
290
|
*/
|
|
291
291
|
readOnly?: boolean;
|
|
292
292
|
};
|
|
293
|
+
/**
|
|
294
|
+
* COW (Copy-on-Write) configuration for instant fork() capability
|
|
295
|
+
* v5.0.1: COW is now always enabled (automatic, zero-config)
|
|
296
|
+
*/
|
|
297
|
+
branch?: string;
|
|
298
|
+
enableCompression?: boolean;
|
|
293
299
|
}
|
|
294
300
|
/**
|
|
295
301
|
* Create a storage adapter based on the environment and configuration
|