@stream-io/video-client 1.39.2 → 1.40.0
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 +13 -0
- package/dist/index.browser.es.js +72 -25
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +72 -25
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +72 -25
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +17 -2
- package/dist/src/StreamSfuClient.d.ts +6 -1
- package/dist/src/gen/coordinator/index.d.ts +450 -16
- package/dist/src/rpc/createClient.d.ts +3 -1
- package/dist/src/rpc/retryable.d.ts +3 -1
- package/dist/src/rpc/types.d.ts +8 -0
- package/package.json +1 -1
- package/src/Call.ts +33 -0
- package/src/StreamSfuClient.ts +51 -11
- package/src/StreamVideoClient.ts +0 -1
- package/src/__tests__/StreamVideoClient.ringing.test.ts +153 -14
- package/src/gen/coordinator/index.ts +448 -16
- package/src/rpc/__tests__/createClient.test.ts +80 -0
- package/src/rpc/__tests__/retryable.test.ts +12 -0
- package/src/rpc/createClient.ts +53 -7
- package/src/rpc/retryable.ts +8 -2
- package/src/rpc/types.ts +11 -0
package/dist/index.es.js
CHANGED
|
@@ -3717,7 +3717,35 @@ const withHeaders = (headers) => {
|
|
|
3717
3717
|
},
|
|
3718
3718
|
};
|
|
3719
3719
|
};
|
|
3720
|
-
const
|
|
3720
|
+
const TIMEOUT_SYMBOL = '@@stream-io/timeout';
|
|
3721
|
+
const withTimeout = (timeoutMs, trace) => {
|
|
3722
|
+
const scheduleTimeout = (methodName) => {
|
|
3723
|
+
const controller = new AbortController();
|
|
3724
|
+
// aborts with specially crafted error that can be reliably recognized by
|
|
3725
|
+
// @protobuf-ts/twirp-transport and our internal retry logic.
|
|
3726
|
+
// https://github.com/timostamm/protobuf-ts/blob/657e64e80009e503e94f608fda423fbcbf4fb5a7/packages/twirp-transport/src/twirp-transport.ts#L102-L107
|
|
3727
|
+
const timeoutId = setTimeout(() => {
|
|
3728
|
+
trace?.(`${methodName}Timeout`, [timeoutMs]);
|
|
3729
|
+
const error = new Error(TIMEOUT_SYMBOL);
|
|
3730
|
+
error.name = 'AbortError';
|
|
3731
|
+
controller.abort(error);
|
|
3732
|
+
}, timeoutMs);
|
|
3733
|
+
return [controller.signal, () => clearTimeout(timeoutId)];
|
|
3734
|
+
};
|
|
3735
|
+
return {
|
|
3736
|
+
interceptUnary(next, method, input, options) {
|
|
3737
|
+
// respect external abort signals if provided
|
|
3738
|
+
if (options.abort)
|
|
3739
|
+
return next(method, input, options);
|
|
3740
|
+
// set up a custom abort signal for the RPC call
|
|
3741
|
+
const [signal, cancel] = scheduleTimeout(method.name);
|
|
3742
|
+
const invocation = next(method, input, { ...options, abort: signal });
|
|
3743
|
+
invocation.then(cancel, cancel);
|
|
3744
|
+
return invocation;
|
|
3745
|
+
},
|
|
3746
|
+
};
|
|
3747
|
+
};
|
|
3748
|
+
const withRequestLogger = (logger, level = 'trace') => {
|
|
3721
3749
|
return {
|
|
3722
3750
|
interceptUnary: (next, method, input, options) => {
|
|
3723
3751
|
const invocation = next(method, input, options);
|
|
@@ -3736,18 +3764,21 @@ const withRequestTracer = (trace) => {
|
|
|
3736
3764
|
const traceError = (name, input, err) => trace(`${name}OnFailure`, [err, input]);
|
|
3737
3765
|
return {
|
|
3738
3766
|
interceptUnary(next, method, input, options) {
|
|
3739
|
-
const
|
|
3740
|
-
if (exclusions.has(
|
|
3767
|
+
const methodName = method.name;
|
|
3768
|
+
if (exclusions.has(methodName))
|
|
3741
3769
|
return next(method, input, options);
|
|
3742
|
-
|
|
3770
|
+
const { invocationMeta: { attempt = 0 } = {} } = options;
|
|
3771
|
+
const traceName = attempt === 0 ? methodName : `${methodName}(${attempt})`;
|
|
3772
|
+
trace(traceName, input);
|
|
3743
3773
|
const unaryCall = next(method, input, options);
|
|
3744
3774
|
unaryCall.then((invocation) => {
|
|
3745
3775
|
const response = invocation.response;
|
|
3746
3776
|
if (response.error)
|
|
3747
|
-
traceError(
|
|
3748
|
-
if (responseInclusions.has(
|
|
3749
|
-
trace(`${
|
|
3750
|
-
|
|
3777
|
+
traceError(traceName, input, response.error);
|
|
3778
|
+
if (responseInclusions.has(methodName)) {
|
|
3779
|
+
trace(`${traceName}Response`, response);
|
|
3780
|
+
}
|
|
3781
|
+
}, (error) => traceError(methodName, input, error));
|
|
3751
3782
|
return unaryCall;
|
|
3752
3783
|
},
|
|
3753
3784
|
};
|
|
@@ -3899,22 +3930,26 @@ const videoLoggerSystem = scopedLogger.createLoggerSystem();
|
|
|
3899
3930
|
*
|
|
3900
3931
|
* @param rpc the closure around the RPC call to execute.
|
|
3901
3932
|
* @param signal the signal to abort the RPC call and retries loop.
|
|
3933
|
+
* @param maxRetries the maximum number of retries to perform. Defaults to `Number.POSITIVE_INFINITY`.
|
|
3902
3934
|
*/
|
|
3903
|
-
const retryable = async (rpc, signal) => {
|
|
3935
|
+
const retryable = async (rpc, signal, maxRetries = Number.POSITIVE_INFINITY) => {
|
|
3904
3936
|
let attempt = 0;
|
|
3905
3937
|
let result = undefined;
|
|
3906
3938
|
do {
|
|
3907
3939
|
if (attempt > 0)
|
|
3908
3940
|
await sleep(retryInterval(attempt));
|
|
3909
3941
|
try {
|
|
3910
|
-
result = await rpc();
|
|
3942
|
+
result = await rpc({ attempt });
|
|
3911
3943
|
}
|
|
3912
3944
|
catch (err) {
|
|
3913
3945
|
const isRequestCancelled = err instanceof RpcError &&
|
|
3946
|
+
err.message !== TIMEOUT_SYMBOL &&
|
|
3914
3947
|
err.code === TwirpErrorCode[TwirpErrorCode.cancelled];
|
|
3915
3948
|
const isAborted = signal?.aborted ?? false;
|
|
3916
3949
|
if (isRequestCancelled || isAborted)
|
|
3917
3950
|
throw err;
|
|
3951
|
+
if (attempt + 1 >= maxRetries)
|
|
3952
|
+
throw err;
|
|
3918
3953
|
videoLoggerSystem
|
|
3919
3954
|
.getLogger('sfu-client', { tags: ['rpc'] })
|
|
3920
3955
|
.debug(`rpc failed (${attempt})`, err);
|
|
@@ -6026,7 +6061,7 @@ const getSdkVersion = (sdk) => {
|
|
|
6026
6061
|
return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
|
|
6027
6062
|
};
|
|
6028
6063
|
|
|
6029
|
-
const version = "1.
|
|
6064
|
+
const version = "1.40.0";
|
|
6030
6065
|
const [major, minor, patch] = version.split('.');
|
|
6031
6066
|
let sdkInfo = {
|
|
6032
6067
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -8264,7 +8299,7 @@ class StreamSfuClient {
|
|
|
8264
8299
|
/**
|
|
8265
8300
|
* Constructs a new SFU client.
|
|
8266
8301
|
*/
|
|
8267
|
-
constructor({ dispatcher, credentials, sessionId, cid, tag, joinResponseTimeout = 5000, onSignalClose, streamClient, enableTracing, }) {
|
|
8302
|
+
constructor({ dispatcher, credentials, sessionId, cid, tag, joinResponseTimeout = 5000, rpcRequestTimeout = 5000, onSignalClose, streamClient, enableTracing, }) {
|
|
8268
8303
|
/**
|
|
8269
8304
|
* A buffer for ICE Candidates that are received before
|
|
8270
8305
|
* the Publisher and Subscriber Peer Connections are ready to handle them.
|
|
@@ -8393,27 +8428,27 @@ class StreamSfuClient {
|
|
|
8393
8428
|
};
|
|
8394
8429
|
this.updateSubscriptions = async (tracks) => {
|
|
8395
8430
|
await this.joinTask;
|
|
8396
|
-
return retryable(() => this.rpc.updateSubscriptions({ sessionId: this.sessionId, tracks }), this.abortController.signal);
|
|
8431
|
+
return retryable((invocationMeta) => this.rpc.updateSubscriptions({ sessionId: this.sessionId, tracks }, { invocationMeta }), this.abortController.signal);
|
|
8397
8432
|
};
|
|
8398
8433
|
this.setPublisher = async (data) => {
|
|
8399
8434
|
await this.joinTask;
|
|
8400
|
-
return retryable(() => this.rpc.setPublisher({ ...data, sessionId: this.sessionId }), this.abortController.signal);
|
|
8435
|
+
return retryable((invocationMeta) => this.rpc.setPublisher({ ...data, sessionId: this.sessionId }, { invocationMeta }), this.abortController.signal, 3);
|
|
8401
8436
|
};
|
|
8402
8437
|
this.sendAnswer = async (data) => {
|
|
8403
8438
|
await this.joinTask;
|
|
8404
|
-
return retryable(() => this.rpc.sendAnswer({ ...data, sessionId: this.sessionId }), this.abortController.signal);
|
|
8439
|
+
return retryable((invocationMeta) => this.rpc.sendAnswer({ ...data, sessionId: this.sessionId }, { invocationMeta }), this.abortController.signal);
|
|
8405
8440
|
};
|
|
8406
8441
|
this.iceTrickle = async (data) => {
|
|
8407
8442
|
await this.joinTask;
|
|
8408
|
-
return retryable(() => this.rpc.iceTrickle({ ...data, sessionId: this.sessionId }), this.abortController.signal);
|
|
8443
|
+
return retryable((invocationMeta) => this.rpc.iceTrickle({ ...data, sessionId: this.sessionId }, { invocationMeta }), this.abortController.signal);
|
|
8409
8444
|
};
|
|
8410
8445
|
this.iceRestart = async (data) => {
|
|
8411
8446
|
await this.joinTask;
|
|
8412
|
-
return retryable(() => this.rpc.iceRestart({ ...data, sessionId: this.sessionId }), this.abortController.signal);
|
|
8447
|
+
return retryable((invocationMeta) => this.rpc.iceRestart({ ...data, sessionId: this.sessionId }, { invocationMeta }), this.abortController.signal);
|
|
8413
8448
|
};
|
|
8414
8449
|
this.updateMuteStates = async (muteStates) => {
|
|
8415
8450
|
await this.joinTask;
|
|
8416
|
-
return retryable(() => this.rpc.updateMuteStates({ muteStates, sessionId: this.sessionId }), this.abortController.signal);
|
|
8451
|
+
return retryable((invocationMeta) => this.rpc.updateMuteStates({ muteStates, sessionId: this.sessionId }, { invocationMeta }), this.abortController.signal);
|
|
8417
8452
|
};
|
|
8418
8453
|
this.sendStats = async (stats) => {
|
|
8419
8454
|
await this.joinTask;
|
|
@@ -8422,11 +8457,11 @@ class StreamSfuClient {
|
|
|
8422
8457
|
};
|
|
8423
8458
|
this.startNoiseCancellation = async () => {
|
|
8424
8459
|
await this.joinTask;
|
|
8425
|
-
return retryable(() => this.rpc.startNoiseCancellation({ sessionId: this.sessionId }), this.abortController.signal);
|
|
8460
|
+
return retryable((invocationMeta) => this.rpc.startNoiseCancellation({ sessionId: this.sessionId }, { invocationMeta }), this.abortController.signal);
|
|
8426
8461
|
};
|
|
8427
8462
|
this.stopNoiseCancellation = async () => {
|
|
8428
8463
|
await this.joinTask;
|
|
8429
|
-
return retryable(() => this.rpc.stopNoiseCancellation({ sessionId: this.sessionId }), this.abortController.signal);
|
|
8464
|
+
return retryable((invocationMeta) => this.rpc.stopNoiseCancellation({ sessionId: this.sessionId }, { invocationMeta }), this.abortController.signal);
|
|
8430
8465
|
};
|
|
8431
8466
|
this.enterMigration = async (opts = {}) => {
|
|
8432
8467
|
this.isLeaving = true;
|
|
@@ -8551,8 +8586,8 @@ class StreamSfuClient {
|
|
|
8551
8586
|
interceptors: [
|
|
8552
8587
|
withHeaders({ Authorization: `Bearer ${token}` }),
|
|
8553
8588
|
this.tracer && withRequestTracer(this.tracer.trace),
|
|
8554
|
-
this.logger.getLogLevel() === 'trace' &&
|
|
8555
|
-
|
|
8589
|
+
this.logger.getLogLevel() === 'trace' && withRequestLogger(this.logger),
|
|
8590
|
+
withTimeout(rpcRequestTimeout, this.tracer?.trace),
|
|
8556
8591
|
].filter((v) => !!v),
|
|
8557
8592
|
});
|
|
8558
8593
|
// Special handling for the ICETrickle kind of events.
|
|
@@ -12362,12 +12397,14 @@ class Call {
|
|
|
12362
12397
|
*
|
|
12363
12398
|
* @returns a promise which resolves once the call join-flow has finished.
|
|
12364
12399
|
*/
|
|
12365
|
-
this.join = async ({ maxJoinRetries = 3, ...data } = {}) => {
|
|
12400
|
+
this.join = async ({ maxJoinRetries = 3, joinResponseTimeout, rpcRequestTimeout, ...data } = {}) => {
|
|
12366
12401
|
await this.setup();
|
|
12367
12402
|
const callingState = this.state.callingState;
|
|
12368
12403
|
if ([CallingState.JOINED, CallingState.JOINING].includes(callingState)) {
|
|
12369
12404
|
throw new Error(`Illegal State: call.join() shall be called only once`);
|
|
12370
12405
|
}
|
|
12406
|
+
this.joinResponseTimeout = joinResponseTimeout;
|
|
12407
|
+
this.rpcRequestTimeout = rpcRequestTimeout;
|
|
12371
12408
|
// we will count the number of join failures per SFU.
|
|
12372
12409
|
// once the number of failures reaches 2, we will piggyback on the `migrating_from`
|
|
12373
12410
|
// field to force the coordinator to provide us another SFU
|
|
@@ -12448,6 +12485,8 @@ class Call {
|
|
|
12448
12485
|
credentials: this.credentials,
|
|
12449
12486
|
streamClient: this.streamClient,
|
|
12450
12487
|
enableTracing: statsOptions.enable_rtc_stats,
|
|
12488
|
+
joinResponseTimeout: this.joinResponseTimeout,
|
|
12489
|
+
rpcRequestTimeout: this.rpcRequestTimeout,
|
|
12451
12490
|
// a new session_id is necessary for the REJOIN strategy.
|
|
12452
12491
|
// we use the previous session_id if available
|
|
12453
12492
|
sessionId: performingRejoin ? undefined : previousSessionId,
|
|
@@ -13716,6 +13755,15 @@ class Call {
|
|
|
13716
13755
|
},
|
|
13717
13756
|
});
|
|
13718
13757
|
};
|
|
13758
|
+
/**
|
|
13759
|
+
* Retrieves the call stats for the current call session in a format suitable
|
|
13760
|
+
* for displaying in map-like UIs.
|
|
13761
|
+
*/
|
|
13762
|
+
this.getCallStatsMap = async (params = {}, callSessionId = this.state.session?.id) => {
|
|
13763
|
+
if (!callSessionId)
|
|
13764
|
+
throw new Error('callSessionId is required');
|
|
13765
|
+
return this.streamClient.get(`${this.streamClient.baseURL}/call_stats/${this.type}/${this.id}/${callSessionId}/map`, params);
|
|
13766
|
+
};
|
|
13719
13767
|
/**
|
|
13720
13768
|
* Sends a custom event to all call participants.
|
|
13721
13769
|
*
|
|
@@ -15041,7 +15089,7 @@ class StreamClient {
|
|
|
15041
15089
|
this.getUserAgent = () => {
|
|
15042
15090
|
if (!this.cachedUserAgent) {
|
|
15043
15091
|
const { clientAppIdentifier = {} } = this.options;
|
|
15044
|
-
const { sdkName = 'js', sdkVersion = "1.
|
|
15092
|
+
const { sdkName = 'js', sdkVersion = "1.40.0", ...extras } = clientAppIdentifier;
|
|
15045
15093
|
this.cachedUserAgent = [
|
|
15046
15094
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
15047
15095
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -15599,7 +15647,6 @@ class StreamVideoClient {
|
|
|
15599
15647
|
return false;
|
|
15600
15648
|
return this.state.calls.some((c) => c.cid !== currentCallId &&
|
|
15601
15649
|
c.ringing &&
|
|
15602
|
-
!c.isCreatedByMe &&
|
|
15603
15650
|
c.state.callingState !== CallingState.IDLE &&
|
|
15604
15651
|
c.state.callingState !== CallingState.LEFT &&
|
|
15605
15652
|
c.state.callingState !== CallingState.RECONNECTING_FAILED);
|