effect-machine 0.11.0 → 0.12.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 (58) hide show
  1. package/dist/actor.d.ts +10 -4
  2. package/dist/actor.js +33 -5
  3. package/dist/cluster/adapters/in-memory.d.ts +28 -0
  4. package/dist/cluster/adapters/in-memory.js +79 -0
  5. package/dist/cluster/entity-actor-ref.d.ts +56 -0
  6. package/dist/cluster/entity-actor-ref.js +33 -0
  7. package/dist/cluster/entity-machine.d.ts +31 -49
  8. package/dist/cluster/entity-machine.js +167 -52
  9. package/dist/cluster/index.d.ts +5 -2
  10. package/dist/cluster/index.js +4 -1
  11. package/dist/cluster/persistence.d.ts +49 -0
  12. package/dist/cluster/persistence.js +18 -0
  13. package/dist/cluster/to-entity.d.ts +9 -3
  14. package/dist/cluster/to-entity.js +16 -4
  15. package/dist/errors.d.ts +12 -1
  16. package/dist/errors.js +8 -1
  17. package/dist/index.d.ts +3 -2
  18. package/dist/internal/brands.d.ts +14 -1
  19. package/dist/internal/runtime.d.ts +67 -0
  20. package/dist/internal/runtime.js +248 -0
  21. package/dist/internal/transition.d.ts +5 -0
  22. package/dist/internal/transition.js +15 -3
  23. package/dist/internal/utils.d.ts +42 -6
  24. package/dist/internal/utils.js +27 -1
  25. package/dist/machine.d.ts +26 -13
  26. package/dist/machine.js +14 -3
  27. package/dist/schema.d.ts +35 -34
  28. package/dist/schema.js +32 -3
  29. package/dist/testing.js +4 -2
  30. package/package.json +3 -3
  31. package/v3/dist/actor.d.ts +4 -3
  32. package/v3/dist/actor.js +15 -3
  33. package/v3/dist/cluster/adapters/in-memory.d.ts +15 -0
  34. package/v3/dist/cluster/adapters/in-memory.js +62 -0
  35. package/v3/dist/cluster/entity-actor-ref.d.ts +49 -0
  36. package/v3/dist/cluster/entity-actor-ref.js +19 -0
  37. package/v3/dist/cluster/entity-machine.d.ts +34 -49
  38. package/v3/dist/cluster/entity-machine.js +134 -50
  39. package/v3/dist/cluster/index.d.ts +5 -2
  40. package/v3/dist/cluster/index.js +4 -1
  41. package/v3/dist/cluster/persistence.d.ts +48 -0
  42. package/v3/dist/cluster/persistence.js +14 -0
  43. package/v3/dist/cluster/to-entity.d.ts +5 -2
  44. package/v3/dist/cluster/to-entity.js +12 -4
  45. package/v3/dist/errors.d.ts +16 -1
  46. package/v3/dist/errors.js +8 -1
  47. package/v3/dist/index.d.ts +3 -2
  48. package/v3/dist/internal/brands.d.ts +15 -1
  49. package/v3/dist/internal/runtime.d.ts +65 -0
  50. package/v3/dist/internal/runtime.js +236 -0
  51. package/v3/dist/internal/transition.d.ts +5 -0
  52. package/v3/dist/internal/transition.js +15 -3
  53. package/v3/dist/internal/utils.d.ts +42 -6
  54. package/v3/dist/internal/utils.js +27 -1
  55. package/v3/dist/machine.d.ts +19 -13
  56. package/v3/dist/machine.js +10 -2
  57. package/v3/dist/schema.d.ts +35 -34
  58. package/v3/dist/schema.js +29 -3
package/dist/actor.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { EffectsDef, GuardsDef, MachineContext } from "./slot.js";
2
+ import { ExtractReply, ReplyTypeBrand } from "./internal/brands.js";
2
3
  import { ActorStoppedError, DuplicateActorError, NoReplyError } from "./errors.js";
3
4
  import { ProcessEventError, ProcessEventHooks, ProcessEventResult, processEventCore, resolveTransition, runSpawnEffects } from "./internal/transition.js";
4
5
  import { BuiltMachine, Machine, MachineRef } from "./machine.js";
@@ -59,11 +60,11 @@ interface ActorRef<State extends {
59
60
  */
60
61
  readonly call: (event: Event) => Effect.Effect<ProcessEventResult<State>>;
61
62
  /**
62
- * Typed request-reply. Event is processed through the queue; caller gets
63
- * the domain value returned by the handler's `reply` field.
63
+ * Typed request-reply. Accepts only events with a reply schema
64
+ * (defined via `Event.reply()`). Return type is inferred from the schema.
64
65
  * Fails with NoReplyError if the handler doesn't provide a reply.
65
66
  */
66
- readonly ask: <R>(event: Event) => Effect.Effect<R, NoReplyError | ActorStoppedError>;
67
+ readonly ask: <E extends Event & ReplyTypeBrand<unknown>>(event: E) => Effect.Effect<ExtractReply<E>, NoReplyError | ActorStoppedError>;
67
68
  /** Observable state. */
68
69
  readonly state: SubscriptionRef.SubscriptionRef<State>;
69
70
  /** Stop the actor gracefully. */
@@ -202,9 +203,14 @@ declare const createActor: <S extends {
202
203
  } | 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
204
  /** Fail all pending call/ask Deferreds with ActorStoppedError. Safe to call multiple times. */
204
205
  declare const settlePendingReplies: (pendingReplies: Set<Deferred.Deferred<unknown, unknown>>, actorId: string) => Effect.Effect<void, never, never>;
206
+ /**
207
+ * Create an ActorSystem instance. Must be run in a Scope.
208
+ * @internal — use Default layer for normal usage
209
+ */
210
+ declare const makeSystem: () => Effect.Effect<ActorSystem, never, Scope.Scope>;
205
211
  /**
206
212
  * Default ActorSystem layer
207
213
  */
208
214
  declare const Default: Layer.Layer<ActorSystem, never, never>;
209
215
  //#endregion
210
- export { ActorRef, ActorRefSync, ActorSystem, Default, Listeners, type ProcessEventError, type ProcessEventHooks, type ProcessEventResult, QueuedEvent, SystemEvent, SystemEventListener, TransitionInfo, buildActorRefCore, createActor, notifyListeners, processEventCore, resolveTransition, runSpawnEffects, settlePendingReplies };
216
+ export { ActorRef, ActorRefSync, ActorSystem, Default, Listeners, type ProcessEventError, type ProcessEventHooks, type ProcessEventResult, QueuedEvent, SystemEvent, SystemEventListener, TransitionInfo, buildActorRefCore, createActor, makeSystem, notifyListeners, processEventCore, resolveTransition, runSpawnEffects, settlePendingReplies };
package/dist/actor.js CHANGED
@@ -3,7 +3,7 @@ import { INTERNAL_INIT_EVENT } from "./internal/utils.js";
3
3
  import { ActorStoppedError, DuplicateActorError, NoReplyError } from "./errors.js";
4
4
  import { emitWithTimestamp } from "./internal/inspection.js";
5
5
  import { processEventCore, resolveTransition, runSpawnEffects, shouldPostpone } from "./internal/transition.js";
6
- import { Cause, Deferred, Effect, Exit, Fiber, Layer, MutableHashMap, Option, PubSub, Queue, Ref, Scope, Semaphore, ServiceMap, Stream, SubscriptionRef } from "effect";
6
+ import { Cause, Deferred, Effect, Exit, Fiber, Layer, MutableHashMap, Option, PubSub, Queue, Ref, Schema, Scope, Semaphore, ServiceMap, Stream, SubscriptionRef } from "effect";
7
7
  //#region src/actor.ts
8
8
  /**
9
9
  * Actor system: spawning, lifecycle, and event processing.
@@ -163,6 +163,7 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
163
163
  const eventQueue = yield* Queue.unbounded();
164
164
  const stoppedRef = yield* Ref.make(false);
165
165
  const childrenMap = /* @__PURE__ */ new Map();
166
+ const deferredReplyRef = { current: void 0 };
166
167
  const selfSend = Effect.fn("effect-machine.actor.self.send")(function* (event) {
167
168
  if (yield* Ref.get(stoppedRef)) return;
168
169
  yield* Queue.offer(eventQueue, {
@@ -181,6 +182,15 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
181
182
  childrenMap.delete(childId);
182
183
  }));
183
184
  return child;
185
+ }),
186
+ reply: (value) => Effect.sync(() => {
187
+ const deferred = deferredReplyRef.current;
188
+ if (deferred !== void 0) {
189
+ deferredReplyRef.current = void 0;
190
+ Effect.runFork(Deferred.succeed(deferred, value));
191
+ return true;
192
+ }
193
+ return false;
184
194
  })
185
195
  };
186
196
  yield* Effect.annotateCurrentSpan("effect_machine.actor.initial_state", initial._tag);
@@ -230,7 +240,7 @@ const createActor = Effect.fn("effect-machine.actor.spawn")(function* (id, machi
230
240
  }
231
241
  const pendingReplies = /* @__PURE__ */ new Set();
232
242
  const transitionsPubSub = yield* PubSub.unbounded();
233
- const loopFiber = yield* Effect.forkDetach(eventLoop(machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, id, inspectorValue, system, pendingReplies, transitionsPubSub));
243
+ const loopFiber = yield* Effect.forkDetach(eventLoop(machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, id, inspectorValue, system, pendingReplies, transitionsPubSub, deferredReplyRef));
234
244
  return buildActorRefCore(id, machine, stateRef, eventQueue, stoppedRef, listeners, Effect.gen(function* () {
235
245
  const finalState = yield* SubscriptionRef.get(stateRef);
236
246
  yield* emitWithTimestamp(inspectorValue, (timestamp) => ({
@@ -258,7 +268,7 @@ const settlePendingReplies = (pendingReplies, actorId) => Effect.sync(() => {
258
268
  * Includes postpone buffer — events matching postpone rules are buffered
259
269
  * and drained after state tag changes (gen_statem semantics).
260
270
  */
261
- const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, actorId, inspector, system, pendingReplies, transitionsPubSub) {
271
+ const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine, stateRef, eventQueue, stoppedRef, self, listeners, backgroundFibers, stateScopeRef, actorId, inspector, system, pendingReplies, transitionsPubSub, deferredReplyRef) {
262
272
  const postponed = [];
263
273
  const hasPostponeRules = machine.postponeRules.length > 0;
264
274
  const processQueued = Effect.fn("effect-machine.actor.processQueued")(function* (queued) {
@@ -274,6 +284,7 @@ const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine
274
284
  lifecycleRan: false,
275
285
  isFinal: false,
276
286
  hasReply: false,
287
+ deferReply: false,
277
288
  reply: void 0,
278
289
  postponed: true
279
290
  };
@@ -294,7 +305,19 @@ const eventLoop = Effect.fn("effect-machine.actor.eventLoop")(function* (machine
294
305
  yield* Deferred.succeed(queued.reply, result);
295
306
  break;
296
307
  case "ask":
297
- if (result.hasReply) yield* Deferred.succeed(queued.reply, result.reply);
308
+ if (result.hasReply) {
309
+ const replySchema = machine._replySchemas?.get(event._tag);
310
+ if (replySchema !== void 0) {
311
+ let decoded;
312
+ try {
313
+ decoded = Schema.decodeUnknownSync(replySchema)(result.reply);
314
+ } catch (decodeError) {
315
+ yield* Deferred.die(queued.reply, decodeError);
316
+ return yield* Effect.die(decodeError);
317
+ }
318
+ yield* Deferred.succeed(queued.reply, decoded);
319
+ } else yield* Deferred.succeed(queued.reply, result.reply);
320
+ } else if (result.deferReply) deferredReplyRef.current = queued.reply;
298
321
  else yield* Deferred.fail(queued.reply, new NoReplyError({
299
322
  actorId,
300
323
  eventTag: event._tag
@@ -529,8 +552,13 @@ const make = Effect.fn("effect-machine.actorSystem.make")(function* () {
529
552
  });
530
553
  });
531
554
  /**
555
+ * Create an ActorSystem instance. Must be run in a Scope.
556
+ * @internal — use Default layer for normal usage
557
+ */
558
+ const makeSystem = make;
559
+ /**
532
560
  * Default ActorSystem layer
533
561
  */
534
562
  const Default = Layer.effect(ActorSystem, make());
535
563
  //#endregion
536
- export { ActorSystem, Default, buildActorRefCore, createActor, notifyListeners, processEventCore, resolveTransition, runSpawnEffects, settlePendingReplies };
564
+ export { ActorSystem, Default, buildActorRefCore, createActor, makeSystem, notifyListeners, processEventCore, resolveTransition, runSpawnEffects, settlePendingReplies };
@@ -0,0 +1,28 @@
1
+ import { PersistedEvent, PersistenceAdapter, Snapshot } from "../persistence.js";
2
+ import { Effect, Layer, Ref } from "effect";
3
+
4
+ //#region src/cluster/adapters/in-memory.d.ts
5
+ interface EntityStore {
6
+ snapshot: Snapshot<unknown> | undefined;
7
+ events: Array<PersistedEvent<unknown>>;
8
+ }
9
+ /**
10
+ * Create an in-memory persistence adapter.
11
+ *
12
+ * Returns a Layer providing `PersistenceAdapter` and a ref
13
+ * to the backing store for test assertions.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const { layer, storeRef } = yield* makeInMemoryPersistenceAdapter
18
+ * // Use layer to provide PersistenceAdapter
19
+ * // Inspect storeRef for test assertions
20
+ * ```
21
+ */
22
+ declare const makeInMemoryPersistenceAdapter: Effect.Effect<{
23
+ adapter: PersistenceAdapter;
24
+ storeRef: Ref.Ref<Map<string, EntityStore>>;
25
+ layer: Layer.Layer<PersistenceAdapter, never, never>;
26
+ }, never, never>;
27
+ //#endregion
28
+ export { makeInMemoryPersistenceAdapter };
@@ -0,0 +1,79 @@
1
+ import { VersionConflictError } from "../../errors.js";
2
+ import { PersistenceAdapter } from "../persistence.js";
3
+ import { Effect, Layer, Option, Ref } from "effect";
4
+ //#region src/cluster/adapters/in-memory.ts
5
+ /**
6
+ * In-memory persistence adapter for testing and development.
7
+ *
8
+ * Backed by a simple Map — state is lost on process exit.
9
+ * Supports both snapshot and journal strategies with proper
10
+ * version checking (CAS on appends, monotonic on snapshots).
11
+ *
12
+ * @module
13
+ */
14
+ const makeKey = (key) => `${key.entityType}/${key.entityId}`;
15
+ const getOrCreate = (store, key) => {
16
+ let entry = store.get(key);
17
+ if (entry === void 0) {
18
+ entry = {
19
+ snapshot: void 0,
20
+ events: []
21
+ };
22
+ store.set(key, entry);
23
+ }
24
+ return entry;
25
+ };
26
+ /**
27
+ * Create an in-memory persistence adapter.
28
+ *
29
+ * Returns a Layer providing `PersistenceAdapter` and a ref
30
+ * to the backing store for test assertions.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const { layer, storeRef } = yield* makeInMemoryPersistenceAdapter
35
+ * // Use layer to provide PersistenceAdapter
36
+ * // Inspect storeRef for test assertions
37
+ * ```
38
+ */
39
+ const makeInMemoryPersistenceAdapter = Effect.gen(function* () {
40
+ const store = /* @__PURE__ */ new Map();
41
+ const storeRef = yield* Ref.make(store);
42
+ const adapter = {
43
+ saveSnapshot: (key, snapshot) => Effect.gen(function* () {
44
+ const entry = getOrCreate(yield* Ref.get(storeRef), makeKey(key));
45
+ if (entry.snapshot !== void 0 && snapshot.version < entry.snapshot.version) return yield* new VersionConflictError({
46
+ expected: snapshot.version,
47
+ actual: entry.snapshot.version
48
+ });
49
+ entry.snapshot = snapshot;
50
+ }),
51
+ loadSnapshot: (key) => Effect.gen(function* () {
52
+ const entry = (yield* Ref.get(storeRef)).get(makeKey(key));
53
+ return Option.fromNullishOr(entry?.snapshot);
54
+ }),
55
+ appendEvents: (key, events, expectedVersion) => Effect.gen(function* () {
56
+ const entry = getOrCreate(yield* Ref.get(storeRef), makeKey(key));
57
+ const lastEvent = entry.events[entry.events.length - 1];
58
+ const currentVersion = lastEvent !== void 0 ? lastEvent.version : 0;
59
+ if (currentVersion !== expectedVersion) return yield* new VersionConflictError({
60
+ expected: expectedVersion,
61
+ actual: currentVersion
62
+ });
63
+ for (const event of events) entry.events.push(event);
64
+ }),
65
+ loadEvents: (key, afterVersion) => Effect.gen(function* () {
66
+ const entry = (yield* Ref.get(storeRef)).get(makeKey(key));
67
+ if (entry === void 0) return [];
68
+ if (afterVersion === void 0) return entry.events;
69
+ return entry.events.filter((e) => e.version > afterVersion);
70
+ })
71
+ };
72
+ return {
73
+ adapter,
74
+ storeRef,
75
+ layer: Layer.succeed(PersistenceAdapter, adapter)
76
+ };
77
+ });
78
+ //#endregion
79
+ export { makeInMemoryPersistenceAdapter };
@@ -0,0 +1,56 @@
1
+ import { ExtractReply, ReplyTypeBrand } from "../internal/brands.js";
2
+ import { ActorStoppedError, NoReplyError } from "../errors.js";
3
+ import { EntityRpcs } from "./to-entity.js";
4
+ import { Effect, Stream } from "effect";
5
+ import { RpcClient } from "effect/unstable/rpc";
6
+
7
+ //#region src/cluster/entity-actor-ref.d.ts
8
+ /**
9
+ * Typed client wrapper for remote entity machines.
10
+ *
11
+ * Unlike local `ActorRef`, this communicates over cluster RPCs.
12
+ * Only operations that make sense over the network are exposed.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const ref = makeEntityActorRef(client, "order-123")
17
+ * yield* ref.send(OrderEvent.Ship({ trackingId: "abc" }))
18
+ * const state = yield* ref.snapshot
19
+ * yield* ref.waitFor((s) => s._tag === "Shipped")
20
+ * ```
21
+ */
22
+ interface EntityActorRef<State extends {
23
+ readonly _tag: string;
24
+ }, Event extends {
25
+ readonly _tag: string;
26
+ }> {
27
+ readonly entityId: string;
28
+ /** Send event. Returns new state after processing. */
29
+ readonly send: (event: Event) => Effect.Effect<State>;
30
+ /** Send event and get typed domain reply (via Event.reply() schema). */
31
+ readonly ask: <E extends Event & ReplyTypeBrand<unknown>>(event: E) => Effect.Effect<ExtractReply<E>, NoReplyError>;
32
+ /** Get current state. */
33
+ readonly snapshot: Effect.Effect<State>;
34
+ /** Stream of state changes (via WatchState streaming RPC). */
35
+ readonly watch: Stream.Stream<State>;
36
+ /** Wait for a state matching the predicate. Snapshots first, then watches stream. */
37
+ readonly waitFor: (predicate: (state: State) => boolean) => Effect.Effect<State, ActorStoppedError>;
38
+ }
39
+ /**
40
+ * Create an EntityActorRef from a RPC client.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const makeClient = yield* Entity.makeTestClient(entity, entityLayer)
45
+ * const client = yield* makeClient("order-123")
46
+ * const ref = makeEntityActorRef(client, "order-123")
47
+ * yield* ref.send(OrderEvent.Process)
48
+ * ```
49
+ */
50
+ declare const makeEntityActorRef: <State extends {
51
+ readonly _tag: string;
52
+ }, Event extends {
53
+ readonly _tag: string;
54
+ }, Rpcs extends EntityRpcs<any, any>[number]>(client: RpcClient.RpcClient<Rpcs>, entityId: string) => EntityActorRef<State, Event>;
55
+ //#endregion
56
+ export { EntityActorRef, makeEntityActorRef };
@@ -0,0 +1,33 @@
1
+ import { ActorStoppedError } from "../errors.js";
2
+ import { Effect, Option, Stream } from "effect";
3
+ //#region src/cluster/entity-actor-ref.ts
4
+ /**
5
+ * Create an EntityActorRef from a RPC client.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const makeClient = yield* Entity.makeTestClient(entity, entityLayer)
10
+ * const client = yield* makeClient("order-123")
11
+ * const ref = makeEntityActorRef(client, "order-123")
12
+ * yield* ref.send(OrderEvent.Process)
13
+ * ```
14
+ */
15
+ const makeEntityActorRef = (client, entityId) => {
16
+ const c = client;
17
+ return {
18
+ entityId,
19
+ send: (event) => c.Send({ event }),
20
+ ask: ((event) => c.Ask({ event })),
21
+ snapshot: c.GetState(),
22
+ watch: c.WatchState(),
23
+ waitFor: (predicate) => Effect.gen(function* () {
24
+ const current = yield* c.GetState();
25
+ if (predicate(current)) return current;
26
+ const result = yield* c.WatchState().pipe(Stream.filter(predicate), Stream.take(1), Stream.runHead);
27
+ if (Option.isSome(result)) return result.value;
28
+ return yield* new ActorStoppedError({ actorId: entityId });
29
+ })
30
+ };
31
+ };
32
+ //#endregion
33
+ export { makeEntityActorRef };
@@ -1,7 +1,7 @@
1
- import { EffectsDef, GuardsDef } from "../slot.js";
2
1
  import { ProcessEventHooks } from "../internal/transition.js";
3
2
  import { Machine } from "../machine.js";
4
- import { Layer } from "effect";
3
+ import { EntityPersistenceConfig } from "./persistence.js";
4
+ import { Duration, Layer, Schedule } from "effect";
5
5
  import { Entity } from "effect/unstable/cluster";
6
6
  import { Rpc } from "effect/unstable/rpc";
7
7
 
@@ -13,77 +13,59 @@ interface EntityMachineOptions<S, E> {
13
13
  /**
14
14
  * Initialize state from entity ID.
15
15
  * Called once when entity is first activated.
16
- *
17
- * @example
18
- * ```ts
19
- * EntityMachine.layer(OrderEntity, orderMachine, {
20
- * initializeState: (entityId) => OrderState.Pending({ orderId: entityId }),
21
- * })
22
- * ```
23
16
  */
24
17
  readonly initializeState?: (entityId: string) => S;
25
18
  /**
26
19
  * Optional hooks for inspection/tracing.
27
- * Called at specific points during event processing.
28
- *
29
- * @example
30
- * ```ts
31
- * EntityMachine.layer(OrderEntity, orderMachine, {
32
- * hooks: {
33
- * onTransition: (from, to, event) =>
34
- * Effect.log(`Transition: ${from._tag} -> ${to._tag}`),
35
- * onSpawnEffect: (state) =>
36
- * Effect.log(`Running spawn effects for ${state._tag}`),
37
- * onError: ({ phase, state }) =>
38
- * Effect.log(`Defect in ${phase} at ${state._tag}`),
39
- * },
40
- * })
41
- * ```
42
20
  */
43
21
  readonly hooks?: ProcessEventHooks<S, E>;
22
+ /**
23
+ * Maximum idle time before entity deactivation.
24
+ * Forwarded to Entity.toLayerQueue.
25
+ */
26
+ readonly maxIdleTime?: Duration.Input;
27
+ /**
28
+ * Mailbox capacity. Default: "unbounded".
29
+ * Forwarded to Entity.toLayerQueue.
30
+ */
31
+ readonly mailboxCapacity?: number | "unbounded";
32
+ /**
33
+ * Disable fatal defects (defects won't crash the entity activation).
34
+ * Forwarded to Entity.toLayerQueue.
35
+ */
36
+ readonly disableFatalDefects?: boolean;
37
+ /**
38
+ * Retry policy for defects (schedule for restarting after defect).
39
+ * Forwarded to Entity.toLayerQueue.
40
+ */
41
+ readonly defectRetryPolicy?: Schedule.Schedule<any, unknown>;
42
+ /**
43
+ * Persistence configuration. When set, requires PersistenceAdapter in R.
44
+ */
45
+ readonly persistence?: EntityPersistenceConfig;
44
46
  }
45
47
  /**
46
48
  * Create an Entity layer that wires a machine to handle RPC calls.
47
49
  *
48
- * The layer:
49
- * - Maintains state via Ref per entity instance
50
- * - Resolves transitions using the indexed lookup
51
- * - Evaluates guards in registration order
52
- * - Runs lifecycle effects (onEnter/spawn)
53
- * - Processes internal events from spawn effects
50
+ * Uses `Entity.toLayerQueue` for a single serialized mailbox per entity.
51
+ * The runtime kernel handles event processing, postpone, background effects,
52
+ * spawn effects, and final state detection.
54
53
  *
55
54
  * @example
56
55
  * ```ts
57
- * const OrderEntity = toEntity(orderMachine, {
58
- * type: "Order",
59
- * stateSchema: OrderState,
60
- * eventSchema: OrderEvent,
61
- * })
56
+ * const OrderEntity = toEntity(orderMachine, { type: "Order" })
62
57
  *
63
58
  * const OrderEntityLayer = EntityMachine.layer(OrderEntity, orderMachine, {
64
59
  * initializeState: (entityId) => OrderState.Pending({ orderId: entityId }),
65
60
  * })
66
- *
67
- * // Use in cluster
68
- * const program = Effect.gen(function* () {
69
- * const client = yield* ShardingClient.client(OrderEntity)
70
- * yield* client.Send("order-123", { event: OrderEvent.Ship({ trackingId: "abc" }) })
71
- * })
72
61
  * ```
73
62
  */
74
63
  declare const EntityMachine: {
75
- /**
76
- * Create a layer that wires a machine to an Entity.
77
- *
78
- * @param entity - Entity created via toEntity()
79
- * @param machine - Machine with all effects provided
80
- * @param options - Optional configuration (state initializer, inspection hooks)
81
- */
82
64
  layer: <S extends {
83
65
  readonly _tag: string;
84
66
  }, E extends {
85
67
  readonly _tag: string;
86
- }, R, GD extends GuardsDef, EFD extends EffectsDef, EntityType extends string, Rpcs extends Rpc.Any>(entity: Entity.Entity<EntityType, Rpcs>, machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, options?: EntityMachineOptions<S, E>) => Layer.Layer<never, never, R>;
68
+ }, R, EntityType extends string, Rpcs extends Rpc.Any>(entity: Entity.Entity<EntityType, Rpcs>, machine: Machine<S, E, R, any, any, any, any>, options?: EntityMachineOptions<S, E>) => Layer.Layer<never, never, R>;
87
69
  };
88
70
  //#endregion
89
71
  export { EntityMachine, EntityMachineOptions };