nope-js-browser
Version:
NoPE Runtime for the Browser. For nodejs please use nope-js-node
155 lines (154 loc) • 6.58 kB
JavaScript
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");