The JssmStateConfig for the active state.
Return the effective island policy for this machine. true means
disconnected components are allowed (the default), false requires a
single connected component, and 'with_start' allows islands only when
every component contains at least one start state.
The island policy stored in the machine.
Check if a machine allows overriding state and data. Resolves the combined effect of code and config permissions — config may not be less strict than code.
The effective override permission.
Check if the code that built the machine allows overriding state and data.
The override permission from the FSL source code.
Check if the machine config allows overriding state and data.
The override permission from the runtime config.
Get the timestamp when construction began (before parsing).
The start-of-construction timestamp as a number.
Get the creation date of this machine as a Date object.
A Date representing when the machine was created.
Get the creation timestamp (milliseconds since epoch).
The timestamp as a number.
Get the end state style. Does not include composition from an applied theme, or things from the underlying base stylesheet; only the modifications applied by this machine.
End states are defined in the directive end_states, and are distinct
from terminal states. End states are voluntary successful endpoints for a
process. Terminal states are states that cannot be exited. By example,
most error states are terminal states, but not end states. Also, since
some end states can be exited and are determined by hooks, such as
recursive or iterative nodes, there is such a thing as an end state that
is not a terminal state.
const light = sm`a -> b;`;
console.log(light.standard_state_style);
// {}
const light = sm`a -> b; end_state: { shape: circle; };`;
console.log(light.standard_state_style);
// { shape: 'circle' }
The JssmStateConfig for end states.
Get a truncated history of the recent states and data of the machine.
Turned off by default; configure with .from('...', {data: 5}) by length,
or set .history_length at runtime.
History does not contain the current state. If you want that, call
.history_inclusive instead.
const foo = jssm.from(
"a 'next' -> b 'next' -> c 'next' -> d 'next' -> e;",
{ history: 3 }
);
foo.action('next');
foo.action('next');
foo.action('next');
foo.action('next');
foo.history; // [ ['b',undefined], ['c',undefined], ['d',undefined] ]
Notice that the machine's current state, e, is not in the returned list.
Get a truncated history of the recent states and data of the machine,
including the current state. Turned off by default; configure with
.from('...', {data: 5}) by length, or set .history_length at runtime.
History inclusive contains the current state. If you only want past
states, call .history instead.
The list returned will be one longer than the history buffer kept, as the history buffer kept gets the current state added to it to produce this list.
const foo = jssm.from(
"a 'next' -> b 'next' -> c 'next' -> d 'next' -> e;",
{ history: 3 }
);
foo.action('next');
foo.action('next');
foo.action('next');
foo.action('next');
foo.history_inclusive; // [ ['b',undefined], ['c',undefined], ['d',undefined], ['e',undefined] ]
Notice that the machine's current state, e, is in the returned list.
Find out how long a history this machine is keeping. Defaults to zero. Settable directly.
const foo = jssm.from("a -> b;");
foo.history_length; // 0
const bar = jssm.from("a -> b;", { history: 3 });
foo.history_length; // 3
foo.history_length = 5;
foo.history_length; // 5
Find out how long a history this machine is keeping. Defaults to zero. Settable directly.
const foo = jssm.from("a -> b;");
foo.history_length; // 0
const bar = jssm.from("a -> b;", { history: 3 });
foo.history_length; // 3
foo.history_length = 5;
foo.history_length; // 5
Get the hooked state style. Does not include composition from an applied theme, or things from the underlying base stylesheet; only the modifications applied by this machine.
The hooked style is only applied to nodes which have a named hook in the graph. Open hooks set through the external API aren't graphed, because that would be literally every node.
const light = sm`a -> b;`;
console.log(light.hooked_state_style);
// {}
const light = sm`a -> b; hooked_state: { shape: circle; };`;
console.log(light.hooked_state_style);
// { shape: 'circle' }
The JssmStateConfig for hooked states.
Get the current RNG seed used for probabilistic transitions.
The numeric seed value.
Set the RNG seed. Pass undefined to reseed from the current time.
Resets the internal PRNG so subsequent probabilistic operations use the
new seed.
The seed value, or undefined for time-based seeding.
The numeric seed value.
Get the standard style for a single state. Does not include composition from an applied theme, or things from the underlying base stylesheet; only the modifications applied by this machine.
const light = sm`a -> b;`;
console.log(light.standard_state_style);
// {}
const light = sm`a -> b; state: { shape: circle; };`;
console.log(light.standard_state_style);
// { shape: 'circle' }
The JssmStateConfig for standard states.
Get the start state style. Does not include composition from an applied theme, or things from the underlying base stylesheet; only the modifications applied by this machine.
Start states are defined by the directive start_states, or in absentia,
are the first mentioned state.
const light = sm`a -> b;`;
console.log(light.start_state_style);
// {}
const light = sm`a -> b; start_state: { shape: circle; };`;
console.log(light.start_state_style);
// { shape: 'circle' }
The JssmStateConfig for start states.
Get the terminal state style. Does not include composition from an applied theme, or things from the underlying base stylesheet; only the modifications applied by this machine.
Terminal state styles are automatically determined by the machine. Any state without a valid exit transition is terminal.
const light = sm`a -> b;`;
console.log(light.terminal_state_style);
// {}
const light = sm`a -> b; terminal_state: { shape: circle; };`;
console.log(light.terminal_state_style);
// { shape: 'circle' }
The JssmStateConfig for terminal states.
Get the active theme(s) for this machine. Always stored as an array internally; the union return type exists for setter compatibility.
The current theme or array of themes.
Set the active theme(s). Accepts a single theme name or an array.
A theme name or array of theme names to apply.
The current theme or array of themes.
Whether any actions are defined on this machine.
true if the machine has at least one action.
Whether any forced (~>) transitions exist in this machine.
true if at least one forced transition is defined.
Folds the static tiers 1–5 of the unified config cascade for a state, plus
— when active is set — the active-state THEME layers, which historically
sit just below the per-state config so that a state foo : { … } block
still overrides a theme's active styling. The user active_state : { … }
overlay (tier 6) is NOT applied here; it is layered on top by
resolve_state_config so it wins over per-state config.
Tiers, folded least-specific → most-specific with {@link merge_state_config} (later wins, never throwing on a cross-tier key collision):
base_theme.state, then each selected theme's
.state block.default_state_config (the implicit state : { … } root over every
state).base_theme.<kind>,
selected themes' .<kind>, and the machine's default_<kind>_state_config.
When active, the active-state theme layers (base_theme.active and
each selected theme's .active) are folded here too.{ declarations } already condensed at construction.state foo : { … } config.The state to resolve config for.
Whether to include the active-state theme layers (true only for the machine's currently-occupied state).
The composited tiers-1–5 JssmStateConfig for the state.
Dispatch an event to every registered subscriber in registration
order. Filters are checked first; non-matching handlers are skipped
without invoking the handler. Exceptions thrown by a handler are
caught and re-emitted as an error event so subsequent handlers
still run.
Re-entry into the error event itself is guarded — if an error
handler throws, the new exception is swallowed rather than rebroadcast
to avoid an infinite loop.
When exactly one subscriber is registered the common case avoids the
Array.from(set) snapshot allocation by capturing the lone entry into a
local first — equivalent to a 1-element snapshot but allocation-free.
The general path still snapshots for re-entrancy safety.
Fire the FSL boundary-hook actions for a single, already-committed state
change. In FSL, do is a synonym for action, so on enter &g do 'X';
means "when the machine crosses INTO group g, dispatch machine action
X" — and likewise on exit / plain-state subjects. This is the runtime
that fires those parked hooks.
Crossing semantics (statechart convention — exits before enters):
prev_groups / next_groups are the deep (transitive) group sets of
the old and new states, from _state_to_groups.prev_groups \ next_groups with an
onExit, plus the plain prev_state's onExit (when the state name
actually changed).next_groups \ prev_groups with an
onEnter, plus the plain next_state's onEnter (when the state name
changed).prev_state === next_state fires
nothing at all.this.action(label). If that action is not valid
from the current state, action is a safe no-op (returns false) — an
inapplicable boundary action never throws. Because firing an action can drive a further transition (which crosses
more boundaries, which fires more actions), this is a bounded
run-to-completion: _boundary_depth tracks the live cascade depth and a
cascade deeper than _boundary_depth_limit throws a JssmError
rather than overflowing the stack or hanging. The limit defaults to 100
and is configurable via the boundary_depth_limit constructor option.
The state the machine was in before this commit.
The state the machine is in now (already committed).
Fire a 'rejection' event caused by a hook vetoing a pending transition.
Extracted from the per-call closures inside transition_impl so
that it is allocated once at class-definition time rather than on every
hooked transition.
Name of the hook that rejected (e.g. 'exit').
State the machine was in when the transition was
attempted; used as the from field of the rejection event.
State that would have been entered had the hook
passed; used as the to field of the rejection event.
Action name when the transition was initiated by an
action call; undefined for plain state transitions.
Machine data at the moment the transition was attempted, before any hook mutations.
The next_data value passed to the transition call.
Whether the transition was attempted via
force_transition.
Invoke a single event-handler entry, respecting its filter, once-removal semantics, and the error re-fire / recursion-guard logic. Extracted so _fire can share identical behavior between the size-1 fast-path and the general snapshotted loop.
The subscriber descriptor to invoke.
The live Set that owns entry; needed for once-removal.
The event name being dispatched (used in error re-fires).
The event payload forwarded to the handler.
Orders the groups a state belongs to by nesting depth for the config cascade — outermost first, innermost last — so that, folded in order, the innermost (nearest / smallest {@link membership_distance}) group's metadata wins. Equal-distance groups are ordered by group declaration order, so a later-declared group of the same depth wins the tie.
Concretely: groups are sorted by descending membership distance (largest distance applied first / wins least), and for equal distances by ascending declaration index (later index applied last / wins most).
The state whose containing groups are being ordered.
The containing group names, ordered for outer→inner folding (the last entry wins).
Reads the condensed per-state style fields (color, shape, …) out of a
state's declaration into a fresh JssmStateConfig — the tier-5
"state foo : { … }" contribution of the config cascade. A state with no
declaration yields an all-undefined config (which contributes nothing
once folded with {@link merge_state_config}).
The state whose per-state declared style is wanted.
The per-state style config (fields may be undefined).
Internal method for fabricating states. Not meant for external use.
Returns the list of resolved theme implementations for this machine, in the order they should layer (outer/base-most first). Each declared theme name is mapped through {@link theme_mapping}; unknown names are skipped.
The list is reversed relative to declaration order to match the historical layering of style_for: a later-declared theme layers under an earlier-declared one.
The resolved JssmBaseTheme stack, base-most first.
Shared registration core used by Machine.on and Machine.once. Normalizes the optional filter argument and installs the entry into the per-event subscription set.
Remove one event-subscription entry from its set and keep
Machine._event_listener_count in sync. The count is decremented
only when the entry was actually present, so calling a stale unsubscribe
closure (or removing an already-fired once entry) is idempotent and
cannot drive the count negative.
The per-event-name subscription set.
The entry to remove.
Validate a HookDescription before registration. Every hook needs
a handler function, and each kind's identifying spatial fields
(from/to/action) must be exactly those set_hook reads for that
kind — present when required, absent otherwise. This turns a mis-shaped
descriptor into a thrown error instead of a silently dead hook keyed on
undefined (e.g. an exit hook handed to instead of from, #734).
The descriptor about to be registered.
Instruct the machine to complete an action. Synonym for do.
const light = sm`red 'next' -> green 'next' -> yellow 'next' -> red; [red yellow green] 'shutdown' ~> off 'start' -> red;`;
light.state(); // 'red'
light.action('next'); // true
light.state(); // 'green'
The action to engage
The data change to insert during the action
true if the action was valid and the transition occurred,
false otherwise.
List all actions available from this state. Please note that the order of the actions is not guaranteed.
import { sm } from 'jssm';
const machine = sm`
red 'next' -> green 'next' -> yellow 'next' -> red;
[red yellow green] 'shutdown' ~> off 'start' -> red;
`;
console.log( machine.state() ); // logs 'red'
console.log( machine.actions() ); // logs ['next', 'shutdown']
machine.action('next'); // true
console.log( machine.state() ); // logs 'green'
console.log( machine.actions() ); // logs ['next', 'shutdown']
machine.action('shutdown'); // true
console.log( machine.state() ); // logs 'off'
console.log( machine.actions() ); // logs ['start']
machine.action('start'); // true
console.log( machine.state() ); // logs 'red'
console.log( machine.actions() ); // logs ['next', 'shutdown']
The state whose actions to list. Defaults to the current state.
An array of action names available from the given state.
List the character ranges accepted inside a single-quoted FSL action
label without escaping. Space is allowed; the apostrophe ' is
explicitly excluded since it terminates the label.
An array of {from, to} inclusive character ranges.
List the character ranges accepted by the FSL grammar in any but the
first position of a state name (atom). Each entry is an inclusive
{from, to} range of single Unicode characters.
An array of {from, to} inclusive character ranges.
List the character ranges accepted by the FSL grammar in the first
position of a state name (atom). Narrower than
all_state_name_chars: notably omits +, (, ), &, #, @.
An array of {from, to} inclusive character ranges.
List all available theme names.
An array of theme name strings.
If the current state has an after timeout configured, schedule it.
Called internally after each transition.
Cancel any pending state timeout. Safe to call when no timeout is active.
Get the full transition object for an action from the current state.
The action name.
The JssmTransition object.
Get the edge index for an action from the current state. Interned dispatch: resolves via the numeric (action, from) index — unknown action names miss without throwing.
The action name.
The edge index, or undefined if the action is not available.
Get the configured after timeout for the current state, if any.
A [targetState, delayMs] tuple, or undefined.
Get the current data of a machine.
import * as jssm from 'jssm';
const lswitch = jssm.from('on <=> off;', {data: 1});
console.log( lswitch.data() ); // 1
A deep clone of the machine's current data value.
Get the consolidated graph: {} default-config block: the ordered,
de-duplicated { key, value }[] list of graph-scope style items. The
compiler folds the deprecated top-level graph keywords
(graph_bg_color → background-color, plus graph_layout, theme,
flow, dot_preamble) into this list first, then lets an explicit
graph: {} block win on key conflict. The viz layer projects the
graph-meaningful keys onto graph-scope Graphviz attributes (e.g.
background-color → bgcolor).
import { sm } from 'jssm';
sm`a -> b; graph: { background-color: #ffffff; };`.default_graph_config();
// [ { key: 'background-color', value: '#ffffffff' } ]
The graph-config item list, or undefined if the machine has no
graph config (no graph: {} block and no deprecated graph keyword).
Get the render-size hint for the machine's visualization. Set via the
FSL default_size directive. Returns undefined when not present.
The three FSL forms each produce a different subset of fields:
default_size: 800; → { width: 800 }default_size: 800 600; → { width: 800, height: 600 }default_size: height 600; → { height: 600 }This is a hint, not a hard constraint. Renderers may ignore it.
The size-hint object, or undefined if not set.
Get the consolidated transition: {} default-config block: the ordered,
de-duplicated { key, value }[] list of edge-default style items compiled
from a transition: {} block (e.g. transition: { color: blue; }). The
viz layer projects this onto a Graphviz edge [ … ] default statement so
every edge inherits it.
import { sm } from 'jssm';
sm`a -> b; transition: { color: blue; };`.default_transition_config();
// [ { key: 'color', value: '#0000ffff' } ]
The transition-config item list, or undefined if the machine
declared no transition: {} block.
Get whatever the node should show as text.
Currently, this means to get the label for a given state, if any; otherwise to return the node's name. However, this definition is expected to grow with time, and it is currently considered ill-advised to manually parse this text.
See also label_for.
import * as jssm from 'jssm';
const lswitch = jssm.from('a -> b; state a: { label: "Foo!"; };');
console.log( lswitch.display_text('a') ); // 'Foo!'
console.log( lswitch.display_text('b') ); // 'b'
The state to get display text for.
The label if one exists, otherwise the state's name.
Instruct the machine to complete an action. Synonym for action.
const light = sm`
off 'start' -> red;
red 'next' -> green 'next' -> yellow 'next' -> red;
[red yellow green] 'shutdown' ~> off;
`;
light.state(); // 'off'
light.do('start'); // true
light.state(); // 'red'
light.do('next'); // true
light.state(); // 'green'
light.do('next'); // true
light.state(); // 'yellow'
light.do('dance'); // !! false - no such action
light.state(); // 'yellow'
light.do('start'); // !! false - yellow does not have the action start
light.state(); // 'yellow'
The action to engage
The data change to insert during the action
true if the action was valid and the transition occurred,
false otherwise.
Get the Graphviz DOT preamble string, injected before the graph body
during visualization. Set via the FSL dot_preamble directive.
The preamble string.
Get all edges between two states (there can be multiple with different actions).
Source state name.
Target state name.
An array of matching JssmTransition objects.
Get the set of states declared as failure outputs for this machine. Returns an array of state labels, or an empty array when none were declared. A state in this list means the machine is in a failure condition when it occupies that state.
Get the flow direction for graph layout (e.g. 'right', 'down').
Set via the FSL flow directive.
The current flow direction.
Instruct the machine to complete a forced transition (which will reject if called with a normal transition call.)
const light = sm`red -> green -> yellow -> red; [red yellow green] 'shutdown' ~> off 'start' -> red;`;
light.state(); // 'red'
light.transition('off'); // false
light.state(); // 'red'
light.force_transition('off'); // true
light.state(); // 'off'
The state to switch to
The data change to insert during the transition
true if a transition (forced or otherwise) existed and occurred,
false otherwise.
Get the FSL language version this machine was compiled under.
The FSL version string.
Look up a transition's edge index by source and target state names.
Source state name.
Target state name.
The edge index in the edges array, or undefined if no
such transition exists.
Instruct the machine to complete a transition. Synonym for transition.
const light = sm`red -> green -> yellow -> red; [red yellow green] 'shutdown' ~> off 'start' -> red;`;
light.state(); // 'red'
light.go('green'); // true
light.state(); // 'green'
The state to switch to
The data change to insert during the transition
true if the transition was legal and occurred, false otherwise.
Get the graph layout direction (e.g. 'LR', 'TB'). Set via the
FSL graph_layout directive.
The layout string, or the default if not set.
Lists all declared group names, in source declaration order. The order
matches the order the &group : [ … ]; declarations appear in the FSL, and
is the same order used to break depth-specificity ties in the config
cascade. Machines that declare no groups return an empty array.
import { sm } from 'jssm';
const m = sm`&first : [a]; &second : [b]; a -> b;`;
m.groups(); // [ 'first', 'second' ]
The declared group names, in declaration order.
Lists every group that transitively contains a given state. Membership is
deep — direct, nested, and spread sub-group containment all count — and the
result is the precomputed inverse-index entry for the state, so the lookup
is constant-time. A state that belongs to no group (or a state name that
appears in no group) yields an empty Set.
import { sm } from 'jssm';
const m = sm`&inner : [a]; &outer : [&inner b]; a -> b;`;
m.groupsOf('a'); // Set { 'inner', 'outer' } — deep through &inner
m.groupsOf('b'); // Set { 'outer' }
m.groupsOf('z'); // Set {} — not in any group
The state whose containing groups are wanted.
A Set of every group name transitively containing state;
empty when state belongs to no group.
Check whether any state in the machine is complete.
true if at least one state is complete.
Is at least one observational hook bound to the given target (megaspec
§12)? The query is read exactly as in Machine.hooks_on. An
optional phase narrows the test to pre- or post-transition hooks only;
omitted, either phase satisfies it.
const m = sm`a -> b;`;
m.has_hook('b'); // false
m.hook_entry('b', () => true);
m.has_hook('b'); // true
m.has_hook('b', 'post'); // false (the entry hook is pre-phase)
The HookQuery naming the target to inspect.
Optional HookPhase to restrict the test to.
true when a matching hook exists.
Check whether the machine knows a given state.
import * as jssm from 'jssm';
const lswitch = jssm.from('on <=> off;');
console.log( lswitch.has_state('off') ); // true
console.log( lswitch.has_state('dance') ); // false
The state to be checked for existence.
true if the state exists, false otherwise.
Check whether any state in the machine is terminal.
true if at least one state has no exits.
Check whether any state in the machine is unenterable.
true if at least one state has no incoming transitions.
Register a pre-transition hook on a specific edge. Fires before
transitioning from from to to. If the handler returns false, the
transition is blocked.
const m = sm`a -> b -> c;`;
m.hook('a', 'b', () => console.log('a->b'));
Source state name.
Target state name.
Callback invoked before the transition.
this for chaining.
Register a pre-transition hook on a specific action-labeled edge.
Source state name.
Target state name.
The action label that triggers this hook.
Callback invoked before the transition.
this for chaining.
Register a hook that fires when a state's after timer elapses — the
delay-over companion to a after 5s -> b; style time transitions. It
does NOT fire when the state is entered or left by ordinary dispatch;
use hook_entry / hook_exit for those. (Versions through
5.143.28 also spuriously fired it on entering the state, the jssm side
of StoneCypher/fsl#1327.)
The state whose after timer is being watched.
Callback invoked when the timer fires, just before the timed transition is taken; informational — its outcome cannot reject the transition.
this for chaining.
Register a pre-transition hook on any action-driven transition.
Callback invoked before any action transition.
this for chaining.
Register a pre-transition hook on any transition regardless of kind.
Callback invoked before every transition.
this for chaining.
Register a hook that fires when entering a specific state.
The state being entered.
Callback invoked on entry.
this for chaining.
Register a pre-transition hook that fires after all other pre-hooks
on every transition. If the handler returns false, the transition is
blocked. The handler receives an EverythingHookContext whose
hook_name is 'everything'.
const m = sm`a -> b -> c;`;
m.hook_everything(({ hook_name }) => {
console.log(`${hook_name} fired`);
return true;
});
Callback invoked after all other pre-hooks.
this for chaining.
Register a hook that fires when leaving a specific state.
The state being exited.
Callback invoked on exit.
this for chaining.
Register a pre-transition hook on any forced (~>) transition.
Callback invoked before any forced transition.
this for chaining.
Register a pre-transition hook on any edge triggered by a specific action.
The action name to hook.
Callback invoked before any transition with this action.
this for chaining.
Register a pre-transition hook on any main-path (=>) transition.
Callback invoked before any main transition.
this for chaining.
Register a post-transition hook that fires after all other
post-hooks on every transition. Cannot block the transition. The
handler receives an EverythingHookContext whose hook_name is
'post everything'.
const m = sm`a -> b -> c;`;
m.hook_post_everything(({ hook_name }) => {
console.log(`${hook_name} fired`);
});
Callback invoked after all other post-hooks.
this for chaining.
Register a pre-transition hook that fires before all other pre-hooks
on every transition. If the handler returns false, the transition is
blocked. The handler receives an EverythingHookContext whose
hook_name is 'pre everything'.
const m = sm`a -> b -> c;`;
m.hook_pre_everything(({ hook_name }) => {
console.log(`${hook_name} fired`);
return true;
});
Callback invoked before all other pre-hooks.
this for chaining.
Register a post-transition hook that fires before all other
post-hooks on every transition. Cannot block the transition. The
handler receives an EverythingHookContext whose hook_name is
'pre post everything'.
const m = sm`a -> b -> c;`;
m.hook_pre_post_everything(({ hook_name }) => {
console.log(`${hook_name} fired`);
});
Callback invoked before all other post-hooks.
this for chaining.
Generate the uniform observational-hook registry — every currently
registered hook projected onto a normalized (kind, target, phase) row
(megaspec §12, → #1357). The registry is generated on demand by
walking the concrete per-kind storage tables rather than maintained as a
second copy, so it can never drift from the tables Machine.set_hook
actually dispatches into. It is the single source of truth behind the
introspection accessors (Machine.has_hook, Machine.hooks_on)
and the hooked_state viz styling.
Targets are normalized: edge hooks become { scope: 'edge', from, to }
(named hooks add action), entry/exit/after become { scope: 'state' },
global-action hooks become { scope: 'action' }, and the any-*,
transition-class, and everything observers become { scope: 'global' }.
const m = sm`a 'go' -> b;`;
m.hook_entry('b', () => true);
m.hook_registry();
// => [ { kind: 'entry', phase: 'pre', target: { scope: 'state', state: 'b' } } ]
Every registered hook as a HookRegistryEntry, in a stable table-walk order (pre-phase tables first, then post-phase).
Register a pre-transition hook on any standard (->) transition.
Callback invoked before any legal transition.
this for chaining.
Return every registry entry observing the given target (megaspec §12).
The query selects the target shape:
from or to),{ from, to, action? } edge matches edge hooks on that
transition (optionally narrowed to the named action),{ action } action matches global-action and named-edge hooks
carrying that action,{ group } group matches that group's boundary hooks (group hooks
are matched by name only and do not propagate to member states).const m = sm`a 'go' -> b;`;
m.hook_entry('b', () => true);
m.hooks_on('b').length; // 1
m.hooks_on({ from: 'a', to: 'b' }); // [] (no edge hook registered)
The matching HookRegistryEntry rows (possibly empty).
Get the instance name of this machine, if one was assigned at creation.
The instance name string, or undefined.
Reports whether the machine's CURRENT state is a transitive member of a
named group. Membership is deep: a state counts as in groupName if it
belongs to that group directly, or via any nested (&child) or spread
(...&child) sub-group, at any depth. An undeclared group simply has no
members, so this returns false rather than throwing.
import { sm } from 'jssm';
const m = sm`&busy : [working]; idle 'go' -> working;`;
m.isIn('busy'); // false — current state is 'idle'
m.action('go');
m.isIn('busy'); // true — current state is now 'working'
m.isIn('nonesuch'); // false — undeclared group has no members
The group to test the current state against.
true if the current state is a transitive member of groupName.
Check whether the current state is complete (every exit has an action).
true if the current state is complete.
Check whether a given state is a valid start state (either because it was explicitly named as such, or because it was the first mentioned state.)
import { sm, is_end_state } from 'jssm';
const example = sm`a -> b;`;
console.log( final_test.is_start_state('a') ); // false
console.log( final_test.is_start_state('b') ); // true
const example = sm`end_states: [a b]; a -> b;`;
console.log( final_test.is_start_state('a') ); // true
console.log( final_test.is_start_state('b') ); // true
The name of the state to check
Check whether the machine is currently in a failure state — that is,
whether its current state is one of the declared failed_outputs.
Check whether a given state is declared as a failure output.
The name of the state to check
Check whether the current state is final (either has no exits or is marked
complete.)
import { sm, is_final } from 'jssm';
const final_test = sm`first -> second;`;
console.log( final_test.is_final() ); // false
state.transition('second');
console.log( final_test.is_final() ); // true
Check whether a given state is a valid start state (either because it was explicitly named as such, or because it was the first mentioned state.)
import { sm, is_start_state } from 'jssm';
const example = sm`a -> b;`;
console.log( final_test.is_start_state('a') ); // true
console.log( final_test.is_start_state('b') ); // false
const example = sm`start_states: [a b]; a -> b;`;
console.log( final_test.is_start_state('a') ); // true
console.log( final_test.is_start_state('b') ); // true
The name of the state to check
Check whether the current state is terminal (has no exits).
true if the current state has zero exits.
Check whether a state has no incoming transitions (unreachable after start).
The state to check.
true if the state has zero entrances.
Check whether a given string is a known property's name.
const example = sm`property foo default 1; a->b;`;
example.known_prop('foo'); // true
example.known_prop('bar'); // false
The relevant property name to look up
List all known property names. If you'd also like values, use props instead. The order of the properties is not defined, and the properties generally will not be sorted.
const m = sm`property color default "grey"; property size default 1; a -> b;`;
m.known_props(); // ['color', 'size']
An array of all property name strings defined on this machine.
Get the label for a given state, if any; return undefined otherwise.
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 display_text.
The state to get the label for.
The label string, or undefined if no label is set.
List all distinct action names defined anywhere in the machine.
An array of action name strings.
Lists all edges of a machine.
import { sm } from 'jssm';
const lswitch = sm`on 'toggle' <=> 'toggle' off;`;
lswitch.list_edges();
[
{
from: 'on',
to: 'off',
kind: 'main',
forced_only: false,
main_path: true,
action: 'toggle'
},
{
from: 'off',
to: 'on',
kind: 'main',
forced_only: false,
main_path: true,
action: 'toggle'
}
]
An array of all JssmTransition edge objects.
List all entrances attached to the current state. Please note that the order of the list is not defined. This list includes both unforced and forced entrances; if this isn't desired, consider {@link list_unforced_entrances} or {@link list_forced_entrances} as appropriate.
import { sm } from 'jssm';
const light = sm`red 'next' -> green 'next' -> yellow 'next' -> red; [red yellow green] 'shutdown' ~> off 'start' -> red;`;
light.state(); // 'red'
light.list_entrances(); // [ 'yellow', 'off' ]
The state whose entrances to have listed
List all action names available as exits from a given state.
Returns the empty array (does not throw) when whichState exists but has
no action-named exits — including terminal states, states whose only
exits are plain -> transitions, and states in machines that use no
actions at all. Only nonexistent states cause a throw.
The state to inspect. Defaults to the current state.
An array of action name strings, possibly empty.
List all exits attached to the current state. Please note that the order of the list is not defined. This list includes both unforced and forced exits; if this isn't desired, consider {@link list_unforced_exits} or {@link list_forced_exits} as appropriate.
import { sm } from 'jssm';
const light = sm`red 'next' -> green 'next' -> yellow 'next' -> red; [red yellow green] 'shutdown' ~> off 'start' -> red;`;
light.state(); // 'red'
light.list_exits(); // [ 'green', 'off' ]
The state whose exits to have listed
Get the map of named transitions (transitions with explicit names).
A Map from transition name to edge index.
List all states that have a specific action attached. Please note that the order of the states is not guaranteed.
import { sm } from 'jssm';
const machine = sm`
red 'next' -> green 'next' -> yellow 'next' -> red;
[red yellow green] 'shutdown' ~> off 'start' -> red;
`;
console.log( machine.list_states_having_action('next') ); // ['red', 'green', 'yellow']
console.log( machine.list_states_having_action('start') ); // ['off']
The action to be checked for associated states
List all transitions attached to the current state, sorted by entrance and exit. The order of each sublist is not defined. A node could appear in both lists.
import { sm } from 'jssm';
const light = sm`red 'next' -> green 'next' -> yellow 'next' -> red; [red yellow green] 'shutdown' ~> off 'start' -> red;`;
light.state(); // 'red'
light.list_transitions(); // { entrances: [ 'yellow', 'off' ], exits: [ 'green', 'off' ] }
The state whose transitions to have listed
Look up the full transition object for a given source→target pair.
Source state name.
Target state name.
The JssmTransition object, or undefined if none exists.
Get the machine's author list. Set via the FSL machine_author directive.
An array of author name strings.
Get the machine's comment string. Set via the FSL machine_comment directive.
The comment string.
Get the machine's contributor list. Set via the FSL machine_contributor directive.
An array of contributor name strings.
Get the machine's definition string. Set via the FSL machine_definition directive.
The definition string.
Get the machine's natural language as an ISO 639-1 code. Set via the FSL
machine_language directive, which accepts a language name or code, or a
BCP-47 tag whose region subtag is dropped (en-us -> en). Unrecognized
values resolve to undefined.
The ISO 639-1 language code (e.g. 'en'), or undefined if the
supplied value did not resolve to a known language.
Get the machine's license string. Set via the FSL machine_license directive.
The license string.
Get the machine's name. Set via the FSL machine_name directive.
The machine name string.
Get the complete internal state of the machine as a serializable structure. Includes actions, edges, edge map, named transitions, reverse actions, current state, and states map.
A JssmMachineInternalState snapshot.
Get the machine's version string. Set via the FSL machine_version directive.
The version string.
Get the npm package name associated with the machine. Set via the FSL npm_name directive.
Returns undefined when not present.
The npm package name string, or undefined.
Remove a previously-registered event handler. Match is by reference —
the same function value passed to Machine.on or
Machine.once. Returns true if a subscription was found and
removed, false otherwise.
const fn = (e: any) => console.log(e);
m.on('transition', fn);
m.off('transition', fn); // true
m.off('transition', fn); // false
The event name.
The handler reference to remove.
true if removed, false if no match was registered.
Subscribe to a typed observation event. Hooks (set_hook and friends)
intercept and may cancel a transition; events fire alongside the same
state-machine moments but cannot influence the outcome. This is the
surface most users actually want for "tell me when state changes".
Handlers run synchronously, in registration order. A throwing handler
does not block subsequent handlers — its exception is caught and
re-emitted as an error event whose detail names the original event
and the offending handler.
const m = sm`a -> b -> c;`;
m.on('transition', e => console.log(`${e.from} -> ${e.to}`));
m.on('entry', { state: 'b' }, e => console.log(`entered ${e.state}`));
const off = m.on('transition', () => {});
off(); // unsubscribe
The event name (drives the detail type).
The event name to subscribe to.
A function that unsubscribes when called.
Subscribe to a typed observation event for one matching delivery, then
auto-remove. Accepts the same (name, handler) and (name, filter, handler) shapes as Machine.on.
m.once('terminal', e => console.log(`done at ${e.state}`));
The event name.
The event name.
A function that unsubscribes early if called before the handler has fired.
Replace the current state and data with no regard to the graph.
import { sm } from 'jssm';
const machine = sm`a -> b -> c;`;
console.log( machine.state() ); // 'a'
machine.go('b');
machine.go('c');
console.log( machine.state() ); // 'c'
machine.override('a');
console.log( machine.state() ); // 'a'
Post-transition hook on a specific edge. Fires after the transition
from from to to has completed. Cannot block the transition.
Source state name.
Target state name.
Callback invoked after the transition.
this for chaining.
Post-transition hook on a specific action-labeled edge.
Source state name.
Target state name.
The action label.
Callback invoked after the transition.
this for chaining.
Post-transition hook on any action-driven transition.
Callback invoked after any action transition.
this for chaining.
Post-transition hook on any transition regardless of kind.
Callback invoked after every transition.
this for chaining.
Post-transition hook that fires after entering a specific state.
The state that was entered.
Callback invoked after entry.
this for chaining.
Post-transition hook that fires after leaving a specific state.
The state that was exited.
Callback invoked after exit.
this for chaining.
Post-transition hook on any forced (~>) transition.
Callback invoked after any forced transition.
this for chaining.
Post-transition hook on any edge triggered by a specific action.
The action name.
Callback invoked after any transition with this action.
this for chaining.
Post-transition hook on any main-path (=>) transition.
Callback invoked after any main transition.
this for chaining.
Post-transition hook on any standard (->) transition.
Callback invoked after any legal transition.
this for chaining.
Take n probabilistic steps and return a histograph of how many times
each state was visited.
Number of steps to walk.
A Map from state name to visit count.
Take a single random transition from the current state, weighted by edge probabilities.
true if a transition was taken, false otherwise.
Take n consecutive probabilistic transitions and return the sequence
of states visited (before each transition).
Number of steps to walk.
An array of state names visited during the walk.
List all action exits from a state with their probabilities.
The state to inspect. Defaults to the current state.
An array of { action, probability } objects.
Get the transitions available from a state for use by the probabilistic walk system.
If any exit declares a probability, only those probability-bearing
exits are returned, so that non-probability peers cannot dilute the
declared distribution. If no exit declares a probability, every
legal (non-forced) exit is returned, which weighted_rand_select
treats as equal weight. Forced-only exits (~>) are always excluded,
since they cannot be taken by an ordinary transition() call.
Fixes StoneCypher/fsl#1325, in which the function previously returned
every exit unconditionally — including forced-only exits and exits
with no probability, which distorted the weighted distribution.
The state to inspect.
An array of JssmTransition edges exiting the state, filtered as described above. May be empty.
Get the current value of a given property name. Checks the current
state's properties first, then falls back to the global default.
Returns undefined if neither exists. For a throwing variant, see
strict_prop.
const m = sm`property color default "grey"; a -> b;
state b: { property color "blue"; };`;
m.prop('color'); // 'grey' (default, because state is 'a')
m.go('b');
m.prop('color'); // 'blue' (state 'b' overrides the default)
m.prop('size'); // undefined (no such property)
The relevant property name to look up.
The value behind the prop name, or undefined if not defined.
Get the current value of every prop, as an object. If no current definition
exists for a prop — that is, if the prop was defined without a default and
the current state also doesn't define the prop — then that prop will be listed
in the returned object with a value of undefined.
const traffic_light = sm`
property can_go default true;
property hesitate default true;
property stop_first default false;
Off -> Red => Green => Yellow => Red;
[Red Yellow Green] ~> [Off FlashingRed];
FlashingRed -> Red;
state Red: { property stop_first true; property can_go false; };
state Off: { property stop_first true; };
state FlashingRed: { property stop_first true; };
state Green: { property hesitate false; };
`;
traffic_light.state(); // Off
traffic_light.props(); // { can_go: true, hesitate: true, stop_first: true; }
traffic_light.go('Red');
traffic_light.props(); // { can_go: false, hesitate: true, stop_first: true; }
traffic_light.go('Green');
traffic_light.props(); // { can_go: true, hesitate: false, stop_first: false; }
An object mapping every known property name to its current value
(or undefined if the property has no default and the current state
doesn't define it).
Get the raw state declaration objects as parsed from the FSL source.
An array of raw state declaration objects.
Remove a previously-registered hook described by a
HookDescription. Match is by kind + identifying keys
(from/to/action/etc.), not by handler reference — there is one
hook per slot in the registry, so the description uniquely identifies
which one to clear. Fires a hook-removal event for inspector tools.
This is the symmetric counterpart of Machine.set_hook for the event-bridging use case (#638). Reasoning about hooks via observation events requires being able to observe their disappearance too.
const m = sm`a -> b;`;
const fn = () => true;
m.set_hook({ kind: 'hook', from: 'a', to: 'b', handler: fn });
m.remove_hook({ kind: 'hook', from: 'a', to: 'b', handler: fn });
A hook descriptor identifying the hook to remove.
true if a hook was removed, false otherwise.
Resolves the full unified style/config cascade for a state — the runtime successor to the ad-hoc layer merge style_for used to perform.
For any state OTHER than the current one, this returns the memoized static
resolution (tiers 1–5; see _compose_state_config) — theme →
default_state_config → per-kind defaults → depth-ordered group metadata →
per-state config. The cache is keyed by state and never invalidated, since
those tiers do not depend on which state is current.
For the machine's CURRENTLY-occupied state the result is recomputed each
call (never cached) and additionally carries the dynamic active_state
layers: the active-state THEME layers fold in just below the per-state
config (tier 3-active), and the user active_state : { … } overlay folds
in LAST (tier 6), on top of everything, so it wins over per-state config.
Every fold uses {@link merge_state_config}, so a key set at a lower tier is
overridden — never rejected — by a higher one.
import { sm } from 'jssm';
const m = sm`&busy : [working]; idle 'go' -> working; state &busy : { color: orange; };`;
m.resolve_state_config('working').color; // '#ffa500ff' — from group &busy
The state to compute the composite config for.
The fully composited JssmStateConfig for the state, including the active overlay when the state is current.
Serialize the current machine, including all defining state but not the machine string, to a structure. This means you will need the machine string to recreate (to not waste repeated space;) if you want the machine string embedded, call {@link serialize_with_string} instead.
An optional comment string to embed in the serialized output for identification or debugging.
A JssmSerialization object containing the machine's current state, data, and timestamp.
Schedule an automatic transition to next_state after after_time
milliseconds. Only one timeout may be active at a time.
The state to transition to when the timer fires.
Delay in milliseconds.
Convenience method to create a new machine from a tagged template literal.
Equivalent to calling the top-level sm function.
The template string array.
Interpolated values.
A new Machine instance.
Get the current state of a machine.
import * as jssm from 'jssm';
const lswitch = jssm.from('on <=> off;');
console.log( lswitch.state() ); // 'on'
lswitch.transition('off');
console.log( lswitch.state() ); // 'off'
The current state name.
Get the processed state declaration for a specific state.
The state to look up.
The JssmStateDeclaration for the given state.
Get all processed state declarations as a Map.
A Map from state name to JssmStateDeclaration.
Get the internal state descriptor for a given state name.
The state to look up.
The JssmGenericState descriptor.
Does the given state carry any observational hook — i.e. should it receive
the hooked_state viz styling? True when an entry/exit/after hook is
bound to the state, any edge hook touches it, or the state has its own
boundary hook. Group-boundary hooks do not count here — they are
matched by group only and never propagate to member states. Powers the
hooked styling layer in Machine.resolve_state_config; replaces
the long-stubbed has_hooks placeholder (megaspec §12).
const m = sm`a -> b;`;
m.state_has_hooks('a'); // false
m.hook_exit('a', () => true);
m.state_has_hooks('a'); // true
The state to test.
true when the state is observed by at least one hook.
Check whether a specific state is complete (every exit has an action).
The state to check.
true if the state is complete.
Check whether a given state is final (either has no exits or is marked
complete.)
import { sm, state_is_final } from 'jssm';
const final_test = sm`first -> second;`;
console.log( final_test.state_is_final('first') ); // false
console.log( final_test.state_is_final('second') ); // true
The name of the state to check for finality
Check whether a specific state is terminal (has no exits).
The state to check.
true if the state has zero exits.
Get the configured after timeout for a given state, if any.
The state to look up.
A [targetState, delayMs] tuple, or undefined if no timeout
is configured for that state.
List all the states known by the machine. Please note that the order of these states is not guaranteed.
import * as jssm from 'jssm';
const lswitch = jssm.from('on <=> off;');
console.log( lswitch.states() ); // ['on', 'off']
An array of all state names in the machine.
Lists every state that is a transitive member of a named group — the flattened membership of the group, descending through nested and spread sub-groups, in member-declaration order.
import { sm } from 'jssm';
const m = sm`&inner : [a b]; &outer : [&inner c]; a -> b -> c;`;
m.statesIn('outer'); // [ 'a', 'b', 'c' ]
m.statesIn('inner'); // [ 'a', 'b' ]
The group whose transitive member states are wanted.
The transitive member states of groupName, in declaration order.
Get the current value of a given property name. If missing on the state
and without a global default, throws a JssmError, unlike
prop, which would return undefined instead.
const m = sm`property color default "grey"; a -> b;`;
m.strict_prop('color'); // 'grey'
m.strict_prop('size'); // throws JssmError
The relevant property name to look up.
The value behind the prop name.
Gets the composite style for a specific node — the public viz entry point, now a thin wrapper over the unified config cascade in resolve_state_config.
The order of composition runs least-specific to most-specific: theme
defaults, then the default_state_config root, then per-kind defaults
(terminal, start, end), then depth-ordered group metadata (inner groups
winning over outer), then the per-state config, and finally — for the
current state only — the active overlay. Last wins at every tier.
The state to compute the composite style for.
The fully composited JssmStateConfig for the given state.
Instruct the machine to complete a transition. Synonym for go.
const light = sm`
off 'start' -> red;
red 'next' -> green 'next' -> yellow 'next' -> red;
[red yellow green] 'shutdown' ~> off;
`;
light.state(); // 'off'
light.go('red'); // true
light.state(); // 'red'
light.go('green'); // true
light.state(); // 'green'
light.go('blue'); // !! false - no such state
light.state(); // 'green'
light.go('red'); // !! false - green may not go directly to red, only to yellow
light.state(); // 'green'
The state to switch to
The data change to insert during the transition
true if the transition was legal and occurred, false otherwise.
Shared transition core used by transition, force_transition, and action. Runs validation, fires the full hook pipeline (pre- everything, any-action, after, any-transition, exit, named, basic, edge-type, entry, everything), commits the new state if nothing rejected, and returns whether the transition succeeded.
Not meant for external use. Call one of the public wrappers instead:
transition for an ordinary legal transitionforce_transition to bypass the legality checkaction to dispatch by action name rather than target stateThe target state name (for a plain or forced
transition) or the action name (when wasAction is true).
Optional replacement machine data to install alongside the transition. Hooks may further override this via complex results.
true if the caller invoked force_transition, in
which case legality is checked against valid_force_transition rather
than valid_transition.
true if the caller invoked action, in which case
newStateOrAction is an action name and the target state is looked up
via the current action edge.
true if the transition was valid and every hook passed;
false if the transition was invalid or any hook rejected.
Check whether an action is available from the current state.
The action name to check.
Reserved for future data validation.
true if the action can be taken.
Check whether a forced transition to a given state exists from the current state.
The target state.
Reserved for future data validation.
true if a forced (or any) transition exists.
Check whether a transition to a given state is legal (non-forced) from the current state.
The target state.
Reserved for future data validation.
true if the transition is legal.
Generated using TypeDoc
Get the style for the active state. Does not include composition from an applied theme, or things from the underlying base stylesheet; only the modifications applied by this machine.