effect-machine 0.3.0 → 0.3.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 (61) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +18 -0
  2. package/dist/actor.d.ts +251 -0
  3. package/dist/actor.js +385 -0
  4. package/dist/cluster/entity-machine.d.ts +90 -0
  5. package/dist/cluster/entity-machine.js +74 -0
  6. package/dist/cluster/index.d.ts +3 -0
  7. package/dist/cluster/index.js +4 -0
  8. package/dist/cluster/to-entity.d.ts +64 -0
  9. package/dist/cluster/to-entity.js +53 -0
  10. package/dist/errors.d.ts +61 -0
  11. package/dist/errors.js +38 -0
  12. package/dist/index.d.ts +13 -0
  13. package/dist/index.js +14 -0
  14. package/dist/inspection.d.ts +125 -0
  15. package/dist/inspection.js +50 -0
  16. package/dist/internal/brands.d.ts +40 -0
  17. package/dist/internal/brands.js +0 -0
  18. package/dist/internal/inspection.d.ts +11 -0
  19. package/dist/internal/inspection.js +15 -0
  20. package/dist/internal/transition.d.ts +159 -0
  21. package/dist/internal/transition.js +235 -0
  22. package/dist/internal/utils.d.ts +52 -0
  23. package/dist/internal/utils.js +31 -0
  24. package/dist/machine.d.ts +271 -0
  25. package/dist/machine.js +317 -0
  26. package/{src/persistence/adapter.ts → dist/persistence/adapter.d.ts} +40 -72
  27. package/dist/persistence/adapter.js +27 -0
  28. package/dist/persistence/adapters/in-memory.d.ts +32 -0
  29. package/dist/persistence/adapters/in-memory.js +176 -0
  30. package/dist/persistence/index.d.ts +5 -0
  31. package/dist/persistence/index.js +6 -0
  32. package/dist/persistence/persistent-actor.d.ts +50 -0
  33. package/dist/persistence/persistent-actor.js +348 -0
  34. package/{src/persistence/persistent-machine.ts → dist/persistence/persistent-machine.d.ts} +28 -54
  35. package/dist/persistence/persistent-machine.js +24 -0
  36. package/dist/schema.d.ts +141 -0
  37. package/dist/schema.js +165 -0
  38. package/dist/slot.d.ts +128 -0
  39. package/dist/slot.js +99 -0
  40. package/dist/testing.d.ts +142 -0
  41. package/dist/testing.js +131 -0
  42. package/package.json +18 -7
  43. package/src/actor.ts +0 -1050
  44. package/src/cluster/entity-machine.ts +0 -201
  45. package/src/cluster/index.ts +0 -43
  46. package/src/cluster/to-entity.ts +0 -99
  47. package/src/errors.ts +0 -64
  48. package/src/index.ts +0 -105
  49. package/src/inspection.ts +0 -178
  50. package/src/internal/brands.ts +0 -51
  51. package/src/internal/inspection.ts +0 -18
  52. package/src/internal/transition.ts +0 -489
  53. package/src/internal/utils.ts +0 -80
  54. package/src/machine.ts +0 -836
  55. package/src/persistence/adapters/in-memory.ts +0 -294
  56. package/src/persistence/index.ts +0 -24
  57. package/src/persistence/persistent-actor.ts +0 -791
  58. package/src/schema.ts +0 -362
  59. package/src/slot.ts +0 -281
  60. package/src/testing.ts +0 -284
  61. package/tsconfig.json +0 -65
@@ -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,251 @@
1
+ import { EffectsDef, GuardsDef, MachineContext } from "./slot.js";
2
+ import { PersistentMachine } from "./persistence/persistent-machine.js";
3
+ import { ProcessEventError, ProcessEventHooks, ProcessEventResult, processEventCore, resolveTransition, runSpawnEffects } from "./internal/transition.js";
4
+ import { PersistentActorRef } from "./persistence/persistent-actor.js";
5
+ import { DuplicateActorError } from "./errors.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
+ /** Base type for stored actors (internal) */
106
+ type AnyState = {
107
+ readonly _tag: string;
108
+ };
109
+ /**
110
+ * Actor system for managing actor lifecycles
111
+ */
112
+ interface ActorSystem {
113
+ /**
114
+ * Spawn a new actor with the given machine.
115
+ *
116
+ * For regular machines, returns ActorRef.
117
+ * For persistent machines (created with Machine.persist), returns PersistentActorRef.
118
+ *
119
+ * All effect slots must be provided via `.build()` before spawning.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * // Regular machine (built)
124
+ * const built = machine.build({ fetchData: ... })
125
+ * const actor = yield* system.spawn("my-actor", built);
126
+ *
127
+ * // Persistent machine (auto-detected)
128
+ * const persistentActor = yield* system.spawn("my-actor", persistentMachine);
129
+ * persistentActor.persist; // available
130
+ * persistentActor.version; // available
131
+ * ```
132
+ */
133
+ readonly spawn: {
134
+ <S extends {
135
+ readonly _tag: string;
136
+ }, E extends {
137
+ readonly _tag: string;
138
+ }, R>(id: string, machine: BuiltMachine<S, E, R>): Effect.Effect<ActorRef<S, E>, DuplicateActorError, R>;
139
+ <S extends {
140
+ readonly _tag: string;
141
+ }, E extends {
142
+ readonly _tag: string;
143
+ }, R>(id: string, machine: PersistentMachine<S, E, R>): Effect.Effect<PersistentActorRef<S, E, R>, PersistenceError | VersionConflictError | DuplicateActorError, R | PersistenceAdapterTag>;
144
+ };
145
+ /**
146
+ * Restore an actor from persistence.
147
+ * Returns None if no persisted state exists for the given ID.
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * const maybeActor = yield* system.restore("order-1", persistentMachine);
152
+ * if (Option.isSome(maybeActor)) {
153
+ * const actor = maybeActor.value;
154
+ * const state = yield* actor.snapshot;
155
+ * console.log(`Restored to state: ${state._tag}`);
156
+ * }
157
+ * ```
158
+ */
159
+ readonly restore: <S extends {
160
+ readonly _tag: string;
161
+ }, E extends {
162
+ readonly _tag: string;
163
+ }, R>(id: string, machine: PersistentMachine<S, E, R>) => Effect.Effect<Option.Option<PersistentActorRef<S, E, R>>, PersistenceError | DuplicateActorError, R | PersistenceAdapterTag>;
164
+ /**
165
+ * Get an existing actor by ID
166
+ */
167
+ readonly get: (id: string) => Effect.Effect<Option.Option<ActorRef<AnyState, unknown>>>;
168
+ /**
169
+ * Stop an actor by ID
170
+ */
171
+ readonly stop: (id: string) => Effect.Effect<boolean>;
172
+ /**
173
+ * List all persisted actor metadata.
174
+ * Returns empty array if adapter doesn't support registry.
175
+ *
176
+ * @example
177
+ * ```ts
178
+ * const actors = yield* system.listPersisted();
179
+ * for (const meta of actors) {
180
+ * console.log(`${meta.id}: ${meta.stateTag} (v${meta.version})`);
181
+ * }
182
+ * ```
183
+ */
184
+ readonly listPersisted: () => Effect.Effect<ReadonlyArray<ActorMetadata>, PersistenceError, PersistenceAdapterTag>;
185
+ /**
186
+ * Restore multiple actors by ID.
187
+ * Returns both successfully restored actors and failures.
188
+ *
189
+ * @example
190
+ * ```ts
191
+ * const result = yield* system.restoreMany(["order-1", "order-2"], orderMachine);
192
+ * console.log(`Restored: ${result.restored.length}, Failed: ${result.failed.length}`);
193
+ * ```
194
+ */
195
+ readonly restoreMany: <S extends {
196
+ readonly _tag: string;
197
+ }, E extends {
198
+ readonly _tag: string;
199
+ }, R>(ids: ReadonlyArray<string>, machine: PersistentMachine<S, E, R>) => Effect.Effect<RestoreResult<S, E, R>, never, R | PersistenceAdapterTag>;
200
+ /**
201
+ * Restore all persisted actors for a machine type.
202
+ * Uses adapter registry if available, otherwise returns empty result.
203
+ *
204
+ * @example
205
+ * ```ts
206
+ * const result = yield* system.restoreAll(orderMachine, {
207
+ * filter: (meta) => meta.stateTag !== "Done"
208
+ * });
209
+ * console.log(`Restored ${result.restored.length} active orders`);
210
+ * ```
211
+ */
212
+ readonly restoreAll: <S extends {
213
+ readonly _tag: string;
214
+ }, E extends {
215
+ readonly _tag: string;
216
+ }, R>(machine: PersistentMachine<S, E, R>, options?: {
217
+ filter?: (meta: ActorMetadata) => boolean;
218
+ }) => Effect.Effect<RestoreResult<S, E, R>, PersistenceError, R | PersistenceAdapterTag>;
219
+ }
220
+ /**
221
+ * ActorSystem service tag
222
+ */
223
+ declare const ActorSystem: Context.Tag<ActorSystem, ActorSystem>;
224
+ /** Listener set for sync subscriptions */
225
+ type Listeners<S> = Set<(state: S) => void>;
226
+ /**
227
+ * Notify all listeners of state change.
228
+ */
229
+ declare const notifyListeners: <S>(listeners: Listeners<S>, state: S) => void;
230
+ /**
231
+ * Build core ActorRef methods shared between regular and persistent actors.
232
+ */
233
+ declare const buildActorRefCore: <S extends {
234
+ readonly _tag: string;
235
+ }, E extends {
236
+ readonly _tag: string;
237
+ }, 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>) => ActorRef<S, E>;
238
+ /**
239
+ * Create and start an actor for a machine
240
+ */
241
+ declare const createActor: <S extends {
242
+ readonly _tag: string;
243
+ }, E extends {
244
+ readonly _tag: string;
245
+ }, 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>>;
246
+ /**
247
+ * Default ActorSystem layer
248
+ */
249
+ declare const Default: Layer.Layer<ActorSystem, never, never>;
250
+ //#endregion
251
+ export { ActorRef, ActorSystem, Default, Listeners, type ProcessEventError, type ProcessEventHooks, type ProcessEventResult, buildActorRefCore, createActor, notifyListeners, processEventCore, resolveTransition, runSpawnEffects };
package/dist/actor.js ADDED
@@ -0,0 +1,385 @@
1
+ import { Inspector } from "./inspection.js";
2
+ import { INTERNAL_INIT_EVENT } from "./internal/utils.js";
3
+ import { DuplicateActorError } from "./errors.js";
4
+ import { isPersistentMachine } from "./persistence/persistent-machine.js";
5
+ import { processEventCore, resolveTransition, runSpawnEffects } from "./internal/transition.js";
6
+ import { emitWithTimestamp } from "./internal/inspection.js";
7
+ import { PersistenceAdapterTag, PersistenceError } from "./persistence/adapter.js";
8
+ import { createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
9
+ import { Cause, Context, Deferred, Effect, Exit, Fiber, Layer, MutableHashMap, Option, Queue, Ref, Runtime, Scope, SubscriptionRef } from "effect";
10
+
11
+ //#region src/actor.ts
12
+ /**
13
+ * ActorSystem service tag
14
+ */
15
+ const ActorSystem = Context.GenericTag("@effect/machine/ActorSystem");
16
+ /**
17
+ * Notify all listeners of state change.
18
+ */
19
+ const notifyListeners = (listeners, state) => {
20
+ for (const listener of listeners) try {
21
+ listener(state);
22
+ } catch {}
23
+ };
24
+ /**
25
+ * Build core ActorRef methods shared between regular and persistent actors.
26
+ */
27
+ const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listeners, stop) => {
28
+ const send = Effect.fn("effect-machine.actor.send")(function* (event) {
29
+ if (yield* Ref.get(stoppedRef)) return;
30
+ yield* Queue.offer(eventQueue, event);
31
+ });
32
+ const snapshot = SubscriptionRef.get(stateRef).pipe(Effect.withSpan("effect-machine.actor.snapshot"));
33
+ const matches = Effect.fn("effect-machine.actor.matches")(function* (tag) {
34
+ return (yield* SubscriptionRef.get(stateRef))._tag === tag;
35
+ });
36
+ const can = Effect.fn("effect-machine.actor.can")(function* (event) {
37
+ return resolveTransition(machine, yield* SubscriptionRef.get(stateRef), event) !== void 0;
38
+ });
39
+ const waitFor = Effect.fn("effect-machine.actor.waitFor")(function* (predicateOrState) {
40
+ const predicate = typeof predicateOrState === "function" && !("_tag" in predicateOrState) ? predicateOrState : (s) => s._tag === predicateOrState._tag;
41
+ const current = yield* SubscriptionRef.get(stateRef);
42
+ if (predicate(current)) return current;
43
+ const done = yield* Deferred.make();
44
+ const rt = yield* Effect.runtime();
45
+ const runFork = Runtime.runFork(rt);
46
+ const listener = (state) => {
47
+ if (predicate(state)) runFork(Deferred.succeed(done, state));
48
+ };
49
+ listeners.add(listener);
50
+ const afterSubscribe = yield* SubscriptionRef.get(stateRef);
51
+ if (predicate(afterSubscribe)) {
52
+ listeners.delete(listener);
53
+ return afterSubscribe;
54
+ }
55
+ const result = yield* Deferred.await(done);
56
+ listeners.delete(listener);
57
+ return result;
58
+ });
59
+ const awaitFinal = waitFor((state) => machine.finalStates.has(state._tag)).pipe(Effect.withSpan("effect-machine.actor.awaitFinal"));
60
+ const sendAndWait = Effect.fn("effect-machine.actor.sendAndWait")(function* (event, predicateOrState) {
61
+ yield* send(event);
62
+ if (predicateOrState !== void 0) return yield* waitFor(predicateOrState);
63
+ return yield* awaitFinal;
64
+ });
65
+ return {
66
+ id,
67
+ send,
68
+ state: stateRef,
69
+ stop,
70
+ stopSync: () => Effect.runFork(stop),
71
+ snapshot,
72
+ snapshotSync: () => Effect.runSync(SubscriptionRef.get(stateRef)),
73
+ matches,
74
+ matchesSync: (tag) => Effect.runSync(SubscriptionRef.get(stateRef))._tag === tag,
75
+ can,
76
+ canSync: (event) => {
77
+ return resolveTransition(machine, Effect.runSync(SubscriptionRef.get(stateRef)), event) !== void 0;
78
+ },
79
+ changes: stateRef.changes,
80
+ waitFor,
81
+ awaitFinal,
82
+ sendAndWait,
83
+ sendSync: (event) => {
84
+ if (!Effect.runSync(Ref.get(stoppedRef))) Effect.runSync(Queue.offer(eventQueue, event));
85
+ },
86
+ subscribe: (fn) => {
87
+ listeners.add(fn);
88
+ return () => {
89
+ listeners.delete(fn);
90
+ };
91
+ }
92
+ };
93
+ };
94
+ /**
95
+ * Create and start an actor for a machine
96
+ */
97
+ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machine) {
98
+ yield* Effect.annotateCurrentSpan("effect_machine.actor.id", id);
99
+ const inspectorValue = Option.getOrUndefined(yield* Effect.serviceOption(Inspector));
100
+ const eventQueue = yield* Queue.unbounded();
101
+ const stoppedRef = yield* Ref.make(false);
102
+ const self = { send: Effect.fn("effect-machine.actor.self.send")(function* (event) {
103
+ if (yield* Ref.get(stoppedRef)) return;
104
+ yield* Queue.offer(eventQueue, event);
105
+ }) };
106
+ yield* Effect.annotateCurrentSpan("effect_machine.actor.initial_state", machine.initial._tag);
107
+ yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
108
+ type: "@machine.spawn",
109
+ actorId: id,
110
+ initialState: machine.initial,
111
+ timestamp
112
+ }));
113
+ const stateRef = yield* SubscriptionRef.make(machine.initial);
114
+ const listeners = /* @__PURE__ */ new Set();
115
+ const backgroundFibers = [];
116
+ const initEvent = { _tag: INTERNAL_INIT_EVENT };
117
+ const ctx = {
118
+ state: machine.initial,
119
+ event: initEvent,
120
+ self
121
+ };
122
+ const { effects: effectSlots } = machine._slots;
123
+ for (const bg of machine.backgroundEffects) {
124
+ const fiber = yield* Effect.forkDaemon(bg.handler({
125
+ state: machine.initial,
126
+ event: initEvent,
127
+ self,
128
+ effects: effectSlots
129
+ }).pipe(Effect.provideService(machine.Context, ctx)));
130
+ backgroundFibers.push(fiber);
131
+ }
132
+ const stateScopeRef = { current: yield* Scope.make() };
133
+ yield* runSpawnEffectsWithInspection(machine, machine.initial, initEvent, self, stateScopeRef.current, id, inspectorValue);
134
+ if (machine.finalStates.has(machine.initial._tag)) {
135
+ yield* Scope.close(stateScopeRef.current, Exit.void);
136
+ yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
137
+ yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
138
+ type: "@machine.stop",
139
+ actorId: id,
140
+ finalState: machine.initial,
141
+ timestamp
142
+ }));
143
+ yield* Ref.set(stoppedRef, true);
144
+ return buildActorRefCore(id, machine, stateRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid));
145
+ }
146
+ const loopFiber = yield* Effect.forkDaemon(eventLoop(machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, id, inspectorValue));
147
+ return buildActorRefCore(id, machine, stateRef, eventQueue, stoppedRef, listeners, Effect.gen(function* () {
148
+ const finalState = yield* SubscriptionRef.get(stateRef);
149
+ yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
150
+ type: "@machine.stop",
151
+ actorId: id,
152
+ finalState,
153
+ timestamp
154
+ }));
155
+ yield* Ref.set(stoppedRef, true);
156
+ yield* Fiber.interrupt(loopFiber);
157
+ yield* Scope.close(stateScopeRef.current, Exit.void);
158
+ yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
159
+ }).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid));
160
+ });
161
+ /**
162
+ * Main event loop for the actor
163
+ */
164
+ const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, actorId, inspector) {
165
+ while (true) {
166
+ const event = yield* Queue.take(eventQueue);
167
+ const currentState = yield* SubscriptionRef.get(stateRef);
168
+ if (yield* Effect.withSpan("effect-machine.event.process", { attributes: {
169
+ "effect_machine.actor.id": actorId,
170
+ "effect_machine.state.current": currentState._tag,
171
+ "effect_machine.event.type": event._tag
172
+ } })(processEvent(machine, currentState, event, stateRef, self, listeners, stateScopeRef, actorId, inspector))) {
173
+ yield* Ref.set(stoppedRef, true);
174
+ yield* Scope.close(stateScopeRef.current, Exit.void);
175
+ yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
176
+ return;
177
+ }
178
+ }
179
+ });
180
+ /**
181
+ * Process a single event, returning true if the actor should stop.
182
+ * Wraps processEventCore with actor-specific concerns (inspection, listeners, state ref).
183
+ */
184
+ const processEvent = Effect.fn("effect-machine.actor.processEvent")(function* (machine, currentState, event, stateRef, self, listeners, stateScopeRef, actorId, inspector) {
185
+ yield* emitWithTimestamp(inspector, (timestamp) => ({
186
+ type: "@machine.event",
187
+ actorId,
188
+ state: currentState,
189
+ event,
190
+ timestamp
191
+ }));
192
+ const result = yield* processEventCore(machine, currentState, event, self, stateScopeRef, inspector === void 0 ? void 0 : {
193
+ onSpawnEffect: (state) => emitWithTimestamp(inspector, (timestamp) => ({
194
+ type: "@machine.effect",
195
+ actorId,
196
+ effectType: "spawn",
197
+ state,
198
+ timestamp
199
+ })),
200
+ onTransition: (from, to, ev) => emitWithTimestamp(inspector, (timestamp) => ({
201
+ type: "@machine.transition",
202
+ actorId,
203
+ fromState: from,
204
+ toState: to,
205
+ event: ev,
206
+ timestamp
207
+ })),
208
+ onError: (info) => emitWithTimestamp(inspector, (timestamp) => ({
209
+ type: "@machine.error",
210
+ actorId,
211
+ phase: info.phase,
212
+ state: info.state,
213
+ event: info.event,
214
+ error: Cause.pretty(info.cause),
215
+ timestamp
216
+ }))
217
+ });
218
+ if (!result.transitioned) {
219
+ yield* Effect.annotateCurrentSpan("effect_machine.transition.matched", false);
220
+ return false;
221
+ }
222
+ yield* Effect.annotateCurrentSpan("effect_machine.transition.matched", true);
223
+ yield* SubscriptionRef.set(stateRef, result.newState);
224
+ notifyListeners(listeners, result.newState);
225
+ if (result.lifecycleRan) {
226
+ yield* Effect.annotateCurrentSpan("effect_machine.state.from", result.previousState._tag);
227
+ yield* Effect.annotateCurrentSpan("effect_machine.state.to", result.newState._tag);
228
+ if (result.isFinal) {
229
+ yield* emitWithTimestamp(inspector, (timestamp) => ({
230
+ type: "@machine.stop",
231
+ actorId,
232
+ finalState: result.newState,
233
+ timestamp
234
+ }));
235
+ return true;
236
+ }
237
+ }
238
+ return false;
239
+ });
240
+ /**
241
+ * Run spawn effects with actor-specific inspection and tracing.
242
+ * Wraps the core runSpawnEffects with inspection events and spans.
243
+ * @internal
244
+ */
245
+ const runSpawnEffectsWithInspection = Effect.fn("effect-machine.actor.spawnEffects")(function* (machine, state, event, self, stateScope, actorId, inspector) {
246
+ yield* emitWithTimestamp(inspector, (timestamp) => ({
247
+ type: "@machine.effect",
248
+ actorId,
249
+ effectType: "spawn",
250
+ state,
251
+ timestamp
252
+ }));
253
+ yield* runSpawnEffects(machine, state, event, self, stateScope, inspector === void 0 ? void 0 : (info) => emitWithTimestamp(inspector, (timestamp) => ({
254
+ type: "@machine.error",
255
+ actorId,
256
+ phase: info.phase,
257
+ state: info.state,
258
+ event: info.event,
259
+ error: Cause.pretty(info.cause),
260
+ timestamp
261
+ })));
262
+ });
263
+ /**
264
+ * Internal implementation
265
+ */
266
+ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
267
+ const actors = MutableHashMap.empty();
268
+ const withSpawnGate = (yield* Effect.makeSemaphore(1)).withPermits(1);
269
+ yield* Effect.addFinalizer(() => {
270
+ const stops = [];
271
+ MutableHashMap.forEach(actors, (actor) => {
272
+ stops.push(actor.stop);
273
+ });
274
+ return Effect.all(stops, { concurrency: "unbounded" }).pipe(Effect.asVoid);
275
+ });
276
+ /** Check for duplicate ID, register actor, attach scope cleanup if available */
277
+ const registerActor = Effect.fn("effect-machine.actorSystem.register")(function* (id, actor) {
278
+ if (MutableHashMap.has(actors, id)) {
279
+ yield* actor.stop;
280
+ return yield* new DuplicateActorError({ actorId: id });
281
+ }
282
+ MutableHashMap.set(actors, id, actor);
283
+ const maybeScope = yield* Effect.serviceOption(Scope.Scope);
284
+ if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, Effect.gen(function* () {
285
+ yield* actor.stop;
286
+ MutableHashMap.remove(actors, id);
287
+ }));
288
+ return actor;
289
+ });
290
+ const spawnRegular = Effect.fn("effect-machine.actorSystem.spawnRegular")(function* (id, built) {
291
+ if (MutableHashMap.has(actors, id)) return yield* new DuplicateActorError({ actorId: id });
292
+ return yield* registerActor(id, yield* createActor(id, built._inner));
293
+ });
294
+ const spawnPersistent = Effect.fn("effect-machine.actorSystem.spawnPersistent")(function* (id, persistentMachine) {
295
+ if (MutableHashMap.has(actors, id)) return yield* new DuplicateActorError({ actorId: id });
296
+ const adapter = yield* PersistenceAdapterTag;
297
+ const maybeSnapshot = yield* adapter.loadSnapshot(id, persistentMachine.persistence.stateSchema);
298
+ return yield* registerActor(id, yield* createPersistentActor(id, persistentMachine, maybeSnapshot, yield* adapter.loadEvents(id, persistentMachine.persistence.eventSchema, Option.isSome(maybeSnapshot) ? maybeSnapshot.value.version : void 0)));
299
+ });
300
+ const spawnImpl = Effect.fn("effect-machine.actorSystem.spawn")(function* (id, machine) {
301
+ if (isPersistentMachine(machine)) return yield* spawnPersistent(id, machine);
302
+ return yield* spawnRegular(id, machine);
303
+ });
304
+ function spawn(id, machine) {
305
+ return withSpawnGate(spawnImpl(id, machine));
306
+ }
307
+ const restoreImpl = Effect.fn("effect-machine.actorSystem.restore")(function* (id, persistentMachine) {
308
+ const maybeActor = yield* restorePersistentActor(id, persistentMachine);
309
+ if (Option.isSome(maybeActor)) yield* registerActor(id, maybeActor.value);
310
+ return maybeActor;
311
+ });
312
+ const restore = (id, persistentMachine) => withSpawnGate(restoreImpl(id, persistentMachine));
313
+ const get = Effect.fn("effect-machine.actorSystem.get")(function* (id) {
314
+ return yield* Effect.sync(() => MutableHashMap.get(actors, id));
315
+ });
316
+ const stop = Effect.fn("effect-machine.actorSystem.stop")(function* (id) {
317
+ const maybeActor = MutableHashMap.get(actors, id);
318
+ if (Option.isNone(maybeActor)) return false;
319
+ yield* maybeActor.value.stop;
320
+ MutableHashMap.remove(actors, id);
321
+ return true;
322
+ });
323
+ const listPersisted = Effect.fn("effect-machine.actorSystem.listPersisted")(function* () {
324
+ const adapter = yield* PersistenceAdapterTag;
325
+ if (adapter.listActors === void 0) return [];
326
+ return yield* adapter.listActors();
327
+ });
328
+ const restoreMany = Effect.fn("effect-machine.actorSystem.restoreMany")(function* (ids, persistentMachine) {
329
+ const restored = [];
330
+ const failed = [];
331
+ for (const id of ids) {
332
+ if (MutableHashMap.has(actors, id)) continue;
333
+ const result = yield* Effect.either(restore(id, persistentMachine));
334
+ if (result._tag === "Left") failed.push({
335
+ id,
336
+ error: result.left
337
+ });
338
+ else if (Option.isSome(result.right)) restored.push(result.right.value);
339
+ else failed.push({
340
+ id,
341
+ error: new PersistenceError({
342
+ operation: "restore",
343
+ actorId: id,
344
+ message: "No persisted state found"
345
+ })
346
+ });
347
+ }
348
+ return {
349
+ restored,
350
+ failed
351
+ };
352
+ });
353
+ const restoreAll = Effect.fn("effect-machine.actorSystem.restoreAll")(function* (persistentMachine, options) {
354
+ const adapter = yield* PersistenceAdapterTag;
355
+ if (adapter.listActors === void 0) return {
356
+ restored: [],
357
+ failed: []
358
+ };
359
+ const machineType = persistentMachine.persistence.machineType;
360
+ if (machineType === void 0) return yield* new PersistenceError({
361
+ operation: "restoreAll",
362
+ actorId: "*",
363
+ message: "restoreAll requires explicit machineType in persistence config"
364
+ });
365
+ let filtered = (yield* adapter.listActors()).filter((meta) => meta.machineType === machineType);
366
+ if (options?.filter !== void 0) filtered = filtered.filter(options.filter);
367
+ return yield* restoreMany(filtered.map((meta) => meta.id), persistentMachine);
368
+ });
369
+ return ActorSystem.of({
370
+ spawn,
371
+ restore,
372
+ get,
373
+ stop,
374
+ listPersisted,
375
+ restoreMany,
376
+ restoreAll
377
+ });
378
+ });
379
+ /**
380
+ * Default ActorSystem layer
381
+ */
382
+ const Default = Layer.scoped(ActorSystem, make());
383
+
384
+ //#endregion
385
+ export { ActorSystem, Default, buildActorRefCore, createActor, notifyListeners, processEventCore, resolveTransition, runSpawnEffects };