effect-machine 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +171 -173
- package/dist/actor.d.ts +95 -24
- package/dist/actor.js +215 -84
- package/dist/atom.d.ts +56 -3
- package/dist/atom.js +33 -2
- package/dist/cluster/entity-machine.d.ts +9 -3
- package/dist/cluster/entity-machine.js +8 -8
- package/dist/cluster/index.d.ts +2 -2
- package/dist/cluster/to-entity.d.ts +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +2 -2
- package/dist/inspection.d.ts +31 -3
- package/dist/inspection.js +21 -0
- package/dist/internal/inspection.d.ts +1 -1
- package/dist/internal/inspection.js +23 -1
- package/dist/internal/machine-definition.d.ts +1 -0
- package/dist/internal/machine-initialization.d.ts +21 -0
- package/dist/internal/machine-initialization.js +27 -0
- package/dist/internal/runtime.d.ts +15 -1
- package/dist/internal/runtime.js +67 -26
- package/dist/internal/transition.d.ts +51 -3
- package/dist/internal/transition.js +216 -38
- package/dist/internal/utils.js +1 -0
- package/dist/machine.d.ts +151 -40
- package/dist/machine.js +139 -40
- package/dist/supervision.d.ts +3 -2
- package/dist/supervision.js +3 -2
- package/dist/testing.d.ts +39 -67
- package/dist/testing.js +13 -52
- package/package.json +3 -2
package/dist/atom.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { Match } from "effect";
|
|
1
2
|
import { dual } from "effect/Function";
|
|
3
|
+
import * as Option$1 from "effect/Option";
|
|
2
4
|
import * as Atom from "effect/unstable/reactivity/Atom";
|
|
5
|
+
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
|
|
3
6
|
//#region src/atom.ts
|
|
4
7
|
/**
|
|
5
8
|
* Effect Atom integration for actors.
|
|
@@ -7,6 +10,17 @@ import * as Atom from "effect/unstable/reactivity/Atom";
|
|
|
7
10
|
* The adapter keeps the actor as the state owner. Atom registries observe the
|
|
8
11
|
* actor's SubscriptionRef and write events through its synchronous boundary.
|
|
9
12
|
*/
|
|
13
|
+
/** Observe the current ActorRef for one ActorSystem key. */
|
|
14
|
+
const fromSystem = dual(2, (system, key) => Atom.make(system.watch(key), { initialValue: Option$1.none() }));
|
|
15
|
+
/** Suspend while an ActorSystem key is absent and follow later generations. */
|
|
16
|
+
const acquire = dual(2, (system, key) => Atom.map(fromSystem(system, key), (result) => Match.value(result).pipe(Match.tagsExhaustive({
|
|
17
|
+
Initial: (initial) => AsyncResult.initial(initial.waiting),
|
|
18
|
+
Failure: (failure) => AsyncResult.failure(failure.cause, { waiting: failure.waiting }),
|
|
19
|
+
Success: (success) => Option$1.match(success.value, {
|
|
20
|
+
onNone: () => AsyncResult.initial(true),
|
|
21
|
+
onSome: (actor) => AsyncResult.success(actor, success)
|
|
22
|
+
})
|
|
23
|
+
}))));
|
|
10
24
|
/**
|
|
11
25
|
* Make a writable Atom from an actor.
|
|
12
26
|
*
|
|
@@ -16,7 +30,7 @@ import * as Atom from "effect/unstable/reactivity/Atom";
|
|
|
16
30
|
*/
|
|
17
31
|
const make = (actor) => {
|
|
18
32
|
const state = Atom.subscriptionRef(actor.state);
|
|
19
|
-
return Atom.writable((get) => get(state), (_ctx, event) => actor.
|
|
33
|
+
return Atom.writable((get) => get(state), (_ctx, event) => actor.client.send(event));
|
|
20
34
|
};
|
|
21
35
|
/**
|
|
22
36
|
* Select part of an actor state.
|
|
@@ -25,5 +39,22 @@ const make = (actor) => {
|
|
|
25
39
|
* The equality function controls when Atom subscribers receive a new value.
|
|
26
40
|
*/
|
|
27
41
|
const select = dual((args) => Atom.isAtom(args[0]), (self, selector, equals = Object.is) => Atom.withEquality(Atom.map(self, selector), equals));
|
|
42
|
+
/** Observe actor lifecycle without coupling it to domain state. */
|
|
43
|
+
const lifecycle = (actor) => Atom.subscriptionRef(actor.lifecycle);
|
|
44
|
+
/** Observe the latest accepted edge. The value remains after actor exit. */
|
|
45
|
+
const latestTransition = (actor) => Atom.subscriptionRef(actor.latestTransition);
|
|
46
|
+
/**
|
|
47
|
+
* Observe whether an event has an enabled transition.
|
|
48
|
+
*
|
|
49
|
+
* The Atom reevaluates after each actor state change. It supports pure and
|
|
50
|
+
* Effect predicates. Effect predicates use the context captured by the actor.
|
|
51
|
+
*/
|
|
52
|
+
const can = dual(2, (actor, event) => {
|
|
53
|
+
const state = Atom.subscriptionRef(actor.state);
|
|
54
|
+
return Atom.make((get) => {
|
|
55
|
+
get(state);
|
|
56
|
+
return actor.can(event);
|
|
57
|
+
}).pipe(Atom.withEquality((value, next) => AsyncResult.isSuccess(value) && AsyncResult.isSuccess(next) && Object.is(value.value, next.value)));
|
|
58
|
+
});
|
|
28
59
|
//#endregion
|
|
29
|
-
export { make, select };
|
|
60
|
+
export { acquire, can, fromSystem, latestTransition, lifecycle, make, select };
|
|
@@ -7,7 +7,7 @@ import { Rpc } from "effect/unstable/rpc";
|
|
|
7
7
|
/**
|
|
8
8
|
* Options for EntityMachine.layer
|
|
9
9
|
*/
|
|
10
|
-
interface
|
|
10
|
+
interface EntityMachineBaseOptions<S> {
|
|
11
11
|
/**
|
|
12
12
|
* Initialize state from entity ID.
|
|
13
13
|
* Called once when entity is first activated.
|
|
@@ -38,6 +38,12 @@ interface EntityMachineOptions<S> {
|
|
|
38
38
|
*/
|
|
39
39
|
readonly persistence?: EntityPersistenceConfig;
|
|
40
40
|
}
|
|
41
|
+
type EntityMachineOptions<S, Input = void> = EntityMachineBaseOptions<S> & ([Input] extends [void] ? {
|
|
42
|
+
readonly input?: never;
|
|
43
|
+
} : {
|
|
44
|
+
/** Map the entity ID to the machine input. */
|
|
45
|
+
readonly input: (entityId: string) => Input;
|
|
46
|
+
});
|
|
41
47
|
/**
|
|
42
48
|
* Create an Entity layer that wires a machine to handle RPC calls.
|
|
43
49
|
*
|
|
@@ -59,7 +65,7 @@ declare const EntityMachine: {
|
|
|
59
65
|
readonly _tag: string;
|
|
60
66
|
}, E extends {
|
|
61
67
|
readonly _tag: string;
|
|
62
|
-
}, R, EntityType extends string, Rpcs extends Rpc.Any>(entity: Entity.Entity<EntityType, Rpcs>, machine: Machine<S, E, R, any, any>, options?: EntityMachineOptions<S>) => Layer.Layer<never, never, R>;
|
|
68
|
+
}, R, Input, Output, EntityType extends string, Rpcs extends Rpc.Any>(entity: Entity.Entity<EntityType, Rpcs>, machine: Machine<S, E, R, any, any, Input, Output>, ...optionsArgument: [Input] extends [void] ? [options?: EntityMachineOptions<S, Input>] : [options: EntityMachineOptions<S, Input>]) => Layer.Layer<never, never, R>;
|
|
63
69
|
};
|
|
64
70
|
//#endregion
|
|
65
|
-
export { EntityMachine, EntityMachineOptions };
|
|
71
|
+
export { EntityMachine, EntityMachineBaseOptions, EntityMachineOptions };
|
|
@@ -35,7 +35,8 @@ import { Entity } from "effect/unstable/cluster";
|
|
|
35
35
|
* })
|
|
36
36
|
* ```
|
|
37
37
|
*/
|
|
38
|
-
const EntityMachine = { layer: (entity, machine,
|
|
38
|
+
const EntityMachine = { layer: (entity, machine, ...optionsArgument) => {
|
|
39
|
+
const options = optionsArgument[0];
|
|
39
40
|
const persistence = options?.persistence;
|
|
40
41
|
const build = Effect.gen(function* () {
|
|
41
42
|
const entityId = yield* Effect.serviceOption(Entity.CurrentAddress).pipe(Effect.map((opt) => {
|
|
@@ -43,23 +44,22 @@ const EntityMachine = { layer: (entity, machine, options) => {
|
|
|
43
44
|
return "";
|
|
44
45
|
}));
|
|
45
46
|
const inspector = Option.getOrUndefined(yield* Effect.serviceOption(Inspector));
|
|
47
|
+
const machineInitial = machine._initial(options?.input?.(entityId));
|
|
46
48
|
const existingSystem = yield* Effect.serviceOption(ActorSystem);
|
|
47
49
|
let system;
|
|
48
50
|
if (Option.isSome(existingSystem)) system = existingSystem.value;
|
|
49
51
|
else system = yield* makeSystem();
|
|
50
|
-
const persistCtx = yield* hydratePersistence(persistence, entity, entityId, machine, options?.initializeState);
|
|
52
|
+
const persistCtx = yield* hydratePersistence(persistence, entity, entityId, machine, machineInitial, options?.initializeState);
|
|
51
53
|
let initialState = persistCtx.hydratedState;
|
|
52
54
|
if (initialState === void 0 && options?.initializeState !== void 0) initialState = options.initializeState(entityId);
|
|
53
|
-
let machineWithState = machine;
|
|
54
|
-
if (initialState !== void 0) machineWithState = machine._withInitial(initialState);
|
|
55
55
|
const versionRef = yield* Ref.make(persistCtx.initialVersion);
|
|
56
|
-
const computedInitial = initialState ??
|
|
56
|
+
const computedInitial = initialState ?? machineInitial;
|
|
57
57
|
const stateRef = yield* SubscriptionRef.make(computedInitial);
|
|
58
58
|
const stoppedRef = yield* Ref.make(false);
|
|
59
59
|
const eventQueue = yield* Queue.unbounded();
|
|
60
60
|
let hooks = void 0;
|
|
61
61
|
if (inspector !== void 0) hooks = makeInspectionHooks(entityId, inspector);
|
|
62
|
-
const runtime = yield* createRuntime(
|
|
62
|
+
const runtime = yield* createRuntime(machine, system, {
|
|
63
63
|
actorId: entityId,
|
|
64
64
|
hooks,
|
|
65
65
|
childIdPrefix: `${entityId}/`,
|
|
@@ -152,7 +152,7 @@ const noPersistence = {
|
|
|
152
152
|
initialVersion: 0
|
|
153
153
|
};
|
|
154
154
|
/** Load snapshot/journal and compute hydrated state. */
|
|
155
|
-
const hydratePersistence = (persistence, entityDef, entityId, machine, initializeState) => Effect.gen(function* () {
|
|
155
|
+
const hydratePersistence = (persistence, entityDef, entityId, machine, machineInitial, initializeState) => Effect.gen(function* () {
|
|
156
156
|
if (persistence === void 0) return noPersistence;
|
|
157
157
|
const adapter = yield* PersistenceAdapter;
|
|
158
158
|
const key = {
|
|
@@ -164,7 +164,7 @@ const hydratePersistence = (persistence, entityDef, entityId, machine, initializ
|
|
|
164
164
|
let baseState;
|
|
165
165
|
if (Option.isSome(maybeSnapshot)) baseState = maybeSnapshot.value.state;
|
|
166
166
|
else if (initializeState !== void 0) baseState = initializeState(entityId);
|
|
167
|
-
else baseState =
|
|
167
|
+
else baseState = machineInitial;
|
|
168
168
|
let snapshotVersion = 0;
|
|
169
169
|
if (Option.isSome(maybeSnapshot)) snapshotVersion = maybeSnapshot.value.version;
|
|
170
170
|
const events = yield* adapter.loadEvents(key, snapshotVersion);
|
package/dist/cluster/index.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ import { EntityPersistenceConfig, PersistedEvent, PersistenceAdapter, Persistenc
|
|
|
2
2
|
import { makeInMemoryPersistenceAdapter } from "./adapters/in-memory.js";
|
|
3
3
|
import { EntityRpcs, ToEntityOptions, toEntity } from "./to-entity.js";
|
|
4
4
|
import { EntityActorRef, makeEntityActorRef } from "./entity-actor-ref.js";
|
|
5
|
-
import { EntityMachine, EntityMachineOptions } from "./entity-machine.js";
|
|
6
|
-
export { type EntityActorRef, EntityMachine, type EntityMachineOptions, type EntityPersistenceConfig, type EntityRpcs, type PersistedEvent, PersistenceAdapter, type PersistenceAdapterService as PersistenceAdapterInterface, type PersistenceKey, type Snapshot, type ToEntityOptions, makeEntityActorRef, makeInMemoryPersistenceAdapter, toEntity };
|
|
5
|
+
import { EntityMachine, EntityMachineBaseOptions, EntityMachineOptions } from "./entity-machine.js";
|
|
6
|
+
export { type EntityActorRef, EntityMachine, type EntityMachineBaseOptions, type EntityMachineOptions, type EntityPersistenceConfig, type EntityRpcs, type PersistedEvent, PersistenceAdapter, type PersistenceAdapterService as PersistenceAdapterInterface, type PersistenceKey, type Snapshot, type ToEntityOptions, makeEntityActorRef, makeInMemoryPersistenceAdapter, toEntity };
|
|
@@ -59,7 +59,7 @@ declare const toEntity: <S extends {
|
|
|
59
59
|
readonly _tag: string;
|
|
60
60
|
}, E extends {
|
|
61
61
|
readonly _tag: string;
|
|
62
|
-
}, R, SD extends Record<string, Schema.Struct.Fields>, ED extends Record<string, Schema.Struct.Fields
|
|
62
|
+
}, R, SD extends Record<string, Schema.Struct.Fields>, ED extends Record<string, Schema.Struct.Fields>, Input, Output>(machine: Machine<S, E, R, SD, ED, Input, Output>, options: ToEntityOptions) => Entity.Entity<string, EntityRpcs<MachineStateSchema<SD> & {
|
|
63
63
|
readonly Type: S;
|
|
64
64
|
}, MachineEventSchema<ED> & {
|
|
65
65
|
readonly Type: E;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ import { DeferReplyResult, ReplyResult } from "./internal/utils.js";
|
|
|
2
2
|
import { Event, MachineEventSchema, MachineStateSchema, ReplyFields, State } from "./schema.js";
|
|
3
3
|
import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, NoReplyError, PersistenceError, VersionConflictError } from "./errors.js";
|
|
4
4
|
import { ActorExit, DefectPhase, Supervision } from "./supervision.js";
|
|
5
|
-
import { Durability, DurabilityCommit, HandlerContext, Lifecycle, Machine, MachineRef, MakeConfig, Recovery, RecoveryContext, StateHandlerContext, TaskOptions, TimeoutConfig, machine_d_exports } from "./machine.js";
|
|
5
|
+
import { Durability, DurabilityCommit, FinalContext, GuardPredicate, HandlerContext, InputMakeConfig, Lifecycle, Machine, MachineRef, MakeConfig, Recovery, RecoveryContext, ReplayOptions, SpawnOptions, StateHandlerContext, TaskOptions, TimeoutConfig, machine_d_exports } from "./machine.js";
|
|
6
6
|
import { ProcessEventResult } from "./internal/transition.js";
|
|
7
|
-
import { ActorRef, ActorRefSync, ActorScope, ActorSystem, ActorSystemService, Default, SystemEvent, SystemEventListener, TransitionInfo } from "./actor.js";
|
|
8
|
-
import { SimulationResult, TestHarness, TestHarnessOptions, assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
|
|
9
|
-
import { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, InspectionEvent, Inspector, InspectorHandler, InspectorService, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
|
|
10
|
-
export { ActorExit, type ActorRef, type ActorRefSync, ActorScope, ActorStoppedError, type ActorSystemService as ActorSystem, Default as ActorSystemDefault, ActorSystem as ActorSystemService, type AnyInspectionEvent, AssertionError, type DefectPhase, type DeferReplyResult, DuplicateActorError, type Durability, type DurabilityCommit, type EffectEvent, type ErrorEvent, Event, type EventReceivedEvent, type HandlerContext, type InspectionEvent, type InspectorService as Inspector, type InspectorHandler, Inspector as InspectorService, InvalidSchemaError, type Lifecycle, machine_d_exports as Machine, type MachineEventSchema, type MachineRef, type MachineStateSchema, type Machine as MachineType, type MakeConfig, MissingMatchHandlerError, NoReplyError, PersistenceError, type ProcessEventResult, type Recovery, type RecoveryContext, type ReplyFields, type ReplyResult, type SimulationResult, type SpawnEvent, State, type StateHandlerContext, type StopEvent, Supervision, type SystemEvent, type SystemEventListener, type TaskEvent, type TaskOptions, type TestHarness, type TestHarnessOptions, type TimeoutConfig, type TracingInspectorOptions, type TransitionEvent, type TransitionInfo, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
|
|
7
|
+
import { ActorClient, ActorLifecycle, ActorRef, ActorRefSync, ActorScope, ActorSystem, ActorSystemKey, ActorSystemService, Default, SystemEvent, SystemEventListener, SystemSpawnOptions, TransitionInfo, actorSystemKey } from "./actor.js";
|
|
8
|
+
import { InputTestHarnessOptions, SimulationOptions, SimulationResult, TestHarness, TestHarnessOptions, assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
|
|
9
|
+
import { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, GuardEvent, InspectionEvent, Inspector, InspectorHandler, InspectorService, OperationEvent, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
|
|
10
|
+
export { type ActorClient, ActorExit, type ActorExit as ActorExitType, type ActorLifecycle, type ActorRef, type ActorRefSync, ActorScope, ActorStoppedError, type ActorSystemService as ActorSystem, Default as ActorSystemDefault, ActorSystemKey, ActorSystem as ActorSystemService, type AnyInspectionEvent, AssertionError, type DefectPhase, type DeferReplyResult, DuplicateActorError, type Durability, type DurabilityCommit, type EffectEvent, type ErrorEvent, Event, type EventReceivedEvent, type FinalContext, type GuardEvent, type GuardPredicate, type HandlerContext, type InputMakeConfig, type InputTestHarnessOptions, type InspectionEvent, type InspectorService as Inspector, type InspectorHandler, Inspector as InspectorService, InvalidSchemaError, type Lifecycle, machine_d_exports as Machine, type MachineEventSchema, type MachineRef, type MachineStateSchema, type Machine as MachineType, type MakeConfig, MissingMatchHandlerError, NoReplyError, type OperationEvent, PersistenceError, type ProcessEventResult, type Recovery, type RecoveryContext, type ReplayOptions, type ReplyFields, type ReplyResult, type SimulationOptions, type SimulationResult, type SpawnEvent, type SpawnOptions, State, type StateHandlerContext, type StopEvent, Supervision, type SystemEvent, type SystemEventListener, type SystemSpawnOptions, type TaskEvent, type TaskOptions, type TestHarness, type TestHarnessOptions, type TimeoutConfig, type TracingInspectorOptions, type TransitionEvent, type TransitionInfo, VersionConflictError, actorSystemKey, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaEr
|
|
|
2
2
|
import { Event, State } from "./schema.js";
|
|
3
3
|
import { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
|
|
4
4
|
import { ActorExit, Supervision } from "./supervision.js";
|
|
5
|
-
import { ActorScope, ActorSystem, Default } from "./actor.js";
|
|
5
|
+
import { ActorScope, ActorSystem, ActorSystemKey, Default, actorSystemKey } from "./actor.js";
|
|
6
6
|
import { machine_exports } from "./machine.js";
|
|
7
7
|
import { assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
|
|
8
|
-
export { ActorExit, ActorScope, ActorStoppedError, Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, NoReplyError, PersistenceError, State, Supervision, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
|
|
8
|
+
export { ActorExit, ActorScope, ActorStoppedError, Default as ActorSystemDefault, ActorSystemKey, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, NoReplyError, PersistenceError, State, Supervision, VersionConflictError, actorSystemKey, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
|
package/dist/inspection.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ type ResolveType<T> = T extends Schema.Schema<infer A> ? A : T;
|
|
|
10
10
|
interface SpawnEvent<S> {
|
|
11
11
|
readonly type: "@machine.spawn";
|
|
12
12
|
readonly actorId: string;
|
|
13
|
+
readonly generation: number;
|
|
13
14
|
readonly initialState: S;
|
|
14
15
|
readonly timestamp: number;
|
|
15
16
|
}
|
|
@@ -19,6 +20,7 @@ interface SpawnEvent<S> {
|
|
|
19
20
|
interface EventReceivedEvent<S, E> {
|
|
20
21
|
readonly type: "@machine.event";
|
|
21
22
|
readonly actorId: string;
|
|
23
|
+
readonly generation: number;
|
|
22
24
|
readonly state: S;
|
|
23
25
|
readonly event: E;
|
|
24
26
|
readonly timestamp: number;
|
|
@@ -29,17 +31,40 @@ interface EventReceivedEvent<S, E> {
|
|
|
29
31
|
interface TransitionEvent<S, E> {
|
|
30
32
|
readonly type: "@machine.transition";
|
|
31
33
|
readonly actorId: string;
|
|
34
|
+
readonly generation: number;
|
|
32
35
|
readonly fromState: S;
|
|
33
36
|
readonly toState: S;
|
|
34
37
|
readonly event: E;
|
|
35
38
|
readonly timestamp: number;
|
|
36
39
|
}
|
|
40
|
+
/** Event emitted after a transition guard runs. */
|
|
41
|
+
interface GuardEvent<S, E> {
|
|
42
|
+
readonly type: "@machine.guard";
|
|
43
|
+
readonly actorId: string;
|
|
44
|
+
readonly generation: number;
|
|
45
|
+
readonly state: S;
|
|
46
|
+
readonly event: E;
|
|
47
|
+
readonly guard: string;
|
|
48
|
+
readonly result: boolean;
|
|
49
|
+
readonly timestamp: number;
|
|
50
|
+
}
|
|
51
|
+
/** Event emitted before an accepted transition handler runs. */
|
|
52
|
+
interface OperationEvent<S, E> {
|
|
53
|
+
readonly type: "@machine.operation";
|
|
54
|
+
readonly actorId: string;
|
|
55
|
+
readonly generation: number;
|
|
56
|
+
readonly operation: string;
|
|
57
|
+
readonly state: S;
|
|
58
|
+
readonly event: E;
|
|
59
|
+
readonly timestamp: number;
|
|
60
|
+
}
|
|
37
61
|
/**
|
|
38
62
|
* Event emitted when a spawn effect runs
|
|
39
63
|
*/
|
|
40
64
|
interface EffectEvent<S> {
|
|
41
65
|
readonly type: "@machine.effect";
|
|
42
66
|
readonly actorId: string;
|
|
67
|
+
readonly generation: number;
|
|
43
68
|
readonly effectType: "spawn";
|
|
44
69
|
readonly state: S;
|
|
45
70
|
readonly timestamp: number;
|
|
@@ -47,9 +72,10 @@ interface EffectEvent<S> {
|
|
|
47
72
|
interface TaskEvent<S> {
|
|
48
73
|
readonly type: "@machine.task";
|
|
49
74
|
readonly actorId: string;
|
|
75
|
+
readonly generation: number;
|
|
50
76
|
readonly state: S;
|
|
51
77
|
readonly taskName?: string;
|
|
52
|
-
readonly phase: "start" | "success" | "failure" | "interrupt";
|
|
78
|
+
readonly phase: "start" | "success" | "failure" | "defect" | "interrupt";
|
|
53
79
|
readonly error?: string;
|
|
54
80
|
readonly timestamp: number;
|
|
55
81
|
}
|
|
@@ -59,6 +85,7 @@ interface TaskEvent<S> {
|
|
|
59
85
|
interface ErrorEvent<S, E> {
|
|
60
86
|
readonly type: "@machine.error";
|
|
61
87
|
readonly actorId: string;
|
|
88
|
+
readonly generation: number;
|
|
62
89
|
readonly phase: "transition" | "spawn";
|
|
63
90
|
readonly state: S;
|
|
64
91
|
readonly event: E;
|
|
@@ -71,13 +98,14 @@ interface ErrorEvent<S, E> {
|
|
|
71
98
|
interface StopEvent<S> {
|
|
72
99
|
readonly type: "@machine.stop";
|
|
73
100
|
readonly actorId: string;
|
|
101
|
+
readonly generation: number;
|
|
74
102
|
readonly finalState: S;
|
|
75
103
|
readonly timestamp: number;
|
|
76
104
|
}
|
|
77
105
|
/**
|
|
78
106
|
* Union of all inspection events
|
|
79
107
|
*/
|
|
80
|
-
type InspectionEvent<S, E> = SpawnEvent<S> | EventReceivedEvent<S, E> | TransitionEvent<S, E> | EffectEvent<S> | TaskEvent<S> | ErrorEvent<S, E> | StopEvent<S>;
|
|
108
|
+
type InspectionEvent<S, E> = SpawnEvent<S> | EventReceivedEvent<S, E> | TransitionEvent<S, E> | GuardEvent<S, E> | OperationEvent<S, E> | EffectEvent<S> | TaskEvent<S> | ErrorEvent<S, E> | StopEvent<S>;
|
|
81
109
|
/**
|
|
82
110
|
* Convenience alias for untyped inspection events.
|
|
83
111
|
* Useful for general-purpose inspectors that don't need specific state/event types.
|
|
@@ -148,4 +176,4 @@ declare const collectingInspector: <S extends {
|
|
|
148
176
|
readonly _tag: string;
|
|
149
177
|
}>(events: InspectionEvent<S, E>[]) => InspectorService<S, E>;
|
|
150
178
|
//#endregion
|
|
151
|
-
export { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, InspectionEvent, Inspector, InspectorHandler, InspectorService, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector };
|
|
179
|
+
export { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, GuardEvent, InspectionEvent, Inspector, InspectorHandler, InspectorService, OperationEvent, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector };
|
package/dist/inspection.js
CHANGED
|
@@ -29,6 +29,8 @@ const inspectionSpanName = (event) => {
|
|
|
29
29
|
case "@machine.spawn": return `Machine.inspect ${event.initialState._tag}`;
|
|
30
30
|
case "@machine.event": return `Machine.inspect ${event.event._tag}`;
|
|
31
31
|
case "@machine.transition": return `Machine.inspect ${event.fromState._tag}->${event.toState._tag}`;
|
|
32
|
+
case "@machine.guard": return `Machine.inspect guard:${event.guard}`;
|
|
33
|
+
case "@machine.operation": return `Machine.inspect operation:${event.operation}`;
|
|
32
34
|
case "@machine.effect": return `Machine.inspect ${event.effectType}`;
|
|
33
35
|
case "@machine.task": return `Machine.inspect task:${event.phase}`;
|
|
34
36
|
case "@machine.error": return `Machine.inspect ${event.phase}`;
|
|
@@ -40,6 +42,8 @@ const inspectionTraceName = (event) => {
|
|
|
40
42
|
case "@machine.spawn": return `machine.spawn ${event.initialState._tag}`;
|
|
41
43
|
case "@machine.event": return `machine.event ${event.event._tag}`;
|
|
42
44
|
case "@machine.transition": return `machine.transition ${event.fromState._tag}->${event.toState._tag}`;
|
|
45
|
+
case "@machine.guard": return `machine.guard ${event.guard}`;
|
|
46
|
+
case "@machine.operation": return `machine.operation ${event.operation}`;
|
|
43
47
|
case "@machine.effect": return `machine.effect ${event.effectType}`;
|
|
44
48
|
case "@machine.task": {
|
|
45
49
|
let taskSuffix = "";
|
|
@@ -53,6 +57,7 @@ const inspectionTraceName = (event) => {
|
|
|
53
57
|
const inspectionAttributes = (event) => {
|
|
54
58
|
const shared = {
|
|
55
59
|
"machine.actor.id": event.actorId,
|
|
60
|
+
"machine.actor.generation": event.generation,
|
|
56
61
|
"machine.inspection.type": event.type
|
|
57
62
|
};
|
|
58
63
|
switch (event.type) {
|
|
@@ -71,6 +76,19 @@ const inspectionAttributes = (event) => {
|
|
|
71
76
|
"machine.state.to": event.toState._tag,
|
|
72
77
|
"machine.event.tag": event.event._tag
|
|
73
78
|
};
|
|
79
|
+
case "@machine.guard": return {
|
|
80
|
+
...shared,
|
|
81
|
+
"machine.state.current": event.state._tag,
|
|
82
|
+
"machine.event.tag": event.event._tag,
|
|
83
|
+
"machine.guard.name": event.guard,
|
|
84
|
+
"machine.guard.result": event.result
|
|
85
|
+
};
|
|
86
|
+
case "@machine.operation": return {
|
|
87
|
+
...shared,
|
|
88
|
+
"machine.state.current": event.state._tag,
|
|
89
|
+
"machine.event.tag": event.event._tag,
|
|
90
|
+
"machine.operation.name": event.operation
|
|
91
|
+
};
|
|
74
92
|
case "@machine.effect": return {
|
|
75
93
|
...shared,
|
|
76
94
|
"machine.state.current": event.state._tag,
|
|
@@ -110,6 +128,7 @@ const tracingInspector = (options) => ({ onInspect: (event) => {
|
|
|
110
128
|
const currentSpan = yield* Effect.option(Effect.currentSpan);
|
|
111
129
|
if (Option.isSome(currentSpan)) currentSpan.value.event(traceName, BigInt(event.timestamp) * 1000000n, {
|
|
112
130
|
actorId: event.actorId,
|
|
131
|
+
generation: event.generation,
|
|
113
132
|
inspectionType: event.type
|
|
114
133
|
});
|
|
115
134
|
})().pipe(Effect.withSpan(spanName ?? inspectionSpanName(event), { attributes }));
|
|
@@ -123,6 +142,8 @@ const consoleInspector = () => makeInspectorEffect((event) => {
|
|
|
123
142
|
case "@machine.spawn": return Effect.log(`${prefix} spawned -> ${event.initialState._tag}`);
|
|
124
143
|
case "@machine.event": return Effect.log(`${prefix} received ${event.event._tag} in ${event.state._tag}`);
|
|
125
144
|
case "@machine.transition": return Effect.log(`${prefix} ${event.fromState._tag} -> ${event.toState._tag}`);
|
|
145
|
+
case "@machine.guard": return Effect.log(`${prefix} guard ${event.guard} -> ${String(event.result)}`);
|
|
146
|
+
case "@machine.operation": return Effect.log(`${prefix} operation ${event.operation} in ${event.state._tag}`);
|
|
126
147
|
case "@machine.effect": return Effect.log(`${prefix} ${event.effectType} effect in ${event.state._tag}`);
|
|
127
148
|
case "@machine.task": return Effect.log(`${prefix} task ${event.phase} ${event.taskName ?? "<unnamed>"} in ${event.state._tag}`);
|
|
128
149
|
case "@machine.error": return Effect.log(`${prefix} error in ${event.phase} ${event.state._tag} - ${String(event.error)}`);
|
|
@@ -2,6 +2,6 @@ import { ProcessEventHooks } from "./transition.js";
|
|
|
2
2
|
import { InspectorService } from "../inspection.js";
|
|
3
3
|
//#region src/internal/inspection.d.ts
|
|
4
4
|
/** Adapt the Inspector service to the transition kernel. */
|
|
5
|
-
declare const makeInspectionHooks: <S, E>(actorId: string, inspector: InspectorService<S, E
|
|
5
|
+
declare const makeInspectionHooks: <S, E>(actorId: string, inspector: InspectorService<S, E>, getGeneration?: () => number) => ProcessEventHooks<S, E>;
|
|
6
6
|
//#endregion
|
|
7
7
|
export { makeInspectionHooks };
|
|
@@ -11,10 +11,30 @@ const emitWithTimestamp = Effect.fn("effect-machine.emitWithTimestamp")(function
|
|
|
11
11
|
if (Effect.isEffect(result)) yield* result.pipe(Effect.ignoreCause);
|
|
12
12
|
});
|
|
13
13
|
/** Adapt the Inspector service to the transition kernel. */
|
|
14
|
-
const makeInspectionHooks = (actorId, inspector) => ({
|
|
14
|
+
const makeInspectionHooks = (actorId, inspector, getGeneration = () => 0) => ({
|
|
15
|
+
onGuard: (evaluation) => emitWithTimestamp(inspector, (timestamp) => ({
|
|
16
|
+
type: "@machine.guard",
|
|
17
|
+
actorId,
|
|
18
|
+
generation: getGeneration(),
|
|
19
|
+
state: evaluation.state,
|
|
20
|
+
event: evaluation.event,
|
|
21
|
+
guard: evaluation.guard,
|
|
22
|
+
result: evaluation.result,
|
|
23
|
+
timestamp
|
|
24
|
+
})),
|
|
25
|
+
onOperation: (operation) => emitWithTimestamp(inspector, (timestamp) => ({
|
|
26
|
+
type: "@machine.operation",
|
|
27
|
+
actorId,
|
|
28
|
+
generation: getGeneration(),
|
|
29
|
+
operation: operation.operation,
|
|
30
|
+
state: operation.state,
|
|
31
|
+
event: operation.event,
|
|
32
|
+
timestamp
|
|
33
|
+
})),
|
|
15
34
|
onSpawnEffect: (state) => emitWithTimestamp(inspector, (timestamp) => ({
|
|
16
35
|
type: "@machine.effect",
|
|
17
36
|
actorId,
|
|
37
|
+
generation: getGeneration(),
|
|
18
38
|
effectType: "spawn",
|
|
19
39
|
state,
|
|
20
40
|
timestamp
|
|
@@ -22,6 +42,7 @@ const makeInspectionHooks = (actorId, inspector) => ({
|
|
|
22
42
|
onTransition: (from, to, event) => emitWithTimestamp(inspector, (timestamp) => ({
|
|
23
43
|
type: "@machine.transition",
|
|
24
44
|
actorId,
|
|
45
|
+
generation: getGeneration(),
|
|
25
46
|
fromState: from,
|
|
26
47
|
toState: to,
|
|
27
48
|
event,
|
|
@@ -30,6 +51,7 @@ const makeInspectionHooks = (actorId, inspector) => ({
|
|
|
30
51
|
onError: (info) => emitWithTimestamp(inspector, (timestamp) => ({
|
|
31
52
|
type: "@machine.error",
|
|
32
53
|
actorId,
|
|
54
|
+
generation: getGeneration(),
|
|
33
55
|
phase: info.phase,
|
|
34
56
|
state: info.state,
|
|
35
57
|
event: info.event,
|
|
@@ -4,6 +4,7 @@ interface Transition<State, Event, R> {
|
|
|
4
4
|
readonly eventTag: string;
|
|
5
5
|
readonly handler: TransitionHandler<State, Event, State, R>;
|
|
6
6
|
readonly reenter?: boolean;
|
|
7
|
+
readonly guard?: GuardPredicate<State, Event, never>;
|
|
7
8
|
}
|
|
8
9
|
interface SpawnEffect<State, Event, R> {
|
|
9
10
|
readonly stateTag: string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/internal/machine-initialization.d.ts
|
|
2
|
+
/** How a machine creates the initial state for one actor. */
|
|
3
|
+
type MachineInitialization<Input, State> = {
|
|
4
|
+
readonly _tag: "Static";
|
|
5
|
+
readonly state: State;
|
|
6
|
+
} | {
|
|
7
|
+
readonly _tag: "Input";
|
|
8
|
+
readonly initialize: (input: Input) => State;
|
|
9
|
+
};
|
|
10
|
+
declare const make: <Input, State>(initial: State | ((input: Input) => State)) => MachineInitialization<Input, State>;
|
|
11
|
+
/** Resolve one actor initial state. This is the only input erasure seam. */
|
|
12
|
+
declare const resolve: <Input, State>(initialization: MachineInitialization<Input, State>, input: unknown) => State;
|
|
13
|
+
/** Return the static value, or undefined for an input machine. */
|
|
14
|
+
declare const staticValue: <Input, State>(initialization: MachineInitialization<Input, State>) => State | undefined;
|
|
15
|
+
declare const MachineInitialization: {
|
|
16
|
+
make: typeof make;
|
|
17
|
+
resolve: typeof resolve;
|
|
18
|
+
staticValue: typeof staticValue;
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { MachineInitialization };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/internal/machine-initialization.ts
|
|
2
|
+
const make = (initial) => {
|
|
3
|
+
if (typeof initial === "function") return {
|
|
4
|
+
_tag: "Input",
|
|
5
|
+
initialize: initial
|
|
6
|
+
};
|
|
7
|
+
return {
|
|
8
|
+
_tag: "Static",
|
|
9
|
+
state: initial
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
/** Resolve one actor initial state. This is the only input erasure seam. */
|
|
13
|
+
const resolve = (initialization, input) => {
|
|
14
|
+
if (initialization._tag === "Static") return initialization.state;
|
|
15
|
+
return initialization.initialize(input);
|
|
16
|
+
};
|
|
17
|
+
/** Return the static value, or undefined for an input machine. */
|
|
18
|
+
const staticValue = (initialization) => {
|
|
19
|
+
if (initialization._tag === "Static") return initialization.state;
|
|
20
|
+
};
|
|
21
|
+
const MachineInitialization = {
|
|
22
|
+
make,
|
|
23
|
+
resolve,
|
|
24
|
+
staticValue
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { MachineInitialization };
|
|
@@ -1 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import { DefectPhase } from "../supervision.js";
|
|
2
|
+
//#region src/internal/runtime.d.ts
|
|
3
|
+
/** Internal exit reason for one runtime generation. */
|
|
4
|
+
type RuntimeExit<S> = {
|
|
5
|
+
readonly _tag: "Final";
|
|
6
|
+
readonly state: S;
|
|
7
|
+
} | {
|
|
8
|
+
readonly _tag: "Stopped";
|
|
9
|
+
} | {
|
|
10
|
+
readonly _tag: "Defect";
|
|
11
|
+
readonly cause: Cause.Cause<unknown>;
|
|
12
|
+
readonly phase: DefectPhase;
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { RuntimeExit };
|