@phnq/message
Version:
Asynchronous, incremental messaging client and server
110 lines (109 loc) • 4.42 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LocalPubSubTransport = void 0;
const MessageTransport_1 = require("../MessageTransport");
const serialize_1 = require("../serialize");
class LocalPubSubTransport {
constructor(options) {
this.subIds = [];
this.subjectById = new Map();
this.options = options;
this.subIds = options.subscriptions.map(sub => PubSub.instance.subscribe(sub, (message) => {
if (this.receiveHandler) {
this.receiveHandler(message);
}
}));
}
send(message) {
return __awaiter(this, void 0, void 0, function* () {
const publishSubject = this.options.publishSubject;
let subject;
if (message.t === MessageTransport_1.MessageType.End) {
subject = this.subjectById.get(message.c);
}
else {
subject = typeof publishSubject === 'string' ? publishSubject : publishSubject(message);
}
if (subject === undefined) {
throw new Error('Could not get subject');
}
if (message.t === MessageTransport_1.MessageType.End) {
this.subjectById.delete(message.c);
}
else {
this.subjectById.set(message.c, subject);
}
const subCount = PubSub.instance.publish(subject, message);
if (subCount === 0) {
throw new Error(`No subscribers for subject: ${subject}`);
}
return undefined;
});
}
onReceive(receive) {
this.receiveHandler = receive;
}
close() {
return __awaiter(this, void 0, void 0, function* () {
for (const subId of this.subIds) {
PubSub.instance.unsubscribe(subId);
}
this.subIds = [];
});
}
}
exports.LocalPubSubTransport = LocalPubSubTransport;
class PubSub {
constructor() {
this.subIdIter = (function* subIdGen() {
let i = 0;
while (true) {
i += 1;
yield i;
}
})();
this.subscriptions = {};
}
publish(subject, message) {
var _a;
const subs = (_a = this.subscriptions[subject]) !== null && _a !== void 0 ? _a : [];
for (const sub of subs) {
/**
* This MessageTransport doesn't strictly require any marshalling or unmarshalling since the messages
* are sent synchronously within the same process. However, we still annotate and deannotate the message,
* effectively cloning it. The benefits are:
* - It ensures that the original message is not mutated by the subscriber.
* - It ensures compatibility with other transports.
*/
const clonedMessage = (0, serialize_1.deannotate)((0, serialize_1.annotate)(message));
sub.handler(clonedMessage);
}
return subs.length;
}
subscribe(subject, callback) {
var _a;
const subs = (_a = this.subscriptions[subject]) !== null && _a !== void 0 ? _a : [];
const subId = this.subIdIter.next().value;
subs.push({ subId, handler: callback });
this.subscriptions[subject] = subs;
return subId;
}
unsubscribe(subId) {
for (const subject in this.subscriptions) {
this.subscriptions[subject] = this.subscriptions[subject].filter(sub => sub.subId !== subId);
if (this.subscriptions[subject].length === 0) {
delete this.subscriptions[subject];
}
}
}
}
PubSub.instance = new PubSub();