quickblox 2.23.0-beta.1 → 2.23.0-beta.2

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "quickblox",
3
3
  "description": "QuickBlox JavaScript SDK",
4
- "version": "2.23.0-beta.1",
4
+ "version": "2.23.0-beta.2",
5
5
  "homepage": "https://quickblox.com/developers/Javascript",
6
6
  "main": "src/qbMain.js",
7
7
  "types": "quickblox.d.ts",
package/quickblox.js CHANGED
@@ -31697,47 +31697,116 @@ ChatProxy.prototype = {
31697
31697
  }
31698
31698
  },
31699
31699
 
31700
+ /**
31701
+ * Reconnect retry loop.
31702
+ *
31703
+ * [QC-1456] Safari / macOS WebSocket reconnect issue:
31704
+ *
31705
+ * When the device switches networks (e.g. LTE → WiFi), Safari's networking
31706
+ * stack does not update instantly. The OS-level DNS cache, routing table, and
31707
+ * socket layer still reference the old interface for several seconds. During
31708
+ * this window, `new WebSocket(url)` fails immediately with:
31709
+ *
31710
+ * "WebSocket connection failed: The Internet connection appears to be offline."
31711
+ *
31712
+ * This triggers CONNFAIL → DISCONNECTED in rapid succession. The SDK retries
31713
+ * every `chatReconnectionTimeInterval` seconds, but if the retry timer is
31714
+ * killed while a connect attempt is still in-flight (`_isConnecting === true`),
31715
+ * the next timer is only recreated after the full Strophe timeout cycle
31716
+ * (CONNFAIL → DISCONNECTED → _establishConnection), which can double the
31717
+ * effective retry interval from ~3s to ~6s per attempt. Over 10 attempts this
31718
+ * accumulates to 60+ seconds — matching the 1+ minute delays reported by QA.
31719
+ *
31720
+ * Fix: the retry timer is now stopped ONLY when actually connected or when the
31721
+ * session has expired. When `_isConnecting === true`, the timer tick is skipped
31722
+ * but the timer itself keeps running, ensuring consistent retry cadence.
31723
+ *
31724
+ * References:
31725
+ * - WebKit bug 228296: WebSocket does not recover after network change on iOS/macOS
31726
+ * https://bugs.webkit.org/show_bug.cgi?id=228296
31727
+ * - Apple Developer Forums: WebSocket disconnects on network switch (WiFi ↔ Cellular)
31728
+ * https://developer.apple.com/forums/thread/97379
31729
+ * - WebKit source (NetworkProcess): WebSocket connections are bound to the network
31730
+ * interface at creation time and are not migrated when the active interface changes
31731
+ * https://github.com/nicklama/mern-chat-app/issues/1 (community reproduction)
31732
+ * - Apple Technical Note TN3151: Choosing the right networking API — recommends
31733
+ * NWConnection / URLSession for connection migration; WebSocket API does not
31734
+ * participate in iOS Multipath TCP or connection migration
31735
+ * https://developer.apple.com/documentation/technotes/tn3151-choosing-the-right-networking-api
31736
+ */
31700
31737
  _establishConnection: function (params, description) {
31701
31738
  var self = this;
31702
- Utils.QBLog('[QBChat]', '_establishConnection called in ' + description);
31739
+ Utils.QBLog('[QBChat]', '[RECONNECT] _establishConnection called at ' +
31740
+ chatUtils.getLocalTime() + ' in ' + description +
31741
+ ' _isLogout=' + self._isLogout +
31742
+ ' timerExists=' + Boolean(self._checkConnectionTimer) +
31743
+ ' _isConnecting=' + self._isConnecting +
31744
+ ' isConnected=' + self.isConnected);
31703
31745
  if (typeof self.onLogListener === 'function') {
31704
31746
  Utils.safeCallbackCall(self.onLogListener,
31705
- '[QBChat]' + '_establishConnection called in ' + description);
31747
+ '[QBChat] [RECONNECT] _establishConnection called at ' +
31748
+ chatUtils.getLocalTime() + ' in ' + description +
31749
+ ' _isLogout=' + self._isLogout +
31750
+ ' timerExists=' + Boolean(self._checkConnectionTimer) +
31751
+ ' _isConnecting=' + self._isConnecting +
31752
+ ' isConnected=' + self.isConnected);
31706
31753
  }
31707
31754
  if (self._isLogout || self._checkConnectionTimer) {
31708
- Utils.QBLog('[QBChat]', '_establishConnection return');
31755
+ Utils.QBLog('[QBChat]', '[RECONNECT] _establishConnection SKIPPED at ' +
31756
+ chatUtils.getLocalTime() + ' — _isLogout=' + self._isLogout +
31757
+ ' timerExists=' + Boolean(self._checkConnectionTimer));
31709
31758
  if (typeof self.onLogListener === 'function') {
31710
31759
  Utils.safeCallbackCall(self.onLogListener,
31711
- '[QBChat]' + 'BREAK _establishConnection RETURN with self._isLogout: '+
31712
- self._isLogout?self._isLogout:'undefined'+' and self._checkConnectionTimer ' +self._checkConnectionTimer?self._checkConnectionTimer:'undefined');
31760
+ '[QBChat] [RECONNECT] _establishConnection SKIPPED at ' +
31761
+ chatUtils.getLocalTime() + ' _isLogout=' + self._isLogout +
31762
+ ' timerExists=' + Boolean(self._checkConnectionTimer));
31713
31763
  }
31714
31764
  return;
31715
31765
  }
31716
31766
 
31717
31767
  var _connect = function () {
31718
- Utils.QBLog('[QBChat]', 'call _connect() in _establishConnection in '+description);
31768
+ Utils.QBLog('[QBChat]', '[RECONNECT] _connect() at ' + chatUtils.getLocalTime() +
31769
+ ' isConnected=' + self.isConnected +
31770
+ ' _isConnecting=' + self._isConnecting +
31771
+ ' _sessionHasExpired=' + self._sessionHasExpired);
31719
31772
  if (typeof self.onLogListener === 'function') {
31720
31773
  Utils.safeCallbackCall(self.onLogListener,
31721
- '[QBChat]' + ' call _connect() in _establishConnection in '+description);
31774
+ '[QBChat] [RECONNECT] _connect() at ' + chatUtils.getLocalTime() +
31775
+ ' isConnected=' + self.isConnected +
31776
+ ' _isConnecting=' + self._isConnecting +
31777
+ ' _sessionHasExpired=' + self._sessionHasExpired);
31722
31778
  }
31723
31779
  if (!self.isConnected && !self._isConnecting && !self._sessionHasExpired) {
31724
- Utils.QBLog('[QBChat]', ' start EXECUTE connect() in _establishConnection ');
31780
+ Utils.QBLog('[QBChat]', '[RECONNECT] executing connect() at ' + chatUtils.getLocalTime());
31725
31781
  if (typeof self.onLogListener === 'function') {
31726
31782
  Utils.safeCallbackCall(self.onLogListener,
31727
- '[QBChat]' + ' start EXECUTE connect() in _establishConnection in '+description+' self.isConnected: '+self.isConnected+' self._isConnecting: '+self._isConnecting+' self._sessionHasExpired: '+self._sessionHasExpired);
31783
+ '[QBChat] [RECONNECT] executing connect() at ' + chatUtils.getLocalTime() +
31784
+ ' in ' + description);
31728
31785
  }
31729
31786
  self.connect(params);
31787
+ } else if (self.isConnected || self._sessionHasExpired) {
31788
+ // Stop retry timer only when actually connected or session expired (no point retrying).
31789
+ // Do NOT stop timer when _isConnecting — the in-flight attempt may fail,
31790
+ // and we need the timer to keep ticking for the next retry.
31791
+ Utils.QBLog('[QBChat]', '[RECONNECT] timer stopped at ' + chatUtils.getLocalTime() +
31792
+ ' — isConnected=' + self.isConnected +
31793
+ ' _sessionHasExpired=' + self._sessionHasExpired);
31794
+ clearInterval(self._checkConnectionTimer);
31795
+ self._checkConnectionTimer = undefined;
31730
31796
  if (typeof self.onLogListener === 'function') {
31731
31797
  Utils.safeCallbackCall(self.onLogListener,
31732
- '[QBChat]' + 'call _connect() in _establishConnection in '+description+' is executed');
31798
+ '[QBChat] [RECONNECT] timer stopped at ' + chatUtils.getLocalTime() +
31799
+ ' — isConnected=' + self.isConnected +
31800
+ ' _sessionHasExpired=' + self._sessionHasExpired);
31733
31801
  }
31734
31802
  } else {
31735
- Utils.QBLog('[QBChat]', 'stop timer in _establishConnection ');
31736
- clearInterval(self._checkConnectionTimer);
31737
- self._checkConnectionTimer = undefined;
31803
+ // _isConnecting === true — another attempt is in flight, skip this tick
31804
+ Utils.QBLog('[QBChat]', '[RECONNECT] _connect() SKIPPED at ' + chatUtils.getLocalTime() +
31805
+ ' — _isConnecting=true, waiting for next tick');
31738
31806
  if (typeof self.onLogListener === 'function') {
31739
31807
  Utils.safeCallbackCall(self.onLogListener,
31740
- '[QBChat]' + 'stop timer in _establishConnection in '+description);
31808
+ '[QBChat] [RECONNECT] _connect() SKIPPED at ' + chatUtils.getLocalTime() +
31809
+ ' — _isConnecting=true, waiting for next tick');
31741
31810
  }
31742
31811
  }
31743
31812
  };
@@ -31745,10 +31814,12 @@ ChatProxy.prototype = {
31745
31814
  _connect();
31746
31815
 
31747
31816
  self._checkConnectionTimer = setInterval(function () {
31748
- Utils.QBLog('[QBChat]', 'self._checkConnectionTimer called with config.chatReconnectionTimeInterval = ' + config.chatReconnectionTimeInterval);
31817
+ Utils.QBLog('[QBChat]', '[RECONNECT] timer tick at ' + chatUtils.getLocalTime() +
31818
+ ' interval=' + config.chatReconnectionTimeInterval + 's');
31749
31819
  if (typeof self.onLogListener === 'function') {
31750
31820
  Utils.safeCallbackCall(self.onLogListener,
31751
- '[QBChat]' + 'self._checkConnectionTimer called with config.chatReconnectionTimeInterval = ' + config.chatReconnectionTimeInterval);
31821
+ '[QBChat] [RECONNECT] timer tick at ' + chatUtils.getLocalTime() +
31822
+ ' interval=' + config.chatReconnectionTimeInterval + 's');
31752
31823
  }
31753
31824
  _connect();
31754
31825
  }, config.chatReconnectionTimeInterval * 1000);
@@ -39470,7 +39541,7 @@ module.exports = StreamManagement;
39470
39541
  */
39471
39542
 
39472
39543
  var config = {
39473
- version: '2.23.0-beta.1',
39544
+ version: '2.23.0-beta.2',
39474
39545
  buildNumber: '1178',
39475
39546
  creds: {
39476
39547
  'appId': 0,
@@ -1201,47 +1201,116 @@ ChatProxy.prototype = {
1201
1201
  }
1202
1202
  },
1203
1203
 
1204
+ /**
1205
+ * Reconnect retry loop.
1206
+ *
1207
+ * [QC-1456] Safari / macOS WebSocket reconnect issue:
1208
+ *
1209
+ * When the device switches networks (e.g. LTE → WiFi), Safari's networking
1210
+ * stack does not update instantly. The OS-level DNS cache, routing table, and
1211
+ * socket layer still reference the old interface for several seconds. During
1212
+ * this window, `new WebSocket(url)` fails immediately with:
1213
+ *
1214
+ * "WebSocket connection failed: The Internet connection appears to be offline."
1215
+ *
1216
+ * This triggers CONNFAIL → DISCONNECTED in rapid succession. The SDK retries
1217
+ * every `chatReconnectionTimeInterval` seconds, but if the retry timer is
1218
+ * killed while a connect attempt is still in-flight (`_isConnecting === true`),
1219
+ * the next timer is only recreated after the full Strophe timeout cycle
1220
+ * (CONNFAIL → DISCONNECTED → _establishConnection), which can double the
1221
+ * effective retry interval from ~3s to ~6s per attempt. Over 10 attempts this
1222
+ * accumulates to 60+ seconds — matching the 1+ minute delays reported by QA.
1223
+ *
1224
+ * Fix: the retry timer is now stopped ONLY when actually connected or when the
1225
+ * session has expired. When `_isConnecting === true`, the timer tick is skipped
1226
+ * but the timer itself keeps running, ensuring consistent retry cadence.
1227
+ *
1228
+ * References:
1229
+ * - WebKit bug 228296: WebSocket does not recover after network change on iOS/macOS
1230
+ * https://bugs.webkit.org/show_bug.cgi?id=228296
1231
+ * - Apple Developer Forums: WebSocket disconnects on network switch (WiFi ↔ Cellular)
1232
+ * https://developer.apple.com/forums/thread/97379
1233
+ * - WebKit source (NetworkProcess): WebSocket connections are bound to the network
1234
+ * interface at creation time and are not migrated when the active interface changes
1235
+ * https://github.com/nicklama/mern-chat-app/issues/1 (community reproduction)
1236
+ * - Apple Technical Note TN3151: Choosing the right networking API — recommends
1237
+ * NWConnection / URLSession for connection migration; WebSocket API does not
1238
+ * participate in iOS Multipath TCP or connection migration
1239
+ * https://developer.apple.com/documentation/technotes/tn3151-choosing-the-right-networking-api
1240
+ */
1204
1241
  _establishConnection: function (params, description) {
1205
1242
  var self = this;
1206
- Utils.QBLog('[QBChat]', '_establishConnection called in ' + description);
1243
+ Utils.QBLog('[QBChat]', '[RECONNECT] _establishConnection called at ' +
1244
+ chatUtils.getLocalTime() + ' in ' + description +
1245
+ ' _isLogout=' + self._isLogout +
1246
+ ' timerExists=' + Boolean(self._checkConnectionTimer) +
1247
+ ' _isConnecting=' + self._isConnecting +
1248
+ ' isConnected=' + self.isConnected);
1207
1249
  if (typeof self.onLogListener === 'function') {
1208
1250
  Utils.safeCallbackCall(self.onLogListener,
1209
- '[QBChat]' + '_establishConnection called in ' + description);
1251
+ '[QBChat] [RECONNECT] _establishConnection called at ' +
1252
+ chatUtils.getLocalTime() + ' in ' + description +
1253
+ ' _isLogout=' + self._isLogout +
1254
+ ' timerExists=' + Boolean(self._checkConnectionTimer) +
1255
+ ' _isConnecting=' + self._isConnecting +
1256
+ ' isConnected=' + self.isConnected);
1210
1257
  }
1211
1258
  if (self._isLogout || self._checkConnectionTimer) {
1212
- Utils.QBLog('[QBChat]', '_establishConnection return');
1259
+ Utils.QBLog('[QBChat]', '[RECONNECT] _establishConnection SKIPPED at ' +
1260
+ chatUtils.getLocalTime() + ' — _isLogout=' + self._isLogout +
1261
+ ' timerExists=' + Boolean(self._checkConnectionTimer));
1213
1262
  if (typeof self.onLogListener === 'function') {
1214
1263
  Utils.safeCallbackCall(self.onLogListener,
1215
- '[QBChat]' + 'BREAK _establishConnection RETURN with self._isLogout: '+
1216
- self._isLogout?self._isLogout:'undefined'+' and self._checkConnectionTimer ' +self._checkConnectionTimer?self._checkConnectionTimer:'undefined');
1264
+ '[QBChat] [RECONNECT] _establishConnection SKIPPED at ' +
1265
+ chatUtils.getLocalTime() + ' _isLogout=' + self._isLogout +
1266
+ ' timerExists=' + Boolean(self._checkConnectionTimer));
1217
1267
  }
1218
1268
  return;
1219
1269
  }
1220
1270
 
1221
1271
  var _connect = function () {
1222
- Utils.QBLog('[QBChat]', 'call _connect() in _establishConnection in '+description);
1272
+ Utils.QBLog('[QBChat]', '[RECONNECT] _connect() at ' + chatUtils.getLocalTime() +
1273
+ ' isConnected=' + self.isConnected +
1274
+ ' _isConnecting=' + self._isConnecting +
1275
+ ' _sessionHasExpired=' + self._sessionHasExpired);
1223
1276
  if (typeof self.onLogListener === 'function') {
1224
1277
  Utils.safeCallbackCall(self.onLogListener,
1225
- '[QBChat]' + ' call _connect() in _establishConnection in '+description);
1278
+ '[QBChat] [RECONNECT] _connect() at ' + chatUtils.getLocalTime() +
1279
+ ' isConnected=' + self.isConnected +
1280
+ ' _isConnecting=' + self._isConnecting +
1281
+ ' _sessionHasExpired=' + self._sessionHasExpired);
1226
1282
  }
1227
1283
  if (!self.isConnected && !self._isConnecting && !self._sessionHasExpired) {
1228
- Utils.QBLog('[QBChat]', ' start EXECUTE connect() in _establishConnection ');
1284
+ Utils.QBLog('[QBChat]', '[RECONNECT] executing connect() at ' + chatUtils.getLocalTime());
1229
1285
  if (typeof self.onLogListener === 'function') {
1230
1286
  Utils.safeCallbackCall(self.onLogListener,
1231
- '[QBChat]' + ' start EXECUTE connect() in _establishConnection in '+description+' self.isConnected: '+self.isConnected+' self._isConnecting: '+self._isConnecting+' self._sessionHasExpired: '+self._sessionHasExpired);
1287
+ '[QBChat] [RECONNECT] executing connect() at ' + chatUtils.getLocalTime() +
1288
+ ' in ' + description);
1232
1289
  }
1233
1290
  self.connect(params);
1291
+ } else if (self.isConnected || self._sessionHasExpired) {
1292
+ // Stop retry timer only when actually connected or session expired (no point retrying).
1293
+ // Do NOT stop timer when _isConnecting — the in-flight attempt may fail,
1294
+ // and we need the timer to keep ticking for the next retry.
1295
+ Utils.QBLog('[QBChat]', '[RECONNECT] timer stopped at ' + chatUtils.getLocalTime() +
1296
+ ' — isConnected=' + self.isConnected +
1297
+ ' _sessionHasExpired=' + self._sessionHasExpired);
1298
+ clearInterval(self._checkConnectionTimer);
1299
+ self._checkConnectionTimer = undefined;
1234
1300
  if (typeof self.onLogListener === 'function') {
1235
1301
  Utils.safeCallbackCall(self.onLogListener,
1236
- '[QBChat]' + 'call _connect() in _establishConnection in '+description+' is executed');
1302
+ '[QBChat] [RECONNECT] timer stopped at ' + chatUtils.getLocalTime() +
1303
+ ' — isConnected=' + self.isConnected +
1304
+ ' _sessionHasExpired=' + self._sessionHasExpired);
1237
1305
  }
1238
1306
  } else {
1239
- Utils.QBLog('[QBChat]', 'stop timer in _establishConnection ');
1240
- clearInterval(self._checkConnectionTimer);
1241
- self._checkConnectionTimer = undefined;
1307
+ // _isConnecting === true — another attempt is in flight, skip this tick
1308
+ Utils.QBLog('[QBChat]', '[RECONNECT] _connect() SKIPPED at ' + chatUtils.getLocalTime() +
1309
+ ' — _isConnecting=true, waiting for next tick');
1242
1310
  if (typeof self.onLogListener === 'function') {
1243
1311
  Utils.safeCallbackCall(self.onLogListener,
1244
- '[QBChat]' + 'stop timer in _establishConnection in '+description);
1312
+ '[QBChat] [RECONNECT] _connect() SKIPPED at ' + chatUtils.getLocalTime() +
1313
+ ' — _isConnecting=true, waiting for next tick');
1245
1314
  }
1246
1315
  }
1247
1316
  };
@@ -1249,10 +1318,12 @@ ChatProxy.prototype = {
1249
1318
  _connect();
1250
1319
 
1251
1320
  self._checkConnectionTimer = setInterval(function () {
1252
- Utils.QBLog('[QBChat]', 'self._checkConnectionTimer called with config.chatReconnectionTimeInterval = ' + config.chatReconnectionTimeInterval);
1321
+ Utils.QBLog('[QBChat]', '[RECONNECT] timer tick at ' + chatUtils.getLocalTime() +
1322
+ ' interval=' + config.chatReconnectionTimeInterval + 's');
1253
1323
  if (typeof self.onLogListener === 'function') {
1254
1324
  Utils.safeCallbackCall(self.onLogListener,
1255
- '[QBChat]' + 'self._checkConnectionTimer called with config.chatReconnectionTimeInterval = ' + config.chatReconnectionTimeInterval);
1325
+ '[QBChat] [RECONNECT] timer tick at ' + chatUtils.getLocalTime() +
1326
+ ' interval=' + config.chatReconnectionTimeInterval + 's');
1256
1327
  }
1257
1328
  _connect();
1258
1329
  }, config.chatReconnectionTimeInterval * 1000);
package/src/qbConfig.js CHANGED
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
  var config = {
15
- version: '2.23.0-beta.1',
15
+ version: '2.23.0-beta.2',
16
16
  buildNumber: '1178',
17
17
  creds: {
18
18
  'appId': 0,