@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
@@ -7,6 +7,9 @@ import { BaseStorageAdapter } from './adapters/baseStorageAdapter.js';
7
7
  import { validateNounType, validateVerbType } from '../utils/typeValidation.js';
8
8
  import { NounType } from '../types/graphTypes.js';
9
9
  import { getShardIdFromUuid } from './sharding.js';
10
+ import { RefManager } from './cow/RefManager.js';
11
+ import { BlobStorage } from './cow/BlobStorage.js';
12
+ import { CommitLog } from './cow/CommitLog.js';
10
13
  // Clean directory structure (v4.7.2+)
11
14
  // All storage adapters use this consistent structure
12
15
  export const NOUNS_METADATA_DIR = 'entities/nouns/metadata';
@@ -38,6 +41,8 @@ export class BaseStorage extends BaseStorageAdapter {
38
41
  super(...arguments);
39
42
  this.isInitialized = false;
40
43
  this.readOnly = false;
44
+ this.currentBranch = 'main';
45
+ this.cowEnabled = false;
41
46
  }
42
47
  /**
43
48
  * Analyze a storage key to determine its routing and path
@@ -119,6 +124,241 @@ export class BaseStorage extends BaseStorageAdapter {
119
124
  await this.init();
120
125
  }
121
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
+ }
141
+ /**
142
+ * Initialize COW (Copy-on-Write) support
143
+ * Creates RefManager and BlobStorage for instant fork() capability
144
+ *
145
+ * v5.0.1: Now called automatically by storageFactory (zero-config)
146
+ *
147
+ * @param options - COW initialization options
148
+ * @param options.branch - Initial branch name (default: 'main')
149
+ * @param options.enableCompression - Enable zstd compression for blobs (default: true)
150
+ * @returns Promise that resolves when COW is initialized
151
+ */
152
+ async initializeCOW(options) {
153
+ // Check if RefManager already initialized (full COW setup complete)
154
+ if (this.refManager) {
155
+ return;
156
+ }
157
+ // Enable lightweight COW if not already enabled
158
+ if (!this.cowEnabled) {
159
+ this.currentBranch = options?.branch || 'main';
160
+ this.cowEnabled = true;
161
+ }
162
+ // Create COWStorageAdapter bridge
163
+ // This adapts BaseStorage's methods to the simple key-value interface
164
+ const cowAdapter = {
165
+ get: async (key) => {
166
+ try {
167
+ const data = await this.readObjectFromPath(`_cow/${key}`);
168
+ if (data === null) {
169
+ return undefined;
170
+ }
171
+ // Convert to Buffer
172
+ if (Buffer.isBuffer(data)) {
173
+ return data;
174
+ }
175
+ return Buffer.from(JSON.stringify(data));
176
+ }
177
+ catch (error) {
178
+ return undefined;
179
+ }
180
+ },
181
+ put: async (key, data) => {
182
+ // Store as Buffer (for blob data) or parse JSON (for metadata)
183
+ let obj;
184
+ try {
185
+ // Try to parse as JSON first (for metadata)
186
+ obj = JSON.parse(data.toString());
187
+ }
188
+ catch {
189
+ // Not JSON, store as binary (base64 encoded for JSON storage)
190
+ obj = { _binary: true, data: data.toString('base64') };
191
+ }
192
+ await this.writeObjectToPath(`_cow/${key}`, obj);
193
+ },
194
+ delete: async (key) => {
195
+ try {
196
+ await this.deleteObjectFromPath(`_cow/${key}`);
197
+ }
198
+ catch (error) {
199
+ // Ignore if doesn't exist
200
+ }
201
+ },
202
+ list: async (prefix) => {
203
+ try {
204
+ const paths = await this.listObjectsUnderPath(`_cow/${prefix}`);
205
+ // Remove _cow/ prefix and return relative keys
206
+ return paths.map(p => p.replace(/^_cow\//, ''));
207
+ }
208
+ catch (error) {
209
+ return [];
210
+ }
211
+ }
212
+ };
213
+ // Initialize RefManager
214
+ this.refManager = new RefManager(cowAdapter);
215
+ // Initialize BlobStorage
216
+ this.blobStorage = new BlobStorage(cowAdapter, {
217
+ enableCompression: options?.enableCompression !== false
218
+ });
219
+ // Initialize CommitLog
220
+ this.commitLog = new CommitLog(this.blobStorage, this.refManager);
221
+ // Check if main branch exists, create if not
222
+ const mainRef = await this.refManager.getRef('main');
223
+ if (!mainRef) {
224
+ // Create initial commit (empty tree)
225
+ const emptyTreeHash = '0000000000000000000000000000000000000000000000000000000000000000';
226
+ await this.refManager.createBranch('main', emptyTreeHash, {
227
+ description: 'Initial branch',
228
+ author: 'system'
229
+ });
230
+ }
231
+ // Set HEAD to current branch
232
+ const currentRef = await this.refManager.getRef(this.currentBranch);
233
+ if (currentRef) {
234
+ await this.refManager.setHead(this.currentBranch);
235
+ }
236
+ else {
237
+ // Branch doesn't exist, create it from main
238
+ const mainCommit = await this.refManager.resolveRef('main');
239
+ if (mainCommit) {
240
+ await this.refManager.createBranch(this.currentBranch, mainCommit, {
241
+ description: `Branch created from main`,
242
+ author: 'system'
243
+ });
244
+ await this.refManager.setHead(this.currentBranch);
245
+ }
246
+ }
247
+ this.cowEnabled = true;
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
+ }
122
362
  /**
123
363
  * Save a noun to storage (v4.0.0: vector only, metadata saved separately)
124
364
  * @param noun Pure HNSW vector data (no metadata)
@@ -731,7 +971,7 @@ export class BaseStorage extends BaseStorageAdapter {
731
971
  async saveMetadata(id, metadata) {
732
972
  await this.ensureInitialized();
733
973
  const keyInfo = this.analyzeKey(id, 'system');
734
- return this.writeObjectToPath(keyInfo.fullPath, metadata);
974
+ return this.writeObjectToBranch(keyInfo.fullPath, metadata);
735
975
  }
736
976
  /**
737
977
  * Get metadata from storage (v4.0.0: now typed)
@@ -740,7 +980,7 @@ export class BaseStorage extends BaseStorageAdapter {
740
980
  async getMetadata(id) {
741
981
  await this.ensureInitialized();
742
982
  const keyInfo = this.analyzeKey(id, 'system');
743
- return this.readObjectFromPath(keyInfo.fullPath);
983
+ return this.readWithInheritance(keyInfo.fullPath);
744
984
  }
745
985
  /**
746
986
  * Save noun metadata to storage (v4.0.0: now typed)
@@ -765,10 +1005,10 @@ export class BaseStorage extends BaseStorageAdapter {
765
1005
  await this.ensureInitialized();
766
1006
  // Determine if this is a new entity by checking if metadata already exists
767
1007
  const keyInfo = this.analyzeKey(id, 'noun-metadata');
768
- const existingMetadata = await this.readObjectFromPath(keyInfo.fullPath);
1008
+ const existingMetadata = await this.readWithInheritance(keyInfo.fullPath);
769
1009
  const isNew = !existingMetadata;
770
- // Save the metadata
771
- await this.writeObjectToPath(keyInfo.fullPath, metadata);
1010
+ // Save the metadata (COW-aware - writes to branch-specific path)
1011
+ await this.writeObjectToBranch(keyInfo.fullPath, metadata);
772
1012
  // CRITICAL FIX (v4.1.2): Increment count for new entities
773
1013
  // This runs AFTER metadata is saved, guaranteeing type information is available
774
1014
  // Uses synchronous increment since storage operations are already serialized
@@ -788,7 +1028,7 @@ export class BaseStorage extends BaseStorageAdapter {
788
1028
  async getNounMetadata(id) {
789
1029
  await this.ensureInitialized();
790
1030
  const keyInfo = this.analyzeKey(id, 'noun-metadata');
791
- return this.readObjectFromPath(keyInfo.fullPath);
1031
+ return this.readWithInheritance(keyInfo.fullPath);
792
1032
  }
793
1033
  /**
794
1034
  * Delete noun metadata from storage
@@ -797,7 +1037,7 @@ export class BaseStorage extends BaseStorageAdapter {
797
1037
  async deleteNounMetadata(id) {
798
1038
  await this.ensureInitialized();
799
1039
  const keyInfo = this.analyzeKey(id, 'noun-metadata');
800
- return this.deleteObjectFromPath(keyInfo.fullPath);
1040
+ return this.deleteObjectFromBranch(keyInfo.fullPath);
801
1041
  }
802
1042
  /**
803
1043
  * Save verb metadata to storage (v4.0.0: now typed)
@@ -823,10 +1063,10 @@ export class BaseStorage extends BaseStorageAdapter {
823
1063
  await this.ensureInitialized();
824
1064
  // Determine if this is a new verb by checking if metadata already exists
825
1065
  const keyInfo = this.analyzeKey(id, 'verb-metadata');
826
- const existingMetadata = await this.readObjectFromPath(keyInfo.fullPath);
1066
+ const existingMetadata = await this.readWithInheritance(keyInfo.fullPath);
827
1067
  const isNew = !existingMetadata;
828
- // Save the metadata
829
- await this.writeObjectToPath(keyInfo.fullPath, metadata);
1068
+ // Save the metadata (COW-aware - writes to branch-specific path)
1069
+ await this.writeObjectToBranch(keyInfo.fullPath, metadata);
830
1070
  // CRITICAL FIX (v4.1.2): Increment verb count for new relationships
831
1071
  // This runs AFTER metadata is saved
832
1072
  // Verb type is now stored in metadata (as of v4.1.2) to avoid loading HNSWVerb
@@ -847,7 +1087,7 @@ export class BaseStorage extends BaseStorageAdapter {
847
1087
  async getVerbMetadata(id) {
848
1088
  await this.ensureInitialized();
849
1089
  const keyInfo = this.analyzeKey(id, 'verb-metadata');
850
- return this.readObjectFromPath(keyInfo.fullPath);
1090
+ return this.readWithInheritance(keyInfo.fullPath);
851
1091
  }
852
1092
  /**
853
1093
  * Delete verb metadata from storage
@@ -856,7 +1096,7 @@ export class BaseStorage extends BaseStorageAdapter {
856
1096
  async deleteVerbMetadata(id) {
857
1097
  await this.ensureInitialized();
858
1098
  const keyInfo = this.analyzeKey(id, 'verb-metadata');
859
- return this.deleteObjectFromPath(keyInfo.fullPath);
1099
+ return this.deleteObjectFromBranch(keyInfo.fullPath);
860
1100
  }
861
1101
  /**
862
1102
  * Helper method to convert a Map to a plain object for serialization
@@ -0,0 +1,232 @@
1
+ /**
2
+ * BlobStorage: Content-Addressable Blob Storage for COW (Copy-on-Write)
3
+ *
4
+ * State-of-the-art implementation featuring:
5
+ * - Content-addressable: SHA-256 hashing
6
+ * - Type-aware chunking: Separate vectors, metadata, relationships
7
+ * - Compression: zstd for JSON, optimized for vectors
8
+ * - LRU caching: Hot blob performance
9
+ * - Streaming: Multipart upload for large blobs
10
+ * - Batch operations: Parallel I/O
11
+ * - Integrity: Cryptographic verification
12
+ * - Observability: Metrics and tracing
13
+ *
14
+ * @module storage/cow/BlobStorage
15
+ */
16
+ /**
17
+ * Simple key-value storage interface for COW primitives
18
+ * This will be implemented by BaseStorage when COW is integrated
19
+ */
20
+ export interface COWStorageAdapter {
21
+ get(key: string): Promise<Buffer | undefined>;
22
+ put(key: string, data: Buffer): Promise<void>;
23
+ delete(key: string): Promise<void>;
24
+ list(prefix: string): Promise<string[]>;
25
+ }
26
+ /**
27
+ * Blob metadata stored alongside blob data
28
+ */
29
+ export interface BlobMetadata {
30
+ hash: string;
31
+ size: number;
32
+ compressedSize: number;
33
+ compression: 'none' | 'zstd';
34
+ type: 'vector' | 'metadata' | 'tree' | 'commit' | 'raw';
35
+ createdAt: number;
36
+ refCount: number;
37
+ }
38
+ /**
39
+ * Blob write options
40
+ */
41
+ export interface BlobWriteOptions {
42
+ compression?: 'none' | 'zstd' | 'auto';
43
+ type?: 'vector' | 'metadata' | 'tree' | 'commit' | 'raw';
44
+ skipVerification?: boolean;
45
+ }
46
+ /**
47
+ * Blob read options
48
+ */
49
+ export interface BlobReadOptions {
50
+ skipDecompression?: boolean;
51
+ skipCache?: boolean;
52
+ skipVerification?: boolean;
53
+ }
54
+ /**
55
+ * Blob statistics for observability
56
+ */
57
+ export interface BlobStats {
58
+ totalBlobs: number;
59
+ totalSize: number;
60
+ compressedSize: number;
61
+ cacheHits: number;
62
+ cacheMisses: number;
63
+ compressionRatio: number;
64
+ avgBlobSize: number;
65
+ dedupSavings: number;
66
+ }
67
+ /**
68
+ * State-of-the-art content-addressable blob storage
69
+ *
70
+ * Features:
71
+ * - Content addressing via SHA-256
72
+ * - Type-aware compression (zstd, vector-optimized)
73
+ * - LRU caching with memory limits
74
+ * - Streaming for large blobs
75
+ * - Batch operations
76
+ * - Integrity verification
77
+ * - Observability metrics
78
+ */
79
+ export declare class BlobStorage {
80
+ private adapter;
81
+ private cache;
82
+ private cacheMaxSize;
83
+ private currentCacheSize;
84
+ private stats;
85
+ private zstdCompress?;
86
+ private zstdDecompress?;
87
+ private readonly CACHE_MAX_SIZE;
88
+ private readonly MULTIPART_THRESHOLD;
89
+ private readonly COMPRESSION_THRESHOLD;
90
+ constructor(adapter: COWStorageAdapter, options?: {
91
+ cacheMaxSize?: number;
92
+ enableCompression?: boolean;
93
+ });
94
+ /**
95
+ * Lazy load zstd compression module
96
+ * (Avoids loading if not needed)
97
+ */
98
+ private initCompression;
99
+ /**
100
+ * Compute SHA-256 hash of data
101
+ *
102
+ * @param data - Data to hash
103
+ * @returns SHA-256 hash as hex string
104
+ */
105
+ static hash(data: Buffer): string;
106
+ /**
107
+ * Write a blob to storage
108
+ *
109
+ * Features:
110
+ * - Content-addressable: hash determines storage key
111
+ * - Deduplication: existing blob not rewritten
112
+ * - Compression: auto-compress based on type
113
+ * - Multipart: for large blobs (>5MB)
114
+ * - Verification: hash verification
115
+ * - Caching: write-through cache
116
+ *
117
+ * @param data - Blob data to write
118
+ * @param options - Write options
119
+ * @returns Blob hash
120
+ */
121
+ write(data: Buffer, options?: BlobWriteOptions): Promise<string>;
122
+ /**
123
+ * Read a blob from storage
124
+ *
125
+ * Features:
126
+ * - Cache lookup first (LRU)
127
+ * - Decompression (if compressed)
128
+ * - Verification (optional hash check)
129
+ * - Streaming for large blobs
130
+ *
131
+ * @param hash - Blob hash
132
+ * @param options - Read options
133
+ * @returns Blob data
134
+ */
135
+ read(hash: string, options?: BlobReadOptions): Promise<Buffer>;
136
+ /**
137
+ * Check if blob exists
138
+ *
139
+ * @param hash - Blob hash
140
+ * @returns True if blob exists
141
+ */
142
+ has(hash: string): Promise<boolean>;
143
+ /**
144
+ * Delete a blob from storage
145
+ *
146
+ * Features:
147
+ * - Reference counting: only delete if refCount = 0
148
+ * - Cascade: delete metadata too
149
+ * - Cache invalidation
150
+ *
151
+ * @param hash - Blob hash
152
+ */
153
+ delete(hash: string): Promise<void>;
154
+ /**
155
+ * Get blob metadata without reading full blob
156
+ *
157
+ * @param hash - Blob hash
158
+ * @returns Blob metadata
159
+ */
160
+ getMetadata(hash: string): Promise<BlobMetadata | undefined>;
161
+ /**
162
+ * Batch write multiple blobs in parallel
163
+ *
164
+ * @param blobs - Array of [data, options] tuples
165
+ * @returns Array of blob hashes
166
+ */
167
+ writeBatch(blobs: Array<[Buffer, BlobWriteOptions?]>): Promise<string[]>;
168
+ /**
169
+ * Batch read multiple blobs in parallel
170
+ *
171
+ * @param hashes - Array of blob hashes
172
+ * @param options - Read options
173
+ * @returns Array of blob data
174
+ */
175
+ readBatch(hashes: string[], options?: BlobReadOptions): Promise<Buffer[]>;
176
+ /**
177
+ * List all blobs (for garbage collection, debugging)
178
+ *
179
+ * @returns Array of blob hashes
180
+ */
181
+ listBlobs(): Promise<string[]>;
182
+ /**
183
+ * Get storage statistics
184
+ *
185
+ * @returns Blob statistics
186
+ */
187
+ getStats(): BlobStats;
188
+ /**
189
+ * Clear cache (useful for testing, memory pressure)
190
+ */
191
+ clearCache(): void;
192
+ /**
193
+ * Garbage collect unreferenced blobs
194
+ *
195
+ * @param referencedHashes - Set of hashes that should be kept
196
+ * @returns Number of blobs deleted
197
+ */
198
+ garbageCollect(referencedHashes: Set<string>): Promise<number>;
199
+ /**
200
+ * Select compression strategy based on data and options
201
+ */
202
+ private selectCompression;
203
+ /**
204
+ * Write large blob using multipart upload
205
+ * (Future enhancement: stream to adapter if supported)
206
+ */
207
+ private writeMultipart;
208
+ /**
209
+ * Increment reference count for a blob
210
+ */
211
+ private incrementRefCount;
212
+ /**
213
+ * Decrement reference count for a blob
214
+ */
215
+ private decrementRefCount;
216
+ /**
217
+ * Add blob to LRU cache
218
+ */
219
+ private addToCache;
220
+ /**
221
+ * Get blob from cache
222
+ */
223
+ private getFromCache;
224
+ /**
225
+ * Remove blob from cache
226
+ */
227
+ private removeFromCache;
228
+ /**
229
+ * Evict least recently used entry from cache
230
+ */
231
+ private evictLRU;
232
+ }