@hansaka02/baileys 7.3.2 → 7.3.6

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 (210) hide show
  1. package/lib/Defaults/baileys-version.json +2 -2
  2. package/lib/Defaults/connection.js +51 -0
  3. package/lib/Defaults/constants.js +74 -0
  4. package/lib/Defaults/history.js +19 -0
  5. package/lib/Defaults/index.js +36 -142
  6. package/lib/Defaults/media.js +48 -0
  7. package/lib/Defaults/prefix.js +18 -0
  8. package/lib/Signal/Group/group-session-builder.js +10 -42
  9. package/lib/Signal/Group/group_cipher.js +9 -6
  10. package/lib/Signal/Group/index.js +39 -53
  11. package/lib/Signal/Group/keyhelper.js +8 -41
  12. package/lib/Signal/Group/sender-chain-key.js +5 -18
  13. package/lib/Signal/Group/sender-key-distribution-message.js +7 -7
  14. package/lib/Signal/Group/sender-key-message.js +12 -8
  15. package/lib/Signal/Group/sender-key-record.js +7 -16
  16. package/lib/Signal/Group/sender-key-state.js +15 -61
  17. package/lib/Signal/Group/sender-message-key.js +2 -2
  18. package/lib/Signal/libsignal.js +237 -177
  19. package/lib/Signal/lid-mapping.js +128 -71
  20. package/lib/Socket/Client/types.js +2 -2
  21. package/lib/Socket/Client/websocket.js +25 -16
  22. package/lib/Socket/business.js +46 -33
  23. package/lib/Socket/chats.js +286 -170
  24. package/lib/Socket/community.js +215 -77
  25. package/lib/Socket/groups.js +77 -61
  26. package/lib/Socket/index.js +4 -4
  27. package/lib/Socket/messages-recv.js +629 -457
  28. package/lib/Socket/messages-send.js +645 -656
  29. package/lib/Socket/mex.js +61 -0
  30. package/lib/Socket/newsletter.js +166 -245
  31. package/lib/Socket/socket.js +396 -170
  32. package/lib/Store/index.js +27 -11
  33. package/lib/Store/make-cache-manager-store.js +14 -15
  34. package/lib/Store/make-in-memory-store.js +28 -24
  35. package/lib/Types/LabelAssociation.js +2 -2
  36. package/lib/Types/Message.js +6 -6
  37. package/lib/Types/MexUpdates.js +5 -5
  38. package/lib/Types/Newsletter.js +32 -25
  39. package/lib/Types/State.js +4 -4
  40. package/lib/Types/index.js +28 -12
  41. package/lib/Utils/auth-utils.js +212 -375
  42. package/lib/Utils/baileys-event-stream.js +68 -69
  43. package/lib/Utils/browser-utils.js +43 -0
  44. package/lib/Utils/business.js +63 -53
  45. package/lib/Utils/chat-utils.js +241 -106
  46. package/lib/Utils/crypto.js +25 -45
  47. package/lib/Utils/decode-wa-message.js +361 -311
  48. package/lib/Utils/event-buffer.js +97 -42
  49. package/lib/Utils/generics.js +90 -207
  50. package/lib/Utils/history.js +29 -27
  51. package/lib/Utils/index.js +28 -14
  52. package/lib/Utils/link-preview.js +24 -62
  53. package/lib/Utils/logger.js +5 -5
  54. package/lib/Utils/lt-hash.js +29 -23
  55. package/lib/Utils/make-mutex.js +26 -28
  56. package/lib/Utils/message-retry-manager.js +55 -7
  57. package/lib/Utils/messages-media.js +434 -247
  58. package/lib/Utils/messages.js +963 -917
  59. package/lib/Utils/noise-handler.js +60 -20
  60. package/lib/Utils/pre-key-manager.js +126 -0
  61. package/lib/Utils/process-message.js +216 -141
  62. package/lib/Utils/signal.js +75 -37
  63. package/lib/Utils/use-multi-file-auth-state.js +18 -22
  64. package/lib/Utils/validate-connection.js +96 -66
  65. package/lib/WABinary/constants.js +1268 -1268
  66. package/lib/WABinary/decode.js +62 -34
  67. package/lib/WABinary/encode.js +57 -36
  68. package/lib/WABinary/generic-utils.js +4 -4
  69. package/lib/WABinary/index.js +27 -11
  70. package/lib/WABinary/jid-utils.js +58 -11
  71. package/lib/WAM/constants.js +19064 -11563
  72. package/lib/WAM/encode.js +71 -14
  73. package/lib/WAM/index.js +27 -11
  74. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +20 -16
  75. package/lib/WAUSync/Protocols/USyncContactProtocol.js +2 -2
  76. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +7 -4
  77. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +2 -2
  78. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +0 -2
  79. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -2
  80. package/lib/WAUSync/Protocols/index.js +27 -11
  81. package/lib/WAUSync/USyncQuery.js +51 -28
  82. package/lib/WAUSync/index.js +27 -11
  83. package/lib/index.js +60 -31
  84. package/package.json +12 -17
  85. package/WAProto/AICommon/AICommon.d.ts +0 -11702
  86. package/WAProto/Adv/Adv.d.ts +0 -643
  87. package/WAProto/BotMetadata/BotMetadata.d.ts +0 -5654
  88. package/WAProto/Cert/Cert.d.ts +0 -613
  89. package/WAProto/ChatLockSettings/ChatLockSettings.d.ts +0 -476
  90. package/WAProto/CompanionReg/CompanionReg.d.ts +0 -1361
  91. package/WAProto/DeviceCapabilities/DeviceCapabilities.d.ts +0 -577
  92. package/WAProto/E2E/E2E.d.ts +0 -41724
  93. package/WAProto/Ephemeral/Ephemeral.d.ts +0 -114
  94. package/WAProto/HistorySync/HistorySync.d.ts +0 -51700
  95. package/WAProto/LidMigrationSyncPayload/LidMigrationSyncPayload.d.ts +0 -229
  96. package/WAProto/MdStorageChatRowOpaqueData/MdStorageChatRowOpaqueData.d.ts +0 -583
  97. package/WAProto/MdStorageMsgRowOpaqueData/MdStorageMsgRowOpaqueData.d.ts +0 -42897
  98. package/WAProto/MmsRetry/MmsRetry.d.ts +0 -243
  99. package/WAProto/Protocol/Protocol.d.ts +0 -270
  100. package/WAProto/Reporting/Reporting.d.ts +0 -371
  101. package/WAProto/ServerSync/ServerSync.d.ts +0 -1285
  102. package/WAProto/SignalLocalStorageProtocol/SignalLocalStorageProtocol.d.ts +0 -1868
  103. package/WAProto/SignalWhisperTextProtocol/SignalWhisperTextProtocol.d.ts +0 -767
  104. package/WAProto/StatusAttributions/StatusAttributions.d.ts +0 -1027
  105. package/WAProto/SyncAction/SyncAction.d.ts +0 -11193
  106. package/WAProto/UserPassword/UserPassword.d.ts +0 -363
  107. package/WAProto/VnameCert/VnameCert.d.ts +0 -821
  108. package/WAProto/Wa6/Wa6.d.ts +0 -2128
  109. package/WAProto/Web/Web.d.ts +0 -46383
  110. package/WAProto/index.d.ts +0 -55
  111. package/lib/Defaults/index.d.ts +0 -77
  112. package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
  113. package/lib/Signal/Group/group-session-builder.d.ts +0 -17
  114. package/lib/Signal/Group/group_cipher.d.ts +0 -19
  115. package/lib/Signal/Group/index.d.ts +0 -11
  116. package/lib/Signal/Group/keyhelper.d.ts +0 -16
  117. package/lib/Signal/Group/sender-chain-key.d.ts +0 -14
  118. package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -17
  119. package/lib/Signal/Group/sender-key-message.d.ts +0 -19
  120. package/lib/Signal/Group/sender-key-name.d.ts +0 -19
  121. package/lib/Signal/Group/sender-key-record.d.ts +0 -32
  122. package/lib/Signal/Group/sender-key-state.d.ts +0 -44
  123. package/lib/Signal/Group/sender-message-key.d.ts +0 -11
  124. package/lib/Signal/libsignal.d.ts +0 -8
  125. package/lib/Signal/lid-mapping.d.ts +0 -28
  126. package/lib/Socket/Client/index.d.ts +0 -2
  127. package/lib/Socket/Client/types.d.ts +0 -16
  128. package/lib/Socket/Client/websocket.d.ts +0 -13
  129. package/lib/Socket/business.d.ts +0 -187
  130. package/lib/Socket/chats.d.ts +0 -97
  131. package/lib/Socket/community.d.ts +0 -129
  132. package/lib/Socket/groups.d.ts +0 -129
  133. package/lib/Socket/index.d.ts +0 -191
  134. package/lib/Socket/messages-recv.d.ts +0 -174
  135. package/lib/Socket/messages-send.d.ts +0 -165
  136. package/lib/Socket/newsletter.d.ts +0 -145
  137. package/lib/Socket/socket.d.ts +0 -45
  138. package/lib/Socket/usync.d.ts +0 -37
  139. package/lib/Socket/usync.js +0 -83
  140. package/lib/Store/index.d.ts +0 -4
  141. package/lib/Store/make-cache-manager-store.d.ts +0 -14
  142. package/lib/Store/make-in-memory-store.d.ts +0 -123
  143. package/lib/Store/make-ordered-dictionary.d.ts +0 -12
  144. package/lib/Store/object-repository.d.ts +0 -10
  145. package/lib/Types/Auth.d.ts +0 -121
  146. package/lib/Types/Bussiness.d.ts +0 -28
  147. package/lib/Types/Call.d.ts +0 -14
  148. package/lib/Types/Chat.d.ts +0 -143
  149. package/lib/Types/Contact.d.ts +0 -23
  150. package/lib/Types/Events.d.ts +0 -226
  151. package/lib/Types/GroupMetadata.d.ts +0 -66
  152. package/lib/Types/Label.d.ts +0 -48
  153. package/lib/Types/LabelAssociation.d.ts +0 -35
  154. package/lib/Types/Message.d.ts +0 -484
  155. package/lib/Types/MexUpdates.d.ts +0 -9
  156. package/lib/Types/Newsletter.d.ts +0 -109
  157. package/lib/Types/Product.d.ts +0 -92
  158. package/lib/Types/Signal.d.ts +0 -98
  159. package/lib/Types/Socket.d.ts +0 -141
  160. package/lib/Types/State.d.ts +0 -41
  161. package/lib/Types/USync.d.ts +0 -26
  162. package/lib/Types/index.d.ts +0 -80
  163. package/lib/Utils/auth-utils.d.ts +0 -21
  164. package/lib/Utils/baileys-event-stream.d.ts +0 -18
  165. package/lib/Utils/business.d.ts +0 -29
  166. package/lib/Utils/chat-utils.d.ts +0 -82
  167. package/lib/Utils/crypto.d.ts +0 -56
  168. package/lib/Utils/decode-wa-message.d.ts +0 -53
  169. package/lib/Utils/event-buffer.d.ts +0 -39
  170. package/lib/Utils/generics.d.ts +0 -117
  171. package/lib/Utils/history.d.ts +0 -23
  172. package/lib/Utils/index.d.ts +0 -20
  173. package/lib/Utils/link-preview.d.ts +0 -23
  174. package/lib/Utils/logger.d.ts +0 -13
  175. package/lib/Utils/lt-hash.d.ts +0 -14
  176. package/lib/Utils/make-mutex.d.ts +0 -9
  177. package/lib/Utils/message-retry-manager.d.ts +0 -88
  178. package/lib/Utils/messages-media.d.ts +0 -135
  179. package/lib/Utils/messages.d.ts +0 -105
  180. package/lib/Utils/noise-handler.d.ts +0 -20
  181. package/lib/Utils/process-message.d.ts +0 -49
  182. package/lib/Utils/signal.d.ts +0 -42
  183. package/lib/Utils/use-mongo-file-auth-state.d.ts +0 -6
  184. package/lib/Utils/use-mongo-file-auth-state.js +0 -84
  185. package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
  186. package/lib/Utils/use-single-file-auth-state.d.ts +0 -13
  187. package/lib/Utils/use-single-file-auth-state.js +0 -80
  188. package/lib/Utils/validate-connection.d.ts +0 -13
  189. package/lib/WABinary/constants.d.ts +0 -30
  190. package/lib/WABinary/decode.d.ts +0 -9
  191. package/lib/WABinary/encode.d.ts +0 -3
  192. package/lib/WABinary/generic-utils.d.ts +0 -28
  193. package/lib/WABinary/index.d.ts +0 -5
  194. package/lib/WABinary/jid-utils.d.ts +0 -58
  195. package/lib/WABinary/types.d.ts +0 -22
  196. package/lib/WAM/BinaryInfo.d.ts +0 -16
  197. package/lib/WAM/constants.d.ts +0 -47
  198. package/lib/WAM/encode.d.ts +0 -3
  199. package/lib/WAM/index.d.ts +0 -3
  200. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.d.ts +0 -28
  201. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -10
  202. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -26
  203. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -14
  204. package/lib/WAUSync/Protocols/USyncLIDProtocol.d.ts +0 -10
  205. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -14
  206. package/lib/WAUSync/Protocols/index.d.ts +0 -6
  207. package/lib/WAUSync/USyncQuery.d.ts +0 -31
  208. package/lib/WAUSync/USyncUser.d.ts +0 -12
  209. package/lib/WAUSync/index.d.ts +0 -3
  210. package/lib/index.d.ts +0 -13
@@ -1,18 +1,23 @@
1
1
  "use strict"
2
2
 
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod }
5
- }
6
-
7
3
  Object.defineProperty(exports, "__esModule", { value: true })
8
4
 
9
- const crypto_1 = require("crypto")
10
- const node_cache_1 = __importDefault(require("@cacheable/node-cache"))
11
- const Defaults_1 = require("../Defaults")
12
- const LRUCache_1 = require("lru-cache")
13
- const crypto_2 = require("./crypto")
14
- const generics_1 = require("./generics")
15
- const mutex_1 = require("async-mutex")
5
+ const { randomBytes } = require("crypto")
6
+ const { default: PQueue } = require("p-queue")
7
+ const { default: NodeCache } = require("@cacheable/node-cache")
8
+ const { DEFAULT_CACHE_TTLS } = require("../Defaults/constants")
9
+ const { AsyncLocalStorage } = require("async_hooks")
10
+ const { LRUCache } = require("lru-cache")
11
+ const { Mutex }= require("async-mutex")
12
+ const {
13
+ Curve,
14
+ signedKeyPair
15
+ } = require("./crypto")
16
+ const {
17
+ delay,
18
+ generateRegistrationId
19
+ } = require("./generics")
20
+ const { PreKeyManager } = require("./pre-key-manager")
16
21
 
17
22
  /**
18
23
  * Adds caching capability to a SignalKeyStore
@@ -21,14 +26,14 @@ const mutex_1 = require("async-mutex")
21
26
  * @param _cache cache store to use
22
27
  */
23
28
  function makeCacheableSignalKeyStore(store, logger, _cache) {
24
- const cache = _cache || new node_cache_1.default({
25
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.SIGNAL_STORE,
29
+ const cache = _cache || new NodeCache({
30
+ stdTTL: DEFAULT_CACHE_TTLS.SIGNAL_STORE,
26
31
  useClones: false,
27
- deleteOnExpire: true,
32
+ deleteOnExpire: true
28
33
  })
29
34
 
30
35
  // Mutex for protecting cache operations
31
- const cacheMutex = new mutex_1.Mutex()
36
+ const cacheMutex = new Mutex()
32
37
 
33
38
  function getUniqueId(type, id) {
34
39
  return `${type}.${id}`
@@ -81,427 +86,258 @@ function makeCacheableSignalKeyStore(store, logger, _cache) {
81
86
  }
82
87
  }
83
88
 
84
- // Module-level specialized mutexes for pre-key operations
85
- const preKeyMutex = new mutex_1.Mutex()
86
- const signedPreKeyMutex = new mutex_1.Mutex()
87
-
88
89
  /**
89
- * Get the appropriate mutex for the key type
90
- */
91
- const getPreKeyMutex = (keyType) => {
92
- return keyType === 'signed-pre-key' ? signedPreKeyMutex : preKeyMutex
93
- }
94
-
95
- /**
96
- * Handles pre-key operations with mutex protection
97
- */
98
- async function handlePreKeyOperations(data, keyType, transactionCache, mutations, logger, isInTransaction, state) {
99
- const mutex = getPreKeyMutex(keyType)
100
- await mutex.runExclusive(async () => {
101
- const keyData = data[keyType]
102
- if (!keyData)
103
- return
104
-
105
- // Ensure structures exist
106
- transactionCache[keyType] = transactionCache[keyType] || {}
107
- mutations[keyType] = mutations[keyType] || {}
108
-
109
- // Separate deletions from updates for batch processing
110
- const deletionKeys = []
111
- const updateKeys = []
112
-
113
- for (const keyId in keyData) {
114
- if (keyData[keyId] === null) {
115
- deletionKeys.push(keyId)
116
- }
117
- else {
118
- updateKeys.push(keyId)
119
- }
120
- }
121
-
122
- // Process updates first (no validation needed)
123
- for (const keyId of updateKeys) {
124
- if (transactionCache[keyType]) {
125
- transactionCache[keyType][keyId] = keyData[keyId]
126
- }
127
- if (mutations[keyType]) {
128
- mutations[keyType][keyId] = keyData[keyId]
129
- }
130
- }
131
-
132
- // Process deletions with validation
133
- if (deletionKeys.length === 0)
134
- return
135
-
136
- if (isInTransaction) {
137
- // In transaction, only allow deletion if key exists in cache
138
- for (const keyId of deletionKeys) {
139
- if (transactionCache[keyType]) {
140
- transactionCache[keyType][keyId] = null
141
- if (mutations[keyType]) {
142
- // Mark for deletion in mutations
143
- mutations[keyType][keyId] = null
144
- }
145
- }
146
- else {
147
- logger.warn(`Skipping deletion of non-existent ${keyType} in transaction: ${keyId}`)
148
- }
149
- }
150
- return
151
- }
152
-
153
- // Outside transaction, batch validate all deletions
154
- if (!state)
155
- return
156
-
157
- const existingKeys = await state.get(keyType, deletionKeys)
158
- for (const keyId of deletionKeys) {
159
- if (existingKeys[keyId]) {
160
- if (transactionCache[keyType])
161
- transactionCache[keyType][keyId] = null
162
- if (mutations[keyType])
163
- mutations[keyType][keyId] = null
164
- }
165
- else {
166
- logger.warn(`Skipping deletion of non-existent ${keyType}: ${keyId}`)
167
- }
168
- }
169
- })
170
- }
171
-
172
- /**
173
- * Handles normal key operations for transactions
174
- */
175
- function handleNormalKeyOperations(data, key, transactionCache, mutations) {
176
- Object.assign(transactionCache[key], data[key])
177
- mutations[key] = mutations[key] || {}
178
- Object.assign(mutations[key], data[key])
179
- }
180
-
181
- /**
182
- * Process pre-key deletions with validation
183
- */
184
- async function processPreKeyDeletions(data, keyType, state, logger) {
185
- const mutex = getPreKeyMutex(keyType)
186
- await mutex.runExclusive(async () => {
187
- const keyData = data[keyType]
188
- if (!keyData)
189
- return
190
-
191
- // Validate deletions
192
- for (const keyId in keyData) {
193
- if (keyData[keyId] === null) {
194
- const existingKeys = await state.get(keyType, [keyId])
195
- if (!existingKeys[keyId]) {
196
- logger.warn(`Skipping deletion of non-existent ${keyType}: ${keyId}`)
197
- if (data[keyType])
198
- delete data[keyType][keyId]
199
- }
200
- }
201
- }
202
- })
203
- }
204
-
205
- /**
206
- * Executes a function with mutexes acquired for given key types
207
- * Uses async-mutex's runExclusive with efficient batching
208
- */
209
- async function withMutexes(keyTypes, getKeyTypeMutex, fn) {
210
- if (keyTypes.length === 0) {
211
- return fn()
212
- }
213
-
214
- if (keyTypes.length === 1) {
215
- return getKeyTypeMutex(keyTypes[0]).runExclusive(fn)
216
- }
217
-
218
- // For multiple mutexes, sort by key type to prevent deadlocks
219
- // Then acquire all mutexes in order using Promise.all for better efficiency
220
- const sortedKeyTypes = [...keyTypes].sort()
221
- const mutexes = sortedKeyTypes.map(getKeyTypeMutex)
222
-
223
- // Acquire all mutexes in order to prevent deadlocks
224
- const releases = []
225
- try {
226
- for (const mutex of mutexes) {
227
- releases.push(await mutex.acquire())
228
- }
229
- return await fn()
230
- }
231
- finally {
232
- // Release in reverse order
233
- while (releases.length > 0) {
234
- const release = releases.pop()
235
- if (release)
236
- release()
237
- }
238
- }
239
- }
240
-
241
- /**
242
- * Adds DB like transaction capability (https://en.wikipedia.org/wiki/Database_transaction) to the SignalKeyStore,
243
- * this allows batch read & write operations & improves the performance of the lib
90
+ * Adds DB-like transaction capability to the SignalKeyStore
91
+ * Uses AsyncLocalStorage for automatic context management
244
92
  * @param state the key store to apply this capability to
245
93
  * @param logger logger to log events
246
94
  * @returns SignalKeyStore with transaction capability
247
95
  */
248
96
  const addTransactionCapability = (state, logger, { maxCommitRetries, delayBetweenTriesMs }) => {
249
- // number of queries made to the DB during the transaction
250
- // only there for logging purposes
251
- let dbQueriesInTransaction = 0
252
- let transactionCache = {}
253
- let mutations = {}
97
+ const txStorage = new AsyncLocalStorage()
254
98
 
255
- // LRU Cache to hold mutexes for different key types
256
- const mutexCache = new LRUCache_1.LRUCache({
257
- ttl: 60 * 60 * 1000, // 1 hour
258
- ttlAutopurge: true,
259
- updateAgeOnGet: true
260
- })
99
+ // Queues for concurrency control (keyed by signal data type - bounded set)
100
+ const keyQueues = new Map()
261
101
 
262
- let transactionsInProgress = 0
102
+ // Transaction mutexes with reference counting for cleanup
103
+ const txMutexes = new Map()
263
104
 
264
- function getKeyTypeMutex(type) {
265
- return getMutex(`keytype:${type}`)
266
- }
105
+ const txMutexRefCounts = new Map()
267
106
 
268
- function getSenderKeyMutex(senderKeyName) {
269
- return getMutex(`senderkey:${senderKeyName}`)
270
- }
107
+ // Pre-key manager for specialized operations
108
+ const preKeyManager = new PreKeyManager(state, logger)
271
109
 
272
- function getTransactionMutex(key) {
273
- return getMutex(`transaction:${key}`)
110
+ /**
111
+ * Get or create a queue for a specific key type
112
+ */
113
+ function getQueue(key) {
114
+ if (!keyQueues.has(key)) {
115
+ keyQueues.set(key, new PQueue({ concurrency: 1 }))
116
+ }
117
+
118
+ return keyQueues.get(key)
274
119
  }
275
120
 
276
- // Get or create a mutex for a specific key name
277
- function getMutex(key) {
278
- let mutex = mutexCache.get(key)
279
- if (!mutex) {
280
- mutex = new mutex_1.Mutex()
281
- mutexCache.set(key, mutex)
282
- logger.info({ key }, 'created new mutex')
121
+ /**
122
+ * Get or create a transaction mutex
123
+ */
124
+ function getTxMutex(key) {
125
+ if (!txMutexes.has(key)) {
126
+ txMutexes.set(key, new Mutex())
127
+ txMutexRefCounts.set(key, 0)
283
128
  }
284
- return mutex
129
+
130
+ return txMutexes.get(key)
285
131
  }
286
132
 
287
- // Sender key operations with proper mutex sequencing
288
- function queueSenderKeyOperation(senderKeyName, operation) {
289
- return getSenderKeyMutex(senderKeyName).runExclusive(operation)
133
+ /**
134
+ * Acquire a reference to a transaction mutex
135
+ */
136
+ function acquireTxMutexRef(key) {
137
+ const count = txMutexRefCounts.get(key) ?? 0
138
+ txMutexRefCounts.set(key, count + 1)
290
139
  }
291
140
 
292
- // Check if we are currently in a transaction
141
+ /**
142
+ * Release a reference to a transaction mutex and cleanup if no longer needed
143
+ */
144
+ function releaseTxMutexRef(key) {
145
+ const count = (txMutexRefCounts.get(key) ?? 1) - 1
146
+
147
+ txMutexRefCounts.set(key, count)
148
+
149
+ // Cleanup if no more references and mutex is not locked
150
+ if (count <= 0) {
151
+ const mutex = txMutexes.get(key)
152
+
153
+ if (mutex && !mutex.isLocked()) {
154
+ txMutexes.delete(key)
155
+ txMutexRefCounts.delete(key)
156
+ }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Check if currently in a transaction
162
+ */
293
163
  function isInTransaction() {
294
- return transactionsInProgress > 0
164
+ return !!txStorage.getStore()
295
165
  }
296
166
 
297
- // Helper function to handle transaction commit with retries
298
- async function commitTransaction() {
299
- if (!Object.keys(mutations).length) {
167
+ /**
168
+ * Commit transaction with retries
169
+ */
170
+ async function commitWithRetry(mutations) {
171
+ if (Object.keys(mutations).length === 0) {
300
172
  logger.trace('no mutations in transaction')
301
173
  return
302
174
  }
303
- logger.trace('committing transaction');
304
- let tries = maxCommitRetries
305
- while (tries > 0) {
306
- tries -= 1;
175
+
176
+ logger.trace('committing transaction')
177
+
178
+ for (let attempt = 0; attempt < maxCommitRetries; attempt++) {
307
179
  try {
308
180
  await state.set(mutations)
309
- logger.trace({ dbQueriesInTransaction }, 'committed transaction')
181
+ logger.trace({ mutationCount: Object.keys(mutations).length }, 'committed transaction')
310
182
  return
311
183
  }
184
+
312
185
  catch (error) {
313
- logger.warn(`failed to commit ${Object.keys(mutations).length} mutations, tries left=${tries}`)
314
- if (tries > 0) {
315
- await generics_1.delay(delayBetweenTriesMs)
186
+ const retriesLeft = maxCommitRetries - attempt - 1
187
+
188
+ logger.warn(`failed to commit mutations, retries left=${retriesLeft}`)
189
+
190
+ if (retriesLeft === 0) {
191
+ throw error
316
192
  }
193
+
194
+ await delay(delayBetweenTriesMs)
317
195
  }
318
196
  }
319
197
  }
320
198
 
321
- // Helper function to clean up transaction state
322
- function cleanupTransactionState() {
323
- transactionsInProgress -= 1
324
- if (transactionsInProgress === 0) {
325
- transactionCache = {}
326
- mutations = {}
327
- dbQueriesInTransaction = 0
328
- }
329
- }
330
-
331
- // Helper function to execute work within transaction
332
- async function executeTransactionWork(work) {
333
- const result = await work()
334
- // commit if this is the outermost transaction
335
- if (transactionsInProgress === 1) {
336
- await commitTransaction()
337
- }
338
- return result
339
- }
340
-
341
199
  return {
342
200
  get: async (type, ids) => {
343
- if (isInTransaction()) {
344
- const dict = transactionCache[type]
345
- const idsRequiringFetch = dict ? ids.filter(item => typeof dict[item] === 'undefined') : ids
201
+ const ctx = txStorage.getStore()
202
+
203
+ if (!ctx) {
204
+ // No transaction - direct read without exclusive lock for concurrency
205
+ return state.get(type, ids)
206
+ }
207
+
208
+ // In transaction - check cache first
209
+ const cached = ctx.cache[type] || {}
210
+ const missing = ids.filter(id => !(id in cached))
211
+
212
+ if (missing.length > 0) {
213
+ ctx.dbQueries++
346
214
 
347
- // only fetch if there are any items to fetch
348
- if (idsRequiringFetch.length) {
349
- dbQueriesInTransaction += 1
350
-
351
- // Use per-sender-key queue for sender-key operations when possible
352
- if (type === 'sender-key') {
353
- logger.info({ idsRequiringFetch }, 'processing sender keys in transaction')
354
- // For sender keys, process each one with queued operations to maintain serialization
355
- for (const senderKeyName of idsRequiringFetch) {
356
- await queueSenderKeyOperation(senderKeyName, async () => {
357
- logger.info({ senderKeyName }, 'fetching sender key in transaction')
358
- const result = await state.get(type, [senderKeyName])
359
- // Update transaction cache
360
- transactionCache[type] || (transactionCache[type] = {})
361
- Object.assign(transactionCache[type], result)
362
- logger.info({ senderKeyName, hasResult: !!result[senderKeyName] }, 'sender key fetch complete')
363
- })
364
- }
365
- }
366
- else {
367
- // Use runExclusive for cleaner mutex handling
368
- await getKeyTypeMutex(type).runExclusive(async () => {
369
- const result = await state.get(type, idsRequiringFetch)
370
- // Update transaction cache
371
- transactionCache[type] || (transactionCache[type] = {})
372
- Object.assign(transactionCache[type], result)
373
- })
374
- }
375
- }
376
- return ids.reduce((dict, id) => {
377
- const value = transactionCache[type]?.[id]
378
- if (value) {
379
- dict[id] = value
380
- }
381
- return dict
382
- }, {})
215
+ logger.trace({ type, count: missing.length }, 'fetching missing keys in transaction')
216
+
217
+ const fetched = await getTxMutex(type).runExclusive(() => state.get(type, missing))
218
+
219
+ // Update cache
220
+ ctx.cache[type] = ctx.cache[type] || {};
221
+ Object.assign(ctx.cache[type], fetched);
383
222
  }
384
- else {
385
- // Not in transaction, fetch directly with queue protection
386
- if (type === 'sender-key') {
387
- // For sender keys, use individual queues to maintain per-key serialization
388
- const results = {}
389
- for (const senderKeyName of ids) {
390
- const result = await queueSenderKeyOperation(senderKeyName, async () => await state.get(type, [senderKeyName]))
391
- Object.assign(results, result)
392
- }
393
- return results
394
- }
395
- else {
396
- return await getKeyTypeMutex(type).runExclusive(() => state.get(type, ids))
223
+
224
+ // Return requested ids from cache
225
+ const result = {}
226
+
227
+ for (const id of ids) {
228
+ const value = ctx.cache[type]?.[id]
229
+
230
+ if (value !== undefined && value !== null) {
231
+ result[id] = value;
397
232
  }
398
233
  }
234
+
235
+ return result
399
236
  },
400
237
  set: async (data) => {
401
- if (isInTransaction()) {
402
- logger.trace({ types: Object.keys(data) }, 'caching in transaction')
403
- for (const key_ in data) {
404
- const key = key_
405
- transactionCache[key] = transactionCache[key] || {}
406
- // Special handling for pre-keys and signed-pre-keys
407
- if (key === 'pre-key') {
408
- await handlePreKeyOperations(data, key, transactionCache, mutations, logger, true)
409
- }
410
- else {
411
- // Normal handling for other key types
412
- handleNormalKeyOperations(data, key, transactionCache, mutations)
238
+ const ctx = txStorage.getStore()
239
+
240
+ if (!ctx) {
241
+ // No transaction - direct write with queue protection
242
+ const types = Object.keys(data)
243
+
244
+ // Process pre-keys with validation
245
+ for (const type_ of types) {
246
+ const type = type_
247
+ if (type === 'pre-key') {
248
+ await preKeyManager.validateDeletions(data, type)
413
249
  }
414
250
  }
251
+
252
+ // Write all data in parallel
253
+ await Promise.all(types.map(type => getQueue(type).add(async () => {
254
+ const typeData = { [type]: data[type] }
255
+ await state.set(typeData)
256
+ })))
257
+
258
+ return
415
259
  }
416
- else {
417
- // Not in transaction, apply directly with mutex protection
418
- const hasSenderKeys = 'sender-key' in data
419
- const senderKeyNames = hasSenderKeys ? Object.keys(data['sender-key'] || {}) : []
420
- if (hasSenderKeys) {
421
- logger.info({ senderKeyNames }, 'processing sender key set operations')
422
- // Handle sender key operations with per-key queues
423
- for (const senderKeyName of senderKeyNames) {
424
- await queueSenderKeyOperation(senderKeyName, async () => {
425
- // Create data subset for this specific sender key
426
- const senderKeyData = {
427
- 'sender-key': {
428
- [senderKeyName]: data['sender-key'][senderKeyName]
429
- }
430
- };
431
- logger.trace({ senderKeyName }, 'storing sender key')
432
- // Apply changes to the store
433
- await state.set(senderKeyData)
434
- logger.trace({ senderKeyName }, 'sender key stored')
435
- })
436
- }
437
- // Handle any non-sender-key data with regular mutexes
438
- const nonSenderKeyData = { ...data }
439
- delete nonSenderKeyData['sender-key']
440
- if (Object.keys(nonSenderKeyData).length > 0) {
441
- await withMutexes(Object.keys(nonSenderKeyData), getKeyTypeMutex, async () => {
442
- // Process pre-keys and signed-pre-keys separately with specialized mutexes
443
- for (const key_ in nonSenderKeyData) {
444
- const keyType = key_
445
- if (keyType === 'pre-key') {
446
- await processPreKeyDeletions(nonSenderKeyData, keyType, state, logger)
447
- }
448
- }
449
- // Apply changes to the store
450
- await state.set(nonSenderKeyData)
451
- })
452
- }
260
+
261
+ // In transaction - update cache and mutations
262
+ logger.trace({ types: Object.keys(data) }, 'caching in transaction')
263
+
264
+ for (const key_ in data) {
265
+ const key = key_
266
+
267
+ // Ensure structures exist
268
+ ctx.cache[key] = ctx.cache[key] || {}
269
+ ctx.mutations[key] = ctx.mutations[key] || {}
270
+
271
+ // Special handling for pre-keys
272
+ if (key === 'pre-key') {
273
+ await preKeyManager.processOperations(data, key, ctx.cache, ctx.mutations, true)
453
274
  }
275
+
454
276
  else {
455
- // No sender keys - use original logic
456
- await withMutexes(Object.keys(data), getKeyTypeMutex, async () => {
457
- // Process pre-keys and signed-pre-keys separately with specialized mutexes
458
- for (const key_ in data) {
459
- const keyType = key_
460
- if (keyType === 'pre-key') {
461
- await processPreKeyDeletions(data, keyType, state, logger)
462
- }
463
- }
464
- // Apply changes to the store
465
- await state.set(data)
466
- })
277
+ // Normal key types
278
+ Object.assign(ctx.cache[key], data[key])
279
+ Object.assign(ctx.mutations[key], data[key])
467
280
  }
468
281
  }
469
282
  },
470
283
  isInTransaction,
471
- async transaction(work, key) {
472
- const releaseTxMutex = await getTransactionMutex(key).acquire()
284
+ transaction: async (work, key) => {
285
+ const existing = txStorage.getStore()
286
+
287
+ // Nested transaction - reuse existing context
288
+ if (existing) {
289
+ logger.trace('reusing existing transaction context')
290
+ return work()
291
+ }
292
+
293
+ // New transaction - acquire mutex and create context
294
+ const mutex = getTxMutex(key)
295
+
296
+ acquireTxMutexRef(key)
297
+
473
298
  try {
474
- transactionsInProgress += 1;
475
- if (transactionsInProgress === 1) {
299
+ return await mutex.runExclusive(async () => {
300
+ const ctx = {
301
+ cache: {},
302
+ mutations: {},
303
+ dbQueries: 0
304
+ }
305
+
476
306
  logger.trace('entering transaction')
477
- }
478
- // Release the transaction mutex now that we've updated the counter
479
- // This allows other transactions to start preparing
480
- releaseTxMutex()
481
- try {
482
- return await executeTransactionWork(work)
483
- }
484
- finally {
485
- cleanupTransactionState();
486
- }
307
+
308
+ try {
309
+ const result = await txStorage.run(ctx, work)
310
+
311
+ // Commit mutations
312
+ await commitWithRetry(ctx.mutations)
313
+
314
+ logger.trace({ dbQueries: ctx.dbQueries }, 'transaction completed')
315
+
316
+ return result
317
+ }
318
+
319
+ catch (error) {
320
+ logger.error({ error }, 'transaction failed, rolling back')
321
+ throw error
322
+ }
323
+ })
487
324
  }
488
- catch (error) {
489
- releaseTxMutex()
490
- throw error
325
+ finally {
326
+ releaseTxMutexRef(key)
491
327
  }
492
328
  }
493
329
  }
494
330
  }
495
331
 
496
332
  const initAuthCreds = () => {
497
- const identityKey = crypto_2.Curve.generateKeyPair()
333
+ const identityKey = Curve.generateKeyPair()
498
334
  return {
499
- noiseKey: crypto_2.Curve.generateKeyPair(),
500
- pairingEphemeralKeyPair: crypto_2.Curve.generateKeyPair(),
335
+ noiseKey: Curve.generateKeyPair(),
336
+ pairingEphemeralKeyPair: Curve.generateKeyPair(),
501
337
  signedIdentityKey: identityKey,
502
- signedPreKey: crypto_2.signedKeyPair(identityKey, 1),
503
- registrationId: generics_1.generateRegistrationId(),
504
- advSecretKey: crypto_1.randomBytes(32).toString('base64'),
338
+ signedPreKey: signedKeyPair(identityKey, 1),
339
+ registrationId: generateRegistrationId(),
340
+ advSecretKey: randomBytes(32).toString('base64'),
505
341
  processedHistoryMessages: [],
506
342
  nextPreKeyId: 1,
507
343
  firstUnuploadedPreKeyId: 1,
@@ -512,7 +348,8 @@ const initAuthCreds = () => {
512
348
  registered: false,
513
349
  pairingCode: undefined,
514
350
  lastPropHash: undefined,
515
- routingInfo: undefined
351
+ routingInfo: undefined,
352
+ additionalData: undefined
516
353
  }
517
354
  }
518
355