Finite state machines for Jennifer: define
states and transitions, fire events with guards and enter and leave hooks,
and keep the current state wherever the program already keeps its data. A
misspelt state or a transition that could never be taken fails when the
machine is built; check finds unreachable states and dead ends. Machines
load from TOML, YAML or JSON and draw as Mermaid or Graphviz.
jvc add @deckyard/fsmThis vendors the deck to vendor/<scope>/fsm/. Without jvc, unpack the
src/ subtree of a release archive (<scope>-fsm-<version>.tar.gz) there.
import "@deckyard/fsm/" as fsm;
def m as fsm.Machine init fsm.machine("door", "closed");
$m = fsm.addStates($m, ["open", "locked"]);
$m = fsm.addTransition($m, "open", ["closed"], "open");
$m = fsm.addTransition($m, "close", ["open"], "closed");
$m = fsm.addTransition($m, "lock", ["closed"], "locked");
$m = fsm.addTransition($m, "unlock", ["locked"], "closed");
def state as string init "closed";
$state = fsm.fire($m, $state, "lock", {}).target;
io.printf("%s, can %v\n", $state, fsm.events($m, $state, {})); # locked, can ["unlock"]
Jennifer 0.25.0 or newer, jennifer or jennifer-tiny. No capabilities:
the deck is pure computation.
tools/check-all.sh # the full gate, as CI runs it
jennifer test src/fsm_test.j # the test overlay
tools/check-docs.sh # docs against the code
tools/pack.sh dist # build the release archiveLGPL-3.0-only, see LICENSE. Copyright (C) 2026 mplx <mplx@jennifer-lang.dev>.
tools/check-headers.sh checks every source file's SPDX header against
deck.toml.