effect-machine 0.3.2 → 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.
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
 
package/dist/actor.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { EffectsDef, GuardsDef, MachineContext } from "./slot.js";
2
2
  import { PersistentMachine } from "./persistence/persistent-machine.js";
3
+ import { DuplicateActorError } from "./errors.js";
3
4
  import { ProcessEventError, ProcessEventHooks, ProcessEventResult, processEventCore, resolveTransition, runSpawnEffects } from "./internal/transition.js";
4
5
  import { PersistentActorRef } from "./persistence/persistent-actor.js";
5
- import { DuplicateActorError } from "./errors.js";
6
6
  import { ActorMetadata, PersistenceAdapterTag, PersistenceError, RestoreResult, VersionConflictError } from "./persistence/adapter.js";
7
7
  import { BuiltMachine, Machine, MachineRef } from "./machine.js";
8
8
  import { Context, Effect, Layer, Option, Queue, Ref, Scope, Stream, SubscriptionRef } from "effect";
@@ -101,6 +101,11 @@ interface ActorRef<State extends {
101
101
  * Returns unsubscribe function
102
102
  */
103
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;
104
109
  }
105
110
  /** Base type for stored actors (internal) */
106
111
  type AnyState = {
@@ -234,7 +239,7 @@ declare const buildActorRefCore: <S extends {
234
239
  readonly _tag: string;
235
240
  }, E extends {
236
241
  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>;
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>;
238
243
  /**
239
244
  * Create and start an actor for a machine
240
245
  */
package/dist/actor.js CHANGED
@@ -24,7 +24,7 @@ const notifyListeners = (listeners, state) => {
24
24
  /**
25
25
  * Build core ActorRef methods shared between regular and persistent actors.
26
26
  */
27
- const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listeners, stop) => {
27
+ const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listeners, stop, system) => {
28
28
  const send = Effect.fn("effect-machine.actor.send")(function* (event) {
29
29
  if (yield* Ref.get(stoppedRef)) return;
30
30
  yield* Queue.offer(eventQueue, event);
@@ -88,7 +88,8 @@ const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listen
88
88
  return () => {
89
89
  listeners.delete(fn);
90
90
  };
91
- }
91
+ },
92
+ system
92
93
  };
93
94
  };
94
95
  /**
@@ -96,13 +97,25 @@ const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listen
96
97
  */
97
98
  const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machine) {
98
99
  yield* Effect.annotateCurrentSpan("effect_machine.actor.id", id);
100
+ const existingSystem = yield* Effect.serviceOption(ActorSystem);
101
+ let system;
102
+ let implicitSystemScope;
103
+ if (Option.isSome(existingSystem)) system = existingSystem.value;
104
+ else {
105
+ const scope = yield* Scope.make();
106
+ system = yield* make().pipe(Effect.provideService(Scope.Scope, scope));
107
+ implicitSystemScope = scope;
108
+ }
99
109
  const inspectorValue = Option.getOrUndefined(yield* Effect.serviceOption(Inspector));
100
110
  const eventQueue = yield* Queue.unbounded();
101
111
  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
- }) };
112
+ const self = {
113
+ send: Effect.fn("effect-machine.actor.self.send")(function* (event) {
114
+ if (yield* Ref.get(stoppedRef)) return;
115
+ yield* Queue.offer(eventQueue, event);
116
+ }),
117
+ spawn: (childId, childMachine) => system.spawn(childId, childMachine).pipe(Effect.provideService(ActorSystem, system))
118
+ };
106
119
  yield* Effect.annotateCurrentSpan("effect_machine.actor.initial_state", machine.initial._tag);
107
120
  yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
108
121
  type: "@machine.spawn",
@@ -117,7 +130,8 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
117
130
  const ctx = {
118
131
  state: machine.initial,
119
132
  event: initEvent,
120
- self
133
+ self,
134
+ system
121
135
  };
122
136
  const { effects: effectSlots } = machine._slots;
123
137
  for (const bg of machine.backgroundEffects) {
@@ -125,12 +139,13 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
125
139
  state: machine.initial,
126
140
  event: initEvent,
127
141
  self,
128
- effects: effectSlots
142
+ effects: effectSlots,
143
+ system
129
144
  }).pipe(Effect.provideService(machine.Context, ctx)));
130
145
  backgroundFibers.push(fiber);
131
146
  }
132
147
  const stateScopeRef = { current: yield* Scope.make() };
133
- yield* runSpawnEffectsWithInspection(machine, machine.initial, initEvent, self, stateScopeRef.current, id, inspectorValue);
148
+ yield* runSpawnEffectsWithInspection(machine, machine.initial, initEvent, self, stateScopeRef.current, id, inspectorValue, system);
134
149
  if (machine.finalStates.has(machine.initial._tag)) {
135
150
  yield* Scope.close(stateScopeRef.current, Exit.void);
136
151
  yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
@@ -141,9 +156,10 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
141
156
  timestamp
142
157
  }));
143
158
  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));
159
+ if (implicitSystemScope !== void 0) yield* Scope.close(implicitSystemScope, Exit.void);
160
+ return buildActorRefCore(id, machine, stateRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid), system);
145
161
  }
146
- const loopFiber = yield* Effect.forkDaemon(eventLoop(machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, id, inspectorValue));
162
+ const loopFiber = yield* Effect.forkDaemon(eventLoop(machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, id, inspectorValue, system));
147
163
  return buildActorRefCore(id, machine, stateRef, eventQueue, stoppedRef, listeners, Effect.gen(function* () {
148
164
  const finalState = yield* SubscriptionRef.get(stateRef);
149
165
  yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
@@ -156,12 +172,13 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
156
172
  yield* Fiber.interrupt(loopFiber);
157
173
  yield* Scope.close(stateScopeRef.current, Exit.void);
158
174
  yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
159
- }).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid));
175
+ if (implicitSystemScope !== void 0) yield* Scope.close(implicitSystemScope, Exit.void);
176
+ }).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid), system);
160
177
  });
161
178
  /**
162
179
  * Main event loop for the actor
163
180
  */
164
- const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, actorId, inspector) {
181
+ const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, actorId, inspector, system) {
165
182
  while (true) {
166
183
  const event = yield* Queue.take(eventQueue);
167
184
  const currentState = yield* SubscriptionRef.get(stateRef);
@@ -169,7 +186,7 @@ const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine
169
186
  "effect_machine.actor.id": actorId,
170
187
  "effect_machine.state.current": currentState._tag,
171
188
  "effect_machine.event.type": event._tag
172
- } })(processEvent(machine, currentState, event, stateRef, self, listeners, stateScopeRef, actorId, inspector))) {
189
+ } })(processEvent(machine, currentState, event, stateRef, self, listeners, stateScopeRef, actorId, inspector, system))) {
173
190
  yield* Ref.set(stoppedRef, true);
174
191
  yield* Scope.close(stateScopeRef.current, Exit.void);
175
192
  yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
@@ -181,7 +198,7 @@ const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine
181
198
  * Process a single event, returning true if the actor should stop.
182
199
  * Wraps processEventCore with actor-specific concerns (inspection, listeners, state ref).
183
200
  */
184
- const processEvent = Effect.fn("effect-machine.actor.processEvent")(function* (machine, currentState, event, stateRef, self, listeners, stateScopeRef, actorId, inspector) {
201
+ const processEvent = Effect.fn("effect-machine.actor.processEvent")(function* (machine, currentState, event, stateRef, self, listeners, stateScopeRef, actorId, inspector, system) {
185
202
  yield* emitWithTimestamp(inspector, (timestamp) => ({
186
203
  type: "@machine.event",
187
204
  actorId,
@@ -189,7 +206,7 @@ const processEvent = Effect.fn("effect-machine.actor.processEvent")(function* (m
189
206
  event,
190
207
  timestamp
191
208
  }));
192
- const result = yield* processEventCore(machine, currentState, event, self, stateScopeRef, inspector === void 0 ? void 0 : {
209
+ const result = yield* processEventCore(machine, currentState, event, self, stateScopeRef, system, inspector === void 0 ? void 0 : {
193
210
  onSpawnEffect: (state) => emitWithTimestamp(inspector, (timestamp) => ({
194
211
  type: "@machine.effect",
195
212
  actorId,
@@ -242,7 +259,7 @@ const processEvent = Effect.fn("effect-machine.actor.processEvent")(function* (m
242
259
  * Wraps the core runSpawnEffects with inspection events and spans.
243
260
  * @internal
244
261
  */
245
- const runSpawnEffectsWithInspection = Effect.fn("effect-machine.actor.spawnEffects")(function* (machine, state, event, self, stateScope, actorId, inspector) {
262
+ const runSpawnEffectsWithInspection = Effect.fn("effect-machine.actor.spawnEffects")(function* (machine, state, event, self, stateScope, actorId, inspector, system) {
246
263
  yield* emitWithTimestamp(inspector, (timestamp) => ({
247
264
  type: "@machine.effect",
248
265
  actorId,
@@ -250,7 +267,7 @@ const runSpawnEffectsWithInspection = Effect.fn("effect-machine.actor.spawnEffec
250
267
  state,
251
268
  timestamp
252
269
  }));
253
- yield* runSpawnEffects(machine, state, event, self, stateScope, inspector === void 0 ? void 0 : (info) => emitWithTimestamp(inspector, (timestamp) => ({
270
+ yield* runSpawnEffects(machine, state, event, self, stateScope, system, inspector === void 0 ? void 0 : (info) => emitWithTimestamp(inspector, (timestamp) => ({
254
271
  type: "@machine.error",
255
272
  actorId,
256
273
  phase: info.phase,
@@ -1,6 +1,6 @@
1
1
  import { processEventCore, runSpawnEffects } from "../internal/transition.js";
2
- import "../actor.js";
3
- import { Effect, Queue, Ref, Scope } from "effect";
2
+ import { ActorSystem } from "../actor.js";
3
+ import { Effect, Option, Queue, Ref, Scope } from "effect";
4
4
  import { Entity } from "@effect/cluster";
5
5
 
6
6
  //#region src/cluster/entity-machine.ts
@@ -13,8 +13,8 @@ import { Entity } from "@effect/cluster";
13
13
  * Process a single event through the machine using shared core.
14
14
  * Returns the new state after processing.
15
15
  */
16
- const processEvent = Effect.fn("effect-machine.cluster.processEvent")(function* (machine, stateRef, event, self, stateScopeRef, hooks) {
17
- const result = yield* processEventCore(machine, yield* Ref.get(stateRef), event, self, stateScopeRef, hooks);
16
+ const processEvent = Effect.fn("effect-machine.cluster.processEvent")(function* (machine, stateRef, event, self, stateScopeRef, system, hooks) {
17
+ const result = yield* processEventCore(machine, yield* Ref.get(stateRef), event, self, stateScopeRef, system, hooks);
18
18
  if (result.transitioned) yield* Ref.set(stateRef, result.newState);
19
19
  return result.newState;
20
20
  });
@@ -51,19 +51,25 @@ const EntityMachine = { layer: (entity, machine, options) => {
51
51
  const layer = Effect.fn("effect-machine.cluster.layer")(function* () {
52
52
  const entityId = yield* Effect.serviceOption(Entity.CurrentAddress).pipe(Effect.map((opt) => opt._tag === "Some" ? opt.value.entityId : ""));
53
53
  const initialState = options?.initializeState !== void 0 ? options.initializeState(entityId) : machine.initial;
54
+ const existingSystem = yield* Effect.serviceOption(ActorSystem);
55
+ if (Option.isNone(existingSystem)) return yield* Effect.die("EntityMachine requires ActorSystem in context");
56
+ const system = existingSystem.value;
54
57
  const internalQueue = yield* Queue.unbounded();
55
- const self = { send: Effect.fn("effect-machine.cluster.self.send")(function* (event) {
56
- yield* Queue.offer(internalQueue, event);
57
- }) };
58
+ const self = {
59
+ send: Effect.fn("effect-machine.cluster.self.send")(function* (event) {
60
+ yield* Queue.offer(internalQueue, event);
61
+ }),
62
+ spawn: (childId, childMachine) => system.spawn(childId, childMachine).pipe(Effect.provideService(ActorSystem, system))
63
+ };
58
64
  const stateRef = yield* Ref.make(initialState);
59
65
  const stateScopeRef = { current: yield* Scope.make() };
60
- yield* runSpawnEffects(machine, initialState, { _tag: "$init" }, self, stateScopeRef.current, options?.hooks?.onError);
66
+ yield* runSpawnEffects(machine, initialState, { _tag: "$init" }, self, stateScopeRef.current, system, options?.hooks?.onError);
61
67
  const runInternalEvent = Effect.fn("effect-machine.cluster.internalEvent")(function* () {
62
- yield* processEvent(machine, stateRef, yield* Queue.take(internalQueue), self, stateScopeRef, options?.hooks);
68
+ yield* processEvent(machine, stateRef, yield* Queue.take(internalQueue), self, stateScopeRef, system, options?.hooks);
63
69
  });
64
70
  yield* Effect.forkScoped(Effect.forever(runInternalEvent()));
65
71
  return entity.of({
66
- Send: (envelope) => processEvent(machine, stateRef, envelope.payload.event, self, stateScopeRef, options?.hooks),
72
+ Send: (envelope) => processEvent(machine, stateRef, envelope.payload.event, self, stateScopeRef, system, options?.hooks),
67
73
  GetState: () => Ref.get(stateRef)
68
74
  });
69
75
  });
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { EffectHandlers, EffectSlot, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers, GuardSlot, GuardSlots, GuardsDef, GuardsSchema, MachineContext, Slot } from "./slot.js";
2
2
  import { Event, MachineEventSchema, MachineStateSchema, State } from "./schema.js";
3
3
  import { PersistenceConfig, PersistentMachine, isPersistentMachine } from "./persistence/persistent-machine.js";
4
- import { PersistentActorRef, createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
5
4
  import { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
5
+ import { PersistentActorRef, createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
6
6
  import { ActorMetadata, PersistedEvent, PersistenceAdapter, PersistenceAdapterTag, PersistenceError, RestoreFailure, RestoreResult, Snapshot, VersionConflictError } from "./persistence/adapter.js";
7
7
  import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
8
8
  import "./persistence/index.js";
@@ -1,5 +1,6 @@
1
1
  import { EffectsDef, GuardsDef, MachineContext } from "../slot.js";
2
2
  import { BuiltMachine, Machine, MachineRef, SpawnEffect, Transition } from "../machine.js";
3
+ import { ActorSystem } from "../actor.js";
3
4
  import { Cause, Effect, Scope } from "effect";
4
5
 
5
6
  //#region src/internal/transition.d.ts
@@ -28,7 +29,7 @@ declare const runTransitionHandler: <S extends {
28
29
  readonly _tag: string;
29
30
  }, E extends {
30
31
  readonly _tag: string;
31
- }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, transition: Transition<S, E, GD, EFD, R>, state: S, event: E, self: MachineRef<E>) => Effect.Effect<S, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
32
+ }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, transition: Transition<S, E, GD, EFD, R>, state: S, event: E, self: MachineRef<E>, system: ActorSystem) => Effect.Effect<S, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
32
33
  /**
33
34
  * Execute a transition for a given state and event.
34
35
  * Handles transition resolution, handler invocation, and guard/effect slot creation.
@@ -44,7 +45,7 @@ declare const executeTransition: <S extends {
44
45
  readonly _tag: string;
45
46
  }, E extends {
46
47
  readonly _tag: string;
47
- }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, currentState: S, event: E, self: MachineRef<E>) => Effect.Effect<{
48
+ }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, currentState: S, event: E, self: MachineRef<E>, system: ActorSystem) => Effect.Effect<{
48
49
  newState: S;
49
50
  transitioned: boolean;
50
51
  reenter: boolean;
@@ -102,7 +103,7 @@ declare const processEventCore: <S extends {
102
103
  readonly _tag: string;
103
104
  }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, currentState: S, event: E, self: MachineRef<E>, stateScopeRef: {
104
105
  current: Scope.CloseableScope;
105
- }, hooks?: ProcessEventHooks<S, E> | undefined) => Effect.Effect<{
106
+ }, system: ActorSystem, hooks?: ProcessEventHooks<S, E> | undefined) => Effect.Effect<{
106
107
  newState: S;
107
108
  previousState: S;
108
109
  transitioned: boolean;
@@ -118,7 +119,7 @@ declare const runSpawnEffects: <S extends {
118
119
  readonly _tag: string;
119
120
  }, E extends {
120
121
  readonly _tag: string;
121
- }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, state: S, event: E, self: MachineRef<E>, stateScope: Scope.CloseableScope, onError?: ((info: ProcessEventError<S, E>) => Effect.Effect<void>) | undefined) => Effect.Effect<void, never, Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
122
+ }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, state: S, event: E, self: MachineRef<E>, stateScope: Scope.CloseableScope, system: ActorSystem, onError?: ((info: ProcessEventError<S, E>) => Effect.Effect<void>) | undefined) => Effect.Effect<void, never, Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
122
123
  /**
123
124
  * Resolve which transition should fire for a given state and event.
124
125
  * Uses indexed O(1) lookup. First matching transition wins.
@@ -23,11 +23,12 @@ import { Cause, Effect, Exit, Scope } from "effect";
23
23
  *
24
24
  * @internal
25
25
  */
26
- const runTransitionHandler = Effect.fn("effect-machine.runTransitionHandler")(function* (machine, transition, state, event, self) {
26
+ const runTransitionHandler = Effect.fn("effect-machine.runTransitionHandler")(function* (machine, transition, state, event, self, system) {
27
27
  const ctx = {
28
28
  state,
29
29
  event,
30
- self
30
+ self,
31
+ system
31
32
  };
32
33
  const { guards, effects } = machine._slots;
33
34
  const handlerCtx = {
@@ -50,7 +51,7 @@ const runTransitionHandler = Effect.fn("effect-machine.runTransitionHandler")(fu
50
51
  *
51
52
  * @internal
52
53
  */
53
- const executeTransition = Effect.fn("effect-machine.executeTransition")(function* (machine, currentState, event, self) {
54
+ const executeTransition = Effect.fn("effect-machine.executeTransition")(function* (machine, currentState, event, self, system) {
54
55
  const transition = resolveTransition(machine, currentState, event);
55
56
  if (transition === void 0) return {
56
57
  newState: currentState,
@@ -58,7 +59,7 @@ const executeTransition = Effect.fn("effect-machine.executeTransition")(function
58
59
  reenter: false
59
60
  };
60
61
  return {
61
- newState: yield* runTransitionHandler(machine, transition, currentState, event, self),
62
+ newState: yield* runTransitionHandler(machine, transition, currentState, event, self, system),
62
63
  transitioned: true,
63
64
  reenter: transition.reenter === true
64
65
  };
@@ -75,8 +76,8 @@ const executeTransition = Effect.fn("effect-machine.executeTransition")(function
75
76
  *
76
77
  * @internal
77
78
  */
78
- const processEventCore = Effect.fn("effect-machine.processEventCore")(function* (machine, currentState, event, self, stateScopeRef, hooks) {
79
- const result = yield* executeTransition(machine, currentState, event, self).pipe(Effect.catchAllCause((cause) => {
79
+ const processEventCore = Effect.fn("effect-machine.processEventCore")(function* (machine, currentState, event, self, stateScopeRef, system, hooks) {
80
+ const result = yield* executeTransition(machine, currentState, event, self, system).pipe(Effect.catchAllCause((cause) => {
80
81
  if (Cause.isInterruptedOnly(cause)) return Effect.interrupt;
81
82
  const onError = hooks?.onError;
82
83
  if (onError === void 0) return Effect.failCause(cause).pipe(Effect.orDie);
@@ -101,7 +102,7 @@ const processEventCore = Effect.fn("effect-machine.processEventCore")(function*
101
102
  stateScopeRef.current = yield* Scope.make();
102
103
  if (hooks?.onTransition !== void 0) yield* hooks.onTransition(currentState, newState, event);
103
104
  if (hooks?.onSpawnEffect !== void 0) yield* hooks.onSpawnEffect(newState);
104
- yield* runSpawnEffects(machine, newState, { _tag: INTERNAL_ENTER_EVENT }, self, stateScopeRef.current, hooks?.onError);
105
+ yield* runSpawnEffects(machine, newState, { _tag: INTERNAL_ENTER_EVENT }, self, stateScopeRef.current, system, hooks?.onError);
105
106
  }
106
107
  return {
107
108
  newState,
@@ -116,12 +117,13 @@ const processEventCore = Effect.fn("effect-machine.processEventCore")(function*
116
117
  *
117
118
  * @internal
118
119
  */
119
- const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (machine, state, event, self, stateScope, onError) {
120
+ const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (machine, state, event, self, stateScope, system, onError) {
120
121
  const spawnEffects = findSpawnEffects(machine, state._tag);
121
122
  const ctx = {
122
123
  state,
123
124
  event,
124
- self
125
+ self,
126
+ system
125
127
  };
126
128
  const { effects: effectSlots } = machine._slots;
127
129
  const reportError = onError;
@@ -130,7 +132,8 @@ const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (m
130
132
  state,
131
133
  event,
132
134
  self,
133
- effects: effectSlots
135
+ effects: effectSlots,
136
+ system
134
137
  }).pipe(Effect.provideService(machine.Context, ctx), Effect.catchAllCause((cause) => {
135
138
  if (Cause.isInterruptedOnly(cause)) return Effect.interrupt;
136
139
  if (reportError === void 0) return Effect.failCause(cause).pipe(Effect.orDie);
@@ -1,3 +1,4 @@
1
+ import { ActorSystem } from "../actor.js";
1
2
  import { Effect } from "effect";
2
3
 
3
4
  //#region src/internal/utils.d.ts
@@ -48,5 +49,12 @@ declare const getTag: (constructorOrValue: {
48
49
  })) => string;
49
50
  /** Check if a value is an Effect */
50
51
  declare const isEffect: (value: unknown) => value is Effect.Effect<unknown, unknown, unknown>;
52
+ /**
53
+ * Stub ActorSystem that dies on any method call.
54
+ * Used in contexts where spawning/system access isn't supported
55
+ * (testing simulation, persistent actor replay).
56
+ * @internal
57
+ */
58
+ declare const stubSystem: ActorSystem;
51
59
  //#endregion
52
- export { ArgsOf, INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, InstanceOf, TagOf, TaggedConstructor, TransitionResult, getTag, isEffect };
60
+ export { ArgsOf, INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, InstanceOf, TagOf, TaggedConstructor, TransitionResult, getTag, isEffect, stubSystem };
@@ -26,6 +26,21 @@ const getTag = (constructorOrValue) => {
26
26
  };
27
27
  /** Check if a value is an Effect */
28
28
  const isEffect = (value) => typeof value === "object" && value !== null && Effect.EffectTypeId in value;
29
+ /**
30
+ * Stub ActorSystem that dies on any method call.
31
+ * Used in contexts where spawning/system access isn't supported
32
+ * (testing simulation, persistent actor replay).
33
+ * @internal
34
+ */
35
+ const stubSystem = {
36
+ spawn: () => Effect.die("spawn not supported in stub system"),
37
+ restore: () => Effect.die("restore not supported in stub system"),
38
+ get: () => Effect.die("get not supported in stub system"),
39
+ stop: () => Effect.die("stop not supported in stub system"),
40
+ listPersisted: () => Effect.die("listPersisted not supported in stub system"),
41
+ restoreMany: () => Effect.die("restoreMany not supported in stub system"),
42
+ restoreAll: () => Effect.die("restoreAll not supported in stub system")
43
+ };
29
44
 
30
45
  //#endregion
31
- export { INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, getTag, isEffect };
46
+ export { INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, getTag, isEffect, stubSystem };
package/dist/machine.d.ts CHANGED
@@ -3,9 +3,10 @@ import { TransitionResult } from "./internal/utils.js";
3
3
  import { BrandedEvent, BrandedState, TaggedOrConstructor } from "./internal/brands.js";
4
4
  import { MachineEventSchema, MachineStateSchema, VariantsUnion } from "./schema.js";
5
5
  import { PersistenceConfig, PersistentMachine } from "./persistence/persistent-machine.js";
6
+ import { DuplicateActorError } from "./errors.js";
6
7
  import { findTransitions } from "./internal/transition.js";
7
8
  import "./persistence/index.js";
8
- import { ActorRef } from "./actor.js";
9
+ import { ActorRef, ActorSystem } from "./actor.js";
9
10
  import { Cause, Context, Effect, Schedule, Schema, Scope } from "effect";
10
11
 
11
12
  //#region src/machine.d.ts
@@ -17,6 +18,11 @@ declare namespace machine_d_exports {
17
18
  */
18
19
  interface MachineRef<Event> {
19
20
  readonly send: (event: Event) => Effect.Effect<void>;
21
+ readonly spawn: <S2 extends {
22
+ readonly _tag: string;
23
+ }, E2 extends {
24
+ readonly _tag: string;
25
+ }, R2>(id: string, machine: BuiltMachine<S2, E2, R2>) => Effect.Effect<ActorRef<S2, E2>, DuplicateActorError, R2>;
20
26
  }
21
27
  /**
22
28
  * Handler context passed to transition handlers
@@ -35,6 +41,7 @@ interface StateHandlerContext<State, Event, ED extends EffectsDef> {
35
41
  readonly event: Event;
36
42
  readonly self: MachineRef<Event>;
37
43
  readonly effects: EffectSlots<ED>;
44
+ readonly system: ActorSystem;
38
45
  }
39
46
  /**
40
47
  * Transition handler function
@@ -1,5 +1,5 @@
1
- import { PersistentActorRef } from "./persistent-actor.js";
2
1
  import { DuplicateActorError } from "../errors.js";
2
+ import { PersistentActorRef } from "./persistent-actor.js";
3
3
  import { Context, Effect, Option, Schema } from "effect";
4
4
 
5
5
  //#region src/persistence/adapter.d.ts
@@ -1,9 +1,9 @@
1
1
  import { Inspector } from "../inspection.js";
2
- import { INTERNAL_INIT_EVENT } from "../internal/utils.js";
2
+ import { INTERNAL_INIT_EVENT, stubSystem } from "../internal/utils.js";
3
3
  import { processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler } from "../internal/transition.js";
4
4
  import { emitWithTimestamp } from "../internal/inspection.js";
5
5
  import { PersistenceAdapterTag } from "./adapter.js";
6
- import { buildActorRefCore, notifyListeners } from "../actor.js";
6
+ import { ActorSystem, buildActorRefCore, notifyListeners } from "../actor.js";
7
7
  import { Cause, Clock, Effect, Exit, Fiber, Option, Queue, Ref, Schedule, Scope, SubscriptionRef } from "effect";
8
8
 
9
9
  //#region src/persistence/persistent-actor.ts
@@ -20,7 +20,7 @@ const replayEvents = Effect.fn("effect-machine.persistentActor.replayEvents")(fu
20
20
  for (const persistedEvent of events) {
21
21
  if (stopVersion !== void 0 && persistedEvent.version > stopVersion) break;
22
22
  const transition = resolveTransition(machine, state, persistedEvent.event);
23
- if (transition !== void 0) state = yield* runTransitionHandler(machine, transition, state, persistedEvent.event, self);
23
+ if (transition !== void 0) state = yield* runTransitionHandler(machine, transition, state, persistedEvent.event, self, stubSystem);
24
24
  version = persistedEvent.version;
25
25
  }
26
26
  return {
@@ -31,7 +31,7 @@ const replayEvents = Effect.fn("effect-machine.persistentActor.replayEvents")(fu
31
31
  /**
32
32
  * Build PersistentActorRef with all methods
33
33
  */
34
- const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, stop, adapter) => {
34
+ const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, stop, adapter, system) => {
35
35
  const { machine, persistence } = persistentMachine;
36
36
  const typedMachine = machine;
37
37
  const persist = Effect.gen(function* () {
@@ -45,12 +45,15 @@ const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, ev
45
45
  const version = Ref.get(versionRef).pipe(Effect.withSpan("effect-machine.persistentActor.version"));
46
46
  const replayTo = Effect.fn("effect-machine.persistentActor.replayTo")(function* (targetVersion) {
47
47
  if (targetVersion <= (yield* Ref.get(versionRef))) {
48
+ const dummySelf = {
49
+ send: Effect.fn("effect-machine.persistentActor.replay.send")((_event) => Effect.void),
50
+ spawn: () => Effect.die("spawn not supported in replay")
51
+ };
48
52
  const maybeSnapshot = yield* adapter.loadSnapshot(id, persistence.stateSchema);
49
53
  if (Option.isSome(maybeSnapshot)) {
50
54
  const snapshot = maybeSnapshot.value;
51
55
  if (snapshot.version <= targetVersion) {
52
56
  const events = yield* adapter.loadEvents(id, persistence.eventSchema, snapshot.version);
53
- const dummySelf = { send: Effect.fn("effect-machine.persistentActor.replay.send")((_event) => Effect.void) };
54
57
  const result = yield* replayEvents(typedMachine, snapshot.state, events, dummySelf, targetVersion);
55
58
  yield* SubscriptionRef.set(stateRef, result.state);
56
59
  yield* Ref.set(versionRef, result.version);
@@ -59,7 +62,6 @@ const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, ev
59
62
  } else {
60
63
  const events = yield* adapter.loadEvents(id, persistence.eventSchema);
61
64
  if (events.length > 0) {
62
- const dummySelf = { send: Effect.fn("effect-machine.persistentActor.replay.send")((_event) => Effect.void) };
63
65
  const result = yield* replayEvents(typedMachine, typedMachine.initial, events, dummySelf, targetVersion);
64
66
  yield* SubscriptionRef.set(stateRef, result.state);
65
67
  yield* Ref.set(versionRef, result.version);
@@ -69,7 +71,7 @@ const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, ev
69
71
  }
70
72
  });
71
73
  return {
72
- ...buildActorRefCore(id, typedMachine, stateRef, eventQueue, stoppedRef, listeners, stop),
74
+ ...buildActorRefCore(id, typedMachine, stateRef, eventQueue, stoppedRef, listeners, stop, system),
73
75
  persist,
74
76
  version,
75
77
  replayTo
@@ -84,13 +86,19 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
84
86
  const adapter = yield* PersistenceAdapterTag;
85
87
  const { machine, persistence } = persistentMachine;
86
88
  const typedMachine = machine;
89
+ const existingSystem = yield* Effect.serviceOption(ActorSystem);
90
+ if (Option.isNone(existingSystem)) return yield* Effect.die("PersistentActor requires ActorSystem in context");
91
+ const system = existingSystem.value;
87
92
  const inspector = Option.getOrUndefined(yield* Effect.serviceOption(Inspector));
88
93
  const eventQueue = yield* Queue.unbounded();
89
94
  const stoppedRef = yield* Ref.make(false);
90
- const self = { send: Effect.fn("effect-machine.persistentActor.self.send")(function* (event) {
91
- if (yield* Ref.get(stoppedRef)) return;
92
- yield* Queue.offer(eventQueue, event);
93
- }) };
95
+ const self = {
96
+ send: Effect.fn("effect-machine.persistentActor.self.send")(function* (event) {
97
+ if (yield* Ref.get(stoppedRef)) return;
98
+ yield* Queue.offer(eventQueue, event);
99
+ }),
100
+ spawn: (childId, childMachine) => system.spawn(childId, childMachine).pipe(Effect.provideService(ActorSystem, system))
101
+ };
94
102
  let resolvedInitial;
95
103
  let initialVersion;
96
104
  if (Option.isSome(initialSnapshot)) {
@@ -131,7 +139,8 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
131
139
  const initCtx = {
132
140
  state: resolvedInitial,
133
141
  event: initEvent,
134
- self
142
+ self,
143
+ system
135
144
  };
136
145
  const { effects: effectSlots } = typedMachine._slots;
137
146
  for (const bg of typedMachine.backgroundEffects) {
@@ -139,12 +148,13 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
139
148
  state: resolvedInitial,
140
149
  event: initEvent,
141
150
  self,
142
- effects: effectSlots
151
+ effects: effectSlots,
152
+ system
143
153
  }).pipe(Effect.provideService(typedMachine.Context, initCtx)));
144
154
  backgroundFibers.push(fiber);
145
155
  }
146
156
  const stateScopeRef = { current: yield* Scope.make() };
147
- yield* runSpawnEffectsWithInspection(typedMachine, resolvedInitial, initEvent, self, stateScopeRef.current, id, inspector);
157
+ yield* runSpawnEffectsWithInspection(typedMachine, resolvedInitial, initEvent, self, stateScopeRef.current, id, inspector, system);
148
158
  if (typedMachine.finalStates.has(resolvedInitial._tag)) {
149
159
  yield* Scope.close(stateScopeRef.current, Exit.void);
150
160
  yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
@@ -157,9 +167,9 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
157
167
  finalState: resolvedInitial,
158
168
  timestamp
159
169
  }));
160
- return buildPersistentActorRef(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter);
170
+ return buildPersistentActorRef(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system);
161
171
  }
162
- const loopFiber = yield* Effect.forkDaemon(persistentEventLoop(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, self, listeners, adapter, createdAt, stateScopeRef, backgroundFibers, snapshotQueue, snapshotEnabledRef, persistenceQueue, snapshotFiber, persistenceFiber, inspector));
172
+ const loopFiber = yield* Effect.forkDaemon(persistentEventLoop(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, self, listeners, adapter, createdAt, stateScopeRef, backgroundFibers, snapshotQueue, snapshotEnabledRef, persistenceQueue, snapshotFiber, persistenceFiber, inspector, system));
163
173
  return buildPersistentActorRef(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, Effect.gen(function* () {
164
174
  const finalState = yield* SubscriptionRef.get(stateRef);
165
175
  yield* emitWithTimestamp(inspector, (timestamp) => ({
@@ -174,12 +184,12 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
174
184
  yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
175
185
  yield* Fiber.interrupt(snapshotFiber);
176
186
  yield* Fiber.interrupt(persistenceFiber);
177
- }).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter);
187
+ }).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system);
178
188
  });
179
189
  /**
180
190
  * Main event loop for persistent actor
181
191
  */
182
- const persistentEventLoop = Effect.fn("effect-machine.persistentActor.eventLoop")(function* (id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, self, listeners, adapter, createdAt, stateScopeRef, backgroundFibers, snapshotQueue, snapshotEnabledRef, persistenceQueue, snapshotFiber, persistenceFiber, inspector) {
192
+ const persistentEventLoop = Effect.fn("effect-machine.persistentActor.eventLoop")(function* (id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, self, listeners, adapter, createdAt, stateScopeRef, backgroundFibers, snapshotQueue, snapshotEnabledRef, persistenceQueue, snapshotFiber, persistenceFiber, inspector, system) {
183
193
  const { machine, persistence } = persistentMachine;
184
194
  const typedMachine = machine;
185
195
  const hooks = inspector === void 0 ? void 0 : {
@@ -219,7 +229,7 @@ const persistentEventLoop = Effect.fn("effect-machine.persistentActor.eventLoop"
219
229
  event,
220
230
  timestamp
221
231
  }));
222
- const result = yield* processEventCore(typedMachine, currentState, event, self, stateScopeRef, hooks);
232
+ const result = yield* processEventCore(typedMachine, currentState, event, self, stateScopeRef, system, hooks);
223
233
  if (!result.transitioned) continue;
224
234
  const newVersion = currentVersion + 1;
225
235
  yield* Ref.set(versionRef, newVersion);
@@ -259,7 +269,7 @@ const persistentEventLoop = Effect.fn("effect-machine.persistentActor.eventLoop"
259
269
  * Run spawn effects with inspection and tracing.
260
270
  * @internal
261
271
  */
262
- const runSpawnEffectsWithInspection = Effect.fn("effect-machine.persistentActor.spawnEffects")(function* (machine, state, event, self, stateScope, actorId, inspector) {
272
+ const runSpawnEffectsWithInspection = Effect.fn("effect-machine.persistentActor.spawnEffects")(function* (machine, state, event, self, stateScope, actorId, inspector, system) {
263
273
  yield* emitWithTimestamp(inspector, (timestamp) => ({
264
274
  type: "@machine.effect",
265
275
  actorId,
@@ -267,7 +277,7 @@ const runSpawnEffectsWithInspection = Effect.fn("effect-machine.persistentActor.
267
277
  state,
268
278
  timestamp
269
279
  }));
270
- yield* runSpawnEffects(machine, state, event, self, stateScope, inspector === void 0 ? void 0 : (info) => emitWithTimestamp(inspector, (timestamp) => ({
280
+ yield* runSpawnEffects(machine, state, event, self, stateScope, system, inspector === void 0 ? void 0 : (info) => emitWithTimestamp(inspector, (timestamp) => ({
271
281
  type: "@machine.error",
272
282
  actorId,
273
283
  phase: info.phase,
package/dist/slot.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { ActorSystem } from "./actor.js";
1
2
  import { Context, Effect, Schema } from "effect";
2
3
 
3
4
  //#region src/slot.d.ts
@@ -45,6 +46,7 @@ interface MachineContext<State, Event, Self> {
45
46
  readonly state: State;
46
47
  readonly event: Event;
47
48
  readonly self: Self;
49
+ readonly system: ActorSystem;
48
50
  }
49
51
  /**
50
52
  * Shared Context tag for all machines.
package/dist/testing.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { stubSystem } from "./internal/utils.js";
1
2
  import { AssertionError } from "./errors.js";
2
3
  import { BuiltMachine } from "./machine.js";
3
4
  import { executeTransition } from "./internal/transition.js";
@@ -26,11 +27,14 @@ import { Effect, SubscriptionRef } from "effect";
26
27
  */
27
28
  const simulate = Effect.fn("effect-machine.simulate")(function* (input, events) {
28
29
  const machine = input instanceof BuiltMachine ? input._inner : input;
29
- const dummySelf = { send: Effect.fn("effect-machine.testing.simulate.send")((_event) => Effect.void) };
30
+ const dummySelf = {
31
+ send: Effect.fn("effect-machine.testing.simulate.send")((_event) => Effect.void),
32
+ spawn: () => Effect.die("spawn not supported in simulation")
33
+ };
30
34
  let currentState = machine.initial;
31
35
  const states = [currentState];
32
36
  for (const event of events) {
33
- const result = yield* executeTransition(machine, currentState, event, dummySelf);
37
+ const result = yield* executeTransition(machine, currentState, event, dummySelf, stubSystem);
34
38
  if (!result.transitioned) continue;
35
39
  currentState = result.newState;
36
40
  states.push(currentState);
@@ -110,13 +114,16 @@ const assertNeverReaches = Effect.fn("effect-machine.assertNeverReaches")(functi
110
114
  */
111
115
  const createTestHarness = Effect.fn("effect-machine.createTestHarness")(function* (input, options) {
112
116
  const machine = input instanceof BuiltMachine ? input._inner : input;
113
- const dummySelf = { send: Effect.fn("effect-machine.testing.harness.send")((_event) => Effect.void) };
117
+ const dummySelf = {
118
+ send: Effect.fn("effect-machine.testing.harness.send")((_event) => Effect.void),
119
+ spawn: () => Effect.die("spawn not supported in test harness")
120
+ };
114
121
  const stateRef = yield* SubscriptionRef.make(machine.initial);
115
122
  return {
116
123
  state: stateRef,
117
124
  send: Effect.fn("effect-machine.testHarness.send")(function* (event) {
118
125
  const currentState = yield* SubscriptionRef.get(stateRef);
119
- const result = yield* executeTransition(machine, currentState, event, dummySelf);
126
+ const result = yield* executeTransition(machine, currentState, event, dummySelf, stubSystem);
120
127
  if (!result.transitioned) return currentState;
121
128
  const newState = result.newState;
122
129
  yield* SubscriptionRef.set(stateRef, newState);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-machine",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/cevr/effect-machine.git"
@@ -41,21 +41,21 @@
41
41
  "release": "bun run build && changeset publish"
42
42
  },
43
43
  "dependencies": {
44
- "effect": "^3.19.15"
44
+ "effect": "^3.19.16"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@changesets/changelog-github": "^0.5.2",
48
48
  "@changesets/cli": "^2.29.8",
49
- "@effect/cluster": "^0.56.1",
49
+ "@effect/cluster": "^0.56.2",
50
50
  "@effect/experimental": "^0.58.0",
51
- "@effect/language-service": "^0.72.0",
51
+ "@effect/language-service": "^0.73.0",
52
52
  "@effect/rpc": "^0.73.0",
53
- "@types/bun": "latest",
53
+ "@types/bun": "1.3.8",
54
54
  "concurrently": "^9.2.1",
55
55
  "effect-bun-test": "^0.1.0",
56
- "lefthook": "^2.0.15",
57
- "oxfmt": "^0.26.0",
58
- "oxlint": "^1.41.0",
56
+ "lefthook": "^2.1.0",
57
+ "oxfmt": "^0.28.0",
58
+ "oxlint": "^1.43.0",
59
59
  "tsdown": "^0.20.3",
60
60
  "typescript": "^5.9.3"
61
61
  },
@@ -70,5 +70,8 @@
70
70
  "@effect/rpc": {
71
71
  "optional": true
72
72
  }
73
+ },
74
+ "overrides": {
75
+ "effect": "^3.19.16"
73
76
  }
74
77
  }