@violetix/baileys 0.5.5 → 0.6.0

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.
Files changed (80) hide show
  1. package/README.md +1530 -2009
  2. package/WAProto/index.d.ts +14030 -14586
  3. package/WAProto/index.js +58426 -88874
  4. package/lib/Defaults/index.d.ts +2 -7
  5. package/lib/Defaults/index.js +5 -20
  6. package/lib/Signal/Group/sender-key-message.js +19 -1
  7. package/lib/Signal/libsignal.js +82 -29
  8. package/lib/Signal/xeddsa-fast.js +114 -0
  9. package/lib/Socket/Client/types.js +6 -8
  10. package/lib/Socket/Client/websocket.js +3 -38
  11. package/lib/Socket/business.d.ts +0 -3
  12. package/lib/Socket/chats.d.ts +0 -3
  13. package/lib/Socket/chats.js +18 -45
  14. package/lib/Socket/communities.d.ts +0 -3
  15. package/lib/Socket/graphql.js +3 -7
  16. package/lib/Socket/groups.d.ts +0 -3
  17. package/lib/Socket/groups.js +3 -8
  18. package/lib/Socket/index.d.ts +15 -17
  19. package/lib/Socket/messages-recv.d.ts +0 -3
  20. package/lib/Socket/messages-recv.js +43 -127
  21. package/lib/Socket/messages-send.d.ts +0 -4
  22. package/lib/Socket/messages-send.js +63 -191
  23. package/lib/Socket/newsletter.d.ts +0 -3
  24. package/lib/Socket/newsletter.js +1 -2
  25. package/lib/Socket/newsletter.js.map +1 -1
  26. package/lib/Socket/socket.d.ts +0 -9
  27. package/lib/Socket/socket.js +53 -341
  28. package/lib/Store/make-in-memory-store.js +30 -41
  29. package/lib/Types/Auth.d.ts +2 -60
  30. package/lib/Types/Bussines.d.ts +2 -9
  31. package/lib/Types/Call.d.ts +2 -22
  32. package/lib/Types/Chat.d.ts +1 -2
  33. package/lib/Types/Contact.d.ts +2 -11
  34. package/lib/Types/Events.d.ts +2 -41
  35. package/lib/Types/GroupMetadata.d.ts +2 -36
  36. package/lib/Types/Message.d.ts +1 -11
  37. package/lib/Types/Product.d.ts +2 -5
  38. package/lib/Types/Signal.d.ts +2 -10
  39. package/lib/Types/Socket.d.ts +2 -33
  40. package/lib/Types/USync.d.ts +2 -9
  41. package/lib/Types/index.d.ts +1 -3
  42. package/lib/Utils/auth-utils.d.ts +2 -3
  43. package/lib/Utils/auth-utils.js +53 -37
  44. package/lib/Utils/business.js +11 -25
  45. package/lib/Utils/chat-utils.js +4 -6
  46. package/lib/Utils/decode-wa-message.js +2 -2
  47. package/lib/Utils/event-buffer.js +12 -21
  48. package/lib/Utils/generics.d.ts +5 -5
  49. package/lib/Utils/generics.js +7 -25
  50. package/lib/Utils/history.d.ts +1 -2
  51. package/lib/Utils/history.js +83 -137
  52. package/lib/Utils/index.d.ts +0 -4
  53. package/lib/Utils/index.js +0 -4
  54. package/lib/Utils/listener-guard.js +0 -2
  55. package/lib/Utils/message-retry-manager.js +0 -20
  56. package/lib/Utils/messages-media.d.ts +8 -39
  57. package/lib/Utils/messages-media.js +89 -218
  58. package/lib/Utils/messages.d.ts +0 -1
  59. package/lib/Utils/messages.js +36 -122
  60. package/lib/Utils/process-message.d.ts +0 -18
  61. package/lib/Utils/process-message.js +47 -99
  62. package/lib/Utils/rich-message-utils.js +4 -3
  63. package/lib/Utils/use-multi-file-auth-state.d.ts +3 -5
  64. package/lib/Utils/use-multi-file-auth-state.js +6 -27
  65. package/lib/Utils/use-single-file-auth-state.d.ts +1 -1
  66. package/lib/Utils/use-single-file-auth-state.js +3 -12
  67. package/lib/WABinary/types.d.ts +2 -6
  68. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +3 -6
  69. package/lib/WAUSync/Protocols/USyncContactProtocol.js +2 -3
  70. package/lib/WAUSync/USyncQuery.d.ts +1 -1
  71. package/lib/WAUSync/USyncQuery.js +4 -7
  72. package/package.json +107 -123
  73. package/lib/Utils/a2ui.d.ts +0 -50
  74. package/lib/Utils/a2ui.js +0 -168
  75. package/lib/Utils/anchor-guard.d.ts +0 -71
  76. package/lib/Utils/anchor-guard.js +0 -404
  77. package/lib/Utils/connection-manager.d.ts +0 -44
  78. package/lib/Utils/connection-manager.js +0 -258
  79. package/lib/Utils/ignore-key.d.ts +0 -51
  80. package/lib/Utils/ignore-key.js +0 -176
@@ -61,7 +61,6 @@ export function makeCacheableSignalKeyStore(store, logger, _cache) {
61
61
  },
62
62
  async set(data) {
63
63
  return cacheMutex.runExclusive(async () => {
64
- await store.set(data);
65
64
  let keys = 0;
66
65
  for (const type in data) {
67
66
  for (const id in data[type]) {
@@ -70,13 +69,12 @@ export function makeCacheableSignalKeyStore(store, logger, _cache) {
70
69
  }
71
70
  }
72
71
  logger?.trace({ keys }, 'updated cache');
72
+ await store.set(data);
73
73
  });
74
74
  },
75
75
  async clear() {
76
- await cacheMutex.runExclusive(async () => {
77
- await store.clear?.();
78
- await cache.flushAll();
79
- });
76
+ await cache.flushAll();
77
+ await store.clear?.();
80
78
  }
81
79
  };
82
80
  }
@@ -93,12 +91,35 @@ export const addTransactionCapability = (state, logger, { maxCommitRetries, dela
93
91
  const txMutexRefCounts = new Map();
94
92
  // Pre-key manager for specialized operations
95
93
  const preKeyManager = new PreKeyManager(state, logger);
96
- const storeToken = Symbol('SignalKeyStore');
94
+ // Debounced writer: collects writes within a window and flushes as one SQLite tx
95
+ const debounceWindow = 5000;
96
+ let debounceTimer = null;
97
+ let debouncedData = {};
98
+ // One writer at a time. A debounced flush and a transaction commit both call state.set, and
99
+ // interleaving them lets an older debounced value land on top of a newer committed one.
97
100
  const writeMutex = new Mutex();
98
- function getContext() {
99
- const ctx = txStorage.getStore()?.get(storeToken);
100
- return ctx?.active ? ctx : undefined;
101
- }
101
+ const flushDebounced = async () => {
102
+ if (debounceTimer) {
103
+ clearTimeout(debounceTimer);
104
+ debounceTimer = null;
105
+ }
106
+ const data = debouncedData;
107
+ debouncedData = {};
108
+ if (Object.keys(data).length === 0) return;
109
+ // All types in one call: that is the single SQLite transaction this debounce exists for.
110
+ await writeMutex.runExclusive(() => state.set(data));
111
+ };
112
+ const scheduleFlush = () => {
113
+ // Max age, not a sliding window: re-arming the timer on every write meant a socket that
114
+ // never went quiet for a full window never flushed at all, leaving ratchet state in memory
115
+ // only until the process happened to die.
116
+ if (debounceTimer) return;
117
+ debounceTimer = setTimeout(() => {
118
+ // The timer has nowhere to report to, and an unhandled rejection here takes down the
119
+ // whole process - every other session in it included.
120
+ flushDebounced().catch(err => logger.error({ err }, 'failed to flush signal keys'));
121
+ }, debounceWindow);
122
+ };
102
123
  /**
103
124
  * Get or create a transaction mutex
104
125
  */
@@ -135,7 +156,7 @@ export const addTransactionCapability = (state, logger, { maxCommitRetries, dela
135
156
  * Check if currently in a transaction
136
157
  */
137
158
  function isInTransaction() {
138
- return !!getContext();
159
+ return !!txStorage.getStore();
139
160
  }
140
161
  /**
141
162
  * Commit transaction with retries
@@ -146,15 +167,17 @@ export const addTransactionCapability = (state, logger, { maxCommitRetries, dela
146
167
  return;
147
168
  }
148
169
  logger.trace('committing transaction');
149
- const attempts = Math.max(1, maxCommitRetries);
150
- for (let attempt = 0; attempt < attempts; attempt++) {
170
+ // Whatever is still debounced was written BEFORE these mutations, so it has to land first
171
+ // or it overwrites them.
172
+ await flushDebounced();
173
+ for (let attempt = 0; attempt < maxCommitRetries; attempt++) {
151
174
  try {
152
175
  await writeMutex.runExclusive(() => state.set(mutations));
153
176
  logger.trace({ mutationCount: Object.keys(mutations).length }, 'committed transaction');
154
177
  return;
155
178
  }
156
179
  catch (error) {
157
- const retriesLeft = attempts - attempt - 1;
180
+ const retriesLeft = maxCommitRetries - attempt - 1;
158
181
  logger.warn(`failed to commit mutations, retries left=${retriesLeft}`);
159
182
  if (retriesLeft === 0) {
160
183
  throw error;
@@ -165,7 +188,7 @@ export const addTransactionCapability = (state, logger, { maxCommitRetries, dela
165
188
  }
166
189
  return {
167
190
  get: async (type, ids) => {
168
- const ctx = getContext();
191
+ const ctx = txStorage.getStore();
169
192
  if (!ctx) {
170
193
  // No transaction - direct read without exclusive lock for concurrency
171
194
  return state.get(type, ids);
@@ -176,7 +199,7 @@ export const addTransactionCapability = (state, logger, { maxCommitRetries, dela
176
199
  if (missing.length > 0) {
177
200
  ctx.dbQueries++;
178
201
  logger.trace({ type, count: missing.length }, 'fetching missing keys in transaction');
179
- const fetched = await state.get(type, missing);
202
+ const fetched = await getTxMutex(type).runExclusive(() => state.get(type, missing));
180
203
  // Update cache
181
204
  ctx.cache[type] = ctx.cache[type] || {};
182
205
  Object.assign(ctx.cache[type], fetched);
@@ -192,14 +215,18 @@ export const addTransactionCapability = (state, logger, { maxCommitRetries, dela
192
215
  return result;
193
216
  },
194
217
  set: async (data) => {
195
- const ctx = getContext();
218
+ const ctx = txStorage.getStore();
196
219
  if (!ctx) {
197
- return writeMutex.runExclusive(async () => {
198
- if (data['pre-key']) {
199
- await preKeyManager.validateDeletions(data, 'pre-key');
220
+ // No transaction - debounce writes to batch into fewer SQLite transactions
221
+ for (const type_ in data) {
222
+ const type = type_;
223
+ if (type === 'pre-key') {
224
+ await preKeyManager.validateDeletions(data, type);
200
225
  }
201
- await state.set(data);
202
- });
226
+ debouncedData[type] = { ...debouncedData[type], ...data[type] };
227
+ }
228
+ scheduleFlush();
229
+ return;
203
230
  }
204
231
  // In transaction - update cache and mutations
205
232
  logger.trace({ types: Object.keys(data) }, 'caching in transaction');
@@ -221,7 +248,7 @@ export const addTransactionCapability = (state, logger, { maxCommitRetries, dela
221
248
  },
222
249
  isInTransaction,
223
250
  transaction: async (work, key) => {
224
- const existing = getContext();
251
+ const existing = txStorage.getStore();
225
252
  // Nested transaction - reuse existing context
226
253
  if (existing) {
227
254
  logger.trace('reusing existing transaction context');
@@ -233,19 +260,13 @@ export const addTransactionCapability = (state, logger, { maxCommitRetries, dela
233
260
  try {
234
261
  return await mutex.runExclusive(async () => {
235
262
  const ctx = {
236
- active: true,
237
263
  cache: {},
238
264
  mutations: {},
239
265
  dbQueries: 0
240
266
  };
241
- const contexts = new Map(txStorage.getStore());
242
- contexts.set(storeToken, ctx);
243
267
  logger.trace('entering transaction');
244
268
  try {
245
- const result = await txStorage.run(contexts, work);
246
- // Detached async work can outlive this callback. It must not write into
247
- // a context whose mutations have already been handed to the commit.
248
- ctx.active = false;
269
+ const result = await txStorage.run(ctx, work);
249
270
  // Commit mutations
250
271
  await commitWithRetry(ctx.mutations);
251
272
  logger.trace({ dbQueries: ctx.dbQueries }, 'transaction completed');
@@ -255,18 +276,13 @@ export const addTransactionCapability = (state, logger, { maxCommitRetries, dela
255
276
  logger.error({ error }, 'transaction failed, rolling back');
256
277
  throw error;
257
278
  }
258
- finally {
259
- ctx.active = false;
260
- }
261
279
  });
262
280
  }
263
281
  finally {
264
282
  releaseTxMutexRef(key);
265
283
  }
266
284
  },
267
- flush: () => writeMutex.runExclusive(async () => {
268
- await state.flush?.();
269
- })
285
+ flush: flushDebounced
270
286
  };
271
287
  };
272
288
  /**
@@ -303,4 +319,4 @@ export const initAuthCreds = () => {
303
319
  additionalData: undefined
304
320
  };
305
321
  };
306
- //# sourceMappingURL=auth-utils.js.map
322
+ //# sourceMappingURL=auth-utils.js.map
@@ -1,6 +1,5 @@
1
1
  import { Boom } from '@hapi/boom';
2
2
  import { createHash } from 'crypto';
3
- import { once } from 'events';
4
3
  import { createWriteStream, promises as fs } from 'fs';
5
4
  import { tmpdir } from 'os';
6
5
  import { join } from 'path';
@@ -200,31 +199,18 @@ export const uploadingNecessaryImages = async (images, waUploadToServer, timeout
200
199
  const hasher = createHash('sha256');
201
200
  const filePath = join(tmpdir(), 'img' + generateMessageIDV2());
202
201
  const encFileWriteStream = createWriteStream(filePath);
203
- try {
204
- for await (const block of stream) {
205
- hasher.update(block);
206
- // Respect backpressure instead of queueing the whole image in memory.
207
- if (!encFileWriteStream.write(block)) {
208
- await once(encFileWriteStream, 'drain');
209
- }
210
- }
211
- // The upload reads the path, so the writes have to be on disk first. Without this the
212
- // upload could read a partial file while the hash covers the whole image, and the
213
- // server rejects the mismatch.
214
- await new Promise((resolve, reject) => encFileWriteStream.end(err => (err ? reject(err) : resolve())));
215
- const sha = hasher.digest('base64');
216
- const { directPath } = await waUploadToServer(filePath, {
217
- mediaType: 'product-catalog-image',
218
- fileEncSha256B64: sha,
219
- timeoutMs
220
- });
221
- return { url: getUrlFromDirectPath(directPath) };
222
- }
223
- finally {
224
- // Also runs when the upload throws, which used to leak the temp file.
225
- encFileWriteStream.destroy();
226
- await fs.unlink(filePath).catch(() => { });
202
+ for await (const block of stream) {
203
+ hasher.update(block);
204
+ encFileWriteStream.write(block);
227
205
  }
206
+ const sha = hasher.digest('base64');
207
+ const { directPath } = await waUploadToServer(filePath, {
208
+ mediaType: 'product-catalog-image',
209
+ fileEncSha256B64: sha,
210
+ timeoutMs
211
+ });
212
+ await fs.unlink(filePath).catch(err => console.log('Error deleting temp file ', err));
213
+ return { url: getUrlFromDirectPath(directPath) };
228
214
  }));
229
215
  return results;
230
216
  };
@@ -344,6 +344,7 @@ export const decodePatches = async (name, syncds, initial, getAppStateSyncKey, o
344
344
  syncd.mutations?.push(...ref.mutations);
345
345
  }
346
346
  const patchVersion = toNumber(version.version);
347
+ newState.version = patchVersion;
347
348
  const shouldMutate = typeof minimumVersionNumber === 'undefined' || patchVersion > minimumVersionNumber;
348
349
  let decodeResult;
349
350
  try {
@@ -357,12 +358,9 @@ export const decodePatches = async (name, syncds, initial, getAppStateSyncKey, o
357
358
  catch (err) {
358
359
  if (isMissingKeyError(err))
359
360
  throw err;
360
- // stop here: advancing the version past an undecoded patch loses its mutations for good;
361
- // the next sync resumes from the last good version
362
- logger?.warn({ name, version: patchVersion, error: err.message }, 'failed to decode patch, stopping');
363
- break;
361
+ logger?.warn({ name, version: patchVersion, error: err.message }, 'failed to decode patch, skipping');
362
+ continue;
364
363
  }
365
- newState.version = patchVersion;
366
364
  newState.hash = decodeResult.hash;
367
365
  newState.indexValueMap = decodeResult.indexValueMap;
368
366
  if (validateMacs) {
@@ -732,7 +730,7 @@ export const processSyncAction = (syncAction, ev, me, initialSyncOpts, logger) =
732
730
  }
733
731
  else if (action?.pushNameSetting) {
734
732
  const name = action?.pushNameSetting?.name;
735
- if (name && me?.id && me?.name !== name) {
733
+ if (name && me?.name !== name) {
736
734
  ev.emit('creds.update', { me: { ...me, name } });
737
735
  }
738
736
  }
@@ -192,7 +192,7 @@ export function decodeMessageNode(stanza, meId, meLid) {
192
192
  if (!participant) {
193
193
  throw new Boom('No participant in group message');
194
194
  }
195
- const isParticipantMe = isMe(participant) || isMeLid(participant);
195
+ const isParticipantMe = isMe(participant);
196
196
  if (isJidStatusBroadcast(from)) {
197
197
  msgType = isParticipantMe ? 'direct_peer_status' : 'other_status';
198
198
  }
@@ -226,7 +226,7 @@ export function decodeMessageNode(stanza, meId, meLid) {
226
226
  remoteJid: chatId,
227
227
  remoteJidAlt: !isJidGroup(chatId) ? addressingContext.senderAlt : undefined,
228
228
  remoteJidUsername: !isJidGroup(chatId)
229
- ? stanza.attrs.peer_recipient_username || stanza.attrs.recipient_username || stanza.attrs.username
229
+ ? stanza.attrs.peer_recipient_username || stanza.attrs.recipient_username
230
230
  : undefined,
231
231
  fromMe,
232
232
  id: msgId,
@@ -26,7 +26,7 @@ const BUFFERABLE_EVENT_SET = new Set(BUFFERABLE_EVENT);
26
26
  export const makeEventBuffer = (logger) => {
27
27
  const ev = new EventEmitter();
28
28
  const historyCache = new Set();
29
- let trackedListeners = [];
29
+ const trackedListeners = [];
30
30
  let data = makeBufferData();
31
31
  let isBuffering = false;
32
32
  let bufferTimeout = null;
@@ -108,7 +108,9 @@ export const makeEventBuffer = (logger) => {
108
108
  }
109
109
  return {
110
110
  process(handler) {
111
- const listener = listenerGuard.wrap(handler);
111
+ const listener = async (map) => {
112
+ await handler(map);
113
+ };
112
114
  ev.on('event', listener);
113
115
  return () => {
114
116
  ev.off('event', listener);
@@ -179,20 +181,8 @@ export const makeEventBuffer = (logger) => {
179
181
  on: (...args) => { trackedOn(...args); return ev; },
180
182
  // unwrap, or removal would look for the caller's function while the registration holds the
181
183
  // guarded wrapper - every off() would silently leave the listener attached.
182
- off: (event, handler) => {
183
- const guarded = listenerGuard.unwrap(handler);
184
- const result = ev.off(event, guarded);
185
- const index = trackedListeners.findLastIndex(([name, listener]) => name === event && listener === guarded);
186
- if (index !== -1) {
187
- trackedListeners.splice(index, 1);
188
- }
189
- return result;
190
- },
191
- removeAllListeners: (...args) => {
192
- const result = ev.removeAllListeners(...args);
193
- trackedListeners = args.length ? trackedListeners.filter(([event]) => event !== args[0]) : [];
194
- return result;
195
- },
184
+ off: (event, handler) => ev.off(event, listenerGuard.unwrap(handler)),
185
+ removeAllListeners: (...args) => ev.removeAllListeners(...args),
196
186
  removeTrackedListeners() {
197
187
  for (const [event, handler] of trackedListeners) {
198
188
  ev.off(event, handler);
@@ -400,8 +390,7 @@ eventData, logger) {
400
390
  data.contactUpserts[contact.id] = upsert;
401
391
  }
402
392
  if (data.contactUpdates[contact.id]) {
403
- // merge into the stored upsert itself; the old code merged into a temp and lost the update
404
- Object.assign(upsert, trimUndefined(data.contactUpdates[contact.id]), trimUndefined(contact));
393
+ upsert = Object.assign(data.contactUpdates[contact.id], trimUndefined(contact));
405
394
  delete data.contactUpdates[contact.id];
406
395
  }
407
396
  }
@@ -526,8 +515,10 @@ eventData, logger) {
526
515
  const groupUpdates = eventData;
527
516
  for (const update of groupUpdates) {
528
517
  const id = update.id;
529
- // merge every update in the window, not just the first
530
- data.groupUpdates[id] = Object.assign(data.groupUpdates[id] || {}, update);
518
+ const groupUpdate = data.groupUpdates[id] || {};
519
+ if (!data.groupUpdates[id]) {
520
+ data.groupUpdates[id] = Object.assign(groupUpdate, update);
521
+ }
531
522
  }
532
523
  break;
533
524
  default:
@@ -647,4 +638,4 @@ function concatChats(a, b) {
647
638
  return Object.assign(a, b);
648
639
  }
649
640
  const stringifyMessageKey = (key) => `${key.remoteJid},${key.id},${key.fromMe ? '1' : '0'}`;
650
- //# sourceMappingURL=event-buffer.js.map
641
+ //# sourceMappingURL=event-buffer.js.map
@@ -32,20 +32,20 @@ export function generateMessageIDV2(userId: any): string;
32
32
  export function generateMessageID(): string;
33
33
  export function bindWaitForConnectionUpdate(ev: any): (check: any, timeoutMs: any) => Promise<void>;
34
34
  export function fetchLatestBaileysVersion(options?: {}): Promise<{
35
- version: [number, number, number];
35
+ version: number[];
36
36
  isLatest: boolean;
37
37
  error?: undefined;
38
38
  } | {
39
- version: [number, number, number];
39
+ version: number[];
40
40
  isLatest: boolean;
41
41
  error: unknown;
42
42
  }>;
43
43
  export function fetchLatestWaWebVersion(options?: {}): Promise<{
44
- version: [number, number, number];
44
+ version: number[];
45
45
  isLatest: boolean;
46
46
  error?: undefined;
47
47
  } | {
48
- version: [number, number, number];
48
+ version: number[];
49
49
  isLatest: boolean;
50
50
  error: unknown;
51
51
  }>;
@@ -61,4 +61,4 @@ export function getCallStatusFromNode({ tag, attrs }: {
61
61
  }): string;
62
62
  export function getCodeFromWSError(error: any): number;
63
63
  export function isWABusinessPlatform(platform: any): boolean;
64
- //# sourceMappingURL=generics.d.ts.map
64
+ //# sourceMappingURL=generics.d.ts.map
@@ -204,13 +204,10 @@ export const fetchLatestWaWebVersion = async (options = {}) => {
204
204
  'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
205
205
  };
206
206
  const headers = { ...defaultHeaders, ...options.headers };
207
- // bounded: a hung sw.js fetch must not stall the connect path that awaits this
208
- const signal = options.signal ?? AbortSignal.timeout(options.timeoutMs ?? 10000);
209
207
  const response = await fetch('https://web.whatsapp.com/sw.js', {
210
208
  ...options,
211
209
  method: 'GET',
212
- headers,
213
- signal
210
+ headers
214
211
  });
215
212
  if (!response.ok) {
216
213
  throw new Boom(`Failed to fetch sw.js: ${response.statusText}`, { statusCode: response.status });
@@ -263,32 +260,17 @@ export const getStatusFromReceiptType = (type) => {
263
260
  }
264
261
  return status;
265
262
  };
263
+ const CODE_MAP = {
264
+ conflict: DisconnectReason.connectionReplaced
265
+ };
266
266
  /**
267
- * Stream errors generally provide a reason, map that to a baileys DisconnectReason.
268
- * Mirrors zapo-js (src/transport/stream/parse.ts): only conflict means the session is gone;
269
- * ack / xml-not-well-formed / unknown reasons are transient and must stay reconnectable.
270
- * The old fallback was badSession (500), which made bots wipe healthy sessions.
267
+ * Stream errors generally provide a reason, map that to a baileys DisconnectReason
268
+ * @param reason the string reason given, eg. "conflict"
271
269
  */
272
270
  export const getErrorCodeFromStreamError = (node) => {
273
271
  const [reasonNode] = getAllBinaryNodeChildren(node);
274
272
  let reason = reasonNode?.tag || 'unknown';
275
- let statusCode;
276
- if (reason === 'conflict') {
277
- // type="replaced": another client took over; any other type (device_removed): unlinked
278
- statusCode = reasonNode.attrs?.type === 'replaced' || !reasonNode.attrs?.type
279
- ? DisconnectReason.connectionReplaced
280
- : DisconnectReason.loggedOut;
281
- }
282
- else if (node.attrs.code) {
283
- statusCode = +node.attrs.code;
284
- // 516 = server-forced logout
285
- if (statusCode === 516) {
286
- statusCode = DisconnectReason.loggedOut;
287
- }
288
- }
289
- else {
290
- statusCode = DisconnectReason.connectionClosed;
291
- }
273
+ const statusCode = +(node.attrs.code || CODE_MAP[reason] || DisconnectReason.badSession);
292
274
  if (statusCode === DisconnectReason.restartRequired) {
293
275
  reason = 'restart required';
294
276
  }
@@ -73,5 +73,4 @@ export function downloadAndProcessHistorySyncNotification(msg: any, options: any
73
73
  }>;
74
74
  export function getHistoryMsg(message: any): any;
75
75
  import { proto } from '../../WAProto/index.js';
76
- //# sourceMappingURL=history.d.ts.map
77
- export function decodeHistorySyncStreaming(buffer: Uint8Array, onConversation: (conversation: proto.IConversation) => void): proto.HistorySync;
76
+ //# sourceMappingURL=history.d.ts.map