@aunucore/baileys 1.0.79 → 1.0.80

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.
@@ -25,6 +25,32 @@ const makeWASocket = (config) => {
25
25
  ...DEFAULT_CONNECTION_CONFIG.transactionOpts,
26
26
  ...(config?.transactionOpts ?? {})
27
27
  };
28
+ // AunuBaileys (1.0.80, HB-139 + HB-174 + addendum HB-139): the
29
+ // numeric connection config was spread raw above — an explicit
30
+ // undefined/NaN from the host (classically `Number(env.X)` of an
31
+ // unset variable) silently replaced the defaults, and every
32
+ // failure mode is silent: promiseTimeout(NaN) never settles (a
33
+ // half-open socket hangs forever), a NaN/0/negative
34
+ // keepAliveIntervalMs collapses setInterval to 1 ms (~850 ping
35
+ // IQs per second, and the dead-connection watchdog compares
36
+ // against NaN so it never fires), and a NaN/Infinity
37
+ // maxMsgRetryCount breaks both retry gates in opposite
38
+ // directions at once (retry receipts never capped, resends
39
+ // never sent). Sanitise once, here at the boundary: timeouts
40
+ // and intervals must be positive finite (else the documented
41
+ // default), the retry count must be finite (else the default),
42
+ // and qrTimeout — which has no Defaults entry (genPairQR falls
43
+ // back with `|| 60000` / `|| 20000`) — must be positive finite
44
+ // or absent (a negative value is truthy and would expire every
45
+ // QR instantly).
46
+ const positiveFiniteOr = (value, fallback) => (Number.isFinite(value) && value > 0 ? value : fallback);
47
+ newConfig.connectTimeoutMs = positiveFiniteOr(newConfig.connectTimeoutMs, DEFAULT_CONNECTION_CONFIG.connectTimeoutMs);
48
+ newConfig.defaultQueryTimeoutMs = positiveFiniteOr(newConfig.defaultQueryTimeoutMs, DEFAULT_CONNECTION_CONFIG.defaultQueryTimeoutMs);
49
+ newConfig.keepAliveIntervalMs = positiveFiniteOr(newConfig.keepAliveIntervalMs, DEFAULT_CONNECTION_CONFIG.keepAliveIntervalMs);
50
+ newConfig.maxMsgRetryCount = Number.isFinite(newConfig.maxMsgRetryCount) ? newConfig.maxMsgRetryCount : DEFAULT_CONNECTION_CONFIG.maxMsgRetryCount;
51
+ if (newConfig.qrTimeout !== undefined && !(Number.isFinite(newConfig.qrTimeout) && newConfig.qrTimeout > 0)) {
52
+ newConfig.qrTimeout = undefined;
53
+ }
28
54
  // If the user hasn't provided their own history sync function,
29
55
  // let's create a default one that respects the syncFullHistory flag.
30
56
  if (config.shouldSyncHistoryMessage === undefined) {
@@ -13,7 +13,15 @@
13
13
  * the receive path uses.
14
14
  */
15
15
  export const makeBoundedTTLCache = ({ ttlSeconds = 3600, maxEntries = 1000 } = {}) => {
16
- const ttlMs = Math.max(1, ttlSeconds * 1000);
16
+ // AunuBaileys (1.0.80, HB-145): NaN/Infinity config defeated
17
+ // both bounds — `size > NaN` is always false (no eviction,
18
+ // ever) and a NaN ttlMs made expiresAt NaN, which never
19
+ // compares <= now, so entries lived forever. Sanitise at the
20
+ // boundary: finite values keep their exact meaning (counts
21
+ // are floored and clamped at 0), non-finite values fall back
22
+ // to the documented defaults (3600 s / 1000 entries).
23
+ const ttlMs = Number.isFinite(ttlSeconds) ? Math.max(1, ttlSeconds * 1000) : 3600 * 1000;
24
+ const limit = Number.isFinite(maxEntries) ? Math.max(0, Math.floor(maxEntries)) : 1000;
17
25
  const map = new Map();
18
26
  return {
19
27
  get(key) {
@@ -28,11 +36,20 @@ export const makeBoundedTTLCache = ({ ttlSeconds = 3600, maxEntries = 1000 } = {
28
36
  },
29
37
  set(key, value) {
30
38
  map.set(key, { value, expiresAt: Date.now() + ttlMs });
31
- while (map.size > maxEntries) {
32
- const oldest = map.keys().next().value;
33
- if (oldest === undefined)
39
+ // AunuBaileys (1.0.80, HB-226): the old guard broke out
40
+ // when the oldest KEY was `undefined` — but a Map can
41
+ // legitimately HOLD undefined as a key (two in-tree
42
+ // callers can produce one: a call offer without a
43
+ // call-id, a placeholder resend without a message id),
44
+ // and one such entry silently disabled the ceiling
45
+ // until it expired. The iterator's `done` flag is the
46
+ // real "map is empty" signal; deleting an undefined
47
+ // key is perfectly safe.
48
+ while (map.size > limit) {
49
+ const oldest = map.keys().next();
50
+ if (oldest.done)
34
51
  break;
35
- map.delete(oldest);
52
+ map.delete(oldest.value);
36
53
  }
37
54
  },
38
55
  del(key) {
@@ -15,7 +15,16 @@ import { jidNormalizedUser, isJidUser, isLidUser } from '../WABinary/jid-utils.j
15
15
  * fed manually via `learnFromMessage` / `addPair`.
16
16
  */
17
17
  export const makeLIDStore = (options = {}) => {
18
- const maxEntries = options.maxEntries ?? 5000;
18
+ // AunuBaileys (1.0.80, HB-190 + addendum HB-145): `?? 5000`
19
+ // let NaN through (the eviction comparison `size > NaN` is
20
+ // always false — the store grew without bound) and let
21
+ // negatives through (evictIfNeeded's while loop then spun
22
+ // forever on an empty map: `size > -1` stays true while
23
+ // delete(undefined) is a no-op — a wedged process, no error).
24
+ // Sanitise at the boundary: finite values are floored and
25
+ // clamped at 0 (0 = store nothing, which terminates),
26
+ // non-finite values fall back to the default 5000.
27
+ const maxEntries = Number.isFinite(options.maxEntries) ? Math.max(0, Math.floor(options.maxEntries)) : 5000;
19
28
  // AunuBaileys (1.0.71): optional listener fired when a genuinely
20
29
  // NEW or changed PN<->LID pair is learned (re-learning an already
21
30
  // known pair does not fire). The socket wires it to 'lid-mapping.update'.
@@ -29,7 +38,17 @@ export const makeLIDStore = (options = {}) => {
29
38
  };
30
39
  const evictIfNeeded = () => {
31
40
  while (pnToLid.size > maxEntries) {
32
- const oldestPn = pnToLid.keys().next().value;
41
+ // AunuBaileys (1.0.80, HB-190 defence in depth): with a
42
+ // sanitised maxEntries (>= 0) this loop always
43
+ // terminates, but never trust a loop whose only exit is
44
+ // a size comparison — bail out if the map is somehow
45
+ // already empty (the iterator's `done`, not the key's
46
+ // value, is the empty signal).
47
+ const oldest = pnToLid.keys().next();
48
+ if (oldest.done) {
49
+ break;
50
+ }
51
+ const oldestPn = oldest.value;
33
52
  const lid = pnToLid.get(oldestPn);
34
53
  pnToLid.delete(oldestPn);
35
54
  if (lid) {
@@ -15,6 +15,14 @@
15
15
  * `sentMessagesCacheSize: 0` in the socket config to disable.
16
16
  */
17
17
  export const makeSentMessageCache = (maxEntries = 256, resolveAlternateJid) => {
18
+ // AunuBaileys (1.0.80, HB-145): a NaN cap (host config from an
19
+ // unset env var — sentMessageCacheFor's typeof check passes NaN
20
+ // straight through) made `cache.size > NaN` false forever, so
21
+ // the cache grew without bound; Infinity did the same.
22
+ // Sanitise at the boundary: finite values are floored and
23
+ // clamped at 0 (0 keeps its documented meaning — the cache is
24
+ // disabled), non-finite values fall back to the default 256.
25
+ const limit = Number.isFinite(maxEntries) ? Math.max(0, Math.floor(maxEntries)) : 256;
18
26
  const cache = new Map();
19
27
  const keyOf = (remoteJid, id) => `${remoteJid}:${id}`;
20
28
  let altResolver = typeof resolveAlternateJid === 'function' ? resolveAlternateJid : undefined;
@@ -30,13 +38,13 @@ export const makeSentMessageCache = (maxEntries = 256, resolveAlternateJid) => {
30
38
  }
31
39
  },
32
40
  add: (remoteJid, id, message) => {
33
- if (!remoteJid || !id || !message || maxEntries <= 0) {
41
+ if (!remoteJid || !id || !message || limit <= 0) {
34
42
  return;
35
43
  }
36
44
  const key = keyOf(remoteJid, id);
37
45
  cache.delete(key);
38
46
  cache.set(key, message);
39
- while (cache.size > maxEntries) {
47
+ while (cache.size > limit) {
40
48
  cache.delete(cache.keys().next().value);
41
49
  }
42
50
  },
@@ -35,6 +35,19 @@ export const generateOrGetPreKeys = (creds, range) => {
35
35
  throw new Error('AunuBaileys: creds.nextPreKeyId / creds.firstUnuploadedPreKeyId are missing — the auth state is incomplete or corrupt; restore a full backup or pair again');
36
36
  }
37
37
  const avaliable = creds.nextPreKeyId - creds.firstUnuploadedPreKeyId;
38
+ // AunuBaileys (1.0.80, HB-140 + HB-225): finite-but-INVERTED
39
+ // counters (firstUnuploadedPreKeyId > nextPreKeyId — hand-edited
40
+ // or partially restored creds) made `remaining` exceed `range`
41
+ // by the size of the gap, so the loop below generated keys for
42
+ // the GAP, not the request: gap 50k ≈ 5 s and +48 MB, gap 200k
43
+ // ≈ 21 s and +238 MB, and a ~2^31 gap would freeze the process
44
+ // outright — synchronously, inside the keys transaction, at
45
+ // every login. Same class as HB-84 above (corrupt counters),
46
+ // same answer: refuse descriptively. With consistent counters
47
+ // `remaining` can never exceed `range` by construction.
48
+ if (avaliable < 0) {
49
+ throw new Error('AunuBaileys: creds pre-key counters are inverted (firstUnuploadedPreKeyId > nextPreKeyId) — the auth state is corrupt; restore a full backup or pair again');
50
+ }
38
51
  const remaining = range - avaliable;
39
52
  const lastPreKeyId = creds.nextPreKeyId + remaining - 1;
40
53
  const newPreKeys = {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@aunucore/baileys",
3
3
  "type": "module",
4
- "version": "1.0.79",
4
+ "version": "1.0.80",
5
5
  "description": "AunuBaileys — the self-healing WhatsApp Web API for Node.js. Bad MAC and \"2000 messages into the future\" errors repair themselves, LID and phone-number identities stay in sync, anti-ban is built in, pairing codes are custom — plus 29 experimental features no other Baileys fork ships: invisible watermarks, per-person tag-all cards, side-profile text, ghost mentions and more. ESM, CJS and full TypeScript types.",
6
6
  "keywords": [
7
7
  "whatsapp",