{"version":3,"sources":["../src/room.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { Mutex } from '@livekit/mutex';\nimport { EncryptionState, type EncryptionType } from '@livekit/rtc-ffi-bindings';\nimport type { FfiEvent } from '@livekit/rtc-ffi-bindings';\nimport type { DisconnectReason, OwnedParticipant } from '@livekit/rtc-ffi-bindings';\nimport type { DataStream_Trailer, DisconnectCallback } from '@livekit/rtc-ffi-bindings';\nimport {\n  type ConnectCallback,\n  ConnectRequest,\n  type ConnectResponse,\n  type ConnectionQuality,\n  ConnectionState,\n  ContinualGatheringPolicy,\n  type DataPacketKind,\n  type DataStream_Chunk,\n  type DataStream_Header,\n  type DisconnectResponse,\n  RoomOptions as FfiRoomOptions,\n  type IceServer,\n  IceTransportType,\n  type RoomInfo,\n} from '@livekit/rtc-ffi-bindings';\nimport { TrackKind } from '@livekit/rtc-ffi-bindings';\nimport type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';\nimport EventEmitter from 'events';\nimport { ByteStreamReader, TextStreamReader } from './data_streams/stream_reader.js';\nimport type {\n  ByteStreamHandler,\n  ByteStreamInfo,\n  StreamController,\n  TextStreamHandler,\n  TextStreamInfo,\n} from './data_streams/types.js';\nimport type { E2EEOptions } from './e2ee.js';\nimport { E2EEManager, defaultE2EEOptions } from './e2ee.js';\nimport { FfiClient, FfiClientEvent, FfiHandle } from './ffi_client.js';\nimport { log } from './log.js';\nimport type { Participant } from './participant.js';\nimport { LocalParticipant, RemoteParticipant } from './participant.js';\nimport type { LocalTrack, RemoteTrack } from './track.js';\nimport { RemoteAudioTrack, RemoteVideoTrack } from './track.js';\nimport type { LocalTrackPublication, TrackPublication } from './track_publication.js';\nimport { RemoteTrackPublication } from './track_publication.js';\nimport type { ChatMessage } from './types.js';\nimport { bigIntToNumber } from './utils.js';\n\nexport interface RtcConfiguration {\n  iceTransportType: IceTransportType;\n  continualGatheringPolicy: ContinualGatheringPolicy;\n  iceServers: IceServer[];\n}\n\nexport const defaultRtcConfiguration: RtcConfiguration = {\n  iceTransportType: IceTransportType.TRANSPORT_ALL,\n  continualGatheringPolicy: ContinualGatheringPolicy.GATHER_CONTINUALLY,\n  iceServers: [],\n};\n\nexport interface RoomOptions {\n  autoSubscribe: boolean;\n  dynacast: boolean;\n  /**\n   * @deprecated Use `encryption` instead. See x for details\n   */\n  e2ee?: E2EEOptions;\n  encryption?: E2EEOptions;\n  rtcConfig?: RtcConfiguration;\n}\n\nexport const defaultRoomOptions = new FfiRoomOptions({\n  autoSubscribe: true,\n  dynacast: false,\n  e2ee: undefined,\n  encryption: undefined,\n  rtcConfig: undefined,\n  adaptiveStream: false,\n  joinRetries: 1,\n});\n\nexport class Room extends (EventEmitter as new () => TypedEmitter<RoomCallbacks>) {\n  private info?: RoomInfo;\n  private ffiHandle?: FfiHandle;\n  /**\n   * used to ensure events are processed sequentially and allow for\n   * the local participant to acquire the lock while doing state updates related to FFI events\n   * before processing the next events\n   */\n  private ffiEventLock = new Mutex();\n\n  private byteStreamControllers = new Map<string, StreamController<DataStream_Chunk>>();\n  private textStreamControllers = new Map<string, StreamController<DataStream_Chunk>>();\n  private byteStreamHandlers = new Map<string, ByteStreamHandler>();\n  private textStreamHandlers = new Map<string, TextStreamHandler>();\n\n  private preConnectEvents: FfiEvent[] = [];\n\n  private _token?: string;\n  private _serverUrl?: string;\n\n  e2eeManager?: E2EEManager;\n  connectionState: ConnectionState = ConnectionState.CONN_DISCONNECTED;\n\n  remoteParticipants: Map<string, RemoteParticipant> = new Map();\n  localParticipant?: LocalParticipant;\n\n  constructor() {\n    super();\n  }\n\n  get name(): string | undefined {\n    return this.info?.name;\n  }\n\n  get metadata(): string | undefined {\n    return this.info?.metadata;\n  }\n\n  get isConnected(): boolean {\n    return this.ffiHandle != undefined && this.connectionState != ConnectionState.CONN_DISCONNECTED;\n  }\n\n  /** @internal */\n  get token(): string | undefined {\n    return this._token;\n  }\n\n  /** @internal */\n  get serverUrl(): string | undefined {\n    return this._serverUrl;\n  }\n\n  /**\n   * Gets the room's server ID. This ID is assigned by the LiveKit server\n   * and is unique for each room session.\n   * SID is assigned asynchronously after connection.\n   * @returns Promise that resolves to the room's server ID, or empty string if not connected\n   */\n  async getSid(): Promise<string> {\n    if (!this.isConnected) {\n      return '';\n    }\n    if (this.info?.sid && this.info.sid !== '') {\n      return this.info.sid;\n    }\n    return new Promise((resolve, reject) => {\n      const handleRoomUpdate = (sid: string) => {\n        if (sid !== '') {\n          this.off(RoomEvent.RoomSidChanged, handleRoomUpdate);\n          resolve(sid);\n        }\n      };\n      this.on(RoomEvent.RoomSidChanged, handleRoomUpdate);\n      this.once(RoomEvent.Disconnected, () => {\n        this.off(RoomEvent.RoomSidChanged, handleRoomUpdate);\n        reject('Room disconnected before room server id was available');\n      });\n    });\n  }\n\n  get numParticipants(): number {\n    return this.info?.numParticipants ?? 0;\n  }\n\n  get numPublishers(): number {\n    return this.info?.numPublishers ?? 0;\n  }\n\n  get creationTime(): Date {\n    // TODO: workaround for Rust SDK bug, remove after updating to:\n    //   https://github.com/livekit/rust-sdks/pull/822\n    // check if creationTime looks like seconds (less than year 3000 in ms), convert to ms if needed\n    let creationTimeMs = Number(this.info?.creationTime ?? 0);\n    if (creationTimeMs > 0 && creationTimeMs < 1e12) {\n      creationTimeMs *= 1000;\n    }\n    return new Date(creationTimeMs);\n  }\n\n  get isRecording(): boolean {\n    return this.info?.activeRecording ?? false;\n  }\n\n  /**\n   * The time in seconds after which a room will be closed after the last\n   * participant has disconnected.\n   */\n  get departureTimeout(): number {\n    return this.info?.departureTimeout ?? 0;\n  }\n\n  /**\n   * The time in seconds after which an empty room will be automatically closed.\n   */\n  get emptyTimeout(): number {\n    return this.info?.emptyTimeout ?? 0;\n  }\n\n  /**\n   * Connects to a LiveKit room using the provided URL and access token.\n   * @param url - The WebSocket URL of the LiveKit server\n   * @param token - A valid LiveKit access token for authentication\n   * @param opts - Optional room configuration options\n   * @throws ConnectError - if connection fails\n   */\n  async connect(url: string, token: string, opts?: RoomOptions) {\n    const options = { ...defaultRoomOptions, ...opts };\n    const e2eeEnabled = options.encryption || options.e2ee;\n    const e2eeOptions = options.encryption\n      ? { ...defaultE2EEOptions, ...options.encryption }\n      : { ...defaultE2EEOptions, ...options.e2ee };\n\n    const req = new ConnectRequest({\n      url: url,\n      token: token,\n      options,\n    });\n\n    FfiClient.instance.on(FfiClientEvent.FfiEvent, this.onFfiEvent);\n\n    const res = FfiClient.instance.request<ConnectResponse>({\n      message: {\n        case: 'connect',\n        value: req,\n      },\n    });\n\n    const cb = await FfiClient.instance.waitFor<ConnectCallback>((ev: FfiEvent) => {\n      return ev.message.case == 'connect' && ev.message.value.asyncId == res.asyncId;\n    });\n\n    log.debug('Connect callback received');\n\n    switch (cb.message.case) {\n      case 'result':\n        this.ffiHandle = new FfiHandle(cb.message.value.room!.handle!.id!);\n        this.e2eeManager = e2eeEnabled && new E2EEManager(this.ffiHandle.handle, e2eeOptions);\n\n        this._token = token;\n        this._serverUrl = url;\n        this.info = cb.message.value.room!.info;\n        this.connectionState = ConnectionState.CONN_CONNECTED;\n        this.localParticipant = new LocalParticipant(\n          cb.message.value.localParticipant!,\n          this.ffiEventLock,\n        );\n\n        for (const pt of cb.message.value.participants) {\n          const rp = this.createRemoteParticipant(pt.participant!);\n\n          for (const pub of pt.publications) {\n            const publication = new RemoteTrackPublication(pub);\n            rp.trackPublications.set(publication.sid!, publication);\n          }\n        }\n        break;\n      case 'error':\n      default:\n        throw new ConnectError(cb.message.value || '');\n    }\n  }\n\n  /**\n   * Disconnects from the room and cleans up all resources.\n   * This will stop all tracks and close the connection.\n   */\n  async disconnect() {\n    if (!this.isConnected) {\n      return;\n    }\n\n    const res = FfiClient.instance.request<DisconnectResponse>({\n      message: {\n        case: 'disconnect',\n        value: {\n          roomHandle: this.ffiHandle?.handle,\n        },\n      },\n    });\n\n    await FfiClient.instance.waitFor<DisconnectCallback>((ev: FfiEvent) => {\n      return ev.message.case == 'disconnect' && ev.message.value.asyncId == res.asyncId;\n    });\n\n    FfiClient.instance.removeListener(FfiClientEvent.FfiEvent, this.onFfiEvent);\n    this.removeAllListeners();\n  }\n\n  /**\n   * Registers a handler for incoming text data streams on a specific topic.\n   * Text streams are used for receiving structured text data from other participants.\n   * @param topic - The topic to listen for text streams on\n   * @param callback - Function to handle incoming text stream data\n   * @throws Error - if a handler for this topic is already registered\n   */\n  registerTextStreamHandler(topic: string, callback: TextStreamHandler) {\n    if (this.textStreamHandlers.has(topic)) {\n      throw new Error(`A text stream handler for topic \"${topic}\" has already been set.`);\n    }\n    this.textStreamHandlers.set(topic, callback);\n  }\n\n  unregisterTextStreamHandler(topic: string) {\n    this.textStreamHandlers.delete(topic);\n  }\n\n  /**\n   * Registers a handler for incoming byte data streams on a specific topic.\n   * Byte streams are used for receiving binary data like files from other participants.\n   * @param topic - The topic to listen for byte streams on\n   * @param callback - Function to handle incoming byte stream data\n   * @throws Error - if a handler for this topic is already registered\n   */\n  registerByteStreamHandler(topic: string, callback: ByteStreamHandler) {\n    if (this.byteStreamHandlers.has(topic)) {\n      throw new Error(`A byte stream handler for topic \"${topic}\" has already been set.`);\n    }\n    this.byteStreamHandlers.set(topic, callback);\n  }\n\n  unregisterByteStreamHandler(topic: string) {\n    this.byteStreamHandlers.delete(topic);\n  }\n\n  private onFfiEvent = async (ffiEvent: FfiEvent) => {\n    const unlock = await this.ffiEventLock.lock();\n    try {\n      if (!this.localParticipant || !this.ffiHandle || !this.info) {\n        this.preConnectEvents.push(ffiEvent);\n        return;\n      }\n\n      // process preConnectEvents if we received the connectCallback after the events were queued\n      for (const ev of this.preConnectEvents) {\n        await this.processFfiEvent(ev);\n      }\n      this.preConnectEvents = [];\n\n      await this.processFfiEvent(ffiEvent);\n    } finally {\n      unlock();\n    }\n  };\n\n  private processFfiEvent = async (ffiEvent: FfiEvent) => {\n    if (!this.localParticipant || !this.ffiHandle || !this.info) {\n      throw new Error('processFfiEvent called before connect');\n    }\n\n    if (ffiEvent.message.case == 'rpcMethodInvocation') {\n      if (\n        ffiEvent.message.value.localParticipantHandle == this.localParticipant.ffi_handle.handle\n      ) {\n        this.localParticipant.handleRpcMethodInvocation(\n          ffiEvent.message.value.invocationId!,\n          ffiEvent.message.value.method!,\n          ffiEvent.message.value.requestId!,\n          ffiEvent.message.value.callerIdentity!,\n          ffiEvent.message.value.payload!,\n          ffiEvent.message.value.responseTimeoutMs!,\n        );\n      }\n      return;\n    } else if (\n      ffiEvent.message.case != 'roomEvent' ||\n      ffiEvent.message.value.roomHandle != this.ffiHandle.handle\n    ) {\n      return;\n    }\n\n    const ev = ffiEvent.message.value.message;\n    if (process.env.LIVEKIT_DEBUG_LOG_ROOM_EVENTS) {\n      console.log('Room event:', ev);\n    }\n    if (ev.case == 'participantConnected') {\n      const participant = this.createRemoteParticipant(ev.value.info!);\n      this.remoteParticipants.set(participant.identity!, participant);\n      this.emit(RoomEvent.ParticipantConnected, participant);\n    } else if (ev.case == 'participantDisconnected') {\n      const participant = this.remoteParticipants.get(ev.value.participantIdentity!);\n      if (participant) {\n        this.remoteParticipants.delete(participant.identity);\n        participant.info.disconnectReason = ev.value.disconnectReason;\n        this.emit(RoomEvent.ParticipantDisconnected, participant);\n      } else {\n        log.warn(`RoomEvent.ParticipantDisconnected: Could not find participant`);\n      }\n    } else if (ev.case == 'localTrackPublished') {\n      const publication = this.localParticipant.trackPublications.get(ev.value.trackSid!);\n      this.emit(RoomEvent.LocalTrackPublished, publication!, this.localParticipant);\n    } else if (ev.case == 'localTrackUnpublished') {\n      const publication = this.localParticipant.trackPublications.get(ev.value.publicationSid!);\n      this.localParticipant.trackPublications.delete(ev.value.publicationSid!);\n      this.emit(RoomEvent.LocalTrackUnpublished, publication!, this.localParticipant!);\n    } else if (ev.case == 'localTrackSubscribed') {\n      const publication = this.localParticipant.trackPublications.get(ev.value.trackSid!);\n      if (publication) {\n        publication.resolveFirstSubscription();\n        this.emit(RoomEvent.LocalTrackSubscribed, publication!.track!);\n      } else {\n        log.warn(`RoomEvent.LocalTrackSubscribed: Publication not found: ${ev.value.trackSid}`);\n      }\n    } else if (ev.case == 'trackPublished') {\n      const participant = this.remoteParticipants.get(ev.value.participantIdentity!);\n      const publication = new RemoteTrackPublication(ev.value.publication!);\n      if (participant) {\n        participant.trackPublications.set(publication.sid!, publication);\n      } else {\n        log.warn(\n          `RoomEvent.TrackPublished: Could not find participant: ${ev.value.participantIdentity}`,\n        );\n      }\n      this.emit(RoomEvent.TrackPublished, publication, participant!);\n    } else if (ev.case == 'trackUnpublished') {\n      const participant = this.requireRemoteParticipant(ev.value.participantIdentity!);\n      const publication = participant.trackPublications.get(ev.value.publicationSid!);\n      participant.trackPublications.delete(ev.value.publicationSid!);\n      if (publication) {\n        this.emit(RoomEvent.TrackUnpublished, publication, participant);\n      } else {\n        log.warn(`RoomEvent.TrackUnpublished: Could not find publication`);\n      }\n    } else if (ev.case == 'trackSubscribed') {\n      const ownedTrack = ev.value.track!;\n      const trackInfo = ownedTrack.info!;\n      try {\n        const { participant, publication } = this.requirePublicationOfRemoteParticipant(\n          ev.value.participantIdentity!,\n          trackInfo.sid!,\n        );\n        publication.subscribed = true;\n        if (trackInfo.kind == TrackKind.KIND_VIDEO) {\n          publication.track = new RemoteVideoTrack(ownedTrack);\n        } else if (trackInfo.kind == TrackKind.KIND_AUDIO) {\n          publication.track = new RemoteAudioTrack(ownedTrack);\n        }\n\n        this.emit(RoomEvent.TrackSubscribed, publication.track!, publication, participant);\n      } catch (e: unknown) {\n        log.warn(`RoomEvent.TrackSubscribed: ${(e as Error).message}`);\n      }\n    } else if (ev.case == 'trackUnsubscribed') {\n      try {\n        const { participant, publication } = this.requirePublicationOfRemoteParticipant(\n          ev.value.participantIdentity!,\n          ev.value.trackSid!,\n        );\n        const track = publication.track!;\n        publication.track = undefined;\n        publication.subscribed = false;\n        this.emit(RoomEvent.TrackUnsubscribed, track, publication, participant);\n      } catch (e: unknown) {\n        log.warn(`RoomEvent.TrackUnsubscribed: ${(e as Error).message}`);\n      }\n    } else if (ev.case == 'trackSubscriptionFailed') {\n      try {\n        const participant = this.requireRemoteParticipant(ev.value.participantIdentity!);\n        this.emit(\n          RoomEvent.TrackSubscriptionFailed,\n          ev.value.trackSid!,\n          participant,\n          ev.value.error,\n        );\n      } catch (e: unknown) {\n        log.warn(`RoomEvent.TrackSubscriptionFailed: ${(e as Error).message}`);\n      }\n    } else if (ev.case == 'trackMuted') {\n      try {\n        const { participant, publication } = this.requirePublicationOfParticipant(\n          ev.value.participantIdentity!,\n          ev.value.trackSid!,\n        );\n        publication.info!.muted = true;\n        if (publication.track) {\n          publication.track.info!.muted = true;\n        }\n        this.emit(RoomEvent.TrackMuted, publication, participant);\n      } catch (e: unknown) {\n        log.warn(`RoomEvent.TrackMuted: ${(e as Error).message}`);\n      }\n    } else if (ev.case == 'trackUnmuted') {\n      try {\n        const { participant, publication } = this.requirePublicationOfParticipant(\n          ev.value.participantIdentity!,\n          ev.value.trackSid!,\n        );\n        publication.info!.muted = false;\n        if (publication.track) {\n          publication.track.info!.muted = false;\n        }\n        this.emit(RoomEvent.TrackUnmuted, publication, participant);\n      } catch (e: unknown) {\n        log.warn(`RoomEvent.TrackUnmuted: ${(e as Error).message}`);\n      }\n    } else if (ev.case == 'activeSpeakersChanged') {\n      try {\n        const activeSpeakers = ev.value.participantIdentities.map((identity) =>\n          this.requireParticipantByIdentity(identity),\n        );\n        this.emit(RoomEvent.ActiveSpeakersChanged, activeSpeakers);\n      } catch (e: unknown) {\n        log.warn(`RoomEvent.ActiveSpeakersChanged: ${(e as Error).message}`);\n      }\n    } else if (ev.case == 'roomMetadataChanged') {\n      this.info.metadata = ev.value.metadata ?? '';\n      this.emit(RoomEvent.RoomMetadataChanged, this.info.metadata);\n    } else if (ev.case == 'participantMetadataChanged') {\n      try {\n        const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);\n        participant.info.metadata = ev.value.metadata;\n        this.emit(RoomEvent.ParticipantMetadataChanged, participant.metadata, participant);\n      } catch (e: unknown) {\n        log.warn(`RoomEvent.ParticipantMetadataChanged: ${(e as Error).message}`);\n      }\n    } else if (ev.case == 'participantNameChanged') {\n      try {\n        const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);\n        participant.info.name = ev.value.name;\n        this.emit(RoomEvent.ParticipantNameChanged, participant.name!, participant);\n      } catch (e: unknown) {\n        log.warn(`RoomEvent.ParticipantNameChanged: ${(e as Error).message}`);\n      }\n    } else if (ev.case == 'participantAttributesChanged') {\n      try {\n        const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);\n        participant.info.attributes = ev.value.attributes.reduce(\n          (acc, value) => {\n            acc[value.key!] = value.value!;\n            return acc;\n          },\n          {} as Record<string, string>,\n        );\n        if (Object.keys(ev.value.changedAttributes).length > 0) {\n          const changedAttributes = ev.value.changedAttributes.reduce(\n            (acc, value) => {\n              acc[value.key!] = value.value!;\n              return acc;\n            },\n            {} as Record<string, string>,\n          );\n          this.emit(RoomEvent.ParticipantAttributesChanged, changedAttributes, participant);\n        }\n      } catch (e: unknown) {\n        log.warn(`RoomEvent.ParticipantAttributesChanged: ${(e as Error).message}`);\n      }\n    } else if (ev.case == 'connectionQualityChanged') {\n      try {\n        const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);\n        this.emit(RoomEvent.ConnectionQualityChanged, ev.value.quality!, participant);\n      } catch (e: unknown) {\n        log.warn(`RoomEvent.ConnectionQualityChanged: ${(e as Error).message}`);\n      }\n    } else if (ev.case == 'chatMessage') {\n      const participant = this.retrieveParticipantByIdentity(ev.value.participantIdentity!);\n      const { id, message: messageText, timestamp, editTimestamp, generated } = ev.value.message!;\n      const message: ChatMessage = {\n        id: id!,\n        message: messageText!,\n        timestamp: Number(timestamp),\n        editTimestamp: Number(editTimestamp),\n        generated,\n      };\n      this.emit(RoomEvent.ChatMessage, message, participant);\n    } else if (ev.case == 'dataPacketReceived') {\n      // Can be undefined if the data is sent from a Server SDK\n      const participant = this.remoteParticipants.get(ev.value.participantIdentity!);\n      const dataPacket = ev.value.value;\n      switch (dataPacket.case) {\n        case 'user':\n          const buffer = FfiClient.instance.copyBuffer(\n            dataPacket.value.data!.data!.dataPtr!,\n            Number(dataPacket.value.data!.data!.dataLen),\n          );\n          new FfiHandle(dataPacket.value.data!.handle!.id!).dispose();\n          this.emit(\n            RoomEvent.DataReceived,\n            buffer,\n            participant,\n            ev.value.kind,\n            dataPacket.value.topic,\n          );\n          break;\n        case 'sipDtmf':\n          const { code, digit } = dataPacket.value;\n          this.emit(RoomEvent.DtmfReceived, code!, digit!, participant!);\n          break;\n        default:\n          break;\n      }\n    } else if (ev.case == 'e2eeStateChanged') {\n      if (ev.value.state == EncryptionState.INTERNAL_ERROR) {\n        // throw generic error until Rust SDK is updated to supply the error alongside INTERNAL_ERROR\n        this.emit(RoomEvent.EncryptionError, new Error('internal server error'));\n      }\n    } else if (ev.case == 'connectionStateChanged') {\n      this.connectionState = ev.value.state!;\n      this.emit(RoomEvent.ConnectionStateChanged, this.connectionState);\n      /*} else if (ev.case == 'connected') {\n      this.emit(RoomEvent.Connected);*/\n    } else if (ev.case == 'disconnected') {\n      this.emit(RoomEvent.Disconnected, ev.value.reason!);\n    } else if (ev.case == 'reconnecting') {\n      this.emit(RoomEvent.Reconnecting);\n    } else if (ev.case == 'reconnected') {\n      this.emit(RoomEvent.Reconnected);\n    } else if (ev.case == 'roomSidChanged') {\n      this.emit(RoomEvent.RoomSidChanged, ev.value.sid!);\n    } else if (ev.case === 'streamHeaderReceived' && ev.value.header) {\n      this.handleStreamHeader(ev.value.header, ev.value.participantIdentity!);\n    } else if (ev.case === 'streamChunkReceived' && ev.value.chunk) {\n      this.handleStreamChunk(ev.value.chunk);\n    } else if (ev.case === 'streamTrailerReceived' && ev.value.trailer) {\n      this.handleStreamTrailer(ev.value.trailer);\n    } else if (ev.case === 'roomUpdated') {\n      this.info = ev.value;\n      this.emit(RoomEvent.RoomUpdated);\n    } else if (ev.case === 'moved') {\n      this.info = ev.value;\n      this.emit(RoomEvent.Moved);\n    } else if (ev.case === 'participantsUpdated') {\n      for (const info of ev.value.participants) {\n        const participant = this.retrieveParticipantByIdentity(info.identity!);\n        if (participant) {\n          participant.info = info;\n        }\n      }\n    } else if (ev.case === 'participantEncryptionStatusChanged') {\n      try {\n        const participant = this.requireParticipantByIdentity(ev.value.participantIdentity!);\n        this.emit(\n          RoomEvent.ParticipantEncryptionStatusChanged,\n          !!ev.value.isEncrypted,\n          participant,\n        );\n      } catch (e: unknown) {\n        log.warn(`RoomEvent.ParticipantEncryptionStatusChanged: ${(e as Error).message}`);\n      }\n    } else if (ev.case === 'tokenRefreshed') {\n      this._token = ev.value.token;\n      this.emit('tokenRefreshed');\n    }\n  };\n\n  private retrieveParticipantByIdentity(identity: string): Participant | undefined {\n    if (this.localParticipant?.identity === identity) {\n      return this.localParticipant;\n    } else {\n      return this.remoteParticipants.get(identity);\n    }\n  }\n\n  private requireParticipantByIdentity(identity: string): Participant {\n    if (this.localParticipant?.identity === identity) {\n      return this.localParticipant;\n    } else if (this.remoteParticipants.has(identity)) {\n      return this.remoteParticipants.get(identity)!;\n    } else {\n      throw new TypeError(`participant ${identity} not found`);\n    }\n  }\n\n  private requireRemoteParticipant(identity: string) {\n    const participant = this.remoteParticipants.get(identity);\n    if (!participant) {\n      throw new TypeError(`participant ${identity} not found`);\n    }\n    return participant;\n  }\n\n  private requirePublicationOfParticipant(identity: string, trackSid: string) {\n    const participant = this.requireParticipantByIdentity(identity);\n    const publication = participant.trackPublications.get(trackSid);\n    if (!publication) {\n      throw new TypeError(`publication ${trackSid} not found`);\n    }\n    return { participant, publication };\n  }\n\n  private requirePublicationOfRemoteParticipant(identity: string, trackSid: string) {\n    const participant = this.requireRemoteParticipant(identity);\n    const publication = participant.trackPublications.get(trackSid);\n    if (!publication) {\n      throw new TypeError(`publication ${trackSid} not found`);\n    }\n    return { participant, publication };\n  }\n\n  private createRemoteParticipant(ownedInfo: OwnedParticipant) {\n    if (this.remoteParticipants.has(ownedInfo.info!.identity!)) {\n      throw new Error('Participant already exists');\n    }\n\n    const participant = new RemoteParticipant(ownedInfo);\n    this.remoteParticipants.set(ownedInfo.info!.identity!, participant);\n    return participant;\n  }\n\n  private handleStreamHeader(streamHeader: DataStream_Header, participantIdentity: string) {\n    if (streamHeader.contentHeader.case === 'byteHeader') {\n      const streamHandlerCallback = this.byteStreamHandlers.get(streamHeader.topic ?? '');\n\n      if (!streamHandlerCallback) {\n        log.debug(\n          'ignoring incoming byte stream due to no handler for topic: %s',\n          streamHeader.topic ?? 'undefined',\n        );\n        return;\n      }\n      let streamController: ReadableStreamDefaultController<DataStream_Chunk>;\n      const stream = new ReadableStream({\n        start: (controller) => {\n          streamController = controller;\n          this.byteStreamControllers.set(streamHeader.streamId!, {\n            header: streamHeader,\n            controller: streamController,\n            startTime: Date.now(),\n          });\n        },\n      });\n      const info: ByteStreamInfo = {\n        streamId: streamHeader.streamId!,\n        name: streamHeader.contentHeader.value.name ?? 'unknown',\n        mimeType: streamHeader.mimeType!,\n        totalSize: streamHeader.totalLength ? Number(streamHeader.totalLength) : undefined,\n        topic: streamHeader.topic!,\n        timestamp: bigIntToNumber(streamHeader.timestamp!),\n        attributes: streamHeader.attributes,\n      };\n      streamHandlerCallback(\n        new ByteStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength)),\n        { identity: participantIdentity },\n      );\n    } else if (streamHeader.contentHeader.case === 'textHeader') {\n      const streamHandlerCallback = this.textStreamHandlers.get(streamHeader.topic ?? '');\n\n      if (!streamHandlerCallback) {\n        log.debug(\n          'ignoring incoming text stream due to no handler for topic: %s',\n          streamHeader.topic ?? 'undefined',\n        );\n        return;\n      }\n      let streamController: ReadableStreamDefaultController<DataStream_Chunk>;\n      const stream = new ReadableStream<DataStream_Chunk>({\n        start: (controller) => {\n          streamController = controller;\n          this.textStreamControllers.set(streamHeader.streamId!, {\n            header: streamHeader,\n            controller: streamController,\n            startTime: Date.now(),\n          });\n        },\n      });\n      const info: TextStreamInfo = {\n        streamId: streamHeader.streamId!,\n        mimeType: streamHeader.mimeType!,\n        totalSize: streamHeader.totalLength ? Number(streamHeader.totalLength) : undefined,\n        topic: streamHeader.topic!,\n        timestamp: Number(streamHeader.timestamp),\n        attributes: streamHeader.attributes,\n      };\n      streamHandlerCallback(\n        new TextStreamReader(info, stream, bigIntToNumber(streamHeader.totalLength)),\n        { identity: participantIdentity },\n      );\n    }\n  }\n\n  private handleStreamChunk(chunk: DataStream_Chunk) {\n    const fileBuffer = this.byteStreamControllers.get(chunk.streamId!);\n    if (fileBuffer) {\n      if (chunk.content!.length > 0) {\n        fileBuffer.controller.enqueue(chunk);\n      }\n    }\n    const textBuffer = this.textStreamControllers.get(chunk.streamId!);\n    if (textBuffer) {\n      if (chunk.content!.length > 0) {\n        textBuffer.controller.enqueue(chunk);\n      }\n    }\n  }\n\n  private handleStreamTrailer(trailer: DataStream_Trailer) {\n    const streamId = trailer.streamId!;\n    const fileBuffer = this.byteStreamControllers.get(streamId);\n    if (fileBuffer) {\n      fileBuffer.controller.close();\n      this.byteStreamControllers.delete(streamId);\n    }\n    const textBuffer = this.textStreamControllers.get(streamId);\n    if (textBuffer) {\n      textBuffer.controller.close();\n      this.textStreamControllers.delete(streamId);\n    }\n  }\n}\n\nexport class ConnectError extends Error {\n  constructor(message: string) {\n    super(message);\n  }\n}\n\nexport type RoomCallbacks = {\n  participantConnected: (participant: RemoteParticipant) => void;\n  participantDisconnected: (participant: RemoteParticipant) => void;\n  localTrackPublished: (publication: LocalTrackPublication, participant: LocalParticipant) => void;\n  localTrackUnpublished: (\n    publication: LocalTrackPublication,\n    participant: LocalParticipant,\n  ) => void;\n  localTrackSubscribed: (track: LocalTrack) => void;\n  trackPublished: (publication: RemoteTrackPublication, participant: RemoteParticipant) => void;\n  trackUnpublished: (publication: RemoteTrackPublication, participant: RemoteParticipant) => void;\n  trackSubscribed: (\n    track: RemoteTrack,\n    publication: RemoteTrackPublication,\n    participant: RemoteParticipant,\n  ) => void;\n  trackUnsubscribed: (\n    track: RemoteTrack,\n    publication: RemoteTrackPublication,\n    participant: RemoteParticipant,\n  ) => void;\n  trackSubscriptionFailed: (\n    trackSid: string,\n    participant: RemoteParticipant,\n    reason?: string,\n  ) => void;\n  trackMuted: (publication: TrackPublication, participant: Participant) => void;\n  trackUnmuted: (publication: TrackPublication, participant: Participant) => void;\n  activeSpeakersChanged: (speakers: Participant[]) => void;\n  roomMetadataChanged: (metadata: string) => void;\n  roomInfoUpdated: (info: RoomInfo) => void;\n  participantMetadataChanged: (metadata: string | undefined, participant: Participant) => void;\n  participantNameChanged: (name: string, participant: Participant) => void;\n  participantAttributesChanged: (\n    changedAttributes: Record<string, string>,\n    participant: Participant,\n  ) => void;\n  participantEncryptionStatusChanged: (isEncrypted: boolean, participant: Participant) => void;\n  connectionQualityChanged: (quality: ConnectionQuality, participant: Participant) => void;\n  dataReceived: (\n    payload: Uint8Array,\n    participant?: RemoteParticipant,\n    kind?: DataPacketKind,\n    topic?: string,\n    encryptionType?: EncryptionType,\n  ) => void;\n  chatMessage: (message: ChatMessage, participant?: Participant) => void;\n  dtmfReceived: (code: number, digit: string, participant: RemoteParticipant) => void;\n  encryptionError: (error: Error) => void;\n  connectionStateChanged: (state: ConnectionState) => void;\n  connected: () => void;\n  disconnected: (reason: DisconnectReason) => void;\n  reconnecting: () => void;\n  reconnected: () => void;\n  roomSidChanged: (sid: string) => void;\n  roomUpdated: () => void;\n  moved: () => void;\n  tokenRefreshed: () => void;\n};\n\nexport enum RoomEvent {\n  ParticipantConnected = 'participantConnected',\n  ParticipantDisconnected = 'participantDisconnected',\n  LocalTrackPublished = 'localTrackPublished',\n  LocalTrackUnpublished = 'localTrackUnpublished',\n  LocalTrackSubscribed = 'localTrackSubscribed',\n  TrackPublished = 'trackPublished',\n  TrackUnpublished = 'trackUnpublished',\n  TrackSubscribed = 'trackSubscribed',\n  TrackUnsubscribed = 'trackUnsubscribed',\n  TrackSubscriptionFailed = 'trackSubscriptionFailed',\n  TrackMuted = 'trackMuted',\n  TrackUnmuted = 'trackUnmuted',\n  ActiveSpeakersChanged = 'activeSpeakersChanged',\n  RoomMetadataChanged = 'roomMetadataChanged',\n  RoomSidChanged = 'roomSidChanged',\n  ParticipantMetadataChanged = 'participantMetadataChanged',\n  ParticipantNameChanged = 'participantNameChanged',\n  ParticipantAttributesChanged = 'participantAttributesChanged',\n  ParticipantEncryptionStatusChanged = 'participantEncryptionStatusChanged',\n  ConnectionQualityChanged = 'connectionQualityChanged',\n  DataReceived = 'dataReceived',\n  ChatMessage = 'chatMessage',\n  DtmfReceived = 'dtmfReceived',\n  EncryptionError = 'encryptionError',\n  ConnectionStateChanged = 'connectionStateChanged',\n  Connected = 'connected',\n  Disconnected = 'disconnected',\n  Reconnecting = 'reconnecting',\n  Reconnected = 'reconnected',\n  RoomUpdated = 'roomUpdated',\n  Moved = 'moved',\n  TokenRefreshed = 'tokenRefreshed',\n}\n"],"mappings":"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","names":["import_rtc_ffi_bindings","FfiRoomOptions","EventEmitter","RoomEvent"]}