UNPKG

cdek-ts-api

Version:

CDEK API client

206 lines (189 loc) 5.99 kB
// Copyright 2020-present the denosaurs team. All rights reserved. MIT license. type Entry<E, K extends keyof E> = { name: K; value: E[K]; }; const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined; export class EventEmitter<E extends Record<string, unknown[]>> { private listeners: { [K in keyof E]?: Array<{ once: boolean; cb: (...args: E[K]) => void; }>; } = {}; private globalWriters: WritableStreamDefaultWriter<Entry<E, keyof E>>[] = []; private onWriters: { [K in keyof E]?: WritableStreamDefaultWriter<E[K]>[]; } = {}; private limit: number; /** * @param maxListenersPerEvent - if set to 0, no limit is applied. defaults to 10 */ constructor(maxListenersPerEvent?: number) { this.limit = maxListenersPerEvent ?? 10; } /** * Appends the listener to the listeners array of the corresponding eventName. * No checks are made if the listener was already added, so adding multiple * listeners will result in the listener being called multiple times. * If no listener is passed, it returns an asyncIterator which will fire * every time eventName is emitted. */ on<K extends keyof E>(eventName: K, listener: (...args: E[K]) => void): this; on<K extends keyof E>(eventName: K): AsyncIterableIterator<E[K]>; on<K extends keyof E>( eventName: K, listener?: (...args: E[K]) => void, ): this | AsyncIterableIterator<E[K]> { if (listener) { if (!this.listeners[eventName]) { this.listeners[eventName] = []; } if ( this.limit !== 0 && this.listeners[eventName]!.length >= this.limit ) { throw new TypeError("Listeners limit reached: limit is " + this.limit); } this.listeners[eventName]!.push({ once: false, cb: listener, }); return this; } else { if (!this.onWriters[eventName]) { this.onWriters[eventName] = []; } if ( this.limit !== 0 && this.onWriters[eventName]!.length >= this.limit ) { throw new TypeError("Listeners limit reached: limit is " + this.limit); } const { readable, writable } = new TransformStream<E[K], E[K]>(); this.onWriters[eventName]!.push(writable.getWriter()); return readable[Symbol.asyncIterator](); } } /** * Adds a one-time listener function for the event named eventName. * The next time eventName is emitted, listener is called and then removed. * If no listener is passed, it returns a Promise that will resolve once * eventName is emitted. */ once<K extends keyof E>( eventName: K, listener: (...args: E[K]) => void, ): this; once<K extends keyof E>(eventName: K): Promise<E[K]>; once<K extends keyof E>( eventName: K, listener?: (...args: E[K]) => void, ): this | Promise<E[K]> { if (!this.listeners[eventName]) { this.listeners[eventName] = []; } if ( this.limit !== 0 && this.listeners[eventName]!.length >= this.limit ) { throw new TypeError("Listeners limit reached: limit is " + this.limit); } if (listener) { this.listeners[eventName]!.push({ once: true, cb: listener, }); return this; } else { return new Promise((res) => { this.listeners[eventName]!.push({ once: true, cb: (...args) => res(args), }); }); } } /** * Removes the listener from eventName. * If no listener is passed, all listeners will be removed from eventName, * this includes async listeners. * If no eventName is passed, all listeners will be removed from the EventEmitter, * including the async iterator for the class */ async off<K extends keyof E>( eventName?: K, listener?: (...args: E[K]) => void, ): Promise<this> { if (!isNullish(eventName)) { if (listener) { this.listeners[eventName] = this.listeners[eventName]?.filter( ({ cb }) => cb !== listener, ); } else { if (this.onWriters[eventName]) { for (const writer of this.onWriters[eventName]!) { await writer.close(); } delete this.onWriters[eventName]; } delete this.listeners[eventName]; } } else { for ( const writers of Object.values( this.onWriters, ) as WritableStreamDefaultWriter<E[K]>[][] ) { for (const writer of writers) { await writer.close(); } } this.onWriters = {}; for (const writer of this.globalWriters) { await writer.close(); } this.globalWriters = []; this.listeners = {}; } return this; } /** * Synchronously calls each of the listeners registered for the event named * eventName, in the order they were registered, passing the supplied * arguments to each. */ protected async emit<K extends keyof E>(eventName: K, ...args: E[K]): Promise<void> { const listeners = this.listeners[eventName]?.slice() ?? []; for (const { cb, once } of listeners) { cb(...args); if (once) { this.off(eventName, cb); } } if (this.onWriters[eventName]) { for (const writer of this.onWriters[eventName]!) { await writer.write(args); } } for (const writer of this.globalWriters) { await writer.write({ name: eventName, value: args, }); } } [Symbol.asyncIterator]<K extends keyof E>(): AsyncIterableIterator< { [V in K]: Entry<E, V> }[K] > { if (this.limit !== 0 && this.globalWriters.length >= this.limit) { throw new TypeError("Listeners limit reached: limit is " + this.limit); } const { readable, writable } = new TransformStream< Entry<E, K>, Entry<E, K> >(); this.globalWriters.push(writable.getWriter()); return readable[Symbol.asyncIterator](); } }