Files
interactor/interactor.js
Bryce Thorup 6f52c56857
All checks were successful
Pull request change / publish (pull_request) Successful in 10s
added better error handling for missing action handlers
2025-11-06 18:44:04 -07:00

82 lines
2.3 KiB
JavaScript

import { Stream } from "stream";
export class Interactor {
#actions = new Stream();
#reactions = new Stream();
#latestReaction;
#latestReactions = {};
constructor(initialReaction = new Reaction()) {
this.#actions.listen((action) => {
let funcRef = `_handle${action.constructor.name}`;
if (!this[funcRef])
throw `${this.constructor.name} does not have a handler defined for the action "${action.constructor.name}". Create one by adding a function definition named "${funcRef}" in the ${this.constructor.name} class.`;
this[funcRef](action);
});
this._react(initialReaction);
}
act(action) {
if (!(action instanceof Action)) throw "invalid action";
this.#actions.add(action);
}
observe(reactionHandler, provideLatestReaction = true) {
if (provideLatestReaction) reactionHandler(this.#latestReaction);
let listener = (reaction) => {
if (!(reaction instanceof Reaction)) throw "invalid reaction";
reactionHandler(reaction);
};
this.#reactions.listen(listener);
return listener;
}
ignore(listener) {
this.#reactions.ignore(listener);
}
_react(reaction) {
if (!(reaction instanceof Reaction)) throw "invalid reaction";
let latest = this.#latestReactions[reaction.constructor.name];
if (!reaction.equals(latest)) {
this.#latestReaction = reaction;
this.#latestReactions[reaction.constructor.name] = reaction;
this.#reactions.add(reaction);
}
}
}
export class Action {}
export class ActionBundle {
add(action) {
if (!(action.prototype instanceof Action)) {
throw "Only classes extended by `Action` can be added to a `ActionBundle`";
}
let actionName = action.name;
this[`#${actionName}`] = action;
Object.defineProperty(this, actionName, {
get: () => this[`#${actionName}`],
});
}
}
export class Reaction {
equals(reaction) {
return false;
}
}
export class ReactionBundle {
add(reaction) {
if (!(reaction.prototype instanceof Reaction)) {
throw "Only classes extended by `Reaction` can be added to a `ReactionBundle`";
}
let reactionName = reaction.name;
this[`#${reactionName}`] = reaction;
Object.defineProperty(this, reactionName, {
get: () => this[`#${reactionName}`],
});
}
}