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,271 @@
1
+ import { EffectHandlers, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers, GuardSlots, GuardsDef, GuardsSchema, MachineContext } from "./slot.js";
2
+ import { TransitionResult } from "./internal/utils.js";
3
+ import { BrandedEvent, BrandedState, TaggedOrConstructor } from "./internal/brands.js";
4
+ import { MachineEventSchema, MachineStateSchema, VariantsUnion } from "./schema.js";
5
+ import { PersistenceConfig, PersistentMachine } from "./persistence/persistent-machine.js";
6
+ import { findTransitions } from "./internal/transition.js";
7
+ import "./persistence/index.js";
8
+ import { ActorRef } from "./actor.js";
9
+ import { Cause, Context, Effect, Schedule, Schema, Scope } from "effect";
10
+
11
+ //#region src/machine.d.ts
12
+ declare namespace machine_d_exports {
13
+ export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, PersistenceConfig, PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, Transition, TransitionHandler, findTransitions, make, spawn };
14
+ }
15
+ /**
16
+ * Self reference for sending events back to the machine
17
+ */
18
+ interface MachineRef<Event> {
19
+ readonly send: (event: Event) => Effect.Effect<void>;
20
+ }
21
+ /**
22
+ * Handler context passed to transition handlers
23
+ */
24
+ interface HandlerContext<State, Event, GD extends GuardsDef, ED extends EffectsDef> {
25
+ readonly state: State;
26
+ readonly event: Event;
27
+ readonly guards: GuardSlots<GD>;
28
+ readonly effects: EffectSlots<ED>;
29
+ }
30
+ /**
31
+ * Handler context passed to state effect handlers (onEnter, spawn, background)
32
+ */
33
+ interface StateHandlerContext<State, Event, ED extends EffectsDef> {
34
+ readonly state: State;
35
+ readonly event: Event;
36
+ readonly self: MachineRef<Event>;
37
+ readonly effects: EffectSlots<ED>;
38
+ }
39
+ /**
40
+ * Transition handler function
41
+ */
42
+ type TransitionHandler<S, E, NewState, GD extends GuardsDef, ED extends EffectsDef, R> = (ctx: HandlerContext<S, E, GD, ED>) => TransitionResult<NewState, R>;
43
+ /**
44
+ * State effect handler function
45
+ */
46
+ type StateEffectHandler<S, E, ED extends EffectsDef, R> = (ctx: StateHandlerContext<S, E, ED>) => Effect.Effect<void, never, R>;
47
+ /**
48
+ * Transition definition
49
+ */
50
+ interface Transition<State, Event, GD extends GuardsDef, ED extends EffectsDef, R> {
51
+ readonly stateTag: string;
52
+ readonly eventTag: string;
53
+ readonly handler: TransitionHandler<State, Event, State, GD, ED, R>;
54
+ readonly reenter?: boolean;
55
+ }
56
+ /**
57
+ * Spawn effect - state-scoped forked effect
58
+ */
59
+ interface SpawnEffect<State, Event, ED extends EffectsDef, R> {
60
+ readonly stateTag: string;
61
+ readonly handler: StateEffectHandler<State, Event, ED, R>;
62
+ }
63
+ /**
64
+ * Background effect - runs for entire machine lifetime
65
+ */
66
+ interface BackgroundEffect<State, Event, ED extends EffectsDef, R> {
67
+ readonly handler: StateEffectHandler<State, Event, ED, R>;
68
+ }
69
+ /** Options for `persist` */
70
+ interface PersistOptions {
71
+ readonly snapshotSchedule: Schedule.Schedule<unknown, {
72
+ readonly _tag: string;
73
+ }>;
74
+ readonly journalEvents: boolean;
75
+ readonly machineType?: string;
76
+ }
77
+ type IsAny<T> = 0 extends 1 & T ? true : false;
78
+ type IsUnknown<T> = unknown extends T ? ([T] extends [unknown] ? true : false) : false;
79
+ type NormalizeR<T> = IsAny<T> extends true ? T : IsUnknown<T> extends true ? never : T;
80
+ 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> {
81
+ readonly state: MachineStateSchema<SD> & {
82
+ Type: S;
83
+ };
84
+ readonly event: MachineEventSchema<ED> & {
85
+ Type: E;
86
+ };
87
+ readonly guards?: GuardsSchema<GD>;
88
+ readonly effects?: EffectsSchema<EFD>;
89
+ readonly initial: S;
90
+ }
91
+ /** Check if a GuardsDef has any actual keys */
92
+ type HasGuardKeys<GD extends GuardsDef> = [keyof GD] extends [never] ? false : GD extends Record<string, never> ? false : true;
93
+ /** Check if an EffectsDef has any actual keys */
94
+ type HasEffectKeys<EFD extends EffectsDef> = [keyof EFD] extends [never] ? false : EFD extends Record<string, never> ? false : true;
95
+ /** Context type passed to guard/effect handlers */
96
+ type SlotContext<State, Event> = MachineContext<State, Event, MachineRef<Event>>;
97
+ /** Combined handlers for build() - guards and effects only */
98
+ 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);
99
+ /** Whether the machine has any guard or effect slots */
100
+ type HasSlots<GD extends GuardsDef, EFD extends EffectsDef> = HasGuardKeys<GD> extends true ? true : HasEffectKeys<EFD>;
101
+ /**
102
+ * A finalized machine ready for spawning.
103
+ *
104
+ * Created by calling `.build()` on a `Machine`. This is the only type
105
+ * accepted by `Machine.spawn` and `ActorSystem.spawn` (regular overload).
106
+ * Testing utilities (`simulate`, `createTestHarness`, etc.) still accept `Machine`.
107
+ */
108
+ declare class BuiltMachine<State, Event, R = never> {
109
+ /** @internal */
110
+ readonly _inner: Machine<State, Event, R, any, any, any, any>;
111
+ /** @internal */
112
+ constructor(machine: Machine<State, Event, R, any, any, any, any>);
113
+ get initial(): State;
114
+ persist(config: PersistOptions): PersistentMachine<State & {
115
+ readonly _tag: string;
116
+ }, Event & {
117
+ readonly _tag: string;
118
+ }, R>;
119
+ }
120
+ /**
121
+ * Machine definition with fluent builder API.
122
+ *
123
+ * Type parameters:
124
+ * - `State`: The state union type
125
+ * - `Event`: The event union type
126
+ * - `R`: Effect requirements
127
+ * - `_SD`: State schema definition (for compile-time validation)
128
+ * - `_ED`: Event schema definition (for compile-time validation)
129
+ * - `GD`: Guard definitions
130
+ * - `EFD`: Effect definitions
131
+ */
132
+ 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>> {
133
+ readonly initial: State;
134
+ /** @internal */
135
+ readonly _transitions: Array<Transition<State, Event, GD, EFD, R>>;
136
+ /** @internal */
137
+ readonly _spawnEffects: Array<SpawnEffect<State, Event, EFD, R>>;
138
+ /** @internal */
139
+ readonly _backgroundEffects: Array<BackgroundEffect<State, Event, EFD, R>>;
140
+ /** @internal */
141
+ readonly _finalStates: Set<string>;
142
+ /** @internal */
143
+ readonly _guardsSchema?: GuardsSchema<GD>;
144
+ /** @internal */
145
+ readonly _effectsSchema?: EffectsSchema<EFD>;
146
+ /** @internal */
147
+ readonly _guardHandlers: Map<string, (params: unknown, ctx: SlotContext<State, Event>) => boolean | Effect.Effect<boolean, never, R>>;
148
+ /** @internal */
149
+ readonly _effectHandlers: Map<string, (params: unknown, ctx: SlotContext<State, Event>) => Effect.Effect<void, never, R>>;
150
+ /** @internal */
151
+ readonly _slots: {
152
+ guards: GuardSlots<GD>;
153
+ effects: EffectSlots<EFD>;
154
+ };
155
+ readonly stateSchema?: Schema.Schema<State, unknown, never>;
156
+ readonly eventSchema?: Schema.Schema<Event, unknown, never>;
157
+ /**
158
+ * Context tag for accessing machine state/event/self in slot handlers.
159
+ * Uses shared module-level tag for all machines.
160
+ */
161
+ readonly Context: Context.Tag<MachineContext<State, Event, MachineRef<Event>>, MachineContext<State, Event, MachineRef<Event>>>;
162
+ get transitions(): ReadonlyArray<Transition<State, Event, GD, EFD, R>>;
163
+ get spawnEffects(): ReadonlyArray<SpawnEffect<State, Event, EFD, R>>;
164
+ get backgroundEffects(): ReadonlyArray<BackgroundEffect<State, Event, EFD, R>>;
165
+ get finalStates(): ReadonlySet<string>;
166
+ get guardsSchema(): GuardsSchema<GD> | undefined;
167
+ get effectsSchema(): EffectsSchema<EFD> | undefined;
168
+ /** @internal */
169
+ constructor(initial: State, stateSchema?: Schema.Schema<State, unknown, never>, eventSchema?: Schema.Schema<Event, unknown, never>, guardsSchema?: GuardsSchema<GD>, effectsSchema?: EffectsSchema<EFD>);
170
+ /** Register transition for a single state */
171
+ 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>;
172
+ /** Register transition for multiple states (handler receives union of state types) */
173
+ 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>;
174
+ /**
175
+ * Like `on()`, but forces onEnter/spawn to run even when transitioning to the same state tag.
176
+ * Use this to restart timers, re-run spawned effects, or reset state-scoped effects.
177
+ */
178
+ /** Single state */
179
+ 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>;
180
+ /** Multiple states */
181
+ 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>;
182
+ /**
183
+ * Register a wildcard transition that fires from any state when no specific transition matches.
184
+ * Specific `.on()` transitions always take priority over `.onAny()`.
185
+ */
186
+ 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>;
187
+ /** @internal */
188
+ private addTransition;
189
+ /**
190
+ * State-scoped effect that is forked on state entry and automatically cancelled on state exit.
191
+ * Use effect slots defined via `Slot.Effects` for the actual work.
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * const MyEffects = Slot.Effects({
196
+ * fetchData: { url: Schema.String },
197
+ * });
198
+ *
199
+ * machine
200
+ * .spawn(State.Loading, ({ effects, state }) => effects.fetchData({ url: state.url }))
201
+ * .build({
202
+ * fetchData: ({ url }, { self }) =>
203
+ * Effect.gen(function* () {
204
+ * yield* Effect.addFinalizer(() => Effect.log("Leaving Loading"));
205
+ * const data = yield* Http.get(url);
206
+ * yield* self.send(Event.Loaded({ data }));
207
+ * }),
208
+ * });
209
+ * ```
210
+ */
211
+ 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>;
212
+ /**
213
+ * State-scoped task that runs on entry and sends success/failure events.
214
+ * Interrupts do not emit failure events.
215
+ */
216
+ 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: {
217
+ readonly onSuccess: (value: A, ctx: StateHandlerContext<NS, VariantsUnion<_ED> & BrandedEvent, EFD>) => ES;
218
+ readonly onFailure?: (cause: Cause.Cause<E1>, ctx: StateHandlerContext<NS, VariantsUnion<_ED> & BrandedEvent, EFD>) => EF;
219
+ }): Machine<State, Event, R, _SD, _ED, GD, EFD>;
220
+ /**
221
+ * Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
222
+ * Use effect slots defined via `Slot.Effects` for the actual work.
223
+ *
224
+ * @example
225
+ * ```ts
226
+ * const MyEffects = Slot.Effects({
227
+ * heartbeat: {},
228
+ * });
229
+ *
230
+ * machine
231
+ * .background(({ effects }) => effects.heartbeat())
232
+ * .build({
233
+ * heartbeat: (_, { self }) =>
234
+ * Effect.forever(
235
+ * Effect.sleep("30 seconds").pipe(Effect.andThen(self.send(Event.Ping)))
236
+ * ),
237
+ * });
238
+ * ```
239
+ */
240
+ background(handler: StateEffectHandler<State, Event, EFD, Scope.Scope>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
241
+ final<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
242
+ /**
243
+ * Finalize the machine. Returns a `BuiltMachine` — the only type accepted by `Machine.spawn`.
244
+ *
245
+ * - Machines with slots: pass implementations as the first argument.
246
+ * - Machines without slots: call with no arguments.
247
+ */
248
+ 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>>;
249
+ /** @internal Persist from raw Machine — prefer BuiltMachine.persist() */
250
+ persist(config: PersistOptions): PersistentMachine<State & {
251
+ readonly _tag: string;
252
+ }, Event & {
253
+ readonly _tag: string;
254
+ }, R>;
255
+ 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>;
256
+ }
257
+ declare const make: typeof Machine.make;
258
+ declare const spawn: {
259
+ <S extends {
260
+ readonly _tag: string;
261
+ }, E extends {
262
+ readonly _tag: string;
263
+ }, R>(machine: BuiltMachine<S, E, R>): Effect.Effect<ActorRef<S, E>, never, R>;
264
+ <S extends {
265
+ readonly _tag: string;
266
+ }, E extends {
267
+ readonly _tag: string;
268
+ }, R>(machine: BuiltMachine<S, E, R>, id: string): Effect.Effect<ActorRef<S, E>, never, R>;
269
+ };
270
+ //#endregion
271
+ 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 };
@@ -0,0 +1,317 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
+ import { getTag } from "./internal/utils.js";
3
+ import { ProvisionValidationError, SlotProvisionError } from "./errors.js";
4
+ import { persist } from "./persistence/persistent-machine.js";
5
+ import { MachineContextTag } from "./slot.js";
6
+ import { findTransitions, invalidateIndex } from "./internal/transition.js";
7
+ import { createActor } from "./actor.js";
8
+ import { Cause, Effect, Exit, Option, Scope } from "effect";
9
+
10
+ //#region src/machine.ts
11
+ var machine_exports = /* @__PURE__ */ __exportAll({
12
+ BuiltMachine: () => BuiltMachine,
13
+ Machine: () => Machine,
14
+ findTransitions: () => findTransitions,
15
+ make: () => make,
16
+ spawn: () => spawn
17
+ });
18
+ /**
19
+ * A finalized machine ready for spawning.
20
+ *
21
+ * Created by calling `.build()` on a `Machine`. This is the only type
22
+ * accepted by `Machine.spawn` and `ActorSystem.spawn` (regular overload).
23
+ * Testing utilities (`simulate`, `createTestHarness`, etc.) still accept `Machine`.
24
+ */
25
+ var BuiltMachine = class {
26
+ /** @internal */
27
+ _inner;
28
+ /** @internal */
29
+ constructor(machine) {
30
+ this._inner = machine;
31
+ }
32
+ get initial() {
33
+ return this._inner.initial;
34
+ }
35
+ persist(config) {
36
+ return this._inner.persist(config);
37
+ }
38
+ };
39
+ /**
40
+ * Machine definition with fluent builder API.
41
+ *
42
+ * Type parameters:
43
+ * - `State`: The state union type
44
+ * - `Event`: The event union type
45
+ * - `R`: Effect requirements
46
+ * - `_SD`: State schema definition (for compile-time validation)
47
+ * - `_ED`: Event schema definition (for compile-time validation)
48
+ * - `GD`: Guard definitions
49
+ * - `EFD`: Effect definitions
50
+ */
51
+ var Machine = class Machine {
52
+ initial;
53
+ /** @internal */ _transitions;
54
+ /** @internal */ _spawnEffects;
55
+ /** @internal */ _backgroundEffects;
56
+ /** @internal */ _finalStates;
57
+ /** @internal */ _guardsSchema;
58
+ /** @internal */ _effectsSchema;
59
+ /** @internal */ _guardHandlers;
60
+ /** @internal */ _effectHandlers;
61
+ /** @internal */ _slots;
62
+ stateSchema;
63
+ eventSchema;
64
+ /**
65
+ * Context tag for accessing machine state/event/self in slot handlers.
66
+ * Uses shared module-level tag for all machines.
67
+ */
68
+ Context = MachineContextTag;
69
+ get transitions() {
70
+ return this._transitions;
71
+ }
72
+ get spawnEffects() {
73
+ return this._spawnEffects;
74
+ }
75
+ get backgroundEffects() {
76
+ return this._backgroundEffects;
77
+ }
78
+ get finalStates() {
79
+ return this._finalStates;
80
+ }
81
+ get guardsSchema() {
82
+ return this._guardsSchema;
83
+ }
84
+ get effectsSchema() {
85
+ return this._effectsSchema;
86
+ }
87
+ /** @internal */
88
+ constructor(initial, stateSchema, eventSchema, guardsSchema, effectsSchema) {
89
+ this.initial = initial;
90
+ this._transitions = [];
91
+ this._spawnEffects = [];
92
+ this._backgroundEffects = [];
93
+ this._finalStates = /* @__PURE__ */ new Set();
94
+ this._guardsSchema = guardsSchema;
95
+ this._effectsSchema = effectsSchema;
96
+ this._guardHandlers = /* @__PURE__ */ new Map();
97
+ this._effectHandlers = /* @__PURE__ */ new Map();
98
+ this.stateSchema = stateSchema;
99
+ this.eventSchema = eventSchema;
100
+ this._slots = {
101
+ guards: this._guardsSchema !== void 0 ? this._guardsSchema._createSlots((name, params) => Effect.flatMap(Effect.serviceOptional(this.Context).pipe(Effect.orDie), (ctx) => {
102
+ const handler = this._guardHandlers.get(name);
103
+ if (handler === void 0) return Effect.die(new SlotProvisionError({
104
+ slotName: name,
105
+ slotType: "guard"
106
+ }));
107
+ const result = handler(params, ctx);
108
+ return typeof result === "boolean" ? Effect.succeed(result) : result;
109
+ })) : {},
110
+ effects: this._effectsSchema !== void 0 ? this._effectsSchema._createSlots((name, params) => Effect.flatMap(Effect.serviceOptional(this.Context).pipe(Effect.orDie), (ctx) => {
111
+ const handler = this._effectHandlers.get(name);
112
+ if (handler === void 0) return Effect.die(new SlotProvisionError({
113
+ slotName: name,
114
+ slotType: "effect"
115
+ }));
116
+ return handler(params, ctx);
117
+ })) : {}
118
+ };
119
+ }
120
+ on(stateOrStates, event, handler) {
121
+ const states = Array.isArray(stateOrStates) ? stateOrStates : [stateOrStates];
122
+ for (const s of states) this.addTransition(s, event, handler, false);
123
+ return this;
124
+ }
125
+ reenter(stateOrStates, event, handler) {
126
+ const states = Array.isArray(stateOrStates) ? stateOrStates : [stateOrStates];
127
+ for (const s of states) this.addTransition(s, event, handler, true);
128
+ return this;
129
+ }
130
+ /**
131
+ * Register a wildcard transition that fires from any state when no specific transition matches.
132
+ * Specific `.on()` transitions always take priority over `.onAny()`.
133
+ */
134
+ onAny(event, handler) {
135
+ const transition = {
136
+ stateTag: "*",
137
+ eventTag: getTag(event),
138
+ handler,
139
+ reenter: false
140
+ };
141
+ this._transitions.push(transition);
142
+ invalidateIndex(this);
143
+ return this;
144
+ }
145
+ /** @internal */
146
+ addTransition(state, event, handler, reenter) {
147
+ const transition = {
148
+ stateTag: getTag(state),
149
+ eventTag: getTag(event),
150
+ handler,
151
+ reenter
152
+ };
153
+ this._transitions.push(transition);
154
+ invalidateIndex(this);
155
+ return this;
156
+ }
157
+ /**
158
+ * State-scoped effect that is forked on state entry and automatically cancelled on state exit.
159
+ * Use effect slots defined via `Slot.Effects` for the actual work.
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * const MyEffects = Slot.Effects({
164
+ * fetchData: { url: Schema.String },
165
+ * });
166
+ *
167
+ * machine
168
+ * .spawn(State.Loading, ({ effects, state }) => effects.fetchData({ url: state.url }))
169
+ * .build({
170
+ * fetchData: ({ url }, { self }) =>
171
+ * Effect.gen(function* () {
172
+ * yield* Effect.addFinalizer(() => Effect.log("Leaving Loading"));
173
+ * const data = yield* Http.get(url);
174
+ * yield* self.send(Event.Loaded({ data }));
175
+ * }),
176
+ * });
177
+ * ```
178
+ */
179
+ spawn(state, handler) {
180
+ const stateTag = getTag(state);
181
+ this._spawnEffects.push({
182
+ stateTag,
183
+ handler
184
+ });
185
+ invalidateIndex(this);
186
+ return this;
187
+ }
188
+ /**
189
+ * State-scoped task that runs on entry and sends success/failure events.
190
+ * Interrupts do not emit failure events.
191
+ */
192
+ task(state, run, options) {
193
+ const handler = Effect.fn("effect-machine.task")(function* (ctx) {
194
+ const exit = yield* Effect.exit(run(ctx));
195
+ if (Exit.isSuccess(exit)) {
196
+ yield* ctx.self.send(options.onSuccess(exit.value, ctx));
197
+ yield* Effect.yieldNow();
198
+ return;
199
+ }
200
+ const cause = exit.cause;
201
+ if (Cause.isInterruptedOnly(cause)) return;
202
+ if (options.onFailure !== void 0) {
203
+ yield* ctx.self.send(options.onFailure(cause, ctx));
204
+ yield* Effect.yieldNow();
205
+ return;
206
+ }
207
+ return yield* Effect.failCause(cause).pipe(Effect.orDie);
208
+ });
209
+ return this.spawn(state, handler);
210
+ }
211
+ /**
212
+ * Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
213
+ * Use effect slots defined via `Slot.Effects` for the actual work.
214
+ *
215
+ * @example
216
+ * ```ts
217
+ * const MyEffects = Slot.Effects({
218
+ * heartbeat: {},
219
+ * });
220
+ *
221
+ * machine
222
+ * .background(({ effects }) => effects.heartbeat())
223
+ * .build({
224
+ * heartbeat: (_, { self }) =>
225
+ * Effect.forever(
226
+ * Effect.sleep("30 seconds").pipe(Effect.andThen(self.send(Event.Ping)))
227
+ * ),
228
+ * });
229
+ * ```
230
+ */
231
+ background(handler) {
232
+ this._backgroundEffects.push({ handler });
233
+ return this;
234
+ }
235
+ final(state) {
236
+ const stateTag = getTag(state);
237
+ this._finalStates.add(stateTag);
238
+ return this;
239
+ }
240
+ /**
241
+ * Finalize the machine. Returns a `BuiltMachine` — the only type accepted by `Machine.spawn`.
242
+ *
243
+ * - Machines with slots: pass implementations as the first argument.
244
+ * - Machines without slots: call with no arguments.
245
+ */
246
+ build(...args) {
247
+ const handlers = args[0];
248
+ if (handlers !== void 0) {
249
+ const requiredSlots = /* @__PURE__ */ new Set();
250
+ if (this._guardsSchema !== void 0) for (const name of Object.keys(this._guardsSchema.definitions)) requiredSlots.add(name);
251
+ if (this._effectsSchema !== void 0) for (const name of Object.keys(this._effectsSchema.definitions)) requiredSlots.add(name);
252
+ const providedSlots = new Set(Object.keys(handlers));
253
+ const missing = [];
254
+ const extra = [];
255
+ for (const name of requiredSlots) if (!providedSlots.has(name)) missing.push(name);
256
+ for (const name of providedSlots) if (!requiredSlots.has(name)) extra.push(name);
257
+ if (missing.length > 0 || extra.length > 0) throw new ProvisionValidationError({
258
+ missing,
259
+ extra
260
+ });
261
+ const result = new Machine(this.initial, this.stateSchema, this.eventSchema, this._guardsSchema, this._effectsSchema);
262
+ result._transitions = [...this._transitions];
263
+ result._finalStates = new Set(this._finalStates);
264
+ result._spawnEffects = [...this._spawnEffects];
265
+ result._backgroundEffects = [...this._backgroundEffects];
266
+ const anyHandlers = handlers;
267
+ if (this._guardsSchema !== void 0) for (const name of Object.keys(this._guardsSchema.definitions)) result._guardHandlers.set(name, anyHandlers[name]);
268
+ if (this._effectsSchema !== void 0) for (const name of Object.keys(this._effectsSchema.definitions)) result._effectHandlers.set(name, anyHandlers[name]);
269
+ return new BuiltMachine(result);
270
+ }
271
+ return new BuiltMachine(this);
272
+ }
273
+ /** @internal Persist from raw Machine — prefer BuiltMachine.persist() */
274
+ persist(config) {
275
+ return persist(config)(this);
276
+ }
277
+ static make(config) {
278
+ return new Machine(config.initial, config.state, config.event, config.guards, config.effects);
279
+ }
280
+ };
281
+ const make = Machine.make;
282
+ /**
283
+ * Spawn an actor directly without ActorSystem ceremony.
284
+ * Accepts only `BuiltMachine` (call `.build()` first).
285
+ *
286
+ * **Single actor, no registry.** Caller manages lifetime via `actor.stop`.
287
+ * If a `Scope` exists in context, cleanup attaches automatically on scope close.
288
+ *
289
+ * For registry, lookup by ID, persistence, or multi-actor coordination,
290
+ * use `ActorSystemService` / `system.spawn` instead.
291
+ *
292
+ * @example
293
+ * ```ts
294
+ * // Fire-and-forget — caller manages lifetime
295
+ * const actor = yield* Machine.spawn(machine.build());
296
+ * yield* actor.send(Event.Start);
297
+ * yield* actor.awaitFinal;
298
+ * yield* actor.stop;
299
+ *
300
+ * // Scope-aware — auto-cleans up on scope close
301
+ * yield* Effect.scoped(Effect.gen(function* () {
302
+ * const actor = yield* Machine.spawn(machine.build());
303
+ * yield* actor.send(Event.Start);
304
+ * // actor.stop called automatically when scope closes
305
+ * }));
306
+ * ```
307
+ */
308
+ const spawnImpl = Effect.fn("effect-machine.spawn")(function* (built, id) {
309
+ const actor = yield* createActor(id ?? `actor-${Math.random().toString(36).slice(2)}`, built._inner);
310
+ const maybeScope = yield* Effect.serviceOption(Scope.Scope);
311
+ if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, actor.stop);
312
+ return actor;
313
+ });
314
+ const spawn = spawnImpl;
315
+
316
+ //#endregion
317
+ export { BuiltMachine, Machine, findTransitions, machine_exports, make, spawn };