{"version":3,"file":"RestrictedMessenger.mjs","sourceRoot":"","sources":["../src/RestrictedMessenger.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n  ActionConstraint,\n  ActionHandler,\n  Messenger,\n  EventConstraint,\n  ExtractActionParameters,\n  ExtractActionResponse,\n  ExtractEventHandler,\n  ExtractEventPayload,\n  NamespacedName,\n  NotNamespacedBy,\n  SelectorEventHandler,\n  SelectorFunction,\n} from './Messenger';\n\n/**\n * A universal supertype of all `RestrictedMessenger` instances. This type can be assigned to any\n * `RestrictedMessenger` type.\n *\n * @template Namespace - Name of the module this messenger is for. Optionally can be used to\n * narrow this type to a constraint for the messenger of a specific module.\n */\nexport type RestrictedMessengerConstraint<Namespace extends string = string> =\n  RestrictedMessenger<\n    Namespace,\n    ActionConstraint,\n    EventConstraint,\n    string,\n    string\n  >;\n\n/**\n * A restricted messenger.\n *\n * This acts as a wrapper around the messenger instance that restricts access to actions\n * and events.\n *\n * @template Namespace - The namespace for this messenger. Typically this is the name of the controller or\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 Action - A type union of all Action types.\n * @template Event - A type union of all Event types.\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 */\nexport class RestrictedMessenger<\n  Namespace extends string,\n  Action extends ActionConstraint,\n  Event extends EventConstraint,\n  AllowedAction extends string,\n  AllowedEvent extends string,\n> {\n  readonly #messenger: Messenger<ActionConstraint, EventConstraint>;\n\n  readonly #namespace: Namespace;\n\n  readonly #allowedActions: NotNamespacedBy<Namespace, AllowedAction>[];\n\n  readonly #allowedEvents: NotNamespacedBy<Namespace, AllowedEvent>[];\n\n  /**\n   * Constructs a restricted messenger\n   *\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 - Options.\n   * @param options.messenger - The messenger instance that is being wrapped.\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   */\n  constructor({\n    messenger,\n    name,\n    allowedActions,\n    allowedEvents,\n  }: {\n    messenger?: Messenger<ActionConstraint, EventConstraint>;\n    name: Namespace;\n    allowedActions: NotNamespacedBy<Namespace, AllowedAction>[];\n    allowedEvents: NotNamespacedBy<Namespace, AllowedEvent>[];\n  }) {\n    if (!messenger) {\n      throw new Error('Messenger not provided');\n    }\n    // The above condition guarantees that one of these options is defined.\n    this.#messenger = messenger;\n    this.#namespace = name;\n    this.#allowedActions = allowedActions;\n    this.#allowedEvents = allowedEvents;\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   * The action type this handler is registered under *must* be in the current namespace.\n   *\n   * @param action - 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 if an action handler that is not in the current namespace is being registered.\n   * @template ActionType - A type union of Action type strings that are namespaced by Namespace.\n   */\n  registerActionHandler<\n    ActionType extends Action['type'] & NamespacedName<Namespace>,\n  >(action: ActionType, handler: ActionHandler<Action, ActionType>) {\n    /* istanbul ignore if */ // Branch unreachable with valid types\n    if (!this.#isInCurrentNamespace(action)) {\n      throw new Error(\n        `Only allowed registering action handlers prefixed by '${\n          this.#namespace\n        }:'`,\n      );\n    }\n    this.#messenger.registerActionHandler(action, handler);\n  }\n\n  /**\n   * Unregister an action handler.\n   *\n   * This will prevent this action from being called.\n   *\n   * The action type being unregistered *must* be in the current namespace.\n   *\n   * @param action - The action type. This is a unique identifier for this action.\n   * @throws Will throw if an action handler that is not in the current namespace is being unregistered.\n   * @template ActionType - A type union of Action type strings that are namespaced by Namespace.\n   */\n  unregisterActionHandler<\n    ActionType extends Action['type'] & NamespacedName<Namespace>,\n  >(action: ActionType) {\n    /* istanbul ignore if */ // Branch unreachable with valid types\n    if (!this.#isInCurrentNamespace(action)) {\n      throw new Error(\n        `Only allowed unregistering action handlers prefixed by '${\n          this.#namespace\n        }:'`,\n      );\n    }\n    this.#messenger.unregisterActionHandler(action);\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   * The action type being called must be on the action allowlist.\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 allowed Action type strings.\n   * @returns The action return value.\n   */\n  call<\n    ActionType extends\n      | AllowedAction\n      | (Action['type'] & NamespacedName<Namespace>),\n  >(\n    actionType: ActionType,\n    ...params: ExtractActionParameters<Action, ActionType>\n  ): ExtractActionResponse<Action, ActionType> {\n    if (!this.#isAllowedAction(actionType)) {\n      throw new Error(`Action missing from allow list: ${actionType}`);\n    }\n    const response = this.#messenger.call<ActionType>(actionType, ...params);\n\n    return response;\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   * The event type *must* be in the current namespace\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<\n    EventType extends Event['type'] & NamespacedName<Namespace>,\n  >({\n    eventType,\n    getPayload,\n  }: {\n    eventType: EventType;\n    getPayload: () => ExtractEventPayload<Event, EventType>;\n  }) {\n    /* istanbul ignore if */ // Branch unreachable with valid types\n    if (!this.#isInCurrentNamespace(eventType)) {\n      throw new Error(\n        `Only allowed publishing events prefixed by '${this.#namespace}:'`,\n      );\n    }\n    this.#messenger.registerInitialEventPayload({\n      eventType,\n      getPayload,\n    });\n  }\n\n  /**\n   * Publish an event.\n   *\n   * Publishes the given payload to all subscribers of the given event type.\n   *\n   * The event type being published *must* be in the current namespace.\n   *\n   * @param event - 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   * @throws Will throw if an event that is not in the current namespace is being published.\n   * @template EventType - A type union of Event type strings that are namespaced by Namespace.\n   */\n  publish<EventType extends Event['type'] & NamespacedName<Namespace>>(\n    event: EventType,\n    ...payload: ExtractEventPayload<Event, EventType>\n  ) {\n    /* istanbul ignore if */ // Branch unreachable with valid types\n    if (!this.#isInCurrentNamespace(event)) {\n      throw new Error(\n        `Only allowed publishing events prefixed by '${this.#namespace}:'`,\n      );\n    }\n    this.#messenger.publish(event, ...payload);\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   * The event type being subscribed to must be on the event allowlist.\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   * @throws Will throw if the given event is not an allowed event for this messenger.\n   * @template EventType - A type union of Event type strings.\n   */\n  subscribe<\n    EventType extends\n      | AllowedEvent\n      | (Event['type'] & NamespacedName<Namespace>),\n  >(eventType: EventType, handler: ExtractEventHandler<Event, EventType>): 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   * The event type being subscribed to must be on the event allowlist.\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   * @throws Will throw if the given event is not an allowed event for this messenger.\n   * @template EventType - A type union of Event type strings.\n   * @template SelectorReturnValue - The selector return value.\n   */\n  subscribe<\n    EventType extends\n      | AllowedEvent\n      | (Event['type'] & NamespacedName<Namespace>),\n    SelectorReturnValue,\n  >(\n    eventType: EventType,\n    handler: SelectorEventHandler<SelectorReturnValue>,\n    selector: SelectorFunction<Event, EventType, SelectorReturnValue>,\n  ): void;\n\n  subscribe<\n    EventType extends\n      | AllowedEvent\n      | (Event['type'] & NamespacedName<Namespace>),\n    SelectorReturnValue,\n  >(\n    event: EventType,\n    handler: ExtractEventHandler<Event, EventType>,\n    selector?: SelectorFunction<Event, EventType, SelectorReturnValue>,\n  ) {\n    if (!this.#isAllowedEvent(event)) {\n      throw new Error(`Event missing from allow list: ${event}`);\n    }\n\n    if (selector) {\n      return this.#messenger.subscribe(event, handler, selector);\n    }\n    return this.#messenger.subscribe(event, handler);\n  }\n\n  /**\n   * Unsubscribe from an event.\n   *\n   * Unregisters the given function as an event handler for the given event.\n   *\n   * The event type being unsubscribed to must be on the event allowlist.\n   *\n   * @param event - The event type. This is a unique identifier for this event.\n   * @param handler - The event handler to unregister.\n   * @throws Will throw if the given event is not an allowed event for this messenger.\n   * @template EventType - A type union of allowed Event type strings.\n   */\n  unsubscribe<\n    EventType extends\n      | AllowedEvent\n      | (Event['type'] & NamespacedName<Namespace>),\n  >(event: EventType, handler: ExtractEventHandler<Event, EventType>) {\n    if (!this.#isAllowedEvent(event)) {\n      throw new Error(`Event missing from allow list: ${event}`);\n    }\n    this.#messenger.unsubscribe(event, handler);\n  }\n\n  /**\n   * Clear subscriptions for a specific event.\n   *\n   * This will remove all subscribed handlers for this event.\n   *\n   * The event type being cleared *must* be in the current namespace.\n   *\n   * @param event - The event type. This is a unique identifier for this event.\n   * @throws Will throw if a subscription for an event that is not in the current namespace is being cleared.\n   * @template EventType - A type union of Event type strings that are namespaced by Namespace.\n   */\n  clearEventSubscriptions<\n    EventType extends Event['type'] & NamespacedName<Namespace>,\n  >(event: EventType) {\n    if (!this.#isInCurrentNamespace(event)) {\n      throw new Error(\n        `Only allowed clearing events prefixed by '${this.#namespace}:'`,\n      );\n    }\n    this.#messenger.clearEventSubscriptions(event);\n  }\n\n  /**\n   * Determine whether the given event type is allowed. Event types are\n   * allowed if they are in the current namespace or on the list of\n   * allowed events.\n   *\n   * @param eventType - The event type to check.\n   * @returns Whether the event type is allowed.\n   */\n  #isAllowedEvent(\n    eventType: Event['type'],\n  ): eventType is\n    | NamespacedName<Namespace>\n    | NotNamespacedBy<Namespace, AllowedEvent> {\n    // Safely upcast to allow runtime check\n    const allowedEvents: string[] | null = this.#allowedEvents;\n    return (\n      this.#isInCurrentNamespace(eventType) ||\n      (allowedEvents !== null && allowedEvents.includes(eventType))\n    );\n  }\n\n  /**\n   * Determine whether the given action type is allowed. Action types\n   * are allowed if they are in the current namespace or on the list of\n   * allowed actions.\n   *\n   * @param actionType - The action type to check.\n   * @returns Whether the action type is allowed.\n   */\n  #isAllowedAction(\n    actionType: Action['type'],\n  ): actionType is\n    | NamespacedName<Namespace>\n    | NotNamespacedBy<Namespace, AllowedAction> {\n    // Safely upcast to allow runtime check\n    const allowedActions: string[] | null = this.#allowedActions;\n    return (\n      this.#isInCurrentNamespace(actionType) ||\n      (allowedActions !== null && allowedActions.includes(actionType))\n    );\n  }\n\n  /**\n   * Determine whether the given name is within the current namespace.\n   *\n   * @param name - The name to check\n   * @returns Whether the name is within the current namespace\n   */\n  #isInCurrentNamespace(name: string): name is NamespacedName<Namespace> {\n    return name.startsWith(`${this.#namespace}:`);\n  }\n}\n"]}