@dbs-portal/core-module-registry
Version:
Core module registry system for automatic module discovery and registration
214 lines • 6.72 kB
JavaScript
/**
* Browser Event Emitter
*
* Browser-compatible event emitter that provides the same API as Node.js EventEmitter
* but works in both browser and Node.js environments without external dependencies.
*/
export class BrowserEventEmitter {
events = new Map();
onceEvents = new Map();
maxListeners = 10;
/**
* Add a listener for the specified event
*/
on(event, listener) {
if (!this.events.has(event)) {
this.events.set(event, new Set());
}
const listeners = this.events.get(event);
listeners.add(listener);
// Warn if too many listeners
if (listeners.size > this.maxListeners) {
console.warn(`MaxListenersExceededWarning: Possible EventEmitter memory leak detected. ${listeners.size} ${event} listeners added.`);
}
return this;
}
/**
* Add a one-time listener for the specified event
*/
once(event, listener) {
if (!this.onceEvents.has(event)) {
this.onceEvents.set(event, new Set());
}
this.onceEvents.get(event).add(listener);
return this;
}
/**
* Remove a listener for the specified event
*/
off(event, listener) {
const listeners = this.events.get(event);
if (listeners) {
listeners.delete(listener);
if (listeners.size === 0) {
this.events.delete(event);
}
}
const onceListeners = this.onceEvents.get(event);
if (onceListeners) {
onceListeners.delete(listener);
if (onceListeners.size === 0) {
this.onceEvents.delete(event);
}
}
return this;
}
/**
* Emit an event to all listeners
*/
emit(event, ...args) {
let hasListeners = false;
// Emit to regular listeners
const listeners = this.events.get(event);
if (listeners && listeners.size > 0) {
hasListeners = true;
// Create array to avoid issues with listeners being removed during iteration
const listenersArray = Array.from(listeners);
for (const listener of listenersArray) {
try {
listener.apply(this, args);
}
catch (error) {
// Emit error event if listener throws
this.emitError(error);
}
}
}
// Emit to once listeners and remove them
const onceListeners = this.onceEvents.get(event);
if (onceListeners && onceListeners.size > 0) {
hasListeners = true;
const onceListenersArray = Array.from(onceListeners);
this.onceEvents.delete(event); // Remove all once listeners
for (const listener of onceListenersArray) {
try {
listener.apply(this, args);
}
catch (error) {
this.emitError(error);
}
}
}
return hasListeners;
}
/**
* Remove all listeners for a specific event or all events
*/
removeAllListeners(event) {
if (event) {
this.events.delete(event);
this.onceEvents.delete(event);
}
else {
this.events.clear();
this.onceEvents.clear();
}
return this;
}
/**
* Get the number of listeners for an event
*/
listenerCount(event) {
const regularCount = this.events.get(event)?.size || 0;
const onceCount = this.onceEvents.get(event)?.size || 0;
return regularCount + onceCount;
}
/**
* Get all listeners for an event
*/
listeners(event) {
const regularListeners = Array.from(this.events.get(event) || []);
const onceListeners = Array.from(this.onceEvents.get(event) || []);
return [...regularListeners, ...onceListeners];
}
/**
* Set the maximum number of listeners before warning
*/
setMaxListeners(n) {
this.maxListeners = n;
return this;
}
/**
* Get the maximum number of listeners
*/
getMaxListeners() {
return this.maxListeners;
}
/**
* Get all event names that have listeners
*/
eventNames() {
const regularEvents = Array.from(this.events.keys());
const onceEvents = Array.from(this.onceEvents.keys());
return [...new Set([...regularEvents, ...onceEvents])];
}
/**
* Add listener (alias for on)
*/
addListener(event, listener) {
return this.on(event, listener);
}
/**
* Remove listener (alias for off)
*/
removeListener(event, listener) {
return this.off(event, listener);
}
/**
* Prepend listener to the beginning of the listeners array
*/
prependListener(event, listener) {
// Since we use Set, we can't really prepend, but we can simulate it
// by removing and re-adding all listeners in the right order
const listeners = this.events.get(event);
if (listeners) {
const existingListeners = Array.from(listeners);
listeners.clear();
listeners.add(listener);
existingListeners.forEach(l => listeners.add(l));
}
else {
this.on(event, listener);
}
return this;
}
/**
* Prepend one-time listener
*/
prependOnceListener(event, listener) {
const onceListeners = this.onceEvents.get(event);
if (onceListeners) {
const existingListeners = Array.from(onceListeners);
onceListeners.clear();
onceListeners.add(listener);
existingListeners.forEach(l => onceListeners.add(l));
}
else {
this.once(event, listener);
}
return this;
}
/**
* Emit error event safely
*/
emitError(error) {
// Avoid infinite recursion if error listener throws
if (this.listenerCount('error') > 0) {
try {
this.emit('error', error);
}
catch (e) {
console.error('EventEmitter error in error handler:', e);
}
}
else {
// If no error listeners, log to console
console.error('Unhandled EventEmitter error:', error);
}
}
}
// Create a default instance for compatibility
export const defaultEventEmitter = new BrowserEventEmitter();
// Export as default for easier importing
export default BrowserEventEmitter;
//# sourceMappingURL=BrowserEventEmitter.js.map