@rljson/bs 0.0.19 → 0.0.20

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/README.public.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <!--
2
2
  @license
3
- Copyright (c) 2025 Rljson
3
+ Copyright (c) 2026 Rljson
4
4
 
5
5
  Use of this source code is governed by terms that can be
6
6
  found in the LICENSE file in the root of this package.
@@ -8,19 +8,20 @@ found in the LICENSE file in the root of this package.
8
8
 
9
9
  # @rljson/bs
10
10
 
11
- Content-addressable blob storage interface and implementations for rljson.
11
+ Content-addressable blob storage interface and implementations for TypeScript/JavaScript.
12
12
 
13
13
  ## Overview
14
14
 
15
- `@rljson/bs` provides a unified interface for blob storage with content-addressable semantics. All blobs are identified by their SHA256 hash, ensuring automatic deduplication and data integrity.
15
+ `@rljson/bs` provides a unified interface for blob storage with content-addressable semantics. All blobs are identified by their SHA256 hash, ensuring automatic deduplication, data integrity verification, and location independence.
16
16
 
17
17
  ### Key Features
18
18
 
19
19
  - **Content-Addressable Storage**: Blobs are identified by SHA256 hash of their content
20
- - **Automatic Deduplication**: Identical content is stored only once
20
+ - **Automatic Deduplication**: Identical content is stored only once across the entire system
21
21
  - **Multiple Implementations**: In-memory, peer-to-peer, server-based, and multi-tier
22
22
  - **Type-Safe**: Full TypeScript support with comprehensive type definitions
23
23
  - **Stream Support**: Efficient handling of large blobs via ReadableStreams
24
+ - **Network Layer**: Built-in peer-to-peer and client-server implementations
24
25
  - **100% Test Coverage**: Fully tested with comprehensive test suite
25
26
 
26
27
  ## Installation
@@ -31,7 +32,7 @@ npm install @rljson/bs
31
32
 
32
33
  ## Quick Start
33
34
 
34
- ### In-Memory Storage
35
+ ### Basic Usage: In-Memory Storage
35
36
 
36
37
  The simplest implementation for testing or temporary storage:
37
38
 
@@ -42,8 +43,9 @@ import { BsMem } from '@rljson/bs';
42
43
  const bs = new BsMem();
43
44
 
44
45
  // Store a blob - returns SHA256 hash as blobId
45
- const { blobId } = await bs.setBlob('Hello, World!');
46
- console.log(blobId); // e.g., "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"
46
+ const { blobId, size } = await bs.setBlob('Hello, World!');
47
+ console.log(blobId); // "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"
48
+ console.log(size); // 13
47
49
 
48
50
  // Retrieve the blob
49
51
  const { content } = await bs.getBlob(blobId);
@@ -53,27 +55,55 @@ console.log(content.toString()); // "Hello, World!"
53
55
  const exists = await bs.blobExists(blobId);
54
56
  console.log(exists); // true
55
57
 
58
+ // Get blob properties without downloading
59
+ const props = await bs.getBlobProperties(blobId);
60
+ console.log(props.createdAt); // Timestamp
61
+
56
62
  // List all blobs
57
63
  const { blobs } = await bs.listBlobs();
58
64
  console.log(blobs.length); // 1
59
65
  ```
60
66
 
67
+ ### Client-Server Architecture
68
+
69
+ Access remote blob storage over a socket connection:
70
+
71
+ ```typescript
72
+ import { BsMem, BsServer, BsPeer, SocketMock } from '@rljson/bs';
73
+
74
+ // Server setup
75
+ const storage = new BsMem();
76
+ const server = new BsServer(storage);
77
+
78
+ // Client setup
79
+ const socket = new SocketMock(); // Use real socket in production
80
+ await server.addSocket(socket);
81
+ const client = new BsPeer(socket);
82
+ await client.init();
83
+
84
+ // Client can now access server storage
85
+ const { blobId } = await client.setBlob('Remote data');
86
+ const { content } = await client.getBlob(blobId);
87
+ console.log(content.toString()); // "Remote data"
88
+
89
+ // Close connection
90
+ await client.close();
91
+ ```
92
+
61
93
  ### Multi-Tier Storage (Cache + Remote)
62
94
 
63
95
  Combine multiple storage backends with automatic caching:
64
96
 
65
97
  ```typescript
66
- import { BsMulti, BsMem, BsPeer, PeerSocketMock } from '@rljson/bs';
67
-
68
- // Setup remote storage (simulated)
69
- const remoteStore = new BsMem();
70
- const remoteSocket = new PeerSocketMock(remoteStore);
71
- const remotePeer = new BsPeer(remoteSocket);
72
- await remotePeer.init();
98
+ import { BsMulti, BsMem, BsPeer } from '@rljson/bs';
73
99
 
74
100
  // Setup local cache
75
101
  const localCache = new BsMem();
76
102
 
103
+ // Setup remote storage (via BsPeer)
104
+ const remotePeer = new BsPeer(remoteSocket);
105
+ await remotePeer.init();
106
+
77
107
  // Create multi-tier storage with cache-first strategy
78
108
  const bs = new BsMulti([
79
109
  { bs: localCache, priority: 0, read: true, write: true }, // Cache first
@@ -84,8 +114,8 @@ await bs.init();
84
114
  // Store blob - writes to cache only (writable stores)
85
115
  const { blobId } = await bs.setBlob('Cached content');
86
116
 
87
- // Read from cache first, falls back to remote
88
- // Automatically hot-swaps remote blobs to cache
117
+ // Read from cache first, falls back to remote if not found
118
+ // Automatically hot-swaps remote blobs to cache for future reads
89
119
  const { content } = await bs.getBlob(blobId);
90
120
  ```
91
121
 
@@ -93,11 +123,7 @@ const { content } = await bs.getBlob(blobId);
93
123
 
94
124
  ### Content-Addressable Storage
95
125
 
96
- Every blob is identified by the SHA256 hash of its content. This means:
97
-
98
- - **Automatic Deduplication**: Storing the same content twice returns the same `blobId`
99
- - **Data Integrity**: The `blobId` serves as a cryptographic checksum
100
- - **Location Independence**: Blobs can be identified and verified anywhere
126
+ Every blob is identified by the SHA256 hash of its content:
101
127
 
102
128
  ```typescript
103
129
  const bs = new BsMem();
@@ -105,133 +131,33 @@ const bs = new BsMem();
105
131
  const result1 = await bs.setBlob('Same content');
106
132
  const result2 = await bs.setBlob('Same content');
107
133
 
108
- // Both return the same blobId
134
+ // Both return the same blobId (automatic deduplication)
109
135
  console.log(result1.blobId === result2.blobId); // true
136
+
137
+ // Different content = different blobId
138
+ const result3 = await bs.setBlob('Different content');
139
+ console.log(result1.blobId !== result3.blobId); // true
110
140
  ```
111
141
 
142
+ **Benefits:**
143
+
144
+ - **Automatic Deduplication**: Identical content stored once, regardless of how many times you call `setBlob`
145
+ - **Data Integrity**: The `blobId` serves as a cryptographic checksum
146
+ - **Location Independence**: Blobs can be identified and verified anywhere
147
+ - **Cache Efficiency**: Content can be cached anywhere and verified by its hash
148
+
112
149
  ### Blob Properties
113
150
 
114
151
  All blobs have associated metadata:
115
152
 
116
153
  ```typescript
117
154
  interface BlobProperties {
118
- blobId: string; // SHA256 hash of content
155
+ blobId: string; // SHA256 hash of content (64 hex characters)
119
156
  size: number; // Size in bytes
120
- contentType: string; // MIME type (default: 'application/octet-stream')
121
157
  createdAt: Date; // Creation timestamp
122
- metadata?: Record<string, string>; // Optional custom metadata
123
158
  }
124
159
  ```
125
160
 
126
- ## Implementations
127
-
128
- ### BsMem - In-Memory Storage
129
-
130
- Fast, ephemeral storage for testing and temporary data:
131
-
132
- ```typescript
133
- import { BsMem } from '@rljson/bs';
134
-
135
- const bs = new BsMem();
136
- const { blobId } = await bs.setBlob('Temporary data');
137
- ```
138
-
139
- **Use Cases:**
140
-
141
- - Unit testing
142
- - Temporary caching
143
- - Development and prototyping
144
-
145
- **Limitations:**
146
-
147
- - Data lost when process ends
148
- - Limited by available RAM
149
-
150
- ### BsPeer - Peer-to-Peer Storage
151
-
152
- Access remote blob storage over a socket connection:
153
-
154
- ```typescript
155
- import { BsPeer, PeerSocketMock } from '@rljson/bs';
156
-
157
- // Create a peer connected to a remote storage
158
- const remoteStorage = new BsMem();
159
- const socket = new PeerSocketMock(remoteStorage);
160
- const peer = new BsPeer(socket);
161
- await peer.init();
162
-
163
- // Use like any other Bs implementation
164
- const { blobId } = await peer.setBlob('Remote data');
165
- const { content } = await peer.getBlob(blobId);
166
-
167
- // Close connection when done
168
- await peer.close();
169
- ```
170
-
171
- **Use Cases:**
172
-
173
- - Distributed systems
174
- - Client-server architectures
175
- - Remote backup
176
-
177
- ### BsServer - Server-Side Handler
178
-
179
- Handle blob storage requests from remote peers:
180
-
181
- ```typescript
182
- import { BsServer, BsMem, SocketMock } from '@rljson/bs';
183
-
184
- // Server-side setup
185
- const storage = new BsMem();
186
- const server = new BsServer(storage);
187
-
188
- // Handle incoming connection
189
- const clientSocket = new SocketMock();
190
- const serverSocket = clientSocket.createPeer();
191
- server.handleConnection(serverSocket);
192
-
193
- // Client can now access storage through clientSocket
194
- ```
195
-
196
- **Use Cases:**
197
-
198
- - Building blob storage services
199
- - Network protocol implementation
200
- - API backends
201
-
202
- ### BsMulti - Multi-Tier Storage
203
-
204
- Combine multiple storage backends with configurable priorities:
205
-
206
- ```typescript
207
- import { BsMulti, BsMem } from '@rljson/bs';
208
-
209
- const fastCache = new BsMem();
210
- const mainStorage = new BsMem();
211
- const backup = new BsMem();
212
-
213
- const bs = new BsMulti([
214
- { bs: fastCache, priority: 0, read: true, write: true }, // L1 cache
215
- { bs: mainStorage, priority: 1, read: true, write: true }, // Main storage
216
- { bs: backup, priority: 2, read: true, write: false }, // Read-only backup
217
- ]);
218
- await bs.init();
219
- ```
220
-
221
- **Features:**
222
-
223
- - **Priority-Based Reads**: Reads from lowest priority number first
224
- - **Hot-Swapping**: Automatically caches blobs from remote to local
225
- - **Parallel Writes**: Writes to all writable stores simultaneously
226
- - **Deduplication**: Merges results from all readable stores
227
-
228
- **Use Cases:**
229
-
230
- - Local cache + remote storage
231
- - Local network storage infrastructure
232
- - Backup and archival systems
233
- - Distributed blob storage across network nodes
234
-
235
161
  ## API Reference
236
162
 
237
163
  ### Bs Interface
@@ -247,10 +173,10 @@ Stores a blob and returns its properties including the SHA256 `blobId`.
247
173
  const { blobId } = await bs.setBlob('Hello');
248
174
 
249
175
  // From Buffer
250
- const buffer = Buffer.from('World');
176
+ const buffer = Buffer.from('World', 'utf8');
251
177
  await bs.setBlob(buffer);
252
178
 
253
- // From ReadableStream
179
+ // From ReadableStream (for large files)
254
180
  const stream = new ReadableStream({
255
181
  start(controller) {
256
182
  controller.enqueue(new TextEncoder().encode('Stream data'));
@@ -297,13 +223,14 @@ Deletes a blob from storage.
297
223
 
298
224
  ```typescript
299
225
  await bs.deleteBlob(blobId);
300
- ```
301
226
 
302
- **Note:** In production systems with content-addressable storage, consider implementing reference counting before deletion.
227
+ // Note: In production with content-addressable storage,
228
+ // consider reference counting before deletion
229
+ ```
303
230
 
304
231
  #### `blobExists(blobId: string): Promise<boolean>`
305
232
 
306
- Checks if a blob exists.
233
+ Checks if a blob exists without downloading it.
307
234
 
308
235
  ```typescript
309
236
  if (await bs.blobExists(blobId)) {
@@ -318,7 +245,7 @@ Gets blob metadata without downloading content.
318
245
  ```typescript
319
246
  const props = await bs.getBlobProperties(blobId);
320
247
  console.log(`Blob size: ${props.size} bytes`);
321
- console.log(`Created: ${props.createdAt}`);
248
+ console.log(`Created: ${props.createdAt.toISOString()}`);
322
249
  ```
323
250
 
324
251
  #### `listBlobs(options?: ListBlobsOptions): Promise<ListBlobsResult>`
@@ -329,7 +256,7 @@ Lists all blobs with optional filtering and pagination.
329
256
  // List all blobs
330
257
  const { blobs } = await bs.listBlobs();
331
258
 
332
- // With prefix filter
259
+ // With prefix filter (blobs starting with "abc")
333
260
  const result = await bs.listBlobs({ prefix: 'abc' });
334
261
 
335
262
  // Paginated listing
@@ -350,56 +277,202 @@ do {
350
277
  Generates a signed URL for temporary access to a blob.
351
278
 
352
279
  ```typescript
353
- // Read-only URL valid for 1 hour
280
+ // Read-only URL valid for 1 hour (3600 seconds)
354
281
  const url = await bs.generateSignedUrl(blobId, 3600);
355
282
 
356
- // Delete permission URL
283
+ // Delete permission URL valid for 5 minutes
357
284
  const deleteUrl = await bs.generateSignedUrl(blobId, 300, 'delete');
358
285
  ```
359
286
 
360
- ## Advanced Usage
287
+ ## Implementations
361
288
 
362
- ### Custom Storage Implementation
289
+ ### BsMem - In-Memory Storage
363
290
 
364
- Implement the `Bs` interface to create custom storage backends:
291
+ Fast, ephemeral storage for testing and temporary data.
365
292
 
366
293
  ```typescript
367
- import { Bs, BlobProperties } from '@rljson/bs';
294
+ import { BsMem } from '@rljson/bs';
368
295
 
369
- class MyCustomStorage implements Bs {
370
- async setBlob(content: Buffer | string | ReadableStream): Promise<BlobProperties> {
371
- // Your implementation
372
- }
296
+ const bs = new BsMem();
297
+ const { blobId } = await bs.setBlob('Temporary data');
298
+ ```
373
299
 
374
- async getBlob(blobId: string) {
375
- // Your implementation
376
- }
300
+ **Use Cases:**
377
301
 
378
- // Implement other methods...
379
- }
302
+ - Unit testing
303
+ - Temporary caching
304
+ - Development and prototyping
305
+ - Fast local storage for small datasets
306
+
307
+ **Limitations:**
308
+
309
+ - Data lost when process ends
310
+ - Limited by available RAM
311
+ - Single-process only
312
+
313
+ ### BsPeer - Peer-to-Peer Storage Client
314
+
315
+ Access remote blob storage over a socket connection.
316
+
317
+ ```typescript
318
+ import { BsPeer } from '@rljson/bs';
319
+
320
+ // Create a peer connected to a remote storage
321
+ const peer = new BsPeer(socket);
322
+ await peer.init();
323
+
324
+ // Use like any other Bs implementation
325
+ const { blobId } = await peer.setBlob('Remote data');
326
+ const { content } = await peer.getBlob(blobId);
327
+
328
+ // Close connection when done
329
+ await peer.close();
330
+ ```
331
+
332
+ **Use Cases:**
333
+
334
+ - Client-server architectures
335
+ - Distributed systems
336
+ - Remote backup
337
+ - Accessing centralized storage
338
+
339
+ **Features:**
340
+
341
+ - Async socket-based communication
342
+ - Error-first callback pattern (Node.js style)
343
+ - Connection state management
344
+ - Automatic retry support
345
+
346
+ ### BsServer - Server-Side Handler
347
+
348
+ Handle blob storage requests from remote peers.
349
+
350
+ ```typescript
351
+ import { BsServer, BsMem, SocketMock } from '@rljson/bs';
352
+
353
+ // Server-side setup
354
+ const storage = new BsMem();
355
+ const server = new BsServer(storage);
356
+
357
+ // Handle incoming connection
358
+ const clientSocket = new SocketMock(); // Use real socket in production
359
+ await server.addSocket(clientSocket);
360
+
361
+ // Client can now access storage through socket
362
+ ```
363
+
364
+ **Use Cases:**
365
+
366
+ - Building blob storage services
367
+ - Network protocol implementation
368
+ - API backends
369
+ - Multi-client storage systems
370
+
371
+ **Features:**
372
+
373
+ - Multiple client support
374
+ - Socket lifecycle management
375
+ - Automatic method mapping
376
+ - Error handling
377
+
378
+ ### BsPeerBridge - PULL Architecture Bridge (Read-Only)
379
+
380
+ Exposes local blob storage for server to PULL from (read-only access).
381
+
382
+ ```typescript
383
+ import { BsPeerBridge, BsMem } from '@rljson/bs';
384
+
385
+ // Client-side: expose local storage for server to read
386
+ const localStorage = new BsMem();
387
+ const bridge = new BsPeerBridge(localStorage, socket);
388
+ bridge.start();
389
+
390
+ // Server can now read from client's local storage
391
+ // but CANNOT write to it (PULL architecture)
392
+ ```
393
+
394
+ **Architecture Pattern:**
395
+
396
+ - **PULL-only**: Server can read from client, but cannot write
397
+ - **Read Operations Only**: `getBlob`, `getBlobStream`, `blobExists`, `getBlobProperties`, `listBlobs`
398
+ - **No Write Operations**: Does not expose `setBlob`, `deleteBlob`, or `generateSignedUrl`
399
+
400
+ **Use Cases:**
401
+
402
+ - Client exposes local cache for server to access
403
+ - Distributed storage where server pulls from clients
404
+ - Peer-to-peer networks with read-only sharing
405
+
406
+ ### BsMulti - Multi-Tier Storage
407
+
408
+ Combine multiple storage backends with configurable priorities.
409
+
410
+ ```typescript
411
+ import { BsMulti, BsMem } from '@rljson/bs';
412
+
413
+ const fastCache = new BsMem();
414
+ const mainStorage = new BsMem();
415
+ const backup = new BsMem();
416
+
417
+ const bs = new BsMulti([
418
+ { bs: fastCache, priority: 0, read: true, write: true }, // L1 cache
419
+ { bs: mainStorage, priority: 1, read: true, write: true }, // Main storage
420
+ { bs: backup, priority: 2, read: true, write: false }, // Read-only backup
421
+ ]);
422
+ await bs.init();
380
423
  ```
381
424
 
382
- ### Multi-Tier Patterns
425
+ **Features:**
426
+
427
+ - **Priority-Based Reads**: Reads from lowest priority number first (0 = highest priority)
428
+ - **Hot-Swapping**: Automatically caches blobs from remote to local on read
429
+ - **Parallel Writes**: Writes to all writable stores simultaneously
430
+ - **Deduplication**: Merges results from all readable stores when listing
431
+ - **Graceful Fallback**: If highest priority fails, falls back to next priority
383
432
 
384
- **Local Cache + Remote Storage:**
433
+ **Use Cases:**
434
+
435
+ - Local cache + remote storage
436
+ - Multi-region storage replication
437
+ - Local network storage infrastructure
438
+ - Backup and archival systems
439
+ - Hierarchical storage management (HSM)
440
+
441
+ ## Common Patterns
442
+
443
+ ### Local Cache + Remote Storage (PULL Architecture)
385
444
 
386
445
  ```typescript
446
+ const localCache = new BsMem();
447
+ const remotePeer = new BsPeer(remoteSocket);
448
+ await remotePeer.init();
449
+
387
450
  const bs = new BsMulti([
388
451
  { bs: localCache, priority: 0, read: true, write: true },
389
- { bs: remoteStorage, priority: 1, read: true, write: false },
452
+ { bs: remotePeer, priority: 1, read: true, write: false }, // Read-only
390
453
  ]);
454
+
455
+ // Writes go to cache only
456
+ await bs.setBlob('data');
457
+
458
+ // Reads check cache first, then remote
459
+ // Remote blobs are automatically cached
460
+ const { content } = await bs.getBlob(blobId);
391
461
  ```
392
462
 
393
- **Write-Through Cache:**
463
+ ### Write-Through Cache
394
464
 
395
465
  ```typescript
396
466
  const bs = new BsMulti([
397
467
  { bs: localCache, priority: 0, read: true, write: true },
398
468
  { bs: remoteStorage, priority: 1, read: true, write: true }, // Also writable
399
469
  ]);
470
+
471
+ // Writes go to both cache and remote simultaneously
472
+ await bs.setBlob('data');
400
473
  ```
401
474
 
402
- **Multi-Region Replication:**
475
+ ### Multi-Region Replication
403
476
 
404
477
  ```typescript
405
478
  const bs = new BsMulti([
@@ -407,9 +480,32 @@ const bs = new BsMulti([
407
480
  { bs: regionEu, priority: 1, read: true, write: true },
408
481
  { bs: regionAsia, priority: 2, read: true, write: true },
409
482
  ]);
483
+
484
+ // Writes replicate to all regions
485
+ // Reads come from fastest responding region
486
+ ```
487
+
488
+ ### Client-Server with BsPeerBridge (PULL Pattern)
489
+
490
+ ```typescript
491
+ // Client setup
492
+ const clientStorage = new BsMem();
493
+ const bridge = new BsPeerBridge(clientStorage, socketToServer);
494
+ bridge.start(); // Exposes read-only access to server
495
+
496
+ const bsPeer = new BsPeer(socketToServer);
497
+ await bsPeer.init();
498
+
499
+ const clientBs = new BsMulti([
500
+ { bs: clientStorage, priority: 0, read: true, write: true }, // Local storage
501
+ { bs: bsPeer, priority: 1, read: true, write: false }, // Server (read-only)
502
+ ]);
503
+
504
+ // Server can pull from client via bridge
505
+ // Client can pull from server via bsPeer
410
506
  ```
411
507
 
412
- ### Error Handling
508
+ ## Error Handling
413
509
 
414
510
  All methods throw errors for invalid operations:
415
511
 
@@ -436,6 +532,7 @@ The package includes comprehensive test utilities:
436
532
 
437
533
  ```typescript
438
534
  import { BsMem } from '@rljson/bs';
535
+ import { describe, it, expect, beforeEach } from 'vitest';
439
536
 
440
537
  describe('My Tests', () => {
441
538
  let bs: BsMem;
@@ -449,6 +546,12 @@ describe('My Tests', () => {
449
546
  const { content } = await bs.getBlob(blobId);
450
547
  expect(content.toString()).toBe('test data');
451
548
  });
549
+
550
+ it('should deduplicate identical content', async () => {
551
+ const result1 = await bs.setBlob('same');
552
+ const result2 = await bs.setBlob('same');
553
+ expect(result1.blobId).toBe(result2.blobId);
554
+ });
452
555
  });
453
556
  ```
454
557
 
@@ -458,21 +561,72 @@ describe('My Tests', () => {
458
561
 
459
562
  - `BsMem` stores all data in RAM - suitable for small to medium datasets
460
563
  - Use streams (`getBlobStream`) for large blobs to avoid loading entire content into memory
461
- - `BsMulti` with local cache reduces network overhead
564
+ - `BsMulti` with local cache reduces network overhead significantly
462
565
 
463
566
  ### Network Efficiency
464
567
 
465
568
  - Use `BsPeer` for remote access with minimal protocol overhead
466
- - `BsMulti` automatically caches frequently accessed blobs
467
- - Content-addressable nature prevents redundant transfers
569
+ - `BsMulti` automatically caches frequently accessed blobs locally
570
+ - Content-addressable nature prevents redundant transfers (same content = same hash)
571
+ - Hot-swapping in `BsMulti` reduces repeated network requests
468
572
 
469
- ### Deduplication
573
+ ### Deduplication Benefits
470
574
 
471
575
  - Identical content stored multiple times occupies space only once
472
576
  - Particularly effective for:
473
- - Version control systems
474
- - Backup solutions
475
- - Build artifact storage
577
+ - Version control systems (many files unchanged between versions)
578
+ - Backup solutions (incremental backups with deduplication)
579
+ - Build artifact storage (shared dependencies)
580
+ - Document management (attachments, templates)
581
+
582
+ ## Migration Guide
583
+
584
+ ### From Traditional Blob Storage
585
+
586
+ Traditional blob storage typically uses arbitrary identifiers:
587
+
588
+ ```typescript
589
+ // Traditional
590
+ await blobStore.put('my-file-id', content);
591
+ const data = await blobStore.get('my-file-id');
592
+ ```
593
+
594
+ With content-addressable storage, the ID is derived from content:
595
+
596
+ ```typescript
597
+ // Content-addressable
598
+ const { blobId } = await bs.setBlob(content); // blobId = SHA256(content)
599
+ const { content } = await bs.getBlob(blobId);
600
+ ```
601
+
602
+ **Key Differences:**
603
+
604
+ 1. **No custom IDs**: You cannot choose blob IDs, they are computed
605
+ 2. **Automatic deduplication**: Same content = same ID
606
+ 3. **Verify on read**: You can verify content integrity by recomputing the hash
607
+ 4. **External metadata**: Store file names, tags, etc. separately (e.g., in @rljson/io)
608
+
609
+ ## Frequently Asked Questions
610
+
611
+ ### Q: How do I organize blobs into folders or containers?
612
+
613
+ A: The Bs interface provides a flat storage pool. Organizational metadata (folders, tags, file names) should be stored separately, such as in a database or using `@rljson/io` (data table storage). Reference blobs by their `blobId`.
614
+
615
+ ### Q: What happens if I delete a blob that's referenced elsewhere?
616
+
617
+ A: The blob is permanently deleted. In production systems with shared blobs, implement reference counting before deletion.
618
+
619
+ ### Q: Can I use this in the browser?
620
+
621
+ A: Yes, but you'll need to provide your own Socket implementation for network communication, or use `BsMem` for local-only storage.
622
+
623
+ ### Q: How does BsMulti handle write conflicts?
624
+
625
+ A: `BsMulti` writes to all writable stores in parallel. If any write fails, the error is thrown. All writable stores will have the blob since content is identical (content-addressable).
626
+
627
+ ### Q: Why is BsPeerBridge read-only?
628
+
629
+ A: BsPeerBridge implements the PULL architecture pattern, where the server can read from client storage but cannot modify it. This prevents the server from pushing unwanted data to clients. Use BsPeer for client-to-server writes.
476
630
 
477
631
  ## License
478
632
 
@@ -485,3 +639,4 @@ Issues and pull requests welcome at <https://github.com/rljson/bs>
485
639
  ## Related Packages
486
640
 
487
641
  - `@rljson/io` - Data table storage interface and implementations
642
+ - `@rljson/hash` - Cryptographic hashing utilities