effect-machine 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -11
- package/dist/actor.d.ts +18 -6
- package/dist/actor.js +151 -61
- package/dist/cluster/entity-machine.d.ts +1 -1
- package/dist/cluster/entity-machine.js +2 -1
- package/dist/cluster/to-entity.d.ts +1 -1
- package/dist/errors.d.ts +9 -2
- package/dist/errors.js +8 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/internal/runtime.d.ts +21 -2
- package/dist/internal/runtime.js +64 -56
- package/dist/internal/transition.d.ts +9 -9
- package/dist/internal/transition.js +3 -5
- package/dist/machine.d.ts +129 -135
- package/dist/machine.js +97 -112
- package/dist/schema.d.ts +17 -1
- package/dist/schema.js +10 -0
- package/dist/slot.d.ts +112 -86
- package/dist/slot.js +92 -59
- package/dist/testing.d.ts +16 -16
- package/dist/testing.js +3 -3
- package/package.json +3 -3
- package/v3/dist/actor.d.ts +25 -12
- package/v3/dist/actor.js +174 -89
- package/v3/dist/cluster/entity-machine.d.ts +1 -1
- package/v3/dist/cluster/entity-machine.js +1 -0
- package/v3/dist/cluster/to-entity.d.ts +1 -1
- package/v3/dist/errors.d.ts +12 -3
- package/v3/dist/errors.js +10 -4
- package/v3/dist/index.d.ts +6 -6
- package/v3/dist/index.js +2 -2
- package/v3/dist/inspection.d.ts +3 -22
- package/v3/dist/inspection.js +1 -15
- package/v3/dist/internal/brands.d.ts +4 -8
- package/v3/dist/internal/inspection.js +1 -1
- package/v3/dist/internal/runtime.d.ts +27 -8
- package/v3/dist/internal/runtime.js +91 -61
- package/v3/dist/internal/transition.d.ts +10 -10
- package/v3/dist/internal/transition.js +8 -10
- package/v3/dist/internal/utils.js +5 -1
- package/v3/dist/machine.d.ts +160 -120
- package/v3/dist/machine.js +118 -115
- package/v3/dist/schema.d.ts +25 -11
- package/v3/dist/schema.js +18 -5
- package/v3/dist/slot.d.ts +112 -86
- package/v3/dist/slot.js +92 -59
- package/v3/dist/testing.d.ts +16 -16
- package/v3/dist/testing.js +7 -7
package/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 }),
|
|
@@ -97,6 +98,7 @@ const actor =
|
|
|
97
98
|
}),
|
|
98
99
|
},
|
|
99
100
|
});
|
|
101
|
+
yield * actor.start;
|
|
100
102
|
```
|
|
101
103
|
|
|
102
104
|
The same machine can run with different slot implementations in tests, local apps, or production. Slots are accepted everywhere the machine runs:
|
|
@@ -108,16 +110,21 @@ The same machine can run with different slot implementations in tests, local app
|
|
|
108
110
|
|
|
109
111
|
## Running Actors
|
|
110
112
|
|
|
111
|
-
`Machine.spawn`
|
|
113
|
+
`Machine.spawn` allocates an actor but does not start it. Call `actor.start` to fork the event loop, background effects, and spawn effects. Events sent before `start()` are queued.
|
|
112
114
|
|
|
113
115
|
```ts
|
|
114
116
|
const program = Effect.gen(function* () {
|
|
115
117
|
const actor = yield* Machine.spawn(checkoutMachine, {
|
|
116
118
|
slots: {
|
|
117
|
-
chargeCard: ({ cartId }
|
|
118
|
-
|
|
119
|
+
chargeCard: ({ cartId }) =>
|
|
120
|
+
checkoutMachine.Context.pipe(
|
|
121
|
+
Effect.flatMap((ctx) =>
|
|
122
|
+
ctx.self.send(CheckoutEvent.Charged({ receiptId: `rcpt_${cartId}` })),
|
|
123
|
+
),
|
|
124
|
+
),
|
|
119
125
|
},
|
|
120
126
|
});
|
|
127
|
+
yield* actor.start;
|
|
121
128
|
|
|
122
129
|
yield* actor.send(CheckoutEvent.Submit);
|
|
123
130
|
const finalState = yield* actor.awaitFinal;
|
|
@@ -128,6 +135,7 @@ Effect.runPromise(Effect.scoped(program));
|
|
|
128
135
|
|
|
129
136
|
Key actor operations:
|
|
130
137
|
|
|
138
|
+
- `start` forks the event loop (idempotent, required after `Machine.spawn`)
|
|
131
139
|
- `send(event)` queues and returns immediately
|
|
132
140
|
- `call(event)` returns full transition info
|
|
133
141
|
- `ask(event)` returns a typed domain reply (requires `Event.reply(...)`)
|
|
@@ -135,7 +143,7 @@ Key actor operations:
|
|
|
135
143
|
- `stop` interrupts now; `drain` processes the remaining queue first
|
|
136
144
|
- `watch(other)` completes when another actor stops
|
|
137
145
|
|
|
138
|
-
For named actors or shared lookup, use an actor system:
|
|
146
|
+
For named actors or shared lookup, use an actor system. `system.spawn` auto-starts — no `actor.start` needed:
|
|
139
147
|
|
|
140
148
|
```ts
|
|
141
149
|
import { ActorSystemDefault, ActorSystemService } from "effect-machine";
|
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 { Lifecycle, Machine } 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
|
|
|
@@ -54,6 +54,15 @@ interface ActorRef<State extends {
|
|
|
54
54
|
readonly state: SubscriptionRef.SubscriptionRef<State>;
|
|
55
55
|
/** Stop the actor gracefully. */
|
|
56
56
|
readonly stop: Effect.Effect<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Start the actor — fork event loop, background effects, spawn effects.
|
|
59
|
+
* Idempotent: first caller runs initialization, subsequent callers await completion.
|
|
60
|
+
* Events sent before start() are queued and processed when start() runs.
|
|
61
|
+
*
|
|
62
|
+
* Called automatically by `system.spawn`. For `Machine.spawn`, the caller
|
|
63
|
+
* must call `start` explicitly.
|
|
64
|
+
*/
|
|
65
|
+
readonly start: Effect.Effect<void>;
|
|
57
66
|
/** Get current state snapshot. */
|
|
58
67
|
readonly snapshot: Effect.Effect<State>;
|
|
59
68
|
/** Check if current state matches tag. */
|
|
@@ -158,8 +167,10 @@ interface ActorSystem {
|
|
|
158
167
|
readonly _tag: string;
|
|
159
168
|
}, E extends {
|
|
160
169
|
readonly _tag: string;
|
|
161
|
-
}, R
|
|
170
|
+
}, R, SD extends SlotsDef = Record<string, never>>(id: string, machine: Machine<S, E, R, any, any, SD>, options?: {
|
|
162
171
|
readonly supervision?: Supervision.Policy;
|
|
172
|
+
readonly slots?: ProvideSlots<SD, any>;
|
|
173
|
+
readonly lifecycle?: Lifecycle<S, E>;
|
|
163
174
|
}) => Effect.Effect<ActorRef<S, E>, DuplicateActorError, R>;
|
|
164
175
|
/**
|
|
165
176
|
* Get an existing actor by ID
|
|
@@ -202,7 +213,7 @@ declare const buildActorRefCore: <S extends {
|
|
|
202
213
|
readonly _tag: string;
|
|
203
214
|
}, E extends {
|
|
204
215
|
readonly _tag: string;
|
|
205
|
-
}, R,
|
|
216
|
+
}, 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>, start: 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
217
|
/**
|
|
207
218
|
* Create and start an actor for a machine.
|
|
208
219
|
* Delegates to the shared runtime kernel with actor-specific lifecycle hooks.
|
|
@@ -211,9 +222,10 @@ declare const createActor: <S extends {
|
|
|
211
222
|
readonly _tag: string;
|
|
212
223
|
}, E extends {
|
|
213
224
|
readonly _tag: string;
|
|
214
|
-
}, R,
|
|
225
|
+
}, R, SD extends SlotsDef>(id: string, machine: Machine<S, E, R, any, any, SD>, options?: {
|
|
215
226
|
initialState?: S;
|
|
216
|
-
supervision?: Supervision.Policy;
|
|
227
|
+
supervision?: Supervision.Policy;
|
|
228
|
+
lifecycle?: Lifecycle<S, E>; /** @internal Called by system after each restart — emits ActorRestarted system event */
|
|
217
229
|
onRestart?: (generation: number, exit: ActorExit<unknown>) => Effect.Effect<void>;
|
|
218
230
|
} | undefined) => Effect.Effect<ActorRef<S, E>, never, never>;
|
|
219
231
|
/** 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
|
|
@@ -28,7 +29,7 @@ const notifyListeners = (listeners, state) => {
|
|
|
28
29
|
/**
|
|
29
30
|
* Build core ActorRef methods.
|
|
30
31
|
*/
|
|
31
|
-
const buildActorRefCore = (id, machine, stateRef, eventQueueRef, stoppedRef, listeners, stop, system, childrenMap, pendingReplies, transitionsPubSub, exitDeferred) => {
|
|
32
|
+
const buildActorRefCore = (id, machine, stateRef, eventQueueRef, stoppedRef, listeners, stop, start, system, childrenMap, pendingReplies, transitionsPubSub, exitDeferred) => {
|
|
32
33
|
const send = Effect.fn("effect-machine.actor.send")(function* (event) {
|
|
33
34
|
if (yield* Ref.get(stoppedRef)) return;
|
|
34
35
|
const q = yield* Ref.get(eventQueueRef);
|
|
@@ -115,6 +116,7 @@ const buildActorRefCore = (id, machine, stateRef, eventQueueRef, stoppedRef, lis
|
|
|
115
116
|
ask,
|
|
116
117
|
state: stateRef,
|
|
117
118
|
stop,
|
|
119
|
+
start,
|
|
118
120
|
snapshot,
|
|
119
121
|
matches,
|
|
120
122
|
can,
|
|
@@ -190,33 +192,87 @@ const buildInspectionHooks = (actorId, inspector) => ({
|
|
|
190
192
|
}))
|
|
191
193
|
});
|
|
192
194
|
/**
|
|
195
|
+
* Resolve actor system from context, creating an implicit one if none exists.
|
|
196
|
+
* @internal
|
|
197
|
+
*/
|
|
198
|
+
const resolveActorSystem = Effect.fn("effect-machine.resolveActorSystem")(function* () {
|
|
199
|
+
const existingSystem = yield* Effect.serviceOption(ActorSystem);
|
|
200
|
+
if (Option.isSome(existingSystem)) return {
|
|
201
|
+
system: existingSystem.value,
|
|
202
|
+
implicitSystemScope: void 0
|
|
203
|
+
};
|
|
204
|
+
const scope = yield* Scope.make();
|
|
205
|
+
return {
|
|
206
|
+
system: yield* make().pipe(Effect.provideService(Scope.Scope, scope)),
|
|
207
|
+
implicitSystemScope: scope
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
/**
|
|
211
|
+
* Run the supervision loop for a supervised actor.
|
|
212
|
+
* Observes exit deferred, applies restart policy, resets cell resources on restart.
|
|
213
|
+
* @internal
|
|
214
|
+
*/
|
|
215
|
+
const runSupervisionLoop = (params) => Effect.gen(function* () {
|
|
216
|
+
const step = yield* Schedule.toStepWithSleep(params.supervision.schedule);
|
|
217
|
+
while (true) {
|
|
218
|
+
const currentRuntime = params.runtimeRef.current;
|
|
219
|
+
if (currentRuntime === void 0) return;
|
|
220
|
+
const generationExit = yield* Deferred.await(currentRuntime.exitDeferred);
|
|
221
|
+
if (generationExit._tag !== "Defect") {
|
|
222
|
+
yield* Deferred.succeed(params.terminalExitDeferred, generationExit);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (params.supervision.shouldRestart !== void 0 && !params.supervision.shouldRestart(generationExit)) {
|
|
226
|
+
yield* Deferred.succeed(params.terminalExitDeferred, generationExit);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if ((yield* step(generationExit).pipe(Effect.exit))._tag === "Failure") {
|
|
230
|
+
yield* Deferred.succeed(params.terminalExitDeferred, generationExit);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const nextGeneration = params.generationRef.get() + 1;
|
|
234
|
+
params.generationRef.set(nextGeneration);
|
|
235
|
+
let restartState = params.machine.initial;
|
|
236
|
+
if (params.lifecycle?.recovery !== void 0) {
|
|
237
|
+
const resolved = yield* params.lifecycle.recovery.resolve({
|
|
238
|
+
actorId: params.id,
|
|
239
|
+
generation: nextGeneration,
|
|
240
|
+
machineInitial: params.machine.initial
|
|
241
|
+
});
|
|
242
|
+
if (Option.isSome(resolved)) restartState = resolved.value;
|
|
243
|
+
}
|
|
244
|
+
yield* settlePendingReplies(params.pendingReplies, params.id);
|
|
245
|
+
const freshQueue = yield* Queue.unbounded();
|
|
246
|
+
yield* Ref.set(params.eventQueueRef, freshQueue);
|
|
247
|
+
yield* SubscriptionRef.set(params.stateRef, restartState);
|
|
248
|
+
yield* Ref.set(params.stoppedRef, false);
|
|
249
|
+
params.childrenMap.clear();
|
|
250
|
+
const machineForRestart = restartState !== params.machine.initial ? Object.create(params.machine, { initial: {
|
|
251
|
+
value: restartState,
|
|
252
|
+
enumerable: true
|
|
253
|
+
} }) : params.machine;
|
|
254
|
+
const newRuntime = yield* params.spawnGeneration(machineForRestart);
|
|
255
|
+
params.runtimeRef.current = newRuntime;
|
|
256
|
+
yield* newRuntime.start;
|
|
257
|
+
if (params.onRestart !== void 0) yield* params.onRestart(nextGeneration, generationExit);
|
|
258
|
+
notifyListeners(params.listeners, restartState);
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
/**
|
|
193
262
|
* Create and start an actor for a machine.
|
|
194
263
|
* Delegates to the shared runtime kernel with actor-specific lifecycle hooks.
|
|
195
264
|
*/
|
|
196
265
|
const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machine, options) {
|
|
266
|
+
const lifecycle = options?.lifecycle;
|
|
197
267
|
const initial = options?.initialState ?? machine.initial;
|
|
198
268
|
yield* Effect.annotateCurrentSpan("effect_machine.actor.id", id);
|
|
199
269
|
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
|
-
}
|
|
270
|
+
const { system, implicitSystemScope } = yield* resolveActorSystem();
|
|
209
271
|
const inspectorValue = Option.getOrUndefined(yield* Effect.serviceOption(Inspector));
|
|
210
272
|
const childrenMap = /* @__PURE__ */ new Map();
|
|
211
273
|
const pendingReplies = /* @__PURE__ */ new Set();
|
|
212
274
|
const listeners = /* @__PURE__ */ new Set();
|
|
213
275
|
const transitionsPubSub = yield* PubSub.unbounded();
|
|
214
|
-
yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
|
|
215
|
-
type: "@machine.spawn",
|
|
216
|
-
actorId: id,
|
|
217
|
-
initialState: initial,
|
|
218
|
-
timestamp
|
|
219
|
-
}));
|
|
220
276
|
const hooks = inspectorValue !== void 0 ? buildInspectionHooks(id, inspectorValue) : void 0;
|
|
221
277
|
const machineWithState = initial !== machine.initial ? Object.create(machine, { initial: {
|
|
222
278
|
value: initial,
|
|
@@ -228,9 +284,11 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
|
|
|
228
284
|
const eventQueueRef = yield* Ref.make(initialQueue);
|
|
229
285
|
const terminalExitDeferred = yield* Deferred.make();
|
|
230
286
|
let stopEmitted = false;
|
|
287
|
+
let generation = 0;
|
|
231
288
|
const runtimeRef = { current: void 0 };
|
|
289
|
+
const supervisorFiberRef = { current: void 0 };
|
|
232
290
|
/** Build lifecycle hooks for a generation */
|
|
233
|
-
const
|
|
291
|
+
const buildRuntimeLifecycle = () => {
|
|
234
292
|
stopEmitted = false;
|
|
235
293
|
return {
|
|
236
294
|
onEvent: inspectorValue !== void 0 ? (state, event) => emitWithTimestamp(inspectorValue, (timestamp) => ({
|
|
@@ -240,8 +298,18 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
|
|
|
240
298
|
event,
|
|
241
299
|
timestamp
|
|
242
300
|
})) : void 0,
|
|
243
|
-
onStateChange: (result,
|
|
301
|
+
onStateChange: (result, event) => Effect.gen(function* () {
|
|
244
302
|
notifyListeners(listeners, result.newState);
|
|
303
|
+
if (lifecycle?.durability !== void 0 && result.transitioned) {
|
|
304
|
+
const durability = lifecycle.durability;
|
|
305
|
+
if (durability.shouldSave === void 0 || durability.shouldSave(result.newState, result.previousState)) yield* durability.save({
|
|
306
|
+
actorId: id,
|
|
307
|
+
generation,
|
|
308
|
+
previousState: result.previousState,
|
|
309
|
+
nextState: result.newState,
|
|
310
|
+
event
|
|
311
|
+
});
|
|
312
|
+
}
|
|
245
313
|
yield* Effect.annotateCurrentSpan("effect_machine.transition.matched", true);
|
|
246
314
|
if (result.lifecycleRan) {
|
|
247
315
|
yield* Effect.annotateCurrentSpan("effect_machine.state.from", result.previousState._tag);
|
|
@@ -293,7 +361,7 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
|
|
|
293
361
|
stoppedRef,
|
|
294
362
|
eventQueue: currentQueue
|
|
295
363
|
},
|
|
296
|
-
lifecycle:
|
|
364
|
+
lifecycle: buildRuntimeLifecycle(),
|
|
297
365
|
wrapProcess: (state, event, inner) => Effect.withSpan("effect-machine.event.process", { attributes: {
|
|
298
366
|
"effect_machine.actor.id": id,
|
|
299
367
|
"effect_machine.state.current": state._tag,
|
|
@@ -307,49 +375,67 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
|
|
|
307
375
|
}));
|
|
308
376
|
})
|
|
309
377
|
})));
|
|
310
|
-
|
|
311
|
-
runtimeRef.current = runtime;
|
|
378
|
+
runtimeRef.current = yield* spawnGeneration(machineWithState);
|
|
312
379
|
const supervision = options?.supervision;
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const step = yield* Schedule.toStepWithSleep(supervision.schedule);
|
|
316
|
-
let generation = 0;
|
|
317
|
-
while (true) {
|
|
318
|
-
const currentRuntime = runtimeRef.current;
|
|
319
|
-
if (currentRuntime === void 0) return;
|
|
320
|
-
const generationExit = yield* Deferred.await(currentRuntime.exitDeferred);
|
|
321
|
-
if (generationExit._tag !== "Defect") {
|
|
322
|
-
yield* Deferred.succeed(terminalExitDeferred, generationExit);
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
if (supervision.shouldRestart !== void 0 && !supervision.shouldRestart(generationExit)) {
|
|
326
|
-
yield* Deferred.succeed(terminalExitDeferred, generationExit);
|
|
327
|
-
return;
|
|
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
|
-
}
|
|
344
|
-
}));
|
|
345
|
-
else yield* Effect.forkDetach(Deferred.await(runtime.exitDeferred).pipe(Effect.tap((exit) => Deferred.succeed(terminalExitDeferred, exit))));
|
|
346
|
-
return buildActorRefCore(id, machine, stateRef, eventQueueRef, stoppedRef, listeners, Effect.gen(function* () {
|
|
347
|
-
if (supervisorFiber !== void 0) yield* Fiber.interrupt(supervisorFiber);
|
|
380
|
+
const stop = Effect.gen(function* () {
|
|
381
|
+
if (supervisorFiberRef.current !== void 0) yield* Fiber.interrupt(supervisorFiberRef.current);
|
|
348
382
|
const currentRuntime = runtimeRef.current;
|
|
349
383
|
if (currentRuntime !== void 0) yield* currentRuntime.stop;
|
|
350
384
|
yield* Deferred.succeed(terminalExitDeferred, { _tag: "Stopped" });
|
|
351
385
|
if (implicitSystemScope !== void 0) yield* Scope.close(implicitSystemScope, Exit.void);
|
|
352
|
-
}).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid)
|
|
386
|
+
}).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid);
|
|
387
|
+
const isHydrated = options?.initialState !== void 0;
|
|
388
|
+
return buildActorRefCore(id, machine, stateRef, eventQueueRef, stoppedRef, listeners, stop, Effect.gen(function* () {
|
|
389
|
+
if (lifecycle?.recovery !== void 0 && !isHydrated) {
|
|
390
|
+
const resolved = yield* lifecycle.recovery.resolve({
|
|
391
|
+
actorId: id,
|
|
392
|
+
generation,
|
|
393
|
+
machineInitial: machine.initial
|
|
394
|
+
});
|
|
395
|
+
if (Option.isSome(resolved)) {
|
|
396
|
+
yield* SubscriptionRef.set(stateRef, resolved.value);
|
|
397
|
+
runtimeRef.current = yield* spawnGeneration(Object.create(machine, { initial: {
|
|
398
|
+
value: resolved.value,
|
|
399
|
+
enumerable: true
|
|
400
|
+
} }));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const currentState = yield* SubscriptionRef.get(stateRef);
|
|
404
|
+
yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
|
|
405
|
+
type: "@machine.spawn",
|
|
406
|
+
actorId: id,
|
|
407
|
+
initialState: currentState,
|
|
408
|
+
timestamp
|
|
409
|
+
}));
|
|
410
|
+
if (supervision !== void 0) supervisorFiberRef.current = yield* Effect.forkDetach(runSupervisionLoop({
|
|
411
|
+
supervision,
|
|
412
|
+
machine,
|
|
413
|
+
id,
|
|
414
|
+
runtimeRef,
|
|
415
|
+
terminalExitDeferred,
|
|
416
|
+
pendingReplies,
|
|
417
|
+
eventQueueRef,
|
|
418
|
+
stateRef,
|
|
419
|
+
stoppedRef,
|
|
420
|
+
childrenMap,
|
|
421
|
+
listeners,
|
|
422
|
+
spawnGeneration,
|
|
423
|
+
lifecycle,
|
|
424
|
+
generationRef: {
|
|
425
|
+
get: () => generation,
|
|
426
|
+
set: (g) => {
|
|
427
|
+
generation = g;
|
|
428
|
+
}
|
|
429
|
+
},
|
|
430
|
+
onRestart: options?.onRestart
|
|
431
|
+
}));
|
|
432
|
+
else {
|
|
433
|
+
const currentRuntime = runtimeRef.current;
|
|
434
|
+
if (currentRuntime !== void 0) yield* Effect.forkDetach(Deferred.await(currentRuntime.exitDeferred).pipe(Effect.tap((exit) => Deferred.succeed(terminalExitDeferred, exit))));
|
|
435
|
+
}
|
|
436
|
+
const currentRuntime = runtimeRef.current;
|
|
437
|
+
if (currentRuntime !== void 0) yield* currentRuntime.start;
|
|
438
|
+
}).pipe(Effect.withSpan("effect-machine.actor.start"), Effect.asVoid), system, childrenMap, pendingReplies, transitionsPubSub, terminalExitDeferred);
|
|
353
439
|
});
|
|
354
440
|
/** Fail all pending call/ask Deferreds with ActorStoppedError. Safe to call multiple times. */
|
|
355
441
|
const settlePendingReplies = (pendingReplies, actorId) => Effect.sync(() => {
|
|
@@ -406,9 +492,11 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
|
|
|
406
492
|
});
|
|
407
493
|
const spawnRegular = Effect.fn("effect-machine.actorSystem.spawnRegular")(function* (id, machine, spawnOptions) {
|
|
408
494
|
if (MutableHashMap.has(actorsMap, id)) return yield* new DuplicateActorError({ actorId: id });
|
|
495
|
+
const materialized = spawnOptions?.slots !== void 0 ? materializeMachine(machine, spawnOptions.slots) : machine;
|
|
409
496
|
let actorRef;
|
|
410
|
-
const actor = yield* createActor(id,
|
|
497
|
+
const actor = yield* createActor(id, materialized, {
|
|
411
498
|
supervision: spawnOptions?.supervision,
|
|
499
|
+
lifecycle: spawnOptions?.lifecycle,
|
|
412
500
|
onRestart: spawnOptions?.supervision !== void 0 ? (generation, exit) => actorRef !== void 0 ? emitSystemEvent({
|
|
413
501
|
_tag: "ActorRestarted",
|
|
414
502
|
id,
|
|
@@ -418,7 +506,9 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
|
|
|
418
506
|
}) : Effect.void : void 0
|
|
419
507
|
});
|
|
420
508
|
actorRef = actor;
|
|
421
|
-
|
|
509
|
+
yield* registerActor(id, actor);
|
|
510
|
+
yield* actor.start.pipe(Effect.catchCause((cause) => actor.stop.pipe(Effect.andThen(Effect.failCause(cause)))));
|
|
511
|
+
return actor;
|
|
422
512
|
});
|
|
423
513
|
const spawn = (id, machine, options) => withSpawnGate(spawnRegular(id, machine, options));
|
|
424
514
|
const get = Effect.fn("effect-machine.actorSystem.get")(function* (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";
|
|
@@ -60,6 +60,7 @@ const EntityMachine = { layer: (entity, machine, options) => {
|
|
|
60
60
|
eventQueue
|
|
61
61
|
}
|
|
62
62
|
});
|
|
63
|
+
yield* runtime.start;
|
|
63
64
|
if (persistCtx.adapter !== void 0) {
|
|
64
65
|
const { adapter: pAdapter, key } = persistCtx;
|
|
65
66
|
const strategy = persistence?.strategy ?? "snapshot";
|
|
@@ -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, Durability, DurabilityCommit, HandlerContext, Lifecycle, Machine, MachineRef, MakeConfig, Recovery, RecoveryContext, 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
|
|
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 Durability, type DurabilityCommit, type EffectEvent, type ErrorEvent, Event, type EventReceivedEvent, type HandlerContext, type HasSlotKeys, type InspectionEvent, type Inspector, type InspectorHandler, Inspector as InspectorService, InvalidSchemaError, type Lifecycle, 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 Recovery, type RecoveryContext, 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";
|
|
@@ -55,6 +55,12 @@ interface RuntimeHandle<S, E> {
|
|
|
55
55
|
readonly isStopped: Effect.Effect<boolean>;
|
|
56
56
|
/** Stop the runtime (interrupt event loop, clean up) */
|
|
57
57
|
readonly stop: Effect.Effect<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Start the runtime — fork event loop, background effects, spawn effects.
|
|
60
|
+
* Idempotent: first caller runs initialization, subsequent callers await completion.
|
|
61
|
+
* Events sent before start() are queued and processed when start() runs.
|
|
62
|
+
*/
|
|
63
|
+
readonly start: Effect.Effect<void>;
|
|
58
64
|
/** @internal — raw event queue for direct enqueue (actor.ts uses this for pendingReplies tracking) */
|
|
59
65
|
readonly _queue: Queue.Queue<RuntimeQueuedEvent<E>>;
|
|
60
66
|
/** @internal — stopped ref for direct access */
|
|
@@ -137,6 +143,19 @@ declare const createRuntime: <S extends {
|
|
|
137
143
|
readonly _tag: string;
|
|
138
144
|
}, E extends {
|
|
139
145
|
readonly _tag: string;
|
|
140
|
-
}, R,
|
|
146
|
+
}, R, SD extends SlotsDef>(machine: Machine<S, E, R, any, any, SD>, system: ActorSystem, config: RuntimeConfig<S, E>) => Effect.Effect<{
|
|
147
|
+
stop: Effect.Effect<void, never, never>;
|
|
148
|
+
start: Effect.Effect<void, unknown, Exclude<R, MachineContext<S, E, MachineRef<E>>> | Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
|
|
149
|
+
send: (event: E) => Effect.Effect<void>;
|
|
150
|
+
sendWait: (event: E) => Effect.Effect<void, unknown>;
|
|
151
|
+
ask: (event: E) => Effect.Effect<unknown, NoReplyError>;
|
|
152
|
+
getState: Effect.Effect<S, never, never>;
|
|
153
|
+
stateRef: SubscriptionRef.SubscriptionRef<S>;
|
|
154
|
+
isStopped: Effect.Effect<boolean>;
|
|
155
|
+
_queue: Queue.Queue<RuntimeQueuedEvent<E>, never>;
|
|
156
|
+
_stoppedRef: Ref.Ref<boolean>;
|
|
157
|
+
exitDeferred: Deferred.Deferred<ActorExit<S>, never>;
|
|
158
|
+
actorScope: Scope.Closeable;
|
|
159
|
+
}, never, Scope.Scope>;
|
|
141
160
|
//#endregion
|
|
142
161
|
export { ProcessQueuedResult, RuntimeCellResources, RuntimeConfig, RuntimeHandle, RuntimeLifecycleHooks, RuntimeQueuedEvent, createRuntime };
|