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