import { compose } from '@cabloy/compose'; import type { IEventRecord, NextEventStrict, NextEventSyncStrict, TypeEventHandler, TypeEventHandlers, TypeEventHandlersMap, TypeEventOff, } from '../../types/interface/event.js'; import { BeanSimple } from '../../bean/beanSimple.ts'; import { cast } from '../../types/utils/cast.ts'; const __adapter = (_context, chain) => { const eventHandlerWrapper = chain; if (!eventHandlerWrapper.fn) return; return { receiver: undefined, fn: eventHandlerWrapper.fn, }; }; export class AppEvent extends BeanSimple { private eventHandlersMap = {} as TypeEventHandlersMap; /** @internal */ public async initialize() {} /** @internal */ public dispose() { this.eventHandlersMap = {} as any; } public getEventHandlers(eventName: K): TypeEventHandlers { let eventHandlers = this.eventHandlersMap[eventName]; if (!eventHandlers) { eventHandlers = this.eventHandlersMap[eventName] = [] as any; } return eventHandlers; } async emit( eventName: K, data?: IEventRecord[K]['data'], nextOrDefault?: NextEventStrict | IEventRecord[K]['result'], ): Promise { const eventHandlers = this.getEventHandlers(eventName); const next = typeof nextOrDefault === 'function' ? cast>(nextOrDefault) : async (): Promise => { return nextOrDefault! as IEventRecord[K]['result']; }; return await compose(eventHandlers.concat(), __adapter)(data, next); } emitSync( eventName: K, data?: IEventRecord[K]['data'], nextOrDefault?: NextEventSyncStrict | IEventRecord[K]['result'], ): IEventRecord[K]['result'] { const eventHandlers = this.getEventHandlers(eventName); const next = typeof nextOrDefault === 'function' ? cast>(nextOrDefault) : (): IEventRecord[K]['result'] => { return nextOrDefault!; }; return compose(eventHandlers.concat(), __adapter)(data, next); } on(eventName: K, fn: TypeEventHandler): TypeEventOff { const eventHandlers = this.getEventHandlers(eventName); eventHandlers.push({ fn }); return () => { const index = eventHandlers.findIndex(item => item.fn === fn); if (index > -1) { eventHandlers[index].fn = undefined; eventHandlers.splice(index, 1); } }; } once(eventName: K, fn: TypeEventHandler): TypeEventOff { const off = this.on(eventName, async (data, next) => { const res = await fn(data, next); off(); return res; }); return off; } }