@twoabove/cue 0.4.2

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 (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +410 -0
  3. package/dist/api/create.d.ts +3 -0
  4. package/dist/api/create.d.ts.map +1 -0
  5. package/dist/api/create.js +106 -0
  6. package/dist/api/define.d.ts +40 -0
  7. package/dist/api/define.d.ts.map +1 -0
  8. package/dist/api/define.js +61 -0
  9. package/dist/api/index.d.ts +5 -0
  10. package/dist/api/index.d.ts.map +1 -0
  11. package/dist/api/index.js +7 -0
  12. package/dist/api/supervisor.d.ts +22 -0
  13. package/dist/api/supervisor.d.ts.map +1 -0
  14. package/dist/api/supervisor.js +39 -0
  15. package/dist/core/Evolution.d.ts +11 -0
  16. package/dist/core/Evolution.d.ts.map +1 -0
  17. package/dist/core/Evolution.js +29 -0
  18. package/dist/core/StateKernel.d.ts +35 -0
  19. package/dist/core/StateKernel.d.ts.map +1 -0
  20. package/dist/core/StateKernel.js +113 -0
  21. package/dist/errors/index.d.ts +22 -0
  22. package/dist/errors/index.d.ts.map +1 -0
  23. package/dist/errors/index.js +43 -0
  24. package/dist/index.d.ts +5 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +2 -0
  27. package/dist/persistence/adapters/inMemory.d.ts +21 -0
  28. package/dist/persistence/adapters/inMemory.d.ts.map +1 -0
  29. package/dist/persistence/adapters/inMemory.js +41 -0
  30. package/dist/persistence/types.d.ts +28 -0
  31. package/dist/persistence/types.d.ts.map +1 -0
  32. package/dist/persistence/types.js +1 -0
  33. package/dist/runtime/Entity.d.ts +42 -0
  34. package/dist/runtime/Entity.d.ts.map +1 -0
  35. package/dist/runtime/Entity.js +357 -0
  36. package/dist/runtime/EntityManager.d.ts +15 -0
  37. package/dist/runtime/EntityManager.d.ts.map +1 -0
  38. package/dist/runtime/EntityManager.js +46 -0
  39. package/dist/runtime/Mailbox.d.ts +5 -0
  40. package/dist/runtime/Mailbox.d.ts.map +1 -0
  41. package/dist/runtime/Mailbox.js +8 -0
  42. package/dist/runtime/Passivation.d.ts +12 -0
  43. package/dist/runtime/Passivation.d.ts.map +1 -0
  44. package/dist/runtime/Passivation.js +42 -0
  45. package/dist/runtime/Supervision.d.ts +4 -0
  46. package/dist/runtime/Supervision.d.ts.map +1 -0
  47. package/dist/runtime/Supervision.js +20 -0
  48. package/dist/serde/index.d.ts +7 -0
  49. package/dist/serde/index.d.ts.map +1 -0
  50. package/dist/serde/index.js +19 -0
  51. package/dist/types/internal.d.ts +10 -0
  52. package/dist/types/internal.d.ts.map +1 -0
  53. package/dist/types/internal.js +10 -0
  54. package/dist/types/public.d.ts +168 -0
  55. package/dist/types/public.d.ts.map +1 -0
  56. package/dist/types/public.js +1 -0
  57. package/dist/utils/clock.d.ts +5 -0
  58. package/dist/utils/clock.d.ts.map +1 -0
  59. package/dist/utils/clock.js +3 -0
  60. package/dist/utils/id.d.ts +2 -0
  61. package/dist/utils/id.d.ts.map +1 -0
  62. package/dist/utils/id.js +2 -0
  63. package/dist/utils/invariants.d.ts +2 -0
  64. package/dist/utils/invariants.d.ts.map +1 -0
  65. package/dist/utils/invariants.js +5 -0
  66. package/package.json +71 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Seva Maltsev
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,410 @@
1
+ # Cue
2
+
3
+ **Durable stateful entities for TypeScript.**
4
+
5
+ Define entities with state, commands, and queries. Cue handles persistence, concurrency, and schema evolution—so you don't have to.
6
+
7
+ ```typescript
8
+ import { define, create } from "cue";
9
+
10
+ const Counter = define("Counter")
11
+ .initialState(() => ({ count: 0 }))
12
+ .commands({
13
+ increment: (s, by = 1) => {
14
+ s.count += by;
15
+ },
16
+ })
17
+ .queries({
18
+ value: (s) => s.count,
19
+ })
20
+ .build();
21
+
22
+ const app = create({ definition: Counter });
23
+ const counter = app.get("my-counter");
24
+
25
+ await counter.send.increment(5);
26
+ console.log(await counter.read.value()); // 5
27
+ ```
28
+
29
+ ## Why Cue?
30
+
31
+ - **Durable.** State survives restarts. Plug in Postgres, SQLite, or Redis—or run in-memory for tests.
32
+ - **Safe.** One operation at a time, always. No race conditions, no corrupted state.
33
+ - **Evolvable.** Schema changes are type-checked and automatic. Add a field, rename a property—old entities migrate on load.
34
+ - **Streamable.** Long-running operations yield progress in real-time.
35
+ - **Time-travel.** Query historical state at any point with full type safety.
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ npm install cue
41
+ ```
42
+
43
+ ## Quick Start
44
+
45
+ ```typescript
46
+ import { create, define, memoryStore } from "cue";
47
+
48
+ // 1. Define your entity
49
+ const Character = define("Character")
50
+ .initialState(() => ({
51
+ level: 1,
52
+ hp: 100,
53
+ quests: new Set<string>(),
54
+ }))
55
+ .commands({
56
+ takeDamage: (state, amount: number) => {
57
+ state.hp -= amount;
58
+ if (state.hp <= 0) {
59
+ state.hp = 0;
60
+ return "You have been defeated!";
61
+ }
62
+ return `Ouch! HP is now ${state.hp}.`;
63
+ },
64
+ levelUp: async (state) => {
65
+ await new Promise((res) => setTimeout(res, 50));
66
+ state.level++;
67
+ state.hp += 10;
68
+ return `Ding! Reached level ${state.level}!`;
69
+ },
70
+ // Streaming command using async generator
71
+ startQuest: async function* (state, quest: string) {
72
+ if (state.quests.has(quest)) {
73
+ yield { status: "already_on_quest" };
74
+ return "Quest already started.";
75
+ }
76
+ state.quests.add(quest);
77
+ yield { status: "started", quest };
78
+ await new Promise((res) => setTimeout(res, 100));
79
+ yield { status: "completed", quest };
80
+ state.quests.delete(quest);
81
+ return "Quest complete!";
82
+ },
83
+ })
84
+ .queries({
85
+ getStats: (state) => ({
86
+ level: state.level,
87
+ hp: state.hp,
88
+ }),
89
+ })
90
+ .build();
91
+
92
+ // 2. Create an entity manager
93
+ const manager = create({ definition: Character });
94
+
95
+ // 3. Get a reference to a specific entity by its unique ID
96
+ const playerOne = manager.get("player-one");
97
+
98
+ // 4. Interact with the entity
99
+ const damageResult = await playerOne.send.takeDamage(10);
100
+ console.log(damageResult); // "Ouch! HP is now 90."
101
+
102
+ const levelUpMessage = await playerOne.send.levelUp();
103
+ console.log(levelUpMessage); // "Ding! Reached level 2!"
104
+
105
+ // Read-only queries
106
+ const stats = await playerOne.read.getStats();
107
+ console.log(stats); // { level: 2, hp: 110 }
108
+
109
+ // Stream progress from long-running commands
110
+ console.log("Starting a new quest...");
111
+ for await (const update of playerOne.stream.startQuest("The Lost Amulet")) {
112
+ console.log(`Quest update: ${update.status}`);
113
+ }
114
+ // > Quest update: started
115
+ // > Quest update: completed
116
+
117
+ // Snapshot for debugging
118
+ const snapshot = await playerOne.snapshot();
119
+ console.log(snapshot.state.quests); // Set(0) {}
120
+
121
+ // Shut down when done
122
+ await manager.stop();
123
+ ```
124
+
125
+ ## Core Concepts
126
+
127
+ ### Defining Entities
128
+
129
+ Use the fluent builder to define your entity's shape and behavior:
130
+
131
+ - `.initialState(() => ({...}))`: Sets the default state for new entities.
132
+ - `.commands({...})`: Methods that can modify state (sync, async, or generators for streaming).
133
+ - `.queries({...})`: Read-only methods for safe state access.
134
+ - `.evolve((prevState) => ({...}))`: Migration function for schema changes.
135
+ - `.persistence({...})`: Configure snapshotting behavior.
136
+ - `.build()`: Finalize the definition.
137
+
138
+ ### Entity References
139
+
140
+ Get a handle to interact with a specific entity:
141
+
142
+ ```typescript
143
+ const ref = manager.get("entity-id");
144
+
145
+ await ref.send.someCommand(); // Execute a command (may modify state)
146
+ await ref.read.someQuery(); // Execute a query (read-only)
147
+ ref.stream.streamingCommand(); // Get AsyncIterable for streaming commands
148
+ await ref.snapshot(); // Get current state and version
149
+ await ref.stateAt(version); // Get historical state at a specific event version
150
+ await ref.stop(); // Manually stop this entity
151
+ ```
152
+
153
+ ## Persistence
154
+
155
+ Provide a store to persist state changes:
156
+
157
+ ```typescript
158
+ import { create, memoryStore } from "cue";
159
+
160
+ const manager = create({
161
+ definition: Character,
162
+ store: new memoryStore(), // or your custom PersistenceAdapter
163
+ });
164
+ ```
165
+
166
+ Implement the `PersistenceAdapter` interface to plug in any database:
167
+
168
+ ```typescript
169
+ interface PersistenceAdapter {
170
+ getEvents(entityId: string, fromVersion: bigint): Promise<EventRecord[]>;
171
+ commitEvent(entityId: string, version: bigint, data: string): Promise<void>;
172
+ getLatestSnapshot(entityId: string): Promise<SnapshotRecord | null>;
173
+ commitSnapshot(entityId: string, version: bigint, data: string): Promise<void>;
174
+ clearEntity?(entityId: string): Promise<void>;
175
+ }
176
+ ```
177
+
178
+ Enable snapshotting to avoid replaying long event histories:
179
+
180
+ ```typescript
181
+ const Entity = define("Entity")
182
+ // ...
183
+ .persistence({ snapshotEvery: 100 }) // Snapshot every 100 versions
184
+ .build();
185
+ ```
186
+
187
+ ## Schema Evolution
188
+
189
+ Migrate entity state without downtime:
190
+
191
+ ```typescript
192
+ // V1
193
+ const Character = define("Character").initialState(() => ({
194
+ name: "Player",
195
+ hitPoints: 100,
196
+ }));
197
+
198
+ // V2 with evolved schema
199
+ const Character = define("Character")
200
+ .initialState(() => ({ name: "Player", hitPoints: 100 }))
201
+ .evolve((v1) => ({
202
+ name: v1.name,
203
+ health: { current: v1.hitPoints, max: 100 },
204
+ mana: 50, // new field
205
+ }))
206
+ .commands({
207
+ takeDamage: (state, amount: number) => {
208
+ state.health.current -= amount;
209
+ },
210
+ })
211
+ .build();
212
+ ```
213
+
214
+ When an old entity loads, Cue automatically runs upcasters to migrate its state.
215
+
216
+ ## Temporal Scrubbing
217
+
218
+ Query historical state at any event version with full type safety:
219
+
220
+ ```typescript
221
+ import { create, define, type HistoryOf, type VersionState } from "cue";
222
+
223
+ const Character = define("Character")
224
+ .initialState(() => ({ hp: 100 }))
225
+ .evolve((v1) => ({ health: { current: v1.hp, max: 100 } }))
226
+ .evolve((v2) => ({ ...v2, mana: 50 }))
227
+ .commands({
228
+ damage: (s, amount: number) => {
229
+ s.health.current -= amount;
230
+ },
231
+ })
232
+ .build();
233
+
234
+ const app = create({ definition: Character, store: new memoryStore() });
235
+ const hero = app.get("hero-1");
236
+
237
+ // Make some changes
238
+ await hero.send.damage(10);
239
+ await hero.send.damage(5);
240
+
241
+ // Query historical state
242
+ const atV1 = await hero.stateAt(1n);
243
+ console.log(atV1.schemaVersion); // 3
244
+ console.log(atV1.state.health.current); // 90
245
+ ```
246
+
247
+ ### Type-Safe History
248
+
249
+ The return type of `stateAt()` is a discriminated union of all schema versions:
250
+
251
+ ```typescript
252
+ // Extract the full history union
253
+ type CharacterHistory = HistoryOf<typeof Character>;
254
+ // | { schemaVersion: 1; state: { hp: number } }
255
+ // | { schemaVersion: 2; state: { health: { current: number; max: number } } }
256
+ // | { schemaVersion: 3; state: { health: { current: number; max: number }; mana: number } }
257
+
258
+ // Extract a specific version's state type
259
+ type CharacterV1 = VersionState<typeof Character, 1>; // { hp: number }
260
+ type CharacterV2 = VersionState<typeof Character, 2>; // { health: { current: number; max: number } }
261
+
262
+ // TypeScript narrows correctly in switch statements
263
+ function renderHistorical(h: CharacterHistory) {
264
+ switch (h.schemaVersion) {
265
+ case 1:
266
+ return `HP: ${h.state.hp}`;
267
+ case 2:
268
+ return `Health: ${h.state.health.current}/${h.state.health.max}`;
269
+ case 3:
270
+ return `Health: ${h.state.health.current}, Mana: ${h.state.mana}`;
271
+ }
272
+ }
273
+ ```
274
+
275
+ This enables building debug UIs, audit logs, and replay systems with compile-time guarantees.
276
+
277
+ ## Supervision
278
+
279
+ Handle errors declaratively:
280
+
281
+ ```typescript
282
+ import { create, supervisor } from "cue";
283
+
284
+ const mySupervisor = supervisor({
285
+ stop: (_state, err) => err.name === "CatastrophicError",
286
+ reset: (_state, err) => err.name === "CorruptionError",
287
+ resume: (_state, err) => err.name === "ValidationError",
288
+ default: "resume",
289
+ });
290
+
291
+ const manager = create({
292
+ definition: MyEntity,
293
+ supervisor: mySupervisor,
294
+ });
295
+ ```
296
+
297
+ Strategies:
298
+
299
+ - **resume**: Error bubbles up, entity stays healthy
300
+ - **reset**: Clear persisted history, reinitialize state
301
+ - **stop**: Entity enters failed state, rejects all future messages
302
+
303
+ ## Passivation
304
+
305
+ Automatically evict idle entities to save memory:
306
+
307
+ ```typescript
308
+ const manager = create({
309
+ definition: MyEntity,
310
+ store: myStore,
311
+ passivation: {
312
+ idleAfter: 5 * 60 * 1000, // Evict after 5 minutes
313
+ sweepInterval: 60 * 1000, // Check every minute
314
+ },
315
+ });
316
+ ```
317
+
318
+ Entities rehydrate transparently when accessed again.
319
+
320
+ ## Metrics
321
+
322
+ Hook into entity lifecycle events:
323
+
324
+ ```typescript
325
+ const manager = create({
326
+ definition: MyEntity,
327
+ metrics: {
328
+ onHydrate: (id) => console.log(`${id} hydrated`),
329
+ onEvict: (id) => console.log(`${id} evicted`),
330
+ onError: (id, error) => console.error(`${id} failed:`, error),
331
+ onSnapshot: (id, version) => console.log(`${id} snapshot at v${version}`),
332
+ onAfterCommit: (id, version, patch) => {
333
+ /* ... */
334
+ },
335
+ onBeforeSnapshot: (id, version) => {
336
+ /* ... */
337
+ },
338
+ onHydrateFallback: (id, reason) => {
339
+ /* ... */
340
+ },
341
+ },
342
+ });
343
+ ```
344
+
345
+ ## Built-in Serialization
346
+
347
+ Cue uses SuperJSON under the hood, so `Date`, `Map`, `Set`, `BigInt`, and `RegExp` values survive serialization automatically.
348
+
349
+ ## How It Works
350
+
351
+ Under the hood, Cue uses **event sourcing**. Every state change is recorded as a patch. Entities rebuild from their history on load, with periodic snapshots for efficiency.
352
+
353
+ But you don't need to think about events. Write mutations directly—Cue captures them automatically via Immer.
354
+
355
+ ## API Reference
356
+
357
+ ### `define(name)`
358
+
359
+ Creates a new entity definition builder.
360
+
361
+ ```typescript
362
+ const Entity = define("Entity")
363
+ .initialState(() => ({ ... }))
364
+ .commands({ ... })
365
+ .queries({ ... })
366
+ .evolve((prev) => ({ ... }))
367
+ .persistence({ snapshotEvery: 100 })
368
+ .build();
369
+ ```
370
+
371
+ ### `create(config)`
372
+
373
+ Creates an entity manager.
374
+
375
+ ```typescript
376
+ const manager = create({
377
+ definition: Entity, // Required: entity definition
378
+ store: new memoryStore(), // Optional: persistence adapter
379
+ supervisor: mySupervisor, // Optional: error handling
380
+ passivation: { ... }, // Optional: idle eviction
381
+ metrics: { ... }, // Optional: lifecycle hooks
382
+ });
383
+ ```
384
+
385
+ ### `EntityRef`
386
+
387
+ ```typescript
388
+ const ref = manager.get("id");
389
+
390
+ ref.send.command(...args); // Execute command, returns Promise
391
+ ref.read.query(...args); // Execute query, returns Promise
392
+ ref.stream.command(...args); // Returns AsyncIterable for streaming commands
393
+ ref.snapshot(); // Returns Promise<{ state, version }>
394
+ ref.stateAt(eventVersion); // Returns Promise<{ schemaVersion, state }>
395
+ ref.stop(); // Stop and release this entity
396
+ ```
397
+
398
+ ### Type Utilities
399
+
400
+ ```typescript
401
+ import { HistoryOf, VersionState, StateOf } from "cue";
402
+
403
+ type History = HistoryOf<typeof Entity>; // Discriminated union of all versions
404
+ type V2State = VersionState<typeof Entity, 2>; // State type at schema version 2
405
+ type Current = StateOf<typeof Entity>; // Current state type
406
+ ```
407
+
408
+ ## License
409
+
410
+ MIT License
@@ -0,0 +1,3 @@
1
+ import type { AnyEntityDefinition, EntityManager, EntityManagerConfig } from "../types/public";
2
+ export declare function create<TDef extends AnyEntityDefinition>(config: EntityManagerConfig<TDef>): EntityManager<TDef>;
3
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/api/create.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,mBAAmB,EACnB,aAAa,EACb,mBAAmB,EAOpB,MAAM,iBAAiB,CAAC;AAEzB,wBAAgB,MAAM,CAAC,IAAI,SAAS,mBAAmB,EACrD,MAAM,EAAE,mBAAmB,CAAC,IAAI,CAAC,GAChC,aAAa,CAAC,IAAI,CAAC,CAkJrB"}
@@ -0,0 +1,106 @@
1
+ import { ManagerShutdownError } from "../errors";
2
+ import { RuntimeEntityManager } from "../runtime/EntityManager";
3
+ export function create(config) {
4
+ const manager = new RuntimeEntityManager(config);
5
+ const entries = new Map();
6
+ function makeSendProxy(id, getEntity, setEntity) {
7
+ return new Proxy({}, {
8
+ get(_t, prop) {
9
+ return async (...args) => {
10
+ if (manager.isShutdown) {
11
+ throw new ManagerShutdownError("EntityManager is shut down. Cannot interact with entities.");
12
+ }
13
+ let entity = getEntity();
14
+ if (entity.isShutdown) {
15
+ entity = manager.getEntity(id);
16
+ setEntity(entity);
17
+ }
18
+ return entity.tell(prop, args);
19
+ };
20
+ },
21
+ });
22
+ }
23
+ function makeReadProxy(id, getEntity, setEntity) {
24
+ return new Proxy({}, {
25
+ get(_t, prop) {
26
+ return async (...args) => {
27
+ if (manager.isShutdown) {
28
+ throw new ManagerShutdownError("EntityManager is shut down. Cannot interact with entities.");
29
+ }
30
+ let entity = getEntity();
31
+ if (entity.isShutdown) {
32
+ entity = manager.getEntity(id);
33
+ setEntity(entity);
34
+ }
35
+ return entity.ask(prop, args);
36
+ };
37
+ },
38
+ });
39
+ }
40
+ function makeStreamProxy(id, getEntity, setEntity) {
41
+ return new Proxy({}, {
42
+ get(_t, prop) {
43
+ return (...args) => {
44
+ if (manager.isShutdown) {
45
+ throw new ManagerShutdownError("EntityManager is shut down. Cannot interact with entities.");
46
+ }
47
+ let entity = getEntity();
48
+ if (entity.isShutdown) {
49
+ entity = manager.getEntity(id);
50
+ setEntity(entity);
51
+ }
52
+ return entity.stream(prop, args);
53
+ };
54
+ },
55
+ });
56
+ }
57
+ return {
58
+ get(id) {
59
+ const existing = entries.get(id);
60
+ if (existing && !existing.entity.isFailed && !existing.entity.isShutdown) {
61
+ return existing.ref;
62
+ }
63
+ let entity = manager.getEntity(id);
64
+ let entry;
65
+ const getEntity = () => entity;
66
+ const setEntity = (e) => {
67
+ entity = e;
68
+ entry.entity = e;
69
+ };
70
+ const ref = {
71
+ send: makeSendProxy(id, getEntity, setEntity),
72
+ read: makeReadProxy(id, getEntity, setEntity),
73
+ stream: makeStreamProxy(id, getEntity, setEntity),
74
+ snapshot: async () => {
75
+ if (manager.isShutdown) {
76
+ throw new ManagerShutdownError("EntityManager is shut down. Cannot interact with entities.");
77
+ }
78
+ if (entity.isShutdown) {
79
+ entity = manager.getEntity(id);
80
+ setEntity(entity);
81
+ }
82
+ return entity.inspect();
83
+ },
84
+ stateAt: async (eventVersion) => {
85
+ if (manager.isShutdown) {
86
+ throw new ManagerShutdownError("EntityManager is shut down. Cannot interact with entities.");
87
+ }
88
+ if (entity.isShutdown) {
89
+ entity = manager.getEntity(id);
90
+ setEntity(entity);
91
+ }
92
+ return entity.stateAt(eventVersion);
93
+ },
94
+ stop: async () => {
95
+ await entity.terminate();
96
+ manager.removeEntity(id);
97
+ entries.delete(id);
98
+ },
99
+ };
100
+ entry = { ref, entity };
101
+ entries.set(id, entry);
102
+ return ref;
103
+ },
104
+ stop: () => manager.terminate(),
105
+ };
106
+ }
@@ -0,0 +1,40 @@
1
+ import { _handlers, _initialStateFn, _messages, _name, _persistence, _state, _tag, _upcasters, _versions } from "../types/internal";
2
+ import type { AnyHandler, CreateMessageMap, Draft, HandlerEntry } from "../types/public";
3
+ declare class DefinitionBuilder<TName extends string, TState extends object, TCommands extends Record<string, AnyHandler> = Record<string, never>, TQueries extends Record<string, AnyHandler> = Record<string, never>, TVersions extends object[] = [TState]> {
4
+ private readonly name;
5
+ private readonly initialStateFn;
6
+ private readonly upcasters;
7
+ private readonly commandsConfig;
8
+ private readonly queriesConfig;
9
+ private persistenceConfig?;
10
+ constructor(name: TName, initialStateFn: () => TVersions[0], upcasters: ReadonlyArray<(prevState: any) => any>, commandsConfig: TCommands, queriesConfig: TQueries, persistenceConfig?: {
11
+ snapshotEvery?: number;
12
+ } | undefined);
13
+ evolve<TNewState extends object>(upcaster: (prevState: TState) => TNewState): DefinitionBuilder<TName, TNewState, Record<string, never>, Record<string, never>, [
14
+ ...TVersions,
15
+ TNewState
16
+ ]>;
17
+ commands<const C extends Record<string, (state: Draft<TState>, ...args: any[]) => unknown>>(newCommands: C): DefinitionBuilder<TName, TState, TCommands & C, TQueries, TVersions>;
18
+ queries<const Q extends Record<string, (state: Readonly<TState>, ...args: any[]) => unknown>>(newQueries: Q): DefinitionBuilder<TName, TState, TCommands, TQueries & Q, TVersions>;
19
+ persistence(config: {
20
+ snapshotEvery?: number;
21
+ }): this;
22
+ build(): {
23
+ [_persistence]?: {
24
+ snapshotEvery?: number;
25
+ };
26
+ [_name]: TName;
27
+ [_state]: TState;
28
+ [_messages]: CreateMessageMap<TCommands & {}, TQueries & {}>;
29
+ [_tag]: "EntityDefinition";
30
+ [_initialStateFn]: () => TVersions[0];
31
+ [_upcasters]: readonly ((prevState: any) => any)[];
32
+ [_handlers]: Record<string, HandlerEntry>;
33
+ [_versions]: TVersions;
34
+ };
35
+ }
36
+ export declare function define<TName extends string>(name: TName): {
37
+ initialState<TState extends object>(initialStateFn: () => TState): DefinitionBuilder<TName, TState, Record<string, never>, Record<string, never>, [TState]>;
38
+ };
39
+ export {};
40
+ //# sourceMappingURL=define.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../src/api/define.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,eAAe,EACf,SAAS,EACT,KAAK,EACL,YAAY,EACZ,MAAM,EACN,IAAI,EACJ,UAAU,EACV,SAAS,EACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,KAAK,EACL,YAAY,EACb,MAAM,iBAAiB,CAAC;AAKzB,cAAM,iBAAiB,CACrB,KAAK,SAAS,MAAM,EACpB,MAAM,SAAS,MAAM,EACrB,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACpE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACnE,SAAS,SAAS,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC;IAGnC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAE/B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,iBAAiB,CAAC;gBANT,IAAI,EAAE,KAAK,EACX,cAAc,EAAE,MAAM,SAAS,CAAC,CAAC,CAAC,EAElC,SAAS,EAAE,aAAa,CAAC,CAAC,SAAS,EAAE,GAAG,KAAK,GAAG,CAAC,EACjD,cAAc,EAAE,SAAS,EACzB,aAAa,EAAE,QAAQ,EAChC,iBAAiB,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,YAAA;IAGjD,MAAM,CAAC,SAAS,SAAS,MAAM,EACpC,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,SAAS,GACzC,iBAAiB,CAClB,KAAK,EACL,SAAS,EACT,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACrB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACrB;QAAC,GAAG,SAAS;QAAE,SAAS;KAAC,CAC1B;IAiBM,QAAQ,CACb,KAAK,CAAC,CAAC,SAAS,MAAM,CACpB,MAAM,EAEN,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAClD,EACD,WAAW,EAAE,CAAC;IAiBT,OAAO,CACZ,KAAK,CAAC,CAAC,SAAS,MAAM,CACpB,MAAM,EAEN,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CACrD,EACD,UAAU,EAAE,CAAC;IAiBR,WAAW,CAAC,MAAM,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE;IAK9C,KAAK;;4BA/EoC,MAAM;;;kBA0FrB,MAAM;qBACH,gBAAgB,CAC9C,SAAS,GAAG,EAAE,EACd,QAAQ,GAAG,EAAE,CACd;;iCAnGoC,SAAS,CAAC,CAAC,CAAC;4CAEG,GAAG,KAAK,GAAG;;qBAsG/B,SAAS;;CAK9C;AAED,wBAAgB,MAAM,CAAC,KAAK,SAAS,MAAM,EAAE,IAAI,EAAE,KAAK;iBAEvC,MAAM,SAAS,MAAM,kBAAkB,MAAM,MAAM;EAUnE"}
@@ -0,0 +1,61 @@
1
+ import { _handlers, _initialStateFn, _messages, _name, _persistence, _state, _tag, _upcasters, _versions, } from "../types/internal";
2
+ const IsAsyncGen = (fn) => Object.prototype.toString.call(fn) === "[object AsyncGeneratorFunction]";
3
+ class DefinitionBuilder {
4
+ name;
5
+ initialStateFn;
6
+ upcasters;
7
+ commandsConfig;
8
+ queriesConfig;
9
+ persistenceConfig;
10
+ constructor(name, initialStateFn,
11
+ // biome-ignore lint/suspicious/noExplicitAny: Upcasters must handle any previous state shape
12
+ upcasters, commandsConfig, queriesConfig, persistenceConfig) {
13
+ this.name = name;
14
+ this.initialStateFn = initialStateFn;
15
+ this.upcasters = upcasters;
16
+ this.commandsConfig = commandsConfig;
17
+ this.queriesConfig = queriesConfig;
18
+ this.persistenceConfig = persistenceConfig;
19
+ }
20
+ evolve(upcaster) {
21
+ return new DefinitionBuilder(this.name, this.initialStateFn, [...this.upcasters, upcaster], {}, {}, this.persistenceConfig);
22
+ }
23
+ commands(newCommands) {
24
+ return new DefinitionBuilder(this.name, this.initialStateFn, this.upcasters, { ...this.commandsConfig, ...newCommands }, this.queriesConfig, this.persistenceConfig);
25
+ }
26
+ queries(newQueries) {
27
+ return new DefinitionBuilder(this.name, this.initialStateFn, this.upcasters, this.commandsConfig, { ...this.queriesConfig, ...newQueries }, this.persistenceConfig);
28
+ }
29
+ persistence(config) {
30
+ this.persistenceConfig = config;
31
+ return this;
32
+ }
33
+ build() {
34
+ const handlers = {};
35
+ for (const [key, fn] of Object.entries(this.commandsConfig)) {
36
+ handlers[key] = { type: IsAsyncGen(fn) ? "stream" : "command", fn };
37
+ }
38
+ for (const [key, fn] of Object.entries(this.queriesConfig)) {
39
+ handlers[key] = { type: "query", fn };
40
+ }
41
+ const definition = {
42
+ [_name]: this.name,
43
+ [_state]: null,
44
+ [_messages]: null,
45
+ [_tag]: "EntityDefinition",
46
+ [_initialStateFn]: this.initialStateFn,
47
+ [_upcasters]: this.upcasters,
48
+ [_handlers]: handlers,
49
+ [_versions]: null,
50
+ ...(this.persistenceConfig && { [_persistence]: this.persistenceConfig }),
51
+ };
52
+ return definition;
53
+ }
54
+ }
55
+ export function define(name) {
56
+ return {
57
+ initialState(initialStateFn) {
58
+ return new DefinitionBuilder(name, initialStateFn, [], {}, {});
59
+ },
60
+ };
61
+ }
@@ -0,0 +1,5 @@
1
+ export { InMemoryPersistenceAdapter } from "../persistence/adapters/inMemory";
2
+ export { create } from "./create";
3
+ export { define } from "./define";
4
+ export { supervisor } from "./supervisor";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { enableMapSet, enablePatches } from "immer";
2
+ enableMapSet();
3
+ enablePatches();
4
+ export { InMemoryPersistenceAdapter } from "../persistence/adapters/inMemory";
5
+ export { create } from "./create";
6
+ export { define } from "./define";
7
+ export { supervisor } from "./supervisor";