{"version":3,"file":"index.mjs","names":["#listeners","#addListener","#proxyEvent","#matchListeners","#callListener","#isTypelessListener"],"sources":["../src/index.ts"],"sourcesContent":["import { LensList } from './lens-list'\n\nexport type DefaultEventMap = {\n  [eventType: string]: TypedEvent<any, any>\n}\n\n/**\n * Reserved event map containing special event types like '*' for catch-all listeners.\n */\nexport type ReservedEventMap = {\n  '*': TypedEvent<any, any, '*'>\n}\n\ntype IsReservedEvent<Type extends string> = Type extends keyof ReservedEventMap\n  ? true\n  : false\n\nexport interface TypedEvent<\n  DataType = void,\n  ReturnType = void,\n  EventType extends string = string,\n> extends Omit<MessageEvent<DataType>, 'type'> {\n  type: EventType\n}\n\nconst kDefaultPrevented = Symbol('kDefaultPrevented')\nconst kPropagationStopped = Symbol('kPropagationStopped')\nconst kImmediatePropagationStopped = Symbol('kImmediatePropagationStopped')\n\nexport class TypedEvent<\n  DataType = void,\n  ReturnType = void,\n  EventType extends string = string,\n>\n  extends MessageEvent<DataType>\n  implements TypedEvent<DataType, ReturnType, EventType>\n{\n  /**\n   * @note Keep a placeholder property with the return type\n   * because the type must be set somewhere in order to be\n   * correctly associated and inferred from the event.\n   */\n  #returnType: ReturnType;\n\n  [kDefaultPrevented]: boolean;\n  [kPropagationStopped]?: Emitter<any>;\n  [kImmediatePropagationStopped]?: boolean\n\n  constructor(\n    ...args: [DataType] extends [void]\n      ? [type: EventType]\n      : [type: EventType, init: { data: DataType }]\n  ) {\n    super(args[0], args[1])\n    this[kDefaultPrevented] = false\n  }\n\n  get defaultPrevented(): boolean {\n    return this[kDefaultPrevented]\n  }\n\n  public preventDefault(): void {\n    super.preventDefault()\n    this[kDefaultPrevented] = true\n  }\n\n  public stopImmediatePropagation(): void {\n    /**\n     * @note Despite `.stopPropagation()` and `.stopImmediatePropagation()` being defined\n     * in Node.js, they do nothing. It is safe to re-define them.\n     */\n    super.stopImmediatePropagation()\n    this[kImmediatePropagationStopped] = true\n  }\n}\n\n/**\n * Brands a TypedEvent or its subclass while preserving its (narrower) type.\n */\ntype Brand<\n  Event extends TypedEvent,\n  EventType extends string,\n  Loose extends boolean = false,\n> = Loose extends true\n  ? Event extends TypedEvent<infer Data, any, any>\n    ? /**\n       * @note Omit the `ReturnType` so emit methods can accept type events\n       * where infering the return type is impossible.\n       */\n      TypedEvent<Data, any, EventType> & {\n        type: EventType\n      }\n    : never\n  : Event & { type: EventType }\n\ntype InferEventMap<Target extends Emitter<any>> =\n  Target extends Emitter<infer EventMap> ? MergedEventMap<EventMap> : never\n\n/**\n * Extracts only user-defined events, excluding reserved event types.\n */\ntype UserEventMap<EventMap extends DefaultEventMap> = Omit<\n  EventMap,\n  keyof ReservedEventMap\n>\n\n/**\n * Merges the user EventMap with the ReservedEventMap.\n * The '*' event type accepts a union of all user-defined events.\n */\ntype MergedEventMap<EventMap extends DefaultEventMap> = EventMap &\n  ReservedEventMap\n\n/**\n * Creates a union of all events in the EventMap with their literal type strings.\n */\ntype AllEvents<EventMap extends DefaultEventMap> = {\n  [K in keyof EventMap & string]: Brand<EventMap[K], K>\n}[keyof EventMap & string]\n\nexport type TypedListenerOptions = {\n  once?: boolean\n  signal?: AbortSignal\n}\n\nconst kListenerOptions = Symbol('kListenerOptions')\n\nexport namespace Emitter {\n  /**\n   * Returns an appropriate `Event` type for the given event type.\n   *\n   * @example\n   * const emitter = new Emitter<{ greeting: TypedEvent<string> }>()\n   * type GreetingEvent = Emitter.InferEventType<typeof emitter, 'greeting'>\n   * // TypedEvent<string>\n   */\n  export type EventType<\n    Target extends Emitter<any>,\n    EventType extends keyof EventMap & string,\n    EventMap extends DefaultEventMap = InferEventMap<Target>,\n  > =\n    IsReservedEvent<EventType> extends true\n      ? AllEvents<UserEventMap<EventMap>>\n      : Brand<EventMap[EventType], EventType>\n\n  export type EventDataType<\n    Target extends Emitter<any>,\n    EventType extends keyof EventMap & string,\n    EventMap extends DefaultEventMap = InferEventMap<Target>,\n  > = EventMap[EventType] extends TypedEvent<infer DataType> ? DataType : never\n\n  /**\n   * Returns the listener type for the given event type.\n   *\n   * @example\n   * const emitter = new Emitter<{ getTotalPrice: TypedEvent<Cart, number> }>()\n   * type Listener = Emitter.ListenerType<typeof emitter, 'getTotalPrice'>\n   * // (event: TypedEvent<Cart>) => number\n   */\n  export type ListenerType<\n    Target extends Emitter<any>,\n    EventType extends keyof EventMap & string,\n    EventMap extends DefaultEventMap = InferEventMap<Target>,\n  > =\n    IsReservedEvent<EventType> extends true\n      ? (event: AllEvents<UserEventMap<EventMap>>) => void\n      : (\n          event: Emitter.EventType<Target, EventType, EventMap>,\n        ) => Emitter.ListenerReturnType<Target, EventType, EventMap> extends [\n          void,\n        ]\n          ? void\n          : Emitter.ListenerReturnType<Target, EventType, EventMap>\n\n  /**\n   * Returns the return type of the listener for the given event type.\n   *\n   * @example\n   * const emitter = new Emitter<{ getTotalPrice: TypedEvent<Cart, number> }>()\n   * type ListenerReturnType = Emitter.InferListenerReturnType<typeof emitter, 'getTotalPrice'>\n   * // number\n   */\n  export type ListenerReturnType<\n    Target extends Emitter<any>,\n    EventType extends keyof EventMap & string,\n    EventMap extends DefaultEventMap = InferEventMap<Target>,\n  > =\n    IsReservedEvent<EventType> extends true\n      ? void\n      : EventMap[EventType] extends TypedEvent<unknown, infer ReturnType>\n        ? ReturnType\n        : never\n}\n\nexport class Emitter<EventMap extends DefaultEventMap> {\n  #listeners: LensList<\n    Emitter.ListenerType<\n      typeof this,\n      keyof MergedEventMap<EventMap> & string,\n      MergedEventMap<EventMap>\n    >\n  >\n\n  constructor() {\n    this.#listeners = new LensList()\n  }\n\n  /**\n   * Adds a listener for the given event type.\n   */\n  public on<EventType extends keyof MergedEventMap<EventMap> & string>(\n    type: EventType,\n    listener: Emitter.ListenerType<\n      typeof this,\n      EventType,\n      MergedEventMap<EventMap>\n    >,\n    options?: TypedListenerOptions,\n  ): typeof this {\n    this.#addListener(type, listener, options)\n    return this\n  }\n\n  /**\n   * Adds a one-time listener for the given event type.\n   */\n  public once<EventType extends keyof MergedEventMap<EventMap> & string>(\n    type: EventType,\n    listener: Emitter.ListenerType<\n      typeof this,\n      EventType,\n      MergedEventMap<EventMap>\n    >,\n    options?: Omit<TypedListenerOptions, 'once'>,\n  ): typeof this {\n    return this.on(type, listener, {\n      ...(options || {}),\n      once: true,\n    })\n  }\n\n  /**\n   * Prepends a listener for the given event type.\n   */\n  public earlyOn<EventType extends keyof MergedEventMap<EventMap> & string>(\n    type: EventType,\n    listener: Emitter.ListenerType<\n      typeof this,\n      EventType,\n      MergedEventMap<EventMap>\n    >,\n    options?: TypedListenerOptions,\n  ): typeof this {\n    this.#addListener(type, listener, options, 'prepend')\n    return this\n  }\n\n  /**\n   * Prepends a one-time listener for the given event type.\n   */\n  public earlyOnce<EventType extends keyof MergedEventMap<EventMap> & string>(\n    type: EventType,\n    listener: Emitter.ListenerType<\n      typeof this,\n      EventType,\n      MergedEventMap<EventMap>\n    >,\n    options?: Omit<TypedListenerOptions, 'once'>,\n  ): typeof this {\n    return this.earlyOn(type, listener, {\n      ...(options || {}),\n      once: true,\n    })\n  }\n\n  /**\n   * Emits the given typed event.\n   *\n   * @returns {boolean} Returns `true` if the event had any listeners, `false` otherwise.\n   */\n  public emit<EventType extends keyof EventMap & string>(\n    event: Brand<EventMap[EventType], EventType, true>,\n  ): boolean {\n    if (this.#listeners.size === 0) {\n      return false\n    }\n\n    /**\n     * @note Calculate matching listeners before calling them\n     * since one-time listeners will self-destruct.\n     */\n    const hasListeners = this.listenerCount(event.type) > 0\n\n    const proxiedEvent = this.#proxyEvent(event)\n\n    for (const listener of this.#matchListeners(event.type)) {\n      if (\n        proxiedEvent.event[kPropagationStopped] != null &&\n        proxiedEvent.event[kPropagationStopped] !== this\n      ) {\n        proxiedEvent.revoke()\n        return false\n      }\n\n      if (proxiedEvent.event[kImmediatePropagationStopped]) {\n        break\n      }\n\n      this.#callListener(proxiedEvent.event, listener)\n    }\n\n    proxiedEvent.revoke()\n\n    return hasListeners\n  }\n\n  /**\n   * Emits the given typed event and returns a promise that resolves\n   * when all the listeners for that event have settled.\n   *\n   * @returns {Promise<Array<Emitter.ListenerReturnType>>} A promise that resolves\n   * with the return values of all listeners.\n   */\n  public async emitAsPromise<EventType extends keyof EventMap & string>(\n    event: Brand<EventMap[EventType], EventType, true>,\n  ): Promise<\n    Array<Emitter.ListenerReturnType<typeof this, EventType, EventMap>>\n  > {\n    if (this.#listeners.size === 0) {\n      return []\n    }\n\n    const pendingListeners: Array<\n      Promise<Emitter.ListenerReturnType<typeof this, EventType, EventMap>>\n    > = []\n\n    const proxiedEvent = this.#proxyEvent(event)\n\n    for (const listener of this.#matchListeners(event.type)) {\n      if (\n        proxiedEvent.event[kPropagationStopped] != null &&\n        proxiedEvent.event[kPropagationStopped] !== this\n      ) {\n        proxiedEvent.revoke()\n        return []\n      }\n\n      if (proxiedEvent.event[kImmediatePropagationStopped]) {\n        break\n      }\n\n      const listenerPromise = Promise.resolve(\n        this.#callListener(proxiedEvent.event, listener),\n      )\n\n      const returnValue = await listenerPromise\n\n      if (!this.#isTypelessListener(listener)) {\n        pendingListeners.push(returnValue)\n      }\n    }\n\n    proxiedEvent.revoke()\n\n    return Promise.allSettled(pendingListeners).then((results) => {\n      return results.map((result) =>\n        result.status === 'fulfilled' ? result.value : result.reason,\n      )\n    })\n  }\n\n  /**\n   * Emits the given event and returns a generator that yields\n   * the result of each listener in the order of their registration.\n   * This way, you stop exhausting the listeners once you get the expected value.\n   */\n  public *emitAsGenerator<EventType extends keyof EventMap & string>(\n    event: Brand<EventMap[EventType], EventType, true>,\n  ): Generator<Emitter.ListenerReturnType<typeof this, EventType, EventMap>> {\n    if (this.#listeners.size === 0) {\n      return\n    }\n\n    const proxiedEvent = this.#proxyEvent(event)\n\n    for (const listener of this.#matchListeners(event.type)) {\n      if (\n        proxiedEvent.event[kPropagationStopped] != null &&\n        proxiedEvent.event[kPropagationStopped] !== this\n      ) {\n        proxiedEvent.revoke()\n        return\n      }\n\n      if (proxiedEvent.event[kImmediatePropagationStopped]) {\n        break\n      }\n\n      const returnValue = this.#callListener(proxiedEvent.event, listener)\n\n      if (!this.#isTypelessListener(listener)) {\n        yield returnValue\n      }\n    }\n\n    proxiedEvent.revoke()\n  }\n\n  /**\n   * Removes a listener for the given event type.\n   */\n  public removeListener<\n    EventType extends keyof MergedEventMap<EventMap> & string,\n  >(\n    type: EventType,\n    listener: Emitter.ListenerType<\n      typeof this,\n      EventType,\n      MergedEventMap<EventMap>\n    >,\n  ): void {\n    this.#listeners.delete(type, listener)\n  }\n\n  /**\n   * Removes all listeners for the given event type.\n   * If no event type is provided, removes all existing listeners.\n   */\n  public removeAllListeners<\n    EventType extends keyof MergedEventMap<EventMap> & string,\n  >(type?: EventType): void {\n    if (type == null) {\n      this.#listeners.clear()\n      return\n    }\n\n    this.#listeners.deleteAll(type)\n  }\n\n  /**\n   * Returns the list of listeners for the given event type.\n   * If no even type is provided, returns all listeners.\n   */\n  public listeners<EventType extends keyof MergedEventMap<EventMap> & string>(\n    type?: EventType,\n  ): Array<\n    Emitter.ListenerType<typeof this, EventType, MergedEventMap<EventMap>>\n  > {\n    if (type == null) {\n      return this.#listeners.getAll()\n    }\n\n    return this.#listeners.get(type)\n  }\n\n  /**\n   * Returns the number of listeners for the given event type.\n   * If no even type is provided, returns the total number of listeners.\n   */\n  public listenerCount<\n    EventType extends keyof MergedEventMap<EventMap> & string,\n  >(type?: EventType): number {\n    if (type == null) {\n      return this.#listeners.size\n    }\n\n    return this.listeners(type).length\n  }\n\n  #addListener<EventType extends keyof MergedEventMap<EventMap> & string>(\n    type: EventType,\n    listener: Emitter.ListenerType<\n      typeof this,\n      EventType,\n      MergedEventMap<EventMap>\n    >,\n    options: TypedListenerOptions | undefined,\n    insertMode: 'append' | 'prepend' = 'append',\n  ): void {\n    if (insertMode === 'prepend') {\n      this.#listeners.prepend(type, listener)\n    } else {\n      this.#listeners.append(type, listener)\n    }\n\n    if (options) {\n      Object.defineProperty(listener, kListenerOptions, {\n        value: options,\n        enumerable: false,\n        writable: false,\n      })\n\n      if (options.signal) {\n        options.signal.addEventListener(\n          'abort',\n          () => {\n            this.removeListener(type, listener)\n          },\n          { once: true },\n        )\n      }\n    }\n  }\n\n  #proxyEvent<Event extends TypedEvent>(\n    event: Event,\n  ): { event: Event; revoke: () => void } {\n    const { stopPropagation } = event\n\n    event.stopPropagation = new Proxy(event.stopPropagation, {\n      apply: (target, thisArg, argArray) => {\n        event[kPropagationStopped] = this\n        return Reflect.apply(target, thisArg, argArray)\n      },\n    })\n\n    return {\n      event,\n      revoke() {\n        event.stopPropagation = stopPropagation\n      },\n    }\n  }\n\n  #callListener(\n    event: Event,\n    listener: ((event: any) => any) & {\n      [kListenerOptions]?: TypedListenerOptions\n    },\n  ) {\n    const returnValue = listener.call(this, event)\n\n    if (listener[kListenerOptions]?.once) {\n      const key = this.#isTypelessListener(listener) ? '*' : event.type\n      this.#listeners.delete(key, listener)\n    }\n\n    return returnValue\n  }\n\n  /**\n   * Return a list of all event listeners relevant for the given event type.\n   * This includes the explicit event listeners and also typeless event listeners.\n   */\n  *#matchListeners<EventType extends keyof EventMap & string>(type: EventType) {\n    for (const [key, listener] of this.#listeners) {\n      if (key === '*' || key === type) {\n        yield listener\n      }\n    }\n  }\n\n  #isTypelessListener(listener: any): boolean {\n    return this.#listeners.get('*').includes(listener)\n  }\n}\n"],"mappings":"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"}