@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/index.mjs
CHANGED
|
@@ -3005,7 +3005,9 @@ const DEFAULT_MEMBER_CAPABILITIES = {
|
|
|
3005
3005
|
position: false,
|
|
3006
3006
|
meta: false,
|
|
3007
3007
|
remove: false,
|
|
3008
|
-
audioFlags: false
|
|
3008
|
+
audioFlags: false,
|
|
3009
|
+
denoise: false,
|
|
3010
|
+
lowbitrate: false
|
|
3009
3011
|
};
|
|
3010
3012
|
/**
|
|
3011
3013
|
* Default call capabilities with no permissions
|
|
@@ -3078,7 +3080,9 @@ function computeMemberCapabilities(flags, memberType) {
|
|
|
3078
3080
|
position: hasBooleanCapability(flags, memberType, null, "position"),
|
|
3079
3081
|
meta: hasBooleanCapability(flags, memberType, null, "meta"),
|
|
3080
3082
|
remove: hasBooleanCapability(flags, memberType, null, "remove"),
|
|
3081
|
-
audioFlags: hasBooleanCapability(flags, memberType, null, "audioflags")
|
|
3083
|
+
audioFlags: hasBooleanCapability(flags, memberType, null, "audioflags"),
|
|
3084
|
+
denoise: hasBooleanCapability(flags, memberType, null, "denoise"),
|
|
3085
|
+
lowbitrate: hasBooleanCapability(flags, memberType, null, "lowbitrate")
|
|
3082
3086
|
};
|
|
3083
3087
|
}
|
|
3084
3088
|
/**
|
|
@@ -3459,6 +3463,10 @@ var Participant = class extends Destroyable {
|
|
|
3459
3463
|
get nodeId() {
|
|
3460
3464
|
return this._state$.value.node_id;
|
|
3461
3465
|
}
|
|
3466
|
+
/** Call ID for this participant's leg, or `undefined` if not available. */
|
|
3467
|
+
get callId() {
|
|
3468
|
+
return this._state$.value.call_id;
|
|
3469
|
+
}
|
|
3462
3470
|
/** @internal */
|
|
3463
3471
|
get value() {
|
|
3464
3472
|
return this._state$.value;
|
|
@@ -3520,8 +3528,9 @@ var Participant = class extends Destroyable {
|
|
|
3520
3528
|
noise_suppression: !this.noiseSuppression
|
|
3521
3529
|
});
|
|
3522
3530
|
}
|
|
3531
|
+
/** Toggles low-bitrate mode for this participant's media. */
|
|
3523
3532
|
async toggleLowbitrate() {
|
|
3524
|
-
|
|
3533
|
+
await this.executeMethod(this.id, "call.lowbitrate.set", { lowbitrate: !this.lowbitrate });
|
|
3525
3534
|
}
|
|
3526
3535
|
/**
|
|
3527
3536
|
* Adjusts the **conference-only** microphone energy gate / sensitivity level
|
|
@@ -3568,10 +3577,27 @@ var Participant = class extends Destroyable {
|
|
|
3568
3577
|
}
|
|
3569
3578
|
/**
|
|
3570
3579
|
* Sets the participant's position in the video layout.
|
|
3580
|
+
*
|
|
3581
|
+
* Requires the `member.position` capability. The gateway keys positions by the
|
|
3582
|
+
* **target member's own** `call_id`/`node_id` (see issue #19400 and the legacy
|
|
3583
|
+
* `setPositions` implementation), so this sends the participant's own call
|
|
3584
|
+
* context — matching {@link Participant.remove}. A resolved promise does not
|
|
3585
|
+
* guarantee a visible change: the backend silently returns `200` (no-op) for
|
|
3586
|
+
* non-conference targets.
|
|
3587
|
+
*
|
|
3571
3588
|
* @param value - The {@link VideoPosition} to assign (e.g. `'auto'`, `'reserved-0'`).
|
|
3572
3589
|
*/
|
|
3573
3590
|
async setPosition(value) {
|
|
3574
|
-
|
|
3591
|
+
const state = this._state$.value;
|
|
3592
|
+
const target = {
|
|
3593
|
+
member_id: this.id,
|
|
3594
|
+
call_id: state.call_id ?? "",
|
|
3595
|
+
node_id: state.node_id ?? ""
|
|
3596
|
+
};
|
|
3597
|
+
await this.executeMethod(target, "call.member.position.set", { targets: [{
|
|
3598
|
+
target,
|
|
3599
|
+
position: value
|
|
3600
|
+
}] });
|
|
3575
3601
|
}
|
|
3576
3602
|
/** Removes this participant from the call. */
|
|
3577
3603
|
async remove() {
|
|
@@ -5979,7 +6005,11 @@ function isVertoInviteMessage(value) {
|
|
|
5979
6005
|
const msg = value;
|
|
5980
6006
|
return msg.method === "verto.invite" && isObject(msg.params) && hasProperty(msg.params, "sdp") && hasProperty(msg.params, "callID");
|
|
5981
6007
|
}
|
|
5982
|
-
|
|
6008
|
+
/**
|
|
6009
|
+
* Guards inbound `verto.bye` frames (server → client). Outbound bye messages are
|
|
6010
|
+
* built locally and never type-guarded, so only the inbound shape is asserted.
|
|
6011
|
+
*/
|
|
6012
|
+
function isVertoByeInboundMessage(value) {
|
|
5983
6013
|
if (!isVertoMethodMessage(value)) return false;
|
|
5984
6014
|
return value.method === "verto.bye";
|
|
5985
6015
|
}
|
|
@@ -5999,6 +6029,14 @@ function isVertoMediaParamsInnerParams(value) {
|
|
|
5999
6029
|
function isVertoPingInnerParams(value) {
|
|
6000
6030
|
return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.ping";
|
|
6001
6031
|
}
|
|
6032
|
+
/**
|
|
6033
|
+
* Guards the params of an inbound `verto.bye` frame (top-level `callID`). Use
|
|
6034
|
+
* this to recognise the bye payload extracted by `vertoBye$`, e.g. when racing
|
|
6035
|
+
* it against a boolean answer/reject signal.
|
|
6036
|
+
*/
|
|
6037
|
+
function isVertoByeInboundParamsGuard(value) {
|
|
6038
|
+
return isObject(value) && hasProperty(value, "callID") && typeof value.callID === "string";
|
|
6039
|
+
}
|
|
6002
6040
|
|
|
6003
6041
|
//#endregion
|
|
6004
6042
|
//#region src/managers/VertoManager.ts
|
|
@@ -6336,7 +6374,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6336
6374
|
return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), takeUntil(this.destroyed$)));
|
|
6337
6375
|
}
|
|
6338
6376
|
get vertoBye$() {
|
|
6339
|
-
return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(
|
|
6377
|
+
return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeInboundMessage, "params"), takeUntil(this.destroyed$)));
|
|
6340
6378
|
}
|
|
6341
6379
|
get vertoAttach$() {
|
|
6342
6380
|
return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), takeUntil(this.destroyed$)));
|
|
@@ -6489,7 +6527,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6489
6527
|
logger$16.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
|
|
6490
6528
|
return;
|
|
6491
6529
|
}
|
|
6492
|
-
if (
|
|
6530
|
+
if (isVertoByeInboundParamsGuard(vertoByeOrAccepted)) {
|
|
6493
6531
|
logger$16.info("[WebRTCManager] Inbound call ended by remote before answer.");
|
|
6494
6532
|
this.callSession?.destroy();
|
|
6495
6533
|
} else if (!vertoByeOrAccepted) {
|
|
@@ -6591,7 +6629,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6591
6629
|
logger$16.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
|
|
6592
6630
|
return;
|
|
6593
6631
|
}
|
|
6594
|
-
if (
|
|
6632
|
+
if (isVertoByeInboundParamsGuard(vertoByeOrAccepted)) {
|
|
6595
6633
|
logger$16.info("[WebRTCManager] Call ended before answer was sent.");
|
|
6596
6634
|
this.callSession?.destroy();
|
|
6597
6635
|
} else if (!vertoByeOrAccepted) {
|
|
@@ -7646,6 +7684,12 @@ function mosToQualityLevel(mos) {
|
|
|
7646
7684
|
//#region src/core/entities/Call.ts
|
|
7647
7685
|
const logger$12 = getLogger();
|
|
7648
7686
|
/**
|
|
7687
|
+
* Verto method for setting member layout positions. Its gateway DTO requires a
|
|
7688
|
+
* `targets` array whose entries are `{ target, position }` (NOT bare targets),
|
|
7689
|
+
* so {@link WebRTCCall.buildMethodParams} special-cases it. See issue #19400.
|
|
7690
|
+
*/
|
|
7691
|
+
const POSITION_SET_METHOD = "call.member.position.set";
|
|
7692
|
+
/**
|
|
7649
7693
|
* Ratio between the critical and warning RTT spike multipliers.
|
|
7650
7694
|
* Warning threshold = baseline * warningMultiplier (default 3x)
|
|
7651
7695
|
* Critical threshold = baseline * warningMultiplier * RTT_CRITICAL_TO_WARNING_RATIO
|
|
@@ -7852,7 +7896,7 @@ var WebRTCCall = class extends Destroyable {
|
|
|
7852
7896
|
* @throws {JSONRPCError} If the RPC call returns an error.
|
|
7853
7897
|
*/
|
|
7854
7898
|
async executeMethod(target, method, args) {
|
|
7855
|
-
const params = this.buildMethodParams(target, args);
|
|
7899
|
+
const params = this.buildMethodParams(target, args, method);
|
|
7856
7900
|
const request = buildRPCRequest({
|
|
7857
7901
|
method,
|
|
7858
7902
|
params
|
|
@@ -7866,12 +7910,16 @@ var WebRTCCall = class extends Destroyable {
|
|
|
7866
7910
|
throw error;
|
|
7867
7911
|
}
|
|
7868
7912
|
}
|
|
7869
|
-
buildMethodParams(target, args) {
|
|
7913
|
+
buildMethodParams(target, args, method) {
|
|
7870
7914
|
const self = {
|
|
7871
7915
|
node_id: this.nodeId ?? "",
|
|
7872
7916
|
call_id: this.id,
|
|
7873
7917
|
member_id: this.vertoManager.selfId ?? ""
|
|
7874
7918
|
};
|
|
7919
|
+
if (method === POSITION_SET_METHOD) return {
|
|
7920
|
+
...args,
|
|
7921
|
+
self
|
|
7922
|
+
};
|
|
7875
7923
|
if (typeof target === "object") return {
|
|
7876
7924
|
...args,
|
|
7877
7925
|
self,
|
|
@@ -8437,10 +8485,21 @@ var WebRTCCall = class extends Destroyable {
|
|
|
8437
8485
|
return this.deferEmission(this._answered$.asObservable());
|
|
8438
8486
|
}
|
|
8439
8487
|
/**
|
|
8440
|
-
* Sets the call layout and participant positions.
|
|
8488
|
+
* Sets the call layout and, optionally, individual participant positions.
|
|
8489
|
+
*
|
|
8490
|
+
* The gateway `call.layout.set` DTO has **no** `positions` member, so when
|
|
8491
|
+
* `positions` is provided this method issues a `call.member.position.set`
|
|
8492
|
+
* request per member (via {@link Participant.setPosition}, which keys each
|
|
8493
|
+
* position by that member's own call context) alongside `call.layout.set`
|
|
8494
|
+
* (issue #19400, Flag #6).
|
|
8495
|
+
*
|
|
8496
|
+
* **These operations are NOT atomic.** The layout is applied first, then each
|
|
8497
|
+
* member position sequentially, so members may briefly flash into their
|
|
8498
|
+
* default slots before being moved to the requested positions.
|
|
8441
8499
|
*
|
|
8442
8500
|
* @param layout - Layout name (must be one of {@link layouts}).
|
|
8443
|
-
* @param positions -
|
|
8501
|
+
* @param positions - Optional map of member IDs to {@link VideoPosition} values.
|
|
8502
|
+
* When omitted or empty, only the layout is changed.
|
|
8444
8503
|
* @throws {InvalidParams} If the layout is not in the available {@link layouts}.
|
|
8445
8504
|
*
|
|
8446
8505
|
* @example
|
|
@@ -8453,10 +8512,17 @@ var WebRTCCall = class extends Destroyable {
|
|
|
8453
8512
|
async setLayout(layout, positions) {
|
|
8454
8513
|
if (!this.layouts.includes(layout)) throw new InvalidParams(`Layout ${layout} is not available in the current call layouts: ${this.layouts.join(", ")}`);
|
|
8455
8514
|
const selfId = await firstValueFrom(this.selfId$.pipe(filter((id) => id !== null)));
|
|
8456
|
-
await this.executeMethod(selfId, "call.layout.set", {
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
|
|
8515
|
+
await this.executeMethod(selfId, "call.layout.set", { layout });
|
|
8516
|
+
const positionEntries = Object.entries(positions ?? {});
|
|
8517
|
+
if (positionEntries.length === 0) return;
|
|
8518
|
+
for (const [memberId, position] of positionEntries) {
|
|
8519
|
+
const participant = this.participants.find((p) => p.id === memberId);
|
|
8520
|
+
if (!participant) {
|
|
8521
|
+
logger$12.warn(`[Call] setLayout: member ${memberId} not found in participants; skipping position ${position}`);
|
|
8522
|
+
continue;
|
|
8523
|
+
}
|
|
8524
|
+
await participant.setPosition(position);
|
|
8525
|
+
}
|
|
8460
8526
|
}
|
|
8461
8527
|
/**
|
|
8462
8528
|
* Transfers the call to another destination.
|
|
@@ -9371,11 +9437,41 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
9371
9437
|
}
|
|
9372
9438
|
} else this._errors$.next(error);
|
|
9373
9439
|
}
|
|
9440
|
+
/**
|
|
9441
|
+
* Clear the resume state (authorization_state + protocol) only.
|
|
9442
|
+
*
|
|
9443
|
+
* This is the stale-auth-state recovery helper used by handleAuthError:
|
|
9444
|
+
* the server rejected a reconnect, so the resume state is discarded and a
|
|
9445
|
+
* fresh connect follows. Attach records are deliberately preserved — the
|
|
9446
|
+
* session lives on through the reconnect and reattachCalls() needs the
|
|
9447
|
+
* stored call references afterwards. Do NOT add detachAll() here.
|
|
9448
|
+
*
|
|
9449
|
+
* For public teardown (disconnect/destroy), use {@link teardownSessionState}
|
|
9450
|
+
* instead, which clears the attach records as well.
|
|
9451
|
+
*/
|
|
9374
9452
|
async cleanupStoredConnectionParams() {
|
|
9375
9453
|
await this.transport.setProtocol(void 0);
|
|
9376
9454
|
await this.updateAuthorizationStateInStorage(void 0);
|
|
9377
9455
|
this._authorization$.next(void 0);
|
|
9378
9456
|
}
|
|
9457
|
+
/**
|
|
9458
|
+
* Public-teardown helper for disconnect()/destroy(). Clears the resume
|
|
9459
|
+
* state (authorization_state + protocol) AND the attach records as one
|
|
9460
|
+
* atomic unit.
|
|
9461
|
+
*
|
|
9462
|
+
* The two stores are coupled: the backend only honors attach records
|
|
9463
|
+
* within the session identified by the resume state, so ending the
|
|
9464
|
+
* session must clear both. Clearing one without the other strands records
|
|
9465
|
+
* no future session can honor (disconnect) or revives a session the
|
|
9466
|
+
* developer explicitly ended (destroy).
|
|
9467
|
+
*
|
|
9468
|
+
* Distinct from {@link cleanupStoredConnectionParams}, which keeps the
|
|
9469
|
+
* attach records for the stale-auth-state recovery path.
|
|
9470
|
+
*/
|
|
9471
|
+
async teardownSessionState() {
|
|
9472
|
+
await this.cleanupStoredConnectionParams();
|
|
9473
|
+
await this.attachManager.detachAll();
|
|
9474
|
+
}
|
|
9379
9475
|
async updateAuthState(authorization_state) {
|
|
9380
9476
|
try {
|
|
9381
9477
|
await this.storage.setItem(this.authorizationStateKey, authorization_state);
|
|
@@ -9470,7 +9566,7 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
9470
9566
|
async disconnect() {
|
|
9471
9567
|
this.transport.disconnect();
|
|
9472
9568
|
this._authState$.next({ kind: "unauthenticated" });
|
|
9473
|
-
await this.
|
|
9569
|
+
await this.teardownSessionState();
|
|
9474
9570
|
}
|
|
9475
9571
|
async createInboundCall(invite) {
|
|
9476
9572
|
const callSession = await this.createCall({
|
|
@@ -10632,6 +10728,10 @@ var TransportManager = class extends Destroyable {
|
|
|
10632
10728
|
if (!isSignalwireRequest(message)) return true;
|
|
10633
10729
|
const eventChannel = message.params.event_channel;
|
|
10634
10730
|
if (!eventChannel) return true;
|
|
10731
|
+
if (message.params.event_type.startsWith("conversation.")) {
|
|
10732
|
+
logger$2.debug(`[Transport] Received conversation event: ${message.params.event_type} (event_channel: ${eventChannel})`);
|
|
10733
|
+
return true;
|
|
10734
|
+
}
|
|
10635
10735
|
const currentProtocol = this._currentProtocol;
|
|
10636
10736
|
if (!currentProtocol) return true;
|
|
10637
10737
|
if (!eventChannel.includes(currentProtocol)) {
|
|
@@ -11231,7 +11331,7 @@ var SignalWire = class extends Destroyable {
|
|
|
11231
11331
|
logger$1.warn("[SignalWire] Failed to initialize VisibilityController:", error);
|
|
11232
11332
|
}
|
|
11233
11333
|
try {
|
|
11234
|
-
this._diagnosticsCollector = new DiagnosticsCollector({ sdkVersion: "4.0.0-rc.
|
|
11334
|
+
this._diagnosticsCollector = new DiagnosticsCollector({ sdkVersion: "4.0.0-rc.1" });
|
|
11235
11335
|
} catch (error) {
|
|
11236
11336
|
logger$1.warn("[SignalWire] Failed to initialize DiagnosticsCollector:", error);
|
|
11237
11337
|
}
|
|
@@ -11239,6 +11339,14 @@ var SignalWire = class extends Destroyable {
|
|
|
11239
11339
|
/**
|
|
11240
11340
|
* Disconnects the WebSocket and tears down the current session.
|
|
11241
11341
|
*
|
|
11342
|
+
* Ends the session identified by the protocol and clears its persisted
|
|
11343
|
+
* resume state (`authorization_state` + protocol) and attach records
|
|
11344
|
+
* together — a later {@link connect} with the same credentials starts a
|
|
11345
|
+
* fresh session and cannot reattach to the ended session's calls.
|
|
11346
|
+
* Credentials and device preferences are preserved. To temporarily stop
|
|
11347
|
+
* receiving inbound calls while keeping the session alive, use
|
|
11348
|
+
* `unregister()` instead.
|
|
11349
|
+
*
|
|
11242
11350
|
* The client can be reconnected by calling {@link connect} again,
|
|
11243
11351
|
* which creates a fresh transport and session.
|
|
11244
11352
|
*/
|
|
@@ -11630,12 +11738,20 @@ var SignalWire = class extends Destroyable {
|
|
|
11630
11738
|
prefs.preferredVideoInput = null;
|
|
11631
11739
|
await this._deviceController.clearDeviceState();
|
|
11632
11740
|
}
|
|
11633
|
-
/**
|
|
11741
|
+
/**
|
|
11742
|
+
* Destroys the client, clearing timers and releasing all resources.
|
|
11743
|
+
*
|
|
11744
|
+
* Intentionally destroying the client ends its session: the resume state
|
|
11745
|
+
* (`authorization_state` + protocol) and the attach records are both
|
|
11746
|
+
* cleared. Credentials and device preferences are preserved — use
|
|
11747
|
+
* {@link resetToDefaults} for a full wipe. To temporarily stop receiving
|
|
11748
|
+
* inbound calls while keeping the session alive, use `unregister()`.
|
|
11749
|
+
*/
|
|
11634
11750
|
destroy() {
|
|
11635
11751
|
this._refreshCoordinator?.destroy();
|
|
11636
11752
|
this._refreshCoordinator = void 0;
|
|
11637
11753
|
this._dpopManager?.destroy();
|
|
11638
|
-
|
|
11754
|
+
this._clientSession.teardownSessionState();
|
|
11639
11755
|
this._transport.destroy();
|
|
11640
11756
|
this._clientSession.destroy();
|
|
11641
11757
|
try {
|
|
@@ -11749,7 +11865,7 @@ var StaticCredentialProvider = class {
|
|
|
11749
11865
|
/**
|
|
11750
11866
|
* Library version from package.json, injected at build time.
|
|
11751
11867
|
*/
|
|
11752
|
-
const version = "4.0.0-rc.
|
|
11868
|
+
const version = "4.0.0-rc.1";
|
|
11753
11869
|
/**
|
|
11754
11870
|
* Flag indicating the library has been loaded and is ready to use.
|
|
11755
11871
|
* For UMD builds: `window.SignalWire.ready`
|
|
@@ -11771,7 +11887,7 @@ const ready = true;
|
|
|
11771
11887
|
*/
|
|
11772
11888
|
const emitReadyEvent = () => {
|
|
11773
11889
|
if (typeof window !== "undefined") {
|
|
11774
|
-
const event = new CustomEvent("signalwire:js:ready", { detail: { version: "4.0.0-rc.
|
|
11890
|
+
const event = new CustomEvent("signalwire:js:ready", { detail: { version: "4.0.0-rc.1" } });
|
|
11775
11891
|
window.dispatchEvent(event);
|
|
11776
11892
|
}
|
|
11777
11893
|
};
|