effect-machine 0.13.0 → 0.14.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 (48) hide show
  1. package/README.md +14 -9
  2. package/dist/actor.d.ts +9 -6
  3. package/dist/actor.js +97 -43
  4. package/dist/cluster/entity-machine.d.ts +1 -1
  5. package/dist/cluster/entity-machine.js +1 -1
  6. package/dist/cluster/to-entity.d.ts +1 -1
  7. package/dist/errors.d.ts +9 -2
  8. package/dist/errors.js +8 -2
  9. package/dist/index.d.ts +4 -4
  10. package/dist/index.js +4 -4
  11. package/dist/internal/runtime.d.ts +2 -2
  12. package/dist/internal/runtime.js +5 -10
  13. package/dist/internal/transition.d.ts +9 -9
  14. package/dist/internal/transition.js +3 -5
  15. package/dist/machine.d.ts +122 -135
  16. package/dist/machine.js +97 -112
  17. package/dist/schema.d.ts +14 -0
  18. package/dist/schema.js +9 -0
  19. package/dist/slot.d.ts +112 -86
  20. package/dist/slot.js +92 -59
  21. package/dist/testing.d.ts +16 -16
  22. package/dist/testing.js +3 -3
  23. package/package.json +3 -3
  24. package/v3/dist/actor.d.ts +19 -12
  25. package/v3/dist/actor.js +130 -75
  26. package/v3/dist/cluster/entity-machine.d.ts +1 -1
  27. package/v3/dist/cluster/to-entity.d.ts +1 -1
  28. package/v3/dist/errors.d.ts +12 -3
  29. package/v3/dist/errors.js +10 -4
  30. package/v3/dist/index.d.ts +6 -6
  31. package/v3/dist/index.js +2 -2
  32. package/v3/dist/inspection.d.ts +3 -22
  33. package/v3/dist/inspection.js +1 -15
  34. package/v3/dist/internal/brands.d.ts +4 -8
  35. package/v3/dist/internal/inspection.js +1 -1
  36. package/v3/dist/internal/runtime.d.ts +8 -8
  37. package/v3/dist/internal/runtime.js +45 -28
  38. package/v3/dist/internal/transition.d.ts +10 -10
  39. package/v3/dist/internal/transition.js +8 -10
  40. package/v3/dist/internal/utils.js +5 -1
  41. package/v3/dist/machine.d.ts +153 -120
  42. package/v3/dist/machine.js +118 -115
  43. package/v3/dist/schema.d.ts +25 -11
  44. package/v3/dist/schema.js +18 -5
  45. package/v3/dist/slot.d.ts +112 -86
  46. package/v3/dist/slot.js +92 -59
  47. package/v3/dist/testing.d.ts +16 -16
  48. package/v3/dist/testing.js +7 -7
@@ -3,7 +3,7 @@ import { NoReplyError } from "../errors.js";
3
3
  import { processEventCore, runSpawnEffects, shouldPostpone } from "./transition.js";
4
4
  import { ActorExit } from "../supervision.js";
5
5
  import { ActorSystem } from "../actor.js";
6
- import { Cause, Deferred, Effect, Exit, Fiber, Queue, Ref, Schema, Scope, SubscriptionRef } from "effect";
6
+ import { Cause, Deferred, Effect, Exit, Fiber, Queue, Ref, Runtime, Schema, Scope, SubscriptionRef } from "effect";
7
7
  //#region src/internal/runtime.ts
8
8
  /**
9
9
  * Shared runtime kernel for machine event processing.
@@ -28,7 +28,7 @@ import { Cause, Deferred, Effect, Exit, Fiber, Queue, Ref, Schema, Scope, Subscr
28
28
  * and querying state. The runtime owns:
29
29
  * - Event loop fiber
30
30
  * - Postpone buffer
31
- * - Background effects
31
+ * - Background effects (under actorScope)
32
32
  * - State scope (spawn effects)
33
33
  * - Final state detection
34
34
  * - Exit reason via exitDeferred
@@ -40,11 +40,14 @@ import { Cause, Deferred, Effect, Exit, Fiber, Queue, Ref, Schema, Scope, Subscr
40
40
  */
41
41
  const createRuntime = Effect.fn("effect-machine.runtime.create")(function* (machine, system, config) {
42
42
  const { actorId, hooks, lifecycle } = config;
43
+ const rt = yield* Effect.runtime();
44
+ const fork = Runtime.runFork(rt);
43
45
  const stateRef = config.cellResources?.stateRef ?? (yield* SubscriptionRef.make(machine.initial));
44
46
  const stoppedRef = config.cellResources?.stoppedRef ?? (yield* Ref.make(false));
45
47
  const eventQueue = config.cellResources?.eventQueue ?? (yield* config.queueFactory ?? Queue.unbounded());
46
48
  const exitDeferred = yield* Deferred.make();
47
49
  const actorScope = yield* Scope.make();
50
+ const deferredReplyRef = { current: void 0 };
48
51
  const selfSend = Effect.fn("effect-machine.runtime.self.send")(function* (event) {
49
52
  if (!(yield* Ref.get(stoppedRef))) yield* Queue.offer(eventQueue, {
50
53
  _tag: "send",
@@ -57,7 +60,16 @@ const createRuntime = Effect.fn("effect-machine.runtime.create")(function* (mach
57
60
  const self = {
58
61
  send: selfSend,
59
62
  cast: selfSend,
60
- spawn: onChildSpawned !== void 0 ? (childId, childMachine) => defaultSpawn(childId, childMachine).pipe(Effect.tap((child) => onChildSpawned(childId, child))) : defaultSpawn
63
+ spawn: onChildSpawned !== void 0 ? (childId, childMachine) => defaultSpawn(childId, childMachine).pipe(Effect.tap((child) => onChildSpawned(childId, child))) : defaultSpawn,
64
+ reply: (value) => Effect.sync(() => {
65
+ const deferred = deferredReplyRef.current;
66
+ if (deferred !== void 0) {
67
+ deferredReplyRef.current = void 0;
68
+ fork(Deferred.succeed(deferred, value));
69
+ return true;
70
+ }
71
+ return false;
72
+ })
61
73
  };
62
74
  const stateScopeRef = { current: yield* Scope.make() };
63
75
  const backgroundFibers = [];
@@ -69,21 +81,21 @@ const createRuntime = Effect.fn("effect-machine.runtime.create")(function* (mach
69
81
  self,
70
82
  system
71
83
  };
72
- const { effects: effectSlots } = machine._slots;
84
+ const slots = machine._slots;
73
85
  for (const bg of machine.backgroundEffects) {
74
- const fiber = yield* Effect.forkDaemon(bg.handler({
86
+ const fiber = yield* bg.handler({
75
87
  actorId,
76
88
  state: machine.initial,
77
89
  event: initEvent,
78
90
  self,
79
- effects: effectSlots,
91
+ slots,
80
92
  system
81
- }).pipe(Effect.provideService(machine.Context, ctx)));
93
+ }).pipe(Effect.provideService(machine.Context, ctx), Effect.forkIn(actorScope));
82
94
  backgroundFibers.push(fiber);
83
95
  }
84
96
  if (lifecycle?.onInitialSpawnEffects !== void 0) yield* lifecycle.onInitialSpawnEffects(machine.initial);
85
97
  const loopFiberRef = { current: void 0 };
86
- const initialSpawnDefectSignal = (cause) => Deferred.succeed(exitDeferred, ActorExit.Defect(cause, "initial-spawn")).pipe(Effect.zipRight(Ref.set(stoppedRef, true)), Effect.zipRight(Effect.suspend(() => loopFiberRef.current !== void 0 ? Fiber.interrupt(loopFiberRef.current) : Effect.void)), Effect.asVoid);
98
+ const initialSpawnDefectSignal = (cause) => Deferred.succeed(exitDeferred, ActorExit.Defect(cause, "initial-spawn")).pipe(Effect.andThen(Ref.set(stoppedRef, true)), Effect.andThen(Effect.suspend(() => loopFiberRef.current !== void 0 ? Fiber.interrupt(loopFiberRef.current) : Effect.void)), Effect.asVoid);
87
99
  yield* runSpawnEffects(machine, machine.initial, initEvent, self, stateScopeRef.current, system, actorId, hooks?.onError, initialSpawnDefectSignal).pipe(Effect.catchAllCause((cause) => {
88
100
  return Effect.gen(function* () {
89
101
  yield* Ref.set(stoppedRef, true);
@@ -100,20 +112,24 @@ const createRuntime = Effect.fn("effect-machine.runtime.create")(function* (mach
100
112
  yield* Ref.set(stoppedRef, true);
101
113
  yield* Scope.close(stateScopeRef.current, Exit.void);
102
114
  yield* Scope.close(actorScope, Exit.void);
103
- yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
104
115
  yield* setExit(ActorExit.Final(machine.initial));
105
116
  return makeHandle(stateRef, stoppedRef, eventQueue, exitDeferred, actorScope);
106
117
  }
107
118
  const augmentedHooks = {
108
119
  ...hooks,
109
- onSpawnDefect: (cause) => Deferred.succeed(exitDeferred, ActorExit.Defect(cause, "spawn")).pipe(Effect.zipRight(Ref.set(stoppedRef, true)), Effect.zipRight(Effect.suspend(() => loopFiberRef.current !== void 0 ? Fiber.interrupt(loopFiberRef.current) : Effect.void)), Effect.asVoid)
120
+ onSpawnDefect: (cause) => Deferred.succeed(exitDeferred, ActorExit.Defect(cause, "spawn")).pipe(Effect.andThen(Ref.set(stoppedRef, true)), Effect.andThen(Effect.suspend(() => loopFiberRef.current !== void 0 ? Fiber.interrupt(loopFiberRef.current) : Effect.void)), Effect.asVoid)
110
121
  };
111
- const loopFiber = yield* Effect.forkDaemon(runtimeEventLoop(machine, stateRef, eventQueue, stoppedRef, self, stateScopeRef, actorId, system, exitDeferred, augmentedHooks, lifecycle, config.wrapProcess));
122
+ const loopFiber = yield* Effect.forkDaemon(runtimeEventLoop(machine, stateRef, eventQueue, stoppedRef, self, stateScopeRef, actorId, system, exitDeferred, augmentedHooks, deferredReplyRef, lifecycle, config.wrapProcess, fork));
112
123
  loopFiberRef.current = loopFiber;
113
- if (backgroundFibers.length > 0) yield* Effect.forkDaemon(Effect.raceAll(backgroundFibers.map((fiber) => Fiber.await(fiber).pipe(Effect.flatMap((exit) => {
114
- if (exit._tag === "Failure" && !Cause.isInterruptedOnly(exit.cause)) return setExit(ActorExit.Defect(exit.cause, "background")).pipe(Effect.zipRight(Ref.set(stoppedRef, true)), Effect.zipRight(Fiber.interrupt(loopFiber)));
124
+ if (backgroundFibers.length > 0) yield* Effect.raceAll(backgroundFibers.map((fiber) => Fiber.await(fiber).pipe(Effect.flatMap((exit) => {
125
+ if (exit._tag === "Failure" && !Cause.isInterruptedOnly(exit.cause)) return setExit(ActorExit.Defect(exit.cause, "background")).pipe(Effect.andThen(Ref.set(stoppedRef, true)), Effect.andThen(Fiber.interrupt(loopFiber)));
115
126
  return Effect.never;
116
- })))).pipe(Effect.catchAllCause(() => Effect.void)));
127
+ })))).pipe(Effect.forkIn(actorScope));
128
+ yield* Effect.forkDaemon(Effect.gen(function* () {
129
+ const loopExit = yield* Fiber.await(loopFiber);
130
+ if (loopExit._tag === "Success") yield* Scope.close(actorScope, Exit.void);
131
+ else yield* Scope.close(actorScope, loopExit);
132
+ }));
117
133
  const stop = Effect.gen(function* () {
118
134
  if (yield* Ref.get(stoppedRef)) return;
119
135
  if (lifecycle?.onShutdown !== void 0) yield* lifecycle.onShutdown();
@@ -121,7 +137,6 @@ const createRuntime = Effect.fn("effect-machine.runtime.create")(function* (mach
121
137
  yield* Fiber.interrupt(loopFiber);
122
138
  yield* Scope.close(stateScopeRef.current, Exit.void);
123
139
  yield* Scope.close(actorScope, Exit.void);
124
- yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
125
140
  yield* setExit(ActorExit.Stopped);
126
141
  }).pipe(Effect.asVoid);
127
142
  if (config.skipFinalizer !== true) yield* Effect.addFinalizer(() => stop);
@@ -174,7 +189,8 @@ const makeHandle = (stateRef, stoppedRef, eventQueue, exitDeferred, actorScope)
174
189
  exitDeferred,
175
190
  actorScope
176
191
  });
177
- const runtimeEventLoop = Effect.fn("effect-machine.runtime.eventLoop")(function* (machine, stateRef, eventQueue, stoppedRef, self, stateScopeRef, actorId, system, exitDeferred, hooks, lifecycle, wrapProcess) {
192
+ const runtimeEventLoop = Effect.fn("effect-machine.runtime.eventLoop")(function* (machine, stateRef, eventQueue, stoppedRef, self, stateScopeRef, actorId, system, exitDeferred, hooks, deferredReplyRef, lifecycle, wrapProcess, fork) {
193
+ const forkEffect = fork ?? Effect.runFork;
178
194
  /** Set the exit deferred exactly once. */
179
195
  const setExit = (exit) => Deferred.succeed(exitDeferred, exit).pipe(Effect.asVoid);
180
196
  const postponed = [];
@@ -242,7 +258,8 @@ const runtimeEventLoop = Effect.fn("effect-machine.runtime.eventLoop")(function*
242
258
  }
243
259
  yield* Deferred.succeed(queued.reply, decoded);
244
260
  } else yield* Deferred.succeed(queued.reply, result.reply);
245
- } else yield* Deferred.fail(queued.reply, new NoReplyError({
261
+ } else if (result.deferReply && deferredReplyRef !== void 0) deferredReplyRef.current = queued.reply;
262
+ else yield* Deferred.fail(queued.reply, new NoReplyError({
246
263
  actorId,
247
264
  eventTag: event._tag
248
265
  }));
@@ -260,16 +277,16 @@ const runtimeEventLoop = Effect.fn("effect-machine.runtime.eventLoop")(function*
260
277
  const shutdown = (exitReason) => Effect.gen(function* () {
261
278
  yield* Ref.set(stoppedRef, true);
262
279
  if (lifecycle?.onShutdown !== void 0) yield* lifecycle.onShutdown();
263
- settlePostponed(postponed, actorId);
280
+ settlePostponed(postponed, actorId, forkEffect);
264
281
  const remaining = yield* Queue.takeAll(eventQueue);
265
- for (const entry of remaining) if (entry._tag === "sendWait") Effect.runFork(Deferred.succeed(entry.done, void 0));
266
- else if (entry._tag === "ask") Effect.runFork(Deferred.fail(entry.reply, new NoReplyError({
282
+ for (const entry of remaining) if (entry._tag === "sendWait") forkEffect(Deferred.succeed(entry.done, void 0));
283
+ else if (entry._tag === "ask") forkEffect(Deferred.fail(entry.reply, new NoReplyError({
267
284
  actorId,
268
285
  eventTag: entry.event._tag
269
286
  })));
270
287
  else if (entry._tag === "call") {
271
288
  const currentState = yield* SubscriptionRef.get(stateRef);
272
- Effect.runFork(Deferred.succeed(entry.reply, {
289
+ forkEffect(Deferred.succeed(entry.reply, {
273
290
  newState: currentState,
274
291
  previousState: currentState,
275
292
  transitioned: false,
@@ -296,10 +313,10 @@ const runtimeEventLoop = Effect.fn("effect-machine.runtime.eventLoop")(function*
296
313
  const { shouldStop, stateChanged } = yield* (wrapProcess !== void 0 ? Effect.gen(function* () {
297
314
  return yield* wrapProcess(yield* SubscriptionRef.get(stateRef), eventQueued.event, processInner);
298
315
  }) : processInner).pipe(Effect.catchAllCause((cause) => {
299
- if (queued._tag === "sendWait") Effect.runFork(Deferred.succeed(queued.done, void 0));
300
- else if (queued._tag === "ask") Effect.runFork(Deferred.die(queued.reply, cause));
301
- else if (queued._tag === "call") Effect.runFork(Deferred.failCause(queued.reply, cause));
302
- return shutdown(ActorExit.Defect(cause, "transition")).pipe(Effect.zipRight(Effect.failCause(cause)));
316
+ if (queued._tag === "sendWait") forkEffect(Deferred.failCause(queued.done, cause));
317
+ else if (queued._tag === "ask") forkEffect(Deferred.die(queued.reply, cause));
318
+ else if (queued._tag === "call") forkEffect(Deferred.failCause(queued.reply, cause));
319
+ return shutdown(ActorExit.Defect(cause, "transition")).pipe(Effect.andThen(Effect.failCause(cause)));
303
320
  }));
304
321
  if (shouldStop) {
305
322
  const finalState = yield* SubscriptionRef.get(stateRef);
@@ -323,12 +340,12 @@ const runtimeEventLoop = Effect.fn("effect-machine.runtime.eventLoop")(function*
323
340
  }
324
341
  });
325
342
  /** Settle all pending Deferreds in the postpone buffer on shutdown. */
326
- const settlePostponed = (postponed, actorId) => {
327
- for (const entry of postponed) if (entry._tag === "ask") Effect.runFork(Deferred.fail(entry.reply, new NoReplyError({
343
+ const settlePostponed = (postponed, actorId, forkFn) => {
344
+ for (const entry of postponed) if (entry._tag === "ask") forkFn(Deferred.fail(entry.reply, new NoReplyError({
328
345
  actorId,
329
346
  eventTag: entry.event._tag
330
347
  })));
331
- else if (entry._tag === "sendWait") Effect.runFork(Deferred.succeed(entry.done, void 0));
348
+ else if (entry._tag === "sendWait") forkFn(Deferred.succeed(entry.done, void 0));
332
349
  postponed.length = 0;
333
350
  };
334
351
  //#endregion
@@ -1,4 +1,4 @@
1
- import { EffectsDef, GuardsDef, MachineContext } from "../slot.js";
1
+ import { MachineContext, SlotsDef } from "../slot.js";
2
2
  import { Machine, MachineRef, SpawnEffect, Transition } from "../machine.js";
3
3
  import { ActorSystem } from "../actor.js";
4
4
  import { Cause, Effect, Scope } from "effect";
@@ -21,7 +21,7 @@ interface TransitionExecutionResult<S> {
21
21
  *
22
22
  * Used by:
23
23
  * - executeTransition (actor event loop, testing)
24
- * - persistent-actor replay (restore, replayTo)
24
+ * - Machine.replay (event sourcing restore)
25
25
  *
26
26
  * @internal
27
27
  */
@@ -29,7 +29,7 @@ declare const runTransitionHandler: <S extends {
29
29
  readonly _tag: string;
30
30
  }, E extends {
31
31
  readonly _tag: string;
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, actorId: string) => Effect.Effect<{
32
+ }, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, transition: Transition<S, E, SD, R>, state: S, event: E, self: MachineRef<E>, system: ActorSystem, actorId: string) => Effect.Effect<{
33
33
  newState: S;
34
34
  hasReply: boolean;
35
35
  deferReply: boolean;
@@ -50,7 +50,7 @@ declare const executeTransition: <S extends {
50
50
  readonly _tag: string;
51
51
  }, E extends {
52
52
  readonly _tag: string;
53
- }, 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, actorId: string) => Effect.Effect<{
53
+ }, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, currentState: S, event: E, self: MachineRef<E>, system: ActorSystem, actorId: string) => Effect.Effect<{
54
54
  newState: S;
55
55
  transitioned: boolean;
56
56
  reenter: boolean;
@@ -111,7 +111,7 @@ declare const shouldPostpone: <S extends {
111
111
  readonly _tag: string;
112
112
  }, E extends {
113
113
  readonly _tag: string;
114
- }, R>(machine: Machine<S, E, R, any, any, any, any>, stateTag: string, eventTag: string) => boolean;
114
+ }, R>(machine: Machine<S, E, R, any, any, any>, stateTag: string, eventTag: string) => boolean;
115
115
  /**
116
116
  * Process a single event through the machine.
117
117
  *
@@ -128,7 +128,7 @@ declare const processEventCore: <S extends {
128
128
  readonly _tag: string;
129
129
  }, E extends {
130
130
  readonly _tag: string;
131
- }, 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: {
131
+ }, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, currentState: S, event: E, self: MachineRef<E>, stateScopeRef: {
132
132
  current: Scope.CloseableScope;
133
133
  }, system: ActorSystem, actorId: string, hooks?: ProcessEventHooks<S, E> | undefined) => Effect.Effect<{
134
134
  newState: S;
@@ -150,7 +150,7 @@ declare const runSpawnEffects: <S extends {
150
150
  readonly _tag: string;
151
151
  }, E extends {
152
152
  readonly _tag: string;
153
- }, 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, actorId: string, onError?: ((info: ProcessEventError<S, E>) => Effect.Effect<void>) | undefined, onSpawnDefect?: ((cause: Cause.Cause<unknown>) => Effect.Effect<void>) | undefined) => Effect.Effect<void, never, Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
153
+ }, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, state: S, event: E, self: MachineRef<E>, stateScope: Scope.CloseableScope, system: ActorSystem, actorId: string, onError?: ((info: ProcessEventError<S, E>) => Effect.Effect<void>) | undefined, onSpawnDefect?: ((cause: Cause.Cause<unknown>) => Effect.Effect<void>) | undefined) => Effect.Effect<void, never, Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
154
154
  /**
155
155
  * Resolve which transition should fire for a given state and event.
156
156
  * Uses indexed O(1) lookup. First matching transition wins.
@@ -159,7 +159,7 @@ declare const resolveTransition: <S extends {
159
159
  readonly _tag: string;
160
160
  }, E extends {
161
161
  readonly _tag: string;
162
- }, R>(machine: Machine<S, E, R, any, any, any, any>, currentState: S, event: E) => (typeof machine.transitions)[number] | undefined;
162
+ }, R>(machine: Machine<S, E, R, any, any, any>, currentState: S, event: E) => (typeof machine.transitions)[number] | undefined;
163
163
  /**
164
164
  * Invalidate cached index for a machine (call after mutation).
165
165
  */
@@ -174,7 +174,7 @@ declare const findTransitions: <S extends {
174
174
  readonly _tag: string;
175
175
  }, E extends {
176
176
  readonly _tag: string;
177
- }, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(input: Machine<S, E, R, any, any, GD, EFD>, stateTag: string, eventTag: string) => ReadonlyArray<Transition<S, E, GD, EFD, R>>;
177
+ }, R, SD extends SlotsDef = Record<string, never>>(machine: Machine<S, E, R, any, any, SD>, stateTag: string, eventTag: string) => ReadonlyArray<Transition<S, E, SD, R>>;
178
178
  /**
179
179
  * Find all spawn effects for a state.
180
180
  * Returns empty array if no matches.
@@ -185,6 +185,6 @@ declare const findSpawnEffects: <S extends {
185
185
  readonly _tag: string;
186
186
  }, E extends {
187
187
  readonly _tag: string;
188
- }, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(machine: Machine<S, E, R, any, any, GD, EFD>, stateTag: string) => ReadonlyArray<SpawnEffect<S, E, EFD, R>>;
188
+ }, R, SD extends SlotsDef = Record<string, never>>(machine: Machine<S, E, R, any, any, SD>, stateTag: string) => ReadonlyArray<SpawnEffect<S, E, SD, R>>;
189
189
  //#endregion
190
190
  export { ProcessEventError, ProcessEventHooks, ProcessEventResult, TransitionExecutionResult, executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler, shouldPostpone };
@@ -17,7 +17,7 @@ import { Cause, Effect, Exit, Scope } from "effect";
17
17
  *
18
18
  * Used by:
19
19
  * - executeTransition (actor event loop, testing)
20
- * - persistent-actor replay (restore, replayTo)
20
+ * - Machine.replay (event sourcing restore)
21
21
  *
22
22
  * @internal
23
23
  */
@@ -29,12 +29,10 @@ const runTransitionHandler = Effect.fn("effect-machine.runTransitionHandler")(fu
29
29
  self,
30
30
  system
31
31
  };
32
- const { guards, effects } = machine._slots;
33
32
  const handlerCtx = {
34
33
  state,
35
34
  event,
36
- guards,
37
- effects
35
+ slots: machine._slots
38
36
  };
39
37
  const raw = transition.handler(handlerCtx);
40
38
  const resolved = isEffect(raw) ? yield* raw.pipe(Effect.provideService(machine.Context, ctx)) : raw;
@@ -118,7 +116,7 @@ const processEventCore = Effect.fn("effect-machine.processEventCore")(function*
118
116
  state: currentState,
119
117
  event,
120
118
  cause
121
- }).pipe(Effect.zipRight(Effect.failCause(cause).pipe(Effect.orDie)));
119
+ }).pipe(Effect.andThen(Effect.failCause(cause).pipe(Effect.orDie)));
122
120
  }));
123
121
  if (!result.transitioned) return {
124
122
  newState: currentState,
@@ -166,7 +164,7 @@ const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (m
166
164
  self,
167
165
  system
168
166
  };
169
- const { effects: effectSlots } = machine._slots;
167
+ const slots = machine._slots;
170
168
  const reportError = onError;
171
169
  const defectSignal = onSpawnDefect;
172
170
  for (const spawnEffect of spawnEffects) {
@@ -175,7 +173,7 @@ const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (m
175
173
  state,
176
174
  event,
177
175
  self,
178
- effects: effectSlots,
176
+ slots,
179
177
  system
180
178
  }).pipe(Effect.provideService(machine.Context, ctx), Effect.catchAllCause((cause) => {
181
179
  if (Cause.isInterruptedOnly(cause)) return Effect.interrupt;
@@ -186,7 +184,7 @@ const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (m
186
184
  cause
187
185
  }) : Effect.void;
188
186
  const signal = defectSignal !== void 0 ? defectSignal(cause) : Effect.void;
189
- return report.pipe(Effect.zipRight(signal), Effect.zipRight(Effect.failCause(cause).pipe(Effect.orDie)));
187
+ return report.pipe(Effect.andThen(signal), Effect.andThen(Effect.failCause(cause).pipe(Effect.orDie)));
190
188
  }));
191
189
  yield* Effect.forkScoped(effect).pipe(Effect.provideService(Scope.Scope, stateScope));
192
190
  }
@@ -261,8 +259,8 @@ const getIndex = (machine) => {
261
259
  *
262
260
  * O(1) lookup after first access (index is lazily built).
263
261
  */
264
- const findTransitions = (input, stateTag, eventTag) => {
265
- const index = getIndex(input);
262
+ const findTransitions = (machine, stateTag, eventTag) => {
263
+ const index = getIndex(machine);
266
264
  const specific = index.transitions.get(stateTag)?.get(eventTag) ?? [];
267
265
  if (specific.length > 0) return specific;
268
266
  return index.transitions.get("*")?.get(eventTag) ?? [];
@@ -1,5 +1,9 @@
1
1
  import { Effect, Stream } from "effect";
2
2
  //#region src/internal/utils.ts
3
+ /**
4
+ * Internal utilities for effect-machine.
5
+ * @internal
6
+ */
3
7
  const ReplyResultSymbol = Symbol.for("effect-machine/ReplyResult");
4
8
  /**
5
9
  * Create a reply result for ask-bearing event handlers.
@@ -50,7 +54,7 @@ const getTag = (constructorOrValue) => {
50
54
  }
51
55
  };
52
56
  /** Check if a value is an Effect */
53
- const isEffect = (value) => typeof value === "object" && value !== null && Effect.EffectTypeId in value;
57
+ const isEffect = Effect.isEffect;
54
58
  /**
55
59
  * Stub ActorSystem that dies on any method call.
56
60
  * Used in contexts where spawning/system access isn't supported