effect-machine 0.13.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 +14 -9
- package/dist/actor.d.ts +9 -6
- package/dist/actor.js +97 -43
- package/dist/cluster/entity-machine.d.ts +1 -1
- package/dist/cluster/entity-machine.js +1 -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 +2 -2
- package/dist/internal/runtime.js +5 -10
- package/dist/internal/transition.d.ts +9 -9
- package/dist/internal/transition.js +3 -5
- package/dist/machine.d.ts +122 -135
- package/dist/machine.js +97 -112
- package/dist/schema.d.ts +14 -0
- package/dist/schema.js +9 -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 +19 -12
- package/v3/dist/actor.js +130 -75
- package/v3/dist/cluster/entity-machine.d.ts +1 -1
- 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 +8 -8
- package/v3/dist/internal/runtime.js +45 -28
- 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 +153 -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/machine.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
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";
|
|
3
|
+
import { ProvisionValidationError, SlotCodecError, SlotProvisionError } from "./errors.js";
|
|
4
4
|
import { findTransitions, invalidateIndex, resolveTransition, runTransitionHandler, shouldPostpone } from "./internal/transition.js";
|
|
5
5
|
import { emitWithTimestamp } from "./internal/inspection.js";
|
|
6
|
-
import {
|
|
7
|
-
import { createActor } from "./actor.js";
|
|
6
|
+
import { Inspector } from "./inspection.js";
|
|
8
7
|
import { MachineContextTag } from "./slot.js";
|
|
9
|
-
import {
|
|
8
|
+
import { createActor } from "./actor.js";
|
|
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
12
|
Machine: () => Machine,
|
|
@@ -36,22 +36,14 @@ const emitTaskInspection = (input) => Effect.flatMap(Effect.serviceOption(Inspec
|
|
|
36
36
|
*/
|
|
37
37
|
const materializeMachine = (machine, handlers) => {
|
|
38
38
|
if (handlers === void 0) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (machine._guardsSchema !== void 0) missing.push(...Object.keys(machine._guardsSchema.definitions));
|
|
44
|
-
if (machine._effectsSchema !== void 0) missing.push(...Object.keys(machine._effectsSchema.definitions));
|
|
45
|
-
throw new ProvisionValidationError({
|
|
46
|
-
missing,
|
|
47
|
-
extra: []
|
|
48
|
-
});
|
|
49
|
-
}
|
|
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
|
+
});
|
|
50
43
|
return machine;
|
|
51
44
|
}
|
|
52
45
|
const requiredSlots = /* @__PURE__ */ new Set();
|
|
53
|
-
if (machine.
|
|
54
|
-
if (machine._effectsSchema !== void 0) for (const name of Object.keys(machine._effectsSchema.definitions)) requiredSlots.add(name);
|
|
46
|
+
if (machine._slotsSchema !== void 0) for (const name of Object.keys(machine._slotsSchema.definitions)) requiredSlots.add(name);
|
|
55
47
|
const providedSlots = new Set(Object.keys(handlers));
|
|
56
48
|
const missing = [];
|
|
57
49
|
const extra = [];
|
|
@@ -61,15 +53,14 @@ const materializeMachine = (machine, handlers) => {
|
|
|
61
53
|
missing,
|
|
62
54
|
extra
|
|
63
55
|
});
|
|
64
|
-
const result = new Machine(machine.initial, machine.stateSchema, machine.eventSchema, machine.
|
|
56
|
+
const result = new Machine(machine.initial, machine.stateSchema, machine.eventSchema, machine._slotsSchema, machine._slotValidation);
|
|
65
57
|
result._transitions = [...machine._transitions];
|
|
66
58
|
result._finalStates = new Set(machine._finalStates);
|
|
67
59
|
result._spawnEffects = [...machine._spawnEffects];
|
|
68
60
|
result._backgroundEffects = [...machine._backgroundEffects];
|
|
69
61
|
result._postponeRules = [...machine._postponeRules];
|
|
70
62
|
result._replySchemas = machine._replySchemas;
|
|
71
|
-
if (machine.
|
|
72
|
-
if (machine._effectsSchema !== void 0) for (const name of Object.keys(machine._effectsSchema.definitions)) result._effectHandlers.set(name, handlers[name]);
|
|
63
|
+
if (machine._slotsSchema !== void 0) for (const name of Object.keys(machine._slotsSchema.definitions)) result._slotHandlers.set(name, handlers[name]);
|
|
73
64
|
return result;
|
|
74
65
|
};
|
|
75
66
|
/**
|
|
@@ -81,8 +72,7 @@ const materializeMachine = (machine, handlers) => {
|
|
|
81
72
|
* - `R`: Effect requirements
|
|
82
73
|
* - `_SD`: State schema definition (for compile-time validation)
|
|
83
74
|
* - `_ED`: Event schema definition (for compile-time validation)
|
|
84
|
-
* - `
|
|
85
|
-
* - `EFD`: Effect definitions
|
|
75
|
+
* - `SD`: Slot definitions
|
|
86
76
|
*/
|
|
87
77
|
var Machine = class Machine {
|
|
88
78
|
initial;
|
|
@@ -91,11 +81,10 @@ var Machine = class Machine {
|
|
|
91
81
|
/** @internal */ _backgroundEffects;
|
|
92
82
|
/** @internal */ _finalStates;
|
|
93
83
|
/** @internal */ _postponeRules;
|
|
94
|
-
/** @internal */
|
|
95
|
-
/** @internal */
|
|
96
|
-
/** @internal */ _guardHandlers;
|
|
97
|
-
/** @internal */ _effectHandlers;
|
|
84
|
+
/** @internal */ _slotsSchema;
|
|
85
|
+
/** @internal */ _slotHandlers;
|
|
98
86
|
/** @internal */ _slots;
|
|
87
|
+
/** @internal */ _slotValidation;
|
|
99
88
|
stateSchema;
|
|
100
89
|
eventSchema;
|
|
101
90
|
/** @internal */ _replySchemas;
|
|
@@ -119,53 +108,73 @@ var Machine = class Machine {
|
|
|
119
108
|
get postponeRules() {
|
|
120
109
|
return this._postponeRules;
|
|
121
110
|
}
|
|
122
|
-
get
|
|
123
|
-
return this.
|
|
124
|
-
}
|
|
125
|
-
get effectsSchema() {
|
|
126
|
-
return this._effectsSchema;
|
|
111
|
+
get slotsSchema() {
|
|
112
|
+
return this._slotsSchema;
|
|
127
113
|
}
|
|
128
114
|
get replySchemas() {
|
|
129
115
|
return this._replySchemas;
|
|
130
116
|
}
|
|
131
117
|
/** @internal */
|
|
132
|
-
constructor(initial, stateSchema, eventSchema,
|
|
118
|
+
constructor(initial, stateSchema, eventSchema, slotsSchema, slotValidation = true) {
|
|
133
119
|
this.initial = initial;
|
|
134
120
|
this._transitions = [];
|
|
135
121
|
this._spawnEffects = [];
|
|
136
122
|
this._backgroundEffects = [];
|
|
137
123
|
this._finalStates = /* @__PURE__ */ new Set();
|
|
138
124
|
this._postponeRules = [];
|
|
139
|
-
this.
|
|
140
|
-
this._effectsSchema = effectsSchema;
|
|
125
|
+
this._slotsSchema = slotsSchema;
|
|
141
126
|
this._replySchemas = eventSchema?._replySchemas ?? /* @__PURE__ */ new Map();
|
|
142
|
-
this.
|
|
143
|
-
this.
|
|
127
|
+
this._slotHandlers = /* @__PURE__ */ new Map();
|
|
128
|
+
this._slotValidation = slotValidation;
|
|
144
129
|
this.stateSchema = stateSchema;
|
|
145
130
|
this.eventSchema = eventSchema;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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) : {};
|
|
169
178
|
}
|
|
170
179
|
from(stateOrStates, build) {
|
|
171
180
|
build(new TransitionScope(this, Array.isArray(stateOrStates) ? stateOrStates : [stateOrStates]));
|
|
@@ -213,42 +222,19 @@ var Machine = class Machine {
|
|
|
213
222
|
invalidateIndex(this);
|
|
214
223
|
return this;
|
|
215
224
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
*
|
|
226
|
-
* machine
|
|
227
|
-
* .spawn(State.Loading, ({ effects, state }) => effects.fetchData({ url: state.url }))
|
|
228
|
-
* .build({
|
|
229
|
-
* fetchData: ({ url }, { self }) =>
|
|
230
|
-
* Effect.gen(function* () {
|
|
231
|
-
* yield* Effect.addFinalizer(() => Effect.log("Leaving Loading"));
|
|
232
|
-
* const data = yield* Http.get(url);
|
|
233
|
-
* yield* self.send(Event.Loaded({ data }));
|
|
234
|
-
* }),
|
|
235
|
-
* });
|
|
236
|
-
* ```
|
|
237
|
-
*/
|
|
238
|
-
spawn(state, handler) {
|
|
239
|
-
const stateTag = getTag(state);
|
|
240
|
-
this._spawnEffects.push({
|
|
241
|
-
stateTag,
|
|
242
|
-
handler
|
|
243
|
-
});
|
|
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
|
+
}
|
|
244
234
|
invalidateIndex(this);
|
|
245
235
|
return this;
|
|
246
236
|
}
|
|
247
|
-
|
|
248
|
-
* State-scoped task that runs on entry and sends success/failure events.
|
|
249
|
-
* Interrupts do not emit failure events.
|
|
250
|
-
*/
|
|
251
|
-
task(state, run, options) {
|
|
237
|
+
task(stateOrStates, run, options) {
|
|
252
238
|
const handler = Effect.fn("effect-machine.task")(function* (ctx) {
|
|
253
239
|
yield* emitTaskInspection({
|
|
254
240
|
actorId: ctx.actorId,
|
|
@@ -264,7 +250,8 @@ var Machine = class Machine {
|
|
|
264
250
|
taskName: options.name,
|
|
265
251
|
phase: "success"
|
|
266
252
|
});
|
|
267
|
-
|
|
253
|
+
const successEvent = options.onSuccess !== void 0 ? options.onSuccess(exit.value, ctx) : exit.value;
|
|
254
|
+
yield* ctx.self.send(successEvent);
|
|
268
255
|
yield* Effect.yieldNow;
|
|
269
256
|
return;
|
|
270
257
|
}
|
|
@@ -292,7 +279,7 @@ var Machine = class Machine {
|
|
|
292
279
|
}
|
|
293
280
|
return yield* Effect.failCause(cause).pipe(Effect.orDie);
|
|
294
281
|
});
|
|
295
|
-
return this.spawn(
|
|
282
|
+
return this.spawn(stateOrStates, handler);
|
|
296
283
|
}
|
|
297
284
|
/**
|
|
298
285
|
* State timeout — gen_statem's `state_timeout`.
|
|
@@ -326,22 +313,14 @@ var Machine = class Machine {
|
|
|
326
313
|
}
|
|
327
314
|
/**
|
|
328
315
|
* Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
|
|
329
|
-
* Use effect slots defined via `Slot.Effects` for the actual work.
|
|
330
316
|
*
|
|
331
317
|
* @example
|
|
332
318
|
* ```ts
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
* .background(({ effects }) => effects.heartbeat())
|
|
339
|
-
* .build({
|
|
340
|
-
* heartbeat: (_, { self }) =>
|
|
341
|
-
* Effect.forever(
|
|
342
|
-
* Effect.sleep("30 seconds").pipe(Effect.andThen(self.send(Event.Ping)))
|
|
343
|
-
* ),
|
|
344
|
-
* });
|
|
319
|
+
* machine.background(({ self }) =>
|
|
320
|
+
* Effect.forever(
|
|
321
|
+
* Effect.sleep("30 seconds").pipe(Effect.andThen(self.send(Event.Ping))),
|
|
322
|
+
* ),
|
|
323
|
+
* );
|
|
345
324
|
* ```
|
|
346
325
|
*/
|
|
347
326
|
background(handler) {
|
|
@@ -383,7 +362,7 @@ var Machine = class Machine {
|
|
|
383
362
|
return this;
|
|
384
363
|
}
|
|
385
364
|
static make(config) {
|
|
386
|
-
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);
|
|
387
366
|
}
|
|
388
367
|
};
|
|
389
368
|
var TransitionScope = class {
|
|
@@ -413,18 +392,24 @@ const make = Machine.make;
|
|
|
413
392
|
*
|
|
414
393
|
* // With slots
|
|
415
394
|
* const actor = yield* Machine.spawn(machine, {
|
|
416
|
-
* slots: { canRetry: ({ max }
|
|
395
|
+
* slots: { canRetry: ({ max }) => attempts < max },
|
|
417
396
|
* });
|
|
418
397
|
*
|
|
419
|
-
* // With
|
|
420
|
-
* const actor = yield* Machine.spawn(machine, {
|
|
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
|
+
* });
|
|
421
405
|
* ```
|
|
422
406
|
*/
|
|
423
407
|
const spawn = Effect.fn("effect-machine.spawn")(function* (machine, idOrOptions) {
|
|
424
408
|
const opts = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
|
|
425
409
|
const actor = yield* createActor(opts?.id ?? `actor-${(yield* Random.next).toString(36).slice(2)}`, materializeMachine(machine, opts?.slots), {
|
|
426
410
|
initialState: opts?.hydrate,
|
|
427
|
-
supervision: opts?.supervision
|
|
411
|
+
supervision: opts?.supervision,
|
|
412
|
+
persist: opts?.persist
|
|
428
413
|
});
|
|
429
414
|
const maybeScope = yield* Effect.serviceOption(Scope.Scope);
|
|
430
415
|
if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, actor.stop);
|
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;
|
|
@@ -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
|
};
|
package/dist/slot.d.ts
CHANGED
|
@@ -4,42 +4,120 @@ import { Effect, Schema, ServiceMap } from "effect";
|
|
|
4
4
|
//#region src/slot.d.ts
|
|
5
5
|
/** Schema fields definition (like Schema.Struct.Fields) */
|
|
6
6
|
type Fields = Record<string, Schema.Top>;
|
|
7
|
-
/** Extract the
|
|
7
|
+
/** Extract the type from schema fields (used for parameters) */
|
|
8
8
|
type FieldsToParams<F extends Fields> = keyof F extends never ? void : Schema.Schema.Type<Schema.Struct<F>>;
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
* Definition of a single slot function.
|
|
11
|
+
* Created via `Slot.fn(params, returnSchema?)`.
|
|
12
|
+
*
|
|
13
|
+
* Carries both type-level information and materialized schemas
|
|
14
|
+
* for runtime validation and serialization.
|
|
15
|
+
*/
|
|
16
|
+
interface SlotFnDef<F extends Fields = Fields, Return = void> {
|
|
17
|
+
readonly _tag: "SlotFnDef";
|
|
18
|
+
readonly fields: F;
|
|
19
|
+
/** Return schema — undefined means void */
|
|
20
|
+
readonly returnSchema: Schema.Schema<Return> | undefined;
|
|
21
|
+
/** Materialized input schema (Schema.Struct of fields, or Schema.Void for empty) */
|
|
22
|
+
readonly inputSchema: Schema.Codec<FieldsToParams<F>>;
|
|
23
|
+
/** Materialized output schema (returnSchema or Schema.Void) */
|
|
24
|
+
readonly outputSchema: Schema.Codec<Return>;
|
|
16
25
|
}
|
|
17
26
|
/**
|
|
18
|
-
*
|
|
27
|
+
* Define a single slot function with parameter schema and optional return schema.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* // Guard-like: returns boolean
|
|
32
|
+
* Slot.fn({ max: Schema.Number }, Schema.Boolean)
|
|
33
|
+
*
|
|
34
|
+
* // Effect-like: returns void (default)
|
|
35
|
+
* Slot.fn({ url: Schema.String })
|
|
36
|
+
*
|
|
37
|
+
* // No params, returns boolean
|
|
38
|
+
* Slot.fn({}, Schema.Boolean)
|
|
39
|
+
* ```
|
|
19
40
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
41
|
+
declare const fn: {
|
|
42
|
+
<F extends Fields, Return>(fields: F, returnSchema: Schema.Schema<Return>): SlotFnDef<F, Return>;
|
|
43
|
+
<F extends Fields>(fields: F): SlotFnDef<F, void>;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Record of slot definitions. Keys are slot names, values are SlotFnDef.
|
|
47
|
+
*/
|
|
48
|
+
type SlotsDef = Record<string, SlotFnDef<Fields, unknown>>;
|
|
25
49
|
/**
|
|
26
|
-
*
|
|
50
|
+
* Slots schema — returned by `Slot.define()`. Passed to `Machine.make({ slots })`.
|
|
27
51
|
*/
|
|
28
|
-
|
|
52
|
+
interface SlotsSchema<D extends SlotsDef> {
|
|
53
|
+
readonly _tag: "SlotsSchema";
|
|
54
|
+
readonly definitions: D;
|
|
55
|
+
/** Schema for slot requests `{ _tag: "SlotRequest", name, params }`. For RPC request payloads. */
|
|
56
|
+
readonly requestSchema: Schema.Codec<SlotRequest<D>>;
|
|
57
|
+
/** Schema for slot results `{ _tag: "SlotResult", name, result }`. For RPC response payloads. */
|
|
58
|
+
readonly resultSchema: Schema.Codec<SlotResult<D>>;
|
|
59
|
+
/** Schema for slot invocations `{ _tag: "SlotInvocation", name, params, result }`. For persistence/logging. */
|
|
60
|
+
readonly invocationSchema: Schema.Codec<SlotInvocation<D>>;
|
|
61
|
+
/** Create callable slot proxies (used by Machine internally) */
|
|
62
|
+
readonly _createSlots: (resolve: <N extends keyof D & string>(name: N, params: SlotParams<D[N]>) => Effect.Effect<SlotReturn<D[N]>>) => SlotCalls<D>;
|
|
63
|
+
}
|
|
29
64
|
/**
|
|
30
|
-
*
|
|
65
|
+
* A serialized slot request — captures name and params (no result).
|
|
66
|
+
* Used for RPC request payloads.
|
|
67
|
+
*/
|
|
68
|
+
type SlotRequest<D extends SlotsDef> = { readonly [K in keyof D & string]: {
|
|
69
|
+
readonly _tag: "SlotRequest";
|
|
70
|
+
readonly name: K;
|
|
71
|
+
readonly params: SlotParams<D[K]>;
|
|
72
|
+
} }[keyof D & string];
|
|
73
|
+
/**
|
|
74
|
+
* A serialized slot result — captures name and result (no params).
|
|
75
|
+
* Used for RPC response payloads.
|
|
76
|
+
*/
|
|
77
|
+
type SlotResult<D extends SlotsDef> = { readonly [K in keyof D & string]: {
|
|
78
|
+
readonly _tag: "SlotResult";
|
|
79
|
+
readonly name: K;
|
|
80
|
+
readonly result: SlotReturn<D[K]>;
|
|
81
|
+
} }[keyof D & string];
|
|
82
|
+
/**
|
|
83
|
+
* A serialized slot invocation — captures name, params, and result.
|
|
84
|
+
* Used for persistence, logging, and audit trails.
|
|
85
|
+
*/
|
|
86
|
+
type SlotInvocation<D extends SlotsDef> = { readonly [K in keyof D & string]: {
|
|
87
|
+
readonly _tag: "SlotInvocation";
|
|
88
|
+
readonly name: K;
|
|
89
|
+
readonly params: SlotParams<D[K]>;
|
|
90
|
+
readonly result: SlotReturn<D[K]>;
|
|
91
|
+
} }[keyof D & string];
|
|
92
|
+
/** Extract params type from a SlotFnDef */
|
|
93
|
+
type SlotParams<D extends SlotFnDef<Fields, unknown>> = D extends SlotFnDef<infer F, unknown> ? FieldsToParams<F> : never;
|
|
94
|
+
/** Extract return type from a SlotFnDef */
|
|
95
|
+
type SlotReturn<D extends SlotFnDef<Fields, unknown>> = D extends SlotFnDef<Fields, infer R> ? R : never;
|
|
96
|
+
/**
|
|
97
|
+
* A callable slot — function that takes params and returns Effect<Return>.
|
|
98
|
+
*/
|
|
99
|
+
interface SlotCall<Name extends string, Params, Return> {
|
|
100
|
+
readonly _tag: "Slot";
|
|
101
|
+
readonly name: Name;
|
|
102
|
+
(params: Params): Effect.Effect<Return>;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Convert slot definitions to callable slot proxies.
|
|
31
106
|
*/
|
|
32
|
-
type
|
|
107
|
+
type SlotCalls<D extends SlotsDef> = { readonly [K in keyof D & string]: SlotCall<K, SlotParams<D[K]>, SlotReturn<D[K]>> };
|
|
33
108
|
/**
|
|
34
|
-
*
|
|
109
|
+
* Slot handler implementation.
|
|
110
|
+
* Receives only params — use `yield* machine.Context` for machine context.
|
|
35
111
|
*/
|
|
36
|
-
type
|
|
112
|
+
type SlotHandler<Params, Return, R = never> = (params: Params) => Return | Effect.Effect<Return, never, R>;
|
|
37
113
|
/**
|
|
38
|
-
*
|
|
114
|
+
* Handler implementations for all slots in a definition.
|
|
39
115
|
*/
|
|
40
|
-
type
|
|
116
|
+
type ProvideSlots<D extends SlotsDef, R = never> = { readonly [K in keyof D & string]: SlotHandler<SlotParams<D[K]>, SlotReturn<D[K]>, R> };
|
|
117
|
+
/** Check if a SlotsDef has any actual keys */
|
|
118
|
+
type HasSlotKeys<SD extends SlotsDef> = [keyof SD] extends [never] ? false : SD extends Record<string, never> ? false : true;
|
|
41
119
|
/**
|
|
42
|
-
* Type for machine context
|
|
120
|
+
* Type for machine context — state, event, and self reference.
|
|
43
121
|
* Shared across all machines via MachineContextTag.
|
|
44
122
|
*/
|
|
45
123
|
interface MachineContext<State, Event, Self> {
|
|
@@ -56,76 +134,24 @@ interface MachineContext<State, Event, Self> {
|
|
|
56
134
|
*/
|
|
57
135
|
declare const MachineContextTag: ServiceMap.Service<MachineContext<any, any, any>, MachineContext<any, any, any>>;
|
|
58
136
|
/**
|
|
59
|
-
*
|
|
60
|
-
* Receives params and context, returns Effect<boolean>.
|
|
61
|
-
*/
|
|
62
|
-
type GuardHandler<Params, Ctx, R = never> = (params: Params, ctx: Ctx) => boolean | Effect.Effect<boolean, never, R>;
|
|
63
|
-
/**
|
|
64
|
-
* Effect handler implementation.
|
|
65
|
-
* Receives params and context, returns Effect<void>.
|
|
66
|
-
*/
|
|
67
|
-
type EffectHandler<Params, Ctx, R = never> = (params: Params, ctx: Ctx) => Effect.Effect<void, never, R>;
|
|
68
|
-
/**
|
|
69
|
-
* Handler types for all guards in a definition
|
|
70
|
-
*/
|
|
71
|
-
type GuardHandlers<D extends GuardsDef, MachineCtx, R = never> = { readonly [K in keyof D & string]: GuardHandler<FieldsToParams<D[K]>, MachineCtx, R> };
|
|
72
|
-
/**
|
|
73
|
-
* Handler types for all effects in a definition
|
|
74
|
-
*/
|
|
75
|
-
type EffectHandlers<D extends EffectsDef, MachineCtx, R = never> = { readonly [K in keyof D & string]: EffectHandler<FieldsToParams<D[K]>, MachineCtx, R> };
|
|
76
|
-
/**
|
|
77
|
-
* Guards schema - returned by Slot.Guards()
|
|
78
|
-
*/
|
|
79
|
-
interface GuardsSchema<D extends GuardsDef> {
|
|
80
|
-
readonly _tag: "GuardsSchema";
|
|
81
|
-
readonly definitions: D;
|
|
82
|
-
/** Create callable guard slots (used by Machine internally) */
|
|
83
|
-
readonly _createSlots: (resolve: <N extends keyof D & string>(name: N, params: FieldsToParams<D[N]>) => Effect.Effect<boolean>) => GuardSlots<D>;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Effects schema - returned by Slot.Effects()
|
|
87
|
-
*/
|
|
88
|
-
interface EffectsSchema<D extends EffectsDef> {
|
|
89
|
-
readonly _tag: "EffectsSchema";
|
|
90
|
-
readonly definitions: D;
|
|
91
|
-
/** Create callable effect slots (used by Machine internally) */
|
|
92
|
-
readonly _createSlots: (resolve: <N extends keyof D & string>(name: N, params: FieldsToParams<D[N]>) => Effect.Effect<void>) => EffectSlots<D>;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Create a guards schema with parameterized guard definitions.
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* ```ts
|
|
99
|
-
* const MyGuards = Slot.Guards({
|
|
100
|
-
* canRetry: { max: Schema.Number },
|
|
101
|
-
* isValid: {},
|
|
102
|
-
* })
|
|
103
|
-
* ```
|
|
104
|
-
*/
|
|
105
|
-
declare const Guards: <D extends GuardsDef>(definitions: D) => GuardsSchema<D>;
|
|
106
|
-
/**
|
|
107
|
-
* Create an effects schema with parameterized effect definitions.
|
|
137
|
+
* Define a set of slots with parameter and return schemas.
|
|
108
138
|
*
|
|
109
139
|
* @example
|
|
110
140
|
* ```ts
|
|
111
|
-
* const
|
|
112
|
-
*
|
|
113
|
-
*
|
|
141
|
+
* const MySlots = Slot.define({
|
|
142
|
+
* canRetry: Slot.fn({ max: Schema.Number }, Schema.Boolean),
|
|
143
|
+
* fetchData: Slot.fn({ url: Schema.String }),
|
|
144
|
+
* notify: Slot.fn({ message: Schema.String }),
|
|
114
145
|
* })
|
|
115
146
|
* ```
|
|
116
147
|
*/
|
|
117
|
-
declare const
|
|
118
|
-
/** Extract guard definition type from GuardsSchema */
|
|
119
|
-
type GuardsDefOf<G> = G extends GuardsSchema<infer D> ? D : never;
|
|
120
|
-
/** Extract effect definition type from EffectsSchema */
|
|
121
|
-
type EffectsDefOf<E> = E extends EffectsSchema<infer D> ? D : never;
|
|
122
|
-
/** Extract guard slots type from GuardsSchema */
|
|
123
|
-
type GuardSlotsOf<G> = G extends GuardsSchema<infer D> ? GuardSlots<D> : never;
|
|
124
|
-
/** Extract effect slots type from EffectsSchema */
|
|
125
|
-
type EffectSlotsOf<E> = E extends EffectsSchema<infer D> ? EffectSlots<D> : never;
|
|
148
|
+
declare const define: <D extends SlotsDef>(definitions: D) => SlotsSchema<D>;
|
|
126
149
|
declare const Slot: {
|
|
127
|
-
readonly
|
|
128
|
-
|
|
150
|
+
readonly fn: {
|
|
151
|
+
<F extends Fields, Return>(fields: F, returnSchema: Schema.Schema<Return>): SlotFnDef<F, Return>;
|
|
152
|
+
<F extends Fields>(fields: F): SlotFnDef<F, void>;
|
|
153
|
+
};
|
|
154
|
+
readonly define: <D extends SlotsDef>(definitions: D) => SlotsSchema<D>;
|
|
129
155
|
};
|
|
130
156
|
//#endregion
|
|
131
|
-
export {
|
|
157
|
+
export { HasSlotKeys, MachineContext, MachineContextTag, ProvideSlots, Slot, SlotCall, SlotCalls, SlotFnDef, SlotHandler, SlotInvocation, SlotRequest, SlotResult, SlotsDef, SlotsSchema, define, fn };
|