effect-machine 0.7.1 → 0.8.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/dist/actor.js CHANGED
@@ -2,8 +2,8 @@ import { Inspector } from "./inspection.js";
2
2
  import { INTERNAL_INIT_EVENT } from "./internal/utils.js";
3
3
  import { DuplicateActorError } 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 } 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, Deferred, Effect, Exit, Fiber, Layer, MutableHashMap, Option, PubSub, Queue, Ref, Scope, Semaphore, ServiceMap, Stream, SubscriptionRef } from "effect";
@@ -144,6 +144,7 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
144
144
  const backgroundFibers = [];
145
145
  const initEvent = { _tag: INTERNAL_INIT_EVENT };
146
146
  const ctx = {
147
+ actorId: id,
147
148
  state: machine.initial,
148
149
  event: initEvent,
149
150
  self,
@@ -152,6 +153,7 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
152
153
  const { effects: effectSlots } = machine._slots;
153
154
  for (const bg of machine.backgroundEffects) {
154
155
  const fiber = yield* Effect.forkDetach(bg.handler({
156
+ actorId: id,
155
157
  state: machine.initial,
156
158
  event: initEvent,
157
159
  self,
@@ -222,7 +224,7 @@ const processEvent = Effect.fn("effect-machine.actor.processEvent")(function* (m
222
224
  event,
223
225
  timestamp
224
226
  }));
225
- const result = yield* processEventCore(machine, currentState, event, self, stateScopeRef, system, inspector === void 0 ? void 0 : {
227
+ const result = yield* processEventCore(machine, currentState, event, self, stateScopeRef, system, actorId, inspector === void 0 ? void 0 : {
226
228
  onSpawnEffect: (state) => emitWithTimestamp(inspector, (timestamp) => ({
227
229
  type: "@machine.effect",
228
230
  actorId,
@@ -283,7 +285,7 @@ const runSpawnEffectsWithInspection = Effect.fn("effect-machine.actor.spawnEffec
283
285
  state,
284
286
  timestamp
285
287
  }));
286
- yield* runSpawnEffects(machine, state, event, self, stateScope, system, inspector === void 0 ? void 0 : (info) => emitWithTimestamp(inspector, (timestamp) => ({
288
+ yield* runSpawnEffects(machine, state, event, self, stateScope, system, actorId, inspector === void 0 ? void 0 : (info) => emitWithTimestamp(inspector, (timestamp) => ({
287
289
  type: "@machine.error",
288
290
  actorId,
289
291
  phase: info.phase,
@@ -14,7 +14,7 @@ import { Entity } from "effect/unstable/cluster";
14
14
  * Returns the new state after processing.
15
15
  */
16
16
  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);
17
+ const result = yield* processEventCore(machine, yield* Ref.get(stateRef), event, self, stateScopeRef, system, "*", hooks);
18
18
  if (result.transitioned) yield* Ref.set(stateRef, result.newState);
19
19
  return result.newState;
20
20
  });
@@ -63,7 +63,7 @@ const EntityMachine = { layer: (entity, machine, options) => {
63
63
  };
64
64
  const stateRef = yield* Ref.make(initialState);
65
65
  const stateScopeRef = { current: yield* Scope.make() };
66
- yield* runSpawnEffects(machine, initialState, { _tag: "$init" }, self, stateScopeRef.current, system, options?.hooks?.onError);
66
+ yield* runSpawnEffects(machine, initialState, { _tag: "$init" }, self, stateScopeRef.current, system, entityId, options?.hooks?.onError);
67
67
  const runInternalEvent = Effect.fn("effect-machine.cluster.internalEvent")(function* () {
68
68
  yield* processEvent(machine, stateRef, yield* Queue.take(internalQueue), self, stateScopeRef, system, options?.hooks);
69
69
  });
package/dist/index.d.ts CHANGED
@@ -6,8 +6,8 @@ import { PersistentActorRef, createPersistentActor, restorePersistentActor } fro
6
6
  import { ActorMetadata, PersistedEvent, PersistenceAdapter, PersistenceAdapterTag, PersistenceError, RestoreFailure, RestoreResult, Snapshot, VersionConflictError } from "./persistence/adapter.js";
7
7
  import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
8
8
  import "./persistence/index.js";
9
- import { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, ProvideHandlers, SpawnEffect, StateHandlerContext, Transition, machine_d_exports } from "./machine.js";
9
+ import { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, ProvideHandlers, SpawnEffect, StateHandlerContext, TaskOptions, Transition, machine_d_exports } from "./machine.js";
10
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
- 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 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 };
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 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, 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 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 };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Inspector, collectingInspector, consoleInspector, makeInspector } from "./inspection.js";
1
+ import { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
2
2
  import { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
3
3
  import { isPersistentMachine } from "./persistence/persistent-machine.js";
4
4
  import { Slot } from "./slot.js";
@@ -11,4 +11,4 @@ import { assertNeverReaches, assertPath, assertReaches, createTestHarness, simul
11
11
  import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
12
12
  import "./persistence/index.js";
13
13
 
14
- export { Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, InMemoryPersistenceAdapter, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, MissingSchemaError, PersistenceAdapterTag, PersistenceError, ProvisionValidationError, Slot, SlotProvisionError, State, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, restorePersistentActor, simulate };
14
+ export { Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, InMemoryPersistenceAdapter, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, MissingSchemaError, PersistenceAdapterTag, PersistenceError, ProvisionValidationError, Slot, SlotProvisionError, State, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, makeInspectorEffect, restorePersistentActor, simulate, tracingInspector };
@@ -1,4 +1,4 @@
1
- import { Schema, ServiceMap } from "effect";
1
+ import { Effect, Schema, ServiceMap } from "effect";
2
2
 
3
3
  //#region src/inspection.d.ts
4
4
  /**
@@ -45,6 +45,15 @@ interface EffectEvent<S> {
45
45
  readonly state: S;
46
46
  readonly timestamp: number;
47
47
  }
48
+ interface TaskEvent<S> {
49
+ readonly type: "@machine.task";
50
+ readonly actorId: string;
51
+ readonly state: S;
52
+ readonly taskName?: string;
53
+ readonly phase: "start" | "success" | "failure" | "interrupt";
54
+ readonly error?: string;
55
+ readonly timestamp: number;
56
+ }
48
57
  /**
49
58
  * Event emitted when a transition handler or spawn effect fails with a defect
50
59
  */
@@ -69,7 +78,7 @@ interface StopEvent<S> {
69
78
  /**
70
79
  * Union of all inspection events
71
80
  */
72
- type InspectionEvent<S, E> = SpawnEvent<S> | EventReceivedEvent<S, E> | TransitionEvent<S, E> | EffectEvent<S> | ErrorEvent<S, E> | StopEvent<S>;
81
+ type InspectionEvent<S, E> = SpawnEvent<S> | EventReceivedEvent<S, E> | TransitionEvent<S, E> | EffectEvent<S> | TaskEvent<S> | ErrorEvent<S, E> | StopEvent<S>;
73
82
  /**
74
83
  * Convenience alias for untyped inspection events.
75
84
  * Useful for general-purpose inspectors that don't need specific state/event types.
@@ -84,8 +93,9 @@ type AnyInspectionEvent = InspectionEvent<{
84
93
  /**
85
94
  * Inspector interface for observing machine behavior
86
95
  */
96
+ type InspectorHandler<S, E> = (event: InspectionEvent<S, E>) => void | Effect.Effect<void, never, never>;
87
97
  interface Inspector<S, E> {
88
- readonly onInspect: (event: InspectionEvent<S, E>) => void;
98
+ readonly onInspect: InspectorHandler<S, E>;
89
99
  }
90
100
  /**
91
101
  * Inspector service tag - optional service for machine introspection
@@ -104,7 +114,23 @@ declare const makeInspector: <S = {
104
114
  readonly _tag: string;
105
115
  }, E = {
106
116
  readonly _tag: string;
107
- }>(onInspect: (event: InspectionEvent<ResolveType<S>, ResolveType<E>>) => void) => Inspector<ResolveType<S>, ResolveType<E>>;
117
+ }>(onInspect: InspectorHandler<ResolveType<S>, ResolveType<E>>) => Inspector<ResolveType<S>, ResolveType<E>>;
118
+ declare const makeInspectorEffect: <S = {
119
+ readonly _tag: string;
120
+ }, E = {
121
+ readonly _tag: string;
122
+ }>(onInspect: (event: InspectionEvent<ResolveType<S>, ResolveType<E>>) => Effect.Effect<void, never, never>) => Inspector<ResolveType<S>, ResolveType<E>>;
123
+ declare const combineInspectors: <S, E>(...inspectors: ReadonlyArray<Inspector<S, E>>) => Inspector<S, E>;
124
+ interface TracingInspectorOptions<S, E> {
125
+ readonly spanName?: string | ((event: InspectionEvent<S, E>) => string);
126
+ readonly attributes?: (event: InspectionEvent<S, E>) => Readonly<Record<string, string | number | boolean>>;
127
+ readonly eventName?: (event: InspectionEvent<S, E>) => string;
128
+ }
129
+ declare const tracingInspector: <S extends {
130
+ readonly _tag: string;
131
+ }, E extends {
132
+ readonly _tag: string;
133
+ }>(options?: TracingInspectorOptions<S, E>) => Inspector<S, E>;
108
134
  /**
109
135
  * Console inspector that logs events in a readable format
110
136
  */
@@ -122,4 +148,4 @@ declare const collectingInspector: <S extends {
122
148
  readonly _tag: string;
123
149
  }>(events: InspectionEvent<S, E>[]) => Inspector<S, E>;
124
150
  //#endregion
125
- export { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, InspectionEvent, Inspector, SpawnEvent, StopEvent, TransitionEvent, collectingInspector, consoleInspector, makeInspector };
151
+ export { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, InspectionEvent, Inspector, InspectorHandler, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector };
@@ -1,4 +1,4 @@
1
- import { ServiceMap } from "effect";
1
+ import { Effect, Option, ServiceMap } from "effect";
2
2
 
3
3
  //#region src/inspection.ts
4
4
  /**
@@ -15,6 +15,95 @@ const Inspector = ServiceMap.Service("@effect/machine/Inspector");
15
15
  * - `makeInspector<typeof MyState, typeof MyEvent>(cb)` — schema constructors (auto-extracts `.Type`)
16
16
  */
17
17
  const makeInspector = (onInspect) => ({ onInspect });
18
+ const makeInspectorEffect = (onInspect) => ({ onInspect });
19
+ const inspectionEffect = (inspector, event) => {
20
+ const result = inspector.onInspect(event);
21
+ return Effect.isEffect(result) ? result : Effect.void;
22
+ };
23
+ const combineInspectors = (...inspectors) => ({ onInspect: (event) => Effect.forEach(inspectors, (inspector) => inspectionEffect(inspector, event).pipe(Effect.catchCause(() => Effect.void)), {
24
+ concurrency: "unbounded",
25
+ discard: true
26
+ }) });
27
+ const inspectionSpanName = (event) => {
28
+ switch (event.type) {
29
+ case "@machine.spawn": return `Machine.inspect ${event.initialState._tag}`;
30
+ case "@machine.event": return `Machine.inspect ${event.event._tag}`;
31
+ case "@machine.transition": return `Machine.inspect ${event.fromState._tag}->${event.toState._tag}`;
32
+ case "@machine.effect": return `Machine.inspect ${event.effectType}`;
33
+ case "@machine.task": return `Machine.inspect task:${event.phase}`;
34
+ case "@machine.error": return `Machine.inspect ${event.phase}`;
35
+ case "@machine.stop": return `Machine.inspect ${event.finalState._tag}`;
36
+ }
37
+ };
38
+ const inspectionTraceName = (event) => {
39
+ switch (event.type) {
40
+ case "@machine.spawn": return `machine.spawn ${event.initialState._tag}`;
41
+ case "@machine.event": return `machine.event ${event.event._tag}`;
42
+ case "@machine.transition": return `machine.transition ${event.fromState._tag}->${event.toState._tag}`;
43
+ case "@machine.effect": return `machine.effect ${event.effectType}`;
44
+ case "@machine.task": return `machine.task ${event.phase}${event.taskName === void 0 ? "" : ` ${event.taskName}`}`;
45
+ case "@machine.error": return `machine.error ${event.phase}`;
46
+ case "@machine.stop": return `machine.stop ${event.finalState._tag}`;
47
+ }
48
+ };
49
+ const inspectionAttributes = (event) => {
50
+ const shared = {
51
+ "machine.actor.id": event.actorId,
52
+ "machine.inspection.type": event.type
53
+ };
54
+ switch (event.type) {
55
+ case "@machine.spawn": return {
56
+ ...shared,
57
+ "machine.state.initial": event.initialState._tag
58
+ };
59
+ case "@machine.event": return {
60
+ ...shared,
61
+ "machine.state.current": event.state._tag,
62
+ "machine.event.tag": event.event._tag
63
+ };
64
+ case "@machine.transition": return {
65
+ ...shared,
66
+ "machine.state.from": event.fromState._tag,
67
+ "machine.state.to": event.toState._tag,
68
+ "machine.event.tag": event.event._tag
69
+ };
70
+ case "@machine.effect": return {
71
+ ...shared,
72
+ "machine.state.current": event.state._tag,
73
+ "machine.effect.kind": event.effectType
74
+ };
75
+ case "@machine.task": return {
76
+ ...shared,
77
+ "machine.state.current": event.state._tag,
78
+ "machine.task.phase": event.phase,
79
+ ...event.taskName === void 0 ? {} : { "machine.task.name": event.taskName }
80
+ };
81
+ case "@machine.error": return {
82
+ ...shared,
83
+ "machine.phase": event.phase,
84
+ "machine.state.current": event.state._tag
85
+ };
86
+ case "@machine.stop": return {
87
+ ...shared,
88
+ "machine.state.final": event.finalState._tag
89
+ };
90
+ }
91
+ };
92
+ const tracingInspector = (options) => ({ onInspect: (event) => {
93
+ const spanName = typeof options?.spanName === "function" ? options.spanName(event) : options?.spanName;
94
+ const traceName = options?.eventName?.(event) ?? inspectionTraceName(event);
95
+ const attributes = {
96
+ ...inspectionAttributes(event),
97
+ ...options?.attributes?.(event) ?? {}
98
+ };
99
+ return Effect.gen(function* () {
100
+ const currentSpan = yield* Effect.option(Effect.currentSpan);
101
+ if (Option.isSome(currentSpan)) currentSpan.value.event(traceName, BigInt(event.timestamp) * 1000000n, {
102
+ actorId: event.actorId,
103
+ inspectionType: event.type
104
+ });
105
+ }).pipe(Effect.withSpan(spanName ?? inspectionSpanName(event), { attributes }));
106
+ } });
18
107
  /**
19
108
  * Console inspector that logs events in a readable format
20
109
  */
@@ -33,6 +122,9 @@ const consoleInspector = () => makeInspector((event) => {
33
122
  case "@machine.effect":
34
123
  console.log(prefix, event.effectType, "effect in", event.state._tag);
35
124
  break;
125
+ case "@machine.task":
126
+ console.log(prefix, "task", event.phase, event.taskName ?? "<unnamed>", "in", event.state._tag);
127
+ break;
36
128
  case "@machine.error":
37
129
  console.log(prefix, "error in", event.phase, event.state._tag, "-", event.error);
38
130
  break;
@@ -44,7 +136,9 @@ const consoleInspector = () => makeInspector((event) => {
44
136
  /**
45
137
  * Collecting inspector that stores events in an array for testing
46
138
  */
47
- const collectingInspector = (events) => ({ onInspect: (event) => events.push(event) });
139
+ const collectingInspector = (events) => ({ onInspect: (event) => {
140
+ events.push(event);
141
+ } });
48
142
 
49
143
  //#endregion
50
- export { Inspector, collectingInspector, consoleInspector, makeInspector };
144
+ export { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector };
@@ -7,12 +7,15 @@ import { Clock, Effect } from "effect";
7
7
  */
8
8
  const emitWithTimestamp = Effect.fn("effect-machine.emitWithTimestamp")(function* (inspector, makeEvent) {
9
9
  if (inspector === void 0) return;
10
- const timestamp = yield* Clock.currentTimeMillis;
11
- yield* Effect.sync(() => {
10
+ const event = makeEvent(yield* Clock.currentTimeMillis);
11
+ const result = yield* Effect.sync(() => {
12
12
  try {
13
- inspector.onInspect(makeEvent(timestamp));
14
- } catch {}
13
+ return inspector.onInspect(event);
14
+ } catch {
15
+ return;
16
+ }
15
17
  });
18
+ if (Effect.isEffect(result)) yield* result.pipe(Effect.catchCause(() => Effect.void));
16
19
  });
17
20
 
18
21
  //#endregion
@@ -29,7 +29,7 @@ declare const runTransitionHandler: <S extends {
29
29
  readonly _tag: string;
30
30
  }, E extends {
31
31
  readonly _tag: string;
32
- }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, transition: Transition<S, E, GD, EFD, R>, state: S, event: E, self: MachineRef<E>, system: ActorSystem) => Effect.Effect<S, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
32
+ }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, transition: Transition<S, E, GD, EFD, R>, state: S, event: E, self: MachineRef<E>, system: ActorSystem, actorId: string) => Effect.Effect<S, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
33
33
  /**
34
34
  * Execute a transition for a given state and event.
35
35
  * Handles transition resolution, handler invocation, and guard/effect slot creation.
@@ -45,7 +45,7 @@ declare const executeTransition: <S extends {
45
45
  readonly _tag: string;
46
46
  }, E extends {
47
47
  readonly _tag: string;
48
- }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, currentState: S, event: E, self: MachineRef<E>, system: ActorSystem) => Effect.Effect<{
48
+ }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, currentState: S, event: E, self: MachineRef<E>, system: ActorSystem, actorId: string) => Effect.Effect<{
49
49
  newState: S;
50
50
  transitioned: boolean;
51
51
  reenter: boolean;
@@ -103,7 +103,7 @@ declare const processEventCore: <S extends {
103
103
  readonly _tag: string;
104
104
  }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, currentState: S, event: E, self: MachineRef<E>, stateScopeRef: {
105
105
  current: Scope.Closeable;
106
- }, system: ActorSystem, hooks?: ProcessEventHooks<S, E> | undefined) => Effect.Effect<{
106
+ }, system: ActorSystem, actorId: string, hooks?: ProcessEventHooks<S, E> | undefined) => Effect.Effect<{
107
107
  newState: S;
108
108
  previousState: S;
109
109
  transitioned: boolean;
@@ -119,7 +119,7 @@ declare const runSpawnEffects: <S extends {
119
119
  readonly _tag: string;
120
120
  }, E extends {
121
121
  readonly _tag: string;
122
- }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, state: S, event: E, self: MachineRef<E>, stateScope: Scope.Closeable, system: ActorSystem, onError?: ((info: ProcessEventError<S, E>) => Effect.Effect<void>) | undefined) => Effect.Effect<void, never, Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
122
+ }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, state: S, event: E, self: MachineRef<E>, stateScope: Scope.Closeable, system: ActorSystem, actorId: string, onError?: ((info: ProcessEventError<S, E>) => Effect.Effect<void>) | undefined) => Effect.Effect<void, never, Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
123
123
  /**
124
124
  * Resolve which transition should fire for a given state and event.
125
125
  * Uses indexed O(1) lookup. First matching transition wins.
@@ -23,8 +23,9 @@ import { Cause, Effect, Exit, Scope } from "effect";
23
23
  *
24
24
  * @internal
25
25
  */
26
- const runTransitionHandler = Effect.fn("effect-machine.runTransitionHandler")(function* (machine, transition, state, event, self, system) {
26
+ const runTransitionHandler = Effect.fn("effect-machine.runTransitionHandler")(function* (machine, transition, state, event, self, system, actorId) {
27
27
  const ctx = {
28
+ actorId,
28
29
  state,
29
30
  event,
30
31
  self,
@@ -51,7 +52,7 @@ const runTransitionHandler = Effect.fn("effect-machine.runTransitionHandler")(fu
51
52
  *
52
53
  * @internal
53
54
  */
54
- const executeTransition = Effect.fn("effect-machine.executeTransition")(function* (machine, currentState, event, self, system) {
55
+ const executeTransition = Effect.fn("effect-machine.executeTransition")(function* (machine, currentState, event, self, system, actorId) {
55
56
  const transition = resolveTransition(machine, currentState, event);
56
57
  if (transition === void 0) return {
57
58
  newState: currentState,
@@ -59,7 +60,7 @@ const executeTransition = Effect.fn("effect-machine.executeTransition")(function
59
60
  reenter: false
60
61
  };
61
62
  return {
62
- newState: yield* runTransitionHandler(machine, transition, currentState, event, self, system),
63
+ newState: yield* runTransitionHandler(machine, transition, currentState, event, self, system, actorId),
63
64
  transitioned: true,
64
65
  reenter: transition.reenter === true
65
66
  };
@@ -76,8 +77,8 @@ const executeTransition = Effect.fn("effect-machine.executeTransition")(function
76
77
  *
77
78
  * @internal
78
79
  */
79
- const processEventCore = Effect.fn("effect-machine.processEventCore")(function* (machine, currentState, event, self, stateScopeRef, system, hooks) {
80
- const result = yield* executeTransition(machine, currentState, event, self, system).pipe(Effect.catchCause((cause) => {
80
+ const processEventCore = Effect.fn("effect-machine.processEventCore")(function* (machine, currentState, event, self, stateScopeRef, system, actorId, hooks) {
81
+ const result = yield* executeTransition(machine, currentState, event, self, system, actorId).pipe(Effect.catchCause((cause) => {
81
82
  if (Cause.hasInterruptsOnly(cause)) return Effect.interrupt;
82
83
  const onError = hooks?.onError;
83
84
  if (onError === void 0) return Effect.failCause(cause).pipe(Effect.orDie);
@@ -102,7 +103,7 @@ const processEventCore = Effect.fn("effect-machine.processEventCore")(function*
102
103
  stateScopeRef.current = yield* Scope.make();
103
104
  if (hooks?.onTransition !== void 0) yield* hooks.onTransition(currentState, newState, event);
104
105
  if (hooks?.onSpawnEffect !== void 0) yield* hooks.onSpawnEffect(newState);
105
- yield* runSpawnEffects(machine, newState, { _tag: INTERNAL_ENTER_EVENT }, self, stateScopeRef.current, system, hooks?.onError);
106
+ yield* runSpawnEffects(machine, newState, { _tag: INTERNAL_ENTER_EVENT }, self, stateScopeRef.current, system, actorId, hooks?.onError);
106
107
  }
107
108
  return {
108
109
  newState,
@@ -117,9 +118,10 @@ const processEventCore = Effect.fn("effect-machine.processEventCore")(function*
117
118
  *
118
119
  * @internal
119
120
  */
120
- const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (machine, state, event, self, stateScope, system, onError) {
121
+ const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (machine, state, event, self, stateScope, system, actorId, onError) {
121
122
  const spawnEffects = findSpawnEffects(machine, state._tag);
122
123
  const ctx = {
124
+ actorId,
123
125
  state,
124
126
  event,
125
127
  self,
@@ -129,6 +131,7 @@ const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (m
129
131
  const reportError = onError;
130
132
  for (const spawnEffect of spawnEffects) {
131
133
  const effect = spawnEffect.handler({
134
+ actorId,
132
135
  state,
133
136
  event,
134
137
  self,
package/dist/machine.d.ts CHANGED
@@ -11,7 +11,7 @@ import { Cause, Effect, Schedule, Schema, Scope, ServiceMap } from "effect";
11
11
 
12
12
  //#region src/machine.d.ts
13
13
  declare namespace machine_d_exports {
14
- export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, PersistenceConfig, PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, Transition, TransitionHandler, findTransitions, make, spawn };
14
+ export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, PersistenceConfig, PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, Transition, TransitionHandler, findTransitions, make, spawn };
15
15
  }
16
16
  /**
17
17
  * Self reference for sending events back to the machine
@@ -37,6 +37,7 @@ interface HandlerContext<State, Event, GD extends GuardsDef, ED extends EffectsD
37
37
  * Handler context passed to state effect handlers (onEnter, spawn, background)
38
38
  */
39
39
  interface StateHandlerContext<State, Event, ED extends EffectsDef> {
40
+ readonly actorId: string;
40
41
  readonly state: State;
41
42
  readonly event: Event;
42
43
  readonly self: MachineRef<Event>;
@@ -81,6 +82,11 @@ interface PersistOptions {
81
82
  readonly journalEvents: boolean;
82
83
  readonly machineType?: string;
83
84
  }
85
+ interface TaskOptions<State, Event, ED extends EffectsDef, A, E1, ES, EF> {
86
+ readonly onSuccess: (value: A, ctx: StateHandlerContext<State, Event, ED>) => ES;
87
+ readonly onFailure?: (cause: Cause.Cause<E1>, ctx: StateHandlerContext<State, Event, ED>) => EF;
88
+ readonly name?: string;
89
+ }
84
90
  type IsAny<T> = 0 extends 1 & T ? true : false;
85
91
  type IsUnknown<T> = unknown extends T ? ([T] extends [unknown] ? true : false) : false;
86
92
  type NormalizeR<T> = IsAny<T> extends true ? T : IsUnknown<T> extends true ? never : T;
@@ -174,6 +180,10 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
174
180
  get effectsSchema(): EffectsSchema<EFD> | undefined;
175
181
  /** @internal */
176
182
  constructor(initial: State, stateSchema?: Schema.Schema<State>, eventSchema?: Schema.Schema<Event>, guardsSchema?: GuardsSchema<GD>, effectsSchema?: EffectsSchema<EFD>);
183
+ from<NS extends VariantsUnion<_SD> & BrandedState, R1>(state: TaggedOrConstructor<NS>, build: (scope: TransitionScope<State, Event, R, _SD, _ED, GD, EFD, NS>) => R1): Machine<State, Event, R, _SD, _ED, GD, EFD>;
184
+ from<NS extends ReadonlyArray<TaggedOrConstructor<VariantsUnion<_SD> & BrandedState>>, R1>(states: NS, build: (scope: TransitionScope<State, Event, R, _SD, _ED, GD, EFD, NS[number] extends TaggedOrConstructor<infer S extends VariantsUnion<_SD> & BrandedState> ? S : never>) => R1): Machine<State, Event, R, _SD, _ED, GD, EFD>;
185
+ /** @internal */
186
+ scopeTransition<NS extends VariantsUnion<_SD> & BrandedState, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(states: ReadonlyArray<TaggedOrConstructor<NS>>, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS, NE, RS, GD, EFD, never>, reenter: boolean): Machine<State, Event, R, _SD, _ED, GD, EFD>;
177
187
  /** Register transition for a single state */
178
188
  on<NS extends VariantsUnion<_SD> & BrandedState, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS, NE, RS, GD, EFD, never>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
179
189
  /** Register transition for multiple states (handler receives union of state types) */
@@ -220,10 +230,7 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
220
230
  * State-scoped task that runs on entry and sends success/failure events.
221
231
  * Interrupts do not emit failure events.
222
232
  */
223
- task<NS extends VariantsUnion<_SD> & BrandedState, A, E1, ES extends VariantsUnion<_ED> & BrandedEvent, EF extends VariantsUnion<_ED> & BrandedEvent>(state: TaggedOrConstructor<NS>, run: (ctx: StateHandlerContext<NS, VariantsUnion<_ED> & BrandedEvent, EFD>) => Effect.Effect<A, E1, Scope.Scope>, options: {
224
- readonly onSuccess: (value: A, ctx: StateHandlerContext<NS, VariantsUnion<_ED> & BrandedEvent, EFD>) => ES;
225
- readonly onFailure?: (cause: Cause.Cause<E1>, ctx: StateHandlerContext<NS, VariantsUnion<_ED> & BrandedEvent, EFD>) => EF;
226
- }): Machine<State, Event, R, _SD, _ED, GD, EFD>;
233
+ task<NS extends VariantsUnion<_SD> & BrandedState, A, E1, ES extends VariantsUnion<_ED> & BrandedEvent, EF extends VariantsUnion<_ED> & BrandedEvent>(state: TaggedOrConstructor<NS>, run: (ctx: StateHandlerContext<NS, VariantsUnion<_ED> & BrandedEvent, EFD>) => Effect.Effect<A, E1, Scope.Scope>, options: TaskOptions<NS, VariantsUnion<_ED> & BrandedEvent, EFD, A, E1, ES, EF>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
227
234
  /**
228
235
  * Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
229
236
  * Use effect slots defined via `Slot.Effects` for the actual work.
@@ -261,6 +268,13 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
261
268
  }, R>;
262
269
  static make<SD extends Record<string, Schema.Struct.Fields>, ED extends Record<string, Schema.Struct.Fields>, S extends BrandedState, E extends BrandedEvent, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(config: MakeConfig<SD, ED, S, E, GD, EFD>): Machine<S, E, never, SD, ED, GD, EFD>;
263
270
  }
271
+ declare class TransitionScope<State, Event, R, _SD extends Record<string, Schema.Struct.Fields>, _ED extends Record<string, Schema.Struct.Fields>, GD extends GuardsDef, EFD extends EffectsDef, SelectedState extends VariantsUnion<_SD> & BrandedState> {
272
+ private readonly machine;
273
+ private readonly states;
274
+ constructor(machine: Machine<State, Event, R, _SD, _ED, GD, EFD>, states: ReadonlyArray<TaggedOrConstructor<SelectedState>>);
275
+ on<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<SelectedState, NE, RS, GD, EFD, never>): TransitionScope<State, Event, R, _SD, _ED, GD, EFD, SelectedState>;
276
+ reenter<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<SelectedState, NE, RS, GD, EFD, never>): TransitionScope<State, Event, R, _SD, _ED, GD, EFD, SelectedState>;
277
+ }
264
278
  declare const make: typeof Machine.make;
265
279
  declare const spawn: {
266
280
  <S extends {
@@ -275,4 +289,4 @@ declare const spawn: {
275
289
  }, R>(machine: BuiltMachine<S, E, R>, id: string): Effect.Effect<ActorRef<S, E>, never, R>;
276
290
  };
277
291
  //#endregion
278
- export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, type PersistenceConfig, type PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, Transition, TransitionHandler, findTransitions, machine_d_exports, make, spawn };
292
+ export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, type PersistenceConfig, type PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, Transition, TransitionHandler, findTransitions, machine_d_exports, make, spawn };
package/dist/machine.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
+ import { Inspector } from "./inspection.js";
2
3
  import { getTag } from "./internal/utils.js";
3
4
  import { ProvisionValidationError, SlotProvisionError } from "./errors.js";
4
5
  import { persist } from "./persistence/persistent-machine.js";
6
+ import { emitWithTimestamp } from "./internal/inspection.js";
5
7
  import { MachineContextTag } from "./slot.js";
6
8
  import { findTransitions, invalidateIndex } from "./internal/transition.js";
7
9
  import { createActor } from "./actor.js";
@@ -15,6 +17,15 @@ var machine_exports = /* @__PURE__ */ __exportAll({
15
17
  make: () => make,
16
18
  spawn: () => spawn
17
19
  });
20
+ const emitTaskInspection = (input) => Effect.flatMap(Effect.serviceOption(Inspector), (inspector) => Option.isNone(inspector) ? Effect.void : emitWithTimestamp(inspector.value, (timestamp) => ({
21
+ type: "@machine.task",
22
+ actorId: input.actorId,
23
+ state: input.state,
24
+ taskName: input.taskName,
25
+ phase: input.phase,
26
+ error: input.error,
27
+ timestamp
28
+ })));
18
29
  /**
19
30
  * A finalized machine ready for spawning.
20
31
  *
@@ -121,6 +132,15 @@ var Machine = class Machine {
121
132
  })) : {}
122
133
  };
123
134
  }
135
+ from(stateOrStates, build) {
136
+ build(new TransitionScope(this, Array.isArray(stateOrStates) ? stateOrStates : [stateOrStates]));
137
+ return this;
138
+ }
139
+ /** @internal */
140
+ scopeTransition(states, event, handler, reenter) {
141
+ for (const state of states) this.addTransition(state, event, handler, reenter);
142
+ return this;
143
+ }
124
144
  on(stateOrStates, event, handler) {
125
145
  const states = Array.isArray(stateOrStates) ? stateOrStates : [stateOrStates];
126
146
  for (const s of states) this.addTransition(s, event, handler, false);
@@ -195,14 +215,41 @@ var Machine = class Machine {
195
215
  */
196
216
  task(state, run, options) {
197
217
  const handler = Effect.fn("effect-machine.task")(function* (ctx) {
218
+ yield* emitTaskInspection({
219
+ actorId: ctx.actorId,
220
+ state: ctx.state,
221
+ taskName: options.name,
222
+ phase: "start"
223
+ });
198
224
  const exit = yield* Effect.exit(run(ctx));
199
225
  if (Exit.isSuccess(exit)) {
226
+ yield* emitTaskInspection({
227
+ actorId: ctx.actorId,
228
+ state: ctx.state,
229
+ taskName: options.name,
230
+ phase: "success"
231
+ });
200
232
  yield* ctx.self.send(options.onSuccess(exit.value, ctx));
201
233
  yield* Effect.yieldNow;
202
234
  return;
203
235
  }
204
236
  const cause = exit.cause;
205
- if (Cause.hasInterruptsOnly(cause)) return;
237
+ if (Cause.hasInterruptsOnly(cause)) {
238
+ yield* emitTaskInspection({
239
+ actorId: ctx.actorId,
240
+ state: ctx.state,
241
+ taskName: options.name,
242
+ phase: "interrupt"
243
+ });
244
+ return;
245
+ }
246
+ yield* emitTaskInspection({
247
+ actorId: ctx.actorId,
248
+ state: ctx.state,
249
+ taskName: options.name,
250
+ phase: "failure",
251
+ error: Cause.pretty(cause)
252
+ });
206
253
  if (options.onFailure !== void 0) {
207
254
  yield* ctx.self.send(options.onFailure(cause, ctx));
208
255
  yield* Effect.yieldNow;
@@ -282,6 +329,20 @@ var Machine = class Machine {
282
329
  return new Machine(config.initial, config.state, config.event, config.guards, config.effects);
283
330
  }
284
331
  };
332
+ var TransitionScope = class {
333
+ constructor(machine, states) {
334
+ this.machine = machine;
335
+ this.states = states;
336
+ }
337
+ on(event, handler) {
338
+ this.machine.scopeTransition(this.states, event, handler, false);
339
+ return this;
340
+ }
341
+ reenter(event, handler) {
342
+ this.machine.scopeTransition(this.states, event, handler, true);
343
+ return this;
344
+ }
345
+ };
285
346
  const make = Machine.make;
286
347
  /**
287
348
  * Spawn an actor directly without ActorSystem ceremony.
@@ -1,7 +1,7 @@
1
1
  import { Inspector } from "../inspection.js";
2
2
  import { INTERNAL_INIT_EVENT, stubSystem } from "../internal/utils.js";
3
- import { processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler } from "../internal/transition.js";
4
3
  import { emitWithTimestamp } from "../internal/inspection.js";
4
+ import { processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler } from "../internal/transition.js";
5
5
  import { PersistenceAdapterTag } from "./adapter.js";
6
6
  import { ActorSystem, buildActorRefCore, notifyListeners } from "../actor.js";
7
7
  import { Cause, Clock, Effect, Exit, Fiber, Option, Queue, Ref, Schedule, Scope, SubscriptionRef } from "effect";
@@ -20,7 +20,7 @@ const replayEvents = Effect.fn("effect-machine.persistentActor.replayEvents")(fu
20
20
  for (const persistedEvent of events) {
21
21
  if (stopVersion !== void 0 && persistedEvent.version > stopVersion) break;
22
22
  const transition = resolveTransition(machine, state, persistedEvent.event);
23
- if (transition !== void 0) state = yield* runTransitionHandler(machine, transition, state, persistedEvent.event, self, stubSystem);
23
+ if (transition !== void 0) state = yield* runTransitionHandler(machine, transition, state, persistedEvent.event, self, stubSystem, "restore");
24
24
  version = persistedEvent.version;
25
25
  }
26
26
  return {
@@ -146,6 +146,7 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
146
146
  const backgroundFibers = [];
147
147
  const initEvent = { _tag: INTERNAL_INIT_EVENT };
148
148
  const initCtx = {
149
+ actorId: id,
149
150
  state: resolvedInitial,
150
151
  event: initEvent,
151
152
  self,
@@ -154,6 +155,7 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
154
155
  const { effects: effectSlots } = typedMachine._slots;
155
156
  for (const bg of typedMachine.backgroundEffects) {
156
157
  const fiber = yield* Effect.forkDetach(bg.handler({
158
+ actorId: id,
157
159
  state: resolvedInitial,
158
160
  event: initEvent,
159
161
  self,
@@ -238,7 +240,7 @@ const persistentEventLoop = Effect.fn("effect-machine.persistentActor.eventLoop"
238
240
  event,
239
241
  timestamp
240
242
  }));
241
- const result = yield* processEventCore(typedMachine, currentState, event, self, stateScopeRef, system, hooks);
243
+ const result = yield* processEventCore(typedMachine, currentState, event, self, stateScopeRef, system, id, hooks);
242
244
  if (!result.transitioned) continue;
243
245
  const newVersion = currentVersion + 1;
244
246
  yield* Ref.set(versionRef, newVersion);
@@ -286,7 +288,7 @@ const runSpawnEffectsWithInspection = Effect.fn("effect-machine.persistentActor.
286
288
  state,
287
289
  timestamp
288
290
  }));
289
- yield* runSpawnEffects(machine, state, event, self, stateScope, system, inspector === void 0 ? void 0 : (info) => emitWithTimestamp(inspector, (timestamp) => ({
291
+ yield* runSpawnEffects(machine, state, event, self, stateScope, system, actorId, inspector === void 0 ? void 0 : (info) => emitWithTimestamp(inspector, (timestamp) => ({
290
292
  type: "@machine.error",
291
293
  actorId,
292
294
  phase: info.phase,
package/dist/schema.js CHANGED
@@ -42,6 +42,7 @@ import { Schema } from "effect";
42
42
  /**
43
43
  * Build a schema-first definition from a record of tag -> fields
44
44
  */
45
+ const RESERVED_DERIVE_KEYS = new Set(["_tag"]);
45
46
  const buildMachineSchema = (definition) => {
46
47
  const variants = {};
47
48
  const constructors = {};
@@ -59,7 +60,10 @@ const buildMachineSchema = (definition) => {
59
60
  constructor.derive = (source, partial) => {
60
61
  const result = { _tag: tag };
61
62
  for (const key of fieldNames) if (key in source) result[key] = source[key];
62
- if (partial !== void 0) for (const [key, value] of Object.entries(partial)) result[key] = value;
63
+ if (partial !== void 0) for (const [key, value] of Object.entries(partial)) {
64
+ if (RESERVED_DERIVE_KEYS.has(key)) continue;
65
+ result[key] = value;
66
+ }
63
67
  return result;
64
68
  };
65
69
  constructors[tag] = constructor;
package/dist/slot.d.ts CHANGED
@@ -43,6 +43,7 @@ type EffectSlots<D extends EffectsDef> = { readonly [K in keyof D & string]: Eff
43
43
  * Shared across all machines via MachineContextTag.
44
44
  */
45
45
  interface MachineContext<State, Event, Self> {
46
+ readonly actorId: string;
46
47
  readonly state: State;
47
48
  readonly event: Event;
48
49
  readonly self: Self;
package/dist/testing.js CHANGED
@@ -34,7 +34,7 @@ const simulate = Effect.fn("effect-machine.simulate")(function* (input, events)
34
34
  let currentState = machine.initial;
35
35
  const states = [currentState];
36
36
  for (const event of events) {
37
- const result = yield* executeTransition(machine, currentState, event, dummySelf, stubSystem);
37
+ const result = yield* executeTransition(machine, currentState, event, dummySelf, stubSystem, "simulation");
38
38
  if (!result.transitioned) continue;
39
39
  currentState = result.newState;
40
40
  states.push(currentState);
@@ -123,7 +123,7 @@ const createTestHarness = Effect.fn("effect-machine.createTestHarness")(function
123
123
  state: stateRef,
124
124
  send: Effect.fn("effect-machine.testHarness.send")(function* (event) {
125
125
  const currentState = yield* SubscriptionRef.get(stateRef);
126
- const result = yield* executeTransition(machine, currentState, event, dummySelf, stubSystem);
126
+ const result = yield* executeTransition(machine, currentState, event, dummySelf, stubSystem, "test-harness");
127
127
  if (!result.transitioned) return currentState;
128
128
  const newState = result.newState;
129
129
  yield* SubscriptionRef.set(stateRef, newState);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-machine",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/cevr/effect-machine.git"
@@ -55,7 +55,7 @@
55
55
  "release": "bun run build && changeset publish"
56
56
  },
57
57
  "dependencies": {
58
- "effect": "4.0.0-beta.21"
58
+ "effect": "4.0.0-beta.35"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@changesets/changelog-github": "^0.5.2",
@@ -80,6 +80,6 @@
80
80
  }
81
81
  },
82
82
  "overrides": {
83
- "effect": "4.0.0-beta.21"
83
+ "effect": "4.0.0-beta.35"
84
84
  }
85
85
  }