UNPKG

jssm

Version:

A Javascript finite state machine (FSM) with a terse DSL and a simple API. Most FSMs are one-liners. Fast, easy, powerful, well tested, typed with TypeScript, and visualizations. MIT License.

1,231 lines 78.4 kB
declare type StateType = string; import { JssmGenericState, JssmGenericConfig, JssmStateConfig, JssmTransition, JssmTransitionList, // JssmTransitionRule, JssmMachineInternalState, JssmAllowsOverride, JssmStateDeclaration, JssmStateStyleKeyList, JssmLayout, JssmHistory, JssmSerialization, FslDirection, FslDirections, FslTheme, HookDescription, HookHandler, HookContext, HookResult, HookComplexResult, EverythingHookContext, EverythingHookHandler, PostEverythingHookHandler, JssmRng } from './jssm_types'; import { arrow_direction, arrow_left_kind, arrow_right_kind } from './jssm_arrow'; import { compile, make, wrap_parse } from './jssm_compiler'; import { seq, unique, find_repeated, weighted_rand_select, weighted_sample_select, histograph, weighted_histo_key, gen_splitmix32, sleep } from './jssm_util'; import * as constants from './jssm_constants'; declare const shapes: string[], gviz_shapes: string[], named_colors: string[], state_name_chars: readonly { from: string; to: string; }[], state_name_first_chars: readonly { from: string; to: string; }[], action_label_chars: readonly { from: string; to: string; }[]; import { version, build_time } from './version'; /********* * * An internal method meant to take a series of declarations and fold them into * a single multi-faceted declaration, in the process of building a state. Not * generally meant for external use. * * @internal * */ declare function transfer_state_properties(state_decl: JssmStateDeclaration): JssmStateDeclaration; /** * * Collapse a list of individual state-style key/value pairs into a single * {@link JssmStateConfig} object, remapping FSL-style kebab-case keys to the * camelCase field names the runtime uses. * * The parser emits state styling as a flat array like * `[{ key: 'color', value: 'red' }, { key: 'line-style', value: 'dashed' }]` * because that is the most natural shape for the grammar to produce. This * helper runs once per style bucket during `Machine` construction to turn * those arrays into the compact `{ color, lineStyle, ... }` objects the * graph-rendering code expects. * * ```typescript * state_style_condense([ * { key: 'color', value: 'red' }, * { key: 'shape', value: 'oval' }, * { key: 'line-style', value: 'dashed' } * ]); * // => { color: 'red', shape: 'oval', lineStyle: 'dashed' } * * state_style_condense(undefined); * // => {} * ``` * * @param jssk The list of style keys to condense. `undefined` is accepted * and yields an empty config. * * @param machine Optional `Machine` reference, used only so that any * {@link JssmError} thrown can point at the offending machine in its * diagnostic message. * * @returns A `JssmStateConfig` object containing every key from `jssk` * remapped into its camelCase field. * * @throws {JssmError} If `jssk` is neither an array nor `undefined`, if any * element is not an object, if the same key appears more than once, or if a * key is not one of the recognized style names. * * @internal * */ declare function state_style_condense(jssk: JssmStateStyleKeyList, machine?: any): JssmStateConfig; /******* * * Core finite state machine class. Holds the full graph of states and * transitions, the current state, hooks, data, properties, and all runtime * behavior. Typically created via the {@link sm} tagged template literal * rather than constructed directly. * * ```typescript * import { sm } from 'jssm'; * * const light = sm`Red 'next' => Green 'next' => Yellow 'next' => Red;`; * light.state(); // 'Red' * light.action('next'); // true * light.state(); // 'Green' * ``` * * @typeparam mDT The machine data type — the type of the value stored in * `.data()`. Defaults to `undefined` when no data is used. * */ declare class Machine<mDT> { _state: StateType; _states: Map<StateType, JssmGenericState>; _edges: Array<JssmTransition<StateType, mDT>>; _edge_map: Map<StateType, Map<StateType, number>>; _named_transitions: Map<StateType, number>; _actions: Map<StateType, Map<StateType, number>>; _reverse_actions: Map<StateType, Map<StateType, number>>; _reverse_action_targets: Map<StateType, Map<StateType, number>>; _start_states: Set<StateType>; _end_states: Set<StateType>; _machine_author?: Array<string>; _machine_comment?: string; _machine_contributor?: Array<string>; _machine_definition?: string; _machine_language?: string; _machine_license?: string; _machine_name?: string; _machine_version?: string; _fsl_version?: string; _raw_state_declaration?: Array<Object>; _state_declarations: Map<StateType, JssmStateDeclaration>; _data?: mDT; _instance_name: string; _rng_seed: number; _rng: JssmRng; _graph_layout: JssmLayout; _dot_preamble: string; _arrange_declaration: Array<Array<StateType>>; _arrange_start_declaration: Array<Array<StateType>>; _arrange_end_declaration: Array<Array<StateType>>; _themes: FslTheme[]; _flow: FslDirection; _has_hooks: boolean; _has_basic_hooks: boolean; _has_named_hooks: boolean; _has_entry_hooks: boolean; _has_exit_hooks: boolean; _has_after_hooks: boolean; _has_global_action_hooks: boolean; _has_transition_hooks: boolean; _has_forced_transitions: boolean; _hooks: Map<string, HookHandler<mDT>>; _named_hooks: Map<string, HookHandler<mDT>>; _entry_hooks: Map<string, HookHandler<mDT>>; _exit_hooks: Map<string, HookHandler<mDT>>; _after_hooks: Map<string, HookHandler<mDT>>; _global_action_hooks: Map<string, HookHandler<mDT>>; _any_action_hook: HookHandler<mDT> | undefined; _standard_transition_hook: HookHandler<mDT> | undefined; _main_transition_hook: HookHandler<mDT> | undefined; _forced_transition_hook: HookHandler<mDT> | undefined; _any_transition_hook: HookHandler<mDT> | undefined; _has_post_hooks: boolean; _has_post_basic_hooks: boolean; _has_post_named_hooks: boolean; _has_post_entry_hooks: boolean; _has_post_exit_hooks: boolean; _has_post_global_action_hooks: boolean; _has_post_transition_hooks: boolean; _code_allows_override: JssmAllowsOverride; _config_allows_override: JssmAllowsOverride; _post_hooks: Map<string, HookHandler<mDT>>; _post_named_hooks: Map<string, HookHandler<mDT>>; _post_entry_hooks: Map<string, HookHandler<mDT>>; _post_exit_hooks: Map<string, HookHandler<mDT>>; _post_global_action_hooks: Map<string, HookHandler<mDT>>; _post_any_action_hook: HookHandler<mDT> | undefined; _post_standard_transition_hook: HookHandler<mDT> | undefined; _post_main_transition_hook: HookHandler<mDT> | undefined; _post_forced_transition_hook: HookHandler<mDT> | undefined; _post_any_transition_hook: HookHandler<mDT> | undefined; _pre_everything_hook: EverythingHookHandler<mDT> | undefined; _everything_hook: EverythingHookHandler<mDT> | undefined; _pre_post_everything_hook: PostEverythingHookHandler<mDT> | undefined; _post_everything_hook: PostEverythingHookHandler<mDT> | undefined; _property_keys: Set<string>; _default_properties: Map<string, any>; _state_properties: Map<string, any>; _required_properties: Set<string>; _history: JssmHistory<mDT>; _history_length: number; _state_style: JssmStateConfig; _active_state_style: JssmStateConfig; _hooked_state_style: JssmStateConfig; _terminal_state_style: JssmStateConfig; _start_state_style: JssmStateConfig; _end_state_style: JssmStateConfig; _state_labels: Map<string, string>; _time_source: () => number; _create_started: number; _created: number; _after_mapping: Map<string, [string, number]>; _timeout_source: (Function: any, number: any) => number; _clear_timeout_source: (h: any) => void; _timeout_handle: number | undefined; _timeout_target: string | undefined; _timeout_target_time: number | undefined; constructor({ start_states, end_states, initial_state, start_states_no_enforce, complete, transitions, machine_author, machine_comment, machine_contributor, machine_definition, machine_language, machine_license, machine_name, machine_version, state_declaration, property_definition, state_property, fsl_version, dot_preamble, arrange_declaration, arrange_start_declaration, arrange_end_declaration, theme, flow, graph_layout, instance_name, history, data, default_state_config, default_active_state_config, default_hooked_state_config, default_terminal_state_config, default_start_state_config, default_end_state_config, allows_override, config_allows_override, rng_seed, time_source, timeout_source, clear_timeout_source }: JssmGenericConfig<StateType, mDT>); /******** * * Internal method for fabricating states. Not meant for external use. * * @internal * */ _new_state(state_config: JssmGenericState): StateType; /********* * * Get the current state of a machine. * * ```typescript * import * as jssm from 'jssm'; * * const lswitch = jssm.from('on <=> off;'); * console.log( lswitch.state() ); // 'on' * * lswitch.transition('off'); * console.log( lswitch.state() ); // 'off' * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @returns The current state name. * */ state(): StateType; /********* * * Get the label for a given state, if any; return `undefined` otherwise. * * ```typescript * import * as jssm from 'jssm'; * * const lswitch = jssm.from('a -> b; state a: { label: "Foo!"; };'); * console.log( lswitch.label_for('a') ); // 'Foo!' * console.log( lswitch.label_for('b') ); // undefined * ``` * * See also {@link display_text}. * * @typeparam mDT The type of the machine data member; usually omitted * * @param state The state to get the label for. * * @returns The label string, or `undefined` if no label is set. * */ label_for(state: StateType): string; /********* * * Get whatever the node should show as text. * * Currently, this means to get the label for a given state, if any; * otherwise to return the node's name. However, this definition is expected * to grow with time, and it is currently considered ill-advised to manually * parse this text. * * See also {@link label_for}. * * ```typescript * import * as jssm from 'jssm'; * * const lswitch = jssm.from('a -> b; state a: { label: "Foo!"; };'); * console.log( lswitch.display_text('a') ); // 'Foo!' * console.log( lswitch.display_text('b') ); // 'b' * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @param state The state to get display text for. * * @returns The label if one exists, otherwise the state's name. * */ display_text(state: StateType): string; /********* * * Get the current data of a machine. * * ```typescript * import * as jssm from 'jssm'; * * const lswitch = jssm.from('on <=> off;', {data: 1}); * console.log( lswitch.data() ); // 1 * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @returns A deep clone of the machine's current data value. * */ data(): mDT; /********* * * Get the current value of a given property name. Checks the current * state's properties first, then falls back to the global default. * Returns `undefined` if neither exists. For a throwing variant, see * {@link strict_prop}. * * ```typescript * const m = sm`property color default "grey"; a -> b; * state b: { property color "blue"; };`; * * m.prop('color'); // 'grey' (default, because state is 'a') * m.go('b'); * m.prop('color'); // 'blue' (state 'b' overrides the default) * m.prop('size'); // undefined (no such property) * ``` * * @param name The relevant property name to look up. * * @returns The value behind the prop name, or `undefined` if not defined. * */ prop(name: string): any; /********* * * Get the current value of a given property name. If missing on the state * and without a global default, throws a {@link JssmError}, unlike * {@link prop}, which would return `undefined` instead. * * ```typescript * const m = sm`property color default "grey"; a -> b;`; * * m.strict_prop('color'); // 'grey' * m.strict_prop('size'); // throws JssmError * ``` * * @param name The relevant property name to look up. * * @returns The value behind the prop name. * * @throws {JssmError} If the property is not defined on the current state * and has no default. * */ strict_prop(name: string): any; /********* * * Get the current value of every prop, as an object. If no current definition * exists for a prop — that is, if the prop was defined without a default and * the current state also doesn't define the prop — then that prop will be listed * in the returned object with a value of `undefined`. * * ```typescript * const traffic_light = sm` * * property can_go default true; * property hesitate default true; * property stop_first default false; * * Off -> Red => Green => Yellow => Red; * [Red Yellow Green] ~> [Off FlashingRed]; * FlashingRed -> Red; * * state Red: { property stop_first true; property can_go false; }; * state Off: { property stop_first true; }; * state FlashingRed: { property stop_first true; }; * state Green: { property hesitate false; }; * * `; * * traffic_light.state(); // Off * traffic_light.props(); // { can_go: true, hesitate: true, stop_first: true; } * * traffic_light.go('Red'); * traffic_light.props(); // { can_go: false, hesitate: true, stop_first: true; } * * traffic_light.go('Green'); * traffic_light.props(); // { can_go: true, hesitate: false, stop_first: false; } * ``` * * @returns An object mapping every known property name to its current value * (or `undefined` if the property has no default and the current state * doesn't define it). * */ props(): object; /********* * * Check whether a given string is a known property's name. * * ```typescript * const example = sm`property foo default 1; a->b;`; * * example.known_prop('foo'); // true * example.known_prop('bar'); // false * ``` * * @param prop_name The relevant property name to look up * */ known_prop(prop_name: string): boolean; /********* * * List all known property names. If you'd also like values, use * {@link props} instead. The order of the properties is not defined, and * the properties generally will not be sorted. * * ```typescript * const m = sm`property color default "grey"; property size default 1; a -> b;`; * * m.known_props(); // ['color', 'size'] * ``` * * @returns An array of all property name strings defined on this machine. * */ known_props(): string[]; /******** * * Check whether a given state is a valid start state (either because it was * explicitly named as such, or because it was the first mentioned state.) * * ```typescript * import { sm, is_start_state } from 'jssm'; * * const example = sm`a -> b;`; * * console.log( final_test.is_start_state('a') ); // true * console.log( final_test.is_start_state('b') ); // false * * const example = sm`start_states: [a b]; a -> b;`; * * console.log( final_test.is_start_state('a') ); // true * console.log( final_test.is_start_state('b') ); // true * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @param whichState The name of the state to check * */ is_start_state(whichState: StateType): boolean; /******** * * Check whether a given state is a valid start state (either because it was * explicitly named as such, or because it was the first mentioned state.) * * ```typescript * import { sm, is_end_state } from 'jssm'; * * const example = sm`a -> b;`; * * console.log( final_test.is_start_state('a') ); // false * console.log( final_test.is_start_state('b') ); // true * * const example = sm`end_states: [a b]; a -> b;`; * * console.log( final_test.is_start_state('a') ); // true * console.log( final_test.is_start_state('b') ); // true * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @param whichState The name of the state to check * */ is_end_state(whichState: StateType): boolean; /******** * * Check whether a given state is final (either has no exits or is marked * `complete`.) * * ```typescript * import { sm, state_is_final } from 'jssm'; * * const final_test = sm`first -> second;`; * * console.log( final_test.state_is_final('first') ); // false * console.log( final_test.state_is_final('second') ); // true * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @param whichState The name of the state to check for finality * */ state_is_final(whichState: StateType): boolean; /******** * * Check whether the current state is final (either has no exits or is marked * `complete`.) * * ```typescript * import { sm, is_final } from 'jssm'; * * const final_test = sm`first -> second;`; * * console.log( final_test.is_final() ); // false * state.transition('second'); * console.log( final_test.is_final() ); // true * ``` * */ is_final(): boolean; /******** * * Serialize the current machine, including all defining state but not the * machine string, to a structure. This means you will need the machine * string to recreate (to not waste repeated space;) if you want the machine * string embedded, call {@link serialize_with_string} instead. * * @typeparam mDT The type of the machine data member; usually omitted * * @param comment An optional comment string to embed in the serialized * output for identification or debugging. * * @returns A {@link JssmSerialization} object containing the machine's * current state, data, and timestamp. * */ serialize(comment?: string | undefined): JssmSerialization<mDT>; /** Get the graph layout direction (e.g. `'LR'`, `'TB'`). Set via the * FSL `graph_layout` directive. * @returns The layout string, or the default if not set. */ graph_layout(): string; /** Get the Graphviz DOT preamble string, injected before the graph body * during visualization. Set via the FSL `dot_preamble` directive. * @returns The preamble string. */ dot_preamble(): string; /** Get the machine's author list. Set via the FSL `machine_author` directive. * @returns An array of author name strings. */ machine_author(): Array<string>; /** Get the machine's comment string. Set via the FSL `machine_comment` directive. * @returns The comment string. */ machine_comment(): string; /** Get the machine's contributor list. Set via the FSL `machine_contributor` directive. * @returns An array of contributor name strings. */ machine_contributor(): Array<string>; /** Get the machine's definition string. Set via the FSL `machine_definition` directive. * @returns The definition string. */ machine_definition(): string; /** Get the machine's language (ISO 639-1). Set via the FSL `machine_language` directive. * @returns The language code string. */ machine_language(): string; /** Get the machine's license string. Set via the FSL `machine_license` directive. * @returns The license string. */ machine_license(): string; /** Get the machine's name. Set via the FSL `machine_name` directive. * @returns The machine name string. */ machine_name(): string; /** Get the machine's version string. Set via the FSL `machine_version` directive. * @returns The version string. */ machine_version(): string; /** Get the raw state declaration objects as parsed from the FSL source. * @returns An array of raw state declaration objects. */ raw_state_declarations(): Array<Object>; /** Get the processed state declaration for a specific state. * @param which - The state to look up. * @returns The {@link JssmStateDeclaration} for the given state. */ state_declaration(which: StateType): JssmStateDeclaration; /** Get all processed state declarations as a Map. * @returns A `Map` from state name to {@link JssmStateDeclaration}. */ state_declarations(): Map<StateType, JssmStateDeclaration>; /** Get the FSL language version this machine was compiled under. * @returns The FSL version string. */ fsl_version(): string; /** Get the complete internal state of the machine as a serializable * structure. Includes actions, edges, edge map, named transitions, * reverse actions, current state, and states map. * @returns A {@link JssmMachineInternalState} snapshot. */ machine_state(): JssmMachineInternalState<mDT>; /********* * * List all the states known by the machine. Please note that the order of * these states is not guaranteed. * * ```typescript * import * as jssm from 'jssm'; * * const lswitch = jssm.from('on <=> off;'); * console.log( lswitch.states() ); // ['on', 'off'] * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @returns An array of all state names in the machine. * */ states(): Array<StateType>; /** Get the internal state descriptor for a given state name. * @param whichState - The state to look up. * @returns The {@link JssmGenericState} descriptor. * @throws {JssmError} If the state does not exist. */ state_for(whichState: StateType): JssmGenericState; /********* * * Check whether the machine knows a given state. * * ```typescript * import * as jssm from 'jssm'; * * const lswitch = jssm.from('on <=> off;'); * * console.log( lswitch.has_state('off') ); // true * console.log( lswitch.has_state('dance') ); // false * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @param whichState The state to be checked for existence. * * @returns `true` if the state exists, `false` otherwise. * */ has_state(whichState: StateType): boolean; /********* * * Lists all edges of a machine. * * ```typescript * import { sm } from 'jssm'; * * const lswitch = sm`on 'toggle' <=> 'toggle' off;`; * * lswitch.list_edges(); * [ * { * from: 'on', * to: 'off', * kind: 'main', * forced_only: false, * main_path: true, * action: 'toggle' * }, * { * from: 'off', * to: 'on', * kind: 'main', * forced_only: false, * main_path: true, * action: 'toggle' * } * ] * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @returns An array of all {@link JssmTransition} edge objects. * */ list_edges(): Array<JssmTransition<StateType, mDT>>; /** Get the map of named transitions (transitions with explicit names). * @returns A `Map` from transition name to edge index. */ list_named_transitions(): Map<StateType, number>; /** List all distinct action names defined anywhere in the machine. * @returns An array of action name strings. */ list_actions(): Array<StateType>; /** Whether any actions are defined on this machine. * @returns `true` if the machine has at least one action. */ get uses_actions(): boolean; /** Whether any forced (`~>`) transitions exist in this machine. * @returns `true` if at least one forced transition is defined. */ get uses_forced_transitions(): boolean; /********* * * Check if the code that built the machine allows overriding state and data. * * @returns The override permission from the FSL source code. * */ get code_allows_override(): JssmAllowsOverride; /********* * * Check if the machine config allows overriding state and data. * * @returns The override permission from the runtime config. * */ get config_allows_override(): JssmAllowsOverride; /********* * * Check if a machine allows overriding state and data. Resolves the * combined effect of code and config permissions — config may not be * less strict than code. * * @returns The effective override permission. * */ get allows_override(): JssmAllowsOverride; /** List all available theme names. * @returns An array of theme name strings. */ all_themes(): FslTheme[]; /** List the character ranges accepted by the FSL grammar in any but the * first position of a state name (atom). Each entry is an inclusive * `{from, to}` range of single Unicode characters. * * @returns An array of `{from, to}` inclusive character ranges. * * @example * const m = sm`a -> b;`; * m.all_state_name_chars().some(r => '+' >= r.from && '+' <= r.to); // true */ all_state_name_chars(): ReadonlyArray<{ from: string; to: string; }>; /** List the character ranges accepted by the FSL grammar in the first * position of a state name (atom). Narrower than * {@link all_state_name_chars}: notably omits `+`, `(`, `)`, `&`, `#`, `@`. * * @returns An array of `{from, to}` inclusive character ranges. * * @example * const m = sm`a -> b;`; * m.all_state_name_first_chars().some(r => '+' >= r.from && '+' <= r.to); // false */ all_state_name_first_chars(): ReadonlyArray<{ from: string; to: string; }>; /** List the character ranges accepted inside a single-quoted FSL action * label without escaping. Space is allowed; the apostrophe `'` is * explicitly excluded since it terminates the label. * * @returns An array of `{from, to}` inclusive character ranges. * * @example * const m = sm`a -> b;`; * m.all_action_label_chars().some(r => ' ' >= r.from && ' ' <= r.to); // true * m.all_action_label_chars().some(r => "'" >= r.from && "'" <= r.to); // false */ all_action_label_chars(): ReadonlyArray<{ from: string; to: string; }>; /** Get the active theme(s) for this machine. Always stored as an array * internally; the union return type exists for setter compatibility. * @returns The current theme or array of themes. */ get themes(): FslTheme | FslTheme[]; /** Set the active theme(s). Accepts a single theme name or an array. * @param to - A theme name or array of theme names to apply. */ set themes(to: FslTheme | FslTheme[]); /** Get the flow direction for graph layout (e.g. `'right'`, `'down'`). * Set via the FSL `flow` directive. * @returns The current flow direction. */ flow(): FslDirection; /** Look up a transition's edge index by source and target state names. * @param from - Source state name. * @param to - Target state name. * @returns The edge index in the edges array, or `undefined` if no * such transition exists. */ get_transition_by_state_names(from: StateType, to: StateType): number; /** Look up the full transition object for a given source→target pair. * @param from - Source state name. * @param to - Target state name. * @returns The {@link JssmTransition} object, or `undefined` if none exists. */ lookup_transition_for(from: StateType, to: StateType): JssmTransition<StateType, mDT>; /******** * * List all transitions attached to the current state, sorted by entrance and * exit. The order of each sublist is not defined. A node could appear in * both lists. * * ```typescript * import { sm } from 'jssm'; * * const light = sm`red 'next' -> green 'next' -> yellow 'next' -> red; [red yellow green] 'shutdown' ~> off 'start' -> red;`; * * light.state(); // 'red' * light.list_transitions(); // { entrances: [ 'yellow', 'off' ], exits: [ 'green', 'off' ] } * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @param whichState The state whose transitions to have listed * */ list_transitions(whichState?: StateType): JssmTransitionList; /******** * * List all entrances attached to the current state. Please note that the * order of the list is not defined. This list includes both unforced and * forced entrances; if this isn't desired, consider * {@link list_unforced_entrances} or {@link list_forced_entrances} as * appropriate. * * ```typescript * import { sm } from 'jssm'; * * const light = sm`red 'next' -> green 'next' -> yellow 'next' -> red; [red yellow green] 'shutdown' ~> off 'start' -> red;`; * * light.state(); // 'red' * light.list_entrances(); // [ 'yellow', 'off' ] * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @param whichState The state whose entrances to have listed * */ list_entrances(whichState?: StateType): Array<StateType>; /******** * * List all exits attached to the current state. Please note that the order * of the list is not defined. This list includes both unforced and forced * exits; if this isn't desired, consider {@link list_unforced_exits} or * {@link list_forced_exits} as appropriate. * * ```typescript * import { sm } from 'jssm'; * * const light = sm`red 'next' -> green 'next' -> yellow 'next' -> red; [red yellow green] 'shutdown' ~> off 'start' -> red;`; * * light.state(); // 'red' * light.list_exits(); // [ 'green', 'off' ] * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @param whichState The state whose exits to have listed * */ list_exits(whichState?: StateType): Array<StateType>; /** Get the transitions available from a state for use by the probabilistic * walk system. * * If any exit declares a `probability`, only those probability-bearing * exits are returned, so that non-probability peers cannot dilute the * declared distribution. If no exit declares a `probability`, every * legal (non-forced) exit is returned, which `weighted_rand_select` * treats as equal weight. Forced-only exits (`~>`) are always excluded, * since they cannot be taken by an ordinary `transition()` call. * * Fixes StoneCypher/fsl#1325, in which the function previously returned * every exit unconditionally — including forced-only exits and exits * with no `probability`, which distorted the weighted distribution. * * @param whichState - The state to inspect. * @returns An array of {@link JssmTransition} edges exiting the state, * filtered as described above. May be empty. * @throws {JssmError} If the state does not exist. */ probable_exits_for(whichState: StateType): Array<JssmTransition<StateType, mDT>>; /** Take a single random transition from the current state, weighted by * edge probabilities. * @returns `true` if a transition was taken, `false` otherwise. */ probabilistic_transition(): boolean; /** Take `n` consecutive probabilistic transitions and return the sequence * of states visited (before each transition). * @param n - Number of steps to walk. * @returns An array of state names visited during the walk. */ probabilistic_walk(n: number): Array<StateType>; /** Take `n` probabilistic steps and return a histograph of how many times * each state was visited. * @param n - Number of steps to walk. * @returns A `Map` from state name to visit count. */ probabilistic_histo_walk(n: number): Map<StateType, number>; /******** * * List all actions available from this state. Please note that the order of * the actions is not guaranteed. * * ```typescript * import { sm } from 'jssm'; * * const machine = sm` * red 'next' -> green 'next' -> yellow 'next' -> red; * [red yellow green] 'shutdown' ~> off 'start' -> red; * `; * * console.log( machine.state() ); // logs 'red' * console.log( machine.actions() ); // logs ['next', 'shutdown'] * * machine.action('next'); // true * console.log( machine.state() ); // logs 'green' * console.log( machine.actions() ); // logs ['next', 'shutdown'] * * machine.action('shutdown'); // true * console.log( machine.state() ); // logs 'off' * console.log( machine.actions() ); // logs ['start'] * * machine.action('start'); // true * console.log( machine.state() ); // logs 'red' * console.log( machine.actions() ); // logs ['next', 'shutdown'] * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @param whichState The state whose actions to list. Defaults to the * current state. * * @returns An array of action names available from the given state. * */ actions(whichState?: StateType): Array<StateType>; /******** * * List all states that have a specific action attached. Please note that * the order of the states is not guaranteed. * * ```typescript * import { sm } from 'jssm'; * * const machine = sm` * red 'next' -> green 'next' -> yellow 'next' -> red; * [red yellow green] 'shutdown' ~> off 'start' -> red; * `; * * console.log( machine.list_states_having_action('next') ); // ['red', 'green', 'yellow'] * console.log( machine.list_states_having_action('start') ); // ['off'] * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @param whichState The action to be checked for associated states * */ list_states_having_action(whichState: StateType): Array<StateType>; /** List all action names available as exits from a given state. * @param whichState - The state to inspect. Defaults to the current state. * @returns An array of action name strings. * @throws {JssmError} If the state does not exist. */ list_exit_actions(whichState?: StateType): Array<StateType>; /** List all action exits from a state with their probabilities. * @param whichState - The state to inspect. Defaults to the current state. * @returns An array of `{ action, probability }` objects. * @throws {JssmError} If the state does not exist. */ probable_action_exits(whichState?: StateType): Array<any>; /** Check whether a state has no incoming transitions (unreachable after start). * @param whichState - The state to check. * @returns `true` if the state has zero entrances. * @throws {JssmError} If the state does not exist. */ is_unenterable(whichState: StateType): boolean; /** Check whether any state in the machine is unenterable. * @returns `true` if at least one state has no incoming transitions. */ has_unenterables(): boolean; /** Check whether the current state is terminal (has no exits). * @returns `true` if the current state has zero exits. */ is_terminal(): boolean; /** Check whether a specific state is terminal (has no exits). * @param whichState - The state to check. * @returns `true` if the state has zero exits. * @throws {JssmError} If the state does not exist. */ state_is_terminal(whichState: StateType): boolean; /** Check whether any state in the machine is terminal. * @returns `true` if at least one state has no exits. */ has_terminals(): boolean; /** Check whether the current state is complete (every exit has an action). * @returns `true` if the current state is complete. */ is_complete(): boolean; /** Check whether a specific state is complete (every exit has an action). * @param whichState - The state to check. * @returns `true` if the state is complete. * @throws {JssmError} If the state does not exist. */ state_is_complete(whichState: StateType): boolean; /** Check whether any state in the machine is complete. * @returns `true` if at least one state is complete. */ has_completes(): boolean; /** Low-level hook registration. Installs a handler described by a * {@link HookDescription} into the appropriate internal map. Prefer the * convenience wrappers ({@link hook}, {@link hook_entry}, etc.) over * calling this directly. * @param HookDesc - A hook descriptor specifying kind, states, and handler. */ set_hook(HookDesc: HookDescription<mDT>): void; /** Register a pre-transition hook on a specific edge. Fires before * transitioning from `from` to `to`. If the handler returns `false`, the * transition is blocked. * * ```typescript * const m = sm`a -> b -> c;`; * m.hook('a', 'b', () => console.log('a->b')); * ``` * * @param from - Source state name. * @param to - Target state name. * @param handler - Callback invoked before the transition. * @returns `this` for chaining. */ hook(from: string, to: string, handler: HookHandler<mDT>): Machine<mDT>; /** Register a pre-transition hook on a specific action-labeled edge. * @param from - Source state name. * @param to - Target state name. * @param action - The action label that triggers this hook. * @param handler - Callback invoked before the transition. * @returns `this` for chaining. */ hook_action(from: string, to: string, action: string, handler: HookHandler<mDT>): Machine<mDT>; /** Register a pre-transition hook on any edge triggered by a specific action. * @param action - The action name to hook. * @param handler - Callback invoked before any transition with this action. * @returns `this` for chaining. */ hook_global_action(action: string, handler: HookHandler<mDT>): Machine<mDT>; /** Register a pre-transition hook on any action-driven transition. * @param handler - Callback invoked before any action transition. * @returns `this` for chaining. */ hook_any_action(handler: HookHandler<mDT>): Machine<mDT>; /** Register a pre-transition hook on any standard (`->`) transition. * @param handler - Callback invoked before any legal transition. * @returns `this` for chaining. */ hook_standard_transition(handler: HookHandler<mDT>): Machine<mDT>; /** Register a pre-transition hook on any main-path (`=>`) transition. * @param handler - Callback invoked before any main transition. * @returns `this` for chaining. */ hook_main_transition(handler: HookHandler<mDT>): Machine<mDT>; /** Register a pre-transition hook on any forced (`~>`) transition. * @param handler - Callback invoked before any forced transition. * @returns `this` for chaining. */ hook_forced_transition(handler: HookHandler<mDT>): Machine<mDT>; /** Register a pre-transition hook on any transition regardless of kind. * @param handler - Callback invoked before every transition. * @returns `this` for chaining. */ hook_any_transition(handler: HookHandler<mDT>): Machine<mDT>; /** Register a hook that fires when entering a specific state. * @param to - The state being entered. * @param handler - Callback invoked on entry. * @returns `this` for chaining. */ hook_entry(to: string, handler: HookHandler<mDT>): Machine<mDT>; /** Register a hook that fires when leaving a specific state. * @param from - The state being exited. * @param handler - Callback invoked on exit. * @returns `this` for chaining. */ hook_exit(from: string, handler: HookHandler<mDT>): Machine<mDT>; /** Register a hook that fires after leaving a specific state (post-exit). * @param from - The state that was exited. * @param handler - Callback invoked after exit completes. * @returns `this` for chaining. */ hook_after(from: string, handler: HookHandler<mDT>): Machine<mDT>; /** Post-transition hook on a specific edge. Fires after the transition * from `from` to `to` has completed. Cannot block the transition. * @param from - Source state name. * @param to - Target state name. * @param handler - Callback invoked after the transition. * @returns `this` for chaining. */ post_hook(from: string, to: string, handler: HookHandler<mDT>): Machine<mDT>; /** Post-transition hook on a specific action-labeled edge. * @param from - Source state name. * @param to - Target state name. * @param action - The action label. * @param handler - Callback invoked after the transition. * @returns `this` for chaining. */ post_hook_action(from: string, to: string, action: string, handler: HookHandler<mDT>): Machine<mDT>; /** Post-transition hook on any edge triggered by a specific action. * @param action - The action name. * @param handler - Callback invoked after any transition with this action. * @returns `this` for chaining. */ post_hook_global_action(action: string, handler: HookHandler<mDT>): Machine<mDT>; /** Post-transition hook on any action-driven transition. * @param handler - Callback invoked after any action transition. * @returns `this` for chaining. */ post_hook_any_action(handler: HookHandler<mDT>): Machine<mDT>; /** Post-transition hook on any standard (`->`) transition. * @param handler - Callback invoked after any legal transition. * @returns `this` for chaining. */ post_hook_standard_transition(handler: HookHandler<mDT>): Machine<mDT>; /** Post-transition hook on any main-path (`=>`) transition. * @param handler - Callback invoked after any main transition. * @returns `this` for chaining. */ post_hook_main_transition(handler: HookHandler<mDT>): Machine<mDT>; /** Post-transition hook on any forced (`~>`) transition. * @param handler - Callback invoked after any forced transition. * @returns `this` for chaining. */ post_hook_forced_transition(handler: HookHandler<mDT>): Machine<mDT>; /** Post-transition hook on any transition regardless of kind. * @param handler - Callback invoked after every transition. * @returns `this` for chaining. */ post_hook_any_transition(handler: HookHandler<mDT>): Machine<mDT>; /** Post-transition hook that fires after entering a specific state. * @param to - The state that was entered. * @param handler - Callback invoked after entry. * @returns `this` for chaining. */ post_hook_entry(to: string, handler: HookHandler<mDT>): Machine<mDT>; /** Post-transition hook that fires after leaving a specific state. * @param from - The state that was exited. * @param handler - Callback invoked after exit. * @returns `this` for chaining. */ post_hook_exit(from: string, handler: HookHandler<mDT>): Machine<mDT>; /** Register a pre-transition hook that fires **before** all other pre-hooks * on every transition. If the handler returns `false`, the transition is * blocked. The handler receives an {@link EverythingHookContext} whose * `hook_name` is `'pre everything'`. * * ```typescript * const m = sm`a -> b -> c;`; * m.hook_pre_everything(({ hook_name }) => { * console.log(`${hook_name} fired`); * return true; * }); * ``` * * @param handler - Callback invoked before all other pre-hooks. * @returns `this` for chaining. */ hook_pre_everything(handler: EverythingHookHandler<mDT>): Machine<mDT>; /** Register a pre-transition hook that fires **after** all other pre-hooks * on every transition. If the handler returns `false`, the transition is * blocked. The handler receives an {@link EverythingHookContext} whose * `hook_name` is `'everything'`. * * ```typescript * const m = sm`a -> b -> c;`; * m.hook_everything(({ hook_name }) => { * console.log(`${hook_name} fired`); * return true; * }); * ``` * * @param handler - Callback invoked after all other pre-hooks. * @returns `this` for chaining. */ hook_everything(handler: EverythingHookHandler<mDT>): Machine<mDT>; /** Register a post-transition hook that fires **after** all other * post-hooks on every transition. Cannot block the transition. The * handler receives an {@link EverythingHookContext} whose `hook_name` is * `'post everything'`. * * ```typescript * const m = sm`a -> b -> c;`; * m.hook_post_everything(({ hook_name }) => { * console.log(`${hook_name} fired`); * }); * ``` * * @param handler - Callback invoked after all other post-hooks. * @returns `this` for chaining. */ hook_post_everything(handler: PostEverythingHookHandler<mDT>): Machine<mDT>; /** Register a post-transition hook that fires **before** all other * post-hooks on every transition. Cannot block the transition. The * handler receives an {@link EverythingHookContext} whose `hook_name` is * `'pre post everything'`. * * ```typescript * const m = sm`a -> b -> c;`; * m.hook_pre_post_everything(({ hook_name }) => { * console.log(`${hook_name} fired`); * }); * ``` * * @param handler - Callback invoked before all other post-hooks. * @returns `this` for chaining. */ hook_pre_post_everything(handler: PostEverythingHookHandler<mDT>): Machine<mDT>; /** Get the current RNG seed used for probabilistic transitions. * @returns The numeric seed value. */ get rng_seed(): number; /** Set the RNG seed. Pass `undefined` to reseed from the current time. * Resets the internal PRNG so subsequent probabilistic operations use the * new seed. * @param to - The seed value, or `undefined` for time-based seeding. */ set rng_seed(to: number | undefined); /** Get all edges between two states (there can be multiple with * different actions). * @param from - Source state name. * @param to - Target state name. * @returns An array of matching {@link JssmTransition} objects. */ edges_between(from: string, to: string): JssmTransition<StateType, mDT>[]; /********* * * Replace the current state and data with no regard to the graph. * * ```typescript * import { sm } from 'jssm'; * * const machine = sm`a -> b -> c;`; * console.log( machine.state() ); // 'a' * * machine.go('b'); * machine.go('c'); * console.log( machine.state() ); // 'c' * * machine.override('a'); * console.log( machine.state() ); // 'a' * ``` * */ override(newState: StateType, newData?: mDT | undefined): void; /********* * * Shared transition core used by {@link transition}, {@link force_transition}, * and {@link action}. Runs validation, fires the full hook pipeline (pre- * everything, any-action, after, any-transition, exit, named, basic, * edge-type, entry, everything), commits the new state if nothing * rejected, and returns whether the transition succeeded. * * Not meant for external use. Call one of the public