This is the template project that's checked out and configured when you run the bando-up command from ljsthw-bandolier. This is where the code really lives.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bandolier-template/client/fsm.js

56 lines
1.7 KiB

import assert from "./assert.js";
import { log } from "./logging.js";
export class FSM {
constructor(data, events) {
// data is really only for logging/debugging
this.data = data;
this.events = events;
this.state = "START";
this.on_state = undefined;
}
onTransition(cb) {
this.transition_cb = cb;
}
transition(next_state) {
this.state = next_state;
if(this.transition_cb) {
this.transition_cb(this);
}
}
event_names() {
return Object.getOwnPropertyNames(Object.getPrototypeOf(this.events)).filter(k => k !== "constructor");
}
async do(event, ...args) {
const evhandler = this.events[event];
assert(evhandler !== undefined, `Invalid event ${event}. Available ones are '${this.event_names()}'.`);
// NOTE: you have to use .call to pass in the this.events object or else this === undefined in the call
const next_state = await evhandler.call(this.events, this.state, ...args);
assert(next_state, `The event "${event}" returned "${next_state}" but must be a truthy true state.`);
if(Array.isArray(next_state)) {
assert(next_state.length == 2, `Returning an array only allows 2 elements (state, func) but you returned ${next_state}`);
let [state, func] = next_state;
log.debug(`FSM ${this.events.constructor.name}: (${event}) = ${this.state} -> ${state} (${args})`, "DATA:", this.data, func ? `THEN ${func.name}()` : undefined);
this.transition(state);
await func();
} else {
log.debug(`FSM ${this.events.constructor.name}: (${event}) = ${this.state} -> ${next_state} (${args})`, "DATA:", this.data);
this.transition(next_state);
}
return this.state;
}
}
export default FSM;