@zapo-js/fake-server 0.3.0 → 1.0.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 (298) hide show
  1. package/README.md +3 -3
  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 +201 -1
  8. package/dist/api/FakeWaServer.d.ts +11 -1
  9. package/dist/api/FakeWaServer.js +34 -2
  10. package/dist/api/PreKeyDispenser.d.ts +0 -1
  11. package/dist/api/PreKeyDispenser.js +0 -1
  12. package/dist/api/Scenario.d.ts +0 -1
  13. package/dist/api/Scenario.js +0 -1
  14. package/dist/api/ServerRegistries.d.ts +0 -1
  15. package/dist/api/ServerRegistries.js +0 -1
  16. package/dist/api/iq-handlers.d.ts +0 -1
  17. package/dist/api/iq-handlers.js +0 -1
  18. package/dist/{cli.d.ts → bin.d.ts} +0 -1
  19. package/dist/{cli.js → bin.js} +1 -2
  20. package/dist/esm/api/AppStateSyncManager.js +146 -0
  21. package/dist/esm/api/FakePairingDriver.js +31 -0
  22. package/dist/esm/api/FakePeer.js +604 -0
  23. package/dist/esm/api/FakeWaServer.js +680 -0
  24. package/dist/esm/api/PreKeyDispenser.js +93 -0
  25. package/dist/esm/api/Scenario.js +32 -0
  26. package/dist/esm/api/ServerRegistries.js +261 -0
  27. package/dist/esm/api/iq-handlers.js +492 -0
  28. package/dist/esm/bin.js +246 -0
  29. package/dist/esm/index.js +26 -0
  30. package/dist/esm/infra/WaFakeConnection.js +49 -0
  31. package/dist/esm/infra/WaFakeConnectionPipeline.js +223 -0
  32. package/dist/esm/infra/WaFakeFrameSocket.js +117 -0
  33. package/dist/esm/infra/WaFakeMediaHttpsServer.js +126 -0
  34. package/dist/esm/infra/WaFakeNoiseHandshake.js +67 -0
  35. package/dist/esm/infra/WaFakeTransport.js +31 -0
  36. package/dist/esm/infra/WaFakeWsServer.js +96 -0
  37. package/dist/esm/package.json +3 -0
  38. package/dist/esm/protocol/auth/cert-chain.js +39 -0
  39. package/dist/esm/protocol/auth/client-payload-validate.js +55 -0
  40. package/dist/esm/protocol/auth/fake-primary-device.js +47 -0
  41. package/dist/esm/protocol/auth/pair-device.js +87 -0
  42. package/dist/esm/protocol/auth/success-node.js +26 -0
  43. package/dist/esm/protocol/iq/abprops.js +30 -0
  44. package/dist/esm/protocol/iq/appstate-sync.js +146 -0
  45. package/dist/esm/protocol/iq/business.js +59 -0
  46. package/dist/esm/protocol/iq/dirty-bits.js +19 -0
  47. package/dist/esm/protocol/iq/group-ops.js +154 -0
  48. package/dist/esm/protocol/iq/newsletter.js +14 -0
  49. package/dist/esm/protocol/iq/prekey-fetch.js +88 -0
  50. package/dist/esm/protocol/iq/privacy-token.js +26 -0
  51. package/dist/esm/protocol/iq/privacy.js +123 -0
  52. package/dist/esm/protocol/iq/profile.js +72 -0
  53. package/dist/esm/protocol/iq/router.js +111 -0
  54. package/dist/esm/protocol/iq/usync.js +64 -0
  55. package/dist/esm/protocol/push/app-state-key-share.js +26 -0
  56. package/dist/esm/protocol/push/call-failure.js +27 -0
  57. package/dist/esm/protocol/push/chatstate.js +23 -0
  58. package/dist/esm/protocol/push/error-stanza.js +13 -0
  59. package/dist/esm/protocol/push/history-sync.js +111 -0
  60. package/dist/esm/protocol/push/message.js +38 -0
  61. package/dist/esm/protocol/push/notification.js +29 -0
  62. package/dist/esm/protocol/push/presence.js +14 -0
  63. package/dist/esm/protocol/push/receipt.js +21 -0
  64. package/dist/esm/protocol/signal/fake-app-state-crypto.js +196 -0
  65. package/dist/esm/protocol/signal/fake-peer-double-ratchet.js +398 -0
  66. package/dist/esm/protocol/signal/fake-peer-group-recv-session.js +120 -0
  67. package/dist/esm/protocol/signal/fake-peer-identity.js +8 -0
  68. package/dist/esm/protocol/signal/fake-peer-key-bundle.js +25 -0
  69. package/dist/esm/protocol/signal/fake-sender-key.js +83 -0
  70. package/dist/esm/protocol/signal/prekey-upload.js +107 -0
  71. package/dist/esm/protocol/stream/stream-error.js +56 -0
  72. package/dist/esm/state/fake-app-state-collection.js +134 -0
  73. package/dist/esm/state/fake-media-store.js +53 -0
  74. package/dist/esm/transport/codec.js +8 -0
  75. package/dist/esm/transport/crypto.js +8 -0
  76. package/dist/esm/transport/protos.js +7 -0
  77. package/dist/esm/transport/util.js +8 -0
  78. package/dist/index.d.ts +0 -1
  79. package/dist/index.js +0 -1
  80. package/dist/infra/WaFakeConnection.d.ts +0 -1
  81. package/dist/infra/WaFakeConnection.js +0 -1
  82. package/dist/infra/WaFakeConnectionPipeline.d.ts +0 -1
  83. package/dist/infra/WaFakeConnectionPipeline.js +27 -24
  84. package/dist/infra/WaFakeFrameSocket.d.ts +0 -1
  85. package/dist/infra/WaFakeFrameSocket.js +0 -1
  86. package/dist/infra/WaFakeMediaHttpsServer.d.ts +0 -1
  87. package/dist/infra/WaFakeMediaHttpsServer.js +0 -1
  88. package/dist/infra/WaFakeNoiseHandshake.d.ts +8 -10
  89. package/dist/infra/WaFakeNoiseHandshake.js +19 -24
  90. package/dist/infra/WaFakeTransport.d.ts +2 -4
  91. package/dist/infra/WaFakeTransport.js +2 -1
  92. package/dist/infra/WaFakeWsServer.d.ts +0 -1
  93. package/dist/infra/WaFakeWsServer.js +0 -1
  94. package/dist/protocol/auth/cert-chain.d.ts +0 -1
  95. package/dist/protocol/auth/cert-chain.js +0 -1
  96. package/dist/protocol/auth/client-payload-validate.d.ts +0 -1
  97. package/dist/protocol/auth/client-payload-validate.js +0 -1
  98. package/dist/protocol/auth/fake-primary-device.d.ts +0 -1
  99. package/dist/protocol/auth/fake-primary-device.js +1 -3
  100. package/dist/protocol/auth/pair-device.d.ts +0 -1
  101. package/dist/protocol/auth/pair-device.js +4 -16
  102. package/dist/protocol/auth/success-node.d.ts +0 -1
  103. package/dist/protocol/auth/success-node.js +0 -1
  104. package/dist/protocol/iq/abprops.d.ts +0 -1
  105. package/dist/protocol/iq/abprops.js +0 -1
  106. package/dist/protocol/iq/appstate-sync.d.ts +0 -1
  107. package/dist/protocol/iq/appstate-sync.js +0 -1
  108. package/dist/protocol/iq/business.d.ts +0 -1
  109. package/dist/protocol/iq/business.js +0 -1
  110. package/dist/protocol/iq/dirty-bits.d.ts +0 -1
  111. package/dist/protocol/iq/dirty-bits.js +0 -1
  112. package/dist/protocol/iq/group-ops.d.ts +0 -1
  113. package/dist/protocol/iq/group-ops.js +0 -1
  114. package/dist/protocol/iq/newsletter.d.ts +0 -1
  115. package/dist/protocol/iq/newsletter.js +0 -1
  116. package/dist/protocol/iq/prekey-fetch.d.ts +0 -1
  117. package/dist/protocol/iq/prekey-fetch.js +0 -1
  118. package/dist/protocol/iq/privacy-token.d.ts +0 -1
  119. package/dist/protocol/iq/privacy-token.js +0 -1
  120. package/dist/protocol/iq/privacy.d.ts +0 -1
  121. package/dist/protocol/iq/privacy.js +15 -16
  122. package/dist/protocol/iq/profile.d.ts +0 -1
  123. package/dist/protocol/iq/profile.js +0 -1
  124. package/dist/protocol/iq/router.d.ts +0 -1
  125. package/dist/protocol/iq/router.js +0 -1
  126. package/dist/protocol/iq/usync.d.ts +0 -1
  127. package/dist/protocol/iq/usync.js +0 -1
  128. package/dist/protocol/push/app-state-key-share.d.ts +0 -1
  129. package/dist/protocol/push/app-state-key-share.js +0 -1
  130. package/dist/protocol/push/call-failure.d.ts +0 -1
  131. package/dist/protocol/push/call-failure.js +0 -1
  132. package/dist/protocol/push/chatstate.d.ts +0 -1
  133. package/dist/protocol/push/chatstate.js +0 -1
  134. package/dist/protocol/push/error-stanza.d.ts +0 -1
  135. package/dist/protocol/push/error-stanza.js +0 -1
  136. package/dist/protocol/push/history-sync.d.ts +0 -1
  137. package/dist/protocol/push/history-sync.js +0 -1
  138. package/dist/protocol/push/message.d.ts +0 -1
  139. package/dist/protocol/push/message.js +0 -1
  140. package/dist/protocol/push/notification.d.ts +0 -1
  141. package/dist/protocol/push/notification.js +0 -1
  142. package/dist/protocol/push/presence.d.ts +0 -1
  143. package/dist/protocol/push/presence.js +0 -1
  144. package/dist/protocol/push/receipt.d.ts +0 -1
  145. package/dist/protocol/push/receipt.js +0 -1
  146. package/dist/protocol/signal/fake-app-state-crypto.d.ts +7 -9
  147. package/dist/protocol/signal/fake-app-state-crypto.js +31 -33
  148. package/dist/protocol/signal/fake-peer-double-ratchet.d.ts +0 -1
  149. package/dist/protocol/signal/fake-peer-double-ratchet.js +23 -28
  150. package/dist/protocol/signal/fake-peer-group-recv-session.d.ts +0 -1
  151. package/dist/protocol/signal/fake-peer-group-recv-session.js +6 -10
  152. package/dist/protocol/signal/fake-peer-identity.d.ts +0 -1
  153. package/dist/protocol/signal/fake-peer-identity.js +0 -1
  154. package/dist/protocol/signal/fake-peer-key-bundle.d.ts +0 -1
  155. package/dist/protocol/signal/fake-peer-key-bundle.js +0 -1
  156. package/dist/protocol/signal/fake-sender-key.d.ts +0 -1
  157. package/dist/protocol/signal/fake-sender-key.js +5 -9
  158. package/dist/protocol/signal/prekey-upload.d.ts +0 -1
  159. package/dist/protocol/signal/prekey-upload.js +0 -1
  160. package/dist/protocol/stream/stream-error.d.ts +0 -1
  161. package/dist/protocol/stream/stream-error.js +0 -1
  162. package/dist/state/fake-app-state-collection.d.ts +0 -1
  163. package/dist/state/fake-app-state-collection.js +0 -1
  164. package/dist/state/fake-media-store.d.ts +0 -1
  165. package/dist/state/fake-media-store.js +2 -2
  166. package/dist/transport/codec.d.ts +2 -3
  167. package/dist/transport/codec.js +2 -3
  168. package/dist/transport/crypto.d.ts +5 -7
  169. package/dist/transport/crypto.js +6 -11
  170. package/dist/transport/protos.d.ts +2 -4
  171. package/dist/transport/protos.js +2 -4
  172. package/dist/transport/util.d.ts +8 -0
  173. package/dist/transport/util.js +15 -0
  174. package/package.json +21 -6
  175. package/dist/api/AppStateSyncManager.d.ts.map +0 -1
  176. package/dist/api/AppStateSyncManager.js.map +0 -1
  177. package/dist/api/FakePairingDriver.d.ts.map +0 -1
  178. package/dist/api/FakePairingDriver.js.map +0 -1
  179. package/dist/api/FakePeer.d.ts.map +0 -1
  180. package/dist/api/FakePeer.js.map +0 -1
  181. package/dist/api/FakeWaServer.d.ts.map +0 -1
  182. package/dist/api/FakeWaServer.js.map +0 -1
  183. package/dist/api/PreKeyDispenser.d.ts.map +0 -1
  184. package/dist/api/PreKeyDispenser.js.map +0 -1
  185. package/dist/api/Scenario.d.ts.map +0 -1
  186. package/dist/api/Scenario.js.map +0 -1
  187. package/dist/api/ServerRegistries.d.ts.map +0 -1
  188. package/dist/api/ServerRegistries.js.map +0 -1
  189. package/dist/api/iq-handlers.d.ts.map +0 -1
  190. package/dist/api/iq-handlers.js.map +0 -1
  191. package/dist/cli.d.ts.map +0 -1
  192. package/dist/cli.js.map +0 -1
  193. package/dist/index.d.ts.map +0 -1
  194. package/dist/index.js.map +0 -1
  195. package/dist/infra/WaFakeConnection.d.ts.map +0 -1
  196. package/dist/infra/WaFakeConnection.js.map +0 -1
  197. package/dist/infra/WaFakeConnectionPipeline.d.ts.map +0 -1
  198. package/dist/infra/WaFakeConnectionPipeline.js.map +0 -1
  199. package/dist/infra/WaFakeFrameSocket.d.ts.map +0 -1
  200. package/dist/infra/WaFakeFrameSocket.js.map +0 -1
  201. package/dist/infra/WaFakeMediaHttpsServer.d.ts.map +0 -1
  202. package/dist/infra/WaFakeMediaHttpsServer.js.map +0 -1
  203. package/dist/infra/WaFakeNoiseHandshake.d.ts.map +0 -1
  204. package/dist/infra/WaFakeNoiseHandshake.js.map +0 -1
  205. package/dist/infra/WaFakeTransport.d.ts.map +0 -1
  206. package/dist/infra/WaFakeTransport.js.map +0 -1
  207. package/dist/infra/WaFakeWsServer.d.ts.map +0 -1
  208. package/dist/infra/WaFakeWsServer.js.map +0 -1
  209. package/dist/protocol/auth/cert-chain.d.ts.map +0 -1
  210. package/dist/protocol/auth/cert-chain.js.map +0 -1
  211. package/dist/protocol/auth/client-payload-validate.d.ts.map +0 -1
  212. package/dist/protocol/auth/client-payload-validate.js.map +0 -1
  213. package/dist/protocol/auth/fake-primary-device.d.ts.map +0 -1
  214. package/dist/protocol/auth/fake-primary-device.js.map +0 -1
  215. package/dist/protocol/auth/pair-device.d.ts.map +0 -1
  216. package/dist/protocol/auth/pair-device.js.map +0 -1
  217. package/dist/protocol/auth/success-node.d.ts.map +0 -1
  218. package/dist/protocol/auth/success-node.js.map +0 -1
  219. package/dist/protocol/iq/abprops.d.ts.map +0 -1
  220. package/dist/protocol/iq/abprops.js.map +0 -1
  221. package/dist/protocol/iq/appstate-sync.d.ts.map +0 -1
  222. package/dist/protocol/iq/appstate-sync.js.map +0 -1
  223. package/dist/protocol/iq/business.d.ts.map +0 -1
  224. package/dist/protocol/iq/business.js.map +0 -1
  225. package/dist/protocol/iq/dirty-bits.d.ts.map +0 -1
  226. package/dist/protocol/iq/dirty-bits.js.map +0 -1
  227. package/dist/protocol/iq/group-ops.d.ts.map +0 -1
  228. package/dist/protocol/iq/group-ops.js.map +0 -1
  229. package/dist/protocol/iq/newsletter.d.ts.map +0 -1
  230. package/dist/protocol/iq/newsletter.js.map +0 -1
  231. package/dist/protocol/iq/prekey-fetch.d.ts.map +0 -1
  232. package/dist/protocol/iq/prekey-fetch.js.map +0 -1
  233. package/dist/protocol/iq/privacy-token.d.ts.map +0 -1
  234. package/dist/protocol/iq/privacy-token.js.map +0 -1
  235. package/dist/protocol/iq/privacy.d.ts.map +0 -1
  236. package/dist/protocol/iq/privacy.js.map +0 -1
  237. package/dist/protocol/iq/profile.d.ts.map +0 -1
  238. package/dist/protocol/iq/profile.js.map +0 -1
  239. package/dist/protocol/iq/router.d.ts.map +0 -1
  240. package/dist/protocol/iq/router.js.map +0 -1
  241. package/dist/protocol/iq/usync.d.ts.map +0 -1
  242. package/dist/protocol/iq/usync.js.map +0 -1
  243. package/dist/protocol/push/app-state-key-share.d.ts.map +0 -1
  244. package/dist/protocol/push/app-state-key-share.js.map +0 -1
  245. package/dist/protocol/push/call-failure.d.ts.map +0 -1
  246. package/dist/protocol/push/call-failure.js.map +0 -1
  247. package/dist/protocol/push/chatstate.d.ts.map +0 -1
  248. package/dist/protocol/push/chatstate.js.map +0 -1
  249. package/dist/protocol/push/error-stanza.d.ts.map +0 -1
  250. package/dist/protocol/push/error-stanza.js.map +0 -1
  251. package/dist/protocol/push/history-sync.d.ts.map +0 -1
  252. package/dist/protocol/push/history-sync.js.map +0 -1
  253. package/dist/protocol/push/message.d.ts.map +0 -1
  254. package/dist/protocol/push/message.js.map +0 -1
  255. package/dist/protocol/push/notification.d.ts.map +0 -1
  256. package/dist/protocol/push/notification.js.map +0 -1
  257. package/dist/protocol/push/presence.d.ts.map +0 -1
  258. package/dist/protocol/push/presence.js.map +0 -1
  259. package/dist/protocol/push/receipt.d.ts.map +0 -1
  260. package/dist/protocol/push/receipt.js.map +0 -1
  261. package/dist/protocol/signal/fake-app-state-crypto.d.ts.map +0 -1
  262. package/dist/protocol/signal/fake-app-state-crypto.js.map +0 -1
  263. package/dist/protocol/signal/fake-peer-double-ratchet.d.ts.map +0 -1
  264. package/dist/protocol/signal/fake-peer-double-ratchet.js.map +0 -1
  265. package/dist/protocol/signal/fake-peer-group-recv-session.d.ts.map +0 -1
  266. package/dist/protocol/signal/fake-peer-group-recv-session.js.map +0 -1
  267. package/dist/protocol/signal/fake-peer-identity.d.ts.map +0 -1
  268. package/dist/protocol/signal/fake-peer-identity.js.map +0 -1
  269. package/dist/protocol/signal/fake-peer-key-bundle.d.ts.map +0 -1
  270. package/dist/protocol/signal/fake-peer-key-bundle.js.map +0 -1
  271. package/dist/protocol/signal/fake-peer-recv-session.d.ts +0 -60
  272. package/dist/protocol/signal/fake-peer-recv-session.d.ts.map +0 -1
  273. package/dist/protocol/signal/fake-peer-recv-session.js +0 -284
  274. package/dist/protocol/signal/fake-peer-recv-session.js.map +0 -1
  275. package/dist/protocol/signal/fake-peer-session.d.ts +0 -87
  276. package/dist/protocol/signal/fake-peer-session.d.ts.map +0 -1
  277. package/dist/protocol/signal/fake-peer-session.js +0 -207
  278. package/dist/protocol/signal/fake-peer-session.js.map +0 -1
  279. package/dist/protocol/signal/fake-sender-key.d.ts.map +0 -1
  280. package/dist/protocol/signal/fake-sender-key.js.map +0 -1
  281. package/dist/protocol/signal/prekey-upload.d.ts.map +0 -1
  282. package/dist/protocol/signal/prekey-upload.js.map +0 -1
  283. package/dist/protocol/stream/stream-error.d.ts.map +0 -1
  284. package/dist/protocol/stream/stream-error.js.map +0 -1
  285. package/dist/state/fake-app-state-collection.d.ts.map +0 -1
  286. package/dist/state/fake-app-state-collection.js.map +0 -1
  287. package/dist/state/fake-media-store.d.ts.map +0 -1
  288. package/dist/state/fake-media-store.js.map +0 -1
  289. package/dist/transport/codec.d.ts.map +0 -1
  290. package/dist/transport/codec.js.map +0 -1
  291. package/dist/transport/crypto.d.ts.map +0 -1
  292. package/dist/transport/crypto.js.map +0 -1
  293. package/dist/transport/index.d.ts +0 -4
  294. package/dist/transport/index.d.ts.map +0 -1
  295. package/dist/transport/index.js +0 -20
  296. package/dist/transport/index.js.map +0 -1
  297. package/dist/transport/protos.d.ts.map +0 -1
  298. package/dist/transport/protos.js.map +0 -1
@@ -0,0 +1,126 @@
1
+ /** HTTPS listener used by fake media upload/download flows. */
2
+ import { createServer as createHttpsServer } from 'node:https';
3
+ const FAKE_MEDIA_TLS_KEY_PEM = `-----BEGIN PRIVATE KEY-----
4
+ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3xTRqB/wZbz/w
5
+ pQgcOfCfizS0MrtVR0ulBa1vK4OlU1FXJNC0dtTKLs/jMWNqwYWXxSoGEHHfhUIS
6
+ PgZ08nauCJs83ZFAQC1Es1sN1R32YokPmKOCTHHh8f5/uX3h5oUD/xr3gwOVs4c2
7
+ ISmk1OTmp77KolxZXHOnlnHweO6DK0CqfT+53RzKj5rz4d7inyMk5ctk+JjHZd8e
8
+ q46e4MH4ZWPM+ehwRwgWMQ2E1bFjVQsBMvtAkUxOEx+B2/Cys6jEUFo/ABkQxXt+
9
+ InqWnYKHbPOToKmpPxDjT54OpVZs4IRtbkTrgEMMz3/c5g4XamGUHLqkI0GBESK2
10
+ 16STZettAgMBAAECggEAAr6CDGOfMMzRkdyeWyKosPKMIGbY+wNiSSbsabim4BO3
11
+ LkLQgSK1d3fF3+KrDj6/DUJUti2/oZEKGoKwUn6T14BmV+7qvjX1WN8kqNsGYLXP
12
+ XFB0jinpkf6XhPKUn7olCGdwglgR28XknLBXsmG6eMK5ODgiZi0Zqng9WPCSP3Aa
13
+ 5q7iSeJF0FsbQ1OdQT3t98WhnSN9t0TzLyNUqXWDBDIrrtT3O7B7theS+3LRhodE
14
+ VFROogp4hfo6pBNweki08uFD250OP6iGpYsE0xY6NIfyebGS2iBmbDKQmMkWLdf0
15
+ mSlvYoksnDan4tJMiXAbb39wLdJEa+x98Qzxq0iJyQKBgQDeBQSgVd7j99W0bdzo
16
+ fnDgnjJS606UrWID/RyTEIMfq5Jf2sM2vTJ2/w4X1JoUxxQCJbFmoJs9RXn52XUa
17
+ 3/GaUbr3akUVjMb93JlbWTQVoKipMM6HGCee4rj69qdsmA8iJrnloGFpymLThcfm
18
+ xZB535vbG3DrYRM8MH/crDQH2QKBgQDT5YlXskOxx/iVxpko4uacIBqHnPGbCPnK
19
+ LXMaa5vrjGgWNK0ajj+0MFdpEl+HXC7ZDISmSRKExrMDaWkjoXzXhxz1BmHVdN3O
20
+ lOZY6gY+WeQUPn2sfqROBAV7B66LmRjbLdK9kwqrD3nhED6XWR+ZuXaH0dMQ2UMe
21
+ T9E1ixr3tQKBgQChrYvR6taWsosYioy9bh5rJCjHg33E/YIMH41odzTVok7EqP1R
22
+ 5nNSfqhXqEXQbazfZ22Jq9mAxBBwZvBFcuFxHKWHuQa4C715buTqxcoNGeLY2qb7
23
+ cBaiOL62W1pO1Wjn2MW1N5bYwD45hQmuvx5X8gOAirovXpDwWu0x2OFG+QKBgAxE
24
+ U4kiJs0Z+IgjMSauzA7pxN9o9Iu3H07XBrzW0fX28OeoMQVCiumRit3oVGNvsL6b
25
+ /OnrfQj2v9JIve90H9gSWFjO/8bttxGIiTVIhwgBCDUr0Pa1dimWDgMrVK18NXYq
26
+ 1vJmms2AXdvrptP4Mt4hYH1IZmGpUjtk/4WMAgmpAoGAKJXzDPY5ll9Ebjz28u3W
27
+ sPZdIz90rpRIBnyDC/AwtEzliU2PkuGpqe1ZJaP6cmPTxf/Bv8hfUMXknURpOShE
28
+ Uusyt3/4dGRMY+XXKuTPMZA8sbIEatA2z2e6oBoyj9avzEKAwDsReuPyyX6BubTx
29
+ rLSyj/PpCngrQw7YXsQc6Wc=
30
+ -----END PRIVATE KEY-----
31
+ `;
32
+ const FAKE_MEDIA_TLS_CERT_PEM = `-----BEGIN CERTIFICATE-----
33
+ MIIDGTCCAgGgAwIBAgIUCb8Qbq/uzK9W14G057lmny5p4kIwDQYJKoZIhvcNAQEL
34
+ BQAwGzEZMBcGA1UEAwwQZmFrZS1tZWRpYS5sb2NhbDAgFw0yNjA0MDgyMjAzMDRa
35
+ GA8yMTI2MDMxNTIyMDMwNFowGzEZMBcGA1UEAwwQZmFrZS1tZWRpYS5sb2NhbDCC
36
+ ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALfFNGoH/BlvP/ClCBw58J+L
37
+ NLQyu1VHS6UFrW8rg6VTUVck0LR21Mouz+MxY2rBhZfFKgYQcd+FQhI+BnTydq4I
38
+ mzzdkUBALUSzWw3VHfZiiQ+Yo4JMceHx/n+5feHmhQP/GveDA5WzhzYhKaTU5Oan
39
+ vsqiXFlcc6eWcfB47oMrQKp9P7ndHMqPmvPh3uKfIyTly2T4mMdl3x6rjp7gwfhl
40
+ Y8z56HBHCBYxDYTVsWNVCwEy+0CRTE4TH4Hb8LKzqMRQWj8AGRDFe34iepadgods
41
+ 85Ogqak/EONPng6lVmzghG1uROuAQwzPf9zmDhdqYZQcuqQjQYERIrbXpJNl620C
42
+ AwEAAaNTMFEwHQYDVR0OBBYEFG3DX82mLaatjTwwspHIj2pOD8CnMB8GA1UdIwQY
43
+ MBaAFG3DX82mLaatjTwwspHIj2pOD8CnMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
44
+ hvcNAQELBQADggEBAEqIu6mXgvSrGyQxRp6cJVg5OhBpwxH/V0IFe2H7xPBxreDS
45
+ e9yb/ESLRFxpfeWhLXOBEsOGbrF8HvZ0zNTHW+MtIkJB/hhYVzDJyhEpf9jWvpaM
46
+ H6CyzWIZgrXMmyeh0WkmVWoYyvlQrz9yJP63VfzCTCO6YBo02S+kKAyJiM9l8ZW5
47
+ 8vALhsyrGSMDnxQSixdc4L/Migam1LHxqwApzDvTdfXNiqjWEmfbw7sQKjd8EHad
48
+ KxNzgzzCRBSkeLe9YoXxLkKRvYeBM/xiv0AVp/ABedHj0xj3ybvvdjowui1uCBip
49
+ BoMF3OiuOeaTubj+tDzpZcauS4L+OyUPjsKqFoM=
50
+ -----END CERTIFICATE-----
51
+ `;
52
+ export class WaFakeMediaHttpsServer {
53
+ constructor() {
54
+ this.server = null;
55
+ this.listenInfo = null;
56
+ this.requestHandler = null;
57
+ }
58
+ setRequestHandler(handler) {
59
+ this.requestHandler = handler;
60
+ }
61
+ async listen(host = '127.0.0.1', port = 0) {
62
+ if (this.server) {
63
+ throw new Error('fake media https server is already listening');
64
+ }
65
+ const server = createHttpsServer({
66
+ key: FAKE_MEDIA_TLS_KEY_PEM,
67
+ cert: FAKE_MEDIA_TLS_CERT_PEM
68
+ }, (req, res) => {
69
+ const handler = this.requestHandler;
70
+ if (!handler) {
71
+ res.statusCode = 404;
72
+ res.end();
73
+ return;
74
+ }
75
+ try {
76
+ const result = handler(req, res);
77
+ if (result instanceof Promise) {
78
+ result.catch((error) => {
79
+ if (!res.headersSent) {
80
+ res.statusCode = 500;
81
+ }
82
+ res.end(error instanceof Error ? error.message : String(error));
83
+ });
84
+ }
85
+ }
86
+ catch (error) {
87
+ if (!res.headersSent) {
88
+ res.statusCode = 500;
89
+ }
90
+ res.end(error instanceof Error ? error.message : String(error));
91
+ }
92
+ });
93
+ await new Promise((resolve, reject) => {
94
+ const onError = (error) => {
95
+ server.off('listening', onListening);
96
+ reject(error);
97
+ };
98
+ const onListening = () => {
99
+ server.off('error', onError);
100
+ resolve();
101
+ };
102
+ server.once('error', onError);
103
+ server.once('listening', onListening);
104
+ server.listen(port, host);
105
+ });
106
+ const address = server.address();
107
+ this.server = server;
108
+ this.listenInfo = { host: address.address, port: address.port };
109
+ return this.listenInfo;
110
+ }
111
+ async close() {
112
+ const server = this.server;
113
+ if (!server)
114
+ return;
115
+ await new Promise((resolve, reject) => {
116
+ server.close((error) => (error ? reject(error) : resolve()));
117
+ });
118
+ // Force any keep-alive sockets to close so the test process can exit.
119
+ server.closeAllConnections?.();
120
+ this.server = null;
121
+ this.listenInfo = null;
122
+ }
123
+ get info() {
124
+ return this.listenInfo;
125
+ }
126
+ }
@@ -0,0 +1,67 @@
1
+ /** Responder-side Noise handshake primitives for fake server pipelines. */
2
+ import { aesGcmDecrypt, aesGcmEncrypt, hkdfSplit, sha256 } from '../transport/crypto.js';
3
+ const EMPTY = new Uint8Array(0);
4
+ export class WaFakeNoiseHandshake {
5
+ constructor() {
6
+ this.hash = EMPTY;
7
+ this.chainingKey = EMPTY;
8
+ this.cipherKey = null;
9
+ this.nonceCounter = 0;
10
+ }
11
+ start(name, prologue) {
12
+ const initialHash = name.byteLength === 32 ? name : sha256(name);
13
+ this.hash = initialHash;
14
+ this.chainingKey = initialHash;
15
+ this.cipherKey = initialHash;
16
+ this.nonceCounter = 0;
17
+ this.authenticate(prologue);
18
+ }
19
+ authenticate(data) {
20
+ const concatenated = new Uint8Array(this.hash.byteLength + data.byteLength);
21
+ concatenated.set(this.hash, 0);
22
+ concatenated.set(data, this.hash.byteLength);
23
+ this.hash = sha256(concatenated);
24
+ }
25
+ mixIntoKey(input) {
26
+ const [nextChainingKey, nextCipherMaterial] = hkdfSplit(input, this.chainingKey, EMPTY);
27
+ this.chainingKey = nextChainingKey;
28
+ this.cipherKey = nextCipherMaterial;
29
+ this.nonceCounter = 0;
30
+ }
31
+ encrypt(plaintext) {
32
+ if (!this.cipherKey) {
33
+ throw new Error('noise handshake encrypt called before key was derived');
34
+ }
35
+ const iv = this.buildNonceIv();
36
+ this.nonceCounter += 1;
37
+ const ciphertext = aesGcmEncrypt(this.cipherKey, iv, plaintext, this.hash);
38
+ this.authenticate(ciphertext);
39
+ return ciphertext;
40
+ }
41
+ decrypt(ciphertext) {
42
+ if (!this.cipherKey) {
43
+ throw new Error('noise handshake decrypt called before key was derived');
44
+ }
45
+ const iv = this.buildNonceIv();
46
+ this.nonceCounter += 1;
47
+ const plaintext = aesGcmDecrypt(this.cipherKey, iv, ciphertext, this.hash);
48
+ this.authenticate(ciphertext);
49
+ return plaintext;
50
+ }
51
+ /** Derives transport keys (`first -> recv`, `second -> send` for responder role). */
52
+ finish() {
53
+ const [first, second] = hkdfSplit(EMPTY, this.chainingKey, EMPTY);
54
+ return {
55
+ recvKey: first,
56
+ sendKey: second,
57
+ recvKeyBytes: first,
58
+ sendKeyBytes: second
59
+ };
60
+ }
61
+ buildNonceIv() {
62
+ const iv = new Uint8Array(12);
63
+ const view = new DataView(iv.buffer);
64
+ view.setUint32(8, this.nonceCounter, false);
65
+ return iv;
66
+ }
67
+ }
@@ -0,0 +1,31 @@
1
+ /** Post-handshake Noise transport (AES-GCM with direction-specific counters). */
2
+ import { aesGcmDecrypt, aesGcmEncrypt } from '../transport/crypto.js';
3
+ export class WaFakeTransport {
4
+ constructor(keys) {
5
+ this.sendCounter = 0;
6
+ this.recvCounter = 0;
7
+ this.recvKey = keys.recvKey;
8
+ this.sendKey = keys.sendKey;
9
+ }
10
+ // eslint-disable-next-line @typescript-eslint/require-await
11
+ async encryptFrame(plaintext) {
12
+ const nonce = buildAesGcmNonce(this.sendCounter);
13
+ this.sendCounter += 1;
14
+ return aesGcmEncrypt(this.sendKey, nonce, plaintext);
15
+ }
16
+ // eslint-disable-next-line @typescript-eslint/require-await
17
+ async decryptFrame(ciphertext) {
18
+ const nonce = buildAesGcmNonce(this.recvCounter);
19
+ this.recvCounter += 1;
20
+ return aesGcmDecrypt(this.recvKey, nonce, ciphertext);
21
+ }
22
+ }
23
+ function buildAesGcmNonce(counter) {
24
+ if (counter > 0xffffffff) {
25
+ throw new Error('noise transport nonce counter overflow');
26
+ }
27
+ const nonce = new Uint8Array(12);
28
+ const view = new DataView(nonce.buffer);
29
+ view.setUint32(8, counter, false);
30
+ return nonce;
31
+ }
@@ -0,0 +1,96 @@
1
+ import { createServer } from 'node:http';
2
+ import { WebSocketServer } from 'ws';
3
+ import { WaFakeConnection } from './WaFakeConnection.js';
4
+ export class WaFakeWsServer {
5
+ constructor(options = {}) {
6
+ this.httpServer = null;
7
+ this.wsServer = null;
8
+ this.connectionListener = null;
9
+ this.httpRequestHandler = null;
10
+ this.nextConnectionId = 0;
11
+ this.options = {
12
+ host: options.host ?? '127.0.0.1',
13
+ port: options.port ?? 0,
14
+ path: options.path ?? '/ws/chat'
15
+ };
16
+ }
17
+ onConnection(listener) {
18
+ this.connectionListener = listener;
19
+ }
20
+ setHttpRequestHandler(handler) {
21
+ this.httpRequestHandler = handler;
22
+ }
23
+ async listen() {
24
+ if (this.httpServer) {
25
+ throw new Error('fake ws server is already listening');
26
+ }
27
+ const httpServer = createServer(async (req, res) => {
28
+ const handler = this.httpRequestHandler;
29
+ if (!handler) {
30
+ res.statusCode = 404;
31
+ res.end();
32
+ return;
33
+ }
34
+ try {
35
+ await Promise.resolve(handler(req, res)).catch((error) => {
36
+ if (!res.headersSent) {
37
+ res.statusCode = 500;
38
+ }
39
+ res.end(error instanceof Error ? error.message : String(error));
40
+ });
41
+ }
42
+ catch (error) {
43
+ if (!res.headersSent) {
44
+ res.statusCode = 500;
45
+ }
46
+ res.end(error instanceof Error ? error.message : String(error));
47
+ }
48
+ });
49
+ const wsServer = new WebSocketServer({ server: httpServer, path: this.options.path });
50
+ wsServer.on('connection', (socket) => {
51
+ const id = `c${this.nextConnectionId++}`;
52
+ const connection = new WaFakeConnection(id, socket);
53
+ this.connectionListener?.(connection);
54
+ });
55
+ await new Promise((resolve, reject) => {
56
+ const onError = (error) => {
57
+ httpServer.off('listening', onListening);
58
+ reject(error);
59
+ };
60
+ const onListening = () => {
61
+ httpServer.off('error', onError);
62
+ resolve();
63
+ };
64
+ httpServer.once('error', onError);
65
+ httpServer.once('listening', onListening);
66
+ httpServer.listen(this.options.port, this.options.host);
67
+ });
68
+ this.httpServer = httpServer;
69
+ this.wsServer = wsServer;
70
+ const address = httpServer.address();
71
+ return {
72
+ host: address.address,
73
+ port: address.port,
74
+ path: this.options.path,
75
+ url: `ws://${address.address}:${address.port}${this.options.path}`
76
+ };
77
+ }
78
+ async close() {
79
+ const wsServer = this.wsServer;
80
+ const httpServer = this.httpServer;
81
+ if (!wsServer || !httpServer) {
82
+ return;
83
+ }
84
+ for (const client of wsServer.clients) {
85
+ client.terminate();
86
+ }
87
+ await new Promise((resolve, reject) => {
88
+ wsServer.close((error) => (error ? reject(error) : resolve()));
89
+ });
90
+ await new Promise((resolve, reject) => {
91
+ httpServer.close((error) => (error ? reject(error) : resolve()));
92
+ });
93
+ this.wsServer = null;
94
+ this.httpServer = null;
95
+ }
96
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,39 @@
1
+ /** Fake noise certificate chain generator for handshake tests. */
2
+ import { X25519, xeddsaSign } from '../../transport/crypto.js';
3
+ import { proto } from '../../transport/protos.js';
4
+ export async function generateFakeNoiseRootCa() {
5
+ const keyPair = await X25519.generateKeyPair();
6
+ return {
7
+ publicKey: keyPair.pubKey,
8
+ privateKey: keyPair.privKey,
9
+ serial: 0
10
+ };
11
+ }
12
+ export async function buildFakeCertChain(input) {
13
+ const intermediateSerial = input.intermediateSerial ?? 1;
14
+ const leafSerial = input.leafSerial ?? 2;
15
+ const intermediateKeyPair = await X25519.generateKeyPair();
16
+ const intermediateDetails = proto.CertChain.NoiseCertificate.Details.encode({
17
+ serial: intermediateSerial,
18
+ issuerSerial: input.root.serial,
19
+ key: intermediateKeyPair.pubKey
20
+ }).finish();
21
+ const intermediateSignature = await xeddsaSign(input.root.privateKey, intermediateDetails);
22
+ const leafDetails = proto.CertChain.NoiseCertificate.Details.encode({
23
+ serial: leafSerial,
24
+ issuerSerial: intermediateSerial,
25
+ key: input.leafKey
26
+ }).finish();
27
+ const leafSignature = await xeddsaSign(intermediateKeyPair.privKey, leafDetails);
28
+ const encoded = proto.CertChain.encode({
29
+ intermediate: {
30
+ details: intermediateDetails,
31
+ signature: intermediateSignature
32
+ },
33
+ leaf: {
34
+ details: leafDetails,
35
+ signature: leafSignature
36
+ }
37
+ }).finish();
38
+ return { encoded };
39
+ }
@@ -0,0 +1,55 @@
1
+ /** ClientPayload parser/validator (registration vs login flavors). */
2
+ import { proto } from '../../transport/protos.js';
3
+ export class ClientPayloadValidationError extends Error {
4
+ constructor(code, message) {
5
+ super(message);
6
+ this.name = 'ClientPayloadValidationError';
7
+ this.code = code;
8
+ }
9
+ }
10
+ export function parseClientPayload(bytes) {
11
+ let raw;
12
+ try {
13
+ raw = proto.ClientPayload.decode(bytes);
14
+ }
15
+ catch (error) {
16
+ throw new ClientPayloadValidationError('invalid_proto', `failed to decode ClientPayload: ${error.message}`);
17
+ }
18
+ const devicePairingData = raw.devicePairingData;
19
+ if (devicePairingData) {
20
+ validateRegistrationFields(devicePairingData);
21
+ return {
22
+ kind: 'registration',
23
+ raw,
24
+ devicePairingData
25
+ };
26
+ }
27
+ if (raw.username === undefined || raw.username === null) {
28
+ throw new ClientPayloadValidationError('missing_username', 'login ClientPayload is missing the username field');
29
+ }
30
+ return {
31
+ kind: 'login',
32
+ raw,
33
+ username: String(raw.username),
34
+ device: typeof raw.device === 'number' ? raw.device : 0,
35
+ loginCounter: typeof raw.lc === 'number' ? raw.lc : 0
36
+ };
37
+ }
38
+ function validateRegistrationFields(devicePairingData) {
39
+ const required = [
40
+ ['eIdent', 'identity public key'],
41
+ ['eRegid', 'registration id'],
42
+ ['eKeytype', 'key type marker'],
43
+ ['eSkeyId', 'signed pre-key id'],
44
+ ['eSkeyVal', 'signed pre-key public'],
45
+ ['eSkeySig', 'signed pre-key signature'],
46
+ ['buildHash', 'client build hash'],
47
+ ['deviceProps', 'device properties']
48
+ ];
49
+ for (const [field, label] of required) {
50
+ const value = devicePairingData[field];
51
+ if (value === undefined || value === null) {
52
+ throw new ClientPayloadValidationError('missing_field', `registration ClientPayload missing ${label} (devicePairingData.${String(field)})`);
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,47 @@
1
+ /** Fake primary-device identity/signature helpers for pairing. */
2
+ import { hmacSha256Sign, X25519, xeddsaSign } from '../../transport/crypto.js';
3
+ import { proto } from '../../transport/protos.js';
4
+ const ADV_PREFIX_ACCOUNT_SIGNATURE = new Uint8Array([0x06, 0x00]);
5
+ export async function generateFakePrimaryDevice() {
6
+ return {
7
+ identityKeyPair: await X25519.generateKeyPair()
8
+ };
9
+ }
10
+ export async function buildAdvSignedDeviceIdentity(input) {
11
+ const advDeviceIdentity = proto.ADVDeviceIdentity.encode({
12
+ rawId: input.companionDeviceId,
13
+ timestamp: input.timestampSeconds ?? Math.floor(Date.now() / 1000),
14
+ keyIndex: input.keyIndex ?? 0
15
+ }).finish();
16
+ // Sign exactly: prefix || details || companionIdentityPublicKey.
17
+ const accountSignatureKey = input.primary.identityKeyPair.pubKey;
18
+ const messageToSign = concatBytes([
19
+ ADV_PREFIX_ACCOUNT_SIGNATURE,
20
+ advDeviceIdentity,
21
+ input.companionIdentityPublicKey
22
+ ]);
23
+ const accountSignature = await xeddsaSign(input.primary.identityKeyPair.privKey, messageToSign);
24
+ const signedIdentity = proto.ADVSignedDeviceIdentity.encode({
25
+ details: advDeviceIdentity,
26
+ accountSignatureKey,
27
+ accountSignature
28
+ }).finish();
29
+ const hmac = hmacSha256Sign(input.advSecretKey, signedIdentity);
30
+ const wrapped = proto.ADVSignedDeviceIdentityHMAC.encode({
31
+ details: signedIdentity,
32
+ hmac
33
+ }).finish();
34
+ return { deviceIdentityBytes: wrapped };
35
+ }
36
+ function concatBytes(parts) {
37
+ let total = 0;
38
+ for (const p of parts)
39
+ total += p.byteLength;
40
+ const out = new Uint8Array(total);
41
+ let offset = 0;
42
+ for (const p of parts) {
43
+ out.set(p, offset);
44
+ offset += p.byteLength;
45
+ }
46
+ return out;
47
+ }
@@ -0,0 +1,87 @@
1
+ /** Pairing IQ builders/parsers (source: `WAWebHandlePairDevice*`, `/wa-web`). */
2
+ import { decodeBase64Url } from '../../transport/util.js';
3
+ export function buildPairDeviceIq(input) {
4
+ if (input.refs.length !== 6) {
5
+ throw new Error(`pair-device requires exactly 6 refs, got ${input.refs.length}`);
6
+ }
7
+ return {
8
+ tag: 'iq',
9
+ attrs: {
10
+ id: input.id ?? `pair-device-${Math.random().toString(36).slice(2, 10)}`,
11
+ type: 'set',
12
+ xmlns: 'md',
13
+ from: 's.whatsapp.net'
14
+ },
15
+ content: [
16
+ {
17
+ tag: 'pair-device',
18
+ attrs: {},
19
+ content: input.refs.map((ref) => ({
20
+ tag: 'ref',
21
+ attrs: {},
22
+ content: ref
23
+ }))
24
+ }
25
+ ]
26
+ };
27
+ }
28
+ export function buildPairSuccessIq(input) {
29
+ const children = [
30
+ {
31
+ tag: 'device',
32
+ attrs: {
33
+ jid: input.deviceJid,
34
+ ...(input.deviceLid !== undefined ? { lid: input.deviceLid } : {})
35
+ }
36
+ },
37
+ {
38
+ tag: 'platform',
39
+ attrs: { name: input.platform }
40
+ },
41
+ {
42
+ tag: 'device-identity',
43
+ attrs: {},
44
+ content: input.deviceIdentityBytes
45
+ }
46
+ ];
47
+ if (input.bizName !== undefined) {
48
+ children.push({
49
+ tag: 'biz',
50
+ attrs: { name: input.bizName }
51
+ });
52
+ }
53
+ return {
54
+ tag: 'iq',
55
+ attrs: {
56
+ id: input.id ?? `pair-success-${Math.random().toString(36).slice(2, 10)}`,
57
+ type: 'set',
58
+ xmlns: 'md',
59
+ from: 's.whatsapp.net'
60
+ },
61
+ content: [
62
+ {
63
+ tag: 'pair-success',
64
+ attrs: {},
65
+ content: children
66
+ }
67
+ ]
68
+ };
69
+ }
70
+ export function parsePairingQrString(qr) {
71
+ const parts = qr.split(',');
72
+ if (parts.length < 5) {
73
+ throw new Error(`pairing qr must have 5 comma-separated parts, got ${parts.length}`);
74
+ }
75
+ const platform = parts[parts.length - 1];
76
+ const advSecretB64 = parts[parts.length - 2];
77
+ const identityPubB64 = parts[parts.length - 3];
78
+ const noisePubB64 = parts[parts.length - 4];
79
+ const ref = parts.slice(0, parts.length - 4).join(',');
80
+ return {
81
+ ref,
82
+ noisePublicKey: decodeBase64Url(noisePubB64, 'qr.noisePublicKey'),
83
+ identityPublicKey: decodeBase64Url(identityPubB64, 'qr.identityPublicKey'),
84
+ advSecretKey: decodeBase64Url(advSecretB64, 'qr.advSecretKey'),
85
+ platform
86
+ };
87
+ }
@@ -0,0 +1,26 @@
1
+ /** Builder for the post-handshake `<success/>` stanza. */
2
+ export function buildSuccessNode(input = {}) {
3
+ const t = input.t ?? Math.floor(Date.now() / 1000);
4
+ const props = input.props ?? 0;
5
+ const attrs = {
6
+ t: String(t),
7
+ props: String(props),
8
+ companion_enc_static: input.companionEncStatic ?? 'NULL'
9
+ };
10
+ if (input.lid !== undefined)
11
+ attrs.lid = input.lid;
12
+ if (input.displayName !== undefined)
13
+ attrs.display_name = input.displayName;
14
+ if (input.abprops !== undefined)
15
+ attrs.abprops = String(input.abprops);
16
+ if (input.groupAbprops !== undefined)
17
+ attrs.group_abprops = String(input.groupAbprops);
18
+ if (input.location !== undefined)
19
+ attrs.location = input.location;
20
+ if (input.creation !== undefined)
21
+ attrs.creation = String(input.creation);
22
+ return {
23
+ tag: 'success',
24
+ attrs
25
+ };
26
+ }
@@ -0,0 +1,30 @@
1
+ /** Builds the `abt/props` IQ result consumed by the client ABProps parser. */
2
+ import { buildIqResult } from './router.js';
3
+ export function buildAbPropsResult(iq, input = {}) {
4
+ const result = buildIqResult(iq);
5
+ const propsAttrs = {
6
+ protocol: '1',
7
+ hash: input.hash ?? 'fake-abprops-hash',
8
+ refresh: String(input.refreshSeconds ?? 86400),
9
+ refresh_id: String(input.refreshId ?? 1),
10
+ delta_update: 'false'
11
+ };
12
+ return {
13
+ ...result,
14
+ content: [
15
+ {
16
+ tag: 'props',
17
+ attrs: propsAttrs,
18
+ content: (input.props ?? []).map((prop) => ({
19
+ tag: 'prop',
20
+ attrs: {
21
+ config_code: String(prop.configCode),
22
+ ...(prop.configValue !== undefined
23
+ ? { config_value: prop.configValue }
24
+ : {})
25
+ }
26
+ }))
27
+ }
28
+ ]
29
+ };
30
+ }