effect-machine 0.20.0 → 0.22.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 CHANGED
@@ -204,6 +204,7 @@ Read [Atom and UI integration](./docs/atom-and-ui.md) and browse [all examples](
204
204
  - Durability saves committed transitions.
205
205
  - Supervision restarts defects within an Effect `Schedule` budget.
206
206
  - Inspection reports events, named transition operations, transitions, named guards, tasks, Effects, errors, stops, and actor generations.
207
+ - Dynamic Inspector hubs let lazy tools observe existing actors without actor restart or state duplication.
207
208
 
208
209
  Read [Persistence and supervision](./docs/persistence-and-supervision.md) and [Inspection](./docs/inspection.md).
209
210
 
package/dist/actor.d.ts CHANGED
@@ -144,6 +144,20 @@ interface ActorRef<State extends {
144
144
  type AnyState = {
145
145
  readonly _tag: string;
146
146
  };
147
+ declare const ActorSystemKeyTypeId: unique symbol;
148
+ /** A typed identity for an actor stored in an ActorSystem. */
149
+ declare class ActorSystemKey<State extends AnyState, Event, Output = State> {
150
+ readonly id: string;
151
+ readonly [ActorSystemKeyTypeId]: {
152
+ readonly state: State;
153
+ readonly event: Event;
154
+ readonly output: Output;
155
+ };
156
+ constructor(id: string);
157
+ }
158
+ /** Create a typed ActorSystem identity. */
159
+ declare const actorSystemKey: <State extends AnyState, Event, Output = State>(id: string) => ActorSystemKey<State, Event, Output>;
160
+ type AnyActorSystemKey = ActorSystemKey<AnyState, unknown, unknown>;
147
161
  /**
148
162
  * Events emitted by the ActorSystem when actors are spawned or stopped.
149
163
  */
@@ -180,9 +194,15 @@ interface ActorSystemService {
180
194
  * ```
181
195
  */
182
196
  readonly spawn: {
197
+ <S extends AnyState, E extends {
198
+ readonly _tag: string;
199
+ }, R, Output>(key: ActorSystemKey<S, E, Output>, machine: Machine<S, E, R, any, any, void, Output>, options?: SystemSpawnOptions<S, E, void>): Effect.Effect<ActorRef<S, E, Output>, DuplicateActorError, R>;
183
200
  <S extends AnyState, E extends {
184
201
  readonly _tag: string;
185
202
  }, R, Output>(id: string, machine: Machine<S, E, R, any, any, void, Output>, options?: SystemSpawnOptions<S, E, void>): Effect.Effect<ActorRef<S, E, Output>, DuplicateActorError, R>;
203
+ <S extends AnyState, E extends {
204
+ readonly _tag: string;
205
+ }, R, Input, Output>(key: ActorSystemKey<S, E, Output>, machine: Machine<S, E, R, any, any, Input, Output>, options: SystemSpawnOptions<S, E, Input>): Effect.Effect<ActorRef<S, E, Output>, DuplicateActorError, R>;
186
206
  <S extends AnyState, E extends {
187
207
  readonly _tag: string;
188
208
  }, R, Input, Output>(id: string, machine: Machine<S, E, R, any, any, Input, Output>, options: SystemSpawnOptions<S, E, Input>): Effect.Effect<ActorRef<S, E, Output>, DuplicateActorError, R>;
@@ -190,11 +210,19 @@ interface ActorSystemService {
190
210
  /**
191
211
  * Get an existing actor by ID
192
212
  */
193
- readonly get: (id: string) => Effect.Effect<Option.Option<ActorRef<AnyState, unknown>>>;
213
+ readonly get: {
214
+ <S extends AnyState, E, Output>(key: ActorSystemKey<S, E, Output>): Effect.Effect<Option.Option<ActorRef<S, E, Output>>>;
215
+ (id: string): Effect.Effect<Option.Option<ActorRef<AnyState, unknown>>>;
216
+ };
217
+ /** Observe the current actor for one ID across actor generations. */
218
+ readonly watch: {
219
+ <S extends AnyState, E, Output>(key: ActorSystemKey<S, E, Output>): Stream.Stream<Option.Option<ActorRef<S, E, Output>>>;
220
+ (id: string): Stream.Stream<Option.Option<ActorRef<AnyState, unknown>>>;
221
+ };
194
222
  /**
195
223
  * Stop an actor by ID
196
224
  */
197
- readonly stop: (id: string) => Effect.Effect<boolean>;
225
+ readonly stop: (id: string | AnyActorSystemKey) => Effect.Effect<boolean>;
198
226
  /**
199
227
  * Async stream of system events (actor spawned/stopped).
200
228
  * Each subscriber gets their own queue — late subscribers miss prior events.
@@ -258,4 +286,4 @@ declare const createActor: <S extends {
258
286
  */
259
287
  declare const Default: Layer.Layer<ActorSystem, never, never>;
260
288
  //#endregion
261
- export { ActorClient, ActorLifecycle, ActorRef, ActorRefSync, ActorScope, ActorSystem, ActorSystemService, Default, type ProcessEventResult, SystemEvent, SystemEventListener, SystemSpawnOptions, TransitionInfo, createActor };
289
+ export { ActorClient, ActorLifecycle, ActorRef, ActorRefSync, ActorScope, ActorSystem, ActorSystemKey, ActorSystemService, Default, type ProcessEventResult, SystemEvent, SystemEventListener, SystemSpawnOptions, TransitionInfo, actorSystemKey, createActor };
package/dist/actor.js CHANGED
@@ -14,6 +14,19 @@ import { Cause, Context, Deferred, Effect, Exit, Fiber, Layer, MutableHashMap, O
14
14
  * - ActorSystem service (spawn/stop/get actors)
15
15
  * - Actor creation and event loop
16
16
  */
17
+ /** A typed identity for an actor stored in an ActorSystem. */
18
+ var ActorSystemKey = class {
19
+ id;
20
+ constructor(id) {
21
+ this.id = id;
22
+ }
23
+ };
24
+ /** Create a typed ActorSystem identity. */
25
+ const actorSystemKey = (id) => new ActorSystemKey(id);
26
+ const actorSystemId = (id) => {
27
+ if (typeof id === "string") return id;
28
+ return id.id;
29
+ };
17
30
  /**
18
31
  * ActorSystem service tag
19
32
  */
@@ -552,11 +565,34 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
552
565
  yield* actor.start.pipe(Effect.catchCause((cause) => actor.stop.pipe(Effect.andThen(Effect.failCause(cause)))));
553
566
  return actor;
554
567
  });
555
- const spawn = (id, machine, options) => withSpawnGate(spawnRegular(id, machine, options));
556
- const get = Effect.fn("effect-machine.actorSystem.get")(function* (id) {
557
- return yield* Effect.sync(() => MutableHashMap.get(actorsMap, id));
558
- });
559
- const stop = Effect.fn("effect-machine.actorSystem.stop")(function* (id) {
568
+ const spawn = (idOrKey, machine, options) => {
569
+ const id = actorSystemId(idOrKey);
570
+ return withSpawnGate(spawnRegular(id, machine, options));
571
+ };
572
+ function get(idOrKey) {
573
+ return Effect.sync(() => MutableHashMap.get(actorsMap, actorSystemId(idOrKey)));
574
+ }
575
+ const sameActor = (left, right) => {
576
+ if (Option.isNone(left)) return Option.isNone(right);
577
+ return Option.isSome(right) && Object.is(left.value, right.value);
578
+ };
579
+ function watch(idOrKey) {
580
+ const id = actorSystemId(idOrKey);
581
+ return Stream.callback((queue) => Effect.acquireRelease(Effect.sync(() => {
582
+ const unsubscribe = (event) => {
583
+ if (event.id !== id) return;
584
+ if (event._tag === "ActorSpawned") Queue.offerUnsafe(queue, Option.some(event.actor));
585
+ else if (event._tag === "ActorStopped") Queue.offerUnsafe(queue, Option.none());
586
+ };
587
+ eventListeners.add(unsubscribe);
588
+ Queue.offerUnsafe(queue, MutableHashMap.get(actorsMap, id));
589
+ return () => {
590
+ eventListeners.delete(unsubscribe);
591
+ };
592
+ }), (unsubscribe) => Effect.sync(unsubscribe))).pipe(Stream.changesWith(sameActor));
593
+ }
594
+ const stop = Effect.fn("effect-machine.actorSystem.stop")(function* (idOrKey) {
595
+ const id = actorSystemId(idOrKey);
560
596
  const maybeActor = MutableHashMap.get(actorsMap, id);
561
597
  if (Option.isNone(maybeActor)) return false;
562
598
  const actor = maybeActor.value;
@@ -573,6 +609,7 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
573
609
  return ActorSystem.of({
574
610
  spawn,
575
611
  get,
612
+ watch,
576
613
  stop,
577
614
  events: Stream.fromPubSub(eventPubSub),
578
615
  get actors() {
@@ -600,4 +637,4 @@ const makeSystem = make;
600
637
  */
601
638
  const Default = Layer.effect(ActorSystem, make());
602
639
  //#endregion
603
- export { ActorScope, ActorSystem, Default, createActor, makeSystem };
640
+ export { ActorScope, ActorSystem, ActorSystemKey, Default, actorSystemKey, createActor, makeSystem };
package/dist/atom.d.ts CHANGED
@@ -1,6 +1,8 @@
1
- import { ActorLifecycle, ActorRef, TransitionInfo } from "./actor.js";
1
+ import { ActorLifecycle, ActorRef, ActorSystemKey, ActorSystemService, TransitionInfo } from "./actor.js";
2
+ import * as Option from "effect/Option";
2
3
  import * as Atom from "effect/unstable/reactivity/Atom";
3
4
  import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
5
+ import * as Cause from "effect/Cause";
4
6
  //#region src/atom.d.ts
5
7
  /**
6
8
  * A writable Atom projection of an actor.
@@ -8,6 +10,32 @@ import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
8
10
  * The Atom value is the current actor state. Atom writes send actor events.
9
11
  */
10
12
  type ActorAtom<State, Event> = Atom.Writable<State, Event>;
13
+ /** A reactive ActorSystem lookup for one typed actor identity. */
14
+ type ActorLookupAtom<State extends {
15
+ readonly _tag: string;
16
+ }, Event, Output> = Atom.Atom<AsyncResult.AsyncResult<Option.Option<ActorRef<State, Event, Output>>, Cause.NoSuchElementError>>;
17
+ /** A Suspense-ready actor acquisition that follows later actor generations. */
18
+ type ActorAcquireAtom<State extends {
19
+ readonly _tag: string;
20
+ }, Event, Output> = Atom.Atom<AsyncResult.AsyncResult<ActorRef<State, Event, Output>, Cause.NoSuchElementError>>;
21
+ /** Observe the current ActorRef for one ActorSystem key. */
22
+ declare const fromSystem: {
23
+ <State extends {
24
+ readonly _tag: string;
25
+ }, Event, Output>(key: ActorSystemKey<State, Event, Output>): (system: ActorSystemService) => ActorLookupAtom<State, Event, Output>;
26
+ <State extends {
27
+ readonly _tag: string;
28
+ }, Event, Output>(system: ActorSystemService, key: ActorSystemKey<State, Event, Output>): ActorLookupAtom<State, Event, Output>;
29
+ };
30
+ /** Suspend while an ActorSystem key is absent and follow later generations. */
31
+ declare const acquire: {
32
+ <State extends {
33
+ readonly _tag: string;
34
+ }, Event, Output>(key: ActorSystemKey<State, Event, Output>): (system: ActorSystemService) => ActorAcquireAtom<State, Event, Output>;
35
+ <State extends {
36
+ readonly _tag: string;
37
+ }, Event, Output>(system: ActorSystemService, key: ActorSystemKey<State, Event, Output>): ActorAcquireAtom<State, Event, Output>;
38
+ };
11
39
  /**
12
40
  * Make a writable Atom from an actor.
13
41
  *
@@ -53,4 +81,4 @@ declare const can: {
53
81
  }, Event, Output>(actor: ActorRef<State, Event, Output>, event: Event): CanAtom;
54
82
  };
55
83
  //#endregion
56
- export { ActorAtom, CanAtom, can, latestTransition, lifecycle, make, select };
84
+ export { ActorAcquireAtom, ActorAtom, ActorLookupAtom, CanAtom, acquire, can, fromSystem, latestTransition, lifecycle, make, select };
package/dist/atom.js CHANGED
@@ -1,4 +1,6 @@
1
+ import { Match } from "effect";
1
2
  import { dual } from "effect/Function";
3
+ import * as Option$1 from "effect/Option";
2
4
  import * as Atom from "effect/unstable/reactivity/Atom";
3
5
  import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
4
6
  //#region src/atom.ts
@@ -8,6 +10,17 @@ import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
8
10
  * The adapter keeps the actor as the state owner. Atom registries observe the
9
11
  * actor's SubscriptionRef and write events through its synchronous boundary.
10
12
  */
13
+ /** Observe the current ActorRef for one ActorSystem key. */
14
+ const fromSystem = dual(2, (system, key) => Atom.make(system.watch(key), { initialValue: Option$1.none() }));
15
+ /** Suspend while an ActorSystem key is absent and follow later generations. */
16
+ const acquire = dual(2, (system, key) => Atom.map(fromSystem(system, key), (result) => Match.value(result).pipe(Match.tagsExhaustive({
17
+ Initial: (initial) => AsyncResult.initial(initial.waiting),
18
+ Failure: (failure) => AsyncResult.failure(failure.cause, { waiting: failure.waiting }),
19
+ Success: (success) => Option$1.match(success.value, {
20
+ onNone: () => AsyncResult.initial(true),
21
+ onSome: (actor) => AsyncResult.success(actor, success)
22
+ })
23
+ }))));
11
24
  /**
12
25
  * Make a writable Atom from an actor.
13
26
  *
@@ -44,4 +57,4 @@ const can = dual(2, (actor, event) => {
44
57
  }).pipe(Atom.withEquality((value, next) => AsyncResult.isSuccess(value) && AsyncResult.isSuccess(next) && Object.is(value.value, next.value)));
45
58
  });
46
59
  //#endregion
47
- export { can, latestTransition, lifecycle, make, select };
60
+ export { acquire, can, fromSystem, latestTransition, lifecycle, make, select };
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaEr
4
4
  import { ActorExit, DefectPhase, Supervision } from "./supervision.js";
5
5
  import { Durability, DurabilityCommit, FinalContext, GuardPredicate, HandlerContext, InputMakeConfig, Lifecycle, Machine, MachineRef, MakeConfig, Recovery, RecoveryContext, ReplayOptions, SpawnOptions, StateHandlerContext, TaskOptions, TimeoutConfig, machine_d_exports } from "./machine.js";
6
6
  import { ProcessEventResult } from "./internal/transition.js";
7
- import { ActorClient, ActorLifecycle, ActorRef, ActorRefSync, ActorScope, ActorSystem, ActorSystemService, Default, SystemEvent, SystemEventListener, SystemSpawnOptions, TransitionInfo } from "./actor.js";
7
+ import { ActorClient, ActorLifecycle, ActorRef, ActorRefSync, ActorScope, ActorSystem, ActorSystemKey, ActorSystemService, Default, SystemEvent, SystemEventListener, SystemSpawnOptions, TransitionInfo, actorSystemKey } from "./actor.js";
8
8
  import { InputTestHarnessOptions, SimulationOptions, SimulationResult, TestHarness, TestHarnessOptions, assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
9
- import { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, GuardEvent, InspectionEvent, Inspector, InspectorHandler, InspectorService, OperationEvent, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
10
- export { type ActorClient, ActorExit, type ActorExit as ActorExitType, type ActorLifecycle, type ActorRef, type ActorRefSync, ActorScope, ActorStoppedError, type ActorSystemService as ActorSystem, Default as ActorSystemDefault, ActorSystem as ActorSystemService, type AnyInspectionEvent, AssertionError, type DefectPhase, type DeferReplyResult, DuplicateActorError, type Durability, type DurabilityCommit, type EffectEvent, type ErrorEvent, Event, type EventReceivedEvent, type FinalContext, type GuardEvent, type GuardPredicate, type HandlerContext, type InputMakeConfig, type InputTestHarnessOptions, type InspectionEvent, type InspectorService as Inspector, type InspectorHandler, Inspector as InspectorService, InvalidSchemaError, type Lifecycle, machine_d_exports as Machine, type MachineEventSchema, type MachineRef, type MachineStateSchema, type Machine as MachineType, type MakeConfig, MissingMatchHandlerError, NoReplyError, type OperationEvent, PersistenceError, type ProcessEventResult, type Recovery, type RecoveryContext, type ReplayOptions, type ReplyFields, type ReplyResult, type SimulationOptions, type SimulationResult, type SpawnEvent, type SpawnOptions, State, type StateHandlerContext, type StopEvent, Supervision, type SystemEvent, type SystemEventListener, type SystemSpawnOptions, type TaskEvent, type TaskOptions, type TestHarness, type TestHarnessOptions, type TimeoutConfig, type TracingInspectorOptions, type TransitionEvent, type TransitionInfo, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
9
+ import { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, GuardEvent, InspectionEvent, Inspector, InspectorHandler, InspectorHub, InspectorService, OperationEvent, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, makeInspectorHub, tracingInspector } from "./inspection.js";
10
+ export { type ActorClient, ActorExit, type ActorExit as ActorExitType, type ActorLifecycle, type ActorRef, type ActorRefSync, ActorScope, ActorStoppedError, type ActorSystemService as ActorSystem, Default as ActorSystemDefault, ActorSystemKey, ActorSystem as ActorSystemService, type AnyInspectionEvent, AssertionError, type DefectPhase, type DeferReplyResult, DuplicateActorError, type Durability, type DurabilityCommit, type EffectEvent, type ErrorEvent, Event, type EventReceivedEvent, type FinalContext, type GuardEvent, type GuardPredicate, type HandlerContext, type InputMakeConfig, type InputTestHarnessOptions, type InspectionEvent, type InspectorService as Inspector, type InspectorHandler, type InspectorHub, Inspector as InspectorService, InvalidSchemaError, type Lifecycle, machine_d_exports as Machine, type MachineEventSchema, type MachineRef, type MachineStateSchema, type Machine as MachineType, type MakeConfig, MissingMatchHandlerError, NoReplyError, type OperationEvent, PersistenceError, type ProcessEventResult, type Recovery, type RecoveryContext, type ReplayOptions, type ReplyFields, type ReplyResult, type SimulationOptions, type SimulationResult, type SpawnEvent, type SpawnOptions, State, type StateHandlerContext, type StopEvent, Supervision, type SystemEvent, type SystemEventListener, type SystemSpawnOptions, type TaskEvent, type TaskOptions, type TestHarness, type TestHarnessOptions, type TimeoutConfig, type TracingInspectorOptions, type TransitionEvent, type TransitionInfo, VersionConflictError, actorSystemKey, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, makeInspectorHub, simulate, tracingInspector };
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, NoReplyError, PersistenceError, VersionConflictError } from "./errors.js";
2
2
  import { Event, State } from "./schema.js";
3
- import { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
3
+ import { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, makeInspectorHub, tracingInspector } from "./inspection.js";
4
4
  import { ActorExit, Supervision } from "./supervision.js";
5
- import { ActorScope, ActorSystem, Default } from "./actor.js";
5
+ import { ActorScope, ActorSystem, ActorSystemKey, Default, actorSystemKey } from "./actor.js";
6
6
  import { machine_exports } from "./machine.js";
7
7
  import { assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
8
- export { ActorExit, ActorScope, ActorStoppedError, Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, NoReplyError, PersistenceError, State, Supervision, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
8
+ export { ActorExit, ActorScope, ActorStoppedError, Default as ActorSystemDefault, ActorSystemKey, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, NoReplyError, PersistenceError, State, Supervision, VersionConflictError, actorSystemKey, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, makeInspectorHub, simulate, tracingInspector };
@@ -124,6 +124,13 @@ type InspectorHandler<S, E> = (event: InspectionEvent<S, E>) => void | Effect.Ef
124
124
  interface InspectorService<S, E> {
125
125
  readonly onInspect: InspectorHandler<S, E>;
126
126
  }
127
+ /**
128
+ * Dynamic Inspector fan-out for applications that load inspection consumers after actors start.
129
+ */
130
+ interface InspectorHub<S, E> {
131
+ readonly inspector: InspectorService<S, E>;
132
+ readonly register: (inspector: InspectorService<S, E>) => () => void;
133
+ }
127
134
  declare const Inspector_base: Context.ServiceClass<Inspector, "effect-machine/inspection/Inspector", InspectorService<any, any>>;
128
135
  /**
129
136
  * Inspector service tag - optional service for machine introspection
@@ -149,6 +156,17 @@ declare const makeInspectorEffect: <S = {
149
156
  readonly _tag: string;
150
157
  }>(onInspect: (event: InspectionEvent<ResolveType<S>, ResolveType<E>>) => Effect.Effect<void>) => InspectorService<ResolveType<S>, ResolveType<E>>;
151
158
  declare const combineInspectors: <S, E>(...inspectors: ReadonlyArray<InspectorService<S, E>>) => InspectorService<S, E>;
159
+ /**
160
+ * Create an Inspector that accepts sinks throughout its lifetime.
161
+ *
162
+ * Provide `hub.inspector` before actor startup. Consumers can then register and unregister sinks
163
+ * without restarting the actor. A sink failure does not affect the actor or other sinks.
164
+ */
165
+ declare const makeInspectorHub: <S = {
166
+ readonly _tag: string;
167
+ }, E = {
168
+ readonly _tag: string;
169
+ }>() => InspectorHub<ResolveType<S>, ResolveType<E>>;
152
170
  interface TracingInspectorOptions<S, E> {
153
171
  readonly spanName?: string | ((event: InspectionEvent<S, E>) => string);
154
172
  readonly attributes?: (event: InspectionEvent<S, E>) => Readonly<Record<string, string | number | boolean>>;
@@ -176,4 +194,4 @@ declare const collectingInspector: <S extends {
176
194
  readonly _tag: string;
177
195
  }>(events: InspectionEvent<S, E>[]) => InspectorService<S, E>;
178
196
  //#endregion
179
- export { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, GuardEvent, InspectionEvent, Inspector, InspectorHandler, InspectorService, OperationEvent, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector };
197
+ export { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, GuardEvent, InspectionEvent, Inspector, InspectorHandler, InspectorHub, InspectorService, OperationEvent, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, makeInspectorHub, tracingInspector };
@@ -15,15 +15,39 @@ var Inspector = class extends Context.Service()("effect-machine/inspection/Inspe
15
15
  */
16
16
  const makeInspector = (onInspect) => ({ onInspect });
17
17
  const makeInspectorEffect = (onInspect) => ({ onInspect });
18
- const inspectionEffect = (inspector, event) => {
18
+ const inspectionEffect = (inspector, event) => Effect.suspend(() => {
19
19
  const result = inspector.onInspect(event);
20
20
  if (Effect.isEffect(result)) return result;
21
21
  return Effect.void;
22
- };
22
+ });
23
23
  const combineInspectors = (...inspectors) => ({ onInspect: (event) => Effect.forEach(inspectors, (inspector) => inspectionEffect(inspector, event).pipe(Effect.ignoreCause), {
24
24
  concurrency: 16,
25
25
  discard: true
26
26
  }) });
27
+ /**
28
+ * Create an Inspector that accepts sinks throughout its lifetime.
29
+ *
30
+ * Provide `hub.inspector` before actor startup. Consumers can then register and unregister sinks
31
+ * without restarting the actor. A sink failure does not affect the actor or other sinks.
32
+ */
33
+ const makeInspectorHub = () => {
34
+ const inspectorByRegistration = /* @__PURE__ */ new Map();
35
+ const inspector = { onInspect: (event) => Effect.forEach(Array.from(inspectorByRegistration.values()), (registeredInspector) => inspectionEffect(registeredInspector, event).pipe(Effect.ignoreCause), {
36
+ concurrency: 16,
37
+ discard: true
38
+ }) };
39
+ const register = (registeredInspector) => {
40
+ const registration = Symbol();
41
+ inspectorByRegistration.set(registration, registeredInspector);
42
+ return () => {
43
+ inspectorByRegistration.delete(registration);
44
+ };
45
+ };
46
+ return {
47
+ inspector,
48
+ register
49
+ };
50
+ };
27
51
  const inspectionSpanName = (event) => {
28
52
  switch (event.type) {
29
53
  case "@machine.spawn": return `Machine.inspect ${event.initialState._tag}`;
@@ -157,4 +181,4 @@ const collectingInspector = (events) => ({ onInspect: (event) => {
157
181
  events.push(event);
158
182
  } });
159
183
  //#endregion
160
- export { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector };
184
+ export { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, makeInspectorHub, tracingInspector };
@@ -64,6 +64,7 @@ const isEffect = Effect.isEffect;
64
64
  const stubSystem = {
65
65
  spawn: () => Effect.die("spawn not supported in stub system"),
66
66
  get: () => Effect.die("get not supported in stub system"),
67
+ watch: () => Stream.die("watch not supported in stub system"),
67
68
  stop: () => Effect.die("stop not supported in stub system"),
68
69
  events: Stream.empty,
69
70
  get actors() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-machine",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/cevr/effect-machine.git"