@signalwire/js 4.0.0-dev-20260908172838 → 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.mjs
CHANGED
|
@@ -4186,7 +4186,7 @@ var require_merge$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
4186
4186
|
var empty_1$7 = require_empty();
|
|
4187
4187
|
var args_1$8 = require_args();
|
|
4188
4188
|
var from_1$4 = require_from();
|
|
4189
|
-
function merge$
|
|
4189
|
+
function merge$7() {
|
|
4190
4190
|
var args = [];
|
|
4191
4191
|
for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
|
|
4192
4192
|
var scheduler = args_1$8.popScheduler(args);
|
|
@@ -4194,7 +4194,7 @@ var require_merge$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
4194
4194
|
var sources = args;
|
|
4195
4195
|
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));
|
|
4196
4196
|
}
|
|
4197
|
-
exports.merge = merge$
|
|
4197
|
+
exports.merge = merge$7;
|
|
4198
4198
|
}));
|
|
4199
4199
|
|
|
4200
4200
|
//#endregion
|
|
@@ -4325,13 +4325,13 @@ var require_race$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
4325
4325
|
var innerFrom_1$28 = require_innerFrom();
|
|
4326
4326
|
var argsOrArgArray_1$4 = require_argsOrArgArray();
|
|
4327
4327
|
var OperatorSubscriber_1$48 = require_OperatorSubscriber();
|
|
4328
|
-
function race$
|
|
4328
|
+
function race$4() {
|
|
4329
4329
|
var sources = [];
|
|
4330
4330
|
for (var _i = 0; _i < arguments.length; _i++) sources[_i] = arguments[_i];
|
|
4331
4331
|
sources = argsOrArgArray_1$4.argsOrArgArray(sources);
|
|
4332
4332
|
return sources.length === 1 ? innerFrom_1$28.innerFrom(sources[0]) : new Observable_1$6.Observable(raceInit(sources));
|
|
4333
4333
|
}
|
|
4334
|
-
exports.race = race$
|
|
4334
|
+
exports.race = race$4;
|
|
4335
4335
|
function raceInit(sources) {
|
|
4336
4336
|
return function(subscriber) {
|
|
4337
4337
|
var subscriptions = [];
|
|
@@ -6076,7 +6076,7 @@ var require_merge = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
6076
6076
|
var mergeAll_1$2 = require_mergeAll();
|
|
6077
6077
|
var args_1$3 = require_args();
|
|
6078
6078
|
var from_1$1 = require_from();
|
|
6079
|
-
function merge$
|
|
6079
|
+
function merge$6() {
|
|
6080
6080
|
var args = [];
|
|
6081
6081
|
for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
|
|
6082
6082
|
var scheduler = args_1$3.popScheduler(args);
|
|
@@ -6085,7 +6085,7 @@ var require_merge = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
6085
6085
|
mergeAll_1$2.mergeAll(concurrent)(from_1$1.from(__spreadArray$8([source], __read$8(args)), scheduler)).subscribe(subscriber);
|
|
6086
6086
|
});
|
|
6087
6087
|
}
|
|
6088
|
-
exports.merge = merge$
|
|
6088
|
+
exports.merge = merge$6;
|
|
6089
6089
|
}));
|
|
6090
6090
|
|
|
6091
6091
|
//#endregion
|
|
@@ -6932,7 +6932,7 @@ var require_switchMap = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
6932
6932
|
var innerFrom_1$6 = require_innerFrom();
|
|
6933
6933
|
var lift_1$13 = require_lift();
|
|
6934
6934
|
var OperatorSubscriber_1$11 = require_OperatorSubscriber();
|
|
6935
|
-
function switchMap$
|
|
6935
|
+
function switchMap$8(project, resultSelector) {
|
|
6936
6936
|
return lift_1$13.operate(function(source, subscriber) {
|
|
6937
6937
|
var innerSubscriber = null;
|
|
6938
6938
|
var index = 0;
|
|
@@ -6956,7 +6956,7 @@ var require_switchMap = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
6956
6956
|
}));
|
|
6957
6957
|
});
|
|
6958
6958
|
}
|
|
6959
|
-
exports.switchMap = switchMap$
|
|
6959
|
+
exports.switchMap = switchMap$8;
|
|
6960
6960
|
}));
|
|
6961
6961
|
|
|
6962
6962
|
//#endregion
|
|
@@ -9022,6 +9022,25 @@ const DEFAULT_ICE_CANDIDATE_TIMEOUT_MS = 600;
|
|
|
9022
9022
|
const DEFAULT_ICE_GATHERING_TIMEOUT_MS = 6e3;
|
|
9023
9023
|
const DEFAULT_RECONNECT_CALLS_TIMEOUT_MS = 300 * 1e3;
|
|
9024
9024
|
const DEFAULT_CONNECTION_TIMEOUT_MS = 1e4;
|
|
9025
|
+
/**
|
|
9026
|
+
* How long call setup may wait for the network, measured from local media
|
|
9027
|
+
* settling until the member id arrives.
|
|
9028
|
+
*
|
|
9029
|
+
* Local media acquisition is deliberately NOT inside this: a permission prompt
|
|
9030
|
+
* or a device picker is human time, and a human may take as long as they like
|
|
9031
|
+
* without spending the server's budget. The clock starts when acquisition ends.
|
|
9032
|
+
*
|
|
9033
|
+
* Must exceed `iceGatheringTimeout + the RPC timeout`, which both run inside it.
|
|
9034
|
+
*/
|
|
9035
|
+
const DEFAULT_CALL_SIGNALING_TIMEOUT_MS = 12e3;
|
|
9036
|
+
/**
|
|
9037
|
+
* How long an auxiliary leg (screen share, additional device) may take to
|
|
9038
|
+
* connect once its media is in hand.
|
|
9039
|
+
*
|
|
9040
|
+
* One bound for both: the picker is human time and sits outside it. What remains
|
|
9041
|
+
* — offer, ICE, invite, answer, DTLS — is the same work for either leg kind.
|
|
9042
|
+
*/
|
|
9043
|
+
const DEFAULT_AUX_LEG_CONNECT_TIMEOUT_MS = 15e3;
|
|
9025
9044
|
const DEFAULT_RECONNECT_DELAY_MIN_MS = 100;
|
|
9026
9045
|
const DEFAULT_RECONNECT_DELAY_MAX_MS = 3e3;
|
|
9027
9046
|
const DEFAULT_DEVICE_DEBOUNCE_TIME_MS = 1500;
|
|
@@ -9374,6 +9393,18 @@ var CollectionFetchError = class extends Error {
|
|
|
9374
9393
|
this.name = "CollectionFetchError";
|
|
9375
9394
|
}
|
|
9376
9395
|
};
|
|
9396
|
+
/**
|
|
9397
|
+
* An auxiliary leg did not connect within its budget. Typed rather than a bare
|
|
9398
|
+
* RxJS `TimeoutError` so the leg and cause survive.
|
|
9399
|
+
*/
|
|
9400
|
+
var AuxiliaryLegTimeoutError = class extends Error {
|
|
9401
|
+
constructor(leg, originalError) {
|
|
9402
|
+
super(`Timed out waiting for the ${leg} connection to be established`, { cause: originalError });
|
|
9403
|
+
this.leg = leg;
|
|
9404
|
+
this.originalError = originalError;
|
|
9405
|
+
this.name = "AuxiliaryLegTimeoutError";
|
|
9406
|
+
}
|
|
9407
|
+
};
|
|
9377
9408
|
var MediaTrackError = class extends Error {
|
|
9378
9409
|
constructor(operation, kind, originalError) {
|
|
9379
9410
|
super(`Media track ${operation} failed for ${kind}`, { cause: originalError instanceof Error ? originalError : void 0 });
|
|
@@ -9650,9 +9681,9 @@ var require_loglevel = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
9650
9681
|
defaultLogger$1 = new Logger();
|
|
9651
9682
|
defaultLogger$1.getLogger = function getLogger$1(name) {
|
|
9652
9683
|
if (typeof name !== "symbol" && typeof name !== "string" || name === "") throw new TypeError("You must supply a name when creating a logger.");
|
|
9653
|
-
var logger$
|
|
9654
|
-
if (!logger$
|
|
9655
|
-
return logger$
|
|
9684
|
+
var logger$34 = _loggersByName[name];
|
|
9685
|
+
if (!logger$34) logger$34 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
|
|
9686
|
+
return logger$34;
|
|
9656
9687
|
};
|
|
9657
9688
|
var _log = typeof window !== undefinedType ? window.log : void 0;
|
|
9658
9689
|
defaultLogger$1.noConflict = function() {
|
|
@@ -9688,8 +9719,8 @@ const defaultLoggerLevel = defaultLogger.levels.WARN;
|
|
|
9688
9719
|
defaultLogger.setLevel(defaultLoggerLevel);
|
|
9689
9720
|
let userLogger = null;
|
|
9690
9721
|
/** Replace the built-in logger with a custom implementation. Pass `null` to restore defaults. */
|
|
9691
|
-
const setLogger = (logger$
|
|
9692
|
-
userLogger = logger$
|
|
9722
|
+
const setLogger = (logger$34) => {
|
|
9723
|
+
userLogger = logger$34;
|
|
9693
9724
|
};
|
|
9694
9725
|
let debugOptions = {};
|
|
9695
9726
|
/** Configure debug options (e.g., `{ logWsTraffic: true }`). */
|
|
@@ -9733,8 +9764,8 @@ const wsTraffic = (options) => {
|
|
|
9733
9764
|
loggerInstance.debug(`${options.type.toUpperCase()}: \n`, msg, "\n");
|
|
9734
9765
|
};
|
|
9735
9766
|
const getLogger = () => {
|
|
9736
|
-
const logger$
|
|
9737
|
-
return new Proxy(logger$
|
|
9767
|
+
const logger$34 = getLoggerInstance();
|
|
9768
|
+
return new Proxy(logger$34, { get(_target, prop, _receiver) {
|
|
9738
9769
|
if (prop === "wsTraffic") return wsTraffic;
|
|
9739
9770
|
const instance = getLoggerInstance();
|
|
9740
9771
|
const value = Reflect.get(instance, prop);
|
|
@@ -9786,7 +9817,7 @@ const asyncRetry = async ({ asyncCallable, maxRetries: retries = DEFAULT_MAX_RET
|
|
|
9786
9817
|
|
|
9787
9818
|
//#endregion
|
|
9788
9819
|
//#region src/controllers/HTTPRequestController.ts
|
|
9789
|
-
const logger$
|
|
9820
|
+
const logger$33 = getLogger();
|
|
9790
9821
|
const GET_PARAMS = {
|
|
9791
9822
|
method: "GET",
|
|
9792
9823
|
headers: { Accept: "application/json" }
|
|
@@ -9850,7 +9881,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
|
|
|
9850
9881
|
this._responses$.next(response);
|
|
9851
9882
|
return response;
|
|
9852
9883
|
} catch (error) {
|
|
9853
|
-
logger$
|
|
9884
|
+
logger$33.error("[HTTPRequestController] Request error:", error);
|
|
9854
9885
|
this._status$.next("error");
|
|
9855
9886
|
const err = error instanceof Error ? error : new Error("HTTP request failed", { cause: error });
|
|
9856
9887
|
this._errors$.next(err);
|
|
@@ -9877,7 +9908,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
|
|
|
9877
9908
|
const url = this.buildURL(request.url);
|
|
9878
9909
|
const headers = this.buildHeaders(request.headers);
|
|
9879
9910
|
const timeout$5 = request.timeout ?? this.requestTimeout;
|
|
9880
|
-
logger$
|
|
9911
|
+
logger$33.debug("[HTTPRequestController] Executing request:", {
|
|
9881
9912
|
method: request.method,
|
|
9882
9913
|
url,
|
|
9883
9914
|
headers: Object.keys(headers).reduce((acc, key) => {
|
|
@@ -9897,7 +9928,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
|
|
|
9897
9928
|
});
|
|
9898
9929
|
clearTimeout(timeoutId);
|
|
9899
9930
|
const httpResponse = await this.convertResponse(response);
|
|
9900
|
-
logger$
|
|
9931
|
+
logger$33.debug("[HTTPRequestController] Response received:", {
|
|
9901
9932
|
status: response.status,
|
|
9902
9933
|
statusText: response.statusText,
|
|
9903
9934
|
headers: [...response.headers.entries()],
|
|
@@ -9907,7 +9938,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
|
|
|
9907
9938
|
} catch (error) {
|
|
9908
9939
|
clearTimeout(timeoutId);
|
|
9909
9940
|
if (error instanceof Error && error.name === "AbortError") throw new RequestTimeoutError(`Request timeout after ${timeout$5}ms`, { cause: error });
|
|
9910
|
-
logger$
|
|
9941
|
+
logger$33.error("[HTTPRequestController] Request failed:", error);
|
|
9911
9942
|
throw error;
|
|
9912
9943
|
}
|
|
9913
9944
|
}
|
|
@@ -9921,8 +9952,8 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
|
|
|
9921
9952
|
const credential = this.getCredential();
|
|
9922
9953
|
if (credential.token) {
|
|
9923
9954
|
headers.Authorization = `Bearer ${credential.token}`;
|
|
9924
|
-
logger$
|
|
9925
|
-
} else logger$
|
|
9955
|
+
logger$33.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
|
|
9956
|
+
} else logger$33.warn("[HTTPRequestController] No credentials available for authentication");
|
|
9926
9957
|
return headers;
|
|
9927
9958
|
}
|
|
9928
9959
|
/**
|
|
@@ -10070,7 +10101,7 @@ function fromMsToSec(milliseconds) {
|
|
|
10070
10101
|
|
|
10071
10102
|
//#endregion
|
|
10072
10103
|
//#region src/containers/PreferencesContainer.ts
|
|
10073
|
-
const logger$
|
|
10104
|
+
const logger$32 = getLogger();
|
|
10074
10105
|
var PreferencesContainer = class PreferencesContainer {
|
|
10075
10106
|
static get instance() {
|
|
10076
10107
|
this._instance ??= new PreferencesContainer();
|
|
@@ -10732,7 +10763,7 @@ var ClientPreferences = class {
|
|
|
10732
10763
|
if (!this._storage) return;
|
|
10733
10764
|
const data = collectStoredPreferences();
|
|
10734
10765
|
this._storage.setItem(PREFERENCES_STORAGE_KEY, data, "local").catch((error) => {
|
|
10735
|
-
logger$
|
|
10766
|
+
logger$32.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
|
|
10736
10767
|
});
|
|
10737
10768
|
}
|
|
10738
10769
|
/** Loads preferences from storage and applies them to the container. */
|
|
@@ -10741,7 +10772,7 @@ var ClientPreferences = class {
|
|
|
10741
10772
|
this._storage.getItem(PREFERENCES_STORAGE_KEY, "local").then((stored) => {
|
|
10742
10773
|
if (stored) applyStoredPreferences(stored);
|
|
10743
10774
|
}).catch((error) => {
|
|
10744
|
-
logger$
|
|
10775
|
+
logger$32.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
|
|
10745
10776
|
});
|
|
10746
10777
|
}
|
|
10747
10778
|
};
|
|
@@ -10763,7 +10794,7 @@ function toError(value) {
|
|
|
10763
10794
|
//#endregion
|
|
10764
10795
|
//#region src/controllers/NavigatorDeviceController.ts
|
|
10765
10796
|
var import_cjs$29 = require_cjs();
|
|
10766
|
-
const logger$
|
|
10797
|
+
const logger$31 = getLogger();
|
|
10767
10798
|
/** Maps a device kind to its storage key. */
|
|
10768
10799
|
const DEVICE_STORAGE_KEYS = {
|
|
10769
10800
|
audioinput: DEVICE_STORAGE_KEY_AUDIO_INPUT,
|
|
@@ -10785,7 +10816,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
10785
10816
|
super();
|
|
10786
10817
|
this.webRTCApiProvider = webRTCApiProvider;
|
|
10787
10818
|
this.deviceChangeHandler = () => {
|
|
10788
|
-
logger$
|
|
10819
|
+
logger$31.debug("[DeviceController] Device change detected");
|
|
10789
10820
|
this.enumerateDevices();
|
|
10790
10821
|
};
|
|
10791
10822
|
this._devicesState$ = this.createBehaviorSubject(initialDevicesState);
|
|
@@ -10850,13 +10881,13 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
10850
10881
|
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$)));
|
|
10851
10882
|
}
|
|
10852
10883
|
get selectedAudioInputDevice$() {
|
|
10853
|
-
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$
|
|
10884
|
+
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))));
|
|
10854
10885
|
}
|
|
10855
10886
|
get selectedAudioOutputDevice$() {
|
|
10856
|
-
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$
|
|
10887
|
+
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))));
|
|
10857
10888
|
}
|
|
10858
10889
|
get selectedVideoInputDevice$() {
|
|
10859
|
-
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$
|
|
10890
|
+
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))));
|
|
10860
10891
|
}
|
|
10861
10892
|
get selectedAudioInputDevice() {
|
|
10862
10893
|
if (this._audioInputDisabled$.value) return null;
|
|
@@ -10931,7 +10962,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
10931
10962
|
if (device) this.persistDeviceSelection("audioinput", device);
|
|
10932
10963
|
}
|
|
10933
10964
|
selectVideoInputDevice(device) {
|
|
10934
|
-
logger$
|
|
10965
|
+
logger$31.debug("[DeviceController] Setting selected video input device:", device);
|
|
10935
10966
|
if (this._videoInputDisabled$.value && device) this._videoInputDisabled$.next(false);
|
|
10936
10967
|
const previous = this._selectedDevicesState$.value.videoinput;
|
|
10937
10968
|
if (previous && previous.deviceId !== device?.deviceId) this._deviceHistory.push("videoinput", previous);
|
|
@@ -10988,7 +11019,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
10988
11019
|
}
|
|
10989
11020
|
const fromHistory = this._deviceHistory.findInHistory(kind, devices);
|
|
10990
11021
|
if (fromHistory) {
|
|
10991
|
-
logger$
|
|
11022
|
+
logger$31.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
|
|
10992
11023
|
this.emitDeviceRecovered(kind, selected, fromHistory, "device_disconnected");
|
|
10993
11024
|
return fromHistory;
|
|
10994
11025
|
}
|
|
@@ -11041,7 +11072,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
11041
11072
|
try {
|
|
11042
11073
|
await this._storageManager.setItem(DEVICE_STORAGE_KEYS[kind], stored, "local");
|
|
11043
11074
|
} catch (error) {
|
|
11044
|
-
logger$
|
|
11075
|
+
logger$31.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
|
|
11045
11076
|
}
|
|
11046
11077
|
}
|
|
11047
11078
|
async loadPersistedDevices() {
|
|
@@ -11057,7 +11088,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
11057
11088
|
[kind]: stored
|
|
11058
11089
|
};
|
|
11059
11090
|
} catch (error) {
|
|
11060
|
-
logger$
|
|
11091
|
+
logger$31.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
|
|
11061
11092
|
}
|
|
11062
11093
|
}
|
|
11063
11094
|
/** Clears device history, persisted selections, and re-enumerates devices. */
|
|
@@ -11075,7 +11106,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
11075
11106
|
this.disableDeviceMonitoring();
|
|
11076
11107
|
this.webRTCApiProvider.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
|
|
11077
11108
|
if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$29.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
|
|
11078
|
-
logger$
|
|
11109
|
+
logger$31.debug("[DeviceController] Polling devices due to interval");
|
|
11079
11110
|
this.enumerateDevices();
|
|
11080
11111
|
});
|
|
11081
11112
|
this.enumerateDevices();
|
|
@@ -11101,13 +11132,13 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
11101
11132
|
videoinput: []
|
|
11102
11133
|
});
|
|
11103
11134
|
this._devicesState$.next(devicesByKind);
|
|
11104
|
-
logger$
|
|
11135
|
+
logger$31.debug("[DeviceController] Devices enumerated:", {
|
|
11105
11136
|
audioInputs: devicesByKind.audioinput.length,
|
|
11106
11137
|
audioOutputs: devicesByKind.audiooutput.length,
|
|
11107
11138
|
videoInputs: devicesByKind.videoinput.length
|
|
11108
11139
|
});
|
|
11109
11140
|
} catch (error) {
|
|
11110
|
-
logger$
|
|
11141
|
+
logger$31.error("[DeviceController] Failed to enumerate devices:", error);
|
|
11111
11142
|
this._errors$.next(toError(error));
|
|
11112
11143
|
}
|
|
11113
11144
|
}
|
|
@@ -11123,7 +11154,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
11123
11154
|
stream.getTracks().forEach((t) => t.stop());
|
|
11124
11155
|
return capabilities;
|
|
11125
11156
|
} catch (error) {
|
|
11126
|
-
logger$
|
|
11157
|
+
logger$31.error("[DeviceController] Failed to get device capabilities:", error);
|
|
11127
11158
|
this._errors$.next(toError(error));
|
|
11128
11159
|
throw error;
|
|
11129
11160
|
}
|
|
@@ -11374,7 +11405,7 @@ var DependencyContainer = class {
|
|
|
11374
11405
|
|
|
11375
11406
|
//#endregion
|
|
11376
11407
|
//#region src/controllers/CryptoController.ts
|
|
11377
|
-
const logger$
|
|
11408
|
+
const logger$30 = getLogger();
|
|
11378
11409
|
const DPOP_DB_NAME = "sw-dpop";
|
|
11379
11410
|
const DPOP_DB_VERSION = 1;
|
|
11380
11411
|
const DPOP_STORE_NAME = "keys";
|
|
@@ -11433,7 +11464,7 @@ async function loadKeyPairFromDB() {
|
|
|
11433
11464
|
tx.oncomplete = () => db.close();
|
|
11434
11465
|
});
|
|
11435
11466
|
} catch (error) {
|
|
11436
|
-
logger$
|
|
11467
|
+
logger$30.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
|
|
11437
11468
|
return null;
|
|
11438
11469
|
}
|
|
11439
11470
|
}
|
|
@@ -11453,7 +11484,7 @@ async function saveKeyPairToDB(keyPair) {
|
|
|
11453
11484
|
};
|
|
11454
11485
|
});
|
|
11455
11486
|
} catch (error) {
|
|
11456
|
-
logger$
|
|
11487
|
+
logger$30.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
|
|
11457
11488
|
}
|
|
11458
11489
|
}
|
|
11459
11490
|
async function deleteKeyPairFromDB() {
|
|
@@ -11472,7 +11503,7 @@ async function deleteKeyPairFromDB() {
|
|
|
11472
11503
|
};
|
|
11473
11504
|
});
|
|
11474
11505
|
} catch (error) {
|
|
11475
|
-
logger$
|
|
11506
|
+
logger$30.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
|
|
11476
11507
|
}
|
|
11477
11508
|
}
|
|
11478
11509
|
/**
|
|
@@ -11532,13 +11563,13 @@ var CryptoController = class {
|
|
|
11532
11563
|
this._publicJwk = await crypto.subtle.exportKey("jwk", stored.publicKey);
|
|
11533
11564
|
this._fingerprint = await computeJwkThumbprint(this._publicJwk);
|
|
11534
11565
|
this._initialized = true;
|
|
11535
|
-
logger$
|
|
11566
|
+
logger$30.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
|
|
11536
11567
|
return this._fingerprint;
|
|
11537
11568
|
} catch (error) {
|
|
11538
|
-
logger$
|
|
11569
|
+
logger$30.warn("[DPoP] Stored key pair unusable, generating new one:", error);
|
|
11539
11570
|
await deleteKeyPairFromDB();
|
|
11540
11571
|
}
|
|
11541
|
-
logger$
|
|
11572
|
+
logger$30.debug("[DPoP] Generating RSA key pair");
|
|
11542
11573
|
this._keyPair = await crypto.subtle.generateKey({
|
|
11543
11574
|
name: "RSASSA-PKCS1-v1_5",
|
|
11544
11575
|
modulusLength: 2048,
|
|
@@ -11553,7 +11584,7 @@ var CryptoController = class {
|
|
|
11553
11584
|
this._fingerprint = await computeJwkThumbprint(this._publicJwk);
|
|
11554
11585
|
this._initialized = true;
|
|
11555
11586
|
await saveKeyPairToDB(this._keyPair);
|
|
11556
|
-
logger$
|
|
11587
|
+
logger$30.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
|
|
11557
11588
|
return this._fingerprint;
|
|
11558
11589
|
}
|
|
11559
11590
|
/**
|
|
@@ -11619,7 +11650,7 @@ var CryptoController = class {
|
|
|
11619
11650
|
this._fingerprint = null;
|
|
11620
11651
|
this._initialized = false;
|
|
11621
11652
|
deleteKeyPairFromDB();
|
|
11622
|
-
logger$
|
|
11653
|
+
logger$30.debug("[DPoP] Controller destroyed");
|
|
11623
11654
|
}
|
|
11624
11655
|
get publicJwk() {
|
|
11625
11656
|
if (!this._publicJwk) throw new DPoPInitError("CryptoController not initialized. Call init() first.");
|
|
@@ -11643,7 +11674,7 @@ var CryptoController = class {
|
|
|
11643
11674
|
//#endregion
|
|
11644
11675
|
//#region src/controllers/NetworkMonitor.ts
|
|
11645
11676
|
var import_cjs$28 = require_cjs();
|
|
11646
|
-
const logger$
|
|
11677
|
+
const logger$29 = getLogger();
|
|
11647
11678
|
/**
|
|
11648
11679
|
* Safely check whether we are running in a browser environment
|
|
11649
11680
|
* with `window` and the relevant event targets.
|
|
@@ -11700,7 +11731,7 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
11700
11731
|
}
|
|
11701
11732
|
attachListeners() {
|
|
11702
11733
|
if (!hasBrowserNetworkEvents()) {
|
|
11703
|
-
logger$
|
|
11734
|
+
logger$29.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
|
|
11704
11735
|
return;
|
|
11705
11736
|
}
|
|
11706
11737
|
window.addEventListener("online", this._onOnline);
|
|
@@ -11708,7 +11739,7 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
11708
11739
|
const connection = getNetworkConnection();
|
|
11709
11740
|
if (connection) connection.addEventListener("change", this._onConnectionChange);
|
|
11710
11741
|
this._listenersAttached = true;
|
|
11711
|
-
logger$
|
|
11742
|
+
logger$29.debug("NetworkMonitor: event listeners attached");
|
|
11712
11743
|
}
|
|
11713
11744
|
removeListeners() {
|
|
11714
11745
|
if (!this._listenersAttached) return;
|
|
@@ -11719,10 +11750,10 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
11719
11750
|
if (connection) connection.removeEventListener("change", this._onConnectionChange);
|
|
11720
11751
|
}
|
|
11721
11752
|
this._listenersAttached = false;
|
|
11722
|
-
logger$
|
|
11753
|
+
logger$29.debug("NetworkMonitor: event listeners removed");
|
|
11723
11754
|
}
|
|
11724
11755
|
handleOnline() {
|
|
11725
|
-
logger$
|
|
11756
|
+
logger$29.info("NetworkMonitor: browser went online");
|
|
11726
11757
|
this._isOnline$.next(true);
|
|
11727
11758
|
this._networkChange$.next({
|
|
11728
11759
|
type: "online",
|
|
@@ -11731,7 +11762,7 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
11731
11762
|
});
|
|
11732
11763
|
}
|
|
11733
11764
|
handleOffline() {
|
|
11734
|
-
logger$
|
|
11765
|
+
logger$29.info("NetworkMonitor: browser went offline");
|
|
11735
11766
|
this._isOnline$.next(false);
|
|
11736
11767
|
this._networkChange$.next({
|
|
11737
11768
|
type: "offline",
|
|
@@ -11740,7 +11771,7 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
11740
11771
|
}
|
|
11741
11772
|
handleConnectionChange() {
|
|
11742
11773
|
const networkType = getNetworkType();
|
|
11743
|
-
logger$
|
|
11774
|
+
logger$29.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
|
|
11744
11775
|
this._networkChange$.next({
|
|
11745
11776
|
type: "connection_change",
|
|
11746
11777
|
timestamp: Date.now(),
|
|
@@ -11856,7 +11887,7 @@ function getNavigatorMediaDevices() {
|
|
|
11856
11887
|
//#endregion
|
|
11857
11888
|
//#region src/controllers/PreflightRunner.ts
|
|
11858
11889
|
var import_cjs$27 = require_cjs();
|
|
11859
|
-
const logger$
|
|
11890
|
+
const logger$28 = getLogger();
|
|
11860
11891
|
const DEFAULT_MEDIA_TEST_DURATION_S = 10;
|
|
11861
11892
|
const ICE_GATHERING_TIMEOUT_MS = 1e4;
|
|
11862
11893
|
const SIGNALING_RTT_TIMEOUT_MS = 5e3;
|
|
@@ -11905,7 +11936,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
11905
11936
|
if (!this._options.skipMediaTest) try {
|
|
11906
11937
|
bandwidth = await this.testMediaBandwidth(destination);
|
|
11907
11938
|
} catch (error) {
|
|
11908
|
-
logger$
|
|
11939
|
+
logger$28.warn("[PreflightRunner] Media bandwidth test failed:", error);
|
|
11909
11940
|
warnings.push("Media bandwidth test failed");
|
|
11910
11941
|
}
|
|
11911
11942
|
return {
|
|
@@ -11917,7 +11948,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
11917
11948
|
warnings
|
|
11918
11949
|
};
|
|
11919
11950
|
} catch (error) {
|
|
11920
|
-
logger$
|
|
11951
|
+
logger$28.error("[PreflightRunner] Preflight test failed:", error);
|
|
11921
11952
|
throw new PreflightError("preflight", error instanceof Error ? error : new Error(String(error)));
|
|
11922
11953
|
} finally {
|
|
11923
11954
|
this.destroy();
|
|
@@ -11948,7 +11979,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
11948
11979
|
if (track.kind === "video" && track.readyState === "live") videoWorking = true;
|
|
11949
11980
|
}
|
|
11950
11981
|
} catch (error) {
|
|
11951
|
-
logger$
|
|
11982
|
+
logger$28.warn("[PreflightRunner] Device test failed:", error);
|
|
11952
11983
|
} finally {
|
|
11953
11984
|
if (audioStream) audioStream.getTracks().forEach((t) => t.stop());
|
|
11954
11985
|
}
|
|
@@ -12006,7 +12037,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
12006
12037
|
rttMs
|
|
12007
12038
|
};
|
|
12008
12039
|
} catch (error) {
|
|
12009
|
-
logger$
|
|
12040
|
+
logger$28.warn("[PreflightRunner] ICE connectivity test failed:", error);
|
|
12010
12041
|
return {
|
|
12011
12042
|
type: "failed",
|
|
12012
12043
|
turnReachable: false,
|
|
@@ -12054,7 +12085,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
12054
12085
|
//#endregion
|
|
12055
12086
|
//#region src/controllers/VisibilityController.ts
|
|
12056
12087
|
var import_cjs$26 = require_cjs();
|
|
12057
|
-
const logger$
|
|
12088
|
+
const logger$27 = getLogger();
|
|
12058
12089
|
/**
|
|
12059
12090
|
* Checks whether the document visibility API is available.
|
|
12060
12091
|
*/
|
|
@@ -12091,8 +12122,8 @@ var VisibilityController = class extends Destroyable {
|
|
|
12091
12122
|
this._boundHandler = this._handleVisibilityChange.bind(this);
|
|
12092
12123
|
if (this._hasVisibilityApi) {
|
|
12093
12124
|
document.addEventListener("visibilitychange", this._boundHandler);
|
|
12094
|
-
logger$
|
|
12095
|
-
} else logger$
|
|
12125
|
+
logger$27.debug("VisibilityController: listening for visibilitychange events");
|
|
12126
|
+
} else logger$27.debug("VisibilityController: document visibility API not available, defaulting to visible");
|
|
12096
12127
|
}
|
|
12097
12128
|
/**
|
|
12098
12129
|
* Observable of the current visibility state.
|
|
@@ -12117,7 +12148,7 @@ var VisibilityController = class extends Destroyable {
|
|
|
12117
12148
|
destroy() {
|
|
12118
12149
|
if (this._hasVisibilityApi) {
|
|
12119
12150
|
document.removeEventListener("visibilitychange", this._boundHandler);
|
|
12120
|
-
logger$
|
|
12151
|
+
logger$27.debug("VisibilityController: removed visibilitychange listener");
|
|
12121
12152
|
}
|
|
12122
12153
|
super.destroy();
|
|
12123
12154
|
}
|
|
@@ -12135,7 +12166,7 @@ var VisibilityController = class extends Destroyable {
|
|
|
12135
12166
|
timestamp: Date.now()
|
|
12136
12167
|
};
|
|
12137
12168
|
this._visibilityChange$.next(changeEvent);
|
|
12138
|
-
logger$
|
|
12169
|
+
logger$27.debug("VisibilityController: visibility changed", {
|
|
12139
12170
|
from: previousState,
|
|
12140
12171
|
to: newState
|
|
12141
12172
|
});
|
|
@@ -12417,7 +12448,7 @@ function isRequesterValidationError(error) {
|
|
|
12417
12448
|
|
|
12418
12449
|
//#endregion
|
|
12419
12450
|
//#region src/managers/AttachManager.ts
|
|
12420
|
-
const logger$
|
|
12451
|
+
const logger$26 = getLogger();
|
|
12421
12452
|
var AttachManager = class {
|
|
12422
12453
|
constructor(storage, deviceController, reconnectCallsTimeout, attachKey, credentialRecovered) {
|
|
12423
12454
|
this.storage = storage;
|
|
@@ -12439,7 +12470,7 @@ var AttachManager = class {
|
|
|
12439
12470
|
try {
|
|
12440
12471
|
return await this.storage.getItem(this.attachKey) ?? {};
|
|
12441
12472
|
} catch (error) {
|
|
12442
|
-
logger$
|
|
12473
|
+
logger$26.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
|
|
12443
12474
|
return {};
|
|
12444
12475
|
}
|
|
12445
12476
|
}
|
|
@@ -12447,7 +12478,7 @@ var AttachManager = class {
|
|
|
12447
12478
|
try {
|
|
12448
12479
|
await this.storage.setItem(this.attachKey, attached);
|
|
12449
12480
|
} catch (error) {
|
|
12450
|
-
logger$
|
|
12481
|
+
logger$26.warn("[AttachManager] Failed to write attached calls to storage", error);
|
|
12451
12482
|
}
|
|
12452
12483
|
}
|
|
12453
12484
|
/**
|
|
@@ -12466,7 +12497,7 @@ var AttachManager = class {
|
|
|
12466
12497
|
}
|
|
12467
12498
|
async attach(call) {
|
|
12468
12499
|
if (!call.to) {
|
|
12469
|
-
logger$
|
|
12500
|
+
logger$26.warn("[AttachManager] Skip attach for calls with no destination");
|
|
12470
12501
|
return;
|
|
12471
12502
|
}
|
|
12472
12503
|
const destination = call.to;
|
|
@@ -12526,25 +12557,25 @@ var AttachManager = class {
|
|
|
12526
12557
|
callId,
|
|
12527
12558
|
...options
|
|
12528
12559
|
});
|
|
12529
|
-
logger$
|
|
12560
|
+
logger$26.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
|
|
12530
12561
|
succeeded = true;
|
|
12531
12562
|
break;
|
|
12532
12563
|
} catch (error) {
|
|
12533
|
-
logger$
|
|
12564
|
+
logger$26.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
|
|
12534
12565
|
if (isRequesterValidationError(error)) {
|
|
12535
12566
|
refusedOnCredentials = true;
|
|
12536
|
-
logger$
|
|
12567
|
+
logger$26.warn(`[AttachManager] Reattach of ${callId} was refused on credentials; not retrying.`);
|
|
12537
12568
|
break;
|
|
12538
12569
|
}
|
|
12539
12570
|
if (attempt < 3) await new Promise((r) => setTimeout(r, (attempt + 1) * 1e3));
|
|
12540
12571
|
}
|
|
12541
12572
|
if (!succeeded) if (this.credentialRecovered() && !refusedOnCredentials) {
|
|
12542
|
-
logger$
|
|
12573
|
+
logger$26.warn(`[AttachManager] Reattach of ${callId} was denied after a verified reauthentication, removing reference`);
|
|
12543
12574
|
await this.detach({
|
|
12544
12575
|
id: callId,
|
|
12545
12576
|
mediaDirections: attachment.mediaDirections
|
|
12546
12577
|
});
|
|
12547
|
-
} else logger$
|
|
12578
|
+
} else logger$26.warn(`[AttachManager] Reattach failed for call ${callId}; keeping the reference (credential refused or never proven good)`);
|
|
12548
12579
|
}
|
|
12549
12580
|
}
|
|
12550
12581
|
/**
|
|
@@ -12645,12 +12676,12 @@ var require_race = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
12645
12676
|
exports.race = void 0;
|
|
12646
12677
|
var argsOrArgArray_1 = require_argsOrArgArray();
|
|
12647
12678
|
var raceWith_1$1 = require_raceWith();
|
|
12648
|
-
function race$
|
|
12679
|
+
function race$3() {
|
|
12649
12680
|
var args = [];
|
|
12650
12681
|
for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
|
|
12651
12682
|
return raceWith_1$1.raceWith.apply(void 0, __spreadArray([], __read(argsOrArgArray_1.argsOrArgArray(args))));
|
|
12652
12683
|
}
|
|
12653
|
-
exports.race = race$
|
|
12684
|
+
exports.race = race$3;
|
|
12654
12685
|
}));
|
|
12655
12686
|
|
|
12656
12687
|
//#endregion
|
|
@@ -13710,7 +13741,7 @@ function toggleHandraiseMethod(is) {
|
|
|
13710
13741
|
|
|
13711
13742
|
//#endregion
|
|
13712
13743
|
//#region src/core/entities/Participant.ts
|
|
13713
|
-
const logger$
|
|
13744
|
+
const logger$25 = getLogger();
|
|
13714
13745
|
const initialState = {};
|
|
13715
13746
|
/**
|
|
13716
13747
|
* Represents a participant in a call.
|
|
@@ -14184,6 +14215,9 @@ var SelfParticipant = class extends Participant {
|
|
|
14184
14215
|
*
|
|
14185
14216
|
* The call is unaffected when acquisition fails.
|
|
14186
14217
|
*
|
|
14218
|
+
* Known issue: stopping the share before its leg connects can leave this
|
|
14219
|
+
* promise pending until the call ends, so do not gate UI on it.
|
|
14220
|
+
*
|
|
14187
14221
|
* @throws The raw `getDisplayMedia` error. A dismissed picker or a
|
|
14188
14222
|
* permission denial rejects with a `NotAllowedError` `DOMException` —
|
|
14189
14223
|
* inspect `error.name` to tell benign cancels apart from real failures.
|
|
@@ -14192,7 +14226,7 @@ var SelfParticipant = class extends Participant {
|
|
|
14192
14226
|
try {
|
|
14193
14227
|
await this.vertoManager.addScreenMedia();
|
|
14194
14228
|
} catch (error) {
|
|
14195
|
-
logger$
|
|
14229
|
+
logger$25.error("[Participant.startScreenShare] Screen share error:", error);
|
|
14196
14230
|
throw error;
|
|
14197
14231
|
}
|
|
14198
14232
|
}
|
|
@@ -14213,14 +14247,18 @@ var SelfParticipant = class extends Participant {
|
|
|
14213
14247
|
*
|
|
14214
14248
|
* The call is unaffected when acquisition fails.
|
|
14215
14249
|
*
|
|
14250
|
+
* Known issue: removing the device before its leg connects can leave this
|
|
14251
|
+
* promise pending until the call ends, so do not gate UI on it.
|
|
14252
|
+
*
|
|
14216
14253
|
* @throws The raw `getUserMedia` error (e.g. `NotAllowedError` on
|
|
14217
|
-
* permission denial) — inspect `error.name` to decide how to react
|
|
14254
|
+
* permission denial) — inspect `error.name` to decide how to react — or
|
|
14255
|
+
* `AuxiliaryLegTimeoutError` if the leg does not connect in time.
|
|
14218
14256
|
*/
|
|
14219
14257
|
async addAdditionalDevice(options) {
|
|
14220
14258
|
try {
|
|
14221
14259
|
await this.vertoManager.addInputDevice(options);
|
|
14222
14260
|
} catch (error) {
|
|
14223
|
-
logger$
|
|
14261
|
+
logger$25.error("[Participant.addAdditionalDevice] Additional device error:", error);
|
|
14224
14262
|
throw error;
|
|
14225
14263
|
}
|
|
14226
14264
|
}
|
|
@@ -14283,7 +14321,7 @@ var SelfParticipant = class extends Participant {
|
|
|
14283
14321
|
*/
|
|
14284
14322
|
exitStudioModeIfActive() {
|
|
14285
14323
|
if (this._studioAudio$.value) {
|
|
14286
|
-
logger$
|
|
14324
|
+
logger$25.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
|
|
14287
14325
|
this._studioAudio$.next(false);
|
|
14288
14326
|
}
|
|
14289
14327
|
}
|
|
@@ -14307,7 +14345,7 @@ var SelfParticipant = class extends Participant {
|
|
|
14307
14345
|
try {
|
|
14308
14346
|
await super.mute();
|
|
14309
14347
|
} catch (error) {
|
|
14310
|
-
logger$
|
|
14348
|
+
logger$25.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
|
|
14311
14349
|
} finally {
|
|
14312
14350
|
this.vertoManager.muteMainAudioInputDevice();
|
|
14313
14351
|
}
|
|
@@ -14317,7 +14355,7 @@ var SelfParticipant = class extends Participant {
|
|
|
14317
14355
|
try {
|
|
14318
14356
|
await super.unmute();
|
|
14319
14357
|
} catch (error) {
|
|
14320
|
-
logger$
|
|
14358
|
+
logger$25.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
|
|
14321
14359
|
} finally {
|
|
14322
14360
|
await this.vertoManager.unmuteMainAudioInputDevice();
|
|
14323
14361
|
}
|
|
@@ -14327,7 +14365,7 @@ var SelfParticipant = class extends Participant {
|
|
|
14327
14365
|
try {
|
|
14328
14366
|
await super.muteVideo();
|
|
14329
14367
|
} catch (error) {
|
|
14330
|
-
logger$
|
|
14368
|
+
logger$25.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
|
|
14331
14369
|
} finally {
|
|
14332
14370
|
this.vertoManager.muteMainVideoInputDevice();
|
|
14333
14371
|
}
|
|
@@ -14337,7 +14375,7 @@ var SelfParticipant = class extends Participant {
|
|
|
14337
14375
|
try {
|
|
14338
14376
|
await super.unmuteVideo();
|
|
14339
14377
|
} catch (error) {
|
|
14340
|
-
logger$
|
|
14378
|
+
logger$25.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
|
|
14341
14379
|
} finally {
|
|
14342
14380
|
await this.vertoManager.unmuteMainVideoInputDevice();
|
|
14343
14381
|
}
|
|
@@ -14542,7 +14580,7 @@ function filterAs(predicate, resultPath) {
|
|
|
14542
14580
|
//#endregion
|
|
14543
14581
|
//#region src/operators/throwOnRPCError.ts
|
|
14544
14582
|
var import_cjs$21 = require_cjs();
|
|
14545
|
-
const logger$
|
|
14583
|
+
const logger$24 = getLogger();
|
|
14546
14584
|
/**
|
|
14547
14585
|
* RxJS operator that throws a {@link JSONRPCError} when the RPC response contains an error.
|
|
14548
14586
|
* Passes successful responses through unchanged.
|
|
@@ -14550,14 +14588,14 @@ const logger$23 = getLogger();
|
|
|
14550
14588
|
function throwOnRPCError() {
|
|
14551
14589
|
return (0, import_cjs$21.map)((response) => {
|
|
14552
14590
|
if (response.error) {
|
|
14553
|
-
logger$
|
|
14591
|
+
logger$24.error("[throwOnRPCError] RPC error response:", {
|
|
14554
14592
|
code: response.error.code,
|
|
14555
14593
|
message: response.error.message,
|
|
14556
14594
|
data: response.error.data
|
|
14557
14595
|
});
|
|
14558
14596
|
throw new JSONRPCError(response.error.code, response.error.message, response.error.data);
|
|
14559
14597
|
}
|
|
14560
|
-
logger$
|
|
14598
|
+
logger$24.debug("[throwOnRPCError] RPC successful response:", response);
|
|
14561
14599
|
return response;
|
|
14562
14600
|
});
|
|
14563
14601
|
}
|
|
@@ -14565,7 +14603,7 @@ function throwOnRPCError() {
|
|
|
14565
14603
|
//#endregion
|
|
14566
14604
|
//#region src/managers/CallEventsManager.ts
|
|
14567
14605
|
var import_cjs$20 = require_cjs();
|
|
14568
|
-
const logger$
|
|
14606
|
+
const logger$23 = getLogger();
|
|
14569
14607
|
const initialSessionState = {};
|
|
14570
14608
|
/** @internal */
|
|
14571
14609
|
var CallEventsManager = class extends Destroyable {
|
|
@@ -14669,7 +14707,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14669
14707
|
}
|
|
14670
14708
|
initSubscriptions() {
|
|
14671
14709
|
this.subscribeTo(this.callJoinedEvent$, (callJoinedEvent) => {
|
|
14672
|
-
logger$
|
|
14710
|
+
logger$23.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
|
|
14673
14711
|
callId: callJoinedEvent.call_id,
|
|
14674
14712
|
roomSessionId: callJoinedEvent.room_session_id
|
|
14675
14713
|
});
|
|
@@ -14696,19 +14734,19 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14696
14734
|
if (this._self$.value?.capabilities.setLayout) this.updateLayouts();
|
|
14697
14735
|
});
|
|
14698
14736
|
this.subscribeTo(this.memberUpdates$, (member) => {
|
|
14699
|
-
logger$
|
|
14737
|
+
logger$23.debug("[CallEventsManager] Handling member update event for member ID:", member);
|
|
14700
14738
|
this.upsertParticipant(member);
|
|
14701
14739
|
});
|
|
14702
14740
|
this.subscribeTo(this.webRtcCallSession.memberLeft$, (memberLeftEvent) => {
|
|
14703
|
-
logger$
|
|
14741
|
+
logger$23.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
|
|
14704
14742
|
const participants = { ...this._participants$.value };
|
|
14705
14743
|
if (memberLeftEvent.member.member_id in participants) {
|
|
14706
14744
|
delete participants[memberLeftEvent.member.member_id];
|
|
14707
14745
|
this._participants$.next(participants);
|
|
14708
|
-
} else logger$
|
|
14746
|
+
} else logger$23.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
|
|
14709
14747
|
});
|
|
14710
14748
|
this.subscribeTo(this.webRtcCallSession.callUpdated$, (callUpdatedEvent) => {
|
|
14711
|
-
logger$
|
|
14749
|
+
logger$23.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
|
|
14712
14750
|
const roomSession = callUpdatedEvent.room_session;
|
|
14713
14751
|
this._sessionState$.next({
|
|
14714
14752
|
...this._sessionState$.value,
|
|
@@ -14723,7 +14761,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14723
14761
|
});
|
|
14724
14762
|
});
|
|
14725
14763
|
this.subscribeTo(this.layoutChangedEvent$, (layoutChangedEvent) => {
|
|
14726
|
-
logger$
|
|
14764
|
+
logger$23.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
|
|
14727
14765
|
this._sessionState$.next({
|
|
14728
14766
|
...this._sessionState$.value,
|
|
14729
14767
|
layout_name: layoutChangedEvent.id,
|
|
@@ -14733,10 +14771,10 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14733
14771
|
});
|
|
14734
14772
|
}
|
|
14735
14773
|
updateParticipantPositions(layoutChangedEvent) {
|
|
14736
|
-
if (Object.keys(this._participants$.value).length > 0 && !layoutChangedEvent.layers.some((layer) => !!layer.member_id)) logger$
|
|
14774
|
+
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.");
|
|
14737
14775
|
layoutChangedEvent.layers.filter((layer) => !!layer.member_id).filter((layer) => {
|
|
14738
14776
|
if (!(layer.member_id in this._participants$.value)) {
|
|
14739
|
-
logger$
|
|
14777
|
+
logger$23.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
|
|
14740
14778
|
return false;
|
|
14741
14779
|
}
|
|
14742
14780
|
return true;
|
|
@@ -14756,7 +14794,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14756
14794
|
this.webRtcCallSession.executeMethod(this.selfId, "call.layout.list", {}).then((response) => {
|
|
14757
14795
|
const layouts = response.result?.layouts;
|
|
14758
14796
|
if (!layouts) {
|
|
14759
|
-
logger$
|
|
14797
|
+
logger$23.warn("[CallEventsManager] Layout list response carried no layouts; keeping current layouts");
|
|
14760
14798
|
return;
|
|
14761
14799
|
}
|
|
14762
14800
|
this._sessionState$.next({
|
|
@@ -14764,7 +14802,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14764
14802
|
layouts
|
|
14765
14803
|
});
|
|
14766
14804
|
}).catch((error) => {
|
|
14767
|
-
logger$
|
|
14805
|
+
logger$23.error("[CallEventsManager] Error fetching layouts:", error);
|
|
14768
14806
|
});
|
|
14769
14807
|
}
|
|
14770
14808
|
updateParticipants(members) {
|
|
@@ -14780,7 +14818,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14780
14818
|
}
|
|
14781
14819
|
const participant = this._participants$.value[member.member_id];
|
|
14782
14820
|
const oldValue = participant.value;
|
|
14783
|
-
logger$
|
|
14821
|
+
logger$23.debug("[CallEventsManager] Updating participant:", member.member_id, {
|
|
14784
14822
|
oldValue,
|
|
14785
14823
|
newValue: member
|
|
14786
14824
|
});
|
|
@@ -14793,17 +14831,17 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14793
14831
|
}
|
|
14794
14832
|
get callJoinedEvent$() {
|
|
14795
14833
|
return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$20.filter)(isCallJoinedPayload), (0, import_cjs$20.tap)((event) => {
|
|
14796
|
-
logger$
|
|
14834
|
+
logger$23.debug("[CallEventsManager] Call joined event:", event);
|
|
14797
14835
|
})));
|
|
14798
14836
|
}
|
|
14799
14837
|
get layoutChangedEvent$() {
|
|
14800
14838
|
return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$20.tap)((event) => {
|
|
14801
|
-
logger$
|
|
14839
|
+
logger$23.debug("[CallEventsManager] Layout changed event:", event);
|
|
14802
14840
|
})));
|
|
14803
14841
|
}
|
|
14804
14842
|
get memberUpdates$() {
|
|
14805
14843
|
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) => {
|
|
14806
|
-
logger$
|
|
14844
|
+
logger$23.debug("[CallEventsManager] Member update event:", event);
|
|
14807
14845
|
})));
|
|
14808
14846
|
}
|
|
14809
14847
|
destroy() {
|
|
@@ -14822,6 +14860,111 @@ var CallEventsManager = class extends Destroyable {
|
|
|
14822
14860
|
}
|
|
14823
14861
|
};
|
|
14824
14862
|
|
|
14863
|
+
//#endregion
|
|
14864
|
+
//#region src/controllers/ConstraintFallbackHelper.ts
|
|
14865
|
+
/**
|
|
14866
|
+
* ConstraintFallbackHelper - Provides getUserMedia with automatic constraint
|
|
14867
|
+
* fallback on OverconstrainedError.
|
|
14868
|
+
*
|
|
14869
|
+
* When a specific device ID is requested, the helper tries progressively
|
|
14870
|
+
* looser constraints:
|
|
14871
|
+
* 1. `{ deviceId: { exact: deviceId } }` -- exact match
|
|
14872
|
+
* 2. `{ deviceId: deviceId }` -- preferred (browser may pick another)
|
|
14873
|
+
* 3. `{}` -- no constraint, browser default
|
|
14874
|
+
*
|
|
14875
|
+
* This prevents stale device IDs from blocking call setup.
|
|
14876
|
+
*
|
|
14877
|
+
* @see Section 5.8 and Section 11 of the Implementation Guide
|
|
14878
|
+
*/
|
|
14879
|
+
const logger$22 = getLogger();
|
|
14880
|
+
/**
|
|
14881
|
+
* Attempts getUserMedia with progressively looser constraints.
|
|
14882
|
+
*
|
|
14883
|
+
* The function tries three levels of constraint specificity for the given
|
|
14884
|
+
* device kind. Each level is only attempted if the previous one fails with
|
|
14885
|
+
* an OverconstrainedError. Non-OverconstrainedError failures (e.g.,
|
|
14886
|
+
* NotAllowedError) are thrown immediately without fallback.
|
|
14887
|
+
*
|
|
14888
|
+
* @param mediaDevices - Anything exposing `getUserMedia` (a full
|
|
14889
|
+
* `WebRTCMediaDevices`, or a shim wrapping one)
|
|
14890
|
+
* @param constraints - The full MediaStreamConstraints to use as a base
|
|
14891
|
+
* @param kind - Which track kind to apply fallback to ('audio' | 'video')
|
|
14892
|
+
* @param deviceId - The device ID to try (if undefined, calls getUserMedia as-is)
|
|
14893
|
+
* @returns The stream and the fallback level that succeeded
|
|
14894
|
+
* @throws When all fallback levels fail, or when a non-OverconstrainedError occurs
|
|
14895
|
+
*/
|
|
14896
|
+
async function getUserMediaWithFallback(mediaDevices, constraints, kind, deviceId) {
|
|
14897
|
+
if (!deviceId) return {
|
|
14898
|
+
stream: await mediaDevices.getUserMedia(constraints),
|
|
14899
|
+
fallbackLevel: "default"
|
|
14900
|
+
};
|
|
14901
|
+
const baseConstraints = typeof constraints[kind] === "object" ? constraints[kind] : {};
|
|
14902
|
+
try {
|
|
14903
|
+
const exactConstraints = {
|
|
14904
|
+
...constraints,
|
|
14905
|
+
[kind]: {
|
|
14906
|
+
...baseConstraints,
|
|
14907
|
+
deviceId: { exact: deviceId }
|
|
14908
|
+
}
|
|
14909
|
+
};
|
|
14910
|
+
return {
|
|
14911
|
+
stream: await mediaDevices.getUserMedia(exactConstraints),
|
|
14912
|
+
fallbackLevel: "exact"
|
|
14913
|
+
};
|
|
14914
|
+
} catch (error) {
|
|
14915
|
+
if (!isOverconstrainedError(error)) throw error;
|
|
14916
|
+
logger$22.debug(`[ConstraintFallbackHelper] Exact constraint failed for ${kind}, trying preferred`, { deviceId });
|
|
14917
|
+
}
|
|
14918
|
+
try {
|
|
14919
|
+
const preferredConstraints = {
|
|
14920
|
+
...constraints,
|
|
14921
|
+
[kind]: {
|
|
14922
|
+
...baseConstraints,
|
|
14923
|
+
deviceId
|
|
14924
|
+
}
|
|
14925
|
+
};
|
|
14926
|
+
return {
|
|
14927
|
+
stream: await mediaDevices.getUserMedia(preferredConstraints),
|
|
14928
|
+
fallbackLevel: "preferred"
|
|
14929
|
+
};
|
|
14930
|
+
} catch (error) {
|
|
14931
|
+
if (!isOverconstrainedError(error)) throw error;
|
|
14932
|
+
logger$22.debug(`[ConstraintFallbackHelper] Preferred constraint failed for ${kind}, trying default`, { deviceId });
|
|
14933
|
+
}
|
|
14934
|
+
try {
|
|
14935
|
+
const defaultConstraints = {
|
|
14936
|
+
...constraints,
|
|
14937
|
+
[kind]: { ...baseConstraints }
|
|
14938
|
+
};
|
|
14939
|
+
if (typeof defaultConstraints[kind] === "object") {
|
|
14940
|
+
const { deviceId: _removed, ...rest } = defaultConstraints[kind];
|
|
14941
|
+
defaultConstraints[kind] = rest;
|
|
14942
|
+
}
|
|
14943
|
+
const stream = await mediaDevices.getUserMedia(defaultConstraints);
|
|
14944
|
+
logger$22.warn(`[ConstraintFallbackHelper] Fell back to browser default for ${kind}`, { requestedDeviceId: deviceId });
|
|
14945
|
+
return {
|
|
14946
|
+
stream,
|
|
14947
|
+
fallbackLevel: "default"
|
|
14948
|
+
};
|
|
14949
|
+
} catch (error) {
|
|
14950
|
+
logger$22.error(`[ConstraintFallbackHelper] All fallback levels exhausted for ${kind}`, {
|
|
14951
|
+
deviceId,
|
|
14952
|
+
error
|
|
14953
|
+
});
|
|
14954
|
+
throw error;
|
|
14955
|
+
}
|
|
14956
|
+
}
|
|
14957
|
+
/**
|
|
14958
|
+
* Checks whether an error is an OverconstrainedError.
|
|
14959
|
+
*
|
|
14960
|
+
* Browsers may throw either a native OverconstrainedError or a DOMException
|
|
14961
|
+
* with a specific name.
|
|
14962
|
+
*/
|
|
14963
|
+
function isOverconstrainedError(error) {
|
|
14964
|
+
if (error instanceof Error) return error.name === "OverconstrainedError" || error.name === "ConstraintNotSatisfiedError";
|
|
14965
|
+
return false;
|
|
14966
|
+
}
|
|
14967
|
+
|
|
14825
14968
|
//#endregion
|
|
14826
14969
|
//#region src/helpers/SDPHelper.ts
|
|
14827
14970
|
/**
|
|
@@ -15380,6 +15523,7 @@ var LocalStreamController = class extends Destroyable {
|
|
|
15380
15523
|
this._localAudioTracks$ = this.createBehaviorSubject([]);
|
|
15381
15524
|
this._localVideoTracks$ = this.createBehaviorSubject([]);
|
|
15382
15525
|
this._mediaTrackEnded$ = this.createSubject();
|
|
15526
|
+
this._trackOrigins = /* @__PURE__ */ new WeakMap();
|
|
15383
15527
|
}
|
|
15384
15528
|
get localStream$() {
|
|
15385
15529
|
return this._localStream$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
|
|
@@ -15402,6 +15546,22 @@ var LocalStreamController = class extends Destroyable {
|
|
|
15402
15546
|
get localVideoTracks() {
|
|
15403
15547
|
return this._localVideoTracks$.value;
|
|
15404
15548
|
}
|
|
15549
|
+
tagTracks(tracks, origin) {
|
|
15550
|
+
for (const track of tracks) this._trackOrigins.set(track, origin);
|
|
15551
|
+
}
|
|
15552
|
+
setTrackOrigin(track, origin) {
|
|
15553
|
+
this._trackOrigins.set(track, origin);
|
|
15554
|
+
}
|
|
15555
|
+
getTrackOrigin(track) {
|
|
15556
|
+
return this._trackOrigins.get(track);
|
|
15557
|
+
}
|
|
15558
|
+
/**
|
|
15559
|
+
* Fail-safe: an unrecorded track reads as not-a-device-capture, so a missed
|
|
15560
|
+
* tagging site leaves media alone rather than destroying it.
|
|
15561
|
+
*/
|
|
15562
|
+
isDeviceCapture(track) {
|
|
15563
|
+
return this._trackOrigins.get(track) === "device";
|
|
15564
|
+
}
|
|
15405
15565
|
/**
|
|
15406
15566
|
* Build the local media stream based on the provided options.
|
|
15407
15567
|
*/
|
|
@@ -15411,6 +15571,7 @@ var LocalStreamController = class extends Destroyable {
|
|
|
15411
15571
|
if (this.options.inputAudioStream ?? this.options.inputVideoStream) {
|
|
15412
15572
|
const tracks = [...this.options.inputAudioStream?.getTracks() ?? [], ...this.options.inputVideoStream?.getTracks() ?? []];
|
|
15413
15573
|
stream = new MediaStream(tracks);
|
|
15574
|
+
this.tagTracks(tracks, "application");
|
|
15414
15575
|
} else if (this.options.propose === "screenshare") {
|
|
15415
15576
|
logger$19.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
|
|
15416
15577
|
stream = await this.options.getDisplayMedia({
|
|
@@ -15418,6 +15579,7 @@ var LocalStreamController = class extends Destroyable {
|
|
|
15418
15579
|
audio: Boolean(this.options.inputAudioDeviceConstraints)
|
|
15419
15580
|
});
|
|
15420
15581
|
logger$19.debug("[LocalStreamController] Screen share media obtained:", stream);
|
|
15582
|
+
this.tagTracks(stream.getTracks(), "display");
|
|
15421
15583
|
} else {
|
|
15422
15584
|
const constraints = {
|
|
15423
15585
|
audio: this.options.inputAudioDeviceConstraints,
|
|
@@ -15426,6 +15588,7 @@ var LocalStreamController = class extends Destroyable {
|
|
|
15426
15588
|
logger$19.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
|
|
15427
15589
|
stream = await this.options.getUserMedia(constraints);
|
|
15428
15590
|
logger$19.debug("[LocalStreamController] User media obtained:", stream);
|
|
15591
|
+
this.tagTracks(stream.getTracks(), "device");
|
|
15429
15592
|
}
|
|
15430
15593
|
this._localStream$.next(stream);
|
|
15431
15594
|
this._localAudioTracks$.next(stream.getAudioTracks());
|
|
@@ -15435,10 +15598,13 @@ var LocalStreamController = class extends Destroyable {
|
|
|
15435
15598
|
/**
|
|
15436
15599
|
* Add a local media track to the local stream.
|
|
15437
15600
|
* @param track - The MediaStreamTrack to add
|
|
15601
|
+
* @param origin - Defaults to `'device'`; every internal caller passes a
|
|
15602
|
+
* fresh `getUserMedia` capture.
|
|
15438
15603
|
* @returns The MediaStream (either existing or newly created)
|
|
15439
15604
|
*/
|
|
15440
|
-
addTrack(track) {
|
|
15605
|
+
addTrack(track, origin = "device") {
|
|
15441
15606
|
const localStream = this._localStream$.value ?? new MediaStream();
|
|
15607
|
+
this._trackOrigins.set(track, origin);
|
|
15442
15608
|
track.addEventListener("ended", this.mediaTrackEndedHandler);
|
|
15443
15609
|
localStream.addTrack(track);
|
|
15444
15610
|
this._localStream$.next(localStream);
|
|
@@ -15708,15 +15874,27 @@ var TransceiverController = class extends Destroyable {
|
|
|
15708
15874
|
for (let i = 0; i < Number(msStreamsNumber); i++) this.peerConnection.addTransceiver("video", { direction: "recvonly" });
|
|
15709
15875
|
}
|
|
15710
15876
|
}
|
|
15877
|
+
/**
|
|
15878
|
+
* @returns whether every live sender of the kind took the constraints. A
|
|
15879
|
+
* skipped non-device sender, an exhausted fallback, and having no live sender
|
|
15880
|
+
* at all all report `false` — `mediaParamsUpdated.applied` is built from this,
|
|
15881
|
+
* and an application told `true` cannot tell a working push from a no-op.
|
|
15882
|
+
*/
|
|
15711
15883
|
async updateSendersConstraints(kind, constraints) {
|
|
15712
15884
|
if (!constraints) {
|
|
15713
15885
|
this.stopTrackSender(kind);
|
|
15714
|
-
return
|
|
15886
|
+
return false;
|
|
15715
15887
|
}
|
|
15716
15888
|
const senders = this.peerConnection.getSenders().filter((sender) => sender.track?.kind === kind && sender.track.readyState === "live");
|
|
15889
|
+
let applied = senders.length > 0;
|
|
15717
15890
|
for (const sender of senders) {
|
|
15718
15891
|
const { track } = sender;
|
|
15719
15892
|
if (track) {
|
|
15893
|
+
if (!this.options.localStreamController.isDeviceCapture(track)) {
|
|
15894
|
+
logger$18.debug(`[TransceiverController] Skipping ${kind} constraints for a non-device track (origin: ${this.options.localStreamController.getTrackOrigin(track) ?? "unrecorded"}), track ${track.id}`);
|
|
15895
|
+
applied = false;
|
|
15896
|
+
continue;
|
|
15897
|
+
}
|
|
15720
15898
|
const constraintsToApply = {
|
|
15721
15899
|
...track.getConstraints(),
|
|
15722
15900
|
...constraints
|
|
@@ -15732,33 +15910,39 @@ var TransceiverController = class extends Destroyable {
|
|
|
15732
15910
|
} catch (fallbackError) {
|
|
15733
15911
|
logger$18.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
|
|
15734
15912
|
this.options.onError?.(new MediaTrackError("updateSendersConstraints", kind, fallbackError));
|
|
15913
|
+
applied = false;
|
|
15735
15914
|
}
|
|
15736
15915
|
}
|
|
15737
15916
|
}
|
|
15738
15917
|
}
|
|
15918
|
+
return applied;
|
|
15739
15919
|
}
|
|
15740
15920
|
/**
|
|
15741
|
-
* Fallback when applyConstraints fails
|
|
15742
|
-
* one via getUserMedia with the merged constraints (preserving the current
|
|
15743
|
-
* deviceId), replace the sender track, and update the localStream.
|
|
15921
|
+
* Fallback when applyConstraints fails, which on iOS Safari it silently does.
|
|
15744
15922
|
*
|
|
15745
|
-
*
|
|
15746
|
-
*
|
|
15923
|
+
* Order matters: acquiring before stopping means a failed acquisition leaves
|
|
15924
|
+
* the existing media playing. The deviceId goes through the fallback ladder
|
|
15925
|
+
* rather than pinned `{ exact }`, so a stale id degrades instead of failing.
|
|
15747
15926
|
*/
|
|
15748
15927
|
async replaceTrackFallback(sender, oldTrack, kind, mergedConstraints) {
|
|
15749
15928
|
const { deviceId } = oldTrack.getSettings();
|
|
15750
|
-
const
|
|
15751
|
-
|
|
15752
|
-
|
|
15753
|
-
|
|
15754
|
-
|
|
15929
|
+
const { stream, fallbackLevel } = await getUserMediaWithFallback({ getUserMedia: this.options.getUserMedia }, { [kind]: mergedConstraints }, kind, deviceId);
|
|
15930
|
+
const newTrack = stream.getTracks().find((t) => t.kind === kind);
|
|
15931
|
+
if (!newTrack) {
|
|
15932
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
15933
|
+
throw new MediaTrackError("replaceTrackFallback", kind, /* @__PURE__ */ new Error("getUserMedia returned no track of the requested kind"));
|
|
15934
|
+
}
|
|
15935
|
+
try {
|
|
15936
|
+
await sender.replaceTrack(newTrack);
|
|
15937
|
+
} catch (error) {
|
|
15938
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
15939
|
+
throw error;
|
|
15940
|
+
}
|
|
15941
|
+
const oldTrackId = oldTrack.id;
|
|
15942
|
+
this.options.localStreamController.removeTrack(oldTrackId);
|
|
15755
15943
|
oldTrack.stop();
|
|
15756
|
-
this.options.localStreamController.removeTrack(trackId);
|
|
15757
|
-
const newTrack = (await this.options.getUserMedia({ [kind]: constraintsWithDevice })).getTracks().find((t) => t.kind === kind);
|
|
15758
|
-
if (!newTrack) throw new MediaTrackError("replaceTrackFallback", kind, /* @__PURE__ */ new Error("getUserMedia returned no track of the requested kind"));
|
|
15759
|
-
await sender.replaceTrack(newTrack);
|
|
15760
15944
|
this.options.localStreamController.addTrack(newTrack);
|
|
15761
|
-
logger$18.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
|
|
15945
|
+
logger$18.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind} (deviceId fallback level: ${fallbackLevel}). New track: ${newTrack.id}`);
|
|
15762
15946
|
}
|
|
15763
15947
|
getMediaDirections() {
|
|
15764
15948
|
if (this.peerConnection.connectionState === "connected") return this.peerConnection.getTransceivers().reduce((acc, transceiver) => {
|
|
@@ -15870,6 +16054,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
15870
16054
|
this._iceGatheringState$ = this.createReplaySubject(1);
|
|
15871
16055
|
this._errors$ = this.createReplaySubject(1);
|
|
15872
16056
|
this._iceCandidates$ = this.createReplaySubject(1);
|
|
16057
|
+
this._localMediaSettled$ = this.createReplaySubject(1);
|
|
15873
16058
|
this._initialized$ = this.createReplaySubject(1);
|
|
15874
16059
|
this._remoteDescription$ = this.createReplaySubject(1);
|
|
15875
16060
|
this._remoteStream$ = this.createBehaviorSubject(null);
|
|
@@ -15973,6 +16158,10 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
15973
16158
|
get remoteDescription$() {
|
|
15974
16159
|
return this.cachedObservable("remoteDescription$", () => this._remoteDescription$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
|
|
15975
16160
|
}
|
|
16161
|
+
/** Emits once local media is settled — acquired, or knowingly receive-only. */
|
|
16162
|
+
get localMediaSettled$() {
|
|
16163
|
+
return this.cachedObservable("localMediaSettled$", () => this._localMediaSettled$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
|
|
16164
|
+
}
|
|
15976
16165
|
get localStream$() {
|
|
15977
16166
|
return this.cachedObservable("localStream$", () => this.localStreamController.localStream$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
|
|
15978
16167
|
}
|
|
@@ -16128,7 +16317,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16128
16317
|
this._isNegotiating$.next(true);
|
|
16129
16318
|
await this._setRemoteDescription(this.sdpInit);
|
|
16130
16319
|
} else {
|
|
16131
|
-
await this.setupTrackHandling();
|
|
16320
|
+
if (!await this.setupTrackHandling()) return;
|
|
16132
16321
|
this._initialized$.next(true);
|
|
16133
16322
|
}
|
|
16134
16323
|
} catch (error) {
|
|
@@ -16252,7 +16441,10 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16252
16441
|
inputVideoDeviceConstraints: this.inputVideoDeviceConstraints
|
|
16253
16442
|
});
|
|
16254
16443
|
}
|
|
16255
|
-
await this.setupLocalTracks()
|
|
16444
|
+
if (!await this.setupLocalTracks()) {
|
|
16445
|
+
logger$17.debug("[RTCPeerConnectionController] Inbound answer abandoned; the connection went away.");
|
|
16446
|
+
return;
|
|
16447
|
+
}
|
|
16256
16448
|
const { answerOptions } = this;
|
|
16257
16449
|
logger$17.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
|
|
16258
16450
|
await this.createAnswer(answerOptions);
|
|
@@ -16390,35 +16582,52 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16390
16582
|
}
|
|
16391
16583
|
/**
|
|
16392
16584
|
* Setup track handling for remote tracks.
|
|
16585
|
+
*
|
|
16586
|
+
* @returns `false` when the connection went away while local media was being
|
|
16587
|
+
* acquired — see {@link setupLocalTracks}.
|
|
16393
16588
|
*/
|
|
16394
16589
|
async setupTrackHandling() {
|
|
16395
16590
|
if (!this.peerConnection) throw new DependencyError("RTCPeerConnection is not initialized");
|
|
16396
|
-
await this.setupLocalTracks();
|
|
16591
|
+
if (!await this.setupLocalTracks()) return false;
|
|
16397
16592
|
await this.setupRemoteTracks();
|
|
16593
|
+
return true;
|
|
16398
16594
|
}
|
|
16595
|
+
/**
|
|
16596
|
+
* @returns `false` when the connection was torn down while getUserMedia was
|
|
16597
|
+
* in flight. The acquisition is not cancellable, so the caller must stop
|
|
16598
|
+
* rather than go on to touch a peer connection that is closed or gone.
|
|
16599
|
+
*/
|
|
16399
16600
|
async setupLocalTracks() {
|
|
16400
16601
|
logger$17.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
|
|
16401
16602
|
if (this.hasNoLocalMediaToSend()) {
|
|
16402
16603
|
if (!this.receiveAudio && !this.receiveVideo) throw new InvalidParams("Call requests no media: enable audio/video or receiveAudio/receiveVideo");
|
|
16403
16604
|
logger$17.debug("[RTCPeerConnectionController] No local media requested; negotiating receive-only.");
|
|
16404
16605
|
this.setupReceiveOnlyTransceivers();
|
|
16405
|
-
|
|
16606
|
+
this._localMediaSettled$.next();
|
|
16607
|
+
return true;
|
|
16406
16608
|
}
|
|
16407
16609
|
let localStream;
|
|
16408
16610
|
try {
|
|
16409
16611
|
localStream = this.localStream ?? await this.localStreamController.buildLocalStream();
|
|
16410
16612
|
} catch (error) {
|
|
16411
16613
|
this.handleLocalMediaFailure(error);
|
|
16412
|
-
|
|
16614
|
+
this._localMediaSettled$.next();
|
|
16615
|
+
return true;
|
|
16413
16616
|
}
|
|
16617
|
+
if (!this.peerConnection || this.peerConnection.signalingState === "closed") {
|
|
16618
|
+
logger$17.debug("[RTCPeerConnectionController] Local media arrived after teardown; releasing it.");
|
|
16619
|
+
localStream.getTracks().forEach((track) => track.stop());
|
|
16620
|
+
return false;
|
|
16621
|
+
}
|
|
16622
|
+
this._localMediaSettled$.next();
|
|
16414
16623
|
if (this.transceiverController?.useAddStream ?? false) {
|
|
16415
16624
|
logger$17.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
|
|
16416
|
-
this.peerConnection
|
|
16625
|
+
this.peerConnection.addStream(localStream);
|
|
16417
16626
|
if (!this.isNegotiating) {
|
|
16418
16627
|
logger$17.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
|
|
16419
16628
|
this.negotiationNeeded$.next();
|
|
16420
16629
|
}
|
|
16421
|
-
return;
|
|
16630
|
+
return true;
|
|
16422
16631
|
}
|
|
16423
16632
|
for (const kind of ["audio", "video"]) {
|
|
16424
16633
|
const tracks = (kind === "audio" ? localStream.getAudioTracks() : localStream.getVideoTracks()).map((track, index) => ({
|
|
@@ -16432,10 +16641,11 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16432
16641
|
await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
|
|
16433
16642
|
} else {
|
|
16434
16643
|
logger$17.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
|
|
16435
|
-
this.peerConnection
|
|
16644
|
+
this.peerConnection.addTrack(track, localStream);
|
|
16436
16645
|
}
|
|
16437
16646
|
}
|
|
16438
16647
|
}
|
|
16648
|
+
return true;
|
|
16439
16649
|
}
|
|
16440
16650
|
/** True for a main connection with no local media to send. */
|
|
16441
16651
|
hasNoLocalMediaToSend() {
|
|
@@ -16556,6 +16766,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16556
16766
|
const sender = (this.transceiverController?.audioTransceivers.at(0))?.sender ?? this.peerConnection.getSenders().find((s) => s.track?.kind === "audio");
|
|
16557
16767
|
if (!sender || !raw) return;
|
|
16558
16768
|
try {
|
|
16769
|
+
this.localStreamController.setTrackOrigin(this._localAudioPipeline.outputTrack, "processed");
|
|
16559
16770
|
await sender.replaceTrack(this._localAudioPipeline.outputTrack);
|
|
16560
16771
|
} catch (error) {
|
|
16561
16772
|
logger$17.warn("[RTCPeerConnectionController] Failed to route audio sender through pipeline:", error);
|
|
@@ -16616,37 +16827,62 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
16616
16827
|
for (const existingTrack of existingTracks) this.removeLocalTrack(existingTrack.id);
|
|
16617
16828
|
this.addLocalTrack(track);
|
|
16618
16829
|
}
|
|
16830
|
+
/**
|
|
16831
|
+
* @returns whether the constraints reached the media the leg is sending.
|
|
16832
|
+
*
|
|
16833
|
+
* With the pipeline engaged the audio sender carries the processed
|
|
16834
|
+
* destination track, so the sender scan would find nothing it may touch and
|
|
16835
|
+
* every audio constraint API would silently no-op. The constraints belong to
|
|
16836
|
+
* the pipeline's device source, which is the capture that sender ultimately
|
|
16837
|
+
* carries.
|
|
16838
|
+
*/
|
|
16619
16839
|
async updateSendersConstraints(kind, constraints) {
|
|
16620
|
-
|
|
16840
|
+
if (kind === "audio" && constraints && this._localAudioPipeline) return this.applyPipelineSourceConstraints(constraints);
|
|
16841
|
+
return await this.transceiverController?.updateSendersConstraints(kind, constraints) ?? false;
|
|
16621
16842
|
}
|
|
16622
16843
|
/**
|
|
16623
|
-
*
|
|
16624
|
-
*
|
|
16625
|
-
*
|
|
16626
|
-
*
|
|
16844
|
+
* Mirror of the sender path for a piped audio leg: same merge, same fallback
|
|
16845
|
+
* ladder, same device-capture invariant — but the swap target is the pipeline
|
|
16846
|
+
* input, so the sender keeps emitting the pipeline's output track and its
|
|
16847
|
+
* identity survives the change.
|
|
16627
16848
|
*/
|
|
16628
|
-
async
|
|
16629
|
-
const
|
|
16630
|
-
|
|
16631
|
-
|
|
16632
|
-
|
|
16849
|
+
async applyPipelineSourceConstraints(constraints) {
|
|
16850
|
+
const pipeline = this._localAudioPipeline;
|
|
16851
|
+
const source = this.localStreamController.localAudioTracks.at(0);
|
|
16852
|
+
if (!pipeline || !source) {
|
|
16853
|
+
logger$17.debug("[RTCPeerConnectionController] No pipeline input to constrain.");
|
|
16854
|
+
return false;
|
|
16633
16855
|
}
|
|
16634
|
-
|
|
16635
|
-
|
|
16636
|
-
|
|
16637
|
-
|
|
16638
|
-
|
|
16639
|
-
|
|
16640
|
-
|
|
16641
|
-
|
|
16642
|
-
|
|
16643
|
-
|
|
16644
|
-
|
|
16645
|
-
|
|
16646
|
-
|
|
16647
|
-
|
|
16856
|
+
if (!this.localStreamController.isDeviceCapture(source)) {
|
|
16857
|
+
logger$17.debug(`[RTCPeerConnectionController] Skipping audio constraints for a non-device pipeline input (origin: ${this.localStreamController.getTrackOrigin(source) ?? "unrecorded"}).`);
|
|
16858
|
+
return false;
|
|
16859
|
+
}
|
|
16860
|
+
const merged = {
|
|
16861
|
+
...source.getConstraints(),
|
|
16862
|
+
...constraints
|
|
16863
|
+
};
|
|
16864
|
+
try {
|
|
16865
|
+
await source.applyConstraints(merged);
|
|
16866
|
+
logger$17.debug("[RTCPeerConnectionController] Pipeline input constraints updated:", merged);
|
|
16867
|
+
return true;
|
|
16868
|
+
} catch (error) {
|
|
16869
|
+
logger$17.warn("[RTCPeerConnectionController] applyConstraints failed on the pipeline input, re-acquiring:", error);
|
|
16870
|
+
}
|
|
16871
|
+
try {
|
|
16872
|
+
const { stream } = await getUserMediaWithFallback({ getUserMedia: async (c) => this.getUserMedia(c) }, { audio: merged }, "audio", source.getSettings().deviceId);
|
|
16873
|
+
const newTrack = stream.getAudioTracks().at(0);
|
|
16874
|
+
if (!newTrack) {
|
|
16875
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
16876
|
+
throw new Error("getUserMedia returned no audio track");
|
|
16877
|
+
}
|
|
16878
|
+
this.localStreamController.removeTrack(source.id);
|
|
16648
16879
|
this.localStreamController.addTrack(newTrack);
|
|
16649
|
-
|
|
16880
|
+
pipeline.setInputTrack(newTrack);
|
|
16881
|
+
return true;
|
|
16882
|
+
} catch (error) {
|
|
16883
|
+
logger$17.warn("[RTCPeerConnectionController] Failed to re-acquire the pipeline input for constraints:", error);
|
|
16884
|
+
this._errors$.next(new MediaTrackError("updateSendersConstraints", "audio", error));
|
|
16885
|
+
return false;
|
|
16650
16886
|
}
|
|
16651
16887
|
}
|
|
16652
16888
|
/**
|
|
@@ -16830,7 +17066,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
16830
17066
|
this._signalingStatus$ = this.createReplaySubject(1);
|
|
16831
17067
|
this._screenShareStatus$ = this.createBehaviorSubject("none");
|
|
16832
17068
|
this._rtcPeerConnectionsMap = /* @__PURE__ */ new Map();
|
|
16833
|
-
this.
|
|
17069
|
+
this._legErrors$ = this.createSubject();
|
|
16834
17070
|
this._nodeId$ = this.createBehaviorSubject(options.nodeId ?? null);
|
|
16835
17071
|
this.onError = options.onError;
|
|
16836
17072
|
this.onModifyFailed = options.onModifyFailed;
|
|
@@ -16878,6 +17114,10 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
16878
17114
|
get selfId$() {
|
|
16879
17115
|
return this._selfId$.asObservable();
|
|
16880
17116
|
}
|
|
17117
|
+
/** Separates the media phase of call creation from the signalling phase. */
|
|
17118
|
+
get localMediaSettled$() {
|
|
17119
|
+
return this.mainPeerConnection.localMediaSettled$;
|
|
17120
|
+
}
|
|
16881
17121
|
get localStream() {
|
|
16882
17122
|
return this._rtcPeerConnectionsMap.get(this.webRtcCallSession.id)?.localStream ?? null;
|
|
16883
17123
|
}
|
|
@@ -16934,20 +17174,11 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
16934
17174
|
const { mediaParams, callID } = event;
|
|
16935
17175
|
const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);
|
|
16936
17176
|
const { audio, video } = mediaParams;
|
|
16937
|
-
|
|
16938
|
-
|
|
16939
|
-
|
|
16940
|
-
|
|
16941
|
-
|
|
16942
|
-
audio,
|
|
16943
|
-
video,
|
|
16944
|
-
timestamp: Date.now()
|
|
16945
|
-
});
|
|
16946
|
-
} catch (error) {
|
|
16947
|
-
logger$16.warn("[WebRTCManager] Error applying server-pushed media params:", error);
|
|
16948
|
-
this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
|
|
16949
|
-
}
|
|
16950
|
-
})();
|
|
17177
|
+
if (!rtcPeerConnController) {
|
|
17178
|
+
logger$16.warn(`[WebRTCManager] Ignoring server-pushed media params for unknown leg ${callID}`);
|
|
17179
|
+
return;
|
|
17180
|
+
}
|
|
17181
|
+
this.applyServerMediaParams(rtcPeerConnController, audio, video);
|
|
16951
17182
|
});
|
|
16952
17183
|
this.subscribeTo(this.vertoPing$, (vertoPing) => {
|
|
16953
17184
|
this.attachManager.attach(this.buildAttachableCall());
|
|
@@ -16955,6 +17186,75 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
16955
17186
|
});
|
|
16956
17187
|
}
|
|
16957
17188
|
/**
|
|
17189
|
+
* An auxiliary-leg failure must never destroy the call; main-leg and
|
|
17190
|
+
* call-level errors keep `CallFactory.isFatalError`'s classification.
|
|
17191
|
+
*
|
|
17192
|
+
* Every site holding a peer connection reports through here, so the invariant
|
|
17193
|
+
* is structural rather than per-call-site — which is how cloud-product#20523
|
|
17194
|
+
* happened, with only one of fourteen sites passing `{ fatal: false }`.
|
|
17195
|
+
*
|
|
17196
|
+
* `override` composes rather than replaces: a caller may force non-fatal for a
|
|
17197
|
+
* reason of its own (a `verto.info` frame is best-effort whichever leg carries
|
|
17198
|
+
* it), and an auxiliary leg stays non-fatal regardless.
|
|
17199
|
+
*/
|
|
17200
|
+
reportLegError(error, rtcPeerConnController, override) {
|
|
17201
|
+
const leg = rtcPeerConnController?.propose;
|
|
17202
|
+
const legId = rtcPeerConnController?.id;
|
|
17203
|
+
const auxiliary = Boolean(rtcPeerConnController) && !rtcPeerConnController?.isMainDevice;
|
|
17204
|
+
this.onError?.(error, {
|
|
17205
|
+
...override?.fatal === false || auxiliary ? { fatal: false } : {},
|
|
17206
|
+
...leg ? { leg } : {},
|
|
17207
|
+
...legId ? { legId } : {}
|
|
17208
|
+
});
|
|
17209
|
+
if (legId) this._legErrors$.next({
|
|
17210
|
+
legId,
|
|
17211
|
+
error
|
|
17212
|
+
});
|
|
17213
|
+
}
|
|
17214
|
+
/**
|
|
17215
|
+
* Errors reported for one leg, as a stream that fails with them.
|
|
17216
|
+
*
|
|
17217
|
+
* Signaling failures are reported, never thrown — so nothing that waits on a
|
|
17218
|
+
* leg's progress would otherwise learn of a rejected invite. Merging this in
|
|
17219
|
+
* lets the wait end with the reason the server gave.
|
|
17220
|
+
*/
|
|
17221
|
+
legError$(legId) {
|
|
17222
|
+
return this._legErrors$.pipe((0, import_cjs$15.filter)((report) => report.legId === legId), (0, import_cjs$15.map)((report) => {
|
|
17223
|
+
throw report.error;
|
|
17224
|
+
}));
|
|
17225
|
+
}
|
|
17226
|
+
/**
|
|
17227
|
+
* Audio and video are applied independently so a failure in one cannot
|
|
17228
|
+
* suppress the other, and `mediaParamsUpdated` is emitted whatever happens —
|
|
17229
|
+
* an application should not be starved of the params by a constraint failure.
|
|
17230
|
+
*/
|
|
17231
|
+
async applyServerMediaParams(rtcPeerConnController, audio, video) {
|
|
17232
|
+
const failures = [];
|
|
17233
|
+
let applied = true;
|
|
17234
|
+
if (audio) try {
|
|
17235
|
+
applied = await rtcPeerConnController.updateSendersConstraints("audio", audio) && applied;
|
|
17236
|
+
} catch (error) {
|
|
17237
|
+
applied = false;
|
|
17238
|
+
failures.push(toError(error));
|
|
17239
|
+
}
|
|
17240
|
+
if (video) try {
|
|
17241
|
+
applied = await rtcPeerConnController.updateSendersConstraints("video", video) && applied;
|
|
17242
|
+
} catch (error) {
|
|
17243
|
+
applied = false;
|
|
17244
|
+
failures.push(toError(error));
|
|
17245
|
+
}
|
|
17246
|
+
this.webRtcCallSession.emitMediaParamsUpdated({
|
|
17247
|
+
audio,
|
|
17248
|
+
video,
|
|
17249
|
+
timestamp: Date.now(),
|
|
17250
|
+
applied
|
|
17251
|
+
});
|
|
17252
|
+
for (const failure of failures) {
|
|
17253
|
+
logger$16.warn("[WebRTCManager] Error applying server-pushed media params:", failure);
|
|
17254
|
+
this.reportLegError(failure, rtcPeerConnController, { fatal: false });
|
|
17255
|
+
}
|
|
17256
|
+
}
|
|
17257
|
+
/**
|
|
16958
17258
|
* Set node_id/selfId only when the current value is null.
|
|
16959
17259
|
*
|
|
16960
17260
|
* During reattach, `call.joined` and `verto.answer` events can deliver
|
|
@@ -16992,7 +17292,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
16992
17292
|
if (video) await this.mainPeerConnection.updateSendersConstraints("video", video);
|
|
16993
17293
|
} catch (error) {
|
|
16994
17294
|
logger$16.warn("[WebRTCManager] Error updating media constraints:", error);
|
|
16995
|
-
this.
|
|
17295
|
+
this.reportLegError(toError(error), this.mainPeerConnection);
|
|
16996
17296
|
throw error;
|
|
16997
17297
|
}
|
|
16998
17298
|
}
|
|
@@ -17160,23 +17460,24 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17160
17460
|
return response;
|
|
17161
17461
|
}
|
|
17162
17462
|
async executeVerto(message, optionals = {}) {
|
|
17163
|
-
const
|
|
17463
|
+
const params = {
|
|
17164
17464
|
callID: optionals.callID ?? this.webRtcCallSession.id,
|
|
17165
17465
|
node_id: optionals.node_id ?? this._nodeId$.value ?? "",
|
|
17166
17466
|
message,
|
|
17167
17467
|
subscribe: optionals.subscribe
|
|
17168
|
-
}
|
|
17468
|
+
};
|
|
17469
|
+
const webrtcVertoMessage = WebrtcVerto(params);
|
|
17169
17470
|
const response = await this.webRtcCallSession.execute(webrtcVertoMessage);
|
|
17170
17471
|
const nonFatal = message.method === "verto.info" ? { fatal: false } : void 0;
|
|
17171
17472
|
if (response.error) {
|
|
17172
17473
|
const error = new JSONRPCError(response.error.code, response.error.message, response.error.data);
|
|
17173
|
-
this.
|
|
17474
|
+
this.reportLegError(error, this._rtcPeerConnectionsMap.get(params.callID), nonFatal);
|
|
17174
17475
|
return response;
|
|
17175
17476
|
}
|
|
17176
17477
|
const innerResult = getValueFrom(response, "result.result");
|
|
17177
17478
|
if (innerResult?.error) {
|
|
17178
17479
|
const error = new JSONRPCError(innerResult.error.code, innerResult.error.message, innerResult.error.data);
|
|
17179
|
-
this.
|
|
17480
|
+
this.reportLegError(error, this._rtcPeerConnectionsMap.get(params.callID), nonFatal);
|
|
17180
17481
|
return response;
|
|
17181
17482
|
}
|
|
17182
17483
|
return response;
|
|
@@ -17197,7 +17498,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17197
17498
|
}
|
|
17198
17499
|
} catch (error) {
|
|
17199
17500
|
logger$16.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
|
|
17200
|
-
this.
|
|
17501
|
+
this.reportLegError(toError(error), rtcPeerConnController);
|
|
17201
17502
|
if (vertoMethod === "verto.modify") this.onModifyFailed?.();
|
|
17202
17503
|
}
|
|
17203
17504
|
}
|
|
@@ -17213,7 +17514,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17213
17514
|
} catch (error) {
|
|
17214
17515
|
logger$16.warn("[WebRTCManager] Error processing modify response:", error);
|
|
17215
17516
|
const modifyError = error instanceof Error ? error : new Error(String(error), { cause: error });
|
|
17216
|
-
this.
|
|
17517
|
+
this.reportLegError(modifyError, rtcPeerConnController);
|
|
17217
17518
|
}
|
|
17218
17519
|
}
|
|
17219
17520
|
}
|
|
@@ -17226,7 +17527,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17226
17527
|
status,
|
|
17227
17528
|
signalingError
|
|
17228
17529
|
});
|
|
17229
|
-
this.
|
|
17530
|
+
this.reportLegError(signalingError, null, { fatal: false });
|
|
17230
17531
|
return;
|
|
17231
17532
|
}
|
|
17232
17533
|
if (rtcPeerConnController.isMainDevice) this._signalingStatus$.next(status);
|
|
@@ -17256,7 +17557,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17256
17557
|
} else {
|
|
17257
17558
|
logger$16.error("[WebRTCManager] Verto invite failed:", response);
|
|
17258
17559
|
const inviteError = response.error ? new JSONRPCError(response.error.code, response.error.message, response.error.data) : /* @__PURE__ */ new Error("Verto invite failed: unexpected response");
|
|
17259
|
-
this.
|
|
17560
|
+
this.reportLegError(inviteError, rtcPeerConnController);
|
|
17260
17561
|
}
|
|
17261
17562
|
}
|
|
17262
17563
|
get RTCPeerConnectionConfig() {
|
|
@@ -17295,7 +17596,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17295
17596
|
this._rtcPeerConnectionsMap.set(rtcPeerConnController.id, rtcPeerConnController);
|
|
17296
17597
|
this._rtcPeerConnections$.next(Array.from(this._rtcPeerConnectionsMap.values()));
|
|
17297
17598
|
this.subscribeTo(rtcPeerConnController.errors$, (error) => {
|
|
17298
|
-
this.
|
|
17599
|
+
this.reportLegError(error, rtcPeerConnController);
|
|
17299
17600
|
});
|
|
17300
17601
|
if (options.initOffer) this.handleInboundAnswer(rtcPeerConnController);
|
|
17301
17602
|
}
|
|
@@ -17324,7 +17625,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17324
17625
|
await rtcPeerConnController.acceptInbound(answerOptions);
|
|
17325
17626
|
} catch (error) {
|
|
17326
17627
|
logger$16.error("[WebRTCManager] Error creating inbound answer:", error);
|
|
17327
|
-
this.
|
|
17628
|
+
this.reportLegError(toError(error), rtcPeerConnController);
|
|
17328
17629
|
}
|
|
17329
17630
|
}
|
|
17330
17631
|
}
|
|
@@ -17428,7 +17729,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17428
17729
|
await this.attachManager.attach(this.buildAttachableCall());
|
|
17429
17730
|
} catch (error) {
|
|
17430
17731
|
logger$16.error("[WebRTCManager] Error sending Verto answer:", error);
|
|
17431
|
-
this.
|
|
17732
|
+
this.reportLegError(toError(error), rtcPeerConnectionController);
|
|
17432
17733
|
await rtcPeerConnectionController.updateAnswerStatus({ status: "failed" });
|
|
17433
17734
|
}
|
|
17434
17735
|
}
|
|
@@ -17500,7 +17801,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17500
17801
|
await this.mainPeerConnection.restoreTrackSender(deviceKind);
|
|
17501
17802
|
} else {
|
|
17502
17803
|
const error = new InvalidParams("No valid device to be added");
|
|
17503
|
-
this.
|
|
17804
|
+
this.reportLegError(error, this.mainPeerConnection);
|
|
17504
17805
|
throw error;
|
|
17505
17806
|
}
|
|
17506
17807
|
}
|
|
@@ -17525,21 +17826,30 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17525
17826
|
this._rtcPeerConnections$.next(Array.from(this._rtcPeerConnectionsMap.values()));
|
|
17526
17827
|
this.subscribeTo(rtcPeerConnController.errors$, (error) => {
|
|
17527
17828
|
firstPeerConnectionError ??= error;
|
|
17528
|
-
this.
|
|
17829
|
+
this.reportLegError(error, rtcPeerConnController);
|
|
17529
17830
|
});
|
|
17530
|
-
|
|
17831
|
+
const pc = rtcPeerConnController;
|
|
17832
|
+
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$)));
|
|
17531
17833
|
if (isScreenShare) this._screenShareStatus$.next("started");
|
|
17532
17834
|
logger$16.info(`[WebRTCManager] Additional peer connection connected (${propose}).`);
|
|
17533
17835
|
return rtcPeerConnController.id;
|
|
17534
17836
|
} catch (error) {
|
|
17535
17837
|
logger$16.warn("[WebRTCManager] Error initializing additional peer connection:", error);
|
|
17536
|
-
if (rtcPeerConnController)
|
|
17537
|
-
|
|
17838
|
+
if (rtcPeerConnController) {
|
|
17839
|
+
rtcPeerConnController.destroy();
|
|
17840
|
+
this._rtcPeerConnectionsMap.delete(rtcPeerConnController.id);
|
|
17841
|
+
this._rtcPeerConnections$.next(Array.from(this._rtcPeerConnectionsMap.values()));
|
|
17842
|
+
}
|
|
17843
|
+
if (isScreenShare) {
|
|
17844
|
+
this._screenShareStatus$.next("none");
|
|
17845
|
+
this._screenShareId = void 0;
|
|
17846
|
+
}
|
|
17538
17847
|
if (firstPeerConnectionError) throw firstPeerConnectionError instanceof MediaAccessError && firstPeerConnectionError.originalError instanceof Error ? firstPeerConnectionError.originalError : firstPeerConnectionError;
|
|
17539
17848
|
if (error instanceof import_cjs$15.EmptyError) {
|
|
17540
17849
|
logger$16.debug("[WebRTCManager] Additional peer connection aborted before connecting.");
|
|
17541
17850
|
return;
|
|
17542
17851
|
}
|
|
17852
|
+
if (error instanceof import_cjs$15.TimeoutError) throw new AuxiliaryLegTimeoutError(propose, error);
|
|
17543
17853
|
throw error instanceof Error ? error : new Error(String(error), { cause: error });
|
|
17544
17854
|
}
|
|
17545
17855
|
}
|
|
@@ -17585,7 +17895,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17585
17895
|
await this.executeVerto(VertoBye({
|
|
17586
17896
|
...causeParams,
|
|
17587
17897
|
dialogParams: this.dialogParams(rtcPeerConnController)
|
|
17588
|
-
}));
|
|
17898
|
+
}), { callID: rtcPeerConnController.id });
|
|
17589
17899
|
} catch (error) {
|
|
17590
17900
|
logger$16.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
|
|
17591
17901
|
throw error;
|
|
@@ -18977,13 +19287,13 @@ var WebRTCCall = class extends Destroyable {
|
|
|
18977
19287
|
get bandwidthConstrained$() {
|
|
18978
19288
|
return this.deferEmission(this._bandwidthConstrained$.asObservable());
|
|
18979
19289
|
}
|
|
18980
|
-
/** Observable that emits when server
|
|
19290
|
+
/** Observable that emits when the server pushes media params. */
|
|
18981
19291
|
get mediaParamsUpdated$() {
|
|
18982
19292
|
return this.deferEmission(this._mediaParamsUpdated$.asObservable());
|
|
18983
19293
|
}
|
|
18984
19294
|
/**
|
|
18985
19295
|
* @internal Emit a media params update event.
|
|
18986
|
-
* Called by the VertoManager when server
|
|
19296
|
+
* Called by the VertoManager when the server pushes media params.
|
|
18987
19297
|
*/
|
|
18988
19298
|
emitMediaParamsUpdated(event) {
|
|
18989
19299
|
this._mediaParamsUpdated$.next(event);
|
|
@@ -19191,6 +19501,10 @@ var WebRTCCall = class extends Destroyable {
|
|
|
19191
19501
|
get selfId$() {
|
|
19192
19502
|
return this.vertoManager.selfId$;
|
|
19193
19503
|
}
|
|
19504
|
+
/** @internal Lets call creation bound the media and signalling phases apart. */
|
|
19505
|
+
get localMediaSettled$() {
|
|
19506
|
+
return this.vertoManager.localMediaSettled$;
|
|
19507
|
+
}
|
|
19194
19508
|
/** Local participant's member ID, or `null` if not joined. */
|
|
19195
19509
|
get selfId() {
|
|
19196
19510
|
return this.vertoManager.selfId;
|
|
@@ -19586,6 +19900,9 @@ var WebRTCCall = class extends Destroyable {
|
|
|
19586
19900
|
/**
|
|
19587
19901
|
* Infers the semantic error category from a raw Error thrown by VertoManager
|
|
19588
19902
|
* or an RTCPeerConnection layer.
|
|
19903
|
+
*
|
|
19904
|
+
* Pure function — exported for unit testing.
|
|
19905
|
+
* @internal
|
|
19589
19906
|
*/
|
|
19590
19907
|
function inferCallErrorKind(error) {
|
|
19591
19908
|
if (error instanceof RPCTimeoutError) return "timeout";
|
|
@@ -19604,7 +19921,14 @@ const RECOVERABLE_RPC_CODES = new Set([
|
|
|
19604
19921
|
RPC_ERROR_AUTHENTICATION_FAILED,
|
|
19605
19922
|
RPC_ERROR_INVALID_PARAMS
|
|
19606
19923
|
]);
|
|
19607
|
-
/**
|
|
19924
|
+
/**
|
|
19925
|
+
* A *fallback*: callers knowing which leg failed pass an explicit `fatal` and
|
|
19926
|
+
* never reach here, so the default-fatal branch only sees call- and main-leg
|
|
19927
|
+
* errors. Auxiliary legs go through `WebRTCVertoManager.reportLegError`.
|
|
19928
|
+
*
|
|
19929
|
+
* Pure function — exported for unit testing.
|
|
19930
|
+
* @internal
|
|
19931
|
+
*/
|
|
19608
19932
|
function isFatalError(error) {
|
|
19609
19933
|
if (error instanceof VertoPongError) return false;
|
|
19610
19934
|
if (error instanceof MediaTrackError) return false;
|
|
@@ -19639,7 +19963,9 @@ var CallFactory = class {
|
|
|
19639
19963
|
kind: inferCallErrorKind(error),
|
|
19640
19964
|
fatal: options$1?.fatal ?? isFatalError(error),
|
|
19641
19965
|
error,
|
|
19642
|
-
callId: callInstance.id
|
|
19966
|
+
callId: callInstance.id,
|
|
19967
|
+
...options$1?.leg ? { leg: options$1.leg } : {},
|
|
19968
|
+
...options$1?.legId ? { legId: options$1.legId } : {}
|
|
19643
19969
|
};
|
|
19644
19970
|
callInstance.emitError(callError);
|
|
19645
19971
|
},
|
|
@@ -20111,6 +20437,30 @@ const logger$9 = getLogger();
|
|
|
20111
20437
|
function shouldAbortDial(callError) {
|
|
20112
20438
|
return callError.fatal || !(callError.error instanceof MediaAccessError);
|
|
20113
20439
|
}
|
|
20440
|
+
/**
|
|
20441
|
+
* Wait for a dialed call to be ready, or for the failure that stops it.
|
|
20442
|
+
*
|
|
20443
|
+
* Local media acquisition is deliberately unbounded: a permission prompt or a
|
|
20444
|
+
* device picker is human time, and `getUserMedia` cannot be cancelled anyway.
|
|
20445
|
+
* The clock starts only once acquisition settles, so a slow human never spends
|
|
20446
|
+
* the server's budget.
|
|
20447
|
+
*
|
|
20448
|
+
* `merge` rather than `race`, because the two legs settle asymmetrically. A
|
|
20449
|
+
* fatal acquisition failure reports the error and then destroys the call in the
|
|
20450
|
+
* same synchronous step; `errors$` defers delivery by a microtask while
|
|
20451
|
+
* `localMediaSettled$` completes immediately. Under `race` that bare completion
|
|
20452
|
+
* ended the wait first and `dial()` rejected with an RxJS `EmptyError`, burying
|
|
20453
|
+
* the `NotAllowedError` applications are told to inspect. Under `merge` the
|
|
20454
|
+
* completed leg is simply spent, and the queued error — enqueued before the
|
|
20455
|
+
* completion, so delivered before it — arrives to reject the wait. A dial
|
|
20456
|
+
* abandoned with no error at all still ends both legs, and the resulting
|
|
20457
|
+
* `EmptyError` remains the benign-cancel signal.
|
|
20458
|
+
*
|
|
20459
|
+
* Exported for unit testing.
|
|
20460
|
+
*/
|
|
20461
|
+
async function awaitDialReady(session, signalingTimeoutMs) {
|
|
20462
|
+
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)))));
|
|
20463
|
+
}
|
|
20114
20464
|
const getAddressSearchURI = (options) => {
|
|
20115
20465
|
const to = options.to?.split("?")[0];
|
|
20116
20466
|
const from$9 = options.from?.startsWith("subscriber://") ? options.from.replace("subscriber://", "") : options.from;
|
|
@@ -20127,7 +20477,6 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
20127
20477
|
this.authorizationStateKey = authorizationStateKey;
|
|
20128
20478
|
this.attachManager = attachManager;
|
|
20129
20479
|
this.dpopManager = dpopManager;
|
|
20130
|
-
this.callCreateTimeout = 6e3;
|
|
20131
20480
|
this.agent = `signalwire-js/4.0.0`;
|
|
20132
20481
|
this.eventAcks = true;
|
|
20133
20482
|
this.authorizationState$ = this.createReplaySubject(1);
|
|
@@ -20555,7 +20904,7 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
20555
20904
|
to: destinationURI,
|
|
20556
20905
|
...options
|
|
20557
20906
|
});
|
|
20558
|
-
await (
|
|
20907
|
+
await awaitDialReady(callSession, DEFAULT_CALL_SIGNALING_TIMEOUT_MS);
|
|
20559
20908
|
this._calls$.next({
|
|
20560
20909
|
[`${callSession.id}`]: callSession,
|
|
20561
20910
|
...this._calls$.value
|
|
@@ -22733,6 +23082,11 @@ var SignalWire = class extends Destroyable {
|
|
|
22733
23082
|
* Returns a {@link Call} in `'ringing'` state. Subscribe to {@link Call.status$}
|
|
22734
23083
|
* to track progression through `'connected'` → `'disconnected'`.
|
|
22735
23084
|
*
|
|
23085
|
+
* Local media acquisition is deliberately unbounded: an unanswered permission
|
|
23086
|
+
* prompt leaves this promise pending indefinitely, so apply your own bound if
|
|
23087
|
+
* your UI needs one. The 12 s signaling budget starts only once acquisition
|
|
23088
|
+
* settles.
|
|
23089
|
+
*
|
|
22736
23090
|
* @param destination - Address URI string (e.g. `'/public/my-room'`) or {@link Address} instance.
|
|
22737
23091
|
* @param options - Media and dial options (audio/video, device constraints). Overrides defaults.
|
|
22738
23092
|
* @returns The created {@link Call} instance.
|
|
@@ -23212,5 +23566,5 @@ emitReadyEvent();
|
|
|
23212
23566
|
if (typeof process === "undefined") globalThis.process = { env: { NODE_ENV: "production" } };
|
|
23213
23567
|
|
|
23214
23568
|
//#endregion
|
|
23215
|
-
export { Address, CallCreateError, CallNotReadyError, ClientPreferences, CollectionFetchError, DPoPInitError, DeviceTokenError, EmbedTokenCredentialProvider, InvalidCredentialsError, MediaAccessError, MediaTrackError, MessageParseError, OverconstrainedFallbackError, Participant, ParticipantNotReadyError, PreflightError, RecoveryError, SelfCapabilities, SelfParticipant, SignalWire, StaticCredentialProvider, TokenRefreshError, UnexpectedError, User, VertoPongError, WebRTCCall, embeddableCall, getLogger, isSelfParticipant, ready, setDebugOptions, setLogLevel, setLogger, version };
|
|
23569
|
+
export { Address, AuxiliaryLegTimeoutError, CallCreateError, CallNotReadyError, ClientPreferences, CollectionFetchError, DPoPInitError, DeviceTokenError, EmbedTokenCredentialProvider, InvalidCredentialsError, MediaAccessError, MediaTrackError, MessageParseError, OverconstrainedFallbackError, Participant, ParticipantNotReadyError, PreflightError, RecoveryError, SelfCapabilities, SelfParticipant, SignalWire, StaticCredentialProvider, TokenRefreshError, UnexpectedError, User, VertoPongError, WebRTCCall, embeddableCall, getLogger, isSelfParticipant, ready, setDebugOptions, setLogLevel, setLogger, version };
|
|
23216
23570
|
//# sourceMappingURL=browser.mjs.map
|