@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.es.js
CHANGED
|
@@ -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);
|
|
@@ -9084,13 +9084,6 @@ const getClientDetails = () => {
|
|
|
9084
9084
|
* An object representation of a `Call`.
|
|
9085
9085
|
*/
|
|
9086
9086
|
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
9087
|
/**
|
|
9095
9088
|
* Constructs a new `Call` instance.
|
|
9096
9089
|
*
|
|
@@ -9136,7 +9129,6 @@ class Call {
|
|
|
9136
9129
|
if (callingState === CallingState.LEFT) {
|
|
9137
9130
|
throw new Error('Cannot leave call that has already been left.');
|
|
9138
9131
|
}
|
|
9139
|
-
this.rejoinPromise = undefined;
|
|
9140
9132
|
if (this.ringing) {
|
|
9141
9133
|
// I'm the one who started the call, so I should cancel it.
|
|
9142
9134
|
const hasOtherParticipants = this.state.remoteParticipants.length > 0;
|
|
@@ -9340,7 +9332,7 @@ class Call {
|
|
|
9340
9332
|
// we shouldn't be republishing the streams if we're migrating
|
|
9341
9333
|
// as the underlying peer connection will take care of it as part
|
|
9342
9334
|
// of the ice-restart process
|
|
9343
|
-
if (localParticipant && !
|
|
9335
|
+
if (localParticipant && !migrate) {
|
|
9344
9336
|
const { audioStream, videoStream, screenShareStream: screenShare, } = localParticipant;
|
|
9345
9337
|
// restore previous publishing state
|
|
9346
9338
|
if (audioStream)
|
|
@@ -9352,7 +9344,6 @@ class Call {
|
|
|
9352
9344
|
}
|
|
9353
9345
|
this.logger('info', `[Rejoin]: State restored. Attempt: ${this.reconnectAttempts}`);
|
|
9354
9346
|
});
|
|
9355
|
-
this.rejoinPromise = rejoin;
|
|
9356
9347
|
// reconnect if the connection was closed unexpectedly. example:
|
|
9357
9348
|
// - SFU crash or restart
|
|
9358
9349
|
// - network change
|
|
@@ -9385,9 +9376,6 @@ class Call {
|
|
|
9385
9376
|
if (e.code === KnownCodes.WS_CLOSED_ABRUPTLY &&
|
|
9386
9377
|
sfuClient.isMigratingAway)
|
|
9387
9378
|
return;
|
|
9388
|
-
// do nothing for react-native as it is handled by SDK
|
|
9389
|
-
if (isReactNative())
|
|
9390
|
-
return;
|
|
9391
9379
|
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
9392
9380
|
rejoin().catch((err) => {
|
|
9393
9381
|
this.logger('error', `[Rejoin]: Rejoin failed for ${this.reconnectAttempts} times. Giving up.`, err);
|
|
@@ -9401,28 +9389,34 @@ class Call {
|
|
|
9401
9389
|
});
|
|
9402
9390
|
});
|
|
9403
9391
|
// 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
|
-
|
|
9392
|
+
const unsubscribeOnlineEvent = this.streamClient.on('connection.changed', (e) => {
|
|
9393
|
+
if (e.type !== 'connection.changed')
|
|
9394
|
+
return;
|
|
9395
|
+
if (!e.online)
|
|
9396
|
+
return;
|
|
9397
|
+
unsubscribeOnlineEvent();
|
|
9398
|
+
const currentCallingState = this.state.callingState;
|
|
9399
|
+
if (currentCallingState === CallingState.OFFLINE ||
|
|
9400
|
+
currentCallingState === CallingState.RECONNECTING_FAILED) {
|
|
9401
|
+
this.logger('info', '[Rejoin]: Going online...');
|
|
9402
|
+
rejoin().catch((err) => {
|
|
9403
|
+
this.logger('error', `[Rejoin]: Rejoin failed for ${this.reconnectAttempts} times. Giving up.`, err);
|
|
9404
|
+
this.state.setCallingState(CallingState.RECONNECTING_FAILED);
|
|
9405
|
+
});
|
|
9406
|
+
}
|
|
9407
|
+
});
|
|
9408
|
+
const unsubscribeOfflineEvent = this.streamClient.on('connection.changed', (e) => {
|
|
9409
|
+
if (e.type !== 'connection.changed')
|
|
9410
|
+
return;
|
|
9411
|
+
if (e.online)
|
|
9412
|
+
return;
|
|
9413
|
+
unsubscribeOfflineEvent();
|
|
9414
|
+
this.state.setCallingState(CallingState.OFFLINE);
|
|
9415
|
+
});
|
|
9416
|
+
this.leaveCallHooks.push(() => {
|
|
9417
|
+
unsubscribeOnlineEvent();
|
|
9418
|
+
unsubscribeOfflineEvent();
|
|
9419
|
+
});
|
|
9426
9420
|
if (!this.subscriber) {
|
|
9427
9421
|
this.subscriber = new Subscriber({
|
|
9428
9422
|
sfuClient,
|
|
@@ -10271,6 +10265,9 @@ const isErrorEvent = (res) => res.error !== undefined;
|
|
|
10271
10265
|
*/
|
|
10272
10266
|
class StableWSConnection {
|
|
10273
10267
|
constructor(client) {
|
|
10268
|
+
this._log = (msg, extra = {}, level = 'info') => {
|
|
10269
|
+
this.client.logger(level, 'connection:' + msg, Object.assign({}, extra));
|
|
10270
|
+
};
|
|
10274
10271
|
this.setClient = (client) => {
|
|
10275
10272
|
this.client = client;
|
|
10276
10273
|
};
|
|
@@ -10299,7 +10296,8 @@ class StableWSConnection {
|
|
|
10299
10296
|
if (event.type === 'offline') {
|
|
10300
10297
|
// mark the connection as down
|
|
10301
10298
|
this._log('onlineStatusChanged() - Status changing to offline');
|
|
10302
|
-
|
|
10299
|
+
// we know that the app is offline so dispatch the unhealthy connection event immediately
|
|
10300
|
+
this._setHealth(false, true);
|
|
10303
10301
|
}
|
|
10304
10302
|
else if (event.type === 'online') {
|
|
10305
10303
|
// retry right now...
|
|
@@ -10427,13 +10425,14 @@ class StableWSConnection {
|
|
|
10427
10425
|
* Broadcasts an event in case the connection status changed.
|
|
10428
10426
|
*
|
|
10429
10427
|
* @param {boolean} healthy boolean indicating if the connection is healthy or not
|
|
10428
|
+
* @param {boolean} dispatchImmediately boolean indicating to dispatch event immediately even if the connection is unhealthy
|
|
10430
10429
|
*
|
|
10431
10430
|
*/
|
|
10432
|
-
this._setHealth = (healthy) => {
|
|
10431
|
+
this._setHealth = (healthy, dispatchImmediately = false) => {
|
|
10433
10432
|
if (healthy === this.isHealthy)
|
|
10434
10433
|
return;
|
|
10435
10434
|
this.isHealthy = healthy;
|
|
10436
|
-
if (this.isHealthy) {
|
|
10435
|
+
if (this.isHealthy || dispatchImmediately) {
|
|
10437
10436
|
this.client.dispatchEvent({
|
|
10438
10437
|
type: 'connection.changed',
|
|
10439
10438
|
online: this.isHealthy,
|
|
@@ -10555,9 +10554,6 @@ class StableWSConnection {
|
|
|
10555
10554
|
this.connectionCheckTimeout = this.pingInterval + 10 * 1000;
|
|
10556
10555
|
addConnectionEventListeners(this.onlineStatusChanged);
|
|
10557
10556
|
}
|
|
10558
|
-
_log(msg, extra = {}, level = 'info') {
|
|
10559
|
-
this.client.logger(level, 'connection:' + msg, Object.assign({}, extra));
|
|
10560
|
-
}
|
|
10561
10557
|
/**
|
|
10562
10558
|
* connect - Connect to the WS URL
|
|
10563
10559
|
* the default 15s timeout allows between 2~3 tries
|
|
@@ -11309,6 +11305,18 @@ class StreamClient {
|
|
|
11309
11305
|
constructor(key, options) {
|
|
11310
11306
|
var _a;
|
|
11311
11307
|
this.nextRequestAbortController = null;
|
|
11308
|
+
this.devToken = (userID) => {
|
|
11309
|
+
return DevToken(userID);
|
|
11310
|
+
};
|
|
11311
|
+
this.getAuthType = () => {
|
|
11312
|
+
return this.anonymous ? 'anonymous' : 'jwt';
|
|
11313
|
+
};
|
|
11314
|
+
this.setBaseURL = (baseURL) => {
|
|
11315
|
+
this.baseURL = baseURL;
|
|
11316
|
+
this.wsBaseURL = this.baseURL
|
|
11317
|
+
.replace('http', 'ws')
|
|
11318
|
+
.replace(':3030', ':8800');
|
|
11319
|
+
};
|
|
11312
11320
|
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
11321
|
this._hasConnectionID = () => Boolean(this._getConnectionID());
|
|
11314
11322
|
/**
|
|
@@ -11373,6 +11381,16 @@ class StreamClient {
|
|
|
11373
11381
|
}
|
|
11374
11382
|
});
|
|
11375
11383
|
this._setToken = (user, userTokenOrProvider, isAnonymous) => this.tokenManager.setTokenOrProvider(userTokenOrProvider, user, isAnonymous);
|
|
11384
|
+
this._setUser = (user) => {
|
|
11385
|
+
/**
|
|
11386
|
+
* This one is used by the frontend. This is a copy of the current user object stored on backend.
|
|
11387
|
+
* It contains reserved properties and own user properties which are not present in `this._user`.
|
|
11388
|
+
*/
|
|
11389
|
+
this.user = user;
|
|
11390
|
+
this.userID = user.id;
|
|
11391
|
+
// this one is actually used for requests. This is a copy of current user provided to `connectUser` function.
|
|
11392
|
+
this._user = Object.assign({}, user);
|
|
11393
|
+
};
|
|
11376
11394
|
/**
|
|
11377
11395
|
* Disconnects the websocket connection, without removing the user set on client.
|
|
11378
11396
|
* client.closeConnection will not trigger default auto-retry mechanism for reconnection. You need
|
|
@@ -11461,6 +11479,66 @@ class StreamClient {
|
|
|
11461
11479
|
// resolve the connection_id here.
|
|
11462
11480
|
this.resolveConnectionId();
|
|
11463
11481
|
});
|
|
11482
|
+
/**
|
|
11483
|
+
* on - Listen to events on all channels and users your watching
|
|
11484
|
+
*
|
|
11485
|
+
* client.on('message.new', event => {console.log("my new message", event, channel.state.messages)})
|
|
11486
|
+
* or
|
|
11487
|
+
* client.on(event => {console.log(event.type)})
|
|
11488
|
+
*
|
|
11489
|
+
* @param {EventHandler | string} callbackOrEventName The event type to listen for (optional)
|
|
11490
|
+
* @param {EventHandler} [callbackOrNothing] The callback to call
|
|
11491
|
+
*
|
|
11492
|
+
* @return {Function} Returns a function which, when called, unsubscribes the event handler.
|
|
11493
|
+
*/
|
|
11494
|
+
this.on = (callbackOrEventName, callbackOrNothing) => {
|
|
11495
|
+
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11496
|
+
const callback = callbackOrNothing
|
|
11497
|
+
? callbackOrNothing
|
|
11498
|
+
: callbackOrEventName;
|
|
11499
|
+
if (!(key in this.listeners)) {
|
|
11500
|
+
this.listeners[key] = [];
|
|
11501
|
+
}
|
|
11502
|
+
this.listeners[key].push(callback);
|
|
11503
|
+
return () => {
|
|
11504
|
+
this.off(key, callback);
|
|
11505
|
+
};
|
|
11506
|
+
};
|
|
11507
|
+
/**
|
|
11508
|
+
* off - Remove the event handler
|
|
11509
|
+
*
|
|
11510
|
+
*/
|
|
11511
|
+
this.off = (callbackOrEventName, callbackOrNothing) => {
|
|
11512
|
+
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11513
|
+
const callback = callbackOrNothing
|
|
11514
|
+
? callbackOrNothing
|
|
11515
|
+
: callbackOrEventName;
|
|
11516
|
+
if (!(key in this.listeners)) {
|
|
11517
|
+
this.listeners[key] = [];
|
|
11518
|
+
}
|
|
11519
|
+
this.logger('debug', `Removing listener for ${key} event`);
|
|
11520
|
+
this.listeners[key] = this.listeners[key].filter((value) => value !== callback);
|
|
11521
|
+
};
|
|
11522
|
+
this._logApiRequest = (type, url, data, config) => {
|
|
11523
|
+
this.logger('trace', `client: ${type} - Request - ${url}`, {
|
|
11524
|
+
payload: data,
|
|
11525
|
+
config,
|
|
11526
|
+
});
|
|
11527
|
+
};
|
|
11528
|
+
this._logApiResponse = (type, url, response) => {
|
|
11529
|
+
this.logger('trace', `client:${type} - Response - url: ${url} > status ${response.status}`, {
|
|
11530
|
+
response,
|
|
11531
|
+
});
|
|
11532
|
+
this.logger('trace', `client:${type} - Response payload`, {
|
|
11533
|
+
response,
|
|
11534
|
+
});
|
|
11535
|
+
};
|
|
11536
|
+
this._logApiError = (type, url, error) => {
|
|
11537
|
+
this.logger('error', `client:${type} - Error - url: ${url}`, {
|
|
11538
|
+
url,
|
|
11539
|
+
error,
|
|
11540
|
+
});
|
|
11541
|
+
};
|
|
11464
11542
|
this.doAxiosRequest = (type, url, data, options = {}) => __awaiter(this, void 0, void 0, function* () {
|
|
11465
11543
|
var _g;
|
|
11466
11544
|
if (!options.publicEndpoint || this.user) {
|
|
@@ -11525,6 +11603,43 @@ class StreamClient {
|
|
|
11525
11603
|
}
|
|
11526
11604
|
}
|
|
11527
11605
|
});
|
|
11606
|
+
this.get = (url, params) => {
|
|
11607
|
+
return this.doAxiosRequest('get', url, null, {
|
|
11608
|
+
params,
|
|
11609
|
+
});
|
|
11610
|
+
};
|
|
11611
|
+
this.put = (url, data) => {
|
|
11612
|
+
return this.doAxiosRequest('put', url, data);
|
|
11613
|
+
};
|
|
11614
|
+
this.post = (url, data) => {
|
|
11615
|
+
return this.doAxiosRequest('post', url, data);
|
|
11616
|
+
};
|
|
11617
|
+
this.patch = (url, data) => {
|
|
11618
|
+
return this.doAxiosRequest('patch', url, data);
|
|
11619
|
+
};
|
|
11620
|
+
this.delete = (url, params) => {
|
|
11621
|
+
return this.doAxiosRequest('delete', url, null, {
|
|
11622
|
+
params,
|
|
11623
|
+
});
|
|
11624
|
+
};
|
|
11625
|
+
this.errorFromResponse = (response) => {
|
|
11626
|
+
let err;
|
|
11627
|
+
err = new ErrorFromResponse(`Stream error HTTP code: ${response.status}`);
|
|
11628
|
+
if (response.data && response.data.code) {
|
|
11629
|
+
err = new Error(`Stream error code ${response.data.code}: ${response.data.message}`);
|
|
11630
|
+
err.code = response.data.code;
|
|
11631
|
+
}
|
|
11632
|
+
err.response = response;
|
|
11633
|
+
err.status = response.status;
|
|
11634
|
+
return err;
|
|
11635
|
+
};
|
|
11636
|
+
this.handleResponse = (response) => {
|
|
11637
|
+
const data = response.data;
|
|
11638
|
+
if (isErrorResponse(response)) {
|
|
11639
|
+
throw this.errorFromResponse(response);
|
|
11640
|
+
}
|
|
11641
|
+
return data;
|
|
11642
|
+
};
|
|
11528
11643
|
this.dispatchEvent = (event) => {
|
|
11529
11644
|
if (!event.received_at)
|
|
11530
11645
|
event.received_at = new Date();
|
|
@@ -11552,10 +11667,119 @@ class StreamClient {
|
|
|
11552
11667
|
listener(event);
|
|
11553
11668
|
}
|
|
11554
11669
|
};
|
|
11670
|
+
/**
|
|
11671
|
+
* @private
|
|
11672
|
+
*/
|
|
11673
|
+
this.connect = () => __awaiter(this, void 0, void 0, function* () {
|
|
11674
|
+
if (!this.userID || !this._user) {
|
|
11675
|
+
throw Error('Call connectUser or connectAnonymousUser before starting the connection');
|
|
11676
|
+
}
|
|
11677
|
+
if (!this.wsBaseURL) {
|
|
11678
|
+
throw Error('Websocket base url not set');
|
|
11679
|
+
}
|
|
11680
|
+
if (!this.clientID) {
|
|
11681
|
+
throw Error('clientID is not set');
|
|
11682
|
+
}
|
|
11683
|
+
if (!this.wsConnection &&
|
|
11684
|
+
(this.options.warmUp || this.options.enableInsights)) {
|
|
11685
|
+
this._sayHi();
|
|
11686
|
+
}
|
|
11687
|
+
// The StableWSConnection handles all the reconnection logic.
|
|
11688
|
+
if (this.options.wsConnection && this.node) {
|
|
11689
|
+
// Intentionally avoiding adding ts generics on wsConnection in options since its only useful for unit test purpose.
|
|
11690
|
+
this.options.wsConnection.setClient(this);
|
|
11691
|
+
this.wsConnection = this.options
|
|
11692
|
+
.wsConnection;
|
|
11693
|
+
}
|
|
11694
|
+
else {
|
|
11695
|
+
this.wsConnection = new StableWSConnection(this);
|
|
11696
|
+
}
|
|
11697
|
+
try {
|
|
11698
|
+
// if fallback is used before, continue using it instead of waiting for WS to fail
|
|
11699
|
+
if (this.wsFallback) {
|
|
11700
|
+
return yield this.wsFallback.connect();
|
|
11701
|
+
}
|
|
11702
|
+
this.logger('info', 'StreamClient.connect: this.wsConnection.connect()');
|
|
11703
|
+
// if WSFallback is enabled, ws connect should timeout faster so fallback can try
|
|
11704
|
+
return yield this.wsConnection.connect(this.options.enableWSFallback
|
|
11705
|
+
? this.defaultWSTimeoutWithFallback
|
|
11706
|
+
: this.defaultWSTimeout);
|
|
11707
|
+
}
|
|
11708
|
+
catch (err) {
|
|
11709
|
+
// run fallback only if it's WS/Network error and not a normal API error
|
|
11710
|
+
// make sure browser is online before even trying the longpoll
|
|
11711
|
+
if (this.options.enableWSFallback &&
|
|
11712
|
+
// @ts-ignore
|
|
11713
|
+
isWSFailure(err) &&
|
|
11714
|
+
isOnline(this.logger)) {
|
|
11715
|
+
this.logger('warn', 'client:connect() - WS failed, fallback to longpoll');
|
|
11716
|
+
this.dispatchEvent({ type: 'transport.changed', mode: 'longpoll' });
|
|
11717
|
+
this.wsConnection._destroyCurrentWSConnection();
|
|
11718
|
+
this.wsConnection.disconnect().then(); // close WS so no retry
|
|
11719
|
+
this.wsFallback = new WSConnectionFallback(this);
|
|
11720
|
+
return yield this.wsFallback.connect();
|
|
11721
|
+
}
|
|
11722
|
+
throw err;
|
|
11723
|
+
}
|
|
11724
|
+
});
|
|
11725
|
+
/**
|
|
11726
|
+
* Check the connectivity with server for warmup purpose.
|
|
11727
|
+
*
|
|
11728
|
+
* @private
|
|
11729
|
+
*/
|
|
11730
|
+
this._sayHi = () => {
|
|
11731
|
+
const client_request_id = randomId();
|
|
11732
|
+
const opts = {
|
|
11733
|
+
headers: AxiosHeaders.from({
|
|
11734
|
+
'x-client-request-id': client_request_id,
|
|
11735
|
+
}),
|
|
11736
|
+
};
|
|
11737
|
+
this.doAxiosRequest('get', this.baseURL + '/hi', null, opts).catch((e) => {
|
|
11738
|
+
if (this.options.enableInsights) {
|
|
11739
|
+
postInsights('http_hi_failed', {
|
|
11740
|
+
api_key: this.key,
|
|
11741
|
+
err: e,
|
|
11742
|
+
client_request_id,
|
|
11743
|
+
});
|
|
11744
|
+
}
|
|
11745
|
+
});
|
|
11746
|
+
};
|
|
11747
|
+
this.getUserAgent = () => {
|
|
11748
|
+
return (this.userAgent ||
|
|
11749
|
+
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.39"}`);
|
|
11750
|
+
};
|
|
11751
|
+
this.setUserAgent = (userAgent) => {
|
|
11752
|
+
this.userAgent = userAgent;
|
|
11753
|
+
};
|
|
11555
11754
|
/**
|
|
11556
11755
|
* _isUsingServerAuth - Returns true if we're using server side auth
|
|
11557
11756
|
*/
|
|
11558
11757
|
this._isUsingServerAuth = () => !!this.secret;
|
|
11758
|
+
this._enrichAxiosOptions = (options = {
|
|
11759
|
+
params: {},
|
|
11760
|
+
headers: {},
|
|
11761
|
+
config: {},
|
|
11762
|
+
}) => {
|
|
11763
|
+
var _a;
|
|
11764
|
+
const token = options.publicEndpoint && !this.user ? undefined : this._getToken();
|
|
11765
|
+
const authorization = token ? { Authorization: token } : undefined;
|
|
11766
|
+
let signal = null;
|
|
11767
|
+
if (this.nextRequestAbortController !== null) {
|
|
11768
|
+
signal = this.nextRequestAbortController.signal;
|
|
11769
|
+
this.nextRequestAbortController = null;
|
|
11770
|
+
}
|
|
11771
|
+
if (!((_a = options.headers) === null || _a === void 0 ? void 0 : _a['x-client-request-id'])) {
|
|
11772
|
+
options.headers = Object.assign(Object.assign({}, options.headers), { 'x-client-request-id': randomId() });
|
|
11773
|
+
}
|
|
11774
|
+
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
|
|
11775
|
+
? 'anonymous'
|
|
11776
|
+
: this.getAuthType(), 'X-Stream-Client': this.getUserAgent() }), options.headers) }, (signal ? { signal } : {})), options.config), this.options.axiosRequestConfig);
|
|
11777
|
+
};
|
|
11778
|
+
this._getToken = () => {
|
|
11779
|
+
if (!this.tokenManager)
|
|
11780
|
+
return null;
|
|
11781
|
+
return this.tokenManager.getToken();
|
|
11782
|
+
};
|
|
11559
11783
|
/**
|
|
11560
11784
|
* encode ws url payload
|
|
11561
11785
|
* @private
|
|
@@ -11568,6 +11792,38 @@ class StreamClient {
|
|
|
11568
11792
|
client_request_id,
|
|
11569
11793
|
});
|
|
11570
11794
|
};
|
|
11795
|
+
/**
|
|
11796
|
+
* creates an abort controller that will be used by the next HTTP Request.
|
|
11797
|
+
*/
|
|
11798
|
+
this.createAbortControllerForNextRequest = () => {
|
|
11799
|
+
return (this.nextRequestAbortController = new AbortController());
|
|
11800
|
+
};
|
|
11801
|
+
/**
|
|
11802
|
+
* createToken - Creates a token to authenticate this user. This function is used server side.
|
|
11803
|
+
* The resulting token should be passed to the client side when the users registers or logs in.
|
|
11804
|
+
*
|
|
11805
|
+
* @param {string} userID The UserWithId ID
|
|
11806
|
+
* @param {number} [exp] The expiration time for the token expressed in the number of seconds since the epoch
|
|
11807
|
+
* @param call_cids for anonymous tokens you have to provide the call cids the use can join
|
|
11808
|
+
*
|
|
11809
|
+
* @return {string} Returns a token
|
|
11810
|
+
*/
|
|
11811
|
+
this.createToken = (userID, exp, iat, call_cids) => {
|
|
11812
|
+
if (this.secret == null) {
|
|
11813
|
+
throw Error(`tokens can only be created server-side using the API Secret`);
|
|
11814
|
+
}
|
|
11815
|
+
const extra = {};
|
|
11816
|
+
if (exp) {
|
|
11817
|
+
extra.exp = exp;
|
|
11818
|
+
}
|
|
11819
|
+
if (iat) {
|
|
11820
|
+
extra.iat = iat;
|
|
11821
|
+
}
|
|
11822
|
+
if (call_cids) {
|
|
11823
|
+
extra.call_cids = call_cids;
|
|
11824
|
+
}
|
|
11825
|
+
return JWTUserToken(this.secret, userID, extra, {});
|
|
11826
|
+
};
|
|
11571
11827
|
// set the key
|
|
11572
11828
|
this.key = key;
|
|
11573
11829
|
this.listeners = {};
|
|
@@ -11619,268 +11875,6 @@ class StreamClient {
|
|
|
11619
11875
|
? inputOptions.logger
|
|
11620
11876
|
: () => null;
|
|
11621
11877
|
}
|
|
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
11878
|
}
|
|
11885
11879
|
|
|
11886
11880
|
/**
|