coremachine 1.0.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 (2) hide show
  1. package/README.md +291 -0
  2. package/package.json +49 -0
package/README.md ADDED
@@ -0,0 +1,291 @@
1
+ # Coremachine
2
+
3
+ A type-safe state machine built on top of Hypercore for distributed, append-only state management. Coremachine combines the power of state machines with the reliability and synchronization capabilities of Hypercore, making it perfect for building distributed applications that need consistent state across multiple peers.
4
+
5
+ Coremachine extends a Duplex stream — write actions in, read state changes out.
6
+
7
+ ## Features
8
+
9
+ - 🔒 **Type Safety**: Full TypeScript support with automatic type inference
10
+ - 🌐 **Distributed**: Built on Hypercore for peer-to-peer state synchronization
11
+ - 📝 **Append-Only**: Immutable state history with complete audit trail
12
+ - 🎯 **State Machine**: Predictable state transitions with guards and actions
13
+ - 🔀 **Streamable**: Duplex stream interface — pipe actions in, pipe state out
14
+ - 📦 **Lightweight**: Minimal dependencies, maximum functionality
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install coremachine
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```javascript
25
+ import Corestore from "corestore";
26
+ import { createMachine, Coremachine } from "coremachine";
27
+
28
+ // Define your state machine
29
+ const definition = createMachine({
30
+ initial: "idle",
31
+ context: {
32
+ running: false,
33
+ counter: 0,
34
+ },
35
+ states: {
36
+ idle: {
37
+ on: {
38
+ START: {
39
+ target: "running",
40
+ action: (ctx, counter) => {
41
+ ctx.running = true;
42
+ ctx.counter = counter;
43
+ },
44
+ },
45
+ },
46
+ },
47
+ running: {
48
+ on: {
49
+ INCREMENT: {
50
+ target: "running",
51
+ action: (ctx) => {
52
+ ctx.counter++;
53
+ },
54
+ },
55
+ STOP: {
56
+ target: "idle",
57
+ action: (ctx, finalCount) => {
58
+ ctx.running = false;
59
+ if (finalCount !== undefined) {
60
+ ctx.counter = finalCount;
61
+ }
62
+ },
63
+ },
64
+ },
65
+ },
66
+ },
67
+ });
68
+
69
+ // Initialize Coremachine
70
+ const store = new Corestore("./store");
71
+ const cube = new Coremachine(
72
+ store.get({ name: "coremachine", valueEncoding: "json" }),
73
+ definition,
74
+ );
75
+
76
+ // Call action directly
77
+ await cube.action("START", 1);
78
+ console.log(cube.state, cube.context); // "running", { running: true, counter: 1 }
79
+
80
+ // Or write actions into the stream
81
+ cube.write({ event: "INCREMENT" });
82
+
83
+ // Read state changes out
84
+ cube.on("data", ({ state, context }) => {
85
+ console.log(state, context); // "running", { running: true, counter: 2 }
86
+ });
87
+ ```
88
+
89
+ ## Core Concepts
90
+
91
+ ### State Machine Definition
92
+
93
+ Define your state machine using the `createMachine` function:
94
+
95
+ ```javascript
96
+ const definition = createMachine({
97
+ initial: "stateName", // Initial state
98
+ context: { // Shared context object
99
+ // your data here
100
+ },
101
+ states: {
102
+ stateName: {
103
+ on: {
104
+ EVENT_NAME: {
105
+ target: "nextState",
106
+ action: (context, payload) => {
107
+ // Modify context here
108
+ }
109
+ }
110
+ }
111
+ }
112
+ }
113
+ });
114
+ ```
115
+
116
+ ### Duplex Stream Interface
117
+
118
+ Coremachine is a Duplex stream. The writable side accepts actions, the readable side emits state changes:
119
+
120
+ ```javascript
121
+ const cube = new Coremachine(core, definition);
122
+
123
+ // Write side — type-safe action messages
124
+ cube.write({ event: "START", value: 1 });
125
+ cube.write({ event: "INCREMENT" });
126
+
127
+ // Read side — typed state + context
128
+ cube.on("data", ({ state, context }) => {
129
+ console.log(state, context);
130
+ });
131
+
132
+ // Pipe it
133
+ actionSource.pipe(cube).pipe(stateConsumer);
134
+ ```
135
+
136
+ The stream opens lazily on first read or write, restoring the latest state from the underlying Hypercore automatically.
137
+
138
+ ### Actions and Transitions
139
+
140
+ Actions are functions that modify the context when transitioning between states:
141
+
142
+ ```javascript
143
+ action: (context, payload) => {
144
+ // Directly modify the context object
145
+ context.someProperty = payload;
146
+ context.counter++;
147
+ }
148
+ ```
149
+
150
+ ## API Reference
151
+
152
+ ### `createMachine(config)`
153
+
154
+ Creates a state machine definition.
155
+
156
+ **Parameters:**
157
+ - `config.initial` (string): The initial state name
158
+ - `config.context` (object): The initial context/data
159
+ - `config.states` (object): State definitions with transitions and actions
160
+
161
+ **Returns:** Machine definition object
162
+
163
+ ### `new Coremachine(hypercore, definition, opts?)`
164
+
165
+ Creates a new Coremachine instance (a Duplex stream).
166
+
167
+ **Parameters:**
168
+ - `hypercore`: A Hypercore instance with JSON encoding
169
+ - `definition`: Machine definition from `createMachine()`
170
+ - `opts.eager` (boolean): If `true`, push the current state immediately on open
171
+
172
+ ### Instance Methods
173
+
174
+ #### `await cube.action(eventName, payload?)`
175
+
176
+ Trigger a state transition directly.
177
+
178
+ **Parameters:**
179
+ - `eventName` (string): The event to trigger
180
+ - `payload` (any, optional): Data to pass to the action function
181
+
182
+ **Returns:** `{ state, context }` after the transition
183
+
184
+ #### `cube.write({ event, value? })`
185
+
186
+ Write an action into the stream. Equivalent to calling `action()` but through the stream interface, with backpressure handled automatically.
187
+
188
+ #### `await cube.forward()`
189
+
190
+ Move forward in the state history.
191
+
192
+ #### `await cube.backward()`
193
+
194
+ Move backward in the state history.
195
+
196
+ #### `cube.truncate(newLength)`
197
+
198
+ Truncate the underlying Hypercore to a new length.
199
+
200
+ ### Properties
201
+
202
+ - `cube.state` (string): Current state name
203
+ - `cube.context` (object): Current context data
204
+ - `cube.isEmpty` (boolean): Whether the hypercore is empty
205
+ - `cube.getAvailableActions()` (array): Actions available in the current state
206
+
207
+ ### Stream Events
208
+
209
+ #### `data`
210
+
211
+ Emitted when the state changes. Each message contains the new state and context:
212
+
213
+ ```javascript
214
+ cube.on("data", ({ state, context }) => {
215
+ console.log(`Now in state: ${state}`, context);
216
+ });
217
+ ```
218
+
219
+ ## Advanced Usage
220
+
221
+ ### Monitoring State Changes
222
+
223
+ Use the `deep-object-diff` library to track specific changes:
224
+
225
+ ```javascript
226
+ import { diff } from "deep-object-diff";
227
+
228
+ let prev = null;
229
+ cube.on("data", ({ state, context }) => {
230
+ if (prev) {
231
+ const changes = diff(prev, context);
232
+ console.log("Changes:", changes);
233
+ }
234
+ prev = structuredClone(context);
235
+ });
236
+ ```
237
+
238
+ ### Distributed State Synchronization
239
+
240
+ Since Coremachine is built on Hypercore, multiple instances can synchronize automatically:
241
+
242
+ ```javascript
243
+ // Peer A
244
+ const storeA = new Corestore("./storeA");
245
+ const cubeA = new Coremachine(
246
+ storeA.get({ name: "coremachine", valueEncoding: "json" }),
247
+ definition
248
+ );
249
+
250
+ // Peer B
251
+ const storeB = new Corestore("./storeB");
252
+ const cubeB = new Coremachine(
253
+ storeB.get({ key: storeA.key, valueEncoding: "json" }),
254
+ definition
255
+ );
256
+ ```
257
+
258
+ ### Piping
259
+
260
+ Since Coremachine is a standard Duplex stream, it composes with the rest of the streaming ecosystem:
261
+
262
+ ```javascript
263
+ // Pipe actions from one source into the cube
264
+ actionStream.pipe(cube);
265
+
266
+ // Pipe state changes to a consumer
267
+ cube.pipe(renderStream);
268
+
269
+ // Full pipeline
270
+ actionStream.pipe(cube).pipe(renderStream);
271
+ ```
272
+
273
+ ## Use Cases
274
+
275
+ - **Distributed Applications**: Synchronize application state across multiple peers
276
+ - **Audit Logging**: Maintain immutable history of all state changes
277
+ - **Collaborative Tools**: Build real-time collaborative applications
278
+ - **IoT Networks**: Coordinate state across distributed IoT devices
279
+ - **Blockchain Alternatives**: Create consensus without traditional blockchain overhead
280
+
281
+ ## Contributing
282
+
283
+ Contributions are welcome! Please feel free to submit a Pull Request.
284
+
285
+ ## License
286
+
287
+ MIT
288
+
289
+ ---
290
+
291
+ Built with ❤️ using [Hypercore](https://github.com/holepunchto/hypercore)
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "coremachine",
3
+ "type": "module",
4
+ "version": "1.0.0",
5
+ "author": {
6
+ "name": "Dominic Cassidy",
7
+ "email": "dominic@cassidy.casa",
8
+ "url": "https://dominic.cassidy.casa"
9
+ },
10
+ "main": "dist/index.js",
11
+ "types": "dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "import": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "require": {
19
+ "types": "./dist/index.d.ts",
20
+ "default": "./dist/index.js"
21
+ }
22
+ }
23
+ },
24
+ "files": [
25
+ "dist/*"
26
+ ],
27
+ "scripts": {
28
+ "build": "tsc",
29
+ "test": "bun run tests/runtime.ts",
30
+ "test:types": "bun tsc --noEmit tests/types.ts",
31
+ "test:all": "bun run test:types && bun run test"
32
+ },
33
+ "devDependencies": {
34
+ "@types/bun": "latest",
35
+ "@types/streamx": "^2.9.5",
36
+ "corestore": "^7.4.5",
37
+ "deep-object-diff": "^1.1.9",
38
+ "tsd": "^0.33.0"
39
+ },
40
+ "peerDependencies": {
41
+ "holepunch-types": "github:Drache93/holepunch-types#main",
42
+ "typescript": "^5"
43
+ },
44
+ "dependencies": {
45
+ "hypercore": "^11.16.0",
46
+ "ready-resource": "^1.2.0",
47
+ "streamx": "^2.23.0"
48
+ }
49
+ }