UNPKG

@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
"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;