@rljson/io 0.0.63 → 0.0.65

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,639 @@
1
- # @rljson/
1
+ # @rljson/io
2
2
 
3
- Todo: Add description here
3
+ Low-level interface for reading and writing RLJSON data. This package provides a unified abstraction layer for working with RLJSON databases, supporting in-memory storage, remote connections, and multi-source data aggregation.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Quick Start](#quick-start)
9
+ - [Core Concepts](#core-concepts)
10
+ - [API Overview](#api-overview)
11
+ - [Implementations](#implementations)
12
+ - [Usage Examples](#usage-examples)
13
+ - [Testing Utilities](#testing-utilities)
14
+ - [TypeScript Support](#typescript-support)
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @rljson/io
20
+ # or
21
+ pnpm add @rljson/io
22
+ # or
23
+ yarn add @rljson/io
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ```typescript
29
+ import { IoMem } from '@rljson/io';
30
+
31
+ // Create an in-memory database
32
+ const io = new IoMem();
33
+ await io.init();
34
+
35
+ // Create table schema
36
+ await io.createOrExtendTable({
37
+ tableCfg: {
38
+ key: 'users',
39
+ type: 'components',
40
+ columns: [
41
+ { key: '_hash', type: 'string', titleShort: 'Hash', titleLong: 'Hash' },
42
+ { key: 'id', type: 'number', titleShort: 'ID', titleLong: 'User ID' },
43
+ { key: 'name', type: 'string', titleShort: 'Name', titleLong: 'Name' }
44
+ ],
45
+ isHead: false,
46
+ isRoot: false,
47
+ isShared: true
48
+ }
49
+ });
50
+
51
+ // Write data
52
+ await io.write({
53
+ data: {
54
+ users: {
55
+ _type: 'components',
56
+ _data: [
57
+ { id: 1, name: 'Alice' },
58
+ { id: 2, name: 'Bob' }
59
+ ]
60
+ }
61
+ }
62
+ });
63
+
64
+ // Query data
65
+ const result = await io.readRows({
66
+ table: 'users',
67
+ where: { id: 1 }
68
+ });
69
+ ```
70
+
71
+ ## Core Concepts
72
+
73
+ ### Io Interface
74
+
75
+ The `Io` interface is the core abstraction that defines a standard set of operations for working with RLJSON data:
76
+
77
+ - **Lifecycle Management**: `init()`, `close()`, `isReady()`
78
+ - **Data Operations**: `write()`, `readRows()`, `dump()`
79
+ - **Schema Management**: `createOrExtendTable()`, `tableExists()`, `rawTableCfgs()`
80
+ - **Metadata**: `contentType()`, `rowCount()`, `lastUpdate()`
81
+
82
+ All implementations (in-memory, remote, multi-source) conform to this interface.
83
+
84
+ ### RLJSON Format
85
+
86
+ RLJSON (Relational JSON) is a structured data format that combines JSON with relational database concepts:
87
+
88
+ ```typescript
89
+ {
90
+ tableName: {
91
+ _type: 'components', // Content type identifier
92
+ _data: [...rows] // Array of data objects
93
+ }
94
+ }
95
+ ```
96
+
97
+ ## API Overview
98
+
99
+ ### Lifecycle
100
+
101
+ ```typescript
102
+ // Initialize the Io instance
103
+ await io.init();
104
+
105
+ // Check if ready
106
+ await io.isReady();
107
+
108
+ // Check if open
109
+ if (io.isOpen) {
110
+ // Perform operations
111
+ }
112
+
113
+ // Close when done
114
+ await io.close();
115
+ ```
116
+
117
+ ### Writing Data
118
+
119
+ ```typescript
120
+ await io.write({
121
+ data: {
122
+ products: {
123
+ _type: 'components',
124
+ _data: [
125
+ { sku: 'ABC123', name: 'Widget', price: 29.99 },
126
+ { sku: 'XYZ789', name: 'Gadget', price: 49.99 }
127
+ ]
128
+ }
129
+ }
130
+ });
131
+ ```
132
+
133
+ ### Reading Data
134
+
135
+ ```typescript
136
+ // Query with conditions
137
+ const users = await io.readRows({
138
+ table: 'users',
139
+ where: { active: true, role: 'admin' }
140
+ });
141
+
142
+ // Get row count
143
+ const count = await io.rowCount('users');
144
+
145
+ // Check if table exists
146
+ const exists = await io.tableExists('users');
147
+
148
+ // Get last update timestamp
149
+ const timestamp = await io.lastUpdate('users');
150
+ ```
151
+
152
+ ### Dumping Data
153
+
154
+ ```typescript
155
+ // Dump entire database
156
+ const allData = await io.dump();
157
+
158
+ // Dump specific table
159
+ const tableData = await io.dumpTable({ table: 'users' });
160
+ ```
161
+
162
+ ### Schema Management
163
+
164
+ ```typescript
165
+ // Create or extend a table
166
+ await io.createOrExtendTable({
167
+ tableCfg: {
168
+ key: 'orders',
169
+ type: 'components',
170
+ columns: [
171
+ { key: '_hash', type: 'string', titleShort: 'Hash', titleLong: 'Hash' },
172
+ { key: 'orderId', type: 'number', titleShort: 'Order ID', titleLong: 'Order ID' },
173
+ { key: 'customerId', type: 'number', titleShort: 'Customer', titleLong: 'Customer ID' },
174
+ { key: 'total', type: 'number', titleShort: 'Total', titleLong: 'Total Amount' },
175
+ { key: 'status', type: 'string', titleShort: 'Status', titleLong: 'Order Status' }
176
+ ],
177
+ isHead: false,
178
+ isRoot: false,
179
+ isShared: true
180
+ }
181
+ });
182
+
183
+ // Get table configurations
184
+ const configs = await io.rawTableCfgs();
185
+
186
+ // Get content type
187
+ const contentType = await io.contentType({ table: 'orders' });
188
+ ```
189
+
190
+ ## Implementations
191
+
192
+ ### IoMem - In-Memory Storage
193
+
194
+ Fast, synchronous in-memory database implementation. Perfect for testing, caching, or small datasets.
195
+
196
+ ```typescript
197
+ import { IoMem } from '@rljson/io';
198
+
199
+ const io = new IoMem();
200
+ await io.init();
201
+ ```
202
+
203
+ **Use Cases:**
204
+
205
+ - Unit testing
206
+ - Temporary data storage
207
+ - Fast prototyping
208
+ - Client-side caching
209
+
210
+ ### IoPeer - Remote Connection
211
+
212
+ Connect to a remote RLJSON database over a socket connection (Socket.IO compatible).
213
+
214
+ ```typescript
215
+ import { IoPeer } from '@rljson/io';
216
+ import { io as socketClient } from 'socket.io-client';
217
+
218
+ const socket = socketClient('http://localhost:3000');
219
+ const io = new IoPeer(socket);
220
+ await io.init();
221
+ ```
222
+
223
+ **Use Cases:**
224
+
225
+ - Distributed applications
226
+ - Client-server architectures
227
+ - Real-time data synchronization
228
+ - Microservices communication
229
+
230
+ ### IoMulti - Multi-Source Aggregation
231
+
232
+ Combine multiple Io instances with priority-based cascading. Supports read/write splitting and hot-swap caching.
233
+
234
+ ```typescript
235
+ import { IoMulti, IoMem, IoPeer } from '@rljson/io';
236
+
237
+ const cache = new IoMem();
238
+ await cache.init();
239
+
240
+ const remote = new IoPeer(socket);
241
+ await remote.init();
242
+
243
+ const io = new IoMulti([
244
+ { io: cache, priority: 1, read: true, write: true, dump: false },
245
+ { io: remote, priority: 2, read: true, write: true, dump: true }
246
+ ]);
247
+ await io.init();
248
+
249
+ // Reads cascade: cache first (priority 1), then remote (priority 2)
250
+ // Writes go to both cache and remote
251
+ // Cache is automatically updated with remote data (hot-swapping)
252
+ ```
253
+
254
+ **Use Cases:**
255
+
256
+ - Performance optimization with caching layers
257
+ - Multi-tier data storage
258
+ - Fallback mechanisms
259
+ - Distributed data aggregation
260
+
261
+ **Key Features:**
262
+
263
+ - **Priority-based cascading**: Higher priority sources are queried first
264
+ - **Hot-swap caching**: Successful reads are written back to cache layers
265
+ - **Flexible capabilities**: Each source can be read-only, write-only, or both
266
+ - **Automatic table merging**: Results from multiple sources are merged intelligently
267
+
268
+ ### IoPeerBridge - Server-Side Handler
269
+
270
+ Bridge between Socket events and Io operations. Used on the server to handle client requests.
271
+
272
+ ```typescript
273
+ import { IoPeerBridge, IoMem } from '@rljson/io';
274
+ import { Server } from 'socket.io';
275
+
276
+ const io = new Server(3000);
277
+ const db = new IoMem();
278
+ await db.init();
279
+
280
+ io.on('connection', (socket) => {
281
+ const bridge = new IoPeerBridge(socket, db);
282
+ bridge.start();
283
+
284
+ socket.on('disconnect', () => {
285
+ bridge.stop();
286
+ });
287
+ });
288
+ ```
289
+
290
+ ## Usage Examples
291
+
292
+ ### Example 1: Simple In-Memory Database
293
+
294
+ ```typescript
295
+ import { IoMem } from '@rljson/io';
296
+
297
+ async function simpleExample() {
298
+ const io = new IoMem();
299
+ await io.init();
300
+
301
+ // Create a table
302
+ await io.createOrExtendTable({
303
+ tableCfg: {
304
+ key: 'tasks',
305
+ type: 'components',
306
+ columns: [
307
+ { key: '_hash', type: 'string', titleShort: 'Hash', titleLong: 'Hash' },
308
+ { key: 'id', type: 'number', titleShort: 'ID', titleLong: 'Task ID' },
309
+ { key: 'title', type: 'string', titleShort: 'Title', titleLong: 'Task Title' },
310
+ { key: 'completed', type: 'boolean', titleShort: 'Done', titleLong: 'Completed' }
311
+ ],
312
+ isHead: false,
313
+ isRoot: false,
314
+ isShared: true
315
+ }
316
+ });
317
+
318
+ // Insert data
319
+ await io.write({
320
+ data: {
321
+ tasks: {
322
+ _type: 'components',
323
+ _data: [
324
+ { id: 1, title: 'Learn RLJSON', completed: true },
325
+ { id: 2, title: 'Build app', completed: false }
326
+ ]
327
+ }
328
+ }
329
+ });
330
+
331
+ // Query incomplete tasks
332
+ const incompleteTasks = await io.readRows({
333
+ table: 'tasks',
334
+ where: { completed: false }
335
+ });
336
+
337
+ console.log(incompleteTasks);
338
+ // { tasks: { _type: 'components', _data: [{ id: 2, title: 'Build app', completed: false }] } }
339
+
340
+ await io.close();
341
+ }
342
+ ```
343
+
344
+ ### Example 2: Client-Server Architecture
345
+
346
+ **Server:**
347
+
348
+ ```typescript
349
+ import { IoMem, IoPeerBridge } from '@rljson/io';
350
+ import { Server } from 'socket.io';
351
+
352
+ const ioServer = new Server(3000, {
353
+ cors: { origin: '*' }
354
+ });
355
+
356
+ const db = new IoMem();
357
+ await db.init();
358
+
359
+ // Create table schema
360
+ await db.createOrExtendTable({
361
+ tableCfg: {
362
+ key: 'products',
363
+ type: 'components',
364
+ columns: [
365
+ { key: '_hash', type: 'string', titleShort: 'Hash', titleLong: 'Hash' },
366
+ { key: 'id', type: 'number', titleShort: 'ID', titleLong: 'Product ID' },
367
+ { key: 'name', type: 'string', titleShort: 'Name', titleLong: 'Product Name' },
368
+ { key: 'price', type: 'number', titleShort: 'Price', titleLong: 'Price' }
369
+ ],
370
+ isHead: false,
371
+ isRoot: false,
372
+ isShared: true
373
+ }
374
+ });
375
+
376
+ // Pre-populate with data
377
+ await db.write({
378
+ data: {
379
+ products: {
380
+ _type: 'components',
381
+ _data: [{ id: 1, name: 'Widget', price: 10 }]
382
+ }
383
+ }
384
+ });
385
+
386
+ ioServer.on('connection', (socket) => {
387
+ const bridge = new IoPeerBridge(socket, db);
388
+ bridge.start();
389
+
390
+ socket.on('disconnect', () => bridge.stop());
391
+ });
392
+ ```
393
+
394
+ **Client:**
395
+
396
+ ```typescript
397
+ import { IoPeer } from '@rljson/io';
398
+ import { io as socketClient } from 'socket.io-client';
399
+
400
+ const socket = socketClient('http://localhost:3000');
401
+ const io = new IoPeer(socket);
402
+ await io.init();
403
+
404
+ // Query remote database
405
+ const products = await io.readRows({
406
+ table: 'products',
407
+ where: {}
408
+ });
409
+
410
+ console.log(products);
411
+ await io.close();
412
+ ```
413
+
414
+ ### Example 3: Multi-Tier with Cache
415
+
416
+ ```typescript
417
+ import { IoMulti, IoMem, IoPeer } from '@rljson/io';
418
+ import { io as socketClient } from 'socket.io-client';
419
+
420
+ // Local cache
421
+ const cache = new IoMem();
422
+ await cache.init();
423
+
424
+ // Remote database
425
+ const socket = socketClient('http://localhost:3000');
426
+ const remote = new IoPeer(socket);
427
+ await remote.init();
428
+
429
+ // Create table schema in both cache and remote
430
+ const tableCfg = {
431
+ key: 'users',
432
+ type: 'components',
433
+ columns: [
434
+ { key: '_hash', type: 'string', titleShort: 'Hash', titleLong: 'Hash' },
435
+ { key: 'id', type: 'number', titleShort: 'ID', titleLong: 'User ID' },
436
+ { key: 'name', type: 'string', titleShort: 'Name', titleLong: 'Name' }
437
+ ],
438
+ isHead: false,
439
+ isRoot: false,
440
+ isShared: true
441
+ };
442
+
443
+ await cache.createOrExtendTable({ tableCfg });
444
+ await remote.createOrExtendTable({ tableCfg });
445
+
446
+ // Combined multi-tier Io
447
+ const io = new IoMulti([
448
+ {
449
+ io: cache,
450
+ priority: 1, // Check cache first
451
+ read: true,
452
+ write: true, // Write to cache
453
+ dump: false
454
+ },
455
+ {
456
+ io: remote,
457
+ priority: 2, // Fallback to remote
458
+ read: true,
459
+ write: true, // Also write to remote
460
+ dump: true // Remote is source of truth for dumps
461
+ }
462
+ ]);
463
+ await io.init();
464
+
465
+ // First read hits remote, caches result
466
+ const data1 = await io.readRows({
467
+ table: 'users',
468
+ where: { id: 1 }
469
+ });
470
+
471
+ // Second read hits cache (faster!)
472
+ const data2 = await io.readRows({
473
+ table: 'users',
474
+ where: { id: 1 }
475
+ });
476
+
477
+ // Writes go to both cache and remote
478
+ await io.write({
479
+ data: {
480
+ users: {
481
+ _type: 'components',
482
+ _data: [{ id: 2, name: 'Charlie' }]
483
+ }
484
+ }
485
+ });
486
+
487
+ await io.close();
488
+ ```
489
+
490
+ ## Testing Utilities
491
+
492
+ ### SocketMock
493
+
494
+ Mock socket for testing without real network connections.
495
+
496
+ ```typescript
497
+ import { SocketMock } from '@rljson/io';
498
+
499
+ const socket = new SocketMock();
500
+ socket.on('test', (data) => console.log(data));
501
+ socket.emit('test', 'hello');
502
+ ```
503
+
504
+ ### DirectionalSocketMock
505
+
506
+ Bidirectional socket pair for client-server testing.
507
+
508
+ ```typescript
509
+ import { createSocketPair } from '@rljson/io';
510
+
511
+ const [clientSocket, serverSocket] = createSocketPair();
512
+
513
+ // Server listens
514
+ serverSocket.on('request', (data, callback) => {
515
+ callback({ result: 'success' });
516
+ });
517
+
518
+ // Client sends
519
+ clientSocket.emit('request', { query: 'data' }, (response) => {
520
+ console.log(response); // { result: 'success' }
521
+ });
522
+ ```
523
+
524
+ ### PeerSocketMock
525
+
526
+ Mock socket pair for testing IoPeer/IoPeerBridge interactions.
527
+
528
+ ```typescript
529
+ import { PeerSocketMock, IoPeer, IoPeerBridge, IoMem } from '@rljson/io';
530
+
531
+ const [clientSocket, serverSocket] = PeerSocketMock.createPeerSocketPair();
532
+
533
+ const serverDb = new IoMem();
534
+ await serverDb.init();
535
+
536
+ const bridge = new IoPeerBridge(serverSocket, serverDb);
537
+ bridge.start();
538
+
539
+ const clientIo = new IoPeer(clientSocket);
540
+ await clientIo.init();
541
+
542
+ // Client can now interact with server
543
+ const data = await clientIo.dump();
544
+ ```
545
+
546
+ ### IoTestSetup
547
+
548
+ Standard test setup interface for creating test fixtures.
549
+
550
+ ```typescript
551
+ import { IoTestSetup } from '@rljson/io';
552
+
553
+ class MyTestSetup implements IoTestSetup {
554
+ constructor(public io: Io) {}
555
+
556
+ async before() {
557
+ await this.io.init();
558
+ }
559
+
560
+ async after() {
561
+ await this.io.close();
562
+ }
563
+ }
564
+ ```
565
+
566
+ ## TypeScript Support
567
+
568
+ Full TypeScript support with comprehensive type definitions:
569
+
570
+ ```typescript
571
+ import {
572
+ Io,
573
+ IoMultiIo,
574
+ Socket
575
+ } from '@rljson/io';
576
+
577
+ // All interfaces are fully typed
578
+ const ios: IoMultiIo[] = [
579
+ {
580
+ io: myIo,
581
+ priority: 1,
582
+ read: true,
583
+ write: true,
584
+ dump: false
585
+ }
586
+ ];
587
+ ```
588
+
589
+ ## Advanced Features
590
+
591
+ ### Database Name Mapping
592
+
593
+ Map table names between different representations:
594
+
595
+ ```typescript
596
+ import { IoDbNameMapping } from '@rljson/io';
597
+
598
+ const mapping = new IoDbNameMapping(underlyingIo, {
599
+ 'user_accounts': 'userAccounts',
600
+ 'order_items': 'orderItems'
601
+ });
602
+
603
+ // Access with either name
604
+ await mapping.readRows({ table: 'userAccounts', where: {} });
605
+ await mapping.readRows({ table: 'user_accounts', where: {} });
606
+ ```
607
+
608
+ ### IoTools
609
+
610
+ Utility functions for working with Io instances:
611
+
612
+ ```typescript
613
+ import { IoTools } from '@rljson/io';
614
+
615
+ // Validate table configurations
616
+ const isValid = IoTools.validateTableCfg(tableCfg);
617
+
618
+ // Merge RLJSON data
619
+ const merged = IoTools.mergeRljson(data1, data2);
620
+
621
+ // Extract type information
622
+ const types = IoTools.extractTypes(rljsonData);
623
+ ```
624
+
625
+ ## API Reference
626
+
627
+ For detailed API documentation, see the TypeScript definitions or visit the [API docs](https://github.com/rljson/io).
4
628
 
5
629
  ## Example
6
630
 
7
- [src/example.ts](src/example.ts)
631
+ See [src/example.ts](src/example.ts) for more examples.
632
+
633
+ ## Contributing
634
+
635
+ See [README.contributors.md](README.contributors.md) for development guidelines.
636
+
637
+ ## License
638
+
639
+ MIT © [Rljson](https://github.com/rljson)