/* eslint-disable no-await-in-loop */ // eslint-disable-next-line @typescript-eslint/naming-convention const _hooks: Record any>> = {}; function isBailed(value: any) { return value !== null && value !== false && value !== undefined; } export type Disposable = () => void; export type VoidReturn = Promise | any; export interface EventMap extends Record { 'scratchpadEditorCreate': (editor, monaco) => any; } function getHooks(name: K) { const hooks = _hooks[name] || (_hooks[name] = []); if (hooks.length >= 2048) { console.warn( 'max listener count (2048) for event "%s" exceeded, which may be caused by a memory leak', name, ); } return hooks; } export function removeListener(name: K, listener: EventMap[K]) { const index = (_hooks[name] || []).findIndex((callback) => callback === listener); if (index >= 0) { _hooks[name].splice(index, 1); return true; } return false; } export function addListener(name: K, listener: EventMap[K]) { getHooks(name).push(listener); return () => removeListener(name, listener); } export function prependListener(name: K, listener: EventMap[K]) { getHooks(name).unshift(listener); return () => removeListener(name, listener); } export function once(name: K, listener: EventMap[K]) { let dispose; function l(...args: any[]) { dispose(); return listener.apply(this, args); } l.toString = () => `// Once \n${listener.toString()}`; dispose = addListener(name, l); return dispose; } export function on(name: K, listener: EventMap[K]) { return addListener(name, listener); } export function off(name: K, listener: EventMap[K]) { return removeListener(name, listener); } export async function parallel(name: K, ...args: Parameters): Promise { const tasks: Promise[] = []; for (const callback of _hooks[name] || []) { tasks.push(callback.apply(this, args)); } await Promise.all(tasks); } export function emit(name: K, ...args: Parameters) { return parallel(name, ...args); } export async function serial(name: K, ...args: Parameters): Promise { const hooks = Array.from(_hooks[name] || []); for (const callback of hooks) { await callback.apply(this, args); } } export async function bail(name: K, ...args: Parameters): Promise> { const hooks = Array.from(_hooks[name] || []); for (const callback of hooks) { let result = callback.apply(this, args); if (result instanceof Promise) result = await result; if (isBailed(result)) return result; } return null; }