effect-machine 0.3.1 → 0.3.2

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.
Files changed (61) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +18 -0
  2. package/dist/actor.d.ts +251 -0
  3. package/dist/actor.js +385 -0
  4. package/dist/cluster/entity-machine.d.ts +90 -0
  5. package/dist/cluster/entity-machine.js +74 -0
  6. package/dist/cluster/index.d.ts +3 -0
  7. package/dist/cluster/index.js +4 -0
  8. package/dist/cluster/to-entity.d.ts +64 -0
  9. package/dist/cluster/to-entity.js +53 -0
  10. package/dist/errors.d.ts +61 -0
  11. package/dist/errors.js +38 -0
  12. package/dist/index.d.ts +13 -0
  13. package/dist/index.js +14 -0
  14. package/dist/inspection.d.ts +125 -0
  15. package/dist/inspection.js +50 -0
  16. package/dist/internal/brands.d.ts +40 -0
  17. package/dist/internal/brands.js +0 -0
  18. package/dist/internal/inspection.d.ts +11 -0
  19. package/dist/internal/inspection.js +15 -0
  20. package/dist/internal/transition.d.ts +159 -0
  21. package/dist/internal/transition.js +235 -0
  22. package/dist/internal/utils.d.ts +52 -0
  23. package/dist/internal/utils.js +31 -0
  24. package/dist/machine.d.ts +271 -0
  25. package/dist/machine.js +317 -0
  26. package/{src/persistence/adapter.ts → dist/persistence/adapter.d.ts} +40 -72
  27. package/dist/persistence/adapter.js +27 -0
  28. package/dist/persistence/adapters/in-memory.d.ts +32 -0
  29. package/dist/persistence/adapters/in-memory.js +176 -0
  30. package/dist/persistence/index.d.ts +5 -0
  31. package/dist/persistence/index.js +6 -0
  32. package/dist/persistence/persistent-actor.d.ts +50 -0
  33. package/dist/persistence/persistent-actor.js +348 -0
  34. package/{src/persistence/persistent-machine.ts → dist/persistence/persistent-machine.d.ts} +28 -54
  35. package/dist/persistence/persistent-machine.js +24 -0
  36. package/dist/schema.d.ts +141 -0
  37. package/dist/schema.js +165 -0
  38. package/dist/slot.d.ts +128 -0
  39. package/dist/slot.js +99 -0
  40. package/dist/testing.d.ts +142 -0
  41. package/dist/testing.js +131 -0
  42. package/package.json +18 -7
  43. package/src/actor.ts +0 -1058
  44. package/src/cluster/entity-machine.ts +0 -201
  45. package/src/cluster/index.ts +0 -43
  46. package/src/cluster/to-entity.ts +0 -99
  47. package/src/errors.ts +0 -64
  48. package/src/index.ts +0 -105
  49. package/src/inspection.ts +0 -178
  50. package/src/internal/brands.ts +0 -51
  51. package/src/internal/inspection.ts +0 -18
  52. package/src/internal/transition.ts +0 -489
  53. package/src/internal/utils.ts +0 -80
  54. package/src/machine.ts +0 -836
  55. package/src/persistence/adapters/in-memory.ts +0 -294
  56. package/src/persistence/index.ts +0 -24
  57. package/src/persistence/persistent-actor.ts +0 -791
  58. package/src/schema.ts +0 -362
  59. package/src/slot.ts +0 -281
  60. package/src/testing.ts +0 -284
  61. package/tsconfig.json +0 -65
@@ -0,0 +1,90 @@
1
+ import { EffectsDef, GuardsDef } from "../slot.js";
2
+ import { ProcessEventHooks } from "../internal/transition.js";
3
+ import { Machine } from "../machine.js";
4
+ import "../actor.js";
5
+ import { Layer } from "effect";
6
+ import { Entity } from "@effect/cluster";
7
+ import { Rpc } from "@effect/rpc";
8
+
9
+ //#region src/cluster/entity-machine.d.ts
10
+ /**
11
+ * Options for EntityMachine.layer
12
+ */
13
+ interface EntityMachineOptions<S, E> {
14
+ /**
15
+ * Initialize state from entity ID.
16
+ * Called once when entity is first activated.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * EntityMachine.layer(OrderEntity, orderMachine, {
21
+ * initializeState: (entityId) => OrderState.Pending({ orderId: entityId }),
22
+ * })
23
+ * ```
24
+ */
25
+ readonly initializeState?: (entityId: string) => S;
26
+ /**
27
+ * Optional hooks for inspection/tracing.
28
+ * Called at specific points during event processing.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * EntityMachine.layer(OrderEntity, orderMachine, {
33
+ * hooks: {
34
+ * onTransition: (from, to, event) =>
35
+ * Effect.log(`Transition: ${from._tag} -> ${to._tag}`),
36
+ * onSpawnEffect: (state) =>
37
+ * Effect.log(`Running spawn effects for ${state._tag}`),
38
+ * onError: ({ phase, state }) =>
39
+ * Effect.log(`Defect in ${phase} at ${state._tag}`),
40
+ * },
41
+ * })
42
+ * ```
43
+ */
44
+ readonly hooks?: ProcessEventHooks<S, E>;
45
+ }
46
+ /**
47
+ * Create an Entity layer that wires a machine to handle RPC calls.
48
+ *
49
+ * The layer:
50
+ * - Maintains state via Ref per entity instance
51
+ * - Resolves transitions using the indexed lookup
52
+ * - Evaluates guards in registration order
53
+ * - Runs lifecycle effects (onEnter/spawn)
54
+ * - Processes internal events from spawn effects
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * const OrderEntity = toEntity(orderMachine, {
59
+ * type: "Order",
60
+ * stateSchema: OrderState,
61
+ * eventSchema: OrderEvent,
62
+ * })
63
+ *
64
+ * const OrderEntityLayer = EntityMachine.layer(OrderEntity, orderMachine, {
65
+ * initializeState: (entityId) => OrderState.Pending({ orderId: entityId }),
66
+ * })
67
+ *
68
+ * // Use in cluster
69
+ * const program = Effect.gen(function* () {
70
+ * const client = yield* ShardingClient.client(OrderEntity)
71
+ * yield* client.Send("order-123", { event: OrderEvent.Ship({ trackingId: "abc" }) })
72
+ * })
73
+ * ```
74
+ */
75
+ declare const EntityMachine: {
76
+ /**
77
+ * Create a layer that wires a machine to an Entity.
78
+ *
79
+ * @param entity - Entity created via toEntity()
80
+ * @param machine - Machine with all effects provided
81
+ * @param options - Optional configuration (state initializer, inspection hooks)
82
+ */
83
+ layer: <S extends {
84
+ readonly _tag: string;
85
+ }, E extends {
86
+ readonly _tag: string;
87
+ }, R, GD extends GuardsDef, EFD extends EffectsDef, EntityType extends string, Rpcs extends Rpc.Any>(entity: Entity.Entity<EntityType, Rpcs>, machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, options?: EntityMachineOptions<S, E>) => Layer.Layer<never, never, R>;
88
+ };
89
+ //#endregion
90
+ export { EntityMachine, EntityMachineOptions };
@@ -0,0 +1,74 @@
1
+ import { processEventCore, runSpawnEffects } from "../internal/transition.js";
2
+ import "../actor.js";
3
+ import { Effect, Queue, Ref, Scope } from "effect";
4
+ import { Entity } from "@effect/cluster";
5
+
6
+ //#region src/cluster/entity-machine.ts
7
+ /**
8
+ * EntityMachine adapter - wires a machine to a cluster Entity layer.
9
+ *
10
+ * @module
11
+ */
12
+ /**
13
+ * Process a single event through the machine using shared core.
14
+ * Returns the new state after processing.
15
+ */
16
+ const processEvent = Effect.fn("effect-machine.cluster.processEvent")(function* (machine, stateRef, event, self, stateScopeRef, hooks) {
17
+ const result = yield* processEventCore(machine, yield* Ref.get(stateRef), event, self, stateScopeRef, hooks);
18
+ if (result.transitioned) yield* Ref.set(stateRef, result.newState);
19
+ return result.newState;
20
+ });
21
+ /**
22
+ * Create an Entity layer that wires a machine to handle RPC calls.
23
+ *
24
+ * The layer:
25
+ * - Maintains state via Ref per entity instance
26
+ * - Resolves transitions using the indexed lookup
27
+ * - Evaluates guards in registration order
28
+ * - Runs lifecycle effects (onEnter/spawn)
29
+ * - Processes internal events from spawn effects
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * const OrderEntity = toEntity(orderMachine, {
34
+ * type: "Order",
35
+ * stateSchema: OrderState,
36
+ * eventSchema: OrderEvent,
37
+ * })
38
+ *
39
+ * const OrderEntityLayer = EntityMachine.layer(OrderEntity, orderMachine, {
40
+ * initializeState: (entityId) => OrderState.Pending({ orderId: entityId }),
41
+ * })
42
+ *
43
+ * // Use in cluster
44
+ * const program = Effect.gen(function* () {
45
+ * const client = yield* ShardingClient.client(OrderEntity)
46
+ * yield* client.Send("order-123", { event: OrderEvent.Ship({ trackingId: "abc" }) })
47
+ * })
48
+ * ```
49
+ */
50
+ const EntityMachine = { layer: (entity, machine, options) => {
51
+ const layer = Effect.fn("effect-machine.cluster.layer")(function* () {
52
+ const entityId = yield* Effect.serviceOption(Entity.CurrentAddress).pipe(Effect.map((opt) => opt._tag === "Some" ? opt.value.entityId : ""));
53
+ const initialState = options?.initializeState !== void 0 ? options.initializeState(entityId) : machine.initial;
54
+ const internalQueue = yield* Queue.unbounded();
55
+ const self = { send: Effect.fn("effect-machine.cluster.self.send")(function* (event) {
56
+ yield* Queue.offer(internalQueue, event);
57
+ }) };
58
+ const stateRef = yield* Ref.make(initialState);
59
+ const stateScopeRef = { current: yield* Scope.make() };
60
+ yield* runSpawnEffects(machine, initialState, { _tag: "$init" }, self, stateScopeRef.current, options?.hooks?.onError);
61
+ const runInternalEvent = Effect.fn("effect-machine.cluster.internalEvent")(function* () {
62
+ yield* processEvent(machine, stateRef, yield* Queue.take(internalQueue), self, stateScopeRef, options?.hooks);
63
+ });
64
+ yield* Effect.forkScoped(Effect.forever(runInternalEvent()));
65
+ return entity.of({
66
+ Send: (envelope) => processEvent(machine, stateRef, envelope.payload.event, self, stateScopeRef, options?.hooks),
67
+ GetState: () => Ref.get(stateRef)
68
+ });
69
+ });
70
+ return entity.toLayer(layer());
71
+ } };
72
+
73
+ //#endregion
74
+ export { EntityMachine };
@@ -0,0 +1,3 @@
1
+ import { EntityMachine, EntityMachineOptions } from "./entity-machine.js";
2
+ import { ToEntityOptions, toEntity } from "./to-entity.js";
3
+ export { EntityMachine, type EntityMachineOptions, type ToEntityOptions, toEntity };
@@ -0,0 +1,4 @@
1
+ import { EntityMachine } from "./entity-machine.js";
2
+ import { toEntity } from "./to-entity.js";
3
+
4
+ export { EntityMachine, toEntity };
@@ -0,0 +1,64 @@
1
+ import { Machine } from "../machine.js";
2
+ import { Schema } from "effect";
3
+ import { Entity } from "@effect/cluster";
4
+ import { Rpc } from "@effect/rpc";
5
+
6
+ //#region src/cluster/to-entity.d.ts
7
+ /**
8
+ * Options for toEntity.
9
+ */
10
+ interface ToEntityOptions {
11
+ /**
12
+ * Entity type name (e.g., "Order", "User")
13
+ */
14
+ readonly type: string;
15
+ }
16
+ /**
17
+ * Default RPC protocol for entity machines.
18
+ *
19
+ * - `Send` - Send event to machine, returns new state
20
+ * - `GetState` - Get current state
21
+ */
22
+ type EntityRpcs<StateSchema extends Schema.Schema.Any, EventSchema extends Schema.Schema.Any> = readonly [Rpc.Rpc<"Send", Schema.Struct<{
23
+ readonly event: EventSchema;
24
+ }>, StateSchema, typeof Schema.Never, never>, Rpc.Rpc<"GetState", typeof Schema.Void, StateSchema, typeof Schema.Never, never>];
25
+ /**
26
+ * Generate an Entity definition from a machine.
27
+ *
28
+ * Creates an Entity with a standard RPC protocol:
29
+ * - `Send(event)` - Process event through machine, returns new state
30
+ * - `GetState()` - Returns current state
31
+ *
32
+ * Schemas are read from the machine - must use `Machine.make({ state, event, initial })`.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * const OrderState = State({
37
+ * Pending: { orderId: Schema.String },
38
+ * Shipped: { trackingId: Schema.String },
39
+ * })
40
+ *
41
+ * const OrderEvent = Event({
42
+ * Ship: { trackingId: Schema.String },
43
+ * })
44
+ *
45
+ * const orderMachine = Machine.make({
46
+ * state: OrderState,
47
+ * event: OrderEvent,
48
+ * initial: OrderState.Pending({ orderId: "" }),
49
+ * }).pipe(
50
+ * Machine.on(OrderState.Pending, OrderEvent.Ship, ...),
51
+ * )
52
+ *
53
+ * const OrderEntity = toEntity(orderMachine, { type: "Order" })
54
+ * ```
55
+ */
56
+ declare const toEntity: <S extends {
57
+ readonly _tag: string;
58
+ }, E extends {
59
+ readonly _tag: string;
60
+ }, R>(machine: Machine<S, E, R, any, any, any, any>, options: ToEntityOptions) => Entity.Entity<string, Rpc.Rpc<"Send", Schema.Struct<{
61
+ event: Schema.Schema<E, unknown, never>;
62
+ }>, Schema.Schema<S, unknown, never>, typeof Schema.Never, never> | Rpc.Rpc<"GetState", typeof Schema.Void, Schema.Schema<S, unknown, never>, typeof Schema.Never, never>>;
63
+ //#endregion
64
+ 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/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,61 @@
1
+ import { Schema } from "effect";
2
+
3
+ //#region src/errors.d.ts
4
+ declare const DuplicateActorError_base: Schema.TaggedErrorClass<DuplicateActorError, "DuplicateActorError", {
5
+ readonly _tag: Schema.tag<"DuplicateActorError">;
6
+ } & {
7
+ actorId: typeof Schema.String;
8
+ }>;
9
+ /** Attempted to spawn/restore actor with ID already in use */
10
+ declare class DuplicateActorError extends DuplicateActorError_base {}
11
+ declare const UnprovidedSlotsError_base: Schema.TaggedErrorClass<UnprovidedSlotsError, "UnprovidedSlotsError", {
12
+ readonly _tag: Schema.tag<"UnprovidedSlotsError">;
13
+ } & {
14
+ slots: Schema.Array$<typeof Schema.String>;
15
+ }>;
16
+ /** Machine has unprovided effect slots */
17
+ declare class UnprovidedSlotsError extends UnprovidedSlotsError_base {}
18
+ declare const MissingSchemaError_base: Schema.TaggedErrorClass<MissingSchemaError, "MissingSchemaError", {
19
+ readonly _tag: Schema.tag<"MissingSchemaError">;
20
+ } & {
21
+ operation: typeof Schema.String;
22
+ }>;
23
+ /** Operation requires schemas attached to machine */
24
+ declare class MissingSchemaError extends MissingSchemaError_base {}
25
+ declare const InvalidSchemaError_base: Schema.TaggedErrorClass<InvalidSchemaError, "InvalidSchemaError", {
26
+ readonly _tag: Schema.tag<"InvalidSchemaError">;
27
+ }>;
28
+ /** State/Event schema has no variants */
29
+ declare class InvalidSchemaError extends InvalidSchemaError_base {}
30
+ declare const MissingMatchHandlerError_base: Schema.TaggedErrorClass<MissingMatchHandlerError, "MissingMatchHandlerError", {
31
+ readonly _tag: Schema.tag<"MissingMatchHandlerError">;
32
+ } & {
33
+ tag: typeof Schema.String;
34
+ }>;
35
+ /** $match called with missing handler for tag */
36
+ declare class MissingMatchHandlerError extends MissingMatchHandlerError_base {}
37
+ declare const SlotProvisionError_base: Schema.TaggedErrorClass<SlotProvisionError, "SlotProvisionError", {
38
+ readonly _tag: Schema.tag<"SlotProvisionError">;
39
+ } & {
40
+ slotName: typeof Schema.String;
41
+ slotType: Schema.Literal<["guard", "effect"]>;
42
+ }>;
43
+ /** Slot handler not found at runtime (internal error) */
44
+ declare class SlotProvisionError extends SlotProvisionError_base {}
45
+ declare const ProvisionValidationError_base: Schema.TaggedErrorClass<ProvisionValidationError, "ProvisionValidationError", {
46
+ readonly _tag: Schema.tag<"ProvisionValidationError">;
47
+ } & {
48
+ missing: Schema.Array$<typeof Schema.String>;
49
+ extra: Schema.Array$<typeof Schema.String>;
50
+ }>;
51
+ /** Machine.build() validation failed - missing or extra handlers */
52
+ declare class ProvisionValidationError extends ProvisionValidationError_base {}
53
+ declare const AssertionError_base: Schema.TaggedErrorClass<AssertionError, "AssertionError", {
54
+ readonly _tag: Schema.tag<"AssertionError">;
55
+ } & {
56
+ message: typeof Schema.String;
57
+ }>;
58
+ /** Assertion failed in testing utilities */
59
+ declare class AssertionError extends AssertionError_base {}
60
+ //#endregion
61
+ export { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError };
package/dist/errors.js ADDED
@@ -0,0 +1,38 @@
1
+ import { Schema } from "effect";
2
+
3
+ //#region src/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 { EffectHandlers, EffectSlot, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers, GuardSlot, GuardSlots, GuardsDef, GuardsSchema, MachineContext, Slot } from "./slot.js";
2
+ import { Event, MachineEventSchema, MachineStateSchema, State } from "./schema.js";
3
+ import { PersistenceConfig, PersistentMachine, isPersistentMachine } from "./persistence/persistent-machine.js";
4
+ import { PersistentActorRef, createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
5
+ import { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.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 } 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 TestHarness, type TestHarnessOptions, type Transition, type TransitionEvent, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, restorePersistentActor, simulate };
package/dist/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 { Context, Schema } from "effect";
2
+
3
+ //#region src/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: Context.Tag<Inspector<any, any>, Inspector<any, 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/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/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/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 };