effect-machine 0.3.1 → 0.4.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 (62) hide show
  1. package/README.md +24 -0
  2. package/dist/_virtual/_rolldown/runtime.js +18 -0
  3. package/dist/actor.d.ts +256 -0
  4. package/dist/actor.js +402 -0
  5. package/dist/cluster/entity-machine.d.ts +90 -0
  6. package/dist/cluster/entity-machine.js +80 -0
  7. package/dist/cluster/index.d.ts +3 -0
  8. package/dist/cluster/index.js +4 -0
  9. package/dist/cluster/to-entity.d.ts +64 -0
  10. package/dist/cluster/to-entity.js +53 -0
  11. package/dist/errors.d.ts +61 -0
  12. package/dist/errors.js +38 -0
  13. package/dist/index.d.ts +13 -0
  14. package/dist/index.js +14 -0
  15. package/dist/inspection.d.ts +125 -0
  16. package/dist/inspection.js +50 -0
  17. package/dist/internal/brands.d.ts +40 -0
  18. package/dist/internal/brands.js +0 -0
  19. package/dist/internal/inspection.d.ts +11 -0
  20. package/dist/internal/inspection.js +15 -0
  21. package/dist/internal/transition.d.ts +160 -0
  22. package/dist/internal/transition.js +238 -0
  23. package/dist/internal/utils.d.ts +60 -0
  24. package/dist/internal/utils.js +46 -0
  25. package/dist/machine.d.ts +278 -0
  26. package/dist/machine.js +317 -0
  27. package/{src/persistence/adapter.ts → dist/persistence/adapter.d.ts} +40 -72
  28. package/dist/persistence/adapter.js +27 -0
  29. package/dist/persistence/adapters/in-memory.d.ts +32 -0
  30. package/dist/persistence/adapters/in-memory.js +176 -0
  31. package/dist/persistence/index.d.ts +5 -0
  32. package/dist/persistence/index.js +6 -0
  33. package/dist/persistence/persistent-actor.d.ts +50 -0
  34. package/dist/persistence/persistent-actor.js +358 -0
  35. package/{src/persistence/persistent-machine.ts → dist/persistence/persistent-machine.d.ts} +28 -54
  36. package/dist/persistence/persistent-machine.js +24 -0
  37. package/dist/schema.d.ts +141 -0
  38. package/dist/schema.js +165 -0
  39. package/dist/slot.d.ts +130 -0
  40. package/dist/slot.js +99 -0
  41. package/dist/testing.d.ts +142 -0
  42. package/dist/testing.js +138 -0
  43. package/package.json +28 -14
  44. package/src/actor.ts +0 -1058
  45. package/src/cluster/entity-machine.ts +0 -201
  46. package/src/cluster/index.ts +0 -43
  47. package/src/cluster/to-entity.ts +0 -99
  48. package/src/errors.ts +0 -64
  49. package/src/index.ts +0 -105
  50. package/src/inspection.ts +0 -178
  51. package/src/internal/brands.ts +0 -51
  52. package/src/internal/inspection.ts +0 -18
  53. package/src/internal/transition.ts +0 -489
  54. package/src/internal/utils.ts +0 -80
  55. package/src/machine.ts +0 -836
  56. package/src/persistence/adapters/in-memory.ts +0 -294
  57. package/src/persistence/index.ts +0 -24
  58. package/src/persistence/persistent-actor.ts +0 -791
  59. package/src/schema.ts +0 -362
  60. package/src/slot.ts +0 -281
  61. package/src/testing.ts +0 -284
  62. package/tsconfig.json +0 -65
package/README.md CHANGED
@@ -183,6 +183,29 @@ machine.task(State.Loading, ({ effects, state }) => effects.fetchData({ url: sta
183
183
  });
184
184
  ```
185
185
 
186
+ ### Child Actors
187
+
188
+ Spawn children from `.spawn()` handlers with `self.spawn`. Children are state-scoped — auto-stopped on state exit:
189
+
190
+ ```ts
191
+ machine
192
+ .spawn(State.Active, ({ self }) =>
193
+ Effect.gen(function* () {
194
+ const child = yield* self.spawn("worker-1", workerMachine).pipe(Effect.orDie);
195
+ yield* child.send(WorkerEvent.Start);
196
+ // child auto-stopped when parent exits Active state
197
+ }),
198
+ )
199
+ .build();
200
+
201
+ // Access children externally via actor.system
202
+ const parent = yield * Machine.spawn(parentMachine);
203
+ yield * parent.send(Event.Activate);
204
+ const child = yield * parent.system.get("worker-1"); // Option<ActorRef>
205
+ ```
206
+
207
+ Every actor always has a system — `Machine.spawn` creates an implicit one if no `ActorSystem` is in context.
208
+
186
209
  ### Testing
187
210
 
188
211
  Test transitions without actors:
@@ -273,6 +296,7 @@ See the [primer](./primer/) for comprehensive documentation:
273
296
  | `actor.awaitFinal` | Wait final state |
274
297
  | `actor.sendAndWait(ev, State.X)` | Send + wait for state |
275
298
  | `actor.subscribe(fn)` | Sync callback |
299
+ | `actor.system` | Access the actor's `ActorSystem` |
276
300
 
277
301
  ## License
278
302
 
@@ -0,0 +1,18 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) {
6
+ __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true
9
+ });
10
+ }
11
+ if (!no_symbols) {
12
+ __defProp(target, Symbol.toStringTag, { value: "Module" });
13
+ }
14
+ return target;
15
+ };
16
+
17
+ //#endregion
18
+ export { __exportAll };
@@ -0,0 +1,256 @@
1
+ import { EffectsDef, GuardsDef, MachineContext } from "./slot.js";
2
+ import { PersistentMachine } from "./persistence/persistent-machine.js";
3
+ import { DuplicateActorError } from "./errors.js";
4
+ import { ProcessEventError, ProcessEventHooks, ProcessEventResult, processEventCore, resolveTransition, runSpawnEffects } from "./internal/transition.js";
5
+ import { PersistentActorRef } from "./persistence/persistent-actor.js";
6
+ import { ActorMetadata, PersistenceAdapterTag, PersistenceError, RestoreResult, VersionConflictError } from "./persistence/adapter.js";
7
+ import { BuiltMachine, Machine, MachineRef } from "./machine.js";
8
+ import { Context, Effect, Layer, Option, Queue, Ref, Scope, Stream, SubscriptionRef } from "effect";
9
+ import * as effect_Tracer0 from "effect/Tracer";
10
+
11
+ //#region src/actor.d.ts
12
+ /**
13
+ * Reference to a running actor.
14
+ */
15
+ interface ActorRef<State extends {
16
+ readonly _tag: string;
17
+ }, Event> {
18
+ /**
19
+ * Unique identifier for this actor
20
+ */
21
+ readonly id: string;
22
+ /**
23
+ * Send an event to the actor
24
+ */
25
+ readonly send: (event: Event) => Effect.Effect<void>;
26
+ /**
27
+ * Observable state of the actor
28
+ */
29
+ readonly state: SubscriptionRef.SubscriptionRef<State>;
30
+ /**
31
+ * Stop the actor gracefully
32
+ */
33
+ readonly stop: Effect.Effect<void>;
34
+ /**
35
+ * Stop the actor (fire-and-forget).
36
+ * Signals graceful shutdown without waiting for completion.
37
+ * Use when stopping from sync contexts (e.g. framework cleanup hooks).
38
+ */
39
+ readonly stopSync: () => void;
40
+ /**
41
+ * Get current state snapshot (Effect)
42
+ */
43
+ readonly snapshot: Effect.Effect<State>;
44
+ /**
45
+ * Get current state snapshot (sync)
46
+ */
47
+ readonly snapshotSync: () => State;
48
+ /**
49
+ * Check if current state matches tag (Effect)
50
+ */
51
+ readonly matches: (tag: State["_tag"]) => Effect.Effect<boolean>;
52
+ /**
53
+ * Check if current state matches tag (sync)
54
+ */
55
+ readonly matchesSync: (tag: State["_tag"]) => boolean;
56
+ /**
57
+ * Check if event can be handled in current state (Effect)
58
+ */
59
+ readonly can: (event: Event) => Effect.Effect<boolean>;
60
+ /**
61
+ * Check if event can be handled in current state (sync)
62
+ */
63
+ readonly canSync: (event: Event) => boolean;
64
+ /**
65
+ * Stream of state changes
66
+ */
67
+ readonly changes: Stream.Stream<State>;
68
+ /**
69
+ * Wait for a state that matches predicate or state variant (includes current snapshot).
70
+ * Accepts a predicate function or a state constructor/value (e.g. `State.Active`).
71
+ */
72
+ readonly waitFor: {
73
+ (predicate: (state: State) => boolean): Effect.Effect<State>;
74
+ (state: {
75
+ readonly _tag: State["_tag"];
76
+ }): Effect.Effect<State>;
77
+ };
78
+ /**
79
+ * Wait for a final state (includes current snapshot)
80
+ */
81
+ readonly awaitFinal: Effect.Effect<State>;
82
+ /**
83
+ * Send event and wait for predicate, state variant, or final state.
84
+ * Accepts a predicate function or a state constructor/value (e.g. `State.Active`).
85
+ */
86
+ readonly sendAndWait: {
87
+ (event: Event, predicate: (state: State) => boolean): Effect.Effect<State>;
88
+ (event: Event, state: {
89
+ readonly _tag: State["_tag"];
90
+ }): Effect.Effect<State>;
91
+ (event: Event): Effect.Effect<State>;
92
+ };
93
+ /**
94
+ * Send event synchronously (fire-and-forget).
95
+ * No-op on stopped actors. Use when you need to send from sync contexts
96
+ * (e.g. framework hooks, event handlers).
97
+ */
98
+ readonly sendSync: (event: Event) => void;
99
+ /**
100
+ * Subscribe to state changes (sync callback)
101
+ * Returns unsubscribe function
102
+ */
103
+ readonly subscribe: (fn: (state: State) => void) => () => void;
104
+ /**
105
+ * The actor system this actor belongs to.
106
+ * Every actor always has a system — either inherited from context or implicitly created.
107
+ */
108
+ readonly system: ActorSystem;
109
+ }
110
+ /** Base type for stored actors (internal) */
111
+ type AnyState = {
112
+ readonly _tag: string;
113
+ };
114
+ /**
115
+ * Actor system for managing actor lifecycles
116
+ */
117
+ interface ActorSystem {
118
+ /**
119
+ * Spawn a new actor with the given machine.
120
+ *
121
+ * For regular machines, returns ActorRef.
122
+ * For persistent machines (created with Machine.persist), returns PersistentActorRef.
123
+ *
124
+ * All effect slots must be provided via `.build()` before spawning.
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * // Regular machine (built)
129
+ * const built = machine.build({ fetchData: ... })
130
+ * const actor = yield* system.spawn("my-actor", built);
131
+ *
132
+ * // Persistent machine (auto-detected)
133
+ * const persistentActor = yield* system.spawn("my-actor", persistentMachine);
134
+ * persistentActor.persist; // available
135
+ * persistentActor.version; // available
136
+ * ```
137
+ */
138
+ readonly spawn: {
139
+ <S extends {
140
+ readonly _tag: string;
141
+ }, E extends {
142
+ readonly _tag: string;
143
+ }, R>(id: string, machine: BuiltMachine<S, E, R>): Effect.Effect<ActorRef<S, E>, DuplicateActorError, R>;
144
+ <S extends {
145
+ readonly _tag: string;
146
+ }, E extends {
147
+ readonly _tag: string;
148
+ }, R>(id: string, machine: PersistentMachine<S, E, R>): Effect.Effect<PersistentActorRef<S, E, R>, PersistenceError | VersionConflictError | DuplicateActorError, R | PersistenceAdapterTag>;
149
+ };
150
+ /**
151
+ * Restore an actor from persistence.
152
+ * Returns None if no persisted state exists for the given ID.
153
+ *
154
+ * @example
155
+ * ```ts
156
+ * const maybeActor = yield* system.restore("order-1", persistentMachine);
157
+ * if (Option.isSome(maybeActor)) {
158
+ * const actor = maybeActor.value;
159
+ * const state = yield* actor.snapshot;
160
+ * console.log(`Restored to state: ${state._tag}`);
161
+ * }
162
+ * ```
163
+ */
164
+ readonly restore: <S extends {
165
+ readonly _tag: string;
166
+ }, E extends {
167
+ readonly _tag: string;
168
+ }, R>(id: string, machine: PersistentMachine<S, E, R>) => Effect.Effect<Option.Option<PersistentActorRef<S, E, R>>, PersistenceError | DuplicateActorError, R | PersistenceAdapterTag>;
169
+ /**
170
+ * Get an existing actor by ID
171
+ */
172
+ readonly get: (id: string) => Effect.Effect<Option.Option<ActorRef<AnyState, unknown>>>;
173
+ /**
174
+ * Stop an actor by ID
175
+ */
176
+ readonly stop: (id: string) => Effect.Effect<boolean>;
177
+ /**
178
+ * List all persisted actor metadata.
179
+ * Returns empty array if adapter doesn't support registry.
180
+ *
181
+ * @example
182
+ * ```ts
183
+ * const actors = yield* system.listPersisted();
184
+ * for (const meta of actors) {
185
+ * console.log(`${meta.id}: ${meta.stateTag} (v${meta.version})`);
186
+ * }
187
+ * ```
188
+ */
189
+ readonly listPersisted: () => Effect.Effect<ReadonlyArray<ActorMetadata>, PersistenceError, PersistenceAdapterTag>;
190
+ /**
191
+ * Restore multiple actors by ID.
192
+ * Returns both successfully restored actors and failures.
193
+ *
194
+ * @example
195
+ * ```ts
196
+ * const result = yield* system.restoreMany(["order-1", "order-2"], orderMachine);
197
+ * console.log(`Restored: ${result.restored.length}, Failed: ${result.failed.length}`);
198
+ * ```
199
+ */
200
+ readonly restoreMany: <S extends {
201
+ readonly _tag: string;
202
+ }, E extends {
203
+ readonly _tag: string;
204
+ }, R>(ids: ReadonlyArray<string>, machine: PersistentMachine<S, E, R>) => Effect.Effect<RestoreResult<S, E, R>, never, R | PersistenceAdapterTag>;
205
+ /**
206
+ * Restore all persisted actors for a machine type.
207
+ * Uses adapter registry if available, otherwise returns empty result.
208
+ *
209
+ * @example
210
+ * ```ts
211
+ * const result = yield* system.restoreAll(orderMachine, {
212
+ * filter: (meta) => meta.stateTag !== "Done"
213
+ * });
214
+ * console.log(`Restored ${result.restored.length} active orders`);
215
+ * ```
216
+ */
217
+ readonly restoreAll: <S extends {
218
+ readonly _tag: string;
219
+ }, E extends {
220
+ readonly _tag: string;
221
+ }, R>(machine: PersistentMachine<S, E, R>, options?: {
222
+ filter?: (meta: ActorMetadata) => boolean;
223
+ }) => Effect.Effect<RestoreResult<S, E, R>, PersistenceError, R | PersistenceAdapterTag>;
224
+ }
225
+ /**
226
+ * ActorSystem service tag
227
+ */
228
+ declare const ActorSystem: Context.Tag<ActorSystem, ActorSystem>;
229
+ /** Listener set for sync subscriptions */
230
+ type Listeners<S> = Set<(state: S) => void>;
231
+ /**
232
+ * Notify all listeners of state change.
233
+ */
234
+ declare const notifyListeners: <S>(listeners: Listeners<S>, state: S) => void;
235
+ /**
236
+ * Build core ActorRef methods shared between regular and persistent actors.
237
+ */
238
+ declare const buildActorRefCore: <S extends {
239
+ readonly _tag: string;
240
+ }, E extends {
241
+ readonly _tag: string;
242
+ }, R, GD extends GuardsDef, EFD extends EffectsDef>(id: string, machine: Machine<S, E, R, any, any, GD, EFD>, stateRef: SubscriptionRef.SubscriptionRef<S>, eventQueue: Queue.Queue<E>, stoppedRef: Ref.Ref<boolean>, listeners: Listeners<S>, stop: Effect.Effect<void>, system: ActorSystem) => ActorRef<S, E>;
243
+ /**
244
+ * Create and start an actor for a machine
245
+ */
246
+ declare const createActor: <S extends {
247
+ readonly _tag: string;
248
+ }, E extends {
249
+ readonly _tag: string;
250
+ }, R, GD extends GuardsDef, EFD extends EffectsDef>(id: string, machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>) => Effect.Effect<ActorRef<S, E>, never, Exclude<R, MachineContext<S, E, MachineRef<E>>> | Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, effect_Tracer0.ParentSpan> | Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope> | Exclude<Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>, effect_Tracer0.ParentSpan>>;
251
+ /**
252
+ * Default ActorSystem layer
253
+ */
254
+ declare const Default: Layer.Layer<ActorSystem, never, never>;
255
+ //#endregion
256
+ export { ActorRef, ActorSystem, Default, Listeners, type ProcessEventError, type ProcessEventHooks, type ProcessEventResult, buildActorRefCore, createActor, notifyListeners, processEventCore, resolveTransition, runSpawnEffects };