/** * @module @dotdo/postgres-shared/event-emitter-safe * * Safe event emitter implementation that prevents memory leaks. * * Features: * - Maximum listener limit with configurable warning/error behavior * - Optional WeakRef pattern for automatic cleanup of garbage-collected listeners * - Safe iteration during emit (handles listener modifications) * - Error isolation (one listener error doesn't block others) * - Full TypeScript support with typed events * * This addresses memory leak issues documented in PLAN-TDD-ISSUES.md: * - postgres-mem1: Memory leak in event listeners * - Extract to shared EventEmitterSafe class */ /** * Listener function type */ export type Listener = (event: T) => void; /** * Error handler for listener errors */ export type ListenerErrorHandler = (error: unknown, event: string, listener: Listener) => void; /** * Configuration options for EventEmitterSafe */ export interface EventEmitterSafeConfig { /** * Maximum number of listeners per event. * Set to 0 for unlimited (not recommended). * @default 100 */ maxListeners?: number; /** * Behavior when max listeners is reached. * - 'warn': Log a warning but allow the listener * - 'error': Throw an error * - 'ignore': Silently ignore * @default 'warn' */ maxListenersBehavior?: 'warn' | 'error' | 'ignore'; /** * Enable WeakRef pattern for listeners. * When enabled, listeners can be garbage collected if no other references exist. * Note: This requires the listener to be referenced elsewhere to work properly. * @default false */ useWeakRef?: boolean; /** * Custom error handler for listener errors. * If not provided, errors are silently caught to prevent blocking other listeners. */ onListenerError?: ListenerErrorHandler; /** * Enable debug logging * @default false */ debug?: boolean; } /** * Statistics about the event emitter */ export interface EventEmitterStats { /** Total number of events registered */ eventCount: number; /** Total number of listeners across all events */ totalListeners: number; /** Breakdown of listeners per event */ listenersPerEvent: Record; /** Number of listeners cleaned up (weak refs that were garbage collected) */ cleanedUpListeners: number; /** Number of times max listeners warning was triggered */ maxListenersWarnings: number; } /** * A safe event emitter that prevents memory leaks and provides better error handling. * * @example Basic usage * ```typescript * const emitter = new EventEmitterSafe<{ * 'user:login': { userId: string } * 'user:logout': { userId: string } * }>() * * const unsubscribe = emitter.on('user:login', (event) => { * console.log(`User ${event.userId} logged in`) * }) * * emitter.emit('user:login', { userId: '123' }) * * // Clean up * unsubscribe() * ``` * * @example With max listeners protection * ```typescript * const emitter = new EventEmitterSafe({ * maxListeners: 10, * maxListenersBehavior: 'error', * }) * ``` * * @example With WeakRef for automatic cleanup * ```typescript * const emitter = new EventEmitterSafe({ useWeakRef: true }) * * // If the listener is garbage collected, it will be automatically removed * let handler: Listener | null = (event) => console.log(event) * emitter.on('event', handler) * * handler = null // Now eligible for garbage collection * ``` */ export declare class EventEmitterSafe = Record> { private config; private listeners; private cleanedUpListeners; private maxListenersWarnings; constructor(config?: EventEmitterSafeConfig); /** * Register a listener for an event. * Returns an unsubscribe function. * * @param event - The event name to listen for * @param listener - The listener function * @returns Unsubscribe function */ on(event: K, listener: Listener): () => void; /** * Register a one-time listener for an event. * The listener will be automatically removed after being called once. * * @param event - The event name to listen for * @param listener - The listener function * @returns Unsubscribe function */ once(event: K, listener: Listener): () => void; /** * Remove a specific listener from an event. * * @param event - The event name * @param listener - The listener function to remove * @returns true if the listener was found and removed */ off(event: K, listener: Listener): boolean; /** * Emit an event to all registered listeners. * * @param event - The event name * @param data - The event data * @returns Number of listeners that were called */ emit(event: K, data: Events[K]): number; /** * Remove all listeners for a specific event. * * @param event - The event name * @returns Number of listeners removed */ removeAllListeners(event: K): number; /** * Remove all listeners for all events. * * @returns Total number of listeners removed */ clear(): number; /** * Get the number of listeners for a specific event. * * @param event - The event name * @returns Number of listeners */ listenerCount(event: K): number; /** * Get all event names that have listeners. * * @returns Array of event names */ eventNames(): (keyof Events)[]; /** * Check if there are any listeners for an event. * * @param event - The event name * @returns true if there are listeners */ hasListeners(event: K): boolean; /** * Get statistics about the event emitter. * * @returns Statistics object */ getStats(): EventEmitterStats; /** * Get the current configuration. * * @returns Configuration object */ getConfig(): Readonly>; /** * Update configuration options. * Note: Changing useWeakRef only affects new listeners. * * @param config - Partial configuration to update */ setConfig(config: Partial): void; /** * Internal method to add a listener */ private addListener; /** * Resolve a listener from an entry, handling WeakRefs * Returns null if the WeakRef has been garbage collected */ private resolveListener; } /** * Create a new EventEmitterSafe instance. * * @param config - Optional configuration * @returns New EventEmitterSafe instance */ export declare function createEventEmitter = Record>(config?: EventEmitterSafeConfig): EventEmitterSafe; /** * Extract event data type from an EventEmitterSafe instance */ export type EventData>, K extends string> = E extends EventEmitterSafe ? K extends keyof Events ? Events[K] : never : never; /** * Extract all event names from an EventEmitterSafe instance */ export type EventNames>> = E extends EventEmitterSafe ? keyof Events : never; //# sourceMappingURL=event-emitter-safe.d.ts.map