zapo-js 0.1.1 → 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 (363) hide show
  1. package/README.md +8 -0
  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 +26 -5
  354. package/dist/crypto/core/encoding.js +0 -29
  355. package/dist/esm/crypto/core/encoding.js +0 -25
  356. package/dist/esm/util/base64.js +0 -18
  357. package/dist/esm/util/signal-address.js +0 -5
  358. package/dist/types/crypto/core/encoding.d.ts +0 -11
  359. package/dist/types/util/base64.d.ts +0 -4
  360. package/dist/types/util/signal-address.d.ts +0 -2
  361. package/dist/util/base64.js +0 -24
  362. package/dist/util/signal-address.js +0 -8
  363. /package/dist/types/transport/node/builders/{accountSync.d.ts → account-sync.d.ts} +0 -0
@@ -1,11 +1,21 @@
1
+ import { signalAddressKey } from '../../../protocol/jid.js';
1
2
  import { decodeSignalPreKeyRow, decodeSignalRegistrationRow, decodeSignalRemoteIdentity, decodeSignalSessionRecord, decodeSignalSignedPreKeyRow, encodeSignalSessionRecord, toSignalAddressParts } from '../../../signal/store/sqlite.js';
2
3
  import { BaseSqliteStore } from '../../providers/sqlite/BaseSqliteStore.js';
3
4
  import { asNumber, asOptionalNumber, asString, toBoolOrUndef, resolvePositive } from '../../../util/coercion.js';
4
- import { signalAddressKey } from '../../../util/signal-address.js';
5
5
  const DEFAULTS = Object.freeze({
6
6
  preKeyBatchSize: 500,
7
7
  hasSessionBatchSize: 250
8
8
  });
9
+ function repeatSqlToken(token, count, separator) {
10
+ if (count <= 1) {
11
+ return token;
12
+ }
13
+ let out = token;
14
+ for (let index = 1; index < count; index += 1) {
15
+ out += separator + token;
16
+ }
17
+ return out;
18
+ }
9
19
  export class WaSignalSqliteStore extends BaseSqliteStore {
10
20
  constructor(options, storeOptions = {}) {
11
21
  super(options, ['signal']);
@@ -141,7 +151,7 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
141
151
  for (let start = 0; start < uniqueKeyIds.length; start += this.preKeyBatchSize) {
142
152
  const end = Math.min(start + this.preKeyBatchSize, uniqueKeyIds.length);
143
153
  const batchLength = end - start;
144
- const placeholders = new Array(batchLength).fill('?').join(', ');
154
+ const placeholders = repeatSqlToken('?', batchLength, ', ');
145
155
  const params = [this.options.sessionId];
146
156
  for (let index = start; index < end; index += 1) {
147
157
  params.push(uniqueKeyIds[index]);
@@ -213,9 +223,7 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
213
223
  for (let start = 0; start < targets.length; start += this.hasSessionBatchSize) {
214
224
  const end = Math.min(start + this.hasSessionBatchSize, targets.length);
215
225
  const batchLength = end - start;
216
- const filters = new Array(batchLength)
217
- .fill('(user = ? AND server = ? AND device = ?)')
218
- .join(' OR ');
226
+ const filters = repeatSqlToken('(user = ? AND server = ? AND device = ?)', batchLength, ' OR ');
219
227
  const params = [this.options.sessionId];
220
228
  for (let index = start; index < end; index += 1) {
221
229
  const target = targets[index];
@@ -232,7 +240,7 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
232
240
  }));
233
241
  }
234
242
  }
235
- return addresses.map((address) => existingKeys.has(signalAddressKey(address)));
243
+ return targets.map((target) => existingKeys.has(signalAddressKey(target)));
236
244
  }
237
245
  async getSession(address) {
238
246
  const db = await this.getConnection();
@@ -242,24 +250,50 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
242
250
  WHERE session_id = ? AND user = ? AND server = ? AND device = ?`, [this.options.sessionId, target.user, target.server, target.device]);
243
251
  return row ? decodeSignalSessionRecord(row.record) : null;
244
252
  }
253
+ async getSessionsBatch(addresses) {
254
+ if (addresses.length === 0) {
255
+ return [];
256
+ }
257
+ const db = await this.getConnection();
258
+ const targets = addresses.map((address) => toSignalAddressParts(address));
259
+ const byAddressKey = new Map();
260
+ for (let start = 0; start < targets.length; start += this.hasSessionBatchSize) {
261
+ const end = Math.min(start + this.hasSessionBatchSize, targets.length);
262
+ const batchLength = end - start;
263
+ const filters = repeatSqlToken('(user = ? AND server = ? AND device = ?)', batchLength, ' OR ');
264
+ const params = [this.options.sessionId];
265
+ for (let index = start; index < end; index += 1) {
266
+ const target = targets[index];
267
+ params.push(target.user, target.server, target.device);
268
+ }
269
+ const rows = db.all(`SELECT user, server, device, record
270
+ FROM signal_session
271
+ WHERE session_id = ? AND (${filters})`, params);
272
+ for (const row of rows) {
273
+ byAddressKey.set(signalAddressKey({
274
+ user: asString(row.user, 'signal_session.user'),
275
+ server: asString(row.server, 'signal_session.server'),
276
+ device: asNumber(row.device, 'signal_session.device')
277
+ }), decodeSignalSessionRecord(row.record));
278
+ }
279
+ }
280
+ return targets.map((target) => byAddressKey.get(signalAddressKey(target)) ?? null);
281
+ }
245
282
  async setSession(address, session) {
246
283
  const db = await this.getConnection();
247
284
  const target = toSignalAddressParts(address);
248
- db.run(`INSERT INTO signal_session (
249
- session_id,
250
- user,
251
- server,
252
- device,
253
- record
254
- ) VALUES (?, ?, ?, ?, ?)
255
- ON CONFLICT(session_id, user, server, device) DO UPDATE SET
256
- record=excluded.record`, [
257
- this.options.sessionId,
258
- target.user,
259
- target.server,
260
- target.device,
261
- encodeSignalSessionRecord(session)
262
- ]);
285
+ this.upsertSession(db, target, session);
286
+ }
287
+ async setSessionsBatch(entries) {
288
+ if (entries.length === 0) {
289
+ return;
290
+ }
291
+ await this.withTransaction((db) => {
292
+ for (let index = 0; index < entries.length; index += 1) {
293
+ const entry = entries[index];
294
+ this.upsertSession(db, toSignalAddressParts(entry.address), entry.session);
295
+ }
296
+ });
263
297
  }
264
298
  async deleteSession(address) {
265
299
  const db = await this.getConnection();
@@ -275,6 +309,35 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
275
309
  WHERE session_id = ? AND user = ? AND server = ? AND device = ?`, [this.options.sessionId, target.user, target.server, target.device]);
276
310
  return row ? decodeSignalRemoteIdentity(row.identity_key) : null;
277
311
  }
312
+ async getRemoteIdentities(addresses) {
313
+ if (addresses.length === 0) {
314
+ return [];
315
+ }
316
+ const db = await this.getConnection();
317
+ const targets = addresses.map((address) => toSignalAddressParts(address));
318
+ const byAddressKey = new Map();
319
+ for (let start = 0; start < targets.length; start += this.hasSessionBatchSize) {
320
+ const end = Math.min(start + this.hasSessionBatchSize, targets.length);
321
+ const batchLength = end - start;
322
+ const filters = repeatSqlToken('(user = ? AND server = ? AND device = ?)', batchLength, ' OR ');
323
+ const params = [this.options.sessionId];
324
+ for (let index = start; index < end; index += 1) {
325
+ const target = targets[index];
326
+ params.push(target.user, target.server, target.device);
327
+ }
328
+ const rows = db.all(`SELECT user, server, device, identity_key
329
+ FROM signal_identity
330
+ WHERE session_id = ? AND (${filters})`, params);
331
+ for (const row of rows) {
332
+ byAddressKey.set(signalAddressKey({
333
+ user: asString(row.user, 'signal_identity.user'),
334
+ server: asString(row.server, 'signal_identity.server'),
335
+ device: asNumber(row.device, 'signal_identity.device')
336
+ }), decodeSignalRemoteIdentity(row.identity_key));
337
+ }
338
+ }
339
+ return targets.map((target) => byAddressKey.get(signalAddressKey(target)) ?? null);
340
+ }
278
341
  async setRemoteIdentity(address, identityKey) {
279
342
  const db = await this.getConnection();
280
343
  const target = toSignalAddressParts(address);
@@ -294,7 +357,9 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
294
357
  async clear() {
295
358
  await this.withTransaction((db) => {
296
359
  db.run('DELETE FROM signal_registration WHERE session_id = ?', [this.options.sessionId]);
297
- db.run('DELETE FROM signal_signed_prekey WHERE session_id = ?', [this.options.sessionId]);
360
+ db.run('DELETE FROM signal_signed_prekey WHERE session_id = ?', [
361
+ this.options.sessionId
362
+ ]);
298
363
  db.run('DELETE FROM signal_prekey WHERE session_id = ?', [this.options.sessionId]);
299
364
  db.run('DELETE FROM signal_session WHERE session_id = ?', [this.options.sessionId]);
300
365
  db.run('DELETE FROM signal_identity WHERE session_id = ?', [this.options.sessionId]);
@@ -320,6 +385,23 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
320
385
  record.uploaded === true ? 1 : 0
321
386
  ]);
322
387
  }
388
+ upsertSession(db, target, session) {
389
+ db.run(`INSERT INTO signal_session (
390
+ session_id,
391
+ user,
392
+ server,
393
+ device,
394
+ record
395
+ ) VALUES (?, ?, ?, ?, ?)
396
+ ON CONFLICT(session_id, user, server, device) DO UPDATE SET
397
+ record=excluded.record`, [
398
+ this.options.sessionId,
399
+ target.user,
400
+ target.server,
401
+ target.device,
402
+ encodeSignalSessionRecord(session)
403
+ ]);
404
+ }
323
405
  ensureMetaRow(db) {
324
406
  db.run(`INSERT INTO signal_meta (
325
407
  session_id,
@@ -0,0 +1,107 @@
1
+ const SQLITE_TABLE_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
2
+ const WA_SQLITE_TABLE_NAME_ORDER = Object.freeze([
3
+ 'wa_migrations',
4
+ 'auth_credentials',
5
+ 'signal_meta',
6
+ 'signal_registration',
7
+ 'signal_signed_prekey',
8
+ 'signal_prekey',
9
+ 'signal_session',
10
+ 'signal_identity',
11
+ 'sender_keys',
12
+ 'sender_key_distribution',
13
+ 'appstate_sync_keys',
14
+ 'appstate_collection_versions',
15
+ 'appstate_collection_index_values',
16
+ 'retry_outbound_messages',
17
+ 'retry_inbound_counters',
18
+ 'mailbox_messages',
19
+ 'mailbox_threads',
20
+ 'mailbox_contacts',
21
+ 'group_participants_cache',
22
+ 'device_list_cache'
23
+ ]);
24
+ const WA_SQLITE_ALLOWED_TABLE_NAME_SET = new Set(WA_SQLITE_TABLE_NAME_ORDER);
25
+ const WA_SQLITE_ALLOWED_TABLE_NAME_LIST = WA_SQLITE_TABLE_NAME_ORDER.join(', ');
26
+ export const WA_SQLITE_DEFAULT_TABLE_NAMES = Object.freeze({
27
+ wa_migrations: 'wa_migrations',
28
+ auth_credentials: 'auth_credentials',
29
+ signal_meta: 'signal_meta',
30
+ signal_registration: 'signal_registration',
31
+ signal_signed_prekey: 'signal_signed_prekey',
32
+ signal_prekey: 'signal_prekey',
33
+ signal_session: 'signal_session',
34
+ signal_identity: 'signal_identity',
35
+ sender_keys: 'sender_keys',
36
+ sender_key_distribution: 'sender_key_distribution',
37
+ appstate_sync_keys: 'appstate_sync_keys',
38
+ appstate_collection_versions: 'appstate_collection_versions',
39
+ appstate_collection_index_values: 'appstate_collection_index_values',
40
+ retry_outbound_messages: 'retry_outbound_messages',
41
+ retry_inbound_counters: 'retry_inbound_counters',
42
+ mailbox_messages: 'mailbox_messages',
43
+ mailbox_threads: 'mailbox_threads',
44
+ mailbox_contacts: 'mailbox_contacts',
45
+ group_participants_cache: 'group_participants_cache',
46
+ device_list_cache: 'device_list_cache'
47
+ });
48
+ const DEFAULT_SQLITE_TABLE_NAME_SERIALIZATION = serializeSqliteTableNames(WA_SQLITE_DEFAULT_TABLE_NAMES);
49
+ function normalizeTableName(table, rawValue) {
50
+ const value = rawValue.trim();
51
+ if (value.length === 0) {
52
+ throw new Error(`sqlite tableNames.${table} must be a non-empty string`);
53
+ }
54
+ if (!SQLITE_TABLE_NAME_PATTERN.test(value)) {
55
+ throw new Error(`sqlite tableNames.${table} must match ${SQLITE_TABLE_NAME_PATTERN.toString()}`);
56
+ }
57
+ return value;
58
+ }
59
+ function assertNoDuplicateTableNames(tableNames) {
60
+ const seen = new Set();
61
+ for (const table of WA_SQLITE_TABLE_NAME_ORDER) {
62
+ const mapped = tableNames[table];
63
+ const normalizedMapped = mapped.toLowerCase();
64
+ if (seen.has(normalizedMapped)) {
65
+ throw new Error(`sqlite tableNames contains duplicate target "${mapped}"`);
66
+ }
67
+ seen.add(normalizedMapped);
68
+ }
69
+ }
70
+ export function resolveSqliteTableNames(overrides) {
71
+ if (!overrides) {
72
+ return WA_SQLITE_DEFAULT_TABLE_NAMES;
73
+ }
74
+ const entries = Object.entries(overrides);
75
+ if (entries.length === 0) {
76
+ return WA_SQLITE_DEFAULT_TABLE_NAMES;
77
+ }
78
+ const resolved = {
79
+ ...WA_SQLITE_DEFAULT_TABLE_NAMES
80
+ };
81
+ for (const [table, rawName] of entries) {
82
+ if (!WA_SQLITE_ALLOWED_TABLE_NAME_SET.has(table)) {
83
+ throw new Error(`unsupported sqlite tableNames key "${table}". Allowed table names: ${WA_SQLITE_ALLOWED_TABLE_NAME_LIST}`);
84
+ }
85
+ if (typeof rawName !== 'string') {
86
+ throw new Error(`sqlite tableNames.${table} must be a string`);
87
+ }
88
+ const tableName = table;
89
+ resolved[tableName] = normalizeTableName(tableName, rawName);
90
+ }
91
+ assertNoDuplicateTableNames(resolved);
92
+ return Object.freeze(resolved);
93
+ }
94
+ export function serializeSqliteTableNames(tableNames) {
95
+ return WA_SQLITE_TABLE_NAME_ORDER.map((table) => `${table}=${tableNames[table]}`).join(';');
96
+ }
97
+ function escapeRegexToken(value) {
98
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
99
+ }
100
+ export function createSqliteTableNameSqlResolver(tableNames) {
101
+ const serialized = serializeSqliteTableNames(tableNames);
102
+ if (serialized === DEFAULT_SQLITE_TABLE_NAME_SERIALIZATION) {
103
+ return (sql) => sql;
104
+ }
105
+ const pattern = new RegExp(`\\b(?:${WA_SQLITE_TABLE_NAME_ORDER.map(escapeRegexToken).join('|')})\\b`, 'g');
106
+ return (sql) => sql.replace(pattern, (token) => tableNames[token] ?? token);
107
+ }
@@ -20,28 +20,17 @@ export class WaThreadSqliteStore extends BaseSqliteStore {
20
20
  }
21
21
  async upsert(record) {
22
22
  const db = await this.getConnection();
23
- db.run(`INSERT INTO mailbox_threads (
24
- session_id, jid, name, unread_count, archived, pinned,
25
- mute_end_ms, marked_as_unread, ephemeral_expiration
26
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
27
- ON CONFLICT(session_id, jid) DO UPDATE SET
28
- name=COALESCE(excluded.name, mailbox_threads.name),
29
- unread_count=COALESCE(excluded.unread_count, mailbox_threads.unread_count),
30
- archived=COALESCE(excluded.archived, mailbox_threads.archived),
31
- pinned=COALESCE(excluded.pinned, mailbox_threads.pinned),
32
- mute_end_ms=COALESCE(excluded.mute_end_ms, mailbox_threads.mute_end_ms),
33
- marked_as_unread=COALESCE(excluded.marked_as_unread, mailbox_threads.marked_as_unread),
34
- ephemeral_expiration=COALESCE(excluded.ephemeral_expiration, mailbox_threads.ephemeral_expiration)`, [
35
- this.options.sessionId,
36
- record.jid,
37
- record.name ?? null,
38
- record.unreadCount ?? null,
39
- record.archived === undefined ? null : record.archived ? 1 : 0,
40
- record.pinned ?? null,
41
- record.muteEndMs ?? null,
42
- record.markedAsUnread === undefined ? null : record.markedAsUnread ? 1 : 0,
43
- record.ephemeralExpiration ?? null
44
- ]);
23
+ this.upsertThreadRow(db, record);
24
+ }
25
+ async upsertBatch(records) {
26
+ if (records.length === 0) {
27
+ return;
28
+ }
29
+ await this.withTransaction((db) => {
30
+ for (const record of records) {
31
+ this.upsertThreadRow(db, record);
32
+ }
33
+ });
45
34
  }
46
35
  async getByJid(jid) {
47
36
  const db = await this.getConnection();
@@ -69,4 +58,28 @@ export class WaThreadSqliteStore extends BaseSqliteStore {
69
58
  const db = await this.getConnection();
70
59
  db.run('DELETE FROM mailbox_threads WHERE session_id = ?', [this.options.sessionId]);
71
60
  }
61
+ upsertThreadRow(db, record) {
62
+ db.run(`INSERT INTO mailbox_threads (
63
+ session_id, jid, name, unread_count, archived, pinned,
64
+ mute_end_ms, marked_as_unread, ephemeral_expiration
65
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
66
+ ON CONFLICT(session_id, jid) DO UPDATE SET
67
+ name=COALESCE(excluded.name, mailbox_threads.name),
68
+ unread_count=COALESCE(excluded.unread_count, mailbox_threads.unread_count),
69
+ archived=COALESCE(excluded.archived, mailbox_threads.archived),
70
+ pinned=COALESCE(excluded.pinned, mailbox_threads.pinned),
71
+ mute_end_ms=COALESCE(excluded.mute_end_ms, mailbox_threads.mute_end_ms),
72
+ marked_as_unread=COALESCE(excluded.marked_as_unread, mailbox_threads.marked_as_unread),
73
+ ephemeral_expiration=COALESCE(excluded.ephemeral_expiration, mailbox_threads.ephemeral_expiration)`, [
74
+ this.options.sessionId,
75
+ record.jid,
76
+ record.name ?? null,
77
+ record.unreadCount ?? null,
78
+ record.archived === undefined ? null : record.archived ? 1 : 0,
79
+ record.pinned ?? null,
80
+ record.muteEndMs ?? null,
81
+ record.markedAsUnread === undefined ? null : record.markedAsUnread ? 1 : 0,
82
+ record.ephemeralExpiration ?? null
83
+ ]);
84
+ }
72
85
  }
@@ -3,7 +3,7 @@ import { BoundedTaskQueue, BoundedTaskQueueFullError } from '../infra/perf/Bound
3
3
  import { WA_DEFAULTS } from '../protocol/constants.js';
4
4
  import { WaNoiseSession } from './noise/WaNoiseSession.js';
5
5
  import { WaWebSocket } from './WaWebSocket.js';
6
- import { bytesToBase64UrlSafe } from '../util/base64.js';
6
+ import { bytesToBase64UrlSafe } from '../util/bytes.js';
7
7
  import { EMPTY_BYTES } from '../util/bytes.js';
8
8
  import { toError } from '../util/primitives.js';
9
9
  const WA_FRAME_HANDLER_QUEUE_MAX_SIZE = 4096;
@@ -27,6 +27,8 @@ export class WaComms {
27
27
  url: this.config.url,
28
28
  urls: this.config.urls,
29
29
  protocols: this.config.protocols,
30
+ dispatcher: this.config.dispatcher,
31
+ agent: this.config.agent,
30
32
  timeoutIntervalMs: this.config.timeoutIntervalMs
31
33
  }, logger);
32
34
  this.socket.setHandlers({
@@ -144,9 +146,7 @@ export class WaComms {
144
146
  startHandlingRequests() {
145
147
  this.handlingRequests = true;
146
148
  this.logger.debug('comms request handling enabled');
147
- this.frameProcessingQueue = this.frameProcessingQueue
148
- .catch(() => undefined)
149
- .then(async () => this.flushPendingFrames());
149
+ this.frameProcessingQueue = this.frameProcessingQueue.then(() => this.flushPendingFrames(), () => this.flushPendingFrames());
150
150
  }
151
151
  async stopComms() {
152
152
  this.logger.info('comms stop requested');
@@ -272,9 +272,7 @@ export class WaComms {
272
272
  });
273
273
  }
274
274
  onSocketMessage(payload) {
275
- this.frameProcessingQueue = this.frameProcessingQueue
276
- .catch(() => undefined)
277
- .then(async () => this.processSocketPayload(payload));
275
+ this.frameProcessingQueue = this.frameProcessingQueue.then(() => this.processSocketPayload(payload), () => this.processSocketPayload(payload));
278
276
  }
279
277
  async processSocketPayload(payload) {
280
278
  if (!this.noiseSession) {
@@ -423,20 +421,19 @@ export class WaComms {
423
421
  }
424
422
  scheduleDecodedFrame(frame) {
425
423
  void this.frameHandlerQueue
426
- .enqueue(async () => this.onDecodedFrame(frame))
424
+ .enqueue(() => this.onDecodedFrame(frame))
427
425
  .catch((error) => {
428
- const normalized = toError(error);
429
- this.logger.error('failed to enqueue decoded frame handler', {
430
- message: normalized.message
431
- });
432
- if (!(error instanceof BoundedTaskQueueFullError)) {
426
+ if (error instanceof BoundedTaskQueueFullError) {
427
+ this.logger.warn('frame handler queue is full, resuming socket to preserve bounds', {
428
+ pending: this.frameHandlerQueue.pending(),
429
+ inFlight: this.frameHandlerQueue.inFlight()
430
+ });
431
+ void this.closeSocketAndResume();
433
432
  return;
434
433
  }
435
- this.logger.warn('frame handler queue is full, processing frame inline', {
436
- pending: this.frameHandlerQueue.pending(),
437
- inFlight: this.frameHandlerQueue.inFlight()
434
+ this.logger.error('failed to enqueue decoded frame handler', {
435
+ message: toError(error).message
438
436
  });
439
- void this.onDecodedFrame(frame);
440
437
  });
441
438
  }
442
439
  scheduleReconnect() {
@@ -460,12 +457,16 @@ export class WaComms {
460
457
  this.reconnectTimer = null;
461
458
  }
462
459
  removeWaiter(reject) {
463
- const index = this.waiters.findIndex((entry) => entry.reject === reject);
464
- if (index === -1) {
460
+ for (let index = 0; index < this.waiters.length; index += 1) {
461
+ const waiter = this.waiters[index];
462
+ if (waiter.reject !== reject) {
463
+ continue;
464
+ }
465
+ clearTimeout(waiter.timer);
466
+ this.waiters[index] = this.waiters[this.waiters.length - 1];
467
+ this.waiters.pop();
465
468
  return;
466
469
  }
467
- const [waiter] = this.waiters.splice(index, 1);
468
- clearTimeout(waiter.timer);
469
470
  }
470
471
  withStickyRoutingCookie(headers) {
471
472
  const out = headers ? { ...headers } : {};
@@ -518,10 +519,11 @@ export class WaComms {
518
519
  }
519
520
  }
520
521
  drainWaiters(drain) {
521
- const waiters = this.waiters.splice(0, this.waiters.length);
522
- for (const waiter of waiters) {
522
+ for (let index = this.waiters.length - 1; index >= 0; index -= 1) {
523
+ const waiter = this.waiters[index];
523
524
  clearTimeout(waiter.timer);
524
525
  drain(waiter);
525
526
  }
527
+ this.waiters.length = 0;
526
528
  }
527
529
  }
@@ -2,6 +2,45 @@ import { ConsoleLogger } from '../infra/log/ConsoleLogger.js';
2
2
  import { WA_DEFAULTS, WA_READY_STATES } from '../protocol/constants.js';
3
3
  import { TEXT_ENCODER, toBytesView } from '../util/bytes.js';
4
4
  import { toError } from '../util/primitives.js';
5
+ const WS_OPTIONAL_MODULE = 'ws';
6
+ function asOptionalNodeWsConstructor(loaded) {
7
+ if (loaded && typeof loaded === 'object') {
8
+ const direct = loaded.WebSocket;
9
+ if (typeof direct === 'function') {
10
+ return direct;
11
+ }
12
+ const fallback = loaded.default;
13
+ if (typeof fallback === 'function') {
14
+ return fallback;
15
+ }
16
+ }
17
+ if (typeof loaded === 'function') {
18
+ return loaded;
19
+ }
20
+ return null;
21
+ }
22
+ async function loadOptionalNodeWsConstructor() {
23
+ try {
24
+ const loaded = await import(WS_OPTIONAL_MODULE);
25
+ const constructor = asOptionalNodeWsConstructor(loaded);
26
+ if (constructor) {
27
+ return constructor;
28
+ }
29
+ throw new Error('invalid ws module export');
30
+ }
31
+ catch (error) {
32
+ const normalized = toError(error);
33
+ const code = normalized.code;
34
+ const message = normalized.message ?? '';
35
+ const isModuleNotFound = (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') &&
36
+ (message.includes(`'${WS_OPTIONAL_MODULE}'`) ||
37
+ message.includes(`"${WS_OPTIONAL_MODULE}"`));
38
+ if (isModuleNotFound) {
39
+ throw new Error('optional dependency "ws" is not installed. Install with: npm i ws');
40
+ }
41
+ throw normalized;
42
+ }
43
+ }
5
44
  function resolveWebSocketConstructor() {
6
45
  const ctor = globalThis
7
46
  .WebSocket;
@@ -13,12 +52,13 @@ function resolveWebSocketConstructor() {
13
52
  function resolveSocketUrls(config) {
14
53
  const preferredUrls = config.urls;
15
54
  if (preferredUrls && preferredUrls.length > 0) {
16
- return Object.freeze(Array.from(new Set(preferredUrls)));
17
- }
18
- if (config.url) {
19
- return Object.freeze([config.url]);
55
+ const unique = [];
56
+ for (const url of preferredUrls)
57
+ if (unique.indexOf(url) === -1)
58
+ unique.push(url);
59
+ return Object.freeze(unique);
20
60
  }
21
- return WA_DEFAULTS.CHAT_SOCKET_URLS;
61
+ return config.url ? Object.freeze([config.url]) : WA_DEFAULTS.CHAT_SOCKET_URLS;
22
62
  }
23
63
  function resolveSocketRuntime() {
24
64
  const maybeNodeProcess = globalThis.process;
@@ -41,6 +81,7 @@ export class WaWebSocket {
41
81
  this.handlers = {};
42
82
  this.socket = null;
43
83
  this.closeWaiter = null;
84
+ this.nodeWsCtorPromise = null;
44
85
  }
45
86
  setHandlers(handlers) {
46
87
  this.handlers = handlers;
@@ -144,7 +185,7 @@ export class WaWebSocket {
144
185
  socket.send(data);
145
186
  }
146
187
  async openSingle(url) {
147
- const pending = this.createPendingSocket(url);
188
+ const pending = await this.createPendingSocket(url);
148
189
  return new Promise((resolve, reject) => {
149
190
  this.bindPendingSocket(pending, {
150
191
  onOpen: () => {
@@ -166,7 +207,20 @@ export class WaWebSocket {
166
207
  });
167
208
  }
168
209
  async openConcurrently(urls) {
169
- const pendingSockets = urls.map((url) => this.createPendingSocket(url));
210
+ const setupResults = await Promise.allSettled(urls.map((url) => this.createPendingSocket(url)));
211
+ const pendingSockets = [];
212
+ let setupError = null;
213
+ for (const result of setupResults) {
214
+ if (result.status === 'fulfilled') {
215
+ pendingSockets.push(result.value);
216
+ continue;
217
+ }
218
+ setupError = setupError ?? toError(result.reason);
219
+ }
220
+ if (setupError) {
221
+ this.releasePendingSocketsAfterSetupFailure(pendingSockets);
222
+ throw setupError;
223
+ }
170
224
  return new Promise((resolve, reject) => {
171
225
  let done = false;
172
226
  let failedCount = 0;
@@ -210,6 +264,14 @@ export class WaWebSocket {
210
264
  }
211
265
  });
212
266
  }
267
+ releasePendingSocketsAfterSetupFailure(entries) {
268
+ for (const entry of entries) {
269
+ if (!this.settlePendingSocket(entry)) {
270
+ continue;
271
+ }
272
+ this.closeSocketSafe(entry.socket, 1000, 'connect_setup_failed');
273
+ }
274
+ }
213
275
  bindRuntimeHandlers(socket) {
214
276
  socket.onmessage = (event) => {
215
277
  void this.handleMessage(event.data);
@@ -257,8 +319,8 @@ export class WaWebSocket {
257
319
  }
258
320
  return null;
259
321
  }
260
- createPendingSocket(url) {
261
- const socket = this.createRawSocket(url);
322
+ async createPendingSocket(url) {
323
+ const socket = await this.createRawSocket(url);
262
324
  socket.binaryType = 'arraybuffer';
263
325
  this.connectingSockets.add(socket);
264
326
  return {
@@ -351,11 +413,52 @@ export class WaWebSocket {
351
413
  // no-op
352
414
  }
353
415
  }
354
- createRawSocket(url) {
416
+ async createRawSocket(url) {
355
417
  const headers = this.config.headers;
356
- if (this.socketRuntime === 'node' && headers && Object.keys(headers).length > 0) {
357
- return new this.webSocketCtor(url, this.config.protocols, { headers });
418
+ const dispatcher = this.config.dispatcher;
419
+ const agent = this.config.agent;
420
+ let hasHeaders = false;
421
+ if (headers) {
422
+ for (const key in headers) {
423
+ if (Object.prototype.hasOwnProperty.call(headers, key)) {
424
+ hasHeaders = true;
425
+ break;
426
+ }
427
+ }
428
+ }
429
+ if (this.socketRuntime === 'node' && agent) {
430
+ const nodeWsCtor = await this.resolveNodeWsConstructor();
431
+ return new nodeWsCtor(url, this.config.protocols, {
432
+ headers,
433
+ agent
434
+ });
435
+ }
436
+ if (this.socketRuntime === 'node' && (hasHeaders || dispatcher || agent)) {
437
+ const globalWebSocketCtor = globalThis.WebSocket;
438
+ if (globalWebSocketCtor && this.webSocketCtor === globalWebSocketCtor) {
439
+ const init = {
440
+ protocols: this.config.protocols,
441
+ headers,
442
+ dispatcher,
443
+ agent
444
+ };
445
+ return new this.webSocketCtor(url, init);
446
+ }
447
+ return new this.webSocketCtor(url, this.config.protocols, {
448
+ headers,
449
+ dispatcher,
450
+ agent
451
+ });
358
452
  }
359
453
  return new this.webSocketCtor(url, this.config.protocols);
360
454
  }
455
+ async resolveNodeWsConstructor() {
456
+ if (!this.nodeWsCtorPromise) {
457
+ this.nodeWsCtorPromise = loadOptionalNodeWsConstructor().catch((error) => {
458
+ this.nodeWsCtorPromise = null;
459
+ throw error;
460
+ });
461
+ }
462
+ return this.nodeWsCtorPromise;
463
+ }
361
464
  }
@@ -238,10 +238,10 @@ function decodeNodeInternal(reader) {
238
238
  }
239
239
  }
240
240
  }
241
- if (content === null || content === undefined) {
242
- return { tag: tagValue, attrs };
243
- }
244
- return { tag: tagValue, attrs, content };
241
+ const node = content === null || content === undefined
242
+ ? { tag: tagValue, attrs }
243
+ : { tag: tagValue, attrs, content };
244
+ return node;
245
245
  }
246
246
  export function decodeBinaryNode(data) {
247
247
  const reader = new ByteReader(data);