@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,293 @@
1
+ "use strict";
2
+ /** Per-session isolated server state: registries, prekeys, app-state, IQ routing, capture. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.FakeServerSession = void 0;
5
+ exports.matchesIq = matchesIq;
6
+ exports.matchesStanza = matchesStanza;
7
+ const router_1 = require("../protocol/iq/router");
8
+ const AppStateSyncManager_1 = require("./AppStateSyncManager");
9
+ const iq_handlers_1 = require("./iq-handlers");
10
+ const PreKeyDispenser_1 = require("./PreKeyDispenser");
11
+ const ServerRegistries_1 = require("./ServerRegistries");
12
+ /**
13
+ * One isolated logical device's worth of server state. Every `WaClient`
14
+ * connected to a `FakeWaServer` is bound to exactly one session; peers,
15
+ * groups, privacy, prekeys, app-state, and captured stanzas never leak across
16
+ * sessions. A single-client server uses one default session, which is why the
17
+ * legacy `FakeWaServer` surface delegates straight through to it.
18
+ */
19
+ class FakeServerSession {
20
+ constructor(id, host, options) {
21
+ this.registries = new ServerRegistries_1.ServerRegistries();
22
+ this.preKeyDispenser = new PreKeyDispenser_1.PreKeyDispenser();
23
+ this.appStateSync = new AppStateSyncManager_1.AppStateSyncManager();
24
+ this.iqRouter = new router_1.WaFakeIqRouter();
25
+ this.capturedStanzas = [];
26
+ this.pendingIqExpectations = new Set();
27
+ this.pendingStanzaExpectations = new Set();
28
+ this.capturedStanzaListeners = new Set();
29
+ this.inboundStanzaListeners = new Set();
30
+ this.id = id;
31
+ if (options.defaultIqHandlers) {
32
+ (0, iq_handlers_1.registerDefaultIqHandlers)(this.iqRouter, this.buildIqHandlerDeps(host));
33
+ }
34
+ }
35
+ registerIqHandler(matcher, respond, label) {
36
+ const handler = { matcher, respond, label };
37
+ return this.iqRouter.register(handler, { priority: 'high' });
38
+ }
39
+ routeIq(iq) {
40
+ return this.iqRouter.route(iq);
41
+ }
42
+ handleCapturedStanza(node) {
43
+ this.capturedStanzas.push(node);
44
+ for (const listener of this.capturedStanzaListeners) {
45
+ try {
46
+ listener(node);
47
+ }
48
+ catch (error) {
49
+ void error;
50
+ }
51
+ }
52
+ for (const listener of this.inboundStanzaListeners) {
53
+ try {
54
+ listener(node);
55
+ }
56
+ catch (error) {
57
+ void error;
58
+ }
59
+ }
60
+ for (const expectation of this.pendingStanzaExpectations) {
61
+ if (matchesStanza(node, expectation.matcher)) {
62
+ expectation.resolve(node);
63
+ break;
64
+ }
65
+ }
66
+ if (node.tag !== 'iq') {
67
+ return;
68
+ }
69
+ for (const expectation of this.pendingIqExpectations) {
70
+ if (matchesIq(node, expectation.matcher)) {
71
+ expectation.resolve(node);
72
+ return;
73
+ }
74
+ }
75
+ }
76
+ subscribeInboundMessages(listener) {
77
+ const wrapped = (node) => {
78
+ if (node.tag !== 'message')
79
+ return;
80
+ listener(node);
81
+ };
82
+ this.inboundStanzaListeners.add(wrapped);
83
+ return () => {
84
+ this.inboundStanzaListeners.delete(wrapped);
85
+ };
86
+ }
87
+ onCapturedStanza(listener) {
88
+ this.capturedStanzaListeners.add(listener);
89
+ return () => {
90
+ this.capturedStanzaListeners.delete(listener);
91
+ };
92
+ }
93
+ capturedStanzaSnapshot() {
94
+ return this.capturedStanzas.slice();
95
+ }
96
+ expectIq(matcher, options = {}) {
97
+ const timeoutMs = options.timeoutMs ?? 2000;
98
+ for (const captured of this.capturedStanzas) {
99
+ if (matchesIq(captured, matcher)) {
100
+ return Promise.resolve(captured);
101
+ }
102
+ }
103
+ return new Promise((resolve, reject) => {
104
+ const expectation = {
105
+ matcher,
106
+ resolve: (iq) => {
107
+ this.pendingIqExpectations.delete(expectation);
108
+ clearTimeout(expectation.timer);
109
+ resolve(iq);
110
+ },
111
+ reject: (error) => {
112
+ this.pendingIqExpectations.delete(expectation);
113
+ clearTimeout(expectation.timer);
114
+ reject(error);
115
+ },
116
+ timer: setTimeout(() => {
117
+ this.pendingIqExpectations.delete(expectation);
118
+ reject(new Error(`expectIq timed out after ${timeoutMs}ms (${describeMatcher(matcher)})`));
119
+ }, timeoutMs)
120
+ };
121
+ this.pendingIqExpectations.add(expectation);
122
+ });
123
+ }
124
+ expectStanza(matcher, options = {}) {
125
+ const timeoutMs = options.timeoutMs ?? 2000;
126
+ for (const captured of this.capturedStanzas) {
127
+ if (matchesStanza(captured, matcher)) {
128
+ return Promise.resolve(captured);
129
+ }
130
+ }
131
+ return new Promise((resolve, reject) => {
132
+ const expectation = {
133
+ matcher,
134
+ resolve: (node) => {
135
+ this.pendingStanzaExpectations.delete(expectation);
136
+ clearTimeout(expectation.timer);
137
+ resolve(node);
138
+ },
139
+ reject: (error) => {
140
+ this.pendingStanzaExpectations.delete(expectation);
141
+ clearTimeout(expectation.timer);
142
+ reject(error);
143
+ },
144
+ timer: setTimeout(() => {
145
+ this.pendingStanzaExpectations.delete(expectation);
146
+ reject(new Error(`expectStanza timed out after ${timeoutMs}ms (${describeStanzaMatcher(matcher)})`));
147
+ }, timeoutMs)
148
+ };
149
+ this.pendingStanzaExpectations.add(expectation);
150
+ });
151
+ }
152
+ buildIqHandlerDeps(host) {
153
+ const reg = this.registries;
154
+ const preKey = this.preKeyDispenser;
155
+ const appState = this.appStateSync;
156
+ return {
157
+ get peerRegistry() {
158
+ return reg.peerRegistry;
159
+ },
160
+ get groupRegistry() {
161
+ return reg.groupRegistry;
162
+ },
163
+ get privacySettings() {
164
+ return reg.privacySettings;
165
+ },
166
+ get blocklistJids() {
167
+ return reg.blocklistJids;
168
+ },
169
+ get profilePicturesByJid() {
170
+ return reg.profilePicturesByJid;
171
+ },
172
+ get businessProfilesByJid() {
173
+ return reg.businessProfilesByJid;
174
+ },
175
+ get abPropsInput() {
176
+ return reg.abPropsInput;
177
+ },
178
+ get issuedPrivacyTokens() {
179
+ return reg.issuedPrivacyTokens;
180
+ },
181
+ get latestStatusText() {
182
+ return reg.latestStatusText;
183
+ },
184
+ setLatestStatusText: (text) => {
185
+ reg.latestStatusText = text;
186
+ },
187
+ lookupDeviceIdsForUser: (userJid) => reg.lookupDeviceIdsForUser(userJid),
188
+ notifyGroupOp: (op) => reg.notifyGroupOp(op),
189
+ mutatePrivacySettings: (category, value) => reg.mutatePrivacySettings(category, value),
190
+ mutateBlocklist: (action, jid) => reg.mutateBlocklist(action, jid),
191
+ notifyProfilePictureSet: (op) => reg.notifyProfilePictureSet(op),
192
+ handleProfilePictureSet: (targetJid, newId) => reg.handleProfilePictureSet(targetJid, newId),
193
+ notifyStatusSet: (text) => reg.notifyStatusSet(text),
194
+ notifyLogout: () => reg.notifyLogout(),
195
+ notifyPrivacyTokenIssue: (token) => reg.notifyPrivacyTokenIssue(token),
196
+ notifyDirtyBitsClear: (op) => reg.notifyDirtyBitsClear(op),
197
+ notifyPrivacySet: (change) => {
198
+ for (const listener of reg.privacySetListeners) {
199
+ try {
200
+ listener(change);
201
+ }
202
+ catch (error) {
203
+ void error;
204
+ }
205
+ }
206
+ },
207
+ notifyBlocklistChange: (change) => {
208
+ for (const listener of reg.blocklistChangeListeners) {
209
+ try {
210
+ listener(change);
211
+ }
212
+ catch (error) {
213
+ void error;
214
+ }
215
+ }
216
+ },
217
+ capturePreKeyBundle: (bundle) => preKey.captureBundle(bundle),
218
+ countServerPreKeys: () => preKey.preKeysAvailable(),
219
+ consumeOutboundAppStatePatches: (iq) => appState.consumeOutboundAppStatePatches(iq),
220
+ get appStateCollectionProviders() {
221
+ return appState.appStateCollectionProviders;
222
+ },
223
+ requireMediaHttpsInfo: () => host.requireMediaHttpsInfo()
224
+ };
225
+ }
226
+ }
227
+ exports.FakeServerSession = FakeServerSession;
228
+ function matchesIq(iq, matcher) {
229
+ if (iq.tag !== 'iq')
230
+ return false;
231
+ if (matcher.id !== undefined && iq.attrs.id !== matcher.id)
232
+ return false;
233
+ if (matcher.type !== undefined && iq.attrs.type !== matcher.type)
234
+ return false;
235
+ if (matcher.xmlns !== undefined && iq.attrs.xmlns !== matcher.xmlns)
236
+ return false;
237
+ if (matcher.childTag !== undefined) {
238
+ const children = Array.isArray(iq.content) ? iq.content : null;
239
+ if (!children || children.length === 0)
240
+ return false;
241
+ if (children[0].tag !== matcher.childTag)
242
+ return false;
243
+ }
244
+ return true;
245
+ }
246
+ function describeMatcher(matcher) {
247
+ const parts = [];
248
+ if (matcher.id !== undefined)
249
+ parts.push(`id=${matcher.id}`);
250
+ if (matcher.type !== undefined)
251
+ parts.push(`type=${matcher.type}`);
252
+ if (matcher.xmlns !== undefined)
253
+ parts.push(`xmlns=${matcher.xmlns}`);
254
+ if (matcher.childTag !== undefined)
255
+ parts.push(`childTag=${matcher.childTag}`);
256
+ return parts.length > 0 ? parts.join(', ') : 'any iq';
257
+ }
258
+ function matchesStanza(node, matcher) {
259
+ if (matcher.tag !== undefined && node.tag !== matcher.tag)
260
+ return false;
261
+ if (matcher.type !== undefined && node.attrs.type !== matcher.type)
262
+ return false;
263
+ if (matcher.xmlns !== undefined && node.attrs.xmlns !== matcher.xmlns)
264
+ return false;
265
+ if (matcher.from !== undefined && node.attrs.from !== matcher.from)
266
+ return false;
267
+ if (matcher.to !== undefined && node.attrs.to !== matcher.to)
268
+ return false;
269
+ if (matcher.childTag !== undefined) {
270
+ const children = Array.isArray(node.content) ? node.content : null;
271
+ if (!children || children.length === 0)
272
+ return false;
273
+ if (children[0].tag !== matcher.childTag)
274
+ return false;
275
+ }
276
+ return true;
277
+ }
278
+ function describeStanzaMatcher(matcher) {
279
+ const parts = [];
280
+ if (matcher.tag !== undefined)
281
+ parts.push(`tag=${matcher.tag}`);
282
+ if (matcher.type !== undefined)
283
+ parts.push(`type=${matcher.type}`);
284
+ if (matcher.xmlns !== undefined)
285
+ parts.push(`xmlns=${matcher.xmlns}`);
286
+ if (matcher.from !== undefined)
287
+ parts.push(`from=${matcher.from}`);
288
+ if (matcher.to !== undefined)
289
+ parts.push(`to=${matcher.to}`);
290
+ if (matcher.childTag !== undefined)
291
+ parts.push(`childTag=${matcher.childTag}`);
292
+ return parts.length > 0 ? parts.join(', ') : 'any stanza';
293
+ }
@@ -1,47 +1,65 @@
1
1
  import { Agent as HttpsAgent } from 'node:https';
2
2
  import { type WaFakeAuthenticatedInfo, WaFakeConnectionPipeline } from '../infra/WaFakeConnectionPipeline';
3
+ import type { ParsedClientPayload } from '../protocol/auth/client-payload-validate';
4
+ import type { BuildSuccessNodeInput } from '../protocol/auth/success-node';
3
5
  import type { BuildAbPropsResultInput } from '../protocol/iq/abprops';
4
6
  import { type BuildServerSyncNotificationInput, type FakeAppStateCollectionPayload } from '../protocol/iq/appstate-sync';
5
7
  import type { FakeBusinessProfile } from '../protocol/iq/business';
6
8
  import type { FakePrivacyCategoryName, FakePrivacySettingsState } from '../protocol/iq/privacy';
7
9
  import type { FakePrivacyTokenIssue } from '../protocol/iq/privacy-token';
8
10
  import type { FakeProfilePictureResult } from '../protocol/iq/profile';
9
- import { type WaFakeIqMatcher, type WaFakeIqResponder } from '../protocol/iq/router';
11
+ import type { WaFakeIqMatcher, WaFakeIqResponder } from '../protocol/iq/router';
10
12
  import type { ClientPreKeyBundle } from '../protocol/signal/prekey-upload';
11
13
  import { type PublishedMediaBlob, type PublishMediaInput } from '../state/fake-media-store';
12
14
  import { type BinaryNode } from '../transport/codec';
13
- import { AppStateSyncManager, type CapturedAppStateMutation } from './AppStateSyncManager';
15
+ import { type AppStateSyncManager, type CapturedAppStateMutation } from './AppStateSyncManager';
14
16
  import { type FakePairingDriverOptions } from './FakePairingDriver';
15
17
  import { type CreateFakePeerOptions, FakePeer } from './FakePeer';
16
- import { PreKeyDispenser } from './PreKeyDispenser';
18
+ import { type ExpectIqOptions, type ExpectStanzaOptions, FakeServerSession, type StanzaMatcher } from './FakeServerSession';
19
+ import type { PreKeyDispenser } from './PreKeyDispenser';
17
20
  import { type AuthenticatedPipelineListener, Scenario } from './Scenario';
18
- import { type CapturedBlocklistChange, type CapturedDirtyBitsClear, type CapturedGroupOp, type CapturedPrivacySet, type CapturedProfilePictureSet, type CapturedStatusSet, type FakeGroupMetadata, ServerRegistries } from './ServerRegistries';
21
+ import type { CapturedBlocklistChange, CapturedDirtyBitsClear, CapturedGroupOp, CapturedPrivacySet, CapturedProfilePictureSet, CapturedStatusSet, FakeGroupMetadata, ServerRegistries } from './ServerRegistries';
19
22
  export type { FakeGroupMetadata, CapturedGroupOp, CapturedPrivacySet, CapturedBlocklistChange, CapturedProfilePictureSet, CapturedStatusSet, CapturedDirtyBitsClear } from './ServerRegistries';
20
23
  export type { CapturedAppStateMutation } from './AppStateSyncManager';
21
24
  export interface FakeWaServerOptions {
22
25
  readonly host?: string;
23
26
  readonly port?: number;
24
27
  readonly path?: string;
28
+ /**
29
+ * Attributes stamped on the post-handshake `<success/>` node of every
30
+ * authenticated connection (lid, display name, props versions, ...).
31
+ */
32
+ readonly successNodeAttributes?: BuildSuccessNodeInput;
33
+ /**
34
+ * Register the built-in IQ auto-handlers that answer everything the lib
35
+ * emits during normal operation. Default `true`; pass `false` to start
36
+ * with an empty router and wire every response via `registerIqHandler`.
37
+ */
38
+ readonly defaultIqHandlers?: boolean;
39
+ /**
40
+ * Assigns each authenticated connection to an isolated session, keyed by
41
+ * the returned id. Two connections that resolve to different ids share no
42
+ * peers, groups, prekeys, app-state, or captured stanzas. When omitted,
43
+ * every connection uses one shared default session (single-client mode).
44
+ *
45
+ * The resolver runs once per connection, right after authentication, so
46
+ * `info.clientPayload` is available for keying by login identity, e.g.
47
+ * `sessionKey: ({ clientPayload }) => clientPayload.kind === 'login' ?
48
+ * clientPayload.username : 'pending'`.
49
+ */
50
+ readonly sessionKey?: (info: FakeSessionKeyInfo) => string;
51
+ }
52
+ export interface FakeSessionKeyInfo {
53
+ readonly clientPayload: ParsedClientPayload;
54
+ readonly pipeline: WaFakeConnectionPipeline;
25
55
  }
26
56
  export interface FakeWaServerNoiseRootCa {
27
57
  readonly publicKey: Uint8Array;
28
58
  readonly serial: number;
29
59
  }
30
60
  export type FakeWaServerPipelineListener = (pipeline: WaFakeConnectionPipeline) => void;
31
- export interface ExpectIqOptions {
32
- readonly timeoutMs?: number;
33
- }
34
- export interface ExpectStanzaOptions {
35
- readonly timeoutMs?: number;
36
- }
37
- export interface StanzaMatcher {
38
- readonly tag?: string;
39
- readonly type?: string;
40
- readonly xmlns?: string;
41
- readonly from?: string;
42
- readonly to?: string;
43
- readonly childTag?: string;
44
- }
61
+ export { FakeServerSession } from './FakeServerSession';
62
+ export type { ExpectIqOptions, ExpectStanzaOptions, StanzaMatcher } from './FakeServerSession';
45
63
  export interface CapturedMediaUpload {
46
64
  readonly path: string;
47
65
  readonly mediaType: string;
@@ -51,21 +69,18 @@ export interface CapturedMediaUpload {
51
69
  readonly receivedAtMs: number;
52
70
  }
53
71
  export declare class FakeWaServer {
54
- readonly registries: ServerRegistries;
55
- readonly preKeyDispenser: PreKeyDispenser;
56
- readonly appStateSync: AppStateSyncManager;
57
72
  private readonly wsServer;
58
73
  private readonly pipelines;
59
- private readonly iqRouter;
60
- private readonly capturedStanzas;
61
- private readonly pendingIqExpectations;
62
- private readonly pendingStanzaExpectations;
74
+ private readonly defaultSession;
75
+ private readonly pipelineSessions;
63
76
  private readonly authenticatedListeners;
64
- private readonly inboundStanzaListeners;
77
+ private readonly sessions;
78
+ private readonly globalIqHandlers;
79
+ private readonly options;
65
80
  private rootCa;
66
81
  private serverStaticKeyPair;
67
82
  private listenInfo;
68
- private pipelineListener;
83
+ private readonly pipelineListeners;
69
84
  private rejectMode;
70
85
  private readonly mediaStore;
71
86
  private readonly mediaHttpsServer;
@@ -73,7 +88,20 @@ export declare class FakeWaServer {
73
88
  private nextUploadCounter;
74
89
  private cachedMediaProxyAgent;
75
90
  constructor(options?: FakeWaServerOptions);
76
- private buildIqHandlerDeps;
91
+ /** State of the single default session (the only session for one client). */
92
+ get registries(): ServerRegistries;
93
+ get preKeyDispenser(): PreKeyDispenser;
94
+ get appStateSync(): AppStateSyncManager;
95
+ /**
96
+ * Returns the isolated session for `id`, creating it on first use. Every
97
+ * session gets its own registries, prekey dispenser, app-state, IQ router,
98
+ * and stanza capture. Use with the `sessionKey` option to run several
99
+ * `WaClient`s against one server without them sharing any state.
100
+ */
101
+ session(id: string): FakeServerSession;
102
+ /** The session a connection is bound to (default before authentication). */
103
+ sessionFor(pipeline: WaFakeConnectionPipeline): FakeServerSession;
104
+ private createSession;
77
105
  registerIqHandler(matcher: WaFakeIqMatcher, respond: WaFakeIqResponder, label?: string): () => void;
78
106
  routeIqForTest(iq: BinaryNode): Promise<BinaryNode | null>;
79
107
  onOutboundGroupOp(listener: (op: CapturedGroupOp) => void): () => void;
@@ -122,6 +150,15 @@ export declare class FakeWaServer {
122
150
  scenario(configure: (s: Scenario) => void): void;
123
151
  expectIq(matcher: WaFakeIqMatcher, options?: ExpectIqOptions): Promise<BinaryNode>;
124
152
  capturedStanzaSnapshot(): readonly BinaryNode[];
153
+ /**
154
+ * Subscribes to every stanza captured from the client side (the lib) of
155
+ * the default session. The listener is called synchronously for each new
156
+ * stanza as it arrives. Returns an unsubscribe function. Useful for
157
+ * benches that need to count or react to a stream of receipts/messages
158
+ * without paying the O(N²) cost of polling `capturedStanzaSnapshot()` or
159
+ * queuing one `expectStanza(...)` per iteration.
160
+ */
161
+ onCapturedStanza(listener: (node: BinaryNode) => void): () => void;
125
162
  expectStanza(matcher: StanzaMatcher, options?: ExpectStanzaOptions): Promise<BinaryNode>;
126
163
  broadcastStanza(node: BinaryNode): Promise<number>;
127
164
  waitForAuthenticatedPipeline(timeoutMs?: number): Promise<WaFakeConnectionPipeline>;
@@ -152,11 +189,21 @@ export declare class FakeWaServer {
152
189
  get host(): string;
153
190
  get port(): number;
154
191
  get noiseRootCa(): FakeWaServerNoiseRootCa;
155
- onPipeline(listener: FakeWaServerPipelineListener): void;
192
+ /**
193
+ * Subscribes to every new connection pipeline (pre-auth). Listeners
194
+ * fan out; returns an unsubscribe function.
195
+ */
196
+ onPipeline(listener: FakeWaServerPipelineListener): () => void;
156
197
  listen(): Promise<void>;
157
198
  stop(): Promise<void>;
158
199
  private handleConnection;
159
- private handleCapturedStanza;
200
+ /**
201
+ * Rebinds a connection to its keyed session once authenticated. Runs
202
+ * before user `onAuthenticated` listeners and before any client stanza, so
203
+ * captures and IQ routing land in the right session from the first frame.
204
+ * No-op without a `sessionKey` resolver (single default session).
205
+ */
206
+ private bindPipelineSession;
160
207
  private buildMediaRequestHandler;
161
208
  private handleMediaUpload;
162
209
  private requireListening;
@@ -164,4 +211,3 @@ export declare class FakeWaServer {
164
211
  }
165
212
  export type { WaFakeAuthenticatedInfo, WaFakeConnectionPipeline };
166
213
  export type { BinaryNode };
167
- //# sourceMappingURL=FakeWaServer.d.ts.map