@signalwire/js 4.0.0-dev-20260714201720 → 4.0.0-dev-20260717142935

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/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_operators = require('./operators-B8ipd4Xl.cjs');
1
+ const require_operators = require('./operators-D3_VhSb1.cjs');
2
2
  let jwt_decode = require("jwt-decode");
3
3
  let rxjs = require("rxjs");
4
4
  let uuid = require("uuid");
@@ -3116,9 +3116,9 @@ const initialState = {};
3116
3116
  * the local participant with additional device control.
3117
3117
  */
3118
3118
  var Participant = class extends Destroyable {
3119
- constructor(id, executeMethod, deviceController) {
3119
+ constructor(id, callExecuteMethod, deviceController) {
3120
3120
  super();
3121
- this.executeMethod = executeMethod;
3121
+ this.callExecuteMethod = callExecuteMethod;
3122
3122
  this.deviceController = deviceController;
3123
3123
  this._state$ = this.createBehaviorSubject(initialState);
3124
3124
  this.id = id;
@@ -3340,22 +3340,55 @@ var Participant = class extends Destroyable {
3340
3340
  get value() {
3341
3341
  return this._state$.value;
3342
3342
  }
3343
+ /**
3344
+ * Target triple for member RPCs, built from the participant's own state.
3345
+ * The backend locates the member's session by the target `call_id`/`node_id`,
3346
+ * so this must always be the participant's own call context — never the
3347
+ * local call's id (issue #19400).
3348
+ *
3349
+ * Reading it doubles as a readiness probe: it throws until the first full
3350
+ * member event (`member.joined`/`member.updated` or the `call.joined`
3351
+ * roster) arrives, and never regresses afterwards.
3352
+ *
3353
+ * @throws {ParticipantNotReadyError} If the member state has not been
3354
+ * received yet (e.g. a participant first seen via `member.talking`) — an
3355
+ * empty call context can never address the member, so fail fast instead of
3356
+ * sending a doomed RPC.
3357
+ */
3358
+ get target() {
3359
+ const { call_id, node_id } = this._state$.value;
3360
+ if (!call_id || !node_id) throw new require_operators.ParticipantNotReadyError(this.id);
3361
+ return {
3362
+ member_id: this.id,
3363
+ call_id,
3364
+ node_id
3365
+ };
3366
+ }
3367
+ /**
3368
+ * Executes a member RPC against this participant, injecting its own
3369
+ * {@link target} as the target.
3370
+ *
3371
+ * @throws {ParticipantNotReadyError} Via {@link target}, when the
3372
+ * member state has not been received yet.
3373
+ */
3374
+ async executeMethod(method, args) {
3375
+ return this.callExecuteMethod(this.target, method, args);
3376
+ }
3343
3377
  /** Toggles the deafened state (mutes/unmutes incoming audio). */
3344
3378
  async toggleDeaf() {
3345
- const method = toggleDeafMethod(this.deaf);
3346
- await this.executeMethod(this.id, method, {});
3379
+ await this.executeMethod(toggleDeafMethod(this.deaf), {});
3347
3380
  }
3348
3381
  /** Toggles the hand-raised state. */
3349
3382
  async toggleHandraise() {
3350
- await this.executeMethod(this.id, toggleHandraiseMethod(this.handraised), {});
3383
+ await this.executeMethod(toggleHandraiseMethod(this.handraised), {});
3351
3384
  }
3352
3385
  /** Mutes the participant's audio. */
3353
3386
  async mute() {
3354
- await this.executeMethod(this.id, "call.mute", { channels: ["audio"] });
3387
+ await this.executeMethod("call.mute", { channels: ["audio"] });
3355
3388
  }
3356
3389
  /** Unmutes the participant's audio. */
3357
3390
  async unmute() {
3358
- await this.executeMethod(this.id, "call.unmute", { channels: ["audio"] });
3391
+ await this.executeMethod("call.unmute", { channels: ["audio"] });
3359
3392
  }
3360
3393
  /** Toggles the participant's audio mute state. */
3361
3394
  async toggleMute() {
@@ -3363,11 +3396,11 @@ var Participant = class extends Destroyable {
3363
3396
  }
3364
3397
  /** Mutes the participant's video. */
3365
3398
  async muteVideo() {
3366
- await this.executeMethod(this.id, "call.mute", { channels: ["video"] });
3399
+ await this.executeMethod("call.mute", { channels: ["video"] });
3367
3400
  }
3368
3401
  /** Unmutes the participant's video. */
3369
3402
  async unmuteVideo() {
3370
- await this.executeMethod(this.id, "call.unmute", { channels: ["video"] });
3403
+ await this.executeMethod("call.unmute", { channels: ["video"] });
3371
3404
  }
3372
3405
  /** Toggles the participant's video mute state. */
3373
3406
  async toggleMuteVideo() {
@@ -3375,7 +3408,7 @@ var Participant = class extends Destroyable {
3375
3408
  }
3376
3409
  /** Toggles echo cancellation on the audio input. */
3377
3410
  async toggleEchoCancellation() {
3378
- await this.executeMethod(this.id, "call.audioflags.set", {
3411
+ await this.executeMethod("call.audioflags.set", {
3379
3412
  echo_cancellation: !this.echoCancellation,
3380
3413
  auto_gain: this.autoGain,
3381
3414
  noise_suppression: this.noiseSuppression
@@ -3383,7 +3416,7 @@ var Participant = class extends Destroyable {
3383
3416
  }
3384
3417
  /** Toggles automatic gain control on the audio input. */
3385
3418
  async toggleAudioInputAutoGain() {
3386
- await this.executeMethod(this.id, "call.audioflags.set", {
3419
+ await this.executeMethod("call.audioflags.set", {
3387
3420
  echo_cancellation: this.echoCancellation,
3388
3421
  auto_gain: !this.autoGain,
3389
3422
  noise_suppression: this.noiseSuppression
@@ -3391,7 +3424,7 @@ var Participant = class extends Destroyable {
3391
3424
  }
3392
3425
  /** Toggles noise suppression on the audio input. */
3393
3426
  async toggleNoiseSuppression() {
3394
- await this.executeMethod(this.id, "call.audioflags.set", {
3427
+ await this.executeMethod("call.audioflags.set", {
3395
3428
  echo_cancellation: this.echoCancellation,
3396
3429
  auto_gain: this.autoGain,
3397
3430
  noise_suppression: !this.noiseSuppression
@@ -3399,7 +3432,7 @@ var Participant = class extends Destroyable {
3399
3432
  }
3400
3433
  /** Toggles low-bitrate mode for this participant's media. */
3401
3434
  async toggleLowbitrate() {
3402
- await this.executeMethod(this.id, "call.lowbitrate.set", { lowbitrate: !this.lowbitrate });
3435
+ await this.executeMethod("call.lowbitrate.set", { lowbitrate: !this.lowbitrate });
3403
3436
  }
3404
3437
  /**
3405
3438
  * Adjusts the **conference-only** microphone energy gate / sensitivity level
@@ -3415,7 +3448,7 @@ var Participant = class extends Destroyable {
3415
3448
  * (integer, larger values are more sensitive).
3416
3449
  */
3417
3450
  async setAudioInputSensitivity(value) {
3418
- await this.executeMethod(this.id, "call.microphone.sensitivity.set", { sensitivity: value });
3451
+ await this.executeMethod("call.microphone.sensitivity.set", { sensitivity: value });
3419
3452
  }
3420
3453
  /**
3421
3454
  * Sets the **server-side** microphone volume on this participant's bridged
@@ -3428,7 +3461,7 @@ var Participant = class extends Destroyable {
3428
3461
  * @param value - Volume level (0-100).
3429
3462
  */
3430
3463
  async setAudioInputVolume(value) {
3431
- await this.executeMethod(this.id, "call.microphone.volume.set", { volume: value });
3464
+ await this.executeMethod("call.microphone.volume.set", { volume: value });
3432
3465
  }
3433
3466
  /**
3434
3467
  * Sets the **server-side** speaker volume on this participant's bridged call
@@ -3442,45 +3475,31 @@ var Participant = class extends Destroyable {
3442
3475
  * @param value - Volume level (0-100).
3443
3476
  */
3444
3477
  async setAudioOutputVolume(value) {
3445
- await this.executeMethod(this.id, "call.speaker.volume.set", { volume: value });
3478
+ await this.executeMethod("call.speaker.volume.set", { volume: value });
3446
3479
  }
3447
3480
  /**
3448
3481
  * Sets the participant's position in the video layout.
3449
3482
  *
3450
- * Requires the `member.position` capability. The gateway keys positions by the
3451
- * **target member's own** `call_id`/`node_id` (see issue #19400 and the legacy
3452
- * `setPositions` implementation), so this sends the participant's own call
3453
- * context — matching {@link Participant.remove}. A resolved promise does not
3454
- * guarantee a visible change: the backend silently returns `200` (no-op) for
3455
- * non-conference targets.
3483
+ * Requires the `member.position` capability. The gateway requires a
3484
+ * `targets` array of `{ target, position }` entries (issue #19400). A
3485
+ * resolved promise does not guarantee a visible change: the backend silently
3486
+ * returns `200` (no-op) for non-conference targets.
3456
3487
  *
3457
3488
  * @param value - The {@link VideoPosition} to assign (e.g. `'auto'`, `'reserved-0'`).
3458
3489
  */
3459
3490
  async setPosition(value) {
3460
- const state = this._state$.value;
3461
- const target = {
3462
- member_id: this.id,
3463
- call_id: state.call_id ?? "",
3464
- node_id: state.node_id ?? ""
3465
- };
3466
- await this.executeMethod(target, "call.member.position.set", { targets: [{
3467
- target,
3491
+ await this.executeMethod("call.member.position.set", { targets: [{
3492
+ target: this.target,
3468
3493
  position: value
3469
3494
  }] });
3470
3495
  }
3471
3496
  /** Removes this participant from the call. */
3472
3497
  async remove() {
3473
- const state = this._state$.value;
3474
- const target = {
3475
- member_id: this.id,
3476
- call_id: state.call_id ?? "",
3477
- node_id: state.node_id ?? ""
3478
- };
3479
- await this.executeMethod(target, "call.member.remove", {});
3498
+ await this.executeMethod("call.member.remove", { targets: [this.target] });
3480
3499
  }
3481
3500
  /** Ends the call for this participant. */
3482
3501
  async end() {
3483
- await this.executeMethod(this.id, "call.end", {});
3502
+ await this.executeMethod("call.end", {});
3484
3503
  }
3485
3504
  /**
3486
3505
  * Replaces custom metadata for this participant.
@@ -3500,7 +3519,7 @@ var Participant = class extends Destroyable {
3500
3519
  }
3501
3520
  /** Destroys the participant, releasing all subscriptions and references. */
3502
3521
  destroy() {
3503
- this.executeMethod = void 0;
3522
+ this.callExecuteMethod = void 0;
3504
3523
  super.destroy();
3505
3524
  }
3506
3525
  };
@@ -3512,8 +3531,8 @@ var Participant = class extends Destroyable {
3512
3531
  */
3513
3532
  var SelfParticipant = class extends Participant {
3514
3533
  /** @internal */
3515
- constructor(id, executeMethod, vertoManager, deviceController) {
3516
- super(id, executeMethod, deviceController);
3534
+ constructor(id, callExecuteMethod, vertoManager, deviceController) {
3535
+ super(id, callExecuteMethod, deviceController);
3517
3536
  this.vertoManager = vertoManager;
3518
3537
  this._studioAudio$ = this.createBehaviorSubject(false);
3519
3538
  this.capabilities = new SelfCapabilities();
@@ -3537,7 +3556,7 @@ var SelfParticipant = class extends Participant {
3537
3556
  async enableStudioAudio() {
3538
3557
  if (this._studioAudio$.value) return;
3539
3558
  this._studioAudio$.next(true);
3540
- await this.executeMethod(this.id, "call.audioflags.set", {
3559
+ await this.executeMethod("call.audioflags.set", {
3541
3560
  echo_cancellation: false,
3542
3561
  auto_gain: false,
3543
3562
  noise_suppression: false
@@ -3550,7 +3569,7 @@ var SelfParticipant = class extends Participant {
3550
3569
  async disableStudioAudio() {
3551
3570
  if (!this._studioAudio$.value) return;
3552
3571
  this._studioAudio$.next(false);
3553
- await this.executeMethod(this.id, "call.audioflags.set", {
3572
+ await this.executeMethod("call.audioflags.set", {
3554
3573
  echo_cancellation: true,
3555
3574
  auto_gain: true,
3556
3575
  noise_suppression: true
@@ -7634,12 +7653,6 @@ function mosToQualityLevel(mos) {
7634
7653
  //#region src/core/entities/Call.ts
7635
7654
  const logger$12 = require_operators.getLogger();
7636
7655
  /**
7637
- * Verto method for setting member layout positions. Its gateway DTO requires a
7638
- * `targets` array whose entries are `{ target, position }` (NOT bare targets),
7639
- * so {@link WebRTCCall.buildMethodParams} special-cases it. See issue #19400.
7640
- */
7641
- const POSITION_SET_METHOD = "call.member.position.set";
7642
- /**
7643
7656
  * Ratio between the critical and warning RTT spike multipliers.
7644
7657
  * Warning threshold = baseline * warningMultiplier (default 3x)
7645
7658
  * Critical threshold = baseline * warningMultiplier * RTT_CRITICAL_TO_WARNING_RATIO
@@ -7790,7 +7803,7 @@ var WebRTCCall = class extends Destroyable {
7790
7803
  /** Toggles the call lock state, preventing or allowing new participants from joining. */
7791
7804
  async toggleLock() {
7792
7805
  const method = this.locked ? "call.unlock" : "call.lock";
7793
- await this.executeMethod(this.selfId ?? "", method, {});
7806
+ await this.executeMethod(this.callSelf, method, {});
7794
7807
  }
7795
7808
  /**
7796
7809
  * Toggles the hold state of the call (pauses/resumes local media transmission).
@@ -7839,14 +7852,24 @@ var WebRTCCall = class extends Destroyable {
7839
7852
  *
7840
7853
  * Constructs call context (node_id, call_id, member_id) and sends the RPC request.
7841
7854
  *
7842
- * @param target - Target member ID string, or a {@link MemberTarget} object.
7855
+ * @param target - Target {@link MemberTarget} triple, or the local member's
7856
+ * ID string for self-operations (any other string is rejected — a bare
7857
+ * member id cannot carry the remote member's own call context).
7843
7858
  * @param method - Verto method name (e.g. `'call.mute'`, `'call.member.remove'`).
7844
7859
  * @param args - Parameters for the RPC method.
7845
7860
  * @returns The RPC response.
7861
+ * @throws {CallNotReadyError} If the call has no self member context yet.
7862
+ * @throws {InvalidParams} If a string target is not the local member's ID.
7846
7863
  * @throws {JSONRPCError} If the RPC call returns an error.
7847
7864
  */
7848
7865
  async executeMethod(target, method, args) {
7849
- const params = this.buildMethodParams(target, args, method);
7866
+ const self = this.callSelf;
7867
+ if (typeof target === "string" && target !== self.member_id) throw new require_operators.InvalidParams(`Target member ID ${target} does not match call's self member ID ${self.member_id}`);
7868
+ const params = {
7869
+ ...args,
7870
+ self,
7871
+ target: typeof target === "string" ? self : target
7872
+ };
7850
7873
  const request = buildRPCRequest({
7851
7874
  method,
7852
7875
  params
@@ -7860,29 +7883,23 @@ var WebRTCCall = class extends Destroyable {
7860
7883
  throw error;
7861
7884
  }
7862
7885
  }
7863
- buildMethodParams(target, args, method) {
7864
- const self = {
7865
- node_id: this.nodeId ?? "",
7866
- call_id: this.id,
7867
- member_id: this.vertoManager.selfId ?? ""
7868
- };
7869
- if (method === POSITION_SET_METHOD) return {
7870
- ...args,
7871
- self
7872
- };
7873
- if (typeof target === "object") return {
7874
- ...args,
7875
- self,
7876
- targets: [target]
7877
- };
7886
+ /**
7887
+ * The local leg's member triple — sent as `self` in every member RPC
7888
+ * envelope, and as the `target` of call-scoped self-operations (e.g. lock,
7889
+ * layout).
7890
+ *
7891
+ * @throws {CallNotReadyError} Before `call.joined` delivers the self member
7892
+ * context (`selfId`/`nodeId`) — an RPC without it cannot be routed, so fail
7893
+ * fast instead of sending a doomed request.
7894
+ */
7895
+ get callSelf() {
7896
+ const node_id = this.nodeId;
7897
+ const member_id = this.vertoManager.selfId;
7898
+ if (!node_id || !member_id) throw new require_operators.CallNotReadyError(this.id);
7878
7899
  return {
7879
- ...args,
7880
- self,
7881
- target: {
7882
- node_id: this.nodeId ?? "",
7883
- call_id: this.id,
7884
- member_id: target
7885
- }
7900
+ node_id,
7901
+ call_id: this.id,
7902
+ member_id
7886
7903
  };
7887
7904
  }
7888
7905
  /** Observable of the current call status (e.g. `'ringing'`, `'connected'`). */
@@ -8445,12 +8462,17 @@ var WebRTCCall = class extends Destroyable {
8445
8462
  *
8446
8463
  * **These operations are NOT atomic.** The layout is applied first, then each
8447
8464
  * member position sequentially, so members may briefly flash into their
8448
- * default slots before being moved to the requested positions.
8465
+ * default slots before being moved to the requested positions. Targeted
8466
+ * members are validated upfront, though: when any of them has no
8467
+ * {@link Participant.target | member call context} yet, the whole call
8468
+ * rejects before any request is sent and the layout is left unchanged.
8449
8469
  *
8450
8470
  * @param layout - Layout name (must be one of {@link layouts}).
8451
8471
  * @param positions - Optional map of member IDs to {@link VideoPosition} values.
8452
8472
  * When omitted or empty, only the layout is changed.
8453
8473
  * @throws {InvalidParams} If the layout is not in the available {@link layouts}.
8474
+ * @throws {ParticipantNotReadyError} If a targeted member's call context has
8475
+ * not been received yet — thrown before any request is sent.
8454
8476
  *
8455
8477
  * @example
8456
8478
  * ```ts
@@ -8461,18 +8483,19 @@ var WebRTCCall = class extends Destroyable {
8461
8483
  */
8462
8484
  async setLayout(layout, positions) {
8463
8485
  if (!this.layouts.includes(layout)) throw new require_operators.InvalidParams(`Layout ${layout} is not available in the current call layouts: ${this.layouts.join(", ")}`);
8464
- const selfId = await (0, rxjs.firstValueFrom)(this.selfId$.pipe((0, rxjs.filter)((id) => id !== null)));
8465
- await this.executeMethod(selfId, "call.layout.set", { layout });
8466
- const positionEntries = Object.entries(positions ?? {});
8467
- if (positionEntries.length === 0) return;
8468
- for (const [memberId, position] of positionEntries) {
8486
+ const targets = [];
8487
+ for (const [memberId, position] of Object.entries(positions ?? {})) {
8469
8488
  const participant = this.participants.find((p) => p.id === memberId);
8470
8489
  if (!participant) {
8471
8490
  logger$12.warn(`[Call] setLayout: member ${memberId} not found in participants; skipping position ${position}`);
8472
8491
  continue;
8473
8492
  }
8474
- await participant.setPosition(position);
8493
+ participant.target;
8494
+ targets.push([participant, position]);
8475
8495
  }
8496
+ const selfId = await (0, rxjs.firstValueFrom)(this.selfId$.pipe((0, rxjs.filter)((id) => id !== null)));
8497
+ await this.executeMethod(selfId, "call.layout.set", { layout });
8498
+ for (const [participant, position] of targets) await participant.setPosition(position);
8476
8499
  }
8477
8500
  /**
8478
8501
  * Transfers the call to another destination.
@@ -11294,7 +11317,7 @@ var SignalWire = class extends Destroyable {
11294
11317
  logger$1.warn("[SignalWire] Failed to initialize VisibilityController:", error);
11295
11318
  }
11296
11319
  try {
11297
- this._diagnosticsCollector = new DiagnosticsCollector({ sdkVersion: "4.0.0-rc.1" });
11320
+ this._diagnosticsCollector = new DiagnosticsCollector({ sdkVersion: "4.0.0-rc.2" });
11298
11321
  } catch (error) {
11299
11322
  logger$1.warn("[SignalWire] Failed to initialize DiagnosticsCollector:", error);
11300
11323
  }
@@ -11828,7 +11851,7 @@ var StaticCredentialProvider = class {
11828
11851
  /**
11829
11852
  * Library version from package.json, injected at build time.
11830
11853
  */
11831
- const version = "4.0.0-rc.1";
11854
+ const version = "4.0.0-rc.2";
11832
11855
  /**
11833
11856
  * Flag indicating the library has been loaded and is ready to use.
11834
11857
  * For UMD builds: `window.SignalWire.ready`
@@ -11850,7 +11873,7 @@ const ready = true;
11850
11873
  */
11851
11874
  const emitReadyEvent = () => {
11852
11875
  if (typeof window !== "undefined") {
11853
- const event = new CustomEvent("signalwire:js:ready", { detail: { version: "4.0.0-rc.1" } });
11876
+ const event = new CustomEvent("signalwire:js:ready", { detail: { version: "4.0.0-rc.2" } });
11854
11877
  window.dispatchEvent(event);
11855
11878
  }
11856
11879
  };
@@ -11859,6 +11882,7 @@ emitReadyEvent();
11859
11882
  //#endregion
11860
11883
  exports.Address = Address;
11861
11884
  exports.CallCreateError = require_operators.CallCreateError;
11885
+ exports.CallNotReadyError = require_operators.CallNotReadyError;
11862
11886
  exports.ClientPreferences = ClientPreferences;
11863
11887
  exports.CollectionFetchError = require_operators.CollectionFetchError;
11864
11888
  exports.DPoPInitError = require_operators.DPoPInitError;
@@ -11870,6 +11894,7 @@ exports.MediaTrackError = require_operators.MediaTrackError;
11870
11894
  exports.MessageParseError = require_operators.MessageParseError;
11871
11895
  exports.OverconstrainedFallbackError = require_operators.OverconstrainedFallbackError;
11872
11896
  exports.Participant = Participant;
11897
+ exports.ParticipantNotReadyError = require_operators.ParticipantNotReadyError;
11873
11898
  exports.PreflightError = require_operators.PreflightError;
11874
11899
  exports.RecoveryError = require_operators.RecoveryError;
11875
11900
  exports.SelfCapabilities = SelfCapabilities;