From 438e36f185ff0199d8a030600527b38649478aa2 Mon Sep 17 00:00:00 2001 From: dswbx Date: Wed, 15 Jan 2025 17:46:41 +0100 Subject: [PATCH] refactor event listener registration unified listener creation logic under `createEventListener`, adding support for a flexible `RegisterListenerConfig`. Updated associated tests and improved error handling for unregistered events. --- app/__test__/core/EventManager.spec.ts | 7 +++- app/src/core/events/EventManager.ts | 56 ++++++++++++++------------ 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/app/__test__/core/EventManager.spec.ts b/app/__test__/core/EventManager.spec.ts index 4f11d19..ba5db93 100644 --- a/app/__test__/core/EventManager.spec.ts +++ b/app/__test__/core/EventManager.spec.ts @@ -61,6 +61,9 @@ describe("EventManager", async () => { "sync" ); + // don't allow unknown + expect(() => emgr.on("unknown", () => void 0)).toThrow(); + emgr.onEvent(InformationalEvent, async (event, name) => { call(); expect(name).toBe("informational-event"); @@ -135,14 +138,14 @@ describe("EventManager", async () => { const call = mock(() => null); const emgr = new EventManager({ InformationalEvent }); - emgr.onEventOnce( + emgr.onEvent( InformationalEvent, async (event, slug) => { expect(event).toBeInstanceOf(InformationalEvent); expect(slug).toBe("informational-event"); call(); }, - "sync" + { mode: "sync", once: true } ); expect(emgr.getListeners().length).toBe(1); diff --git a/app/src/core/events/EventManager.ts b/app/src/core/events/EventManager.ts index 6e48224..ec271a4 100644 --- a/app/src/core/events/EventManager.ts +++ b/app/src/core/events/EventManager.ts @@ -1,6 +1,13 @@ import { type Event, InvalidEventReturn } from "./Event"; import { EventListener, type ListenerHandler, type ListenerMode } from "./EventListener"; +export type RegisterListenerConfig = + | ListenerMode + | { + mode?: ListenerMode; + once?: boolean; + }; + export interface EmitsEvents { emgr: EventManager; } @@ -86,9 +93,11 @@ export class EventManager< return !!this.events.find((e) => slug === e.slug); } - protected throwIfEventNotRegistered(event: EventClass) { - if (!this.eventExists(event)) { - throw new Error(`Event "${event.slug}" not registered`); + protected throwIfEventNotRegistered(event: EventClass | Event | string) { + if (!this.eventExists(event as any)) { + // @ts-expect-error + const name = event.constructor?.slug ?? event.slug ?? event; + throw new Error(`Event "${name}" not registered`); } } @@ -121,44 +130,39 @@ export class EventManager< return this; } - onEvent>( - event: ActualEvent, - handler: ListenerHandler, - mode: ListenerMode = "async" + protected createEventListener( + _event: EventClass | string, + handler: ListenerHandler, + _config: RegisterListenerConfig = "async" ) { - this.throwIfEventNotRegistered(event); - - const listener = new EventListener(event, handler, mode); + const event = + typeof _event === "string" ? this.events.find((e) => e.slug === _event)! : _event; + const config = typeof _config === "string" ? { mode: _config } : _config; + const listener = new EventListener(event, handler, config.mode); + if (config.once) { + listener.once = true; + } this.addListener(listener as any); } - onEventOnce>( + onEvent>( event: ActualEvent, handler: ListenerHandler, - mode: ListenerMode = "async" + config?: RegisterListenerConfig ) { - this.throwIfEventNotRegistered(event); - - const listener = new EventListener(event, handler, mode); - listener.once = true; - this.addListener(listener as any); + this.createEventListener(event, handler, config); } on( slug: string, handler: ListenerHandler>, - mode: ListenerMode = "async" + config?: RegisterListenerConfig ) { - const event = this.events.find((e) => e.slug === slug); - if (!event) { - throw new Error(`Event "${slug}" not registered`); - } - - this.onEvent(event, handler, mode); + this.createEventListener(slug, handler, config); } - onAny(handler: ListenerHandler>, mode: ListenerMode = "async") { - this.events.forEach((event) => this.onEvent(event, handler, mode)); + onAny(handler: ListenerHandler>, config?: RegisterListenerConfig) { + this.events.forEach((event) => this.onEvent(event, handler, config)); } protected executeAsyncs(promises: (() => Promise)[]) {