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}`], }); } }