effect-machine 0.17.1 → 0.19.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 (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -54
  3. package/dist/actor.d.ts +8 -45
  4. package/dist/actor.js +157 -231
  5. package/dist/atom.d.ts +31 -0
  6. package/dist/atom.js +29 -0
  7. package/dist/cluster/adapters/in-memory.d.ts +0 -1
  8. package/dist/cluster/adapters/in-memory.js +10 -5
  9. package/dist/cluster/entity-actor-ref.d.ts +2 -3
  10. package/dist/cluster/entity-actor-ref.js +14 -17
  11. package/dist/cluster/entity-machine.d.ts +2 -8
  12. package/dist/cluster/entity-machine.js +57 -34
  13. package/dist/cluster/index.js +1 -1
  14. package/dist/cluster/persistence.d.ts +0 -1
  15. package/dist/cluster/to-entity.d.ts +19 -21
  16. package/dist/cluster/to-entity.js +18 -19
  17. package/dist/errors.d.ts +11 -37
  18. package/dist/errors.js +12 -30
  19. package/dist/index.d.ts +4 -5
  20. package/dist/index.js +4 -5
  21. package/dist/inspection.d.ts +0 -1
  22. package/dist/inspection.js +24 -13
  23. package/dist/internal/brands.d.ts +0 -1
  24. package/dist/internal/event-advancement.d.ts +50 -0
  25. package/dist/internal/event-advancement.js +79 -0
  26. package/dist/internal/inspection.d.ts +5 -9
  27. package/dist/internal/inspection.js +31 -10
  28. package/dist/internal/machine-definition.d.ts +16 -0
  29. package/dist/internal/runtime.d.ts +1 -161
  30. package/dist/internal/runtime.js +191 -150
  31. package/dist/internal/transition.d.ts +4 -135
  32. package/dist/internal/transition.js +82 -157
  33. package/dist/internal/utils.d.ts +3 -40
  34. package/dist/internal/utils.js +2 -2
  35. package/dist/machine.d.ts +47 -137
  36. package/dist/machine.js +147 -215
  37. package/dist/schema.d.ts +42 -21
  38. package/dist/schema.js +28 -18
  39. package/dist/supervision.d.ts +1 -24
  40. package/dist/supervision.js +2 -3
  41. package/dist/testing.d.ts +15 -29
  42. package/dist/testing.js +76 -95
  43. package/package.json +41 -35
  44. package/dist/slot.d.ts +0 -159
  45. package/dist/slot.js +0 -165
  46. package/v3/dist/_virtual/_rolldown/runtime.js +0 -13
  47. package/v3/dist/actor.d.ts +0 -250
  48. package/v3/dist/actor.js +0 -577
  49. package/v3/dist/cluster/adapters/in-memory.d.ts +0 -15
  50. package/v3/dist/cluster/adapters/in-memory.js +0 -62
  51. package/v3/dist/cluster/entity-actor-ref.d.ts +0 -49
  52. package/v3/dist/cluster/entity-actor-ref.js +0 -19
  53. package/v3/dist/cluster/entity-machine.d.ts +0 -74
  54. package/v3/dist/cluster/entity-machine.js +0 -166
  55. package/v3/dist/cluster/index.d.ts +0 -6
  56. package/v3/dist/cluster/index.js +0 -6
  57. package/v3/dist/cluster/persistence.d.ts +0 -48
  58. package/v3/dist/cluster/persistence.js +0 -14
  59. package/v3/dist/cluster/to-entity.d.ts +0 -69
  60. package/v3/dist/cluster/to-entity.js +0 -59
  61. package/v3/dist/errors.d.ts +0 -95
  62. package/v3/dist/errors.js +0 -54
  63. package/v3/dist/index.d.ts +0 -11
  64. package/v3/dist/index.js +0 -9
  65. package/v3/dist/inspection.d.ts +0 -151
  66. package/v3/dist/inspection.js +0 -128
  67. package/v3/dist/internal/brands.d.ts +0 -50
  68. package/v3/dist/internal/inspection.d.ts +0 -11
  69. package/v3/dist/internal/inspection.js +0 -20
  70. package/v3/dist/internal/runtime.d.ts +0 -161
  71. package/v3/dist/internal/runtime.js +0 -360
  72. package/v3/dist/internal/transition.d.ts +0 -190
  73. package/v3/dist/internal/transition.js +0 -278
  74. package/v3/dist/internal/utils.d.ts +0 -101
  75. package/v3/dist/internal/utils.js +0 -75
  76. package/v3/dist/machine.d.ts +0 -398
  77. package/v3/dist/machine.js +0 -487
  78. package/v3/dist/schema.d.ts +0 -174
  79. package/v3/dist/schema.js +0 -206
  80. package/v3/dist/slot.d.ts +0 -158
  81. package/v3/dist/slot.js +0 -165
  82. package/v3/dist/supervision.d.ts +0 -97
  83. package/v3/dist/supervision.js +0 -42
  84. package/v3/dist/testing.d.ts +0 -151
  85. package/v3/dist/testing.js +0 -189
  86. /package/{v3/dist/internal/brands.js → dist/internal/machine-definition.js} +0 -0
@@ -1,360 +0,0 @@
1
- import { INTERNAL_INIT_EVENT } from "./utils.js";
2
- import { NoReplyError } from "../errors.js";
3
- import { processEventCore, runSpawnEffects, shouldPostpone } from "./transition.js";
4
- import { ActorExit } from "../supervision.js";
5
- import { ActorSystem } from "../actor.js";
6
- import { Cause, Deferred, Effect, Exit, Fiber, Queue, Ref, Runtime, Schema, Scope, SubscriptionRef } from "effect";
7
- //#region src/internal/runtime.ts
8
- /**
9
- * Shared runtime kernel for machine event processing.
10
- *
11
- * Provides a single-queue event loop with:
12
- * - Sequential event processing (no split-mailbox race)
13
- * - Postpone buffer with drain-on-state-change (gen_statem)
14
- * - Background effect lifecycle (under actorScope fault boundary)
15
- * - Spawn effect lifecycle (per-state scope)
16
- * - Final state detection → stop
17
- * - Reply settlement (call/ask Deferreds)
18
- * - Reply schema validation
19
- * - Lifecycle hooks for actor-specific concerns (inspection, listeners, etc.)
20
- * - ActorExit with exit reason (Final/Stopped/Defect) via exitDeferred
21
- *
22
- * Used by entity-machine and local actor (actor.ts delegates here).
23
- *
24
- * @internal
25
- */
26
- /**
27
- * Create a runtime for a machine. Returns a handle for sending events
28
- * and querying state. The runtime owns:
29
- * - Event loop fiber
30
- * - Postpone buffer
31
- * - Background effects (under actorScope)
32
- * - State scope (spawn effects)
33
- * - Final state detection
34
- * - Exit reason via exitDeferred
35
- *
36
- * Resources (stateRef, eventQueue, stoppedRef) are either cell-provided
37
- * or allocated fresh by the runtime.
38
- *
39
- * @internal
40
- */
41
- const createRuntime = Effect.fn("effect-machine.runtime.create")(function* (machine, system, config) {
42
- const { actorId, hooks, lifecycle } = config;
43
- const rt = yield* Effect.runtime();
44
- const fork = Runtime.runFork(rt);
45
- const stateRef = config.cellResources?.stateRef ?? (yield* SubscriptionRef.make(machine.initial));
46
- const stoppedRef = config.cellResources?.stoppedRef ?? (yield* Ref.make(false));
47
- const eventQueue = config.cellResources?.eventQueue ?? (yield* config.queueFactory ?? Queue.unbounded());
48
- const exitDeferred = yield* Deferred.make();
49
- const actorScope = yield* Scope.make();
50
- const deferredReplyRef = { current: void 0 };
51
- const selfSend = Effect.fn("effect-machine.runtime.self.send")(function* (event) {
52
- if (!(yield* Ref.get(stoppedRef))) yield* Queue.offer(eventQueue, {
53
- _tag: "send",
54
- event
55
- });
56
- });
57
- const childPrefix = config.childIdPrefix ?? "";
58
- const defaultSpawn = (childId, childMachine) => system.spawn(`${childPrefix}${childId}`, childMachine).pipe(Effect.provideService(ActorSystem, system));
59
- const onChildSpawned = config.onChildSpawned;
60
- const self = {
61
- send: selfSend,
62
- cast: selfSend,
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
- })
73
- };
74
- const stateScopeRef = { current: yield* Scope.make() };
75
- const initEvent = { _tag: INTERNAL_INIT_EVENT };
76
- const ctx = {
77
- actorId,
78
- state: machine.initial,
79
- event: initEvent,
80
- self,
81
- system
82
- };
83
- const slots = machine._slots;
84
- const loopFiberRef = { current: void 0 };
85
- /** Set the exit deferred exactly once. */
86
- const setExit = (exit) => Deferred.succeed(exitDeferred, exit).pipe(Effect.asVoid);
87
- const startDeferred = yield* Deferred.make();
88
- const startedRef = yield* Ref.make(false);
89
- const start = Effect.gen(function* () {
90
- if (yield* Ref.getAndSet(startedRef, true)) {
91
- yield* Deferred.await(startDeferred);
92
- return;
93
- }
94
- const backgroundFibers = [];
95
- for (const bg of machine.backgroundEffects) {
96
- const fiber = yield* bg.handler({
97
- actorId,
98
- state: machine.initial,
99
- event: initEvent,
100
- self,
101
- slots,
102
- system
103
- }).pipe(Effect.provideService(machine.Context, ctx), Effect.forkIn(actorScope));
104
- backgroundFibers.push(fiber);
105
- }
106
- if (lifecycle?.onInitialSpawnEffects !== void 0) yield* lifecycle.onInitialSpawnEffects(machine.initial);
107
- 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);
108
- yield* runSpawnEffects(machine, machine.initial, initEvent, self, stateScopeRef.current, system, actorId, hooks?.onError, initialSpawnDefectSignal).pipe(Effect.catchAllCause((cause) => Effect.gen(function* () {
109
- yield* Ref.set(stoppedRef, true);
110
- yield* Scope.close(stateScopeRef.current, Exit.void);
111
- yield* Scope.close(actorScope, Exit.void);
112
- yield* Deferred.succeed(exitDeferred, ActorExit.Defect(cause, "initial-spawn"));
113
- return yield* Effect.failCause(cause);
114
- })));
115
- if (machine.finalStates.has(machine.initial._tag)) {
116
- if (lifecycle?.onFinal !== void 0) yield* lifecycle.onFinal(machine.initial);
117
- yield* Ref.set(stoppedRef, true);
118
- yield* Scope.close(stateScopeRef.current, Exit.void);
119
- yield* Scope.close(actorScope, Exit.void);
120
- yield* setExit(ActorExit.Final(machine.initial));
121
- yield* Deferred.succeed(startDeferred, void 0);
122
- return;
123
- }
124
- const augmentedHooks = {
125
- ...hooks,
126
- 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)
127
- };
128
- const loopFiber = yield* Effect.forkDaemon(runtimeEventLoop(machine, stateRef, eventQueue, stoppedRef, self, stateScopeRef, actorId, system, exitDeferred, augmentedHooks, deferredReplyRef, lifecycle, config.wrapProcess, fork));
129
- loopFiberRef.current = loopFiber;
130
- if (backgroundFibers.length > 0) yield* Effect.raceAll(backgroundFibers.map((fiber) => Fiber.await(fiber).pipe(Effect.flatMap((exit) => {
131
- 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)));
132
- return Effect.never;
133
- })))).pipe(Effect.forkIn(actorScope));
134
- yield* Effect.forkDaemon(Effect.gen(function* () {
135
- const loopExit = yield* Fiber.await(loopFiber);
136
- if (loopExit._tag === "Success") yield* Scope.close(actorScope, Exit.void);
137
- else yield* Scope.close(actorScope, loopExit);
138
- }));
139
- yield* Deferred.succeed(startDeferred, void 0);
140
- }).pipe(Effect.catchAllCause((cause) => Deferred.failCause(startDeferred, cause).pipe(Effect.andThen(Effect.failCause(cause)))));
141
- const stop = Effect.gen(function* () {
142
- if (yield* Ref.get(stoppedRef)) return;
143
- if (lifecycle?.onShutdown !== void 0) yield* lifecycle.onShutdown();
144
- yield* Ref.set(stoppedRef, true);
145
- const loopFiber = loopFiberRef.current;
146
- if (loopFiber !== void 0) yield* Fiber.interrupt(loopFiber);
147
- yield* Scope.close(stateScopeRef.current, Exit.void);
148
- yield* Scope.close(actorScope, Exit.void);
149
- yield* setExit(ActorExit.Stopped);
150
- }).pipe(Effect.asVoid);
151
- if (config.skipFinalizer !== true) yield* Effect.addFinalizer(() => stop);
152
- return {
153
- ...makeHandle(stateRef, stoppedRef, eventQueue, exitDeferred, actorScope),
154
- stop,
155
- start
156
- };
157
- });
158
- /**
159
- * Build the runtime handle (send/ask/getState/isStopped).
160
- * Shared between initial-final and normal paths.
161
- */
162
- const makeHandle = (stateRef, stoppedRef, eventQueue, exitDeferred, actorScope) => ({
163
- send: (event) => Effect.gen(function* () {
164
- if (!(yield* Ref.get(stoppedRef))) yield* Queue.offer(eventQueue, {
165
- _tag: "send",
166
- event
167
- });
168
- }),
169
- sendWait: (event) => Effect.gen(function* () {
170
- if (!(yield* Ref.get(stoppedRef))) {
171
- const done = yield* Deferred.make();
172
- yield* Queue.offer(eventQueue, {
173
- _tag: "sendWait",
174
- event,
175
- done
176
- });
177
- yield* Deferred.await(done);
178
- }
179
- }),
180
- ask: (event) => Effect.gen(function* () {
181
- if (yield* Ref.get(stoppedRef)) return yield* new NoReplyError({
182
- actorId: "stopped",
183
- eventTag: event._tag
184
- });
185
- const reply = yield* Deferred.make();
186
- yield* Queue.offer(eventQueue, {
187
- _tag: "ask",
188
- event,
189
- reply
190
- });
191
- return yield* Deferred.await(reply);
192
- }),
193
- getState: SubscriptionRef.get(stateRef),
194
- stateRef,
195
- isStopped: Ref.get(stoppedRef),
196
- stop: Effect.void,
197
- start: Effect.void,
198
- _queue: eventQueue,
199
- _stoppedRef: stoppedRef,
200
- exitDeferred,
201
- actorScope
202
- });
203
- const runtimeEventLoop = Effect.fn("effect-machine.runtime.eventLoop")(function* (machine, stateRef, eventQueue, stoppedRef, self, stateScopeRef, actorId, system, exitDeferred, hooks, deferredReplyRef, lifecycle, wrapProcess, fork) {
204
- const forkEffect = fork ?? Effect.runFork;
205
- /** Set the exit deferred exactly once. */
206
- const setExit = (exit) => Deferred.succeed(exitDeferred, exit).pipe(Effect.asVoid);
207
- const postponed = [];
208
- const hasPostponeRules = machine.postponeRules.length > 0;
209
- const processQueued = Effect.fn("effect-machine.runtime.processQueued")(function* (queued) {
210
- const event = queued.event;
211
- const currentState = yield* SubscriptionRef.get(stateRef);
212
- if (hasPostponeRules && shouldPostpone(machine, currentState._tag, event._tag)) {
213
- if (queued._tag === "call") {
214
- const postponedResult = {
215
- newState: currentState,
216
- previousState: currentState,
217
- transitioned: false,
218
- lifecycleRan: false,
219
- isFinal: false,
220
- hasReply: false,
221
- deferReply: false,
222
- reply: void 0,
223
- postponed: true
224
- };
225
- yield* Deferred.succeed(queued.reply, postponedResult);
226
- }
227
- if (queued._tag === "sendWait") yield* Deferred.succeed(queued.done, void 0);
228
- postponed.push({
229
- _tag: "send",
230
- event
231
- });
232
- return {
233
- shouldStop: false,
234
- stateChanged: false,
235
- result: {
236
- newState: currentState,
237
- previousState: currentState,
238
- transitioned: false,
239
- lifecycleRan: false,
240
- isFinal: false,
241
- hasReply: false,
242
- deferReply: false,
243
- reply: void 0,
244
- postponed: true
245
- }
246
- };
247
- }
248
- if (lifecycle?.onEvent !== void 0) yield* lifecycle.onEvent(currentState, event);
249
- const result = yield* processEventCore(machine, currentState, event, self, stateScopeRef, system, actorId, hooks);
250
- if (result.transitioned) yield* SubscriptionRef.set(stateRef, result.newState);
251
- if (lifecycle?.onStateChange !== void 0 && result.transitioned) yield* lifecycle.onStateChange(result, event);
252
- switch (queued._tag) {
253
- case "call":
254
- yield* Deferred.succeed(queued.reply, result);
255
- break;
256
- case "sendWait":
257
- yield* Deferred.succeed(queued.done, void 0);
258
- break;
259
- case "ask":
260
- if (result.hasReply) {
261
- const replySchema = machine._replySchemas?.get(event._tag);
262
- if (replySchema !== void 0) {
263
- const decoded = yield* Schema.decodeUnknown(replySchema)(result.reply).pipe(Effect.catchAll((decodeError) => Effect.gen(function* () {
264
- yield* Deferred.die(queued.reply, decodeError);
265
- return yield* Effect.die(decodeError);
266
- })));
267
- yield* Deferred.succeed(queued.reply, decoded);
268
- } else yield* Deferred.succeed(queued.reply, result.reply);
269
- } else if (result.deferReply && deferredReplyRef !== void 0) deferredReplyRef.current = queued.reply;
270
- else yield* Deferred.fail(queued.reply, new NoReplyError({
271
- actorId,
272
- eventTag: event._tag
273
- }));
274
- break;
275
- }
276
- if (lifecycle?.onProcessed !== void 0 && result.transitioned) yield* lifecycle.onProcessed(result, event);
277
- const shouldStop = result.isFinal && result.lifecycleRan;
278
- if (shouldStop && lifecycle?.onFinal !== void 0) yield* lifecycle.onFinal(result.newState);
279
- return {
280
- shouldStop,
281
- stateChanged: result.lifecycleRan,
282
- result
283
- };
284
- });
285
- const shutdown = (exitReason) => Effect.gen(function* () {
286
- yield* Ref.set(stoppedRef, true);
287
- if (lifecycle?.onShutdown !== void 0) yield* lifecycle.onShutdown();
288
- settlePostponed(postponed, actorId, forkEffect);
289
- const remaining = yield* Queue.takeAll(eventQueue);
290
- for (const entry of remaining) if (entry._tag === "sendWait") forkEffect(Deferred.succeed(entry.done, void 0));
291
- else if (entry._tag === "ask") forkEffect(Deferred.fail(entry.reply, new NoReplyError({
292
- actorId,
293
- eventTag: entry.event._tag
294
- })));
295
- else if (entry._tag === "call") {
296
- const currentState = yield* SubscriptionRef.get(stateRef);
297
- forkEffect(Deferred.succeed(entry.reply, {
298
- newState: currentState,
299
- previousState: currentState,
300
- transitioned: false,
301
- lifecycleRan: false,
302
- isFinal: machine.finalStates.has(currentState._tag),
303
- hasReply: false,
304
- deferReply: false,
305
- reply: void 0,
306
- postponed: false
307
- }));
308
- }
309
- yield* Scope.close(stateScopeRef.current, Exit.void);
310
- yield* setExit(exitReason);
311
- });
312
- while (true) {
313
- const queued = yield* Queue.take(eventQueue);
314
- if (queued._tag === "drain") {
315
- yield* shutdown(ActorExit.Stopped);
316
- yield* Deferred.succeed(queued.done, void 0);
317
- return;
318
- }
319
- const eventQueued = queued;
320
- const processInner = processQueued(eventQueued);
321
- const { shouldStop, stateChanged } = yield* (wrapProcess !== void 0 ? Effect.gen(function* () {
322
- return yield* wrapProcess(yield* SubscriptionRef.get(stateRef), eventQueued.event, processInner);
323
- }) : processInner).pipe(Effect.catchAllCause((cause) => {
324
- if (queued._tag === "sendWait") forkEffect(Deferred.failCause(queued.done, cause));
325
- else if (queued._tag === "ask") forkEffect(Deferred.die(queued.reply, cause));
326
- else if (queued._tag === "call") forkEffect(Deferred.failCause(queued.reply, cause));
327
- return shutdown(ActorExit.Defect(cause, "transition")).pipe(Effect.andThen(Effect.failCause(cause)));
328
- }));
329
- if (shouldStop) {
330
- const finalState = yield* SubscriptionRef.get(stateRef);
331
- yield* shutdown(ActorExit.Final(finalState));
332
- return;
333
- }
334
- let drainTriggered = stateChanged;
335
- while (drainTriggered && postponed.length > 0) {
336
- drainTriggered = false;
337
- const drained = postponed.splice(0);
338
- for (const entry of drained) {
339
- const drain = yield* processQueued(entry);
340
- if (drain.shouldStop) {
341
- const finalState = yield* SubscriptionRef.get(stateRef);
342
- yield* shutdown(ActorExit.Final(finalState));
343
- return;
344
- }
345
- if (drain.stateChanged) drainTriggered = true;
346
- }
347
- }
348
- }
349
- });
350
- /** Settle all pending Deferreds in the postpone buffer on shutdown. */
351
- const settlePostponed = (postponed, actorId, forkFn) => {
352
- for (const entry of postponed) if (entry._tag === "ask") forkFn(Deferred.fail(entry.reply, new NoReplyError({
353
- actorId,
354
- eventTag: entry.event._tag
355
- })));
356
- else if (entry._tag === "sendWait") forkFn(Deferred.succeed(entry.done, void 0));
357
- postponed.length = 0;
358
- };
359
- //#endregion
360
- export { createRuntime };
@@ -1,190 +0,0 @@
1
- import { MachineContext, SlotsDef } from "../slot.js";
2
- import { Machine, MachineRef, SpawnEffect, Transition } from "../machine.js";
3
- import { ActorSystem } from "../actor.js";
4
- import { Cause, Effect, Scope } from "effect";
5
-
6
- //#region src/internal/transition.d.ts
7
- /**
8
- * Result of executing a transition.
9
- */
10
- interface TransitionExecutionResult<S> {
11
- /** New state after transition (or current state if no transition matched) */
12
- readonly newState: S;
13
- /** Whether a transition was executed */
14
- readonly transitioned: boolean;
15
- /** Whether reenter was specified on the transition */
16
- readonly reenter: boolean;
17
- }
18
- /**
19
- * Run a transition handler and return the new state.
20
- * Shared logic for executing handlers with proper context.
21
- *
22
- * Used by:
23
- * - executeTransition (actor event loop, testing)
24
- * - Machine.replay (event sourcing restore)
25
- *
26
- * @internal
27
- */
28
- declare const runTransitionHandler: <S extends {
29
- readonly _tag: string;
30
- }, E extends {
31
- readonly _tag: string;
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
- newState: S;
34
- hasReply: boolean;
35
- deferReply: boolean;
36
- reply: unknown;
37
- }, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
38
- /**
39
- * Execute a transition for a given state and event.
40
- * Handles transition resolution, handler invocation, and guard/effect slot creation.
41
- *
42
- * Used by:
43
- * - processEvent in actor.ts (actual actor event loop)
44
- * - simulate in testing.ts (pure transition simulation)
45
- * - createTestHarness.send in testing.ts (step-by-step testing)
46
- *
47
- * @internal
48
- */
49
- declare const executeTransition: <S extends {
50
- readonly _tag: string;
51
- }, E extends {
52
- readonly _tag: string;
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
- newState: S;
55
- transitioned: boolean;
56
- reenter: boolean;
57
- hasReply: boolean;
58
- deferReply: boolean;
59
- reply: unknown;
60
- }, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
61
- /**
62
- * Optional hooks for event processing inspection/tracing.
63
- */
64
- interface ProcessEventHooks<S, E> {
65
- /** Called before running spawn effects */
66
- readonly onSpawnEffect?: (state: S) => Effect.Effect<void>;
67
- /** Called after transition completes */
68
- readonly onTransition?: (from: S, to: S, event: E) => Effect.Effect<void>;
69
- /** Called when a transition handler or spawn effect fails with a defect */
70
- readonly onError?: (info: ProcessEventError<S, E>) => Effect.Effect<void>;
71
- /** Called when a forked spawn fiber defects — signals the runtime to set exitDeferred */
72
- readonly onSpawnDefect?: (cause: Cause.Cause<unknown>) => Effect.Effect<void>;
73
- }
74
- /**
75
- * Error info for inspection hooks.
76
- */
77
- interface ProcessEventError<S, E> {
78
- readonly phase: "transition" | "spawn";
79
- readonly state: S;
80
- readonly event: E;
81
- readonly cause: Cause.Cause<unknown>;
82
- }
83
- /**
84
- * Result of processing an event through the machine.
85
- */
86
- interface ProcessEventResult<S> {
87
- /** New state after processing */
88
- readonly newState: S;
89
- /** Previous state before processing */
90
- readonly previousState: S;
91
- /** Whether a transition occurred */
92
- readonly transitioned: boolean;
93
- /** Whether lifecycle effects ran (state change or reenter) */
94
- readonly lifecycleRan: boolean;
95
- /** Whether new state is final */
96
- readonly isFinal: boolean;
97
- /** Whether the handler provided a reply (structural, not value-based) */
98
- readonly hasReply: boolean;
99
- /** Whether the handler deferred the reply to a spawn handler (Machine.deferReply) */
100
- readonly deferReply: boolean;
101
- /** Domain reply value from handler (used by ask). Only meaningful when hasReply is true. */
102
- readonly reply?: unknown;
103
- /** Whether the event was postponed (buffered for retry after next state change) */
104
- readonly postponed: boolean;
105
- }
106
- /**
107
- * Check if an event should be postponed in the current state.
108
- * @internal
109
- */
110
- declare const shouldPostpone: <S extends {
111
- readonly _tag: string;
112
- }, E extends {
113
- readonly _tag: string;
114
- }, R>(machine: Machine<S, E, R, any, any, any>, stateTag: string, eventTag: string) => boolean;
115
- /**
116
- * Process a single event through the machine.
117
- *
118
- * Handles:
119
- * - Transition execution
120
- * - State scope lifecycle (close old, create new)
121
- * - Running spawn effects
122
- *
123
- * Optional hooks allow inspection/tracing without coupling to specific impl.
124
- *
125
- * @internal
126
- */
127
- declare const processEventCore: <S extends {
128
- readonly _tag: string;
129
- }, E extends {
130
- readonly _tag: string;
131
- }, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, currentState: S, event: E, self: MachineRef<E>, stateScopeRef: {
132
- current: Scope.CloseableScope;
133
- }, system: ActorSystem, actorId: string, hooks?: ProcessEventHooks<S, E> | undefined) => Effect.Effect<{
134
- newState: S;
135
- previousState: S;
136
- transitioned: boolean;
137
- lifecycleRan: boolean;
138
- isFinal: boolean;
139
- hasReply: boolean;
140
- deferReply: boolean;
141
- reply: unknown;
142
- postponed: boolean;
143
- }, never, Exclude<R, MachineContext<S, E, MachineRef<E>>> | Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
144
- /**
145
- * Run spawn effects for a state (forked into state scope, auto-cancelled on state exit).
146
- *
147
- * @internal
148
- */
149
- declare const runSpawnEffects: <S extends {
150
- readonly _tag: string;
151
- }, E extends {
152
- readonly _tag: string;
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
- /**
155
- * Resolve which transition should fire for a given state and event.
156
- * Uses indexed O(1) lookup. First matching transition wins.
157
- */
158
- declare const resolveTransition: <S extends {
159
- readonly _tag: string;
160
- }, E extends {
161
- readonly _tag: string;
162
- }, R>(machine: Machine<S, E, R, any, any, any>, currentState: S, event: E) => (typeof machine.transitions)[number] | undefined;
163
- /**
164
- * Invalidate cached index for a machine (call after mutation).
165
- */
166
- declare const invalidateIndex: (machine: object) => void;
167
- /**
168
- * Find all transitions matching a state/event pair.
169
- * Returns empty array if no matches.
170
- *
171
- * O(1) lookup after first access (index is lazily built).
172
- */
173
- declare const findTransitions: <S extends {
174
- readonly _tag: string;
175
- }, E extends {
176
- readonly _tag: string;
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
- /**
179
- * Find all spawn effects for a state.
180
- * Returns empty array if no matches.
181
- *
182
- * O(1) lookup after first access (index is lazily built).
183
- */
184
- declare const findSpawnEffects: <S extends {
185
- readonly _tag: string;
186
- }, E extends {
187
- readonly _tag: string;
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
- //#endregion
190
- export { ProcessEventError, ProcessEventHooks, ProcessEventResult, TransitionExecutionResult, executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler, shouldPostpone };