@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.
Files changed (44) hide show
  1. package/CHANGELOG.md +271 -0
  2. package/README.md +38 -1
  3. package/dist/augmentations/brainyAugmentation.d.ts +76 -0
  4. package/dist/augmentations/brainyAugmentation.js +126 -0
  5. package/dist/augmentations/cacheAugmentation.js +9 -4
  6. package/dist/brainy.d.ts +248 -15
  7. package/dist/brainy.js +707 -17
  8. package/dist/cli/commands/cow.d.ts +60 -0
  9. package/dist/cli/commands/cow.js +444 -0
  10. package/dist/cli/commands/import.js +1 -1
  11. package/dist/cli/commands/vfs.js +24 -40
  12. package/dist/cli/index.js +50 -0
  13. package/dist/hnsw/hnswIndex.d.ts +41 -0
  14. package/dist/hnsw/hnswIndex.js +96 -1
  15. package/dist/hnsw/typeAwareHNSWIndex.d.ts +9 -0
  16. package/dist/hnsw/typeAwareHNSWIndex.js +22 -0
  17. package/dist/import/ImportHistory.js +3 -3
  18. package/dist/importers/VFSStructureGenerator.d.ts +1 -1
  19. package/dist/importers/VFSStructureGenerator.js +3 -3
  20. package/dist/index.d.ts +6 -0
  21. package/dist/index.js +10 -0
  22. package/dist/storage/adapters/memoryStorage.d.ts +6 -0
  23. package/dist/storage/adapters/memoryStorage.js +39 -14
  24. package/dist/storage/adapters/typeAwareStorageAdapter.d.ts +31 -1
  25. package/dist/storage/adapters/typeAwareStorageAdapter.js +272 -43
  26. package/dist/storage/baseStorage.d.ts +64 -0
  27. package/dist/storage/baseStorage.js +252 -12
  28. package/dist/storage/cow/BlobStorage.d.ts +232 -0
  29. package/dist/storage/cow/BlobStorage.js +437 -0
  30. package/dist/storage/cow/CommitLog.d.ts +199 -0
  31. package/dist/storage/cow/CommitLog.js +363 -0
  32. package/dist/storage/cow/CommitObject.d.ts +276 -0
  33. package/dist/storage/cow/CommitObject.js +431 -0
  34. package/dist/storage/cow/RefManager.d.ts +213 -0
  35. package/dist/storage/cow/RefManager.js +409 -0
  36. package/dist/storage/cow/TreeObject.d.ts +177 -0
  37. package/dist/storage/cow/TreeObject.js +293 -0
  38. package/dist/storage/storageFactory.d.ts +6 -0
  39. package/dist/storage/storageFactory.js +92 -74
  40. package/dist/types/brainy.types.d.ts +1 -0
  41. package/dist/vfs/FSCompat.d.ts +1 -1
  42. package/dist/vfs/FSCompat.js +1 -1
  43. package/dist/vfs/VirtualFileSystem.js +5 -6
  44. 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