@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.
- package/lib/Defaults/baileys-version.json +2 -2
- package/lib/Defaults/connection.js +51 -0
- package/lib/Defaults/constants.js +74 -0
- package/lib/Defaults/history.js +19 -0
- package/lib/Defaults/index.js +36 -142
- package/lib/Defaults/media.js +48 -0
- package/lib/Defaults/prefix.js +18 -0
- package/lib/Signal/Group/group-session-builder.js +10 -42
- package/lib/Signal/Group/group_cipher.js +9 -6
- package/lib/Signal/Group/index.js +39 -53
- package/lib/Signal/Group/keyhelper.js +8 -41
- package/lib/Signal/Group/sender-chain-key.js +5 -18
- package/lib/Signal/Group/sender-key-distribution-message.js +7 -7
- package/lib/Signal/Group/sender-key-message.js +12 -8
- package/lib/Signal/Group/sender-key-record.js +7 -16
- package/lib/Signal/Group/sender-key-state.js +15 -61
- package/lib/Signal/Group/sender-message-key.js +2 -2
- package/lib/Signal/libsignal.js +237 -177
- package/lib/Signal/lid-mapping.js +128 -71
- package/lib/Socket/Client/types.js +2 -2
- package/lib/Socket/Client/websocket.js +25 -16
- package/lib/Socket/business.js +46 -33
- package/lib/Socket/chats.js +286 -170
- package/lib/Socket/community.js +215 -77
- package/lib/Socket/groups.js +77 -61
- package/lib/Socket/index.js +4 -4
- package/lib/Socket/messages-recv.js +629 -457
- package/lib/Socket/messages-send.js +645 -656
- package/lib/Socket/mex.js +61 -0
- package/lib/Socket/newsletter.js +166 -245
- package/lib/Socket/socket.js +396 -170
- package/lib/Store/index.js +27 -11
- package/lib/Store/make-cache-manager-store.js +14 -15
- package/lib/Store/make-in-memory-store.js +28 -24
- package/lib/Types/LabelAssociation.js +2 -2
- package/lib/Types/Message.js +6 -6
- package/lib/Types/MexUpdates.js +5 -5
- package/lib/Types/Newsletter.js +32 -25
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +28 -12
- package/lib/Utils/auth-utils.js +212 -375
- package/lib/Utils/baileys-event-stream.js +68 -69
- package/lib/Utils/browser-utils.js +43 -0
- package/lib/Utils/business.js +63 -53
- package/lib/Utils/chat-utils.js +241 -106
- package/lib/Utils/crypto.js +25 -45
- package/lib/Utils/decode-wa-message.js +361 -311
- package/lib/Utils/event-buffer.js +97 -42
- package/lib/Utils/generics.js +90 -207
- package/lib/Utils/history.js +29 -27
- package/lib/Utils/index.js +28 -14
- package/lib/Utils/link-preview.js +24 -62
- package/lib/Utils/logger.js +5 -5
- package/lib/Utils/lt-hash.js +29 -23
- package/lib/Utils/make-mutex.js +26 -28
- package/lib/Utils/message-retry-manager.js +55 -7
- package/lib/Utils/messages-media.js +434 -247
- package/lib/Utils/messages.js +963 -917
- package/lib/Utils/noise-handler.js +60 -20
- package/lib/Utils/pre-key-manager.js +126 -0
- package/lib/Utils/process-message.js +216 -141
- package/lib/Utils/signal.js +75 -37
- package/lib/Utils/use-multi-file-auth-state.js +18 -22
- package/lib/Utils/validate-connection.js +96 -66
- package/lib/WABinary/constants.js +1268 -1268
- package/lib/WABinary/decode.js +62 -34
- package/lib/WABinary/encode.js +57 -36
- package/lib/WABinary/generic-utils.js +4 -4
- package/lib/WABinary/index.js +27 -11
- package/lib/WABinary/jid-utils.js +58 -11
- package/lib/WAM/constants.js +19064 -11563
- package/lib/WAM/encode.js +71 -14
- package/lib/WAM/index.js +27 -11
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +20 -16
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +7 -4
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +0 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -2
- package/lib/WAUSync/Protocols/index.js +27 -11
- package/lib/WAUSync/USyncQuery.js +51 -28
- package/lib/WAUSync/index.js +27 -11
- package/lib/index.js +60 -31
- package/package.json +12 -17
- package/WAProto/AICommon/AICommon.d.ts +0 -11702
- package/WAProto/Adv/Adv.d.ts +0 -643
- package/WAProto/BotMetadata/BotMetadata.d.ts +0 -5654
- package/WAProto/Cert/Cert.d.ts +0 -613
- package/WAProto/ChatLockSettings/ChatLockSettings.d.ts +0 -476
- package/WAProto/CompanionReg/CompanionReg.d.ts +0 -1361
- package/WAProto/DeviceCapabilities/DeviceCapabilities.d.ts +0 -577
- package/WAProto/E2E/E2E.d.ts +0 -41724
- package/WAProto/Ephemeral/Ephemeral.d.ts +0 -114
- package/WAProto/HistorySync/HistorySync.d.ts +0 -51700
- package/WAProto/LidMigrationSyncPayload/LidMigrationSyncPayload.d.ts +0 -229
- package/WAProto/MdStorageChatRowOpaqueData/MdStorageChatRowOpaqueData.d.ts +0 -583
- package/WAProto/MdStorageMsgRowOpaqueData/MdStorageMsgRowOpaqueData.d.ts +0 -42897
- package/WAProto/MmsRetry/MmsRetry.d.ts +0 -243
- package/WAProto/Protocol/Protocol.d.ts +0 -270
- package/WAProto/Reporting/Reporting.d.ts +0 -371
- package/WAProto/ServerSync/ServerSync.d.ts +0 -1285
- package/WAProto/SignalLocalStorageProtocol/SignalLocalStorageProtocol.d.ts +0 -1868
- package/WAProto/SignalWhisperTextProtocol/SignalWhisperTextProtocol.d.ts +0 -767
- package/WAProto/StatusAttributions/StatusAttributions.d.ts +0 -1027
- package/WAProto/SyncAction/SyncAction.d.ts +0 -11193
- package/WAProto/UserPassword/UserPassword.d.ts +0 -363
- package/WAProto/VnameCert/VnameCert.d.ts +0 -821
- package/WAProto/Wa6/Wa6.d.ts +0 -2128
- package/WAProto/Web/Web.d.ts +0 -46383
- package/WAProto/index.d.ts +0 -55
- package/lib/Defaults/index.d.ts +0 -77
- package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
- package/lib/Signal/Group/group-session-builder.d.ts +0 -17
- package/lib/Signal/Group/group_cipher.d.ts +0 -19
- package/lib/Signal/Group/index.d.ts +0 -11
- package/lib/Signal/Group/keyhelper.d.ts +0 -16
- package/lib/Signal/Group/sender-chain-key.d.ts +0 -14
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -17
- package/lib/Signal/Group/sender-key-message.d.ts +0 -19
- package/lib/Signal/Group/sender-key-name.d.ts +0 -19
- package/lib/Signal/Group/sender-key-record.d.ts +0 -32
- package/lib/Signal/Group/sender-key-state.d.ts +0 -44
- package/lib/Signal/Group/sender-message-key.d.ts +0 -11
- package/lib/Signal/libsignal.d.ts +0 -8
- package/lib/Signal/lid-mapping.d.ts +0 -28
- package/lib/Socket/Client/index.d.ts +0 -2
- package/lib/Socket/Client/types.d.ts +0 -16
- package/lib/Socket/Client/websocket.d.ts +0 -13
- package/lib/Socket/business.d.ts +0 -187
- package/lib/Socket/chats.d.ts +0 -97
- package/lib/Socket/community.d.ts +0 -129
- package/lib/Socket/groups.d.ts +0 -129
- package/lib/Socket/index.d.ts +0 -191
- package/lib/Socket/messages-recv.d.ts +0 -174
- package/lib/Socket/messages-send.d.ts +0 -165
- package/lib/Socket/newsletter.d.ts +0 -145
- package/lib/Socket/socket.d.ts +0 -45
- package/lib/Socket/usync.d.ts +0 -37
- package/lib/Socket/usync.js +0 -83
- package/lib/Store/index.d.ts +0 -4
- package/lib/Store/make-cache-manager-store.d.ts +0 -14
- package/lib/Store/make-in-memory-store.d.ts +0 -123
- package/lib/Store/make-ordered-dictionary.d.ts +0 -12
- package/lib/Store/object-repository.d.ts +0 -10
- package/lib/Types/Auth.d.ts +0 -121
- package/lib/Types/Bussiness.d.ts +0 -28
- package/lib/Types/Call.d.ts +0 -14
- package/lib/Types/Chat.d.ts +0 -143
- package/lib/Types/Contact.d.ts +0 -23
- package/lib/Types/Events.d.ts +0 -226
- package/lib/Types/GroupMetadata.d.ts +0 -66
- package/lib/Types/Label.d.ts +0 -48
- package/lib/Types/LabelAssociation.d.ts +0 -35
- package/lib/Types/Message.d.ts +0 -484
- package/lib/Types/MexUpdates.d.ts +0 -9
- package/lib/Types/Newsletter.d.ts +0 -109
- package/lib/Types/Product.d.ts +0 -92
- package/lib/Types/Signal.d.ts +0 -98
- package/lib/Types/Socket.d.ts +0 -141
- package/lib/Types/State.d.ts +0 -41
- package/lib/Types/USync.d.ts +0 -26
- package/lib/Types/index.d.ts +0 -80
- package/lib/Utils/auth-utils.d.ts +0 -21
- package/lib/Utils/baileys-event-stream.d.ts +0 -18
- package/lib/Utils/business.d.ts +0 -29
- package/lib/Utils/chat-utils.d.ts +0 -82
- package/lib/Utils/crypto.d.ts +0 -56
- package/lib/Utils/decode-wa-message.d.ts +0 -53
- package/lib/Utils/event-buffer.d.ts +0 -39
- package/lib/Utils/generics.d.ts +0 -117
- package/lib/Utils/history.d.ts +0 -23
- package/lib/Utils/index.d.ts +0 -20
- package/lib/Utils/link-preview.d.ts +0 -23
- package/lib/Utils/logger.d.ts +0 -13
- package/lib/Utils/lt-hash.d.ts +0 -14
- package/lib/Utils/make-mutex.d.ts +0 -9
- package/lib/Utils/message-retry-manager.d.ts +0 -88
- package/lib/Utils/messages-media.d.ts +0 -135
- package/lib/Utils/messages.d.ts +0 -105
- package/lib/Utils/noise-handler.d.ts +0 -20
- package/lib/Utils/process-message.d.ts +0 -49
- package/lib/Utils/signal.d.ts +0 -42
- package/lib/Utils/use-mongo-file-auth-state.d.ts +0 -6
- package/lib/Utils/use-mongo-file-auth-state.js +0 -84
- package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
- package/lib/Utils/use-single-file-auth-state.d.ts +0 -13
- package/lib/Utils/use-single-file-auth-state.js +0 -80
- package/lib/Utils/validate-connection.d.ts +0 -13
- package/lib/WABinary/constants.d.ts +0 -30
- package/lib/WABinary/decode.d.ts +0 -9
- package/lib/WABinary/encode.d.ts +0 -3
- package/lib/WABinary/generic-utils.d.ts +0 -28
- package/lib/WABinary/index.d.ts +0 -5
- package/lib/WABinary/jid-utils.d.ts +0 -58
- package/lib/WABinary/types.d.ts +0 -22
- package/lib/WAM/BinaryInfo.d.ts +0 -16
- package/lib/WAM/constants.d.ts +0 -47
- package/lib/WAM/encode.d.ts +0 -3
- package/lib/WAM/index.d.ts +0 -3
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.d.ts +0 -28
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -10
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -26
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -14
- package/lib/WAUSync/Protocols/USyncLIDProtocol.d.ts +0 -10
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -14
- package/lib/WAUSync/Protocols/index.d.ts +0 -6
- package/lib/WAUSync/USyncQuery.d.ts +0 -31
- package/lib/WAUSync/USyncUser.d.ts +0 -12
- package/lib/WAUSync/index.d.ts +0 -3
- package/lib/index.d.ts +0 -13
package/lib/Utils/auth-utils.js
CHANGED
|
@@ -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
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
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
|
|
25
|
-
stdTTL:
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
250
|
-
// only there for logging purposes
|
|
251
|
-
let dbQueriesInTransaction = 0
|
|
252
|
-
let transactionCache = {}
|
|
253
|
-
let mutations = {}
|
|
97
|
+
const txStorage = new AsyncLocalStorage()
|
|
254
98
|
|
|
255
|
-
//
|
|
256
|
-
const
|
|
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
|
-
|
|
102
|
+
// Transaction mutexes with reference counting for cleanup
|
|
103
|
+
const txMutexes = new Map()
|
|
263
104
|
|
|
264
|
-
|
|
265
|
-
return getMutex(`keytype:${type}`)
|
|
266
|
-
}
|
|
105
|
+
const txMutexRefCounts = new Map()
|
|
267
106
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
107
|
+
// Pre-key manager for specialized operations
|
|
108
|
+
const preKeyManager = new PreKeyManager(state, logger)
|
|
271
109
|
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
-
|
|
129
|
+
|
|
130
|
+
return txMutexes.get(key)
|
|
285
131
|
}
|
|
286
132
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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
|
-
|
|
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
|
|
164
|
+
return !!txStorage.getStore()
|
|
295
165
|
}
|
|
296
166
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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({
|
|
181
|
+
logger.trace({ mutationCount: Object.keys(mutations).length }, 'committed transaction')
|
|
310
182
|
return
|
|
311
183
|
}
|
|
184
|
+
|
|
312
185
|
catch (error) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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
|
-
//
|
|
456
|
-
|
|
457
|
-
|
|
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
|
|
472
|
-
const
|
|
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
|
-
|
|
475
|
-
|
|
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
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
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
|
-
|
|
489
|
-
|
|
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 =
|
|
333
|
+
const identityKey = Curve.generateKeyPair()
|
|
498
334
|
return {
|
|
499
|
-
noiseKey:
|
|
500
|
-
pairingEphemeralKeyPair:
|
|
335
|
+
noiseKey: Curve.generateKeyPair(),
|
|
336
|
+
pairingEphemeralKeyPair: Curve.generateKeyPair(),
|
|
501
337
|
signedIdentityKey: identityKey,
|
|
502
|
-
signedPreKey:
|
|
503
|
-
registrationId:
|
|
504
|
-
advSecretKey:
|
|
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
|
|