@rstmdb/client 0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 rstmdb contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,362 @@
1
+ # @rstmdb/client
2
+
3
+ [![CI](https://github.com/rstmdb/rstmdb-js/actions/workflows/ci.yml/badge.svg)](https://github.com/rstmdb/rstmdb-js/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/@rstmdb/client.svg)](https://www.npmjs.com/package/@rstmdb/client)
5
+ [![npm downloads](https://img.shields.io/npm/dm/@rstmdb/client.svg)](https://www.npmjs.com/package/@rstmdb/client)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.3+-blue.svg)](https://www.typescriptlang.org/)
8
+ [![Node.js](https://img.shields.io/badge/Node.js-18+-green.svg)](https://nodejs.org/)
9
+
10
+ Official Node.js/TypeScript client for rstmdb - a distributed state machine database.
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @rstmdb/client
16
+ # or
17
+ pnpm add @rstmdb/client
18
+ # or
19
+ yarn add @rstmdb/client
20
+ ```
21
+
22
+ **Requirements:** Node.js 18.0.0 or later
23
+
24
+ ## Quick Start
25
+
26
+ ```typescript
27
+ import { Client } from '@rstmdb/client';
28
+
29
+ // Connect using the static factory method
30
+ const client = await Client.connect('localhost', 7401, {
31
+ auth: 'your-token',
32
+ });
33
+
34
+ // Register a state machine
35
+ await client.putMachine('order', 1, {
36
+ states: ['created', 'paid', 'shipped', 'delivered'],
37
+ initial: 'created',
38
+ transitions: [
39
+ { from: 'created', event: 'PAY', to: 'paid' },
40
+ { from: 'paid', event: 'SHIP', to: 'shipped' },
41
+ { from: 'shipped', event: 'DELIVER', to: 'delivered' },
42
+ ],
43
+ });
44
+
45
+ // Create an instance
46
+ const instance = await client.createInstance('order', 1, {
47
+ initialCtx: { customerId: 'c-123', items: ['item-1', 'item-2'] },
48
+ });
49
+
50
+ console.log(`Created order: ${instance.instanceId}`);
51
+ console.log(`Initial state: ${instance.state}`);
52
+
53
+ // Apply events to transition state
54
+ const result = await client.applyEvent(instance.instanceId, 'PAY', {
55
+ payload: { amount: 99.99, paymentMethod: 'card' },
56
+ });
57
+
58
+ console.log(`State: ${result.fromState} -> ${result.toState}`);
59
+
60
+ await client.close();
61
+ ```
62
+
63
+ ## Configuration
64
+
65
+ ### Quick Connect (Static Factory)
66
+
67
+ The simplest way to connect:
68
+
69
+ ```typescript
70
+ // Basic connection
71
+ const client = await Client.connect('localhost', 7401);
72
+
73
+ // With authentication
74
+ const client = await Client.connect('localhost', 7401, {
75
+ auth: 'your-token',
76
+ });
77
+
78
+ // With TLS
79
+ const client = await Client.connect('localhost', 7401, {
80
+ auth: 'your-token',
81
+ tls: true,
82
+ clientName: 'my-service',
83
+ });
84
+ ```
85
+
86
+ ### Builder Pattern (Full Control)
87
+
88
+ Use `ClientOptions` builder for complex configurations:
89
+
90
+ ```typescript
91
+ import { Client, ClientOptions } from '@rstmdb/client';
92
+
93
+ const config = ClientOptions.create('localhost')
94
+ .port(7401)
95
+ .auth('your-token')
96
+ .tls({
97
+ ca: fs.readFileSync('ca.pem'),
98
+ cert: fs.readFileSync('client.pem'), // For mTLS
99
+ key: fs.readFileSync('client-key.pem'),
100
+ rejectUnauthorized: true,
101
+ })
102
+ .timeout({ connect: 5000, request: 30000 })
103
+ .reconnect({ enabled: true, interval: 2000, maxAttempts: 10 })
104
+ .clientName('my-service')
105
+ .build();
106
+
107
+ const client = new Client(config);
108
+ await client.connect();
109
+ ```
110
+
111
+ ### Configuration Options
112
+
113
+ | Option | Default | Description |
114
+ | ---------------------- | -------- | ------------------------------------------- |
115
+ | `host` | required | Server hostname |
116
+ | `port` | `7401` | Server port |
117
+ | `connectTimeout` | `10000` | Connection timeout (ms) |
118
+ | `requestTimeout` | `30000` | Request timeout (ms) |
119
+ | `authToken` | - | Bearer token for authentication |
120
+ | `tls` | - | `true` for system CA, or `TlsConfig` object |
121
+ | `reconnect` | `true` | Auto-reconnect on disconnect |
122
+ | `reconnectInterval` | `1000` | Initial reconnect delay (ms) |
123
+ | `reconnectMaxAttempts` | `10` | Max reconnection attempts |
124
+ | `clientName` | - | Client identifier sent in handshake |
125
+
126
+ ## API Reference
127
+
128
+ ### Connection Lifecycle
129
+
130
+ ```typescript
131
+ // Static factory (connects automatically)
132
+ const client = await Client.connect('localhost', 7401);
133
+
134
+ // Or manual connect
135
+ const client = new Client(config);
136
+ await client.connect(); // Connect to server
137
+
138
+ await client.close(); // Close connection
139
+ client.isConnected(); // Check connection status
140
+ ```
141
+
142
+ ### System Operations
143
+
144
+ ```typescript
145
+ await client.ping(); // Ping server
146
+ const info = await client.info(); // Get server info
147
+ ```
148
+
149
+ ### Machine Operations
150
+
151
+ ```typescript
152
+ // Create or update a state machine
153
+ await client.putMachine('order', 1, {
154
+ states: ['created', 'paid', 'shipped'],
155
+ initial: 'created',
156
+ transitions: [
157
+ { from: 'created', event: 'PAY', to: 'paid' },
158
+ { from: 'paid', event: 'SHIP', to: 'shipped' },
159
+ ],
160
+ });
161
+
162
+ // Get a machine definition
163
+ const machine = await client.getMachine('order', 1);
164
+
165
+ // List all machines
166
+ const list = await client.listMachines({ limit: 10 });
167
+ ```
168
+
169
+ ### Instance Operations
170
+
171
+ ```typescript
172
+ // Create an instance
173
+ const instance = await client.createInstance('order', 1, {
174
+ instanceId: 'order-123', // Optional custom ID
175
+ initialCtx: { customerId: 'c-1' },
176
+ });
177
+
178
+ // Get instance state
179
+ const state = await client.getInstance('order-123');
180
+ console.log(state.state, state.ctx);
181
+
182
+ // Delete an instance
183
+ await client.deleteInstance('order-123');
184
+ ```
185
+
186
+ ### Event Operations
187
+
188
+ ```typescript
189
+ // Apply an event
190
+ const result = await client.applyEvent('order-123', 'PAY', {
191
+ payload: { amount: 99.99 },
192
+ expectedState: 'created', // Optimistic concurrency
193
+ });
194
+
195
+ // Batch operations (atomic)
196
+ const batch = await client.batch(
197
+ [
198
+ { op: 'CREATE_INSTANCE', params: { machine: 'order', version: 1 } },
199
+ { op: 'APPLY_EVENT', params: { instanceId: 'i-1', event: 'PAY' } },
200
+ ],
201
+ { mode: 'atomic' }
202
+ );
203
+ ```
204
+
205
+ ### Watch/Streaming
206
+
207
+ ```typescript
208
+ // Watch a specific instance
209
+ const sub = await client.watchInstance('order-123');
210
+
211
+ // AsyncIterator style
212
+ for await (const event of sub) {
213
+ console.log(`${event.fromState} -> ${event.toState}`);
214
+ if (event.toState === 'delivered') {
215
+ await sub.unsubscribe();
216
+ break;
217
+ }
218
+ }
219
+
220
+ // Event emitter style
221
+ sub.on('event', (event) => {
222
+ console.log(`Event: ${event.event}`);
223
+ });
224
+
225
+ // Watch all instances with filters
226
+ const allSub = await client.watchAll({
227
+ machines: ['order'],
228
+ toStates: ['shipped', 'delivered'],
229
+ });
230
+ ```
231
+
232
+ ### WAL Operations
233
+
234
+ ```typescript
235
+ // Read WAL entries
236
+ const entries = await client.walRead(0n, { limit: 100 });
237
+
238
+ // Snapshot an instance
239
+ await client.snapshotInstance('order-123');
240
+
241
+ // Trigger compaction
242
+ await client.compact();
243
+ ```
244
+
245
+ ## Error Handling
246
+
247
+ ```typescript
248
+ import {
249
+ Client,
250
+ RstmdbError,
251
+ NotFoundError,
252
+ ConflictError,
253
+ InvalidTransitionError,
254
+ AuthenticationError,
255
+ ConnectionError,
256
+ TimeoutError,
257
+ } from '@rstmdb/client';
258
+
259
+ try {
260
+ await client.applyEvent('order-123', 'PAY', {
261
+ expectedState: 'created',
262
+ });
263
+ } catch (error) {
264
+ if (error instanceof ConflictError) {
265
+ // State changed since we last checked
266
+ console.log('Expected:', error.details?.expectedState);
267
+ console.log('Actual:', error.details?.actualState);
268
+ } else if (error instanceof InvalidTransitionError) {
269
+ // No valid transition from current state
270
+ console.log('Cannot apply PAY from current state');
271
+ } else if (error instanceof NotFoundError) {
272
+ // Instance doesn't exist
273
+ console.log('Instance not found');
274
+ } else if (error.retryable) {
275
+ // Transient error, can retry
276
+ console.log('Retrying...');
277
+ } else {
278
+ throw error;
279
+ }
280
+ }
281
+ ```
282
+
283
+ ### Error Classes
284
+
285
+ | Class | Description |
286
+ | ------------------------ | ------------------------- |
287
+ | `RstmdbError` | Base error class |
288
+ | `ConnectionError` | Connection failures |
289
+ | `TimeoutError` | Request timeouts |
290
+ | `ProtocolError` | Protocol-level errors |
291
+ | `ServerError` | Server-returned errors |
292
+ | `NotFoundError` | Resource not found |
293
+ | `ConflictError` | Version/state conflicts |
294
+ | `AuthenticationError` | Auth failures |
295
+ | `InvalidTransitionError` | Invalid state transitions |
296
+ | `GuardFailedError` | Guard conditions failed |
297
+
298
+ ## Events
299
+
300
+ ```typescript
301
+ client.on('connect', () => {
302
+ console.log('Connected');
303
+ });
304
+
305
+ client.on('disconnect', (error) => {
306
+ console.log('Disconnected:', error?.message);
307
+ });
308
+
309
+ client.on('reconnect', (attempt) => {
310
+ console.log(`Reconnecting (attempt ${attempt})`);
311
+ });
312
+
313
+ client.on('error', (error) => {
314
+ console.error('Error:', error);
315
+ });
316
+ ```
317
+
318
+ ## TypeScript
319
+
320
+ Full TypeScript support with comprehensive type definitions:
321
+
322
+ ```typescript
323
+ import {
324
+ // Client
325
+ Client,
326
+ ClientOptions,
327
+ ClientConfig,
328
+ TlsConfig,
329
+
330
+ // Machine types
331
+ MachineDefinition,
332
+ Transition,
333
+ PutMachineResult,
334
+ GetMachineResult,
335
+
336
+ // Instance types
337
+ CreateInstanceResult,
338
+ GetInstanceResult,
339
+
340
+ // Event types
341
+ ApplyEventResult,
342
+ BatchOperation,
343
+ BatchResult,
344
+
345
+ // Streaming
346
+ StreamEvent,
347
+ Subscription,
348
+
349
+ // Server info
350
+ ServerInfo,
351
+
352
+ // Errors
353
+ RstmdbError,
354
+ NotFoundError,
355
+ ConflictError,
356
+ InvalidTransitionError,
357
+ } from '@rstmdb/client';
358
+ ```
359
+
360
+ ## License
361
+
362
+ MIT