You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
5.3 KiB
JavaScript

(() => {
// 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();
})();