(() => { // 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 Interpreter(machine2, initialContext, initialStateName) { if (typeof initialStateName === "undefined") { initialStateName = machine2.states[0].name; } const interpreter = { machine: machine2, state: initialStateName, context: initialContext, eventQueue: [], isTransitioning: false, peers: {}, peerSubscriptionIds: /* @__PURE__ */ new Map(), subscriptionsToEvents: {}, subscriptionsToState: {}, subscriptionsToSettledState: {}, isPaused: true }; interpreter.start = () => { start(interpreter); return interpreter; }; send(interpreter, ["entry", null]); return interpreter; } function start(interpreter) { if (interpreter.isPaused === true) { interpreter.isPaused = false; processEvents(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) { processEvents(interpreter); } } function processEvents(interpreter) { interpreter.isTransitioning = true; while (interpreter.eventQueue.length > 0 && interpreter.isPaused === false) { processNextEvent(interpreter); } interpreter.isTransitioning = false; Object.values(interpreter.subscriptionsToSettledState).forEach((callbackFunction) => { callbackFunction(interpreter); }); } function processNextEvent(interpreter) { const event = interpreter.eventQueue.shift(); if (typeof event !== "undefined") { const state = getState(interpreter); const eventReactionCouplings = getMatchingEventReactionCouplings(state, event); const reactions = eventReactionCouplings.map((eventReactionCoupling) => eventReactionCoupling.reactions).flat(); const { sideEffects, contextMutations, peerings, goto_ } = categorizeReactions(reactions); const originalContext = interpreter.context; contextMutations.forEach((contextMutation) => { interpreter.context = contextMutation.fn(interpreter.context, event, interpreter); }); if (goto_ !== null) { send(interpreter, ["exit", null]); interpreter.state = goto_.targetStateName; Object.values(interpreter.subscriptionsToState).forEach((callbackFunction) => { callbackFunction(event, interpreter); }); send(interpreter, ["entry", null]); } peerings.forEach((peering) => { addPeer(interpreter, peering.name, peering.peerCreationFunction(interpreter.context, event, interpreter)); }); Object.values(interpreter.subscriptionsToEvents).forEach((callbackFunction) => { callbackFunction(event, interpreter); }); sideEffects.forEach((sideEffect) => { sideEffect.fn(interpreter.context, event, interpreter, originalContext); }); } } function categorizeReactions(reactions) { let sideEffects = [], contextMutations = [], peerings = [], goto_ = null; reactions.forEach((reaction) => { if (reaction.type === "SideEffect") { sideEffects.push(reaction); } else if (reaction.type === "ContextMutation") { contextMutations.push(reaction); } else if (reaction.type === "Peering") { peerings.push(reaction); } else if (reaction.type === "Goto") { goto_ = reaction; } }); return { sideEffects, contextMutations, peerings, goto_ }; } var subscriptionId = 0; function subscribeToEvents(interpreter, callback) { subscriptionId++; interpreter.subscriptionsToEvents[subscriptionId.toString()] = callback; return subscriptionId.toString(); } function addPeer(self, name, peer) { self.peers[name] = peer; subscribeToEvents(peer, (e, peer2) => { if (self.isTransitioning === false) { send(self, [name + "." + e[0], e[1]]); } }); } // 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 = Interpreter(machine, { context: {} }).start(); })();