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/README.md
CHANGED
|
@@ -39,14 +39,14 @@ const CheckoutEvent = Event({
|
|
|
39
39
|
Cancel: {},
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
const
|
|
43
|
-
chargeCard: { cartId: Schema.String, totalCents: Schema.Number },
|
|
42
|
+
const CheckoutSlots = Slot.define({
|
|
43
|
+
chargeCard: Slot.fn({ cartId: Schema.String, totalCents: Schema.Number }),
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
const checkoutMachine = Machine.make({
|
|
47
47
|
state: CheckoutState,
|
|
48
48
|
event: CheckoutEvent,
|
|
49
|
-
|
|
49
|
+
slots: CheckoutSlots,
|
|
50
50
|
initial: CheckoutState.ReviewingCart({ cartId: "cart_123", totalCents: 4200 }),
|
|
51
51
|
})
|
|
52
52
|
.on(CheckoutState.ReviewingCart, CheckoutEvent.Submit, ({ state }) =>
|
|
@@ -61,8 +61,8 @@ const checkoutMachine = Machine.make({
|
|
|
61
61
|
.onAny(CheckoutEvent.Cancel, ({ state }) =>
|
|
62
62
|
CheckoutState.Failed.derive(state, { reason: "cancelled" }),
|
|
63
63
|
)
|
|
64
|
-
.spawn(CheckoutState.ChargingCard, ({
|
|
65
|
-
|
|
64
|
+
.spawn(CheckoutState.ChargingCard, ({ slots, state }) =>
|
|
65
|
+
slots.chargeCard({ cartId: state.cartId, totalCents: state.totalCents }),
|
|
66
66
|
)
|
|
67
67
|
.final(CheckoutState.Confirmed)
|
|
68
68
|
.final(CheckoutState.Failed);
|
|
@@ -86,10 +86,11 @@ const actor =
|
|
|
86
86
|
yield *
|
|
87
87
|
Machine.spawn(checkoutMachine, {
|
|
88
88
|
slots: {
|
|
89
|
-
chargeCard: ({ cartId, totalCents }
|
|
89
|
+
chargeCard: ({ cartId, totalCents }) =>
|
|
90
90
|
Effect.gen(function* () {
|
|
91
|
+
const ctx = yield* checkoutMachine.Context;
|
|
91
92
|
const result = yield* PaymentService.charge(cartId, totalCents);
|
|
92
|
-
yield* self.send(
|
|
93
|
+
yield* ctx.self.send(
|
|
93
94
|
result.ok
|
|
94
95
|
? CheckoutEvent.Charged({ receiptId: result.receiptId })
|
|
95
96
|
: CheckoutEvent.Declined({ reason: result.error }),
|
|
@@ -114,8 +115,12 @@ The same machine can run with different slot implementations in tests, local app
|
|
|
114
115
|
const program = Effect.gen(function* () {
|
|
115
116
|
const actor = yield* Machine.spawn(checkoutMachine, {
|
|
116
117
|
slots: {
|
|
117
|
-
chargeCard: ({ cartId }
|
|
118
|
-
|
|
118
|
+
chargeCard: ({ cartId }) =>
|
|
119
|
+
checkoutMachine.Context.pipe(
|
|
120
|
+
Effect.flatMap((ctx) =>
|
|
121
|
+
ctx.self.send(CheckoutEvent.Charged({ receiptId: `rcpt_${cartId}` })),
|
|
122
|
+
),
|
|
123
|
+
),
|
|
119
124
|
},
|
|
120
125
|
});
|
|
121
126
|
|
package/dist/actor.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ExtractReply, ReplyTypeBrand } from "./internal/brands.js";
|
|
2
2
|
import { ActorStoppedError, DuplicateActorError, NoReplyError } from "./errors.js";
|
|
3
|
-
import {
|
|
3
|
+
import { ProvideSlots, SlotsDef } from "./slot.js";
|
|
4
4
|
import { ActorExit, Supervision } from "./supervision.js";
|
|
5
5
|
import { ProcessEventError, ProcessEventHooks, ProcessEventResult, processEventCore, resolveTransition, runSpawnEffects } from "./internal/transition.js";
|
|
6
|
-
import { Machine } from "./machine.js";
|
|
6
|
+
import { Machine, PersistConfig } from "./machine.js";
|
|
7
7
|
import { RuntimeQueuedEvent } from "./internal/runtime.js";
|
|
8
8
|
import { Deferred, Effect, Layer, Option, PubSub, Queue, Ref, Scope, ServiceMap, Stream, SubscriptionRef } from "effect";
|
|
9
9
|
|
|
@@ -158,8 +158,10 @@ interface ActorSystem {
|
|
|
158
158
|
readonly _tag: string;
|
|
159
159
|
}, E extends {
|
|
160
160
|
readonly _tag: string;
|
|
161
|
-
}, R
|
|
161
|
+
}, R, SD extends SlotsDef = Record<string, never>>(id: string, machine: Machine<S, E, R, any, any, SD>, options?: {
|
|
162
162
|
readonly supervision?: Supervision.Policy;
|
|
163
|
+
readonly slots?: ProvideSlots<SD, any>;
|
|
164
|
+
readonly persist?: PersistConfig<S>;
|
|
163
165
|
}) => Effect.Effect<ActorRef<S, E>, DuplicateActorError, R>;
|
|
164
166
|
/**
|
|
165
167
|
* Get an existing actor by ID
|
|
@@ -202,7 +204,7 @@ declare const buildActorRefCore: <S extends {
|
|
|
202
204
|
readonly _tag: string;
|
|
203
205
|
}, E extends {
|
|
204
206
|
readonly _tag: string;
|
|
205
|
-
}, R,
|
|
207
|
+
}, R, SD extends SlotsDef>(id: string, machine: Machine<S, E, R, any, any, SD>, stateRef: SubscriptionRef.SubscriptionRef<S>, eventQueueRef: Ref.Ref<Queue.Queue<QueuedEvent<E>>>, stoppedRef: Ref.Ref<boolean>, listeners: Listeners<S>, stop: Effect.Effect<void>, system: ActorSystem, childrenMap: ReadonlyMap<string, ActorRef<AnyState, unknown>>, pendingReplies: Set<Deferred.Deferred<unknown, unknown>>, transitionsPubSub: PubSub.PubSub<TransitionInfo<S, E>> | undefined, exitDeferred: Deferred.Deferred<ActorExit<S>, never>) => ActorRef<S, E>;
|
|
206
208
|
/**
|
|
207
209
|
* Create and start an actor for a machine.
|
|
208
210
|
* Delegates to the shared runtime kernel with actor-specific lifecycle hooks.
|
|
@@ -211,9 +213,10 @@ declare const createActor: <S extends {
|
|
|
211
213
|
readonly _tag: string;
|
|
212
214
|
}, E extends {
|
|
213
215
|
readonly _tag: string;
|
|
214
|
-
}, R,
|
|
216
|
+
}, R, SD extends SlotsDef>(id: string, machine: Machine<S, E, R, any, any, SD>, options?: {
|
|
215
217
|
initialState?: S;
|
|
216
|
-
supervision?: Supervision.Policy;
|
|
218
|
+
supervision?: Supervision.Policy;
|
|
219
|
+
persist?: PersistConfig<S>; /** @internal Called by system after each restart — emits ActorRestarted system event */
|
|
217
220
|
onRestart?: (generation: number, exit: ActorExit<unknown>) => Effect.Effect<void>;
|
|
218
221
|
} | undefined) => Effect.Effect<ActorRef<S, E>, never, never>;
|
|
219
222
|
/** Fail all pending call/ask Deferreds with ActorStoppedError. Safe to call multiple times. */
|
package/dist/actor.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ActorStoppedError, DuplicateActorError } from "./errors.js";
|
|
2
2
|
import { processEventCore, resolveTransition, runSpawnEffects } from "./internal/transition.js";
|
|
3
3
|
import { emitWithTimestamp } from "./internal/inspection.js";
|
|
4
|
-
import {
|
|
4
|
+
import { Inspector } from "./inspection.js";
|
|
5
|
+
import { materializeMachine } from "./machine.js";
|
|
5
6
|
import { createRuntime } from "./internal/runtime.js";
|
|
6
7
|
import { Cause, Deferred, Effect, Exit, Fiber, Layer, MutableHashMap, Option, PubSub, Queue, Ref, Schedule, Scope, Semaphore, ServiceMap, Stream, SubscriptionRef } from "effect";
|
|
7
8
|
//#region src/actor.ts
|
|
@@ -190,22 +191,85 @@ const buildInspectionHooks = (actorId, inspector) => ({
|
|
|
190
191
|
}))
|
|
191
192
|
});
|
|
192
193
|
/**
|
|
194
|
+
* Load persisted state and run onRestore hook if present.
|
|
195
|
+
* Returns the resolved initial state (loaded, restored, or fallback to machineInitial).
|
|
196
|
+
* @internal
|
|
197
|
+
*/
|
|
198
|
+
const loadAndRestore = (persist, machineInitial) => Effect.gen(function* () {
|
|
199
|
+
const loaded = yield* persist.load();
|
|
200
|
+
if (Option.isNone(loaded)) return machineInitial;
|
|
201
|
+
if (persist.onRestore === void 0) return loaded.value;
|
|
202
|
+
const restored = yield* persist.onRestore(loaded.value, { initial: machineInitial });
|
|
203
|
+
return Option.getOrElse(restored, () => machineInitial);
|
|
204
|
+
});
|
|
205
|
+
/**
|
|
206
|
+
* Resolve actor system from context, creating an implicit one if none exists.
|
|
207
|
+
* @internal
|
|
208
|
+
*/
|
|
209
|
+
const resolveActorSystem = Effect.fn("effect-machine.resolveActorSystem")(function* () {
|
|
210
|
+
const existingSystem = yield* Effect.serviceOption(ActorSystem);
|
|
211
|
+
if (Option.isSome(existingSystem)) return {
|
|
212
|
+
system: existingSystem.value,
|
|
213
|
+
implicitSystemScope: void 0
|
|
214
|
+
};
|
|
215
|
+
const scope = yield* Scope.make();
|
|
216
|
+
return {
|
|
217
|
+
system: yield* make().pipe(Effect.provideService(Scope.Scope, scope)),
|
|
218
|
+
implicitSystemScope: scope
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
/**
|
|
222
|
+
* Run the supervision loop for a supervised actor.
|
|
223
|
+
* Observes exit deferred, applies restart policy, resets cell resources on restart.
|
|
224
|
+
* @internal
|
|
225
|
+
*/
|
|
226
|
+
const runSupervisionLoop = (params) => Effect.gen(function* () {
|
|
227
|
+
const step = yield* Schedule.toStepWithSleep(params.supervision.schedule);
|
|
228
|
+
let generation = 0;
|
|
229
|
+
while (true) {
|
|
230
|
+
const currentRuntime = params.runtimeRef.current;
|
|
231
|
+
if (currentRuntime === void 0) return;
|
|
232
|
+
const generationExit = yield* Deferred.await(currentRuntime.exitDeferred);
|
|
233
|
+
if (generationExit._tag !== "Defect") {
|
|
234
|
+
yield* Deferred.succeed(params.terminalExitDeferred, generationExit);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (params.supervision.shouldRestart !== void 0 && !params.supervision.shouldRestart(generationExit)) {
|
|
238
|
+
yield* Deferred.succeed(params.terminalExitDeferred, generationExit);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if ((yield* step(generationExit).pipe(Effect.exit))._tag === "Failure") {
|
|
242
|
+
yield* Deferred.succeed(params.terminalExitDeferred, generationExit);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const restartState = params.persist !== void 0 ? yield* loadAndRestore(params.persist, params.machine.initial) : params.machine.initial;
|
|
246
|
+
yield* settlePendingReplies(params.pendingReplies, params.id);
|
|
247
|
+
const freshQueue = yield* Queue.unbounded();
|
|
248
|
+
yield* Ref.set(params.eventQueueRef, freshQueue);
|
|
249
|
+
yield* SubscriptionRef.set(params.stateRef, restartState);
|
|
250
|
+
yield* Ref.set(params.stoppedRef, false);
|
|
251
|
+
params.childrenMap.clear();
|
|
252
|
+
const machineForRestart = restartState !== params.machine.initial ? Object.create(params.machine, { initial: {
|
|
253
|
+
value: restartState,
|
|
254
|
+
enumerable: true
|
|
255
|
+
} }) : params.machine;
|
|
256
|
+
const newRuntime = yield* params.spawnGeneration(machineForRestart);
|
|
257
|
+
params.runtimeRef.current = newRuntime;
|
|
258
|
+
generation++;
|
|
259
|
+
if (params.onRestart !== void 0) yield* params.onRestart(generation, generationExit);
|
|
260
|
+
notifyListeners(params.listeners, restartState);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
/**
|
|
193
264
|
* Create and start an actor for a machine.
|
|
194
265
|
* Delegates to the shared runtime kernel with actor-specific lifecycle hooks.
|
|
195
266
|
*/
|
|
196
267
|
const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machine, options) {
|
|
197
|
-
const
|
|
268
|
+
const persist = options?.persist;
|
|
269
|
+
const initial = options?.initialState ?? (persist !== void 0 ? yield* loadAndRestore(persist, machine.initial) : machine.initial);
|
|
198
270
|
yield* Effect.annotateCurrentSpan("effect_machine.actor.id", id);
|
|
199
271
|
yield* Effect.annotateCurrentSpan("effect_machine.actor.initial_state", initial._tag);
|
|
200
|
-
const
|
|
201
|
-
let system;
|
|
202
|
-
let implicitSystemScope;
|
|
203
|
-
if (Option.isSome(existingSystem)) system = existingSystem.value;
|
|
204
|
-
else {
|
|
205
|
-
const scope = yield* Scope.make();
|
|
206
|
-
system = yield* make().pipe(Effect.provideService(Scope.Scope, scope));
|
|
207
|
-
implicitSystemScope = scope;
|
|
208
|
-
}
|
|
272
|
+
const { system, implicitSystemScope } = yield* resolveActorSystem();
|
|
209
273
|
const inspectorValue = Option.getOrUndefined(yield* Effect.serviceOption(Inspector));
|
|
210
274
|
const childrenMap = /* @__PURE__ */ new Map();
|
|
211
275
|
const pendingReplies = /* @__PURE__ */ new Set();
|
|
@@ -242,6 +306,9 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
|
|
|
242
306
|
})) : void 0,
|
|
243
307
|
onStateChange: (result, _event) => Effect.gen(function* () {
|
|
244
308
|
notifyListeners(listeners, result.newState);
|
|
309
|
+
if (persist !== void 0 && result.transitioned) {
|
|
310
|
+
if (persist.shouldSave === void 0 || persist.shouldSave(result.newState, result.previousState)) yield* persist.save(result.newState);
|
|
311
|
+
}
|
|
245
312
|
yield* Effect.annotateCurrentSpan("effect_machine.transition.matched", true);
|
|
246
313
|
if (result.lifecycleRan) {
|
|
247
314
|
yield* Effect.annotateCurrentSpan("effect_machine.state.from", result.previousState._tag);
|
|
@@ -311,36 +378,21 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
|
|
|
311
378
|
runtimeRef.current = runtime;
|
|
312
379
|
const supervision = options?.supervision;
|
|
313
380
|
let supervisorFiber;
|
|
314
|
-
if (supervision !== void 0) supervisorFiber = yield* Effect.forkDetach(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
if ((yield* step(generationExit).pipe(Effect.exit))._tag === "Failure") {
|
|
330
|
-
yield* Deferred.succeed(terminalExitDeferred, generationExit);
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
yield* settlePendingReplies(pendingReplies, id);
|
|
334
|
-
const freshQueue = yield* Queue.unbounded();
|
|
335
|
-
yield* Ref.set(eventQueueRef, freshQueue);
|
|
336
|
-
yield* SubscriptionRef.set(stateRef, machine.initial);
|
|
337
|
-
yield* Ref.set(stoppedRef, false);
|
|
338
|
-
childrenMap.clear();
|
|
339
|
-
runtimeRef.current = yield* spawnGeneration(machine);
|
|
340
|
-
generation++;
|
|
341
|
-
if (options?.onRestart !== void 0) yield* options.onRestart(generation, generationExit);
|
|
342
|
-
notifyListeners(listeners, machine.initial);
|
|
343
|
-
}
|
|
381
|
+
if (supervision !== void 0) supervisorFiber = yield* Effect.forkDetach(runSupervisionLoop({
|
|
382
|
+
supervision,
|
|
383
|
+
machine,
|
|
384
|
+
id,
|
|
385
|
+
runtimeRef,
|
|
386
|
+
terminalExitDeferred,
|
|
387
|
+
pendingReplies,
|
|
388
|
+
eventQueueRef,
|
|
389
|
+
stateRef,
|
|
390
|
+
stoppedRef,
|
|
391
|
+
childrenMap,
|
|
392
|
+
listeners,
|
|
393
|
+
spawnGeneration,
|
|
394
|
+
persist,
|
|
395
|
+
onRestart: options?.onRestart
|
|
344
396
|
}));
|
|
345
397
|
else yield* Effect.forkDetach(Deferred.await(runtime.exitDeferred).pipe(Effect.tap((exit) => Deferred.succeed(terminalExitDeferred, exit))));
|
|
346
398
|
return buildActorRefCore(id, machine, stateRef, eventQueueRef, stoppedRef, listeners, Effect.gen(function* () {
|
|
@@ -406,9 +458,11 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
|
|
|
406
458
|
});
|
|
407
459
|
const spawnRegular = Effect.fn("effect-machine.actorSystem.spawnRegular")(function* (id, machine, spawnOptions) {
|
|
408
460
|
if (MutableHashMap.has(actorsMap, id)) return yield* new DuplicateActorError({ actorId: id });
|
|
461
|
+
const materialized = spawnOptions?.slots !== void 0 ? materializeMachine(machine, spawnOptions.slots) : machine;
|
|
409
462
|
let actorRef;
|
|
410
|
-
const actor = yield* createActor(id,
|
|
463
|
+
const actor = yield* createActor(id, materialized, {
|
|
411
464
|
supervision: spawnOptions?.supervision,
|
|
465
|
+
persist: spawnOptions?.persist,
|
|
412
466
|
onRestart: spawnOptions?.supervision !== void 0 ? (generation, exit) => actorRef !== void 0 ? emitSystemEvent({
|
|
413
467
|
_tag: "ActorRestarted",
|
|
414
468
|
id,
|
|
@@ -65,7 +65,7 @@ declare const EntityMachine: {
|
|
|
65
65
|
readonly _tag: string;
|
|
66
66
|
}, E extends {
|
|
67
67
|
readonly _tag: string;
|
|
68
|
-
}, R, EntityType extends string, Rpcs extends Rpc.Any>(entity: Entity.Entity<EntityType, Rpcs>, machine: Machine<S, E, R, any, any, any
|
|
68
|
+
}, R, EntityType extends string, Rpcs extends Rpc.Any>(entity: Entity.Entity<EntityType, Rpcs>, machine: Machine<S, E, R, any, any, any>, options?: EntityMachineOptions<S, E>) => Layer.Layer<never, never, R>;
|
|
69
69
|
};
|
|
70
70
|
//#endregion
|
|
71
71
|
export { EntityMachine, EntityMachineOptions };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { replay } from "../machine.js";
|
|
1
2
|
import { createRuntime } from "../internal/runtime.js";
|
|
2
3
|
import { ActorSystem, makeSystem } from "../actor.js";
|
|
3
|
-
import { replay } from "../machine.js";
|
|
4
4
|
import { PersistenceAdapter } from "./persistence.js";
|
|
5
5
|
import { Clock, Effect, Option, Queue, Ref, Stream, SubscriptionRef } from "effect";
|
|
6
6
|
import { Entity } from "effect/unstable/cluster";
|
|
@@ -61,7 +61,7 @@ declare const toEntity: <S extends {
|
|
|
61
61
|
readonly _tag: string;
|
|
62
62
|
}, E extends {
|
|
63
63
|
readonly _tag: string;
|
|
64
|
-
}, R>(machine: Machine<S, E, R, any, any, any
|
|
64
|
+
}, R>(machine: Machine<S, E, R, any, any, any>, options: ToEntityOptions) => Entity.Entity<string, Rpc.Rpc<"Send", Schema.Struct<{
|
|
65
65
|
event: Schema.Schema<E>;
|
|
66
66
|
}>, Schema.Schema<S>, Schema.Never, never, never> | Rpc.Rpc<"Ask", Schema.Struct<{
|
|
67
67
|
event: Schema.Schema<E>;
|
package/dist/errors.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ declare const MissingMatchHandlerError_base: Schema.ErrorClass<MissingMatchHandl
|
|
|
24
24
|
declare class MissingMatchHandlerError extends MissingMatchHandlerError_base {}
|
|
25
25
|
declare const SlotProvisionError_base: Schema.ErrorClass<SlotProvisionError, Schema.TaggedStruct<"SlotProvisionError", {
|
|
26
26
|
readonly slotName: Schema.String;
|
|
27
|
-
readonly slotType: Schema.
|
|
27
|
+
readonly slotType: Schema.Literal<"slot">;
|
|
28
28
|
}>, _$effect_Cause0.YieldableError>;
|
|
29
29
|
/** Slot handler not found at runtime (internal error) */
|
|
30
30
|
declare class SlotProvisionError extends SlotProvisionError_base {}
|
|
@@ -55,6 +55,13 @@ declare const PersistenceError_base: Schema.ErrorClass<PersistenceError, Schema.
|
|
|
55
55
|
}>, _$effect_Cause0.YieldableError>;
|
|
56
56
|
/** Persistence adapter operation failed */
|
|
57
57
|
declare class PersistenceError extends PersistenceError_base {}
|
|
58
|
+
declare const SlotCodecError_base: Schema.ErrorClass<SlotCodecError, Schema.TaggedStruct<"SlotCodecError", {
|
|
59
|
+
readonly slotName: Schema.String;
|
|
60
|
+
readonly phase: Schema.Literals<readonly ["input", "output"]>;
|
|
61
|
+
readonly message: Schema.String;
|
|
62
|
+
}>, _$effect_Cause0.YieldableError>;
|
|
63
|
+
/** Slot input/output schema validation failed */
|
|
64
|
+
declare class SlotCodecError extends SlotCodecError_base {}
|
|
58
65
|
declare const VersionConflictError_base: Schema.ErrorClass<VersionConflictError, Schema.TaggedStruct<"VersionConflictError", {
|
|
59
66
|
readonly expected: Schema.Number;
|
|
60
67
|
readonly actual: Schema.Number;
|
|
@@ -62,4 +69,4 @@ declare const VersionConflictError_base: Schema.ErrorClass<VersionConflictError,
|
|
|
62
69
|
/** Optimistic locking failure — stored version doesn't match expected */
|
|
63
70
|
declare class VersionConflictError extends VersionConflictError_base {}
|
|
64
71
|
//#endregion
|
|
65
|
-
export { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, ProvisionValidationError, SlotProvisionError, VersionConflictError };
|
|
72
|
+
export { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, ProvisionValidationError, SlotCodecError, SlotProvisionError, VersionConflictError };
|
package/dist/errors.js
CHANGED
|
@@ -21,7 +21,7 @@ var MissingMatchHandlerError = class extends Schema.TaggedErrorClass()("MissingM
|
|
|
21
21
|
/** Slot handler not found at runtime (internal error) */
|
|
22
22
|
var SlotProvisionError = class extends Schema.TaggedErrorClass()("SlotProvisionError", {
|
|
23
23
|
slotName: Schema.String,
|
|
24
|
-
slotType: Schema.
|
|
24
|
+
slotType: Schema.Literal("slot")
|
|
25
25
|
}) {};
|
|
26
26
|
/** Slot provision validation failed — missing or extra handlers */
|
|
27
27
|
var ProvisionValidationError = class extends Schema.TaggedErrorClass()("ProvisionValidationError", {
|
|
@@ -39,10 +39,16 @@ var NoReplyError = class extends Schema.TaggedErrorClass()("NoReplyError", {
|
|
|
39
39
|
}) {};
|
|
40
40
|
/** Persistence adapter operation failed */
|
|
41
41
|
var PersistenceError = class extends Schema.TaggedErrorClass()("PersistenceError", { message: Schema.String }) {};
|
|
42
|
+
/** Slot input/output schema validation failed */
|
|
43
|
+
var SlotCodecError = class extends Schema.TaggedErrorClass()("SlotCodecError", {
|
|
44
|
+
slotName: Schema.String,
|
|
45
|
+
phase: Schema.Literals(["input", "output"]),
|
|
46
|
+
message: Schema.String
|
|
47
|
+
}) {};
|
|
42
48
|
/** Optimistic locking failure — stored version doesn't match expected */
|
|
43
49
|
var VersionConflictError = class extends Schema.TaggedErrorClass()("VersionConflictError", {
|
|
44
50
|
expected: Schema.Number,
|
|
45
51
|
actual: Schema.Number
|
|
46
52
|
}) {};
|
|
47
53
|
//#endregion
|
|
48
|
-
export { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, ProvisionValidationError, SlotProvisionError, VersionConflictError };
|
|
54
|
+
export { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, ProvisionValidationError, SlotCodecError, SlotProvisionError, VersionConflictError };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { DeferReplyResult, ReplyResult } from "./internal/utils.js";
|
|
2
2
|
import { Event, MachineEventSchema, MachineStateSchema, ReplyFields, State } from "./schema.js";
|
|
3
|
-
import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, ProvisionValidationError, SlotProvisionError, VersionConflictError } from "./errors.js";
|
|
4
|
-
import {
|
|
3
|
+
import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, ProvisionValidationError, SlotCodecError, SlotProvisionError, VersionConflictError } from "./errors.js";
|
|
4
|
+
import { HasSlotKeys, MachineContext, ProvideSlots, Slot, SlotCall, SlotCalls, SlotFnDef, SlotHandler, SlotInvocation, SlotRequest, SlotResult, SlotsDef, SlotsSchema } from "./slot.js";
|
|
5
5
|
import { ActorExit, CellPhase, DefectPhase, Supervision } from "./supervision.js";
|
|
6
6
|
import { ProcessEventResult } from "./internal/transition.js";
|
|
7
|
-
import { BackgroundEffect, HandlerContext, Machine, MachineRef, MakeConfig,
|
|
7
|
+
import { BackgroundEffect, HandlerContext, Machine, MachineRef, MakeConfig, SpawnEffect, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, machine_d_exports } from "./machine.js";
|
|
8
8
|
import { ActorRef, ActorRefSync, ActorSystem, Default, SystemEvent, SystemEventListener, TransitionInfo } from "./actor.js";
|
|
9
9
|
import { SimulationResult, TestHarness, TestHarnessOptions, assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
|
|
10
10
|
import { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, InspectionEvent, Inspector, InspectorHandler, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
|
|
11
|
-
export { ActorExit, type ActorRef, type ActorRefSync, ActorStoppedError, type ActorSystem, Default as ActorSystemDefault, ActorSystem as ActorSystemService, type AnyInspectionEvent, AssertionError, type BackgroundEffect, type CellPhase, type DefectPhase, type DeferReplyResult, DuplicateActorError, type EffectEvent, type
|
|
11
|
+
export { ActorExit, type ActorRef, type ActorRefSync, ActorStoppedError, type ActorSystem, Default as ActorSystemDefault, ActorSystem as ActorSystemService, type AnyInspectionEvent, AssertionError, type BackgroundEffect, type CellPhase, type DefectPhase, type DeferReplyResult, DuplicateActorError, type EffectEvent, type ErrorEvent, Event, type EventReceivedEvent, type HandlerContext, type HasSlotKeys, type InspectionEvent, type Inspector, type InspectorHandler, Inspector as InspectorService, InvalidSchemaError, machine_d_exports as Machine, type MachineContext, type MachineEventSchema, type MachineRef, type MachineStateSchema, type Machine as MachineType, type MakeConfig, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, type ProcessEventResult, type ProvideSlots, ProvisionValidationError, type ReplyFields, type ReplyResult, type SimulationResult, Slot, type SlotCall, type SlotCalls, SlotCodecError, type SlotFnDef, type SlotHandler, type SlotInvocation, SlotProvisionError, type SlotRequest, type SlotResult, type SlotsDef, type SlotsSchema, type SpawnEffect, type SpawnEvent, State, type StateHandlerContext, type StopEvent, Supervision, type SystemEvent, type SystemEventListener, type TaskEvent, type TaskOptions, type TestHarness, type TestHarnessOptions, type TimeoutConfig, type TracingInspectorOptions, type Transition, type TransitionEvent, type TransitionInfo, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, ProvisionValidationError, SlotCodecError, SlotProvisionError, VersionConflictError } from "./errors.js";
|
|
1
2
|
import { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
|
|
2
|
-
import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, ProvisionValidationError, SlotProvisionError, VersionConflictError } from "./errors.js";
|
|
3
|
-
import { ActorExit, Supervision } from "./supervision.js";
|
|
4
|
-
import { ActorSystem, Default } from "./actor.js";
|
|
5
3
|
import { Slot } from "./slot.js";
|
|
6
4
|
import { machine_exports } from "./machine.js";
|
|
5
|
+
import { ActorExit, Supervision } from "./supervision.js";
|
|
6
|
+
import { ActorSystem, Default } from "./actor.js";
|
|
7
7
|
import { Event, State } from "./schema.js";
|
|
8
8
|
import { assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
|
|
9
|
-
export { ActorExit, ActorStoppedError, Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, ProvisionValidationError, Slot, SlotProvisionError, State, Supervision, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
|
|
9
|
+
export { ActorExit, ActorStoppedError, Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceError, ProvisionValidationError, Slot, SlotCodecError, SlotProvisionError, State, Supervision, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NoReplyError } from "../errors.js";
|
|
2
|
-
import {
|
|
2
|
+
import { MachineContext, SlotsDef } from "../slot.js";
|
|
3
3
|
import { ActorExit } from "../supervision.js";
|
|
4
4
|
import { ProcessEventHooks, ProcessEventResult } from "./transition.js";
|
|
5
5
|
import { Machine, MachineRef } from "../machine.js";
|
|
@@ -137,6 +137,6 @@ declare const createRuntime: <S extends {
|
|
|
137
137
|
readonly _tag: string;
|
|
138
138
|
}, E extends {
|
|
139
139
|
readonly _tag: string;
|
|
140
|
-
}, R,
|
|
140
|
+
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, system: ActorSystem, config: RuntimeConfig<S, E>) => Effect.Effect<RuntimeHandle<S, E>, never, Scope.Scope | Exclude<R, MachineContext<S, E, MachineRef<E>>> | Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
|
|
141
141
|
//#endregion
|
|
142
142
|
export { ProcessQueuedResult, RuntimeCellResources, RuntimeConfig, RuntimeHandle, RuntimeLifecycleHooks, RuntimeQueuedEvent, createRuntime };
|
package/dist/internal/runtime.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { INTERNAL_INIT_EVENT } from "./utils.js";
|
|
2
|
-
import { processEventCore, runSpawnEffects, shouldPostpone } from "./transition.js";
|
|
3
2
|
import { NoReplyError } from "../errors.js";
|
|
3
|
+
import { processEventCore, runSpawnEffects, shouldPostpone } from "./transition.js";
|
|
4
4
|
import { ActorExit } from "../supervision.js";
|
|
5
5
|
import { ActorSystem } from "../actor.js";
|
|
6
|
-
import { Cause, Deferred, Effect, Exit, Fiber,
|
|
6
|
+
import { Cause, Deferred, Effect, Exit, Fiber, Queue, Ref, Schema, Scope, SubscriptionRef } from "effect";
|
|
7
7
|
//#region src/internal/runtime.ts
|
|
8
8
|
/**
|
|
9
9
|
* Shared runtime kernel for machine event processing.
|
|
@@ -81,14 +81,14 @@ const createRuntime = Effect.fn("effect-machine.runtime.create")(function* (mach
|
|
|
81
81
|
self,
|
|
82
82
|
system
|
|
83
83
|
};
|
|
84
|
-
const
|
|
84
|
+
const slots = machine._slots;
|
|
85
85
|
for (const bg of machine.backgroundEffects) {
|
|
86
86
|
const fiber = yield* bg.handler({
|
|
87
87
|
actorId,
|
|
88
88
|
state: machine.initial,
|
|
89
89
|
event: initEvent,
|
|
90
90
|
self,
|
|
91
|
-
|
|
91
|
+
slots,
|
|
92
92
|
system
|
|
93
93
|
}).pipe(Effect.provideService(machine.Context, ctx), Effect.forkIn(actorScope));
|
|
94
94
|
backgroundFibers.push(fiber);
|
|
@@ -278,12 +278,7 @@ const runtimeEventLoop = Effect.fn("effect-machine.runtime.eventLoop")(function*
|
|
|
278
278
|
yield* Ref.set(stoppedRef, true);
|
|
279
279
|
if (lifecycle?.onShutdown !== void 0) yield* lifecycle.onShutdown();
|
|
280
280
|
settlePostponed(postponed, actorId, forkEffect);
|
|
281
|
-
const remaining =
|
|
282
|
-
let next = yield* Queue.poll(eventQueue);
|
|
283
|
-
while (Option.isSome(next)) {
|
|
284
|
-
remaining.push(next.value);
|
|
285
|
-
next = yield* Queue.poll(eventQueue);
|
|
286
|
-
}
|
|
281
|
+
const remaining = yield* Queue.clear(eventQueue);
|
|
287
282
|
for (const entry of remaining) if (entry._tag === "sendWait") forkEffect(Deferred.succeed(entry.done, void 0));
|
|
288
283
|
else if (entry._tag === "ask") forkEffect(Deferred.fail(entry.reply, new NoReplyError({
|
|
289
284
|
actorId,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MachineContext, SlotsDef } from "../slot.js";
|
|
2
2
|
import { Machine, MachineRef, SpawnEffect, Transition } from "../machine.js";
|
|
3
3
|
import { ActorSystem } from "../actor.js";
|
|
4
4
|
import { Cause, Effect, Scope } from "effect";
|
|
@@ -29,7 +29,7 @@ declare const runTransitionHandler: <S extends {
|
|
|
29
29
|
readonly _tag: string;
|
|
30
30
|
}, E extends {
|
|
31
31
|
readonly _tag: string;
|
|
32
|
-
}, R,
|
|
32
|
+
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, transition: Transition<S, E, SD, R>, state: S, event: E, self: MachineRef<E>, system: ActorSystem, actorId: string) => Effect.Effect<{
|
|
33
33
|
newState: S;
|
|
34
34
|
hasReply: boolean;
|
|
35
35
|
deferReply: boolean;
|
|
@@ -50,7 +50,7 @@ declare const executeTransition: <S extends {
|
|
|
50
50
|
readonly _tag: string;
|
|
51
51
|
}, E extends {
|
|
52
52
|
readonly _tag: string;
|
|
53
|
-
}, R,
|
|
53
|
+
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, currentState: S, event: E, self: MachineRef<E>, system: ActorSystem, actorId: string) => Effect.Effect<{
|
|
54
54
|
newState: S;
|
|
55
55
|
transitioned: boolean;
|
|
56
56
|
reenter: boolean;
|
|
@@ -111,7 +111,7 @@ declare const shouldPostpone: <S extends {
|
|
|
111
111
|
readonly _tag: string;
|
|
112
112
|
}, E extends {
|
|
113
113
|
readonly _tag: string;
|
|
114
|
-
}, R>(machine: Machine<S, E, R, any, any, any
|
|
114
|
+
}, R>(machine: Machine<S, E, R, any, any, any>, stateTag: string, eventTag: string) => boolean;
|
|
115
115
|
/**
|
|
116
116
|
* Process a single event through the machine.
|
|
117
117
|
*
|
|
@@ -128,7 +128,7 @@ declare const processEventCore: <S extends {
|
|
|
128
128
|
readonly _tag: string;
|
|
129
129
|
}, E extends {
|
|
130
130
|
readonly _tag: string;
|
|
131
|
-
}, R,
|
|
131
|
+
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, currentState: S, event: E, self: MachineRef<E>, stateScopeRef: {
|
|
132
132
|
current: Scope.Closeable;
|
|
133
133
|
}, system: ActorSystem, actorId: string, hooks?: ProcessEventHooks<S, E> | undefined) => Effect.Effect<{
|
|
134
134
|
newState: S;
|
|
@@ -150,7 +150,7 @@ declare const runSpawnEffects: <S extends {
|
|
|
150
150
|
readonly _tag: string;
|
|
151
151
|
}, E extends {
|
|
152
152
|
readonly _tag: string;
|
|
153
|
-
}, R,
|
|
153
|
+
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, state: S, event: E, self: MachineRef<E>, stateScope: Scope.Closeable, system: ActorSystem, actorId: string, onError?: ((info: ProcessEventError<S, E>) => Effect.Effect<void>) | undefined, onSpawnDefect?: ((cause: Cause.Cause<unknown>) => Effect.Effect<void>) | undefined) => Effect.Effect<void, never, Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
|
|
154
154
|
/**
|
|
155
155
|
* Resolve which transition should fire for a given state and event.
|
|
156
156
|
* Uses indexed O(1) lookup. First matching transition wins.
|
|
@@ -159,7 +159,7 @@ declare const resolveTransition: <S extends {
|
|
|
159
159
|
readonly _tag: string;
|
|
160
160
|
}, E extends {
|
|
161
161
|
readonly _tag: string;
|
|
162
|
-
}, R>(machine: Machine<S, E, R, any, any, any
|
|
162
|
+
}, R>(machine: Machine<S, E, R, any, any, any>, currentState: S, event: E) => (typeof machine.transitions)[number] | undefined;
|
|
163
163
|
/**
|
|
164
164
|
* Invalidate cached index for a machine (call after mutation).
|
|
165
165
|
*/
|
|
@@ -174,7 +174,7 @@ declare const findTransitions: <S extends {
|
|
|
174
174
|
readonly _tag: string;
|
|
175
175
|
}, E extends {
|
|
176
176
|
readonly _tag: string;
|
|
177
|
-
}, R,
|
|
177
|
+
}, R, SD extends SlotsDef = Record<string, never>>(machine: Machine<S, E, R, any, any, SD>, stateTag: string, eventTag: string) => ReadonlyArray<Transition<S, E, SD, R>>;
|
|
178
178
|
/**
|
|
179
179
|
* Find all spawn effects for a state.
|
|
180
180
|
* Returns empty array if no matches.
|
|
@@ -185,6 +185,6 @@ declare const findSpawnEffects: <S extends {
|
|
|
185
185
|
readonly _tag: string;
|
|
186
186
|
}, E extends {
|
|
187
187
|
readonly _tag: string;
|
|
188
|
-
}, R,
|
|
188
|
+
}, R, SD extends SlotsDef = Record<string, never>>(machine: Machine<S, E, R, any, any, SD>, stateTag: string) => ReadonlyArray<SpawnEffect<S, E, SD, R>>;
|
|
189
189
|
//#endregion
|
|
190
190
|
export { ProcessEventError, ProcessEventHooks, ProcessEventResult, TransitionExecutionResult, executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler, shouldPostpone };
|
|
@@ -29,12 +29,10 @@ const runTransitionHandler = Effect.fn("effect-machine.runTransitionHandler")(fu
|
|
|
29
29
|
self,
|
|
30
30
|
system
|
|
31
31
|
};
|
|
32
|
-
const { guards, effects } = machine._slots;
|
|
33
32
|
const handlerCtx = {
|
|
34
33
|
state,
|
|
35
34
|
event,
|
|
36
|
-
|
|
37
|
-
effects
|
|
35
|
+
slots: machine._slots
|
|
38
36
|
};
|
|
39
37
|
const raw = transition.handler(handlerCtx);
|
|
40
38
|
const resolved = isEffect(raw) ? yield* raw.pipe(Effect.provideService(machine.Context, ctx)) : raw;
|
|
@@ -166,7 +164,7 @@ const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (m
|
|
|
166
164
|
self,
|
|
167
165
|
system
|
|
168
166
|
};
|
|
169
|
-
const
|
|
167
|
+
const slots = machine._slots;
|
|
170
168
|
const reportError = onError;
|
|
171
169
|
const defectSignal = onSpawnDefect;
|
|
172
170
|
for (const spawnEffect of spawnEffects) {
|
|
@@ -175,7 +173,7 @@ const runSpawnEffects = Effect.fn("effect-machine.runSpawnEffects")(function* (m
|
|
|
175
173
|
state,
|
|
176
174
|
event,
|
|
177
175
|
self,
|
|
178
|
-
|
|
176
|
+
slots,
|
|
179
177
|
system
|
|
180
178
|
}).pipe(Effect.provideService(machine.Context, ctx), Effect.catchCause((cause) => {
|
|
181
179
|
if (Cause.hasInterruptsOnly(cause)) return Effect.interrupt;
|