UNPKG

@segment/analytics-generic-utils

Version:

This monorepo's version of "lodash". This package contains shared generic utilities that can be used within the ecosystem. This package should not have dependencies, and should not contain any references to the Analytics domain.

99 lines (90 loc) 2.55 kB
type EventName = string type EventFnArgs = any[] type EmitterContract = Record<EventName, EventFnArgs> export interface EmitterOptions { /** How many event listeners for a particular event before emitting a warning (0 = disabled) * @default 10 **/ maxListeners?: number } /** * Event Emitter that takes the expected contract as a generic * @example * ```ts * type Contract = { * delivery_success: [DeliverySuccessResponse, Metrics], * delivery_failure: [DeliveryError] * } * new Emitter<Contract>() * .on('delivery_success', (res, metrics) => ...) * .on('delivery_failure', (err) => ...) * ``` */ export class Emitter<Contract extends EmitterContract = EmitterContract> { maxListeners: number constructor(options?: EmitterOptions) { this.maxListeners = options?.maxListeners ?? 10 } private callbacks: Partial<Contract> = {} private warned = false private warnIfPossibleMemoryLeak<EventName extends keyof Contract>( event: EventName ) { if (this.warned) { return } if ( this.maxListeners && this.callbacks[event]!.length > this.maxListeners ) { console.warn( `Event Emitter: Possible memory leak detected; ${String( event )} has exceeded ${this.maxListeners} listeners.` ) this.warned = true } } on<EventName extends keyof Contract>( event: EventName, callback: (...args: Contract[EventName]) => void ): this { if (!this.callbacks[event]) { this.callbacks[event] = [callback] as Contract[EventName] } else { this.callbacks[event]!.push(callback) this.warnIfPossibleMemoryLeak(event) } return this } once<EventName extends keyof Contract>( event: EventName, callback: (...args: Contract[EventName]) => void ): this { const on = (...args: Contract[EventName]): void => { this.off(event, on) callback.apply(this, args) } this.on(event, on) return this } off<EventName extends keyof Contract>( event: EventName, callback: (...args: Contract[EventName]) => void ): this { const fns = this.callbacks[event] ?? [] const without = fns.filter((fn) => fn !== callback) as Contract[EventName] this.callbacks[event] = without return this } emit<EventName extends keyof Contract>( event: EventName, ...args: Contract[EventName] ): this { const callbacks = this.callbacks[event] ?? [] callbacks.forEach((callback) => { callback.apply(this, args) }) return this } }