@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,146 @@
1
+ /** App-state sync IQ/notification builders used by the fake server. */
2
+ import { proto } from '../../transport/protos.js';
3
+ import { buildIqResult } from './router.js';
4
+ /** Encodes `ExternalBlobReference` used by external snapshot downloads. */
5
+ export function buildExternalBlobReference(input) {
6
+ return proto.ExternalBlobReference.encode({
7
+ mediaKey: input.mediaKey,
8
+ directPath: input.directPath,
9
+ fileSha256: input.fileSha256,
10
+ fileEncSha256: input.fileEncSha256,
11
+ fileSizeBytes: input.fileSizeBytes
12
+ }).finish();
13
+ }
14
+ export function parseAppStateSyncRequest(iq) {
15
+ if (iq.tag !== 'iq')
16
+ return [];
17
+ if (!Array.isArray(iq.content))
18
+ return [];
19
+ const sync = iq.content.find((child) => child.tag === 'sync');
20
+ if (!sync || !Array.isArray(sync.content))
21
+ return [];
22
+ const out = [];
23
+ for (const child of sync.content) {
24
+ if (child.tag !== 'collection')
25
+ continue;
26
+ const name = child.attrs.name;
27
+ if (!name)
28
+ continue;
29
+ const versionAttr = child.attrs.version;
30
+ const version = versionAttr ? Number.parseInt(versionAttr, 10) : 0;
31
+ out.push({
32
+ name,
33
+ version: Number.isFinite(version) ? version : 0
34
+ });
35
+ }
36
+ return out;
37
+ }
38
+ export function buildAppStateSyncResult(iq, input = {}) {
39
+ const requests = parseAppStateSyncRequest(iq);
40
+ const versions = input.versions ?? {};
41
+ const collectionNodes = requests.map((entry) => {
42
+ const overriddenVersion = versions[entry.name];
43
+ const responseVersion = overriddenVersion ?? entry.version;
44
+ return {
45
+ tag: 'collection',
46
+ attrs: {
47
+ name: entry.name,
48
+ version: String(responseVersion),
49
+ type: 'result'
50
+ }
51
+ };
52
+ });
53
+ const result = buildIqResult(iq);
54
+ return {
55
+ ...result,
56
+ attrs: { ...result.attrs, from: 's.whatsapp.net' },
57
+ content: [
58
+ {
59
+ tag: 'sync',
60
+ attrs: {},
61
+ content: collectionNodes
62
+ }
63
+ ]
64
+ };
65
+ }
66
+ export function buildAppStateSyncFullResult(iq, input) {
67
+ const requests = parseAppStateSyncRequest(iq);
68
+ const payloadsByName = new Map();
69
+ for (const payload of input.payloads) {
70
+ payloadsByName.set(payload.name, payload);
71
+ }
72
+ const collectionNodes = requests.map((request) => {
73
+ const payload = payloadsByName.get(request.name);
74
+ if (!payload) {
75
+ return {
76
+ tag: 'collection',
77
+ attrs: {
78
+ name: request.name,
79
+ version: String(request.version),
80
+ type: 'result'
81
+ }
82
+ };
83
+ }
84
+ const children = [];
85
+ if (payload.snapshot && payload.patches) {
86
+ throw new Error(`app-state collection ${request.name}: snapshot and patches are mutually exclusive`);
87
+ }
88
+ if (payload.snapshot) {
89
+ children.push({
90
+ tag: 'snapshot',
91
+ attrs: {},
92
+ content: payload.snapshot
93
+ });
94
+ }
95
+ if (payload.patches && payload.patches.length > 0) {
96
+ children.push({
97
+ tag: 'patches',
98
+ attrs: {},
99
+ content: payload.patches.map((patchBytes) => ({
100
+ tag: 'patch',
101
+ attrs: {},
102
+ content: patchBytes
103
+ }))
104
+ });
105
+ }
106
+ const attrs = {
107
+ name: request.name,
108
+ version: String(payload.version),
109
+ type: 'result'
110
+ };
111
+ if (payload.hasMore) {
112
+ attrs.has_more_patches = 'true';
113
+ }
114
+ return {
115
+ tag: 'collection',
116
+ attrs,
117
+ content: children.length > 0 ? children : undefined
118
+ };
119
+ });
120
+ const result = buildIqResult(iq);
121
+ return {
122
+ ...result,
123
+ attrs: { ...result.attrs, from: 's.whatsapp.net' },
124
+ content: [
125
+ {
126
+ tag: 'sync',
127
+ attrs: {},
128
+ content: collectionNodes
129
+ }
130
+ ]
131
+ };
132
+ }
133
+ export function buildServerSyncNotification(input) {
134
+ return {
135
+ tag: 'notification',
136
+ attrs: {
137
+ id: input.id ?? `server-sync-${Date.now()}`,
138
+ type: 'server_sync',
139
+ from: 's.whatsapp.net'
140
+ },
141
+ content: input.collections.map((name) => ({
142
+ tag: 'collection',
143
+ attrs: { name }
144
+ }))
145
+ };
146
+ }
@@ -0,0 +1,59 @@
1
+ /** Builders/parsers for `w:biz` business profile IQs. */
2
+ import { buildIqResult } from './router.js';
3
+ export function buildBusinessProfileResult(iq, profiles) {
4
+ const result = buildIqResult(iq);
5
+ return {
6
+ ...result,
7
+ content: [
8
+ {
9
+ tag: 'business_profile',
10
+ attrs: { v: '116' },
11
+ content: profiles.map((profile) => ({
12
+ tag: 'profile',
13
+ attrs: { jid: profile.jid, tag: 'fake-profile-tag' },
14
+ content: buildBusinessProfileChildren(profile)
15
+ }))
16
+ }
17
+ ]
18
+ };
19
+ }
20
+ function buildBusinessProfileChildren(profile) {
21
+ const out = [];
22
+ if (profile.address !== undefined) {
23
+ out.push({ tag: 'address', attrs: {}, content: profile.address });
24
+ }
25
+ if (profile.description !== undefined) {
26
+ out.push({ tag: 'description', attrs: {}, content: profile.description });
27
+ }
28
+ if (profile.email !== undefined) {
29
+ out.push({ tag: 'email', attrs: {}, content: profile.email });
30
+ }
31
+ for (const website of profile.websites ?? []) {
32
+ out.push({ tag: 'website', attrs: {}, content: website });
33
+ }
34
+ if (profile.categoryIds && profile.categoryIds.length > 0) {
35
+ out.push({
36
+ tag: 'categories',
37
+ attrs: {},
38
+ content: profile.categoryIds.map((id) => ({
39
+ tag: 'category',
40
+ attrs: { id }
41
+ }))
42
+ });
43
+ }
44
+ return out;
45
+ }
46
+ export function parseGetBusinessProfileIq(iq) {
47
+ if (!Array.isArray(iq.content))
48
+ return null;
49
+ const profile = iq.content.find((child) => child.tag === 'business_profile');
50
+ if (!profile || !Array.isArray(profile.content))
51
+ return null;
52
+ const jids = [];
53
+ for (const child of profile.content) {
54
+ if (child.tag === 'profile' && child.attrs.jid) {
55
+ jids.push(child.attrs.jid);
56
+ }
57
+ }
58
+ return jids;
59
+ }
@@ -0,0 +1,19 @@
1
+ /** Parser for `urn:xmpp:whatsapp:dirty` clear IQs. */
2
+ export function parseClearDirtyBitsIq(iq) {
3
+ if (!Array.isArray(iq.content))
4
+ return null;
5
+ const out = [];
6
+ for (const child of iq.content) {
7
+ if (child.tag !== 'clean')
8
+ continue;
9
+ const type = child.attrs.type;
10
+ const timestamp = child.attrs.timestamp;
11
+ if (!type || !timestamp)
12
+ continue;
13
+ const parsed = Number.parseInt(timestamp, 10);
14
+ if (!Number.isFinite(parsed))
15
+ continue;
16
+ out.push({ type, timestamp: parsed });
17
+ }
18
+ return out.length > 0 ? out : null;
19
+ }
@@ -0,0 +1,154 @@
1
+ /** Builders/parsers for `w:g2` group IQ operations. */
2
+ import { buildIqResult } from './router.js';
3
+ export function parseGroupParticipantChangeIq(iq) {
4
+ if (!Array.isArray(iq.content))
5
+ return null;
6
+ for (const child of iq.content) {
7
+ if (child.tag === 'add' ||
8
+ child.tag === 'remove' ||
9
+ child.tag === 'promote' ||
10
+ child.tag === 'demote') {
11
+ const groupJid = iq.attrs.to;
12
+ if (!groupJid)
13
+ return null;
14
+ const participantJids = [];
15
+ if (Array.isArray(child.content)) {
16
+ for (const participant of child.content) {
17
+ if (participant.tag === 'participant' && participant.attrs.jid) {
18
+ participantJids.push(participant.attrs.jid);
19
+ }
20
+ }
21
+ }
22
+ return {
23
+ action: child.tag,
24
+ groupJid,
25
+ participantJids
26
+ };
27
+ }
28
+ }
29
+ return null;
30
+ }
31
+ export function buildGroupParticipantChangeResult(iq, action, participantJids) {
32
+ const result = buildIqResult(iq);
33
+ return {
34
+ ...result,
35
+ content: [
36
+ {
37
+ tag: action,
38
+ attrs: {},
39
+ content: participantJids.map((jid) => ({
40
+ tag: 'participant',
41
+ attrs: { jid, error: '200' }
42
+ }))
43
+ }
44
+ ]
45
+ };
46
+ }
47
+ export function parseCreateGroupIq(iq) {
48
+ if (!Array.isArray(iq.content))
49
+ return null;
50
+ const create = iq.content.find((child) => child.tag === 'create');
51
+ if (!create)
52
+ return null;
53
+ const subject = create.attrs.subject ?? '';
54
+ const participantJids = [];
55
+ let description;
56
+ if (Array.isArray(create.content)) {
57
+ for (const child of create.content) {
58
+ if (child.tag === 'participant' && child.attrs.jid) {
59
+ participantJids.push(child.attrs.jid);
60
+ }
61
+ else if (child.tag === 'description' && Array.isArray(child.content)) {
62
+ const body = child.content.find((c) => c.tag === 'body');
63
+ if (body && typeof body.content === 'string') {
64
+ description = body.content;
65
+ }
66
+ else if (body && body.content instanceof Uint8Array) {
67
+ description = new TextDecoder().decode(body.content);
68
+ }
69
+ }
70
+ }
71
+ }
72
+ return { subject, participantJids, description };
73
+ }
74
+ export function parseSetSubjectIq(iq) {
75
+ if (!Array.isArray(iq.content))
76
+ return null;
77
+ const subject = iq.content.find((child) => child.tag === 'subject');
78
+ if (!subject)
79
+ return null;
80
+ const groupJid = iq.attrs.to;
81
+ if (!groupJid)
82
+ return null;
83
+ return {
84
+ groupJid,
85
+ subject: decodeNodeContentToString(subject.content) ?? ''
86
+ };
87
+ }
88
+ export function parseSetDescriptionIq(iq) {
89
+ if (!Array.isArray(iq.content))
90
+ return null;
91
+ const desc = iq.content.find((child) => child.tag === 'description');
92
+ if (!desc)
93
+ return null;
94
+ const groupJid = iq.attrs.to;
95
+ if (!groupJid)
96
+ return null;
97
+ if (desc.attrs.delete === 'true') {
98
+ return { groupJid, description: null, descriptionId: desc.attrs.id };
99
+ }
100
+ let body = null;
101
+ if (Array.isArray(desc.content)) {
102
+ const bodyNode = desc.content.find((c) => c.tag === 'body');
103
+ if (bodyNode) {
104
+ body = decodeNodeContentToString(bodyNode.content) ?? '';
105
+ }
106
+ }
107
+ return { groupJid, description: body, descriptionId: desc.attrs.id };
108
+ }
109
+ export function parseLeaveGroupIq(iq) {
110
+ if (!Array.isArray(iq.content))
111
+ return null;
112
+ const leave = iq.content.find((child) => child.tag === 'leave');
113
+ if (!leave || !Array.isArray(leave.content))
114
+ return null;
115
+ const out = [];
116
+ for (const child of leave.content) {
117
+ if (child.tag === 'group' && child.attrs.id) {
118
+ out.push(child.attrs.id);
119
+ }
120
+ }
121
+ return out;
122
+ }
123
+ export function buildGroupMetadataNode(input) {
124
+ const children = input.participantJids.map((jid) => ({
125
+ tag: 'participant',
126
+ attrs: { jid }
127
+ }));
128
+ if (input.description !== undefined) {
129
+ children.push({
130
+ tag: 'description',
131
+ attrs: { id: input.descriptionId ?? `${Date.now()}` },
132
+ content: [{ tag: 'body', attrs: {}, content: input.description }]
133
+ });
134
+ }
135
+ return {
136
+ tag: 'group',
137
+ attrs: {
138
+ id: input.groupJid,
139
+ subject: input.subject,
140
+ creation: String(input.creationSeconds),
141
+ creator: input.creator,
142
+ s_t: String(input.creationSeconds),
143
+ s_o: input.creator
144
+ },
145
+ content: children
146
+ };
147
+ }
148
+ function decodeNodeContentToString(content) {
149
+ if (typeof content === 'string')
150
+ return content;
151
+ if (content instanceof Uint8Array)
152
+ return new TextDecoder().decode(content);
153
+ return undefined;
154
+ }
@@ -0,0 +1,14 @@
1
+ /** Builds the `newsletter/my_addons` IQ result expected by the lib. */
2
+ import { buildIqResult } from './router.js';
3
+ export function buildNewsletterMyAddonsResult(iq) {
4
+ const result = buildIqResult(iq);
5
+ return {
6
+ ...result,
7
+ content: [
8
+ {
9
+ tag: 'my_addons',
10
+ attrs: {}
11
+ }
12
+ ]
13
+ };
14
+ }
@@ -0,0 +1,88 @@
1
+ /** Builder for `encrypt` prekey-fetch IQ responses. */
2
+ export function buildPreKeyFetchResult(inboundIq, bundles) {
3
+ const id = inboundIq.attrs.id;
4
+ if (!id) {
5
+ throw new Error('cannot build prekey-fetch result without an inbound id');
6
+ }
7
+ const userNodes = bundles.map((bundle) => {
8
+ const children = [
9
+ {
10
+ tag: 'registration',
11
+ attrs: {},
12
+ content: bigEndianBytes(bundle.registrationId, 4)
13
+ },
14
+ {
15
+ tag: 'identity',
16
+ attrs: {},
17
+ content: bundle.identityPublicKey
18
+ },
19
+ {
20
+ tag: 'skey',
21
+ attrs: {},
22
+ content: [
23
+ {
24
+ tag: 'id',
25
+ attrs: {},
26
+ content: bigEndianBytes(bundle.signedPreKey.id, 3)
27
+ },
28
+ {
29
+ tag: 'value',
30
+ attrs: {},
31
+ content: bundle.signedPreKey.publicKey
32
+ },
33
+ {
34
+ tag: 'signature',
35
+ attrs: {},
36
+ content: bundle.signedPreKey.signature
37
+ }
38
+ ]
39
+ }
40
+ ];
41
+ if (bundle.oneTimePreKey) {
42
+ children.push({
43
+ tag: 'key',
44
+ attrs: {},
45
+ content: [
46
+ {
47
+ tag: 'id',
48
+ attrs: {},
49
+ content: bigEndianBytes(bundle.oneTimePreKey.id, 3)
50
+ },
51
+ {
52
+ tag: 'value',
53
+ attrs: {},
54
+ content: bundle.oneTimePreKey.publicKey
55
+ }
56
+ ]
57
+ });
58
+ }
59
+ return {
60
+ tag: 'user',
61
+ attrs: { jid: bundle.userJid },
62
+ content: children
63
+ };
64
+ });
65
+ return {
66
+ tag: 'iq',
67
+ attrs: {
68
+ id,
69
+ from: inboundIq.attrs.to ?? 's.whatsapp.net',
70
+ type: 'result'
71
+ },
72
+ content: [
73
+ {
74
+ tag: 'list',
75
+ attrs: {},
76
+ content: userNodes
77
+ }
78
+ ]
79
+ };
80
+ }
81
+ function bigEndianBytes(value, length) {
82
+ const out = new Uint8Array(length);
83
+ for (let i = length - 1; i >= 0; i -= 1) {
84
+ out[i] = value & 0xff;
85
+ value >>>= 8;
86
+ }
87
+ return out;
88
+ }
@@ -0,0 +1,26 @@
1
+ /** Parser helpers for outbound trusted-contact privacy token IQs. */
2
+ export function parsePrivacyTokenIssueIq(iq) {
3
+ if (!Array.isArray(iq.content))
4
+ return null;
5
+ const tokens = iq.content.find((child) => child.tag === 'tokens');
6
+ if (!tokens || !Array.isArray(tokens.content))
7
+ return null;
8
+ const out = [];
9
+ for (const child of tokens.content) {
10
+ if (child.tag !== 'token')
11
+ continue;
12
+ const jid = child.attrs.jid;
13
+ const t = child.attrs.t;
14
+ if (!jid || !t)
15
+ continue;
16
+ const timestampS = Number.parseInt(t, 10);
17
+ if (!Number.isFinite(timestampS))
18
+ continue;
19
+ out.push({
20
+ jid,
21
+ timestampS,
22
+ type: child.attrs.type ?? 'trusted_contact'
23
+ });
24
+ }
25
+ return out;
26
+ }
@@ -0,0 +1,123 @@
1
+ /** Builders/parsers for `privacy` and `blocklist` IQs. */
2
+ import { buildIqResult } from './router.js';
3
+ export const FAKE_DEFAULT_PRIVACY_SETTINGS = Object.freeze({
4
+ settings: Object.freeze({
5
+ readreceipts: 'all',
6
+ last: 'all',
7
+ online: 'all',
8
+ profile: 'all',
9
+ status: 'contacts',
10
+ groupadd: 'all',
11
+ calladd: 'all',
12
+ messages: 'all',
13
+ defense: 'off'
14
+ }),
15
+ disallowed: Object.freeze({
16
+ readreceipts: Object.freeze([]),
17
+ last: Object.freeze([]),
18
+ online: Object.freeze([]),
19
+ profile: Object.freeze([]),
20
+ status: Object.freeze([]),
21
+ groupadd: Object.freeze([]),
22
+ calladd: Object.freeze([]),
23
+ messages: Object.freeze([]),
24
+ defense: Object.freeze([])
25
+ })
26
+ });
27
+ export function buildPrivacySettingsResult(iq, state) {
28
+ const result = buildIqResult(iq);
29
+ return {
30
+ ...result,
31
+ content: [
32
+ {
33
+ tag: 'privacy',
34
+ attrs: {},
35
+ content: Object.keys(state.settings).map((name) => ({
36
+ tag: 'category',
37
+ attrs: { name, value: state.settings[name] }
38
+ }))
39
+ }
40
+ ]
41
+ };
42
+ }
43
+ export function buildPrivacyDisallowedListResult(iq, category, jids) {
44
+ const result = buildIqResult(iq);
45
+ return {
46
+ ...result,
47
+ content: [
48
+ {
49
+ tag: 'privacy',
50
+ attrs: {},
51
+ content: [
52
+ {
53
+ tag: 'list',
54
+ attrs: {
55
+ name: category,
56
+ value: 'contact_blacklist',
57
+ dhash: 'fake-dhash'
58
+ },
59
+ content: jids.map((jid) => ({
60
+ tag: 'user',
61
+ attrs: { jid }
62
+ }))
63
+ }
64
+ ]
65
+ }
66
+ ]
67
+ };
68
+ }
69
+ export function buildBlocklistResult(iq, jids) {
70
+ const result = buildIqResult(iq);
71
+ return {
72
+ ...result,
73
+ content: [
74
+ {
75
+ tag: 'list',
76
+ attrs: { dhash: 'fake-blocklist-dhash' },
77
+ content: jids.map((jid) => ({
78
+ tag: 'item',
79
+ attrs: { jid }
80
+ }))
81
+ }
82
+ ]
83
+ };
84
+ }
85
+ export function parsePrivacySetCategoryIq(iq) {
86
+ if (!Array.isArray(iq.content))
87
+ return null;
88
+ const privacy = iq.content.find((child) => child.tag === 'privacy');
89
+ if (!privacy || !Array.isArray(privacy.content))
90
+ return null;
91
+ const category = privacy.content.find((child) => child.tag === 'category');
92
+ if (!category)
93
+ return null;
94
+ const name = category.attrs.name;
95
+ const value = category.attrs.value;
96
+ if (!name || !value)
97
+ return null;
98
+ return { category: name, value };
99
+ }
100
+ export function parseBlocklistChangeIq(iq) {
101
+ if (!Array.isArray(iq.content))
102
+ return null;
103
+ const item = iq.content.find((child) => child.tag === 'item');
104
+ if (!item)
105
+ return null;
106
+ const jid = item.attrs.jid;
107
+ const action = item.attrs.action;
108
+ if (!jid || (action !== 'block' && action !== 'unblock'))
109
+ return null;
110
+ return { jid, action };
111
+ }
112
+ export function parsePrivacyDisallowedListGetIq(iq) {
113
+ if (!Array.isArray(iq.content))
114
+ return null;
115
+ const privacy = iq.content.find((child) => child.tag === 'privacy');
116
+ if (!privacy || !Array.isArray(privacy.content))
117
+ return null;
118
+ const list = privacy.content.find((child) => child.tag === 'list');
119
+ if (!list)
120
+ return null;
121
+ const name = list.attrs.name;
122
+ return name ?? null;
123
+ }