From b9eb1e68b28af69dc954a306781ff7811c4ae9a3 Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Fri, 16 Oct 2026 16:33:55 -0700 Subject: [PATCH] Add RoomEvent.RoomSidChanged Co-Authored-By: Claude Opus 5.5 (1M context) --- .changeset/room-sid-changed-event.md | 5 +++++ src/room/Room.test.ts | 28 ++++++++++++++++++++++++++++ src/room/Room.ts | 11 +++++++++++ src/room/events.ts | 9 +++++++++ 4 files changed, 53 insertions(+) create mode 100644 .changeset/room-sid-changed-event.md diff --git a/.changeset/room-sid-changed-event.md b/.changeset/room-sid-changed-event.md new file mode 100644 index 0000000000..175356538b --- /dev/null +++ b/.changeset/room-sid-changed-event.md @@ -0,0 +1,5 @@ +--- +'livekit-client': patch +--- + +Add `RoomEvent.RoomSidChanged`, emitted when the room's session ID changes while connected, such as when the server starts a new session for a room that is still live. diff --git a/src/room/Room.test.ts b/src/room/Room.test.ts index 04317e97d4..0c2383c242 100644 --- a/src/room/Room.test.ts +++ b/src/room/Room.test.ts @@ -2,6 +2,7 @@ import { ClientInfo_Capability, JoinResponse, StreamState as ProtoStreamState, + Room as RoomModel, StreamStateUpdate, SubscriptionError, SubscriptionResponse, @@ -463,3 +464,30 @@ describe('audio context ownership', () => { expect(created[0].closed).toBe(true); }); }); + +describe('room updates', () => { + function pushRoomUpdate(room: Room, update: { name?: string; sid?: string }) { + (room as unknown as { handleRoomUpdate: (room: RoomModel) => void }).handleRoomUpdate( + new RoomModel(update), + ); + } + + it('emits RoomSidChanged when the session ID changes', async () => { + const room = new Room(); + room.state = ConnectionState.Connected; + const sidChanged = vi.fn(); + room.on(RoomEvent.RoomSidChanged, sidChanged); + + pushRoomUpdate(room, { name: 'room', sid: 'RM_first' }); + expect(sidChanged).not.toHaveBeenCalled(); + + pushRoomUpdate(room, { name: 'room', sid: 'RM_first' }); + pushRoomUpdate(room, { name: 'room', sid: '' }); + expect(sidChanged).not.toHaveBeenCalled(); + + pushRoomUpdate(room, { name: 'room', sid: 'RM_second' }); + expect(sidChanged).toHaveBeenCalledOnce(); + expect(sidChanged).toHaveBeenCalledWith('RM_second'); + await expect(room.getSid()).resolves.toBe('RM_second'); + }); +}); diff --git a/src/room/Room.ts b/src/room/Room.ts index 0b11fb49e1..390ab53907 100644 --- a/src/room/Room.ts +++ b/src/room/Room.ts @@ -211,6 +211,9 @@ class Room extends (EventEmitter as new () => TypedEmitter) private roomInfo?: RoomModel; + /** last non-empty session ID, updates can carry an empty one */ + private knownSid?: string; + private sidToIdentity: Map; /** connect options of room */ @@ -2455,6 +2458,13 @@ class Room extends (EventEmitter as new () => TypedEmitter) if (oldRoom && oldRoom.metadata !== room.metadata) { this.emitWhenConnected(RoomEvent.RoomMetadataChanged, room.metadata); } + if (room.sid && room.sid !== this.knownSid) { + const hadSid = !!this.knownSid; + this.knownSid = room.sid; + if (hadSid) { + this.emitWhenConnected(RoomEvent.RoomSidChanged, room.sid); + } + } if (oldRoom?.activeRecording !== room.activeRecording) { this.emitWhenConnected(RoomEvent.RecordingStatusChanged, room.activeRecording); } @@ -3285,6 +3295,7 @@ export type RoomEventCallbacks = { ) => void; activeSpeakersChanged: (speakers: Array) => void; roomMetadataChanged: (metadata: string) => void; + roomSidChanged: (sid: string) => void; dataReceived: ( payload: NonSharedUint8Array, participant?: RemoteParticipant, diff --git a/src/room/events.ts b/src/room/events.ts index 73e235fe1b..302ec13ee2 100644 --- a/src/room/events.ts +++ b/src/room/events.ts @@ -220,6 +220,15 @@ export enum RoomEvent { */ RoomMetadataChanged = 'roomMetadataChanged', + /** + * The room's session ID changed while connected, for example when the server + * starts a new session for a room that is still live, or the local participant + * was moved to another room. Room.getSid() returns the new ID. + * + * args: (sid: string) + */ + RoomSidChanged = 'roomSidChanged', + /** * Data received from another participant. * Data packets provides the ability to use LiveKit to send/receive arbitrary payloads.