@zapo-js/fake-server 0.3.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 (238) hide show
  1. package/README.md +176 -0
  2. package/dist/api/AppStateSyncManager.d.ts +27 -0
  3. package/dist/api/AppStateSyncManager.d.ts.map +1 -0
  4. package/dist/api/AppStateSyncManager.js +151 -0
  5. package/dist/api/AppStateSyncManager.js.map +1 -0
  6. package/dist/api/FakePairingDriver.d.ts +27 -0
  7. package/dist/api/FakePairingDriver.d.ts.map +1 -0
  8. package/dist/api/FakePairingDriver.js +36 -0
  9. package/dist/api/FakePairingDriver.js.map +1 -0
  10. package/dist/api/FakePeer.d.ts +143 -0
  11. package/dist/api/FakePeer.d.ts.map +1 -0
  12. package/dist/api/FakePeer.js +408 -0
  13. package/dist/api/FakePeer.js.map +1 -0
  14. package/dist/api/FakeWaServer.d.ts +167 -0
  15. package/dist/api/FakeWaServer.d.ts.map +1 -0
  16. package/dist/api/FakeWaServer.js +652 -0
  17. package/dist/api/FakeWaServer.js.map +1 -0
  18. package/dist/api/PreKeyDispenser.d.ts +23 -0
  19. package/dist/api/PreKeyDispenser.d.ts.map +1 -0
  20. package/dist/api/PreKeyDispenser.js +98 -0
  21. package/dist/api/PreKeyDispenser.js.map +1 -0
  22. package/dist/api/Scenario.d.ts +24 -0
  23. package/dist/api/Scenario.d.ts.map +1 -0
  24. package/dist/api/Scenario.js +38 -0
  25. package/dist/api/Scenario.js.map +1 -0
  26. package/dist/api/ServerRegistries.d.ts +110 -0
  27. package/dist/api/ServerRegistries.d.ts.map +1 -0
  28. package/dist/api/ServerRegistries.js +268 -0
  29. package/dist/api/ServerRegistries.js.map +1 -0
  30. package/dist/api/iq-handlers.d.ts +47 -0
  31. package/dist/api/iq-handlers.d.ts.map +1 -0
  32. package/dist/api/iq-handlers.js +498 -0
  33. package/dist/api/iq-handlers.js.map +1 -0
  34. package/dist/cli.d.ts +4 -0
  35. package/dist/cli.d.ts.map +1 -0
  36. package/dist/cli.js +249 -0
  37. package/dist/cli.js.map +1 -0
  38. package/dist/index.d.ts +51 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +74 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/infra/WaFakeConnection.d.ts +23 -0
  43. package/dist/infra/WaFakeConnection.d.ts.map +1 -0
  44. package/dist/infra/WaFakeConnection.js +54 -0
  45. package/dist/infra/WaFakeConnection.js.map +1 -0
  46. package/dist/infra/WaFakeConnectionPipeline.d.ts +51 -0
  47. package/dist/infra/WaFakeConnectionPipeline.d.ts.map +1 -0
  48. package/dist/infra/WaFakeConnectionPipeline.js +224 -0
  49. package/dist/infra/WaFakeConnectionPipeline.js.map +1 -0
  50. package/dist/infra/WaFakeFrameSocket.d.ts +29 -0
  51. package/dist/infra/WaFakeFrameSocket.d.ts.map +1 -0
  52. package/dist/infra/WaFakeFrameSocket.js +122 -0
  53. package/dist/infra/WaFakeFrameSocket.js.map +1 -0
  54. package/dist/infra/WaFakeMediaHttpsServer.d.ts +16 -0
  55. package/dist/infra/WaFakeMediaHttpsServer.d.ts.map +1 -0
  56. package/dist/infra/WaFakeMediaHttpsServer.js +131 -0
  57. package/dist/infra/WaFakeMediaHttpsServer.js.map +1 -0
  58. package/dist/infra/WaFakeNoiseHandshake.d.ts +23 -0
  59. package/dist/infra/WaFakeNoiseHandshake.d.ts.map +1 -0
  60. package/dist/infra/WaFakeNoiseHandshake.js +76 -0
  61. package/dist/infra/WaFakeNoiseHandshake.js.map +1 -0
  62. package/dist/infra/WaFakeTransport.d.ts +15 -0
  63. package/dist/infra/WaFakeTransport.d.ts.map +1 -0
  64. package/dist/infra/WaFakeTransport.js +34 -0
  65. package/dist/infra/WaFakeTransport.js.map +1 -0
  66. package/dist/infra/WaFakeWsServer.d.ts +29 -0
  67. package/dist/infra/WaFakeWsServer.d.ts.map +1 -0
  68. package/dist/infra/WaFakeWsServer.js +101 -0
  69. package/dist/infra/WaFakeWsServer.js.map +1 -0
  70. package/dist/protocol/auth/cert-chain.d.ts +18 -0
  71. package/dist/protocol/auth/cert-chain.d.ts.map +1 -0
  72. package/dist/protocol/auth/cert-chain.js +44 -0
  73. package/dist/protocol/auth/cert-chain.js.map +1 -0
  74. package/dist/protocol/auth/client-payload-validate.d.ts +21 -0
  75. package/dist/protocol/auth/client-payload-validate.d.ts.map +1 -0
  76. package/dist/protocol/auth/client-payload-validate.js +61 -0
  77. package/dist/protocol/auth/client-payload-validate.js.map +1 -0
  78. package/dist/protocol/auth/fake-primary-device.d.ts +19 -0
  79. package/dist/protocol/auth/fake-primary-device.d.ts.map +1 -0
  80. package/dist/protocol/auth/fake-primary-device.js +53 -0
  81. package/dist/protocol/auth/fake-primary-device.js.map +1 -0
  82. package/dist/protocol/auth/pair-device.d.ts +26 -0
  83. package/dist/protocol/auth/pair-device.d.ts.map +1 -0
  84. package/dist/protocol/auth/pair-device.js +104 -0
  85. package/dist/protocol/auth/pair-device.js.map +1 -0
  86. package/dist/protocol/auth/success-node.d.ts +15 -0
  87. package/dist/protocol/auth/success-node.d.ts.map +1 -0
  88. package/dist/protocol/auth/success-node.js +30 -0
  89. package/dist/protocol/auth/success-node.js.map +1 -0
  90. package/dist/protocol/iq/abprops.d.ts +14 -0
  91. package/dist/protocol/iq/abprops.d.ts.map +1 -0
  92. package/dist/protocol/iq/abprops.js +34 -0
  93. package/dist/protocol/iq/abprops.js.map +1 -0
  94. package/dist/protocol/iq/appstate-sync.d.ts +38 -0
  95. package/dist/protocol/iq/appstate-sync.d.ts.map +1 -0
  96. package/dist/protocol/iq/appstate-sync.js +154 -0
  97. package/dist/protocol/iq/appstate-sync.js.map +1 -0
  98. package/dist/protocol/iq/business.d.ts +13 -0
  99. package/dist/protocol/iq/business.d.ts.map +1 -0
  100. package/dist/protocol/iq/business.js +64 -0
  101. package/dist/protocol/iq/business.js.map +1 -0
  102. package/dist/protocol/iq/dirty-bits.d.ts +8 -0
  103. package/dist/protocol/iq/dirty-bits.d.ts.map +1 -0
  104. package/dist/protocol/iq/dirty-bits.js +23 -0
  105. package/dist/protocol/iq/dirty-bits.js.map +1 -0
  106. package/dist/protocol/iq/group-ops.d.ts +34 -0
  107. package/dist/protocol/iq/group-ops.d.ts.map +1 -0
  108. package/dist/protocol/iq/group-ops.js +164 -0
  109. package/dist/protocol/iq/group-ops.js.map +1 -0
  110. package/dist/protocol/iq/newsletter.d.ts +4 -0
  111. package/dist/protocol/iq/newsletter.d.ts.map +1 -0
  112. package/dist/protocol/iq/newsletter.js +18 -0
  113. package/dist/protocol/iq/newsletter.js.map +1 -0
  114. package/dist/protocol/iq/prekey-fetch.d.ts +18 -0
  115. package/dist/protocol/iq/prekey-fetch.d.ts.map +1 -0
  116. package/dist/protocol/iq/prekey-fetch.js +92 -0
  117. package/dist/protocol/iq/prekey-fetch.js.map +1 -0
  118. package/dist/protocol/iq/privacy-token.d.ts +9 -0
  119. package/dist/protocol/iq/privacy-token.d.ts.map +1 -0
  120. package/dist/protocol/iq/privacy-token.js +30 -0
  121. package/dist/protocol/iq/privacy-token.js.map +1 -0
  122. package/dist/protocol/iq/privacy.d.ts +22 -0
  123. package/dist/protocol/iq/privacy.d.ts.map +1 -0
  124. package/dist/protocol/iq/privacy.js +133 -0
  125. package/dist/protocol/iq/privacy.js.map +1 -0
  126. package/dist/protocol/iq/profile.d.ts +22 -0
  127. package/dist/protocol/iq/profile.d.ts.map +1 -0
  128. package/dist/protocol/iq/profile.js +80 -0
  129. package/dist/protocol/iq/profile.js.map +1 -0
  130. package/dist/protocol/iq/router.d.ts +39 -0
  131. package/dist/protocol/iq/router.d.ts.map +1 -0
  132. package/dist/protocol/iq/router.js +118 -0
  133. package/dist/protocol/iq/router.js.map +1 -0
  134. package/dist/protocol/iq/usync.d.ts +8 -0
  135. package/dist/protocol/iq/usync.d.ts.map +1 -0
  136. package/dist/protocol/iq/usync.js +68 -0
  137. package/dist/protocol/iq/usync.js.map +1 -0
  138. package/dist/protocol/push/app-state-key-share.d.ts +17 -0
  139. package/dist/protocol/push/app-state-key-share.d.ts.map +1 -0
  140. package/dist/protocol/push/app-state-key-share.js +30 -0
  141. package/dist/protocol/push/app-state-key-share.js.map +1 -0
  142. package/dist/protocol/push/call-failure.d.ts +16 -0
  143. package/dist/protocol/push/call-failure.d.ts.map +1 -0
  144. package/dist/protocol/push/call-failure.js +32 -0
  145. package/dist/protocol/push/call-failure.js.map +1 -0
  146. package/dist/protocol/push/chatstate.d.ts +15 -0
  147. package/dist/protocol/push/chatstate.d.ts.map +1 -0
  148. package/dist/protocol/push/chatstate.js +27 -0
  149. package/dist/protocol/push/chatstate.js.map +1 -0
  150. package/dist/protocol/push/error-stanza.d.ts +8 -0
  151. package/dist/protocol/push/error-stanza.d.ts.map +1 -0
  152. package/dist/protocol/push/error-stanza.js +17 -0
  153. package/dist/protocol/push/error-stanza.js.map +1 -0
  154. package/dist/protocol/push/history-sync.d.ts +46 -0
  155. package/dist/protocol/push/history-sync.d.ts.map +1 -0
  156. package/dist/protocol/push/history-sync.js +117 -0
  157. package/dist/protocol/push/history-sync.js.map +1 -0
  158. package/dist/protocol/push/message.d.ts +21 -0
  159. package/dist/protocol/push/message.d.ts.map +1 -0
  160. package/dist/protocol/push/message.js +42 -0
  161. package/dist/protocol/push/message.js.map +1 -0
  162. package/dist/protocol/push/notification.d.ts +21 -0
  163. package/dist/protocol/push/notification.d.ts.map +1 -0
  164. package/dist/protocol/push/notification.js +34 -0
  165. package/dist/protocol/push/notification.js.map +1 -0
  166. package/dist/protocol/push/presence.d.ts +11 -0
  167. package/dist/protocol/push/presence.d.ts.map +1 -0
  168. package/dist/protocol/push/presence.js +18 -0
  169. package/dist/protocol/push/presence.js.map +1 -0
  170. package/dist/protocol/push/receipt.d.ts +14 -0
  171. package/dist/protocol/push/receipt.d.ts.map +1 -0
  172. package/dist/protocol/push/receipt.js +25 -0
  173. package/dist/protocol/push/receipt.js.map +1 -0
  174. package/dist/protocol/signal/fake-app-state-crypto.d.ts +49 -0
  175. package/dist/protocol/signal/fake-app-state-crypto.d.ts.map +1 -0
  176. package/dist/protocol/signal/fake-app-state-crypto.js +202 -0
  177. package/dist/protocol/signal/fake-app-state-crypto.js.map +1 -0
  178. package/dist/protocol/signal/fake-peer-double-ratchet.d.ts +38 -0
  179. package/dist/protocol/signal/fake-peer-double-ratchet.d.ts.map +1 -0
  180. package/dist/protocol/signal/fake-peer-double-ratchet.js +408 -0
  181. package/dist/protocol/signal/fake-peer-double-ratchet.js.map +1 -0
  182. package/dist/protocol/signal/fake-peer-group-recv-session.d.ts +10 -0
  183. package/dist/protocol/signal/fake-peer-group-recv-session.d.ts.map +1 -0
  184. package/dist/protocol/signal/fake-peer-group-recv-session.js +129 -0
  185. package/dist/protocol/signal/fake-peer-group-recv-session.js.map +1 -0
  186. package/dist/protocol/signal/fake-peer-identity.d.ts +7 -0
  187. package/dist/protocol/signal/fake-peer-identity.d.ts.map +1 -0
  188. package/dist/protocol/signal/fake-peer-identity.js +12 -0
  189. package/dist/protocol/signal/fake-peer-identity.js.map +1 -0
  190. package/dist/protocol/signal/fake-peer-key-bundle.d.ts +24 -0
  191. package/dist/protocol/signal/fake-peer-key-bundle.d.ts.map +1 -0
  192. package/dist/protocol/signal/fake-peer-key-bundle.js +29 -0
  193. package/dist/protocol/signal/fake-peer-key-bundle.js.map +1 -0
  194. package/dist/protocol/signal/fake-peer-recv-session.d.ts +60 -0
  195. package/dist/protocol/signal/fake-peer-recv-session.d.ts.map +1 -0
  196. package/dist/protocol/signal/fake-peer-recv-session.js +284 -0
  197. package/dist/protocol/signal/fake-peer-recv-session.js.map +1 -0
  198. package/dist/protocol/signal/fake-peer-session.d.ts +87 -0
  199. package/dist/protocol/signal/fake-peer-session.d.ts.map +1 -0
  200. package/dist/protocol/signal/fake-peer-session.js +207 -0
  201. package/dist/protocol/signal/fake-peer-session.js.map +1 -0
  202. package/dist/protocol/signal/fake-sender-key.d.ts +15 -0
  203. package/dist/protocol/signal/fake-sender-key.d.ts.map +1 -0
  204. package/dist/protocol/signal/fake-sender-key.js +91 -0
  205. package/dist/protocol/signal/fake-sender-key.js.map +1 -0
  206. package/dist/protocol/signal/prekey-upload.d.ts +22 -0
  207. package/dist/protocol/signal/prekey-upload.d.ts.map +1 -0
  208. package/dist/protocol/signal/prekey-upload.js +113 -0
  209. package/dist/protocol/signal/prekey-upload.js.map +1 -0
  210. package/dist/protocol/stream/stream-error.d.ts +8 -0
  211. package/dist/protocol/stream/stream-error.d.ts.map +1 -0
  212. package/dist/protocol/stream/stream-error.js +64 -0
  213. package/dist/protocol/stream/stream-error.js.map +1 -0
  214. package/dist/state/fake-app-state-collection.d.ts +35 -0
  215. package/dist/state/fake-app-state-collection.d.ts.map +1 -0
  216. package/dist/state/fake-app-state-collection.js +139 -0
  217. package/dist/state/fake-app-state-collection.js.map +1 -0
  218. package/dist/state/fake-media-store.d.ts +35 -0
  219. package/dist/state/fake-media-store.d.ts.map +1 -0
  220. package/dist/state/fake-media-store.js +57 -0
  221. package/dist/state/fake-media-store.js.map +1 -0
  222. package/dist/transport/codec.d.ts +10 -0
  223. package/dist/transport/codec.d.ts.map +1 -0
  224. package/dist/transport/codec.js +17 -0
  225. package/dist/transport/codec.js.map +1 -0
  226. package/dist/transport/crypto.d.ts +11 -0
  227. package/dist/transport/crypto.d.ts.map +1 -0
  228. package/dist/transport/crypto.js +36 -0
  229. package/dist/transport/crypto.js.map +1 -0
  230. package/dist/transport/index.d.ts +4 -0
  231. package/dist/transport/index.d.ts.map +1 -0
  232. package/dist/transport/index.js +20 -0
  233. package/dist/transport/index.js.map +1 -0
  234. package/dist/transport/protos.d.ts +10 -0
  235. package/dist/transport/protos.d.ts.map +1 -0
  236. package/dist/transport/protos.js +13 -0
  237. package/dist/transport/protos.js.map +1 -0
  238. package/package.json +43 -0
package/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # @zapo-js/fake-server
2
+
3
+ In-process fake WhatsApp Web server that drives the real `zapo-js` `WaClient` end-to-end — full Noise XX/IK handshake, QR pairing, Signal Protocol (X3DH + Double Ratchet), SenderKey for groups, media upload/download over self-signed HTTPS, app-state sync — all without touching WhatsApp servers.
4
+
5
+ ## Quick start
6
+
7
+ ```ts
8
+ import { FakeWaServer } from '@zapo-js/fake-server'
9
+ import { createStore, WaClient } from 'zapo-js'
10
+
11
+ const server = await FakeWaServer.start()
12
+ const client = new WaClient({
13
+ store: createStore({
14
+ providers: { auth: 'memory', signal: 'memory', senderKey: 'memory', appState: 'memory' }
15
+ }),
16
+ chatSocketUrls: [server.url],
17
+ testHooks: { noiseRootCa: server.noiseRootCa },
18
+ proxy: { mediaUpload: server.mediaProxyAgent, mediaDownload: server.mediaProxyAgent }
19
+ })
20
+
21
+ await client.connect()
22
+ const pipeline = await server.waitForAuthenticatedPipeline()
23
+ // ... pair, create peers, send messages
24
+ await server.stop()
25
+ ```
26
+
27
+ ## Architecture
28
+
29
+ ```text
30
+ src/
31
+ ├── api/ # Public-facing API
32
+ │ ├── FakeWaServer.ts # Main facade — WS server, IQ router, registries, lifecycle
33
+ │ ├── FakePeer.ts # Simulated WhatsApp peer — Signal crypto, send/recv, groups
34
+ │ ├── FakePairingDriver.ts # QR pairing flow orchestrator
35
+ │ └── Scenario.ts # Declarative test scenario DSL
36
+ ├── protocol/ # Protocol-layer builders, parsers, crypto
37
+ │ ├── auth/ # Pairing, ADV identity, cert chain, client payload
38
+ │ ├── iq/ # IQ stanza handlers (abprops, privacy, groups, profile, ...)
39
+ │ ├── push/ # Inbound stanza builders (message, notification, receipt, ...)
40
+ │ ├── signal/ # Signal Protocol impl (Double Ratchet, SenderKey, prekeys)
41
+ │ └── stream/ # Stream error builders
42
+ ├── infra/ # Transport infrastructure
43
+ │ ├── WaFakeWsServer.ts # Raw WebSocket server (ws)
44
+ │ ├── WaFakeConnection.ts # Per-connection WS wrapper
45
+ │ ├── WaFakeConnectionPipeline.ts # Noise handshake + authenticated frame transport
46
+ │ ├── WaFakeFrameSocket.ts # Length-prefixed framing layer
47
+ │ ├── WaFakeTransport.ts # AES-GCM noise transport (post-handshake encrypt/decrypt)
48
+ │ └── WaFakeMediaHttpsServer.ts # Self-signed HTTPS media up/download
49
+ ├── state/ # State stores
50
+ │ ├── fake-media-store.ts # In-memory media blob store
51
+ │ └── fake-app-state-collection.ts # App-state patch/snapshot provider
52
+ ├── transport/ # Re-exports from zapo-js (codec, crypto, protos)
53
+ └── __tests__/ # Cross-check test suite (147 tests)
54
+ └── helpers/ # Shared test utilities (zapo-client factory)
55
+
56
+ bench/
57
+ ├── messaging.bench.ts # 4-scenario messaging profiler (send/recv × 1:1/group)
58
+ ├── server-process.ts # Child-process entry for --separate-process mode
59
+ └── server-rpc.ts # IPC client for the child-process server
60
+ ```
61
+
62
+ ## Core concepts
63
+
64
+ ### FakeWaServer
65
+
66
+ Central facade. Manages the WS listener, noise handshake, IQ router, and all state registries:
67
+
68
+ - **Peer registry** (`peerRegistry`): maps device JIDs → `FakePeer` instances. The global `usync` + `prekey-fetch` IQ handlers consult this.
69
+ - **Group registry** (`groupRegistry`): maps group JIDs → group metadata + participants. The `w:g2` handler serves it.
70
+ - **IQ router** (`WaFakeIqRouter`): first-match-wins stanza dispatcher with `{ xmlns, type, childTag }` matchers. ~20 global handlers registered in the constructor cover every IQ the lib emits during normal operation.
71
+ - **Prekey dispenser**: hands out unique one-time prekeys from the lib's upload to FakePeers. Resets on each forced refill (`triggerPreKeyUpload({ force: true })`).
72
+ - **Listener fan-outs**: `onOutboundGroupOp`, `onOutboundPrivacySet`, `onOutboundBlocklistChange`, `onOutboundProfilePictureSet`, `onOutboundStatusSet`, `onLogout`, `onOutboundPrivacyTokenIssue`, `onOutboundDirtyBitsClear`.
73
+
74
+ ### FakePeer
75
+
76
+ Simulated WhatsApp peer with real Signal Protocol crypto:
77
+
78
+ - **1:1 messaging**: `peer.sendConversation(text)` encrypts via Double Ratchet and pushes a `<message><enc type="pkmsg|msg"/></message>` stanza.
79
+ - **Group messaging**: `peer.sendGroupConversation(groupJid, text)` bootstraps a SenderKey chain, encrypts the SKDM via 1:1 session, and sends `<enc type="skmsg"/>`.
80
+ - **Receive**: `peer.expectMessage()` / `peer.expectGroupMessage()` capture and decrypt the lib's outbound stanzas.
81
+ - **X3DH**: each peer generates its own identity keypair, signed prekey, and one-time prekeys. The prekey dispenser ensures unique consumption.
82
+
83
+ ### Pairing
84
+
85
+ `server.runPairing(pipeline, { deviceJid }, materialFn)` drives the full QR-pairing flow:
86
+
87
+ 1. Sends `pair-device` IQ with random refs
88
+ 2. Awaits the `advSecretKey` + `identityPublicKey` from the client's `auth_qr` event
89
+ 3. Builds an `ADVSignedDeviceIdentityHMAC` with a fresh fake primary keypair
90
+ 4. Pushes `pair-success` IQ
91
+
92
+ After pairing, the lib reconnects with IK handshake. Use `waitForNextAuthenticatedPipeline()` to capture the post-pair pipeline.
93
+
94
+ ## IQ coverage
95
+
96
+ Every outbound IQ the lib sends during normal operation is handled:
97
+
98
+ | IQ | Handler | State mutation |
99
+ | -------------------------------------------------------------------------------- | ----------------------------- | --------------------------------------- |
100
+ | `abt` get | `abprops` | Seedable via `setAbProps()` |
101
+ | `w:p` / `urn:xmpp:ping` get | `whatsapp-ping` / `xmpp-ping` | Ack |
102
+ | `encrypt` set | `prekey-upload` | Captures bundle, resets dispenser |
103
+ | `encrypt` get `<digest>` | `signal-digest` | Returns 404 → forces upload |
104
+ | `encrypt` set `<rotate>` | `signed-prekey-rotate` | Ack |
105
+ | `encrypt` get `<key>` | `prekey-fetch` | Serves peer bundles from registry |
106
+ | `usync` get | `usync` | Resolves device IDs from registry |
107
+ | `w:m` set `<media_conn>` | `media-conn` | Points lib at fake HTTPS server |
108
+ | `w:sync:app:state` set | `app-state-sync` | Serves patches/snapshots from providers |
109
+ | `w:g2` get `<query>` | `group-metadata` | Serves from group registry |
110
+ | `w:g2` set `<create\|add\|remove\|promote\|demote\|subject\|description\|leave>` | `group-*` | Mutates group registry |
111
+ | `privacy` get | `privacy-get` | Serves settings + disallowed lists |
112
+ | `privacy` set `<privacy>` | `privacy-set` | Mutates privacy state |
113
+ | `privacy` set `<tokens>` | `privacy-token-issue` | Captures issued tokens |
114
+ | `blocklist` get/set | `blocklist-*` | Mutates blocklist |
115
+ | `w:profile:picture` get/set | `profile-picture-*` | Mutates profile picture registry |
116
+ | `status` set | `status-set` | Captures latest status |
117
+ | `w:biz` get/set | `business-profile-*` | Serves/captures business profiles |
118
+ | `md` set `<remove-companion-device>` | `remove-companion-device` | Fires logout listeners |
119
+ | `newsletter` get `<my_addons>` | `newsletter-my-addons` | Ack |
120
+ | `urn:xmpp:whatsapp:dirty` set | `dirty-bits-clear` | Captures cleared bits |
121
+
122
+ ## Benchmarking
123
+
124
+ ```bash
125
+ # Default: 1000 contacts × 2 devices, 4 groups × 500 members, 1000 msgs/scenario
126
+ npm --workspace=@zapo-js/fake-server run bench:messaging
127
+
128
+ # With CPU + heap profiling (separate process for clean lib-only profiles)
129
+ node --expose-gc --import tsx packages/fake-server/bench/messaging.bench.ts \
130
+ --separate-process --cpu --per-scenario --heap --out-dir=./profiles
131
+
132
+ # Focused on a single scenario
133
+ ZAPO_BENCH_SCENARIOS=send_group ZAPO_BENCH_MESSAGES=2000 \
134
+ node --expose-gc --import tsx packages/fake-server/bench/messaging.bench.ts --cpu
135
+ ```
136
+
137
+ ### Profiling flags
138
+
139
+ | Flag | Output |
140
+ | ------------------------- | ---------------------------------------------------------------- |
141
+ | `--cpu` | `cpu-<ts>.cpuprofile` (whole run) |
142
+ | `--heap` | `heap-<ts>.heaptimeline` (allocation tracking) |
143
+ | `--snapshot` | `snapshot-{start,end}-<ts>.heapsnapshot` |
144
+ | `--per-scenario` | Per-scenario CPU profiles |
145
+ | `--snapshot-per-scenario` | Per-scenario heap snapshots |
146
+ | `--separate-process` | Forks fake server into child process for clean lib CPU profiling |
147
+ | `--out-dir=<path>` | Output directory (default: cwd) |
148
+
149
+ ### Env vars
150
+
151
+ | Var | Default | Description |
152
+ | ---------------------------- | ------- | ------------------------------------------------------------ |
153
+ | `ZAPO_BENCH_CONTACTS` | 1000 | Number of contacts |
154
+ | `ZAPO_BENCH_CONTACT_DEVICES` | 2 | Devices per contact |
155
+ | `ZAPO_BENCH_GROUPS` | 4 | Number of groups |
156
+ | `ZAPO_BENCH_GROUP_MEMBERS` | 500 | Members per group |
157
+ | `ZAPO_BENCH_MESSAGES` | 1000 | Messages per scenario |
158
+ | `ZAPO_BENCH_SCENARIOS` | all | CSV of: `send_1to1`, `recv_1to1`, `send_group`, `recv_group` |
159
+ | `ZAPO_BENCH_JSON` | 0 | Set to `1` to print JSON results |
160
+ | `ZAPO_BENCH_VERBOSE` | 0 | Set to `1` to forward lib warns/errors |
161
+
162
+ ## CLI
163
+
164
+ ```bash
165
+ # Standalone server for manual testing
166
+ npm --workspace=@zapo-js/fake-server run cli -- --port 5222 --peer 5511888@s.whatsapp.net --log
167
+ ```
168
+
169
+ ## Test suite
170
+
171
+ ```bash
172
+ npm --workspace=@zapo-js/fake-server test
173
+ # 147 tests, --test-concurrency=1
174
+ ```
175
+
176
+ Cross-check tests drive a real `WaClient` against the fake server and assert on both sides (lib emits correct events, peer decrypts correctly). Unit tests validate protocol builders/parsers in isolation.
@@ -0,0 +1,27 @@
1
+ import { type WaFakeConnectionPipeline } from '../infra/WaFakeConnectionPipeline';
2
+ import { type BuildServerSyncNotificationInput, type FakeAppStateCollectionPayload } from '../protocol/iq/appstate-sync';
3
+ import { FakeAppStateCrypto } from '../protocol/signal/fake-app-state-crypto';
4
+ import { type BinaryNode } from '../transport/codec';
5
+ import { type Proto } from '../transport/protos';
6
+ export interface CapturedAppStateMutation {
7
+ readonly collection: string;
8
+ readonly operation: 'set' | 'remove';
9
+ readonly index: string;
10
+ readonly action: string | undefined;
11
+ readonly version: number;
12
+ readonly value: Proto.ISyncActionValue | null;
13
+ readonly patchVersion: number;
14
+ }
15
+ export declare class AppStateSyncManager {
16
+ readonly appStateCollectionProviders: Map<string, () => Promise<FakeAppStateCollectionPayload | null> | FakeAppStateCollectionPayload | null>;
17
+ readonly appStateSyncKeysByKeyId: Map<string, Uint8Array<ArrayBufferLike>>;
18
+ readonly appStateCrypto: FakeAppStateCrypto;
19
+ readonly outboundMutationListeners: Set<(mutation: CapturedAppStateMutation) => void>;
20
+ registerAppStateSyncKey(keyId: Uint8Array, keyData: Uint8Array): void;
21
+ onOutboundAppStateMutation(listener: (mutation: CapturedAppStateMutation) => void): () => void;
22
+ expectAppStateMutation(predicate: (mutation: CapturedAppStateMutation) => boolean, timeoutMs?: number): Promise<CapturedAppStateMutation>;
23
+ provideAppStateCollection(name: string, provider: () => Promise<FakeAppStateCollectionPayload | null> | FakeAppStateCollectionPayload | null): () => void;
24
+ pushServerSyncNotification(pipeline: WaFakeConnectionPipeline, input: BuildServerSyncNotificationInput): Promise<void>;
25
+ consumeOutboundAppStatePatches(iq: BinaryNode): Promise<void>;
26
+ }
27
+ //# sourceMappingURL=AppStateSyncManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppStateSyncManager.d.ts","sourceRoot":"","sources":["../../src/api/AppStateSyncManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,mCAAmC,CAAA;AACjF,OAAO,EAEH,KAAK,gCAAgC,EACrC,KAAK,6BAA6B,EACrC,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAA;AAC7E,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAS,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAEvD,MAAM,WAAW,wBAAwB;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAG,QAAQ,CAAA;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAAA;IAC7C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAChC;AAWD,qBAAa,mBAAmB;IAC5B,SAAgB,2BAA2B,oBAEjC,OAAO,CAAC,6BAA6B,GAAG,IAAI,CAAC,GAAG,6BAA6B,GAAG,IAAI,EAC3F;IACH,SAAgB,uBAAuB,2CAAgC;IACvE,SAAgB,cAAc,qBAA2B;IACzD,SAAgB,yBAAyB,iBAC1B,wBAAwB,KAAK,IAAI,EAC7C;IAEI,uBAAuB,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI;IAIrE,0BAA0B,CAC7B,QAAQ,EAAE,CAAC,QAAQ,EAAE,wBAAwB,KAAK,IAAI,GACvD,MAAM,IAAI;IAON,sBAAsB,CACzB,SAAS,EAAE,CAAC,QAAQ,EAAE,wBAAwB,KAAK,OAAO,EAC1D,SAAS,SAAS,GACnB,OAAO,CAAC,wBAAwB,CAAC;IAe7B,yBAAyB,CAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MACJ,OAAO,CAAC,6BAA6B,GAAG,IAAI,CAAC,GAC7C,6BAA6B,GAC7B,IAAI,GACX,MAAM,IAAI;IAOA,0BAA0B,CACnC,QAAQ,EAAE,wBAAwB,EAClC,KAAK,EAAE,gCAAgC,GACxC,OAAO,CAAC,IAAI,CAAC;IAIH,8BAA8B,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAiF7E"}
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AppStateSyncManager = void 0;
4
+ const appstate_sync_1 = require("../protocol/iq/appstate-sync");
5
+ const fake_app_state_crypto_1 = require("../protocol/signal/fake-app-state-crypto");
6
+ const protos_1 = require("../transport/protos");
7
+ function toHex(bytes) {
8
+ let out = '';
9
+ for (let index = 0; index < bytes.byteLength; index += 1) {
10
+ const value = bytes[index];
11
+ out += value < 16 ? `0${value.toString(16)}` : value.toString(16);
12
+ }
13
+ return out;
14
+ }
15
+ class AppStateSyncManager {
16
+ constructor() {
17
+ this.appStateCollectionProviders = new Map();
18
+ this.appStateSyncKeysByKeyId = new Map();
19
+ this.appStateCrypto = new fake_app_state_crypto_1.FakeAppStateCrypto();
20
+ this.outboundMutationListeners = new Set();
21
+ }
22
+ registerAppStateSyncKey(keyId, keyData) {
23
+ this.appStateSyncKeysByKeyId.set(toHex(keyId), keyData);
24
+ }
25
+ onOutboundAppStateMutation(listener) {
26
+ this.outboundMutationListeners.add(listener);
27
+ return () => {
28
+ this.outboundMutationListeners.delete(listener);
29
+ };
30
+ }
31
+ expectAppStateMutation(predicate, timeoutMs = 15000) {
32
+ return new Promise((resolve, reject) => {
33
+ const timer = setTimeout(() => {
34
+ unsubscribe();
35
+ reject(new Error(`expectAppStateMutation timed out after ${timeoutMs}ms`));
36
+ }, timeoutMs);
37
+ const unsubscribe = this.onOutboundAppStateMutation((mutation) => {
38
+ if (!predicate(mutation))
39
+ return;
40
+ clearTimeout(timer);
41
+ unsubscribe();
42
+ resolve(mutation);
43
+ });
44
+ });
45
+ }
46
+ provideAppStateCollection(name, provider) {
47
+ this.appStateCollectionProviders.set(name, provider);
48
+ return () => {
49
+ this.appStateCollectionProviders.delete(name);
50
+ };
51
+ }
52
+ async pushServerSyncNotification(pipeline, input) {
53
+ await pipeline.sendStanza((0, appstate_sync_1.buildServerSyncNotification)(input));
54
+ }
55
+ async consumeOutboundAppStatePatches(iq) {
56
+ if (!Array.isArray(iq.content))
57
+ return;
58
+ const sync = iq.content.find((child) => child.tag === 'sync');
59
+ if (!sync || !Array.isArray(sync.content))
60
+ return;
61
+ for (const collectionNode of sync.content) {
62
+ if (collectionNode.tag !== 'collection')
63
+ continue;
64
+ if (!Array.isArray(collectionNode.content))
65
+ continue;
66
+ const collectionName = collectionNode.attrs.name;
67
+ if (!collectionName)
68
+ continue;
69
+ for (const patchNode of collectionNode.content) {
70
+ if (patchNode.tag !== 'patch')
71
+ continue;
72
+ const patchBytes = patchNode.content;
73
+ if (!(patchBytes instanceof Uint8Array))
74
+ continue;
75
+ try {
76
+ const decoded = protos_1.proto.SyncdPatch.decode(patchBytes);
77
+ const keyId = decoded.keyId?.id;
78
+ if (!keyId)
79
+ continue;
80
+ const keyData = this.appStateSyncKeysByKeyId.get(toHex(keyId));
81
+ if (!keyData)
82
+ continue;
83
+ const rawVersion = decoded.version?.version;
84
+ let patchVersion = 0;
85
+ if (typeof rawVersion === 'number') {
86
+ patchVersion = rawVersion;
87
+ }
88
+ else if (rawVersion !== null &&
89
+ rawVersion !== undefined &&
90
+ typeof rawVersion.toNumber === 'function') {
91
+ patchVersion = rawVersion.toNumber();
92
+ }
93
+ for (const mutation of decoded.mutations ?? []) {
94
+ const operationCode = mutation.operation;
95
+ if (operationCode === null || operationCode === undefined)
96
+ continue;
97
+ const record = mutation.record;
98
+ if (!record)
99
+ continue;
100
+ const indexMac = record.index?.blob;
101
+ const valueBlob = record.value?.blob;
102
+ if (!indexMac || !valueBlob)
103
+ continue;
104
+ const operation = operationCode === protos_1.proto.SyncdMutation.SyncdOperation.REMOVE
105
+ ? 'remove'
106
+ : 'set';
107
+ const decrypted = await this.appStateCrypto.decryptMutation({
108
+ operation,
109
+ keyId,
110
+ keyData,
111
+ indexMac,
112
+ valueBlob
113
+ });
114
+ let action;
115
+ try {
116
+ const parsed = JSON.parse(decrypted.index);
117
+ if (Array.isArray(parsed) && typeof parsed[0] === 'string') {
118
+ action = parsed[0];
119
+ }
120
+ }
121
+ catch {
122
+ // Ignore malformed index payloads; mutation capture still proceeds.
123
+ }
124
+ const captured = {
125
+ collection: collectionName,
126
+ operation,
127
+ index: decrypted.index,
128
+ action,
129
+ version: decrypted.version,
130
+ value: decrypted.value,
131
+ patchVersion
132
+ };
133
+ for (const listener of this.outboundMutationListeners) {
134
+ try {
135
+ listener(captured);
136
+ }
137
+ catch {
138
+ // Listener failures are best-effort.
139
+ }
140
+ }
141
+ }
142
+ }
143
+ catch {
144
+ // Ignore malformed/undecryptable patches.
145
+ }
146
+ }
147
+ }
148
+ }
149
+ }
150
+ exports.AppStateSyncManager = AppStateSyncManager;
151
+ //# sourceMappingURL=AppStateSyncManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppStateSyncManager.js","sourceRoot":"","sources":["../../src/api/AppStateSyncManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /** Runs the server side of the QR pairing flow. */
2
+ import type { WaFakeConnectionPipeline } from '../infra/WaFakeConnectionPipeline';
3
+ import { type FakePrimaryDevice } from '../protocol/auth/fake-primary-device';
4
+ export interface FakePairingDriverOptions {
5
+ readonly deviceJid: string;
6
+ readonly deviceLid?: string;
7
+ readonly companionDeviceId?: number;
8
+ readonly platform?: string;
9
+ readonly primary?: FakePrimaryDevice;
10
+ }
11
+ export interface CompanionPairingMaterial {
12
+ readonly advSecretKey: Uint8Array;
13
+ readonly identityPublicKey: Uint8Array;
14
+ }
15
+ export interface FakePairingDriverDeps {
16
+ readonly pipeline: WaFakeConnectionPipeline;
17
+ readonly waitForPairDeviceAck?: (pairDeviceIqId: string) => Promise<void>;
18
+ readonly companionMaterialResolver: () => Promise<CompanionPairingMaterial>;
19
+ }
20
+ export declare class FakePairingDriver {
21
+ private readonly options;
22
+ private readonly deps;
23
+ private primary;
24
+ constructor(options: FakePairingDriverOptions, deps: FakePairingDriverDeps);
25
+ run(): Promise<void>;
26
+ }
27
+ //# sourceMappingURL=FakePairingDriver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FakePairingDriver.d.ts","sourceRoot":"","sources":["../../src/api/FakePairingDriver.ts"],"names":[],"mappings":"AAAA,mDAAmD;AAEnD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAA;AACjF,OAAO,EAEH,KAAK,iBAAiB,EAEzB,MAAM,sCAAsC,CAAA;AAI7C,MAAM,WAAW,wBAAwB;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAA;CACvC;AAED,MAAM,WAAW,wBAAwB;IACrC,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAA;IACjC,QAAQ,CAAC,iBAAiB,EAAE,UAAU,CAAA;CACzC;AAED,MAAM,WAAW,qBAAqB;IAClC,QAAQ,CAAC,QAAQ,EAAE,wBAAwB,CAAA;IAC3C,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACzE,QAAQ,CAAC,yBAAyB,EAAE,MAAM,OAAO,CAAC,wBAAwB,CAAC,CAAA;CAC9E;AAED,qBAAa,iBAAiB;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAClD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAuB;IAC5C,OAAO,CAAC,OAAO,CAAiC;gBAE7B,OAAO,EAAE,wBAAwB,EAAE,IAAI,EAAE,qBAAqB;IAKpE,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA2BpC"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /** Runs the server side of the QR pairing flow. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.FakePairingDriver = void 0;
5
+ const fake_primary_device_1 = require("../protocol/auth/fake-primary-device");
6
+ const pair_device_1 = require("../protocol/auth/pair-device");
7
+ const crypto_1 = require("../transport/crypto");
8
+ class FakePairingDriver {
9
+ constructor(options, deps) {
10
+ this.primary = null;
11
+ this.options = options;
12
+ this.deps = deps;
13
+ }
14
+ async run() {
15
+ this.primary = this.options.primary ?? (await (0, fake_primary_device_1.generateFakePrimaryDevice)());
16
+ const refs = await Promise.all(Array.from({ length: 6 }, () => (0, crypto_1.randomBytesAsync)(16)));
17
+ const pairDeviceIq = (0, pair_device_1.buildPairDeviceIq)({ refs });
18
+ await this.deps.pipeline.sendStanza(pairDeviceIq);
19
+ const waitForAck = this.deps.waitForPairDeviceAck?.(pairDeviceIq.attrs.id) ?? Promise.resolve();
20
+ const [material] = await Promise.all([this.deps.companionMaterialResolver(), waitForAck]);
21
+ const { deviceIdentityBytes } = await (0, fake_primary_device_1.buildAdvSignedDeviceIdentity)({
22
+ primary: this.primary,
23
+ advSecretKey: material.advSecretKey,
24
+ companionIdentityPublicKey: material.identityPublicKey,
25
+ companionDeviceId: this.options.companionDeviceId ?? 1
26
+ });
27
+ await this.deps.pipeline.sendStanza((0, pair_device_1.buildPairSuccessIq)({
28
+ deviceJid: this.options.deviceJid,
29
+ deviceLid: this.options.deviceLid,
30
+ platform: this.options.platform ?? 'IOS',
31
+ deviceIdentityBytes
32
+ }));
33
+ }
34
+ }
35
+ exports.FakePairingDriver = FakePairingDriver;
36
+ //# sourceMappingURL=FakePairingDriver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FakePairingDriver.js","sourceRoot":"","sources":["../../src/api/FakePairingDriver.ts"],"names":[],"mappings":";AAAA,mDAAmD;;;AAGnD,8EAI6C;AAC7C,8DAAoF;AACpF,gDAAsD;AAqBtD,MAAa,iBAAiB;IAK1B,YAAmB,OAAiC,EAAE,IAA2B;QAFzE,YAAO,GAA6B,IAAI,CAAA;QAG5C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IACpB,CAAC;IAEM,KAAK,CAAC,GAAG;QACZ,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,MAAM,IAAA,+CAAyB,GAAE,CAAC,CAAA;QAE1E,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,IAAA,yBAAgB,EAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QACrF,MAAM,YAAY,GAAG,IAAA,+BAAiB,EAAC,EAAE,IAAI,EAAE,CAAC,CAAA;QAChD,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,YAAY,CAAC,CAAA;QACjD,MAAM,UAAU,GACZ,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAA;QAEhF,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,yBAAyB,EAAE,EAAE,UAAU,CAAC,CAAC,CAAA;QAEzF,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,IAAA,kDAA4B,EAAC;YAC/D,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,YAAY,EAAE,QAAQ,CAAC,YAAY;YACnC,0BAA0B,EAAE,QAAQ,CAAC,iBAAiB;YACtD,iBAAiB,EAAE,IAAI,CAAC,OAAO,CAAC,iBAAiB,IAAI,CAAC;SACzD,CAAC,CAAA;QAEF,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAC/B,IAAA,gCAAkB,EAAC;YACf,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;YACjC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;YACjC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,KAAK;YACxC,mBAAmB;SACtB,CAAC,CACL,CAAA;IACL,CAAC;CACJ;AArCD,8CAqCC"}
@@ -0,0 +1,143 @@
1
+ /** Fake peer used by tests to encrypt and push Signal messages to the lib. */
2
+ import { type BuildAppStateSyncKeyShareInput } from '../protocol/push/app-state-key-share';
3
+ import { type BuildHistorySyncExternalInput, type BuildHistorySyncInput } from '../protocol/push/history-sync';
4
+ import { buildMessage } from '../protocol/push/message';
5
+ import type { FakePeerIdentity } from '../protocol/signal/fake-peer-identity';
6
+ import { type FakePeerKeyBundle } from '../protocol/signal/fake-peer-key-bundle';
7
+ import type { ClientPreKeyBundle } from '../protocol/signal/prekey-upload';
8
+ import type { BinaryNode } from '../transport/codec';
9
+ import { proto } from '../transport/protos';
10
+ export interface CreateFakePeerOptions {
11
+ readonly jid: string;
12
+ readonly displayName?: string;
13
+ readonly keyBundle?: FakePeerKeyBundle;
14
+ readonly skipOneTimePreKey?: boolean;
15
+ }
16
+ export interface SendMessageOptions {
17
+ readonly id?: string;
18
+ readonly t?: number;
19
+ readonly type?: string;
20
+ readonly from?: string;
21
+ readonly participant?: string;
22
+ /** Mutates ciphertext before wrapping in `<enc>` (for corruption tests). */
23
+ readonly tamperCiphertext?: (ciphertext: Uint8Array) => Uint8Array;
24
+ }
25
+ export interface ReceivedMessage {
26
+ readonly message: proto.IMessage;
27
+ readonly stanza: BinaryNode;
28
+ readonly encType: 'pkmsg' | 'msg' | 'skmsg';
29
+ }
30
+ export interface ExpectMessageOptions {
31
+ readonly timeoutMs?: number;
32
+ }
33
+ export interface ExpectGroupMessageOptions extends ExpectMessageOptions {
34
+ readonly senderJid?: string;
35
+ }
36
+ interface FakePeerDeps {
37
+ readonly bundleResolver: () => Promise<ClientPreKeyBundle>;
38
+ readonly reserveOneTimePreKey?: () => {
39
+ readonly keyId: number;
40
+ readonly publicKey: Uint8Array;
41
+ } | null;
42
+ readonly pushStanza: (node: ReturnType<typeof buildMessage>) => Promise<void>;
43
+ readonly subscribeInboundMessages: (listener: (stanza: BinaryNode) => void) => () => void;
44
+ }
45
+ export declare class FakePeer {
46
+ readonly jid: string;
47
+ readonly displayName?: string;
48
+ readonly keyBundle: FakePeerKeyBundle;
49
+ readonly identity: FakePeerIdentity;
50
+ private readonly deps;
51
+ private readonly ratchet;
52
+ private readonly skipOneTimePreKey;
53
+ private readonly reservedOneTimePreKey;
54
+ private ratchetInitiated;
55
+ private nextMessageCounter;
56
+ private readonly senderKeysByGroup;
57
+ private readonly groupsBootstrapped;
58
+ private readonly groupRecvSession;
59
+ private constructor();
60
+ static create(options: CreateFakePeerOptions, deps: FakePeerDeps): Promise<FakePeer>;
61
+ sendMessage(message: proto.IMessage, options?: SendMessageOptions): Promise<void>;
62
+ sendConversation(text: string, options?: SendMessageOptions): Promise<void>;
63
+ sendMessageEdit(input: {
64
+ readonly targetMessageId: string;
65
+ readonly newText: string;
66
+ }, options?: SendMessageOptions): Promise<void>;
67
+ sendMessageRevoke(input: {
68
+ readonly targetMessageId: string;
69
+ }, options?: SendMessageOptions): Promise<void>;
70
+ sendReaction(input: {
71
+ readonly targetMessageId: string;
72
+ readonly emoji: string;
73
+ }, options?: SendMessageOptions): Promise<void>;
74
+ sendHistorySync(input?: BuildHistorySyncInput, options?: SendMessageOptions): Promise<void>;
75
+ sendHistorySyncExternal(input: BuildHistorySyncExternalInput, options?: SendMessageOptions): Promise<void>;
76
+ sendImageMessage(input: {
77
+ readonly directPath: string;
78
+ readonly mediaKey: Uint8Array;
79
+ readonly fileSha256: Uint8Array;
80
+ readonly fileEncSha256: Uint8Array;
81
+ readonly fileLength: number;
82
+ readonly mimetype?: string;
83
+ readonly caption?: string;
84
+ readonly width?: number;
85
+ readonly height?: number;
86
+ }, options?: SendMessageOptions): Promise<void>;
87
+ sendVideoMessage(input: {
88
+ readonly directPath: string;
89
+ readonly mediaKey: Uint8Array;
90
+ readonly fileSha256: Uint8Array;
91
+ readonly fileEncSha256: Uint8Array;
92
+ readonly fileLength: number;
93
+ readonly mimetype?: string;
94
+ readonly caption?: string;
95
+ readonly seconds?: number;
96
+ readonly width?: number;
97
+ readonly height?: number;
98
+ }, options?: SendMessageOptions): Promise<void>;
99
+ sendAudioMessage(input: {
100
+ readonly directPath: string;
101
+ readonly mediaKey: Uint8Array;
102
+ readonly fileSha256: Uint8Array;
103
+ readonly fileEncSha256: Uint8Array;
104
+ readonly fileLength: number;
105
+ readonly mimetype?: string;
106
+ readonly seconds?: number;
107
+ readonly ptt?: boolean;
108
+ }, options?: SendMessageOptions): Promise<void>;
109
+ sendDocumentMessage(input: {
110
+ readonly directPath: string;
111
+ readonly mediaKey: Uint8Array;
112
+ readonly fileSha256: Uint8Array;
113
+ readonly fileEncSha256: Uint8Array;
114
+ readonly fileLength: number;
115
+ readonly mimetype?: string;
116
+ readonly title?: string;
117
+ readonly fileName?: string;
118
+ readonly pageCount?: number;
119
+ }, options?: SendMessageOptions): Promise<void>;
120
+ sendStickerMessage(input: {
121
+ readonly directPath: string;
122
+ readonly mediaKey: Uint8Array;
123
+ readonly fileSha256: Uint8Array;
124
+ readonly fileEncSha256: Uint8Array;
125
+ readonly fileLength: number;
126
+ readonly mimetype?: string;
127
+ readonly width?: number;
128
+ readonly height?: number;
129
+ readonly isAnimated?: boolean;
130
+ }, options?: SendMessageOptions): Promise<void>;
131
+ sendAppStateSyncKeyShare(input: BuildAppStateSyncKeyShareInput, options?: SendMessageOptions): Promise<void>;
132
+ sendGroupConversation(groupJid: string, text: string, options?: SendMessageOptions): Promise<void>;
133
+ sendGroupMessage(groupJid: string, message: proto.IMessage, options?: SendMessageOptions): Promise<void>;
134
+ expectMessage(options?: ExpectMessageOptions): Promise<ReceivedMessage>;
135
+ expectGroupMessage(groupJid: string, options?: ExpectGroupMessageOptions): Promise<ReceivedMessage>;
136
+ private decryptPairwise;
137
+ decryptStanza(stanza: BinaryNode): Promise<ReceivedMessage | null>;
138
+ private bootstrapAndDecryptGroup;
139
+ private ensureRatchetInitiated;
140
+ private nextId;
141
+ }
142
+ export {};
143
+ //# sourceMappingURL=FakePeer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FakePeer.d.ts","sourceRoot":"","sources":["../../src/api/FakePeer.ts"],"names":[],"mappings":"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"}