effect-machine 0.9.0 → 0.11.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 +118 -55
- package/dist/actor.d.ts +77 -179
- package/dist/actor.js +161 -113
- package/dist/cluster/entity-machine.js +5 -3
- package/dist/errors.d.ts +12 -1
- package/dist/errors.js +8 -1
- package/dist/index.d.ts +4 -8
- package/dist/index.js +2 -7
- package/dist/internal/transition.d.ts +27 -3
- package/dist/internal/transition.js +38 -9
- package/dist/internal/utils.d.ts +7 -2
- package/dist/internal/utils.js +1 -5
- package/dist/machine.d.ts +94 -35
- package/dist/machine.js +128 -13
- package/dist/testing.js +57 -3
- package/package.json +10 -9
- package/v3/dist/actor.d.ts +210 -0
- package/{dist-v3 → v3/dist}/actor.js +198 -117
- package/{dist-v3 → v3/dist}/cluster/entity-machine.d.ts +1 -1
- package/{dist-v3 → v3/dist}/cluster/entity-machine.js +8 -6
- package/{dist-v3 → v3/dist}/cluster/to-entity.d.ts +1 -1
- package/{dist-v3 → v3/dist}/cluster/to-entity.js +1 -1
- package/v3/dist/errors.d.ts +76 -0
- package/{dist-v3 → v3/dist}/errors.js +9 -2
- package/v3/dist/index.d.ts +9 -0
- package/v3/dist/index.js +8 -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 -15
- package/{dist-v3 → v3/dist}/internal/utils.d.ts +9 -4
- package/{dist-v3 → v3/dist}/internal/utils.js +2 -6
- package/{dist-v3 → v3/dist}/machine.d.ts +113 -40
- package/{dist-v3 → v3/dist}/machine.js +191 -15
- package/{dist-v3 → v3/dist}/schema.d.ts +1 -1
- package/{dist-v3 → v3/dist}/schema.js +5 -2
- package/{dist-v3 → v3/dist}/slot.d.ts +4 -3
- package/{dist-v3 → v3/dist}/slot.js +1 -1
- package/{dist-v3 → v3/dist}/testing.d.ts +14 -8
- package/{dist-v3 → v3/dist}/testing.js +60 -6
- package/dist/persistence/adapter.d.ts +0 -135
- package/dist/persistence/adapter.js +0 -25
- package/dist/persistence/adapters/in-memory.d.ts +0 -32
- package/dist/persistence/adapters/in-memory.js +0 -174
- package/dist/persistence/index.d.ts +0 -5
- package/dist/persistence/index.js +0 -5
- package/dist/persistence/persistent-actor.d.ts +0 -50
- package/dist/persistence/persistent-actor.js +0 -368
- package/dist/persistence/persistent-machine.d.ts +0 -105
- package/dist/persistence/persistent-machine.js +0 -22
- package/dist-v3/actor.d.ts +0 -291
- package/dist-v3/errors.d.ts +0 -27
- package/dist-v3/index.d.ts +0 -12
- package/dist-v3/index.js +0 -13
- package/dist-v3/inspection.js +0 -48
- package/dist-v3/internal/inspection.js +0 -13
- package/dist-v3/persistence/adapter.d.ts +0 -125
- package/dist-v3/persistence/adapter.js +0 -25
- package/dist-v3/persistence/adapters/in-memory.d.ts +0 -32
- package/dist-v3/persistence/adapters/in-memory.js +0 -174
- package/dist-v3/persistence/index.d.ts +0 -5
- package/dist-v3/persistence/index.js +0 -5
- package/dist-v3/persistence/persistent-actor.d.ts +0 -49
- package/dist-v3/persistence/persistent-actor.js +0 -365
- package/dist-v3/persistence/persistent-machine.d.ts +0 -105
- package/dist-v3/persistence/persistent-machine.js +0 -22
- /package/{dist-v3 → v3/dist}/_virtual/_rolldown/runtime.js +0 -0
- /package/{dist-v3 → v3/dist}/cluster/index.d.ts +0 -0
- /package/{dist-v3 → v3/dist}/cluster/index.js +0 -0
- /package/{dist-v3 → v3/dist}/internal/brands.js +0 -0
|
@@ -1,22 +1,23 @@
|
|
|
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
|
-
import { PersistenceConfig, PersistentMachine } from "./persistence/persistent-machine.js";
|
|
5
5
|
import { DuplicateActorError } from "./errors.js";
|
|
6
|
-
import { EffectHandlers, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers, GuardSlots, GuardsDef, GuardsSchema, MachineContext } from "./slot.js";
|
|
7
6
|
import { findTransitions } from "./internal/transition.js";
|
|
8
7
|
import { ActorRef, ActorSystem } from "./actor.js";
|
|
9
|
-
import { Cause, Context,
|
|
8
|
+
import { Cause, Context, Duration, Effect, Schema, Scope } from "effect";
|
|
10
9
|
|
|
11
|
-
//#region src
|
|
10
|
+
//#region src/machine.d.ts
|
|
12
11
|
declare namespace machine_d_exports {
|
|
13
|
-
export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig,
|
|
12
|
+
export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, make, replay, spawn };
|
|
14
13
|
}
|
|
15
14
|
/**
|
|
16
15
|
* Self reference for sending events back to the machine
|
|
17
16
|
*/
|
|
18
17
|
interface MachineRef<Event> {
|
|
19
18
|
readonly send: (event: Event) => Effect.Effect<void>;
|
|
19
|
+
/** Fire-and-forget alias for send (OTP gen_server:cast). */
|
|
20
|
+
readonly cast: (event: Event) => Effect.Effect<void>;
|
|
20
21
|
readonly spawn: <S2 extends {
|
|
21
22
|
readonly _tag: string;
|
|
22
23
|
}, E2 extends {
|
|
@@ -36,6 +37,7 @@ interface HandlerContext<State, Event, GD extends GuardsDef, ED extends EffectsD
|
|
|
36
37
|
* Handler context passed to state effect handlers (onEnter, spawn, background)
|
|
37
38
|
*/
|
|
38
39
|
interface StateHandlerContext<State, Event, ED extends EffectsDef> {
|
|
40
|
+
readonly actorId: string;
|
|
39
41
|
readonly state: State;
|
|
40
42
|
readonly event: Event;
|
|
41
43
|
readonly self: MachineRef<Event>;
|
|
@@ -72,13 +74,22 @@ interface SpawnEffect<State, Event, ED extends EffectsDef, R> {
|
|
|
72
74
|
interface BackgroundEffect<State, Event, ED extends EffectsDef, R> {
|
|
73
75
|
readonly handler: StateEffectHandler<State, Event, ED, R>;
|
|
74
76
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
readonly
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
interface TaskOptions<State, Event, ED extends EffectsDef, A, E1, ES, EF> {
|
|
78
|
+
readonly onSuccess: (value: A, ctx: StateHandlerContext<State, Event, ED>) => ES;
|
|
79
|
+
readonly onFailure?: (cause: Cause.Cause<E1>, ctx: StateHandlerContext<State, Event, ED>) => EF;
|
|
80
|
+
readonly name?: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Configuration for `.timeout()` — gen_statem-style state timeouts.
|
|
84
|
+
*
|
|
85
|
+
* Entering the state starts a timer. Leaving cancels it.
|
|
86
|
+
* `.reenter()` restarts the timer with fresh state values.
|
|
87
|
+
*/
|
|
88
|
+
interface TimeoutConfig<State, Event> {
|
|
89
|
+
/** Duration before firing. Static or derived from current state. */
|
|
90
|
+
readonly duration: Duration.DurationInput | ((state: State) => Duration.DurationInput);
|
|
91
|
+
/** Event to send when the timer fires. Static or derived from current state. */
|
|
92
|
+
readonly event: Event | ((state: State) => Event);
|
|
82
93
|
}
|
|
83
94
|
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
84
95
|
type IsUnknown<T> = unknown extends T ? ([T] extends [unknown] ? true : false) : false;
|
|
@@ -117,11 +128,6 @@ declare class BuiltMachine<State, Event, R = never> {
|
|
|
117
128
|
/** @internal */
|
|
118
129
|
constructor(machine: Machine<State, Event, R, any, any, any, any>);
|
|
119
130
|
get initial(): State;
|
|
120
|
-
persist(config: PersistOptions): PersistentMachine<State & {
|
|
121
|
-
readonly _tag: string;
|
|
122
|
-
}, Event & {
|
|
123
|
-
readonly _tag: string;
|
|
124
|
-
}, R>;
|
|
125
131
|
}
|
|
126
132
|
/**
|
|
127
133
|
* Machine definition with fluent builder API.
|
|
@@ -146,6 +152,11 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
146
152
|
/** @internal */
|
|
147
153
|
readonly _finalStates: Set<string>;
|
|
148
154
|
/** @internal */
|
|
155
|
+
readonly _postponeRules: Array<{
|
|
156
|
+
readonly stateTag: string;
|
|
157
|
+
readonly eventTag: string;
|
|
158
|
+
}>;
|
|
159
|
+
/** @internal */
|
|
149
160
|
readonly _guardsSchema?: GuardsSchema<GD>;
|
|
150
161
|
/** @internal */
|
|
151
162
|
readonly _effectsSchema?: EffectsSchema<EFD>;
|
|
@@ -169,10 +180,18 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
169
180
|
get spawnEffects(): ReadonlyArray<SpawnEffect<State, Event, EFD, R>>;
|
|
170
181
|
get backgroundEffects(): ReadonlyArray<BackgroundEffect<State, Event, EFD, R>>;
|
|
171
182
|
get finalStates(): ReadonlySet<string>;
|
|
183
|
+
get postponeRules(): ReadonlyArray<{
|
|
184
|
+
readonly stateTag: string;
|
|
185
|
+
readonly eventTag: string;
|
|
186
|
+
}>;
|
|
172
187
|
get guardsSchema(): GuardsSchema<GD> | undefined;
|
|
173
188
|
get effectsSchema(): EffectsSchema<EFD> | undefined;
|
|
174
189
|
/** @internal */
|
|
175
190
|
constructor(initial: State, stateSchema?: Schema.Schema<State, unknown, never>, eventSchema?: Schema.Schema<Event, unknown, never>, guardsSchema?: GuardsSchema<GD>, effectsSchema?: EffectsSchema<EFD>);
|
|
191
|
+
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>;
|
|
192
|
+
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>;
|
|
193
|
+
/** @internal */
|
|
194
|
+
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>;
|
|
176
195
|
/** Register transition for a single state */
|
|
177
196
|
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>;
|
|
178
197
|
/** Register transition for multiple states (handler receives union of state types) */
|
|
@@ -219,10 +238,29 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
219
238
|
* State-scoped task that runs on entry and sends success/failure events.
|
|
220
239
|
* Interrupts do not emit failure events.
|
|
221
240
|
*/
|
|
222
|
-
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:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
241
|
+
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>;
|
|
242
|
+
/**
|
|
243
|
+
* State timeout — gen_statem's `state_timeout`.
|
|
244
|
+
*
|
|
245
|
+
* Entering the state starts a timer. Leaving cancels it (via state scope).
|
|
246
|
+
* `.reenter()` restarts the timer with fresh state values.
|
|
247
|
+
* Compiles to `.task()` internally — preserves `@machine.task` inspection events.
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* machine
|
|
252
|
+
* .timeout(State.Loading, {
|
|
253
|
+
* duration: Duration.seconds(30),
|
|
254
|
+
* event: Event.Timeout,
|
|
255
|
+
* })
|
|
256
|
+
* // Dynamic duration from state
|
|
257
|
+
* .timeout(State.Retrying, {
|
|
258
|
+
* duration: (state) => Duration.seconds(state.backoff),
|
|
259
|
+
* event: Event.GiveUp,
|
|
260
|
+
* })
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
timeout<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, config: TimeoutConfig<NS, VariantsUnion<_ED> & BrandedEvent>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
226
264
|
/**
|
|
227
265
|
* Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
|
|
228
266
|
* Use effect slots defined via `Slot.Effects` for the actual work.
|
|
@@ -244,6 +282,24 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
244
282
|
* ```
|
|
245
283
|
*/
|
|
246
284
|
background(handler: StateEffectHandler<State, Event, EFD, Scope.Scope>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
285
|
+
/**
|
|
286
|
+
* Postpone events — gen_statem's event postpone.
|
|
287
|
+
*
|
|
288
|
+
* When a matching event arrives in the given state, it is buffered instead of
|
|
289
|
+
* processed. After the next state transition (tag change), all buffered events
|
|
290
|
+
* are drained through the loop in FIFO order.
|
|
291
|
+
*
|
|
292
|
+
* Reply-bearing events (from `call`/`ask`) in the postpone buffer are settled
|
|
293
|
+
* with `ActorStoppedError` on stop/interrupt/final-state.
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```ts
|
|
297
|
+
* machine
|
|
298
|
+
* .postpone(State.Connecting, Event.Data) // single event
|
|
299
|
+
* .postpone(State.Connecting, [Event.Data, Event.Cmd]) // multiple events
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
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>;
|
|
247
303
|
final<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
248
304
|
/**
|
|
249
305
|
* Finalize the machine. Returns a `BuiltMachine` — the only type accepted by `Machine.spawn`.
|
|
@@ -252,26 +308,43 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
252
308
|
* - Machines without slots: call with no arguments.
|
|
253
309
|
*/
|
|
254
310
|
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>>;
|
|
255
|
-
/** @internal Persist from raw Machine — prefer BuiltMachine.persist() */
|
|
256
|
-
persist(config: PersistOptions): PersistentMachine<State & {
|
|
257
|
-
readonly _tag: string;
|
|
258
|
-
}, Event & {
|
|
259
|
-
readonly _tag: string;
|
|
260
|
-
}, R>;
|
|
261
311
|
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>;
|
|
262
312
|
}
|
|
313
|
+
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> {
|
|
314
|
+
private readonly machine;
|
|
315
|
+
private readonly states;
|
|
316
|
+
constructor(machine: Machine<State, Event, R, _SD, _ED, GD, EFD>, states: ReadonlyArray<TaggedOrConstructor<SelectedState>>);
|
|
317
|
+
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>;
|
|
318
|
+
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>;
|
|
319
|
+
}
|
|
263
320
|
declare const make: typeof Machine.make;
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
321
|
+
/**
|
|
322
|
+
* Spawn an actor from a built machine.
|
|
323
|
+
*
|
|
324
|
+
* Options:
|
|
325
|
+
* - `id` — custom actor ID (default: random)
|
|
326
|
+
* - `hydrate` — restore from a previously-saved state snapshot.
|
|
327
|
+
* The actor starts in the hydrated state and re-runs spawn effects
|
|
328
|
+
* for that state (timers, scoped resources, etc.). Transition history
|
|
329
|
+
* is not replayed — only the current state's entry effects run.
|
|
330
|
+
*
|
|
331
|
+
* Persistence is composed in userland by observing `actor.changes`
|
|
332
|
+
* and saving snapshots to your own storage.
|
|
333
|
+
*/
|
|
334
|
+
declare const spawn: <S extends {
|
|
335
|
+
readonly _tag: string;
|
|
336
|
+
}, E extends {
|
|
337
|
+
readonly _tag: string;
|
|
338
|
+
}, R>(machine: BuiltMachine<S, E, R>, idOrOptions?: string | {
|
|
339
|
+
id?: string;
|
|
340
|
+
hydrate?: S;
|
|
341
|
+
}) => Effect.Effect<ActorRef<S, E>, never, R>;
|
|
342
|
+
declare const replay: <S extends {
|
|
343
|
+
readonly _tag: string;
|
|
344
|
+
}, E extends {
|
|
345
|
+
readonly _tag: string;
|
|
346
|
+
}, R>(machine: BuiltMachine<S, E, R>, events: ReadonlyArray<E>, options?: {
|
|
347
|
+
from?: S;
|
|
348
|
+
}) => Effect.Effect<S, never, R>;
|
|
276
349
|
//#endregion
|
|
277
|
-
export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig,
|
|
350
|
+
export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, machine_d_exports, make, replay, spawn };
|
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
-
import {
|
|
2
|
+
import { Inspector } from "./inspection.js";
|
|
3
|
+
import { getTag, stubSystem } from "./internal/utils.js";
|
|
3
4
|
import { ProvisionValidationError, SlotProvisionError } from "./errors.js";
|
|
4
|
-
import {
|
|
5
|
+
import { emitWithTimestamp } from "./internal/inspection.js";
|
|
5
6
|
import { MachineContextTag } from "./slot.js";
|
|
6
|
-
import { findTransitions, invalidateIndex } from "./internal/transition.js";
|
|
7
|
+
import { findTransitions, invalidateIndex, resolveTransition, runTransitionHandler, shouldPostpone } from "./internal/transition.js";
|
|
7
8
|
import { createActor } from "./actor.js";
|
|
8
9
|
import { Cause, Effect, Exit, Option, Scope } from "effect";
|
|
9
|
-
//#region src
|
|
10
|
+
//#region src/machine.ts
|
|
10
11
|
var machine_exports = /* @__PURE__ */ __exportAll({
|
|
11
12
|
BuiltMachine: () => BuiltMachine,
|
|
12
13
|
Machine: () => Machine,
|
|
13
14
|
findTransitions: () => findTransitions,
|
|
14
15
|
make: () => make,
|
|
16
|
+
replay: () => replay,
|
|
15
17
|
spawn: () => spawn
|
|
16
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
|
+
})));
|
|
17
28
|
/**
|
|
18
29
|
* A finalized machine ready for spawning.
|
|
19
30
|
*
|
|
@@ -31,9 +42,6 @@ var BuiltMachine = class {
|
|
|
31
42
|
get initial() {
|
|
32
43
|
return this._inner.initial;
|
|
33
44
|
}
|
|
34
|
-
persist(config) {
|
|
35
|
-
return this._inner.persist(config);
|
|
36
|
-
}
|
|
37
45
|
};
|
|
38
46
|
/**
|
|
39
47
|
* Machine definition with fluent builder API.
|
|
@@ -53,6 +61,7 @@ var Machine = class Machine {
|
|
|
53
61
|
/** @internal */ _spawnEffects;
|
|
54
62
|
/** @internal */ _backgroundEffects;
|
|
55
63
|
/** @internal */ _finalStates;
|
|
64
|
+
/** @internal */ _postponeRules;
|
|
56
65
|
/** @internal */ _guardsSchema;
|
|
57
66
|
/** @internal */ _effectsSchema;
|
|
58
67
|
/** @internal */ _guardHandlers;
|
|
@@ -77,6 +86,9 @@ var Machine = class Machine {
|
|
|
77
86
|
get finalStates() {
|
|
78
87
|
return this._finalStates;
|
|
79
88
|
}
|
|
89
|
+
get postponeRules() {
|
|
90
|
+
return this._postponeRules;
|
|
91
|
+
}
|
|
80
92
|
get guardsSchema() {
|
|
81
93
|
return this._guardsSchema;
|
|
82
94
|
}
|
|
@@ -90,6 +102,7 @@ var Machine = class Machine {
|
|
|
90
102
|
this._spawnEffects = [];
|
|
91
103
|
this._backgroundEffects = [];
|
|
92
104
|
this._finalStates = /* @__PURE__ */ new Set();
|
|
105
|
+
this._postponeRules = [];
|
|
93
106
|
this._guardsSchema = guardsSchema;
|
|
94
107
|
this._effectsSchema = effectsSchema;
|
|
95
108
|
this._guardHandlers = /* @__PURE__ */ new Map();
|
|
@@ -116,6 +129,15 @@ var Machine = class Machine {
|
|
|
116
129
|
})) : {}
|
|
117
130
|
};
|
|
118
131
|
}
|
|
132
|
+
from(stateOrStates, build) {
|
|
133
|
+
build(new TransitionScope(this, Array.isArray(stateOrStates) ? stateOrStates : [stateOrStates]));
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
/** @internal */
|
|
137
|
+
scopeTransition(states, event, handler, reenter) {
|
|
138
|
+
for (const state of states) this.addTransition(state, event, handler, reenter);
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
119
141
|
on(stateOrStates, event, handler) {
|
|
120
142
|
const states = Array.isArray(stateOrStates) ? stateOrStates : [stateOrStates];
|
|
121
143
|
for (const s of states) this.addTransition(s, event, handler, false);
|
|
@@ -190,14 +212,41 @@ var Machine = class Machine {
|
|
|
190
212
|
*/
|
|
191
213
|
task(state, run, options) {
|
|
192
214
|
const handler = Effect.fn("effect-machine.task")(function* (ctx) {
|
|
215
|
+
yield* emitTaskInspection({
|
|
216
|
+
actorId: ctx.actorId,
|
|
217
|
+
state: ctx.state,
|
|
218
|
+
taskName: options.name,
|
|
219
|
+
phase: "start"
|
|
220
|
+
});
|
|
193
221
|
const exit = yield* Effect.exit(run(ctx));
|
|
194
222
|
if (Exit.isSuccess(exit)) {
|
|
223
|
+
yield* emitTaskInspection({
|
|
224
|
+
actorId: ctx.actorId,
|
|
225
|
+
state: ctx.state,
|
|
226
|
+
taskName: options.name,
|
|
227
|
+
phase: "success"
|
|
228
|
+
});
|
|
195
229
|
yield* ctx.self.send(options.onSuccess(exit.value, ctx));
|
|
196
230
|
yield* Effect.yieldNow();
|
|
197
231
|
return;
|
|
198
232
|
}
|
|
199
233
|
const cause = exit.cause;
|
|
200
|
-
if (Cause.isInterruptedOnly(cause))
|
|
234
|
+
if (Cause.isInterruptedOnly(cause)) {
|
|
235
|
+
yield* emitTaskInspection({
|
|
236
|
+
actorId: ctx.actorId,
|
|
237
|
+
state: ctx.state,
|
|
238
|
+
taskName: options.name,
|
|
239
|
+
phase: "interrupt"
|
|
240
|
+
});
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
yield* emitTaskInspection({
|
|
244
|
+
actorId: ctx.actorId,
|
|
245
|
+
state: ctx.state,
|
|
246
|
+
taskName: options.name,
|
|
247
|
+
phase: "failure",
|
|
248
|
+
error: Cause.pretty(cause)
|
|
249
|
+
});
|
|
201
250
|
if (options.onFailure !== void 0) {
|
|
202
251
|
yield* ctx.self.send(options.onFailure(cause, ctx));
|
|
203
252
|
yield* Effect.yieldNow();
|
|
@@ -208,6 +257,36 @@ var Machine = class Machine {
|
|
|
208
257
|
return this.spawn(state, handler);
|
|
209
258
|
}
|
|
210
259
|
/**
|
|
260
|
+
* State timeout — gen_statem's `state_timeout`.
|
|
261
|
+
*
|
|
262
|
+
* Entering the state starts a timer. Leaving cancels it (via state scope).
|
|
263
|
+
* `.reenter()` restarts the timer with fresh state values.
|
|
264
|
+
* Compiles to `.task()` internally — preserves `@machine.task` inspection events.
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```ts
|
|
268
|
+
* machine
|
|
269
|
+
* .timeout(State.Loading, {
|
|
270
|
+
* duration: Duration.seconds(30),
|
|
271
|
+
* event: Event.Timeout,
|
|
272
|
+
* })
|
|
273
|
+
* // Dynamic duration from state
|
|
274
|
+
* .timeout(State.Retrying, {
|
|
275
|
+
* duration: (state) => Duration.seconds(state.backoff),
|
|
276
|
+
* event: Event.GiveUp,
|
|
277
|
+
* })
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
timeout(state, config) {
|
|
281
|
+
const stateTag = getTag(state);
|
|
282
|
+
const resolveDuration = typeof config.duration === "function" ? config.duration : () => config.duration;
|
|
283
|
+
const resolveEvent = typeof config.event === "function" ? config.event : () => config.event;
|
|
284
|
+
return this.task(state, (ctx) => Effect.sleep(resolveDuration(ctx.state)), {
|
|
285
|
+
onSuccess: (_, ctx) => resolveEvent(ctx.state),
|
|
286
|
+
name: `$timeout:${stateTag}`
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
211
290
|
* Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
|
|
212
291
|
* Use effect slots defined via `Slot.Effects` for the actual work.
|
|
213
292
|
*
|
|
@@ -231,6 +310,35 @@ var Machine = class Machine {
|
|
|
231
310
|
this._backgroundEffects.push({ handler });
|
|
232
311
|
return this;
|
|
233
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Postpone events — gen_statem's event postpone.
|
|
315
|
+
*
|
|
316
|
+
* When a matching event arrives in the given state, it is buffered instead of
|
|
317
|
+
* processed. After the next state transition (tag change), all buffered events
|
|
318
|
+
* are drained through the loop in FIFO order.
|
|
319
|
+
*
|
|
320
|
+
* Reply-bearing events (from `call`/`ask`) in the postpone buffer are settled
|
|
321
|
+
* with `ActorStoppedError` on stop/interrupt/final-state.
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ```ts
|
|
325
|
+
* machine
|
|
326
|
+
* .postpone(State.Connecting, Event.Data) // single event
|
|
327
|
+
* .postpone(State.Connecting, [Event.Data, Event.Cmd]) // multiple events
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
postpone(state, events) {
|
|
331
|
+
const stateTag = getTag(state);
|
|
332
|
+
const eventList = Array.isArray(events) ? events : [events];
|
|
333
|
+
for (const ev of eventList) {
|
|
334
|
+
const eventTag = getTag(ev);
|
|
335
|
+
this._postponeRules.push({
|
|
336
|
+
stateTag,
|
|
337
|
+
eventTag
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
return this;
|
|
341
|
+
}
|
|
234
342
|
final(state) {
|
|
235
343
|
const stateTag = getTag(state);
|
|
236
344
|
this._finalStates.add(stateTag);
|
|
@@ -262,6 +370,7 @@ var Machine = class Machine {
|
|
|
262
370
|
result._finalStates = new Set(this._finalStates);
|
|
263
371
|
result._spawnEffects = [...this._spawnEffects];
|
|
264
372
|
result._backgroundEffects = [...this._backgroundEffects];
|
|
373
|
+
result._postponeRules = [...this._postponeRules];
|
|
265
374
|
const anyHandlers = handlers;
|
|
266
375
|
if (this._guardsSchema !== void 0) for (const name of Object.keys(this._guardsSchema.definitions)) result._guardHandlers.set(name, anyHandlers[name]);
|
|
267
376
|
if (this._effectsSchema !== void 0) for (const name of Object.keys(this._effectsSchema.definitions)) result._effectHandlers.set(name, anyHandlers[name]);
|
|
@@ -269,20 +378,87 @@ var Machine = class Machine {
|
|
|
269
378
|
}
|
|
270
379
|
return new BuiltMachine(this);
|
|
271
380
|
}
|
|
272
|
-
/** @internal Persist from raw Machine — prefer BuiltMachine.persist() */
|
|
273
|
-
persist(config) {
|
|
274
|
-
return persist(config)(this);
|
|
275
|
-
}
|
|
276
381
|
static make(config) {
|
|
277
382
|
return new Machine(config.initial, config.state, config.event, config.guards, config.effects);
|
|
278
383
|
}
|
|
279
384
|
};
|
|
385
|
+
var TransitionScope = class {
|
|
386
|
+
constructor(machine, states) {
|
|
387
|
+
this.machine = machine;
|
|
388
|
+
this.states = states;
|
|
389
|
+
}
|
|
390
|
+
on(event, handler) {
|
|
391
|
+
this.machine.scopeTransition(this.states, event, handler, false);
|
|
392
|
+
return this;
|
|
393
|
+
}
|
|
394
|
+
reenter(event, handler) {
|
|
395
|
+
this.machine.scopeTransition(this.states, event, handler, true);
|
|
396
|
+
return this;
|
|
397
|
+
}
|
|
398
|
+
};
|
|
280
399
|
const make = Machine.make;
|
|
281
|
-
|
|
282
|
-
|
|
400
|
+
/**
|
|
401
|
+
* Spawn an actor from a built machine.
|
|
402
|
+
*
|
|
403
|
+
* Options:
|
|
404
|
+
* - `id` — custom actor ID (default: random)
|
|
405
|
+
* - `hydrate` — restore from a previously-saved state snapshot.
|
|
406
|
+
* The actor starts in the hydrated state and re-runs spawn effects
|
|
407
|
+
* for that state (timers, scoped resources, etc.). Transition history
|
|
408
|
+
* is not replayed — only the current state's entry effects run.
|
|
409
|
+
*
|
|
410
|
+
* Persistence is composed in userland by observing `actor.changes`
|
|
411
|
+
* and saving snapshots to your own storage.
|
|
412
|
+
*/
|
|
413
|
+
const spawn = Effect.fn("effect-machine.spawn")(function* (built, idOrOptions) {
|
|
414
|
+
const opts = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
415
|
+
const actor = yield* createActor(opts?.id ?? `actor-${Math.random().toString(36).slice(2)}`, built._inner, { initialState: opts?.hydrate });
|
|
283
416
|
const maybeScope = yield* Effect.serviceOption(Scope.Scope);
|
|
284
417
|
if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, actor.stop);
|
|
285
418
|
return actor;
|
|
286
419
|
});
|
|
420
|
+
const replay = Effect.fn("effect-machine.replay")(function* (built, events, options) {
|
|
421
|
+
const machine = built._inner;
|
|
422
|
+
let state = options?.from ?? machine.initial;
|
|
423
|
+
const hasPostponeRules = machine.postponeRules.length > 0;
|
|
424
|
+
const postponed = [];
|
|
425
|
+
const dummySend = Effect.fn("effect-machine.replay.send")((_event) => Effect.void);
|
|
426
|
+
const self = {
|
|
427
|
+
send: dummySend,
|
|
428
|
+
cast: dummySend,
|
|
429
|
+
spawn: () => Effect.die("spawn not supported in replay")
|
|
430
|
+
};
|
|
431
|
+
for (const event of events) {
|
|
432
|
+
if (machine.finalStates.has(state._tag)) break;
|
|
433
|
+
if (hasPostponeRules && shouldPostpone(machine, state._tag, event._tag)) {
|
|
434
|
+
postponed.push(event);
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
const transition = resolveTransition(machine, state, event);
|
|
438
|
+
if (transition !== void 0) {
|
|
439
|
+
const result = yield* runTransitionHandler(machine, transition, state, event, self, stubSystem, "replay");
|
|
440
|
+
const previousTag = state._tag;
|
|
441
|
+
state = result.newState;
|
|
442
|
+
if ((state._tag !== previousTag || transition.reenter === true) && postponed.length > 0) {
|
|
443
|
+
let drainTag = previousTag;
|
|
444
|
+
while (state._tag !== drainTag && postponed.length > 0) {
|
|
445
|
+
if (machine.finalStates.has(state._tag)) break;
|
|
446
|
+
drainTag = state._tag;
|
|
447
|
+
const drained = postponed.splice(0);
|
|
448
|
+
for (const postponedEvent of drained) {
|
|
449
|
+
if (machine.finalStates.has(state._tag)) break;
|
|
450
|
+
if (shouldPostpone(machine, state._tag, postponedEvent._tag)) {
|
|
451
|
+
postponed.push(postponedEvent);
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
const pTransition = resolveTransition(machine, state, postponedEvent);
|
|
455
|
+
if (pTransition !== void 0) state = (yield* runTransitionHandler(machine, pTransition, state, postponedEvent, self, stubSystem, "replay")).newState;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return state;
|
|
462
|
+
});
|
|
287
463
|
//#endregion
|
|
288
|
-
export { BuiltMachine, Machine, findTransitions, machine_exports, make, spawn };
|
|
464
|
+
export { BuiltMachine, Machine, findTransitions, machine_exports, make, replay, spawn };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { InvalidSchemaError, MissingMatchHandlerError } from "./errors.js";
|
|
2
2
|
import { Schema } from "effect";
|
|
3
|
-
//#region src
|
|
3
|
+
//#region src/schema.ts
|
|
4
4
|
/**
|
|
5
5
|
* Schema-first State/Event definitions for effect-machine.
|
|
6
6
|
*
|
|
@@ -58,7 +58,10 @@ const buildMachineSchema = (definition) => {
|
|
|
58
58
|
constructor.derive = (source, partial) => {
|
|
59
59
|
const result = { _tag: tag };
|
|
60
60
|
for (const key of fieldNames) if (key in source) result[key] = source[key];
|
|
61
|
-
if (partial !== void 0) for (const [key, value] of Object.entries(partial))
|
|
61
|
+
if (partial !== void 0) for (const [key, value] of Object.entries(partial)) {
|
|
62
|
+
if (key === "_tag") continue;
|
|
63
|
+
result[key] = value;
|
|
64
|
+
}
|
|
62
65
|
return result;
|
|
63
66
|
};
|
|
64
67
|
constructors[tag] = constructor;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ActorSystem } from "./actor.js";
|
|
2
|
-
import { Effect, Schema } from "effect";
|
|
2
|
+
import { Context, Effect, Schema } from "effect";
|
|
3
3
|
|
|
4
|
-
//#region src
|
|
4
|
+
//#region src/slot.d.ts
|
|
5
5
|
/** Schema fields definition (like Schema.Struct.Fields) */
|
|
6
6
|
type Fields = Record<string, Schema.Schema.All>;
|
|
7
7
|
/** Extract the encoded type from schema fields (used for parameters) */
|
|
@@ -43,6 +43,7 @@ type EffectSlots<D extends EffectsDef> = { readonly [K in keyof D & string]: Eff
|
|
|
43
43
|
* Shared across all machines via MachineContextTag.
|
|
44
44
|
*/
|
|
45
45
|
interface MachineContext<State, Event, Self> {
|
|
46
|
+
readonly actorId: string;
|
|
46
47
|
readonly state: State;
|
|
47
48
|
readonly event: Event;
|
|
48
49
|
readonly self: Self;
|
|
@@ -53,7 +54,7 @@ interface MachineContext<State, Event, Self> {
|
|
|
53
54
|
* Single module-level tag instead of per-machine allocation.
|
|
54
55
|
* @internal
|
|
55
56
|
*/
|
|
56
|
-
declare const MachineContextTag: any
|
|
57
|
+
declare const MachineContextTag: Context.Tag<MachineContext<any, any, any>, MachineContext<any, any, any>>;
|
|
57
58
|
/**
|
|
58
59
|
* Guard handler implementation.
|
|
59
60
|
* Receives params and context, returns Effect<boolean>.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { EffectsDef, GuardsDef, MachineContext } from "./slot.js";
|
|
1
2
|
import { AssertionError } from "./errors.js";
|
|
2
|
-
import {
|
|
3
|
-
import { BuiltMachine, Machine } from "./machine.js";
|
|
3
|
+
import { BuiltMachine, Machine, MachineRef } from "./machine.js";
|
|
4
4
|
import { Effect, SubscriptionRef } from "effect";
|
|
5
5
|
|
|
6
|
-
//#region src
|
|
6
|
+
//#region src/testing.d.ts
|
|
7
7
|
/** Accept either Machine or BuiltMachine for testing utilities. */
|
|
8
8
|
type MachineInput<S, E, R, GD extends GuardsDef, EFD extends EffectsDef> = Machine<S, E, R, any, any, GD, EFD> | BuiltMachine<S, E, R>;
|
|
9
9
|
/**
|
|
@@ -40,7 +40,7 @@ declare const simulate: <S extends {
|
|
|
40
40
|
}, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(input: MachineInput<S, E, R, GD, EFD>, events: readonly E[]) => Effect.Effect<{
|
|
41
41
|
states: S[];
|
|
42
42
|
finalState: S;
|
|
43
|
-
}, never, Exclude<R,
|
|
43
|
+
}, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
|
|
44
44
|
/**
|
|
45
45
|
* Assert that a machine can reach a specific state given a sequence of events
|
|
46
46
|
*/
|
|
@@ -48,7 +48,7 @@ declare const assertReaches: <S extends {
|
|
|
48
48
|
readonly _tag: string;
|
|
49
49
|
}, E extends {
|
|
50
50
|
readonly _tag: string;
|
|
51
|
-
}, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(input: MachineInput<S, E, R, GD, EFD>, events: readonly E[], expectedTag: string) => Effect.Effect<
|
|
51
|
+
}, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(input: MachineInput<S, E, R, GD, EFD>, events: readonly E[], expectedTag: string) => Effect.Effect<S, AssertionError, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
|
|
52
52
|
/**
|
|
53
53
|
* Assert that a machine follows a specific path of state tags
|
|
54
54
|
*
|
|
@@ -65,7 +65,10 @@ declare const assertPath: <S extends {
|
|
|
65
65
|
readonly _tag: string;
|
|
66
66
|
}, E extends {
|
|
67
67
|
readonly _tag: string;
|
|
68
|
-
}, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(input: MachineInput<S, E, R, GD, EFD>, events: readonly E[], expectedPath: readonly string[]) => Effect.Effect<
|
|
68
|
+
}, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(input: MachineInput<S, E, R, GD, EFD>, events: readonly E[], expectedPath: readonly string[]) => Effect.Effect<{
|
|
69
|
+
states: S[];
|
|
70
|
+
finalState: S;
|
|
71
|
+
}, AssertionError, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
|
|
69
72
|
/**
|
|
70
73
|
* Assert that a machine never reaches a specific state given a sequence of events
|
|
71
74
|
*
|
|
@@ -83,7 +86,10 @@ declare const assertNeverReaches: <S extends {
|
|
|
83
86
|
readonly _tag: string;
|
|
84
87
|
}, E extends {
|
|
85
88
|
readonly _tag: string;
|
|
86
|
-
}, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(input: MachineInput<S, E, R, GD, EFD>, events: readonly E[], forbiddenTag: string) => Effect.Effect<
|
|
89
|
+
}, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(input: MachineInput<S, E, R, GD, EFD>, events: readonly E[], forbiddenTag: string) => Effect.Effect<{
|
|
90
|
+
states: S[];
|
|
91
|
+
finalState: S;
|
|
92
|
+
}, AssertionError, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
|
|
87
93
|
/**
|
|
88
94
|
* Create a controllable test harness for a machine
|
|
89
95
|
*/
|
|
@@ -129,7 +135,7 @@ declare const createTestHarness: <S extends {
|
|
|
129
135
|
readonly _tag: string;
|
|
130
136
|
}, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(input: MachineInput<S, E, R, GD, EFD>, options?: TestHarnessOptions<S, E> | undefined) => Effect.Effect<{
|
|
131
137
|
state: SubscriptionRef.SubscriptionRef<S>;
|
|
132
|
-
send: (event: E) => Effect.Effect<S, never,
|
|
138
|
+
send: (event: E) => Effect.Effect<S, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
|
|
133
139
|
getState: Effect.Effect<S, never, never>;
|
|
134
140
|
}, never, never>;
|
|
135
141
|
//#endregion
|