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.
- package/README.md +76 -16
- package/dist/_virtual/_rolldown/runtime.js +6 -11
- package/dist/actor.d.ts +58 -72
- package/dist/actor.js +166 -32
- package/dist/cluster/entity-machine.d.ts +0 -1
- package/dist/cluster/entity-machine.js +6 -6
- package/dist/cluster/index.js +1 -2
- package/dist/cluster/to-entity.js +1 -3
- package/dist/errors.d.ts +12 -1
- package/dist/errors.js +8 -3
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -3
- package/dist/inspection.js +1 -3
- package/dist/internal/inspection.js +1 -3
- package/dist/internal/transition.d.ts +26 -2
- package/dist/internal/transition.js +37 -10
- package/dist/internal/utils.d.ts +7 -2
- package/dist/internal/utils.js +1 -3
- package/dist/machine.d.ts +66 -4
- package/dist/machine.js +67 -31
- package/dist/persistence/adapter.js +1 -3
- package/dist/persistence/adapters/in-memory.js +1 -3
- package/dist/persistence/index.js +1 -2
- package/dist/persistence/persistent-actor.js +54 -19
- package/dist/persistence/persistent-machine.js +1 -3
- package/dist/schema.js +1 -3
- package/dist/slot.js +1 -3
- package/dist/testing.js +58 -6
- package/package.json +19 -18
- package/v3/dist/_virtual/_rolldown/runtime.js +13 -0
- package/{dist-v3 → v3/dist}/actor.d.ts +65 -78
- package/{dist-v3 → v3/dist}/actor.js +173 -37
- package/{dist-v3 → v3/dist}/cluster/entity-machine.d.ts +1 -2
- package/{dist-v3 → v3/dist}/cluster/entity-machine.js +9 -9
- package/{dist-v3 → v3/dist}/cluster/index.js +1 -2
- package/{dist-v3 → v3/dist}/cluster/to-entity.d.ts +1 -1
- package/{dist-v3 → v3/dist}/cluster/to-entity.js +2 -4
- package/v3/dist/errors.d.ts +76 -0
- package/{dist-v3 → v3/dist}/errors.js +9 -4
- package/v3/dist/index.d.ts +13 -0
- package/v3/dist/index.js +13 -0
- package/{dist-v3 → v3/dist}/inspection.d.ts +53 -8
- package/v3/dist/inspection.js +156 -0
- package/{dist-v3 → v3/dist}/internal/brands.d.ts +1 -1
- package/{dist-v3 → v3/dist}/internal/inspection.d.ts +1 -1
- package/v3/dist/internal/inspection.js +20 -0
- package/{dist-v3 → v3/dist}/internal/transition.d.ts +35 -11
- package/{dist-v3 → v3/dist}/internal/transition.js +47 -17
- package/{dist-v3 → v3/dist}/internal/utils.d.ts +9 -4
- package/{dist-v3 → v3/dist}/internal/utils.js +2 -4
- package/{dist-v3 → v3/dist}/machine.d.ts +86 -10
- package/{dist-v3 → v3/dist}/machine.js +130 -33
- package/{dist-v3 → v3/dist}/persistence/adapter.d.ts +18 -5
- package/{dist-v3 → v3/dist}/persistence/adapter.js +2 -4
- package/{dist-v3 → v3/dist}/persistence/adapters/in-memory.d.ts +1 -1
- package/{dist-v3 → v3/dist}/persistence/adapters/in-memory.js +2 -4
- package/{dist-v3 → v3/dist}/persistence/index.js +1 -2
- package/{dist-v3 → v3/dist}/persistence/persistent-actor.d.ts +7 -6
- package/{dist-v3 → v3/dist}/persistence/persistent-actor.js +59 -22
- package/{dist-v3 → v3/dist}/persistence/persistent-machine.d.ts +1 -1
- package/{dist-v3 → v3/dist}/persistence/persistent-machine.js +2 -4
- package/{dist-v3 → v3/dist}/schema.d.ts +1 -1
- package/{dist-v3 → v3/dist}/schema.js +6 -5
- package/{dist-v3 → v3/dist}/slot.d.ts +4 -3
- package/{dist-v3 → v3/dist}/slot.js +2 -4
- package/{dist-v3 → v3/dist}/testing.d.ts +14 -8
- package/{dist-v3 → v3/dist}/testing.js +61 -9
- package/dist-v3/_virtual/_rolldown/runtime.js +0 -18
- package/dist-v3/errors.d.ts +0 -27
- package/dist-v3/index.d.ts +0 -13
- package/dist-v3/index.js +0 -14
- package/dist-v3/inspection.js +0 -50
- package/dist-v3/internal/inspection.js +0 -15
- /package/{dist-v3 → v3/dist}/cluster/index.d.ts +0 -0
- /package/{dist-v3 → v3/dist}/internal/brands.js +0 -0
- /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
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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))
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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 {
|
|
3
|
-
import {
|
|
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
|
|
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>,
|
|
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>>,
|
|
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 };
|