@agatx/serenada-core 0.6.10
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/ConsoleLogger.d.ts +6 -0
- package/dist/ConsoleLogger.d.ts.map +1 -0
- package/dist/ConsoleLogger.js +21 -0
- package/dist/ConsoleLogger.js.map +1 -0
- package/dist/RoomWatcher.d.ts +34 -0
- package/dist/RoomWatcher.d.ts.map +1 -0
- package/dist/RoomWatcher.js +103 -0
- package/dist/RoomWatcher.js.map +1 -0
- package/dist/SerenadaCore.d.ts +47 -0
- package/dist/SerenadaCore.d.ts.map +1 -0
- package/dist/SerenadaCore.js +141 -0
- package/dist/SerenadaCore.js.map +1 -0
- package/dist/SerenadaDiagnostics.d.ts +49 -0
- package/dist/SerenadaDiagnostics.d.ts.map +1 -0
- package/dist/SerenadaDiagnostics.js +421 -0
- package/dist/SerenadaDiagnostics.js.map +1 -0
- package/dist/SerenadaServerProvider.d.ts +48 -0
- package/dist/SerenadaServerProvider.d.ts.map +1 -0
- package/dist/SerenadaServerProvider.js +296 -0
- package/dist/SerenadaServerProvider.js.map +1 -0
- package/dist/SerenadaSession.d.ts +180 -0
- package/dist/SerenadaSession.d.ts.map +1 -0
- package/dist/SerenadaSession.js +1082 -0
- package/dist/SerenadaSession.js.map +1 -0
- package/dist/SignalingProvider.d.ts +132 -0
- package/dist/SignalingProvider.d.ts.map +1 -0
- package/dist/SignalingProvider.js +50 -0
- package/dist/SignalingProvider.js.map +1 -0
- package/dist/api/roomApi.d.ts +2 -0
- package/dist/api/roomApi.d.ts.map +1 -0
- package/dist/api/roomApi.js +14 -0
- package/dist/api/roomApi.js.map +1 -0
- package/dist/cameraModes.d.ts +13 -0
- package/dist/cameraModes.d.ts.map +1 -0
- package/dist/cameraModes.js +35 -0
- package/dist/cameraModes.js.map +1 -0
- package/dist/configValidation.d.ts +10 -0
- package/dist/configValidation.d.ts.map +1 -0
- package/dist/configValidation.js +24 -0
- package/dist/configValidation.js.map +1 -0
- package/dist/constants.d.ts +33 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +65 -0
- package/dist/constants.js.map +1 -0
- package/dist/formatError.d.ts +3 -0
- package/dist/formatError.d.ts.map +1 -0
- package/dist/formatError.js +7 -0
- package/dist/formatError.js.map +1 -0
- package/dist/iceServers.d.ts +2 -0
- package/dist/iceServers.d.ts.map +1 -0
- package/dist/iceServers.js +21 -0
- package/dist/iceServers.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/layout/computeLayout.d.ts +81 -0
- package/dist/layout/computeLayout.d.ts.map +1 -0
- package/dist/layout/computeLayout.js +380 -0
- package/dist/layout/computeLayout.js.map +1 -0
- package/dist/media/AudioLevelMonitor.d.ts +51 -0
- package/dist/media/AudioLevelMonitor.d.ts.map +1 -0
- package/dist/media/AudioLevelMonitor.js +179 -0
- package/dist/media/AudioLevelMonitor.js.map +1 -0
- package/dist/media/MediaEngine.d.ts +137 -0
- package/dist/media/MediaEngine.d.ts.map +1 -0
- package/dist/media/MediaEngine.js +1224 -0
- package/dist/media/MediaEngine.js.map +1 -0
- package/dist/media/callStats.d.ts +16 -0
- package/dist/media/callStats.d.ts.map +1 -0
- package/dist/media/callStats.js +214 -0
- package/dist/media/callStats.js.map +1 -0
- package/dist/media/localVideoRecovery.d.ts +16 -0
- package/dist/media/localVideoRecovery.d.ts.map +1 -0
- package/dist/media/localVideoRecovery.js +14 -0
- package/dist/media/localVideoRecovery.js.map +1 -0
- package/dist/recoveryStorage.d.ts +33 -0
- package/dist/recoveryStorage.d.ts.map +1 -0
- package/dist/recoveryStorage.js +88 -0
- package/dist/recoveryStorage.js.map +1 -0
- package/dist/serverUrls.d.ts +8 -0
- package/dist/serverUrls.d.ts.map +1 -0
- package/dist/serverUrls.js +65 -0
- package/dist/serverUrls.js.map +1 -0
- package/dist/signaling/SignalingEngine.d.ts +126 -0
- package/dist/signaling/SignalingEngine.d.ts.map +1 -0
- package/dist/signaling/SignalingEngine.js +720 -0
- package/dist/signaling/SignalingEngine.js.map +1 -0
- package/dist/signaling/payloads.d.ts +76 -0
- package/dist/signaling/payloads.d.ts.map +1 -0
- package/dist/signaling/payloads.js +160 -0
- package/dist/signaling/payloads.js.map +1 -0
- package/dist/signaling/roomStatuses.d.ts +9 -0
- package/dist/signaling/roomStatuses.d.ts.map +1 -0
- package/dist/signaling/roomStatuses.js +71 -0
- package/dist/signaling/roomStatuses.js.map +1 -0
- package/dist/signaling/transportConfig.d.ts +3 -0
- package/dist/signaling/transportConfig.d.ts.map +1 -0
- package/dist/signaling/transportConfig.js +27 -0
- package/dist/signaling/transportConfig.js.map +1 -0
- package/dist/signaling/transports/index.d.ts +13 -0
- package/dist/signaling/transports/index.d.ts.map +1 -0
- package/dist/signaling/transports/index.js +11 -0
- package/dist/signaling/transports/index.js.map +1 -0
- package/dist/signaling/transports/sse.d.ts +26 -0
- package/dist/signaling/transports/sse.d.ts.map +1 -0
- package/dist/signaling/transports/sse.js +131 -0
- package/dist/signaling/transports/sse.js.map +1 -0
- package/dist/signaling/transports/types.d.ts +17 -0
- package/dist/signaling/transports/types.d.ts.map +1 -0
- package/dist/signaling/transports/types.js +2 -0
- package/dist/signaling/transports/types.js.map +1 -0
- package/dist/signaling/transports/ws.d.ts +21 -0
- package/dist/signaling/transports/ws.d.ts.map +1 -0
- package/dist/signaling/transports/ws.js +93 -0
- package/dist/signaling/transports/ws.js.map +1 -0
- package/dist/signaling/types.d.ts +53 -0
- package/dist/signaling/types.d.ts.map +1 -0
- package/dist/signaling/types.js +2 -0
- package/dist/signaling/types.js.map +1 -0
- package/dist/types.d.ts +279 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +43 -0
- package/src/ConsoleLogger.ts +14 -0
- package/src/RoomWatcher.ts +127 -0
- package/src/SerenadaCore.ts +163 -0
- package/src/SerenadaDiagnostics.ts +485 -0
- package/src/SerenadaServerProvider.ts +362 -0
- package/src/SerenadaSession.ts +1258 -0
- package/src/SignalingProvider.ts +207 -0
- package/src/api/roomApi.ts +16 -0
- package/src/cameraModes.ts +34 -0
- package/src/configValidation.ts +35 -0
- package/src/constants.ts +77 -0
- package/src/formatError.ts +5 -0
- package/src/iceServers.ts +20 -0
- package/src/index.ts +155 -0
- package/src/layout/computeLayout.ts +639 -0
- package/src/media/AudioLevelMonitor.ts +190 -0
- package/src/media/MediaEngine.ts +1183 -0
- package/src/media/callStats.ts +260 -0
- package/src/media/localVideoRecovery.ts +39 -0
- package/src/recoveryStorage.ts +101 -0
- package/src/serverUrls.ts +69 -0
- package/src/signaling/SignalingEngine.ts +762 -0
- package/src/signaling/payloads.ts +215 -0
- package/src/signaling/roomStatuses.ts +89 -0
- package/src/signaling/transportConfig.ts +30 -0
- package/src/signaling/transports/index.ts +26 -0
- package/src/signaling/transports/sse.ts +146 -0
- package/src/signaling/transports/types.ts +19 -0
- package/src/signaling/transports/ws.ts +108 -0
- package/src/signaling/types.ts +68 -0
- package/src/types.ts +299 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { SignalingProviderEmitter, type JoinOptions, type RoomEndedEvent, type RoomStateEvent, type SignalingProviderParticipant } from './SignalingProvider.js';
|
|
2
|
+
import type { SerenadaLogger } from './types.js';
|
|
3
|
+
import { SignalingEngine } from './signaling/SignalingEngine.js';
|
|
4
|
+
import {
|
|
5
|
+
parseErrorPayload,
|
|
6
|
+
parseJoinedPayload,
|
|
7
|
+
parseNegotiationDirtyPayload,
|
|
8
|
+
parseRelayFailedPayload,
|
|
9
|
+
parseRoomStatePayload,
|
|
10
|
+
parseTurnRefreshedPayload,
|
|
11
|
+
} from './signaling/payloads.js';
|
|
12
|
+
import type { RoomParticipant, SignalingMessage } from './signaling/types.js';
|
|
13
|
+
import type { TransportKind } from './signaling/transports/types.js';
|
|
14
|
+
import { TURN_FETCH_TIMEOUT_MS } from './constants.js';
|
|
15
|
+
import { formatError } from './formatError.js';
|
|
16
|
+
import { buildApiUrl, resolveServerUrls } from './serverUrls.js';
|
|
17
|
+
|
|
18
|
+
interface TurnCredentialsResponse {
|
|
19
|
+
username?: string;
|
|
20
|
+
password?: string;
|
|
21
|
+
uris?: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SerenadaServerProviderConfig {
|
|
25
|
+
serverHost: string;
|
|
26
|
+
transports?: TransportKind[];
|
|
27
|
+
logger?: SerenadaLogger;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class SerenadaServerProvider extends SignalingProviderEmitter {
|
|
31
|
+
readonly capabilities = { handlesReconnection: true };
|
|
32
|
+
|
|
33
|
+
private readonly serverHost: string;
|
|
34
|
+
private readonly signaling: SignalingEngine;
|
|
35
|
+
private readonly logger?: SerenadaLogger;
|
|
36
|
+
private readonly unsubscribeStateChange: () => void;
|
|
37
|
+
private readonly unsubscribeMessages: () => void;
|
|
38
|
+
private lastConnected = false;
|
|
39
|
+
private currentTurnToken: string | null = null;
|
|
40
|
+
private previousParticipants = new Map<string, SignalingProviderParticipant>();
|
|
41
|
+
private currentHostPeerId: string | null = null;
|
|
42
|
+
private disconnected = false;
|
|
43
|
+
|
|
44
|
+
constructor(config: SerenadaServerProviderConfig) {
|
|
45
|
+
super();
|
|
46
|
+
const urls = resolveServerUrls(config.serverHost);
|
|
47
|
+
this.serverHost = config.serverHost;
|
|
48
|
+
this.logger = config.logger;
|
|
49
|
+
this.signaling = new SignalingEngine({
|
|
50
|
+
wsUrl: urls.wsUrl,
|
|
51
|
+
httpBaseUrl: urls.httpBaseUrl,
|
|
52
|
+
transports: config.transports,
|
|
53
|
+
logger: config.logger,
|
|
54
|
+
});
|
|
55
|
+
this.unsubscribeStateChange = this.signaling.onStateChange(() => {
|
|
56
|
+
this.handleStateChange();
|
|
57
|
+
});
|
|
58
|
+
this.unsubscribeMessages = this.signaling.subscribeToMessages((message) => {
|
|
59
|
+
this.handleMessage(message);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
connect(): void {
|
|
64
|
+
this.signaling.connect();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
disconnect(): void {
|
|
68
|
+
if (this.disconnected) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
this.disconnected = true;
|
|
72
|
+
this.unsubscribeStateChange();
|
|
73
|
+
this.unsubscribeMessages();
|
|
74
|
+
this.signaling.destroy();
|
|
75
|
+
this.previousParticipants.clear();
|
|
76
|
+
this.currentTurnToken = null;
|
|
77
|
+
this.currentHostPeerId = null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
joinRoom(roomId: string, options?: JoinOptions): void {
|
|
81
|
+
this.previousParticipants.clear();
|
|
82
|
+
this.signaling.joinRoom(roomId, {
|
|
83
|
+
createMaxParticipants: options?.maxParticipants,
|
|
84
|
+
displayName: options?.displayName,
|
|
85
|
+
peerId: options?.appPeerId,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
leaveRoom(): void {
|
|
90
|
+
this.previousParticipants.clear();
|
|
91
|
+
this.currentTurnToken = null;
|
|
92
|
+
this.currentHostPeerId = null;
|
|
93
|
+
this.signaling.leaveRoom();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
endRoom(): void {
|
|
97
|
+
this.signaling.endRoom();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
sendToPeer(peerId: string, type: string, payload: unknown): void {
|
|
101
|
+
this.signaling.sendMessage(type, toRecordPayload(payload), peerId);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
broadcast(type: string, payload: unknown): void {
|
|
105
|
+
this.signaling.sendMessage(type, toRecordPayload(payload));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Install (or clear) a gate consulted before each periodic TURN refresh.
|
|
110
|
+
* The gate returns false to skip the refresh — used by the session to
|
|
111
|
+
* suppress refreshes while every peer is on a direct ICE path, so the
|
|
112
|
+
* call remains independent of signaling for the refresh cadence.
|
|
113
|
+
*/
|
|
114
|
+
setTurnRefreshGate(gate: (() => Promise<boolean>) | null): void {
|
|
115
|
+
this.signaling.setTurnRefreshGate(gate);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async getIceServers(): Promise<RTCIceServer[]> {
|
|
119
|
+
const token = this.currentTurnToken?.trim();
|
|
120
|
+
if (!token) {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const controller = new AbortController();
|
|
125
|
+
const timeout = globalThis.setTimeout(() => controller.abort(), TURN_FETCH_TIMEOUT_MS);
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
const response = await fetch(
|
|
129
|
+
buildApiUrl(this.serverHost, `/api/turn-credentials?token=${encodeURIComponent(token)}`),
|
|
130
|
+
{ signal: controller.signal },
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if (!response.ok) {
|
|
134
|
+
throw new Error(`TURN credentials request failed: ${response.status}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const data = await response.json() as TurnCredentialsResponse;
|
|
138
|
+
const uris = Array.isArray(data.uris)
|
|
139
|
+
? data.uris.filter((uri): uri is string => typeof uri === 'string' && uri.trim().length > 0)
|
|
140
|
+
: [];
|
|
141
|
+
|
|
142
|
+
if (uris.length === 0) {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return [{
|
|
147
|
+
urls: uris,
|
|
148
|
+
username: typeof data.username === 'string' ? data.username : undefined,
|
|
149
|
+
credential: typeof data.password === 'string' ? data.password : undefined,
|
|
150
|
+
}];
|
|
151
|
+
} finally {
|
|
152
|
+
globalThis.clearTimeout(timeout);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private handleStateChange(): void {
|
|
157
|
+
const connected = this.signaling.isConnected;
|
|
158
|
+
if (connected === this.lastConnected) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this.lastConnected = connected;
|
|
163
|
+
if (connected) {
|
|
164
|
+
this.emit('connected', {
|
|
165
|
+
transport: this.signaling.activeTransport ?? undefined,
|
|
166
|
+
});
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
this.emit('disconnected', undefined);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private handleMessage(message: SignalingMessage): void {
|
|
174
|
+
switch (message.type) {
|
|
175
|
+
case 'joined':
|
|
176
|
+
this.handleJoined(message);
|
|
177
|
+
break;
|
|
178
|
+
case 'room_state':
|
|
179
|
+
this.handleRoomState(message);
|
|
180
|
+
break;
|
|
181
|
+
case 'room_ended':
|
|
182
|
+
this.previousParticipants.clear();
|
|
183
|
+
this.emitRoomEnded(message.payload);
|
|
184
|
+
break;
|
|
185
|
+
case 'error': {
|
|
186
|
+
const error = parseErrorPayload(message.payload);
|
|
187
|
+
if (error) {
|
|
188
|
+
this.emit('error', error);
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
case 'turn-refreshed': {
|
|
193
|
+
const refreshed = parseTurnRefreshedPayload(message.payload);
|
|
194
|
+
if (!refreshed) {
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
this.currentTurnToken = refreshed.turnToken;
|
|
198
|
+
void this.refreshIceServers();
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
case 'offer':
|
|
202
|
+
case 'answer':
|
|
203
|
+
case 'ice':
|
|
204
|
+
case 'content_state':
|
|
205
|
+
case 'participant_media_state':
|
|
206
|
+
this.emitPeerMessage(message);
|
|
207
|
+
break;
|
|
208
|
+
case 'negotiation_dirty': {
|
|
209
|
+
const dirty = parseNegotiationDirtyPayload(message.payload);
|
|
210
|
+
if (dirty) {
|
|
211
|
+
this.emit('negotiationDirty', { withCid: dirty.with });
|
|
212
|
+
}
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
case 'relay_failed': {
|
|
216
|
+
const failed = parseRelayFailedPayload(message.payload);
|
|
217
|
+
if (failed) {
|
|
218
|
+
this.emit('relayFailed', failed);
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private emitRoomEnded(payload: Record<string, unknown> | undefined): void {
|
|
226
|
+
const by = roomEndedBy(payload) ?? this.currentHostPeerId ?? undefined;
|
|
227
|
+
const event = {
|
|
228
|
+
reason: roomEndedReason(payload) ?? 'room ended',
|
|
229
|
+
} as RoomEndedEvent;
|
|
230
|
+
if (by) {
|
|
231
|
+
event.by = by;
|
|
232
|
+
}
|
|
233
|
+
this.emit('roomEnded', event);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private handleJoined(message: SignalingMessage): void {
|
|
237
|
+
const payload = parseJoinedPayload(message.payload);
|
|
238
|
+
if (!payload || !message.cid) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (payload.turnToken) {
|
|
243
|
+
this.currentTurnToken = payload.turnToken;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const event = {
|
|
247
|
+
peerId: message.cid,
|
|
248
|
+
participants: payload.participants.map(mapParticipant),
|
|
249
|
+
hostPeerId: payload.hostCid ?? undefined,
|
|
250
|
+
maxParticipants: payload.maxParticipants,
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
this.currentHostPeerId = event.hostPeerId ?? null;
|
|
254
|
+
this.previousParticipants = toParticipantMap(event.participants);
|
|
255
|
+
this.emit('joined', event);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private handleRoomState(message: SignalingMessage): void {
|
|
259
|
+
const payload = parseRoomStatePayload(message.payload);
|
|
260
|
+
if (!payload) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (typeof message.payload?.turnToken === 'string') {
|
|
265
|
+
this.currentTurnToken = message.payload.turnToken;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const nextState: RoomStateEvent = {
|
|
269
|
+
participants: payload.participants.map(mapParticipant),
|
|
270
|
+
hostPeerId: payload.hostCid ?? undefined,
|
|
271
|
+
maxParticipants: payload.maxParticipants,
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const nextMap = toParticipantMap(nextState.participants);
|
|
275
|
+
this.emitParticipantDiffs(this.previousParticipants, nextMap);
|
|
276
|
+
this.previousParticipants = nextMap;
|
|
277
|
+
this.currentHostPeerId = nextState.hostPeerId ?? null;
|
|
278
|
+
this.emit('roomStateUpdated', nextState);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private emitParticipantDiffs(
|
|
282
|
+
previous: Map<string, SignalingProviderParticipant>,
|
|
283
|
+
next: Map<string, SignalingProviderParticipant>,
|
|
284
|
+
): void {
|
|
285
|
+
for (const [peerId, participant] of next) {
|
|
286
|
+
if (!previous.has(peerId)) {
|
|
287
|
+
this.emit('peerJoined', participant);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
for (const [peerId, participant] of previous) {
|
|
292
|
+
if (!next.has(peerId)) {
|
|
293
|
+
this.emit('peerLeft', participant);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private emitPeerMessage(message: SignalingMessage): void {
|
|
299
|
+
const payload = message.payload;
|
|
300
|
+
const from = typeof payload?.from === 'string'
|
|
301
|
+
? payload.from
|
|
302
|
+
: (typeof message.cid === 'string' ? message.cid : null);
|
|
303
|
+
if (!from) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
this.emit('message', {
|
|
308
|
+
from,
|
|
309
|
+
type: message.type,
|
|
310
|
+
payload: payload ?? {},
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private async refreshIceServers(): Promise<void> {
|
|
315
|
+
try {
|
|
316
|
+
const servers = await this.getIceServers();
|
|
317
|
+
this.emit('iceServersChanged', servers);
|
|
318
|
+
} catch (error) {
|
|
319
|
+
this.logger?.log('warning', 'Signaling', `Failed to refresh ICE servers: ${formatError(error)}`);
|
|
320
|
+
this.emit('error', {
|
|
321
|
+
code: 'TURN_REFRESH_FAILED',
|
|
322
|
+
message: formatError(error),
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function mapParticipant(participant: RoomParticipant): SignalingProviderParticipant {
|
|
329
|
+
return {
|
|
330
|
+
peerId: participant.cid,
|
|
331
|
+
joinedAt: participant.joinedAt,
|
|
332
|
+
displayName: participant.displayName,
|
|
333
|
+
appPeerId: participant.peerId,
|
|
334
|
+
audioEnabled: participant.audioEnabled,
|
|
335
|
+
videoEnabled: participant.videoEnabled,
|
|
336
|
+
connectionStatus: participant.connectionStatus,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function toParticipantMap(participants: SignalingProviderParticipant[]): Map<string, SignalingProviderParticipant> {
|
|
341
|
+
return new Map(participants.map((participant) => [participant.peerId, participant]));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function toRecordPayload(payload: unknown): Record<string, unknown> | undefined {
|
|
345
|
+
if (payload === undefined) {
|
|
346
|
+
return undefined;
|
|
347
|
+
}
|
|
348
|
+
if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
|
|
349
|
+
return payload as Record<string, unknown>;
|
|
350
|
+
}
|
|
351
|
+
return { value: payload };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function roomEndedBy(payload: Record<string, unknown> | undefined): string | null {
|
|
355
|
+
const by = payload?.by;
|
|
356
|
+
return typeof by === 'string' && by.trim().length > 0 ? by : null;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function roomEndedReason(payload: Record<string, unknown> | undefined): string | null {
|
|
360
|
+
const reason = payload?.reason;
|
|
361
|
+
return typeof reason === 'string' && reason.trim().length > 0 ? reason : null;
|
|
362
|
+
}
|