@akala/core
Version:
322 lines (287 loc) • 13.1 kB
text/typescript
import { type AsyncSubscription, AsyncTeardownManager, type Subscription } from "../teardown-manager.js";
import { Event, type EventArgs, type EventKeys, type EventListener, type EventOptions, type EventReturnType, type IEvent } from "./shared.js";
/**
* Represents special events with a dispose symbol.
* @typedef {Object} SpecialEvents
* @property {Event<[]>} [Symbol.dispose] - The dispose event.
*/
export type SpecialEvents = { [Symbol.dispose]: IEvent<[], void> }
export type EventMap<T extends object> = { [key in EventKeys<T>]?: T[key] }
/**
* Represents all event keys, including special events.
* @template T
* @typedef {EventKeys<T> | keyof SpecialEvents} AllEventKeys
*/
export type AllEventKeys<T extends object> = EventKeys<T> | keyof SpecialEvents;
/**
* EventBus interface to manage events and listeners.
* @template T
* @implements {Disposable}
*/
export interface EventBus<T extends EventMap<T> = Record<PropertyKey, IEvent<unknown[], unknown>>> extends AsyncTeardownManager
{
/**
* Checks if there are listeners for a given event.
* @template TKey
* @param {TKey} name - The name of the event.
* @returns {boolean} - True if there are listeners, false otherwise.
*/
hasListener<const TKey extends EventKeys<T>>(name: TKey): boolean;
/**
* Gets the defined events.
* @returns {string[]} - An array of defined event names.
*/
get definedEvents(): (EventKeys<T>)[];
/**
* Emits an event.
* @template TEvent
* @param {TEvent} event - The event to emit.
* @param {...EventArgs<T[TEvent]>} args - The arguments to pass to the event listeners.
* @returns {false | EventReturnType<T[TEvent]>} - The return value of the event listeners or false if the event does not exist.
*/
emit<const TEvent extends EventKeys<T>>(event: TEvent, ...args: EventArgs<T[TEvent]>): false | EventReturnType<T[TEvent]>;
/**
* Adds a listener for an event.
* @template TEvent
* @param {TEvent} event - The event to listen to.
* @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler.
* @param {EventOptions<AllEvents<T>[TEvent]>} [options] - The event options.
* @returns {Subscription} - The subscription.
*/
on<const TEvent extends EventKeys<T>>(event: TEvent, handler: EventListener<T[TEvent]>, options?: EventOptions<T[TEvent]>): Subscription;
/**
* Adds a one-time listener for an event.
* @template TEvent
* @param {TEvent} event - The event to listen to.
* @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler.
* @param {Omit<EventOptions<AllEvents<T>[TEvent]>, 'once'>} [options] - The event options.
* @returns {Subscription} - The subscription.
*/
once<const TEvent extends EventKeys<T>>(event: TEvent, handler: EventListener<T[TEvent]>, options?: Omit<EventOptions<T[TEvent]>, 'once'>): Subscription;
/**
* Removes a listener for an event.
* @template TEvent
* @param {TEvent} event - The event to remove the listener from.
* @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler.
* @returns {boolean} - True if the listener was removed, false otherwise.
*/
off<const TEvent extends EventKeys<T>>(event: TEvent, handler?: EventListener<T[TEvent]>): boolean;
}
/**
* AsyncEventBus interface to manage events and listeners.
* @template T
* @implements {Disposable}
*/
export interface AsyncEventBus<T extends { [key in keyof T]: IEvent<unknown[], unknown> } = Record<PropertyKey, IEvent<unknown[], unknown>>> extends AsyncTeardownManager
{
/**
* Checks if there are listeners for a given event.
* @template TKey
* @param {TKey} name - The name of the event.
* @returns {Promise<boolean>} - True if there are listeners, false otherwise.
*/
hasListener<const TKey extends EventKeys<T>>(name: TKey): Promise<boolean>;
/**
* Gets the defined events.
* @returns {Promise<string[]>} - An array of defined event names.
*/
get definedEvents(): Promise<(EventKeys<T>)[]>;
/**
* Emits an event.
* @template TEvent
* @param {TEvent} event - The event to emit.
* @param {...EventArgs<T[TEvent]>} args - The arguments to pass to the event listeners.
* @returns {Promise<false | EventReturnType<T[TEvent]>>} - The return value of the event listeners or false if the event does not exist.
*/
emit<const TEvent extends EventKeys<T>>(event: TEvent, ...args: EventArgs<T[TEvent]>): Promise<false | EventReturnType<T[TEvent]>>;
/**
* Adds a listener for an event.
* @template TEvent
* @param {TEvent} event - The event to listen to.
* @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler.
* @param {EventOptions<AllEvents<T>[TEvent]>} [options] - The event options.
* @returns {Promise<Subscription>} - The subscription.
*/
on<const TEvent extends EventKeys<T>>(event: TEvent, handler: EventListener<T[TEvent]>, options?: EventOptions<T[TEvent]>): Promise<AsyncSubscription>;
/**
* Adds a one-time listener for an event.
* @template TEvent
* @param {TEvent} event - The event to listen to.
* @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler.
* @param {Omit<EventOptions<AllEvents<T>[TEvent]>, 'once'>} [options] - The event options.
* @returns {Promise<Subscription>} - The subscription.
*/
once<const TEvent extends EventKeys<T>>(event: TEvent, handler: EventListener<T[TEvent]>, options?: Omit<EventOptions<T[TEvent]>, 'once'>): Promise<AsyncSubscription>;
/**
* Removes a listener for an event.
* @template TEvent
* @param {TEvent} event - The event to remove the listener from.
* @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler.
* @returns {Promise<boolean>} - True if the listener was removed, false otherwise.
*/
off<const TEvent extends EventKeys<T>>(event: TEvent, handler?: EventListener<T[TEvent]>): Promise<boolean>;
}
/**
* Wrapper class for EventEmitter to manage subscriptions.
* @template T
* @implements {Disposable}
*/
export class EventBusWrapper<T extends Record<string, IEvent<unknown[], unknown>> = Record<string, Event<unknown[]>>> extends AsyncTeardownManager implements EventBus<T>
{
constructor(private readonly emitter: EventBus<T>)
{
super();
emitter.once(Symbol.dispose as EventKeys<T>, (() => this[Symbol.dispose]()) as EventListener<T[EventKeys<T>]>);
}
public readonly subscriptions: Subscription[] = []
/**
* Checks if there are listeners for a given event.
* @template TKey
* @param {TKey} name - The name of the event.
* @returns {boolean} - True if there are listeners, false otherwise.
*/
hasListener<const TKey extends EventKeys<T>>(name: TKey): boolean
{
return this.emitter.hasListener(name);
}
/**
* Gets the defined events.
* @returns {string[]} - An array of defined event names.
*/
public get definedEvents(): EventKeys<T>[]
{
return this.emitter.definedEvents;
}
/**
* Emits an event.
* @template TEvent
* @param {TEvent} event - The event to emit.
* @param {...EventArgs<T[TEvent]>} args - The arguments to pass to the event listeners.
* @returns {false | EventReturnType<T[TEvent]>} - The return value of the event listeners or false if the event does not exist.
*/
emit<const TEvent extends EventKeys<T>>(event: TEvent, ...args: EventArgs<T[TEvent]>): false | EventReturnType<T[TEvent]>
{
return this.emitter.emit(event, ...args);
}
/**
* Adds a listener for an event.
* @template TEvent
* @param {TEvent} event - The event to listen to.
* @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler.
* @param {EventOptions<AllEvents<T>[TEvent]>} [options] - The event options.
* @returns {Subscription} - The subscription.
*/
on<const TEvent extends EventKeys<T>>(event: TEvent, handler: EventListener<T[TEvent]>, options?: EventOptions<T[TEvent]>): Subscription
{
const sub = this.emitter.on(event, handler, options);
this.subscriptions.push(sub);
return sub;
}
/**
* Adds a one-time listener for an event.
* @template TEvent
* @param {TEvent} event - The event to listen to.
* @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler.
* @param {Omit<EventOptions<AllEvents<T>[TEvent]>, "once">} [options] - The event options.
* @returns {Subscription} - The subscription.
*/
once<const TEvent extends EventKeys<T>>(event: TEvent, handler: EventListener<T[TEvent]>, options?: Omit<EventOptions<T[TEvent]>, "once">): Subscription
{
const sub = this.emitter.once(event, handler, options);
this.subscriptions.push(sub);
return sub;
}
/**
* Removes a listener for an event.
* @template TEvent
* @param {TEvent} event - The event to remove the listener from.
* @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler.
* @returns {boolean} - True if the listener was removed, false otherwise.
*/
off<const TEvent extends EventKeys<T>>(event: TEvent, handler: EventListener<T[TEvent]>): boolean
{
return this.emitter.off(event, handler);
}
}
/**
* Wrapper class to make a sync event bus async.
* @template T
* @implements {Disposable}
*/
export class EventBus2AsyncEventBus<T extends Record<string, IEvent<unknown[], unknown>> = Record<string, IEvent<unknown[], unknown>>> extends AsyncTeardownManager implements AsyncEventBus<T>
{
constructor(private readonly emitter: EventBus<T>)
{
super();
emitter.once(Symbol.dispose as EventKeys<T>, (() => this[Symbol.asyncDispose]()) as EventListener<T[EventKeys<T>]>);
}
public readonly subscriptions: Subscription[] = []
/**
* Checks if there are listeners for a given event.
* @template TKey
* @param {TKey} name - The name of the event.
* @returns {boolean} - True if there are listeners, false otherwise.
*/
hasListener<const TKey extends EventKeys<T>>(name: TKey): Promise<boolean>
{
return Promise.resolve(this.emitter.hasListener(name));
}
/**
* Gets the defined events.
* @returns {string[]} - An array of defined event names.
*/
public get definedEvents(): Promise<EventKeys<T>[]>
{
return Promise.resolve(this.emitter.definedEvents);
}
/**
* Emits an event.
* @template TEvent
* @param {TEvent} event - The event to emit.
* @param {...EventArgs<T[TEvent]>} args - The arguments to pass to the event listeners.
* @returns {false | EventReturnType<T[TEvent]>} - The return value of the event listeners or false if the event does not exist.
*/
emit<const TEvent extends EventKeys<T>>(event: TEvent, ...args: EventArgs<T[TEvent]>): Promise<false | EventReturnType<T[TEvent]>>
{
return Promise.resolve(this.emitter.emit(event, ...args));
}
/**
* Adds a listener for an event.
* @template TEvent
* @param {TEvent} event - The event to listen to.
* @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler.
* @param {EventOptions<AllEvents<T>[TEvent]>} [options] - The event options.
* @returns {Subscription} - The subscription.
*/
on<const TEvent extends EventKeys<T>>(event: TEvent, handler: EventListener<T[TEvent]>, options?: EventOptions<T[TEvent]>): Promise<AsyncSubscription>
{
const sub = this.emitter.on(event, handler, options);
this.subscriptions.push(sub);
return Promise.resolve(() => Promise.resolve(sub()));
}
/**
* Adds a one-time listener for an event.
* @template TEvent
* @param {TEvent} event - The event to listen to.
* @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler.
* @param {Omit<EventOptions<AllEvents<T>[TEvent]>, "once">} [options] - The event options.
* @returns {Subscription} - The subscription.
*/
once<const TEvent extends EventKeys<T>>(event: TEvent, handler: EventListener<T[TEvent]>, options?: Omit<EventOptions<T[TEvent]>, "once">): Promise<AsyncSubscription>
{
const sub = this.emitter.once(event, handler, options);
this.subscriptions.push(sub);
return Promise.resolve(() => Promise.resolve(sub()));
}
/**
* Removes a listener for an event.
* @template TEvent
* @param {TEvent} event - The event to remove the listener from.
* @param {EventListener<AllEvents<T>[TEvent]>} handler - The event handler.
* @returns {boolean} - True if the listener was removed, false otherwise.
*/
off<const TEvent extends EventKeys<T>>(event: TEvent, handler: EventListener<T[TEvent]>): Promise<boolean>
{
return Promise.resolve(this.emitter.off(event, handler));
}
}