zapo-js 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (589) hide show
  1. package/README.md +20 -4
  2. package/dist/appstate/WaAppStateCrypto.js +19 -26
  3. package/dist/appstate/WaAppStateSyncClient.js +293 -181
  4. package/dist/appstate/WaAppStateSyncResponseParser.js +16 -5
  5. package/dist/appstate/constants.js +4 -3
  6. package/dist/appstate/{store/sqlite.js → encoding.js} +13 -8
  7. package/dist/appstate/index.js +8 -6
  8. package/dist/appstate/utils.js +9 -34
  9. package/dist/auth/WaAuthClient.js +43 -61
  10. package/dist/auth/flow/WaAuthCredentialsFlow.js +22 -15
  11. package/dist/auth/index.js +1 -8
  12. package/dist/auth/pairing/WaPairingCodeCrypto.js +6 -4
  13. package/dist/auth/pairing/WaPairingFlow.js +34 -26
  14. package/dist/auth/pairing/WaQrFlow.js +37 -24
  15. package/dist/client/WaClient.js +275 -324
  16. package/dist/client/WaClientFactory.js +500 -133
  17. package/dist/client/connection/WaConnectionManager.js +301 -0
  18. package/dist/client/connection/WaKeyShareCoordinator.js +63 -0
  19. package/dist/client/connection/WaReceiptQueue.js +51 -0
  20. package/dist/client/coordinators/WaAppStateMutationCoordinator.js +471 -0
  21. package/dist/client/coordinators/WaBusinessCoordinator.js +241 -0
  22. package/dist/client/coordinators/WaGroupCoordinator.js +30 -16
  23. package/dist/client/coordinators/WaIncomingNodeCoordinator.js +21 -27
  24. package/dist/client/coordinators/WaMessageDispatchCoordinator.js +439 -701
  25. package/dist/client/coordinators/WaPassiveTasksCoordinator.js +74 -31
  26. package/dist/client/coordinators/WaPrivacyCoordinator.js +134 -0
  27. package/dist/client/coordinators/WaProfileCoordinator.js +212 -0
  28. package/dist/client/coordinators/WaRetryCoordinator.js +242 -57
  29. package/dist/client/coordinators/WaStreamControlCoordinator.js +18 -11
  30. package/dist/client/coordinators/WaTrustedContactTokenCoordinator.js +166 -0
  31. package/dist/client/dirty.js +74 -48
  32. package/dist/client/events/chat.js +4 -3
  33. package/dist/client/events/devices.js +72 -0
  34. package/dist/client/events/group.js +62 -47
  35. package/dist/client/events/identity.js +22 -0
  36. package/dist/client/events/privacy-token.js +39 -0
  37. package/dist/client/history-sync.js +94 -63
  38. package/dist/client/incoming.js +60 -27
  39. package/dist/client/mailbox.js +24 -23
  40. package/dist/client/messages.js +107 -31
  41. package/dist/client/messaging/fanout.js +199 -0
  42. package/dist/client/messaging/key-protocol.js +130 -0
  43. package/dist/client/messaging/participants.js +193 -0
  44. package/dist/client/persistence/WriteBehindPersistence.js +129 -0
  45. package/dist/client/tokens/cs-token.js +50 -0
  46. package/dist/client/tokens/tc-token.js +25 -0
  47. package/dist/crypto/core/hkdf.js +3 -8
  48. package/dist/crypto/core/index.js +2 -5
  49. package/dist/crypto/core/keys.js +6 -7
  50. package/dist/crypto/core/nonce.js +2 -0
  51. package/dist/crypto/core/primitives.js +12 -23
  52. package/dist/crypto/core/random.js +26 -23
  53. package/dist/crypto/curves/Ed25519.js +7 -8
  54. package/dist/crypto/curves/X25519.js +38 -22
  55. package/dist/crypto/index.js +1 -3
  56. package/dist/crypto/math/constants.js +13 -36
  57. package/dist/crypto/math/edwards.js +171 -44
  58. package/dist/crypto/math/fe.js +706 -0
  59. package/dist/crypto/math/mod.js +10 -3
  60. package/dist/esm/appstate/WaAppStateCrypto.js +7 -14
  61. package/dist/esm/appstate/WaAppStateSyncClient.js +284 -172
  62. package/dist/esm/appstate/WaAppStateSyncResponseParser.js +17 -6
  63. package/dist/esm/appstate/constants.js +3 -2
  64. package/dist/esm/appstate/{store/sqlite.js → encoding.js} +13 -8
  65. package/dist/esm/appstate/index.js +2 -2
  66. package/dist/esm/appstate/utils.js +8 -30
  67. package/dist/esm/auth/WaAuthClient.js +43 -61
  68. package/dist/esm/auth/flow/WaAuthCredentialsFlow.js +22 -15
  69. package/dist/esm/auth/index.js +0 -3
  70. package/dist/esm/auth/pairing/WaPairingCodeCrypto.js +6 -4
  71. package/dist/esm/auth/pairing/WaPairingFlow.js +28 -20
  72. package/dist/esm/auth/pairing/WaQrFlow.js +37 -24
  73. package/dist/esm/client/WaClient.js +275 -324
  74. package/dist/esm/client/WaClientFactory.js +501 -134
  75. package/dist/esm/client/connection/WaConnectionManager.js +297 -0
  76. package/dist/esm/client/connection/WaKeyShareCoordinator.js +59 -0
  77. package/dist/esm/client/connection/WaReceiptQueue.js +47 -0
  78. package/dist/esm/client/coordinators/WaAppStateMutationCoordinator.js +467 -0
  79. package/dist/esm/client/coordinators/WaBusinessCoordinator.js +238 -0
  80. package/dist/esm/client/coordinators/WaGroupCoordinator.js +23 -9
  81. package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +21 -27
  82. package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +443 -705
  83. package/dist/esm/client/coordinators/WaPassiveTasksCoordinator.js +74 -31
  84. package/dist/esm/client/coordinators/WaPrivacyCoordinator.js +131 -0
  85. package/dist/esm/client/coordinators/WaProfileCoordinator.js +209 -0
  86. package/dist/esm/client/coordinators/WaRetryCoordinator.js +244 -59
  87. package/dist/esm/client/coordinators/WaStreamControlCoordinator.js +19 -12
  88. package/dist/esm/client/coordinators/WaTrustedContactTokenCoordinator.js +162 -0
  89. package/dist/esm/client/dirty.js +69 -43
  90. package/dist/esm/client/events/chat.js +4 -3
  91. package/dist/esm/client/events/devices.js +68 -0
  92. package/dist/esm/client/events/group.js +53 -39
  93. package/dist/esm/client/events/identity.js +19 -0
  94. package/dist/esm/client/events/privacy-token.js +36 -0
  95. package/dist/esm/client/history-sync.js +91 -60
  96. package/dist/esm/client/incoming.js +61 -28
  97. package/dist/esm/client/mailbox.js +24 -23
  98. package/dist/esm/client/messages.js +108 -32
  99. package/dist/esm/client/messaging/fanout.js +196 -0
  100. package/dist/esm/client/messaging/key-protocol.js +127 -0
  101. package/dist/esm/client/messaging/participants.js +190 -0
  102. package/dist/esm/client/persistence/WriteBehindPersistence.js +125 -0
  103. package/dist/esm/client/tokens/cs-token.js +46 -0
  104. package/dist/esm/client/tokens/tc-token.js +18 -0
  105. package/dist/esm/crypto/core/hkdf.js +3 -8
  106. package/dist/esm/crypto/core/index.js +2 -3
  107. package/dist/esm/crypto/core/keys.js +3 -4
  108. package/dist/esm/crypto/core/nonce.js +2 -0
  109. package/dist/esm/crypto/core/primitives.js +12 -22
  110. package/dist/esm/crypto/core/random.js +25 -23
  111. package/dist/esm/crypto/curves/Ed25519.js +4 -5
  112. package/dist/esm/crypto/curves/X25519.js +35 -19
  113. package/dist/esm/crypto/index.js +0 -1
  114. package/dist/esm/crypto/math/constants.js +12 -35
  115. package/dist/esm/crypto/math/edwards.js +174 -47
  116. package/dist/esm/crypto/math/fe.js +691 -0
  117. package/dist/esm/crypto/math/mod.js +10 -1
  118. package/dist/esm/index.js +1 -1
  119. package/dist/esm/infra/log/ConsoleLogger.js +18 -17
  120. package/dist/esm/infra/log/PinoLogger.js +15 -9
  121. package/dist/esm/infra/log/types.js +11 -1
  122. package/dist/esm/infra/perf/BackgroundQueue.js +478 -0
  123. package/dist/esm/infra/perf/BoundedTaskQueue.js +16 -18
  124. package/dist/esm/infra/perf/PromiseDedup.js +20 -0
  125. package/dist/esm/infra/perf/SharedExclusiveGate.js +109 -0
  126. package/dist/esm/infra/perf/StoreLock.js +77 -0
  127. package/dist/esm/media/WaMediaCrypto.js +96 -16
  128. package/dist/esm/media/WaMediaTransferClient.js +251 -91
  129. package/dist/esm/media/conn.js +10 -6
  130. package/dist/esm/media/constants.js +6 -2
  131. package/dist/esm/message/WaMessageClient.js +30 -32
  132. package/dist/esm/message/ack.js +6 -6
  133. package/dist/esm/message/addon-crypto.js +59 -0
  134. package/dist/esm/message/content.js +195 -9
  135. package/dist/esm/message/icdc.js +76 -0
  136. package/dist/esm/message/incoming.js +129 -122
  137. package/dist/esm/message/index.js +2 -0
  138. package/dist/esm/message/phash.js +3 -1
  139. package/dist/esm/message/reporting-token.js +425 -0
  140. package/dist/esm/message/use-case-secret.js +49 -0
  141. package/dist/esm/protocol/appstate.js +27 -0
  142. package/dist/esm/protocol/browser.js +10 -18
  143. package/dist/esm/protocol/constants.js +6 -3
  144. package/dist/esm/protocol/defaults.js +6 -0
  145. package/dist/esm/protocol/index.js +2 -11
  146. package/dist/esm/protocol/jid.js +133 -52
  147. package/dist/esm/protocol/media.js +3 -3
  148. package/dist/esm/protocol/message.js +61 -1
  149. package/dist/esm/protocol/nodes.js +4 -0
  150. package/dist/esm/protocol/notification.js +3 -1
  151. package/dist/esm/protocol/privacy-token.js +17 -0
  152. package/dist/esm/protocol/privacy.js +55 -0
  153. package/dist/esm/protocol/stream.js +26 -1
  154. package/dist/esm/protocol/usync.js +11 -0
  155. package/dist/esm/retry/codec.js +216 -0
  156. package/dist/esm/retry/constants.js +1 -1
  157. package/dist/esm/retry/index.js +3 -2
  158. package/dist/esm/retry/parse.js +88 -86
  159. package/dist/esm/retry/replay.js +54 -51
  160. package/dist/esm/retry/tracker.js +94 -0
  161. package/dist/esm/signal/api/SignalDeviceSyncApi.js +276 -92
  162. package/dist/esm/signal/api/SignalDigestSyncApi.js +17 -8
  163. package/dist/esm/signal/api/SignalIdentitySyncApi.js +67 -37
  164. package/dist/esm/signal/api/SignalMissingPreKeysSyncApi.js +86 -67
  165. package/dist/esm/signal/api/SignalRotateKeyApi.js +4 -2
  166. package/dist/esm/signal/api/SignalSessionSyncApi.js +36 -34
  167. package/dist/esm/signal/api/result-map.js +10 -0
  168. package/dist/esm/signal/constants.js +0 -4
  169. package/dist/esm/signal/crypto/WaAdvSignature.js +13 -9
  170. package/dist/esm/signal/{store/sqlite.js → encoding.js} +93 -60
  171. package/dist/esm/signal/group/SenderKeyChain.js +28 -23
  172. package/dist/esm/signal/group/SenderKeyCodec.js +5 -6
  173. package/dist/esm/signal/group/SenderKeyManager.js +144 -115
  174. package/dist/esm/signal/index.js +2 -0
  175. package/dist/esm/signal/registration/keygen.js +6 -2
  176. package/dist/esm/signal/registration/utils.js +1 -0
  177. package/dist/esm/signal/session/SignalProtocol.js +164 -53
  178. package/dist/esm/signal/session/SignalRatchet.js +24 -15
  179. package/dist/esm/signal/session/SignalSession.js +14 -9
  180. package/dist/esm/signal/session/resolver.js +221 -0
  181. package/dist/esm/store/contracts/privacy-token.store.js +1 -0
  182. package/dist/esm/store/createStore.js +100 -188
  183. package/dist/esm/store/index.js +1 -10
  184. package/dist/esm/store/locks/appstate.lock.js +26 -0
  185. package/dist/esm/store/locks/auth.lock.js +15 -0
  186. package/dist/esm/store/locks/contact.lock.js +20 -0
  187. package/dist/esm/store/locks/device-list.lock.js +20 -0
  188. package/dist/esm/store/locks/message.lock.js +21 -0
  189. package/dist/esm/store/locks/participants.lock.js +20 -0
  190. package/dist/esm/store/locks/privacy-token.lock.js +18 -0
  191. package/dist/esm/store/locks/retry.lock.js +29 -0
  192. package/dist/esm/store/locks/sender-key.lock.js +52 -0
  193. package/dist/esm/store/locks/signal.lock.js +63 -0
  194. package/dist/esm/store/locks/thread.lock.js +21 -0
  195. package/dist/esm/store/noop.store.js +4 -7
  196. package/dist/esm/store/providers/memory/appstate.store.js +38 -16
  197. package/dist/esm/store/providers/memory/contact.store.js +5 -0
  198. package/dist/esm/store/providers/memory/device-list.store.js +12 -34
  199. package/dist/esm/store/providers/memory/message.store.js +11 -5
  200. package/dist/esm/store/providers/memory/participants.store.js +1 -8
  201. package/dist/esm/store/providers/memory/privacy-token.store.js +43 -0
  202. package/dist/esm/store/providers/memory/retry.store.js +77 -2
  203. package/dist/esm/store/providers/memory/sender-key.store.js +11 -8
  204. package/dist/esm/store/providers/memory/signal.store.js +47 -18
  205. package/dist/esm/store/providers/memory/thread.store.js +5 -0
  206. package/dist/esm/transport/WaComms.js +28 -24
  207. package/dist/esm/transport/WaWebSocket.js +115 -18
  208. package/dist/esm/transport/binary/constants.js +0 -30
  209. package/dist/esm/transport/binary/decoder.js +8 -8
  210. package/dist/esm/transport/binary/encoder.js +10 -9
  211. package/dist/esm/transport/binary/index.js +0 -1
  212. package/dist/esm/transport/index.js +1 -0
  213. package/dist/esm/transport/keepalive/WaKeepAlive.js +2 -8
  214. package/dist/esm/transport/node/WaNodeOrchestrator.js +25 -21
  215. package/dist/esm/transport/node/WaNodeTransport.js +0 -3
  216. package/dist/esm/transport/node/builders/{accountSync.js → account-sync.js} +16 -36
  217. package/dist/esm/transport/node/builders/business.js +129 -0
  218. package/dist/esm/transport/node/builders/global.js +370 -0
  219. package/dist/esm/transport/node/builders/index.js +7 -3
  220. package/dist/esm/transport/node/builders/message.js +63 -230
  221. package/dist/esm/transport/node/builders/pairing.js +2 -27
  222. package/dist/esm/transport/node/builders/privacy-token.js +41 -0
  223. package/dist/esm/transport/node/builders/privacy.js +48 -0
  224. package/dist/esm/transport/node/builders/profile.js +70 -0
  225. package/dist/esm/transport/node/builders/retry.js +10 -22
  226. package/dist/esm/transport/node/builders/usync.js +45 -0
  227. package/dist/esm/transport/node/helpers.js +125 -5
  228. package/dist/esm/transport/node/usync.js +5 -0
  229. package/dist/esm/transport/node/xml.js +35 -14
  230. package/dist/esm/transport/noise/WaClientPayload.js +10 -10
  231. package/dist/esm/transport/noise/WaFrameCodec.js +48 -33
  232. package/dist/esm/transport/noise/WaNoiseCert.js +4 -7
  233. package/dist/esm/transport/noise/WaNoiseSession.js +77 -29
  234. package/dist/esm/transport/noise/WaNoiseSocket.js +8 -4
  235. package/dist/esm/transport/proxy.js +27 -0
  236. package/dist/esm/transport/stream/parse.js +17 -48
  237. package/dist/esm/util/bytes.js +67 -45
  238. package/dist/esm/util/coercion.js +6 -14
  239. package/dist/esm/util/index.js +5 -0
  240. package/dist/esm/util/primitives.js +40 -14
  241. package/dist/index.js +7 -1
  242. package/dist/infra/log/ConsoleLogger.js +18 -17
  243. package/dist/infra/log/PinoLogger.js +15 -9
  244. package/dist/infra/log/types.js +12 -0
  245. package/dist/infra/perf/BackgroundQueue.js +482 -0
  246. package/dist/infra/perf/BoundedTaskQueue.js +16 -18
  247. package/dist/infra/perf/PromiseDedup.js +24 -0
  248. package/dist/infra/perf/SharedExclusiveGate.js +113 -0
  249. package/dist/infra/perf/StoreLock.js +81 -0
  250. package/dist/media/WaMediaCrypto.js +95 -15
  251. package/dist/media/WaMediaTransferClient.js +284 -91
  252. package/dist/media/conn.js +10 -6
  253. package/dist/media/constants.js +6 -2
  254. package/dist/message/WaMessageClient.js +31 -33
  255. package/dist/message/ack.js +6 -6
  256. package/dist/message/addon-crypto.js +65 -0
  257. package/dist/message/content.js +198 -9
  258. package/dist/message/icdc.js +81 -0
  259. package/dist/message/incoming.js +127 -120
  260. package/dist/message/index.js +2 -0
  261. package/dist/message/phash.js +3 -1
  262. package/dist/message/reporting-token.js +429 -0
  263. package/dist/message/use-case-secret.js +55 -0
  264. package/dist/protocol/appstate.js +28 -1
  265. package/dist/protocol/browser.js +10 -18
  266. package/dist/protocol/constants.js +26 -1
  267. package/dist/protocol/defaults.js +6 -0
  268. package/dist/protocol/index.js +23 -42
  269. package/dist/protocol/jid.js +140 -52
  270. package/dist/protocol/media.js +3 -3
  271. package/dist/protocol/message.js +62 -2
  272. package/dist/protocol/nodes.js +4 -0
  273. package/dist/protocol/notification.js +3 -1
  274. package/dist/protocol/privacy-token.js +20 -0
  275. package/dist/protocol/privacy.js +58 -0
  276. package/dist/protocol/stream.js +27 -2
  277. package/dist/protocol/usync.js +14 -0
  278. package/dist/retry/codec.js +220 -0
  279. package/dist/retry/constants.js +1 -1
  280. package/dist/retry/index.js +7 -5
  281. package/dist/retry/parse.js +88 -85
  282. package/dist/retry/replay.js +52 -49
  283. package/dist/retry/tracker.js +97 -0
  284. package/dist/signal/api/SignalDeviceSyncApi.js +273 -89
  285. package/dist/signal/api/SignalDigestSyncApi.js +17 -8
  286. package/dist/signal/api/SignalIdentitySyncApi.js +66 -36
  287. package/dist/signal/api/SignalMissingPreKeysSyncApi.js +82 -63
  288. package/dist/signal/api/SignalRotateKeyApi.js +4 -2
  289. package/dist/signal/api/SignalSessionSyncApi.js +36 -34
  290. package/dist/signal/api/result-map.js +13 -0
  291. package/dist/signal/constants.js +1 -5
  292. package/dist/signal/crypto/WaAdvSignature.js +11 -7
  293. package/dist/signal/{store/sqlite.js → encoding.js} +94 -61
  294. package/dist/signal/group/SenderKeyChain.js +27 -22
  295. package/dist/signal/group/SenderKeyCodec.js +5 -6
  296. package/dist/signal/group/SenderKeyManager.js +144 -115
  297. package/dist/signal/index.js +15 -1
  298. package/dist/signal/registration/keygen.js +6 -2
  299. package/dist/signal/registration/utils.js +1 -0
  300. package/dist/signal/session/SignalProtocol.js +164 -53
  301. package/dist/signal/session/SignalRatchet.js +24 -15
  302. package/dist/signal/session/SignalSession.js +14 -9
  303. package/dist/signal/session/resolver.js +224 -0
  304. package/dist/store/contracts/privacy-token.store.js +2 -0
  305. package/dist/store/createStore.js +100 -188
  306. package/dist/store/index.js +15 -33
  307. package/dist/store/locks/appstate.lock.js +29 -0
  308. package/dist/store/locks/auth.lock.js +18 -0
  309. package/dist/store/locks/contact.lock.js +23 -0
  310. package/dist/store/locks/device-list.lock.js +23 -0
  311. package/dist/store/locks/message.lock.js +24 -0
  312. package/dist/store/locks/participants.lock.js +23 -0
  313. package/dist/store/locks/privacy-token.lock.js +21 -0
  314. package/dist/store/locks/retry.lock.js +32 -0
  315. package/dist/store/locks/sender-key.lock.js +55 -0
  316. package/dist/store/locks/signal.lock.js +66 -0
  317. package/dist/store/locks/thread.lock.js +24 -0
  318. package/dist/store/noop.store.js +4 -7
  319. package/dist/store/providers/memory/appstate.store.js +36 -14
  320. package/dist/store/providers/memory/contact.store.js +5 -0
  321. package/dist/store/providers/memory/device-list.store.js +12 -34
  322. package/dist/store/providers/memory/message.store.js +11 -5
  323. package/dist/store/providers/memory/participants.store.js +1 -8
  324. package/dist/store/providers/memory/privacy-token.store.js +47 -0
  325. package/dist/store/providers/memory/retry.store.js +77 -2
  326. package/dist/store/providers/memory/sender-key.store.js +14 -11
  327. package/dist/store/providers/memory/signal.store.js +54 -25
  328. package/dist/store/providers/memory/thread.store.js +5 -0
  329. package/dist/transport/WaComms.js +30 -26
  330. package/dist/transport/WaWebSocket.js +148 -18
  331. package/dist/transport/binary/constants.js +1 -31
  332. package/dist/transport/binary/decoder.js +8 -8
  333. package/dist/transport/binary/encoder.js +10 -9
  334. package/dist/transport/binary/index.js +0 -4
  335. package/dist/transport/index.js +7 -1
  336. package/dist/transport/keepalive/WaKeepAlive.js +1 -7
  337. package/dist/transport/node/WaNodeOrchestrator.js +25 -21
  338. package/dist/transport/node/WaNodeTransport.js +0 -3
  339. package/dist/transport/node/builders/{accountSync.js → account-sync.js} +15 -35
  340. package/dist/transport/node/builders/business.js +137 -0
  341. package/dist/transport/node/builders/global.js +375 -0
  342. package/dist/transport/node/builders/index.js +29 -17
  343. package/dist/transport/node/builders/message.js +64 -236
  344. package/dist/transport/node/builders/pairing.js +2 -29
  345. package/dist/transport/node/builders/privacy-token.js +46 -0
  346. package/dist/transport/node/builders/privacy.js +55 -0
  347. package/dist/transport/node/builders/profile.js +78 -0
  348. package/dist/transport/node/builders/retry.js +9 -21
  349. package/dist/transport/node/builders/usync.js +49 -0
  350. package/dist/transport/node/helpers.js +131 -4
  351. package/dist/transport/node/usync.js +8 -0
  352. package/dist/transport/node/xml.js +35 -14
  353. package/dist/transport/noise/WaClientPayload.js +13 -13
  354. package/dist/transport/noise/WaFrameCodec.js +47 -32
  355. package/dist/transport/noise/WaNoiseCert.js +5 -8
  356. package/dist/transport/noise/WaNoiseSession.js +77 -29
  357. package/dist/transport/noise/WaNoiseSocket.js +8 -4
  358. package/dist/transport/proxy.js +34 -0
  359. package/dist/transport/stream/parse.js +20 -52
  360. package/dist/types/appstate/WaAppStateCrypto.d.ts +0 -1
  361. package/dist/types/appstate/WaAppStateSyncClient.d.ts +5 -2
  362. package/dist/types/appstate/constants.d.ts +1 -0
  363. package/dist/types/appstate/encoding.d.ts +7 -0
  364. package/dist/types/appstate/index.d.ts +3 -3
  365. package/dist/types/appstate/utils.d.ts +0 -3
  366. package/dist/types/auth/WaAuthClient.d.ts +10 -12
  367. package/dist/types/auth/flow/WaAuthCredentialsFlow.d.ts +1 -1
  368. package/dist/types/auth/index.d.ts +0 -4
  369. package/dist/types/auth/pairing/WaQrFlow.d.ts +1 -1
  370. package/dist/types/auth/types.d.ts +7 -9
  371. package/dist/types/client/WaClient.d.ts +42 -25
  372. package/dist/types/client/WaClientFactory.d.ts +33 -26
  373. package/dist/types/client/connection/WaConnectionManager.d.ts +66 -0
  374. package/dist/types/client/connection/WaKeyShareCoordinator.d.ts +14 -0
  375. package/dist/types/client/connection/WaReceiptQueue.d.ts +13 -0
  376. package/dist/types/client/coordinators/WaAppStateMutationCoordinator.d.ts +46 -0
  377. package/dist/types/client/coordinators/WaBusinessCoordinator.d.ts +57 -0
  378. package/dist/types/client/coordinators/WaIncomingNodeCoordinator.d.ts +3 -2
  379. package/dist/types/client/coordinators/WaMessageDispatchCoordinator.d.ts +29 -38
  380. package/dist/types/client/coordinators/WaPassiveTasksCoordinator.d.ts +4 -0
  381. package/dist/types/client/coordinators/WaPrivacyCoordinator.d.ts +26 -0
  382. package/dist/types/client/coordinators/WaProfileCoordinator.d.ts +36 -0
  383. package/dist/types/client/coordinators/WaRetryCoordinator.d.ts +8 -0
  384. package/dist/types/client/coordinators/WaStreamControlCoordinator.d.ts +3 -2
  385. package/dist/types/client/coordinators/WaTrustedContactTokenCoordinator.d.ts +45 -0
  386. package/dist/types/client/dirty.d.ts +1 -0
  387. package/dist/types/client/events/devices.d.ts +20 -0
  388. package/dist/types/client/events/group.d.ts +2 -1
  389. package/dist/types/client/events/identity.d.ts +9 -0
  390. package/dist/types/client/events/privacy-token.d.ts +7 -0
  391. package/dist/types/client/history-sync.d.ts +9 -6
  392. package/dist/types/client/incoming.d.ts +3 -1
  393. package/dist/types/client/index.d.ts +1 -1
  394. package/dist/types/client/mailbox.d.ts +3 -5
  395. package/dist/types/client/messages.d.ts +1 -2
  396. package/dist/types/client/messaging/fanout.d.ts +14 -0
  397. package/dist/types/client/messaging/key-protocol.d.ts +18 -0
  398. package/dist/types/client/messaging/participants.d.ts +13 -0
  399. package/dist/types/client/persistence/WriteBehindPersistence.d.ts +34 -0
  400. package/dist/types/client/tokens/cs-token.d.ts +10 -0
  401. package/dist/types/client/tokens/tc-token.d.ts +5 -0
  402. package/dist/types/client/types.d.ts +75 -4
  403. package/dist/types/crypto/core/hkdf.d.ts +0 -6
  404. package/dist/types/crypto/core/index.d.ts +2 -3
  405. package/dist/types/crypto/core/nonce.d.ts +2 -0
  406. package/dist/types/crypto/core/primitives.d.ts +0 -1
  407. package/dist/types/crypto/core/random.d.ts +2 -7
  408. package/dist/types/crypto/index.d.ts +0 -1
  409. package/dist/types/crypto/math/constants.d.ts +4 -2
  410. package/dist/types/crypto/math/fe.d.ts +30 -0
  411. package/dist/types/crypto/math/mod.d.ts +0 -2
  412. package/dist/types/crypto/math/types.d.ts +11 -4
  413. package/dist/types/index.d.ts +5 -3
  414. package/dist/types/infra/log/ConsoleLogger.d.ts +2 -1
  415. package/dist/types/infra/log/PinoLogger.d.ts +1 -1
  416. package/dist/types/infra/log/types.d.ts +1 -0
  417. package/dist/types/infra/perf/BackgroundQueue.d.ts +58 -0
  418. package/dist/types/infra/perf/BoundedTaskQueue.d.ts +1 -1
  419. package/dist/types/infra/perf/PromiseDedup.d.ts +4 -0
  420. package/dist/types/infra/perf/SharedExclusiveGate.d.ts +17 -0
  421. package/dist/types/infra/perf/StoreLock.d.ts +10 -0
  422. package/dist/types/media/WaMediaCrypto.d.ts +3 -2
  423. package/dist/types/media/WaMediaTransferClient.d.ts +16 -15
  424. package/dist/types/media/constants.d.ts +1 -1
  425. package/dist/types/media/index.d.ts +1 -1
  426. package/dist/types/media/types.d.ts +15 -2
  427. package/dist/types/message/addon-crypto.d.ts +25 -0
  428. package/dist/types/message/content.d.ts +8 -0
  429. package/dist/types/message/icdc.d.ts +13 -0
  430. package/dist/types/message/index.d.ts +2 -0
  431. package/dist/types/message/reporting-token.d.ts +18 -0
  432. package/dist/types/message/types.d.ts +45 -6
  433. package/dist/types/message/use-case-secret.d.ts +20 -0
  434. package/dist/types/protocol/appstate.d.ts +47 -0
  435. package/dist/types/protocol/constants.d.ts +8 -3
  436. package/dist/types/protocol/defaults.d.ts +6 -0
  437. package/dist/types/protocol/index.d.ts +2 -11
  438. package/dist/types/protocol/jid.d.ts +22 -5
  439. package/dist/types/protocol/message.d.ts +60 -0
  440. package/dist/types/protocol/nodes.d.ts +4 -0
  441. package/dist/types/protocol/notification.d.ts +2 -0
  442. package/dist/types/protocol/privacy-token.d.ts +17 -0
  443. package/dist/types/protocol/privacy.d.ts +75 -0
  444. package/dist/types/protocol/stream.d.ts +30 -0
  445. package/dist/types/protocol/usync.d.ts +11 -0
  446. package/dist/types/retry/codec.d.ts +3 -0
  447. package/dist/types/retry/index.d.ts +4 -3
  448. package/dist/types/retry/parse.d.ts +5 -2
  449. package/dist/types/retry/replay.d.ts +0 -4
  450. package/dist/types/retry/tracker.d.ts +20 -0
  451. package/dist/types/retry/types.d.ts +10 -4
  452. package/dist/types/signal/api/SignalDeviceSyncApi.d.ts +15 -2
  453. package/dist/types/signal/api/SignalDigestSyncApi.d.ts +6 -0
  454. package/dist/types/signal/api/SignalIdentitySyncApi.d.ts +2 -0
  455. package/dist/types/signal/api/SignalRotateKeyApi.d.ts +4 -5
  456. package/dist/types/signal/api/SignalSessionSyncApi.d.ts +8 -6
  457. package/dist/types/signal/api/result-map.d.ts +1 -0
  458. package/dist/types/signal/constants.d.ts +0 -3
  459. package/dist/types/signal/{store/sqlite.d.ts → encoding.d.ts} +3 -3
  460. package/dist/types/signal/group/SenderKeyCodec.d.ts +4 -6
  461. package/dist/types/signal/group/SenderKeyManager.d.ts +10 -5
  462. package/dist/types/signal/index.d.ts +3 -0
  463. package/dist/types/signal/session/SignalProtocol.d.ts +19 -4
  464. package/dist/types/signal/session/resolver.d.ts +22 -0
  465. package/dist/types/store/contracts/appstate.store.d.ts +4 -1
  466. package/dist/types/store/contracts/contact.store.d.ts +1 -0
  467. package/dist/types/store/contracts/device-list.store.d.ts +0 -3
  468. package/dist/types/store/contracts/message.store.d.ts +1 -0
  469. package/dist/types/store/contracts/participants.store.d.ts +0 -1
  470. package/dist/types/store/contracts/privacy-token.store.d.ts +16 -0
  471. package/dist/types/store/contracts/retry.store.d.ts +7 -0
  472. package/dist/types/store/contracts/sender-key.store.d.ts +0 -1
  473. package/dist/types/store/contracts/signal.store.d.ts +13 -0
  474. package/dist/types/store/contracts/thread.store.d.ts +1 -0
  475. package/dist/types/store/createStore.d.ts +1 -1
  476. package/dist/types/store/index.d.ts +5 -13
  477. package/dist/types/store/locks/appstate.lock.d.ts +3 -0
  478. package/dist/types/store/locks/auth.lock.d.ts +3 -0
  479. package/dist/types/store/locks/contact.lock.d.ts +3 -0
  480. package/dist/types/store/locks/device-list.lock.d.ts +2 -0
  481. package/dist/types/store/locks/message.lock.d.ts +3 -0
  482. package/dist/types/store/locks/participants.lock.d.ts +2 -0
  483. package/dist/types/store/locks/privacy-token.lock.d.ts +2 -0
  484. package/dist/types/store/locks/retry.lock.d.ts +2 -0
  485. package/dist/types/store/locks/sender-key.lock.d.ts +3 -0
  486. package/dist/types/store/locks/signal.lock.d.ts +3 -0
  487. package/dist/types/store/locks/thread.lock.d.ts +3 -0
  488. package/dist/types/store/providers/memory/appstate.store.d.ts +3 -1
  489. package/dist/types/store/providers/memory/contact.store.d.ts +1 -0
  490. package/dist/types/store/providers/memory/device-list.store.d.ts +0 -3
  491. package/dist/types/store/providers/memory/message.store.d.ts +1 -0
  492. package/dist/types/store/providers/memory/participants.store.d.ts +0 -1
  493. package/dist/types/store/providers/memory/privacy-token.store.d.ts +13 -0
  494. package/dist/types/store/providers/memory/retry.store.d.ts +8 -0
  495. package/dist/types/store/providers/memory/sender-key.store.d.ts +0 -1
  496. package/dist/types/store/providers/memory/signal.store.d.ts +8 -1
  497. package/dist/types/store/providers/memory/thread.store.d.ts +1 -0
  498. package/dist/types/store/types.d.ts +49 -58
  499. package/dist/types/transport/WaWebSocket.d.ts +3 -1
  500. package/dist/types/transport/binary/constants.d.ts +0 -30
  501. package/dist/types/transport/binary/index.d.ts +0 -1
  502. package/dist/types/transport/index.d.ts +2 -1
  503. package/dist/types/transport/keepalive/WaKeepAlive.d.ts +0 -1
  504. package/dist/types/transport/node/WaNodeOrchestrator.d.ts +3 -4
  505. package/dist/types/transport/node/WaNodeTransport.d.ts +0 -9
  506. package/dist/types/transport/node/builders/business.d.ts +29 -0
  507. package/dist/types/transport/node/builders/global.d.ts +102 -0
  508. package/dist/types/transport/node/builders/group.d.ts +4 -6
  509. package/dist/types/transport/node/builders/index.d.ts +7 -3
  510. package/dist/types/transport/node/builders/message.d.ts +20 -30
  511. package/dist/types/transport/node/builders/pairing.d.ts +0 -2
  512. package/dist/types/transport/node/builders/privacy-token.d.ts +9 -0
  513. package/dist/types/transport/node/builders/privacy.d.ts +7 -0
  514. package/dist/types/transport/node/builders/profile.d.ts +8 -0
  515. package/dist/types/transport/node/builders/retry.d.ts +2 -5
  516. package/dist/types/transport/node/builders/usync.d.ts +21 -0
  517. package/dist/types/transport/node/helpers.d.ts +13 -0
  518. package/dist/types/transport/node/usync.d.ts +2 -0
  519. package/dist/types/transport/noise/WaFrameCodec.d.ts +3 -0
  520. package/dist/types/transport/noise/WaNoiseSession.d.ts +4 -2
  521. package/dist/types/transport/noise/WaNoiseSocket.d.ts +4 -2
  522. package/dist/types/transport/proxy.d.ts +6 -0
  523. package/dist/types/transport/stream/parse.d.ts +0 -1
  524. package/dist/types/transport/types.d.ts +18 -1
  525. package/dist/types/util/bytes.d.ts +5 -0
  526. package/dist/types/util/index.d.ts +5 -0
  527. package/dist/types/util/primitives.d.ts +2 -0
  528. package/dist/util/bytes.js +72 -46
  529. package/dist/util/coercion.js +6 -14
  530. package/dist/util/index.js +23 -0
  531. package/dist/util/primitives.js +42 -14
  532. package/package.json +52 -9
  533. package/proto/index.js +1 -1
  534. package/dist/crypto/core/constants.js +0 -4
  535. package/dist/crypto/core/encoding.js +0 -29
  536. package/dist/esm/crypto/core/constants.js +0 -1
  537. package/dist/esm/crypto/core/encoding.js +0 -25
  538. package/dist/esm/retry/outbound.js +0 -83
  539. package/dist/esm/store/providers/sqlite/BaseSqliteStore.js +0 -37
  540. package/dist/esm/store/providers/sqlite/appstate.store.js +0 -169
  541. package/dist/esm/store/providers/sqlite/auth.store.js +0 -176
  542. package/dist/esm/store/providers/sqlite/connection.js +0 -240
  543. package/dist/esm/store/providers/sqlite/contact.store.js +0 -61
  544. package/dist/esm/store/providers/sqlite/device-list.store.js +0 -155
  545. package/dist/esm/store/providers/sqlite/message.store.js +0 -119
  546. package/dist/esm/store/providers/sqlite/migrations.js +0 -347
  547. package/dist/esm/store/providers/sqlite/participants.store.js +0 -85
  548. package/dist/esm/store/providers/sqlite/retry.store.js +0 -144
  549. package/dist/esm/store/providers/sqlite/sender-key.store.js +0 -203
  550. package/dist/esm/store/providers/sqlite/signal.store.js +0 -353
  551. package/dist/esm/store/providers/sqlite/thread.store.js +0 -72
  552. package/dist/esm/util/base64.js +0 -18
  553. package/dist/esm/util/signal-address.js +0 -5
  554. package/dist/retry/outbound.js +0 -88
  555. package/dist/store/providers/sqlite/BaseSqliteStore.js +0 -41
  556. package/dist/store/providers/sqlite/appstate.store.js +0 -173
  557. package/dist/store/providers/sqlite/auth.store.js +0 -180
  558. package/dist/store/providers/sqlite/connection.js +0 -276
  559. package/dist/store/providers/sqlite/contact.store.js +0 -65
  560. package/dist/store/providers/sqlite/device-list.store.js +0 -159
  561. package/dist/store/providers/sqlite/message.store.js +0 -123
  562. package/dist/store/providers/sqlite/migrations.js +0 -350
  563. package/dist/store/providers/sqlite/participants.store.js +0 -89
  564. package/dist/store/providers/sqlite/retry.store.js +0 -148
  565. package/dist/store/providers/sqlite/sender-key.store.js +0 -207
  566. package/dist/store/providers/sqlite/signal.store.js +0 -357
  567. package/dist/store/providers/sqlite/thread.store.js +0 -76
  568. package/dist/types/appstate/store/sqlite.d.ts +0 -21
  569. package/dist/types/crypto/core/constants.d.ts +0 -1
  570. package/dist/types/crypto/core/encoding.d.ts +0 -11
  571. package/dist/types/retry/outbound.d.ts +0 -4
  572. package/dist/types/store/providers/sqlite/BaseSqliteStore.d.ts +0 -12
  573. package/dist/types/store/providers/sqlite/appstate.store.d.ts +0 -15
  574. package/dist/types/store/providers/sqlite/auth.store.d.ts +0 -10
  575. package/dist/types/store/providers/sqlite/connection.d.ts +0 -10
  576. package/dist/types/store/providers/sqlite/contact.store.d.ts +0 -10
  577. package/dist/types/store/providers/sqlite/device-list.store.d.ts +0 -18
  578. package/dist/types/store/providers/sqlite/message.store.d.ts +0 -11
  579. package/dist/types/store/providers/sqlite/migrations.d.ts +0 -3
  580. package/dist/types/store/providers/sqlite/participants.store.d.ts +0 -13
  581. package/dist/types/store/providers/sqlite/retry.store.d.ts +0 -16
  582. package/dist/types/store/providers/sqlite/sender-key.store.d.ts +0 -25
  583. package/dist/types/store/providers/sqlite/signal.store.d.ts +0 -46
  584. package/dist/types/store/providers/sqlite/thread.store.d.ts +0 -11
  585. package/dist/types/util/base64.d.ts +0 -4
  586. package/dist/types/util/signal-address.d.ts +0 -2
  587. package/dist/util/base64.js +0 -24
  588. package/dist/util/signal-address.js +0 -8
  589. /package/dist/types/transport/node/builders/{accountSync.d.ts → account-sync.d.ts} +0 -0
@@ -1,39 +1,45 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WaMessageDispatchCoordinator = void 0;
4
- const keys_1 = require("../../crypto/core/keys");
4
+ const _crypto_1 = require("../../crypto/index.js");
5
+ const PromiseDedup_1 = require("../../infra/perf/PromiseDedup");
6
+ const _message_1 = require("../../message/index.js");
5
7
  const content_1 = require("../../message/content");
6
8
  const device_sent_1 = require("../../message/device-sent");
9
+ const icdc_1 = require("../../message/icdc");
7
10
  const padding_1 = require("../../message/padding");
8
11
  const phash_1 = require("../../message/phash");
12
+ const reporting_token_1 = require("../../message/reporting-token");
9
13
  const _proto_1 = require("../../proto.js");
10
14
  const constants_1 = require("../../protocol/constants");
11
15
  const jid_1 = require("../../protocol/jid");
12
- const constants_2 = require("../../retry/constants");
13
- const outbound_1 = require("../../retry/outbound");
16
+ const jid_2 = require("../../protocol/jid");
14
17
  const binary_1 = require("../../transport/binary");
15
18
  const message_1 = require("../../transport/node/builders/message");
16
19
  const bytes_1 = require("../../util/bytes");
17
20
  const primitives_1 = require("../../util/primitives");
18
- const signal_address_1 = require("../../util/signal-address");
19
21
  class WaMessageDispatchCoordinator {
20
22
  constructor(options) {
23
+ this.icdcDedup = new PromiseDedup_1.PromiseDedup();
24
+ this.privacyTokenDedup = new PromiseDedup_1.PromiseDedup();
25
+ this.distributionDedup = new PromiseDedup_1.PromiseDedup();
21
26
  this.logger = options.logger;
22
27
  this.messageClient = options.messageClient;
23
- this.retryStore = options.retryStore;
24
- this.participantsStore = options.participantsStore;
25
- this.retryTtlMs = this.retryStore.getTtlMs?.() ?? constants_2.RETRY_OUTBOUND_TTL_MS;
28
+ this.retryTracker = options.retryTracker;
29
+ this.sessionResolver = options.sessionResolver;
30
+ this.fanoutResolver = options.fanoutResolver;
31
+ this.participantsCache = options.participantsCache;
32
+ this.appStateSyncKeyProtocol = options.appStateSyncKeyProtocol;
26
33
  this.buildMessageContent = options.buildMessageContent;
27
- this.queryGroupParticipantJids = options.queryGroupParticipantJids;
28
34
  this.senderKeyManager = options.senderKeyManager;
29
35
  this.signalProtocol = options.signalProtocol;
30
36
  this.signalStore = options.signalStore;
31
- this.signalDeviceSync = options.signalDeviceSync;
32
- this.signalIdentitySync = options.signalIdentitySync;
33
- this.signalSessionSync = options.signalSessionSync;
37
+ this.deviceListStore = options.deviceListStore;
34
38
  this.getCurrentMeJid = options.getCurrentMeJid;
35
39
  this.getCurrentMeLid = options.getCurrentMeLid;
36
40
  this.getCurrentSignedIdentity = options.getCurrentSignedIdentity;
41
+ this.resolvePrivacyTokenNode = options.resolvePrivacyTokenNode;
42
+ this.onDirectMessageSent = options.onDirectMessageSent;
37
43
  }
38
44
  async publishMessageNode(node, options = {}) {
39
45
  this.logger.debug('wa client publish message node', {
@@ -46,13 +52,13 @@ class WaMessageDispatchCoordinator {
46
52
  mode: 'opaque_node',
47
53
  node: (0, binary_1.encodeBinaryNode)(node)
48
54
  };
49
- return this.publishWithRetryTracking({
55
+ return this.retryTracker.track({
50
56
  messageIdHint: node.attrs.id,
51
57
  toJid: node.attrs.to,
58
+ type: messageType,
59
+ replayPayload,
52
60
  participantJid: node.attrs.participant,
53
- recipientJid: node.attrs.recipient,
54
- messageType,
55
- replayPayload
61
+ recipientJid: node.attrs.recipient
56
62
  }, async () => this.messageClient.publishNode(node, options));
57
63
  }
58
64
  async publishEncryptedMessage(input, options = {}) {
@@ -69,12 +75,13 @@ class WaMessageDispatchCoordinator {
69
75
  ciphertext: input.ciphertext,
70
76
  participant: input.participant
71
77
  };
72
- return this.publishWithRetryTracking({
78
+ return this.retryTracker.track({
73
79
  messageIdHint: input.id,
74
80
  toJid: input.to,
81
+ type: input.type ?? 'text',
82
+ replayPayload,
75
83
  participantJid: input.participant,
76
- messageType: input.type ?? 'text',
77
- replayPayload
84
+ eligibleRequesterDeviceJids: [input.to]
78
85
  }, async () => this.messageClient.publishEncrypted(input, options));
79
86
  }
80
87
  async publishSignalMessage(input, options = {}) {
@@ -89,7 +96,7 @@ class WaMessageDispatchCoordinator {
89
96
  });
90
97
  const [paddedPlaintext] = await Promise.all([
91
98
  (0, padding_1.writeRandomPadMax16)(input.plaintext),
92
- this.ensureSignalSession(address, input.to, input.expectedIdentity)
99
+ this.sessionResolver.ensureSession(address, input.to, input.expectedIdentity)
93
100
  ]);
94
101
  const encrypted = await this.signalProtocol.encryptMessage(address, paddedPlaintext, input.expectedIdentity);
95
102
  const messageType = input.type ?? 'text';
@@ -99,12 +106,13 @@ class WaMessageDispatchCoordinator {
99
106
  type: messageType,
100
107
  plaintext: paddedPlaintext
101
108
  };
102
- return this.publishWithRetryTracking({
109
+ return this.retryTracker.track({
103
110
  messageIdHint: input.id,
104
111
  toJid: input.to,
112
+ type: messageType,
113
+ replayPayload,
105
114
  participantJid: input.participant,
106
- messageType,
107
- replayPayload
115
+ eligibleRequesterDeviceJids: [input.to]
108
116
  }, async () => this.messageClient.publishEncrypted({
109
117
  to: input.to,
110
118
  encType: encrypted.type,
@@ -119,286 +127,56 @@ class WaMessageDispatchCoordinator {
119
127
  }
120
128
  async sendMessage(to, content, options = {}) {
121
129
  const recipientJid = (0, jid_1.normalizeRecipientJid)(to);
122
- const message = await this.buildMessageContent(content);
123
- const plaintext = await (0, padding_1.writeRandomPadMax16)(_proto_1.proto.Message.encode(message).finish());
124
- const type = (0, content_1.resolveMessageTypeAttr)(message);
125
- if ((0, jid_1.isGroupJid)(recipientJid)) {
126
- if (this.shouldUseGroupDirectPath(message)) {
127
- return this.publishGroupDirectMessage(recipientJid, plaintext, type, options);
128
- }
129
- return this.publishGroupSenderKeyMessage(recipientJid, plaintext, type, options);
130
+ const [message, sendOptions] = await Promise.all([
131
+ this.buildMessageContent(content),
132
+ this.withResolvedMessageId(options)
133
+ ]);
134
+ const messageWithSecret = await (0, _message_1.ensureMessageSecret)(message);
135
+ const meJid = this.getCurrentMeJid();
136
+ const regInfo = meJid ? await this.signalStore.getRegistrationInfo() : null;
137
+ const localPubKey = regInfo?.identityKeyPair.pubKey;
138
+ const meParsed = meJid ? (0, jid_1.parseJidFull)(meJid) : undefined;
139
+ const meUserJid = meParsed?.userJid;
140
+ const localIdentity = meParsed && localPubKey ? { address: meParsed.address, pubKey: localPubKey } : undefined;
141
+ const isGroup = (0, jid_1.isGroupJid)(recipientJid);
142
+ const [senderIcdc, recipientIcdc] = await Promise.all([
143
+ meUserJid ? this.resolveUserIcdc(meUserJid, localIdentity) : null,
144
+ !isGroup ? this.resolveUserIcdc((0, jid_1.toUserJid)(recipientJid)) : null
145
+ ]);
146
+ const messageWithIcdc = (0, icdc_1.injectDeviceListMetadata)(messageWithSecret, senderIcdc, recipientIcdc);
147
+ const plaintext = await (0, padding_1.writeRandomPadMax16)(_proto_1.proto.Message.encode(messageWithIcdc).finish());
148
+ const type = (0, content_1.resolveMessageTypeAttr)(messageWithIcdc);
149
+ const edit = (0, content_1.resolveEditAttr)(messageWithIcdc, sendOptions.subtype) ?? undefined;
150
+ const mediatype = (0, content_1.resolveEncMediaType)(messageWithIcdc) ?? undefined;
151
+ const metaAttrs = (0, content_1.resolveMetaAttrs)(messageWithIcdc);
152
+ const metaNode = metaAttrs ? (0, message_1.buildMetaNode)(metaAttrs) : undefined;
153
+ if (isGroup) {
154
+ if (this.shouldUseGroupDirectPath(messageWithIcdc)) {
155
+ return this.publishGroupDirectMessage(recipientJid, messageWithIcdc, plaintext, type, sendOptions, {}, edit, mediatype, metaNode);
156
+ }
157
+ return this.publishGroupSenderKeyMessage(recipientJid, messageWithIcdc, plaintext, type, sendOptions, {}, edit, mediatype, metaNode);
130
158
  }
131
159
  const directRecipientJid = (0, jid_1.toUserJid)(recipientJid);
132
- return this.publishDirectSignalMessageWithFanout(directRecipientJid, message, plaintext, type, options);
160
+ return this.publishDirectSignalMessageWithFanout(directRecipientJid, messageWithIcdc, plaintext, type, sendOptions, edit, mediatype, metaNode);
133
161
  }
134
162
  async syncSignalSession(jid, reasonIdentity = false) {
135
163
  const address = (0, jid_1.parseSignalAddressFromJid)(jid);
136
164
  if (address.server === constants_1.WA_DEFAULTS.GROUP_SERVER) {
137
165
  throw new Error('syncSignalSession supports only direct chats');
138
166
  }
139
- await this.ensureSignalSession(address, jid, undefined, reasonIdentity);
167
+ await this.sessionResolver.ensureSession(address, jid, undefined, reasonIdentity);
140
168
  }
141
169
  async sendReceipt(input) {
142
170
  await this.messageClient.sendReceipt(input);
143
171
  }
144
172
  async requestAppStateSyncKeys(keyIds) {
145
- const normalizedKeyIds = this.normalizeKeyIds(keyIds);
146
- if (normalizedKeyIds.length === 0) {
147
- return [];
148
- }
149
- const peerDeviceJids = await this.resolveOwnPeerDeviceJids();
150
- if (peerDeviceJids.length === 0) {
151
- this.logger.warn('app-state sync key request skipped: no peer devices available', {
152
- keys: normalizedKeyIds.length
153
- });
154
- return [];
155
- }
156
- const protocolMessage = {
157
- type: _proto_1.proto.Message.ProtocolMessage.Type.APP_STATE_SYNC_KEY_REQUEST,
158
- appStateSyncKeyRequest: {
159
- keyIds: normalizedKeyIds.map((keyId) => ({
160
- keyId
161
- }))
162
- }
163
- };
164
- await Promise.all(peerDeviceJids.map((deviceJid) => this.publishProtocolMessageToDevice(deviceJid, protocolMessage)));
165
- this.logger.info('app-state sync key request sent to peer devices', {
166
- devices: peerDeviceJids.length,
167
- keys: normalizedKeyIds.length,
168
- keyIds: normalizedKeyIds.map((keyId) => (0, bytes_1.bytesToHex)(keyId)).join(',')
169
- });
170
- return peerDeviceJids;
173
+ return this.appStateSyncKeyProtocol.requestKeys(keyIds);
171
174
  }
172
175
  async sendAppStateSyncKeyShare(toDeviceJid, keys, missingKeyIds = []) {
173
- const normalizedTo = (0, jid_1.normalizeDeviceJid)(toDeviceJid);
174
- const dedupedKeysById = new Map();
175
- for (const key of keys) {
176
- dedupedKeysById.set((0, bytes_1.bytesToHex)(key.keyId), key);
177
- }
178
- const dedupedKeys = [...dedupedKeysById.values()];
179
- const dedupedMissingKeyIds = this.normalizeKeyIds(missingKeyIds).filter((keyId) => !dedupedKeysById.has((0, bytes_1.bytesToHex)(keyId)));
180
- const keyShareEntries = [
181
- ...dedupedKeys.map((key) => ({
182
- keyId: { keyId: key.keyId },
183
- keyData: {
184
- keyData: key.keyData,
185
- timestamp: key.timestamp,
186
- ...(key.fingerprint ? { fingerprint: key.fingerprint } : {})
187
- }
188
- })),
189
- ...dedupedMissingKeyIds.map((keyId) => ({
190
- keyId: { keyId }
191
- }))
192
- ];
193
- const protocolMessage = {
194
- type: _proto_1.proto.Message.ProtocolMessage.Type.APP_STATE_SYNC_KEY_SHARE,
195
- appStateSyncKeyShare: {
196
- keys: keyShareEntries
197
- }
198
- };
199
- await this.publishProtocolMessageToDevice(normalizedTo, protocolMessage);
200
- this.logger.info('app-state sync key share sent', {
201
- to: normalizedTo,
202
- keys: dedupedKeys.length,
203
- orphanKeys: dedupedMissingKeyIds.length
204
- });
176
+ await this.appStateSyncKeyProtocol.sendKeyShare(toDeviceJid, keys, missingKeyIds);
205
177
  }
206
178
  async mutateParticipantsCacheFromGroupEvent(event) {
207
- const groupJid = this.resolveGroupJidForParticipantCacheEvent(event);
208
- if (!groupJid) {
209
- return;
210
- }
211
- if (event.action === 'delete') {
212
- await this.participantsStore.deleteGroupParticipants(groupJid);
213
- return;
214
- }
215
- const participantUsers = this.extractParticipantUsersFromGroupEvent(event);
216
- if (event.action === 'create') {
217
- if (participantUsers.length === 0) {
218
- return;
219
- }
220
- await this.participantsStore.upsertGroupParticipants({
221
- groupJid,
222
- participants: participantUsers,
223
- updatedAtMs: Date.now()
224
- });
225
- return;
226
- }
227
- const cached = await this.participantsStore.getGroupParticipants(groupJid);
228
- if (!cached || cached.participants.length === 0) {
229
- return;
230
- }
231
- const cachedParticipants = this.sanitizeParticipantUsers(cached.participants);
232
- if (cachedParticipants.length === 0) {
233
- return;
234
- }
235
- if (event.action === 'add' ||
236
- event.action === 'promote' ||
237
- event.action === 'demote' ||
238
- event.action === 'linked_group_promote' ||
239
- event.action === 'linked_group_demote') {
240
- await this.mergeParticipantUsersIntoCache(groupJid, cachedParticipants, participantUsers);
241
- return;
242
- }
243
- if (event.action === 'remove') {
244
- await this.removeParticipantUsersFromCache(groupJid, cachedParticipants, participantUsers);
245
- return;
246
- }
247
- if (event.action === 'modify') {
248
- const authorUsers = event.authorJid
249
- ? this.sanitizeParticipantUsers([event.authorJid])
250
- : [];
251
- await this.replaceParticipantUsersInCache(groupJid, cachedParticipants, authorUsers, participantUsers);
252
- }
253
- }
254
- async publishProtocolMessageToDevice(deviceJid, protocolMessage) {
255
- const plaintext = await (0, padding_1.writeRandomPadMax16)(_proto_1.proto.Message.encode({
256
- protocolMessage
257
- }).finish());
258
- await this.publishSignalMessage({
259
- to: deviceJid,
260
- plaintext,
261
- type: 'protocol',
262
- category: 'peer',
263
- pushPriority: 'high'
264
- });
265
- }
266
- async resolveOwnPeerDeviceJids() {
267
- const meJid = this.requireCurrentMeJid('resolveOwnPeerDeviceJids');
268
- const meUserJid = (0, jid_1.toUserJid)(meJid);
269
- const meDevices = new Set();
270
- meDevices.add((0, jid_1.normalizeDeviceJid)(meJid));
271
- const meLid = this.getCurrentMeLid();
272
- if (meLid && meLid.includes('@')) {
273
- try {
274
- meDevices.add((0, jid_1.normalizeDeviceJid)(meLid));
275
- }
276
- catch (error) {
277
- this.logger.trace('ignoring malformed me lid jid while resolving peer devices', {
278
- meLid,
279
- message: (0, primitives_1.toError)(error).message
280
- });
281
- }
282
- }
283
- try {
284
- const synced = await this.signalDeviceSync.syncDeviceList([meUserJid]);
285
- const peerDevices = new Set();
286
- for (const entry of synced) {
287
- const sourceDevices = entry.deviceJids.length > 0 ? entry.deviceJids : [entry.jid];
288
- for (const deviceJid of sourceDevices) {
289
- try {
290
- const normalized = (0, jid_1.normalizeDeviceJid)(deviceJid);
291
- if (meDevices.has(normalized)) {
292
- continue;
293
- }
294
- peerDevices.add(normalized);
295
- }
296
- catch (error) {
297
- this.logger.trace('ignoring malformed peer device jid while resolving app-state peers', {
298
- deviceJid,
299
- message: (0, primitives_1.toError)(error).message
300
- });
301
- }
302
- }
303
- }
304
- return [...peerDevices];
305
- }
306
- catch (error) {
307
- this.logger.warn('failed to resolve peer devices for app-state key request', {
308
- message: (0, primitives_1.toError)(error).message
309
- });
310
- return [];
311
- }
312
- }
313
- normalizeKeyIds(keyIds) {
314
- const deduped = new Map();
315
- for (const keyId of keyIds) {
316
- if (keyId.byteLength === 0) {
317
- continue;
318
- }
319
- const keyHex = (0, bytes_1.bytesToHex)(keyId);
320
- if (deduped.has(keyHex)) {
321
- continue;
322
- }
323
- deduped.set(keyHex, keyId);
324
- }
325
- return [...deduped.values()];
326
- }
327
- async publishWithRetryTracking(args, publish) {
328
- const nowMs = Date.now();
329
- const expiresAtMs = nowMs + this.retryTtlMs;
330
- const hintedMessageId = args.messageIdHint?.trim();
331
- const resolvedToJid = args.toJid ?? (args.replayPayload.mode === 'opaque_node' ? '' : args.replayPayload.to);
332
- let hintedPersisted = false;
333
- if (hintedMessageId) {
334
- hintedPersisted = await this.safeUpsertRetryOutboundRecord(this.createRetryOutboundRecord({
335
- messageId: hintedMessageId,
336
- toJid: resolvedToJid,
337
- participantJid: args.participantJid,
338
- recipientJid: args.recipientJid,
339
- messageType: args.messageType,
340
- replayPayload: args.replayPayload,
341
- createdAtMs: nowMs,
342
- updatedAtMs: nowMs,
343
- expiresAtMs
344
- }));
345
- }
346
- const result = await publish();
347
- if (hintedPersisted && hintedMessageId && result.id === hintedMessageId) {
348
- // Hint and final message id matched; avoid a second equivalent upsert on the hot path.
349
- return result;
350
- }
351
- const persistedNowMs = Date.now();
352
- await this.safeUpsertRetryOutboundRecord(this.createRetryOutboundRecord({
353
- messageId: result.id,
354
- toJid: resolvedToJid,
355
- participantJid: args.participantJid,
356
- recipientJid: args.recipientJid,
357
- messageType: args.messageType,
358
- replayPayload: args.replayPayload,
359
- createdAtMs: hintedMessageId ? nowMs : persistedNowMs,
360
- updatedAtMs: persistedNowMs,
361
- expiresAtMs: persistedNowMs + this.retryTtlMs
362
- }));
363
- return result;
364
- }
365
- createRetryOutboundRecord(input) {
366
- return {
367
- messageId: input.messageId,
368
- toJid: input.toJid,
369
- participantJid: input.participantJid,
370
- recipientJid: input.recipientJid,
371
- messageType: input.messageType,
372
- replayMode: input.replayPayload.mode,
373
- replayPayload: (0, outbound_1.encodeRetryReplayPayload)(input.replayPayload),
374
- state: 'pending',
375
- createdAtMs: input.createdAtMs,
376
- updatedAtMs: input.updatedAtMs,
377
- expiresAtMs: input.expiresAtMs
378
- };
379
- }
380
- async safeUpsertRetryOutboundRecord(record) {
381
- try {
382
- await this.retryStore.upsertOutboundMessage(record);
383
- }
384
- catch (error) {
385
- this.logger.warn('failed to persist retry outbound message record', {
386
- messageId: record.messageId,
387
- to: record.toJid,
388
- mode: record.replayMode,
389
- message: (0, primitives_1.toError)(error).message
390
- });
391
- return false;
392
- }
393
- try {
394
- await this.retryStore.cleanupExpired(Date.now());
395
- }
396
- catch (error) {
397
- this.logger.warn('failed to cleanup retry records after outbound persist', {
398
- message: (0, primitives_1.toError)(error).message
399
- });
400
- }
401
- return true;
179
+ await this.participantsCache.mutateFromGroupEvent(event);
402
180
  }
403
181
  shouldUseGroupDirectPath(message) {
404
182
  const protocolType = message.protocolMessage?.type;
@@ -408,41 +186,81 @@ class WaMessageDispatchCoordinator {
408
186
  }
409
187
  return message.keepInChatMessage?.keepType === _proto_1.proto.KeepType.UNDO_KEEP_FOR_ALL;
410
188
  }
411
- async publishGroupDirectMessage(groupJid, plaintext, type, options, retryContext = {}) {
189
+ async publishGroupDirectMessage(groupJid, message, plaintext, type, options, retryContext = {}, edit, mediatype, metaNode) {
190
+ const sendOptions = await this.withResolvedMessageId(options);
412
191
  const meJid = this.requireCurrentMeJid('sendMessage');
413
192
  const participantUserJids = retryContext.forceRefreshParticipants
414
- ? await this.refreshGroupParticipantUsers(groupJid)
415
- : await this.resolveGroupParticipantUsers(groupJid);
193
+ ? await this.participantsCache.refreshParticipantUsers(groupJid)
194
+ : await this.participantsCache.resolveParticipantUsers(groupJid);
416
195
  const addressingMode = retryContext.forceAddressingMode ??
417
196
  this.resolveGroupAddressingMode(participantUserJids, groupJid);
418
197
  const senderForPhash = this.resolveSenderForAddressingMode(addressingMode, meJid);
419
- const fanoutDeviceJids = await this.resolveGroupParticipantDeviceJids(participantUserJids);
198
+ const fanoutDeviceJids = await this.fanoutResolver.resolveGroupParticipantDeviceJids(participantUserJids);
420
199
  if (fanoutDeviceJids.length === 0) {
421
200
  throw new Error('group direct send resolved no target devices');
422
201
  }
423
- await this.ensureSignalSessionsBatch(fanoutDeviceJids);
424
- const participants = await Promise.all(fanoutDeviceJids.map(async (targetJid) => {
425
- const address = (0, jid_1.parseSignalAddressFromJid)(targetJid);
426
- await this.ensureSignalSession(address, targetJid);
427
- const encrypted = await this.signalProtocol.encryptMessage(address, plaintext);
428
- return {
429
- jid: targetJid,
430
- encType: encrypted.type,
431
- ciphertext: encrypted.ciphertext
202
+ const resolvedFanoutTargets = await this.sessionResolver.ensureSessionsBatch(fanoutDeviceJids);
203
+ const uniqueNormalizedFanoutJids = new Set();
204
+ for (let index = 0; index < fanoutDeviceJids.length; index += 1) {
205
+ uniqueNormalizedFanoutJids.add((0, jid_1.normalizeDeviceJid)(fanoutDeviceJids[index]));
206
+ }
207
+ if (resolvedFanoutTargets.length !== uniqueNormalizedFanoutJids.size) {
208
+ throw new Error('group direct send resolved incomplete signal sessions');
209
+ }
210
+ const participantEncryptRequests = new Array(resolvedFanoutTargets.length);
211
+ for (let index = 0; index < resolvedFanoutTargets.length; index += 1) {
212
+ const target = resolvedFanoutTargets[index];
213
+ participantEncryptRequests[index] = {
214
+ address: target.address,
215
+ plaintext
216
+ };
217
+ }
218
+ const encryptedParticipants = await this.signalProtocol.encryptMessagesBatch(participantEncryptRequests, resolvedFanoutTargets);
219
+ const participants = new Array(resolvedFanoutTargets.length);
220
+ for (let index = 0; index < resolvedFanoutTargets.length; index += 1) {
221
+ const target = resolvedFanoutTargets[index];
222
+ participants[index] = {
223
+ jid: target.jid,
224
+ encType: encryptedParticipants[index].type,
225
+ ciphertext: encryptedParticipants[index].ciphertext
432
226
  };
433
- }));
434
- const shouldAttachDeviceIdentity = participants.some((participant) => participant.encType === 'pkmsg');
435
- const localPhash = await (0, phash_1.computePhashV2)([...fanoutDeviceJids, senderForPhash]);
436
- const messageNode = (0, message_1.buildGroupDirectMessageNode)({
227
+ }
228
+ let shouldAttachDeviceIdentity = false;
229
+ for (let index = 0; index < participants.length; index += 1) {
230
+ if (participants[index].encType === 'pkmsg') {
231
+ shouldAttachDeviceIdentity = true;
232
+ break;
233
+ }
234
+ }
235
+ const phashTargets = new Array(resolvedFanoutTargets.length + 1);
236
+ for (let index = 0; index < resolvedFanoutTargets.length; index += 1) {
237
+ phashTargets[index] = resolvedFanoutTargets[index].jid;
238
+ }
239
+ phashTargets[resolvedFanoutTargets.length] = senderForPhash;
240
+ const [localPhash, reportingArtifacts] = await Promise.all([
241
+ (0, phash_1.computePhashV2)(phashTargets),
242
+ this.tryBuildReportingTokenArtifacts({
243
+ message,
244
+ stanzaId: sendOptions.id,
245
+ senderUserJid: (0, jid_1.toUserJid)(senderForPhash),
246
+ remoteJid: groupJid,
247
+ context: 'group_direct'
248
+ })
249
+ ]);
250
+ const messageNode = (0, message_1.buildDirectMessageFanoutNode)({
437
251
  to: groupJid,
438
252
  type,
439
- id: options.id,
253
+ id: sendOptions.id,
254
+ edit,
440
255
  phash: localPhash,
441
256
  addressingMode,
442
257
  participants,
443
258
  deviceIdentity: shouldAttachDeviceIdentity
444
259
  ? this.getEncodedSignedDeviceIdentity()
445
- : undefined
260
+ : undefined,
261
+ reportingNode: reportingArtifacts?.node ?? undefined,
262
+ metaNode,
263
+ mediatype
446
264
  });
447
265
  const replayPayload = {
448
266
  mode: 'plaintext',
@@ -450,12 +268,13 @@ class WaMessageDispatchCoordinator {
450
268
  type,
451
269
  plaintext
452
270
  };
453
- const result = await this.publishWithRetryTracking({
454
- messageIdHint: options.id ?? messageNode.attrs.id,
271
+ const result = await this.retryTracker.track({
272
+ messageIdHint: sendOptions.id ?? messageNode.attrs.id,
455
273
  toJid: groupJid,
456
- messageType: type,
457
- replayPayload
458
- }, async () => this.messageClient.publishNode(messageNode, options));
274
+ type,
275
+ replayPayload,
276
+ eligibleRequesterDeviceJids: undefined
277
+ }, async () => this.messageClient.publishNode(messageNode, sendOptions));
459
278
  const ackError = result.ack.error;
460
279
  const serverPhash = result.ack.phash;
461
280
  const serverAddressingMode = result.ack.addressingMode;
@@ -473,43 +292,67 @@ class WaMessageDispatchCoordinator {
473
292
  serverAddressingMode,
474
293
  ackError
475
294
  });
476
- return this.publishGroupDirectMessage(groupJid, plaintext, type, {
477
- ...options,
295
+ return this.publishGroupDirectMessage(groupJid, message, plaintext, type, {
296
+ ...sendOptions,
478
297
  id: result.id
479
298
  }, {
480
299
  retried: true,
481
300
  forceRefreshParticipants: true,
482
301
  forceAddressingMode: serverAddressingMode
483
- });
302
+ }, edit, mediatype, metaNode);
484
303
  }
485
304
  return result;
486
305
  }
487
- async publishGroupSenderKeyMessage(groupJid, plaintext, type, options, retryContext = {}) {
306
+ async publishGroupSenderKeyMessage(groupJid, message, plaintext, type, options, retryContext = {}, edit, mediatype, metaNode) {
307
+ const sendOptions = await this.withResolvedMessageId(options);
488
308
  const meJid = this.requireCurrentMeJid('sendMessage');
489
309
  const participantUserJids = retryContext.forceRefreshParticipants
490
- ? await this.refreshGroupParticipantUsers(groupJid)
491
- : await this.resolveGroupParticipantUsers(groupJid);
310
+ ? await this.participantsCache.refreshParticipantUsers(groupJid)
311
+ : await this.participantsCache.resolveParticipantUsers(groupJid);
492
312
  const addressingMode = retryContext.forceAddressingMode ??
493
313
  this.resolveGroupAddressingMode(participantUserJids, groupJid);
494
314
  const senderJid = this.resolveSenderForAddressingMode(addressingMode, meJid);
495
315
  const sender = (0, jid_1.parseSignalAddressFromJid)(senderJid);
496
- const senderKeyDistributionMessage = await this.senderKeyManager.createSenderKeyDistributionMessage(groupJid, sender);
497
- const groupCiphertext = await this.senderKeyManager.encryptGroupMessage(groupJid, sender, plaintext);
498
- const distributionData = await this.encryptGroupDistributionParticipants(groupJid, sender, senderKeyDistributionMessage, participantUserJids);
316
+ const { distributionMessage: senderKeyDistributionMessage, ciphertext: groupCiphertext, keyId: senderKeyId } = await this.senderKeyManager.prepareGroupEncryption(groupJid, sender, plaintext);
317
+ const distributionData = await this.distributionDedup.run(`dist:${groupJid}:${senderKeyId}`, () => this.encryptGroupDistributionParticipants(groupJid, senderKeyId, senderKeyDistributionMessage, participantUserJids));
499
318
  const { fanoutDeviceJids, distributionParticipants } = distributionData;
500
- const shouldAttachDeviceIdentity = distributionParticipants.some((participant) => participant.encType === 'pkmsg');
501
- const localPhash = await (0, phash_1.computePhashV2)([...fanoutDeviceJids, senderJid]);
319
+ let shouldAttachDeviceIdentity = false;
320
+ for (let index = 0; index < distributionParticipants.length; index += 1) {
321
+ if (distributionParticipants[index].encType === 'pkmsg') {
322
+ shouldAttachDeviceIdentity = true;
323
+ break;
324
+ }
325
+ }
326
+ const phashTargets = new Array(fanoutDeviceJids.length + 1);
327
+ for (let index = 0; index < fanoutDeviceJids.length; index += 1) {
328
+ phashTargets[index] = fanoutDeviceJids[index];
329
+ }
330
+ phashTargets[fanoutDeviceJids.length] = senderJid;
331
+ const [localPhash, reportingArtifacts] = await Promise.all([
332
+ (0, phash_1.computePhashV2)(phashTargets),
333
+ this.tryBuildReportingTokenArtifacts({
334
+ message,
335
+ stanzaId: sendOptions.id,
336
+ senderUserJid: (0, jid_1.toUserJid)(senderJid),
337
+ remoteJid: groupJid,
338
+ context: 'group_sender_key'
339
+ })
340
+ ]);
502
341
  const messageNode = (0, message_1.buildGroupSenderKeyMessageNode)({
503
342
  to: groupJid,
504
343
  type,
505
- id: options.id,
344
+ id: sendOptions.id,
345
+ edit,
506
346
  phash: localPhash,
507
347
  addressingMode,
508
348
  groupCiphertext: groupCiphertext.ciphertext,
509
349
  participants: distributionParticipants,
510
350
  deviceIdentity: shouldAttachDeviceIdentity
511
351
  ? this.getEncodedSignedDeviceIdentity()
512
- : undefined
352
+ : undefined,
353
+ reportingNode: reportingArtifacts?.node ?? undefined,
354
+ metaNode,
355
+ mediatype
513
356
  });
514
357
  const replayPayload = {
515
358
  mode: 'plaintext',
@@ -517,15 +360,19 @@ class WaMessageDispatchCoordinator {
517
360
  type,
518
361
  plaintext
519
362
  };
520
- const result = await this.publishWithRetryTracking({
521
- messageIdHint: options.id ?? messageNode.attrs.id,
363
+ const result = await this.retryTracker.track({
364
+ messageIdHint: sendOptions.id ?? messageNode.attrs.id,
522
365
  toJid: groupJid,
523
- messageType: type,
524
- replayPayload
525
- }, async () => this.messageClient.publishNode(messageNode, options));
526
- const distributedAddresses = distributionParticipants.map((participant) => participant.address);
366
+ type,
367
+ replayPayload,
368
+ eligibleRequesterDeviceJids: undefined
369
+ }, async () => this.messageClient.publishNode(messageNode, sendOptions));
370
+ const distributedAddresses = new Array(distributionParticipants.length);
371
+ for (let index = 0; index < distributionParticipants.length; index += 1) {
372
+ distributedAddresses[index] = distributionParticipants[index].address;
373
+ }
527
374
  try {
528
- await this.senderKeyManager.markSenderKeyDistributed(groupJid, sender, distributedAddresses);
375
+ await this.senderKeyManager.markSenderKeyDistributed(groupJid, senderKeyId, distributedAddresses);
529
376
  }
530
377
  catch (error) {
531
378
  this.logger.warn('failed to mark sender key distribution targets', {
@@ -551,150 +398,20 @@ class WaMessageDispatchCoordinator {
551
398
  serverAddressingMode,
552
399
  ackError
553
400
  });
554
- return this.publishGroupSenderKeyMessage(groupJid, plaintext, type, {
555
- ...options,
401
+ return this.publishGroupSenderKeyMessage(groupJid, message, plaintext, type, {
402
+ ...sendOptions,
556
403
  id: result.id
557
404
  }, {
558
405
  retried: true,
559
406
  forceRefreshParticipants: true,
560
407
  forceAddressingMode: serverAddressingMode
561
- });
408
+ }, edit, mediatype, metaNode);
562
409
  }
563
410
  return result;
564
411
  }
565
- async resolveGroupParticipantUsers(groupJid) {
566
- const cached = await this.participantsStore.getGroupParticipants(groupJid);
567
- if (cached && cached.participants.length > 0) {
568
- return this.sanitizeParticipantUsers(cached.participants);
569
- }
570
- return this.refreshGroupParticipantUsers(groupJid);
571
- }
572
- resolveGroupJidForParticipantCacheEvent(event) {
573
- if (event.action === 'linked_group_promote' || event.action === 'linked_group_demote') {
574
- return event.contextGroupJid ?? event.groupJid ?? null;
575
- }
576
- return event.groupJid ?? null;
577
- }
578
- extractParticipantUsersFromGroupEvent(event) {
579
- const candidates = [];
580
- for (const participant of event.participants ?? []) {
581
- if (participant.jid) {
582
- candidates.push(participant.jid);
583
- }
584
- if (participant.lidJid) {
585
- candidates.push(participant.lidJid);
586
- }
587
- if (participant.phoneJid) {
588
- candidates.push(participant.phoneJid);
589
- }
590
- }
591
- return this.sanitizeParticipantUsers(candidates);
592
- }
593
- async mergeParticipantUsersIntoCache(groupJid, cachedParticipants, participantsToAdd) {
594
- if (participantsToAdd.length === 0) {
595
- return;
596
- }
597
- const nextParticipants = [...cachedParticipants];
598
- const existing = new Set(cachedParticipants);
599
- for (const participant of participantsToAdd) {
600
- if (existing.has(participant)) {
601
- continue;
602
- }
603
- existing.add(participant);
604
- nextParticipants.push(participant);
605
- }
606
- if (nextParticipants.length === cachedParticipants.length) {
607
- return;
608
- }
609
- await this.participantsStore.upsertGroupParticipants({
610
- groupJid,
611
- participants: nextParticipants,
612
- updatedAtMs: Date.now()
613
- });
614
- }
615
- async removeParticipantUsersFromCache(groupJid, cachedParticipants, participantsToRemove) {
616
- if (participantsToRemove.length === 0) {
617
- return;
618
- }
619
- const removed = new Set(participantsToRemove);
620
- const nextParticipants = cachedParticipants.filter((participant) => !removed.has(participant));
621
- if (nextParticipants.length === cachedParticipants.length) {
622
- return;
623
- }
624
- if (nextParticipants.length === 0) {
625
- await this.participantsStore.deleteGroupParticipants(groupJid);
626
- return;
627
- }
628
- await this.participantsStore.upsertGroupParticipants({
629
- groupJid,
630
- participants: nextParticipants,
631
- updatedAtMs: Date.now()
632
- });
633
- }
634
- async replaceParticipantUsersInCache(groupJid, cachedParticipants, participantsToReplace, replacementParticipants) {
635
- const toReplace = new Set(participantsToReplace);
636
- const nextParticipants = cachedParticipants.filter((participant) => !toReplace.has(participant));
637
- const existing = new Set(nextParticipants);
638
- for (const participant of replacementParticipants) {
639
- if (existing.has(participant)) {
640
- continue;
641
- }
642
- existing.add(participant);
643
- nextParticipants.push(participant);
644
- }
645
- if (this.areParticipantListsEqual(cachedParticipants, nextParticipants)) {
646
- return;
647
- }
648
- if (nextParticipants.length === 0) {
649
- await this.participantsStore.deleteGroupParticipants(groupJid);
650
- return;
651
- }
652
- await this.participantsStore.upsertGroupParticipants({
653
- groupJid,
654
- participants: nextParticipants,
655
- updatedAtMs: Date.now()
656
- });
657
- }
658
- areParticipantListsEqual(left, right) {
659
- if (left.length !== right.length) {
660
- return false;
661
- }
662
- for (let index = 0; index < left.length; index += 1) {
663
- if (left[index] !== right[index]) {
664
- return false;
665
- }
666
- }
667
- return true;
668
- }
669
- async refreshGroupParticipantUsers(groupJid) {
670
- const queried = await this.queryGroupParticipantJids(groupJid);
671
- const participants = this.sanitizeParticipantUsers(queried);
672
- await this.participantsStore.upsertGroupParticipants({
673
- groupJid,
674
- participants,
675
- updatedAtMs: Date.now()
676
- });
677
- return participants;
678
- }
679
- sanitizeParticipantUsers(participants) {
680
- const deduped = new Set();
681
- for (const participant of participants) {
682
- if (!participant || !participant.includes('@'))
683
- continue;
684
- try {
685
- deduped.add((0, jid_1.toUserJid)(participant));
686
- }
687
- catch (error) {
688
- this.logger.trace('ignoring malformed participant jid', {
689
- participant,
690
- message: (0, primitives_1.toError)(error).message
691
- });
692
- }
693
- }
694
- return [...deduped];
695
- }
696
412
  resolveGroupAddressingMode(participantUserJids, groupJid) {
697
- for (const participantJid of participantUserJids) {
413
+ for (let index = 0; index < participantUserJids.length; index += 1) {
414
+ const participantJid = participantUserJids[index];
698
415
  try {
699
416
  if ((0, jid_1.splitJid)(participantJid).server === 'lid') {
700
417
  return 'lid';
@@ -727,115 +444,131 @@ class WaMessageDispatchCoordinator {
727
444
  }
728
445
  return (0, jid_1.normalizeDeviceJid)(meJid);
729
446
  }
730
- async encryptGroupDistributionParticipants(groupJid, sender, senderKeyDistributionMessage, participantUserJids) {
447
+ async encryptGroupDistributionParticipants(groupJid, senderKeyId, senderKeyDistributionMessage, participantUserJids) {
731
448
  const distributionPayload = await (0, padding_1.writeRandomPadMax16)(_proto_1.proto.Message.encode({
732
449
  senderKeyDistributionMessage
733
450
  }).finish());
734
- const fanoutDeviceJids = await this.resolveGroupParticipantDeviceJids(participantUserJids);
451
+ const fanoutDeviceJids = await this.fanoutResolver.resolveGroupParticipantDeviceJids(participantUserJids);
735
452
  if (fanoutDeviceJids.length === 0) {
736
453
  return {
737
454
  fanoutDeviceJids,
738
455
  distributionParticipants: []
739
456
  };
740
457
  }
741
- const fanoutTargets = fanoutDeviceJids.map((jid) => ({
742
- jid,
743
- address: (0, jid_1.parseSignalAddressFromJid)(jid)
744
- }));
745
- const pendingAddresses = await this.senderKeyManager.filterParticipantsNeedingDistribution(groupJid, sender, fanoutTargets.map((target) => target.address));
458
+ const fanoutTargetsByAddressKey = new Map();
459
+ const fanoutAddresses = new Array(fanoutDeviceJids.length);
460
+ for (let index = 0; index < fanoutDeviceJids.length; index += 1) {
461
+ const jid = fanoutDeviceJids[index];
462
+ const address = (0, jid_1.parseSignalAddressFromJid)(jid);
463
+ fanoutAddresses[index] = address;
464
+ fanoutTargetsByAddressKey.set((0, jid_2.signalAddressKey)(address), { jid, address });
465
+ }
466
+ const pendingAddresses = await this.senderKeyManager.filterParticipantsNeedingDistribution(groupJid, senderKeyId, fanoutAddresses);
746
467
  if (pendingAddresses.length === 0) {
747
468
  return {
748
469
  fanoutDeviceJids,
749
470
  distributionParticipants: []
750
471
  };
751
472
  }
752
- const pendingAddressKeys = new Set(pendingAddresses.map(signal_address_1.signalAddressKey));
753
- const pendingTargets = fanoutTargets.filter((target) => pendingAddressKeys.has((0, signal_address_1.signalAddressKey)(target.address)));
473
+ const pendingAddressKeys = new Set();
474
+ const pendingTargets = [];
475
+ for (let index = 0; index < pendingAddresses.length; index += 1) {
476
+ const key = (0, jid_2.signalAddressKey)(pendingAddresses[index]);
477
+ if (pendingAddressKeys.has(key)) {
478
+ continue;
479
+ }
480
+ pendingAddressKeys.add(key);
481
+ const target = fanoutTargetsByAddressKey.get(key);
482
+ if (target) {
483
+ pendingTargets.push(target);
484
+ }
485
+ }
754
486
  if (pendingTargets.length === 0) {
755
487
  return {
756
488
  fanoutDeviceJids,
757
489
  distributionParticipants: []
758
490
  };
759
491
  }
760
- await this.ensureSignalSessionsBatch(pendingTargets.map((target) => target.jid));
761
- const distributionParticipants = await Promise.all(pendingTargets.map(async (target) => {
762
- await this.ensureSignalSession(target.address, target.jid);
763
- const encrypted = await this.signalProtocol.encryptMessage(target.address, distributionPayload);
492
+ const pendingTargetJids = new Array(pendingTargets.length);
493
+ for (let index = 0; index < pendingTargets.length; index += 1) {
494
+ pendingTargetJids[index] = pendingTargets[index].jid;
495
+ }
496
+ let availableTargets = [];
497
+ let prefetchedAvailableTargets;
498
+ try {
499
+ const resolvedTargets = await this.sessionResolver.ensureSessionsBatch(pendingTargetJids);
500
+ availableTargets = resolvedTargets;
501
+ prefetchedAvailableTargets = resolvedTargets;
502
+ }
503
+ catch (error) {
504
+ const normalized = (0, primitives_1.toError)(error);
505
+ if (normalized.message === 'identity mismatch') {
506
+ throw normalized;
507
+ }
508
+ this.logger.warn('group sender-key distribution session sync failed, continuing with available sessions', {
509
+ groupJid,
510
+ requested: pendingTargetJids.length,
511
+ message: normalized.message
512
+ });
513
+ const pendingTargetAddresses = new Array(pendingTargets.length);
514
+ for (let index = 0; index < pendingTargets.length; index += 1) {
515
+ pendingTargetAddresses[index] = pendingTargets[index].address;
516
+ }
517
+ const hasPendingSessions = await this.signalStore.hasSessions(pendingTargetAddresses);
518
+ const nextAvailableTargets = [];
519
+ for (let index = 0; index < pendingTargets.length; index += 1) {
520
+ if (hasPendingSessions[index]) {
521
+ nextAvailableTargets.push(pendingTargets[index]);
522
+ }
523
+ }
524
+ availableTargets = nextAvailableTargets;
525
+ }
526
+ if (availableTargets.length === 0) {
764
527
  return {
528
+ fanoutDeviceJids,
529
+ distributionParticipants: []
530
+ };
531
+ }
532
+ const distributionEncryptRequests = new Array(availableTargets.length);
533
+ for (let index = 0; index < availableTargets.length; index += 1) {
534
+ const target = availableTargets[index];
535
+ distributionEncryptRequests[index] = {
536
+ address: target.address,
537
+ plaintext: distributionPayload
538
+ };
539
+ }
540
+ const encryptedDistributionParticipants = await this.signalProtocol.encryptMessagesBatch(distributionEncryptRequests, prefetchedAvailableTargets);
541
+ const distributionParticipants = new Array(availableTargets.length);
542
+ for (let index = 0; index < availableTargets.length; index += 1) {
543
+ const target = availableTargets[index];
544
+ distributionParticipants[index] = {
765
545
  jid: target.jid,
766
546
  address: target.address,
767
- encType: encrypted.type,
768
- ciphertext: encrypted.ciphertext
547
+ encType: encryptedDistributionParticipants[index].type,
548
+ ciphertext: encryptedDistributionParticipants[index].ciphertext
769
549
  };
770
- }));
550
+ }
771
551
  return {
772
552
  fanoutDeviceJids,
773
553
  distributionParticipants
774
554
  };
775
555
  }
776
- async resolveGroupParticipantDeviceJids(participantUserJids) {
777
- const meDeviceJids = new Set();
778
- const meJid = this.getCurrentMeJid();
779
- if (meJid) {
780
- try {
781
- meDeviceJids.add((0, jid_1.normalizeDeviceJid)(meJid));
782
- }
783
- catch (error) {
784
- this.logger.trace('ignoring malformed me jid', {
785
- meJid,
786
- message: (0, primitives_1.toError)(error).message
787
- });
788
- }
789
- }
790
- const meLid = this.getCurrentMeLid();
791
- if (meLid && meLid.includes('@')) {
792
- try {
793
- meDeviceJids.add((0, jid_1.normalizeDeviceJid)(meLid));
794
- }
795
- catch (error) {
796
- this.logger.trace('ignoring malformed me lid jid', {
797
- meLid,
798
- message: (0, primitives_1.toError)(error).message
799
- });
800
- }
801
- }
802
- const candidateUsers = [...new Set(participantUserJids)];
803
- if (candidateUsers.length === 0) {
804
- return [];
805
- }
806
- try {
807
- const synced = await this.signalDeviceSync.syncDeviceList(candidateUsers);
808
- const fanout = new Set();
809
- for (const entry of synced) {
810
- if (entry.deviceJids.length === 0) {
811
- const normalizedEntryJid = (0, jid_1.normalizeDeviceJid)(entry.jid);
812
- if (meDeviceJids.has(normalizedEntryJid))
813
- continue;
814
- fanout.add(normalizedEntryJid);
815
- continue;
816
- }
817
- for (const deviceJid of entry.deviceJids) {
818
- const normalizedDeviceJid = (0, jid_1.normalizeDeviceJid)(deviceJid);
819
- if (meDeviceJids.has(normalizedDeviceJid))
820
- continue;
821
- fanout.add(normalizedDeviceJid);
822
- }
823
- }
824
- return [...fanout];
825
- }
826
- catch (error) {
827
- this.logger.warn('group participant device sync failed, falling back to participant user jids', {
828
- participants: candidateUsers.length,
829
- message: (0, primitives_1.toError)(error).message
830
- });
831
- return [...new Set(candidateUsers.map((jid) => (0, jid_1.normalizeDeviceJid)(jid)))].filter((jid) => !meDeviceJids.has(jid));
832
- }
833
- }
834
- async publishDirectSignalMessageWithFanout(recipientJid, message, plaintext, type, options) {
556
+ async publishDirectSignalMessageWithFanout(recipientJid, message, plaintext, type, options, edit, mediatype, metaNode) {
557
+ const sendOptions = await this.withResolvedMessageId(options);
835
558
  const meJid = this.requireCurrentMeJid('sendMessage');
836
559
  const meLid = this.getCurrentMeLid();
837
- const selfDeviceJidForRecipient = this.resolveSelfDeviceJidForRecipient(recipientJid, meJid, meLid);
838
- const deviceJids = await this.resolveDirectFanoutDeviceJids(recipientJid, selfDeviceJidForRecipient);
560
+ const selfDeviceJidForRecipient = this.fanoutResolver.resolveSelfDeviceJidForRecipient(recipientJid, meJid, meLid);
561
+ const deviceJids = await this.fanoutResolver.resolveDirectFanoutDeviceJids(recipientJid, selfDeviceJidForRecipient);
562
+ const targets = new Array(deviceJids.length);
563
+ for (let index = 0; index < deviceJids.length; index += 1) {
564
+ const jid = deviceJids[index];
565
+ const parsed = (0, jid_1.parseJidFull)(jid);
566
+ targets[index] = {
567
+ jid,
568
+ normalizedJid: parsed.normalizedJid,
569
+ userJid: parsed.userJid
570
+ };
571
+ }
839
572
  const recipientUserJid = (0, jid_1.toUserJid)(recipientJid);
840
573
  const meUserJid = (0, jid_1.toUserJid)(selfDeviceJidForRecipient);
841
574
  this.logger.debug('wa client publish signal fanout', {
@@ -844,44 +577,115 @@ class WaMessageDispatchCoordinator {
844
577
  type
845
578
  });
846
579
  const expectedIdentityByJid = new Map();
847
- if (options.expectedIdentity) {
848
- for (let index = 0; index < deviceJids.length; index += 1) {
849
- const targetJid = deviceJids[index];
850
- if ((0, jid_1.toUserJid)(targetJid) === recipientUserJid) {
851
- expectedIdentityByJid.set((0, jid_1.normalizeDeviceJid)(targetJid), options.expectedIdentity);
580
+ if (sendOptions.expectedIdentity) {
581
+ for (let index = 0; index < targets.length; index += 1) {
582
+ const target = targets[index];
583
+ if (target.userJid === recipientUserJid) {
584
+ expectedIdentityByJid.set(target.normalizedJid, sendOptions.expectedIdentity);
852
585
  }
853
586
  }
854
587
  }
855
- await this.ensureSignalSessionsBatch(deviceJids, expectedIdentityByJid);
856
- const hasSelfDeviceFanout = deviceJids.some((targetJid) => (0, jid_1.toUserJid)(targetJid) === meUserJid);
588
+ const resolvedFanoutTargets = await this.sessionResolver.ensureSessionsBatch(deviceJids, expectedIdentityByJid);
589
+ const resolvedFanoutTargetsByJid = new Map();
590
+ for (let index = 0; index < resolvedFanoutTargets.length; index += 1) {
591
+ const target = resolvedFanoutTargets[index];
592
+ resolvedFanoutTargetsByJid.set((0, jid_1.normalizeDeviceJid)(target.jid), target);
593
+ }
594
+ for (let index = 0; index < targets.length; index += 1) {
595
+ if (!resolvedFanoutTargetsByJid.has(targets[index].normalizedJid)) {
596
+ throw new Error('direct fanout missing signal sessions for one or more targets');
597
+ }
598
+ }
599
+ let hasSelfDeviceFanout = false;
600
+ for (let index = 0; index < targets.length; index += 1) {
601
+ if (targets[index].userJid === meUserJid) {
602
+ hasSelfDeviceFanout = true;
603
+ break;
604
+ }
605
+ }
857
606
  const selfDevicePlaintext = hasSelfDeviceFanout
858
607
  ? await (0, padding_1.writeRandomPadMax16)(_proto_1.proto.Message.encode((0, device_sent_1.wrapDeviceSentMessage)(message, recipientUserJid)).finish())
859
608
  : null;
860
- const participants = await Promise.all(deviceJids.map(async (targetJid) => {
861
- const address = (0, jid_1.parseSignalAddressFromJid)(targetJid);
862
- const targetUserJid = (0, jid_1.toUserJid)(targetJid);
863
- const expectedIdentity = targetUserJid === recipientUserJid ? options.expectedIdentity : undefined;
864
- const plaintextForTarget = selfDevicePlaintext && targetUserJid === meUserJid
865
- ? selfDevicePlaintext
866
- : plaintext;
867
- await this.ensureSignalSession(address, targetJid, expectedIdentity);
868
- const encrypted = await this.signalProtocol.encryptMessage(address, plaintextForTarget, expectedIdentity);
869
- return {
870
- jid: targetJid,
871
- encType: encrypted.type,
872
- ciphertext: encrypted.ciphertext
609
+ const participantRequests = new Array(targets.length);
610
+ for (let index = 0; index < targets.length; index += 1) {
611
+ const target = targets[index];
612
+ const resolvedTarget = resolvedFanoutTargetsByJid.get(target.normalizedJid);
613
+ if (!resolvedTarget) {
614
+ throw new Error('direct fanout missing signal session for target');
615
+ }
616
+ participantRequests[index] = {
617
+ target,
618
+ address: resolvedTarget.address,
619
+ session: resolvedTarget.session,
620
+ expectedIdentity: target.userJid === recipientUserJid ? sendOptions.expectedIdentity : undefined,
621
+ plaintext: selfDevicePlaintext && target.userJid === meUserJid
622
+ ? selfDevicePlaintext
623
+ : plaintext
624
+ };
625
+ }
626
+ const encryptRequests = new Array(participantRequests.length);
627
+ const prefetchedSessions = new Array(participantRequests.length);
628
+ for (let index = 0; index < participantRequests.length; index += 1) {
629
+ const request = participantRequests[index];
630
+ encryptRequests[index] = {
631
+ address: request.address,
632
+ plaintext: request.plaintext,
633
+ expectedIdentity: request.expectedIdentity
634
+ };
635
+ prefetchedSessions[index] = {
636
+ address: request.address,
637
+ session: request.session
873
638
  };
874
- }));
875
- const shouldAttachDeviceIdentity = participants.some((participant) => participant.encType === 'pkmsg');
639
+ }
640
+ const encryptedParticipants = await this.signalProtocol.encryptMessagesBatch(encryptRequests, prefetchedSessions);
641
+ const participants = new Array(participantRequests.length);
642
+ for (let index = 0; index < participantRequests.length; index += 1) {
643
+ const request = participantRequests[index];
644
+ participants[index] = {
645
+ jid: request.target.jid,
646
+ encType: encryptedParticipants[index].type,
647
+ ciphertext: encryptedParticipants[index].ciphertext
648
+ };
649
+ }
650
+ let shouldAttachDeviceIdentity = false;
651
+ for (let index = 0; index < participants.length; index += 1) {
652
+ if (participants[index].encType === 'pkmsg') {
653
+ shouldAttachDeviceIdentity = true;
654
+ break;
655
+ }
656
+ }
876
657
  const deviceIdentity = shouldAttachDeviceIdentity
877
658
  ? this.getEncodedSignedDeviceIdentity()
878
659
  : undefined;
660
+ const reportingArtifacts = await this.tryBuildReportingTokenArtifacts({
661
+ message,
662
+ stanzaId: sendOptions.id,
663
+ senderUserJid: meUserJid,
664
+ remoteJid: recipientUserJid,
665
+ context: 'direct_fanout'
666
+ });
667
+ let privacyTokenNode;
668
+ try {
669
+ privacyTokenNode =
670
+ (await this.privacyTokenDedup.run(`pt:${recipientUserJid}`, () => this.resolvePrivacyTokenNode(recipientUserJid))) ?? undefined;
671
+ }
672
+ catch (error) {
673
+ this.logger.warn('privacy token resolution failed', {
674
+ to: recipientUserJid,
675
+ message: (0, primitives_1.toError)(error).message
676
+ });
677
+ }
879
678
  const messageNode = (0, message_1.buildDirectMessageFanoutNode)({
880
679
  to: recipientJid,
881
680
  type,
882
- id: options.id,
681
+ id: sendOptions.id,
682
+ edit,
883
683
  participants,
884
- deviceIdentity
684
+ deviceIdentity,
685
+ reportingNode: reportingArtifacts?.node ?? undefined,
686
+ privacyTokenNode,
687
+ metaNode,
688
+ mediatype
885
689
  });
886
690
  const replayPayload = {
887
691
  mode: 'plaintext',
@@ -889,130 +693,75 @@ class WaMessageDispatchCoordinator {
889
693
  type,
890
694
  plaintext
891
695
  };
892
- return this.publishWithRetryTracking({
893
- messageIdHint: options.id ?? messageNode.attrs.id,
696
+ const result = await this.retryTracker.track({
697
+ messageIdHint: sendOptions.id ?? messageNode.attrs.id,
894
698
  toJid: recipientJid,
895
- messageType: type,
896
- replayPayload
897
- }, async () => this.messageClient.publishNode(messageNode, options));
699
+ type,
700
+ replayPayload,
701
+ eligibleRequesterDeviceJids: deviceJids
702
+ }, async () => this.messageClient.publishNode(messageNode, sendOptions));
703
+ this.onDirectMessageSent(recipientUserJid);
704
+ return result;
898
705
  }
899
- async ensureSignalSessionsBatch(targetJids, expectedIdentityByJid = new Map()) {
900
- const normalizedTargetJids = [...new Set(targetJids.map((jid) => (0, jid_1.normalizeDeviceJid)(jid)))];
901
- if (normalizedTargetJids.length === 0) {
902
- return;
903
- }
904
- const normalizedTargets = normalizedTargetJids.map((jid) => ({
905
- jid,
906
- address: (0, jid_1.parseSignalAddressFromJid)(jid)
907
- }));
908
- const hasSessions = await this.signalProtocol.hasSessions(normalizedTargets.map((target) => target.address));
909
- const missingTargets = normalizedTargets.filter((_, index) => !hasSessions[index]);
910
- if (missingTargets.length === 0) {
911
- return;
706
+ async withResolvedMessageId(options) {
707
+ const normalizedId = options.id?.trim();
708
+ if (normalizedId) {
709
+ if (normalizedId === options.id) {
710
+ return options;
711
+ }
712
+ return {
713
+ ...options,
714
+ id: normalizedId
715
+ };
912
716
  }
717
+ return {
718
+ ...options,
719
+ id: await this.generateOutgoingMessageId()
720
+ };
721
+ }
722
+ async generateOutgoingMessageId() {
913
723
  try {
914
- const batchResults = await this.signalSessionSync.fetchKeyBundles(missingTargets.map((target) => ({ jid: target.jid })));
915
- const resultByJid = new Map(batchResults.map((result) => [(0, jid_1.normalizeDeviceJid)(result.jid), result]));
916
- const fallbackJids = [];
917
- const establishPromises = [];
918
- for (let index = 0; index < missingTargets.length; index += 1) {
919
- const target = missingTargets[index];
920
- const result = resultByJid.get(target.jid);
921
- if (!result || !('bundle' in result)) {
922
- fallbackJids.push(target.jid);
923
- continue;
924
- }
925
- const expectedIdentity = expectedIdentityByJid.get(target.jid);
926
- const remoteIdentity = (0, keys_1.toSerializedPubKey)(result.bundle.identity);
927
- if (expectedIdentity &&
928
- !(0, bytes_1.uint8Equal)(remoteIdentity, (0, keys_1.toSerializedPubKey)(expectedIdentity))) {
929
- throw new Error('identity mismatch');
930
- }
931
- establishPromises.push(this.signalProtocol
932
- .establishOutgoingSession(target.address, result.bundle)
933
- .then(() => {
934
- this.logger.debug('signal session synchronized from batch key fetch', {
935
- jid: target.jid,
936
- regId: result.bundle.regId,
937
- hasOneTimeKey: result.bundle.oneTimeKey !== undefined
938
- });
939
- }));
940
- }
941
- await Promise.all(establishPromises);
942
- if (fallbackJids.length === 0) {
943
- return;
944
- }
945
- this.logger.warn('signal batch key fetch returned partial errors, falling back to single requests', {
946
- requested: missingTargets.length,
947
- fallbackTargets: fallbackJids.length
948
- });
949
- for (let index = 0; index < fallbackJids.length; index += 1) {
950
- const jid = fallbackJids[index];
951
- const address = (0, jid_1.parseSignalAddressFromJid)(jid);
952
- await this.ensureSignalSession(address, jid, expectedIdentityByJid.get(jid));
953
- }
724
+ const meUserJid = (0, jid_1.toUserJid)(this.requireCurrentMeJid('sendMessage'));
725
+ const timestampSeconds = Math.floor(Date.now() / 1000);
726
+ const timestampBytes = new Uint8Array(8);
727
+ new DataView(timestampBytes.buffer, timestampBytes.byteOffset, timestampBytes.byteLength).setBigUint64(0, BigInt(timestampSeconds), false);
728
+ const entropy = (0, bytes_1.concatBytes)([
729
+ timestampBytes,
730
+ bytes_1.TEXT_ENCODER.encode(meUserJid),
731
+ await (0, _crypto_1.randomBytesAsync)(8)
732
+ ]);
733
+ const digest = await (0, _crypto_1.sha256)(entropy);
734
+ return `3EB0${(0, bytes_1.bytesToHex)(digest.subarray(0, 9)).toUpperCase()}`;
954
735
  }
955
736
  catch (error) {
956
- const normalized = (0, primitives_1.toError)(error);
957
- if (normalized.message === 'identity mismatch') {
958
- throw normalized;
959
- }
960
- this.logger.warn('signal batch key fetch failed, falling back to single requests', {
961
- requested: missingTargets.length,
962
- message: normalized.message
737
+ this.logger.warn('failed to generate sha256 message id, falling back to random id', {
738
+ message: (0, primitives_1.toError)(error).message
963
739
  });
964
- for (let index = 0; index < missingTargets.length; index += 1) {
965
- const target = missingTargets[index];
966
- await this.ensureSignalSession(target.address, target.jid, expectedIdentityByJid.get(target.jid));
967
- }
740
+ return `3EB0${(0, bytes_1.bytesToHex)(await (0, _crypto_1.randomBytesAsync)(8)).toUpperCase()}`;
968
741
  }
969
742
  }
970
- async resolveDirectFanoutDeviceJids(recipientJid, selfDeviceJidForRecipient) {
971
- const recipientUserJid = (0, jid_1.toUserJid)(recipientJid);
972
- const meUserJid = (0, jid_1.toUserJid)(selfDeviceJidForRecipient);
973
- const targets = recipientUserJid === meUserJid ? [recipientUserJid] : [recipientUserJid, meUserJid];
743
+ async tryBuildReportingTokenArtifacts(input) {
744
+ if (!input.stanzaId) {
745
+ return null;
746
+ }
974
747
  try {
975
- const synced = await this.signalDeviceSync.syncDeviceList(targets);
976
- const byUser = new Map(synced.map((entry) => [(0, jid_1.toUserJid)(entry.jid), entry.deviceJids]));
977
- const fanout = new Set();
978
- const recipientDevices = byUser.get(recipientUserJid) ?? [];
979
- if (recipientDevices.length === 0) {
980
- fanout.add(recipientUserJid);
981
- }
982
- else {
983
- for (let index = 0; index < recipientDevices.length; index += 1) {
984
- fanout.add(recipientDevices[index]);
985
- }
986
- }
987
- const meDevices = byUser.get(meUserJid) ?? [];
988
- const normalizedMeJid = (0, jid_1.normalizeDeviceJid)(selfDeviceJidForRecipient);
989
- for (let index = 0; index < meDevices.length; index += 1) {
990
- const deviceJid = meDevices[index];
991
- if ((0, jid_1.normalizeDeviceJid)(deviceJid) === normalizedMeJid) {
992
- continue;
993
- }
994
- fanout.add(deviceJid);
995
- }
996
- return [...fanout];
748
+ return await (0, reporting_token_1.buildReportingTokenArtifacts)({
749
+ message: input.message,
750
+ stanzaId: input.stanzaId,
751
+ senderUserJid: input.senderUserJid,
752
+ remoteJid: input.remoteJid
753
+ });
997
754
  }
998
755
  catch (error) {
999
- const message = error instanceof Error ? error.message : String(error);
1000
- this.logger.warn('signal device fanout sync failed, falling back to direct recipient', {
1001
- to: recipientJid,
1002
- message
756
+ this.logger.warn('failed to generate reporting token', {
757
+ context: input.context,
758
+ id: input.stanzaId,
759
+ remoteJid: input.remoteJid,
760
+ message: (0, primitives_1.toError)(error).message
1003
761
  });
1004
- return [recipientUserJid];
762
+ return null;
1005
763
  }
1006
764
  }
1007
- resolveSelfDeviceJidForRecipient(recipientJid, meJid, meLid) {
1008
- if ((0, jid_1.splitJid)(recipientJid).server !== 'lid') {
1009
- return meJid;
1010
- }
1011
- if (!meLid || !meLid.includes('@')) {
1012
- return meJid;
1013
- }
1014
- return meLid;
1015
- }
1016
765
  getEncodedSignedDeviceIdentity() {
1017
766
  const signedIdentity = this.getCurrentSignedIdentity();
1018
767
  if (!signedIdentity) {
@@ -1020,34 +769,23 @@ class WaMessageDispatchCoordinator {
1020
769
  }
1021
770
  return _proto_1.proto.ADVSignedDeviceIdentity.encode(signedIdentity).finish();
1022
771
  }
1023
- async ensureSignalSession(address, jid, expectedIdentity, reasonIdentity = false) {
1024
- this.requireCurrentMeJid('ensureSignalSession');
1025
- if (reasonIdentity) {
1026
- await this.signalIdentitySync.syncIdentityKeys([jid]);
1027
- }
1028
- if (await this.signalProtocol.hasSession(address)) {
1029
- return;
1030
- }
1031
- this.logger.info('signal session missing, fetching remote key bundle', { jid });
1032
- const fetched = await this.signalSessionSync.fetchKeyBundle({
1033
- jid,
1034
- reasonIdentity
1035
- });
1036
- const remoteIdentity = (0, keys_1.toSerializedPubKey)(fetched.bundle.identity);
1037
- if (reasonIdentity) {
1038
- const storedIdentity = await this.signalStore.getRemoteIdentity(address);
1039
- if (storedIdentity && !(0, bytes_1.uint8Equal)(remoteIdentity, storedIdentity)) {
1040
- throw new Error('identity mismatch');
772
+ resolveUserIcdc(userJid, localIdentity) {
773
+ return this.icdcDedup.run(`icdc:${userJid}:${localIdentity ? '1' : '0'}`, async () => {
774
+ try {
775
+ const snapshots = await this.deviceListStore.getUserDevicesBatch([userJid]);
776
+ const snapshot = snapshots[0];
777
+ if (!snapshot || snapshot.deviceJids.length === 0) {
778
+ return null;
779
+ }
780
+ return (0, icdc_1.resolveIcdcMeta)(snapshot.deviceJids, this.signalStore, snapshot.updatedAtMs, localIdentity);
781
+ }
782
+ catch (error) {
783
+ this.logger.trace('icdc resolution failed', {
784
+ userJid,
785
+ message: (0, primitives_1.toError)(error).message
786
+ });
787
+ return null;
1041
788
  }
1042
- }
1043
- if (expectedIdentity && !(0, bytes_1.uint8Equal)(remoteIdentity, (0, keys_1.toSerializedPubKey)(expectedIdentity))) {
1044
- throw new Error('identity mismatch');
1045
- }
1046
- await this.signalProtocol.establishOutgoingSession(address, fetched.bundle);
1047
- this.logger.info('signal session synchronized', {
1048
- jid,
1049
- regId: fetched.bundle.regId,
1050
- hasOneTimeKey: fetched.bundle.oneTimeKey !== undefined
1051
789
  });
1052
790
  }
1053
791
  requireCurrentMeJid(context) {