@signalwire/js 4.0.0-rc.0 → 4.0.0-rc.1
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 +138 -22
- package/dist/browser.mjs.map +1 -1
- package/dist/browser.umd.js +138 -22
- package/dist/browser.umd.js.map +1 -1
- package/dist/index.cjs +138 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +106 -16
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +106 -16
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +138 -22
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser.mjs
CHANGED
|
@@ -13367,7 +13367,9 @@ const DEFAULT_MEMBER_CAPABILITIES = {
|
|
|
13367
13367
|
position: false,
|
|
13368
13368
|
meta: false,
|
|
13369
13369
|
remove: false,
|
|
13370
|
-
audioFlags: false
|
|
13370
|
+
audioFlags: false,
|
|
13371
|
+
denoise: false,
|
|
13372
|
+
lowbitrate: false
|
|
13371
13373
|
};
|
|
13372
13374
|
/**
|
|
13373
13375
|
* Default call capabilities with no permissions
|
|
@@ -13440,7 +13442,9 @@ function computeMemberCapabilities(flags, memberType) {
|
|
|
13440
13442
|
position: hasBooleanCapability(flags, memberType, null, "position"),
|
|
13441
13443
|
meta: hasBooleanCapability(flags, memberType, null, "meta"),
|
|
13442
13444
|
remove: hasBooleanCapability(flags, memberType, null, "remove"),
|
|
13443
|
-
audioFlags: hasBooleanCapability(flags, memberType, null, "audioflags")
|
|
13445
|
+
audioFlags: hasBooleanCapability(flags, memberType, null, "audioflags"),
|
|
13446
|
+
denoise: hasBooleanCapability(flags, memberType, null, "denoise"),
|
|
13447
|
+
lowbitrate: hasBooleanCapability(flags, memberType, null, "lowbitrate")
|
|
13444
13448
|
};
|
|
13445
13449
|
}
|
|
13446
13450
|
/**
|
|
@@ -13823,6 +13827,10 @@ var Participant = class extends Destroyable {
|
|
|
13823
13827
|
get nodeId() {
|
|
13824
13828
|
return this._state$.value.node_id;
|
|
13825
13829
|
}
|
|
13830
|
+
/** Call ID for this participant's leg, or `undefined` if not available. */
|
|
13831
|
+
get callId() {
|
|
13832
|
+
return this._state$.value.call_id;
|
|
13833
|
+
}
|
|
13826
13834
|
/** @internal */
|
|
13827
13835
|
get value() {
|
|
13828
13836
|
return this._state$.value;
|
|
@@ -13884,8 +13892,9 @@ var Participant = class extends Destroyable {
|
|
|
13884
13892
|
noise_suppression: !this.noiseSuppression
|
|
13885
13893
|
});
|
|
13886
13894
|
}
|
|
13895
|
+
/** Toggles low-bitrate mode for this participant's media. */
|
|
13887
13896
|
async toggleLowbitrate() {
|
|
13888
|
-
|
|
13897
|
+
await this.executeMethod(this.id, "call.lowbitrate.set", { lowbitrate: !this.lowbitrate });
|
|
13889
13898
|
}
|
|
13890
13899
|
/**
|
|
13891
13900
|
* Adjusts the **conference-only** microphone energy gate / sensitivity level
|
|
@@ -13932,10 +13941,27 @@ var Participant = class extends Destroyable {
|
|
|
13932
13941
|
}
|
|
13933
13942
|
/**
|
|
13934
13943
|
* Sets the participant's position in the video layout.
|
|
13944
|
+
*
|
|
13945
|
+
* Requires the `member.position` capability. The gateway keys positions by the
|
|
13946
|
+
* **target member's own** `call_id`/`node_id` (see issue #19400 and the legacy
|
|
13947
|
+
* `setPositions` implementation), so this sends the participant's own call
|
|
13948
|
+
* context — matching {@link Participant.remove}. A resolved promise does not
|
|
13949
|
+
* guarantee a visible change: the backend silently returns `200` (no-op) for
|
|
13950
|
+
* non-conference targets.
|
|
13951
|
+
*
|
|
13935
13952
|
* @param value - The {@link VideoPosition} to assign (e.g. `'auto'`, `'reserved-0'`).
|
|
13936
13953
|
*/
|
|
13937
13954
|
async setPosition(value) {
|
|
13938
|
-
|
|
13955
|
+
const state = this._state$.value;
|
|
13956
|
+
const target = {
|
|
13957
|
+
member_id: this.id,
|
|
13958
|
+
call_id: state.call_id ?? "",
|
|
13959
|
+
node_id: state.node_id ?? ""
|
|
13960
|
+
};
|
|
13961
|
+
await this.executeMethod(target, "call.member.position.set", { targets: [{
|
|
13962
|
+
target,
|
|
13963
|
+
position: value
|
|
13964
|
+
}] });
|
|
13939
13965
|
}
|
|
13940
13966
|
/** Removes this participant from the call. */
|
|
13941
13967
|
async remove() {
|
|
@@ -16481,7 +16507,11 @@ function isVertoInviteMessage(value) {
|
|
|
16481
16507
|
const msg = value;
|
|
16482
16508
|
return msg.method === "verto.invite" && isObject(msg.params) && hasProperty(msg.params, "sdp") && hasProperty(msg.params, "callID");
|
|
16483
16509
|
}
|
|
16484
|
-
|
|
16510
|
+
/**
|
|
16511
|
+
* Guards inbound `verto.bye` frames (server → client). Outbound bye messages are
|
|
16512
|
+
* built locally and never type-guarded, so only the inbound shape is asserted.
|
|
16513
|
+
*/
|
|
16514
|
+
function isVertoByeInboundMessage(value) {
|
|
16485
16515
|
if (!isVertoMethodMessage(value)) return false;
|
|
16486
16516
|
return value.method === "verto.bye";
|
|
16487
16517
|
}
|
|
@@ -16501,6 +16531,14 @@ function isVertoMediaParamsInnerParams(value) {
|
|
|
16501
16531
|
function isVertoPingInnerParams(value) {
|
|
16502
16532
|
return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.ping";
|
|
16503
16533
|
}
|
|
16534
|
+
/**
|
|
16535
|
+
* Guards the params of an inbound `verto.bye` frame (top-level `callID`). Use
|
|
16536
|
+
* this to recognise the bye payload extracted by `vertoBye$`, e.g. when racing
|
|
16537
|
+
* it against a boolean answer/reject signal.
|
|
16538
|
+
*/
|
|
16539
|
+
function isVertoByeInboundParamsGuard(value) {
|
|
16540
|
+
return isObject(value) && hasProperty(value, "callID") && typeof value.callID === "string";
|
|
16541
|
+
}
|
|
16504
16542
|
|
|
16505
16543
|
//#endregion
|
|
16506
16544
|
//#region src/managers/VertoManager.ts
|
|
@@ -16839,7 +16877,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
16839
16877
|
return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
|
|
16840
16878
|
}
|
|
16841
16879
|
get vertoBye$() {
|
|
16842
|
-
return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(
|
|
16880
|
+
return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeInboundMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
|
|
16843
16881
|
}
|
|
16844
16882
|
get vertoAttach$() {
|
|
16845
16883
|
return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
|
|
@@ -16992,7 +17030,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
16992
17030
|
logger$16.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
|
|
16993
17031
|
return;
|
|
16994
17032
|
}
|
|
16995
|
-
if (
|
|
17033
|
+
if (isVertoByeInboundParamsGuard(vertoByeOrAccepted)) {
|
|
16996
17034
|
logger$16.info("[WebRTCManager] Inbound call ended by remote before answer.");
|
|
16997
17035
|
this.callSession?.destroy();
|
|
16998
17036
|
} else if (!vertoByeOrAccepted) {
|
|
@@ -17094,7 +17132,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
17094
17132
|
logger$16.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
|
|
17095
17133
|
return;
|
|
17096
17134
|
}
|
|
17097
|
-
if (
|
|
17135
|
+
if (isVertoByeInboundParamsGuard(vertoByeOrAccepted)) {
|
|
17098
17136
|
logger$16.info("[WebRTCManager] Call ended before answer was sent.");
|
|
17099
17137
|
this.callSession?.destroy();
|
|
17100
17138
|
} else if (!vertoByeOrAccepted) {
|
|
@@ -18147,6 +18185,12 @@ function mosToQualityLevel(mos) {
|
|
|
18147
18185
|
var import_cjs$11 = require_cjs();
|
|
18148
18186
|
const logger$12 = getLogger();
|
|
18149
18187
|
/**
|
|
18188
|
+
* Verto method for setting member layout positions. Its gateway DTO requires a
|
|
18189
|
+
* `targets` array whose entries are `{ target, position }` (NOT bare targets),
|
|
18190
|
+
* so {@link WebRTCCall.buildMethodParams} special-cases it. See issue #19400.
|
|
18191
|
+
*/
|
|
18192
|
+
const POSITION_SET_METHOD = "call.member.position.set";
|
|
18193
|
+
/**
|
|
18150
18194
|
* Ratio between the critical and warning RTT spike multipliers.
|
|
18151
18195
|
* Warning threshold = baseline * warningMultiplier (default 3x)
|
|
18152
18196
|
* Critical threshold = baseline * warningMultiplier * RTT_CRITICAL_TO_WARNING_RATIO
|
|
@@ -18353,7 +18397,7 @@ var WebRTCCall = class extends Destroyable {
|
|
|
18353
18397
|
* @throws {JSONRPCError} If the RPC call returns an error.
|
|
18354
18398
|
*/
|
|
18355
18399
|
async executeMethod(target, method, args) {
|
|
18356
|
-
const params = this.buildMethodParams(target, args);
|
|
18400
|
+
const params = this.buildMethodParams(target, args, method);
|
|
18357
18401
|
const request = buildRPCRequest({
|
|
18358
18402
|
method,
|
|
18359
18403
|
params
|
|
@@ -18367,12 +18411,16 @@ var WebRTCCall = class extends Destroyable {
|
|
|
18367
18411
|
throw error;
|
|
18368
18412
|
}
|
|
18369
18413
|
}
|
|
18370
|
-
buildMethodParams(target, args) {
|
|
18414
|
+
buildMethodParams(target, args, method) {
|
|
18371
18415
|
const self = {
|
|
18372
18416
|
node_id: this.nodeId ?? "",
|
|
18373
18417
|
call_id: this.id,
|
|
18374
18418
|
member_id: this.vertoManager.selfId ?? ""
|
|
18375
18419
|
};
|
|
18420
|
+
if (method === POSITION_SET_METHOD) return {
|
|
18421
|
+
...args,
|
|
18422
|
+
self
|
|
18423
|
+
};
|
|
18376
18424
|
if (typeof target === "object") return {
|
|
18377
18425
|
...args,
|
|
18378
18426
|
self,
|
|
@@ -18938,10 +18986,21 @@ var WebRTCCall = class extends Destroyable {
|
|
|
18938
18986
|
return this.deferEmission(this._answered$.asObservable());
|
|
18939
18987
|
}
|
|
18940
18988
|
/**
|
|
18941
|
-
* Sets the call layout and participant positions.
|
|
18989
|
+
* Sets the call layout and, optionally, individual participant positions.
|
|
18990
|
+
*
|
|
18991
|
+
* The gateway `call.layout.set` DTO has **no** `positions` member, so when
|
|
18992
|
+
* `positions` is provided this method issues a `call.member.position.set`
|
|
18993
|
+
* request per member (via {@link Participant.setPosition}, which keys each
|
|
18994
|
+
* position by that member's own call context) alongside `call.layout.set`
|
|
18995
|
+
* (issue #19400, Flag #6).
|
|
18996
|
+
*
|
|
18997
|
+
* **These operations are NOT atomic.** The layout is applied first, then each
|
|
18998
|
+
* member position sequentially, so members may briefly flash into their
|
|
18999
|
+
* default slots before being moved to the requested positions.
|
|
18942
19000
|
*
|
|
18943
19001
|
* @param layout - Layout name (must be one of {@link layouts}).
|
|
18944
|
-
* @param positions -
|
|
19002
|
+
* @param positions - Optional map of member IDs to {@link VideoPosition} values.
|
|
19003
|
+
* When omitted or empty, only the layout is changed.
|
|
18945
19004
|
* @throws {InvalidParams} If the layout is not in the available {@link layouts}.
|
|
18946
19005
|
*
|
|
18947
19006
|
* @example
|
|
@@ -18954,10 +19013,17 @@ var WebRTCCall = class extends Destroyable {
|
|
|
18954
19013
|
async setLayout(layout, positions) {
|
|
18955
19014
|
if (!this.layouts.includes(layout)) throw new InvalidParams(`Layout ${layout} is not available in the current call layouts: ${this.layouts.join(", ")}`);
|
|
18956
19015
|
const selfId = await (0, import_cjs$11.firstValueFrom)(this.selfId$.pipe((0, import_cjs$11.filter)((id) => id !== null)));
|
|
18957
|
-
await this.executeMethod(selfId, "call.layout.set", {
|
|
18958
|
-
|
|
18959
|
-
|
|
18960
|
-
|
|
19016
|
+
await this.executeMethod(selfId, "call.layout.set", { layout });
|
|
19017
|
+
const positionEntries = Object.entries(positions ?? {});
|
|
19018
|
+
if (positionEntries.length === 0) return;
|
|
19019
|
+
for (const [memberId, position] of positionEntries) {
|
|
19020
|
+
const participant = this.participants.find((p) => p.id === memberId);
|
|
19021
|
+
if (!participant) {
|
|
19022
|
+
logger$12.warn(`[Call] setLayout: member ${memberId} not found in participants; skipping position ${position}`);
|
|
19023
|
+
continue;
|
|
19024
|
+
}
|
|
19025
|
+
await participant.setPosition(position);
|
|
19026
|
+
}
|
|
18961
19027
|
}
|
|
18962
19028
|
/**
|
|
18963
19029
|
* Transfers the call to another destination.
|
|
@@ -19876,11 +19942,41 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
19876
19942
|
}
|
|
19877
19943
|
} else this._errors$.next(error);
|
|
19878
19944
|
}
|
|
19945
|
+
/**
|
|
19946
|
+
* Clear the resume state (authorization_state + protocol) only.
|
|
19947
|
+
*
|
|
19948
|
+
* This is the stale-auth-state recovery helper used by handleAuthError:
|
|
19949
|
+
* the server rejected a reconnect, so the resume state is discarded and a
|
|
19950
|
+
* fresh connect follows. Attach records are deliberately preserved — the
|
|
19951
|
+
* session lives on through the reconnect and reattachCalls() needs the
|
|
19952
|
+
* stored call references afterwards. Do NOT add detachAll() here.
|
|
19953
|
+
*
|
|
19954
|
+
* For public teardown (disconnect/destroy), use {@link teardownSessionState}
|
|
19955
|
+
* instead, which clears the attach records as well.
|
|
19956
|
+
*/
|
|
19879
19957
|
async cleanupStoredConnectionParams() {
|
|
19880
19958
|
await this.transport.setProtocol(void 0);
|
|
19881
19959
|
await this.updateAuthorizationStateInStorage(void 0);
|
|
19882
19960
|
this._authorization$.next(void 0);
|
|
19883
19961
|
}
|
|
19962
|
+
/**
|
|
19963
|
+
* Public-teardown helper for disconnect()/destroy(). Clears the resume
|
|
19964
|
+
* state (authorization_state + protocol) AND the attach records as one
|
|
19965
|
+
* atomic unit.
|
|
19966
|
+
*
|
|
19967
|
+
* The two stores are coupled: the backend only honors attach records
|
|
19968
|
+
* within the session identified by the resume state, so ending the
|
|
19969
|
+
* session must clear both. Clearing one without the other strands records
|
|
19970
|
+
* no future session can honor (disconnect) or revives a session the
|
|
19971
|
+
* developer explicitly ended (destroy).
|
|
19972
|
+
*
|
|
19973
|
+
* Distinct from {@link cleanupStoredConnectionParams}, which keeps the
|
|
19974
|
+
* attach records for the stale-auth-state recovery path.
|
|
19975
|
+
*/
|
|
19976
|
+
async teardownSessionState() {
|
|
19977
|
+
await this.cleanupStoredConnectionParams();
|
|
19978
|
+
await this.attachManager.detachAll();
|
|
19979
|
+
}
|
|
19884
19980
|
async updateAuthState(authorization_state) {
|
|
19885
19981
|
try {
|
|
19886
19982
|
await this.storage.setItem(this.authorizationStateKey, authorization_state);
|
|
@@ -19975,7 +20071,7 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
19975
20071
|
async disconnect() {
|
|
19976
20072
|
this.transport.disconnect();
|
|
19977
20073
|
this._authState$.next({ kind: "unauthenticated" });
|
|
19978
|
-
await this.
|
|
20074
|
+
await this.teardownSessionState();
|
|
19979
20075
|
}
|
|
19980
20076
|
async createInboundCall(invite) {
|
|
19981
20077
|
const callSession = await this.createCall({
|
|
@@ -21142,6 +21238,10 @@ var TransportManager = class extends Destroyable {
|
|
|
21142
21238
|
if (!isSignalwireRequest(message)) return true;
|
|
21143
21239
|
const eventChannel = message.params.event_channel;
|
|
21144
21240
|
if (!eventChannel) return true;
|
|
21241
|
+
if (message.params.event_type.startsWith("conversation.")) {
|
|
21242
|
+
logger$2.debug(`[Transport] Received conversation event: ${message.params.event_type} (event_channel: ${eventChannel})`);
|
|
21243
|
+
return true;
|
|
21244
|
+
}
|
|
21145
21245
|
const currentProtocol = this._currentProtocol;
|
|
21146
21246
|
if (!currentProtocol) return true;
|
|
21147
21247
|
if (!eventChannel.includes(currentProtocol)) {
|
|
@@ -21742,7 +21842,7 @@ var SignalWire = class extends Destroyable {
|
|
|
21742
21842
|
logger$1.warn("[SignalWire] Failed to initialize VisibilityController:", error);
|
|
21743
21843
|
}
|
|
21744
21844
|
try {
|
|
21745
|
-
this._diagnosticsCollector = new DiagnosticsCollector({ sdkVersion: "4.0.0-rc.
|
|
21845
|
+
this._diagnosticsCollector = new DiagnosticsCollector({ sdkVersion: "4.0.0-rc.1" });
|
|
21746
21846
|
} catch (error) {
|
|
21747
21847
|
logger$1.warn("[SignalWire] Failed to initialize DiagnosticsCollector:", error);
|
|
21748
21848
|
}
|
|
@@ -21750,6 +21850,14 @@ var SignalWire = class extends Destroyable {
|
|
|
21750
21850
|
/**
|
|
21751
21851
|
* Disconnects the WebSocket and tears down the current session.
|
|
21752
21852
|
*
|
|
21853
|
+
* Ends the session identified by the protocol and clears its persisted
|
|
21854
|
+
* resume state (`authorization_state` + protocol) and attach records
|
|
21855
|
+
* together — a later {@link connect} with the same credentials starts a
|
|
21856
|
+
* fresh session and cannot reattach to the ended session's calls.
|
|
21857
|
+
* Credentials and device preferences are preserved. To temporarily stop
|
|
21858
|
+
* receiving inbound calls while keeping the session alive, use
|
|
21859
|
+
* `unregister()` instead.
|
|
21860
|
+
*
|
|
21753
21861
|
* The client can be reconnected by calling {@link connect} again,
|
|
21754
21862
|
* which creates a fresh transport and session.
|
|
21755
21863
|
*/
|
|
@@ -22141,12 +22249,20 @@ var SignalWire = class extends Destroyable {
|
|
|
22141
22249
|
prefs.preferredVideoInput = null;
|
|
22142
22250
|
await this._deviceController.clearDeviceState();
|
|
22143
22251
|
}
|
|
22144
|
-
/**
|
|
22252
|
+
/**
|
|
22253
|
+
* Destroys the client, clearing timers and releasing all resources.
|
|
22254
|
+
*
|
|
22255
|
+
* Intentionally destroying the client ends its session: the resume state
|
|
22256
|
+
* (`authorization_state` + protocol) and the attach records are both
|
|
22257
|
+
* cleared. Credentials and device preferences are preserved — use
|
|
22258
|
+
* {@link resetToDefaults} for a full wipe. To temporarily stop receiving
|
|
22259
|
+
* inbound calls while keeping the session alive, use `unregister()`.
|
|
22260
|
+
*/
|
|
22145
22261
|
destroy() {
|
|
22146
22262
|
this._refreshCoordinator?.destroy();
|
|
22147
22263
|
this._refreshCoordinator = void 0;
|
|
22148
22264
|
this._dpopManager?.destroy();
|
|
22149
|
-
|
|
22265
|
+
this._clientSession.teardownSessionState();
|
|
22150
22266
|
this._transport.destroy();
|
|
22151
22267
|
this._clientSession.destroy();
|
|
22152
22268
|
try {
|
|
@@ -22261,7 +22377,7 @@ var StaticCredentialProvider = class {
|
|
|
22261
22377
|
/**
|
|
22262
22378
|
* Library version from package.json, injected at build time.
|
|
22263
22379
|
*/
|
|
22264
|
-
const version = "4.0.0-rc.
|
|
22380
|
+
const version = "4.0.0-rc.1";
|
|
22265
22381
|
/**
|
|
22266
22382
|
* Flag indicating the library has been loaded and is ready to use.
|
|
22267
22383
|
* For UMD builds: `window.SignalWire.ready`
|
|
@@ -22283,7 +22399,7 @@ const ready = true;
|
|
|
22283
22399
|
*/
|
|
22284
22400
|
const emitReadyEvent = () => {
|
|
22285
22401
|
if (typeof window !== "undefined") {
|
|
22286
|
-
const event = new CustomEvent("signalwire:js:ready", { detail: { version: "4.0.0-rc.
|
|
22402
|
+
const event = new CustomEvent("signalwire:js:ready", { detail: { version: "4.0.0-rc.1" } });
|
|
22287
22403
|
window.dispatchEvent(event);
|
|
22288
22404
|
}
|
|
22289
22405
|
};
|