effect-machine 0.11.0 → 0.12.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/dist/actor.d.ts +10 -4
- package/dist/actor.js +33 -5
- package/dist/cluster/adapters/in-memory.d.ts +28 -0
- package/dist/cluster/adapters/in-memory.js +79 -0
- package/dist/cluster/entity-actor-ref.d.ts +56 -0
- package/dist/cluster/entity-actor-ref.js +33 -0
- package/dist/cluster/entity-machine.d.ts +31 -49
- package/dist/cluster/entity-machine.js +167 -52
- package/dist/cluster/index.d.ts +5 -2
- package/dist/cluster/index.js +4 -1
- package/dist/cluster/persistence.d.ts +49 -0
- package/dist/cluster/persistence.js +18 -0
- package/dist/cluster/to-entity.d.ts +9 -3
- package/dist/cluster/to-entity.js +16 -4
- package/dist/errors.d.ts +12 -1
- package/dist/errors.js +8 -1
- package/dist/index.d.ts +3 -2
- package/dist/internal/brands.d.ts +14 -1
- package/dist/internal/runtime.d.ts +67 -0
- package/dist/internal/runtime.js +248 -0
- package/dist/internal/transition.d.ts +5 -0
- package/dist/internal/transition.js +15 -3
- package/dist/internal/utils.d.ts +42 -6
- package/dist/internal/utils.js +27 -1
- package/dist/machine.d.ts +26 -13
- package/dist/machine.js +14 -3
- package/dist/schema.d.ts +35 -34
- package/dist/schema.js +32 -3
- package/dist/testing.js +4 -2
- package/package.json +3 -3
- package/v3/dist/actor.d.ts +4 -3
- package/v3/dist/actor.js +15 -3
- package/v3/dist/cluster/adapters/in-memory.d.ts +15 -0
- package/v3/dist/cluster/adapters/in-memory.js +62 -0
- package/v3/dist/cluster/entity-actor-ref.d.ts +49 -0
- package/v3/dist/cluster/entity-actor-ref.js +19 -0
- package/v3/dist/cluster/entity-machine.d.ts +34 -49
- package/v3/dist/cluster/entity-machine.js +134 -50
- package/v3/dist/cluster/index.d.ts +5 -2
- package/v3/dist/cluster/index.js +4 -1
- package/v3/dist/cluster/persistence.d.ts +48 -0
- package/v3/dist/cluster/persistence.js +14 -0
- package/v3/dist/cluster/to-entity.d.ts +5 -2
- package/v3/dist/cluster/to-entity.js +12 -4
- package/v3/dist/errors.d.ts +16 -1
- package/v3/dist/errors.js +8 -1
- package/v3/dist/index.d.ts +3 -2
- package/v3/dist/internal/brands.d.ts +15 -1
- package/v3/dist/internal/runtime.d.ts +65 -0
- package/v3/dist/internal/runtime.js +236 -0
- package/v3/dist/internal/transition.d.ts +5 -0
- package/v3/dist/internal/transition.js +15 -3
- package/v3/dist/internal/utils.d.ts +42 -6
- package/v3/dist/internal/utils.js +27 -1
- package/v3/dist/machine.d.ts +19 -13
- package/v3/dist/machine.js +10 -2
- package/v3/dist/schema.d.ts +35 -34
- package/v3/dist/schema.js +29 -3
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { INTERNAL_INIT_EVENT } from "./utils.js";
|
|
2
|
+
import { NoReplyError } from "../errors.js";
|
|
3
|
+
import { processEventCore, runSpawnEffects, shouldPostpone } from "./transition.js";
|
|
4
|
+
import { ActorSystem } from "../actor.js";
|
|
5
|
+
import { Deferred, Effect, Exit, Fiber, Queue, Ref, Schema, Scope, SubscriptionRef } from "effect";
|
|
6
|
+
//#region src/internal/runtime.ts
|
|
7
|
+
/**
|
|
8
|
+
* Shared runtime kernel for machine event processing.
|
|
9
|
+
*
|
|
10
|
+
* Provides a single-queue event loop with:
|
|
11
|
+
* - Sequential event processing (no split-mailbox race)
|
|
12
|
+
* - Postpone buffer with drain-on-state-change (gen_statem)
|
|
13
|
+
* - Background effect lifecycle
|
|
14
|
+
* - Spawn effect lifecycle (per-state scope)
|
|
15
|
+
* - Final state detection → stop
|
|
16
|
+
* - Reply settlement (call/ask Deferreds)
|
|
17
|
+
* - Reply schema validation
|
|
18
|
+
*
|
|
19
|
+
* Used by entity-machine. Local actor (actor.ts) has its own event loop
|
|
20
|
+
* with additional concerns (inspection, listeners, subscription ref, etc.)
|
|
21
|
+
* that will be migrated to use this kernel in a future refactor.
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Create a runtime for a machine. Returns a handle for sending events
|
|
27
|
+
* and querying state. The runtime owns:
|
|
28
|
+
* - Single event queue (all events serialized)
|
|
29
|
+
* - Event loop fiber
|
|
30
|
+
* - Postpone buffer
|
|
31
|
+
* - Background effects
|
|
32
|
+
* - State scope (spawn effects)
|
|
33
|
+
* - Final state detection
|
|
34
|
+
*
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
const createRuntime = Effect.fn("effect-machine.runtime.create")(function* (machine, system, config) {
|
|
38
|
+
const { actorId, hooks } = config;
|
|
39
|
+
const stateRef = yield* SubscriptionRef.make(machine.initial);
|
|
40
|
+
const stoppedRef = yield* Ref.make(false);
|
|
41
|
+
const eventQueue = yield* config.queueFactory ?? Queue.unbounded();
|
|
42
|
+
const deferredReplyRef = { current: void 0 };
|
|
43
|
+
const selfSend = Effect.fn("effect-machine.runtime.self.send")(function* (event) {
|
|
44
|
+
if (!(yield* Ref.get(stoppedRef))) yield* Queue.offer(eventQueue, {
|
|
45
|
+
_tag: "send",
|
|
46
|
+
event
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
const self = {
|
|
50
|
+
send: selfSend,
|
|
51
|
+
cast: selfSend,
|
|
52
|
+
spawn: (childId, childMachine) => system.spawn(`${actorId}/${childId}`, childMachine).pipe(Effect.provideService(ActorSystem, system)),
|
|
53
|
+
reply: (value) => Effect.sync(() => {
|
|
54
|
+
const deferred = deferredReplyRef.current;
|
|
55
|
+
if (deferred !== void 0) {
|
|
56
|
+
deferredReplyRef.current = void 0;
|
|
57
|
+
Effect.runFork(Deferred.succeed(deferred, value));
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
})
|
|
62
|
+
};
|
|
63
|
+
const stateScopeRef = { current: yield* Scope.make() };
|
|
64
|
+
const backgroundFibers = [];
|
|
65
|
+
const initEvent = { _tag: INTERNAL_INIT_EVENT };
|
|
66
|
+
const ctx = {
|
|
67
|
+
actorId,
|
|
68
|
+
state: machine.initial,
|
|
69
|
+
event: initEvent,
|
|
70
|
+
self,
|
|
71
|
+
system
|
|
72
|
+
};
|
|
73
|
+
const { effects: effectSlots } = machine._slots;
|
|
74
|
+
for (const bg of machine.backgroundEffects) {
|
|
75
|
+
const fiber = yield* Effect.forkDetach(bg.handler({
|
|
76
|
+
actorId,
|
|
77
|
+
state: machine.initial,
|
|
78
|
+
event: initEvent,
|
|
79
|
+
self,
|
|
80
|
+
effects: effectSlots,
|
|
81
|
+
system
|
|
82
|
+
}).pipe(Effect.provideService(machine.Context, ctx)));
|
|
83
|
+
backgroundFibers.push(fiber);
|
|
84
|
+
}
|
|
85
|
+
yield* runSpawnEffects(machine, machine.initial, initEvent, self, stateScopeRef.current, system, actorId, hooks?.onError);
|
|
86
|
+
if (machine.finalStates.has(machine.initial._tag)) {
|
|
87
|
+
yield* Ref.set(stoppedRef, true);
|
|
88
|
+
yield* Scope.close(stateScopeRef.current, Exit.void);
|
|
89
|
+
yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
|
|
90
|
+
return makeHandle(stateRef, stoppedRef, eventQueue, machine);
|
|
91
|
+
}
|
|
92
|
+
const loopFiber = yield* Effect.forkDetach(runtimeEventLoop(machine, stateRef, eventQueue, stoppedRef, self, backgroundFibers, stateScopeRef, actorId, system, hooks, deferredReplyRef));
|
|
93
|
+
const stop = Effect.gen(function* () {
|
|
94
|
+
if (yield* Ref.get(stoppedRef)) return;
|
|
95
|
+
yield* Ref.set(stoppedRef, true);
|
|
96
|
+
yield* Fiber.interrupt(loopFiber);
|
|
97
|
+
yield* Scope.close(stateScopeRef.current, Exit.void);
|
|
98
|
+
yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
|
|
99
|
+
}).pipe(Effect.asVoid);
|
|
100
|
+
yield* Effect.addFinalizer(() => stop);
|
|
101
|
+
return {
|
|
102
|
+
...makeHandle(stateRef, stoppedRef, eventQueue, machine),
|
|
103
|
+
stop
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
/**
|
|
107
|
+
* Build the runtime handle (send/ask/getState/isStopped).
|
|
108
|
+
* Shared between initial-final and normal paths.
|
|
109
|
+
*/
|
|
110
|
+
const makeHandle = (stateRef, stoppedRef, eventQueue, _machine) => ({
|
|
111
|
+
send: (event) => Effect.gen(function* () {
|
|
112
|
+
if (!(yield* Ref.get(stoppedRef))) yield* Queue.offer(eventQueue, {
|
|
113
|
+
_tag: "send",
|
|
114
|
+
event
|
|
115
|
+
});
|
|
116
|
+
}),
|
|
117
|
+
sendWait: (event) => Effect.gen(function* () {
|
|
118
|
+
if (!(yield* Ref.get(stoppedRef))) {
|
|
119
|
+
const done = yield* Deferred.make();
|
|
120
|
+
yield* Queue.offer(eventQueue, {
|
|
121
|
+
_tag: "sendWait",
|
|
122
|
+
event,
|
|
123
|
+
done
|
|
124
|
+
});
|
|
125
|
+
yield* Deferred.await(done);
|
|
126
|
+
}
|
|
127
|
+
}),
|
|
128
|
+
ask: (event) => Effect.gen(function* () {
|
|
129
|
+
if (yield* Ref.get(stoppedRef)) return yield* new NoReplyError({
|
|
130
|
+
actorId: "stopped",
|
|
131
|
+
eventTag: event._tag
|
|
132
|
+
});
|
|
133
|
+
const reply = yield* Deferred.make();
|
|
134
|
+
yield* Queue.offer(eventQueue, {
|
|
135
|
+
_tag: "ask",
|
|
136
|
+
event,
|
|
137
|
+
reply
|
|
138
|
+
});
|
|
139
|
+
return yield* Deferred.await(reply);
|
|
140
|
+
}),
|
|
141
|
+
getState: SubscriptionRef.get(stateRef),
|
|
142
|
+
stateRef,
|
|
143
|
+
isStopped: Ref.get(stoppedRef),
|
|
144
|
+
stop: Effect.void
|
|
145
|
+
});
|
|
146
|
+
const runtimeEventLoop = Effect.fn("effect-machine.runtime.eventLoop")(function* (machine, stateRef, eventQueue, stoppedRef, self, backgroundFibers, stateScopeRef, actorId, system, hooks, deferredReplyRef) {
|
|
147
|
+
const postponed = [];
|
|
148
|
+
const hasPostponeRules = machine.postponeRules.length > 0;
|
|
149
|
+
const processQueued = Effect.fn("effect-machine.runtime.processQueued")(function* (queued) {
|
|
150
|
+
const event = queued.event;
|
|
151
|
+
const currentState = yield* SubscriptionRef.get(stateRef);
|
|
152
|
+
if (hasPostponeRules && shouldPostpone(machine, currentState._tag, event._tag)) {
|
|
153
|
+
postponed.push({
|
|
154
|
+
_tag: "send",
|
|
155
|
+
event
|
|
156
|
+
});
|
|
157
|
+
if (queued._tag === "sendWait") yield* Deferred.succeed(queued.done, void 0);
|
|
158
|
+
return {
|
|
159
|
+
shouldStop: false,
|
|
160
|
+
stateChanged: false
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const result = yield* processEventCore(machine, currentState, event, self, stateScopeRef, system, actorId, hooks);
|
|
164
|
+
if (result.transitioned) yield* SubscriptionRef.set(stateRef, result.newState);
|
|
165
|
+
switch (queued._tag) {
|
|
166
|
+
case "sendWait":
|
|
167
|
+
yield* Deferred.succeed(queued.done, void 0);
|
|
168
|
+
break;
|
|
169
|
+
case "ask":
|
|
170
|
+
if (result.hasReply) {
|
|
171
|
+
const replySchema = machine._replySchemas?.get(event._tag);
|
|
172
|
+
if (replySchema !== void 0) {
|
|
173
|
+
let decoded;
|
|
174
|
+
try {
|
|
175
|
+
decoded = Schema.decodeUnknownSync(replySchema)(result.reply);
|
|
176
|
+
} catch (decodeError) {
|
|
177
|
+
yield* Deferred.die(queued.reply, decodeError);
|
|
178
|
+
return yield* Effect.die(decodeError);
|
|
179
|
+
}
|
|
180
|
+
yield* Deferred.succeed(queued.reply, decoded);
|
|
181
|
+
} else yield* Deferred.succeed(queued.reply, result.reply);
|
|
182
|
+
} else if (result.deferReply && deferredReplyRef !== void 0) deferredReplyRef.current = queued.reply;
|
|
183
|
+
else yield* Deferred.fail(queued.reply, new NoReplyError({
|
|
184
|
+
actorId,
|
|
185
|
+
eventTag: event._tag
|
|
186
|
+
}));
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
shouldStop: result.isFinal && result.lifecycleRan,
|
|
191
|
+
stateChanged: result.lifecycleRan
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
while (true) {
|
|
195
|
+
const queued = yield* Queue.take(eventQueue);
|
|
196
|
+
const { shouldStop, stateChanged } = yield* processQueued(queued).pipe(Effect.catchCause((cause) => {
|
|
197
|
+
if (queued._tag === "sendWait") Effect.runFork(Deferred.failCause(queued.done, cause));
|
|
198
|
+
else if (queued._tag === "ask") Effect.runFork(Deferred.die(queued.reply, cause));
|
|
199
|
+
return Effect.failCause(cause);
|
|
200
|
+
}));
|
|
201
|
+
if (shouldStop) {
|
|
202
|
+
yield* Ref.set(stoppedRef, true);
|
|
203
|
+
settlePostponed(postponed, actorId);
|
|
204
|
+
const remaining = yield* Queue.takeAll(eventQueue);
|
|
205
|
+
for (const entry of remaining) if (entry._tag === "sendWait") Effect.runFork(Deferred.succeed(entry.done, void 0));
|
|
206
|
+
else if (entry._tag === "ask") Effect.runFork(Deferred.fail(entry.reply, new NoReplyError({
|
|
207
|
+
actorId,
|
|
208
|
+
eventTag: entry.event._tag
|
|
209
|
+
})));
|
|
210
|
+
yield* Scope.close(stateScopeRef.current, Exit.void);
|
|
211
|
+
yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
let drainTriggered = stateChanged;
|
|
215
|
+
while (drainTriggered && postponed.length > 0) {
|
|
216
|
+
drainTriggered = false;
|
|
217
|
+
const drained = postponed.splice(0);
|
|
218
|
+
for (const entry of drained) {
|
|
219
|
+
const drain = yield* processQueued(entry);
|
|
220
|
+
if (drain.shouldStop) {
|
|
221
|
+
yield* Ref.set(stoppedRef, true);
|
|
222
|
+
settlePostponed(postponed, actorId);
|
|
223
|
+
const remaining2 = yield* Queue.takeAll(eventQueue);
|
|
224
|
+
for (const r of remaining2) if (r._tag === "sendWait") Effect.runFork(Deferred.succeed(r.done, void 0));
|
|
225
|
+
else if (r._tag === "ask") Effect.runFork(Deferred.fail(r.reply, new NoReplyError({
|
|
226
|
+
actorId,
|
|
227
|
+
eventTag: r.event._tag
|
|
228
|
+
})));
|
|
229
|
+
yield* Scope.close(stateScopeRef.current, Exit.void);
|
|
230
|
+
yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (drain.stateChanged) drainTriggered = true;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
/** Settle all pending Deferreds in the postpone buffer on shutdown. */
|
|
239
|
+
const settlePostponed = (postponed, actorId) => {
|
|
240
|
+
for (const entry of postponed) if (entry._tag === "ask") Effect.runFork(Deferred.fail(entry.reply, new NoReplyError({
|
|
241
|
+
actorId,
|
|
242
|
+
eventTag: entry.event._tag
|
|
243
|
+
})));
|
|
244
|
+
else if (entry._tag === "sendWait") Effect.runFork(Deferred.succeed(entry.done, void 0));
|
|
245
|
+
postponed.length = 0;
|
|
246
|
+
};
|
|
247
|
+
//#endregion
|
|
248
|
+
export { createRuntime };
|
|
@@ -32,6 +32,7 @@ declare const runTransitionHandler: <S extends {
|
|
|
32
32
|
}, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, transition: Transition<S, E, GD, EFD, R>, state: S, event: E, self: MachineRef<E>, system: ActorSystem, actorId: string) => Effect.Effect<{
|
|
33
33
|
newState: S;
|
|
34
34
|
hasReply: boolean;
|
|
35
|
+
deferReply: boolean;
|
|
35
36
|
reply: unknown;
|
|
36
37
|
}, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
|
|
37
38
|
/**
|
|
@@ -54,6 +55,7 @@ declare const executeTransition: <S extends {
|
|
|
54
55
|
transitioned: boolean;
|
|
55
56
|
reenter: boolean;
|
|
56
57
|
hasReply: boolean;
|
|
58
|
+
deferReply: boolean;
|
|
57
59
|
reply: unknown;
|
|
58
60
|
}, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
|
|
59
61
|
/**
|
|
@@ -92,6 +94,8 @@ interface ProcessEventResult<S> {
|
|
|
92
94
|
readonly isFinal: boolean;
|
|
93
95
|
/** Whether the handler provided a reply (structural, not value-based) */
|
|
94
96
|
readonly hasReply: boolean;
|
|
97
|
+
/** Whether the handler deferred the reply to a spawn handler (Machine.deferReply) */
|
|
98
|
+
readonly deferReply: boolean;
|
|
95
99
|
/** Domain reply value from handler (used by ask). Only meaningful when hasReply is true. */
|
|
96
100
|
readonly reply?: unknown;
|
|
97
101
|
/** Whether the event was postponed (buffered for retry after next state change) */
|
|
@@ -131,6 +135,7 @@ declare const processEventCore: <S extends {
|
|
|
131
135
|
lifecycleRan: boolean;
|
|
132
136
|
isFinal: boolean;
|
|
133
137
|
hasReply: boolean;
|
|
138
|
+
deferReply: boolean;
|
|
134
139
|
reply: unknown;
|
|
135
140
|
postponed: boolean;
|
|
136
141
|
}, never, Exclude<R, MachineContext<S, E, MachineRef<E>>> | Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { INTERNAL_ENTER_EVENT, isEffect } from "./utils.js";
|
|
1
|
+
import { INTERNAL_ENTER_EVENT, isDeferReplyResult, isEffect, isReplyResult } from "./utils.js";
|
|
2
2
|
import { BuiltMachine } from "../machine.js";
|
|
3
3
|
import { Cause, Effect, Exit, Scope } from "effect";
|
|
4
4
|
//#region src/internal/transition.ts
|
|
@@ -39,14 +39,22 @@ const runTransitionHandler = Effect.fn("effect-machine.runTransitionHandler")(fu
|
|
|
39
39
|
};
|
|
40
40
|
const raw = transition.handler(handlerCtx);
|
|
41
41
|
const resolved = isEffect(raw) ? yield* raw.pipe(Effect.provideService(machine.Context, ctx)) : raw;
|
|
42
|
-
if (
|
|
42
|
+
if (isReplyResult(resolved)) return {
|
|
43
43
|
newState: resolved.state,
|
|
44
44
|
hasReply: true,
|
|
45
|
+
deferReply: false,
|
|
45
46
|
reply: resolved.reply
|
|
46
47
|
};
|
|
48
|
+
if (isDeferReplyResult(resolved)) return {
|
|
49
|
+
newState: resolved.state,
|
|
50
|
+
hasReply: false,
|
|
51
|
+
deferReply: true,
|
|
52
|
+
reply: void 0
|
|
53
|
+
};
|
|
47
54
|
return {
|
|
48
55
|
newState: resolved,
|
|
49
56
|
hasReply: false,
|
|
57
|
+
deferReply: false,
|
|
50
58
|
reply: void 0
|
|
51
59
|
};
|
|
52
60
|
});
|
|
@@ -68,14 +76,16 @@ const executeTransition = Effect.fn("effect-machine.executeTransition")(function
|
|
|
68
76
|
transitioned: false,
|
|
69
77
|
reenter: false,
|
|
70
78
|
hasReply: false,
|
|
79
|
+
deferReply: false,
|
|
71
80
|
reply: void 0
|
|
72
81
|
};
|
|
73
|
-
const { newState, hasReply, reply } = yield* runTransitionHandler(machine, transition, currentState, event, self, system, actorId);
|
|
82
|
+
const { newState, hasReply, deferReply, reply } = yield* runTransitionHandler(machine, transition, currentState, event, self, system, actorId);
|
|
74
83
|
return {
|
|
75
84
|
newState,
|
|
76
85
|
transitioned: true,
|
|
77
86
|
reenter: transition.reenter === true,
|
|
78
87
|
hasReply,
|
|
88
|
+
deferReply,
|
|
79
89
|
reply
|
|
80
90
|
};
|
|
81
91
|
});
|
|
@@ -118,6 +128,7 @@ const processEventCore = Effect.fn("effect-machine.processEventCore")(function*
|
|
|
118
128
|
lifecycleRan: false,
|
|
119
129
|
isFinal: false,
|
|
120
130
|
hasReply: false,
|
|
131
|
+
deferReply: false,
|
|
121
132
|
reply: void 0,
|
|
122
133
|
postponed: false
|
|
123
134
|
};
|
|
@@ -137,6 +148,7 @@ const processEventCore = Effect.fn("effect-machine.processEventCore")(function*
|
|
|
137
148
|
lifecycleRan: runLifecycle,
|
|
138
149
|
isFinal: machine.finalStates.has(newState._tag),
|
|
139
150
|
hasReply: result.hasReply,
|
|
151
|
+
deferReply: result.deferReply,
|
|
140
152
|
reply: result.reply,
|
|
141
153
|
postponed: false
|
|
142
154
|
};
|
package/dist/internal/utils.d.ts
CHANGED
|
@@ -23,15 +23,51 @@ type InstanceOf<C> = C extends ((...args: unknown[]) => infer R) ? R : never;
|
|
|
23
23
|
type TaggedConstructor<T extends {
|
|
24
24
|
readonly _tag: string;
|
|
25
25
|
}> = (args: Omit<T, "_tag">) => T;
|
|
26
|
+
declare const ReplyResultSymbol: unique symbol;
|
|
27
|
+
type ReplyResultSymbol = typeof ReplyResultSymbol;
|
|
26
28
|
/**
|
|
27
|
-
*
|
|
29
|
+
* Branded reply result from a transition handler.
|
|
30
|
+
* Created via `Machine.reply(state, value)`.
|
|
28
31
|
*/
|
|
29
|
-
|
|
30
|
-
interface TransitionReply<State> {
|
|
32
|
+
interface ReplyResult<State, Reply> {
|
|
31
33
|
readonly state: State;
|
|
32
|
-
readonly reply:
|
|
34
|
+
readonly reply: Reply;
|
|
35
|
+
readonly [ReplyResultSymbol]: true;
|
|
33
36
|
}
|
|
34
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Create a reply result for ask-bearing event handlers.
|
|
39
|
+
*/
|
|
40
|
+
declare const makeReply: <State, Reply>(state: State, reply: Reply) => ReplyResult<State, Reply>;
|
|
41
|
+
/**
|
|
42
|
+
* Type guard for ReplyResult (symbol-based, replaces duck-typing).
|
|
43
|
+
*/
|
|
44
|
+
declare const isReplyResult: (value: unknown) => value is ReplyResult<unknown, unknown>;
|
|
45
|
+
declare const DeferReplySymbol: unique symbol;
|
|
46
|
+
type DeferReplySymbol = typeof DeferReplySymbol;
|
|
47
|
+
/**
|
|
48
|
+
* Branded deferred reply result from a transition handler.
|
|
49
|
+
* Signals that the reply will be settled later by `self.reply()` in a spawn handler.
|
|
50
|
+
* Created via `Machine.deferReply(state)`.
|
|
51
|
+
*/
|
|
52
|
+
interface DeferReplyResult<State> {
|
|
53
|
+
readonly state: State;
|
|
54
|
+
readonly [DeferReplySymbol]: true;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create a deferred reply result. Handler returns this to signal
|
|
58
|
+
* "spawn handler will call self.reply(value) later".
|
|
59
|
+
*/
|
|
60
|
+
declare const makeDeferReply: <State>(state: State) => DeferReplyResult<State>;
|
|
61
|
+
/**
|
|
62
|
+
* Type guard for DeferReplyResult.
|
|
63
|
+
*/
|
|
64
|
+
declare const isDeferReplyResult: (value: unknown) => value is DeferReplyResult<unknown>;
|
|
65
|
+
/**
|
|
66
|
+
* Transition handler result.
|
|
67
|
+
* - When Reply is `never`: handler returns plain State (no reply allowed)
|
|
68
|
+
* - When Reply is concrete: handler must return ReplyResult via Machine.reply()
|
|
69
|
+
*/
|
|
70
|
+
type TransitionResult<State, R, Reply = never> = [Reply] extends [never] ? State | Effect.Effect<State, never, R> : ReplyResult<State, Reply> | DeferReplyResult<State> | Effect.Effect<ReplyResult<State, Reply> | DeferReplyResult<State>, never, R>;
|
|
35
71
|
/**
|
|
36
72
|
* Internal event tags used for lifecycle effect contexts.
|
|
37
73
|
* Prefixed with $ to distinguish from user events.
|
|
@@ -62,4 +98,4 @@ declare const isEffect: (value: unknown) => value is Effect.Effect<unknown, unkn
|
|
|
62
98
|
*/
|
|
63
99
|
declare const stubSystem: ActorSystem;
|
|
64
100
|
//#endregion
|
|
65
|
-
export { ArgsOf, INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, InstanceOf, TagOf, TaggedConstructor,
|
|
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
|
@@ -4,6 +4,32 @@ import { Effect, Stream } from "effect";
|
|
|
4
4
|
* Internal utilities for effect-machine.
|
|
5
5
|
* @internal
|
|
6
6
|
*/
|
|
7
|
+
const ReplyResultSymbol = Symbol.for("effect-machine/ReplyResult");
|
|
8
|
+
/**
|
|
9
|
+
* Create a reply result for ask-bearing event handlers.
|
|
10
|
+
*/
|
|
11
|
+
const makeReply = (state, reply) => ({
|
|
12
|
+
state,
|
|
13
|
+
reply,
|
|
14
|
+
[ReplyResultSymbol]: true
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Type guard for ReplyResult (symbol-based, replaces duck-typing).
|
|
18
|
+
*/
|
|
19
|
+
const isReplyResult = (value) => value !== null && typeof value === "object" && ReplyResultSymbol in value;
|
|
20
|
+
const DeferReplySymbol = Symbol.for("effect-machine/DeferReply");
|
|
21
|
+
/**
|
|
22
|
+
* Create a deferred reply result. Handler returns this to signal
|
|
23
|
+
* "spawn handler will call self.reply(value) later".
|
|
24
|
+
*/
|
|
25
|
+
const makeDeferReply = (state) => ({
|
|
26
|
+
state,
|
|
27
|
+
[DeferReplySymbol]: true
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* Type guard for DeferReplyResult.
|
|
31
|
+
*/
|
|
32
|
+
const isDeferReplyResult = (value) => value !== null && typeof value === "object" && DeferReplySymbol in value;
|
|
7
33
|
/**
|
|
8
34
|
* Internal event tags used for lifecycle effect contexts.
|
|
9
35
|
* Prefixed with $ to distinguish from user events.
|
|
@@ -46,4 +72,4 @@ const stubSystem = {
|
|
|
46
72
|
subscribe: () => () => {}
|
|
47
73
|
};
|
|
48
74
|
//#endregion
|
|
49
|
-
export { INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, getTag, isEffect, stubSystem };
|
|
75
|
+
export { INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, getTag, isDeferReplyResult, isEffect, isReplyResult, makeDeferReply, makeReply, stubSystem };
|
package/dist/machine.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EffectHandlers, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers, GuardSlots, GuardsDef, GuardsSchema, MachineContext } from "./slot.js";
|
|
2
|
-
import { TransitionResult } from "./internal/utils.js";
|
|
3
|
-
import { BrandedEvent, BrandedState, TaggedOrConstructor } from "./internal/brands.js";
|
|
2
|
+
import { DeferReplyResult, ReplyResult, TransitionResult } from "./internal/utils.js";
|
|
3
|
+
import { BrandedEvent, BrandedState, ExtractReply, TaggedOrConstructor } from "./internal/brands.js";
|
|
4
4
|
import { MachineEventSchema, MachineStateSchema, VariantsUnion } from "./schema.js";
|
|
5
5
|
import { DuplicateActorError } from "./errors.js";
|
|
6
6
|
import { findTransitions } from "./internal/transition.js";
|
|
@@ -9,7 +9,7 @@ import { Cause, Duration, Effect, Schema, Scope, ServiceMap } from "effect";
|
|
|
9
9
|
|
|
10
10
|
//#region src/machine.d.ts
|
|
11
11
|
declare namespace machine_d_exports {
|
|
12
|
-
export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, make, replay, spawn };
|
|
12
|
+
export { BackgroundEffect, BuiltMachine, DeferReplyResult, HandlerContext, Machine, MachineRef, MakeConfig, ProvideHandlers, ReplyResult, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, deferReply, findTransitions, make, replay, reply, spawn };
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* Self reference for sending events back to the machine
|
|
@@ -23,6 +23,12 @@ interface MachineRef<Event> {
|
|
|
23
23
|
}, E2 extends {
|
|
24
24
|
readonly _tag: string;
|
|
25
25
|
}, R2>(id: string, machine: BuiltMachine<S2, E2, R2>) => Effect.Effect<ActorRef<S2, E2>, DuplicateActorError, R2>;
|
|
26
|
+
/**
|
|
27
|
+
* Settle a deferred reply from a spawn handler.
|
|
28
|
+
* Only usable when the transition handler returned `Machine.deferReply(state)`.
|
|
29
|
+
* Returns true if a pending reply was settled, false if none was pending.
|
|
30
|
+
*/
|
|
31
|
+
readonly reply: (value: unknown) => Effect.Effect<boolean>;
|
|
26
32
|
}
|
|
27
33
|
/**
|
|
28
34
|
* Handler context passed to transition handlers
|
|
@@ -45,9 +51,11 @@ interface StateHandlerContext<State, Event, ED extends EffectsDef> {
|
|
|
45
51
|
readonly system: ActorSystem;
|
|
46
52
|
}
|
|
47
53
|
/**
|
|
48
|
-
* Transition handler function
|
|
54
|
+
* Transition handler function.
|
|
55
|
+
* When Reply is concrete (event has a reply schema), handler must return Machine.reply().
|
|
56
|
+
* When Reply is never, handler returns plain state.
|
|
49
57
|
*/
|
|
50
|
-
type TransitionHandler<S, E, NewState, GD extends GuardsDef, ED extends EffectsDef, R> = (ctx: HandlerContext<S, E, GD, ED>) => TransitionResult<NewState, R>;
|
|
58
|
+
type TransitionHandler<S, E, NewState, GD extends GuardsDef, ED extends EffectsDef, R, Reply = never> = (ctx: HandlerContext<S, E, GD, ED>) => TransitionResult<NewState, R, Reply>;
|
|
51
59
|
/**
|
|
52
60
|
* State effect handler function
|
|
53
61
|
*/
|
|
@@ -171,6 +179,8 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
171
179
|
};
|
|
172
180
|
readonly stateSchema?: Schema.Schema<State>;
|
|
173
181
|
readonly eventSchema?: Schema.Schema<Event>;
|
|
182
|
+
/** @internal */
|
|
183
|
+
readonly _replySchemas: ReadonlyMap<string, Schema.Decoder<unknown>>;
|
|
174
184
|
/**
|
|
175
185
|
* Context tag for accessing machine state/event/self in slot handlers.
|
|
176
186
|
* Uses shared module-level tag for all machines.
|
|
@@ -186,24 +196,25 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
186
196
|
}>;
|
|
187
197
|
get guardsSchema(): GuardsSchema<GD> | undefined;
|
|
188
198
|
get effectsSchema(): EffectsSchema<EFD> | undefined;
|
|
199
|
+
get replySchemas(): ReadonlyMap<string, Schema.Decoder<unknown>>;
|
|
189
200
|
/** @internal */
|
|
190
201
|
constructor(initial: State, stateSchema?: Schema.Schema<State>, eventSchema?: Schema.Schema<Event>, guardsSchema?: GuardsSchema<GD>, effectsSchema?: EffectsSchema<EFD>);
|
|
191
202
|
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
203
|
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
204
|
/** @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
|
|
205
|
+
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, ExtractReply<NE>>, reenter: boolean): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
195
206
|
/** Register transition for a single state */
|
|
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
|
|
207
|
+
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, ExtractReply<NE>>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
197
208
|
/** Register transition for multiple states (handler receives union of state types) */
|
|
198
|
-
on<NS extends ReadonlyArray<TaggedOrConstructor<VariantsUnion<_SD> & BrandedState>>, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(states: NS, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS[number] extends TaggedOrConstructor<infer S> ? S : never, NE, RS, GD, EFD, never
|
|
209
|
+
on<NS extends ReadonlyArray<TaggedOrConstructor<VariantsUnion<_SD> & BrandedState>>, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(states: NS, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS[number] extends TaggedOrConstructor<infer S> ? S : never, NE, RS, GD, EFD, never, ExtractReply<NE>>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
199
210
|
/**
|
|
200
211
|
* Like `on()`, but forces onEnter/spawn to run even when transitioning to the same state tag.
|
|
201
212
|
* Use this to restart timers, re-run spawned effects, or reset state-scoped effects.
|
|
202
213
|
*/
|
|
203
214
|
/** Single state */
|
|
204
|
-
reenter<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
|
|
215
|
+
reenter<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, ExtractReply<NE>>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
205
216
|
/** Multiple states */
|
|
206
|
-
reenter<NS extends ReadonlyArray<TaggedOrConstructor<VariantsUnion<_SD> & BrandedState>>, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(states: NS, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS[number] extends TaggedOrConstructor<infer S> ? S : never, NE, RS, GD, EFD, never
|
|
217
|
+
reenter<NS extends ReadonlyArray<TaggedOrConstructor<VariantsUnion<_SD> & BrandedState>>, NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(states: NS, event: TaggedOrConstructor<NE>, handler: TransitionHandler<NS[number] extends TaggedOrConstructor<infer S> ? S : never, NE, RS, GD, EFD, never, ExtractReply<NE>>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
207
218
|
/**
|
|
208
219
|
* Register a wildcard transition that fires from any state when no specific transition matches.
|
|
209
220
|
* Specific `.on()` transitions always take priority over `.onAny()`.
|
|
@@ -314,8 +325,8 @@ declare class TransitionScope<State, Event, R, _SD extends Record<string, Schema
|
|
|
314
325
|
private readonly machine;
|
|
315
326
|
private readonly states;
|
|
316
327
|
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
|
|
318
|
-
reenter<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<SelectedState, NE, RS, GD, EFD, never
|
|
328
|
+
on<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<SelectedState, NE, RS, GD, EFD, never, ExtractReply<NE>>): TransitionScope<State, Event, R, _SD, _ED, GD, EFD, SelectedState>;
|
|
329
|
+
reenter<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<SelectedState, NE, RS, GD, EFD, never, ExtractReply<NE>>): TransitionScope<State, Event, R, _SD, _ED, GD, EFD, SelectedState>;
|
|
319
330
|
}
|
|
320
331
|
declare const make: typeof Machine.make;
|
|
321
332
|
/**
|
|
@@ -346,5 +357,7 @@ declare const replay: <S extends {
|
|
|
346
357
|
}, R>(machine: BuiltMachine<S, E, R>, events: ReadonlyArray<E>, options?: {
|
|
347
358
|
from?: S;
|
|
348
359
|
}) => Effect.Effect<S, never, R>;
|
|
360
|
+
declare const reply: <State, Reply>(state: State, reply: Reply) => ReplyResult<State, Reply>;
|
|
361
|
+
declare const deferReply: <State>(state: State) => DeferReplyResult<State>;
|
|
349
362
|
//#endregion
|
|
350
|
-
export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, machine_d_exports, make, replay, spawn };
|
|
363
|
+
export { BackgroundEffect, BuiltMachine, type DeferReplyResult, HandlerContext, Machine, MachineRef, MakeConfig, ProvideHandlers, type ReplyResult, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, deferReply, findTransitions, machine_d_exports, make, replay, reply, spawn };
|
package/dist/machine.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { Inspector } from "./inspection.js";
|
|
3
|
-
import { getTag, stubSystem } from "./internal/utils.js";
|
|
3
|
+
import { getTag, makeDeferReply, makeReply, stubSystem } from "./internal/utils.js";
|
|
4
4
|
import { ProvisionValidationError, SlotProvisionError } from "./errors.js";
|
|
5
5
|
import { emitWithTimestamp } from "./internal/inspection.js";
|
|
6
6
|
import { MachineContextTag } from "./slot.js";
|
|
@@ -11,9 +11,11 @@ import { Cause, Effect, Exit, Option, Scope } from "effect";
|
|
|
11
11
|
var machine_exports = /* @__PURE__ */ __exportAll({
|
|
12
12
|
BuiltMachine: () => BuiltMachine,
|
|
13
13
|
Machine: () => Machine,
|
|
14
|
+
deferReply: () => deferReply,
|
|
14
15
|
findTransitions: () => findTransitions,
|
|
15
16
|
make: () => make,
|
|
16
17
|
replay: () => replay,
|
|
18
|
+
reply: () => reply,
|
|
17
19
|
spawn: () => spawn
|
|
18
20
|
});
|
|
19
21
|
const emitTaskInspection = (input) => Effect.flatMap(Effect.serviceOption(Inspector), (inspector) => Option.isNone(inspector) ? Effect.void : emitWithTimestamp(inspector.value, (timestamp) => ({
|
|
@@ -69,6 +71,7 @@ var Machine = class Machine {
|
|
|
69
71
|
/** @internal */ _slots;
|
|
70
72
|
stateSchema;
|
|
71
73
|
eventSchema;
|
|
74
|
+
/** @internal */ _replySchemas;
|
|
72
75
|
/**
|
|
73
76
|
* Context tag for accessing machine state/event/self in slot handlers.
|
|
74
77
|
* Uses shared module-level tag for all machines.
|
|
@@ -95,6 +98,9 @@ var Machine = class Machine {
|
|
|
95
98
|
get effectsSchema() {
|
|
96
99
|
return this._effectsSchema;
|
|
97
100
|
}
|
|
101
|
+
get replySchemas() {
|
|
102
|
+
return this._replySchemas;
|
|
103
|
+
}
|
|
98
104
|
/** @internal */
|
|
99
105
|
constructor(initial, stateSchema, eventSchema, guardsSchema, effectsSchema) {
|
|
100
106
|
this.initial = initial;
|
|
@@ -105,6 +111,7 @@ var Machine = class Machine {
|
|
|
105
111
|
this._postponeRules = [];
|
|
106
112
|
this._guardsSchema = guardsSchema;
|
|
107
113
|
this._effectsSchema = effectsSchema;
|
|
114
|
+
this._replySchemas = eventSchema?._replySchemas ?? /* @__PURE__ */ new Map();
|
|
108
115
|
this._guardHandlers = /* @__PURE__ */ new Map();
|
|
109
116
|
this._effectHandlers = /* @__PURE__ */ new Map();
|
|
110
117
|
this.stateSchema = stateSchema;
|
|
@@ -375,6 +382,7 @@ var Machine = class Machine {
|
|
|
375
382
|
result._spawnEffects = [...this._spawnEffects];
|
|
376
383
|
result._backgroundEffects = [...this._backgroundEffects];
|
|
377
384
|
result._postponeRules = [...this._postponeRules];
|
|
385
|
+
result._replySchemas = this._replySchemas;
|
|
378
386
|
const anyHandlers = handlers;
|
|
379
387
|
if (this._guardsSchema !== void 0) for (const name of Object.keys(this._guardsSchema.definitions)) result._guardHandlers.set(name, anyHandlers[name]);
|
|
380
388
|
if (this._effectsSchema !== void 0) for (const name of Object.keys(this._effectsSchema.definitions)) result._effectHandlers.set(name, anyHandlers[name]);
|
|
@@ -430,7 +438,8 @@ const replay = Effect.fn("effect-machine.replay")(function* (built, events, opti
|
|
|
430
438
|
const self = {
|
|
431
439
|
send: dummySend,
|
|
432
440
|
cast: dummySend,
|
|
433
|
-
spawn: () => Effect.die("spawn not supported in replay")
|
|
441
|
+
spawn: () => Effect.die("spawn not supported in replay"),
|
|
442
|
+
reply: () => Effect.succeed(false)
|
|
434
443
|
};
|
|
435
444
|
for (const event of events) {
|
|
436
445
|
if (machine.finalStates.has(state._tag)) break;
|
|
@@ -464,5 +473,7 @@ const replay = Effect.fn("effect-machine.replay")(function* (built, events, opti
|
|
|
464
473
|
}
|
|
465
474
|
return state;
|
|
466
475
|
});
|
|
476
|
+
const reply = makeReply;
|
|
477
|
+
const deferReply = makeDeferReply;
|
|
467
478
|
//#endregion
|
|
468
|
-
export { BuiltMachine, Machine, findTransitions, machine_exports, make, replay, spawn };
|
|
479
|
+
export { BuiltMachine, Machine, deferReply, findTransitions, machine_exports, make, replay, reply, spawn };
|