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