effect-machine 0.7.2 → 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 +5 -3
- package/dist/cluster/entity-machine.js +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/inspection.d.ts +31 -5
- package/dist/inspection.js +97 -3
- package/dist/internal/inspection.js +7 -4
- package/dist/internal/transition.d.ts +4 -4
- package/dist/internal/transition.js +10 -7
- package/dist/machine.d.ts +20 -6
- package/dist/machine.js +62 -1
- package/dist/persistence/persistent-actor.js +6 -4
- package/dist/schema.js +5 -1
- package/dist/slot.d.ts +1 -0
- package/dist/testing.js +2 -2
- package/package.json +3 -3
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 };
|
package/dist/inspection.d.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
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 };
|
package/dist/inspection.js
CHANGED
|
@@ -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) =>
|
|
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
|
|
11
|
-
yield* Effect.sync(() => {
|
|
10
|
+
const event = makeEvent(yield* Clock.currentTimeMillis);
|
|
11
|
+
const result = yield* Effect.sync(() => {
|
|
12
12
|
try {
|
|
13
|
-
inspector.onInspect(
|
|
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))
|
|
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))
|
|
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.
|
|
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.
|
|
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.
|
|
83
|
+
"effect": "4.0.0-beta.35"
|
|
84
84
|
}
|
|
85
85
|
}
|