zapo-js 0.1.0 → 0.1.2

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 (366) hide show
  1. package/README.md +12 -7
  2. package/dist/appstate/WaAppStateCrypto.js +18 -25
  3. package/dist/appstate/WaAppStateSyncClient.js +181 -114
  4. package/dist/appstate/WaAppStateSyncResponseParser.js +16 -5
  5. package/dist/appstate/constants.js +4 -3
  6. package/dist/appstate/utils.js +10 -30
  7. package/dist/auth/WaAuthClient.js +48 -55
  8. package/dist/auth/flow/WaAuthCredentialsFlow.js +21 -14
  9. package/dist/auth/index.js +1 -3
  10. package/dist/auth/pairing/WaPairingFlow.js +21 -23
  11. package/dist/auth/pairing/WaQrFlow.js +37 -24
  12. package/dist/client/WaClient.js +103 -276
  13. package/dist/client/WaClientFactory.js +227 -110
  14. package/dist/client/connection/WaConnectionManager.js +292 -0
  15. package/dist/client/connection/WaKeyShareCoordinator.js +63 -0
  16. package/dist/client/connection/WaReceiptQueue.js +51 -0
  17. package/dist/client/coordinators/WaAppStateMutationCoordinator.js +471 -0
  18. package/dist/client/coordinators/WaGroupCoordinator.js +27 -17
  19. package/dist/client/coordinators/WaIncomingNodeCoordinator.js +20 -27
  20. package/dist/client/coordinators/WaMessageDispatchCoordinator.js +231 -686
  21. package/dist/client/coordinators/WaRetryCoordinator.js +70 -37
  22. package/dist/client/dirty.js +35 -29
  23. package/dist/client/events/chat.js +4 -3
  24. package/dist/client/events/group.js +59 -36
  25. package/dist/client/history-sync.js +53 -63
  26. package/dist/client/incoming.js +23 -20
  27. package/dist/client/mailbox.js +8 -8
  28. package/dist/client/messages.js +4 -4
  29. package/dist/client/messaging/fanout.js +189 -0
  30. package/dist/client/messaging/key-protocol.js +130 -0
  31. package/dist/client/messaging/participants.js +191 -0
  32. package/dist/crypto/core/hkdf.js +3 -8
  33. package/dist/crypto/core/index.js +1 -4
  34. package/dist/crypto/core/keys.js +2 -3
  35. package/dist/crypto/core/primitives.js +12 -15
  36. package/dist/crypto/core/random.js +7 -26
  37. package/dist/crypto/curves/Ed25519.js +7 -8
  38. package/dist/crypto/curves/X25519.js +13 -16
  39. package/dist/crypto/index.js +0 -5
  40. package/dist/esm/appstate/WaAppStateCrypto.js +6 -13
  41. package/dist/esm/appstate/WaAppStateSyncClient.js +174 -107
  42. package/dist/esm/appstate/WaAppStateSyncResponseParser.js +17 -6
  43. package/dist/esm/appstate/constants.js +3 -2
  44. package/dist/esm/appstate/utils.js +8 -27
  45. package/dist/esm/auth/WaAuthClient.js +48 -55
  46. package/dist/esm/auth/flow/WaAuthCredentialsFlow.js +21 -14
  47. package/dist/esm/auth/index.js +0 -1
  48. package/dist/esm/auth/pairing/WaPairingFlow.js +14 -16
  49. package/dist/esm/auth/pairing/WaQrFlow.js +37 -24
  50. package/dist/esm/client/WaClient.js +103 -276
  51. package/dist/esm/client/WaClientFactory.js +227 -110
  52. package/dist/esm/client/connection/WaConnectionManager.js +288 -0
  53. package/dist/esm/client/connection/WaKeyShareCoordinator.js +59 -0
  54. package/dist/esm/client/connection/WaReceiptQueue.js +47 -0
  55. package/dist/esm/client/coordinators/WaAppStateMutationCoordinator.js +467 -0
  56. package/dist/esm/client/coordinators/WaGroupCoordinator.js +20 -10
  57. package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +20 -27
  58. package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +232 -687
  59. package/dist/esm/client/coordinators/WaRetryCoordinator.js +71 -38
  60. package/dist/esm/client/dirty.js +30 -24
  61. package/dist/esm/client/events/chat.js +4 -3
  62. package/dist/esm/client/events/group.js +50 -28
  63. package/dist/esm/client/history-sync.js +50 -60
  64. package/dist/esm/client/incoming.js +23 -20
  65. package/dist/esm/client/mailbox.js +8 -8
  66. package/dist/esm/client/messages.js +1 -1
  67. package/dist/esm/client/messaging/fanout.js +186 -0
  68. package/dist/esm/client/messaging/key-protocol.js +127 -0
  69. package/dist/esm/client/messaging/participants.js +188 -0
  70. package/dist/esm/crypto/core/hkdf.js +3 -8
  71. package/dist/esm/crypto/core/index.js +0 -1
  72. package/dist/esm/crypto/core/keys.js +2 -3
  73. package/dist/esm/crypto/core/primitives.js +12 -15
  74. package/dist/esm/crypto/core/random.js +6 -25
  75. package/dist/esm/crypto/curves/Ed25519.js +4 -5
  76. package/dist/esm/crypto/curves/X25519.js +10 -13
  77. package/dist/esm/crypto/index.js +0 -2
  78. package/dist/esm/infra/log/ConsoleLogger.js +18 -17
  79. package/dist/esm/infra/log/PinoLogger.js +15 -9
  80. package/dist/esm/infra/log/types.js +11 -1
  81. package/dist/esm/infra/perf/BoundedTaskQueue.js +13 -17
  82. package/dist/esm/media/WaMediaCrypto.js +2 -4
  83. package/dist/esm/media/WaMediaTransferClient.js +226 -58
  84. package/dist/esm/media/conn.js +10 -6
  85. package/dist/esm/media/constants.js +4 -1
  86. package/dist/esm/message/WaMessageClient.js +4 -13
  87. package/dist/esm/message/ack.js +6 -6
  88. package/dist/esm/message/addon-crypto.js +59 -0
  89. package/dist/esm/message/incoming.js +106 -111
  90. package/dist/esm/message/index.js +2 -0
  91. package/dist/esm/message/reporting-token.js +438 -0
  92. package/dist/esm/message/use-case-secret.js +49 -0
  93. package/dist/esm/protocol/appstate.js +58 -0
  94. package/dist/esm/protocol/constants.js +2 -1
  95. package/dist/esm/protocol/index.js +2 -10
  96. package/dist/esm/protocol/jid.js +63 -51
  97. package/dist/esm/protocol/media.js +3 -3
  98. package/dist/esm/protocol/nodes.js +2 -0
  99. package/dist/esm/protocol/usync.js +11 -0
  100. package/dist/esm/retry/index.js +1 -0
  101. package/dist/esm/retry/outbound.js +4 -5
  102. package/dist/esm/retry/parse.js +58 -76
  103. package/dist/esm/retry/replay.js +48 -49
  104. package/dist/esm/retry/tracker.js +56 -0
  105. package/dist/esm/signal/api/SignalDeviceSyncApi.js +249 -82
  106. package/dist/esm/signal/api/SignalDigestSyncApi.js +6 -1
  107. package/dist/esm/signal/api/SignalIdentitySyncApi.js +49 -34
  108. package/dist/esm/signal/api/SignalMissingPreKeysSyncApi.js +70 -62
  109. package/dist/esm/signal/api/SignalSessionSyncApi.js +23 -30
  110. package/dist/esm/signal/crypto/WaAdvSignature.js +3 -5
  111. package/dist/esm/signal/group/SenderKeyChain.js +28 -23
  112. package/dist/esm/signal/group/SenderKeyCodec.js +2 -4
  113. package/dist/esm/signal/group/SenderKeyManager.js +26 -16
  114. package/dist/esm/signal/index.js +1 -0
  115. package/dist/esm/signal/session/SignalProtocol.js +49 -14
  116. package/dist/esm/signal/session/SignalRatchet.js +24 -15
  117. package/dist/esm/signal/session/SignalSession.js +14 -9
  118. package/dist/esm/signal/session/resolver.js +186 -0
  119. package/dist/esm/signal/store/sqlite.js +16 -37
  120. package/dist/esm/store/createStore.js +16 -18
  121. package/dist/esm/store/noop.store.js +3 -6
  122. package/dist/esm/store/providers/memory/appstate.store.js +30 -6
  123. package/dist/esm/store/providers/memory/contact.store.js +5 -0
  124. package/dist/esm/store/providers/memory/device-list.store.js +3 -30
  125. package/dist/esm/store/providers/memory/message.store.js +11 -5
  126. package/dist/esm/store/providers/memory/participants.store.js +1 -8
  127. package/dist/esm/store/providers/memory/sender-key.store.js +5 -7
  128. package/dist/esm/store/providers/memory/signal.store.js +13 -1
  129. package/dist/esm/store/providers/memory/thread.store.js +5 -0
  130. package/dist/esm/store/providers/sqlite/appstate.store.js +82 -1
  131. package/dist/esm/store/providers/sqlite/connection.js +18 -13
  132. package/dist/esm/store/providers/sqlite/contact.store.js +31 -18
  133. package/dist/esm/store/providers/sqlite/device-list.store.js +7 -35
  134. package/dist/esm/store/providers/sqlite/message.store.js +45 -32
  135. package/dist/esm/store/providers/sqlite/migrations.js +1 -1
  136. package/dist/esm/store/providers/sqlite/participants.store.js +1 -9
  137. package/dist/esm/store/providers/sqlite/retry.store.js +8 -11
  138. package/dist/esm/store/providers/sqlite/sender-key.store.js +25 -30
  139. package/dist/esm/store/providers/sqlite/signal.store.js +104 -22
  140. package/dist/esm/store/providers/sqlite/table-names.js +107 -0
  141. package/dist/esm/store/providers/sqlite/thread.store.js +35 -22
  142. package/dist/esm/transport/WaComms.js +25 -23
  143. package/dist/esm/transport/WaWebSocket.js +115 -12
  144. package/dist/esm/transport/binary/decoder.js +4 -4
  145. package/dist/esm/transport/binary/encoder.js +12 -4
  146. package/dist/esm/transport/index.js +1 -0
  147. package/dist/esm/transport/keepalive/WaKeepAlive.js +2 -8
  148. package/dist/esm/transport/node/WaNodeOrchestrator.js +2 -4
  149. package/dist/esm/transport/node/WaNodeTransport.js +0 -3
  150. package/dist/esm/transport/node/builders/{accountSync.js → account-sync.js} +16 -36
  151. package/dist/esm/transport/node/builders/index.js +2 -1
  152. package/dist/esm/transport/node/builders/message.js +9 -0
  153. package/dist/esm/transport/node/builders/pairing.js +4 -5
  154. package/dist/esm/transport/node/builders/usync.js +41 -0
  155. package/dist/esm/transport/node/helpers.js +107 -5
  156. package/dist/esm/transport/node/usync.js +35 -0
  157. package/dist/esm/transport/noise/WaFrameCodec.js +48 -33
  158. package/dist/esm/transport/noise/WaNoiseCert.js +3 -6
  159. package/dist/esm/transport/noise/WaNoiseSession.js +17 -10
  160. package/dist/esm/transport/proxy.js +27 -0
  161. package/dist/esm/transport/stream/parse.js +13 -48
  162. package/dist/esm/util/bytes.js +50 -32
  163. package/dist/esm/util/coercion.js +6 -14
  164. package/dist/esm/util/primitives.js +39 -14
  165. package/dist/infra/log/ConsoleLogger.js +18 -17
  166. package/dist/infra/log/PinoLogger.js +15 -9
  167. package/dist/infra/log/types.js +12 -0
  168. package/dist/infra/perf/BoundedTaskQueue.js +13 -17
  169. package/dist/media/WaMediaCrypto.js +1 -3
  170. package/dist/media/WaMediaTransferClient.js +259 -58
  171. package/dist/media/conn.js +10 -6
  172. package/dist/media/constants.js +4 -1
  173. package/dist/message/WaMessageClient.js +5 -14
  174. package/dist/message/ack.js +6 -6
  175. package/dist/message/addon-crypto.js +65 -0
  176. package/dist/message/incoming.js +104 -109
  177. package/dist/message/index.js +2 -0
  178. package/dist/message/reporting-token.js +443 -0
  179. package/dist/message/use-case-secret.js +55 -0
  180. package/dist/protocol/appstate.js +59 -1
  181. package/dist/protocol/constants.js +7 -1
  182. package/dist/protocol/index.js +20 -42
  183. package/dist/protocol/jid.js +64 -51
  184. package/dist/protocol/media.js +3 -3
  185. package/dist/protocol/nodes.js +2 -0
  186. package/dist/protocol/usync.js +14 -0
  187. package/dist/retry/index.js +3 -1
  188. package/dist/retry/outbound.js +6 -7
  189. package/dist/retry/parse.js +57 -75
  190. package/dist/retry/replay.js +46 -47
  191. package/dist/retry/tracker.js +59 -0
  192. package/dist/signal/api/SignalDeviceSyncApi.js +247 -80
  193. package/dist/signal/api/SignalDigestSyncApi.js +6 -1
  194. package/dist/signal/api/SignalIdentitySyncApi.js +49 -34
  195. package/dist/signal/api/SignalMissingPreKeysSyncApi.js +67 -59
  196. package/dist/signal/api/SignalSessionSyncApi.js +23 -30
  197. package/dist/signal/crypto/WaAdvSignature.js +2 -4
  198. package/dist/signal/group/SenderKeyChain.js +27 -22
  199. package/dist/signal/group/SenderKeyCodec.js +1 -3
  200. package/dist/signal/group/SenderKeyManager.js +26 -16
  201. package/dist/signal/index.js +3 -1
  202. package/dist/signal/session/SignalProtocol.js +49 -14
  203. package/dist/signal/session/SignalRatchet.js +24 -15
  204. package/dist/signal/session/SignalSession.js +14 -9
  205. package/dist/signal/session/resolver.js +189 -0
  206. package/dist/signal/store/sqlite.js +16 -37
  207. package/dist/store/createStore.js +16 -18
  208. package/dist/store/noop.store.js +3 -6
  209. package/dist/store/providers/memory/appstate.store.js +28 -4
  210. package/dist/store/providers/memory/contact.store.js +5 -0
  211. package/dist/store/providers/memory/device-list.store.js +3 -30
  212. package/dist/store/providers/memory/message.store.js +11 -5
  213. package/dist/store/providers/memory/participants.store.js +1 -8
  214. package/dist/store/providers/memory/sender-key.store.js +8 -10
  215. package/dist/store/providers/memory/signal.store.js +21 -9
  216. package/dist/store/providers/memory/thread.store.js +5 -0
  217. package/dist/store/providers/sqlite/appstate.store.js +81 -0
  218. package/dist/store/providers/sqlite/connection.js +18 -13
  219. package/dist/store/providers/sqlite/contact.store.js +31 -18
  220. package/dist/store/providers/sqlite/device-list.store.js +7 -35
  221. package/dist/store/providers/sqlite/message.store.js +45 -32
  222. package/dist/store/providers/sqlite/migrations.js +1 -1
  223. package/dist/store/providers/sqlite/participants.store.js +1 -9
  224. package/dist/store/providers/sqlite/retry.store.js +8 -11
  225. package/dist/store/providers/sqlite/sender-key.store.js +24 -29
  226. package/dist/store/providers/sqlite/signal.store.js +105 -23
  227. package/dist/store/providers/sqlite/table-names.js +113 -0
  228. package/dist/store/providers/sqlite/thread.store.js +35 -22
  229. package/dist/transport/WaComms.js +27 -25
  230. package/dist/transport/WaWebSocket.js +148 -12
  231. package/dist/transport/binary/decoder.js +4 -4
  232. package/dist/transport/binary/encoder.js +12 -4
  233. package/dist/transport/index.js +7 -1
  234. package/dist/transport/keepalive/WaKeepAlive.js +1 -7
  235. package/dist/transport/node/WaNodeOrchestrator.js +2 -4
  236. package/dist/transport/node/WaNodeTransport.js +0 -3
  237. package/dist/transport/node/builders/{accountSync.js → account-sync.js} +15 -35
  238. package/dist/transport/node/builders/index.js +12 -9
  239. package/dist/transport/node/builders/message.js +9 -0
  240. package/dist/transport/node/builders/pairing.js +4 -5
  241. package/dist/transport/node/builders/usync.js +45 -0
  242. package/dist/transport/node/helpers.js +112 -4
  243. package/dist/transport/node/usync.js +38 -0
  244. package/dist/transport/noise/WaFrameCodec.js +47 -32
  245. package/dist/transport/noise/WaNoiseCert.js +5 -8
  246. package/dist/transport/noise/WaNoiseSession.js +17 -10
  247. package/dist/transport/proxy.js +34 -0
  248. package/dist/transport/stream/parse.js +17 -53
  249. package/dist/types/appstate/WaAppStateCrypto.d.ts +0 -1
  250. package/dist/types/appstate/WaAppStateSyncClient.d.ts +5 -2
  251. package/dist/types/appstate/constants.d.ts +1 -0
  252. package/dist/types/appstate/store/sqlite.d.ts +4 -18
  253. package/dist/types/appstate/utils.d.ts +0 -1
  254. package/dist/types/auth/WaAuthClient.d.ts +10 -12
  255. package/dist/types/auth/index.d.ts +0 -2
  256. package/dist/types/auth/pairing/WaQrFlow.d.ts +1 -1
  257. package/dist/types/auth/types.d.ts +6 -9
  258. package/dist/types/client/WaClient.d.ts +27 -25
  259. package/dist/types/client/WaClientFactory.d.ts +22 -23
  260. package/dist/types/client/connection/WaConnectionManager.d.ts +64 -0
  261. package/dist/types/client/connection/WaKeyShareCoordinator.d.ts +14 -0
  262. package/dist/types/client/connection/WaReceiptQueue.d.ts +13 -0
  263. package/dist/types/client/coordinators/WaAppStateMutationCoordinator.d.ts +46 -0
  264. package/dist/types/client/coordinators/WaIncomingNodeCoordinator.d.ts +0 -1
  265. package/dist/types/client/coordinators/WaMessageDispatchCoordinator.d.ts +18 -41
  266. package/dist/types/client/coordinators/WaRetryCoordinator.d.ts +2 -0
  267. package/dist/types/client/dirty.d.ts +1 -0
  268. package/dist/types/client/events/group.d.ts +2 -1
  269. package/dist/types/client/index.d.ts +1 -1
  270. package/dist/types/client/messaging/fanout.d.ts +14 -0
  271. package/dist/types/client/messaging/key-protocol.d.ts +18 -0
  272. package/dist/types/client/messaging/participants.d.ts +13 -0
  273. package/dist/types/client/types.d.ts +24 -1
  274. package/dist/types/crypto/core/hkdf.d.ts +0 -6
  275. package/dist/types/crypto/core/index.d.ts +0 -1
  276. package/dist/types/crypto/core/random.d.ts +1 -7
  277. package/dist/types/crypto/index.d.ts +0 -2
  278. package/dist/types/index.d.ts +1 -1
  279. package/dist/types/infra/log/ConsoleLogger.d.ts +2 -1
  280. package/dist/types/infra/log/PinoLogger.d.ts +1 -1
  281. package/dist/types/infra/log/types.d.ts +1 -0
  282. package/dist/types/infra/perf/BoundedTaskQueue.d.ts +1 -1
  283. package/dist/types/media/WaMediaTransferClient.d.ts +13 -3
  284. package/dist/types/media/types.d.ts +5 -0
  285. package/dist/types/message/addon-crypto.d.ts +25 -0
  286. package/dist/types/message/index.d.ts +2 -0
  287. package/dist/types/message/reporting-token.d.ts +19 -0
  288. package/dist/types/message/use-case-secret.d.ts +20 -0
  289. package/dist/types/protocol/appstate.d.ts +58 -0
  290. package/dist/types/protocol/constants.d.ts +2 -1
  291. package/dist/types/protocol/index.d.ts +2 -10
  292. package/dist/types/protocol/jid.d.ts +3 -3
  293. package/dist/types/protocol/nodes.d.ts +2 -0
  294. package/dist/types/protocol/usync.d.ts +11 -0
  295. package/dist/types/retry/index.d.ts +1 -0
  296. package/dist/types/retry/replay.d.ts +0 -4
  297. package/dist/types/retry/tracker.d.ts +19 -0
  298. package/dist/types/retry/types.d.ts +4 -3
  299. package/dist/types/signal/api/SignalDeviceSyncApi.d.ts +13 -1
  300. package/dist/types/signal/group/SenderKeyCodec.d.ts +4 -6
  301. package/dist/types/signal/index.d.ts +1 -0
  302. package/dist/types/signal/session/SignalProtocol.d.ts +9 -0
  303. package/dist/types/signal/session/resolver.d.ts +17 -0
  304. package/dist/types/store/contracts/appstate.store.d.ts +3 -0
  305. package/dist/types/store/contracts/contact.store.d.ts +1 -0
  306. package/dist/types/store/contracts/device-list.store.d.ts +0 -3
  307. package/dist/types/store/contracts/message.store.d.ts +1 -0
  308. package/dist/types/store/contracts/participants.store.d.ts +0 -1
  309. package/dist/types/store/contracts/sender-key.store.d.ts +0 -1
  310. package/dist/types/store/contracts/signal.store.d.ts +6 -0
  311. package/dist/types/store/contracts/thread.store.d.ts +1 -0
  312. package/dist/types/store/index.d.ts +1 -1
  313. package/dist/types/store/providers/memory/appstate.store.d.ts +2 -0
  314. package/dist/types/store/providers/memory/contact.store.d.ts +1 -0
  315. package/dist/types/store/providers/memory/device-list.store.d.ts +0 -3
  316. package/dist/types/store/providers/memory/message.store.d.ts +1 -0
  317. package/dist/types/store/providers/memory/participants.store.d.ts +0 -1
  318. package/dist/types/store/providers/memory/sender-key.store.d.ts +0 -1
  319. package/dist/types/store/providers/memory/signal.store.d.ts +6 -0
  320. package/dist/types/store/providers/memory/thread.store.d.ts +1 -0
  321. package/dist/types/store/providers/sqlite/appstate.store.d.ts +2 -0
  322. package/dist/types/store/providers/sqlite/contact.store.d.ts +2 -0
  323. package/dist/types/store/providers/sqlite/device-list.store.d.ts +0 -3
  324. package/dist/types/store/providers/sqlite/message.store.d.ts +2 -0
  325. package/dist/types/store/providers/sqlite/participants.store.d.ts +0 -1
  326. package/dist/types/store/providers/sqlite/retry.store.d.ts +0 -1
  327. package/dist/types/store/providers/sqlite/sender-key.store.d.ts +0 -1
  328. package/dist/types/store/providers/sqlite/signal.store.d.ts +7 -0
  329. package/dist/types/store/providers/sqlite/table-names.d.ts +5 -0
  330. package/dist/types/store/providers/sqlite/thread.store.d.ts +2 -0
  331. package/dist/types/store/types.d.ts +3 -0
  332. package/dist/types/transport/WaWebSocket.d.ts +3 -0
  333. package/dist/types/transport/index.d.ts +2 -1
  334. package/dist/types/transport/keepalive/WaKeepAlive.d.ts +0 -1
  335. package/dist/types/transport/node/WaNodeTransport.d.ts +0 -9
  336. package/dist/types/transport/node/builders/group.d.ts +4 -6
  337. package/dist/types/transport/node/builders/index.d.ts +2 -1
  338. package/dist/types/transport/node/builders/message.d.ts +14 -25
  339. package/dist/types/transport/node/builders/retry.d.ts +2 -4
  340. package/dist/types/transport/node/builders/usync.d.ts +21 -0
  341. package/dist/types/transport/node/helpers.d.ts +8 -0
  342. package/dist/types/transport/node/usync.d.ts +2 -0
  343. package/dist/types/transport/noise/WaFrameCodec.d.ts +3 -0
  344. package/dist/types/transport/noise/WaNoiseSession.d.ts +1 -0
  345. package/dist/types/transport/proxy.d.ts +6 -0
  346. package/dist/types/transport/stream/parse.d.ts +0 -1
  347. package/dist/types/transport/types.d.ts +18 -1
  348. package/dist/types/util/bytes.d.ts +5 -0
  349. package/dist/types/util/primitives.d.ts +3 -0
  350. package/dist/util/bytes.js +55 -33
  351. package/dist/util/coercion.js +6 -14
  352. package/dist/util/primitives.js +42 -14
  353. package/package.json +27 -9
  354. package/proto/index.d.ts +1090 -1048
  355. package/proto/index.js +1 -1
  356. package/scripts/check-node-version.cjs +0 -1
  357. package/dist/crypto/core/encoding.js +0 -29
  358. package/dist/esm/crypto/core/encoding.js +0 -25
  359. package/dist/esm/util/base64.js +0 -18
  360. package/dist/esm/util/signal-address.js +0 -5
  361. package/dist/types/crypto/core/encoding.d.ts +0 -11
  362. package/dist/types/util/base64.d.ts +0 -4
  363. package/dist/types/util/signal-address.d.ts +0 -2
  364. package/dist/util/base64.js +0 -24
  365. package/dist/util/signal-address.js +0 -8
  366. /package/dist/types/transport/node/builders/{accountSync.d.ts → account-sync.d.ts} +0 -0
@@ -1,36 +1,33 @@
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 _message_1 = require("../../message/index.js");
5
6
  const content_1 = require("../../message/content");
6
7
  const device_sent_1 = require("../../message/device-sent");
7
8
  const padding_1 = require("../../message/padding");
8
9
  const phash_1 = require("../../message/phash");
10
+ const reporting_token_1 = require("../../message/reporting-token");
9
11
  const _proto_1 = require("../../proto.js");
10
12
  const constants_1 = require("../../protocol/constants");
11
13
  const jid_1 = require("../../protocol/jid");
12
- const constants_2 = require("../../retry/constants");
13
- const outbound_1 = require("../../retry/outbound");
14
+ const jid_2 = require("../../protocol/jid");
14
15
  const binary_1 = require("../../transport/binary");
15
16
  const message_1 = require("../../transport/node/builders/message");
16
17
  const bytes_1 = require("../../util/bytes");
17
18
  const primitives_1 = require("../../util/primitives");
18
- const signal_address_1 = require("../../util/signal-address");
19
19
  class WaMessageDispatchCoordinator {
20
20
  constructor(options) {
21
21
  this.logger = options.logger;
22
22
  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;
23
+ this.retryTracker = options.retryTracker;
24
+ this.sessionResolver = options.sessionResolver;
25
+ this.fanoutResolver = options.fanoutResolver;
26
+ this.participantsCache = options.participantsCache;
27
+ this.appStateSyncKeyProtocol = options.appStateSyncKeyProtocol;
26
28
  this.buildMessageContent = options.buildMessageContent;
27
- this.queryGroupParticipantJids = options.queryGroupParticipantJids;
28
29
  this.senderKeyManager = options.senderKeyManager;
29
30
  this.signalProtocol = options.signalProtocol;
30
- this.signalStore = options.signalStore;
31
- this.signalDeviceSync = options.signalDeviceSync;
32
- this.signalIdentitySync = options.signalIdentitySync;
33
- this.signalSessionSync = options.signalSessionSync;
34
31
  this.getCurrentMeJid = options.getCurrentMeJid;
35
32
  this.getCurrentMeLid = options.getCurrentMeLid;
36
33
  this.getCurrentSignedIdentity = options.getCurrentSignedIdentity;
@@ -46,13 +43,13 @@ class WaMessageDispatchCoordinator {
46
43
  mode: 'opaque_node',
47
44
  node: (0, binary_1.encodeBinaryNode)(node)
48
45
  };
49
- return this.publishWithRetryTracking({
46
+ return this.retryTracker.track({
50
47
  messageIdHint: node.attrs.id,
51
48
  toJid: node.attrs.to,
49
+ type: messageType,
50
+ replayPayload,
52
51
  participantJid: node.attrs.participant,
53
- recipientJid: node.attrs.recipient,
54
- messageType,
55
- replayPayload
52
+ recipientJid: node.attrs.recipient
56
53
  }, async () => this.messageClient.publishNode(node, options));
57
54
  }
58
55
  async publishEncryptedMessage(input, options = {}) {
@@ -69,12 +66,12 @@ class WaMessageDispatchCoordinator {
69
66
  ciphertext: input.ciphertext,
70
67
  participant: input.participant
71
68
  };
72
- return this.publishWithRetryTracking({
69
+ return this.retryTracker.track({
73
70
  messageIdHint: input.id,
74
71
  toJid: input.to,
75
- participantJid: input.participant,
76
- messageType: input.type ?? 'text',
77
- replayPayload
72
+ type: input.type ?? 'text',
73
+ replayPayload,
74
+ participantJid: input.participant
78
75
  }, async () => this.messageClient.publishEncrypted(input, options));
79
76
  }
80
77
  async publishSignalMessage(input, options = {}) {
@@ -89,7 +86,7 @@ class WaMessageDispatchCoordinator {
89
86
  });
90
87
  const [paddedPlaintext] = await Promise.all([
91
88
  (0, padding_1.writeRandomPadMax16)(input.plaintext),
92
- this.ensureSignalSession(address, input.to, input.expectedIdentity)
89
+ this.sessionResolver.ensureSession(address, input.to, input.expectedIdentity)
93
90
  ]);
94
91
  const encrypted = await this.signalProtocol.encryptMessage(address, paddedPlaintext, input.expectedIdentity);
95
92
  const messageType = input.type ?? 'text';
@@ -99,12 +96,12 @@ class WaMessageDispatchCoordinator {
99
96
  type: messageType,
100
97
  plaintext: paddedPlaintext
101
98
  };
102
- return this.publishWithRetryTracking({
99
+ return this.retryTracker.track({
103
100
  messageIdHint: input.id,
104
101
  toJid: input.to,
105
- participantJid: input.participant,
106
- messageType,
107
- replayPayload
102
+ type: messageType,
103
+ replayPayload,
104
+ participantJid: input.participant
108
105
  }, async () => this.messageClient.publishEncrypted({
109
106
  to: input.to,
110
107
  encType: encrypted.type,
@@ -119,286 +116,40 @@ class WaMessageDispatchCoordinator {
119
116
  }
120
117
  async sendMessage(to, content, options = {}) {
121
118
  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);
119
+ const [message, sendOptions] = await Promise.all([
120
+ this.buildMessageContent(content),
121
+ this.withResolvedMessageId(options)
122
+ ]);
123
+ const messageWithSecret = await (0, _message_1.ensureMessageSecret)(message);
124
+ const plaintext = await (0, padding_1.writeRandomPadMax16)(_proto_1.proto.Message.encode(messageWithSecret).finish());
125
+ const type = (0, content_1.resolveMessageTypeAttr)(messageWithSecret);
125
126
  if ((0, jid_1.isGroupJid)(recipientJid)) {
126
- if (this.shouldUseGroupDirectPath(message)) {
127
- return this.publishGroupDirectMessage(recipientJid, plaintext, type, options);
127
+ if (this.shouldUseGroupDirectPath(messageWithSecret)) {
128
+ return this.publishGroupDirectMessage(recipientJid, messageWithSecret, plaintext, type, sendOptions);
128
129
  }
129
- return this.publishGroupSenderKeyMessage(recipientJid, plaintext, type, options);
130
+ return this.publishGroupSenderKeyMessage(recipientJid, messageWithSecret, plaintext, type, sendOptions);
130
131
  }
131
132
  const directRecipientJid = (0, jid_1.toUserJid)(recipientJid);
132
- return this.publishDirectSignalMessageWithFanout(directRecipientJid, message, plaintext, type, options);
133
+ return this.publishDirectSignalMessageWithFanout(directRecipientJid, messageWithSecret, plaintext, type, sendOptions);
133
134
  }
134
135
  async syncSignalSession(jid, reasonIdentity = false) {
135
136
  const address = (0, jid_1.parseSignalAddressFromJid)(jid);
136
137
  if (address.server === constants_1.WA_DEFAULTS.GROUP_SERVER) {
137
138
  throw new Error('syncSignalSession supports only direct chats');
138
139
  }
139
- await this.ensureSignalSession(address, jid, undefined, reasonIdentity);
140
+ await this.sessionResolver.ensureSession(address, jid, undefined, reasonIdentity);
140
141
  }
141
142
  async sendReceipt(input) {
142
143
  await this.messageClient.sendReceipt(input);
143
144
  }
144
145
  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;
146
+ return this.appStateSyncKeyProtocol.requestKeys(keyIds);
171
147
  }
172
148
  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
- });
149
+ await this.appStateSyncKeyProtocol.sendKeyShare(toDeviceJid, keys, missingKeyIds);
205
150
  }
206
151
  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;
152
+ await this.participantsCache.mutateFromGroupEvent(event);
402
153
  }
403
154
  shouldUseGroupDirectPath(message) {
404
155
  const protocolType = message.protocolMessage?.type;
@@ -408,41 +159,50 @@ class WaMessageDispatchCoordinator {
408
159
  }
409
160
  return message.keepInChatMessage?.keepType === _proto_1.proto.KeepType.UNDO_KEEP_FOR_ALL;
410
161
  }
411
- async publishGroupDirectMessage(groupJid, plaintext, type, options, retryContext = {}) {
162
+ async publishGroupDirectMessage(groupJid, message, plaintext, type, options, retryContext = {}) {
163
+ const sendOptions = await this.withResolvedMessageId(options);
412
164
  const meJid = this.requireCurrentMeJid('sendMessage');
413
165
  const participantUserJids = retryContext.forceRefreshParticipants
414
- ? await this.refreshGroupParticipantUsers(groupJid)
415
- : await this.resolveGroupParticipantUsers(groupJid);
166
+ ? await this.participantsCache.refreshParticipantUsers(groupJid)
167
+ : await this.participantsCache.resolveParticipantUsers(groupJid);
416
168
  const addressingMode = retryContext.forceAddressingMode ??
417
169
  this.resolveGroupAddressingMode(participantUserJids, groupJid);
418
170
  const senderForPhash = this.resolveSenderForAddressingMode(addressingMode, meJid);
419
- const fanoutDeviceJids = await this.resolveGroupParticipantDeviceJids(participantUserJids);
171
+ const fanoutDeviceJids = await this.fanoutResolver.resolveGroupParticipantDeviceJids(participantUserJids);
420
172
  if (fanoutDeviceJids.length === 0) {
421
173
  throw new Error('group direct send resolved no target devices');
422
174
  }
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
432
- };
175
+ await this.sessionResolver.ensureSessionsBatch(fanoutDeviceJids);
176
+ const participantAddresses = fanoutDeviceJids.map((targetJid) => (0, jid_1.parseSignalAddressFromJid)(targetJid));
177
+ const encryptedParticipants = await this.signalProtocol.encryptMessagesBatch(participantAddresses.map((address) => ({
178
+ address,
179
+ plaintext
180
+ })));
181
+ const participants = fanoutDeviceJids.map((targetJid, index) => ({
182
+ jid: targetJid,
183
+ encType: encryptedParticipants[index].type,
184
+ ciphertext: encryptedParticipants[index].ciphertext
433
185
  }));
434
186
  const shouldAttachDeviceIdentity = participants.some((participant) => participant.encType === 'pkmsg');
435
187
  const localPhash = await (0, phash_1.computePhashV2)([...fanoutDeviceJids, senderForPhash]);
188
+ const reportingArtifacts = await this.tryBuildReportingTokenArtifacts({
189
+ message,
190
+ stanzaId: sendOptions.id,
191
+ senderUserJid: (0, jid_1.toUserJid)(senderForPhash),
192
+ remoteJid: groupJid,
193
+ context: 'group_direct'
194
+ });
436
195
  const messageNode = (0, message_1.buildGroupDirectMessageNode)({
437
196
  to: groupJid,
438
197
  type,
439
- id: options.id,
198
+ id: sendOptions.id,
440
199
  phash: localPhash,
441
200
  addressingMode,
442
201
  participants,
443
202
  deviceIdentity: shouldAttachDeviceIdentity
444
203
  ? this.getEncodedSignedDeviceIdentity()
445
- : undefined
204
+ : undefined,
205
+ reportingNode: reportingArtifacts?.node ?? undefined
446
206
  });
447
207
  const replayPayload = {
448
208
  mode: 'plaintext',
@@ -450,12 +210,12 @@ class WaMessageDispatchCoordinator {
450
210
  type,
451
211
  plaintext
452
212
  };
453
- const result = await this.publishWithRetryTracking({
454
- messageIdHint: options.id ?? messageNode.attrs.id,
213
+ const result = await this.retryTracker.track({
214
+ messageIdHint: sendOptions.id ?? messageNode.attrs.id,
455
215
  toJid: groupJid,
456
- messageType: type,
216
+ type,
457
217
  replayPayload
458
- }, async () => this.messageClient.publishNode(messageNode, options));
218
+ }, async () => this.messageClient.publishNode(messageNode, sendOptions));
459
219
  const ackError = result.ack.error;
460
220
  const serverPhash = result.ack.phash;
461
221
  const serverAddressingMode = result.ack.addressingMode;
@@ -473,8 +233,8 @@ class WaMessageDispatchCoordinator {
473
233
  serverAddressingMode,
474
234
  ackError
475
235
  });
476
- return this.publishGroupDirectMessage(groupJid, plaintext, type, {
477
- ...options,
236
+ return this.publishGroupDirectMessage(groupJid, message, plaintext, type, {
237
+ ...sendOptions,
478
238
  id: result.id
479
239
  }, {
480
240
  retried: true,
@@ -484,11 +244,12 @@ class WaMessageDispatchCoordinator {
484
244
  }
485
245
  return result;
486
246
  }
487
- async publishGroupSenderKeyMessage(groupJid, plaintext, type, options, retryContext = {}) {
247
+ async publishGroupSenderKeyMessage(groupJid, message, plaintext, type, options, retryContext = {}) {
248
+ const sendOptions = await this.withResolvedMessageId(options);
488
249
  const meJid = this.requireCurrentMeJid('sendMessage');
489
250
  const participantUserJids = retryContext.forceRefreshParticipants
490
- ? await this.refreshGroupParticipantUsers(groupJid)
491
- : await this.resolveGroupParticipantUsers(groupJid);
251
+ ? await this.participantsCache.refreshParticipantUsers(groupJid)
252
+ : await this.participantsCache.resolveParticipantUsers(groupJid);
492
253
  const addressingMode = retryContext.forceAddressingMode ??
493
254
  this.resolveGroupAddressingMode(participantUserJids, groupJid);
494
255
  const senderJid = this.resolveSenderForAddressingMode(addressingMode, meJid);
@@ -499,17 +260,25 @@ class WaMessageDispatchCoordinator {
499
260
  const { fanoutDeviceJids, distributionParticipants } = distributionData;
500
261
  const shouldAttachDeviceIdentity = distributionParticipants.some((participant) => participant.encType === 'pkmsg');
501
262
  const localPhash = await (0, phash_1.computePhashV2)([...fanoutDeviceJids, senderJid]);
263
+ const reportingArtifacts = await this.tryBuildReportingTokenArtifacts({
264
+ message,
265
+ stanzaId: sendOptions.id,
266
+ senderUserJid: (0, jid_1.toUserJid)(senderJid),
267
+ remoteJid: groupJid,
268
+ context: 'group_sender_key'
269
+ });
502
270
  const messageNode = (0, message_1.buildGroupSenderKeyMessageNode)({
503
271
  to: groupJid,
504
272
  type,
505
- id: options.id,
273
+ id: sendOptions.id,
506
274
  phash: localPhash,
507
275
  addressingMode,
508
276
  groupCiphertext: groupCiphertext.ciphertext,
509
277
  participants: distributionParticipants,
510
278
  deviceIdentity: shouldAttachDeviceIdentity
511
279
  ? this.getEncodedSignedDeviceIdentity()
512
- : undefined
280
+ : undefined,
281
+ reportingNode: reportingArtifacts?.node ?? undefined
513
282
  });
514
283
  const replayPayload = {
515
284
  mode: 'plaintext',
@@ -517,12 +286,12 @@ class WaMessageDispatchCoordinator {
517
286
  type,
518
287
  plaintext
519
288
  };
520
- const result = await this.publishWithRetryTracking({
521
- messageIdHint: options.id ?? messageNode.attrs.id,
289
+ const result = await this.retryTracker.track({
290
+ messageIdHint: sendOptions.id ?? messageNode.attrs.id,
522
291
  toJid: groupJid,
523
- messageType: type,
292
+ type,
524
293
  replayPayload
525
- }, async () => this.messageClient.publishNode(messageNode, options));
294
+ }, async () => this.messageClient.publishNode(messageNode, sendOptions));
526
295
  const distributedAddresses = distributionParticipants.map((participant) => participant.address);
527
296
  try {
528
297
  await this.senderKeyManager.markSenderKeyDistributed(groupJid, sender, distributedAddresses);
@@ -551,8 +320,8 @@ class WaMessageDispatchCoordinator {
551
320
  serverAddressingMode,
552
321
  ackError
553
322
  });
554
- return this.publishGroupSenderKeyMessage(groupJid, plaintext, type, {
555
- ...options,
323
+ return this.publishGroupSenderKeyMessage(groupJid, message, plaintext, type, {
324
+ ...sendOptions,
556
325
  id: result.id
557
326
  }, {
558
327
  retried: true,
@@ -562,137 +331,6 @@ class WaMessageDispatchCoordinator {
562
331
  }
563
332
  return result;
564
333
  }
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
334
  resolveGroupAddressingMode(participantUserJids, groupJid) {
697
335
  for (const participantJid of participantUserJids) {
698
336
  try {
@@ -731,111 +369,95 @@ class WaMessageDispatchCoordinator {
731
369
  const distributionPayload = await (0, padding_1.writeRandomPadMax16)(_proto_1.proto.Message.encode({
732
370
  senderKeyDistributionMessage
733
371
  }).finish());
734
- const fanoutDeviceJids = await this.resolveGroupParticipantDeviceJids(participantUserJids);
372
+ const fanoutDeviceJids = await this.fanoutResolver.resolveGroupParticipantDeviceJids(participantUserJids);
735
373
  if (fanoutDeviceJids.length === 0) {
736
374
  return {
737
375
  fanoutDeviceJids,
738
376
  distributionParticipants: []
739
377
  };
740
378
  }
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));
379
+ const fanoutTargetsByAddressKey = new Map();
380
+ const fanoutAddresses = new Array(fanoutDeviceJids.length);
381
+ for (let index = 0; index < fanoutDeviceJids.length; index += 1) {
382
+ const jid = fanoutDeviceJids[index];
383
+ const address = (0, jid_1.parseSignalAddressFromJid)(jid);
384
+ fanoutAddresses[index] = address;
385
+ fanoutTargetsByAddressKey.set((0, jid_2.signalAddressKey)(address), { jid, address });
386
+ }
387
+ const pendingAddresses = await this.senderKeyManager.filterParticipantsNeedingDistribution(groupJid, sender, fanoutAddresses);
746
388
  if (pendingAddresses.length === 0) {
747
389
  return {
748
390
  fanoutDeviceJids,
749
391
  distributionParticipants: []
750
392
  };
751
393
  }
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)));
394
+ const pendingAddressKeys = new Set();
395
+ const pendingTargets = [];
396
+ for (let index = 0; index < pendingAddresses.length; index += 1) {
397
+ const key = (0, jid_2.signalAddressKey)(pendingAddresses[index]);
398
+ if (pendingAddressKeys.has(key)) {
399
+ continue;
400
+ }
401
+ pendingAddressKeys.add(key);
402
+ const target = fanoutTargetsByAddressKey.get(key);
403
+ if (target) {
404
+ pendingTargets.push(target);
405
+ }
406
+ }
754
407
  if (pendingTargets.length === 0) {
755
408
  return {
756
409
  fanoutDeviceJids,
757
410
  distributionParticipants: []
758
411
  };
759
412
  }
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);
764
- return {
765
- jid: target.jid,
766
- address: target.address,
767
- encType: encrypted.type,
768
- ciphertext: encrypted.ciphertext
769
- };
770
- }));
771
- return {
772
- fanoutDeviceJids,
773
- distributionParticipants
774
- };
775
- }
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 [];
413
+ const pendingTargetJids = new Array(pendingTargets.length);
414
+ for (let index = 0; index < pendingTargets.length; index += 1) {
415
+ pendingTargetJids[index] = pendingTargets[index].jid;
805
416
  }
806
417
  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];
418
+ await this.sessionResolver.ensureSessionsBatch(pendingTargetJids);
825
419
  }
826
420
  catch (error) {
827
- this.logger.warn('group participant device sync failed, falling back to participant user jids', {
828
- participants: candidateUsers.length,
421
+ this.logger.warn('group sender-key distribution session sync failed, continuing with available sessions', {
422
+ groupJid,
423
+ requested: pendingTargetJids.length,
829
424
  message: (0, primitives_1.toError)(error).message
830
425
  });
831
- return [...new Set(candidateUsers.map((jid) => (0, jid_1.normalizeDeviceJid)(jid)))].filter((jid) => !meDeviceJids.has(jid));
832
426
  }
427
+ const hasPendingSessions = await this.signalProtocol.hasSessions(pendingTargets.map((target) => target.address));
428
+ const availableTargets = pendingTargets.filter((_target, index) => hasPendingSessions[index]);
429
+ if (availableTargets.length === 0) {
430
+ return {
431
+ fanoutDeviceJids,
432
+ distributionParticipants: []
433
+ };
434
+ }
435
+ const encryptedDistributionParticipants = await this.signalProtocol.encryptMessagesBatch(availableTargets.map((target) => ({
436
+ address: target.address,
437
+ plaintext: distributionPayload
438
+ })));
439
+ const distributionParticipants = availableTargets.map((target, index) => ({
440
+ jid: target.jid,
441
+ address: target.address,
442
+ encType: encryptedDistributionParticipants[index].type,
443
+ ciphertext: encryptedDistributionParticipants[index].ciphertext
444
+ }));
445
+ return {
446
+ fanoutDeviceJids,
447
+ distributionParticipants
448
+ };
833
449
  }
834
450
  async publishDirectSignalMessageWithFanout(recipientJid, message, plaintext, type, options) {
451
+ const sendOptions = await this.withResolvedMessageId(options);
835
452
  const meJid = this.requireCurrentMeJid('sendMessage');
836
453
  const meLid = this.getCurrentMeLid();
837
- const selfDeviceJidForRecipient = this.resolveSelfDeviceJidForRecipient(recipientJid, meJid, meLid);
838
- const deviceJids = await this.resolveDirectFanoutDeviceJids(recipientJid, selfDeviceJidForRecipient);
454
+ const selfDeviceJidForRecipient = this.fanoutResolver.resolveSelfDeviceJidForRecipient(recipientJid, meJid, meLid);
455
+ const deviceJids = await this.fanoutResolver.resolveDirectFanoutDeviceJids(recipientJid, selfDeviceJidForRecipient);
456
+ const targets = deviceJids.map((jid) => ({
457
+ jid,
458
+ normalizedJid: (0, jid_1.normalizeDeviceJid)(jid),
459
+ userJid: (0, jid_1.toUserJid)(jid)
460
+ }));
839
461
  const recipientUserJid = (0, jid_1.toUserJid)(recipientJid);
840
462
  const meUserJid = (0, jid_1.toUserJid)(selfDeviceJidForRecipient);
841
463
  this.logger.debug('wa client publish signal fanout', {
@@ -844,44 +466,55 @@ class WaMessageDispatchCoordinator {
844
466
  type
845
467
  });
846
468
  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);
469
+ if (sendOptions.expectedIdentity) {
470
+ for (let index = 0; index < targets.length; index += 1) {
471
+ const target = targets[index];
472
+ if (target.userJid === recipientUserJid) {
473
+ expectedIdentityByJid.set(target.normalizedJid, sendOptions.expectedIdentity);
852
474
  }
853
475
  }
854
476
  }
855
- await this.ensureSignalSessionsBatch(deviceJids, expectedIdentityByJid);
856
- const hasSelfDeviceFanout = deviceJids.some((targetJid) => (0, jid_1.toUserJid)(targetJid) === meUserJid);
477
+ await this.sessionResolver.ensureSessionsBatch(deviceJids, expectedIdentityByJid);
478
+ const hasSelfDeviceFanout = targets.some((target) => target.userJid === meUserJid);
857
479
  const selfDevicePlaintext = hasSelfDeviceFanout
858
480
  ? await (0, padding_1.writeRandomPadMax16)(_proto_1.proto.Message.encode((0, device_sent_1.wrapDeviceSentMessage)(message, recipientUserJid)).finish())
859
481
  : 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
482
+ const participantRequests = targets.map((target) => ({
483
+ target,
484
+ address: (0, jid_1.parseSignalAddressFromJid)(target.jid),
485
+ expectedIdentity: target.userJid === recipientUserJid ? sendOptions.expectedIdentity : undefined,
486
+ plaintext: selfDevicePlaintext && target.userJid === meUserJid
865
487
  ? 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
873
- };
488
+ : plaintext
489
+ }));
490
+ const encryptedParticipants = await this.signalProtocol.encryptMessagesBatch(participantRequests.map((request) => ({
491
+ address: request.address,
492
+ plaintext: request.plaintext,
493
+ expectedIdentity: request.expectedIdentity
494
+ })));
495
+ const participants = participantRequests.map((request, index) => ({
496
+ jid: request.target.jid,
497
+ encType: encryptedParticipants[index].type,
498
+ ciphertext: encryptedParticipants[index].ciphertext
874
499
  }));
875
500
  const shouldAttachDeviceIdentity = participants.some((participant) => participant.encType === 'pkmsg');
876
501
  const deviceIdentity = shouldAttachDeviceIdentity
877
502
  ? this.getEncodedSignedDeviceIdentity()
878
503
  : undefined;
504
+ const reportingArtifacts = await this.tryBuildReportingTokenArtifacts({
505
+ message,
506
+ stanzaId: sendOptions.id,
507
+ senderUserJid: meUserJid,
508
+ remoteJid: recipientUserJid,
509
+ context: 'direct_fanout'
510
+ });
879
511
  const messageNode = (0, message_1.buildDirectMessageFanoutNode)({
880
512
  to: recipientJid,
881
513
  type,
882
- id: options.id,
514
+ id: sendOptions.id,
883
515
  participants,
884
- deviceIdentity
516
+ deviceIdentity,
517
+ reportingNode: reportingArtifacts?.node ?? undefined
885
518
  });
886
519
  const replayPayload = {
887
520
  mode: 'plaintext',
@@ -889,130 +522,72 @@ class WaMessageDispatchCoordinator {
889
522
  type,
890
523
  plaintext
891
524
  };
892
- return this.publishWithRetryTracking({
893
- messageIdHint: options.id ?? messageNode.attrs.id,
525
+ return this.retryTracker.track({
526
+ messageIdHint: sendOptions.id ?? messageNode.attrs.id,
894
527
  toJid: recipientJid,
895
- messageType: type,
528
+ type,
896
529
  replayPayload
897
- }, async () => this.messageClient.publishNode(messageNode, options));
530
+ }, async () => this.messageClient.publishNode(messageNode, sendOptions));
898
531
  }
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;
532
+ async withResolvedMessageId(options) {
533
+ const normalizedId = options.id?.trim();
534
+ if (normalizedId) {
535
+ if (normalizedId === options.id) {
536
+ return options;
537
+ }
538
+ return {
539
+ ...options,
540
+ id: normalizedId
541
+ };
912
542
  }
543
+ return {
544
+ ...options,
545
+ id: await this.generateOutgoingMessageId()
546
+ };
547
+ }
548
+ async generateOutgoingMessageId() {
913
549
  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
- }
550
+ const meUserJid = (0, jid_1.toUserJid)(this.requireCurrentMeJid('sendMessage'));
551
+ const timestampSeconds = Math.floor(Date.now() / 1000);
552
+ const timestampBytes = new Uint8Array(8);
553
+ new DataView(timestampBytes.buffer, timestampBytes.byteOffset, timestampBytes.byteLength).setBigUint64(0, BigInt(timestampSeconds), false);
554
+ const entropy = (0, bytes_1.concatBytes)([
555
+ timestampBytes,
556
+ bytes_1.TEXT_ENCODER.encode(meUserJid),
557
+ await (0, _crypto_1.randomBytesAsync)(8)
558
+ ]);
559
+ const digest = await (0, _crypto_1.sha256)(entropy);
560
+ return `3EB0${(0, bytes_1.bytesToHex)(digest.subarray(0, 9)).toUpperCase()}`;
954
561
  }
955
562
  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
563
+ this.logger.warn('failed to generate sha256 message id, falling back to random id', {
564
+ message: (0, primitives_1.toError)(error).message
963
565
  });
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
- }
566
+ return `3EB0${(0, bytes_1.bytesToHex)(await (0, _crypto_1.randomBytesAsync)(8)).toUpperCase()}`;
968
567
  }
969
568
  }
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];
569
+ async tryBuildReportingTokenArtifacts(input) {
570
+ if (!input.stanzaId) {
571
+ return null;
572
+ }
974
573
  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];
574
+ return await (0, reporting_token_1.buildReportingTokenArtifacts)({
575
+ message: input.message,
576
+ stanzaId: input.stanzaId,
577
+ senderUserJid: input.senderUserJid,
578
+ remoteJid: input.remoteJid
579
+ });
997
580
  }
998
581
  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
582
+ this.logger.warn('failed to generate reporting token', {
583
+ context: input.context,
584
+ id: input.stanzaId,
585
+ remoteJid: input.remoteJid,
586
+ message: (0, primitives_1.toError)(error).message
1003
587
  });
1004
- return [recipientUserJid];
588
+ return null;
1005
589
  }
1006
590
  }
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
591
  getEncodedSignedDeviceIdentity() {
1017
592
  const signedIdentity = this.getCurrentSignedIdentity();
1018
593
  if (!signedIdentity) {
@@ -1020,36 +595,6 @@ class WaMessageDispatchCoordinator {
1020
595
  }
1021
596
  return _proto_1.proto.ADVSignedDeviceIdentity.encode(signedIdentity).finish();
1022
597
  }
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');
1041
- }
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
- });
1052
- }
1053
598
  requireCurrentMeJid(context) {
1054
599
  const meJid = this.getCurrentMeJid();
1055
600
  if (meJid) {