@nerxa/socketon 0.0.0-stage → 1.21.1

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 (289) hide show
  1. package/EXPERIMENTAL.md +350 -0
  2. package/LICENSE +24 -0
  3. package/README.md +6317 -2
  4. package/WAProto/index.d.ts +17301 -0
  5. package/WAProto/index.js +125088 -0
  6. package/engine-requirements.js +14 -0
  7. package/package.json +125 -4
  8. package/src/Defaults/index.d.ts +152 -0
  9. package/src/Defaults/index.js +164 -0
  10. package/src/MessageBuilder/bot-signature.d.ts +22 -0
  11. package/src/MessageBuilder/bot-signature.js +122 -0
  12. package/src/MessageBuilder/extras.d.ts +449 -0
  13. package/src/MessageBuilder/extras.js +1356 -0
  14. package/src/MessageBuilder/index.d.ts +210 -0
  15. package/src/MessageBuilder/index.js +3594 -0
  16. package/src/MessageBuilder/metaai.d.ts +438 -0
  17. package/src/MessageBuilder/metaai.js +890 -0
  18. package/src/Signal/Group/ciphertext-message.d.ts +9 -0
  19. package/src/Signal/Group/ciphertext-message.js +11 -0
  20. package/src/Signal/Group/group-session-builder.d.ts +7 -0
  21. package/src/Signal/Group/group-session-builder.js +27 -0
  22. package/src/Signal/Group/group_cipher.d.ts +11 -0
  23. package/src/Signal/Group/group_cipher.js +78 -0
  24. package/src/Signal/Group/index.d.ts +11 -0
  25. package/src/Signal/Group/index.js +11 -0
  26. package/src/Signal/Group/keyhelper.d.ts +6 -0
  27. package/src/Signal/Group/keyhelper.js +17 -0
  28. package/src/Signal/Group/sender-chain-key.d.ts +13 -0
  29. package/src/Signal/Group/sender-chain-key.js +25 -0
  30. package/src/Signal/Group/sender-key-distribution-message.d.ts +16 -0
  31. package/src/Signal/Group/sender-key-distribution-message.js +62 -0
  32. package/src/Signal/Group/sender-key-message.d.ts +18 -0
  33. package/src/Signal/Group/sender-key-message.js +65 -0
  34. package/src/Signal/Group/sender-key-name.d.ts +11 -0
  35. package/src/Signal/Group/sender-key-name.js +47 -0
  36. package/src/Signal/Group/sender-key-record.d.ts +12 -0
  37. package/src/Signal/Group/sender-key-record.js +40 -0
  38. package/src/Signal/Group/sender-key-state.d.ts +16 -0
  39. package/src/Signal/Group/sender-key-state.js +83 -0
  40. package/src/Signal/Group/sender-message-key.d.ts +11 -0
  41. package/src/Signal/Group/sender-message-key.js +25 -0
  42. package/src/Signal/libsignal.d.ts +54 -0
  43. package/src/Signal/libsignal.js +427 -0
  44. package/src/Signal/lid-mapping.d.ts +20 -0
  45. package/src/Signal/lid-mapping.js +274 -0
  46. package/src/Socket/Client/index.d.ts +2 -0
  47. package/src/Socket/Client/index.js +3 -0
  48. package/src/Socket/Client/tcp.js +177 -0
  49. package/src/Socket/Client/types.d.ts +5 -0
  50. package/src/Socket/Client/types.js +9 -0
  51. package/src/Socket/Client/websocket.d.ts +12 -0
  52. package/src/Socket/Client/websocket.js +53 -0
  53. package/src/Socket/business.d.ts +431 -0
  54. package/src/Socket/business.js +379 -0
  55. package/src/Socket/chats.d.ts +247 -0
  56. package/src/Socket/chats.js +1484 -0
  57. package/src/Socket/communities.d.ts +598 -0
  58. package/src/Socket/communities.js +421 -0
  59. package/src/Socket/email.js +145 -0
  60. package/src/Socket/groups.d.ts +298 -0
  61. package/src/Socket/groups.js +339 -0
  62. package/src/Socket/index.d.ts +581 -0
  63. package/src/Socket/index.js +77 -0
  64. package/src/Socket/messages-recv.d.ts +370 -0
  65. package/src/Socket/messages-recv.js +1864 -0
  66. package/src/Socket/messages-send.d.ts +390 -0
  67. package/src/Socket/messages-send.js +1574 -0
  68. package/src/Socket/mex.d.ts +1 -0
  69. package/src/Socket/mex.js +44 -0
  70. package/src/Socket/mobile-builders.js +72 -0
  71. package/src/Socket/mobile.js +424 -0
  72. package/src/Socket/newsletter.d.ts +468 -0
  73. package/src/Socket/newsletter.js +671 -0
  74. package/src/Socket/socket.d.ts +77 -0
  75. package/src/Socket/socket.js +1106 -0
  76. package/src/Store/index.d.ts +3 -0
  77. package/src/Store/index.js +3 -0
  78. package/src/Store/make-in-memory-store.d.ts +62 -0
  79. package/src/Store/make-in-memory-store.js +396 -0
  80. package/src/Store/make-ordered-dictionary.d.ts +12 -0
  81. package/src/Store/make-ordered-dictionary.js +78 -0
  82. package/src/Store/object-repository.d.ts +10 -0
  83. package/src/Store/object-repository.js +23 -0
  84. package/src/Types/Auth.d.ts +1 -0
  85. package/src/Types/Auth.js +1 -0
  86. package/src/Types/Bussines.d.ts +1 -0
  87. package/src/Types/Bussines.js +1 -0
  88. package/src/Types/Call.d.ts +1 -0
  89. package/src/Types/Call.js +1 -0
  90. package/src/Types/Chat.d.ts +2 -0
  91. package/src/Types/Chat.js +24 -0
  92. package/src/Types/Contact.d.ts +1 -0
  93. package/src/Types/Contact.js +1 -0
  94. package/src/Types/Events.d.ts +1 -0
  95. package/src/Types/Events.js +0 -0
  96. package/src/Types/GroupMetadata.d.ts +5 -0
  97. package/src/Types/GroupMetadata.js +6 -0
  98. package/src/Types/Label.d.ts +2 -0
  99. package/src/Types/Label.js +24 -0
  100. package/src/Types/LabelAssociation.d.ts +2 -0
  101. package/src/Types/LabelAssociation.js +6 -0
  102. package/src/Types/Message.d.ts +13 -0
  103. package/src/Types/Message.js +23 -0
  104. package/src/Types/Mex.d.ts +2 -0
  105. package/src/Types/Mex.js +95 -0
  106. package/src/Types/Product.d.ts +1 -0
  107. package/src/Types/Product.js +1 -0
  108. package/src/Types/Signal.d.ts +1 -0
  109. package/src/Types/Signal.js +0 -0
  110. package/src/Types/Socket.d.ts +1 -0
  111. package/src/Types/Socket.js +0 -0
  112. package/src/Types/State.d.ts +5 -0
  113. package/src/Types/State.js +54 -0
  114. package/src/Types/USync.d.ts +1 -0
  115. package/src/Types/USync.js +0 -0
  116. package/src/Types/index.d.ts +13 -0
  117. package/src/Types/index.js +25 -0
  118. package/src/Utils/auth-store.d.ts +29 -0
  119. package/src/Utils/auth-store.js +76 -0
  120. package/src/Utils/auth-utils.d.ts +58 -0
  121. package/src/Utils/auth-utils.js +273 -0
  122. package/src/Utils/browser-utils.d.ts +9 -0
  123. package/src/Utils/browser-utils.js +28 -0
  124. package/src/Utils/business.d.ts +49 -0
  125. package/src/Utils/business.js +226 -0
  126. package/src/Utils/chat-utils.d.ts +408 -0
  127. package/src/Utils/chat-utils.js +846 -0
  128. package/src/Utils/companion-reg-client-utils.d.ts +4 -0
  129. package/src/Utils/companion-reg-client-utils.js +39 -0
  130. package/src/Utils/crypto.d.ts +39 -0
  131. package/src/Utils/crypto.js +110 -0
  132. package/src/Utils/decode-wa-message.d.ts +99 -0
  133. package/src/Utils/decode-wa-message.js +373 -0
  134. package/src/Utils/event-buffer.d.ts +22 -0
  135. package/src/Utils/event-buffer.js +624 -0
  136. package/src/Utils/generics.d.ts +63 -0
  137. package/src/Utils/generics.js +375 -0
  138. package/src/Utils/group-metadata-cache.d.ts +4 -0
  139. package/src/Utils/group-metadata-cache.js +25 -0
  140. package/src/Utils/history.d.ts +75 -0
  141. package/src/Utils/history.js +131 -0
  142. package/src/Utils/html-app.d.ts +19 -0
  143. package/src/Utils/html-app.js +96 -0
  144. package/src/Utils/html-multiplayer.d.ts +10 -0
  145. package/src/Utils/html-multiplayer.js +94 -0
  146. package/src/Utils/identity-change-handler.d.ts +13 -0
  147. package/src/Utils/identity-change-handler.js +48 -0
  148. package/src/Utils/index.d.ts +44 -0
  149. package/src/Utils/index.js +44 -0
  150. package/src/Utils/link-preview-metadata.d.ts +47 -0
  151. package/src/Utils/link-preview-metadata.js +88 -0
  152. package/src/Utils/link-preview.d.ts +12 -0
  153. package/src/Utils/link-preview.js +79 -0
  154. package/src/Utils/logger.d.ts +2 -0
  155. package/src/Utils/logger.js +2 -0
  156. package/src/Utils/lt-hash.d.ts +6 -0
  157. package/src/Utils/lt-hash.js +3 -0
  158. package/src/Utils/make-mutex.d.ts +6 -0
  159. package/src/Utils/make-mutex.js +32 -0
  160. package/src/Utils/message-retry-manager.d.ts +79 -0
  161. package/src/Utils/message-retry-manager.js +255 -0
  162. package/src/Utils/messages-media.d.ts +136 -0
  163. package/src/Utils/messages-media.js +814 -0
  164. package/src/Utils/messages.d.ts +62 -0
  165. package/src/Utils/messages.js +2223 -0
  166. package/src/Utils/mobile-companion-crypto.js +191 -0
  167. package/src/Utils/modern-messages.d.ts +150 -0
  168. package/src/Utils/modern-messages.js +496 -0
  169. package/src/Utils/native-flow.d.ts +11 -0
  170. package/src/Utils/native-flow.js +115 -0
  171. package/src/Utils/newsletter-status.d.ts +158 -0
  172. package/src/Utils/newsletter-status.js +651 -0
  173. package/src/Utils/noise-handler.d.ts +20 -0
  174. package/src/Utils/noise-handler.js +200 -0
  175. package/src/Utils/offline-node-processor.d.ts +9 -0
  176. package/src/Utils/offline-node-processor.js +33 -0
  177. package/src/Utils/optional-media.d.ts +3 -0
  178. package/src/Utils/optional-media.js +45 -0
  179. package/src/Utils/payment-messages.d.ts +66 -0
  180. package/src/Utils/payment-messages.js +160 -0
  181. package/src/Utils/pre-key-manager.d.ts +25 -0
  182. package/src/Utils/pre-key-manager.js +95 -0
  183. package/src/Utils/process-message.d.ts +60 -0
  184. package/src/Utils/process-message.js +688 -0
  185. package/src/Utils/reporting-utils.d.ts +12 -0
  186. package/src/Utils/reporting-utils.js +256 -0
  187. package/src/Utils/scheduled-message.d.ts +51 -0
  188. package/src/Utils/scheduled-message.js +112 -0
  189. package/src/Utils/sender-key-memory.d.ts +19 -0
  190. package/src/Utils/sender-key-memory.js +51 -0
  191. package/src/Utils/signal.d.ts +82 -0
  192. package/src/Utils/signal.js +193 -0
  193. package/src/Utils/stanza-ack.d.ts +16 -0
  194. package/src/Utils/stanza-ack.js +30 -0
  195. package/src/Utils/status-stickers.d.ts +127 -0
  196. package/src/Utils/status-stickers.js +260 -0
  197. package/src/Utils/sync-action-utils.d.ts +2 -0
  198. package/src/Utils/sync-action-utils.js +44 -0
  199. package/src/Utils/tc-token-utils.d.ts +29 -0
  200. package/src/Utils/tc-token-utils.js +155 -0
  201. package/src/Utils/ttl-cache.d.ts +28 -0
  202. package/src/Utils/ttl-cache.js +120 -0
  203. package/src/Utils/use-mongo-auth-state.d.ts +21 -0
  204. package/src/Utils/use-mongo-auth-state.js +67 -0
  205. package/src/Utils/use-multi-file-auth-state.d.ts +11 -0
  206. package/src/Utils/use-multi-file-auth-state.js +106 -0
  207. package/src/Utils/use-mysql-auth-state.d.ts +19 -0
  208. package/src/Utils/use-mysql-auth-state.js +79 -0
  209. package/src/Utils/use-postgres-auth-state.d.ts +19 -0
  210. package/src/Utils/use-postgres-auth-state.js +79 -0
  211. package/src/Utils/use-redis-auth-state.d.ts +19 -0
  212. package/src/Utils/use-redis-auth-state.js +89 -0
  213. package/src/Utils/use-single-file-auth-state.d.ts +10 -0
  214. package/src/Utils/use-single-file-auth-state.js +108 -0
  215. package/src/Utils/use-sqlite-auth-state.d.ts +12 -0
  216. package/src/Utils/use-sqlite-auth-state.js +120 -0
  217. package/src/Utils/useNekoDBAuth.d.ts +19 -0
  218. package/src/Utils/useNekoDBAuth.js +111 -0
  219. package/src/Utils/username.d.ts +12 -0
  220. package/src/Utils/username.js +58 -0
  221. package/src/Utils/validate-connection.d.ts +43 -0
  222. package/src/Utils/validate-connection.js +277 -0
  223. package/src/Utils/voice-recognition.d.ts +53 -0
  224. package/src/Utils/voice-recognition.js +219 -0
  225. package/src/Voip/audio-feeder.d.ts +24 -0
  226. package/src/Voip/audio-feeder.js +268 -0
  227. package/src/Voip/index.d.ts +88 -0
  228. package/src/Voip/index.js +936 -0
  229. package/src/Voip/relay-transport.d.ts +5 -0
  230. package/src/Voip/relay-transport.js +824 -0
  231. package/src/Voip/signaling.d.ts +11 -0
  232. package/src/Voip/signaling.js +1099 -0
  233. package/src/Voip/types.d.ts +2 -0
  234. package/src/Voip/types.js +12 -0
  235. package/src/Voip/video-feeder.d.ts +24 -0
  236. package/src/Voip/video-feeder.js +268 -0
  237. package/src/Voip/wasm-engine.d.ts +26 -0
  238. package/src/Voip/wasm-engine.js +1307 -0
  239. package/src/Voip/worker-bootstrap.js +1023 -0
  240. package/src/WABinary/constants.d.ts +22 -0
  241. package/src/WABinary/constants.js +1300 -0
  242. package/src/WABinary/decode.d.ts +9 -0
  243. package/src/WABinary/decode.js +261 -0
  244. package/src/WABinary/encode.d.ts +2 -0
  245. package/src/WABinary/encode.js +219 -0
  246. package/src/WABinary/generic-utils.d.ts +76 -0
  247. package/src/WABinary/generic-utils.js +214 -0
  248. package/src/WABinary/index.d.ts +5 -0
  249. package/src/WABinary/index.js +5 -0
  250. package/src/WABinary/jid-utils.d.ts +30 -0
  251. package/src/WABinary/jid-utils.js +97 -0
  252. package/src/WABinary/types.d.ts +1 -0
  253. package/src/WABinary/types.js +0 -0
  254. package/src/WAM/BinaryInfo.d.ts +7 -0
  255. package/src/WAM/BinaryInfo.js +9 -0
  256. package/src/WAM/constants.d.ts +34926 -0
  257. package/src/WAM/constants.js +22852 -0
  258. package/src/WAM/encode.d.ts +1 -0
  259. package/src/WAM/encode.js +148 -0
  260. package/src/WAM/index.d.ts +3 -0
  261. package/src/WAM/index.js +3 -0
  262. package/src/WAUSync/Protocols/USyncContactProtocol.d.ts +36 -0
  263. package/src/WAUSync/Protocols/USyncContactProtocol.js +50 -0
  264. package/src/WAUSync/Protocols/USyncDeviceProtocol.d.ts +22 -0
  265. package/src/WAUSync/Protocols/USyncDeviceProtocol.js +51 -0
  266. package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +12 -0
  267. package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -0
  268. package/src/WAUSync/Protocols/USyncStatusProtocol.d.ts +12 -0
  269. package/src/WAUSync/Protocols/USyncStatusProtocol.js +37 -0
  270. package/src/WAUSync/Protocols/USyncUsernameProtocol.d.ts +9 -0
  271. package/src/WAUSync/Protocols/USyncUsernameProtocol.js +23 -0
  272. package/src/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +38 -0
  273. package/src/WAUSync/Protocols/UsyncBotProfileProtocol.js +49 -0
  274. package/src/WAUSync/Protocols/UsyncLIDProtocol.d.ts +14 -0
  275. package/src/WAUSync/Protocols/UsyncLIDProtocol.js +28 -0
  276. package/src/WAUSync/Protocols/index.d.ts +5 -0
  277. package/src/WAUSync/Protocols/index.js +5 -0
  278. package/src/WAUSync/USyncQuery.d.ts +20 -0
  279. package/src/WAUSync/USyncQuery.js +93 -0
  280. package/src/WAUSync/USyncUser.d.ts +16 -0
  281. package/src/WAUSync/USyncUser.js +30 -0
  282. package/src/WAUSync/index.d.ts +3 -0
  283. package/src/WAUSync/index.js +3 -0
  284. package/src/assets/wasm/README.md +76 -0
  285. package/src/assets/wasm/loader.js +5 -0
  286. package/src/assets/wasm/whatsapp.wasm +0 -0
  287. package/src/assets/wasm/worker-modules.js +273 -0
  288. package/src/index.d.ts +43 -0
  289. package/src/index.js +48 -0
@@ -0,0 +1,200 @@
1
+ import { Boom } from '@hapi/boom';
2
+ import { proto } from '../../WAProto/index.js';
3
+ import { NOISE_MODE, WA_CERT_DETAILS } from '../Defaults/index.js';
4
+ import { decodeBinaryNode } from '../WABinary/index.js';
5
+ import { aesDecryptGCM, aesEncryptGCM, Curve, hkdf, sha256 } from './crypto.js';
6
+ const IV_LENGTH = 12;
7
+ const EMPTY_BUFFER = Buffer.alloc(0);
8
+ const generateIV = (counter) => {
9
+ const iv = new ArrayBuffer(IV_LENGTH);
10
+ new DataView(iv).setUint32(8, counter);
11
+ return new Uint8Array(iv);
12
+ };
13
+ class TransportState {
14
+ constructor(encKey, decKey) {
15
+ this.encKey = encKey;
16
+ this.decKey = decKey;
17
+ this.readCounter = 0;
18
+ this.writeCounter = 0;
19
+ this.iv = new Uint8Array(IV_LENGTH);
20
+ }
21
+ encrypt(plaintext) {
22
+ const c = this.writeCounter++;
23
+ this.iv[8] = (c >>> 24) & 0xff;
24
+ this.iv[9] = (c >>> 16) & 0xff;
25
+ this.iv[10] = (c >>> 8) & 0xff;
26
+ this.iv[11] = c & 0xff;
27
+ return aesEncryptGCM(plaintext, this.encKey, this.iv, EMPTY_BUFFER);
28
+ }
29
+ decrypt(ciphertext) {
30
+ const c = this.readCounter++;
31
+ this.iv[8] = (c >>> 24) & 0xff;
32
+ this.iv[9] = (c >>> 16) & 0xff;
33
+ this.iv[10] = (c >>> 8) & 0xff;
34
+ this.iv[11] = c & 0xff;
35
+ return aesDecryptGCM(ciphertext, this.decKey, this.iv, EMPTY_BUFFER);
36
+ }
37
+ }
38
+ export const makeNoiseHandler = ({ keyPair: { private: privateKey, public: publicKey }, NOISE_HEADER, logger, routingInfo }) => {
39
+ logger = logger.child({ class: 'ns' });
40
+ const data = Buffer.from(NOISE_MODE);
41
+ let hash = data.byteLength === 32 ? data : sha256(data);
42
+ let salt = hash;
43
+ let encKey = hash;
44
+ let decKey = hash;
45
+ let counter = 0;
46
+ let sentIntro = false;
47
+ let inBytes = Buffer.alloc(0);
48
+ let transport = null;
49
+ let isWaitingForTransport = false;
50
+ let pendingOnFrame = null;
51
+ let introHeader;
52
+ if (routingInfo) {
53
+ introHeader = Buffer.alloc(7 + routingInfo.byteLength + NOISE_HEADER.length);
54
+ introHeader.write('ED', 0, 'utf8');
55
+ introHeader.writeUint8(0, 2);
56
+ introHeader.writeUint8(1, 3);
57
+ introHeader.writeUint8(routingInfo.byteLength >> 16, 4);
58
+ introHeader.writeUint16BE(routingInfo.byteLength & 65535, 5);
59
+ introHeader.set(routingInfo, 7);
60
+ introHeader.set(NOISE_HEADER, 7 + routingInfo.byteLength);
61
+ }
62
+ else {
63
+ introHeader = Buffer.from(NOISE_HEADER);
64
+ }
65
+ const authenticate = (data) => {
66
+ if (!transport) {
67
+ hash = sha256(Buffer.concat([hash, data]));
68
+ }
69
+ };
70
+ const encrypt = (plaintext) => {
71
+ if (transport) {
72
+ return transport.encrypt(plaintext);
73
+ }
74
+ const result = aesEncryptGCM(plaintext, encKey, generateIV(counter++), hash);
75
+ authenticate(result);
76
+ return result;
77
+ };
78
+ const decrypt = (ciphertext) => {
79
+ if (transport) {
80
+ return transport.decrypt(ciphertext);
81
+ }
82
+ const result = aesDecryptGCM(ciphertext, decKey, generateIV(counter++), hash);
83
+ authenticate(ciphertext);
84
+ return result;
85
+ };
86
+ const localHKDF = (data) => {
87
+ const key = hkdf(Buffer.from(data), 64, { salt, info: '' });
88
+ return [key.subarray(0, 32), key.subarray(32)];
89
+ };
90
+ const mixIntoKey = (data) => {
91
+ const [write, read] = localHKDF(data);
92
+ salt = write;
93
+ encKey = read;
94
+ decKey = read;
95
+ counter = 0;
96
+ };
97
+ const finishInit = async () => {
98
+ isWaitingForTransport = true;
99
+ const [write, read] = localHKDF(new Uint8Array(0));
100
+ transport = new TransportState(write, read);
101
+ isWaitingForTransport = false;
102
+ logger.trace('Noise handler transitioned to Transport state');
103
+ if (pendingOnFrame) {
104
+ logger.trace({ length: inBytes.length }, 'Flushing buffered frames after transport ready');
105
+ await processData(pendingOnFrame);
106
+ pendingOnFrame = null;
107
+ }
108
+ };
109
+ const processData = async (onFrame) => {
110
+ let size;
111
+ while (true) {
112
+ if (inBytes.length < 3)
113
+ return;
114
+ size = (inBytes[0] << 16) | (inBytes[1] << 8) | inBytes[2];
115
+ if (inBytes.length < size + 3)
116
+ return;
117
+ let frame = inBytes.subarray(3, size + 3);
118
+ inBytes = inBytes.subarray(size + 3);
119
+ if (transport) {
120
+ const result = transport.decrypt(frame);
121
+ frame = await decodeBinaryNode(result);
122
+ }
123
+ if (logger.level === 'trace') {
124
+ logger.trace({ msg: frame?.attrs?.id }, 'recv frame');
125
+ }
126
+ onFrame(frame);
127
+ }
128
+ };
129
+ authenticate(NOISE_HEADER);
130
+ authenticate(publicKey);
131
+ return {
132
+ encrypt,
133
+ decrypt,
134
+ authenticate,
135
+ mixIntoKey,
136
+ finishInit,
137
+ processHandshake: ({ serverHello }, noiseKey) => {
138
+ authenticate(serverHello.ephemeral);
139
+ mixIntoKey(Curve.sharedKey(privateKey, serverHello.ephemeral));
140
+ const decStaticContent = decrypt(serverHello.static);
141
+ mixIntoKey(Curve.sharedKey(privateKey, decStaticContent));
142
+ const certDecoded = decrypt(serverHello.payload);
143
+ const { intermediate: certIntermediate, leaf } = proto.CertChain.decode(certDecoded);
144
+
145
+ if (!leaf?.details || !leaf?.signature) {
146
+ throw new Boom('invalid noise leaf certificate', { statusCode: 400 });
147
+ }
148
+ if (!certIntermediate?.details || !certIntermediate?.signature) {
149
+ throw new Boom('invalid noise intermediate certificate', { statusCode: 400 });
150
+ }
151
+ const details = proto.CertChain.NoiseCertificate.Details.decode(certIntermediate.details);
152
+ const { issuerSerial } = details;
153
+ const verify = Curve.verify(details.key, leaf.details, leaf.signature);
154
+ const verifyIntermediate = Curve.verify(WA_CERT_DETAILS.PUBLIC_KEY, certIntermediate.details, certIntermediate.signature);
155
+ if (!verify) {
156
+ throw new Boom('noise certificate signature invalid', { statusCode: 400 });
157
+ }
158
+ if (!verifyIntermediate) {
159
+ throw new Boom('noise intermediate certificate signature invalid', { statusCode: 400 });
160
+ }
161
+ if (issuerSerial !== WA_CERT_DETAILS.SERIAL) {
162
+ throw new Boom('certification match failed', { statusCode: 400 });
163
+ }
164
+ const keyEnc = encrypt(noiseKey.public);
165
+ mixIntoKey(Curve.sharedKey(noiseKey.private, serverHello.ephemeral));
166
+ return keyEnc;
167
+ },
168
+ encodeFrame: (data) => {
169
+ if (transport) {
170
+ data = transport.encrypt(data);
171
+ }
172
+ const dataLen = data.byteLength;
173
+ const introSize = sentIntro ? 0 : introHeader.length;
174
+ const frame = Buffer.allocUnsafe(introSize + 3 + dataLen);
175
+ if (!sentIntro) {
176
+ frame.set(introHeader);
177
+ sentIntro = true;
178
+ }
179
+ frame[introSize] = (dataLen >>> 16) & 0xff;
180
+ frame[introSize + 1] = (dataLen >>> 8) & 0xff;
181
+ frame[introSize + 2] = dataLen & 0xff;
182
+ frame.set(data, introSize + 3);
183
+ return frame;
184
+ },
185
+ decodeFrame: async (newData, onFrame) => {
186
+ if (isWaitingForTransport) {
187
+ inBytes = Buffer.concat([inBytes, newData]);
188
+ pendingOnFrame = onFrame;
189
+ return;
190
+ }
191
+ if (inBytes.length === 0) {
192
+ inBytes = Buffer.from(newData);
193
+ }
194
+ else {
195
+ inBytes = Buffer.concat([inBytes, newData]);
196
+ }
197
+ await processData(onFrame);
198
+ }
199
+ };
200
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Creates a processor for offline stanza nodes that:
3
+ * - Queues nodes for sequential processing
4
+ * - Yields to the event loop periodically to avoid blocking
5
+ * - Catches handler errors to prevent the processing loop from crashing
6
+ */
7
+ export function makeOfflineNodeProcessor(nodeProcessorMap: any, deps: any, batchSize?: number): {
8
+ enqueue: (type: any, node: any) => void;
9
+ };
@@ -0,0 +1,33 @@
1
+
2
+ export function makeOfflineNodeProcessor(nodeProcessorMap, deps, batchSize = 10) {
3
+ const nodes = [];
4
+ let isProcessing = false;
5
+ const enqueue = (type, node) => {
6
+ nodes.push({ type, node });
7
+ if (isProcessing) {
8
+ return;
9
+ }
10
+ isProcessing = true;
11
+ const promise = async () => {
12
+ let processedInBatch = 0;
13
+ while (nodes.length && deps.isWsOpen()) {
14
+ const { type, node } = nodes.shift();
15
+ const nodeProcessor = nodeProcessorMap.get(type);
16
+ if (!nodeProcessor) {
17
+ deps.onUnexpectedError(new Error(`unknown offline node type: ${type}`), 'processing offline node');
18
+ continue;
19
+ }
20
+ await nodeProcessor(node).catch(err => deps.onUnexpectedError(err, `processing offline ${type}`));
21
+ processedInBatch++;
22
+
23
+ if (processedInBatch >= batchSize) {
24
+ processedInBatch = 0;
25
+ await deps.yieldToEventLoop();
26
+ }
27
+ }
28
+ isProcessing = false;
29
+ };
30
+ promise().catch(error => deps.onUnexpectedError(error, 'processing offline nodes'));
31
+ };
32
+ return { enqueue };
33
+ }
@@ -0,0 +1,3 @@
1
+ export declare function loadSharp(): Promise<any>
2
+ export declare function loadFfmpeg(): Promise<any>
3
+ export declare function hasOptionalMedia(name: 'sharp' | 'fluent-ffmpeg'): Promise<boolean>
@@ -0,0 +1,45 @@
1
+ const cache = new Map()
2
+
3
+ const MESSAGES = {
4
+ sharp: 'sharp is an optional dependency. Install it with: npm i sharp',
5
+ 'fluent-ffmpeg': 'fluent-ffmpeg is an optional dependency. Install it with: npm i fluent-ffmpeg'
6
+ }
7
+
8
+ const isMissingModule = (error, name) => {
9
+ const code = error?.code
10
+ if (code !== 'ERR_MODULE_NOT_FOUND' && code !== 'MODULE_NOT_FOUND') {
11
+ return false
12
+ }
13
+ const message = String(error?.message ?? '')
14
+ return message.includes(`'${name}'`) || message.includes(`"${name}"`)
15
+ }
16
+
17
+ const loadOptional = (name) => {
18
+ if (!cache.has(name)) {
19
+ cache.set(name, import(name).then(
20
+ loaded => loaded?.default ?? loaded,
21
+ error => {
22
+ cache.delete(name)
23
+ if (isMissingModule(error, name)) {
24
+ throw new Error(MESSAGES[name] ?? `${name} is an optional dependency. Install it with: npm i ${name}`)
25
+ }
26
+ throw error
27
+ }
28
+ ))
29
+ }
30
+ return cache.get(name)
31
+ }
32
+
33
+ export const loadSharp = () => loadOptional('sharp')
34
+
35
+ export const loadFfmpeg = () => loadOptional('fluent-ffmpeg')
36
+
37
+ export const hasOptionalMedia = async (name) => {
38
+ try {
39
+ await loadOptional(name)
40
+ return true
41
+ }
42
+ catch {
43
+ return false
44
+ }
45
+ }
@@ -0,0 +1,66 @@
1
+ import type { proto } from '../../WAProto/index.js';
2
+
3
+ export declare const SplitPaymentStatus: Readonly<{ PENDING: 0; PAID: 1 }>;
4
+ export declare const ReminderFrequency: Readonly<Record<string, number>>;
5
+ export declare const ReminderStatus: Readonly<Record<string, number>>;
6
+ export declare const MONEY_OFFSET: 1000;
7
+
8
+ /** Takes the human amount; the wire value is amount * offset. */
9
+ export declare function money(amount: number, currencyCode: string, offset?: number): proto.IMoney;
10
+ export declare function readMoney(value?: proto.IMoney | null): { amount: number; currencyCode?: string } | undefined;
11
+
12
+ export interface SplitPaymentParticipantOptions {
13
+ jid: string;
14
+ amount?: number;
15
+ currency?: string;
16
+ status?: number;
17
+ }
18
+
19
+ export declare function buildSplitPayment(options: {
20
+ splitId: string | number;
21
+ total?: number;
22
+ currency?: string;
23
+ description?: string;
24
+ requesterJid?: string;
25
+ participants: SplitPaymentParticipantOptions[];
26
+ createdAt?: number;
27
+ }): proto.Message.ISplitPaymentMessage;
28
+
29
+ export declare function buildSplitPaymentUpdate(options: {
30
+ splitId: string | number;
31
+ participantJid: string;
32
+ }): proto.Message.ISplitPaymentUpdateMessage;
33
+
34
+ export declare function buildPaymentReminder(options: {
35
+ reminderId: string | number;
36
+ instanceId?: string | number;
37
+ description?: string;
38
+ frequency?: number;
39
+ status?: number;
40
+ amount?: number;
41
+ currency?: string;
42
+ payeeVpa?: string;
43
+ payeeJid?: string;
44
+ payerJid?: string;
45
+ }): proto.Message.IPaymentReminderMessage;
46
+
47
+ export declare function readSplitPayment(msg: any): {
48
+ splitId?: string;
49
+ total?: { amount: number; currencyCode?: string };
50
+ description?: string;
51
+ requesterJid?: string;
52
+ createdAtMs?: number;
53
+ participants: { jid?: string; amount?: { amount: number; currencyCode?: string }; status?: number }[];
54
+ } | null;
55
+
56
+ export declare function readPaymentReminder(msg: any): {
57
+ reminderId?: string;
58
+ instanceId?: string;
59
+ description?: string;
60
+ frequency?: number;
61
+ status?: number;
62
+ amount?: { amount: number; currencyCode?: string };
63
+ payeeVpa?: string;
64
+ payeeJid?: string;
65
+ payerJid?: string;
66
+ } | null;
@@ -0,0 +1,160 @@
1
+
2
+ export const SplitPaymentStatus = Object.freeze({ PENDING: 0, PAID: 1 })
3
+
4
+ export const ReminderFrequency = Object.freeze({
5
+ REMINDER_FREQUENCY_UNKNOWN: 0,
6
+ WEEKLY: 1,
7
+ BI_WEEKLY: 2,
8
+ MONTHLY: 3,
9
+ QUARTERLY: 4
10
+ })
11
+
12
+ export const ReminderStatus = Object.freeze({
13
+ REMINDER_STATUS_UNKNOWN: 0,
14
+ ACTIVE: 1,
15
+ CANCELLED_BY_CREATOR: 2,
16
+ STOPPED_BY_RECEIVER: 3,
17
+ EXPIRED: 4,
18
+ PAID: 5
19
+ })
20
+
21
+ export const MONEY_OFFSET = 1000
22
+
23
+ const drop = (object) => {
24
+ for (const key of Object.keys(object)) {
25
+ if (object[key] === undefined || object[key] === null) {
26
+ delete object[key]
27
+ }
28
+ }
29
+ return object
30
+ }
31
+
32
+ /**
33
+ * Money travels as an integer scaled by offset, so 50000 rupiah at the usual
34
+ * offset of 1000 goes on the wire as 50000000. Passing the scaled value by
35
+ * mistake overcharges by three orders of magnitude, so the amount here is
36
+ * always the human one.
37
+ */
38
+ export const money = (amount, currencyCode, offset = MONEY_OFFSET) => {
39
+ const value = Number(amount)
40
+ if (!Number.isFinite(value) || value < 0) {
41
+ throw new TypeError('money needs a non-negative amount')
42
+ }
43
+ if (typeof currencyCode !== 'string' || currencyCode.length !== 3) {
44
+ throw new TypeError('money needs a three letter currency code, like IDR or USD')
45
+ }
46
+ if (!Number.isInteger(offset) || offset <= 0) {
47
+ throw new TypeError('money offset must be a positive integer')
48
+ }
49
+ return { value: Math.round(value * offset), offset, currencyCode: currencyCode.toUpperCase() }
50
+ }
51
+
52
+ export const readMoney = (value) => {
53
+ if (!value?.offset) {
54
+ return undefined
55
+ }
56
+ return {
57
+ amount: Number(value.value) / Number(value.offset),
58
+ currencyCode: value.currencyCode
59
+ }
60
+ }
61
+
62
+ export const buildSplitPayment = ({ splitId, total, currency, description, requesterJid, participants = [], createdAt } = {}) => {
63
+ if (!splitId) {
64
+ throw new TypeError('buildSplitPayment needs a splitId, the update message is keyed on it')
65
+ }
66
+ if (!participants.length) {
67
+ throw new TypeError('buildSplitPayment needs at least one participant')
68
+ }
69
+ return drop({
70
+ splitId: String(splitId),
71
+ totalAmount: total === undefined ? undefined : money(total, currency),
72
+ description,
73
+ requesterJid,
74
+ participants: participants.map((participant, index) => {
75
+ if (!participant?.jid) {
76
+ throw new TypeError(`participant ${index} needs a jid`)
77
+ }
78
+ return drop({
79
+ jid: participant.jid,
80
+ amount: participant.amount === undefined ? undefined : money(participant.amount, participant.currency ?? currency),
81
+ status: participant.status ?? SplitPaymentStatus.PENDING
82
+ })
83
+ }),
84
+ createdAtMs: createdAt === undefined ? Date.now() : Number(createdAt)
85
+ })
86
+ }
87
+
88
+ export const buildSplitPaymentUpdate = ({ splitId, participantJid } = {}) => {
89
+ if (!splitId || !participantJid) {
90
+ throw new TypeError('buildSplitPaymentUpdate needs both splitId and participantJid')
91
+ }
92
+ return { splitId: String(splitId), participantJid }
93
+ }
94
+
95
+ export const buildPaymentReminder = ({
96
+ reminderId,
97
+ instanceId,
98
+ description,
99
+ frequency = ReminderFrequency.MONTHLY,
100
+ status = ReminderStatus.ACTIVE,
101
+ amount,
102
+ currency,
103
+ payeeVpa,
104
+ payeeJid,
105
+ payerJid
106
+ } = {}) => {
107
+ if (!reminderId) {
108
+ throw new TypeError('buildPaymentReminder needs a reminderId')
109
+ }
110
+ return drop({
111
+ reminderId: String(reminderId),
112
+ instanceId: instanceId === undefined ? undefined : String(instanceId),
113
+ description,
114
+ frequency,
115
+ status,
116
+ amount: amount === undefined ? undefined : money(amount, currency),
117
+ payeeVpa,
118
+ payeeJid,
119
+ payerJid
120
+ })
121
+ }
122
+
123
+ export const readSplitPayment = (msg) => {
124
+ const message = msg?.message ?? msg
125
+ const split = message?.splitPaymentMessage
126
+ if (!split) {
127
+ return null
128
+ }
129
+ return drop({
130
+ splitId: split.splitId,
131
+ total: readMoney(split.totalAmount),
132
+ description: split.description,
133
+ requesterJid: split.requesterJid,
134
+ createdAtMs: split.createdAtMs === undefined ? undefined : Number(split.createdAtMs),
135
+ participants: (split.participants ?? []).map(participant => drop({
136
+ jid: participant.jid,
137
+ amount: readMoney(participant.amount),
138
+ status: participant.status
139
+ }))
140
+ })
141
+ }
142
+
143
+ export const readPaymentReminder = (msg) => {
144
+ const message = msg?.message ?? msg
145
+ const reminder = message?.paymentReminderMessage
146
+ if (!reminder) {
147
+ return null
148
+ }
149
+ return drop({
150
+ reminderId: reminder.reminderId,
151
+ instanceId: reminder.instanceId,
152
+ description: reminder.description,
153
+ frequency: reminder.frequency,
154
+ status: reminder.status,
155
+ amount: readMoney(reminder.amount),
156
+ payeeVpa: reminder.payeeVpa,
157
+ payeeJid: reminder.payeeJid,
158
+ payerJid: reminder.payerJid
159
+ })
160
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Manages pre-key operations with proper concurrency control
3
+ */
4
+ export class PreKeyManager {
5
+ constructor(store: any, logger: any);
6
+ store: any;
7
+ logger: any;
8
+ queues: Map<any, any>;
9
+ /**
10
+ * Get or create a queue for a specific key type
11
+ */
12
+ getQueue(keyType: any): any;
13
+ /**
14
+ * Process pre-key operations (updates and deletions)
15
+ */
16
+ processOperations(data: any, keyType: any, transactionCache: any, mutations: any, isInTransaction: any): Promise<any>;
17
+ /**
18
+ * Process deletions with validation
19
+ */
20
+ processDeletions(keyType: any, ids: any, transactionCache: any, mutations: any, isInTransaction: any): Promise<void>;
21
+ /**
22
+ * Validate and process pre-key deletions outside transactions
23
+ */
24
+ validateDeletions(data: any, keyType: any): Promise<any>;
25
+ }
@@ -0,0 +1,95 @@
1
+ import PQueue from 'p-queue';
2
+
3
+ export class PreKeyManager {
4
+ constructor(store, logger) {
5
+ this.store = store;
6
+ this.logger = logger;
7
+ this.queues = new Map();
8
+ }
9
+
10
+ getQueue(keyType) {
11
+ if (!this.queues.has(keyType)) {
12
+ this.queues.set(keyType, new PQueue({ concurrency: 1 }));
13
+ }
14
+ return this.queues.get(keyType);
15
+ }
16
+
17
+ async processOperations(data, keyType, transactionCache, mutations, isInTransaction) {
18
+ const keyData = data[keyType];
19
+ if (!keyData)
20
+ return;
21
+ return this.getQueue(keyType).add(async () => {
22
+
23
+ transactionCache[keyType] = transactionCache[keyType] || {};
24
+ mutations[keyType] = mutations[keyType] || {};
25
+
26
+ const deletions = [];
27
+ const updates = {};
28
+ for (const keyId in keyData) {
29
+ if (keyData[keyId] === null) {
30
+ deletions.push(keyId);
31
+ }
32
+ else {
33
+ updates[keyId] = keyData[keyId];
34
+ }
35
+ }
36
+
37
+ if (Object.keys(updates).length > 0) {
38
+ Object.assign(transactionCache[keyType], updates);
39
+ Object.assign(mutations[keyType], updates);
40
+ }
41
+
42
+ if (deletions.length > 0) {
43
+ await this.processDeletions(keyType, deletions, transactionCache, mutations, isInTransaction);
44
+ }
45
+ });
46
+ }
47
+
48
+ async processDeletions(keyType, ids, transactionCache, mutations, isInTransaction) {
49
+ if (isInTransaction) {
50
+
51
+ for (const keyId of ids) {
52
+ if (transactionCache[keyType]?.[keyId]) {
53
+ transactionCache[keyType][keyId] = null;
54
+ mutations[keyType][keyId] = null;
55
+ }
56
+ else {
57
+ this.logger.warn(`Skipping deletion of non-existent ${keyType} in transaction: ${keyId}`);
58
+ }
59
+ }
60
+ }
61
+ else {
62
+
63
+ const existingKeys = await this.store.get(keyType, ids);
64
+ for (const keyId of ids) {
65
+ if (existingKeys[keyId]) {
66
+ transactionCache[keyType][keyId] = null;
67
+ mutations[keyType][keyId] = null;
68
+ }
69
+ else {
70
+ this.logger.warn(`Skipping deletion of non-existent ${keyType}: ${keyId}`);
71
+ }
72
+ }
73
+ }
74
+ }
75
+
76
+ async validateDeletions(data, keyType) {
77
+ const keyData = data[keyType];
78
+ if (!keyData)
79
+ return;
80
+ return this.getQueue(keyType).add(async () => {
81
+
82
+ const deletionIds = Object.keys(keyData).filter(id => keyData[id] === null);
83
+ if (deletionIds.length === 0)
84
+ return;
85
+
86
+ const existingKeys = await this.store.get(keyType, deletionIds);
87
+ for (const keyId of deletionIds) {
88
+ if (!existingKeys[keyId]) {
89
+ this.logger.warn(`Skipping deletion of non-existent ${keyType}: ${keyId}`);
90
+ delete data[keyType][keyId];
91
+ }
92
+ }
93
+ });
94
+ }
95
+ }