@stream-io/video-client 0.0.39 → 0.0.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/index.browser.es.js +316 -311
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +316 -311
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +316 -311
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +0 -6
- package/dist/src/coordinator/connection/client.d.ts +29 -29
- package/dist/src/coordinator/connection/connection.d.ts +3 -2
- package/dist/src/events/internal.d.ts +1 -0
- package/package.json +1 -1
- package/src/Call.ts +27 -38
- package/src/StreamSfuClient.ts +2 -2
- package/src/StreamVideoClient.ts +4 -1
- package/src/coordinator/connection/client.ts +58 -54
- package/src/coordinator/connection/connection.ts +7 -5
- package/src/events/callEventHandlers.ts +2 -0
- package/src/events/internal.ts +15 -0
- package/src/rtc/Publisher.ts +3 -3
- package/src/rtc/Subscriber.ts +1 -1
- package/src/stats/state-store-stats-reporter.ts +1 -1
package/dist/index.browser.es.js
CHANGED
|
@@ -6280,7 +6280,7 @@ class Publisher {
|
|
|
6280
6280
|
this.onIceCandidate = (e) => __awaiter(this, void 0, void 0, function* () {
|
|
6281
6281
|
const { candidate } = e;
|
|
6282
6282
|
if (!candidate) {
|
|
6283
|
-
this.logger('
|
|
6283
|
+
this.logger('debug', 'null ice candidate');
|
|
6284
6284
|
return;
|
|
6285
6285
|
}
|
|
6286
6286
|
yield this.sfuClient.iceTrickle({
|
|
@@ -6440,10 +6440,10 @@ class Publisher {
|
|
|
6440
6440
|
this.logger('error', `ICE Candidate error`, errorMessage);
|
|
6441
6441
|
};
|
|
6442
6442
|
this.onIceConnectionStateChange = () => {
|
|
6443
|
-
this.logger('
|
|
6443
|
+
this.logger('debug', `ICE Connection state changed`, this.pc.iceConnectionState);
|
|
6444
6444
|
};
|
|
6445
6445
|
this.onIceGatheringStateChange = () => {
|
|
6446
|
-
this.logger('
|
|
6446
|
+
this.logger('debug', `ICE Gathering State`, this.pc.iceGatheringState);
|
|
6447
6447
|
};
|
|
6448
6448
|
this.onSignalingStateChange = () => {
|
|
6449
6449
|
this.logger('debug', `Signaling state changed`, this.pc.signalingState);
|
|
@@ -6610,7 +6610,7 @@ class Subscriber {
|
|
|
6610
6610
|
this.onIceCandidate = (e) => __awaiter(this, void 0, void 0, function* () {
|
|
6611
6611
|
const { candidate } = e;
|
|
6612
6612
|
if (!candidate) {
|
|
6613
|
-
logger$1('
|
|
6613
|
+
logger$1('debug', 'null ice candidate');
|
|
6614
6614
|
return;
|
|
6615
6615
|
}
|
|
6616
6616
|
yield this.sfuClient.iceTrickle({
|
|
@@ -6845,7 +6845,7 @@ class StreamSfuClient {
|
|
|
6845
6845
|
* from this SFU.
|
|
6846
6846
|
*/
|
|
6847
6847
|
this.isMigratingAway = false;
|
|
6848
|
-
this.pingIntervalInMs =
|
|
6848
|
+
this.pingIntervalInMs = 10 * 1000;
|
|
6849
6849
|
this.unhealthyTimeoutInMs = this.pingIntervalInMs + 5 * 1000;
|
|
6850
6850
|
this.close = (code = 1000, reason = 'Requested signal connection close') => {
|
|
6851
6851
|
this.signalWs.close(code, reason);
|
|
@@ -6919,7 +6919,7 @@ class StreamSfuClient {
|
|
|
6919
6919
|
if (this.lastMessageTimestamp) {
|
|
6920
6920
|
const timeSinceLastMessage = new Date().getTime() - this.lastMessageTimestamp.getTime();
|
|
6921
6921
|
if (timeSinceLastMessage > this.unhealthyTimeoutInMs) {
|
|
6922
|
-
this.logger('
|
|
6922
|
+
this.logger('debug', 'SFU connection unhealthy, closing');
|
|
6923
6923
|
this.close(4001, `SFU connection unhealthy. Didn't receive any healthcheck messages for ${this.unhealthyTimeoutInMs}ms`);
|
|
6924
6924
|
}
|
|
6925
6925
|
}
|
|
@@ -8026,6 +8026,16 @@ const watchParticipantCountChanged = (dispatcher, state) => {
|
|
|
8026
8026
|
}
|
|
8027
8027
|
});
|
|
8028
8028
|
};
|
|
8029
|
+
const watchLiveEnded = (dispatcher, call) => {
|
|
8030
|
+
return dispatcher.on('error', (e) => {
|
|
8031
|
+
if (e.eventPayload.oneofKind !== 'error' ||
|
|
8032
|
+
!e.eventPayload.error.error ||
|
|
8033
|
+
e.eventPayload.error.error.code !== ErrorCode.LIVE_ENDED)
|
|
8034
|
+
return;
|
|
8035
|
+
if (!call.permissionsContext.hasPermission(OwnCapability.JOIN_BACKSTAGE))
|
|
8036
|
+
call.leave();
|
|
8037
|
+
});
|
|
8038
|
+
};
|
|
8029
8039
|
/**
|
|
8030
8040
|
* Watches and logs the errors reported by the currently connected SFU.
|
|
8031
8041
|
*/
|
|
@@ -8415,6 +8425,7 @@ const registerEventHandlers = (call, state, dispatcher) => {
|
|
|
8415
8425
|
'call.user_muted': () => console.log('call.user_muted received'),
|
|
8416
8426
|
};
|
|
8417
8427
|
const eventHandlers = [
|
|
8428
|
+
watchLiveEnded(dispatcher, call),
|
|
8418
8429
|
watchSfuErrorReports(dispatcher),
|
|
8419
8430
|
watchChangePublishQuality(dispatcher, call),
|
|
8420
8431
|
watchConnectionQualityChanged(dispatcher, state),
|
|
@@ -8673,7 +8684,7 @@ const createStatsReporter = ({ subscriber, publisher, state, pollingIntervalInMs
|
|
|
8673
8684
|
if (pollingIntervalInMs > 0) {
|
|
8674
8685
|
const loop = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
8675
8686
|
yield run().catch((e) => {
|
|
8676
|
-
logger('
|
|
8687
|
+
logger('debug', 'Failed to collect stats', e);
|
|
8677
8688
|
});
|
|
8678
8689
|
timeoutId = setTimeout(loop, pollingIntervalInMs);
|
|
8679
8690
|
});
|
|
@@ -9081,13 +9092,6 @@ const getClientDetails = () => {
|
|
|
9081
9092
|
* An object representation of a `Call`.
|
|
9082
9093
|
*/
|
|
9083
9094
|
class Call {
|
|
9084
|
-
/**
|
|
9085
|
-
* A promise that exposes the reconnection logic
|
|
9086
|
-
* The use-case is for the react-native platform where online/offline events are not available in the window
|
|
9087
|
-
*/
|
|
9088
|
-
get rejoin() {
|
|
9089
|
-
return this.rejoinPromise;
|
|
9090
|
-
}
|
|
9091
9095
|
/**
|
|
9092
9096
|
* Constructs a new `Call` instance.
|
|
9093
9097
|
*
|
|
@@ -9133,7 +9137,6 @@ class Call {
|
|
|
9133
9137
|
if (callingState === CallingState.LEFT) {
|
|
9134
9138
|
throw new Error('Cannot leave call that has already been left.');
|
|
9135
9139
|
}
|
|
9136
|
-
this.rejoinPromise = undefined;
|
|
9137
9140
|
if (this.ringing) {
|
|
9138
9141
|
// I'm the one who started the call, so I should cancel it.
|
|
9139
9142
|
const hasOtherParticipants = this.state.remoteParticipants.length > 0;
|
|
@@ -9337,7 +9340,7 @@ class Call {
|
|
|
9337
9340
|
// we shouldn't be republishing the streams if we're migrating
|
|
9338
9341
|
// as the underlying peer connection will take care of it as part
|
|
9339
9342
|
// of the ice-restart process
|
|
9340
|
-
if (localParticipant && !
|
|
9343
|
+
if (localParticipant && !migrate) {
|
|
9341
9344
|
const { audioStream, videoStream, screenShareStream: screenShare, } = localParticipant;
|
|
9342
9345
|
// restore previous publishing state
|
|
9343
9346
|
if (audioStream)
|
|
@@ -9349,7 +9352,6 @@ class Call {
|
|
|
9349
9352
|
}
|
|
9350
9353
|
this.logger('info', `[Rejoin]: State restored. Attempt: ${this.reconnectAttempts}`);
|
|
9351
9354
|
});
|
|
9352
|
-
this.rejoinPromise = rejoin;
|
|
9353
9355
|
// reconnect if the connection was closed unexpectedly. example:
|
|
9354
9356
|
// - SFU crash or restart
|
|
9355
9357
|
// - network change
|
|
@@ -9382,9 +9384,6 @@ class Call {
|
|
|
9382
9384
|
if (e.code === KnownCodes.WS_CLOSED_ABRUPTLY &&
|
|
9383
9385
|
sfuClient.isMigratingAway)
|
|
9384
9386
|
return;
|
|
9385
|
-
// do nothing for react-native as it is handled by SDK
|
|
9386
|
-
if (isReactNative())
|
|
9387
|
-
return;
|
|
9388
9387
|
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
9389
9388
|
rejoin().catch((err) => {
|
|
9390
9389
|
this.logger('error', `[Rejoin]: Rejoin failed for ${this.reconnectAttempts} times. Giving up.`, err);
|
|
@@ -9398,28 +9397,34 @@ class Call {
|
|
|
9398
9397
|
});
|
|
9399
9398
|
});
|
|
9400
9399
|
// handlers for connection online/offline events
|
|
9401
|
-
|
|
9402
|
-
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
9400
|
+
const unsubscribeOnlineEvent = this.streamClient.on('connection.changed', (e) => {
|
|
9401
|
+
if (e.type !== 'connection.changed')
|
|
9402
|
+
return;
|
|
9403
|
+
if (!e.online)
|
|
9404
|
+
return;
|
|
9405
|
+
unsubscribeOnlineEvent();
|
|
9406
|
+
const currentCallingState = this.state.callingState;
|
|
9407
|
+
if (currentCallingState === CallingState.OFFLINE ||
|
|
9408
|
+
currentCallingState === CallingState.RECONNECTING_FAILED) {
|
|
9409
|
+
this.logger('info', '[Rejoin]: Going online...');
|
|
9410
|
+
rejoin().catch((err) => {
|
|
9411
|
+
this.logger('error', `[Rejoin]: Rejoin failed for ${this.reconnectAttempts} times. Giving up.`, err);
|
|
9412
|
+
this.state.setCallingState(CallingState.RECONNECTING_FAILED);
|
|
9413
|
+
});
|
|
9414
|
+
}
|
|
9415
|
+
});
|
|
9416
|
+
const unsubscribeOfflineEvent = this.streamClient.on('connection.changed', (e) => {
|
|
9417
|
+
if (e.type !== 'connection.changed')
|
|
9418
|
+
return;
|
|
9419
|
+
if (e.online)
|
|
9420
|
+
return;
|
|
9421
|
+
unsubscribeOfflineEvent();
|
|
9422
|
+
this.state.setCallingState(CallingState.OFFLINE);
|
|
9423
|
+
});
|
|
9424
|
+
this.leaveCallHooks.push(() => {
|
|
9425
|
+
unsubscribeOnlineEvent();
|
|
9426
|
+
unsubscribeOfflineEvent();
|
|
9427
|
+
});
|
|
9423
9428
|
if (!this.subscriber) {
|
|
9424
9429
|
this.subscriber = new Subscriber({
|
|
9425
9430
|
sfuClient,
|
|
@@ -10270,6 +10275,9 @@ const isErrorEvent = (res) => res.error !== undefined;
|
|
|
10270
10275
|
*/
|
|
10271
10276
|
class StableWSConnection {
|
|
10272
10277
|
constructor(client) {
|
|
10278
|
+
this._log = (msg, extra = {}, level = 'info') => {
|
|
10279
|
+
this.client.logger(level, 'connection:' + msg, Object.assign({}, extra));
|
|
10280
|
+
};
|
|
10273
10281
|
this.setClient = (client) => {
|
|
10274
10282
|
this.client = client;
|
|
10275
10283
|
};
|
|
@@ -10298,7 +10306,8 @@ class StableWSConnection {
|
|
|
10298
10306
|
if (event.type === 'offline') {
|
|
10299
10307
|
// mark the connection as down
|
|
10300
10308
|
this._log('onlineStatusChanged() - Status changing to offline');
|
|
10301
|
-
|
|
10309
|
+
// we know that the app is offline so dispatch the unhealthy connection event immediately
|
|
10310
|
+
this._setHealth(false, true);
|
|
10302
10311
|
}
|
|
10303
10312
|
else if (event.type === 'online') {
|
|
10304
10313
|
// retry right now...
|
|
@@ -10426,13 +10435,14 @@ class StableWSConnection {
|
|
|
10426
10435
|
* Broadcasts an event in case the connection status changed.
|
|
10427
10436
|
*
|
|
10428
10437
|
* @param {boolean} healthy boolean indicating if the connection is healthy or not
|
|
10438
|
+
* @param {boolean} dispatchImmediately boolean indicating to dispatch event immediately even if the connection is unhealthy
|
|
10429
10439
|
*
|
|
10430
10440
|
*/
|
|
10431
|
-
this._setHealth = (healthy) => {
|
|
10441
|
+
this._setHealth = (healthy, dispatchImmediately = false) => {
|
|
10432
10442
|
if (healthy === this.isHealthy)
|
|
10433
10443
|
return;
|
|
10434
10444
|
this.isHealthy = healthy;
|
|
10435
|
-
if (this.isHealthy) {
|
|
10445
|
+
if (this.isHealthy || dispatchImmediately) {
|
|
10436
10446
|
this.client.dispatchEvent({
|
|
10437
10447
|
type: 'connection.changed',
|
|
10438
10448
|
online: this.isHealthy,
|
|
@@ -10554,9 +10564,6 @@ class StableWSConnection {
|
|
|
10554
10564
|
this.connectionCheckTimeout = this.pingInterval + 10 * 1000;
|
|
10555
10565
|
addConnectionEventListeners(this.onlineStatusChanged);
|
|
10556
10566
|
}
|
|
10557
|
-
_log(msg, extra = {}, level = 'info') {
|
|
10558
|
-
this.client.logger(level, 'connection:' + msg, Object.assign({}, extra));
|
|
10559
|
-
}
|
|
10560
10567
|
/**
|
|
10561
10568
|
* connect - Connect to the WS URL
|
|
10562
10569
|
* the default 15s timeout allows between 2~3 tries
|
|
@@ -11305,6 +11312,18 @@ class StreamClient {
|
|
|
11305
11312
|
constructor(key, options) {
|
|
11306
11313
|
var _a;
|
|
11307
11314
|
this.nextRequestAbortController = null;
|
|
11315
|
+
this.devToken = (userID) => {
|
|
11316
|
+
return DevToken(userID);
|
|
11317
|
+
};
|
|
11318
|
+
this.getAuthType = () => {
|
|
11319
|
+
return this.anonymous ? 'anonymous' : 'jwt';
|
|
11320
|
+
};
|
|
11321
|
+
this.setBaseURL = (baseURL) => {
|
|
11322
|
+
this.baseURL = baseURL;
|
|
11323
|
+
this.wsBaseURL = this.baseURL
|
|
11324
|
+
.replace('http', 'ws')
|
|
11325
|
+
.replace(':3030', ':8800');
|
|
11326
|
+
};
|
|
11308
11327
|
this._getConnectionID = () => { var _a, _b; return ((_a = this.wsConnection) === null || _a === void 0 ? void 0 : _a.connectionID) || ((_b = this.wsFallback) === null || _b === void 0 ? void 0 : _b.connectionID); };
|
|
11309
11328
|
this._hasConnectionID = () => Boolean(this._getConnectionID());
|
|
11310
11329
|
/**
|
|
@@ -11369,6 +11388,16 @@ class StreamClient {
|
|
|
11369
11388
|
}
|
|
11370
11389
|
});
|
|
11371
11390
|
this._setToken = (user, userTokenOrProvider, isAnonymous) => this.tokenManager.setTokenOrProvider(userTokenOrProvider, user, isAnonymous);
|
|
11391
|
+
this._setUser = (user) => {
|
|
11392
|
+
/**
|
|
11393
|
+
* This one is used by the frontend. This is a copy of the current user object stored on backend.
|
|
11394
|
+
* It contains reserved properties and own user properties which are not present in `this._user`.
|
|
11395
|
+
*/
|
|
11396
|
+
this.user = user;
|
|
11397
|
+
this.userID = user.id;
|
|
11398
|
+
// this one is actually used for requests. This is a copy of current user provided to `connectUser` function.
|
|
11399
|
+
this._user = Object.assign({}, user);
|
|
11400
|
+
};
|
|
11372
11401
|
/**
|
|
11373
11402
|
* Disconnects the websocket connection, without removing the user set on client.
|
|
11374
11403
|
* client.closeConnection will not trigger default auto-retry mechanism for reconnection. You need
|
|
@@ -11457,6 +11486,66 @@ class StreamClient {
|
|
|
11457
11486
|
// resolve the connection_id here.
|
|
11458
11487
|
this.resolveConnectionId();
|
|
11459
11488
|
});
|
|
11489
|
+
/**
|
|
11490
|
+
* on - Listen to events on all channels and users your watching
|
|
11491
|
+
*
|
|
11492
|
+
* client.on('message.new', event => {console.log("my new message", event, channel.state.messages)})
|
|
11493
|
+
* or
|
|
11494
|
+
* client.on(event => {console.log(event.type)})
|
|
11495
|
+
*
|
|
11496
|
+
* @param {EventHandler | string} callbackOrEventName The event type to listen for (optional)
|
|
11497
|
+
* @param {EventHandler} [callbackOrNothing] The callback to call
|
|
11498
|
+
*
|
|
11499
|
+
* @return {Function} Returns a function which, when called, unsubscribes the event handler.
|
|
11500
|
+
*/
|
|
11501
|
+
this.on = (callbackOrEventName, callbackOrNothing) => {
|
|
11502
|
+
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11503
|
+
const callback = callbackOrNothing
|
|
11504
|
+
? callbackOrNothing
|
|
11505
|
+
: callbackOrEventName;
|
|
11506
|
+
if (!(key in this.listeners)) {
|
|
11507
|
+
this.listeners[key] = [];
|
|
11508
|
+
}
|
|
11509
|
+
this.listeners[key].push(callback);
|
|
11510
|
+
return () => {
|
|
11511
|
+
this.off(key, callback);
|
|
11512
|
+
};
|
|
11513
|
+
};
|
|
11514
|
+
/**
|
|
11515
|
+
* off - Remove the event handler
|
|
11516
|
+
*
|
|
11517
|
+
*/
|
|
11518
|
+
this.off = (callbackOrEventName, callbackOrNothing) => {
|
|
11519
|
+
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11520
|
+
const callback = callbackOrNothing
|
|
11521
|
+
? callbackOrNothing
|
|
11522
|
+
: callbackOrEventName;
|
|
11523
|
+
if (!(key in this.listeners)) {
|
|
11524
|
+
this.listeners[key] = [];
|
|
11525
|
+
}
|
|
11526
|
+
this.logger('debug', `Removing listener for ${key} event`);
|
|
11527
|
+
this.listeners[key] = this.listeners[key].filter((value) => value !== callback);
|
|
11528
|
+
};
|
|
11529
|
+
this._logApiRequest = (type, url, data, config) => {
|
|
11530
|
+
this.logger('trace', `client: ${type} - Request - ${url}`, {
|
|
11531
|
+
payload: data,
|
|
11532
|
+
config,
|
|
11533
|
+
});
|
|
11534
|
+
};
|
|
11535
|
+
this._logApiResponse = (type, url, response) => {
|
|
11536
|
+
this.logger('trace', `client:${type} - Response - url: ${url} > status ${response.status}`, {
|
|
11537
|
+
response,
|
|
11538
|
+
});
|
|
11539
|
+
this.logger('trace', `client:${type} - Response payload`, {
|
|
11540
|
+
response,
|
|
11541
|
+
});
|
|
11542
|
+
};
|
|
11543
|
+
this._logApiError = (type, url, error) => {
|
|
11544
|
+
this.logger('error', `client:${type} - Error - url: ${url}`, {
|
|
11545
|
+
url,
|
|
11546
|
+
error,
|
|
11547
|
+
});
|
|
11548
|
+
};
|
|
11460
11549
|
this.doAxiosRequest = (type, url, data, options = {}) => __awaiter(this, void 0, void 0, function* () {
|
|
11461
11550
|
var _g;
|
|
11462
11551
|
if (!options.publicEndpoint || this.user) {
|
|
@@ -11521,6 +11610,43 @@ class StreamClient {
|
|
|
11521
11610
|
}
|
|
11522
11611
|
}
|
|
11523
11612
|
});
|
|
11613
|
+
this.get = (url, params) => {
|
|
11614
|
+
return this.doAxiosRequest('get', url, null, {
|
|
11615
|
+
params,
|
|
11616
|
+
});
|
|
11617
|
+
};
|
|
11618
|
+
this.put = (url, data) => {
|
|
11619
|
+
return this.doAxiosRequest('put', url, data);
|
|
11620
|
+
};
|
|
11621
|
+
this.post = (url, data) => {
|
|
11622
|
+
return this.doAxiosRequest('post', url, data);
|
|
11623
|
+
};
|
|
11624
|
+
this.patch = (url, data) => {
|
|
11625
|
+
return this.doAxiosRequest('patch', url, data);
|
|
11626
|
+
};
|
|
11627
|
+
this.delete = (url, params) => {
|
|
11628
|
+
return this.doAxiosRequest('delete', url, null, {
|
|
11629
|
+
params,
|
|
11630
|
+
});
|
|
11631
|
+
};
|
|
11632
|
+
this.errorFromResponse = (response) => {
|
|
11633
|
+
let err;
|
|
11634
|
+
err = new ErrorFromResponse(`Stream error HTTP code: ${response.status}`);
|
|
11635
|
+
if (response.data && response.data.code) {
|
|
11636
|
+
err = new Error(`Stream error code ${response.data.code}: ${response.data.message}`);
|
|
11637
|
+
err.code = response.data.code;
|
|
11638
|
+
}
|
|
11639
|
+
err.response = response;
|
|
11640
|
+
err.status = response.status;
|
|
11641
|
+
return err;
|
|
11642
|
+
};
|
|
11643
|
+
this.handleResponse = (response) => {
|
|
11644
|
+
const data = response.data;
|
|
11645
|
+
if (isErrorResponse(response)) {
|
|
11646
|
+
throw this.errorFromResponse(response);
|
|
11647
|
+
}
|
|
11648
|
+
return data;
|
|
11649
|
+
};
|
|
11524
11650
|
this.dispatchEvent = (event) => {
|
|
11525
11651
|
if (!event.received_at)
|
|
11526
11652
|
event.received_at = new Date();
|
|
@@ -11548,10 +11674,119 @@ class StreamClient {
|
|
|
11548
11674
|
listener(event);
|
|
11549
11675
|
}
|
|
11550
11676
|
};
|
|
11677
|
+
/**
|
|
11678
|
+
* @private
|
|
11679
|
+
*/
|
|
11680
|
+
this.connect = () => __awaiter(this, void 0, void 0, function* () {
|
|
11681
|
+
if (!this.userID || !this._user) {
|
|
11682
|
+
throw Error('Call connectUser or connectAnonymousUser before starting the connection');
|
|
11683
|
+
}
|
|
11684
|
+
if (!this.wsBaseURL) {
|
|
11685
|
+
throw Error('Websocket base url not set');
|
|
11686
|
+
}
|
|
11687
|
+
if (!this.clientID) {
|
|
11688
|
+
throw Error('clientID is not set');
|
|
11689
|
+
}
|
|
11690
|
+
if (!this.wsConnection &&
|
|
11691
|
+
(this.options.warmUp || this.options.enableInsights)) {
|
|
11692
|
+
this._sayHi();
|
|
11693
|
+
}
|
|
11694
|
+
// The StableWSConnection handles all the reconnection logic.
|
|
11695
|
+
if (this.options.wsConnection && this.node) {
|
|
11696
|
+
// Intentionally avoiding adding ts generics on wsConnection in options since its only useful for unit test purpose.
|
|
11697
|
+
this.options.wsConnection.setClient(this);
|
|
11698
|
+
this.wsConnection = this.options
|
|
11699
|
+
.wsConnection;
|
|
11700
|
+
}
|
|
11701
|
+
else {
|
|
11702
|
+
this.wsConnection = new StableWSConnection(this);
|
|
11703
|
+
}
|
|
11704
|
+
try {
|
|
11705
|
+
// if fallback is used before, continue using it instead of waiting for WS to fail
|
|
11706
|
+
if (this.wsFallback) {
|
|
11707
|
+
return yield this.wsFallback.connect();
|
|
11708
|
+
}
|
|
11709
|
+
this.logger('info', 'StreamClient.connect: this.wsConnection.connect()');
|
|
11710
|
+
// if WSFallback is enabled, ws connect should timeout faster so fallback can try
|
|
11711
|
+
return yield this.wsConnection.connect(this.options.enableWSFallback
|
|
11712
|
+
? this.defaultWSTimeoutWithFallback
|
|
11713
|
+
: this.defaultWSTimeout);
|
|
11714
|
+
}
|
|
11715
|
+
catch (err) {
|
|
11716
|
+
// run fallback only if it's WS/Network error and not a normal API error
|
|
11717
|
+
// make sure browser is online before even trying the longpoll
|
|
11718
|
+
if (this.options.enableWSFallback &&
|
|
11719
|
+
// @ts-ignore
|
|
11720
|
+
isWSFailure(err) &&
|
|
11721
|
+
isOnline(this.logger)) {
|
|
11722
|
+
this.logger('warn', 'client:connect() - WS failed, fallback to longpoll');
|
|
11723
|
+
this.dispatchEvent({ type: 'transport.changed', mode: 'longpoll' });
|
|
11724
|
+
this.wsConnection._destroyCurrentWSConnection();
|
|
11725
|
+
this.wsConnection.disconnect().then(); // close WS so no retry
|
|
11726
|
+
this.wsFallback = new WSConnectionFallback(this);
|
|
11727
|
+
return yield this.wsFallback.connect();
|
|
11728
|
+
}
|
|
11729
|
+
throw err;
|
|
11730
|
+
}
|
|
11731
|
+
});
|
|
11732
|
+
/**
|
|
11733
|
+
* Check the connectivity with server for warmup purpose.
|
|
11734
|
+
*
|
|
11735
|
+
* @private
|
|
11736
|
+
*/
|
|
11737
|
+
this._sayHi = () => {
|
|
11738
|
+
const client_request_id = randomId();
|
|
11739
|
+
const opts = {
|
|
11740
|
+
headers: AxiosHeaders.from({
|
|
11741
|
+
'x-client-request-id': client_request_id,
|
|
11742
|
+
}),
|
|
11743
|
+
};
|
|
11744
|
+
this.doAxiosRequest('get', this.baseURL + '/hi', null, opts).catch((e) => {
|
|
11745
|
+
if (this.options.enableInsights) {
|
|
11746
|
+
postInsights('http_hi_failed', {
|
|
11747
|
+
api_key: this.key,
|
|
11748
|
+
err: e,
|
|
11749
|
+
client_request_id,
|
|
11750
|
+
});
|
|
11751
|
+
}
|
|
11752
|
+
});
|
|
11753
|
+
};
|
|
11754
|
+
this.getUserAgent = () => {
|
|
11755
|
+
return (this.userAgent ||
|
|
11756
|
+
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.40"}`);
|
|
11757
|
+
};
|
|
11758
|
+
this.setUserAgent = (userAgent) => {
|
|
11759
|
+
this.userAgent = userAgent;
|
|
11760
|
+
};
|
|
11551
11761
|
/**
|
|
11552
11762
|
* _isUsingServerAuth - Returns true if we're using server side auth
|
|
11553
11763
|
*/
|
|
11554
11764
|
this._isUsingServerAuth = () => !!this.secret;
|
|
11765
|
+
this._enrichAxiosOptions = (options = {
|
|
11766
|
+
params: {},
|
|
11767
|
+
headers: {},
|
|
11768
|
+
config: {},
|
|
11769
|
+
}) => {
|
|
11770
|
+
var _a;
|
|
11771
|
+
const token = options.publicEndpoint && !this.user ? undefined : this._getToken();
|
|
11772
|
+
const authorization = token ? { Authorization: token } : undefined;
|
|
11773
|
+
let signal = null;
|
|
11774
|
+
if (this.nextRequestAbortController !== null) {
|
|
11775
|
+
signal = this.nextRequestAbortController.signal;
|
|
11776
|
+
this.nextRequestAbortController = null;
|
|
11777
|
+
}
|
|
11778
|
+
if (!((_a = options.headers) === null || _a === void 0 ? void 0 : _a['x-client-request-id'])) {
|
|
11779
|
+
options.headers = Object.assign(Object.assign({}, options.headers), { 'x-client-request-id': randomId() });
|
|
11780
|
+
}
|
|
11781
|
+
return Object.assign(Object.assign(Object.assign({ params: Object.assign({ user_id: this.userID, connection_id: this._getConnectionID(), api_key: this.key }, options.params), headers: Object.assign(Object.assign(Object.assign({}, authorization), { 'stream-auth-type': options.publicEndpoint && !this.user
|
|
11782
|
+
? 'anonymous'
|
|
11783
|
+
: this.getAuthType(), 'X-Stream-Client': this.getUserAgent() }), options.headers) }, (signal ? { signal } : {})), options.config), this.options.axiosRequestConfig);
|
|
11784
|
+
};
|
|
11785
|
+
this._getToken = () => {
|
|
11786
|
+
if (!this.tokenManager)
|
|
11787
|
+
return null;
|
|
11788
|
+
return this.tokenManager.getToken();
|
|
11789
|
+
};
|
|
11555
11790
|
/**
|
|
11556
11791
|
* encode ws url payload
|
|
11557
11792
|
* @private
|
|
@@ -11564,6 +11799,38 @@ class StreamClient {
|
|
|
11564
11799
|
client_request_id,
|
|
11565
11800
|
});
|
|
11566
11801
|
};
|
|
11802
|
+
/**
|
|
11803
|
+
* creates an abort controller that will be used by the next HTTP Request.
|
|
11804
|
+
*/
|
|
11805
|
+
this.createAbortControllerForNextRequest = () => {
|
|
11806
|
+
return (this.nextRequestAbortController = new AbortController());
|
|
11807
|
+
};
|
|
11808
|
+
/**
|
|
11809
|
+
* createToken - Creates a token to authenticate this user. This function is used server side.
|
|
11810
|
+
* The resulting token should be passed to the client side when the users registers or logs in.
|
|
11811
|
+
*
|
|
11812
|
+
* @param {string} userID The UserWithId ID
|
|
11813
|
+
* @param {number} [exp] The expiration time for the token expressed in the number of seconds since the epoch
|
|
11814
|
+
* @param call_cids for anonymous tokens you have to provide the call cids the use can join
|
|
11815
|
+
*
|
|
11816
|
+
* @return {string} Returns a token
|
|
11817
|
+
*/
|
|
11818
|
+
this.createToken = (userID, exp, iat, call_cids) => {
|
|
11819
|
+
if (this.secret == null) {
|
|
11820
|
+
throw Error(`tokens can only be created server-side using the API Secret`);
|
|
11821
|
+
}
|
|
11822
|
+
const extra = {};
|
|
11823
|
+
if (exp) {
|
|
11824
|
+
extra.exp = exp;
|
|
11825
|
+
}
|
|
11826
|
+
if (iat) {
|
|
11827
|
+
extra.iat = iat;
|
|
11828
|
+
}
|
|
11829
|
+
if (call_cids) {
|
|
11830
|
+
extra.call_cids = call_cids;
|
|
11831
|
+
}
|
|
11832
|
+
return JWTUserToken(this.secret, userID, extra, {});
|
|
11833
|
+
};
|
|
11567
11834
|
// set the key
|
|
11568
11835
|
this.key = key;
|
|
11569
11836
|
this.listeners = {};
|
|
@@ -11615,268 +11882,6 @@ class StreamClient {
|
|
|
11615
11882
|
? inputOptions.logger
|
|
11616
11883
|
: () => null;
|
|
11617
11884
|
}
|
|
11618
|
-
devToken(userID) {
|
|
11619
|
-
return DevToken(userID);
|
|
11620
|
-
}
|
|
11621
|
-
getAuthType() {
|
|
11622
|
-
return this.anonymous ? 'anonymous' : 'jwt';
|
|
11623
|
-
}
|
|
11624
|
-
setBaseURL(baseURL) {
|
|
11625
|
-
this.baseURL = baseURL;
|
|
11626
|
-
this.wsBaseURL = this.baseURL
|
|
11627
|
-
.replace('http', 'ws')
|
|
11628
|
-
.replace(':3030', ':8800');
|
|
11629
|
-
}
|
|
11630
|
-
_setUser(user) {
|
|
11631
|
-
/**
|
|
11632
|
-
* This one is used by the frontend. This is a copy of the current user object stored on backend.
|
|
11633
|
-
* It contains reserved properties and own user properties which are not present in `this._user`.
|
|
11634
|
-
*/
|
|
11635
|
-
this.user = user;
|
|
11636
|
-
this.userID = user.id;
|
|
11637
|
-
// this one is actually used for requests. This is a copy of current user provided to `connectUser` function.
|
|
11638
|
-
this._user = Object.assign({}, user);
|
|
11639
|
-
}
|
|
11640
|
-
/**
|
|
11641
|
-
* on - Listen to events on all channels and users your watching
|
|
11642
|
-
*
|
|
11643
|
-
* client.on('message.new', event => {console.log("my new message", event, channel.state.messages)})
|
|
11644
|
-
* or
|
|
11645
|
-
* client.on(event => {console.log(event.type)})
|
|
11646
|
-
*
|
|
11647
|
-
* @param {EventHandler | string} callbackOrEventName The event type to listen for (optional)
|
|
11648
|
-
* @param {EventHandler} [callbackOrNothing] The callback to call
|
|
11649
|
-
*
|
|
11650
|
-
* @return {Function} Returns a function which, when called, unsubscribes the event handler.
|
|
11651
|
-
*/
|
|
11652
|
-
on(callbackOrEventName, callbackOrNothing) {
|
|
11653
|
-
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11654
|
-
const callback = callbackOrNothing
|
|
11655
|
-
? callbackOrNothing
|
|
11656
|
-
: callbackOrEventName;
|
|
11657
|
-
if (!(key in this.listeners)) {
|
|
11658
|
-
this.listeners[key] = [];
|
|
11659
|
-
}
|
|
11660
|
-
this.listeners[key].push(callback);
|
|
11661
|
-
return () => {
|
|
11662
|
-
this.off(key, callback);
|
|
11663
|
-
};
|
|
11664
|
-
}
|
|
11665
|
-
/**
|
|
11666
|
-
* off - Remove the event handler
|
|
11667
|
-
*
|
|
11668
|
-
*/
|
|
11669
|
-
off(callbackOrEventName, callbackOrNothing) {
|
|
11670
|
-
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11671
|
-
const callback = callbackOrNothing
|
|
11672
|
-
? callbackOrNothing
|
|
11673
|
-
: callbackOrEventName;
|
|
11674
|
-
if (!(key in this.listeners)) {
|
|
11675
|
-
this.listeners[key] = [];
|
|
11676
|
-
}
|
|
11677
|
-
this.logger('debug', `Removing listener for ${key} event`);
|
|
11678
|
-
this.listeners[key] = this.listeners[key].filter((value) => value !== callback);
|
|
11679
|
-
}
|
|
11680
|
-
_logApiRequest(type, url, data, config) {
|
|
11681
|
-
this.logger('trace', `client: ${type} - Request - ${url}`, {
|
|
11682
|
-
payload: data,
|
|
11683
|
-
config,
|
|
11684
|
-
});
|
|
11685
|
-
}
|
|
11686
|
-
_logApiResponse(type, url, response) {
|
|
11687
|
-
this.logger('trace', `client:${type} - Response - url: ${url} > status ${response.status}`, {
|
|
11688
|
-
response,
|
|
11689
|
-
});
|
|
11690
|
-
this.logger('trace', `client:${type} - Response payload`, {
|
|
11691
|
-
response,
|
|
11692
|
-
});
|
|
11693
|
-
}
|
|
11694
|
-
_logApiError(type, url, error) {
|
|
11695
|
-
this.logger('error', `client:${type} - Error - url: ${url}`, {
|
|
11696
|
-
url,
|
|
11697
|
-
error,
|
|
11698
|
-
});
|
|
11699
|
-
}
|
|
11700
|
-
get(url, params) {
|
|
11701
|
-
return this.doAxiosRequest('get', url, null, {
|
|
11702
|
-
params,
|
|
11703
|
-
});
|
|
11704
|
-
}
|
|
11705
|
-
put(url, data) {
|
|
11706
|
-
return this.doAxiosRequest('put', url, data);
|
|
11707
|
-
}
|
|
11708
|
-
post(url, data) {
|
|
11709
|
-
return this.doAxiosRequest('post', url, data);
|
|
11710
|
-
}
|
|
11711
|
-
patch(url, data) {
|
|
11712
|
-
return this.doAxiosRequest('patch', url, data);
|
|
11713
|
-
}
|
|
11714
|
-
delete(url, params) {
|
|
11715
|
-
return this.doAxiosRequest('delete', url, null, {
|
|
11716
|
-
params,
|
|
11717
|
-
});
|
|
11718
|
-
}
|
|
11719
|
-
errorFromResponse(response) {
|
|
11720
|
-
let err;
|
|
11721
|
-
err = new ErrorFromResponse(`Stream error HTTP code: ${response.status}`);
|
|
11722
|
-
if (response.data && response.data.code) {
|
|
11723
|
-
err = new Error(`Stream error code ${response.data.code}: ${response.data.message}`);
|
|
11724
|
-
err.code = response.data.code;
|
|
11725
|
-
}
|
|
11726
|
-
err.response = response;
|
|
11727
|
-
err.status = response.status;
|
|
11728
|
-
return err;
|
|
11729
|
-
}
|
|
11730
|
-
handleResponse(response) {
|
|
11731
|
-
const data = response.data;
|
|
11732
|
-
if (isErrorResponse(response)) {
|
|
11733
|
-
throw this.errorFromResponse(response);
|
|
11734
|
-
}
|
|
11735
|
-
return data;
|
|
11736
|
-
}
|
|
11737
|
-
/**
|
|
11738
|
-
* @private
|
|
11739
|
-
*/
|
|
11740
|
-
connect() {
|
|
11741
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
11742
|
-
if (!this.userID || !this._user) {
|
|
11743
|
-
throw Error('Call connectUser or connectAnonymousUser before starting the connection');
|
|
11744
|
-
}
|
|
11745
|
-
if (!this.wsBaseURL) {
|
|
11746
|
-
throw Error('Websocket base url not set');
|
|
11747
|
-
}
|
|
11748
|
-
if (!this.clientID) {
|
|
11749
|
-
throw Error('clientID is not set');
|
|
11750
|
-
}
|
|
11751
|
-
if (!this.wsConnection &&
|
|
11752
|
-
(this.options.warmUp || this.options.enableInsights)) {
|
|
11753
|
-
this._sayHi();
|
|
11754
|
-
}
|
|
11755
|
-
// The StableWSConnection handles all the reconnection logic.
|
|
11756
|
-
if (this.options.wsConnection && this.node) {
|
|
11757
|
-
// Intentionally avoiding adding ts generics on wsConnection in options since its only useful for unit test purpose.
|
|
11758
|
-
this.options.wsConnection.setClient(this);
|
|
11759
|
-
this.wsConnection = this.options
|
|
11760
|
-
.wsConnection;
|
|
11761
|
-
}
|
|
11762
|
-
else {
|
|
11763
|
-
this.wsConnection = new StableWSConnection(this);
|
|
11764
|
-
}
|
|
11765
|
-
try {
|
|
11766
|
-
// if fallback is used before, continue using it instead of waiting for WS to fail
|
|
11767
|
-
if (this.wsFallback) {
|
|
11768
|
-
return yield this.wsFallback.connect();
|
|
11769
|
-
}
|
|
11770
|
-
this.logger('info', 'StreamClient.connect: this.wsConnection.connect()');
|
|
11771
|
-
// if WSFallback is enabled, ws connect should timeout faster so fallback can try
|
|
11772
|
-
return yield this.wsConnection.connect(this.options.enableWSFallback
|
|
11773
|
-
? this.defaultWSTimeoutWithFallback
|
|
11774
|
-
: this.defaultWSTimeout);
|
|
11775
|
-
}
|
|
11776
|
-
catch (err) {
|
|
11777
|
-
// run fallback only if it's WS/Network error and not a normal API error
|
|
11778
|
-
// make sure browser is online before even trying the longpoll
|
|
11779
|
-
if (this.options.enableWSFallback &&
|
|
11780
|
-
// @ts-ignore
|
|
11781
|
-
isWSFailure(err) &&
|
|
11782
|
-
isOnline(this.logger)) {
|
|
11783
|
-
this.logger('warn', 'client:connect() - WS failed, fallback to longpoll');
|
|
11784
|
-
this.dispatchEvent({ type: 'transport.changed', mode: 'longpoll' });
|
|
11785
|
-
this.wsConnection._destroyCurrentWSConnection();
|
|
11786
|
-
this.wsConnection.disconnect().then(); // close WS so no retry
|
|
11787
|
-
this.wsFallback = new WSConnectionFallback(this);
|
|
11788
|
-
return yield this.wsFallback.connect();
|
|
11789
|
-
}
|
|
11790
|
-
throw err;
|
|
11791
|
-
}
|
|
11792
|
-
});
|
|
11793
|
-
}
|
|
11794
|
-
/**
|
|
11795
|
-
* Check the connectivity with server for warmup purpose.
|
|
11796
|
-
*
|
|
11797
|
-
* @private
|
|
11798
|
-
*/
|
|
11799
|
-
_sayHi() {
|
|
11800
|
-
const client_request_id = randomId();
|
|
11801
|
-
const opts = {
|
|
11802
|
-
headers: AxiosHeaders.from({
|
|
11803
|
-
'x-client-request-id': client_request_id,
|
|
11804
|
-
}),
|
|
11805
|
-
};
|
|
11806
|
-
this.doAxiosRequest('get', this.baseURL + '/hi', null, opts).catch((e) => {
|
|
11807
|
-
if (this.options.enableInsights) {
|
|
11808
|
-
postInsights('http_hi_failed', {
|
|
11809
|
-
api_key: this.key,
|
|
11810
|
-
err: e,
|
|
11811
|
-
client_request_id,
|
|
11812
|
-
});
|
|
11813
|
-
}
|
|
11814
|
-
});
|
|
11815
|
-
}
|
|
11816
|
-
getUserAgent() {
|
|
11817
|
-
return (this.userAgent ||
|
|
11818
|
-
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.38"}`);
|
|
11819
|
-
}
|
|
11820
|
-
setUserAgent(userAgent) {
|
|
11821
|
-
this.userAgent = userAgent;
|
|
11822
|
-
}
|
|
11823
|
-
_enrichAxiosOptions(options = {
|
|
11824
|
-
params: {},
|
|
11825
|
-
headers: {},
|
|
11826
|
-
config: {},
|
|
11827
|
-
}) {
|
|
11828
|
-
var _a;
|
|
11829
|
-
const token = options.publicEndpoint && !this.user ? undefined : this._getToken();
|
|
11830
|
-
const authorization = token ? { Authorization: token } : undefined;
|
|
11831
|
-
let signal = null;
|
|
11832
|
-
if (this.nextRequestAbortController !== null) {
|
|
11833
|
-
signal = this.nextRequestAbortController.signal;
|
|
11834
|
-
this.nextRequestAbortController = null;
|
|
11835
|
-
}
|
|
11836
|
-
if (!((_a = options.headers) === null || _a === void 0 ? void 0 : _a['x-client-request-id'])) {
|
|
11837
|
-
options.headers = Object.assign(Object.assign({}, options.headers), { 'x-client-request-id': randomId() });
|
|
11838
|
-
}
|
|
11839
|
-
return Object.assign(Object.assign(Object.assign({ params: Object.assign({ user_id: this.userID, connection_id: this._getConnectionID(), api_key: this.key }, options.params), headers: Object.assign(Object.assign(Object.assign({}, authorization), { 'stream-auth-type': options.publicEndpoint && !this.user
|
|
11840
|
-
? 'anonymous'
|
|
11841
|
-
: this.getAuthType(), 'X-Stream-Client': this.getUserAgent() }), options.headers) }, (signal ? { signal } : {})), options.config), this.options.axiosRequestConfig);
|
|
11842
|
-
}
|
|
11843
|
-
_getToken() {
|
|
11844
|
-
if (!this.tokenManager)
|
|
11845
|
-
return null;
|
|
11846
|
-
return this.tokenManager.getToken();
|
|
11847
|
-
}
|
|
11848
|
-
/**
|
|
11849
|
-
* creates an abort controller that will be used by the next HTTP Request.
|
|
11850
|
-
*/
|
|
11851
|
-
createAbortControllerForNextRequest() {
|
|
11852
|
-
return (this.nextRequestAbortController = new AbortController());
|
|
11853
|
-
}
|
|
11854
|
-
/**
|
|
11855
|
-
* createToken - Creates a token to authenticate this user. This function is used server side.
|
|
11856
|
-
* The resulting token should be passed to the client side when the users registers or logs in.
|
|
11857
|
-
*
|
|
11858
|
-
* @param {string} userID The UserWithId ID
|
|
11859
|
-
* @param {number} [exp] The expiration time for the token expressed in the number of seconds since the epoch
|
|
11860
|
-
* @param call_cids for anonymous tokens you have to provide the call cids the use can join
|
|
11861
|
-
*
|
|
11862
|
-
* @return {string} Returns a token
|
|
11863
|
-
*/
|
|
11864
|
-
createToken(userID, exp, iat, call_cids) {
|
|
11865
|
-
if (this.secret == null) {
|
|
11866
|
-
throw Error(`tokens can only be created server-side using the API Secret`);
|
|
11867
|
-
}
|
|
11868
|
-
const extra = {};
|
|
11869
|
-
if (exp) {
|
|
11870
|
-
extra.exp = exp;
|
|
11871
|
-
}
|
|
11872
|
-
if (iat) {
|
|
11873
|
-
extra.iat = iat;
|
|
11874
|
-
}
|
|
11875
|
-
if (call_cids) {
|
|
11876
|
-
extra.call_cids = call_cids;
|
|
11877
|
-
}
|
|
11878
|
-
return JWTUserToken(this.secret, userID, extra, {});
|
|
11879
|
-
}
|
|
11880
11885
|
}
|
|
11881
11886
|
|
|
11882
11887
|
/**
|
|
@@ -12173,7 +12178,7 @@ class StreamVideoClient {
|
|
|
12173
12178
|
return;
|
|
12174
12179
|
const { call, members } = event;
|
|
12175
12180
|
if (userToConnect.id === call.created_by.id) {
|
|
12176
|
-
this.logger('
|
|
12181
|
+
this.logger('debug', 'Received `call.ring` sent by the current user so ignoring the event');
|
|
12177
12182
|
return;
|
|
12178
12183
|
}
|
|
12179
12184
|
// The call might already be tracked by the client,
|