@camcima/finita 3.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -9,16 +9,26 @@ interface Metadata {
9
9
  }
10
10
 
11
11
  interface Observer {
12
- update(subject: ObservableSubject): MaybePromise<void>;
12
+ /**
13
+ * @param args The arguments the notification was invoked with — for
14
+ * Statemachine events, [subject, context]. Passed per-call so shared
15
+ * Event instances carry no per-invocation state.
16
+ */
17
+ update(subject: ObservableSubject, args?: readonly unknown[]): MaybePromise<void>;
13
18
  }
14
19
  interface ObservableSubject {
15
20
  attach(observer: Observer): void;
16
21
  detach(observer: Observer): void;
17
- notify(): Promise<void>;
22
+ notify(args?: readonly unknown[]): Promise<void>;
18
23
  getObservers(): Iterable<Observer>;
19
24
  }
20
25
 
21
26
  interface EventInterface extends Named, Metadata, ObservableSubject {
27
+ /**
28
+ * @deprecated Always returns []. Invoke args are now passed directly to
29
+ * Observer.update — reading them from the event was racy when one
30
+ * Process served multiple Statemachines.
31
+ */
22
32
  getInvokeArgs(): unknown[];
23
33
  invoke(...args: unknown[]): Promise<void>;
24
34
  getMetadataValue(key: string): unknown;
@@ -31,14 +41,18 @@ declare class Event implements EventInterface {
31
41
  private readonly name;
32
42
  private readonly observers;
33
43
  private readonly metadata;
34
- private invokeArgs;
35
44
  constructor(name: string);
36
45
  getName(): string;
46
+ /**
47
+ * @deprecated Always returns []. Invoke args are now passed directly to
48
+ * Observer.update — reading them from the event was racy when one
49
+ * Process served multiple Statemachines.
50
+ */
37
51
  getInvokeArgs(): unknown[];
38
52
  invoke(...args: unknown[]): Promise<void>;
39
53
  attach(observer: Observer): void;
40
54
  detach(observer: Observer): void;
41
- notify(): Promise<void>;
55
+ notify(args?: readonly unknown[]): Promise<void>;
42
56
  getObservers(): Iterable<Observer>;
43
57
  getMetadata(): Record<string, unknown>;
44
58
  getMetadataValue(key: string): unknown;
@@ -167,9 +181,14 @@ declare class ProcessBuilder<TSubject = unknown> {
167
181
  addState(name: string, options?: AddStateOptions): this;
168
182
  addTransition(fromState: string, toState: string, options?: AddTransitionOptions<TSubject>): this;
169
183
  build(options?: BuildOptions): Process;
184
+ /** One name rule for every named entity: non-empty, no leading/trailing whitespace. */
185
+ private validateName;
170
186
  private validateInitialState;
171
187
  private findInitialStateName;
172
188
  private validateTransitionEndpoints;
189
+ /** Transition identity: (fromState, eventName, toState). Used by both the
190
+ * conflict check and the build-time dedup — keep them in lockstep. */
191
+ private static transitionKey;
173
192
  private validateNoConflictingDuplicates;
174
193
  private collectEventNamesByState;
175
194
  /**
@@ -187,13 +206,18 @@ declare class ProcessBuilder<TSubject = unknown> {
187
206
  }
188
207
 
189
208
  /**
190
- * Immutable snapshot passed to AfterTransitionObserver.notify().
209
+ * Immutable snapshot passed to transition observers.
191
210
  *
192
- * Captures the post-commit transition: state has already moved from
193
- * fromState to toState. Reading any field is safe and stable for the
194
- * duration of the observer call (and beyond — the frame is frozen).
211
+ * For AfterTransitionObserver.notify() the transition has committed:
212
+ * state has already moved from fromState to toState. For
213
+ * BeforeTransitionObserver.notify() the same shape represents the
214
+ * *proposed* transition — fromState is still the current state, and
215
+ * throwing aborts the commit. Reading any field is safe and stable for
216
+ * the duration of the observer call (and beyond — the frame is frozen).
195
217
  */
196
218
  interface TransitionFrame<TSubject = unknown> {
219
+ /** The subject this machine drives — identifies whose transition this is. */
220
+ readonly subject: TSubject;
197
221
  readonly fromState: StateInterface;
198
222
  readonly toState: StateInterface;
199
223
  readonly transition: TransitionInterface<TSubject>;
@@ -204,14 +228,10 @@ interface TransitionFrame<TSubject = unknown> {
204
228
  readonly machineName: string | null;
205
229
  }
206
230
  /**
207
- * Immutable snapshot passed to BeforeTransitionObserver.notify().
208
- *
209
- * Same shape as TransitionFrame but represents a *proposed* transition
210
- * — fromState is still the current state at notification time. Throwing
211
- * from a before-observer aborts the transition; otherwise commit proceeds.
231
+ * The frame as seen by BeforeTransitionObserver — same shape; the
232
+ * distinct name documents the pre-commit timing.
212
233
  */
213
- interface ProposedTransitionFrame<TSubject = unknown> extends TransitionFrame<TSubject> {
214
- }
234
+ type ProposedTransitionFrame<TSubject = unknown> = TransitionFrame<TSubject>;
215
235
 
216
236
  /**
217
237
  * Runs before a transition commits. Throwing aborts the transition —
@@ -219,7 +239,9 @@ interface ProposedTransitionFrame<TSubject = unknown> extends TransitionFrame<TS
219
239
  * the thrown error.
220
240
  *
221
241
  * Implementations must be pure relative to the FSM: they MUST NOT call
222
- * triggerEvent / checkTransitions on the same Statemachine. There is no
242
+ * triggerEvent / checkTransitions on the same Statemachine. Doing so throws
243
+ * ReentrancyError when the call happens before the observer's first await;
244
+ * calls made after an await cannot be detected and will deadlock. There is no
223
245
  * enqueue handle in the before phase by design — vetoes and validations
224
246
  * complete synchronously per observer; chained behaviour belongs in
225
247
  * AfterTransitionObserver.
@@ -237,7 +259,14 @@ interface BeforeTransitionObserver<TSubject = unknown> {
237
259
  * (and any auto-follow-on transitions) completes.
238
260
  */
239
261
  interface EnqueueContext {
240
- enqueue(event: string, context?: Map<string, unknown>): void;
262
+ /**
263
+ * @param ifStateName When provided, the enqueued event is silently
264
+ * skipped unless the machine is still in that state when the operation
265
+ * is dequeued — the machine may have moved on in the meantime.
266
+ * If the machine leaves and returns to that state, the op is not skipped
267
+ * — only the state name is compared, not entry identity or count.
268
+ */
269
+ enqueue(event: string, context?: Map<string, unknown>, ifStateName?: string): void;
241
270
  }
242
271
  /**
243
272
  * Runs after a transition has committed. State has already moved.
@@ -258,6 +287,12 @@ interface StatemachineInterface<TSubject = unknown> {
258
287
  getProcess(): ProcessInterface;
259
288
  triggerEvent(name: string, context?: Map<string, unknown>): Promise<void>;
260
289
  checkTransitions(context?: Map<string, unknown>): Promise<void>;
290
+ /**
291
+ * Resolves once the operation queue is empty and the runner is idle,
292
+ * including operations chained via EnqueueContext.enqueue(). Resolves
293
+ * immediately if the machine is already idle.
294
+ */
295
+ whenIdle(): Promise<void>;
261
296
  attachBefore(observer: BeforeTransitionObserver<TSubject>): void;
262
297
  detachBefore(observer: BeforeTransitionObserver<TSubject>): void;
263
298
  getBeforeObservers(): Iterable<BeforeTransitionObserver<TSubject>>;
@@ -287,10 +322,59 @@ interface StatemachineOptions<TSubject = unknown> {
287
322
  initialStateName?: string;
288
323
  /** Defaults to OneOrNoneActiveTransition. */
289
324
  transitionSelector?: TransitionSelectorInterface<TSubject>;
290
- /** Defaults to NullMutex (no cross-process serialization). */
325
+ /**
326
+ * Defaults to NullMutex (no cross-process serialization).
327
+ *
328
+ * Must be exclusive to this machine — never share one MutexInterface
329
+ * instance between machines: the engine reads isAcquired() as "this
330
+ * machine holds the lock", so a shared instance silently disables mutual
331
+ * exclusion. To coordinate machines, share the underlying
332
+ * LockAdapterInterface (same resource name) and construct one mutex per
333
+ * machine, as MutexFactory does.
334
+ */
291
335
  mutex?: MutexInterface;
292
336
  /** When true, the engine releases the mutex at the end of each top-level operation. Defaults to true. */
293
337
  autoreleaseLock?: boolean;
338
+ /**
339
+ * Maximum number of automatic (eventless) transitions a single operation
340
+ * may take before AutomaticTransitionCycleError is thrown. Guards against
341
+ * non-terminating automatic loops while allowing legitimate bounded loops
342
+ * (e.g. condition-terminated retry cycles). Note: transitions committed
343
+ * before the limit is hit are NOT rolled back. Must be a positive integer;
344
+ * constructing a Statemachine with a value < 1 throws a RangeError.
345
+ * Defaults to 100.
346
+ */
347
+ maxAutomaticHops?: number;
348
+ /**
349
+ * Called when an operation chained via EnqueueContext.enqueue() fails.
350
+ * Chained operations are not awaited by the caller whose transition
351
+ * enqueued them, so without this hook their errors are discarded.
352
+ * Exceptions thrown by the hook itself are swallowed — it must not be
353
+ * able to break the machine's drain loop.
354
+ */
355
+ onChainedOperationError?: (error: unknown, info: {
356
+ eventName: string;
357
+ }) => void;
358
+ /**
359
+ * Maximum number of operations that may wait in the queue (the running
360
+ * operation does not count). When the limit is reached, further
361
+ * triggerEvent/checkTransitions calls reject with
362
+ * QueueLimitExceededError, and EnqueueContext.enqueue() throws it into
363
+ * the enqueuing after-observer's error path. In that case the original
364
+ * caller's promise rejects even though its transition already
365
+ * committed — check the machine's state, not just the rejection, before
366
+ * retrying. Must be a positive integer when set. Defaults to Infinity
367
+ * (unbounded, the previous behavior).
368
+ */
369
+ maxQueueLength?: number;
370
+ /**
371
+ * Diagnostic hook called whenever the automatic post-operation lock
372
+ * release throws — including when the operation itself also failed, in
373
+ * which case the caller's rejection carries the operation error and the
374
+ * release error would otherwise be discarded. Does not change rejection
375
+ * behavior. Exceptions thrown by the hook itself are swallowed.
376
+ */
377
+ onReleaseError?: (error: unknown) => void;
294
378
  }
295
379
 
296
380
  declare class Statemachine<TSubject = unknown> implements StatemachineInterface<TSubject> {
@@ -301,10 +385,16 @@ declare class Statemachine<TSubject = unknown> implements StatemachineInterface<
301
385
  private currentState;
302
386
  private lastState;
303
387
  private autoreleaseLock;
388
+ private readonly maxAutomaticHops;
389
+ private readonly maxQueueLength;
304
390
  private readonly queue;
305
391
  private running;
392
+ private idleWaiters;
393
+ private inSyncCallback;
306
394
  private readonly beforeObservers;
307
395
  private readonly afterObservers;
396
+ private readonly onChainedOperationError?;
397
+ private readonly onReleaseError?;
308
398
  constructor(subject: TSubject, process: ProcessInterface, options?: StatemachineOptions<TSubject>);
309
399
  getCurrentState(): StateInterface;
310
400
  getLastState(): StateInterface | null;
@@ -323,6 +413,24 @@ declare class Statemachine<TSubject = unknown> implements StatemachineInterface<
323
413
  setAutoreleaseLock(autorelease: boolean): void;
324
414
  triggerEvent(name: string, context?: Map<string, unknown>): Promise<void>;
325
415
  checkTransitions(context?: Map<string, unknown>): Promise<void>;
416
+ /**
417
+ * Resolves once the operation queue is empty and the runner is idle —
418
+ * i.e. every operation enqueued so far, including operations chained via
419
+ * EnqueueContext.enqueue(), has completed. Resolves immediately if the
420
+ * machine is already idle. Note this is a quiescence point, not a
421
+ * receipt: work scheduled later (e.g. from a timer) starts a new drain.
422
+ */
423
+ whenIdle(): Promise<void>;
424
+ /** Runs fn with the re-entrancy flag set for its SYNCHRONOUS portion only:
425
+ * the flag is cleared as soon as fn returns (before any promise it returned
426
+ * is awaited), so concurrent external callers are never affected. This
427
+ * catches triggerEvent/checkTransitions calls made before a callback's first
428
+ * await; calls made after a prior await are not detectable without
429
+ * AsyncLocalStorage (Node-only) and will still deadlock — a documented gap. */
430
+ private guardSync;
431
+ private assertNotReentrant;
432
+ /** Single entry point to the operation queue — every enqueue kicks the runner. */
433
+ private enqueueOperation;
326
434
  private runIfIdle;
327
435
  private runOperation;
328
436
  private resolveEvent;
@@ -372,9 +480,11 @@ interface LastStateHasChangedDateInterface {
372
480
  getLastStateHasChangedDate(): Date;
373
481
  }
374
482
 
483
+ /** @deprecated No longer used internally; will be removed in v4. */
375
484
  interface CallbackInterface {
376
485
  invoke(): MaybePromise<void>;
377
486
  }
487
+ /** @deprecated No longer used internally; will be removed in v4. */
378
488
  interface DispatcherInterface extends CallbackInterface {
379
489
  dispatch(event: EventInterface, args?: unknown[]): void;
380
490
  invoke(): Promise<void>;
@@ -416,19 +526,24 @@ declare class Timeout implements ConditionInterface {
416
526
  checkCondition(subject: unknown, context: Map<string, unknown>): boolean;
417
527
  }
418
528
 
419
- declare class AndComposite<TSubject = unknown> implements ConditionInterface<TSubject> {
420
- private readonly conditions;
529
+ declare abstract class CompositeCondition<TSubject = unknown> implements ConditionInterface<TSubject> {
530
+ protected readonly conditions: ConditionInterface<TSubject>[];
531
+ private readonly joinWord;
532
+ constructor(joinWord: string, condition: ConditionInterface<TSubject>);
533
+ protected addCondition(condition: ConditionInterface<TSubject>): this;
534
+ getName(): string;
535
+ abstract checkCondition(subject: TSubject, context: Map<string, unknown>): Promise<boolean>;
536
+ }
537
+
538
+ declare class AndComposite<TSubject = unknown> extends CompositeCondition<TSubject> {
421
539
  constructor(condition: ConditionInterface<TSubject>);
422
540
  addAnd(condition: ConditionInterface<TSubject>): this;
423
- getName(): string;
424
541
  checkCondition(subject: TSubject, context: Map<string, unknown>): Promise<boolean>;
425
542
  }
426
543
 
427
- declare class OrComposite<TSubject = unknown> implements ConditionInterface<TSubject> {
428
- private readonly conditions;
544
+ declare class OrComposite<TSubject = unknown> extends CompositeCondition<TSubject> {
429
545
  constructor(condition: ConditionInterface<TSubject>);
430
546
  addOr(condition: ConditionInterface<TSubject>): this;
431
- getName(): string;
432
547
  checkCondition(subject: TSubject, context: Map<string, unknown>): Promise<boolean>;
433
548
  }
434
549
 
@@ -449,12 +564,18 @@ declare class Not<TSubject = unknown> implements ConditionInterface<TSubject> {
449
564
  declare class CallbackObserver implements Observer {
450
565
  private readonly callback;
451
566
  constructor(callback: (...args: unknown[]) => MaybePromise<void>);
452
- update(subject: ObservableSubject): MaybePromise<void>;
567
+ update(subject: ObservableSubject, args?: readonly unknown[]): MaybePromise<void>;
453
568
  }
454
569
 
455
570
  declare class StatefulStatusChanger<TSubject extends StatefulInterface> implements AfterTransitionObserver<TSubject> {
456
571
  private readonly subject;
457
- constructor(subject: TSubject);
572
+ /**
573
+ * @param subject Optional explicit subject to write to. When omitted
574
+ * (recommended), the observer writes to frame.subject — the subject of
575
+ * whichever machine fired the transition — so a single instance can be
576
+ * shared safely across every machine a Factory creates.
577
+ */
578
+ constructor(subject?: TSubject);
458
579
  notify(frame: TransitionFrame<TSubject>): void;
459
580
  }
460
581
 
@@ -466,6 +587,10 @@ declare class StatefulStatusChanger<TSubject extends StatefulInterface> implemen
466
587
  * top-level operation after the current operation completes. Other
467
588
  * after-observers registered after OnEnterObserver still see the original
468
589
  * frame, not the chained one.
590
+ *
591
+ * The chained event only fires if the machine is still in the entered state
592
+ * when the queue drains — states passed through transiently by automatic
593
+ * transitions do not fire onEnter.
469
594
  */
470
595
  declare class OnEnterObserver<TSubject = unknown> implements AfterTransitionObserver<TSubject> {
471
596
  static readonly DEFAULT_EVENT_NAME = "onEnter";
@@ -482,7 +607,14 @@ declare class TransitionLogger<TSubject = unknown> implements AfterTransitionObs
482
607
  }
483
608
 
484
609
  declare class ActiveTransitionFilter {
485
- static filter<TSubject = unknown>(transitions: Iterable<TransitionInterface<TSubject>>, subject: TSubject, context: Map<string, unknown>, event?: EventInterface): Promise<TransitionInterface<TSubject>[]>;
610
+ static filter<TSubject = unknown>(transitions: Iterable<TransitionInterface<TSubject>>, subject: TSubject, context: Map<string, unknown>, event?: EventInterface,
611
+ /**
612
+ * Optional wrapper run around each individual isActive() evaluation. The
613
+ * Statemachine passes its re-entrancy guard here so that every condition —
614
+ * not just the first — is evaluated with the guard active; without per-item
615
+ * wrapping a re-entrant condition on a later transition would deadlock.
616
+ */
617
+ wrap?: <T>(fn: () => T) => T): Promise<TransitionInterface<TSubject>[]>;
486
618
  }
487
619
 
488
620
  declare class FilterStateByEvent {
@@ -599,11 +731,12 @@ interface Graph {
599
731
  nodes: GraphNode[];
600
732
  edges: GraphEdge[];
601
733
  }
734
+ type GraphDirection = "TB" | "BT" | "LR" | "RL";
602
735
  interface DotOptions {
603
- rankdir?: string;
736
+ rankdir?: GraphDirection;
604
737
  }
605
738
  interface MermaidOptions {
606
- direction?: string;
739
+ direction?: GraphDirection;
607
740
  }
608
741
  declare class GraphBuilder {
609
742
  private readonly nodes;
@@ -648,7 +781,7 @@ declare class ProcessFinalizedError extends FinitaError {
648
781
  constructor(processName: string);
649
782
  }
650
783
 
651
- type GraphValidationCode = "unknownTarget" | "unknownSource" | "missingInitialState" | "multipleInitialStates" | "invalidEventName" | "invalidConditionName" | "orphanState";
784
+ type GraphValidationCode = "unknownTarget" | "unknownSource" | "missingInitialState" | "multipleInitialStates" | "invalidStateName" | "invalidEventName" | "invalidConditionName" | "invalidTransitionWeight" | "orphanState";
652
785
  declare class GraphValidationError extends FinitaError {
653
786
  readonly code: GraphValidationCode;
654
787
  readonly details: Readonly<Record<string, unknown>>;
@@ -661,6 +794,8 @@ interface DuplicateTransitionConflict {
661
794
  eventName: string | null;
662
795
  existingConditionName: string | null;
663
796
  newConditionName: string | null;
797
+ existingWeight?: number;
798
+ newWeight?: number;
664
799
  }
665
800
  declare class DuplicateTransitionError extends FinitaError {
666
801
  readonly code = "duplicateTransition";
@@ -704,9 +839,19 @@ declare class AmbiguousTransitionError extends FinitaError {
704
839
 
705
840
  declare class AutomaticTransitionCycleError extends FinitaError {
706
841
  readonly code = "automaticTransitionCycle";
707
- readonly targetStateName: string;
708
- readonly visitedStateNames: readonly string[];
709
- constructor(targetStateName: string, visitedStateNames: Iterable<string>);
842
+ readonly stateName: string;
843
+ readonly hopLimit: number;
844
+ constructor(stateName: string, hopLimit: number);
845
+ }
846
+
847
+ declare class ReentrancyError extends FinitaError {
848
+ readonly code = "reentrancy";
849
+ constructor(operation: string);
850
+ }
851
+
852
+ declare class QueueLimitExceededError extends FinitaError {
853
+ readonly code = "queueLimitExceeded";
854
+ constructor(limit: number, eventName: string | null);
710
855
  }
711
856
 
712
- export { AbstractNamedProcessDetector, ActiveTransitionFilter, type AddStateOptions, type AddTransitionOptions, type AfterTransitionObserver, AmbiguousTransitionError, AndComposite, AutomaticTransitionCycleError, type BeforeTransitionObserver, type BuildOptions, CallbackCondition, type CallbackInterface, CallbackObserver, type ConditionCallbackFn, type ConditionInterface, Contradiction, type DispatcherInterface, type DotOptions, DuplicateStateError, type DuplicateTransitionConflict, DuplicateTransitionError, type EnqueueContext, Event, type EventInterface, Factory, type FactoryInterface, FilterStateByEvent, FilterStateByFinalState, FilterStateByTransition, FilterTransitionByEvent, FinitaError, type Graph, GraphBuilder, type GraphEdge, type GraphNode, type GraphValidationCode, GraphValidationError, InvalidSubjectError, type LastStateHasChangedDateInterface, type LockAdapterInterface, LockAdapterMutex, LockCanNotBeAcquiredError, type LoggerInterface, type MaybePromise, type MermaidOptions, type Metadata, MutexFactory, type MutexFactoryInterface, type MutexInterface, type Named, Not, NullMutex, type ObservableSubject, type Observer, OnEnterObserver, OneOrNoneActiveTransition, OrComposite, Process, ProcessBuilder, type ProcessDetectorInterface, ProcessFinalizedError, type ProcessInterface, ProcessNotFoundError, type ProposedTransitionFrame, ScoreTransition, SingleProcessDetector, State, type StateCollectionInterface, StateEventNotFoundError, type StateInterface, type StateNameDetectorInterface, StateNotFoundError, type StatefulInterface, StatefulStateNameDetector, StatefulStatusChanger, Statemachine, type StatemachineInterface, type StatemachineOptions, type StringConverter, Tautology, Timeout, Transition, type TransitionFrame, type TransitionInterface, TransitionLogger, type TransitionSelectorInterface, WeightTransition, type Weighted, WrongEventForStateError };
857
+ export { AbstractNamedProcessDetector, ActiveTransitionFilter, type AddStateOptions, type AddTransitionOptions, type AfterTransitionObserver, AmbiguousTransitionError, AndComposite, AutomaticTransitionCycleError, type BeforeTransitionObserver, type BuildOptions, CallbackCondition, type CallbackInterface, CallbackObserver, type ConditionCallbackFn, type ConditionInterface, Contradiction, type DispatcherInterface, type DotOptions, DuplicateStateError, type DuplicateTransitionConflict, DuplicateTransitionError, type EnqueueContext, Event, type EventInterface, Factory, type FactoryInterface, FilterStateByEvent, FilterStateByFinalState, FilterStateByTransition, FilterTransitionByEvent, FinitaError, type Graph, GraphBuilder, type GraphDirection, type GraphEdge, type GraphNode, type GraphValidationCode, GraphValidationError, InvalidSubjectError, type LastStateHasChangedDateInterface, type LockAdapterInterface, LockAdapterMutex, LockCanNotBeAcquiredError, type LoggerInterface, type MaybePromise, type MermaidOptions, type Metadata, MutexFactory, type MutexFactoryInterface, type MutexInterface, type Named, Not, NullMutex, type ObservableSubject, type Observer, OnEnterObserver, OneOrNoneActiveTransition, OrComposite, Process, ProcessBuilder, type ProcessDetectorInterface, ProcessFinalizedError, type ProcessInterface, ProcessNotFoundError, type ProposedTransitionFrame, QueueLimitExceededError, ReentrancyError, ScoreTransition, SingleProcessDetector, State, type StateCollectionInterface, StateEventNotFoundError, type StateInterface, type StateNameDetectorInterface, StateNotFoundError, type StatefulInterface, StatefulStateNameDetector, StatefulStatusChanger, Statemachine, type StatemachineInterface, type StatemachineOptions, type StringConverter, Tautology, Timeout, Transition, type TransitionFrame, type TransitionInterface, TransitionLogger, type TransitionSelectorInterface, WeightTransition, type Weighted, WrongEventForStateError };