import { assertFunction, assertNumber, assertString } from '../utils'; import type { ReadonlyAsyncEmitter } from './AsyncEmitter'; import type { ReadonlyEmitter } from './Emitter'; import { SerialAsyncEmitter } from './SerialAsyncEmitter'; import { SerialSyncEmitter } from './SerialSyncEmitter'; export type ReadonlySemiAsyncEmitter = ReadonlyAsyncEmitter & ReadonlyEmitter; export class SemiAsyncEmitter implements ReadonlyAsyncEmitter, ReadonlyEmitter { readonly #asyncEmitter: SerialAsyncEmitter; readonly #syncEmitter: SerialSyncEmitter; readonly #syncEvents: Set; constructor(name: string, syncEvents: Iterable) { this.#asyncEmitter = new SerialAsyncEmitter(name); this.#syncEmitter = new SerialSyncEmitter(name); this.#syncEvents = new Set(syncEvents); } on( type: K | '*', listener: (event: SyncMap[K]) => unknown, order?: number, ): this; on( type: K | '*', listener: (event: AsyncMap[K]) => unknown, order?: number, ): this; on( type: K | '*', listener: (event: any) => unknown, order?: number, ): this { assertString(type, 'type'); assertFunction(listener, 'listener'); order !== undefined && assertNumber(order, 'order'); return this.#invoke('on', type, listener, order); } once( type: K | '*', listener: (event: SyncMap[K]) => unknown, order?: number, ): this; once( type: K | '*', listener: (event: AsyncMap[K]) => unknown, order?: number, ): this; once( type: K | '*', listener: (event: any) => unknown, order?: number, ): this { assertString(type, 'type'); assertFunction(listener, 'listener'); order !== undefined && assertNumber(order, 'order'); return this.#invoke('once', type, listener, order); } off(type: K | '*', listener: (event: SyncMap[K]) => unknown): this; off(type: K | '*', listener: (event: AsyncMap[K]) => unknown): this; off( type: K | '*', listener: (event: any) => unknown, ): this { assertString(type, 'type'); assertFunction(listener, 'listener'); return this.#invoke('off', type, listener); } emit(type: K, event: SyncMap[K]): void; emit(type: K, event: AsyncMap[K]): Promise; emit(type: K, event: any): void | Promise { assertString(type, 'type'); return this.#syncEvents.has(type as keyof SyncMap) ? this.#syncEmitter.emit(type as keyof SyncMap, event) : this.#asyncEmitter.emit(type as keyof AsyncMap, event); } #invoke( methodName: 'on' | 'once' | 'off', type: K | '*', listener: (event: any) => unknown, order?: number, ): this { const isSync = this.#syncEvents.has(type as keyof SyncMap); if (type === '*' || isSync) { this.#syncEmitter[methodName](type as keyof SyncMap, listener, order); } if (type === '*' || !isSync) { this.#asyncEmitter[methodName]( type as keyof AsyncMap, listener as (event: Event) => Promise, order, ); } return this; } }