zapo-js 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (468) hide show
  1. package/README.md +12 -4
  2. package/dist/appstate/WaAppStateCrypto.js +1 -1
  3. package/dist/appstate/WaAppStateSyncClient.js +138 -93
  4. package/dist/appstate/{store/sqlite.js → encoding.js} +13 -8
  5. package/dist/appstate/index.js +8 -6
  6. package/dist/appstate/utils.js +0 -5
  7. package/dist/auth/WaAuthClient.js +36 -47
  8. package/dist/auth/flow/WaAuthCredentialsFlow.js +7 -7
  9. package/dist/auth/index.js +1 -6
  10. package/dist/auth/pairing/WaPairingCodeCrypto.js +6 -4
  11. package/dist/auth/pairing/WaPairingFlow.js +13 -3
  12. package/dist/client/WaClient.js +225 -101
  13. package/dist/client/WaClientFactory.js +294 -44
  14. package/dist/client/connection/WaConnectionManager.js +19 -10
  15. package/dist/client/coordinators/WaBusinessCoordinator.js +241 -0
  16. package/dist/client/coordinators/WaGroupCoordinator.js +11 -7
  17. package/dist/client/coordinators/WaIncomingNodeCoordinator.js +1 -0
  18. package/dist/client/coordinators/WaMessageDispatchCoordinator.js +292 -99
  19. package/dist/client/coordinators/WaPassiveTasksCoordinator.js +74 -31
  20. package/dist/client/coordinators/WaPrivacyCoordinator.js +134 -0
  21. package/dist/client/coordinators/WaProfileCoordinator.js +212 -0
  22. package/dist/client/coordinators/WaRetryCoordinator.js +179 -27
  23. package/dist/client/coordinators/WaStreamControlCoordinator.js +18 -11
  24. package/dist/client/coordinators/WaTrustedContactTokenCoordinator.js +166 -0
  25. package/dist/client/dirty.js +40 -20
  26. package/dist/client/events/devices.js +72 -0
  27. package/dist/client/events/group.js +3 -11
  28. package/dist/client/events/identity.js +22 -0
  29. package/dist/client/events/privacy-token.js +39 -0
  30. package/dist/client/history-sync.js +50 -9
  31. package/dist/client/incoming.js +37 -7
  32. package/dist/client/mailbox.js +24 -23
  33. package/dist/client/messages.js +107 -31
  34. package/dist/client/messaging/fanout.js +21 -11
  35. package/dist/client/messaging/participants.js +6 -4
  36. package/dist/client/persistence/WriteBehindPersistence.js +129 -0
  37. package/dist/client/tokens/cs-token.js +50 -0
  38. package/dist/client/tokens/tc-token.js +25 -0
  39. package/dist/crypto/core/index.js +2 -2
  40. package/dist/crypto/core/keys.js +4 -4
  41. package/dist/crypto/core/nonce.js +2 -0
  42. package/dist/crypto/core/primitives.js +0 -8
  43. package/dist/crypto/core/random.js +22 -0
  44. package/dist/crypto/curves/X25519.js +25 -6
  45. package/dist/crypto/index.js +3 -0
  46. package/dist/crypto/math/constants.js +13 -36
  47. package/dist/crypto/math/edwards.js +171 -44
  48. package/dist/crypto/math/fe.js +706 -0
  49. package/dist/crypto/math/mod.js +10 -3
  50. package/dist/esm/appstate/WaAppStateCrypto.js +1 -1
  51. package/dist/esm/appstate/WaAppStateSyncClient.js +138 -93
  52. package/dist/esm/appstate/{store/sqlite.js → encoding.js} +13 -8
  53. package/dist/esm/appstate/index.js +2 -2
  54. package/dist/esm/appstate/utils.js +2 -5
  55. package/dist/esm/auth/WaAuthClient.js +36 -47
  56. package/dist/esm/auth/flow/WaAuthCredentialsFlow.js +7 -7
  57. package/dist/esm/auth/index.js +0 -2
  58. package/dist/esm/auth/pairing/WaPairingCodeCrypto.js +6 -4
  59. package/dist/esm/auth/pairing/WaPairingFlow.js +14 -4
  60. package/dist/esm/client/WaClient.js +225 -101
  61. package/dist/esm/client/WaClientFactory.js +295 -45
  62. package/dist/esm/client/connection/WaConnectionManager.js +19 -10
  63. package/dist/esm/client/coordinators/WaBusinessCoordinator.js +238 -0
  64. package/dist/esm/client/coordinators/WaGroupCoordinator.js +11 -7
  65. package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +1 -0
  66. package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +295 -102
  67. package/dist/esm/client/coordinators/WaPassiveTasksCoordinator.js +74 -31
  68. package/dist/esm/client/coordinators/WaPrivacyCoordinator.js +131 -0
  69. package/dist/esm/client/coordinators/WaProfileCoordinator.js +209 -0
  70. package/dist/esm/client/coordinators/WaRetryCoordinator.js +181 -29
  71. package/dist/esm/client/coordinators/WaStreamControlCoordinator.js +19 -12
  72. package/dist/esm/client/coordinators/WaTrustedContactTokenCoordinator.js +162 -0
  73. package/dist/esm/client/dirty.js +40 -20
  74. package/dist/esm/client/events/devices.js +68 -0
  75. package/dist/esm/client/events/group.js +3 -11
  76. package/dist/esm/client/events/identity.js +19 -0
  77. package/dist/esm/client/events/privacy-token.js +36 -0
  78. package/dist/esm/client/history-sync.js +50 -9
  79. package/dist/esm/client/incoming.js +38 -8
  80. package/dist/esm/client/mailbox.js +24 -23
  81. package/dist/esm/client/messages.js +108 -32
  82. package/dist/esm/client/messaging/fanout.js +22 -12
  83. package/dist/esm/client/messaging/participants.js +6 -4
  84. package/dist/esm/client/persistence/WriteBehindPersistence.js +125 -0
  85. package/dist/esm/client/tokens/cs-token.js +46 -0
  86. package/dist/esm/client/tokens/tc-token.js +18 -0
  87. package/dist/esm/crypto/core/index.js +2 -2
  88. package/dist/esm/crypto/core/keys.js +1 -1
  89. package/dist/esm/crypto/core/nonce.js +2 -0
  90. package/dist/esm/crypto/core/primitives.js +0 -7
  91. package/dist/esm/crypto/core/random.js +22 -1
  92. package/dist/esm/crypto/curves/X25519.js +25 -6
  93. package/dist/esm/crypto/index.js +1 -0
  94. package/dist/esm/crypto/math/constants.js +12 -35
  95. package/dist/esm/crypto/math/edwards.js +174 -47
  96. package/dist/esm/crypto/math/fe.js +691 -0
  97. package/dist/esm/crypto/math/mod.js +10 -1
  98. package/dist/esm/index.js +1 -1
  99. package/dist/esm/infra/perf/BackgroundQueue.js +478 -0
  100. package/dist/esm/infra/perf/BoundedTaskQueue.js +3 -1
  101. package/dist/esm/infra/perf/PromiseDedup.js +20 -0
  102. package/dist/esm/infra/perf/SharedExclusiveGate.js +109 -0
  103. package/dist/esm/infra/perf/StoreLock.js +77 -0
  104. package/dist/esm/media/WaMediaCrypto.js +95 -13
  105. package/dist/esm/media/WaMediaTransferClient.js +39 -47
  106. package/dist/esm/media/constants.js +2 -1
  107. package/dist/esm/message/WaMessageClient.js +26 -19
  108. package/dist/esm/message/content.js +195 -9
  109. package/dist/esm/message/icdc.js +76 -0
  110. package/dist/esm/message/incoming.js +24 -12
  111. package/dist/esm/message/phash.js +3 -1
  112. package/dist/esm/message/reporting-token.js +14 -27
  113. package/dist/esm/protocol/appstate.js +9 -40
  114. package/dist/esm/protocol/browser.js +10 -18
  115. package/dist/esm/protocol/constants.js +5 -3
  116. package/dist/esm/protocol/defaults.js +6 -0
  117. package/dist/esm/protocol/index.js +1 -2
  118. package/dist/esm/protocol/jid.js +105 -36
  119. package/dist/esm/protocol/message.js +61 -1
  120. package/dist/esm/protocol/nodes.js +2 -0
  121. package/dist/esm/protocol/notification.js +3 -1
  122. package/dist/esm/protocol/privacy-token.js +17 -0
  123. package/dist/esm/protocol/privacy.js +55 -0
  124. package/dist/esm/protocol/stream.js +26 -1
  125. package/dist/esm/retry/codec.js +216 -0
  126. package/dist/esm/retry/constants.js +1 -1
  127. package/dist/esm/retry/index.js +2 -2
  128. package/dist/esm/retry/parse.js +50 -30
  129. package/dist/esm/retry/replay.js +11 -7
  130. package/dist/esm/retry/tracker.js +50 -12
  131. package/dist/esm/signal/api/SignalDeviceSyncApi.js +49 -32
  132. package/dist/esm/signal/api/SignalDigestSyncApi.js +13 -9
  133. package/dist/esm/signal/api/SignalIdentitySyncApi.js +26 -11
  134. package/dist/esm/signal/api/SignalMissingPreKeysSyncApi.js +18 -7
  135. package/dist/esm/signal/api/SignalRotateKeyApi.js +4 -2
  136. package/dist/esm/signal/api/SignalSessionSyncApi.js +16 -7
  137. package/dist/esm/signal/api/result-map.js +10 -0
  138. package/dist/esm/signal/constants.js +0 -4
  139. package/dist/esm/signal/crypto/WaAdvSignature.js +12 -6
  140. package/dist/esm/signal/{store/sqlite.js → encoding.js} +78 -24
  141. package/dist/esm/signal/group/SenderKeyCodec.js +3 -2
  142. package/dist/esm/signal/group/SenderKeyManager.js +125 -106
  143. package/dist/esm/signal/index.js +1 -0
  144. package/dist/esm/signal/registration/keygen.js +6 -2
  145. package/dist/esm/signal/registration/utils.js +1 -0
  146. package/dist/esm/signal/session/SignalProtocol.js +150 -74
  147. package/dist/esm/signal/session/resolver.js +137 -102
  148. package/dist/esm/store/contracts/privacy-token.store.js +1 -0
  149. package/dist/esm/store/createStore.js +101 -187
  150. package/dist/esm/store/index.js +1 -10
  151. package/dist/esm/store/locks/appstate.lock.js +26 -0
  152. package/dist/esm/store/locks/auth.lock.js +15 -0
  153. package/dist/esm/store/locks/contact.lock.js +20 -0
  154. package/dist/esm/store/locks/device-list.lock.js +20 -0
  155. package/dist/esm/store/locks/message.lock.js +21 -0
  156. package/dist/esm/store/locks/participants.lock.js +20 -0
  157. package/dist/esm/store/locks/privacy-token.lock.js +18 -0
  158. package/dist/esm/store/locks/retry.lock.js +29 -0
  159. package/dist/esm/store/locks/sender-key.lock.js +52 -0
  160. package/dist/esm/store/locks/signal.lock.js +63 -0
  161. package/dist/esm/store/locks/thread.lock.js +21 -0
  162. package/dist/esm/store/noop.store.js +1 -1
  163. package/dist/esm/store/providers/memory/appstate.store.js +22 -24
  164. package/dist/esm/store/providers/memory/device-list.store.js +10 -5
  165. package/dist/esm/store/providers/memory/privacy-token.store.js +43 -0
  166. package/dist/esm/store/providers/memory/retry.store.js +77 -2
  167. package/dist/esm/store/providers/memory/sender-key.store.js +6 -1
  168. package/dist/esm/store/providers/memory/signal.store.js +36 -19
  169. package/dist/esm/transport/WaComms.js +3 -1
  170. package/dist/esm/transport/WaWebSocket.js +0 -6
  171. package/dist/esm/transport/binary/constants.js +0 -30
  172. package/dist/esm/transport/binary/decoder.js +4 -4
  173. package/dist/esm/transport/binary/encoder.js +8 -15
  174. package/dist/esm/transport/binary/index.js +0 -1
  175. package/dist/esm/transport/node/WaNodeOrchestrator.js +25 -19
  176. package/dist/esm/transport/node/builders/business.js +129 -0
  177. package/dist/esm/transport/node/builders/global.js +370 -0
  178. package/dist/esm/transport/node/builders/index.js +5 -2
  179. package/dist/esm/transport/node/builders/message.js +63 -239
  180. package/dist/esm/transport/node/builders/pairing.js +0 -24
  181. package/dist/esm/transport/node/builders/privacy-token.js +41 -0
  182. package/dist/esm/transport/node/builders/privacy.js +48 -0
  183. package/dist/esm/transport/node/builders/profile.js +70 -0
  184. package/dist/esm/transport/node/builders/retry.js +10 -22
  185. package/dist/esm/transport/node/builders/usync.js +6 -2
  186. package/dist/esm/transport/node/helpers.js +19 -1
  187. package/dist/esm/transport/node/usync.js +3 -33
  188. package/dist/esm/transport/node/xml.js +35 -14
  189. package/dist/esm/transport/noise/WaClientPayload.js +10 -10
  190. package/dist/esm/transport/noise/WaNoiseCert.js +3 -3
  191. package/dist/esm/transport/noise/WaNoiseSession.js +64 -23
  192. package/dist/esm/transport/noise/WaNoiseSocket.js +8 -4
  193. package/dist/esm/transport/stream/parse.js +8 -4
  194. package/dist/esm/util/bytes.js +22 -18
  195. package/dist/esm/util/index.js +5 -0
  196. package/dist/esm/util/primitives.js +3 -2
  197. package/dist/index.js +7 -1
  198. package/dist/infra/perf/BackgroundQueue.js +482 -0
  199. package/dist/infra/perf/BoundedTaskQueue.js +3 -1
  200. package/dist/infra/perf/PromiseDedup.js +24 -0
  201. package/dist/infra/perf/SharedExclusiveGate.js +113 -0
  202. package/dist/infra/perf/StoreLock.js +81 -0
  203. package/dist/media/WaMediaCrypto.js +94 -12
  204. package/dist/media/WaMediaTransferClient.js +39 -47
  205. package/dist/media/constants.js +2 -1
  206. package/dist/message/WaMessageClient.js +26 -19
  207. package/dist/message/content.js +198 -9
  208. package/dist/message/icdc.js +81 -0
  209. package/dist/message/incoming.js +24 -12
  210. package/dist/message/phash.js +3 -1
  211. package/dist/message/reporting-token.js +14 -28
  212. package/dist/protocol/appstate.js +10 -41
  213. package/dist/protocol/browser.js +10 -18
  214. package/dist/protocol/constants.js +21 -2
  215. package/dist/protocol/defaults.js +6 -0
  216. package/dist/protocol/index.js +8 -5
  217. package/dist/protocol/jid.js +111 -36
  218. package/dist/protocol/message.js +62 -2
  219. package/dist/protocol/nodes.js +2 -0
  220. package/dist/protocol/notification.js +3 -1
  221. package/dist/protocol/privacy-token.js +20 -0
  222. package/dist/protocol/privacy.js +58 -0
  223. package/dist/protocol/stream.js +27 -2
  224. package/dist/retry/codec.js +220 -0
  225. package/dist/retry/constants.js +1 -1
  226. package/dist/retry/index.js +5 -5
  227. package/dist/retry/parse.js +51 -30
  228. package/dist/retry/replay.js +10 -6
  229. package/dist/retry/tracker.js +50 -12
  230. package/dist/signal/api/SignalDeviceSyncApi.js +48 -31
  231. package/dist/signal/api/SignalDigestSyncApi.js +13 -9
  232. package/dist/signal/api/SignalIdentitySyncApi.js +25 -10
  233. package/dist/signal/api/SignalMissingPreKeysSyncApi.js +17 -6
  234. package/dist/signal/api/SignalRotateKeyApi.js +4 -2
  235. package/dist/signal/api/SignalSessionSyncApi.js +16 -7
  236. package/dist/signal/api/result-map.js +13 -0
  237. package/dist/signal/constants.js +1 -5
  238. package/dist/signal/crypto/WaAdvSignature.js +11 -5
  239. package/dist/signal/{store/sqlite.js → encoding.js} +79 -25
  240. package/dist/signal/group/SenderKeyCodec.js +4 -3
  241. package/dist/signal/group/SenderKeyManager.js +125 -106
  242. package/dist/signal/index.js +13 -1
  243. package/dist/signal/registration/keygen.js +6 -2
  244. package/dist/signal/registration/utils.js +1 -0
  245. package/dist/signal/session/SignalProtocol.js +150 -74
  246. package/dist/signal/session/resolver.js +135 -100
  247. package/dist/store/contracts/privacy-token.store.js +2 -0
  248. package/dist/store/createStore.js +101 -187
  249. package/dist/store/index.js +15 -33
  250. package/dist/store/locks/appstate.lock.js +29 -0
  251. package/dist/store/locks/auth.lock.js +18 -0
  252. package/dist/store/locks/contact.lock.js +23 -0
  253. package/dist/store/locks/device-list.lock.js +23 -0
  254. package/dist/store/locks/message.lock.js +24 -0
  255. package/dist/store/locks/participants.lock.js +23 -0
  256. package/dist/store/locks/privacy-token.lock.js +21 -0
  257. package/dist/store/locks/retry.lock.js +32 -0
  258. package/dist/store/locks/sender-key.lock.js +55 -0
  259. package/dist/store/locks/signal.lock.js +66 -0
  260. package/dist/store/locks/thread.lock.js +24 -0
  261. package/dist/store/noop.store.js +1 -1
  262. package/dist/store/providers/memory/appstate.store.js +22 -24
  263. package/dist/store/providers/memory/device-list.store.js +10 -5
  264. package/dist/store/providers/memory/privacy-token.store.js +47 -0
  265. package/dist/store/providers/memory/retry.store.js +77 -2
  266. package/dist/store/providers/memory/sender-key.store.js +6 -1
  267. package/dist/store/providers/memory/signal.store.js +36 -19
  268. package/dist/transport/WaComms.js +3 -1
  269. package/dist/transport/WaWebSocket.js +0 -6
  270. package/dist/transport/binary/constants.js +1 -31
  271. package/dist/transport/binary/decoder.js +4 -4
  272. package/dist/transport/binary/encoder.js +8 -15
  273. package/dist/transport/binary/index.js +0 -4
  274. package/dist/transport/node/WaNodeOrchestrator.js +24 -18
  275. package/dist/transport/node/builders/business.js +137 -0
  276. package/dist/transport/node/builders/global.js +375 -0
  277. package/dist/transport/node/builders/index.js +18 -9
  278. package/dist/transport/node/builders/message.js +64 -245
  279. package/dist/transport/node/builders/pairing.js +0 -26
  280. package/dist/transport/node/builders/privacy-token.js +46 -0
  281. package/dist/transport/node/builders/privacy.js +55 -0
  282. package/dist/transport/node/builders/profile.js +78 -0
  283. package/dist/transport/node/builders/retry.js +9 -21
  284. package/dist/transport/node/builders/usync.js +6 -2
  285. package/dist/transport/node/helpers.js +20 -1
  286. package/dist/transport/node/usync.js +2 -32
  287. package/dist/transport/node/xml.js +35 -14
  288. package/dist/transport/noise/WaClientPayload.js +13 -13
  289. package/dist/transport/noise/WaNoiseCert.js +2 -2
  290. package/dist/transport/noise/WaNoiseSession.js +64 -23
  291. package/dist/transport/noise/WaNoiseSocket.js +8 -4
  292. package/dist/transport/stream/parse.js +7 -3
  293. package/dist/types/appstate/encoding.d.ts +7 -0
  294. package/dist/types/appstate/index.d.ts +3 -3
  295. package/dist/types/appstate/utils.d.ts +0 -2
  296. package/dist/types/auth/flow/WaAuthCredentialsFlow.d.ts +1 -1
  297. package/dist/types/auth/index.d.ts +0 -2
  298. package/dist/types/auth/types.d.ts +1 -0
  299. package/dist/types/client/WaClient.d.ts +27 -12
  300. package/dist/types/client/WaClientFactory.d.ts +12 -4
  301. package/dist/types/client/connection/WaConnectionManager.d.ts +2 -0
  302. package/dist/types/client/coordinators/WaBusinessCoordinator.d.ts +57 -0
  303. package/dist/types/client/coordinators/WaIncomingNodeCoordinator.d.ts +3 -1
  304. package/dist/types/client/coordinators/WaMessageDispatchCoordinator.d.ts +14 -0
  305. package/dist/types/client/coordinators/WaPassiveTasksCoordinator.d.ts +4 -0
  306. package/dist/types/client/coordinators/WaPrivacyCoordinator.d.ts +26 -0
  307. package/dist/types/client/coordinators/WaProfileCoordinator.d.ts +36 -0
  308. package/dist/types/client/coordinators/WaRetryCoordinator.d.ts +6 -0
  309. package/dist/types/client/coordinators/WaStreamControlCoordinator.d.ts +3 -2
  310. package/dist/types/client/coordinators/WaTrustedContactTokenCoordinator.d.ts +45 -0
  311. package/dist/types/client/events/devices.d.ts +20 -0
  312. package/dist/types/client/events/identity.d.ts +9 -0
  313. package/dist/types/client/events/privacy-token.d.ts +7 -0
  314. package/dist/types/client/history-sync.d.ts +9 -6
  315. package/dist/types/client/incoming.d.ts +3 -1
  316. package/dist/types/client/index.d.ts +1 -1
  317. package/dist/types/client/mailbox.d.ts +3 -5
  318. package/dist/types/client/messages.d.ts +1 -2
  319. package/dist/types/client/persistence/WriteBehindPersistence.d.ts +34 -0
  320. package/dist/types/client/tokens/cs-token.d.ts +10 -0
  321. package/dist/types/client/tokens/tc-token.d.ts +5 -0
  322. package/dist/types/client/types.d.ts +51 -3
  323. package/dist/types/crypto/core/index.d.ts +2 -2
  324. package/dist/types/crypto/core/nonce.d.ts +2 -0
  325. package/dist/types/crypto/core/primitives.d.ts +0 -1
  326. package/dist/types/crypto/core/random.d.ts +1 -0
  327. package/dist/types/crypto/index.d.ts +1 -0
  328. package/dist/types/crypto/math/constants.d.ts +4 -2
  329. package/dist/types/crypto/math/fe.d.ts +30 -0
  330. package/dist/types/crypto/math/mod.d.ts +0 -2
  331. package/dist/types/crypto/math/types.d.ts +11 -4
  332. package/dist/types/index.d.ts +5 -3
  333. package/dist/types/infra/perf/BackgroundQueue.d.ts +58 -0
  334. package/dist/types/infra/perf/PromiseDedup.d.ts +4 -0
  335. package/dist/types/infra/perf/SharedExclusiveGate.d.ts +17 -0
  336. package/dist/types/infra/perf/StoreLock.d.ts +10 -0
  337. package/dist/types/media/WaMediaCrypto.d.ts +3 -2
  338. package/dist/types/media/WaMediaTransferClient.d.ts +3 -12
  339. package/dist/types/media/constants.d.ts +1 -1
  340. package/dist/types/media/index.d.ts +1 -1
  341. package/dist/types/media/types.d.ts +10 -2
  342. package/dist/types/message/content.d.ts +8 -0
  343. package/dist/types/message/icdc.d.ts +13 -0
  344. package/dist/types/message/reporting-token.d.ts +0 -1
  345. package/dist/types/message/types.d.ts +45 -6
  346. package/dist/types/protocol/appstate.d.ts +0 -11
  347. package/dist/types/protocol/constants.d.ts +7 -3
  348. package/dist/types/protocol/defaults.d.ts +6 -0
  349. package/dist/types/protocol/index.d.ts +1 -2
  350. package/dist/types/protocol/jid.d.ts +19 -2
  351. package/dist/types/protocol/message.d.ts +60 -0
  352. package/dist/types/protocol/nodes.d.ts +2 -0
  353. package/dist/types/protocol/notification.d.ts +2 -0
  354. package/dist/types/protocol/privacy-token.d.ts +17 -0
  355. package/dist/types/protocol/privacy.d.ts +75 -0
  356. package/dist/types/protocol/stream.d.ts +30 -0
  357. package/dist/types/retry/codec.d.ts +3 -0
  358. package/dist/types/retry/index.d.ts +3 -3
  359. package/dist/types/retry/parse.d.ts +5 -2
  360. package/dist/types/retry/tracker.d.ts +1 -0
  361. package/dist/types/retry/types.d.ts +6 -1
  362. package/dist/types/signal/api/SignalDeviceSyncApi.d.ts +2 -1
  363. package/dist/types/signal/api/SignalDigestSyncApi.d.ts +6 -0
  364. package/dist/types/signal/api/SignalIdentitySyncApi.d.ts +2 -0
  365. package/dist/types/signal/api/SignalRotateKeyApi.d.ts +4 -5
  366. package/dist/types/signal/api/SignalSessionSyncApi.d.ts +8 -6
  367. package/dist/types/signal/api/result-map.d.ts +1 -0
  368. package/dist/types/signal/constants.d.ts +0 -3
  369. package/dist/types/signal/{store/sqlite.d.ts → encoding.d.ts} +3 -3
  370. package/dist/types/signal/group/SenderKeyManager.d.ts +10 -5
  371. package/dist/types/signal/index.d.ts +2 -0
  372. package/dist/types/signal/session/SignalProtocol.d.ts +10 -4
  373. package/dist/types/signal/session/resolver.d.ts +7 -2
  374. package/dist/types/store/contracts/appstate.store.d.ts +1 -1
  375. package/dist/types/store/contracts/privacy-token.store.d.ts +16 -0
  376. package/dist/types/store/contracts/retry.store.d.ts +7 -0
  377. package/dist/types/store/contracts/signal.store.d.ts +7 -0
  378. package/dist/types/store/createStore.d.ts +1 -1
  379. package/dist/types/store/index.d.ts +5 -13
  380. package/dist/types/store/locks/appstate.lock.d.ts +3 -0
  381. package/dist/types/store/locks/auth.lock.d.ts +3 -0
  382. package/dist/types/store/locks/contact.lock.d.ts +3 -0
  383. package/dist/types/store/locks/device-list.lock.d.ts +2 -0
  384. package/dist/types/store/locks/message.lock.d.ts +3 -0
  385. package/dist/types/store/locks/participants.lock.d.ts +2 -0
  386. package/dist/types/store/locks/privacy-token.lock.d.ts +2 -0
  387. package/dist/types/store/locks/retry.lock.d.ts +2 -0
  388. package/dist/types/store/locks/sender-key.lock.d.ts +3 -0
  389. package/dist/types/store/locks/signal.lock.d.ts +3 -0
  390. package/dist/types/store/locks/thread.lock.d.ts +3 -0
  391. package/dist/types/store/providers/memory/appstate.store.d.ts +1 -1
  392. package/dist/types/store/providers/memory/privacy-token.store.d.ts +13 -0
  393. package/dist/types/store/providers/memory/retry.store.d.ts +8 -0
  394. package/dist/types/store/providers/memory/signal.store.d.ts +2 -1
  395. package/dist/types/store/types.d.ts +49 -61
  396. package/dist/types/transport/WaWebSocket.d.ts +0 -1
  397. package/dist/types/transport/binary/constants.d.ts +0 -30
  398. package/dist/types/transport/binary/index.d.ts +0 -1
  399. package/dist/types/transport/node/WaNodeOrchestrator.d.ts +3 -4
  400. package/dist/types/transport/node/builders/business.d.ts +29 -0
  401. package/dist/types/transport/node/builders/global.d.ts +102 -0
  402. package/dist/types/transport/node/builders/index.d.ts +5 -2
  403. package/dist/types/transport/node/builders/message.d.ts +8 -7
  404. package/dist/types/transport/node/builders/pairing.d.ts +0 -2
  405. package/dist/types/transport/node/builders/privacy-token.d.ts +9 -0
  406. package/dist/types/transport/node/builders/privacy.d.ts +7 -0
  407. package/dist/types/transport/node/builders/profile.d.ts +8 -0
  408. package/dist/types/transport/node/builders/retry.d.ts +0 -1
  409. package/dist/types/transport/node/helpers.d.ts +5 -0
  410. package/dist/types/transport/noise/WaNoiseSession.d.ts +3 -2
  411. package/dist/types/transport/noise/WaNoiseSocket.d.ts +4 -2
  412. package/dist/types/util/bytes.d.ts +1 -1
  413. package/dist/types/util/index.d.ts +5 -0
  414. package/dist/types/util/primitives.d.ts +0 -1
  415. package/dist/util/bytes.js +22 -18
  416. package/dist/util/index.js +23 -0
  417. package/dist/util/primitives.js +2 -2
  418. package/package.json +29 -7
  419. package/proto/index.js +1 -1
  420. package/dist/crypto/core/constants.js +0 -4
  421. package/dist/esm/crypto/core/constants.js +0 -1
  422. package/dist/esm/retry/outbound.js +0 -82
  423. package/dist/esm/store/providers/sqlite/BaseSqliteStore.js +0 -37
  424. package/dist/esm/store/providers/sqlite/appstate.store.js +0 -250
  425. package/dist/esm/store/providers/sqlite/auth.store.js +0 -176
  426. package/dist/esm/store/providers/sqlite/connection.js +0 -245
  427. package/dist/esm/store/providers/sqlite/contact.store.js +0 -74
  428. package/dist/esm/store/providers/sqlite/device-list.store.js +0 -127
  429. package/dist/esm/store/providers/sqlite/message.store.js +0 -132
  430. package/dist/esm/store/providers/sqlite/migrations.js +0 -347
  431. package/dist/esm/store/providers/sqlite/participants.store.js +0 -77
  432. package/dist/esm/store/providers/sqlite/retry.store.js +0 -141
  433. package/dist/esm/store/providers/sqlite/sender-key.store.js +0 -198
  434. package/dist/esm/store/providers/sqlite/signal.store.js +0 -435
  435. package/dist/esm/store/providers/sqlite/table-names.js +0 -107
  436. package/dist/esm/store/providers/sqlite/thread.store.js +0 -85
  437. package/dist/retry/outbound.js +0 -87
  438. package/dist/store/providers/sqlite/BaseSqliteStore.js +0 -41
  439. package/dist/store/providers/sqlite/appstate.store.js +0 -254
  440. package/dist/store/providers/sqlite/auth.store.js +0 -180
  441. package/dist/store/providers/sqlite/connection.js +0 -281
  442. package/dist/store/providers/sqlite/contact.store.js +0 -78
  443. package/dist/store/providers/sqlite/device-list.store.js +0 -131
  444. package/dist/store/providers/sqlite/message.store.js +0 -136
  445. package/dist/store/providers/sqlite/migrations.js +0 -350
  446. package/dist/store/providers/sqlite/participants.store.js +0 -81
  447. package/dist/store/providers/sqlite/retry.store.js +0 -145
  448. package/dist/store/providers/sqlite/sender-key.store.js +0 -202
  449. package/dist/store/providers/sqlite/signal.store.js +0 -439
  450. package/dist/store/providers/sqlite/table-names.js +0 -113
  451. package/dist/store/providers/sqlite/thread.store.js +0 -89
  452. package/dist/types/appstate/store/sqlite.d.ts +0 -7
  453. package/dist/types/crypto/core/constants.d.ts +0 -1
  454. package/dist/types/retry/outbound.d.ts +0 -4
  455. package/dist/types/store/providers/sqlite/BaseSqliteStore.d.ts +0 -12
  456. package/dist/types/store/providers/sqlite/appstate.store.d.ts +0 -17
  457. package/dist/types/store/providers/sqlite/auth.store.d.ts +0 -10
  458. package/dist/types/store/providers/sqlite/connection.d.ts +0 -10
  459. package/dist/types/store/providers/sqlite/contact.store.d.ts +0 -12
  460. package/dist/types/store/providers/sqlite/device-list.store.d.ts +0 -15
  461. package/dist/types/store/providers/sqlite/message.store.d.ts +0 -13
  462. package/dist/types/store/providers/sqlite/migrations.d.ts +0 -3
  463. package/dist/types/store/providers/sqlite/participants.store.d.ts +0 -12
  464. package/dist/types/store/providers/sqlite/retry.store.d.ts +0 -15
  465. package/dist/types/store/providers/sqlite/sender-key.store.d.ts +0 -24
  466. package/dist/types/store/providers/sqlite/signal.store.d.ts +0 -53
  467. package/dist/types/store/providers/sqlite/table-names.d.ts +0 -5
  468. package/dist/types/store/providers/sqlite/thread.store.d.ts +0 -13
@@ -0,0 +1,68 @@
1
+ import { parseSignalAddressFromJid } from '../../protocol/jid.js';
2
+ import { findNodeChild, getNodeChildrenByTag } from '../../transport/node/helpers.js';
3
+ export const DEVICE_NOTIFICATION_ACTIONS = Object.freeze({
4
+ ADD: 'add',
5
+ REMOVE: 'remove',
6
+ UPDATE: 'update'
7
+ });
8
+ export function parseDeviceNotification(node) {
9
+ const stanzaId = node.attrs.id;
10
+ const fromJid = node.attrs.from;
11
+ if (!stanzaId || !fromJid) {
12
+ return null;
13
+ }
14
+ let action;
15
+ let actionNode;
16
+ if (findNodeChild(node, DEVICE_NOTIFICATION_ACTIONS.REMOVE)) {
17
+ action = DEVICE_NOTIFICATION_ACTIONS.REMOVE;
18
+ actionNode = findNodeChild(node, DEVICE_NOTIFICATION_ACTIONS.REMOVE);
19
+ }
20
+ else if (findNodeChild(node, DEVICE_NOTIFICATION_ACTIONS.ADD)) {
21
+ action = DEVICE_NOTIFICATION_ACTIONS.ADD;
22
+ actionNode = findNodeChild(node, DEVICE_NOTIFICATION_ACTIONS.ADD);
23
+ }
24
+ else if (findNodeChild(node, DEVICE_NOTIFICATION_ACTIONS.UPDATE)) {
25
+ action = DEVICE_NOTIFICATION_ACTIONS.UPDATE;
26
+ actionNode = findNodeChild(node, DEVICE_NOTIFICATION_ACTIONS.UPDATE);
27
+ }
28
+ else {
29
+ return null;
30
+ }
31
+ const devices = [];
32
+ if (action !== DEVICE_NOTIFICATION_ACTIONS.UPDATE && actionNode) {
33
+ const deviceNodes = getNodeChildrenByTag(actionNode, 'device');
34
+ for (let index = 0; index < deviceNodes.length; index += 1) {
35
+ const deviceNode = deviceNodes[index];
36
+ const jidAttr = deviceNode.attrs.jid;
37
+ if (!jidAttr) {
38
+ continue;
39
+ }
40
+ let deviceId;
41
+ try {
42
+ deviceId = parseSignalAddressFromJid(jidAttr).device;
43
+ }
44
+ catch {
45
+ continue;
46
+ }
47
+ const keyIndexAttr = deviceNode.attrs['key-index'];
48
+ const parsedKeyIndex = keyIndexAttr === undefined ? null : Number.parseInt(keyIndexAttr, 10);
49
+ const keyIndex = parsedKeyIndex !== null &&
50
+ Number.isSafeInteger(parsedKeyIndex) &&
51
+ parsedKeyIndex >= 0
52
+ ? parsedKeyIndex
53
+ : null;
54
+ devices[devices.length] = {
55
+ deviceId,
56
+ keyIndex
57
+ };
58
+ }
59
+ }
60
+ return {
61
+ action,
62
+ stanzaId,
63
+ fromJid,
64
+ lid: node.attrs.lid,
65
+ hash: action === DEVICE_NOTIFICATION_ACTIONS.UPDATE ? actionNode?.attrs.hash : undefined,
66
+ devices
67
+ };
68
+ }
@@ -1,3 +1,4 @@
1
+ import { createUnhandledIncomingNodeEvent } from '../incoming.js';
1
2
  import { WA_GROUP_NOTIFICATION_TAGS, WA_NOTIFICATION_TYPES } from '../../protocol/constants.js';
2
3
  import { WA_NODE_TAGS } from '../../protocol/nodes.js';
3
4
  import { findNodeChild, getNodeChildren, getNodeChildrenByTag } from '../../transport/node/helpers.js';
@@ -99,15 +100,6 @@ function createBaseGroupEvent(notificationNode, actionNode) {
99
100
  timestampSeconds: parseOptionalInt(notificationNode.attrs.t)
100
101
  };
101
102
  }
102
- function createUnhandledStanzaEvent(notificationNode, reason) {
103
- return {
104
- rawNode: notificationNode,
105
- stanzaId: notificationNode.attrs.id,
106
- chatJid: notificationNode.attrs.from,
107
- stanzaType: notificationNode.attrs.type,
108
- reason
109
- };
110
- }
111
103
  function parseCreateGroupAction(notificationNode, actionNode) {
112
104
  const groupNode = findNodeChild(actionNode, WA_NODE_TAGS.GROUP);
113
105
  if (!groupNode) {
@@ -413,13 +405,13 @@ export function parseGroupNotificationEvents(notificationNode) {
413
405
  try {
414
406
  const parsedEvent = parseGroupActionNode(notificationNode, actionNode);
415
407
  if (!parsedEvent) {
416
- unhandled.push(createUnhandledStanzaEvent(notificationNode, `notification.${WA_NOTIFICATION_TYPES.GROUP}.${actionNode.tag}.not_supported`));
408
+ unhandled.push(createUnhandledIncomingNodeEvent(notificationNode, `notification.${WA_NOTIFICATION_TYPES.GROUP}.${actionNode.tag}.not_supported`));
417
409
  continue;
418
410
  }
419
411
  events.push(parsedEvent);
420
412
  }
421
413
  catch {
422
- unhandled.push(createUnhandledStanzaEvent(notificationNode, `notification.${WA_NOTIFICATION_TYPES.GROUP}.${actionNode.tag}.parse_failed`));
414
+ unhandled.push(createUnhandledIncomingNodeEvent(notificationNode, `notification.${WA_NOTIFICATION_TYPES.GROUP}.${actionNode.tag}.parse_failed`));
423
415
  }
424
416
  }
425
417
  return {
@@ -0,0 +1,19 @@
1
+ import { getFirstNodeChild } from '../../transport/node/helpers.js';
2
+ export function parseIdentityChangeNotification(node) {
3
+ const child = getFirstNodeChild(node);
4
+ if (!child || child.tag !== 'identity') {
5
+ return null;
6
+ }
7
+ const fromJid = node.attrs.from;
8
+ const stanzaId = node.attrs.id;
9
+ if (!fromJid || !stanzaId) {
10
+ return null;
11
+ }
12
+ return {
13
+ fromJid,
14
+ stanzaId,
15
+ displayName: node.attrs.display_name,
16
+ lid: node.attrs.lid,
17
+ offline: node.attrs.offline
18
+ };
19
+ }
@@ -0,0 +1,36 @@
1
+ import { WA_PRIVACY_TOKEN_TAGS } from '../../protocol/privacy-token.js';
2
+ import { findNodeChild, getNodeChildren } from '../../transport/node/helpers.js';
3
+ import { toBytesView } from '../../util/bytes.js';
4
+ import { asNumber } from '../../util/coercion.js';
5
+ export function parsePrivacyTokenNotification(node) {
6
+ const tokensNode = findNodeChild(node, WA_PRIVACY_TOKEN_TAGS.TOKENS);
7
+ if (!tokensNode) {
8
+ return [];
9
+ }
10
+ const children = getNodeChildren(tokensNode);
11
+ const result = [];
12
+ for (let i = 0; i < children.length; i += 1) {
13
+ const child = children[i];
14
+ if (child.tag !== WA_PRIVACY_TOKEN_TAGS.TOKEN) {
15
+ continue;
16
+ }
17
+ const type = child.attrs.type;
18
+ if (!type) {
19
+ continue;
20
+ }
21
+ const rawTimestamp = child.attrs.t;
22
+ if (!rawTimestamp) {
23
+ continue;
24
+ }
25
+ const content = child.content;
26
+ if (!(content instanceof Uint8Array)) {
27
+ continue;
28
+ }
29
+ result[result.length] = {
30
+ type,
31
+ tokenBytes: toBytesView(content),
32
+ timestampS: asNumber(Number(rawTimestamp), 'privacy_token.t')
33
+ };
34
+ }
35
+ return result;
36
+ }
@@ -10,6 +10,7 @@ const HANDLED_SYNC_TYPES = new Set([
10
10
  proto.Message.HistorySyncType.FULL,
11
11
  proto.Message.HistorySyncType.PUSH_NAME
12
12
  ]);
13
+ const HISTORY_SYNC_MAX_PENDING_WRITES = 1024;
13
14
  export async function processHistorySyncNotification(deps, notification) {
14
15
  const syncType = notification.syncType;
15
16
  if (syncType === null || syncType === undefined || !HANDLED_SYNC_TYPES.has(syncType)) {
@@ -27,19 +28,20 @@ export async function processHistorySyncNotification(deps, notification) {
27
28
  pushnames: historySync.pushnames.length
28
29
  });
29
30
  const nowMs = Date.now();
30
- const contacts = [];
31
+ const pendingWrites = [];
31
32
  for (const pn of historySync.pushnames) {
32
33
  if (!pn.id) {
33
34
  continue;
34
35
  }
35
- contacts.push({
36
+ pendingWrites[pendingWrites.length] = deps.writeBehind.persistContactAsync({
36
37
  jid: pn.id,
37
38
  pushName: pn.pushname ?? undefined,
38
39
  lastUpdatedMs: nowMs
39
40
  });
41
+ if (pendingWrites.length >= HISTORY_SYNC_MAX_PENDING_WRITES) {
42
+ await flushPendingWrites(pendingWrites);
43
+ }
40
44
  }
41
- const threads = [];
42
- const messages = [];
43
45
  let messagesCount = 0;
44
46
  for (const conversation of historySync.conversations) {
45
47
  const threadJid = conversation.id;
@@ -47,7 +49,7 @@ export async function processHistorySyncNotification(deps, notification) {
47
49
  deps.logger.debug('skipping history sync conversation without thread jid');
48
50
  continue;
49
51
  }
50
- threads.push({
52
+ pendingWrites[pendingWrites.length] = deps.writeBehind.persistThreadAsync({
51
53
  jid: threadJid,
52
54
  name: conversation.name ?? undefined,
53
55
  unreadCount: conversation.unreadCount ?? undefined,
@@ -57,13 +59,16 @@ export async function processHistorySyncNotification(deps, notification) {
57
59
  markedAsUnread: conversation.markedAsUnread ?? undefined,
58
60
  ephemeralExpiration: conversation.ephemeralExpiration ?? undefined
59
61
  });
62
+ if (pendingWrites.length >= HISTORY_SYNC_MAX_PENDING_WRITES) {
63
+ await flushPendingWrites(pendingWrites);
64
+ }
60
65
  for (const histMsg of conversation.messages ?? []) {
61
66
  const webMsg = histMsg.message;
62
67
  if (!webMsg?.key?.id) {
63
68
  continue;
64
69
  }
65
70
  const timestampMs = longToNumber(webMsg.messageTimestamp) * 1000;
66
- messages.push({
71
+ pendingWrites[pendingWrites.length] = deps.writeBehind.persistMessageAsync({
67
72
  id: webMsg.key.id,
68
73
  threadJid,
69
74
  senderJid: webMsg.key.participant ?? undefined,
@@ -73,12 +78,35 @@ export async function processHistorySyncNotification(deps, notification) {
73
78
  ? proto.Message.encode(webMsg.message).finish()
74
79
  : undefined
75
80
  });
81
+ if (pendingWrites.length >= HISTORY_SYNC_MAX_PENDING_WRITES) {
82
+ await flushPendingWrites(pendingWrites);
83
+ }
76
84
  messagesCount += 1;
77
85
  }
78
86
  }
79
- await deps.contactStore.upsertBatch(contacts);
80
- await deps.threadStore.upsertBatch(threads);
81
- await deps.messageStore.upsertBatch(messages);
87
+ if (deps.onPrivacyTokens) {
88
+ const tokenConversations = [];
89
+ for (const conversation of historySync.conversations) {
90
+ if (!conversation.id)
91
+ continue;
92
+ if (conversation.tcToken ||
93
+ conversation.tcTokenTimestamp ||
94
+ conversation.tcTokenSenderTimestamp) {
95
+ tokenConversations[tokenConversations.length] = {
96
+ jid: conversation.id,
97
+ tcToken: conversation.tcToken,
98
+ tcTokenTimestamp: longToNumber(conversation.tcTokenTimestamp) || undefined,
99
+ tcTokenSenderTimestamp: longToNumber(conversation.tcTokenSenderTimestamp) || undefined
100
+ };
101
+ }
102
+ }
103
+ if (tokenConversations.length > 0) {
104
+ pendingWrites[pendingWrites.length] = deps.onPrivacyTokens(tokenConversations);
105
+ }
106
+ }
107
+ if (deps.onNctSalt && historySync.nctSalt) {
108
+ pendingWrites[pendingWrites.length] = deps.onNctSalt(historySync.nctSalt);
109
+ }
82
110
  const event = {
83
111
  syncType,
84
112
  messagesCount,
@@ -87,8 +115,21 @@ export async function processHistorySyncNotification(deps, notification) {
87
115
  chunkOrder: historySync.chunkOrder ?? undefined,
88
116
  progress: historySync.progress ?? undefined
89
117
  };
118
+ await flushPendingWrites(pendingWrites);
90
119
  deps.emitEvent('history_sync_chunk', event);
91
120
  }
121
+ async function flushPendingWrites(pendingWrites) {
122
+ if (pendingWrites.length === 0) {
123
+ return;
124
+ }
125
+ const pendingCount = pendingWrites.length;
126
+ const batch = new Array(pendingCount);
127
+ for (let index = 0; index < pendingCount; index += 1) {
128
+ batch[index] = pendingWrites[index];
129
+ }
130
+ pendingWrites.length = 0;
131
+ await Promise.all(batch);
132
+ }
92
133
  async function downloadHistorySyncBlob(deps, notification) {
93
134
  if (notification.initialHistBootstrapInlinePayload) {
94
135
  return decodeProtoBytes(notification.initialHistBootstrapInlinePayload, 'initialHistBootstrapInlinePayload');
@@ -1,9 +1,16 @@
1
1
  import { parseGroupNotificationEvents } from './events/group.js';
2
- import { WA_NODE_TAGS, WA_NOTIFICATION_TYPES } from '../protocol/constants.js';
3
- import { buildInboundReceiptAckNode, buildInboundRetryReceiptAckNode } from '../transport/node/builders/message.js';
4
- import { buildNotificationAckNode } from '../transport/node/builders/pairing.js';
2
+ import { WA_DISCONNECT_REASONS, WA_NODE_TAGS, WA_NOTIFICATION_TYPES } from '../protocol/constants.js';
3
+ import { buildAckNode } from '../transport/node/builders/global.js';
5
4
  import { getFirstNodeChild, getNodeChildrenNonEmptyAttrValuesByTag } from '../transport/node/helpers.js';
6
5
  import { parseOptionalInt, toError } from '../util/primitives.js';
6
+ const FAILURE_REASON_TO_DISCONNECT = {
7
+ 401: WA_DISCONNECT_REASONS.FAILURE_NOT_AUTHORIZED,
8
+ 403: WA_DISCONNECT_REASONS.FAILURE_LOCKED,
9
+ 406: WA_DISCONNECT_REASONS.FAILURE_BANNED,
10
+ 405: WA_DISCONNECT_REASONS.FAILURE_CLIENT_TOO_OLD,
11
+ 409: WA_DISCONNECT_REASONS.FAILURE_BAD_USER_AGENT,
12
+ 503: WA_DISCONNECT_REASONS.FAILURE_SERVICE_UNAVAILABLE
13
+ };
7
14
  const LOGOUT_FAILURE_REASONS = new Set([401, 403, 406]);
8
15
  const DISCONNECT_FAILURE_REASONS = new Set([405, 409, 503]);
9
16
  const CORE_NOTIFICATION_TYPES = new Set([
@@ -30,6 +37,8 @@ const OUT_OF_SCOPE_NOTIFICATION_TYPES = new Set([
30
37
  'waffle',
31
38
  'hosted'
32
39
  ]);
40
+ const NOTIFICATION_TYPES_WITH_PARTICIPANT_ACK = new Set(['mediaretry', 'psa']);
41
+ const NOTIFICATION_TYPES_WITHOUT_TYPE_ACK = new Set(['encrypt', 'devices']);
33
42
  export function createIncomingBaseEvent(node) {
34
43
  return {
35
44
  rawNode: node,
@@ -63,7 +72,9 @@ function classifyNotificationType(notificationType) {
63
72
  }
64
73
  async function applyFailureAction(options, reason, clearStoredCredentials) {
65
74
  try {
66
- await options.disconnect();
75
+ options.stopComms();
76
+ const disconnectReason = FAILURE_REASON_TO_DISCONNECT[reason] ?? WA_DISCONNECT_REASONS.STREAM_ERROR_OTHER;
77
+ await options.disconnect(disconnectReason, clearStoredCredentials, reason);
67
78
  if (clearStoredCredentials) {
68
79
  await options.clearStoredCredentials();
69
80
  }
@@ -108,11 +119,19 @@ export function createIncomingReceiptHandler(options) {
108
119
  await options.handleIncomingRetryReceipt(node);
109
120
  }
110
121
  else {
111
- await sendSafeAck(options.logger, options.sendNode, buildInboundRetryReceiptAckNode(node));
122
+ await sendSafeAck(options.logger, options.sendNode, buildAckNode({
123
+ kind: 'receipt',
124
+ node,
125
+ retryType: true
126
+ }));
112
127
  }
113
128
  return true;
114
129
  }
115
- await sendSafeAck(options.logger, options.sendNode, buildInboundReceiptAckNode(node));
130
+ await sendSafeAck(options.logger, options.sendNode, buildAckNode({
131
+ kind: 'receipt',
132
+ node,
133
+ includeParticipant: receiptType !== 'server-error'
134
+ }));
116
135
  return true;
117
136
  };
118
137
  }
@@ -138,6 +157,8 @@ export function createIncomingFailureHandler(options) {
138
157
  export function createIncomingNotificationHandler(options) {
139
158
  return async (node) => {
140
159
  const notificationType = node.attrs.type ?? '';
160
+ const includeParticipantInAck = NOTIFICATION_TYPES_WITH_PARTICIPANT_ACK.has(notificationType);
161
+ const includeTypeInAck = !NOTIFICATION_TYPES_WITHOUT_TYPE_ACK.has(notificationType);
141
162
  const classification = classifyNotificationType(notificationType);
142
163
  const firstChildTag = getFirstNodeChild(node)?.tag;
143
164
  const baseEvent = createIncomingBaseEvent(node);
@@ -172,7 +193,12 @@ export function createIncomingNotificationHandler(options) {
172
193
  reason: `notification.${notificationType || 'unknown'}.not_supported`
173
194
  });
174
195
  }
175
- await sendSafeAck(options.logger, options.sendNode, buildNotificationAckNode(node));
196
+ await sendSafeAck(options.logger, options.sendNode, buildAckNode({
197
+ kind: 'notification',
198
+ node,
199
+ includeParticipant: includeParticipantInAck,
200
+ includeType: includeTypeInAck
201
+ }));
176
202
  if (notificationType === 'server_sync' && serverSyncCollections.length > 0) {
177
203
  const collectionsCsv = serverSyncCollections.join(',');
178
204
  if (!options.syncAppState) {
@@ -210,7 +236,11 @@ export function createIncomingGroupNotificationHandler(options) {
210
236
  reason: `notification.${WA_NOTIFICATION_TYPES.GROUP}.empty`
211
237
  });
212
238
  }
213
- await sendSafeAck(options.logger, options.sendNode, buildNotificationAckNode(node));
239
+ await sendSafeAck(options.logger, options.sendNode, buildAckNode({
240
+ kind: 'notification',
241
+ node,
242
+ includeParticipant: true
243
+ }));
214
244
  return true;
215
245
  };
216
246
  }
@@ -1,15 +1,20 @@
1
1
  import { proto } from '../proto.js';
2
2
  import { toError } from '../util/primitives.js';
3
- async function persistContacts(contactStore, event, nowMs) {
4
- const candidateJids = [event.senderJid, event.rawNode.attrs.participant].filter((jid) => !!jid);
5
- if (candidateJids.length === 0) {
3
+ function persistContacts(writeBehind, event, nowMs) {
4
+ const senderJid = event.senderJid;
5
+ const participantJid = event.rawNode.attrs.participant;
6
+ if (!senderJid && !participantJid) {
6
7
  return;
7
8
  }
8
- const contacts = [...new Set(candidateJids)].map((jid) => ({ jid, lastUpdatedMs: nowMs }));
9
- await contactStore.upsertBatch(contacts);
9
+ if (senderJid) {
10
+ writeBehind.persistContact({ jid: senderJid, lastUpdatedMs: nowMs });
11
+ }
12
+ if (participantJid && participantJid !== senderJid) {
13
+ writeBehind.persistContact({ jid: participantJid, lastUpdatedMs: nowMs });
14
+ }
10
15
  }
11
- export async function persistIncomingMailboxEntities(options) {
12
- const { logger, contactStore, messageStore, event } = options;
16
+ export function persistIncomingMailboxEntities(options) {
17
+ const { logger, writeBehind, event } = options;
13
18
  const { stanzaId, chatJid } = event;
14
19
  if (!stanzaId || !chatJid) {
15
20
  return;
@@ -19,22 +24,18 @@ export async function persistIncomingMailboxEntities(options) {
19
24
  const messageBytes = event.message
20
25
  ? proto.Message.encode(event.message).finish()
21
26
  : undefined;
22
- await Promise.all([
23
- messageStore.upsert({
24
- id: stanzaId,
25
- threadJid: chatJid,
26
- senderJid: event.senderJid,
27
- participantJid: event.rawNode.attrs.participant,
28
- fromMe: false,
29
- timestampMs: event.timestampSeconds === undefined
30
- ? undefined
31
- : event.timestampSeconds * 1000,
32
- encType: event.encryptionType,
33
- plaintext: event.plaintext,
34
- messageBytes
35
- }),
36
- persistContacts(contactStore, event, nowMs)
37
- ]);
27
+ writeBehind.persistMessage({
28
+ id: stanzaId,
29
+ threadJid: chatJid,
30
+ senderJid: event.senderJid,
31
+ participantJid: event.rawNode.attrs.participant,
32
+ fromMe: false,
33
+ timestampMs: event.timestampSeconds === undefined ? undefined : event.timestampSeconds * 1000,
34
+ encType: event.encryptionType,
35
+ plaintext: event.plaintext,
36
+ messageBytes
37
+ });
38
+ persistContacts(writeBehind, event, nowMs);
38
39
  }
39
40
  catch (error) {
40
41
  logger.warn('failed to persist incoming mailbox entities', {
@@ -1,11 +1,11 @@
1
+ import { createReadStream } from 'node:fs';
1
2
  import { parseMediaConnResponse } from '../media/conn.js';
2
3
  import { MEDIA_CONN_CACHE_GRACE_MS, MEDIA_UPLOAD_PATHS } from '../media/constants.js';
3
4
  import { WaMediaCrypto } from '../media/WaMediaCrypto.js';
4
5
  import { isSendMediaMessage } from '../message/content.js';
5
6
  import { WA_DEFAULTS } from '../protocol/constants.js';
6
7
  import { buildMediaConnIq } from '../transport/node/builders/media.js';
7
- import { bytesToBase64UrlSafe } from '../util/bytes.js';
8
- import { TEXT_DECODER, toBytesView } from '../util/bytes.js';
8
+ import { bytesToBase64UrlSafe, TEXT_DECODER, toBytesView } from '../util/bytes.js';
9
9
  import { toError } from '../util/primitives.js';
10
10
  export async function buildMediaMessageContent(options, content) {
11
11
  if (typeof content === 'string') {
@@ -31,15 +31,29 @@ export async function getMediaConn(options, forceRefresh = false) {
31
31
  options.setMediaConnCache(mediaConn);
32
32
  return mediaConn;
33
33
  }
34
+ function resolveUploadType(content) {
35
+ if (content.type === 'video' && content.gifPlayback)
36
+ return 'gif';
37
+ if (content.type === 'audio' && content.ptt)
38
+ return 'ptt';
39
+ return content.type;
40
+ }
41
+ function isReadableStream(value) {
42
+ return (!!value &&
43
+ typeof value === 'object' &&
44
+ 'pipe' in value &&
45
+ typeof value.pipe === 'function');
46
+ }
34
47
  async function buildMediaMessage(options, content) {
35
- const mediaBytes = toBytesView(content.media);
36
- const uploaded = await uploadMedia(options, content, mediaBytes);
48
+ const uploaded = isReadableStream(content.media)
49
+ ? await uploadMediaStream(options, content, content.media)
50
+ : await uploadMediaBytes(options, content, toBytesView(content.media));
37
51
  const mediaKeyTimestamp = Math.floor(Date.now() / 1000);
38
52
  const common = {
39
53
  url: uploaded.url,
40
54
  mimetype: content.mimetype,
41
55
  fileSha256: uploaded.fileSha256,
42
- fileLength: mediaBytes.byteLength,
56
+ fileLength: uploaded.fileLength,
43
57
  mediaKey: uploaded.mediaKey,
44
58
  fileEncSha256: uploaded.fileEncSha256,
45
59
  directPath: uploaded.directPath,
@@ -67,6 +81,15 @@ async function buildMediaMessage(options, content) {
67
81
  metadataUrl: uploaded.metadataUrl
68
82
  }
69
83
  };
84
+ case 'ptv':
85
+ return {
86
+ ptvMessage: {
87
+ ...common,
88
+ seconds: content.seconds,
89
+ width: content.width,
90
+ height: content.height
91
+ }
92
+ };
70
93
  case 'audio':
71
94
  return {
72
95
  audioMessage: {
@@ -93,24 +116,46 @@ async function buildMediaMessage(options, content) {
93
116
  }
94
117
  };
95
118
  default:
96
- throw new Error(`unsupported media type: ${content.type}`);
119
+ throw new Error(`unsupported media message type: ${String(content.type)}`);
120
+ }
121
+ }
122
+ function buildUploadUrl(host, uploadType, auth, fileEncSha256) {
123
+ const hashToken = bytesToBase64UrlSafe(fileEncSha256);
124
+ const uploadPath = MEDIA_UPLOAD_PATHS[uploadType];
125
+ if (!uploadPath) {
126
+ throw new Error(`unknown media upload type: ${String(uploadType)}`);
97
127
  }
128
+ return `https://${host}${uploadPath}/${hashToken}?auth=${encodeURIComponent(auth)}&token=${encodeURIComponent(hashToken)}`;
98
129
  }
99
- async function uploadMedia(options, content, mediaBytes) {
130
+ function parseUploadResponse(body, status) {
131
+ if (status < 200 || status >= 300) {
132
+ throw new Error(`media upload failed with status ${status}`);
133
+ }
134
+ let parsed;
135
+ try {
136
+ parsed = JSON.parse(TEXT_DECODER.decode(body));
137
+ }
138
+ catch (error) {
139
+ throw new Error(`media upload returned invalid json: ${toError(error).message}`);
140
+ }
141
+ if (!parsed.url || !parsed.direct_path) {
142
+ throw new Error('media upload response missing url/direct_path');
143
+ }
144
+ return {
145
+ url: parsed.url,
146
+ directPath: parsed.direct_path,
147
+ ...(parsed.metadata_url ? { metadataUrl: parsed.metadata_url } : {})
148
+ };
149
+ }
150
+ async function uploadMediaBytes(options, content, mediaBytes) {
151
+ const uploadType = resolveUploadType(content);
100
152
  const mediaKey = await WaMediaCrypto.generateMediaKey();
101
- const uploadType = content.type === 'video' && content.gifPlayback
102
- ? 'gif'
103
- : content.type === 'audio' && content.ptt
104
- ? 'ptt'
105
- : content.type;
106
153
  const [encrypted, mediaConn] = await Promise.all([
107
154
  WaMediaCrypto.encryptBytes(uploadType, mediaKey, mediaBytes),
108
155
  getMediaConn(options)
109
156
  ]);
110
157
  const selectedHost = mediaConn.hosts.find((host) => !host.isFallback)?.hostname ?? mediaConn.hosts[0].hostname;
111
- const uploadPath = MEDIA_UPLOAD_PATHS[uploadType];
112
- const hashToken = bytesToBase64UrlSafe(encrypted.fileEncSha256);
113
- const uploadUrl = `https://${selectedHost}${uploadPath}/${hashToken}?auth=${encodeURIComponent(mediaConn.auth)}&token=${encodeURIComponent(hashToken)}`;
158
+ const uploadUrl = buildUploadUrl(selectedHost, uploadType, mediaConn.auth, encrypted.fileEncSha256);
114
159
  options.logger.debug('sending media upload request', {
115
160
  mediaType: content.type,
116
161
  uploadType,
@@ -123,26 +168,57 @@ async function uploadMedia(options, content, mediaBytes) {
123
168
  contentLength: encrypted.ciphertextHmac.byteLength,
124
169
  contentType: content.mimetype
125
170
  });
126
- const uploadBody = await options.mediaTransfer.readResponseBytes(uploadResponse);
127
- if (!uploadResponse.ok) {
128
- throw new Error(`media upload failed with status ${uploadResponse.status}`);
129
- }
130
- let parsedBody;
131
- try {
132
- parsedBody = JSON.parse(TEXT_DECODER.decode(uploadBody));
133
- }
134
- catch (error) {
135
- throw new Error(`media upload returned invalid json: ${toError(error).message}`);
136
- }
137
- if (!parsedBody.url || !parsedBody.direct_path) {
138
- throw new Error('media upload response missing url/direct_path');
139
- }
171
+ const responseBody = await options.mediaTransfer.readResponseBytes(uploadResponse);
172
+ const parsed = parseUploadResponse(responseBody, uploadResponse.status);
140
173
  return {
141
- url: parsedBody.url,
142
- directPath: parsedBody.direct_path,
174
+ ...parsed,
143
175
  mediaKey,
144
176
  fileSha256: encrypted.fileSha256,
145
177
  fileEncSha256: encrypted.fileEncSha256,
146
- ...(parsedBody.metadata_url ? { metadataUrl: parsedBody.metadata_url } : {})
178
+ fileLength: mediaBytes.byteLength
147
179
  };
148
180
  }
181
+ async function uploadMediaStream(options, content, stream) {
182
+ const uploadType = resolveUploadType(content);
183
+ const mediaKey = await WaMediaCrypto.generateMediaKey();
184
+ const encResult = await WaMediaCrypto.encryptToFile(uploadType, mediaKey, stream);
185
+ let readStream;
186
+ try {
187
+ const mediaConn = await getMediaConn(options);
188
+ const selectedHost = mediaConn.hosts.find((host) => !host.isFallback)?.hostname ??
189
+ mediaConn.hosts[0].hostname;
190
+ const uploadUrl = buildUploadUrl(selectedHost, uploadType, mediaConn.auth, encResult.fileEncSha256);
191
+ options.logger.debug('sending media stream upload request', {
192
+ mediaType: content.type,
193
+ uploadType,
194
+ host: selectedHost,
195
+ encryptedSize: encResult.fileSize
196
+ });
197
+ readStream = createReadStream(encResult.filePath);
198
+ const uploadResponse = await options.mediaTransfer.uploadStream({
199
+ url: uploadUrl,
200
+ method: 'POST',
201
+ body: readStream,
202
+ contentLength: encResult.fileSize,
203
+ contentType: content.mimetype
204
+ });
205
+ const responseBody = await options.mediaTransfer.readResponseBytes(uploadResponse);
206
+ const parsed = parseUploadResponse(responseBody, uploadResponse.status);
207
+ return {
208
+ ...parsed,
209
+ mediaKey,
210
+ fileSha256: encResult.fileSha256,
211
+ fileEncSha256: encResult.fileEncSha256,
212
+ fileLength: encResult.plaintextLength
213
+ };
214
+ }
215
+ finally {
216
+ if (readStream && !readStream.closed) {
217
+ await new Promise((resolve) => {
218
+ readStream.once('close', resolve);
219
+ readStream.destroy();
220
+ });
221
+ }
222
+ await WaMediaCrypto.cleanupEncryptedFile(encResult.filePath);
223
+ }
224
+ }