@signalwire/js 4.0.0-rc.3 → 4.0.0-rc.4

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.
@@ -4331,13 +4331,13 @@ var require_race$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
4331
4331
  var innerFrom_1$28 = require_innerFrom();
4332
4332
  var argsOrArgArray_1$4 = require_argsOrArgArray();
4333
4333
  var OperatorSubscriber_1$48 = require_OperatorSubscriber();
4334
- function race$4() {
4334
+ function race$5() {
4335
4335
  var sources = [];
4336
4336
  for (var _i = 0; _i < arguments.length; _i++) sources[_i] = arguments[_i];
4337
4337
  sources = argsOrArgArray_1$4.argsOrArgArray(sources);
4338
4338
  return sources.length === 1 ? innerFrom_1$28.innerFrom(sources[0]) : new Observable_1$6.Observable(raceInit(sources));
4339
4339
  }
4340
- exports.race = race$4;
4340
+ exports.race = race$5;
4341
4341
  function raceInit(sources) {
4342
4342
  return function(subscriber) {
4343
4343
  var subscriptions = [];
@@ -6829,12 +6829,12 @@ var require_skip = /* @__PURE__ */ __commonJSMin(((exports) => {
6829
6829
  Object.defineProperty(exports, "__esModule", { value: true });
6830
6830
  exports.skip = void 0;
6831
6831
  var filter_1$3 = require_filter();
6832
- function skip$3(count$1) {
6832
+ function skip$5(count$1) {
6833
6833
  return filter_1$3.filter(function(_, index) {
6834
6834
  return count$1 <= index;
6835
6835
  });
6836
6836
  }
6837
- exports.skip = skip$3;
6837
+ exports.skip = skip$5;
6838
6838
  }));
6839
6839
 
6840
6840
  //#endregion
@@ -9029,6 +9029,18 @@ const DEFAULT_ICE_GATHERING_TIMEOUT_MS = 6e3;
9029
9029
  const DEFAULT_RECONNECT_CALLS_TIMEOUT_MS = 300 * 1e3;
9030
9030
  const DEFAULT_CONNECTION_TIMEOUT_MS = 1e4;
9031
9031
  /**
9032
+ * The server sends `signalwire.ping` every 10s. A socket that stays silent
9033
+ * longer than this is half-open: the server has dropped the session but the
9034
+ * browser never fired `close`.
9035
+ */
9036
+ const SERVER_PING_TIMEOUT_MS = 15e3;
9037
+ /**
9038
+ * How long a `signalwire.ping` the client sends after the server's pings
9039
+ * stopped may take to be answered. The server defers its own pings while its
9040
+ * threadpool is congested, so silence alone does not mean the socket is dead.
9041
+ */
9042
+ const SERVER_PING_PROBE_TIMEOUT_MS = 1e4;
9043
+ /**
9032
9044
  * How long call setup may wait for the network, measured from local media
9033
9045
  * settling until the member id arrives.
9034
9046
  *
@@ -12359,6 +12371,12 @@ const RPCReauthenticate = (params) => {
12359
12371
 
12360
12372
  //#endregion
12361
12373
  //#region src/core/RPCMessages/RPCPing.ts
12374
+ const RPCPing = () => {
12375
+ return buildRPCRequest({
12376
+ method: "signalwire.ping",
12377
+ params: { timestamp: Date.now() / 1e3 }
12378
+ });
12379
+ };
12362
12380
  const RPCPingResponse = (id, timestamp$1) => {
12363
12381
  return makeRPCResponse({
12364
12382
  id,
@@ -12742,12 +12760,12 @@ var require_race = /* @__PURE__ */ __commonJSMin(((exports) => {
12742
12760
  exports.race = void 0;
12743
12761
  var argsOrArgArray_1 = require_argsOrArgArray();
12744
12762
  var raceWith_1$1 = require_raceWith();
12745
- function race$3() {
12763
+ function race$4() {
12746
12764
  var args = [];
12747
12765
  for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
12748
12766
  return raceWith_1$1.raceWith.apply(void 0, __spreadArray([], __read(argsOrArgArray_1.argsOrArgArray(args))));
12749
12767
  }
12750
- exports.race = race$3;
12768
+ exports.race = race$4;
12751
12769
  }));
12752
12770
 
12753
12771
  //#endregion
@@ -17213,6 +17231,7 @@ var WebRTCVertoManager = class extends VertoManager {
17213
17231
  this._signalingStatus$ = this.createReplaySubject(1);
17214
17232
  this._screenShareStatus$ = this.createBehaviorSubject("none");
17215
17233
  this._rtcPeerConnectionsMap = /* @__PURE__ */ new Map();
17234
+ this._lastVertoPings = /* @__PURE__ */ new Map();
17216
17235
  this._legErrors$ = this.createSubject();
17217
17236
  this._nodeId$ = this.createBehaviorSubject(options.nodeId ?? null);
17218
17237
  this.onError = options.onError;
@@ -17328,9 +17347,13 @@ var WebRTCVertoManager = class extends VertoManager {
17328
17347
  this.applyServerMediaParams(rtcPeerConnController, audio, video);
17329
17348
  });
17330
17349
  this.subscribeTo(this.vertoPing$, (vertoPing) => {
17350
+ this._lastVertoPings.set(vertoPing.callID, vertoPing);
17331
17351
  this.attachManager.refresh(this.buildAttachableCall());
17332
17352
  this.sendVertoPong(vertoPing);
17333
17353
  });
17354
+ this.subscribeTo(this.webRtcCallSession.clientSession.authenticated$.pipe((0, import_cjs$15.skip)(1), (0, import_cjs$15.filter)(Boolean)), () => {
17355
+ this._lastVertoPings.forEach((vertoPing) => void this.sendVertoPong(vertoPing));
17356
+ });
17334
17357
  }
17335
17358
  /**
17336
17359
  * An auxiliary-leg failure must never destroy the call; main-leg and
@@ -20867,6 +20890,10 @@ var ClientSessionManager = class extends Destroyable {
20867
20890
  await (0, import_cjs$7.firstValueFrom)(this.authenticated$.pipe((0, import_cjs$7.takeUntil)(this.destroyed$), (0, import_cjs$7.filter)(Boolean), (0, import_cjs$7.take)(1), (0, import_cjs$7.timeout)({ first: 15e3 })));
20868
20891
  }
20869
20892
  async handleAuthenticationError(error) {
20893
+ if (error instanceof TransportConnectionError) {
20894
+ logger$9.debug("[Session] Socket closed during authentication, waiting for reconnect");
20895
+ return;
20896
+ }
20870
20897
  logger$9.error("Authentication error:", error);
20871
20898
  const isRecoverableAuthError$1 = error instanceof JSONRPCError && (error.code === RPC_ERROR_REQUESTER_VALIDATION_FAILED || error.code === RPC_ERROR_INVALID_PARAMS || error.code === RPC_ERROR_AUTHENTICATION_FAILED);
20872
20899
  const hasStoredState = await (0, import_cjs$7.firstValueFrom)(this.authorizationState$.pipe((0, import_cjs$7.take)(1))) !== void 0;
@@ -21006,7 +21033,9 @@ var ClientSessionManager = class extends Destroyable {
21006
21033
  protocol: persistedParams.protocol
21007
21034
  } : {}
21008
21035
  });
21009
- const response = await (0, import_cjs$7.lastValueFrom)((0, import_cjs$7.from)(this.transport.execute(rpcConnectRequest)).pipe(throwOnRPCError(), (0, import_cjs$7.map)((res) => res.result), (0, import_cjs$7.filter)(isRPCConnectResult), (0, import_cjs$7.tap)(() => {
21036
+ if (this.transport.connectionStatus !== "connected") throw new TransportConnectionError("WebSocket closed during authentication");
21037
+ const socketClosed$ = this.transport.connectionStatus$.pipe((0, import_cjs$7.skip)(1), (0, import_cjs$7.filter)((status) => status === "reconnecting" || status === "disconnected"), (0, import_cjs$7.take)(1), (0, import_cjs$7.switchMap)(() => (0, import_cjs$7.throwError)(() => new TransportConnectionError("WebSocket closed during authentication"))));
21038
+ const response = await (0, import_cjs$7.lastValueFrom)((0, import_cjs$7.race)((0, import_cjs$7.from)(this.transport.execute(rpcConnectRequest)), socketClosed$).pipe(throwOnRPCError(), (0, import_cjs$7.map)((res) => res.result), (0, import_cjs$7.filter)(isRPCConnectResult), (0, import_cjs$7.tap)(() => {
21010
21039
  logger$9.debug("[Session] Response passed filter, processing authentication result");
21011
21040
  }), (0, import_cjs$7.take)(1), (0, import_cjs$7.catchError)((err) => {
21012
21041
  logger$9.error("[Session] Authentication RPC failed:", err);
@@ -21021,6 +21050,7 @@ var ClientSessionManager = class extends Destroyable {
21021
21050
  this._authorization$.next(response.authorization);
21022
21051
  if (response.authorization.cnf?.jkt) this._wasClientBound = true;
21023
21052
  this._iceServers$.next(response.ice_servers ?? []);
21053
+ this.transport.setAuthenticated();
21024
21054
  this._authState$.next({ kind: "authenticated" });
21025
21055
  logger$9.debug("[Session] Authentication completed successfully");
21026
21056
  }
@@ -22217,6 +22247,7 @@ var WebSocketController = class WebSocketController extends Destroyable {
22217
22247
  }
22218
22248
  handleClose(_event) {
22219
22249
  this.clearConnectionTimeout();
22250
+ this.messageQueue = [];
22220
22251
  if (this.shouldReconnect) {
22221
22252
  this._status$.next("reconnecting");
22222
22253
  this.scheduleReconnection();
@@ -22286,6 +22317,9 @@ var WebSocketController = class WebSocketController extends Destroyable {
22286
22317
  function isSignalwirePingRequest(value) {
22287
22318
  return isJSONRPCRequest(value) && value.method === "signalwire.ping";
22288
22319
  }
22320
+ function isSignalwireConnectRequest(value) {
22321
+ return isJSONRPCRequest(value) && value.method === "signalwire.connect";
22322
+ }
22289
22323
 
22290
22324
  //#endregion
22291
22325
  //#region src/managers/TransportManager.ts
@@ -22300,6 +22334,10 @@ var TransportManager = class extends Destroyable {
22300
22334
  this.protocol$ = this.createReplaySubject(1);
22301
22335
  this.isConnecting = false;
22302
22336
  this.isConnected = false;
22337
+ this.socketStatus = "disconnected";
22338
+ this.sessionAuthenticated = false;
22339
+ this.heldRequests = [];
22340
+ this.serverPingWatch = 0;
22303
22341
  this.ackEvent = () => {
22304
22342
  return (0, import_cjs$2.tap)((message) => {
22305
22343
  if (isSignalwireRequest(message)) try {
@@ -22315,6 +22353,7 @@ var TransportManager = class extends Destroyable {
22315
22353
  if (isSignalwirePingRequest(message)) {
22316
22354
  try {
22317
22355
  logger$2.debug("[Transport] Received ping, sending pong", { pingId: message.id });
22356
+ if (this.sessionAuthenticated) this.armServerPingWatchdog();
22318
22357
  this.send(RPCPingResponse(message.id));
22319
22358
  } catch (error) {
22320
22359
  logger$2.error("[Transport] Failed to send ping response:", error);
@@ -22352,6 +22391,14 @@ var TransportManager = class extends Destroyable {
22352
22391
  this.subscribeTo(this._webSocketConnections.errors$, (error) => {
22353
22392
  this.onError?.(error);
22354
22393
  });
22394
+ this.subscribeTo(this._webSocketConnections.status$, (status) => {
22395
+ this.socketStatus = status;
22396
+ if (status !== "connected") {
22397
+ this.sessionAuthenticated = false;
22398
+ this.clearServerPingWatchdog();
22399
+ }
22400
+ if (status === "disconnected") this.heldRequests = [];
22401
+ });
22355
22402
  this.initialized$ = (0, import_cjs$2.defer)(() => (0, import_cjs$2.from)(this._init())).pipe((0, import_cjs$2.shareReplay)(1), (0, import_cjs$2.takeUntil)(this.destroyed$));
22356
22403
  this._jsonRPCMessage$ = this._webSocketConnections.incomingMessages$.pipe((0, import_cjs$2.map)((event) => {
22357
22404
  try {
@@ -22380,6 +22427,9 @@ var TransportManager = class extends Destroyable {
22380
22427
  get connectionStatus$() {
22381
22428
  return this._webSocketConnections.status$;
22382
22429
  }
22430
+ get connectionStatus() {
22431
+ return this.socketStatus;
22432
+ }
22383
22433
  async connect() {
22384
22434
  if (this.isConnecting || this.isConnected) {
22385
22435
  logger$2.warn("[Transport] Already connecting or connected");
@@ -22424,16 +22474,45 @@ var TransportManager = class extends Destroyable {
22424
22474
  }
22425
22475
  async execute(request, options) {
22426
22476
  this.send(request);
22427
- return new PendingRPC(request, this._jsonRPCResponse$, options).promise;
22477
+ const pending = new PendingRPC(request, this._jsonRPCResponse$, options).promise;
22478
+ pending.catch(() => {
22479
+ this.heldRequests = this.heldRequests.filter((held) => held.id !== request.id);
22480
+ });
22481
+ return pending;
22428
22482
  }
22429
22483
  send(message) {
22430
22484
  const payload = JSON.stringify(message);
22485
+ if (isJSONRPCResponse(message)) {
22486
+ if (this.socketStatus !== "connected") {
22487
+ logger$2.debug("[Transport] Dropping response while disconnected", { id: message.id });
22488
+ return;
22489
+ }
22490
+ } else if (!this.sessionAuthenticated && !isSignalwireConnectRequest(message)) {
22491
+ this.heldRequests.push({
22492
+ id: message.id,
22493
+ payload
22494
+ });
22495
+ return;
22496
+ }
22431
22497
  this._outgoingMessages$.next(payload);
22432
22498
  }
22499
+ /**
22500
+ * Called by the session when signalwire.connect succeeds on the current
22501
+ * socket. Sends the requests held since the socket opened, in order.
22502
+ */
22503
+ setAuthenticated() {
22504
+ if (this.socketStatus !== "connected") return;
22505
+ this.sessionAuthenticated = true;
22506
+ this.armServerPingWatchdog();
22507
+ const held = this.heldRequests;
22508
+ this.heldRequests = [];
22509
+ held.forEach(({ payload }) => this._outgoingMessages$.next(payload));
22510
+ }
22433
22511
  disconnect() {
22434
22512
  logger$2.debug("[Transport] Disconnecting");
22435
22513
  this.isConnected = false;
22436
22514
  this.isConnecting = false;
22515
+ this.clearServerPingWatchdog();
22437
22516
  this._webSocketConnections.disconnect();
22438
22517
  }
22439
22518
  destroy() {
@@ -22442,6 +22521,28 @@ var TransportManager = class extends Destroyable {
22442
22521
  super.destroy();
22443
22522
  this._webSocketConnections.destroy();
22444
22523
  }
22524
+ armServerPingWatchdog() {
22525
+ this.clearServerPingWatchdog();
22526
+ const watch = this.serverPingWatch;
22527
+ this.serverPingTimer = setTimeout(() => void this.probeServer(watch), SERVER_PING_TIMEOUT_MS);
22528
+ }
22529
+ /** Bumps the watch so a probe still in flight no longer acts on its result. */
22530
+ clearServerPingWatchdog() {
22531
+ clearTimeout(this.serverPingTimer);
22532
+ this.serverPingTimer = void 0;
22533
+ this.serverPingWatch += 1;
22534
+ }
22535
+ async probeServer(watch) {
22536
+ logger$2.debug(`[Transport] No signalwire.ping for ${SERVER_PING_TIMEOUT_MS}ms, probing the server`);
22537
+ try {
22538
+ await this.execute(RPCPing(), { timeoutMs: SERVER_PING_PROBE_TIMEOUT_MS });
22539
+ if (watch === this.serverPingWatch) this.armServerPingWatchdog();
22540
+ } catch (error) {
22541
+ if (watch !== this.serverPingWatch) return;
22542
+ logger$2.warn("[Transport] Server did not answer the ping probe, replacing the socket", error);
22543
+ this.reconnect();
22544
+ }
22545
+ }
22445
22546
  async _loadProtocolFromStorage() {
22446
22547
  try {
22447
22548
  const storedProtocol = await this.storage.getItem(this.protocolKey);
@@ -23147,7 +23248,7 @@ var SignalWire = class extends Destroyable {
23147
23248
  logger$1.warn("[SignalWire] Failed to initialize VisibilityController:", error);
23148
23249
  }
23149
23250
  try {
23150
- this._diagnosticsCollector = new DiagnosticsCollector({ sdkVersion: "4.0.0-rc.3" });
23251
+ this._diagnosticsCollector = new DiagnosticsCollector({ sdkVersion: "4.0.0-rc.4" });
23151
23252
  } catch (error) {
23152
23253
  logger$1.warn("[SignalWire] Failed to initialize DiagnosticsCollector:", error);
23153
23254
  }
@@ -23702,7 +23803,7 @@ var StaticCredentialProvider = class {
23702
23803
  /**
23703
23804
  * Library version from package.json, injected at build time.
23704
23805
  */
23705
- const version = "4.0.0-rc.3";
23806
+ const version = "4.0.0-rc.4";
23706
23807
  /**
23707
23808
  * Flag indicating the library has been loaded and is ready to use.
23708
23809
  * For UMD builds: `window.SignalWire.ready`
@@ -23724,7 +23825,7 @@ const ready = true;
23724
23825
  */
23725
23826
  const emitReadyEvent = () => {
23726
23827
  if (typeof window !== "undefined") {
23727
- const event = new CustomEvent("signalwire:js:ready", { detail: { version: "4.0.0-rc.3" } });
23828
+ const event = new CustomEvent("signalwire:js:ready", { detail: { version: "4.0.0-rc.4" } });
23728
23829
  window.dispatchEvent(event);
23729
23830
  }
23730
23831
  };