@stream-io/video-client 1.11.11 → 1.11.13
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 +232 -73
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +232 -73
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +232 -73
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +7 -0
- package/dist/src/StreamSfuClient.d.ts +1 -3
- package/dist/src/coordinator/connection/connection.d.ts +1 -1
- package/dist/src/coordinator/connection/types.d.ts +5 -0
- package/dist/src/helpers/promise.d.ts +14 -0
- package/dist/src/timers/index.d.ts +22 -0
- package/dist/src/timers/types.d.ts +12 -0
- package/dist/src/timers/worker.build.d.ts +3 -0
- package/dist/src/timers/worker.d.ts +1 -0
- package/package.json +4 -3
- package/src/Call.ts +38 -3
- package/src/StreamSfuClient.ts +33 -32
- package/src/StreamVideoClient.ts +4 -0
- package/src/coordinator/connection/connection.ts +14 -5
- package/src/coordinator/connection/types.ts +6 -0
- package/src/helpers/promise.ts +44 -0
- package/src/timers/index.ts +137 -0
- package/src/timers/types.ts +15 -0
- package/src/timers/worker.build.ts +26 -0
- package/src/timers/worker.ts +40 -0
- package/dist/src/helpers/withResolvers.d.ts +0 -14
- package/src/helpers/withResolvers.ts +0 -43
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [1.11.13](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.11.12...@stream-io/video-client-1.11.13) (2024-12-03)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* use worker to prevent timer throttling ([#1557](https://github.com/GetStream/stream-video-js/issues/1557)) ([c11c3ca](https://github.com/GetStream/stream-video-js/commit/c11c3caf455787fe531c83601bad71e7a0a0e9b9))
|
|
11
|
+
|
|
12
|
+
## [1.11.12](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.11.11...@stream-io/video-client-1.11.12) (2024-12-03)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* handle timeout on SFU WS connections ([#1600](https://github.com/GetStream/stream-video-js/issues/1600)) ([5f2db7b](https://github.com/GetStream/stream-video-js/commit/5f2db7bd5cfdf57cdc04d6a6ed752f43e5b06657))
|
|
18
|
+
|
|
5
19
|
## [1.11.11](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.11.10...@stream-io/video-client-1.11.11) (2024-11-29)
|
|
6
20
|
|
|
7
21
|
|
package/dist/index.browser.es.js
CHANGED
|
@@ -3297,7 +3297,7 @@ const retryable = async (rpc, signal) => {
|
|
|
3297
3297
|
return result;
|
|
3298
3298
|
};
|
|
3299
3299
|
|
|
3300
|
-
const version = "1.11.
|
|
3300
|
+
const version = "1.11.13";
|
|
3301
3301
|
const [major, minor, patch] = version.split('.');
|
|
3302
3302
|
let sdkInfo = {
|
|
3303
3303
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -6374,6 +6374,32 @@ const createWebSocketSignalChannel = (opts) => {
|
|
|
6374
6374
|
return ws;
|
|
6375
6375
|
};
|
|
6376
6376
|
|
|
6377
|
+
/**
|
|
6378
|
+
* Saving a long-lived reference to a promise that can reject can be unsafe,
|
|
6379
|
+
* since rejecting the promise causes an unhandled rejection error (even if the
|
|
6380
|
+
* rejection is handled everywhere promise result is expected).
|
|
6381
|
+
*
|
|
6382
|
+
* To avoid that, we add both resolution and rejection handlers to the promise.
|
|
6383
|
+
* That way, the saved promise never rejects. A callback is provided as return
|
|
6384
|
+
* value to build a *new* promise, that resolves and rejects along with
|
|
6385
|
+
* the original promise.
|
|
6386
|
+
* @param promise Promise to wrap, which possibly rejects
|
|
6387
|
+
* @returns Callback to build a new promise, which resolves and rejects along
|
|
6388
|
+
* with the original promise
|
|
6389
|
+
*/
|
|
6390
|
+
function makeSafePromise(promise) {
|
|
6391
|
+
let isPending = true;
|
|
6392
|
+
const safePromise = promise
|
|
6393
|
+
.then((result) => ({ status: 'resolved', result }), (error) => ({ status: 'rejected', error }))
|
|
6394
|
+
.finally(() => (isPending = false));
|
|
6395
|
+
const unwrapPromise = () => safePromise.then((fulfillment) => {
|
|
6396
|
+
if (fulfillment.status === 'rejected')
|
|
6397
|
+
throw fulfillment.error;
|
|
6398
|
+
return fulfillment.result;
|
|
6399
|
+
});
|
|
6400
|
+
unwrapPromise.checkPending = () => isPending;
|
|
6401
|
+
return unwrapPromise;
|
|
6402
|
+
}
|
|
6377
6403
|
/**
|
|
6378
6404
|
* Creates a new promise with resolvers.
|
|
6379
6405
|
*
|
|
@@ -6406,6 +6432,151 @@ const promiseWithResolvers = () => {
|
|
|
6406
6432
|
};
|
|
6407
6433
|
};
|
|
6408
6434
|
|
|
6435
|
+
const uninitialized = Symbol('uninitialized');
|
|
6436
|
+
/**
|
|
6437
|
+
* Lazily creates a value using a provided factory
|
|
6438
|
+
*/
|
|
6439
|
+
function lazy(factory) {
|
|
6440
|
+
let value = uninitialized;
|
|
6441
|
+
return () => {
|
|
6442
|
+
if (value === uninitialized) {
|
|
6443
|
+
value = factory();
|
|
6444
|
+
}
|
|
6445
|
+
return value;
|
|
6446
|
+
};
|
|
6447
|
+
}
|
|
6448
|
+
|
|
6449
|
+
const timerWorker = {
|
|
6450
|
+
src: `var timerIdMapping = new Map();
|
|
6451
|
+
self.addEventListener('message', function (event) {
|
|
6452
|
+
var request = event.data;
|
|
6453
|
+
switch (request.type) {
|
|
6454
|
+
case 'setTimeout':
|
|
6455
|
+
case 'setInterval':
|
|
6456
|
+
timerIdMapping.set(request.id, (request.type === 'setTimeout' ? setTimeout : setInterval)(function () {
|
|
6457
|
+
tick(request.id);
|
|
6458
|
+
if (request.type === 'setTimeout') {
|
|
6459
|
+
timerIdMapping.delete(request.id);
|
|
6460
|
+
}
|
|
6461
|
+
}, request.timeout));
|
|
6462
|
+
break;
|
|
6463
|
+
case 'clearTimeout':
|
|
6464
|
+
case 'clearInterval':
|
|
6465
|
+
(request.type === 'clearTimeout' ? clearTimeout : clearInterval)(timerIdMapping.get(request.id));
|
|
6466
|
+
timerIdMapping.delete(request.id);
|
|
6467
|
+
break;
|
|
6468
|
+
}
|
|
6469
|
+
});
|
|
6470
|
+
function tick(id) {
|
|
6471
|
+
var message = { type: 'tick', id: id };
|
|
6472
|
+
self.postMessage(message);
|
|
6473
|
+
}`,
|
|
6474
|
+
};
|
|
6475
|
+
|
|
6476
|
+
class TimerWorker {
|
|
6477
|
+
constructor() {
|
|
6478
|
+
this.currentTimerId = 1;
|
|
6479
|
+
this.callbacks = new Map();
|
|
6480
|
+
this.fallback = false;
|
|
6481
|
+
}
|
|
6482
|
+
setup({ useTimerWorker = true } = {}) {
|
|
6483
|
+
if (!useTimerWorker) {
|
|
6484
|
+
this.fallback = true;
|
|
6485
|
+
return;
|
|
6486
|
+
}
|
|
6487
|
+
try {
|
|
6488
|
+
const source = timerWorker.src;
|
|
6489
|
+
const blob = new Blob([source], {
|
|
6490
|
+
type: 'application/javascript; charset=utf-8',
|
|
6491
|
+
});
|
|
6492
|
+
const script = URL.createObjectURL(blob);
|
|
6493
|
+
this.worker = new Worker(script, { name: 'str-timer-worker' });
|
|
6494
|
+
this.worker.addEventListener('message', (event) => {
|
|
6495
|
+
const { type, id } = event.data;
|
|
6496
|
+
if (type === 'tick') {
|
|
6497
|
+
this.callbacks.get(id)?.();
|
|
6498
|
+
}
|
|
6499
|
+
});
|
|
6500
|
+
}
|
|
6501
|
+
catch (err) {
|
|
6502
|
+
getLogger(['timer-worker'])('error', err);
|
|
6503
|
+
this.fallback = true;
|
|
6504
|
+
}
|
|
6505
|
+
}
|
|
6506
|
+
destroy() {
|
|
6507
|
+
this.callbacks.clear();
|
|
6508
|
+
this.worker?.terminate();
|
|
6509
|
+
this.worker = undefined;
|
|
6510
|
+
this.fallback = false;
|
|
6511
|
+
}
|
|
6512
|
+
get ready() {
|
|
6513
|
+
return this.fallback || Boolean(this.worker);
|
|
6514
|
+
}
|
|
6515
|
+
setInterval(callback, timeout) {
|
|
6516
|
+
return this.setTimer('setInterval', callback, timeout);
|
|
6517
|
+
}
|
|
6518
|
+
clearInterval(id) {
|
|
6519
|
+
this.clearTimer('clearInterval', id);
|
|
6520
|
+
}
|
|
6521
|
+
setTimeout(callback, timeout) {
|
|
6522
|
+
return this.setTimer('setTimeout', callback, timeout);
|
|
6523
|
+
}
|
|
6524
|
+
clearTimeout(id) {
|
|
6525
|
+
this.clearTimer('clearTimeout', id);
|
|
6526
|
+
}
|
|
6527
|
+
setTimer(type, callback, timeout) {
|
|
6528
|
+
if (!this.ready) {
|
|
6529
|
+
this.setup();
|
|
6530
|
+
}
|
|
6531
|
+
if (this.fallback) {
|
|
6532
|
+
return (type === 'setTimeout' ? setTimeout : setInterval)(callback, timeout);
|
|
6533
|
+
}
|
|
6534
|
+
const id = this.getTimerId();
|
|
6535
|
+
this.callbacks.set(id, () => {
|
|
6536
|
+
callback();
|
|
6537
|
+
// Timeouts are one-off operations, so no need to keep callback reference
|
|
6538
|
+
// after timer has fired
|
|
6539
|
+
if (type === 'setTimeout') {
|
|
6540
|
+
this.callbacks.delete(id);
|
|
6541
|
+
}
|
|
6542
|
+
});
|
|
6543
|
+
this.sendMessage({ type, id, timeout });
|
|
6544
|
+
return id;
|
|
6545
|
+
}
|
|
6546
|
+
clearTimer(type, id) {
|
|
6547
|
+
if (!id) {
|
|
6548
|
+
return;
|
|
6549
|
+
}
|
|
6550
|
+
if (!this.ready) {
|
|
6551
|
+
this.setup();
|
|
6552
|
+
}
|
|
6553
|
+
if (this.fallback) {
|
|
6554
|
+
(type === 'clearTimeout' ? clearTimeout : clearInterval)(id);
|
|
6555
|
+
return;
|
|
6556
|
+
}
|
|
6557
|
+
this.callbacks.delete(id);
|
|
6558
|
+
this.sendMessage({ type, id });
|
|
6559
|
+
}
|
|
6560
|
+
getTimerId() {
|
|
6561
|
+
return this.currentTimerId++;
|
|
6562
|
+
}
|
|
6563
|
+
sendMessage(message) {
|
|
6564
|
+
if (!this.worker) {
|
|
6565
|
+
throw new Error("Cannot use timer worker before it's set up");
|
|
6566
|
+
}
|
|
6567
|
+
this.worker.postMessage(message);
|
|
6568
|
+
}
|
|
6569
|
+
}
|
|
6570
|
+
let timerWorkerEnabled = false;
|
|
6571
|
+
const enableTimerWorker = () => {
|
|
6572
|
+
timerWorkerEnabled = true;
|
|
6573
|
+
};
|
|
6574
|
+
const getTimers = lazy(() => {
|
|
6575
|
+
const instance = new TimerWorker();
|
|
6576
|
+
instance.setup({ useTimerWorker: timerWorkerEnabled });
|
|
6577
|
+
return instance;
|
|
6578
|
+
});
|
|
6579
|
+
|
|
6409
6580
|
/**
|
|
6410
6581
|
* The client used for exchanging information with the SFU.
|
|
6411
6582
|
*/
|
|
@@ -6426,7 +6597,6 @@ class StreamSfuClient {
|
|
|
6426
6597
|
this.isLeaving = false;
|
|
6427
6598
|
this.pingIntervalInMs = 10 * 1000;
|
|
6428
6599
|
this.unhealthyTimeoutInMs = this.pingIntervalInMs + 5 * 1000;
|
|
6429
|
-
this.restoreWebSocketConcurrencyTag = Symbol('recoverWebSocket');
|
|
6430
6600
|
/**
|
|
6431
6601
|
* Promise that resolves when the JoinResponse is received.
|
|
6432
6602
|
* Rejects after a certain threshold if the response is not received.
|
|
@@ -6447,31 +6617,25 @@ class StreamSfuClient {
|
|
|
6447
6617
|
},
|
|
6448
6618
|
});
|
|
6449
6619
|
this.signalWs.addEventListener('close', this.handleWebSocketClose);
|
|
6450
|
-
this.
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6620
|
+
this.signalReady = makeSafePromise(Promise.race([
|
|
6621
|
+
new Promise((resolve) => {
|
|
6622
|
+
const onOpen = () => {
|
|
6623
|
+
this.signalWs.removeEventListener('open', onOpen);
|
|
6624
|
+
resolve(this.signalWs);
|
|
6625
|
+
};
|
|
6626
|
+
this.signalWs.addEventListener('open', onOpen);
|
|
6627
|
+
}),
|
|
6628
|
+
new Promise((resolve, reject) => {
|
|
6629
|
+
setTimeout(() => reject(new Error('SFU WS connection timed out')), this.joinResponseTimeout);
|
|
6630
|
+
}),
|
|
6631
|
+
]));
|
|
6458
6632
|
};
|
|
6459
6633
|
this.cleanUpWebSocket = () => {
|
|
6460
|
-
this.signalWs.removeEventListener('error', this.restoreWebSocket);
|
|
6461
6634
|
this.signalWs.removeEventListener('close', this.handleWebSocketClose);
|
|
6462
6635
|
};
|
|
6463
|
-
this.restoreWebSocket = () => {
|
|
6464
|
-
withoutConcurrency(this.restoreWebSocketConcurrencyTag, async () => {
|
|
6465
|
-
await this.networkAvailableTask?.promise;
|
|
6466
|
-
this.logger('debug', 'Restoring SFU WS connection');
|
|
6467
|
-
this.cleanUpWebSocket();
|
|
6468
|
-
await sleep(500);
|
|
6469
|
-
this.createWebSocket();
|
|
6470
|
-
}).catch((err) => this.logger('debug', `Can't restore WS connection`, err));
|
|
6471
|
-
};
|
|
6472
6636
|
this.handleWebSocketClose = () => {
|
|
6473
6637
|
this.signalWs.removeEventListener('close', this.handleWebSocketClose);
|
|
6474
|
-
clearInterval(this.keepAliveInterval);
|
|
6638
|
+
getTimers().clearInterval(this.keepAliveInterval);
|
|
6475
6639
|
clearTimeout(this.connectionCheckTimeout);
|
|
6476
6640
|
this.onSignalClose?.();
|
|
6477
6641
|
};
|
|
@@ -6562,7 +6726,7 @@ class StreamSfuClient {
|
|
|
6562
6726
|
};
|
|
6563
6727
|
this.join = async (data) => {
|
|
6564
6728
|
// wait for the signal web socket to be ready before sending "joinRequest"
|
|
6565
|
-
await this.signalReady;
|
|
6729
|
+
await this.signalReady();
|
|
6566
6730
|
if (this.joinResponseTask.isResolved || this.joinResponseTask.isRejected) {
|
|
6567
6731
|
// we need to lock the RPC requests until we receive a JoinResponse.
|
|
6568
6732
|
// that's why we have this primitive lock mechanism.
|
|
@@ -6617,7 +6781,7 @@ class StreamSfuClient {
|
|
|
6617
6781
|
}));
|
|
6618
6782
|
};
|
|
6619
6783
|
this.send = async (message) => {
|
|
6620
|
-
await this.signalReady; // wait for the signal ws to be open
|
|
6784
|
+
await this.signalReady(); // wait for the signal ws to be open
|
|
6621
6785
|
const msgJson = SfuRequest.toJson(message);
|
|
6622
6786
|
if (this.signalWs.readyState !== WebSocket.OPEN) {
|
|
6623
6787
|
this.logger('debug', 'Signal WS is not open. Skipping message', msgJson);
|
|
@@ -6627,8 +6791,9 @@ class StreamSfuClient {
|
|
|
6627
6791
|
this.signalWs.send(SfuRequest.toBinary(message));
|
|
6628
6792
|
};
|
|
6629
6793
|
this.keepAlive = () => {
|
|
6630
|
-
|
|
6631
|
-
this.keepAliveInterval
|
|
6794
|
+
const timers = getTimers();
|
|
6795
|
+
timers.clearInterval(this.keepAliveInterval);
|
|
6796
|
+
this.keepAliveInterval = timers.setInterval(() => {
|
|
6632
6797
|
this.ping().catch((e) => {
|
|
6633
6798
|
this.logger('error', 'Error sending healthCheckRequest to SFU', e);
|
|
6634
6799
|
});
|
|
@@ -8209,20 +8374,6 @@ function canQueryPermissions() {
|
|
|
8209
8374
|
!!navigator.permissions?.query);
|
|
8210
8375
|
}
|
|
8211
8376
|
|
|
8212
|
-
const uninitialized = Symbol('uninitialized');
|
|
8213
|
-
/**
|
|
8214
|
-
* Lazily creates a value using a provided factory
|
|
8215
|
-
*/
|
|
8216
|
-
function lazy(factory) {
|
|
8217
|
-
let value = uninitialized;
|
|
8218
|
-
return () => {
|
|
8219
|
-
if (value === uninitialized) {
|
|
8220
|
-
value = factory();
|
|
8221
|
-
}
|
|
8222
|
-
return value;
|
|
8223
|
-
};
|
|
8224
|
-
}
|
|
8225
|
-
|
|
8226
8377
|
/**
|
|
8227
8378
|
* Returns an Observable that emits the list of available devices
|
|
8228
8379
|
* that meet the given constraints.
|
|
@@ -9834,6 +9985,7 @@ class Call {
|
|
|
9834
9985
|
this.reconnectAttempts = 0;
|
|
9835
9986
|
this.reconnectStrategy = WebsocketReconnectStrategy.UNSPECIFIED;
|
|
9836
9987
|
this.fastReconnectDeadlineSeconds = 0;
|
|
9988
|
+
this.disconnectionTimeoutSeconds = 0;
|
|
9837
9989
|
this.lastOfflineTimestamp = 0;
|
|
9838
9990
|
// maintain the order of publishing tracks to restore them after a reconnection
|
|
9839
9991
|
// it shouldn't contain duplicates
|
|
@@ -10350,6 +10502,10 @@ class Call {
|
|
|
10350
10502
|
*/
|
|
10351
10503
|
this.handleSfuSignalClose = (sfuClient) => {
|
|
10352
10504
|
this.logger('debug', '[Reconnect] SFU signal connection closed');
|
|
10505
|
+
// SFU WS closed before we finished current join, no need to schedule reconnect
|
|
10506
|
+
// because join operation will fail
|
|
10507
|
+
if (this.state.callingState === CallingState.JOINING)
|
|
10508
|
+
return;
|
|
10353
10509
|
// normal close, no need to reconnect
|
|
10354
10510
|
if (sfuClient.isLeaving)
|
|
10355
10511
|
return;
|
|
@@ -10365,10 +10521,21 @@ class Call {
|
|
|
10365
10521
|
* @param strategy the reconnection strategy to use.
|
|
10366
10522
|
*/
|
|
10367
10523
|
this.reconnect = async (strategy) => {
|
|
10524
|
+
if (this.state.callingState === CallingState.RECONNECTING ||
|
|
10525
|
+
this.state.callingState === CallingState.RECONNECTING_FAILED)
|
|
10526
|
+
return;
|
|
10368
10527
|
return withoutConcurrency(this.reconnectConcurrencyTag, async () => {
|
|
10369
10528
|
this.logger('info', `[Reconnect] Reconnecting with strategy ${WebsocketReconnectStrategy[strategy]}`);
|
|
10529
|
+
let reconnectStartTime = Date.now();
|
|
10370
10530
|
this.reconnectStrategy = strategy;
|
|
10371
10531
|
do {
|
|
10532
|
+
if (this.disconnectionTimeoutSeconds > 0 &&
|
|
10533
|
+
(Date.now() - reconnectStartTime) / 1000 >
|
|
10534
|
+
this.disconnectionTimeoutSeconds) {
|
|
10535
|
+
this.logger('warn', '[Reconnect] Stopping reconnection attempts after reaching disconnection timeout');
|
|
10536
|
+
this.state.setCallingState(CallingState.RECONNECTING_FAILED);
|
|
10537
|
+
return;
|
|
10538
|
+
}
|
|
10372
10539
|
// we don't increment reconnect attempts for the FAST strategy.
|
|
10373
10540
|
if (this.reconnectStrategy !== WebsocketReconnectStrategy.FAST) {
|
|
10374
10541
|
this.reconnectAttempts++;
|
|
@@ -10468,7 +10635,7 @@ class Call {
|
|
|
10468
10635
|
const currentPublisher = this.publisher;
|
|
10469
10636
|
currentSubscriber?.detachEventHandlers();
|
|
10470
10637
|
currentPublisher?.detachEventHandlers();
|
|
10471
|
-
const migrationTask = currentSfuClient.enterMigration();
|
|
10638
|
+
const migrationTask = makeSafePromise(currentSfuClient.enterMigration());
|
|
10472
10639
|
try {
|
|
10473
10640
|
const currentSfu = currentSfuClient.edgeName;
|
|
10474
10641
|
await this.join({ ...this.joinCallData, migrating_from: currentSfu });
|
|
@@ -10484,7 +10651,7 @@ class Call {
|
|
|
10484
10651
|
// Wait for the migration to complete, then close the previous SFU client
|
|
10485
10652
|
// and the peer connection instances. In case of failure, the migration
|
|
10486
10653
|
// task would throw an error and REJOIN would be attempted.
|
|
10487
|
-
await migrationTask;
|
|
10654
|
+
await migrationTask();
|
|
10488
10655
|
// in MIGRATE, we can consider the call as joined only after
|
|
10489
10656
|
// `participantMigrationComplete` event is received, signaled by
|
|
10490
10657
|
// the `migrationTask`
|
|
@@ -11340,6 +11507,14 @@ class Call {
|
|
|
11340
11507
|
this.dynascaleManager.setVideoTrackSubscriptionOverrides(enabled ? undefined : { enabled: false });
|
|
11341
11508
|
this.dynascaleManager.applyTrackSubscriptions();
|
|
11342
11509
|
};
|
|
11510
|
+
/**
|
|
11511
|
+
* Sets the maximum amount of time a user can remain waiting for a reconnect
|
|
11512
|
+
* after a network disruption
|
|
11513
|
+
* @param timeoutSeconds Timeout in seconds, or 0 to keep reconnecting indefinetely
|
|
11514
|
+
*/
|
|
11515
|
+
this.setDisconnectionTimeout = (timeoutSeconds) => {
|
|
11516
|
+
this.disconnectionTimeoutSeconds = timeoutSeconds;
|
|
11517
|
+
};
|
|
11343
11518
|
this.type = type;
|
|
11344
11519
|
this.id = id;
|
|
11345
11520
|
this.cid = `${type}:${id}`;
|
|
@@ -11495,33 +11670,6 @@ class Call {
|
|
|
11495
11670
|
|
|
11496
11671
|
var https = null;
|
|
11497
11672
|
|
|
11498
|
-
/**
|
|
11499
|
-
* Saving a long-lived reference to a promise that can reject can be unsafe,
|
|
11500
|
-
* since rejecting the promise causes an unhandled rejection error (even if the
|
|
11501
|
-
* rejection is handled everywhere promise result is expected).
|
|
11502
|
-
*
|
|
11503
|
-
* To avoid that, we add both resolution and rejection handlers to the promise.
|
|
11504
|
-
* That way, the saved promise never rejects. A callback is provided as return
|
|
11505
|
-
* value to build a *new* promise, that resolves and rejects along with
|
|
11506
|
-
* the original promise.
|
|
11507
|
-
* @param promise Promise to wrap, which possibly rejects
|
|
11508
|
-
* @returns Callback to build a new promise, which resolves and rejects along
|
|
11509
|
-
* with the original promise
|
|
11510
|
-
*/
|
|
11511
|
-
function makeSafePromise(promise) {
|
|
11512
|
-
let isPending = true;
|
|
11513
|
-
const safePromise = promise
|
|
11514
|
-
.then((result) => ({ status: 'resolved', result }), (error) => ({ status: 'rejected', error }))
|
|
11515
|
-
.finally(() => (isPending = false));
|
|
11516
|
-
const unwrapPromise = () => safePromise.then((fulfillment) => {
|
|
11517
|
-
if (fulfillment.status === 'rejected')
|
|
11518
|
-
throw fulfillment.error;
|
|
11519
|
-
return fulfillment.result;
|
|
11520
|
-
});
|
|
11521
|
-
unwrapPromise.checkPending = () => isPending;
|
|
11522
|
-
return unwrapPromise;
|
|
11523
|
-
}
|
|
11524
|
-
|
|
11525
11673
|
/**
|
|
11526
11674
|
* StableWSConnection - A WS connection that reconnects upon failure.
|
|
11527
11675
|
* - the browser will sometimes report that you're online or offline
|
|
@@ -11774,9 +11922,12 @@ class StableWSConnection {
|
|
|
11774
11922
|
* Schedules a next health check ping for websocket.
|
|
11775
11923
|
*/
|
|
11776
11924
|
this.scheduleNextPing = () => {
|
|
11925
|
+
const timers = getTimers();
|
|
11926
|
+
if (this.healthCheckTimeoutRef) {
|
|
11927
|
+
timers.clearTimeout(this.healthCheckTimeoutRef);
|
|
11928
|
+
}
|
|
11777
11929
|
// 30 seconds is the recommended interval (messenger uses this)
|
|
11778
|
-
|
|
11779
|
-
this.healthCheckTimeoutRef = setTimeout(() => {
|
|
11930
|
+
this.healthCheckTimeoutRef = timers.setTimeout(() => {
|
|
11780
11931
|
// send the healthcheck..., server replies with a health check event
|
|
11781
11932
|
const data = [{ type: 'health.check', client_id: this.client.clientID }];
|
|
11782
11933
|
// try to send on the connection
|
|
@@ -11919,8 +12070,12 @@ class StableWSConnection {
|
|
|
11919
12070
|
this.isConnecting = false;
|
|
11920
12071
|
this.isDisconnected = true;
|
|
11921
12072
|
// start by removing all the listeners
|
|
11922
|
-
|
|
11923
|
-
|
|
12073
|
+
if (this.healthCheckTimeoutRef) {
|
|
12074
|
+
getTimers().clearInterval(this.healthCheckTimeoutRef);
|
|
12075
|
+
}
|
|
12076
|
+
if (this.connectionCheckTimeoutRef) {
|
|
12077
|
+
clearInterval(this.connectionCheckTimeoutRef);
|
|
12078
|
+
}
|
|
11924
12079
|
removeConnectionEventListeners(this.onlineStatusChanged);
|
|
11925
12080
|
this.isHealthy = false;
|
|
11926
12081
|
let isClosedPromise;
|
|
@@ -12620,7 +12775,7 @@ class StreamClient {
|
|
|
12620
12775
|
return await this.wsConnection.connect(this.defaultWSTimeout);
|
|
12621
12776
|
};
|
|
12622
12777
|
this.getUserAgent = () => {
|
|
12623
|
-
const version = "1.11.
|
|
12778
|
+
const version = "1.11.13";
|
|
12624
12779
|
return (this.userAgent ||
|
|
12625
12780
|
`stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
|
|
12626
12781
|
};
|
|
@@ -13031,10 +13186,14 @@ class StreamVideoClient {
|
|
|
13031
13186
|
if (typeof apiKeyOrArgs === 'string') {
|
|
13032
13187
|
logLevel = opts?.logLevel || logLevel;
|
|
13033
13188
|
logger = opts?.logger || logger;
|
|
13189
|
+
if (opts?.expertimental_enableTimerWorker)
|
|
13190
|
+
enableTimerWorker();
|
|
13034
13191
|
}
|
|
13035
13192
|
else {
|
|
13036
13193
|
logLevel = apiKeyOrArgs.options?.logLevel || logLevel;
|
|
13037
13194
|
logger = apiKeyOrArgs.options?.logger || logger;
|
|
13195
|
+
if (apiKeyOrArgs.options?.expertimental_enableTimerWorker)
|
|
13196
|
+
enableTimerWorker();
|
|
13038
13197
|
}
|
|
13039
13198
|
setLogger(logger, logLevel);
|
|
13040
13199
|
this.logger = getLogger(['client']);
|