{"version":3,"file":"Messenger.cjs","sourceRoot":"","sources":["../src/Messenger.ts"],"names":[],"mappings":"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","sourcesContent":["import { RestrictedMessenger } from './RestrictedMessenger';\n\nexport type ActionHandler<\n  Action extends ActionConstraint,\n  ActionType = Action['type'],\n> = (\n  ...args: ExtractActionParameters<Action, ActionType>\n) => ExtractActionResponse<Action, ActionType>;\n\nexport type ExtractActionParameters<\n  Action extends ActionConstraint,\n  ActionType = Action['type'],\n> = Action extends {\n  type: ActionType;\n  handler: (...args: infer HandlerArgs) => unknown;\n}\n  ? HandlerArgs\n  : never;\n\nexport type ExtractActionResponse<\n  Action extends ActionConstraint,\n  ActionType = Action['type'],\n> = Action extends {\n  type: ActionType;\n  handler: (...args: infer _) => infer HandlerReturnValue;\n}\n  ? HandlerReturnValue\n  : never;\n\nexport type ExtractEventHandler<\n  Event extends EventConstraint,\n  EventType = Event['type'],\n> = Event extends {\n  type: EventType;\n  payload: infer Payload;\n}\n  ? Payload extends unknown[]\n    ? (...payload: Payload) => void\n    : never\n  : never;\n\nexport type ExtractEventPayload<\n  Event extends EventConstraint,\n  EventType = Event['type'],\n> = Event extends {\n  type: EventType;\n  payload: infer Payload;\n}\n  ? Payload extends unknown[]\n    ? Payload\n    : never\n  : never;\n\nexport type GenericEventHandler = (...args: unknown[]) => void;\n\nexport type SelectorFunction<\n  Event extends EventConstraint,\n  EventType extends Event['type'],\n  ReturnValue,\n> = (...args: ExtractEventPayload<Event, EventType>) => ReturnValue;\nexport type SelectorEventHandler<SelectorReturnValue> = (\n  newValue: SelectorReturnValue,\n  previousValue: SelectorReturnValue | undefined,\n) => void;\n\nexport type ActionConstraint = {\n  type: string;\n  handler: ((...args: never) => unknown) | ((...args: never[]) => unknown);\n};\nexport type EventConstraint = {\n  type: string;\n  payload: unknown[];\n};\n\ntype EventSubscriptionMap<\n  Event extends EventConstraint,\n  ReturnValue = unknown,\n> = Map<\n  GenericEventHandler | SelectorEventHandler<ReturnValue>,\n  SelectorFunction<Event, Event['type'], ReturnValue> | undefined\n>;\n\n/**\n * A namespaced string\n *\n * This type verifies that the string Name is prefixed by the string Name followed by a colon.\n *\n * @template Namespace - The namespace we're checking for.\n * @template Name - The full string, including the namespace.\n */\nexport type NamespacedBy<\n  Namespace extends string,\n  Name extends string,\n> = Name extends `${Namespace}:${string}` ? Name : never;\n\nexport type NotNamespacedBy<\n  Namespace extends string,\n  Name extends string,\n> = Name extends `${Namespace}:${string}` ? never : Name;\n\nexport type NamespacedName<Namespace extends string = string> =\n  `${Namespace}:${string}`;\n\ntype NarrowToNamespace<Name, Namespace extends string> = Name extends {\n  type: `${Namespace}:${string}`;\n}\n  ? Name\n  : never;\n\ntype NarrowToAllowed<Name, Allowed extends string> = Name extends {\n  type: Allowed;\n}\n  ? Name\n  : never;\n\n/**\n * A message broker for \"actions\" and \"events\".\n *\n * The messenger allows registering functions as 'actions' that can be called elsewhere,\n * and it allows publishing and subscribing to events. Both actions and events are identified by\n * unique strings.\n *\n * @template Action - A type union of all Action types.\n * @template Event - A type union of all Event types.\n */\nexport class Messenger<\n  Action extends ActionConstraint,\n  Event extends EventConstraint,\n> {\n  readonly #actions = new Map<Action['type'], unknown>();\n\n  readonly #events = new Map<Event['type'], EventSubscriptionMap<Event>>();\n\n  /**\n   * A map of functions for getting the initial event payload.\n   *\n   * Used only for events that represent state changes.\n   */\n  readonly #initialEventPayloadGetters = new Map<\n    Event['type'],\n    () => ExtractEventPayload<Event, Event['type']>\n  >();\n\n  /**\n   * A cache of selector return values for their respective handlers.\n   */\n  readonly #eventPayloadCache = new Map<\n    GenericEventHandler,\n    unknown | undefined\n  >();\n\n  /**\n   * Register an action handler.\n   *\n   * This will make the registered function available to call via the `call` method.\n   *\n   * @param actionType - The action type. This is a unqiue identifier for this action.\n   * @param handler - The action handler. This function gets called when the `call` method is\n   * invoked with the given action type.\n   * @throws Will throw when a handler has been registered for this action type already.\n   * @template ActionType - A type union of Action type strings.\n   */\n  registerActionHandler<ActionType extends Action['type']>(\n    actionType: ActionType,\n    handler: ActionHandler<Action, ActionType>,\n  ) {\n    if (this.#actions.has(actionType)) {\n      throw new Error(\n        `A handler for ${actionType} has already been registered`,\n      );\n    }\n    this.#actions.set(actionType, handler);\n  }\n\n  /**\n   * Unregister an action handler.\n   *\n   * This will prevent this action from being called.\n   *\n   * @param actionType - The action type. This is a unqiue identifier for this action.\n   * @template ActionType - A type union of Action type strings.\n   */\n  unregisterActionHandler<ActionType extends Action['type']>(\n    actionType: ActionType,\n  ) {\n    this.#actions.delete(actionType);\n  }\n\n  /**\n   * Unregister all action handlers.\n   *\n   * This prevents all actions from being called.\n   */\n  clearActions() {\n    this.#actions.clear();\n  }\n\n  /**\n   * Call an action.\n   *\n   * This function will call the action handler corresponding to the given action type, passing\n   * along any parameters given.\n   *\n   * @param actionType - The action type. This is a unqiue identifier for this action.\n   * @param params - The action parameters. These must match the type of the parameters of the\n   * registered action handler.\n   * @throws Will throw when no handler has been registered for the given type.\n   * @template ActionType - A type union of Action type strings.\n   * @returns The action return value.\n   */\n  call<ActionType extends Action['type']>(\n    actionType: ActionType,\n    ...params: ExtractActionParameters<Action, ActionType>\n  ): ExtractActionResponse<Action, ActionType> {\n    const handler = this.#actions.get(actionType) as ActionHandler<\n      Action,\n      ActionType\n    >;\n    if (!handler) {\n      throw new Error(`A handler for ${actionType} has not been registered`);\n    }\n    return handler(...params);\n  }\n\n  /**\n   * Register a function for getting the initial payload for an event.\n   *\n   * This is used for events that represent a state change, where the payload is the state.\n   * Registering a function for getting the payload allows event selectors to have a point of\n   * comparison the first time state changes.\n   *\n   * @param args - The arguments to this function\n   * @param args.eventType - The event type to register a payload for.\n   * @param args.getPayload - A function for retrieving the event payload.\n   */\n  registerInitialEventPayload<EventType extends Event['type']>({\n    eventType,\n    getPayload,\n  }: {\n    eventType: EventType;\n    getPayload: () => ExtractEventPayload<Event, EventType>;\n  }) {\n    this.#initialEventPayloadGetters.set(eventType, getPayload);\n  }\n\n  /**\n   * Publish an event.\n   *\n   * Publishes the given payload to all subscribers of the given event type.\n   *\n   * Note that this method should never throw directly. Any errors from\n   * subscribers are captured and re-thrown in a timeout handler.\n   *\n   * @param eventType - The event type. This is a unique identifier for this event.\n   * @param payload - The event payload. The type of the parameters for each event handler must\n   * match the type of this payload.\n   * @template EventType - A type union of Event type strings.\n   */\n  publish<EventType extends Event['type']>(\n    eventType: EventType,\n    ...payload: ExtractEventPayload<Event, EventType>\n  ) {\n    const subscribers = this.#events.get(eventType);\n\n    if (subscribers) {\n      for (const [handler, selector] of subscribers.entries()) {\n        try {\n          if (selector) {\n            const previousValue = this.#eventPayloadCache.get(handler);\n            const newValue = selector(...payload);\n\n            if (newValue !== previousValue) {\n              this.#eventPayloadCache.set(handler, newValue);\n              handler(newValue, previousValue);\n            }\n          } else {\n            (handler as GenericEventHandler)(...payload);\n          }\n        } catch (error) {\n          // Throw error after timeout so that it is capured as a console error\n          // (and by Sentry) without interrupting the event publishing.\n          setTimeout(() => {\n            throw error;\n          });\n        }\n      }\n    }\n  }\n\n  /**\n   * Subscribe to an event.\n   *\n   * Registers the given function as an event handler for the given event type.\n   *\n   * @param eventType - The event type. This is a unique identifier for this event.\n   * @param handler - The event handler. The type of the parameters for this event handler must\n   * match the type of the payload for this event type.\n   * @template EventType - A type union of Event type strings.\n   */\n  subscribe<EventType extends Event['type']>(\n    eventType: EventType,\n    handler: ExtractEventHandler<Event, EventType>,\n  ): void;\n\n  /**\n   * Subscribe to an event, with a selector.\n   *\n   * Registers the given handler function as an event handler for the given\n   * event type. When an event is published, its payload is first passed to the\n   * selector. The event handler is only called if the selector's return value\n   * differs from its last known return value.\n   *\n   * @param eventType - The event type. This is a unique identifier for this event.\n   * @param handler - The event handler. The type of the parameters for this event\n   * handler must match the return type of the selector.\n   * @param selector - The selector function used to select relevant data from\n   * the event payload. The type of the parameters for this selector must match\n   * the type of the payload for this event type.\n   * @template EventType - A type union of Event type strings.\n   * @template SelectorReturnValue - The selector return value.\n   */\n  subscribe<EventType extends Event['type'], SelectorReturnValue>(\n    eventType: EventType,\n    handler: SelectorEventHandler<SelectorReturnValue>,\n    selector: SelectorFunction<Event, EventType, SelectorReturnValue>,\n  ): void;\n\n  subscribe<EventType extends Event['type'], SelectorReturnValue>(\n    eventType: EventType,\n    handler: ExtractEventHandler<Event, EventType>,\n    selector?: SelectorFunction<Event, EventType, SelectorReturnValue>,\n  ): void {\n    let subscribers = this.#events.get(eventType);\n    if (!subscribers) {\n      subscribers = new Map();\n      this.#events.set(eventType, subscribers);\n    }\n\n    subscribers.set(handler, selector);\n\n    if (selector) {\n      const getPayload = this.#initialEventPayloadGetters.get(eventType);\n      if (getPayload) {\n        const initialValue = selector(...getPayload());\n        this.#eventPayloadCache.set(handler, initialValue);\n      }\n    }\n  }\n\n  /**\n   * Unsubscribe from an event.\n   *\n   * Unregisters the given function as an event handler for the given event.\n   *\n   * @param eventType - The event type. This is a unique identifier for this event.\n   * @param handler - The event handler to unregister.\n   * @throws Will throw when the given event handler is not registered for this event.\n   * @template EventType - A type union of Event type strings.\n   */\n  unsubscribe<EventType extends Event['type']>(\n    eventType: EventType,\n    handler: ExtractEventHandler<Event, EventType>,\n  ) {\n    const subscribers = this.#events.get(eventType);\n\n    if (!subscribers || !subscribers.has(handler)) {\n      throw new Error(`Subscription not found for event: ${eventType}`);\n    }\n\n    const selector = subscribers.get(handler);\n    if (selector) {\n      this.#eventPayloadCache.delete(handler);\n    }\n\n    subscribers.delete(handler);\n  }\n\n  /**\n   * Clear subscriptions for a specific event.\n   *\n   * This will remove all subscribed handlers for this event.\n   *\n   * @param eventType - The event type. This is a unique identifier for this event.\n   * @template EventType - A type union of Event type strings.\n   */\n  clearEventSubscriptions<EventType extends Event['type']>(\n    eventType: EventType,\n  ) {\n    this.#events.delete(eventType);\n  }\n\n  /**\n   * Clear all subscriptions.\n   *\n   * This will remove all subscribed handlers for all events.\n   */\n  clearSubscriptions() {\n    this.#events.clear();\n  }\n\n  /**\n   * Get a restricted messenger\n   *\n   * Returns a wrapper around the messenger instance that restricts access to actions and events.\n   * The provided allowlists grant the ability to call the listed actions and subscribe to the\n   * listed events. The \"name\" provided grants ownership of any actions and events under that\n   * namespace. Ownership allows registering actions and publishing events, as well as\n   * unregistering actions and clearing event subscriptions.\n   *\n   * @param options - Messenger options.\n   * @param options.name - The name of the thing this messenger will be handed to (e.g. the\n   * controller name). This grants \"ownership\" of actions and events under this namespace to the\n   * restricted messenger returned.\n   * @param options.allowedActions - The list of actions that this restricted messenger should be\n   * allowed to call.\n   * @param options.allowedEvents - The list of events that this restricted messenger should be\n   * allowed to subscribe to.\n   * @template Namespace - The namespace for this messenger. Typically this is the name of the\n   * module that this messenger has been created for. The authority to publish events and register\n   * actions under this namespace is granted to this restricted messenger instance.\n   * @template AllowedAction - A type union of the 'type' string for any allowed actions.\n   * This must not include internal actions that are in the messenger's namespace.\n   * @template AllowedEvent - A type union of the 'type' string for any allowed events.\n   * This must not include internal events that are in the messenger's namespace.\n   * @returns The restricted messenger.\n   */\n  getRestricted<\n    Namespace extends string,\n    AllowedAction extends NotNamespacedBy<Namespace, Action['type']> = never,\n    AllowedEvent extends NotNamespacedBy<Namespace, Event['type']> = never,\n  >({\n    name,\n    allowedActions,\n    allowedEvents,\n  }: {\n    name: Namespace;\n    allowedActions: NotNamespacedBy<\n      Namespace,\n      Extract<Action['type'], AllowedAction>\n    >[];\n    allowedEvents: NotNamespacedBy<\n      Namespace,\n      Extract<Event['type'], AllowedEvent>\n    >[];\n  }): RestrictedMessenger<\n    Namespace,\n    | NarrowToNamespace<Action, Namespace>\n    | NarrowToAllowed<Action, AllowedAction>,\n    NarrowToNamespace<Event, Namespace> | NarrowToAllowed<Event, AllowedEvent>,\n    AllowedAction,\n    AllowedEvent\n  > {\n    return new RestrictedMessenger({\n      messenger: this,\n      name,\n      allowedActions,\n      allowedEvents,\n    });\n  }\n}\n"]}