@stream-io/video-client 0.0.39 → 0.0.40
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 +7 -0
- package/dist/index.browser.es.js +298 -304
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +298 -304
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +298 -304
- 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/package.json +1 -1
- package/src/Call.ts +27 -38
- package/src/StreamSfuClient.ts +1 -1
- package/src/coordinator/connection/client.ts +58 -54
- package/src/coordinator/connection/connection.ts +7 -5
package/dist/index.cjs.js
CHANGED
|
@@ -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);
|
|
@@ -9104,13 +9104,6 @@ const getClientDetails = () => {
|
|
|
9104
9104
|
* An object representation of a `Call`.
|
|
9105
9105
|
*/
|
|
9106
9106
|
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
9107
|
/**
|
|
9115
9108
|
* Constructs a new `Call` instance.
|
|
9116
9109
|
*
|
|
@@ -9156,7 +9149,6 @@ class Call {
|
|
|
9156
9149
|
if (callingState === exports.CallingState.LEFT) {
|
|
9157
9150
|
throw new Error('Cannot leave call that has already been left.');
|
|
9158
9151
|
}
|
|
9159
|
-
this.rejoinPromise = undefined;
|
|
9160
9152
|
if (this.ringing) {
|
|
9161
9153
|
// I'm the one who started the call, so I should cancel it.
|
|
9162
9154
|
const hasOtherParticipants = this.state.remoteParticipants.length > 0;
|
|
@@ -9360,7 +9352,7 @@ class Call {
|
|
|
9360
9352
|
// we shouldn't be republishing the streams if we're migrating
|
|
9361
9353
|
// as the underlying peer connection will take care of it as part
|
|
9362
9354
|
// of the ice-restart process
|
|
9363
|
-
if (localParticipant && !
|
|
9355
|
+
if (localParticipant && !migrate) {
|
|
9364
9356
|
const { audioStream, videoStream, screenShareStream: screenShare, } = localParticipant;
|
|
9365
9357
|
// restore previous publishing state
|
|
9366
9358
|
if (audioStream)
|
|
@@ -9372,7 +9364,6 @@ class Call {
|
|
|
9372
9364
|
}
|
|
9373
9365
|
this.logger('info', `[Rejoin]: State restored. Attempt: ${this.reconnectAttempts}`);
|
|
9374
9366
|
});
|
|
9375
|
-
this.rejoinPromise = rejoin;
|
|
9376
9367
|
// reconnect if the connection was closed unexpectedly. example:
|
|
9377
9368
|
// - SFU crash or restart
|
|
9378
9369
|
// - network change
|
|
@@ -9405,9 +9396,6 @@ class Call {
|
|
|
9405
9396
|
if (e.code === KnownCodes.WS_CLOSED_ABRUPTLY &&
|
|
9406
9397
|
sfuClient.isMigratingAway)
|
|
9407
9398
|
return;
|
|
9408
|
-
// do nothing for react-native as it is handled by SDK
|
|
9409
|
-
if (isReactNative())
|
|
9410
|
-
return;
|
|
9411
9399
|
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
9412
9400
|
rejoin().catch((err) => {
|
|
9413
9401
|
this.logger('error', `[Rejoin]: Rejoin failed for ${this.reconnectAttempts} times. Giving up.`, err);
|
|
@@ -9421,28 +9409,34 @@ class Call {
|
|
|
9421
9409
|
});
|
|
9422
9410
|
});
|
|
9423
9411
|
// 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
|
-
|
|
9412
|
+
const unsubscribeOnlineEvent = this.streamClient.on('connection.changed', (e) => {
|
|
9413
|
+
if (e.type !== 'connection.changed')
|
|
9414
|
+
return;
|
|
9415
|
+
if (!e.online)
|
|
9416
|
+
return;
|
|
9417
|
+
unsubscribeOnlineEvent();
|
|
9418
|
+
const currentCallingState = this.state.callingState;
|
|
9419
|
+
if (currentCallingState === exports.CallingState.OFFLINE ||
|
|
9420
|
+
currentCallingState === exports.CallingState.RECONNECTING_FAILED) {
|
|
9421
|
+
this.logger('info', '[Rejoin]: Going online...');
|
|
9422
|
+
rejoin().catch((err) => {
|
|
9423
|
+
this.logger('error', `[Rejoin]: Rejoin failed for ${this.reconnectAttempts} times. Giving up.`, err);
|
|
9424
|
+
this.state.setCallingState(exports.CallingState.RECONNECTING_FAILED);
|
|
9425
|
+
});
|
|
9426
|
+
}
|
|
9427
|
+
});
|
|
9428
|
+
const unsubscribeOfflineEvent = this.streamClient.on('connection.changed', (e) => {
|
|
9429
|
+
if (e.type !== 'connection.changed')
|
|
9430
|
+
return;
|
|
9431
|
+
if (e.online)
|
|
9432
|
+
return;
|
|
9433
|
+
unsubscribeOfflineEvent();
|
|
9434
|
+
this.state.setCallingState(exports.CallingState.OFFLINE);
|
|
9435
|
+
});
|
|
9436
|
+
this.leaveCallHooks.push(() => {
|
|
9437
|
+
unsubscribeOnlineEvent();
|
|
9438
|
+
unsubscribeOfflineEvent();
|
|
9439
|
+
});
|
|
9446
9440
|
if (!this.subscriber) {
|
|
9447
9441
|
this.subscriber = new Subscriber({
|
|
9448
9442
|
sfuClient,
|
|
@@ -10291,6 +10285,9 @@ const isErrorEvent = (res) => res.error !== undefined;
|
|
|
10291
10285
|
*/
|
|
10292
10286
|
class StableWSConnection {
|
|
10293
10287
|
constructor(client) {
|
|
10288
|
+
this._log = (msg, extra = {}, level = 'info') => {
|
|
10289
|
+
this.client.logger(level, 'connection:' + msg, Object.assign({}, extra));
|
|
10290
|
+
};
|
|
10294
10291
|
this.setClient = (client) => {
|
|
10295
10292
|
this.client = client;
|
|
10296
10293
|
};
|
|
@@ -10319,7 +10316,8 @@ class StableWSConnection {
|
|
|
10319
10316
|
if (event.type === 'offline') {
|
|
10320
10317
|
// mark the connection as down
|
|
10321
10318
|
this._log('onlineStatusChanged() - Status changing to offline');
|
|
10322
|
-
|
|
10319
|
+
// we know that the app is offline so dispatch the unhealthy connection event immediately
|
|
10320
|
+
this._setHealth(false, true);
|
|
10323
10321
|
}
|
|
10324
10322
|
else if (event.type === 'online') {
|
|
10325
10323
|
// retry right now...
|
|
@@ -10447,13 +10445,14 @@ class StableWSConnection {
|
|
|
10447
10445
|
* Broadcasts an event in case the connection status changed.
|
|
10448
10446
|
*
|
|
10449
10447
|
* @param {boolean} healthy boolean indicating if the connection is healthy or not
|
|
10448
|
+
* @param {boolean} dispatchImmediately boolean indicating to dispatch event immediately even if the connection is unhealthy
|
|
10450
10449
|
*
|
|
10451
10450
|
*/
|
|
10452
|
-
this._setHealth = (healthy) => {
|
|
10451
|
+
this._setHealth = (healthy, dispatchImmediately = false) => {
|
|
10453
10452
|
if (healthy === this.isHealthy)
|
|
10454
10453
|
return;
|
|
10455
10454
|
this.isHealthy = healthy;
|
|
10456
|
-
if (this.isHealthy) {
|
|
10455
|
+
if (this.isHealthy || dispatchImmediately) {
|
|
10457
10456
|
this.client.dispatchEvent({
|
|
10458
10457
|
type: 'connection.changed',
|
|
10459
10458
|
online: this.isHealthy,
|
|
@@ -10575,9 +10574,6 @@ class StableWSConnection {
|
|
|
10575
10574
|
this.connectionCheckTimeout = this.pingInterval + 10 * 1000;
|
|
10576
10575
|
addConnectionEventListeners(this.onlineStatusChanged);
|
|
10577
10576
|
}
|
|
10578
|
-
_log(msg, extra = {}, level = 'info') {
|
|
10579
|
-
this.client.logger(level, 'connection:' + msg, Object.assign({}, extra));
|
|
10580
|
-
}
|
|
10581
10577
|
/**
|
|
10582
10578
|
* connect - Connect to the WS URL
|
|
10583
10579
|
* the default 15s timeout allows between 2~3 tries
|
|
@@ -11329,6 +11325,18 @@ class StreamClient {
|
|
|
11329
11325
|
constructor(key, options) {
|
|
11330
11326
|
var _a;
|
|
11331
11327
|
this.nextRequestAbortController = null;
|
|
11328
|
+
this.devToken = (userID) => {
|
|
11329
|
+
return DevToken(userID);
|
|
11330
|
+
};
|
|
11331
|
+
this.getAuthType = () => {
|
|
11332
|
+
return this.anonymous ? 'anonymous' : 'jwt';
|
|
11333
|
+
};
|
|
11334
|
+
this.setBaseURL = (baseURL) => {
|
|
11335
|
+
this.baseURL = baseURL;
|
|
11336
|
+
this.wsBaseURL = this.baseURL
|
|
11337
|
+
.replace('http', 'ws')
|
|
11338
|
+
.replace(':3030', ':8800');
|
|
11339
|
+
};
|
|
11332
11340
|
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
11341
|
this._hasConnectionID = () => Boolean(this._getConnectionID());
|
|
11334
11342
|
/**
|
|
@@ -11393,6 +11401,16 @@ class StreamClient {
|
|
|
11393
11401
|
}
|
|
11394
11402
|
});
|
|
11395
11403
|
this._setToken = (user, userTokenOrProvider, isAnonymous) => this.tokenManager.setTokenOrProvider(userTokenOrProvider, user, isAnonymous);
|
|
11404
|
+
this._setUser = (user) => {
|
|
11405
|
+
/**
|
|
11406
|
+
* This one is used by the frontend. This is a copy of the current user object stored on backend.
|
|
11407
|
+
* It contains reserved properties and own user properties which are not present in `this._user`.
|
|
11408
|
+
*/
|
|
11409
|
+
this.user = user;
|
|
11410
|
+
this.userID = user.id;
|
|
11411
|
+
// this one is actually used for requests. This is a copy of current user provided to `connectUser` function.
|
|
11412
|
+
this._user = Object.assign({}, user);
|
|
11413
|
+
};
|
|
11396
11414
|
/**
|
|
11397
11415
|
* Disconnects the websocket connection, without removing the user set on client.
|
|
11398
11416
|
* client.closeConnection will not trigger default auto-retry mechanism for reconnection. You need
|
|
@@ -11481,6 +11499,66 @@ class StreamClient {
|
|
|
11481
11499
|
// resolve the connection_id here.
|
|
11482
11500
|
this.resolveConnectionId();
|
|
11483
11501
|
});
|
|
11502
|
+
/**
|
|
11503
|
+
* on - Listen to events on all channels and users your watching
|
|
11504
|
+
*
|
|
11505
|
+
* client.on('message.new', event => {console.log("my new message", event, channel.state.messages)})
|
|
11506
|
+
* or
|
|
11507
|
+
* client.on(event => {console.log(event.type)})
|
|
11508
|
+
*
|
|
11509
|
+
* @param {EventHandler | string} callbackOrEventName The event type to listen for (optional)
|
|
11510
|
+
* @param {EventHandler} [callbackOrNothing] The callback to call
|
|
11511
|
+
*
|
|
11512
|
+
* @return {Function} Returns a function which, when called, unsubscribes the event handler.
|
|
11513
|
+
*/
|
|
11514
|
+
this.on = (callbackOrEventName, callbackOrNothing) => {
|
|
11515
|
+
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11516
|
+
const callback = callbackOrNothing
|
|
11517
|
+
? callbackOrNothing
|
|
11518
|
+
: callbackOrEventName;
|
|
11519
|
+
if (!(key in this.listeners)) {
|
|
11520
|
+
this.listeners[key] = [];
|
|
11521
|
+
}
|
|
11522
|
+
this.listeners[key].push(callback);
|
|
11523
|
+
return () => {
|
|
11524
|
+
this.off(key, callback);
|
|
11525
|
+
};
|
|
11526
|
+
};
|
|
11527
|
+
/**
|
|
11528
|
+
* off - Remove the event handler
|
|
11529
|
+
*
|
|
11530
|
+
*/
|
|
11531
|
+
this.off = (callbackOrEventName, callbackOrNothing) => {
|
|
11532
|
+
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11533
|
+
const callback = callbackOrNothing
|
|
11534
|
+
? callbackOrNothing
|
|
11535
|
+
: callbackOrEventName;
|
|
11536
|
+
if (!(key in this.listeners)) {
|
|
11537
|
+
this.listeners[key] = [];
|
|
11538
|
+
}
|
|
11539
|
+
this.logger('debug', `Removing listener for ${key} event`);
|
|
11540
|
+
this.listeners[key] = this.listeners[key].filter((value) => value !== callback);
|
|
11541
|
+
};
|
|
11542
|
+
this._logApiRequest = (type, url, data, config) => {
|
|
11543
|
+
this.logger('trace', `client: ${type} - Request - ${url}`, {
|
|
11544
|
+
payload: data,
|
|
11545
|
+
config,
|
|
11546
|
+
});
|
|
11547
|
+
};
|
|
11548
|
+
this._logApiResponse = (type, url, response) => {
|
|
11549
|
+
this.logger('trace', `client:${type} - Response - url: ${url} > status ${response.status}`, {
|
|
11550
|
+
response,
|
|
11551
|
+
});
|
|
11552
|
+
this.logger('trace', `client:${type} - Response payload`, {
|
|
11553
|
+
response,
|
|
11554
|
+
});
|
|
11555
|
+
};
|
|
11556
|
+
this._logApiError = (type, url, error) => {
|
|
11557
|
+
this.logger('error', `client:${type} - Error - url: ${url}`, {
|
|
11558
|
+
url,
|
|
11559
|
+
error,
|
|
11560
|
+
});
|
|
11561
|
+
};
|
|
11484
11562
|
this.doAxiosRequest = (type, url, data, options = {}) => __awaiter(this, void 0, void 0, function* () {
|
|
11485
11563
|
var _g;
|
|
11486
11564
|
if (!options.publicEndpoint || this.user) {
|
|
@@ -11545,6 +11623,43 @@ class StreamClient {
|
|
|
11545
11623
|
}
|
|
11546
11624
|
}
|
|
11547
11625
|
});
|
|
11626
|
+
this.get = (url, params) => {
|
|
11627
|
+
return this.doAxiosRequest('get', url, null, {
|
|
11628
|
+
params,
|
|
11629
|
+
});
|
|
11630
|
+
};
|
|
11631
|
+
this.put = (url, data) => {
|
|
11632
|
+
return this.doAxiosRequest('put', url, data);
|
|
11633
|
+
};
|
|
11634
|
+
this.post = (url, data) => {
|
|
11635
|
+
return this.doAxiosRequest('post', url, data);
|
|
11636
|
+
};
|
|
11637
|
+
this.patch = (url, data) => {
|
|
11638
|
+
return this.doAxiosRequest('patch', url, data);
|
|
11639
|
+
};
|
|
11640
|
+
this.delete = (url, params) => {
|
|
11641
|
+
return this.doAxiosRequest('delete', url, null, {
|
|
11642
|
+
params,
|
|
11643
|
+
});
|
|
11644
|
+
};
|
|
11645
|
+
this.errorFromResponse = (response) => {
|
|
11646
|
+
let err;
|
|
11647
|
+
err = new ErrorFromResponse(`Stream error HTTP code: ${response.status}`);
|
|
11648
|
+
if (response.data && response.data.code) {
|
|
11649
|
+
err = new Error(`Stream error code ${response.data.code}: ${response.data.message}`);
|
|
11650
|
+
err.code = response.data.code;
|
|
11651
|
+
}
|
|
11652
|
+
err.response = response;
|
|
11653
|
+
err.status = response.status;
|
|
11654
|
+
return err;
|
|
11655
|
+
};
|
|
11656
|
+
this.handleResponse = (response) => {
|
|
11657
|
+
const data = response.data;
|
|
11658
|
+
if (isErrorResponse(response)) {
|
|
11659
|
+
throw this.errorFromResponse(response);
|
|
11660
|
+
}
|
|
11661
|
+
return data;
|
|
11662
|
+
};
|
|
11548
11663
|
this.dispatchEvent = (event) => {
|
|
11549
11664
|
if (!event.received_at)
|
|
11550
11665
|
event.received_at = new Date();
|
|
@@ -11572,10 +11687,119 @@ class StreamClient {
|
|
|
11572
11687
|
listener(event);
|
|
11573
11688
|
}
|
|
11574
11689
|
};
|
|
11690
|
+
/**
|
|
11691
|
+
* @private
|
|
11692
|
+
*/
|
|
11693
|
+
this.connect = () => __awaiter(this, void 0, void 0, function* () {
|
|
11694
|
+
if (!this.userID || !this._user) {
|
|
11695
|
+
throw Error('Call connectUser or connectAnonymousUser before starting the connection');
|
|
11696
|
+
}
|
|
11697
|
+
if (!this.wsBaseURL) {
|
|
11698
|
+
throw Error('Websocket base url not set');
|
|
11699
|
+
}
|
|
11700
|
+
if (!this.clientID) {
|
|
11701
|
+
throw Error('clientID is not set');
|
|
11702
|
+
}
|
|
11703
|
+
if (!this.wsConnection &&
|
|
11704
|
+
(this.options.warmUp || this.options.enableInsights)) {
|
|
11705
|
+
this._sayHi();
|
|
11706
|
+
}
|
|
11707
|
+
// The StableWSConnection handles all the reconnection logic.
|
|
11708
|
+
if (this.options.wsConnection && this.node) {
|
|
11709
|
+
// Intentionally avoiding adding ts generics on wsConnection in options since its only useful for unit test purpose.
|
|
11710
|
+
this.options.wsConnection.setClient(this);
|
|
11711
|
+
this.wsConnection = this.options
|
|
11712
|
+
.wsConnection;
|
|
11713
|
+
}
|
|
11714
|
+
else {
|
|
11715
|
+
this.wsConnection = new StableWSConnection(this);
|
|
11716
|
+
}
|
|
11717
|
+
try {
|
|
11718
|
+
// if fallback is used before, continue using it instead of waiting for WS to fail
|
|
11719
|
+
if (this.wsFallback) {
|
|
11720
|
+
return yield this.wsFallback.connect();
|
|
11721
|
+
}
|
|
11722
|
+
this.logger('info', 'StreamClient.connect: this.wsConnection.connect()');
|
|
11723
|
+
// if WSFallback is enabled, ws connect should timeout faster so fallback can try
|
|
11724
|
+
return yield this.wsConnection.connect(this.options.enableWSFallback
|
|
11725
|
+
? this.defaultWSTimeoutWithFallback
|
|
11726
|
+
: this.defaultWSTimeout);
|
|
11727
|
+
}
|
|
11728
|
+
catch (err) {
|
|
11729
|
+
// run fallback only if it's WS/Network error and not a normal API error
|
|
11730
|
+
// make sure browser is online before even trying the longpoll
|
|
11731
|
+
if (this.options.enableWSFallback &&
|
|
11732
|
+
// @ts-ignore
|
|
11733
|
+
isWSFailure(err) &&
|
|
11734
|
+
isOnline(this.logger)) {
|
|
11735
|
+
this.logger('warn', 'client:connect() - WS failed, fallback to longpoll');
|
|
11736
|
+
this.dispatchEvent({ type: 'transport.changed', mode: 'longpoll' });
|
|
11737
|
+
this.wsConnection._destroyCurrentWSConnection();
|
|
11738
|
+
this.wsConnection.disconnect().then(); // close WS so no retry
|
|
11739
|
+
this.wsFallback = new WSConnectionFallback(this);
|
|
11740
|
+
return yield this.wsFallback.connect();
|
|
11741
|
+
}
|
|
11742
|
+
throw err;
|
|
11743
|
+
}
|
|
11744
|
+
});
|
|
11745
|
+
/**
|
|
11746
|
+
* Check the connectivity with server for warmup purpose.
|
|
11747
|
+
*
|
|
11748
|
+
* @private
|
|
11749
|
+
*/
|
|
11750
|
+
this._sayHi = () => {
|
|
11751
|
+
const client_request_id = randomId();
|
|
11752
|
+
const opts = {
|
|
11753
|
+
headers: axios.AxiosHeaders.from({
|
|
11754
|
+
'x-client-request-id': client_request_id,
|
|
11755
|
+
}),
|
|
11756
|
+
};
|
|
11757
|
+
this.doAxiosRequest('get', this.baseURL + '/hi', null, opts).catch((e) => {
|
|
11758
|
+
if (this.options.enableInsights) {
|
|
11759
|
+
postInsights('http_hi_failed', {
|
|
11760
|
+
api_key: this.key,
|
|
11761
|
+
err: e,
|
|
11762
|
+
client_request_id,
|
|
11763
|
+
});
|
|
11764
|
+
}
|
|
11765
|
+
});
|
|
11766
|
+
};
|
|
11767
|
+
this.getUserAgent = () => {
|
|
11768
|
+
return (this.userAgent ||
|
|
11769
|
+
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.39"}`);
|
|
11770
|
+
};
|
|
11771
|
+
this.setUserAgent = (userAgent) => {
|
|
11772
|
+
this.userAgent = userAgent;
|
|
11773
|
+
};
|
|
11575
11774
|
/**
|
|
11576
11775
|
* _isUsingServerAuth - Returns true if we're using server side auth
|
|
11577
11776
|
*/
|
|
11578
11777
|
this._isUsingServerAuth = () => !!this.secret;
|
|
11778
|
+
this._enrichAxiosOptions = (options = {
|
|
11779
|
+
params: {},
|
|
11780
|
+
headers: {},
|
|
11781
|
+
config: {},
|
|
11782
|
+
}) => {
|
|
11783
|
+
var _a;
|
|
11784
|
+
const token = options.publicEndpoint && !this.user ? undefined : this._getToken();
|
|
11785
|
+
const authorization = token ? { Authorization: token } : undefined;
|
|
11786
|
+
let signal = null;
|
|
11787
|
+
if (this.nextRequestAbortController !== null) {
|
|
11788
|
+
signal = this.nextRequestAbortController.signal;
|
|
11789
|
+
this.nextRequestAbortController = null;
|
|
11790
|
+
}
|
|
11791
|
+
if (!((_a = options.headers) === null || _a === void 0 ? void 0 : _a['x-client-request-id'])) {
|
|
11792
|
+
options.headers = Object.assign(Object.assign({}, options.headers), { 'x-client-request-id': randomId() });
|
|
11793
|
+
}
|
|
11794
|
+
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
|
|
11795
|
+
? 'anonymous'
|
|
11796
|
+
: this.getAuthType(), 'X-Stream-Client': this.getUserAgent() }), options.headers) }, (signal ? { signal } : {})), options.config), this.options.axiosRequestConfig);
|
|
11797
|
+
};
|
|
11798
|
+
this._getToken = () => {
|
|
11799
|
+
if (!this.tokenManager)
|
|
11800
|
+
return null;
|
|
11801
|
+
return this.tokenManager.getToken();
|
|
11802
|
+
};
|
|
11579
11803
|
/**
|
|
11580
11804
|
* encode ws url payload
|
|
11581
11805
|
* @private
|
|
@@ -11588,6 +11812,38 @@ class StreamClient {
|
|
|
11588
11812
|
client_request_id,
|
|
11589
11813
|
});
|
|
11590
11814
|
};
|
|
11815
|
+
/**
|
|
11816
|
+
* creates an abort controller that will be used by the next HTTP Request.
|
|
11817
|
+
*/
|
|
11818
|
+
this.createAbortControllerForNextRequest = () => {
|
|
11819
|
+
return (this.nextRequestAbortController = new AbortController());
|
|
11820
|
+
};
|
|
11821
|
+
/**
|
|
11822
|
+
* createToken - Creates a token to authenticate this user. This function is used server side.
|
|
11823
|
+
* The resulting token should be passed to the client side when the users registers or logs in.
|
|
11824
|
+
*
|
|
11825
|
+
* @param {string} userID The UserWithId ID
|
|
11826
|
+
* @param {number} [exp] The expiration time for the token expressed in the number of seconds since the epoch
|
|
11827
|
+
* @param call_cids for anonymous tokens you have to provide the call cids the use can join
|
|
11828
|
+
*
|
|
11829
|
+
* @return {string} Returns a token
|
|
11830
|
+
*/
|
|
11831
|
+
this.createToken = (userID, exp, iat, call_cids) => {
|
|
11832
|
+
if (this.secret == null) {
|
|
11833
|
+
throw Error(`tokens can only be created server-side using the API Secret`);
|
|
11834
|
+
}
|
|
11835
|
+
const extra = {};
|
|
11836
|
+
if (exp) {
|
|
11837
|
+
extra.exp = exp;
|
|
11838
|
+
}
|
|
11839
|
+
if (iat) {
|
|
11840
|
+
extra.iat = iat;
|
|
11841
|
+
}
|
|
11842
|
+
if (call_cids) {
|
|
11843
|
+
extra.call_cids = call_cids;
|
|
11844
|
+
}
|
|
11845
|
+
return JWTUserToken(this.secret, userID, extra, {});
|
|
11846
|
+
};
|
|
11591
11847
|
// set the key
|
|
11592
11848
|
this.key = key;
|
|
11593
11849
|
this.listeners = {};
|
|
@@ -11639,268 +11895,6 @@ class StreamClient {
|
|
|
11639
11895
|
? inputOptions.logger
|
|
11640
11896
|
: () => null;
|
|
11641
11897
|
}
|
|
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
11898
|
}
|
|
11905
11899
|
|
|
11906
11900
|
/**
|