effect-machine 0.9.0 → 0.11.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 +118 -55
- package/dist/actor.d.ts +77 -179
- package/dist/actor.js +161 -113
- package/dist/cluster/entity-machine.js +5 -3
- package/dist/errors.d.ts +12 -1
- package/dist/errors.js +8 -1
- package/dist/index.d.ts +4 -8
- package/dist/index.js +2 -7
- package/dist/internal/transition.d.ts +27 -3
- package/dist/internal/transition.js +38 -9
- package/dist/internal/utils.d.ts +7 -2
- package/dist/internal/utils.js +1 -5
- package/dist/machine.d.ts +94 -35
- package/dist/machine.js +128 -13
- package/dist/testing.js +57 -3
- package/package.json +10 -9
- package/v3/dist/actor.d.ts +210 -0
- package/{dist-v3 → v3/dist}/actor.js +198 -117
- package/{dist-v3 → v3/dist}/cluster/entity-machine.d.ts +1 -1
- package/{dist-v3 → v3/dist}/cluster/entity-machine.js +8 -6
- package/{dist-v3 → v3/dist}/cluster/to-entity.d.ts +1 -1
- package/{dist-v3 → v3/dist}/cluster/to-entity.js +1 -1
- package/v3/dist/errors.d.ts +76 -0
- package/{dist-v3 → v3/dist}/errors.js +9 -2
- package/v3/dist/index.d.ts +9 -0
- package/v3/dist/index.js +8 -0
- package/{dist-v3 → v3/dist}/inspection.d.ts +53 -8
- package/v3/dist/inspection.js +156 -0
- package/{dist-v3 → v3/dist}/internal/brands.d.ts +1 -1
- package/{dist-v3 → v3/dist}/internal/inspection.d.ts +1 -1
- package/v3/dist/internal/inspection.js +20 -0
- package/{dist-v3 → v3/dist}/internal/transition.d.ts +35 -11
- package/{dist-v3 → v3/dist}/internal/transition.js +47 -15
- package/{dist-v3 → v3/dist}/internal/utils.d.ts +9 -4
- package/{dist-v3 → v3/dist}/internal/utils.js +2 -6
- package/{dist-v3 → v3/dist}/machine.d.ts +113 -40
- package/{dist-v3 → v3/dist}/machine.js +191 -15
- package/{dist-v3 → v3/dist}/schema.d.ts +1 -1
- package/{dist-v3 → v3/dist}/schema.js +5 -2
- package/{dist-v3 → v3/dist}/slot.d.ts +4 -3
- package/{dist-v3 → v3/dist}/slot.js +1 -1
- package/{dist-v3 → v3/dist}/testing.d.ts +14 -8
- package/{dist-v3 → v3/dist}/testing.js +60 -6
- package/dist/persistence/adapter.d.ts +0 -135
- package/dist/persistence/adapter.js +0 -25
- package/dist/persistence/adapters/in-memory.d.ts +0 -32
- package/dist/persistence/adapters/in-memory.js +0 -174
- package/dist/persistence/index.d.ts +0 -5
- package/dist/persistence/index.js +0 -5
- package/dist/persistence/persistent-actor.d.ts +0 -50
- package/dist/persistence/persistent-actor.js +0 -368
- package/dist/persistence/persistent-machine.d.ts +0 -105
- package/dist/persistence/persistent-machine.js +0 -22
- package/dist-v3/actor.d.ts +0 -291
- package/dist-v3/errors.d.ts +0 -27
- package/dist-v3/index.d.ts +0 -12
- package/dist-v3/index.js +0 -13
- package/dist-v3/inspection.js +0 -48
- package/dist-v3/internal/inspection.js +0 -13
- package/dist-v3/persistence/adapter.d.ts +0 -125
- package/dist-v3/persistence/adapter.js +0 -25
- package/dist-v3/persistence/adapters/in-memory.d.ts +0 -32
- package/dist-v3/persistence/adapters/in-memory.js +0 -174
- package/dist-v3/persistence/index.d.ts +0 -5
- package/dist-v3/persistence/index.js +0 -5
- package/dist-v3/persistence/persistent-actor.d.ts +0 -49
- package/dist-v3/persistence/persistent-actor.js +0 -365
- package/dist-v3/persistence/persistent-machine.d.ts +0 -105
- package/dist-v3/persistence/persistent-machine.js +0 -22
- /package/{dist-v3 → v3/dist}/_virtual/_rolldown/runtime.js +0 -0
- /package/{dist-v3 → v3/dist}/cluster/index.d.ts +0 -0
- /package/{dist-v3 → v3/dist}/cluster/index.js +0 -0
- /package/{dist-v3 → v3/dist}/internal/brands.js +0 -0
package/README.md
CHANGED
|
@@ -74,8 +74,12 @@ const orderMachine = Machine.make({
|
|
|
74
74
|
const program = Effect.gen(function* () {
|
|
75
75
|
const actor = yield* Machine.spawn(orderMachine);
|
|
76
76
|
|
|
77
|
+
// fire-and-forget
|
|
77
78
|
yield* actor.send(OrderEvent.Process);
|
|
78
|
-
|
|
79
|
+
|
|
80
|
+
// request-reply — get ProcessEventResult back
|
|
81
|
+
const result = yield* actor.call(OrderEvent.Ship({ trackingId: "TRACK-123" }));
|
|
82
|
+
console.log(result.transitioned); // true
|
|
79
83
|
|
|
80
84
|
const state = yield* actor.waitFor(OrderState.Shipped);
|
|
81
85
|
console.log(state); // Shipped { orderId: "order-1", trackingId: "TRACK-123" }
|
|
@@ -183,6 +187,53 @@ machine.task(State.Loading, ({ effects, state }) => effects.fetchData({ url: sta
|
|
|
183
187
|
});
|
|
184
188
|
```
|
|
185
189
|
|
|
190
|
+
### State Timeouts
|
|
191
|
+
|
|
192
|
+
`.timeout()` — gen_statem-style state timeouts. Timer starts on state entry, cancels on exit:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
machine
|
|
196
|
+
.timeout(State.Loading, {
|
|
197
|
+
duration: Duration.seconds(30),
|
|
198
|
+
event: Event.Timeout,
|
|
199
|
+
})
|
|
200
|
+
// Dynamic duration from state
|
|
201
|
+
.timeout(State.Retrying, {
|
|
202
|
+
duration: (state) => Duration.seconds(state.backoff),
|
|
203
|
+
event: Event.GiveUp,
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`.reenter()` restarts the timer with fresh state values.
|
|
208
|
+
|
|
209
|
+
### Event Postpone
|
|
210
|
+
|
|
211
|
+
`.postpone()` — gen_statem-style event postpone. When a matching event arrives in the given state, it is buffered. After the next state transition (tag change), buffered events drain in FIFO order, looping until stable:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
machine
|
|
215
|
+
.postpone(State.Connecting, Event.Data) // single event
|
|
216
|
+
.postpone(State.Connecting, [Event.Data, Event.Cmd]); // multiple events
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Reply-bearing events (`call`/`ask`) in the postpone buffer are settled with `ActorStoppedError` on stop/interrupt/final-state.
|
|
220
|
+
|
|
221
|
+
### ask / reply
|
|
222
|
+
|
|
223
|
+
Handlers can return a domain reply via `{ state, reply }`:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
.on(State.Active, Event.GetCount, ({ state }) => ({
|
|
227
|
+
state, // stay in same state
|
|
228
|
+
reply: state.count, // domain value returned to caller
|
|
229
|
+
}))
|
|
230
|
+
|
|
231
|
+
// Caller side:
|
|
232
|
+
const count = yield* actor.ask<number>(Event.GetCount);
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`ask` fails with `NoReplyError` if the handler doesn't provide a reply, and `ActorStoppedError` if the actor stops while the request is pending.
|
|
236
|
+
|
|
186
237
|
### Child Actors
|
|
187
238
|
|
|
188
239
|
Spawn children from `.spawn()` handlers with `self.spawn`. Children are state-scoped — auto-stopped on state exit:
|
|
@@ -206,6 +257,31 @@ const child = yield * parent.system.get("worker-1"); // Option<ActorRef>
|
|
|
206
257
|
|
|
207
258
|
Every actor always has a system — `Machine.spawn` creates an implicit one if no `ActorSystem` is in context.
|
|
208
259
|
|
|
260
|
+
### Persistence
|
|
261
|
+
|
|
262
|
+
Persistence is composed from primitives — no built-in adapter or framework:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
// Snapshot persistence — observe state changes, save externally
|
|
266
|
+
yield * actor.changes.pipe(Stream.runForEach((state) => saveSnapshot(actor.id, state)));
|
|
267
|
+
|
|
268
|
+
// Event journal — observe transitions
|
|
269
|
+
yield * actor.transitions.pipe(Stream.runForEach(({ event }) => appendEvent(actor.id, event)));
|
|
270
|
+
|
|
271
|
+
// Restore from snapshot
|
|
272
|
+
const savedState = yield * loadSnapshot(id);
|
|
273
|
+
const actor = yield * Machine.spawn(machine, { hydrate: savedState });
|
|
274
|
+
|
|
275
|
+
// Restore from event log
|
|
276
|
+
const events = yield * loadEvents(id);
|
|
277
|
+
const state = yield * Machine.replay(machine, events);
|
|
278
|
+
const actor = yield * Machine.spawn(machine, { hydrate: state });
|
|
279
|
+
|
|
280
|
+
// Restore from snapshot + tail events
|
|
281
|
+
const state = yield * Machine.replay(machine, tailEvents, { from: snapshot });
|
|
282
|
+
const actor = yield * Machine.spawn(machine, { hydrate: state });
|
|
283
|
+
```
|
|
284
|
+
|
|
209
285
|
### System Observation
|
|
210
286
|
|
|
211
287
|
React to actors joining and leaving the system:
|
|
@@ -245,21 +321,6 @@ expect(result.states.map((s) => s._tag)).toEqual(["Idle", "Loading", "Done"]);
|
|
|
245
321
|
yield * assertPath(machine, events, ["Idle", "Loading", "Done"]);
|
|
246
322
|
```
|
|
247
323
|
|
|
248
|
-
## Documentation
|
|
249
|
-
|
|
250
|
-
See the [primer](./primer/) for comprehensive documentation:
|
|
251
|
-
|
|
252
|
-
| Topic | File | Description |
|
|
253
|
-
| ----------- | ----------------------------------------- | ------------------------------ |
|
|
254
|
-
| Overview | [index.md](./primer/index.md) | Navigation and quick reference |
|
|
255
|
-
| Basics | [basics.md](./primer/basics.md) | Core concepts |
|
|
256
|
-
| Handlers | [handlers.md](./primer/handlers.md) | Transitions and guards |
|
|
257
|
-
| Effects | [effects.md](./primer/effects.md) | spawn, background, timeouts |
|
|
258
|
-
| Testing | [testing.md](./primer/testing.md) | simulate, harness, assertions |
|
|
259
|
-
| Actors | [actors.md](./primer/actors.md) | ActorSystem, ActorRef |
|
|
260
|
-
| Persistence | [persistence.md](./primer/persistence.md) | Snapshots, event sourcing |
|
|
261
|
-
| Gotchas | [gotchas.md](./primer/gotchas.md) | Common mistakes |
|
|
262
|
-
|
|
263
324
|
## API Quick Reference
|
|
264
325
|
|
|
265
326
|
### Building
|
|
@@ -273,55 +334,47 @@ See the [primer](./primer/) for comprehensive documentation:
|
|
|
273
334
|
| `.reenter(State.X, Event.Y, handler)` | Force re-entry on same state |
|
|
274
335
|
| `.spawn(State.X, handler)` | State-scoped effect |
|
|
275
336
|
| `.task(State.X, run, { onSuccess })` | State-scoped task |
|
|
337
|
+
| `.timeout(State.X, { duration, event })` | State timeout (gen_statem) |
|
|
338
|
+
| `.postpone(State.X, Event.Y)` | Postpone event in state (gen_statem) |
|
|
276
339
|
| `.background(handler)` | Machine-lifetime effect |
|
|
277
340
|
| `.final(State.X)` | Mark final state |
|
|
278
341
|
| `.build({ slot: impl })` | Provide implementations, returns `BuiltMachine` (terminal) |
|
|
279
342
|
| `.build()` | Finalize no-slot machine, returns `BuiltMachine` (terminal) |
|
|
280
|
-
| `.persist(config)` | Enable persistence |
|
|
281
|
-
|
|
282
|
-
### State Constructors
|
|
283
|
-
|
|
284
|
-
| Method | Purpose |
|
|
285
|
-
| -------------------------------------- | ------------------------------ |
|
|
286
|
-
| `State.X.derive(source)` | Pick target fields from source |
|
|
287
|
-
| `State.X.derive(source, { field: v })` | Pick fields + apply overrides |
|
|
288
|
-
| `State.$is("X")(value)` | Type guard |
|
|
289
|
-
| `State.$match(value, { X: fn, ... })` | Pattern matching |
|
|
290
343
|
|
|
291
344
|
### Running
|
|
292
345
|
|
|
293
|
-
| Method
|
|
294
|
-
|
|
|
295
|
-
| `Machine.spawn(machine)`
|
|
296
|
-
| `Machine.spawn(machine, id)`
|
|
297
|
-
| `
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
| Function | Description |
|
|
302
|
-
| ------------------------------------------ | ---------------------------------------------------------------- |
|
|
303
|
-
| `simulate(machine, events)` | Run events, get all states (accepts `Machine` or `BuiltMachine`) |
|
|
304
|
-
| `createTestHarness(machine)` | Step-by-step testing (accepts `Machine` or `BuiltMachine`) |
|
|
305
|
-
| `assertPath(machine, events, path)` | Assert exact path |
|
|
306
|
-
| `assertReaches(machine, events, tag)` | Assert final state |
|
|
307
|
-
| `assertNeverReaches(machine, events, tag)` | Assert state never visited |
|
|
346
|
+
| Method | Purpose |
|
|
347
|
+
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
348
|
+
| `Machine.spawn(machine)` | Single actor, no registry. Caller manages lifetime via `actor.stop`. Auto-cleans up if `Scope` present. |
|
|
349
|
+
| `Machine.spawn(machine, id)` | Same as above with custom ID |
|
|
350
|
+
| `Machine.spawn(machine, { hydrate: s })` | Restore from saved state — re-runs spawn effects for that state |
|
|
351
|
+
| `Machine.replay(machine, events)` | Fold events through handlers to compute state (for event sourcing restore) |
|
|
352
|
+
| `system.spawn(id, machine)` | Registry, lookup by ID, bulk ops. Cleans up on system teardown. |
|
|
308
353
|
|
|
309
354
|
### Actor
|
|
310
355
|
|
|
311
|
-
| Method | Description
|
|
312
|
-
| -------------------------------- |
|
|
313
|
-
| `actor.send(event)` |
|
|
314
|
-
| `actor.
|
|
315
|
-
| `actor.
|
|
316
|
-
| `actor.
|
|
317
|
-
| `actor.
|
|
318
|
-
| `actor.
|
|
319
|
-
| `actor.
|
|
320
|
-
| `actor.
|
|
321
|
-
| `actor.
|
|
322
|
-
| `actor.
|
|
323
|
-
| `actor.
|
|
324
|
-
| `actor.
|
|
356
|
+
| Method | Description |
|
|
357
|
+
| -------------------------------- | ----------------------------------------------- |
|
|
358
|
+
| `actor.send(event)` | Fire-and-forget (queue event) |
|
|
359
|
+
| `actor.cast(event)` | Alias for send (OTP gen_server:cast) |
|
|
360
|
+
| `actor.call(event)` | Request-reply, returns `ProcessEventResult` |
|
|
361
|
+
| `actor.ask<R>(event)` | Typed domain reply from handler |
|
|
362
|
+
| `actor.snapshot` | Get current state |
|
|
363
|
+
| `actor.matches(tag)` | Check state tag |
|
|
364
|
+
| `actor.can(event)` | Can handle event? |
|
|
365
|
+
| `actor.changes` | Stream of state changes |
|
|
366
|
+
| `actor.transitions` | Stream of `{ fromState, toState, event }` edges |
|
|
367
|
+
| `actor.waitFor(State.X)` | Wait for state (constructor or fn) |
|
|
368
|
+
| `actor.awaitFinal` | Wait final state |
|
|
369
|
+
| `actor.sendAndWait(ev, State.X)` | Send + wait for state |
|
|
370
|
+
| `actor.subscribe(fn)` | Sync callback |
|
|
371
|
+
| `actor.sync.send(event)` | Sync fire-and-forget (for UI) |
|
|
372
|
+
| `actor.sync.stop()` | Sync stop |
|
|
373
|
+
| `actor.sync.snapshot()` | Sync get state |
|
|
374
|
+
| `actor.sync.matches(tag)` | Sync check state tag |
|
|
375
|
+
| `actor.sync.can(event)` | Sync can handle event? |
|
|
376
|
+
| `actor.system` | Access the actor's `ActorSystem` |
|
|
377
|
+
| `actor.children` | Child actors (`ReadonlyMap`) |
|
|
325
378
|
|
|
326
379
|
### ActorSystem
|
|
327
380
|
|
|
@@ -334,6 +387,16 @@ See the [primer](./primer/) for comprehensive documentation:
|
|
|
334
387
|
| `system.subscribe(fn)` | Sync callback for spawn/stop events |
|
|
335
388
|
| `system.events` | Async `Stream<SystemEvent>` for spawn/stop |
|
|
336
389
|
|
|
390
|
+
### Testing
|
|
391
|
+
|
|
392
|
+
| Function | Description |
|
|
393
|
+
| ------------------------------------------ | ---------------------------------------------------------------- |
|
|
394
|
+
| `simulate(machine, events)` | Run events, get all states (accepts `Machine` or `BuiltMachine`) |
|
|
395
|
+
| `createTestHarness(machine)` | Step-by-step testing (accepts `Machine` or `BuiltMachine`) |
|
|
396
|
+
| `assertPath(machine, events, path)` | Assert exact path |
|
|
397
|
+
| `assertReaches(machine, events, tag)` | Assert final state |
|
|
398
|
+
| `assertNeverReaches(machine, events, tag)` | Assert state never visited |
|
|
399
|
+
|
|
337
400
|
## License
|
|
338
401
|
|
|
339
402
|
MIT
|
package/dist/actor.d.ts
CHANGED
|
@@ -1,95 +1,99 @@
|
|
|
1
1
|
import { EffectsDef, GuardsDef, MachineContext } from "./slot.js";
|
|
2
|
-
import {
|
|
3
|
-
import { DuplicateActorError } from "./errors.js";
|
|
2
|
+
import { ActorStoppedError, DuplicateActorError, NoReplyError } from "./errors.js";
|
|
4
3
|
import { ProcessEventError, ProcessEventHooks, ProcessEventResult, processEventCore, resolveTransition, runSpawnEffects } from "./internal/transition.js";
|
|
5
|
-
import { PersistentActorRef } from "./persistence/persistent-actor.js";
|
|
6
|
-
import { ActorMetadata, PersistenceAdapterTag, PersistenceError, RestoreResult, VersionConflictError } from "./persistence/adapter.js";
|
|
7
4
|
import { BuiltMachine, Machine, MachineRef } from "./machine.js";
|
|
8
|
-
import { Deferred, Effect, Layer, Option, Queue, Ref, Scope, ServiceMap, Stream, SubscriptionRef } from "effect";
|
|
5
|
+
import { Deferred, Effect, Layer, Option, PubSub, Queue, Ref, Scope, ServiceMap, Stream, SubscriptionRef } from "effect";
|
|
9
6
|
import * as effect_Tracer0 from "effect/Tracer";
|
|
10
7
|
|
|
11
8
|
//#region src/actor.d.ts
|
|
12
|
-
/**
|
|
13
|
-
|
|
9
|
+
/** Discriminated mailbox request */
|
|
10
|
+
type QueuedEvent<E> = {
|
|
11
|
+
readonly _tag: "send";
|
|
14
12
|
readonly event: E;
|
|
15
|
-
|
|
13
|
+
} | {
|
|
14
|
+
readonly _tag: "call";
|
|
15
|
+
readonly event: E;
|
|
16
|
+
readonly reply: Deferred.Deferred<ProcessEventResult<{
|
|
16
17
|
readonly _tag: string;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
18
|
+
}>, ActorStoppedError>;
|
|
19
|
+
} | {
|
|
20
|
+
readonly _tag: "ask";
|
|
21
|
+
readonly event: E;
|
|
22
|
+
readonly reply: Deferred.Deferred<unknown, NoReplyError | ActorStoppedError>;
|
|
23
|
+
};
|
|
19
24
|
/**
|
|
20
25
|
* Reference to a running actor.
|
|
21
26
|
*/
|
|
27
|
+
/**
|
|
28
|
+
* Sync projection of ActorRef for non-Effect boundaries (React hooks, framework callbacks).
|
|
29
|
+
*/
|
|
30
|
+
interface ActorRefSync<State extends {
|
|
31
|
+
readonly _tag: string;
|
|
32
|
+
}, Event> {
|
|
33
|
+
readonly send: (event: Event) => void;
|
|
34
|
+
readonly stop: () => void;
|
|
35
|
+
readonly snapshot: () => State;
|
|
36
|
+
readonly matches: (tag: State["_tag"]) => boolean;
|
|
37
|
+
readonly can: (event: Event) => boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Information about a successful transition.
|
|
41
|
+
* Emitted on the `transitions` stream after each accepted event.
|
|
42
|
+
*/
|
|
43
|
+
interface TransitionInfo<State, Event> {
|
|
44
|
+
readonly fromState: State;
|
|
45
|
+
readonly toState: State;
|
|
46
|
+
readonly event: Event;
|
|
47
|
+
}
|
|
22
48
|
interface ActorRef<State extends {
|
|
23
49
|
readonly _tag: string;
|
|
24
50
|
}, Event> {
|
|
25
|
-
/**
|
|
26
|
-
* Unique identifier for this actor
|
|
27
|
-
*/
|
|
28
51
|
readonly id: string;
|
|
29
|
-
/**
|
|
30
|
-
* Send an event to the actor
|
|
31
|
-
*/
|
|
52
|
+
/** Send an event (fire-and-forget). */
|
|
32
53
|
readonly send: (event: Event) => Effect.Effect<void>;
|
|
54
|
+
/** Fire-and-forget alias for send (OTP gen_server:cast). */
|
|
55
|
+
readonly cast: (event: Event) => Effect.Effect<void>;
|
|
33
56
|
/**
|
|
34
|
-
*
|
|
57
|
+
* Serialized request-reply (OTP gen_server:call).
|
|
58
|
+
* Event is processed through the queue; caller gets ProcessEventResult back.
|
|
35
59
|
*/
|
|
36
|
-
readonly
|
|
60
|
+
readonly call: (event: Event) => Effect.Effect<ProcessEventResult<State>>;
|
|
37
61
|
/**
|
|
38
|
-
*
|
|
62
|
+
* Typed request-reply. Event is processed through the queue; caller gets
|
|
63
|
+
* the domain value returned by the handler's `reply` field.
|
|
64
|
+
* Fails with NoReplyError if the handler doesn't provide a reply.
|
|
39
65
|
*/
|
|
66
|
+
readonly ask: <R>(event: Event) => Effect.Effect<R, NoReplyError | ActorStoppedError>;
|
|
67
|
+
/** Observable state. */
|
|
68
|
+
readonly state: SubscriptionRef.SubscriptionRef<State>;
|
|
69
|
+
/** Stop the actor gracefully. */
|
|
40
70
|
readonly stop: Effect.Effect<void>;
|
|
41
|
-
/**
|
|
42
|
-
* Stop the actor (fire-and-forget).
|
|
43
|
-
* Signals graceful shutdown without waiting for completion.
|
|
44
|
-
* Use when stopping from sync contexts (e.g. framework cleanup hooks).
|
|
45
|
-
*/
|
|
46
|
-
readonly stopSync: () => void;
|
|
47
|
-
/**
|
|
48
|
-
* Get current state snapshot (Effect)
|
|
49
|
-
*/
|
|
71
|
+
/** Get current state snapshot. */
|
|
50
72
|
readonly snapshot: Effect.Effect<State>;
|
|
51
|
-
/**
|
|
52
|
-
* Get current state snapshot (sync)
|
|
53
|
-
*/
|
|
54
|
-
readonly snapshotSync: () => State;
|
|
55
|
-
/**
|
|
56
|
-
* Check if current state matches tag (Effect)
|
|
57
|
-
*/
|
|
73
|
+
/** Check if current state matches tag. */
|
|
58
74
|
readonly matches: (tag: State["_tag"]) => Effect.Effect<boolean>;
|
|
59
|
-
/**
|
|
60
|
-
* Check if current state matches tag (sync)
|
|
61
|
-
*/
|
|
62
|
-
readonly matchesSync: (tag: State["_tag"]) => boolean;
|
|
63
|
-
/**
|
|
64
|
-
* Check if event can be handled in current state (Effect)
|
|
65
|
-
*/
|
|
75
|
+
/** Check if event can be handled in current state. */
|
|
66
76
|
readonly can: (event: Event) => Effect.Effect<boolean>;
|
|
67
|
-
/**
|
|
68
|
-
* Check if event can be handled in current state (sync)
|
|
69
|
-
*/
|
|
70
|
-
readonly canSync: (event: Event) => boolean;
|
|
71
|
-
/**
|
|
72
|
-
* Stream of state changes
|
|
73
|
-
*/
|
|
77
|
+
/** Stream of state changes. */
|
|
74
78
|
readonly changes: Stream.Stream<State>;
|
|
75
79
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
80
|
+
* Stream of accepted transitions (edge stream).
|
|
81
|
+
*
|
|
82
|
+
* Emits `{ fromState, toState, event }` on every successful transition,
|
|
83
|
+
* including same-state reenters. PubSub-backed — late subscribers miss
|
|
84
|
+
* past edges. This is observational, not a durability guarantee.
|
|
78
85
|
*/
|
|
86
|
+
readonly transitions: Stream.Stream<TransitionInfo<State, Event>>;
|
|
87
|
+
/** Wait for a state matching predicate or variant (includes current snapshot). */
|
|
79
88
|
readonly waitFor: {
|
|
80
89
|
(predicate: (state: State) => boolean): Effect.Effect<State>;
|
|
81
90
|
(state: {
|
|
82
91
|
readonly _tag: State["_tag"];
|
|
83
92
|
}): Effect.Effect<State>;
|
|
84
93
|
};
|
|
85
|
-
/**
|
|
86
|
-
* Wait for a final state (includes current snapshot)
|
|
87
|
-
*/
|
|
94
|
+
/** Wait for a final state (includes current snapshot). */
|
|
88
95
|
readonly awaitFinal: Effect.Effect<State>;
|
|
89
|
-
/**
|
|
90
|
-
* Send event and wait for predicate, state variant, or final state.
|
|
91
|
-
* Accepts a predicate function or a state constructor/value (e.g. `State.Active`).
|
|
92
|
-
*/
|
|
96
|
+
/** Send event and wait for predicate, state variant, or final state. */
|
|
93
97
|
readonly sendAndWait: {
|
|
94
98
|
(event: Event, predicate: (state: State) => boolean): Effect.Effect<State>;
|
|
95
99
|
(event: Event, state: {
|
|
@@ -97,39 +101,13 @@ interface ActorRef<State extends {
|
|
|
97
101
|
}): Effect.Effect<State>;
|
|
98
102
|
(event: Event): Effect.Effect<State>;
|
|
99
103
|
};
|
|
100
|
-
/**
|
|
101
|
-
* Send event synchronously (fire-and-forget).
|
|
102
|
-
* No-op on stopped actors. Use when you need to send from sync contexts
|
|
103
|
-
* (e.g. framework hooks, event handlers).
|
|
104
|
-
*/
|
|
105
|
-
readonly sendSync: (event: Event) => void;
|
|
106
|
-
/**
|
|
107
|
-
* Send event and wait for the transition result (synchronous processing).
|
|
108
|
-
* The event is processed through the queue (preserving serialization)
|
|
109
|
-
* but the caller gets back the ProcessEventResult.
|
|
110
|
-
*
|
|
111
|
-
* OTP gen_server:call equivalent — use when you need to know what happened.
|
|
112
|
-
*/
|
|
113
|
-
readonly dispatch: (event: Event) => Effect.Effect<ProcessEventResult<State>>;
|
|
114
|
-
/**
|
|
115
|
-
* Promise-based dispatch — send event and get back the transition result.
|
|
116
|
-
* Use at non-Effect boundaries (React event handlers, framework hooks, tests).
|
|
117
|
-
*/
|
|
118
|
-
readonly dispatchPromise: (event: Event) => Promise<ProcessEventResult<State>>;
|
|
119
|
-
/**
|
|
120
|
-
* Subscribe to state changes (sync callback)
|
|
121
|
-
* Returns unsubscribe function
|
|
122
|
-
*/
|
|
104
|
+
/** Subscribe to state changes (sync callback). Returns unsubscribe function. */
|
|
123
105
|
readonly subscribe: (fn: (state: State) => void) => () => void;
|
|
124
|
-
/**
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
*/
|
|
106
|
+
/** Sync helpers for non-Effect boundaries. */
|
|
107
|
+
readonly sync: ActorRefSync<State, Event>;
|
|
108
|
+
/** The actor system this actor belongs to. */
|
|
128
109
|
readonly system: ActorSystem;
|
|
129
|
-
/**
|
|
130
|
-
* Child actors spawned via `self.spawn` in this actor's handlers.
|
|
131
|
-
* State-scoped children are auto-removed on state exit.
|
|
132
|
-
*/
|
|
110
|
+
/** Child actors spawned via `self.spawn` in this actor's handlers. */
|
|
133
111
|
readonly children: ReadonlyMap<string, ActorRef<AnyState, unknown>>;
|
|
134
112
|
}
|
|
135
113
|
/** Base type for stored actors (internal) */
|
|
@@ -159,54 +137,17 @@ interface ActorSystem {
|
|
|
159
137
|
/**
|
|
160
138
|
* Spawn a new actor with the given machine.
|
|
161
139
|
*
|
|
162
|
-
* For regular machines, returns ActorRef.
|
|
163
|
-
* For persistent machines (created with Machine.persist), returns PersistentActorRef.
|
|
164
|
-
*
|
|
165
|
-
* All effect slots must be provided via `.build()` before spawning.
|
|
166
|
-
*
|
|
167
140
|
* @example
|
|
168
141
|
* ```ts
|
|
169
|
-
* // Regular machine (built)
|
|
170
142
|
* const built = machine.build({ fetchData: ... })
|
|
171
143
|
* const actor = yield* system.spawn("my-actor", built);
|
|
172
|
-
*
|
|
173
|
-
* // Persistent machine (auto-detected)
|
|
174
|
-
* const persistentActor = yield* system.spawn("my-actor", persistentMachine);
|
|
175
|
-
* persistentActor.persist; // available
|
|
176
|
-
* persistentActor.version; // available
|
|
177
144
|
* ```
|
|
178
145
|
*/
|
|
179
|
-
readonly spawn: {
|
|
180
|
-
<S extends {
|
|
181
|
-
readonly _tag: string;
|
|
182
|
-
}, E extends {
|
|
183
|
-
readonly _tag: string;
|
|
184
|
-
}, R>(id: string, machine: BuiltMachine<S, E, R>): Effect.Effect<ActorRef<S, E>, DuplicateActorError, R>;
|
|
185
|
-
<S extends {
|
|
186
|
-
readonly _tag: string;
|
|
187
|
-
}, E extends {
|
|
188
|
-
readonly _tag: string;
|
|
189
|
-
}, R>(id: string, machine: PersistentMachine<S, E, R>): Effect.Effect<PersistentActorRef<S, E, R>, PersistenceError | VersionConflictError | DuplicateActorError, R | PersistenceAdapterTag>;
|
|
190
|
-
};
|
|
191
|
-
/**
|
|
192
|
-
* Restore an actor from persistence.
|
|
193
|
-
* Returns None if no persisted state exists for the given ID.
|
|
194
|
-
*
|
|
195
|
-
* @example
|
|
196
|
-
* ```ts
|
|
197
|
-
* const maybeActor = yield* system.restore("order-1", persistentMachine);
|
|
198
|
-
* if (Option.isSome(maybeActor)) {
|
|
199
|
-
* const actor = maybeActor.value;
|
|
200
|
-
* const state = yield* actor.snapshot;
|
|
201
|
-
* console.log(`Restored to state: ${state._tag}`);
|
|
202
|
-
* }
|
|
203
|
-
* ```
|
|
204
|
-
*/
|
|
205
|
-
readonly restore: <S extends {
|
|
146
|
+
readonly spawn: <S extends {
|
|
206
147
|
readonly _tag: string;
|
|
207
148
|
}, E extends {
|
|
208
149
|
readonly _tag: string;
|
|
209
|
-
}, R>(id: string, machine:
|
|
150
|
+
}, R>(id: string, machine: BuiltMachine<S, E, R>) => Effect.Effect<ActorRef<S, E>, DuplicateActorError, R>;
|
|
210
151
|
/**
|
|
211
152
|
* Get an existing actor by ID
|
|
212
153
|
*/
|
|
@@ -230,53 +171,6 @@ interface ActorSystem {
|
|
|
230
171
|
* Returns an unsubscribe function.
|
|
231
172
|
*/
|
|
232
173
|
readonly subscribe: (fn: SystemEventListener) => () => void;
|
|
233
|
-
/**
|
|
234
|
-
* List all persisted actor metadata.
|
|
235
|
-
* Returns empty array if adapter doesn't support registry.
|
|
236
|
-
*
|
|
237
|
-
* @example
|
|
238
|
-
* ```ts
|
|
239
|
-
* const actors = yield* system.listPersisted();
|
|
240
|
-
* for (const meta of actors) {
|
|
241
|
-
* console.log(`${meta.id}: ${meta.stateTag} (v${meta.version})`);
|
|
242
|
-
* }
|
|
243
|
-
* ```
|
|
244
|
-
*/
|
|
245
|
-
readonly listPersisted: () => Effect.Effect<ReadonlyArray<ActorMetadata>, PersistenceError, PersistenceAdapterTag>;
|
|
246
|
-
/**
|
|
247
|
-
* Restore multiple actors by ID.
|
|
248
|
-
* Returns both successfully restored actors and failures.
|
|
249
|
-
*
|
|
250
|
-
* @example
|
|
251
|
-
* ```ts
|
|
252
|
-
* const result = yield* system.restoreMany(["order-1", "order-2"], orderMachine);
|
|
253
|
-
* console.log(`Restored: ${result.restored.length}, Failed: ${result.failed.length}`);
|
|
254
|
-
* ```
|
|
255
|
-
*/
|
|
256
|
-
readonly restoreMany: <S extends {
|
|
257
|
-
readonly _tag: string;
|
|
258
|
-
}, E extends {
|
|
259
|
-
readonly _tag: string;
|
|
260
|
-
}, R>(ids: ReadonlyArray<string>, machine: PersistentMachine<S, E, R>) => Effect.Effect<RestoreResult<S, E, R>, never, R | PersistenceAdapterTag>;
|
|
261
|
-
/**
|
|
262
|
-
* Restore all persisted actors for a machine type.
|
|
263
|
-
* Uses adapter registry if available, otherwise returns empty result.
|
|
264
|
-
*
|
|
265
|
-
* @example
|
|
266
|
-
* ```ts
|
|
267
|
-
* const result = yield* system.restoreAll(orderMachine, {
|
|
268
|
-
* filter: (meta) => meta.stateTag !== "Done"
|
|
269
|
-
* });
|
|
270
|
-
* console.log(`Restored ${result.restored.length} active orders`);
|
|
271
|
-
* ```
|
|
272
|
-
*/
|
|
273
|
-
readonly restoreAll: <S extends {
|
|
274
|
-
readonly _tag: string;
|
|
275
|
-
}, E extends {
|
|
276
|
-
readonly _tag: string;
|
|
277
|
-
}, R>(machine: PersistentMachine<S, E, R>, options?: {
|
|
278
|
-
filter?: (meta: ActorMetadata) => boolean;
|
|
279
|
-
}) => Effect.Effect<RestoreResult<S, E, R>, PersistenceError, R | PersistenceAdapterTag>;
|
|
280
174
|
}
|
|
281
175
|
/**
|
|
282
176
|
* ActorSystem service tag
|
|
@@ -289,13 +183,13 @@ type Listeners<S> = Set<(state: S) => void>;
|
|
|
289
183
|
*/
|
|
290
184
|
declare const notifyListeners: <S>(listeners: Listeners<S>, state: S) => void;
|
|
291
185
|
/**
|
|
292
|
-
* Build core ActorRef methods
|
|
186
|
+
* Build core ActorRef methods.
|
|
293
187
|
*/
|
|
294
188
|
declare const buildActorRefCore: <S extends {
|
|
295
189
|
readonly _tag: string;
|
|
296
190
|
}, E extends {
|
|
297
191
|
readonly _tag: string;
|
|
298
|
-
}, R, GD extends GuardsDef, EFD extends EffectsDef>(id: string, machine: Machine<S, E, R, any, any, GD, EFD>, stateRef: SubscriptionRef.SubscriptionRef<S>, eventQueue: Queue.Queue<QueuedEvent<E>>, stoppedRef: Ref.Ref<boolean>, listeners: Listeners<S>, stop: Effect.Effect<void>, system: ActorSystem, childrenMap: ReadonlyMap<string, ActorRef<AnyState, unknown>>) => ActorRef<S, E>;
|
|
192
|
+
}, R, GD extends GuardsDef, EFD extends EffectsDef>(id: string, machine: Machine<S, E, R, any, any, GD, EFD>, stateRef: SubscriptionRef.SubscriptionRef<S>, eventQueue: 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>>) => ActorRef<S, E>;
|
|
299
193
|
/**
|
|
300
194
|
* Create and start an actor for a machine
|
|
301
195
|
*/
|
|
@@ -303,10 +197,14 @@ declare const createActor: <S extends {
|
|
|
303
197
|
readonly _tag: string;
|
|
304
198
|
}, E extends {
|
|
305
199
|
readonly _tag: string;
|
|
306
|
-
}, R, GD extends GuardsDef, EFD extends EffectsDef>(id: string, machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD
|
|
200
|
+
}, R, GD extends GuardsDef, EFD extends EffectsDef>(id: string, machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, options?: {
|
|
201
|
+
initialState?: S;
|
|
202
|
+
} | undefined) => Effect.Effect<ActorRef<S, E>, never, Exclude<R, MachineContext<S, E, MachineRef<E>>> | Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, effect_Tracer0.ParentSpan> | Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope> | Exclude<Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>, effect_Tracer0.ParentSpan>>;
|
|
203
|
+
/** Fail all pending call/ask Deferreds with ActorStoppedError. Safe to call multiple times. */
|
|
204
|
+
declare const settlePendingReplies: (pendingReplies: Set<Deferred.Deferred<unknown, unknown>>, actorId: string) => Effect.Effect<void, never, never>;
|
|
307
205
|
/**
|
|
308
206
|
* Default ActorSystem layer
|
|
309
207
|
*/
|
|
310
208
|
declare const Default: Layer.Layer<ActorSystem, never, never>;
|
|
311
209
|
//#endregion
|
|
312
|
-
export { ActorRef, ActorSystem, Default, Listeners, type ProcessEventError, type ProcessEventHooks, type ProcessEventResult, QueuedEvent, SystemEvent, SystemEventListener, buildActorRefCore, createActor, notifyListeners, processEventCore, resolveTransition, runSpawnEffects };
|
|
210
|
+
export { ActorRef, ActorRefSync, ActorSystem, Default, Listeners, type ProcessEventError, type ProcessEventHooks, type ProcessEventResult, QueuedEvent, SystemEvent, SystemEventListener, TransitionInfo, buildActorRefCore, createActor, notifyListeners, processEventCore, resolveTransition, runSpawnEffects, settlePendingReplies };
|