/** * @file Event-Driven Adapters for @doeixd/machine * @description Provides primitives to adapt a machine's method-call-based API * to standard event-driven interfaces like the browser's `EventTarget` and * Node.js's `EventEmitter`. These adapters allow your type-safe machines to * integrate seamlessly into decoupled, event-driven architectures. */ import { EventEmitter } from 'events'; import { Machine, Context, TransitionNames } from './index'; /** * A minimal Observer interface for reactive streams. * Compatible with RxJS and other Observable implementations. * * @typeParam T - Value delivered to `next`. */ export interface Observer { next?: (value: T) => void; error?: (error: unknown) => void; complete?: () => void; } /** * A minimal Observable interface for reactive streams. * Compatible with RxJS and other Observable implementations. * * @typeParam T - Value delivered to subscribers. */ export interface Observable { subscribe(observer: Observer): { unsubscribe: () => void; }; } /** * A helper type that extracts the detail payload for a given machine event. * If the transition has arguments, it's an array of those arguments. * If it has no arguments, it's `undefined`. * * @template M The machine type. * @template K The name of the transition. */ export type MachineEventDetail, K extends TransitionNames> = M[K] extends (...args: infer A) => any ? (A extends [] ? undefined : A) : never; /** * A mapped type that creates a DOM-standard event map for a machine. * This is crucial for providing type safety when using `addEventListener`. * * It includes: * - A `statechange` event with the new machine state in its detail. * - An `error` event with an `Error` object in its detail. * - An entry for every possible machine transition. * * @template M The machine type. */ export type MachineEventMap> = { [K in TransitionNames]: CustomEvent>; } & { statechange: CustomEvent<{ state: M; }>; error: CustomEvent<{ error: Error; }>; }; /** * A type-safe, augmented EventTarget that wraps a state machine. * * It provides two key functionalities: * 1. Emits a `CustomEvent` named 'statechange' whenever the machine's state updates. * 2. Listens for other `CustomEvent`s and translates them into type-safe machine transitions. * * @template M The machine type (can be a union of states). */ export declare class MachineEventTarget> extends EventTarget { private readonly runner; /** * The current, readonly state of the machine. * Access this property to get the latest machine instance for UI rendering or inspection. * @example * console.log(machineTarget.state.context.count); */ get state(): M; /** * A direct, readonly accessor to the machine's current context. * A convenience property equivalent to `machineTarget.state.context`. */ get context(): Context; constructor(initialMachine: M); addMachineEventListener>(type: K, listener: (event: MachineEventMap[K]) => void, options?: boolean | AddEventListenerOptions): void; removeMachineEventListener>(type: K, listener: (event: MachineEventMap[K]) => void, options?: boolean | EventListenerOptions): void; /** * A type-safe method for dispatching transition events. * This is the recommended way to interact with the machine from your application code. * * @param type The name of the transition to trigger (e.g., 'add'). * @param detail The arguments for that transition, matching the method signature. * * @example * // For a transition `add(n: number)` * machineTarget.dispatch('add', [5]); * * // For a transition `increment()` * machineTarget.dispatch('increment'); */ dispatch>(type: K, detail?: MachineEventDetail): void; } /** * Creates a browser-native EventTarget from a machine. * * This powerful adapter makes your machine behave like a standard DOM element, * perfect for decoupling components or integrating with event-driven browser APIs. * * @param initialMachine The machine instance to wrap. * @returns A `MachineEventTarget` instance. * @typeParam M - Machine or typestate union owned by the adapter. * @example * ```ts * const target = asEventTarget(counter); * target.addMachineEventListener('statechange', event => { * console.log(event.detail.state.context); * }); * target.dispatch('add', [2]); * ``` */ export declare function asEventTarget>(initialMachine: M): MachineEventTarget; /** * A utility function to ergonomically add and clean up a listener on a MachineEventTarget. * It returns an `unsubscribe` function, which is ideal for use in `useEffect` hooks. * * @param target The `MachineEventTarget` to listen to. * @param type The name of the event to listen for. * @param listener The callback function to execute. * @returns A cleanup function that removes the event listener. * @typeParam M - Machine type owned by `target`. * @typeParam K - Event-map key selected for the listener. * * @example * useEffect(() => { * // The listener is automatically typed based on the event name. * const unsubscribe = listen(counterTarget, 'statechange', (event) => { * setCount(event.detail.state.context.count); * }); * * // The returned function is perfect for a useEffect cleanup. * return unsubscribe; * }, []); */ export declare function listen, K extends keyof MachineEventMap>(target: MachineEventTarget, type: K, listener: (event: MachineEventMap[K]) => void): () => void; /** * Defines the events and their payloads that our MachineEventEmitter can emit, * providing strict type safety for listeners. */ interface MachineEmitterEvents> { statechange: (newState: M) => void; error: (error: Error) => void; } /** * A type-safe, augmented EventEmitter that wraps a state machine. * * It provides two key functionalities: * 1. Emits a `'statechange'` event whenever the machine's state updates. * 2. Exposes a type-safe `dispatch` method to trigger machine transitions. * * @template M The machine type (can be a union of states). */ export declare class MachineEventEmitter> extends EventEmitter { private readonly runner; on>(event: E, listener: MachineEmitterEvents[E]): this; emit>(event: E, ...args: Parameters[E]>): boolean; get state(): M; get context(): Context; constructor(initialMachine: M); /** * A type-safe method for dispatching transitions to the machine. * This is the primary input for the machine in an event-driven system. * * @param eventName The name of the transition to trigger. * @param args The arguments for that transition, matching the method signature. * * @example * sessionEmitter.dispatch('login', 'username', 'password'); */ dispatch>(eventName: K, ...args: M[K] extends (...args: infer A) => any ? A : never): void; } /** * Creates a Node.js-style EventEmitter from a machine. * * This adapter is perfect for backend services, scripts, or any architecture that * uses the classic EventEmitter pattern for decoupling system components. * * @param initialMachine The machine instance to wrap. * @returns A `MachineEventEmitter` instance. * @typeParam M - Machine or typestate union owned by the adapter. * @example * ```ts * const emitter = asEventEmitter(counter); * emitter.on('statechange', state => console.log(state.context)); * emitter.dispatch('increment'); * ``` */ export declare function asEventEmitter>(initialMachine: M): MachineEventEmitter; /** * A type-safe Observable that wraps a state machine, emitting the new state * on every transition. * * This class conforms to the standard Observable interface, making it compatible * with libraries like RxJS and frameworks that use Observables (e.g., Angular). * * @template M The machine type (can be a union of states). */ export declare class MachineObservable> implements Observable { private readonly runner; private observers; private completed; get state(): M; get context(): Context; constructor(initialMachine: M); /** * Subscribes to the stream of machine states. * * @param observer An object with `next`, `error`, and `complete` methods. * @returns A subscription object with an `unsubscribe` method. */ subscribe(observer: Observer): { unsubscribe: () => void; }; /** * A type-safe method for dispatching transitions to the machine. * * @param eventName The name of the transition to trigger. * @param args The arguments for that transition. */ dispatch>(eventName: K, ...args: M[K] extends (...args: infer A) => any ? A : never): void; /** * Signals to all observers that the stream is complete. * This is useful when the machine reaches a final state. */ complete(): void; private emitNext; private notifyNext; private emitError; private notifyError; } /** * Creates an Observable from a machine. * * This adapter is perfect for integrating your machine into architectures that * rely on Observables and reactive streams (e.g., RxJS, Angular). It emits the * new machine state on every transition. * * @param initialMachine The machine instance to wrap. * @returns A `MachineObservable` instance. * @typeParam M - Machine or typestate union owned by the adapter. * @example * ```ts * const observable = asObservable(counter); * const subscription = observable.subscribe({ next: console.log }); * observable.dispatch('increment'); * subscription.unsubscribe(); * ``` */ export declare function asObservable>(initialMachine: M): MachineObservable; export {}; //# sourceMappingURL=adapters.d.ts.map