@glandjs/events
Version:
A fast, zero‑dependency event broker and message bus for building scalable, event‑driven applications.
273 lines (272 loc) • 8.97 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventBroker = void 0;
const utils_1 = require("./utils");
const broker_channel_1 = require("./broker-channel");
const event_emitter_1 = require("./engine/event-emitter");
class EventBroker {
constructor(options) {
this.options = options;
this._connections = new Map();
this._channels = new Map();
this._eventTraces = new Map();
this.options = this.normalizeOptions(options);
this._id = options.name;
this._emitter = new event_emitter_1.EventEmitter(this.options.delimiter, this.options.cacheSize, this.options.defaultTimeout, this.options.maxListeners);
}
trackEvent(eventId, sourceId) {
let trace = this._eventTraces.get(eventId);
if (!trace) {
trace = {
eventId,
sourceId,
visited: new Set([this._id]),
timestamp: Date.now(),
};
this._eventTraces.set(eventId, trace);
}
else {
trace.visited.add(this._id);
}
return trace;
}
hasProcessedEvent(eventId) {
const trace = this._eventTraces.get(eventId);
return trace ? trace.visited.has(this._id) : false;
}
normalizeOptions(options) {
return {
name: options.name,
cacheSize: options.cacheSize ?? 6,
delimiter: options.delimiter ?? ':',
ignoreErrors: options.ignoreErrors ?? false,
defaultTimeout: options.defaultTimeout ?? 1000,
maxListeners: options.maxListeners ?? 5,
};
}
get id() {
return this._id;
}
on(event, listener, options) {
const result = this._emitter.on(event, listener, options);
if (result instanceof Promise) {
return result;
}
return this;
}
off(event, listener) {
this._emitter.off(event, listener);
return this;
}
emit(event, payload, options) {
const eventId = options?._eventId || (0, utils_1.generateUUID)();
const sourceId = options?._sourceId || this._id;
const propagate = options?._propagate ?? false;
if (!this.hasProcessedEvent(eventId)) {
this.trackEvent(eventId, sourceId);
this._emitter.emit(event, payload, options);
if (propagate) {
for (const [brokerId, connectedBroker] of this._connections.entries()) {
try {
const trace = this._eventTraces.get(eventId);
if (trace && !trace.visited.has(brokerId)) {
connectedBroker.emit(event, payload, {
...options,
_eventId: eventId,
_sourceId: sourceId,
_propagate: true,
});
}
}
catch (error) {
if (!this.options.ignoreErrors) {
throw error;
}
}
}
}
}
return this;
}
once(event, listener, options) {
const result = this._emitter.once(event, listener, options);
if (result instanceof Promise) {
return result;
}
return this;
}
watch(event, timeoutMs) {
return this._emitter.watch(event, timeoutMs);
}
getListener(event) {
return this._emitter.getListener(event);
}
call(event, data, strategy) {
const listeners = this.getListener(event);
if (!listeners.length) {
return [];
}
switch (strategy) {
case 'all': {
return listeners.map((listener) => listener(data));
}
default:
const firstListener = listeners[0];
return firstListener(data);
}
}
channel(name) {
if (this._channels.has(name)) {
return this._channels.get(name);
}
const channel = new broker_channel_1.BrokerChannel(this, name, this.options.delimiter);
this._channels.set(name, channel);
return channel;
}
broadcast(event, payload, options) {
const eventId = (0, utils_1.generateUUID)();
this.emit(event, payload, {
...options,
_eventId: eventId,
_sourceId: this._id,
_propagate: true,
});
return this;
}
send(event, target, options) {
const eventId = (0, utils_1.generateUUID)();
target.emit(event, null, {
...options,
_eventId: eventId,
_sourceId: this._id,
_propagate: false,
});
return this;
}
connectTo(broker, options) {
if (!broker || !broker.id) {
throw new Error('Invalid broker');
}
if (broker.id === this._id) {
throw new Error(`Cannot connect broker "${this._id}" to itself`);
}
if (this._connections.has(broker.id)) {
return this;
}
this._connections.set(broker.id, broker);
if (typeof broker.connectTo === 'function' && !broker.isConnected(this._id)) {
broker.connectTo(this, options);
}
if (options?.events && options.events.length > 0) {
for (const event of options.events) {
this.on(event, (payload) => {
broker.emit(event, payload, {
_eventId: (0, utils_1.generateUUID)(),
_sourceId: this._id,
_propagate: false,
});
return undefined;
});
}
}
return this;
}
disconnect(brokerId) {
const broker = this._connections.get(brokerId);
const result = this._connections.delete(brokerId);
if (result && broker && typeof broker.disconnect === 'function') {
broker.disconnect(this._id);
}
return result;
}
isConnected(brokerId) {
return this._connections.has(brokerId);
}
emitTo(brokerId, event, payload, options) {
const broker = this._connections.get(brokerId);
if (!broker) {
return false;
}
const eventId = (0, utils_1.generateUUID)();
broker.emit(event, payload, {
...options,
_eventId: eventId,
_sourceId: this._id,
_propagate: false,
});
return true;
}
createConnections(brokers, options) {
for (const broker of brokers) {
this.connectTo(broker, options);
}
for (let i = 0; i < brokers.length; i++) {
for (let j = i + 1; j < brokers.length; j++) {
brokers[i].connectTo(brokers[j], options);
}
}
return this;
}
getConnections() {
return Array.from(this._connections.keys());
}
getConnection(brokerId) {
return this._connections.get(brokerId);
}
disconnectAll() {
for (const brokerId of this._connections.keys()) {
this.disconnect(brokerId);
}
return this;
}
callTo(brokerId, event, data, strategy) {
const broker = this._connections.get(brokerId);
if (!broker) {
return [];
}
return broker.call(event, data, strategy);
}
broadcastTo(brokerIds, event, payload, options) {
const eventId = (0, utils_1.generateUUID)();
for (const brokerId of brokerIds) {
const broker = this._connections.get(brokerId);
if (broker) {
broker.emit(event, payload, {
...options,
_eventId: eventId,
_sourceId: this._id,
_propagate: false,
});
}
}
return this;
}
findBroker(brokerId, maxDepth = 3) {
if (this.id === brokerId) {
return this;
}
const directConnection = this._connections.get(brokerId);
if (directConnection) {
return directConnection;
}
if (maxDepth <= 0) {
return undefined;
}
for (const [, connectedBroker] of this._connections.entries()) {
if (connectedBroker.id === this.id) {
continue;
}
const found = connectedBroker.findBroker(brokerId, maxDepth - 1);
if (found) {
return found;
}
}
return undefined;
}
shutdown() {
this._emitter.shutdown();
this._connections.clear();
this._channels.clear();
}
}
exports.EventBroker = EventBroker;