effect-machine 0.13.0 → 0.15.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 +19 -11
- package/dist/actor.d.ts +18 -6
- package/dist/actor.js +151 -61
- package/dist/cluster/entity-machine.d.ts +1 -1
- package/dist/cluster/entity-machine.js +2 -1
- package/dist/cluster/to-entity.d.ts +1 -1
- package/dist/errors.d.ts +9 -2
- package/dist/errors.js +8 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/internal/runtime.d.ts +21 -2
- package/dist/internal/runtime.js +64 -56
- package/dist/internal/transition.d.ts +9 -9
- package/dist/internal/transition.js +3 -5
- package/dist/machine.d.ts +129 -135
- package/dist/machine.js +97 -112
- package/dist/schema.d.ts +17 -1
- package/dist/schema.js +10 -0
- package/dist/slot.d.ts +112 -86
- package/dist/slot.js +92 -59
- package/dist/testing.d.ts +16 -16
- package/dist/testing.js +3 -3
- package/package.json +3 -3
- package/v3/dist/actor.d.ts +25 -12
- package/v3/dist/actor.js +174 -89
- package/v3/dist/cluster/entity-machine.d.ts +1 -1
- package/v3/dist/cluster/entity-machine.js +1 -0
- package/v3/dist/cluster/to-entity.d.ts +1 -1
- package/v3/dist/errors.d.ts +12 -3
- package/v3/dist/errors.js +10 -4
- package/v3/dist/index.d.ts +6 -6
- package/v3/dist/index.js +2 -2
- package/v3/dist/inspection.d.ts +3 -22
- package/v3/dist/inspection.js +1 -15
- package/v3/dist/internal/brands.d.ts +4 -8
- package/v3/dist/internal/inspection.js +1 -1
- package/v3/dist/internal/runtime.d.ts +27 -8
- package/v3/dist/internal/runtime.js +91 -61
- package/v3/dist/internal/transition.d.ts +10 -10
- package/v3/dist/internal/transition.js +8 -10
- package/v3/dist/internal/utils.js +5 -1
- package/v3/dist/machine.d.ts +160 -120
- package/v3/dist/machine.js +118 -115
- package/v3/dist/schema.d.ts +25 -11
- package/v3/dist/schema.js +18 -5
- package/v3/dist/slot.d.ts +112 -86
- package/v3/dist/slot.js +92 -59
- package/v3/dist/testing.d.ts +16 -16
- package/v3/dist/testing.js +7 -7
package/dist/internal/runtime.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { INTERNAL_INIT_EVENT } from "./utils.js";
|
|
2
|
-
import { processEventCore, runSpawnEffects, shouldPostpone } from "./transition.js";
|
|
3
2
|
import { NoReplyError } from "../errors.js";
|
|
3
|
+
import { processEventCore, runSpawnEffects, shouldPostpone } from "./transition.js";
|
|
4
4
|
import { ActorExit } from "../supervision.js";
|
|
5
5
|
import { ActorSystem } from "../actor.js";
|
|
6
|
-
import { Cause, Deferred, Effect, Exit, Fiber,
|
|
6
|
+
import { Cause, Deferred, Effect, Exit, Fiber, Queue, Ref, Schema, Scope, SubscriptionRef } from "effect";
|
|
7
7
|
//#region src/internal/runtime.ts
|
|
8
8
|
/**
|
|
9
9
|
* Shared runtime kernel for machine event processing.
|
|
@@ -72,7 +72,6 @@ const createRuntime = Effect.fn("effect-machine.runtime.create")(function* (mach
|
|
|
72
72
|
})
|
|
73
73
|
};
|
|
74
74
|
const stateScopeRef = { current: yield* Scope.make() };
|
|
75
|
-
const backgroundFibers = [];
|
|
76
75
|
const initEvent = { _tag: INTERNAL_INIT_EVENT };
|
|
77
76
|
const ctx = {
|
|
78
77
|
actorId,
|
|
@@ -81,60 +80,72 @@ const createRuntime = Effect.fn("effect-machine.runtime.create")(function* (mach
|
|
|
81
80
|
self,
|
|
82
81
|
system
|
|
83
82
|
};
|
|
84
|
-
const
|
|
85
|
-
for (const bg of machine.backgroundEffects) {
|
|
86
|
-
const fiber = yield* bg.handler({
|
|
87
|
-
actorId,
|
|
88
|
-
state: machine.initial,
|
|
89
|
-
event: initEvent,
|
|
90
|
-
self,
|
|
91
|
-
effects: effectSlots,
|
|
92
|
-
system
|
|
93
|
-
}).pipe(Effect.provideService(machine.Context, ctx), Effect.forkIn(actorScope));
|
|
94
|
-
backgroundFibers.push(fiber);
|
|
95
|
-
}
|
|
96
|
-
if (lifecycle?.onInitialSpawnEffects !== void 0) yield* lifecycle.onInitialSpawnEffects(machine.initial);
|
|
83
|
+
const slots = machine._slots;
|
|
97
84
|
const loopFiberRef = { current: void 0 };
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
85
|
+
/** Set the exit deferred exactly once. */
|
|
86
|
+
const setExit = (exit) => Deferred.succeed(exitDeferred, exit).pipe(Effect.asVoid);
|
|
87
|
+
const startDeferred = yield* Deferred.make();
|
|
88
|
+
const startedRef = yield* Ref.make(false);
|
|
89
|
+
const start = Effect.gen(function* () {
|
|
90
|
+
if (yield* Ref.getAndSet(startedRef, true)) {
|
|
91
|
+
yield* Deferred.await(startDeferred);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const backgroundFibers = [];
|
|
95
|
+
for (const bg of machine.backgroundEffects) {
|
|
96
|
+
const fiber = yield* bg.handler({
|
|
97
|
+
actorId,
|
|
98
|
+
state: machine.initial,
|
|
99
|
+
event: initEvent,
|
|
100
|
+
self,
|
|
101
|
+
slots,
|
|
102
|
+
system
|
|
103
|
+
}).pipe(Effect.provideService(machine.Context, ctx), Effect.forkIn(actorScope));
|
|
104
|
+
backgroundFibers.push(fiber);
|
|
105
|
+
}
|
|
106
|
+
if (lifecycle?.onInitialSpawnEffects !== void 0) yield* lifecycle.onInitialSpawnEffects(machine.initial);
|
|
107
|
+
const initialSpawnDefectSignal = (cause) => Deferred.succeed(exitDeferred, ActorExit.Defect(cause, "initial-spawn")).pipe(Effect.andThen(Ref.set(stoppedRef, true)), Effect.andThen(Effect.suspend(() => loopFiberRef.current !== void 0 ? Fiber.interrupt(loopFiberRef.current) : Effect.void)), Effect.asVoid);
|
|
108
|
+
yield* runSpawnEffects(machine, machine.initial, initEvent, self, stateScopeRef.current, system, actorId, hooks?.onError, initialSpawnDefectSignal).pipe(Effect.catchCause((cause) => {
|
|
109
|
+
return Effect.gen(function* () {
|
|
110
|
+
yield* Ref.set(stoppedRef, true);
|
|
111
|
+
yield* Scope.close(stateScopeRef.current, Exit.void);
|
|
112
|
+
yield* Scope.close(actorScope, Exit.void);
|
|
113
|
+
yield* Deferred.succeed(exitDeferred, ActorExit.Defect(cause, "initial-spawn"));
|
|
114
|
+
return yield* Effect.failCause(cause);
|
|
115
|
+
});
|
|
116
|
+
}));
|
|
117
|
+
if (machine.finalStates.has(machine.initial._tag)) {
|
|
118
|
+
if (lifecycle?.onFinal !== void 0) yield* lifecycle.onFinal(machine.initial);
|
|
101
119
|
yield* Ref.set(stoppedRef, true);
|
|
102
120
|
yield* Scope.close(stateScopeRef.current, Exit.void);
|
|
103
121
|
yield* Scope.close(actorScope, Exit.void);
|
|
104
|
-
yield*
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
yield*
|
|
113
|
-
|
|
114
|
-
yield*
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (exit._tag === "Failure" && !Cause.hasInterruptsOnly(exit.cause)) return setExit(ActorExit.Defect(exit.cause, "background")).pipe(Effect.andThen(Ref.set(stoppedRef, true)), Effect.andThen(Fiber.interrupt(loopFiber)));
|
|
126
|
-
return Effect.never;
|
|
127
|
-
})))).pipe(Effect.forkIn(actorScope));
|
|
128
|
-
yield* Effect.forkDetach(Effect.gen(function* () {
|
|
129
|
-
const loopExit = yield* Fiber.await(loopFiber);
|
|
130
|
-
if (loopExit._tag === "Success") yield* Scope.close(actorScope, Exit.void);
|
|
131
|
-
else yield* Scope.close(actorScope, loopExit);
|
|
132
|
-
}));
|
|
122
|
+
yield* setExit(ActorExit.Final(machine.initial));
|
|
123
|
+
yield* Deferred.succeed(startDeferred, void 0);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const augmentedHooks = {
|
|
127
|
+
...hooks,
|
|
128
|
+
onSpawnDefect: (cause) => Deferred.succeed(exitDeferred, ActorExit.Defect(cause, "spawn")).pipe(Effect.andThen(Ref.set(stoppedRef, true)), Effect.andThen(Effect.suspend(() => loopFiberRef.current !== void 0 ? Fiber.interrupt(loopFiberRef.current) : Effect.void)), Effect.asVoid)
|
|
129
|
+
};
|
|
130
|
+
const loopFiber = yield* Effect.forkDetach(runtimeEventLoop(machine, stateRef, eventQueue, stoppedRef, self, stateScopeRef, actorId, system, exitDeferred, augmentedHooks, deferredReplyRef, lifecycle, config.wrapProcess, fork));
|
|
131
|
+
loopFiberRef.current = loopFiber;
|
|
132
|
+
if (backgroundFibers.length > 0) yield* Effect.raceAll(backgroundFibers.map((fiber) => Fiber.await(fiber).pipe(Effect.flatMap((exit) => {
|
|
133
|
+
if (exit._tag === "Failure" && !Cause.hasInterruptsOnly(exit.cause)) return setExit(ActorExit.Defect(exit.cause, "background")).pipe(Effect.andThen(Ref.set(stoppedRef, true)), Effect.andThen(Fiber.interrupt(loopFiber)));
|
|
134
|
+
return Effect.never;
|
|
135
|
+
})))).pipe(Effect.forkIn(actorScope));
|
|
136
|
+
yield* Effect.forkDetach(Effect.gen(function* () {
|
|
137
|
+
const loopExit = yield* Fiber.await(loopFiber);
|
|
138
|
+
if (loopExit._tag === "Success") yield* Scope.close(actorScope, Exit.void);
|
|
139
|
+
else yield* Scope.close(actorScope, loopExit);
|
|
140
|
+
}));
|
|
141
|
+
yield* Deferred.succeed(startDeferred, void 0);
|
|
142
|
+
}).pipe(Effect.catchCause((cause) => Deferred.failCause(startDeferred, cause).pipe(Effect.andThen(Effect.failCause(cause)))));
|
|
133
143
|
const stop = Effect.gen(function* () {
|
|
134
144
|
if (yield* Ref.get(stoppedRef)) return;
|
|
135
145
|
if (lifecycle?.onShutdown !== void 0) yield* lifecycle.onShutdown();
|
|
136
146
|
yield* Ref.set(stoppedRef, true);
|
|
137
|
-
|
|
147
|
+
const loopFiber = loopFiberRef.current;
|
|
148
|
+
if (loopFiber !== void 0) yield* Fiber.interrupt(loopFiber);
|
|
138
149
|
yield* Scope.close(stateScopeRef.current, Exit.void);
|
|
139
150
|
yield* Scope.close(actorScope, Exit.void);
|
|
140
151
|
yield* setExit(ActorExit.Stopped);
|
|
@@ -142,7 +153,8 @@ const createRuntime = Effect.fn("effect-machine.runtime.create")(function* (mach
|
|
|
142
153
|
if (config.skipFinalizer !== true) yield* Effect.addFinalizer(() => stop);
|
|
143
154
|
return {
|
|
144
155
|
...makeHandle(stateRef, stoppedRef, eventQueue, exitDeferred, actorScope),
|
|
145
|
-
stop
|
|
156
|
+
stop,
|
|
157
|
+
start
|
|
146
158
|
};
|
|
147
159
|
});
|
|
148
160
|
/**
|
|
@@ -184,6 +196,7 @@ const makeHandle = (stateRef, stoppedRef, eventQueue, exitDeferred, actorScope)
|
|
|
184
196
|
stateRef,
|
|
185
197
|
isStopped: Ref.get(stoppedRef),
|
|
186
198
|
stop: Effect.void,
|
|
199
|
+
start: Effect.void,
|
|
187
200
|
_queue: eventQueue,
|
|
188
201
|
_stoppedRef: stoppedRef,
|
|
189
202
|
exitDeferred,
|
|
@@ -278,12 +291,7 @@ const runtimeEventLoop = Effect.fn("effect-machine.runtime.eventLoop")(function*
|
|
|
278
291
|
yield* Ref.set(stoppedRef, true);
|
|
279
292
|
if (lifecycle?.onShutdown !== void 0) yield* lifecycle.onShutdown();
|
|
280
293
|
settlePostponed(postponed, actorId, forkEffect);
|
|
281
|
-
const remaining =
|
|
282
|
-
let next = yield* Queue.poll(eventQueue);
|
|
283
|
-
while (Option.isSome(next)) {
|
|
284
|
-
remaining.push(next.value);
|
|
285
|
-
next = yield* Queue.poll(eventQueue);
|
|
286
|
-
}
|
|
294
|
+
const remaining = yield* Queue.clear(eventQueue);
|
|
287
295
|
for (const entry of remaining) if (entry._tag === "sendWait") forkEffect(Deferred.succeed(entry.done, void 0));
|
|
288
296
|
else if (entry._tag === "ask") forkEffect(Deferred.fail(entry.reply, new NoReplyError({
|
|
289
297
|
actorId,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MachineContext, SlotsDef } from "../slot.js";
|
|
2
2
|
import { Machine, MachineRef, SpawnEffect, Transition } from "../machine.js";
|
|
3
3
|
import { ActorSystem } from "../actor.js";
|
|
4
4
|
import { Cause, Effect, Scope } from "effect";
|
|
@@ -29,7 +29,7 @@ declare const runTransitionHandler: <S extends {
|
|
|
29
29
|
readonly _tag: string;
|
|
30
30
|
}, E extends {
|
|
31
31
|
readonly _tag: string;
|
|
32
|
-
}, R,
|
|
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: ActorSystem, actorId: string) => Effect.Effect<{
|
|
33
33
|
newState: S;
|
|
34
34
|
hasReply: boolean;
|
|
35
35
|
deferReply: boolean;
|
|
@@ -50,7 +50,7 @@ declare const executeTransition: <S extends {
|
|
|
50
50
|
readonly _tag: string;
|
|
51
51
|
}, E extends {
|
|
52
52
|
readonly _tag: string;
|
|
53
|
-
}, R,
|
|
53
|
+
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, currentState: S, event: E, self: MachineRef<E>, system: ActorSystem, actorId: string) => Effect.Effect<{
|
|
54
54
|
newState: S;
|
|
55
55
|
transitioned: boolean;
|
|
56
56
|
reenter: boolean;
|
|
@@ -111,7 +111,7 @@ declare const shouldPostpone: <S extends {
|
|
|
111
111
|
readonly _tag: string;
|
|
112
112
|
}, E extends {
|
|
113
113
|
readonly _tag: string;
|
|
114
|
-
}, R>(machine: Machine<S, E, R, any, any, any
|
|
114
|
+
}, R>(machine: Machine<S, E, R, any, any, any>, stateTag: string, eventTag: string) => boolean;
|
|
115
115
|
/**
|
|
116
116
|
* Process a single event through the machine.
|
|
117
117
|
*
|
|
@@ -128,7 +128,7 @@ declare const processEventCore: <S extends {
|
|
|
128
128
|
readonly _tag: string;
|
|
129
129
|
}, E extends {
|
|
130
130
|
readonly _tag: string;
|
|
131
|
-
}, R,
|
|
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
133
|
}, system: ActorSystem, actorId: string, hooks?: ProcessEventHooks<S, E> | undefined) => Effect.Effect<{
|
|
134
134
|
newState: S;
|
|
@@ -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,
|
|
153
|
+
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, state: S, event: E, self: MachineRef<E>, stateScope: Scope.Closeable, system: ActorSystem, 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, MachineContext<S, E, MachineRef<E>>>, 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.
|
|
@@ -159,7 +159,7 @@ declare const resolveTransition: <S extends {
|
|
|
159
159
|
readonly _tag: string;
|
|
160
160
|
}, E extends {
|
|
161
161
|
readonly _tag: string;
|
|
162
|
-
}, R>(machine: Machine<S, E, R, any, any, any
|
|
162
|
+
}, R>(machine: Machine<S, E, R, any, any, any>, currentState: S, event: E) => (typeof machine.transitions)[number] | undefined;
|
|
163
163
|
/**
|
|
164
164
|
* Invalidate cached index for a machine (call after mutation).
|
|
165
165
|
*/
|
|
@@ -174,7 +174,7 @@ declare const findTransitions: <S extends {
|
|
|
174
174
|
readonly _tag: string;
|
|
175
175
|
}, E extends {
|
|
176
176
|
readonly _tag: string;
|
|
177
|
-
}, R,
|
|
177
|
+
}, R, SD extends SlotsDef = Record<string, never>>(machine: Machine<S, E, R, any, any, SD>, stateTag: string, eventTag: string) => ReadonlyArray<Transition<S, E, SD, R>>;
|
|
178
178
|
/**
|
|
179
179
|
* Find all spawn effects for a state.
|
|
180
180
|
* Returns empty array if no matches.
|
|
@@ -185,6 +185,6 @@ declare const findSpawnEffects: <S extends {
|
|
|
185
185
|
readonly _tag: string;
|
|
186
186
|
}, E extends {
|
|
187
187
|
readonly _tag: string;
|
|
188
|
-
}, R,
|
|
188
|
+
}, R, SD extends SlotsDef = Record<string, never>>(machine: Machine<S, E, R, any, any, SD>, stateTag: string) => ReadonlyArray<SpawnEffect<S, E, SD, R>>;
|
|
189
189
|
//#endregion
|
|
190
190
|
export { ProcessEventError, ProcessEventHooks, ProcessEventResult, TransitionExecutionResult, executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler, shouldPostpone };
|
|
@@ -29,12 +29,10 @@ const runTransitionHandler = Effect.fn("effect-machine.runTransitionHandler")(fu
|
|
|
29
29
|
self,
|
|
30
30
|
system
|
|
31
31
|
};
|
|
32
|
-
const { guards, effects } = machine._slots;
|
|
33
32
|
const handlerCtx = {
|
|
34
33
|
state,
|
|
35
34
|
event,
|
|
36
|
-
|
|
37
|
-
effects
|
|
35
|
+
slots: machine._slots
|
|
38
36
|
};
|
|
39
37
|
const raw = transition.handler(handlerCtx);
|
|
40
38
|
const resolved = isEffect(raw) ? yield* raw.pipe(Effect.provideService(machine.Context, ctx)) : raw;
|
|
@@ -166,7 +164,7 @@ const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (m
|
|
|
166
164
|
self,
|
|
167
165
|
system
|
|
168
166
|
};
|
|
169
|
-
const
|
|
167
|
+
const slots = machine._slots;
|
|
170
168
|
const reportError = onError;
|
|
171
169
|
const defectSignal = onSpawnDefect;
|
|
172
170
|
for (const spawnEffect of spawnEffects) {
|
|
@@ -175,7 +173,7 @@ const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (m
|
|
|
175
173
|
state,
|
|
176
174
|
event,
|
|
177
175
|
self,
|
|
178
|
-
|
|
176
|
+
slots,
|
|
179
177
|
system
|
|
180
178
|
}).pipe(Effect.provideService(machine.Context, ctx), Effect.catchCause((cause) => {
|
|
181
179
|
if (Cause.hasInterruptsOnly(cause)) return Effect.interrupt;
|