effect-machine 0.15.2 → 0.17.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 +4 -0
- package/dist/actor.d.ts +19 -7
- package/dist/actor.js +26 -4
- package/dist/cluster/adapters/in-memory.d.ts +4 -4
- package/dist/cluster/adapters/in-memory.js +2 -2
- package/dist/cluster/entity-machine.d.ts +2 -2
- package/dist/cluster/index.d.ts +2 -2
- package/dist/cluster/persistence.d.ts +5 -4
- package/dist/cluster/persistence.js +2 -2
- package/dist/cluster/to-entity.d.ts +2 -2
- package/dist/errors.d.ts +12 -12
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/inspection.d.ts +12 -11
- package/dist/inspection.js +10 -24
- package/dist/internal/inspection.d.ts +2 -2
- package/dist/internal/runtime.d.ts +7 -7
- package/dist/internal/runtime.js +10 -15
- package/dist/internal/transition.d.ts +9 -9
- package/dist/internal/utils.d.ts +3 -3
- package/dist/internal/utils.js +1 -1
- package/dist/machine.d.ts +27 -7
- package/dist/machine.js +26 -4
- package/dist/schema.d.ts +24 -13
- package/dist/schema.js +8 -8
- package/dist/slot.d.ts +8 -6
- package/dist/slot.js +38 -3
- package/dist/testing.d.ts +7 -7
- package/package.json +22 -22
- package/v3/dist/actor.d.ts +12 -2
- package/v3/dist/actor.js +24 -2
- package/v3/dist/cluster/entity-machine.d.ts +1 -1
- package/v3/dist/cluster/entity-machine.js +4 -3
- package/v3/dist/cluster/to-entity.d.ts +8 -3
- package/v3/dist/index.d.ts +2 -2
- package/v3/dist/index.js +2 -2
- package/v3/dist/inspection.d.ts +2 -2
- package/v3/dist/inspection.js +8 -22
- package/v3/dist/internal/runtime.d.ts +5 -5
- package/v3/dist/internal/runtime.js +9 -14
- package/v3/dist/machine.d.ts +22 -2
- package/v3/dist/machine.js +26 -4
- package/v3/dist/schema.d.ts +25 -12
- package/v3/dist/schema.js +8 -7
- package/v3/dist/slot.d.ts +3 -2
- package/v3/dist/slot.js +37 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MachineContextTag, SlotsDef } from "../slot.js";
|
|
2
2
|
import { Machine, MachineRef, SpawnEffect, Transition } from "../machine.js";
|
|
3
|
-
import {
|
|
3
|
+
import { ActorSystemService } from "../actor.js";
|
|
4
4
|
import { Cause, Effect, Scope } from "effect";
|
|
5
5
|
|
|
6
6
|
//#region src/internal/transition.d.ts
|
|
@@ -29,12 +29,12 @@ declare const runTransitionHandler: <S extends {
|
|
|
29
29
|
readonly _tag: string;
|
|
30
30
|
}, E extends {
|
|
31
31
|
readonly _tag: string;
|
|
32
|
-
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, transition: Transition<S, E, SD, R>, state: S, event: E, self: MachineRef<E>, system:
|
|
32
|
+
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, transition: Transition<S, E, SD, R>, state: S, event: E, self: MachineRef<E>, system: ActorSystemService, actorId: string) => Effect.Effect<{
|
|
33
33
|
newState: S;
|
|
34
34
|
hasReply: boolean;
|
|
35
35
|
deferReply: boolean;
|
|
36
36
|
reply: unknown;
|
|
37
|
-
}, never, Exclude<R,
|
|
37
|
+
}, never, Exclude<R, MachineContextTag>>;
|
|
38
38
|
/**
|
|
39
39
|
* Execute a transition for a given state and event.
|
|
40
40
|
* Handles transition resolution, handler invocation, and guard/effect slot creation.
|
|
@@ -50,14 +50,14 @@ declare const executeTransition: <S extends {
|
|
|
50
50
|
readonly _tag: string;
|
|
51
51
|
}, E extends {
|
|
52
52
|
readonly _tag: string;
|
|
53
|
-
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, currentState: S, event: E, self: MachineRef<E>, system:
|
|
53
|
+
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, currentState: S, event: E, self: MachineRef<E>, system: ActorSystemService, actorId: string) => Effect.Effect<{
|
|
54
54
|
newState: S;
|
|
55
55
|
transitioned: boolean;
|
|
56
56
|
reenter: boolean;
|
|
57
57
|
hasReply: boolean;
|
|
58
58
|
deferReply: boolean;
|
|
59
59
|
reply: unknown;
|
|
60
|
-
}, never, Exclude<R,
|
|
60
|
+
}, never, Exclude<R, MachineContextTag>>;
|
|
61
61
|
/**
|
|
62
62
|
* Optional hooks for event processing inspection/tracing.
|
|
63
63
|
*/
|
|
@@ -130,7 +130,7 @@ declare const processEventCore: <S extends {
|
|
|
130
130
|
readonly _tag: string;
|
|
131
131
|
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, currentState: S, event: E, self: MachineRef<E>, stateScopeRef: {
|
|
132
132
|
current: Scope.Closeable;
|
|
133
|
-
}, system:
|
|
133
|
+
}, system: ActorSystemService, actorId: string, hooks?: ProcessEventHooks<S, E> | undefined) => Effect.Effect<{
|
|
134
134
|
newState: S;
|
|
135
135
|
previousState: S;
|
|
136
136
|
transitioned: boolean;
|
|
@@ -140,7 +140,7 @@ declare const processEventCore: <S extends {
|
|
|
140
140
|
deferReply: boolean;
|
|
141
141
|
reply: unknown;
|
|
142
142
|
postponed: boolean;
|
|
143
|
-
}, never, Exclude<R,
|
|
143
|
+
}, never, Exclude<R, MachineContextTag> | Exclude<Exclude<R, MachineContextTag>, Scope.Scope>>;
|
|
144
144
|
/**
|
|
145
145
|
* Run spawn effects for a state (forked into state scope, auto-cancelled on state exit).
|
|
146
146
|
*
|
|
@@ -150,7 +150,7 @@ declare const runSpawnEffects: <S extends {
|
|
|
150
150
|
readonly _tag: string;
|
|
151
151
|
}, E extends {
|
|
152
152
|
readonly _tag: string;
|
|
153
|
-
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, state: S, event: E, self: MachineRef<E>, stateScope: Scope.Closeable, system:
|
|
153
|
+
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, state: S, event: E, self: MachineRef<E>, stateScope: Scope.Closeable, system: ActorSystemService, actorId: string, onError?: ((info: ProcessEventError<S, E>) => Effect.Effect<void>) | undefined, onSpawnDefect?: ((cause: Cause.Cause<unknown>) => Effect.Effect<void>) | undefined) => Effect.Effect<void, never, Exclude<Exclude<R, MachineContextTag>, Scope.Scope>>;
|
|
154
154
|
/**
|
|
155
155
|
* Resolve which transition should fire for a given state and event.
|
|
156
156
|
* Uses indexed O(1) lookup. First matching transition wins.
|
package/dist/internal/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ActorSystemService } from "../actor.js";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
3
|
|
|
4
4
|
//#region src/internal/utils.d.ts
|
|
@@ -91,11 +91,11 @@ declare const getTag: (constructorOrValue: {
|
|
|
91
91
|
/** Check if a value is an Effect */
|
|
92
92
|
declare const isEffect: (value: unknown) => value is Effect.Effect<unknown, unknown, unknown>;
|
|
93
93
|
/**
|
|
94
|
-
* Stub
|
|
94
|
+
* Stub ActorSystemService that dies on any method call.
|
|
95
95
|
* Used in contexts where spawning/system access isn't supported
|
|
96
96
|
* (testing simulation, persistent actor replay).
|
|
97
97
|
* @internal
|
|
98
98
|
*/
|
|
99
|
-
declare const stubSystem:
|
|
99
|
+
declare const stubSystem: ActorSystemService;
|
|
100
100
|
//#endregion
|
|
101
101
|
export { ArgsOf, DeferReplyResult, DeferReplySymbol, INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, InstanceOf, ReplyResult, ReplyResultSymbol, TagOf, TaggedConstructor, TransitionResult, getTag, isDeferReplyResult, isEffect, isReplyResult, makeDeferReply, makeReply, stubSystem };
|
package/dist/internal/utils.js
CHANGED
|
@@ -56,7 +56,7 @@ const getTag = (constructorOrValue) => {
|
|
|
56
56
|
/** Check if a value is an Effect */
|
|
57
57
|
const isEffect = Effect.isEffect;
|
|
58
58
|
/**
|
|
59
|
-
* Stub
|
|
59
|
+
* Stub ActorSystemService that dies on any method call.
|
|
60
60
|
* Used in contexts where spawning/system access isn't supported
|
|
61
61
|
* (testing simulation, persistent actor replay).
|
|
62
62
|
* @internal
|
package/dist/machine.d.ts
CHANGED
|
@@ -2,15 +2,15 @@ import { DeferReplyResult, ReplyResult, TransitionResult } from "./internal/util
|
|
|
2
2
|
import { BrandedEvent, BrandedState, ExtractReply, TaggedOrConstructor } from "./internal/brands.js";
|
|
3
3
|
import { MachineEventSchema, MachineStateSchema, VariantsUnion } from "./schema.js";
|
|
4
4
|
import { DuplicateActorError } from "./errors.js";
|
|
5
|
-
import { MachineContext, ProvideSlots, SlotCalls, SlotsDef, SlotsSchema } from "./slot.js";
|
|
5
|
+
import { MachineContext, MachineContextTag, ProvideSlots, SlotCalls, SlotsDef, SlotsSchema } from "./slot.js";
|
|
6
6
|
import { Supervision } from "./supervision.js";
|
|
7
7
|
import { findTransitions } from "./internal/transition.js";
|
|
8
|
-
import { ActorRef,
|
|
9
|
-
import { Cause, Duration, Effect, Option, Schema, Scope
|
|
8
|
+
import { ActorRef, ActorSystemService } from "./actor.js";
|
|
9
|
+
import { Cause, Context, Duration, Effect, Option, Schema, Scope } from "effect";
|
|
10
10
|
|
|
11
11
|
//#region src/machine.d.ts
|
|
12
12
|
declare namespace machine_d_exports {
|
|
13
|
-
export { BackgroundEffect, DeferReplyResult, Durability, DurabilityCommit, HandlerContext, Lifecycle, Machine, MachineRef, MakeConfig, Recovery, RecoveryContext, ReplyResult, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, deferReply, findTransitions, make, materializeMachine, replay, reply, spawn };
|
|
13
|
+
export { BackgroundEffect, DeferReplyResult, Durability, DurabilityCommit, HandlerContext, Lifecycle, Machine, MachineRef, MakeConfig, Recovery, RecoveryContext, ReplyResult, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, deferReply, findTransitions, make, materializeMachine, replay, reply, scoped, spawn };
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Self reference for sending events back to the machine
|
|
@@ -48,7 +48,7 @@ interface StateHandlerContext<State, Event, SD extends SlotsDef = Record<string,
|
|
|
48
48
|
readonly event: Event;
|
|
49
49
|
readonly self: MachineRef<Event>;
|
|
50
50
|
readonly slots: SlotCalls<SD>;
|
|
51
|
-
readonly system:
|
|
51
|
+
readonly system: ActorSystemService;
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
54
|
* Transition handler function.
|
|
@@ -196,7 +196,7 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
196
196
|
* Context tag for accessing machine state/event/self in slot handlers.
|
|
197
197
|
* Uses shared module-level tag for all machines.
|
|
198
198
|
*/
|
|
199
|
-
readonly Context:
|
|
199
|
+
readonly Context: Context.Service<MachineContextTag, MachineContext<State, Event, MachineRef<Event>>>;
|
|
200
200
|
get transitions(): ReadonlyArray<Transition<State, Event, SD, R>>;
|
|
201
201
|
get spawnEffects(): ReadonlyArray<SpawnEffect<State, Event, SD, R>>;
|
|
202
202
|
get backgroundEffects(): ReadonlyArray<BackgroundEffect<State, Event, SD, R>>;
|
|
@@ -362,6 +362,26 @@ declare const spawn: <S extends {
|
|
|
362
362
|
supervision?: Supervision.Policy;
|
|
363
363
|
lifecycle?: Lifecycle<S, E>;
|
|
364
364
|
}) => Effect.Effect<ActorRef<S, E>, never, R>;
|
|
365
|
+
/**
|
|
366
|
+
* Wrap an effect to provide an `ActorScope` from the current `Scope`.
|
|
367
|
+
*
|
|
368
|
+
* Actors spawned inside will attach cleanup finalizers to this scope,
|
|
369
|
+
* so they are automatically stopped when the scope closes.
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ```ts
|
|
373
|
+
* yield* Effect.scoped(
|
|
374
|
+
* Machine.scoped(
|
|
375
|
+
* Effect.gen(function* () {
|
|
376
|
+
* const actor = yield* Machine.spawn(machine);
|
|
377
|
+
* yield* actor.start;
|
|
378
|
+
* // actor auto-stopped when scope closes
|
|
379
|
+
* }),
|
|
380
|
+
* ),
|
|
381
|
+
* );
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
384
|
+
declare const scoped: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R | Scope.Scope>;
|
|
365
385
|
declare const replay: {
|
|
366
386
|
<S extends {
|
|
367
387
|
readonly _tag: string;
|
|
@@ -375,4 +395,4 @@ declare const replay: {
|
|
|
375
395
|
declare const reply: <State, Reply>(state: State, reply: Reply) => ReplyResult<State, Reply>;
|
|
376
396
|
declare const deferReply: <State>(state: State) => DeferReplyResult<State>;
|
|
377
397
|
//#endregion
|
|
378
|
-
export { BackgroundEffect, type DeferReplyResult, Durability, DurabilityCommit, HandlerContext, Lifecycle, Machine, MachineRef, MakeConfig, Recovery, RecoveryContext, type ReplyResult, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, deferReply, findTransitions, machine_d_exports, make, materializeMachine, replay, reply, spawn };
|
|
398
|
+
export { BackgroundEffect, type DeferReplyResult, Durability, DurabilityCommit, HandlerContext, Lifecycle, Machine, MachineRef, MakeConfig, Recovery, RecoveryContext, type ReplyResult, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, deferReply, findTransitions, machine_d_exports, make, materializeMachine, replay, reply, scoped, spawn };
|
package/dist/machine.js
CHANGED
|
@@ -5,7 +5,7 @@ import { findTransitions, invalidateIndex, resolveTransition, runTransitionHandl
|
|
|
5
5
|
import { emitWithTimestamp } from "./internal/inspection.js";
|
|
6
6
|
import { Inspector } from "./inspection.js";
|
|
7
7
|
import { MachineContextTag } from "./slot.js";
|
|
8
|
-
import { createActor } from "./actor.js";
|
|
8
|
+
import { ActorScope, createActor } from "./actor.js";
|
|
9
9
|
import { Cause, Effect, Exit, Option, Random, Schema, Scope } from "effect";
|
|
10
10
|
//#region src/machine.ts
|
|
11
11
|
var machine_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -16,6 +16,7 @@ var machine_exports = /* @__PURE__ */ __exportAll({
|
|
|
16
16
|
materializeMachine: () => materializeMachine,
|
|
17
17
|
replay: () => replay,
|
|
18
18
|
reply: () => reply,
|
|
19
|
+
scoped: () => scoped,
|
|
19
20
|
spawn: () => spawn
|
|
20
21
|
});
|
|
21
22
|
const emitTaskInspection = (input) => Effect.flatMap(Effect.serviceOption(Inspector), (inspector) => Option.isNone(inspector) ? Effect.void : emitWithTimestamp(inspector.value, (timestamp) => ({
|
|
@@ -177,7 +178,8 @@ var Machine = class Machine {
|
|
|
177
178
|
this._slots = this._slotsSchema !== void 0 ? this._slotsSchema._createSlots(resolve) : {};
|
|
178
179
|
}
|
|
179
180
|
from(stateOrStates, build) {
|
|
180
|
-
|
|
181
|
+
const states = Array.isArray(stateOrStates) ? stateOrStates : [stateOrStates];
|
|
182
|
+
build(new TransitionScope(this, states));
|
|
181
183
|
return this;
|
|
182
184
|
}
|
|
183
185
|
/** @internal */
|
|
@@ -411,10 +413,30 @@ const spawn = Effect.fn("effect-machine.spawn")(function* (machine, idOrOptions)
|
|
|
411
413
|
supervision: opts?.supervision,
|
|
412
414
|
lifecycle: opts?.lifecycle
|
|
413
415
|
});
|
|
414
|
-
const maybeScope = yield* Effect.serviceOption(
|
|
416
|
+
const maybeScope = yield* Effect.serviceOption(ActorScope);
|
|
415
417
|
if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, actor.stop);
|
|
416
418
|
return actor;
|
|
417
419
|
});
|
|
420
|
+
/**
|
|
421
|
+
* Wrap an effect to provide an `ActorScope` from the current `Scope`.
|
|
422
|
+
*
|
|
423
|
+
* Actors spawned inside will attach cleanup finalizers to this scope,
|
|
424
|
+
* so they are automatically stopped when the scope closes.
|
|
425
|
+
*
|
|
426
|
+
* @example
|
|
427
|
+
* ```ts
|
|
428
|
+
* yield* Effect.scoped(
|
|
429
|
+
* Machine.scoped(
|
|
430
|
+
* Effect.gen(function* () {
|
|
431
|
+
* const actor = yield* Machine.spawn(machine);
|
|
432
|
+
* yield* actor.start;
|
|
433
|
+
* // actor auto-stopped when scope closes
|
|
434
|
+
* }),
|
|
435
|
+
* ),
|
|
436
|
+
* );
|
|
437
|
+
* ```
|
|
438
|
+
*/
|
|
439
|
+
const scoped = (effect) => Effect.flatMap(Effect.service(Scope.Scope), (scope) => Effect.provideService(effect, ActorScope, scope));
|
|
418
440
|
const replay = Effect.fn("effect-machine.replay")(function* (input, events, options) {
|
|
419
441
|
const machine = materializeMachine(input, options?.slots);
|
|
420
442
|
let state = options?.from ?? machine.initial;
|
|
@@ -462,4 +484,4 @@ const replay = Effect.fn("effect-machine.replay")(function* (input, events, opti
|
|
|
462
484
|
const reply = makeReply;
|
|
463
485
|
const deferReply = makeDeferReply;
|
|
464
486
|
//#endregion
|
|
465
|
-
export { Machine, deferReply, findTransitions, machine_exports, make, materializeMachine, replay, reply, spawn };
|
|
487
|
+
export { Machine, deferReply, findTransitions, machine_exports, make, materializeMachine, replay, reply, scoped, spawn };
|
package/dist/schema.d.ts
CHANGED
|
@@ -49,16 +49,23 @@ type VariantReplyBrand<Fields extends Schema.Struct.Fields> = Fields extends {
|
|
|
49
49
|
* Empty structs: plain values with `_tag`: `State.Idle`
|
|
50
50
|
* Non-empty structs require args: `State.Loading({ url })`
|
|
51
51
|
*
|
|
52
|
-
* Each variant also has a `
|
|
52
|
+
* Each variant also has a `with` method for constructing from a source object,
|
|
53
|
+
* copying matching fields and overriding with a partial.
|
|
53
54
|
* The source type uses `object` to accept branded state types without index signature issues.
|
|
54
55
|
* Reply-bearing variants carry ReplyTypeBrand<R> for ask() type inference.
|
|
55
56
|
*/
|
|
56
57
|
type VariantConstructors<D extends Record<string, Schema.Struct.Fields>, Brand> = { readonly [K in keyof D & string]: IsEmptyFields<D[K]> extends true ? TaggedStructType<K, D[K]> & Brand & VariantReplyBrand<D[K]> & {
|
|
57
|
-
readonly
|
|
58
|
+
readonly with: (source: object) => TaggedStructType<K, D[K]> & Brand;
|
|
58
59
|
} : ((args: Schema.Struct.Type<PayloadFields<D[K]>>) => TaggedStructType<K, D[K]> & Brand & VariantReplyBrand<D[K]>) & {
|
|
59
|
-
readonly
|
|
60
|
+
readonly with: (source: object, partial?: Partial<Schema.Struct.Type<PayloadFields<D[K]>>>) => TaggedStructType<K, D[K]> & Brand;
|
|
60
61
|
readonly _tag: K;
|
|
61
62
|
} };
|
|
63
|
+
/**
|
|
64
|
+
* Keys present in ALL variants (intersection of field names).
|
|
65
|
+
* Used by union-level `with` to accept only fields safe to update
|
|
66
|
+
* regardless of which variant the source is.
|
|
67
|
+
*/
|
|
68
|
+
type SharedKeys<D extends Record<string, Schema.Struct.Fields>> = keyof D[keyof D & string] & string;
|
|
62
69
|
/**
|
|
63
70
|
* Pattern matching cases type
|
|
64
71
|
*/
|
|
@@ -87,19 +94,23 @@ interface MachineSchemaBase<D extends Record<string, Schema.Struct.Fields>, Bran
|
|
|
87
94
|
<R>(value: VariantsUnion<D> & Brand, cases: MatchCases<D, R>): R;
|
|
88
95
|
};
|
|
89
96
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
97
|
+
* Copy fields from `source` into the same variant, overriding with `partial`.
|
|
98
|
+
* Preserves the specific variant subtype in the return.
|
|
92
99
|
*
|
|
93
|
-
*
|
|
100
|
+
* The partial accepts fields common to all variants, so it works safely
|
|
101
|
+
* when `S` is a generic type parameter (e.g., `<S extends MyState>`).
|
|
94
102
|
*
|
|
95
103
|
* @example
|
|
96
104
|
* ```ts
|
|
97
|
-
* //
|
|
98
|
-
* const
|
|
99
|
-
*
|
|
105
|
+
* // Per-variant field update — partial accepts that variant's fields
|
|
106
|
+
* const next = MyState.Streaming.with(state, { draft: newDraft })
|
|
107
|
+
*
|
|
108
|
+
* // Cross-variant shared field — works with generic state
|
|
109
|
+
* const updateQueue = <S extends MyState>(state: S, queue: Queue): S =>
|
|
110
|
+
* MyState.with(state, { queue })
|
|
100
111
|
* ```
|
|
101
112
|
*/
|
|
102
|
-
readonly
|
|
113
|
+
readonly with: <S extends VariantsUnion<D> & Brand>(source: S, partial?: Partial<Record<SharedKeys<D>, unknown>>) => S;
|
|
103
114
|
/**
|
|
104
115
|
* Reply schemas per variant tag. Only populated for event schemas
|
|
105
116
|
* with variants defined via `Event.reply()`.
|
|
@@ -116,8 +127,8 @@ interface MachineSchemaBase<D extends Record<string, Schema.Struct.Fields>, Bran
|
|
|
116
127
|
* The D type parameter captures the definition, creating a unique brand
|
|
117
128
|
* per distinct schema definition shape.
|
|
118
129
|
*/
|
|
119
|
-
type MachineStateSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Codec<VariantsUnion<D> & FullStateBrand<D>, unknown
|
|
120
|
-
/**
|
|
130
|
+
type MachineStateSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Codec<VariantsUnion<D> & FullStateBrand<D>, unknown> & MachineSchemaBase<D, FullStateBrand<D>> & VariantConstructors<D, FullStateBrand<D>> & {
|
|
131
|
+
/** Schema for persistence, config, and registration. */readonly schema: Schema.Schema<VariantsUnion<D> & FullStateBrand<D>>;
|
|
121
132
|
};
|
|
122
133
|
/**
|
|
123
134
|
* Schema-first event definition (same structure as state, different brand)
|
|
@@ -125,7 +136,7 @@ type MachineStateSchema<D extends Record<string, Schema.Struct.Fields>> = Schema
|
|
|
125
136
|
* The D type parameter captures the definition, creating a unique brand
|
|
126
137
|
* per distinct schema definition shape.
|
|
127
138
|
*/
|
|
128
|
-
type MachineEventSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Codec<VariantsUnion<D> & FullEventBrand<D>, unknown
|
|
139
|
+
type MachineEventSchema<D extends Record<string, Schema.Struct.Fields>> = Schema.Codec<VariantsUnion<D> & FullEventBrand<D>, unknown> & MachineSchemaBase<D, FullEventBrand<D>> & VariantConstructors<D, FullEventBrand<D>>;
|
|
129
140
|
/**
|
|
130
141
|
* Create a schema-first State definition.
|
|
131
142
|
*
|
package/dist/schema.js
CHANGED
|
@@ -62,7 +62,7 @@ const buildMachineSchema = (definition) => {
|
|
|
62
62
|
_tag: tag
|
|
63
63
|
});
|
|
64
64
|
constructor._tag = tag;
|
|
65
|
-
constructor.
|
|
65
|
+
constructor.with = (source, partial) => {
|
|
66
66
|
const result = { _tag: tag };
|
|
67
67
|
for (const key of fieldNames) if (key in source) result[key] = source[key];
|
|
68
68
|
if (partial !== void 0) for (const [key, value] of Object.entries(partial)) {
|
|
@@ -75,7 +75,7 @@ const buildMachineSchema = (definition) => {
|
|
|
75
75
|
constructors[tag] = constructor;
|
|
76
76
|
} else constructors[tag] = {
|
|
77
77
|
_tag: tag,
|
|
78
|
-
|
|
78
|
+
with: () => ({ _tag: tag })
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
const variantArray = Object.values(variants);
|
|
@@ -112,21 +112,21 @@ const buildMachineSchema = (definition) => {
|
|
|
112
112
|
*/
|
|
113
113
|
const createMachineSchema = (definition) => {
|
|
114
114
|
const { schema, variants, constructors, _definition, replySchemas, $is, $match } = buildMachineSchema(definition);
|
|
115
|
-
const
|
|
115
|
+
const withFn = (source, partial) => {
|
|
116
116
|
const ctor = constructors[source._tag];
|
|
117
117
|
if (ctor === void 0) throw new MissingMatchHandlerError({ tag: source._tag });
|
|
118
|
-
const
|
|
119
|
-
if (
|
|
120
|
-
return
|
|
118
|
+
const fn = ctor.with;
|
|
119
|
+
if (fn === void 0) throw new MissingMatchHandlerError({ tag: source._tag });
|
|
120
|
+
return fn(source, partial);
|
|
121
121
|
};
|
|
122
122
|
return Object.assign(Object.create(schema), {
|
|
123
123
|
variants,
|
|
124
124
|
_definition,
|
|
125
125
|
_replySchemas: replySchemas,
|
|
126
|
-
|
|
126
|
+
schema,
|
|
127
127
|
$is,
|
|
128
128
|
$match,
|
|
129
|
-
|
|
129
|
+
with: withFn,
|
|
130
130
|
...constructors
|
|
131
131
|
});
|
|
132
132
|
};
|
package/dist/slot.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Effect, Schema
|
|
1
|
+
import { ActorSystemService } from "./actor.js";
|
|
2
|
+
import { Context, Effect, Schema } from "effect";
|
|
3
3
|
|
|
4
4
|
//#region src/slot.d.ts
|
|
5
5
|
/** Schema fields definition (like Schema.Struct.Fields) */
|
|
@@ -40,7 +40,7 @@ interface SlotFnDef<F extends Fields = Fields, Return = void> {
|
|
|
40
40
|
*/
|
|
41
41
|
declare const fn: {
|
|
42
42
|
<F extends Fields, Return>(fields: F, returnSchema: Schema.Schema<Return>): SlotFnDef<F, Return>;
|
|
43
|
-
<F extends Fields>(fields: F): SlotFnDef<F
|
|
43
|
+
<F extends Fields>(fields: F): SlotFnDef<F>;
|
|
44
44
|
};
|
|
45
45
|
/**
|
|
46
46
|
* Record of slot definitions. Keys are slot names, values are SlotFnDef.
|
|
@@ -125,14 +125,15 @@ interface MachineContext<State, Event, Self> {
|
|
|
125
125
|
readonly state: State;
|
|
126
126
|
readonly event: Event;
|
|
127
127
|
readonly self: Self;
|
|
128
|
-
readonly system:
|
|
128
|
+
readonly system: ActorSystemService;
|
|
129
129
|
}
|
|
130
|
+
declare const MachineContextTag_base: Context.ServiceClass<MachineContextTag, "effect-machine/slot/MachineContextTag", MachineContext<any, any, any>>;
|
|
130
131
|
/**
|
|
131
132
|
* Shared Context tag for all machines.
|
|
132
133
|
* Single module-level tag instead of per-machine allocation.
|
|
133
134
|
* @internal
|
|
134
135
|
*/
|
|
135
|
-
declare
|
|
136
|
+
declare class MachineContextTag extends MachineContextTag_base {}
|
|
136
137
|
/**
|
|
137
138
|
* Define a set of slots with parameter and return schemas.
|
|
138
139
|
*
|
|
@@ -149,9 +150,10 @@ declare const define: <D extends SlotsDef>(definitions: D) => SlotsSchema<D>;
|
|
|
149
150
|
declare const Slot: {
|
|
150
151
|
readonly fn: {
|
|
151
152
|
<F extends Fields, Return>(fields: F, returnSchema: Schema.Schema<Return>): SlotFnDef<F, Return>;
|
|
152
|
-
<F extends Fields>(fields: F): SlotFnDef<F
|
|
153
|
+
<F extends Fields>(fields: F): SlotFnDef<F>;
|
|
153
154
|
};
|
|
154
155
|
readonly define: <D extends SlotsDef>(definitions: D) => SlotsSchema<D>;
|
|
156
|
+
readonly of: <D extends SlotsDef>(slotsSchema: SlotsSchema<D>, provided: ProvideSlots<D>) => SlotCalls<D>;
|
|
155
157
|
};
|
|
156
158
|
//#endregion
|
|
157
159
|
export { HasSlotKeys, MachineContext, MachineContextTag, ProvideSlots, Slot, SlotCall, SlotCalls, SlotFnDef, SlotHandler, SlotInvocation, SlotRequest, SlotResult, SlotsDef, SlotsSchema, define, fn };
|
package/dist/slot.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Context, Effect, Schema } from "effect";
|
|
2
2
|
//#region src/slot.ts
|
|
3
3
|
/**
|
|
4
4
|
* Slot module — unified, schema-based parameterized slots.
|
|
@@ -62,7 +62,7 @@ const fn = (fields, returnSchema) => {
|
|
|
62
62
|
* Single module-level tag instead of per-machine allocation.
|
|
63
63
|
* @internal
|
|
64
64
|
*/
|
|
65
|
-
|
|
65
|
+
var MachineContextTag = class extends Context.Service()("effect-machine/slot/MachineContextTag") {};
|
|
66
66
|
/**
|
|
67
67
|
* Define a set of slots with parameter and return schemas.
|
|
68
68
|
*
|
|
@@ -122,9 +122,44 @@ const define = (definitions) => {
|
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
};
|
|
125
|
+
/**
|
|
126
|
+
* Convert raw slot handler implementations into the callable `SlotCalls` form.
|
|
127
|
+
*
|
|
128
|
+
* Handlers that return plain values are wrapped in `Effect.succeed`.
|
|
129
|
+
* Handlers that return Effects are called directly inside `Effect.suspend`.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* const provided = yield* myExtension.slots(ctx)
|
|
134
|
+
* const slots = Slot.of(slotsSchema, provided)
|
|
135
|
+
* // slots.mySlot({ param: 1 }) returns Effect<ReturnType>
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
const of = (slotsSchema, provided) => {
|
|
139
|
+
const slots = {};
|
|
140
|
+
for (const name of Object.keys(slotsSchema.definitions)) {
|
|
141
|
+
const handler = provided[name];
|
|
142
|
+
if (handler === void 0) continue;
|
|
143
|
+
const call = (params) => Effect.suspend(() => {
|
|
144
|
+
const result = handler(params);
|
|
145
|
+
return Effect.isEffect(result) ? result : Effect.succeed(result);
|
|
146
|
+
});
|
|
147
|
+
Object.defineProperty(call, "_tag", {
|
|
148
|
+
value: "Slot",
|
|
149
|
+
enumerable: true
|
|
150
|
+
});
|
|
151
|
+
Object.defineProperty(call, "name", {
|
|
152
|
+
value: name,
|
|
153
|
+
enumerable: true
|
|
154
|
+
});
|
|
155
|
+
slots[name] = call;
|
|
156
|
+
}
|
|
157
|
+
return slots;
|
|
158
|
+
};
|
|
125
159
|
const Slot = {
|
|
126
160
|
fn,
|
|
127
|
-
define
|
|
161
|
+
define,
|
|
162
|
+
of
|
|
128
163
|
};
|
|
129
164
|
//#endregion
|
|
130
165
|
export { MachineContextTag, Slot, define, fn };
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AssertionError } from "./errors.js";
|
|
2
|
-
import {
|
|
3
|
-
import { Machine
|
|
2
|
+
import { MachineContextTag, ProvideSlots, SlotsDef } from "./slot.js";
|
|
3
|
+
import { Machine } from "./machine.js";
|
|
4
4
|
import { Effect, SubscriptionRef } from "effect";
|
|
5
5
|
|
|
6
6
|
//#region src/testing.d.ts
|
|
@@ -41,7 +41,7 @@ declare const simulate: <S extends {
|
|
|
41
41
|
} | undefined) => Effect.Effect<{
|
|
42
42
|
states: S[];
|
|
43
43
|
finalState: S;
|
|
44
|
-
}, never, Exclude<R,
|
|
44
|
+
}, never, Exclude<R, MachineContextTag>>;
|
|
45
45
|
/**
|
|
46
46
|
* Assert that a machine can reach a specific state given a sequence of events
|
|
47
47
|
*/
|
|
@@ -51,7 +51,7 @@ declare const assertReaches: <S extends {
|
|
|
51
51
|
readonly _tag: string;
|
|
52
52
|
}, R, SD extends SlotsDef = Record<string, never>>(input: MachineInput<S, E, R, SD>, events: readonly E[], expectedTag: string, options?: {
|
|
53
53
|
slots?: ProvideSlots<SD, any>;
|
|
54
|
-
} | undefined) => Effect.Effect<S, AssertionError, Exclude<R,
|
|
54
|
+
} | undefined) => Effect.Effect<S, AssertionError, Exclude<R, MachineContextTag>>;
|
|
55
55
|
/**
|
|
56
56
|
* Assert that a machine follows a specific path of state tags
|
|
57
57
|
*
|
|
@@ -73,7 +73,7 @@ declare const assertPath: <S extends {
|
|
|
73
73
|
} | undefined) => Effect.Effect<{
|
|
74
74
|
states: S[];
|
|
75
75
|
finalState: S;
|
|
76
|
-
}, AssertionError, Exclude<R,
|
|
76
|
+
}, AssertionError, Exclude<R, MachineContextTag>>;
|
|
77
77
|
/**
|
|
78
78
|
* Assert that a machine never reaches a specific state given a sequence of events
|
|
79
79
|
*
|
|
@@ -96,7 +96,7 @@ declare const assertNeverReaches: <S extends {
|
|
|
96
96
|
} | undefined) => Effect.Effect<{
|
|
97
97
|
states: S[];
|
|
98
98
|
finalState: S;
|
|
99
|
-
}, AssertionError, Exclude<R,
|
|
99
|
+
}, AssertionError, Exclude<R, MachineContextTag>>;
|
|
100
100
|
/**
|
|
101
101
|
* Create a controllable test harness for a machine
|
|
102
102
|
*/
|
|
@@ -144,7 +144,7 @@ declare const createTestHarness: <S extends {
|
|
|
144
144
|
readonly _tag: string;
|
|
145
145
|
}, R, SD extends SlotsDef = Record<string, never>>(input: MachineInput<S, E, R, SD>, options?: TestHarnessOptions<S, E, SD> | undefined) => Effect.Effect<{
|
|
146
146
|
state: SubscriptionRef.SubscriptionRef<S>;
|
|
147
|
-
send: (event: E) => Effect.Effect<S, never, Exclude<R,
|
|
147
|
+
send: (event: E) => Effect.Effect<S, never, Exclude<R, MachineContextTag>>;
|
|
148
148
|
getState: Effect.Effect<S, never, never>;
|
|
149
149
|
}, never, never>;
|
|
150
150
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "effect-machine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/cevr/effect-machine.git"
|
|
@@ -40,47 +40,47 @@
|
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
|
-
"typecheck": "
|
|
44
|
-
"typecheck:v3": "
|
|
43
|
+
"typecheck": "concurrently -n v4,v3 -c blue,magenta \"tsgo --noEmit\" \"bun run typecheck:v3\"",
|
|
44
|
+
"typecheck:v3": "tsgo --noEmit -p v3/tsconfig.json",
|
|
45
45
|
"lint": "oxlint",
|
|
46
46
|
"lint:fix": "oxlint --fix",
|
|
47
47
|
"fmt": "oxfmt",
|
|
48
48
|
"fmt:check": "oxfmt --check",
|
|
49
|
-
"test": "bun test",
|
|
50
|
-
"test:v3": "bun test --tsconfig-override=
|
|
49
|
+
"test": "bun test --tsconfig-override=tsconfig.json",
|
|
50
|
+
"test:v3": "cd v3 && bun test --tsconfig-override=tsconfig.json ./test/",
|
|
51
51
|
"test:watch": "bun test --watch",
|
|
52
|
-
"
|
|
53
|
-
"
|
|
52
|
+
"test:all": "bun run test && bun run test:v3",
|
|
53
|
+
"gate": "concurrently -n type,lint,fmt,test,build -c blue,yellow,magenta,green,cyan \"bun run typecheck\" \"bun run lint:fix\" \"bun run fmt\" \"bun run test:all\" \"bun run build\"",
|
|
54
|
+
"prepare": "lefthook install && effect-tsgo patch",
|
|
54
55
|
"version": "changeset version",
|
|
55
|
-
"build": "
|
|
56
|
+
"build": "concurrently -n v4,v3 -c cyan,magenta \"tsdown\" \"tsdown --config v3/tsdown.config.ts\"",
|
|
56
57
|
"release": "bun run build && changeset publish"
|
|
57
58
|
},
|
|
58
|
-
"dependencies": {
|
|
59
|
-
"effect": "4.0.0-beta.43"
|
|
60
|
-
},
|
|
61
59
|
"devDependencies": {
|
|
62
60
|
"@changesets/changelog-github": "^0.6.0",
|
|
63
61
|
"@changesets/cli": "^2.30.0",
|
|
64
|
-
"@effect/
|
|
65
|
-
"@
|
|
62
|
+
"@effect/cluster": "0.58.2",
|
|
63
|
+
"@effect/rpc": "0.75.1",
|
|
64
|
+
"@effect/tsgo": "0.5.2",
|
|
65
|
+
"@types/bun": "1.3.12",
|
|
66
|
+
"@typescript/native-preview": "7.0.0-dev.20260507.1",
|
|
66
67
|
"concurrently": "^9.2.1",
|
|
68
|
+
"effect": "4.0.0-beta.63",
|
|
67
69
|
"effect-bun-test": "0.3.0",
|
|
68
|
-
"effect-v3": "npm:effect
|
|
69
|
-
"lefthook": "^2.1.
|
|
70
|
-
"oxfmt": "^0.
|
|
71
|
-
"oxlint": "^1.
|
|
70
|
+
"effect-v3": "npm:effect@3.21.2",
|
|
71
|
+
"lefthook": "^2.1.5",
|
|
72
|
+
"oxfmt": "^0.44.0",
|
|
73
|
+
"oxlint": "^1.63.0",
|
|
74
|
+
"oxlint-tsgolint": "^0.22.1",
|
|
72
75
|
"tsdown": "^0.21.7",
|
|
73
|
-
"typescript": "^
|
|
76
|
+
"typescript": "^6.0.2"
|
|
74
77
|
},
|
|
75
78
|
"peerDependencies": {
|
|
76
|
-
"effect": ">=
|
|
79
|
+
"effect": ">=4.0.0-beta.63"
|
|
77
80
|
},
|
|
78
81
|
"peerDependenciesMeta": {
|
|
79
82
|
"effect": {
|
|
80
83
|
"optional": false
|
|
81
84
|
}
|
|
82
|
-
},
|
|
83
|
-
"overrides": {
|
|
84
|
-
"effect": "4.0.0-beta.43"
|
|
85
85
|
}
|
|
86
86
|
}
|
package/v3/dist/actor.d.ts
CHANGED
|
@@ -197,6 +197,16 @@ interface ActorSystem {
|
|
|
197
197
|
* ActorSystem service tag
|
|
198
198
|
*/
|
|
199
199
|
declare const ActorSystem: Context.Tag<ActorSystem, ActorSystem>;
|
|
200
|
+
/**
|
|
201
|
+
* Explicit scope for actor lifecycle management.
|
|
202
|
+
*
|
|
203
|
+
* When present in context, actors attach cleanup finalizers to this scope.
|
|
204
|
+
* This replaces ambient `Scope.Scope` detection which caused bugs where
|
|
205
|
+
* unrelated scopes would tear down actors unexpectedly.
|
|
206
|
+
*
|
|
207
|
+
* Provide via `Machine.scoped` or `Effect.provideService(ActorScope, scope)`.
|
|
208
|
+
*/
|
|
209
|
+
declare const ActorScope: Context.Tag<Scope.Scope, Scope.Scope>;
|
|
200
210
|
/** Listener set for sync subscriptions */
|
|
201
211
|
type Listeners<S> = Set<(state: S) => void>;
|
|
202
212
|
/**
|
|
@@ -210,7 +220,7 @@ declare const buildActorRefCore: <S extends {
|
|
|
210
220
|
readonly _tag: string;
|
|
211
221
|
}, E extends {
|
|
212
222
|
readonly _tag: string;
|
|
213
|
-
}, R, SD extends SlotsDef>(id: string, machine: Machine<S, E, R, any, any, SD>, stateRef: SubscriptionRef.SubscriptionRef<S>, eventQueueRef: Ref.Ref<Queue.Queue<QueuedEvent<E>>>, stoppedRef: Ref.Ref<boolean>, listeners: Listeners<S>, stop: Effect.Effect<void>, start: Effect.Effect<void>, system: ActorSystem, childrenMap: ReadonlyMap<string, ActorRef<AnyState, unknown>>, pendingReplies: Set<Deferred.Deferred<unknown, unknown>>, transitionsPubSub: PubSub.PubSub<TransitionInfo<S, E>> | undefined, exitDeferred: Deferred.Deferred<ActorExit<S
|
|
223
|
+
}, R, SD extends SlotsDef>(id: string, machine: Machine<S, E, R, any, any, SD>, stateRef: SubscriptionRef.SubscriptionRef<S>, eventQueueRef: Ref.Ref<Queue.Queue<QueuedEvent<E>>>, stoppedRef: Ref.Ref<boolean>, listeners: Listeners<S>, stop: Effect.Effect<void>, start: Effect.Effect<void>, system: ActorSystem, childrenMap: ReadonlyMap<string, ActorRef<AnyState, unknown>>, pendingReplies: Set<Deferred.Deferred<unknown, unknown>>, transitionsPubSub: PubSub.PubSub<TransitionInfo<S, E>> | undefined, exitDeferred: Deferred.Deferred<ActorExit<S>>) => ActorRef<S, E>;
|
|
214
224
|
/**
|
|
215
225
|
* Create and start an actor for a machine.
|
|
216
226
|
* Delegates to the shared runtime kernel with actor-specific lifecycle hooks.
|
|
@@ -237,4 +247,4 @@ declare const makeSystem: () => Effect.Effect<ActorSystem, never, Scope.Scope>;
|
|
|
237
247
|
*/
|
|
238
248
|
declare const Default: Layer.Layer<ActorSystem, never, never>;
|
|
239
249
|
//#endregion
|
|
240
|
-
export { ActorRef, ActorRefSync, ActorSystem, Default, Listeners, type ProcessEventError, type ProcessEventHooks, type ProcessEventResult, QueuedEvent, SystemEvent, SystemEventListener, TransitionInfo, buildActorRefCore, createActor, makeSystem, notifyListeners, processEventCore, resolveTransition, runSpawnEffects, settlePendingReplies };
|
|
250
|
+
export { ActorRef, ActorRefSync, ActorScope, ActorSystem, Default, Listeners, type ProcessEventError, type ProcessEventHooks, type ProcessEventResult, QueuedEvent, SystemEvent, SystemEventListener, TransitionInfo, buildActorRefCore, createActor, makeSystem, notifyListeners, processEventCore, resolveTransition, runSpawnEffects, settlePendingReplies };
|