effect-machine 0.12.0 → 0.14.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 +133 -324
- package/dist/actor.d.ts +46 -28
- package/dist/actor.js +276 -315
- package/dist/cluster/entity-machine.d.ts +1 -1
- package/dist/cluster/entity-machine.js +20 -9
- package/dist/cluster/to-entity.d.ts +3 -3
- package/dist/errors.d.ts +24 -20
- package/dist/errors.js +10 -6
- package/dist/index.d.ts +5 -4
- package/dist/index.js +3 -2
- package/dist/internal/runtime.d.ts +82 -7
- package/dist/internal/runtime.js +162 -58
- package/dist/internal/transition.d.ts +12 -11
- package/dist/internal/transition.js +12 -14
- package/dist/machine.d.ts +148 -140
- package/dist/machine.js +141 -155
- package/dist/schema.d.ts +14 -0
- package/dist/schema.js +10 -1
- package/dist/slot.d.ts +112 -86
- package/dist/slot.js +92 -59
- package/dist/supervision.d.ts +97 -0
- package/dist/supervision.js +42 -0
- package/dist/testing.d.ts +21 -12
- package/dist/testing.js +23 -26
- package/package.json +7 -7
- package/v3/dist/actor.d.ts +53 -30
- package/v3/dist/actor.js +286 -311
- package/v3/dist/cluster/entity-machine.d.ts +1 -1
- package/v3/dist/cluster/entity-machine.js +5 -5
- package/v3/dist/cluster/to-entity.d.ts +1 -1
- package/v3/dist/errors.d.ts +14 -10
- package/v3/dist/errors.js +11 -7
- package/v3/dist/index.d.ts +6 -5
- package/v3/dist/index.js +3 -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 +87 -10
- package/v3/dist/internal/runtime.js +177 -61
- package/v3/dist/internal/transition.d.ts +13 -12
- package/v3/dist/internal/transition.js +14 -16
- package/v3/dist/internal/utils.js +5 -1
- package/v3/dist/machine.d.ts +158 -143
- package/v3/dist/machine.js +148 -155
- 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/supervision.d.ts +97 -0
- package/v3/dist/supervision.js +42 -0
- package/v3/dist/testing.d.ts +21 -12
- package/v3/dist/testing.js +23 -24
package/dist/machine.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { Inspector } from "./inspection.js";
|
|
3
2
|
import { getTag, makeDeferReply, makeReply, stubSystem } from "./internal/utils.js";
|
|
4
|
-
import { ProvisionValidationError, SlotProvisionError } from "./errors.js";
|
|
3
|
+
import { ProvisionValidationError, SlotCodecError, SlotProvisionError } from "./errors.js";
|
|
4
|
+
import { findTransitions, invalidateIndex, resolveTransition, runTransitionHandler, shouldPostpone } from "./internal/transition.js";
|
|
5
5
|
import { emitWithTimestamp } from "./internal/inspection.js";
|
|
6
|
+
import { Inspector } from "./inspection.js";
|
|
6
7
|
import { MachineContextTag } from "./slot.js";
|
|
7
|
-
import { findTransitions, invalidateIndex, resolveTransition, runTransitionHandler, shouldPostpone } from "./internal/transition.js";
|
|
8
8
|
import { createActor } from "./actor.js";
|
|
9
|
-
import { Cause, Effect, Exit, Option, Scope } from "effect";
|
|
9
|
+
import { Cause, Effect, Exit, Option, Random, Schema, Scope } from "effect";
|
|
10
10
|
//#region src/machine.ts
|
|
11
11
|
var machine_exports = /* @__PURE__ */ __exportAll({
|
|
12
|
-
BuiltMachine: () => BuiltMachine,
|
|
13
12
|
Machine: () => Machine,
|
|
14
13
|
deferReply: () => deferReply,
|
|
15
14
|
findTransitions: () => findTransitions,
|
|
16
15
|
make: () => make,
|
|
16
|
+
materializeMachine: () => materializeMachine,
|
|
17
17
|
replay: () => replay,
|
|
18
18
|
reply: () => reply,
|
|
19
19
|
spawn: () => spawn
|
|
@@ -28,22 +28,40 @@ const emitTaskInspection = (input) => Effect.flatMap(Effect.serviceOption(Inspec
|
|
|
28
28
|
timestamp
|
|
29
29
|
})));
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* Bind slot handlers to a machine, returning a fresh copy with handlers installed.
|
|
32
|
+
* If no handlers provided and machine has no slots, returns the machine as-is.
|
|
33
|
+
* Validates that all required slots are provided and no extra slots are given.
|
|
32
34
|
*
|
|
33
|
-
*
|
|
34
|
-
* accepted by `Machine.spawn` and `ActorSystem.spawn` (regular overload).
|
|
35
|
-
* Testing utilities (`simulate`, `createTestHarness`, etc.) still accept `Machine`.
|
|
35
|
+
* @internal — used by spawn, replay, simulate, test harness, entity-machine
|
|
36
36
|
*/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
get initial() {
|
|
45
|
-
return this._inner.initial;
|
|
37
|
+
const materializeMachine = (machine, handlers) => {
|
|
38
|
+
if (handlers === void 0) {
|
|
39
|
+
if (machine._slotsSchema !== void 0 && Object.keys(machine._slotsSchema.definitions).length > 0) throw new ProvisionValidationError({
|
|
40
|
+
missing: Object.keys(machine._slotsSchema.definitions),
|
|
41
|
+
extra: []
|
|
42
|
+
});
|
|
43
|
+
return machine;
|
|
46
44
|
}
|
|
45
|
+
const requiredSlots = /* @__PURE__ */ new Set();
|
|
46
|
+
if (machine._slotsSchema !== void 0) for (const name of Object.keys(machine._slotsSchema.definitions)) requiredSlots.add(name);
|
|
47
|
+
const providedSlots = new Set(Object.keys(handlers));
|
|
48
|
+
const missing = [];
|
|
49
|
+
const extra = [];
|
|
50
|
+
for (const name of requiredSlots) if (!providedSlots.has(name)) missing.push(name);
|
|
51
|
+
for (const name of providedSlots) if (!requiredSlots.has(name)) extra.push(name);
|
|
52
|
+
if (missing.length > 0 || extra.length > 0) throw new ProvisionValidationError({
|
|
53
|
+
missing,
|
|
54
|
+
extra
|
|
55
|
+
});
|
|
56
|
+
const result = new Machine(machine.initial, machine.stateSchema, machine.eventSchema, machine._slotsSchema, machine._slotValidation);
|
|
57
|
+
result._transitions = [...machine._transitions];
|
|
58
|
+
result._finalStates = new Set(machine._finalStates);
|
|
59
|
+
result._spawnEffects = [...machine._spawnEffects];
|
|
60
|
+
result._backgroundEffects = [...machine._backgroundEffects];
|
|
61
|
+
result._postponeRules = [...machine._postponeRules];
|
|
62
|
+
result._replySchemas = machine._replySchemas;
|
|
63
|
+
if (machine._slotsSchema !== void 0) for (const name of Object.keys(machine._slotsSchema.definitions)) result._slotHandlers.set(name, handlers[name]);
|
|
64
|
+
return result;
|
|
47
65
|
};
|
|
48
66
|
/**
|
|
49
67
|
* Machine definition with fluent builder API.
|
|
@@ -54,8 +72,7 @@ var BuiltMachine = class {
|
|
|
54
72
|
* - `R`: Effect requirements
|
|
55
73
|
* - `_SD`: State schema definition (for compile-time validation)
|
|
56
74
|
* - `_ED`: Event schema definition (for compile-time validation)
|
|
57
|
-
* - `
|
|
58
|
-
* - `EFD`: Effect definitions
|
|
75
|
+
* - `SD`: Slot definitions
|
|
59
76
|
*/
|
|
60
77
|
var Machine = class Machine {
|
|
61
78
|
initial;
|
|
@@ -64,11 +81,10 @@ var Machine = class Machine {
|
|
|
64
81
|
/** @internal */ _backgroundEffects;
|
|
65
82
|
/** @internal */ _finalStates;
|
|
66
83
|
/** @internal */ _postponeRules;
|
|
67
|
-
/** @internal */
|
|
68
|
-
/** @internal */
|
|
69
|
-
/** @internal */ _guardHandlers;
|
|
70
|
-
/** @internal */ _effectHandlers;
|
|
84
|
+
/** @internal */ _slotsSchema;
|
|
85
|
+
/** @internal */ _slotHandlers;
|
|
71
86
|
/** @internal */ _slots;
|
|
87
|
+
/** @internal */ _slotValidation;
|
|
72
88
|
stateSchema;
|
|
73
89
|
eventSchema;
|
|
74
90
|
/** @internal */ _replySchemas;
|
|
@@ -92,53 +108,73 @@ var Machine = class Machine {
|
|
|
92
108
|
get postponeRules() {
|
|
93
109
|
return this._postponeRules;
|
|
94
110
|
}
|
|
95
|
-
get
|
|
96
|
-
return this.
|
|
97
|
-
}
|
|
98
|
-
get effectsSchema() {
|
|
99
|
-
return this._effectsSchema;
|
|
111
|
+
get slotsSchema() {
|
|
112
|
+
return this._slotsSchema;
|
|
100
113
|
}
|
|
101
114
|
get replySchemas() {
|
|
102
115
|
return this._replySchemas;
|
|
103
116
|
}
|
|
104
117
|
/** @internal */
|
|
105
|
-
constructor(initial, stateSchema, eventSchema,
|
|
118
|
+
constructor(initial, stateSchema, eventSchema, slotsSchema, slotValidation = true) {
|
|
106
119
|
this.initial = initial;
|
|
107
120
|
this._transitions = [];
|
|
108
121
|
this._spawnEffects = [];
|
|
109
122
|
this._backgroundEffects = [];
|
|
110
123
|
this._finalStates = /* @__PURE__ */ new Set();
|
|
111
124
|
this._postponeRules = [];
|
|
112
|
-
this.
|
|
113
|
-
this._effectsSchema = effectsSchema;
|
|
125
|
+
this._slotsSchema = slotsSchema;
|
|
114
126
|
this._replySchemas = eventSchema?._replySchemas ?? /* @__PURE__ */ new Map();
|
|
115
|
-
this.
|
|
116
|
-
this.
|
|
127
|
+
this._slotHandlers = /* @__PURE__ */ new Map();
|
|
128
|
+
this._slotValidation = slotValidation;
|
|
117
129
|
this.stateSchema = stateSchema;
|
|
118
130
|
this.eventSchema = eventSchema;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
131
|
+
const validators = slotValidation && slotsSchema !== void 0 ? new Map(Object.entries(slotsSchema.definitions).map(([name, def]) => [name, {
|
|
132
|
+
decodeInput: Schema.decodeUnknownSync(def.inputSchema),
|
|
133
|
+
decodeOutput: Schema.decodeUnknownSync(def.outputSchema)
|
|
134
|
+
}])) : void 0;
|
|
135
|
+
const resolve = (name, params) => Effect.flatMap(Effect.serviceOption(this.Context), (maybeCtx) => {
|
|
136
|
+
if (Option.isNone(maybeCtx)) return Effect.die("MachineContext not available");
|
|
137
|
+
const handler = this._slotHandlers.get(name);
|
|
138
|
+
if (handler === void 0) return Effect.die(new SlotProvisionError({
|
|
139
|
+
slotName: name,
|
|
140
|
+
slotType: "slot"
|
|
141
|
+
}));
|
|
142
|
+
const validatedParams = validators !== void 0 ? (() => {
|
|
143
|
+
try {
|
|
144
|
+
const v = validators.get(name);
|
|
145
|
+
return v !== void 0 ? v.decodeInput(params) : params;
|
|
146
|
+
} catch (e) {
|
|
147
|
+
return Effect.die(new SlotCodecError({
|
|
148
|
+
slotName: name,
|
|
149
|
+
phase: "input",
|
|
150
|
+
message: e instanceof Error ? e.message : String(e)
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
})() : params;
|
|
154
|
+
if (Effect.isEffect(validatedParams)) return validatedParams;
|
|
155
|
+
const result = handler(validatedParams);
|
|
156
|
+
let resultEffect;
|
|
157
|
+
if (result === void 0 || result === null) resultEffect = Effect.void;
|
|
158
|
+
else if (Effect.isEffect(result)) resultEffect = result;
|
|
159
|
+
else resultEffect = Effect.succeed(result);
|
|
160
|
+
if (validators !== void 0) {
|
|
161
|
+
const v = validators.get(name);
|
|
162
|
+
if (v !== void 0) return Effect.flatMap(resultEffect, (value) => {
|
|
163
|
+
try {
|
|
164
|
+
const decoded = v.decodeOutput(value);
|
|
165
|
+
return Effect.succeed(decoded);
|
|
166
|
+
} catch (e) {
|
|
167
|
+
return Effect.die(new SlotCodecError({
|
|
168
|
+
slotName: name,
|
|
169
|
+
phase: "output",
|
|
170
|
+
message: e instanceof Error ? e.message : String(e)
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return resultEffect;
|
|
176
|
+
});
|
|
177
|
+
this._slots = this._slotsSchema !== void 0 ? this._slotsSchema._createSlots(resolve) : {};
|
|
142
178
|
}
|
|
143
179
|
from(stateOrStates, build) {
|
|
144
180
|
build(new TransitionScope(this, Array.isArray(stateOrStates) ? stateOrStates : [stateOrStates]));
|
|
@@ -186,42 +222,19 @@ var Machine = class Machine {
|
|
|
186
222
|
invalidateIndex(this);
|
|
187
223
|
return this;
|
|
188
224
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
*
|
|
199
|
-
* machine
|
|
200
|
-
* .spawn(State.Loading, ({ effects, state }) => effects.fetchData({ url: state.url }))
|
|
201
|
-
* .build({
|
|
202
|
-
* fetchData: ({ url }, { self }) =>
|
|
203
|
-
* Effect.gen(function* () {
|
|
204
|
-
* yield* Effect.addFinalizer(() => Effect.log("Leaving Loading"));
|
|
205
|
-
* const data = yield* Http.get(url);
|
|
206
|
-
* yield* self.send(Event.Loaded({ data }));
|
|
207
|
-
* }),
|
|
208
|
-
* });
|
|
209
|
-
* ```
|
|
210
|
-
*/
|
|
211
|
-
spawn(state, handler) {
|
|
212
|
-
const stateTag = getTag(state);
|
|
213
|
-
this._spawnEffects.push({
|
|
214
|
-
stateTag,
|
|
215
|
-
handler
|
|
216
|
-
});
|
|
225
|
+
spawn(stateOrStates, handler) {
|
|
226
|
+
const states = Array.isArray(stateOrStates) ? stateOrStates : [stateOrStates];
|
|
227
|
+
for (const s of states) {
|
|
228
|
+
const stateTag = getTag(s);
|
|
229
|
+
this._spawnEffects.push({
|
|
230
|
+
stateTag,
|
|
231
|
+
handler
|
|
232
|
+
});
|
|
233
|
+
}
|
|
217
234
|
invalidateIndex(this);
|
|
218
235
|
return this;
|
|
219
236
|
}
|
|
220
|
-
|
|
221
|
-
* State-scoped task that runs on entry and sends success/failure events.
|
|
222
|
-
* Interrupts do not emit failure events.
|
|
223
|
-
*/
|
|
224
|
-
task(state, run, options) {
|
|
237
|
+
task(stateOrStates, run, options) {
|
|
225
238
|
const handler = Effect.fn("effect-machine.task")(function* (ctx) {
|
|
226
239
|
yield* emitTaskInspection({
|
|
227
240
|
actorId: ctx.actorId,
|
|
@@ -237,7 +250,8 @@ var Machine = class Machine {
|
|
|
237
250
|
taskName: options.name,
|
|
238
251
|
phase: "success"
|
|
239
252
|
});
|
|
240
|
-
|
|
253
|
+
const successEvent = options.onSuccess !== void 0 ? options.onSuccess(exit.value, ctx) : exit.value;
|
|
254
|
+
yield* ctx.self.send(successEvent);
|
|
241
255
|
yield* Effect.yieldNow;
|
|
242
256
|
return;
|
|
243
257
|
}
|
|
@@ -265,7 +279,7 @@ var Machine = class Machine {
|
|
|
265
279
|
}
|
|
266
280
|
return yield* Effect.failCause(cause).pipe(Effect.orDie);
|
|
267
281
|
});
|
|
268
|
-
return this.spawn(
|
|
282
|
+
return this.spawn(stateOrStates, handler);
|
|
269
283
|
}
|
|
270
284
|
/**
|
|
271
285
|
* State timeout — gen_statem's `state_timeout`.
|
|
@@ -299,22 +313,14 @@ var Machine = class Machine {
|
|
|
299
313
|
}
|
|
300
314
|
/**
|
|
301
315
|
* Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
|
|
302
|
-
* Use effect slots defined via `Slot.Effects` for the actual work.
|
|
303
316
|
*
|
|
304
317
|
* @example
|
|
305
318
|
* ```ts
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
* .background(({ effects }) => effects.heartbeat())
|
|
312
|
-
* .build({
|
|
313
|
-
* heartbeat: (_, { self }) =>
|
|
314
|
-
* Effect.forever(
|
|
315
|
-
* Effect.sleep("30 seconds").pipe(Effect.andThen(self.send(Event.Ping)))
|
|
316
|
-
* ),
|
|
317
|
-
* });
|
|
319
|
+
* machine.background(({ self }) =>
|
|
320
|
+
* Effect.forever(
|
|
321
|
+
* Effect.sleep("30 seconds").pipe(Effect.andThen(self.send(Event.Ping))),
|
|
322
|
+
* ),
|
|
323
|
+
* );
|
|
318
324
|
* ```
|
|
319
325
|
*/
|
|
320
326
|
background(handler) {
|
|
@@ -355,43 +361,8 @@ var Machine = class Machine {
|
|
|
355
361
|
this._finalStates.add(stateTag);
|
|
356
362
|
return this;
|
|
357
363
|
}
|
|
358
|
-
/**
|
|
359
|
-
* Finalize the machine. Returns a `BuiltMachine` — the only type accepted by `Machine.spawn`.
|
|
360
|
-
*
|
|
361
|
-
* - Machines with slots: pass implementations as the first argument.
|
|
362
|
-
* - Machines without slots: call with no arguments.
|
|
363
|
-
*/
|
|
364
|
-
build(...args) {
|
|
365
|
-
const handlers = args[0];
|
|
366
|
-
if (handlers !== void 0) {
|
|
367
|
-
const requiredSlots = /* @__PURE__ */ new Set();
|
|
368
|
-
if (this._guardsSchema !== void 0) for (const name of Object.keys(this._guardsSchema.definitions)) requiredSlots.add(name);
|
|
369
|
-
if (this._effectsSchema !== void 0) for (const name of Object.keys(this._effectsSchema.definitions)) requiredSlots.add(name);
|
|
370
|
-
const providedSlots = new Set(Object.keys(handlers));
|
|
371
|
-
const missing = [];
|
|
372
|
-
const extra = [];
|
|
373
|
-
for (const name of requiredSlots) if (!providedSlots.has(name)) missing.push(name);
|
|
374
|
-
for (const name of providedSlots) if (!requiredSlots.has(name)) extra.push(name);
|
|
375
|
-
if (missing.length > 0 || extra.length > 0) throw new ProvisionValidationError({
|
|
376
|
-
missing,
|
|
377
|
-
extra
|
|
378
|
-
});
|
|
379
|
-
const result = new Machine(this.initial, this.stateSchema, this.eventSchema, this._guardsSchema, this._effectsSchema);
|
|
380
|
-
result._transitions = [...this._transitions];
|
|
381
|
-
result._finalStates = new Set(this._finalStates);
|
|
382
|
-
result._spawnEffects = [...this._spawnEffects];
|
|
383
|
-
result._backgroundEffects = [...this._backgroundEffects];
|
|
384
|
-
result._postponeRules = [...this._postponeRules];
|
|
385
|
-
result._replySchemas = this._replySchemas;
|
|
386
|
-
const anyHandlers = handlers;
|
|
387
|
-
if (this._guardsSchema !== void 0) for (const name of Object.keys(this._guardsSchema.definitions)) result._guardHandlers.set(name, anyHandlers[name]);
|
|
388
|
-
if (this._effectsSchema !== void 0) for (const name of Object.keys(this._effectsSchema.definitions)) result._effectHandlers.set(name, anyHandlers[name]);
|
|
389
|
-
return new BuiltMachine(result);
|
|
390
|
-
}
|
|
391
|
-
return new BuiltMachine(this);
|
|
392
|
-
}
|
|
393
364
|
static make(config) {
|
|
394
|
-
return new Machine(config.initial, config.state, config.event, config.
|
|
365
|
+
return new Machine(config.initial, config.state, config.event, config.slots, config.slotValidation ?? true);
|
|
395
366
|
}
|
|
396
367
|
};
|
|
397
368
|
var TransitionScope = class {
|
|
@@ -410,27 +381,42 @@ var TransitionScope = class {
|
|
|
410
381
|
};
|
|
411
382
|
const make = Machine.make;
|
|
412
383
|
/**
|
|
413
|
-
* Spawn an actor from a
|
|
384
|
+
* Spawn an actor from a machine.
|
|
385
|
+
*
|
|
386
|
+
* For machines with slots, pass implementations via `{ slots: { ... } }`.
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* ```ts
|
|
390
|
+
* // No slots
|
|
391
|
+
* const actor = yield* Machine.spawn(machine);
|
|
414
392
|
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
* for that state (timers, scoped resources, etc.). Transition history
|
|
420
|
-
* is not replayed — only the current state's entry effects run.
|
|
393
|
+
* // With slots
|
|
394
|
+
* const actor = yield* Machine.spawn(machine, {
|
|
395
|
+
* slots: { canRetry: ({ max }) => attempts < max },
|
|
396
|
+
* });
|
|
421
397
|
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
398
|
+
* // With persistence
|
|
399
|
+
* const actor = yield* Machine.spawn(machine, {
|
|
400
|
+
* persist: {
|
|
401
|
+
* load: () => storage.get("actor-state"),
|
|
402
|
+
* save: (state) => storage.set("actor-state", state),
|
|
403
|
+
* },
|
|
404
|
+
* });
|
|
405
|
+
* ```
|
|
424
406
|
*/
|
|
425
|
-
const spawn = Effect.fn("effect-machine.spawn")(function* (
|
|
407
|
+
const spawn = Effect.fn("effect-machine.spawn")(function* (machine, idOrOptions) {
|
|
426
408
|
const opts = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
427
|
-
const actor = yield* createActor(opts?.id ?? `actor-${
|
|
409
|
+
const actor = yield* createActor(opts?.id ?? `actor-${(yield* Random.next).toString(36).slice(2)}`, materializeMachine(machine, opts?.slots), {
|
|
410
|
+
initialState: opts?.hydrate,
|
|
411
|
+
supervision: opts?.supervision,
|
|
412
|
+
persist: opts?.persist
|
|
413
|
+
});
|
|
428
414
|
const maybeScope = yield* Effect.serviceOption(Scope.Scope);
|
|
429
415
|
if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, actor.stop);
|
|
430
416
|
return actor;
|
|
431
417
|
});
|
|
432
|
-
const replay = Effect.fn("effect-machine.replay")(function* (
|
|
433
|
-
const machine =
|
|
418
|
+
const replay = Effect.fn("effect-machine.replay")(function* (input, events, options) {
|
|
419
|
+
const machine = materializeMachine(input, options?.slots);
|
|
434
420
|
let state = options?.from ?? machine.initial;
|
|
435
421
|
const hasPostponeRules = machine.postponeRules.length > 0;
|
|
436
422
|
const postponed = [];
|
|
@@ -476,4 +462,4 @@ const replay = Effect.fn("effect-machine.replay")(function* (built, events, opti
|
|
|
476
462
|
const reply = makeReply;
|
|
477
463
|
const deferReply = makeDeferReply;
|
|
478
464
|
//#endregion
|
|
479
|
-
export {
|
|
465
|
+
export { Machine, deferReply, findTransitions, machine_exports, make, materializeMachine, replay, reply, spawn };
|
package/dist/schema.d.ts
CHANGED
|
@@ -81,6 +81,20 @@ interface MachineSchemaBase<D extends Record<string, Schema.Struct.Fields>, Bran
|
|
|
81
81
|
<R>(cases: MatchCases<D, R>): (value: VariantsUnion<D> & Brand) => R;
|
|
82
82
|
<R>(value: VariantsUnion<D> & Brand, cases: MatchCases<D, R>): R;
|
|
83
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* Union-level derive: copies fields from `source` into the same variant,
|
|
86
|
+
* overriding with `partial`. Preserves the specific variant subtype.
|
|
87
|
+
*
|
|
88
|
+
* Dispatches to the per-variant `derive` based on `source._tag`.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* // Instead of switching on _tag to call per-variant derive:
|
|
93
|
+
* const updated = AgentLoopState.derive(state, { queue: newQueue })
|
|
94
|
+
* // If state is StreamingState, returns StreamingState (not LoopState)
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
readonly derive: <S extends VariantsUnion<D> & Brand>(source: S, partial?: Partial<Omit<S, "_tag">>) => S;
|
|
84
98
|
/**
|
|
85
99
|
* Reply schemas per variant tag. Only populated for event schemas
|
|
86
100
|
* with variants defined via `Event.reply()`.
|
package/dist/schema.js
CHANGED
|
@@ -67,6 +67,7 @@ const buildMachineSchema = (definition) => {
|
|
|
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)) {
|
|
69
69
|
if (RESERVED_DERIVE_KEYS.has(key)) continue;
|
|
70
|
+
if (!fieldNames.has(key)) continue;
|
|
70
71
|
result[key] = value;
|
|
71
72
|
}
|
|
72
73
|
return result;
|
|
@@ -78,7 +79,7 @@ const buildMachineSchema = (definition) => {
|
|
|
78
79
|
};
|
|
79
80
|
}
|
|
80
81
|
const variantArray = Object.values(variants);
|
|
81
|
-
if (variantArray.length === 0) throw new InvalidSchemaError({});
|
|
82
|
+
if (variantArray.length === 0) throw new InvalidSchemaError({ message: "Schema must have at least one variant" });
|
|
82
83
|
const unionSchema = variantArray.length === 1 ? variantArray[0] : Schema.Union(variantArray);
|
|
83
84
|
const $is = (tag) => (u) => typeof u === "object" && u !== null && "_tag" in u && u._tag === tag;
|
|
84
85
|
const $match = (valueOrCases, maybeCases) => {
|
|
@@ -111,12 +112,20 @@ const buildMachineSchema = (definition) => {
|
|
|
111
112
|
*/
|
|
112
113
|
const createMachineSchema = (definition) => {
|
|
113
114
|
const { schema, variants, constructors, _definition, replySchemas, $is, $match } = buildMachineSchema(definition);
|
|
115
|
+
const derive = (source, partial) => {
|
|
116
|
+
const ctor = constructors[source._tag];
|
|
117
|
+
if (ctor === void 0) throw new MissingMatchHandlerError({ tag: source._tag });
|
|
118
|
+
const deriveFn = ctor.derive;
|
|
119
|
+
if (deriveFn === void 0) throw new MissingMatchHandlerError({ tag: source._tag });
|
|
120
|
+
return deriveFn(source, partial);
|
|
121
|
+
};
|
|
114
122
|
return Object.assign(Object.create(schema), {
|
|
115
123
|
variants,
|
|
116
124
|
_definition,
|
|
117
125
|
_replySchemas: replySchemas,
|
|
118
126
|
$is,
|
|
119
127
|
$match,
|
|
128
|
+
derive,
|
|
120
129
|
...constructors
|
|
121
130
|
});
|
|
122
131
|
};
|