UNPKG

nope-js-browser

Version:

NoPE Runtime for the Browser. For nodejs please use nope-js-node

155 lines (154 loc) 6.58 kB
import { plugin } from "./plugin"; import { NopeEventEmitter } from "../eventEmitter"; import { generateId, difference } from "../helpers/index.browser"; export const extend = plugin([ "communication.Bridge", "dispatcher.connectivityManager.NopeConnectivityManager", ], (clBridge, clConnectivityManager) => { class Bridge extends clBridge { constructor(id, logger) { super(id, logger); this._onMessageReceived = new NopeEventEmitter(); this._openMessages = new Map(); this.defaultTargets = []; this.ackReplyId = null; this.onTransportError = new NopeEventEmitter(); this.onTransportError.subscribe((err) => { if (this._logger) { this._logger.error("Failed to receive an acknowledge message!"); this._logger.error(err); } else { console.error("Failed to receive an acknowledge message!"); console.error(err); } }); this.on("ackMessage", (msg) => this._onMessageReceived.emit(msg)).catch((err) => { if (this._logger) { this._logger.error("Failed to subscribe to 'ackMessage'"); this._logger.error(err); } else { console.error("Failed to subscribe to 'ackMessage'"); console.error(err); } }); } async emit(eventname, data, target = null, timeout = 0) { if (eventname !== "ackMessage" && this.ackReplyId) { // Firstly we try to define the Target. let targetToUse = new Set(); if (target === null) { if (this.defaultTargets) { targetToUse = new Set(this.defaultTargets); } else if (data.target) { targetToUse.add(data.target); } } else { if (typeof target === "string") { targetToUse.add(target); } else if (Array.isArray(target)) { target.map((item) => targetToUse.add(item)); } } if (targetToUse.size) { const messageId = generateId(); data.messageId = messageId; // We will define a Promise, which will wait for the ackknowledge ment. const promise = this._onMessageReceived.waitFor((msg) => { // If the Message is still open we try to // close it. if (this._openMessages.has(msg.messageId)) { const target = this._openMessages.get(msg.messageId).target; const received = this._openMessages.get(msg.messageId).received; received.add(msg.dispatcherId); // Therefore we determine the difference between // the targets and if (difference(target, received).size === 0) { this._openMessages.delete(msg.messageId); return true; } } return false; }, { timeout, }); // Now lets call emit const res = await super.emit(eventname, data); // And now we will await the // Wait - For result. await promise; return res; } } return await super.emit(eventname, data); } async on(eventname, cb) { if (eventname === "ackMessage") { return await super.on(eventname, cb); } else { return await super.on(eventname, (msg) => { cb(msg); if (msg.messageId && this.ackReplyId) { this.emit("ackMessage", { messageId: msg.messageId, dispatcherId: this.ackReplyId, }).catch((err) => { if (this._logger) { this._logger.error("Failed to emit an acknowledge message!"); this._logger.error(err); } else { console.error("Failed to emit an acknowledge message!"); console.error(err); } }); } }); } } } class NopeConnectivityManager extends clConnectivityManager { constructor(options, _generateObservable, id) { super(options, _generateObservable, id); this._communicator.ackReplyId = this.id; this.forceAckMessage = true; this.dispatchers.data.subscribe((dispatchers) => { if (this.forceAckMessage) { const dispatchersWithPlugin = dispatchers.filter((item) => { return this.dispatchers.originalData .get(item) .plugins.includes("ackMessages"); }); this._communicator.defaultTargets = dispatchersWithPlugin; } }); } /** * Add our Plugin to the Status Message. * @returns We now enlist our Plugin. */ _info() { const ret = super._info(); ret.plugins.push("ackMessages"); return ret; } } return [ { adapted: Bridge, name: "Bridge", path: "communication.Bridge", }, { adapted: NopeConnectivityManager, name: "NopeConnectivityManager", path: "dispatcher.connectivityManager.NopeConnectivityManager", }, ]; }, "ackMessages");