@pontasockets/baileys 0.3.3 → 0.3.4

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.
@@ -404,7 +404,7 @@ const addTransactionCapability = (state, logger, { maxCommitRetries, delayBetwee
404
404
  const key = key_
405
405
  transactionCache[key] = transactionCache[key] || {}
406
406
  // Special handling for pre-keys and signed-pre-keys
407
- if (key === 'pre-key') {
407
+ if (key === 'pre-key' || key === 'signed-pre-key') {
408
408
  await handlePreKeyOperations(data, key, transactionCache, mutations, logger, true)
409
409
  }
410
410
  else {
@@ -475,17 +475,22 @@ const addTransactionCapability = (state, logger, { maxCommitRetries, delayBetwee
475
475
  if (transactionsInProgress === 1) {
476
476
  logger.trace('entering transaction')
477
477
  }
478
- // Release the transaction mutex now that we've updated the counter
479
- // This allows other transactions to start preparing
480
- releaseTxMutex()
481
478
  try {
482
479
  return await executeTransactionWork(work)
483
480
  }
484
481
  finally {
485
482
  cleanupTransactionState();
483
+ // Release AFTER commit & cleanup to prevent race conditions
484
+ // where another transaction starts before this one finishes writing
485
+ releaseTxMutex()
486
486
  }
487
487
  }
488
488
  catch (error) {
489
+ // Only release here if we haven't already released in the finally block
490
+ // (i.e., error thrown before transactionsInProgress was incremented)
491
+ if (transactionsInProgress > 0) {
492
+ cleanupTransactionState()
493
+ }
489
494
  releaseTxMutex()
490
495
  throw error
491
496
  }
@@ -520,4 +525,4 @@ module.exports = {
520
525
  makeCacheableSignalKeyStore,
521
526
  addTransactionCapability,
522
527
  initAuthCreds
523
- }
528
+ }
@@ -40,6 +40,7 @@ const makeEventBuffer = (logger) => {
40
40
  let data = makeBufferData()
41
41
  let isBuffering = false
42
42
  let bufferTimeout = null
43
+ let flushPendingTimeout = null
43
44
  let bufferCount = 0
44
45
  const MAX_HISTORY_CACHE_SIZE = 10000 // Limit the history cache size to prevent memory bloat
45
46
  const BUFFER_TIMEOUT_MS = 30000 // 30 seconds
@@ -81,11 +82,15 @@ const makeEventBuffer = (logger) => {
81
82
  isBuffering = false
82
83
  bufferCount = 0
83
84
 
84
- // Clear timeout
85
+ // Clear timeouts
85
86
  if (bufferTimeout) {
86
87
  clearTimeout(bufferTimeout)
87
88
  bufferTimeout = null
88
89
  }
90
+ if (flushPendingTimeout) {
91
+ clearTimeout(flushPendingTimeout)
92
+ flushPendingTimeout = null
93
+ }
89
94
 
90
95
  // Clear history cache if it exceeds the max size
91
96
  if (historyCache.size > MAX_HISTORY_CACHE_SIZE) {
@@ -138,15 +143,6 @@ const makeEventBuffer = (logger) => {
138
143
  buffer()
139
144
  try {
140
145
  const result = await work(...args)
141
-
142
- // If this is the only buffer, flush after a small delay
143
- if (bufferCount === 1) {
144
- setTimeout(() => {
145
- if (isBuffering && bufferCount === 1) {
146
- flush()
147
- }
148
- }, 100) // Small delay to allow nested buffers
149
- }
150
146
  return result
151
147
  }
152
148
  catch (error) {
@@ -155,9 +151,16 @@ const makeEventBuffer = (logger) => {
155
151
  finally {
156
152
  bufferCount = Math.max(0, bufferCount - 1)
157
153
 
158
- if (bufferCount === 0) {
159
- // Auto-flush when no other buffers are active
160
- setTimeout(flush, 100)
154
+ if (bufferCount === 0 && isBuffering) {
155
+ // Only schedule ONE flush timer at a time to prevent timer storms
156
+ if (!flushPendingTimeout) {
157
+ flushPendingTimeout = setTimeout(() => {
158
+ flushPendingTimeout = null
159
+ if (isBuffering && bufferCount === 0) {
160
+ flush()
161
+ }
162
+ }, 100)
163
+ }
161
164
  }
162
165
  }
163
166
  }
@@ -591,4 +594,4 @@ const stringifyMessageKey = (key) => `${key.remoteJid},${key.id},${key.fromMe ?
591
594
 
592
595
  module.exports = {
593
596
  makeEventBuffer
594
- }
597
+ }
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "__esModule", { value: true })
8
8
 
9
9
  const async_mutex_1 = __importDefault(require("async-mutex"))
10
10
  const promises_1 = require("fs/promises")
11
+ const { rename: fsRename } = promises_1
11
12
  const path_1 = require("path")
12
13
  const WAProto_1 = require("../../WAProto")
13
14
  const auth_utils_1 = require("./auth-utils")
@@ -37,13 +38,20 @@ const getFileLock = (path) => {
37
38
  * Would recommend writing an auth state for use with a proper SQL or No-SQL DB
38
39
  * */
39
40
  const useMultiFileAuthState = async (folder) => {
41
+ // Debounce timers per file to avoid rapid successive writes hammering disk
42
+ const writeDebounceTimers = new Map()
43
+ const WRITE_DEBOUNCE_MS = 300
44
+
40
45
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
46
  const writeData = async (data, file) => {
42
47
  const filePath = path_1.join(folder, fixFileName(file))
43
48
  const mutex = getFileLock(filePath)
44
49
  return mutex.acquire().then(async (release) => {
45
50
  try {
46
- await promises_1.writeFile(filePath, JSON.stringify(data, generics_1.BufferJSON.replacer))
51
+ // Atomic write: write to temp file then rename to prevent corruption
52
+ const tmpPath = filePath + '.tmp'
53
+ await promises_1.writeFile(tmpPath, JSON.stringify(data, generics_1.BufferJSON.replacer))
54
+ await fsRename(tmpPath, filePath)
47
55
  } finally {
48
56
  release()
49
57
  }
@@ -120,7 +128,24 @@ const useMultiFileAuthState = async (folder) => {
120
128
  }
121
129
  }
122
130
  },
123
- saveCreds: async () => {
131
+ // saveCreds debounced by default — aman dipanggil tiap creds.update
132
+ // tanpa spam disk. Untuk force immediate write, pakai saveCredsNow.
133
+ saveCreds: () => {
134
+ const existing = writeDebounceTimers.get('creds.json')
135
+ if (existing) clearTimeout(existing)
136
+ const timer = setTimeout(() => {
137
+ writeDebounceTimers.delete('creds.json')
138
+ writeData(creds, 'creds.json').catch(err => {
139
+ console.error('[useMultiFileAuthState] Failed to save creds.json:', err)
140
+ })
141
+ }, WRITE_DEBOUNCE_MS)
142
+ writeDebounceTimers.set('creds.json', timer)
143
+ },
144
+ // Force immediate write — pakai saat disconnect/cleanup agar creds tidak hilang
145
+ saveCredsNow: async () => {
146
+ const existing = writeDebounceTimers.get('creds.json')
147
+ if (existing) clearTimeout(existing)
148
+ writeDebounceTimers.delete('creds.json')
124
149
  return writeData(creds, 'creds.json')
125
150
  }
126
151
  }
@@ -128,4 +153,4 @@ const useMultiFileAuthState = async (folder) => {
128
153
 
129
154
  module.exports = {
130
155
  useMultiFileAuthState
131
- }
156
+ }
@@ -58,7 +58,7 @@ const generateLoginNode = (userJid, config) => {
58
58
  const { user, device } = WABinary_1.jidDecode(userJid)
59
59
  const payload = {
60
60
  ...getClientPayload(config),
61
- passive: false,
61
+ passive: true,
62
62
  pull: true,
63
63
  username: +user,
64
64
  device: device,
@@ -185,4 +185,4 @@ module.exports = {
185
185
  generateRegistrationNode,
186
186
  configureSuccessfulPairing,
187
187
  encodeSignedDeviceIdentity
188
- }
188
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pontasockets/baileys",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Baileys is a lightweight JavaScript library for interacting with the WhatsApp Web API using WebSocket.",
5
5
  "keywords": [
6
6
  "facebook",