effect-machine 0.13.0 → 0.14.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.
Files changed (48) hide show
  1. package/README.md +14 -9
  2. package/dist/actor.d.ts +9 -6
  3. package/dist/actor.js +97 -43
  4. package/dist/cluster/entity-machine.d.ts +1 -1
  5. package/dist/cluster/entity-machine.js +1 -1
  6. package/dist/cluster/to-entity.d.ts +1 -1
  7. package/dist/errors.d.ts +9 -2
  8. package/dist/errors.js +8 -2
  9. package/dist/index.d.ts +4 -4
  10. package/dist/index.js +4 -4
  11. package/dist/internal/runtime.d.ts +2 -2
  12. package/dist/internal/runtime.js +5 -10
  13. package/dist/internal/transition.d.ts +9 -9
  14. package/dist/internal/transition.js +3 -5
  15. package/dist/machine.d.ts +122 -135
  16. package/dist/machine.js +97 -112
  17. package/dist/schema.d.ts +14 -0
  18. package/dist/schema.js +9 -0
  19. package/dist/slot.d.ts +112 -86
  20. package/dist/slot.js +92 -59
  21. package/dist/testing.d.ts +16 -16
  22. package/dist/testing.js +3 -3
  23. package/package.json +3 -3
  24. package/v3/dist/actor.d.ts +19 -12
  25. package/v3/dist/actor.js +130 -75
  26. package/v3/dist/cluster/entity-machine.d.ts +1 -1
  27. package/v3/dist/cluster/to-entity.d.ts +1 -1
  28. package/v3/dist/errors.d.ts +12 -3
  29. package/v3/dist/errors.js +10 -4
  30. package/v3/dist/index.d.ts +6 -6
  31. package/v3/dist/index.js +2 -2
  32. package/v3/dist/inspection.d.ts +3 -22
  33. package/v3/dist/inspection.js +1 -15
  34. package/v3/dist/internal/brands.d.ts +4 -8
  35. package/v3/dist/internal/inspection.js +1 -1
  36. package/v3/dist/internal/runtime.d.ts +8 -8
  37. package/v3/dist/internal/runtime.js +45 -28
  38. package/v3/dist/internal/transition.d.ts +10 -10
  39. package/v3/dist/internal/transition.js +8 -10
  40. package/v3/dist/internal/utils.js +5 -1
  41. package/v3/dist/machine.d.ts +153 -120
  42. package/v3/dist/machine.js +118 -115
  43. package/v3/dist/schema.d.ts +25 -11
  44. package/v3/dist/schema.js +18 -5
  45. package/v3/dist/slot.d.ts +112 -86
  46. package/v3/dist/slot.js +92 -59
  47. package/v3/dist/testing.d.ts +16 -16
  48. package/v3/dist/testing.js +7 -7
@@ -1,16 +1,16 @@
1
- import { Supervision } from "./supervision.js";
2
- import { ReplyResult, TransitionResult } from "./internal/utils.js";
1
+ import { DeferReplyResult, ReplyResult, TransitionResult } from "./internal/utils.js";
3
2
  import { BrandedEvent, BrandedState, ExtractReply, TaggedOrConstructor } from "./internal/brands.js";
4
3
  import { MachineEventSchema, MachineStateSchema, VariantsUnion } from "./schema.js";
5
4
  import { DuplicateActorError } from "./errors.js";
6
- import { EffectHandlers, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers, GuardSlots, GuardsDef, GuardsSchema, MachineContext } from "./slot.js";
5
+ import { MachineContext, ProvideSlots, SlotCalls, SlotsDef, SlotsSchema } from "./slot.js";
6
+ import { Supervision } from "./supervision.js";
7
7
  import { findTransitions } from "./internal/transition.js";
8
8
  import { ActorRef, ActorSystem } from "./actor.js";
9
- import { Cause, Context, Duration, Effect, Schema, Scope } from "effect";
9
+ import { Cause, Context, Duration, Effect, Option, Schema, Scope } from "effect";
10
10
 
11
11
  //#region src/machine.d.ts
12
12
  declare namespace machine_d_exports {
13
- export { BackgroundEffect, HandlerContext, Machine, MachineRef, MakeConfig, ProvideHandlers, ReplyResult, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, make, materializeMachine, replay, reply, spawn };
13
+ export { BackgroundEffect, DeferReplyResult, HandlerContext, Machine, MachineRef, MakeConfig, PersistConfig, ReplyResult, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, deferReply, findTransitions, make, materializeMachine, replay, reply, spawn };
14
14
  }
15
15
  /**
16
16
  * Self reference for sending events back to the machine
@@ -23,26 +23,31 @@ interface MachineRef<Event> {
23
23
  readonly _tag: string;
24
24
  }, E2 extends {
25
25
  readonly _tag: string;
26
- }, R2>(id: string, machine: Machine<S2, E2, R2, any, any, any, any>) => Effect.Effect<ActorRef<S2, E2>, DuplicateActorError, R2>;
26
+ }, R2>(id: string, machine: Machine<S2, E2, R2, any, any, any>) => Effect.Effect<ActorRef<S2, E2>, DuplicateActorError, R2>;
27
+ /**
28
+ * Settle a deferred reply from a spawn handler.
29
+ * Only usable when the transition handler returned `Machine.deferReply(state)`.
30
+ * Returns true if a pending reply was settled, false if none was pending.
31
+ */
32
+ readonly reply: (value: unknown) => Effect.Effect<boolean>;
27
33
  }
28
34
  /**
29
35
  * Handler context passed to transition handlers
30
36
  */
31
- interface HandlerContext<State, Event, GD extends GuardsDef, ED extends EffectsDef> {
37
+ interface HandlerContext<State, Event, SD extends SlotsDef = Record<string, never>> {
32
38
  readonly state: State;
33
39
  readonly event: Event;
34
- readonly guards: GuardSlots<GD>;
35
- readonly effects: EffectSlots<ED>;
40
+ readonly slots: SlotCalls<SD>;
36
41
  }
37
42
  /**
38
43
  * Handler context passed to state effect handlers (onEnter, spawn, background)
39
44
  */
40
- interface StateHandlerContext<State, Event, ED extends EffectsDef> {
45
+ interface StateHandlerContext<State, Event, SD extends SlotsDef = Record<string, never>> {
41
46
  readonly actorId: string;
42
47
  readonly state: State;
43
48
  readonly event: Event;
44
49
  readonly self: MachineRef<Event>;
45
- readonly effects: EffectSlots<ED>;
50
+ readonly slots: SlotCalls<SD>;
46
51
  readonly system: ActorSystem;
47
52
  }
48
53
  /**
@@ -50,38 +55,66 @@ interface StateHandlerContext<State, Event, ED extends EffectsDef> {
50
55
  * When Reply is concrete (event has a reply schema), handler must return Machine.reply().
51
56
  * When Reply is never, handler returns plain state.
52
57
  */
53
- type TransitionHandler<S, E, NewState, GD extends GuardsDef, ED extends EffectsDef, R, Reply = never> = (ctx: HandlerContext<S, E, GD, ED>) => TransitionResult<NewState, R, Reply>;
58
+ type TransitionHandler<S, E, NewState, SD extends SlotsDef, R, Reply = never> = (ctx: HandlerContext<S, E, SD>) => TransitionResult<NewState, R, Reply>;
54
59
  /**
55
60
  * State effect handler function
56
61
  */
57
- type StateEffectHandler<S, E, ED extends EffectsDef, R> = (ctx: StateHandlerContext<S, E, ED>) => Effect.Effect<void, never, R>;
62
+ type StateEffectHandler<S, E, SD extends SlotsDef, R> = (ctx: StateHandlerContext<S, E, SD>) => Effect.Effect<void, never, R>;
58
63
  /**
59
64
  * Transition definition
60
65
  */
61
- interface Transition<State, Event, GD extends GuardsDef, ED extends EffectsDef, R> {
66
+ interface Transition<State, Event, SD extends SlotsDef, R> {
62
67
  readonly stateTag: string;
63
68
  readonly eventTag: string;
64
- readonly handler: TransitionHandler<State, Event, State, GD, ED, R>;
69
+ readonly handler: TransitionHandler<State, Event, State, SD, R>;
65
70
  readonly reenter?: boolean;
66
71
  }
67
72
  /**
68
73
  * Spawn effect - state-scoped forked effect
69
74
  */
70
- interface SpawnEffect<State, Event, ED extends EffectsDef, R> {
75
+ interface SpawnEffect<State, Event, SD extends SlotsDef, R> {
71
76
  readonly stateTag: string;
72
- readonly handler: StateEffectHandler<State, Event, ED, R>;
77
+ readonly handler: StateEffectHandler<State, Event, SD, R>;
73
78
  }
74
79
  /**
75
80
  * Background effect - runs for entire machine lifetime
76
81
  */
77
- interface BackgroundEffect<State, Event, ED extends EffectsDef, R> {
78
- readonly handler: StateEffectHandler<State, Event, ED, R>;
82
+ interface BackgroundEffect<State, Event, SD extends SlotsDef, R> {
83
+ readonly handler: StateEffectHandler<State, Event, SD, R>;
79
84
  }
80
- interface TaskOptions<State, Event, ED extends EffectsDef, A, E1, ES, EF> {
81
- readonly onSuccess: (value: A, ctx: StateHandlerContext<State, Event, ED>) => ES;
82
- readonly onFailure?: (cause: Cause.Cause<E1>, ctx: StateHandlerContext<State, Event, ED>) => EF;
85
+ interface TaskOptions<State, Event, SD extends SlotsDef, A, E1, ES, EF> {
86
+ readonly onSuccess?: (value: A, ctx: StateHandlerContext<State, Event, SD>) => ES;
87
+ readonly onFailure?: (cause: Cause.Cause<E1>, ctx: StateHandlerContext<State, Event, SD>) => EF;
83
88
  readonly name?: string;
84
89
  }
90
+ /**
91
+ * Local persistence configuration for Machine.spawn.
92
+ *
93
+ * Fully-resolved callbacks — no service dependency.
94
+ * Separate from cluster EntityPersistence which uses a service-based adapter.
95
+ */
96
+ interface PersistConfig<S> {
97
+ /** Load saved state on actor start (and restart). Returns None for cold start. */
98
+ readonly load: () => Effect.Effect<Option.Option<S>>;
99
+ /** Save state after each transition. Runs inline (blocks next event). */
100
+ readonly save: (state: S) => Effect.Effect<void>;
101
+ /** Optional filter — return false to skip saving for this transition. */
102
+ readonly shouldSave?: (state: S, previousState: S) => boolean;
103
+ /**
104
+ * Called after load() returns Some(state). Inspect the restored state
105
+ * and decide how to proceed before the machine starts.
106
+ *
107
+ * Return Some(state) to use that state, or None to discard and start fresh.
108
+ * Called on both initial spawn and supervision restart.
109
+ * Receives `initial` (machine.initial) for comparison.
110
+ *
111
+ * Use cases: validate persisted state against external systems,
112
+ * migrate schema changes, downgrade to a safe state on partial corruption.
113
+ */
114
+ readonly onRestore?: (state: S, context: {
115
+ readonly initial: S;
116
+ }) => Effect.Effect<Option.Option<S>>;
117
+ }
85
118
  /**
86
119
  * Configuration for `.timeout()` — gen_statem-style state timeouts.
87
120
  *
@@ -94,25 +127,18 @@ interface TimeoutConfig<State, Event> {
94
127
  /** Event to send when the timer fires. Static or derived from current state. */
95
128
  readonly event: Event | ((state: State) => Event);
96
129
  }
97
- interface MakeConfig<SD extends Record<string, Schema.Struct.Fields>, ED extends Record<string, Schema.Struct.Fields>, S extends BrandedState, E extends BrandedEvent, GD extends GuardsDef, EFD extends EffectsDef> {
130
+ interface MakeConfig<SD extends Record<string, Schema.Struct.Fields>, ED extends Record<string, Schema.Struct.Fields>, S extends BrandedState, E extends BrandedEvent, SLD extends SlotsDef = Record<string, never>> {
98
131
  readonly state: MachineStateSchema<SD> & {
99
132
  Type: S;
100
133
  };
101
134
  readonly event: MachineEventSchema<ED> & {
102
135
  Type: E;
103
136
  };
104
- readonly guards?: GuardsSchema<GD>;
105
- readonly effects?: EffectsSchema<EFD>;
137
+ readonly slots?: SlotsSchema<SLD>;
106
138
  readonly initial: S;
139
+ /** Validate slot inputs/outputs at runtime. Default: true. Set to false for hot paths. */
140
+ readonly slotValidation?: boolean;
107
141
  }
108
- /** Check if a GuardsDef has any actual keys */
109
- type HasGuardKeys<GD extends GuardsDef> = [keyof GD] extends [never] ? false : GD extends Record<string, never> ? false : true;
110
- /** Check if an EffectsDef has any actual keys */
111
- type HasEffectKeys<EFD extends EffectsDef> = [keyof EFD] extends [never] ? false : EFD extends Record<string, never> ? false : true;
112
- /** Context type passed to guard/effect handlers */
113
- type SlotContext<State, Event> = MachineContext<State, Event, MachineRef<Event>>;
114
- /** Combined handlers for build() - guards and effects only */
115
- type ProvideHandlers<State, Event, GD extends GuardsDef, EFD extends EffectsDef, R> = (HasGuardKeys<GD> extends true ? GuardHandlers<GD, SlotContext<State, Event>, R> : object) & (HasEffectKeys<EFD> extends true ? EffectHandlers<EFD, SlotContext<State, Event>, R> : object);
116
142
  /**
117
143
  * Bind slot handlers to a machine, returning a fresh copy with handlers installed.
118
144
  * If no handlers provided and machine has no slots, returns the machine as-is.
@@ -120,7 +146,7 @@ type ProvideHandlers<State, Event, GD extends GuardsDef, EFD extends EffectsDef,
120
146
  *
121
147
  * @internal — used by spawn, replay, simulate, test harness, entity-machine
122
148
  */
123
- declare const materializeMachine: <S, E, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, any, any, GD, EFD>, handlers?: Record<string, any>) => Machine<S, E, never, any, any, GD, EFD>;
149
+ declare const materializeMachine: <S, E, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, handlers?: Record<string, any>) => Machine<S, E, never, any, any, SD>;
124
150
  /**
125
151
  * Machine definition with fluent builder API.
126
152
  *
@@ -130,17 +156,16 @@ declare const materializeMachine: <S, E, R, GD extends GuardsDef, EFD extends Ef
130
156
  * - `R`: Effect requirements
131
157
  * - `_SD`: State schema definition (for compile-time validation)
132
158
  * - `_ED`: Event schema definition (for compile-time validation)
133
- * - `GD`: Guard definitions
134
- * - `EFD`: Effect definitions
159
+ * - `SD`: Slot definitions
135
160
  */
136
- declare class Machine<State, Event, R = never, _SD extends Record<string, Schema.Struct.Fields> = Record<string, Schema.Struct.Fields>, _ED extends Record<string, Schema.Struct.Fields> = Record<string, Schema.Struct.Fields>, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>> {
161
+ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema.Struct.Fields> = Record<string, Schema.Struct.Fields>, _ED extends Record<string, Schema.Struct.Fields> = Record<string, Schema.Struct.Fields>, SD extends SlotsDef = Record<string, never>> {
137
162
  readonly initial: State;
138
163
  /** @internal */
139
- readonly _transitions: Array<Transition<State, Event, GD, EFD, R>>;
164
+ readonly _transitions: Array<Transition<State, Event, SD, R>>;
140
165
  /** @internal */
141
- readonly _spawnEffects: Array<SpawnEffect<State, Event, EFD, R>>;
166
+ readonly _spawnEffects: Array<SpawnEffect<State, Event, SD, R>>;
142
167
  /** @internal */
143
- readonly _backgroundEffects: Array<BackgroundEffect<State, Event, EFD, R>>;
168
+ readonly _backgroundEffects: Array<BackgroundEffect<State, Event, SD, R>>;
144
169
  /** @internal */
145
170
  readonly _finalStates: Set<string>;
146
171
  /** @internal */
@@ -149,20 +174,15 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
149
174
  readonly eventTag: string;
150
175
  }>;
151
176
  /** @internal */
152
- readonly _guardsSchema?: GuardsSchema<GD>;
177
+ readonly _slotsSchema?: SlotsSchema<SD>;
153
178
  /** @internal */
154
- readonly _effectsSchema?: EffectsSchema<EFD>;
179
+ readonly _slotHandlers: Map<string, (params: unknown) => unknown | Effect.Effect<unknown, never, R>>;
155
180
  /** @internal */
156
- readonly _guardHandlers: Map<string, (params: unknown, ctx: SlotContext<State, Event>) => boolean | Effect.Effect<boolean, never, R>>;
181
+ readonly _slots: SlotCalls<SD>;
157
182
  /** @internal */
158
- readonly _effectHandlers: Map<string, (params: unknown, ctx: SlotContext<State, Event>) => Effect.Effect<void, never, R>>;
159
- /** @internal */
160
- readonly _slots: {
161
- guards: GuardSlots<GD>;
162
- effects: EffectSlots<EFD>;
163
- };
164
- readonly stateSchema?: Schema.Schema<State, unknown, never>;
165
- readonly eventSchema?: Schema.Schema<Event, unknown, never>;
183
+ readonly _slotValidation: boolean;
184
+ readonly stateSchema?: Schema.Schema<State>;
185
+ readonly eventSchema?: Schema.Schema<Event>;
166
186
  /** @internal */
167
187
  readonly _replySchemas: ReadonlyMap<string, Schema.Schema.Any>;
168
188
  /**
@@ -170,70 +190,72 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
170
190
  * Uses shared module-level tag for all machines.
171
191
  */
172
192
  readonly Context: Context.Tag<MachineContext<State, Event, MachineRef<Event>>, MachineContext<State, Event, MachineRef<Event>>>;
173
- get transitions(): ReadonlyArray<Transition<State, Event, GD, EFD, R>>;
174
- get spawnEffects(): ReadonlyArray<SpawnEffect<State, Event, EFD, R>>;
175
- get backgroundEffects(): ReadonlyArray<BackgroundEffect<State, Event, EFD, R>>;
193
+ get transitions(): ReadonlyArray<Transition<State, Event, SD, R>>;
194
+ get spawnEffects(): ReadonlyArray<SpawnEffect<State, Event, SD, R>>;
195
+ get backgroundEffects(): ReadonlyArray<BackgroundEffect<State, Event, SD, R>>;
176
196
  get finalStates(): ReadonlySet<string>;
177
197
  get postponeRules(): ReadonlyArray<{
178
198
  readonly stateTag: string;
179
199
  readonly eventTag: string;
180
200
  }>;
181
- get guardsSchema(): GuardsSchema<GD> | undefined;
182
- get effectsSchema(): EffectsSchema<EFD> | undefined;
201
+ get slotsSchema(): SlotsSchema<SD> | undefined;
183
202
  get replySchemas(): ReadonlyMap<string, Schema.Schema.Any>;
184
203
  /** @internal */
185
- constructor(initial: State, stateSchema?: Schema.Schema<State, unknown, never>, eventSchema?: Schema.Schema<Event, unknown, never>, guardsSchema?: GuardsSchema<GD>, effectsSchema?: EffectsSchema<EFD>);
186
- from<NS extends VariantsUnion<_SD> & BrandedState, R1>(state: TaggedOrConstructor<NS>, build: (scope: TransitionScope<State, Event, R, _SD, _ED, GD, EFD, NS>) => R1): Machine<State, Event, R, _SD, _ED, GD, EFD>;
187
- from<NS extends ReadonlyArray<TaggedOrConstructor<VariantsUnion<_SD> & BrandedState>>, R1>(states: NS, build: (scope: TransitionScope<State, Event, R, _SD, _ED, GD, EFD, NS[number] extends TaggedOrConstructor<infer S extends VariantsUnion<_SD> & BrandedState> ? S : never>) => R1): Machine<State, Event, R, _SD, _ED, GD, EFD>;
204
+ constructor(initial: State, stateSchema?: Schema.Schema<State>, eventSchema?: Schema.Schema<Event>, slotsSchema?: SlotsSchema<SD>, slotValidation?: boolean);
205
+ from<NS extends VariantsUnion<_SD> & BrandedState, R1>(state: TaggedOrConstructor<NS>, build: (scope: TransitionScope<State, Event, R, _SD, _ED, SD, NS>) => R1): Machine<State, Event, R, _SD, _ED, SD>;
206
+ from<NS extends ReadonlyArray<TaggedOrConstructor<VariantsUnion<_SD> & BrandedState>>, R1>(states: NS, build: (scope: TransitionScope<State, Event, R, _SD, _ED, SD, NS[number] extends TaggedOrConstructor<infer S extends VariantsUnion<_SD> & BrandedState> ? S : never>) => R1): Machine<State, Event, R, _SD, _ED, SD>;
188
207
  /** @internal */
189
- scopeTransition<NS extends VariantsUnion<_SD> & BrandedState, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(states: ReadonlyArray<TaggedOrConstructor<NS>>, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS, NE, RS, GD, EFD, never, ExtractReply<NE>>, reenter: boolean): Machine<State, Event, R, _SD, _ED, GD, EFD>;
208
+ scopeTransition<NS extends VariantsUnion<_SD> & BrandedState, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(states: ReadonlyArray<TaggedOrConstructor<NS>>, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS, NE, RS, SD, never, ExtractReply<NE>>, reenter: boolean): Machine<State, Event, R, _SD, _ED, SD>;
190
209
  /** Register transition for a single state */
191
- on<NS extends VariantsUnion<_SD> & BrandedState, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS, NE, RS, GD, EFD, never, ExtractReply<NE>>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
210
+ on<NS extends VariantsUnion<_SD> & BrandedState, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS, NE, RS, SD, never, ExtractReply<NE>>): Machine<State, Event, R, _SD, _ED, SD>;
192
211
  /** Register transition for multiple states (handler receives union of state types) */
193
- on<NS extends ReadonlyArray<TaggedOrConstructor<VariantsUnion<_SD> & BrandedState>>, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(states: NS, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS[number] extends TaggedOrConstructor<infer S> ? S : never, NE, RS, GD, EFD, never, ExtractReply<NE>>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
212
+ on<NS extends ReadonlyArray<TaggedOrConstructor<VariantsUnion<_SD> & BrandedState>>, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(states: NS, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS[number] extends TaggedOrConstructor<infer S> ? S : never, NE, RS, SD, never, ExtractReply<NE>>): Machine<State, Event, R, _SD, _ED, SD>;
194
213
  /**
195
214
  * Like `on()`, but forces onEnter/spawn to run even when transitioning to the same state tag.
196
215
  * Use this to restart timers, re-run spawned effects, or reset state-scoped effects.
197
216
  */
198
217
  /** Single state */
199
- reenter<NS extends VariantsUnion<_SD> & BrandedState, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS, NE, RS, GD, EFD, never, ExtractReply<NE>>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
218
+ reenter<NS extends VariantsUnion<_SD> & BrandedState, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS, NE, RS, SD, never, ExtractReply<NE>>): Machine<State, Event, R, _SD, _ED, SD>;
200
219
  /** Multiple states */
201
- reenter<NS extends ReadonlyArray<TaggedOrConstructor<VariantsUnion<_SD> & BrandedState>>, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(states: NS, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS[number] extends TaggedOrConstructor<infer S> ? S : never, NE, RS, GD, EFD, never, ExtractReply<NE>>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
220
+ reenter<NS extends ReadonlyArray<TaggedOrConstructor<VariantsUnion<_SD> & BrandedState>>, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(states: NS, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS[number] extends TaggedOrConstructor<infer S> ? S : never, NE, RS, SD, never, ExtractReply<NE>>): Machine<State, Event, R, _SD, _ED, SD>;
202
221
  /**
203
222
  * Register a wildcard transition that fires from any state when no specific transition matches.
204
223
  * Specific `.on()` transitions always take priority over `.onAny()`.
205
224
  */
206
- onAny<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<VariantsUnion<_SD> & BrandedState, NE, RS, GD, EFD, never>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
225
+ onAny<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<VariantsUnion<_SD> & BrandedState, NE, RS, SD, never>): Machine<State, Event, R, _SD, _ED, SD>;
207
226
  /** @internal */
208
227
  private addTransition;
209
228
  /**
210
229
  * State-scoped effect that is forked on state entry and automatically cancelled on state exit.
211
- * Use effect slots defined via `Slot.Effects` for the actual work.
212
230
  *
213
231
  * @example
214
232
  * ```ts
215
- * const MyEffects = Slot.Effects({
216
- * fetchData: { url: Schema.String },
217
- * });
218
- *
219
- * machine
220
- * .spawn(State.Loading, ({ effects, state }) => effects.fetchData({ url: state.url }))
221
- * .build({
222
- * fetchData: ({ url }, { self }) =>
223
- * Effect.gen(function* () {
224
- * yield* Effect.addFinalizer(() => Effect.log("Leaving Loading"));
225
- * const data = yield* Http.get(url);
226
- * yield* self.send(Event.Loaded({ data }));
227
- * }),
228
- * });
233
+ * machine.spawn(State.Loading, ({ self, state }) =>
234
+ * Effect.gen(function* () {
235
+ * yield* Effect.addFinalizer(() => Effect.log("Leaving Loading"));
236
+ * const data = yield* Http.get(state.url);
237
+ * yield* self.send(Event.Loaded({ data }));
238
+ * }),
239
+ * );
229
240
  * ```
230
241
  */
231
- spawn<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, handler: StateEffectHandler<NS, VariantsUnion<_ED> & BrandedEvent, EFD, Scope.Scope>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
242
+ /** Single state */
243
+ spawn<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, handler: StateEffectHandler<NS, VariantsUnion<_ED> & BrandedEvent, SD, Scope.Scope>): Machine<State, Event, R, _SD, _ED, SD>;
244
+ /** Multiple states */
245
+ spawn<NS extends ReadonlyArray<TaggedOrConstructor<VariantsUnion<_SD> & BrandedState>>>(states: NS, handler: StateEffectHandler<NS[number] extends TaggedOrConstructor<infer S> ? S : never, VariantsUnion<_ED> & BrandedEvent, SD, Scope.Scope>): Machine<State, Event, R, _SD, _ED, SD>;
232
246
  /**
233
247
  * State-scoped task that runs on entry and sends success/failure events.
234
248
  * Interrupts do not emit failure events.
249
+ *
250
+ * Supports multi-state and shorthand overloads:
251
+ * - `.task(State.X, run, { onSuccess, onFailure })` — explicit mapping
252
+ * - `.task(State.X, run, { onFailure })` — shorthand when run returns Event directly
253
+ * - `.task([State.X, State.Y], run, opts)` — multi-state
235
254
  */
236
- task<NS extends VariantsUnion<_SD> & BrandedState, A, E1, ES extends VariantsUnion<_ED> & BrandedEvent, EF extends VariantsUnion<_ED> & BrandedEvent>(state: TaggedOrConstructor<NS>, run: (ctx: StateHandlerContext<NS, VariantsUnion<_ED> & BrandedEvent, EFD>) => Effect.Effect<A, E1, Scope.Scope>, options: TaskOptions<NS, VariantsUnion<_ED> & BrandedEvent, EFD, A, E1, ES, EF>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
255
+ /** Single state onSuccess optional (defaults to identity when task returns Event) */
256
+ task<NS extends VariantsUnion<_SD> & BrandedState, A, E1, ES extends VariantsUnion<_ED> & BrandedEvent, EF extends VariantsUnion<_ED> & BrandedEvent>(state: TaggedOrConstructor<NS>, run: (ctx: StateHandlerContext<NS, VariantsUnion<_ED> & BrandedEvent, SD>) => Effect.Effect<A, E1, Scope.Scope>, options: TaskOptions<NS, VariantsUnion<_ED> & BrandedEvent, SD, A, E1, ES, EF>): Machine<State, Event, R, _SD, _ED, SD>;
257
+ /** Multiple states — onSuccess optional */
258
+ task<NS extends ReadonlyArray<TaggedOrConstructor<VariantsUnion<_SD> & BrandedState>>, A, E1, ES extends VariantsUnion<_ED> & BrandedEvent, EF extends VariantsUnion<_ED> & BrandedEvent>(states: NS, run: (ctx: StateHandlerContext<NS[number] extends TaggedOrConstructor<infer S> ? S : never, VariantsUnion<_ED> & BrandedEvent, SD>) => Effect.Effect<A, E1, Scope.Scope>, options: TaskOptions<NS[number] extends TaggedOrConstructor<infer S> ? S : never, VariantsUnion<_ED> & BrandedEvent, SD, A, E1, ES, EF>): Machine<State, Event, R, _SD, _ED, SD>;
237
259
  /**
238
260
  * State timeout — gen_statem's `state_timeout`.
239
261
  *
@@ -255,28 +277,20 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
255
277
  * })
256
278
  * ```
257
279
  */
258
- timeout<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, config: TimeoutConfig<NS, VariantsUnion<_ED> & BrandedEvent>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
280
+ timeout<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, config: TimeoutConfig<NS, VariantsUnion<_ED> & BrandedEvent>): Machine<State, Event, R, _SD, _ED, SD>;
259
281
  /**
260
282
  * Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
261
- * Use effect slots defined via `Slot.Effects` for the actual work.
262
283
  *
263
284
  * @example
264
285
  * ```ts
265
- * const MyEffects = Slot.Effects({
266
- * heartbeat: {},
267
- * });
268
- *
269
- * machine
270
- * .background(({ effects }) => effects.heartbeat())
271
- * .build({
272
- * heartbeat: (_, { self }) =>
273
- * Effect.forever(
274
- * Effect.sleep("30 seconds").pipe(Effect.andThen(self.send(Event.Ping)))
275
- * ),
276
- * });
286
+ * machine.background(({ self }) =>
287
+ * Effect.forever(
288
+ * Effect.sleep("30 seconds").pipe(Effect.andThen(self.send(Event.Ping))),
289
+ * ),
290
+ * );
277
291
  * ```
278
292
  */
279
- background(handler: StateEffectHandler<State, Event, EFD, Scope.Scope>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
293
+ background(handler: StateEffectHandler<State, Event, SD, Scope.Scope>): Machine<State, Event, R, _SD, _ED, SD>;
280
294
  /**
281
295
  * Postpone events — gen_statem's event postpone.
282
296
  *
@@ -294,45 +308,64 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
294
308
  * .postpone(State.Connecting, [Event.Data, Event.Cmd]) // multiple events
295
309
  * ```
296
310
  */
297
- postpone<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, events: TaggedOrConstructor<VariantsUnion<_ED> & BrandedEvent> | ReadonlyArray<TaggedOrConstructor<VariantsUnion<_ED> & BrandedEvent>>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
298
- final<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
299
- static make<SD extends Record<string, Schema.Struct.Fields>, ED extends Record<string, Schema.Struct.Fields>, S extends BrandedState, E extends BrandedEvent, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(config: MakeConfig<SD, ED, S, E, GD, EFD>): Machine<S, E, never, SD, ED, GD, EFD>;
311
+ postpone<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, events: TaggedOrConstructor<VariantsUnion<_ED> & BrandedEvent> | ReadonlyArray<TaggedOrConstructor<VariantsUnion<_ED> & BrandedEvent>>): Machine<State, Event, R, _SD, _ED, SD>;
312
+ final<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>): Machine<State, Event, R, _SD, _ED, SD>;
313
+ static make<SD extends Record<string, Schema.Struct.Fields>, ED extends Record<string, Schema.Struct.Fields>, S extends BrandedState, E extends BrandedEvent, SLD extends SlotsDef = Record<string, never>>(config: MakeConfig<SD, ED, S, E, SLD>): Machine<S, E, never, SD, ED, SLD>;
300
314
  }
301
- declare class TransitionScope<State, Event, R, _SD extends Record<string, Schema.Struct.Fields>, _ED extends Record<string, Schema.Struct.Fields>, GD extends GuardsDef, EFD extends EffectsDef, SelectedState extends VariantsUnion<_SD> & BrandedState> {
315
+ declare class TransitionScope<State, Event, R, _SD extends Record<string, Schema.Struct.Fields>, _ED extends Record<string, Schema.Struct.Fields>, SD extends SlotsDef, SelectedState extends VariantsUnion<_SD> & BrandedState> {
302
316
  private readonly machine;
303
317
  private readonly states;
304
- constructor(machine: Machine<State, Event, R, _SD, _ED, GD, EFD>, states: ReadonlyArray<TaggedOrConstructor<SelectedState>>);
305
- on<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<SelectedState, NE, RS, GD, EFD, never, ExtractReply<NE>>): TransitionScope<State, Event, R, _SD, _ED, GD, EFD, SelectedState>;
306
- reenter<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<SelectedState, NE, RS, GD, EFD, never, ExtractReply<NE>>): TransitionScope<State, Event, R, _SD, _ED, GD, EFD, SelectedState>;
318
+ constructor(machine: Machine<State, Event, R, _SD, _ED, SD>, states: ReadonlyArray<TaggedOrConstructor<SelectedState>>);
319
+ on<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<SelectedState, NE, RS, SD, never, ExtractReply<NE>>): TransitionScope<State, Event, R, _SD, _ED, SD, SelectedState>;
320
+ reenter<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<SelectedState, NE, RS, SD, never, ExtractReply<NE>>): TransitionScope<State, Event, R, _SD, _ED, SD, SelectedState>;
307
321
  }
308
322
  declare const make: typeof Machine.make;
309
- type AnyMachine<S, E, R> = Machine<S, E, R, any, any, any, any>;
310
323
  /**
311
324
  * Spawn an actor from a machine.
312
325
  *
313
- * Options:
314
- * - `id` — custom actor ID (default: random)
315
- * - `hydrate` — restore from a previously-saved state snapshot.
316
- * - `slots` — slot handler implementations for slotful machines.
326
+ * For machines with slots, pass implementations via `{ slots: { ... } }`.
327
+ *
328
+ * @example
329
+ * ```ts
330
+ * // No slots
331
+ * const actor = yield* Machine.spawn(machine);
332
+ *
333
+ * // With slots
334
+ * const actor = yield* Machine.spawn(machine, {
335
+ * slots: { canRetry: ({ max }) => attempts < max },
336
+ * });
337
+ *
338
+ * // With persistence
339
+ * const actor = yield* Machine.spawn(machine, {
340
+ * persist: {
341
+ * load: () => storage.get("actor-state"),
342
+ * save: (state) => storage.set("actor-state", state),
343
+ * },
344
+ * });
345
+ * ```
317
346
  */
318
347
  declare const spawn: <S extends {
319
348
  readonly _tag: string;
320
349
  }, E extends {
321
350
  readonly _tag: string;
322
- }, R>(machine: AnyMachine<S, E, R>, options?: string | {
351
+ }, R, SD extends SlotsDef = Record<string, never>>(machine: Machine<S, E, R, any, any, SD>, options?: string | {
323
352
  id?: string;
324
353
  hydrate?: S;
325
- slots?: Record<string, any>;
354
+ slots?: ProvideSlots<SD, any>;
326
355
  supervision?: Supervision.Policy;
356
+ persist?: PersistConfig<S>;
327
357
  }) => Effect.Effect<ActorRef<S, E>, never, R>;
328
- declare const replay: <S extends {
329
- readonly _tag: string;
330
- }, E extends {
331
- readonly _tag: string;
332
- }, R>(machine: AnyMachine<S, E, R>, events: ReadonlyArray<E>, options?: {
333
- from?: S;
334
- slots?: Record<string, any>;
335
- }) => Effect.Effect<S, never, R>;
358
+ declare const replay: {
359
+ <S extends {
360
+ readonly _tag: string;
361
+ }, E extends {
362
+ readonly _tag: string;
363
+ }, R, SD extends SlotsDef = Record<string, never>>(machine: Machine<S, E, R, any, any, SD>, events: ReadonlyArray<E>, options?: {
364
+ from?: S;
365
+ slots?: ProvideSlots<SD, any>;
366
+ }): Effect.Effect<S, never, R>;
367
+ };
336
368
  declare const reply: <State, Reply>(state: State, reply: Reply) => ReplyResult<State, Reply>;
369
+ declare const deferReply: <State>(state: State) => DeferReplyResult<State>;
337
370
  //#endregion
338
- export { BackgroundEffect, HandlerContext, Machine, MachineRef, MakeConfig, ProvideHandlers, type ReplyResult, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, machine_d_exports, make, materializeMachine, replay, reply, spawn };
371
+ export { BackgroundEffect, type DeferReplyResult, HandlerContext, Machine, MachineRef, MakeConfig, PersistConfig, type ReplyResult, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, deferReply, findTransitions, machine_d_exports, make, materializeMachine, replay, reply, spawn };