@observertc/observer-js 1.0.1-9d3744d.0 → 1.0.1-a6fb9d3.0

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.
Files changed (166) hide show
  1. package/README.md +535 -1
  2. package/lib/ObservedCall.d.ts +8 -7
  3. package/lib/ObservedCall.d.ts.map +1 -1
  4. package/lib/ObservedCall.js +42 -38
  5. package/lib/ObservedCallEventMonitor.d.ts +104 -0
  6. package/lib/ObservedCallEventMonitor.d.ts.map +1 -0
  7. package/lib/ObservedCallEventMonitor.js +314 -0
  8. package/lib/ObservedClient.d.ts +20 -10
  9. package/lib/ObservedClient.d.ts.map +1 -1
  10. package/lib/ObservedClient.js +330 -13
  11. package/lib/ObservedClientEventMonitor.d.ts +91 -0
  12. package/lib/ObservedClientEventMonitor.d.ts.map +1 -0
  13. package/lib/ObservedClientEventMonitor.js +254 -0
  14. package/lib/ObservedTURN.d.ts +1 -1
  15. package/lib/ObservedTURN.d.ts.map +1 -1
  16. package/lib/ObservedTURN.js +1 -0
  17. package/lib/ObservedTurnServer.d.ts +2 -2
  18. package/lib/ObservedTurnServer.d.ts.map +1 -1
  19. package/lib/Observer.d.ts +9 -6
  20. package/lib/Observer.d.ts.map +1 -1
  21. package/lib/Observer.js +26 -25
  22. package/lib/ObserverEventMonitor.d.ts +138 -0
  23. package/lib/ObserverEventMonitor.d.ts.map +1 -0
  24. package/lib/ObserverEventMonitor.js +433 -0
  25. package/lib/common/ObserverSummary.d.ts.map +1 -0
  26. package/lib/common/SummaryTypes.d.ts +75 -0
  27. package/lib/common/SummaryTypes.d.ts.map +1 -0
  28. package/lib/common/SummaryTypes.js +2 -0
  29. package/lib/index.d.ts +19 -17
  30. package/lib/index.d.ts.map +1 -1
  31. package/lib/index.js +21 -17
  32. package/lib/mediasoup/ObservedMediasoupRouter.d.ts +7 -0
  33. package/lib/mediasoup/ObservedMediasoupRouter.d.ts.map +1 -0
  34. package/lib/mediasoup/ObservedMediasoupRouter.js +12 -0
  35. package/lib/monitors/SummaryMonitor.d.ts +19 -0
  36. package/lib/monitors/SummaryMonitor.d.ts.map +1 -0
  37. package/lib/monitors/SummaryMonitor.js +129 -0
  38. package/lib/schema/ClientEventTypes.d.ts +147 -19
  39. package/lib/schema/ClientEventTypes.d.ts.map +1 -1
  40. package/lib/schema/ClientEventTypes.js +5 -2
  41. package/lib/schema/ClientSample.d.ts +1 -1
  42. package/lib/schema/ClientSample.d.ts.map +1 -1
  43. package/lib/updaters/{OnAllCallObserverUpdater_.d.ts → OnAllCallObserverUpdater.d.ts} +3 -1
  44. package/lib/updaters/OnAllCallObserverUpdater.d.ts.map +1 -0
  45. package/lib/updaters/{OnAllCallObserverUpdater_.js → OnAllCallObserverUpdater.js} +13 -1
  46. package/lib/updaters/OnAllClientCallUpdater.d.ts +2 -0
  47. package/lib/updaters/OnAllClientCallUpdater.d.ts.map +1 -1
  48. package/lib/updaters/OnAllClientCallUpdater.js +16 -0
  49. package/lib/updaters/{OnAnyCallObserverUpdater_.d.ts → OnAnyCallObserverUpdater.d.ts} +2 -1
  50. package/lib/updaters/OnAnyCallObserverUpdater.d.ts.map +1 -0
  51. package/lib/updaters/{OnAnyCallObserverUpdater_.js → OnAnyCallObserverUpdater.js} +11 -1
  52. package/lib/updaters/OnAnyClientCallUpdater.d.ts +1 -0
  53. package/lib/updaters/OnAnyClientCallUpdater.d.ts.map +1 -1
  54. package/lib/updaters/OnAnyClientCallUpdater.js +11 -2
  55. package/lib/utils/MediasoupRemoteTrackResolver.d.ts +25 -0
  56. package/lib/utils/MediasoupRemoteTrackResolver.d.ts.map +1 -0
  57. package/lib/utils/MediasoupRemoteTrackResolver.js +110 -0
  58. package/lib/utils/RemoteTrackResolver.d.ts +2 -2
  59. package/lib/utils/RemoteTrackResolver.d.ts.map +1 -1
  60. package/lib/{ObservedCertificate.d.ts → webrtc/ObservedCertificate.d.ts} +1 -1
  61. package/lib/webrtc/ObservedCertificate.d.ts.map +1 -0
  62. package/lib/{ObservedCodec.d.ts → webrtc/ObservedCodec.d.ts} +1 -1
  63. package/lib/webrtc/ObservedCodec.d.ts.map +1 -0
  64. package/lib/{ObservedDataChannel.d.ts → webrtc/ObservedDataChannel.d.ts} +3 -1
  65. package/lib/webrtc/ObservedDataChannel.d.ts.map +1 -0
  66. package/lib/{ObservedDataChannel.js → webrtc/ObservedDataChannel.js} +2 -0
  67. package/lib/{ObservedIceCandidate.d.ts → webrtc/ObservedIceCandidate.d.ts} +1 -1
  68. package/lib/webrtc/ObservedIceCandidate.d.ts.map +1 -0
  69. package/lib/{ObservedIceCandidatePair.d.ts → webrtc/ObservedIceCandidatePair.d.ts} +3 -2
  70. package/lib/webrtc/ObservedIceCandidatePair.d.ts.map +1 -0
  71. package/lib/{ObservedIceCandidatePair.js → webrtc/ObservedIceCandidatePair.js} +17 -1
  72. package/lib/{ObservedIceTransport.d.ts → webrtc/ObservedIceTransport.d.ts} +1 -1
  73. package/lib/webrtc/ObservedIceTransport.d.ts.map +1 -0
  74. package/lib/{ObservedInboundRtp.d.ts → webrtc/ObservedInboundRtp.d.ts} +2 -2
  75. package/lib/webrtc/ObservedInboundRtp.d.ts.map +1 -0
  76. package/lib/{ObservedInboundTrack.d.ts → webrtc/ObservedInboundTrack.d.ts} +7 -4
  77. package/lib/webrtc/ObservedInboundTrack.d.ts.map +1 -0
  78. package/lib/{ObservedInboundTrack.js → webrtc/ObservedInboundTrack.js} +4 -1
  79. package/lib/{ObservedMediaPlayout.d.ts → webrtc/ObservedMediaPlayout.d.ts} +2 -2
  80. package/lib/webrtc/ObservedMediaPlayout.d.ts.map +1 -0
  81. package/lib/{ObservedMediaSource.d.ts → webrtc/ObservedMediaSource.d.ts} +2 -2
  82. package/lib/webrtc/ObservedMediaSource.d.ts.map +1 -0
  83. package/lib/{ObservedOutboundRtp.d.ts → webrtc/ObservedOutboundRtp.d.ts} +2 -2
  84. package/lib/webrtc/ObservedOutboundRtp.d.ts.map +1 -0
  85. package/lib/{ObservedOutboundTrack.d.ts → webrtc/ObservedOutboundTrack.d.ts} +7 -4
  86. package/lib/webrtc/ObservedOutboundTrack.d.ts.map +1 -0
  87. package/lib/{ObservedOutboundTrack.js → webrtc/ObservedOutboundTrack.js} +4 -1
  88. package/lib/{ObservedPeerConnection.d.ts → webrtc/ObservedPeerConnection.d.ts} +26 -4
  89. package/lib/webrtc/ObservedPeerConnection.d.ts.map +1 -0
  90. package/lib/{ObservedPeerConnection.js → webrtc/ObservedPeerConnection.js} +82 -17
  91. package/lib/{ObservedPeerConnectionTransport.d.ts → webrtc/ObservedPeerConnectionTransport.d.ts} +1 -1
  92. package/lib/webrtc/ObservedPeerConnectionTransport.d.ts.map +1 -0
  93. package/lib/{ObservedRemoteInboundRtp.d.ts → webrtc/ObservedRemoteInboundRtp.d.ts} +2 -2
  94. package/lib/webrtc/ObservedRemoteInboundRtp.d.ts.map +1 -0
  95. package/lib/{ObservedRemoteOutboundRtp.d.ts → webrtc/ObservedRemoteOutboundRtp.d.ts} +2 -2
  96. package/lib/webrtc/ObservedRemoteOutboundRtp.d.ts.map +1 -0
  97. package/package.json +1 -1
  98. package/src/ObservedCall.ts +44 -48
  99. package/src/ObservedCallEventMonitor.ts +392 -0
  100. package/src/ObservedClient.ts +363 -22
  101. package/src/ObservedClientEventMonitor.ts +309 -0
  102. package/src/ObservedTURN.ts +2 -1
  103. package/src/ObservedTurnServer.ts +2 -2
  104. package/src/Observer.ts +37 -33
  105. package/src/ObserverEventMonitor.ts +551 -0
  106. package/src/common/SummaryTypes.ts +91 -0
  107. package/src/index.ts +19 -18
  108. package/src/mediasoup/ObservedMediasoupRouter.ts +12 -0
  109. package/src/monitors/SummaryMonitor.ts +181 -0
  110. package/src/schema/ClientEventTypes.ts +187 -19
  111. package/src/schema/ClientSample.ts +1 -1
  112. package/src/updaters/{OnAllCallObserverUpdater_.ts → OnAllCallObserverUpdater.ts} +14 -1
  113. package/src/updaters/OnAllClientCallUpdater.ts +15 -0
  114. package/src/updaters/{OnAnyCallObserverUpdater_.ts → OnAnyCallObserverUpdater.ts} +12 -3
  115. package/src/updaters/OnAnyClientCallUpdater.ts +9 -2
  116. package/src/utils/MediasoupRemoteTrackResolver.ts +155 -0
  117. package/src/utils/RemoteTrackResolver.ts +2 -2
  118. package/src/{ObservedCertificate.ts → webrtc/ObservedCertificate.ts} +1 -1
  119. package/src/{ObservedCodec.ts → webrtc/ObservedCodec.ts} +1 -1
  120. package/src/{ObservedDataChannel.ts → webrtc/ObservedDataChannel.ts} +4 -1
  121. package/src/{ObservedIceCandidate.ts → webrtc/ObservedIceCandidate.ts} +1 -1
  122. package/src/{ObservedIceCandidatePair.ts → webrtc/ObservedIceCandidatePair.ts} +19 -3
  123. package/src/{ObservedIceTransport.ts → webrtc/ObservedIceTransport.ts} +1 -1
  124. package/src/{ObservedInboundRtp.ts → webrtc/ObservedInboundRtp.ts} +2 -2
  125. package/src/{ObservedInboundTrack.ts → webrtc/ObservedInboundTrack.ts} +9 -4
  126. package/src/{ObservedMediaPlayout.ts → webrtc/ObservedMediaPlayout.ts} +2 -2
  127. package/src/{ObservedMediaSource.ts → webrtc/ObservedMediaSource.ts} +2 -2
  128. package/src/{ObservedOutboundRtp.ts → webrtc/ObservedOutboundRtp.ts} +2 -2
  129. package/src/{ObservedOutboundTrack.ts → webrtc/ObservedOutboundTrack.ts} +9 -4
  130. package/src/{ObservedPeerConnection.ts → webrtc/ObservedPeerConnection.ts} +124 -37
  131. package/src/{ObservedPeerConnectionTransport.ts → webrtc/ObservedPeerConnectionTransport.ts} +1 -1
  132. package/src/{ObservedRemoteInboundRtp.ts → webrtc/ObservedRemoteInboundRtp.ts} +2 -2
  133. package/src/{ObservedRemoteOutboundRtp.ts → webrtc/ObservedRemoteOutboundRtp.ts} +2 -2
  134. package/lib/ObservedCertificate.d.ts.map +0 -1
  135. package/lib/ObservedCodec.d.ts.map +0 -1
  136. package/lib/ObservedDataChannel.d.ts.map +0 -1
  137. package/lib/ObservedIceCandidate.d.ts.map +0 -1
  138. package/lib/ObservedIceCandidatePair.d.ts.map +0 -1
  139. package/lib/ObservedIceTransport.d.ts.map +0 -1
  140. package/lib/ObservedInboundRtp.d.ts.map +0 -1
  141. package/lib/ObservedInboundTrack.d.ts.map +0 -1
  142. package/lib/ObservedMediaPlayout.d.ts.map +0 -1
  143. package/lib/ObservedMediaSource.d.ts.map +0 -1
  144. package/lib/ObservedOutboundRtp.d.ts.map +0 -1
  145. package/lib/ObservedOutboundTrack.d.ts.map +0 -1
  146. package/lib/ObservedPeerConnection.d.ts.map +0 -1
  147. package/lib/ObservedPeerConnectionTransport.d.ts.map +0 -1
  148. package/lib/ObservedRemoteInboundRtp.d.ts.map +0 -1
  149. package/lib/ObservedRemoteOutboundRtp.d.ts.map +0 -1
  150. package/lib/ObserverSummary.d.ts.map +0 -1
  151. package/lib/updaters/OnAllCallObserverUpdater_.d.ts.map +0 -1
  152. package/lib/updaters/OnAnyCallObserverUpdater_.d.ts.map +0 -1
  153. /package/lib/{ObserverSummary.d.ts → common/ObserverSummary.d.ts} +0 -0
  154. /package/lib/{ObserverSummary.js → common/ObserverSummary.js} +0 -0
  155. /package/lib/{ObservedCertificate.js → webrtc/ObservedCertificate.js} +0 -0
  156. /package/lib/{ObservedCodec.js → webrtc/ObservedCodec.js} +0 -0
  157. /package/lib/{ObservedIceCandidate.js → webrtc/ObservedIceCandidate.js} +0 -0
  158. /package/lib/{ObservedIceTransport.js → webrtc/ObservedIceTransport.js} +0 -0
  159. /package/lib/{ObservedInboundRtp.js → webrtc/ObservedInboundRtp.js} +0 -0
  160. /package/lib/{ObservedMediaPlayout.js → webrtc/ObservedMediaPlayout.js} +0 -0
  161. /package/lib/{ObservedMediaSource.js → webrtc/ObservedMediaSource.js} +0 -0
  162. /package/lib/{ObservedOutboundRtp.js → webrtc/ObservedOutboundRtp.js} +0 -0
  163. /package/lib/{ObservedPeerConnectionTransport.js → webrtc/ObservedPeerConnectionTransport.js} +0 -0
  164. /package/lib/{ObservedRemoteInboundRtp.js → webrtc/ObservedRemoteInboundRtp.js} +0 -0
  165. /package/lib/{ObservedRemoteOutboundRtp.js → webrtc/ObservedRemoteOutboundRtp.js} +0 -0
  166. /package/src/{ObserverSummary.ts → common/ObserverSummary.ts} +0 -0
@@ -1,5 +1,5 @@
1
1
  import { EventEmitter } from 'events';
2
- import { ObservedPeerConnection } from './ObservedPeerConnection';
2
+ import { ObservedPeerConnection } from './webrtc/ObservedPeerConnection';
3
3
  import { createLogger } from './common/logger';
4
4
  // eslint-disable-next-line camelcase
5
5
  import { ClientEvent, ClientMetaData, ClientSample, PeerConnectionSample, ClientIssue, ExtensionStat } from './schema/ClientSample';
@@ -10,21 +10,18 @@ import { ClientMetaTypes } from './schema/ClientMetaTypes';
10
10
  import { parseJsonAs } from './common/utils';
11
11
  import { CalculatedScore } from './scores/CalculatedScore';
12
12
  import { Detectors } from './detectors/Detectors';
13
+ import { clear } from 'console';
13
14
 
14
15
  const logger = createLogger('ObservedClient');
15
16
 
16
17
  export type ObservedClientSettings<AppData extends Record<string, unknown> = Record<string, unknown>> = {
17
18
  clientId: string;
19
+ maxIdleTimeMs?: number;
18
20
  appData?: AppData;
19
21
  };
20
22
 
21
23
  export type ObservedClientEvents = {
22
- update: [
23
- {
24
- sample: ClientSample;
25
- elapsedTimeInMs: number;
26
- }
27
- ];
24
+ update: [sample: ClientSample, elapsedTimeInMs: number];
28
25
  close: [];
29
26
  joined: [];
30
27
  issue: [ClientIssue];
@@ -33,8 +30,9 @@ export type ObservedClientEvents = {
33
30
  left: [];
34
31
  usingturn: [boolean];
35
32
  usermediaerror: [string];
33
+ extensionStats: [ExtensionStat];
34
+ clientEvent: [ClientEvent];
36
35
 
37
- // do we need this?
38
36
  newpeerconnection: [ObservedPeerConnection];
39
37
  };
40
38
 
@@ -48,14 +46,12 @@ export declare interface ObservedClient {
48
46
  export class ObservedClient<AppData extends Record<string, unknown> = Record<string, unknown>> extends EventEmitter {
49
47
  public readonly detectors: Detectors;
50
48
 
51
- public readonly clientId: string;
52
49
  public readonly observedPeerConnections = new Map<string, ObservedPeerConnection>();
53
50
  public readonly calculatedScore: CalculatedScore = {
54
51
  weight: 1,
55
52
  value: undefined,
56
53
  };
57
54
 
58
- public appData: AppData;
59
55
  public attachments?: Record<string, unknown>;
60
56
 
61
57
  public updated = Date.now();
@@ -64,6 +60,8 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
64
60
 
65
61
  public joinedAt?: number;
66
62
  public leftAt?: number;
63
+ public closedAt?: number;
64
+ public lastSampleTimestamp?: number;
67
65
  // the timestamp of the CLIENT_JOINED event
68
66
  public operationSystem?: MetaData.OperationSystem;
69
67
  public engine?: MetaData.Engine;
@@ -136,15 +134,27 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
136
134
  public readonly mediaDevices: MetaData.MediaDeviceInfo[] = [];
137
135
  public issues: ClientIssue[] = [];
138
136
 
139
- public constructor(settings: ObservedClientSettings<AppData>, public readonly call: ObservedCall) {
137
+ private _injections: Pick<ClientSample, 'clientEvents' | 'clientIssues' | 'extensionStats' | 'attachments' | 'clientMetaItems'> = {};
138
+ private _autoCloseTimer?: ReturnType<typeof setTimeout>;
139
+
140
+ public constructor(
141
+ public readonly settings: ObservedClientSettings<AppData>,
142
+ public readonly call: ObservedCall
143
+ ) {
140
144
  super();
141
145
  this.setMaxListeners(Infinity);
146
+ this._createAutoClose();
142
147
 
143
- this.clientId = settings.clientId;
144
- this.appData = settings.appData ?? {} as AppData;
145
-
146
148
  this.detectors = new Detectors();
147
149
  }
150
+
151
+ public get clientId() {
152
+ return this.settings.clientId;
153
+ }
154
+
155
+ public get appData(): AppData {
156
+ return this.settings.appData ?? {} as AppData;
157
+ }
148
158
 
149
159
  public get numberOfPeerConnections() {
150
160
  return this.observedPeerConnections.size;
@@ -158,13 +168,42 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
158
168
  if (this.closed) return;
159
169
  this.closed = true;
160
170
 
171
+ this._injections.clientEvents?.forEach((clientEvent) => this._processClientEvent(clientEvent));
172
+ this._injections.clientIssues?.forEach((clientIssue) => this.addIssue(clientIssue));
173
+ this._injections.extensionStats?.forEach((extensionStat) => this.addExtensionStats(extensionStat));
174
+ this._injections.clientMetaItems?.forEach((clientMetaItem) => this.addMetadata(clientMetaItem));
175
+
161
176
  Array.from(this.observedPeerConnections.values()).forEach((peerConnection) => peerConnection.close());
177
+ if (!this.leftAt) {
178
+ this.leftAt = this.lastSampleTimestamp;
179
+
180
+ if (this.leftAt) {
181
+ this.emit('left');
182
+ }
183
+ }
184
+ this.closedAt = Date.now();
162
185
 
163
186
  this.emit('close');
164
187
  }
165
188
 
189
+ private _createAutoClose() {
190
+ if (this.closed) return;
191
+ if (this.settings.maxIdleTimeMs === undefined || this.settings.maxIdleTimeMs < 1) return;
192
+
193
+ clearTimeout(this._autoCloseTimer);
194
+
195
+ this._autoCloseTimer = setTimeout(() => {
196
+ if (this.closed) return;
197
+
198
+ logger.debug(`Client ${this.clientId} is idle for ${this.settings.maxIdleTimeMs}ms, closing...`);
199
+ this.close();
200
+ }, this.settings.maxIdleTimeMs);
201
+ }
202
+
166
203
  public accept(sample: ClientSample): void {
167
204
  if (this.closed) throw new Error(`Client ${this.clientId} is closed`);
205
+
206
+ this._createAutoClose();
168
207
 
169
208
  const now = Date.now();
170
209
  const elapsedInMs = now - this.updated;
@@ -204,9 +243,15 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
204
243
  this.usingTCP = false;
205
244
  this.currentMinRttInMs = undefined;
206
245
  this.currentMaxRttInMs = undefined;
207
-
246
+
247
+ this._mergeInjections(sample);
248
+
249
+ const clientEventsPostBuffer: ClientEvent[] = [];
250
+
208
251
  for (const clientEvent of sample.clientEvents ?? []) {
209
- this._processClientEvent(clientEvent);
252
+ this._processClientEvent(clientEvent, clientEventsPostBuffer);
253
+
254
+ this.call.observer.emit('client-event', this, clientEvent);
210
255
  }
211
256
 
212
257
  for (const metaData of sample.clientMetaItems ?? []) {
@@ -280,7 +325,9 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
280
325
  }
281
326
  }
282
327
 
283
- sample.peerConnections?.forEach(this._updatePeerConnection.bind(this));
328
+ for (const clientEvent of clientEventsPostBuffer) {
329
+ this._processClientEvent(clientEvent);
330
+ }
284
331
 
285
332
  // emit new attachments?
286
333
  this.attachments = sample.attachments;
@@ -313,11 +360,12 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
313
360
  this.calculatedScore.value = sample.score;
314
361
  this.detectors.update();
315
362
 
363
+ this.lastSampleTimestamp = sample.timestamp;
316
364
  // emit update
317
- this.emit('update', {
365
+ this.emit('update',
318
366
  sample,
319
- elapsedTimeInMs: now - this.updated,
320
- });
367
+ now - this.updated,
368
+ );
321
369
  this.updated = now;
322
370
 
323
371
  // if result changed after update
@@ -327,7 +375,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
327
375
  }
328
376
  }
329
377
 
330
- private _processClientEvent(event: ClientEvent) {
378
+ private _processClientEvent(event: ClientEvent, postBuffer?: ClientEvent[]) {
331
379
  // eslint-disable-next-line no-console
332
380
  // console.warn('ClientEvent', event);
333
381
  switch (event.type) {
@@ -343,6 +391,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
343
391
  logger.warn(`Client ${this.clientId} joinedAt timestamp was updated to ${event.timestamp}. the joined event will not be emitted.`);
344
392
  }
345
393
  }
394
+ logger.debug('Client %s joined at %o', this.clientId, event);
346
395
  break;
347
396
  }
348
397
  case ClientEventTypes.CLIENT_LEFT: {
@@ -353,12 +402,262 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
353
402
  } else {
354
403
  logger.warn(`Client ${this.clientId} leftAt timestamp was already set`);
355
404
  }
405
+
406
+ }
407
+ logger.debug('Client %s left at %o', this.clientId, event);
408
+ break;
409
+ }
410
+ case ClientEventTypes.PEER_CONNECTION_OPENED: {
411
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
412
+
413
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string') {
414
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
415
+
416
+ if (observedPeerConnection) {
417
+ observedPeerConnection.openedAt = event.timestamp;
418
+ } else if (postBuffer) {
419
+ postBuffer.push(event);
420
+ } else {
421
+ logger.warn('Received PEER_CONNECTION_OPENED event without a corresponding observedPeerConnection: %o', event);
422
+ }
423
+
424
+ }
425
+ break;
426
+ }
427
+ case ClientEventTypes.PEER_CONNECTION_CLOSED: {
428
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
429
+
430
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string') {
431
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
432
+
433
+ if (observedPeerConnection) {
434
+ observedPeerConnection.closedAt = event.timestamp;
435
+ } else if (postBuffer) {
436
+ postBuffer.push(event);
437
+ } else {
438
+ logger.warn('Received PEER_CONNECTION_CLOSED event without a corresponding observedPeerConnection: %o', event);
439
+ }
440
+ }
441
+ break;
442
+ }
443
+ case ClientEventTypes.MEDIA_TRACK_ADDED: {
444
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
445
+
446
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
447
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
448
+ const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
449
+
450
+ if (observedTrack) {
451
+ observedTrack.addedAt = event.timestamp;
452
+ } else if (postBuffer) {
453
+ postBuffer.push(event);
454
+ } else {
455
+ logger.warn('Received MEDIA_TRACK_ADDED event without a corresponding observedPeerConnection or observedTrack: %o', event);
456
+ }
457
+ }
458
+ break;
459
+ }
460
+ case ClientEventTypes.MEDIA_TRACK_REMOVED: {
461
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
462
+
463
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
464
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
465
+ const observedTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId) ?? observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
466
+
467
+ if (observedTrack) {
468
+ observedTrack.removedAt = event.timestamp;
469
+ } else if (postBuffer) {
470
+ postBuffer.push(event);
471
+ } else {
472
+ logger.warn('Received MEDIA_TRACK_REMOVED event without a corresponding observedPeerConnection or observedTrack: %o', event);
473
+ }
474
+ }
475
+ break;
476
+ }
477
+ case ClientEventTypes.DATA_CHANNEL_OPEN: {
478
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
479
+
480
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.dataChannelId && typeof payload.dataChannelId === 'string') {
481
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
482
+ const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
483
+
484
+ if (observedDataChannel) {
485
+ observedDataChannel.addedAt = event.timestamp;
486
+ } else if (postBuffer) {
487
+ postBuffer.push(event);
488
+ } else {
489
+ logger.warn('Received DATA_CHANNEL_OPENED event without a corresponding observedPeerConnection or observedDataChannel: %o', event);
490
+ }
491
+ }
492
+ break;
493
+ }
494
+ case ClientEventTypes.DATA_CHANNEL_CLOSED: {
495
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
496
+
497
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.dataChannelId && typeof payload.dataChannelId === 'string') {
498
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
499
+ const observedDataChannel = observedPeerConnection?.observedDataChannels.get(payload.dataChannelId);
500
+
501
+ if (observedDataChannel) {
502
+ observedDataChannel.removedAt = event.timestamp;
503
+ } else if (postBuffer) {
504
+ postBuffer.push(event);
505
+ } else {
506
+ logger.warn('Received DATA_CHANNEL_CLOSE event without a corresponding observedPeerConnection or observedDataChannel: %o', event);
507
+ }
508
+ }
509
+ break;
510
+ }
511
+ case ClientEventTypes.MEDIA_TRACK_MUTED: {
512
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
513
+
514
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
515
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
516
+ const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
517
+ const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
518
+
519
+ if (observedPeerConnection) {
520
+ if (observedInboundTrack) {
521
+ observedInboundTrack.muted = true;
522
+ observedPeerConnection?.emit('muted-inbound-track', observedInboundTrack);
523
+ } else if (observedOutboundTrack) {
524
+ observedOutboundTrack.muted = true;
525
+ observedPeerConnection?.emit('muted-outbound-track', observedOutboundTrack);
526
+ }
527
+ } else if (postBuffer) {
528
+ postBuffer.push(event);
529
+ } else {
530
+ logger.warn('Received MEDIA_TRACK_MUTED event without a corresponding observedPeerConnection or observedTrack: %o', event);
531
+ }
532
+ }
533
+
534
+ break;
535
+ }
536
+ case ClientEventTypes.MEDIA_TRACK_UNMUTED: {
537
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
538
+
539
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.trackId && typeof payload.trackId === 'string') {
540
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
541
+ const observedInboundTrack = observedPeerConnection?.observedInboundTracks.get(payload.trackId);
542
+ const observedOutboundTrack = observedPeerConnection?.observedOutboundTracks.get(payload.trackId);
543
+
544
+ if (observedPeerConnection) {
545
+ if (observedInboundTrack) {
546
+ observedInboundTrack.muted = false;
547
+ observedPeerConnection?.emit('unmuted-inbound-track', observedInboundTrack);
548
+ } else if (observedOutboundTrack) {
549
+ observedOutboundTrack.muted = false;
550
+ observedPeerConnection?.emit('unmuted-outbound-track', observedOutboundTrack);
551
+ }
552
+ } else if (postBuffer) {
553
+ postBuffer.push(event);
554
+ } else {
555
+ logger.warn('Received MEDIA_TRACK_UNMUTED event without a corresponding observedPeerConnection or observedTrack: %o', event);
556
+ }
557
+ }
558
+ break;
559
+ }
560
+ case ClientEventTypes.ICE_CONNECTION_STATE_CHANGED: {
561
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
562
+
563
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.iceConnectionState && typeof payload.iceConnectionState === 'string') {
564
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
565
+
566
+ if (observedPeerConnection) {
567
+ observedPeerConnection.iceConnectionState = payload.iceConnectionState;
568
+ observedPeerConnection.emit('iceconnectionstatechange', {
569
+ state: payload.iceConnectionState,
570
+ });
571
+ } else if (postBuffer) {
572
+ postBuffer.push(event);
573
+ } else {
574
+ logger.warn('Received ICE_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
575
+ }
576
+ }
577
+ break;
578
+ }
579
+ case ClientEventTypes.ICE_GATHERING_STATE_CHANGED: {
580
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
581
+
582
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.iceGatheringState && typeof payload.iceGatheringState === 'string') {
583
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
584
+
585
+ if (observedPeerConnection) {
586
+ observedPeerConnection.iceGatheringState = payload.iceGatheringState;
587
+ observedPeerConnection.emit('icegatheringstatechange', {
588
+ state: payload.iceGatheringState,
589
+ });
590
+ } else if (postBuffer) {
591
+ postBuffer.push(event);
592
+ } else {
593
+ logger.warn('Received ICE_GATHERING_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
594
+ }
595
+ }
596
+ break;
597
+ }
598
+ case ClientEventTypes.PEER_CONNECTION_STATE_CHANGED: {
599
+ const payload = parseJsonAs<Record<string, unknown>>(event.payload);
600
+
601
+ if (payload?.peerConnectionId && typeof payload.peerConnectionId === 'string' && payload?.peerConnectionState && typeof payload.peerConnectionState === 'string') {
602
+ const observedPeerConnection = this.observedPeerConnections.get(payload.peerConnectionId);
603
+
604
+ if (observedPeerConnection) {
605
+ observedPeerConnection.connectionState = payload.peerConnectionState;
606
+ observedPeerConnection.emit('connectionstatechange', {
607
+ state: payload.peerConnectionState,
608
+ });
609
+ } else if (postBuffer) {
610
+ postBuffer.push(event);
611
+ } else {
612
+ logger.warn('Received PEER_CONNECTION_STATE_CHANGED event without a corresponding observedPeerConnection: %o', event);
613
+ }
356
614
  }
357
615
  break;
358
616
  }
359
617
  }
618
+
619
+ this.emit('clientEvent', event);
620
+ }
621
+
622
+ public injectMetaData(metaData: ClientMetaData) {
623
+ if (this.closed) return;
624
+
625
+ if (!this._injections.clientMetaItems) this._injections.clientMetaItems = [];
626
+
627
+ this._injections.clientMetaItems.push(metaData);
628
+ }
629
+
630
+ public injectEvent(event: ClientEvent) {
631
+ if (this.closed) return;
632
+
633
+ if (!this._injections.clientEvents) this._injections.clientEvents = [];
360
634
 
361
- this.call.observer.emit('client-event', this, event);
635
+ this._injections.clientEvents.push(event);
636
+ }
637
+
638
+ public injectIssue(issue: ClientIssue) {
639
+ if (this.closed) return;
640
+
641
+ if (!this._injections.clientIssues) this._injections.clientIssues = [];
642
+
643
+ this._injections.clientIssues.push(issue);
644
+ }
645
+
646
+ public injectExtensionStat(stat: ExtensionStat) {
647
+ if (this.closed) return;
648
+
649
+ if (!this._injections.extensionStats) this._injections.extensionStats = [];
650
+
651
+ this._injections.extensionStats.push(stat);
652
+ }
653
+
654
+ public injectAttachment(key: string, value: unknown) {
655
+ if (this.closed) return;
656
+
657
+ if (!this._injections.attachments) this._injections.attachments = {};
658
+
659
+ this._injections.attachments[key] = value;
660
+
362
661
  }
363
662
 
364
663
  public addMetadata(metadata: ClientMetaData) {
@@ -395,6 +694,7 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
395
694
 
396
695
  public addExtensionStats(stats: ExtensionStat) {
397
696
  this.call.observer.emit('client-extension-stats', this, stats);
697
+ this.emit('extensionStats', stats);
398
698
  }
399
699
 
400
700
  private _updatePeerConnection(sample: PeerConnectionSample): ObservedPeerConnection | undefined {
@@ -423,6 +723,47 @@ export class ObservedClient<AppData extends Record<string, unknown> = Record<str
423
723
  return observedPeerConnection;
424
724
  }
425
725
 
726
+ private _mergeInjections(sample: ClientSample): ClientSample {
727
+ if (this.closed) return sample;
728
+
729
+ if (this._injections.clientEvents) {
730
+ if (!sample.clientEvents) sample.clientEvents = [];
731
+ sample.clientEvents.push(...this._injections.clientEvents);
732
+
733
+ this._injections.clientEvents = undefined;
734
+ }
735
+
736
+ if (this._injections.clientIssues) {
737
+ if (!sample.clientIssues) sample.clientIssues = [];
738
+ sample.clientIssues.push(...this._injections.clientIssues);
739
+
740
+ this._injections.clientIssues = undefined;
741
+ }
742
+
743
+ if (this._injections.extensionStats) {
744
+ if (!sample.extensionStats) sample.extensionStats = [];
745
+ sample.extensionStats.push(...this._injections.extensionStats);
746
+
747
+ this._injections.extensionStats = undefined;
748
+ }
749
+
750
+ if (this._injections.attachments) {
751
+ if (!sample.attachments) sample.attachments = {};
752
+ Object.assign(sample.attachments, this._injections.attachments);
753
+
754
+ this._injections.attachments = undefined;
755
+ }
756
+
757
+ if (this._injections.clientMetaItems) {
758
+ if (!sample.clientMetaItems) sample.clientMetaItems = [];
759
+ sample.clientMetaItems.push(...this._injections.clientMetaItems);
760
+
761
+ this._injections.clientMetaItems = undefined;
762
+ }
763
+
764
+ return sample;
765
+ }
766
+
426
767
  // public resetSummaryMetrics() {
427
768
  // this.totalDataChannelBytesReceived = 0;
428
769
  // this.totalDataChannelBytesSent = 0;