(() => { // src/index.ts var Machine = function(...states) { return { states }; }; var State = function(name, ...ons) { return { name, ons }; }; var On = function(eventName, ...reactions) { return { eventName, reactions }; }; var Do = function(fn) { return { type: "Do", fn }; }; var Goto = function(targetStateName) { return { type: "Goto", targetStateName }; }; var Tick = function(doFunctions) { return { doFunctions }; }; function interpret(machine, options) { let { state, context } = options; if (typeof state === "undefined") { state = machine.states[0].name; } const interpreter = { machine, state, context, tickQueues: [] }; console.log(interpreter); send(interpreter, ["entry", null]); return interpreter; } function getState(interpreter) { return interpreter.machine.states.find((state) => state.name === interpreter.state); } function getOns(state, event) { return state.ons.filter((on) => on.eventName === event[0]); } function noop() { } function send(interpreter, event) { const state = getState(interpreter); const onsTree = getOns(state, event); const reactions = onsTree.map((on) => on.reactions).flat(); const indexOfFirstGoto = reactions.findIndex((reaction) => reaction.type === "Goto"); const indexOfFinalReaction = indexOfFirstGoto === -1 ? reactions.length - 1 : indexOfFirstGoto; const reactionsUntilFirstGoto = reactions.slice(0, indexOfFinalReaction + 1); const functionsToRunInTick = reactionsUntilFirstGoto.map((reaction) => { if (reaction.type === "Do") { return reaction.fn; } else if (reaction.type === "Goto") { return (ctx, e, self) => { self.state = reaction.targetStateName; send(self, ["entry", e]); }; } else { return noop; } }); const tick = Tick(functionsToRunInTick); tick.doFunctions.forEach((fn) => { fn(interpreter.context, event, interpreter); }); } // src/tests/01-ping-pong.ts var wait = (ms) => new Promise((resolve) => { setTimeout(() => { resolve(1); }, ms); }); var makeRequest = (ctx, e, self) => { send(ctx.serverActor, ["received-request", self]); }; var sendResponse = (ctx, e, self) => { send(ctx.clientActor, ["received-response", self]); }; var startTimer = async (ctx, e, self) => { await wait(500); send(self, ["timer-finished", null]); }; var log = (ctx, e, self) => { console.log(self.state); }; var client = Machine( State( "idle", On( "entry", Do(log) ), On( "server-created", Do((_ctx, [_eventName, serverActor2], self) => { self.context.serverActor = serverActor2; }), Goto("making-request") ) ), State( "making-request", On( "entry", Do(log), Do(makeRequest), Goto("awaiting-response") ) ), State( "awaiting-response", On( "entry", Do(log) ), On( "received-response", Do(log), Goto("making-request") ) ) ); var server = Machine( State( "awaiting-request", On( "entry", Do(log) ), On( "received-request", Do((_ctx, [_eventName, clientActor2], self) => { self.context.clientActor = clientActor2; }), Goto("sending-response") ) ), State( "sending-response", On( "entry", Do(log), Do(startTimer) ), On( "timer-finished", Do(sendResponse), Goto("awaiting-request") ) ) ); var clientActor = interpret(client, { context: {} }); var serverActor = interpret(server, { context: {} }); send(clientActor, ["server-created", serverActor]); })();