@stream-io/video-client 0.0.38 → 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 +14 -0
- package/dist/index.browser.es.js +299 -304
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +299 -304
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +299 -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/src/rtc/Publisher.ts +1 -0
package/dist/index.browser.es.js
CHANGED
|
@@ -6093,6 +6093,7 @@ class Publisher {
|
|
|
6093
6093
|
this.trackLayersCache[trackType] = undefined;
|
|
6094
6094
|
});
|
|
6095
6095
|
}
|
|
6096
|
+
this.pc.removeEventListener('negotiationneeded', this.onNegotiationNeeded);
|
|
6096
6097
|
this.pc.close();
|
|
6097
6098
|
};
|
|
6098
6099
|
/**
|
|
@@ -6844,7 +6845,7 @@ class StreamSfuClient {
|
|
|
6844
6845
|
* from this SFU.
|
|
6845
6846
|
*/
|
|
6846
6847
|
this.isMigratingAway = false;
|
|
6847
|
-
this.pingIntervalInMs =
|
|
6848
|
+
this.pingIntervalInMs = 10 * 1000;
|
|
6848
6849
|
this.unhealthyTimeoutInMs = this.pingIntervalInMs + 5 * 1000;
|
|
6849
6850
|
this.close = (code = 1000, reason = 'Requested signal connection close') => {
|
|
6850
6851
|
this.signalWs.close(code, reason);
|
|
@@ -9080,13 +9081,6 @@ const getClientDetails = () => {
|
|
|
9080
9081
|
* An object representation of a `Call`.
|
|
9081
9082
|
*/
|
|
9082
9083
|
class Call {
|
|
9083
|
-
/**
|
|
9084
|
-
* A promise that exposes the reconnection logic
|
|
9085
|
-
* The use-case is for the react-native platform where online/offline events are not available in the window
|
|
9086
|
-
*/
|
|
9087
|
-
get rejoin() {
|
|
9088
|
-
return this.rejoinPromise;
|
|
9089
|
-
}
|
|
9090
9084
|
/**
|
|
9091
9085
|
* Constructs a new `Call` instance.
|
|
9092
9086
|
*
|
|
@@ -9132,7 +9126,6 @@ class Call {
|
|
|
9132
9126
|
if (callingState === CallingState.LEFT) {
|
|
9133
9127
|
throw new Error('Cannot leave call that has already been left.');
|
|
9134
9128
|
}
|
|
9135
|
-
this.rejoinPromise = undefined;
|
|
9136
9129
|
if (this.ringing) {
|
|
9137
9130
|
// I'm the one who started the call, so I should cancel it.
|
|
9138
9131
|
const hasOtherParticipants = this.state.remoteParticipants.length > 0;
|
|
@@ -9336,7 +9329,7 @@ class Call {
|
|
|
9336
9329
|
// we shouldn't be republishing the streams if we're migrating
|
|
9337
9330
|
// as the underlying peer connection will take care of it as part
|
|
9338
9331
|
// of the ice-restart process
|
|
9339
|
-
if (localParticipant && !
|
|
9332
|
+
if (localParticipant && !migrate) {
|
|
9340
9333
|
const { audioStream, videoStream, screenShareStream: screenShare, } = localParticipant;
|
|
9341
9334
|
// restore previous publishing state
|
|
9342
9335
|
if (audioStream)
|
|
@@ -9348,7 +9341,6 @@ class Call {
|
|
|
9348
9341
|
}
|
|
9349
9342
|
this.logger('info', `[Rejoin]: State restored. Attempt: ${this.reconnectAttempts}`);
|
|
9350
9343
|
});
|
|
9351
|
-
this.rejoinPromise = rejoin;
|
|
9352
9344
|
// reconnect if the connection was closed unexpectedly. example:
|
|
9353
9345
|
// - SFU crash or restart
|
|
9354
9346
|
// - network change
|
|
@@ -9381,9 +9373,6 @@ class Call {
|
|
|
9381
9373
|
if (e.code === KnownCodes.WS_CLOSED_ABRUPTLY &&
|
|
9382
9374
|
sfuClient.isMigratingAway)
|
|
9383
9375
|
return;
|
|
9384
|
-
// do nothing for react-native as it is handled by SDK
|
|
9385
|
-
if (isReactNative())
|
|
9386
|
-
return;
|
|
9387
9376
|
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
9388
9377
|
rejoin().catch((err) => {
|
|
9389
9378
|
this.logger('error', `[Rejoin]: Rejoin failed for ${this.reconnectAttempts} times. Giving up.`, err);
|
|
@@ -9397,28 +9386,34 @@ class Call {
|
|
|
9397
9386
|
});
|
|
9398
9387
|
});
|
|
9399
9388
|
// handlers for connection online/offline events
|
|
9400
|
-
|
|
9401
|
-
|
|
9402
|
-
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9389
|
+
const unsubscribeOnlineEvent = this.streamClient.on('connection.changed', (e) => {
|
|
9390
|
+
if (e.type !== 'connection.changed')
|
|
9391
|
+
return;
|
|
9392
|
+
if (!e.online)
|
|
9393
|
+
return;
|
|
9394
|
+
unsubscribeOnlineEvent();
|
|
9395
|
+
const currentCallingState = this.state.callingState;
|
|
9396
|
+
if (currentCallingState === CallingState.OFFLINE ||
|
|
9397
|
+
currentCallingState === CallingState.RECONNECTING_FAILED) {
|
|
9398
|
+
this.logger('info', '[Rejoin]: Going online...');
|
|
9399
|
+
rejoin().catch((err) => {
|
|
9400
|
+
this.logger('error', `[Rejoin]: Rejoin failed for ${this.reconnectAttempts} times. Giving up.`, err);
|
|
9401
|
+
this.state.setCallingState(CallingState.RECONNECTING_FAILED);
|
|
9402
|
+
});
|
|
9403
|
+
}
|
|
9404
|
+
});
|
|
9405
|
+
const unsubscribeOfflineEvent = this.streamClient.on('connection.changed', (e) => {
|
|
9406
|
+
if (e.type !== 'connection.changed')
|
|
9407
|
+
return;
|
|
9408
|
+
if (e.online)
|
|
9409
|
+
return;
|
|
9410
|
+
unsubscribeOfflineEvent();
|
|
9411
|
+
this.state.setCallingState(CallingState.OFFLINE);
|
|
9412
|
+
});
|
|
9413
|
+
this.leaveCallHooks.push(() => {
|
|
9414
|
+
unsubscribeOnlineEvent();
|
|
9415
|
+
unsubscribeOfflineEvent();
|
|
9416
|
+
});
|
|
9422
9417
|
if (!this.subscriber) {
|
|
9423
9418
|
this.subscriber = new Subscriber({
|
|
9424
9419
|
sfuClient,
|
|
@@ -10269,6 +10264,9 @@ const isErrorEvent = (res) => res.error !== undefined;
|
|
|
10269
10264
|
*/
|
|
10270
10265
|
class StableWSConnection {
|
|
10271
10266
|
constructor(client) {
|
|
10267
|
+
this._log = (msg, extra = {}, level = 'info') => {
|
|
10268
|
+
this.client.logger(level, 'connection:' + msg, Object.assign({}, extra));
|
|
10269
|
+
};
|
|
10272
10270
|
this.setClient = (client) => {
|
|
10273
10271
|
this.client = client;
|
|
10274
10272
|
};
|
|
@@ -10297,7 +10295,8 @@ class StableWSConnection {
|
|
|
10297
10295
|
if (event.type === 'offline') {
|
|
10298
10296
|
// mark the connection as down
|
|
10299
10297
|
this._log('onlineStatusChanged() - Status changing to offline');
|
|
10300
|
-
|
|
10298
|
+
// we know that the app is offline so dispatch the unhealthy connection event immediately
|
|
10299
|
+
this._setHealth(false, true);
|
|
10301
10300
|
}
|
|
10302
10301
|
else if (event.type === 'online') {
|
|
10303
10302
|
// retry right now...
|
|
@@ -10425,13 +10424,14 @@ class StableWSConnection {
|
|
|
10425
10424
|
* Broadcasts an event in case the connection status changed.
|
|
10426
10425
|
*
|
|
10427
10426
|
* @param {boolean} healthy boolean indicating if the connection is healthy or not
|
|
10427
|
+
* @param {boolean} dispatchImmediately boolean indicating to dispatch event immediately even if the connection is unhealthy
|
|
10428
10428
|
*
|
|
10429
10429
|
*/
|
|
10430
|
-
this._setHealth = (healthy) => {
|
|
10430
|
+
this._setHealth = (healthy, dispatchImmediately = false) => {
|
|
10431
10431
|
if (healthy === this.isHealthy)
|
|
10432
10432
|
return;
|
|
10433
10433
|
this.isHealthy = healthy;
|
|
10434
|
-
if (this.isHealthy) {
|
|
10434
|
+
if (this.isHealthy || dispatchImmediately) {
|
|
10435
10435
|
this.client.dispatchEvent({
|
|
10436
10436
|
type: 'connection.changed',
|
|
10437
10437
|
online: this.isHealthy,
|
|
@@ -10553,9 +10553,6 @@ class StableWSConnection {
|
|
|
10553
10553
|
this.connectionCheckTimeout = this.pingInterval + 10 * 1000;
|
|
10554
10554
|
addConnectionEventListeners(this.onlineStatusChanged);
|
|
10555
10555
|
}
|
|
10556
|
-
_log(msg, extra = {}, level = 'info') {
|
|
10557
|
-
this.client.logger(level, 'connection:' + msg, Object.assign({}, extra));
|
|
10558
|
-
}
|
|
10559
10556
|
/**
|
|
10560
10557
|
* connect - Connect to the WS URL
|
|
10561
10558
|
* the default 15s timeout allows between 2~3 tries
|
|
@@ -11304,6 +11301,18 @@ class StreamClient {
|
|
|
11304
11301
|
constructor(key, options) {
|
|
11305
11302
|
var _a;
|
|
11306
11303
|
this.nextRequestAbortController = null;
|
|
11304
|
+
this.devToken = (userID) => {
|
|
11305
|
+
return DevToken(userID);
|
|
11306
|
+
};
|
|
11307
|
+
this.getAuthType = () => {
|
|
11308
|
+
return this.anonymous ? 'anonymous' : 'jwt';
|
|
11309
|
+
};
|
|
11310
|
+
this.setBaseURL = (baseURL) => {
|
|
11311
|
+
this.baseURL = baseURL;
|
|
11312
|
+
this.wsBaseURL = this.baseURL
|
|
11313
|
+
.replace('http', 'ws')
|
|
11314
|
+
.replace(':3030', ':8800');
|
|
11315
|
+
};
|
|
11307
11316
|
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); };
|
|
11308
11317
|
this._hasConnectionID = () => Boolean(this._getConnectionID());
|
|
11309
11318
|
/**
|
|
@@ -11368,6 +11377,16 @@ class StreamClient {
|
|
|
11368
11377
|
}
|
|
11369
11378
|
});
|
|
11370
11379
|
this._setToken = (user, userTokenOrProvider, isAnonymous) => this.tokenManager.setTokenOrProvider(userTokenOrProvider, user, isAnonymous);
|
|
11380
|
+
this._setUser = (user) => {
|
|
11381
|
+
/**
|
|
11382
|
+
* This one is used by the frontend. This is a copy of the current user object stored on backend.
|
|
11383
|
+
* It contains reserved properties and own user properties which are not present in `this._user`.
|
|
11384
|
+
*/
|
|
11385
|
+
this.user = user;
|
|
11386
|
+
this.userID = user.id;
|
|
11387
|
+
// this one is actually used for requests. This is a copy of current user provided to `connectUser` function.
|
|
11388
|
+
this._user = Object.assign({}, user);
|
|
11389
|
+
};
|
|
11371
11390
|
/**
|
|
11372
11391
|
* Disconnects the websocket connection, without removing the user set on client.
|
|
11373
11392
|
* client.closeConnection will not trigger default auto-retry mechanism for reconnection. You need
|
|
@@ -11456,6 +11475,66 @@ class StreamClient {
|
|
|
11456
11475
|
// resolve the connection_id here.
|
|
11457
11476
|
this.resolveConnectionId();
|
|
11458
11477
|
});
|
|
11478
|
+
/**
|
|
11479
|
+
* on - Listen to events on all channels and users your watching
|
|
11480
|
+
*
|
|
11481
|
+
* client.on('message.new', event => {console.log("my new message", event, channel.state.messages)})
|
|
11482
|
+
* or
|
|
11483
|
+
* client.on(event => {console.log(event.type)})
|
|
11484
|
+
*
|
|
11485
|
+
* @param {EventHandler | string} callbackOrEventName The event type to listen for (optional)
|
|
11486
|
+
* @param {EventHandler} [callbackOrNothing] The callback to call
|
|
11487
|
+
*
|
|
11488
|
+
* @return {Function} Returns a function which, when called, unsubscribes the event handler.
|
|
11489
|
+
*/
|
|
11490
|
+
this.on = (callbackOrEventName, callbackOrNothing) => {
|
|
11491
|
+
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11492
|
+
const callback = callbackOrNothing
|
|
11493
|
+
? callbackOrNothing
|
|
11494
|
+
: callbackOrEventName;
|
|
11495
|
+
if (!(key in this.listeners)) {
|
|
11496
|
+
this.listeners[key] = [];
|
|
11497
|
+
}
|
|
11498
|
+
this.listeners[key].push(callback);
|
|
11499
|
+
return () => {
|
|
11500
|
+
this.off(key, callback);
|
|
11501
|
+
};
|
|
11502
|
+
};
|
|
11503
|
+
/**
|
|
11504
|
+
* off - Remove the event handler
|
|
11505
|
+
*
|
|
11506
|
+
*/
|
|
11507
|
+
this.off = (callbackOrEventName, callbackOrNothing) => {
|
|
11508
|
+
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11509
|
+
const callback = callbackOrNothing
|
|
11510
|
+
? callbackOrNothing
|
|
11511
|
+
: callbackOrEventName;
|
|
11512
|
+
if (!(key in this.listeners)) {
|
|
11513
|
+
this.listeners[key] = [];
|
|
11514
|
+
}
|
|
11515
|
+
this.logger('debug', `Removing listener for ${key} event`);
|
|
11516
|
+
this.listeners[key] = this.listeners[key].filter((value) => value !== callback);
|
|
11517
|
+
};
|
|
11518
|
+
this._logApiRequest = (type, url, data, config) => {
|
|
11519
|
+
this.logger('trace', `client: ${type} - Request - ${url}`, {
|
|
11520
|
+
payload: data,
|
|
11521
|
+
config,
|
|
11522
|
+
});
|
|
11523
|
+
};
|
|
11524
|
+
this._logApiResponse = (type, url, response) => {
|
|
11525
|
+
this.logger('trace', `client:${type} - Response - url: ${url} > status ${response.status}`, {
|
|
11526
|
+
response,
|
|
11527
|
+
});
|
|
11528
|
+
this.logger('trace', `client:${type} - Response payload`, {
|
|
11529
|
+
response,
|
|
11530
|
+
});
|
|
11531
|
+
};
|
|
11532
|
+
this._logApiError = (type, url, error) => {
|
|
11533
|
+
this.logger('error', `client:${type} - Error - url: ${url}`, {
|
|
11534
|
+
url,
|
|
11535
|
+
error,
|
|
11536
|
+
});
|
|
11537
|
+
};
|
|
11459
11538
|
this.doAxiosRequest = (type, url, data, options = {}) => __awaiter(this, void 0, void 0, function* () {
|
|
11460
11539
|
var _g;
|
|
11461
11540
|
if (!options.publicEndpoint || this.user) {
|
|
@@ -11520,6 +11599,43 @@ class StreamClient {
|
|
|
11520
11599
|
}
|
|
11521
11600
|
}
|
|
11522
11601
|
});
|
|
11602
|
+
this.get = (url, params) => {
|
|
11603
|
+
return this.doAxiosRequest('get', url, null, {
|
|
11604
|
+
params,
|
|
11605
|
+
});
|
|
11606
|
+
};
|
|
11607
|
+
this.put = (url, data) => {
|
|
11608
|
+
return this.doAxiosRequest('put', url, data);
|
|
11609
|
+
};
|
|
11610
|
+
this.post = (url, data) => {
|
|
11611
|
+
return this.doAxiosRequest('post', url, data);
|
|
11612
|
+
};
|
|
11613
|
+
this.patch = (url, data) => {
|
|
11614
|
+
return this.doAxiosRequest('patch', url, data);
|
|
11615
|
+
};
|
|
11616
|
+
this.delete = (url, params) => {
|
|
11617
|
+
return this.doAxiosRequest('delete', url, null, {
|
|
11618
|
+
params,
|
|
11619
|
+
});
|
|
11620
|
+
};
|
|
11621
|
+
this.errorFromResponse = (response) => {
|
|
11622
|
+
let err;
|
|
11623
|
+
err = new ErrorFromResponse(`Stream error HTTP code: ${response.status}`);
|
|
11624
|
+
if (response.data && response.data.code) {
|
|
11625
|
+
err = new Error(`Stream error code ${response.data.code}: ${response.data.message}`);
|
|
11626
|
+
err.code = response.data.code;
|
|
11627
|
+
}
|
|
11628
|
+
err.response = response;
|
|
11629
|
+
err.status = response.status;
|
|
11630
|
+
return err;
|
|
11631
|
+
};
|
|
11632
|
+
this.handleResponse = (response) => {
|
|
11633
|
+
const data = response.data;
|
|
11634
|
+
if (isErrorResponse(response)) {
|
|
11635
|
+
throw this.errorFromResponse(response);
|
|
11636
|
+
}
|
|
11637
|
+
return data;
|
|
11638
|
+
};
|
|
11523
11639
|
this.dispatchEvent = (event) => {
|
|
11524
11640
|
if (!event.received_at)
|
|
11525
11641
|
event.received_at = new Date();
|
|
@@ -11547,10 +11663,119 @@ class StreamClient {
|
|
|
11547
11663
|
listener(event);
|
|
11548
11664
|
}
|
|
11549
11665
|
};
|
|
11666
|
+
/**
|
|
11667
|
+
* @private
|
|
11668
|
+
*/
|
|
11669
|
+
this.connect = () => __awaiter(this, void 0, void 0, function* () {
|
|
11670
|
+
if (!this.userID || !this._user) {
|
|
11671
|
+
throw Error('Call connectUser or connectAnonymousUser before starting the connection');
|
|
11672
|
+
}
|
|
11673
|
+
if (!this.wsBaseURL) {
|
|
11674
|
+
throw Error('Websocket base url not set');
|
|
11675
|
+
}
|
|
11676
|
+
if (!this.clientID) {
|
|
11677
|
+
throw Error('clientID is not set');
|
|
11678
|
+
}
|
|
11679
|
+
if (!this.wsConnection &&
|
|
11680
|
+
(this.options.warmUp || this.options.enableInsights)) {
|
|
11681
|
+
this._sayHi();
|
|
11682
|
+
}
|
|
11683
|
+
// The StableWSConnection handles all the reconnection logic.
|
|
11684
|
+
if (this.options.wsConnection && this.node) {
|
|
11685
|
+
// Intentionally avoiding adding ts generics on wsConnection in options since its only useful for unit test purpose.
|
|
11686
|
+
this.options.wsConnection.setClient(this);
|
|
11687
|
+
this.wsConnection = this.options
|
|
11688
|
+
.wsConnection;
|
|
11689
|
+
}
|
|
11690
|
+
else {
|
|
11691
|
+
this.wsConnection = new StableWSConnection(this);
|
|
11692
|
+
}
|
|
11693
|
+
try {
|
|
11694
|
+
// if fallback is used before, continue using it instead of waiting for WS to fail
|
|
11695
|
+
if (this.wsFallback) {
|
|
11696
|
+
return yield this.wsFallback.connect();
|
|
11697
|
+
}
|
|
11698
|
+
this.logger('info', 'StreamClient.connect: this.wsConnection.connect()');
|
|
11699
|
+
// if WSFallback is enabled, ws connect should timeout faster so fallback can try
|
|
11700
|
+
return yield this.wsConnection.connect(this.options.enableWSFallback
|
|
11701
|
+
? this.defaultWSTimeoutWithFallback
|
|
11702
|
+
: this.defaultWSTimeout);
|
|
11703
|
+
}
|
|
11704
|
+
catch (err) {
|
|
11705
|
+
// run fallback only if it's WS/Network error and not a normal API error
|
|
11706
|
+
// make sure browser is online before even trying the longpoll
|
|
11707
|
+
if (this.options.enableWSFallback &&
|
|
11708
|
+
// @ts-ignore
|
|
11709
|
+
isWSFailure(err) &&
|
|
11710
|
+
isOnline(this.logger)) {
|
|
11711
|
+
this.logger('warn', 'client:connect() - WS failed, fallback to longpoll');
|
|
11712
|
+
this.dispatchEvent({ type: 'transport.changed', mode: 'longpoll' });
|
|
11713
|
+
this.wsConnection._destroyCurrentWSConnection();
|
|
11714
|
+
this.wsConnection.disconnect().then(); // close WS so no retry
|
|
11715
|
+
this.wsFallback = new WSConnectionFallback(this);
|
|
11716
|
+
return yield this.wsFallback.connect();
|
|
11717
|
+
}
|
|
11718
|
+
throw err;
|
|
11719
|
+
}
|
|
11720
|
+
});
|
|
11721
|
+
/**
|
|
11722
|
+
* Check the connectivity with server for warmup purpose.
|
|
11723
|
+
*
|
|
11724
|
+
* @private
|
|
11725
|
+
*/
|
|
11726
|
+
this._sayHi = () => {
|
|
11727
|
+
const client_request_id = randomId();
|
|
11728
|
+
const opts = {
|
|
11729
|
+
headers: AxiosHeaders.from({
|
|
11730
|
+
'x-client-request-id': client_request_id,
|
|
11731
|
+
}),
|
|
11732
|
+
};
|
|
11733
|
+
this.doAxiosRequest('get', this.baseURL + '/hi', null, opts).catch((e) => {
|
|
11734
|
+
if (this.options.enableInsights) {
|
|
11735
|
+
postInsights('http_hi_failed', {
|
|
11736
|
+
api_key: this.key,
|
|
11737
|
+
err: e,
|
|
11738
|
+
client_request_id,
|
|
11739
|
+
});
|
|
11740
|
+
}
|
|
11741
|
+
});
|
|
11742
|
+
};
|
|
11743
|
+
this.getUserAgent = () => {
|
|
11744
|
+
return (this.userAgent ||
|
|
11745
|
+
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.39"}`);
|
|
11746
|
+
};
|
|
11747
|
+
this.setUserAgent = (userAgent) => {
|
|
11748
|
+
this.userAgent = userAgent;
|
|
11749
|
+
};
|
|
11550
11750
|
/**
|
|
11551
11751
|
* _isUsingServerAuth - Returns true if we're using server side auth
|
|
11552
11752
|
*/
|
|
11553
11753
|
this._isUsingServerAuth = () => !!this.secret;
|
|
11754
|
+
this._enrichAxiosOptions = (options = {
|
|
11755
|
+
params: {},
|
|
11756
|
+
headers: {},
|
|
11757
|
+
config: {},
|
|
11758
|
+
}) => {
|
|
11759
|
+
var _a;
|
|
11760
|
+
const token = options.publicEndpoint && !this.user ? undefined : this._getToken();
|
|
11761
|
+
const authorization = token ? { Authorization: token } : undefined;
|
|
11762
|
+
let signal = null;
|
|
11763
|
+
if (this.nextRequestAbortController !== null) {
|
|
11764
|
+
signal = this.nextRequestAbortController.signal;
|
|
11765
|
+
this.nextRequestAbortController = null;
|
|
11766
|
+
}
|
|
11767
|
+
if (!((_a = options.headers) === null || _a === void 0 ? void 0 : _a['x-client-request-id'])) {
|
|
11768
|
+
options.headers = Object.assign(Object.assign({}, options.headers), { 'x-client-request-id': randomId() });
|
|
11769
|
+
}
|
|
11770
|
+
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
|
|
11771
|
+
? 'anonymous'
|
|
11772
|
+
: this.getAuthType(), 'X-Stream-Client': this.getUserAgent() }), options.headers) }, (signal ? { signal } : {})), options.config), this.options.axiosRequestConfig);
|
|
11773
|
+
};
|
|
11774
|
+
this._getToken = () => {
|
|
11775
|
+
if (!this.tokenManager)
|
|
11776
|
+
return null;
|
|
11777
|
+
return this.tokenManager.getToken();
|
|
11778
|
+
};
|
|
11554
11779
|
/**
|
|
11555
11780
|
* encode ws url payload
|
|
11556
11781
|
* @private
|
|
@@ -11563,6 +11788,38 @@ class StreamClient {
|
|
|
11563
11788
|
client_request_id,
|
|
11564
11789
|
});
|
|
11565
11790
|
};
|
|
11791
|
+
/**
|
|
11792
|
+
* creates an abort controller that will be used by the next HTTP Request.
|
|
11793
|
+
*/
|
|
11794
|
+
this.createAbortControllerForNextRequest = () => {
|
|
11795
|
+
return (this.nextRequestAbortController = new AbortController());
|
|
11796
|
+
};
|
|
11797
|
+
/**
|
|
11798
|
+
* createToken - Creates a token to authenticate this user. This function is used server side.
|
|
11799
|
+
* The resulting token should be passed to the client side when the users registers or logs in.
|
|
11800
|
+
*
|
|
11801
|
+
* @param {string} userID The UserWithId ID
|
|
11802
|
+
* @param {number} [exp] The expiration time for the token expressed in the number of seconds since the epoch
|
|
11803
|
+
* @param call_cids for anonymous tokens you have to provide the call cids the use can join
|
|
11804
|
+
*
|
|
11805
|
+
* @return {string} Returns a token
|
|
11806
|
+
*/
|
|
11807
|
+
this.createToken = (userID, exp, iat, call_cids) => {
|
|
11808
|
+
if (this.secret == null) {
|
|
11809
|
+
throw Error(`tokens can only be created server-side using the API Secret`);
|
|
11810
|
+
}
|
|
11811
|
+
const extra = {};
|
|
11812
|
+
if (exp) {
|
|
11813
|
+
extra.exp = exp;
|
|
11814
|
+
}
|
|
11815
|
+
if (iat) {
|
|
11816
|
+
extra.iat = iat;
|
|
11817
|
+
}
|
|
11818
|
+
if (call_cids) {
|
|
11819
|
+
extra.call_cids = call_cids;
|
|
11820
|
+
}
|
|
11821
|
+
return JWTUserToken(this.secret, userID, extra, {});
|
|
11822
|
+
};
|
|
11566
11823
|
// set the key
|
|
11567
11824
|
this.key = key;
|
|
11568
11825
|
this.listeners = {};
|
|
@@ -11614,268 +11871,6 @@ class StreamClient {
|
|
|
11614
11871
|
? inputOptions.logger
|
|
11615
11872
|
: () => null;
|
|
11616
11873
|
}
|
|
11617
|
-
devToken(userID) {
|
|
11618
|
-
return DevToken(userID);
|
|
11619
|
-
}
|
|
11620
|
-
getAuthType() {
|
|
11621
|
-
return this.anonymous ? 'anonymous' : 'jwt';
|
|
11622
|
-
}
|
|
11623
|
-
setBaseURL(baseURL) {
|
|
11624
|
-
this.baseURL = baseURL;
|
|
11625
|
-
this.wsBaseURL = this.baseURL
|
|
11626
|
-
.replace('http', 'ws')
|
|
11627
|
-
.replace(':3030', ':8800');
|
|
11628
|
-
}
|
|
11629
|
-
_setUser(user) {
|
|
11630
|
-
/**
|
|
11631
|
-
* This one is used by the frontend. This is a copy of the current user object stored on backend.
|
|
11632
|
-
* It contains reserved properties and own user properties which are not present in `this._user`.
|
|
11633
|
-
*/
|
|
11634
|
-
this.user = user;
|
|
11635
|
-
this.userID = user.id;
|
|
11636
|
-
// this one is actually used for requests. This is a copy of current user provided to `connectUser` function.
|
|
11637
|
-
this._user = Object.assign({}, user);
|
|
11638
|
-
}
|
|
11639
|
-
/**
|
|
11640
|
-
* on - Listen to events on all channels and users your watching
|
|
11641
|
-
*
|
|
11642
|
-
* client.on('message.new', event => {console.log("my new message", event, channel.state.messages)})
|
|
11643
|
-
* or
|
|
11644
|
-
* client.on(event => {console.log(event.type)})
|
|
11645
|
-
*
|
|
11646
|
-
* @param {EventHandler | string} callbackOrEventName The event type to listen for (optional)
|
|
11647
|
-
* @param {EventHandler} [callbackOrNothing] The callback to call
|
|
11648
|
-
*
|
|
11649
|
-
* @return {Function} Returns a function which, when called, unsubscribes the event handler.
|
|
11650
|
-
*/
|
|
11651
|
-
on(callbackOrEventName, callbackOrNothing) {
|
|
11652
|
-
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11653
|
-
const callback = callbackOrNothing
|
|
11654
|
-
? callbackOrNothing
|
|
11655
|
-
: callbackOrEventName;
|
|
11656
|
-
if (!(key in this.listeners)) {
|
|
11657
|
-
this.listeners[key] = [];
|
|
11658
|
-
}
|
|
11659
|
-
this.listeners[key].push(callback);
|
|
11660
|
-
return () => {
|
|
11661
|
-
this.off(key, callback);
|
|
11662
|
-
};
|
|
11663
|
-
}
|
|
11664
|
-
/**
|
|
11665
|
-
* off - Remove the event handler
|
|
11666
|
-
*
|
|
11667
|
-
*/
|
|
11668
|
-
off(callbackOrEventName, callbackOrNothing) {
|
|
11669
|
-
const key = callbackOrNothing ? callbackOrEventName : 'all';
|
|
11670
|
-
const callback = callbackOrNothing
|
|
11671
|
-
? callbackOrNothing
|
|
11672
|
-
: callbackOrEventName;
|
|
11673
|
-
if (!(key in this.listeners)) {
|
|
11674
|
-
this.listeners[key] = [];
|
|
11675
|
-
}
|
|
11676
|
-
this.logger('debug', `Removing listener for ${key} event`);
|
|
11677
|
-
this.listeners[key] = this.listeners[key].filter((value) => value !== callback);
|
|
11678
|
-
}
|
|
11679
|
-
_logApiRequest(type, url, data, config) {
|
|
11680
|
-
this.logger('trace', `client: ${type} - Request - ${url}`, {
|
|
11681
|
-
payload: data,
|
|
11682
|
-
config,
|
|
11683
|
-
});
|
|
11684
|
-
}
|
|
11685
|
-
_logApiResponse(type, url, response) {
|
|
11686
|
-
this.logger('trace', `client:${type} - Response - url: ${url} > status ${response.status}`, {
|
|
11687
|
-
response,
|
|
11688
|
-
});
|
|
11689
|
-
this.logger('trace', `client:${type} - Response payload`, {
|
|
11690
|
-
response,
|
|
11691
|
-
});
|
|
11692
|
-
}
|
|
11693
|
-
_logApiError(type, url, error) {
|
|
11694
|
-
this.logger('error', `client:${type} - Error - url: ${url}`, {
|
|
11695
|
-
url,
|
|
11696
|
-
error,
|
|
11697
|
-
});
|
|
11698
|
-
}
|
|
11699
|
-
get(url, params) {
|
|
11700
|
-
return this.doAxiosRequest('get', url, null, {
|
|
11701
|
-
params,
|
|
11702
|
-
});
|
|
11703
|
-
}
|
|
11704
|
-
put(url, data) {
|
|
11705
|
-
return this.doAxiosRequest('put', url, data);
|
|
11706
|
-
}
|
|
11707
|
-
post(url, data) {
|
|
11708
|
-
return this.doAxiosRequest('post', url, data);
|
|
11709
|
-
}
|
|
11710
|
-
patch(url, data) {
|
|
11711
|
-
return this.doAxiosRequest('patch', url, data);
|
|
11712
|
-
}
|
|
11713
|
-
delete(url, params) {
|
|
11714
|
-
return this.doAxiosRequest('delete', url, null, {
|
|
11715
|
-
params,
|
|
11716
|
-
});
|
|
11717
|
-
}
|
|
11718
|
-
errorFromResponse(response) {
|
|
11719
|
-
let err;
|
|
11720
|
-
err = new ErrorFromResponse(`Stream error HTTP code: ${response.status}`);
|
|
11721
|
-
if (response.data && response.data.code) {
|
|
11722
|
-
err = new Error(`Stream error code ${response.data.code}: ${response.data.message}`);
|
|
11723
|
-
err.code = response.data.code;
|
|
11724
|
-
}
|
|
11725
|
-
err.response = response;
|
|
11726
|
-
err.status = response.status;
|
|
11727
|
-
return err;
|
|
11728
|
-
}
|
|
11729
|
-
handleResponse(response) {
|
|
11730
|
-
const data = response.data;
|
|
11731
|
-
if (isErrorResponse(response)) {
|
|
11732
|
-
throw this.errorFromResponse(response);
|
|
11733
|
-
}
|
|
11734
|
-
return data;
|
|
11735
|
-
}
|
|
11736
|
-
/**
|
|
11737
|
-
* @private
|
|
11738
|
-
*/
|
|
11739
|
-
connect() {
|
|
11740
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
11741
|
-
if (!this.userID || !this._user) {
|
|
11742
|
-
throw Error('Call connectUser or connectAnonymousUser before starting the connection');
|
|
11743
|
-
}
|
|
11744
|
-
if (!this.wsBaseURL) {
|
|
11745
|
-
throw Error('Websocket base url not set');
|
|
11746
|
-
}
|
|
11747
|
-
if (!this.clientID) {
|
|
11748
|
-
throw Error('clientID is not set');
|
|
11749
|
-
}
|
|
11750
|
-
if (!this.wsConnection &&
|
|
11751
|
-
(this.options.warmUp || this.options.enableInsights)) {
|
|
11752
|
-
this._sayHi();
|
|
11753
|
-
}
|
|
11754
|
-
// The StableWSConnection handles all the reconnection logic.
|
|
11755
|
-
if (this.options.wsConnection && this.node) {
|
|
11756
|
-
// Intentionally avoiding adding ts generics on wsConnection in options since its only useful for unit test purpose.
|
|
11757
|
-
this.options.wsConnection.setClient(this);
|
|
11758
|
-
this.wsConnection = this.options
|
|
11759
|
-
.wsConnection;
|
|
11760
|
-
}
|
|
11761
|
-
else {
|
|
11762
|
-
this.wsConnection = new StableWSConnection(this);
|
|
11763
|
-
}
|
|
11764
|
-
try {
|
|
11765
|
-
// if fallback is used before, continue using it instead of waiting for WS to fail
|
|
11766
|
-
if (this.wsFallback) {
|
|
11767
|
-
return yield this.wsFallback.connect();
|
|
11768
|
-
}
|
|
11769
|
-
this.logger('info', 'StreamClient.connect: this.wsConnection.connect()');
|
|
11770
|
-
// if WSFallback is enabled, ws connect should timeout faster so fallback can try
|
|
11771
|
-
return yield this.wsConnection.connect(this.options.enableWSFallback
|
|
11772
|
-
? this.defaultWSTimeoutWithFallback
|
|
11773
|
-
: this.defaultWSTimeout);
|
|
11774
|
-
}
|
|
11775
|
-
catch (err) {
|
|
11776
|
-
// run fallback only if it's WS/Network error and not a normal API error
|
|
11777
|
-
// make sure browser is online before even trying the longpoll
|
|
11778
|
-
if (this.options.enableWSFallback &&
|
|
11779
|
-
// @ts-ignore
|
|
11780
|
-
isWSFailure(err) &&
|
|
11781
|
-
isOnline(this.logger)) {
|
|
11782
|
-
this.logger('warn', 'client:connect() - WS failed, fallback to longpoll');
|
|
11783
|
-
this.dispatchEvent({ type: 'transport.changed', mode: 'longpoll' });
|
|
11784
|
-
this.wsConnection._destroyCurrentWSConnection();
|
|
11785
|
-
this.wsConnection.disconnect().then(); // close WS so no retry
|
|
11786
|
-
this.wsFallback = new WSConnectionFallback(this);
|
|
11787
|
-
return yield this.wsFallback.connect();
|
|
11788
|
-
}
|
|
11789
|
-
throw err;
|
|
11790
|
-
}
|
|
11791
|
-
});
|
|
11792
|
-
}
|
|
11793
|
-
/**
|
|
11794
|
-
* Check the connectivity with server for warmup purpose.
|
|
11795
|
-
*
|
|
11796
|
-
* @private
|
|
11797
|
-
*/
|
|
11798
|
-
_sayHi() {
|
|
11799
|
-
const client_request_id = randomId();
|
|
11800
|
-
const opts = {
|
|
11801
|
-
headers: AxiosHeaders.from({
|
|
11802
|
-
'x-client-request-id': client_request_id,
|
|
11803
|
-
}),
|
|
11804
|
-
};
|
|
11805
|
-
this.doAxiosRequest('get', this.baseURL + '/hi', null, opts).catch((e) => {
|
|
11806
|
-
if (this.options.enableInsights) {
|
|
11807
|
-
postInsights('http_hi_failed', {
|
|
11808
|
-
api_key: this.key,
|
|
11809
|
-
err: e,
|
|
11810
|
-
client_request_id,
|
|
11811
|
-
});
|
|
11812
|
-
}
|
|
11813
|
-
});
|
|
11814
|
-
}
|
|
11815
|
-
getUserAgent() {
|
|
11816
|
-
return (this.userAgent ||
|
|
11817
|
-
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.37"}`);
|
|
11818
|
-
}
|
|
11819
|
-
setUserAgent(userAgent) {
|
|
11820
|
-
this.userAgent = userAgent;
|
|
11821
|
-
}
|
|
11822
|
-
_enrichAxiosOptions(options = {
|
|
11823
|
-
params: {},
|
|
11824
|
-
headers: {},
|
|
11825
|
-
config: {},
|
|
11826
|
-
}) {
|
|
11827
|
-
var _a;
|
|
11828
|
-
const token = options.publicEndpoint && !this.user ? undefined : this._getToken();
|
|
11829
|
-
const authorization = token ? { Authorization: token } : undefined;
|
|
11830
|
-
let signal = null;
|
|
11831
|
-
if (this.nextRequestAbortController !== null) {
|
|
11832
|
-
signal = this.nextRequestAbortController.signal;
|
|
11833
|
-
this.nextRequestAbortController = null;
|
|
11834
|
-
}
|
|
11835
|
-
if (!((_a = options.headers) === null || _a === void 0 ? void 0 : _a['x-client-request-id'])) {
|
|
11836
|
-
options.headers = Object.assign(Object.assign({}, options.headers), { 'x-client-request-id': randomId() });
|
|
11837
|
-
}
|
|
11838
|
-
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
|
|
11839
|
-
? 'anonymous'
|
|
11840
|
-
: this.getAuthType(), 'X-Stream-Client': this.getUserAgent() }), options.headers) }, (signal ? { signal } : {})), options.config), this.options.axiosRequestConfig);
|
|
11841
|
-
}
|
|
11842
|
-
_getToken() {
|
|
11843
|
-
if (!this.tokenManager)
|
|
11844
|
-
return null;
|
|
11845
|
-
return this.tokenManager.getToken();
|
|
11846
|
-
}
|
|
11847
|
-
/**
|
|
11848
|
-
* creates an abort controller that will be used by the next HTTP Request.
|
|
11849
|
-
*/
|
|
11850
|
-
createAbortControllerForNextRequest() {
|
|
11851
|
-
return (this.nextRequestAbortController = new AbortController());
|
|
11852
|
-
}
|
|
11853
|
-
/**
|
|
11854
|
-
* createToken - Creates a token to authenticate this user. This function is used server side.
|
|
11855
|
-
* The resulting token should be passed to the client side when the users registers or logs in.
|
|
11856
|
-
*
|
|
11857
|
-
* @param {string} userID The UserWithId ID
|
|
11858
|
-
* @param {number} [exp] The expiration time for the token expressed in the number of seconds since the epoch
|
|
11859
|
-
* @param call_cids for anonymous tokens you have to provide the call cids the use can join
|
|
11860
|
-
*
|
|
11861
|
-
* @return {string} Returns a token
|
|
11862
|
-
*/
|
|
11863
|
-
createToken(userID, exp, iat, call_cids) {
|
|
11864
|
-
if (this.secret == null) {
|
|
11865
|
-
throw Error(`tokens can only be created server-side using the API Secret`);
|
|
11866
|
-
}
|
|
11867
|
-
const extra = {};
|
|
11868
|
-
if (exp) {
|
|
11869
|
-
extra.exp = exp;
|
|
11870
|
-
}
|
|
11871
|
-
if (iat) {
|
|
11872
|
-
extra.iat = iat;
|
|
11873
|
-
}
|
|
11874
|
-
if (call_cids) {
|
|
11875
|
-
extra.call_cids = call_cids;
|
|
11876
|
-
}
|
|
11877
|
-
return JWTUserToken(this.secret, userID, extra, {});
|
|
11878
|
-
}
|
|
11879
11874
|
}
|
|
11880
11875
|
|
|
11881
11876
|
/**
|