effect-machine 0.8.0 → 0.10.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 (76) hide show
  1. package/README.md +76 -16
  2. package/dist/_virtual/_rolldown/runtime.js +6 -11
  3. package/dist/actor.d.ts +58 -72
  4. package/dist/actor.js +166 -32
  5. package/dist/cluster/entity-machine.d.ts +0 -1
  6. package/dist/cluster/entity-machine.js +6 -6
  7. package/dist/cluster/index.js +1 -2
  8. package/dist/cluster/to-entity.js +1 -3
  9. package/dist/errors.d.ts +12 -1
  10. package/dist/errors.js +8 -3
  11. package/dist/index.d.ts +4 -4
  12. package/dist/index.js +2 -3
  13. package/dist/inspection.js +1 -3
  14. package/dist/internal/inspection.js +1 -3
  15. package/dist/internal/transition.d.ts +26 -2
  16. package/dist/internal/transition.js +37 -10
  17. package/dist/internal/utils.d.ts +7 -2
  18. package/dist/internal/utils.js +1 -3
  19. package/dist/machine.d.ts +66 -4
  20. package/dist/machine.js +67 -31
  21. package/dist/persistence/adapter.js +1 -3
  22. package/dist/persistence/adapters/in-memory.js +1 -3
  23. package/dist/persistence/index.js +1 -2
  24. package/dist/persistence/persistent-actor.js +54 -19
  25. package/dist/persistence/persistent-machine.js +1 -3
  26. package/dist/schema.js +1 -3
  27. package/dist/slot.js +1 -3
  28. package/dist/testing.js +58 -6
  29. package/package.json +19 -18
  30. package/v3/dist/_virtual/_rolldown/runtime.js +13 -0
  31. package/{dist-v3 → v3/dist}/actor.d.ts +65 -78
  32. package/{dist-v3 → v3/dist}/actor.js +173 -37
  33. package/{dist-v3 → v3/dist}/cluster/entity-machine.d.ts +1 -2
  34. package/{dist-v3 → v3/dist}/cluster/entity-machine.js +9 -9
  35. package/{dist-v3 → v3/dist}/cluster/index.js +1 -2
  36. package/{dist-v3 → v3/dist}/cluster/to-entity.d.ts +1 -1
  37. package/{dist-v3 → v3/dist}/cluster/to-entity.js +2 -4
  38. package/v3/dist/errors.d.ts +76 -0
  39. package/{dist-v3 → v3/dist}/errors.js +9 -4
  40. package/v3/dist/index.d.ts +13 -0
  41. package/v3/dist/index.js +13 -0
  42. package/{dist-v3 → v3/dist}/inspection.d.ts +53 -8
  43. package/v3/dist/inspection.js +156 -0
  44. package/{dist-v3 → v3/dist}/internal/brands.d.ts +1 -1
  45. package/{dist-v3 → v3/dist}/internal/inspection.d.ts +1 -1
  46. package/v3/dist/internal/inspection.js +20 -0
  47. package/{dist-v3 → v3/dist}/internal/transition.d.ts +35 -11
  48. package/{dist-v3 → v3/dist}/internal/transition.js +47 -17
  49. package/{dist-v3 → v3/dist}/internal/utils.d.ts +9 -4
  50. package/{dist-v3 → v3/dist}/internal/utils.js +2 -4
  51. package/{dist-v3 → v3/dist}/machine.d.ts +86 -10
  52. package/{dist-v3 → v3/dist}/machine.js +130 -33
  53. package/{dist-v3 → v3/dist}/persistence/adapter.d.ts +18 -5
  54. package/{dist-v3 → v3/dist}/persistence/adapter.js +2 -4
  55. package/{dist-v3 → v3/dist}/persistence/adapters/in-memory.d.ts +1 -1
  56. package/{dist-v3 → v3/dist}/persistence/adapters/in-memory.js +2 -4
  57. package/{dist-v3 → v3/dist}/persistence/index.js +1 -2
  58. package/{dist-v3 → v3/dist}/persistence/persistent-actor.d.ts +7 -6
  59. package/{dist-v3 → v3/dist}/persistence/persistent-actor.js +59 -22
  60. package/{dist-v3 → v3/dist}/persistence/persistent-machine.d.ts +1 -1
  61. package/{dist-v3 → v3/dist}/persistence/persistent-machine.js +2 -4
  62. package/{dist-v3 → v3/dist}/schema.d.ts +1 -1
  63. package/{dist-v3 → v3/dist}/schema.js +6 -5
  64. package/{dist-v3 → v3/dist}/slot.d.ts +4 -3
  65. package/{dist-v3 → v3/dist}/slot.js +2 -4
  66. package/{dist-v3 → v3/dist}/testing.d.ts +14 -8
  67. package/{dist-v3 → v3/dist}/testing.js +61 -9
  68. package/dist-v3/_virtual/_rolldown/runtime.js +0 -18
  69. package/dist-v3/errors.d.ts +0 -27
  70. package/dist-v3/index.d.ts +0 -13
  71. package/dist-v3/index.js +0 -14
  72. package/dist-v3/inspection.js +0 -50
  73. package/dist-v3/internal/inspection.js +0 -15
  74. /package/{dist-v3 → v3/dist}/cluster/index.d.ts +0 -0
  75. /package/{dist-v3 → v3/dist}/internal/brands.js +0 -0
  76. /package/{dist-v3 → v3/dist}/persistence/index.d.ts +0 -0
@@ -2,7 +2,6 @@ import { processEventCore, runSpawnEffects } from "../internal/transition.js";
2
2
  import { ActorSystem } from "../actor.js";
3
3
  import { Effect, Option, Queue, Ref, Scope } from "effect";
4
4
  import { Entity } from "effect/unstable/cluster";
5
-
6
5
  //#region src/cluster/entity-machine.ts
7
6
  /**
8
7
  * EntityMachine adapter - wires a machine to a cluster Entity layer.
@@ -55,10 +54,12 @@ const EntityMachine = { layer: (entity, machine, options) => {
55
54
  if (Option.isNone(existingSystem)) return yield* Effect.die("EntityMachine requires ActorSystem in context");
56
55
  const system = existingSystem.value;
57
56
  const internalQueue = yield* Queue.unbounded();
57
+ const clusterSend = Effect.fn("effect-machine.cluster.self.send")(function* (event) {
58
+ yield* Queue.offer(internalQueue, event);
59
+ });
58
60
  const self = {
59
- send: Effect.fn("effect-machine.cluster.self.send")(function* (event) {
60
- yield* Queue.offer(internalQueue, event);
61
- }),
61
+ send: clusterSend,
62
+ cast: clusterSend,
62
63
  spawn: (childId, childMachine) => system.spawn(childId, childMachine).pipe(Effect.provideService(ActorSystem, system))
63
64
  };
64
65
  const stateRef = yield* Ref.make(initialState);
@@ -75,6 +76,5 @@ const EntityMachine = { layer: (entity, machine, options) => {
75
76
  });
76
77
  return entity.toLayer(layer());
77
78
  } };
78
-
79
79
  //#endregion
80
- export { EntityMachine };
80
+ export { EntityMachine };
@@ -1,4 +1,3 @@
1
1
  import { EntityMachine } from "./entity-machine.js";
2
2
  import { toEntity } from "./to-entity.js";
3
-
4
- export { EntityMachine, toEntity };
3
+ export { EntityMachine, toEntity };
@@ -1,7 +1,6 @@
1
1
  import { MissingSchemaError } from "../errors.js";
2
2
  import { Entity } from "effect/unstable/cluster";
3
3
  import { Rpc } from "effect/unstable/rpc";
4
-
5
4
  //#region src/cluster/to-entity.ts
6
5
  /**
7
6
  * Generate Entity definition from a machine.
@@ -48,6 +47,5 @@ const toEntity = (machine, options) => {
48
47
  success: stateSchema
49
48
  }), Rpc.make("GetState", { success: stateSchema })]);
50
49
  };
51
-
52
50
  //#endregion
53
- export { toEntity };
51
+ export { toEntity };
package/dist/errors.d.ts CHANGED
@@ -42,5 +42,16 @@ declare const AssertionError_base: Schema.ErrorClass<AssertionError, Schema.Tagg
42
42
  }>, effect_Cause0.YieldableError>;
43
43
  /** Assertion failed in testing utilities */
44
44
  declare class AssertionError extends AssertionError_base {}
45
+ declare const ActorStoppedError_base: Schema.ErrorClass<ActorStoppedError, Schema.TaggedStruct<"ActorStoppedError", {
46
+ readonly actorId: Schema.String;
47
+ }>, effect_Cause0.YieldableError>;
48
+ /** Actor was stopped while a call/ask was pending */
49
+ declare class ActorStoppedError extends ActorStoppedError_base {}
50
+ declare const NoReplyError_base: Schema.ErrorClass<NoReplyError, Schema.TaggedStruct<"NoReplyError", {
51
+ readonly actorId: Schema.String;
52
+ readonly eventTag: Schema.String;
53
+ }>, effect_Cause0.YieldableError>;
54
+ /** ask() was used but the transition handler did not call reply */
55
+ declare class NoReplyError extends NoReplyError_base {}
45
56
  //#endregion
46
- export { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError };
57
+ export { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError };
package/dist/errors.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { Schema } from "effect";
2
-
3
2
  //#region src/errors.ts
4
3
  /**
5
4
  * Typed error classes for effect-machine.
@@ -33,6 +32,12 @@ var ProvisionValidationError = class extends Schema.TaggedErrorClass()("Provisio
33
32
  }) {};
34
33
  /** Assertion failed in testing utilities */
35
34
  var AssertionError = class extends Schema.TaggedErrorClass()("AssertionError", { message: Schema.String }) {};
36
-
35
+ /** Actor was stopped while a call/ask was pending */
36
+ var ActorStoppedError = class extends Schema.TaggedErrorClass()("ActorStoppedError", { actorId: Schema.String }) {};
37
+ /** ask() was used but the transition handler did not call reply */
38
+ var NoReplyError = class extends Schema.TaggedErrorClass()("NoReplyError", {
39
+ actorId: Schema.String,
40
+ eventTag: Schema.String
41
+ }) {};
37
42
  //#endregion
38
- export { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError };
43
+ export { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,13 @@
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
3
  import { PersistenceConfig, PersistentMachine, isPersistentMachine } from "./persistence/persistent-machine.js";
4
- import { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
4
+ import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
5
+ import { ProcessEventResult } from "./internal/transition.js";
5
6
  import { PersistentActorRef, createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
6
7
  import { ActorMetadata, PersistedEvent, PersistenceAdapter, PersistenceAdapterTag, PersistenceError, RestoreFailure, RestoreResult, Snapshot, VersionConflictError } from "./persistence/adapter.js";
7
8
  import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
8
- import "./persistence/index.js";
9
9
  import { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, ProvideHandlers, SpawnEffect, StateHandlerContext, TaskOptions, Transition, machine_d_exports } from "./machine.js";
10
- import { ActorRef, ActorSystem, Default, SystemEvent, SystemEventListener } from "./actor.js";
10
+ import { ActorRef, ActorRefSync, ActorSystem, Default, SystemEvent, SystemEventListener } from "./actor.js";
11
11
  import { SimulationResult, TestHarness, TestHarnessOptions, assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
12
12
  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 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, type PersistOptions, type PersistedEvent, type PersistenceAdapter, PersistenceAdapterTag, type PersistenceConfig, PersistenceError, type PersistentActorRef, type PersistentMachine, 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 };
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 };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
2
- import { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
2
+ import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
3
3
  import { isPersistentMachine } from "./persistence/persistent-machine.js";
4
4
  import { Slot } from "./slot.js";
5
5
  import { machine_exports } from "./machine.js";
@@ -10,5 +10,4 @@ import { Event, State } from "./schema.js";
10
10
  import { assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
11
11
  import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
12
12
  import "./persistence/index.js";
13
-
14
- export { Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, InMemoryPersistenceAdapter, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, MissingSchemaError, PersistenceAdapterTag, PersistenceError, ProvisionValidationError, Slot, SlotProvisionError, State, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, makeInspectorEffect, restorePersistentActor, simulate, tracingInspector };
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 };
@@ -1,5 +1,4 @@
1
1
  import { Effect, Option, ServiceMap } from "effect";
2
-
3
2
  //#region src/inspection.ts
4
3
  /**
5
4
  * Inspector service tag - optional service for machine introspection
@@ -139,6 +138,5 @@ const consoleInspector = () => makeInspector((event) => {
139
138
  const collectingInspector = (events) => ({ onInspect: (event) => {
140
139
  events.push(event);
141
140
  } });
142
-
143
141
  //#endregion
144
- export { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector };
142
+ export { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector };
@@ -1,5 +1,4 @@
1
1
  import { Clock, Effect } from "effect";
2
-
3
2
  //#region src/internal/inspection.ts
4
3
  /**
5
4
  * Emit an inspection event with timestamp from Clock.
@@ -17,6 +16,5 @@ const emitWithTimestamp = Effect.fn("effect-machine.emitWithTimestamp")(function
17
16
  });
18
17
  if (Effect.isEffect(result)) yield* result.pipe(Effect.catchCause(() => Effect.void));
19
18
  });
20
-
21
19
  //#endregion
22
- export { emitWithTimestamp };
20
+ export { emitWithTimestamp };
@@ -29,7 +29,11 @@ declare const runTransitionHandler: <S extends {
29
29
  readonly _tag: string;
30
30
  }, E extends {
31
31
  readonly _tag: string;
32
- }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, transition: Transition<S, E, GD, EFD, R>, state: S, event: E, self: MachineRef<E>, system: ActorSystem, actorId: string) => Effect.Effect<S, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
32
+ }, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, transition: Transition<S, E, GD, EFD, R>, state: S, event: E, self: MachineRef<E>, system: ActorSystem, actorId: string) => Effect.Effect<{
33
+ newState: S;
34
+ hasReply: boolean;
35
+ reply: unknown;
36
+ }, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
33
37
  /**
34
38
  * Execute a transition for a given state and event.
35
39
  * Handles transition resolution, handler invocation, and guard/effect slot creation.
@@ -49,6 +53,8 @@ declare const executeTransition: <S extends {
49
53
  newState: S;
50
54
  transitioned: boolean;
51
55
  reenter: boolean;
56
+ hasReply: boolean;
57
+ reply: unknown;
52
58
  }, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
53
59
  /**
54
60
  * Optional hooks for event processing inspection/tracing.
@@ -84,7 +90,22 @@ interface ProcessEventResult<S> {
84
90
  readonly lifecycleRan: boolean;
85
91
  /** Whether new state is final */
86
92
  readonly isFinal: boolean;
93
+ /** Whether the handler provided a reply (structural, not value-based) */
94
+ readonly hasReply: boolean;
95
+ /** Domain reply value from handler (used by ask). Only meaningful when hasReply is true. */
96
+ readonly reply?: unknown;
97
+ /** Whether the event was postponed (buffered for retry after next state change) */
98
+ readonly postponed: boolean;
87
99
  }
100
+ /**
101
+ * Check if an event should be postponed in the current state.
102
+ * @internal
103
+ */
104
+ declare const shouldPostpone: <S extends {
105
+ readonly _tag: string;
106
+ }, E extends {
107
+ readonly _tag: string;
108
+ }, R>(machine: Machine<S, E, R, any, any, any, any>, stateTag: string, eventTag: string) => boolean;
88
109
  /**
89
110
  * Process a single event through the machine.
90
111
  *
@@ -109,6 +130,9 @@ declare const processEventCore: <S extends {
109
130
  transitioned: boolean;
110
131
  lifecycleRan: boolean;
111
132
  isFinal: boolean;
133
+ hasReply: boolean;
134
+ reply: unknown;
135
+ postponed: boolean;
112
136
  }, never, Exclude<R, MachineContext<S, E, MachineRef<E>>> | Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
113
137
  /**
114
138
  * Run spawn effects for a state (forked into state scope, auto-cancelled on state exit).
@@ -157,4 +181,4 @@ declare const findSpawnEffects: <S extends {
157
181
  readonly _tag: string;
158
182
  }, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(machine: Machine<S, E, R, any, any, GD, EFD>, stateTag: string) => ReadonlyArray<SpawnEffect<S, E, EFD, R>>;
159
183
  //#endregion
160
- export { ProcessEventError, ProcessEventHooks, ProcessEventResult, TransitionExecutionResult, executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler };
184
+ export { ProcessEventError, ProcessEventHooks, ProcessEventResult, TransitionExecutionResult, executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler, shouldPostpone };
@@ -1,7 +1,6 @@
1
1
  import { INTERNAL_ENTER_EVENT, isEffect } from "./utils.js";
2
2
  import { BuiltMachine } from "../machine.js";
3
3
  import { Cause, Effect, Exit, Scope } from "effect";
4
-
5
4
  //#region src/internal/transition.ts
6
5
  /**
7
6
  * Transition execution and indexing.
@@ -38,8 +37,18 @@ const runTransitionHandler = Effect.fn("effect-machine.runTransitionHandler")(fu
38
37
  guards,
39
38
  effects
40
39
  };
41
- const result = transition.handler(handlerCtx);
42
- return isEffect(result) ? yield* result.pipe(Effect.provideService(machine.Context, ctx)) : result;
40
+ const raw = transition.handler(handlerCtx);
41
+ const resolved = isEffect(raw) ? yield* raw.pipe(Effect.provideService(machine.Context, ctx)) : raw;
42
+ if (resolved !== null && typeof resolved === "object" && "state" in resolved && "reply" in resolved && !("_tag" in resolved)) return {
43
+ newState: resolved.state,
44
+ hasReply: true,
45
+ reply: resolved.reply
46
+ };
47
+ return {
48
+ newState: resolved,
49
+ hasReply: false,
50
+ reply: void 0
51
+ };
43
52
  });
44
53
  /**
45
54
  * Execute a transition for a given state and event.
@@ -57,15 +66,28 @@ const executeTransition = Effect.fn("effect-machine.executeTransition")(function
57
66
  if (transition === void 0) return {
58
67
  newState: currentState,
59
68
  transitioned: false,
60
- reenter: false
69
+ reenter: false,
70
+ hasReply: false,
71
+ reply: void 0
61
72
  };
73
+ const { newState, hasReply, reply } = yield* runTransitionHandler(machine, transition, currentState, event, self, system, actorId);
62
74
  return {
63
- newState: yield* runTransitionHandler(machine, transition, currentState, event, self, system, actorId),
75
+ newState,
64
76
  transitioned: true,
65
- reenter: transition.reenter === true
77
+ reenter: transition.reenter === true,
78
+ hasReply,
79
+ reply
66
80
  };
67
81
  });
68
82
  /**
83
+ * Check if an event should be postponed in the current state.
84
+ * @internal
85
+ */
86
+ const shouldPostpone = (machine, stateTag, eventTag) => {
87
+ for (const rule of machine.postponeRules) if (rule.stateTag === stateTag && rule.eventTag === eventTag) return true;
88
+ return false;
89
+ };
90
+ /**
69
91
  * Process a single event through the machine.
70
92
  *
71
93
  * Handles:
@@ -94,7 +116,10 @@ const processEventCore = Effect.fn("effect-machine.processEventCore")(function*
94
116
  previousState: currentState,
95
117
  transitioned: false,
96
118
  lifecycleRan: false,
97
- isFinal: false
119
+ isFinal: false,
120
+ hasReply: false,
121
+ reply: void 0,
122
+ postponed: false
98
123
  };
99
124
  const newState = result.newState;
100
125
  const runLifecycle = newState._tag !== currentState._tag || result.reenter;
@@ -110,7 +135,10 @@ const processEventCore = Effect.fn("effect-machine.processEventCore")(function*
110
135
  previousState: currentState,
111
136
  transitioned: true,
112
137
  lifecycleRan: runLifecycle,
113
- isFinal: machine.finalStates.has(newState._tag)
138
+ isFinal: machine.finalStates.has(newState._tag),
139
+ hasReply: result.hasReply,
140
+ reply: result.reply,
141
+ postponed: false
114
142
  };
115
143
  });
116
144
  /**
@@ -236,6 +264,5 @@ const findTransitions = (input, stateTag, eventTag) => {
236
264
  const findSpawnEffects = (machine, stateTag) => {
237
265
  return getIndex(machine).spawn.get(stateTag) ?? [];
238
266
  };
239
-
240
267
  //#endregion
241
- export { executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler };
268
+ export { executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler, shouldPostpone };
@@ -26,7 +26,12 @@ type TaggedConstructor<T extends {
26
26
  /**
27
27
  * Transition handler result - either a new state or Effect producing one
28
28
  */
29
- type TransitionResult<State, R> = State | Effect.Effect<State, never, R>;
29
+ /** Reply tuple returned from transition handlers for ask support */
30
+ interface TransitionReply<State> {
31
+ readonly state: State;
32
+ readonly reply: unknown;
33
+ }
34
+ type TransitionResult<State, R> = State | TransitionReply<State> | Effect.Effect<State | TransitionReply<State>, never, R>;
30
35
  /**
31
36
  * Internal event tags used for lifecycle effect contexts.
32
37
  * Prefixed with $ to distinguish from user events.
@@ -57,4 +62,4 @@ declare const isEffect: (value: unknown) => value is Effect.Effect<unknown, unkn
57
62
  */
58
63
  declare const stubSystem: ActorSystem;
59
64
  //#endregion
60
- export { ArgsOf, INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, InstanceOf, TagOf, TaggedConstructor, TransitionResult, getTag, isEffect, stubSystem };
65
+ export { ArgsOf, INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, InstanceOf, TagOf, TaggedConstructor, TransitionReply, TransitionResult, getTag, isEffect, stubSystem };
@@ -1,5 +1,4 @@
1
1
  import { Effect, Stream } from "effect";
2
-
3
2
  //#region src/internal/utils.ts
4
3
  /**
5
4
  * Internal utilities for effect-machine.
@@ -50,6 +49,5 @@ const stubSystem = {
50
49
  restoreMany: () => Effect.die("restoreMany not supported in stub system"),
51
50
  restoreAll: () => Effect.die("restoreAll not supported in stub system")
52
51
  };
53
-
54
52
  //#endregion
55
- export { INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, getTag, isEffect, stubSystem };
53
+ export { INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, getTag, isEffect, stubSystem };
package/dist/machine.d.ts CHANGED
@@ -5,19 +5,20 @@ import { MachineEventSchema, MachineStateSchema, VariantsUnion } from "./schema.
5
5
  import { PersistenceConfig, PersistentMachine } from "./persistence/persistent-machine.js";
6
6
  import { DuplicateActorError } from "./errors.js";
7
7
  import { findTransitions } from "./internal/transition.js";
8
- import "./persistence/index.js";
9
8
  import { ActorRef, ActorSystem } from "./actor.js";
10
- import { Cause, Effect, Schedule, Schema, Scope, ServiceMap } from "effect";
9
+ import { Cause, Duration, Effect, Schedule, Schema, Scope, ServiceMap } from "effect";
11
10
 
12
11
  //#region src/machine.d.ts
13
12
  declare namespace machine_d_exports {
14
- export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, PersistenceConfig, PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, Transition, TransitionHandler, findTransitions, make, spawn };
13
+ export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, PersistenceConfig, PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, make, spawn };
15
14
  }
16
15
  /**
17
16
  * Self reference for sending events back to the machine
18
17
  */
19
18
  interface MachineRef<Event> {
20
19
  readonly send: (event: Event) => Effect.Effect<void>;
20
+ /** Fire-and-forget alias for send (OTP gen_server:cast). */
21
+ readonly cast: (event: Event) => Effect.Effect<void>;
21
22
  readonly spawn: <S2 extends {
22
23
  readonly _tag: string;
23
24
  }, E2 extends {
@@ -87,6 +88,18 @@ interface TaskOptions<State, Event, ED extends EffectsDef, A, E1, ES, EF> {
87
88
  readonly onFailure?: (cause: Cause.Cause<E1>, ctx: StateHandlerContext<State, Event, ED>) => EF;
88
89
  readonly name?: string;
89
90
  }
91
+ /**
92
+ * Configuration for `.timeout()` — gen_statem-style state timeouts.
93
+ *
94
+ * Entering the state starts a timer. Leaving cancels it.
95
+ * `.reenter()` restarts the timer with fresh state values.
96
+ */
97
+ interface TimeoutConfig<State, Event> {
98
+ /** Duration before firing. Static or derived from current state. */
99
+ readonly duration: Duration.Input | ((state: State) => Duration.Input);
100
+ /** Event to send when the timer fires. Static or derived from current state. */
101
+ readonly event: Event | ((state: State) => Event);
102
+ }
90
103
  type IsAny<T> = 0 extends 1 & T ? true : false;
91
104
  type IsUnknown<T> = unknown extends T ? ([T] extends [unknown] ? true : false) : false;
92
105
  type NormalizeR<T> = IsAny<T> extends true ? T : IsUnknown<T> extends true ? never : T;
@@ -153,6 +166,11 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
153
166
  /** @internal */
154
167
  readonly _finalStates: Set<string>;
155
168
  /** @internal */
169
+ readonly _postponeRules: Array<{
170
+ readonly stateTag: string;
171
+ readonly eventTag: string;
172
+ }>;
173
+ /** @internal */
156
174
  readonly _guardsSchema?: GuardsSchema<GD>;
157
175
  /** @internal */
158
176
  readonly _effectsSchema?: EffectsSchema<EFD>;
@@ -176,6 +194,10 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
176
194
  get spawnEffects(): ReadonlyArray<SpawnEffect<State, Event, EFD, R>>;
177
195
  get backgroundEffects(): ReadonlyArray<BackgroundEffect<State, Event, EFD, R>>;
178
196
  get finalStates(): ReadonlySet<string>;
197
+ get postponeRules(): ReadonlyArray<{
198
+ readonly stateTag: string;
199
+ readonly eventTag: string;
200
+ }>;
179
201
  get guardsSchema(): GuardsSchema<GD> | undefined;
180
202
  get effectsSchema(): EffectsSchema<EFD> | undefined;
181
203
  /** @internal */
@@ -231,6 +253,28 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
231
253
  * Interrupts do not emit failure events.
232
254
  */
233
255
  task<NS extends VariantsUnion<_SD> & BrandedState, A, E1, ES extends VariantsUnion<_ED> & BrandedEvent, EF extends VariantsUnion<_ED> & BrandedEvent>(state: TaggedOrConstructor<NS>, run: (ctx: StateHandlerContext<NS, VariantsUnion<_ED> & BrandedEvent, EFD>) => Effect.Effect<A, E1, Scope.Scope>, options: TaskOptions<NS, VariantsUnion<_ED> & BrandedEvent, EFD, A, E1, ES, EF>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
256
+ /**
257
+ * State timeout — gen_statem's `state_timeout`.
258
+ *
259
+ * Entering the state starts a timer. Leaving cancels it (via state scope).
260
+ * `.reenter()` restarts the timer with fresh state values.
261
+ * Compiles to `.task()` internally — preserves `@machine.task` inspection events.
262
+ *
263
+ * @example
264
+ * ```ts
265
+ * machine
266
+ * .timeout(State.Loading, {
267
+ * duration: Duration.seconds(30),
268
+ * event: Event.Timeout,
269
+ * })
270
+ * // Dynamic duration from state
271
+ * .timeout(State.Retrying, {
272
+ * duration: (state) => Duration.seconds(state.backoff),
273
+ * event: Event.GiveUp,
274
+ * })
275
+ * ```
276
+ */
277
+ timeout<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, config: TimeoutConfig<NS, VariantsUnion<_ED> & BrandedEvent>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
234
278
  /**
235
279
  * Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
236
280
  * Use effect slots defined via `Slot.Effects` for the actual work.
@@ -252,6 +296,24 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
252
296
  * ```
253
297
  */
254
298
  background(handler: StateEffectHandler<State, Event, EFD, Scope.Scope>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
299
+ /**
300
+ * Postpone events — gen_statem's event postpone.
301
+ *
302
+ * When a matching event arrives in the given state, it is buffered instead of
303
+ * processed. After the next state transition (tag change), all buffered events
304
+ * are drained through the loop in FIFO order.
305
+ *
306
+ * Reply-bearing events (from `call`/`ask`) in the postpone buffer are settled
307
+ * with `ActorStoppedError` on stop/interrupt/final-state.
308
+ *
309
+ * @example
310
+ * ```ts
311
+ * machine
312
+ * .postpone(State.Connecting, Event.Data) // single event
313
+ * .postpone(State.Connecting, [Event.Data, Event.Cmd]) // multiple events
314
+ * ```
315
+ */
316
+ postpone<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, events: TaggedOrConstructor<VariantsUnion<_ED> & BrandedEvent> | ReadonlyArray<TaggedOrConstructor<VariantsUnion<_ED> & BrandedEvent>>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
255
317
  final<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
256
318
  /**
257
319
  * Finalize the machine. Returns a `BuiltMachine` — the only type accepted by `Machine.spawn`.
@@ -289,4 +351,4 @@ declare const spawn: {
289
351
  }, R>(machine: BuiltMachine<S, E, R>, id: string): Effect.Effect<ActorRef<S, E>, never, R>;
290
352
  };
291
353
  //#endregion
292
- export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, type PersistenceConfig, type PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, Transition, TransitionHandler, findTransitions, machine_d_exports, make, spawn };
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 };
package/dist/machine.js CHANGED
@@ -8,7 +8,6 @@ import { MachineContextTag } from "./slot.js";
8
8
  import { findTransitions, invalidateIndex } from "./internal/transition.js";
9
9
  import { createActor } from "./actor.js";
10
10
  import { Cause, Effect, Exit, Option, Scope } from "effect";
11
-
12
11
  //#region src/machine.ts
13
12
  var machine_exports = /* @__PURE__ */ __exportAll({
14
13
  BuiltMachine: () => BuiltMachine,
@@ -65,6 +64,7 @@ var Machine = class Machine {
65
64
  /** @internal */ _spawnEffects;
66
65
  /** @internal */ _backgroundEffects;
67
66
  /** @internal */ _finalStates;
67
+ /** @internal */ _postponeRules;
68
68
  /** @internal */ _guardsSchema;
69
69
  /** @internal */ _effectsSchema;
70
70
  /** @internal */ _guardHandlers;
@@ -89,6 +89,9 @@ var Machine = class Machine {
89
89
  get finalStates() {
90
90
  return this._finalStates;
91
91
  }
92
+ get postponeRules() {
93
+ return this._postponeRules;
94
+ }
92
95
  get guardsSchema() {
93
96
  return this._guardsSchema;
94
97
  }
@@ -102,6 +105,7 @@ var Machine = class Machine {
102
105
  this._spawnEffects = [];
103
106
  this._backgroundEffects = [];
104
107
  this._finalStates = /* @__PURE__ */ new Set();
108
+ this._postponeRules = [];
105
109
  this._guardsSchema = guardsSchema;
106
110
  this._effectsSchema = effectsSchema;
107
111
  this._guardHandlers = /* @__PURE__ */ new Map();
@@ -260,6 +264,36 @@ var Machine = class Machine {
260
264
  return this.spawn(state, handler);
261
265
  }
262
266
  /**
267
+ * State timeout — gen_statem's `state_timeout`.
268
+ *
269
+ * Entering the state starts a timer. Leaving cancels it (via state scope).
270
+ * `.reenter()` restarts the timer with fresh state values.
271
+ * Compiles to `.task()` internally — preserves `@machine.task` inspection events.
272
+ *
273
+ * @example
274
+ * ```ts
275
+ * machine
276
+ * .timeout(State.Loading, {
277
+ * duration: Duration.seconds(30),
278
+ * event: Event.Timeout,
279
+ * })
280
+ * // Dynamic duration from state
281
+ * .timeout(State.Retrying, {
282
+ * duration: (state) => Duration.seconds(state.backoff),
283
+ * event: Event.GiveUp,
284
+ * })
285
+ * ```
286
+ */
287
+ timeout(state, config) {
288
+ const stateTag = getTag(state);
289
+ const resolveDuration = typeof config.duration === "function" ? config.duration : () => config.duration;
290
+ const resolveEvent = typeof config.event === "function" ? config.event : () => config.event;
291
+ return this.task(state, (ctx) => Effect.sleep(resolveDuration(ctx.state)), {
292
+ onSuccess: (_, ctx) => resolveEvent(ctx.state),
293
+ name: `$timeout:${stateTag}`
294
+ });
295
+ }
296
+ /**
263
297
  * Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
264
298
  * Use effect slots defined via `Slot.Effects` for the actual work.
265
299
  *
@@ -283,6 +317,35 @@ var Machine = class Machine {
283
317
  this._backgroundEffects.push({ handler });
284
318
  return this;
285
319
  }
320
+ /**
321
+ * Postpone events — gen_statem's event postpone.
322
+ *
323
+ * When a matching event arrives in the given state, it is buffered instead of
324
+ * processed. After the next state transition (tag change), all buffered events
325
+ * are drained through the loop in FIFO order.
326
+ *
327
+ * Reply-bearing events (from `call`/`ask`) in the postpone buffer are settled
328
+ * with `ActorStoppedError` on stop/interrupt/final-state.
329
+ *
330
+ * @example
331
+ * ```ts
332
+ * machine
333
+ * .postpone(State.Connecting, Event.Data) // single event
334
+ * .postpone(State.Connecting, [Event.Data, Event.Cmd]) // multiple events
335
+ * ```
336
+ */
337
+ postpone(state, events) {
338
+ const stateTag = getTag(state);
339
+ const eventList = Array.isArray(events) ? events : [events];
340
+ for (const ev of eventList) {
341
+ const eventTag = getTag(ev);
342
+ this._postponeRules.push({
343
+ stateTag,
344
+ eventTag
345
+ });
346
+ }
347
+ return this;
348
+ }
286
349
  final(state) {
287
350
  const stateTag = getTag(state);
288
351
  this._finalStates.add(stateTag);
@@ -314,6 +377,7 @@ var Machine = class Machine {
314
377
  result._finalStates = new Set(this._finalStates);
315
378
  result._spawnEffects = [...this._spawnEffects];
316
379
  result._backgroundEffects = [...this._backgroundEffects];
380
+ result._postponeRules = [...this._postponeRules];
317
381
  const anyHandlers = handlers;
318
382
  if (this._guardsSchema !== void 0) for (const name of Object.keys(this._guardsSchema.definitions)) result._guardHandlers.set(name, anyHandlers[name]);
319
383
  if (this._effectsSchema !== void 0) for (const name of Object.keys(this._effectsSchema.definitions)) result._effectHandlers.set(name, anyHandlers[name]);
@@ -344,39 +408,11 @@ var TransitionScope = class {
344
408
  }
345
409
  };
346
410
  const make = Machine.make;
347
- /**
348
- * Spawn an actor directly without ActorSystem ceremony.
349
- * Accepts only `BuiltMachine` (call `.build()` first).
350
- *
351
- * **Single actor, no registry.** Caller manages lifetime via `actor.stop`.
352
- * If a `Scope` exists in context, cleanup attaches automatically on scope close.
353
- *
354
- * For registry, lookup by ID, persistence, or multi-actor coordination,
355
- * use `ActorSystemService` / `system.spawn` instead.
356
- *
357
- * @example
358
- * ```ts
359
- * // Fire-and-forget — caller manages lifetime
360
- * const actor = yield* Machine.spawn(machine.build());
361
- * yield* actor.send(Event.Start);
362
- * yield* actor.awaitFinal;
363
- * yield* actor.stop;
364
- *
365
- * // Scope-aware — auto-cleans up on scope close
366
- * yield* Effect.scoped(Effect.gen(function* () {
367
- * const actor = yield* Machine.spawn(machine.build());
368
- * yield* actor.send(Event.Start);
369
- * // actor.stop called automatically when scope closes
370
- * }));
371
- * ```
372
- */
373
- const spawnImpl = Effect.fn("effect-machine.spawn")(function* (built, id) {
411
+ const spawn = Effect.fn("effect-machine.spawn")(function* (built, id) {
374
412
  const actor = yield* createActor(id ?? `actor-${Math.random().toString(36).slice(2)}`, built._inner);
375
413
  const maybeScope = yield* Effect.serviceOption(Scope.Scope);
376
414
  if (Option.isSome(maybeScope)) yield* Scope.addFinalizer(maybeScope.value, actor.stop);
377
415
  return actor;
378
416
  });
379
- const spawn = spawnImpl;
380
-
381
417
  //#endregion
382
- export { BuiltMachine, Machine, findTransitions, machine_exports, make, spawn };
418
+ export { BuiltMachine, Machine, findTransitions, machine_exports, make, spawn };
@@ -1,5 +1,4 @@
1
1
  import { Schema, ServiceMap } from "effect";
2
-
3
2
  //#region src/persistence/adapter.ts
4
3
  /**
5
4
  * Error type for persistence operations
@@ -22,6 +21,5 @@ var VersionConflictError = class extends Schema.TaggedErrorClass()("VersionConfl
22
21
  * PersistenceAdapter service tag
23
22
  */
24
23
  var PersistenceAdapterTag = class extends ServiceMap.Service()("effect-machine/src/persistence/adapter/PersistenceAdapterTag") {};
25
-
26
24
  //#endregion
27
- export { PersistenceAdapterTag, PersistenceError, VersionConflictError };
25
+ export { PersistenceAdapterTag, PersistenceError, VersionConflictError };