@rljson/db 0.0.11 → 0.0.13

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,7 +1,946 @@
1
- # @rljson/
1
+ # @rljson/db
2
2
 
3
- Todo: Add description here
3
+ A high-level TypeScript database abstraction for content-addressed, immutable RLJSON data. Provides intuitive querying with native support for hierarchical structures, version history, and pluggable storage backends.
4
4
 
5
- ## Example
5
+ ## Features
6
6
 
7
- [src/example.ts](src/example.ts)
7
+ - **Content-Addressed:** All data identified by SHA-based hashes
8
+ - **Immutable:** Changes create new versions automatically
9
+ - **Hierarchical:** First-class tree structures with smart expansion
10
+ - **Type-Safe:** Full TypeScript support
11
+ - **Time-Travel:** Query historical versions
12
+ - **Storage-Agnostic:** Memory, file, or network backends
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @rljson/db @rljson/io @rljson/rljson
18
+ ```
19
+
20
+ **Requirements:** Node.js >= 22.14.0
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { Db } from '@rljson/db';
26
+ import { IoMem } from '@rljson/io';
27
+ import { Route, createComponentsTableCfg } from '@rljson/rljson';
28
+
29
+ // Initialize database with in-memory storage
30
+ const io = new IoMem();
31
+ await io.init();
32
+ const db = new Db(io);
33
+
34
+ // Create a table
35
+ const tableCfg = createComponentsTableCfg('users', [
36
+ { key: 'name', type: 'string' },
37
+ { key: 'email', type: 'string' }
38
+ ]);
39
+ await db.core.createTableWithInsertHistory(tableCfg);
40
+
41
+ // Import data
42
+ await db.core.import({
43
+ users: {
44
+ _type: 'components',
45
+ _data: [
46
+ { name: 'Alice', email: 'alice@example.com', _hash: '...' }
47
+ ]
48
+ }
49
+ });
50
+
51
+ // Query data
52
+ const route = Route.fromFlat('users');
53
+ const result = await db.get(route, {});
54
+ console.log(result.rljson.users._data);
55
+ ```
56
+
57
+ ## Core Concepts
58
+
59
+ ### Content-Addressed Data
60
+
61
+ All data in @rljson/db is identified by its **content hash** (SHA-256 based). This means:
62
+
63
+ - Identical data always has the same hash
64
+ - Changes to data produce a new hash
65
+ - Perfect caching: same hash = same data
66
+ - Deduplication: store each unique value once
67
+
68
+ ```typescript
69
+ import { hsh } from '@rljson/hash';
70
+
71
+ const data = { name: 'Alice', age: 30 };
72
+ const hash = hsh(data)._hash;
73
+ // 'abc123...' - deterministic hash based on content
74
+ ```
75
+
76
+ ### Immutability
77
+
78
+ Data is **never modified in place**. All mutations create new versions:
79
+
80
+ - INSERT creates new data with new hash
81
+ - Old versions remain accessible
82
+ - Time-travel queries via insert history
83
+ - No update conflicts
84
+
85
+ ### Routes
86
+
87
+ Routes define paths through related data. They use a flat string syntax:
88
+
89
+ ```
90
+ /tableName # Root table
91
+ /tableName@hash # Specific row by hash
92
+ /tableName@timeId # Historic version
93
+ /tableName/childTable # Relationship traversal
94
+ /tableName@hash/childTable@hash2 # Nested navigation
95
+ ```
96
+
97
+ Example:
98
+
99
+ ```typescript
100
+ // Simple route
101
+ Route.fromFlat('users')
102
+
103
+ // With hash reference
104
+ Route.fromFlat('users@abc123')
105
+
106
+ // Nested relationship
107
+ Route.fromFlat('projects/tasks')
108
+
109
+ // Complex navigation
110
+ Route.fromFlat('projects@hash1/tasks@hash2')
111
+ ```
112
+
113
+ ## Data Types
114
+
115
+ ### 1. Components
116
+
117
+ Flat tables with arbitrary columns. Similar to traditional database tables.
118
+
119
+ ```typescript
120
+ type Component = {
121
+ [column: string]: JsonValue;
122
+ _hash: string;
123
+ };
124
+ ```
125
+
126
+ **Use Cases:**
127
+
128
+ - User records
129
+ - Configuration data
130
+ - Event logs
131
+ - Any flat, record-based data
132
+
133
+ **Example:**
134
+
135
+ ```typescript
136
+ const component = {
137
+ name: 'Alice',
138
+ email: 'alice@example.com',
139
+ role: 'admin',
140
+ _hash: '...'
141
+ };
142
+ ```
143
+
144
+ ### 2. Trees
145
+
146
+ Hierarchical structures where each node has:
147
+
148
+ - `id`: Node identifier
149
+ - `children`: Array of child hash references
150
+ - `meta`: Node metadata
151
+ - `isParent`: Boolean flag for parent nodes
152
+ - `_hash`: Content hash
153
+
154
+ ```typescript
155
+ type Tree = {
156
+ id: string;
157
+ children: string[]; // Hash references
158
+ meta: Json;
159
+ isParent: boolean;
160
+ _hash: string;
161
+ };
162
+ ```
163
+
164
+ **Use Cases:**
165
+
166
+ - File systems
167
+ - Organizational hierarchies
168
+ - Menu structures
169
+ - DOM-like structures
170
+
171
+ **Important:** Tree queries behave differently based on context:
172
+
173
+ - **WHERE clause** (`{ _hash: ... }`): Returns single node only (prevents infinite recursion)
174
+ - **Route navigation**: Expands children recursively
175
+
176
+ **Example:**
177
+
178
+ ```typescript
179
+ const tree = {
180
+ id: 'root',
181
+ children: ['childHash1', 'childHash2'],
182
+ meta: { name: 'Root Node' },
183
+ isParent: true,
184
+ _hash: '...'
185
+ };
186
+
187
+ // Single node query (efficient)
188
+ await db.get(route, { _hash: tree._hash });
189
+
190
+ // Expanded navigation (recursive)
191
+ await db.get(Route.fromFlat(`tree@${tree._hash}`));
192
+ ```
193
+
194
+ ### 3. Cakes
195
+
196
+ Multi-dimensional data cubes with slicing capabilities.
197
+
198
+ ```typescript
199
+ type Cake = {
200
+ sliceIdsTable: string;
201
+ sliceIdsRow: string;
202
+ layers: Record<string, string>; // layerTable -> layerRef
203
+ _hash: string;
204
+ };
205
+ ```
206
+
207
+ **Use Cases:**
208
+
209
+ - Multi-environment configurations
210
+ - A/B testing variants
211
+ - Feature flags
212
+ - Dimensional data analysis
213
+
214
+ **Example:**
215
+
216
+ ```typescript
217
+ const cake = {
218
+ sliceIdsTable: 'environments',
219
+ sliceIdsRow: 'production',
220
+ layers: {
221
+ configLayer: 'layerHash1',
222
+ settingsLayer: 'layerHash2'
223
+ },
224
+ _hash: '...'
225
+ };
226
+ ```
227
+
228
+ ### 4. Layers
229
+
230
+ Composable data layers with inheritance and dimension filtering.
231
+
232
+ ```typescript
233
+ type Layer = {
234
+ base?: string; // Base layer hash
235
+ sliceIdsTable: string;
236
+ sliceIdsTableRow: string;
237
+ componentsTable: string;
238
+ _hash: string;
239
+ };
240
+ ```
241
+
242
+ **Use Cases:**
243
+
244
+ - Configuration inheritance
245
+ - Environment-specific settings
246
+ - Progressive overrides
247
+ - Template-based data
248
+
249
+ **Example:**
250
+
251
+ ```typescript
252
+ const layer = {
253
+ base: 'baseLayerHash',
254
+ sliceIdsTable: 'environments',
255
+ sliceIdsTableRow: 'staging',
256
+ componentsTable: 'settings',
257
+ _hash: '...'
258
+ };
259
+ ```
260
+
261
+ ### 5. SliceIds
262
+
263
+ Dimension identifiers for cakes and layers. Enable multi-dimensional data organization.
264
+
265
+ ```typescript
266
+ type SliceId = string; // e.g., 'production', 'staging', 'featureA'
267
+ ```
268
+
269
+ ## Querying Data
270
+
271
+ ### Basic Queries
272
+
273
+ ```typescript
274
+ // Get all records from a table
275
+ const route = Route.fromFlat('users');
276
+ const result = await db.get(route, {});
277
+ console.log(result.rljson.users._data); // All users
278
+
279
+ // Query by hash
280
+ const result = await db.get(route, { _hash: 'specific-hash' });
281
+ console.log(result.rljson.users._data[0]); // Single user
282
+
283
+ // Query by field values
284
+ const result = await db.get(route, { role: 'admin' });
285
+ console.log(result.rljson.users._data); // All admin users
286
+ ```
287
+
288
+ ### Nested Queries
289
+
290
+ ```typescript
291
+ // Query with relationships
292
+ const route = Route.fromFlat('projects/tasks');
293
+ const result = await db.get(route, {
294
+ projects: { status: 'active' },
295
+ tasks: { priority: 'high' }
296
+ });
297
+
298
+ // Result contains both projects and their related tasks
299
+ console.log(result.rljson.projects);
300
+ console.log(result.rljson.tasks);
301
+ ```
302
+
303
+ ### Tree Queries
304
+
305
+ **Critical:** Tree behavior depends on query context.
306
+
307
+ ```typescript
308
+ // ✅ WHERE clause - Returns ONLY requested node
309
+ // Use this for querying specific nodes without expansion
310
+ const nodeRoute = Route.fromFlat('fileSystem');
311
+ const nodeResult = await db.get(nodeRoute, { _hash: nodeHash });
312
+ // Returns: { fileSystem: { _data: [singleNode] } }
313
+ // No children expanded - prevents heap crashes on large trees
314
+
315
+ // ✅ Route navigation - Expands children recursively
316
+ // Use this for traversing tree structures
317
+ const treeRoute = Route.fromFlat(`fileSystem@${rootHash}`);
318
+ const treeResult = await db.get(treeRoute);
319
+ // Returns: Complete tree with all children expanded
320
+ // Access via treeResult.tree for hierarchical view
321
+ ```
322
+
323
+ ### Result Container
324
+
325
+ All queries return a `Container` object with three views of the data:
326
+
327
+ ```typescript
328
+ type Container = {
329
+ rljson: Rljson; // Table data indexed by table name
330
+ tree: Json; // Hierarchical representation
331
+ cell: Cell[]; // Path-value pairs for modifications
332
+ };
333
+
334
+ type Cell = {
335
+ route: Route;
336
+ value: JsonValue;
337
+ row: JsonValue;
338
+ path: Array<Array<string | number>>;
339
+ };
340
+ ```
341
+
342
+ **Example:**
343
+
344
+ ```typescript
345
+ const { rljson, tree, cell } = await db.get(route, where);
346
+
347
+ // RLJSON view - Table-oriented
348
+ console.log(rljson.users._data);
349
+
350
+ // Tree view - Hierarchical
351
+ console.log(tree.users[0].name);
352
+
353
+ // Cell view - Path-based for mutations
354
+ console.log(cell[0].path); // ['users', 0, 'name']
355
+ console.log(cell[0].value); // 'Alice'
356
+ ```
357
+
358
+ ## Inserting Data
359
+
360
+ ### Using isolate() and inject()
361
+
362
+ The recommended pattern for data modifications:
363
+
364
+ ```typescript
365
+ import { isolate, inject } from '@rljson/db';
366
+
367
+ // 1. Query existing data
368
+ const route = Route.fromFlat('users/profile/settings');
369
+ const { tree, cell } = await db.get(route, {});
370
+
371
+ // 2. Isolate specific path
372
+ const path = cell[0].path;
373
+ const isolated = isolate(tree, path);
374
+ // Returns only the data at the specified path
375
+
376
+ // 3. Modify isolated data
377
+ isolated.profile.settings.theme = 'dark';
378
+ isolated.profile.settings.notifications = true;
379
+
380
+ // 4. Inject changes back
381
+ inject(isolated, path, {
382
+ theme: 'dark',
383
+ notifications: true
384
+ });
385
+
386
+ // 5. Insert into database
387
+ const results = await db.insert(route, isolated);
388
+ console.log(results); // InsertHistoryRow[]
389
+ ```
390
+
391
+ ### Direct Import
392
+
393
+ For bulk data loading:
394
+
395
+ ```typescript
396
+ await db.core.import({
397
+ users: {
398
+ _type: 'components',
399
+ _data: [
400
+ { name: 'Alice', email: 'alice@example.com', _hash: 'hash1' },
401
+ { name: 'Bob', email: 'bob@example.com', _hash: 'hash2' }
402
+ ]
403
+ }
404
+ });
405
+ ```
406
+
407
+ ### Tree INSERT
408
+
409
+ **Important:** When inserting tree data that has already been isolated, use the fixed behavior:
410
+
411
+ ```typescript
412
+ // The treeFromObject function now has skipRootCreation parameter
413
+ // This is handled internally by db.insert() - no action needed
414
+
415
+ const treeData = isolate(existingTree, path);
416
+ // Modify treeData...
417
+
418
+ // INSERT automatically uses skipRootCreation=true internally
419
+ await db.insert(route, treeData);
420
+ ```
421
+
422
+ **Why this matters:** The `treeFromObject` function creates an explicit root node. When inserting already-isolated subtrees, this created a double-root structure causing navigation failures. The fix in v0.0.12+ handles this automatically.
423
+
424
+ ## Version History
425
+
426
+ Every insert creates a history entry with timestamps and references:
427
+
428
+ ```typescript
429
+ // Get insert history for a table
430
+ const history = await db.getInsertHistory('users', {
431
+ sorted: true,
432
+ ascending: false // Most recent first
433
+ });
434
+
435
+ console.log(history.usersInsertHistory._data);
436
+ // [
437
+ // {
438
+ // timeId: 'ABC123:20260126T150000Z',
439
+ // usersRef: 'hash-of-inserted-data',
440
+ // route: '/users',
441
+ // previous: ['previous-timeId'],
442
+ // ...
443
+ // }
444
+ // ]
445
+ ```
446
+
447
+ ### Time-Travel Queries
448
+
449
+ Query historical versions using timeIds:
450
+
451
+ ```typescript
452
+ // Query specific version by timeId
453
+ const route = Route.fromFlat(`users@ABC123:20260126T150000Z`);
454
+ const historicData = await db.get(route);
455
+ console.log(historicData.rljson.users._data); // Data as it was at that time
456
+
457
+ // Get all timeIds for a specific hash
458
+ const timeIds = await db.getTimeIdsForRef('users', 'hash1');
459
+ console.log(timeIds); // ['timeId1', 'timeId2', ...]
460
+
461
+ // Get data hash for a specific timeId
462
+ const ref = await db.getRefOfTimeId('users', 'ABC123:20260126T150000Z');
463
+ console.log(ref); // 'hash1'
464
+ ```
465
+
466
+ ## Advanced Features
467
+
468
+ ### Join System
469
+
470
+ The Join system provides SQL-like operations on query results:
471
+
472
+ ```typescript
473
+ import { Join, ColumnSelection, RowFilter } from '@rljson/db';
474
+
475
+ // Create a join from query results
476
+ const { rljson, tree, cell } = await db.get(route, where);
477
+ const join = new Join({ rljson, tree, cell });
478
+
479
+ // Apply column selection
480
+ const selection = new ColumnSelection(
481
+ route,
482
+ [
483
+ { key: 'name', type: 'include' },
484
+ { key: 'email', type: 'include' }
485
+ ]
486
+ );
487
+ join.select(selection);
488
+
489
+ // Apply row filtering
490
+ const filter = new RowFilter(
491
+ route,
492
+ {
493
+ age: { gt: 25 } // Greater than 25
494
+ }
495
+ );
496
+ join.filter(filter);
497
+
498
+ // Apply sorting
499
+ const sort = new RowSort(
500
+ route,
501
+ [{ key: 'name', direction: 'asc' }]
502
+ );
503
+ join.sort(sort);
504
+
505
+ // Get transformed results
506
+ const resultRows = await join.rows();
507
+ console.log(resultRows);
508
+ ```
509
+
510
+ ### Multi-Edit Operations
511
+
512
+ Transactional editing with rollback support:
513
+
514
+ ```typescript
515
+ import { MultiEditManager } from '@rljson/db';
516
+
517
+ const manager = new MultiEditManager('configCake', db);
518
+ await manager.init();
519
+
520
+ // Perform multiple edits as a transaction
521
+ await manager.multiEdit(async (head) => {
522
+ // Edit 1: Update column selection
523
+ await head.edit({
524
+ type: 'columnSelection',
525
+ params: { columns: ['name', 'email'] }
526
+ });
527
+
528
+ // Edit 2: Apply filter
529
+ await head.edit({
530
+ type: 'rowFilter',
531
+ params: { age: { gt: 18 } }
532
+ });
533
+
534
+ return head;
535
+ });
536
+
537
+ // Publish changes (commits transaction)
538
+ const published = await manager.publishHead();
539
+ console.log(published);
540
+ ```
541
+
542
+ ### Real-Time Notifications
543
+
544
+ Register callbacks for data changes:
545
+
546
+ ```typescript
547
+ // Register callback
548
+ db.notify.registerCallback('myCallback', async (insertHistoryRow) => {
549
+ console.log('Data changed:', insertHistoryRow);
550
+ // Perform side effects (cache invalidation, UI updates, etc.)
551
+ });
552
+
553
+ // Perform operations - callback fires automatically
554
+ await db.insert(route, data);
555
+
556
+ // Unregister when done
557
+ db.notify.unregisterCallback('myCallback');
558
+
559
+ // Or unregister all callbacks for a route
560
+ db.notify.unregisterAll(route);
561
+ ```
562
+
563
+ ### Caching
564
+
565
+ The database automatically caches query results based on query signatures:
566
+
567
+ ```typescript
568
+ // Check cache size
569
+ console.log(`Cache size: ${db.cache.size}`);
570
+
571
+ // Clear cache manually
572
+ db.clearCache();
573
+
574
+ // Caching is automatic when:
575
+ // - Route contains hash references (@hash)
576
+ // - Filters are applied
577
+ // - SliceIds are specified
578
+ ```
579
+
580
+ ## Best Practices
581
+
582
+ ### 1. Content-Addressed Design
583
+
584
+ All data is immutable - never modify in place:
585
+
586
+ ```typescript
587
+ // ❌ WRONG - Modifying data directly
588
+ const { rljson } = await db.get(route, {});
589
+ rljson.users._data[0].name = 'Changed'; // This won't persist!
590
+
591
+ // ✅ CORRECT - Use isolate/inject pattern
592
+ const { tree, cell } = await db.get(route, {});
593
+ const isolated = isolate(tree, cell[0].path);
594
+ isolated.name = 'Changed';
595
+ await db.insert(route, isolated); // Creates new version
596
+ ```
597
+
598
+ ### 2. Tree Query Optimization
599
+
600
+ When querying trees by hash, use WHERE clauses for single nodes:
601
+
602
+ ```typescript
603
+ // ✅ Efficient - Returns single node only
604
+ await db.get(Route.fromFlat('tree'), { _hash: nodeHash });
605
+
606
+ // ❌ Avoid for large trees - Expands ALL children recursively
607
+ // Only use for navigation when you need the full tree
608
+ await db.get(Route.fromFlat(`tree@${nodeHash}`));
609
+ ```
610
+
611
+ This is **critical** for large trees to prevent:
612
+
613
+ - Heap exhaustion
614
+ - Infinite recursion
615
+ - Performance degradation
616
+
617
+ ### 3. Batch Operations
618
+
619
+ Import data in batches for better performance:
620
+
621
+ ```typescript
622
+ const batchSize = 100;
623
+ for (let i = 0; i < data.length; i += batchSize) {
624
+ const batch = data.slice(i, i + batchSize);
625
+ await db.core.import({
626
+ users: { _type: 'components', _data: batch }
627
+ });
628
+ }
629
+ ```
630
+
631
+ ### 4. Always Use Insert History
632
+
633
+ Create tables with insert history to enable time-travel:
634
+
635
+ ```typescript
636
+ // ✅ CORRECT - Enables version tracking
637
+ await db.core.createTableWithInsertHistory(tableCfg);
638
+
639
+ // ❌ AVOID - No version history
640
+ await db.core.createTable(tableCfg);
641
+ ```
642
+
643
+ ### 5. Type Safety
644
+
645
+ Use TypeScript types from `@rljson/rljson` for type-safe operations:
646
+
647
+ ```typescript
648
+ import type { Component, Tree, Cake, Layer } from '@rljson/rljson';
649
+
650
+ // Type-safe component
651
+ const user: Component = {
652
+ name: 'Alice',
653
+ email: 'alice@example.com',
654
+ _hash: '...'
655
+ };
656
+
657
+ // Type-safe tree
658
+ const tree: Tree = {
659
+ id: 'root',
660
+ children: [],
661
+ meta: { name: 'Root' },
662
+ isParent: false,
663
+ _hash: '...'
664
+ };
665
+ ```
666
+
667
+ ### 6. Error Handling
668
+
669
+ Always wrap database operations in try-catch:
670
+
671
+ ```typescript
672
+ try {
673
+ const result = await db.get(route, where);
674
+ // Process result...
675
+ } catch (error) {
676
+ if (error.message.includes('Maximum recursion depth')) {
677
+ console.error('Infinite recursion detected in route');
678
+ } else if (error.message.includes('not valid')) {
679
+ console.error('Invalid route or data structure');
680
+ } else {
681
+ console.error('Unexpected error:', error);
682
+ }
683
+ }
684
+ ```
685
+
686
+ ## Troubleshooting
687
+
688
+ ### Tree INSERT Failures
689
+
690
+ **Symptoms:**
691
+
692
+ - Tree INSERT completes without errors
693
+ - GET queries return empty results or only root node
694
+ - Cell length is 1 instead of expected count
695
+
696
+ **Solution:** This was fixed in v0.0.12+. Upgrade to latest version:
697
+
698
+ ```bash
699
+ pnpm update @rljson/db@latest
700
+ ```
701
+
702
+ See [README.trouble.md](./README.trouble.md) for more troubleshooting guides.
703
+
704
+ ### Infinite Recursion Errors
705
+
706
+ **Problem:** Tree queries causing stack overflow or heap exhaustion.
707
+
708
+ **Solution:** Use WHERE clause for single-node queries:
709
+
710
+ ```typescript
711
+ // ✅ This
712
+ await db.get(route, { _hash: nodeHash });
713
+
714
+ // ❌ Not this (for large trees)
715
+ await db.get(Route.fromFlat(`tree@${nodeHash}`));
716
+ ```
717
+
718
+ ### Cache Not Clearing
719
+
720
+ **Problem:** Stale data persists after modifications.
721
+
722
+ **Solution:** Register notify callbacks to clear cache automatically:
723
+
724
+ ```typescript
725
+ db.notify.register(route, async () => {
726
+ db.clearCache();
727
+ });
728
+ ```
729
+
730
+ ## API Reference
731
+
732
+ ### Core Methods
733
+
734
+ #### `db.get(route, where, filter?, sliceIds?, options?)`
735
+
736
+ Query data from the database.
737
+
738
+ **Parameters:**
739
+
740
+ - `route: Route` - The route to query
741
+ - `where: string | Json` - Filter criteria
742
+ - `filter?: ControllerChildProperty[]` - Optional child filters
743
+ - `sliceIds?: SliceId[]` - Optional dimension filters
744
+ - `options?: GetOptions` - Query options (`skipRljson`, `skipTree`, `skipCell`)
745
+
746
+ **Returns:** `Promise<ContainerWithControllers>`
747
+
748
+ #### `db.insert(route, data, origin?, refs?)`
749
+
750
+ Insert new data into the database.
751
+
752
+ **Parameters:**
753
+
754
+ - `route: Route` - Destination route
755
+ - `data: Json` - Data to insert
756
+ - `origin?: Ref` - Optional origin reference
757
+ - `refs?: ControllerRefs` - Optional controller references
758
+
759
+ **Returns:** `Promise<InsertHistoryRow[]>`
760
+
761
+ #### `db.getInsertHistory(table, options?)`
762
+
763
+ Get version history for a table.
764
+
765
+ **Parameters:**
766
+
767
+ - `table: string` - Table name
768
+ - `options?: { sorted?: boolean, ascending?: boolean }` - Sort options
769
+
770
+ **Returns:** `Promise<InsertHistoryTable>`
771
+
772
+ #### `db.core.import(rljson)`
773
+
774
+ Import RLJSON data into the database.
775
+
776
+ **Parameters:**
777
+
778
+ - `rljson: Rljson` - Data to import
779
+
780
+ **Returns:** `Promise<void>`
781
+
782
+ #### `db.core.dump()`
783
+
784
+ Export all database data.
785
+
786
+ **Returns:** `Promise<Rljson>`
787
+
788
+ #### `db.core.createTableWithInsertHistory(cfg)`
789
+
790
+ Create a table with automatic version tracking.
791
+
792
+ **Parameters:**
793
+
794
+ - `cfg: TableCfg` - Table configuration
795
+
796
+ **Returns:** `Promise<void>`
797
+
798
+ ### Utility Functions
799
+
800
+ #### `isolate(tree, path, preservedKeys?)`
801
+
802
+ Extract data at a specific path.
803
+
804
+ **Parameters:**
805
+
806
+ - `tree: any` - Source tree
807
+ - `path: (string | number)[]` - Path to isolate
808
+ - `preservedKeys?: string[]` - Keys to preserve (e.g., metadata)
809
+
810
+ **Returns:** Isolated data subtree
811
+
812
+ #### `inject(tree, path, value)`
813
+
814
+ Insert value at a specific path.
815
+
816
+ **Parameters:**
817
+
818
+ - `tree: any` - Target tree
819
+ - `path: (string | number)[]` - Destination path
820
+ - `value: any` - Value to inject
821
+
822
+ **Returns:** Modified tree
823
+
824
+ #### `treeFromObject(obj, skipRootCreation?)`
825
+
826
+ Convert object to tree structure.
827
+
828
+ **Parameters:**
829
+
830
+ - `obj: Json` - Object to convert
831
+ - `skipRootCreation?: boolean` - Skip automatic root creation (default: false)
832
+
833
+ **Returns:** `Tree[]`
834
+
835
+ #### `makeUnique(array)`
836
+
837
+ Remove duplicates by hash.
838
+
839
+ **Parameters:**
840
+
841
+ - `array: any[]` - Array with potential duplicates
842
+
843
+ **Returns:** `any[]` - Unique elements
844
+
845
+ #### `mergeTrees(trees)`
846
+
847
+ Combine multiple tree structures.
848
+
849
+ **Parameters:**
850
+
851
+ - `trees: Tree[][]` - Arrays of trees to merge
852
+
853
+ **Returns:** `Tree[]`
854
+
855
+ ## Storage Backends
856
+
857
+ @rljson/db works with any `@rljson/io` implementation:
858
+
859
+ ### IoMem (In-Memory)
860
+
861
+ Perfect for development, testing, and temporary data:
862
+
863
+ ```typescript
864
+ import { IoMem } from '@rljson/io';
865
+
866
+ const io = new IoMem();
867
+ await io.init();
868
+ const db = new Db(io);
869
+
870
+ // Data stored in memory - cleared on process exit
871
+ ```
872
+
873
+ ### IoFile (File System)
874
+
875
+ Persistent storage using the file system:
876
+
877
+ ```typescript
878
+ import { IoFile } from '@rljson/io';
879
+
880
+ const io = new IoFile('/path/to/data');
881
+ await io.init();
882
+ const db = new Db(io);
883
+
884
+ // Data persisted to disk
885
+ ```
886
+
887
+ ### IoMulti (Multiple Backends)
888
+
889
+ Redundant storage across multiple backends with priority-based cascade:
890
+
891
+ ```typescript
892
+ import { IoMulti, IoMem, IoFile } from '@rljson/io';
893
+
894
+ const io1 = new IoMem();
895
+ const io2 = new IoFile('/path/to/backup');
896
+ const io3 = new IoFile('/path/to/archive');
897
+
898
+ await io1.init();
899
+ await io2.init();
900
+ await io3.init();
901
+
902
+ const multi = new IoMulti([io1, io2, io3]);
903
+ const db = new Db(multi);
904
+
905
+ // Writes to all backends, reads from first available
906
+ // Priority: io1 > io2 > io3
907
+ ```
908
+
909
+ ## Examples
910
+
911
+ See [src/example.ts](src/example.ts) for a complete working example demonstrating:
912
+
913
+ - Table creation
914
+ - Data import/export
915
+ - Queries and filters
916
+ - Tree operations
917
+ - Version history
918
+ - Multi-edit operations
919
+
920
+ Run the example:
921
+
922
+ ```bash
923
+ pnpm run build
924
+ node dist/example.js
925
+ ```
926
+
927
+ ## Related Packages
928
+
929
+ - `@rljson/rljson` - Core RLJSON data structures and types
930
+ - `@rljson/io` - Storage backend implementations
931
+ - `@rljson/hash` - Content-addressing utilities
932
+ - `@rljson/json` - JSON manipulation helpers
933
+ - `@rljson/validate` - Data validation utilities
934
+
935
+ ## License
936
+
937
+ MIT
938
+
939
+ ## Documentation
940
+
941
+ - [README.md](./README.md) - Main documentation index
942
+ - [README.public.md](./README.public.md) - This file (public API)
943
+ - [README.contributors.md](./README.contributors.md) - Developer guide
944
+ - [README.architecture.md](./README.architecture.md) - Technical architecture
945
+ - [README.trouble.md](./README.trouble.md) - Troubleshooting guide
946
+ - [README.blog.md](./README.blog.md) - Blog posts and updates