@neelegirl/baileys 1.5.2 → 1.5.3

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 (193) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +195 -187
  3. package/WAProto/WAProto.proto +537 -236
  4. package/WAProto/index.d.ts +5971 -2388
  5. package/WAProto/index.js +17298 -6513
  6. package/lib/Defaults/baileys-version.json +3 -3
  7. package/lib/Defaults/index.d.ts +77 -67
  8. package/lib/Defaults/index.js +148 -136
  9. package/lib/Defaults/phonenumber-mcc.json +223 -223
  10. package/lib/Signal/WASignalGroup/GroupProtocol.js +1908 -1908
  11. package/lib/Signal/WASignalGroup/ciphertext-message.d.ts +9 -0
  12. package/lib/Signal/WASignalGroup/ciphertext-message.js +19 -0
  13. package/lib/Signal/WASignalGroup/ciphertext_message.js +15 -15
  14. package/lib/Signal/WASignalGroup/group-session-builder.d.ts +17 -0
  15. package/lib/Signal/WASignalGroup/group-session-builder.js +72 -0
  16. package/lib/Signal/WASignalGroup/group.proto +41 -41
  17. package/lib/Signal/WASignalGroup/group_cipher.d.ts +19 -0
  18. package/lib/Signal/WASignalGroup/group_cipher.js +101 -110
  19. package/lib/Signal/WASignalGroup/group_session_builder.js +45 -45
  20. package/lib/Signal/WASignalGroup/index.d.ts +11 -0
  21. package/lib/Signal/WASignalGroup/index.js +61 -6
  22. package/lib/Signal/WASignalGroup/keyhelper.d.ts +16 -0
  23. package/lib/Signal/WASignalGroup/keyhelper.js +58 -13
  24. package/lib/Signal/WASignalGroup/protobufs.js +2 -2
  25. package/lib/Signal/WASignalGroup/queue_job.js +68 -68
  26. package/lib/Signal/WASignalGroup/readme.md +5 -5
  27. package/lib/Signal/WASignalGroup/sender-chain-key.d.ts +14 -0
  28. package/lib/Signal/WASignalGroup/sender-chain-key.js +47 -0
  29. package/lib/Signal/WASignalGroup/sender-key-distribution-message.d.ts +17 -0
  30. package/lib/Signal/WASignalGroup/sender-key-distribution-message.js +71 -0
  31. package/lib/Signal/WASignalGroup/sender-key-message.d.ts +19 -0
  32. package/lib/Signal/WASignalGroup/sender-key-message.js +73 -0
  33. package/lib/Signal/WASignalGroup/sender-key-name.d.ts +19 -0
  34. package/lib/Signal/WASignalGroup/sender-key-name.js +59 -0
  35. package/lib/Signal/WASignalGroup/sender-key-record.d.ts +32 -0
  36. package/lib/Signal/WASignalGroup/sender-key-record.js +58 -0
  37. package/lib/Signal/WASignalGroup/sender-key-state.d.ts +44 -0
  38. package/lib/Signal/WASignalGroup/sender-key-state.js +147 -0
  39. package/lib/Signal/WASignalGroup/sender-message-key.d.ts +11 -0
  40. package/lib/Signal/WASignalGroup/sender-message-key.js +33 -0
  41. package/lib/Signal/WASignalGroup/sender_chain_key.js +49 -49
  42. package/lib/Signal/WASignalGroup/sender_key_distribution_message.js +77 -77
  43. package/lib/Signal/WASignalGroup/sender_key_message.js +91 -91
  44. package/lib/Signal/WASignalGroup/sender_key_name.js +69 -69
  45. package/lib/Signal/WASignalGroup/sender_key_record.js +55 -55
  46. package/lib/Signal/WASignalGroup/sender_key_state.js +128 -128
  47. package/lib/Signal/WASignalGroup/sender_message_key.js +38 -38
  48. package/lib/Signal/libsignal.d.ts +5 -1
  49. package/lib/Signal/libsignal.js +390 -161
  50. package/lib/Signal/lid-mapping.d.ts +28 -0
  51. package/lib/Signal/lid-mapping.js +184 -0
  52. package/lib/Socket/Client/abstract-socket-client.d.ts +15 -15
  53. package/lib/Socket/Client/abstract-socket-client.js +13 -13
  54. package/lib/Socket/Client/index.d.ts +2 -2
  55. package/lib/Socket/Client/mobile-socket-client.d.ts +12 -12
  56. package/lib/Socket/Client/mobile-socket-client.js +65 -65
  57. package/lib/Socket/Client/types.d.ts +1 -1
  58. package/lib/Socket/Client/websocket.d.ts +1 -1
  59. package/lib/Socket/business.d.ts +6 -6
  60. package/lib/Socket/business.js +152 -5
  61. package/lib/Socket/chats.d.ts +3 -4
  62. package/lib/Socket/chats.js +31 -26
  63. package/lib/Socket/communities.d.ts +223 -223
  64. package/lib/Socket/communities.js +432 -432
  65. package/lib/Socket/groups.d.ts +2 -4
  66. package/lib/Socket/groups.js +22 -14
  67. package/lib/Socket/index.d.ts +69 -69
  68. package/lib/Socket/index.js +3 -2
  69. package/lib/Socket/messages-recv.d.ts +3 -6
  70. package/lib/Socket/messages-recv.js +1449 -1707
  71. package/lib/Socket/messages-send.d.ts +2 -4
  72. package/lib/Socket/messages-send.js +617 -126
  73. package/lib/Socket/mex.d.ts +2 -2
  74. package/lib/Socket/mex.js +46 -46
  75. package/lib/Socket/newsletter.d.ts +2 -4
  76. package/lib/Socket/newsletter.js +294 -285
  77. package/lib/Socket/socket.js +318 -132
  78. package/lib/Socket/usync.js +3 -3
  79. package/lib/Store/index.d.ts +4 -4
  80. package/lib/Store/index.js +23 -23
  81. package/lib/Store/make-cache-manager-store.d.ts +13 -13
  82. package/lib/Store/make-cache-manager-store.js +89 -89
  83. package/lib/Store/make-in-memory-store.d.ts +122 -122
  84. package/lib/Store/make-in-memory-store.js +428 -428
  85. package/lib/Store/make-ordered-dictionary.d.ts +11 -11
  86. package/lib/Store/make-ordered-dictionary.js +85 -85
  87. package/lib/Store/object-repository.d.ts +9 -9
  88. package/lib/Store/object-repository.js +30 -30
  89. package/lib/Types/Auth.d.ts +5 -4
  90. package/lib/Types/Bussines.js +3 -0
  91. package/lib/Types/Bussiness.d.ts +28 -0
  92. package/lib/Types/Chat.d.ts +13 -8
  93. package/lib/Types/Contact.d.ts +4 -1
  94. package/lib/Types/Events.d.ts +13 -16
  95. package/lib/Types/GroupMetadata.d.ts +1 -1
  96. package/lib/Types/Message.d.ts +18 -7
  97. package/lib/Types/Message.js +7 -1
  98. package/lib/Types/MexUpdates.d.ts +8 -8
  99. package/lib/Types/MexUpdates.js +17 -17
  100. package/lib/Types/Newsletter.d.ts +1 -1
  101. package/lib/Types/Product.d.ts +1 -1
  102. package/lib/Types/Signal.d.ts +31 -1
  103. package/lib/Types/Socket.d.ts +34 -13
  104. package/lib/Types/State.d.ts +1 -1
  105. package/lib/Types/USync.d.ts +2 -2
  106. package/lib/Types/index.d.ts +16 -15
  107. package/lib/Types/index.js +4 -2
  108. package/lib/Utils/auth-utils.d.ts +20 -20
  109. package/lib/Utils/auth-utils.js +527 -204
  110. package/lib/Utils/baileys-event-stream.d.ts +17 -17
  111. package/lib/Utils/baileys-event-stream.js +69 -69
  112. package/lib/Utils/business.d.ts +28 -28
  113. package/lib/Utils/business.js +254 -254
  114. package/lib/Utils/chat-utils.d.ts +81 -81
  115. package/lib/Utils/chat-utils.js +808 -780
  116. package/lib/Utils/crypto.d.ts +55 -55
  117. package/lib/Utils/crypto.js +188 -178
  118. package/lib/Utils/decode-wa-message.d.ts +52 -40
  119. package/lib/Utils/decode-wa-message.js +322 -252
  120. package/lib/Utils/event-buffer.d.ts +38 -38
  121. package/lib/Utils/event-buffer.js +594 -564
  122. package/lib/Utils/generics.d.ts +131 -129
  123. package/lib/Utils/generics.js +629 -623
  124. package/lib/Utils/history.d.ts +22 -22
  125. package/lib/Utils/history.js +103 -109
  126. package/lib/Utils/index.d.ts +20 -19
  127. package/lib/Utils/index.js +39 -38
  128. package/lib/Utils/link-preview.d.ts +22 -22
  129. package/lib/Utils/link-preview.js +119 -119
  130. package/lib/Utils/logger.d.ts +13 -13
  131. package/lib/Utils/logger.js +7 -7
  132. package/lib/Utils/lt-hash.d.ts +13 -13
  133. package/lib/Utils/lt-hash.js +57 -57
  134. package/lib/Utils/make-mutex.d.ts +8 -8
  135. package/lib/Utils/make-mutex.js +48 -48
  136. package/lib/Utils/message-retry-manager.d.ts +88 -0
  137. package/lib/Utils/message-retry-manager.js +160 -0
  138. package/lib/Utils/messages-media.d.ts +134 -128
  139. package/lib/Utils/messages-media.js +868 -805
  140. package/lib/Utils/messages.d.ts +104 -102
  141. package/lib/Utils/messages.js +1744 -1578
  142. package/lib/Utils/noise-handler.d.ts +20 -19
  143. package/lib/Utils/noise-handler.js +164 -154
  144. package/lib/Utils/process-message.d.ts +48 -48
  145. package/lib/Utils/process-message.js +427 -428
  146. package/lib/Utils/signal.d.ts +41 -41
  147. package/lib/Utils/signal.js +165 -165
  148. package/lib/Utils/use-mongo-file-auth-state.d.ts +5 -5
  149. package/lib/Utils/use-mongo-file-auth-state.js +83 -83
  150. package/lib/Utils/use-multi-file-auth-state.d.ts +17 -17
  151. package/lib/Utils/use-multi-file-auth-state.js +237 -237
  152. package/lib/Utils/use-single-file-auth-state.d.ts +12 -12
  153. package/lib/Utils/use-single-file-auth-state.js +79 -79
  154. package/lib/Utils/validate-connection.d.ts +12 -12
  155. package/lib/Utils/validate-connection.js +219 -186
  156. package/lib/WABinary/constants.d.ts +29 -29
  157. package/lib/WABinary/constants.js +1315 -1315
  158. package/lib/WABinary/decode.d.ts +8 -8
  159. package/lib/WABinary/decode.js +287 -287
  160. package/lib/WABinary/encode.d.ts +2 -2
  161. package/lib/WABinary/encode.js +264 -264
  162. package/lib/WABinary/generic-utils.d.ts +27 -27
  163. package/lib/WABinary/generic-utils.js +141 -141
  164. package/lib/WABinary/index.d.ts +5 -5
  165. package/lib/WABinary/index.js +24 -24
  166. package/lib/WABinary/jid-utils.d.ts +58 -53
  167. package/lib/WABinary/jid-utils.js +103 -91
  168. package/lib/WABinary/types.d.ts +21 -21
  169. package/lib/WABinary/types.js +2 -2
  170. package/lib/WAM/BinaryInfo.d.ts +15 -15
  171. package/lib/WAM/BinaryInfo.js +16 -16
  172. package/lib/WAM/constants.d.ts +46 -46
  173. package/lib/WAM/constants.js +15370 -15370
  174. package/lib/WAM/encode.d.ts +2 -2
  175. package/lib/WAM/encode.js +163 -164
  176. package/lib/WAM/index.d.ts +3 -3
  177. package/lib/WAM/index.js +22 -22
  178. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.d.ts +27 -27
  179. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +68 -68
  180. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +3 -3
  181. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +2 -2
  182. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +2 -2
  183. package/lib/WAUSync/Protocols/USyncLIDProtocol.d.ts +9 -8
  184. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +37 -29
  185. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +2 -2
  186. package/lib/WAUSync/Protocols/index.d.ts +6 -6
  187. package/lib/WAUSync/USyncQuery.d.ts +3 -3
  188. package/lib/WAUSync/index.d.ts +3 -3
  189. package/lib/index.d.ts +13 -13
  190. package/lib/index.js +33 -33
  191. package/package.json +96 -94
  192. package/lib/Socket/registration.d.ts +0 -266
  193. package/lib/Socket/registration.js +0 -166
@@ -1,205 +1,528 @@
1
- "use strict"
2
-
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod }
5
- }
6
-
7
- Object.defineProperty(exports, "__esModule", { value: true })
8
-
9
- const crypto_1 = require("crypto")
10
- const node_cache_1 = __importDefault(require("@cacheable/node-cache"))
11
- const Defaults_1 = require("../Defaults")
12
- const crypto_2 = require("./crypto")
13
- const generics_1 = require("./generics")
14
-
15
- /**
16
- * Adds caching capability to a SignalKeyStore
17
- * @param store the store to add caching to
18
- * @param logger to log trace events
19
- * @param _cache cache store to use
20
- */
21
- function makeCacheableSignalKeyStore(store, logger, _cache) {
22
- const cache = _cache || new node_cache_1.default({
23
- stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.SIGNAL_STORE,
24
- useClones: false,
25
- deleteOnExpire: true,
26
- })
27
- function getUniqueId(type, id) {
28
- return `${type}.${id}`
29
- }
30
- return {
31
- async get(type, ids) {
32
- const data = {}
33
- const idsToFetch = []
34
- for (const id of ids) {
35
- const item = cache.get(getUniqueId(type, id))
36
- if (typeof item !== 'undefined') {
37
- data[id] = item
38
- }
39
- else {
40
- idsToFetch.push(id)
41
- }
42
- }
43
- if (idsToFetch.length) {
44
- logger?.trace({ items: idsToFetch.length }, 'loading from store')
45
- const fetched = await store.get(type, idsToFetch)
46
- for (const id of idsToFetch) {
47
- const item = fetched[id]
48
- if (item) {
49
- data[id] = item
50
- cache.set(getUniqueId(type, id), item)
51
- }
52
- }
53
- }
54
- return data
55
- },
56
- async set(data) {
57
- let keys = 0
58
- for (const type in data) {
59
- for (const id in data[type]) {
60
- cache.set(getUniqueId(type, id), data[type][id])
61
- keys += 1
62
- }
63
- }
64
- logger?.trace({ keys }, 'updated cache')
65
- await store.set(data)
66
- },
67
- async clear() {
68
- cache.flushAll()
69
- await store.clear?.call(store)
70
- }
71
- }
72
- }
73
-
74
- /**
75
- * Adds DB like transaction capability (https://en.wikipedia.org/wiki/Database_transaction) to the SignalKeyStore,
76
- * this allows batch read & write operations & improves the performance of the lib
77
- * @param state the key store to apply this capability to
78
- * @param logger logger to log events
79
- * @returns SignalKeyStore with transaction capability
80
- */
81
- const addTransactionCapability = (state, logger, { maxCommitRetries, delayBetweenTriesMs }) => {
82
- // number of queries made to the DB during the transaction
83
- // only there for logging purposes
84
- let dbQueriesInTransaction = 0
85
- let transactionCache = {}
86
- let mutations = {}
87
- let transactionsInProgress = 0
88
- return {
89
- get: async (type, ids) => {
90
- if (isInTransaction()) {
91
- const dict = transactionCache[type]
92
- const idsRequiringFetch = dict
93
- ? ids.filter(item => typeof dict[item] === 'undefined')
94
- : ids
95
- // only fetch if there are any items to fetch
96
- if (idsRequiringFetch.length) {
97
- dbQueriesInTransaction += 1
98
- const result = await state.get(type, idsRequiringFetch)
99
- transactionCache[type] || (transactionCache[type] = {})
100
- Object.assign(transactionCache[type], result)
101
- }
102
- return ids.reduce((dict, id) => {
103
- const value = transactionCache[type]?.[id]
104
- if (value) {
105
- dict[id] = value
106
- }
107
- return dict
108
- }, {})
109
- }
110
- else {
111
- return state.get(type, ids)
112
- }
113
- },
114
- set: data => {
115
- if (isInTransaction()) {
116
- logger.trace({ types: Object.keys(data) }, 'caching in transaction')
117
- for (const key in data) {
118
- transactionCache[key] = transactionCache[key] || {}
119
- Object.assign(transactionCache[key], data[key])
120
- mutations[key] = mutations[key] || {}
121
- Object.assign(mutations[key], data[key])
122
- }
123
- }
124
- else {
125
- return state.set(data)
126
- }
127
- },
128
- isInTransaction,
129
- async transaction(work) {
130
- let result
131
- transactionsInProgress += 1
132
- if (transactionsInProgress === 1) {
133
- logger.trace('entering transaction')
134
- }
135
- try {
136
- result = await work()
137
- // commit if this is the outermost transaction
138
- if (transactionsInProgress === 1) {
139
- if (Object.keys(mutations).length) {
140
- logger.trace('committing transaction')
141
- // retry mechanism to ensure we've some recovery
142
- // in case a transaction fails in the first attempt
143
- let tries = maxCommitRetries
144
- while (tries) {
145
- tries -= 1
146
- try {
147
- await state.set(mutations)
148
- logger.trace({ dbQueriesInTransaction }, 'committed transaction')
149
- break
150
- }
151
- catch (error) {
152
- logger.warn(`failed to commit ${Object.keys(mutations).length} mutations, tries left=${tries}`)
153
- await generics_1.delay(delayBetweenTriesMs)
154
- }
155
- }
156
- }
157
- else {
158
- logger.trace('no mutations in transaction')
159
- }
160
- }
161
- }
162
- finally {
163
- transactionsInProgress -= 1
164
- if (transactionsInProgress === 0) {
165
- transactionCache = {}
166
- mutations = {}
167
- dbQueriesInTransaction = 0
168
- }
169
- }
170
- return result
171
- }
172
- }
173
- function isInTransaction() {
174
- return transactionsInProgress > 0
175
- }
176
- }
177
-
178
- const initAuthCreds = () => {
179
- const identityKey = crypto_2.Curve.generateKeyPair()
180
- return {
181
- noiseKey: crypto_2.Curve.generateKeyPair(),
182
- pairingEphemeralKeyPair: crypto_2.Curve.generateKeyPair(),
183
- signedIdentityKey: identityKey,
184
- signedPreKey: crypto_2.signedKeyPair(identityKey, 1),
185
- registrationId: generics_1.generateRegistrationId(),
186
- advSecretKey: crypto_1.randomBytes(32).toString('base64'),
187
- processedHistoryMessages: [],
188
- nextPreKeyId: 1,
189
- firstUnuploadedPreKeyId: 1,
190
- accountSyncCounter: 0,
191
- accountSettings: {
192
- unarchiveChats: false
193
- },
194
- registered: false,
195
- pairingCode: undefined,
196
- lastPropHash: undefined,
197
- routingInfo: undefined
198
- }
199
- }
200
-
201
- module.exports = {
202
- makeCacheableSignalKeyStore,
203
- addTransactionCapability,
204
- initAuthCreds
1
+ "use strict"
2
+
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod }
5
+ }
6
+
7
+ Object.defineProperty(exports, "__esModule", { value: true })
8
+
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")
16
+ const uuid_1 = require("uuid");
17
+ /**
18
+ * Adds caching capability to a SignalKeyStore
19
+ * @param store the store to add caching to
20
+ * @param logger to log trace events
21
+ * @param _cache cache store to use
22
+ */
23
+ function makeCacheableSignalKeyStore(store, logger, _cache) {
24
+ const cache = _cache || new node_cache_1.default({
25
+ stdTTL: Defaults_1.DEFAULT_CACHE_TTLS.SIGNAL_STORE,
26
+ useClones: false,
27
+ deleteOnExpire: true,
28
+ })
29
+
30
+ // Mutex for protecting cache operations
31
+ const cacheMutex = new mutex_1.Mutex()
32
+
33
+ function getUniqueId(type, id) {
34
+ return `${type}.${id}`
35
+ }
36
+ return {
37
+ async get(type, ids) {
38
+ return cacheMutex.runExclusive(async () => {
39
+ const data = {}
40
+ const idsToFetch = []
41
+ for (const id of ids) {
42
+ const item = cache.get(getUniqueId(type, id))
43
+ if (typeof item !== 'undefined') {
44
+ data[id] = item
45
+ }
46
+ else {
47
+ idsToFetch.push(id)
48
+ }
49
+ }
50
+ if (idsToFetch.length) {
51
+ logger?.trace({ items: idsToFetch.length }, 'loading from store')
52
+ const fetched = await store.get(type, idsToFetch)
53
+ for (const id of idsToFetch) {
54
+ const item = fetched[id]
55
+ if (item) {
56
+ data[id] = item
57
+ cache.set(getUniqueId(type, id), item)
58
+ }
59
+ }
60
+ }
61
+ return data
62
+ })
63
+ },
64
+ async set(data) {
65
+ return cacheMutex.runExclusive(async () => {
66
+ let keys = 0
67
+ for (const type in data) {
68
+ for (const id in data[type]) {
69
+ await cache.set(getUniqueId(type, id), data[type][id])
70
+ keys += 1
71
+ }
72
+ }
73
+ logger?.trace({ keys }, 'updated cache')
74
+ await store.set(data)
75
+ })
76
+ },
77
+ async clear() {
78
+ await cache.flushAll()
79
+ await store.clear?.call(store)
80
+ }
81
+ }
82
+ }
83
+
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
+ * 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
244
+ * @param state the key store to apply this capability to
245
+ * @param logger logger to log events
246
+ * @returns SignalKeyStore with transaction capability
247
+ */
248
+ 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 = {}
254
+
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
+ })
261
+
262
+ let transactionsInProgress = 0
263
+
264
+ function getKeyTypeMutex(type) {
265
+ return getMutex(`keytype:${type}`)
266
+ }
267
+
268
+ function getSenderKeyMutex(senderKeyName) {
269
+ return getMutex(`senderkey:${senderKeyName}`)
270
+ }
271
+
272
+ function getTransactionMutex(key) {
273
+ return getMutex(`transaction:${key}`)
274
+ }
275
+
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')
283
+ }
284
+ return mutex
285
+ }
286
+
287
+ // Sender key operations with proper mutex sequencing
288
+ function queueSenderKeyOperation(senderKeyName, operation) {
289
+ return getSenderKeyMutex(senderKeyName).runExclusive(operation)
290
+ }
291
+
292
+ // Check if we are currently in a transaction
293
+ function isInTransaction() {
294
+ return transactionsInProgress > 0
295
+ }
296
+
297
+ // Helper function to handle transaction commit with retries
298
+ async function commitTransaction() {
299
+ if (!Object.keys(mutations).length) {
300
+ logger.trace('no mutations in transaction')
301
+ return
302
+ }
303
+ logger.trace('committing transaction');
304
+ let tries = maxCommitRetries
305
+ while (tries > 0) {
306
+ tries -= 1;
307
+ try {
308
+ await state.set(mutations)
309
+ logger.trace({ dbQueriesInTransaction }, 'committed transaction')
310
+ return
311
+ }
312
+ 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)
316
+ }
317
+ }
318
+ }
319
+ }
320
+
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
+ return {
342
+ get: async (type, ids) => {
343
+ if (isInTransaction()) {
344
+ const dict = transactionCache[type]
345
+ const idsRequiringFetch = dict ? ids.filter(item => typeof dict[item] === 'undefined') : ids
346
+
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
+ }, {})
383
+ }
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))
397
+ }
398
+ }
399
+ },
400
+ 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)
413
+ }
414
+ }
415
+ }
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
+ }
453
+ }
454
+ 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
+ })
467
+ }
468
+ }
469
+ },
470
+ isInTransaction,
471
+ async transaction(work, key) {
472
+ const releaseTxMutex = await getTransactionMutex(key).acquire()
473
+ try {
474
+ transactionsInProgress += 1;
475
+ if (transactionsInProgress === 1) {
476
+ 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
+ }
487
+ }
488
+ catch (error) {
489
+ releaseTxMutex()
490
+ throw error
491
+ }
492
+ }
493
+ }
494
+ }
495
+
496
+ const initAuthCreds = () => {
497
+ const identityKey = crypto_2.Curve.generateKeyPair()
498
+ return {
499
+ noiseKey: crypto_2.Curve.generateKeyPair(),
500
+ pairingEphemeralKeyPair: crypto_2.Curve.generateKeyPair(),
501
+ signedIdentityKey: identityKey,
502
+ signedPreKey: crypto_2.signedKeyPair(identityKey, 1),
503
+ registrationId: generics_1.generateRegistrationId(),
504
+ advSecretKey: crypto_1.randomBytes(32).toString('base64'),
505
+ processedHistoryMessages: [],
506
+ nextPreKeyId: 1,
507
+ firstUnuploadedPreKeyId: 1,
508
+ accountSyncCounter: 0,
509
+ accountSettings: {
510
+ unarchiveChats: false
511
+ },
512
+ registered: false,
513
+ pairingCode: undefined,
514
+ lastPropHash: undefined,
515
+ routingInfo: undefined,
516
+ deviceId: Buffer.from((0, uuid_1.v4)().replace(/-/g, ''), 'hex').toString('base64url'),
517
+ phoneId: (0, uuid_1.v4)(),
518
+ identityId: (0, crypto_1.randomBytes)(20),
519
+ backupToken: (0, crypto_1.randomBytes)(20),
520
+ registration: {},
521
+ }
522
+ }
523
+
524
+ module.exports = {
525
+ makeCacheableSignalKeyStore,
526
+ addTransactionCapability,
527
+ initAuthCreds
205
528
  }