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
TypeScript
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