(() => { // src/index.ts var Machine = function(...states) { return { states }; }; var State = function(name, ...eventReactionCouplings) { return { name, eventReactionCouplings }; }; var On = function(eventName, ...reactions) { return { eventName, reactions }; }; var SideEffect = function(fn) { return { type: "SideEffect", fn }; }; var Goto = function(targetStateName) { return { type: "Goto", targetStateName }; }; function interpret(machine2, options) { let { state, context } = options; if (typeof state === "undefined") { state = machine2.states[0].name; } const interpreter = { machine: machine2, state, context, eventQueue: [], isTransitioning: false }; send(interpreter, ["entry", null]); return interpreter; } function getState(interpreter) { return interpreter.machine.states.find((state) => state.name === interpreter.state); } function getMatchingEventReactionCouplings(state, event) { return state.eventReactionCouplings.filter((eventReactionCoupling) => eventReactionCoupling.eventName === event[0]); } function send(interpreter, event) { interpreter.eventQueue.push(event); if (interpreter.isTransitioning === false) { interpreter.isTransitioning = true; while (interpreter.eventQueue.length > 0) { processNextEvent(interpreter); } interpreter.isTransitioning = false; } } function processNextEvent(interpreter) { const nextEvent = interpreter.eventQueue.shift(); if (typeof nextEvent !== "undefined") { const state = getState(interpreter); const eventReactionCouplings = getMatchingEventReactionCouplings(state, nextEvent); const reactions = eventReactionCouplings.map((eventReactionCoupling) => eventReactionCoupling.reactions).flat(); const { sideEffects, contextMutations, goto_ } = categorizeReactions(reactions); sideEffects.forEach((sideEffect) => { sideEffect.fn(interpreter.context, nextEvent, interpreter); }); contextMutations.forEach((contextMutation) => { interpreter.context = contextMutation.fn(interpreter.context, nextEvent, interpreter); }); if (goto_ !== null) { interpreter.state = goto_.targetStateName; send(interpreter, ["entry", null]); } } } function categorizeReactions(reactions) { let sideEffects = [], contextMutations = [], goto_ = null; reactions.forEach((reaction) => { if (reaction.type === "SideEffect") { sideEffects.push(reaction); } else if (reaction.type === "ContextMutation") { contextMutations.push(reaction); } else if (reaction.type === "Goto") { goto_ = reaction; } }); return { sideEffects, contextMutations, goto_ }; } // src/tests/00-basic.ts var beginTimer = (ctx, e, self) => { setTimeout(() => { send(self, ["timer-finished", null]); }, 800); }; var log = (ctx, e, self) => { console.log(self.state); }; var machine = Machine( State( "green", On( "entry", SideEffect(beginTimer), SideEffect(log) ), On( "timer-finished", Goto("yellow") ) ), State( "yellow", On( "entry", SideEffect(beginTimer), SideEffect(log) ), On( "timer-finished", Goto("red") ) ), State( "red", On( "entry", SideEffect(beginTimer), SideEffect(log) ), On( "timer-finished", Goto("green") ) ) ); var actor = interpret(machine, { context: {} }); })();