@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.cjs.js
CHANGED
|
@@ -6303,7 +6303,7 @@ class Publisher {
|
|
|
6303
6303
|
this.onIceCandidate = (e) => __awaiter(this, void 0, void 0, function* () {
|
|
6304
6304
|
const { candidate } = e;
|
|
6305
6305
|
if (!candidate) {
|
|
6306
|
-
this.logger('
|
|
6306
|
+
this.logger('debug', 'null ice candidate');
|
|
6307
6307
|
return;
|
|
6308
6308
|
}
|
|
6309
6309
|
yield this.sfuClient.iceTrickle({
|
|
@@ -6463,10 +6463,10 @@ class Publisher {
|
|
|
6463
6463
|
this.logger('error', `ICE Candidate error`, errorMessage);
|
|
6464
6464
|
};
|
|
6465
6465
|
this.onIceConnectionStateChange = () => {
|
|
6466
|
-
this.logger('
|
|
6466
|
+
this.logger('debug', `ICE Connection state changed`, this.pc.iceConnectionState);
|
|
6467
6467
|
};
|
|
6468
6468
|
this.onIceGatheringStateChange = () => {
|
|
6469
|
-
this.logger('
|
|
6469
|
+
this.logger('debug', `ICE Gathering State`, this.pc.iceGatheringState);
|
|
6470
6470
|
};
|
|
6471
6471
|
this.onSignalingStateChange = () => {
|
|
6472
6472
|
this.logger('debug', `Signaling state changed`, this.pc.signalingState);
|
|
@@ -6633,7 +6633,7 @@ class Subscriber {
|
|
|
6633
6633
|
this.onIceCandidate = (e) => __awaiter(this, void 0, void 0, function* () {
|
|
6634
6634
|
const { candidate } = e;
|
|
6635
6635
|
if (!candidate) {
|
|
6636
|
-
logger$1('
|
|
6636
|
+
logger$1('debug', 'null ice candidate');
|
|
6637
6637
|
return;
|
|
6638
6638
|
}
|
|
6639
6639
|
yield this.sfuClient.iceTrickle({
|
|
@@ -6868,7 +6868,7 @@ class StreamSfuClient {
|
|
|
6868
6868
|
* from this SFU.
|
|
6869
6869
|
*/
|
|
6870
6870
|
this.isMigratingAway = false;
|
|
6871
|
-
this.pingIntervalInMs =
|
|
6871
|
+
this.pingIntervalInMs = 10 * 1000;
|
|
6872
6872
|
this.unhealthyTimeoutInMs = this.pingIntervalInMs + 5 * 1000;
|
|
6873
6873
|
this.close = (code = 1000, reason = 'Requested signal connection close') => {
|
|
6874
6874
|
this.signalWs.close(code, reason);
|
|
@@ -6942,7 +6942,7 @@ class StreamSfuClient {
|
|
|
6942
6942
|
if (this.lastMessageTimestamp) {
|
|
6943
6943
|
const timeSinceLastMessage = new Date().getTime() - this.lastMessageTimestamp.getTime();
|
|
6944
6944
|
if (timeSinceLastMessage > this.unhealthyTimeoutInMs) {
|
|
6945
|
-
this.logger('
|
|
6945
|
+
this.logger('debug', 'SFU connection unhealthy, closing');
|
|
6946
6946
|
this.close(4001, `SFU connection unhealthy. Didn't receive any healthcheck messages for ${this.unhealthyTimeoutInMs}ms`);
|
|
6947
6947
|
}
|
|
6948
6948
|
}
|
|
@@ -8049,6 +8049,16 @@ const watchParticipantCountChanged = (dispatcher, state) => {
|
|
|
8049
8049
|
}
|
|
8050
8050
|
});
|
|
8051
8051
|
};
|
|
8052
|
+
const watchLiveEnded = (dispatcher, call) => {
|
|
8053
|
+
return dispatcher.on('error', (e) => {
|
|
8054
|
+
if (e.eventPayload.oneofKind !== 'error' ||
|
|
8055
|
+
!e.eventPayload.error.error ||
|
|
8056
|
+
e.eventPayload.error.error.code !== ErrorCode.LIVE_ENDED)
|
|
8057
|
+
return;
|
|
8058
|
+
if (!call.permissionsContext.hasPermission(OwnCapability.JOIN_BACKSTAGE))
|
|
8059
|
+
call.leave();
|
|
8060
|
+
});
|
|
8061
|
+
};
|
|
8052
8062
|
/**
|
|
8053
8063
|
* Watches and logs the errors reported by the currently connected SFU.
|
|
8054
8064
|
*/
|
|
@@ -8438,6 +8448,7 @@ const registerEventHandlers = (call, state, dispatcher) => {
|
|
|
8438
8448
|
'call.user_muted': () => console.log('call.user_muted received'),
|
|
8439
8449
|
};
|
|
8440
8450
|
const eventHandlers = [
|
|
8451
|
+
watchLiveEnded(dispatcher, call),
|
|
8441
8452
|
watchSfuErrorReports(dispatcher),
|
|
8442
8453
|
watchChangePublishQuality(dispatcher, call),
|
|
8443
8454
|
watchConnectionQualityChanged(dispatcher, state),
|
|
@@ -8696,7 +8707,7 @@ const createStatsReporter = ({ subscriber, publisher, state, pollingIntervalInMs
|
|
|
8696
8707
|
if (pollingIntervalInMs > 0) {
|
|
8697
8708
|
const loop = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
8698
8709
|
yield run().catch((e) => {
|
|
8699
|
-
logger('
|
|
8710
|
+
logger('debug', 'Failed to collect stats', e);
|
|
8700
8711
|
});
|
|
8701
8712
|
timeoutId = setTimeout(loop, pollingIntervalInMs);
|
|
8702
8713
|
});
|
|
@@ -9104,13 +9115,6 @@ const getClientDetails = () => {
|
|
|
9104
9115
|
* An object representation of a `Call`.
|
|
9105
9116
|
*/
|
|
9106
9117
|
class Call {
|
|
9107
|
-
/**
|
|
9108
|
-
* A promise that exposes the reconnection logic
|
|
9109
|
-
* The use-case is for the react-native platform where online/offline events are not available in the window
|
|
9110
|
-
*/
|
|
9111
|
-
get rejoin() {
|
|
9112
|
-
return this.rejoinPromise;
|
|
9113
|
-
}
|
|
9114
9118
|
/**
|
|
9115
9119
|
* Constructs a new `Call` instance.
|
|
9116
9120
|
*
|
|
@@ -9156,7 +9160,6 @@ class Call {
|
|
|
9156
9160
|
if (callingState === exports.CallingState.LEFT) {
|
|
9157
9161
|
throw new Error('Cannot leave call that has already been left.');
|
|
9158
9162
|
}
|
|
9159
|
-
this.rejoinPromise = undefined;
|
|
9160
9163
|
if (this.ringing) {
|
|
9161
9164
|
// I'm the one who started the call, so I should cancel it.
|
|
9162
9165
|
const hasOtherParticipants = this.state.remoteParticipants.length > 0;
|
|
@@ -9360,7 +9363,7 @@ class Call {
|
|
|
9360
9363
|
// we shouldn't be republishing the streams if we're migrating
|
|
9361
9364
|
// as the underlying peer connection will take care of it as part
|
|
9362
9365
|
// of the ice-restart process
|
|
9363
|
-
if (localParticipant && !
|
|
9366
|
+
if (localParticipant && !migrate) {
|
|
9364
9367
|
const { audioStream, videoStream, screenShareStream: screenShare, } = localParticipant;
|
|
9365
9368
|
// restore previous publishing state
|
|
9366
9369
|
if (audioStream)
|
|
@@ -9372,7 +9375,6 @@ class Call {
|
|
|
9372
9375
|
}
|
|
9373
9376
|
this.logger('info', `[Rejoin]: State restored. Attempt: ${this.reconnectAttempts}`);
|
|
9374
9377
|
});
|
|
9375
|
-
this.rejoinPromise = rejoin;
|
|
9376
9378
|
// reconnect if the connection was closed unexpectedly. example:
|
|
9377
9379
|
// - SFU crash or restart
|
|
9378
9380
|
// - network change
|
|
@@ -9405,9 +9407,6 @@ class Call {
|
|
|
9405
9407
|
if (e.code === KnownCodes.WS_CLOSED_ABRUPTLY &&
|
|
9406
9408
|
sfuClient.isMigratingAway)
|
|
9407
9409
|
return;
|
|
9408
|
-
// do nothing for react-native as it is handled by SDK
|
|
9409
|
-
if (isReactNative())
|
|
9410
|
-
return;
|
|
9411
9410
|
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
9412
9411
|
rejoin().catch((err) => {
|
|
9413
9412
|
this.logger('error', `[Rejoin]: Rejoin failed for ${this.reconnectAttempts} times. Giving up.`, err);
|
|
@@ -9421,28 +9420,34 @@ class Call {
|
|
|
9421
9420
|
});
|
|
9422
9421
|
});
|
|
9423
9422
|
// handlers for connection online/offline events
|
|
9424
|
-
|
|
9425
|
-
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
|
|
9429
|
-
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9423
|
+
const unsubscribeOnlineEvent = this.streamClient.on('connection.changed', (e) => {
|
|
9424
|
+
if (e.type !== 'connection.changed')
|
|
9425
|
+
return;
|
|
9426
|
+
if (!e.online)
|
|
9427
|
+
return;
|
|
9428
|
+
unsubscribeOnlineEvent();
|
|
9429
|
+
const currentCallingState = this.state.callingState;
|
|
9430
|
+
if (currentCallingState === exports.CallingState.OFFLINE ||
|
|
9431
|
+
currentCallingState === exports.CallingState.RECONNECTING_FAILED) {
|
|
9432
|
+
this.logger('info', '[Rejoin]: Going online...');
|
|
9433
|
+
rejoin().catch((err) => {
|
|
9434
|
+
this.logger('error', `[Rejoin]: Rejoin failed for ${this.reconnectAttempts} times. Giving up.`, err);
|
|
9435
|
+
this.state.setCallingState(exports.CallingState.RECONNECTING_FAILED);
|
|
9436
|
+
});
|
|
9437
|
+
}
|
|
9438
|
+
});
|
|
9439
|
+
const unsubscribeOfflineEvent = this.streamClient.on('connection.changed', (e) => {
|
|
9440
|
+
if (e.type !== 'connection.changed')
|
|
9441
|
+
return;
|
|
9442
|
+
if (e.online)
|
|
9443
|
+
return;
|
|
9444
|
+
unsubscribeOfflineEvent();
|
|
9445
|
+
this.state.setCallingState(exports.CallingState.OFFLINE);
|
|
9446
|
+
});
|
|
9447
|
+
this.leaveCallHooks.push(() => {
|
|
9448
|
+
unsubscribeOnlineEvent();
|
|
9449
|
+
unsubscribeOfflineEvent();
|
|
9450
|
+
});
|
|
9446
9451
|
if (!this.subscriber) {
|
|
9447
9452
|
this.subscriber = new Subscriber({
|
|
9448
9453
|
sfuClient,
|
|
@@ -10291,6 +10296,9 @@ const isErrorEvent = (res) => res.error !== undefined;
|
|
|
10291
10296
|
*/
|
|
10292
10297
|
class StableWSConnection {
|
|
10293
10298
|
constructor(client) {
|
|
10299
|
+
this._log = (msg, extra = {}, level = 'info') => {
|
|
10300
|
+
this.client.logger(level, 'connection:' + msg, Object.assign({}, extra));
|
|
10301
|
+
};
|
|
10294
10302
|
this.setClient = (client) => {
|
|
10295
10303
|
this.client = client;
|
|
10296
10304
|
};
|
|
@@ -10319,7 +10327,8 @@ class StableWSConnection {
|
|
|
10319
10327
|
if (event.type === 'offline') {
|
|
10320
10328
|
// mark the connection as down
|
|
10321
10329
|
this._log('onlineStatusChanged() - Status changing to offline');
|
|
10322
|
-
|
|
10330
|
+
// we know that the app is offline so dispatch the unhealthy connection event immediately
|
|
10331
|
+
this._setHealth(false, true);
|
|
10323
10332
|
}
|
|
10324
10333
|
else if (event.type === 'online') {
|
|
10325
10334
|
// retry right now...
|
|
@@ -10447,13 +10456,14 @@ class StableWSConnection {
|
|
|
10447
10456
|
* Broadcasts an event in case the connection status changed.
|
|
10448
10457
|
*
|
|
10449
10458
|
* @param {boolean} healthy boolean indicating if the connection is healthy or not
|
|
10459
|
+
* @param {boolean} dispatchImmediately boolean indicating to dispatch event immediately even if the connection is unhealthy
|
|
10450
10460
|
*
|
|
10451
10461
|
*/
|
|
10452
|
-
this._setHealth = (healthy) => {
|
|
10462
|
+
this._setHealth = (healthy, dispatchImmediately = false) => {
|
|
10453
10463
|
if (healthy === this.isHealthy)
|
|
10454
10464
|
return;
|
|
10455
10465
|
this.isHealthy = healthy;
|
|
10456
|
-
if (this.isHealthy) {
|
|
10466
|
+
if (this.isHealthy || dispatchImmediately) {
|
|
10457
10467
|
this.client.dispatchEvent({
|
|
10458
10468
|
type: 'connection.changed',
|
|
10459
10469
|
online: this.isHealthy,
|
|
@@ -10575,9 +10585,6 @@ class StableWSConnection {
|
|
|
10575
10585
|
this.connectionCheckTimeout = this.pingInterval + 10 * 1000;
|
|
10576
10586
|
addConnectionEventListeners(this.onlineStatusChanged);
|
|
10577
10587
|
}
|
|
10578
|
-
_log(msg, extra = {}, level = 'info') {
|
|
10579
|
-
this.client.logger(level, 'connection:' + msg, Object.assign({}, extra));
|
|
10580
|
-
}
|
|
10581
10588
|
/**
|
|
10582
10589
|
* connect - Connect to the WS URL
|
|
10583
10590
|
* the default 15s timeout allows between 2~3 tries
|
|
@@ -11329,6 +11336,18 @@ class StreamClient {
|
|
|
11329
11336
|
constructor(key, options) {
|
|
11330
11337
|
var _a;
|
|
11331
11338
|
this.nextRequestAbortController = null;
|
|
11339
|
+
this.devToken = (userID) => {
|
|
11340
|
+
return DevToken(userID);
|
|
11341
|
+
};
|
|
11342
|
+
this.getAuthType = () => {
|
|
11343
|
+
return this.anonymous ? 'anonymous' : 'jwt';
|
|
11344
|
+
};
|
|
11345
|
+
this.setBaseURL = (baseURL) => {
|
|
11346
|
+
this.baseURL = baseURL;
|
|
11347
|
+
this.wsBaseURL = this.baseURL
|
|
11348
|
+
.replace('http', 'ws')
|
|
11349
|
+
.replace(':3030', ':8800');
|
|
11350
|
+
};
|
|
11332
11351
|
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); };
|
|
11333
11352
|
this._hasConnectionID = () => Boolean(this._getConnectionID());
|
|
11334
11353
|
/**
|
|
@@ -11393,6 +11412,16 @@ class StreamClient {
|
|
|
11393
11412
|
}
|
|
11394
11413
|
});
|
|
11395
11414
|
this._setToken = (user, userTokenOrProvider, isAnonymous) => this.tokenManager.setTokenOrProvider(userTokenOrProvider, user, isAnonymous);
|
|
11415
|
+
this._setUser = (user) => {
|
|
11416
|
+
/**
|
|
11417
|
+
* This one is used by the frontend. This is a copy of the current user object stored on backend.
|
|
11418
|
+
* It contains reserved properties and own user properties which are not present in `this._user`.
|
|
11419
|
+
*/
|
|
11420
|
+
this.user = user;
|
|
11421
|
+
this.userID = user.id;
|
|
11422
|
+
// this one is actually used for requests. This is a copy of current user provided to `connectUser` function.
|
|
11423
|
+
this._user = Object.assign({}, user);
|
|
11424
|
+
};
|
|
11396
11425
|
/**
|
|
11397
11426
|
* Disconnects the websocket connection, without removing the user set on client.
|
|
11398
11427
|
* client.closeConnection will not trigger default auto-retry mechanism for reconnection. You need
|
|
@@ -11481,6 +11510,66 @@ class StreamClient {
|
|
|
11481
11510
|
// resolve the connection_id here.
|
|
11482
11511
|
this.resolveConnectionId();
|
|
11483
11512
|
});
|
|
11513
|
+
/**
|
|
11514
|
+
* on - Listen to events on all channels and users your watching
|
|
11515
|
+
*
|
|
11516
|
+
* client.on('message.new', event => {console.log("my new message", event, channel.state.messages)})
|
|
11517
|
+
* or
|
|
11518
|
+
* client.on(event => {console.log(event.type)})
|
|
11519
|
+
*
|
|
11520
|
+
* @param {EventHandler | string} callbackOrEventName The event type to listen for (optional)
|
|
11521
|
+
* @param {EventHandler} [callbackOrNothing] The callback to call
|
|
11522
|
+
*
|
|
11523
|
+
* @return {Function} Returns a function which, when called, unsubscribes the event handler.
|
|
11524
|
+
*/
|
|
11525
|
+
this.on = (callbackOrEventName, callbackOrNothing) => {
|
|
11526
|
+
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11527
|
+
const callback = callbackOrNothing
|
|
11528
|
+
? callbackOrNothing
|
|
11529
|
+
: callbackOrEventName;
|
|
11530
|
+
if (!(key in this.listeners)) {
|
|
11531
|
+
this.listeners[key] = [];
|
|
11532
|
+
}
|
|
11533
|
+
this.listeners[key].push(callback);
|
|
11534
|
+
return () => {
|
|
11535
|
+
this.off(key, callback);
|
|
11536
|
+
};
|
|
11537
|
+
};
|
|
11538
|
+
/**
|
|
11539
|
+
* off - Remove the event handler
|
|
11540
|
+
*
|
|
11541
|
+
*/
|
|
11542
|
+
this.off = (callbackOrEventName, callbackOrNothing) => {
|
|
11543
|
+
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11544
|
+
const callback = callbackOrNothing
|
|
11545
|
+
? callbackOrNothing
|
|
11546
|
+
: callbackOrEventName;
|
|
11547
|
+
if (!(key in this.listeners)) {
|
|
11548
|
+
this.listeners[key] = [];
|
|
11549
|
+
}
|
|
11550
|
+
this.logger('debug', `Removing listener for ${key} event`);
|
|
11551
|
+
this.listeners[key] = this.listeners[key].filter((value) => value !== callback);
|
|
11552
|
+
};
|
|
11553
|
+
this._logApiRequest = (type, url, data, config) => {
|
|
11554
|
+
this.logger('trace', `client: ${type} - Request - ${url}`, {
|
|
11555
|
+
payload: data,
|
|
11556
|
+
config,
|
|
11557
|
+
});
|
|
11558
|
+
};
|
|
11559
|
+
this._logApiResponse = (type, url, response) => {
|
|
11560
|
+
this.logger('trace', `client:${type} - Response - url: ${url} > status ${response.status}`, {
|
|
11561
|
+
response,
|
|
11562
|
+
});
|
|
11563
|
+
this.logger('trace', `client:${type} - Response payload`, {
|
|
11564
|
+
response,
|
|
11565
|
+
});
|
|
11566
|
+
};
|
|
11567
|
+
this._logApiError = (type, url, error) => {
|
|
11568
|
+
this.logger('error', `client:${type} - Error - url: ${url}`, {
|
|
11569
|
+
url,
|
|
11570
|
+
error,
|
|
11571
|
+
});
|
|
11572
|
+
};
|
|
11484
11573
|
this.doAxiosRequest = (type, url, data, options = {}) => __awaiter(this, void 0, void 0, function* () {
|
|
11485
11574
|
var _g;
|
|
11486
11575
|
if (!options.publicEndpoint || this.user) {
|
|
@@ -11545,6 +11634,43 @@ class StreamClient {
|
|
|
11545
11634
|
}
|
|
11546
11635
|
}
|
|
11547
11636
|
});
|
|
11637
|
+
this.get = (url, params) => {
|
|
11638
|
+
return this.doAxiosRequest('get', url, null, {
|
|
11639
|
+
params,
|
|
11640
|
+
});
|
|
11641
|
+
};
|
|
11642
|
+
this.put = (url, data) => {
|
|
11643
|
+
return this.doAxiosRequest('put', url, data);
|
|
11644
|
+
};
|
|
11645
|
+
this.post = (url, data) => {
|
|
11646
|
+
return this.doAxiosRequest('post', url, data);
|
|
11647
|
+
};
|
|
11648
|
+
this.patch = (url, data) => {
|
|
11649
|
+
return this.doAxiosRequest('patch', url, data);
|
|
11650
|
+
};
|
|
11651
|
+
this.delete = (url, params) => {
|
|
11652
|
+
return this.doAxiosRequest('delete', url, null, {
|
|
11653
|
+
params,
|
|
11654
|
+
});
|
|
11655
|
+
};
|
|
11656
|
+
this.errorFromResponse = (response) => {
|
|
11657
|
+
let err;
|
|
11658
|
+
err = new ErrorFromResponse(`Stream error HTTP code: ${response.status}`);
|
|
11659
|
+
if (response.data && response.data.code) {
|
|
11660
|
+
err = new Error(`Stream error code ${response.data.code}: ${response.data.message}`);
|
|
11661
|
+
err.code = response.data.code;
|
|
11662
|
+
}
|
|
11663
|
+
err.response = response;
|
|
11664
|
+
err.status = response.status;
|
|
11665
|
+
return err;
|
|
11666
|
+
};
|
|
11667
|
+
this.handleResponse = (response) => {
|
|
11668
|
+
const data = response.data;
|
|
11669
|
+
if (isErrorResponse(response)) {
|
|
11670
|
+
throw this.errorFromResponse(response);
|
|
11671
|
+
}
|
|
11672
|
+
return data;
|
|
11673
|
+
};
|
|
11548
11674
|
this.dispatchEvent = (event) => {
|
|
11549
11675
|
if (!event.received_at)
|
|
11550
11676
|
event.received_at = new Date();
|
|
@@ -11572,10 +11698,119 @@ class StreamClient {
|
|
|
11572
11698
|
listener(event);
|
|
11573
11699
|
}
|
|
11574
11700
|
};
|
|
11701
|
+
/**
|
|
11702
|
+
* @private
|
|
11703
|
+
*/
|
|
11704
|
+
this.connect = () => __awaiter(this, void 0, void 0, function* () {
|
|
11705
|
+
if (!this.userID || !this._user) {
|
|
11706
|
+
throw Error('Call connectUser or connectAnonymousUser before starting the connection');
|
|
11707
|
+
}
|
|
11708
|
+
if (!this.wsBaseURL) {
|
|
11709
|
+
throw Error('Websocket base url not set');
|
|
11710
|
+
}
|
|
11711
|
+
if (!this.clientID) {
|
|
11712
|
+
throw Error('clientID is not set');
|
|
11713
|
+
}
|
|
11714
|
+
if (!this.wsConnection &&
|
|
11715
|
+
(this.options.warmUp || this.options.enableInsights)) {
|
|
11716
|
+
this._sayHi();
|
|
11717
|
+
}
|
|
11718
|
+
// The StableWSConnection handles all the reconnection logic.
|
|
11719
|
+
if (this.options.wsConnection && this.node) {
|
|
11720
|
+
// Intentionally avoiding adding ts generics on wsConnection in options since its only useful for unit test purpose.
|
|
11721
|
+
this.options.wsConnection.setClient(this);
|
|
11722
|
+
this.wsConnection = this.options
|
|
11723
|
+
.wsConnection;
|
|
11724
|
+
}
|
|
11725
|
+
else {
|
|
11726
|
+
this.wsConnection = new StableWSConnection(this);
|
|
11727
|
+
}
|
|
11728
|
+
try {
|
|
11729
|
+
// if fallback is used before, continue using it instead of waiting for WS to fail
|
|
11730
|
+
if (this.wsFallback) {
|
|
11731
|
+
return yield this.wsFallback.connect();
|
|
11732
|
+
}
|
|
11733
|
+
this.logger('info', 'StreamClient.connect: this.wsConnection.connect()');
|
|
11734
|
+
// if WSFallback is enabled, ws connect should timeout faster so fallback can try
|
|
11735
|
+
return yield this.wsConnection.connect(this.options.enableWSFallback
|
|
11736
|
+
? this.defaultWSTimeoutWithFallback
|
|
11737
|
+
: this.defaultWSTimeout);
|
|
11738
|
+
}
|
|
11739
|
+
catch (err) {
|
|
11740
|
+
// run fallback only if it's WS/Network error and not a normal API error
|
|
11741
|
+
// make sure browser is online before even trying the longpoll
|
|
11742
|
+
if (this.options.enableWSFallback &&
|
|
11743
|
+
// @ts-ignore
|
|
11744
|
+
isWSFailure(err) &&
|
|
11745
|
+
isOnline(this.logger)) {
|
|
11746
|
+
this.logger('warn', 'client:connect() - WS failed, fallback to longpoll');
|
|
11747
|
+
this.dispatchEvent({ type: 'transport.changed', mode: 'longpoll' });
|
|
11748
|
+
this.wsConnection._destroyCurrentWSConnection();
|
|
11749
|
+
this.wsConnection.disconnect().then(); // close WS so no retry
|
|
11750
|
+
this.wsFallback = new WSConnectionFallback(this);
|
|
11751
|
+
return yield this.wsFallback.connect();
|
|
11752
|
+
}
|
|
11753
|
+
throw err;
|
|
11754
|
+
}
|
|
11755
|
+
});
|
|
11756
|
+
/**
|
|
11757
|
+
* Check the connectivity with server for warmup purpose.
|
|
11758
|
+
*
|
|
11759
|
+
* @private
|
|
11760
|
+
*/
|
|
11761
|
+
this._sayHi = () => {
|
|
11762
|
+
const client_request_id = randomId();
|
|
11763
|
+
const opts = {
|
|
11764
|
+
headers: axios.AxiosHeaders.from({
|
|
11765
|
+
'x-client-request-id': client_request_id,
|
|
11766
|
+
}),
|
|
11767
|
+
};
|
|
11768
|
+
this.doAxiosRequest('get', this.baseURL + '/hi', null, opts).catch((e) => {
|
|
11769
|
+
if (this.options.enableInsights) {
|
|
11770
|
+
postInsights('http_hi_failed', {
|
|
11771
|
+
api_key: this.key,
|
|
11772
|
+
err: e,
|
|
11773
|
+
client_request_id,
|
|
11774
|
+
});
|
|
11775
|
+
}
|
|
11776
|
+
});
|
|
11777
|
+
};
|
|
11778
|
+
this.getUserAgent = () => {
|
|
11779
|
+
return (this.userAgent ||
|
|
11780
|
+
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.40"}`);
|
|
11781
|
+
};
|
|
11782
|
+
this.setUserAgent = (userAgent) => {
|
|
11783
|
+
this.userAgent = userAgent;
|
|
11784
|
+
};
|
|
11575
11785
|
/**
|
|
11576
11786
|
* _isUsingServerAuth - Returns true if we're using server side auth
|
|
11577
11787
|
*/
|
|
11578
11788
|
this._isUsingServerAuth = () => !!this.secret;
|
|
11789
|
+
this._enrichAxiosOptions = (options = {
|
|
11790
|
+
params: {},
|
|
11791
|
+
headers: {},
|
|
11792
|
+
config: {},
|
|
11793
|
+
}) => {
|
|
11794
|
+
var _a;
|
|
11795
|
+
const token = options.publicEndpoint && !this.user ? undefined : this._getToken();
|
|
11796
|
+
const authorization = token ? { Authorization: token } : undefined;
|
|
11797
|
+
let signal = null;
|
|
11798
|
+
if (this.nextRequestAbortController !== null) {
|
|
11799
|
+
signal = this.nextRequestAbortController.signal;
|
|
11800
|
+
this.nextRequestAbortController = null;
|
|
11801
|
+
}
|
|
11802
|
+
if (!((_a = options.headers) === null || _a === void 0 ? void 0 : _a['x-client-request-id'])) {
|
|
11803
|
+
options.headers = Object.assign(Object.assign({}, options.headers), { 'x-client-request-id': randomId() });
|
|
11804
|
+
}
|
|
11805
|
+
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
|
|
11806
|
+
? 'anonymous'
|
|
11807
|
+
: this.getAuthType(), 'X-Stream-Client': this.getUserAgent() }), options.headers) }, (signal ? { signal } : {})), options.config), this.options.axiosRequestConfig);
|
|
11808
|
+
};
|
|
11809
|
+
this._getToken = () => {
|
|
11810
|
+
if (!this.tokenManager)
|
|
11811
|
+
return null;
|
|
11812
|
+
return this.tokenManager.getToken();
|
|
11813
|
+
};
|
|
11579
11814
|
/**
|
|
11580
11815
|
* encode ws url payload
|
|
11581
11816
|
* @private
|
|
@@ -11588,6 +11823,38 @@ class StreamClient {
|
|
|
11588
11823
|
client_request_id,
|
|
11589
11824
|
});
|
|
11590
11825
|
};
|
|
11826
|
+
/**
|
|
11827
|
+
* creates an abort controller that will be used by the next HTTP Request.
|
|
11828
|
+
*/
|
|
11829
|
+
this.createAbortControllerForNextRequest = () => {
|
|
11830
|
+
return (this.nextRequestAbortController = new AbortController());
|
|
11831
|
+
};
|
|
11832
|
+
/**
|
|
11833
|
+
* createToken - Creates a token to authenticate this user. This function is used server side.
|
|
11834
|
+
* The resulting token should be passed to the client side when the users registers or logs in.
|
|
11835
|
+
*
|
|
11836
|
+
* @param {string} userID The UserWithId ID
|
|
11837
|
+
* @param {number} [exp] The expiration time for the token expressed in the number of seconds since the epoch
|
|
11838
|
+
* @param call_cids for anonymous tokens you have to provide the call cids the use can join
|
|
11839
|
+
*
|
|
11840
|
+
* @return {string} Returns a token
|
|
11841
|
+
*/
|
|
11842
|
+
this.createToken = (userID, exp, iat, call_cids) => {
|
|
11843
|
+
if (this.secret == null) {
|
|
11844
|
+
throw Error(`tokens can only be created server-side using the API Secret`);
|
|
11845
|
+
}
|
|
11846
|
+
const extra = {};
|
|
11847
|
+
if (exp) {
|
|
11848
|
+
extra.exp = exp;
|
|
11849
|
+
}
|
|
11850
|
+
if (iat) {
|
|
11851
|
+
extra.iat = iat;
|
|
11852
|
+
}
|
|
11853
|
+
if (call_cids) {
|
|
11854
|
+
extra.call_cids = call_cids;
|
|
11855
|
+
}
|
|
11856
|
+
return JWTUserToken(this.secret, userID, extra, {});
|
|
11857
|
+
};
|
|
11591
11858
|
// set the key
|
|
11592
11859
|
this.key = key;
|
|
11593
11860
|
this.listeners = {};
|
|
@@ -11639,268 +11906,6 @@ class StreamClient {
|
|
|
11639
11906
|
? inputOptions.logger
|
|
11640
11907
|
: () => null;
|
|
11641
11908
|
}
|
|
11642
|
-
devToken(userID) {
|
|
11643
|
-
return DevToken(userID);
|
|
11644
|
-
}
|
|
11645
|
-
getAuthType() {
|
|
11646
|
-
return this.anonymous ? 'anonymous' : 'jwt';
|
|
11647
|
-
}
|
|
11648
|
-
setBaseURL(baseURL) {
|
|
11649
|
-
this.baseURL = baseURL;
|
|
11650
|
-
this.wsBaseURL = this.baseURL
|
|
11651
|
-
.replace('http', 'ws')
|
|
11652
|
-
.replace(':3030', ':8800');
|
|
11653
|
-
}
|
|
11654
|
-
_setUser(user) {
|
|
11655
|
-
/**
|
|
11656
|
-
* This one is used by the frontend. This is a copy of the current user object stored on backend.
|
|
11657
|
-
* It contains reserved properties and own user properties which are not present in `this._user`.
|
|
11658
|
-
*/
|
|
11659
|
-
this.user = user;
|
|
11660
|
-
this.userID = user.id;
|
|
11661
|
-
// this one is actually used for requests. This is a copy of current user provided to `connectUser` function.
|
|
11662
|
-
this._user = Object.assign({}, user);
|
|
11663
|
-
}
|
|
11664
|
-
/**
|
|
11665
|
-
* on - Listen to events on all channels and users your watching
|
|
11666
|
-
*
|
|
11667
|
-
* client.on('message.new', event => {console.log("my new message", event, channel.state.messages)})
|
|
11668
|
-
* or
|
|
11669
|
-
* client.on(event => {console.log(event.type)})
|
|
11670
|
-
*
|
|
11671
|
-
* @param {EventHandler | string} callbackOrEventName The event type to listen for (optional)
|
|
11672
|
-
* @param {EventHandler} [callbackOrNothing] The callback to call
|
|
11673
|
-
*
|
|
11674
|
-
* @return {Function} Returns a function which, when called, unsubscribes the event handler.
|
|
11675
|
-
*/
|
|
11676
|
-
on(callbackOrEventName, callbackOrNothing) {
|
|
11677
|
-
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11678
|
-
const callback = callbackOrNothing
|
|
11679
|
-
? callbackOrNothing
|
|
11680
|
-
: callbackOrEventName;
|
|
11681
|
-
if (!(key in this.listeners)) {
|
|
11682
|
-
this.listeners[key] = [];
|
|
11683
|
-
}
|
|
11684
|
-
this.listeners[key].push(callback);
|
|
11685
|
-
return () => {
|
|
11686
|
-
this.off(key, callback);
|
|
11687
|
-
};
|
|
11688
|
-
}
|
|
11689
|
-
/**
|
|
11690
|
-
* off - Remove the event handler
|
|
11691
|
-
*
|
|
11692
|
-
*/
|
|
11693
|
-
off(callbackOrEventName, callbackOrNothing) {
|
|
11694
|
-
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11695
|
-
const callback = callbackOrNothing
|
|
11696
|
-
? callbackOrNothing
|
|
11697
|
-
: callbackOrEventName;
|
|
11698
|
-
if (!(key in this.listeners)) {
|
|
11699
|
-
this.listeners[key] = [];
|
|
11700
|
-
}
|
|
11701
|
-
this.logger('debug', `Removing listener for ${key} event`);
|
|
11702
|
-
this.listeners[key] = this.listeners[key].filter((value) => value !== callback);
|
|
11703
|
-
}
|
|
11704
|
-
_logApiRequest(type, url, data, config) {
|
|
11705
|
-
this.logger('trace', `client: ${type} - Request - ${url}`, {
|
|
11706
|
-
payload: data,
|
|
11707
|
-
config,
|
|
11708
|
-
});
|
|
11709
|
-
}
|
|
11710
|
-
_logApiResponse(type, url, response) {
|
|
11711
|
-
this.logger('trace', `client:${type} - Response - url: ${url} > status ${response.status}`, {
|
|
11712
|
-
response,
|
|
11713
|
-
});
|
|
11714
|
-
this.logger('trace', `client:${type} - Response payload`, {
|
|
11715
|
-
response,
|
|
11716
|
-
});
|
|
11717
|
-
}
|
|
11718
|
-
_logApiError(type, url, error) {
|
|
11719
|
-
this.logger('error', `client:${type} - Error - url: ${url}`, {
|
|
11720
|
-
url,
|
|
11721
|
-
error,
|
|
11722
|
-
});
|
|
11723
|
-
}
|
|
11724
|
-
get(url, params) {
|
|
11725
|
-
return this.doAxiosRequest('get', url, null, {
|
|
11726
|
-
params,
|
|
11727
|
-
});
|
|
11728
|
-
}
|
|
11729
|
-
put(url, data) {
|
|
11730
|
-
return this.doAxiosRequest('put', url, data);
|
|
11731
|
-
}
|
|
11732
|
-
post(url, data) {
|
|
11733
|
-
return this.doAxiosRequest('post', url, data);
|
|
11734
|
-
}
|
|
11735
|
-
patch(url, data) {
|
|
11736
|
-
return this.doAxiosRequest('patch', url, data);
|
|
11737
|
-
}
|
|
11738
|
-
delete(url, params) {
|
|
11739
|
-
return this.doAxiosRequest('delete', url, null, {
|
|
11740
|
-
params,
|
|
11741
|
-
});
|
|
11742
|
-
}
|
|
11743
|
-
errorFromResponse(response) {
|
|
11744
|
-
let err;
|
|
11745
|
-
err = new ErrorFromResponse(`Stream error HTTP code: ${response.status}`);
|
|
11746
|
-
if (response.data && response.data.code) {
|
|
11747
|
-
err = new Error(`Stream error code ${response.data.code}: ${response.data.message}`);
|
|
11748
|
-
err.code = response.data.code;
|
|
11749
|
-
}
|
|
11750
|
-
err.response = response;
|
|
11751
|
-
err.status = response.status;
|
|
11752
|
-
return err;
|
|
11753
|
-
}
|
|
11754
|
-
handleResponse(response) {
|
|
11755
|
-
const data = response.data;
|
|
11756
|
-
if (isErrorResponse(response)) {
|
|
11757
|
-
throw this.errorFromResponse(response);
|
|
11758
|
-
}
|
|
11759
|
-
return data;
|
|
11760
|
-
}
|
|
11761
|
-
/**
|
|
11762
|
-
* @private
|
|
11763
|
-
*/
|
|
11764
|
-
connect() {
|
|
11765
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
11766
|
-
if (!this.userID || !this._user) {
|
|
11767
|
-
throw Error('Call connectUser or connectAnonymousUser before starting the connection');
|
|
11768
|
-
}
|
|
11769
|
-
if (!this.wsBaseURL) {
|
|
11770
|
-
throw Error('Websocket base url not set');
|
|
11771
|
-
}
|
|
11772
|
-
if (!this.clientID) {
|
|
11773
|
-
throw Error('clientID is not set');
|
|
11774
|
-
}
|
|
11775
|
-
if (!this.wsConnection &&
|
|
11776
|
-
(this.options.warmUp || this.options.enableInsights)) {
|
|
11777
|
-
this._sayHi();
|
|
11778
|
-
}
|
|
11779
|
-
// The StableWSConnection handles all the reconnection logic.
|
|
11780
|
-
if (this.options.wsConnection && this.node) {
|
|
11781
|
-
// Intentionally avoiding adding ts generics on wsConnection in options since its only useful for unit test purpose.
|
|
11782
|
-
this.options.wsConnection.setClient(this);
|
|
11783
|
-
this.wsConnection = this.options
|
|
11784
|
-
.wsConnection;
|
|
11785
|
-
}
|
|
11786
|
-
else {
|
|
11787
|
-
this.wsConnection = new StableWSConnection(this);
|
|
11788
|
-
}
|
|
11789
|
-
try {
|
|
11790
|
-
// if fallback is used before, continue using it instead of waiting for WS to fail
|
|
11791
|
-
if (this.wsFallback) {
|
|
11792
|
-
return yield this.wsFallback.connect();
|
|
11793
|
-
}
|
|
11794
|
-
this.logger('info', 'StreamClient.connect: this.wsConnection.connect()');
|
|
11795
|
-
// if WSFallback is enabled, ws connect should timeout faster so fallback can try
|
|
11796
|
-
return yield this.wsConnection.connect(this.options.enableWSFallback
|
|
11797
|
-
? this.defaultWSTimeoutWithFallback
|
|
11798
|
-
: this.defaultWSTimeout);
|
|
11799
|
-
}
|
|
11800
|
-
catch (err) {
|
|
11801
|
-
// run fallback only if it's WS/Network error and not a normal API error
|
|
11802
|
-
// make sure browser is online before even trying the longpoll
|
|
11803
|
-
if (this.options.enableWSFallback &&
|
|
11804
|
-
// @ts-ignore
|
|
11805
|
-
isWSFailure(err) &&
|
|
11806
|
-
isOnline(this.logger)) {
|
|
11807
|
-
this.logger('warn', 'client:connect() - WS failed, fallback to longpoll');
|
|
11808
|
-
this.dispatchEvent({ type: 'transport.changed', mode: 'longpoll' });
|
|
11809
|
-
this.wsConnection._destroyCurrentWSConnection();
|
|
11810
|
-
this.wsConnection.disconnect().then(); // close WS so no retry
|
|
11811
|
-
this.wsFallback = new WSConnectionFallback(this);
|
|
11812
|
-
return yield this.wsFallback.connect();
|
|
11813
|
-
}
|
|
11814
|
-
throw err;
|
|
11815
|
-
}
|
|
11816
|
-
});
|
|
11817
|
-
}
|
|
11818
|
-
/**
|
|
11819
|
-
* Check the connectivity with server for warmup purpose.
|
|
11820
|
-
*
|
|
11821
|
-
* @private
|
|
11822
|
-
*/
|
|
11823
|
-
_sayHi() {
|
|
11824
|
-
const client_request_id = randomId();
|
|
11825
|
-
const opts = {
|
|
11826
|
-
headers: axios.AxiosHeaders.from({
|
|
11827
|
-
'x-client-request-id': client_request_id,
|
|
11828
|
-
}),
|
|
11829
|
-
};
|
|
11830
|
-
this.doAxiosRequest('get', this.baseURL + '/hi', null, opts).catch((e) => {
|
|
11831
|
-
if (this.options.enableInsights) {
|
|
11832
|
-
postInsights('http_hi_failed', {
|
|
11833
|
-
api_key: this.key,
|
|
11834
|
-
err: e,
|
|
11835
|
-
client_request_id,
|
|
11836
|
-
});
|
|
11837
|
-
}
|
|
11838
|
-
});
|
|
11839
|
-
}
|
|
11840
|
-
getUserAgent() {
|
|
11841
|
-
return (this.userAgent ||
|
|
11842
|
-
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.38"}`);
|
|
11843
|
-
}
|
|
11844
|
-
setUserAgent(userAgent) {
|
|
11845
|
-
this.userAgent = userAgent;
|
|
11846
|
-
}
|
|
11847
|
-
_enrichAxiosOptions(options = {
|
|
11848
|
-
params: {},
|
|
11849
|
-
headers: {},
|
|
11850
|
-
config: {},
|
|
11851
|
-
}) {
|
|
11852
|
-
var _a;
|
|
11853
|
-
const token = options.publicEndpoint && !this.user ? undefined : this._getToken();
|
|
11854
|
-
const authorization = token ? { Authorization: token } : undefined;
|
|
11855
|
-
let signal = null;
|
|
11856
|
-
if (this.nextRequestAbortController !== null) {
|
|
11857
|
-
signal = this.nextRequestAbortController.signal;
|
|
11858
|
-
this.nextRequestAbortController = null;
|
|
11859
|
-
}
|
|
11860
|
-
if (!((_a = options.headers) === null || _a === void 0 ? void 0 : _a['x-client-request-id'])) {
|
|
11861
|
-
options.headers = Object.assign(Object.assign({}, options.headers), { 'x-client-request-id': randomId() });
|
|
11862
|
-
}
|
|
11863
|
-
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
|
|
11864
|
-
? 'anonymous'
|
|
11865
|
-
: this.getAuthType(), 'X-Stream-Client': this.getUserAgent() }), options.headers) }, (signal ? { signal } : {})), options.config), this.options.axiosRequestConfig);
|
|
11866
|
-
}
|
|
11867
|
-
_getToken() {
|
|
11868
|
-
if (!this.tokenManager)
|
|
11869
|
-
return null;
|
|
11870
|
-
return this.tokenManager.getToken();
|
|
11871
|
-
}
|
|
11872
|
-
/**
|
|
11873
|
-
* creates an abort controller that will be used by the next HTTP Request.
|
|
11874
|
-
*/
|
|
11875
|
-
createAbortControllerForNextRequest() {
|
|
11876
|
-
return (this.nextRequestAbortController = new AbortController());
|
|
11877
|
-
}
|
|
11878
|
-
/**
|
|
11879
|
-
* createToken - Creates a token to authenticate this user. This function is used server side.
|
|
11880
|
-
* The resulting token should be passed to the client side when the users registers or logs in.
|
|
11881
|
-
*
|
|
11882
|
-
* @param {string} userID The UserWithId ID
|
|
11883
|
-
* @param {number} [exp] The expiration time for the token expressed in the number of seconds since the epoch
|
|
11884
|
-
* @param call_cids for anonymous tokens you have to provide the call cids the use can join
|
|
11885
|
-
*
|
|
11886
|
-
* @return {string} Returns a token
|
|
11887
|
-
*/
|
|
11888
|
-
createToken(userID, exp, iat, call_cids) {
|
|
11889
|
-
if (this.secret == null) {
|
|
11890
|
-
throw Error(`tokens can only be created server-side using the API Secret`);
|
|
11891
|
-
}
|
|
11892
|
-
const extra = {};
|
|
11893
|
-
if (exp) {
|
|
11894
|
-
extra.exp = exp;
|
|
11895
|
-
}
|
|
11896
|
-
if (iat) {
|
|
11897
|
-
extra.iat = iat;
|
|
11898
|
-
}
|
|
11899
|
-
if (call_cids) {
|
|
11900
|
-
extra.call_cids = call_cids;
|
|
11901
|
-
}
|
|
11902
|
-
return JWTUserToken(this.secret, userID, extra, {});
|
|
11903
|
-
}
|
|
11904
11909
|
}
|
|
11905
11910
|
|
|
11906
11911
|
/**
|
|
@@ -12197,7 +12202,7 @@ class StreamVideoClient {
|
|
|
12197
12202
|
return;
|
|
12198
12203
|
const { call, members } = event;
|
|
12199
12204
|
if (userToConnect.id === call.created_by.id) {
|
|
12200
|
-
this.logger('
|
|
12205
|
+
this.logger('debug', 'Received `call.ring` sent by the current user so ignoring the event');
|
|
12201
12206
|
return;
|
|
12202
12207
|
}
|
|
12203
12208
|
// The call might already be tracked by the client,
|