effect-machine 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -0
- package/dist/actor.d.ts +38 -2
- package/dist/actor.js +79 -22
- package/dist/index.d.ts +2 -2
- package/dist/internal/utils.js +6 -1
- package/dist/persistence/persistent-actor.js +14 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -206,6 +206,30 @@ const child = yield * parent.system.get("worker-1"); // Option<ActorRef>
|
|
|
206
206
|
|
|
207
207
|
Every actor always has a system — `Machine.spawn` creates an implicit one if no `ActorSystem` is in context.
|
|
208
208
|
|
|
209
|
+
### System Observation
|
|
210
|
+
|
|
211
|
+
React to actors joining and leaving the system:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
const system = yield * ActorSystemService;
|
|
215
|
+
|
|
216
|
+
// Sync callback — like ActorRef.subscribe
|
|
217
|
+
const unsub = system.subscribe((event) => {
|
|
218
|
+
// event._tag: "ActorSpawned" | "ActorStopped"
|
|
219
|
+
console.log(`${event._tag}: ${event.id}`);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Sync snapshot of all registered actors
|
|
223
|
+
const actors = system.actors; // ReadonlyMap<string, ActorRef>
|
|
224
|
+
|
|
225
|
+
// Async stream (each subscriber gets own queue)
|
|
226
|
+
yield *
|
|
227
|
+
system.events.pipe(
|
|
228
|
+
Stream.tap((e) => Effect.log(e._tag, e.id)),
|
|
229
|
+
Stream.runDrain,
|
|
230
|
+
);
|
|
231
|
+
```
|
|
232
|
+
|
|
209
233
|
### Testing
|
|
210
234
|
|
|
211
235
|
Test transitions without actors:
|
|
@@ -297,6 +321,18 @@ See the [primer](./primer/) for comprehensive documentation:
|
|
|
297
321
|
| `actor.sendAndWait(ev, State.X)` | Send + wait for state |
|
|
298
322
|
| `actor.subscribe(fn)` | Sync callback |
|
|
299
323
|
| `actor.system` | Access the actor's `ActorSystem` |
|
|
324
|
+
| `actor.children` | Child actors (`ReadonlyMap`) |
|
|
325
|
+
|
|
326
|
+
### ActorSystem
|
|
327
|
+
|
|
328
|
+
| Method / Property | Description |
|
|
329
|
+
| ---------------------- | ------------------------------------------- |
|
|
330
|
+
| `system.spawn(id, m)` | Spawn actor |
|
|
331
|
+
| `system.get(id)` | Get actor by ID |
|
|
332
|
+
| `system.stop(id)` | Stop actor by ID |
|
|
333
|
+
| `system.actors` | Sync snapshot of all actors (`ReadonlyMap`) |
|
|
334
|
+
| `system.subscribe(fn)` | Sync callback for spawn/stop events |
|
|
335
|
+
| `system.events` | Async `Stream<SystemEvent>` for spawn/stop |
|
|
300
336
|
|
|
301
337
|
## License
|
|
302
338
|
|
package/dist/actor.d.ts
CHANGED
|
@@ -106,11 +106,32 @@ interface ActorRef<State extends {
|
|
|
106
106
|
* Every actor always has a system — either inherited from context or implicitly created.
|
|
107
107
|
*/
|
|
108
108
|
readonly system: ActorSystem;
|
|
109
|
+
/**
|
|
110
|
+
* Child actors spawned via `self.spawn` in this actor's handlers.
|
|
111
|
+
* State-scoped children are auto-removed on state exit.
|
|
112
|
+
*/
|
|
113
|
+
readonly children: ReadonlyMap<string, ActorRef<AnyState, unknown>>;
|
|
109
114
|
}
|
|
110
115
|
/** Base type for stored actors (internal) */
|
|
111
116
|
type AnyState = {
|
|
112
117
|
readonly _tag: string;
|
|
113
118
|
};
|
|
119
|
+
/**
|
|
120
|
+
* Events emitted by the ActorSystem when actors are spawned or stopped.
|
|
121
|
+
*/
|
|
122
|
+
type SystemEvent = {
|
|
123
|
+
readonly _tag: "ActorSpawned";
|
|
124
|
+
readonly id: string;
|
|
125
|
+
readonly actor: ActorRef<AnyState, unknown>;
|
|
126
|
+
} | {
|
|
127
|
+
readonly _tag: "ActorStopped";
|
|
128
|
+
readonly id: string;
|
|
129
|
+
readonly actor: ActorRef<AnyState, unknown>;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Listener callback for system events.
|
|
133
|
+
*/
|
|
134
|
+
type SystemEventListener = (event: SystemEvent) => void;
|
|
114
135
|
/**
|
|
115
136
|
* Actor system for managing actor lifecycles
|
|
116
137
|
*/
|
|
@@ -174,6 +195,21 @@ interface ActorSystem {
|
|
|
174
195
|
* Stop an actor by ID
|
|
175
196
|
*/
|
|
176
197
|
readonly stop: (id: string) => Effect.Effect<boolean>;
|
|
198
|
+
/**
|
|
199
|
+
* Async stream of system events (actor spawned/stopped).
|
|
200
|
+
* Each subscriber gets their own queue — late subscribers miss prior events.
|
|
201
|
+
*/
|
|
202
|
+
readonly events: Stream.Stream<SystemEvent>;
|
|
203
|
+
/**
|
|
204
|
+
* Sync snapshot of all currently registered actors.
|
|
205
|
+
* Returns a new Map on each access (not live).
|
|
206
|
+
*/
|
|
207
|
+
readonly actors: ReadonlyMap<string, ActorRef<AnyState, unknown>>;
|
|
208
|
+
/**
|
|
209
|
+
* Subscribe to system events synchronously.
|
|
210
|
+
* Returns an unsubscribe function.
|
|
211
|
+
*/
|
|
212
|
+
readonly subscribe: (fn: SystemEventListener) => () => void;
|
|
177
213
|
/**
|
|
178
214
|
* List all persisted actor metadata.
|
|
179
215
|
* Returns empty array if adapter doesn't support registry.
|
|
@@ -239,7 +275,7 @@ declare const buildActorRefCore: <S extends {
|
|
|
239
275
|
readonly _tag: string;
|
|
240
276
|
}, E extends {
|
|
241
277
|
readonly _tag: string;
|
|
242
|
-
}, R, GD extends GuardsDef, EFD extends EffectsDef>(id: string, machine: Machine<S, E, R, any, any, GD, EFD>, stateRef: SubscriptionRef.SubscriptionRef<S>, eventQueue: Queue.Queue<E>, stoppedRef: Ref.Ref<boolean>, listeners: Listeners<S>, stop: Effect.Effect<void>, system: ActorSystem) => ActorRef<S, E>;
|
|
278
|
+
}, R, GD extends GuardsDef, EFD extends EffectsDef>(id: string, machine: Machine<S, E, R, any, any, GD, EFD>, stateRef: SubscriptionRef.SubscriptionRef<S>, eventQueue: Queue.Queue<E>, stoppedRef: Ref.Ref<boolean>, listeners: Listeners<S>, stop: Effect.Effect<void>, system: ActorSystem, childrenMap: ReadonlyMap<string, ActorRef<AnyState, unknown>>) => ActorRef<S, E>;
|
|
243
279
|
/**
|
|
244
280
|
* Create and start an actor for a machine
|
|
245
281
|
*/
|
|
@@ -253,4 +289,4 @@ declare const createActor: <S extends {
|
|
|
253
289
|
*/
|
|
254
290
|
declare const Default: Layer.Layer<ActorSystem, never, never>;
|
|
255
291
|
//#endregion
|
|
256
|
-
export { ActorRef, ActorSystem, Default, Listeners, type ProcessEventError, type ProcessEventHooks, type ProcessEventResult, buildActorRefCore, createActor, notifyListeners, processEventCore, resolveTransition, runSpawnEffects };
|
|
292
|
+
export { ActorRef, ActorSystem, Default, Listeners, type ProcessEventError, type ProcessEventHooks, type ProcessEventResult, SystemEvent, SystemEventListener, buildActorRefCore, createActor, notifyListeners, processEventCore, resolveTransition, runSpawnEffects };
|
package/dist/actor.js
CHANGED
|
@@ -6,10 +6,18 @@ import { processEventCore, resolveTransition, runSpawnEffects } from "./internal
|
|
|
6
6
|
import { emitWithTimestamp } from "./internal/inspection.js";
|
|
7
7
|
import { PersistenceAdapterTag, PersistenceError } from "./persistence/adapter.js";
|
|
8
8
|
import { createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
|
|
9
|
-
import { Cause, Context, Deferred, Effect, Exit, Fiber, Layer, MutableHashMap, Option, Queue, Ref, Runtime, Scope, SubscriptionRef } from "effect";
|
|
9
|
+
import { Cause, Context, Deferred, Effect, Exit, Fiber, Layer, MutableHashMap, Option, PubSub, Queue, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect";
|
|
10
10
|
|
|
11
11
|
//#region src/actor.ts
|
|
12
12
|
/**
|
|
13
|
+
* Actor system: spawning, lifecycle, and event processing.
|
|
14
|
+
*
|
|
15
|
+
* Combines:
|
|
16
|
+
* - ActorRef interface (running actor handle)
|
|
17
|
+
* - ActorSystem service (spawn/stop/get actors)
|
|
18
|
+
* - Actor creation and event loop
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
13
21
|
* ActorSystem service tag
|
|
14
22
|
*/
|
|
15
23
|
const ActorSystem = Context.GenericTag("@effect/machine/ActorSystem");
|
|
@@ -24,7 +32,7 @@ const notifyListeners = (listeners, state) => {
|
|
|
24
32
|
/**
|
|
25
33
|
* Build core ActorRef methods shared between regular and persistent actors.
|
|
26
34
|
*/
|
|
27
|
-
const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listeners, stop, system) => {
|
|
35
|
+
const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listeners, stop, system, childrenMap) => {
|
|
28
36
|
const send = Effect.fn("effect-machine.actor.send")(function* (event) {
|
|
29
37
|
if (yield* Ref.get(stoppedRef)) return;
|
|
30
38
|
yield* Queue.offer(eventQueue, event);
|
|
@@ -89,7 +97,8 @@ const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listen
|
|
|
89
97
|
listeners.delete(fn);
|
|
90
98
|
};
|
|
91
99
|
},
|
|
92
|
-
system
|
|
100
|
+
system,
|
|
101
|
+
children: childrenMap
|
|
93
102
|
};
|
|
94
103
|
};
|
|
95
104
|
/**
|
|
@@ -109,12 +118,21 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
|
|
|
109
118
|
const inspectorValue = Option.getOrUndefined(yield* Effect.serviceOption(Inspector));
|
|
110
119
|
const eventQueue = yield* Queue.unbounded();
|
|
111
120
|
const stoppedRef = yield* Ref.make(false);
|
|
121
|
+
const childrenMap = /* @__PURE__ */ new Map();
|
|
112
122
|
const self = {
|
|
113
123
|
send: Effect.fn("effect-machine.actor.self.send")(function* (event) {
|
|
114
124
|
if (yield* Ref.get(stoppedRef)) return;
|
|
115
125
|
yield* Queue.offer(eventQueue, event);
|
|
116
126
|
}),
|
|
117
|
-
spawn: (childId, childMachine) =>
|
|
127
|
+
spawn: (childId, childMachine) => Effect.gen(function* () {
|
|
128
|
+
const child = yield* system.spawn(childId, childMachine).pipe(Effect.provideService(ActorSystem, system));
|
|
129
|
+
childrenMap.set(childId, child);
|
|
130
|
+
const maybeScope = yield* Effect.serviceOption(Scope.Scope);
|
|
131
|
+
if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, Effect.sync(() => {
|
|
132
|
+
childrenMap.delete(childId);
|
|
133
|
+
}));
|
|
134
|
+
return child;
|
|
135
|
+
})
|
|
118
136
|
};
|
|
119
137
|
yield* Effect.annotateCurrentSpan("effect_machine.actor.initial_state", machine.initial._tag);
|
|
120
138
|
yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
|
|
@@ -157,7 +175,7 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
|
|
|
157
175
|
}));
|
|
158
176
|
yield* Ref.set(stoppedRef, true);
|
|
159
177
|
if (implicitSystemScope !== void 0) yield* Scope.close(implicitSystemScope, Exit.void);
|
|
160
|
-
return buildActorRefCore(id, machine, stateRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid), system);
|
|
178
|
+
return buildActorRefCore(id, machine, stateRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid), system, childrenMap);
|
|
161
179
|
}
|
|
162
180
|
const loopFiber = yield* Effect.forkDaemon(eventLoop(machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, id, inspectorValue, system));
|
|
163
181
|
return buildActorRefCore(id, machine, stateRef, eventQueue, stoppedRef, listeners, Effect.gen(function* () {
|
|
@@ -173,7 +191,7 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
|
|
|
173
191
|
yield* Scope.close(stateScopeRef.current, Exit.void);
|
|
174
192
|
yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
|
|
175
193
|
if (implicitSystemScope !== void 0) yield* Scope.close(implicitSystemScope, Exit.void);
|
|
176
|
-
}).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid), system);
|
|
194
|
+
}).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid), system, childrenMap);
|
|
177
195
|
});
|
|
178
196
|
/**
|
|
179
197
|
* Main event loop for the actor
|
|
@@ -277,39 +295,58 @@ const runSpawnEffectsWithInspection = Effect.fn("effect-machine.actor.spawnEffec
|
|
|
277
295
|
timestamp
|
|
278
296
|
})));
|
|
279
297
|
});
|
|
280
|
-
/**
|
|
281
|
-
|
|
282
|
-
|
|
298
|
+
/** Notify all system event listeners (sync). */
|
|
299
|
+
const notifySystemListeners = (listeners, event) => {
|
|
300
|
+
for (const listener of listeners) try {
|
|
301
|
+
listener(event);
|
|
302
|
+
} catch {}
|
|
303
|
+
};
|
|
283
304
|
const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
|
|
284
|
-
const
|
|
305
|
+
const actorsMap = MutableHashMap.empty();
|
|
285
306
|
const withSpawnGate = (yield* Effect.makeSemaphore(1)).withPermits(1);
|
|
307
|
+
const eventPubSub = yield* PubSub.unbounded();
|
|
308
|
+
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);
|
|
286
310
|
yield* Effect.addFinalizer(() => {
|
|
287
311
|
const stops = [];
|
|
288
|
-
MutableHashMap.forEach(
|
|
312
|
+
MutableHashMap.forEach(actorsMap, (actor) => {
|
|
289
313
|
stops.push(actor.stop);
|
|
290
314
|
});
|
|
291
|
-
return Effect.all(stops, { concurrency: "unbounded" }).pipe(Effect.asVoid);
|
|
315
|
+
return Effect.all(stops, { concurrency: "unbounded" }).pipe(Effect.andThen(PubSub.shutdown(eventPubSub)), Effect.asVoid);
|
|
292
316
|
});
|
|
293
317
|
/** Check for duplicate ID, register actor, attach scope cleanup if available */
|
|
294
318
|
const registerActor = Effect.fn("effect-machine.actorSystem.register")(function* (id, actor) {
|
|
295
|
-
if (MutableHashMap.has(
|
|
319
|
+
if (MutableHashMap.has(actorsMap, id)) {
|
|
296
320
|
yield* actor.stop;
|
|
297
321
|
return yield* new DuplicateActorError({ actorId: id });
|
|
298
322
|
}
|
|
299
|
-
|
|
323
|
+
const actorRef = actor;
|
|
324
|
+
MutableHashMap.set(actorsMap, id, actorRef);
|
|
325
|
+
yield* emitSystemEvent({
|
|
326
|
+
_tag: "ActorSpawned",
|
|
327
|
+
id,
|
|
328
|
+
actor: actorRef
|
|
329
|
+
});
|
|
300
330
|
const maybeScope = yield* Effect.serviceOption(Scope.Scope);
|
|
301
331
|
if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, Effect.gen(function* () {
|
|
332
|
+
if (MutableHashMap.has(actorsMap, id)) {
|
|
333
|
+
yield* emitSystemEvent({
|
|
334
|
+
_tag: "ActorStopped",
|
|
335
|
+
id,
|
|
336
|
+
actor: actorRef
|
|
337
|
+
});
|
|
338
|
+
MutableHashMap.remove(actorsMap, id);
|
|
339
|
+
}
|
|
302
340
|
yield* actor.stop;
|
|
303
|
-
MutableHashMap.remove(actors, id);
|
|
304
341
|
}));
|
|
305
342
|
return actor;
|
|
306
343
|
});
|
|
307
344
|
const spawnRegular = Effect.fn("effect-machine.actorSystem.spawnRegular")(function* (id, built) {
|
|
308
|
-
if (MutableHashMap.has(
|
|
345
|
+
if (MutableHashMap.has(actorsMap, id)) return yield* new DuplicateActorError({ actorId: id });
|
|
309
346
|
return yield* registerActor(id, yield* createActor(id, built._inner));
|
|
310
347
|
});
|
|
311
348
|
const spawnPersistent = Effect.fn("effect-machine.actorSystem.spawnPersistent")(function* (id, persistentMachine) {
|
|
312
|
-
if (MutableHashMap.has(
|
|
349
|
+
if (MutableHashMap.has(actorsMap, id)) return yield* new DuplicateActorError({ actorId: id });
|
|
313
350
|
const adapter = yield* PersistenceAdapterTag;
|
|
314
351
|
const maybeSnapshot = yield* adapter.loadSnapshot(id, persistentMachine.persistence.stateSchema);
|
|
315
352
|
return yield* registerActor(id, yield* createPersistentActor(id, persistentMachine, maybeSnapshot, yield* adapter.loadEvents(id, persistentMachine.persistence.eventSchema, Option.isSome(maybeSnapshot) ? maybeSnapshot.value.version : void 0)));
|
|
@@ -328,13 +365,19 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
|
|
|
328
365
|
});
|
|
329
366
|
const restore = (id, persistentMachine) => withSpawnGate(restoreImpl(id, persistentMachine));
|
|
330
367
|
const get = Effect.fn("effect-machine.actorSystem.get")(function* (id) {
|
|
331
|
-
return yield* Effect.sync(() => MutableHashMap.get(
|
|
368
|
+
return yield* Effect.sync(() => MutableHashMap.get(actorsMap, id));
|
|
332
369
|
});
|
|
333
370
|
const stop = Effect.fn("effect-machine.actorSystem.stop")(function* (id) {
|
|
334
|
-
const maybeActor = MutableHashMap.get(
|
|
371
|
+
const maybeActor = MutableHashMap.get(actorsMap, id);
|
|
335
372
|
if (Option.isNone(maybeActor)) return false;
|
|
336
|
-
|
|
337
|
-
MutableHashMap.remove(
|
|
373
|
+
const actor = maybeActor.value;
|
|
374
|
+
MutableHashMap.remove(actorsMap, id);
|
|
375
|
+
yield* emitSystemEvent({
|
|
376
|
+
_tag: "ActorStopped",
|
|
377
|
+
id,
|
|
378
|
+
actor
|
|
379
|
+
});
|
|
380
|
+
yield* actor.stop;
|
|
338
381
|
return true;
|
|
339
382
|
});
|
|
340
383
|
const listPersisted = Effect.fn("effect-machine.actorSystem.listPersisted")(function* () {
|
|
@@ -346,7 +389,7 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
|
|
|
346
389
|
const restored = [];
|
|
347
390
|
const failed = [];
|
|
348
391
|
for (const id of ids) {
|
|
349
|
-
if (MutableHashMap.has(
|
|
392
|
+
if (MutableHashMap.has(actorsMap, id)) continue;
|
|
350
393
|
const result = yield* Effect.either(restore(id, persistentMachine));
|
|
351
394
|
if (result._tag === "Left") failed.push({
|
|
352
395
|
id,
|
|
@@ -388,6 +431,20 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
|
|
|
388
431
|
restore,
|
|
389
432
|
get,
|
|
390
433
|
stop,
|
|
434
|
+
events: Stream.fromPubSub(eventPubSub),
|
|
435
|
+
get actors() {
|
|
436
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
437
|
+
MutableHashMap.forEach(actorsMap, (actor, id) => {
|
|
438
|
+
snapshot.set(id, actor);
|
|
439
|
+
});
|
|
440
|
+
return snapshot;
|
|
441
|
+
},
|
|
442
|
+
subscribe: (fn) => {
|
|
443
|
+
eventListeners.add(fn);
|
|
444
|
+
return () => {
|
|
445
|
+
eventListeners.delete(fn);
|
|
446
|
+
};
|
|
447
|
+
},
|
|
391
448
|
listPersisted,
|
|
392
449
|
restoreMany,
|
|
393
450
|
restoreAll
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { ActorMetadata, PersistedEvent, PersistenceAdapter, PersistenceAdapterTa
|
|
|
7
7
|
import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
|
|
8
8
|
import "./persistence/index.js";
|
|
9
9
|
import { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, ProvideHandlers, SpawnEffect, StateHandlerContext, Transition, machine_d_exports } from "./machine.js";
|
|
10
|
-
import { ActorRef, ActorSystem, Default } from "./actor.js";
|
|
10
|
+
import { ActorRef, ActorSystem, Default, SystemEvent, SystemEventListener } from "./actor.js";
|
|
11
11
|
import { SimulationResult, TestHarness, TestHarnessOptions, assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
|
|
12
12
|
import { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, InspectionEvent, Inspector, SpawnEvent, StopEvent, TransitionEvent, collectingInspector, consoleInspector, makeInspector } from "./inspection.js";
|
|
13
|
-
export { type ActorMetadata, type ActorRef, 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, 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, type PersistOptions, type PersistedEvent, type PersistenceAdapter, PersistenceAdapterTag, type PersistenceConfig, PersistenceError, type PersistentActorRef, type PersistentMachine, 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 TestHarness, type TestHarnessOptions, type Transition, type TransitionEvent, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, restorePersistentActor, simulate };
|
|
13
|
+
export { type ActorMetadata, type ActorRef, 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, 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, type PersistOptions, type PersistedEvent, type PersistenceAdapter, PersistenceAdapterTag, type PersistenceConfig, PersistenceError, type PersistentActorRef, type PersistentMachine, 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 TestHarness, type TestHarnessOptions, type Transition, type TransitionEvent, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, restorePersistentActor, simulate };
|
package/dist/internal/utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
1
|
+
import { Effect, Stream } from "effect";
|
|
2
2
|
|
|
3
3
|
//#region src/internal/utils.ts
|
|
4
4
|
/**
|
|
@@ -37,6 +37,11 @@ const stubSystem = {
|
|
|
37
37
|
restore: () => Effect.die("restore not supported in stub system"),
|
|
38
38
|
get: () => Effect.die("get not supported in stub system"),
|
|
39
39
|
stop: () => Effect.die("stop not supported in stub system"),
|
|
40
|
+
events: Stream.empty,
|
|
41
|
+
get actors() {
|
|
42
|
+
return /* @__PURE__ */ new Map();
|
|
43
|
+
},
|
|
44
|
+
subscribe: () => () => {},
|
|
40
45
|
listPersisted: () => Effect.die("listPersisted not supported in stub system"),
|
|
41
46
|
restoreMany: () => Effect.die("restoreMany not supported in stub system"),
|
|
42
47
|
restoreAll: () => Effect.die("restoreAll not supported in stub system")
|
|
@@ -31,7 +31,7 @@ const replayEvents = Effect.fn("effect-machine.persistentActor.replayEvents")(fu
|
|
|
31
31
|
/**
|
|
32
32
|
* Build PersistentActorRef with all methods
|
|
33
33
|
*/
|
|
34
|
-
const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, stop, adapter, system) => {
|
|
34
|
+
const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, stop, adapter, system, childrenMap) => {
|
|
35
35
|
const { machine, persistence } = persistentMachine;
|
|
36
36
|
const typedMachine = machine;
|
|
37
37
|
const persist = Effect.gen(function* () {
|
|
@@ -71,7 +71,7 @@ const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, ev
|
|
|
71
71
|
}
|
|
72
72
|
});
|
|
73
73
|
return {
|
|
74
|
-
...buildActorRefCore(id, typedMachine, stateRef, eventQueue, stoppedRef, listeners, stop, system),
|
|
74
|
+
...buildActorRefCore(id, typedMachine, stateRef, eventQueue, stoppedRef, listeners, stop, system, childrenMap),
|
|
75
75
|
persist,
|
|
76
76
|
version,
|
|
77
77
|
replayTo
|
|
@@ -92,12 +92,21 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
|
|
|
92
92
|
const inspector = Option.getOrUndefined(yield* Effect.serviceOption(Inspector));
|
|
93
93
|
const eventQueue = yield* Queue.unbounded();
|
|
94
94
|
const stoppedRef = yield* Ref.make(false);
|
|
95
|
+
const childrenMap = /* @__PURE__ */ new Map();
|
|
95
96
|
const self = {
|
|
96
97
|
send: Effect.fn("effect-machine.persistentActor.self.send")(function* (event) {
|
|
97
98
|
if (yield* Ref.get(stoppedRef)) return;
|
|
98
99
|
yield* Queue.offer(eventQueue, event);
|
|
99
100
|
}),
|
|
100
|
-
spawn: (childId, childMachine) =>
|
|
101
|
+
spawn: (childId, childMachine) => Effect.gen(function* () {
|
|
102
|
+
const child = yield* system.spawn(childId, childMachine).pipe(Effect.provideService(ActorSystem, system));
|
|
103
|
+
childrenMap.set(childId, child);
|
|
104
|
+
const maybeScope = yield* Effect.serviceOption(Scope.Scope);
|
|
105
|
+
if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, Effect.sync(() => {
|
|
106
|
+
childrenMap.delete(childId);
|
|
107
|
+
}));
|
|
108
|
+
return child;
|
|
109
|
+
})
|
|
101
110
|
};
|
|
102
111
|
let resolvedInitial;
|
|
103
112
|
let initialVersion;
|
|
@@ -167,7 +176,7 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
|
|
|
167
176
|
finalState: resolvedInitial,
|
|
168
177
|
timestamp
|
|
169
178
|
}));
|
|
170
|
-
return buildPersistentActorRef(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system);
|
|
179
|
+
return buildPersistentActorRef(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system, childrenMap);
|
|
171
180
|
}
|
|
172
181
|
const loopFiber = yield* Effect.forkDaemon(persistentEventLoop(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, self, listeners, adapter, createdAt, stateScopeRef, backgroundFibers, snapshotQueue, snapshotEnabledRef, persistenceQueue, snapshotFiber, persistenceFiber, inspector, system));
|
|
173
182
|
return buildPersistentActorRef(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, Effect.gen(function* () {
|
|
@@ -184,7 +193,7 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
|
|
|
184
193
|
yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
|
|
185
194
|
yield* Fiber.interrupt(snapshotFiber);
|
|
186
195
|
yield* Fiber.interrupt(persistenceFiber);
|
|
187
|
-
}).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system);
|
|
196
|
+
}).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system, childrenMap);
|
|
188
197
|
});
|
|
189
198
|
/**
|
|
190
199
|
* Main event loop for persistent actor
|