effect-machine 0.8.0 → 0.10.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 (76) hide show
  1. package/README.md +76 -16
  2. package/dist/_virtual/_rolldown/runtime.js +6 -11
  3. package/dist/actor.d.ts +58 -72
  4. package/dist/actor.js +166 -32
  5. package/dist/cluster/entity-machine.d.ts +0 -1
  6. package/dist/cluster/entity-machine.js +6 -6
  7. package/dist/cluster/index.js +1 -2
  8. package/dist/cluster/to-entity.js +1 -3
  9. package/dist/errors.d.ts +12 -1
  10. package/dist/errors.js +8 -3
  11. package/dist/index.d.ts +4 -4
  12. package/dist/index.js +2 -3
  13. package/dist/inspection.js +1 -3
  14. package/dist/internal/inspection.js +1 -3
  15. package/dist/internal/transition.d.ts +26 -2
  16. package/dist/internal/transition.js +37 -10
  17. package/dist/internal/utils.d.ts +7 -2
  18. package/dist/internal/utils.js +1 -3
  19. package/dist/machine.d.ts +66 -4
  20. package/dist/machine.js +67 -31
  21. package/dist/persistence/adapter.js +1 -3
  22. package/dist/persistence/adapters/in-memory.js +1 -3
  23. package/dist/persistence/index.js +1 -2
  24. package/dist/persistence/persistent-actor.js +54 -19
  25. package/dist/persistence/persistent-machine.js +1 -3
  26. package/dist/schema.js +1 -3
  27. package/dist/slot.js +1 -3
  28. package/dist/testing.js +58 -6
  29. package/package.json +19 -18
  30. package/v3/dist/_virtual/_rolldown/runtime.js +13 -0
  31. package/{dist-v3 → v3/dist}/actor.d.ts +65 -78
  32. package/{dist-v3 → v3/dist}/actor.js +173 -37
  33. package/{dist-v3 → v3/dist}/cluster/entity-machine.d.ts +1 -2
  34. package/{dist-v3 → v3/dist}/cluster/entity-machine.js +9 -9
  35. package/{dist-v3 → v3/dist}/cluster/index.js +1 -2
  36. package/{dist-v3 → v3/dist}/cluster/to-entity.d.ts +1 -1
  37. package/{dist-v3 → v3/dist}/cluster/to-entity.js +2 -4
  38. package/v3/dist/errors.d.ts +76 -0
  39. package/{dist-v3 → v3/dist}/errors.js +9 -4
  40. package/v3/dist/index.d.ts +13 -0
  41. package/v3/dist/index.js +13 -0
  42. package/{dist-v3 → v3/dist}/inspection.d.ts +53 -8
  43. package/v3/dist/inspection.js +156 -0
  44. package/{dist-v3 → v3/dist}/internal/brands.d.ts +1 -1
  45. package/{dist-v3 → v3/dist}/internal/inspection.d.ts +1 -1
  46. package/v3/dist/internal/inspection.js +20 -0
  47. package/{dist-v3 → v3/dist}/internal/transition.d.ts +35 -11
  48. package/{dist-v3 → v3/dist}/internal/transition.js +47 -17
  49. package/{dist-v3 → v3/dist}/internal/utils.d.ts +9 -4
  50. package/{dist-v3 → v3/dist}/internal/utils.js +2 -4
  51. package/{dist-v3 → v3/dist}/machine.d.ts +86 -10
  52. package/{dist-v3 → v3/dist}/machine.js +130 -33
  53. package/{dist-v3 → v3/dist}/persistence/adapter.d.ts +18 -5
  54. package/{dist-v3 → v3/dist}/persistence/adapter.js +2 -4
  55. package/{dist-v3 → v3/dist}/persistence/adapters/in-memory.d.ts +1 -1
  56. package/{dist-v3 → v3/dist}/persistence/adapters/in-memory.js +2 -4
  57. package/{dist-v3 → v3/dist}/persistence/index.js +1 -2
  58. package/{dist-v3 → v3/dist}/persistence/persistent-actor.d.ts +7 -6
  59. package/{dist-v3 → v3/dist}/persistence/persistent-actor.js +59 -22
  60. package/{dist-v3 → v3/dist}/persistence/persistent-machine.d.ts +1 -1
  61. package/{dist-v3 → v3/dist}/persistence/persistent-machine.js +2 -4
  62. package/{dist-v3 → v3/dist}/schema.d.ts +1 -1
  63. package/{dist-v3 → v3/dist}/schema.js +6 -5
  64. package/{dist-v3 → v3/dist}/slot.d.ts +4 -3
  65. package/{dist-v3 → v3/dist}/slot.js +2 -4
  66. package/{dist-v3 → v3/dist}/testing.d.ts +14 -8
  67. package/{dist-v3 → v3/dist}/testing.js +61 -9
  68. package/dist-v3/_virtual/_rolldown/runtime.js +0 -18
  69. package/dist-v3/errors.d.ts +0 -27
  70. package/dist-v3/index.d.ts +0 -13
  71. package/dist-v3/index.js +0 -14
  72. package/dist-v3/inspection.js +0 -50
  73. package/dist-v3/internal/inspection.js +0 -15
  74. /package/{dist-v3 → v3/dist}/cluster/index.d.ts +0 -0
  75. /package/{dist-v3 → v3/dist}/internal/brands.js +0 -0
  76. /package/{dist-v3 → v3/dist}/persistence/index.d.ts +0 -0
@@ -1,23 +1,24 @@
1
+ import { EffectHandlers, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers, GuardSlots, GuardsDef, GuardsSchema, MachineContext } from "./slot.js";
1
2
  import { TransitionResult } from "./internal/utils.js";
2
3
  import { BrandedEvent, BrandedState, TaggedOrConstructor } from "./internal/brands.js";
3
4
  import { MachineEventSchema, MachineStateSchema, VariantsUnion } from "./schema.js";
4
5
  import { PersistenceConfig, PersistentMachine } from "./persistence/persistent-machine.js";
5
6
  import { DuplicateActorError } from "./errors.js";
6
- import { EffectHandlers, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers, GuardSlots, GuardsDef, GuardsSchema, MachineContext } from "./slot.js";
7
7
  import { findTransitions } from "./internal/transition.js";
8
- import "./persistence/index.js";
9
8
  import { ActorRef, ActorSystem } from "./actor.js";
10
- import { Cause, Context, Effect, Schedule, Schema, Scope } from "effect";
9
+ import { Cause, Context, Duration, Effect, Schedule, Schema, Scope } from "effect";
11
10
 
12
- //#region src-v3/machine.d.ts
11
+ //#region src/machine.d.ts
13
12
  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 };
13
+ export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, PersistenceConfig, PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, make, spawn };
15
14
  }
16
15
  /**
17
16
  * Self reference for sending events back to the machine
18
17
  */
19
18
  interface MachineRef<Event> {
20
19
  readonly send: (event: Event) => Effect.Effect<void>;
20
+ /** Fire-and-forget alias for send (OTP gen_server:cast). */
21
+ readonly cast: (event: Event) => Effect.Effect<void>;
21
22
  readonly spawn: <S2 extends {
22
23
  readonly _tag: string;
23
24
  }, E2 extends {
@@ -37,6 +38,7 @@ interface HandlerContext<State, Event, GD extends GuardsDef, ED extends EffectsD
37
38
  * Handler context passed to state effect handlers (onEnter, spawn, background)
38
39
  */
39
40
  interface StateHandlerContext<State, Event, ED extends EffectsDef> {
41
+ readonly actorId: string;
40
42
  readonly state: State;
41
43
  readonly event: Event;
42
44
  readonly self: MachineRef<Event>;
@@ -81,6 +83,23 @@ interface PersistOptions {
81
83
  readonly journalEvents: boolean;
82
84
  readonly machineType?: string;
83
85
  }
86
+ interface TaskOptions<State, Event, ED extends EffectsDef, A, E1, ES, EF> {
87
+ readonly onSuccess: (value: A, ctx: StateHandlerContext<State, Event, ED>) => ES;
88
+ readonly onFailure?: (cause: Cause.Cause<E1>, ctx: StateHandlerContext<State, Event, ED>) => EF;
89
+ readonly name?: string;
90
+ }
91
+ /**
92
+ * Configuration for `.timeout()` — gen_statem-style state timeouts.
93
+ *
94
+ * Entering the state starts a timer. Leaving cancels it.
95
+ * `.reenter()` restarts the timer with fresh state values.
96
+ */
97
+ interface TimeoutConfig<State, Event> {
98
+ /** Duration before firing. Static or derived from current state. */
99
+ readonly duration: Duration.DurationInput | ((state: State) => Duration.DurationInput);
100
+ /** Event to send when the timer fires. Static or derived from current state. */
101
+ readonly event: Event | ((state: State) => Event);
102
+ }
84
103
  type IsAny<T> = 0 extends 1 & T ? true : false;
85
104
  type IsUnknown<T> = unknown extends T ? ([T] extends [unknown] ? true : false) : false;
86
105
  type NormalizeR<T> = IsAny<T> extends true ? T : IsUnknown<T> extends true ? never : T;
@@ -147,6 +166,11 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
147
166
  /** @internal */
148
167
  readonly _finalStates: Set<string>;
149
168
  /** @internal */
169
+ readonly _postponeRules: Array<{
170
+ readonly stateTag: string;
171
+ readonly eventTag: string;
172
+ }>;
173
+ /** @internal */
150
174
  readonly _guardsSchema?: GuardsSchema<GD>;
151
175
  /** @internal */
152
176
  readonly _effectsSchema?: EffectsSchema<EFD>;
@@ -170,10 +194,18 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
170
194
  get spawnEffects(): ReadonlyArray<SpawnEffect<State, Event, EFD, R>>;
171
195
  get backgroundEffects(): ReadonlyArray<BackgroundEffect<State, Event, EFD, R>>;
172
196
  get finalStates(): ReadonlySet<string>;
197
+ get postponeRules(): ReadonlyArray<{
198
+ readonly stateTag: string;
199
+ readonly eventTag: string;
200
+ }>;
173
201
  get guardsSchema(): GuardsSchema<GD> | undefined;
174
202
  get effectsSchema(): EffectsSchema<EFD> | undefined;
175
203
  /** @internal */
176
204
  constructor(initial: State, stateSchema?: Schema.Schema<State, unknown, never>, eventSchema?: Schema.Schema<Event, unknown, never>, guardsSchema?: GuardsSchema<GD>, effectsSchema?: EffectsSchema<EFD>);
205
+ 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>;
206
+ 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>;
207
+ /** @internal */
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, GD, EFD, never>, reenter: boolean): Machine<State, Event, R, _SD, _ED, GD, EFD>;
177
209
  /** Register transition for a single state */
178
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, GD, EFD, never>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
179
211
  /** Register transition for multiple states (handler receives union of state types) */
@@ -220,10 +252,29 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
220
252
  * State-scoped task that runs on entry and sends success/failure events.
221
253
  * Interrupts do not emit failure events.
222
254
  */
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>;
255
+ 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>;
256
+ /**
257
+ * State timeout gen_statem's `state_timeout`.
258
+ *
259
+ * Entering the state starts a timer. Leaving cancels it (via state scope).
260
+ * `.reenter()` restarts the timer with fresh state values.
261
+ * Compiles to `.task()` internally — preserves `@machine.task` inspection events.
262
+ *
263
+ * @example
264
+ * ```ts
265
+ * machine
266
+ * .timeout(State.Loading, {
267
+ * duration: Duration.seconds(30),
268
+ * event: Event.Timeout,
269
+ * })
270
+ * // Dynamic duration from state
271
+ * .timeout(State.Retrying, {
272
+ * duration: (state) => Duration.seconds(state.backoff),
273
+ * event: Event.GiveUp,
274
+ * })
275
+ * ```
276
+ */
277
+ timeout<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, config: TimeoutConfig<NS, VariantsUnion<_ED> & BrandedEvent>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
227
278
  /**
228
279
  * Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
229
280
  * Use effect slots defined via `Slot.Effects` for the actual work.
@@ -245,6 +296,24 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
245
296
  * ```
246
297
  */
247
298
  background(handler: StateEffectHandler<State, Event, EFD, Scope.Scope>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
299
+ /**
300
+ * Postpone events — gen_statem's event postpone.
301
+ *
302
+ * When a matching event arrives in the given state, it is buffered instead of
303
+ * processed. After the next state transition (tag change), all buffered events
304
+ * are drained through the loop in FIFO order.
305
+ *
306
+ * Reply-bearing events (from `call`/`ask`) in the postpone buffer are settled
307
+ * with `ActorStoppedError` on stop/interrupt/final-state.
308
+ *
309
+ * @example
310
+ * ```ts
311
+ * machine
312
+ * .postpone(State.Connecting, Event.Data) // single event
313
+ * .postpone(State.Connecting, [Event.Data, Event.Cmd]) // multiple events
314
+ * ```
315
+ */
316
+ 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>;
248
317
  final<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
249
318
  /**
250
319
  * Finalize the machine. Returns a `BuiltMachine` — the only type accepted by `Machine.spawn`.
@@ -261,6 +330,13 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
261
330
  }, R>;
262
331
  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
332
  }
333
+ 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> {
334
+ private readonly machine;
335
+ private readonly states;
336
+ constructor(machine: Machine<State, Event, R, _SD, _ED, GD, EFD>, states: ReadonlyArray<TaggedOrConstructor<SelectedState>>);
337
+ on<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<SelectedState, NE, RS, GD, EFD, never>): TransitionScope<State, Event, R, _SD, _ED, GD, EFD, SelectedState>;
338
+ reenter<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<SelectedState, NE, RS, GD, EFD, never>): TransitionScope<State, Event, R, _SD, _ED, GD, EFD, SelectedState>;
339
+ }
264
340
  declare const make: typeof Machine.make;
265
341
  declare const spawn: {
266
342
  <S extends {
@@ -275,4 +351,4 @@ declare const spawn: {
275
351
  }, R>(machine: BuiltMachine<S, E, R>, id: string): Effect.Effect<ActorRef<S, E>, never, R>;
276
352
  };
277
353
  //#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 };
354
+ export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, type PersistenceConfig, type PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, machine_d_exports, make, spawn };
@@ -1,13 +1,14 @@
1
1
  import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
+ import { Inspector } from "./inspection.js";
2
3
  import { getTag } from "./internal/utils.js";
3
4
  import { ProvisionValidationError, SlotProvisionError } from "./errors.js";
4
5
  import { persist } from "./persistence/persistent-machine.js";
6
+ import { emitWithTimestamp } from "./internal/inspection.js";
5
7
  import { MachineContextTag } from "./slot.js";
6
8
  import { findTransitions, invalidateIndex } from "./internal/transition.js";
7
9
  import { createActor } from "./actor.js";
8
10
  import { Cause, Effect, Exit, Option, Scope } from "effect";
9
-
10
- //#region src-v3/machine.ts
11
+ //#region src/machine.ts
11
12
  var machine_exports = /* @__PURE__ */ __exportAll({
12
13
  BuiltMachine: () => BuiltMachine,
13
14
  Machine: () => Machine,
@@ -15,6 +16,15 @@ var machine_exports = /* @__PURE__ */ __exportAll({
15
16
  make: () => make,
16
17
  spawn: () => spawn
17
18
  });
19
+ const emitTaskInspection = (input) => Effect.flatMap(Effect.serviceOptional(Inspector).pipe(Effect.option), (inspector) => Option.isNone(inspector) ? Effect.void : emitWithTimestamp(inspector.value, (timestamp) => ({
20
+ type: "@machine.task",
21
+ actorId: input.actorId,
22
+ state: input.state,
23
+ taskName: input.taskName,
24
+ phase: input.phase,
25
+ error: input.error,
26
+ timestamp
27
+ })));
18
28
  /**
19
29
  * A finalized machine ready for spawning.
20
30
  *
@@ -54,6 +64,7 @@ var Machine = class Machine {
54
64
  /** @internal */ _spawnEffects;
55
65
  /** @internal */ _backgroundEffects;
56
66
  /** @internal */ _finalStates;
67
+ /** @internal */ _postponeRules;
57
68
  /** @internal */ _guardsSchema;
58
69
  /** @internal */ _effectsSchema;
59
70
  /** @internal */ _guardHandlers;
@@ -78,6 +89,9 @@ var Machine = class Machine {
78
89
  get finalStates() {
79
90
  return this._finalStates;
80
91
  }
92
+ get postponeRules() {
93
+ return this._postponeRules;
94
+ }
81
95
  get guardsSchema() {
82
96
  return this._guardsSchema;
83
97
  }
@@ -91,6 +105,7 @@ var Machine = class Machine {
91
105
  this._spawnEffects = [];
92
106
  this._backgroundEffects = [];
93
107
  this._finalStates = /* @__PURE__ */ new Set();
108
+ this._postponeRules = [];
94
109
  this._guardsSchema = guardsSchema;
95
110
  this._effectsSchema = effectsSchema;
96
111
  this._guardHandlers = /* @__PURE__ */ new Map();
@@ -117,6 +132,15 @@ var Machine = class Machine {
117
132
  })) : {}
118
133
  };
119
134
  }
135
+ from(stateOrStates, build) {
136
+ build(new TransitionScope(this, Array.isArray(stateOrStates) ? stateOrStates : [stateOrStates]));
137
+ return this;
138
+ }
139
+ /** @internal */
140
+ scopeTransition(states, event, handler, reenter) {
141
+ for (const state of states) this.addTransition(state, event, handler, reenter);
142
+ return this;
143
+ }
120
144
  on(stateOrStates, event, handler) {
121
145
  const states = Array.isArray(stateOrStates) ? stateOrStates : [stateOrStates];
122
146
  for (const s of states) this.addTransition(s, event, handler, false);
@@ -191,14 +215,41 @@ var Machine = class Machine {
191
215
  */
192
216
  task(state, run, options) {
193
217
  const handler = Effect.fn("effect-machine.task")(function* (ctx) {
218
+ yield* emitTaskInspection({
219
+ actorId: ctx.actorId,
220
+ state: ctx.state,
221
+ taskName: options.name,
222
+ phase: "start"
223
+ });
194
224
  const exit = yield* Effect.exit(run(ctx));
195
225
  if (Exit.isSuccess(exit)) {
226
+ yield* emitTaskInspection({
227
+ actorId: ctx.actorId,
228
+ state: ctx.state,
229
+ taskName: options.name,
230
+ phase: "success"
231
+ });
196
232
  yield* ctx.self.send(options.onSuccess(exit.value, ctx));
197
233
  yield* Effect.yieldNow();
198
234
  return;
199
235
  }
200
236
  const cause = exit.cause;
201
- if (Cause.isInterruptedOnly(cause)) return;
237
+ if (Cause.isInterruptedOnly(cause)) {
238
+ yield* emitTaskInspection({
239
+ actorId: ctx.actorId,
240
+ state: ctx.state,
241
+ taskName: options.name,
242
+ phase: "interrupt"
243
+ });
244
+ return;
245
+ }
246
+ yield* emitTaskInspection({
247
+ actorId: ctx.actorId,
248
+ state: ctx.state,
249
+ taskName: options.name,
250
+ phase: "failure",
251
+ error: Cause.pretty(cause)
252
+ });
202
253
  if (options.onFailure !== void 0) {
203
254
  yield* ctx.self.send(options.onFailure(cause, ctx));
204
255
  yield* Effect.yieldNow();
@@ -209,6 +260,36 @@ var Machine = class Machine {
209
260
  return this.spawn(state, handler);
210
261
  }
211
262
  /**
263
+ * State timeout — gen_statem's `state_timeout`.
264
+ *
265
+ * Entering the state starts a timer. Leaving cancels it (via state scope).
266
+ * `.reenter()` restarts the timer with fresh state values.
267
+ * Compiles to `.task()` internally — preserves `@machine.task` inspection events.
268
+ *
269
+ * @example
270
+ * ```ts
271
+ * machine
272
+ * .timeout(State.Loading, {
273
+ * duration: Duration.seconds(30),
274
+ * event: Event.Timeout,
275
+ * })
276
+ * // Dynamic duration from state
277
+ * .timeout(State.Retrying, {
278
+ * duration: (state) => Duration.seconds(state.backoff),
279
+ * event: Event.GiveUp,
280
+ * })
281
+ * ```
282
+ */
283
+ timeout(state, config) {
284
+ const stateTag = getTag(state);
285
+ const resolveDuration = typeof config.duration === "function" ? config.duration : () => config.duration;
286
+ const resolveEvent = typeof config.event === "function" ? config.event : () => config.event;
287
+ return this.task(state, (ctx) => Effect.sleep(resolveDuration(ctx.state)), {
288
+ onSuccess: (_, ctx) => resolveEvent(ctx.state),
289
+ name: `$timeout:${stateTag}`
290
+ });
291
+ }
292
+ /**
212
293
  * Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
213
294
  * Use effect slots defined via `Slot.Effects` for the actual work.
214
295
  *
@@ -232,6 +313,35 @@ var Machine = class Machine {
232
313
  this._backgroundEffects.push({ handler });
233
314
  return this;
234
315
  }
316
+ /**
317
+ * Postpone events — gen_statem's event postpone.
318
+ *
319
+ * When a matching event arrives in the given state, it is buffered instead of
320
+ * processed. After the next state transition (tag change), all buffered events
321
+ * are drained through the loop in FIFO order.
322
+ *
323
+ * Reply-bearing events (from `call`/`ask`) in the postpone buffer are settled
324
+ * with `ActorStoppedError` on stop/interrupt/final-state.
325
+ *
326
+ * @example
327
+ * ```ts
328
+ * machine
329
+ * .postpone(State.Connecting, Event.Data) // single event
330
+ * .postpone(State.Connecting, [Event.Data, Event.Cmd]) // multiple events
331
+ * ```
332
+ */
333
+ postpone(state, events) {
334
+ const stateTag = getTag(state);
335
+ const eventList = Array.isArray(events) ? events : [events];
336
+ for (const ev of eventList) {
337
+ const eventTag = getTag(ev);
338
+ this._postponeRules.push({
339
+ stateTag,
340
+ eventTag
341
+ });
342
+ }
343
+ return this;
344
+ }
235
345
  final(state) {
236
346
  const stateTag = getTag(state);
237
347
  this._finalStates.add(stateTag);
@@ -263,6 +373,7 @@ var Machine = class Machine {
263
373
  result._finalStates = new Set(this._finalStates);
264
374
  result._spawnEffects = [...this._spawnEffects];
265
375
  result._backgroundEffects = [...this._backgroundEffects];
376
+ result._postponeRules = [...this._postponeRules];
266
377
  const anyHandlers = handlers;
267
378
  if (this._guardsSchema !== void 0) for (const name of Object.keys(this._guardsSchema.definitions)) result._guardHandlers.set(name, anyHandlers[name]);
268
379
  if (this._effectsSchema !== void 0) for (const name of Object.keys(this._effectsSchema.definitions)) result._effectHandlers.set(name, anyHandlers[name]);
@@ -278,40 +389,26 @@ var Machine = class Machine {
278
389
  return new Machine(config.initial, config.state, config.event, config.guards, config.effects);
279
390
  }
280
391
  };
392
+ var TransitionScope = class {
393
+ constructor(machine, states) {
394
+ this.machine = machine;
395
+ this.states = states;
396
+ }
397
+ on(event, handler) {
398
+ this.machine.scopeTransition(this.states, event, handler, false);
399
+ return this;
400
+ }
401
+ reenter(event, handler) {
402
+ this.machine.scopeTransition(this.states, event, handler, true);
403
+ return this;
404
+ }
405
+ };
281
406
  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) {
407
+ const spawn = Effect.fn("effect-machine.spawn")(function* (built, id) {
309
408
  const actor = yield* createActor(id ?? `actor-${Math.random().toString(36).slice(2)}`, built._inner);
310
409
  const maybeScope = yield* Effect.serviceOption(Scope.Scope);
311
410
  if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, actor.stop);
312
411
  return actor;
313
412
  });
314
- const spawn = spawnImpl;
315
-
316
413
  //#endregion
317
- export { BuiltMachine, Machine, findTransitions, machine_exports, make, spawn };
414
+ export { BuiltMachine, Machine, findTransitions, machine_exports, make, spawn };
@@ -1,8 +1,8 @@
1
1
  import { DuplicateActorError } from "../errors.js";
2
2
  import { PersistentActorRef } from "./persistent-actor.js";
3
- import { Effect, Option, Schema } from "effect";
3
+ import { Context, Effect, Option, Schema } from "effect";
4
4
 
5
- //#region src-v3/persistence/adapter.d.ts
5
+ //#region src/persistence/adapter.d.ts
6
6
  /**
7
7
  * Metadata for a persisted actor.
8
8
  * Used for discovery and filtering during bulk restore.
@@ -106,17 +106,30 @@ interface PersistenceAdapter {
106
106
  */
107
107
  readonly loadMetadata?: (id: string) => Effect.Effect<Option.Option<ActorMetadata>, PersistenceError>;
108
108
  }
109
- declare const PersistenceError_base: any;
109
+ declare const PersistenceError_base: Schema.TaggedErrorClass<PersistenceError, "PersistenceError", {
110
+ readonly _tag: Schema.tag<"PersistenceError">;
111
+ } & {
112
+ operation: typeof Schema.String;
113
+ actorId: typeof Schema.String;
114
+ cause: Schema.optional<typeof Schema.Unknown>;
115
+ message: Schema.optional<typeof Schema.String>;
116
+ }>;
110
117
  /**
111
118
  * Error type for persistence operations
112
119
  */
113
120
  declare class PersistenceError extends PersistenceError_base {}
114
- declare const VersionConflictError_base: any;
121
+ declare const VersionConflictError_base: Schema.TaggedErrorClass<VersionConflictError, "VersionConflictError", {
122
+ readonly _tag: Schema.tag<"VersionConflictError">;
123
+ } & {
124
+ actorId: typeof Schema.String;
125
+ expectedVersion: typeof Schema.Number;
126
+ actualVersion: typeof Schema.Number;
127
+ }>;
115
128
  /**
116
129
  * Version conflict error — snapshot version doesn't match expected
117
130
  */
118
131
  declare class VersionConflictError extends VersionConflictError_base {}
119
- declare const PersistenceAdapterTag_base: any;
132
+ declare const PersistenceAdapterTag_base: Context.TagClass<PersistenceAdapterTag, "effect-machine/src/persistence/adapter/PersistenceAdapterTag", PersistenceAdapter>;
120
133
  /**
121
134
  * PersistenceAdapter service tag
122
135
  */
@@ -1,6 +1,5 @@
1
1
  import { Context, Schema } from "effect";
2
-
3
- //#region src-v3/persistence/adapter.ts
2
+ //#region src/persistence/adapter.ts
4
3
  /**
5
4
  * Error type for persistence operations
6
5
  */
@@ -22,6 +21,5 @@ var VersionConflictError = class extends Schema.TaggedError()("VersionConflictEr
22
21
  * PersistenceAdapter service tag
23
22
  */
24
23
  var PersistenceAdapterTag = class extends Context.Tag("effect-machine/src/persistence/adapter/PersistenceAdapterTag")() {};
25
-
26
24
  //#endregion
27
- export { PersistenceAdapterTag, PersistenceError, VersionConflictError };
25
+ export { PersistenceAdapterTag, PersistenceError, VersionConflictError };
@@ -1,7 +1,7 @@
1
1
  import { PersistenceAdapter, PersistenceAdapterTag } from "../adapter.js";
2
2
  import { Effect, Layer } from "effect";
3
3
 
4
- //#region src-v3/persistence/adapters/in-memory.d.ts
4
+ //#region src/persistence/adapters/in-memory.d.ts
5
5
  /**
6
6
  * Create an in-memory persistence adapter effect.
7
7
  * Returns the adapter directly for custom layer composition.
@@ -1,7 +1,6 @@
1
1
  import { PersistenceAdapterTag, PersistenceError, VersionConflictError } from "../adapter.js";
2
2
  import { Effect, Layer, Option, Ref, Schema } from "effect";
3
-
4
- //#region src-v3/persistence/adapters/in-memory.ts
3
+ //#region src/persistence/adapters/in-memory.ts
5
4
  /**
6
5
  * Create an in-memory persistence adapter.
7
6
  * Useful for testing and development.
@@ -171,6 +170,5 @@ const makeInMemoryPersistenceAdapter = make;
171
170
  * ```
172
171
  */
173
172
  const InMemoryPersistenceAdapter = Layer.effect(PersistenceAdapterTag, make);
174
-
175
173
  //#endregion
176
- export { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter };
174
+ export { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter };
@@ -2,5 +2,4 @@ import { isPersistentMachine, persist } from "./persistent-machine.js";
2
2
  import { PersistenceAdapterTag, PersistenceError, VersionConflictError } from "./adapter.js";
3
3
  import { createPersistentActor, restorePersistentActor } from "./persistent-actor.js";
4
4
  import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./adapters/in-memory.js";
5
-
6
- export { InMemoryPersistenceAdapter, PersistenceAdapterTag, PersistenceError, VersionConflictError, createPersistentActor, isPersistentMachine, makeInMemoryPersistenceAdapter, persist, restorePersistentActor };
5
+ export { InMemoryPersistenceAdapter, PersistenceAdapterTag, PersistenceError, VersionConflictError, createPersistentActor, isPersistentMachine, makeInMemoryPersistenceAdapter, persist, restorePersistentActor };
@@ -1,10 +1,11 @@
1
+ import { EffectsDef, GuardsDef, MachineContext } from "../slot.js";
1
2
  import { PersistentMachine } from "./persistent-machine.js";
2
- import { EffectsDef, GuardsDef } from "../slot.js";
3
- import { PersistedEvent, PersistenceError, Snapshot, VersionConflictError } from "./adapter.js";
3
+ import { PersistedEvent, PersistenceAdapterTag, PersistenceError, Snapshot, VersionConflictError } from "./adapter.js";
4
+ import { MachineRef } from "../machine.js";
4
5
  import { ActorRef } from "../actor.js";
5
- import { Effect, Option } from "effect";
6
+ import { Effect, Option, Scope } from "effect";
6
7
 
7
- //#region src-v3/persistence/persistent-actor.d.ts
8
+ //#region src/persistence/persistent-actor.d.ts
8
9
  /**
9
10
  * Extended ActorRef with persistence capabilities
10
11
  */
@@ -35,7 +36,7 @@ declare const createPersistentActor: <S extends {
35
36
  readonly _tag: string;
36
37
  }, E extends {
37
38
  readonly _tag: string;
38
- }, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(id: string, persistentMachine: PersistentMachine<S, E, R>, initialSnapshot: Option.Option<Snapshot<S>>, initialEvents: readonly PersistedEvent<E>[]) => Effect.Effect<PersistentActorRef<S, E, R>, unknown, unknown>;
39
+ }, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(id: string, persistentMachine: PersistentMachine<S, E, R>, initialSnapshot: Option.Option<Snapshot<S>>, initialEvents: readonly PersistedEvent<E>[]) => Effect.Effect<PersistentActorRef<S, E, R>, PersistenceError, PersistenceAdapterTag | Exclude<R, MachineContext<S, E, MachineRef<E>>> | Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
39
40
  /**
40
41
  * Restore an actor from persistence.
41
42
  * Returns None if no persisted state exists.
@@ -44,6 +45,6 @@ declare const restorePersistentActor: <S extends {
44
45
  readonly _tag: string;
45
46
  }, E extends {
46
47
  readonly _tag: string;
47
- }, R>(id: string, persistentMachine: PersistentMachine<S, E, R>) => Effect.Effect<Option.None<PersistentActorRef<S, E, R>> | Option.Some<PersistentActorRef<S, E, R>>, unknown, unknown>;
48
+ }, R>(id: string, persistentMachine: PersistentMachine<S, E, R>) => Effect.Effect<Option.None<PersistentActorRef<S, E, R>> | Option.Some<PersistentActorRef<S, E, R>>, PersistenceError, PersistenceAdapterTag | Exclude<R, MachineContext<S, E, MachineRef<E>>> | Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
48
49
  //#endregion
49
50
  export { PersistentActorRef, createPersistentActor, restorePersistentActor };