effect-machine 0.8.0 → 0.10.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 (76) hide show
  1. package/README.md +76 -16
  2. package/dist/_virtual/_rolldown/runtime.js +6 -11
  3. package/dist/actor.d.ts +58 -72
  4. package/dist/actor.js +166 -32
  5. package/dist/cluster/entity-machine.d.ts +0 -1
  6. package/dist/cluster/entity-machine.js +6 -6
  7. package/dist/cluster/index.js +1 -2
  8. package/dist/cluster/to-entity.js +1 -3
  9. package/dist/errors.d.ts +12 -1
  10. package/dist/errors.js +8 -3
  11. package/dist/index.d.ts +4 -4
  12. package/dist/index.js +2 -3
  13. package/dist/inspection.js +1 -3
  14. package/dist/internal/inspection.js +1 -3
  15. package/dist/internal/transition.d.ts +26 -2
  16. package/dist/internal/transition.js +37 -10
  17. package/dist/internal/utils.d.ts +7 -2
  18. package/dist/internal/utils.js +1 -3
  19. package/dist/machine.d.ts +66 -4
  20. package/dist/machine.js +67 -31
  21. package/dist/persistence/adapter.js +1 -3
  22. package/dist/persistence/adapters/in-memory.js +1 -3
  23. package/dist/persistence/index.js +1 -2
  24. package/dist/persistence/persistent-actor.js +54 -19
  25. package/dist/persistence/persistent-machine.js +1 -3
  26. package/dist/schema.js +1 -3
  27. package/dist/slot.js +1 -3
  28. package/dist/testing.js +58 -6
  29. package/package.json +19 -18
  30. package/v3/dist/_virtual/_rolldown/runtime.js +13 -0
  31. package/{dist-v3 → v3/dist}/actor.d.ts +65 -78
  32. package/{dist-v3 → v3/dist}/actor.js +173 -37
  33. package/{dist-v3 → v3/dist}/cluster/entity-machine.d.ts +1 -2
  34. package/{dist-v3 → v3/dist}/cluster/entity-machine.js +9 -9
  35. package/{dist-v3 → v3/dist}/cluster/index.js +1 -2
  36. package/{dist-v3 → v3/dist}/cluster/to-entity.d.ts +1 -1
  37. package/{dist-v3 → v3/dist}/cluster/to-entity.js +2 -4
  38. package/v3/dist/errors.d.ts +76 -0
  39. package/{dist-v3 → v3/dist}/errors.js +9 -4
  40. package/v3/dist/index.d.ts +13 -0
  41. package/v3/dist/index.js +13 -0
  42. package/{dist-v3 → v3/dist}/inspection.d.ts +53 -8
  43. package/v3/dist/inspection.js +156 -0
  44. package/{dist-v3 → v3/dist}/internal/brands.d.ts +1 -1
  45. package/{dist-v3 → v3/dist}/internal/inspection.d.ts +1 -1
  46. package/v3/dist/internal/inspection.js +20 -0
  47. package/{dist-v3 → v3/dist}/internal/transition.d.ts +35 -11
  48. package/{dist-v3 → v3/dist}/internal/transition.js +47 -17
  49. package/{dist-v3 → v3/dist}/internal/utils.d.ts +9 -4
  50. package/{dist-v3 → v3/dist}/internal/utils.js +2 -4
  51. package/{dist-v3 → v3/dist}/machine.d.ts +86 -10
  52. package/{dist-v3 → v3/dist}/machine.js +130 -33
  53. package/{dist-v3 → v3/dist}/persistence/adapter.d.ts +18 -5
  54. package/{dist-v3 → v3/dist}/persistence/adapter.js +2 -4
  55. package/{dist-v3 → v3/dist}/persistence/adapters/in-memory.d.ts +1 -1
  56. package/{dist-v3 → v3/dist}/persistence/adapters/in-memory.js +2 -4
  57. package/{dist-v3 → v3/dist}/persistence/index.js +1 -2
  58. package/{dist-v3 → v3/dist}/persistence/persistent-actor.d.ts +7 -6
  59. package/{dist-v3 → v3/dist}/persistence/persistent-actor.js +59 -22
  60. package/{dist-v3 → v3/dist}/persistence/persistent-machine.d.ts +1 -1
  61. package/{dist-v3 → v3/dist}/persistence/persistent-machine.js +2 -4
  62. package/{dist-v3 → v3/dist}/schema.d.ts +1 -1
  63. package/{dist-v3 → v3/dist}/schema.js +6 -5
  64. package/{dist-v3 → v3/dist}/slot.d.ts +4 -3
  65. package/{dist-v3 → v3/dist}/slot.js +2 -4
  66. package/{dist-v3 → v3/dist}/testing.d.ts +14 -8
  67. package/{dist-v3 → v3/dist}/testing.js +61 -9
  68. package/dist-v3/_virtual/_rolldown/runtime.js +0 -18
  69. package/dist-v3/errors.d.ts +0 -27
  70. package/dist-v3/index.d.ts +0 -13
  71. package/dist-v3/index.js +0 -14
  72. package/dist-v3/inspection.js +0 -50
  73. package/dist-v3/internal/inspection.js +0 -15
  74. /package/{dist-v3 → v3/dist}/cluster/index.d.ts +0 -0
  75. /package/{dist-v3 → v3/dist}/internal/brands.js +0 -0
  76. /package/{dist-v3 → v3/dist}/persistence/index.d.ts +0 -0
@@ -1,14 +1,13 @@
1
1
  import { Inspector } from "./inspection.js";
2
2
  import { INTERNAL_INIT_EVENT } from "./internal/utils.js";
3
- import { DuplicateActorError } from "./errors.js";
3
+ import { ActorStoppedError, DuplicateActorError, NoReplyError } from "./errors.js";
4
4
  import { isPersistentMachine } from "./persistence/persistent-machine.js";
5
- import { processEventCore, resolveTransition, runSpawnEffects } from "./internal/transition.js";
6
5
  import { emitWithTimestamp } from "./internal/inspection.js";
6
+ import { processEventCore, resolveTransition, runSpawnEffects, shouldPostpone } from "./internal/transition.js";
7
7
  import { PersistenceAdapterTag, PersistenceError } from "./persistence/adapter.js";
8
8
  import { createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
9
9
  import { Cause, Context, Deferred, Effect, Exit, Fiber, Layer, MutableHashMap, Option, PubSub, Queue, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect";
10
-
11
- //#region src-v3/actor.ts
10
+ //#region src/actor.ts
12
11
  /**
13
12
  * Actor system: spawning, lifecycle, and event processing.
14
13
  *
@@ -32,10 +31,50 @@ const notifyListeners = (listeners, state) => {
32
31
  /**
33
32
  * Build core ActorRef methods shared between regular and persistent actors.
34
33
  */
35
- const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listeners, stop, system, childrenMap) => {
34
+ const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listeners, stop, system, childrenMap, pendingReplies) => {
36
35
  const send = Effect.fn("effect-machine.actor.send")(function* (event) {
37
36
  if (yield* Ref.get(stoppedRef)) return;
38
- yield* Queue.offer(eventQueue, event);
37
+ yield* Queue.offer(eventQueue, {
38
+ _tag: "send",
39
+ event
40
+ });
41
+ });
42
+ const call = Effect.fn("effect-machine.actor.call")(function* (event) {
43
+ if (yield* Ref.get(stoppedRef)) {
44
+ const currentState = yield* SubscriptionRef.get(stateRef);
45
+ return {
46
+ newState: currentState,
47
+ previousState: currentState,
48
+ transitioned: false,
49
+ lifecycleRan: false,
50
+ isFinal: machine.finalStates.has(currentState._tag)
51
+ };
52
+ }
53
+ const reply = yield* Deferred.make();
54
+ pendingReplies.add(reply);
55
+ yield* Queue.offer(eventQueue, {
56
+ _tag: "call",
57
+ event,
58
+ reply
59
+ });
60
+ return yield* Deferred.await(reply).pipe(Effect.ensuring(Effect.sync(() => pendingReplies.delete(reply))), Effect.catchTag("ActorStoppedError", () => SubscriptionRef.get(stateRef).pipe(Effect.map((currentState) => ({
61
+ newState: currentState,
62
+ previousState: currentState,
63
+ transitioned: false,
64
+ lifecycleRan: false,
65
+ isFinal: machine.finalStates.has(currentState._tag)
66
+ })))));
67
+ });
68
+ const ask = Effect.fn("effect-machine.actor.ask")(function* (event) {
69
+ if (yield* Ref.get(stoppedRef)) return yield* new ActorStoppedError({ actorId: id });
70
+ const reply = yield* Deferred.make();
71
+ pendingReplies.add(reply);
72
+ yield* Queue.offer(eventQueue, {
73
+ _tag: "ask",
74
+ event,
75
+ reply
76
+ });
77
+ return yield* Deferred.await(reply).pipe(Effect.ensuring(Effect.sync(() => pendingReplies.delete(reply))));
39
78
  });
40
79
  const snapshot = SubscriptionRef.get(stateRef).pipe(Effect.withSpan("effect-machine.actor.snapshot"));
41
80
  const matches = Effect.fn("effect-machine.actor.matches")(function* (tag) {
@@ -73,30 +112,38 @@ const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listen
73
112
  return {
74
113
  id,
75
114
  send,
115
+ cast: send,
116
+ call,
117
+ ask,
76
118
  state: stateRef,
77
119
  stop,
78
- stopSync: () => Effect.runFork(stop),
79
120
  snapshot,
80
- snapshotSync: () => Effect.runSync(SubscriptionRef.get(stateRef)),
81
121
  matches,
82
- matchesSync: (tag) => Effect.runSync(SubscriptionRef.get(stateRef))._tag === tag,
83
122
  can,
84
- canSync: (event) => {
85
- return resolveTransition(machine, Effect.runSync(SubscriptionRef.get(stateRef)), event) !== void 0;
86
- },
87
123
  changes: stateRef.changes,
88
124
  waitFor,
89
125
  awaitFinal,
90
126
  sendAndWait,
91
- sendSync: (event) => {
92
- if (!Effect.runSync(Ref.get(stoppedRef))) Effect.runSync(Queue.offer(eventQueue, event));
93
- },
94
127
  subscribe: (fn) => {
95
128
  listeners.add(fn);
96
129
  return () => {
97
130
  listeners.delete(fn);
98
131
  };
99
132
  },
133
+ sync: {
134
+ send: (event) => {
135
+ if (!Effect.runSync(Ref.get(stoppedRef))) Effect.runSync(Queue.offer(eventQueue, {
136
+ _tag: "send",
137
+ event
138
+ }));
139
+ },
140
+ stop: () => Effect.runFork(stop),
141
+ snapshot: () => Effect.runSync(SubscriptionRef.get(stateRef)),
142
+ matches: (tag) => Effect.runSync(SubscriptionRef.get(stateRef))._tag === tag,
143
+ can: (event) => {
144
+ return resolveTransition(machine, Effect.runSync(SubscriptionRef.get(stateRef)), event) !== void 0;
145
+ }
146
+ },
100
147
  system,
101
148
  children: childrenMap
102
149
  };
@@ -119,11 +166,16 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
119
166
  const eventQueue = yield* Queue.unbounded();
120
167
  const stoppedRef = yield* Ref.make(false);
121
168
  const childrenMap = /* @__PURE__ */ new Map();
169
+ const selfSend = Effect.fn("effect-machine.actor.self.send")(function* (event) {
170
+ if (yield* Ref.get(stoppedRef)) return;
171
+ yield* Queue.offer(eventQueue, {
172
+ _tag: "send",
173
+ event
174
+ });
175
+ });
122
176
  const self = {
123
- send: Effect.fn("effect-machine.actor.self.send")(function* (event) {
124
- if (yield* Ref.get(stoppedRef)) return;
125
- yield* Queue.offer(eventQueue, event);
126
- }),
177
+ send: selfSend,
178
+ cast: selfSend,
127
179
  spawn: (childId, childMachine) => Effect.gen(function* () {
128
180
  const child = yield* system.spawn(childId, childMachine).pipe(Effect.provideService(ActorSystem, system));
129
181
  childrenMap.set(childId, child);
@@ -146,6 +198,7 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
146
198
  const backgroundFibers = [];
147
199
  const initEvent = { _tag: INTERNAL_INIT_EVENT };
148
200
  const ctx = {
201
+ actorId: id,
149
202
  state: machine.initial,
150
203
  event: initEvent,
151
204
  self,
@@ -154,6 +207,7 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
154
207
  const { effects: effectSlots } = machine._slots;
155
208
  for (const bg of machine.backgroundEffects) {
156
209
  const fiber = yield* Effect.forkDaemon(bg.handler({
210
+ actorId: id,
157
211
  state: machine.initial,
158
212
  event: initEvent,
159
213
  self,
@@ -175,9 +229,10 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
175
229
  }));
176
230
  yield* Ref.set(stoppedRef, true);
177
231
  if (implicitSystemScope !== void 0) yield* Scope.close(implicitSystemScope, Exit.void);
178
- return buildActorRefCore(id, machine, stateRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid), system, childrenMap);
232
+ return buildActorRefCore(id, machine, stateRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid), system, childrenMap, /* @__PURE__ */ new Set());
179
233
  }
180
- const loopFiber = yield* Effect.forkDaemon(eventLoop(machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, id, inspectorValue, system));
234
+ const pendingReplies = /* @__PURE__ */ new Set();
235
+ const loopFiber = yield* Effect.forkDaemon(eventLoop(machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, id, inspectorValue, system, pendingReplies));
181
236
  return buildActorRefCore(id, machine, stateRef, eventQueue, stoppedRef, listeners, Effect.gen(function* () {
182
237
  const finalState = yield* SubscriptionRef.get(stateRef);
183
238
  yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
@@ -188,31 +243,104 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
188
243
  }));
189
244
  yield* Ref.set(stoppedRef, true);
190
245
  yield* Fiber.interrupt(loopFiber);
246
+ yield* settlePendingReplies(pendingReplies, id);
191
247
  yield* Scope.close(stateScopeRef.current, Exit.void);
192
248
  yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
193
249
  if (implicitSystemScope !== void 0) yield* Scope.close(implicitSystemScope, Exit.void);
194
- }).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid), system, childrenMap);
250
+ }).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid), system, childrenMap, pendingReplies);
251
+ });
252
+ /** Fail all pending call/ask Deferreds with ActorStoppedError. Safe to call multiple times. */
253
+ const settlePendingReplies = (pendingReplies, actorId) => Effect.sync(() => {
254
+ const error = new ActorStoppedError({ actorId });
255
+ for (const deferred of pendingReplies) Effect.runFork(Deferred.fail(deferred, error));
256
+ pendingReplies.clear();
195
257
  });
196
258
  /**
197
- * Main event loop for the actor
259
+ * Main event loop for the actor.
260
+ * Includes postpone buffer — events matching postpone rules are buffered
261
+ * and drained after state tag changes (gen_statem semantics).
198
262
  */
199
- const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, actorId, inspector, system) {
200
- while (true) {
201
- const event = yield* Queue.take(eventQueue);
263
+ const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, actorId, inspector, system, pendingReplies) {
264
+ const postponed = [];
265
+ const hasPostponeRules = machine.postponeRules.length > 0;
266
+ const processQueued = Effect.fn("effect-machine.actor.processQueued")(function* (queued) {
267
+ const event = queued.event;
202
268
  const currentState = yield* SubscriptionRef.get(stateRef);
203
- if (yield* Effect.withSpan("effect-machine.event.process", { attributes: {
269
+ if (hasPostponeRules && shouldPostpone(machine, currentState._tag, event._tag)) {
270
+ postponed.push(queued);
271
+ if (queued._tag === "call") {
272
+ const postponedResult = {
273
+ newState: currentState,
274
+ previousState: currentState,
275
+ transitioned: false,
276
+ lifecycleRan: false,
277
+ isFinal: false,
278
+ hasReply: false,
279
+ reply: void 0,
280
+ postponed: true
281
+ };
282
+ yield* Deferred.succeed(queued.reply, postponedResult);
283
+ }
284
+ return {
285
+ shouldStop: false,
286
+ stateChanged: false
287
+ };
288
+ }
289
+ const { shouldStop, result } = yield* Effect.withSpan("effect-machine.event.process", { attributes: {
204
290
  "effect_machine.actor.id": actorId,
205
291
  "effect_machine.state.current": currentState._tag,
206
292
  "effect_machine.event.type": event._tag
207
- } })(processEvent(machine, currentState, event, stateRef, self, listeners, stateScopeRef, actorId, inspector, system))) {
293
+ } })(processEvent(machine, currentState, event, stateRef, self, listeners, stateScopeRef, actorId, inspector, system));
294
+ switch (queued._tag) {
295
+ case "call":
296
+ yield* Deferred.succeed(queued.reply, result);
297
+ break;
298
+ case "ask":
299
+ if (result.hasReply) yield* Deferred.succeed(queued.reply, result.reply);
300
+ else yield* Deferred.fail(queued.reply, new NoReplyError({
301
+ actorId,
302
+ eventTag: event._tag
303
+ }));
304
+ break;
305
+ }
306
+ return {
307
+ shouldStop,
308
+ stateChanged: result.lifecycleRan
309
+ };
310
+ });
311
+ while (true) {
312
+ const { shouldStop, stateChanged } = yield* processQueued(yield* Queue.take(eventQueue));
313
+ if (shouldStop) {
208
314
  yield* Ref.set(stoppedRef, true);
315
+ settlePostponedBuffer(postponed, pendingReplies, actorId);
316
+ yield* settlePendingReplies(pendingReplies, actorId);
209
317
  yield* Scope.close(stateScopeRef.current, Exit.void);
210
318
  yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
211
319
  return;
212
320
  }
321
+ if (stateChanged && postponed.length > 0) {
322
+ const drained = postponed.splice(0);
323
+ for (const entry of drained) if ((yield* processQueued(entry)).shouldStop) {
324
+ yield* Ref.set(stoppedRef, true);
325
+ settlePostponedBuffer(postponed, pendingReplies, actorId);
326
+ yield* settlePendingReplies(pendingReplies, actorId);
327
+ yield* Scope.close(stateScopeRef.current, Exit.void);
328
+ yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
329
+ return;
330
+ }
331
+ }
213
332
  }
214
333
  });
215
334
  /**
335
+ * Settle all reply-bearing entries in the postpone buffer on shutdown.
336
+ * Call entries already had their Deferred settled with the postponed result
337
+ * (so their pendingReplies entry is already removed). Ask/send entries
338
+ * with Deferreds are settled via the pendingReplies registry.
339
+ */
340
+ const settlePostponedBuffer = (postponed, _pendingReplies, _actorId) => {
341
+ postponed.length = 0;
342
+ };
343
+ /**
216
344
  * Process a single event, returning true if the actor should stop.
217
345
  * Wraps processEventCore with actor-specific concerns (inspection, listeners, state ref).
218
346
  */
@@ -224,7 +352,7 @@ const processEvent = Effect.fn("effect-machine.actor.processEvent")(function* (m
224
352
  event,
225
353
  timestamp
226
354
  }));
227
- const result = yield* processEventCore(machine, currentState, event, self, stateScopeRef, system, inspector === void 0 ? void 0 : {
355
+ const result = yield* processEventCore(machine, currentState, event, self, stateScopeRef, system, actorId, inspector === void 0 ? void 0 : {
228
356
  onSpawnEffect: (state) => emitWithTimestamp(inspector, (timestamp) => ({
229
357
  type: "@machine.effect",
230
358
  actorId,
@@ -252,7 +380,10 @@ const processEvent = Effect.fn("effect-machine.actor.processEvent")(function* (m
252
380
  });
253
381
  if (!result.transitioned) {
254
382
  yield* Effect.annotateCurrentSpan("effect_machine.transition.matched", false);
255
- return false;
383
+ return {
384
+ shouldStop: false,
385
+ result
386
+ };
256
387
  }
257
388
  yield* Effect.annotateCurrentSpan("effect_machine.transition.matched", true);
258
389
  yield* SubscriptionRef.set(stateRef, result.newState);
@@ -267,10 +398,16 @@ const processEvent = Effect.fn("effect-machine.actor.processEvent")(function* (m
267
398
  finalState: result.newState,
268
399
  timestamp
269
400
  }));
270
- return true;
401
+ return {
402
+ shouldStop: true,
403
+ result
404
+ };
271
405
  }
272
406
  }
273
- return false;
407
+ return {
408
+ shouldStop: false,
409
+ result
410
+ };
274
411
  });
275
412
  /**
276
413
  * Run spawn effects with actor-specific inspection and tracing.
@@ -285,7 +422,7 @@ const runSpawnEffectsWithInspection = Effect.fn("effect-machine.actor.spawnEffec
285
422
  state,
286
423
  timestamp
287
424
  }));
288
- yield* runSpawnEffects(machine, state, event, self, stateScope, system, inspector === void 0 ? void 0 : (info) => emitWithTimestamp(inspector, (timestamp) => ({
425
+ yield* runSpawnEffects(machine, state, event, self, stateScope, system, actorId, inspector === void 0 ? void 0 : (info) => emitWithTimestamp(inspector, (timestamp) => ({
289
426
  type: "@machine.error",
290
427
  actorId,
291
428
  phase: info.phase,
@@ -306,13 +443,13 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
306
443
  const withSpawnGate = (yield* Effect.makeSemaphore(1)).withPermits(1);
307
444
  const eventPubSub = yield* PubSub.unbounded();
308
445
  const eventListeners = /* @__PURE__ */ new Set();
309
- const emitSystemEvent = (event) => Effect.sync(() => notifySystemListeners(eventListeners, event)).pipe(Effect.andThen(PubSub.publish(eventPubSub, event)), Effect.catchAllCause(() => Effect.void), Effect.asVoid);
446
+ const emitSystemEvent = (event) => Effect.sync(() => notifySystemListeners(eventListeners, event)).pipe(Effect.zipRight(PubSub.publish(eventPubSub, event)), Effect.catchAllCause(() => Effect.void), Effect.asVoid);
310
447
  yield* Effect.addFinalizer(() => {
311
448
  const stops = [];
312
449
  MutableHashMap.forEach(actorsMap, (actor) => {
313
450
  stops.push(actor.stop);
314
451
  });
315
- return Effect.all(stops, { concurrency: "unbounded" }).pipe(Effect.andThen(PubSub.shutdown(eventPubSub)), Effect.asVoid);
452
+ return Effect.all(stops, { concurrency: "unbounded" }).pipe(Effect.zipRight(PubSub.shutdown(eventPubSub)), Effect.asVoid);
316
453
  });
317
454
  /** Check for duplicate ID, register actor, attach scope cleanup if available */
318
455
  const registerActor = Effect.fn("effect-machine.actorSystem.register")(function* (id, actor) {
@@ -454,6 +591,5 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
454
591
  * Default ActorSystem layer
455
592
  */
456
593
  const Default = Layer.scoped(ActorSystem, make());
457
-
458
594
  //#endregion
459
- export { ActorSystem, Default, buildActorRefCore, createActor, notifyListeners, processEventCore, resolveTransition, runSpawnEffects };
595
+ export { ActorSystem, Default, buildActorRefCore, createActor, notifyListeners, processEventCore, resolveTransition, runSpawnEffects, settlePendingReplies };
@@ -1,12 +1,11 @@
1
1
  import { EffectsDef, GuardsDef } from "../slot.js";
2
2
  import { ProcessEventHooks } from "../internal/transition.js";
3
3
  import { Machine } from "../machine.js";
4
- import "../actor.js";
5
4
  import { Layer } from "effect";
6
5
  import { Entity } from "@effect/cluster";
7
6
  import { Rpc } from "@effect/rpc";
8
7
 
9
- //#region src-v3/cluster/entity-machine.d.ts
8
+ //#region src/cluster/entity-machine.d.ts
10
9
  /**
11
10
  * Options for EntityMachine.layer
12
11
  */
@@ -2,8 +2,7 @@ import { processEventCore, runSpawnEffects } from "../internal/transition.js";
2
2
  import { ActorSystem } from "../actor.js";
3
3
  import { Effect, Option, Queue, Ref, Scope } from "effect";
4
4
  import { Entity } from "@effect/cluster";
5
-
6
- //#region src-v3/cluster/entity-machine.ts
5
+ //#region src/cluster/entity-machine.ts
7
6
  /**
8
7
  * EntityMachine adapter - wires a machine to a cluster Entity layer.
9
8
  *
@@ -14,7 +13,7 @@ import { Entity } from "@effect/cluster";
14
13
  * Returns the new state after processing.
15
14
  */
16
15
  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);
16
+ const result = yield* processEventCore(machine, yield* Ref.get(stateRef), event, self, stateScopeRef, system, "*", hooks);
18
17
  if (result.transitioned) yield* Ref.set(stateRef, result.newState);
19
18
  return result.newState;
20
19
  });
@@ -55,15 +54,17 @@ const EntityMachine = { layer: (entity, machine, options) => {
55
54
  if (Option.isNone(existingSystem)) return yield* Effect.die("EntityMachine requires ActorSystem in context");
56
55
  const system = existingSystem.value;
57
56
  const internalQueue = yield* Queue.unbounded();
57
+ const clusterSend = Effect.fn("effect-machine.cluster.self.send")(function* (event) {
58
+ yield* Queue.offer(internalQueue, event);
59
+ });
58
60
  const self = {
59
- send: Effect.fn("effect-machine.cluster.self.send")(function* (event) {
60
- yield* Queue.offer(internalQueue, event);
61
- }),
61
+ send: clusterSend,
62
+ cast: clusterSend,
62
63
  spawn: (childId, childMachine) => system.spawn(childId, childMachine).pipe(Effect.provideService(ActorSystem, system))
63
64
  };
64
65
  const stateRef = yield* Ref.make(initialState);
65
66
  const stateScopeRef = { current: yield* Scope.make() };
66
- yield* runSpawnEffects(machine, initialState, { _tag: "$init" }, self, stateScopeRef.current, system, options?.hooks?.onError);
67
+ yield* runSpawnEffects(machine, initialState, { _tag: "$init" }, self, stateScopeRef.current, system, entityId, options?.hooks?.onError);
67
68
  const runInternalEvent = Effect.fn("effect-machine.cluster.internalEvent")(function* () {
68
69
  yield* processEvent(machine, stateRef, yield* Queue.take(internalQueue), self, stateScopeRef, system, options?.hooks);
69
70
  });
@@ -75,6 +76,5 @@ const EntityMachine = { layer: (entity, machine, options) => {
75
76
  });
76
77
  return entity.toLayer(layer());
77
78
  } };
78
-
79
79
  //#endregion
80
- export { EntityMachine };
80
+ export { EntityMachine };
@@ -1,4 +1,3 @@
1
1
  import { EntityMachine } from "./entity-machine.js";
2
2
  import { toEntity } from "./to-entity.js";
3
-
4
- export { EntityMachine, toEntity };
3
+ export { EntityMachine, toEntity };
@@ -2,7 +2,7 @@ import { Machine } from "../machine.js";
2
2
  import { Schema } from "effect";
3
3
  import { Rpc } from "@effect/rpc";
4
4
 
5
- //#region src-v3/cluster/to-entity.d.ts
5
+ //#region src/cluster/to-entity.d.ts
6
6
  /**
7
7
  * Options for toEntity.
8
8
  */
@@ -1,8 +1,7 @@
1
1
  import { MissingSchemaError } from "../errors.js";
2
2
  import { Entity } from "@effect/cluster";
3
3
  import { Rpc } from "@effect/rpc";
4
-
5
- //#region src-v3/cluster/to-entity.ts
4
+ //#region src/cluster/to-entity.ts
6
5
  /**
7
6
  * Generate Entity definition from a machine.
8
7
  *
@@ -48,6 +47,5 @@ const toEntity = (machine, options) => {
48
47
  success: stateSchema
49
48
  }), Rpc.make("GetState", { success: stateSchema })]);
50
49
  };
51
-
52
50
  //#endregion
53
- export { toEntity };
51
+ export { toEntity };
@@ -0,0 +1,76 @@
1
+ import { Schema } from "effect";
2
+
3
+ //#region src/errors.d.ts
4
+ declare const DuplicateActorError_base: Schema.TaggedErrorClass<DuplicateActorError, "DuplicateActorError", {
5
+ readonly _tag: Schema.tag<"DuplicateActorError">;
6
+ } & {
7
+ actorId: typeof Schema.String;
8
+ }>;
9
+ /** Attempted to spawn/restore actor with ID already in use */
10
+ declare class DuplicateActorError extends DuplicateActorError_base {}
11
+ declare const UnprovidedSlotsError_base: Schema.TaggedErrorClass<UnprovidedSlotsError, "UnprovidedSlotsError", {
12
+ readonly _tag: Schema.tag<"UnprovidedSlotsError">;
13
+ } & {
14
+ slots: Schema.Array$<typeof Schema.String>;
15
+ }>;
16
+ /** Machine has unprovided effect slots */
17
+ declare class UnprovidedSlotsError extends UnprovidedSlotsError_base {}
18
+ declare const MissingSchemaError_base: Schema.TaggedErrorClass<MissingSchemaError, "MissingSchemaError", {
19
+ readonly _tag: Schema.tag<"MissingSchemaError">;
20
+ } & {
21
+ operation: typeof Schema.String;
22
+ }>;
23
+ /** Operation requires schemas attached to machine */
24
+ declare class MissingSchemaError extends MissingSchemaError_base {}
25
+ declare const InvalidSchemaError_base: Schema.TaggedErrorClass<InvalidSchemaError, "InvalidSchemaError", {
26
+ readonly _tag: Schema.tag<"InvalidSchemaError">;
27
+ }>;
28
+ /** State/Event schema has no variants */
29
+ declare class InvalidSchemaError extends InvalidSchemaError_base {}
30
+ declare const MissingMatchHandlerError_base: Schema.TaggedErrorClass<MissingMatchHandlerError, "MissingMatchHandlerError", {
31
+ readonly _tag: Schema.tag<"MissingMatchHandlerError">;
32
+ } & {
33
+ tag: typeof Schema.String;
34
+ }>;
35
+ /** $match called with missing handler for tag */
36
+ declare class MissingMatchHandlerError extends MissingMatchHandlerError_base {}
37
+ declare const SlotProvisionError_base: Schema.TaggedErrorClass<SlotProvisionError, "SlotProvisionError", {
38
+ readonly _tag: Schema.tag<"SlotProvisionError">;
39
+ } & {
40
+ slotName: typeof Schema.String;
41
+ slotType: Schema.Literal<["guard", "effect"]>;
42
+ }>;
43
+ /** Slot handler not found at runtime (internal error) */
44
+ declare class SlotProvisionError extends SlotProvisionError_base {}
45
+ declare const ProvisionValidationError_base: Schema.TaggedErrorClass<ProvisionValidationError, "ProvisionValidationError", {
46
+ readonly _tag: Schema.tag<"ProvisionValidationError">;
47
+ } & {
48
+ missing: Schema.Array$<typeof Schema.String>;
49
+ extra: Schema.Array$<typeof Schema.String>;
50
+ }>;
51
+ /** Machine.build() validation failed - missing or extra handlers */
52
+ declare class ProvisionValidationError extends ProvisionValidationError_base {}
53
+ declare const AssertionError_base: Schema.TaggedErrorClass<AssertionError, "AssertionError", {
54
+ readonly _tag: Schema.tag<"AssertionError">;
55
+ } & {
56
+ message: typeof Schema.String;
57
+ }>;
58
+ /** Assertion failed in testing utilities */
59
+ declare class AssertionError extends AssertionError_base {}
60
+ declare const ActorStoppedError_base: Schema.TaggedErrorClass<ActorStoppedError, "ActorStoppedError", {
61
+ readonly _tag: Schema.tag<"ActorStoppedError">;
62
+ } & {
63
+ actorId: typeof Schema.String;
64
+ }>;
65
+ /** Actor was stopped while a call/ask was pending */
66
+ declare class ActorStoppedError extends ActorStoppedError_base {}
67
+ declare const NoReplyError_base: Schema.TaggedErrorClass<NoReplyError, "NoReplyError", {
68
+ readonly _tag: Schema.tag<"NoReplyError">;
69
+ } & {
70
+ actorId: typeof Schema.String;
71
+ eventTag: typeof Schema.String;
72
+ }>;
73
+ /** ask() was used but the transition handler did not call reply */
74
+ declare class NoReplyError extends NoReplyError_base {}
75
+ //#endregion
76
+ export { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError };
@@ -1,6 +1,5 @@
1
1
  import { Schema } from "effect";
2
-
3
- //#region src-v3/errors.ts
2
+ //#region src/errors.ts
4
3
  /**
5
4
  * Typed error classes for effect-machine.
6
5
  *
@@ -33,6 +32,12 @@ var ProvisionValidationError = class extends Schema.TaggedError()("ProvisionVali
33
32
  }) {};
34
33
  /** Assertion failed in testing utilities */
35
34
  var AssertionError = class extends Schema.TaggedError()("AssertionError", { message: Schema.String }) {};
36
-
35
+ /** Actor was stopped while a call/ask was pending */
36
+ var ActorStoppedError = class extends Schema.TaggedError()("ActorStoppedError", { actorId: Schema.String }) {};
37
+ /** ask() was used but the transition handler did not call reply */
38
+ var NoReplyError = class extends Schema.TaggedError()("NoReplyError", {
39
+ actorId: Schema.String,
40
+ eventTag: Schema.String
41
+ }) {};
37
42
  //#endregion
38
- export { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError };
43
+ export { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError };
@@ -0,0 +1,13 @@
1
+ import { EffectHandlers, EffectSlot, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers, GuardSlot, GuardSlots, GuardsDef, GuardsSchema, MachineContext, Slot } from "./slot.js";
2
+ import { Event, MachineEventSchema, MachineStateSchema, State } from "./schema.js";
3
+ import { PersistenceConfig, PersistentMachine, isPersistentMachine } from "./persistence/persistent-machine.js";
4
+ import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
5
+ import { ProcessEventResult } from "./internal/transition.js";
6
+ import { PersistentActorRef, createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
7
+ import { ActorMetadata, PersistedEvent, PersistenceAdapter, PersistenceAdapterTag, PersistenceError, RestoreFailure, RestoreResult, Snapshot, VersionConflictError } from "./persistence/adapter.js";
8
+ import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
9
+ import { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, ProvideHandlers, SpawnEffect, StateHandlerContext, TaskOptions, Transition, machine_d_exports } from "./machine.js";
10
+ import { ActorRef, ActorRefSync, ActorSystem, Default, SystemEvent, SystemEventListener } from "./actor.js";
11
+ import { SimulationResult, TestHarness, TestHarnessOptions, assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
12
+ import { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, InspectionEvent, Inspector, InspectorHandler, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
13
+ export { type ActorMetadata, type ActorRef, type ActorRefSync, ActorStoppedError, type ActorSystem, Default as ActorSystemDefault, ActorSystem as ActorSystemService, type AnyInspectionEvent, AssertionError, type BackgroundEffect, type BuiltMachine, DuplicateActorError, type EffectEvent, type EffectSlots, type EffectsDef, type EffectsSchema, type ErrorEvent, Event, type EventReceivedEvent, type GuardHandlers, type GuardSlot, type GuardSlots, type GuardsDef, type GuardsSchema, type HandlerContext, InMemoryPersistenceAdapter, type InspectionEvent, type Inspector, type InspectorHandler, Inspector as InspectorService, InvalidSchemaError, machine_d_exports as Machine, type MachineContext, type MachineEventSchema, type MachineRef, type MachineStateSchema, type Machine as MachineType, type MakeConfig, MissingMatchHandlerError, MissingSchemaError, NoReplyError, type PersistOptions, type PersistedEvent, type PersistenceAdapter, PersistenceAdapterTag, type PersistenceConfig, PersistenceError, type PersistentActorRef, type PersistentMachine, type ProcessEventResult, type ProvideHandlers, ProvisionValidationError, type RestoreFailure, type RestoreResult, type SimulationResult, Slot, type EffectHandlers as SlotEffectHandlers, type EffectSlot as SlotEffectSlot, SlotProvisionError, type Snapshot, type SpawnEffect, type SpawnEvent, State, type StateHandlerContext, type StopEvent, type SystemEvent, type SystemEventListener, type TaskEvent, type TaskOptions, type TestHarness, type TestHarnessOptions, type TracingInspectorOptions, type Transition, type TransitionEvent, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, makeInspectorEffect, restorePersistentActor, simulate, tracingInspector };
@@ -0,0 +1,13 @@
1
+ import { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
2
+ import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
3
+ import { isPersistentMachine } from "./persistence/persistent-machine.js";
4
+ import { Slot } from "./slot.js";
5
+ import { machine_exports } from "./machine.js";
6
+ import { PersistenceAdapterTag, PersistenceError, VersionConflictError } from "./persistence/adapter.js";
7
+ import { createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
8
+ import { ActorSystem, Default } from "./actor.js";
9
+ import { Event, State } from "./schema.js";
10
+ import { assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
11
+ import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
12
+ import "./persistence/index.js";
13
+ export { ActorStoppedError, Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, InMemoryPersistenceAdapter, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceAdapterTag, PersistenceError, ProvisionValidationError, Slot, SlotProvisionError, State, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, makeInspectorEffect, restorePersistentActor, simulate, tracingInspector };