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,329 lines (1,319 loc) 118 kB
import { circular_buffer } from 'circular_buffer_js'; declare type StateType$1 = string; /** * A color value accepted by jssm-viz for state and arrow styling. Currently * any string, validated downstream by Graphviz / the named-colors list. * Intended to be narrowed to `#RRGGBB` / `#RRGGBBAA` and CSS named colors * in a future release. */ declare type JssmColor = string; /** * Three-state policy flag: `'required'`, `'disallowed'`, or `'optional'`. * Used by machine configuration where a default-permissive middle ground * is meaningful (for example, the `actions` config key). */ declare type JssmPermittedOpt = 'required' | 'disallowed' | 'optional'; /** * The set of ASCII arrow tokens recognized by the FSL grammar. Each arrow * encodes a direction (one-way left/right, or two-way) and a "kind" for * each direction (`-` legal, `=` main path, `~` forced-only). See the * Language Reference docs for the full semantic table. */ declare type JssmArrow = '->' | '<-' | '<->' | '<=->' | '<~->' | '=>' | '<=' | '<=>' | '<-=>' | '<~=>' | '~>' | '<~' | '<~>' | '<-~>' | '<=~>'; /** * A type teaching Typescript the various supported shapes for nodes, mostly inherited from GraphViz */ declare type JssmShape = "box" | "polygon" | "ellipse" | "oval" | "circle" | "point" | "egg" | "triangle" | "plaintext" | "plain" | "diamond" | "trapezium" | "parallelogram" | "house" | "pentagon" | "hexagon" | "septagon" | "octagon" | "doublecircle" | "doubleoctagon" | "tripleoctagon" | "invtriangle" | "invtrapezium" | "invhouse" | "Mdiamond" | "Msquare" | "Mcircle" | "rect" | "rectangle" | "square" | "star" | "none" | "underline" | "cylinder" | "note" | "tab" | "folder" | "box3d" | "component" | "promoter" | "cds" | "terminator" | "utr" | "primersite" | "restrictionsite" | "fivepoverhang" | "threepoverhang" | "noverhang" | "assembly" | "signature" | "insulator" | "ribosite" | "rnastab" | "proteasesite" | "proteinstab" | "rpromoter" | "rarrow" | "larrow" | "lpromoter" | "record"; /** * Direction polarity of an arrow: pointing only `'left'`, only `'right'`, * or `'both'` (a bidirectional arrow). */ declare type JssmArrowDirection = 'left' | 'right' | 'both'; /** * Semantic category of an arrow's transition. `'legal'` is a normal * transition, `'main'` is part of the machine's primary path, `'forced'` * may only be taken via {@link Machine.force_transition}, and `'none'` * means no transition exists in that direction. */ declare type JssmArrowKind = 'none' | 'legal' | 'main' | 'forced'; /** * Graphviz layout engine selector. Controls how jssm-viz lays out the * rendered diagram; `'dot'` is the default and most useful for state * machines. See the Graphviz documentation for the differences. */ declare type JssmLayout = 'dot' | 'circo' | 'twopi' | 'fdp' | 'neato'; declare type JssmCorner = 'regular' | 'rounded' | 'lined'; declare type JssmLineStyle = 'solid' | 'dashed' | 'dotted'; /** * Tristate flag for whether a property may be overridden at runtime. * `true` permits overrides, `false` forbids them, and `undefined` defers * the decision to the surrounding configuration's default. */ declare type JssmAllowsOverride = true | false | undefined; /** * Runtime-iterable list of valid `flow` directions for FSL diagrams. * Use this when you need to enumerate directions; for the type itself * see {@link FslDirection}. */ declare const FslDirections: readonly ["up", "right", "down", "left"]; /** * String literal type of the four supported FSL flow directions. This is * the type of the `flow` config key on a machine. */ declare type FslDirection = typeof FslDirections[number]; /** * Runtime-iterable list of the built-in theme names that ship with jssm-viz. * Use this when you need to enumerate themes; for the type itself see * {@link FslTheme}. */ declare const FslThemes: readonly ["default", "ocean", "modern", "plain", "bold"]; /** * String literal type of the built-in theme names. This is the element * type of the `theme` config key (which accepts an array so that themes * can be layered). */ declare type FslTheme = typeof FslThemes[number]; /** * Persistable snapshot of a Machine produced by {@link Machine.serialize} * and consumed by {@link deserialize}. Carries the current state, the * associated machine data, the recent history (subject to the configured * capacity), and metadata to detect version-skew on rehydration. * * @typeParam DataType - The type of the user-supplied data payload (`mDT`). */ declare type JssmSerialization<DataType> = { jssm_version: string; timestamp: number; comment?: string | undefined; state: StateType$1; history: [string, DataType][]; history_capacity: number; data: DataType; }; /** * Declaration of a named property that a machine's states may carry. * Set `required: true` to force every state to define the property, or * provide `default_value` to fall back when the state does not specify it. */ declare type JssmPropertyDefinition = { name: string; default_value?: any; required?: boolean; }; declare type JssmTransitionPermitter<DataType> = (OldState: StateType$1, NewState: StateType$1, OldData: DataType, NewData: DataType) => boolean; declare type JssmTransitionPermitterMaybeArray<DataType> = JssmTransitionPermitter<DataType> | Array<JssmTransitionPermitter<DataType>>; /** * A single directed transition (edge) within a state machine. Captures * both the topology (`from` / `to`), the FSL semantics (`kind`, * `forced_only`, `main_path`), and any optional metadata such as a * per-edge `name`, an action label, a guard `check`, a transition * `probability` for stochastic models, and an `after_time` for timed * transitions. * * @typeParam StateType - The state-name type (usually `string`). * @typeParam DataType - The machine's data payload type (`mDT`). */ declare type JssmTransition<StateType, DataType> = { from: StateType; to: StateType; after_time?: number; se?: JssmCompileSe<StateType, DataType>; name?: StateType; action?: StateType; check?: JssmTransitionPermitterMaybeArray<DataType>; probability?: number; kind: JssmArrowKind; forced_only: boolean; main_path: boolean; }; /** A list of {@link JssmTransition}s — the edge set of a machine. */ declare type JssmTransitions<StateType, DataType> = JssmTransition<StateType, DataType>[]; /** * The set of states that can immediately precede or follow a given state. * Returned by jssm helpers that report a state's connectivity in the graph. */ declare type JssmTransitionList = { entrances: Array<StateType$1>; exits: Array<StateType$1>; }; /** * Topology record for one node in a compiled machine: its name, the set of * states it can be reached from, the set of states it can transition to, * and whether reaching it constitutes "completing" the machine. */ declare type JssmGenericState = { from: Array<StateType$1>; name: StateType$1; to: Array<StateType$1>; complete: boolean; }; /** * The full internal bookkeeping snapshot of a {@link Machine}, exposed for * advanced introspection. Contains the current state, the state map, the * edge map and reverse-action map, and the original edge list. The * `internal_state_impl_version` field exists so that consumers can detect * shape changes if this representation evolves. */ declare type JssmMachineInternalState<DataType> = { internal_state_impl_version: 1; state: StateType$1; states: Map<StateType$1, JssmGenericState>; named_transitions: Map<StateType$1, number>; edge_map: Map<StateType$1, Map<StateType$1, number>>; actions: Map<StateType$1, Map<StateType$1, number>>; reverse_actions: Map<StateType$1, Map<StateType$1, number>>; edges: Array<JssmTransition<StateType$1, DataType>>; }; declare type JssmStatePermitter<DataType> = (OldState: StateType$1, NewState: StateType$1, OldData: DataType, NewData: DataType) => boolean; declare type JssmStatePermitterMaybeArray<DataType> = JssmStatePermitter<DataType> | Array<JssmStatePermitter<DataType>>; /** * A single key/value pair from an FSL `state X: { ... };` block, in the * raw form produced by the parser before being condensed into a * {@link JssmStateDeclaration}. */ declare type JssmStateDeclarationRule = { key: string; value: any; name?: string; }; /** * The fully-condensed declaration for a single state, including its raw * rule list (`declarations`) and the well-known styling fields jssm-viz * understands. Returned by {@link Machine.state_declaration}. */ declare type JssmStateDeclaration = { declarations: Array<JssmStateDeclarationRule>; shape?: JssmShape; color?: JssmColor; corners?: JssmCorner; lineStyle?: JssmLineStyle; stateLabel?: string; textColor?: JssmColor; backgroundColor?: JssmColor; borderColor?: JssmColor; image?: string; state: StateType$1; property?: { name: string; value: unknown; }; }; /** * A loosened version of {@link JssmStateDeclaration} where every field is * optional. Used as the value type for theme entries and for default * state configuration where most fields will be inherited or merged. */ declare type JssmStateConfig = Partial<JssmStateDeclaration>; declare type JssmStateStyleShape = { key: 'shape'; value: JssmShape; }; declare type JssmStateStyleColor = { key: 'color'; value: JssmColor; }; declare type JssmStateStyleTextColor = { key: 'text-color'; value: JssmColor; }; declare type JssmStateStyleCorners = { key: 'corners'; value: JssmCorner; }; declare type JssmStateStyleLineStyle = { key: 'line-style'; value: JssmLineStyle; }; declare type JssmStateStyleStateLabel = { key: 'state-label'; value: string; }; declare type JssmStateStyleBackgroundColor = { key: 'background-color'; value: JssmColor; }; declare type JssmStateStyleBorderColor = { key: 'border-color'; value: JssmColor; }; declare type JssmStateStyleImage = { key: 'image'; value: string; }; /** * Tagged union of all individual style key/value pairs that may appear in * a state's style configuration. The `key` discriminator selects which * member, and the `value` is typed accordingly. */ declare type JssmStateStyleKey = JssmStateStyleShape | JssmStateStyleColor | JssmStateStyleTextColor | JssmStateStyleCorners | JssmStateStyleLineStyle | JssmStateStyleBackgroundColor | JssmStateStyleStateLabel | JssmStateStyleBorderColor | JssmStateStyleImage; /** * An ordered list of {@link JssmStateStyleKey} entries. Used by the * `default_*_state_config` machine config options to provide a fallback * style stack. */ declare type JssmStateStyleKeyList = JssmStateStyleKey[]; /** * Full configuration object accepted by the {@link Machine} constructor and * by {@link from}. Carries the transition list and the optional knobs * governing layout, theming, history, start/end states, property * definitions, machine metadata (author, license, version, ...) and the * runtime hook surfaces (`time_source`, `timeout_source`, ...). * * Most users never construct one of these directly — the `sm` tagged * template literal and {@link from} produce one from FSL source. * * @typeParam StateType - The state-name type (usually `string`). * @typeParam DataType - The user-supplied data payload type (`mDT`). */ declare type JssmGenericConfig<StateType, DataType> = { graph_layout?: JssmLayout; complete?: Array<StateType>; transitions: JssmTransitions<StateType, DataType>; theme?: FslTheme[]; flow?: FslDirection; name?: string; data?: DataType; nodes?: Array<StateType>; check?: JssmStatePermitterMaybeArray<DataType>; history?: number; min_exits?: number; max_exits?: number; allow_islands?: false; allow_force?: false; actions?: JssmPermittedOpt; simplify_bidi?: boolean; allows_override?: JssmAllowsOverride; config_allows_override?: JssmAllowsOverride; dot_preamble?: string; start_states: Array<StateType>; end_states?: Array<StateType>; initial_state?: StateType; start_states_no_enforce?: boolean; state_declaration?: Object[]; property_definition?: JssmPropertyDefinition[]; state_property?: JssmPropertyDefinition[]; arrange_declaration?: Array<Array<StateType>>; arrange_start_declaration?: Array<Array<StateType>>; arrange_end_declaration?: Array<Array<StateType>>; machine_author?: string | Array<string>; machine_comment?: string; machine_contributor?: string | Array<string>; machine_definition?: string; machine_language?: string; machine_license?: string; machine_name?: string; machine_version?: string; fsl_version?: string; auto_api?: boolean | string; instance_name?: string | undefined; default_state_config?: JssmStateStyleKeyList; default_start_state_config?: JssmStateStyleKeyList; default_end_state_config?: JssmStateStyleKeyList; default_hooked_state_config?: JssmStateStyleKeyList; default_terminal_state_config?: JssmStateStyleKeyList; default_active_state_config?: JssmStateStyleKeyList; rng_seed?: number | undefined; time_source?: () => number; timeout_source?: (Function: any, number: any) => number; clear_timeout_source?: (number: any) => void; }; /** * Internal compiler intermediate: one link in a chained transition * expression (an "s-expression" segment). Carries both directions of an * arrow with optional per-direction action labels, probabilities, and * after-times. The recursive `se` field allows the parser to chain * arrows of the form `A -> B -> C`. Not intended for end-user code. * * @internal */ declare type JssmCompileSe<StateType, mDT> = { to: StateType; se?: JssmCompileSe<StateType, mDT>; kind: JssmArrow; l_action?: StateType; r_action?: StateType; l_probability: number; r_probability: number; l_after?: number; r_after?: number; }; /** * Internal compiler intermediate: the root of a chained transition * expression, anchored at a `from` state. Also doubles as the carrier * for non-transition rules (state declarations, property definitions, * machine metadata) via its `key`/`value`/`name`/`state` fields. Not * intended for end-user code. * * @internal */ declare type JssmCompileSeStart<StateType, DataType> = { from: StateType; se: JssmCompileSe<StateType, DataType>; key: string; value?: string | number; name?: string; state?: string; default_value?: any; required?: boolean; }; /** * The output shape of the FSL parser: a flat array of * {@link JssmCompileSeStart} entries, one per top-level rule in the * source. Consumed by the compiler to build a machine configuration. * * @internal */ declare type JssmParseTree<StateType, mDT> = Array<JssmCompileSeStart<StateType, mDT>>; declare type BasicHookDescription<mDT> = { kind: 'hook'; from: string; to: string; handler: HookHandler<mDT>; }; declare type HookDescriptionWithAction<mDT> = { kind: 'named'; from: string; to: string; action: string; handler: HookHandler<mDT>; }; declare type StandardTransitionHook<mDT> = { kind: 'standard transition'; handler: HookHandler<mDT>; }; declare type MainTransitionHook<mDT> = { kind: 'main transition'; handler: HookHandler<mDT>; }; declare type ForcedTransitionHook<mDT> = { kind: 'forced transition'; handler: HookHandler<mDT>; }; declare type AnyTransitionHook<mDT> = { kind: 'any transition'; handler: HookHandler<mDT>; }; declare type GlobalActionHook<mDT> = { kind: 'global action'; action: string; handler: HookHandler<mDT>; }; declare type AnyActionHook<mDT> = { kind: 'any action'; handler: HookHandler<mDT>; }; declare type EntryHook<mDT> = { kind: 'entry'; to: string; handler: HookHandler<mDT>; }; declare type ExitHook<mDT> = { kind: 'exit'; from: string; handler: HookHandler<mDT>; }; declare type AfterHook<mDT> = { kind: 'after'; from: string; handler: HookHandler<mDT>; }; declare type PostBasicHookDescription<mDT> = { kind: 'post hook'; from: string; to: string; handler: PostHookHandler<mDT>; }; declare type PostHookDescriptionWithAction<mDT> = { kind: 'post named'; from: string; to: string; action: string; handler: PostHookHandler<mDT>; }; declare type PostStandardTransitionHook<mDT> = { kind: 'post standard transition'; handler: PostHookHandler<mDT>; }; declare type PostMainTransitionHook<mDT> = { kind: 'post main transition'; handler: PostHookHandler<mDT>; }; declare type PostForcedTransitionHook<mDT> = { kind: 'post forced transition'; handler: PostHookHandler<mDT>; }; declare type PostAnyTransitionHook<mDT> = { kind: 'post any transition'; handler: PostHookHandler<mDT>; }; declare type PostGlobalActionHook<mDT> = { kind: 'post global action'; action: string; handler: PostHookHandler<mDT>; }; declare type PostAnyActionHook<mDT> = { kind: 'post any action'; handler: PostHookHandler<mDT>; }; declare type PostEntryHook<mDT> = { kind: 'post entry'; to: string; handler: PostHookHandler<mDT>; }; declare type PostExitHook<mDT> = { kind: 'post exit'; from: string; handler: PostHookHandler<mDT>; }; declare type PreEverythingHook<mDT> = { kind: 'pre everything'; handler: EverythingHookHandler<mDT>; }; declare type EverythingHook<mDT> = { kind: 'everything'; handler: EverythingHookHandler<mDT>; }; declare type PrePostEverythingHook<mDT> = { kind: 'pre post everything'; handler: PostEverythingHookHandler<mDT>; }; declare type PostEverythingHook<mDT> = { kind: 'post everything'; handler: PostEverythingHookHandler<mDT>; }; /** * Discriminated union of every kind of hook registration jssm understands, * pre-transition and post-transition. The `kind` field selects the * variant; remaining fields describe which transitions / states / actions * the hook is bound to and supply the {@link HookHandler} or * {@link PostHookHandler} to invoke. * * Pre-transition variants (`'hook'`, `'named'`, `'standard transition'`, * `'main transition'`, `'forced transition'`, `'any transition'`, * `'global action'`, `'any action'`, `'entry'`, `'exit'`, `'after'`) * may return a falsy value to veto a transition. Post-transition * variants (`'post *'`) cannot veto and are invoked only after a * successful transition. */ declare type HookDescription<mDT> = BasicHookDescription<mDT> | HookDescriptionWithAction<mDT> | GlobalActionHook<mDT> | AnyActionHook<mDT> | StandardTransitionHook<mDT> | MainTransitionHook<mDT> | ForcedTransitionHook<mDT> | AnyTransitionHook<mDT> | EntryHook<mDT> | ExitHook<mDT> | AfterHook<mDT> | PostBasicHookDescription<mDT> | PostHookDescriptionWithAction<mDT> | PostGlobalActionHook<mDT> | PostAnyActionHook<mDT> | PostStandardTransitionHook<mDT> | PostMainTransitionHook<mDT> | PostForcedTransitionHook<mDT> | PostAnyTransitionHook<mDT> | PostEntryHook<mDT> | PostExitHook<mDT> | PreEverythingHook<mDT> | EverythingHook<mDT> | PrePostEverythingHook<mDT> | PostEverythingHook<mDT>; /** * Richer hook return value used when a hook needs to do more than just * accept or veto a transition. `pass` is the required accept/veto flag * (kept non-optional so that returning a stray object doesn't accidentally * veto everything). The optional `state` overrides the destination state, * `data` overrides the data observed by other hooks in the same chain, * and `next_data` overrides the data committed after the transition. */ declare type HookComplexResult<mDT> = { pass: boolean; state?: StateType$1; data?: mDT; next_data?: mDT; }; /** * Return value from a {@link HookHandler}. May be a plain boolean to * accept (`true`/`undefined`/`void`) or veto (`false`) the transition, or * a {@link HookComplexResult} that additionally rewrites the next state * and/or the next data payload. */ declare type HookResult<mDT> = true | false | undefined | void | HookComplexResult<mDT>; /** * Context object passed to every {@link HookHandler}. `data` is the * data payload as it stands before the transition, and `next_data` is * the payload that will be committed if the transition is accepted — * handlers may inspect or mutate the latter via a * {@link HookComplexResult} return value. */ declare type HookContext<mDT> = { data: mDT; next_data: mDT; }; /** * Context object passed to "everything" hooks ({@link EverythingHookHandler} * and {@link PostEverythingHookHandler}). Extends the usual * {@link HookContext} with `hook_name`, which identifies which specific * hook fired so a single handler can route on it. */ declare type EverythingHookContext<mDT> = HookContext<mDT> & { hook_name: string; }; /** * Signature of a pre-transition hook handler. Receives the current and * proposed-next data payloads via a {@link HookContext} and returns a * {@link HookResult}: a falsy result vetoes the transition, a truthy * result allows it, and a {@link HookComplexResult} can additionally * rewrite the next state or next data. */ declare type HookHandler<mDT> = (hook_context: HookContext<mDT>) => HookResult<mDT>; /** * Signature of a post-transition hook handler. Invoked after a successful * transition has been committed; the return value is ignored (the * transition cannot be undone). */ declare type PostHookHandler<mDT> = (hook_context: HookContext<mDT>) => void; /** * Signature of an "everything" pre-transition hook handler. Like * {@link HookHandler} but receives an {@link EverythingHookContext} so the * handler can dispatch on `hook_name`. */ declare type EverythingHookHandler<mDT> = (hook_context: EverythingHookContext<mDT>) => HookResult<mDT>; /** * Signature of an "everything" post-transition hook handler. Like * {@link PostHookHandler} but receives an {@link EverythingHookContext}. * The return value is ignored. */ declare type PostEverythingHookHandler<mDT> = (hook_context: EverythingHookContext<mDT>) => void; /** * Bounded history of recently-visited states paired with the data payload * observed in each. Backed by `circular_buffer_js`, so the oldest entry * is dropped silently once the configured capacity is exceeded. */ declare type JssmHistory<mDT> = circular_buffer<[StateType$1, mDT]>; /** * Pluggable random-number-generator function shape. Must return a value * in `[0, 1)` exactly as `Math.random` does. Supplied via the * `rng_seed`-aware machine configuration so that stochastic models can be * made reproducible. */ declare type JssmRng = () => number; /********* * * Return the direction of an arrow - `right`, `left`, or `both`. * * ```typescript * import { arrow_direction } from 'jssm'; * * arrow_direction('->'); // 'right' * arrow_direction('<~=>'); // 'both' * ``` * * @param arrow The arrow to be evaluated * */ declare function arrow_direction(arrow: JssmArrow): JssmArrowDirection; /********* * * Return the direction of an arrow - `right`, `left`, or `both`. * * ```typescript * import { arrow_left_kind } from 'jssm'; * * arrow_left_kind('<-'); // 'legal' * arrow_left_kind('<='); // 'main' * arrow_left_kind('<~'); // 'forced' * arrow_left_kind('<->'); // 'legal' * arrow_left_kind('->'); // 'none' * ``` * * @param arrow The arrow to be evaluated * */ declare function arrow_left_kind(arrow: JssmArrow): JssmArrowKind; /********* * * Return the direction of an arrow - `right`, `left`, or `both`. * * ```typescript * import { arrow_left_kind } from 'jssm'; * * arrow_left_kind('->'); // 'legal' * arrow_left_kind('=>'); // 'main' * arrow_left_kind('~>'); // 'forced' * arrow_left_kind('<->'); // 'legal' * arrow_left_kind('<-'); // 'none' * ``` * * @param arrow The arrow to be evaluated * */ declare function arrow_right_kind(arrow: JssmArrow): JssmArrowKind; /********* * * This method wraps the parser call that comes from the peg grammar, * {@link parse}. Generally neither this nor that should be used directly * unless you mean to develop plugins or extensions for the machine. * * Parses the intermediate representation of a compiled string down to a * machine configuration object. If you're using this (probably don't,) you're * probably also using {@link compile} and {@link Machine.constructor}. * * ```typescript * import { parse, compile, Machine } from 'jssm'; * * const intermediate = wrap_parse('a -> b;', {}); * // [ {key:'transition', from:'a', se:{kind:'->',to:'b'}} ] * * const cfg = compile(intermediate); * // { start_states:['a'], transitions: [{ from:'a', to:'b', kind:'legal', forced_only:false, main_path:false }] } * * const machine = new Machine(cfg); * // Machine { _instance_name: undefined, _state: 'a', ... * ``` * * This method is mostly for plugin and intermediate tool authors, or people * who need to work with the machine's intermediate representation. * * # Hey! * * Most people looking at this want either the `sm` operator or method `from`, * which perform all the steps in the chain. The library's author mostly uses * operator `sm`, and mostly falls back to `.from` when needing to parse * strings dynamically instead of from template literals. * * Operator {@link sm}: * * ```typescript * import { sm } from 'jssm'; * * const lswitch = sm`on <=> off;`; * ``` * * Method {@link from}: * * ```typescript * import * as jssm from 'jssm'; * * const toggle = jssm.from('up <=> down;'); * ``` * * `wrap_parse` itself is an internal convenience method for alting out an * object as the options call. Not generally meant for external use. * * @param input The FSL code to be evaluated * * @param options Things to control about the instance * */ declare function wrap_parse(input: string, options?: Object): any; /********* * * Compile a machine's JSON intermediate representation to a config object. If * you're using this (probably don't,) you're probably also using * {@link parse} to get the IR, and the object constructor * {@link Machine.construct} to turn the config object into a workable machine. * * ```typescript * import { parse, compile, Machine } from 'jssm'; * * const intermediate = parse('a -> b;'); * // [ {key:'transition', from:'a', se:{kind:'->',to:'b'}} ] * * const cfg = compile(intermediate); * // { start_states:['a'], transitions: [{ from:'a', to:'b', kind:'legal', forced_only:false, main_path:false }] } * * const machine = new Machine(cfg); * // Machine { _instance_name: undefined, _state: 'a', ... * ``` * * This method is mostly for plugin and intermediate tool authors, or people * who need to work with the machine's intermediate representation. * * # Hey! * * Most people looking at this want either the `sm` operator or method `from`, * which perform all the steps in the chain. The library's author mostly uses * operator `sm`, and mostly falls back to `.from` when needing to parse * strings dynamically instead of from template literals. * * Operator {@link sm}: * * ```typescript * import { sm } from 'jssm'; * * const lswitch = sm`on <=> off;`; * ``` * * Method {@link from}: * * ```typescript * import * as jssm from 'jssm'; * * const toggle = jssm.from('up <=> down;'); * ``` * * @typeparam mDT The type of the machine data member; usually omitted * * @param tree The parse tree to be boiled down into a machine config * */ declare function compile<StateType, mDT>(tree: JssmParseTree<StateType, mDT>): JssmGenericConfig<StateType, mDT>; /********* * * An internal convenience wrapper for parsing then compiling a machine string. * Not generally meant for external use. Please see {@link compile} or * {@link sm}. * * @typeparam mDT The type of the machine data member; usually omitted * * @param plan The FSL code to be evaluated and built into a machine config * */ declare function make<StateType, mDT>(plan: string): JssmGenericConfig<StateType, mDT>; /******* * * Selects a single item from a weighted array of objects using cumulative * probability. Each object in the array should have a numeric property * indicating its relative weight (defaults to `'probability'`). Objects * missing the property are treated as weight 1. * * ```typescript * const opts = [ * { value: 'common', probability: 0.8 }, * { value: 'rare', probability: 0.2 } * ]; * * weighted_rand_select(opts); // most often { value: 'common', ... } * ``` * * @param options - Non-empty array of objects to choose from. * @param probability_property - Name of the numeric weight property on each * object. Defaults to `'probability'`. * @param rng - Optional random number generator `() => number` * in `[0, 1)`. Defaults to `Math.random`. * * @returns One element from `options`, chosen by weighted random selection. * * @throws {TypeError} If `options` is not a non-empty array of objects. * */ declare const weighted_rand_select: Function; /******* * * Returns, for a non-negative integer argument `n`, the series `[0 .. n]`. * * ```typescript * import { seq } from './jssm'; * * seq(5); // [0, 1, 2, 3, 4] * seq(0); // [] * ``` * */ declare function seq(n: number): number[]; /******* * * Returns the histograph of an array as a `Map`. Makes no attempt to cope * with deep equality; will fail for complex contents, as such. * * ```typescript * import { histograph } from './jssm'; * * histograph( [0, 0, 1, 1, 2, 2, 1] ); // Map() * ``` * */ declare const histograph: Function; /******* * * Draws `n` weighted random samples from an array of objects. Each draw is * independent (with replacement), delegating to {@link weighted_rand_select}. * * ```typescript * const opts = [ * { value: 'a', probability: 0.9 }, * { value: 'b', probability: 0.1 } * ]; * * weighted_sample_select(3, opts, 'probability'); * // e.g. [ { value: 'a', ... }, { value: 'a', ... }, { value: 'b', ... } ] * ``` * * @param n - Number of samples to draw. * @param options - Non-empty array of weighted objects. * @param probability_property - Name of the numeric weight property. * @param rng - Optional random number generator. * * @returns An array of `n` independently selected items. * */ declare const weighted_sample_select: Function; /******* * * Draws `n` weighted random samples, extracts a named key from each, and * returns a histograph (`Map`) of how often each key value appeared. Useful * for validating that a probabilistic transition distribution is roughly * correct over many trials. * * ```typescript * const opts = [ * { to: 'a', probability: 0.7 }, * { to: 'b', probability: 0.3 } * ]; * * weighted_histo_key(1000, opts, 'probability', 'to'); * // Map { 'a' => ~700, 'b' => ~300 } * ``` * * @param n - Number of samples to draw. * @param opts - Non-empty array of weighted objects. * @param prob_prop - Name of the numeric weight property. * @param extract - Name of the property to extract from each sample for * histogramming. * @param rng - Optional random number generator. * * @returns A `Map` from extracted key values to their occurrence counts. * */ declare const weighted_histo_key: Function; /******* * * Creates a SplitMix32 random generator. Used by the randomness test suite. * * Sourced from `bryc`: https://github.com/bryc/code/blob/master/jshash/PRNGs.md#splitmix32 * * Replaces the Mulberry generator, which was found to have problems * */ declare function gen_splitmix32(a?: number | undefined): () => number; /******* * * Reduces an array to its unique contents. Compares with `===` and makes no * effort to deep-compare contents; two matching arrays or objects contained * will be treated as distinct, according to javascript rules. This also means * that `NaNs` will be ***dropped***, because they do not self-compare. * * ```typescript * unique( [] ); // [] * unique( [0,0] ); // [0] * unique( [0,1,2, 0,1,2, 0,1,2] ); // [0,1,2] * unique( [ [1], [1] ] ); // [ [1], [1] ] because arrays don't match * unique( [0,NaN,2] ); // [0,2] * ``` * */ declare const unique: <T>(arr: T[]) => T[]; /******* * * Lists all repeated items in an array along with their counts. Subject to * matching rules of Map. `NaN` is manually removed because of conflict rules * around {@link unique}. Because these are compared with `===` and because * arrays and objects never match that way unless they're the same object, * arrays and objects are never considered repeats. * * ```typescript * find_repeated<string>([ ]); // [] * find_repeated<string>([ "one" ]); // [] * find_repeated<string>([ "one", "two" ]); // [] * find_repeated<string>([ "one", "one" ]); // [ ["one", 2] ] * find_repeated<string>([ "one", "two", "one" ]); // [ ["one", 2] ] * find_repeated<number>([ 0, NaN, 0, NaN ]); // [ [0, 2] ] * ``` * */ declare function find_repeated<T>(arr: T[]): [T, number][]; /******* * * Returns a `Promise` that resolves after `ms` milliseconds. Useful for * inserting delays in async test flows or demos. * * ```typescript * await sleep(100); // pauses execution for 100ms * ``` * * @param ms - Number of milliseconds to wait before resolving. * * @returns A `Promise<void>` that resolves after the timeout. * */ declare function sleep(ms: number): Promise<unknown>; /******* * * Convenience aliases for common mathematical and numeric constants from * `Number` and `Math`. Re-exported so that FSL data expressions and tests * can reference them without importing `Math` directly. * * Includes: `NegInfinity`, `PosInfinity`, `Epsilon`, `Pi`, `E`, `Root2`, * `RootHalf`, `Ln2`, `Ln10`, `Log2E`, `Log10E`, `MaxSafeInt`, `MinSafeInt`, * `MaxPosNum`, `MinPosNum`, `Phi` (golden ratio), `EulerC` (Euler–Mascheroni). * */ declare const NegInfinity: number; declare const PosInfinity: number; declare const Epsilon: number; declare const Pi: number; declare const E: number; declare const Root2: number; declare const RootHalf: number; declare const Ln2: number; declare const Ln10: number; declare const Log2E: number; declare const Log10E: number; declare const MaxSafeInt: number; declare const MinSafeInt: number; declare const MaxPosNum: number; declare const MinPosNum: number; declare const Phi = 1.618033988749895; declare const EulerC = 0.5772156649015329; /******* * * Complete list of node shapes supported by Graphviz. Used by jssm-viz to * validate and render state shapes in FSL `state ... : { shape: ... }` blocks. * * `shapes` is an alias for `gviz_shapes`. * */ declare const gviz_shapes$1: string[]; /** * Public alias for {@link gviz_shapes}. The list of node shapes supported * by Graphviz that jssm-viz accepts in FSL `state ... : { shape: ... }` * declarations. */ declare const shapes$1: string[]; /******* * * List of CSS/SVG named colors accepted by jssm-viz for state styling * properties like `background-color` and `text-color`. Case-insensitive * matching is done at parse time; the canonical casing here follows the * CSS specification. * */ declare const named_colors$1: string[]; /******* * * Character ranges accepted by the FSL grammar for identifier and label * tokens. Each entry is an inclusive `{from, to}` range of single Unicode * characters. Single-character entries (e.g. `.`) appear with `from === to`. * * These are intended for tooling, validators, and editors that need to know * which characters are legal in a given FSL token position without re-parsing * the PEG grammar. * */ /** * Inclusive character ranges accepted by `AtomLetter` — i.e., the characters * legal in any but the first position of an FSL state name (atom). * * Includes ASCII digits/letters and the symbols * `.`, `+`, `_`, `^`, `(`, `)`, `*`, `&`, `$`, `#`, `@`, `!`, `?`, `,`, * plus the high-Unicode range `U+0080`–`U+FFFF`. * * @example * state_name_chars.some(r => 'A' >= r.from && 'A' <= r.to); // true */ declare const state_name_chars$1: ReadonlyArray<{ from: string; to: string; }>; /** * Inclusive character ranges accepted by `AtomFirstLetter` — i.e., the * characters legal in the first position of an FSL state name (atom). * * Notably narrower than {@link state_name_chars}: omits `+`, `(`, `)`, `&`, * `#`, `@`. Includes ASCII digits/letters, `.`, `_`, `!`, `$`, `^`, `*`, * `?`, `,`, and the high-Unicode range `U+0080`–`U+FFFF`. * * @example * state_name_first_chars.some(r => '+' >= r.from && '+' <= r.to); // false */ declare const state_name_first_chars$1: ReadonlyArray<{ from: string; to: string; }>; /** * Inclusive character ranges accepted by `ActionLabelUnescaped` — i.e., the * characters legal inside a single-quoted action label without escaping. * Space (`U+0020`) is included; the apostrophe `'` (`U+0027`) is explicitly * excluded since it terminates the label. * * Three ranges: `U+0020`–`U+0026`, `U+0028`–`U+005B`, `U+005D`–`U+FFFF`. * * @example * action_label_chars.some(r => ' ' >= r.from && ' ' <= r.to); // true * action_label_chars.some(r => "'" >= r.from && "'" <= r.to); // false */ declare const action_label_chars$1: ReadonlyArray<{ from: string; to: string; }>; declare const jssm_constants_d_E: typeof E; declare const jssm_constants_d_Epsilon: typeof Epsilon; declare const jssm_constants_d_EulerC: typeof EulerC; declare const jssm_constants_d_Ln10: typeof Ln10; declare const jssm_constants_d_Ln2: typeof Ln2; declare const jssm_constants_d_Log10E: typeof Log10E; declare const jssm_constants_d_Log2E: typeof Log2E; declare const jssm_constants_d_MaxPosNum: typeof MaxPosNum; declare const jssm_constants_d_MaxSafeInt: typeof MaxSafeInt; declare const jssm_constants_d_MinPosNum: typeof MinPosNum; declare const jssm_constants_d_MinSafeInt: typeof MinSafeInt; declare const jssm_constants_d_NegInfinity: typeof NegInfinity; declare const jssm_constants_d_Phi: typeof Phi; declare const jssm_constants_d_Pi: typeof Pi; declare const jssm_constants_d_PosInfinity: typeof PosInfinity; declare const jssm_constants_d_Root2: typeof Root2; declare const jssm_constants_d_RootHalf: typeof RootHalf; declare namespace jssm_constants_d { export { jssm_constants_d_E as E, jssm_constants_d_Epsilon as Epsilon, jssm_constants_d_EulerC as EulerC, jssm_constants_d_Ln10 as Ln10, jssm_constants_d_Ln2 as Ln2, jssm_constants_d_Log10E as Log10E, jssm_constants_d_Log2E as Log2E, jssm_constants_d_MaxPosNum as MaxPosNum, jssm_constants_d_MaxSafeInt as MaxSafeInt, jssm_constants_d_MinPosNum as MinPosNum, jssm_constants_d_MinSafeInt as MinSafeInt, jssm_constants_d_NegInfinity as NegInfinity, jssm_constants_d_Phi as Phi, jssm_constants_d_Pi as Pi, jssm_constants_d_PosInfinity as PosInfinity, jssm_constants_d_Root2 as Root2, jssm_constants_d_RootHalf as RootHalf, action_label_chars$1 as action_label_chars, gviz_shapes$1 as gviz_shapes, named_colors$1 as named_colors, shapes$1 as shapes, state_name_chars$1 as state_name_chars, state_name_first_chars$1 as state_name_first_chars, }; } /** * The published semantic version of the jssm package this build was cut from. * Mirrored from `package.json` by `src/buildjs/makever.cjs` at build time. * Useful for runtime diagnostics and for embedding in serialized machine * snapshots so that deserializers can detect version-skew. */ declare const version: string; /** * The Unix epoch timestamp (in milliseconds) at which this build was produced, * written by `src/buildjs/makever.cjs`. Useful for distinguishing builds * with the same `version` string during development, and for diagnostic logs. */ declare const build_time: number; declare type StateType = string; declare const shapes: string[]; declare const gviz_shapes: string[]; declare const named_colors: string[]; declare const state_name_chars: readonly { from: string; to: string; }[]; declare const state_name_first_chars: readonly { from: string; to: string; }[]; declare const action_label_chars: readonly { from: string; to: string; }[]; /********* * * 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 `u