import EventEmitter from './ee'; import Transition from './transition'; import { BaseStates, IBind, IEmit, IPipedStateTarget, IQueueRow, IState, PipeFlags, MutationTypes, StateRelations, TAbortFunction, TAsyncMachine, TLogHandler, TPipeBindings, TransitionException } from './types'; export { PipeFlags, StateStructFields, TransitionStepTypes, TransitionStepFields, MutationTypes, StateRelations, IBind, IEmit, IState, TAsyncMachine } from './types'; export { default as Transition } from './transition'; export { AsyncMachine }; /** * Factory function which creates an AsyncMachine instance with specified * states. * * States properties are empty, so you'd need to define the relations by * yourself. * * @param states List of state names to register on the new instance or a map * of state names and their attributes. * @return The machine instance. You can inherit from it by [[createChild]] to * make more copies, which are efficient. * * Using a list of names: * ``` * import { machine } from 'asyncmachine' * * let states = machine(['A', 'B','C']) * states.A = { add: ['B'] } * states.add('A') * states.is() // -> ['A', 'B'] * ``` * * Using a map: * ``` * import { machine } from 'asyncmachine' * * let states = machine({ * A: { add: ['B'] }, * B: {}, * C: {} * }) * states.add('A') * states.is() // -> ['A', 'B'] * ``` */ export declare function machine>(states: string[] | { [K in keyof States]?: IState; }, constructor?: { new (...params: any[]): T; }): T; export declare function machine(states: string[]): AsyncMachine; export declare function machine(states: { [K in States]?: IState; }): AsyncMachine; /** * Base class to extend. Define states as prototype attributes or * inside of the constructor. In the latter case remember to call * this.registerAll() afterwards (for every sub constructor). * * The [[Exception]] state is already provided. * * ``` * class FooStates extends AsyncMachine { * Enabled: {} * * Downloading: { * drop: 'Downloaded' * } * * Downloaded = { * drop: 'Downloading' * } * * constructor(target) { * super(target) * this.registerAll() * } * } * * class Foo { * constructor() { * this.states = new FooStates(this) * } * * Downloading_state(states, url) { * fetch(url, this.states.addByCallack('Downloaded')) * } * * Downloaded_state(states, local_path) { * console.log(`Downloaded ${this.url} to ${local_path}`) * } * } * ``` */ export default class AsyncMachine extends EventEmitter { /** * Exception state's properties. See [[Exception_state]] final transition * handler. * @type {IState} */ Exception: IState; /** List of all registered state names */ states_all: (TStates | BaseStates)[]; /** * Promise created by one of the delayed mutation methods: * - `...ByCallback` * - `...ByListener` * * Resolved once the callback/listener gets called. */ last_promise: Promise | null; /** Map of piped states and their targets. */ piped: { [K in TStates | BaseStates]?: IPipedStateTarget[]; }; /** * If true, an exception will be printed immediately after it's thrown. * Automatically turned on with logLevel > 0. */ print_exception: boolean; /** List of active states. */ states_active: (TStates | BaseStates)[]; protected queue_: IQueueRow[]; /** If true, this machine is currently during a transition. */ lock: boolean; /** If true, this machine's queue is currently being executed. */ lock_queue: boolean; /** List of current ticks per state. */ protected clock_: { [K in TStates | BaseStates]?: number; }; /** * Target object for the transitions, useful when composing the states * instance. */ target: {} | null; protected log_level_: number; /** Currently executing transition (if any) */ transition: Transition | null; /** List of handlers receiving log messeges */ log_handlers: TLogHandler[]; /** * If true, the default log handler will be disabled - no `console.log` * messages. */ def_log_handler_off: boolean; /** * Queue execution got postponed, because this machine is currently during * a transition from another machine's queue. */ protected postponed_queue: boolean; protected internal_fields: string[]; private id_; /** * Creates a new instance with only one registered state - Exception. * * When extending the class, you should register your states by using either * the [[registerAll]] or [[register]] methods at the end of every sub * constructor. * * @param target Target object for the transitions, useful when composing a * machine. * @param register_all Automatically registers all defined states. Works * only in case there no other attributes defined on the prototype. * @see [[AsyncMachine]] for the usage example. */ constructor(target?: {}, register_all?: boolean); /** * All exceptions are caught into this state, including both synchronous and * asynchronous ones, coming from async methods and callbacks. You can * override it with your own and handle exceptions based on passed state * data and the actual state. * * @param err The exception object. * @param target_states Target states of the transition during * which the exception was thrown. * @param base_states Base states in which the transition originated. * @param exception_src_handler The handler state which thrown the exception. * @param async_target_states Only for delayed mutations like * [[addByCallback]], these are states which we're supposed to be activated * by the callback. * * Example of a custom exception handler * ``` * states = machine(['A', 'B', 'C']) * states.Exception_state = function(err, target_states) { * // Re-adds the state 'C' in case of an exception when A is active * if (exception_states.some((state) => state == 'C') && this.is('A')) { * states.add 'C' * } * } * ``` * * Example of a manually thrown exception * ``` * states.A_state = function(states) { * foo = new SomeAsyncTask() * foo.start() * foo.once('error', (error) => { * this.add('Exception', error, states) * } * } * ``` */ Exception_state(err: Error | TransitionException, target_states: string[], base_states: string[], exception_src_handler: string, async_target_states?: string[]): void; /** * Sets the target for the transition handlers. Useful to keep all your * methods in in one class while the states class is composed as an * attribute of the target object. There's also a shorthand for this method * as a [[AsyncMachine.constructor]]'s param. * * @param target Target object. * * ``` * class Foo { * constructor() { * this.states = machine(['A', 'B', 'C']) * this.states.setTarget(this) * this.states.add('A') * } * * A_state() { * console.log('State A set') * } * } * ``` */ setTarget(target: {}): this; /** * Registers all defined states. Use it only if you don't define any other * attributes on the object (or it's prototype). If you do, register the * states manually with the [[register]] method. There's also a shorthand * for this method as [[AsyncMachine.constructor]]'s param. * * ``` * class States extends AsyncMachine { * constructor() { * this.A = {} * this.B = {} * * this.registerAll() * console.log(this.states_all) // -> ['Exception', 'A', 'B'] * } * } * ``` */ registerAll(): void; /** * Returns defined relations between two registered states. * * @param from_state * @param to_state * @returns List of relations. */ getRelationsOf(from_state: TStates | BaseStates, to_state?: TStates | BaseStates): StateRelations[]; /** * Without any params passed, returns all of the current states. * * When a list of states is provided, returns a boolean if all of them are * currently active. * * If only one state is passed, you can assert on a certain tick of that * state (see [[clock]]). * * @param states One or more state names. * @param tick When checking only one state, additionally asserts the clock * value for that state. * * ``` * states = machine(['A', 'B']) * states.add('A') * states.is('A') // -> true * states.is(['A']) // -> true * states.is(['A', 'B']) // -> false * // assert the tick * tick = states.clock('A') * states.drop('A') * states.add('A') * states.is('A', tick) // -> false * ``` */ is(states: (TStates | BaseStates) | (TStates | BaseStates)[], tick?: number): boolean; is(): (TStates | BaseStates)[]; /** * Returns `true` in case of all of the passed states aren't active. * * Example: * * ``` * const example = machine(['A', 'B', 'C', 'D']) * example.add(['A', 'B']) * * // not(A) and not(C) * example.not(['A', 'C']) // -> false * // not(C) and not(D) * example.not(['C', 'D']) // -> true * ``` * * @param states * @return */ not(states: (TStates | BaseStates) | (TStates | BaseStates)[]): boolean; /** * Checks if any of the passed states is active. State can also be an array, * then all states from this param has to be active. * * @param states State names and/or lists of state names. * @return * * ``` * states = machine(['A', 'B', 'C']) * states.add(['A', 'B']) * * states.any('A', 'C') // -> true * states.any(['A', 'C'], 'C') // -> false * ``` */ any(...states: (TStates | BaseStates)[]): boolean; any(...states: (TStates | BaseStates)[][]): boolean; /** * Returns the current queue. * * To understand the returned format, refer to [[IQueueRow]]. */ queue(): IQueueRow[]; /** * Simple method to check if a particular mutation has been queued. Returns * an index of the match or -1 in not found. * * All 3 params are checked and only the first match is returned, thus * this is not a full flaged solution for quering the queue. * * Example - check if `this.add('Bar')` is currently queued: * ```typescipt * import { machine, MutationTypes } from 'asyncmachine' * const example = machine(['Foo', 'Bar']) * example.Foo_state = function() { * this.add('Bar') // -> null * this.isQueued(MutationTypes.ADD, ['Bar']) // -> true * } * example.add('Foo') // -> true * ``` * * @param mutation_type Type: add, drop or set * @param states List of states used in the mutation * @param target_machine Target to mutate. Defaults to `this. * @param without_params_only Matches only mutation without params. * @param states_strict_equal States of the mutation have to be exactly like * `states`. If `false`, sets wider than (containing all of) the passed * `states` will also be accepted. * @param start_index Start index for the lookup (in the queue) * @return Found index of -1 * * TODO write a test */ isQueued(mutation_type: MutationTypes, states: string[], target_machine?: TAsyncMachine, without_params_only?: boolean, states_strict_equal?: boolean, start_index?: number): number; /** * Register passed state names. State properties should be already defined. * * @param states State names. * @return * * ``` * const example = machine() * example.Enabled = {} * example.Disposed = { drop: ['Enabled'] } * * states.register('Enabled', 'Disposed') * * states.add('Enabled') * states.is() // -> 'Enabled' * ``` */ register(...states: (TStates | BaseStates)[]): void; /** * TODO desc * TODO sample * TODO test * @param name */ unregister(name: string): void; /** * Returns state's properties and relations. * * @param state State name. * * ``` * states = machine(['A', 'B', 'C']) * states.A = { drop: ['B'] } * * states.get('A') // -> { drop: ['B'] } * ``` */ get(state: TStates | BaseStates): IState; /** * Activates only the specified states and de-activates all the other ones * which are currently active. * * @param target OPTIONAL. Pass it if you want to execute a transition on an * external machine, but using this machine's queue. * @param states Array of state names or a single state name. * @param params Params to be passed to the transition handlers (only the ones * belonging to the explicitly requested states, not implied or auto * states). * @return Result of the transition. `false` can mean that either the * requested states weren't accepted or that the transition has been aborted * by one of the negotiation handlers. `null` means that the machine is busy * and the mutation has been queued. * * Basic usage * ``` * const example = machine(['A', 'B', 'C']) * example.set('A') // -> true * example.is() // -> ['A'] * example.set('B') // -> true * example.is() // -> ['B'] * ``` * * State negotiation * ``` * const example = machine(['A', 'B']) * // reject the transition with a negotiation handler * example.A_enter = function() { * return false * } * example.add('A') // -> false * ``` * * Activating a state on an external machine * ``` * const m1 = machine(['A', 'B']) * const m2 = machine(['C', 'D']) * * m1.A_enter = function() { * // this transition will be queued and executed after the current * // transition is completed * m1.add(m2, 'C') // -> null * } * ``` */ set(target: AsyncMachine, states: (S | BaseStates)[] | (S | BaseStates), ...params: any[]): boolean | null; set(target: (TStates | BaseStates)[] | (TStates | BaseStates), states?: any, ...params: any[]): boolean | null; /** * Deferred version of [[set]], returning a node-style callback for setting * the state. Errors are handled automatically and forwarded to the Exception * state. * * After the call, the related promise object is available as the * [[last_promise]] attribute. * * See [[set]] for the params description. * * Example * ``` * const example = machine(['A', 'B', 'C']) * setTimeout(example.setByCallback('B')) * ``` * */ setByCallback(target: AsyncMachine, states?: (TStates | BaseStates)[] | (TStates | BaseStates), ...params: any[]): (err?: any, ...params: any[]) => void; setByCallback(target: (TStates | BaseStates)[] | (TStates | BaseStates), states?: any, ...params: any[]): (err?: any, ...params: any[]) => void; /** * Deferred version of [[set]], returning a listener for setting the state. * * Errors need to be handled manually by binding the exception * state to the 'error' event (or equivalent). * * After the call, the related promise object is available as the * [[last_promise]] attribute. * * See [[set]] for the params description. * * Example * ``` * const example = machine(['A', 'B', 'C']) * emitter = new EventEmitter * emitter.on('ready', states.setByListener('A')) * emitter.on('error', states.addByListener('Exception')) * ``` */ setByListener(target: AsyncMachine, states?: (TStates | BaseStates)[] | (TStates | BaseStates), ...params: any[]): (...params: any[]) => void; setByListener(target: (TStates | BaseStates)[] | (TStates | BaseStates), states?: any, ...params: any[]): (...params: any[]) => void; /** * Deferred version of [[set]], setting the requested states on the next * event loop's tick. Useful if you want to start with a fresh stack trace. * * See [[set]] for the params description. * * Example * ``` * states = machine(['A', 'B', 'C']) * states.set('A') * states.setNext('B') * states.is() // -> ['A'] * ``` */ setNext(target: AsyncMachine, states: (S | BaseStates)[] | (S | BaseStates), ...params: any[]): number; setNext(target: (TStates | BaseStates)[] | (TStates | BaseStates), states?: any, ...params: any[]): number; /** * Adds specified states to the currently active ones. * * @param target OPTIONAL. Pass it if you want to execute a transition on an * external machine, but using this machine's queue. * @param states Array of state names or a single state name. * @param params Params to be passed to the transition handlers (only the ones * belonging to the explicitly requested states, not implied or auto * states). * @return Result of the transition. `false` can mean that either the * requested states weren't accepted or that the transition has been aborted * by one of the negotiation handlers. `null` means that the machine is busy * and the mutation has been queued. * * Basic usage * ``` * const example = machine(['A', 'B', 'C']) * example.add 'A' * example.is() // -> ['A'] * example.add('B') * example.is() // -> ['B'] * ``` * * State negotiation * ``` * const example = machine(['A', 'B']) * // reject the transition with a negotiation handler * example.A_enter = function() { * return false * } * example.add('A' ) // -> false * ``` * * Adding a state on an external machine * ``` * const m1 = machine(['A', 'B']) * const m2 = machine(['C', 'D']) * * m1.A_enter = function() { * // this transition will be queued and executed after the current * // transition is completed * m1.add(m2, 'C') // -> null * } * ``` */ add(target: AsyncMachine, states: (S | BaseStates)[] | (S | BaseStates), ...params: any[]): boolean | null; add(target: (TStates | BaseStates)[] | (TStates | BaseStates), states?: any, ...params: any[]): boolean | null; /** * Deferred version of [[add]], returning a node-style callback for adding * the state. Errors are handled automatically and forwarded to the Exception * state. * * After the call, the related promise object is available as the * [[last_promise]] attribute. * * See [[add]] for the params description. * * Example * * ``` * const example = machine(['A', 'B', 'C']) * someNodeCallback('foo.com', example.addByCallback('B')) * ``` */ addByCallback(target: AsyncMachine, states?: (TStates | BaseStates)[] | (TStates | BaseStates), ...params: any[]): (err?: any, ...params: any[]) => void; addByCallback(target: (TStates | BaseStates)[] | (TStates | BaseStates), states?: any, ...params: any[]): (err?: any, ...params: any[]) => void; /** * Deferred version of [[add]], returning a listener for adding the state. * * Errors need to be handled manually by binding the exception * state to the 'error' event (or equivalent). * * After the call, the related promise object is available as the * [[last_promise]] attribute. * * See [[add]] for the params description. * * Example * ``` * const example = machine(['A', 'B', 'C']) * emitter = new EventEmitter * emitter.on('ready', example.addByListener('A')) * emitter.on('error', example.addByListener('Exception')) * ``` */ addByListener(target: AsyncMachine, states?: (TStates | BaseStates)[] | (TStates | BaseStates), ...params: any[]): (...params: any[]) => void; addByListener(target: (TStates | BaseStates)[] | (TStates | BaseStates), states?: any, ...params: any[]): (...params: any[]) => void; /** * Deferred version of [[add]], adding the requested states on the next event * loop's tick. Useful if you want to start with a fresh stack trace. * * See [[add]] for the params description. * * Example * ``` * const example = machine(['A', 'B', 'C']) * example.add('A') * example.addNext('B') * example.is() // -> ['A', 'B'] * ``` */ addNext(target: AsyncMachine, states: (S | BaseStates)[] | (S | BaseStates), ...params: any[]): number; addNext(target: (TStates | BaseStates)[] | (TStates | BaseStates), states?: any, ...params: any[]): number; /** * De-activates specified states if any of them is currently active. * * @param target OPTIONAL. Pass it if you want to execute a transition on an * external machine, but using this machine's queue. * @param states Array of state names or a single state name. * @param params Params to be passed to the transition handlers (only the ones * belonging to the explicitly requested states, not implied or auto * states). * @return Result of the transition. `false` can mean that either the * requested states weren't accepted or that the transition has been aborted * by one of the negotiation handlers. `null` means that the machine is busy * and the mutation has been queued. * * Basic usage * ``` * const example = machine(['A', 'B', 'C']) * states.drop('A') * states.is() // -> ['A'] * states.drop('B') * states.is() // -> ['B'] * ``` * * State negotiation * ``` * const example = machine(['A', 'B']) * // reject the transition with a negotiation handler * states.A_enter = function() { * return false * } * states.add('A') // -> false * ``` * * Dropping a state on an external machine * ``` * const m1 = machine(['A', 'B']) * const m2 = machine(['C', 'D']) * * m1.A_enter = function() { * // this transition will be queued and executed after the current * // transition is completed * m1.drop(m2, 'C') // -> null * } * ``` */ drop(target: AsyncMachine, states: (S | BaseStates)[] | (S | BaseStates), ...params: any[]): boolean | null; drop(target: (TStates | BaseStates)[] | (TStates | BaseStates), states?: any, ...params: any[]): boolean | null; /** * Deferred version of [[drop]], returning a node-style callback for dropping * the state. Errors are handled automatically and forwarded to the Exception * state. * * After the call, the related promise object is available as the * [[last_promise]] attribute. * * See [[drop]] for the params description. * * Example * * ``` * const example = machine(['A', 'B', 'C']) * someNodeCallback('foo.com', states.dropByCallback('B')) * ``` */ dropByCallback(target: AsyncMachine, states?: (TStates | BaseStates)[] | (TStates | BaseStates), ...params: any[]): (err?: any, ...params: any[]) => void; dropByCallback(target: (TStates | BaseStates)[] | (TStates | BaseStates), states?: any, ...params: any[]): (err?: any, ...params: any[]) => void; /** * Deferred version of [[drop]], returning a listener for dropping the state. * * Errors need to be handled manually by binding the exception * state to the 'error' event (or equivalent). * * After the call, the related promise object is available as the * [[last_promise]] attribute. * * See [[drop]] for the params description. * * Example * ``` * const example = machine(['A', 'B', 'C']) * emitter = new EventEmitter * emitter.on('ready', example.dropByListener('A')) * emitter.on('error', example.setByListener('Exception')) * ``` */ dropByListener(target: AsyncMachine, states?: (TStates | BaseStates)[] | (TStates | BaseStates), ...params: any[]): (...params: any[]) => void; dropByListener(target: (TStates | BaseStates)[] | (TStates | BaseStates), states?: any, ...params: any[]): (...params: any[]) => void; /** * Deferred version of [[drop]], dropping the requested states on the next * event loop's tick. Useful if you want to start with a fresh stack trace. * * See [[drop]] for the params description. * * Example * ``` * const example = machine(['A', 'B', 'C']) * states.add('A') * states.dropNext('A') * states.is('A') // -> true * ``` */ dropNext(target: AsyncMachine, states: (S | BaseStates)[] | (S | BaseStates), ...params: any[]): number; dropNext(target: (TStates | BaseStates)[] | (TStates | BaseStates), states?: any, ...params: any[]): number; /** * Pipes (forwards) a state to another machine. * * @param state Name of the state to pipe. * @param machine Target machine to which the state should be forwarded to. * @param target_state If the target state name should be different, this is * that name. Applicable if only one state is being piped. * @param flags Different modes of piping. See [[PipeFlags]]. * * Piping without negotiation * ``` * const m1 = machine(['A', 'B', 'C']) * const m2 = machine(['A', 'B', 'C']) * m1.pipe('A', m2) * m1.add('A') * m2.is('A') // -> true * ``` * * Piping with negotiation * ``` * import { PipeFlags } from 'asyncmachine' * const m1 = machine(['A', 'B', 'C']) * const m2 = machine(['A', 'B', 'C']) * m2.A_enter = function() { * return false * } * m1.pipe('A', m2, null, PipeFlags.NEGOTIATION) * m1.add('A') * m2.is('A') // -> false * ``` */ pipe(state: (TStates | BaseStates) | (TStates | BaseStates)[], machine: AsyncMachine, target_state?: S, flags?: PipeFlags): void; /** * Pipes all the states from this machine to the `target`. Doesn't pipe the * `Exception` state. * * The exception state is never piped. * * @param target Target machine to which the state should be forwarded. */ pipeAll(target: AsyncMachine, flags?: PipeFlags): void; /** * Removes an existing pipe. All params are optional. * * @param states Source states. Empty means any state. * @param machine Target machine. Empty means any machine. * @param flags Pipe flags. Empty means any flags. * * TODO optimise, if needed */ pipeRemove(states?: (TStates | BaseStates) | (TStates | BaseStates)[], machine?: AsyncMachine, flags?: PipeFlags): void; /** * TODO should remove a binding returned by pipe() and pipeAll() methods */ pipeRemoveBinding(): void; /** * Returns the current tick of the passed state. * * State's clock starts with 0 and on each activation gets incremented * by 1. Ticks lets you keep control flow's integrity across async listeners, * by aborting them once the state changes. Easiest way to get the tick-based * abort function is to use [[getAbort]]. * * @param state Name of the state * @return Current tick of the passed state * * Example * ``` * const example = machine(['A', 'B', 'C']) * example.add('A') * example.add('A') * example.clock('A') // -> 1 * example.drop('A') * example.add('A') * example.clock('A') // -> 2 * ``` */ clock(state: TStates | BaseStates): number; /** * Creates a prototype child with it's own active states, clock, queue and * locks. * * Useful for creating new instances of machines created using the * [[machine]] factory in an efficient manner. * * Example * ``` * const parent = machine(['A', 'B', 'C']) * const child = parent.createChild() * * child.add('A') * child.is() // -> ['A'] * parent.is() // -> [] * ``` * * // TODO write a test */ createChild(): this; /** * Indicates if this instance is currently during a transition. * * When a machine is during a transition, all state changes will be queued * and executed once the transition and the previously queued state * changes are finished. See [[queue]]. * * Example * ``` * const example = machine(['A', 'B', 'C']) * * example.A_enter = function() { * this.duringTransition() // -> true * } * * example.A_state = function() { * this.duringTransition() // -> true * } * * example.add('A') * ``` */ duringTransition(): boolean; /** * Returns the list of active states from which the current transition * started. * * Requires [[duringTranstion]] to be true or it'll throw. */ from(): (TStates | BaseStates)[]; /** * Returns the list of target states which are about to be active after the * transition finishes. * * Requires [[duringTranstion]] to be true or it'll throw. */ to(): (TStates | BaseStates)[]; /** * Returns an abort function, based on the current [[clock]] tick of the * passed state. Optionally allows to next an existing abort function. * * The abort function return a boolean `true` in case the flow for the * specific state should be aborted, because: * - the state has been de-activated (at least once) * - the nested abort function returns `true` * * Example * ``` * const example = machine(['A', 'B', 'C']) * * example.A_state = function() { * const abort = this.getAbort('A') * setTimeout( () => { * if (abort()) return * console.log('never reached') * }, 0) * } * * example.add('A') * example.drop('A') * ``` * * TODO support multiple states * * @param state Name of the state * @param abort Existing abort function (optional) * @return A new abort function */ getAbort(state: TStates | BaseStates, abort?: () => boolean): () => boolean; /** * Resolves the returned promise when all of the passed states are active * (at the same time). Accepts an optional abort function. * * Example * ``` * const example = machine(['A', 'B', 'C']) * example.when(['A', 'B']).then( () => { * console.log('A, B') * } * * example.add('A') * example.add('B') // prints 'A, B' * ``` * * TODO support push cancellation * * @param states List of state names * @param abort Existing abort function (optional) * @return Promise resolved once all states are set simultaneously. */ when(states: (TStates | BaseStates) | (TStates | BaseStates)[], abort?: TAbortFunction): Promise; /** * Resolves the returned promise when all of the passed states are NOT active * (at the same time). Accepts an optional abort function. * * Example * ``` * const example = machine(['A', 'B', 'C']) * example.when(['A', 'B']).then( () => { * console.log('A, B') * } * * example.add('A') * example.add('B') // prints 'A, B' * ``` * * TODO support push cancellation * TODO merge with `when(active, inactive)` * * @param states List of state names * @param abort Existing abort function (optional) * @return Promise resolved once all states are set simultaneously. */ whenNot(states: (TStates | BaseStates) | (TStates | BaseStates)[], abort?: TAbortFunction): Promise; /** * Enables debug messages sent to the console (or the custom handler). * * There's 4 log levels: * - 0: logging is off * - 1: displays only the state changes in a diff format * - 2: displays all operations which happened along with rejected state * changes * - 3: displays more decision logic * - 4: displays everything, including all possible handlers * * Example * ``` * const example = machine(['A', 'B', 'C']) * example.logLevel(1) * example.add('A') * // -> [add] state Enabled * // -> [state] +Enabled * ``` * * @param prefix Prefix before all console messages. * @param level Error level (1-3). */ logLevel(log_level: number | string): this; logLevel(): number; /** * Managed the ID of a machine. * * Sets or gets and also support returning a normalized version. * * Example * ``` * const example = machine() * // set * example.id('a b c') * // get * example.id() // -> 'a b c' * // get normalized * example.id(true) // -> 'a-b-c' * ``` */ id(id: string): this; id(get_normalized: true): string; id(): string; /** * TODO docs * TODO rename TPipeBindings to TPipeBinding * TODO copy these to once() and emit() */ // @ts-ignore /**/on: TBind & IBind; // @ts-ignore /**/on(event: string, listener: Function, context?: Object): this; /** * TODO docs * TODO extract eventToStateName(name: string): (TStates | States) * and loose the type casting */ // @ts-ignore /**/once: TBind & IBind; // @ts-ignore /**/once(event: string, listener: Function, context?: Object): this; // @ts-ignore /**/emit: TEmit & IEmit; /** * Binds the Exception state to the promise error handler. Handy when working * with promises. * * See [[Exception_state]]. * * @param promise The promise to handle * @param target_states States for which the promise was created (the * one that failed). * @return The source promise, for piping. */ catchPromise(promise: T, target_states?: (TStates | BaseStates)[]): T; /** * Diffs two state sets and returns the ones present only in the first one. * * @param states1 List of source states. * @param states2 List of states to diff against (picking up the non * existing ones). * @return List of states in states1, but not in states2. */ diffStates(states1: (TStates | BaseStates)[], states2: (TStates | BaseStates)[]): (TStates | "Exception")[]; /** * Push a new message to the log with an optional level (defaults to 1). * * @param {string} msg * @param {number} level */ log(msg: string, level?: number): void; /** * Returns a string representation of the machine using the [[statesToString]] * method. */ toString(): string; /** * Returns a string representation of the machine (name and the list of * states), optionally including the in-active states. * * @param include_inactive */ statesToString(include_inactive?: boolean): string; protected getPipeBindings(flags?: PipeFlags): TPipeBindings; protected getPipeFlagsLabels(flags: PipeFlags): any[]; protected pipeBind(states: (TStates | BaseStates) | (TStates | BaseStates)[], machine: AsyncMachine, requested_state?: S | null, flags?: PipeFlags): void; /** * Override for EventEmitter method calling a specific listener. Binds to * a promise if returned by the listener. * * TODO incorporate into EE, saving one call stack frame */ protected callListener(listener: Function, context: Object, params: any[]): any; setImmediate(fn: Function, ...params: any[]): any; /** * Return `true` if the current state differs from the `states_before`. * * @param states_before List of previously active states. */ hasStateChanged(states_before: (TStates | BaseStates)[]): boolean; /** * Parse state names, check if they exist and always return an array. Throws * in of an error. * * @param states State names to check * @return An array of valid state names. */ parseStates(states: (TStates | BaseStates) | (TStates | BaseStates)[]): (TStates | BaseStates)[]; /** * Returns the JSON structure of states along with their relations. * * @return JSON version of the current machine. Valid input for the * [[machine]] factory. */ states(): { [K in TStates | BaseStates]: IState; }; private enqueue_(type, states, params?, target?); /** * Detect queue duplicates for mutations without params. * * 1. Check if a mutation is scheduled * 2. Check if a counter mutation isn't scheduled later * * @param machine The machine which queue is being checked * @param target Target to mutate * @param mutation_type * @param states * @private * * TODO write a test */ private detectQueueDuplicates_(target, mutation_type, states); private processQueue_(); private createDeferred(fn, target, states, state_params); private createCallback(deferred); private createListener(deferred); /** * Sets the new active states incrementing the counters. * * @return An array of previously active states. */ setActiveStates_(explicite_states: (TStates | BaseStates)[], target: (TStates | BaseStates)[]): (TStates | BaseStates)[]; /** * Returns an object for a given handler. * @param name Handler's name */ getMethodContext(name: string): Object | null; private bindToStates(states, listener, abort?); private bindToNotStates(states, listener, abort?); private getAbortFunction(state, tick, abort?); }