UNPKG

@454creative/easy-events

Version:

A minimal event engine for Node.js and NestJS, wrapping Emmett for lightweight in-process event handling

263 lines 9.76 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EmmettEngine = void 0; const emmett_1 = __importDefault(require("emmett")); class EmmettEngine { constructor(observability) { this.emitter = new emmett_1.default(); this.handlerWrappers = new Map(); this.onceWrappers = new Map(); this.maxListeners = 10; // Default max listeners this.observability = observability; } emit(event, payload, tracing) { // Record event for observability (non-blocking) if (this.observability) { try { this.observability.recordEvent(event, payload, tracing); } catch (error) { // Silently ignore observability errors console.warn("Observability error:", error); } } // Emit the event this.emitter.emit(event, payload); } on(event, handler) { // Check max listeners const currentListeners = this.listeners(event); if (currentListeners >= this.maxListeners) { console.warn(`EmmettEngine: Max listeners (${this.maxListeners}) exceeded for event '${event}'. Use setMaxListeners() to increase the limit.`); } // Create wrapper function for observability const wrapper = (eventObj) => { try { // Execute handler handler(eventObj.data); // Record successful execution (non-blocking) if (this.observability) { try { this.observability.recordHandlerExecution(event, "handler", 0, true); } catch (_error) { // Silently ignore observability errors } } } catch (error) { // Record error (non-blocking) if (this.observability) { try { this.observability.recordError(event, error instanceof Error ? error : new Error(String(error))); } catch (_obsError) { // Silently ignore observability errors } } throw error; } }; // Store the mapping between original handler and wrapper if (!this.handlerWrappers.has(event)) { this.handlerWrappers.set(event, new Map()); } this.handlerWrappers.get(event).set(handler, wrapper); // Register the wrapper with emmett this.emitter.on(event, wrapper); } once(event, handler) { // Check max listeners const currentListeners = this.listeners(event); if (currentListeners >= this.maxListeners) { console.warn(`EmmettEngine: Max listeners (${this.maxListeners}) exceeded for event '${event}'. Use setMaxListeners() to increase the limit.`); } // Create wrapper function for observability that removes itself after execution const wrapper = (eventObj) => { try { // Execute handler handler(eventObj.data); // Record successful execution (non-blocking) if (this.observability) { try { this.observability.recordHandlerExecution(event, "handler", 0, true); } catch (_error) { // Silently ignore observability errors } } // Remove the handler after execution this.off(event, handler); } catch (error) { // Record error (non-blocking) if (this.observability) { try { this.observability.recordError(event, error instanceof Error ? error : new Error(String(error))); } catch (_obsError) { // Silently ignore observability errors } } throw error; } }; // Store the mapping between original handler and wrapper if (!this.onceWrappers.has(event)) { this.onceWrappers.set(event, new Map()); } this.onceWrappers.get(event).set(handler, wrapper); // Register the wrapper with emmett this.emitter.on(event, wrapper); } off(event, handler) { if (event && handler) { // Remove specific handler for specific event const wrapper = this.handlerWrappers.get(event)?.get(handler); if (wrapper) { this.emitter.off(event, wrapper); this.handlerWrappers.get(event).delete(handler); if (this.handlerWrappers.get(event).size === 0) { this.handlerWrappers.delete(event); } } // Also check once wrappers const onceWrapper = this.onceWrappers.get(event)?.get(handler); if (onceWrapper) { this.emitter.off(event, onceWrapper); this.onceWrappers.get(event).delete(handler); if (this.onceWrappers.get(event).size === 0) { this.onceWrappers.delete(event); } } } else if (event) { // Remove all handlers for specific event const wrappers = this.handlerWrappers.get(event); if (wrappers) { wrappers.forEach((wrapper) => { this.emitter.off(event, wrapper); }); this.handlerWrappers.delete(event); } const onceWrappers = this.onceWrappers.get(event); if (onceWrappers) { onceWrappers.forEach((wrapper) => { this.emitter.off(event, wrapper); }); this.onceWrappers.delete(event); } } else if (handler) { // Remove specific handler from all events this.handlerWrappers.forEach((eventWrappers, eventName) => { const wrapper = eventWrappers.get(handler); if (wrapper) { this.emitter.off(eventName, wrapper); eventWrappers.delete(handler); if (eventWrappers.size === 0) { this.handlerWrappers.delete(eventName); } } }); this.onceWrappers.forEach((eventWrappers, eventName) => { const wrapper = eventWrappers.get(handler); if (wrapper) { this.emitter.off(eventName, wrapper); eventWrappers.delete(handler); if (eventWrappers.size === 0) { this.onceWrappers.delete(eventName); } } }); } else { // Remove all handlers this.handlerWrappers.forEach((eventWrappers, eventName) => { eventWrappers.forEach((wrapper) => { this.emitter.off(eventName, wrapper); }); }); this.handlerWrappers.clear(); this.onceWrappers.forEach((eventWrappers, eventName) => { eventWrappers.forEach((wrapper) => { this.emitter.off(eventName, wrapper); }); }); this.onceWrappers.clear(); } } listeners(event) { return this.emitter.listeners(event).length; } removeAllListeners(event) { if (event) { this.off(event); } else { this.off(); } } eventNames() { const events = new Set(); // Add events from handler wrappers this.handlerWrappers.forEach((_, event) => { events.add(event); }); // Add events from once wrappers this.onceWrappers.forEach((_, event) => { events.add(event); }); return Array.from(events); } listenerCount(event) { return this.listeners(event); } prependListener(event, handler) { // Note: Emmett doesn't support prepend, so we'll just use regular on // This is a limitation of the underlying library this.on(event, handler); } setMaxListeners(n) { this.maxListeners = n; } getMaxListeners() { return this.maxListeners; } rawListeners(event) { const wrappers = []; // Add regular handler wrappers const handlerWrappers = this.handlerWrappers.get(event); if (handlerWrappers) { handlerWrappers.forEach((wrapper) => { wrappers.push(wrapper); }); } // Add once handler wrappers const onceWrappers = this.onceWrappers.get(event); if (onceWrappers) { onceWrappers.forEach((wrapper) => { wrappers.push(wrapper); }); } return wrappers; } hasListeners(event) { return this.listeners(event) > 0; } clear() { this.removeAllListeners(); } // Observability methods setObservability(observability) { this.observability = observability; } getObservability() { return this.observability; } } exports.EmmettEngine = EmmettEngine; //# sourceMappingURL=emmett-engine.js.map