UNPKG

@ably/chat

Version:

Ably Chat is a set of purpose-built APIs for a host of chat features enabling you to create 1:1, 1:Many, Many:1 and Many:Many chat rooms for any scale. It is designed to meet a wide range of chat use cases, such as livestreams, in-game communication, cust

112 lines (100 loc) 4.08 kB
import * as Ably from 'ably'; /** * This type represents a callback that can be registered with an EventEmitter. * * The EventsMap is an interface of event names to the types of the payloads of * those events. For example: * * interface MyEvents { * reaction: {emoji: string} * } * * There is no need to use this type directly, it is used for defining the * InterfaceEventEmitter. */ type Callback<EventsMap> = (arg: EventsMap[keyof EventsMap]) => void; type CallbackSingle<K> = (arg: K) => void; /** * This interface extends the Ably.EventEmitter interface to add a type-safe * emit method as well as convert an EventsMap into the type parameters used by * Ably.EventEmitter. */ interface InterfaceEventEmitter<EventsMap> extends Ably.EventEmitter<Callback<EventsMap>, void, keyof EventsMap> { emit<K extends keyof EventsMap>(event: K, arg: EventsMap[K]): void; on<K extends keyof EventsMap>(event: K, callback: CallbackSingle<EventsMap[K]>): void; on<K1 extends keyof EventsMap, K2 extends keyof EventsMap>( events: [K1, K2], callback: CallbackSingle<EventsMap[K1] | EventsMap[K2]>, ): void; on<K1 extends keyof EventsMap, K2 extends keyof EventsMap, K3 extends keyof EventsMap>( events: [K1, K2, K3], callback: CallbackSingle<EventsMap[K1] | EventsMap[K2] | EventsMap[K3]>, ): void; on(events: (keyof EventsMap)[], callback: Callback<EventsMap>): void; on(callback: Callback<EventsMap>): void; off<K extends keyof EventsMap>(event: K, listener: CallbackSingle<EventsMap[K]>): void; off(listener?: Callback<EventsMap>): void; off<K extends EventsMap[keyof EventsMap]>(listener: CallbackSingle<K>): void; } /** * This is a workaround for the fact that the EventEmitter constructor is only * exported from the ably-js package for internal use by other Ably SDKs (like * this one). * * It is a correctly-typed constructor for the ably-js EventEmitter. * * We do not export this directly because we prefer to export a class, which is * what we normally expect EventEmitter to be. */ const InternalEventEmitter: new <EventsMap>() => InterfaceEventEmitter<EventsMap> = ( Ably.Realtime as unknown as { EventEmitter: new <EventsMap>() => InterfaceEventEmitter<EventsMap> } ).EventEmitter; /** * EventEmitter class based on the internal ably-js EventEmitter. It is * different from the ably-js EventEmitter because it takes an EventsMap type * parameter as opposed to the three type parameters required by * {@link Ably.EventEmitter}. * * We find the EventsMap type parameter to be more convenient to use in this * Chat SDK. * * The EventsMap is an interface of event names to the types of the payloads of * those events. For example: * * interface MyEvents { * reaction: {emoji: string} * } * * There is no need to use this type directly, it is used for defining the * InterfaceEventEmitter. */ class EventEmitter<EventsMap> extends InternalEventEmitter<EventsMap> {} export default EventEmitter; /** * Creates a wrapper function that forwards all arguments to the provided function. * @param fn The function to wrap * @returns A new function with the same signature as the input function */ export const wrap = <Args extends unknown[], Return>(fn: (...args: Args) => Return): ((...args: Args) => Return) => (...args: Args) => fn(...args); /** * Checks if an EventEmitter has any listeners registered. * @param emitter The EventEmitter instance to check * @returns true if the emitter has listeners, false otherwise */ export const emitterHasListeners = <EventsMap>(emitter: EventEmitter<EventsMap>): boolean => { const destructured = emitter as unknown as { events: Record<string, unknown[]>; any: unknown[]; eventsOnce: Record<string, unknown[]>; anyOnce: unknown[]; }; const numListeners = Object.values(destructured.events).flat().length + destructured.any.length + Object.values(destructured.eventsOnce).flat().length + destructured.anyOnce.length; return numListeners ? numListeners > 0 : false; };