Light Bulb

The smallest machine worth writing: two states, Off and On, and a single toggle event that flips between them. It is the “hello world” of state machines — just enough to see the engine, its lifecycle hooks, and nothing else.

Try it

Click Toggle to dispatch the toggle event. The statechart highlights the active state; the log records each transition.

💡

The configuration

const config = {
  key: "LightBulb",
  transitions: [
    ["", "*", "Off"],        // initial: enter Off
    ["Off", "toggle", "On"], // Off + toggle → On
    ["On", "toggle", "Off"], // On  + toggle → Off
  ],
};

The first triple is the initial transition: "" as the source means “on start, go here,” and "*" matches any event. The other two are the toggle cycle.

Wiring behaviour with the engine

Register one onStateCreate callback. It fires for every state the machine creates; attach that state’s onEnter / onExit hooks inside it.

import { FsmProcess } from "@statewalker/fsm";

const process = new FsmProcess(config);

process.onStateCreate((state) => {
  state.onEnter(() => console.log("→", state.key));
});

await process.dispatch("");        // enter Off
await process.dispatch("toggle");  // Off → On
await process.dispatch("toggle");  // On → Off

That is the whole example. dispatch("") enters the initial state; each dispatch("toggle") flips it.

The same thing with startProcess

If you would rather not manage onStateCreate yourself, the runner does the wiring. You return the handlers for each state from a single load callback:

import { startProcess, KEY_DISPATCH } from "@statewalker/fsm";

const context = {};
await startProcess(context, config, (stateKey) => {
  if (stateKey === "On")  return [() => console.log("💡 on")];
  if (stateKey === "Off") return [() => console.log("· off")];
  return [];
});

await context[KEY_DISPATCH]("toggle");   // 💡 on

Next