export interface HandlerContext { data: T emit: (eventName: string, ...params: any[]) => void } export interface Events { enqueue: Array<(payload: T, queueLength: number) => void> dequeue: Array<(payload: T, queueLength: number) => void> process: Array<(payload: T, handler: (param: HandlerContext) => Promise) => void> error: Array<(payload: T, error: unknown, handler: (param: HandlerContext) => Promise) => void> result: Array<(payload: T, result: R, handler: (param: HandlerContext) => Promise) => void> drain: Array<() => void> } export function createQueue(options: { handlers: Array<(ctx: HandlerContext) => Promise> }) { const queue: T[] = [] let drainTask: Promise | undefined const internalEventListeners: Events = { enqueue: [], dequeue: [], process: [], error: [], result: [], drain: [], } const internalHandlerEventListeners: Record void>> = {} function on>(eventName: E, listener: Events[E][number]) { internalEventListeners[eventName].push(listener as any) } function emit>(eventName: E, ...params: Parameters[E][number]>) { const listeners = internalEventListeners[eventName] as Events[E] listeners.forEach(listener => (listener as any)(...params)) } function onHandlerEvent(eventName: string, listener: (...params: any[]) => void) { internalHandlerEventListeners[eventName] = internalHandlerEventListeners[eventName] || [] internalHandlerEventListeners[eventName].push(listener) } function emitHandlerEvent(eventName: string, ...params: any[]) { const listeners = internalHandlerEventListeners[eventName] || [] listeners.forEach(listener => listener(...params)) } function enqueue(payload: T) { queue.push(payload) emit('enqueue', payload, queue.length) if (!drainTask) { drainTask = drain() } } function clear() { queue.length = 0 } async function drain() { while (queue.length > 0) { const payload = queue.shift() as T emit('dequeue', payload, queue.length) for (const handler of options.handlers) { emit('process', payload, handler) try { const result = await handler({ data: payload, emit: emitHandlerEvent }) emit('result', payload, result, handler) } catch (err) { emit('error', payload, err, handler) continue } } } emit('drain') drainTask = undefined } function length() { return queue.length } return { enqueue, clear, length, on, onHandlerEvent, } } export type UseQueueReturn = ReturnType>