machina 6.0.0 → 6.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 DELETED
@@ -1,628 +0,0 @@
1
- /**
2
- * Returned by `on()`. Call `off()` to remove the subscription.
3
- * Safe to call multiple times — subsequent calls are no-ops because
4
- * the underlying Set deduplicates membership.
5
- */
6
- type Subscription = {
7
- off(): void;
8
- };
9
-
10
- /**
11
- * Keys on a state object that have special meaning and are NOT input names.
12
- * Used by InputNamesOf to filter these out when collecting input names.
13
- */
14
- type SpecialStateKeys = "_onEnter" | "_onExit" | "_child" | "*";
15
- /**
16
- * Extracts state names as a string literal union from a states config object.
17
- *
18
- * @example
19
- * ```ts
20
- * type S = StateNamesOf<{ green: {...}, yellow: {...}, red: {...} }>;
21
- * // => "green" | "yellow" | "red"
22
- * ```
23
- */
24
- type StateNamesOf<TStates> = keyof TStates & string;
25
- /**
26
- * Extracts input names as a string literal union from a states config object.
27
- * Collects all handler keys across ALL states, then strips out lifecycle hooks
28
- * and special keys (_onEnter, _onExit, _child, *).
29
- *
30
- * This is what flows into `handle(inputName)` to provide compile-time
31
- * validation of input names.
32
- *
33
- * @example
34
- * ```ts
35
- * type I = InputNamesOf<{
36
- * idle: { start: "running", reset: fn };
37
- * running: { pause: "paused", stop: "idle" };
38
- * }>;
39
- * // => "start" | "reset" | "pause" | "stop"
40
- * ```
41
- *
42
- * How it works:
43
- * 1. `{ [S in keyof TStates]: keyof TStates[S] & string }` — maps each state
44
- * to the union of its handler key names
45
- * 2. `[keyof TStates]` — collapses the mapped type into a flat union of ALL
46
- * handler keys across every state
47
- * 3. `Exclude<..., SpecialStateKeys>` — strips lifecycle/special keys
48
- */
49
- type InputNamesOf<TStates> = Exclude<{
50
- [S in keyof TStates]: keyof TStates[S] & string;
51
- }[keyof TStates], SpecialStateKeys>;
52
- /**
53
- * The single combined object passed to every handler.
54
- *
55
- * Handlers return a state name to transition, or void to stay put.
56
- * This replaces imperative `transition()` calls — closer to gen_fsm's
57
- * return-based model and symmetrical with string shorthand handlers.
58
- *
59
- * @typeParam TCtx - The context type. For Fsm this is the config-defined
60
- * context object. For BehavioralFsm this is the client object itself.
61
- * @typeParam TStateNames - String literal union of valid state names.
62
- * Defaults to `string` for loose usage; the factory functions narrow this
63
- * to the actual state names inferred from the config.
64
- *
65
- * @example
66
- * ```ts
67
- * // Conditional transition — return the target state:
68
- * timeout({ ctx }) {
69
- * if (ctx.tickCount >= 3) return "yellow";
70
- * }
71
- *
72
- * // Side effects without transition — return nothing:
73
- * tick({ ctx }) {
74
- * ctx.tickCount++;
75
- * }
76
- *
77
- * // In a catch-all — inputName tells you what was received:
78
- * "*"({ inputName }) {
79
- * console.log(`unhandled input: ${inputName}`);
80
- * }
81
- * ```
82
- */
83
- interface HandlerArgs<TCtx, TStateNames extends string = string> {
84
- /** The context (Fsm) or client object (BehavioralFsm) */
85
- ctx: TCtx;
86
- /**
87
- * The name of the input currently being handled.
88
- *
89
- * Typed as `string` rather than the inferred input union because:
90
- * 1. Inside a named handler you already know the input name
91
- * 2. In a catch-all (*) handler it could be anything
92
- * 3. Narrowing to the literal per-handler would require complex
93
- * mapped types for zero practical benefit
94
- */
95
- inputName: string;
96
- /**
97
- * Defer the current input for replay after a future transition.
98
- * Erlang's selective receive, in JS form.
99
- *
100
- * @example
101
- * ```ts
102
- * // Replay on the next transition to any state
103
- * defer();
104
- *
105
- * // Replay only when entering "yellow"
106
- * defer({ until: "yellow" });
107
- * ```
108
- */
109
- defer(opts?: {
110
- until: TStateNames;
111
- }): void;
112
- /**
113
- * Emit a custom event through the FSM's emitter.
114
- * Built-in events (transitioning, transitioned, etc.) are emitted
115
- * automatically by the FSM engine — this is for user-defined events.
116
- */
117
- emit(eventName: string, data?: unknown): void;
118
- }
119
- /**
120
- * A function handler for state inputs, lifecycle hooks (_onEnter, _onExit),
121
- * and catch-all (*) handlers.
122
- *
123
- * **Return value determines transition:**
124
- * - Return a valid state name → FSM transitions to that state
125
- * - Return void/undefined → FSM stays in the current state
126
- *
127
- * This mirrors gen_fsm's `{next_state, StateName, NewStateData}` return.
128
- * Guards are just `if` statements. Actions are just code before the return.
129
- *
130
- * The `...extra` rest parameter captures additional arguments passed through
131
- * `handle(inputName, ...extraArgs)`. These are untyped (`unknown[]`) because
132
- * correlating per-input arg types with handle() call sites would require
133
- * prohibitively complex mapped types for minimal benefit.
134
- *
135
- * @example
136
- * ```ts
137
- * // Side effects only, no transition:
138
- * tick({ ctx }) { ctx.tickCount++; }
139
- *
140
- * // Conditional transition (replaces guard + target):
141
- * timeout({ ctx }) {
142
- * if (ctx.tickCount >= 3) return "yellow";
143
- * }
144
- *
145
- * // Unconditional transition with side effect (replaces action + target):
146
- * timeout({ ctx }) {
147
- * console.log("transitioning after", ctx.tickCount, "ticks");
148
- * return "yellow";
149
- * }
150
- *
151
- * // Handler with extra args passed via handle("success", responseData):
152
- * success({ ctx }, data) { ctx.result = data; }
153
- * ```
154
- */
155
- type HandlerFn<TCtx, TStateNames extends string = string> = (args: HandlerArgs<TCtx, TStateNames>, ...extra: unknown[]) => TStateNames | void;
156
- /**
157
- * The union of valid handler definition forms for a state input.
158
- *
159
- * - `TStateNames` — string shorthand, auto-transitions to that state
160
- * - `HandlerFn` — function that returns a state name (transition) or void (stay)
161
- *
162
- * @example
163
- * ```ts
164
- * states: {
165
- * green: {
166
- * timeout: "yellow", // string shorthand
167
- * tick({ ctx }) { ctx.tickCount++; }, // function, no transition
168
- * emergency({ ctx }) { // function, conditional transition
169
- * if (ctx.severity > 5) return "red";
170
- * },
171
- * },
172
- * }
173
- * ```
174
- */
175
- /**
176
- * The union of valid handler definition forms for a state input.
177
- *
178
- * - `TStateNames` — string shorthand, auto-transitions to that state
179
- * - `HandlerFn` — function that returns a state name (transition) or void (stay)
180
- * - `MachinaInstance` — included to satisfy TypeScript's structural widening
181
- * when `ValidateStates` falls back to its constraint type. The per-key
182
- * restriction on `_child` is still enforced by `ValidateStates`; this
183
- * union member just prevents the inference engine from rejecting
184
- * `_child: childFsm` before the conditional mapping can evaluate it.
185
- *
186
- * @example
187
- * ```ts
188
- * states: {
189
- * green: {
190
- * timeout: "yellow", // string shorthand
191
- * tick({ ctx }) { ctx.tickCount++; }, // function, no transition
192
- * emergency({ ctx }) { // function, conditional transition
193
- * if (ctx.severity > 5) return "red";
194
- * },
195
- * },
196
- * }
197
- * ```
198
- */
199
- type HandlerDef<TCtx, TStateNames extends string = string> = TStateNames | HandlerFn<TCtx, TStateNames> | MachinaInstance;
200
- /**
201
- * Validates and constrains the states object at the type level.
202
- *
203
- * This is a mapped type that walks every state and every property within
204
- * each state, assigning the correct expected type based on the property key:
205
- *
206
- * | Key | Expected type |
207
- * |------------------|-----------------------------------------|
208
- * | `_onEnter` | HandlerFn (lifecycle hook) |
209
- * | `_onExit` | HandlerFn (lifecycle hook) |
210
- * | `_child` | MachinaInstance (Fsm or BehavioralFsm) |
211
- * | `*` | HandlerFn (catch-all) |
212
- * | anything else | HandlerDef (string or fn) |
213
- *
214
- * @typeParam TCtx - Context/client type, flows into handler signatures
215
- * @typeParam TStates - The literal states object type captured by the
216
- * factory function's generic parameter
217
- */
218
- type ValidateStates<TCtx, TStates extends Record<string, Record<string, unknown>>> = {
219
- [S in keyof TStates]: {
220
- [K in keyof TStates[S]]: K extends "_onEnter" | "_onExit" ? HandlerFn<TCtx, keyof TStates & string> : K extends "_child" ? MachinaInstance : K extends "*" ? HandlerFn<TCtx, keyof TStates & string> : HandlerDef<TCtx, keyof TStates & string>;
221
- };
222
- };
223
- /**
224
- * Configuration object for creating an FSM.
225
- *
226
- * @typeParam TCtx - The context type (Fsm) or client type (BehavioralFsm).
227
- * For Fsm, this is inferred from the `context` property. For BehavioralFsm,
228
- * it's the client object type provided explicitly or as a generic parameter.
229
- *
230
- * @typeParam TStates - The literal states object type. Captured by the factory
231
- * function's generic parameter (ideally with `const` to preserve string
232
- * literal types). Defaults to a loose record for unconstrained usage.
233
- *
234
- * @example
235
- * ```ts
236
- * // TCtx inferred as { tickCount: number }, TStates inferred from states object:
237
- * createFsm({
238
- * id: "traffic-light",
239
- * initialState: "green", // validated against state keys
240
- * context: { tickCount: 0 }, // inference site for TCtx
241
- * states: {
242
- * green: { timeout: "yellow" }, // "yellow" validated against state keys
243
- * yellow: { timeout: "red" },
244
- * red: { timeout: "green" },
245
- * },
246
- * });
247
- * ```
248
- */
249
- interface FsmConfig<TCtx, TStates extends Record<string, Record<string, unknown>> = Record<string, Record<string, unknown>>> {
250
- /** Unique identifier for this FSM */
251
- id: string;
252
- /**
253
- * The state to start in. Must be a key of `states`.
254
- *
255
- * Wrapped in NoInfer to prevent TypeScript from using this value as an
256
- * inference site for TStates. Without it, `initialState: "green"` could
257
- * narrow TStates to only have a "green" key. We want inference to come
258
- * exclusively from the `states` property.
259
- */
260
- initialState: NoInfer<keyof TStates & string>;
261
- /**
262
- * Initial context data. The type is inferred from this value and flows
263
- * into every handler's `ctx` parameter.
264
- *
265
- * For BehavioralFsm, this property is optional and serves only as a
266
- * type constraint — the client object IS the context.
267
- */
268
- context?: TCtx;
269
- /** State definitions. Keys become the state name union. */
270
- states: ValidateStates<TCtx, TStates>;
271
- }
272
- /**
273
- * Symbol used as a property key to identify machina FSM instances at runtime.
274
- * Each class stamps itself with a MachinaType value so the ChildLink adapter
275
- * can dispatch handle()/canHandle()/reset() correctly without circular imports.
276
- */
277
- declare const MACHINA_TYPE: unique symbol;
278
- /**
279
- * Discriminant values stamped onto FSM instances via `MACHINA_TYPE`.
280
- * Used by the `ChildLink` adapter to dispatch calls correctly without
281
- * importing either class directly (which would create circular dependencies).
282
- */
283
- type MachinaType = "Fsm" | "BehavioralFsm";
284
- /** Structural type matching any machina FSM instance (Fsm or BehavioralFsm) */
285
- type MachinaInstance = {
286
- readonly [MACHINA_TYPE]: MachinaType;
287
- };
288
- /**
289
- * Options for FSM disposal.
290
- */
291
- interface DisposeOptions {
292
- /**
293
- * When true, child FSMs declared via _child are NOT disposed.
294
- * Default: false (children ARE disposed along with the parent).
295
- */
296
- preserveChildren?: boolean;
297
- }
298
- /**
299
- * Built-in event map for Fsm instances.
300
- * Payloads do NOT include a client reference (Fsm is its own client).
301
- *
302
- * @typeParam TStateNames - The state name union, flows into transition
303
- * event payloads so fromState/toState are narrowed to actual state names.
304
- */
305
- interface FsmEventMap<TStateNames extends string = string> {
306
- /** Fired just before a state transition occurs */
307
- transitioning: {
308
- fromState: TStateNames;
309
- toState: TStateNames;
310
- };
311
- /** Fired just after a state transition completes */
312
- transitioned: {
313
- fromState: TStateNames;
314
- toState: TStateNames;
315
- };
316
- /** Fired when an input is about to be dispatched to a handler */
317
- handling: {
318
- inputName: string;
319
- };
320
- /** Fired after an input has been successfully handled */
321
- handled: {
322
- inputName: string;
323
- };
324
- /** Fired when an input has no matching handler in the current state */
325
- nohandler: {
326
- inputName: string;
327
- args: unknown[];
328
- };
329
- /** Fired when a transition targets a state that doesn't exist */
330
- invalidstate: {
331
- stateName: string;
332
- };
333
- /** Fired when an input is deferred for later replay */
334
- deferred: {
335
- inputName: string;
336
- };
337
- }
338
- /**
339
- * Built-in event map for BehavioralFsm instances.
340
- * Every payload is intersected with `{ client: TClient }` so subscribers
341
- * can identify which client the event pertains to.
342
- *
343
- * @typeParam TClient - The client object type
344
- * @typeParam TStateNames - The state name union
345
- */
346
- type BehavioralFsmEventMap<TClient, TStateNames extends string = string> = {
347
- [K in keyof FsmEventMap<TStateNames>]: FsmEventMap<TStateNames>[K] & {
348
- client: TClient;
349
- };
350
- };
351
-
352
- /**
353
- * Single-client FSM. Wraps a BehavioralFsm and uses the config's `context`
354
- * object as the implicit client, so callers never pass a client argument.
355
- *
356
- * Prefer `createFsm()` over constructing this directly — the factory infers
357
- * all generic parameters from the config object.
358
- *
359
- * All public methods silently no-op after `dispose()` is called.
360
- *
361
- * @typeParam TCtx - The context type, inferred from `config.context`.
362
- * @typeParam TStateNames - String literal union of valid state names.
363
- * @typeParam TInputNames - String literal union of valid input names.
364
- */
365
- declare class Fsm<TCtx extends object, TStateNames extends string, TInputNames extends string> {
366
- readonly id: string;
367
- readonly [MACHINA_TYPE]: "Fsm";
368
- private readonly bfsm;
369
- private readonly context;
370
- private readonly emitter;
371
- private disposed;
372
- constructor(config: FsmConfig<TCtx, Record<string, Record<string, unknown>>>);
373
- /**
374
- * Dispatch an input to the current state's handler.
375
- * If a `_child` FSM in the current state can handle it, delegation occurs
376
- * there first; unhandled inputs bubble up to the parent.
377
- * No-ops silently when disposed.
378
- */
379
- handle(inputName: TInputNames, ...args: unknown[]): void;
380
- /**
381
- * Returns true if the current state has a handler for `inputName`
382
- * (or a catch-all `"*"` handler). Does not trigger initialization
383
- * or any side effects. Returns false when disposed.
384
- */
385
- canHandle(inputName: string): boolean;
386
- /**
387
- * Transition back to `initialState`, firing `_onEnter` and lifecycle
388
- * events as if entering it fresh. No-ops silently when disposed.
389
- */
390
- reset(): void;
391
- /**
392
- * Returns the current state name. Always defined — Fsm eagerly
393
- * initializes into `initialState` during construction.
394
- */
395
- currentState(): TStateNames;
396
- /**
397
- * Directly transition to `toState`, firing `_onExit`, `_onEnter`, and
398
- * lifecycle events. Same-state transitions are silently ignored.
399
- * No-ops when disposed.
400
- */
401
- transition(toState: TStateNames): void;
402
- /**
403
- * Returns the current state as a dot-delimited path that includes
404
- * any active child FSM states (e.g. `"active.connecting.retrying"`).
405
- * Returns just the current state name when no child is active.
406
- */
407
- compositeState(): string;
408
- /**
409
- * Subscribe to a built-in lifecycle event or the wildcard.
410
- *
411
- * Named overload: typed payload, no event name in callback.
412
- * Wildcard (`"*"`): receives `(eventName, data)` for every event.
413
- * Returns a no-op `Subscription` when disposed.
414
- */
415
- on<K extends keyof FsmEventMap<TStateNames> & string>(eventName: K, callback: (data: FsmEventMap<TStateNames>[K]) => void): Subscription;
416
- on(eventName: "*", callback: (eventName: string, data: unknown) => void): Subscription;
417
- /**
418
- * Emit a custom event through the FSM. Built-in lifecycle events are
419
- * emitted automatically — this is for user-defined events from handlers.
420
- * Routes through the BehavioralFsm so all relay paths are consistent.
421
- * No-ops when disposed.
422
- */
423
- emit(eventName: string, data?: unknown): void;
424
- /**
425
- * Permanently shut down this FSM. Irreversible — all subsequent method
426
- * calls become silent no-ops. Clears all listeners and cascades disposal
427
- * to child FSMs (unless `preserveChildren` is set).
428
- */
429
- dispose(options?: DisposeOptions): void;
430
- }
431
- /**
432
- * Create a single-client FSM from a config object.
433
- *
434
- * Generic parameters are inferred automatically:
435
- * - `TCtx` comes from `config.context` (defaults to `{}` if omitted).
436
- * - `TStates` is captured with `const` inference to preserve string literal
437
- * types, enabling compile-time validation of transition targets and `handle()`
438
- * input names.
439
- *
440
- * State names, input names, and all handler signatures derive from `TStates`.
441
- *
442
- * @example
443
- * ```ts
444
- * const light = createFsm({
445
- * id: "traffic-light",
446
- * initialState: "green",
447
- * context: { tickCount: 0 },
448
- * states: {
449
- * green: { timeout: "yellow" },
450
- * yellow: { timeout: "red" },
451
- * red: { timeout: "green" },
452
- * },
453
- * });
454
- *
455
- * light.handle("timeout"); // transitions green → yellow
456
- * ```
457
- */
458
- declare function createFsm<TCtx extends object = Record<string, never>, const TStates extends Record<string, Record<string, unknown>> = Record<string, Record<string, unknown>>>(config: FsmConfig<TCtx, TStates>): Fsm<TCtx, StateNamesOf<TStates>, InputNamesOf<TStates>>;
459
-
460
- /**
461
- * Defines FSM behavior (states + transitions) while tracking per-client state
462
- * in a `WeakMap`. A single `BehavioralFsm` instance can drive any number of
463
- * independent client objects simultaneously — each gets its own state,
464
- * deferred queue, and lifecycle.
465
- *
466
- * Prefer `createBehavioralFsm()` over constructing this directly — the factory
467
- * infers all generic parameters from the config object.
468
- *
469
- * All public methods silently no-op after `dispose()` is called.
470
- *
471
- * @typeParam TClient - The client object type. Must be an object (non-primitive)
472
- * so it can serve as a WeakMap key.
473
- * @typeParam TStateNames - String literal union of valid state names.
474
- * @typeParam TInputNames - String literal union of valid input names.
475
- */
476
- declare class BehavioralFsm<TClient extends object, TStateNames extends string, TInputNames extends string> {
477
- readonly id: string;
478
- readonly initialState: TStateNames;
479
- readonly [MACHINA_TYPE]: "BehavioralFsm";
480
- private readonly states;
481
- private readonly emitter;
482
- private readonly clients;
483
- private readonly knownClients;
484
- private readonly childSubscriptions;
485
- private disposed;
486
- private transitionDepth;
487
- constructor(config: FsmConfig<TClient, Record<string, Record<string, unknown>>>);
488
- /**
489
- * Dispatch an input to the given client's current state handler.
490
- *
491
- * Delegation order: if the current state has a `_child` FSM that can
492
- * handle the input, it is dispatched there. If the child emits `nohandler`,
493
- * the input bubbles up to this FSM's local handler. If no handler exists
494
- * here either, `nohandler` is emitted on this FSM's emitter.
495
- *
496
- * No-ops silently when disposed.
497
- */
498
- handle(client: TClient, inputName: TInputNames, ...args: unknown[]): void;
499
- /**
500
- * Returns true if the client's current state has a handler for `inputName`
501
- * (or a catch-all `"*"` handler). Does NOT initialize the client — no
502
- * `_onEnter`, no events, no side effects. Unseen clients are treated as
503
- * if they were already in `initialState`. Returns false when disposed.
504
- */
505
- canHandle(client: TClient, inputName: string): boolean;
506
- /**
507
- * Transition the client back to `initialState`, firing `_onEnter` and
508
- * lifecycle events as if entering it fresh. No-ops when disposed.
509
- */
510
- reset(client: TClient): void;
511
- /**
512
- * Returns the client's current state, or `undefined` if the client has
513
- * never been initialized (i.e. `handle()`, `transition()`, or `reset()`
514
- * have never been called for it). Does NOT trigger initialization.
515
- */
516
- currentState(client: TClient): TStateNames | undefined;
517
- /**
518
- * Directly transition `client` to `toState`, running the full lifecycle:
519
- * `_onExit` for the current state → `transitioning` event → update state →
520
- * `_onEnter` for new state → `transitioned` event → child reset → deferred
521
- * queue replay → bounce (if `_onEnter` returned a state name).
522
- *
523
- * Same-state transitions are silently ignored. Transitions to unknown state
524
- * names emit `invalidstate` instead of throwing. Throws if the transition
525
- * depth exceeds `MAX_TRANSITION_DEPTH` (likely an `_onEnter` → transition loop).
526
- *
527
- * No-ops when disposed.
528
- */
529
- transition(client: TClient, toState: TStateNames): void;
530
- /**
531
- * Returns the client's state as a dot-delimited path including any active
532
- * child FSM states (e.g. `"active.connecting.retrying"`). Returns just the
533
- * current state name when no child is active. Returns `""` for clients that
534
- * have never been initialized (unlike `currentState()` which returns `undefined`).
535
- */
536
- compositeState(client: TClient): string;
537
- /**
538
- * Subscribe to a built-in lifecycle event or the wildcard.
539
- *
540
- * Named overload: typed payload includes `{ client: TClient }` so you can
541
- * identify which client the event pertains to. Wildcard (`"*"`) receives
542
- * `(eventName, data)` for every event. Returns a no-op `Subscription`
543
- * when disposed.
544
- */
545
- on<K extends keyof BehavioralFsmEventMap<TClient, TStateNames> & string>(eventName: K, callback: (data: BehavioralFsmEventMap<TClient, TStateNames>[K]) => void): Subscription;
546
- on(eventName: "*", callback: (eventName: string, data: unknown) => void): Subscription;
547
- /**
548
- * Emit a custom event through the FSM. Built-in lifecycle events are
549
- * emitted automatically — this is for user-defined events from handlers.
550
- * No-ops when disposed.
551
- */
552
- emit(eventName: string, data?: unknown): void;
553
- /**
554
- * Permanently shut down this FSM. Irreversible — all subsequent method
555
- * calls become silent no-ops. Tears down child subscriptions, clears all
556
- * listeners, and cascades disposal to child FSMs (unless `preserveChildren`
557
- * is set). The same child appearing in multiple states is disposed once.
558
- */
559
- dispose(options?: DisposeOptions): void;
560
- /**
561
- * Walks all states at construction time, detects raw FSM instances assigned
562
- * to _child, and wraps them into ChildLink adapters via createChildLink().
563
- * Must run BEFORE setupChildSubscriptions() so the subscriptions see
564
- * ChildLink objects, not raw FSM instances.
565
- */
566
- private wrapChildLinks;
567
- /**
568
- * Walks all states at construction time, finds states with _child, and
569
- * subscribes once to each unique child's wildcard events. Subscriptions are
570
- * stored for cleanup in dispose(). We deduplicate by child reference to
571
- * avoid double-subscribing when the same child appears in multiple states.
572
- */
573
- private setupChildSubscriptions;
574
- /**
575
- * Bubbles a child nohandler to the parent for the given client.
576
- * Only fires if the client is currently in a state that has this childLink.
577
- * Extracted from the lambda in setupChildSubscriptions to keep it readable.
578
- */
579
- private bubbleNohandler;
580
- /**
581
- * Returns true if the given client is currently in a parent state whose
582
- * _child is the specified childLink. Returns false if the client has no
583
- * meta (never initialized) or is in a state with a different (or no) child.
584
- */
585
- private isChildActiveForClient;
586
- /**
587
- * The inner handler dispatch — no delegation, no initialization side effects
588
- * beyond what getOrCreateClientMeta already did. Called by handle() after
589
- * the delegation check, and by the nohandler child listener for bubbling.
590
- */
591
- private handleLocally;
592
- private getOrCreateClientMeta;
593
- private buildHandlerArgs;
594
- private processQueue;
595
- }
596
- /**
597
- * Create a behavioral FSM (one definition, many clients) from a config object.
598
- *
599
- * Generic parameters are inferred automatically:
600
- * - `TClient` must be provided explicitly as a type parameter (it can't be
601
- * inferred from the config since no `context` property exists at the FSM level).
602
- * - `TStates` is captured with `const` inference to preserve string literal
603
- * types, enabling compile-time validation of transition targets and `handle()`
604
- * input names.
605
- *
606
- * State names, input names, and all handler signatures derive from `TStates`.
607
- *
608
- * @example
609
- * ```ts
610
- * interface Connection { url: string; retries: number; }
611
- *
612
- * const connFsm = createBehavioralFsm<Connection>({
613
- * id: "connectivity",
614
- * initialState: "disconnected",
615
- * states: {
616
- * disconnected: { connect: "connecting" },
617
- * connecting: { connected: "online", failed: "disconnected" },
618
- * online: { disconnect: "disconnected" },
619
- * },
620
- * });
621
- *
622
- * const conn = { url: "wss://example.com", retries: 0 };
623
- * connFsm.handle(conn, "connect");
624
- * ```
625
- */
626
- declare function createBehavioralFsm<TClient extends object, const TStates extends Record<string, Record<string, unknown>>>(config: FsmConfig<TClient, TStates>): BehavioralFsm<TClient, StateNamesOf<TStates>, InputNamesOf<TStates>>;
627
-
628
- export { BehavioralFsm, type BehavioralFsmEventMap, type DisposeOptions, Fsm, type FsmConfig, type FsmEventMap, type HandlerArgs, type HandlerDef, type HandlerFn, type InputNamesOf, type MachinaInstance, type StateNamesOf, type Subscription, createBehavioralFsm, createFsm };