@stream-io/video-client 0.0.13 → 0.0.15

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 (45) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/index.browser.es.js +237 -130
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +239 -129
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.es.js +237 -130
  8. package/dist/index.es.js.map +1 -1
  9. package/dist/src/Call.d.ts +2 -1
  10. package/dist/src/StreamSfuClient.d.ts +1 -0
  11. package/dist/src/StreamVideoClient.d.ts +5 -1
  12. package/dist/src/coordinator/connection/types.d.ts +3 -2
  13. package/dist/src/coordinator/connection/utils.d.ts +2 -1
  14. package/dist/src/logger.d.ts +4 -0
  15. package/dist/src/rtc/Dispatcher.d.ts +2 -0
  16. package/dist/src/rtc/IceTrickleBuffer.d.ts +2 -0
  17. package/dist/src/rtc/publisher.d.ts +1 -0
  18. package/dist/src/store/CallState.d.ts +2 -0
  19. package/dist/src/types.d.ts +1 -1
  20. package/index.ts +1 -0
  21. package/package.json +1 -1
  22. package/src/Call.ts +69 -42
  23. package/src/StreamSfuClient.ts +70 -29
  24. package/src/StreamVideoClient.ts +46 -3
  25. package/src/coordinator/connection/client.ts +22 -29
  26. package/src/coordinator/connection/connection.ts +2 -3
  27. package/src/coordinator/connection/connection_fallback.ts +0 -1
  28. package/src/coordinator/connection/types.ts +4 -2
  29. package/src/coordinator/connection/utils.ts +5 -2
  30. package/src/devices/devices.ts +10 -3
  31. package/src/events/__tests__/call-permissions.test.ts +2 -2
  32. package/src/events/call.ts +11 -4
  33. package/src/events/sessions.ts +7 -2
  34. package/src/logger.ts +45 -0
  35. package/src/rtc/Dispatcher.ts +14 -4
  36. package/src/rtc/IceTrickleBuffer.ts +8 -1
  37. package/src/rtc/__tests__/publisher.test.ts +1 -1
  38. package/src/rtc/codecs.ts +7 -5
  39. package/src/rtc/flows/join.ts +4 -1
  40. package/src/rtc/publisher.ts +31 -12
  41. package/src/rtc/signal.ts +8 -7
  42. package/src/rtc/subscriber.ts +16 -10
  43. package/src/stats/state-store-stats-reporter.ts +12 -4
  44. package/src/store/CallState.ts +7 -2
  45. package/src/types.ts +3 -2
package/dist/index.cjs.js CHANGED
@@ -4250,7 +4250,7 @@ exports.DebounceType = void 0;
4250
4250
  DebounceType[DebounceType["SLOW"] = 1200] = "SLOW";
4251
4251
  })(exports.DebounceType || (exports.DebounceType = {}));
4252
4252
  const isStreamVideoLocalParticipant = (p) => {
4253
- return !!p.isLoggedInUser;
4253
+ return !!p.isLocalParticipant;
4254
4254
  };
4255
4255
 
4256
4256
  /******************************************************************************
@@ -5277,13 +5277,41 @@ const toggleDtx = (sdp, enable) => {
5277
5277
  return sdp;
5278
5278
  };
5279
5279
 
5280
+ let logger;
5281
+ const logToConsole = (logLevel, message, extraData, tags) => {
5282
+ let logMethod;
5283
+ if (logLevel === 'error') {
5284
+ logMethod = console.error;
5285
+ }
5286
+ else if (logLevel === 'warn') {
5287
+ logMethod = console.warn;
5288
+ }
5289
+ else {
5290
+ logMethod = console.log;
5291
+ }
5292
+ logMethod(logLevel, `${tags === null || tags === void 0 ? void 0 : tags.join(':')} - ${message}`, extraData ? extraData : '');
5293
+ };
5294
+ const setLogger = (l) => {
5295
+ logger = l;
5296
+ };
5297
+ const getLogger = (withTags) => {
5298
+ const loggerMethod = logger || (() => { });
5299
+ const result = (logLevel, messeage, extraData, tags) => {
5300
+ loggerMethod(logLevel, messeage, extraData, [
5301
+ ...(tags || []),
5302
+ ...(withTags || []),
5303
+ ]);
5304
+ };
5305
+ return result;
5306
+ };
5307
+
5280
5308
  const getPreferredCodecs = (kind, preferredCodec, codecToRemove) => {
5309
+ const logger = getLogger(['codecs']);
5281
5310
  if (!('getCapabilities' in RTCRtpSender)) {
5282
- console.warn('RTCRtpSender.getCapabilities is not supported');
5311
+ logger === null || logger === void 0 ? void 0 : logger('warn', 'RTCRtpSender.getCapabilities is not supported');
5283
5312
  return;
5284
5313
  }
5285
5314
  const cap = RTCRtpSender.getCapabilities(kind);
5286
- console.log('s4e');
5287
5315
  if (!cap)
5288
5316
  return;
5289
5317
  const matched = [];
@@ -5291,7 +5319,7 @@ const getPreferredCodecs = (kind, preferredCodec, codecToRemove) => {
5291
5319
  const unmatched = [];
5292
5320
  cap.codecs.forEach((c) => {
5293
5321
  const codec = c.mimeType.toLowerCase();
5294
- console.log(c);
5322
+ logger === null || logger === void 0 ? void 0 : logger('debug', `Found supported codec: ${codec}`);
5295
5323
  const shouldRemoveCodec = codecToRemove && codec === `${kind}/${codecToRemove}`;
5296
5324
  if (shouldRemoveCodec)
5297
5325
  return;
@@ -5311,10 +5339,11 @@ const getPreferredCodecs = (kind, preferredCodec, codecToRemove) => {
5311
5339
  }
5312
5340
  return;
5313
5341
  }
5314
- console.log('matched', matched);
5315
5342
  matched.push(c);
5316
5343
  });
5317
- return [...matched, ...partialMatched, ...unmatched];
5344
+ const result = [...matched, ...partialMatched, ...unmatched];
5345
+ logger === null || logger === void 0 ? void 0 : logger('info', `Preffered codecs: `, result);
5346
+ return result;
5318
5347
  };
5319
5348
  const getGenericSdp = (direction, isRedEnabled, preferredVideoCodec) => __awaiter(void 0, void 0, void 0, function* () {
5320
5349
  var _a;
@@ -5380,17 +5409,19 @@ class Dispatcher {
5380
5409
  constructor() {
5381
5410
  this.subscribers = {};
5382
5411
  this.dispatch = (message) => {
5412
+ var _a, _b;
5383
5413
  const eventKind = message.eventPayload.oneofKind;
5384
5414
  if (eventKind) {
5385
- // @ts-ignore
5386
- console.log(`Dispatching`, eventKind, message.eventPayload[eventKind]);
5415
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.call(this, 'info', `Dispatching ${eventKind}`);
5416
+ (_b = this.logger) === null || _b === void 0 ? void 0 : _b.call(this, 'debug', `Event payload`, message.eventPayload[eventKind]);
5387
5417
  const listeners = this.subscribers[eventKind];
5388
5418
  listeners === null || listeners === void 0 ? void 0 : listeners.forEach((fn) => {
5419
+ var _a;
5389
5420
  try {
5390
5421
  fn(message);
5391
5422
  }
5392
5423
  catch (e) {
5393
- console.warn(`Listener failed with error`, e);
5424
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.call(this, 'warn', 'Listener failed with error', e);
5394
5425
  }
5395
5426
  });
5396
5427
  }
@@ -5414,6 +5445,7 @@ class Dispatcher {
5414
5445
  this.subscribers = {};
5415
5446
  }
5416
5447
  };
5448
+ this.logger = getLogger(['sfu-client']);
5417
5449
  }
5418
5450
  }
5419
5451
 
@@ -5426,6 +5458,7 @@ class IceTrickleBuffer {
5426
5458
  this.subscriberCandidates = new rxjs.ReplaySubject();
5427
5459
  this.publisherCandidates = new rxjs.ReplaySubject();
5428
5460
  this.push = (iceTrickle) => {
5461
+ var _a;
5429
5462
  if (iceTrickle.peerType === PeerType.SUBSCRIBER) {
5430
5463
  this.subscriberCandidates.next(iceTrickle);
5431
5464
  }
@@ -5433,9 +5466,10 @@ class IceTrickleBuffer {
5433
5466
  this.publisherCandidates.next(iceTrickle);
5434
5467
  }
5435
5468
  else {
5436
- console.warn(`ICETrickle, Unknown peer type`, iceTrickle);
5469
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.call(this, 'warn', `ICETrickle, Unknown peer type`, iceTrickle);
5437
5470
  }
5438
5471
  };
5472
+ this.logger = getLogger(['sfu-client']);
5439
5473
  }
5440
5474
  }
5441
5475
 
@@ -5626,6 +5660,7 @@ class Publisher {
5626
5660
  * @param opts
5627
5661
  */
5628
5662
  this.publishStream = (mediaStream, track, trackType, opts = {}) => __awaiter(this, void 0, void 0, function* () {
5663
+ var _a;
5629
5664
  let transceiver = this.publisher
5630
5665
  .getTransceivers()
5631
5666
  .find((t) => {
@@ -5639,7 +5674,8 @@ class Publisher {
5639
5674
  * Once the track has ended, it will notify the SFU and update the state.
5640
5675
  */
5641
5676
  const handleTrackEnded = () => __awaiter(this, void 0, void 0, function* () {
5642
- console.log(`Track ${TrackType[trackType]} has ended, notifying the SFU`);
5677
+ var _b;
5678
+ (_b = this.logger) === null || _b === void 0 ? void 0 : _b.call(this, 'info', `Track ${TrackType[trackType]} has ended, notifying the SFU`);
5643
5679
  yield this.notifyTrackMuteStateChanged(mediaStream, track, trackType, true);
5644
5680
  // clean-up, this event listener needs to run only once.
5645
5681
  track.removeEventListener('ended', handleTrackEnded);
@@ -5664,7 +5700,7 @@ class Publisher {
5664
5700
  });
5665
5701
  this.transceiverRegistry[trackType] = transceiver;
5666
5702
  if ('setCodecPreferences' in transceiver && codecPreferences) {
5667
- console.log(`Setting ${TrackType[trackType]} codec preferences`, codecPreferences);
5703
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.call(this, 'info', `Setting ${TrackType[trackType]} codec preferences`, codecPreferences);
5668
5704
  transceiver.setCodecPreferences(codecPreferences);
5669
5705
  }
5670
5706
  }
@@ -5748,9 +5784,9 @@ class Publisher {
5748
5784
  this.publisher.close();
5749
5785
  };
5750
5786
  this.updateVideoPublishQuality = (enabledRids) => __awaiter(this, void 0, void 0, function* () {
5751
- var _a;
5752
- console.log('Update publish quality, requested rids by SFU:', enabledRids);
5753
- const videoSender = (_a = this.transceiverRegistry[TrackType.VIDEO]) === null || _a === void 0 ? void 0 : _a.sender;
5787
+ var _c, _d, _e, _f;
5788
+ (_c = this.logger) === null || _c === void 0 ? void 0 : _c.call(this, 'info', 'Update publish quality, requested rids by SFU:', enabledRids);
5789
+ const videoSender = (_d = this.transceiverRegistry[TrackType.VIDEO]) === null || _d === void 0 ? void 0 : _d.sender;
5754
5790
  if (!videoSender)
5755
5791
  return;
5756
5792
  const params = videoSender.getParameters();
@@ -5765,10 +5801,10 @@ class Publisher {
5765
5801
  });
5766
5802
  if (changed) {
5767
5803
  if (params.encodings.length === 0) {
5768
- console.warn('No suitable video encoding quality found');
5804
+ (_e = this.logger) === null || _e === void 0 ? void 0 : _e.call(this, 'warn', 'No suitable video encoding quality found');
5769
5805
  }
5770
5806
  yield videoSender.setParameters(params);
5771
- console.log(`Update publish quality, enabled rids: ${params.encodings
5807
+ (_f = this.logger) === null || _f === void 0 ? void 0 : _f.call(this, 'info', `Update publish quality, enabled rids: ${params.encodings
5772
5808
  .filter((e) => e.active)
5773
5809
  .map((e) => e.rid)
5774
5810
  .join(', ')}`);
@@ -5785,9 +5821,10 @@ class Publisher {
5785
5821
  }
5786
5822
  };
5787
5823
  this.onIceCandidate = (e) => __awaiter(this, void 0, void 0, function* () {
5824
+ var _g;
5788
5825
  const { candidate } = e;
5789
5826
  if (!candidate) {
5790
- console.log('null ice candidate');
5827
+ (_g = this.logger) === null || _g === void 0 ? void 0 : _g.call(this, 'warn', 'null ice candidate');
5791
5828
  return;
5792
5829
  }
5793
5830
  yield this.sfuClient.iceTrickle({
@@ -5796,7 +5833,8 @@ class Publisher {
5796
5833
  });
5797
5834
  });
5798
5835
  this.onNegotiationNeeded = () => __awaiter(this, void 0, void 0, function* () {
5799
- console.log('AAA onNegotiationNeeded');
5836
+ var _h, _j;
5837
+ (_h = this.logger) === null || _h === void 0 ? void 0 : _h.call(this, 'info', 'AAA onNegotiationNeeded');
5800
5838
  const offer = yield this.publisher.createOffer();
5801
5839
  let sdp = offer.sdp;
5802
5840
  if (sdp) {
@@ -5859,28 +5897,38 @@ class Publisher {
5859
5897
  });
5860
5898
  }
5861
5899
  catch (e) {
5862
- console.error(`Publisher: setRemoteDescription error`, response.sdp, e);
5900
+ (_j = this.logger) === null || _j === void 0 ? void 0 : _j.call(this, 'error', `Publisher: setRemoteDescription error`, {
5901
+ sdp: response.sdp,
5902
+ error: e,
5903
+ });
5863
5904
  }
5864
5905
  this.sfuClient.iceTrickleBuffer.publisherCandidates.subscribe((candidate) => __awaiter(this, void 0, void 0, function* () {
5906
+ var _k;
5865
5907
  try {
5866
5908
  const iceCandidate = JSON.parse(candidate.iceCandidate);
5867
5909
  yield this.publisher.addIceCandidate(iceCandidate);
5868
5910
  }
5869
5911
  catch (e) {
5870
- console.error(`Publisher: ICE candidate error`, e, candidate);
5912
+ (_k = this.logger) === null || _k === void 0 ? void 0 : _k.call(this, 'error', `Publisher: ICE candidate error`, {
5913
+ error: e,
5914
+ candidate,
5915
+ });
5871
5916
  }
5872
5917
  }));
5873
5918
  });
5874
5919
  this.onIceCandidateError = (e) => {
5920
+ var _a;
5875
5921
  const errorMessage = e instanceof RTCPeerConnectionIceErrorEvent &&
5876
5922
  `${e.errorCode}: ${e.errorText}`;
5877
- console.error(`Publisher: ICE Candidate error`, errorMessage);
5923
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.call(this, 'error', `Publisher: ICE Candidate error`, errorMessage);
5878
5924
  };
5879
5925
  this.onIceConnectionStateChange = () => {
5880
- console.log(`Publisher: ICE Connection state changed`, this.publisher.iceConnectionState);
5926
+ var _a;
5927
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.call(this, 'error', `Publisher: ICE Connection state changed`, this.publisher.iceConnectionState);
5881
5928
  };
5882
5929
  this.onIceGatheringStateChange = () => {
5883
- console.log(`Publisher: ICE Gathering State`, this.publisher.iceGatheringState);
5930
+ var _a;
5931
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.call(this, 'error', `Publisher: ICE Gathering State`, this.publisher.iceGatheringState);
5884
5932
  };
5885
5933
  this.ridToVideoQuality = (rid) => {
5886
5934
  return rid === 'q'
@@ -5913,12 +5961,13 @@ class Publisher {
5913
5961
  }
5914
5962
 
5915
5963
  const createSubscriber = ({ sfuClient, dispatcher, connectionConfig, onTrack, }) => {
5964
+ const logger = getLogger(['sfu-client']);
5916
5965
  const subscriber = new RTCPeerConnection(connectionConfig);
5917
5966
  attachDebugEventListeners(subscriber);
5918
5967
  subscriber.addEventListener('icecandidate', (e) => __awaiter(void 0, void 0, void 0, function* () {
5919
5968
  const { candidate } = e;
5920
5969
  if (!candidate) {
5921
- console.log('null ice candidate');
5970
+ logger === null || logger === void 0 ? void 0 : logger('warn', 'null ice candidate');
5922
5971
  return;
5923
5972
  }
5924
5973
  yield sfuClient.iceTrickle({
@@ -5934,7 +5983,7 @@ const createSubscriber = ({ sfuClient, dispatcher, connectionConfig, onTrack, })
5934
5983
  if (message.eventPayload.oneofKind !== 'subscriberOffer')
5935
5984
  return;
5936
5985
  const { subscriberOffer } = message.eventPayload;
5937
- console.log(`Received subscriberOffer`, subscriberOffer);
5986
+ logger === null || logger === void 0 ? void 0 : logger('info', 'Received subscriberOffer', subscriberOffer);
5938
5987
  yield subscriber.setRemoteDescription({
5939
5988
  type: 'offer',
5940
5989
  sdp: subscriberOffer.sdp,
@@ -5945,7 +5994,10 @@ const createSubscriber = ({ sfuClient, dispatcher, connectionConfig, onTrack, })
5945
5994
  yield subscriber.addIceCandidate(iceCandidate);
5946
5995
  }
5947
5996
  catch (e) {
5948
- console.error(`Subscriber: ICE candidate error`, e, candidate);
5997
+ logger === null || logger === void 0 ? void 0 : logger('error', `Subscriber: ICE candidate error`, {
5998
+ error: e,
5999
+ candidate,
6000
+ });
5949
6001
  }
5950
6002
  }));
5951
6003
  // apply ice candidates
@@ -5968,31 +6020,33 @@ const createSubscriber = ({ sfuClient, dispatcher, connectionConfig, onTrack, })
5968
6020
  return subscriber;
5969
6021
  };
5970
6022
  const attachDebugEventListeners = (subscriber) => {
6023
+ const logger = getLogger(['sfu-client']);
5971
6024
  subscriber.addEventListener('icecandidateerror', (e) => {
5972
6025
  const errorMessage = e instanceof RTCPeerConnectionIceErrorEvent &&
5973
6026
  `${e.errorCode}: ${e.errorText}`;
5974
- console.error(`Subscriber: ICE Candidate error`, errorMessage);
6027
+ logger === null || logger === void 0 ? void 0 : logger('error', `Subscriber: ICE Candidate error: ${errorMessage}`);
5975
6028
  });
5976
6029
  subscriber.addEventListener('iceconnectionstatechange', () => {
5977
- console.log(`Subscriber: ICE Connection state changed`, subscriber.iceConnectionState);
6030
+ logger === null || logger === void 0 ? void 0 : logger('info', `Subscriber: ICE Connection state changed: ${subscriber.iceConnectionState}`);
5978
6031
  });
5979
6032
  subscriber.addEventListener('icegatheringstatechange', () => {
5980
- console.log(`Subscriber: ICE Gathering State`, subscriber.iceGatheringState);
6033
+ logger === null || logger === void 0 ? void 0 : logger('info', `Subscriber: ICE Gathering State: ${subscriber.iceGatheringState}`);
5981
6034
  });
5982
6035
  };
5983
6036
 
5984
6037
  const createWebSocketSignalChannel = (opts) => {
6038
+ const logger = getLogger(['sfu-client']);
5985
6039
  const { endpoint, onMessage } = opts;
5986
6040
  const ws = new WebSocket(endpoint);
5987
6041
  ws.binaryType = 'arraybuffer'; // do we need this?
5988
6042
  ws.addEventListener('error', (e) => {
5989
- console.log('Signaling WS channel error', e);
6043
+ logger === null || logger === void 0 ? void 0 : logger('error', 'Signaling WS channel error', e);
5990
6044
  });
5991
6045
  ws.addEventListener('close', (e) => {
5992
- console.log('Signaling WS channel is closed', e);
6046
+ logger === null || logger === void 0 ? void 0 : logger('info', 'Signaling WS channel is closed', e);
5993
6047
  });
5994
6048
  ws.addEventListener('open', (e) => {
5995
- console.log('Signaling WS channel is open', e);
6049
+ logger === null || logger === void 0 ? void 0 : logger('info', 'Signaling WS channel is open', e);
5996
6050
  });
5997
6051
  if (onMessage) {
5998
6052
  ws.addEventListener('message', (e) => {
@@ -6003,7 +6057,7 @@ const createWebSocketSignalChannel = (opts) => {
6003
6057
  onMessage(message);
6004
6058
  }
6005
6059
  catch (err) {
6006
- console.error('Failed to decode a message. Check whether the Proto models match.', e.data, e, err);
6060
+ logger === null || logger === void 0 ? void 0 : logger('error', 'Failed to decode a message. Check whether the Proto models match.', { event: e, error: err });
6007
6061
  }
6008
6062
  });
6009
6063
  }
@@ -6104,14 +6158,14 @@ function convertErrorToJson(err) {
6104
6158
  * isOnline safely return the navigator.online value for browser env
6105
6159
  * if navigator is not in global object, it always return true
6106
6160
  */
6107
- function isOnline() {
6161
+ function isOnline(logger) {
6108
6162
  const nav = typeof navigator !== 'undefined'
6109
6163
  ? navigator
6110
6164
  : typeof window !== 'undefined' && window.navigator
6111
6165
  ? window.navigator
6112
6166
  : undefined;
6113
6167
  if (!nav) {
6114
- console.warn('isOnline failed to access window.navigator and assume browser is online');
6168
+ logger('warn', 'isOnline failed to access window.navigator and assume browser is online');
6115
6169
  return true;
6116
6170
  }
6117
6171
  // RN navigator has undefined for onLine
@@ -6167,16 +6221,16 @@ class StreamSfuClient {
6167
6221
  return retryable(() => this.rpc.updateSubscriptions({
6168
6222
  sessionId: this.sessionId,
6169
6223
  tracks: subscriptions,
6170
- }));
6224
+ }), this.logger);
6171
6225
  });
6172
6226
  this.setPublisher = (data) => __awaiter(this, void 0, void 0, function* () {
6173
- return retryable(() => this.rpc.setPublisher(Object.assign(Object.assign({}, data), { sessionId: this.sessionId })));
6227
+ return retryable(() => this.rpc.setPublisher(Object.assign(Object.assign({}, data), { sessionId: this.sessionId })), this.logger);
6174
6228
  });
6175
6229
  this.sendAnswer = (data) => __awaiter(this, void 0, void 0, function* () {
6176
- return retryable(() => this.rpc.sendAnswer(Object.assign(Object.assign({}, data), { sessionId: this.sessionId })));
6230
+ return retryable(() => this.rpc.sendAnswer(Object.assign(Object.assign({}, data), { sessionId: this.sessionId })), this.logger);
6177
6231
  });
6178
6232
  this.iceTrickle = (data) => __awaiter(this, void 0, void 0, function* () {
6179
- return retryable(() => this.rpc.iceTrickle(Object.assign(Object.assign({}, data), { sessionId: this.sessionId })));
6233
+ return retryable(() => this.rpc.iceTrickle(Object.assign(Object.assign({}, data), { sessionId: this.sessionId })), this.logger);
6180
6234
  });
6181
6235
  this.updateMuteState = (trackType, muted) => __awaiter(this, void 0, void 0, function* () {
6182
6236
  return this.updateMuteStates({
@@ -6189,7 +6243,7 @@ class StreamSfuClient {
6189
6243
  });
6190
6244
  });
6191
6245
  this.updateMuteStates = (data) => __awaiter(this, void 0, void 0, function* () {
6192
- return retryable(() => this.rpc.updateMuteStates(Object.assign(Object.assign({}, data), { sessionId: this.sessionId })));
6246
+ return retryable(() => this.rpc.updateMuteStates(Object.assign(Object.assign({}, data), { sessionId: this.sessionId })), this.logger);
6193
6247
  });
6194
6248
  this.join = (data) => __awaiter(this, void 0, void 0, function* () {
6195
6249
  const joinRequest = JoinRequest.create(Object.assign(Object.assign({}, data), { sessionId: this.sessionId, token: this.token }));
@@ -6210,7 +6264,7 @@ class StreamSfuClient {
6210
6264
  clearInterval(this.keepAliveInterval);
6211
6265
  }
6212
6266
  this.keepAliveInterval = setInterval(() => {
6213
- console.log('Sending healthCheckRequest to SFU');
6267
+ this.logger('info', 'Sending healthCheckRequest to SFU');
6214
6268
  const message = SfuRequest.create({
6215
6269
  requestPayload: {
6216
6270
  oneofKind: 'healthCheckRequest',
@@ -6228,7 +6282,7 @@ class StreamSfuClient {
6228
6282
  if (this.lastMessageTimestamp) {
6229
6283
  const timeSinceLastMessage = new Date().getTime() - this.lastMessageTimestamp.getTime();
6230
6284
  if (timeSinceLastMessage > this.unhealthyTimeoutInMs) {
6231
- console.log('SFU connection unhealthy, closing');
6285
+ this.logger('error', 'SFU connection unhealthy, closing');
6232
6286
  this.close(4001, `SFU connection unhealthy. Didn't receive any healthcheck messages for ${this.unhealthyTimeoutInMs}ms`);
6233
6287
  }
6234
6288
  }
@@ -6236,12 +6290,22 @@ class StreamSfuClient {
6236
6290
  };
6237
6291
  this.sessionId = sessionId || generateUUIDv4();
6238
6292
  this.token = token;
6293
+ this.logger = getLogger(['sfu-client']);
6294
+ const logger = this.logger;
6295
+ const logInterceptor = {
6296
+ interceptUnary(next, method, input, options) {
6297
+ logger('info', `Calling SFU RPC method ${method.name}`);
6298
+ logger('debug', `Method call payload`, { input, options });
6299
+ return next(method, input, options);
6300
+ },
6301
+ };
6239
6302
  this.rpc = createSignalClient({
6240
6303
  baseUrl: url,
6241
6304
  interceptors: [
6242
6305
  withHeaders({
6243
6306
  Authorization: `Bearer ${token}`,
6244
6307
  }),
6308
+ logInterceptor,
6245
6309
  ],
6246
6310
  });
6247
6311
  // Special handling for the ICETrickle kind of events.
@@ -6283,7 +6347,7 @@ const MAX_RETRIES = 5;
6283
6347
  * @param <I> the type of the request object.
6284
6348
  * @param <O> the type of the response object.
6285
6349
  */
6286
- const retryable = (rpc) => __awaiter(void 0, void 0, void 0, function* () {
6350
+ const retryable = (rpc, logger) => __awaiter(void 0, void 0, void 0, function* () {
6287
6351
  var _a;
6288
6352
  let retryAttempt = 0;
6289
6353
  let rpcCallResult;
@@ -6293,9 +6357,11 @@ const retryable = (rpc) => __awaiter(void 0, void 0, void 0, function* () {
6293
6357
  yield sleep(retryInterval(retryAttempt));
6294
6358
  }
6295
6359
  rpcCallResult = yield rpc();
6360
+ logger('info', `SFU RPC response received for ${rpcCallResult.method.name}`);
6361
+ logger('debug', `Response payload`, rpcCallResult);
6296
6362
  // if the RPC call failed, log the error and retry
6297
6363
  if (rpcCallResult.response.error) {
6298
- console.error('SFU Error:', rpcCallResult.response.error);
6364
+ logger('error', 'SFU RPC Error:', rpcCallResult.response.error);
6299
6365
  }
6300
6366
  retryAttempt++;
6301
6367
  } while (((_a = rpcCallResult.response.error) === null || _a === void 0 ? void 0 : _a.shouldRetry) &&
@@ -6978,7 +7044,7 @@ class CallState {
6978
7044
  this.updateParticipant = (sessionId, patch) => {
6979
7045
  const participant = this.findParticipantBySessionId(sessionId);
6980
7046
  if (!participant) {
6981
- console.warn(`Participant with sessionId ${sessionId} not found`);
7047
+ this.logger('warn', `Participant with sessionId ${sessionId} not found`);
6982
7048
  return;
6983
7049
  }
6984
7050
  const thePatch = typeof patch === 'function' ? patch(participant) : patch;
@@ -7027,9 +7093,10 @@ class CallState {
7027
7093
  return p;
7028
7094
  }));
7029
7095
  };
7096
+ this.logger = getLogger(['call-state']);
7030
7097
  this.participants$ = this.participantsSubject.pipe(operators.map((ps) => ps.sort(this.sortParticipantsBy)));
7031
7098
  this.localParticipant$ = this.participants$.pipe(operators.map((participants) => participants.find(isStreamVideoLocalParticipant)));
7032
- this.remoteParticipants$ = this.participants$.pipe(operators.map((participants) => participants.filter((p) => !p.isLoggedInUser)));
7099
+ this.remoteParticipants$ = this.participants$.pipe(operators.map((participants) => participants.filter((p) => !p.isLocalParticipant)));
7033
7100
  this.pinnedParticipants$ = this.participants$.pipe(operators.map((participants) => participants.filter((p) => p.pinnedAt)));
7034
7101
  this.dominantSpeaker$ = this.participants$.pipe(operators.map((participants) => participants.find((p) => p.isDominantSpeaker)));
7035
7102
  this.hasOngoingScreenShare$ = this.participants$.pipe(operators.map((participants) => {
@@ -7183,13 +7250,13 @@ const watchCallRejected = (call) => {
7183
7250
  const { call: eventCall } = event;
7184
7251
  const { session: callSession } = eventCall;
7185
7252
  if (!callSession) {
7186
- console.log('No call session provided. Ignoring call.rejected event.');
7253
+ call.logger('warn', 'No call session provided. Ignoring call.rejected event.', event);
7187
7254
  return;
7188
7255
  }
7189
7256
  const rejectedBy = callSession.rejected_by;
7190
7257
  const { members, callingState } = call.state;
7191
7258
  if (callingState !== exports.CallingState.RINGING) {
7192
- console.log('Call is not in ringing mode (it is either accepted or rejected already). Ignoring call.rejected event.');
7259
+ call.logger('warn', 'Call is not in ringing mode (it is either accepted or rejected already). Ignoring call.rejected event.', event);
7193
7260
  return;
7194
7261
  }
7195
7262
  if (call.isCreatedByMe) {
@@ -7197,13 +7264,13 @@ const watchCallRejected = (call) => {
7197
7264
  .filter((m) => m.user_id !== call.currentUserId)
7198
7265
  .every((m) => rejectedBy[m.user_id]);
7199
7266
  if (everyoneElseRejected) {
7200
- console.log('everyone rejected, leaving the call');
7267
+ call.logger('info', 'everyone rejected, leaving the call');
7201
7268
  yield call.leave();
7202
7269
  }
7203
7270
  }
7204
7271
  else {
7205
7272
  if (rejectedBy[eventCall.created_by.id]) {
7206
- console.log('call creator rejected, leaving call');
7273
+ call.logger('info', 'call creator rejected, leaving call');
7207
7274
  yield call.leave();
7208
7275
  }
7209
7276
  }
@@ -7611,7 +7678,7 @@ const watchCallSessionParticipantJoined = (state) => {
7611
7678
  const { user } = event;
7612
7679
  state.setMetadata((metadata) => {
7613
7680
  if (!metadata || !metadata.session) {
7614
- console.warn(`Received call.session_participant_joined event but the metadata structure is invalid.`);
7681
+ state.logger('warn', `Received call.session_participant_joined event but the metadata structure is invalid.`, event);
7615
7682
  return metadata;
7616
7683
  }
7617
7684
  const { session } = metadata;
@@ -7639,7 +7706,7 @@ const watchCallSessionParticipantLeft = (state) => {
7639
7706
  const { user } = event;
7640
7707
  state.setMetadata((metadata) => {
7641
7708
  if (!metadata || !metadata.session) {
7642
- console.warn(`Received call.session_participant_left event but the metadata structure is invalid.`);
7709
+ state.logger('warn', `Received call.session_participant_left event but the metadata structure is invalid.`, event);
7643
7710
  return metadata;
7644
7711
  }
7645
7712
  const { session } = metadata;
@@ -7784,16 +7851,18 @@ const getLocationHint = () => __awaiter(void 0, void 0, void 0, function* () {
7784
7851
  const hintURL = `https://hint.stream-io-video.com/`;
7785
7852
  const abortController = new AbortController();
7786
7853
  const timeoutId = setTimeout(() => abortController.abort(), 1000);
7854
+ const logger = getLogger(['call']);
7787
7855
  try {
7788
7856
  const response = yield fetch(hintURL, {
7789
7857
  method: 'HEAD',
7790
7858
  signal: abortController.signal,
7791
7859
  });
7792
7860
  const awsPop = response.headers.get('x-amz-cf-pop') || 'ERR';
7861
+ logger === null || logger === void 0 ? void 0 : logger('info', `Location header: ${awsPop}`);
7793
7862
  return awsPop.substring(0, 3); // AMS1-P2 -> AMS
7794
7863
  }
7795
7864
  catch (e) {
7796
- console.error(`Failed to get location hint from ${hintURL}`, e);
7865
+ logger === null || logger === void 0 ? void 0 : logger('error', `Failed to get location hint from ${hintURL}`, e);
7797
7866
  return 'ERR';
7798
7867
  }
7799
7868
  finally {
@@ -7832,6 +7901,7 @@ const getCascadingModeParams = () => {
7832
7901
  * Creates a new StatsReporter instance that collects metrics about the ongoing call and reports them to the state store
7833
7902
  */
7834
7903
  const createStatsReporter = ({ subscriber, publisher, state, edgeName, pollingIntervalInMs = 2000, }) => {
7904
+ const logger = getLogger(['stats']);
7835
7905
  const getRawStatsForTrack = (kind, selector) => __awaiter(void 0, void 0, void 0, function* () {
7836
7906
  if (kind === 'subscriber' && subscriber) {
7837
7907
  return subscriber.getStats(selector);
@@ -7840,7 +7910,7 @@ const createStatsReporter = ({ subscriber, publisher, state, edgeName, pollingIn
7840
7910
  return publisher.getStats(selector);
7841
7911
  }
7842
7912
  else {
7843
- console.warn(`Can't retrieve RTC stats for`, kind);
7913
+ logger('warn', `Can't retrieve RTC stats for ${kind}`);
7844
7914
  return undefined;
7845
7915
  }
7846
7916
  });
@@ -7878,7 +7948,9 @@ const createStatsReporter = ({ subscriber, publisher, state, edgeName, pollingIn
7878
7948
  for (let participant of state.participants) {
7879
7949
  if (!sessionIds.has(participant.sessionId))
7880
7950
  continue;
7881
- const kind = participant.isLoggedInUser ? 'publisher' : 'subscriber';
7951
+ const kind = participant.isLocalParticipant
7952
+ ? 'publisher'
7953
+ : 'subscriber';
7882
7954
  try {
7883
7955
  const mergedStream = new MediaStream([
7884
7956
  ...(((_a = participant.videoStream) === null || _a === void 0 ? void 0 : _a.getVideoTracks()) || []),
@@ -7890,7 +7962,7 @@ const createStatsReporter = ({ subscriber, publisher, state, edgeName, pollingIn
7890
7962
  });
7891
7963
  }
7892
7964
  catch (e) {
7893
- console.error(`Failed to collect stats for ${kind}`, participant, e);
7965
+ logger('error', `Failed to collect stats for ${kind} if ${participant.userId}`, e);
7894
7966
  }
7895
7967
  }
7896
7968
  }
@@ -7928,7 +8000,7 @@ const createStatsReporter = ({ subscriber, publisher, state, edgeName, pollingIn
7928
8000
  if (pollingIntervalInMs > 0) {
7929
8001
  const loop = () => __awaiter(void 0, void 0, void 0, function* () {
7930
8002
  yield run().catch((e) => {
7931
- console.log('Failed to collect stats', e);
8003
+ logger('warn', 'Failed to collect stats', e);
7932
8004
  });
7933
8005
  timeoutId = setTimeout(loop, pollingIntervalInMs);
7934
8006
  });
@@ -8471,6 +8543,9 @@ class Call {
8471
8543
  if ([exports.CallingState.JOINED, exports.CallingState.JOINING].includes(this.state.callingState)) {
8472
8544
  throw new Error(`Illegal State: Already joined.`);
8473
8545
  }
8546
+ if (this.state.callingState === exports.CallingState.LEFT) {
8547
+ throw new Error('Illegal State: Cannot join already left call. Create a new Call instance to join a call.');
8548
+ }
8474
8549
  const previousCallingState = this.state.callingState;
8475
8550
  this.state.setCallingState(exports.CallingState.JOINING);
8476
8551
  if ((data === null || data === void 0 ? void 0 : data.ring) && !this.ringing) {
@@ -8526,7 +8601,7 @@ class Call {
8526
8601
  */
8527
8602
  const rejoin = () => __awaiter(this, void 0, void 0, function* () {
8528
8603
  var _g, _h, _j;
8529
- console.log(`Rejoining call ${this.cid} (${this.reconnectAttempts})...`);
8604
+ this.logger('debug', `Rejoining call ${this.cid} (${this.reconnectAttempts})...`);
8530
8605
  this.reconnectAttempts++;
8531
8606
  this.state.setCallingState(exports.CallingState.RECONNECTING);
8532
8607
  // take a snapshot of the current "local participant" state
@@ -8538,7 +8613,7 @@ class Call {
8538
8613
  sfuClient.close(); // clean up previous connection
8539
8614
  yield sleep(retryInterval(this.reconnectAttempts));
8540
8615
  yield this.join(data);
8541
- console.log(`Rejoin: ${this.reconnectAttempts} successful!`);
8616
+ this.logger('info', `Rejoin: ${this.reconnectAttempts} successful!`);
8542
8617
  if (localParticipant && !isReactNative()) {
8543
8618
  const { audioStream, videoStream, screenShareStream: screenShare, } = localParticipant;
8544
8619
  // restore previous publishing state
@@ -8549,7 +8624,7 @@ class Call {
8549
8624
  if (screenShare)
8550
8625
  yield this.publishScreenShareStream(screenShare);
8551
8626
  }
8552
- console.log(`Rejoin: state restored ${this.reconnectAttempts}`);
8627
+ this.logger('info', `Rejoin: state restored ${this.reconnectAttempts}`);
8553
8628
  });
8554
8629
  this.rejoinPromise = rejoin;
8555
8630
  // reconnect if the connection was closed unexpectedly. example:
@@ -8569,12 +8644,12 @@ class Call {
8569
8644
  return;
8570
8645
  if (this.reconnectAttempts < this.maxReconnectAttempts) {
8571
8646
  rejoin().catch(() => {
8572
- console.log(`Rejoin failed for ${this.reconnectAttempts} times. Giving up.`);
8647
+ this.logger('error', `Rejoin failed for ${this.reconnectAttempts} times. Giving up.`);
8573
8648
  this.state.setCallingState(exports.CallingState.RECONNECTING_FAILED);
8574
8649
  });
8575
8650
  }
8576
8651
  else {
8577
- console.log('Reconnect attempts exceeded. Giving up...');
8652
+ this.logger('error', 'Reconnect attempts exceeded. Giving up...');
8578
8653
  this.state.setCallingState(exports.CallingState.RECONNECTING_FAILED);
8579
8654
  }
8580
8655
  });
@@ -8584,15 +8659,15 @@ class Call {
8584
8659
  if (typeof window !== 'undefined' && window.addEventListener) {
8585
8660
  const handleOnOffline = () => {
8586
8661
  window.removeEventListener('offline', handleOnOffline);
8587
- console.log('Join: Going offline...');
8662
+ this.logger('warn', 'Join: Going offline...');
8588
8663
  this.state.setCallingState(exports.CallingState.OFFLINE);
8589
8664
  };
8590
8665
  const handleOnOnline = () => {
8591
8666
  window.removeEventListener('online', handleOnOnline);
8592
8667
  if (this.state.callingState === exports.CallingState.OFFLINE) {
8593
- console.log('Join: Going online...');
8668
+ this.logger('info', 'Join: Going online...');
8594
8669
  rejoin().catch(() => {
8595
- console.log(`Rejoin failed for ${this.reconnectAttempts} times. Giving up.`);
8670
+ this.logger('error', `Rejoin failed for ${this.reconnectAttempts} times. Giving up.`);
8596
8671
  this.state.setCallingState(exports.CallingState.RECONNECTING_FAILED);
8597
8672
  });
8598
8673
  }
@@ -8649,15 +8724,20 @@ class Call {
8649
8724
  clientDetails.sdk = getSdkInfo();
8650
8725
  // 1. wait for the signal server to be ready before sending "joinRequest"
8651
8726
  sfuClient.signalReady
8652
- .catch((err) => console.warn('Signal ready failed', err))
8727
+ .catch((err) => this.logger('error', 'Signal ready failed', err))
8653
8728
  // prepare a generic SDP and send it to the SFU.
8654
8729
  // this is a throw-away SDP that the SFU will use to determine
8655
8730
  // the capabilities of the client (codec support, etc.)
8656
8731
  .then(() => getGenericSdp('recvonly', isRedEnabled, this.streamClient.options.preferredVideoCodec))
8657
- .then((sdp) => sfuClient.join({
8658
- subscriberSdp: sdp || '',
8659
- clientDetails,
8660
- }));
8732
+ .then((sdp) => {
8733
+ const joinRequest = {
8734
+ subscriberSdp: sdp || '',
8735
+ clientDetails,
8736
+ };
8737
+ this.logger('info', 'Sending join request to SFU');
8738
+ this.logger('debug', 'Join request payload', joinRequest);
8739
+ sfuClient.join(joinRequest);
8740
+ });
8661
8741
  // 2. in parallel, wait for the SFU to send us the "joinResponse"
8662
8742
  // this will throw an error if the SFU rejects the join request or
8663
8743
  // fails to respond in time
@@ -8667,22 +8747,22 @@ class Call {
8667
8747
  const startedAt = (callState === null || callState === void 0 ? void 0 : callState.startedAt)
8668
8748
  ? Timestamp.toDate(callState.startedAt)
8669
8749
  : new Date();
8670
- this.state.setParticipants(currentParticipants.map((participant) => (Object.assign(Object.assign({}, participant), { isLoggedInUser: participant.sessionId === sfuClient.sessionId, viewportVisibilityState: exports.VisibilityState.UNKNOWN }))));
8750
+ this.state.setParticipants(currentParticipants.map((participant) => (Object.assign(Object.assign({}, participant), { isLocalParticipant: participant.sessionId === sfuClient.sessionId, viewportVisibilityState: exports.VisibilityState.UNKNOWN }))));
8671
8751
  this.state.setParticipantCount((participantCount === null || participantCount === void 0 ? void 0 : participantCount.total) || 0);
8672
8752
  this.state.setAnonymousParticipantCount((participantCount === null || participantCount === void 0 ? void 0 : participantCount.anonymous) || 0);
8673
8753
  this.state.setStartedAt(startedAt);
8674
8754
  this.reconnectAttempts = 0; // reset the reconnect attempts counter
8675
8755
  this.state.setCallingState(exports.CallingState.JOINED);
8676
- console.log(`Joined call ${this.cid}`);
8756
+ this.logger('info', `Joined call ${this.cid}`);
8677
8757
  }
8678
8758
  catch (err) {
8679
8759
  // join failed, try to rejoin
8680
8760
  if (this.reconnectAttempts < this.maxReconnectAttempts) {
8681
8761
  yield rejoin();
8682
- console.log(`Rejoin ${this.reconnectAttempts} successful!`);
8762
+ this.logger('info', `Rejoin ${this.reconnectAttempts} successful!`);
8683
8763
  }
8684
8764
  else {
8685
- console.log(`Rejoin failed for ${this.reconnectAttempts} times. Giving up.`);
8765
+ this.logger('error', `Rejoin failed for ${this.reconnectAttempts} times. Giving up.`);
8686
8766
  this.state.setCallingState(exports.CallingState.RECONNECTING_FAILED);
8687
8767
  throw new Error('Join failed');
8688
8768
  }
@@ -8707,7 +8787,8 @@ class Call {
8707
8787
  }
8708
8788
  const [videoTrack] = videoStream.getVideoTracks();
8709
8789
  if (!videoTrack) {
8710
- return console.error(`There is no video track in the stream.`);
8790
+ this.logger('error', `There is no video track to publish in the stream.`);
8791
+ return;
8711
8792
  }
8712
8793
  yield this.publisher.publishStream(videoStream, videoTrack, TrackType.VIDEO, opts);
8713
8794
  });
@@ -8730,7 +8811,8 @@ class Call {
8730
8811
  }
8731
8812
  const [audioTrack] = audioStream.getAudioTracks();
8732
8813
  if (!audioTrack) {
8733
- return console.error(`There is no audio track in the stream`);
8814
+ this.logger('error', `There is no audio track in the stream to publish`);
8815
+ return;
8734
8816
  }
8735
8817
  yield this.publisher.publishStream(audioStream, audioTrack, TrackType.AUDIO);
8736
8818
  });
@@ -8752,7 +8834,8 @@ class Call {
8752
8834
  }
8753
8835
  const [screenShareTrack] = screenShareStream.getVideoTracks();
8754
8836
  if (!screenShareTrack) {
8755
- return console.error(`There is no video track in the stream`);
8837
+ this.logger('error', `There is no video track in the screen share stream to publish`);
8838
+ return;
8756
8839
  }
8757
8840
  yield this.publisher.publishStream(screenShareStream, screenShareTrack, TrackType.SCREEN_SHARE);
8758
8841
  });
@@ -8767,7 +8850,7 @@ class Call {
8767
8850
  */
8768
8851
  this.stopPublish = (trackType) => __awaiter(this, void 0, void 0, function* () {
8769
8852
  var _k;
8770
- console.log(`stopPublish`, TrackType[trackType]);
8853
+ this.logger('info', `stopPublish ${TrackType[trackType]}`);
8771
8854
  yield ((_k = this.publisher) === null || _k === void 0 ? void 0 : _k.unpublishStream(trackType));
8772
8855
  });
8773
8856
  /**
@@ -8801,7 +8884,7 @@ class Call {
8801
8884
  const subscriptions = [];
8802
8885
  participants.forEach((p) => {
8803
8886
  // we don't want to subscribe to our own tracks
8804
- if (p.isLoggedInUser)
8887
+ if (p.isLocalParticipant)
8805
8888
  return;
8806
8889
  // NOTE: audio tracks don't have to be requested explicitly
8807
8890
  // as the SFU will implicitly subscribe us to all of them,
@@ -8916,20 +8999,21 @@ class Call {
8916
8999
  const [primaryStream] = e.streams;
8917
9000
  // example: `e3f6aaf8-b03d-4911-be36-83f47d37a76a:TRACK_TYPE_VIDEO`
8918
9001
  const [trackId, trackType] = primaryStream.id.split(':');
8919
- console.log(`Got remote ${trackType} track:`, e.track);
9002
+ this.logger('info', `Got remote ${trackType} track:`);
9003
+ this.logger('debug', `Track: `, e.track);
8920
9004
  const participantToUpdate = this.state.participants.find((p) => p.trackLookupPrefix === trackId);
8921
9005
  if (!participantToUpdate) {
8922
- console.error('Received track for unknown participant', trackId, e);
9006
+ this.logger('error', `'Received track for unknown participant: ${trackId}'`, e);
8923
9007
  return;
8924
9008
  }
8925
9009
  e.track.addEventListener('mute', () => {
8926
- console.log(`Track muted:`, participantToUpdate.userId, `${trackType}:${trackId}`, e.track);
9010
+ this.logger('info', `Track muted: ${participantToUpdate.userId} ${trackType}:${trackId}`);
8927
9011
  });
8928
9012
  e.track.addEventListener('unmute', () => {
8929
- console.log(`Track unmuted:`, participantToUpdate.userId, `${trackType}:${trackId}`, e.track);
9013
+ this.logger('info', `Track unmuted: ${participantToUpdate.userId} ${trackType}:${trackId}`);
8930
9014
  });
8931
9015
  e.track.addEventListener('ended', () => {
8932
- console.log(`Track ended:`, participantToUpdate.userId, `${trackType}:${trackId}`, e.track);
9016
+ this.logger('info', `Track ended: ${participantToUpdate.userId} ${trackType}:${trackId}`);
8933
9017
  });
8934
9018
  const streamKindProp = {
8935
9019
  TRACK_TYPE_AUDIO: 'audioStream',
@@ -8937,12 +9021,12 @@ class Call {
8937
9021
  TRACK_TYPE_SCREEN_SHARE: 'screenShareStream',
8938
9022
  }[trackType];
8939
9023
  if (!streamKindProp) {
8940
- console.error('Unknown track type', trackType);
9024
+ this.logger('error', `Unknown track type: ${trackType}`);
8941
9025
  return;
8942
9026
  }
8943
9027
  const previousStream = participantToUpdate[streamKindProp];
8944
9028
  if (previousStream) {
8945
- console.log(`Cleaning up previous remote tracks`, e.track.kind);
9029
+ this.logger('info', `Cleaning up previous remote tracks: ${e.track.kind}`);
8946
9030
  previousStream.getTracks().forEach((t) => {
8947
9031
  t.stop();
8948
9032
  previousStream.removeTrack(t);
@@ -9242,6 +9326,7 @@ class Call {
9242
9326
  this.streamClient = streamClient;
9243
9327
  this.clientStore = clientStore;
9244
9328
  this.streamClientBasePath = `/call/${this.type}/${this.id}`;
9329
+ this.logger = getLogger(['call']);
9245
9330
  const callTypeConfig = CallTypes.get(type);
9246
9331
  const participantSorter = sortParticipantsBy || callTypeConfig.options.sortParticipantsBy;
9247
9332
  if (participantSorter) {
@@ -9279,7 +9364,7 @@ class Call {
9279
9364
  const hasPermission = this.permissionsContext.hasPermission(permission);
9280
9365
  if (!hasPermission && this.publisher.isPublishing(trackType)) {
9281
9366
  this.stopPublish(trackType).catch((err) => {
9282
- console.error('Error stopping publish', trackType, err);
9367
+ this.logger('error', `Error stopping publish ${trackType}`, err);
9283
9368
  });
9284
9369
  }
9285
9370
  }
@@ -9498,12 +9583,12 @@ class StableWSConnection {
9498
9583
  return;
9499
9584
  const user = this.client.user;
9500
9585
  if (!user) {
9501
- console.error(`User not set, can't connect to WS`);
9586
+ this.client.logger('error', `User not set, can't connect to WS`);
9502
9587
  return;
9503
9588
  }
9504
9589
  const token = this.client._getToken();
9505
9590
  if (!token) {
9506
- console.error(`Token not set, can't connect authenticate`);
9591
+ this.client.logger('error', `Token not set, can't connect authenticate`);
9507
9592
  return;
9508
9593
  }
9509
9594
  const authMessage = {
@@ -9736,7 +9821,7 @@ class StableWSConnection {
9736
9821
  addConnectionEventListeners(this.onlineStatusChanged);
9737
9822
  }
9738
9823
  _log(msg, extra = {}, level = 'info') {
9739
- this.client.logger(level, 'connection:' + msg, Object.assign({ tags: ['connection'] }, extra));
9824
+ this.client.logger(level, 'connection:' + msg, Object.assign({}, extra));
9740
9825
  }
9741
9826
  /**
9742
9827
  * connect - Connect to the WS URL
@@ -10456,7 +10541,7 @@ class WSConnectionFallback {
10456
10541
  addConnectionEventListeners(this._onlineStatusChanged);
10457
10542
  }
10458
10543
  _log(msg, extra = {}, level = 'info') {
10459
- this.client.logger(level, 'WSConnectionFallback:' + msg, Object.assign({ tags: ['connection_fallback', 'connection'] }, extra));
10544
+ this.client.logger(level, 'WSConnectionFallback:' + msg, Object.assign({}, extra));
10460
10545
  }
10461
10546
  _setState(state) {
10462
10547
  this._log(`_setState() - ${state}`);
@@ -10516,7 +10601,7 @@ class StreamClient {
10516
10601
  * If the user id remains the same we don't throw error
10517
10602
  */
10518
10603
  if (this.userID === user.id && this.setUserPromise) {
10519
- console.warn('Consecutive calls to connectUser is detected, ideally you should only call this function once in your app.');
10604
+ this.logger('warn', 'Consecutive calls to connectUser is detected, ideally you should only call this function once in your app.');
10520
10605
  return this.setUserPromise;
10521
10606
  }
10522
10607
  if (this.userID) {
@@ -10524,7 +10609,7 @@ class StreamClient {
10524
10609
  }
10525
10610
  if ((this._isUsingServerAuth() || this.node) &&
10526
10611
  !this.options.allowServerSideConnect) {
10527
- console.warn('Please do not use connectUser server side. connectUser impacts MAU and concurrent connection usage and thus your bill. If you have a valid use-case, add "allowServerSideConnect: true" to the client options to disable this warning.');
10612
+ this.logger('warn', 'Please do not use connectUser server side. connectUser impacts MAU and concurrent connection usage and thus your bill. If you have a valid use-case, add "allowServerSideConnect: true" to the client options to disable this warning.');
10528
10613
  }
10529
10614
  // we generate the client id client side
10530
10615
  this.userID = user.id;
@@ -10587,16 +10672,12 @@ class StreamClient {
10587
10672
  throw Error('UserWithId is not set on client, use client.connectUser or client.connectAnonymousUser instead');
10588
10673
  }
10589
10674
  if (((_d = this.wsConnection) === null || _d === void 0 ? void 0 : _d.isConnecting) && this.wsPromise) {
10590
- this.logger('info', 'client:openConnection() - connection already in progress', {
10591
- tags: ['connection', 'client'],
10592
- });
10675
+ this.logger('info', 'client:openConnection() - connection already in progress');
10593
10676
  return this.wsPromise;
10594
10677
  }
10595
10678
  if ((((_e = this.wsConnection) === null || _e === void 0 ? void 0 : _e.isHealthy) || ((_f = this.wsFallback) === null || _f === void 0 ? void 0 : _f.isHealthy())) &&
10596
10679
  this._hasConnectionID()) {
10597
- this.logger('info', 'client:openConnection() - openConnection called twice, healthy connection already exists', {
10598
- tags: ['connection', 'client'],
10599
- });
10680
+ this.logger('info', 'client:openConnection() - openConnection called twice, healthy connection already exists');
10600
10681
  return Promise.resolve();
10601
10682
  }
10602
10683
  this.clientID = `${this.userID}--${randomId()}`;
@@ -10619,9 +10700,7 @@ class StreamClient {
10619
10700
  * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
10620
10701
  */
10621
10702
  this.disconnectUser = (timeout) => __awaiter(this, void 0, void 0, function* () {
10622
- this.logger('info', 'client:disconnect() - Disconnecting the client', {
10623
- tags: ['connection', 'client'],
10624
- });
10703
+ this.logger('info', 'client:disconnect() - Disconnecting the client');
10625
10704
  // remove the user specific fields
10626
10705
  delete this.user;
10627
10706
  delete this._user;
@@ -10711,7 +10790,8 @@ class StreamClient {
10711
10790
  this.dispatchEvent = (event) => {
10712
10791
  if (!event.received_at)
10713
10792
  event.received_at = new Date();
10714
- console.log(`Dispatching event: ${event.type}`, event);
10793
+ this.logger('info', `Dispatching event: ${event.type}`);
10794
+ this.logger('debug', 'Event payload:', event);
10715
10795
  this._callClientListeners(event);
10716
10796
  };
10717
10797
  this.handleEvent = (messageEvent) => {
@@ -10844,9 +10924,7 @@ class StreamClient {
10844
10924
  if (!(key in this.listeners)) {
10845
10925
  this.listeners[key] = [];
10846
10926
  }
10847
- this.logger('info', `Attaching listener for ${key} event`, {
10848
- tags: ['event', 'client'],
10849
- });
10927
+ this.logger('info', `Attaching listener for ${key} event`);
10850
10928
  this.listeners[key].push(callback);
10851
10929
  return () => {
10852
10930
  this.off(key, callback);
@@ -10864,29 +10942,26 @@ class StreamClient {
10864
10942
  if (!(key in this.listeners)) {
10865
10943
  this.listeners[key] = [];
10866
10944
  }
10867
- this.logger('info', `Removing listener for ${key} event`, {
10868
- tags: ['event', 'client'],
10869
- });
10945
+ this.logger('info', `Removing listener for ${key} event`);
10870
10946
  this.listeners[key] = this.listeners[key].filter((value) => value !== callback);
10871
10947
  }
10872
10948
  _logApiRequest(type, url, data, config) {
10873
- this.logger('info', `client: ${type} - Request - ${url}`, {
10874
- tags: ['api', 'api_request', 'client'],
10875
- url,
10949
+ this.logger('info', `client: ${type} - Request - ${url}`);
10950
+ this.logger('debug', `client: ${type} - Request payload`, {
10876
10951
  payload: data,
10877
10952
  config,
10878
10953
  });
10879
10954
  }
10880
10955
  _logApiResponse(type, url, response) {
10881
10956
  this.logger('info', `client:${type} - Response - url: ${url} > status ${response.status}`, {
10882
- tags: ['api', 'api_response', 'client'],
10883
- url,
10957
+ response,
10958
+ });
10959
+ this.logger('debug', `client:${type} - Response payload`, {
10884
10960
  response,
10885
10961
  });
10886
10962
  }
10887
10963
  _logApiError(type, url, error) {
10888
10964
  this.logger('error', `client:${type} - Error - url: ${url}`, {
10889
- tags: ['api', 'api_response', 'client'],
10890
10965
  url,
10891
10966
  error,
10892
10967
  });
@@ -10961,7 +11036,7 @@ class StreamClient {
10961
11036
  if (this.wsFallback) {
10962
11037
  return yield this.wsFallback.connect();
10963
11038
  }
10964
- console.log('StreamClient.connect: this.wsConnection.connect()');
11039
+ this.logger('info', 'StreamClient.connect: this.wsConnection.connect()');
10965
11040
  // if WSFallback is enabled, ws connect should timeout faster so fallback can try
10966
11041
  return yield this.wsConnection.connect(this.options.enableWSFallback
10967
11042
  ? this.defaultWSTimeoutWithFallback
@@ -10970,9 +11045,11 @@ class StreamClient {
10970
11045
  catch (err) {
10971
11046
  // run fallback only if it's WS/Network error and not a normal API error
10972
11047
  // make sure browser is online before even trying the longpoll
10973
- // @ts-ignore
10974
- if (this.options.enableWSFallback && isWSFailure(err) && isOnline()) {
10975
- this.logger('info', 'client:connect() - WS failed, fallback to longpoll', { tags: ['connection', 'client'] });
11048
+ if (this.options.enableWSFallback &&
11049
+ // @ts-ignore
11050
+ isWSFailure(err) &&
11051
+ isOnline(this.logger)) {
11052
+ this.logger('warn', 'client:connect() - WS failed, fallback to longpoll');
10976
11053
  this.dispatchEvent({ type: 'transport.changed', mode: 'longpoll' });
10977
11054
  this.wsConnection._destroyCurrentWSConnection();
10978
11055
  this.wsConnection.disconnect().then(); // close WS so no retry
@@ -11007,7 +11084,7 @@ class StreamClient {
11007
11084
  }
11008
11085
  getUserAgent() {
11009
11086
  return (this.userAgent ||
11010
- `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.12"}`);
11087
+ `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.14"}`);
11011
11088
  }
11012
11089
  setUserAgent(userAgent) {
11013
11090
  this.userAgent = userAgent;
@@ -11076,7 +11153,10 @@ class StreamClient {
11076
11153
  */
11077
11154
  class StreamVideoClient {
11078
11155
  constructor(apiKeyOrArgs, opts) {
11156
+ var _a, _b;
11157
+ this.logLevel = 'warn';
11079
11158
  this.eventHandlersToUnregister = [];
11159
+ this.logLevels = ['debug', 'info', 'warn', 'error'];
11080
11160
  /**
11081
11161
  * Disconnects the currently connected user from the client.
11082
11162
  *
@@ -11235,11 +11315,30 @@ class StreamVideoClient {
11235
11315
  this.connectionPromise.finally(() => (this.connectionPromise = undefined));
11236
11316
  return this.connectionPromise;
11237
11317
  });
11318
+ this.filterLogs = (logMethod) => {
11319
+ return (logLevel, messeage, extraData, tags) => {
11320
+ if (this.logLevels.indexOf(logLevel) >=
11321
+ this.logLevels.indexOf(this.logLevel)) {
11322
+ logMethod(logLevel, messeage, extraData, tags);
11323
+ }
11324
+ };
11325
+ };
11326
+ let defaultLogger = logToConsole;
11238
11327
  if (typeof apiKeyOrArgs === 'string') {
11239
- this.streamClient = new StreamClient(apiKeyOrArgs, Object.assign({ persistUserOnConnectionFailure: true }, opts));
11328
+ this.logLevel = (opts === null || opts === void 0 ? void 0 : opts.logLevel) || this.logLevel;
11329
+ this.logger = (opts === null || opts === void 0 ? void 0 : opts.logger) || defaultLogger;
11240
11330
  }
11241
11331
  else {
11242
- this.streamClient = new StreamClient(apiKeyOrArgs.apiKey, Object.assign({ persistUserOnConnectionFailure: true }, apiKeyOrArgs.options));
11332
+ this.logLevel = ((_a = apiKeyOrArgs.options) === null || _a === void 0 ? void 0 : _a.logLevel) || this.logLevel;
11333
+ this.logger = ((_b = apiKeyOrArgs.options) === null || _b === void 0 ? void 0 : _b.logger) || defaultLogger;
11334
+ }
11335
+ setLogger(this.filterLogs(defaultLogger));
11336
+ const clientLogger = getLogger(['client']);
11337
+ if (typeof apiKeyOrArgs === 'string') {
11338
+ this.streamClient = new StreamClient(apiKeyOrArgs, Object.assign(Object.assign({ persistUserOnConnectionFailure: true }, opts), { logLevel: this.logLevel, logger: clientLogger }));
11339
+ }
11340
+ else {
11341
+ this.streamClient = new StreamClient(apiKeyOrArgs.apiKey, Object.assign(Object.assign({ persistUserOnConnectionFailure: true }, apiKeyOrArgs.options), { logLevel: this.logLevel, logger: clientLogger }));
11243
11342
  this.user = apiKeyOrArgs.user;
11244
11343
  this.token = apiKeyOrArgs.token || apiKeyOrArgs.tokenProvider;
11245
11344
  if (this.user) {
@@ -11301,7 +11400,7 @@ class StreamVideoClient {
11301
11400
  },
11302
11401
  sort: [{ field: 'cid', direction: 1 }],
11303
11402
  }).catch((err) => {
11304
- console.warn('Failed to re-watch calls', err);
11403
+ this.logger('error', 'Failed to re-watch calls', err);
11305
11404
  });
11306
11405
  }
11307
11406
  }
@@ -11311,7 +11410,7 @@ class StreamVideoClient {
11311
11410
  return;
11312
11411
  const { call, members } = event;
11313
11412
  if (userToConnect.id === call.created_by.id) {
11314
- console.warn('Received `call.created` sent by the current user');
11413
+ this.logger('warn', 'Received `call.created` sent by the current user');
11315
11414
  return;
11316
11415
  }
11317
11416
  this.writeableStateStore.registerCall(new Call({
@@ -11328,7 +11427,7 @@ class StreamVideoClient {
11328
11427
  return;
11329
11428
  const { call, members } = event;
11330
11429
  if (userToConnect.id === call.created_by.id) {
11331
- console.warn('Received `call.ring` sent by the current user');
11430
+ this.logger('warn', 'Received `call.ring` sent by the current user');
11332
11431
  return;
11333
11432
  }
11334
11433
  // The call might already be tracked by the client,
@@ -11395,7 +11494,10 @@ const getDevices = (constraints) => {
11395
11494
  });
11396
11495
  })
11397
11496
  .catch((error) => {
11398
- console.error('Failed to get devices', error);
11497
+ const logger = getLogger(['devices']);
11498
+ if (logger) {
11499
+ logger('error', 'Failed to get devices', error);
11500
+ }
11399
11501
  subscriber.error(error);
11400
11502
  });
11401
11503
  });
@@ -11466,11 +11568,15 @@ const getAudioOutputDevices = () => {
11466
11568
  return audioDevices$.pipe(rxjs.map((values) => values.filter((d) => d.kind === 'audiooutput')));
11467
11569
  };
11468
11570
  const getStream = (constraints) => __awaiter(void 0, void 0, void 0, function* () {
11571
+ var _a;
11469
11572
  try {
11470
11573
  return yield navigator.mediaDevices.getUserMedia(constraints);
11471
11574
  }
11472
11575
  catch (e) {
11473
- console.error(`Failed get user media`, constraints, e);
11576
+ (_a = getLogger(['devices'])) === null || _a === void 0 ? void 0 : _a('error', `Failed get user media`, {
11577
+ error: e,
11578
+ constraints: constraints,
11579
+ });
11474
11580
  throw e;
11475
11581
  }
11476
11582
  });
@@ -11513,11 +11619,12 @@ const getVideoStream = (trackConstraints) => __awaiter(void 0, void 0, void 0, f
11513
11619
  * @param options any additional options to pass to the [`getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia) API.
11514
11620
  */
11515
11621
  const getScreenShareStream = (options) => __awaiter(void 0, void 0, void 0, function* () {
11622
+ var _b;
11516
11623
  try {
11517
11624
  return yield navigator.mediaDevices.getDisplayMedia(Object.assign({ video: true, audio: false }, options));
11518
11625
  }
11519
11626
  catch (e) {
11520
- console.error('Failed to get screen share stream', e);
11627
+ (_b = getLogger(['devices'])) === null || _b === void 0 ? void 0 : _b('error', 'Failed to get screen share stream', e);
11521
11628
  throw e;
11522
11629
  }
11523
11630
  });
@@ -11740,12 +11847,14 @@ exports.dominantSpeaker = dominantSpeaker;
11740
11847
  exports.getAudioDevices = getAudioDevices;
11741
11848
  exports.getAudioOutputDevices = getAudioOutputDevices;
11742
11849
  exports.getAudioStream = getAudioStream;
11850
+ exports.getLogger = getLogger;
11743
11851
  exports.getScreenShareStream = getScreenShareStream;
11744
11852
  exports.getSdkInfo = getSdkInfo;
11745
11853
  exports.getVideoDevices = getVideoDevices;
11746
11854
  exports.getVideoStream = getVideoStream;
11747
11855
  exports.isStreamVideoLocalParticipant = isStreamVideoLocalParticipant;
11748
11856
  exports.livestreamOrAudioRoomSortPreset = livestreamOrAudioRoomSortPreset;
11857
+ exports.logToConsole = logToConsole;
11749
11858
  exports.name = name;
11750
11859
  exports.noopComparator = noopComparator;
11751
11860
  exports.pinned = pinned;
@@ -11754,6 +11863,7 @@ exports.publishingVideo = publishingVideo;
11754
11863
  exports.reactionType = reactionType;
11755
11864
  exports.role = role;
11756
11865
  exports.screenSharing = screenSharing;
11866
+ exports.setLogger = setLogger;
11757
11867
  exports.setSdkInfo = setSdkInfo;
11758
11868
  exports.speakerLayoutSortPreset = speakerLayoutSortPreset;
11759
11869
  exports.speaking = speaking;