UNPKG

10.8 kBSource Map (JSON)View Raw
1{"version":3,"file":"Bus.js","sourceRoot":"","sources":["Bus.ts"],"names":[],"mappings":";;;AAGA;;;;;;;;;;;;;;GAcG;AACH,MAAa,GAAG;IAQd;;;;;;OAMG;IACH,YAAmB,IAAc;QAZd,WAAM,GAAQ,EAAE,CAAC;QACjB,cAAS,GAA2B,EAAE,CAAC;QAC1D,yDAAyD;QACtC,oBAAe,GAAgB,IAAI,GAAG,EAAE,CAAC;QAU1D,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED;;;;;;;OAOG;IACI,SAAS,CAAC,KAAQ;QACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,IAAI,CAAC,sBAAsB,EAAE,CAAC;IAChC,CAAC;IAED;;;;;;;OAOG;IACI,iBAAiB,CAAC,QAA8B;QACrD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED;;;;;;;;OAQG;IACI,WAAW,CAAC,KAAQ;QACzB,MAAM,KAAK,GAAW,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACjD,IAAI,KAAK,IAAI,CAAC,EAAE;YACd,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAC7B,OAAO,IAAI,CAAC;SACb;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;;;OAQG;IACI,mBAAmB,CAAC,QAA8B;QACvD,MAAM,KAAK,GAAW,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACvD,IAAI,KAAK,IAAI,CAAC,EAAE;YACd,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAChC,OAAO,IAAI,CAAC;SACb;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;;;OAQG;IACI,OAAO,CAAC,MAAS;QACtB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAQ,EAA2B,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IACxG,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,KAAqB,EAAE,MAAS,EAAE,MAAkB;QAC/D,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE;YACrC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;SACvC;IACH,CAAC;IAED;;;;;;;OAOG;IACI,eAAe,CAAC,SAAY,EAAE,YAAiB;QACpD,KAAK,MAAM,UAAU,IAAI,YAAY,EAAE;YACrC,IAAI,oBAAoB,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAChE,IAAI,CAAC,oBAAoB,EAAE;gBACzB,oBAAoB,GAAG,EAAE,CAAC;gBAC1B,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC;aAC5D;YACD,oBAAoB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;SACtC;QACD,IAAI,CAAC,sBAAsB,EAAE,CAAC;IAChC,CAAC;IAED;;OAEG;IACI,sBAAsB;QAC3B,IAAI,IAAI,CAAC,eAAe,CAAC,IAAI,GAAG,CAAC,EAAE;YACjC,MAAM,WAAW,GAAG,EAAE,CAAC;YAEvB,uDAAuD;YACvD,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,EAAE;gBACpD,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;gBACrD,IAAI,YAAY,IAAI,CAAC,EAAE;oBACrB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;oBACpC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;iBAC9B;aACF;YAED,+DAA+D;YAC/D,0DAA0D;YAC1D,OAAO,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC7B,mEAAmE;gBACnE,IAAI,kBAAkB,GAAG,CAAC,CAAC,CAAC;gBAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;oBAC3C,IAAI,SAAS,GAAG,IAAI,CAAC;oBACrB,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAE,EAAE;wBAClE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE;4BACzE,SAAS,GAAG,KAAK,CAAC;4BAClB,MAAM;yBACP;qBACF;oBACD,IAAI,SAAS,EAAE;wBACb,kBAAkB,GAAG,CAAC,CAAC;wBACvB,MAAM;qBACP;iBACF;gBAED,+EAA+E;gBAC/E,IAAI,kBAAkB,GAAG,CAAC,EAAE;oBAC1B,MAAM,IAAI,KAAK,CAAC,2CAA2C,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;iBACzE;gBAED,mFAAmF;gBACnF,MAAM,gBAAgB,GAAG,WAAW,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACtE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;aACpC;SACF;IACH,CAAC;CACF;AA5KD,kBA4KC","sourcesContent":["import type { ActionObserver } from './ActionObserver';\nimport type { Actor, IAction, IActorOutput, IActorTest } from './Actor';\n\n/**\n * A publish-subscribe bus for sending actions to actors\n * to test whether or not they can run an action.\n *\n * This bus does not run the action itself,\n * for that a {@link Mediator} can be used.\n *\n * @see Actor\n * @see Mediator\n *\n * @template A The actor type that can subscribe to the sub.\n * @template I The input type of an actor.\n * @template T The test type of an actor.\n * @template O The output type of an actor.\n */\nexport class Bus<A extends Actor<I, T, O>, I extends IAction, T extends IActorTest, O extends IActorOutput> implements\n IBusArgs {\n public readonly name: string;\n protected readonly actors: A[] = [];\n protected readonly observers: ActionObserver<I, O>[] = [];\n // Mapping from dependency (after) to dependents (before)\n protected readonly dependencyLinks: Map<A, A[]> = new Map();\n\n /**\n * All enumerable properties from the `args` object are inherited to this bus.\n *\n * @param {IBusArgs} args Arguments object\n * @param {string} args.name The name for the bus\n * @throws When required arguments are missing.\n */\n public constructor(args: IBusArgs) {\n Object.assign(this, args);\n }\n\n /**\n * Subscribe the given actor to the bus.\n * After this, the given actor can be unsubscribed from the bus by calling {@link Bus#unsubscribe}.\n *\n * An actor that is subscribed multiple times will exist that amount of times in the bus.\n *\n * @param {A} actor The actor to subscribe.\n */\n public subscribe(actor: A): void {\n this.actors.push(actor);\n this.reorderForDependencies();\n }\n\n /**\n * Subscribe the given observer to the bus.\n * After this, the given observer can be unsubscribed from the bus by calling {@link Bus#unsubscribeObserver}.\n *\n * An observer that is subscribed multiple times will exist that amount of times in the bus.\n *\n * @param {ActionObserver<I, O>} observer The observer to subscribe.\n */\n public subscribeObserver(observer: ActionObserver<I, O>): void {\n this.observers.push(observer);\n }\n\n /**\n * Unsubscribe the given actor from the bus.\n *\n * An actor that is subscribed multiple times will be unsubscribed only once.\n *\n * @param {A} actor The actor to unsubscribe\n * @return {boolean} If the given actor was successfully unsubscribed,\n * otherwise it was not subscribed before.\n */\n public unsubscribe(actor: A): boolean {\n const index: number = this.actors.indexOf(actor);\n if (index >= 0) {\n this.actors.splice(index, 1);\n return true;\n }\n return false;\n }\n\n /**\n * Unsubscribe the given observer from the bus.\n *\n * An observer that is subscribed multiple times will be unsubscribed only once.\n *\n * @param {ActionObserver<I, O>} observer The observer to unsubscribe.\n * @return {boolean} If the given observer was successfully unsubscribed,\n * otherwise it was not subscribed before.\n */\n public unsubscribeObserver(observer: ActionObserver<I, O>): boolean {\n const index: number = this.observers.indexOf(observer);\n if (index >= 0) {\n this.observers.splice(index, 1);\n return true;\n }\n return false;\n }\n\n /**\n * Publish an action to all actors in the bus to test if they can run the action.\n *\n * @param {I} action An action to publish\n * @return {IActorReply<A extends Actor<I, T, O>, I extends IAction, T extends IActorTest,\n * O extends IActorOutput>[]}\n * An array of reply objects. Each object contains a reference to the actor,\n * and a promise to its {@link Actor#test} result.\n */\n public publish(action: I): IActorReply<A, I, T, O>[] {\n return this.actors.map((actor: A): IActorReply<A, I, T, O> => ({ actor, reply: actor.test(action) }));\n }\n\n /**\n * Invoked when an action was run by an actor.\n *\n * @param actor The action on which the {@link Actor#run} method was invoked.\n * @param {I} action The original action input.\n * @param {Promise<O>} output A promise resolving to the final action output.\n */\n public onRun(actor: Actor<I, T, O>, action: I, output: Promise<O>): void {\n for (const observer of this.observers) {\n observer.onRun(actor, action, output);\n }\n }\n\n /**\n * Indicate that the given actor has the given actor dependencies.\n *\n * This will ensure that the given actor will be present in the bus *before* the given dependencies.\n *\n * @param {A} dependent A dependent actor that will be placed before the given actors.\n * @param {A[]} dependencies Actor dependencies that will be placed after the given actor.\n */\n public addDependencies(dependent: A, dependencies: A[]): void {\n for (const dependency of dependencies) {\n let existingDependencies = this.dependencyLinks.get(dependency);\n if (!existingDependencies) {\n existingDependencies = [];\n this.dependencyLinks.set(dependency, existingDependencies);\n }\n existingDependencies.push(dependent);\n }\n this.reorderForDependencies();\n }\n\n /**\n * Reorder the bus based on all present dependencies.\n */\n public reorderForDependencies(): void {\n if (this.dependencyLinks.size > 0) {\n const actorsAfter = [];\n\n // Temporarily remove all actors that have dependencies\n for (const actorAfter of this.dependencyLinks.keys()) {\n const dependentPos = this.actors.indexOf(actorAfter);\n if (dependentPos >= 0) {\n this.actors.splice(dependentPos, 1);\n actorsAfter.push(actorAfter);\n }\n }\n\n // Iteratively append actors based on the first dependency link\n // that has all of its dependencies available in the array\n while (actorsAfter.length > 0) {\n // Find the first actor that has all of its dependencies available.\n let activeActorAfterId = -1;\n for (let i = 0; i < actorsAfter.length; i++) {\n let validLink = true;\n for (const dependency of this.dependencyLinks.get(actorsAfter[i])!) {\n if (!this.actors.includes(dependency) && actorsAfter.includes(dependency)) {\n validLink = false;\n break;\n }\n }\n if (validLink) {\n activeActorAfterId = i;\n break;\n }\n }\n\n // If none of the pending links are possible, there must be a cyclic dependency\n if (activeActorAfterId < 0) {\n throw new Error(`Cyclic dependency links detected in bus ${this.name}`);\n }\n\n // The dependent may not be available (yet), so we don't add it to the array (yet).\n const activeActorAfter = actorsAfter.splice(activeActorAfterId, 1)[0];\n this.actors.push(activeActorAfter);\n }\n }\n }\n}\n\nexport interface IBusArgs {\n /**\n * The name for this bus.\n * @default {<rdf:subject>}\n */\n name: string;\n}\n\n/**\n * Data interface for holding an actor and a promise to a reply from that actor.\n */\nexport interface IActorReply<A extends Actor<I, T, O>,\n I extends IAction, T extends IActorTest, O extends IActorOutput> {\n actor: A;\n reply: Promise<T>;\n}\n\nexport type IReply<\n I extends IAction = IAction,\n O extends IActorOutput = IActorOutput,\n T extends IActorTest = IActorTest\n> = IActorReply<Actor<I, T, O>, I, T, O>;\n\nexport type IBus<I extends IAction = IAction, O extends IActorOutput = IActorOutput, T extends IActorTest = IActorTest>\n= Bus<Actor<I, T, O>, I, T, O>;\n"]}
\No newline at end of file