@signalwire/js 4.0.0-dev-20260910191529 → 4.0.0-dev-20260911135809
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/dist/browser.mjs +546 -192
- package/dist/browser.mjs.map +1 -1
- package/dist/browser.umd.js +546 -191
- package/dist/browser.umd.js.map +1 -1
- package/dist/index.cjs +496 -172
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2092 -2025
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2092 -2025
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +496 -173
- package/dist/index.mjs.map +1 -1
- package/dist/operators/index.cjs +1 -1
- package/dist/operators/index.mjs +1 -1
- package/dist/{operators-Bn4Ij3VB.cjs → operators-B3G5z2B1.cjs} +50 -1
- package/dist/operators-B3G5z2B1.cjs.map +1 -0
- package/dist/{operators-Zxmwpb0j.mjs → operators-GSyCbUIU.mjs} +33 -2
- package/dist/operators-GSyCbUIU.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/operators-Bn4Ij3VB.cjs.map +0 -1
- package/dist/operators-Zxmwpb0j.mjs.map +0 -1
package/dist/browser.umd.js
CHANGED
|
@@ -4192,7 +4192,7 @@ var require_merge$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
4192
4192
|
var empty_1$7 = require_empty();
|
|
4193
4193
|
var args_1$8 = require_args();
|
|
4194
4194
|
var from_1$4 = require_from();
|
|
4195
|
-
function merge$
|
|
4195
|
+
function merge$7() {
|
|
4196
4196
|
var args = [];
|
|
4197
4197
|
for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
|
|
4198
4198
|
var scheduler = args_1$8.popScheduler(args);
|
|
@@ -4200,7 +4200,7 @@ var require_merge$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
4200
4200
|
var sources = args;
|
|
4201
4201
|
return !sources.length ? empty_1$7.EMPTY : sources.length === 1 ? innerFrom_1$31.innerFrom(sources[0]) : mergeAll_1$3.mergeAll(concurrent)(from_1$4.from(sources, scheduler));
|
|
4202
4202
|
}
|
|
4203
|
-
exports.merge = merge$
|
|
4203
|
+
exports.merge = merge$7;
|
|
4204
4204
|
}));
|
|
4205
4205
|
|
|
4206
4206
|
//#endregion
|
|
@@ -4331,13 +4331,13 @@ var require_race$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
4331
4331
|
var innerFrom_1$28 = require_innerFrom();
|
|
4332
4332
|
var argsOrArgArray_1$4 = require_argsOrArgArray();
|
|
4333
4333
|
var OperatorSubscriber_1$48 = require_OperatorSubscriber();
|
|
4334
|
-
function race$
|
|
4334
|
+
function race$4() {
|
|
4335
4335
|
var sources = [];
|
|
4336
4336
|
for (var _i = 0; _i < arguments.length; _i++) sources[_i] = arguments[_i];
|
|
4337
4337
|
sources = argsOrArgArray_1$4.argsOrArgArray(sources);
|
|
4338
4338
|
return sources.length === 1 ? innerFrom_1$28.innerFrom(sources[0]) : new Observable_1$6.Observable(raceInit(sources));
|
|
4339
4339
|
}
|
|
4340
|
-
exports.race = race$
|
|
4340
|
+
exports.race = race$4;
|
|
4341
4341
|
function raceInit(sources) {
|
|
4342
4342
|
return function(subscriber) {
|
|
4343
4343
|
var subscriptions = [];
|
|
@@ -6082,7 +6082,7 @@ var require_merge = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
6082
6082
|
var mergeAll_1$2 = require_mergeAll();
|
|
6083
6083
|
var args_1$3 = require_args();
|
|
6084
6084
|
var from_1$1 = require_from();
|
|
6085
|
-
function merge$
|
|
6085
|
+
function merge$6() {
|
|
6086
6086
|
var args = [];
|
|
6087
6087
|
for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
|
|
6088
6088
|
var scheduler = args_1$3.popScheduler(args);
|
|
@@ -6091,7 +6091,7 @@ var require_merge = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
6091
6091
|
mergeAll_1$2.mergeAll(concurrent)(from_1$1.from(__spreadArray$8([source], __read$8(args)), scheduler)).subscribe(subscriber);
|
|
6092
6092
|
});
|
|
6093
6093
|
}
|
|
6094
|
-
exports.merge = merge$
|
|
6094
|
+
exports.merge = merge$6;
|
|
6095
6095
|
}));
|
|
6096
6096
|
|
|
6097
6097
|
//#endregion
|
|
@@ -6938,7 +6938,7 @@ var require_switchMap = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
6938
6938
|
var innerFrom_1$6 = require_innerFrom();
|
|
6939
6939
|
var lift_1$13 = require_lift();
|
|
6940
6940
|
var OperatorSubscriber_1$11 = require_OperatorSubscriber();
|
|
6941
|
-
function switchMap$
|
|
6941
|
+
function switchMap$8(project, resultSelector) {
|
|
6942
6942
|
return lift_1$13.operate(function(source, subscriber) {
|
|
6943
6943
|
var innerSubscriber = null;
|
|
6944
6944
|
var index = 0;
|
|
@@ -6962,7 +6962,7 @@ var require_switchMap = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
6962
6962
|
}));
|
|
6963
6963
|
});
|
|
6964
6964
|
}
|
|
6965
|
-
exports.switchMap = switchMap$
|
|
6965
|
+
exports.switchMap = switchMap$8;
|
|
6966
6966
|
}));
|
|
6967
6967
|
|
|
6968
6968
|
//#endregion
|
|
@@ -9028,6 +9028,25 @@ const DEFAULT_ICE_CANDIDATE_TIMEOUT_MS = 600;
|
|
|
9028
9028
|
const DEFAULT_ICE_GATHERING_TIMEOUT_MS = 6e3;
|
|
9029
9029
|
const DEFAULT_RECONNECT_CALLS_TIMEOUT_MS = 300 * 1e3;
|
|
9030
9030
|
const DEFAULT_CONNECTION_TIMEOUT_MS = 1e4;
|
|
9031
|
+
/**
|
|
9032
|
+
* How long call setup may wait for the network, measured from local media
|
|
9033
|
+
* settling until the member id arrives.
|
|
9034
|
+
*
|
|
9035
|
+
* Local media acquisition is deliberately NOT inside this: a permission prompt
|
|
9036
|
+
* or a device picker is human time, and a human may take as long as they like
|
|
9037
|
+
* without spending the server's budget. The clock starts when acquisition ends.
|
|
9038
|
+
*
|
|
9039
|
+
* Must exceed `iceGatheringTimeout + the RPC timeout`, which both run inside it.
|
|
9040
|
+
*/
|
|
9041
|
+
const DEFAULT_CALL_SIGNALING_TIMEOUT_MS = 12e3;
|
|
9042
|
+
/**
|
|
9043
|
+
* How long an auxiliary leg (screen share, additional device) may take to
|
|
9044
|
+
* connect once its media is in hand.
|
|
9045
|
+
*
|
|
9046
|
+
* One bound for both: the picker is human time and sits outside it. What remains
|
|
9047
|
+
* — offer, ICE, invite, answer, DTLS — is the same work for either leg kind.
|
|
9048
|
+
*/
|
|
9049
|
+
const DEFAULT_AUX_LEG_CONNECT_TIMEOUT_MS = 15e3;
|
|
9031
9050
|
const DEFAULT_RECONNECT_DELAY_MIN_MS = 100;
|
|
9032
9051
|
const DEFAULT_RECONNECT_DELAY_MAX_MS = 3e3;
|
|
9033
9052
|
const DEFAULT_DEVICE_DEBOUNCE_TIME_MS = 1500;
|
|
@@ -9380,6 +9399,18 @@ var CollectionFetchError = class extends Error {
|
|
|
9380
9399
|
this.name = "CollectionFetchError";
|
|
9381
9400
|
}
|
|
9382
9401
|
};
|
|
9402
|
+
/**
|
|
9403
|
+
* An auxiliary leg did not connect within its budget. Typed rather than a bare
|
|
9404
|
+
* RxJS `TimeoutError` so the leg and cause survive.
|
|
9405
|
+
*/
|
|
9406
|
+
var AuxiliaryLegTimeoutError = class extends Error {
|
|
9407
|
+
constructor(leg, originalError) {
|
|
9408
|
+
super(`Timed out waiting for the ${leg} connection to be established`, { cause: originalError });
|
|
9409
|
+
this.leg = leg;
|
|
9410
|
+
this.originalError = originalError;
|
|
9411
|
+
this.name = "AuxiliaryLegTimeoutError";
|
|
9412
|
+
}
|
|
9413
|
+
};
|
|
9383
9414
|
var MediaTrackError = class extends Error {
|
|
9384
9415
|
constructor(operation, kind, originalError) {
|
|
9385
9416
|
super(`Media track ${operation} failed for ${kind}`, { cause: originalError instanceof Error ? originalError : void 0 });
|
|
@@ -9656,9 +9687,9 @@ var require_loglevel = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
9656
9687
|
defaultLogger$1 = new Logger();
|
|
9657
9688
|
defaultLogger$1.getLogger = function getLogger$1(name) {
|
|
9658
9689
|
if (typeof name !== "symbol" && typeof name !== "string" || name === "") throw new TypeError("You must supply a name when creating a logger.");
|
|
9659
|
-
var logger$
|
|
9660
|
-
if (!logger$
|
|
9661
|
-
return logger$
|
|
9690
|
+
var logger$34 = _loggersByName[name];
|
|
9691
|
+
if (!logger$34) logger$34 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
|
|
9692
|
+
return logger$34;
|
|
9662
9693
|
};
|
|
9663
9694
|
var _log = typeof window !== undefinedType ? window.log : void 0;
|
|
9664
9695
|
defaultLogger$1.noConflict = function() {
|
|
@@ -9694,8 +9725,8 @@ const defaultLoggerLevel = defaultLogger.levels.WARN;
|
|
|
9694
9725
|
defaultLogger.setLevel(defaultLoggerLevel);
|
|
9695
9726
|
let userLogger = null;
|
|
9696
9727
|
/** Replace the built-in logger with a custom implementation. Pass `null` to restore defaults. */
|
|
9697
|
-
const setLogger = (logger$
|
|
9698
|
-
userLogger = logger$
|
|
9728
|
+
const setLogger = (logger$34) => {
|
|
9729
|
+
userLogger = logger$34;
|
|
9699
9730
|
};
|
|
9700
9731
|
let debugOptions = {};
|
|
9701
9732
|
/** Configure debug options (e.g., `{ logWsTraffic: true }`). */
|
|
@@ -9739,8 +9770,8 @@ const wsTraffic = (options) => {
|
|
|
9739
9770
|
loggerInstance.debug(`${options.type.toUpperCase()}: \n`, msg, "\n");
|
|
9740
9771
|
};
|
|
9741
9772
|
const getLogger = () => {
|
|
9742
|
-
const logger$
|
|
9743
|
-
return new Proxy(logger$
|
|
9773
|
+
const logger$34 = getLoggerInstance();
|
|
9774
|
+
return new Proxy(logger$34, { get(_target, prop, _receiver) {
|
|
9744
9775
|
if (prop === "wsTraffic") return wsTraffic;
|
|
9745
9776
|
const instance = getLoggerInstance();
|
|
9746
9777
|
const value = Reflect.get(instance, prop);
|
|
@@ -9792,7 +9823,7 @@ const asyncRetry = async ({ asyncCallable, maxRetries: retries = DEFAULT_MAX_RET
|
|
|
9792
9823
|
|
|
9793
9824
|
//#endregion
|
|
9794
9825
|
//#region src/controllers/HTTPRequestController.ts
|
|
9795
|
-
const logger$
|
|
9826
|
+
const logger$33 = getLogger();
|
|
9796
9827
|
const GET_PARAMS = {
|
|
9797
9828
|
method: "GET",
|
|
9798
9829
|
headers: { Accept: "application/json" }
|
|
@@ -9856,7 +9887,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
|
|
|
9856
9887
|
this._responses$.next(response);
|
|
9857
9888
|
return response;
|
|
9858
9889
|
} catch (error) {
|
|
9859
|
-
logger$
|
|
9890
|
+
logger$33.error("[HTTPRequestController] Request error:", error);
|
|
9860
9891
|
this._status$.next("error");
|
|
9861
9892
|
const err = error instanceof Error ? error : new Error("HTTP request failed", { cause: error });
|
|
9862
9893
|
this._errors$.next(err);
|
|
@@ -9883,7 +9914,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
|
|
|
9883
9914
|
const url = this.buildURL(request.url);
|
|
9884
9915
|
const headers = this.buildHeaders(request.headers);
|
|
9885
9916
|
const timeout$5 = request.timeout ?? this.requestTimeout;
|
|
9886
|
-
logger$
|
|
9917
|
+
logger$33.debug("[HTTPRequestController] Executing request:", {
|
|
9887
9918
|
method: request.method,
|
|
9888
9919
|
url,
|
|
9889
9920
|
headers: Object.keys(headers).reduce((acc, key) => {
|
|
@@ -9903,7 +9934,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
|
|
|
9903
9934
|
});
|
|
9904
9935
|
clearTimeout(timeoutId);
|
|
9905
9936
|
const httpResponse = await this.convertResponse(response);
|
|
9906
|
-
logger$
|
|
9937
|
+
logger$33.debug("[HTTPRequestController] Response received:", {
|
|
9907
9938
|
status: response.status,
|
|
9908
9939
|
statusText: response.statusText,
|
|
9909
9940
|
headers: [...response.headers.entries()],
|
|
@@ -9913,7 +9944,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
|
|
|
9913
9944
|
} catch (error) {
|
|
9914
9945
|
clearTimeout(timeoutId);
|
|
9915
9946
|
if (error instanceof Error && error.name === "AbortError") throw new RequestTimeoutError(`Request timeout after ${timeout$5}ms`, { cause: error });
|
|
9916
|
-
logger$
|
|
9947
|
+
logger$33.error("[HTTPRequestController] Request failed:", error);
|
|
9917
9948
|
throw error;
|
|
9918
9949
|
}
|
|
9919
9950
|
}
|
|
@@ -9927,8 +9958,8 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
|
|
|
9927
9958
|
const credential = this.getCredential();
|
|
9928
9959
|
if (credential.token) {
|
|
9929
9960
|
headers.Authorization = `Bearer ${credential.token}`;
|
|
9930
|
-
logger$
|
|
9931
|
-
} else logger$
|
|
9961
|
+
logger$33.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
|
|
9962
|
+
} else logger$33.warn("[HTTPRequestController] No credentials available for authentication");
|
|
9932
9963
|
return headers;
|
|
9933
9964
|
}
|
|
9934
9965
|
/**
|
|
@@ -10076,7 +10107,7 @@ function fromMsToSec(milliseconds) {
|
|
|
10076
10107
|
|
|
10077
10108
|
//#endregion
|
|
10078
10109
|
//#region src/containers/PreferencesContainer.ts
|
|
10079
|
-
const logger$
|
|
10110
|
+
const logger$32 = getLogger();
|
|
10080
10111
|
var PreferencesContainer = class PreferencesContainer {
|
|
10081
10112
|
static get instance() {
|
|
10082
10113
|
this._instance ??= new PreferencesContainer();
|
|
@@ -10738,7 +10769,7 @@ var ClientPreferences = class {
|
|
|
10738
10769
|
if (!this._storage) return;
|
|
10739
10770
|
const data = collectStoredPreferences();
|
|
10740
10771
|
this._storage.setItem(PREFERENCES_STORAGE_KEY, data, "local").catch((error) => {
|
|
10741
|
-
logger$
|
|
10772
|
+
logger$32.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
|
|
10742
10773
|
});
|
|
10743
10774
|
}
|
|
10744
10775
|
/** Loads preferences from storage and applies them to the container. */
|
|
@@ -10747,7 +10778,7 @@ var ClientPreferences = class {
|
|
|
10747
10778
|
this._storage.getItem(PREFERENCES_STORAGE_KEY, "local").then((stored) => {
|
|
10748
10779
|
if (stored) applyStoredPreferences(stored);
|
|
10749
10780
|
}).catch((error) => {
|
|
10750
|
-
logger$
|
|
10781
|
+
logger$32.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
|
|
10751
10782
|
});
|
|
10752
10783
|
}
|
|
10753
10784
|
};
|
|
@@ -10769,7 +10800,7 @@ function toError(value) {
|
|
|
10769
10800
|
//#endregion
|
|
10770
10801
|
//#region src/controllers/NavigatorDeviceController.ts
|
|
10771
10802
|
var import_cjs$29 = require_cjs();
|
|
10772
|
-
const logger$
|
|
10803
|
+
const logger$31 = getLogger();
|
|
10773
10804
|
/** Maps a device kind to its storage key. */
|
|
10774
10805
|
const DEVICE_STORAGE_KEYS = {
|
|
10775
10806
|
audioinput: DEVICE_STORAGE_KEY_AUDIO_INPUT,
|
|
@@ -10791,7 +10822,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
10791
10822
|
super();
|
|
10792
10823
|
this.webRTCApiProvider = webRTCApiProvider;
|
|
10793
10824
|
this.deviceChangeHandler = () => {
|
|
10794
|
-
logger$
|
|
10825
|
+
logger$31.debug("[DeviceController] Device change detected");
|
|
10795
10826
|
this.enumerateDevices();
|
|
10796
10827
|
};
|
|
10797
10828
|
this._devicesState$ = this.createBehaviorSubject(initialDevicesState);
|
|
@@ -10856,13 +10887,13 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
10856
10887
|
return this.cachedObservable("videoInputDevices$", () => this._devicesState$.pipe((0, import_cjs$29.map)((state) => state.videoinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
|
|
10857
10888
|
}
|
|
10858
10889
|
get selectedAudioInputDevice$() {
|
|
10859
|
-
return this.cachedObservable("selectedAudioInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.audioinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$
|
|
10890
|
+
return this.cachedObservable("selectedAudioInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.audioinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$31.debug("[DeviceController] Selected audio input device changed:", info))));
|
|
10860
10891
|
}
|
|
10861
10892
|
get selectedAudioOutputDevice$() {
|
|
10862
|
-
return this.cachedObservable("selectedAudioOutputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.audiooutput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$
|
|
10893
|
+
return this.cachedObservable("selectedAudioOutputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.audiooutput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$31.debug("[DeviceController] Selected audio output device changed:", info))));
|
|
10863
10894
|
}
|
|
10864
10895
|
get selectedVideoInputDevice$() {
|
|
10865
|
-
return this.cachedObservable("selectedVideoInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.videoinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$
|
|
10896
|
+
return this.cachedObservable("selectedVideoInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.videoinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$31.debug("[DeviceController] Selected video input device changed:", info))));
|
|
10866
10897
|
}
|
|
10867
10898
|
get selectedAudioInputDevice() {
|
|
10868
10899
|
if (this._audioInputDisabled$.value) return null;
|
|
@@ -10937,7 +10968,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
10937
10968
|
if (device) this.persistDeviceSelection("audioinput", device);
|
|
10938
10969
|
}
|
|
10939
10970
|
selectVideoInputDevice(device) {
|
|
10940
|
-
logger$
|
|
10971
|
+
logger$31.debug("[DeviceController] Setting selected video input device:", device);
|
|
10941
10972
|
if (this._videoInputDisabled$.value && device) this._videoInputDisabled$.next(false);
|
|
10942
10973
|
const previous = this._selectedDevicesState$.value.videoinput;
|
|
10943
10974
|
if (previous && previous.deviceId !== device?.deviceId) this._deviceHistory.push("videoinput", previous);
|
|
@@ -10994,7 +11025,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
10994
11025
|
}
|
|
10995
11026
|
const fromHistory = this._deviceHistory.findInHistory(kind, devices);
|
|
10996
11027
|
if (fromHistory) {
|
|
10997
|
-
logger$
|
|
11028
|
+
logger$31.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
|
|
10998
11029
|
this.emitDeviceRecovered(kind, selected, fromHistory, "device_disconnected");
|
|
10999
11030
|
return fromHistory;
|
|
11000
11031
|
}
|
|
@@ -11047,7 +11078,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
11047
11078
|
try {
|
|
11048
11079
|
await this._storageManager.setItem(DEVICE_STORAGE_KEYS[kind], stored, "local");
|
|
11049
11080
|
} catch (error) {
|
|
11050
|
-
logger$
|
|
11081
|
+
logger$31.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
|
|
11051
11082
|
}
|
|
11052
11083
|
}
|
|
11053
11084
|
async loadPersistedDevices() {
|
|
@@ -11063,7 +11094,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
11063
11094
|
[kind]: stored
|
|
11064
11095
|
};
|
|
11065
11096
|
} catch (error) {
|
|
11066
|
-
logger$
|
|
11097
|
+
logger$31.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
|
|
11067
11098
|
}
|
|
11068
11099
|
}
|
|
11069
11100
|
/** Clears device history, persisted selections, and re-enumerates devices. */
|
|
@@ -11081,7 +11112,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
11081
11112
|
this.disableDeviceMonitoring();
|
|
11082
11113
|
this.webRTCApiProvider.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
|
|
11083
11114
|
if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$29.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
|
|
11084
|
-
logger$
|
|
11115
|
+
logger$31.debug("[DeviceController] Polling devices due to interval");
|
|
11085
11116
|
this.enumerateDevices();
|
|
11086
11117
|
});
|
|
11087
11118
|
this.enumerateDevices();
|
|
@@ -11107,13 +11138,13 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
11107
11138
|
videoinput: []
|
|
11108
11139
|
});
|
|
11109
11140
|
this._devicesState$.next(devicesByKind);
|
|
11110
|
-
logger$
|
|
11141
|
+
logger$31.debug("[DeviceController] Devices enumerated:", {
|
|
11111
11142
|
audioInputs: devicesByKind.audioinput.length,
|
|
11112
11143
|
audioOutputs: devicesByKind.audiooutput.length,
|
|
11113
11144
|
videoInputs: devicesByKind.videoinput.length
|
|
11114
11145
|
});
|
|
11115
11146
|
} catch (error) {
|
|
11116
|
-
logger$
|
|
11147
|
+
logger$31.error("[DeviceController] Failed to enumerate devices:", error);
|
|
11117
11148
|
this._errors$.next(toError(error));
|
|
11118
11149
|
}
|
|
11119
11150
|
}
|
|
@@ -11129,7 +11160,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
11129
11160
|
stream.getTracks().forEach((t) => t.stop());
|
|
11130
11161
|
return capabilities;
|
|
11131
11162
|
} catch (error) {
|
|
11132
|
-
logger$
|
|
11163
|
+
logger$31.error("[DeviceController] Failed to get device capabilities:", error);
|
|
11133
11164
|
this._errors$.next(toError(error));
|
|
11134
11165
|
throw error;
|
|
11135
11166
|
}
|
|
@@ -11380,7 +11411,7 @@ var DependencyContainer = class {
|
|
|
11380
11411
|
|
|
11381
11412
|
//#endregion
|
|
11382
11413
|
//#region src/controllers/CryptoController.ts
|
|
11383
|
-
const logger$
|
|
11414
|
+
const logger$30 = getLogger();
|
|
11384
11415
|
const DPOP_DB_NAME = "sw-dpop";
|
|
11385
11416
|
const DPOP_DB_VERSION = 1;
|
|
11386
11417
|
const DPOP_STORE_NAME = "keys";
|
|
@@ -11439,7 +11470,7 @@ async function loadKeyPairFromDB() {
|
|
|
11439
11470
|
tx.oncomplete = () => db.close();
|
|
11440
11471
|
});
|
|
11441
11472
|
} catch (error) {
|
|
11442
|
-
logger$
|
|
11473
|
+
logger$30.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
|
|
11443
11474
|
return null;
|
|
11444
11475
|
}
|
|
11445
11476
|
}
|
|
@@ -11459,7 +11490,7 @@ async function saveKeyPairToDB(keyPair) {
|
|
|
11459
11490
|
};
|
|
11460
11491
|
});
|
|
11461
11492
|
} catch (error) {
|
|
11462
|
-
logger$
|
|
11493
|
+
logger$30.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
|
|
11463
11494
|
}
|
|
11464
11495
|
}
|
|
11465
11496
|
async function deleteKeyPairFromDB() {
|
|
@@ -11478,7 +11509,7 @@ async function deleteKeyPairFromDB() {
|
|
|
11478
11509
|
};
|
|
11479
11510
|
});
|
|
11480
11511
|
} catch (error) {
|
|
11481
|
-
logger$
|
|
11512
|
+
logger$30.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
|
|
11482
11513
|
}
|
|
11483
11514
|
}
|
|
11484
11515
|
/**
|
|
@@ -11538,13 +11569,13 @@ var CryptoController = class {
|
|
|
11538
11569
|
this._publicJwk = await crypto.subtle.exportKey("jwk", stored.publicKey);
|
|
11539
11570
|
this._fingerprint = await computeJwkThumbprint(this._publicJwk);
|
|
11540
11571
|
this._initialized = true;
|
|
11541
|
-
logger$
|
|
11572
|
+
logger$30.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
|
|
11542
11573
|
return this._fingerprint;
|
|
11543
11574
|
} catch (error) {
|
|
11544
|
-
logger$
|
|
11575
|
+
logger$30.warn("[DPoP] Stored key pair unusable, generating new one:", error);
|
|
11545
11576
|
await deleteKeyPairFromDB();
|
|
11546
11577
|
}
|
|
11547
|
-
logger$
|
|
11578
|
+
logger$30.debug("[DPoP] Generating RSA key pair");
|
|
11548
11579
|
this._keyPair = await crypto.subtle.generateKey({
|
|
11549
11580
|
name: "RSASSA-PKCS1-v1_5",
|
|
11550
11581
|
modulusLength: 2048,
|
|
@@ -11559,7 +11590,7 @@ var CryptoController = class {
|
|
|
11559
11590
|
this._fingerprint = await computeJwkThumbprint(this._publicJwk);
|
|
11560
11591
|
this._initialized = true;
|
|
11561
11592
|
await saveKeyPairToDB(this._keyPair);
|
|
11562
|
-
logger$
|
|
11593
|
+
logger$30.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
|
|
11563
11594
|
return this._fingerprint;
|
|
11564
11595
|
}
|
|
11565
11596
|
/**
|
|
@@ -11625,7 +11656,7 @@ var CryptoController = class {
|
|
|
11625
11656
|
this._fingerprint = null;
|
|
11626
11657
|
this._initialized = false;
|
|
11627
11658
|
deleteKeyPairFromDB();
|
|
11628
|
-
logger$
|
|
11659
|
+
logger$30.debug("[DPoP] Controller destroyed");
|
|
11629
11660
|
}
|
|
11630
11661
|
get publicJwk() {
|
|
11631
11662
|
if (!this._publicJwk) throw new DPoPInitError("CryptoController not initialized. Call init() first.");
|
|
@@ -11649,7 +11680,7 @@ var CryptoController = class {
|
|
|
11649
11680
|
//#endregion
|
|
11650
11681
|
//#region src/controllers/NetworkMonitor.ts
|
|
11651
11682
|
var import_cjs$28 = require_cjs();
|
|
11652
|
-
const logger$
|
|
11683
|
+
const logger$29 = getLogger();
|
|
11653
11684
|
/**
|
|
11654
11685
|
* Safely check whether we are running in a browser environment
|
|
11655
11686
|
* with `window` and the relevant event targets.
|
|
@@ -11706,7 +11737,7 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
11706
11737
|
}
|
|
11707
11738
|
attachListeners() {
|
|
11708
11739
|
if (!hasBrowserNetworkEvents()) {
|
|
11709
|
-
logger$
|
|
11740
|
+
logger$29.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
|
|
11710
11741
|
return;
|
|
11711
11742
|
}
|
|
11712
11743
|
window.addEventListener("online", this._onOnline);
|
|
@@ -11714,7 +11745,7 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
11714
11745
|
const connection = getNetworkConnection();
|
|
11715
11746
|
if (connection) connection.addEventListener("change", this._onConnectionChange);
|
|
11716
11747
|
this._listenersAttached = true;
|
|
11717
|
-
logger$
|
|
11748
|
+
logger$29.debug("NetworkMonitor: event listeners attached");
|
|
11718
11749
|
}
|
|
11719
11750
|
removeListeners() {
|
|
11720
11751
|
if (!this._listenersAttached) return;
|
|
@@ -11725,10 +11756,10 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
11725
11756
|
if (connection) connection.removeEventListener("change", this._onConnectionChange);
|
|
11726
11757
|
}
|
|
11727
11758
|
this._listenersAttached = false;
|
|
11728
|
-
logger$
|
|
11759
|
+
logger$29.debug("NetworkMonitor: event listeners removed");
|
|
11729
11760
|
}
|
|
11730
11761
|
handleOnline() {
|
|
11731
|
-
logger$
|
|
11762
|
+
logger$29.info("NetworkMonitor: browser went online");
|
|
11732
11763
|
this._isOnline$.next(true);
|
|
11733
11764
|
this._networkChange$.next({
|
|
11734
11765
|
type: "online",
|
|
@@ -11737,7 +11768,7 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
11737
11768
|
});
|
|
11738
11769
|
}
|
|
11739
11770
|
handleOffline() {
|
|
11740
|
-
logger$
|
|
11771
|
+
logger$29.info("NetworkMonitor: browser went offline");
|
|
11741
11772
|
this._isOnline$.next(false);
|
|
11742
11773
|
this._networkChange$.next({
|
|
11743
11774
|
type: "offline",
|
|
@@ -11746,7 +11777,7 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
11746
11777
|
}
|
|
11747
11778
|
handleConnectionChange() {
|
|
11748
11779
|
const networkType = getNetworkType();
|
|
11749
|
-
logger$
|
|
11780
|
+
logger$29.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
|
|
11750
11781
|
this._networkChange$.next({
|
|
11751
11782
|
type: "connection_change",
|
|
11752
11783
|
timestamp: Date.now(),
|
|
@@ -11862,7 +11893,7 @@ function getNavigatorMediaDevices() {
|
|
|
11862
11893
|
//#endregion
|
|
11863
11894
|
//#region src/controllers/PreflightRunner.ts
|
|
11864
11895
|
var import_cjs$27 = require_cjs();
|
|
11865
|
-
const logger$
|
|
11896
|
+
const logger$28 = getLogger();
|
|
11866
11897
|
const DEFAULT_MEDIA_TEST_DURATION_S = 10;
|
|
11867
11898
|
const ICE_GATHERING_TIMEOUT_MS = 1e4;
|
|
11868
11899
|
const SIGNALING_RTT_TIMEOUT_MS = 5e3;
|
|
@@ -11911,7 +11942,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
11911
11942
|
if (!this._options.skipMediaTest) try {
|
|
11912
11943
|
bandwidth = await this.testMediaBandwidth(destination);
|
|
11913
11944
|
} catch (error) {
|
|
11914
|
-
logger$
|
|
11945
|
+
logger$28.warn("[PreflightRunner] Media bandwidth test failed:", error);
|
|
11915
11946
|
warnings.push("Media bandwidth test failed");
|
|
11916
11947
|
}
|
|
11917
11948
|
return {
|
|
@@ -11923,7 +11954,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
11923
11954
|
warnings
|
|
11924
11955
|
};
|
|
11925
11956
|
} catch (error) {
|
|
11926
|
-
logger$
|
|
11957
|
+
logger$28.error("[PreflightRunner] Preflight test failed:", error);
|
|
11927
11958
|
throw new PreflightError("preflight", error instanceof Error ? error : new Error(String(error)));
|
|
11928
11959
|
} finally {
|
|
11929
11960
|
this.destroy();
|
|
@@ -11954,7 +11985,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
11954
11985
|
if (track.kind === "video" && track.readyState === "live") videoWorking = true;
|
|
11955
11986
|
}
|
|
11956
11987
|
} catch (error) {
|
|
11957
|
-
logger$
|
|
11988
|
+
logger$28.warn("[PreflightRunner] Device test failed:", error);
|
|
11958
11989
|
} finally {
|
|
11959
11990
|
if (audioStream) audioStream.getTracks().forEach((t) => t.stop());
|
|
11960
11991
|
}
|
|
@@ -12012,7 +12043,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
12012
12043
|
rttMs
|
|
12013
12044
|
};
|
|
12014
12045
|
} catch (error) {
|
|
12015
|
-
logger$
|
|
12046
|
+
logger$28.warn("[PreflightRunner] ICE connectivity test failed:", error);
|
|
12016
12047
|
return {
|
|
12017
12048
|
type: "failed",
|
|
12018
12049
|
turnReachable: false,
|
|
@@ -12060,7 +12091,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
12060
12091
|
//#endregion
|
|
12061
12092
|
//#region src/controllers/VisibilityController.ts
|
|
12062
12093
|
var import_cjs$26 = require_cjs();
|
|
12063
|
-
const logger$
|
|
12094
|
+
const logger$27 = getLogger();
|
|
12064
12095
|
/**
|
|
12065
12096
|
* Checks whether the document visibility API is available.
|
|
12066
12097
|
*/
|
|
@@ -12097,8 +12128,8 @@ var VisibilityController = class extends Destroyable {
|
|
|
12097
12128
|
this._boundHandler = this._handleVisibilityChange.bind(this);
|
|
12098
12129
|
if (this._hasVisibilityApi) {
|
|
12099
12130
|
document.addEventListener("visibilitychange", this._boundHandler);
|
|
12100
|
-
logger$
|
|
12101
|
-
} else logger$
|
|
12131
|
+
logger$27.debug("VisibilityController: listening for visibilitychange events");
|
|
12132
|
+
} else logger$27.debug("VisibilityController: document visibility API not available, defaulting to visible");
|
|
12102
12133
|
}
|
|
12103
12134
|
/**
|
|
12104
12135
|
* Observable of the current visibility state.
|
|
@@ -12123,7 +12154,7 @@ var VisibilityController = class extends Destroyable {
|
|
|
12123
12154
|
destroy() {
|
|
12124
12155
|
if (this._hasVisibilityApi) {
|
|
12125
12156
|
document.removeEventListener("visibilitychange", this._boundHandler);
|
|
12126
|
-
logger$
|
|
12157
|
+
logger$27.debug("VisibilityController: removed visibilitychange listener");
|
|
12127
12158
|
}
|
|
12128
12159
|
super.destroy();
|
|
12129
12160
|
}
|
|
@@ -12141,7 +12172,7 @@ var VisibilityController = class extends Destroyable {
|
|
|
12141
12172
|
timestamp: Date.now()
|
|
12142
12173
|
};
|
|
12143
12174
|
this._visibilityChange$.next(changeEvent);
|
|
12144
|
-
logger$
|
|
12175
|
+
logger$27.debug("VisibilityController: visibility changed", {
|
|
12145
12176
|
from: previousState,
|
|
12146
12177
|
to: newState
|
|
12147
12178
|
});
|
|
@@ -12423,7 +12454,7 @@ function isRequesterValidationError(error) {
|
|
|
12423
12454
|
|
|
12424
12455
|
//#endregion
|
|
12425
12456
|
//#region src/managers/AttachManager.ts
|
|
12426
|
-
const logger$
|
|
12457
|
+
const logger$26 = getLogger();
|
|
12427
12458
|
var AttachManager = class {
|
|
12428
12459
|
constructor(storage, deviceController, reconnectCallsTimeout, attachKey, credentialRecovered) {
|
|
12429
12460
|
this.storage = storage;
|
|
@@ -12445,7 +12476,7 @@ var AttachManager = class {
|
|
|
12445
12476
|
try {
|
|
12446
12477
|
return await this.storage.getItem(this.attachKey) ?? {};
|
|
12447
12478
|
} catch (error) {
|
|
12448
|
-
logger$
|
|
12479
|
+
logger$26.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
|
|
12449
12480
|
return {};
|
|
12450
12481
|
}
|
|
12451
12482
|
}
|
|
@@ -12453,7 +12484,7 @@ var AttachManager = class {
|
|
|
12453
12484
|
try {
|
|
12454
12485
|
await this.storage.setItem(this.attachKey, attached);
|
|
12455
12486
|
} catch (error) {
|
|
12456
|
-
logger$
|
|
12487
|
+
logger$26.warn("[AttachManager] Failed to write attached calls to storage", error);
|
|
12457
12488
|
}
|
|
12458
12489
|
}
|
|
12459
12490
|
/**
|
|
@@ -12472,7 +12503,7 @@ var AttachManager = class {
|
|
|
12472
12503
|
}
|
|
12473
12504
|
async attach(call) {
|
|
12474
12505
|
if (!call.to) {
|
|
12475
|
-
logger$
|
|
12506
|
+
logger$26.warn("[AttachManager] Skip attach for calls with no destination");
|
|
12476
12507
|
return;
|
|
12477
12508
|
}
|
|
12478
12509
|
const destination = call.to;
|
|
@@ -12532,25 +12563,25 @@ var AttachManager = class {
|
|
|
12532
12563
|
callId,
|
|
12533
12564
|
...options
|
|
12534
12565
|
});
|
|
12535
|
-
logger$
|
|
12566
|
+
logger$26.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
|
|
12536
12567
|
succeeded = true;
|
|
12537
12568
|
break;
|
|
12538
12569
|
} catch (error) {
|
|
12539
|
-
logger$
|
|
12570
|
+
logger$26.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
|
|
12540
12571
|
if (isRequesterValidationError(error)) {
|
|
12541
12572
|
refusedOnCredentials = true;
|
|
12542
|
-
logger$
|
|
12573
|
+
logger$26.warn(`[AttachManager] Reattach of ${callId} was refused on credentials; not retrying.`);
|
|
12543
12574
|
break;
|
|
12544
12575
|
}
|
|
12545
12576
|
if (attempt < 3) await new Promise((r) => setTimeout(r, (attempt + 1) * 1e3));
|
|
12546
12577
|
}
|
|
12547
12578
|
if (!succeeded) if (this.credentialRecovered() && !refusedOnCredentials) {
|
|
12548
|
-
logger$
|
|
12579
|
+
logger$26.warn(`[AttachManager] Reattach of ${callId} was denied after a verified reauthentication, removing reference`);
|
|
12549
12580
|
await this.detach({
|
|
12550
12581
|
id: callId,
|
|
12551
12582
|
mediaDirections: attachment.mediaDirections
|
|
12552
12583
|
});
|
|
12553
|
-
} else logger$
|
|
12584
|
+
} else logger$26.warn(`[AttachManager] Reattach failed for call ${callId}; keeping the reference (credential refused or never proven good)`);
|
|
12554
12585
|
}
|
|
12555
12586
|
}
|
|
12556
12587
|
/**
|
|
@@ -12651,12 +12682,12 @@ var require_race = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
12651
12682
|
exports.race = void 0;
|
|
12652
12683
|
var argsOrArgArray_1 = require_argsOrArgArray();
|
|
12653
12684
|
var raceWith_1$1 = require_raceWith();
|
|
12654
|
-
function race$
|
|
12685
|
+
function race$3() {
|
|
12655
12686
|
var args = [];
|
|
12656
12687
|
for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
|
|
12657
12688
|
return raceWith_1$1.raceWith.apply(void 0, __spreadArray([], __read(argsOrArgArray_1.argsOrArgArray(args))));
|
|
12658
12689
|
}
|
|
12659
|
-
exports.race = race$
|
|
12690
|
+
exports.race = race$3;
|
|
12660
12691
|
}));
|
|
12661
12692
|
|
|
12662
12693
|
//#endregion
|
|
@@ -13716,7 +13747,7 @@ function toggleHandraiseMethod(is) {
|
|
|
13716
13747
|
|
|
13717
13748
|
//#endregion
|
|
13718
13749
|
//#region src/core/entities/Participant.ts
|
|
13719
|
-
const logger$
|
|
13750
|
+
const logger$25 = getLogger();
|
|
13720
13751
|
const initialState = {};
|
|
13721
13752
|
/**
|
|
13722
13753
|
* Represents a participant in a call.
|
|
@@ -14190,6 +14221,9 @@ var SelfParticipant = class extends Participant {
|
|
|
14190
14221
|
*
|
|
14191
14222
|
* The call is unaffected when acquisition fails.
|
|
14192
14223
|
*
|
|
14224
|
+
* Known issue: stopping the share before its leg connects can leave this
|
|
14225
|
+
* promise pending until the call ends, so do not gate UI on it.
|
|
14226
|
+
*
|
|
14193
14227
|
* @throws The raw `getDisplayMedia` error. A dismissed picker or a
|
|
14194
14228
|
* permission denial rejects with a `NotAllowedError` `DOMException` —
|
|
14195
14229
|
* inspect `error.name` to tell benign cancels apart from real failures.
|
|
@@ -14198,7 +14232,7 @@ var SelfParticipant = class extends Participant {
|
|
|
14198
14232
|
try {
|
|
14199
14233
|
await this.vertoManager.addScreenMedia();
|
|
14200
14234
|
} catch (error) {
|
|
14201
|
-
logger$
|
|
14235
|
+
logger$25.error("[Participant.startScreenShare] Screen share error:", error);
|
|
14202
14236
|
throw error;
|
|
14203
14237
|
}
|
|
14204
14238
|
}
|
|
@@ -14219,14 +14253,18 @@ var SelfParticipant = class extends Participant {
|
|
|
14219
14253
|
*
|
|
14220
14254
|
* The call is unaffected when acquisition fails.
|
|
14221
14255
|
*
|
|
14256
|
+
* Known issue: removing the device before its leg connects can leave this
|
|
14257
|
+
* promise pending until the call ends, so do not gate UI on it.
|
|
14258
|
+
*
|
|
14222
14259
|
* @throws The raw `getUserMedia` error (e.g. `NotAllowedError` on
|
|
14223
|
-
* permission denial) — inspect `error.name` to decide how to react
|
|
14260
|
+
* permission denial) — inspect `error.name` to decide how to react — or
|
|
14261
|
+
* `AuxiliaryLegTimeoutError` if the leg does not connect in time.
|
|
14224
14262
|
*/
|
|
14225
14263
|
async addAdditionalDevice(options) {
|
|
14226
14264
|
try {
|
|
14227
14265
|
await this.vertoManager.addInputDevice(options);
|
|
14228
14266
|
} catch (error) {
|
|
14229
|
-
logger$
|
|
14267
|
+
logger$25.error("[Participant.addAdditionalDevice] Additional device error:", error);
|
|
14230
14268
|
throw error;
|
|
14231
14269
|
}
|
|
14232
14270
|
}
|
|
@@ -14289,7 +14327,7 @@ var SelfParticipant = class extends Participant {
|
|
|
14289
14327
|
*/
|
|
14290
14328
|
exitStudioModeIfActive() {
|
|
14291
14329
|
if (this._studioAudio$.value) {
|
|
14292
|
-
logger$
|
|
14330
|
+
logger$25.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
|
|
14293
14331
|
this._studioAudio$.next(false);
|
|
14294
14332
|
}
|
|
14295
14333
|
}
|
|
@@ -14313,7 +14351,7 @@ var SelfParticipant = class extends Participant {
|
|
|
14313
14351
|
try {
|
|
14314
14352
|
await super.mute();
|
|
14315
14353
|
} catch (error) {
|
|
14316
|
-
logger$
|
|
14354
|
+
logger$25.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
|
|
14317
14355
|
} finally {
|
|
14318
14356
|
this.vertoManager.muteMainAudioInputDevice();
|
|
14319
14357
|
}
|
|
@@ -14323,7 +14361,7 @@ var SelfParticipant = class extends Participant {
|
|
|
14323
14361
|
try {
|
|
14324
14362
|
await super.unmute();
|
|
14325
14363
|
} catch (error) {
|
|
14326
|
-
logger$
|
|
14364
|
+
logger$25.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
|
|
14327
14365
|
} finally {
|
|
14328
14366
|
await this.vertoManager.unmuteMainAudioInputDevice();
|
|
14329
14367
|
}
|
|
@@ -14333,7 +14371,7 @@ var SelfParticipant = class extends Participant {
|
|
|
14333
14371
|
try {
|
|
14334
14372
|
await super.muteVideo();
|
|
14335
14373
|
} catch (error) {
|
|
14336
|
-
logger$
|
|
14374
|
+
logger$25.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
|
|
14337
14375
|
} finally {
|
|
14338
14376
|
this.vertoManager.muteMainVideoInputDevice();
|
|
14339
14377
|
}
|
|
@@ -14343,7 +14381,7 @@ var SelfParticipant = class extends Participant {
|
|
|
14343
14381
|
try {
|
|
14344
14382
|
await super.unmuteVideo();
|
|
14345
14383
|
} catch (error) {
|
|
14346
|
-
logger$
|
|
14384
|
+
logger$25.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
|
|
14347
14385
|
} finally {
|
|
14348
14386
|
await this.vertoManager.unmuteMainVideoInputDevice();
|
|
14349
14387
|
}
|
|
@@ -14548,7 +14586,7 @@ function filterAs(predicate, resultPath) {
|
|
|
14548
14586
|
//#endregion
|
|
14549
14587
|
//#region src/operators/throwOnRPCError.ts
|
|
14550
14588
|
var import_cjs$21 = require_cjs();
|
|
14551
|
-
const logger$
|
|
14589
|
+
const logger$24 = getLogger();
|
|
14552
14590
|
/**
|
|
14553
14591
|
* RxJS operator that throws a {@link JSONRPCError} when the RPC response contains an error.
|
|
14554
14592
|
* Passes successful responses through unchanged.
|
|
@@ -14556,14 +14594,14 @@ const logger$23 = getLogger();
|
|
|
14556
14594
|
function throwOnRPCError() {
|
|
14557
14595
|
return (0, import_cjs$21.map)((response) => {
|
|
14558
14596
|
if (response.error) {
|
|
14559
|
-
logger$
|
|
14597
|
+
logger$24.error("[throwOnRPCError] RPC error response:", {
|
|
14560
14598
|
code: response.error.code,
|
|
14561
14599
|
message: response.error.message,
|
|
14562
14600
|
data: response.error.data
|
|
14563
14601
|
});
|
|
14564
14602
|
throw new JSONRPCError(response.error.code, response.error.message, response.error.data);
|
|
14565
14603
|
}
|
|
14566
|
-
logger$
|
|
14604
|
+
logger$24.debug("[throwOnRPCError] RPC successful response:", response);
|
|
14567
14605
|
return response;
|
|
14568
14606
|
});
|
|
14569
14607
|
}
|
|
@@ -14571,7 +14609,7 @@ function throwOnRPCError() {
|
|
|
14571
14609
|
//#endregion
|
|
14572
14610
|
//#region src/managers/CallEventsManager.ts
|
|
14573
14611
|
var import_cjs$20 = require_cjs();
|
|
14574
|
-
const logger$
|
|
14612
|
+
const logger$23 = getLogger();
|
|
14575
14613
|
const initialSessionState = {};
|
|
14576
14614
|
/** @internal */
|
|
14577
14615
|
var CallEventsManager = class extends Destroyable {
|
|
@@ -14675,7 +14713,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14675
14713
|
}
|
|
14676
14714
|
initSubscriptions() {
|
|
14677
14715
|
this.subscribeTo(this.callJoinedEvent$, (callJoinedEvent) => {
|
|
14678
|
-
logger$
|
|
14716
|
+
logger$23.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
|
|
14679
14717
|
callId: callJoinedEvent.call_id,
|
|
14680
14718
|
roomSessionId: callJoinedEvent.room_session_id
|
|
14681
14719
|
});
|
|
@@ -14702,19 +14740,19 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14702
14740
|
if (this._self$.value?.capabilities.setLayout) this.updateLayouts();
|
|
14703
14741
|
});
|
|
14704
14742
|
this.subscribeTo(this.memberUpdates$, (member) => {
|
|
14705
|
-
logger$
|
|
14743
|
+
logger$23.debug("[CallEventsManager] Handling member update event for member ID:", member);
|
|
14706
14744
|
this.upsertParticipant(member);
|
|
14707
14745
|
});
|
|
14708
14746
|
this.subscribeTo(this.webRtcCallSession.memberLeft$, (memberLeftEvent) => {
|
|
14709
|
-
logger$
|
|
14747
|
+
logger$23.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
|
|
14710
14748
|
const participants = { ...this._participants$.value };
|
|
14711
14749
|
if (memberLeftEvent.member.member_id in participants) {
|
|
14712
14750
|
delete participants[memberLeftEvent.member.member_id];
|
|
14713
14751
|
this._participants$.next(participants);
|
|
14714
|
-
} else logger$
|
|
14752
|
+
} else logger$23.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
|
|
14715
14753
|
});
|
|
14716
14754
|
this.subscribeTo(this.webRtcCallSession.callUpdated$, (callUpdatedEvent) => {
|
|
14717
|
-
logger$
|
|
14755
|
+
logger$23.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
|
|
14718
14756
|
const roomSession = callUpdatedEvent.room_session;
|
|
14719
14757
|
this._sessionState$.next({
|
|
14720
14758
|
...this._sessionState$.value,
|
|
@@ -14729,7 +14767,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14729
14767
|
});
|
|
14730
14768
|
});
|
|
14731
14769
|
this.subscribeTo(this.layoutChangedEvent$, (layoutChangedEvent) => {
|
|
14732
|
-
logger$
|
|
14770
|
+
logger$23.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
|
|
14733
14771
|
this._sessionState$.next({
|
|
14734
14772
|
...this._sessionState$.value,
|
|
14735
14773
|
layout_name: layoutChangedEvent.id,
|
|
@@ -14739,10 +14777,10 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14739
14777
|
});
|
|
14740
14778
|
}
|
|
14741
14779
|
updateParticipantPositions(layoutChangedEvent) {
|
|
14742
|
-
if (Object.keys(this._participants$.value).length > 0 && !layoutChangedEvent.layers.some((layer) => !!layer.member_id)) logger$
|
|
14780
|
+
if (Object.keys(this._participants$.value).length > 0 && !layoutChangedEvent.layers.some((layer) => !!layer.member_id)) logger$23.warn("[CallEventsManager] No layers with member_id found in layout.changed event. Nothing to update.");
|
|
14743
14781
|
layoutChangedEvent.layers.filter((layer) => !!layer.member_id).filter((layer) => {
|
|
14744
14782
|
if (!(layer.member_id in this._participants$.value)) {
|
|
14745
|
-
logger$
|
|
14783
|
+
logger$23.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
|
|
14746
14784
|
return false;
|
|
14747
14785
|
}
|
|
14748
14786
|
return true;
|
|
@@ -14762,7 +14800,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14762
14800
|
this.webRtcCallSession.executeMethod(this.selfId, "call.layout.list", {}).then((response) => {
|
|
14763
14801
|
const layouts = response.result?.layouts;
|
|
14764
14802
|
if (!layouts) {
|
|
14765
|
-
logger$
|
|
14803
|
+
logger$23.warn("[CallEventsManager] Layout list response carried no layouts; keeping current layouts");
|
|
14766
14804
|
return;
|
|
14767
14805
|
}
|
|
14768
14806
|
this._sessionState$.next({
|
|
@@ -14770,7 +14808,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14770
14808
|
layouts
|
|
14771
14809
|
});
|
|
14772
14810
|
}).catch((error) => {
|
|
14773
|
-
logger$
|
|
14811
|
+
logger$23.error("[CallEventsManager] Error fetching layouts:", error);
|
|
14774
14812
|
});
|
|
14775
14813
|
}
|
|
14776
14814
|
updateParticipants(members) {
|
|
@@ -14786,7 +14824,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14786
14824
|
}
|
|
14787
14825
|
const participant = this._participants$.value[member.member_id];
|
|
14788
14826
|
const oldValue = participant.value;
|
|
14789
|
-
logger$
|
|
14827
|
+
logger$23.debug("[CallEventsManager] Updating participant:", member.member_id, {
|
|
14790
14828
|
oldValue,
|
|
14791
14829
|
newValue: member
|
|
14792
14830
|
});
|
|
@@ -14799,17 +14837,17 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14799
14837
|
}
|
|
14800
14838
|
get callJoinedEvent$() {
|
|
14801
14839
|
return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$20.filter)(isCallJoinedPayload), (0, import_cjs$20.tap)((event) => {
|
|
14802
|
-
logger$
|
|
14840
|
+
logger$23.debug("[CallEventsManager] Call joined event:", event);
|
|
14803
14841
|
})));
|
|
14804
14842
|
}
|
|
14805
14843
|
get layoutChangedEvent$() {
|
|
14806
14844
|
return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$20.tap)((event) => {
|
|
14807
|
-
logger$
|
|
14845
|
+
logger$23.debug("[CallEventsManager] Layout changed event:", event);
|
|
14808
14846
|
})));
|
|
14809
14847
|
}
|
|
14810
14848
|
get memberUpdates$() {
|
|
14811
14849
|
return this.cachedObservable("memberUpdates$", () => (0, import_cjs$20.merge)(this.webRtcCallSession.memberJoined$, this.webRtcCallSession.memberUpdated$, this.webRtcCallSession.memberTalking$).pipe((0, import_cjs$20.map)((event) => event.member), (0, import_cjs$20.tap)((event) => {
|
|
14812
|
-
logger$
|
|
14850
|
+
logger$23.debug("[CallEventsManager] Member update event:", event);
|
|
14813
14851
|
})));
|
|
14814
14852
|
}
|
|
14815
14853
|
destroy() {
|
|
@@ -14828,6 +14866,111 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14828
14866
|
}
|
|
14829
14867
|
};
|
|
14830
14868
|
|
|
14869
|
+
//#endregion
|
|
14870
|
+
//#region src/controllers/ConstraintFallbackHelper.ts
|
|
14871
|
+
/**
|
|
14872
|
+
* ConstraintFallbackHelper - Provides getUserMedia with automatic constraint
|
|
14873
|
+
* fallback on OverconstrainedError.
|
|
14874
|
+
*
|
|
14875
|
+
* When a specific device ID is requested, the helper tries progressively
|
|
14876
|
+
* looser constraints:
|
|
14877
|
+
* 1. `{ deviceId: { exact: deviceId } }` -- exact match
|
|
14878
|
+
* 2. `{ deviceId: deviceId }` -- preferred (browser may pick another)
|
|
14879
|
+
* 3. `{}` -- no constraint, browser default
|
|
14880
|
+
*
|
|
14881
|
+
* This prevents stale device IDs from blocking call setup.
|
|
14882
|
+
*
|
|
14883
|
+
* @see Section 5.8 and Section 11 of the Implementation Guide
|
|
14884
|
+
*/
|
|
14885
|
+
const logger$22 = getLogger();
|
|
14886
|
+
/**
|
|
14887
|
+
* Attempts getUserMedia with progressively looser constraints.
|
|
14888
|
+
*
|
|
14889
|
+
* The function tries three levels of constraint specificity for the given
|
|
14890
|
+
* device kind. Each level is only attempted if the previous one fails with
|
|
14891
|
+
* an OverconstrainedError. Non-OverconstrainedError failures (e.g.,
|
|
14892
|
+
* NotAllowedError) are thrown immediately without fallback.
|
|
14893
|
+
*
|
|
14894
|
+
* @param mediaDevices - Anything exposing `getUserMedia` (a full
|
|
14895
|
+
* `WebRTCMediaDevices`, or a shim wrapping one)
|
|
14896
|
+
* @param constraints - The full MediaStreamConstraints to use as a base
|
|
14897
|
+
* @param kind - Which track kind to apply fallback to ('audio' | 'video')
|
|
14898
|
+
* @param deviceId - The device ID to try (if undefined, calls getUserMedia as-is)
|
|
14899
|
+
* @returns The stream and the fallback level that succeeded
|
|
14900
|
+
* @throws When all fallback levels fail, or when a non-OverconstrainedError occurs
|
|
14901
|
+
*/
|
|
14902
|
+
async function getUserMediaWithFallback(mediaDevices, constraints, kind, deviceId) {
|
|
14903
|
+
if (!deviceId) return {
|
|
14904
|
+
stream: await mediaDevices.getUserMedia(constraints),
|
|
14905
|
+
fallbackLevel: "default"
|
|
14906
|
+
};
|
|
14907
|
+
const baseConstraints = typeof constraints[kind] === "object" ? constraints[kind] : {};
|
|
14908
|
+
try {
|
|
14909
|
+
const exactConstraints = {
|
|
14910
|
+
...constraints,
|
|
14911
|
+
[kind]: {
|
|
14912
|
+
...baseConstraints,
|
|
14913
|
+
deviceId: { exact: deviceId }
|
|
14914
|
+
}
|
|
14915
|
+
};
|
|
14916
|
+
return {
|
|
14917
|
+
stream: await mediaDevices.getUserMedia(exactConstraints),
|
|
14918
|
+
fallbackLevel: "exact"
|
|
14919
|
+
};
|
|
14920
|
+
} catch (error) {
|
|
14921
|
+
if (!isOverconstrainedError(error)) throw error;
|
|
14922
|
+
logger$22.debug(`[ConstraintFallbackHelper] Exact constraint failed for ${kind}, trying preferred`, { deviceId });
|
|
14923
|
+
}
|
|
14924
|
+
try {
|
|
14925
|
+
const preferredConstraints = {
|
|
14926
|
+
...constraints,
|
|
14927
|
+
[kind]: {
|
|
14928
|
+
...baseConstraints,
|
|
14929
|
+
deviceId
|
|
14930
|
+
}
|
|
14931
|
+
};
|
|
14932
|
+
return {
|
|
14933
|
+
stream: await mediaDevices.getUserMedia(preferredConstraints),
|
|
14934
|
+
fallbackLevel: "preferred"
|
|
14935
|
+
};
|
|
14936
|
+
} catch (error) {
|
|
14937
|
+
if (!isOverconstrainedError(error)) throw error;
|
|
14938
|
+
logger$22.debug(`[ConstraintFallbackHelper] Preferred constraint failed for ${kind}, trying default`, { deviceId });
|
|
14939
|
+
}
|
|
14940
|
+
try {
|
|
14941
|
+
const defaultConstraints = {
|
|
14942
|
+
...constraints,
|
|
14943
|
+
[kind]: { ...baseConstraints }
|
|
14944
|
+
};
|
|
14945
|
+
if (typeof defaultConstraints[kind] === "object") {
|
|
14946
|
+
const { deviceId: _removed, ...rest } = defaultConstraints[kind];
|
|
14947
|
+
defaultConstraints[kind] = rest;
|
|
14948
|
+
}
|
|
14949
|
+
const stream = await mediaDevices.getUserMedia(defaultConstraints);
|
|
14950
|
+
logger$22.warn(`[ConstraintFallbackHelper] Fell back to browser default for ${kind}`, { requestedDeviceId: deviceId });
|
|
14951
|
+
return {
|
|
14952
|
+
stream,
|
|
14953
|
+
fallbackLevel: "default"
|
|
14954
|
+
};
|
|
14955
|
+
} catch (error) {
|
|
14956
|
+
logger$22.error(`[ConstraintFallbackHelper] All fallback levels exhausted for ${kind}`, {
|
|
14957
|
+
deviceId,
|
|
14958
|
+
error
|
|
14959
|
+
});
|
|
14960
|
+
throw error;
|
|
14961
|
+
}
|
|
14962
|
+
}
|
|
14963
|
+
/**
|
|
14964
|
+
* Checks whether an error is an OverconstrainedError.
|
|
14965
|
+
*
|
|
14966
|
+
* Browsers may throw either a native OverconstrainedError or a DOMException
|
|
14967
|
+
* with a specific name.
|
|
14968
|
+
*/
|
|
14969
|
+
function isOverconstrainedError(error) {
|
|
14970
|
+
if (error instanceof Error) return error.name === "OverconstrainedError" || error.name === "ConstraintNotSatisfiedError";
|
|
14971
|
+
return false;
|
|
14972
|
+
}
|
|
14973
|
+
|
|
14831
14974
|
//#endregion
|
|
14832
14975
|
//#region src/helpers/SDPHelper.ts
|
|
14833
14976
|
/**
|
|
@@ -15386,6 +15529,7 @@ var LocalStreamController = class extends Destroyable {
|
|
|
15386
15529
|
this._localAudioTracks$ = this.createBehaviorSubject([]);
|
|
15387
15530
|
this._localVideoTracks$ = this.createBehaviorSubject([]);
|
|
15388
15531
|
this._mediaTrackEnded$ = this.createSubject();
|
|
15532
|
+
this._trackOrigins = /* @__PURE__ */ new WeakMap();
|
|
15389
15533
|
}
|
|
15390
15534
|
get localStream$() {
|
|
15391
15535
|
return this._localStream$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
|
|
@@ -15408,6 +15552,22 @@ var LocalStreamController = class extends Destroyable {
|
|
|
15408
15552
|
get localVideoTracks() {
|
|
15409
15553
|
return this._localVideoTracks$.value;
|
|
15410
15554
|
}
|
|
15555
|
+
tagTracks(tracks, origin) {
|
|
15556
|
+
for (const track of tracks) this._trackOrigins.set(track, origin);
|
|
15557
|
+
}
|
|
15558
|
+
setTrackOrigin(track, origin) {
|
|
15559
|
+
this._trackOrigins.set(track, origin);
|
|
15560
|
+
}
|
|
15561
|
+
getTrackOrigin(track) {
|
|
15562
|
+
return this._trackOrigins.get(track);
|
|
15563
|
+
}
|
|
15564
|
+
/**
|
|
15565
|
+
* Fail-safe: an unrecorded track reads as not-a-device-capture, so a missed
|
|
15566
|
+
* tagging site leaves media alone rather than destroying it.
|
|
15567
|
+
*/
|
|
15568
|
+
isDeviceCapture(track) {
|
|
15569
|
+
return this._trackOrigins.get(track) === "device";
|
|
15570
|
+
}
|
|
15411
15571
|
/**
|
|
15412
15572
|
* Build the local media stream based on the provided options.
|
|
15413
15573
|
*/
|
|
@@ -15417,6 +15577,7 @@ var LocalStreamController = class extends Destroyable {
|
|
|
15417
15577
|
if (this.options.inputAudioStream ?? this.options.inputVideoStream) {
|
|
15418
15578
|
const tracks = [...this.options.inputAudioStream?.getTracks() ?? [], ...this.options.inputVideoStream?.getTracks() ?? []];
|
|
15419
15579
|
stream = new MediaStream(tracks);
|
|
15580
|
+
this.tagTracks(tracks, "application");
|
|
15420
15581
|
} else if (this.options.propose === "screenshare") {
|
|
15421
15582
|
logger$19.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
|
|
15422
15583
|
stream = await this.options.getDisplayMedia({
|
|
@@ -15424,6 +15585,7 @@ var LocalStreamController = class extends Destroyable {
|
|
|
15424
15585
|
audio: Boolean(this.options.inputAudioDeviceConstraints)
|
|
15425
15586
|
});
|
|
15426
15587
|
logger$19.debug("[LocalStreamController] Screen share media obtained:", stream);
|
|
15588
|
+
this.tagTracks(stream.getTracks(), "display");
|
|
15427
15589
|
} else {
|
|
15428
15590
|
const constraints = {
|
|
15429
15591
|
audio: this.options.inputAudioDeviceConstraints,
|
|
@@ -15432,6 +15594,7 @@ var LocalStreamController = class extends Destroyable {
|
|
|
15432
15594
|
logger$19.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
|
|
15433
15595
|
stream = await this.options.getUserMedia(constraints);
|
|
15434
15596
|
logger$19.debug("[LocalStreamController] User media obtained:", stream);
|
|
15597
|
+
this.tagTracks(stream.getTracks(), "device");
|
|
15435
15598
|
}
|
|
15436
15599
|
this._localStream$.next(stream);
|
|
15437
15600
|
this._localAudioTracks$.next(stream.getAudioTracks());
|
|
@@ -15441,10 +15604,13 @@ var LocalStreamController = class extends Destroyable {
|
|
|
15441
15604
|
/**
|
|
15442
15605
|
* Add a local media track to the local stream.
|
|
15443
15606
|
* @param track - The MediaStreamTrack to add
|
|
15607
|
+
* @param origin - Defaults to `'device'`; every internal caller passes a
|
|
15608
|
+
* fresh `getUserMedia` capture.
|
|
15444
15609
|
* @returns The MediaStream (either existing or newly created)
|
|
15445
15610
|
*/
|
|
15446
|
-
addTrack(track) {
|
|
15611
|
+
addTrack(track, origin = "device") {
|
|
15447
15612
|
const localStream = this._localStream$.value ?? new MediaStream();
|
|
15613
|
+
this._trackOrigins.set(track, origin);
|
|
15448
15614
|
track.addEventListener("ended", this.mediaTrackEndedHandler);
|
|
15449
15615
|
localStream.addTrack(track);
|
|
15450
15616
|
this._localStream$.next(localStream);
|
|
@@ -15714,15 +15880,27 @@ var TransceiverController = class extends Destroyable {
|
|
|
15714
15880
|
for (let i = 0; i < Number(msStreamsNumber); i++) this.peerConnection.addTransceiver("video", { direction: "recvonly" });
|
|
15715
15881
|
}
|
|
15716
15882
|
}
|
|
15883
|
+
/**
|
|
15884
|
+
* @returns whether every live sender of the kind took the constraints. A
|
|
15885
|
+
* skipped non-device sender, an exhausted fallback, and having no live sender
|
|
15886
|
+
* at all all report `false` — `mediaParamsUpdated.applied` is built from this,
|
|
15887
|
+
* and an application told `true` cannot tell a working push from a no-op.
|
|
15888
|
+
*/
|
|
15717
15889
|
async updateSendersConstraints(kind, constraints) {
|
|
15718
15890
|
if (!constraints) {
|
|
15719
15891
|
this.stopTrackSender(kind);
|
|
15720
|
-
return
|
|
15892
|
+
return false;
|
|
15721
15893
|
}
|
|
15722
15894
|
const senders = this.peerConnection.getSenders().filter((sender) => sender.track?.kind === kind && sender.track.readyState === "live");
|
|
15895
|
+
let applied = senders.length > 0;
|
|
15723
15896
|
for (const sender of senders) {
|
|
15724
15897
|
const { track } = sender;
|
|
15725
15898
|
if (track) {
|
|
15899
|
+
if (!this.options.localStreamController.isDeviceCapture(track)) {
|
|
15900
|
+
logger$18.debug(`[TransceiverController] Skipping ${kind} constraints for a non-device track (origin: ${this.options.localStreamController.getTrackOrigin(track) ?? "unrecorded"}), track ${track.id}`);
|
|
15901
|
+
applied = false;
|
|
15902
|
+
continue;
|
|
15903
|
+
}
|
|
15726
15904
|
const constraintsToApply = {
|
|
15727
15905
|
...track.getConstraints(),
|
|
15728
15906
|
...constraints
|
|
@@ -15738,33 +15916,39 @@ var TransceiverController = class extends Destroyable {
|
|
|
15738
15916
|
} catch (fallbackError) {
|
|
15739
15917
|
logger$18.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
|
|
15740
15918
|
this.options.onError?.(new MediaTrackError("updateSendersConstraints", kind, fallbackError));
|
|
15919
|
+
applied = false;
|
|
15741
15920
|
}
|
|
15742
15921
|
}
|
|
15743
15922
|
}
|
|
15744
15923
|
}
|
|
15924
|
+
return applied;
|
|
15745
15925
|
}
|
|
15746
15926
|
/**
|
|
15747
|
-
* Fallback when applyConstraints fails
|
|
15748
|
-
* one via getUserMedia with the merged constraints (preserving the current
|
|
15749
|
-
* deviceId), replace the sender track, and update the localStream.
|
|
15927
|
+
* Fallback when applyConstraints fails, which on iOS Safari it silently does.
|
|
15750
15928
|
*
|
|
15751
|
-
*
|
|
15752
|
-
*
|
|
15929
|
+
* Order matters: acquiring before stopping means a failed acquisition leaves
|
|
15930
|
+
* the existing media playing. The deviceId goes through the fallback ladder
|
|
15931
|
+
* rather than pinned `{ exact }`, so a stale id degrades instead of failing.
|
|
15753
15932
|
*/
|
|
15754
15933
|
async replaceTrackFallback(sender, oldTrack, kind, mergedConstraints) {
|
|
15755
15934
|
const { deviceId } = oldTrack.getSettings();
|
|
15756
|
-
const
|
|
15757
|
-
|
|
15758
|
-
|
|
15759
|
-
|
|
15760
|
-
|
|
15935
|
+
const { stream, fallbackLevel } = await getUserMediaWithFallback({ getUserMedia: this.options.getUserMedia }, { [kind]: mergedConstraints }, kind, deviceId);
|
|
15936
|
+
const newTrack = stream.getTracks().find((t) => t.kind === kind);
|
|
15937
|
+
if (!newTrack) {
|
|
15938
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
15939
|
+
throw new MediaTrackError("replaceTrackFallback", kind, /* @__PURE__ */ new Error("getUserMedia returned no track of the requested kind"));
|
|
15940
|
+
}
|
|
15941
|
+
try {
|
|
15942
|
+
await sender.replaceTrack(newTrack);
|
|
15943
|
+
} catch (error) {
|
|
15944
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
15945
|
+
throw error;
|
|
15946
|
+
}
|
|
15947
|
+
const oldTrackId = oldTrack.id;
|
|
15948
|
+
this.options.localStreamController.removeTrack(oldTrackId);
|
|
15761
15949
|
oldTrack.stop();
|
|
15762
|
-
this.options.localStreamController.removeTrack(trackId);
|
|
15763
|
-
const newTrack = (await this.options.getUserMedia({ [kind]: constraintsWithDevice })).getTracks().find((t) => t.kind === kind);
|
|
15764
|
-
if (!newTrack) throw new MediaTrackError("replaceTrackFallback", kind, /* @__PURE__ */ new Error("getUserMedia returned no track of the requested kind"));
|
|
15765
|
-
await sender.replaceTrack(newTrack);
|
|
15766
15950
|
this.options.localStreamController.addTrack(newTrack);
|
|
15767
|
-
logger$18.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
|
|
15951
|
+
logger$18.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind} (deviceId fallback level: ${fallbackLevel}). New track: ${newTrack.id}`);
|
|
15768
15952
|
}
|
|
15769
15953
|
getMediaDirections() {
|
|
15770
15954
|
if (this.peerConnection.connectionState === "connected") return this.peerConnection.getTransceivers().reduce((acc, transceiver) => {
|
|
@@ -15876,6 +16060,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
15876
16060
|
this._iceGatheringState$ = this.createReplaySubject(1);
|
|
15877
16061
|
this._errors$ = this.createReplaySubject(1);
|
|
15878
16062
|
this._iceCandidates$ = this.createReplaySubject(1);
|
|
16063
|
+
this._localMediaSettled$ = this.createReplaySubject(1);
|
|
15879
16064
|
this._initialized$ = this.createReplaySubject(1);
|
|
15880
16065
|
this._remoteDescription$ = this.createReplaySubject(1);
|
|
15881
16066
|
this._remoteStream$ = this.createBehaviorSubject(null);
|
|
@@ -15979,6 +16164,10 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
15979
16164
|
get remoteDescription$() {
|
|
15980
16165
|
return this.cachedObservable("remoteDescription$", () => this._remoteDescription$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
|
|
15981
16166
|
}
|
|
16167
|
+
/** Emits once local media is settled — acquired, or knowingly receive-only. */
|
|
16168
|
+
get localMediaSettled$() {
|
|
16169
|
+
return this.cachedObservable("localMediaSettled$", () => this._localMediaSettled$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
|
|
16170
|
+
}
|
|
15982
16171
|
get localStream$() {
|
|
15983
16172
|
return this.cachedObservable("localStream$", () => this.localStreamController.localStream$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
|
|
15984
16173
|
}
|
|
@@ -16134,7 +16323,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16134
16323
|
this._isNegotiating$.next(true);
|
|
16135
16324
|
await this._setRemoteDescription(this.sdpInit);
|
|
16136
16325
|
} else {
|
|
16137
|
-
await this.setupTrackHandling();
|
|
16326
|
+
if (!await this.setupTrackHandling()) return;
|
|
16138
16327
|
this._initialized$.next(true);
|
|
16139
16328
|
}
|
|
16140
16329
|
} catch (error) {
|
|
@@ -16258,7 +16447,10 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16258
16447
|
inputVideoDeviceConstraints: this.inputVideoDeviceConstraints
|
|
16259
16448
|
});
|
|
16260
16449
|
}
|
|
16261
|
-
await this.setupLocalTracks()
|
|
16450
|
+
if (!await this.setupLocalTracks()) {
|
|
16451
|
+
logger$17.debug("[RTCPeerConnectionController] Inbound answer abandoned; the connection went away.");
|
|
16452
|
+
return;
|
|
16453
|
+
}
|
|
16262
16454
|
const { answerOptions } = this;
|
|
16263
16455
|
logger$17.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
|
|
16264
16456
|
await this.createAnswer(answerOptions);
|
|
@@ -16396,35 +16588,52 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16396
16588
|
}
|
|
16397
16589
|
/**
|
|
16398
16590
|
* Setup track handling for remote tracks.
|
|
16591
|
+
*
|
|
16592
|
+
* @returns `false` when the connection went away while local media was being
|
|
16593
|
+
* acquired — see {@link setupLocalTracks}.
|
|
16399
16594
|
*/
|
|
16400
16595
|
async setupTrackHandling() {
|
|
16401
16596
|
if (!this.peerConnection) throw new DependencyError("RTCPeerConnection is not initialized");
|
|
16402
|
-
await this.setupLocalTracks();
|
|
16597
|
+
if (!await this.setupLocalTracks()) return false;
|
|
16403
16598
|
await this.setupRemoteTracks();
|
|
16599
|
+
return true;
|
|
16404
16600
|
}
|
|
16601
|
+
/**
|
|
16602
|
+
* @returns `false` when the connection was torn down while getUserMedia was
|
|
16603
|
+
* in flight. The acquisition is not cancellable, so the caller must stop
|
|
16604
|
+
* rather than go on to touch a peer connection that is closed or gone.
|
|
16605
|
+
*/
|
|
16405
16606
|
async setupLocalTracks() {
|
|
16406
16607
|
logger$17.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
|
|
16407
16608
|
if (this.hasNoLocalMediaToSend()) {
|
|
16408
16609
|
if (!this.receiveAudio && !this.receiveVideo) throw new InvalidParams("Call requests no media: enable audio/video or receiveAudio/receiveVideo");
|
|
16409
16610
|
logger$17.debug("[RTCPeerConnectionController] No local media requested; negotiating receive-only.");
|
|
16410
16611
|
this.setupReceiveOnlyTransceivers();
|
|
16411
|
-
|
|
16612
|
+
this._localMediaSettled$.next();
|
|
16613
|
+
return true;
|
|
16412
16614
|
}
|
|
16413
16615
|
let localStream;
|
|
16414
16616
|
try {
|
|
16415
16617
|
localStream = this.localStream ?? await this.localStreamController.buildLocalStream();
|
|
16416
16618
|
} catch (error) {
|
|
16417
16619
|
this.handleLocalMediaFailure(error);
|
|
16418
|
-
|
|
16620
|
+
this._localMediaSettled$.next();
|
|
16621
|
+
return true;
|
|
16419
16622
|
}
|
|
16623
|
+
if (!this.peerConnection || this.peerConnection.signalingState === "closed") {
|
|
16624
|
+
logger$17.debug("[RTCPeerConnectionController] Local media arrived after teardown; releasing it.");
|
|
16625
|
+
localStream.getTracks().forEach((track) => track.stop());
|
|
16626
|
+
return false;
|
|
16627
|
+
}
|
|
16628
|
+
this._localMediaSettled$.next();
|
|
16420
16629
|
if (this.transceiverController?.useAddStream ?? false) {
|
|
16421
16630
|
logger$17.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
|
|
16422
|
-
this.peerConnection
|
|
16631
|
+
this.peerConnection.addStream(localStream);
|
|
16423
16632
|
if (!this.isNegotiating) {
|
|
16424
16633
|
logger$17.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
|
|
16425
16634
|
this.negotiationNeeded$.next();
|
|
16426
16635
|
}
|
|
16427
|
-
return;
|
|
16636
|
+
return true;
|
|
16428
16637
|
}
|
|
16429
16638
|
for (const kind of ["audio", "video"]) {
|
|
16430
16639
|
const tracks = (kind === "audio" ? localStream.getAudioTracks() : localStream.getVideoTracks()).map((track, index) => ({
|
|
@@ -16438,10 +16647,11 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16438
16647
|
await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
|
|
16439
16648
|
} else {
|
|
16440
16649
|
logger$17.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
|
|
16441
|
-
this.peerConnection
|
|
16650
|
+
this.peerConnection.addTrack(track, localStream);
|
|
16442
16651
|
}
|
|
16443
16652
|
}
|
|
16444
16653
|
}
|
|
16654
|
+
return true;
|
|
16445
16655
|
}
|
|
16446
16656
|
/** True for a main connection with no local media to send. */
|
|
16447
16657
|
hasNoLocalMediaToSend() {
|
|
@@ -16562,6 +16772,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16562
16772
|
const sender = (this.transceiverController?.audioTransceivers.at(0))?.sender ?? this.peerConnection.getSenders().find((s) => s.track?.kind === "audio");
|
|
16563
16773
|
if (!sender || !raw) return;
|
|
16564
16774
|
try {
|
|
16775
|
+
this.localStreamController.setTrackOrigin(this._localAudioPipeline.outputTrack, "processed");
|
|
16565
16776
|
await sender.replaceTrack(this._localAudioPipeline.outputTrack);
|
|
16566
16777
|
} catch (error) {
|
|
16567
16778
|
logger$17.warn("[RTCPeerConnectionController] Failed to route audio sender through pipeline:", error);
|
|
@@ -16622,37 +16833,62 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16622
16833
|
for (const existingTrack of existingTracks) this.removeLocalTrack(existingTrack.id);
|
|
16623
16834
|
this.addLocalTrack(track);
|
|
16624
16835
|
}
|
|
16836
|
+
/**
|
|
16837
|
+
* @returns whether the constraints reached the media the leg is sending.
|
|
16838
|
+
*
|
|
16839
|
+
* With the pipeline engaged the audio sender carries the processed
|
|
16840
|
+
* destination track, so the sender scan would find nothing it may touch and
|
|
16841
|
+
* every audio constraint API would silently no-op. The constraints belong to
|
|
16842
|
+
* the pipeline's device source, which is the capture that sender ultimately
|
|
16843
|
+
* carries.
|
|
16844
|
+
*/
|
|
16625
16845
|
async updateSendersConstraints(kind, constraints) {
|
|
16626
|
-
|
|
16846
|
+
if (kind === "audio" && constraints && this._localAudioPipeline) return this.applyPipelineSourceConstraints(constraints);
|
|
16847
|
+
return await this.transceiverController?.updateSendersConstraints(kind, constraints) ?? false;
|
|
16627
16848
|
}
|
|
16628
16849
|
/**
|
|
16629
|
-
*
|
|
16630
|
-
*
|
|
16631
|
-
*
|
|
16632
|
-
*
|
|
16850
|
+
* Mirror of the sender path for a piped audio leg: same merge, same fallback
|
|
16851
|
+
* ladder, same device-capture invariant — but the swap target is the pipeline
|
|
16852
|
+
* input, so the sender keeps emitting the pipeline's output track and its
|
|
16853
|
+
* identity survives the change.
|
|
16633
16854
|
*/
|
|
16634
|
-
async
|
|
16635
|
-
const
|
|
16636
|
-
|
|
16637
|
-
|
|
16638
|
-
|
|
16855
|
+
async applyPipelineSourceConstraints(constraints) {
|
|
16856
|
+
const pipeline = this._localAudioPipeline;
|
|
16857
|
+
const source = this.localStreamController.localAudioTracks.at(0);
|
|
16858
|
+
if (!pipeline || !source) {
|
|
16859
|
+
logger$17.debug("[RTCPeerConnectionController] No pipeline input to constrain.");
|
|
16860
|
+
return false;
|
|
16639
16861
|
}
|
|
16640
|
-
|
|
16641
|
-
|
|
16642
|
-
|
|
16643
|
-
|
|
16644
|
-
|
|
16645
|
-
|
|
16646
|
-
|
|
16647
|
-
|
|
16648
|
-
|
|
16649
|
-
|
|
16650
|
-
|
|
16651
|
-
|
|
16652
|
-
|
|
16653
|
-
|
|
16862
|
+
if (!this.localStreamController.isDeviceCapture(source)) {
|
|
16863
|
+
logger$17.debug(`[RTCPeerConnectionController] Skipping audio constraints for a non-device pipeline input (origin: ${this.localStreamController.getTrackOrigin(source) ?? "unrecorded"}).`);
|
|
16864
|
+
return false;
|
|
16865
|
+
}
|
|
16866
|
+
const merged = {
|
|
16867
|
+
...source.getConstraints(),
|
|
16868
|
+
...constraints
|
|
16869
|
+
};
|
|
16870
|
+
try {
|
|
16871
|
+
await source.applyConstraints(merged);
|
|
16872
|
+
logger$17.debug("[RTCPeerConnectionController] Pipeline input constraints updated:", merged);
|
|
16873
|
+
return true;
|
|
16874
|
+
} catch (error) {
|
|
16875
|
+
logger$17.warn("[RTCPeerConnectionController] applyConstraints failed on the pipeline input, re-acquiring:", error);
|
|
16876
|
+
}
|
|
16877
|
+
try {
|
|
16878
|
+
const { stream } = await getUserMediaWithFallback({ getUserMedia: async (c) => this.getUserMedia(c) }, { audio: merged }, "audio", source.getSettings().deviceId);
|
|
16879
|
+
const newTrack = stream.getAudioTracks().at(0);
|
|
16880
|
+
if (!newTrack) {
|
|
16881
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
16882
|
+
throw new Error("getUserMedia returned no audio track");
|
|
16883
|
+
}
|
|
16884
|
+
this.localStreamController.removeTrack(source.id);
|
|
16654
16885
|
this.localStreamController.addTrack(newTrack);
|
|
16655
|
-
|
|
16886
|
+
pipeline.setInputTrack(newTrack);
|
|
16887
|
+
return true;
|
|
16888
|
+
} catch (error) {
|
|
16889
|
+
logger$17.warn("[RTCPeerConnectionController] Failed to re-acquire the pipeline input for constraints:", error);
|
|
16890
|
+
this._errors$.next(new MediaTrackError("updateSendersConstraints", "audio", error));
|
|
16891
|
+
return false;
|
|
16656
16892
|
}
|
|
16657
16893
|
}
|
|
16658
16894
|
/**
|
|
@@ -16836,7 +17072,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
16836
17072
|
this._signalingStatus$ = this.createReplaySubject(1);
|
|
16837
17073
|
this._screenShareStatus$ = this.createBehaviorSubject("none");
|
|
16838
17074
|
this._rtcPeerConnectionsMap = /* @__PURE__ */ new Map();
|
|
16839
|
-
this.
|
|
17075
|
+
this._legErrors$ = this.createSubject();
|
|
16840
17076
|
this._nodeId$ = this.createBehaviorSubject(options.nodeId ?? null);
|
|
16841
17077
|
this.onError = options.onError;
|
|
16842
17078
|
this.onModifyFailed = options.onModifyFailed;
|
|
@@ -16884,6 +17120,10 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
16884
17120
|
get selfId$() {
|
|
16885
17121
|
return this._selfId$.asObservable();
|
|
16886
17122
|
}
|
|
17123
|
+
/** Separates the media phase of call creation from the signalling phase. */
|
|
17124
|
+
get localMediaSettled$() {
|
|
17125
|
+
return this.mainPeerConnection.localMediaSettled$;
|
|
17126
|
+
}
|
|
16887
17127
|
get localStream() {
|
|
16888
17128
|
return this._rtcPeerConnectionsMap.get(this.webRtcCallSession.id)?.localStream ?? null;
|
|
16889
17129
|
}
|
|
@@ -16940,20 +17180,11 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
16940
17180
|
const { mediaParams, callID } = event;
|
|
16941
17181
|
const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);
|
|
16942
17182
|
const { audio, video } = mediaParams;
|
|
16943
|
-
|
|
16944
|
-
|
|
16945
|
-
|
|
16946
|
-
|
|
16947
|
-
|
|
16948
|
-
audio,
|
|
16949
|
-
video,
|
|
16950
|
-
timestamp: Date.now()
|
|
16951
|
-
});
|
|
16952
|
-
} catch (error) {
|
|
16953
|
-
logger$16.warn("[WebRTCManager] Error applying server-pushed media params:", error);
|
|
16954
|
-
this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
|
|
16955
|
-
}
|
|
16956
|
-
})();
|
|
17183
|
+
if (!rtcPeerConnController) {
|
|
17184
|
+
logger$16.warn(`[WebRTCManager] Ignoring server-pushed media params for unknown leg ${callID}`);
|
|
17185
|
+
return;
|
|
17186
|
+
}
|
|
17187
|
+
this.applyServerMediaParams(rtcPeerConnController, audio, video);
|
|
16957
17188
|
});
|
|
16958
17189
|
this.subscribeTo(this.vertoPing$, (vertoPing) => {
|
|
16959
17190
|
this.attachManager.attach(this.buildAttachableCall());
|
|
@@ -16961,6 +17192,75 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
16961
17192
|
});
|
|
16962
17193
|
}
|
|
16963
17194
|
/**
|
|
17195
|
+
* An auxiliary-leg failure must never destroy the call; main-leg and
|
|
17196
|
+
* call-level errors keep `CallFactory.isFatalError`'s classification.
|
|
17197
|
+
*
|
|
17198
|
+
* Every site holding a peer connection reports through here, so the invariant
|
|
17199
|
+
* is structural rather than per-call-site — which is how cloud-product#20523
|
|
17200
|
+
* happened, with only one of fourteen sites passing `{ fatal: false }`.
|
|
17201
|
+
*
|
|
17202
|
+
* `override` composes rather than replaces: a caller may force non-fatal for a
|
|
17203
|
+
* reason of its own (a `verto.info` frame is best-effort whichever leg carries
|
|
17204
|
+
* it), and an auxiliary leg stays non-fatal regardless.
|
|
17205
|
+
*/
|
|
17206
|
+
reportLegError(error, rtcPeerConnController, override) {
|
|
17207
|
+
const leg = rtcPeerConnController?.propose;
|
|
17208
|
+
const legId = rtcPeerConnController?.id;
|
|
17209
|
+
const auxiliary = Boolean(rtcPeerConnController) && !rtcPeerConnController?.isMainDevice;
|
|
17210
|
+
this.onError?.(error, {
|
|
17211
|
+
...override?.fatal === false || auxiliary ? { fatal: false } : {},
|
|
17212
|
+
...leg ? { leg } : {},
|
|
17213
|
+
...legId ? { legId } : {}
|
|
17214
|
+
});
|
|
17215
|
+
if (legId) this._legErrors$.next({
|
|
17216
|
+
legId,
|
|
17217
|
+
error
|
|
17218
|
+
});
|
|
17219
|
+
}
|
|
17220
|
+
/**
|
|
17221
|
+
* Errors reported for one leg, as a stream that fails with them.
|
|
17222
|
+
*
|
|
17223
|
+
* Signaling failures are reported, never thrown — so nothing that waits on a
|
|
17224
|
+
* leg's progress would otherwise learn of a rejected invite. Merging this in
|
|
17225
|
+
* lets the wait end with the reason the server gave.
|
|
17226
|
+
*/
|
|
17227
|
+
legError$(legId) {
|
|
17228
|
+
return this._legErrors$.pipe((0, import_cjs$15.filter)((report) => report.legId === legId), (0, import_cjs$15.map)((report) => {
|
|
17229
|
+
throw report.error;
|
|
17230
|
+
}));
|
|
17231
|
+
}
|
|
17232
|
+
/**
|
|
17233
|
+
* Audio and video are applied independently so a failure in one cannot
|
|
17234
|
+
* suppress the other, and `mediaParamsUpdated` is emitted whatever happens —
|
|
17235
|
+
* an application should not be starved of the params by a constraint failure.
|
|
17236
|
+
*/
|
|
17237
|
+
async applyServerMediaParams(rtcPeerConnController, audio, video) {
|
|
17238
|
+
const failures = [];
|
|
17239
|
+
let applied = true;
|
|
17240
|
+
if (audio) try {
|
|
17241
|
+
applied = await rtcPeerConnController.updateSendersConstraints("audio", audio) && applied;
|
|
17242
|
+
} catch (error) {
|
|
17243
|
+
applied = false;
|
|
17244
|
+
failures.push(toError(error));
|
|
17245
|
+
}
|
|
17246
|
+
if (video) try {
|
|
17247
|
+
applied = await rtcPeerConnController.updateSendersConstraints("video", video) && applied;
|
|
17248
|
+
} catch (error) {
|
|
17249
|
+
applied = false;
|
|
17250
|
+
failures.push(toError(error));
|
|
17251
|
+
}
|
|
17252
|
+
this.webRtcCallSession.emitMediaParamsUpdated({
|
|
17253
|
+
audio,
|
|
17254
|
+
video,
|
|
17255
|
+
timestamp: Date.now(),
|
|
17256
|
+
applied
|
|
17257
|
+
});
|
|
17258
|
+
for (const failure of failures) {
|
|
17259
|
+
logger$16.warn("[WebRTCManager] Error applying server-pushed media params:", failure);
|
|
17260
|
+
this.reportLegError(failure, rtcPeerConnController, { fatal: false });
|
|
17261
|
+
}
|
|
17262
|
+
}
|
|
17263
|
+
/**
|
|
16964
17264
|
* Set node_id/selfId only when the current value is null.
|
|
16965
17265
|
*
|
|
16966
17266
|
* During reattach, `call.joined` and `verto.answer` events can deliver
|
|
@@ -16998,7 +17298,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
16998
17298
|
if (video) await this.mainPeerConnection.updateSendersConstraints("video", video);
|
|
16999
17299
|
} catch (error) {
|
|
17000
17300
|
logger$16.warn("[WebRTCManager] Error updating media constraints:", error);
|
|
17001
|
-
this.
|
|
17301
|
+
this.reportLegError(toError(error), this.mainPeerConnection);
|
|
17002
17302
|
throw error;
|
|
17003
17303
|
}
|
|
17004
17304
|
}
|
|
@@ -17166,23 +17466,24 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17166
17466
|
return response;
|
|
17167
17467
|
}
|
|
17168
17468
|
async executeVerto(message, optionals = {}) {
|
|
17169
|
-
const
|
|
17469
|
+
const params = {
|
|
17170
17470
|
callID: optionals.callID ?? this.webRtcCallSession.id,
|
|
17171
17471
|
node_id: optionals.node_id ?? this._nodeId$.value ?? "",
|
|
17172
17472
|
message,
|
|
17173
17473
|
subscribe: optionals.subscribe
|
|
17174
|
-
}
|
|
17474
|
+
};
|
|
17475
|
+
const webrtcVertoMessage = WebrtcVerto(params);
|
|
17175
17476
|
const response = await this.webRtcCallSession.execute(webrtcVertoMessage);
|
|
17176
17477
|
const nonFatal = message.method === "verto.info" ? { fatal: false } : void 0;
|
|
17177
17478
|
if (response.error) {
|
|
17178
17479
|
const error = new JSONRPCError(response.error.code, response.error.message, response.error.data);
|
|
17179
|
-
this.
|
|
17480
|
+
this.reportLegError(error, this._rtcPeerConnectionsMap.get(params.callID), nonFatal);
|
|
17180
17481
|
return response;
|
|
17181
17482
|
}
|
|
17182
17483
|
const innerResult = getValueFrom(response, "result.result");
|
|
17183
17484
|
if (innerResult?.error) {
|
|
17184
17485
|
const error = new JSONRPCError(innerResult.error.code, innerResult.error.message, innerResult.error.data);
|
|
17185
|
-
this.
|
|
17486
|
+
this.reportLegError(error, this._rtcPeerConnectionsMap.get(params.callID), nonFatal);
|
|
17186
17487
|
return response;
|
|
17187
17488
|
}
|
|
17188
17489
|
return response;
|
|
@@ -17203,7 +17504,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17203
17504
|
}
|
|
17204
17505
|
} catch (error) {
|
|
17205
17506
|
logger$16.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
|
|
17206
|
-
this.
|
|
17507
|
+
this.reportLegError(toError(error), rtcPeerConnController);
|
|
17207
17508
|
if (vertoMethod === "verto.modify") this.onModifyFailed?.();
|
|
17208
17509
|
}
|
|
17209
17510
|
}
|
|
@@ -17219,7 +17520,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17219
17520
|
} catch (error) {
|
|
17220
17521
|
logger$16.warn("[WebRTCManager] Error processing modify response:", error);
|
|
17221
17522
|
const modifyError = error instanceof Error ? error : new Error(String(error), { cause: error });
|
|
17222
|
-
this.
|
|
17523
|
+
this.reportLegError(modifyError, rtcPeerConnController);
|
|
17223
17524
|
}
|
|
17224
17525
|
}
|
|
17225
17526
|
}
|
|
@@ -17232,7 +17533,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17232
17533
|
status,
|
|
17233
17534
|
signalingError
|
|
17234
17535
|
});
|
|
17235
|
-
this.
|
|
17536
|
+
this.reportLegError(signalingError, null, { fatal: false });
|
|
17236
17537
|
return;
|
|
17237
17538
|
}
|
|
17238
17539
|
if (rtcPeerConnController.isMainDevice) this._signalingStatus$.next(status);
|
|
@@ -17262,7 +17563,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17262
17563
|
} else {
|
|
17263
17564
|
logger$16.error("[WebRTCManager] Verto invite failed:", response);
|
|
17264
17565
|
const inviteError = response.error ? new JSONRPCError(response.error.code, response.error.message, response.error.data) : /* @__PURE__ */ new Error("Verto invite failed: unexpected response");
|
|
17265
|
-
this.
|
|
17566
|
+
this.reportLegError(inviteError, rtcPeerConnController);
|
|
17266
17567
|
}
|
|
17267
17568
|
}
|
|
17268
17569
|
get RTCPeerConnectionConfig() {
|
|
@@ -17301,7 +17602,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17301
17602
|
this._rtcPeerConnectionsMap.set(rtcPeerConnController.id, rtcPeerConnController);
|
|
17302
17603
|
this._rtcPeerConnections$.next(Array.from(this._rtcPeerConnectionsMap.values()));
|
|
17303
17604
|
this.subscribeTo(rtcPeerConnController.errors$, (error) => {
|
|
17304
|
-
this.
|
|
17605
|
+
this.reportLegError(error, rtcPeerConnController);
|
|
17305
17606
|
});
|
|
17306
17607
|
if (options.initOffer) this.handleInboundAnswer(rtcPeerConnController);
|
|
17307
17608
|
}
|
|
@@ -17330,7 +17631,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17330
17631
|
await rtcPeerConnController.acceptInbound(answerOptions);
|
|
17331
17632
|
} catch (error) {
|
|
17332
17633
|
logger$16.error("[WebRTCManager] Error creating inbound answer:", error);
|
|
17333
|
-
this.
|
|
17634
|
+
this.reportLegError(toError(error), rtcPeerConnController);
|
|
17334
17635
|
}
|
|
17335
17636
|
}
|
|
17336
17637
|
}
|
|
@@ -17434,7 +17735,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17434
17735
|
await this.attachManager.attach(this.buildAttachableCall());
|
|
17435
17736
|
} catch (error) {
|
|
17436
17737
|
logger$16.error("[WebRTCManager] Error sending Verto answer:", error);
|
|
17437
|
-
this.
|
|
17738
|
+
this.reportLegError(toError(error), rtcPeerConnectionController);
|
|
17438
17739
|
await rtcPeerConnectionController.updateAnswerStatus({ status: "failed" });
|
|
17439
17740
|
}
|
|
17440
17741
|
}
|
|
@@ -17506,7 +17807,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17506
17807
|
await this.mainPeerConnection.restoreTrackSender(deviceKind);
|
|
17507
17808
|
} else {
|
|
17508
17809
|
const error = new InvalidParams("No valid device to be added");
|
|
17509
|
-
this.
|
|
17810
|
+
this.reportLegError(error, this.mainPeerConnection);
|
|
17510
17811
|
throw error;
|
|
17511
17812
|
}
|
|
17512
17813
|
}
|
|
@@ -17531,21 +17832,30 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17531
17832
|
this._rtcPeerConnections$.next(Array.from(this._rtcPeerConnectionsMap.values()));
|
|
17532
17833
|
this.subscribeTo(rtcPeerConnController.errors$, (error) => {
|
|
17533
17834
|
firstPeerConnectionError ??= error;
|
|
17534
|
-
this.
|
|
17835
|
+
this.reportLegError(error, rtcPeerConnController);
|
|
17535
17836
|
});
|
|
17536
|
-
|
|
17837
|
+
const pc = rtcPeerConnController;
|
|
17838
|
+
await (0, import_cjs$15.firstValueFrom)((0, import_cjs$15.merge)(pc.localMediaSettled$.pipe((0, import_cjs$15.take)(1), (0, import_cjs$15.switchMap)(() => pc.connectionState$.pipe((0, import_cjs$15.filter)((state) => state === "connected"), (0, import_cjs$15.take)(1), (0, import_cjs$15.timeout)(DEFAULT_AUX_LEG_CONNECT_TIMEOUT_MS)))), this.legError$(pc.id)).pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
|
|
17537
17839
|
if (isScreenShare) this._screenShareStatus$.next("started");
|
|
17538
17840
|
logger$16.info(`[WebRTCManager] Additional peer connection connected (${propose}).`);
|
|
17539
17841
|
return rtcPeerConnController.id;
|
|
17540
17842
|
} catch (error) {
|
|
17541
17843
|
logger$16.warn("[WebRTCManager] Error initializing additional peer connection:", error);
|
|
17542
|
-
if (rtcPeerConnController)
|
|
17543
|
-
|
|
17844
|
+
if (rtcPeerConnController) {
|
|
17845
|
+
rtcPeerConnController.destroy();
|
|
17846
|
+
this._rtcPeerConnectionsMap.delete(rtcPeerConnController.id);
|
|
17847
|
+
this._rtcPeerConnections$.next(Array.from(this._rtcPeerConnectionsMap.values()));
|
|
17848
|
+
}
|
|
17849
|
+
if (isScreenShare) {
|
|
17850
|
+
this._screenShareStatus$.next("none");
|
|
17851
|
+
this._screenShareId = void 0;
|
|
17852
|
+
}
|
|
17544
17853
|
if (firstPeerConnectionError) throw firstPeerConnectionError instanceof MediaAccessError && firstPeerConnectionError.originalError instanceof Error ? firstPeerConnectionError.originalError : firstPeerConnectionError;
|
|
17545
17854
|
if (error instanceof import_cjs$15.EmptyError) {
|
|
17546
17855
|
logger$16.debug("[WebRTCManager] Additional peer connection aborted before connecting.");
|
|
17547
17856
|
return;
|
|
17548
17857
|
}
|
|
17858
|
+
if (error instanceof import_cjs$15.TimeoutError) throw new AuxiliaryLegTimeoutError(propose, error);
|
|
17549
17859
|
throw error instanceof Error ? error : new Error(String(error), { cause: error });
|
|
17550
17860
|
}
|
|
17551
17861
|
}
|
|
@@ -17591,7 +17901,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17591
17901
|
await this.executeVerto(VertoBye({
|
|
17592
17902
|
...causeParams,
|
|
17593
17903
|
dialogParams: this.dialogParams(rtcPeerConnController)
|
|
17594
|
-
}));
|
|
17904
|
+
}), { callID: rtcPeerConnController.id });
|
|
17595
17905
|
} catch (error) {
|
|
17596
17906
|
logger$16.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
|
|
17597
17907
|
throw error;
|
|
@@ -18983,13 +19293,13 @@ var WebRTCCall = class extends Destroyable {
|
|
|
18983
19293
|
get bandwidthConstrained$() {
|
|
18984
19294
|
return this.deferEmission(this._bandwidthConstrained$.asObservable());
|
|
18985
19295
|
}
|
|
18986
|
-
/** Observable that emits when server
|
|
19296
|
+
/** Observable that emits when the server pushes media params. */
|
|
18987
19297
|
get mediaParamsUpdated$() {
|
|
18988
19298
|
return this.deferEmission(this._mediaParamsUpdated$.asObservable());
|
|
18989
19299
|
}
|
|
18990
19300
|
/**
|
|
18991
19301
|
* @internal Emit a media params update event.
|
|
18992
|
-
* Called by the VertoManager when server
|
|
19302
|
+
* Called by the VertoManager when the server pushes media params.
|
|
18993
19303
|
*/
|
|
18994
19304
|
emitMediaParamsUpdated(event) {
|
|
18995
19305
|
this._mediaParamsUpdated$.next(event);
|
|
@@ -19197,6 +19507,10 @@ var WebRTCCall = class extends Destroyable {
|
|
|
19197
19507
|
get selfId$() {
|
|
19198
19508
|
return this.vertoManager.selfId$;
|
|
19199
19509
|
}
|
|
19510
|
+
/** @internal Lets call creation bound the media and signalling phases apart. */
|
|
19511
|
+
get localMediaSettled$() {
|
|
19512
|
+
return this.vertoManager.localMediaSettled$;
|
|
19513
|
+
}
|
|
19200
19514
|
/** Local participant's member ID, or `null` if not joined. */
|
|
19201
19515
|
get selfId() {
|
|
19202
19516
|
return this.vertoManager.selfId;
|
|
@@ -19592,6 +19906,9 @@ var WebRTCCall = class extends Destroyable {
|
|
|
19592
19906
|
/**
|
|
19593
19907
|
* Infers the semantic error category from a raw Error thrown by VertoManager
|
|
19594
19908
|
* or an RTCPeerConnection layer.
|
|
19909
|
+
*
|
|
19910
|
+
* Pure function — exported for unit testing.
|
|
19911
|
+
* @internal
|
|
19595
19912
|
*/
|
|
19596
19913
|
function inferCallErrorKind(error) {
|
|
19597
19914
|
if (error instanceof RPCTimeoutError) return "timeout";
|
|
@@ -19610,7 +19927,14 @@ const RECOVERABLE_RPC_CODES = new Set([
|
|
|
19610
19927
|
RPC_ERROR_AUTHENTICATION_FAILED,
|
|
19611
19928
|
RPC_ERROR_INVALID_PARAMS
|
|
19612
19929
|
]);
|
|
19613
|
-
/**
|
|
19930
|
+
/**
|
|
19931
|
+
* A *fallback*: callers knowing which leg failed pass an explicit `fatal` and
|
|
19932
|
+
* never reach here, so the default-fatal branch only sees call- and main-leg
|
|
19933
|
+
* errors. Auxiliary legs go through `WebRTCVertoManager.reportLegError`.
|
|
19934
|
+
*
|
|
19935
|
+
* Pure function — exported for unit testing.
|
|
19936
|
+
* @internal
|
|
19937
|
+
*/
|
|
19614
19938
|
function isFatalError(error) {
|
|
19615
19939
|
if (error instanceof VertoPongError) return false;
|
|
19616
19940
|
if (error instanceof MediaTrackError) return false;
|
|
@@ -19645,7 +19969,9 @@ var CallFactory = class {
|
|
|
19645
19969
|
kind: inferCallErrorKind(error),
|
|
19646
19970
|
fatal: options$1?.fatal ?? isFatalError(error),
|
|
19647
19971
|
error,
|
|
19648
|
-
callId: callInstance.id
|
|
19972
|
+
callId: callInstance.id,
|
|
19973
|
+
...options$1?.leg ? { leg: options$1.leg } : {},
|
|
19974
|
+
...options$1?.legId ? { legId: options$1.legId } : {}
|
|
19649
19975
|
};
|
|
19650
19976
|
callInstance.emitError(callError);
|
|
19651
19977
|
},
|
|
@@ -20117,6 +20443,30 @@ const logger$9 = getLogger();
|
|
|
20117
20443
|
function shouldAbortDial(callError) {
|
|
20118
20444
|
return callError.fatal || !(callError.error instanceof MediaAccessError);
|
|
20119
20445
|
}
|
|
20446
|
+
/**
|
|
20447
|
+
* Wait for a dialed call to be ready, or for the failure that stops it.
|
|
20448
|
+
*
|
|
20449
|
+
* Local media acquisition is deliberately unbounded: a permission prompt or a
|
|
20450
|
+
* device picker is human time, and `getUserMedia` cannot be cancelled anyway.
|
|
20451
|
+
* The clock starts only once acquisition settles, so a slow human never spends
|
|
20452
|
+
* the server's budget.
|
|
20453
|
+
*
|
|
20454
|
+
* `merge` rather than `race`, because the two legs settle asymmetrically. A
|
|
20455
|
+
* fatal acquisition failure reports the error and then destroys the call in the
|
|
20456
|
+
* same synchronous step; `errors$` defers delivery by a microtask while
|
|
20457
|
+
* `localMediaSettled$` completes immediately. Under `race` that bare completion
|
|
20458
|
+
* ended the wait first and `dial()` rejected with an RxJS `EmptyError`, burying
|
|
20459
|
+
* the `NotAllowedError` applications are told to inspect. Under `merge` the
|
|
20460
|
+
* completed leg is simply spent, and the queued error — enqueued before the
|
|
20461
|
+
* completion, so delivered before it — arrives to reject the wait. A dial
|
|
20462
|
+
* abandoned with no error at all still ends both legs, and the resulting
|
|
20463
|
+
* `EmptyError` remains the benign-cancel signal.
|
|
20464
|
+
*
|
|
20465
|
+
* Exported for unit testing.
|
|
20466
|
+
*/
|
|
20467
|
+
async function awaitDialReady(session, signalingTimeoutMs) {
|
|
20468
|
+
return (0, import_cjs$7.firstValueFrom)((0, import_cjs$7.merge)(session.localMediaSettled$.pipe((0, import_cjs$7.take)(1), (0, import_cjs$7.switchMap)(() => session.selfId$.pipe((0, import_cjs$7.filter)((id) => Boolean(id)), (0, import_cjs$7.take)(1), (0, import_cjs$7.timeout)(signalingTimeoutMs)))), session.errors$.pipe((0, import_cjs$7.filter)(shouldAbortDial), (0, import_cjs$7.take)(1), (0, import_cjs$7.switchMap)((callError) => (0, import_cjs$7.throwError)(() => callError.error)))));
|
|
20469
|
+
}
|
|
20120
20470
|
const getAddressSearchURI = (options) => {
|
|
20121
20471
|
const to = options.to?.split("?")[0];
|
|
20122
20472
|
const from$9 = options.from?.startsWith("subscriber://") ? options.from.replace("subscriber://", "") : options.from;
|
|
@@ -20133,7 +20483,6 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
20133
20483
|
this.authorizationStateKey = authorizationStateKey;
|
|
20134
20484
|
this.attachManager = attachManager;
|
|
20135
20485
|
this.dpopManager = dpopManager;
|
|
20136
|
-
this.callCreateTimeout = 6e3;
|
|
20137
20486
|
this.agent = `signalwire-js/4.0.0`;
|
|
20138
20487
|
this.eventAcks = true;
|
|
20139
20488
|
this.authorizationState$ = this.createReplaySubject(1);
|
|
@@ -20561,7 +20910,7 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
20561
20910
|
to: destinationURI,
|
|
20562
20911
|
...options
|
|
20563
20912
|
});
|
|
20564
|
-
await (
|
|
20913
|
+
await awaitDialReady(callSession, DEFAULT_CALL_SIGNALING_TIMEOUT_MS);
|
|
20565
20914
|
this._calls$.next({
|
|
20566
20915
|
[`${callSession.id}`]: callSession,
|
|
20567
20916
|
...this._calls$.value
|
|
@@ -22739,6 +23088,11 @@ var SignalWire = class extends Destroyable {
|
|
|
22739
23088
|
* Returns a {@link Call} in `'ringing'` state. Subscribe to {@link Call.status$}
|
|
22740
23089
|
* to track progression through `'connected'` → `'disconnected'`.
|
|
22741
23090
|
*
|
|
23091
|
+
* Local media acquisition is deliberately unbounded: an unanswered permission
|
|
23092
|
+
* prompt leaves this promise pending indefinitely, so apply your own bound if
|
|
23093
|
+
* your UI needs one. The 12 s signaling budget starts only once acquisition
|
|
23094
|
+
* settles.
|
|
23095
|
+
*
|
|
22742
23096
|
* @param destination - Address URI string (e.g. `'/public/my-room'`) or {@link Address} instance.
|
|
22743
23097
|
* @param options - Media and dial options (audio/video, device constraints). Overrides defaults.
|
|
22744
23098
|
* @returns The created {@link Call} instance.
|
|
@@ -23219,6 +23573,7 @@ if (typeof process === "undefined") globalThis.process = { env: { NODE_ENV: "pro
|
|
|
23219
23573
|
|
|
23220
23574
|
//#endregion
|
|
23221
23575
|
exports.Address = Address;
|
|
23576
|
+
exports.AuxiliaryLegTimeoutError = AuxiliaryLegTimeoutError;
|
|
23222
23577
|
exports.CallCreateError = CallCreateError;
|
|
23223
23578
|
exports.CallNotReadyError = CallNotReadyError;
|
|
23224
23579
|
exports.ClientPreferences = ClientPreferences;
|