@livekit/rtc-node 0.13.33 → 0.13.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{audio_stream-CnODFAmo.d.cts → audio_stream-BNVaONuH.d.cts} +36 -11
- package/dist/audio_stream.d.cts +2 -2
- package/dist/data_streams/index.d.cts +1 -1
- package/dist/data_streams/stream_reader.cjs +97 -58
- package/dist/data_streams/stream_reader.cjs.map +1 -1
- package/dist/data_streams/stream_reader.d.cts +1 -1
- package/dist/data_streams/stream_reader.d.ts +28 -11
- package/dist/data_streams/stream_reader.d.ts.map +1 -1
- package/dist/data_streams/stream_writer.d.cts +1 -1
- package/dist/data_streams/types.cjs.map +1 -1
- package/dist/data_streams/types.d.cts +1 -1
- package/dist/data_streams/types.d.ts +6 -0
- package/dist/data_streams/types.d.ts.map +1 -1
- package/dist/e2ee.cjs +5 -1
- package/dist/e2ee.cjs.map +1 -1
- package/dist/e2ee.d.cts +3 -1
- package/dist/e2ee.d.ts +3 -1
- package/dist/e2ee.d.ts.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/participant.cjs +178 -196
- package/dist/participant.cjs.map +1 -1
- package/dist/participant.d.cts +2 -2
- package/dist/participant.d.ts +22 -5
- package/dist/participant.d.ts.map +1 -1
- package/dist/room.cjs +166 -126
- package/dist/room.cjs.map +1 -1
- package/dist/room.d.cts +3 -3
- package/dist/room.d.ts +16 -8
- package/dist/room.d.ts.map +1 -1
- package/dist/tests/e2e_common.cjs +157 -0
- package/dist/tests/e2e_common.cjs.map +1 -0
- package/dist/tests/e2e_common.d.cts +56 -0
- package/dist/tests/e2e_common.d.ts +37 -0
- package/dist/tests/e2e_common.d.ts.map +1 -0
- package/dist/track.d.cts +2 -2
- package/dist/track_publication.d.cts +2 -2
- package/dist/{types-_PdPVish.d.cts → types-f7wTlFjs.d.cts} +34 -11
- package/dist/utils.cjs +27 -2
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +8 -1
- package/dist/utils.d.ts +6 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/video_stream.d.cts +2 -2
- package/package.json +2 -2
- package/src/audio_stream_room_lifecycle.test.ts +4 -1
- package/src/data_streams/stream_reader.ts +132 -82
- package/src/data_streams/types.ts +6 -0
- package/src/e2ee.ts +7 -0
- package/src/participant.ts +251 -228
- package/src/room.ts +233 -144
- package/src/tests/e2e.test.ts +50 -139
- package/src/tests/e2e_common.ts +163 -0
- package/src/tests/e2e_data_streams.test.ts +639 -0
- package/src/utils.ts +30 -0
- package/src/version.ts +1 -1
package/dist/room.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"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 {\n FfiEvent,\n GetSessionStatsCallback,\n GetSessionStatsResponse,\n} from '@livekit/rtc-ffi-bindings';\nimport { DisconnectReason, type OwnedParticipant } from '@livekit/rtc-ffi-bindings';\nimport type {\n DataStream_Trailer,\n DisconnectCallback,\n TrackPublicationInfo,\n} 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 ReadyForRoomEventResponse,\n RoomDataStreamOptions,\n type RoomInfo,\n type SimulateScenarioCallback,\n type SimulateScenarioKind,\n type SimulateScenarioResponse,\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 { RoomDataStreamOptions };\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 // Aborted on disconnect to cancel any pending FfiClient.waitFor() listeners,\n // preventing them from leaking when the room goes away.\n private disconnectController = new AbortController();\n\n // Guards cleanupOnDisconnect so the ConnectionStateChanged/Disconnected\n // events fire exactly once, no matter which path (explicit disconnect()\n // vs. FFI 'disconnected' event) wins the race.\n private hasCleanedUp = false;\n\n private _token?: string;\n private _serverUrl?: string;\n private _connectionState: ConnectionState = ConnectionState.CONN_DISCONNECTED;\n\n e2eeManager?: E2EEManager;\n\n remoteParticipants: Map<string, RemoteParticipant> = new Map();\n localParticipant?: LocalParticipant;\n\n constructor() {\n super();\n }\n\n get connectionState() {\n return this._connectionState;\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 // Shared promise for concurrent getSid() callers. Without this, each call\n // registers its own RoomSidChanged + Disconnected listeners, and if many\n // calls race only one of each pair is cleaned up — leaking the rest.\n private sidPromise?: Promise<string>;\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 if (!this.sidPromise) {\n this.sidPromise = new Promise<string>((resolve, reject) => {\n const handleDisconnect = () => {\n this.off(RoomEvent.RoomSidChanged, handleRoomUpdate);\n this.sidPromise = undefined;\n reject('Room disconnected before room server id was available');\n };\n const handleRoomUpdate = (sid: string) => {\n if (sid !== '') {\n this.off(RoomEvent.RoomSidChanged, handleRoomUpdate);\n this.off(RoomEvent.Disconnected as any, handleDisconnect);\n this.sidPromise = undefined;\n resolve(sid);\n }\n };\n this.on(RoomEvent.RoomSidChanged, handleRoomUpdate);\n this.once(RoomEvent.Disconnected, handleDisconnect);\n });\n }\n return this.sidPromise;\n }\n\n async getRtcStats() {\n if (!this.isConnected || !this.ffiHandle) {\n throw new Error('getRtcStats requires a connected room');\n }\n // subscribe before issuing the request\n const requestId = FfiClient.instance.getNextRequestAsyncId();\n const cbPromise = FfiClient.instance.waitFor<GetSessionStatsCallback>(\n (ev: FfiEvent) =>\n ev.message.case === 'getSessionStats' && ev.message.value.asyncId === requestId,\n { signal: this.disconnectController.signal },\n );\n FfiClient.instance.request<GetSessionStatsResponse>({\n message: {\n case: 'getSessionStats',\n value: {\n roomHandle: this.ffiHandle.handle,\n requestAsyncId: requestId,\n },\n },\n });\n const cb = await cbPromise;\n if (cb.message.case === 'error') {\n throw new Error(cb.message.value);\n } else if (cb.message.case === 'result') {\n return cb.message.value;\n } else {\n throw new Error('could not retrieve rtc stats');\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 // The Node SDK still implements data streams in TypeScript on top of raw FFI\n // packets, so always advertise only legacy (v1) data stream support to other\n // clients, preserving any other data stream options provided by the user.\n options.dataStream = new RoomDataStreamOptions({\n ...options.dataStream,\n useLegacyClientImplementation: true,\n });\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 try {\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 // Reset the abort controller for this connection session so that\n // a previous disconnect doesn't immediately cancel new operations.\n this.disconnectController = new AbortController();\n this.hasCleanedUp = false;\n this.localParticipant = new LocalParticipant(\n cb.message.value.localParticipant!,\n this.ffiEventLock,\n this.disconnectController.signal,\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 this.updateConnectionState(ConnectionState.CONN_CONNECTED);\n // Release the FFI room event gate after our listener and local state are ready.\n FfiClient.instance.request<ReadyForRoomEventResponse>({\n message: {\n case: 'readyForRoomEvent',\n value: {\n roomHandle: this.ffiHandle.handle,\n },\n },\n });\n break;\n case 'error':\n default:\n throw new ConnectError(cb.message.value || '');\n }\n } catch (e) {\n FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onFfiEvent);\n this.preConnectEvents = [];\n throw e;\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 this.cleanupOnDisconnect(DisconnectReason.CLIENT_INITIATED);\n FfiClient.instance.removeListener(FfiClientEvent.FfiEvent, this.onFfiEvent);\n\n this.removeAllListeners();\n }\n\n /**\n * Trigger a reconnection / chaos scenario for testing. Most useful in\n * tests to deterministically force a Resume (signal-only reconnect that\n * preserves the PeerConnection and existing publications) or a full\n * reconnect (the SDK rebuilds the RtcSession and re-publishes existing\n * local tracks; `RoomEvent.Reconnected` fires).\n */\n async simulateScenario(scenario: SimulateScenarioKind): Promise<void> {\n if (!this.isConnected || !this.ffiHandle) {\n throw new Error('simulateScenario requires a connected room');\n }\n const res = FfiClient.instance.request<SimulateScenarioResponse>({\n message: {\n case: 'simulateScenario',\n value: {\n roomHandle: this.ffiHandle.handle,\n scenario,\n },\n },\n });\n const cb = await FfiClient.instance.waitFor<SimulateScenarioCallback>(\n (ev: FfiEvent) =>\n ev.message.case === 'simulateScenario' && ev.message.value.asyncId === res.asyncId,\n { signal: this.disconnectController.signal },\n );\n if (cb.error) {\n throw new Error(`simulateScenario failed: ${cb.error}`);\n }\n }\n\n private updateConnectionState(newState: ConnectionState) {\n if (this._connectionState === newState) {\n return;\n }\n this._connectionState = newState;\n this.emit(RoomEvent.ConnectionStateChanged, this._connectionState);\n }\n\n // Runs at most once per connection session. The FFI layer and explicit\n // disconnect() both race to get here — whichever wins emits the events,\n // the other is a no-op. A reconnect via connect() clears hasCleanedUp.\n private cleanupOnDisconnect(reason: DisconnectReason = DisconnectReason.CLIENT_INITIATED) {\n if (this.hasCleanedUp) return;\n this.hasCleanedUp = true;\n\n // Error all in-progress stream controllers to prevent FD leaks.\n // Streams that were receiving data but never got a trailer (e.g. the sender\n // disconnected mid-transfer) would otherwise keep their ReadableStream open\n // indefinitely, leaking the underlying controller and any buffered chunks.\n // Using error() instead of close() signals an abnormal termination to consumers.\n for (const [, streamController] of this.byteStreamControllers) {\n try {\n streamController.controller.error(new Error('Disconnected while receiving'));\n } catch {\n // controller may already be closed or errored\n }\n }\n this.byteStreamControllers.clear();\n\n for (const [, streamController] of this.textStreamControllers) {\n try {\n streamController.controller.error(new Error('Disconnected while receiving'));\n } catch {\n // controller may already be closed or errored\n }\n }\n this.textStreamControllers.clear();\n\n // Detach every track from this room so attached FrameProcessors receive\n // onStreamInfoCleared/onCredentialsCleared and the tokenRefreshed listener\n // is removed. Otherwise a server-initiated disconnect leaves processors with\n // stale room context and the Room holding a strong ref to each Track's bound\n // listener until GC. setRoom(null) is idempotent, so this is safe even if a\n // track was already detached (e.g. via unsubscribe/unpublish).\n if (this.localParticipant) {\n for (const pub of this.localParticipant.trackPublications.values()) {\n pub.track?.setRoom(null);\n }\n }\n for (const participant of this.remoteParticipants.values()) {\n for (const pub of participant.trackPublications.values()) {\n pub.track?.setRoom(null);\n }\n }\n\n // Clear sidPromise before removing listeners so that a reconnect\n // doesn't return a stale, permanently-pending promise.\n this.sidPromise = undefined;\n // Abort all pending FfiClient.waitFor() listeners so they don't leak.\n // This causes any in-flight operations (publishData, publishTrack, etc.)\n // to reject and clean up their event listeners.\n this.disconnectController.abort();\n\n this.updateConnectionState(ConnectionState.CONN_DISCONNECTED);\n this.emit(RoomEvent.Disconnected, reason);\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 if (publication?.track) {\n publication.track.setRoom(this);\n }\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 const track = publication?.track;\n if (track) {\n track.setRoom(null);\n }\n this.localParticipant.trackPublications.delete(ev.value.publicationSid!);\n // Emit while `publication.track` is still set, preserving the pre-existing\n // payload for callbacks. The handler is synchronous, so nulling the track\n // right after still completes before any other turn can observe it.\n this.emit(RoomEvent.LocalTrackUnpublished, publication!, this.localParticipant!);\n // Mirror trackUnsubscribed: drop the publication's track reference. This\n // also makes unpublishTrack's own setRoom(null) a no-op when it loses the\n // race (its `pub.track` guard short-circuits), avoiding a redundant clear.\n if (track && publication) {\n publication.track = undefined;\n }\n } else if ((ev.case as string) == 'localTrackRepublished') {\n const value = (ev as any).value;\n const previousSid: string = value.previousSid!;\n const newInfo: TrackPublicationInfo = value.info!;\n const publication = this.localParticipant.trackPublications.get(previousSid);\n if (publication) {\n publication.updateInfo(newInfo);\n this.localParticipant.trackPublications.delete(previousSid);\n this.localParticipant.trackPublications.set(publication.sid!, publication);\n if (publication.track?.info) {\n // Keep the local-track invariant (track.sid == publication.sid, set at\n // publishTrack) intact across republish, then re-push metadata so any\n // attached FrameProcessor learns the new publication SID / credentials.\n // setRoom with the same room is a no-op for the tokenRefreshed listener\n // but re-fans the metadata to every registered AudioStream.\n publication.track.info.sid = publication.sid;\n publication.track.setRoom(this);\n }\n this.emit(RoomEvent.LocalTrackRepublished, publication, previousSid, this.localParticipant);\n } else {\n log.warn(`RoomEvent.LocalTrackRepublished: previous publication not found: ${previousSid}`);\n }\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 // Dispose eagerly so handles don't accumulate when a participant\n // publishes and unpublishes many tracks during a long-lived session.\n publication.ffiHandle.dispose();\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 publication.track?.setRoom(this);\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 track.setRoom(null);\n publication.track = undefined;\n publication.subscribed = false;\n this.emit(RoomEvent.TrackUnsubscribed, track, publication, participant);\n // An unsubscribed track never gets an `eos`, so any AudioStream on it\n // keeps delivering frames. Tear them down here — after the event, so\n // handlers still see a live track.\n track.closeAudioStreams();\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.updateConnectionState(ev.value.state!);\n /*} else if (ev.case == 'connected') {\n this.emit(RoomEvent.Connected);*/\n } else if (ev.case == 'disconnected') {\n // cleanupOnDisconnect emits RoomEvent.Disconnected itself (guarded by\n // hasCleanedUp so it fires exactly once across both disconnect paths).\n this.cleanupOnDisconnect(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 /**\n * Fired when the SDK auto-republished a local track during a full\n * reconnect. The publication object's identity is preserved (the same\n * instance is updated in place with the new server-assigned SIDs);\n * `previousSid` is provided for callers that key external state on the\n * old SID and need to reconcile.\n */\n localTrackRepublished: (\n publication: LocalTrackPublication,\n previousSid: string,\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 LocalTrackRepublished = 'localTrackRepublished',\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"]}
|
|
1
|
+
{"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 {\n FfiEvent,\n GetSessionStatsCallback,\n GetSessionStatsResponse,\n} from '@livekit/rtc-ffi-bindings';\nimport { DisconnectReason, type OwnedParticipant } from '@livekit/rtc-ffi-bindings';\nimport { type DisconnectCallback, type TrackPublicationInfo } from '@livekit/rtc-ffi-bindings';\nimport {\n ByteStreamReaderReadIncrementalRequest,\n type ByteStreamReaderReadIncrementalResponse,\n type ConnectCallback,\n ConnectRequest,\n type ConnectResponse,\n type ConnectionQuality,\n ConnectionState,\n ContinualGatheringPolicy,\n type DataPacketKind,\n DataStream_Chunk,\n type DisconnectResponse,\n RoomDataStreamOptions as FfiRoomDataStreamOptions,\n RoomOptions as FfiRoomOptions,\n type IceServer,\n IceTransportType,\n type OwnedByteStreamReader,\n type OwnedTextStreamReader,\n type ReadyForRoomEventResponse,\n type RoomInfo,\n type SimulateScenarioCallback,\n type SimulateScenarioKind,\n type SimulateScenarioResponse,\n TextStreamReaderReadIncrementalRequest,\n type TextStreamReaderReadIncrementalResponse,\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 ByteStreamHandler, type TextStreamHandler } 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 {\n bigIntToNumber,\n byteStreamInfoFromProto,\n numberToBigInt,\n textStreamInfoFromProto,\n} 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 RoomDataStreamOptions {\n /**\n * Maximum decompressed payload size in bytes accepted for a single incoming\n * data stream. Incoming streams exceeding this limit terminate with an error\n * on the receiving side. Unset falls back to the SDK default of 5gb.\n */\n maxPayloadByteLength?: number;\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 /** Options controlling data stream behavior for this room. */\n dataStream?: RoomDataStreamOptions;\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 // Active incoming stream readers keyed by their FFI reader handle id. Each entry\n // owns the FfiClient listener feeding reader events into the ReadableStream.\n private streamReaders = new Map<\n bigint,\n {\n controller: ReadableStreamDefaultController<DataStream_Chunk>;\n listener: (ev: FfiEvent) => void;\n }\n >();\n private byteStreamHandlers = new Map<string, ByteStreamHandler>();\n private textStreamHandlers = new Map<string, TextStreamHandler>();\n\n private preConnectEvents: FfiEvent[] = [];\n\n // Aborted on disconnect to cancel any pending FfiClient.waitFor() listeners,\n // preventing them from leaking when the room goes away.\n private disconnectController = new AbortController();\n\n // Guards cleanupOnDisconnect so the ConnectionStateChanged/Disconnected\n // events fire exactly once, no matter which path (explicit disconnect()\n // vs. FFI 'disconnected' event) wins the race.\n private hasCleanedUp = false;\n\n private _token?: string;\n private _serverUrl?: string;\n private _connectionState: ConnectionState = ConnectionState.CONN_DISCONNECTED;\n\n e2eeManager?: E2EEManager;\n\n remoteParticipants: Map<string, RemoteParticipant> = new Map();\n localParticipant?: LocalParticipant;\n\n constructor() {\n super();\n }\n\n get connectionState() {\n return this._connectionState;\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 // Shared promise for concurrent getSid() callers. Without this, each call\n // registers its own RoomSidChanged + Disconnected listeners, and if many\n // calls race only one of each pair is cleaned up — leaking the rest.\n private sidPromise?: Promise<string>;\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 if (!this.sidPromise) {\n this.sidPromise = new Promise<string>((resolve, reject) => {\n const handleDisconnect = () => {\n this.off(RoomEvent.RoomSidChanged, handleRoomUpdate);\n this.sidPromise = undefined;\n reject('Room disconnected before room server id was available');\n };\n const handleRoomUpdate = (sid: string) => {\n if (sid !== '') {\n this.off(RoomEvent.RoomSidChanged, handleRoomUpdate);\n this.off(RoomEvent.Disconnected as any, handleDisconnect);\n this.sidPromise = undefined;\n resolve(sid);\n }\n };\n this.on(RoomEvent.RoomSidChanged, handleRoomUpdate);\n this.once(RoomEvent.Disconnected, handleDisconnect);\n });\n }\n return this.sidPromise;\n }\n\n async getRtcStats() {\n if (!this.isConnected || !this.ffiHandle) {\n throw new Error('getRtcStats requires a connected room');\n }\n // subscribe before issuing the request\n const requestId = FfiClient.instance.getNextRequestAsyncId();\n const cbPromise = FfiClient.instance.waitFor<GetSessionStatsCallback>(\n (ev: FfiEvent) =>\n ev.message.case === 'getSessionStats' && ev.message.value.asyncId === requestId,\n { signal: this.disconnectController.signal },\n );\n FfiClient.instance.request<GetSessionStatsResponse>({\n message: {\n case: 'getSessionStats',\n value: {\n roomHandle: this.ffiHandle.handle,\n requestAsyncId: requestId,\n },\n },\n });\n const cb = await cbPromise;\n if (cb.message.case === 'error') {\n throw new Error(cb.message.value);\n } else if (cb.message.case === 'result') {\n return cb.message.value;\n } else {\n throw new Error('could not retrieve rtc stats');\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 // dataStream is a plain user-facing object; convert it to its proto\n // message separately instead of spreading it into the FFI options.\n const { dataStream, ...restOpts } = opts ?? {};\n const options = { ...defaultRoomOptions, ...restOpts };\n const e2eeEnabled = options.encryption || options.e2ee;\n const userE2EEOptions = options.encryption ?? options.e2ee;\n const e2eeOptions: E2EEOptions = {\n ...defaultE2EEOptions,\n ...userE2EEOptions,\n keyProviderOptions: {\n ...defaultE2EEOptions.keyProviderOptions,\n ...userE2EEOptions?.keyProviderOptions,\n },\n };\n\n const mergedOptions = { ...options };\n\n if (options.encryption) {\n mergedOptions.encryption = e2eeOptions;\n } else if (options.e2ee) {\n mergedOptions.e2ee = e2eeOptions;\n }\n\n if (dataStream) {\n mergedOptions.dataStream = new FfiRoomDataStreamOptions({\n maxPayloadByteLength: numberToBigInt(dataStream.maxPayloadByteLength),\n });\n }\n\n const req = new ConnectRequest({\n url: url,\n token: token,\n options: mergedOptions,\n });\n\n FfiClient.instance.on(FfiClientEvent.FfiEvent, this.onFfiEvent);\n\n try {\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 // Reset the abort controller for this connection session so that\n // a previous disconnect doesn't immediately cancel new operations.\n this.disconnectController = new AbortController();\n this.hasCleanedUp = false;\n this.localParticipant = new LocalParticipant(\n cb.message.value.localParticipant!,\n this.ffiEventLock,\n this.disconnectController.signal,\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 this.updateConnectionState(ConnectionState.CONN_CONNECTED);\n // Release the FFI room event gate after our listener and local state are ready.\n FfiClient.instance.request<ReadyForRoomEventResponse>({\n message: {\n case: 'readyForRoomEvent',\n value: {\n roomHandle: this.ffiHandle.handle,\n },\n },\n });\n break;\n case 'error':\n default:\n throw new ConnectError(cb.message.value || '');\n }\n } catch (e) {\n FfiClient.instance.off(FfiClientEvent.FfiEvent, this.onFfiEvent);\n this.preConnectEvents = [];\n throw e;\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 this.cleanupOnDisconnect(DisconnectReason.CLIENT_INITIATED);\n FfiClient.instance.removeListener(FfiClientEvent.FfiEvent, this.onFfiEvent);\n\n this.removeAllListeners();\n }\n\n /**\n * Trigger a reconnection / chaos scenario for testing. Most useful in\n * tests to deterministically force a Resume (signal-only reconnect that\n * preserves the PeerConnection and existing publications) or a full\n * reconnect (the SDK rebuilds the RtcSession and re-publishes existing\n * local tracks; `RoomEvent.Reconnected` fires).\n */\n async simulateScenario(scenario: SimulateScenarioKind): Promise<void> {\n if (!this.isConnected || !this.ffiHandle) {\n throw new Error('simulateScenario requires a connected room');\n }\n const res = FfiClient.instance.request<SimulateScenarioResponse>({\n message: {\n case: 'simulateScenario',\n value: {\n roomHandle: this.ffiHandle.handle,\n scenario,\n },\n },\n });\n const cb = await FfiClient.instance.waitFor<SimulateScenarioCallback>(\n (ev: FfiEvent) =>\n ev.message.case === 'simulateScenario' && ev.message.value.asyncId === res.asyncId,\n { signal: this.disconnectController.signal },\n );\n if (cb.error) {\n throw new Error(`simulateScenario failed: ${cb.error}`);\n }\n }\n\n private updateConnectionState(newState: ConnectionState) {\n if (this._connectionState === newState) {\n return;\n }\n this._connectionState = newState;\n this.emit(RoomEvent.ConnectionStateChanged, this._connectionState);\n }\n\n // Runs at most once per connection session. The FFI layer and explicit\n // disconnect() both race to get here — whichever wins emits the events,\n // the other is a no-op. A reconnect via connect() clears hasCleanedUp.\n private cleanupOnDisconnect(reason: DisconnectReason = DisconnectReason.CLIENT_INITIATED) {\n if (this.hasCleanedUp) return;\n this.hasCleanedUp = true;\n\n // Error all in-progress stream readers to prevent FD leaks.\n // Streams that were receiving data but never reached end-of-stream (e.g. the\n // sender disconnected mid-transfer) would otherwise keep their ReadableStream\n // open indefinitely, leaking the underlying controller and any buffered chunks.\n // Using error() instead of close() signals an abnormal termination to consumers.\n for (const [, { controller, listener }] of this.streamReaders) {\n FfiClient.instance.off(FfiClientEvent.FfiEvent, listener);\n try {\n controller.error(new Error('Disconnected while receiving'));\n } catch {\n // controller may already be closed or errored\n }\n }\n this.streamReaders.clear();\n\n // Detach every track from this room so attached FrameProcessors receive\n // onStreamInfoCleared/onCredentialsCleared and the tokenRefreshed listener\n // is removed. Otherwise a server-initiated disconnect leaves processors with\n // stale room context and the Room holding a strong ref to each Track's bound\n // listener until GC. setRoom(null) is idempotent, so this is safe even if a\n // track was already detached (e.g. via unsubscribe/unpublish).\n if (this.localParticipant) {\n // Data stream writers that were never closed (abandoned by the caller, or\n // left errored by a failed write) keep their native writer alive until\n // their handle is dropped.\n this.localParticipant.disposeOpenStreamWriters();\n\n for (const pub of this.localParticipant.trackPublications.values()) {\n pub.track?.setRoom(null);\n }\n }\n for (const participant of this.remoteParticipants.values()) {\n for (const pub of participant.trackPublications.values()) {\n pub.track?.setRoom(null);\n }\n }\n\n // Clear sidPromise before removing listeners so that a reconnect\n // doesn't return a stale, permanently-pending promise.\n this.sidPromise = undefined;\n // Abort all pending FfiClient.waitFor() listeners so they don't leak.\n // This causes any in-flight operations (publishData, publishTrack, etc.)\n // to reject and clean up their event listeners.\n this.disconnectController.abort();\n\n this.updateConnectionState(ConnectionState.CONN_DISCONNECTED);\n this.emit(RoomEvent.Disconnected, reason);\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 if (publication?.track) {\n publication.track.setRoom(this);\n }\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 const track = publication?.track;\n if (track) {\n track.setRoom(null);\n }\n this.localParticipant.trackPublications.delete(ev.value.publicationSid!);\n // Emit while `publication.track` is still set, preserving the pre-existing\n // payload for callbacks. The handler is synchronous, so nulling the track\n // right after still completes before any other turn can observe it.\n this.emit(RoomEvent.LocalTrackUnpublished, publication!, this.localParticipant!);\n // Mirror trackUnsubscribed: drop the publication's track reference. This\n // also makes unpublishTrack's own setRoom(null) a no-op when it loses the\n // race (its `pub.track` guard short-circuits), avoiding a redundant clear.\n if (track && publication) {\n publication.track = undefined;\n }\n } else if ((ev.case as string) == 'localTrackRepublished') {\n const value = (ev as any).value;\n const previousSid: string = value.previousSid!;\n const newInfo: TrackPublicationInfo = value.info!;\n const publication = this.localParticipant.trackPublications.get(previousSid);\n if (publication) {\n publication.updateInfo(newInfo);\n this.localParticipant.trackPublications.delete(previousSid);\n this.localParticipant.trackPublications.set(publication.sid!, publication);\n if (publication.track?.info) {\n // Keep the local-track invariant (track.sid == publication.sid, set at\n // publishTrack) intact across republish, then re-push metadata so any\n // attached FrameProcessor learns the new publication SID / credentials.\n // setRoom with the same room is a no-op for the tokenRefreshed listener\n // but re-fans the metadata to every registered AudioStream.\n publication.track.info.sid = publication.sid;\n publication.track.setRoom(this);\n }\n this.emit(RoomEvent.LocalTrackRepublished, publication, previousSid, this.localParticipant);\n } else {\n log.warn(`RoomEvent.LocalTrackRepublished: previous publication not found: ${previousSid}`);\n }\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 // Dispose eagerly so handles don't accumulate when a participant\n // publishes and unpublishes many tracks during a long-lived session.\n publication.ffiHandle.dispose();\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 publication.track?.setRoom(this);\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 track.setRoom(null);\n publication.track = undefined;\n publication.subscribed = false;\n this.emit(RoomEvent.TrackUnsubscribed, track, publication, participant);\n // An unsubscribed track never gets an `eos`, so any AudioStream on it\n // keeps delivering frames. Tear them down here — after the event, so\n // handlers still see a live track.\n track.closeAudioStreams();\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.updateConnectionState(ev.value.state!);\n /*} else if (ev.case == 'connected') {\n this.emit(RoomEvent.Connected);*/\n } else if (ev.case == 'disconnected') {\n // cleanupOnDisconnect emits RoomEvent.Disconnected itself (guarded by\n // hasCleanedUp so it fires exactly once across both disconnect paths).\n this.cleanupOnDisconnect(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 === 'byteStreamOpened' && ev.value.reader) {\n this.handleByteStreamOpened(ev.value.reader, ev.value.participantIdentity!);\n } else if (ev.case === 'textStreamOpened' && ev.value.reader) {\n this.handleTextStreamOpened(ev.value.reader, ev.value.participantIdentity!);\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 handleByteStreamOpened(ownedReader: OwnedByteStreamReader, participantIdentity: string) {\n const info = byteStreamInfoFromProto(ownedReader.info!);\n const readerHandle = ownedReader.handle!.id!;\n\n const streamHandlerCallback = this.byteStreamHandlers.get(info.topic);\n if (!streamHandlerCallback) {\n log.debug('ignoring incoming byte stream due to no handler for topic: %s', info.topic);\n // The native reader was never consumed — dispose it to free the handle\n // and any chunks it has buffered.\n new FfiHandle(readerHandle).dispose();\n return;\n }\n\n // Assigned by start(); unsubscribe() drops it once the stream is done with,\n // whether that's end-of-stream or the consumer walking away early.\n let listener: ((ev: FfiEvent) => void) | null = null;\n const unsubscribe = () => {\n if (!listener) {\n return;\n }\n FfiClient.instance.off(FfiClientEvent.FfiEvent, listener);\n listener = null;\n this.streamReaders.delete(readerHandle);\n };\n\n const stream = new ReadableStream<DataStream_Chunk>({\n start: (controller) => {\n listener = (ev: FfiEvent) => {\n if (\n ev.message.case !== 'byteStreamReaderEvent' ||\n ev.message.value.readerHandle !== readerHandle\n ) {\n return;\n }\n const detail = ev.message.value.detail;\n if (detail.case === 'chunkReceived') {\n const content = detail.value.content!;\n if (content.length > 0) {\n controller.enqueue(new DataStream_Chunk({ content }));\n }\n } else if (detail.case === 'eos') {\n unsubscribe();\n const error = detail.value.error;\n if (error) {\n // Abnormal termination (e.g. remote abort, payload over the\n // receiver's size limit): surface the error to the consumer\n // instead of presenting a truncated payload as a clean EOF.\n controller.error(new Error(error.description ?? 'stream terminated'));\n } else {\n controller.close();\n }\n }\n };\n FfiClient.instance.on(FfiClientEvent.FfiEvent, listener);\n this.streamReaders.set(readerHandle, { controller, listener });\n\n // Start the incremental read; the native reader buffers chunks received\n // before this request, so nothing is lost. This consumes the FFI handle.\n FfiClient.instance.request<ByteStreamReaderReadIncrementalResponse>({\n message: {\n case: 'byteReadIncremental',\n value: new ByteStreamReaderReadIncrementalRequest({ readerHandle }),\n },\n });\n },\n // The consumer stopped reading before end-of-stream — ByteStreamReader\n // .close(), or breaking out of a for-await. The eos event that would have\n // unsubscribed is never consumed, so the listener would otherwise be run\n // against every FFI event for the rest of the process. Only the\n // subscription is released: the reader handle was consumed by the\n // readIncremental request above, so there is nothing left to dispose.\n cancel: () => unsubscribe(),\n });\n\n streamHandlerCallback(\n new ByteStreamReader(info, stream, bigIntToNumber(ownedReader.info!.totalLength)),\n { identity: participantIdentity },\n );\n }\n\n private handleTextStreamOpened(ownedReader: OwnedTextStreamReader, participantIdentity: string) {\n const info = textStreamInfoFromProto(ownedReader.info!);\n const readerHandle = ownedReader.handle!.id!;\n\n const streamHandlerCallback = this.textStreamHandlers.get(info.topic);\n if (!streamHandlerCallback) {\n log.debug('ignoring incoming text stream due to no handler for topic: %s', info.topic);\n // The native reader was never consumed — dispose it to free the handle\n // and any chunks it has buffered.\n new FfiHandle(readerHandle).dispose();\n return;\n }\n\n const textEncoder = new TextEncoder();\n\n // Assigned by start(); unsubscribe() drops it once the stream is done with,\n // whether that's end-of-stream or the consumer walking away early.\n let listener: ((ev: FfiEvent) => void) | null = null;\n const unsubscribe = () => {\n if (!listener) {\n return;\n }\n FfiClient.instance.off(FfiClientEvent.FfiEvent, listener);\n listener = null;\n this.streamReaders.delete(readerHandle);\n };\n\n const stream = new ReadableStream<DataStream_Chunk>({\n start: (controller) => {\n listener = (ev: FfiEvent) => {\n if (\n ev.message.case !== 'textStreamReaderEvent' ||\n ev.message.value.readerHandle !== readerHandle\n ) {\n return;\n }\n const detail = ev.message.value.detail;\n if (detail.case === 'chunkReceived') {\n const content = textEncoder.encode(detail.value.content!);\n if (content.length > 0) {\n controller.enqueue(new DataStream_Chunk({ content }));\n }\n } else if (detail.case === 'eos') {\n unsubscribe();\n const error = detail.value.error;\n if (error) {\n // Abnormal termination (e.g. remote abort, payload over the\n // receiver's size limit): surface the error to the consumer\n // instead of presenting a truncated payload as a clean EOF.\n controller.error(new Error(error.description ?? 'stream terminated'));\n } else {\n controller.close();\n }\n }\n };\n FfiClient.instance.on(FfiClientEvent.FfiEvent, listener);\n this.streamReaders.set(readerHandle, { controller, listener });\n\n // Start the incremental read; the native reader buffers chunks received\n // before this request, so nothing is lost. This consumes the FFI handle.\n FfiClient.instance.request<TextStreamReaderReadIncrementalResponse>({\n message: {\n case: 'textReadIncremental',\n value: new TextStreamReaderReadIncrementalRequest({ readerHandle }),\n },\n });\n },\n // The consumer stopped reading before end-of-stream — TextStreamReader\n // .close(), or breaking out of a for-await. The eos event that would have\n // unsubscribed is never consumed, so the listener would otherwise be run\n // against every FFI event for the rest of the process. Only the\n // subscription is released: the reader handle was consumed by the\n // readIncremental request above, so there is nothing left to dispose.\n cancel: () => unsubscribe(),\n });\n\n streamHandlerCallback(\n new TextStreamReader(info, stream, bigIntToNumber(ownedReader.info!.totalLength)),\n { identity: participantIdentity },\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 /**\n * Fired when the SDK auto-republished a local track during a full\n * reconnect. The publication object's identity is preserved (the same\n * instance is updated in place with the new server-assigned SIDs);\n * `previousSid` is provided for callers that key external state on the\n * old SID and need to reconcile.\n */\n localTrackRepublished: (\n publication: LocalTrackPublication,\n previousSid: string,\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 LocalTrackRepublished = 'localTrackRepublished',\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","FfiRoomDataStreamOptions","RoomEvent"]}
|
package/dist/room.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import '@livekit/rtc-ffi-bindings';
|
|
2
2
|
import '@livekit/typed-emitter';
|
|
3
|
-
import './types-
|
|
3
|
+
import './types-f7wTlFjs.cjs';
|
|
4
4
|
import './e2ee.cjs';
|
|
5
|
-
export { C as ConnectError, j as Room,
|
|
5
|
+
export { C as ConnectError, j as Room, p as RoomCallbacks, k as RoomDataStreamOptions, l as RoomEvent, m as RoomOptions, n as RtcConfiguration, q as defaultRoomOptions, r as defaultRtcConfiguration } from './audio_stream-BNVaONuH.cjs';
|
|
6
6
|
import './types.cjs';
|
|
7
7
|
import 'node:stream/web';
|
|
8
8
|
import './audio_frame.cjs';
|
package/dist/room.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type EncryptionType } from '@livekit/rtc-ffi-bindings';
|
|
2
2
|
import { DisconnectReason } from '@livekit/rtc-ffi-bindings';
|
|
3
|
-
import { type ConnectionQuality, ConnectionState, ContinualGatheringPolicy, type DataPacketKind, RoomOptions as FfiRoomOptions, type IceServer, IceTransportType,
|
|
3
|
+
import { type ConnectionQuality, ConnectionState, ContinualGatheringPolicy, type DataPacketKind, RoomOptions as FfiRoomOptions, type IceServer, IceTransportType, type RoomInfo, type SimulateScenarioKind } from '@livekit/rtc-ffi-bindings';
|
|
4
4
|
import type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';
|
|
5
|
-
import type
|
|
5
|
+
import { type ByteStreamHandler, type TextStreamHandler } from './data_streams/types.js';
|
|
6
6
|
import type { E2EEOptions } from './e2ee.js';
|
|
7
7
|
import { E2EEManager } from './e2ee.js';
|
|
8
8
|
import type { Participant } from './participant.js';
|
|
@@ -11,13 +11,20 @@ import type { LocalTrack, RemoteTrack } from './track.js';
|
|
|
11
11
|
import type { LocalTrackPublication, TrackPublication } from './track_publication.js';
|
|
12
12
|
import { RemoteTrackPublication } from './track_publication.js';
|
|
13
13
|
import type { ChatMessage } from './types.js';
|
|
14
|
-
export { RoomDataStreamOptions };
|
|
15
14
|
export interface RtcConfiguration {
|
|
16
15
|
iceTransportType: IceTransportType;
|
|
17
16
|
continualGatheringPolicy: ContinualGatheringPolicy;
|
|
18
17
|
iceServers: IceServer[];
|
|
19
18
|
}
|
|
20
19
|
export declare const defaultRtcConfiguration: RtcConfiguration;
|
|
20
|
+
export interface RoomDataStreamOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Maximum decompressed payload size in bytes accepted for a single incoming
|
|
23
|
+
* data stream. Incoming streams exceeding this limit terminate with an error
|
|
24
|
+
* on the receiving side. Unset falls back to the SDK default of 5gb.
|
|
25
|
+
*/
|
|
26
|
+
maxPayloadByteLength?: number;
|
|
27
|
+
}
|
|
21
28
|
export interface RoomOptions {
|
|
22
29
|
autoSubscribe: boolean;
|
|
23
30
|
dynacast: boolean;
|
|
@@ -27,6 +34,8 @@ export interface RoomOptions {
|
|
|
27
34
|
e2ee?: E2EEOptions;
|
|
28
35
|
encryption?: E2EEOptions;
|
|
29
36
|
rtcConfig?: RtcConfiguration;
|
|
37
|
+
/** Options controlling data stream behavior for this room. */
|
|
38
|
+
dataStream?: RoomDataStreamOptions;
|
|
30
39
|
}
|
|
31
40
|
export declare const defaultRoomOptions: FfiRoomOptions;
|
|
32
41
|
declare const Room_base: new () => TypedEmitter<RoomCallbacks>;
|
|
@@ -39,8 +48,7 @@ export declare class Room extends Room_base {
|
|
|
39
48
|
* before processing the next events
|
|
40
49
|
*/
|
|
41
50
|
private ffiEventLock;
|
|
42
|
-
private
|
|
43
|
-
private textStreamControllers;
|
|
51
|
+
private streamReaders;
|
|
44
52
|
private byteStreamHandlers;
|
|
45
53
|
private textStreamHandlers;
|
|
46
54
|
private preConnectEvents;
|
|
@@ -132,9 +140,8 @@ export declare class Room extends Room_base {
|
|
|
132
140
|
private requirePublicationOfParticipant;
|
|
133
141
|
private requirePublicationOfRemoteParticipant;
|
|
134
142
|
private createRemoteParticipant;
|
|
135
|
-
private
|
|
136
|
-
private
|
|
137
|
-
private handleStreamTrailer;
|
|
143
|
+
private handleByteStreamOpened;
|
|
144
|
+
private handleTextStreamOpened;
|
|
138
145
|
}
|
|
139
146
|
export declare class ConnectError extends Error {
|
|
140
147
|
constructor(message: string);
|
|
@@ -217,4 +224,5 @@ export declare enum RoomEvent {
|
|
|
217
224
|
Moved = "moved",
|
|
218
225
|
TokenRefreshed = "tokenRefreshed"
|
|
219
226
|
}
|
|
227
|
+
export {};
|
|
220
228
|
//# sourceMappingURL=room.d.ts.map
|
package/dist/room.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"room.d.ts","sourceRoot":"","sources":["../src/room.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAMjF,OAAO,EAAE,gBAAgB,EAAyB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"room.d.ts","sourceRoot":"","sources":["../src/room.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var e2e_common_exports = {};
|
|
20
|
+
__export(e2e_common_exports, {
|
|
21
|
+
concatUint8: () => concatUint8,
|
|
22
|
+
connectTestRooms: () => connectTestRooms,
|
|
23
|
+
createJoinToken: () => createJoinToken,
|
|
24
|
+
describeE2E: () => describeE2E,
|
|
25
|
+
getTestEnv: () => getTestEnv,
|
|
26
|
+
hasE2EEnv: () => hasE2EEnv,
|
|
27
|
+
testTimeoutMs: () => testTimeoutMs,
|
|
28
|
+
waitFor: () => waitFor,
|
|
29
|
+
waitForRoomEvent: () => waitForRoomEvent,
|
|
30
|
+
withTimeout: () => withTimeout
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(e2e_common_exports);
|
|
33
|
+
var import_livekit_server_sdk = require("livekit-server-sdk");
|
|
34
|
+
var import_node_crypto = require("node:crypto");
|
|
35
|
+
var import_promises = require("node:timers/promises");
|
|
36
|
+
var import_vitest = require("vitest");
|
|
37
|
+
var import__ = require("../index.cjs");
|
|
38
|
+
const hasE2EEnv = !!process.env.LIVEKIT_URL && !!process.env.LIVEKIT_API_KEY && !!process.env.LIVEKIT_API_SECRET;
|
|
39
|
+
const describeE2E = hasE2EEnv ? import_vitest.describe : import_vitest.describe.skip;
|
|
40
|
+
const testTimeoutMs = 1e4;
|
|
41
|
+
function normalizeLiveKitUrl(url) {
|
|
42
|
+
if (url.startsWith("http://")) return `ws://${url.slice("http://".length)}`;
|
|
43
|
+
if (url.startsWith("https://")) return `wss://${url.slice("https://".length)}`;
|
|
44
|
+
return url;
|
|
45
|
+
}
|
|
46
|
+
function getTestEnv() {
|
|
47
|
+
if (!hasE2EEnv) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
"Missing required env vars for e2e: LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET"
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
url: normalizeLiveKitUrl(process.env.LIVEKIT_URL),
|
|
54
|
+
apiKey: process.env.LIVEKIT_API_KEY,
|
|
55
|
+
apiSecret: process.env.LIVEKIT_API_SECRET
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async function withTimeout(promise, timeoutMs, message) {
|
|
59
|
+
return await Promise.race([
|
|
60
|
+
promise,
|
|
61
|
+
(async () => {
|
|
62
|
+
await (0, import_promises.setTimeout)(timeoutMs);
|
|
63
|
+
throw new Error(message);
|
|
64
|
+
})()
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
async function waitFor(condition, opts) {
|
|
68
|
+
const intervalMs = opts.intervalMs ?? 50;
|
|
69
|
+
const deadline = Date.now() + opts.timeoutMs;
|
|
70
|
+
while (Date.now() < deadline) {
|
|
71
|
+
if (condition()) return;
|
|
72
|
+
await (0, import_promises.setTimeout)(intervalMs);
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`Timed out waiting for condition${opts.debugName ? ` (${opts.debugName})` : ""}`);
|
|
75
|
+
}
|
|
76
|
+
async function createJoinToken(params) {
|
|
77
|
+
const token = new import_livekit_server_sdk.AccessToken(params.env.apiKey, params.env.apiSecret, {
|
|
78
|
+
identity: params.identity,
|
|
79
|
+
name: params.name,
|
|
80
|
+
ttl: "30m"
|
|
81
|
+
});
|
|
82
|
+
token.addGrant({
|
|
83
|
+
room: params.roomName,
|
|
84
|
+
roomJoin: true,
|
|
85
|
+
roomCreate: true,
|
|
86
|
+
canPublish: true,
|
|
87
|
+
canSubscribe: true
|
|
88
|
+
});
|
|
89
|
+
return await token.toJwt();
|
|
90
|
+
}
|
|
91
|
+
async function connectTestRooms(count, options) {
|
|
92
|
+
const env = getTestEnv();
|
|
93
|
+
const roomName = `test_room_${(0, import_node_crypto.randomUUID)()}`;
|
|
94
|
+
const rooms = await Promise.all(
|
|
95
|
+
Array.from({ length: count }, async (_, i) => {
|
|
96
|
+
const token = await createJoinToken({
|
|
97
|
+
env,
|
|
98
|
+
roomName,
|
|
99
|
+
identity: `p${i}`,
|
|
100
|
+
name: `Participant ${i}`
|
|
101
|
+
});
|
|
102
|
+
const room = new import__.Room();
|
|
103
|
+
await room.connect(env.url, token, {
|
|
104
|
+
autoSubscribe: true,
|
|
105
|
+
dynacast: false,
|
|
106
|
+
...options
|
|
107
|
+
});
|
|
108
|
+
return room;
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
const start = Date.now();
|
|
112
|
+
await waitFor(() => rooms.every((r) => r.remoteParticipants.size === count - 1), {
|
|
113
|
+
timeoutMs: 5e3,
|
|
114
|
+
debugName: `participant visibility (${Date.now() - start}ms)`
|
|
115
|
+
});
|
|
116
|
+
return { roomName, rooms };
|
|
117
|
+
}
|
|
118
|
+
function waitForRoomEvent(room, event, timeoutMs, take, match) {
|
|
119
|
+
return withTimeout(
|
|
120
|
+
new Promise((resolve) => {
|
|
121
|
+
const handler = (...args) => {
|
|
122
|
+
if (match && !match(...args)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
room.off(event, handler);
|
|
126
|
+
resolve(take(...args));
|
|
127
|
+
};
|
|
128
|
+
room.on(event, handler);
|
|
129
|
+
}),
|
|
130
|
+
timeoutMs,
|
|
131
|
+
`Timed out waiting for ${event}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
function concatUint8(chunks) {
|
|
135
|
+
const len = chunks.reduce((acc, c) => acc + c.byteLength, 0);
|
|
136
|
+
const out = new Uint8Array(len);
|
|
137
|
+
let offset = 0;
|
|
138
|
+
for (const c of chunks) {
|
|
139
|
+
out.set(c, offset);
|
|
140
|
+
offset += c.byteLength;
|
|
141
|
+
}
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
145
|
+
0 && (module.exports = {
|
|
146
|
+
concatUint8,
|
|
147
|
+
connectTestRooms,
|
|
148
|
+
createJoinToken,
|
|
149
|
+
describeE2E,
|
|
150
|
+
getTestEnv,
|
|
151
|
+
hasE2EEnv,
|
|
152
|
+
testTimeoutMs,
|
|
153
|
+
waitFor,
|
|
154
|
+
waitForRoomEvent,
|
|
155
|
+
withTimeout
|
|
156
|
+
});
|
|
157
|
+
//# sourceMappingURL=e2e_common.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/tests/e2e_common.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { AccessToken } from 'livekit-server-sdk';\nimport { randomUUID } from 'node:crypto';\nimport { setTimeout as delay } from 'node:timers/promises';\nimport { describe } from 'vitest';\nimport { Room, type RoomEvent, type RoomOptions } from '../index.js';\n\nexport const hasE2EEnv =\n !!process.env.LIVEKIT_URL && !!process.env.LIVEKIT_API_KEY && !!process.env.LIVEKIT_API_SECRET;\n// Explicitly typed so declaration emit doesn't reference vitest's\n// non-exported internal suite types (TS4023).\nexport const describeE2E: typeof describe = hasE2EEnv\n ? describe\n : (describe.skip as typeof describe);\nexport const testTimeoutMs = 10_000;\n\nexport type TestEnv = {\n url: string;\n apiKey: string;\n apiSecret: string;\n};\n\nfunction normalizeLiveKitUrl(url: string): string {\n if (url.startsWith('http://')) return `ws://${url.slice('http://'.length)}`;\n if (url.startsWith('https://')) return `wss://${url.slice('https://'.length)}`;\n return url;\n}\n\nexport function getTestEnv(): TestEnv {\n if (!hasE2EEnv) {\n throw new Error(\n 'Missing required env vars for e2e: LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET',\n );\n }\n return {\n url: normalizeLiveKitUrl(process.env.LIVEKIT_URL!),\n apiKey: process.env.LIVEKIT_API_KEY!,\n apiSecret: process.env.LIVEKIT_API_SECRET!,\n };\n}\n\nexport async function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n message: string,\n): Promise<T> {\n return await Promise.race([\n promise,\n (async () => {\n await delay(timeoutMs);\n throw new Error(message);\n })(),\n ]);\n}\n\nexport async function waitFor(\n condition: () => boolean,\n opts: { timeoutMs: number; intervalMs?: number; debugName?: string },\n): Promise<void> {\n const intervalMs = opts.intervalMs ?? 50;\n const deadline = Date.now() + opts.timeoutMs;\n while (Date.now() < deadline) {\n if (condition()) return;\n await delay(intervalMs);\n }\n throw new Error(`Timed out waiting for condition${opts.debugName ? ` (${opts.debugName})` : ''}`);\n}\n\nexport async function createJoinToken(params: {\n env: TestEnv;\n roomName: string;\n identity: string;\n name: string;\n}): Promise<string> {\n const token = new AccessToken(params.env.apiKey, params.env.apiSecret, {\n identity: params.identity,\n name: params.name,\n ttl: '30m',\n });\n token.addGrant({\n room: params.roomName,\n roomJoin: true,\n roomCreate: true,\n canPublish: true,\n canSubscribe: true,\n });\n return await token.toJwt();\n}\n\nexport async function connectTestRooms(\n count: number,\n /**\n * Extra room options applied to every room, e.g. data stream limits or\n * `encryption` to bring up the whole set with a shared e2ee key.\n */\n options?: Partial<RoomOptions>,\n): Promise<{ roomName: string; rooms: Room[] }> {\n const env = getTestEnv();\n const roomName = `test_room_${randomUUID()}`;\n const rooms = await Promise.all(\n Array.from({ length: count }, async (_, i) => {\n const token = await createJoinToken({\n env,\n roomName,\n identity: `p${i}`,\n name: `Participant ${i}`,\n });\n const room = new Room();\n await room.connect(env.url, token, {\n autoSubscribe: true,\n dynacast: false,\n ...options,\n });\n return room;\n }),\n );\n\n const start = Date.now();\n await waitFor(() => rooms.every((r) => r.remoteParticipants.size === count - 1), {\n timeoutMs: 5000,\n debugName: `participant visibility (${Date.now() - start}ms)`,\n });\n\n return { roomName, rooms };\n}\n\nexport function waitForRoomEvent<R>(\n room: Room,\n event: RoomEvent,\n timeoutMs: number,\n take: (...args: any[]) => R,\n /** Ignore emissions that don't match, e.g. events for another participant. */\n match?: (...args: unknown[]) => boolean,\n): Promise<R> {\n return withTimeout(\n new Promise<R>((resolve) => {\n const handler = (...args: any[]) => {\n if (match && !match(...args)) {\n return;\n }\n // typed-emitter doesn't expose `.once` in the type surface, so do manual once+cleanup.\n room.off(event as any, handler as any);\n resolve(take(...args));\n };\n room.on(event as any, handler as any);\n }),\n timeoutMs,\n `Timed out waiting for ${event}`,\n );\n}\n\nexport function concatUint8(chunks: Uint8Array[]): Uint8Array {\n const len = chunks.reduce((acc, c) => acc + c.byteLength, 0);\n const out = new Uint8Array(len);\n let offset = 0;\n for (const c of chunks) {\n out.set(c, offset);\n offset += c.byteLength;\n }\n return out;\n}\n"],"mappings":"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","names":["delay"]}
|