@zapo-js/fake-server 0.3.0 → 1.1.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 (301) hide show
  1. package/README.md +96 -8
  2. package/dist/api/AppStateSyncManager.d.ts +0 -1
  3. package/dist/api/AppStateSyncManager.js +0 -1
  4. package/dist/api/FakePairingDriver.d.ts +0 -1
  5. package/dist/api/FakePairingDriver.js +0 -1
  6. package/dist/api/FakePeer.d.ts +77 -4
  7. package/dist/api/FakePeer.js +208 -2
  8. package/dist/api/FakeServerSession.d.ts +67 -0
  9. package/dist/api/FakeServerSession.js +293 -0
  10. package/dist/api/FakeWaServer.d.ts +77 -31
  11. package/dist/api/FakeWaServer.js +140 -259
  12. package/dist/api/PreKeyDispenser.d.ts +0 -1
  13. package/dist/api/PreKeyDispenser.js +0 -1
  14. package/dist/api/Scenario.d.ts +0 -1
  15. package/dist/api/Scenario.js +0 -1
  16. package/dist/api/ServerRegistries.d.ts +0 -1
  17. package/dist/api/ServerRegistries.js +0 -1
  18. package/dist/api/iq-handlers.d.ts +1 -1
  19. package/dist/api/iq-handlers.js +23 -1
  20. package/dist/{cli.d.ts → bin.d.ts} +0 -1
  21. package/dist/bin.js +296 -0
  22. package/dist/esm/api/AppStateSyncManager.js +146 -0
  23. package/dist/esm/api/FakePairingDriver.js +31 -0
  24. package/dist/esm/api/FakePeer.js +610 -0
  25. package/dist/esm/api/FakeServerSession.js +287 -0
  26. package/dist/esm/api/FakeWaServer.js +528 -0
  27. package/dist/esm/api/PreKeyDispenser.js +93 -0
  28. package/dist/esm/api/Scenario.js +32 -0
  29. package/dist/esm/api/ServerRegistries.js +261 -0
  30. package/dist/esm/api/iq-handlers.js +515 -0
  31. package/dist/{cli.js → esm/bin.js} +70 -25
  32. package/dist/esm/index.js +26 -0
  33. package/dist/esm/infra/WaFakeConnection.js +49 -0
  34. package/dist/esm/infra/WaFakeConnectionPipeline.js +235 -0
  35. package/dist/esm/infra/WaFakeFrameSocket.js +117 -0
  36. package/dist/esm/infra/WaFakeMediaHttpsServer.js +126 -0
  37. package/dist/esm/infra/WaFakeNoiseHandshake.js +67 -0
  38. package/dist/esm/infra/WaFakeTransport.js +31 -0
  39. package/dist/esm/infra/WaFakeWsServer.js +96 -0
  40. package/dist/esm/package.json +3 -0
  41. package/dist/esm/protocol/auth/cert-chain.js +50 -0
  42. package/dist/esm/protocol/auth/client-payload-validate.js +55 -0
  43. package/dist/esm/protocol/auth/fake-primary-device.js +47 -0
  44. package/dist/esm/protocol/auth/pair-device.js +87 -0
  45. package/dist/esm/protocol/auth/success-node.js +26 -0
  46. package/dist/esm/protocol/iq/abprops.js +30 -0
  47. package/dist/esm/protocol/iq/appstate-sync.js +146 -0
  48. package/dist/esm/protocol/iq/business.js +59 -0
  49. package/dist/esm/protocol/iq/dirty-bits.js +19 -0
  50. package/dist/esm/protocol/iq/group-ops.js +154 -0
  51. package/dist/esm/protocol/iq/newsletter.js +14 -0
  52. package/dist/esm/protocol/iq/prekey-fetch.js +88 -0
  53. package/dist/esm/protocol/iq/privacy-token.js +26 -0
  54. package/dist/esm/protocol/iq/privacy.js +123 -0
  55. package/dist/esm/protocol/iq/profile.js +72 -0
  56. package/dist/esm/protocol/iq/router.js +117 -0
  57. package/dist/esm/protocol/iq/usync.js +64 -0
  58. package/dist/esm/protocol/push/app-state-key-share.js +26 -0
  59. package/dist/esm/protocol/push/call-failure.js +27 -0
  60. package/dist/esm/protocol/push/chatstate.js +23 -0
  61. package/dist/esm/protocol/push/error-stanza.js +13 -0
  62. package/dist/esm/protocol/push/history-sync.js +111 -0
  63. package/dist/esm/protocol/push/message.js +38 -0
  64. package/dist/esm/protocol/push/notification.js +29 -0
  65. package/dist/esm/protocol/push/presence.js +14 -0
  66. package/dist/esm/protocol/push/receipt.js +21 -0
  67. package/dist/esm/protocol/signal/fake-app-state-crypto.js +196 -0
  68. package/dist/esm/protocol/signal/fake-peer-double-ratchet.js +398 -0
  69. package/dist/esm/protocol/signal/fake-peer-group-recv-session.js +120 -0
  70. package/dist/esm/protocol/signal/fake-peer-identity.js +8 -0
  71. package/dist/esm/protocol/signal/fake-peer-key-bundle.js +25 -0
  72. package/dist/esm/protocol/signal/fake-sender-key.js +83 -0
  73. package/dist/esm/protocol/signal/prekey-upload.js +107 -0
  74. package/dist/esm/protocol/stream/stream-error.js +56 -0
  75. package/dist/esm/state/fake-app-state-collection.js +134 -0
  76. package/dist/esm/state/fake-media-store.js +53 -0
  77. package/dist/esm/transport/codec.js +8 -0
  78. package/dist/esm/transport/crypto.js +8 -0
  79. package/dist/esm/transport/protos.js +7 -0
  80. package/dist/esm/transport/util.js +8 -0
  81. package/dist/index.d.ts +2 -3
  82. package/dist/index.js +2 -2
  83. package/dist/infra/WaFakeConnection.d.ts +0 -1
  84. package/dist/infra/WaFakeConnection.js +0 -1
  85. package/dist/infra/WaFakeConnectionPipeline.d.ts +10 -3
  86. package/dist/infra/WaFakeConnectionPipeline.js +47 -32
  87. package/dist/infra/WaFakeFrameSocket.d.ts +0 -1
  88. package/dist/infra/WaFakeFrameSocket.js +0 -1
  89. package/dist/infra/WaFakeMediaHttpsServer.d.ts +0 -1
  90. package/dist/infra/WaFakeMediaHttpsServer.js +0 -1
  91. package/dist/infra/WaFakeNoiseHandshake.d.ts +8 -10
  92. package/dist/infra/WaFakeNoiseHandshake.js +19 -24
  93. package/dist/infra/WaFakeTransport.d.ts +2 -4
  94. package/dist/infra/WaFakeTransport.js +2 -1
  95. package/dist/infra/WaFakeWsServer.d.ts +0 -1
  96. package/dist/infra/WaFakeWsServer.js +0 -1
  97. package/dist/protocol/auth/cert-chain.d.ts +4 -1
  98. package/dist/protocol/auth/cert-chain.js +13 -3
  99. package/dist/protocol/auth/client-payload-validate.d.ts +0 -1
  100. package/dist/protocol/auth/client-payload-validate.js +0 -1
  101. package/dist/protocol/auth/fake-primary-device.d.ts +0 -1
  102. package/dist/protocol/auth/fake-primary-device.js +1 -3
  103. package/dist/protocol/auth/pair-device.d.ts +0 -1
  104. package/dist/protocol/auth/pair-device.js +4 -16
  105. package/dist/protocol/auth/success-node.d.ts +0 -1
  106. package/dist/protocol/auth/success-node.js +0 -1
  107. package/dist/protocol/iq/abprops.d.ts +0 -1
  108. package/dist/protocol/iq/abprops.js +0 -1
  109. package/dist/protocol/iq/appstate-sync.d.ts +0 -1
  110. package/dist/protocol/iq/appstate-sync.js +0 -1
  111. package/dist/protocol/iq/business.d.ts +0 -1
  112. package/dist/protocol/iq/business.js +0 -1
  113. package/dist/protocol/iq/dirty-bits.d.ts +0 -1
  114. package/dist/protocol/iq/dirty-bits.js +0 -1
  115. package/dist/protocol/iq/group-ops.d.ts +0 -1
  116. package/dist/protocol/iq/group-ops.js +0 -1
  117. package/dist/protocol/iq/newsletter.d.ts +0 -1
  118. package/dist/protocol/iq/newsletter.js +0 -1
  119. package/dist/protocol/iq/prekey-fetch.d.ts +0 -1
  120. package/dist/protocol/iq/prekey-fetch.js +0 -1
  121. package/dist/protocol/iq/privacy-token.d.ts +0 -1
  122. package/dist/protocol/iq/privacy-token.js +0 -1
  123. package/dist/protocol/iq/privacy.d.ts +0 -1
  124. package/dist/protocol/iq/privacy.js +15 -16
  125. package/dist/protocol/iq/profile.d.ts +0 -1
  126. package/dist/protocol/iq/profile.js +0 -1
  127. package/dist/protocol/iq/router.d.ts +7 -2
  128. package/dist/protocol/iq/router.js +8 -3
  129. package/dist/protocol/iq/usync.d.ts +0 -1
  130. package/dist/protocol/iq/usync.js +0 -1
  131. package/dist/protocol/push/app-state-key-share.d.ts +0 -1
  132. package/dist/protocol/push/app-state-key-share.js +0 -1
  133. package/dist/protocol/push/call-failure.d.ts +0 -1
  134. package/dist/protocol/push/call-failure.js +0 -1
  135. package/dist/protocol/push/chatstate.d.ts +0 -1
  136. package/dist/protocol/push/chatstate.js +0 -1
  137. package/dist/protocol/push/error-stanza.d.ts +0 -1
  138. package/dist/protocol/push/error-stanza.js +0 -1
  139. package/dist/protocol/push/history-sync.d.ts +0 -1
  140. package/dist/protocol/push/history-sync.js +0 -1
  141. package/dist/protocol/push/message.d.ts +0 -1
  142. package/dist/protocol/push/message.js +0 -1
  143. package/dist/protocol/push/notification.d.ts +0 -1
  144. package/dist/protocol/push/notification.js +0 -1
  145. package/dist/protocol/push/presence.d.ts +0 -1
  146. package/dist/protocol/push/presence.js +0 -1
  147. package/dist/protocol/push/receipt.d.ts +0 -1
  148. package/dist/protocol/push/receipt.js +0 -1
  149. package/dist/protocol/signal/fake-app-state-crypto.d.ts +7 -9
  150. package/dist/protocol/signal/fake-app-state-crypto.js +31 -33
  151. package/dist/protocol/signal/fake-peer-double-ratchet.d.ts +0 -1
  152. package/dist/protocol/signal/fake-peer-double-ratchet.js +23 -28
  153. package/dist/protocol/signal/fake-peer-group-recv-session.d.ts +0 -1
  154. package/dist/protocol/signal/fake-peer-group-recv-session.js +6 -10
  155. package/dist/protocol/signal/fake-peer-identity.d.ts +0 -1
  156. package/dist/protocol/signal/fake-peer-identity.js +0 -1
  157. package/dist/protocol/signal/fake-peer-key-bundle.d.ts +0 -1
  158. package/dist/protocol/signal/fake-peer-key-bundle.js +0 -1
  159. package/dist/protocol/signal/fake-sender-key.d.ts +0 -1
  160. package/dist/protocol/signal/fake-sender-key.js +5 -9
  161. package/dist/protocol/signal/prekey-upload.d.ts +0 -1
  162. package/dist/protocol/signal/prekey-upload.js +0 -1
  163. package/dist/protocol/stream/stream-error.d.ts +0 -1
  164. package/dist/protocol/stream/stream-error.js +0 -1
  165. package/dist/state/fake-app-state-collection.d.ts +0 -1
  166. package/dist/state/fake-app-state-collection.js +0 -1
  167. package/dist/state/fake-media-store.d.ts +0 -1
  168. package/dist/state/fake-media-store.js +2 -2
  169. package/dist/transport/codec.d.ts +2 -3
  170. package/dist/transport/codec.js +2 -3
  171. package/dist/transport/crypto.d.ts +5 -7
  172. package/dist/transport/crypto.js +6 -11
  173. package/dist/transport/protos.d.ts +2 -4
  174. package/dist/transport/protos.js +2 -4
  175. package/dist/transport/util.d.ts +8 -0
  176. package/dist/transport/util.js +16 -0
  177. package/package.json +26 -6
  178. package/dist/api/AppStateSyncManager.d.ts.map +0 -1
  179. package/dist/api/AppStateSyncManager.js.map +0 -1
  180. package/dist/api/FakePairingDriver.d.ts.map +0 -1
  181. package/dist/api/FakePairingDriver.js.map +0 -1
  182. package/dist/api/FakePeer.d.ts.map +0 -1
  183. package/dist/api/FakePeer.js.map +0 -1
  184. package/dist/api/FakeWaServer.d.ts.map +0 -1
  185. package/dist/api/FakeWaServer.js.map +0 -1
  186. package/dist/api/PreKeyDispenser.d.ts.map +0 -1
  187. package/dist/api/PreKeyDispenser.js.map +0 -1
  188. package/dist/api/Scenario.d.ts.map +0 -1
  189. package/dist/api/Scenario.js.map +0 -1
  190. package/dist/api/ServerRegistries.d.ts.map +0 -1
  191. package/dist/api/ServerRegistries.js.map +0 -1
  192. package/dist/api/iq-handlers.d.ts.map +0 -1
  193. package/dist/api/iq-handlers.js.map +0 -1
  194. package/dist/cli.d.ts.map +0 -1
  195. package/dist/cli.js.map +0 -1
  196. package/dist/index.d.ts.map +0 -1
  197. package/dist/index.js.map +0 -1
  198. package/dist/infra/WaFakeConnection.d.ts.map +0 -1
  199. package/dist/infra/WaFakeConnection.js.map +0 -1
  200. package/dist/infra/WaFakeConnectionPipeline.d.ts.map +0 -1
  201. package/dist/infra/WaFakeConnectionPipeline.js.map +0 -1
  202. package/dist/infra/WaFakeFrameSocket.d.ts.map +0 -1
  203. package/dist/infra/WaFakeFrameSocket.js.map +0 -1
  204. package/dist/infra/WaFakeMediaHttpsServer.d.ts.map +0 -1
  205. package/dist/infra/WaFakeMediaHttpsServer.js.map +0 -1
  206. package/dist/infra/WaFakeNoiseHandshake.d.ts.map +0 -1
  207. package/dist/infra/WaFakeNoiseHandshake.js.map +0 -1
  208. package/dist/infra/WaFakeTransport.d.ts.map +0 -1
  209. package/dist/infra/WaFakeTransport.js.map +0 -1
  210. package/dist/infra/WaFakeWsServer.d.ts.map +0 -1
  211. package/dist/infra/WaFakeWsServer.js.map +0 -1
  212. package/dist/protocol/auth/cert-chain.d.ts.map +0 -1
  213. package/dist/protocol/auth/cert-chain.js.map +0 -1
  214. package/dist/protocol/auth/client-payload-validate.d.ts.map +0 -1
  215. package/dist/protocol/auth/client-payload-validate.js.map +0 -1
  216. package/dist/protocol/auth/fake-primary-device.d.ts.map +0 -1
  217. package/dist/protocol/auth/fake-primary-device.js.map +0 -1
  218. package/dist/protocol/auth/pair-device.d.ts.map +0 -1
  219. package/dist/protocol/auth/pair-device.js.map +0 -1
  220. package/dist/protocol/auth/success-node.d.ts.map +0 -1
  221. package/dist/protocol/auth/success-node.js.map +0 -1
  222. package/dist/protocol/iq/abprops.d.ts.map +0 -1
  223. package/dist/protocol/iq/abprops.js.map +0 -1
  224. package/dist/protocol/iq/appstate-sync.d.ts.map +0 -1
  225. package/dist/protocol/iq/appstate-sync.js.map +0 -1
  226. package/dist/protocol/iq/business.d.ts.map +0 -1
  227. package/dist/protocol/iq/business.js.map +0 -1
  228. package/dist/protocol/iq/dirty-bits.d.ts.map +0 -1
  229. package/dist/protocol/iq/dirty-bits.js.map +0 -1
  230. package/dist/protocol/iq/group-ops.d.ts.map +0 -1
  231. package/dist/protocol/iq/group-ops.js.map +0 -1
  232. package/dist/protocol/iq/newsletter.d.ts.map +0 -1
  233. package/dist/protocol/iq/newsletter.js.map +0 -1
  234. package/dist/protocol/iq/prekey-fetch.d.ts.map +0 -1
  235. package/dist/protocol/iq/prekey-fetch.js.map +0 -1
  236. package/dist/protocol/iq/privacy-token.d.ts.map +0 -1
  237. package/dist/protocol/iq/privacy-token.js.map +0 -1
  238. package/dist/protocol/iq/privacy.d.ts.map +0 -1
  239. package/dist/protocol/iq/privacy.js.map +0 -1
  240. package/dist/protocol/iq/profile.d.ts.map +0 -1
  241. package/dist/protocol/iq/profile.js.map +0 -1
  242. package/dist/protocol/iq/router.d.ts.map +0 -1
  243. package/dist/protocol/iq/router.js.map +0 -1
  244. package/dist/protocol/iq/usync.d.ts.map +0 -1
  245. package/dist/protocol/iq/usync.js.map +0 -1
  246. package/dist/protocol/push/app-state-key-share.d.ts.map +0 -1
  247. package/dist/protocol/push/app-state-key-share.js.map +0 -1
  248. package/dist/protocol/push/call-failure.d.ts.map +0 -1
  249. package/dist/protocol/push/call-failure.js.map +0 -1
  250. package/dist/protocol/push/chatstate.d.ts.map +0 -1
  251. package/dist/protocol/push/chatstate.js.map +0 -1
  252. package/dist/protocol/push/error-stanza.d.ts.map +0 -1
  253. package/dist/protocol/push/error-stanza.js.map +0 -1
  254. package/dist/protocol/push/history-sync.d.ts.map +0 -1
  255. package/dist/protocol/push/history-sync.js.map +0 -1
  256. package/dist/protocol/push/message.d.ts.map +0 -1
  257. package/dist/protocol/push/message.js.map +0 -1
  258. package/dist/protocol/push/notification.d.ts.map +0 -1
  259. package/dist/protocol/push/notification.js.map +0 -1
  260. package/dist/protocol/push/presence.d.ts.map +0 -1
  261. package/dist/protocol/push/presence.js.map +0 -1
  262. package/dist/protocol/push/receipt.d.ts.map +0 -1
  263. package/dist/protocol/push/receipt.js.map +0 -1
  264. package/dist/protocol/signal/fake-app-state-crypto.d.ts.map +0 -1
  265. package/dist/protocol/signal/fake-app-state-crypto.js.map +0 -1
  266. package/dist/protocol/signal/fake-peer-double-ratchet.d.ts.map +0 -1
  267. package/dist/protocol/signal/fake-peer-double-ratchet.js.map +0 -1
  268. package/dist/protocol/signal/fake-peer-group-recv-session.d.ts.map +0 -1
  269. package/dist/protocol/signal/fake-peer-group-recv-session.js.map +0 -1
  270. package/dist/protocol/signal/fake-peer-identity.d.ts.map +0 -1
  271. package/dist/protocol/signal/fake-peer-identity.js.map +0 -1
  272. package/dist/protocol/signal/fake-peer-key-bundle.d.ts.map +0 -1
  273. package/dist/protocol/signal/fake-peer-key-bundle.js.map +0 -1
  274. package/dist/protocol/signal/fake-peer-recv-session.d.ts +0 -60
  275. package/dist/protocol/signal/fake-peer-recv-session.d.ts.map +0 -1
  276. package/dist/protocol/signal/fake-peer-recv-session.js +0 -284
  277. package/dist/protocol/signal/fake-peer-recv-session.js.map +0 -1
  278. package/dist/protocol/signal/fake-peer-session.d.ts +0 -87
  279. package/dist/protocol/signal/fake-peer-session.d.ts.map +0 -1
  280. package/dist/protocol/signal/fake-peer-session.js +0 -207
  281. package/dist/protocol/signal/fake-peer-session.js.map +0 -1
  282. package/dist/protocol/signal/fake-sender-key.d.ts.map +0 -1
  283. package/dist/protocol/signal/fake-sender-key.js.map +0 -1
  284. package/dist/protocol/signal/prekey-upload.d.ts.map +0 -1
  285. package/dist/protocol/signal/prekey-upload.js.map +0 -1
  286. package/dist/protocol/stream/stream-error.d.ts.map +0 -1
  287. package/dist/protocol/stream/stream-error.js.map +0 -1
  288. package/dist/state/fake-app-state-collection.d.ts.map +0 -1
  289. package/dist/state/fake-app-state-collection.js.map +0 -1
  290. package/dist/state/fake-media-store.d.ts.map +0 -1
  291. package/dist/state/fake-media-store.js.map +0 -1
  292. package/dist/transport/codec.d.ts.map +0 -1
  293. package/dist/transport/codec.js.map +0 -1
  294. package/dist/transport/crypto.d.ts.map +0 -1
  295. package/dist/transport/crypto.js.map +0 -1
  296. package/dist/transport/index.d.ts +0 -4
  297. package/dist/transport/index.d.ts.map +0 -1
  298. package/dist/transport/index.js +0 -20
  299. package/dist/transport/index.js.map +0 -1
  300. package/dist/transport/protos.d.ts.map +0 -1
  301. package/dist/transport/protos.js.map +0 -1
@@ -0,0 +1,72 @@
1
+ /** Builders/parsers for profile picture and status IQs. */
2
+ import { buildIqResult } from './router.js';
3
+ export function buildGetProfilePictureResult(iq, picture) {
4
+ const attrs = {};
5
+ if (picture.url)
6
+ attrs.url = picture.url;
7
+ if (picture.directPath)
8
+ attrs.direct_path = picture.directPath;
9
+ if (picture.id)
10
+ attrs.id = picture.id;
11
+ if (picture.type)
12
+ attrs.type = picture.type;
13
+ const result = buildIqResult(iq);
14
+ return {
15
+ ...result,
16
+ content: [
17
+ {
18
+ tag: 'picture',
19
+ attrs
20
+ }
21
+ ]
22
+ };
23
+ }
24
+ export function buildSetProfilePictureResult(iq, newId) {
25
+ const result = buildIqResult(iq);
26
+ return {
27
+ ...result,
28
+ content: [
29
+ {
30
+ tag: 'picture',
31
+ attrs: { id: newId }
32
+ }
33
+ ]
34
+ };
35
+ }
36
+ export function parseGetProfilePictureIq(iq) {
37
+ const targetJid = iq.attrs.target;
38
+ if (!targetJid)
39
+ return null;
40
+ let type = 'preview';
41
+ if (Array.isArray(iq.content)) {
42
+ const picture = iq.content.find((child) => child.tag === 'picture');
43
+ if (picture && picture.attrs.type === 'image') {
44
+ type = 'image';
45
+ }
46
+ }
47
+ return { targetJid, type };
48
+ }
49
+ export function parseSetProfilePictureIq(iq) {
50
+ if (!Array.isArray(iq.content))
51
+ return null;
52
+ const picture = iq.content.find((child) => child.tag === 'picture');
53
+ if (!picture || !(picture.content instanceof Uint8Array))
54
+ return null;
55
+ return {
56
+ targetJid: iq.attrs.target,
57
+ imageBytes: picture.content
58
+ };
59
+ }
60
+ export function parseSetStatusIq(iq) {
61
+ if (!Array.isArray(iq.content))
62
+ return null;
63
+ const status = iq.content.find((child) => child.tag === 'status');
64
+ if (!status)
65
+ return null;
66
+ if (typeof status.content === 'string')
67
+ return { text: status.content };
68
+ if (status.content instanceof Uint8Array) {
69
+ return { text: new TextDecoder().decode(status.content) };
70
+ }
71
+ return { text: '' };
72
+ }
@@ -0,0 +1,117 @@
1
+ /** IQ matcher/dispatcher used by the fake server. */
2
+ export class WaFakeIqRouter {
3
+ constructor() {
4
+ /** High-priority handlers override default ones for a test case. */
5
+ this.highPriorityHandlers = [];
6
+ this.handlers = [];
7
+ this.events = {};
8
+ }
9
+ register(handler, options = {}) {
10
+ const list = options.priority === 'high' ? this.highPriorityHandlers : this.handlers;
11
+ list.push(handler);
12
+ return () => {
13
+ const index = list.indexOf(handler);
14
+ if (index >= 0) {
15
+ list.splice(index, 1);
16
+ }
17
+ };
18
+ }
19
+ setEvents(events) {
20
+ this.events = events;
21
+ }
22
+ async route(iq) {
23
+ if (iq.tag !== 'iq') {
24
+ return null;
25
+ }
26
+ for (const handler of this.highPriorityHandlers) {
27
+ if (matches(iq, handler.matcher)) {
28
+ const response = await handler.respond(iq);
29
+ if (response !== null) {
30
+ return response;
31
+ }
32
+ }
33
+ }
34
+ for (const handler of this.handlers) {
35
+ if (matches(iq, handler.matcher)) {
36
+ const response = await handler.respond(iq);
37
+ if (response !== null) {
38
+ return response;
39
+ }
40
+ }
41
+ }
42
+ this.events.onUnhandled?.(iq);
43
+ return null;
44
+ }
45
+ }
46
+ function matches(iq, matcher) {
47
+ if (matcher.id !== undefined) {
48
+ if (iq.attrs.id !== matcher.id)
49
+ return false;
50
+ }
51
+ if (matcher.type !== undefined) {
52
+ if (iq.attrs.type !== matcher.type)
53
+ return false;
54
+ }
55
+ if (matcher.xmlns !== undefined) {
56
+ if (iq.attrs.xmlns !== matcher.xmlns)
57
+ return false;
58
+ }
59
+ if (matcher.childTag !== undefined) {
60
+ const children = Array.isArray(iq.content) ? iq.content : null;
61
+ if (!children || children.length === 0)
62
+ return false;
63
+ if (children[0].tag !== matcher.childTag)
64
+ return false;
65
+ }
66
+ return true;
67
+ }
68
+ export function buildIqResult(inbound, options = {}) {
69
+ const id = inbound.attrs.id;
70
+ if (!id) {
71
+ throw new Error('cannot build iq result for inbound iq without an id');
72
+ }
73
+ const attrs = {
74
+ type: 'result',
75
+ id
76
+ };
77
+ if (options.from !== undefined) {
78
+ attrs.from = options.from;
79
+ }
80
+ else if (typeof inbound.attrs.to === 'string') {
81
+ attrs.from = inbound.attrs.to;
82
+ }
83
+ return {
84
+ tag: 'iq',
85
+ attrs,
86
+ ...(options.content ? { content: options.content } : {})
87
+ };
88
+ }
89
+ export function buildIqError(inbound, options) {
90
+ const id = inbound.attrs.id;
91
+ if (!id) {
92
+ throw new Error('cannot build iq error for inbound iq without an id');
93
+ }
94
+ const attrs = {
95
+ type: 'error',
96
+ id
97
+ };
98
+ if (options.from !== undefined) {
99
+ attrs.from = options.from;
100
+ }
101
+ else if (typeof inbound.attrs.to === 'string') {
102
+ attrs.from = inbound.attrs.to;
103
+ }
104
+ return {
105
+ tag: 'iq',
106
+ attrs,
107
+ content: [
108
+ {
109
+ tag: 'error',
110
+ attrs: {
111
+ code: String(options.code),
112
+ ...(options.text !== undefined ? { text: options.text } : {})
113
+ }
114
+ }
115
+ ]
116
+ };
117
+ }
@@ -0,0 +1,64 @@
1
+ /** Builder for `usync` IQ result responses. */
2
+ export function buildUsyncDevicesResult(inboundIq, users) {
3
+ const id = inboundIq.attrs.id;
4
+ if (!id) {
5
+ throw new Error('cannot build usync result without an inbound id');
6
+ }
7
+ const sid = extractUsyncSid(inboundIq) ?? `usync-${id}`;
8
+ const userNodes = users.map((user) => ({
9
+ tag: 'user',
10
+ attrs: { jid: user.userJid },
11
+ content: [
12
+ {
13
+ tag: 'devices',
14
+ attrs: {},
15
+ content: [
16
+ {
17
+ tag: 'device-list',
18
+ attrs: {},
19
+ content: (user.deviceIds ?? [0]).map((deviceId) => ({
20
+ tag: 'device',
21
+ attrs: { id: String(deviceId) }
22
+ }))
23
+ }
24
+ ]
25
+ }
26
+ ]
27
+ }));
28
+ return {
29
+ tag: 'iq',
30
+ attrs: {
31
+ id,
32
+ from: inboundIq.attrs.to ?? 's.whatsapp.net',
33
+ type: 'result'
34
+ },
35
+ content: [
36
+ {
37
+ tag: 'usync',
38
+ attrs: {
39
+ sid,
40
+ mode: 'query',
41
+ last: 'true',
42
+ index: '0'
43
+ },
44
+ content: [
45
+ {
46
+ tag: 'list',
47
+ attrs: {},
48
+ content: userNodes
49
+ }
50
+ ]
51
+ }
52
+ ]
53
+ };
54
+ }
55
+ function extractUsyncSid(iq) {
56
+ if (!Array.isArray(iq.content))
57
+ return undefined;
58
+ for (const child of iq.content) {
59
+ if (child.tag === 'usync' && typeof child.attrs.sid === 'string') {
60
+ return child.attrs.sid;
61
+ }
62
+ }
63
+ return undefined;
64
+ }
@@ -0,0 +1,26 @@
1
+ /** Builder for `APP_STATE_SYNC_KEY_SHARE` protocol messages. */
2
+ import { proto } from '../../transport/protos.js';
3
+ export function buildAppStateSyncKeyShareMessage(input) {
4
+ if (input.keys.length === 0) {
5
+ throw new Error('buildAppStateSyncKeyShareMessage requires at least one key');
6
+ }
7
+ return {
8
+ protocolMessage: {
9
+ type: proto.Message.ProtocolMessage.Type.APP_STATE_SYNC_KEY_SHARE,
10
+ appStateSyncKeyShare: {
11
+ keys: input.keys.map((key) => ({
12
+ keyId: { keyId: key.keyId },
13
+ keyData: {
14
+ keyData: key.keyData,
15
+ timestamp: key.timestamp ?? Date.now(),
16
+ fingerprint: {
17
+ rawId: key.fingerprint?.rawId ?? 0,
18
+ currentIndex: key.fingerprint?.currentIndex ?? 0,
19
+ deviceIndexes: [...(key.fingerprint?.deviceIndexes ?? [])]
20
+ }
21
+ }
22
+ }))
23
+ }
24
+ }
25
+ };
26
+ }
@@ -0,0 +1,27 @@
1
+ export function buildCall(input) {
2
+ const attrs = {
3
+ id: input.id,
4
+ from: input.from
5
+ };
6
+ if (input.to !== undefined)
7
+ attrs.to = input.to;
8
+ if (input.t !== undefined)
9
+ attrs.t = String(input.t);
10
+ return {
11
+ tag: 'call',
12
+ attrs,
13
+ ...(input.children ? { content: input.children } : {})
14
+ };
15
+ }
16
+ export function buildFailure(input) {
17
+ const attrs = {
18
+ reason: input.reason,
19
+ ...(input.extraAttrs ?? {})
20
+ };
21
+ if (input.location !== undefined)
22
+ attrs.location = input.location;
23
+ return {
24
+ tag: 'failure',
25
+ attrs
26
+ };
27
+ }
@@ -0,0 +1,23 @@
1
+ /** Builder for inbound `<chatstate/>` stanzas. */
2
+ export function buildChatstate(input) {
3
+ const attrs = { from: input.from };
4
+ if (input.participant !== undefined) {
5
+ attrs.participant = input.participant;
6
+ }
7
+ let stateChild;
8
+ if (input.state.kind === 'composing') {
9
+ const childAttrs = {};
10
+ if (input.state.media !== undefined) {
11
+ childAttrs.media = input.state.media;
12
+ }
13
+ stateChild = { tag: 'composing', attrs: childAttrs };
14
+ }
15
+ else {
16
+ stateChild = { tag: 'paused', attrs: {} };
17
+ }
18
+ return {
19
+ tag: 'chatstate',
20
+ attrs,
21
+ content: [stateChild]
22
+ };
23
+ }
@@ -0,0 +1,13 @@
1
+ export function buildIncomingErrorStanza(input) {
2
+ const attrs = {
3
+ code: String(input.code)
4
+ };
5
+ if (input.text !== undefined)
6
+ attrs.text = input.text;
7
+ if (input.from !== undefined)
8
+ attrs.from = input.from;
9
+ return {
10
+ tag: 'error',
11
+ attrs
12
+ };
13
+ }
@@ -0,0 +1,111 @@
1
+ /** History-sync message builders (inline and external blob variants). */
2
+ import { promisify } from 'node:util';
3
+ import { deflate } from 'node:zlib';
4
+ import { proto } from '../../transport/protos.js';
5
+ const deflateAsync = promisify(deflate);
6
+ export async function buildHistorySyncMessage(input = {}) {
7
+ const syncType = input.syncType ?? proto.Message.HistorySyncType.INITIAL_BOOTSTRAP;
8
+ const conversations = (input.conversations ?? []).map((conversation) => ({
9
+ id: conversation.id,
10
+ name: conversation.name,
11
+ unreadCount: conversation.unreadCount,
12
+ archived: conversation.archived,
13
+ pinned: conversation.pinned,
14
+ muteEndTime: conversation.muteEndTime,
15
+ markedAsUnread: conversation.markedAsUnread,
16
+ ephemeralExpiration: conversation.ephemeralExpiration,
17
+ messages: (conversation.messages ?? []).map((histMsg) => ({
18
+ message: {
19
+ key: {
20
+ id: histMsg.id,
21
+ fromMe: histMsg.fromMe ?? false,
22
+ remoteJid: conversation.id,
23
+ participant: histMsg.participant
24
+ },
25
+ messageTimestamp: histMsg.timestamp,
26
+ message: histMsg.message
27
+ }
28
+ }))
29
+ }));
30
+ const pushnames = (input.pushnames ?? []).map((entry) => ({
31
+ id: entry.id,
32
+ pushname: entry.pushname
33
+ }));
34
+ const historySync = proto.HistorySync.encode({
35
+ syncType: syncType,
36
+ chunkOrder: input.chunkOrder ?? 0,
37
+ progress: input.progress ?? 100,
38
+ conversations,
39
+ pushnames,
40
+ nctSalt: input.nctSalt
41
+ }).finish();
42
+ const compressed = await deflateAsync(historySync);
43
+ return {
44
+ protocolMessage: {
45
+ type: proto.Message.ProtocolMessage.Type.HISTORY_SYNC_NOTIFICATION,
46
+ historySyncNotification: {
47
+ syncType,
48
+ chunkOrder: input.chunkOrder ?? 0,
49
+ progress: input.progress ?? 100,
50
+ initialHistBootstrapInlinePayload: new Uint8Array(compressed)
51
+ }
52
+ }
53
+ };
54
+ }
55
+ export async function encodeHistorySyncPlaintext(input = {}) {
56
+ const syncType = input.syncType ?? proto.Message.HistorySyncType.INITIAL_BOOTSTRAP;
57
+ const conversations = (input.conversations ?? []).map((conversation) => ({
58
+ id: conversation.id,
59
+ name: conversation.name,
60
+ unreadCount: conversation.unreadCount,
61
+ archived: conversation.archived,
62
+ pinned: conversation.pinned,
63
+ muteEndTime: conversation.muteEndTime,
64
+ markedAsUnread: conversation.markedAsUnread,
65
+ ephemeralExpiration: conversation.ephemeralExpiration,
66
+ messages: (conversation.messages ?? []).map((histMsg) => ({
67
+ message: {
68
+ key: {
69
+ id: histMsg.id,
70
+ fromMe: histMsg.fromMe ?? false,
71
+ remoteJid: conversation.id,
72
+ participant: histMsg.participant
73
+ },
74
+ messageTimestamp: histMsg.timestamp,
75
+ message: histMsg.message
76
+ }
77
+ }))
78
+ }));
79
+ const pushnames = (input.pushnames ?? []).map((entry) => ({
80
+ id: entry.id,
81
+ pushname: entry.pushname
82
+ }));
83
+ const historySync = proto.HistorySync.encode({
84
+ syncType: syncType,
85
+ chunkOrder: input.chunkOrder ?? 0,
86
+ progress: input.progress ?? 100,
87
+ conversations,
88
+ pushnames,
89
+ nctSalt: input.nctSalt
90
+ }).finish();
91
+ const compressed = await deflateAsync(historySync);
92
+ return new Uint8Array(compressed);
93
+ }
94
+ export function buildHistorySyncExternalMessage(input) {
95
+ const syncType = input.syncType ?? proto.Message.HistorySyncType.INITIAL_BOOTSTRAP;
96
+ return {
97
+ protocolMessage: {
98
+ type: proto.Message.ProtocolMessage.Type.HISTORY_SYNC_NOTIFICATION,
99
+ historySyncNotification: {
100
+ syncType,
101
+ chunkOrder: input.chunkOrder ?? 0,
102
+ progress: input.progress ?? 100,
103
+ directPath: input.directPath,
104
+ mediaKey: input.mediaKey,
105
+ fileSha256: input.fileSha256,
106
+ fileEncSha256: input.fileEncSha256,
107
+ fileLength: input.fileLength
108
+ }
109
+ }
110
+ };
111
+ }
@@ -0,0 +1,38 @@
1
+ /** Builder for inbound encrypted `<message/>` stanzas. */
2
+ export function buildMessage(input) {
3
+ const attrs = {
4
+ id: input.id,
5
+ from: input.from,
6
+ t: String(input.t ?? Math.floor(Date.now() / 1000)),
7
+ type: input.type ?? 'text'
8
+ };
9
+ if (input.participant !== undefined)
10
+ attrs.participant = input.participant;
11
+ if (input.notify !== undefined)
12
+ attrs.notify = input.notify;
13
+ if (input.offline !== undefined)
14
+ attrs.offline = String(input.offline);
15
+ const children = input.enc.map((enc) => {
16
+ const encAttrs = {
17
+ v: '2',
18
+ type: enc.type
19
+ };
20
+ if (enc.mediatype !== undefined)
21
+ encAttrs.mediatype = enc.mediatype;
22
+ return {
23
+ tag: 'enc',
24
+ attrs: encAttrs,
25
+ content: enc.ciphertext
26
+ };
27
+ });
28
+ if (input.extraChildren) {
29
+ for (const extra of input.extraChildren) {
30
+ children.push(extra);
31
+ }
32
+ }
33
+ return {
34
+ tag: 'message',
35
+ attrs,
36
+ content: children
37
+ };
38
+ }
@@ -0,0 +1,29 @@
1
+ /** Builders for inbound notification stanzas. */
2
+ export function buildNotification(input) {
3
+ const attrs = {
4
+ id: input.id,
5
+ type: input.type,
6
+ from: input.from ?? 's.whatsapp.net',
7
+ ...(input.extraAttrs ?? {})
8
+ };
9
+ if (input.t !== undefined)
10
+ attrs.t = String(input.t);
11
+ if (input.participant !== undefined)
12
+ attrs.participant = input.participant;
13
+ return {
14
+ tag: 'notification',
15
+ attrs,
16
+ ...(input.content ? { content: input.content } : {})
17
+ };
18
+ }
19
+ export function buildGroupNotification(input) {
20
+ // Group notifications use wire type `w:gp2`.
21
+ return buildNotification({
22
+ id: input.id,
23
+ type: 'w:gp2',
24
+ from: input.groupJid,
25
+ participant: input.participant,
26
+ t: input.t,
27
+ content: input.children
28
+ });
29
+ }
@@ -0,0 +1,14 @@
1
+ /** Builder for inbound `<presence/>` stanzas. */
2
+ export function buildIncomingPresence(input) {
3
+ const attrs = {
4
+ from: input.from,
5
+ type: input.type ?? 'available'
6
+ };
7
+ if (input.last !== undefined) {
8
+ attrs.last = typeof input.last === 'number' ? String(input.last) : input.last;
9
+ }
10
+ return {
11
+ tag: 'presence',
12
+ attrs
13
+ };
14
+ }
@@ -0,0 +1,21 @@
1
+ /** Builder for inbound `<receipt/>` stanzas. */
2
+ export function buildReceipt(input) {
3
+ const attrs = {
4
+ id: input.id,
5
+ from: input.from
6
+ };
7
+ if (input.type !== undefined && input.type !== 'delivery') {
8
+ attrs.type = input.type;
9
+ }
10
+ if (input.t !== undefined)
11
+ attrs.t = String(input.t);
12
+ if (input.participant !== undefined)
13
+ attrs.participant = input.participant;
14
+ if (input.recipient !== undefined)
15
+ attrs.recipient = input.recipient;
16
+ return {
17
+ tag: 'receipt',
18
+ attrs,
19
+ ...(input.content ? { content: input.content } : {})
20
+ };
21
+ }