nodejs-insta-private-api-mqt 1.3.92 → 1.4.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.
@@ -1036,8 +1036,48 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
1036
1036
  // actually connect the mqtt client (this will emit connect when done)
1037
1037
  await this._mqtt.connect();
1038
1038
  // Commands uses mqtt client; Commands.updateSubscriptions has been set to use qos 0.
1039
- this.commands = new commands_1.Commands(this._mqtt);
1040
- // Notify higher-level code that we are connected
1039
+ // Commands uses mqtt client; wrap commands to ensure online + serialize calls (prevents "idle-dead" send failures)
1040
+
1041
+ this._rawCommands = new commands_1.Commands(this._mqtt);
1042
+
1043
+ // Simple promise-chain queue to avoid concurrent publishes racing during reconnects
1044
+
1045
+ this._cmdQueue = this._cmdQueue || Promise.resolve();
1046
+
1047
+ const runQueued = (fn) => {
1048
+
1049
+ const task = async () => {
1050
+
1051
+ await this.ensureOnline(); // ping/reconnect if needed
1052
+
1053
+ return await fn();
1054
+
1055
+ };
1056
+
1057
+ // keep the queue alive even if a task fails
1058
+
1059
+ this._cmdQueue = this._cmdQueue.then(task, task);
1060
+
1061
+ return this._cmdQueue;
1062
+
1063
+ };
1064
+
1065
+ // Proxy: every command method becomes queued + guarded
1066
+
1067
+ this.commands = new Proxy(this._rawCommands, {
1068
+
1069
+ get: (target, prop) => {
1070
+
1071
+ const value = target[prop];
1072
+
1073
+ if (typeof value !== 'function') return value;
1074
+
1075
+ return (...args) => runQueued(() => value.apply(target, args));
1076
+
1077
+ }
1078
+
1079
+ });
1080
+ // Notify higher-level code that we are connected
1041
1081
  this._mqttConnected = true;
1042
1082
  this._connectInProgress = false;
1043
1083
  this.emit('connected');
@@ -1057,11 +1097,12 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
1057
1097
  this.on('iris', updateLast);
1058
1098
  const KEEPALIVE_FOREGROUND_MS = (this.initOptions && this.initOptions.keepaliveForegroundMs) ? this.initOptions.keepaliveForegroundMs : 45000; // 45s keepalive pulse
1059
1099
  const MESSAGE_SYNC_REFRESH_MS = (this.initOptions && this.initOptions.messageSyncRefreshMs) ? this.initOptions.messageSyncRefreshMs : 300000;
1060
- const TRAFFIC_INACTIVITY_MS = (this.initOptions && this.initOptions.trafficInactivityMs) ? this.initOptions.trafficInactivityMs : 180000; // 3 min default for faster recovery
1100
+ const TRAFFIC_INACTIVITY_MS = (this.initOptions && this.initOptions.trafficInactivityMs) ? this.initOptions.trafficInactivityMs : 120000; // 2 min default for faster recovery
1061
1101
  const HEARTBEAT_MS = (this.initOptions && this.initOptions.heartbeatMs) ? this.initOptions.heartbeatMs : 90000; // 90s mobile-like heartbeat
1062
1102
  try {
1063
1103
  if (this._foregroundTimer)
1064
1104
  clearInterval(this._foregroundTimer);
1105
+ this._foregroundConsecutiveFailures = 0;
1065
1106
  this._foregroundTimer = setInterval(async () => {
1066
1107
  try {
1067
1108
  if (!this.commands)
@@ -1071,10 +1112,18 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
1071
1112
  data: { foreground: true }
1072
1113
  });
1073
1114
  this._lastMessageAt = Date.now();
1115
+ this._foregroundConsecutiveFailures = 0;
1074
1116
  this.realtimeDebug('[KEEPALIVE] Foreground pulse sent.');
1075
1117
  }
1076
1118
  catch (e) {
1077
- this.realtimeDebug('[KEEPALIVE] Foreground pulse failed:', e?.message || e);
1119
+ this._foregroundConsecutiveFailures = (this._foregroundConsecutiveFailures || 0) + 1;
1120
+ this.realtimeDebug(`[KEEPALIVE] Foreground pulse failed (${this._foregroundConsecutiveFailures}):`, e?.message || e);
1121
+ if (this._foregroundConsecutiveFailures >= 3 && !this._reconnectInProgress && !this.safeDisconnect) {
1122
+ this.realtimeDebug('[KEEPALIVE] 3 consecutive foreground failures - connection likely dead, triggering reconnect');
1123
+ this._mqttConnected = false;
1124
+ this._foregroundConsecutiveFailures = 0;
1125
+ try { await this._attemptReconnectSafely(e); } catch(re) {}
1126
+ }
1078
1127
  }
1079
1128
  }, KEEPALIVE_FOREGROUND_MS + Math.floor(Math.random() * 5000));
1080
1129
  }
@@ -1113,6 +1162,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
1113
1162
  if (this._mqtt && typeof this._mqtt.ping === 'function') {
1114
1163
  await this._mqtt.ping();
1115
1164
  this._lastMessageAt = Date.now();
1165
+ this._lastServerTrafficAt = Date.now();
1116
1166
  this.realtimeDebug('[WATCHDOG] Ping succeeded, connection is alive.');
1117
1167
  return;
1118
1168
  }
@@ -1140,10 +1190,16 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
1140
1190
  try {
1141
1191
  await this._mqtt.ping();
1142
1192
  this._lastMessageAt = Date.now();
1193
+ this._lastServerTrafficAt = Date.now();
1143
1194
  this.realtimeDebug('[HEARTBEAT] mqtt.ping() ok.');
1144
1195
  }
1145
1196
  catch (e) {
1146
1197
  this.realtimeDebug('[HEARTBEAT] mqtt.ping failed:', e?.message || e);
1198
+ if (!this._reconnectInProgress && !this.safeDisconnect) {
1199
+ this.realtimeDebug('[HEARTBEAT] Ping failed - triggering reconnect');
1200
+ this._mqttConnected = false;
1201
+ try { await this._attemptReconnectSafely(e); } catch(re) {}
1202
+ }
1147
1203
  }
1148
1204
  }
1149
1205
  }
@@ -1155,6 +1211,33 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
1155
1211
  catch (e) {
1156
1212
  this.realtimeDebug('[HEARTBEAT] Could not start heartbeat timer:', e?.message || e);
1157
1213
  }
1214
+ try {
1215
+ if (this._reconnectSafetyNet)
1216
+ clearInterval(this._reconnectSafetyNet);
1217
+ this._reconnectSafetyNet = setInterval(async () => {
1218
+ try {
1219
+ if (this._reconnectInProgress && this._reconnectStartedAt) {
1220
+ const stuckMs = Date.now() - this._reconnectStartedAt;
1221
+ if (stuckMs > 10 * 60 * 1000) {
1222
+ this.realtimeDebug('[SAFETY_NET] _reconnectInProgress stuck for >10min — force resetting and retrying');
1223
+ this._reconnectInProgress = false;
1224
+ this._reconnectStartedAt = null;
1225
+ try { await this._attemptReconnectSafely(); } catch(e) {}
1226
+ }
1227
+ }
1228
+ if (!this._mqttConnected && !this._reconnectInProgress && !this.safeDisconnect && !this._connectInProgress) {
1229
+ this.realtimeDebug('[SAFETY_NET] Not connected and no reconnect in progress — triggering reconnect');
1230
+ try { await this._attemptReconnectSafely(); } catch(e) {}
1231
+ }
1232
+ }
1233
+ catch (e) {
1234
+ this.realtimeDebug('[SAFETY_NET] fault:', e?.message || e);
1235
+ }
1236
+ }, 2 * 60 * 1000);
1237
+ }
1238
+ catch (e) {
1239
+ this.realtimeDebug('[SAFETY_NET] Could not start safety net timer:', e?.message || e);
1240
+ }
1158
1241
  }
1159
1242
  catch (e) {
1160
1243
  this.realtimeDebug('[WATCHDOG] initialization error:', e?.message || e);
@@ -1558,18 +1641,33 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
1558
1641
  */
1559
1642
  async _attemptReconnectSafely(lastError) {
1560
1643
  if (this._reconnectInProgress) {
1561
- this.realtimeDebug('[RECONNECT] Skipped reconnect already in progress');
1562
- return;
1644
+ const stuckMs = Date.now() - (this._reconnectStartedAt || Date.now());
1645
+ if (stuckMs > 5 * 60 * 1000) {
1646
+ this.realtimeDebug('[RECONNECT] _reconnectInProgress stuck for >5min — resetting flag');
1647
+ this._reconnectInProgress = false;
1648
+ } else {
1649
+ this.realtimeDebug('[RECONNECT] Skipped — reconnect already in progress');
1650
+ return;
1651
+ }
1563
1652
  }
1564
1653
  if (this._mqttConnected) {
1565
- this.realtimeDebug('[RECONNECT] Skipped — MQTT is already connected');
1566
- return;
1654
+ try {
1655
+ if (this._mqtt && typeof this._mqtt.ping === 'function') {
1656
+ await this._mqtt.ping();
1657
+ this.realtimeDebug('[RECONNECT] Skipped — MQTT is already connected and ping succeeded');
1658
+ return;
1659
+ }
1660
+ } catch(e) {
1661
+ this.realtimeDebug('[RECONNECT] MQTT marked connected but ping failed — proceeding with reconnect');
1662
+ this._mqttConnected = false;
1663
+ }
1567
1664
  }
1568
1665
  if (this.safeDisconnect) {
1569
1666
  this.realtimeDebug('[RECONNECT] Skipped — safe disconnect active');
1570
1667
  return;
1571
1668
  }
1572
1669
  this._reconnectInProgress = true;
1670
+ this._reconnectStartedAt = Date.now();
1573
1671
  if (this._reconnectTimeoutId) {
1574
1672
  clearTimeout(this._reconnectTimeoutId);
1575
1673
  this._reconnectTimeoutId = null;
@@ -1581,7 +1679,7 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
1581
1679
  }
1582
1680
  try {
1583
1681
  let attempt = 0;
1584
- const maxAttempts = 20;
1682
+ const maxAttempts = 999;
1585
1683
  let lastErrorType = lastError ? (this.errorHandler ? this.errorHandler.classifyError(lastError) : 'unknown') : 'unknown';
1586
1684
  while (attempt < maxAttempts) {
1587
1685
  attempt++;
@@ -1960,6 +2058,11 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
1960
2058
  clearInterval(this._activeKeepaliveTimer);
1961
2059
  this._activeKeepaliveTimer = null;
1962
2060
  }
2061
+ // Clear reconnect safety net
2062
+ if (this._reconnectSafetyNet) {
2063
+ clearInterval(this._reconnectSafetyNet);
2064
+ this._reconnectSafetyNet = null;
2065
+ }
1963
2066
  // clear credential refresh timer
1964
2067
  if (this._mqttAuthRefreshTimer) {
1965
2068
  try {
@@ -2102,5 +2205,80 @@ class RealtimeClient extends eventemitter3_1.EventEmitter {
2102
2205
  this.realtimeDebug('[ACTIVE_QUERY] could not start keepalive timer:', e?.message || e);
2103
2206
  }
2104
2207
  }
2208
+ /**
2209
+ * ensureOnline()
2210
+ * - fixes the classic "idle -> socket half-dead -> sends fail" issue
2211
+ * - pings the broker; if ping fails/timeouts, triggers a safe reconnect and waits until connected
2212
+ */
2213
+ async ensureOnline(options = {}) {
2214
+ const timeoutMs = typeof options.timeoutMs === 'number' ? options.timeoutMs : 15000;
2215
+ const pingTimeoutMs = typeof options.pingTimeoutMs === 'number' ? options.pingTimeoutMs : 7000;
2216
+ if (this.safeDisconnect) {
2217
+ throw new Error('RealtimeClient is in safeDisconnect state');
2218
+ }
2219
+ // If a connect/reconnect is already happening, just wait for it
2220
+ if (this._connectInProgress || this._reconnectInProgress) {
2221
+ await this._waitForConnected(timeoutMs);
2222
+ return true;
2223
+ }
2224
+ // If we are not connected, reconnect and wait
2225
+ if (!this._mqtt || !this._mqttConnected) {
2226
+ try {
2227
+ await this._attemptReconnectSafely();
2228
+ } catch (e) { /* ignore */ }
2229
+ await this._waitForConnected(timeoutMs);
2230
+ return true;
2231
+ }
2232
+ // If we are connected, do a lightweight ping to detect half-dead sockets
2233
+ if (this._mqtt && typeof this._mqtt.ping === 'function') {
2234
+ try {
2235
+ await Promise.race([
2236
+ this._mqtt.ping(),
2237
+ this._delay(pingTimeoutMs).then(() => { throw new Error('mqtt.ping timeout'); })
2238
+ ]);
2239
+ this._lastMessageAt = Date.now();
2240
+ this._lastServerTrafficAt = Date.now();
2241
+ return true;
2242
+ } catch (e) {
2243
+ // Ping failed -> force reconnect
2244
+ this.realtimeDebug('[ensureOnline] ping failed -> reconnect', e?.message || e);
2245
+ this._mqttConnected = false;
2246
+ try { await this._attemptReconnectSafely(e); } catch (re) { /* ignore */ }
2247
+ await this._waitForConnected(timeoutMs);
2248
+ return true;
2249
+ }
2250
+ }
2251
+ return true;
2252
+ }
2253
+ _delay(ms) {
2254
+ return new Promise((resolve) => setTimeout(resolve, ms));
2255
+ }
2256
+ _waitForConnected(timeoutMs) {
2257
+ return new Promise((resolve, reject) => {
2258
+ if (this._mqttConnected) return resolve(true);
2259
+ let done = false;
2260
+ const finishOk = () => {
2261
+ if (done) return;
2262
+ done = true;
2263
+ cleanup();
2264
+ resolve(true);
2265
+ };
2266
+ const finishErr = (err) => {
2267
+ if (done) return;
2268
+ done = true;
2269
+ cleanup();
2270
+ reject(err);
2271
+ };
2272
+ const timer = setTimeout(() => finishErr(new Error('Timed out waiting for MQTT connection')), timeoutMs);
2273
+ const cleanup = () => {
2274
+ try { clearTimeout(timer); } catch (_) { }
2275
+ try { this.off('connected', finishOk); } catch (_) { }
2276
+ try { this.off('mqtt_connected', finishOk); } catch (_) { }
2277
+ };
2278
+ try { this.on('connected', finishOk); } catch (_) { }
2279
+ try { this.on('mqtt_connected', finishOk); } catch (_) { }
2280
+ });
2281
+ }
2282
+
2105
2283
  }
2106
2284
  exports.RealtimeClient = RealtimeClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodejs-insta-private-api-mqt",
3
- "version": "1.3.92",
3
+ "version": "1.4.1",
4
4
  "description": "Complete Instagram MQTT protocol with full-featured REALTIME and REST API — all in one project.",
5
5
 
6
6
  "main": "dist/dist/index.js",