{"version":3,"file":"ably-chat.umd.cjs","sources":["../../src/core/errors.ts","../../src/core/client-id.ts","../../src/core/logger.ts","../../src/core/config.ts","../../src/core/realtime-subscriptions.ts","../../src/core/utils/event-emitter.ts","../../src/core/connection.ts","../../src/core/id.ts","../../node_modules/dequal/dist/index.mjs","../../src/core/events.ts","../../node_modules/lodash.clonedeep/index.js","../../src/core/message.ts","../../src/core/message-parser.ts","../../src/core/serial.ts","../../src/core/message-reactions.ts","../../src/core/rest-types.ts","../../src/core/messages.ts","../../src/core/chat-api.ts","../../src/core/channel.ts","../../src/core/version.ts","../../src/core/channel-manager.ts","../../src/core/occupancy-parser.ts","../../src/core/occupancy.ts","../../src/core/presence.ts","../../node_modules/async-mutex/index.mjs","../../src/core/room-status.ts","../../src/core/room-lifecycle-manager.ts","../../src/core/room-options.ts","../../src/core/realtime.ts","../../src/core/room-reaction.ts","../../src/core/room-reaction-parser.ts","../../src/core/room-reactions.ts","../../src/core/typing.ts","../../src/core/room.ts","../../src/core/rooms.ts","../../src/core/chat-client.ts"],"sourcesContent":["import * as Ably from 'ably';\n\n/**\n * Error codes for the Chat SDK.\n */\nexport enum ErrorCode {\n  /**\n   * The request was invalid.\n   */\n  BadRequest = 40000,\n\n  /**\n   * Invalid argument provided.\n   */\n  InvalidArgument = 40003,\n\n  /**\n   * Invalid client ID.\n   */\n  InvalidClientId = 40012,\n\n  /**\n   * Resource has been disposed.\n   */\n  ResourceDisposed = 40014,\n\n  /**\n   * The message was rejected before publishing by a rule on the chat room.\n   */\n  MessageRejectedByBeforePublishRule = 42211,\n\n  /**\n   * The message was rejected before publishing by a moderation rule on the chat room.\n   */\n  MessageRejectedByModeration = 42213,\n\n  /**\n   * The client is not connected to Ably.\n   */\n  Disconnected = 80003,\n\n  /**\n   * Could not re-enter presence automatically after a room re-attach occurred.\n   */\n  PresenceAutoReentryFailed = 91004,\n\n  /**\n   * The room has experienced a discontinuity.\n   */\n  RoomDiscontinuity = 102100,\n\n  // Unable to perform operation;\n\n  /**\n   * Cannot perform operation because the room is in an invalid state.\n   */\n  RoomInInvalidState = 102112,\n\n  /**\n   * Room was released before the operation could complete.\n   */\n  RoomReleasedBeforeOperationCompleted = 102106,\n\n  /**\n   * A room already exists with different options.\n   */\n  RoomExistsWithDifferentOptions = 102107,\n\n  /**\n   * Feature is not enabled in room options.\n   */\n  FeatureNotEnabledInRoom = 102108,\n\n  /**\n   * Listener has not been subscribed yet.\n   */\n  ListenerNotSubscribed = 102109,\n\n  /**\n   * Channel serial is not defined when expected.\n   */\n  ChannelSerialNotDefined = 102110,\n\n  /**\n   * Channel options cannot be modified after the channel has been requested.\n   */\n  ChannelOptionsCannotBeModified = 102111,\n\n  /**\n   * Failed to enforce sequential execution of the operation.\n   */\n  OperationSerializationFailed = 102113,\n\n  // 102200 - 102300 are reserved for React errors\n\n  /**\n   * React hook must be used within the appropriate provider.\n   */\n  ReactHookMustBeUsedWithinProvider = 102200,\n\n  /**\n   * React component has been unmounted.\n   */\n  ReactComponentUnmounted = 102201,\n\n  /**\n   * Failed to fetch presence data after maximum retries.\n   */\n  PresenceFetchFailed = 102202,\n}\n\n/**\n * Returns true if the {@link Ably.ErrorInfo} code matches the provided ErrorCode value.\n * @param errorInfo The error info to check.\n * @param error The error code to compare against.\n * @returns true if the error code matches, false otherwise.\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison\nexport const errorInfoIs = (errorInfo: Ably.ErrorInfo, error: ErrorCode): boolean => errorInfo.code === error;\n","import * as Ably from 'ably';\n\nimport { ErrorCode } from './errors.js';\nimport { Logger } from './logger.js';\n\n/**\n * Interface for a type that resolves the current clientId from the realtime client.\n */\nexport interface ClientIdResolver {\n  /**\n   * Return the current clientId, throwing an exception if one isn't set.\n   * @throws Ably.ErrorInfo if no clientId is set.\n   * @returns string The resolved clientId\n   */\n  get(): string;\n}\n\nexport class DefaultClientIdResolver implements ClientIdResolver {\n  constructor(\n    private _realtime: Ably.Realtime,\n    private _logger: Logger,\n  ) {}\n\n  get(): string {\n    const clientId = this._realtime.auth.clientId;\n    if (!clientId) {\n      this._logger.error('unable to get client id; client id is not set', { clientId });\n      throw new Ably.ErrorInfo('unable to get client id; client id is not set', ErrorCode.InvalidClientId, 400);\n    }\n\n    return clientId;\n  }\n}\n","import * as Ably from 'ably';\n\nimport { NormalizedChatClientOptions } from './config.js';\nimport { ErrorCode } from './errors.js';\n\n/**\n * Interface for loggers.\n */\nexport interface Logger {\n  /**\n   * Log a message at the trace level.\n   * @param message The message to log.\n   * @param context The context of the log message as key-value pairs.\n   */\n  trace(message: string, context?: LogContext): void;\n\n  /**\n   * Log a message at the debug level.\n   * @param message The message to log.\n   * @param context The context of the log message as key-value pairs.\n   */\n  debug(message: string, context?: LogContext): void;\n\n  /**\n   * Log a message at the info level.\n   * @param message The message to log.\n   * @param context The context of the log message as key-value pairs.\n   */\n  info(message: string, context?: LogContext): void;\n\n  /**\n   * Log a message at the warn level.\n   * @param message The message to log.\n   * @param context The context of the log message as key-value pairs.\n   */\n  warn(message: string, context?: LogContext): void;\n\n  /**\n   * Log a message at the error level.\n   * @param message The message to log.\n   * @param context The context of the log message as key-value pairs.\n   */\n  error(message: string, context?: LogContext): void;\n\n  /**\n   * Creates a new logger with a context that will be merged with any context provided to individual log calls.\n   * The context will be overridden by any matching keys in the individual log call's context.\n   * @param context The context to use for all log calls.\n   * @returns A new logger instance with the context.\n   */\n  withContext(context: LogContext): Logger;\n}\n\n/**\n * Represents the different levels of logging that can be used.\n */\nexport enum LogLevel {\n  /**\n   * Something routine and expected has occurred. This level will provide logs for the vast majority of operations\n   * and function calls.\n   */\n  Trace = 'trace',\n\n  /**\n   * Development information, messages that are useful when trying to debug library behavior,\n   * but superfluous to normal operation.\n   */\n  Debug = 'debug',\n\n  /**\n   * Informational messages. Operationally significant to the library but not out of the ordinary.\n   */\n  Info = 'info',\n\n  /**\n   * Anything that is not immediately an error, but could cause unexpected behavior in the future. For example,\n   * passing an invalid value to an option. Indicates that some action should be taken to prevent future errors.\n   */\n  Warn = 'warn',\n\n  /**\n   * A given operation has failed and cannot be automatically recovered. The error may threaten the continuity\n   * of operation.\n   */\n  Error = 'error',\n\n  /**\n   * No logging will be performed.\n   */\n  Silent = 'silent',\n}\n\n/**\n * Represents the context of a log message.\n * It is an object of key-value pairs that can be used to provide additional context to a log message.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type LogContext = Record<string, any>;\n\n/**\n * A function that can be used to handle log messages.\n * @param message The message to log.\n * @param level The log level of the message.\n * @param context The context of the log message as key-value pairs.\n */\nexport type LogHandler = (message: string, level: LogLevel, context?: LogContext) => void;\n\n/**\n * A simple console logger that logs messages to the console.\n * @param message The message to log.\n * @param level The log level of the message.\n * @param context - The context of the log message as key-value pairs.\n */\nexport const consoleLogger = (message: string, level: LogLevel, context?: LogContext) => {\n  const contextString = context ? `, context: ${JSON.stringify(context)}` : '';\n  const formattedMessage = `[${new Date().toISOString()}] ${level.valueOf().toUpperCase()} ably-chat: ${message}${contextString}`;\n\n  switch (level) {\n    case LogLevel.Trace:\n    case LogLevel.Debug: {\n      console.log(formattedMessage);\n      break;\n    }\n    case LogLevel.Info: {\n      console.info(formattedMessage);\n      break;\n    }\n    case LogLevel.Warn: {\n      console.warn(formattedMessage);\n      break;\n    }\n    case LogLevel.Error: {\n      console.error(formattedMessage);\n      break;\n    }\n    case LogLevel.Silent: {\n      break;\n    }\n  }\n};\n\nexport const makeLogger = (options: NormalizedChatClientOptions): Logger => {\n  const logHandler = options.logHandler ?? consoleLogger;\n\n  return new DefaultLogger(logHandler, options.logLevel);\n};\n\n/**\n * A convenient list of log levels as numbers that can be used for easier comparison.\n */\nenum LogLevelNumber {\n  Trace = 0,\n  Debug = 1,\n  Info = 2,\n  Warn = 3,\n  Error = 4,\n  Silent = 5,\n}\n\n/**\n * A mapping of log levels to their numeric equivalents.\n */\nconst logLevelNumberMap = new Map<LogLevel, LogLevelNumber>([\n  [LogLevel.Trace, LogLevelNumber.Trace],\n  [LogLevel.Debug, LogLevelNumber.Debug],\n  [LogLevel.Info, LogLevelNumber.Info],\n  [LogLevel.Warn, LogLevelNumber.Warn],\n  [LogLevel.Error, LogLevelNumber.Error],\n  [LogLevel.Silent, LogLevelNumber.Silent],\n]);\n\n/**\n * A default logger implementation.\n */\nclass DefaultLogger implements Logger {\n  private readonly _handler: LogHandler;\n  private readonly _levelNumber: LogLevelNumber;\n  private readonly _context?: LogContext;\n\n  constructor(handler: LogHandler, level: LogLevel, context?: LogContext) {\n    this._handler = handler;\n    this._context = context;\n\n    const levelNumber = logLevelNumberMap.get(level);\n    if (levelNumber === undefined) {\n      throw new Ably.ErrorInfo(`unable to create logger; invalid log level: ${level}`, ErrorCode.InvalidArgument, 400);\n    }\n\n    this._levelNumber = levelNumber;\n  }\n\n  trace(message: string, context?: LogContext): void {\n    this._write(message, LogLevel.Trace, LogLevelNumber.Trace, context);\n  }\n\n  debug(message: string, context?: LogContext): void {\n    this._write(message, LogLevel.Debug, LogLevelNumber.Debug, context);\n  }\n\n  info(message: string, context?: LogContext): void {\n    this._write(message, LogLevel.Info, LogLevelNumber.Info, context);\n  }\n\n  warn(message: string, context?: LogContext): void {\n    this._write(message, LogLevel.Warn, LogLevelNumber.Warn, context);\n  }\n\n  error(message: string, context?: LogContext): void {\n    this._write(message, LogLevel.Error, LogLevelNumber.Error, context);\n  }\n\n  withContext(context: LogContext): Logger {\n    // Get the original log level by finding the key in logLevelNumberMap that matches this._levelNumber\n    const originalLevel =\n      [...logLevelNumberMap.entries()].find(([, value]) => value === this._levelNumber)?.[0] ?? LogLevel.Error;\n\n    return new DefaultLogger(this._handler, originalLevel, this._mergeContext(context));\n  }\n\n  private _write(message: string, level: LogLevel, levelNumber: LogLevelNumber, context?: LogContext): void {\n    if (levelNumber >= this._levelNumber) {\n      this._handler(message, level, this._mergeContext(context));\n    }\n  }\n\n  private _mergeContext(context?: LogContext): LogContext | undefined {\n    if (!this._context) {\n      return context ?? undefined;\n    }\n\n    return context ? { ...this._context, ...context } : this._context;\n  }\n}\n","import { LogHandler, LogLevel } from './logger.js';\n\n/**\n * Configuration options for the chat client.\n */\nexport interface ChatClientOptions {\n  /**\n   * A custom log handler that will be used to log messages from the client.\n   * @defaultValue The client will log messages to the console.\n   */\n  logHandler?: LogHandler;\n\n  /**\n   * The minimum log level at which messages will be logged.\n   * @defaultValue LogLevel.error\n   */\n  logLevel?: LogLevel;\n}\n\n/**\n * Default configuration options for the chat client.\n */\nconst defaultClientOptions = {\n  logLevel: LogLevel.Error,\n};\n\n/**\n * This type is used to modify the properties of one type with the properties of another type and thus\n * can be used to turn client options into normalized client options.\n */\ntype Modify<T, R> = Omit<T, keyof R> & R;\n\n/**\n * These are the normalized client options, with default values filled in for any missing properties.\n */\nexport type NormalizedChatClientOptions = Modify<\n  ChatClientOptions,\n  {\n    logLevel: LogLevel;\n  }\n>;\n\nexport const normalizeClientOptions = (options?: ChatClientOptions): NormalizedChatClientOptions => {\n  options = options ?? {};\n\n  return {\n    ...options,\n    logLevel: options.logLevel ?? defaultClientOptions.logLevel,\n  };\n};\n","/**\n * Interface for objects that support listeners via the on/off pattern.\n */\nexport interface Onable<T> {\n  /**\n   * Subscribe to events with a callback.\n   * @param callback The callback function to be called when events occur.\n   */\n  on(callback: (data: T) => void): void;\n\n  /**\n   * Subscribe to specific events, with a callback.\n   * @param events The events to subscribe to.\n   * @param callback The callback function to be called when events occur.\n   */\n  on(events: string[] | string, callback: (data: T) => void): void;\n\n  /**\n   * Subscribe to all events, once.\n   */\n  once(callback: (data: T) => void): void;\n\n  /**\n   * Subscribe to specific events, once.\n   */\n  once(events: string[] | string, callback: (data: T) => void): void;\n\n  /**\n   * Unsubscribe from events with a callback.\n   * @param callback The callback function to remove from the subscription.\n   */\n  off(callback: (data: T) => void): void;\n}\n\n/**\n * Interface for objects that support listeners via the subscribe/unsubscribe pattern.\n */\nexport interface Subscribable<T> {\n  /**\n   * Subscribe to events with a callback.\n   * @param callback The callback function to be called when events occur.\n   */\n  subscribe(callback: (data: T) => void): Promise<unknown>;\n\n  /**\n   * Subscribe to specific events, with a callback.\n   * @param events The events to subscribe to.\n   * @param callback The callback function to be called when events occur.\n   */\n  subscribe(events: string[] | string, callback: (data: T) => void): Promise<unknown>;\n\n  /**\n   * Unsubscribe from events with a callback.\n   * @param callback The callback function to remove from the subscription.\n   */\n  unsubscribe(callback: (data: T) => void): void;\n}\n\n/**\n * Subscribe to events on an emitter and return a cleanup function.\n *\n * Overload 1: Subscribe to all events.\n * Overload 2: Subscribe to specific events (string or string[]).\n * @param emitter The event emitter object that has `on` and `off` methods.\n * @param callback The callback function to be called when events occur.\n * @returns A cleanup function that when called will unsubscribe the callback.\n */\nexport function on<T>(emitter: Onable<T>, callback: (data: T) => void): () => void;\nexport function on<T>(emitter: Onable<T>, events: string | string[], callback: (data: T) => void): () => void;\n\n/**\n * @param emitter The event emitter object that has `on` and `off` methods.\n * @param arg2 Either a callback function (for subscribing to all events) or event names (string or string[]) to subscribe to specific events.\n * @param arg3 The callback function to be called when events occur (only used when arg2 is event names).\n * @returns A cleanup function that when called will unsubscribe the callback.\n * @throws {TypeError} If the arguments passed are invalid.\n * @example\n * ```typescript\n * // Subscribe to all events\n * const cleanup = on(emitter, (data) => console.log(data));\n * // Subscribe to specific events\n * const cleanup = on(emitter, 'eventName', (data) => console.log(data));\n * const cleanup = on(emitter, ['event1', 'event2'], (data) => console.log(data));\n * ```\n */\nexport function on<T>(\n  emitter: Onable<T>,\n  arg2: ((data: T) => void) | string | string[],\n  arg3?: (data: T) => void,\n): () => void {\n  if ((Array.isArray(arg2) || typeof arg2 === 'string') && arg3) {\n    emitter.on(arg2, arg3);\n    return () => {\n      emitter.off(arg3);\n    };\n  } else if (typeof arg2 === 'function') {\n    emitter.on(arg2);\n    return () => {\n      emitter.off(arg2);\n    };\n  } else {\n    throw new TypeError('Invalid arguments passed to on()');\n  }\n}\n\n/**\n * Subscribe to events on an emitter and return a cleanup function.\n *\n * Overload 1: Subscribe to all events once.\n * Overload 2: Subscribe to specific events (string or string[]) once.\n * @param emitter The event emitter object that has `once` and `off` methods.\n * @param callback The callback function to be called when events occur.\n * @returns A cleanup function that when called will unsubscribe the callback.\n * @example\n * ```typescript\n * // Subscribe to all events once\n * const cleanup = once(emitter, (data) => console.log(data));\n * // Subscribe to specific events once\n * const cleanup = once(emitter, 'eventName', (data) => console.log(data));\n * const cleanup = once(emitter, ['event1', 'event2'], (data) => console.log(data));\n * ```\n */\nexport function once<T>(emitter: Onable<T>, callback: (data: T) => void): () => void;\nexport function once<T>(emitter: Onable<T>, events: string | string[], callback: (data: T) => void): () => void;\n/**\n * @param emitter The event emitter object that has `once` and `off` methods.\n * @param arg2 Either a callback function (for subscribing to all events) or event names (string or string[]) to subscribe to specific events.\n * @param arg3 The callback function to be called when events occur (only used when arg2 is event names).\n * @returns A cleanup function that when called will unsubscribe the callback.\n */\nexport function once<T>(\n  emitter: Onable<T>,\n  arg2: ((data: T) => void) | string | string[],\n  arg3?: (data: T) => void,\n): () => void {\n  if ((Array.isArray(arg2) || typeof arg2 === 'string') && arg3) {\n    emitter.once(arg2, arg3);\n    return () => {\n      emitter.off(arg3);\n    };\n  } else if (typeof arg2 === 'function') {\n    emitter.once(arg2);\n    return () => {\n      emitter.off(arg2);\n    };\n  } else {\n    throw new TypeError('Invalid arguments passed to once()');\n  }\n}\n\n/**\n * Subscribe to events on an emitter and return a cleanup function.\n *\n * Overload 1: Subscribe to all events.\n * Overload 2: Subscribe to specific events (string or string[]).\n * @param emitter The event emitter object that has `subscribe` and `unsubscribe` methods.\n * @param callback The callback function to be called when events occur (only used when first parameter is event names).\n * @returns A cleanup function that when called will unsubscribe the callback.\n * @example\n * ```typescript\n * // Subscribe to all events\n * const unsubscribe = subscribe(emitter, (data) => console.log(data));\n *\n * // Subscribe to specific events\n * const unsubscribe = subscribe(emitter, 'eventName', (data) => console.log(data));\n * const unsubscribe = subscribe(emitter, ['event1', 'event2'], (data) => console.log(data));\n * ```\n */\nexport function subscribe<T>(emitter: Subscribable<T>, callback: (data: T) => void): () => void;\nexport function subscribe<T>(\n  emitter: Subscribable<T>,\n  events: string | string[],\n  callback: (data: T) => void,\n): () => void;\n/**\n * Subscribe to events on an emitter and return a cleanup function.\n * This is the implementation function that handles both overloads.\n * @param emitter The event emitter object that has `subscribe` and `unsubscribe` methods.\n * @param arg2 Either a callback function (for subscribing to all events) or event names (string or string[]) to subscribe to specific events.\n * @param arg3 The callback function to be called when events occur (only used when arg2 is event names).\n * @returns A cleanup function that when called will unsubscribe the callback.\n */\nexport function subscribe<T>(\n  emitter: Subscribable<T>,\n  arg2: ((data: T) => void) | string | string[],\n  arg3?: (data: T) => void,\n): () => void {\n  if ((Array.isArray(arg2) || typeof arg2 === 'string') && arg3) {\n    const subscribePromise = emitter.subscribe(arg2, arg3);\n    return () => {\n      subscribePromise\n        .then(() => {\n          emitter.unsubscribe(arg3);\n        })\n        .catch((error: unknown) => {\n          console.error('Error subscribing to events:', error);\n        });\n    };\n  } else if (typeof arg2 === 'function') {\n    const subscribePromise = emitter.subscribe(arg2);\n    return () => {\n      subscribePromise\n        .then(() => {\n          emitter.unsubscribe(arg2);\n        })\n        .catch((error: unknown) => {\n          console.error('Error subscribing to events:', error);\n        });\n    };\n  } else {\n    throw new TypeError('Invalid arguments passed to subscribe()');\n  }\n}\n","import * as Ably from 'ably';\n\n/**\n * This type represents a callback that can be registered with an EventEmitter.\n *\n * The EventsMap is an interface of event names to the types of the payloads of\n * those events. For example:\n *\n *     interface MyEvents {\n *         reaction: {emoji: string}\n *     }\n *\n * There is no need to use this type directly, it is used for defining the\n * InterfaceEventEmitter.\n */\ntype Callback<EventsMap> = (arg: EventsMap[keyof EventsMap]) => void;\ntype CallbackSingle<K> = (arg: K) => void;\n\n/**\n * This interface extends the Ably.EventEmitter interface to add a type-safe\n * emit method as well as convert an EventsMap into the type parameters used by\n * Ably.EventEmitter.\n */\ninterface InterfaceEventEmitter<EventsMap> extends Ably.EventEmitter<Callback<EventsMap>, void, keyof EventsMap> {\n  emit<K extends keyof EventsMap>(event: K, arg: EventsMap[K]): void;\n\n  on<K extends keyof EventsMap>(event: K, callback: CallbackSingle<EventsMap[K]>): void;\n  on<K1 extends keyof EventsMap, K2 extends keyof EventsMap>(\n    events: [K1, K2],\n    callback: CallbackSingle<EventsMap[K1] | EventsMap[K2]>,\n  ): void;\n  on<K1 extends keyof EventsMap, K2 extends keyof EventsMap, K3 extends keyof EventsMap>(\n    events: [K1, K2, K3],\n    callback: CallbackSingle<EventsMap[K1] | EventsMap[K2] | EventsMap[K3]>,\n  ): void;\n  on(events: (keyof EventsMap)[], callback: Callback<EventsMap>): void;\n  on(callback: Callback<EventsMap>): void;\n\n  off<K extends keyof EventsMap>(event: K, listener: CallbackSingle<EventsMap[K]>): void;\n  off(listener?: Callback<EventsMap>): void;\n  off<K extends EventsMap[keyof EventsMap]>(listener: CallbackSingle<K>): void;\n}\n\n/**\n * This is a workaround for the fact that the EventEmitter constructor is only\n * exported from the ably-js package for internal use by other Ably SDKs (like\n * this one).\n *\n * It is a correctly-typed constructor for the ably-js EventEmitter.\n *\n * We do not export this directly because we prefer to export a class, which is\n * what we normally expect EventEmitter to be.\n */\nconst InternalEventEmitter: new <EventsMap>() => InterfaceEventEmitter<EventsMap> = (\n  Ably.Realtime as unknown as { EventEmitter: new <EventsMap>() => InterfaceEventEmitter<EventsMap> }\n).EventEmitter;\n\n/**\n * EventEmitter class based on the internal ably-js EventEmitter. It is\n * different from the ably-js EventEmitter because it takes an EventsMap type\n * parameter as opposed to the three type parameters required by\n * {@link Ably.EventEmitter}.\n *\n * We find the EventsMap type parameter to be more convenient to use in this\n * Chat SDK.\n *\n * The EventsMap is an interface of event names to the types of the payloads of\n * those events. For example:\n *\n *     interface MyEvents {\n *         reaction: {emoji: string}\n *     }\n *\n * There is no need to use this type directly, it is used for defining the\n * InterfaceEventEmitter.\n */\nclass EventEmitter<EventsMap> extends InternalEventEmitter<EventsMap> {}\n\nexport default EventEmitter;\n\n/**\n * Creates a wrapper function that forwards all arguments to the provided function.\n * @param fn The function to wrap\n * @returns A new function with the same signature as the input function\n */\nexport const wrap =\n  <Args extends unknown[], Return>(fn: (...args: Args) => Return): ((...args: Args) => Return) =>\n  (...args: Args) =>\n    fn(...args);\n\n/**\n * Checks if an EventEmitter has any listeners registered.\n * @param emitter The EventEmitter instance to check\n * @returns true if the emitter has listeners, false otherwise\n */\nexport const emitterHasListeners = <EventsMap>(emitter: EventEmitter<EventsMap>): boolean => {\n  const destructured = emitter as unknown as {\n    events: Record<string, unknown[]>;\n    any: unknown[];\n    eventsOnce: Record<string, unknown[]>;\n    anyOnce: unknown[];\n  };\n\n  const numListeners =\n    Object.values(destructured.events).flat().length +\n    destructured.any.length +\n    Object.values(destructured.eventsOnce).flat().length +\n    destructured.anyOnce.length;\n\n  return numListeners ? numListeners > 0 : false;\n};\n","import * as Ably from 'ably';\n\nimport { Logger } from './logger.js';\nimport { on } from './realtime-subscriptions.js';\nimport { StatusSubscription } from './subscription.js';\nimport EventEmitter, { emitterHasListeners, wrap } from './utils/event-emitter.js';\n\n/**\n * The different states that the connection can be in through its lifecycle.\n */\nexport enum ConnectionStatus {\n  /**\n   * A temporary state for when the library is first initialized.\n   */\n  Initialized = 'initialized',\n\n  /**\n   * The library is currently connecting to Ably.\n   */\n  Connecting = 'connecting',\n\n  /**\n   * The library is currently connected to Ably.\n   */\n  Connected = 'connected',\n\n  /**\n   * The library is currently disconnected from Ably, but will attempt to reconnect.\n   */\n  Disconnected = 'disconnected',\n\n  /**\n   * The library is in an extended state of disconnection, but will attempt to reconnect.\n   */\n  Suspended = 'suspended',\n\n  /**\n   * The library is currently disconnected from Ably and will not attempt to reconnect.\n   */\n  Failed = 'failed',\n\n  /**\n   * An explicit request by the developer to close the connection has been sent to the Ably service.\n   * If a reply is not received from Ably within a short period of time, the connection is forcibly\n   * terminated and the connection status becomes Closed.\n   */\n  Closing = 'closing',\n\n  /**\n   * The connection has been explicitly closed by the client. In the closed state, no reconnection\n   * attempts are made automatically. No connection state is preserved by the service or the library.\n   */\n  Closed = 'closed',\n}\n\n/**\n * Represents a change in the status of the connection.\n */\nexport interface ConnectionStatusChange {\n  /**\n   * The new status of the connection.\n   */\n  current: ConnectionStatus;\n\n  /**\n   * The previous status of the connection.\n   */\n  previous: ConnectionStatus;\n\n  /**\n   * An error that provides a reason why the connection has\n   * entered the new status, if applicable.\n   */\n  error?: Ably.ErrorInfo;\n\n  /**\n   * The time in milliseconds that the client will wait before attempting to reconnect.\n   */\n  retryIn?: number;\n}\n\n/**\n * A function that can be called when the connection status changes.\n * @param change The change in status.\n */\nexport type ConnectionStatusListener = (change: ConnectionStatusChange) => void;\n\n/**\n * Represents a connection to Ably.\n */\nexport interface Connection {\n  /**\n   * The current status of the connection.\n   */\n  get status(): ConnectionStatus;\n\n  /**\n   * The current error, if any, that caused the connection to enter the current status.\n   */\n  get error(): Ably.ErrorInfo | undefined;\n\n  /**\n   * Registers a listener that will be called whenever the connection status changes.\n   * @param listener The function to call when the status changes.\n   * @returns An object that can be used to unregister the listener.\n   */\n  onStatusChange(listener: ConnectionStatusListener): StatusSubscription;\n}\n\n/**\n * An internal interface for the connection with additional methods.\n */\nexport interface InternalConnection extends Connection {\n  /**\n   * Disposes of the connection instance, cleaning up any registered listeners.\n   * This method should be called when the connection is no longer needed.\n   * @internal\n   */\n  dispose(): void;\n}\n\ntype ConnectionEventsMap = Record<ConnectionStatus, ConnectionStatusChange>;\n\n/**\n * An implementation of the `Connection` interface.\n * @internal\n */\nexport class DefaultConnection implements InternalConnection {\n  private _status: ConnectionStatus = ConnectionStatus.Initialized;\n  private _error?: Ably.ErrorInfo;\n  private readonly _logger: Logger;\n  private _emitter = new EventEmitter<ConnectionEventsMap>();\n  private readonly _clearAblyConnectionListener: () => void;\n\n  /**\n   * Constructs a new `DefaultConnection` instance.\n   * @param ably The Ably Realtime client.\n   * @param logger The logger to use.\n   */\n  constructor(ably: Ably.Realtime, logger: Logger) {\n    this._logger = logger;\n\n    // Set our initial status and error\n    // CHA-RS5\n    this._status = this._mapAblyStatusToChat(ably.connection.state);\n    this._error = ably.connection.errorReason;\n\n    // Store the listener function so we can dispose of it later\n    const connectionListener = (change: Ably.ConnectionStateChange) => {\n      const chatState = this._mapAblyStatusToChat(change.current);\n      if (chatState === this._status) {\n        return;\n      }\n\n      const stateChange: ConnectionStatusChange = {\n        current: chatState,\n        previous: this._status,\n        error: change.reason,\n        retryIn: change.retryIn,\n      };\n\n      this._applyStatusChange(stateChange);\n    };\n\n    // Use subscription helper to create cleanup function\n    this._clearAblyConnectionListener = on(ably.connection, connectionListener);\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get status(): ConnectionStatus {\n    return this._status;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get error(): Ably.ErrorInfo | undefined {\n    return this._error;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  onStatusChange(listener: ConnectionStatusListener): StatusSubscription {\n    const wrapped = wrap(listener);\n    this._emitter.on(wrapped);\n\n    return {\n      off: () => {\n        this._emitter.off(wrapped);\n      },\n    };\n  }\n\n  /**\n   * @inheritdoc\n   */\n  dispose(): void {\n    this._logger.trace('DefaultConnection.dispose();');\n    // Remove the connection state listener from the Ably connection\n    this._clearAblyConnectionListener();\n    // Clear all listeners from the internal emitter\n    this._emitter.off();\n  }\n\n  /**\n   * Checks if there are any listeners registered on the connection.\n   * @internal\n   * @returns true if there are listeners, false otherwise.\n   */\n  hasListeners(): boolean {\n    return emitterHasListeners(this._emitter);\n  }\n\n  private _applyStatusChange(change: ConnectionStatusChange): void {\n    this._status = change.current;\n    this._error = change.error;\n    this._logger.info(`Connection state changed`, change);\n    this._emitter.emit(change.current, change);\n  }\n\n  /**\n   * Maps an Ably connection state to a connection status.\n   * @param status The Ably connection state to map.\n   * @returns The corresponding connection status.\n   */\n  private _mapAblyStatusToChat(status: Ably.ConnectionState): ConnectionStatus {\n    switch (status) {\n      case 'initialized': {\n        return ConnectionStatus.Initialized;\n      }\n      case 'connecting': {\n        return ConnectionStatus.Connecting;\n      }\n      case 'connected': {\n        return ConnectionStatus.Connected;\n      }\n      case 'disconnected': {\n        return ConnectionStatus.Disconnected;\n      }\n      case 'suspended': {\n        return ConnectionStatus.Suspended;\n      }\n      case 'closing': {\n        return ConnectionStatus.Closing;\n      }\n      case 'closed': {\n        return ConnectionStatus.Closed;\n      }\n      case 'failed': {\n        return ConnectionStatus.Failed;\n      }\n      default: {\n        this._logger.error('DefaultConnection._mapAblyStatusToChat(); unknown connection state', {\n          status,\n        });\n        return ConnectionStatus.Failed;\n      }\n    }\n  }\n}\n","/**\n * Generates a random string that can be used as an identifier, for instance, in identifying specific room\n * objects.\n * @returns A random string that can be used as an identifier.\n */\nexport const randomId = (): string => Math.random().toString(36).slice(2);\n","var has = Object.prototype.hasOwnProperty;\n\nfunction find(iter, tar, key) {\n\tfor (key of iter.keys()) {\n\t\tif (dequal(key, tar)) return key;\n\t}\n}\n\nexport function dequal(foo, bar) {\n\tvar ctor, len, tmp;\n\tif (foo === bar) return true;\n\n\tif (foo && bar && (ctor=foo.constructor) === bar.constructor) {\n\t\tif (ctor === Date) return foo.getTime() === bar.getTime();\n\t\tif (ctor === RegExp) return foo.toString() === bar.toString();\n\n\t\tif (ctor === Array) {\n\t\t\tif ((len=foo.length) === bar.length) {\n\t\t\t\twhile (len-- && dequal(foo[len], bar[len]));\n\t\t\t}\n\t\t\treturn len === -1;\n\t\t}\n\n\t\tif (ctor === Set) {\n\t\t\tif (foo.size !== bar.size) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfor (len of foo) {\n\t\t\t\ttmp = len;\n\t\t\t\tif (tmp && typeof tmp === 'object') {\n\t\t\t\t\ttmp = find(bar, tmp);\n\t\t\t\t\tif (!tmp) return false;\n\t\t\t\t}\n\t\t\t\tif (!bar.has(tmp)) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tif (ctor === Map) {\n\t\t\tif (foo.size !== bar.size) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfor (len of foo) {\n\t\t\t\ttmp = len[0];\n\t\t\t\tif (tmp && typeof tmp === 'object') {\n\t\t\t\t\ttmp = find(bar, tmp);\n\t\t\t\t\tif (!tmp) return false;\n\t\t\t\t}\n\t\t\t\tif (!dequal(len[1], bar.get(tmp))) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tif (ctor === ArrayBuffer) {\n\t\t\tfoo = new Uint8Array(foo);\n\t\t\tbar = new Uint8Array(bar);\n\t\t} else if (ctor === DataView) {\n\t\t\tif ((len=foo.byteLength) === bar.byteLength) {\n\t\t\t\twhile (len-- && foo.getInt8(len) === bar.getInt8(len));\n\t\t\t}\n\t\t\treturn len === -1;\n\t\t}\n\n\t\tif (ArrayBuffer.isView(foo)) {\n\t\t\tif ((len=foo.byteLength) === bar.byteLength) {\n\t\t\t\twhile (len-- && foo[len] === bar[len]);\n\t\t\t}\n\t\t\treturn len === -1;\n\t\t}\n\n\t\tif (!ctor || typeof foo === 'object') {\n\t\t\tlen = 0;\n\t\t\tfor (ctor in foo) {\n\t\t\t\tif (has.call(foo, ctor) && ++len && !has.call(bar, ctor)) return false;\n\t\t\t\tif (!(ctor in bar) || !dequal(foo[ctor], bar[ctor])) return false;\n\t\t\t}\n\t\t\treturn Object.keys(bar).length === len;\n\t\t}\n\t}\n\n\treturn foo !== foo && bar !== bar;\n}\n","import type { Message, MessageReactionSummary } from './message.js';\nimport type { OccupancyData } from './occupancy-parser.js';\nimport type { RoomReaction } from './room-reaction.js';\n\n/**\n * All chat message events.\n */\nexport enum ChatMessageEventType {\n  /** Fires when a new chat message is received. */\n  Created = 'message.created',\n\n  /** Fires when a chat message is updated. */\n  Updated = 'message.updated',\n\n  /** Fires when a chat message is deleted. */\n  Deleted = 'message.deleted',\n}\n\n/**\n * Realtime chat message names.\n */\nexport enum RealtimeMessageName {\n  /** Represents a regular chat message. */\n  ChatMessage = 'chat.message',\n}\n\n/**\n * Realtime meta event types.\n */\nexport enum RealtimeMetaEventType {\n  /** Represents a meta occupancy event. */\n  Occupancy = '[meta]occupancy',\n}\n\n/**\n * Chat Message Actions.\n */\nexport enum ChatMessageAction {\n  /** Action applied to a new message. */\n  MessageCreate = 'message.create',\n\n  /** Action applied to an updated message. */\n  MessageUpdate = 'message.update',\n\n  /** Action applied to a deleted message. */\n  MessageDelete = 'message.delete',\n}\n\n/**\n * Enum representing presence events.\n */\nexport enum PresenceEventType {\n  /**\n   * Event triggered when a user enters.\n   */\n  Enter = 'enter',\n\n  /**\n   * Event triggered when a user leaves.\n   */\n  Leave = 'leave',\n\n  /**\n   * Event triggered when a user updates their presence data.\n   */\n  Update = 'update',\n  /**\n   * Event triggered when a user initially subscribes to presence.\n   */\n  Present = 'present',\n}\n\n/**\n * Enum representing the typing event types.\n */\nexport enum TypingEventType {\n  /**\n   * Event triggered when a user is typing.\n   */\n  Started = 'typing.started',\n\n  /**\n   * Event triggered when a user stops typing.\n   */\n  Stopped = 'typing.stopped',\n}\n\n/**\n * Enum representing the typing set event types.\n */\nexport enum TypingSetEventType {\n  /**\n   * Event triggered when a change occurs in the set of typers.\n   */\n  SetChanged = 'typing.set.changed',\n}\n\n/**\n * Represents a change in the state of current typers.\n */\nexport interface TypingSetEvent {\n  /**\n   * The type of the event.\n   */\n  type: TypingSetEventType;\n\n  /**\n   * The set of clientIds that are currently typing.\n   */\n  currentlyTyping: Set<string>;\n\n  /**\n   * Represents the change that resulted in the new set of typers.\n   */\n  change: {\n    /**\n     * The client ID of the user who stopped/started typing.\n     */\n    clientId: string;\n\n    /**\n     * Type of the change.\n     */\n    type: TypingEventType;\n  };\n}\n\n/**\n * Room reaction events. This is used for the realtime system since room reactions\n * have only one event: \"roomReaction\".\n */\nexport enum RoomReactionRealtimeEventType {\n  /**\n   * Event triggered when a room reaction was received.\n   */\n  Reaction = 'roomReaction',\n}\n\n/**\n * The type of room reaction events.\n */\nexport enum RoomReactionEventType {\n  /**\n   * Event triggered when a room reaction was received.\n   */\n  Reaction = 'reaction',\n}\n\n/**\n * Event that is emitted when a room reaction is received.\n */\nexport interface RoomReactionEvent {\n  /**\n   * The type of the event.\n   */\n  readonly type: RoomReactionEventType;\n\n  /**\n   * The reaction that was received.\n   */\n  readonly reaction: RoomReaction;\n}\n\n/**\n * Payload for a message event.\n */\nexport interface ChatMessageEvent {\n  /**\n   * The type of the message event.\n   */\n  type: ChatMessageEventType;\n\n  /**\n   * The message that was received.\n   */\n  message: Message;\n}\n\n/**\n * All annotation types supported by Chat Message Reactions.\n */\nexport enum MessageReactionType {\n  /**\n   * Allows for at most one reaction per client per message. If a client reacts\n   * to a message a second time, only the second reaction is counted in the\n   * summary.\n   *\n   * This is similar to reactions on iMessage, Facebook Messenger or WhatsApp.\n   */\n  Unique = 'unique',\n\n  /**\n   * Allows for at most one reaction of each type per client per message. It is\n   * possible for a client to add multiple reactions to the same message as\n   * long as they are different (eg different emojis). Duplicates are not\n   * counted in the summary.\n   *\n   * This is similar to reactions on Slack.\n   */\n  Distinct = 'distinct',\n\n  /**\n   * Allows any number of reactions, including repeats, and they are counted in\n   * the summary. The reaction payload also includes a count of how many times\n   * each reaction should be counted (defaults to 1 if not set).\n   *\n   * This is similar to the clap feature on Medium or how room reactions work.\n   */\n  Multiple = 'multiple',\n}\n\n/**\n * Enum representing the different annotation types used for message reactions.\n */\nexport enum ReactionAnnotationType {\n  Unique = 'reaction:unique.v1',\n  Distinct = 'reaction:distinct.v1',\n  Multiple = 'reaction:multiple.v1',\n}\n\n/**\n * Maps Ably PubSub annotation types to Ably Chat message reaction types.\n *\n * The key type is string because we use it to lookup by PubSub event.type, which is a string.\n */\nexport const AnnotationTypeToReactionType: Record<string, MessageReactionType> = {\n  [ReactionAnnotationType.Unique]: MessageReactionType.Unique,\n  [ReactionAnnotationType.Distinct]: MessageReactionType.Distinct,\n  [ReactionAnnotationType.Multiple]: MessageReactionType.Multiple,\n} as const;\n\n/**\n * Enum representing different raw message reaction events in the chat system.\n */\nexport enum MessageReactionRawEventType {\n  /**\n   * A reaction was added to a message.\n   */\n  Create = 'reaction.create',\n  /**\n   * A reaction was removed from a message.\n   */\n  Delete = 'reaction.delete',\n}\n\n/**\n * Enum representing different message reaction summary events in the chat system.\n */\nexport enum MessageReactionSummaryEventType {\n  /**\n   * A reactions summary was updated for a message.\n   */\n  Summary = 'reaction.summary',\n}\n\n/**\n * Represents an individual message reaction event.\n */\nexport interface MessageReactionRawEvent {\n  /** Whether reaction was added or removed */\n  type: MessageReactionRawEventType;\n\n  /** The timestamp of this event */\n  timestamp: Date;\n\n  /** The message reaction that was received. */\n  reaction: {\n    /** Serial of the message this reaction is for */\n    messageSerial: string;\n\n    /** Type of reaction */\n    type: MessageReactionType;\n\n    /** The reaction name (typically an emoji) */\n    name: string;\n\n    /** Count of the reaction (only for type Multiple, if set) */\n    count?: number;\n\n    /** The client ID of the user who added/removed the reaction */\n    clientId: string;\n  };\n}\n\n/**\n * Event interface representing a summary of message reactions.\n * This event aggregates different types of reactions (single, distinct, counter) for a specific message.\n */\nexport interface MessageReactionSummaryEvent {\n  /** The type of the event */\n  type: MessageReactionSummaryEventType;\n\n  /** Reference to the original message's serial */\n  messageSerial: string;\n\n  /** The message reactions summary. */\n  reactions: MessageReactionSummary;\n}\n\n/**\n * Enum representing occupancy events.\n */\nexport enum OccupancyEventType {\n  /**\n   * Event triggered when occupancy is updated.\n   */\n  Updated = 'occupancy.updated',\n}\n\n/**\n * Represents an occupancy event.\n */\nexport interface OccupancyEvent {\n  /**\n   * The type of the occupancy event.\n   */\n  type: OccupancyEventType;\n\n  /**\n   * The occupancy data.\n   */\n  occupancy: OccupancyData;\n}\n\n/**\n * Room events.\n */\nexport enum RoomEventType {\n  /**\n   * Event triggered when a discontinuity is detected in the room's channel connection.\n   * A discontinuity occurs when an attached or update event comes from the channel with resume=false,\n   * except for the first attach or attaches after explicit detach calls.\n   */\n  Discontinuity = 'room.discontinuity',\n}\n","/**\n * lodash (Custom Build) <https://lodash.com/>\n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright jQuery Foundation and other contributors <https://jquery.org/>\n * Released under MIT license <https://lodash.com/license>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the size to enable large array optimizations. */\nvar LARGE_ARRAY_SIZE = 200;\n\n/** Used to stand-in for `undefined` hash values. */\nvar HASH_UNDEFINED = '__lodash_hash_undefined__';\n\n/** Used as references for various `Number` constants. */\nvar MAX_SAFE_INTEGER = 9007199254740991;\n\n/** `Object#toString` result references. */\nvar argsTag = '[object Arguments]',\n    arrayTag = '[object Array]',\n    boolTag = '[object Boolean]',\n    dateTag = '[object Date]',\n    errorTag = '[object Error]',\n    funcTag = '[object Function]',\n    genTag = '[object GeneratorFunction]',\n    mapTag = '[object Map]',\n    numberTag = '[object Number]',\n    objectTag = '[object Object]',\n    promiseTag = '[object Promise]',\n    regexpTag = '[object RegExp]',\n    setTag = '[object Set]',\n    stringTag = '[object String]',\n    symbolTag = '[object Symbol]',\n    weakMapTag = '[object WeakMap]';\n\nvar arrayBufferTag = '[object ArrayBuffer]',\n    dataViewTag = '[object DataView]',\n    float32Tag = '[object Float32Array]',\n    float64Tag = '[object Float64Array]',\n    int8Tag = '[object Int8Array]',\n    int16Tag = '[object Int16Array]',\n    int32Tag = '[object Int32Array]',\n    uint8Tag = '[object Uint8Array]',\n    uint8ClampedTag = '[object Uint8ClampedArray]',\n    uint16Tag = '[object Uint16Array]',\n    uint32Tag = '[object Uint32Array]';\n\n/**\n * Used to match `RegExp`\n * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).\n */\nvar reRegExpChar = /[\\\\^$.*+?()[\\]{}|]/g;\n\n/** Used to match `RegExp` flags from their coerced string values. */\nvar reFlags = /\\w*$/;\n\n/** Used to detect host constructors (Safari). */\nvar reIsHostCtor = /^\\[object .+?Constructor\\]$/;\n\n/** Used to detect unsigned integer values. */\nvar reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n/** Used to identify `toStringTag` values supported by `_.clone`. */\nvar cloneableTags = {};\ncloneableTags[argsTag] = cloneableTags[arrayTag] =\ncloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =\ncloneableTags[boolTag] = cloneableTags[dateTag] =\ncloneableTags[float32Tag] = cloneableTags[float64Tag] =\ncloneableTags[int8Tag] = cloneableTags[int16Tag] =\ncloneableTags[int32Tag] = cloneableTags[mapTag] =\ncloneableTags[numberTag] = cloneableTags[objectTag] =\ncloneableTags[regexpTag] = cloneableTags[setTag] =\ncloneableTags[stringTag] = cloneableTags[symbolTag] =\ncloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =\ncloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;\ncloneableTags[errorTag] = cloneableTags[funcTag] =\ncloneableTags[weakMapTag] = false;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/** Detect free variable `exports`. */\nvar freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;\n\n/** Detect free variable `module`. */\nvar freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;\n\n/** Detect the popular CommonJS extension `module.exports`. */\nvar moduleExports = freeModule && freeModule.exports === freeExports;\n\n/**\n * Adds the key-value `pair` to `map`.\n *\n * @private\n * @param {Object} map The map to modify.\n * @param {Array} pair The key-value pair to add.\n * @returns {Object} Returns `map`.\n */\nfunction addMapEntry(map, pair) {\n  // Don't return `map.set` because it's not chainable in IE 11.\n  map.set(pair[0], pair[1]);\n  return map;\n}\n\n/**\n * Adds `value` to `set`.\n *\n * @private\n * @param {Object} set The set to modify.\n * @param {*} value The value to add.\n * @returns {Object} Returns `set`.\n */\nfunction addSetEntry(set, value) {\n  // Don't return `set.add` because it's not chainable in IE 11.\n  set.add(value);\n  return set;\n}\n\n/**\n * A specialized version of `_.forEach` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns `array`.\n */\nfunction arrayEach(array, iteratee) {\n  var index = -1,\n      length = array ? array.length : 0;\n\n  while (++index < length) {\n    if (iteratee(array[index], index, array) === false) {\n      break;\n    }\n  }\n  return array;\n}\n\n/**\n * Appends the elements of `values` to `array`.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to append.\n * @returns {Array} Returns `array`.\n */\nfunction arrayPush(array, values) {\n  var index = -1,\n      length = values.length,\n      offset = array.length;\n\n  while (++index < length) {\n    array[offset + index] = values[index];\n  }\n  return array;\n}\n\n/**\n * A specialized version of `_.reduce` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @param {boolean} [initAccum] Specify using the first element of `array` as\n *  the initial value.\n * @returns {*} Returns the accumulated value.\n */\nfunction arrayReduce(array, iteratee, accumulator, initAccum) {\n  var index = -1,\n      length = array ? array.length : 0;\n\n  if (initAccum && length) {\n    accumulator = array[++index];\n  }\n  while (++index < length) {\n    accumulator = iteratee(accumulator, array[index], index, array);\n  }\n  return accumulator;\n}\n\n/**\n * The base implementation of `_.times` without support for iteratee shorthands\n * or max array length checks.\n *\n * @private\n * @param {number} n The number of times to invoke `iteratee`.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the array of results.\n */\nfunction baseTimes(n, iteratee) {\n  var index = -1,\n      result = Array(n);\n\n  while (++index < n) {\n    result[index] = iteratee(index);\n  }\n  return result;\n}\n\n/**\n * Gets the value at `key` of `object`.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\nfunction getValue(object, key) {\n  return object == null ? undefined : object[key];\n}\n\n/**\n * Checks if `value` is a host object in IE < 9.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a host object, else `false`.\n */\nfunction isHostObject(value) {\n  // Many host objects are `Object` objects that can coerce to strings\n  // despite having improperly defined `toString` methods.\n  var result = false;\n  if (value != null && typeof value.toString != 'function') {\n    try {\n      result = !!(value + '');\n    } catch (e) {}\n  }\n  return result;\n}\n\n/**\n * Converts `map` to its key-value pairs.\n *\n * @private\n * @param {Object} map The map to convert.\n * @returns {Array} Returns the key-value pairs.\n */\nfunction mapToArray(map) {\n  var index = -1,\n      result = Array(map.size);\n\n  map.forEach(function(value, key) {\n    result[++index] = [key, value];\n  });\n  return result;\n}\n\n/**\n * Creates a unary function that invokes `func` with its argument transformed.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {Function} transform The argument transform.\n * @returns {Function} Returns the new function.\n */\nfunction overArg(func, transform) {\n  return function(arg) {\n    return func(transform(arg));\n  };\n}\n\n/**\n * Converts `set` to an array of its values.\n *\n * @private\n * @param {Object} set The set to convert.\n * @returns {Array} Returns the values.\n */\nfunction setToArray(set) {\n  var index = -1,\n      result = Array(set.size);\n\n  set.forEach(function(value) {\n    result[++index] = value;\n  });\n  return result;\n}\n\n/** Used for built-in method references. */\nvar arrayProto = Array.prototype,\n    funcProto = Function.prototype,\n    objectProto = Object.prototype;\n\n/** Used to detect overreaching core-js shims. */\nvar coreJsData = root['__core-js_shared__'];\n\n/** Used to detect methods masquerading as native. */\nvar maskSrcKey = (function() {\n  var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');\n  return uid ? ('Symbol(src)_1.' + uid) : '';\n}());\n\n/** Used to resolve the decompiled source of functions. */\nvar funcToString = funcProto.toString;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar objectToString = objectProto.toString;\n\n/** Used to detect if a method is native. */\nvar reIsNative = RegExp('^' +\n  funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\\\$&')\n  .replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') + '$'\n);\n\n/** Built-in value references. */\nvar Buffer = moduleExports ? root.Buffer : undefined,\n    Symbol = root.Symbol,\n    Uint8Array = root.Uint8Array,\n    getPrototype = overArg(Object.getPrototypeOf, Object),\n    objectCreate = Object.create,\n    propertyIsEnumerable = objectProto.propertyIsEnumerable,\n    splice = arrayProto.splice;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeGetSymbols = Object.getOwnPropertySymbols,\n    nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,\n    nativeKeys = overArg(Object.keys, Object);\n\n/* Built-in method references that are verified to be native. */\nvar DataView = getNative(root, 'DataView'),\n    Map = getNative(root, 'Map'),\n    Promise = getNative(root, 'Promise'),\n    Set = getNative(root, 'Set'),\n    WeakMap = getNative(root, 'WeakMap'),\n    nativeCreate = getNative(Object, 'create');\n\n/** Used to detect maps, sets, and weakmaps. */\nvar dataViewCtorString = toSource(DataView),\n    mapCtorString = toSource(Map),\n    promiseCtorString = toSource(Promise),\n    setCtorString = toSource(Set),\n    weakMapCtorString = toSource(WeakMap);\n\n/** Used to convert symbols to primitives and strings. */\nvar symbolProto = Symbol ? Symbol.prototype : undefined,\n    symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;\n\n/**\n * Creates a hash object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Hash(entries) {\n  var index = -1,\n      length = entries ? entries.length : 0;\n\n  this.clear();\n  while (++index < length) {\n    var entry = entries[index];\n    this.set(entry[0], entry[1]);\n  }\n}\n\n/**\n * Removes all key-value entries from the hash.\n *\n * @private\n * @name clear\n * @memberOf Hash\n */\nfunction hashClear() {\n  this.__data__ = nativeCreate ? nativeCreate(null) : {};\n}\n\n/**\n * Removes `key` and its value from the hash.\n *\n * @private\n * @name delete\n * @memberOf Hash\n * @param {Object} hash The hash to modify.\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction hashDelete(key) {\n  return this.has(key) && delete this.__data__[key];\n}\n\n/**\n * Gets the hash value for `key`.\n *\n * @private\n * @name get\n * @memberOf Hash\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction hashGet(key) {\n  var data = this.__data__;\n  if (nativeCreate) {\n    var result = data[key];\n    return result === HASH_UNDEFINED ? undefined : result;\n  }\n  return hasOwnProperty.call(data, key) ? data[key] : undefined;\n}\n\n/**\n * Checks if a hash value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Hash\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction hashHas(key) {\n  var data = this.__data__;\n  return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);\n}\n\n/**\n * Sets the hash `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Hash\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the hash instance.\n */\nfunction hashSet(key, value) {\n  var data = this.__data__;\n  data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;\n  return this;\n}\n\n// Add methods to `Hash`.\nHash.prototype.clear = hashClear;\nHash.prototype['delete'] = hashDelete;\nHash.prototype.get = hashGet;\nHash.prototype.has = hashHas;\nHash.prototype.set = hashSet;\n\n/**\n * Creates an list cache object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction ListCache(entries) {\n  var index = -1,\n      length = entries ? entries.length : 0;\n\n  this.clear();\n  while (++index < length) {\n    var entry = entries[index];\n    this.set(entry[0], entry[1]);\n  }\n}\n\n/**\n * Removes all key-value entries from the list cache.\n *\n * @private\n * @name clear\n * @memberOf ListCache\n */\nfunction listCacheClear() {\n  this.__data__ = [];\n}\n\n/**\n * Removes `key` and its value from the list cache.\n *\n * @private\n * @name delete\n * @memberOf ListCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction listCacheDelete(key) {\n  var data = this.__data__,\n      index = assocIndexOf(data, key);\n\n  if (index < 0) {\n    return false;\n  }\n  var lastIndex = data.length - 1;\n  if (index == lastIndex) {\n    data.pop();\n  } else {\n    splice.call(data, index, 1);\n  }\n  return true;\n}\n\n/**\n * Gets the list cache value for `key`.\n *\n * @private\n * @name get\n * @memberOf ListCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction listCacheGet(key) {\n  var data = this.__data__,\n      index = assocIndexOf(data, key);\n\n  return index < 0 ? undefined : data[index][1];\n}\n\n/**\n * Checks if a list cache value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf ListCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction listCacheHas(key) {\n  return assocIndexOf(this.__data__, key) > -1;\n}\n\n/**\n * Sets the list cache `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf ListCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the list cache instance.\n */\nfunction listCacheSet(key, value) {\n  var data = this.__data__,\n      index = assocIndexOf(data, key);\n\n  if (index < 0) {\n    data.push([key, value]);\n  } else {\n    data[index][1] = value;\n  }\n  return this;\n}\n\n// Add methods to `ListCache`.\nListCache.prototype.clear = listCacheClear;\nListCache.prototype['delete'] = listCacheDelete;\nListCache.prototype.get = listCacheGet;\nListCache.prototype.has = listCacheHas;\nListCache.prototype.set = listCacheSet;\n\n/**\n * Creates a map cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction MapCache(entries) {\n  var index = -1,\n      length = entries ? entries.length : 0;\n\n  this.clear();\n  while (++index < length) {\n    var entry = entries[index];\n    this.set(entry[0], entry[1]);\n  }\n}\n\n/**\n * Removes all key-value entries from the map.\n *\n * @private\n * @name clear\n * @memberOf MapCache\n */\nfunction mapCacheClear() {\n  this.__data__ = {\n    'hash': new Hash,\n    'map': new (Map || ListCache),\n    'string': new Hash\n  };\n}\n\n/**\n * Removes `key` and its value from the map.\n *\n * @private\n * @name delete\n * @memberOf MapCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction mapCacheDelete(key) {\n  return getMapData(this, key)['delete'](key);\n}\n\n/**\n * Gets the map value for `key`.\n *\n * @private\n * @name get\n * @memberOf MapCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction mapCacheGet(key) {\n  return getMapData(this, key).get(key);\n}\n\n/**\n * Checks if a map value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf MapCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction mapCacheHas(key) {\n  return getMapData(this, key).has(key);\n}\n\n/**\n * Sets the map `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf MapCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the map cache instance.\n */\nfunction mapCacheSet(key, value) {\n  getMapData(this, key).set(key, value);\n  return this;\n}\n\n// Add methods to `MapCache`.\nMapCache.prototype.clear = mapCacheClear;\nMapCache.prototype['delete'] = mapCacheDelete;\nMapCache.prototype.get = mapCacheGet;\nMapCache.prototype.has = mapCacheHas;\nMapCache.prototype.set = mapCacheSet;\n\n/**\n * Creates a stack cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Stack(entries) {\n  this.__data__ = new ListCache(entries);\n}\n\n/**\n * Removes all key-value entries from the stack.\n *\n * @private\n * @name clear\n * @memberOf Stack\n */\nfunction stackClear() {\n  this.__data__ = new ListCache;\n}\n\n/**\n * Removes `key` and its value from the stack.\n *\n * @private\n * @name delete\n * @memberOf Stack\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction stackDelete(key) {\n  return this.__data__['delete'](key);\n}\n\n/**\n * Gets the stack value for `key`.\n *\n * @private\n * @name get\n * @memberOf Stack\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction stackGet(key) {\n  return this.__data__.get(key);\n}\n\n/**\n * Checks if a stack value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Stack\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction stackHas(key) {\n  return this.__data__.has(key);\n}\n\n/**\n * Sets the stack `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Stack\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the stack cache instance.\n */\nfunction stackSet(key, value) {\n  var cache = this.__data__;\n  if (cache instanceof ListCache) {\n    var pairs = cache.__data__;\n    if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {\n      pairs.push([key, value]);\n      return this;\n    }\n    cache = this.__data__ = new MapCache(pairs);\n  }\n  cache.set(key, value);\n  return this;\n}\n\n// Add methods to `Stack`.\nStack.prototype.clear = stackClear;\nStack.prototype['delete'] = stackDelete;\nStack.prototype.get = stackGet;\nStack.prototype.has = stackHas;\nStack.prototype.set = stackSet;\n\n/**\n * Creates an array of the enumerable property names of the array-like `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @param {boolean} inherited Specify returning inherited property names.\n * @returns {Array} Returns the array of property names.\n */\nfunction arrayLikeKeys(value, inherited) {\n  // Safari 8.1 makes `arguments.callee` enumerable in strict mode.\n  // Safari 9 makes `arguments.length` enumerable in strict mode.\n  var result = (isArray(value) || isArguments(value))\n    ? baseTimes(value.length, String)\n    : [];\n\n  var length = result.length,\n      skipIndexes = !!length;\n\n  for (var key in value) {\n    if ((inherited || hasOwnProperty.call(value, key)) &&\n        !(skipIndexes && (key == 'length' || isIndex(key, length)))) {\n      result.push(key);\n    }\n  }\n  return result;\n}\n\n/**\n * Assigns `value` to `key` of `object` if the existing value is not equivalent\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\nfunction assignValue(object, key, value) {\n  var objValue = object[key];\n  if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||\n      (value === undefined && !(key in object))) {\n    object[key] = value;\n  }\n}\n\n/**\n * Gets the index at which the `key` is found in `array` of key-value pairs.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} key The key to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\nfunction assocIndexOf(array, key) {\n  var length = array.length;\n  while (length--) {\n    if (eq(array[length][0], key)) {\n      return length;\n    }\n  }\n  return -1;\n}\n\n/**\n * The base implementation of `_.assign` without support for multiple sources\n * or `customizer` functions.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @returns {Object} Returns `object`.\n */\nfunction baseAssign(object, source) {\n  return object && copyObject(source, keys(source), object);\n}\n\n/**\n * The base implementation of `_.clone` and `_.cloneDeep` which tracks\n * traversed objects.\n *\n * @private\n * @param {*} value The value to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @param {boolean} [isFull] Specify a clone including symbols.\n * @param {Function} [customizer] The function to customize cloning.\n * @param {string} [key] The key of `value`.\n * @param {Object} [object] The parent object of `value`.\n * @param {Object} [stack] Tracks traversed objects and their clone counterparts.\n * @returns {*} Returns the cloned value.\n */\nfunction baseClone(value, isDeep, isFull, customizer, key, object, stack) {\n  var result;\n  if (customizer) {\n    result = object ? customizer(value, key, object, stack) : customizer(value);\n  }\n  if (result !== undefined) {\n    return result;\n  }\n  if (!isObject(value)) {\n    return value;\n  }\n  var isArr = isArray(value);\n  if (isArr) {\n    result = initCloneArray(value);\n    if (!isDeep) {\n      return copyArray(value, result);\n    }\n  } else {\n    var tag = getTag(value),\n        isFunc = tag == funcTag || tag == genTag;\n\n    if (isBuffer(value)) {\n      return cloneBuffer(value, isDeep);\n    }\n    if (tag == objectTag || tag == argsTag || (isFunc && !object)) {\n      if (isHostObject(value)) {\n        return object ? value : {};\n      }\n      result = initCloneObject(isFunc ? {} : value);\n      if (!isDeep) {\n        return copySymbols(value, baseAssign(result, value));\n      }\n    } else {\n      if (!cloneableTags[tag]) {\n        return object ? value : {};\n      }\n      result = initCloneByTag(value, tag, baseClone, isDeep);\n    }\n  }\n  // Check for circular references and return its corresponding clone.\n  stack || (stack = new Stack);\n  var stacked = stack.get(value);\n  if (stacked) {\n    return stacked;\n  }\n  stack.set(value, result);\n\n  if (!isArr) {\n    var props = isFull ? getAllKeys(value) : keys(value);\n  }\n  arrayEach(props || value, function(subValue, key) {\n    if (props) {\n      key = subValue;\n      subValue = value[key];\n    }\n    // Recursively populate clone (susceptible to call stack limits).\n    assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack));\n  });\n  return result;\n}\n\n/**\n * The base implementation of `_.create` without support for assigning\n * properties to the created object.\n *\n * @private\n * @param {Object} prototype The object to inherit from.\n * @returns {Object} Returns the new object.\n */\nfunction baseCreate(proto) {\n  return isObject(proto) ? objectCreate(proto) : {};\n}\n\n/**\n * The base implementation of `getAllKeys` and `getAllKeysIn` which uses\n * `keysFunc` and `symbolsFunc` to get the enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @param {Function} symbolsFunc The function to get the symbols of `object`.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction baseGetAllKeys(object, keysFunc, symbolsFunc) {\n  var result = keysFunc(object);\n  return isArray(object) ? result : arrayPush(result, symbolsFunc(object));\n}\n\n/**\n * The base implementation of `getTag`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nfunction baseGetTag(value) {\n  return objectToString.call(value);\n}\n\n/**\n * The base implementation of `_.isNative` without bad shim checks.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a native function,\n *  else `false`.\n */\nfunction baseIsNative(value) {\n  if (!isObject(value) || isMasked(value)) {\n    return false;\n  }\n  var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;\n  return pattern.test(toSource(value));\n}\n\n/**\n * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\nfunction baseKeys(object) {\n  if (!isPrototype(object)) {\n    return nativeKeys(object);\n  }\n  var result = [];\n  for (var key in Object(object)) {\n    if (hasOwnProperty.call(object, key) && key != 'constructor') {\n      result.push(key);\n    }\n  }\n  return result;\n}\n\n/**\n * Creates a clone of  `buffer`.\n *\n * @private\n * @param {Buffer} buffer The buffer to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Buffer} Returns the cloned buffer.\n */\nfunction cloneBuffer(buffer, isDeep) {\n  if (isDeep) {\n    return buffer.slice();\n  }\n  var result = new buffer.constructor(buffer.length);\n  buffer.copy(result);\n  return result;\n}\n\n/**\n * Creates a clone of `arrayBuffer`.\n *\n * @private\n * @param {ArrayBuffer} arrayBuffer The array buffer to clone.\n * @returns {ArrayBuffer} Returns the cloned array buffer.\n */\nfunction cloneArrayBuffer(arrayBuffer) {\n  var result = new arrayBuffer.constructor(arrayBuffer.byteLength);\n  new Uint8Array(result).set(new Uint8Array(arrayBuffer));\n  return result;\n}\n\n/**\n * Creates a clone of `dataView`.\n *\n * @private\n * @param {Object} dataView The data view to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned data view.\n */\nfunction cloneDataView(dataView, isDeep) {\n  var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;\n  return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);\n}\n\n/**\n * Creates a clone of `map`.\n *\n * @private\n * @param {Object} map The map to clone.\n * @param {Function} cloneFunc The function to clone values.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned map.\n */\nfunction cloneMap(map, isDeep, cloneFunc) {\n  var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map);\n  return arrayReduce(array, addMapEntry, new map.constructor);\n}\n\n/**\n * Creates a clone of `regexp`.\n *\n * @private\n * @param {Object} regexp The regexp to clone.\n * @returns {Object} Returns the cloned regexp.\n */\nfunction cloneRegExp(regexp) {\n  var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));\n  result.lastIndex = regexp.lastIndex;\n  return result;\n}\n\n/**\n * Creates a clone of `set`.\n *\n * @private\n * @param {Object} set The set to clone.\n * @param {Function} cloneFunc The function to clone values.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned set.\n */\nfunction cloneSet(set, isDeep, cloneFunc) {\n  var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set);\n  return arrayReduce(array, addSetEntry, new set.constructor);\n}\n\n/**\n * Creates a clone of the `symbol` object.\n *\n * @private\n * @param {Object} symbol The symbol object to clone.\n * @returns {Object} Returns the cloned symbol object.\n */\nfunction cloneSymbol(symbol) {\n  return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};\n}\n\n/**\n * Creates a clone of `typedArray`.\n *\n * @private\n * @param {Object} typedArray The typed array to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned typed array.\n */\nfunction cloneTypedArray(typedArray, isDeep) {\n  var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;\n  return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);\n}\n\n/**\n * Copies the values of `source` to `array`.\n *\n * @private\n * @param {Array} source The array to copy values from.\n * @param {Array} [array=[]] The array to copy values to.\n * @returns {Array} Returns `array`.\n */\nfunction copyArray(source, array) {\n  var index = -1,\n      length = source.length;\n\n  array || (array = Array(length));\n  while (++index < length) {\n    array[index] = source[index];\n  }\n  return array;\n}\n\n/**\n * Copies properties of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy properties from.\n * @param {Array} props The property identifiers to copy.\n * @param {Object} [object={}] The object to copy properties to.\n * @param {Function} [customizer] The function to customize copied values.\n * @returns {Object} Returns `object`.\n */\nfunction copyObject(source, props, object, customizer) {\n  object || (object = {});\n\n  var index = -1,\n      length = props.length;\n\n  while (++index < length) {\n    var key = props[index];\n\n    var newValue = customizer\n      ? customizer(object[key], source[key], key, object, source)\n      : undefined;\n\n    assignValue(object, key, newValue === undefined ? source[key] : newValue);\n  }\n  return object;\n}\n\n/**\n * Copies own symbol properties of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy symbols from.\n * @param {Object} [object={}] The object to copy symbols to.\n * @returns {Object} Returns `object`.\n */\nfunction copySymbols(source, object) {\n  return copyObject(source, getSymbols(source), object);\n}\n\n/**\n * Creates an array of own enumerable property names and symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction getAllKeys(object) {\n  return baseGetAllKeys(object, keys, getSymbols);\n}\n\n/**\n * Gets the data for `map`.\n *\n * @private\n * @param {Object} map The map to query.\n * @param {string} key The reference key.\n * @returns {*} Returns the map data.\n */\nfunction getMapData(map, key) {\n  var data = map.__data__;\n  return isKeyable(key)\n    ? data[typeof key == 'string' ? 'string' : 'hash']\n    : data.map;\n}\n\n/**\n * Gets the native function at `key` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the method to get.\n * @returns {*} Returns the function if it's native, else `undefined`.\n */\nfunction getNative(object, key) {\n  var value = getValue(object, key);\n  return baseIsNative(value) ? value : undefined;\n}\n\n/**\n * Creates an array of the own enumerable symbol properties of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\nvar getSymbols = nativeGetSymbols ? overArg(nativeGetSymbols, Object) : stubArray;\n\n/**\n * Gets the `toStringTag` of `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nvar getTag = baseGetTag;\n\n// Fallback for data views, maps, sets, and weak maps in IE 11,\n// for data views in Edge < 14, and promises in Node.js.\nif ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||\n    (Map && getTag(new Map) != mapTag) ||\n    (Promise && getTag(Promise.resolve()) != promiseTag) ||\n    (Set && getTag(new Set) != setTag) ||\n    (WeakMap && getTag(new WeakMap) != weakMapTag)) {\n  getTag = function(value) {\n    var result = objectToString.call(value),\n        Ctor = result == objectTag ? value.constructor : undefined,\n        ctorString = Ctor ? toSource(Ctor) : undefined;\n\n    if (ctorString) {\n      switch (ctorString) {\n        case dataViewCtorString: return dataViewTag;\n        case mapCtorString: return mapTag;\n        case promiseCtorString: return promiseTag;\n        case setCtorString: return setTag;\n        case weakMapCtorString: return weakMapTag;\n      }\n    }\n    return result;\n  };\n}\n\n/**\n * Initializes an array clone.\n *\n * @private\n * @param {Array} array The array to clone.\n * @returns {Array} Returns the initialized clone.\n */\nfunction initCloneArray(array) {\n  var length = array.length,\n      result = array.constructor(length);\n\n  // Add properties assigned by `RegExp#exec`.\n  if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {\n    result.index = array.index;\n    result.input = array.input;\n  }\n  return result;\n}\n\n/**\n * Initializes an object clone.\n *\n * @private\n * @param {Object} object The object to clone.\n * @returns {Object} Returns the initialized clone.\n */\nfunction initCloneObject(object) {\n  return (typeof object.constructor == 'function' && !isPrototype(object))\n    ? baseCreate(getPrototype(object))\n    : {};\n}\n\n/**\n * Initializes an object clone based on its `toStringTag`.\n *\n * **Note:** This function only supports cloning values with tags of\n * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.\n *\n * @private\n * @param {Object} object The object to clone.\n * @param {string} tag The `toStringTag` of the object to clone.\n * @param {Function} cloneFunc The function to clone values.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the initialized clone.\n */\nfunction initCloneByTag(object, tag, cloneFunc, isDeep) {\n  var Ctor = object.constructor;\n  switch (tag) {\n    case arrayBufferTag:\n      return cloneArrayBuffer(object);\n\n    case boolTag:\n    case dateTag:\n      return new Ctor(+object);\n\n    case dataViewTag:\n      return cloneDataView(object, isDeep);\n\n    case float32Tag: case float64Tag:\n    case int8Tag: case int16Tag: case int32Tag:\n    case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:\n      return cloneTypedArray(object, isDeep);\n\n    case mapTag:\n      return cloneMap(object, isDeep, cloneFunc);\n\n    case numberTag:\n    case stringTag:\n      return new Ctor(object);\n\n    case regexpTag:\n      return cloneRegExp(object);\n\n    case setTag:\n      return cloneSet(object, isDeep, cloneFunc);\n\n    case symbolTag:\n      return cloneSymbol(object);\n  }\n}\n\n/**\n * Checks if `value` is a valid array-like index.\n *\n * @private\n * @param {*} value The value to check.\n * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n */\nfunction isIndex(value, length) {\n  length = length == null ? MAX_SAFE_INTEGER : length;\n  return !!length &&\n    (typeof value == 'number' || reIsUint.test(value)) &&\n    (value > -1 && value % 1 == 0 && value < length);\n}\n\n/**\n * Checks if `value` is suitable for use as unique object key.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is suitable, else `false`.\n */\nfunction isKeyable(value) {\n  var type = typeof value;\n  return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')\n    ? (value !== '__proto__')\n    : (value === null);\n}\n\n/**\n * Checks if `func` has its source masked.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` is masked, else `false`.\n */\nfunction isMasked(func) {\n  return !!maskSrcKey && (maskSrcKey in func);\n}\n\n/**\n * Checks if `value` is likely a prototype object.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.\n */\nfunction isPrototype(value) {\n  var Ctor = value && value.constructor,\n      proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;\n\n  return value === proto;\n}\n\n/**\n * Converts `func` to its source code.\n *\n * @private\n * @param {Function} func The function to process.\n * @returns {string} Returns the source code.\n */\nfunction toSource(func) {\n  if (func != null) {\n    try {\n      return funcToString.call(func);\n    } catch (e) {}\n    try {\n      return (func + '');\n    } catch (e) {}\n  }\n  return '';\n}\n\n/**\n * This method is like `_.clone` except that it recursively clones `value`.\n *\n * @static\n * @memberOf _\n * @since 1.0.0\n * @category Lang\n * @param {*} value The value to recursively clone.\n * @returns {*} Returns the deep cloned value.\n * @see _.clone\n * @example\n *\n * var objects = [{ 'a': 1 }, { 'b': 2 }];\n *\n * var deep = _.cloneDeep(objects);\n * console.log(deep[0] === objects[0]);\n * // => false\n */\nfunction cloneDeep(value) {\n  return baseClone(value, true, true);\n}\n\n/**\n * Performs a\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * comparison between two values to determine if they are equivalent.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.eq(object, object);\n * // => true\n *\n * _.eq(object, other);\n * // => false\n *\n * _.eq('a', 'a');\n * // => true\n *\n * _.eq('a', Object('a'));\n * // => false\n *\n * _.eq(NaN, NaN);\n * // => true\n */\nfunction eq(value, other) {\n  return value === other || (value !== value && other !== other);\n}\n\n/**\n * Checks if `value` is likely an `arguments` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n *  else `false`.\n * @example\n *\n * _.isArguments(function() { return arguments; }());\n * // => true\n *\n * _.isArguments([1, 2, 3]);\n * // => false\n */\nfunction isArguments(value) {\n  // Safari 8.1 makes `arguments.callee` enumerable in strict mode.\n  return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&\n    (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);\n}\n\n/**\n * Checks if `value` is classified as an `Array` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array, else `false`.\n * @example\n *\n * _.isArray([1, 2, 3]);\n * // => true\n *\n * _.isArray(document.body.children);\n * // => false\n *\n * _.isArray('abc');\n * // => false\n *\n * _.isArray(_.noop);\n * // => false\n */\nvar isArray = Array.isArray;\n\n/**\n * Checks if `value` is array-like. A value is considered array-like if it's\n * not a function and has a `value.length` that's an integer greater than or\n * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n * @example\n *\n * _.isArrayLike([1, 2, 3]);\n * // => true\n *\n * _.isArrayLike(document.body.children);\n * // => true\n *\n * _.isArrayLike('abc');\n * // => true\n *\n * _.isArrayLike(_.noop);\n * // => false\n */\nfunction isArrayLike(value) {\n  return value != null && isLength(value.length) && !isFunction(value);\n}\n\n/**\n * This method is like `_.isArrayLike` except that it also checks if `value`\n * is an object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array-like object,\n *  else `false`.\n * @example\n *\n * _.isArrayLikeObject([1, 2, 3]);\n * // => true\n *\n * _.isArrayLikeObject(document.body.children);\n * // => true\n *\n * _.isArrayLikeObject('abc');\n * // => false\n *\n * _.isArrayLikeObject(_.noop);\n * // => false\n */\nfunction isArrayLikeObject(value) {\n  return isObjectLike(value) && isArrayLike(value);\n}\n\n/**\n * Checks if `value` is a buffer.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.\n * @example\n *\n * _.isBuffer(new Buffer(2));\n * // => true\n *\n * _.isBuffer(new Uint8Array(2));\n * // => false\n */\nvar isBuffer = nativeIsBuffer || stubFalse;\n\n/**\n * Checks if `value` is classified as a `Function` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a function, else `false`.\n * @example\n *\n * _.isFunction(_);\n * // => true\n *\n * _.isFunction(/abc/);\n * // => false\n */\nfunction isFunction(value) {\n  // The use of `Object#toString` avoids issues with the `typeof` operator\n  // in Safari 8-9 which returns 'object' for typed array and other constructors.\n  var tag = isObject(value) ? objectToString.call(value) : '';\n  return tag == funcTag || tag == genTag;\n}\n\n/**\n * Checks if `value` is a valid array-like length.\n *\n * **Note:** This method is loosely based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.\n * @example\n *\n * _.isLength(3);\n * // => true\n *\n * _.isLength(Number.MIN_VALUE);\n * // => false\n *\n * _.isLength(Infinity);\n * // => false\n *\n * _.isLength('3');\n * // => false\n */\nfunction isLength(value) {\n  return typeof value == 'number' &&\n    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n  var type = typeof value;\n  return !!value && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n  return !!value && typeof value == 'object';\n}\n\n/**\n * Creates an array of the own enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects. See the\n * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * for more details.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n *   this.a = 1;\n *   this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keys(new Foo);\n * // => ['a', 'b'] (iteration order is not guaranteed)\n *\n * _.keys('hi');\n * // => ['0', '1']\n */\nfunction keys(object) {\n  return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);\n}\n\n/**\n * This method returns a new empty array.\n *\n * @static\n * @memberOf _\n * @since 4.13.0\n * @category Util\n * @returns {Array} Returns the new empty array.\n * @example\n *\n * var arrays = _.times(2, _.stubArray);\n *\n * console.log(arrays);\n * // => [[], []]\n *\n * console.log(arrays[0] === arrays[1]);\n * // => false\n */\nfunction stubArray() {\n  return [];\n}\n\n/**\n * This method returns `false`.\n *\n * @static\n * @memberOf _\n * @since 4.13.0\n * @category Util\n * @returns {boolean} Returns `false`.\n * @example\n *\n * _.times(2, _.stubFalse);\n * // => [false, false]\n */\nfunction stubFalse() {\n  return false;\n}\n\nmodule.exports = cloneDeep;\n","import * as Ably from 'ably';\nimport cloneDeep from 'lodash.clonedeep';\n\nimport { ErrorCode } from './errors.js';\nimport {\n  ChatMessageAction,\n  ChatMessageEvent,\n  ChatMessageEventType,\n  MessageReactionSummaryEvent,\n  MessageReactionSummaryEventType,\n} from './events.js';\nimport { Headers } from './headers.js';\nimport { Metadata } from './metadata.js';\nimport { OperationMetadata } from './operation-metadata.js';\n\n/**\n * {@link Headers} type for chat messages.\n */\nexport type MessageHeaders = Headers;\n\n/**\n * {@link Metadata} type for chat messages.\n */\nexport type MessageMetadata = Metadata;\n\n/**\n * {@link OperationMetadata} type for a chat message. Contains information about an update or deletion operation.\n */\nexport type MessageOperationMetadata = OperationMetadata;\n\n/**\n * Represents the detail of a message deletion or update.\n */\nexport interface MessageVersion {\n  /**\n   * A unique identifier for the latest version of this message.\n   */\n  serial: string;\n\n  /**\n   * The timestamp at which this version was updated, deleted, or created.\n   */\n  timestamp: Date;\n\n  /**\n   * The optional clientId of the user who performed an update or deletion.\n   */\n  clientId?: string;\n\n  /**\n   * The optional description for an update or deletion.\n   */\n  description?: string;\n\n  /**\n   * The optional metadata associated with an update or deletion.\n   */\n  metadata?: MessageOperationMetadata;\n}\n\n/**\n * Represents a single message in a chat room.\n */\nexport interface Message {\n  /**\n   * The unique identifier of the message.\n   */\n  readonly serial: string;\n\n  /**\n   * The clientId of the user who created the message.\n   */\n  readonly clientId: string;\n\n  /**\n   * The text of the message.\n   */\n  readonly text: string;\n\n  /**\n   * The timestamp at which the message was created.\n   */\n  readonly timestamp: Date;\n\n  /**\n   * The metadata of a chat message. Allows for attaching extra info to a message,\n   * which can be used for various features such as animations, effects, or simply\n   * to link it to other resources such as images, relative points in time, etc.\n   *\n   * Metadata is part of the Ably Pub/sub message content and is not read by Ably.\n   *\n   * This value is always set. If there is no metadata, this is an empty object.\n   *\n   * Do not use metadata for authoritative information. There is no server-side\n   * validation. When reading the metadata treat it like user input.\n   */\n  readonly metadata: MessageMetadata;\n\n  /**\n   * The headers of a chat message. Headers enable attaching extra info to a message,\n   * which can be used for various features such as linking to a relative point in\n   * time of a livestream video or flagging this message as important or pinned.\n   *\n   * Headers are part of the Ably realtime message extras.headers and they can be used\n   * for Filtered Subscriptions and similar.\n   *\n   * This value is always set. If there are no headers, this is an empty object.\n   *\n   * Do not use the headers for authoritative information. There is no server-side\n   * validation. When reading the headers, treat them like user input.\n   */\n  readonly headers: MessageHeaders;\n\n  /**\n   * The action type of the message. This can be used to determine if the message was created, updated, or deleted.\n   */\n  readonly action: ChatMessageAction;\n\n  /**\n   * Information about the latest version of this message.\n   */\n  readonly version: MessageVersion;\n\n  /**\n   * The reactions summary for this message.\n   */\n  readonly reactions: MessageReactionSummary;\n\n  /**\n   * Creates a new message instance with the event applied.\n   *\n   * NOTE: This method will not replace the message reactions if the event is of type `Message`.\n   * @param event The event to be applied to the returned message.\n   * @throws {@link ErrorInfo} if the event is for a different message.\n   * @throws {@link ErrorInfo} if the event is a {@link ChatMessageEventType.Created}.\n   * @returns A new message instance with the event applied. If the event is a no-op, such\n   *    as an event for an old version, the same message is returned (not a copy).\n   */\n  with(event: Message | ChatMessageEvent | MessageReactionSummaryEvent): Message;\n\n  /**\n   * Creates a copy of the message with fields replaced per the parameters.\n   * @param params The parameters to replace in the message.\n   * @returns The message copy.\n   */\n  copy(params?: MessageCopyParams): Message;\n}\n\n/**\n * Parameters for copying a message.\n */\nexport interface MessageCopyParams {\n  /**\n   * The text of the copied message.\n   */\n  text?: string;\n\n  /**\n   * The metadata of the copied message.\n   */\n  metadata?: MessageMetadata;\n\n  /**\n   * The headers of the copied message.\n   */\n  headers?: MessageHeaders;\n}\n\n/**\n * Represents a summary of all reactions on a message.\n */\nexport interface MessageReactionSummary {\n  /**\n   * Map of reaction to the summary (total and clients) for reactions of type {@link MessageReactionType.Unique}.\n   */\n  unique: Ably.SummaryUniqueValues;\n\n  /**\n   * Map of reaction to the summary (total and clients) for reactions of type {@link MessageReactionType.Distinct}.\n   */\n  distinct: Ably.SummaryDistinctValues;\n\n  /**\n   * Map of reaction to the summary (total and clients) for reactions of type {@link MessageReactionType.Multiple}.\n   */\n  multiple: Ably.SummaryMultipleValues;\n}\n\n/**\n * Parameters for creating a new DefaultMessage instance.\n */\nexport interface DefaultMessageParams {\n  serial: string;\n  clientId: string;\n  text: string;\n  metadata: MessageMetadata;\n  headers: MessageHeaders;\n  action: ChatMessageAction;\n  version: MessageVersion;\n  timestamp: Date;\n  reactions: MessageReactionSummary;\n}\n\n/**\n * An implementation of the Message interface for chat messages.\n *\n * Allows for comparison of messages based on their serials.\n */\nexport class DefaultMessage implements Message {\n  public readonly serial: string;\n  public readonly clientId: string;\n  public readonly text: string;\n  public readonly metadata: MessageMetadata;\n  public readonly headers: MessageHeaders;\n  public readonly action: ChatMessageAction;\n  public readonly version: MessageVersion;\n  public readonly timestamp: Date;\n  public readonly reactions: MessageReactionSummary;\n\n  constructor({\n    serial,\n    clientId,\n    text,\n    metadata,\n    headers,\n    action,\n    version,\n    timestamp,\n    reactions,\n  }: DefaultMessageParams) {\n    this.serial = serial;\n    this.clientId = clientId;\n    this.text = text;\n    this.metadata = metadata;\n    this.headers = headers;\n    this.action = action;\n    this.version = version;\n    this.timestamp = timestamp;\n    this.reactions = reactions;\n    // The object is frozen after constructing to enforce readonly at runtime too\n    Object.freeze(this.version);\n    Object.freeze(this.reactions);\n    Object.freeze(this.reactions.multiple);\n    Object.freeze(this.reactions.distinct);\n    Object.freeze(this.reactions.unique);\n    Object.freeze(this);\n  }\n\n  with(event: Message | ChatMessageEvent | MessageReactionSummaryEvent): Message {\n    // If event has the property \"serial\", then it's a message\n    if ('serial' in event) {\n      return this._getLatestMessageVersion(event);\n    }\n\n    // If the event is a created event, throw an error\n    if (event.type === ChatMessageEventType.Created) {\n      throw new Ably.ErrorInfo(\n        'unable to apply message event; unable to apply created event to existing message',\n        ErrorCode.InvalidArgument,\n        400,\n      );\n    }\n\n    // reaction summary\n    if (event.type === MessageReactionSummaryEventType.Summary) {\n      if (event.messageSerial !== this.serial) {\n        throw new Ably.ErrorInfo(\n          'unable to apply message event; event is for a different message',\n          ErrorCode.InvalidArgument,\n          400,\n        );\n      }\n\n      const newReactions: MessageReactionSummary = {\n        unique: cloneDeep(event.reactions.unique),\n        distinct: cloneDeep(event.reactions.distinct),\n        multiple: cloneDeep(event.reactions.multiple),\n      };\n\n      return DefaultMessage._clone(this, { reactions: newReactions });\n    }\n\n    // Message event (update or delete)\n    return this._getLatestMessageVersion(event.message);\n  }\n\n  /**\n   * Get the latest message version, based on the event.\n   * If \"this\" is the latest version, return \"this\", otherwise clone the message and apply the reactions.\n   * @param message The message to get the latest version of\n   * @returns The latest message version\n   */\n  private _getLatestMessageVersion(message: Message): Message {\n    // message event (update or delete)\n    if (message.serial !== this.serial) {\n      throw new Ably.ErrorInfo(\n        'unable to apply message event; event is for a different message',\n        ErrorCode.InvalidArgument,\n        400,\n      );\n    }\n\n    // event is older, keep this instead\n    if (this.version.serial >= message.version.serial) {\n      return this;\n    }\n\n    // event is newer, copy reactions from this and make new message from event\n    // TODO: This ignores summaries being newer on the message passed in, and is something we need to address\n    return DefaultMessage._clone(message, { reactions: this.reactions });\n  }\n\n  // Clone a message, optionally replace the given fields\n  private static _clone(source: Message, replace?: Partial<Message>): DefaultMessage {\n    return new DefaultMessage({\n      serial: replace?.serial ?? source.serial,\n      clientId: replace?.clientId ?? source.clientId,\n      text: replace?.text ?? source.text,\n      metadata: replace?.metadata ?? cloneDeep(source.metadata),\n      headers: replace?.headers ?? cloneDeep(source.headers),\n      action: replace?.action ?? source.action,\n      version: replace?.version ?? cloneDeep(source.version),\n      timestamp: replace?.timestamp ?? source.timestamp,\n      reactions: replace?.reactions ?? cloneDeep(source.reactions),\n    });\n  }\n\n  copy(params: MessageCopyParams = {}): Message {\n    return DefaultMessage._clone(this, params);\n  }\n}\n\n/**\n * Creates an empty MessageReactionSummary object with empty unique and distinct reaction collections.\n * @returns An empty MessageReactionSummary object.\n */\nexport const emptyMessageReactions = (): MessageReactionSummary => ({\n  unique: {},\n  distinct: {},\n  multiple: {},\n});\n","import * as Ably from 'ably';\n\nimport { ChatMessageAction } from './events.js';\nimport { DefaultMessage, emptyMessageReactions, Message, MessageHeaders, MessageMetadata } from './message.js';\n\ninterface MessagePayload {\n  data?: {\n    text?: string;\n    metadata?: MessageMetadata;\n  };\n  clientId?: string;\n  timestamp: number;\n  extras?: {\n    headers?: MessageHeaders;\n  };\n\n  serial: string;\n  action: Ably.MessageAction;\n  version: Ably.MessageVersion;\n  annotations: Ably.MessageAnnotations;\n}\n\n// Parse a realtime message to a chat message\nexport const parseMessage = (inboundMessage: Ably.InboundMessage): Message => {\n  const message = inboundMessage as MessagePayload;\n\n  // Provide default values for all fields\n  const data = message.data && typeof message.data === 'object' ? message.data : {};\n  const extras = message.extras && typeof message.extras === 'object' ? message.extras : {};\n  const clientId = message.clientId || '';\n  const text = data.text || '';\n  // Spec: CHA-M4k5\n  const timestamp = new Date(message.timestamp || 0);\n  const serial = message.serial || '';\n  const metadata = data.metadata && typeof data.metadata === 'object' ? data.metadata : {};\n  const headers = extras.headers || {};\n\n  // Create the version, using defaults as required\n  const version = {\n    ...message.version,\n    // Spec: CHA-M4k6\n    serial: message.version.serial || serial,\n    // Spec: CHA-M4k7\n    timestamp: new Date(message.version.timestamp || timestamp),\n  };\n\n  // Convert the action to a ChatMessageAction enum, defaulting to MessageCreate if the action is not found.\n  const action = Object.values(ChatMessageAction).includes(message.action as ChatMessageAction)\n    ? (message.action as ChatMessageAction)\n    : ChatMessageAction.MessageCreate;\n\n  return new DefaultMessage({\n    serial,\n    clientId,\n    text,\n    metadata,\n    headers,\n    action,\n    version,\n    timestamp,\n    reactions: emptyMessageReactions(),\n  });\n};\n","import * as Ably from 'ably';\n\nimport { ErrorCode } from './errors.js';\n\n/**\n * Asserts that a serial parameter is valid (not undefined, null, or empty string).\n * @internal\n * @param serial The serial value to validate.\n * @param op The operation being performed (e.g., \"send message reaction\").\n * @param paramName The name of the parameter (e.g., \"messageSerial\").\n * @throws {Ably.ErrorInfo} With InvalidArgument code if the serial is invalid.\n */\nexport const assertValidSerial = (serial: unknown, op: string, paramName: string): void => {\n  if (!serial) {\n    throw new Ably.ErrorInfo(\n      `unable to ${op}; ${paramName} must be a non-empty string`,\n      ErrorCode.InvalidArgument,\n      400,\n    );\n  }\n};\n","import * as Ably from 'ably';\n\nimport { ChannelOptionsMerger } from './channel-manager.js';\nimport {\n  ChatApi,\n  DeleteMessageReactionParams as APIDeleteMessageReactionParams,\n  SendMessageReactionParams as APISendMessageReactionParams,\n} from './chat-api.js';\nimport { ErrorCode } from './errors.js';\nimport {\n  AnnotationTypeToReactionType,\n  MessageReactionRawEvent,\n  MessageReactionRawEventType,\n  MessageReactionSummaryEvent,\n  MessageReactionSummaryEventType,\n  MessageReactionType,\n  ReactionAnnotationType,\n} from './events.js';\nimport { Logger } from './logger.js';\nimport { Message } from './message.js';\nimport { subscribe } from './realtime-subscriptions.js';\nimport { InternalRoomOptions, MessagesOptions } from './room-options.js';\nimport { assertValidSerial } from './serial.js';\nimport { Subscription } from './subscription.js';\nimport EventEmitter, { emitterHasListeners, wrap } from './utils/event-emitter.js';\n\n/**\n * A listener for summary message reaction events.\n * @param event The message reaction summary event that was received. Use it\n *   with {@link Message.with} to keep an up-to-date reaction count.\n */\nexport type MessageReactionListener = (event: MessageReactionSummaryEvent) => void;\n\n/**\n * A listener for individual message reaction events.\n * @param event The message reaction event that was received.\n */\nexport type MessageRawReactionListener = (event: MessageReactionRawEvent) => void;\n\n/**\n * Parameters for sending a message reaction.\n */\nexport interface SendMessageReactionParams {\n  /**\n   * The reaction name to send; ie. the emoji.\n   */\n  name: string;\n\n  /**\n   * The type of reaction, must be one of {@link MessageReactionType}.\n   * If not set, the default type will be used which is configured in the {@link MessagesOptions.defaultMessageReactionType} of the room.\n   */\n  type?: MessageReactionType;\n\n  /**\n   * The count of the reaction for type {@link MessageReactionType.Multiple}.\n   * Defaults to 1 if not set. Not supported for other reaction types.\n   * @defaultValue 1\n   */\n  count?: number;\n}\n\n/**\n * Parameters for deleting a message reaction.\n */\nexport interface DeleteMessageReactionParams {\n  /**\n   * The reaction name to delete; ie. the emoji. Required for all reaction types\n   * except {@link MessageReactionType.Unique}.\n   */\n  name?: string;\n\n  /**\n   * The type of reaction, must be one of {@link MessageReactionType}.\n   * If not set, the default type will be used which is configured in the {@link MessagesOptions.defaultMessageReactionType} of the room.\n   */\n  type?: MessageReactionType;\n}\n\n/**\n * Send, delete, and subscribe to message reactions.\n */\nexport interface MessageReactions {\n  /**\n   * Send a message reaction.\n   * @param messageSerial The serial of the message to react to.\n   * @param params Describe the reaction to send.\n   * @returns A promise that resolves when the reaction is sent.\n   */\n  send(messageSerial: string, params: SendMessageReactionParams): Promise<void>;\n\n  /**\n   * Delete a message reaction\n   * @param messageSerial The serial of the message to remove the reaction from.\n   * @param params The type of reaction annotation and the specific reaction to remove. The reaction to remove is required for all types except {@link MessageReactionType.Unique}.\n   * @returns A promise that resolves when the reaction is deleted.\n   */\n  delete(messageSerial: string, params?: DeleteMessageReactionParams): Promise<void>;\n\n  /**\n   * Subscribe to message reaction summaries. Use this to keep message reaction\n   * counts up to date efficiently in the UI.\n   * @param listener The listener to call when a message reaction summary is received.\n   * @returns A subscription object that should be used to unsubscribe.\n   */\n  subscribe(listener: MessageReactionListener): Subscription;\n\n  /**\n   * Subscribe to individual reaction events.\n   *\n   * If you only need to keep track of reaction counts and clients, use\n   * {@link subscribe} instead.\n   * @param listener The listener to call when a message reaction event is received.\n   * @returns A subscription object that should be used to unsubscribe.\n   */\n  subscribeRaw(listener: MessageRawReactionListener): Subscription;\n\n  /**\n   * Get the reaction count for a message for a particular client.\n   * @param messageSerial The serial of the message to remove the reaction from.\n   * @param clientId The client to fetch the reaction summary for (leave unset for current client).\n   * @returns A clipped reaction summary containing only the requested clientId.\n   * @example\n   * ```typescript\n   * // Subscribe to reaction summaries and check for specific client reactions\n   * room.messages.reactions.subscribe(async (event) => {\n   *   // For brevity of example, we check unique 👍 (normally iterate for all relevant reactions)\n   *   const uniqueLikes = event.summary.unique['👍'];\n   *   if (uniqueLikes && uniqueLikes.clipped && !uniqueLikes.clientIds.includes(myClientId)) {\n   *     // summary is clipped and doesn't include myClientId, so we need to fetch a clientSummary\n   *     const clientReactions = await room.messages.reactions.clientReactions(\n   *       event.messageSerial,\n   *       myClientId\n   *     );\n   *     if (clientReactions.unique && clientReactions.unique['👍']) {\n   *       // client has reacted with 👍\n   *       event.reactions.unique['👍'].clientIds.push(myClientId);\n   *     }\n   *   }\n   *   // from here, process the summary as usual\n   * });\n   * ```\n   */\n  clientReactions(messageSerial: string, clientId?: string): Promise<Message['reactions']>;\n}\n\n/**\n * Maps Ably PubSub annotation action to message reaction event type.\n */\nconst eventTypeMap: Record<string, MessageReactionRawEventType> = {\n  'annotation.create': MessageReactionRawEventType.Create,\n  'annotation.delete': MessageReactionRawEventType.Delete,\n};\n\n/**\n * @inheritDoc\n */\nexport class DefaultMessageReactions implements MessageReactions {\n  private _emitter = new EventEmitter<{\n    [MessageReactionRawEventType.Create]: MessageReactionRawEvent;\n    [MessageReactionRawEventType.Delete]: MessageReactionRawEvent;\n    [MessageReactionSummaryEventType.Summary]: MessageReactionSummaryEvent;\n  }>();\n\n  private readonly _defaultType: MessageReactionType;\n  private readonly _unsubscribeMessageEvents: () => void;\n  private readonly _unsubscribeAnnotationEvents?: () => void;\n\n  constructor(\n    private readonly _logger: Logger,\n    private readonly _options: MessagesOptions | undefined,\n    private readonly _api: ChatApi,\n    private readonly _roomName: string,\n    private readonly _channel: Ably.RealtimeChannel,\n  ) {\n    // Use subscription helper to create cleanup function\n    this._unsubscribeMessageEvents = subscribe(_channel, this._processMessageEvent.bind(this));\n\n    if (this._options?.rawMessageReactions) {\n      this._unsubscribeAnnotationEvents = subscribe(_channel.annotations, this._processAnnotationEvent.bind(this));\n    }\n    this._defaultType = this._options?.defaultMessageReactionType ?? MessageReactionType.Distinct;\n  }\n\n  private _processAnnotationEvent(event: Ably.Annotation) {\n    this._logger.trace('MessageReactions._processAnnotationEvent();', { event });\n\n    // If we don't know the reaction type, ignore it\n    const reactionType = AnnotationTypeToReactionType[event.type];\n    if (!reactionType) {\n      this._logger.info('MessageReactions._processAnnotationEvent(); ignoring unknown reaction type', { event });\n      return;\n    }\n\n    // If we don't know the event type, ignore it\n    const eventType = eventTypeMap[event.action];\n    if (!eventType) {\n      this._logger.info('MessageReactions._processAnnotationEvent(); ignoring unknown reaction event type', { event });\n      return;\n    }\n\n    const name = event.name ?? '';\n    const reactionEvent: MessageReactionRawEvent = {\n      type: eventType,\n      timestamp: new Date(event.timestamp),\n      reaction: {\n        messageSerial: event.messageSerial,\n        type: reactionType,\n        name: name,\n        clientId: event.clientId ?? '',\n      },\n    };\n\n    if (event.count) {\n      reactionEvent.reaction.count = event.count;\n    } else if (eventType === MessageReactionRawEventType.Create && reactionType === MessageReactionType.Multiple) {\n      reactionEvent.reaction.count = 1; // count defaults to 1 for multiple if not set\n    }\n\n    this._emitter.emit(eventType, reactionEvent);\n  }\n\n  private _processMessageEvent(event: Ably.InboundMessage) {\n    this._logger.trace('MessageReactions._processMessageEvent();', { event });\n\n    // only process summary events\n    if (event.action !== 'message.summary') {\n      return;\n    }\n\n    // As Chat uses mutable messages, we know that `serial` will be defined, so this cast is ok\n    const serial = event.serial as unknown as string;\n\n    // Set the reaction types from the summary\n    const summary = event.annotations.summary;\n\n    const unique = (summary[ReactionAnnotationType.Unique] ?? {}) as unknown as Ably.SummaryUniqueValues;\n    const distinct = (summary[ReactionAnnotationType.Distinct] ?? {}) as unknown as Ably.SummaryDistinctValues;\n    const multiple = (summary[ReactionAnnotationType.Multiple] ?? {}) as Ably.SummaryMultipleValues;\n\n    this._emitter.emit(MessageReactionSummaryEventType.Summary, {\n      type: MessageReactionSummaryEventType.Summary,\n      messageSerial: serial,\n      reactions: {\n        unique: unique,\n        distinct: distinct,\n        multiple: multiple,\n      },\n    });\n  }\n\n  /**\n   * @inheritDoc\n   */\n  async send(messageSerial: string, params: SendMessageReactionParams): Promise<void> {\n    this._logger.trace('MessageReactions.send();', { messageSerial, params });\n    // Spec: CHA-MR4a2\n    assertValidSerial(messageSerial, 'send message reaction', 'messageSerial');\n\n    let { type, count } = params;\n    if (!type) {\n      type = this._defaultType;\n    }\n    if (type === MessageReactionType.Multiple && !count) {\n      count = 1;\n    }\n    const apiParams: APISendMessageReactionParams = { type, name: params.name };\n    if (count) {\n      apiParams.count = count;\n    }\n    return this._api.sendMessageReaction(this._roomName, messageSerial, apiParams);\n  }\n\n  /**\n   * @inheritDoc\n   */\n  async delete(messageSerial: string, params?: DeleteMessageReactionParams): Promise<void> {\n    this._logger.trace('MessageReactions.delete();', { messageSerial, params });\n    // Spec: CHA-MR11a2\n    assertValidSerial(messageSerial, 'delete message reaction', 'messageSerial');\n\n    let type = params?.type;\n    if (!type) {\n      type = this._defaultType;\n    }\n    if (type !== MessageReactionType.Unique && !params?.name) {\n      throw new Ably.ErrorInfo(\n        `unable to delete reaction of type ${type}; name not specified`,\n        ErrorCode.InvalidArgument,\n        400,\n      );\n    }\n    const apiParams: APIDeleteMessageReactionParams = { type };\n    if (type !== MessageReactionType.Unique) {\n      apiParams.name = params?.name;\n    }\n    return this._api.deleteMessageReaction(this._roomName, messageSerial, apiParams);\n  }\n\n  /**\n   * @inheritDoc\n   */\n  subscribe(listener: MessageReactionListener): Subscription {\n    this._logger.trace('MessageReactions.subscribe();');\n\n    const wrapped = wrap(listener);\n    this._emitter.on(MessageReactionSummaryEventType.Summary, wrapped);\n    return {\n      unsubscribe: () => {\n        this._emitter.off(wrapped);\n      },\n    };\n  }\n\n  /**\n   * @inheritDoc\n   */\n  subscribeRaw(listener: MessageRawReactionListener): Subscription {\n    this._logger.trace('MessageReactions.subscribeRaw();');\n\n    if (!this._options?.rawMessageReactions) {\n      throw new Ably.ErrorInfo(\n        'unable to subscribe to message reactions; raw message reactions are not enabled',\n        ErrorCode.FeatureNotEnabledInRoom,\n        400,\n      );\n    }\n    const wrapped = wrap(listener);\n    this._emitter.on([MessageReactionRawEventType.Create, MessageReactionRawEventType.Delete], wrapped);\n    return {\n      unsubscribe: () => {\n        this._emitter.off(wrapped);\n      },\n    };\n  }\n\n  /**\n   * Merges the channel options to add support for message reactions.\n   * @param roomOptions The room options to merge for.\n   * @returns A function that merges the channel options for the room with the ones required for presence.\n   */\n  static channelOptionMerger(roomOptions: InternalRoomOptions): ChannelOptionsMerger {\n    return (options) => {\n      // annotation publish is always required for message reactions\n      if (!options.modes.includes('ANNOTATION_PUBLISH')) {\n        options.modes.push('ANNOTATION_PUBLISH');\n      }\n      // annotation subscribe is only required if the room has raw message reactions\n      if (roomOptions.messages.rawMessageReactions && !options.modes.includes('ANNOTATION_SUBSCRIBE')) {\n        options.modes.push('ANNOTATION_SUBSCRIBE');\n      }\n      return options;\n    };\n  }\n\n  async clientReactions(messageSerial: string, clientId?: string): Promise<Message['reactions']> {\n    this._logger.trace('MessageReactions.clientReactions();', { messageSerial, clientId });\n    assertValidSerial(messageSerial, 'get client reactions', 'messageSerial');\n    return this._api.getClientReactions(this._roomName, messageSerial, clientId);\n  }\n\n  /**\n   * Disposes of the message reactions instance, removing all listeners and subscriptions.\n   * This method should be called when the room is being released to ensure proper cleanup.\n   * @internal\n   */\n  dispose(): void {\n    this._logger.trace('DefaultMessageReactions.dispose();');\n\n    // Remove all user-level listeners from the emitter\n    this._emitter.off();\n\n    // Unsubscribe from channel events using stored unsubscribe functions\n    this._unsubscribeMessageEvents();\n\n    // Unsubscribe from annotations if they were enabled\n    this._unsubscribeAnnotationEvents?.();\n\n    this._logger.debug('DefaultMessageReactions.dispose(); disposed successfully');\n  }\n\n  /**\n   * Checks if there are any listeners registered by users.\n   * @internal\n   * @returns true if there are listeners, false otherwise.\n   */\n  hasListeners(): boolean {\n    return emitterHasListeners(this._emitter);\n  }\n}\n","import { ChatMessageAction } from './events.js';\nimport { JsonObject } from './json.js';\nimport { DefaultMessage, emptyMessageReactions, Message, MessageReactionSummary } from './message.js';\n\n// RestClientIdList represents a list of client IDs with aggregation data\nexport interface RestClientIdList {\n  total: number;\n  clientIds: string[];\n  clipped?: boolean;\n}\n\n// RestClientIdCounts represents client ID counts with aggregation data\nexport interface RestClientIdCounts {\n  total: number;\n  clientIds: Record<string, number>;\n  totalUnidentified: number;\n  clipped?: boolean;\n  totalClientIds?: number;\n}\n\n// RestVersion represents the version information of a message. (i.e. an update or delete)\nexport interface RestVersion {\n  serial: string;\n  timestamp: number;\n  clientId?: string;\n  description?: string;\n  metadata?: Record<string, string>;\n}\n\n// ChatMessageReactions represents reactions on a chat message\nexport interface RestChatMessageReactions {\n  unique?: Record<string, RestClientIdList>;\n  distinct?: Record<string, RestClientIdList>;\n  multiple?: Record<string, RestClientIdCounts>;\n}\n\n// RestMessage represents a message in V4 of the REST API.\nexport interface RestMessage {\n  serial: string;\n  version: RestVersion;\n  text: string;\n  clientId: string;\n  action: 'message.create' | 'message.update' | 'message.delete';\n  metadata: JsonObject;\n  headers: Record<string, string>;\n  timestamp: number;\n  reactions?: RestChatMessageReactions;\n}\n\n/**\n * Converts a message object from its REST representation to the standard message object in the SDK.\n * @param message The message to convert from REST.\n * @returns The converted message.\n */\nexport const messageFromRest = (message: RestMessage): Message => {\n  const reactions = {\n    ...emptyMessageReactions(),\n    ...message.reactions,\n  };\n\n  // Iterate through all reaction types and normalize fields\n  // For unique and distinct reactions, ensure clipped is explicitly set to false if not present\n  for (const reactionType of ['unique', 'distinct']) {\n    const reactionTypeData = reactions[reactionType as keyof typeof reactions] as Record<string, RestClientIdList>;\n    for (const reactionName of Object.keys(reactionTypeData)) {\n      const reaction = reactionTypeData[reactionName];\n      if (reaction && !reaction.clipped) {\n        reaction.clipped = false;\n      }\n    }\n  }\n\n  // For multiple reactions, ensure clipped is explicitly set to false and totalClientIds is set to zero if not present\n  const multipleReactions = reactions.multiple;\n  for (const reactionName of Object.keys(multipleReactions)) {\n    const reaction = multipleReactions[reactionName];\n    if (reaction) {\n      if (!reaction.clipped) {\n        reaction.clipped = false;\n      }\n      if (!reaction.totalClientIds) {\n        reaction.totalClientIds = 0;\n      }\n    }\n  }\n\n  // Convert the action to a ChatMessageAction enum, defaulting to MessageCreate if the action is not found.\n  const action = Object.values(ChatMessageAction).includes(message.action as ChatMessageAction)\n    ? (message.action as ChatMessageAction)\n    : ChatMessageAction.MessageCreate;\n\n  // Create version information from the message\n  const version = {\n    serial: message.version.serial,\n    timestamp: new Date(message.version.timestamp),\n    clientId: message.version.clientId,\n    description: message.version.description,\n    metadata: message.version.metadata,\n  };\n\n  return new DefaultMessage({\n    serial: message.serial,\n    clientId: message.clientId,\n    text: message.text,\n    metadata: message.metadata,\n    headers: message.headers,\n    action,\n    version,\n    timestamp: new Date(message.timestamp),\n    reactions: reactions as MessageReactionSummary,\n  });\n};\n","import * as Ably from 'ably';\n\nimport { ChatApi } from './chat-api.js';\nimport { ErrorCode } from './errors.js';\nimport { ChatMessageAction, ChatMessageEvent, ChatMessageEventType, RealtimeMessageName } from './events.js';\nimport { Logger } from './logger.js';\nimport { Message, MessageHeaders, MessageMetadata, MessageOperationMetadata } from './message.js';\nimport { parseMessage } from './message-parser.js';\nimport { DefaultMessageReactions, MessageReactions } from './message-reactions.js';\nimport { PaginatedResult } from './query.js';\nimport { on, once, subscribe } from './realtime-subscriptions.js';\nimport { messageFromRest } from './rest-types.js';\nimport { MessagesOptions } from './room-options.js';\nimport { assertValidSerial } from './serial.js';\nimport { Subscription } from './subscription.js';\nimport EventEmitter, { emitterHasListeners, wrap } from './utils/event-emitter.js';\n\n/**\n * Event names and their respective payloads emitted by the messages feature.\n */\ninterface MessageEventsMap {\n  [ChatMessageEventType.Created]: ChatMessageEvent;\n  [ChatMessageEventType.Updated]: ChatMessageEvent;\n  [ChatMessageEventType.Deleted]: ChatMessageEvent;\n}\n\n/**\n * Mapping of chat message actions to message events.\n */\nconst MessageActionsToEventsMap: Map<ChatMessageAction, ChatMessageEventType> = new Map<\n  ChatMessageAction,\n  ChatMessageEventType\n>([\n  [ChatMessageAction.MessageCreate, ChatMessageEventType.Created],\n  [ChatMessageAction.MessageUpdate, ChatMessageEventType.Updated],\n  [ChatMessageAction.MessageDelete, ChatMessageEventType.Deleted],\n]);\n\n/**\n * The order in which results should be returned when performing a paginated query (e.g. message history).\n */\nexport enum OrderBy {\n  /**\n   * Return results in ascending order (oldest first).\n   */\n  OldestFirst = 'oldestFirst',\n\n  /**\n   * Return results in descending order (newest first).\n   */\n  NewestFirst = 'newestFirst',\n}\n\n/**\n * Parameters for querying messages in a chat room.\n */\nexport interface HistoryParams {\n  /**\n   * The start of the time window to query from. If provided, the response will include\n   * messages with timestamps equal to or greater than this value.\n   * @defaultValue The beginning of time\n   */\n  start?: number;\n\n  /**\n   * The end of the time window to query from. If provided, the response will include\n   * messages with timestamps less than this value.\n   * @defaultValue Now\n   */\n  end?: number;\n\n  /**\n   * The maximum number of messages to return in the response.\n   * @defaultValue 100\n   */\n  limit?: number;\n\n  /**\n   * The direction to query messages in.\n   * If {@link OrderBy.OldestFirst}, the response will include messages from the start of the time window to the end.\n   * If {@link OrderBy.NewestFirst}, the response will include messages from the end of the time window to the start.\n   * If not provided, the default is {@link OrderBy.NewestFirst}.\n   * @defaultValue {@link OrderBy.NewestFirst}\n   */\n  orderBy?: OrderBy;\n}\n\n/**\n * The parameters supplied to a message action like delete or update.\n */\nexport interface OperationDetails {\n  /**\n   * Optional description for the message action.\n   */\n  description?: string;\n\n  /**\n   * Optional metadata that will be added to the action. Defaults to empty.\n   *\n   */\n  metadata?: MessageOperationMetadata;\n}\n\n/**\n * Parameters for updating a message.\n */\nexport interface UpdateMessageParams {\n  /**\n   * The new text of the message.\n   */\n  text: string;\n\n  /**\n   * Optional metadata of the message.\n   */\n  metadata?: MessageMetadata;\n\n  /**\n   * Optional headers of the message.\n   */\n  headers?: MessageHeaders;\n}\n\n/**\n * Params for sending a text message. Only `text` is mandatory.\n */\nexport interface SendMessageParams {\n  /**\n   * The text of the message.\n   */\n  text: string;\n\n  /**\n   * Optional metadata of the message.\n   *\n   * The metadata is a map of extra information that can be attached to chat\n   * messages. It is not used by Ably and is sent as part of the realtime\n   * message payload. Example use cases are setting custom styling like\n   * background or text colors or fonts, adding links to external images,\n   * emojis, etc.\n   *\n   * Do not use metadata for authoritative information. There is no server-side\n   * validation. When reading the metadata, treat it like user input.\n   *\n   */\n  metadata?: MessageMetadata;\n\n  /**\n   * Optional headers of the message.\n   *\n   * The headers are a flat key-value map and are sent as part of the realtime\n   * message's extras inside the `headers` property. They can serve similar\n   * purposes as the metadata, but they are read by Ably and can be used for\n   * features such as\n   * [subscription filters](https://faqs.ably.com/subscription-filters).\n   *\n   * Do not use the headers for authoritative information. There is no\n   * server-side validation. When reading the headers, treat them like user\n   * input.\n   *\n   */\n  headers?: MessageHeaders;\n}\n\n/**\n * A listener for message events in a chat room.\n * @param event The message event that was received.\n */\nexport type MessageListener = (event: ChatMessageEvent) => void;\n\n/**\n * A response object that allows you to control a message subscription.\n */\nexport interface MessageSubscriptionResponse extends Subscription {\n  /**\n   * Get the previous messages that were sent to the room before the listener was subscribed.\n   *\n   * If the client experiences a discontinuity event (i.e. the connection was lost and could not be resumed), the starting point of\n   * historyBeforeSubscribe will be reset.\n   *\n   * Calls to historyBeforeSubscribe will wait for continuity to be restored before resolving.\n   *\n   * Once continuity is restored, the subscription point will be set to the beginning of this new period of continuity. To\n   * ensure that no messages are missed, you should call historyBeforeSubscribe after any period of discontinuity to\n   * fill any gaps in the message history.\n   *\n   * ```typescript\n   * const { historyBeforeSubscribe } = room.messages.subscribe(listener);\n   * await historyBeforeSubscribe({ limit: 10 });\n   * ```\n   * @param params Parameters for the history query.\n   * @returns A promise that resolves with the paginated result of messages, in newest-to-oldest order.\n   */\n  historyBeforeSubscribe(params: Omit<HistoryParams, 'orderBy'>): Promise<PaginatedResult<Message>>;\n}\n\n/**\n * This interface is used to interact with messages in a chat room: subscribing\n * to new messages, fetching history, or sending messages.\n *\n * Get an instance via {@link Room.messages}.\n */\nexport interface Messages {\n  /**\n   * Subscribe to new messages in this chat room.\n   * @param listener callback that will be called\n   * @returns A response object that allows you to control the subscription.\n   */\n  subscribe(listener: MessageListener): MessageSubscriptionResponse;\n\n  /**\n   * Get messages that have been previously sent to the chat room, based on the provided parameters.\n   * @param params Parameters for the query.\n   * @returns A promise that resolves with the paginated result of messages. This paginated result can\n   * be used to fetch more messages if available.\n   */\n  history(params: HistoryParams): Promise<PaginatedResult<Message>>;\n\n  /**\n   * Get a message by its serial.\n   * @param serial The serial of the message to get.\n   * @returns A promise that resolves with the message.\n   */\n  get(serial: string): Promise<Message>;\n\n  /**\n   * Send a message in the chat room.\n   *\n   * This method uses the Ably Chat API endpoint for sending messages.\n   *\n   * Note that the Promise may resolve before OR after the message is received\n   * from the realtime channel. This means you may see the message that was just\n   * sent in a callback to `subscribe` before the returned promise resolves.\n   * @param params an object containing {text, headers, metadata} for the message\n   * to be sent. Text is required, metadata and headers are optional.\n   * @returns A promise that resolves when the message was published.\n   */\n  send(params: SendMessageParams): Promise<Message>;\n\n  /**\n   * Delete a message in the chat room.\n   *\n   * This method uses the Ably Chat API REST endpoint for deleting messages.\n   * It performs a `soft` delete, meaning the message is marked as deleted.\n   *\n   * Note that the Promise may resolve before OR after the message is deleted\n   * from the realtime channel. This means you may see the message that was just\n   * deleted in a callback to `subscribe` before the returned promise resolves.\n   *\n   * NOTE: The Message instance returned by this method is the state of the message as a result of the delete operation.\n   * If you have a subscription to message events via `subscribe`, you should discard the message instance returned by\n   * this method and use the event payloads from the subscription instead.\n   *\n   * Should you wish to restore a deleted message, and providing you have the appropriate permissions,\n   * you can simply send an update to the original message.\n   * Note: This is subject to change in future versions, whereby a new permissions model will be introduced\n   * and a deleted message may not be restorable in this way.\n   * @returns A promise that resolves when the message was deleted.\n   * @param serial - The serial of the message to delete.\n   * @param details - Optional details to record about the delete action.\n   * @returns A promise that resolves to the deleted message.\n   */\n  delete(serial: string, details?: OperationDetails): Promise<Message>;\n\n  /**\n   * Update a message in the chat room.\n   *\n   * Note that the Promise may resolve before OR after the updated message is\n   * received from the realtime channel. This means you may see the update that\n   * was just sent in a callback to `subscribe` before the returned promise\n   * resolves.\n   *\n   * NOTE: The Message instance returned by this method is the state of the message as a result of the update operation.\n   * If you have a subscription to message events via `subscribe`, you should discard the message instance returned by\n   * this method and use the event payloads from the subscription instead.\n   *\n   * This method uses PUT-like semantics: if headers and metadata are omitted from the updateParams, then\n   * the existing headers and metadata are replaced with the empty objects.\n   * @param serial - The serial of the message to update.\n   * @param updateParams - The parameters for updating the message.\n   * @param details - Optional details to record about the update action.\n   * @returns A promise of the updated message.\n   */\n  update(serial: string, updateParams: UpdateMessageParams, details?: OperationDetails): Promise<Message>;\n\n  /**\n   * Send, delete, and subscribe to message reactions.\n   */\n  reactions: MessageReactions;\n}\n\n/**\n * @inheritDoc\n */\nexport class DefaultMessages implements Messages {\n  private readonly _roomName: string;\n  private readonly _options: MessagesOptions;\n  private readonly _channel: Ably.RealtimeChannel;\n  private readonly _chatApi: ChatApi;\n  private readonly _listenerSubscriptionPoints: Map<\n    MessageListener,\n    Promise<{\n      fromSerial: string;\n    }>\n  >;\n  private readonly _pendingPromiseRejecters = new Set<(error: Error) => void>();\n  private readonly _pendingAttachListeners = new Set<() => void>();\n  private readonly _logger: Logger;\n  private readonly _emitter = new EventEmitter<MessageEventsMap>();\n  private readonly _unsubscribeMessageEvents: () => void;\n  private readonly _offChannelAttached: () => void;\n  private readonly _offChannelUpdate: () => void;\n  private readonly _reactions: DefaultMessageReactions;\n\n  /**\n   * Constructs a new `DefaultMessages` instance.\n   * @param roomName The unique identifier of the room.\n   * @param options The room options for the messages.\n   * @param channel An instance of the Realtime channel for the room.\n   * @param chatApi An instance of the ChatApi.\n   * @param logger An instance of the Logger.\n   */\n  constructor(\n    roomName: string,\n    options: MessagesOptions,\n    channel: Ably.RealtimeChannel,\n    chatApi: ChatApi,\n    logger: Logger,\n  ) {\n    this._roomName = roomName;\n    this._options = options;\n    this._channel = channel;\n    this._chatApi = chatApi;\n    this._logger = logger;\n    this._listenerSubscriptionPoints = new Map<MessageListener, Promise<{ fromSerial: string }>>();\n\n    this._reactions = new DefaultMessageReactions(this._logger, options, this._chatApi, this._roomName, this._channel);\n\n    // Create bound listeners\n    const messageEventsListener = this._processEvent.bind(this);\n    const channelAttachedListener = (stateChange: Ably.ChannelStateChange) => {\n      this._handleAttach(stateChange.resumed);\n    };\n    const channelUpdateListener = (stateChange: Ably.ChannelStateChange) => {\n      if (stateChange.current === 'attached' && stateChange.previous === 'attached') {\n        this._handleAttach(stateChange.resumed);\n      }\n    };\n\n    // Use subscription helpers to create cleanup functions\n    this._unsubscribeMessageEvents = subscribe(this._channel, [RealtimeMessageName.ChatMessage], messageEventsListener);\n    this._offChannelAttached = on(this._channel, 'attached', channelAttachedListener);\n    this._offChannelUpdate = on(this._channel, 'update', channelUpdateListener);\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get reactions(): MessageReactions {\n    return this._reactions;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  private async _getBeforeSubscriptionStart(\n    listener: MessageListener,\n    params: Omit<HistoryParams, 'orderBy'>,\n  ): Promise<PaginatedResult<Message>> {\n    this._logger.trace(`DefaultSubscriptionManager.getBeforeSubscriptionStart();`);\n\n    const subscriptionPoint = this._listenerSubscriptionPoints.get(listener);\n\n    if (subscriptionPoint === undefined) {\n      this._logger.error(`DefaultSubscriptionManager.getBeforeSubscriptionStart(); listener has not been subscribed`);\n      throw new Ably.ErrorInfo(\n        'unable to query history; listener has not been subscribed',\n        ErrorCode.ListenerNotSubscribed,\n        400,\n      ) as unknown as Error;\n    }\n\n    // Get the subscription point of the listener\n    const subscriptionPointParams = await subscriptionPoint;\n\n    // Query messages from the subscription point to the start of the time window\n    return this._chatApi.history(this._roomName, {\n      ...params,\n      orderBy: OrderBy.NewestFirst,\n      ...subscriptionPointParams,\n    });\n  }\n\n  /**\n   * Handle the case where the channel experiences a detach and reattaches.\n   * @param fromResume Whether the attach is from a resume operation.\n   */\n  private _handleAttach(fromResume: boolean) {\n    this._logger.trace(`DefaultSubscriptionManager.handleAttach();`);\n\n    // Do nothing if we have resumed as there is no discontinuity in the message stream\n    if (fromResume) return;\n\n    // Reset subscription points for all listeners\n    const newSubscriptionStartResolver = this._subscribeAtChannelAttach();\n    for (const [listener] of this._listenerSubscriptionPoints.entries()) {\n      this._listenerSubscriptionPoints.set(listener, newSubscriptionStartResolver);\n    }\n  }\n\n  /**\n   * Create a promise that resolves with the attachSerial of the channel or the serial of the latest message.\n   * @returns A promise that resolves to an object containing fromSerial and subscriptionPoint.\n   */\n  private async _resolveSubscriptionStart(): Promise<{\n    fromSerial: string;\n  }> {\n    const channelWithProperties = this._getChannelProperties();\n\n    // If we are attached, we can resolve with the channelSerial\n    if (channelWithProperties.state === 'attached') {\n      if (channelWithProperties.properties.channelSerial) {\n        return { fromSerial: channelWithProperties.properties.channelSerial };\n      }\n      this._logger.error(`DefaultSubscriptionManager.handleAttach(); channelSerial is undefined`);\n      throw new Ably.ErrorInfo(\n        'unable to query messages; channel is attached but channelSerial is not defined',\n        ErrorCode.ChannelSerialNotDefined,\n        500,\n      ) as unknown as Error;\n    }\n\n    return this._subscribeAtChannelAttach();\n  }\n\n  private _getChannelProperties(): Ably.RealtimeChannel & {\n    properties: { attachSerial: string | undefined; channelSerial: string | undefined };\n  } {\n    // Get the attachSerial from the channel properties\n    return this._channel as Ably.RealtimeChannel & {\n      properties: {\n        attachSerial: string | undefined;\n        channelSerial: string | undefined;\n      };\n    };\n  }\n\n  private async _subscribeAtChannelAttach(): Promise<{ fromSerial: string }> {\n    const channelWithProperties = this._getChannelProperties();\n    return new Promise((resolve, reject) => {\n      // Store the reject function so we can call it during disposal\n      this._pendingPromiseRejecters.add(reject);\n\n      const cleanup = () => {\n        this._pendingPromiseRejecters.delete(reject);\n      };\n\n      // Check if the state is now attached\n      if (channelWithProperties.state === 'attached') {\n        // Get the attachSerial from the channel properties\n        // AttachSerial should always be defined at this point, but we check just in case\n        this._logger.debug('Messages._subscribeAtChannelAttach(); channel is attached already, using attachSerial', {\n          attachSerial: channelWithProperties.properties.attachSerial,\n        });\n        cleanup();\n\n        if (channelWithProperties.properties.attachSerial) {\n          resolve({ fromSerial: channelWithProperties.properties.attachSerial });\n        } else {\n          this._logger.error(`DefaultSubscriptionManager.handleAttach(); attachSerial is undefined`);\n          cleanup();\n          reject(\n            new Ably.ErrorInfo(\n              'unable to query messages; channel is attached but attachSerial is not defined',\n              ErrorCode.ChannelSerialNotDefined,\n              500,\n            ) as unknown as Error,\n          );\n        }\n        return;\n      }\n\n      const offAttachedListener = once(channelWithProperties, 'attached', () => {\n        // Get the attachSerial from the channel properties\n        // AttachSerial should always be defined at this point, but we check just in case\n        this._logger.debug('Messages._subscribeAtChannelAttach(); channel is now attached, using attachSerial', {\n          attachSerial: channelWithProperties.properties.attachSerial,\n        });\n        cleanup();\n        this._pendingAttachListeners.delete(offAttachedListener);\n\n        if (channelWithProperties.properties.attachSerial) {\n          resolve({ fromSerial: channelWithProperties.properties.attachSerial });\n        } else {\n          this._logger.error(`DefaultSubscriptionManager.handleAttach(); attachSerial is undefined`);\n          reject(\n            new Ably.ErrorInfo(\n              'unable to query messages; channel is attached but attachSerial is not defined',\n              ErrorCode.ChannelSerialNotDefined,\n              500,\n            ) as unknown as Error,\n          );\n        }\n      });\n\n      this._pendingAttachListeners.add(offAttachedListener);\n    });\n  }\n\n  /**\n   * @inheritdoc\n   */\n  async history(options: HistoryParams): Promise<PaginatedResult<Message>> {\n    this._logger.trace('Messages.query();');\n    return this._chatApi.history(this._roomName, options);\n  }\n\n  /**\n   * @inheritdoc\n   */\n  async get(serial: string): Promise<Message> {\n    this._logger.trace('Messages.get();', { serial });\n    return this._chatApi.getMessage(this._roomName, serial);\n  }\n\n  /**\n   * @inheritdoc\n   */\n  async send(params: SendMessageParams): Promise<Message> {\n    this._logger.trace('Messages.send();', { params });\n\n    const { text, metadata, headers } = params;\n\n    const response = await this._chatApi.sendMessage(this._roomName, { text, headers, metadata });\n    return messageFromRest(response);\n  }\n\n  /**\n   * @inheritdoc\n   */\n  async delete(serial: string, details?: OperationDetails): Promise<Message> {\n    this._logger.trace('Messages.delete();', { serial, details });\n    // Spec: CHA-M9f\n    assertValidSerial(serial, 'delete message', 'serial');\n    const response = await this._chatApi.deleteMessage(this._roomName, serial, details);\n\n    return messageFromRest(response);\n  }\n\n  /**\n   * @inheritdoc\n   */\n  async update(serial: string, updateParams: UpdateMessageParams, details?: OperationDetails): Promise<Message> {\n    this._logger.trace('Messages.update();', { serial, updateParams, details });\n    // Spec: CHA-M8g\n    assertValidSerial(serial, 'update message', 'serial');\n    const response = await this._chatApi.updateMessage(this._roomName, serial, {\n      message: {\n        text: updateParams.text,\n        metadata: updateParams.metadata,\n        headers: updateParams.headers,\n      },\n      ...details,\n    });\n\n    this._logger.debug('Messages.update(); message update successfully', { updateParams });\n    return messageFromRest(response);\n  }\n\n  /**\n   * @inheritdoc\n   */\n  subscribe(listener: MessageListener): MessageSubscriptionResponse {\n    this._logger.trace('Messages.subscribe();');\n    const wrapped = wrap(listener);\n    this._emitter.on(\n      [ChatMessageEventType.Created, ChatMessageEventType.Updated, ChatMessageEventType.Deleted],\n      wrapped,\n    );\n\n    // Set the subscription point to a promise that resolves when the channel attaches or with the latest message\n    const resolvedSubscriptionStart = this._resolveSubscriptionStart();\n\n    // Add a handler for unhandled rejections in case the room is released before the subscription point is resolved\n    resolvedSubscriptionStart.catch(() => {\n      this._logger.debug('Messages.subscribe(); subscription point was not resolved before the room was released');\n    });\n\n    this._listenerSubscriptionPoints.set(wrapped, resolvedSubscriptionStart);\n\n    return {\n      unsubscribe: () => {\n        // Remove the wrapped listener from the subscription points\n        this._listenerSubscriptionPoints.delete(wrapped);\n        this._logger.trace('Messages.unsubscribe();');\n        this._emitter.off(wrapped);\n      },\n      historyBeforeSubscribe: async (params: Omit<HistoryParams, 'orderBy'>) =>\n        this._getBeforeSubscriptionStart(wrapped, params),\n    };\n  }\n\n  private _processEvent(channelEventMessage: Ably.InboundMessage) {\n    this._logger.trace('Messages._processEvent();', {\n      channelEventMessage,\n    });\n    const { action } = channelEventMessage;\n    const event = MessageActionsToEventsMap.get(action as ChatMessageAction);\n    if (!event) {\n      this._logger.debug('Messages._processEvent(); received unknown message action', { action });\n      return;\n    }\n    // Send the message to the listeners\n    const message = parseMessage(channelEventMessage);\n    this._emitter.emit(event, { type: event, message: message });\n  }\n\n  /**\n   * Disposes of the messages instance, removing all listeners and subscriptions.\n   * This method should be called when the room is being released to ensure proper cleanup.\n   * @internal\n   */\n  dispose(): void {\n    this._logger.trace('DefaultMessages.dispose();');\n\n    // Remove all user-level listeners from the emitter\n    this._emitter.off();\n\n    // Reject all pending subscription point promises to break circular references\n    const disposalError = new Ably.ErrorInfo(\n      'unable to query messages; room has been disposed',\n      ErrorCode.ResourceDisposed,\n      400,\n    ) as unknown as Error;\n    for (const rejectFn of this._pendingPromiseRejecters) {\n      try {\n        rejectFn(disposalError);\n      } catch {\n        // Ignore errors from already resolved/rejected promises\n      }\n    }\n    this._pendingPromiseRejecters.clear();\n\n    // Clear all subscription points\n    this._listenerSubscriptionPoints.clear();\n\n    // Remove all pending attach listeners\n    for (const offAttachedListener of this._pendingAttachListeners) {\n      offAttachedListener();\n    }\n    this._pendingAttachListeners.clear();\n\n    // Unsubscribe from channel events using stored unsubscribe functions\n    this._unsubscribeMessageEvents();\n\n    // Remove specific channel state listeners using stored unsubscribe functions\n    this._offChannelAttached();\n    this._offChannelUpdate();\n\n    // Dispose of the reactions instance\n    this._reactions.dispose();\n\n    this._logger.debug('DefaultMessages.dispose(); disposed successfully');\n  }\n\n  /**\n   * Checks if there are any listeners registered by users.\n   * @internal\n   * @returns true if there are listeners, false otherwise.\n   */\n  hasListeners(): boolean {\n    return emitterHasListeners(this._emitter);\n  }\n}\n","import * as Ably from 'ably';\n\nimport { ErrorCode } from './errors.js';\nimport { Logger } from './logger.js';\nimport { Message, MessageHeaders, MessageMetadata, MessageOperationMetadata } from './message.js';\nimport { OrderBy } from './messages.js';\nimport { OccupancyData } from './occupancy-parser.js';\nimport { PaginatedResult } from './query.js';\nimport { messageFromRest, RestMessage } from './rest-types.js';\n\nexport interface HistoryQueryParams {\n  start?: number;\n  end?: number;\n  orderBy?: OrderBy;\n  limit?: number;\n  /**\n   * Serial indicating the starting point for message retrieval.\n   * This serial is specific to the region of the channel the client is connected to. Messages published within\n   * the same region of the channel are guaranteed to be received in increasing serial order.\n   * @defaultValue undefined (not used if not specified)\n   */\n  fromSerial?: string;\n}\n\n/**\n * In the REST API, we currently use the `direction` query parameter to specify the order of messages instead\n * of orderBy. So define this type for conversion purposes.\n */\ntype ApiHistoryQueryParams = Omit<HistoryQueryParams, 'orderBy'> & {\n  direction?: 'forwards' | 'backwards';\n};\n\ninterface SendMessageParams {\n  text: string;\n  metadata?: MessageMetadata;\n  headers?: MessageHeaders;\n}\n\ntype UpdateMessageResponse = RestMessage;\n\ntype DeleteMessageResponse = RestMessage;\n\ninterface UpdateMessageParams {\n  /**\n   * Message data to update. All fields are updated and, if omitted, they are\n   * set to empty.\n   */\n  message: {\n    text: string;\n    metadata?: MessageMetadata;\n    headers?: MessageHeaders;\n  };\n\n  /** Description of the update action */\n  description?: string;\n\n  /** Metadata of the update action */\n  metadata?: MessageOperationMetadata;\n}\n\ninterface OperationDetails {\n  /** Description of the operation */\n  description?: string;\n\n  /** Metadata of the operation */\n  metadata?: MessageOperationMetadata;\n}\n\n/**\n * Parameters for sending a message reaction.\n */\nexport interface SendMessageReactionParams {\n  /**\n   * The type of reaction, must be one of {@link MessageReactionType}.\n   */\n  type: string;\n\n  /**\n   * The reaction name to add; ie. the emoji.\n   */\n  name: string;\n\n  /**\n   * The count of the reaction for type {@link MessageReactionType.Multiple}.\n   * Defaults to 1 if not set. Not supported for other reaction types.\n   * @defaultValue 1\n   */\n  count?: number;\n}\n\n/**\n * Parameters for deleting a message reaction.\n */\nexport interface DeleteMessageReactionParams {\n  /**\n   * The type of reaction, must be one of {@link MessageReactionType}.\n   */\n  type: string;\n\n  /**\n   * The reaction name to remove, ie. the emoji. Required for all reaction types\n   * except {@link MessageReactionType.Unique}.\n   */\n  name?: string;\n}\n\n/**\n * Bridge for the Chat REST API.\n * @internal\n */\nexport class ChatApi {\n  private readonly _realtime: Ably.Realtime;\n  private readonly _logger: Logger;\n  private readonly _apiProtocolVersion: number = 4;\n\n  constructor(realtime: Ably.Realtime, logger: Logger) {\n    this._realtime = realtime;\n    this._logger = logger;\n  }\n\n  async history(roomName: string, params: HistoryQueryParams): Promise<PaginatedResult<Message>> {\n    // convert the params into internal format\n    const apiParams: ApiHistoryQueryParams = { ...params };\n    if (params.orderBy) {\n      switch (params.orderBy) {\n        case OrderBy.NewestFirst: {\n          apiParams.direction = 'backwards';\n          break;\n        }\n        case OrderBy.OldestFirst: {\n          apiParams.direction = 'forwards';\n          break;\n        }\n        default: {\n          // in vanilla JS use-cases, without types, we need to check non-enum values\n          throw new Ably.ErrorInfo(\n            // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n            `unable to query messages; invalid orderBy value: ${params.orderBy}`,\n            ErrorCode.InvalidArgument,\n            400,\n          );\n        }\n      }\n    }\n\n    const data = await this._makeAuthorizedPaginatedRequest<RestMessage>(\n      this._roomUrl(roomName, '/messages'),\n      apiParams,\n    );\n    return this._recursivePaginateMessages(data);\n  }\n\n  private _recursivePaginateMessages(data: PaginatedResult<RestMessage>): PaginatedResult<Message> {\n    const result: PaginatedResult<Message> = {} as PaginatedResult<Message>;\n    result.items = data.items.map((payload) => messageFromRest(payload));\n\n    // Recursively map the next paginated data\n    result.next = async () => {\n      const nextData = await data.next();\n      // eslint-disable-next-line unicorn/no-null\n      return nextData ? this._recursivePaginateMessages(nextData) : null;\n    };\n\n    result.first = async () => {\n      const firstData = await data.first();\n      return this._recursivePaginateMessages(firstData);\n    };\n\n    result.current = async () => {\n      const currentData = await data.current();\n      return this._recursivePaginateMessages(currentData);\n    };\n\n    result.hasNext = () => data.hasNext();\n\n    result.isLast = () => data.isLast();\n\n    return { ...data, ...result };\n  }\n\n  async getMessage(roomName: string, serial: string): Promise<Message> {\n    const restMessage = await this._makeAuthorizedRequest<RestMessage>(this._messageUrl(roomName, serial), 'GET');\n    return messageFromRest(restMessage);\n  }\n\n  async deleteMessage(roomName: string, serial: string, details?: OperationDetails): Promise<DeleteMessageResponse> {\n    const body = {\n      ...(details?.description && { description: details.description }),\n      ...(details?.metadata && { metadata: details.metadata }),\n    };\n    return this._makeAuthorizedRequest<DeleteMessageResponse>(\n      this._messageUrl(roomName, serial, '/delete'),\n      'POST',\n      body,\n      {},\n    );\n  }\n\n  async sendMessage(roomName: string, params: SendMessageParams): Promise<RestMessage> {\n    const body = {\n      text: params.text,\n      ...(params.metadata && { metadata: params.metadata }),\n      ...(params.headers && { headers: params.headers }),\n    };\n    return this._makeAuthorizedRequest<RestMessage>(this._roomUrl(roomName, '/messages'), 'POST', body);\n  }\n\n  async updateMessage(roomName: string, serial: string, params: UpdateMessageParams): Promise<UpdateMessageResponse> {\n    return this._makeAuthorizedRequest<UpdateMessageResponse>(this._messageUrl(roomName, serial), 'PUT', params);\n  }\n\n  async sendMessageReaction(roomName: string, serial: string, data: SendMessageReactionParams): Promise<void> {\n    return this._makeAuthorizedRequest(this._messageUrl(roomName, serial, '/reactions'), 'POST', data);\n  }\n\n  async deleteMessageReaction(roomName: string, serial: string, data: DeleteMessageReactionParams): Promise<void> {\n    return this._makeAuthorizedRequest(this._messageUrl(roomName, serial, '/reactions'), 'DELETE', undefined, data);\n  }\n\n  async getClientReactions(roomName: string, serial: string, clientId?: string): Promise<Message['reactions']> {\n    const params = clientId ? { forClientId: clientId } : {};\n    return this._makeAuthorizedRequest<Message['reactions']>(\n      this._messageUrl(roomName, serial, '/client-reactions'),\n      'GET',\n      undefined,\n      params,\n    );\n  }\n\n  async getOccupancy(roomName: string): Promise<OccupancyData> {\n    return this._makeAuthorizedRequest<OccupancyData>(this._roomUrl(roomName, '/occupancy'), 'GET');\n  }\n\n  private async _makeAuthorizedRequest<RES = undefined>(\n    url: string,\n    method: 'POST' | 'GET' | 'PUT' | 'DELETE' | 'PATCH',\n    body?: unknown,\n    params?: unknown,\n  ): Promise<RES> {\n    const response = await this._doRequest(url, method, params, body);\n    return response.items[0] as RES;\n  }\n\n  private async _makeAuthorizedPaginatedRequest<RES>(\n    url: string,\n    params?: unknown,\n    body?: unknown,\n  ): Promise<PaginatedResult<RES>> {\n    return this._doRequest(url, 'GET', params, body);\n  }\n\n  private async _doRequest<RES>(\n    url: string,\n    method: 'POST' | 'GET' | 'PUT' | 'DELETE' | 'PATCH',\n    params?: unknown,\n    body?: unknown,\n  ): Promise<PaginatedResult<RES>> {\n    const response = await this._realtime.request(method, url, this._apiProtocolVersion, params, body);\n    if (!response.success) {\n      this._logger.error('ChatApi._doRequest(); failed to make request', {\n        url,\n        method,\n        statusCode: response.statusCode,\n        errorCode: response.errorCode,\n        errorMessage: response.errorMessage,\n      });\n      throw new Ably.ErrorInfo(response.errorMessage, response.errorCode, response.statusCode);\n    }\n\n    return response;\n  }\n\n  /**\n   * Returns a URL for a specific room.\n   * @param roomName Name of the room\n   * @param suffix The suffix to add to the room URL, prefixed with /\n   * @returns string The formatted URL\n   */\n  private _roomUrl(roomName: string, suffix = ''): string {\n    return `/chat/v4/rooms/${encodeURIComponent(roomName)}${suffix}`;\n  }\n\n  /**\n   * Returns a URL for a specific message in the room.\n   * @param roomName string Name of the room\n   * @param serial string The serial of the message\n   * @param suffix The suffix to add to the room URL, prefixed with /\n   * @returns string The formatted URL\n   */\n  private _messageUrl(roomName: string, serial: string, suffix = ''): string {\n    return `${this._roomUrl(roomName, '/messages')}/${encodeURIComponent(serial)}${suffix}`;\n  }\n}\n","/**\n * Gets the single main channel for the chat room.\n * @param roomName The room name.\n * @returns  The channel name.\n */\nexport const roomChannelName = (roomName: string): string => `${roomName}::$chat`;\n","import * as Ably from 'ably';\n\n// Update this when you release a new version\nexport const VERSION = '1.1.0';\nexport const CHANNEL_OPTIONS_AGENT_STRING = `chat-js/${VERSION}`;\nexport const CHANNEL_OPTIONS_AGENT_STRING_REACT = `chat-react/${VERSION}`;\n// Modes required for basic message functionality\nexport const DEFAULT_CHANNEL_MODES: Ably.ChannelMode[] = ['PUBLISH', 'SUBSCRIBE'];\nexport const DEFAULT_CHANNEL_OPTIONS: Ably.ChannelOptions = {\n  params: { agent: CHANNEL_OPTIONS_AGENT_STRING },\n  attachOnSubscribe: false,\n  modes: DEFAULT_CHANNEL_MODES,\n};\n\nexport const DEFAULT_CHANNEL_OPTIONS_REACT: Ably.ChannelOptions = {\n  // Spec: CHA-IN1b1\n  params: { agent: `${CHANNEL_OPTIONS_AGENT_STRING} ${CHANNEL_OPTIONS_AGENT_STRING_REACT}` },\n  attachOnSubscribe: false,\n  modes: DEFAULT_CHANNEL_MODES,\n};\n","import * as Ably from 'ably';\n\nimport { roomChannelName } from './channel.js';\nimport { ErrorCode } from './errors.js';\nimport { Logger } from './logger.js';\nimport { DEFAULT_CHANNEL_OPTIONS, DEFAULT_CHANNEL_OPTIONS_REACT } from './version.js';\n\nexport type ChannelOptionsWithModes = Omit<Ably.ChannelOptions, 'modes'> & Required<Pick<Ably.ChannelOptions, 'modes'>>;\n\nexport type ChannelOptionsMerger = (options: ChannelOptionsWithModes) => ChannelOptionsWithModes;\n\nexport class ChannelManager {\n  private readonly _realtime: Ably.Realtime;\n  private readonly _logger: Logger;\n  private _registeredOptions: ChannelOptionsWithModes;\n  private readonly _isReact: boolean;\n  private _resolvedChannel?: Ably.RealtimeChannel;\n  private readonly _channelId: string;\n\n  constructor(roomName: string, realtime: Ably.Realtime, logger: Logger, isReact: boolean) {\n    logger.trace('ChannelManager();', { isReact });\n    this._realtime = realtime;\n    this._logger = logger;\n    this._isReact = isReact;\n    this._registeredOptions = this._defaultChannelOptions();\n    this._channelId = roomChannelName(roomName);\n  }\n\n  mergeOptions(merger: ChannelOptionsMerger): void {\n    this._logger.trace('ChannelManager.mergeOptions();');\n    if (this._resolvedChannel) {\n      this._logger.error('unable to modify channel options; channel has already been requested');\n      throw new Ably.ErrorInfo(\n        'unable to modify channel options; channel has already been requested',\n        ErrorCode.ChannelOptionsCannotBeModified,\n        400,\n      );\n    }\n\n    this._registeredOptions = merger(this._registeredOptions);\n  }\n\n  get(): Ably.RealtimeChannel {\n    this._logger.trace('ChannelManager.get();');\n\n    this._resolvedChannel ??= this._realtime.channels.get(this._channelId, this._registeredOptions);\n\n    return this._resolvedChannel;\n  }\n\n  release(): void {\n    this._logger.trace('ChannelManager.release();', { channelId: this._channelId });\n    if (!this._resolvedChannel) {\n      return;\n    }\n\n    this._realtime.channels.release(this._channelId);\n  }\n\n  private _defaultChannelOptions(): ChannelOptionsWithModes {\n    this._logger.trace('ChannelManager._defaultChannelOptions();');\n\n    // Spec: CHA-IN1e\n    const baseOptions = this._isReact ? DEFAULT_CHANNEL_OPTIONS_REACT : DEFAULT_CHANNEL_OPTIONS;\n    this._logger.trace(this._isReact ? 'using react channel options' : 'using default channel options');\n\n    // Create a deep copy of the options, ensuring modes array is also copied\n    return { ...baseOptions, modes: [...(baseOptions.modes ?? [])] } as ChannelOptionsWithModes;\n  }\n}\n","import * as Ably from 'ably';\n\n/**\n * Represents the occupancy data of a chat room.\n */\nexport interface OccupancyData {\n  /**\n   * The number of connections to the chat room.\n   */\n  connections: number;\n\n  /**\n   * The number of presence members in the chat room - members who have entered presence.\n   */\n  presenceMembers: number;\n}\n\n/**\n * Represents the structure of an occupancy message payload.\n */\ninterface OccupancyPayload {\n  data?: {\n    metrics?: {\n      connections?: number;\n      presenceMembers?: number;\n    };\n  };\n}\n\n/**\n * Parses occupancy data from an Ably message, using fallback values of 0 for invalid data.\n * @param message The Ably message containing occupancy data\n * @returns Parsed occupancy data with fallback values for invalid fields\n */\nexport const parseOccupancyMessage = (message: Ably.InboundMessage): OccupancyData => {\n  const payload = message as OccupancyPayload;\n  let connections = 0;\n  let presenceMembers = 0;\n\n  // Check if data is a valid object\n  if (!payload.data || typeof payload.data !== 'object') {\n    return { connections, presenceMembers };\n  }\n\n  const { metrics } = payload.data;\n\n  // Check if metrics is undefined or null\n  if (!metrics || typeof metrics !== 'object') {\n    return { connections, presenceMembers };\n  }\n\n  // Parse connections\n  if (typeof metrics.connections === 'number' && Number.isInteger(metrics.connections)) {\n    connections = metrics.connections;\n  }\n\n  // Parse presenceMembers\n  if (typeof metrics.presenceMembers === 'number' && Number.isInteger(metrics.presenceMembers)) {\n    presenceMembers = metrics.presenceMembers;\n  }\n\n  return { connections, presenceMembers };\n};\n","import * as Ably from 'ably';\n\nimport { ChannelOptionsMerger } from './channel-manager.js';\nimport { ChatApi } from './chat-api.js';\nimport { ErrorCode } from './errors.js';\nimport { OccupancyEvent, OccupancyEventType, RealtimeMetaEventType } from './events.js';\nimport { Logger } from './logger.js';\nimport { OccupancyData, parseOccupancyMessage } from './occupancy-parser.js';\nimport { subscribe } from './realtime-subscriptions.js';\nimport { InternalRoomOptions } from './room-options.js';\nimport { Subscription } from './subscription.js';\nimport EventEmitter, { emitterHasListeners, wrap } from './utils/event-emitter.js';\n\n/**\n * This interface is used to interact with occupancy in a chat room: subscribing to occupancy updates and\n * fetching the current room occupancy metrics.\n *\n * Get an instance via {@link Room.occupancy}.\n */\nexport interface Occupancy {\n  /**\n   * Subscribe a given listener to occupancy updates of the chat room.\n   *\n   * Note: This requires occupancy events to be enabled via the `enableEvents` option in\n   * the {@link OccupancyOptions} options provided to the room. If this is not enabled, an error will be thrown.\n   * @param listener A listener to be called when the occupancy of the room changes.\n   * @returns A subscription object that can be used to unsubscribe the listener.\n   * @throws {Ably.ErrorInfo} If occupancy events are not enabled for this room.\n   */\n  subscribe(listener: OccupancyListener): Subscription;\n\n  /**\n   * Get the current occupancy of the chat room.\n   * @returns A promise that resolves to the current occupancy of the chat room.\n   */\n  get(): Promise<OccupancyData>;\n\n  /**\n   * Get the latest occupancy data received from realtime events.\n   * @returns The latest occupancy data, or undefined if no realtime events have been received yet.\n   * @throws {Ably.ErrorInfo} If occupancy events are not enabled for this room.\n   */\n  get current(): OccupancyData | undefined;\n}\n\n/**\n * A listener that is called when the occupancy of a chat room changes.\n * @param event The occupancy event.\n */\nexport type OccupancyListener = (event: OccupancyEvent) => void;\n\ninterface OccupancyEventsMap {\n  [OccupancyEventType.Updated]: OccupancyEvent;\n}\n\n/**\n * @inheritDoc\n */\nexport class DefaultOccupancy implements Occupancy {\n  private readonly _roomName: string;\n  private readonly _channel: Ably.RealtimeChannel;\n  private readonly _chatApi: ChatApi;\n  private readonly _logger: Logger;\n  private readonly _emitter = new EventEmitter<OccupancyEventsMap>();\n  private readonly _roomOptions: InternalRoomOptions;\n  private _latestOccupancyData?: OccupancyData;\n  private readonly _unsubscribeOccupancyEvents: () => void;\n\n  /**\n   * Constructs a new `DefaultOccupancy` instance.\n   * @param roomName The unique identifier of the room.\n   * @param channel An instance of the Realtime channel.\n   * @param chatApi An instance of the ChatApi.\n   * @param logger An instance of the Logger.\n   * @param roomOptions The room options.\n   */\n  constructor(\n    roomName: string,\n    channel: Ably.RealtimeChannel,\n    chatApi: ChatApi,\n    logger: Logger,\n    roomOptions: InternalRoomOptions,\n  ) {\n    this._roomName = roomName;\n    this._channel = channel;\n    this._chatApi = chatApi;\n    this._logger = logger;\n    this._roomOptions = roomOptions;\n\n    // Create bound listener\n    const occupancyEventsListener = this._internalOccupancyListener.bind(this);\n\n    // Use subscription helper to create cleanup function\n    if (this._roomOptions.occupancy.enableEvents) {\n      this._logger.debug('DefaultOccupancy(); subscribing to occupancy events');\n      this._unsubscribeOccupancyEvents = subscribe(\n        this._channel,\n        [RealtimeMetaEventType.Occupancy],\n        occupancyEventsListener,\n      );\n    } else {\n      this._unsubscribeOccupancyEvents = () => {\n        // No-op function when events are not enabled\n      };\n    }\n  }\n\n  /**\n   * @inheritdoc\n   */\n  subscribe(listener: OccupancyListener): Subscription {\n    this._logger.trace('Occupancy.subscribe();');\n\n    if (!this._roomOptions.occupancy.enableEvents) {\n      throw new Ably.ErrorInfo(\n        'unable to subscribe to occupancy; occupancy events are not enabled in room options',\n        ErrorCode.FeatureNotEnabledInRoom,\n        400,\n      ) as unknown as Error;\n    }\n\n    const wrapped = wrap(listener);\n    this._emitter.on(wrapped);\n\n    return {\n      unsubscribe: () => {\n        this._logger.trace('Occupancy.unsubscribe();');\n        this._emitter.off(wrapped);\n      },\n    };\n  }\n\n  /**\n   * @inheritdoc\n   */\n  async get(): Promise<OccupancyData> {\n    this._logger.trace('Occupancy.get();');\n    return this._chatApi.getOccupancy(this._roomName);\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get current(): OccupancyData | undefined {\n    this._logger.trace('Occupancy.current();');\n\n    // CHA-O7c\n    if (!this._roomOptions.occupancy.enableEvents) {\n      throw new Ably.ErrorInfo(\n        'unable to get current occupancy; occupancy events are not enabled in room options',\n        ErrorCode.FeatureNotEnabledInRoom,\n        400,\n      ) as unknown as Error;\n    }\n\n    // CHA-07a\n    // CHA-07b\n    return this._latestOccupancyData;\n  }\n\n  /**\n   * An internal listener that listens for occupancy events from the underlying channel and translates them into\n   * occupancy events for the public API.\n   * @param message The inbound message containing occupancy data.\n   */\n  private _internalOccupancyListener(message: Ably.InboundMessage): void {\n    this._logger.trace('Occupancy._internalOccupancyListener();', message);\n\n    this._latestOccupancyData = parseOccupancyMessage(message);\n\n    this._emitter.emit(OccupancyEventType.Updated, {\n      type: OccupancyEventType.Updated,\n      occupancy: this._latestOccupancyData,\n    });\n  }\n\n  /**\n   * Merges the channel options for the room with the ones required for occupancy.\n   * @param roomOptions The internal room options.\n   * @returns A function that merges the channel options for the room with the ones required for occupancy.\n   */\n  static channelOptionMerger(roomOptions: InternalRoomOptions): ChannelOptionsMerger {\n    return (options) => {\n      // Occupancy not required, so we can skip this.\n      if (!roomOptions.occupancy.enableEvents) {\n        return options;\n      }\n\n      return { ...options, params: { ...options.params, occupancy: 'metrics' } };\n    };\n  }\n\n  /**\n   * Disposes of the occupancy instance, removing all listeners and subscriptions.\n   * This method should be called when the room is being released to ensure proper cleanup.\n   * @internal\n   */\n  dispose(): void {\n    this._logger.trace('DefaultOccupancy.dispose();');\n\n    // Remove occupancy event subscriptions using stored unsubscribe function\n    this._unsubscribeOccupancyEvents();\n\n    // Remove user-level listeners\n    this._emitter.off();\n\n    this._logger.debug('DefaultOccupancy.dispose(); disposed successfully');\n  }\n\n  /**\n   * Checks if there are any listeners registered by users.\n   * @internal\n   * @returns true if there are listeners, false otherwise.\n   */\n  hasListeners(): boolean {\n    return emitterHasListeners(this._emitter);\n  }\n}\n","import * as Ably from 'ably';\n\nimport { ChannelOptionsMerger } from './channel-manager.js';\nimport { ErrorCode } from './errors.js';\nimport { PresenceEventType } from './events.js';\nimport { JsonObject } from './json.js';\nimport { Logger } from './logger.js';\nimport { on, subscribe } from './realtime-subscriptions.js';\nimport { InternalRoomOptions } from './room-options.js';\nimport { Subscription } from './subscription.js';\nimport EventEmitter, { emitterHasListeners, wrap } from './utils/event-emitter.js';\n\n/**\n * The state of presence in a room\n */\nexport interface PresenceState {\n  /**\n   * Whether the current user is present in the room\n   */\n  readonly present: boolean;\n}\n\n/**\n * A presence state change event\n */\nexport interface PresenceStateChange {\n  /**\n   * The presence state before this change\n   */\n  readonly previous: PresenceState;\n\n  /**\n   * The presence state after this change\n   */\n  readonly current: PresenceState;\n\n  /**\n   * Any error that occurred during this state change\n   * This will be set if there was an error fetching presence data or performing presence operations\n   */\n  readonly error?: Ably.ErrorInfo;\n}\n\n/**\n * Listener for presence state changes\n */\nexport type PresenceStateChangeListener = (change: PresenceStateChange) => void;\n\n/**\n * Interface for PresenceEventsMap\n */\ninterface PresenceEventsMap {\n  [PresenceEventType.Enter]: PresenceEvent;\n  [PresenceEventType.Leave]: PresenceEvent;\n  [PresenceEventType.Update]: PresenceEvent;\n  [PresenceEventType.Present]: PresenceEvent;\n}\n\n/**\n * Type for data that can be entered into presence as an object literal.\n * @example\n * ```ts\n * const foo: PresenceData = {\n *   bar: {\n *     baz: 1\n *   }\n * }\n * ```\n * @example\n * ```ts\n * // Defining a custom type for presence data. It must be a JSON serializable object.\n * interface MyPresenceData {\n *   [key: string]: JsonValue; // Type check for JSON compatibility.\n *   foo: string;\n *   bar: {\n *     baz: string;\n *   }\n *  }\n * ```\n */\nexport type PresenceData = JsonObject;\n\n/**\n * Type for PresenceEvent\n */\nexport interface PresenceEvent {\n  /**\n   * The type of the presence event.\n   */\n  type: PresenceEventType;\n\n  /**\n   * The presence member associated with this event.\n   */\n  member: PresenceMember;\n}\n\n/**\n * Type for PresenceMember.\n *\n * Presence members are unique based on their `connectionId` and `clientId`. It is possible for\n * multiple users to have the same `clientId` if they are connected to the room from different devices.\n */\nexport type PresenceMember = Omit<Ably.PresenceMessage, 'id' | 'action' | 'timestamp'> & {\n  /**\n   * The timestamp of when the last change in state occurred for this presence member.\n   */\n  updatedAt: Date;\n\n  /**\n   * The data associated with the presence member.\n   */\n  data: PresenceData | undefined;\n\n  /**\n   * The extras associated with the presence member.\n   */\n  extras: JsonObject | undefined;\n};\n\n/**\n * Type for PresenceListener\n * @param event The presence event that was received.\n */\nexport type PresenceListener = (event: PresenceEvent) => void;\n\n/**\n * This interface is used to interact with presence in a chat room: subscribing to presence events,\n * fetching presence members, or sending presence events (join,update,leave).\n *\n * Get an instance via {@link Room.presence}.\n */\nexport interface Presence {\n  /**\n   * Method to get list of the current online users and returns the latest presence messages associated to it.\n   * @param params - Parameters that control how the presence set is retrieved.\n   * @throws If the room is not in the `attached` or `attaching` state.\n   * @returns or upon failure, the promise will be rejected with an {@link Ably.ErrorInfo} object which explains the error.\n   */\n  get(params?: Ably.RealtimePresenceParams): Promise<PresenceMember[]>;\n\n  /**\n   * Method to check if user with supplied clientId is online\n   * @param clientId - The client ID to check if it is present in the room.\n   * @throws If the room is not in the `attached` or `attaching` state.\n   * @returns or upon failure, the promise will be rejected with an {@link Ably.ErrorInfo} object which explains the error.\n   */\n  isUserPresent(clientId: string): Promise<boolean>;\n\n  /**\n   * Method to join room presence, will emit an enter event to all subscribers. Repeat calls will trigger more enter events.\n   * @param data - The users data, a JSON serializable object that will be sent to all subscribers.\n   * @throws If the room is not in the `attached` or `attaching` state.\n   * @returns or upon failure, the promise will be rejected with an {@link Ably.ErrorInfo} object which explains the error.\n   */\n  enter(data?: PresenceData): Promise<void>;\n\n  /**\n   * Method to update room presence, will emit an update event to all subscribers. If the user is not present, it will be treated as a join event.\n   * @param data - The users data, a JSON serializable object that will be sent to all subscribers.\n   * @throws If the room is not in the `attached` or `attaching` state.\n   * @returns or upon failure, the promise will be rejected with an {@link Ably.ErrorInfo} object which explains the error.\n   */\n  update(data?: PresenceData): Promise<void>;\n\n  /**\n   * Method to leave room presence, will emit a leave event to all subscribers. If the user is not present, it will be treated as a no-op.\n   * @param data - The users data, a JSON serializable object that will be sent to all subscribers.\n   * @throws If the room is not in the `attached` or `attaching` state.\n   * @returns or upon failure, the promise will be rejected with an {@link Ably.ErrorInfo} object which explains the error.\n   */\n  leave(data?: PresenceData): Promise<void>;\n\n  /**\n   * Subscribe the given listener to all presence events.\n   *\n   * Note: This requires presence events to be enabled via the `enableEvents` option in\n   * the {@link PresenceOptions} provided to the room. If this is not enabled, an error will be thrown.\n   * @param listener listener to subscribe\n   * @throws An {@link Ably.ErrorInfo} with code 40000 if presence events are not enabled\n   */\n  subscribe(listener: PresenceListener): Subscription;\n}\n\n/**\n * @inheritDoc\n */\nexport class DefaultPresence implements Presence {\n  private readonly _channel: Ably.RealtimeChannel;\n  private readonly _logger: Logger;\n  private readonly _emitter = new EventEmitter<PresenceEventsMap>();\n  private readonly _stateEmitter = new EventEmitter<{ 'presence.state.change': PresenceStateChange }>();\n  private readonly _options: InternalRoomOptions;\n  private _presenceState: PresenceState = {\n    present: false,\n  };\n  private readonly _unsubscribePresenceEvents: () => void;\n  private readonly _offChannelUpdate: () => void;\n  private readonly _offChannelDetach: () => void;\n\n  /**\n   * Constructs a new `DefaultPresence` instance.\n   * @param channel The Realtime channel instance.\n   * @param logger An instance of the Logger.\n   * @param options The room options.\n   */\n  constructor(channel: Ably.RealtimeChannel, logger: Logger, options: InternalRoomOptions) {\n    this._channel = channel;\n    this._logger = logger;\n    this._options = options;\n\n    // Create bound listener\n    const presenceEventsListener = this.subscribeToEvents.bind(this);\n\n    const channelUpdateListener = (stateChange: Ably.ChannelStateChange) => {\n      if (stateChange.reason?.code === 91004) {\n        // PresenceAutoReentryFailed\n        this._logger.debug('Presence auto-reentry failed', { reason: stateChange.reason });\n        this._emitPresenceStateChange(false, stateChange.reason);\n        return;\n      }\n\n      // Channel has been moved to detached, which means any members we have will be removed\n      if (stateChange.current === 'detached') {\n        this._emitPresenceStateChange(false);\n        return;\n      }\n    };\n\n    const channelDetachListener = (stateChange: Ably.ChannelStateChange) => {\n      this._emitPresenceStateChange(false, stateChange.reason);\n    };\n\n    this._offChannelUpdate = on(this._channel, 'update', channelUpdateListener);\n    this._offChannelDetach = on(this._channel, ['detached', 'failed'], channelDetachListener);\n\n    // Use subscription helper to create cleanup function\n    this._unsubscribePresenceEvents = subscribe(this._channel.presence, presenceEventsListener);\n  }\n\n  /**\n   * @inheritDoc\n   */\n  async get(params?: Ably.RealtimePresenceParams): Promise<PresenceMember[]> {\n    this._logger.trace('Presence.get()', { params });\n    this._assertChannelState();\n    const userOnPresence = await this._channel.presence.get(params);\n\n    // ably-js never emits the 'absent' event, so we can safely ignore it here.\n    return userOnPresence.map((user) => this._realtimeMemberToPresenceMember(user));\n  }\n\n  /**\n   * @inheritDoc\n   */\n  async isUserPresent(clientId: string): Promise<boolean> {\n    this._logger.trace(`Presence.isUserPresent()`, { clientId });\n    this._assertChannelState();\n    const presenceSet = await this._channel.presence.get({ clientId: clientId });\n    return presenceSet.length > 0;\n  }\n\n  /**\n   * @inheritDoc\n   */\n  async enter(data?: PresenceData): Promise<void> {\n    this._logger.trace(`Presence.enter()`, { data });\n    this._assertChannelState();\n    try {\n      await this._channel.presence.enter(data);\n      this._emitPresenceStateChange(true);\n    } catch (error) {\n      this._emitPresenceStateChange(false, error as Ably.ErrorInfo);\n      throw error;\n    }\n  }\n\n  /**\n   * @inheritDoc\n   */\n  async update(data?: PresenceData): Promise<void> {\n    this._logger.trace(`Presence.update()`, { data });\n    this._assertChannelState();\n    try {\n      await this._channel.presence.update(data);\n      this._emitPresenceStateChange(true);\n    } catch (error) {\n      this._emitPresenceStateChange(false, error as Ably.ErrorInfo);\n      throw error;\n    }\n  }\n\n  /**\n   * @inheritDoc\n   */\n  async leave(data?: PresenceData): Promise<void> {\n    this._logger.trace(`Presence.leave()`, { data });\n    this._assertChannelState();\n    try {\n      await this._channel.presence.leave(data);\n      this._emitPresenceStateChange(false);\n    } catch (error) {\n      this._emitPresenceStateChange(false, error as Ably.ErrorInfo);\n      throw error;\n    }\n  }\n\n  /**\n   * @inheritDoc\n   */\n  subscribe(listener: PresenceListener): Subscription {\n    this._logger.trace('Presence.subscribe()');\n\n    // Check if presence events are enabled\n    if (!this._options.presence.enableEvents) {\n      this._logger.error('unable to subscribe to presence; presence events are not enabled');\n      throw new Ably.ErrorInfo(\n        'unable to subscribe to presence; presence events are not enabled',\n        ErrorCode.FeatureNotEnabledInRoom,\n        400,\n      );\n    }\n\n    const wrapped = wrap(listener);\n    this._emitter.on(wrapped);\n    return {\n      unsubscribe: () => {\n        this._logger.trace('Presence.unsubscribe();');\n        this._emitter.off(wrapped);\n      },\n    };\n  }\n\n  /**\n   * Method to handle and emit presence events\n   * @param member - PresenceMessage ably-js object\n   */\n  subscribeToEvents = (member: Ably.PresenceMessage) => {\n    this._emitter.emit(member.action as PresenceEventType, {\n      type: member.action as PresenceEventType,\n      member: this._realtimeMemberToPresenceMember(member),\n    });\n  };\n\n  /**\n   * Merges the channel options for the room with the ones required for presence.\n   * @param roomOptions The room options to merge for.\n   * @returns A function that merges the channel options for the room with the ones required for presence.\n   */\n  static channelOptionMerger(roomOptions: InternalRoomOptions): ChannelOptionsMerger {\n    return (options) => {\n      // Presence mode is always required\n      if (!options.modes.includes('PRESENCE')) {\n        options.modes.push('PRESENCE');\n      }\n      // If presence events are enabled, add the PRESENCE_SUBSCRIBE mode\n      if (roomOptions.presence.enableEvents && !options.modes.includes('PRESENCE_SUBSCRIBE')) {\n        options.modes.push('PRESENCE_SUBSCRIBE');\n      }\n      return options;\n    };\n  }\n\n  /**\n   * Disposes of the presence instance, removing all listeners and subscriptions.\n   * This method should be called when the room is being released to ensure proper cleanup.\n   * @internal\n   */\n  dispose(): void {\n    this._logger.trace('DefaultPresence.dispose();');\n\n    // Remove all user-level listeners from the emitter\n    this._emitter.off();\n\n    // Unsubscribe from presence events using stored unsubscribe function\n    this._unsubscribePresenceEvents();\n\n    // Remove the channel update listener\n    this._offChannelUpdate();\n\n    // Remove the channel detach listener\n    this._offChannelDetach();\n\n    this._logger.debug('DefaultPresence.dispose(); disposed successfully');\n  }\n\n  /**\n   * Checks if there are any listeners registered by users.\n   * @internal\n   * @returns true if there are listeners, false otherwise.\n   */\n  hasListeners(): boolean {\n    return emitterHasListeners(this._emitter);\n  }\n\n  /**\n   * Converts an Ably presence message to a presence member.\n   * @param member The Ably presence message to convert.\n   * @returns The presence member.\n   */\n  private _realtimeMemberToPresenceMember(member: Ably.PresenceMessage): PresenceMember {\n    return {\n      // Note that we're casting `extras` from ably-js's `any` to our `JsonObject | undefined`; although ably-js's types don't express it we can assume this type per https://sdk.ably.com/builds/ably/specification/main/features/#TP3i.\n      ...member,\n      data: member.data as PresenceData,\n      updatedAt: new Date(member.timestamp),\n    };\n  }\n\n  private _assertChannelState(): void {\n    if (this._channel.state !== 'attaching' && this._channel.state !== 'attached') {\n      this._logger.error('unable to perform presence operation; room is not attached');\n      throw new Ably.ErrorInfo(\n        'unable to perform presence operation; room is not attached',\n        ErrorCode.RoomInInvalidState,\n        400,\n      );\n    }\n  }\n\n  /**\n   * Private method to emit the presence state change event.\n   * @param present - Whether the user is present\n   * @param error - Optional error information\n   */\n  private _emitPresenceStateChange(present: boolean, error?: Ably.ErrorInfo): void {\n    this._logger.trace('Presence._emitPresenceStateChange()', { present, error });\n    const previous: PresenceState = { ...this._presenceState };\n    this._presenceState = { present };\n    const stateChange: PresenceStateChange = {\n      previous,\n      current: this._presenceState,\n      error,\n    };\n    this._stateEmitter.emit('presence.state.change', stateChange);\n  }\n\n  /**\n   * @param listener The listener to subscribe to presence state changes.\n   * @returns A subscription that can be used to unsubscribe from presence state changes.\n   * @internal\n   */\n  onPresenceStateChange(listener: PresenceStateChangeListener): Subscription {\n    this._logger.trace('Presence.onPresenceStateChange()');\n    const wrapped = wrap(listener);\n    this._stateEmitter.on('presence.state.change', wrapped);\n    return {\n      unsubscribe: () => {\n        this._logger.trace('Presence.unsubscribeFromPresenceStateChanges()');\n        this._stateEmitter.off(wrapped);\n      },\n    };\n  }\n}\n","const E_TIMEOUT = new Error('timeout while waiting for mutex to become available');\nconst E_ALREADY_LOCKED = new Error('mutex already locked');\nconst E_CANCELED = new Error('request for lock canceled');\n\nvar __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nclass Semaphore {\n    constructor(_value, _cancelError = E_CANCELED) {\n        this._value = _value;\n        this._cancelError = _cancelError;\n        this._queue = [];\n        this._weightedWaiters = [];\n    }\n    acquire(weight = 1, priority = 0) {\n        if (weight <= 0)\n            throw new Error(`invalid weight ${weight}: must be positive`);\n        return new Promise((resolve, reject) => {\n            const task = { resolve, reject, weight, priority };\n            const i = findIndexFromEnd(this._queue, (other) => priority <= other.priority);\n            if (i === -1 && weight <= this._value) {\n                // Needs immediate dispatch, skip the queue\n                this._dispatchItem(task);\n            }\n            else {\n                this._queue.splice(i + 1, 0, task);\n            }\n        });\n    }\n    runExclusive(callback_1) {\n        return __awaiter$2(this, arguments, void 0, function* (callback, weight = 1, priority = 0) {\n            const [value, release] = yield this.acquire(weight, priority);\n            try {\n                return yield callback(value);\n            }\n            finally {\n                release();\n            }\n        });\n    }\n    waitForUnlock(weight = 1, priority = 0) {\n        if (weight <= 0)\n            throw new Error(`invalid weight ${weight}: must be positive`);\n        if (this._couldLockImmediately(weight, priority)) {\n            return Promise.resolve();\n        }\n        else {\n            return new Promise((resolve) => {\n                if (!this._weightedWaiters[weight - 1])\n                    this._weightedWaiters[weight - 1] = [];\n                insertSorted(this._weightedWaiters[weight - 1], { resolve, priority });\n            });\n        }\n    }\n    isLocked() {\n        return this._value <= 0;\n    }\n    getValue() {\n        return this._value;\n    }\n    setValue(value) {\n        this._value = value;\n        this._dispatchQueue();\n    }\n    release(weight = 1) {\n        if (weight <= 0)\n            throw new Error(`invalid weight ${weight}: must be positive`);\n        this._value += weight;\n        this._dispatchQueue();\n    }\n    cancel() {\n        this._queue.forEach((entry) => entry.reject(this._cancelError));\n        this._queue = [];\n    }\n    _dispatchQueue() {\n        this._drainUnlockWaiters();\n        while (this._queue.length > 0 && this._queue[0].weight <= this._value) {\n            this._dispatchItem(this._queue.shift());\n            this._drainUnlockWaiters();\n        }\n    }\n    _dispatchItem(item) {\n        const previousValue = this._value;\n        this._value -= item.weight;\n        item.resolve([previousValue, this._newReleaser(item.weight)]);\n    }\n    _newReleaser(weight) {\n        let called = false;\n        return () => {\n            if (called)\n                return;\n            called = true;\n            this.release(weight);\n        };\n    }\n    _drainUnlockWaiters() {\n        if (this._queue.length === 0) {\n            for (let weight = this._value; weight > 0; weight--) {\n                const waiters = this._weightedWaiters[weight - 1];\n                if (!waiters)\n                    continue;\n                waiters.forEach((waiter) => waiter.resolve());\n                this._weightedWaiters[weight - 1] = [];\n            }\n        }\n        else {\n            const queuedPriority = this._queue[0].priority;\n            for (let weight = this._value; weight > 0; weight--) {\n                const waiters = this._weightedWaiters[weight - 1];\n                if (!waiters)\n                    continue;\n                const i = waiters.findIndex((waiter) => waiter.priority <= queuedPriority);\n                (i === -1 ? waiters : waiters.splice(0, i))\n                    .forEach((waiter => waiter.resolve()));\n            }\n        }\n    }\n    _couldLockImmediately(weight, priority) {\n        return (this._queue.length === 0 || this._queue[0].priority < priority) &&\n            weight <= this._value;\n    }\n}\nfunction insertSorted(a, v) {\n    const i = findIndexFromEnd(a, (other) => v.priority <= other.priority);\n    a.splice(i + 1, 0, v);\n}\nfunction findIndexFromEnd(a, predicate) {\n    for (let i = a.length - 1; i >= 0; i--) {\n        if (predicate(a[i])) {\n            return i;\n        }\n    }\n    return -1;\n}\n\nvar __awaiter$1 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nclass Mutex {\n    constructor(cancelError) {\n        this._semaphore = new Semaphore(1, cancelError);\n    }\n    acquire() {\n        return __awaiter$1(this, arguments, void 0, function* (priority = 0) {\n            const [, releaser] = yield this._semaphore.acquire(1, priority);\n            return releaser;\n        });\n    }\n    runExclusive(callback, priority = 0) {\n        return this._semaphore.runExclusive(() => callback(), 1, priority);\n    }\n    isLocked() {\n        return this._semaphore.isLocked();\n    }\n    waitForUnlock(priority = 0) {\n        return this._semaphore.waitForUnlock(1, priority);\n    }\n    release() {\n        if (this._semaphore.isLocked())\n            this._semaphore.release();\n    }\n    cancel() {\n        return this._semaphore.cancel();\n    }\n}\n\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nfunction withTimeout(sync, timeout, timeoutError = E_TIMEOUT) {\n    return {\n        acquire: (weightOrPriority, priority) => {\n            let weight;\n            if (isSemaphore(sync)) {\n                weight = weightOrPriority;\n            }\n            else {\n                weight = undefined;\n                priority = weightOrPriority;\n            }\n            if (weight !== undefined && weight <= 0) {\n                throw new Error(`invalid weight ${weight}: must be positive`);\n            }\n            return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {\n                let isTimeout = false;\n                const handle = setTimeout(() => {\n                    isTimeout = true;\n                    reject(timeoutError);\n                }, timeout);\n                try {\n                    const ticket = yield (isSemaphore(sync)\n                        ? sync.acquire(weight, priority)\n                        : sync.acquire(priority));\n                    if (isTimeout) {\n                        const release = Array.isArray(ticket) ? ticket[1] : ticket;\n                        release();\n                    }\n                    else {\n                        clearTimeout(handle);\n                        resolve(ticket);\n                    }\n                }\n                catch (e) {\n                    if (!isTimeout) {\n                        clearTimeout(handle);\n                        reject(e);\n                    }\n                }\n            }));\n        },\n        runExclusive(callback, weight, priority) {\n            return __awaiter(this, void 0, void 0, function* () {\n                let release = () => undefined;\n                try {\n                    const ticket = yield this.acquire(weight, priority);\n                    if (Array.isArray(ticket)) {\n                        release = ticket[1];\n                        return yield callback(ticket[0]);\n                    }\n                    else {\n                        release = ticket;\n                        return yield callback();\n                    }\n                }\n                finally {\n                    release();\n                }\n            });\n        },\n        release(weight) {\n            sync.release(weight);\n        },\n        cancel() {\n            return sync.cancel();\n        },\n        waitForUnlock: (weightOrPriority, priority) => {\n            let weight;\n            if (isSemaphore(sync)) {\n                weight = weightOrPriority;\n            }\n            else {\n                weight = undefined;\n                priority = weightOrPriority;\n            }\n            if (weight !== undefined && weight <= 0) {\n                throw new Error(`invalid weight ${weight}: must be positive`);\n            }\n            return new Promise((resolve, reject) => {\n                const handle = setTimeout(() => reject(timeoutError), timeout);\n                (isSemaphore(sync)\n                    ? sync.waitForUnlock(weight, priority)\n                    : sync.waitForUnlock(priority)).then(() => {\n                    clearTimeout(handle);\n                    resolve();\n                });\n            });\n        },\n        isLocked: () => sync.isLocked(),\n        getValue: () => sync.getValue(),\n        setValue: (value) => sync.setValue(value),\n    };\n}\nfunction isSemaphore(sync) {\n    return sync.getValue !== undefined;\n}\n\n// eslint-disable-next-lisne @typescript-eslint/explicit-module-boundary-types\nfunction tryAcquire(sync, alreadyAcquiredError = E_ALREADY_LOCKED) {\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    return withTimeout(sync, 0, alreadyAcquiredError);\n}\n\nexport { E_ALREADY_LOCKED, E_CANCELED, E_TIMEOUT, Mutex, Semaphore, tryAcquire, withTimeout };\n","import * as Ably from 'ably';\n\nimport { Logger } from './logger.js';\nimport { StatusSubscription } from './subscription.js';\nimport EventEmitter, { emitterHasListeners, wrap } from './utils/event-emitter.js';\n\n/**\n * The different states that a room can be in throughout its lifecycle.\n */\nexport enum RoomStatus {\n  /**\n   * The library is currently initializing the room. This state is a temporary state used in React prior\n   * to the room being resolved.\n   */\n  Initializing = 'initializing',\n\n  /**\n   * A temporary state for when the room object is first initialized.\n   */\n  Initialized = 'initialized',\n\n  /**\n   * The library is currently attempting to attach the room.\n   */\n  Attaching = 'attaching',\n\n  /**\n   * The room is currently attached and receiving events.\n   */\n  Attached = 'attached',\n\n  /**\n   * The room is currently detaching and will not receive events.\n   */\n  Detaching = 'detaching',\n\n  /**\n   * The room is currently detached and will not receive events.\n   */\n  Detached = 'detached',\n\n  /**\n   * The room is in an extended state of detachment, but will attempt to re-attach when able.\n   */\n  Suspended = 'suspended',\n\n  /**\n   * The room is currently detached and will not attempt to re-attach. User intervention is required.\n   */\n  Failed = 'failed',\n\n  /**\n   * The room is in the process of releasing. Attempting to use a room in this state may result in undefined behavior.\n   */\n  Releasing = 'releasing',\n\n  /**\n   * The room has been released and is no longer usable.\n   */\n  Released = 'released',\n}\n\n/**\n * Represents a change in the status of the room.\n */\nexport interface RoomStatusChange {\n  /**\n   * The new status of the room.\n   */\n  current: RoomStatus;\n\n  /**\n   * The previous status of the room.\n   */\n  previous: RoomStatus;\n\n  /**\n   * An error that provides a reason why the room has\n   * entered the new status, if applicable.\n   */\n  error?: Ably.ErrorInfo;\n}\n\n/**\n * A function that can be called when the room status changes.\n * @param change The change in status.\n */\nexport type RoomStatusListener = (change: RoomStatusChange) => void;\n\n/**\n * Represents the status of a Room.\n */\nexport interface RoomLifecycle {\n  /**\n   * The current status of the room.\n   */\n  get status(): RoomStatus;\n\n  /**\n   * The current error, if any, that caused the room to enter the current status.\n   */\n  get error(): Ably.ErrorInfo | undefined;\n\n  /**\n   * Registers a listener that will be called whenever the room status changes.\n   * @param listener The function to call when the status changes.\n   * @returns An object that can be used to unregister the listener.\n   */\n  onChange(listener: RoomStatusListener): StatusSubscription;\n}\n\n/**\n * An internal interface for the status of a room, which can be used to separate critical\n * internal functionality from user listeners.\n * @internal\n */\nexport interface InternalRoomLifecycle extends RoomLifecycle {\n  /**\n   * Sets the status of the room.\n   * @param params The new status of the room.\n   */\n  setStatus(params: NewRoomStatus): void;\n}\n\n/**\n * A new room status that can be set.\n */\nexport interface NewRoomStatus {\n  /**\n   * The new status of the room.\n   */\n  status: RoomStatus;\n\n  /**\n   * An error that provides a reason why the room has\n   * entered the new status, if applicable.\n   */\n  error?: Ably.ErrorInfo;\n}\n\ntype RoomStatusEventsMap = Record<RoomStatus, RoomStatusChange>;\n\n/**\n * An implementation of the `Status` interface.\n * @internal\n */\nexport class DefaultRoomLifecycle implements InternalRoomLifecycle {\n  private _status: RoomStatus = RoomStatus.Initialized;\n  private _error?: Ably.ErrorInfo;\n  private readonly _logger: Logger;\n  private readonly _emitter = new EventEmitter<RoomStatusEventsMap>();\n\n  /**\n   * Constructs a new DefaultRoomLifecycle instance.\n   * @param logger An instance of the Logger.\n   */\n  constructor(logger: Logger) {\n    this._logger = logger;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get status(): RoomStatus {\n    return this._status;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get error(): Ably.ErrorInfo | undefined {\n    return this._error;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  onChange(listener: RoomStatusListener): StatusSubscription {\n    const wrapped = wrap(listener);\n    this._emitter.on(wrapped);\n\n    return {\n      off: () => {\n        this._emitter.off(wrapped);\n      },\n    };\n  }\n\n  setStatus(params: NewRoomStatus): void {\n    const change: RoomStatusChange = {\n      current: params.status,\n      error: params.error,\n      previous: this._status,\n    };\n\n    this._status = change.current;\n    this._error = change.error;\n    this._logger.info(`room status changed`, { ...change });\n    this._emitter.emit(change.current, change);\n  }\n\n  /**\n   * Disposes of the room lifecycle instance, removing all listeners.\n   * This method should be called when the room is being released to ensure proper cleanup.\n   * @internal\n   */\n  dispose(): void {\n    this._logger.trace('DefaultRoomLifecycle.dispose();');\n\n    // Remove all user-level listeners\n    this._emitter.off();\n\n    this._logger.debug('DefaultRoomLifecycle.dispose(); disposed successfully');\n  }\n\n  /**\n   * Checks if there are any listeners registered by users.\n   * @internal\n   * @returns true if there are listeners, false otherwise.\n   */\n  hasListeners(): boolean {\n    return emitterHasListeners(this._emitter);\n  }\n}\n","import * as Ably from 'ably';\nimport { Mutex } from 'async-mutex';\n\nimport { ChannelManager } from './channel-manager.js';\nimport { DiscontinuityListener } from './discontinuity.js';\nimport { ErrorCode } from './errors.js';\nimport { RoomEventType } from './events.js';\nimport { Logger } from './logger.js';\nimport { on } from './realtime-subscriptions.js';\nimport { InternalRoomLifecycle, RoomStatus } from './room-status.js';\nimport { StatusSubscription } from './subscription.js';\nimport EventEmitter, { emitterHasListeners, wrap } from './utils/event-emitter.js';\n\n/**\n * Events that can be emitted by the RoomLifecycleManager\n */\nexport interface RoomLifeCycleEvents {\n  [RoomEventType.Discontinuity]: Ably.ErrorInfo;\n}\n\n/**\n * Priority levels for operations, lower numbers are higher priority\n */\nenum OperationPriority {\n  Release = 0,\n  AttachDetach = 1,\n}\n\n/**\n * Manages the lifecycle of a room's underlying channel, handling attach, detach and release operations\n * while maintaining the room's status.\n */\nexport class RoomLifecycleManager {\n  private readonly _channelManager: ChannelManager;\n  private readonly _roomLifecycle: InternalRoomLifecycle;\n  private readonly _logger: Logger;\n  private readonly _eventEmitter: EventEmitter<RoomLifeCycleEvents>;\n  private _hasAttachedOnce: boolean; // CHA-RL13\n  private _isExplicitlyDetached: boolean; // CHA-RL14\n  private readonly _mutex: Mutex; // CHA-RL7\n  private readonly _unsubscribeChannelStateListener: () => void;\n  private readonly _offDiscontinuityAttached: () => void;\n  private readonly _offDiscontinuityUpdate: () => void;\n\n  constructor(channelManager: ChannelManager, roomLifecycle: InternalRoomLifecycle, logger: Logger) {\n    this._channelManager = channelManager;\n    this._roomLifecycle = roomLifecycle;\n    this._logger = logger;\n    this._eventEmitter = new EventEmitter();\n    this._hasAttachedOnce = false; // CHA-RL13\n    this._isExplicitlyDetached = false; // CHA-RL14\n    this._mutex = new Mutex(); // CHA-RL7\n\n    // Create bound listeners\n    const channelStateListener = this._channelStateListener.bind(this);\n    const discontinuityOnAttachedListener = this._discontinuityOnAttachedListener.bind(this);\n    const discontinuityOnUpdateListener = this._discontinuityOnUpdateListener.bind(this);\n\n    // Use subscription helpers to create cleanup functions\n    const channel = this._channelManager.get();\n    this._unsubscribeChannelStateListener = on(channel, channelStateListener);\n    this._offDiscontinuityAttached = on(channel, 'attached', discontinuityOnAttachedListener);\n    this._offDiscontinuityUpdate = on(channel, 'update', discontinuityOnUpdateListener);\n  }\n\n  /**\n   * Registers a handler for discontinuity events.\n   * @param handler The function to be called when a discontinuity is detected\n   * @returns An object with an off() method to deregister the handler\n   */\n  onDiscontinuity(handler: DiscontinuityListener): StatusSubscription {\n    this._logger.trace('RoomLifecycleManager.onDiscontinuity()');\n    const wrapped = wrap(handler);\n    this._eventEmitter.on(RoomEventType.Discontinuity, wrapped);\n    return {\n      off: () => {\n        this._eventEmitter.off(RoomEventType.Discontinuity, wrapped);\n      },\n    };\n  }\n\n  /**\n   * Attaches to the channel and updates room status accordingly.\n   * If the room is released/releasing, this operation fails.\n   * If already attached, this is a no-op.\n   */\n  async attach(): Promise<void> {\n    // CHA-RL1d, CHA-RL7a\n    await this._mutex.runExclusive(async () => {\n      this._logger.trace('RoomLifecycleManager.attach();');\n\n      // CHA-RL1b, CHA-RL1c\n      this._checkRoomNotReleasing('attach');\n\n      // CHA-RL1a\n      if (this._roomStatusIs(RoomStatus.Attached)) {\n        this._logger.debug('RoomLifecycleManager.attach(); room already attached, no-op');\n        return;\n      }\n\n      const channel = this._channelManager.get();\n      this._logger.debug('RoomLifecycleManager.attach(); attaching room', {\n        channelState: channel.state,\n      });\n\n      try {\n        // CHA-RL1e\n        this._setStatus(RoomStatus.Attaching);\n        // CHA-RL1k\n        await channel.attach();\n        this._setStatus(RoomStatus.Attached);\n        this._isExplicitlyDetached = false;\n        this._hasAttachedOnce = true;\n        this._logger.debug('RoomLifecycleManager.attach(); room attached successfully');\n      } catch (error) {\n        const errInfo = error as Ably.ErrorInfo;\n        const attachError = new Ably.ErrorInfo(\n          `failed to attach room: ${errInfo.message}`,\n          errInfo.code,\n          errInfo.statusCode,\n          errInfo,\n        );\n\n        const newStatus = this._mapChannelStateToRoomStatus(channel.state);\n        this._setStatus(newStatus, attachError);\n        throw attachError;\n      }\n    }, OperationPriority.AttachDetach);\n  }\n\n  /**\n   * Detaches from the channel and updates room status accordingly.\n   * If the room is released/releasing, this operation fails.\n   * If already detached, this is a no-op.\n   */\n  async detach(): Promise<void> {\n    // CHA-RL2i, CHA-RL7a\n    await this._mutex.runExclusive(async () => {\n      this._logger.trace('RoomLifecycleManager.detach();');\n\n      // CHA-RL2d\n      if (this._roomStatusIs(RoomStatus.Failed)) {\n        throw new Ably.ErrorInfo('unable to detach room; room is in failed state', ErrorCode.RoomInInvalidState, 400);\n      }\n\n      // CHA-RL2b, CHA-RL2c\n      this._checkRoomNotReleasing('detach');\n\n      // CHA-RL2a\n      if (this._roomStatusIs(RoomStatus.Detached)) {\n        this._logger.debug('RoomLifecycleManager.detach(); room already detached, no-op');\n        return;\n      }\n\n      const channel = this._channelManager.get();\n      this._logger.debug('RoomLifecycleManager.detach(); detaching room', {\n        channelState: channel.state,\n      });\n\n      try {\n        // CHA-RL2j\n        this._setStatus(RoomStatus.Detaching);\n        // CHA-RL2k\n        await channel.detach();\n        this._isExplicitlyDetached = true;\n        this._setStatus(RoomStatus.Detached);\n        this._logger.debug('RoomLifecycleManager.detach(); room detached successfully');\n      } catch (error) {\n        const errInfo = error as Ably.ErrorInfo;\n        const detachError = new Ably.ErrorInfo(\n          `failed to detach room: ${errInfo.message}`,\n          errInfo.code,\n          errInfo.statusCode,\n          errInfo,\n        );\n\n        const newStatus = this._mapChannelStateToRoomStatus(channel.state);\n        this._setStatus(newStatus, detachError);\n        throw detachError;\n      }\n    }, OperationPriority.AttachDetach);\n  }\n\n  /**\n   * Releases the room by detaching the channel and releasing it from the channel manager.\n   * If the channel is in a failed state, skips the detach operation.\n   * Will retry detach until successful unless in failed state.\n   */\n  async release(): Promise<void> {\n    // CHA-RL3k, CHA-RL7a\n    await this._mutex.runExclusive(async () => {\n      this._logger.trace('RoomLifecycleManager.release();');\n\n      // CHA-RL3a\n      if (this._roomStatusIs(RoomStatus.Released)) {\n        this._logger.debug('RoomLifecycleManager.release(); room already released, no-op');\n        return;\n      }\n\n      // CHA-RL3b, CHA-RL3j\n      if (this._roomStatusIs(RoomStatus.Initialized) || this._roomStatusIs(RoomStatus.Detached)) {\n        this._logger.debug('RoomLifecycleManager.release(); room is initialized or detached, releasing immediately', {\n          status: this._roomLifecycle.status,\n        });\n        this._releaseChannel();\n        return;\n      }\n\n      // CHA-RL3m\n      this._setStatus(RoomStatus.Releasing);\n      const channel = this._channelManager.get();\n\n      // CHA-RL3n\n      this._logger.debug('RoomLifecycleManager.release(); attempting channel detach before release', {\n        channelState: channel.state,\n      });\n      await this._channelDetachLoop(channel);\n\n      // CHA-RL3o, CHA-RL3h\n      this._releaseChannel();\n    }, OperationPriority.Release);\n  }\n\n  /**\n   * Maps an Ably channel state to a room status\n   * @param channelState The Ably channel state to map.\n   * @returns The corresponding room status.\n   */\n  private _mapChannelStateToRoomStatus(channelState: Ably.ChannelState): RoomStatus {\n    switch (channelState) {\n      case 'initialized': {\n        return RoomStatus.Initialized;\n      }\n      case 'attaching': {\n        return RoomStatus.Attaching;\n      }\n      case 'attached': {\n        return RoomStatus.Attached;\n      }\n      case 'detaching': {\n        return RoomStatus.Detaching;\n      }\n      case 'detached': {\n        return RoomStatus.Detached;\n      }\n      case 'suspended': {\n        return RoomStatus.Suspended;\n      }\n      case 'failed': {\n        return RoomStatus.Failed;\n      }\n      default: {\n        this._logger.error('RoomLifecycleManager._mapChannelStateToRoomStatus(); unknown channel state', {\n          channelState,\n        });\n        return RoomStatus.Failed;\n      }\n    }\n  }\n\n  private _checkRoomNotReleasing(op: string) {\n    switch (this._roomLifecycle.status) {\n      case RoomStatus.Released: {\n        throw new Ably.ErrorInfo(`unable to ${op} room; room is released`, ErrorCode.RoomInInvalidState, 400);\n      }\n      case RoomStatus.Releasing: {\n        throw new Ably.ErrorInfo(\n          `unable to ${op} room; room is currently releasing`,\n          ErrorCode.RoomInInvalidState,\n          400,\n        );\n      }\n    }\n  }\n\n  /**\n   * Returns the current room status\n   * @param status The room status to check against.\n   * @returns true if the room status matches, false otherwise.\n   */\n  private _roomStatusIs(status: RoomStatus): boolean {\n    return this._roomLifecycle.status === status;\n  }\n\n  /**\n   * Disposes of the room lifecycle manager, removing all listeners and subscriptions.\n   * This method should be called when the room is being released to ensure proper cleanup.\n   * @internal\n   */\n  dispose(): void {\n    // Clean up channel listeners using stored unsubscribe functions\n    this._unsubscribeChannelStateListener();\n    this._offDiscontinuityAttached();\n    this._offDiscontinuityUpdate();\n\n    // Clean up user-level listeners\n    this._eventEmitter.off();\n  }\n\n  /**\n   * Checks if there are any listeners registered by users.\n   * @internal\n   * @returns true if there are listeners, false otherwise.\n   */\n  hasListeners(): boolean {\n    return emitterHasListeners(this._eventEmitter);\n  }\n\n  private _channelStateListener(stateChange: Ably.ChannelStateChange): void {\n    this._logger.debug('RoomLifecycleManager.channel state changed', {\n      oldState: stateChange.previous,\n      newState: stateChange.current,\n      reason: stateChange.reason,\n      resumed: stateChange.resumed,\n    });\n\n    // CHA-RL11b\n    if (this._operationInProgress()) {\n      this._logger.debug(\n        'RoomLifecycleManager._startMonitoringChannelState(); ignoring channel state change - operation in progress',\n        {\n          status: this._roomLifecycle.status,\n        },\n      );\n      return;\n    }\n\n    // CHA-RL11c\n    const newStatus = this._mapChannelStateToRoomStatus(stateChange.current);\n    this._setStatus(newStatus, stateChange.reason);\n  }\n\n  private _discontinuityOnAttachedListener(stateChange: Ably.ChannelStateChange): void {\n    if (!stateChange.resumed && this._hasAttachedOnce && !this._isExplicitlyDetached) {\n      const error = new Ably.ErrorInfo(\n        'discontinuity detected',\n        ErrorCode.RoomDiscontinuity,\n        stateChange.reason?.statusCode ?? 0,\n        stateChange.reason,\n      );\n\n      this._logger.warn('RoomLifecycleManager._startMonitoringDiscontinuity(); discontinuity detected', {\n        error,\n      });\n      this._eventEmitter.emit(RoomEventType.Discontinuity, error);\n    }\n  }\n\n  private _discontinuityOnUpdateListener(stateChange: Ably.ChannelStateChange): void {\n    if (\n      !stateChange.resumed &&\n      this._hasAttachedOnce &&\n      !this._isExplicitlyDetached &&\n      stateChange.current === 'attached' &&\n      stateChange.previous === 'attached'\n    ) {\n      const error = new Ably.ErrorInfo(\n        'discontinuity detected',\n        ErrorCode.RoomDiscontinuity,\n        stateChange.reason?.statusCode ?? 0,\n        stateChange.reason,\n      );\n\n      this._logger.warn('RoomLifecycleManager._startMonitoringDiscontinuity(); discontinuity detected', {\n        error,\n      });\n      this._eventEmitter.emit(RoomEventType.Discontinuity, error);\n    }\n  }\n\n  private async _channelDetachLoop(channel: Ably.RealtimeChannel) {\n    for (;;) {\n      // If channel is now failed, we can stop trying to detach\n      const currentState: Ably.ChannelState = channel.state;\n      if (currentState === 'failed') {\n        this._logger.debug('RoomLifecycleManager._channelDetachLoop(); channel is failed, skipping detach');\n        break;\n      }\n\n      try {\n        await channel.detach();\n        break;\n      } catch (error) {\n        // keep trying\n        this._logger.error('RoomLifecycleManager._channelDetachLoop(); failed to detach channel during release', {\n          error,\n        });\n        await new Promise((resolve) => setTimeout(resolve, 250)); // Wait 250ms before retry\n      }\n    }\n  }\n\n  private _setStatus(status: RoomStatus, error?: Ably.ErrorInfo) {\n    this._logger.debug('RoomLifecycleManager._setStatus(); updating room status', {\n      oldStatus: this._roomLifecycle.status,\n      newStatus: status,\n      hasError: !!error,\n    });\n    this._roomLifecycle.setStatus({ status, error });\n  }\n\n  private _releaseChannel() {\n    this._channelManager.release();\n    this._setStatus(RoomStatus.Released);\n    this._logger.debug('RoomLifecycleManager._releaseChannel(); room released successfully');\n  }\n\n  /**\n   * Returns whether there is currently an operation (attach/detach/release) in progress\n   * @returns True if an operation is in progress, false otherwise.\n   */\n  private _operationInProgress(): boolean {\n    return this._mutex.isLocked();\n  }\n\n  testForceHasAttachedOnce(firstAttach: boolean) {\n    this._logger.trace('RoomLifecycleManager.testForceHasAttachedOnce();', { firstAttach });\n    this._hasAttachedOnce = firstAttach;\n  }\n}\n","import * as Ably from 'ably';\n\nimport { ErrorCode } from './errors.js';\nimport { MessageReactionType } from './events.js';\n\n/**\n * The default values for RoomOptions.\n */\nconst DefaultRoomOptions: Omit<InternalRoomOptions, 'isReactClient'> = {\n  /**\n   * The default typing options for a chat room.\n   */\n  typing: {\n    /**\n     * The default time that a client will wait between sending one typing heartbeat and the next.\n     *\n     * Spec: CHA-T10.\n     */\n    heartbeatThrottleMs: 10000,\n  },\n\n  /**\n   * The default occupancy options for a chat room.\n   */\n  occupancy: {\n    /**\n     * Whether to enable occupancy events.\n     */\n    enableEvents: false,\n  },\n\n  /**\n   * The default presence options for the room.\n   */\n  presence: {\n    enableEvents: true,\n  },\n\n  /**\n   * The default options for messages.\n   */\n  messages: {\n    rawMessageReactions: false,\n    defaultMessageReactionType: MessageReactionType.Distinct,\n  },\n};\n\n/**\n * Represents the message options for a chat room.\n */\nexport interface MessagesOptions {\n  /**\n   * Whether to enable receiving raw individual message reactions from the\n   * realtime channel. Set to true if subscribing to raw message reactions.\n   *\n   * Note reaction summaries (aggregates) are always available regardless of\n   * this setting.\n   * @defaultValue false\n   */\n  rawMessageReactions?: boolean;\n\n  /**\n   * The default message reaction type to use for sending message reactions.\n   *\n   * Any message reaction type can be sent regardless of this setting by specifying the `type` parameter\n   * in the {@link MessageReactions.send} method.\n   * @defaultValue {@link MessageReactionType.Distinct}\n   */\n  defaultMessageReactionType?: MessageReactionType;\n}\n\n/**\n * Represents the typing options for a chat room.\n */\nexport interface TypingOptions {\n  /**\n   * A throttle, in milliseconds, that enforces the minimum time interval between consecutive `typing.started`\n   * events sent by the client to the server.\n   * If typing.start() is called, the first call will emit an event immediately.\n   * Later calls will no-op until the time has elapsed.\n   * Calling typing.stop() will immediately send a `typing.stopped` event to the server and reset the interval,\n   * allowing the client to send another `typing.started` event immediately.\n   * @defaultValue 10000\n   */\n  heartbeatThrottleMs?: number;\n}\n\n/**\n * Represents the occupancy options for a chat room.\n */\nexport interface OccupancyOptions {\n  /**\n   * Whether to enable occupancy events.\n   *\n   * Note that enabling this feature will increase the number of messages received by the client as additional\n   * messages will be sent by the server to indicate occupancy changes.\n   * @defaultValue false\n   */\n  enableEvents?: boolean;\n}\n\n/**\n * Represents the presence options for a chat room.\n */\nexport interface PresenceOptions {\n  /**\n   * Whether or not the client should receive presence events from the server. This setting\n   * can be disabled if you are using presence in your Chat Room, but this particular client does not\n   * need to receive the messages.\n   * @defaultValue true\n   */\n  enableEvents?: boolean;\n}\n\n/**\n * Represents the options for a given chat room.\n */\nexport interface RoomOptions {\n  /**\n   * The typing options for the room.\n   */\n  typing?: TypingOptions;\n\n  /**\n   * The occupancy options for the room.\n   */\n  occupancy?: OccupancyOptions;\n\n  /**\n   * The presence options for the room.\n   */\n  presence?: PresenceOptions;\n\n  /**\n   * The message options for the room.\n   */\n  messages?: MessagesOptions;\n}\n\n/**\n * Represents the normalized typing options for a chat room, which makes every property required.\n */\nexport type InternalTypingOptions = Required<TypingOptions>;\n\n/**\n * Represents the normalized occupancy options for a chat room. Everything becomes required.\n */\nexport type InternalOccupancyOptions = Required<OccupancyOptions>;\n\n/**\n * Represents the normalized presence options for a chat room. Everything becomes required.\n */\nexport type InternalPresenceOptions = Required<PresenceOptions>;\n\n/**\n * Represents the normalized message options for a chat room. Everything becomes required.\n */\nexport type InternalMessagesOptions = Required<MessagesOptions>;\n\n/**\n * Represents the normalized options for a chat room.\n */\nexport interface InternalRoomOptions {\n  /**\n   * Are we running the client in a React environment?\n   */\n  isReactClient: boolean;\n\n  /**\n   * Typing options with everything made mandatory.\n   */\n  typing: InternalTypingOptions;\n\n  /**\n   * Occupancy options with everything made mandatory.\n   */\n  occupancy: InternalOccupancyOptions;\n\n  /**\n   * Presence options with everything made mandatory.\n   */\n  presence: InternalPresenceOptions;\n\n  /**\n   * Message options with everything made mandatory.\n   */\n  messages: InternalMessagesOptions;\n}\n\n/**\n * Creates an {@link ErrorInfo} for invalid room configuration.\n * @param reason The reason for the invalid room configuration.\n * @returns An ErrorInfo.\n */\nconst invalidRoomConfiguration = (reason: string): Error =>\n  new Ably.ErrorInfo(`unable to create room; invalid room configuration: ${reason}`, ErrorCode.InvalidArgument, 400);\n\nexport const validateRoomOptions = (options: InternalRoomOptions): void => {\n  validateTypingOptions(options.typing);\n};\n\nconst validateTypingOptions = (options: InternalTypingOptions): void => {\n  if (options.heartbeatThrottleMs <= 0) {\n    throw invalidRoomConfiguration('typing heartbeat interval must be greater than 0');\n  }\n};\n\nconst normalizeTypingOptions = (options: RoomOptions | undefined): InternalTypingOptions => ({\n  ...DefaultRoomOptions.typing,\n  ...options?.typing,\n});\n\nconst normalizeOccupancyOptions = (options: RoomOptions | undefined): InternalOccupancyOptions => ({\n  ...DefaultRoomOptions.occupancy,\n  ...options?.occupancy,\n});\n\nconst normalizePresenceOptions = (options: RoomOptions | undefined): InternalPresenceOptions => ({\n  ...DefaultRoomOptions.presence,\n  ...options?.presence,\n});\n\nconst normalizeMessagesOptions = (options: RoomOptions | undefined): InternalMessagesOptions => ({\n  ...DefaultRoomOptions.messages,\n  ...options?.messages,\n});\n\nexport const normalizeRoomOptions = (options: RoomOptions | undefined, react: boolean): InternalRoomOptions => ({\n  typing: normalizeTypingOptions(options),\n  occupancy: normalizeOccupancyOptions(options),\n  presence: normalizePresenceOptions(options),\n  messages: normalizeMessagesOptions(options),\n  isReactClient: react,\n});\n","import * as Ably from 'ably';\n\n/**\n * Convenience function that takes an event name and optional data and turns it into a\n * message that the server will recognize as ephemeral.\n * @param name The name of the event.\n * @param data Optional data to send with the event.\n * @returns An Ably message.\n */\nexport const ephemeralMessage = (name: string, data?: unknown): Ably.Message => ({\n  name: name,\n  data: data,\n  extras: {\n    ephemeral: true,\n  },\n});\n\n/**\n * Takes an existing Ably message and converts it to an ephemeral message by adding\n * the ephemeral flag in the extras field.\n * @param message The Ably message to convert.\n * @returns A new Ably message with the ephemeral flag set.\n */\nexport const messageToEphemeral = (message: Ably.Message): Ably.Message => {\n  const extras = message.extras ? (message.extras as object) : {};\n\n  return {\n    ...message,\n    extras: {\n      ...extras,\n      ephemeral: true,\n    },\n  };\n};\n","import { Headers } from './headers.js';\nimport { Metadata } from './metadata.js';\n\n/**\n * {@link Headers} type for chat messages.\n */\nexport type RoomReactionHeaders = Headers;\n\n/**\n * {@link Metadata} type for chat messages.\n */\nexport type RoomReactionMetadata = Metadata;\n\n/**\n * Represents a room-level reaction.\n */\nexport interface RoomReaction {\n  /**\n   * The name of the reaction, for example \"like\" or \"love\".\n   */\n  readonly name: string;\n\n  /**\n   * Metadata of the reaction. If no metadata was set this is an empty object.\n   */\n  readonly metadata: RoomReactionMetadata;\n\n  /**\n   * Headers of the reaction. If no headers were set this is an empty object.\n   */\n  readonly headers: RoomReactionHeaders;\n\n  /**\n   * The timestamp at which the reaction was sent.\n   */\n  readonly createdAt: Date;\n\n  /**\n   * The clientId of the user who sent the reaction.\n   */\n  readonly clientId: string;\n\n  /**\n   * Whether the reaction was sent by the current user.\n   */\n  readonly isSelf: boolean;\n}\n\n/**\n * An implementation of the RoomReaction interface for room-level reactions.\n */\nexport class DefaultRoomReaction implements RoomReaction {\n  constructor(\n    public readonly name: string,\n    public readonly clientId: string,\n    public readonly createdAt: Date,\n    public readonly isSelf: boolean,\n    public readonly metadata: RoomReactionMetadata,\n    public readonly headers: RoomReactionHeaders,\n  ) {\n    // The object is frozen after constructing to enforce readonly at runtime too\n    Object.freeze(this);\n  }\n}\n","import * as Ably from 'ably';\n\nimport { DefaultRoomReaction, RoomReaction, RoomReactionHeaders, RoomReactionMetadata } from './room-reaction.js';\n\ninterface ReactionPayload {\n  data?: {\n    name?: string;\n    metadata?: RoomReactionMetadata;\n  };\n  clientId?: string;\n  timestamp: number;\n  extras?: {\n    headers?: RoomReactionHeaders;\n  };\n}\n\n/**\n * Parses a room reaction from an inbound message.\n * @param message The inbound message containing the reaction data.\n * @param clientId The client ID of the user.\n * @returns The parsed room reaction.\n */\nexport const parseRoomReaction = (message: Ably.InboundMessage, clientId?: string): RoomReaction => {\n  const reactionCreatedMessage = message as ReactionPayload;\n\n  // Use empty string if type is missing or invalid\n  const name =\n    reactionCreatedMessage.data?.name && typeof reactionCreatedMessage.data.name === 'string'\n      ? reactionCreatedMessage.data.name\n      : '';\n\n  // Use empty string if clientId is missing\n  const messageClientId = reactionCreatedMessage.clientId ?? '';\n\n  // Use current time if timestamp is missing\n  const timestamp = reactionCreatedMessage.timestamp ? new Date(reactionCreatedMessage.timestamp) : new Date();\n\n  return new DefaultRoomReaction(\n    name,\n    messageClientId,\n    timestamp,\n    clientId ? clientId === messageClientId : false,\n    reactionCreatedMessage.data?.metadata ?? {},\n    reactionCreatedMessage.extras?.headers ?? {},\n  );\n};\n","import * as Ably from 'ably';\n\nimport { ClientIdResolver } from './client-id.js';\nimport { ErrorCode } from './errors.js';\nimport { RoomReactionEvent, RoomReactionEventType, RoomReactionRealtimeEventType } from './events.js';\nimport { Logger } from './logger.js';\nimport { messageToEphemeral } from './realtime.js';\nimport { subscribe } from './realtime-subscriptions.js';\nimport { RoomReactionHeaders, RoomReactionMetadata } from './room-reaction.js';\nimport { parseRoomReaction } from './room-reaction-parser.js';\nimport { Subscription } from './subscription.js';\nimport EventEmitter, { emitterHasListeners, wrap } from './utils/event-emitter.js';\n\n/**\n * Params for sending a room-level reactions. Only `name` is mandatory.\n */\nexport interface SendReactionParams {\n  /**\n   * The name of the reaction, for example an emoji or a short string such as\n   * \"like\".\n   *\n   * It is the only mandatory parameter to send a room-level reaction.\n   */\n  name: string;\n\n  /**\n   * Optional metadata of the reaction.\n   *\n   * The metadata is a map of extra information that can be attached to the\n   * room reaction. It is not used by Ably and is sent as part of the realtime\n   * message payload. Example use cases are custom animations or other effects.\n   *\n   * Do not use metadata for authoritative information. There is no server-side\n   * validation. When reading the metadata treat it like user input.\n   *\n   */\n  metadata?: RoomReactionMetadata;\n\n  /**\n   * Optional headers of the room reaction.\n   *\n   * The headers are a flat key-value map and are sent as part of the realtime\n   * message's `extras` inside the `headers` property. They can serve similar\n   * purposes as the metadata but they are read by Ably and can be used for\n   * features such as\n   * [subscription filters](https://faqs.ably.com/subscription-filters).\n   *\n   * Do not use the headers for authoritative information. There is no\n   * server-side validation. When reading the headers treat them like user\n   * input.\n   *\n   */\n  headers?: RoomReactionHeaders;\n}\n\n/**\n * The listener function type for room-level reactions.\n * @param event The reaction event that was received.\n */\nexport type RoomReactionListener = (event: RoomReactionEvent) => void;\n\n/**\n * This interface is used to interact with room-level reactions in a chat room: subscribing to reactions and sending them.\n *\n * Get an instance via {@link Room.reactions}.\n */\nexport interface RoomReactions {\n  /**\n   * Send a reaction to the room including some metadata.\n   *\n   * This method accepts parameters for a room-level reaction. It accepts an object\n   * @param params an object containing {name, headers, metadata} for the room\n   * reaction to be sent. Name is required, metadata and headers are optional.\n   * @throws If the `Connection` is not in the `Connected` state.\n   * @returns The returned promise resolves when the reaction was sent. Note\n   * that it is possible to receive your own reaction via the reactions\n   * listener before this promise resolves.\n   */\n  send(params: SendReactionParams): Promise<void>;\n\n  /**\n   * Subscribe to receive room-level reactions.\n   * @param listener The listener function to be called when a reaction is received.\n   * @returns A response object that allows you to control the subscription.\n   */\n  subscribe(listener: RoomReactionListener): Subscription;\n}\n\ninterface RoomReactionEventsMap {\n  [RoomReactionEventType.Reaction]: RoomReactionEvent;\n}\n\ninterface ReactionPayload {\n  name: string;\n  metadata?: RoomReactionMetadata;\n}\n\n/**\n * @inheritDoc\n */\nexport class DefaultRoomReactions implements RoomReactions {\n  private readonly _channel: Ably.RealtimeChannel;\n  private readonly _connection: Ably.Connection;\n  private readonly _clientIdResolver: ClientIdResolver;\n  private readonly _logger: Logger;\n  private readonly _emitter = new EventEmitter<RoomReactionEventsMap>();\n  private readonly _unsubscribeRoomReactionEvents: () => void;\n\n  /**\n   * Constructs a new `DefaultRoomReactions` instance.\n   * @param channel The Realtime channel instance.\n   * @param connection The connection instance.\n   * @param clientIdResolver The client ID resolver.\n   * @param logger An instance of the Logger.\n   */\n  constructor(\n    channel: Ably.RealtimeChannel,\n    connection: Ably.Connection,\n    clientIdResolver: ClientIdResolver,\n    logger: Logger,\n  ) {\n    this._channel = channel;\n    this._connection = connection;\n    this._clientIdResolver = clientIdResolver;\n    this._logger = logger;\n\n    // Create bound listener\n    const roomReactionEventsListener = this._forwarder.bind(this);\n\n    // Use subscription helper to create cleanup function\n    this._unsubscribeRoomReactionEvents = subscribe(\n      this._channel,\n      [RoomReactionRealtimeEventType.Reaction],\n      roomReactionEventsListener,\n    );\n  }\n\n  /**\n   * @inheritDoc\n   */\n  async send(params: SendReactionParams): Promise<void> {\n    this._logger.trace('RoomReactions.send();', params);\n\n    const { name, metadata, headers } = params;\n\n    if (!name) {\n      throw new Ably.ErrorInfo(\n        'unable to send reaction; name not set and it is required',\n        ErrorCode.InvalidArgument,\n        400,\n      );\n    }\n\n    // CHA-ER3f\n    if (this._connection.state !== 'connected') {\n      throw new Ably.ErrorInfo('unable to send reaction; not connected to Ably', ErrorCode.Disconnected, 400);\n    }\n\n    const payload: ReactionPayload = {\n      name: name,\n      metadata: metadata ?? {},\n    };\n\n    const realtimeMessage: Ably.Message = {\n      name: RoomReactionRealtimeEventType.Reaction,\n      data: payload,\n      extras: {\n        headers: headers ?? {},\n      },\n    };\n\n    return this._channel.publish(messageToEphemeral(realtimeMessage));\n  }\n\n  /**\n   * @inheritDoc\n   */\n  subscribe(listener: RoomReactionListener): Subscription {\n    this._logger.trace(`RoomReactions.subscribe();`);\n    const wrapped = wrap(listener);\n    this._emitter.on(wrapped);\n\n    return {\n      unsubscribe: () => {\n        this._logger.trace('RoomReactions.unsubscribe();');\n        this._emitter.off(wrapped);\n      },\n    };\n  }\n\n  // parses reactions from realtime channel into Reaction objects and forwards them to the EventEmitter\n  private _forwarder = (inbound: Ably.InboundMessage) => {\n    const reaction = parseRoomReaction(inbound, this._clientIdResolver.get());\n    this._emitter.emit(RoomReactionEventType.Reaction, {\n      type: RoomReactionEventType.Reaction,\n      reaction,\n    });\n  };\n\n  /**\n   * Disposes of the room reactions instance, removing all listeners and subscriptions.\n   * This method should be called when the room is being released to ensure proper cleanup.\n   * @internal\n   */\n  dispose(): void {\n    // Remove room reaction event subscriptions using stored unsubscribe function\n    this._unsubscribeRoomReactionEvents();\n\n    // Remove user-level listeners\n    this._emitter.off();\n  }\n\n  /**\n   * Checks if there are any listeners registered by users.\n   * @internal\n   * @returns true if there are listeners, false otherwise.\n   */\n  hasListeners(): boolean {\n    return emitterHasListeners(this._emitter);\n  }\n}\n","import * as Ably from 'ably';\nimport { E_CANCELED, Mutex } from 'async-mutex';\n\nimport { ErrorCode } from './errors.js';\nimport { TypingEventType, TypingSetEvent, TypingSetEventType } from './events.js';\nimport { Logger } from './logger.js';\nimport { ephemeralMessage } from './realtime.js';\nimport { subscribe } from './realtime-subscriptions.js';\nimport { InternalTypingOptions } from './room-options.js';\nimport { Subscription } from './subscription.js';\nimport EventEmitter, { wrap } from './utils/event-emitter.js';\n\n/**\n * This interface is used to interact with typing in a chat room including subscribing to typing events and\n * fetching the current set of typing clients.\n *\n * Get an instance via {@link Room.typing}.\n */\nexport interface Typing {\n  /**\n   * Subscribe a given listener to all typing events from users in the chat room.\n   * @param listener A listener to be called when the typing state of a user in the room changes.\n   * @returns A response object that allows you to control the subscription to typing events.\n   */\n  subscribe(listener: TypingListener): Subscription;\n\n  /**\n   * Get the current typers, a set of clientIds.\n   * @returns The set of clientIds that are currently typing.\n   */\n  get current(): Set<string>;\n\n  /**\n   * This will send a `typing.started` event to the server.\n   * Events are throttled according to the `heartbeatThrottleMs` room option.\n   * If an event has been sent within the interval, this operation is no-op.\n   *\n   *\n   * Calls to `keystroke()` and `stop()` are serialized and will always resolve in the correct order.\n   * - For example, if multiple `keystroke()` calls are made in quick succession before the first `keystroke()` call has\n   * sent a `typing.started` event to the server, followed by one `stop()` call, the `stop()` call will execute\n   * as soon as the first `keystroke()` call completes.\n   * All intermediate `keystroke()` calls will be treated as no-ops.\n   * - The most recent operation (`keystroke()` or `stop()`) will always determine the final state, ensuring operations\n   * resolve to a consistent and correct state.\n   * @returns A promise which resolves upon success of the operation and rejects with an {@link Ably.ErrorInfo} object upon its failure.\n   * @throws If the `Connection` is not in the `Connected` state.\n   * @throws If the operation fails to send the event to the server.\n   * @throws If there is a problem acquiring the mutex that controls serialization.\n   */\n  keystroke(): Promise<void>;\n\n  /**\n   * This will send a `typing.stopped` event to the server.\n   * If the user was not currently typing, this operation is no-op.\n   *\n   * Calls to `keystroke()` and `stop()` are serialized and will always resolve in the correct order.\n   * - For example, if multiple `keystroke()` calls are made in quick succession before the first `keystroke()` call has\n   * sent a `typing.started` event to the server, followed by one `stop()` call, the `stop()` call will execute\n   * as soon as the first `keystroke()` call completes.\n   * All intermediate `keystroke()` calls will be treated as no-ops.\n   * - The most recent operation (`keystroke()` or `stop()`) will always determine the final state, ensuring operations\n   * resolve to a consistent and correct state.\n   * @returns A promise which resolves upon success of the operation and rejects with an {@link Ably.ErrorInfo} object upon its failure.\n   * @throws If the `Connection` is not in the `Connected` state.\n   * @throws If the operation fails to send the event to the server.\n   * @throws If there is a problem acquiring the mutex that controls serialization.\n   */\n  stop(): Promise<void>;\n}\n\n/**\n * A listener which listens for typing events.\n * @param event The typing event.\n */\nexport type TypingListener = (event: TypingSetEvent) => void;\n\n/**\n * Represents the typing events mapped to their respective event payloads.\n */\ninterface TypingEventsMap {\n  [TypingSetEventType.SetChanged]: TypingSetEvent;\n}\n\n/**\n * Represents a timer handle that can be undefined.\n */\ntype TypingTimerHandle = ReturnType<typeof setTimeout> | undefined;\n\n/**\n * @inheritDoc\n */\nexport class DefaultTyping extends EventEmitter<TypingEventsMap> implements Typing {\n  private readonly _channel: Ably.RealtimeChannel;\n  private readonly _connection: Ably.Connection;\n  private readonly _logger: Logger;\n\n  // Throttle for the heartbeat, how often we should emit a typing event with repeated calls to keystroke()\n  // CHA-T10\n  private readonly _heartbeatThrottleMs: number;\n\n  // Grace period for inactivity before another user is considered to have stopped typing\n  // CHA-T10a\n  private readonly _timeoutMs = 2000;\n  private _heartbeatTimerId: TypingTimerHandle;\n  private readonly _currentlyTyping: Map<string, TypingTimerHandle>;\n\n  // Mutex for controlling `keystroke` and `stop` operations\n  private readonly _mutex = new Mutex();\n\n  // Cleanup function for the channel subscription\n  private readonly _unsubscribeTypingEvents: () => void;\n\n  /**\n   * Constructs a new `DefaultTyping` instance.\n   * @param options The options for typing in the room.\n   * @param connection The connection instance.\n   * @param channel The channel for the room.\n   * @param logger An instance of the Logger.\n   */\n  constructor(\n    options: InternalTypingOptions,\n    connection: Ably.Connection,\n    channel: Ably.RealtimeChannel,\n    logger: Logger,\n  ) {\n    super();\n    this._channel = channel;\n    this._connection = connection;\n\n    // Interval for the heartbeat, how often we should emit a typing event with repeated calls to start()\n    this._heartbeatThrottleMs = options.heartbeatThrottleMs;\n\n    // Map of clientIds to their typing timers, used to track typing state\n    this._currentlyTyping = new Map<string, TypingTimerHandle>();\n    this._logger = logger;\n\n    // Use subscription helper to create cleanup function\n    this._unsubscribeTypingEvents = subscribe(\n      this._channel,\n      [TypingEventType.Started, TypingEventType.Stopped],\n      this._internalSubscribeToEvents.bind(this),\n    );\n  }\n\n  /**\n   * Clears all typing states.\n   * This includes clearing all timeouts and the currently typing map.\n   */\n  private _clearAllTypingStates(): void {\n    this._logger.debug(`DefaultTyping._clearAllTypingStates(); clearing all typing states`);\n    this._clearHeartbeatTimer();\n    this._clearCurrentlyTyping();\n  }\n\n  /**\n   * Clears the heartbeat timer.\n   */\n  private _clearHeartbeatTimer(): void {\n    this._logger.trace(`DefaultTyping._clearHeartbeatTimer(); clearing heartbeat timer`);\n    if (this._heartbeatTimerId) {\n      clearTimeout(this._heartbeatTimerId);\n      this._heartbeatTimerId = undefined;\n    }\n  }\n\n  /**\n   * Clears the currently typing store and removes all timeouts for associated clients.\n   */\n  private _clearCurrentlyTyping(): void {\n    this._logger.trace('DefaultTyping._clearCurrentlyTyping(); clearing current store and timeouts');\n    // Clear all client typing timeouts\n    for (const [, timeoutId] of this._currentlyTyping.entries()) {\n      clearTimeout(timeoutId);\n    }\n    // Clear the currently typing map\n    this._currentlyTyping.clear();\n  }\n\n  /**\n   * CHA-T16\n   * @inheritDoc\n   */\n  get current(): Set<string> {\n    this._logger.trace(`DefaultTyping.current();`);\n    return new Set<string>(this._currentlyTyping.keys());\n  }\n\n  /**\n   * @inheritDoc\n   */\n  get channel(): Ably.RealtimeChannel {\n    return this._channel;\n  }\n\n  /**\n   * Start the heartbeat timer. This will expire after the configured interval.\n   */\n  private _startHeartbeatTimer(): void {\n    if (!this._heartbeatTimerId) {\n      this._logger.trace(`DefaultTyping.startHeartbeatTimer();`);\n      const timer = (this._heartbeatTimerId = setTimeout(() => {\n        this._logger.debug(`DefaultTyping.startHeartbeatTimer(); heartbeat timer expired`);\n        // CHA-T2a\n        if (timer === this._heartbeatTimerId) {\n          this._heartbeatTimerId = undefined;\n        }\n      }, this._heartbeatThrottleMs));\n    }\n  }\n\n  /**\n   * @inheritDoc\n   */\n  async keystroke(): Promise<void> {\n    this._logger.trace(`DefaultTyping.keystroke();`);\n    this._mutex.cancel();\n\n    // Acquire a mutex\n    try {\n      await this._mutex.acquire();\n    } catch (error: unknown) {\n      if (error === E_CANCELED) {\n        this._logger.debug(`DefaultTyping.keystroke(); mutex was canceled by a later operation`);\n        return;\n      }\n      throw new Ably.ErrorInfo(\n        'unable to send keystroke event; failed to enforce sequential execution of the operation',\n        ErrorCode.OperationSerializationFailed,\n        500,\n      );\n    }\n    try {\n      // Check if connection is connected\n      // CHA-T4e\n      if (this._connection.state !== 'connected') {\n        this._logger.error(`DefaultTyping.keystroke(); connection is not connected`, {\n          status: this._connection.state,\n        });\n        throw new Ably.ErrorInfo(\n          'unable to send typing keystroke event; disconnected from Ably',\n          ErrorCode.Disconnected,\n          400,\n        );\n      }\n\n      // Check whether user is already typing before publishing again\n      // CHA-T4c1, CHA-T4c2\n      if (this._heartbeatTimerId) {\n        this._logger.debug(`DefaultTyping.keystroke(); no-op, already typing and heartbeat timer has not expired`);\n        return;\n      }\n\n      // Perform the publish\n      // CHA-T4a3\n      await this._channel.publish(ephemeralMessage(TypingEventType.Started));\n\n      // Start the timer after publishing\n      // CHA-T4a5\n      this._startHeartbeatTimer();\n      this._logger.trace(`DefaultTyping.keystroke(); starting timers`);\n    } finally {\n      this._logger.trace(`DefaultTyping.keystroke(); releasing mutex`);\n      this._mutex.release();\n    }\n  }\n\n  /**\n   * @inheritDoc\n   */\n  async stop(): Promise<void> {\n    this._logger.trace(`DefaultTyping.stop();`);\n\n    this._mutex.cancel();\n    // Acquire a mutex\n    try {\n      await this._mutex.acquire();\n    } catch (error: unknown) {\n      if (error === E_CANCELED) {\n        this._logger.debug(`DefaultTyping.stop(); mutex was canceled by a later operation`);\n        return;\n      }\n      throw new Ably.ErrorInfo(\n        'unable to send typing stop event; failed to enforce sequential execution of the operation',\n        ErrorCode.OperationSerializationFailed,\n        500,\n      );\n    }\n    try {\n      // Check if connection is connected\n      if (this._connection.state !== 'connected') {\n        this._logger.error(`DefaultTyping.stop(); connection is not connected`, {\n          status: this._connection.state,\n        });\n        throw new Ably.ErrorInfo(\n          'unable to send typing stop event; disconnected from Ably',\n          ErrorCode.Disconnected,\n          400,\n        );\n      }\n\n      // If the user is not typing, do nothing.\n      // CHA-T5f\n      if (!this._heartbeatTimerId) {\n        this._logger.debug(`DefaultTyping.stop(); no-op, not currently typing`);\n        return;\n      }\n\n      // CHA-T5d\n      await this._channel.publish(ephemeralMessage(TypingEventType.Stopped));\n      this._logger.trace(`DefaultTyping.stop(); clearing timers`);\n\n      // CHA-T5e\n      // Clear the heartbeat timer\n      clearTimeout(this._heartbeatTimerId);\n      this._heartbeatTimerId = undefined;\n    } finally {\n      this._logger.trace(`DefaultTyping.stop(); releasing mutex`);\n      this._mutex.release();\n    }\n  }\n\n  /**\n   * @inheritDoc\n   */\n  subscribe(listener: TypingListener): Subscription {\n    this._logger.trace(`DefaultTyping.subscribe();`);\n    const wrapped = wrap(listener);\n    this.on(wrapped);\n\n    return {\n      unsubscribe: () => {\n        this._logger.trace('DefaultTyping.unsubscribe();');\n        this.off(wrapped);\n      },\n    };\n  }\n\n  /**\n   * @inheritDoc\n   */\n  // CHA-RL3h\n  async dispose(): Promise<void> {\n    this._logger.trace(`DefaultTyping.dispose();`);\n\n    // Keep trying to acquire the mutex; wait 200 ms between attempts.\n    for (;;) {\n      try {\n        this._mutex.cancel();\n        await this._mutex.acquire();\n        break; // success – exit the loop\n      } catch (error: unknown) {\n        if (error === E_CANCELED) {\n          // In this case, the mutex was canceled by a later operation,\n          // but we are trying to release, so we should always take precedence here.\n          // Let's continue trying to acquire it until we win the acquisition lock.\n          this._logger.debug(`DefaultTyping.dispose(); mutex was canceled`);\n          await new Promise((resolve) => setTimeout(resolve, 200));\n          this._logger.debug(`DefaultTyping.dispose(); retrying mutex acquisition`);\n        } else {\n          // If we encounter any other error, we log it and exit the loop.\n          // This is to ensure that we don't get stuck in an infinite loop\n          // if the mutex acquisition fails for some other non-retryable reason.\n          this._logger.error(`DefaultTyping.dispose(); failed to acquire mutex; could not complete resource disposal`, {\n            error,\n          });\n          return;\n        }\n      }\n    }\n    this._clearAllTypingStates();\n    this._unsubscribeTypingEvents();\n    this.off();\n    this._mutex.release();\n  }\n\n  /**\n   * Update the currently typing users. This method is called when a typing event is received.\n   * It will also acquire a mutex to ensure that the currentlyTyping state is updated safely.\n   * @param clientId The client ID of the user.\n   * @param event The typing event.\n   */\n  private _updateCurrentlyTyping(clientId: string, event: TypingEventType): void {\n    this._logger.trace(`DefaultTyping._updateCurrentlyTyping();`, { clientId, event });\n\n    if (event === TypingEventType.Started) {\n      this._handleTypingStart(clientId);\n    } else {\n      this._handleTypingStop(clientId);\n    }\n  }\n\n  /**\n   * Starts a new inactivity timer for the client.\n   * This timer will expire after the configured timeout,\n   * which is the sum of the heartbeat interval and the inactivity timeout.\n   * @param clientId The client ID for which to start the timer.\n   * @returns The timeout ID for the new timer.\n   */\n  private _startNewClientInactivityTimer(clientId: string): ReturnType<typeof setTimeout> {\n    this._logger.trace(`DefaultTyping._startNewClientInactivityTimer(); starting new inactivity timer`, {\n      clientId,\n    });\n    // Set or reset the typing timeout for this client\n    const timeoutId = setTimeout(() => {\n      this._logger.trace(`DefaultTyping._startNewClientInactivityTimer(); client typing timeout expired`, {\n        clientId,\n      });\n      // Verify the timer is still valid (it might have been reset)\n      if (this._currentlyTyping.get(clientId) !== timeoutId) {\n        this._logger.debug(`DefaultTyping._startNewClientInactivityTimer(); timeout already cleared; ignoring`, {\n          clientId,\n        });\n        return;\n      }\n\n      // Remove client whose timeout has expired\n      this._currentlyTyping.delete(clientId);\n      this.emit(TypingSetEventType.SetChanged, {\n        type: TypingSetEventType.SetChanged,\n        currentlyTyping: new Set<string>(this._currentlyTyping.keys()),\n        change: {\n          clientId,\n          type: TypingEventType.Stopped,\n        },\n      });\n    }, this._heartbeatThrottleMs + this._timeoutMs);\n    return timeoutId;\n  }\n\n  /**\n   * Handles logic for TypingEventType.Started, including starting a new timeout or resetting an existing one.\n   * @param clientId The client ID that started typing.\n   */\n  private _handleTypingStart(clientId: string): void {\n    this._logger.debug(`DefaultTyping._handleTypingStart();`, { clientId });\n    // Start a new timeout for the client\n    const timeoutId = this._startNewClientInactivityTimer(clientId);\n\n    const existingTimeout = this._currentlyTyping.get(clientId);\n\n    // Set the new timeout for the client\n    this._currentlyTyping.set(clientId, timeoutId);\n\n    if (existingTimeout) {\n      // Heartbeat - User is already typing, we just need to clear the existing timeout\n      this._logger.debug(`DefaultTyping._handleTypingStart(); received heartbeat for currently typing client`, {\n        clientId,\n      });\n      clearTimeout(existingTimeout);\n    } else {\n      // Otherwise, we need to emit a new typing event\n      this._logger.debug(`DefaultTyping._handleTypingStart(); new client started typing`, {\n        clientId,\n      });\n      this.emit(TypingSetEventType.SetChanged, {\n        type: TypingSetEventType.SetChanged,\n        currentlyTyping: new Set<string>(this._currentlyTyping.keys()),\n        change: {\n          clientId,\n          type: TypingEventType.Started,\n        },\n      });\n    }\n  }\n\n  /**\n   * Handles logic for TypingEventType.Stopped, including clearing the timeout for the client.\n   * @param clientId The client ID that stopped typing.\n   */\n  private _handleTypingStop(clientId: string): void {\n    const existingTimeout = this._currentlyTyping.get(clientId);\n    if (!existingTimeout) {\n      // Stop requested for a client that isn't currently typing\n      this._logger.trace(\n        `DefaultTyping._handleTypingStop(); received \"Stop\" event for client not in currentlyTyping list`,\n        { clientId },\n      );\n      return;\n    }\n\n    // Stop typing: clear their timeout and remove from the currently typing set\n    this._logger.debug(`DefaultTyping._handleTypingStop(); client stopped typing`, { clientId });\n    clearTimeout(existingTimeout);\n    this._currentlyTyping.delete(clientId);\n    // Emit stop event only when the client is removed\n    this.emit(TypingSetEventType.SetChanged, {\n      type: TypingSetEventType.SetChanged,\n      currentlyTyping: new Set<string>(this._currentlyTyping.keys()),\n      change: {\n        clientId,\n        type: TypingEventType.Stopped,\n      },\n    });\n  }\n\n  /**\n   * Subscribe to internal events. This listens to events and converts them into typing updates, with validation.\n   * @param inbound The inbound message containing typing event data.\n   */\n  private _internalSubscribeToEvents = (inbound: Ably.InboundMessage): void => {\n    const { name, clientId } = inbound;\n    this._logger.trace(`DefaultTyping._internalSubscribeToEvents(); received event`, {\n      name,\n      clientId,\n    });\n\n    if (!clientId) {\n      this._logger.error(`DefaultTyping._internalSubscribeToEvents(); invalid clientId in received event`, {\n        inbound,\n      });\n      return;\n    }\n\n    // Safety check to ensure we are handling only typing events\n    if (name === TypingEventType.Started || name === TypingEventType.Stopped) {\n      this._updateCurrentlyTyping(clientId, name);\n    } else {\n      this._logger.warn(`DefaultTyping._internalSubscribeToEvents(); unrecognized event`, {\n        name,\n      });\n    }\n  };\n\n  get heartbeatThrottleMs(): number {\n    return this._heartbeatThrottleMs;\n  }\n\n  get hasHeartbeatTimer(): boolean {\n    return !!this._heartbeatTimerId;\n  }\n}\n","import * as Ably from 'ably';\nimport cloneDeep from 'lodash.clonedeep';\n\nimport { ChannelManager } from './channel-manager.js';\nimport { ChatApi } from './chat-api.js';\nimport { ClientIdResolver } from './client-id.js';\nimport { DiscontinuityListener } from './discontinuity.js';\nimport { Logger } from './logger.js';\nimport { DefaultMessageReactions } from './message-reactions.js';\nimport { DefaultMessages, Messages } from './messages.js';\nimport { DefaultOccupancy, Occupancy } from './occupancy.js';\nimport { DefaultPresence, Presence } from './presence.js';\nimport { RoomLifecycleManager } from './room-lifecycle-manager.js';\nimport { InternalRoomOptions, RoomOptions, validateRoomOptions } from './room-options.js';\nimport { DefaultRoomReactions, RoomReactions } from './room-reactions.js';\nimport { DefaultRoomLifecycle, InternalRoomLifecycle, RoomStatus, RoomStatusListener } from './room-status.js';\nimport { StatusSubscription } from './subscription.js';\nimport { DefaultTyping, Typing } from './typing.js';\n\n/**\n * Represents a chat room.\n */\nexport interface Room {\n  /**\n   * The unique identifier of the room.\n   * @returns The room name.\n   */\n  get name(): string;\n\n  /**\n   * Allows you to send, subscribe-to and query messages in the room.\n   * @returns The messages instance for the room.\n   */\n  get messages(): Messages;\n\n  /**\n   * Allows you to subscribe to presence events in the room.\n   * @returns The presence instance for the room.\n   */\n  get presence(): Presence;\n\n  /**\n   * Allows you to interact with room-level reactions.\n   * @returns The room reactions instance for the room.\n   */\n  get reactions(): RoomReactions;\n\n  /**\n   * Allows you to interact with typing events in the room.\n   * @returns The typing instance for the room.\n   */\n  get typing(): Typing;\n\n  /**\n   * Allows you to interact with occupancy metrics for the room.\n   * @returns The occupancy instance for the room.\n   */\n  get occupancy(): Occupancy;\n\n  /**\n   * The current status of the room.\n   * @returns The current status.\n   */\n  get status(): RoomStatus;\n\n  /**\n   * The current error, if any, that caused the room to enter the current status.\n   */\n  get error(): Ably.ErrorInfo | undefined;\n\n  /**\n   * Registers a listener that will be called whenever the room status changes.\n   * @param listener The function to call when the status changes.\n   * @returns An object that can be used to unregister the listener.\n   */\n  onStatusChange(listener: RoomStatusListener): StatusSubscription;\n\n  /**\n   * Attaches to the room to receive events in realtime.\n   *\n   * If a room fails to attach, it will enter either the {@link RoomStatus.Suspended} or {@link RoomStatus.Failed} state.\n   *\n   * If the room enters the failed state, then it will not automatically retry attaching and intervention is required.\n   *\n   * If the room enters the suspended state, then the call to attach will reject with the {@link ErrorInfo} that caused the suspension. However,\n   * the room will automatically retry attaching after a delay.\n   * @returns A promise that resolves when the room is attached.\n   */\n  attach(): Promise<void>;\n\n  /**\n   * Detaches from the room to stop receiving events in realtime.\n   * @returns A promise that resolves when the room is detached.\n   */\n  detach(): Promise<void>;\n\n  /**\n   * Returns the room options.\n   * @returns A copy of the options used to create the room.\n   */\n  options(): RoomOptions;\n\n  /**\n   * Registers a handler that will be called whenever a discontinuity is detected in the room's connection.\n   * A discontinuity occurs when the room's connection is interrupted and cannot be resumed from its previous state.\n   * @param handler The function to call when a discontinuity is detected.\n   * @returns An object that can be used to unregister the handler.\n   */\n  onDiscontinuity(handler: DiscontinuityListener): StatusSubscription;\n\n  /**\n   * Get the underlying Ably realtime channel used for the room.\n   * @returns The realtime channel.\n   */\n  get channel(): Ably.RealtimeChannel;\n}\n\nexport class DefaultRoom implements Room {\n  private readonly _name: string;\n  private readonly _options: RoomOptions;\n  private readonly _chatApi: ChatApi;\n  private readonly _messages: DefaultMessages;\n  private readonly _typing: DefaultTyping;\n  private readonly _presence: DefaultPresence;\n  private readonly _reactions: DefaultRoomReactions;\n  private readonly _occupancy: DefaultOccupancy;\n  private readonly _logger: Logger;\n  private readonly _lifecycle: DefaultRoomLifecycle;\n  private readonly _lifecycleManager: RoomLifecycleManager;\n  private readonly _finalizer: () => Promise<void>;\n  private readonly _channelManager: ChannelManager;\n\n  /**\n   * A random identifier for the room instance, useful in debugging and logging.\n   */\n  private readonly _nonce: string;\n\n  /**\n   * Constructs a new Room instance.\n   * @param name The unique identifier of the room.\n   * @param nonce A random identifier for the room instance, useful in debugging and logging.\n   * @param options The options for the room.\n   * @param realtime An instance of the Ably Realtime client.\n   * @param chatApi An instance of the ChatApi.\n   * @param clientIdResolver An instance of the ClientIdResolver.\n   * @param logger An instance of the Logger.\n   */\n  constructor(\n    name: string,\n    nonce: string,\n    options: InternalRoomOptions,\n    realtime: Ably.Realtime,\n    chatApi: ChatApi,\n    clientIdResolver: ClientIdResolver,\n    logger: Logger,\n  ) {\n    validateRoomOptions(options);\n    this._nonce = nonce;\n\n    // Create a logger with room context\n    this._logger = logger.withContext({ roomName: name, roomNonce: nonce });\n    this._logger.debug('Room();', { options });\n\n    this._name = name;\n    this._options = options;\n    this._chatApi = chatApi;\n    this._lifecycle = new DefaultRoomLifecycle(this._logger);\n\n    const channelManager = (this._channelManager = this._getChannelManager(options, realtime, this._logger));\n    const channel = channelManager.get();\n\n    // Setup features\n    this._messages = new DefaultMessages(name, options.messages, channel, this._chatApi, this._logger);\n    this._presence = new DefaultPresence(channel, this._logger, options);\n    this._typing = new DefaultTyping(options.typing, realtime.connection, channel, this._logger);\n    this._reactions = new DefaultRoomReactions(channel, realtime.connection, clientIdResolver, this._logger);\n    this._occupancy = new DefaultOccupancy(name, channel, this._chatApi, this._logger, options);\n\n    // Set the lifecycle manager last, so it becomes the last thing to find out about channel state changes\n    // This is to allow Messages to reset subscription points before users get told of a discontinuity\n    this._lifecycleManager = new RoomLifecycleManager(channelManager, this._lifecycle, this._logger);\n\n    // Setup a finalization function to clean up resources\n    let finalized = false;\n    this._finalizer = async () => {\n      // Cycle the channels in the feature and release them from the realtime client\n      if (finalized) {\n        this._logger.debug('Room.finalizer(); already finalized');\n        return;\n      }\n\n      // Release via the lifecycle manager\n      await this._lifecycleManager.release();\n\n      // Dispose of the lifecycle manager, removing all user-registered listeners from emitters\n      // and any listeners that have been registered to the realtime instance\n      this._lifecycleManager.dispose();\n\n      // Dispose of all features, removing any listeners that have been subscribed to the realtime instance\n      // and also removing any user-level listeners from the emitters\n      this._messages.dispose();\n      this._presence.dispose();\n      this._reactions.dispose();\n      this._occupancy.dispose();\n      await this._typing.dispose();\n\n      // Dispose of the RoomStatus instance\n      this._lifecycle.dispose();\n\n      finalized = true;\n    };\n  }\n\n  /**\n   * Gets the channel manager for the room, which handles merging channel options together and creating channels.\n   * @param options The room options.\n   * @param realtime  An instance of the Ably Realtime client.\n   * @param logger An instance of the Logger.\n   * @returns The channel manager instance.\n   */\n  private _getChannelManager(options: InternalRoomOptions, realtime: Ably.Realtime, logger: Logger): ChannelManager {\n    const manager = new ChannelManager(this._name, realtime, logger, options.isReactClient);\n\n    manager.mergeOptions(DefaultOccupancy.channelOptionMerger(options));\n    manager.mergeOptions(DefaultPresence.channelOptionMerger(options));\n    manager.mergeOptions(DefaultMessageReactions.channelOptionMerger(options));\n    return manager;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get name(): string {\n    return this._name;\n  }\n\n  /**\n   * @inheritDoc\n   */\n  options(): RoomOptions {\n    return cloneDeep(this._options);\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get messages(): Messages {\n    return this._messages;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get presence(): Presence {\n    return this._presence;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get reactions(): RoomReactions {\n    return this._reactions;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get typing(): Typing {\n    return this._typing;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get occupancy(): Occupancy {\n    return this._occupancy;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get status(): RoomStatus {\n    return this._lifecycle.status;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get error(): Ably.ErrorInfo | undefined {\n    return this._lifecycle.error;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  onStatusChange(listener: RoomStatusListener): StatusSubscription {\n    return this._lifecycle.onChange(listener);\n  }\n\n  /**\n   * @inheritdoc\n   */\n  async attach() {\n    this._logger.trace('Room.attach();');\n    return this._lifecycleManager.attach();\n  }\n\n  /**\n   * @inheritdoc\n   */\n  async detach(): Promise<void> {\n    this._logger.trace('Room.detach();');\n    return this._lifecycleManager.detach();\n  }\n\n  /**\n   * Releases resources associated with the room.\n   * @returns A promise that resolves when the room is released.\n   */\n  async release(): Promise<void> {\n    this._logger.trace('Room.release();');\n    return this._finalizer();\n  }\n\n  /**\n   * A random identifier for the room instance, useful in debugging and logging.\n   * @returns The nonce.\n   */\n  get nonce(): string {\n    return this._nonce;\n  }\n\n  /**\n   * @internal\n   * @returns The internal room lifecycle.\n   */\n  get lifecycle(): InternalRoomLifecycle {\n    return this._lifecycle;\n  }\n\n  /**\n   * @internal\n   * @returns The room lifecycle manager.\n   */\n  get lifecycleManager(): RoomLifecycleManager {\n    return this._lifecycleManager;\n  }\n\n  /**\n   * @inheritdoc\n   */\n  onDiscontinuity(handler: DiscontinuityListener): StatusSubscription {\n    this._logger.trace('Room.onDiscontinuity();');\n    return this._lifecycleManager.onDiscontinuity(handler);\n  }\n\n  /**\n   * @inheritdoc\n   */\n  get channel(): Ably.RealtimeChannel {\n    return this._channelManager.get();\n  }\n}\n","import * as Ably from 'ably';\nimport { dequal } from 'dequal';\n\nimport { ChatApi } from './chat-api.js';\nimport { ClientIdResolver } from './client-id.js';\nimport { ErrorCode } from './errors.js';\nimport { randomId } from './id.js';\nimport { Logger } from './logger.js';\nimport { DefaultRoom, Room } from './room.js';\nimport { normalizeRoomOptions, RoomOptions } from './room-options.js';\n\n/**\n * Manages the lifecycle of chat rooms.\n */\nexport interface Rooms {\n  /**\n   * Gets a room reference by its unique identifier. The Rooms class ensures that only one reference\n   * exists for each room. A new reference object is created if it doesn't already\n   * exist, or if the one used previously was released using release(name).\n   *\n   * Always call `release(name)` after the Room object is no longer needed.\n   *\n   * If a call to `get` is made for a room that is currently being released, then the promise will resolve only when\n   * the release operation is complete.\n   *\n   * If a call to `get` is made, followed by a subsequent call to `release` before the promise resolves, then the\n   * promise will reject with an error.\n   * @param name The unique identifier of the room.\n   * @param options The options for the room.\n   * @throws {@link ErrorInfo} if a room with the same name but different options already exists.\n   * @returns Room A promise to a new or existing Room object.\n   */\n  get(name: string, options?: RoomOptions): Promise<Room>;\n\n  /**\n   * Release the Room object if it exists. This method only releases the reference\n   * to the Room object from the Rooms instance and detaches the room from Ably. It does not unsubscribe to any\n   * events.\n   *\n   * After calling this function, the room object is no-longer usable. If you wish to get the room object again,\n   * you must call {@link Rooms.get}.\n   *\n   * Calling this function will abort any in-progress `get` calls for the same room.\n   * @param name The unique identifier of the room.\n   */\n  release(name: string): Promise<void>;\n\n  /**\n   * Disposes all rooms that are currently in the rooms map.\n   * This method releases all rooms concurrently and clears the rooms map.\n   * @returns A promise that resolves when all rooms have been released.\n   */\n  dispose(): Promise<void>;\n}\n\n/**\n * An internal interface for Rooms.\n * @internal\n */\nexport interface InternalRooms extends Rooms {\n  /**\n   * Get the number of rooms currently in the rooms map.\n   * @returns The number of rooms currently in the rooms map.\n   */\n  get count(): number;\n}\n\n/**\n * Represents an entry in the chat room map.\n */\ninterface RoomMapEntry {\n  /**\n   * The promise that will eventually resolve to the room.\n   */\n  promise: Promise<DefaultRoom>;\n\n  /**\n   * A random, internal identifier useful for debugging and logging.\n   */\n  nonce: string;\n\n  /**\n   * The options for the room.\n   */\n  options: RoomOptions | undefined;\n\n  /**\n   * An abort controller to abort the get operation if the room is released before the get operation completes.\n   */\n  abort?: AbortController;\n}\n\n/**\n * Manages the chat rooms.\n */\nexport class DefaultRooms implements InternalRooms {\n  private readonly _realtime: Ably.Realtime;\n  private readonly _chatApi: ChatApi;\n  private readonly _rooms: Map<string, RoomMapEntry> = new Map<string, RoomMapEntry>();\n  private readonly _releasing = new Map<string, Promise<void>>();\n  private readonly _clientIdResolver: ClientIdResolver;\n  private readonly _logger: Logger;\n  private _isReact = false;\n  private _disposed = false;\n\n  /**\n   * Constructs a new Rooms instance.\n   * @param realtime An instance of the Ably Realtime client.\n   * @param clientIdResolver A resolver for the clientId.\n   * @param logger An instance of the Logger.\n   */\n  constructor(realtime: Ably.Realtime, clientIdResolver: ClientIdResolver, logger: Logger) {\n    this._realtime = realtime;\n    this._chatApi = new ChatApi(realtime, logger);\n    this._clientIdResolver = clientIdResolver;\n    this._logger = logger;\n  }\n\n  /**\n   * @inheritDoc\n   */\n  async get(name: string, options?: RoomOptions): Promise<Room> {\n    this._logger.trace('Rooms.get();', { roomName: name });\n\n    this._ensureNotDisposed();\n\n    const existingRoom = this._rooms.get(name);\n    if (existingRoom) {\n      return this._handleExistingRoom(existingRoom, name, options);\n    }\n\n    const ongoingRelease = this._releasing.get(name);\n    const nonce = randomId();\n\n    if (!ongoingRelease) {\n      return this._createNewRoom(name, nonce, options);\n    }\n\n    return this._waitForReleaseAndCreateRoom(name, nonce, options, ongoingRelease);\n  }\n\n  /**\n   * @inheritDoc\n   */\n  async release(name: string): Promise<void> {\n    this._logger.trace('Rooms.release();', { roomName: name });\n\n    const existingRoom = this._rooms.get(name);\n    const ongoingRelease = this._releasing.get(name);\n\n    if (!existingRoom) {\n      return this._handleNonExistentRoomRelease(name, ongoingRelease);\n    }\n\n    if (ongoingRelease) {\n      return this._handleConcurrentRelease(name, existingRoom, ongoingRelease);\n    }\n\n    return this._performRoomRelease(name, existingRoom);\n  }\n\n  /**\n   * Disposes all rooms that are currently in the rooms map and waits for any ongoing release operations to complete.\n   * This method releases all rooms concurrently, waits for any in-flight releases to finish, and clears the rooms map.\n   * After this method resolves, all rooms will have been fully released and cleaned up.\n   * @internal\n   * @returns A promise that resolves when all rooms have been released.\n   */\n  async dispose(): Promise<void> {\n    this._logger.trace('Rooms.dispose();');\n\n    // Mark this instance as disposed\n    this._disposed = true;\n\n    // Get all room names currently in the map\n    const roomNames = [...this._rooms.keys()];\n\n    if (roomNames.length === 0) {\n      this._logger.debug('Rooms.dispose(); no rooms to release');\n      return;\n    }\n\n    // Release all rooms concurrently\n    const releasePromises = roomNames.map(async (roomName) => this.release(roomName));\n\n    // Ensure we wait for all ongoing releases too, since we guarantee that all rooms are released after this call\n    // resolves.\n    const inFlight = [...this._releasing.values()];\n    const all = [...releasePromises, ...inFlight];\n\n    this._logger.debug('Rooms.dispose(); releasing rooms', { roomCount: roomNames.length, roomNames });\n\n    await Promise.all(all);\n    this._logger.debug('Rooms.dispose(); all rooms released successfully');\n  }\n\n  /**\n   * @inheritDoc\n   */\n  get count(): number {\n    return this._rooms.size;\n  }\n\n  /**\n   * Ensures the rooms instance has not been disposed.\n   */\n  private _ensureNotDisposed(): void {\n    if (this._disposed) {\n      throw new Ably.ErrorInfo('unable to get room; rooms instance has been disposed', ErrorCode.ResourceDisposed, 400);\n    }\n  }\n\n  /**\n   * Handles the case where a room already exists.\n   * @param existingRoom The existing room entry in the map.\n   * @param name The unique identifier of the room.\n   * @param options The options for the room.\n   * @returns A promise that resolves to the existing room.\n   */\n  private async _handleExistingRoom(existingRoom: RoomMapEntry, name: string, options?: RoomOptions): Promise<Room> {\n    if (!dequal(existingRoom.options, options)) {\n      throw new Ably.ErrorInfo(\n        'unable to get room; room already exists with different options',\n        ErrorCode.RoomExistsWithDifferentOptions,\n        400,\n      );\n    }\n\n    this._logger.debug('Rooms.get(); returning existing room', {\n      roomName: name,\n      nonce: existingRoom.nonce,\n      options,\n    });\n    return await existingRoom.promise;\n  }\n\n  /**\n   * Creates a new room when no existing room or ongoing release exists.\n   * @param name The unique identifier of the room.\n   * @param nonce A random, internal identifier useful for debugging and logging.\n   * @param options The options for the room.\n   * @returns A new room object.\n   */\n  private _createNewRoom(name: string, nonce: string, options?: RoomOptions): Room {\n    const room = this._makeRoom(name, nonce, options);\n    const entry: RoomMapEntry = {\n      promise: Promise.resolve(room),\n      nonce: nonce,\n      options: options,\n    };\n\n    this._rooms.set(name, entry);\n    this._logger.debug('Rooms.get(); returning new room', { roomName: name, nonce: room.nonce });\n    return room; // No need to await Promise.resolve(room)\n  }\n\n  /**\n   * Waits for an ongoing release to complete, then creates a new room.\n   * @param name The unique identifier of the room.\n   * @param nonce A random, internal identifier useful for debugging and logging.\n   * @param options The options for the room.\n   * @param ongoingRelease The promise of an ongoing release operation.\n   * @returns A promise that resolves to a room.\n   */\n  private async _waitForReleaseAndCreateRoom(\n    name: string,\n    nonce: string,\n    options: RoomOptions | undefined,\n    ongoingRelease: Promise<void>,\n  ): Promise<Room> {\n    const abortController = new AbortController();\n    const roomPromise = this._createAbortableRoomPromise(name, nonce, options, ongoingRelease, abortController);\n\n    this._rooms.set(name, {\n      promise: roomPromise,\n      options: options,\n      nonce: nonce,\n      abort: abortController,\n    });\n\n    this._logger.debug('Rooms.get(); creating new promise dependent on previous release', { roomName: name });\n    return await roomPromise;\n  }\n\n  /**\n   * Creates a promise that can be aborted if the room is released before completion.\n   * @param name The unique identifier of the room.\n   * @param nonce A random, internal identifier useful for debugging and logging.\n   * @param options The options for the room.\n   * @param ongoingRelease A promise that resolves when the previous release operation is complete.\n   * @param abortController An AbortController to manage the abort signal.\n   * @returns A promise that resolves to a new room or rejects if the operation is aborted.\n   */\n  private async _createAbortableRoomPromise(\n    name: string,\n    nonce: string,\n    options: RoomOptions | undefined,\n    ongoingRelease: Promise<void>,\n    abortController: AbortController,\n  ): Promise<DefaultRoom> {\n    // Create a promise that rejects when the abort signal fires\n    const abortPromise = new Promise<never>((_, reject) => {\n      const abortListener = () => {\n        this._logger.debug('Rooms.get(); aborted before init', { roomName: name });\n        reject(\n          new Ably.ErrorInfo(\n            'unable to get room; room released before operation could complete',\n            ErrorCode.RoomReleasedBeforeOperationCompleted,\n            400,\n          ),\n        );\n      };\n\n      abortController.signal.addEventListener('abort', abortListener, { once: true });\n    });\n\n    // Race between the ongoing release and the abort signal\n    await Promise.race([ongoingRelease, abortPromise]);\n\n    // If we get here, the release completed without being aborted\n    this._logger.debug('Rooms.get(); releasing finished', { roomName: name });\n    const room = this._makeRoom(name, nonce, options);\n    return room;\n  }\n\n  /**\n   * Handles release when no room exists.\n   * @param name The unique identifier of the room.\n   * @param ongoingRelease An ongoing release promise, if any.\n   * @returns A promise that resolves when the release operation is complete.\n   */\n  private async _handleNonExistentRoomRelease(name: string, ongoingRelease?: Promise<void>): Promise<void> {\n    if (ongoingRelease) {\n      this._logger.debug('Rooms.release(); waiting for previous release call', { roomName: name });\n      await ongoingRelease;\n      return;\n    }\n\n    this._logger.debug('Rooms.release(); room does not exist', { roomName: name });\n  }\n\n  /**\n   * Handles release when there's already a release in progress.\n   * @param name The unique identifier of the room.\n   * @param existingRoom The existing room entry in the map.\n   * @param ongoingRelease The promise of an ongoing release operation.\n   */\n  private async _handleConcurrentRelease(\n    name: string,\n    existingRoom: RoomMapEntry,\n    ongoingRelease: Promise<void>,\n  ): Promise<void> {\n    if (existingRoom.abort) {\n      this._logger.debug('Rooms.release(); aborting get call', {\n        roomName: name,\n        existingNonce: existingRoom.nonce,\n      });\n      existingRoom.abort.abort();\n      this._rooms.delete(name);\n    }\n\n    await ongoingRelease;\n  }\n\n  /**\n   * Performs the actual room release operation.\n   * @param name The unique identifier of the room.\n   * @param existingRoom The existing room entry in the map.\n   */\n  private async _performRoomRelease(name: string, existingRoom: RoomMapEntry): Promise<void> {\n    this._rooms.delete(name);\n\n    const releasePromise = this._executeRoomRelease(name, existingRoom);\n    this._releasing.set(name, releasePromise);\n\n    this._logger.debug('Rooms.release(); creating new release promise', {\n      roomName: name,\n      nonce: existingRoom.nonce,\n    });\n\n    await releasePromise;\n  }\n\n  /**\n   * Executes the room release and cleanup.\n   * @param name The unique identifier of the room.\n   * @param existingRoom The existing room entry in the map.\n   */\n  private async _executeRoomRelease(name: string, existingRoom: RoomMapEntry): Promise<void> {\n    const room = await existingRoom.promise;\n    this._logger.debug('Rooms.release(); releasing room', { roomName: name, nonce: existingRoom.nonce });\n    await room.release();\n    this._logger.debug('Rooms.release(); room released', { roomName: name, nonce: existingRoom.nonce });\n    this._releasing.delete(name);\n  }\n\n  /**\n   * makes a new room object\n   * @param name The unique identifier of the room.\n   * @param nonce A random, internal identifier useful for debugging and logging.\n   * @param options The options for the room.\n   * @returns DefaultRoom A new room object.\n   */\n  private _makeRoom(name: string, nonce: string, options: RoomOptions | undefined): DefaultRoom {\n    return new DefaultRoom(\n      name,\n      nonce,\n      normalizeRoomOptions(options, this._isReact),\n      this._realtime,\n      this._chatApi,\n      this._clientIdResolver,\n      this._logger,\n    );\n  }\n\n  /**\n   * Sets react JS mode.\n   */\n  useReact(): void {\n    this._logger.trace('Rooms.useReact();');\n    this._isReact = true;\n  }\n}\n","import * as Ably from 'ably';\n\nimport { DefaultClientIdResolver } from './client-id.js';\nimport { ChatClientOptions, normalizeClientOptions, NormalizedChatClientOptions } from './config.js';\nimport { Connection, DefaultConnection, InternalConnection } from './connection.js';\nimport { randomId } from './id.js';\nimport { Logger, makeLogger } from './logger.js';\nimport { RealtimeWithOptions } from './realtime-extensions.js';\nimport { DefaultRooms, Rooms } from './rooms.js';\nimport { VERSION } from './version.js';\n\n/**\n * This is the core client for Ably chat. It provides access to chat rooms.\n */\nexport class ChatClient {\n  /**\n   * @internal\n   */\n  private readonly _realtime: Ably.Realtime;\n\n  /**\n   * @internal\n   */\n  private readonly _rooms: DefaultRooms;\n\n  /**\n   * @internal\n   */\n  private readonly _clientOptions: NormalizedChatClientOptions;\n\n  /**\n   * @internal\n   */\n  private readonly _connection: InternalConnection;\n\n  /**\n   * @internal\n   */\n  private readonly _logger: Logger;\n\n  /**\n   * @internal\n   */\n  private readonly _nonce: string;\n\n  /**\n   * @internal\n   */\n  private readonly _clientIdResolver: DefaultClientIdResolver;\n\n  /**\n   * Constructor for Chat\n   *\n   * **Important**: The Ably Realtime client must have a clientId set. This can be done by configuring\n   * token-based authentication that returns a token with a clientId, or by setting\n   * the clientId directly in the Realtime client options.\n   * @example\n   * ```typescript\n   * import * as Ably from 'ably';\n   * import { ChatClient } from '@ably/chat';\n   *\n   * // Preferred in production: Use auth URL that returns a token with clientId\n   * const realtime = new Ably.Realtime({\n   *   authUrl: '/api/ably-auth', // Your server endpoint that returns an Ably token with clientId\n   *   authMethod: 'POST'\n   * });\n   * const chatClient = new ChatClient(realtime);\n   *\n   * // Alternative for development and server-side operations: Set clientId directly (requires API key)\n   * const realtime = new Ably.Realtime({\n   *   key: 'your-ably-api-key',\n   *   clientId: 'user-123'\n   * });\n   * const chatClient = new ChatClient(realtime);\n   * ```\n   * @param realtime - The Ably Realtime client.\n   * @param clientOptions - The client options.\n   */\n  constructor(realtime: Ably.Realtime, clientOptions?: ChatClientOptions) {\n    this._realtime = realtime;\n    this._clientOptions = normalizeClientOptions(clientOptions);\n    this._nonce = randomId();\n    this._logger = makeLogger(this._clientOptions).withContext({\n      chatClientNonce: this._nonce,\n    });\n\n    this._connection = new DefaultConnection(realtime, this._logger);\n    this._clientIdResolver = new DefaultClientIdResolver(realtime, this._logger);\n    this._rooms = new DefaultRooms(realtime, this._clientIdResolver, this._logger);\n    this._addAgent('chat-js');\n    this._logger.trace(`ably chat client version ${VERSION}; initialized`);\n  }\n\n  /**\n   * Returns the rooms object, which provides access to chat rooms.\n   * @returns The rooms object.\n   */\n  get rooms(): Rooms {\n    return this._rooms;\n  }\n\n  /**\n   * Returns the underlying connection to Ably, which can be used to monitor the client's\n   * connection to Ably servers.\n   * @returns The connection object.\n   */\n  get connection(): Connection {\n    return this._connection;\n  }\n\n  /**\n   * Returns the clientId of the current client, if known.\n   *\n   * **Important** When using an Ably key for authentication, this value is determined immediately. If using a token,\n   * the clientId is not known until the client has successfully connected to and authenticated with\n   * the server. Use the `chatClient.connection.status` to check the connection status.\n   * @returns The clientId, or undefined if unknown.\n   */\n  get clientId(): string | undefined {\n    return this._realtime.auth.clientId;\n  }\n\n  /**\n   * Returns the underlying Ably Realtime client.\n   * @returns The Ably Realtime client.\n   */\n  get realtime(): Ably.Realtime {\n    return this._realtime;\n  }\n\n  /**\n   * Returns the resolved client options for the client, including any defaults that have been set.\n   * @returns The client options.\n   */\n  get clientOptions(): ChatClientOptions {\n    return this._clientOptions;\n  }\n\n  /**\n   * Returns the logger instance for the client.\n   * @internal\n   * @returns The logger instance.\n   */\n  get logger(): Logger {\n    return this._logger;\n  }\n\n  /**\n   * Adds additional agent information to the client.\n   * Used internally to add React-specific agent information.\n   * @internal\n   */\n  public addReactAgent(): void {\n    this._addAgent('chat-react');\n    this._rooms.useReact();\n  }\n\n  /**\n   * Adds additional agent information to the client.\n   * This is used internally to add a specific agent with a version.\n   * @param agent - The agent to add.\n   * @param version - The version of the agent, defaults to the current client version.\n   * @internal\n   */\n  public addAgentWithVersion(agent: string, version: string): void {\n    this._addAgent(agent, version);\n    this._logger.trace(`Added agent ${agent} with version ${version}`);\n  }\n\n  /**\n   * Disposes of the ChatClient instance, cleaning up any resources and rendering it unusable.\n   * This method will release all rooms before disposing of the client.\n   */\n  async dispose(): Promise<void> {\n    this._logger.trace('ChatClient.dispose();');\n\n    // Release all rooms before disposing\n    await this._rooms.dispose();\n\n    // Dispose of the connection instance\n    this._connection.dispose();\n\n    this._logger.debug('ChatClient.dispose(); client disposed successfully');\n  }\n\n  /**\n   * Sets the agent string for the client.\n   * @param agent - The agent to add.\n   * @param version - The version of the agent, defaults to the current client version.\n   * @internal\n   */\n  private _addAgent(agent: string, version?: string): void {\n    const realtime = this._realtime as RealtimeWithOptions;\n    realtime.options.agents = { ...(realtime.options.agents ?? realtime.options.agents), [agent]: version ?? VERSION };\n  }\n}\n"],"names":["ErrorCode","errorInfoIs","errorInfo","error","DefaultClientIdResolver","_realtime","_logger","clientId","Ably","LogLevel","consoleLogger","message","level","context","contextString","formattedMessage","makeLogger","options","logHandler","DefaultLogger","logLevelNumberMap","handler","levelNumber","originalLevel","value","defaultClientOptions","normalizeClientOptions","on","emitter","arg2","arg3","once","subscribe","subscribePromise","InternalEventEmitter","EventEmitter","wrap","fn","args","emitterHasListeners","destructured","numListeners","ConnectionStatus","DefaultConnection","ably","logger","connectionListener","change","chatState","stateChange","listener","wrapped","status","randomId","has","find","iter","tar","key","dequal","foo","bar","ctor","len","tmp","ChatMessageEventType","RealtimeMessageName","RealtimeMetaEventType","ChatMessageAction","PresenceEventType","TypingEventType","TypingSetEventType","RoomReactionRealtimeEventType","RoomReactionEventType","MessageReactionType","ReactionAnnotationType","AnnotationTypeToReactionType","MessageReactionRawEventType","MessageReactionSummaryEventType","OccupancyEventType","RoomEventType","LARGE_ARRAY_SIZE","HASH_UNDEFINED","MAX_SAFE_INTEGER","argsTag","arrayTag","boolTag","dateTag","errorTag","funcTag","genTag","mapTag","numberTag","objectTag","promiseTag","regexpTag","setTag","stringTag","symbolTag","weakMapTag","arrayBufferTag","dataViewTag","float32Tag","float64Tag","int8Tag","int16Tag","int32Tag","uint8Tag","uint8ClampedTag","uint16Tag","uint32Tag","reRegExpChar","reFlags","reIsHostCtor","reIsUint","cloneableTags","freeGlobal","global","freeSelf","root","freeExports","exports","freeModule","module","moduleExports","addMapEntry","map","pair","addSetEntry","set","arrayEach","array","iteratee","index","length","arrayPush","values","offset","arrayReduce","accumulator","initAccum","baseTimes","result","getValue","object","isHostObject","mapToArray","overArg","func","transform","arg","setToArray","arrayProto","funcProto","objectProto","coreJsData","maskSrcKey","uid","funcToString","hasOwnProperty","objectToString","reIsNative","Buffer","Symbol","Uint8Array","getPrototype","objectCreate","propertyIsEnumerable","splice","nativeGetSymbols","nativeIsBuffer","nativeKeys","DataView","getNative","Map","Promise","Set","WeakMap","nativeCreate","dataViewCtorString","toSource","mapCtorString","promiseCtorString","setCtorString","weakMapCtorString","symbolProto","symbolValueOf","Hash","entries","entry","hashClear","hashDelete","hashGet","data","hashHas","hashSet","ListCache","listCacheClear","listCacheDelete","assocIndexOf","lastIndex","listCacheGet","listCacheHas","listCacheSet","MapCache","mapCacheClear","mapCacheDelete","getMapData","mapCacheGet","mapCacheHas","mapCacheSet","Stack","stackClear","stackDelete","stackGet","stackHas","stackSet","cache","pairs","arrayLikeKeys","inherited","isArray","isArguments","skipIndexes","isIndex","assignValue","objValue","eq","baseAssign","source","copyObject","keys","baseClone","isDeep","isFull","customizer","stack","isObject","isArr","initCloneArray","copyArray","tag","getTag","isFunc","isBuffer","cloneBuffer","initCloneObject","copySymbols","initCloneByTag","stacked","props","getAllKeys","subValue","baseCreate","proto","baseGetAllKeys","keysFunc","symbolsFunc","baseGetTag","baseIsNative","isMasked","pattern","isFunction","baseKeys","isPrototype","buffer","cloneArrayBuffer","arrayBuffer","cloneDataView","dataView","cloneMap","cloneFunc","cloneRegExp","regexp","cloneSet","cloneSymbol","symbol","cloneTypedArray","typedArray","newValue","getSymbols","isKeyable","stubArray","Ctor","ctorString","type","cloneDeep","other","isArrayLikeObject","isArrayLike","isLength","isObjectLike","stubFalse","DefaultMessage","serial","text","metadata","headers","action","version","timestamp","reactions","event","newReactions","replace","params","emptyMessageReactions","parseMessage","inboundMessage","extras","assertValidSerial","op","paramName","eventTypeMap","DefaultMessageReactions","_options","_api","_roomName","_channel","reactionType","eventType","name","reactionEvent","summary","unique","distinct","multiple","messageSerial","count","apiParams","roomOptions","messageFromRest","reactionTypeData","reactionName","reaction","multipleReactions","MessageActionsToEventsMap","OrderBy","DefaultMessages","roomName","channel","chatApi","messageEventsListener","channelAttachedListener","channelUpdateListener","subscriptionPoint","subscriptionPointParams","fromResume","newSubscriptionStartResolver","channelWithProperties","resolve","reject","cleanup","offAttachedListener","response","details","updateParams","resolvedSubscriptionStart","channelEventMessage","disposalError","rejectFn","ChatApi","realtime","payload","nextData","firstData","currentData","restMessage","body","url","method","suffix","roomChannelName","VERSION","CHANNEL_OPTIONS_AGENT_STRING","CHANNEL_OPTIONS_AGENT_STRING_REACT","DEFAULT_CHANNEL_MODES","DEFAULT_CHANNEL_OPTIONS","DEFAULT_CHANNEL_OPTIONS_REACT","ChannelManager","isReact","merger","baseOptions","parseOccupancyMessage","connections","presenceMembers","metrics","DefaultOccupancy","occupancyEventsListener","DefaultPresence","member","presenceEventsListener","channelDetachListener","user","present","previous","E_CANCELED","__awaiter$2","thisArg","_arguments","P","generator","adopt","fulfilled","step","e","rejected","Semaphore","_value","_cancelError","weight","priority","task","i","findIndexFromEnd","callback_1","callback","release","insertSorted","item","previousValue","called","waiters","waiter","queuedPriority","a","v","predicate","__awaiter$1","Mutex","cancelError","releaser","RoomStatus","DefaultRoomLifecycle","RoomLifecycleManager","channelManager","roomLifecycle","channelStateListener","discontinuityOnAttachedListener","discontinuityOnUpdateListener","errInfo","attachError","newStatus","detachError","channelState","firstAttach","DefaultRoomOptions","invalidRoomConfiguration","reason","validateRoomOptions","validateTypingOptions","normalizeTypingOptions","normalizeOccupancyOptions","normalizePresenceOptions","normalizeMessagesOptions","normalizeRoomOptions","react","ephemeralMessage","messageToEphemeral","DefaultRoomReaction","createdAt","isSelf","parseRoomReaction","reactionCreatedMessage","messageClientId","DefaultRoomReactions","connection","clientIdResolver","inbound","roomReactionEventsListener","realtimeMessage","DefaultTyping","timeoutId","timer","existingTimeout","DefaultRoom","nonce","finalized","manager","DefaultRooms","existingRoom","ongoingRelease","roomNames","releasePromises","inFlight","all","room","abortController","roomPromise","abortPromise","_","abortListener","releasePromise","ChatClient","clientOptions","agent"],"mappings":"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","x_google_ignoreList":[8,10,24]}