effect-machine 0.4.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -0
- package/dist/actor.d.ts +40 -4
- package/dist/actor.js +89 -34
- package/dist/cluster/entity-machine.d.ts +2 -2
- package/dist/cluster/entity-machine.js +1 -1
- package/dist/cluster/to-entity.d.ts +5 -5
- package/dist/cluster/to-entity.js +2 -2
- package/dist/errors.d.ts +25 -40
- package/dist/errors.js +10 -10
- package/dist/index.d.ts +2 -2
- package/dist/inspection.d.ts +3 -3
- package/dist/inspection.js +2 -2
- package/dist/internal/brands.d.ts +3 -6
- package/dist/internal/inspection.js +5 -1
- package/dist/internal/transition.d.ts +2 -2
- package/dist/internal/transition.js +6 -6
- package/dist/internal/utils.js +11 -2
- package/dist/machine.d.ts +5 -5
- package/dist/machine.js +9 -5
- package/dist/persistence/adapter.d.ts +18 -21
- package/dist/persistence/adapter.js +4 -4
- package/dist/persistence/adapters/in-memory.js +4 -4
- package/dist/persistence/persistent-actor.js +23 -14
- package/dist/persistence/persistent-machine.d.ts +3 -3
- package/dist/schema.d.ts +4 -4
- package/dist/schema.js +2 -2
- package/dist/slot.d.ts +3 -3
- package/dist/slot.js +2 -2
- package/dist-v3/_virtual/_rolldown/runtime.js +18 -0
- package/dist-v3/actor.d.ts +291 -0
- package/dist-v3/actor.js +459 -0
- package/dist-v3/cluster/entity-machine.d.ts +90 -0
- package/dist-v3/cluster/entity-machine.js +80 -0
- package/dist-v3/cluster/index.d.ts +3 -0
- package/dist-v3/cluster/index.js +4 -0
- package/dist-v3/cluster/to-entity.d.ts +61 -0
- package/dist-v3/cluster/to-entity.js +53 -0
- package/dist-v3/errors.d.ts +27 -0
- package/dist-v3/errors.js +38 -0
- package/dist-v3/index.d.ts +13 -0
- package/dist-v3/index.js +14 -0
- package/dist-v3/inspection.d.ts +125 -0
- package/dist-v3/inspection.js +50 -0
- package/dist-v3/internal/brands.d.ts +40 -0
- package/dist-v3/internal/brands.js +0 -0
- package/dist-v3/internal/inspection.d.ts +11 -0
- package/dist-v3/internal/inspection.js +15 -0
- package/dist-v3/internal/transition.d.ts +160 -0
- package/dist-v3/internal/transition.js +238 -0
- package/dist-v3/internal/utils.d.ts +60 -0
- package/dist-v3/internal/utils.js +51 -0
- package/dist-v3/machine.d.ts +278 -0
- package/dist-v3/machine.js +317 -0
- package/dist-v3/persistence/adapter.d.ts +125 -0
- package/dist-v3/persistence/adapter.js +27 -0
- package/dist-v3/persistence/adapters/in-memory.d.ts +32 -0
- package/dist-v3/persistence/adapters/in-memory.js +176 -0
- package/dist-v3/persistence/index.d.ts +5 -0
- package/dist-v3/persistence/index.js +6 -0
- package/dist-v3/persistence/persistent-actor.d.ts +49 -0
- package/dist-v3/persistence/persistent-actor.js +367 -0
- package/dist-v3/persistence/persistent-machine.d.ts +105 -0
- package/dist-v3/persistence/persistent-machine.js +24 -0
- package/dist-v3/schema.d.ts +141 -0
- package/dist-v3/schema.js +165 -0
- package/dist-v3/slot.d.ts +130 -0
- package/dist-v3/slot.js +99 -0
- package/dist-v3/testing.d.ts +136 -0
- package/dist-v3/testing.js +138 -0
- package/package.json +29 -21
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Machine } from "../machine.js";
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
import { Rpc } from "@effect/rpc";
|
|
4
|
+
|
|
5
|
+
//#region src-v3/cluster/to-entity.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Options for toEntity.
|
|
8
|
+
*/
|
|
9
|
+
interface ToEntityOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Entity type name (e.g., "Order", "User")
|
|
12
|
+
*/
|
|
13
|
+
readonly type: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Default RPC protocol for entity machines.
|
|
17
|
+
*
|
|
18
|
+
* - `Send` - Send event to machine, returns new state
|
|
19
|
+
* - `GetState` - Get current state
|
|
20
|
+
*/
|
|
21
|
+
type EntityRpcs<StateSchema extends Schema.Schema.Any, EventSchema extends Schema.Schema.Any> = readonly [Rpc.Rpc<"Send", Schema.Struct<{
|
|
22
|
+
readonly event: EventSchema;
|
|
23
|
+
}>, StateSchema, typeof Schema.Never, never>, Rpc.Rpc<"GetState", typeof Schema.Void, StateSchema, typeof Schema.Never, never>];
|
|
24
|
+
/**
|
|
25
|
+
* Generate an Entity definition from a machine.
|
|
26
|
+
*
|
|
27
|
+
* Creates an Entity with a standard RPC protocol:
|
|
28
|
+
* - `Send(event)` - Process event through machine, returns new state
|
|
29
|
+
* - `GetState()` - Returns current state
|
|
30
|
+
*
|
|
31
|
+
* Schemas are read from the machine - must use `Machine.make({ state, event, initial })`.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* const OrderState = State({
|
|
36
|
+
* Pending: { orderId: Schema.String },
|
|
37
|
+
* Shipped: { trackingId: Schema.String },
|
|
38
|
+
* })
|
|
39
|
+
*
|
|
40
|
+
* const OrderEvent = Event({
|
|
41
|
+
* Ship: { trackingId: Schema.String },
|
|
42
|
+
* })
|
|
43
|
+
*
|
|
44
|
+
* const orderMachine = Machine.make({
|
|
45
|
+
* state: OrderState,
|
|
46
|
+
* event: OrderEvent,
|
|
47
|
+
* initial: OrderState.Pending({ orderId: "" }),
|
|
48
|
+
* }).pipe(
|
|
49
|
+
* Machine.on(OrderState.Pending, OrderEvent.Ship, ...),
|
|
50
|
+
* )
|
|
51
|
+
*
|
|
52
|
+
* const OrderEntity = toEntity(orderMachine, { type: "Order" })
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare const toEntity: <S extends {
|
|
56
|
+
readonly _tag: string;
|
|
57
|
+
}, E extends {
|
|
58
|
+
readonly _tag: string;
|
|
59
|
+
}, R>(machine: Machine<S, E, R, any, any, any, any>, options: ToEntityOptions) => any;
|
|
60
|
+
//#endregion
|
|
61
|
+
export { EntityRpcs, ToEntityOptions, toEntity };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { MissingSchemaError } from "../errors.js";
|
|
2
|
+
import { Entity } from "@effect/cluster";
|
|
3
|
+
import { Rpc } from "@effect/rpc";
|
|
4
|
+
|
|
5
|
+
//#region src-v3/cluster/to-entity.ts
|
|
6
|
+
/**
|
|
7
|
+
* Generate Entity definition from a machine.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Generate an Entity definition from a machine.
|
|
13
|
+
*
|
|
14
|
+
* Creates an Entity with a standard RPC protocol:
|
|
15
|
+
* - `Send(event)` - Process event through machine, returns new state
|
|
16
|
+
* - `GetState()` - Returns current state
|
|
17
|
+
*
|
|
18
|
+
* Schemas are read from the machine - must use `Machine.make({ state, event, initial })`.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const OrderState = State({
|
|
23
|
+
* Pending: { orderId: Schema.String },
|
|
24
|
+
* Shipped: { trackingId: Schema.String },
|
|
25
|
+
* })
|
|
26
|
+
*
|
|
27
|
+
* const OrderEvent = Event({
|
|
28
|
+
* Ship: { trackingId: Schema.String },
|
|
29
|
+
* })
|
|
30
|
+
*
|
|
31
|
+
* const orderMachine = Machine.make({
|
|
32
|
+
* state: OrderState,
|
|
33
|
+
* event: OrderEvent,
|
|
34
|
+
* initial: OrderState.Pending({ orderId: "" }),
|
|
35
|
+
* }).pipe(
|
|
36
|
+
* Machine.on(OrderState.Pending, OrderEvent.Ship, ...),
|
|
37
|
+
* )
|
|
38
|
+
*
|
|
39
|
+
* const OrderEntity = toEntity(orderMachine, { type: "Order" })
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
const toEntity = (machine, options) => {
|
|
43
|
+
const stateSchema = machine.stateSchema;
|
|
44
|
+
const eventSchema = machine.eventSchema;
|
|
45
|
+
if (stateSchema === void 0 || eventSchema === void 0) throw new MissingSchemaError({ operation: "toEntity" });
|
|
46
|
+
return Entity.make(options.type, [Rpc.make("Send", {
|
|
47
|
+
payload: { event: eventSchema },
|
|
48
|
+
success: stateSchema
|
|
49
|
+
}), Rpc.make("GetState", { success: stateSchema })]);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
//#endregion
|
|
53
|
+
export { toEntity };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src-v3/errors.d.ts
|
|
2
|
+
declare const DuplicateActorError_base: any;
|
|
3
|
+
/** Attempted to spawn/restore actor with ID already in use */
|
|
4
|
+
declare class DuplicateActorError extends DuplicateActorError_base {}
|
|
5
|
+
declare const UnprovidedSlotsError_base: any;
|
|
6
|
+
/** Machine has unprovided effect slots */
|
|
7
|
+
declare class UnprovidedSlotsError extends UnprovidedSlotsError_base {}
|
|
8
|
+
declare const MissingSchemaError_base: any;
|
|
9
|
+
/** Operation requires schemas attached to machine */
|
|
10
|
+
declare class MissingSchemaError extends MissingSchemaError_base {}
|
|
11
|
+
declare const InvalidSchemaError_base: any;
|
|
12
|
+
/** State/Event schema has no variants */
|
|
13
|
+
declare class InvalidSchemaError extends InvalidSchemaError_base {}
|
|
14
|
+
declare const MissingMatchHandlerError_base: any;
|
|
15
|
+
/** $match called with missing handler for tag */
|
|
16
|
+
declare class MissingMatchHandlerError extends MissingMatchHandlerError_base {}
|
|
17
|
+
declare const SlotProvisionError_base: any;
|
|
18
|
+
/** Slot handler not found at runtime (internal error) */
|
|
19
|
+
declare class SlotProvisionError extends SlotProvisionError_base {}
|
|
20
|
+
declare const ProvisionValidationError_base: any;
|
|
21
|
+
/** Machine.build() validation failed - missing or extra handlers */
|
|
22
|
+
declare class ProvisionValidationError extends ProvisionValidationError_base {}
|
|
23
|
+
declare const AssertionError_base: any;
|
|
24
|
+
/** Assertion failed in testing utilities */
|
|
25
|
+
declare class AssertionError extends AssertionError_base {}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src-v3/errors.ts
|
|
4
|
+
/**
|
|
5
|
+
* Typed error classes for effect-machine.
|
|
6
|
+
*
|
|
7
|
+
* All errors extend Schema.TaggedError for:
|
|
8
|
+
* - Type-safe catching via Effect.catchTag
|
|
9
|
+
* - Serialization support
|
|
10
|
+
* - Composable error handling
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
/** Attempted to spawn/restore actor with ID already in use */
|
|
15
|
+
var DuplicateActorError = class extends Schema.TaggedError()("DuplicateActorError", { actorId: Schema.String }) {};
|
|
16
|
+
/** Machine has unprovided effect slots */
|
|
17
|
+
var UnprovidedSlotsError = class extends Schema.TaggedError()("UnprovidedSlotsError", { slots: Schema.Array(Schema.String) }) {};
|
|
18
|
+
/** Operation requires schemas attached to machine */
|
|
19
|
+
var MissingSchemaError = class extends Schema.TaggedError()("MissingSchemaError", { operation: Schema.String }) {};
|
|
20
|
+
/** State/Event schema has no variants */
|
|
21
|
+
var InvalidSchemaError = class extends Schema.TaggedError()("InvalidSchemaError", {}) {};
|
|
22
|
+
/** $match called with missing handler for tag */
|
|
23
|
+
var MissingMatchHandlerError = class extends Schema.TaggedError()("MissingMatchHandlerError", { tag: Schema.String }) {};
|
|
24
|
+
/** Slot handler not found at runtime (internal error) */
|
|
25
|
+
var SlotProvisionError = class extends Schema.TaggedError()("SlotProvisionError", {
|
|
26
|
+
slotName: Schema.String,
|
|
27
|
+
slotType: Schema.Literal("guard", "effect")
|
|
28
|
+
}) {};
|
|
29
|
+
/** Machine.build() validation failed - missing or extra handlers */
|
|
30
|
+
var ProvisionValidationError = class extends Schema.TaggedError()("ProvisionValidationError", {
|
|
31
|
+
missing: Schema.Array(Schema.String),
|
|
32
|
+
extra: Schema.Array(Schema.String)
|
|
33
|
+
}) {};
|
|
34
|
+
/** Assertion failed in testing utilities */
|
|
35
|
+
var AssertionError = class extends Schema.TaggedError()("AssertionError", { message: Schema.String }) {};
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
export { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Event, MachineEventSchema, MachineStateSchema, State } from "./schema.js";
|
|
2
|
+
import { PersistenceConfig, PersistentMachine, isPersistentMachine } from "./persistence/persistent-machine.js";
|
|
3
|
+
import { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
|
|
4
|
+
import { EffectHandlers, EffectSlot, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers, GuardSlot, GuardSlots, GuardsDef, GuardsSchema, MachineContext, Slot } from "./slot.js";
|
|
5
|
+
import { PersistentActorRef, createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
|
|
6
|
+
import { ActorMetadata, PersistedEvent, PersistenceAdapter, PersistenceAdapterTag, PersistenceError, RestoreFailure, RestoreResult, Snapshot, VersionConflictError } from "./persistence/adapter.js";
|
|
7
|
+
import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
|
|
8
|
+
import "./persistence/index.js";
|
|
9
|
+
import { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, ProvideHandlers, SpawnEffect, StateHandlerContext, Transition, machine_d_exports } from "./machine.js";
|
|
10
|
+
import { ActorRef, ActorSystem, Default, SystemEvent, SystemEventListener } from "./actor.js";
|
|
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 };
|
package/dist-v3/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Inspector, collectingInspector, consoleInspector, makeInspector } from "./inspection.js";
|
|
2
|
+
import { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
|
|
3
|
+
import { isPersistentMachine } from "./persistence/persistent-machine.js";
|
|
4
|
+
import { Slot } from "./slot.js";
|
|
5
|
+
import { machine_exports } from "./machine.js";
|
|
6
|
+
import { PersistenceAdapterTag, PersistenceError, VersionConflictError } from "./persistence/adapter.js";
|
|
7
|
+
import { createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
|
|
8
|
+
import { ActorSystem, Default } from "./actor.js";
|
|
9
|
+
import { Event, State } from "./schema.js";
|
|
10
|
+
import { assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
|
|
11
|
+
import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
|
|
12
|
+
import "./persistence/index.js";
|
|
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 };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src-v3/inspection.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Resolve a type param: if it's a Schema, extract `.Type`; otherwise use as-is.
|
|
6
|
+
*/
|
|
7
|
+
type ResolveType<T> = T extends Schema.Schema<infer A, infer _I, infer _R> ? A : T;
|
|
8
|
+
/**
|
|
9
|
+
* Event emitted when an actor is spawned
|
|
10
|
+
*/
|
|
11
|
+
interface SpawnEvent<S> {
|
|
12
|
+
readonly type: "@machine.spawn";
|
|
13
|
+
readonly actorId: string;
|
|
14
|
+
readonly initialState: S;
|
|
15
|
+
readonly timestamp: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Event emitted when an actor receives an event
|
|
19
|
+
*/
|
|
20
|
+
interface EventReceivedEvent<S, E> {
|
|
21
|
+
readonly type: "@machine.event";
|
|
22
|
+
readonly actorId: string;
|
|
23
|
+
readonly state: S;
|
|
24
|
+
readonly event: E;
|
|
25
|
+
readonly timestamp: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Event emitted when a transition occurs
|
|
29
|
+
*/
|
|
30
|
+
interface TransitionEvent<S, E> {
|
|
31
|
+
readonly type: "@machine.transition";
|
|
32
|
+
readonly actorId: string;
|
|
33
|
+
readonly fromState: S;
|
|
34
|
+
readonly toState: S;
|
|
35
|
+
readonly event: E;
|
|
36
|
+
readonly timestamp: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Event emitted when a spawn effect runs
|
|
40
|
+
*/
|
|
41
|
+
interface EffectEvent<S> {
|
|
42
|
+
readonly type: "@machine.effect";
|
|
43
|
+
readonly actorId: string;
|
|
44
|
+
readonly effectType: "spawn";
|
|
45
|
+
readonly state: S;
|
|
46
|
+
readonly timestamp: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Event emitted when a transition handler or spawn effect fails with a defect
|
|
50
|
+
*/
|
|
51
|
+
interface ErrorEvent<S, E> {
|
|
52
|
+
readonly type: "@machine.error";
|
|
53
|
+
readonly actorId: string;
|
|
54
|
+
readonly phase: "transition" | "spawn";
|
|
55
|
+
readonly state: S;
|
|
56
|
+
readonly event: E;
|
|
57
|
+
readonly error: string;
|
|
58
|
+
readonly timestamp: number;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Event emitted when an actor stops
|
|
62
|
+
*/
|
|
63
|
+
interface StopEvent<S> {
|
|
64
|
+
readonly type: "@machine.stop";
|
|
65
|
+
readonly actorId: string;
|
|
66
|
+
readonly finalState: S;
|
|
67
|
+
readonly timestamp: number;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Union of all inspection events
|
|
71
|
+
*/
|
|
72
|
+
type InspectionEvent<S, E> = SpawnEvent<S> | EventReceivedEvent<S, E> | TransitionEvent<S, E> | EffectEvent<S> | ErrorEvent<S, E> | StopEvent<S>;
|
|
73
|
+
/**
|
|
74
|
+
* Convenience alias for untyped inspection events.
|
|
75
|
+
* Useful for general-purpose inspectors that don't need specific state/event types.
|
|
76
|
+
* State and event fields are typed as `{ readonly _tag: string }` so discriminated
|
|
77
|
+
* access to `_tag` works without casting.
|
|
78
|
+
*/
|
|
79
|
+
type AnyInspectionEvent = InspectionEvent<{
|
|
80
|
+
readonly _tag: string;
|
|
81
|
+
}, {
|
|
82
|
+
readonly _tag: string;
|
|
83
|
+
}>;
|
|
84
|
+
/**
|
|
85
|
+
* Inspector interface for observing machine behavior
|
|
86
|
+
*/
|
|
87
|
+
interface Inspector<S, E> {
|
|
88
|
+
readonly onInspect: (event: InspectionEvent<S, E>) => void;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Inspector service tag - optional service for machine introspection
|
|
92
|
+
* Uses `any` types to allow variance flexibility when providing the service
|
|
93
|
+
*/
|
|
94
|
+
declare const Inspector: any;
|
|
95
|
+
/**
|
|
96
|
+
* Create an inspector from a callback function.
|
|
97
|
+
*
|
|
98
|
+
* Type params accept either raw tagged types or Schema constructors:
|
|
99
|
+
* - `makeInspector(cb)` — defaults to `AnyInspectionEvent`
|
|
100
|
+
* - `makeInspector<MyState, MyEvent>(cb)` — explicit tagged types
|
|
101
|
+
* - `makeInspector<typeof MyState, typeof MyEvent>(cb)` — schema constructors (auto-extracts `.Type`)
|
|
102
|
+
*/
|
|
103
|
+
declare const makeInspector: <S = {
|
|
104
|
+
readonly _tag: string;
|
|
105
|
+
}, E = {
|
|
106
|
+
readonly _tag: string;
|
|
107
|
+
}>(onInspect: (event: InspectionEvent<ResolveType<S>, ResolveType<E>>) => void) => Inspector<ResolveType<S>, ResolveType<E>>;
|
|
108
|
+
/**
|
|
109
|
+
* Console inspector that logs events in a readable format
|
|
110
|
+
*/
|
|
111
|
+
declare const consoleInspector: () => Inspector<{
|
|
112
|
+
readonly _tag: string;
|
|
113
|
+
}, {
|
|
114
|
+
readonly _tag: string;
|
|
115
|
+
}>;
|
|
116
|
+
/**
|
|
117
|
+
* Collecting inspector that stores events in an array for testing
|
|
118
|
+
*/
|
|
119
|
+
declare const collectingInspector: <S extends {
|
|
120
|
+
readonly _tag: string;
|
|
121
|
+
}, E extends {
|
|
122
|
+
readonly _tag: string;
|
|
123
|
+
}>(events: InspectionEvent<S, E>[]) => Inspector<S, E>;
|
|
124
|
+
//#endregion
|
|
125
|
+
export { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, InspectionEvent, Inspector, SpawnEvent, StopEvent, TransitionEvent, collectingInspector, consoleInspector, makeInspector };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Context } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src-v3/inspection.ts
|
|
4
|
+
/**
|
|
5
|
+
* Inspector service tag - optional service for machine introspection
|
|
6
|
+
* Uses `any` types to allow variance flexibility when providing the service
|
|
7
|
+
*/
|
|
8
|
+
const Inspector = Context.GenericTag("@effect/machine/Inspector");
|
|
9
|
+
/**
|
|
10
|
+
* Create an inspector from a callback function.
|
|
11
|
+
*
|
|
12
|
+
* Type params accept either raw tagged types or Schema constructors:
|
|
13
|
+
* - `makeInspector(cb)` — defaults to `AnyInspectionEvent`
|
|
14
|
+
* - `makeInspector<MyState, MyEvent>(cb)` — explicit tagged types
|
|
15
|
+
* - `makeInspector<typeof MyState, typeof MyEvent>(cb)` — schema constructors (auto-extracts `.Type`)
|
|
16
|
+
*/
|
|
17
|
+
const makeInspector = (onInspect) => ({ onInspect });
|
|
18
|
+
/**
|
|
19
|
+
* Console inspector that logs events in a readable format
|
|
20
|
+
*/
|
|
21
|
+
const consoleInspector = () => makeInspector((event) => {
|
|
22
|
+
const prefix = `[${event.actorId}]`;
|
|
23
|
+
switch (event.type) {
|
|
24
|
+
case "@machine.spawn":
|
|
25
|
+
console.log(prefix, "spawned →", event.initialState._tag);
|
|
26
|
+
break;
|
|
27
|
+
case "@machine.event":
|
|
28
|
+
console.log(prefix, "received", event.event._tag, "in", event.state._tag);
|
|
29
|
+
break;
|
|
30
|
+
case "@machine.transition":
|
|
31
|
+
console.log(prefix, event.fromState._tag, "→", event.toState._tag);
|
|
32
|
+
break;
|
|
33
|
+
case "@machine.effect":
|
|
34
|
+
console.log(prefix, event.effectType, "effect in", event.state._tag);
|
|
35
|
+
break;
|
|
36
|
+
case "@machine.error":
|
|
37
|
+
console.log(prefix, "error in", event.phase, event.state._tag, "-", event.error);
|
|
38
|
+
break;
|
|
39
|
+
case "@machine.stop":
|
|
40
|
+
console.log(prefix, "stopped in", event.finalState._tag);
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Collecting inspector that stores events in an array for testing
|
|
46
|
+
*/
|
|
47
|
+
const collectingInspector = (events) => ({ onInspect: (event) => events.push(event) });
|
|
48
|
+
|
|
49
|
+
//#endregion
|
|
50
|
+
export { Inspector, collectingInspector, consoleInspector, makeInspector };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Brand } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src-v3/internal/brands.d.ts
|
|
4
|
+
declare const StateTypeId: unique symbol;
|
|
5
|
+
declare const EventTypeId: unique symbol;
|
|
6
|
+
type StateTypeId = typeof StateTypeId;
|
|
7
|
+
type EventTypeId = typeof EventTypeId;
|
|
8
|
+
interface StateBrand extends Brand.Brand<StateTypeId> {}
|
|
9
|
+
interface EventBrand extends Brand.Brand<EventTypeId> {}
|
|
10
|
+
type BrandedState = {
|
|
11
|
+
readonly _tag: string;
|
|
12
|
+
} & StateBrand;
|
|
13
|
+
type BrandedEvent = {
|
|
14
|
+
readonly _tag: string;
|
|
15
|
+
} & EventBrand;
|
|
16
|
+
declare const SchemaIdTypeId: unique symbol;
|
|
17
|
+
type SchemaIdTypeId = typeof SchemaIdTypeId;
|
|
18
|
+
/**
|
|
19
|
+
* Brand that captures the schema definition type D.
|
|
20
|
+
* Two schemas with identical definition shapes will have compatible brands.
|
|
21
|
+
* Different definitions = incompatible brands.
|
|
22
|
+
*/
|
|
23
|
+
interface SchemaIdBrand<_D extends Record<string, unknown>> extends Brand.Brand<SchemaIdTypeId> {}
|
|
24
|
+
/**
|
|
25
|
+
* Full state brand: combines base state brand with schema-specific brand
|
|
26
|
+
*/
|
|
27
|
+
type FullStateBrand<D extends Record<string, unknown>> = StateBrand & SchemaIdBrand<D>;
|
|
28
|
+
/**
|
|
29
|
+
* Full event brand: combines base event brand with schema-specific brand
|
|
30
|
+
*/
|
|
31
|
+
type FullEventBrand<D extends Record<string, unknown>> = EventBrand & SchemaIdBrand<D>;
|
|
32
|
+
/**
|
|
33
|
+
* Value or constructor for a tagged type.
|
|
34
|
+
* Accepts both plain values (empty structs) and constructor functions (non-empty structs).
|
|
35
|
+
*/
|
|
36
|
+
type TaggedOrConstructor<T extends {
|
|
37
|
+
readonly _tag: string;
|
|
38
|
+
}> = T | ((...args: never[]) => T);
|
|
39
|
+
//#endregion
|
|
40
|
+
export { BrandedEvent, BrandedState, EventBrand, EventTypeId, FullEventBrand, FullStateBrand, SchemaIdBrand, StateBrand, StateTypeId, TaggedOrConstructor };
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { InspectionEvent, Inspector } from "../inspection.js";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
|
|
4
|
+
//#region src-v3/internal/inspection.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Emit an inspection event with timestamp from Clock.
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
declare const emitWithTimestamp: <S, E>(inspector: Inspector<S, E> | undefined, makeEvent: (timestamp: number) => InspectionEvent<S, E>) => Effect.Effect<void, never, never>;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { emitWithTimestamp };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Clock, Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src-v3/internal/inspection.ts
|
|
4
|
+
/**
|
|
5
|
+
* Emit an inspection event with timestamp from Clock.
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
const emitWithTimestamp = Effect.fn("effect-machine.emitWithTimestamp")(function* (inspector, makeEvent) {
|
|
9
|
+
if (inspector === void 0) return;
|
|
10
|
+
const timestamp = yield* Clock.currentTimeMillis;
|
|
11
|
+
yield* Effect.try(() => inspector.onInspect(makeEvent(timestamp))).pipe(Effect.ignore);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
export { emitWithTimestamp };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { EffectsDef, GuardsDef } from "../slot.js";
|
|
2
|
+
import { BuiltMachine, Machine, MachineRef, SpawnEffect, Transition } from "../machine.js";
|
|
3
|
+
import { ActorSystem } from "../actor.js";
|
|
4
|
+
import { Cause, Effect, Scope } from "effect";
|
|
5
|
+
|
|
6
|
+
//#region src-v3/internal/transition.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Result of executing a transition.
|
|
9
|
+
*/
|
|
10
|
+
interface TransitionExecutionResult<S> {
|
|
11
|
+
/** New state after transition (or current state if no transition matched) */
|
|
12
|
+
readonly newState: S;
|
|
13
|
+
/** Whether a transition was executed */
|
|
14
|
+
readonly transitioned: boolean;
|
|
15
|
+
/** Whether reenter was specified on the transition */
|
|
16
|
+
readonly reenter: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Run a transition handler and return the new state.
|
|
20
|
+
* Shared logic for executing handlers with proper context.
|
|
21
|
+
*
|
|
22
|
+
* Used by:
|
|
23
|
+
* - executeTransition (actor event loop, testing)
|
|
24
|
+
* - persistent-actor replay (restore, replayTo)
|
|
25
|
+
*
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
declare const runTransitionHandler: <S extends {
|
|
29
|
+
readonly _tag: string;
|
|
30
|
+
}, E extends {
|
|
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, unknown>>;
|
|
33
|
+
/**
|
|
34
|
+
* Execute a transition for a given state and event.
|
|
35
|
+
* Handles transition resolution, handler invocation, and guard/effect slot creation.
|
|
36
|
+
*
|
|
37
|
+
* Used by:
|
|
38
|
+
* - processEvent in actor.ts (actual actor event loop)
|
|
39
|
+
* - simulate in testing.ts (pure transition simulation)
|
|
40
|
+
* - createTestHarness.send in testing.ts (step-by-step testing)
|
|
41
|
+
*
|
|
42
|
+
* @internal
|
|
43
|
+
*/
|
|
44
|
+
declare const executeTransition: <S extends {
|
|
45
|
+
readonly _tag: string;
|
|
46
|
+
}, E extends {
|
|
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<{
|
|
49
|
+
newState: S;
|
|
50
|
+
transitioned: boolean;
|
|
51
|
+
reenter: boolean;
|
|
52
|
+
}, never, Exclude<R, unknown>>;
|
|
53
|
+
/**
|
|
54
|
+
* Optional hooks for event processing inspection/tracing.
|
|
55
|
+
*/
|
|
56
|
+
interface ProcessEventHooks<S, E> {
|
|
57
|
+
/** Called before running spawn effects */
|
|
58
|
+
readonly onSpawnEffect?: (state: S) => Effect.Effect<void>;
|
|
59
|
+
/** Called after transition completes */
|
|
60
|
+
readonly onTransition?: (from: S, to: S, event: E) => Effect.Effect<void>;
|
|
61
|
+
/** Called when a transition handler or spawn effect fails with a defect */
|
|
62
|
+
readonly onError?: (info: ProcessEventError<S, E>) => Effect.Effect<void>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Error info for inspection hooks.
|
|
66
|
+
*/
|
|
67
|
+
interface ProcessEventError<S, E> {
|
|
68
|
+
readonly phase: "transition" | "spawn";
|
|
69
|
+
readonly state: S;
|
|
70
|
+
readonly event: E;
|
|
71
|
+
readonly cause: Cause.Cause<unknown>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Result of processing an event through the machine.
|
|
75
|
+
*/
|
|
76
|
+
interface ProcessEventResult<S> {
|
|
77
|
+
/** New state after processing */
|
|
78
|
+
readonly newState: S;
|
|
79
|
+
/** Previous state before processing */
|
|
80
|
+
readonly previousState: S;
|
|
81
|
+
/** Whether a transition occurred */
|
|
82
|
+
readonly transitioned: boolean;
|
|
83
|
+
/** Whether lifecycle effects ran (state change or reenter) */
|
|
84
|
+
readonly lifecycleRan: boolean;
|
|
85
|
+
/** Whether new state is final */
|
|
86
|
+
readonly isFinal: boolean;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Process a single event through the machine.
|
|
90
|
+
*
|
|
91
|
+
* Handles:
|
|
92
|
+
* - Transition execution
|
|
93
|
+
* - State scope lifecycle (close old, create new)
|
|
94
|
+
* - Running spawn effects
|
|
95
|
+
*
|
|
96
|
+
* Optional hooks allow inspection/tracing without coupling to specific impl.
|
|
97
|
+
*
|
|
98
|
+
* @internal
|
|
99
|
+
*/
|
|
100
|
+
declare const processEventCore: <S extends {
|
|
101
|
+
readonly _tag: string;
|
|
102
|
+
}, E extends {
|
|
103
|
+
readonly _tag: string;
|
|
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
|
+
current: Scope.CloseableScope;
|
|
106
|
+
}, system: ActorSystem, hooks?: ProcessEventHooks<S, E> | undefined) => Effect.Effect<{
|
|
107
|
+
newState: any;
|
|
108
|
+
previousState: S;
|
|
109
|
+
transitioned: boolean;
|
|
110
|
+
lifecycleRan: any;
|
|
111
|
+
isFinal: boolean;
|
|
112
|
+
}, unknown, unknown>;
|
|
113
|
+
/**
|
|
114
|
+
* Run spawn effects for a state (forked into state scope, auto-cancelled on state exit).
|
|
115
|
+
*
|
|
116
|
+
* @internal
|
|
117
|
+
*/
|
|
118
|
+
declare const runSpawnEffects: <S extends {
|
|
119
|
+
readonly _tag: string;
|
|
120
|
+
}, E extends {
|
|
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.CloseableScope, system: ActorSystem, onError?: ((info: ProcessEventError<S, E>) => Effect.Effect<void>) | undefined) => Effect.Effect<void, never, unknown>;
|
|
123
|
+
/**
|
|
124
|
+
* Resolve which transition should fire for a given state and event.
|
|
125
|
+
* Uses indexed O(1) lookup. First matching transition wins.
|
|
126
|
+
*/
|
|
127
|
+
declare const resolveTransition: <S extends {
|
|
128
|
+
readonly _tag: string;
|
|
129
|
+
}, E extends {
|
|
130
|
+
readonly _tag: string;
|
|
131
|
+
}, R>(machine: Machine<S, E, R, any, any, any, any>, currentState: S, event: E) => (typeof machine.transitions)[number] | undefined;
|
|
132
|
+
/**
|
|
133
|
+
* Invalidate cached index for a machine (call after mutation).
|
|
134
|
+
*/
|
|
135
|
+
declare const invalidateIndex: (machine: object) => void;
|
|
136
|
+
/**
|
|
137
|
+
* Find all transitions matching a state/event pair.
|
|
138
|
+
* Returns empty array if no matches.
|
|
139
|
+
*
|
|
140
|
+
* Accepts both `Machine` and `BuiltMachine`.
|
|
141
|
+
* O(1) lookup after first access (index is lazily built).
|
|
142
|
+
*/
|
|
143
|
+
declare const findTransitions: <S extends {
|
|
144
|
+
readonly _tag: string;
|
|
145
|
+
}, E extends {
|
|
146
|
+
readonly _tag: string;
|
|
147
|
+
}, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(input: Machine<S, E, R, any, any, GD, EFD> | BuiltMachine<S, E, R>, stateTag: string, eventTag: string) => ReadonlyArray<Transition<S, E, GD, EFD, R>>;
|
|
148
|
+
/**
|
|
149
|
+
* Find all spawn effects for a state.
|
|
150
|
+
* Returns empty array if no matches.
|
|
151
|
+
*
|
|
152
|
+
* O(1) lookup after first access (index is lazily built).
|
|
153
|
+
*/
|
|
154
|
+
declare const findSpawnEffects: <S extends {
|
|
155
|
+
readonly _tag: string;
|
|
156
|
+
}, E extends {
|
|
157
|
+
readonly _tag: string;
|
|
158
|
+
}, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(machine: Machine<S, E, R, any, any, GD, EFD>, stateTag: string) => ReadonlyArray<SpawnEffect<S, E, EFD, R>>;
|
|
159
|
+
//#endregion
|
|
160
|
+
export { ProcessEventError, ProcessEventHooks, ProcessEventResult, TransitionExecutionResult, executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler };
|