effect-machine 0.17.1 → 0.18.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 (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -54
  3. package/dist/actor.d.ts +8 -45
  4. package/dist/actor.js +157 -231
  5. package/dist/cluster/adapters/in-memory.d.ts +0 -1
  6. package/dist/cluster/adapters/in-memory.js +10 -5
  7. package/dist/cluster/entity-actor-ref.d.ts +2 -3
  8. package/dist/cluster/entity-actor-ref.js +14 -17
  9. package/dist/cluster/entity-machine.d.ts +2 -8
  10. package/dist/cluster/entity-machine.js +57 -34
  11. package/dist/cluster/index.js +1 -1
  12. package/dist/cluster/persistence.d.ts +0 -1
  13. package/dist/cluster/to-entity.d.ts +19 -21
  14. package/dist/cluster/to-entity.js +18 -19
  15. package/dist/errors.d.ts +11 -37
  16. package/dist/errors.js +12 -30
  17. package/dist/index.d.ts +4 -5
  18. package/dist/index.js +4 -5
  19. package/dist/inspection.d.ts +0 -1
  20. package/dist/inspection.js +24 -13
  21. package/dist/internal/brands.d.ts +0 -1
  22. package/dist/internal/event-advancement.d.ts +50 -0
  23. package/dist/internal/event-advancement.js +79 -0
  24. package/dist/internal/inspection.d.ts +5 -9
  25. package/dist/internal/inspection.js +31 -10
  26. package/dist/internal/machine-definition.d.ts +16 -0
  27. package/dist/internal/runtime.d.ts +1 -161
  28. package/dist/internal/runtime.js +191 -150
  29. package/dist/internal/transition.d.ts +4 -135
  30. package/dist/internal/transition.js +82 -157
  31. package/dist/internal/utils.d.ts +3 -40
  32. package/dist/internal/utils.js +2 -2
  33. package/dist/machine.d.ts +47 -137
  34. package/dist/machine.js +147 -215
  35. package/dist/schema.d.ts +42 -21
  36. package/dist/schema.js +28 -18
  37. package/dist/supervision.d.ts +1 -24
  38. package/dist/supervision.js +2 -3
  39. package/dist/testing.d.ts +15 -29
  40. package/dist/testing.js +76 -95
  41. package/package.json +19 -38
  42. package/dist/slot.d.ts +0 -159
  43. package/dist/slot.js +0 -165
  44. package/v3/dist/_virtual/_rolldown/runtime.js +0 -13
  45. package/v3/dist/actor.d.ts +0 -250
  46. package/v3/dist/actor.js +0 -577
  47. package/v3/dist/cluster/adapters/in-memory.d.ts +0 -15
  48. package/v3/dist/cluster/adapters/in-memory.js +0 -62
  49. package/v3/dist/cluster/entity-actor-ref.d.ts +0 -49
  50. package/v3/dist/cluster/entity-actor-ref.js +0 -19
  51. package/v3/dist/cluster/entity-machine.d.ts +0 -74
  52. package/v3/dist/cluster/entity-machine.js +0 -166
  53. package/v3/dist/cluster/index.d.ts +0 -6
  54. package/v3/dist/cluster/index.js +0 -6
  55. package/v3/dist/cluster/persistence.d.ts +0 -48
  56. package/v3/dist/cluster/persistence.js +0 -14
  57. package/v3/dist/cluster/to-entity.d.ts +0 -69
  58. package/v3/dist/cluster/to-entity.js +0 -59
  59. package/v3/dist/errors.d.ts +0 -95
  60. package/v3/dist/errors.js +0 -54
  61. package/v3/dist/index.d.ts +0 -11
  62. package/v3/dist/index.js +0 -9
  63. package/v3/dist/inspection.d.ts +0 -151
  64. package/v3/dist/inspection.js +0 -128
  65. package/v3/dist/internal/brands.d.ts +0 -50
  66. package/v3/dist/internal/inspection.d.ts +0 -11
  67. package/v3/dist/internal/inspection.js +0 -20
  68. package/v3/dist/internal/runtime.d.ts +0 -161
  69. package/v3/dist/internal/runtime.js +0 -360
  70. package/v3/dist/internal/transition.d.ts +0 -190
  71. package/v3/dist/internal/transition.js +0 -278
  72. package/v3/dist/internal/utils.d.ts +0 -101
  73. package/v3/dist/internal/utils.js +0 -75
  74. package/v3/dist/machine.d.ts +0 -398
  75. package/v3/dist/machine.js +0 -487
  76. package/v3/dist/schema.d.ts +0 -174
  77. package/v3/dist/schema.js +0 -206
  78. package/v3/dist/slot.d.ts +0 -158
  79. package/v3/dist/slot.js +0 -165
  80. package/v3/dist/supervision.d.ts +0 -97
  81. package/v3/dist/supervision.js +0 -42
  82. package/v3/dist/testing.d.ts +0 -151
  83. package/v3/dist/testing.js +0 -189
  84. /package/{v3/dist/internal/brands.js → dist/internal/machine-definition.js} +0 -0
package/dist/actor.js CHANGED
@@ -1,10 +1,9 @@
1
1
  import { ActorStoppedError, DuplicateActorError } from "./errors.js";
2
- import { processEventCore, resolveTransition, runSpawnEffects } from "./internal/transition.js";
3
- import { emitWithTimestamp } from "./internal/inspection.js";
2
+ import { resolveTransition } from "./internal/transition.js";
3
+ import { emitWithTimestamp, makeInspectionHooks } from "./internal/inspection.js";
4
4
  import { Inspector } from "./inspection.js";
5
- import { materializeMachine } from "./machine.js";
6
5
  import { createRuntime } from "./internal/runtime.js";
7
- import { Cause, Context, Deferred, Effect, Exit, Fiber, Layer, MutableHashMap, Option, PubSub, Queue, Ref, Schedule, Scope, Semaphore, Stream, SubscriptionRef } from "effect";
6
+ import { Context, Deferred, Effect, Exit, Fiber, Layer, MutableHashMap, Option, PubSub, Queue, Ref, Schedule, Scope, Semaphore, Stream, SubscriptionRef } from "effect";
8
7
  //#region src/actor.ts
9
8
  /**
10
9
  * Actor system: spawning, lifecycle, and event processing.
@@ -39,43 +38,19 @@ const notifyListeners = (listeners, state) => {
39
38
  /**
40
39
  * Build core ActorRef methods.
41
40
  */
42
- const buildActorRefCore = (id, machine, stateRef, eventQueueRef, stoppedRef, listeners, stop, start, system, childrenMap, pendingReplies, transitionsPubSub, exitDeferred) => {
43
- const send = Effect.fn("effect-machine.actor.send")(function* (event) {
44
- if (yield* Ref.get(stoppedRef)) return;
45
- const q = yield* Ref.get(eventQueueRef);
46
- yield* Queue.offer(q, {
47
- _tag: "send",
48
- event
49
- });
41
+ const buildActorRefCore = (cell, stop, start) => {
42
+ const { id, machine, stateRef, runtimeRef, listeners, system } = cell;
43
+ const send = (event) => Effect.gen(function* () {
44
+ const runtime = runtimeRef.current;
45
+ if (runtime !== void 0) yield* runtime.send(event);
50
46
  });
51
- const call = Effect.fn("effect-machine.actor.call")(function* (event) {
52
- if (yield* Ref.get(stoppedRef)) {
53
- yield* Effect.logWarning("effect-machine.actor.call.stopped").pipe(Effect.annotateLogs({
54
- actorId: id,
55
- eventTag: event._tag
56
- }));
57
- const currentState = yield* SubscriptionRef.get(stateRef);
58
- return {
59
- newState: currentState,
60
- previousState: currentState,
61
- transitioned: false,
62
- hasReply: false,
63
- deferReply: false,
64
- reply: void 0,
65
- postponed: false,
66
- lifecycleRan: false,
67
- isFinal: machine.finalStates.has(currentState._tag)
68
- };
69
- }
70
- const reply = yield* Deferred.make();
71
- pendingReplies.add(reply);
72
- const q = yield* Ref.get(eventQueueRef);
73
- yield* Queue.offer(q, {
74
- _tag: "call",
75
- event,
76
- reply
77
- });
78
- return yield* Deferred.await(reply).pipe(Effect.ensuring(Effect.sync(() => pendingReplies.delete(reply))), Effect.catchTag("ActorStoppedError", () => SubscriptionRef.get(stateRef).pipe(Effect.map((currentState) => ({
47
+ const stoppedCall = (event) => Effect.gen(function* () {
48
+ yield* Effect.logWarning("effect-machine.actor.call.stopped").pipe(Effect.annotateLogs({
49
+ actorId: id,
50
+ eventTag: event._tag
51
+ }));
52
+ const currentState = yield* SubscriptionRef.get(stateRef);
53
+ return {
79
54
  newState: currentState,
80
55
  previousState: currentState,
81
56
  transitioned: false,
@@ -84,30 +59,31 @@ const buildActorRefCore = (id, machine, stateRef, eventQueueRef, stoppedRef, lis
84
59
  reply: void 0,
85
60
  postponed: false,
86
61
  lifecycleRan: false,
87
- isFinal: machine.finalStates.has(currentState._tag)
88
- })))));
62
+ isFinal: machine._isFinal(currentState._tag)
63
+ };
64
+ });
65
+ const call = (event) => Effect.suspend(() => {
66
+ const runtime = runtimeRef.current;
67
+ if (runtime === void 0) return stoppedCall(event);
68
+ return runtime.call(event).pipe(Effect.catch(() => stoppedCall(event)));
89
69
  });
90
70
  const ask = Effect.fn("effect-machine.actor.ask")(function* (event) {
91
- if (yield* Ref.get(stoppedRef)) return yield* new ActorStoppedError({ actorId: id });
92
- const reply = yield* Deferred.make();
93
- pendingReplies.add(reply);
94
- const q = yield* Ref.get(eventQueueRef);
95
- yield* Queue.offer(q, {
96
- _tag: "ask",
97
- event,
98
- reply
99
- });
100
- return yield* Deferred.await(reply).pipe(Effect.ensuring(Effect.sync(() => pendingReplies.delete(reply))));
71
+ const runtime = runtimeRef.current;
72
+ if (runtime === void 0) return yield* ActorStoppedError.make({ actorId: id });
73
+ return yield* runtime.ask(event);
101
74
  });
102
75
  const snapshot = SubscriptionRef.get(stateRef).pipe(Effect.withSpan("effect-machine.actor.snapshot"));
103
76
  const matches = Effect.fn("effect-machine.actor.matches")(function* (tag) {
104
77
  return (yield* SubscriptionRef.get(stateRef))._tag === tag;
105
78
  });
106
79
  const can = Effect.fn("effect-machine.actor.can")(function* (event) {
107
- return resolveTransition(machine, yield* SubscriptionRef.get(stateRef), event) !== void 0;
80
+ const state = yield* SubscriptionRef.get(stateRef);
81
+ return resolveTransition(machine, state, event) !== void 0;
108
82
  });
109
83
  const waitFor = Effect.fn("effect-machine.actor.waitFor")(function* (predicateOrState) {
110
- const predicate = typeof predicateOrState === "function" && !("_tag" in predicateOrState) ? predicateOrState : (s) => s._tag === predicateOrState._tag;
84
+ let predicate;
85
+ if (typeof predicateOrState === "function" && !("_tag" in predicateOrState)) predicate = predicateOrState;
86
+ else predicate = (s) => s._tag === predicateOrState._tag;
111
87
  const current = yield* SubscriptionRef.get(stateRef);
112
88
  if (predicate(current)) return current;
113
89
  const done = yield* Deferred.make();
@@ -124,16 +100,16 @@ const buildActorRefCore = (id, machine, stateRef, eventQueueRef, stoppedRef, lis
124
100
  listeners.delete(listener);
125
101
  return result;
126
102
  });
127
- const awaitFinal = waitFor((state) => machine.finalStates.has(state._tag)).pipe(Effect.withSpan("effect-machine.actor.awaitFinal"));
103
+ const awaitFinal = waitFor((state) => machine._isFinal(state._tag)).pipe(Effect.withSpan("effect-machine.actor.awaitFinal"));
128
104
  const sendAndWait = Effect.fn("effect-machine.actor.sendAndWait")(function* (event, predicateOrState) {
129
105
  yield* send(event);
130
106
  if (predicateOrState !== void 0) return yield* waitFor(predicateOrState);
131
107
  return yield* awaitFinal;
132
108
  });
109
+ const transitions = Stream.fromPubSub(cell.transitions);
133
110
  return {
134
111
  id,
135
112
  send,
136
- cast: send,
137
113
  call,
138
114
  ask,
139
115
  state: stateRef,
@@ -143,7 +119,7 @@ const buildActorRefCore = (id, machine, stateRef, eventQueueRef, stoppedRef, lis
143
119
  matches,
144
120
  can,
145
121
  changes: SubscriptionRef.changes(stateRef),
146
- transitions: transitionsPubSub !== void 0 ? Stream.fromPubSub(transitionsPubSub) : Stream.empty,
122
+ transitions,
147
123
  waitFor,
148
124
  awaitFinal,
149
125
  sendAndWait,
@@ -153,66 +129,24 @@ const buildActorRefCore = (id, machine, stateRef, eventQueueRef, stoppedRef, lis
153
129
  listeners.delete(fn);
154
130
  };
155
131
  },
156
- awaitExit: Deferred.await(exitDeferred),
157
- watch: (other) => other.awaitExit,
158
- drain: Effect.gen(function* () {
159
- if (yield* Ref.get(stoppedRef)) return;
160
- const q = yield* Ref.get(eventQueueRef);
161
- const done = yield* Deferred.make();
162
- yield* Queue.offer(q, {
163
- _tag: "drain",
164
- done
165
- });
166
- yield* Deferred.await(done);
167
- }).pipe(Effect.asVoid),
132
+ awaitExit: Deferred.await(cell.terminalExitDeferred),
133
+ drain: Effect.suspend(() => runtimeRef.current?.drain ?? Effect.void),
168
134
  sync: {
169
135
  send: (event) => {
170
- if (!Effect.runSync(Ref.get(stoppedRef))) {
171
- const q = Effect.runSync(Ref.get(eventQueueRef));
172
- Effect.runSync(Queue.offer(q, {
173
- _tag: "send",
174
- event
175
- }));
176
- }
136
+ runtimeRef.current?.sendSync(event);
177
137
  },
178
138
  stop: () => Effect.runFork(stop),
179
139
  snapshot: () => Effect.runSync(SubscriptionRef.get(stateRef)),
180
140
  matches: (tag) => Effect.runSync(SubscriptionRef.get(stateRef))._tag === tag,
181
141
  can: (event) => {
182
- return resolveTransition(machine, Effect.runSync(SubscriptionRef.get(stateRef)), event) !== void 0;
142
+ const state = Effect.runSync(SubscriptionRef.get(stateRef));
143
+ return resolveTransition(machine, state, event) !== void 0;
183
144
  }
184
145
  },
185
146
  system,
186
- children: childrenMap
147
+ children: cell.children
187
148
  };
188
149
  };
189
- /** Build ProcessEventHooks from an inspector */
190
- const buildInspectionHooks = (actorId, inspector) => ({
191
- onSpawnEffect: (state) => emitWithTimestamp(inspector, (timestamp) => ({
192
- type: "@machine.effect",
193
- actorId,
194
- effectType: "spawn",
195
- state,
196
- timestamp
197
- })),
198
- onTransition: (from, to, ev) => emitWithTimestamp(inspector, (timestamp) => ({
199
- type: "@machine.transition",
200
- actorId,
201
- fromState: from,
202
- toState: to,
203
- event: ev,
204
- timestamp
205
- })),
206
- onError: (info) => emitWithTimestamp(inspector, (timestamp) => ({
207
- type: "@machine.error",
208
- actorId,
209
- phase: info.phase,
210
- state: info.state,
211
- event: info.event,
212
- error: Cause.pretty(info.cause),
213
- timestamp
214
- }))
215
- });
216
150
  /**
217
151
  * Resolve actor system from context, creating an implicit one if none exists.
218
152
  * @internal
@@ -234,50 +168,48 @@ const resolveActorSystem = Effect.fn("effect-machine.resolveActorSystem")(functi
234
168
  * Observes exit deferred, applies restart policy, resets cell resources on restart.
235
169
  * @internal
236
170
  */
237
- const runSupervisionLoop = (params) => Effect.gen(function* () {
238
- const step = yield* Schedule.toStepWithSleep(params.supervision.schedule);
171
+ const runSupervisionLoop = (cell, options) => Effect.gen(function* () {
172
+ const step = yield* Schedule.toStepWithSleep(options.supervision.schedule);
239
173
  while (true) {
240
- const currentRuntime = params.runtimeRef.current;
174
+ const currentRuntime = cell.runtimeRef.current;
241
175
  if (currentRuntime === void 0) return;
242
176
  const generationExit = yield* Deferred.await(currentRuntime.exitDeferred);
243
177
  if (generationExit._tag !== "Defect") {
244
- yield* Deferred.succeed(params.terminalExitDeferred, generationExit);
178
+ yield* Deferred.succeed(cell.terminalExitDeferred, generationExit);
245
179
  return;
246
180
  }
247
- if (params.supervision.shouldRestart !== void 0 && !params.supervision.shouldRestart(generationExit)) {
248
- yield* Deferred.succeed(params.terminalExitDeferred, generationExit);
181
+ if (options.supervision.shouldRestart !== void 0 && !options.supervision.shouldRestart(generationExit)) {
182
+ yield* Deferred.succeed(cell.terminalExitDeferred, generationExit);
249
183
  return;
250
184
  }
251
185
  if ((yield* step(generationExit).pipe(Effect.exit))._tag === "Failure") {
252
- yield* Deferred.succeed(params.terminalExitDeferred, generationExit);
186
+ yield* Deferred.succeed(cell.terminalExitDeferred, generationExit);
253
187
  return;
254
188
  }
255
- const nextGeneration = params.generationRef.get() + 1;
256
- params.generationRef.set(nextGeneration);
257
- let restartState = params.machine.initial;
258
- if (params.lifecycle?.recovery !== void 0) {
259
- const resolved = yield* params.lifecycle.recovery.resolve({
260
- actorId: params.id,
189
+ const nextGeneration = cell.generation.current + 1;
190
+ cell.generation.current = nextGeneration;
191
+ let restartState = cell.machine.initial;
192
+ if (options.lifecycle?.recovery !== void 0) {
193
+ const resolved = yield* options.lifecycle.recovery.resolve({
194
+ actorId: cell.id,
261
195
  generation: nextGeneration,
262
- machineInitial: params.machine.initial
196
+ machineInitial: cell.machine.initial
263
197
  });
264
198
  if (Option.isSome(resolved)) restartState = resolved.value;
265
199
  }
266
- yield* settlePendingReplies(params.pendingReplies, params.id);
200
+ yield* currentRuntime.settlePendingRequests;
267
201
  const freshQueue = yield* Queue.unbounded();
268
- yield* Ref.set(params.eventQueueRef, freshQueue);
269
- yield* SubscriptionRef.set(params.stateRef, restartState);
270
- yield* Ref.set(params.stoppedRef, false);
271
- params.childrenMap.clear();
272
- const machineForRestart = restartState !== params.machine.initial ? Object.create(params.machine, { initial: {
273
- value: restartState,
274
- enumerable: true
275
- } }) : params.machine;
276
- const newRuntime = yield* params.spawnGeneration(machineForRestart);
277
- params.runtimeRef.current = newRuntime;
202
+ yield* Ref.set(cell.eventQueueRef, freshQueue);
203
+ yield* SubscriptionRef.set(cell.stateRef, restartState);
204
+ yield* Ref.set(cell.stoppedRef, false);
205
+ cell.children.clear();
206
+ let machineForRestart = cell.machine;
207
+ if (restartState !== cell.machine.initial) machineForRestart = cell.machine._withInitial(restartState);
208
+ const newRuntime = yield* options.spawnGeneration(machineForRestart);
209
+ cell.runtimeRef.current = newRuntime;
278
210
  yield* newRuntime.start;
279
- if (params.onRestart !== void 0) yield* params.onRestart(nextGeneration, generationExit);
280
- notifyListeners(params.listeners, restartState);
211
+ if (options.onRestart !== void 0) yield* options.onRestart(nextGeneration, generationExit);
212
+ notifyListeners(cell.listeners, restartState);
281
213
  }
282
214
  });
283
215
  /**
@@ -286,72 +218,95 @@ const runSupervisionLoop = (params) => Effect.gen(function* () {
286
218
  */
287
219
  const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machine, options) {
288
220
  const lifecycle = options?.lifecycle;
221
+ const serviceContext = yield* Effect.context();
289
222
  const initial = options?.initialState ?? machine.initial;
290
223
  yield* Effect.annotateCurrentSpan("effect_machine.actor.id", id);
291
224
  yield* Effect.annotateCurrentSpan("effect_machine.actor.initial_state", initial._tag);
292
225
  const { system, implicitSystemScope } = yield* resolveActorSystem();
293
226
  const inspectorValue = Option.getOrUndefined(yield* Effect.serviceOption(Inspector));
294
227
  const childrenMap = /* @__PURE__ */ new Map();
295
- const pendingReplies = /* @__PURE__ */ new Set();
296
228
  const listeners = /* @__PURE__ */ new Set();
297
229
  const transitionsPubSub = yield* PubSub.unbounded();
298
- const hooks = inspectorValue !== void 0 ? buildInspectionHooks(id, inspectorValue) : void 0;
299
- const machineWithState = initial !== machine.initial ? Object.create(machine, { initial: {
300
- value: initial,
301
- enumerable: true
302
- } }) : machine;
230
+ let hooks = void 0;
231
+ if (inspectorValue !== void 0) hooks = makeInspectionHooks(id, inspectorValue);
232
+ let machineWithState = machine;
233
+ if (initial !== machine.initial) machineWithState = machine._withInitial(initial);
303
234
  const stateRef = yield* SubscriptionRef.make(initial);
304
235
  const stoppedRef = yield* Ref.make(false);
305
236
  const initialQueue = yield* Queue.unbounded();
306
237
  const eventQueueRef = yield* Ref.make(initialQueue);
307
238
  const terminalExitDeferred = yield* Deferred.make();
308
239
  let stopEmitted = false;
309
- let generation = 0;
240
+ const generation = { current: 0 };
310
241
  const runtimeRef = { current: void 0 };
311
242
  const supervisorFiberRef = { current: void 0 };
243
+ const cell = {
244
+ id,
245
+ machine,
246
+ stateRef,
247
+ stoppedRef,
248
+ eventQueueRef,
249
+ runtimeRef,
250
+ terminalExitDeferred,
251
+ listeners,
252
+ children: childrenMap,
253
+ transitions: transitionsPubSub,
254
+ system,
255
+ generation
256
+ };
312
257
  /** Build lifecycle hooks for a generation */
313
258
  const buildRuntimeLifecycle = () => {
314
259
  stopEmitted = false;
315
- return {
316
- onEvent: inspectorValue !== void 0 ? (state, event) => emitWithTimestamp(inspectorValue, (timestamp) => ({
317
- type: "@machine.event",
260
+ let onEvent = void 0;
261
+ if (inspectorValue !== void 0) onEvent = (state, event) => emitWithTimestamp(inspectorValue, (timestamp) => ({
262
+ type: "@machine.event",
263
+ actorId: id,
264
+ state,
265
+ event,
266
+ timestamp
267
+ }));
268
+ let onFinal = void 0;
269
+ if (inspectorValue !== void 0) onFinal = (state) => Effect.gen(function* () {
270
+ stopEmitted = true;
271
+ yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
272
+ type: "@machine.stop",
318
273
  actorId: id,
319
- state,
320
- event,
274
+ finalState: state,
321
275
  timestamp
322
- })) : void 0,
323
- onStateChange: (result, event) => Effect.gen(function* () {
276
+ }));
277
+ });
278
+ let onInitialSpawnEffects = void 0;
279
+ if (inspectorValue !== void 0) onInitialSpawnEffects = (state) => emitWithTimestamp(inspectorValue, (timestamp) => ({
280
+ type: "@machine.effect",
281
+ actorId: id,
282
+ effectType: "spawn",
283
+ state,
284
+ timestamp
285
+ }));
286
+ return {
287
+ onEvent,
288
+ onStateChange: (result, event) => {
324
289
  notifyListeners(listeners, result.newState);
325
- if (lifecycle?.durability !== void 0 && result.transitioned) {
326
- const durability = lifecycle.durability;
327
- if (durability.shouldSave === void 0 || durability.shouldSave(result.newState, result.previousState)) yield* durability.save({
328
- actorId: id,
329
- generation,
330
- previousState: result.previousState,
331
- nextState: result.newState,
332
- event
333
- });
334
- }
335
- yield* Effect.annotateCurrentSpan("effect_machine.transition.matched", true);
336
- if (result.lifecycleRan) {
337
- yield* Effect.annotateCurrentSpan("effect_machine.state.from", result.previousState._tag);
338
- yield* Effect.annotateCurrentSpan("effect_machine.state.to", result.newState._tag);
339
- }
340
- }),
341
- onProcessed: (result, event) => result.transitioned ? PubSub.publish(transitionsPubSub, {
342
- fromState: result.previousState,
343
- toState: result.newState,
344
- event
345
- }).pipe(Effect.asVoid) : Effect.void,
346
- onFinal: inspectorValue !== void 0 ? (state) => Effect.gen(function* () {
347
- stopEmitted = true;
348
- yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
349
- type: "@machine.stop",
290
+ const durability = lifecycle?.durability;
291
+ if (durability === void 0 || !result.transitioned) return;
292
+ if (!(durability.shouldSave === void 0 || durability.shouldSave(result.newState, result.previousState))) return;
293
+ return durability.save({
350
294
  actorId: id,
351
- finalState: state,
352
- timestamp
353
- }));
354
- }) : void 0,
295
+ generation: generation.current,
296
+ previousState: result.previousState,
297
+ nextState: result.newState,
298
+ event
299
+ });
300
+ },
301
+ onProcessed: (result, event) => {
302
+ if (!result.transitioned || transitionsPubSub.subscribers.size === 0) return;
303
+ return PubSub.publish(transitionsPubSub, {
304
+ fromState: result.previousState,
305
+ toState: result.newState,
306
+ event
307
+ }).pipe(Effect.asVoid);
308
+ },
309
+ onFinal,
355
310
  onShutdown: () => Effect.gen(function* () {
356
311
  if (!stopEmitted) {
357
312
  const finalState = yield* SubscriptionRef.get(stateRef);
@@ -362,15 +317,8 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
362
317
  timestamp
363
318
  }));
364
319
  }
365
- yield* settlePendingReplies(pendingReplies, id);
366
320
  }),
367
- onInitialSpawnEffects: inspectorValue !== void 0 ? (state) => emitWithTimestamp(inspectorValue, (timestamp) => ({
368
- type: "@machine.effect",
369
- actorId: id,
370
- effectType: "spawn",
371
- state,
372
- timestamp
373
- })) : void 0
321
+ onInitialSpawnEffects
374
322
  };
375
323
  };
376
324
  /** Create a single runtime generation. machineForGen is machineWithState for initial, machine for restarts. */
@@ -384,11 +332,6 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
384
332
  eventQueue: currentQueue
385
333
  },
386
334
  lifecycle: buildRuntimeLifecycle(),
387
- wrapProcess: (state, event, inner) => Effect.withSpan("effect-machine.event.process", { attributes: {
388
- "effect_machine.actor.id": id,
389
- "effect_machine.state.current": state._tag,
390
- "effect_machine.event.type": event._tag
391
- } })(inner.pipe(Effect.tap((r) => Effect.annotateCurrentSpan("effect_machine.transition.matched", r.result.transitioned)))),
392
335
  onChildSpawned: (childId, child) => Effect.gen(function* () {
393
336
  childrenMap.set(childId, child);
394
337
  const maybeScope = yield* Effect.serviceOption(Scope.Scope);
@@ -399,27 +342,26 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
399
342
  })));
400
343
  runtimeRef.current = yield* spawnGeneration(machineWithState);
401
344
  const supervision = options?.supervision;
402
- const stop = Effect.gen(function* () {
345
+ const stop = Effect.fn("effect-machine.actor.stop")(function* () {
403
346
  if (supervisorFiberRef.current !== void 0) yield* Fiber.interrupt(supervisorFiberRef.current);
404
347
  const currentRuntime = runtimeRef.current;
405
348
  if (currentRuntime !== void 0) yield* currentRuntime.stop;
406
349
  yield* Deferred.succeed(terminalExitDeferred, { _tag: "Stopped" });
407
350
  if (implicitSystemScope !== void 0) yield* Scope.close(implicitSystemScope, Exit.void);
408
- }).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid);
351
+ })().pipe(Effect.provide(serviceContext), Effect.asVoid);
409
352
  const isHydrated = options?.initialState !== void 0;
410
- return buildActorRefCore(id, machine, stateRef, eventQueueRef, stoppedRef, listeners, stop, Effect.gen(function* () {
353
+ const start = Effect.fn("effect-machine.actor.start")(function* () {
411
354
  if (lifecycle?.recovery !== void 0 && !isHydrated) {
412
355
  const resolved = yield* lifecycle.recovery.resolve({
413
356
  actorId: id,
414
- generation,
357
+ generation: generation.current,
415
358
  machineInitial: machine.initial
416
359
  });
417
360
  if (Option.isSome(resolved)) {
418
361
  yield* SubscriptionRef.set(stateRef, resolved.value);
419
- runtimeRef.current = yield* spawnGeneration(Object.create(machine, { initial: {
420
- value: resolved.value,
421
- enumerable: true
422
- } }));
362
+ const recoveredMachine = machine._withInitial(resolved.value);
363
+ const newRuntime = yield* spawnGeneration(recoveredMachine);
364
+ runtimeRef.current = newRuntime;
423
365
  }
424
366
  }
425
367
  const currentState = yield* SubscriptionRef.get(stateRef);
@@ -429,26 +371,10 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
429
371
  initialState: currentState,
430
372
  timestamp
431
373
  }));
432
- if (supervision !== void 0) supervisorFiberRef.current = yield* Effect.forkDetach(runSupervisionLoop({
374
+ if (supervision !== void 0) supervisorFiberRef.current = yield* Effect.forkDetach(runSupervisionLoop(cell, {
433
375
  supervision,
434
- machine,
435
- id,
436
- runtimeRef,
437
- terminalExitDeferred,
438
- pendingReplies,
439
- eventQueueRef,
440
- stateRef,
441
- stoppedRef,
442
- childrenMap,
443
- listeners,
444
376
  spawnGeneration,
445
377
  lifecycle,
446
- generationRef: {
447
- get: () => generation,
448
- set: (g) => {
449
- generation = g;
450
- }
451
- },
452
378
  onRestart: options?.onRestart
453
379
  }));
454
380
  else {
@@ -457,13 +383,8 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
457
383
  }
458
384
  const currentRuntime = runtimeRef.current;
459
385
  if (currentRuntime !== void 0) yield* currentRuntime.start;
460
- }).pipe(Effect.withSpan("effect-machine.actor.start"), Effect.asVoid), system, childrenMap, pendingReplies, transitionsPubSub, terminalExitDeferred);
461
- });
462
- /** Fail all pending call/ask Deferreds with ActorStoppedError. Safe to call multiple times. */
463
- const settlePendingReplies = (pendingReplies, actorId) => Effect.sync(() => {
464
- const error = new ActorStoppedError({ actorId });
465
- for (const deferred of pendingReplies) Effect.runFork(Deferred.fail(deferred, error));
466
- pendingReplies.clear();
386
+ })().pipe(Effect.provide(serviceContext), Effect.asVoid);
387
+ return buildActorRefCore(cell, stop, start);
467
388
  });
468
389
  /** Notify all system event listeners (sync). */
469
390
  const notifySystemListeners = (listeners, event) => {
@@ -482,13 +403,13 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
482
403
  MutableHashMap.forEach(actorsMap, (actor) => {
483
404
  stops.push(actor.stop);
484
405
  });
485
- return Effect.all(stops, { concurrency: "unbounded" }).pipe(Effect.andThen(PubSub.shutdown(eventPubSub)), Effect.asVoid);
406
+ return Effect.all(stops).pipe(Effect.andThen(PubSub.shutdown(eventPubSub)), Effect.asVoid);
486
407
  });
487
408
  /** Check for duplicate ID, register actor, attach scope cleanup if available */
488
409
  const registerActor = Effect.fn("effect-machine.actorSystem.register")(function* (id, actor) {
489
410
  if (MutableHashMap.has(actorsMap, id)) {
490
411
  yield* actor.stop;
491
- return yield* new DuplicateActorError({ actorId: id });
412
+ return yield* DuplicateActorError.make({ actorId: id });
492
413
  }
493
414
  const actorRef = actor;
494
415
  MutableHashMap.set(actorsMap, id, actorRef);
@@ -513,19 +434,24 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
513
434
  return actor;
514
435
  });
515
436
  const spawnRegular = Effect.fn("effect-machine.actorSystem.spawnRegular")(function* (id, machine, spawnOptions) {
516
- if (MutableHashMap.has(actorsMap, id)) return yield* new DuplicateActorError({ actorId: id });
517
- const materialized = spawnOptions?.slots !== void 0 ? materializeMachine(machine, spawnOptions.slots) : machine;
437
+ if (MutableHashMap.has(actorsMap, id)) return yield* DuplicateActorError.make({ actorId: id });
518
438
  let actorRef;
519
- const actor = yield* createActor(id, materialized, {
520
- supervision: spawnOptions?.supervision,
521
- lifecycle: spawnOptions?.lifecycle,
522
- onRestart: spawnOptions?.supervision !== void 0 ? (generation, exit) => actorRef !== void 0 ? emitSystemEvent({
439
+ let onRestart = void 0;
440
+ if (spawnOptions?.supervision !== void 0) onRestart = (generation, exit) => {
441
+ const currentActor = actorRef;
442
+ if (currentActor === void 0) return Effect.void;
443
+ return emitSystemEvent({
523
444
  _tag: "ActorRestarted",
524
445
  id,
525
- actor: actorRef,
446
+ actor: currentActor,
526
447
  generation,
527
448
  exit
528
- }) : Effect.void : void 0
449
+ });
450
+ };
451
+ const actor = yield* createActor(id, machine, {
452
+ supervision: spawnOptions?.supervision,
453
+ lifecycle: spawnOptions?.lifecycle,
454
+ onRestart
529
455
  });
530
456
  actorRef = actor;
531
457
  yield* registerActor(id, actor);
@@ -580,4 +506,4 @@ const makeSystem = make;
580
506
  */
581
507
  const Default = Layer.effect(ActorSystem, make());
582
508
  //#endregion
583
- export { ActorScope, ActorSystem, Default, buildActorRefCore, createActor, makeSystem, notifyListeners, processEventCore, resolveTransition, runSpawnEffects, settlePendingReplies };
509
+ export { ActorScope, ActorSystem, Default, createActor, makeSystem };
@@ -1,6 +1,5 @@
1
1
  import { PersistedEvent, PersistenceAdapter, PersistenceAdapterService, Snapshot } from "../persistence.js";
2
2
  import { Effect, Layer, Ref } from "effect";
3
-
4
3
  //#region src/cluster/adapters/in-memory.d.ts
5
4
  interface EntityStore {
6
5
  snapshot: Snapshot<unknown> | undefined;
@@ -41,8 +41,10 @@ const makeInMemoryPersistenceAdapter = Effect.gen(function* () {
41
41
  const storeRef = yield* Ref.make(store);
42
42
  const adapter = {
43
43
  saveSnapshot: (key, snapshot) => Effect.gen(function* () {
44
- const entry = getOrCreate(yield* Ref.get(storeRef), makeKey(key));
45
- if (entry.snapshot !== void 0 && snapshot.version < entry.snapshot.version) return yield* new VersionConflictError({
44
+ const s = yield* Ref.get(storeRef);
45
+ const k = makeKey(key);
46
+ const entry = getOrCreate(s, k);
47
+ if (entry.snapshot !== void 0 && snapshot.version < entry.snapshot.version) return yield* VersionConflictError.make({
46
48
  expected: snapshot.version,
47
49
  actual: entry.snapshot.version
48
50
  });
@@ -53,10 +55,13 @@ const makeInMemoryPersistenceAdapter = Effect.gen(function* () {
53
55
  return Option.fromNullishOr(entry?.snapshot);
54
56
  }),
55
57
  appendEvents: (key, events, expectedVersion) => Effect.gen(function* () {
56
- const entry = getOrCreate(yield* Ref.get(storeRef), makeKey(key));
58
+ const s = yield* Ref.get(storeRef);
59
+ const k = makeKey(key);
60
+ const entry = getOrCreate(s, k);
57
61
  const lastEvent = entry.events[entry.events.length - 1];
58
- const currentVersion = lastEvent !== void 0 ? lastEvent.version : 0;
59
- if (currentVersion !== expectedVersion) return yield* new VersionConflictError({
62
+ let currentVersion = 0;
63
+ if (lastEvent !== void 0) currentVersion = lastEvent.version;
64
+ if (currentVersion !== expectedVersion) return yield* VersionConflictError.make({
60
65
  expected: expectedVersion,
61
66
  actual: currentVersion
62
67
  });
@@ -1,9 +1,8 @@
1
1
  import { ExtractReply, ReplyTypeBrand } from "../internal/brands.js";
2
2
  import { ActorStoppedError, NoReplyError } from "../errors.js";
3
3
  import { EntityRpcs } from "./to-entity.js";
4
- import { Effect, Stream } from "effect";
4
+ import { Effect, Schema, Stream } from "effect";
5
5
  import { RpcClient } from "effect/unstable/rpc";
6
-
7
6
  //#region src/cluster/entity-actor-ref.d.ts
8
7
  /**
9
8
  * Typed client wrapper for remote entity machines.
@@ -51,6 +50,6 @@ declare const makeEntityActorRef: <State extends {
51
50
  readonly _tag: string;
52
51
  }, Event extends {
53
52
  readonly _tag: string;
54
- }, Rpcs extends EntityRpcs<any, any>[number]>(client: RpcClient.RpcClient<Rpcs>, entityId: string) => EntityActorRef<State, Event>;
53
+ }>(client: RpcClient.RpcClient<EntityRpcs<Schema.Codec<State, unknown>, Schema.Codec<Event, unknown>>>, entityId: string) => EntityActorRef<State, Event>;
55
54
  //#endregion
56
55
  export { EntityActorRef, makeEntityActorRef };