effect-machine 0.6.0 → 0.7.1

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 (68) hide show
  1. package/dist/actor.d.ts +2 -2
  2. package/dist/actor.js +13 -15
  3. package/dist/cluster/entity-machine.d.ts +2 -2
  4. package/dist/cluster/entity-machine.js +1 -1
  5. package/dist/cluster/to-entity.d.ts +5 -5
  6. package/dist/cluster/to-entity.js +2 -2
  7. package/dist/errors.d.ts +25 -40
  8. package/dist/errors.js +10 -10
  9. package/dist/inspection.d.ts +3 -3
  10. package/dist/inspection.js +2 -2
  11. package/dist/internal/brands.d.ts +3 -6
  12. package/dist/internal/inspection.js +5 -1
  13. package/dist/internal/transition.d.ts +2 -2
  14. package/dist/internal/transition.js +6 -6
  15. package/dist/internal/utils.js +5 -1
  16. package/dist/machine.d.ts +5 -5
  17. package/dist/machine.js +9 -5
  18. package/dist/persistence/adapter.d.ts +18 -21
  19. package/dist/persistence/adapter.js +4 -4
  20. package/dist/persistence/adapters/in-memory.js +4 -4
  21. package/dist/persistence/persistent-actor.js +9 -9
  22. package/dist/persistence/persistent-machine.d.ts +3 -3
  23. package/dist/schema.d.ts +4 -4
  24. package/dist/schema.js +2 -2
  25. package/dist/slot.d.ts +3 -3
  26. package/dist/slot.js +2 -2
  27. package/dist-v3/_virtual/_rolldown/runtime.js +18 -0
  28. package/dist-v3/actor.d.ts +291 -0
  29. package/dist-v3/actor.js +459 -0
  30. package/dist-v3/cluster/entity-machine.d.ts +90 -0
  31. package/dist-v3/cluster/entity-machine.js +80 -0
  32. package/dist-v3/cluster/index.d.ts +3 -0
  33. package/dist-v3/cluster/index.js +4 -0
  34. package/dist-v3/cluster/to-entity.d.ts +61 -0
  35. package/dist-v3/cluster/to-entity.js +53 -0
  36. package/dist-v3/errors.d.ts +27 -0
  37. package/dist-v3/errors.js +38 -0
  38. package/dist-v3/index.d.ts +13 -0
  39. package/dist-v3/index.js +14 -0
  40. package/dist-v3/inspection.d.ts +125 -0
  41. package/dist-v3/inspection.js +50 -0
  42. package/dist-v3/internal/brands.d.ts +40 -0
  43. package/dist-v3/internal/brands.js +0 -0
  44. package/dist-v3/internal/inspection.d.ts +11 -0
  45. package/dist-v3/internal/inspection.js +15 -0
  46. package/dist-v3/internal/transition.d.ts +160 -0
  47. package/dist-v3/internal/transition.js +238 -0
  48. package/dist-v3/internal/utils.d.ts +60 -0
  49. package/dist-v3/internal/utils.js +51 -0
  50. package/dist-v3/machine.d.ts +278 -0
  51. package/dist-v3/machine.js +317 -0
  52. package/dist-v3/persistence/adapter.d.ts +125 -0
  53. package/dist-v3/persistence/adapter.js +27 -0
  54. package/dist-v3/persistence/adapters/in-memory.d.ts +32 -0
  55. package/dist-v3/persistence/adapters/in-memory.js +176 -0
  56. package/dist-v3/persistence/index.d.ts +5 -0
  57. package/dist-v3/persistence/index.js +6 -0
  58. package/dist-v3/persistence/persistent-actor.d.ts +49 -0
  59. package/dist-v3/persistence/persistent-actor.js +367 -0
  60. package/dist-v3/persistence/persistent-machine.d.ts +105 -0
  61. package/dist-v3/persistence/persistent-machine.js +24 -0
  62. package/dist-v3/schema.d.ts +141 -0
  63. package/dist-v3/schema.js +165 -0
  64. package/dist-v3/slot.d.ts +130 -0
  65. package/dist-v3/slot.js +99 -0
  66. package/dist-v3/testing.d.ts +136 -0
  67. package/dist-v3/testing.js +138 -0
  68. package/package.json +29 -21
@@ -0,0 +1,238 @@
1
+ import { INTERNAL_ENTER_EVENT, isEffect } from "./utils.js";
2
+ import { BuiltMachine } from "../machine.js";
3
+ import { Cause, Effect, Exit, Scope } from "effect";
4
+
5
+ //#region src-v3/internal/transition.ts
6
+ /**
7
+ * Transition execution and indexing.
8
+ *
9
+ * Combines:
10
+ * - Transition execution logic (for event processing, simulation, test harness)
11
+ * - Event processing core (shared between actor and cluster entity)
12
+ * - O(1) indexed lookup by state/event tag
13
+ *
14
+ * @internal
15
+ */
16
+ /**
17
+ * Run a transition handler and return the new state.
18
+ * Shared logic for executing handlers with proper context.
19
+ *
20
+ * Used by:
21
+ * - executeTransition (actor event loop, testing)
22
+ * - persistent-actor replay (restore, replayTo)
23
+ *
24
+ * @internal
25
+ */
26
+ const runTransitionHandler = Effect.fn("effect-machine.runTransitionHandler")(function* (machine, transition, state, event, self, system) {
27
+ const ctx = {
28
+ state,
29
+ event,
30
+ self,
31
+ system
32
+ };
33
+ const { guards, effects } = machine._slots;
34
+ const handlerCtx = {
35
+ state,
36
+ event,
37
+ guards,
38
+ effects
39
+ };
40
+ const result = transition.handler(handlerCtx);
41
+ return isEffect(result) ? yield* result.pipe(Effect.provideService(machine.Context, ctx)) : result;
42
+ });
43
+ /**
44
+ * Execute a transition for a given state and event.
45
+ * Handles transition resolution, handler invocation, and guard/effect slot creation.
46
+ *
47
+ * Used by:
48
+ * - processEvent in actor.ts (actual actor event loop)
49
+ * - simulate in testing.ts (pure transition simulation)
50
+ * - createTestHarness.send in testing.ts (step-by-step testing)
51
+ *
52
+ * @internal
53
+ */
54
+ const executeTransition = Effect.fn("effect-machine.executeTransition")(function* (machine, currentState, event, self, system) {
55
+ const transition = resolveTransition(machine, currentState, event);
56
+ if (transition === void 0) return {
57
+ newState: currentState,
58
+ transitioned: false,
59
+ reenter: false
60
+ };
61
+ return {
62
+ newState: yield* runTransitionHandler(machine, transition, currentState, event, self, system),
63
+ transitioned: true,
64
+ reenter: transition.reenter === true
65
+ };
66
+ });
67
+ /**
68
+ * Process a single event through the machine.
69
+ *
70
+ * Handles:
71
+ * - Transition execution
72
+ * - State scope lifecycle (close old, create new)
73
+ * - Running spawn effects
74
+ *
75
+ * Optional hooks allow inspection/tracing without coupling to specific impl.
76
+ *
77
+ * @internal
78
+ */
79
+ const processEventCore = Effect.fn("effect-machine.processEventCore")(function* (machine, currentState, event, self, stateScopeRef, system, hooks) {
80
+ const result = yield* executeTransition(machine, currentState, event, self, system).pipe(Effect.catchAllCause((cause) => {
81
+ if (Cause.isInterruptedOnly(cause)) return Effect.interrupt;
82
+ const onError = hooks?.onError;
83
+ if (onError === void 0) return Effect.failCause(cause).pipe(Effect.orDie);
84
+ return onError({
85
+ phase: "transition",
86
+ state: currentState,
87
+ event,
88
+ cause
89
+ }).pipe(Effect.zipRight(Effect.failCause(cause).pipe(Effect.orDie)));
90
+ }));
91
+ if (!result.transitioned) return {
92
+ newState: currentState,
93
+ previousState: currentState,
94
+ transitioned: false,
95
+ lifecycleRan: false,
96
+ isFinal: false
97
+ };
98
+ const newState = result.newState;
99
+ const runLifecycle = newState._tag !== currentState._tag || result.reenter;
100
+ if (runLifecycle) {
101
+ yield* Scope.close(stateScopeRef.current, Exit.void);
102
+ stateScopeRef.current = yield* Scope.make();
103
+ if (hooks?.onTransition !== void 0) yield* hooks.onTransition(currentState, newState, event);
104
+ if (hooks?.onSpawnEffect !== void 0) yield* hooks.onSpawnEffect(newState);
105
+ yield* runSpawnEffects(machine, newState, { _tag: INTERNAL_ENTER_EVENT }, self, stateScopeRef.current, system, hooks?.onError);
106
+ }
107
+ return {
108
+ newState,
109
+ previousState: currentState,
110
+ transitioned: true,
111
+ lifecycleRan: runLifecycle,
112
+ isFinal: machine.finalStates.has(newState._tag)
113
+ };
114
+ });
115
+ /**
116
+ * Run spawn effects for a state (forked into state scope, auto-cancelled on state exit).
117
+ *
118
+ * @internal
119
+ */
120
+ const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (machine, state, event, self, stateScope, system, onError) {
121
+ const spawnEffects = findSpawnEffects(machine, state._tag);
122
+ const ctx = {
123
+ state,
124
+ event,
125
+ self,
126
+ system
127
+ };
128
+ const { effects: effectSlots } = machine._slots;
129
+ const reportError = onError;
130
+ for (const spawnEffect of spawnEffects) {
131
+ const effect = spawnEffect.handler({
132
+ state,
133
+ event,
134
+ self,
135
+ effects: effectSlots,
136
+ system
137
+ }).pipe(Effect.provideService(machine.Context, ctx), Effect.catchAllCause((cause) => {
138
+ if (Cause.isInterruptedOnly(cause)) return Effect.interrupt;
139
+ if (reportError === void 0) return Effect.failCause(cause).pipe(Effect.orDie);
140
+ return reportError({
141
+ phase: "spawn",
142
+ state,
143
+ event,
144
+ cause
145
+ }).pipe(Effect.zipRight(Effect.failCause(cause).pipe(Effect.orDie)));
146
+ }));
147
+ yield* Effect.forkScoped(effect).pipe(Effect.provideService(Scope.Scope, stateScope));
148
+ }
149
+ });
150
+ /**
151
+ * Resolve which transition should fire for a given state and event.
152
+ * Uses indexed O(1) lookup. First matching transition wins.
153
+ */
154
+ const resolveTransition = (machine, currentState, event) => {
155
+ return findTransitions(machine, currentState._tag, event._tag)[0];
156
+ };
157
+ const indexCache = /* @__PURE__ */ new WeakMap();
158
+ /**
159
+ * Invalidate cached index for a machine (call after mutation).
160
+ */
161
+ const invalidateIndex = (machine) => {
162
+ indexCache.delete(machine);
163
+ };
164
+ /**
165
+ * Build transition index from machine definition.
166
+ * O(n) where n = number of transitions.
167
+ */
168
+ const buildTransitionIndex = (transitions) => {
169
+ const index = /* @__PURE__ */ new Map();
170
+ for (const t of transitions) {
171
+ let stateMap = index.get(t.stateTag);
172
+ if (stateMap === void 0) {
173
+ stateMap = /* @__PURE__ */ new Map();
174
+ index.set(t.stateTag, stateMap);
175
+ }
176
+ let eventList = stateMap.get(t.eventTag);
177
+ if (eventList === void 0) {
178
+ eventList = [];
179
+ stateMap.set(t.eventTag, eventList);
180
+ }
181
+ eventList.push(t);
182
+ }
183
+ return index;
184
+ };
185
+ /**
186
+ * Build spawn index from machine definition.
187
+ */
188
+ const buildSpawnIndex = (effects) => {
189
+ const index = /* @__PURE__ */ new Map();
190
+ for (const e of effects) {
191
+ let stateList = index.get(e.stateTag);
192
+ if (stateList === void 0) {
193
+ stateList = [];
194
+ index.set(e.stateTag, stateList);
195
+ }
196
+ stateList.push(e);
197
+ }
198
+ return index;
199
+ };
200
+ /**
201
+ * Get or build index for a machine.
202
+ */
203
+ const getIndex = (machine) => {
204
+ let index = indexCache.get(machine);
205
+ if (index === void 0) {
206
+ index = {
207
+ transitions: buildTransitionIndex(machine.transitions),
208
+ spawn: buildSpawnIndex(machine.spawnEffects)
209
+ };
210
+ indexCache.set(machine, index);
211
+ }
212
+ return index;
213
+ };
214
+ /**
215
+ * Find all transitions matching a state/event pair.
216
+ * Returns empty array if no matches.
217
+ *
218
+ * Accepts both `Machine` and `BuiltMachine`.
219
+ * O(1) lookup after first access (index is lazily built).
220
+ */
221
+ const findTransitions = (input, stateTag, eventTag) => {
222
+ const index = getIndex(input instanceof BuiltMachine ? input._inner : input);
223
+ const specific = index.transitions.get(stateTag)?.get(eventTag) ?? [];
224
+ if (specific.length > 0) return specific;
225
+ return index.transitions.get("*")?.get(eventTag) ?? [];
226
+ };
227
+ /**
228
+ * Find all spawn effects for a state.
229
+ * Returns empty array if no matches.
230
+ *
231
+ * O(1) lookup after first access (index is lazily built).
232
+ */
233
+ const findSpawnEffects = (machine, stateTag) => {
234
+ return getIndex(machine).spawn.get(stateTag) ?? [];
235
+ };
236
+
237
+ //#endregion
238
+ export { executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler };
@@ -0,0 +1,60 @@
1
+ import { ActorSystem } from "../actor.js";
2
+ import { Effect } from "effect";
3
+
4
+ //#region src-v3/internal/utils.d.ts
5
+ /**
6
+ * Extracts _tag from a tagged union member
7
+ */
8
+ type TagOf<T> = T extends {
9
+ readonly _tag: infer Tag;
10
+ } ? Tag : never;
11
+ /**
12
+ * Extracts args type from a Data.taggedEnum constructor
13
+ */
14
+ type ArgsOf<C> = C extends ((args: infer A) => unknown) ? A : never;
15
+ /**
16
+ * Extracts return type from a Data.taggedEnum constructor
17
+ * @internal
18
+ */
19
+ type InstanceOf<C> = C extends ((...args: unknown[]) => infer R) ? R : never;
20
+ /**
21
+ * A tagged union constructor (from Data.taggedEnum)
22
+ */
23
+ type TaggedConstructor<T extends {
24
+ readonly _tag: string;
25
+ }> = (args: Omit<T, "_tag">) => T;
26
+ /**
27
+ * Transition handler result - either a new state or Effect producing one
28
+ */
29
+ type TransitionResult<State, R> = State | Effect.Effect<State, never, R>;
30
+ /**
31
+ * Internal event tags used for lifecycle effect contexts.
32
+ * Prefixed with $ to distinguish from user events.
33
+ * @internal
34
+ */
35
+ declare const INTERNAL_INIT_EVENT: "$init";
36
+ declare const INTERNAL_ENTER_EVENT: "$enter";
37
+ /**
38
+ * Extract _tag from a tagged value or constructor.
39
+ *
40
+ * Supports:
41
+ * - Plain values with `_tag` (MachineSchema empty structs)
42
+ * - Constructors with static `_tag` (MachineSchema non-empty structs)
43
+ * - Data.taggedEnum constructors (fallback via instantiation)
44
+ */
45
+ declare const getTag: (constructorOrValue: {
46
+ _tag: string;
47
+ } | ((...args: never[]) => {
48
+ _tag: string;
49
+ })) => string;
50
+ /** Check if a value is an Effect */
51
+ declare const isEffect: (value: unknown) => value is Effect.Effect<unknown, unknown, unknown>;
52
+ /**
53
+ * Stub ActorSystem that dies on any method call.
54
+ * Used in contexts where spawning/system access isn't supported
55
+ * (testing simulation, persistent actor replay).
56
+ * @internal
57
+ */
58
+ declare const stubSystem: ActorSystem;
59
+ //#endregion
60
+ export { ArgsOf, INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, InstanceOf, TagOf, TaggedConstructor, TransitionResult, getTag, isEffect, stubSystem };
@@ -0,0 +1,51 @@
1
+ import { Effect, Stream } from "effect";
2
+
3
+ //#region src-v3/internal/utils.ts
4
+ /**
5
+ * Internal event tags used for lifecycle effect contexts.
6
+ * Prefixed with $ to distinguish from user events.
7
+ * @internal
8
+ */
9
+ const INTERNAL_INIT_EVENT = "$init";
10
+ const INTERNAL_ENTER_EVENT = "$enter";
11
+ /**
12
+ * Extract _tag from a tagged value or constructor.
13
+ *
14
+ * Supports:
15
+ * - Plain values with `_tag` (MachineSchema empty structs)
16
+ * - Constructors with static `_tag` (MachineSchema non-empty structs)
17
+ * - Data.taggedEnum constructors (fallback via instantiation)
18
+ */
19
+ const getTag = (constructorOrValue) => {
20
+ if ("_tag" in constructorOrValue && typeof constructorOrValue._tag === "string") return constructorOrValue._tag;
21
+ try {
22
+ return constructorOrValue()._tag;
23
+ } catch {
24
+ return constructorOrValue({})._tag;
25
+ }
26
+ };
27
+ /** Check if a value is an Effect */
28
+ const isEffect = (value) => typeof value === "object" && value !== null && Effect.EffectTypeId in value;
29
+ /**
30
+ * Stub ActorSystem that dies on any method call.
31
+ * Used in contexts where spawning/system access isn't supported
32
+ * (testing simulation, persistent actor replay).
33
+ * @internal
34
+ */
35
+ const stubSystem = {
36
+ spawn: () => Effect.die("spawn not supported in stub system"),
37
+ restore: () => Effect.die("restore not supported in stub system"),
38
+ get: () => Effect.die("get not supported in stub system"),
39
+ stop: () => Effect.die("stop not supported in stub system"),
40
+ events: Stream.empty,
41
+ get actors() {
42
+ return /* @__PURE__ */ new Map();
43
+ },
44
+ subscribe: () => () => {},
45
+ listPersisted: () => Effect.die("listPersisted not supported in stub system"),
46
+ restoreMany: () => Effect.die("restoreMany not supported in stub system"),
47
+ restoreAll: () => Effect.die("restoreAll not supported in stub system")
48
+ };
49
+
50
+ //#endregion
51
+ export { INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, getTag, isEffect, stubSystem };
@@ -0,0 +1,278 @@
1
+ import { TransitionResult } from "./internal/utils.js";
2
+ import { BrandedEvent, BrandedState, TaggedOrConstructor } from "./internal/brands.js";
3
+ import { MachineEventSchema, MachineStateSchema, VariantsUnion } from "./schema.js";
4
+ import { PersistenceConfig, PersistentMachine } from "./persistence/persistent-machine.js";
5
+ import { DuplicateActorError } from "./errors.js";
6
+ import { EffectHandlers, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers, GuardSlots, GuardsDef, GuardsSchema, MachineContext } from "./slot.js";
7
+ import { findTransitions } from "./internal/transition.js";
8
+ import "./persistence/index.js";
9
+ import { ActorRef, ActorSystem } from "./actor.js";
10
+ import { Cause, Context, Effect, Schedule, Schema, Scope } from "effect";
11
+
12
+ //#region src-v3/machine.d.ts
13
+ declare namespace machine_d_exports {
14
+ export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, PersistenceConfig, PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, Transition, TransitionHandler, findTransitions, make, spawn };
15
+ }
16
+ /**
17
+ * Self reference for sending events back to the machine
18
+ */
19
+ interface MachineRef<Event> {
20
+ readonly send: (event: Event) => Effect.Effect<void>;
21
+ readonly spawn: <S2 extends {
22
+ readonly _tag: string;
23
+ }, E2 extends {
24
+ readonly _tag: string;
25
+ }, R2>(id: string, machine: BuiltMachine<S2, E2, R2>) => Effect.Effect<ActorRef<S2, E2>, DuplicateActorError, R2>;
26
+ }
27
+ /**
28
+ * Handler context passed to transition handlers
29
+ */
30
+ interface HandlerContext<State, Event, GD extends GuardsDef, ED extends EffectsDef> {
31
+ readonly state: State;
32
+ readonly event: Event;
33
+ readonly guards: GuardSlots<GD>;
34
+ readonly effects: EffectSlots<ED>;
35
+ }
36
+ /**
37
+ * Handler context passed to state effect handlers (onEnter, spawn, background)
38
+ */
39
+ interface StateHandlerContext<State, Event, ED extends EffectsDef> {
40
+ readonly state: State;
41
+ readonly event: Event;
42
+ readonly self: MachineRef<Event>;
43
+ readonly effects: EffectSlots<ED>;
44
+ readonly system: ActorSystem;
45
+ }
46
+ /**
47
+ * Transition handler function
48
+ */
49
+ type TransitionHandler<S, E, NewState, GD extends GuardsDef, ED extends EffectsDef, R> = (ctx: HandlerContext<S, E, GD, ED>) => TransitionResult<NewState, R>;
50
+ /**
51
+ * State effect handler function
52
+ */
53
+ type StateEffectHandler<S, E, ED extends EffectsDef, R> = (ctx: StateHandlerContext<S, E, ED>) => Effect.Effect<void, never, R>;
54
+ /**
55
+ * Transition definition
56
+ */
57
+ interface Transition<State, Event, GD extends GuardsDef, ED extends EffectsDef, R> {
58
+ readonly stateTag: string;
59
+ readonly eventTag: string;
60
+ readonly handler: TransitionHandler<State, Event, State, GD, ED, R>;
61
+ readonly reenter?: boolean;
62
+ }
63
+ /**
64
+ * Spawn effect - state-scoped forked effect
65
+ */
66
+ interface SpawnEffect<State, Event, ED extends EffectsDef, R> {
67
+ readonly stateTag: string;
68
+ readonly handler: StateEffectHandler<State, Event, ED, R>;
69
+ }
70
+ /**
71
+ * Background effect - runs for entire machine lifetime
72
+ */
73
+ interface BackgroundEffect<State, Event, ED extends EffectsDef, R> {
74
+ readonly handler: StateEffectHandler<State, Event, ED, R>;
75
+ }
76
+ /** Options for `persist` */
77
+ interface PersistOptions {
78
+ readonly snapshotSchedule: Schedule.Schedule<unknown, {
79
+ readonly _tag: string;
80
+ }>;
81
+ readonly journalEvents: boolean;
82
+ readonly machineType?: string;
83
+ }
84
+ type IsAny<T> = 0 extends 1 & T ? true : false;
85
+ type IsUnknown<T> = unknown extends T ? ([T] extends [unknown] ? true : false) : false;
86
+ type NormalizeR<T> = IsAny<T> extends true ? T : IsUnknown<T> extends true ? never : T;
87
+ 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> {
88
+ readonly state: MachineStateSchema<SD> & {
89
+ Type: S;
90
+ };
91
+ readonly event: MachineEventSchema<ED> & {
92
+ Type: E;
93
+ };
94
+ readonly guards?: GuardsSchema<GD>;
95
+ readonly effects?: EffectsSchema<EFD>;
96
+ readonly initial: S;
97
+ }
98
+ /** Check if a GuardsDef has any actual keys */
99
+ type HasGuardKeys<GD extends GuardsDef> = [keyof GD] extends [never] ? false : GD extends Record<string, never> ? false : true;
100
+ /** Check if an EffectsDef has any actual keys */
101
+ type HasEffectKeys<EFD extends EffectsDef> = [keyof EFD] extends [never] ? false : EFD extends Record<string, never> ? false : true;
102
+ /** Context type passed to guard/effect handlers */
103
+ type SlotContext<State, Event> = MachineContext<State, Event, MachineRef<Event>>;
104
+ /** Combined handlers for build() - guards and effects only */
105
+ 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);
106
+ /** Whether the machine has any guard or effect slots */
107
+ type HasSlots<GD extends GuardsDef, EFD extends EffectsDef> = HasGuardKeys<GD> extends true ? true : HasEffectKeys<EFD>;
108
+ /**
109
+ * A finalized machine ready for spawning.
110
+ *
111
+ * Created by calling `.build()` on a `Machine`. This is the only type
112
+ * accepted by `Machine.spawn` and `ActorSystem.spawn` (regular overload).
113
+ * Testing utilities (`simulate`, `createTestHarness`, etc.) still accept `Machine`.
114
+ */
115
+ declare class BuiltMachine<State, Event, R = never> {
116
+ /** @internal */
117
+ readonly _inner: Machine<State, Event, R, any, any, any, any>;
118
+ /** @internal */
119
+ constructor(machine: Machine<State, Event, R, any, any, any, any>);
120
+ get initial(): State;
121
+ persist(config: PersistOptions): PersistentMachine<State & {
122
+ readonly _tag: string;
123
+ }, Event & {
124
+ readonly _tag: string;
125
+ }, R>;
126
+ }
127
+ /**
128
+ * Machine definition with fluent builder API.
129
+ *
130
+ * Type parameters:
131
+ * - `State`: The state union type
132
+ * - `Event`: The event union type
133
+ * - `R`: Effect requirements
134
+ * - `_SD`: State schema definition (for compile-time validation)
135
+ * - `_ED`: Event schema definition (for compile-time validation)
136
+ * - `GD`: Guard definitions
137
+ * - `EFD`: Effect definitions
138
+ */
139
+ 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>> {
140
+ readonly initial: State;
141
+ /** @internal */
142
+ readonly _transitions: Array<Transition<State, Event, GD, EFD, R>>;
143
+ /** @internal */
144
+ readonly _spawnEffects: Array<SpawnEffect<State, Event, EFD, R>>;
145
+ /** @internal */
146
+ readonly _backgroundEffects: Array<BackgroundEffect<State, Event, EFD, R>>;
147
+ /** @internal */
148
+ readonly _finalStates: Set<string>;
149
+ /** @internal */
150
+ readonly _guardsSchema?: GuardsSchema<GD>;
151
+ /** @internal */
152
+ readonly _effectsSchema?: EffectsSchema<EFD>;
153
+ /** @internal */
154
+ readonly _guardHandlers: Map<string, (params: unknown, ctx: SlotContext<State, Event>) => boolean | Effect.Effect<boolean, never, R>>;
155
+ /** @internal */
156
+ readonly _effectHandlers: Map<string, (params: unknown, ctx: SlotContext<State, Event>) => Effect.Effect<void, never, R>>;
157
+ /** @internal */
158
+ readonly _slots: {
159
+ guards: GuardSlots<GD>;
160
+ effects: EffectSlots<EFD>;
161
+ };
162
+ readonly stateSchema?: Schema.Schema<State, unknown, never>;
163
+ readonly eventSchema?: Schema.Schema<Event, unknown, never>;
164
+ /**
165
+ * Context tag for accessing machine state/event/self in slot handlers.
166
+ * Uses shared module-level tag for all machines.
167
+ */
168
+ readonly Context: Context.Tag<MachineContext<State, Event, MachineRef<Event>>, MachineContext<State, Event, MachineRef<Event>>>;
169
+ get transitions(): ReadonlyArray<Transition<State, Event, GD, EFD, R>>;
170
+ get spawnEffects(): ReadonlyArray<SpawnEffect<State, Event, EFD, R>>;
171
+ get backgroundEffects(): ReadonlyArray<BackgroundEffect<State, Event, EFD, R>>;
172
+ get finalStates(): ReadonlySet<string>;
173
+ get guardsSchema(): GuardsSchema<GD> | undefined;
174
+ get effectsSchema(): EffectsSchema<EFD> | undefined;
175
+ /** @internal */
176
+ constructor(initial: State, stateSchema?: Schema.Schema<State, unknown, never>, eventSchema?: Schema.Schema<Event, unknown, never>, guardsSchema?: GuardsSchema<GD>, effectsSchema?: EffectsSchema<EFD>);
177
+ /** Register transition for a single state */
178
+ on<NS extends VariantsUnion<_SD> & BrandedState, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS, NE, RS, GD, EFD, never>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
179
+ /** Register transition for multiple states (handler receives union of state types) */
180
+ 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>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
181
+ /**
182
+ * Like `on()`, but forces onEnter/spawn to run even when transitioning to the same state tag.
183
+ * Use this to restart timers, re-run spawned effects, or reset state-scoped effects.
184
+ */
185
+ /** Single state */
186
+ 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>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
187
+ /** Multiple states */
188
+ 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>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
189
+ /**
190
+ * Register a wildcard transition that fires from any state when no specific transition matches.
191
+ * Specific `.on()` transitions always take priority over `.onAny()`.
192
+ */
193
+ 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>;
194
+ /** @internal */
195
+ private addTransition;
196
+ /**
197
+ * State-scoped effect that is forked on state entry and automatically cancelled on state exit.
198
+ * Use effect slots defined via `Slot.Effects` for the actual work.
199
+ *
200
+ * @example
201
+ * ```ts
202
+ * const MyEffects = Slot.Effects({
203
+ * fetchData: { url: Schema.String },
204
+ * });
205
+ *
206
+ * machine
207
+ * .spawn(State.Loading, ({ effects, state }) => effects.fetchData({ url: state.url }))
208
+ * .build({
209
+ * fetchData: ({ url }, { self }) =>
210
+ * Effect.gen(function* () {
211
+ * yield* Effect.addFinalizer(() => Effect.log("Leaving Loading"));
212
+ * const data = yield* Http.get(url);
213
+ * yield* self.send(Event.Loaded({ data }));
214
+ * }),
215
+ * });
216
+ * ```
217
+ */
218
+ 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>;
219
+ /**
220
+ * State-scoped task that runs on entry and sends success/failure events.
221
+ * Interrupts do not emit failure events.
222
+ */
223
+ task<NS extends VariantsUnion<_SD> & BrandedState, A, E1, ES extends VariantsUnion<_ED> & BrandedEvent, EF extends VariantsUnion<_ED> & BrandedEvent>(state: TaggedOrConstructor<NS>, run: (ctx: StateHandlerContext<NS, VariantsUnion<_ED> & BrandedEvent, EFD>) => Effect.Effect<A, E1, Scope.Scope>, options: {
224
+ readonly onSuccess: (value: A, ctx: StateHandlerContext<NS, VariantsUnion<_ED> & BrandedEvent, EFD>) => ES;
225
+ readonly onFailure?: (cause: Cause.Cause<E1>, ctx: StateHandlerContext<NS, VariantsUnion<_ED> & BrandedEvent, EFD>) => EF;
226
+ }): Machine<State, Event, R, _SD, _ED, GD, EFD>;
227
+ /**
228
+ * Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
229
+ * Use effect slots defined via `Slot.Effects` for the actual work.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * const MyEffects = Slot.Effects({
234
+ * heartbeat: {},
235
+ * });
236
+ *
237
+ * machine
238
+ * .background(({ effects }) => effects.heartbeat())
239
+ * .build({
240
+ * heartbeat: (_, { self }) =>
241
+ * Effect.forever(
242
+ * Effect.sleep("30 seconds").pipe(Effect.andThen(self.send(Event.Ping)))
243
+ * ),
244
+ * });
245
+ * ```
246
+ */
247
+ background(handler: StateEffectHandler<State, Event, EFD, Scope.Scope>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
248
+ final<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
249
+ /**
250
+ * Finalize the machine. Returns a `BuiltMachine` — the only type accepted by `Machine.spawn`.
251
+ *
252
+ * - Machines with slots: pass implementations as the first argument.
253
+ * - Machines without slots: call with no arguments.
254
+ */
255
+ build<R2 = never>(...args: HasSlots<GD, EFD> extends true ? [handlers: ProvideHandlers<State, Event, GD, EFD, R2>] : [handlers?: ProvideHandlers<State, Event, GD, EFD, R2>]): BuiltMachine<State, Event, R | NormalizeR<R2>>;
256
+ /** @internal Persist from raw Machine — prefer BuiltMachine.persist() */
257
+ persist(config: PersistOptions): PersistentMachine<State & {
258
+ readonly _tag: string;
259
+ }, Event & {
260
+ readonly _tag: string;
261
+ }, R>;
262
+ static make<SD extends Record<string, Schema.Struct.Fields>, ED extends Record<string, Schema.Struct.Fields>, S extends BrandedState, E extends BrandedEvent, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(config: MakeConfig<SD, ED, S, E, GD, EFD>): Machine<S, E, never, SD, ED, GD, EFD>;
263
+ }
264
+ declare const make: typeof Machine.make;
265
+ declare const spawn: {
266
+ <S extends {
267
+ readonly _tag: string;
268
+ }, E extends {
269
+ readonly _tag: string;
270
+ }, R>(machine: BuiltMachine<S, E, R>): Effect.Effect<ActorRef<S, E>, never, R>;
271
+ <S extends {
272
+ readonly _tag: string;
273
+ }, E extends {
274
+ readonly _tag: string;
275
+ }, R>(machine: BuiltMachine<S, E, R>, id: string): Effect.Effect<ActorRef<S, E>, never, R>;
276
+ };
277
+ //#endregion
278
+ export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, type PersistenceConfig, type PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, Transition, TransitionHandler, findTransitions, machine_d_exports, make, spawn };