@aunucore/baileys 1.0.80 → 1.0.81

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.
@@ -884,7 +884,19 @@ export const makeChatsSocket = (config) => {
884
884
  }
885
885
  // update our pushname too
886
886
  if (msg.key.fromMe && msg.pushName && authState.creds.me?.name !== msg.pushName) {
887
- ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } });
887
+ // AunuBaileys (1.0.81, HB-257): 'creds.update' is
888
+ // not a bufferable event, so this emit is
889
+ // synchronous — and it sits BEFORE the Promise.all
890
+ // below, so a throwing listener used to reject
891
+ // upsertMessage before processMessage ever ran for
892
+ // this message, costing a NACK 500 on a delivered
893
+ // stanza. Log and continue with the message.
894
+ try {
895
+ ev.emit('creds.update', { me: { ...authState.creds.me, name: msg.pushName } });
896
+ }
897
+ catch (err) {
898
+ onUnexpectedError(err, 'creds.update (pushName)');
899
+ }
888
900
  }
889
901
  }
890
902
  const historyMsg = getHistoryMsg(msg.message);
@@ -917,7 +929,17 @@ export const makeChatsSocket = (config) => {
917
929
  logger.info('App state sync complete, transitioning to Online state and flushing buffer');
918
930
  ev.flush();
919
931
  const accountSyncCounter = (authState.creds.accountSyncCounter || 0) + 1;
920
- ev.emit('creds.update', { accountSyncCounter });
932
+ // AunuBaileys (1.0.81, HB-257): same guard as the
933
+ // pushName emit above — a throwing 'creds.update'
934
+ // listener must not reject the app-state sync that
935
+ // just completed (and skip the rest of this
936
+ // message's processing with it).
937
+ try {
938
+ ev.emit('creds.update', { accountSyncCounter });
939
+ }
940
+ catch (err) {
941
+ onUnexpectedError(err, 'creds.update (accountSyncCounter)');
942
+ }
921
943
  }
922
944
  };
923
945
  await Promise.all([
@@ -425,7 +425,18 @@ export const makeInteropSocket = (sock) => {
425
425
  throw new Error(`sendInteropMessage: not an interop JID: ${jid}`);
426
426
  }
427
427
  const sent = await sock.sendMessage(jid, content, options);
428
- ev.emit('interop', { type: 'message.sent', jid, id: sent?.key?.id });
428
+ // AunuBaileys (1.0.81, HB-196): this emit reports a send
429
+ // that has ALREADY succeeded — a throwing 'interop'
430
+ // listener used to reject the promise anyway, so the
431
+ // caller saw a failure and retried, duplicating a
432
+ // delivered message. Log the listener error and return
433
+ // the send result.
434
+ try {
435
+ ev.emit('interop', { type: 'message.sent', jid, id: sent?.key?.id });
436
+ }
437
+ catch (err) {
438
+ logger?.error?.({ err }, 'interop: error in message.sent listener');
439
+ }
429
440
  return sent;
430
441
  };
431
442
  return {
@@ -1727,25 +1727,36 @@ export const makeMessagesRecvSocket = (config) => {
1727
1727
  // first without a catch, so a socket that closed in that window
1728
1728
  // (sendNode throws Boom 'Connection Closed') swallowed the
1729
1729
  // status event entirely.
1730
- ev.emit('newsletter.status', {
1731
- id: from,
1732
- messageId: id,
1733
- serverId,
1734
- timestamp,
1735
- isSender,
1736
- ...(offlineIndex !== undefined ? { offlineIndex } : {}),
1737
- ...(meta ? { meta } : {}),
1738
- ...(viewsCount !== undefined ? { viewsCount } : {}),
1739
- ...(responsesCount !== undefined ? { responsesCount } : {}),
1740
- ...(reactionCounts.length ? { reactionCounts } : {}),
1741
- content
1742
- });
1743
- const ackType = type === 'reaction' ? 'reaction' : edit ? 'revoke' : type;
1744
- await sendNode({
1745
- tag: 'ack',
1746
- attrs: { id, to: from, class: 'status', type: ackType || 'text' },
1747
- content: undefined
1748
- }).catch((err) => logger.warn({ err }, 'failed to ack newsletter status'));
1730
+ // AunuBaileys (1.0.81, HB-192): the ack now sits in a
1731
+ // finally. The emit-first order is unchanged (HB-59), but
1732
+ // a throwing 'newsletter.status' listener can no longer
1733
+ // take the ack down with it — an un-acked <status> stanza
1734
+ // is redelivered (or the status lost) for the sake of a
1735
+ // host bug. The throw itself still propagates to the
1736
+ // CB:status registration's catch, as before.
1737
+ try {
1738
+ ev.emit('newsletter.status', {
1739
+ id: from,
1740
+ messageId: id,
1741
+ serverId,
1742
+ timestamp,
1743
+ isSender,
1744
+ ...(offlineIndex !== undefined ? { offlineIndex } : {}),
1745
+ ...(meta ? { meta } : {}),
1746
+ ...(viewsCount !== undefined ? { viewsCount } : {}),
1747
+ ...(responsesCount !== undefined ? { responsesCount } : {}),
1748
+ ...(reactionCounts.length ? { reactionCounts } : {}),
1749
+ content
1750
+ });
1751
+ }
1752
+ finally {
1753
+ const ackType = type === 'reaction' ? 'reaction' : edit ? 'revoke' : type;
1754
+ await sendNode({
1755
+ tag: 'ack',
1756
+ attrs: { id, to: from, class: 'status', type: ackType || 'text' },
1757
+ content: undefined
1758
+ }).catch((err) => logger.warn({ err }, 'failed to ack newsletter status'));
1759
+ }
1749
1760
  };
1750
1761
  // recv a message
1751
1762
  ws.on('CB:message', (node) => {
@@ -367,13 +367,30 @@ export const makeSocket = (config) => {
367
367
  }
368
368
  catch { }
369
369
  }
370
- ev.emit('connection.update', {
371
- connection: 'close',
372
- lastDisconnect: {
373
- error,
374
- date: new Date()
375
- }
376
- });
370
+ // AunuBaileys (1.0.81, HB-138): this emit sits BEFORE the
371
+ // socket-end handlers and ev.destroy() below — cleanup the
372
+ // lib owns (watchdog stop, offline-drain stop, scheduled-
373
+ // task cleanup). A throwing host 'connection.update'
374
+ // listener used to unwind straight out of end() — and,
375
+ // via the ws 'error'/'close' handlers that call end(),
376
+ // out of the ws emit chain into an uncaught exception —
377
+ // skipping all of it, with no recovery path: `closed` is
378
+ // already true, so a second end() is a no-op. The
379
+ // event-buffer dispatcher already logs listener errors
380
+ // (1.0.81); this guard keeps end()'s own cleanup
381
+ // unconditional regardless of the emit path.
382
+ try {
383
+ ev.emit('connection.update', {
384
+ connection: 'close',
385
+ lastDisconnect: {
386
+ error,
387
+ date: new Date()
388
+ }
389
+ });
390
+ }
391
+ catch (err) {
392
+ logger.error({ err }, 'error emitting connection close update');
393
+ }
377
394
  for (const handler of socketEndHandlers) {
378
395
  try {
379
396
  handler();
@@ -32,7 +32,25 @@ export const makeEventBuffer = (logger) => {
32
32
  // take the generic event and fire it as a baileys event
33
33
  ev.on('event', (map) => {
34
34
  for (const event in map) {
35
- ev.emit(event, map[event]);
35
+ // AunuBaileys (1.0.81, HB-227/HB-228/HB-229): a host
36
+ // listener that throws must never escape this
37
+ // dispatcher. In the host's own call stack a throw is
38
+ // the host's to catch — but most emits originate inside
39
+ // lib timers and async callbacks (the nextTick
40
+ // 'connecting' update, QR cycles, the session
41
+ // watchdog's interval, PDO resend timeouts, ws
42
+ // handlers), where no host frame exists to catch it:
43
+ // the throw became an uncaught exception and killed
44
+ // the process, or unwound past lib cleanup that still
45
+ // had to run. Log the error and keep dispatching the
46
+ // remaining events of the batch; the event itself
47
+ // still fired, with its payload unchanged.
48
+ try {
49
+ ev.emit(event, map[event]);
50
+ }
51
+ catch (err) {
52
+ logger.error({ err, event }, 'error in event listener — dispatch continues');
53
+ }
36
54
  }
37
55
  });
38
56
  function buffer() {
@@ -58,10 +76,29 @@ export const makeEventBuffer = (logger) => {
58
76
  }
59
77
  }
60
78
  const consolidatedData = consolidateEvents(data);
79
+ // AunuBaileys (1.0.81, HB-166 + HB-224): swap the fresh
80
+ // buffer in BEFORE emitting, and guard the emit. The old
81
+ // order assigned `data = newData` after the emit, so
82
+ // (a) an event a listener re-buffered during the flush
83
+ // emit landed in the OLD buffer object and was silently
84
+ // discarded with it, and (b) a throw mid-emit left the
85
+ // whole batch in `data`, to be delivered a second time by
86
+ // the next flush. With the swap first, re-buffered events
87
+ // survive to the next flush and the emitted batch is
88
+ // detached no matter what a listener does. The guard
89
+ // matters because flush() is called from timer callbacks
90
+ // (chats.js history-sync transitions) and fire-and-forget
91
+ // paths (processNodeWithBuffer), where a throw became an
92
+ // uncaught exception / unhandled rejection.
93
+ data = newData;
61
94
  if (Object.keys(consolidatedData).length) {
62
- ev.emit('event', consolidatedData);
95
+ try {
96
+ ev.emit('event', consolidatedData);
97
+ }
98
+ catch (err) {
99
+ logger.error({ err }, 'error emitting flushed events');
100
+ }
63
101
  }
64
- data = newData;
65
102
  logger.trace({ conditionalChatUpdatesLeft }, 'released buffered events');
66
103
  return true;
67
104
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@aunucore/baileys",
3
3
  "type": "module",
4
- "version": "1.0.80",
4
+ "version": "1.0.81",
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",