effect-machine 0.10.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.
Files changed (38) hide show
  1. package/README.md +65 -62
  2. package/dist/actor.d.ts +26 -94
  3. package/dist/actor.js +38 -95
  4. package/dist/index.d.ts +3 -7
  5. package/dist/index.js +1 -6
  6. package/dist/internal/transition.d.ts +1 -1
  7. package/dist/internal/transition.js +1 -1
  8. package/dist/internal/utils.js +1 -5
  9. package/dist/machine.d.ts +31 -35
  10. package/dist/machine.js +63 -13
  11. package/package.json +2 -2
  12. package/v3/dist/actor.d.ts +25 -93
  13. package/v3/dist/actor.js +37 -94
  14. package/v3/dist/index.d.ts +3 -7
  15. package/v3/dist/index.js +1 -6
  16. package/v3/dist/internal/utils.js +1 -5
  17. package/v3/dist/machine.d.ts +31 -35
  18. package/v3/dist/machine.js +63 -13
  19. package/dist/persistence/adapter.d.ts +0 -135
  20. package/dist/persistence/adapter.js +0 -25
  21. package/dist/persistence/adapters/in-memory.d.ts +0 -32
  22. package/dist/persistence/adapters/in-memory.js +0 -174
  23. package/dist/persistence/index.d.ts +0 -5
  24. package/dist/persistence/index.js +0 -5
  25. package/dist/persistence/persistent-actor.d.ts +0 -50
  26. package/dist/persistence/persistent-actor.js +0 -404
  27. package/dist/persistence/persistent-machine.d.ts +0 -105
  28. package/dist/persistence/persistent-machine.js +0 -22
  29. package/v3/dist/persistence/adapter.d.ts +0 -138
  30. package/v3/dist/persistence/adapter.js +0 -25
  31. package/v3/dist/persistence/adapters/in-memory.d.ts +0 -32
  32. package/v3/dist/persistence/adapters/in-memory.js +0 -174
  33. package/v3/dist/persistence/index.d.ts +0 -5
  34. package/v3/dist/persistence/index.js +0 -5
  35. package/v3/dist/persistence/persistent-actor.d.ts +0 -50
  36. package/v3/dist/persistence/persistent-actor.js +0 -404
  37. package/v3/dist/persistence/persistent-machine.d.ts +0 -105
  38. package/v3/dist/persistence/persistent-machine.js +0 -22
package/v3/dist/actor.js CHANGED
@@ -1,11 +1,8 @@
1
1
  import { Inspector } from "./inspection.js";
2
2
  import { INTERNAL_INIT_EVENT } from "./internal/utils.js";
3
3
  import { ActorStoppedError, DuplicateActorError, NoReplyError } from "./errors.js";
4
- import { isPersistentMachine } from "./persistence/persistent-machine.js";
5
4
  import { emitWithTimestamp } from "./internal/inspection.js";
6
5
  import { processEventCore, resolveTransition, runSpawnEffects, shouldPostpone } from "./internal/transition.js";
7
- import { PersistenceAdapterTag, PersistenceError } from "./persistence/adapter.js";
8
- import { createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
9
6
  import { Cause, Context, Deferred, Effect, Exit, Fiber, Layer, MutableHashMap, Option, PubSub, Queue, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect";
10
7
  //#region src/actor.ts
11
8
  /**
@@ -31,7 +28,7 @@ const notifyListeners = (listeners, state) => {
31
28
  /**
32
29
  * Build core ActorRef methods shared between regular and persistent actors.
33
30
  */
34
- const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listeners, stop, system, childrenMap, pendingReplies) => {
31
+ const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listeners, stop, system, childrenMap, pendingReplies, transitionsPubSub) => {
35
32
  const send = Effect.fn("effect-machine.actor.send")(function* (event) {
36
33
  if (yield* Ref.get(stoppedRef)) return;
37
34
  yield* Queue.offer(eventQueue, {
@@ -121,6 +118,7 @@ const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listen
121
118
  matches,
122
119
  can,
123
120
  changes: stateRef.changes,
121
+ transitions: transitionsPubSub !== void 0 ? Stream.fromPubSub(transitionsPubSub) : Stream.empty,
124
122
  waitFor,
125
123
  awaitFinal,
126
124
  sendAndWait,
@@ -151,7 +149,8 @@ const buildActorRefCore = (id, machine, stateRef, eventQueue, stoppedRef, listen
151
149
  /**
152
150
  * Create and start an actor for a machine
153
151
  */
154
- const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machine) {
152
+ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machine, options) {
153
+ const initial = options?.initialState ?? machine.initial;
155
154
  yield* Effect.annotateCurrentSpan("effect_machine.actor.id", id);
156
155
  const existingSystem = yield* Effect.serviceOption(ActorSystem);
157
156
  let system;
@@ -186,20 +185,20 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
186
185
  return child;
187
186
  })
188
187
  };
189
- yield* Effect.annotateCurrentSpan("effect_machine.actor.initial_state", machine.initial._tag);
188
+ yield* Effect.annotateCurrentSpan("effect_machine.actor.initial_state", initial._tag);
190
189
  yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
191
190
  type: "@machine.spawn",
192
191
  actorId: id,
193
- initialState: machine.initial,
192
+ initialState: initial,
194
193
  timestamp
195
194
  }));
196
- const stateRef = yield* SubscriptionRef.make(machine.initial);
195
+ const stateRef = yield* SubscriptionRef.make(initial);
197
196
  const listeners = /* @__PURE__ */ new Set();
198
197
  const backgroundFibers = [];
199
198
  const initEvent = { _tag: INTERNAL_INIT_EVENT };
200
199
  const ctx = {
201
200
  actorId: id,
202
- state: machine.initial,
201
+ state: initial,
203
202
  event: initEvent,
204
203
  self,
205
204
  system
@@ -208,7 +207,7 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
208
207
  for (const bg of machine.backgroundEffects) {
209
208
  const fiber = yield* Effect.forkDaemon(bg.handler({
210
209
  actorId: id,
211
- state: machine.initial,
210
+ state: initial,
212
211
  event: initEvent,
213
212
  self,
214
213
  effects: effectSlots,
@@ -217,14 +216,14 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
217
216
  backgroundFibers.push(fiber);
218
217
  }
219
218
  const stateScopeRef = { current: yield* Scope.make() };
220
- yield* runSpawnEffectsWithInspection(machine, machine.initial, initEvent, self, stateScopeRef.current, id, inspectorValue, system);
221
- if (machine.finalStates.has(machine.initial._tag)) {
219
+ yield* runSpawnEffectsWithInspection(machine, initial, initEvent, self, stateScopeRef.current, id, inspectorValue, system);
220
+ if (machine.finalStates.has(initial._tag)) {
222
221
  yield* Scope.close(stateScopeRef.current, Exit.void);
223
222
  yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
224
223
  yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
225
224
  type: "@machine.stop",
226
225
  actorId: id,
227
- finalState: machine.initial,
226
+ finalState: initial,
228
227
  timestamp
229
228
  }));
230
229
  yield* Ref.set(stoppedRef, true);
@@ -232,7 +231,8 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
232
231
  return buildActorRefCore(id, machine, stateRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid), system, childrenMap, /* @__PURE__ */ new Set());
233
232
  }
234
233
  const pendingReplies = /* @__PURE__ */ new Set();
235
- const loopFiber = yield* Effect.forkDaemon(eventLoop(machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, id, inspectorValue, system, pendingReplies));
234
+ const transitionsPubSub = yield* PubSub.unbounded();
235
+ const loopFiber = yield* Effect.forkDaemon(eventLoop(machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, id, inspectorValue, system, pendingReplies, transitionsPubSub));
236
236
  return buildActorRefCore(id, machine, stateRef, eventQueue, stoppedRef, listeners, Effect.gen(function* () {
237
237
  const finalState = yield* SubscriptionRef.get(stateRef);
238
238
  yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
@@ -247,7 +247,7 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
247
247
  yield* Scope.close(stateScopeRef.current, Exit.void);
248
248
  yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
249
249
  if (implicitSystemScope !== void 0) yield* Scope.close(implicitSystemScope, Exit.void);
250
- }).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid), system, childrenMap, pendingReplies);
250
+ }).pipe(Effect.withSpan("effect-machine.actor.stop"), Effect.asVoid), system, childrenMap, pendingReplies, transitionsPubSub);
251
251
  });
252
252
  /** Fail all pending call/ask Deferreds with ActorStoppedError. Safe to call multiple times. */
253
253
  const settlePendingReplies = (pendingReplies, actorId) => Effect.sync(() => {
@@ -260,7 +260,7 @@ const settlePendingReplies = (pendingReplies, actorId) => Effect.sync(() => {
260
260
  * Includes postpone buffer — events matching postpone rules are buffered
261
261
  * and drained after state tag changes (gen_statem semantics).
262
262
  */
263
- const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, actorId, inspector, system, pendingReplies) {
263
+ const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, actorId, inspector, system, pendingReplies, transitionsPubSub) {
264
264
  const postponed = [];
265
265
  const hasPostponeRules = machine.postponeRules.length > 0;
266
266
  const processQueued = Effect.fn("effect-machine.actor.processQueued")(function* (queued) {
@@ -303,6 +303,11 @@ const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine
303
303
  }));
304
304
  break;
305
305
  }
306
+ if (result.transitioned) yield* PubSub.publish(transitionsPubSub, {
307
+ fromState: result.previousState,
308
+ toState: result.newState,
309
+ event
310
+ });
306
311
  return {
307
312
  shouldStop,
308
313
  stateChanged: result.lifecycleRan
@@ -318,15 +323,21 @@ const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine
318
323
  yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
319
324
  return;
320
325
  }
321
- if (stateChanged && postponed.length > 0) {
326
+ let drainTriggered = stateChanged;
327
+ while (drainTriggered && postponed.length > 0) {
328
+ drainTriggered = false;
322
329
  const drained = postponed.splice(0);
323
- for (const entry of drained) if ((yield* processQueued(entry)).shouldStop) {
324
- yield* Ref.set(stoppedRef, true);
325
- settlePostponedBuffer(postponed, pendingReplies, actorId);
326
- yield* settlePendingReplies(pendingReplies, actorId);
327
- yield* Scope.close(stateScopeRef.current, Exit.void);
328
- yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
329
- return;
330
+ for (const entry of drained) {
331
+ const drain = yield* processQueued(entry);
332
+ if (drain.shouldStop) {
333
+ yield* Ref.set(stoppedRef, true);
334
+ settlePostponedBuffer(postponed, pendingReplies, actorId);
335
+ yield* settlePendingReplies(pendingReplies, actorId);
336
+ yield* Scope.close(stateScopeRef.current, Exit.void);
337
+ yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
338
+ return;
339
+ }
340
+ if (drain.stateChanged) drainTriggered = true;
330
341
  }
331
342
  }
332
343
  }
@@ -482,25 +493,7 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
482
493
  if (MutableHashMap.has(actorsMap, id)) return yield* new DuplicateActorError({ actorId: id });
483
494
  return yield* registerActor(id, yield* createActor(id, built._inner));
484
495
  });
485
- const spawnPersistent = Effect.fn("effect-machine.actorSystem.spawnPersistent")(function* (id, persistentMachine) {
486
- if (MutableHashMap.has(actorsMap, id)) return yield* new DuplicateActorError({ actorId: id });
487
- const adapter = yield* PersistenceAdapterTag;
488
- const maybeSnapshot = yield* adapter.loadSnapshot(id, persistentMachine.persistence.stateSchema);
489
- return yield* registerActor(id, yield* createPersistentActor(id, persistentMachine, maybeSnapshot, yield* adapter.loadEvents(id, persistentMachine.persistence.eventSchema, Option.isSome(maybeSnapshot) ? maybeSnapshot.value.version : void 0)));
490
- });
491
- const spawnImpl = Effect.fn("effect-machine.actorSystem.spawn")(function* (id, machine) {
492
- if (isPersistentMachine(machine)) return yield* spawnPersistent(id, machine);
493
- return yield* spawnRegular(id, machine);
494
- });
495
- function spawn(id, machine) {
496
- return withSpawnGate(spawnImpl(id, machine));
497
- }
498
- const restoreImpl = Effect.fn("effect-machine.actorSystem.restore")(function* (id, persistentMachine) {
499
- const maybeActor = yield* restorePersistentActor(id, persistentMachine);
500
- if (Option.isSome(maybeActor)) yield* registerActor(id, maybeActor.value);
501
- return maybeActor;
502
- });
503
- const restore = (id, persistentMachine) => withSpawnGate(restoreImpl(id, persistentMachine));
496
+ const spawn = (id, machine) => withSpawnGate(spawnRegular(id, machine));
504
497
  const get = Effect.fn("effect-machine.actorSystem.get")(function* (id) {
505
498
  return yield* Effect.sync(() => MutableHashMap.get(actorsMap, id));
506
499
  });
@@ -517,55 +510,8 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
517
510
  yield* actor.stop;
518
511
  return true;
519
512
  });
520
- const listPersisted = Effect.fn("effect-machine.actorSystem.listPersisted")(function* () {
521
- const adapter = yield* PersistenceAdapterTag;
522
- if (adapter.listActors === void 0) return [];
523
- return yield* adapter.listActors();
524
- });
525
- const restoreMany = Effect.fn("effect-machine.actorSystem.restoreMany")(function* (ids, persistentMachine) {
526
- const restored = [];
527
- const failed = [];
528
- for (const id of ids) {
529
- if (MutableHashMap.has(actorsMap, id)) continue;
530
- const result = yield* Effect.either(restore(id, persistentMachine));
531
- if (result._tag === "Left") failed.push({
532
- id,
533
- error: result.left
534
- });
535
- else if (Option.isSome(result.right)) restored.push(result.right.value);
536
- else failed.push({
537
- id,
538
- error: new PersistenceError({
539
- operation: "restore",
540
- actorId: id,
541
- message: "No persisted state found"
542
- })
543
- });
544
- }
545
- return {
546
- restored,
547
- failed
548
- };
549
- });
550
- const restoreAll = Effect.fn("effect-machine.actorSystem.restoreAll")(function* (persistentMachine, options) {
551
- const adapter = yield* PersistenceAdapterTag;
552
- if (adapter.listActors === void 0) return {
553
- restored: [],
554
- failed: []
555
- };
556
- const machineType = persistentMachine.persistence.machineType;
557
- if (machineType === void 0) return yield* new PersistenceError({
558
- operation: "restoreAll",
559
- actorId: "*",
560
- message: "restoreAll requires explicit machineType in persistence config"
561
- });
562
- let filtered = (yield* adapter.listActors()).filter((meta) => meta.machineType === machineType);
563
- if (options?.filter !== void 0) filtered = filtered.filter(options.filter);
564
- return yield* restoreMany(filtered.map((meta) => meta.id), persistentMachine);
565
- });
566
513
  return ActorSystem.of({
567
514
  spawn,
568
- restore,
569
515
  get,
570
516
  stop,
571
517
  events: Stream.fromPubSub(eventPubSub),
@@ -581,10 +527,7 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
581
527
  return () => {
582
528
  eventListeners.delete(fn);
583
529
  };
584
- },
585
- listPersisted,
586
- restoreMany,
587
- restoreAll
530
+ }
588
531
  });
589
532
  });
590
533
  /**
@@ -1,13 +1,9 @@
1
1
  import { EffectHandlers, EffectSlot, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers, GuardSlot, GuardSlots, GuardsDef, GuardsSchema, MachineContext, Slot } from "./slot.js";
2
2
  import { Event, MachineEventSchema, MachineStateSchema, State } from "./schema.js";
3
- import { PersistenceConfig, PersistentMachine, isPersistentMachine } from "./persistence/persistent-machine.js";
4
3
  import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
5
4
  import { ProcessEventResult } from "./internal/transition.js";
6
- import { PersistentActorRef, createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
7
- import { ActorMetadata, PersistedEvent, PersistenceAdapter, PersistenceAdapterTag, PersistenceError, RestoreFailure, RestoreResult, Snapshot, VersionConflictError } from "./persistence/adapter.js";
8
- import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
9
- import { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, ProvideHandlers, SpawnEffect, StateHandlerContext, TaskOptions, Transition, machine_d_exports } from "./machine.js";
10
- import { ActorRef, ActorRefSync, ActorSystem, Default, SystemEvent, SystemEventListener } from "./actor.js";
5
+ import { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, ProvideHandlers, SpawnEffect, StateHandlerContext, TaskOptions, Transition, machine_d_exports } from "./machine.js";
6
+ import { ActorRef, ActorRefSync, ActorSystem, Default, SystemEvent, SystemEventListener, TransitionInfo } from "./actor.js";
11
7
  import { SimulationResult, TestHarness, TestHarnessOptions, assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
12
8
  import { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, InspectionEvent, Inspector, InspectorHandler, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
13
- export { type ActorMetadata, type ActorRef, type ActorRefSync, ActorStoppedError, type ActorSystem, Default as ActorSystemDefault, ActorSystem as ActorSystemService, type AnyInspectionEvent, AssertionError, type BackgroundEffect, type BuiltMachine, DuplicateActorError, type EffectEvent, type EffectSlots, type EffectsDef, type EffectsSchema, type ErrorEvent, Event, type EventReceivedEvent, type GuardHandlers, type GuardSlot, type GuardSlots, type GuardsDef, type GuardsSchema, type HandlerContext, InMemoryPersistenceAdapter, 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, type PersistOptions, type PersistedEvent, type PersistenceAdapter, PersistenceAdapterTag, type PersistenceConfig, PersistenceError, type PersistentActorRef, type PersistentMachine, type ProcessEventResult, type ProvideHandlers, ProvisionValidationError, type RestoreFailure, type RestoreResult, type SimulationResult, Slot, type EffectHandlers as SlotEffectHandlers, type EffectSlot as SlotEffectSlot, SlotProvisionError, type Snapshot, type SpawnEffect, type SpawnEvent, State, type StateHandlerContext, type StopEvent, type SystemEvent, type SystemEventListener, type TaskEvent, type TaskOptions, type TestHarness, type TestHarnessOptions, type TracingInspectorOptions, type Transition, type TransitionEvent, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, makeInspectorEffect, restorePersistentActor, simulate, tracingInspector };
9
+ export { type ActorRef, type ActorRefSync, ActorStoppedError, type ActorSystem, Default as ActorSystemDefault, ActorSystem as ActorSystemService, type AnyInspectionEvent, AssertionError, type BackgroundEffect, type BuiltMachine, DuplicateActorError, type EffectEvent, type EffectSlots, type EffectsDef, type EffectsSchema, type ErrorEvent, Event, type EventReceivedEvent, type GuardHandlers, type GuardSlot, type GuardSlots, type GuardsDef, type GuardsSchema, type HandlerContext, 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, type ProcessEventResult, type ProvideHandlers, ProvisionValidationError, type SimulationResult, Slot, type EffectHandlers as SlotEffectHandlers, type EffectSlot as SlotEffectSlot, SlotProvisionError, type SpawnEffect, type SpawnEvent, State, type StateHandlerContext, type StopEvent, type SystemEvent, type SystemEventListener, type TaskEvent, type TaskOptions, type TestHarness, type TestHarnessOptions, type TracingInspectorOptions, type Transition, type TransitionEvent, type TransitionInfo, UnprovidedSlotsError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
package/v3/dist/index.js CHANGED
@@ -1,13 +1,8 @@
1
1
  import { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
2
2
  import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
3
- import { isPersistentMachine } from "./persistence/persistent-machine.js";
4
3
  import { Slot } from "./slot.js";
5
4
  import { machine_exports } from "./machine.js";
6
- import { PersistenceAdapterTag, PersistenceError, VersionConflictError } from "./persistence/adapter.js";
7
- import { createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
8
5
  import { ActorSystem, Default } from "./actor.js";
9
6
  import { Event, State } from "./schema.js";
10
7
  import { assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
11
- import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
12
- import "./persistence/index.js";
13
- export { ActorStoppedError, Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, InMemoryPersistenceAdapter, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceAdapterTag, PersistenceError, ProvisionValidationError, Slot, SlotProvisionError, State, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, makeInspectorEffect, restorePersistentActor, simulate, tracingInspector };
8
+ export { ActorStoppedError, Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, MissingSchemaError, NoReplyError, ProvisionValidationError, Slot, SlotProvisionError, State, UnprovidedSlotsError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createTestHarness, makeInspector, makeInspectorEffect, simulate, tracingInspector };
@@ -33,17 +33,13 @@ const isEffect = (value) => typeof value === "object" && value !== null && Effec
33
33
  */
34
34
  const stubSystem = {
35
35
  spawn: () => Effect.die("spawn not supported in stub system"),
36
- restore: () => Effect.die("restore not supported in stub system"),
37
36
  get: () => Effect.die("get not supported in stub system"),
38
37
  stop: () => Effect.die("stop not supported in stub system"),
39
38
  events: Stream.empty,
40
39
  get actors() {
41
40
  return /* @__PURE__ */ new Map();
42
41
  },
43
- subscribe: () => () => {},
44
- listPersisted: () => Effect.die("listPersisted not supported in stub system"),
45
- restoreMany: () => Effect.die("restoreMany not supported in stub system"),
46
- restoreAll: () => Effect.die("restoreAll not supported in stub system")
42
+ subscribe: () => () => {}
47
43
  };
48
44
  //#endregion
49
45
  export { INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, getTag, isEffect, stubSystem };
@@ -2,15 +2,14 @@ import { EffectHandlers, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers,
2
2
  import { TransitionResult } from "./internal/utils.js";
3
3
  import { BrandedEvent, BrandedState, TaggedOrConstructor } from "./internal/brands.js";
4
4
  import { MachineEventSchema, MachineStateSchema, VariantsUnion } from "./schema.js";
5
- import { PersistenceConfig, PersistentMachine } from "./persistence/persistent-machine.js";
6
5
  import { DuplicateActorError } from "./errors.js";
7
6
  import { findTransitions } from "./internal/transition.js";
8
7
  import { ActorRef, ActorSystem } from "./actor.js";
9
- import { Cause, Context, Duration, Effect, Schedule, Schema, Scope } from "effect";
8
+ import { Cause, Context, Duration, Effect, Schema, Scope } from "effect";
10
9
 
11
10
  //#region src/machine.d.ts
12
11
  declare namespace machine_d_exports {
13
- export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, PersistenceConfig, PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, make, spawn };
12
+ export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, make, replay, spawn };
14
13
  }
15
14
  /**
16
15
  * Self reference for sending events back to the machine
@@ -75,14 +74,6 @@ interface SpawnEffect<State, Event, ED extends EffectsDef, R> {
75
74
  interface BackgroundEffect<State, Event, ED extends EffectsDef, R> {
76
75
  readonly handler: StateEffectHandler<State, Event, ED, R>;
77
76
  }
78
- /** Options for `persist` */
79
- interface PersistOptions {
80
- readonly snapshotSchedule: Schedule.Schedule<unknown, {
81
- readonly _tag: string;
82
- }>;
83
- readonly journalEvents: boolean;
84
- readonly machineType?: string;
85
- }
86
77
  interface TaskOptions<State, Event, ED extends EffectsDef, A, E1, ES, EF> {
87
78
  readonly onSuccess: (value: A, ctx: StateHandlerContext<State, Event, ED>) => ES;
88
79
  readonly onFailure?: (cause: Cause.Cause<E1>, ctx: StateHandlerContext<State, Event, ED>) => EF;
@@ -137,11 +128,6 @@ declare class BuiltMachine<State, Event, R = never> {
137
128
  /** @internal */
138
129
  constructor(machine: Machine<State, Event, R, any, any, any, any>);
139
130
  get initial(): State;
140
- persist(config: PersistOptions): PersistentMachine<State & {
141
- readonly _tag: string;
142
- }, Event & {
143
- readonly _tag: string;
144
- }, R>;
145
131
  }
146
132
  /**
147
133
  * Machine definition with fluent builder API.
@@ -322,12 +308,6 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
322
308
  * - Machines without slots: call with no arguments.
323
309
  */
324
310
  build<R2 = never>(...args: HasSlots<GD, EFD> extends true ? [handlers: ProvideHandlers<State, Event, GD, EFD, R2>] : [handlers?: ProvideHandlers<State, Event, GD, EFD, R2>]): BuiltMachine<State, Event, R | NormalizeR<R2>>;
325
- /** @internal Persist from raw Machine — prefer BuiltMachine.persist() */
326
- persist(config: PersistOptions): PersistentMachine<State & {
327
- readonly _tag: string;
328
- }, Event & {
329
- readonly _tag: string;
330
- }, R>;
331
311
  static make<SD extends Record<string, Schema.Struct.Fields>, ED extends Record<string, Schema.Struct.Fields>, S extends BrandedState, E extends BrandedEvent, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(config: MakeConfig<SD, ED, S, E, GD, EFD>): Machine<S, E, never, SD, ED, GD, EFD>;
332
312
  }
333
313
  declare class TransitionScope<State, Event, R, _SD extends Record<string, Schema.Struct.Fields>, _ED extends Record<string, Schema.Struct.Fields>, GD extends GuardsDef, EFD extends EffectsDef, SelectedState extends VariantsUnion<_SD> & BrandedState> {
@@ -338,17 +318,33 @@ declare class TransitionScope<State, Event, R, _SD extends Record<string, Schema
338
318
  reenter<NE extends VariantsUnion<_ED> & BrandedEvent, RS extends VariantsUnion<_SD> & BrandedState>(event: TaggedOrConstructor<NE>, handler: TransitionHandler<SelectedState, NE, RS, GD, EFD, never>): TransitionScope<State, Event, R, _SD, _ED, GD, EFD, SelectedState>;
339
319
  }
340
320
  declare const make: typeof Machine.make;
341
- declare const spawn: {
342
- <S extends {
343
- readonly _tag: string;
344
- }, E extends {
345
- readonly _tag: string;
346
- }, R>(machine: BuiltMachine<S, E, R>): Effect.Effect<ActorRef<S, E>, never, R>;
347
- <S extends {
348
- readonly _tag: string;
349
- }, E extends {
350
- readonly _tag: string;
351
- }, R>(machine: BuiltMachine<S, E, R>, id: string): Effect.Effect<ActorRef<S, E>, never, R>;
352
- };
321
+ /**
322
+ * Spawn an actor from a built machine.
323
+ *
324
+ * Options:
325
+ * - `id` — custom actor ID (default: random)
326
+ * - `hydrate` restore from a previously-saved state snapshot.
327
+ * The actor starts in the hydrated state and re-runs spawn effects
328
+ * for that state (timers, scoped resources, etc.). Transition history
329
+ * is not replayed — only the current state's entry effects run.
330
+ *
331
+ * Persistence is composed in userland by observing `actor.changes`
332
+ * and saving snapshots to your own storage.
333
+ */
334
+ declare const spawn: <S extends {
335
+ readonly _tag: string;
336
+ }, E extends {
337
+ readonly _tag: string;
338
+ }, R>(machine: BuiltMachine<S, E, R>, idOrOptions?: string | {
339
+ id?: string;
340
+ hydrate?: S;
341
+ }) => Effect.Effect<ActorRef<S, E>, never, R>;
342
+ declare const replay: <S extends {
343
+ readonly _tag: string;
344
+ }, E extends {
345
+ readonly _tag: string;
346
+ }, R>(machine: BuiltMachine<S, E, R>, events: ReadonlyArray<E>, options?: {
347
+ from?: S;
348
+ }) => Effect.Effect<S, never, R>;
353
349
  //#endregion
354
- export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, type PersistenceConfig, type PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, machine_d_exports, make, spawn };
350
+ export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, machine_d_exports, make, replay, spawn };
@@ -1,11 +1,10 @@
1
1
  import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
2
  import { Inspector } from "./inspection.js";
3
- import { getTag } from "./internal/utils.js";
3
+ import { getTag, stubSystem } from "./internal/utils.js";
4
4
  import { ProvisionValidationError, SlotProvisionError } from "./errors.js";
5
- import { persist } from "./persistence/persistent-machine.js";
6
5
  import { emitWithTimestamp } from "./internal/inspection.js";
7
6
  import { MachineContextTag } from "./slot.js";
8
- import { findTransitions, invalidateIndex } from "./internal/transition.js";
7
+ import { findTransitions, invalidateIndex, resolveTransition, runTransitionHandler, shouldPostpone } from "./internal/transition.js";
9
8
  import { createActor } from "./actor.js";
10
9
  import { Cause, Effect, Exit, Option, Scope } from "effect";
11
10
  //#region src/machine.ts
@@ -14,6 +13,7 @@ var machine_exports = /* @__PURE__ */ __exportAll({
14
13
  Machine: () => Machine,
15
14
  findTransitions: () => findTransitions,
16
15
  make: () => make,
16
+ replay: () => replay,
17
17
  spawn: () => spawn
18
18
  });
19
19
  const emitTaskInspection = (input) => Effect.flatMap(Effect.serviceOptional(Inspector).pipe(Effect.option), (inspector) => Option.isNone(inspector) ? Effect.void : emitWithTimestamp(inspector.value, (timestamp) => ({
@@ -42,9 +42,6 @@ var BuiltMachine = class {
42
42
  get initial() {
43
43
  return this._inner.initial;
44
44
  }
45
- persist(config) {
46
- return this._inner.persist(config);
47
- }
48
45
  };
49
46
  /**
50
47
  * Machine definition with fluent builder API.
@@ -381,10 +378,6 @@ var Machine = class Machine {
381
378
  }
382
379
  return new BuiltMachine(this);
383
380
  }
384
- /** @internal Persist from raw Machine — prefer BuiltMachine.persist() */
385
- persist(config) {
386
- return persist(config)(this);
387
- }
388
381
  static make(config) {
389
382
  return new Machine(config.initial, config.state, config.event, config.guards, config.effects);
390
383
  }
@@ -404,11 +397,68 @@ var TransitionScope = class {
404
397
  }
405
398
  };
406
399
  const make = Machine.make;
407
- const spawn = Effect.fn("effect-machine.spawn")(function* (built, id) {
408
- const actor = yield* createActor(id ?? `actor-${Math.random().toString(36).slice(2)}`, built._inner);
400
+ /**
401
+ * Spawn an actor from a built machine.
402
+ *
403
+ * Options:
404
+ * - `id` — custom actor ID (default: random)
405
+ * - `hydrate` — restore from a previously-saved state snapshot.
406
+ * The actor starts in the hydrated state and re-runs spawn effects
407
+ * for that state (timers, scoped resources, etc.). Transition history
408
+ * is not replayed — only the current state's entry effects run.
409
+ *
410
+ * Persistence is composed in userland by observing `actor.changes`
411
+ * and saving snapshots to your own storage.
412
+ */
413
+ const spawn = Effect.fn("effect-machine.spawn")(function* (built, idOrOptions) {
414
+ const opts = typeof idOrOptions === "string" ? { id: idOrOptions } : idOrOptions;
415
+ const actor = yield* createActor(opts?.id ?? `actor-${Math.random().toString(36).slice(2)}`, built._inner, { initialState: opts?.hydrate });
409
416
  const maybeScope = yield* Effect.serviceOption(Scope.Scope);
410
417
  if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, actor.stop);
411
418
  return actor;
412
419
  });
420
+ const replay = Effect.fn("effect-machine.replay")(function* (built, events, options) {
421
+ const machine = built._inner;
422
+ let state = options?.from ?? machine.initial;
423
+ const hasPostponeRules = machine.postponeRules.length > 0;
424
+ const postponed = [];
425
+ const dummySend = Effect.fn("effect-machine.replay.send")((_event) => Effect.void);
426
+ const self = {
427
+ send: dummySend,
428
+ cast: dummySend,
429
+ spawn: () => Effect.die("spawn not supported in replay")
430
+ };
431
+ for (const event of events) {
432
+ if (machine.finalStates.has(state._tag)) break;
433
+ if (hasPostponeRules && shouldPostpone(machine, state._tag, event._tag)) {
434
+ postponed.push(event);
435
+ continue;
436
+ }
437
+ const transition = resolveTransition(machine, state, event);
438
+ if (transition !== void 0) {
439
+ const result = yield* runTransitionHandler(machine, transition, state, event, self, stubSystem, "replay");
440
+ const previousTag = state._tag;
441
+ state = result.newState;
442
+ if ((state._tag !== previousTag || transition.reenter === true) && postponed.length > 0) {
443
+ let drainTag = previousTag;
444
+ while (state._tag !== drainTag && postponed.length > 0) {
445
+ if (machine.finalStates.has(state._tag)) break;
446
+ drainTag = state._tag;
447
+ const drained = postponed.splice(0);
448
+ for (const postponedEvent of drained) {
449
+ if (machine.finalStates.has(state._tag)) break;
450
+ if (shouldPostpone(machine, state._tag, postponedEvent._tag)) {
451
+ postponed.push(postponedEvent);
452
+ continue;
453
+ }
454
+ const pTransition = resolveTransition(machine, state, postponedEvent);
455
+ if (pTransition !== void 0) state = (yield* runTransitionHandler(machine, pTransition, state, postponedEvent, self, stubSystem, "replay")).newState;
456
+ }
457
+ }
458
+ }
459
+ }
460
+ }
461
+ return state;
462
+ });
413
463
  //#endregion
414
- export { BuiltMachine, Machine, findTransitions, machine_exports, make, spawn };
464
+ export { BuiltMachine, Machine, findTransitions, machine_exports, make, replay, spawn };