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,6 +1,6 @@
1
1
  import { proto } from '../../proto.js';
2
2
  import { WA_MESSAGE_TAGS } from '../../protocol/constants.js';
3
- import { isGroupOrBroadcastJid, normalizeDeviceJid, parseSignalAddressFromJid, toUserJid } from '../../protocol/jid.js';
3
+ import { isGroupOrBroadcastJid, normalizeDeviceJid, parseSignalAddressFromJid } from '../../protocol/jid.js';
4
4
  import { MAX_RETRY_ATTEMPTS, RETRY_KEYS_MIN_COUNT, RETRY_OUTBOUND_TTL_MS, RETRY_REASON } from '../../retry/constants.js';
5
5
  import { pickRetryStateMax } from '../../retry/outbound.js';
6
6
  import { parseRetryReceiptRequest } from '../../retry/parse.js';
@@ -10,12 +10,16 @@ import { generatePreKeyPair } from '../../signal/registration/keygen.js';
10
10
  import { buildInboundRetryReceiptAckNode } from '../../transport/node/builders/message.js';
11
11
  import { buildRetryReceiptNode } from '../../transport/node/builders/retry.js';
12
12
  import { toError } from '../../util/primitives.js';
13
+ const RETRY_CLEANUP_INTERVAL_MS = 30000;
13
14
  function getRetryReasonName(code) {
14
15
  if (code === undefined) {
15
16
  return undefined;
16
17
  }
17
- const entry = Object.entries(RETRY_REASON).find(([, value]) => value === code);
18
- return entry ? entry[0] : 'unknown';
18
+ for (const reasonName in RETRY_REASON) {
19
+ if (RETRY_REASON[reasonName] === code)
20
+ return reasonName;
21
+ }
22
+ return 'unknown';
19
23
  }
20
24
  function getRemoteRetryReasonLogFields(reason) {
21
25
  return {
@@ -26,6 +30,7 @@ function getRemoteRetryReasonLogFields(reason) {
26
30
  }
27
31
  export class WaRetryCoordinator {
28
32
  constructor(options) {
33
+ this.nextRetryCleanupAtMs = 0;
29
34
  this.logger = options.logger;
30
35
  this.retryStore = options.retryStore;
31
36
  this.retryTtlMs = this.retryStore.getTtlMs?.() ?? RETRY_OUTBOUND_TTL_MS;
@@ -72,7 +77,7 @@ export class WaRetryCoordinator {
72
77
  return;
73
78
  }
74
79
  try {
75
- await this.retryStore.cleanupExpired(Date.now());
80
+ await this.maybeCleanupRetryStore(Date.now());
76
81
  const request = parseRetryReceiptRequest(receiptNode);
77
82
  if (!request) {
78
83
  return;
@@ -97,10 +102,7 @@ export class WaRetryCoordinator {
97
102
  }
98
103
  async prepareDecryptFailureRetry(context, error) {
99
104
  const nowMs = Date.now();
100
- const [, registrationInfo] = await Promise.all([
101
- this.retryStore.cleanupExpired(nowMs),
102
- this.signalStore.getRegistrationInfo()
103
- ]);
105
+ const registrationInfo = await this.signalStore.getRegistrationInfo();
104
106
  if (!registrationInfo) {
105
107
  this.logger.warn('retry receipt skipped: missing local registration info', {
106
108
  id: context.stanzaId,
@@ -195,6 +197,21 @@ export class WaRetryCoordinator {
195
197
  });
196
198
  return null;
197
199
  }
200
+ let requesterAddress;
201
+ let requesterNormalizedDeviceJid;
202
+ try {
203
+ requesterAddress = parseSignalAddressFromJid(requesterJid);
204
+ requesterNormalizedDeviceJid = normalizeDeviceJid(requesterJid);
205
+ }
206
+ catch (error) {
207
+ this.logger.info('retry request rejected: invalid requester jid', {
208
+ id: request.stanzaId,
209
+ originalMsgId: request.originalMsgId,
210
+ requester: requesterJid,
211
+ message: toError(error).message
212
+ });
213
+ return null;
214
+ }
198
215
  if (request.retryCount >= MAX_RETRY_ATTEMPTS) {
199
216
  this.logger.info('retry request rejected: retry count exceeded', {
200
217
  id: request.stanzaId,
@@ -214,7 +231,7 @@ export class WaRetryCoordinator {
214
231
  });
215
232
  return null;
216
233
  }
217
- const sessionReady = await this.updateLocalSessionFromRetryRequest(request, requesterJid);
234
+ const sessionReady = await this.updateLocalSessionFromRetryRequest(request, requesterJid, requesterAddress, requesterNormalizedDeviceJid);
218
235
  if (!sessionReady) {
219
236
  this.logger.info('retry request rejected: missing compatible session', {
220
237
  id: request.stanzaId,
@@ -225,7 +242,7 @@ export class WaRetryCoordinator {
225
242
  });
226
243
  return null;
227
244
  }
228
- const authorization = await this.authorizeRetryRequest(request, outbound, requesterJid);
245
+ const authorization = await this.authorizeRetryRequest(request, outbound, requesterJid, requesterAddress, requesterNormalizedDeviceJid);
229
246
  if (!authorization.authorized) {
230
247
  this.logger.info('retry request rejected', {
231
248
  id: request.stanzaId,
@@ -239,6 +256,8 @@ export class WaRetryCoordinator {
239
256
  }
240
257
  return {
241
258
  requesterJid,
259
+ requesterAddress,
260
+ requesterNormalizedDeviceJid,
242
261
  outbound
243
262
  };
244
263
  }
@@ -271,7 +290,7 @@ export class WaRetryCoordinator {
271
290
  }
272
291
  async runRetryTaskSerialized(messageId, task) {
273
292
  const previous = this.retryProcessingByMessageId.get(messageId) ?? Promise.resolve();
274
- const current = previous.catch(() => undefined).then(async () => task());
293
+ const current = previous.then(task, task);
275
294
  const tracker = current.then(() => undefined, () => undefined);
276
295
  this.retryProcessingByMessageId.set(messageId, tracker);
277
296
  try {
@@ -311,18 +330,17 @@ export class WaRetryCoordinator {
311
330
  : undefined
312
331
  };
313
332
  }
314
- async updateLocalSessionFromRetryRequest(request, requesterJid) {
315
- await this.markRetryRequesterSenderKeyAsStale(request, requesterJid);
316
- const address = parseSignalAddressFromJid(requesterJid);
317
- const currentSession = await this.signalStore.getSession(address);
333
+ async updateLocalSessionFromRetryRequest(request, requesterJid, requesterAddress, requesterNormalizedDeviceJid) {
334
+ await this.markRetryRequesterSenderKeyAsStale(request, requesterJid, requesterAddress);
335
+ const currentSession = await this.signalStore.getSession(requesterAddress);
318
336
  if (currentSession && request.regId > 0 && currentSession.remote.regId !== request.regId) {
319
- await this.signalStore.deleteSession(address);
337
+ await this.signalStore.deleteSession(requesterAddress);
320
338
  }
321
339
  if (request.keyBundle) {
322
- if (!request.keyBundle.key) {
340
+ if (!request.keyBundle.key || !request.keyBundle.skey.signature) {
323
341
  return false;
324
342
  }
325
- await this.signalProtocol.establishOutgoingSession(address, {
343
+ await this.signalProtocol.establishOutgoingSession(requesterAddress, {
326
344
  regId: request.regId,
327
345
  identity: request.keyBundle.identity,
328
346
  signedKey: {
@@ -337,23 +355,22 @@ export class WaRetryCoordinator {
337
355
  });
338
356
  return true;
339
357
  }
340
- const hasSession = await this.signalProtocol.hasSession(address);
358
+ const hasSession = await this.signalProtocol.hasSession(requesterAddress);
341
359
  if (hasSession) {
342
360
  return true;
343
361
  }
344
- const fetched = await this.fetchMissingPreKeysSession(requesterJid, request.regId);
362
+ const fetched = await this.fetchMissingPreKeysSession(requesterJid, requesterAddress, requesterNormalizedDeviceJid, request.regId);
345
363
  if (!fetched) {
346
364
  return false;
347
365
  }
348
- await this.signalProtocol.establishOutgoingSession(address, fetched);
366
+ await this.signalProtocol.establishOutgoingSession(requesterAddress, fetched);
349
367
  return true;
350
368
  }
351
- async markRetryRequesterSenderKeyAsStale(request, requesterJid) {
369
+ async markRetryRequesterSenderKeyAsStale(request, requesterJid, requesterAddress) {
352
370
  if (!isGroupOrBroadcastJid(request.from)) {
353
371
  return;
354
372
  }
355
373
  try {
356
- const requesterAddress = parseSignalAddressFromJid(requesterJid);
357
374
  const deleted = await this.senderKeyStore.markForgetSenderKey(request.from, [
358
375
  requesterAddress
359
376
  ]);
@@ -371,12 +388,11 @@ export class WaRetryCoordinator {
371
388
  });
372
389
  }
373
390
  }
374
- async fetchMissingPreKeysSession(requesterJid, requesterRegistrationId) {
391
+ async fetchMissingPreKeysSession(requesterJid, requesterAddress, requesterNormalizedDeviceJid, requesterRegistrationId) {
375
392
  try {
376
- const requesterAddress = parseSignalAddressFromJid(requesterJid);
377
393
  const results = await this.signalMissingPreKeysSync.fetchMissingPreKeys([
378
394
  {
379
- userJid: toUserJid(requesterJid),
395
+ userJid: `${requesterAddress.user}@${requesterAddress.server}`,
380
396
  devices: [
381
397
  {
382
398
  deviceId: requesterAddress.device,
@@ -393,8 +409,7 @@ export class WaRetryCoordinator {
393
409
  });
394
410
  return null;
395
411
  }
396
- const requesterDeviceJid = normalizeDeviceJid(requesterJid);
397
- const matched = first.devices.find((device) => normalizeDeviceJid(device.deviceJid) === requesterDeviceJid);
412
+ const matched = first.devices.find((device) => normalizeDeviceJid(device.deviceJid) === requesterNormalizedDeviceJid);
398
413
  if (!matched) {
399
414
  this.logger.warn('missing prekeys fetch did not return requested device', {
400
415
  requester: requesterJid,
@@ -412,7 +427,7 @@ export class WaRetryCoordinator {
412
427
  return null;
413
428
  }
414
429
  }
415
- async authorizeRetryRequest(request, outbound, requesterJid) {
430
+ async authorizeRetryRequest(request, outbound, requesterJid, requesterAddress, requesterNormalizedDeviceJid) {
416
431
  if (outbound.state === 'ineligible') {
417
432
  return { authorized: false, reason: `state_${outbound.state}` };
418
433
  }
@@ -420,25 +435,29 @@ export class WaRetryCoordinator {
420
435
  if (!isGroupOutbound && (outbound.state === 'read' || outbound.state === 'played')) {
421
436
  return { authorized: false, reason: `state_${outbound.state}` };
422
437
  }
423
- const requesterAuthorized = await this.isRequesterAuthorizedDevice(requesterJid);
438
+ const requesterAuthorized = await this.isRequesterAuthorizedDevice(requesterJid, requesterAddress, requesterNormalizedDeviceJid);
424
439
  if (!requesterAuthorized) {
425
440
  return { authorized: false, reason: 'requester_device_not_authorized' };
426
441
  }
427
442
  return { authorized: true };
428
443
  }
429
- async isRequesterAuthorizedDevice(requesterJid) {
444
+ async isRequesterAuthorizedDevice(requesterJid, requesterAddress, requesterNormalizedDeviceJid) {
430
445
  try {
431
- const requesterUser = toUserJid(requesterJid);
446
+ const requesterUser = `${requesterAddress.user}@${requesterAddress.server}`;
447
+ if (requesterNormalizedDeviceJid === normalizeDeviceJid(requesterUser)) {
448
+ return true;
449
+ }
432
450
  const synced = await this.signalDeviceSync.syncDeviceList([requesterUser]);
433
451
  const target = synced.find((entry) => entry.jid === requesterUser);
434
- const authorized = new Set();
435
- authorized.add(normalizeDeviceJid(requesterUser));
436
- if (target) {
437
- for (let index = 0; index < target.deviceJids.length; index += 1) {
438
- authorized.add(normalizeDeviceJid(target.deviceJids[index]));
452
+ if (!target) {
453
+ return false;
454
+ }
455
+ for (let index = 0; index < target.deviceJids.length; index += 1) {
456
+ if (normalizeDeviceJid(target.deviceJids[index]) === requesterNormalizedDeviceJid) {
457
+ return true;
439
458
  }
440
459
  }
441
- return authorized.has(normalizeDeviceJid(requesterJid));
460
+ return false;
442
461
  }
443
462
  catch (error) {
444
463
  this.logger.warn('retry authorization failed while syncing requester device list', {
@@ -487,4 +506,18 @@ export class WaRetryCoordinator {
487
506
  });
488
507
  }
489
508
  }
509
+ async maybeCleanupRetryStore(nowMs) {
510
+ if (nowMs < this.nextRetryCleanupAtMs) {
511
+ return;
512
+ }
513
+ this.nextRetryCleanupAtMs = nowMs + RETRY_CLEANUP_INTERVAL_MS;
514
+ try {
515
+ await this.retryStore.cleanupExpired(nowMs);
516
+ }
517
+ catch (error) {
518
+ this.logger.warn('retry store cleanup failed', {
519
+ message: toError(error).message
520
+ });
521
+ }
522
+ }
490
523
  }
@@ -1,10 +1,8 @@
1
- import { randomBytesAsync } from '../crypto/index.js';
2
1
  import { WA_ACCOUNT_SYNC_PROTOCOLS, WA_DEFAULTS, WA_DIRTY_PROTOCOLS, WA_DIRTY_TYPES, WA_IQ_TYPES, WA_SUPPORTED_DIRTY_TYPES } from '../protocol/constants.js';
3
2
  import { toUserJid } from '../protocol/jid.js';
4
- import { buildAccountBlocklistSyncIq, buildAccountDevicesSyncIq, buildAccountPictureSyncIq, buildAccountPrivacySyncIq, buildClearDirtyBitsIq, buildGroupsDirtySyncIq, buildNewsletterMetadataSyncIq } from '../transport/node/builders/accountSync.js';
5
- import { getNodeChildren } from '../transport/node/helpers.js';
3
+ import { buildAccountBlocklistSyncIq, buildAccountDevicesSyncIq, buildAccountPictureSyncIq, buildAccountPrivacySyncIq, buildClearDirtyBitsIq, buildGroupsDirtySyncIq, buildNewsletterMetadataSyncIq } from '../transport/node/builders/account-sync.js';
4
+ import { getNodeChildrenTags } from '../transport/node/helpers.js';
6
5
  import { assertIqResult, parseIqError } from '../transport/node/query.js';
7
- import { bytesToHex } from '../util/bytes.js';
8
6
  import { toError } from '../util/primitives.js';
9
7
  const SUPPORTED_DIRTY_TYPES = new Set(WA_SUPPORTED_DIRTY_TYPES);
10
8
  const ACCOUNT_SYNC_PROTOCOL_SET = new Set(WA_ACCOUNT_SYNC_PROTOCOLS);
@@ -18,19 +16,13 @@ function parseDirtyBitNode(node, logger) {
18
16
  });
19
17
  return null;
20
18
  }
21
- const protocols = getNodeChildren(node).map((child) => child.tag);
19
+ const protocols = getNodeChildrenTags(node);
22
20
  return {
23
21
  type,
24
22
  timestamp,
25
23
  protocols
26
24
  };
27
25
  }
28
- function splitDirtyBitsBySupport(dirtyBits) {
29
- return {
30
- supported: dirtyBits.filter((dirtyBit) => SUPPORTED_DIRTY_TYPES.has(dirtyBit.type)),
31
- unsupported: dirtyBits.filter((dirtyBit) => !SUPPORTED_DIRTY_TYPES.has(dirtyBit.type))
32
- };
33
- }
34
26
  function resolveAccountSyncProtocols(protocols) {
35
27
  const selected = protocols.filter((protocol) => ACCOUNT_SYNC_PROTOCOL_SET.has(protocol));
36
28
  if (selected.length > 0) {
@@ -39,9 +31,15 @@ function resolveAccountSyncProtocols(protocols) {
39
31
  return WA_ACCOUNT_SYNC_PROTOCOLS;
40
32
  }
41
33
  export function parseDirtyBits(nodes, logger) {
42
- return nodes
43
- .map((node) => parseDirtyBitNode(node, logger))
44
- .filter((dirtyBit) => dirtyBit !== null);
34
+ const parsed = [];
35
+ for (const node of nodes) {
36
+ const dirtyBit = parseDirtyBitNode(node, logger);
37
+ if (!dirtyBit) {
38
+ continue;
39
+ }
40
+ parsed.push(dirtyBit);
41
+ }
42
+ return parsed;
45
43
  }
46
44
  export async function handleDirtyBits(runtime, dirtyBits) {
47
45
  const meJid = runtime.getCurrentCredentials()?.meJid ?? null;
@@ -49,22 +47,33 @@ export async function handleDirtyBits(runtime, dirtyBits) {
49
47
  runtime.logger.trace('dirty bits skipped: session is not registered');
50
48
  return;
51
49
  }
52
- const { supported, unsupported } = splitDirtyBitsBySupport(dirtyBits);
50
+ const supported = [];
51
+ const unsupported = [];
52
+ for (const dirtyBit of dirtyBits) {
53
+ if (SUPPORTED_DIRTY_TYPES.has(dirtyBit.type)) {
54
+ supported.push(dirtyBit);
55
+ continue;
56
+ }
57
+ unsupported.push(dirtyBit);
58
+ }
53
59
  runtime.logger.info('handling dirty bits from info bulletin', {
54
60
  supported: supported.map((entry) => entry.type).join(','),
55
61
  unsupported: unsupported.map((entry) => entry.type).join(',')
56
62
  });
57
- const handledSupported = (await Promise.allSettled(supported.map(async (dirtyBit) => handleDirtyBit(runtime, dirtyBit)))).flatMap((result, index) => {
63
+ const clearableDirtyBits = [...unsupported];
64
+ const settledSupported = await Promise.allSettled(supported.map(async (dirtyBit) => handleDirtyBit(runtime, dirtyBit)));
65
+ for (let index = 0; index < settledSupported.length; index += 1) {
66
+ const result = settledSupported[index];
58
67
  if (result.status === 'fulfilled') {
59
- return [supported[index]];
68
+ clearableDirtyBits.push(supported[index]);
69
+ continue;
60
70
  }
61
71
  runtime.logger.warn('failed handling dirty bit', {
62
72
  type: supported[index].type,
63
73
  message: toError(result.reason).message
64
74
  });
65
- return [];
66
- });
67
- await clearDirtyBits(runtime, unsupported.concat(handledSupported));
75
+ }
76
+ await clearDirtyBits(runtime, clearableDirtyBits);
68
77
  }
69
78
  async function handleDirtyBit(runtime, dirtyBit) {
70
79
  switch (dirtyBit.type) {
@@ -149,7 +158,7 @@ async function syncAccountDevicesDirtyBit(runtime) {
149
158
  }
150
159
  await runSyncQuery(runtime, {
151
160
  queryContext: 'account_sync.devices',
152
- node: buildAccountDevicesSyncIq(userJids, await generateUsyncSid()),
161
+ node: buildAccountDevicesSyncIq(userJids, await runtime.generateUsyncSid()),
153
162
  logMessage: 'account_sync devices synchronized',
154
163
  contextData: { meJid, targets: userJids.join(',') }
155
164
  });
@@ -213,9 +222,6 @@ async function syncNewsletterMetadataDirtyBit(runtime) {
213
222
  runtime.logger.info('newsletter_metadata dirty bit received (GraphQL/MEX sync intentionally disabled)');
214
223
  await runtime.queryWithContext('dirty.newsletter_metadata', buildNewsletterMetadataSyncIq(), WA_DEFAULTS.IQ_TIMEOUT_MS);
215
224
  }
216
- async function generateUsyncSid() {
217
- return bytesToHex(await randomBytesAsync(8));
218
- }
219
225
  function resolveAccountSyncDeviceTargets(credentials) {
220
226
  if (!credentials?.meJid) {
221
227
  return [];
@@ -75,8 +75,7 @@ export function parseChatEventFromAppStateMutation(mutation) {
75
75
  ...pickOptionalPrimitive('deviceAgentId', value.chatAssignment.deviceAgentID, 'string')
76
76
  };
77
77
  }
78
- const fallbackAction = normalizeIndexAction(parsedIndex?.action) ??
79
- normalizeValueActionKey(syncActionValueKey);
78
+ const fallbackAction = normalizeIndexAction(parsedIndex?.action) ?? normalizeValueActionKey(syncActionValueKey);
80
79
  if (!fallbackAction) {
81
80
  return null;
82
81
  }
@@ -201,10 +200,12 @@ function extractChatJid(parts) {
201
200
  return undefined;
202
201
  }
203
202
  function findPresentSyncActionValueKey(value) {
204
- for (const [key, keyValue] of Object.entries(value)) {
203
+ const valueRecord = value;
204
+ for (const key in valueRecord) {
205
205
  if (key === 'timestamp') {
206
206
  continue;
207
207
  }
208
+ const keyValue = valueRecord[key];
208
209
  if (keyValue === null || keyValue === undefined) {
209
210
  continue;
210
211
  }
@@ -1,8 +1,8 @@
1
1
  import { WA_GROUP_NOTIFICATION_TAGS, WA_NOTIFICATION_TYPES } from '../../protocol/constants.js';
2
2
  import { WA_NODE_TAGS } from '../../protocol/nodes.js';
3
3
  import { findNodeChild, getNodeChildren, getNodeChildrenByTag } from '../../transport/node/helpers.js';
4
- import { parseOptionalInt } from '../../transport/stream/parse.js';
5
4
  import { TEXT_DECODER } from '../../util/bytes.js';
5
+ import { parseOptionalInt } from '../../util/primitives.js';
6
6
  function readNodeTextContent(node) {
7
7
  if (!node) {
8
8
  return undefined;
@@ -15,31 +15,43 @@ function readNodeTextContent(node) {
15
15
  }
16
16
  return undefined;
17
17
  }
18
- function parseParticipants(node) {
19
- return getNodeChildrenByTag(node, WA_NODE_TAGS.PARTICIPANT).map((participantNode) => ({
20
- jid: participantNode.attrs.jid,
21
- role: participantNode.attrs.type,
22
- lidJid: participantNode.attrs.lid,
23
- phoneJid: participantNode.attrs.phone_number,
24
- displayName: participantNode.attrs.display_name,
25
- username: participantNode.attrs.username,
26
- expirationSeconds: parseOptionalInt(participantNode.attrs.expiration)
27
- }));
18
+ export function parseParticipants(node) {
19
+ const participants = [];
20
+ for (const participantNode of getNodeChildrenByTag(node, WA_NODE_TAGS.PARTICIPANT)) {
21
+ participants.push({
22
+ jid: participantNode.attrs.jid,
23
+ role: participantNode.attrs.type,
24
+ lidJid: participantNode.attrs.lid,
25
+ phoneJid: participantNode.attrs.phone_number,
26
+ displayName: participantNode.attrs.display_name,
27
+ username: participantNode.attrs.username,
28
+ expirationSeconds: parseOptionalInt(participantNode.attrs.expiration)
29
+ });
30
+ }
31
+ return participants;
28
32
  }
29
33
  function parseLinkedGroups(node) {
30
- return getNodeChildrenByTag(node, WA_NODE_TAGS.GROUP).map((groupNode) => ({
31
- jid: groupNode.attrs.jid,
32
- subject: groupNode.attrs.subject,
33
- subjectTimestampSeconds: parseOptionalInt(groupNode.attrs.s_t),
34
- hiddenSubgroup: findNodeChild(groupNode, 'hidden_group') !== undefined
35
- }));
34
+ const groups = [];
35
+ for (const groupNode of getNodeChildrenByTag(node, WA_NODE_TAGS.GROUP)) {
36
+ groups.push({
37
+ jid: groupNode.attrs.jid,
38
+ subject: groupNode.attrs.subject,
39
+ subjectTimestampSeconds: parseOptionalInt(groupNode.attrs.s_t),
40
+ hiddenSubgroup: findNodeChild(groupNode, 'hidden_group') !== undefined
41
+ });
42
+ }
43
+ return groups;
36
44
  }
37
45
  function parseMembershipRequests(node) {
38
- return getNodeChildrenByTag(node, 'requested_user').map((requestNode) => ({
39
- jid: requestNode.attrs.jid,
40
- username: requestNode.attrs.username,
41
- phoneJid: requestNode.attrs.phone_number
42
- }));
46
+ const requests = [];
47
+ for (const requestNode of getNodeChildrenByTag(node, 'requested_user')) {
48
+ requests.push({
49
+ jid: requestNode.attrs.jid,
50
+ username: requestNode.attrs.username,
51
+ phoneJid: requestNode.attrs.phone_number
52
+ });
53
+ }
54
+ return requests;
43
55
  }
44
56
  function parseSubgroupSuggestion(node) {
45
57
  const subjectNode = findNodeChild(node, WA_NODE_TAGS.SUBJECT);
@@ -65,9 +77,15 @@ function parseSubgroupSuggestion(node) {
65
77
  };
66
78
  }
67
79
  function parseSubgroupSuggestions(node) {
68
- return getNodeChildrenByTag(node, 'sub_group_suggestion')
69
- .map(parseSubgroupSuggestion)
70
- .filter((suggestion) => suggestion !== null);
80
+ const suggestions = [];
81
+ for (const suggestionNode of getNodeChildrenByTag(node, 'sub_group_suggestion')) {
82
+ const suggestion = parseSubgroupSuggestion(suggestionNode);
83
+ if (!suggestion) {
84
+ continue;
85
+ }
86
+ suggestions.push(suggestion);
87
+ }
88
+ return suggestions;
71
89
  }
72
90
  function createBaseGroupEvent(notificationNode, actionNode) {
73
91
  return {
@@ -136,8 +154,7 @@ function parseCreateGroupAction(notificationNode, actionNode) {
136
154
  ephemeralDuration: parseOptionalInt(findNodeChild(groupNode, WA_NODE_TAGS.EPHEMERAL)?.attrs.expiration),
137
155
  disappearingTrigger: findNodeChild(groupNode, WA_NODE_TAGS.EPHEMERAL)?.attrs.trigger,
138
156
  membershipApprovalEnabled: groupJoinNode?.attrs.state === 'on',
139
- allowNonAdminSubGroupCreation: findNodeChild(groupNode, WA_GROUP_NOTIFICATION_TAGS.ALLOW_NON_ADMIN_SUB_GROUP_CREATION) !==
140
- undefined,
157
+ allowNonAdminSubGroupCreation: findNodeChild(groupNode, WA_GROUP_NOTIFICATION_TAGS.ALLOW_NON_ADMIN_SUB_GROUP_CREATION) !== undefined,
141
158
  linkedParentGroupJid: findNodeChild(groupNode, 'linked_parent')?.attrs.jid ??
142
159
  findNodeChild(groupNode, 'parent')?.attrs.jid
143
160
  }
@@ -224,7 +241,12 @@ function parseGroupActionNode(notificationNode, actionNode) {
224
241
  };
225
242
  }
226
243
  case WA_GROUP_NOTIFICATION_TAGS.LOCKED:
227
- return { ...baseEvent, action: 'restrict', enabled: true, mode: actionNode.attrs.threshold };
244
+ return {
245
+ ...baseEvent,
246
+ action: 'restrict',
247
+ enabled: true,
248
+ mode: actionNode.attrs.threshold
249
+ };
228
250
  case WA_GROUP_NOTIFICATION_TAGS.UNLOCKED:
229
251
  return { ...baseEvent, action: 'restrict', enabled: false };
230
252
  case WA_GROUP_NOTIFICATION_TAGS.ANNOUNCEMENT:
@@ -1,8 +1,7 @@
1
1
  import { promisify } from 'node:util';
2
2
  import { unzip } from 'node:zlib';
3
3
  import { proto } from '../proto.js';
4
- import { decodeProtoBytes } from '../util/base64.js';
5
- import { toBytesView } from '../util/bytes.js';
4
+ import { decodeProtoBytes, toBytesView } from '../util/bytes.js';
6
5
  import { longToNumber } from '../util/primitives.js';
7
6
  const unzipAsync = promisify(unzip);
8
7
  const HANDLED_SYNC_TYPES = new Set([
@@ -28,15 +27,58 @@ export async function processHistorySyncNotification(deps, notification) {
28
27
  pushnames: historySync.pushnames.length
29
28
  });
30
29
  const nowMs = Date.now();
30
+ const contacts = [];
31
+ for (const pn of historySync.pushnames) {
32
+ if (!pn.id) {
33
+ continue;
34
+ }
35
+ contacts.push({
36
+ jid: pn.id,
37
+ pushName: pn.pushname ?? undefined,
38
+ lastUpdatedMs: nowMs
39
+ });
40
+ }
41
+ const threads = [];
42
+ const messages = [];
31
43
  let messagesCount = 0;
32
- const conversationPromises = [];
33
44
  for (const conversation of historySync.conversations) {
34
- conversationPromises.push(persistConversation(deps, conversation, nowMs).then((count) => {
35
- messagesCount += count;
36
- }));
45
+ const threadJid = conversation.id;
46
+ if (!threadJid) {
47
+ deps.logger.debug('skipping history sync conversation without thread jid');
48
+ continue;
49
+ }
50
+ threads.push({
51
+ jid: threadJid,
52
+ name: conversation.name ?? undefined,
53
+ unreadCount: conversation.unreadCount ?? undefined,
54
+ archived: conversation.archived ?? undefined,
55
+ pinned: conversation.pinned ?? undefined,
56
+ muteEndMs: longToNumber(conversation.muteEndTime) || undefined,
57
+ markedAsUnread: conversation.markedAsUnread ?? undefined,
58
+ ephemeralExpiration: conversation.ephemeralExpiration ?? undefined
59
+ });
60
+ for (const histMsg of conversation.messages ?? []) {
61
+ const webMsg = histMsg.message;
62
+ if (!webMsg?.key?.id) {
63
+ continue;
64
+ }
65
+ const timestampMs = longToNumber(webMsg.messageTimestamp) * 1000;
66
+ messages.push({
67
+ id: webMsg.key.id,
68
+ threadJid,
69
+ senderJid: webMsg.key.participant ?? undefined,
70
+ fromMe: webMsg.key.fromMe === true,
71
+ timestampMs: timestampMs || undefined,
72
+ messageBytes: webMsg.message
73
+ ? proto.Message.encode(webMsg.message).finish()
74
+ : undefined
75
+ });
76
+ messagesCount += 1;
77
+ }
37
78
  }
38
- const pushnamePromise = persistPushnames(deps, historySync.pushnames, nowMs);
39
- await Promise.all([...conversationPromises, pushnamePromise]);
79
+ await deps.contactStore.upsertBatch(contacts);
80
+ await deps.threadStore.upsertBatch(threads);
81
+ await deps.messageStore.upsertBatch(messages);
40
82
  const event = {
41
83
  syncType,
42
84
  messagesCount,
@@ -65,55 +107,3 @@ async function downloadHistorySyncBlob(deps, notification) {
65
107
  fileEncSha256
66
108
  });
67
109
  }
68
- async function persistConversation(deps, conversation, _nowMs) {
69
- const threadJid = conversation.id;
70
- if (!threadJid) {
71
- deps.logger.debug('skipping history sync conversation without thread jid');
72
- return 0;
73
- }
74
- const messages = conversation.messages ?? [];
75
- const messageRecords = [];
76
- for (const histMsg of messages) {
77
- const webMsg = histMsg.message;
78
- if (!webMsg?.key?.id) {
79
- continue;
80
- }
81
- const timestampMs = longToNumber(webMsg.messageTimestamp) * 1000;
82
- const messageBytes = webMsg.message
83
- ? proto.Message.encode(webMsg.message).finish()
84
- : undefined;
85
- messageRecords.push({
86
- id: webMsg.key.id,
87
- threadJid,
88
- senderJid: webMsg.key.participant ?? undefined,
89
- fromMe: webMsg.key.fromMe === true,
90
- timestampMs: timestampMs || undefined,
91
- messageBytes
92
- });
93
- }
94
- const threadPromise = deps.threadStore.upsert({
95
- jid: threadJid,
96
- name: conversation.name ?? undefined,
97
- unreadCount: conversation.unreadCount ?? undefined,
98
- archived: conversation.archived ?? undefined,
99
- pinned: conversation.pinned ?? undefined,
100
- muteEndMs: longToNumber(conversation.muteEndTime) || undefined,
101
- markedAsUnread: conversation.markedAsUnread ?? undefined,
102
- ephemeralExpiration: conversation.ephemeralExpiration ?? undefined
103
- });
104
- const messagePromises = messageRecords.map((record) => deps.messageStore.upsert(record));
105
- await Promise.all([threadPromise, ...messagePromises]);
106
- return messageRecords.length;
107
- }
108
- async function persistPushnames(deps, pushnames, nowMs) {
109
- if (pushnames.length === 0) {
110
- return;
111
- }
112
- await Promise.all(pushnames
113
- .filter((pn) => !!pn.id)
114
- .map((pn) => deps.contactStore.upsert({
115
- jid: pn.id,
116
- pushName: pn.pushname ?? undefined,
117
- lastUpdatedMs: nowMs
118
- })));
119
- }