@xbibzlibrary/whatsbibz 1.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 (260) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/LICENSE +22 -0
  3. package/LICENSE.upstream +21 -0
  4. package/README.md +280 -0
  5. package/VERIFIKASI-IDENTITAS-2026-09-03.md +138 -0
  6. package/WAProto/GenerateStatics.sh +3 -0
  7. package/WAProto/WAProto.proto +6035 -0
  8. package/WAProto/fix-imports.js +81 -0
  9. package/WAProto/index.d.ts +15314 -0
  10. package/WAProto/index.js +132326 -0
  11. package/assets/logo/README.md +21 -0
  12. package/assets/logo/png/banner-1200x360.png +0 -0
  13. package/assets/logo/png/icon-1024.png +0 -0
  14. package/assets/logo/png/icon-128.png +0 -0
  15. package/assets/logo/png/icon-16.png +0 -0
  16. package/assets/logo/png/icon-24.png +0 -0
  17. package/assets/logo/png/icon-256.png +0 -0
  18. package/assets/logo/png/icon-32.png +0 -0
  19. package/assets/logo/png/icon-48.png +0 -0
  20. package/assets/logo/png/icon-512.png +0 -0
  21. package/assets/logo/png/icon-64.png +0 -0
  22. package/assets/logo/png/mark-512.png +0 -0
  23. package/assets/logo/preview.png +0 -0
  24. package/assets/logo/whatsbibz.ico +0 -0
  25. package/lib/BibzWhats/client.d.ts +95 -0
  26. package/lib/BibzWhats/client.js +501 -0
  27. package/lib/BibzWhats/device-identity.d.ts +60 -0
  28. package/lib/BibzWhats/device-identity.js +234 -0
  29. package/lib/BibzWhats/extract.d.ts +29 -0
  30. package/lib/BibzWhats/extract.js +115 -0
  31. package/lib/BibzWhats/index.d.ts +7 -0
  32. package/lib/BibzWhats/index.js +8 -0
  33. package/lib/BibzWhats/jid.d.ts +22 -0
  34. package/lib/BibzWhats/jid.js +98 -0
  35. package/lib/BibzWhats/pairing.d.ts +35 -0
  36. package/lib/BibzWhats/pairing.js +186 -0
  37. package/lib/BibzWhats/send.d.ts +9 -0
  38. package/lib/BibzWhats/send.js +107 -0
  39. package/lib/BibzWhats/version.d.ts +6 -0
  40. package/lib/BibzWhats/version.js +7 -0
  41. package/lib/Defaults/index.d.ts +73 -0
  42. package/lib/Defaults/index.js +141 -0
  43. package/lib/Modded/message_builder.d.ts +15 -0
  44. package/lib/Modded/message_builder.js +2330 -0
  45. package/lib/Signal/Group/ciphertext-message.d.ts +9 -0
  46. package/lib/Signal/Group/ciphertext-message.js +11 -0
  47. package/lib/Signal/Group/group-session-builder.d.ts +14 -0
  48. package/lib/Signal/Group/group-session-builder.js +29 -0
  49. package/lib/Signal/Group/group_cipher.d.ts +16 -0
  50. package/lib/Signal/Group/group_cipher.js +81 -0
  51. package/lib/Signal/Group/index.d.ts +11 -0
  52. package/lib/Signal/Group/index.js +11 -0
  53. package/lib/Signal/Group/keyhelper.d.ts +10 -0
  54. package/lib/Signal/Group/keyhelper.js +17 -0
  55. package/lib/Signal/Group/sender-chain-key.d.ts +13 -0
  56. package/lib/Signal/Group/sender-chain-key.js +25 -0
  57. package/lib/Signal/Group/sender-key-distribution-message.d.ts +16 -0
  58. package/lib/Signal/Group/sender-key-distribution-message.js +62 -0
  59. package/lib/Signal/Group/sender-key-message.d.ts +18 -0
  60. package/lib/Signal/Group/sender-key-message.js +65 -0
  61. package/lib/Signal/Group/sender-key-name.d.ts +17 -0
  62. package/lib/Signal/Group/sender-key-name.js +47 -0
  63. package/lib/Signal/Group/sender-key-record.d.ts +30 -0
  64. package/lib/Signal/Group/sender-key-record.js +40 -0
  65. package/lib/Signal/Group/sender-key-state.d.ts +38 -0
  66. package/lib/Signal/Group/sender-key-state.js +83 -0
  67. package/lib/Signal/Group/sender-message-key.d.ts +11 -0
  68. package/lib/Signal/Group/sender-message-key.js +25 -0
  69. package/lib/Signal/libsignal.d.ts +4 -0
  70. package/lib/Signal/libsignal.js +406 -0
  71. package/lib/Signal/lid-mapping.d.ts +18 -0
  72. package/lib/Signal/lid-mapping.js +276 -0
  73. package/lib/Socket/Client/index.d.ts +2 -0
  74. package/lib/Socket/Client/index.js +2 -0
  75. package/lib/Socket/Client/types.d.ts +15 -0
  76. package/lib/Socket/Client/types.js +10 -0
  77. package/lib/Socket/Client/websocket.d.ts +12 -0
  78. package/lib/Socket/Client/websocket.js +53 -0
  79. package/lib/Socket/business.d.ts +537 -0
  80. package/lib/Socket/business.js +421 -0
  81. package/lib/Socket/chats.d.ts +110 -0
  82. package/lib/Socket/chats.js +1006 -0
  83. package/lib/Socket/communities.d.ts +646 -0
  84. package/lib/Socket/communities.js +430 -0
  85. package/lib/Socket/engine.d.ts +129 -0
  86. package/lib/Socket/engine.js +767 -0
  87. package/lib/Socket/groups.d.ts +149 -0
  88. package/lib/Socket/groups.js +326 -0
  89. package/lib/Socket/index.d.ts +637 -0
  90. package/lib/Socket/index.js +14 -0
  91. package/lib/Socket/messages-recv.d.ts +239 -0
  92. package/lib/Socket/messages-recv.js +1307 -0
  93. package/lib/Socket/messages-send.d.ts +580 -0
  94. package/lib/Socket/messages-send.js +1639 -0
  95. package/lib/Socket/mex.d.ts +2 -0
  96. package/lib/Socket/mex.js +47 -0
  97. package/lib/Socket/newsletter.d.ts +176 -0
  98. package/lib/Socket/newsletter.js +456 -0
  99. package/lib/Socket/socket.d.ts +56 -0
  100. package/lib/Socket/socket.js +998 -0
  101. package/lib/Types/Auth.d.ts +115 -0
  102. package/lib/Types/Auth.js +1 -0
  103. package/lib/Types/Bussines.d.ts +36 -0
  104. package/lib/Types/Bussines.js +1 -0
  105. package/lib/Types/Call.d.ts +14 -0
  106. package/lib/Types/Call.js +1 -0
  107. package/lib/Types/Chat.d.ts +124 -0
  108. package/lib/Types/Chat.js +7 -0
  109. package/lib/Types/Contact.d.ts +23 -0
  110. package/lib/Types/Contact.js +1 -0
  111. package/lib/Types/Events.d.ts +241 -0
  112. package/lib/Types/Events.js +1 -0
  113. package/lib/Types/GroupMetadata.d.ts +67 -0
  114. package/lib/Types/GroupMetadata.js +1 -0
  115. package/lib/Types/Label.d.ts +46 -0
  116. package/lib/Types/Label.js +24 -0
  117. package/lib/Types/LabelAssociation.d.ts +29 -0
  118. package/lib/Types/LabelAssociation.js +6 -0
  119. package/lib/Types/Message.d.ts +427 -0
  120. package/lib/Types/Message.js +10 -0
  121. package/lib/Types/Newsletter.d.ts +134 -0
  122. package/lib/Types/Newsletter.js +30 -0
  123. package/lib/Types/Product.d.ts +78 -0
  124. package/lib/Types/Product.js +1 -0
  125. package/lib/Types/Signal.d.ts +77 -0
  126. package/lib/Types/Signal.js +1 -0
  127. package/lib/Types/Socket.d.ts +173 -0
  128. package/lib/Types/Socket.js +2 -0
  129. package/lib/Types/State.d.ts +38 -0
  130. package/lib/Types/State.js +12 -0
  131. package/lib/Types/USync.d.ts +25 -0
  132. package/lib/Types/USync.js +1 -0
  133. package/lib/Types/index.d.ts +75 -0
  134. package/lib/Types/index.js +32 -0
  135. package/lib/Utils/auth-utils.d.ts +18 -0
  136. package/lib/Utils/auth-utils.js +289 -0
  137. package/lib/Utils/browser-utils.d.ts +3 -0
  138. package/lib/Utils/browser-utils.js +47 -0
  139. package/lib/Utils/business.d.ts +22 -0
  140. package/lib/Utils/business.js +230 -0
  141. package/lib/Utils/chat-utils.d.ts +69 -0
  142. package/lib/Utils/chat-utils.js +811 -0
  143. package/lib/Utils/companion-reg-client-utils.d.ts +20 -0
  144. package/lib/Utils/companion-reg-client-utils.js +66 -0
  145. package/lib/Utils/crypto.d.ts +41 -0
  146. package/lib/Utils/crypto.js +117 -0
  147. package/lib/Utils/decode-wa-message.d.ts +47 -0
  148. package/lib/Utils/decode-wa-message.js +282 -0
  149. package/lib/Utils/event-buffer.d.ts +35 -0
  150. package/lib/Utils/event-buffer.js +590 -0
  151. package/lib/Utils/generics.d.ts +90 -0
  152. package/lib/Utils/generics.js +384 -0
  153. package/lib/Utils/history.d.ts +21 -0
  154. package/lib/Utils/history.js +130 -0
  155. package/lib/Utils/identity-change-handler.d.ts +36 -0
  156. package/lib/Utils/identity-change-handler.js +48 -0
  157. package/lib/Utils/index.d.ts +25 -0
  158. package/lib/Utils/index.js +24 -0
  159. package/lib/Utils/link-preview.d.ts +20 -0
  160. package/lib/Utils/link-preview.js +84 -0
  161. package/lib/Utils/logger.d.ts +12 -0
  162. package/lib/Utils/logger.js +2 -0
  163. package/lib/Utils/lt-hash.d.ts +10 -0
  164. package/lib/Utils/lt-hash.js +7 -0
  165. package/lib/Utils/make-mutex.d.ts +8 -0
  166. package/lib/Utils/make-mutex.js +32 -0
  167. package/lib/Utils/message-retry-manager.d.ts +109 -0
  168. package/lib/Utils/message-retry-manager.js +224 -0
  169. package/lib/Utils/messages-media.d.ts +129 -0
  170. package/lib/Utils/messages-media.js +784 -0
  171. package/lib/Utils/messages.d.ts +90 -0
  172. package/lib/Utils/messages.js +1240 -0
  173. package/lib/Utils/noise-handler.d.ts +19 -0
  174. package/lib/Utils/noise-handler.js +200 -0
  175. package/lib/Utils/offline-node-processor.d.ts +16 -0
  176. package/lib/Utils/offline-node-processor.js +39 -0
  177. package/lib/Utils/platform-identity.d.ts +14 -0
  178. package/lib/Utils/platform-identity.js +148 -0
  179. package/lib/Utils/pre-key-manager.d.ts +27 -0
  180. package/lib/Utils/pre-key-manager.js +105 -0
  181. package/lib/Utils/process-message.d.ts +59 -0
  182. package/lib/Utils/process-message.js +544 -0
  183. package/lib/Utils/reporting-utils.d.ts +10 -0
  184. package/lib/Utils/reporting-utils.js +257 -0
  185. package/lib/Utils/rich-messages.d.ts +276 -0
  186. package/lib/Utils/rich-messages.js +1143 -0
  187. package/lib/Utils/signal.d.ts +33 -0
  188. package/lib/Utils/signal.js +158 -0
  189. package/lib/Utils/stanza-ack.d.ts +10 -0
  190. package/lib/Utils/stanza-ack.js +37 -0
  191. package/lib/Utils/sticker-pack.d.ts +10 -0
  192. package/lib/Utils/sticker-pack.js +111 -0
  193. package/lib/Utils/sync-action-utils.d.ts +18 -0
  194. package/lib/Utils/sync-action-utils.js +47 -0
  195. package/lib/Utils/tc-token-utils.d.ts +8 -0
  196. package/lib/Utils/tc-token-utils.js +45 -0
  197. package/lib/Utils/use-multi-file-auth-state.d.ts +12 -0
  198. package/lib/Utils/use-multi-file-auth-state.js +120 -0
  199. package/lib/Utils/validate-connection.d.ts +10 -0
  200. package/lib/Utils/validate-connection.js +222 -0
  201. package/lib/VoIP/audio-feeder.d.ts +15 -0
  202. package/lib/VoIP/audio-feeder.js +136 -0
  203. package/lib/VoIP/index.d.ts +12 -0
  204. package/lib/VoIP/index.js +282 -0
  205. package/lib/VoIP/relay-transport.d.ts +43 -0
  206. package/lib/VoIP/relay-transport.js +559 -0
  207. package/lib/VoIP/signaling.js +603 -0
  208. package/lib/VoIP/types.d.ts +69 -0
  209. package/lib/VoIP/types.js +17 -0
  210. package/lib/VoIP/wasm-engine.d.ts +103 -0
  211. package/lib/VoIP/wasm-engine.js +1214 -0
  212. package/lib/VoIP/worker-bootstrap.js +1042 -0
  213. package/lib/WABinary/constants.d.ts +27 -0
  214. package/lib/WABinary/constants.js +1300 -0
  215. package/lib/WABinary/decode.d.ts +6 -0
  216. package/lib/WABinary/decode.js +261 -0
  217. package/lib/WABinary/encode.d.ts +2 -0
  218. package/lib/WABinary/encode.js +219 -0
  219. package/lib/WABinary/generic-utils.d.ts +16 -0
  220. package/lib/WABinary/generic-utils.js +190 -0
  221. package/lib/WABinary/index.d.ts +5 -0
  222. package/lib/WABinary/index.js +5 -0
  223. package/lib/WABinary/jid-utils.d.ts +47 -0
  224. package/lib/WABinary/jid-utils.js +95 -0
  225. package/lib/WABinary/types.d.ts +18 -0
  226. package/lib/WABinary/types.js +1 -0
  227. package/lib/WAM/BinaryInfo.d.ts +8 -0
  228. package/lib/WAM/BinaryInfo.js +9 -0
  229. package/lib/WAM/constants.d.ts +39 -0
  230. package/lib/WAM/constants.js +22852 -0
  231. package/lib/WAM/encode.d.ts +2 -0
  232. package/lib/WAM/encode.js +149 -0
  233. package/lib/WAM/index.d.ts +3 -0
  234. package/lib/WAM/index.js +3 -0
  235. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +9 -0
  236. package/lib/WAUSync/Protocols/USyncContactProtocol.js +28 -0
  237. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +22 -0
  238. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +53 -0
  239. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +12 -0
  240. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -0
  241. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +12 -0
  242. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +37 -0
  243. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +25 -0
  244. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +50 -0
  245. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +9 -0
  246. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +28 -0
  247. package/lib/WAUSync/Protocols/index.d.ts +4 -0
  248. package/lib/WAUSync/Protocols/index.js +4 -0
  249. package/lib/WAUSync/USyncQuery.d.ts +28 -0
  250. package/lib/WAUSync/USyncQuery.js +93 -0
  251. package/lib/WAUSync/USyncUser.d.ts +12 -0
  252. package/lib/WAUSync/USyncUser.js +22 -0
  253. package/lib/WAUSync/index.d.ts +3 -0
  254. package/lib/WAUSync/index.js +3 -0
  255. package/lib/assets/wasm/loader.js +5 -0
  256. package/lib/assets/wasm/whatsapp.wasm +0 -0
  257. package/lib/assets/wasm/worker-modules.js +273 -0
  258. package/lib/index.d.ts +21 -0
  259. package/lib/index.js +22 -0
  260. package/package.json +111 -0
@@ -0,0 +1,1214 @@
1
+ var _a;
2
+ /**
3
+ * WhatsApp VoIP WASM engine.
4
+ *
5
+ * Loads the WhatsApp Web VoIP WASM stack inside a Node.js `vm.Context`,
6
+ * spawns a 20-thread `worker_threads` pool to mirror the browser's pthread
7
+ * model, and exposes a callback-based JS bridge. Audio-only (no video).
8
+ *
9
+ * @author ShellTear
10
+ */
11
+ import * as vm from "node:vm";
12
+ import * as fs from "node:fs";
13
+ import * as path from "node:path";
14
+ import { randomFillSync } from "node:crypto";
15
+ import { Worker } from "node:worker_threads";
16
+ import { fileURLToPath } from "node:url";
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = path.dirname(__filename);
19
+ const CALL_WASM_AB_PROPS_JSON = process.env.CALL_WASM_AB_PROPS_JSON ?? "";
20
+ const PTHREAD_POOL_SIZE = 20;
21
+ const VOIP_READY_TIMEOUT_MS = 15_000;
22
+ const parseJsonObjectEnv = (raw) => {
23
+ if (!raw)
24
+ return {};
25
+ try {
26
+ const parsed = JSON.parse(raw);
27
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
28
+ return parsed;
29
+ }
30
+ catch { }
31
+ return {};
32
+ };
33
+ const toByteArray = (input) => {
34
+ if (!input)
35
+ return new Uint8Array(0);
36
+ if (input instanceof Uint8Array)
37
+ return input;
38
+ if (typeof input === "string")
39
+ return new TextEncoder().encode(input);
40
+ if (ArrayBuffer.isView(input))
41
+ return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
42
+ if (input instanceof ArrayBuffer)
43
+ return new Uint8Array(input);
44
+ if (typeof input === "object" && typeof input.length === "number") {
45
+ const arr = new Uint8Array(input.length);
46
+ for (let i = 0; i < input.length; i += 1)
47
+ arr[i] = input[i] ?? 0;
48
+ return arr;
49
+ }
50
+ return new Uint8Array(0);
51
+ };
52
+ const filterWorkerStderr = (chunk) => {
53
+ const line = chunk.toString().trim();
54
+ if (line && !line.startsWith("voip:") && !line.startsWith("still waiting")) {
55
+ process.stderr.write(chunk);
56
+ }
57
+ };
58
+ const resolveWorkerScriptPath = () => {
59
+ const compiled = path.join(__dirname, "worker-bootstrap.js");
60
+ return fs.existsSync(compiled) ? compiled : path.join(__dirname, "worker-bootstrap.mts");
61
+ };
62
+ class NodeWorkerMessagePort {
63
+ #listeners = new Map();
64
+ #worker;
65
+ fullyConnected;
66
+ name;
67
+ workerID = 0;
68
+ pthread_ptr = 0;
69
+ constructor(worker, name = "WAWebVoipWebWasmWorker") {
70
+ this.#worker = worker;
71
+ this.name = name;
72
+ this.fullyConnected = new Promise((resolve) => {
73
+ const loadedHandler = (msg) => {
74
+ if (msg && msg.cmd === "loaded") {
75
+ this.workerID = msg.workerID ?? 0;
76
+ resolve(this);
77
+ }
78
+ };
79
+ this.addMessageListener("cmd", loadedHandler);
80
+ });
81
+ if (typeof worker.on === "function") {
82
+ worker.on("message", (data) => this.#handleMessage(data));
83
+ worker.on("error", () => { });
84
+ }
85
+ else if (typeof worker.addEventListener === "function") {
86
+ worker.addEventListener("message", (ev) => this.#handleMessage(ev?.data ?? ev));
87
+ }
88
+ }
89
+ postMessage = (msg, transferList) => {
90
+ const out = msg && typeof msg === "object" && msg.cmd && !msg.type ? { ...msg, type: "cmd" } : msg;
91
+ this.#worker.postMessage(out, transferList);
92
+ };
93
+ addMessageListener = (type, handler) => {
94
+ let set = this.#listeners.get(type);
95
+ if (!set) {
96
+ set = new Set();
97
+ this.#listeners.set(type, set);
98
+ }
99
+ set.add(handler);
100
+ return handler;
101
+ };
102
+ removeMessageListener = (type, handler) => this.#listeners.get(type)?.delete(handler) ?? false;
103
+ removeAllMessageListeners = (type) => {
104
+ if (type)
105
+ this.#listeners.get(type)?.clear();
106
+ else
107
+ this.#listeners.clear();
108
+ };
109
+ terminate = () => { this.#worker.terminate(); };
110
+ close = () => { };
111
+ isWrappingVirtualMessagePort = () => false;
112
+ getWorker = () => this.#worker;
113
+ #handleMessage = (data) => {
114
+ if (!data || typeof data !== "object")
115
+ return;
116
+ if (data.type === "callback" || data.type === "waWasmWorkerCompatibleCallback") {
117
+ let callbackName;
118
+ let callbackArgs;
119
+ if (data.__name) {
120
+ callbackName = data.__name;
121
+ callbackArgs = {};
122
+ for (const key in data) {
123
+ if (key !== "type" && key !== "__name" && key !== "prototype" && key !== "args" && !key.startsWith("__")) {
124
+ callbackArgs[key] = data[key];
125
+ }
126
+ }
127
+ }
128
+ else if (data.name) {
129
+ callbackName = data.name;
130
+ callbackArgs = data.args ?? {};
131
+ }
132
+ else if (data.payload?.name) {
133
+ callbackName = data.payload.name;
134
+ callbackArgs = data.payload.args ?? {};
135
+ }
136
+ else {
137
+ return;
138
+ }
139
+ if (callbackName === "onSignalingXmpp" &&
140
+ (!callbackArgs || Object.keys(callbackArgs).length === 0)) {
141
+ callbackArgs = {
142
+ peerJid: data.peerJid, callId: data.callId, xmlPayload: data.xmlPayload,
143
+ };
144
+ }
145
+ let listenerData = callbackArgs;
146
+ if (!callbackArgs || Object.keys(callbackArgs).length === 0 ||
147
+ (Object.keys(callbackArgs).length === 1 && callbackArgs.prototype)) {
148
+ listenerData = {};
149
+ for (const key in data) {
150
+ if (key !== "type" && key !== "__name" && key !== "prototype" && key !== "args" && !key.startsWith("__")) {
151
+ listenerData[key] = data[key];
152
+ }
153
+ }
154
+ }
155
+ else if (callbackName === "sendDataToRelay") {
156
+ listenerData = { ...callbackArgs };
157
+ if (data.data !== undefined)
158
+ listenerData.data = data.data;
159
+ if (data.len !== undefined)
160
+ listenerData.len = data.len;
161
+ if (data.ip !== undefined)
162
+ listenerData.ip = data.ip;
163
+ if (data.port !== undefined)
164
+ listenerData.port = data.port;
165
+ }
166
+ else if (callbackName === "onCallEvent") {
167
+ listenerData = { ...callbackArgs };
168
+ if (data.eventType !== undefined)
169
+ listenerData.eventType = data.eventType;
170
+ if (data.userData !== undefined)
171
+ listenerData.userData = data.userData;
172
+ if (data.eventDataJson !== undefined)
173
+ listenerData.eventDataJson = data.eventDataJson;
174
+ }
175
+ WasmEngine.notifyGlobalCallbackListeners(callbackName, listenerData);
176
+ return;
177
+ }
178
+ const dispatch = (key) => {
179
+ if (!key)
180
+ return;
181
+ for (const handler of this.#listeners.get(key) ?? []) {
182
+ try {
183
+ handler(data);
184
+ }
185
+ catch { }
186
+ }
187
+ };
188
+ dispatch(data.type);
189
+ if (data.cmd !== data.type)
190
+ dispatch(data.cmd);
191
+ };
192
+ }
193
+ export class WasmEngine {
194
+ static #globalCallbackListeners = new Map();
195
+ static #globalCallbacksRegistered = false;
196
+ static registerGlobalCallbackListener = (callbackName, handler) => {
197
+ const key = `callback:${callbackName}`;
198
+ let set = _a.#globalCallbackListeners.get(key);
199
+ if (!set) {
200
+ set = new Set();
201
+ _a.#globalCallbackListeners.set(key, set);
202
+ }
203
+ set.add(handler);
204
+ };
205
+ static notifyGlobalCallbackListeners = (callbackName, data) => {
206
+ const set = _a.#globalCallbackListeners.get(`callback:${callbackName}`);
207
+ if (!set)
208
+ return;
209
+ for (const handler of set) {
210
+ try {
211
+ handler(data);
212
+ }
213
+ catch { }
214
+ }
215
+ };
216
+ #config;
217
+ #instance = null;
218
+ #initialized = false;
219
+ #moduleRegistry = new Map();
220
+ #vmContext = null;
221
+ #unusedWorkers = [];
222
+ #runningWorkers = [];
223
+ #pthreads = {};
224
+ #nextWorkerID = 1;
225
+ #wasmModule = null;
226
+ #wasmMemory = null;
227
+ #removeRunDependencyCallback = null;
228
+ #workersLoadedCount = 0;
229
+ #audioPlaybackLoopInterval = null;
230
+ #audioPlaybackBuffer = null;
231
+ #isPlaybackActive = false;
232
+ #voipStackInitialized = false;
233
+ #voipStackInitPromise = null;
234
+ #voipReadyResolver = null;
235
+ #voipReadyPromise = null;
236
+ #workerModulesCode = "";
237
+ #loaderCode = "";
238
+ constructor(config = {}) {
239
+ const basePath = config.resourcesPath
240
+ ? (path.isAbsolute(config.resourcesPath) ? config.resourcesPath : path.resolve(process.cwd(), config.resourcesPath))
241
+ : path.resolve(__dirname, "..");
242
+ const wasmPath = config.wasmPath
243
+ ? (path.isAbsolute(config.wasmPath) ? config.wasmPath : path.resolve(process.cwd(), config.wasmPath))
244
+ : path.join(basePath, "assets", "wasm", "whatsapp.wasm");
245
+ this.#config = {
246
+ ...config,
247
+ wasmPath,
248
+ resourcesPath: basePath,
249
+ enableLogs: config.enableLogs ?? true,
250
+ options: {
251
+ heartbeatInterval: 30,
252
+ lobbyTimeout: 1,
253
+ maxParticipantsScreenShare: 32,
254
+ maxGroupSizeLongRingtone: 32,
255
+ ...config.options,
256
+ },
257
+ };
258
+ }
259
+ initialize = async () => {
260
+ if (this.#initialized)
261
+ throw new Error("WasmEngine already initialized");
262
+ const voipStorageDir = "/tmp/voip";
263
+ try {
264
+ if (!fs.existsSync(voipStorageDir))
265
+ fs.mkdirSync(voipStorageDir, { recursive: true });
266
+ }
267
+ catch { }
268
+ const loaderFile = path.join(this.#config.resourcesPath, "assets", "wasm", "loader.js");
269
+ const workerFile = path.join(this.#config.resourcesPath, "assets", "wasm", "worker-modules.js");
270
+ if (!this.#config.wasmBinary && !fs.existsSync(this.#config.wasmPath)) {
271
+ throw new Error(`WASM file not found: ${this.#config.wasmPath}`);
272
+ }
273
+ const wasmBuffer = this.#config.wasmBinary
274
+ ? Buffer.from(this.#config.wasmBinary)
275
+ : fs.readFileSync(this.#config.wasmPath);
276
+ const diskWorkerCode = fs.existsSync(workerFile) ? fs.readFileSync(workerFile, "utf8") : "";
277
+ this.#workerModulesCode = this.#config.workerModulesCode ?? diskWorkerCode;
278
+ const workerBundleHasLoader = typeof this.#workerModulesCode === "string" && /WAWebVoipWebWasmLoader/.test(this.#workerModulesCode);
279
+ // Skip the on-disk loader.js if the worker bundle already has a loader —
280
+ // the standalone loader.js is older and would clobber the freshly fetched
281
+ // bindings inside worker-modules.js.
282
+ this.#loaderCode = this.#config.loaderCode ??
283
+ (workerBundleHasLoader ? "" : fs.existsSync(loaderFile) ? fs.readFileSync(loaderFile, "utf8") : "");
284
+ if (!this.#loaderCode && !this.#workerModulesCode) {
285
+ throw new Error("No loader/worker code available to initialize VoIP");
286
+ }
287
+ const memory = new WebAssembly.Memory({ initial: 256, maximum: 32768, shared: true });
288
+ this.#wasmMemory = memory;
289
+ this.#wasmModule = await WebAssembly.compile(wasmBuffer);
290
+ this.#vmContext = this.#createVMContext(memory);
291
+ const runModuleCode = (code) => { if (code)
292
+ vm.runInContext(code, this.#vmContext); };
293
+ runModuleCode(this.#workerModulesCode);
294
+ runModuleCode(this.#loaderCode);
295
+ this.#vmContext.WAWebVoipWebWasmWorkerResource = this.#requireModule("WAWebVoipWebWasmWorkerResource");
296
+ const loaderModuleNames = [
297
+ this.#config.loaderModuleName,
298
+ "WAWebVoipWebWasmLoader",
299
+ "WAWebVoipWebWasmLoader.worker",
300
+ "WAWebVoipWebWasmLoader_ProdLab_internal.worker",
301
+ "WAWebVoipWebWasmLoader_ProdLabvideo_internal.worker",
302
+ ].filter((v, i, a) => !!v && a.indexOf(v) === i);
303
+ let wasmLoader = null;
304
+ for (const moduleName of loaderModuleNames) {
305
+ const candidate = this.#requireModule(moduleName);
306
+ if (typeof candidate === "function") {
307
+ wasmLoader = candidate;
308
+ break;
309
+ }
310
+ if (typeof candidate?.default === "function") {
311
+ wasmLoader = candidate.default;
312
+ break;
313
+ }
314
+ }
315
+ if (typeof wasmLoader !== "function") {
316
+ throw new Error(`No compatible WASM loader found. Tried: ${loaderModuleNames.join(", ")}`);
317
+ }
318
+ if (!_a.#globalCallbacksRegistered)
319
+ this.#registerGlobalCallbacks();
320
+ await this.#initPThreadPool();
321
+ const workersLoadingPromise = this.#loadWasmModuleToAllWorkers();
322
+ const readyPromise = wasmLoader({
323
+ wasmBinary: wasmBuffer,
324
+ wasmMemory: memory,
325
+ locateFile: () => this.#config.wasmPath,
326
+ onRuntimeInitialized: () => { },
327
+ });
328
+ const [instance] = await Promise.all([readyPromise, workersLoadingPromise]);
329
+ this.#instance = instance;
330
+ this.#initialized = true;
331
+ };
332
+ isInitialized = () => this.#initialized;
333
+ destroy = () => {
334
+ this.#stopAudioPlaybackLoop();
335
+ if (this.#instance && typeof this.#instance.endCall === "function") {
336
+ try {
337
+ this.#instance.endCall(0, false);
338
+ }
339
+ catch { }
340
+ }
341
+ for (const worker of [...this.#runningWorkers, ...this.#unusedWorkers]) {
342
+ try {
343
+ worker.terminate();
344
+ }
345
+ catch { }
346
+ }
347
+ this.#runningWorkers = [];
348
+ this.#unusedWorkers = [];
349
+ this.#instance = null;
350
+ this.#vmContext = null;
351
+ this.#moduleRegistry.clear();
352
+ this.#wasmModule = null;
353
+ this.#wasmMemory = null;
354
+ this.#initialized = false;
355
+ };
356
+ initVoipStack = (selfJid, meUserJid, selfLid) => {
357
+ this.#ensureInitialized();
358
+ if (this.#voipStackInitialized || this.#voipStackInitPromise)
359
+ return;
360
+ this.#voipStackInitPromise = new Promise((resolveInit) => {
361
+ this.#voipReadyPromise = new Promise((readyResolve) => {
362
+ this.#voipReadyResolver = () => {
363
+ this.#voipStackInitialized = true;
364
+ this.#voipReadyResolver = null;
365
+ this.#voipReadyPromise = null;
366
+ readyResolve();
367
+ };
368
+ });
369
+ try {
370
+ this.#applyDefaultAbProps();
371
+ try {
372
+ this.#instance.initVoipStack(selfJid, meUserJid, selfLid);
373
+ }
374
+ catch (modernErr) {
375
+ if (modernErr?.name === "BindingError" &&
376
+ (String(modernErr?.message ?? "").includes("expected 8 args") ||
377
+ String(modernErr?.message ?? "").includes("takes 8"))) {
378
+ this.#instance.initVoipStack(selfJid, meUserJid, selfLid, true, 5, 0, 8, 16);
379
+ }
380
+ else {
381
+ throw modernErr;
382
+ }
383
+ }
384
+ Promise.race([
385
+ this.#voipReadyPromise,
386
+ new Promise((r) => setTimeout(() => {
387
+ this.#voipStackInitialized = true;
388
+ r();
389
+ }, VOIP_READY_TIMEOUT_MS)),
390
+ ]).finally(() => {
391
+ this.#voipStackInitPromise = null;
392
+ resolveInit();
393
+ });
394
+ }
395
+ catch {
396
+ this.#voipReadyResolver = null;
397
+ this.#voipReadyPromise = null;
398
+ this.#voipStackInitPromise = null;
399
+ resolveInit();
400
+ }
401
+ });
402
+ };
403
+ waitForVoipStackReady = async () => {
404
+ if (this.#voipStackInitialized)
405
+ return;
406
+ if (this.#voipStackInitPromise) {
407
+ await this.#voipStackInitPromise;
408
+ }
409
+ else {
410
+ await new Promise((r) => setTimeout(r, 100));
411
+ }
412
+ };
413
+ isVoipStackReady = () => this.#voipStackInitialized;
414
+ startCall = (options) => {
415
+ this.#ensureInitialized();
416
+ const peers = this.#makeStringList(options.peerList ?? [options.peerJid]);
417
+ const tcToken = this.#createUint8List(options.extraData);
418
+ const isLidCall = options.isLidCall ?? options.peerJid.includes("@lid");
419
+ const isFromDialer = options.isFromDialer ?? false;
420
+ const peerJid = String(options.peerJid);
421
+ try {
422
+ try {
423
+ return this.#instance.startVoipCall(peerJid, peers, options.callId, options.isVideo, options.peerPn, isLidCall, isFromDialer, tcToken);
424
+ }
425
+ catch (error) {
426
+ if (error?.name !== "BindingError")
427
+ throw error;
428
+ return this.#instance.startVoipCall(peerJid, peers, options.callId, options.isVideo, options.peerPn, isFromDialer, tcToken);
429
+ }
430
+ }
431
+ finally {
432
+ peers?.delete?.();
433
+ tcToken?.delete?.();
434
+ }
435
+ };
436
+ endCall = (reason = 0, sendTerminate = true) => {
437
+ this.#ensureInitialized();
438
+ this.#instance.endCall(reason, sendTerminate);
439
+ };
440
+ setMute = (muted) => {
441
+ this.#ensureInitialized();
442
+ return this.#instance.setCallMute(muted);
443
+ };
444
+ updateNetworkMedium = (networkMedium, networkMtu = 0) => {
445
+ this.#ensureInitialized();
446
+ this.#instance.updateNetworkMedium?.(networkMedium, networkMtu);
447
+ };
448
+ handleSignalingOffer = (msg) => {
449
+ this.#ensureInitialized();
450
+ const tcTokenList = this.#createUint8List(msg.tcToken);
451
+ try {
452
+ this.#instance.handleIncomingSignalingOffer(msg.payload, String(msg.peerPlatform ?? 0), String(msg.peerAppVersion ?? "0"), String(msg.epochId ?? "0"), String(msg.timestamp ?? "0"), msg.isOffline ?? false, msg.isOfferNotContact ?? false, String(msg.peerJid), tcTokenList);
453
+ }
454
+ finally {
455
+ tcTokenList?.delete?.();
456
+ }
457
+ };
458
+ handleSignalingMessage = (msg) => {
459
+ this.#ensureInitialized();
460
+ const tcTokenList = this.#createUint8List(msg.tcToken);
461
+ try {
462
+ this.#instance.handleIncomingSignalingMessage(msg.payload, String(msg.peerPlatform ?? "0"), String(msg.peerAppVersion ?? "0"), String(msg.epochId ?? "0"), String(msg.timestamp ?? "0"), msg.isOffline ?? false, String(msg.peerJid), tcTokenList);
463
+ }
464
+ finally {
465
+ tcTokenList?.delete?.();
466
+ }
467
+ };
468
+ handleSignalingAck = (msg) => {
469
+ this.#ensureInitialized();
470
+ const options = this.#createUint8List(msg.extraData);
471
+ try {
472
+ this.#instance.handleIncomingSignalingAck(msg.payload, String(msg.ackError ?? "0"), String(msg.msgType ?? ""), msg.peerJid ?? "", options);
473
+ }
474
+ finally {
475
+ options?.delete?.();
476
+ }
477
+ };
478
+ handleSignalingReceipt = (msg) => {
479
+ this.#ensureInitialized();
480
+ const tcTokenList = this.#createUint8List(msg.tcToken);
481
+ try {
482
+ this.#instance.handleIncomingSignalingReceipt?.(msg.payload, msg.peerJid, tcTokenList);
483
+ }
484
+ finally {
485
+ tcTokenList?.delete?.();
486
+ }
487
+ };
488
+ handleOnTransportMessage = (data, ip, port) => {
489
+ this.#ensureInitialized();
490
+ if (typeof this.#instance.handleOnMessageFromHeap === "function") {
491
+ const ptr = this.malloc(data.byteLength);
492
+ if (!ptr)
493
+ return;
494
+ try {
495
+ const heapU8 = this.#instance.GROWABLE_HEAP_U8?.() ?? this.#instance.HEAPU8;
496
+ if (!heapU8)
497
+ return;
498
+ heapU8.set(data, ptr);
499
+ this.#instance.handleOnMessageFromHeap(ptr, data.byteLength, ip, port);
500
+ }
501
+ finally {
502
+ this.free(ptr);
503
+ }
504
+ return;
505
+ }
506
+ if (typeof this.#instance.handleOnMessage !== "function")
507
+ return;
508
+ const dataList = this.#createUint8List(data);
509
+ try {
510
+ this.#instance.handleOnMessage(dataList, ip, port);
511
+ }
512
+ finally {
513
+ dataList?.delete?.();
514
+ }
515
+ };
516
+ updateIceRtt = (rttMs, relayIp, relayPort) => {
517
+ this.#ensureInitialized();
518
+ this.#instance.updateIceRtt?.(rttMs, relayIp, relayPort);
519
+ };
520
+ sendAudioData = (data, ptr) => {
521
+ this.#ensureInitialized();
522
+ if (!data || data.length === 0 || !ptr)
523
+ return;
524
+ if (typeof this.#instance.onAudioDataFromJs !== "function")
525
+ return;
526
+ try {
527
+ const heapF32 = this.#instance.GROWABLE_HEAP_F32?.() ?? this.#instance.HEAPF32;
528
+ if (!heapF32)
529
+ return;
530
+ const index = Math.floor(ptr / 4);
531
+ if (index < 0 || index + data.length > heapF32.length)
532
+ return;
533
+ heapF32.set(data, index);
534
+ this.#instance.onAudioDataFromJs(ptr, data.length);
535
+ }
536
+ catch { }
537
+ };
538
+ malloc = (size) => {
539
+ this.#ensureInitialized();
540
+ return this.#instance._malloc(size);
541
+ };
542
+ free = (ptr) => {
543
+ this.#ensureInitialized();
544
+ this.#instance._free(ptr);
545
+ };
546
+ // ─── private ──────────────────────────────────────────────────────────────
547
+ #ensureInitialized = () => {
548
+ if (!this.#initialized || !this.#instance) {
549
+ throw new Error("WasmEngine not initialized. Call initialize() first.");
550
+ }
551
+ };
552
+ #makeStringList = (arr) => {
553
+ const list = new this.#instance.StringList();
554
+ for (const v of arr)
555
+ list.push_back(v);
556
+ return list;
557
+ };
558
+ #createUint8List = (data) => {
559
+ if (!this.#instance?.Uint8List)
560
+ return null;
561
+ const list = new this.#instance.Uint8List();
562
+ if (data)
563
+ data.forEach((byte) => list.push_back(byte));
564
+ return list;
565
+ };
566
+ #startAudioPlaybackLoop = () => {
567
+ if (this.#audioPlaybackLoopInterval)
568
+ return;
569
+ this.#ensureInitialized();
570
+ this.#isPlaybackActive = true;
571
+ if (typeof this.#instance.requestAudioDataFromWasmVoip !== "function")
572
+ return;
573
+ const framesPerChunk = 320;
574
+ const bufferSize = framesPerChunk * 4;
575
+ try {
576
+ const _malloc = this.#instance._malloc ?? this.#instance.malloc;
577
+ if (!_malloc)
578
+ return;
579
+ this.#audioPlaybackBuffer = _malloc(bufferSize);
580
+ }
581
+ catch {
582
+ return;
583
+ }
584
+ if (!this.#audioPlaybackBuffer || this.#audioPlaybackBuffer <= 0)
585
+ return;
586
+ this.#audioPlaybackLoopInterval = setInterval(() => {
587
+ if (!this.#isPlaybackActive || !this.#instance || !this.#initialized) {
588
+ this.#stopAudioPlaybackLoop();
589
+ return;
590
+ }
591
+ try {
592
+ this.#instance.requestAudioDataFromWasmVoip(this.#audioPlaybackBuffer, bufferSize);
593
+ const heapF32 = this.#instance.GROWABLE_HEAP_F32?.();
594
+ if (!heapF32)
595
+ return;
596
+ const index = Math.floor(this.#audioPlaybackBuffer / 4);
597
+ const numFloats = Math.floor(bufferSize / 4);
598
+ if (index < 0 || index + numFloats > heapF32.length)
599
+ return;
600
+ const audioData = new Float32Array(heapF32.buffer, heapF32.byteOffset + index * 4, numFloats);
601
+ const hasNonZero = audioData.some((s) => Math.abs(s) > 0.0001);
602
+ if (hasNonZero)
603
+ this.#config.callbacks?.onAudioPlaybackData?.(audioData);
604
+ }
605
+ catch { }
606
+ }, 16);
607
+ };
608
+ #stopAudioPlaybackLoop = () => {
609
+ this.#isPlaybackActive = false;
610
+ if (this.#audioPlaybackLoopInterval) {
611
+ clearInterval(this.#audioPlaybackLoopInterval);
612
+ this.#audioPlaybackLoopInterval = null;
613
+ }
614
+ if (this.#audioPlaybackBuffer && this.#audioPlaybackBuffer > 0) {
615
+ try {
616
+ this.#instance?._free?.(this.#audioPlaybackBuffer);
617
+ }
618
+ catch { }
619
+ this.#audioPlaybackBuffer = null;
620
+ }
621
+ };
622
+ #applyDefaultAbProps = () => {
623
+ if (!this.#instance)
624
+ return;
625
+ const setInt = typeof this.#instance.setABPropInt === "function"
626
+ ? (k, v) => { this.#instance.setABPropInt(k, v); }
627
+ : null;
628
+ const setBool = typeof this.#instance.setABPropBool === "function"
629
+ ? (k, v) => { this.#instance.setABPropBool(k, v); }
630
+ : null;
631
+ const setString = typeof this.#instance.setABPropString === "function"
632
+ ? (k, v) => { this.#instance.setABPropString(k, v); }
633
+ : null;
634
+ if (!setInt && !setBool && !setString)
635
+ return;
636
+ const opts = this.#config.options ?? {};
637
+ const intProps = {
638
+ heartbeat_interval_s: opts.heartbeatInterval ?? 30,
639
+ lobby_timeout_min: opts.lobbyTimeout ?? 1,
640
+ max_num_participants_for_ss: opts.maxParticipantsScreenShare ?? 32,
641
+ max_group_size_for_long_ringtone: opts.maxGroupSizeLongRingtone ?? 32,
642
+ app_exit_reason_version: 1,
643
+ log_level: opts.logLevel ?? 3,
644
+ calling_rust_migration_bitmap: 0,
645
+ calling_rust_migration_incoming_stanza_bitmap: 0,
646
+ default_endpoint_thread_poll_timeout: 0,
647
+ aigc_version: 0,
648
+ call_admin_version: 0,
649
+ vid_stream_pause_resume_jb_reset_threshold_ms: 0,
650
+ // Opus: max bandwidth WB (16 kHz). FB (48 kHz) needs native audio device
651
+ // hooks not available in this JS-only WASM context.
652
+ opus_max_bandwidth: 1103, // OPUS_BANDWIDTH_WIDEBAND
653
+ };
654
+ const boolProps = {
655
+ enable_av_downgrade: false,
656
+ enable_new_user_action_stanza_for_raise_hand_sender: false,
657
+ enable_webcodec_video_encode: false,
658
+ enable_init_bwe_for_group_call: false,
659
+ enable_ring_for_gc_on_offer_expire: false,
660
+ allow_reporting_call_replayer_id: false,
661
+ enable_offer_v2_upgrade: false,
662
+ enable_silent_offer: false,
663
+ voice_ai_conversation_starter_latency_tracking: false,
664
+ enable_waiting_room_logging: false,
665
+ attach_transport_rtx: false,
666
+ ignore_joinable_terminate_on_expired_offer: false,
667
+ enable_passthrough_video_decoder: false,
668
+ };
669
+ for (const [key, value] of Object.entries(intProps)) {
670
+ if (setInt && Number.isFinite(value))
671
+ try {
672
+ setInt(key, value);
673
+ }
674
+ catch { }
675
+ }
676
+ for (const [key, value] of Object.entries(boolProps)) {
677
+ if (setBool)
678
+ try {
679
+ setBool(key, value);
680
+ }
681
+ catch { }
682
+ }
683
+ const overrideProps = parseJsonObjectEnv(CALL_WASM_AB_PROPS_JSON);
684
+ for (const [key, value] of Object.entries(overrideProps)) {
685
+ try {
686
+ if (typeof value === "boolean" && setBool)
687
+ setBool(key, value);
688
+ else if (typeof value === "number" && setInt)
689
+ setInt(key, value);
690
+ else if (typeof value === "string" && setString)
691
+ setString(key, value);
692
+ }
693
+ catch { }
694
+ }
695
+ };
696
+ #allocateUnusedWorker = () => {
697
+ const workerScriptPath = resolveWorkerScriptPath();
698
+ if (!fs.existsSync(workerScriptPath))
699
+ return;
700
+ try {
701
+ const worker = new Worker(workerScriptPath, {
702
+ stdout: true, stderr: true,
703
+ workerData: {
704
+ wasmPath: this.#config.wasmPath,
705
+ wasmBinary: this.#config.wasmBinary,
706
+ workerModulesCode: this.#workerModulesCode,
707
+ loaderCode: this.#loaderCode,
708
+ loaderModuleName: this.#config.loaderModuleName,
709
+ resourcesPath: this.#config.resourcesPath,
710
+ enableLogs: this.#config.enableLogs,
711
+ },
712
+ });
713
+ const port = new NodeWorkerMessagePort(worker, "WAWebVoipWebWasmWorker");
714
+ worker.stdout?.on("data", () => { }); // suppress noisy worker stdout
715
+ worker.stderr?.on("data", filterWorkerStderr);
716
+ this.#unusedWorkers.push(port);
717
+ }
718
+ catch { }
719
+ };
720
+ #initPThreadPool = async () => {
721
+ for (let i = 0; i < PTHREAD_POOL_SIZE; i += 1)
722
+ this.#allocateUnusedWorker();
723
+ };
724
+ #loadWasmModuleToWorker = (worker) => new Promise((resolve) => {
725
+ const loadedHandler = (msg) => {
726
+ if (msg && msg.cmd === "loaded") {
727
+ worker.removeMessageListener("cmd", loadedHandler);
728
+ this.#workersLoadedCount += 1;
729
+ if (this.#workersLoadedCount >= PTHREAD_POOL_SIZE && this.#removeRunDependencyCallback) {
730
+ this.#removeRunDependencyCallback("loading-workers");
731
+ }
732
+ resolve();
733
+ }
734
+ };
735
+ worker.addMessageListener("cmd", loadedHandler);
736
+ worker.workerID = this.#nextWorkerID++;
737
+ worker.postMessage({
738
+ cmd: "load", type: "cmd",
739
+ wasmMemory: this.#wasmMemory,
740
+ wasmModule: this.#wasmModule,
741
+ workerID: worker.workerID,
742
+ handlers: [],
743
+ });
744
+ });
745
+ #loadWasmModuleToAllWorkers = async () => {
746
+ this.#workersLoadedCount = 0;
747
+ await Promise.all(this.#unusedWorkers.map((w) => this.#loadWasmModuleToWorker(w)));
748
+ };
749
+ #registerGlobalCallbacks = () => {
750
+ const callbacks = this.#config.callbacks ?? {};
751
+ _a.registerGlobalCallbackListener("loggingCallback", (data) => {
752
+ if (!this.#config.enableLogs)
753
+ return;
754
+ const level = data?.level;
755
+ const msg = data?.message ?? "";
756
+ const mapped = level === 1 ? "error" : level === 2 ? "warn" : level === 3 ? "log" : "debug";
757
+ callbacks.onLog?.(mapped, msg);
758
+ });
759
+ if (callbacks.onAudioCaptureInit) {
760
+ _a.registerGlobalCallbackListener("initCaptureDriverJS", (data) => {
761
+ callbacks.onAudioCaptureInit({
762
+ sampleRate: data?.sample_rate ?? data?.sampleRate,
763
+ channels: data?.channels,
764
+ bitsPerSample: data?.bits_per_sample ?? data?.bitsPerSample,
765
+ framesPerChunk: data?.frames_per_chunk ?? data?.framesPerChunk,
766
+ });
767
+ });
768
+ }
769
+ _a.registerGlobalCallbackListener("startCaptureJS", () => callbacks.onAudioCaptureStart?.());
770
+ _a.registerGlobalCallbackListener("stopCaptureJS", () => callbacks.onAudioCaptureStop?.());
771
+ if (callbacks.onAudioPlaybackInit) {
772
+ _a.registerGlobalCallbackListener("initPlaybackDriverJS", (data) => {
773
+ callbacks.onAudioPlaybackInit({
774
+ sampleRate: data?.sample_rate ?? data?.sampleRate,
775
+ channels: data?.channels,
776
+ bitsPerSample: data?.bits_per_sample ?? data?.bitsPerSample,
777
+ framesPerChunk: data?.frames_per_chunk ?? data?.framesPerChunk,
778
+ });
779
+ });
780
+ }
781
+ _a.registerGlobalCallbackListener("startPlaybackJS", () => {
782
+ callbacks.onAudioPlaybackStart?.();
783
+ this.#startAudioPlaybackLoop();
784
+ });
785
+ _a.registerGlobalCallbackListener("stopPlaybackJS", () => {
786
+ this.#stopAudioPlaybackLoop();
787
+ callbacks.onAudioPlaybackStop?.();
788
+ });
789
+ if (callbacks.onSignalingXmpp) {
790
+ _a.registerGlobalCallbackListener("onSignalingXmpp", (data) => {
791
+ const peerJid = data.peerJid ?? data.args?.peerJid;
792
+ const callId = data.callId ?? data.args?.callId;
793
+ let xmlPayload = data.xmlPayload ?? data.args?.xmlPayload;
794
+ if (Array.isArray(xmlPayload))
795
+ xmlPayload = new Uint8Array(xmlPayload);
796
+ else if (xmlPayload && typeof xmlPayload === "object" &&
797
+ !(xmlPayload instanceof Uint8Array) && !Buffer.isBuffer(xmlPayload)) {
798
+ xmlPayload = new Uint8Array(xmlPayload);
799
+ }
800
+ callbacks.onSignalingXmpp(peerJid, callId, xmlPayload);
801
+ });
802
+ }
803
+ if (callbacks.onCallEvent) {
804
+ _a.registerGlobalCallbackListener("onCallEvent", (data) => {
805
+ callbacks.onCallEvent(data.eventType, data.eventDataJson);
806
+ });
807
+ }
808
+ if (callbacks.sendDataToRelay) {
809
+ _a.registerGlobalCallbackListener("sendDataToRelay", (data) => {
810
+ let relayData = data.data ?? data.args?.data;
811
+ const ip = data.ip ?? data.args?.ip;
812
+ const portNum = data.port ?? data.args?.port;
813
+ if (relayData instanceof Uint8Array) { /* ok */ }
814
+ else if (Array.isArray(relayData))
815
+ relayData = new Uint8Array(relayData);
816
+ else if (Buffer.isBuffer(relayData))
817
+ relayData = new Uint8Array(relayData);
818
+ else if (relayData && typeof relayData === "object" && relayData.buffer) {
819
+ relayData = new Uint8Array(relayData.buffer, relayData.byteOffset ?? 0, relayData.byteLength ?? relayData.length);
820
+ }
821
+ else if (relayData instanceof ArrayBuffer)
822
+ relayData = new Uint8Array(relayData);
823
+ else
824
+ return 0;
825
+ if (!ip || !portNum)
826
+ return 0;
827
+ callbacks.sendDataToRelay(relayData, ip, portNum);
828
+ return relayData.byteLength;
829
+ });
830
+ }
831
+ _a.#globalCallbacksRegistered = true;
832
+ };
833
+ #requireModule = (name) => {
834
+ const preDefinedModules = {
835
+ Promise,
836
+ WAWebVoipWebWasmWorkerResource: {
837
+ resourcePath: resolveWorkerScriptPath(),
838
+ name: "WAWebVoipWebWasmWorker",
839
+ },
840
+ WorkerBundleResource: {
841
+ createDedicatedWebWorker: (resource) => {
842
+ const scriptPath = resource?.resourcePath && fs.existsSync(resource.resourcePath)
843
+ ? resource.resourcePath
844
+ : resolveWorkerScriptPath();
845
+ const worker = new Worker(scriptPath, {
846
+ stdout: true, stderr: true,
847
+ workerData: {
848
+ wasmPath: this.#config.wasmPath,
849
+ wasmBinary: this.#config.wasmBinary,
850
+ workerModulesCode: this.#workerModulesCode,
851
+ loaderCode: this.#loaderCode,
852
+ loaderModuleName: this.#config.loaderModuleName,
853
+ resourcesPath: this.#config.resourcesPath,
854
+ enableLogs: this.#config.enableLogs,
855
+ },
856
+ });
857
+ worker.stdout?.on("data", () => { });
858
+ worker.stderr?.on("data", filterWorkerStderr);
859
+ return worker;
860
+ },
861
+ },
862
+ WorkerClient: { init: () => { } },
863
+ WorkerMessagePort: {
864
+ WorkerMessagePort: NodeWorkerMessagePort,
865
+ CastWorkerMessagePort: (w) => w,
866
+ WorkerSyncedMessagePort: NodeWorkerMessagePort,
867
+ },
868
+ bx: Object.assign((id) => String(id), { getURL: () => "" }),
869
+ HasteSupportData: { handle: () => { } },
870
+ ServiceWorkerDynamicModules: { handle: () => { } },
871
+ WhatsAppWebServiceWorker: { default: true },
872
+ WAWebLogger: { initializeWAWebLogger: () => { } },
873
+ WAWebSw: { initHandlers: () => { } },
874
+ WAWebWamRuntimeProvider: { setWamRuntime: () => { } },
875
+ WAWebWamWorkerInterface: { commit: () => { }, set: () => { } },
876
+ ServerJSDefine: { handleDefine: () => { } },
877
+ ix: { add: () => { } },
878
+ MetaConfigMap: { add: () => { } },
879
+ QPLHasteSupportDataStorage: { default: { add: () => { }, get: () => null } },
880
+ getFalcoLogPolicy_DO_NOT_USE: { add: () => { } },
881
+ gkx: { add: () => { } },
882
+ justknobx: { add: () => { } },
883
+ qex: { add: () => { } },
884
+ };
885
+ if (preDefinedModules[name])
886
+ return preDefinedModules[name];
887
+ const mod = this.#moduleRegistry.get(name);
888
+ if (!mod)
889
+ return {};
890
+ if (mod.exports !== undefined)
891
+ return mod.exports;
892
+ const requireFn = this.#requireModule;
893
+ const normalizeModuleResult = (value) => {
894
+ if (value && typeof value === "object" && "exports" in value && Object.keys(value).length === 1) {
895
+ return value.exports;
896
+ }
897
+ return value;
898
+ };
899
+ const importDefaultFn = (dep) => {
900
+ const v = requireFn(dep);
901
+ return v && v.__esModule ? v.default : v;
902
+ };
903
+ const importAllFn = (dep) => {
904
+ const v = requireFn(dep);
905
+ if (v == null)
906
+ return { default: v };
907
+ if (v.__esModule)
908
+ return v;
909
+ if (typeof v !== "object" && typeof v !== "function")
910
+ return { default: v };
911
+ const ns = {};
912
+ for (const key of Object.keys(v))
913
+ ns[key] = v[key];
914
+ ns.default = v;
915
+ return ns;
916
+ };
917
+ const tryMetro = () => {
918
+ const module = { exports: {} };
919
+ mod.factory(this.#vmContext ?? globalThis, requireFn, importDefaultFn, importAllFn, null, module, module.exports);
920
+ return normalizeModuleResult(module.exports);
921
+ };
922
+ const tryLegacy = () => {
923
+ const exports = {};
924
+ const module = { exports };
925
+ const resolvedDeps = mod.deps.map((dep) => requireFn(dep));
926
+ mod.factory(this.#vmContext ?? globalThis, requireFn, requireFn, requireFn, module, exports, ...resolvedDeps);
927
+ return normalizeModuleResult(module.exports);
928
+ };
929
+ let result = {};
930
+ let metroError = null;
931
+ try {
932
+ result = tryMetro();
933
+ }
934
+ catch (e) {
935
+ metroError = e;
936
+ }
937
+ if (metroError != null || (result && typeof result === "object" && Object.keys(result).length === 0)) {
938
+ try {
939
+ const legacyResult = tryLegacy();
940
+ if (typeof legacyResult === "function" || (legacyResult && Object.keys(legacyResult).length > 0)) {
941
+ result = legacyResult;
942
+ }
943
+ }
944
+ catch { }
945
+ }
946
+ mod.exports = result;
947
+ return result;
948
+ };
949
+ #createVMContext = (memory) => {
950
+ const callbacks = this.#config.callbacks ?? {};
951
+ const wasmCallbacks = {
952
+ onVoipReady: () => {
953
+ this.#voipReadyResolver?.();
954
+ callbacks.onVoipReady?.();
955
+ },
956
+ onSignalingXmpp: (data) => callbacks.onSignalingXmpp?.(data?.peerJid, data?.callId, data?.xmlPayload),
957
+ onCallEvent: (data) => callbacks.onCallEvent?.(data?.eventType, data?.eventDataJson),
958
+ sendDataToRelay: (data) => callbacks.sendDataToRelay?.(data?.data, data?.ip, data?.port),
959
+ loggingCallback: (data) => {
960
+ if (!this.#config.enableLogs)
961
+ return;
962
+ const level = data?.level;
963
+ const msg = data?.message ?? "";
964
+ const mapped = level === 1 ? "error" : level === 2 ? "warn" : level === 3 ? "log" : "debug";
965
+ callbacks.onLog?.(mapped, msg);
966
+ },
967
+ initCaptureDriverJS: (data) => {
968
+ callbacks.onAudioCaptureInit?.({
969
+ sampleRate: data?.sample_rate, channels: data?.channels,
970
+ bitsPerSample: data?.bits_per_sample, framesPerChunk: data?.frames_per_chunk,
971
+ });
972
+ return 0;
973
+ },
974
+ startCaptureJS: () => { callbacks.onAudioCaptureStart?.(); return 0; },
975
+ stopCaptureJS: () => { callbacks.onAudioCaptureStop?.(); return 0; },
976
+ initPlaybackDriverJS: (data) => {
977
+ callbacks.onAudioPlaybackInit?.({
978
+ sampleRate: data?.sample_rate, channels: data?.channels,
979
+ bitsPerSample: data?.bits_per_sample, framesPerChunk: data?.frames_per_chunk,
980
+ });
981
+ return 0;
982
+ },
983
+ startPlaybackJS: () => {
984
+ callbacks.onAudioPlaybackStart?.();
985
+ this.#startAudioPlaybackLoop();
986
+ return 0;
987
+ },
988
+ stopPlaybackJS: () => {
989
+ this.#stopAudioPlaybackLoop();
990
+ callbacks.onAudioPlaybackStop?.();
991
+ return 0;
992
+ },
993
+ startVideoCaptureJS: () => 0,
994
+ stopVideoCaptureJS: () => 0,
995
+ startDesktopCaptureJS: () => 0,
996
+ stopDesktopCaptureJS: () => 0,
997
+ dataChannelStateCallback: () => 0,
998
+ getBrowserAudioProcessingStatus: () => 7,
999
+ getBweModelPath: () => null,
1000
+ videoFrameConsumed: () => 0,
1001
+ cryptoHkdfExtractWithSaltAndExpand: (data) => {
1002
+ const key = toByteArray(data?.key_);
1003
+ const salt = data?.salt_ ? toByteArray(data.salt_) : new Uint8Array(0);
1004
+ const info = toByteArray(data?.info_);
1005
+ const length = data?.length ?? 32;
1006
+ return callbacks.cryptoHkdf?.(key, salt, info, length) ?? new Uint8Array(length);
1007
+ },
1008
+ hmacSha256KeyGenerator: (data) => {
1009
+ const hmacData = new Uint8Array(data?.data_ ?? []);
1010
+ const hmacKey = new Uint8Array(data?.key_ ?? []);
1011
+ return callbacks.hmacSha256?.(hmacData, hmacKey) ?? new Uint8Array(32);
1012
+ },
1013
+ isParticipantKnownContact: () => true,
1014
+ getPersistentDirectoryPath: () => {
1015
+ const dir = "/tmp/voip";
1016
+ try {
1017
+ if (!fs.existsSync(dir))
1018
+ fs.mkdirSync(dir, { recursive: true });
1019
+ }
1020
+ catch { }
1021
+ return dir;
1022
+ },
1023
+ };
1024
+ const __d = (name, deps, factory) => {
1025
+ this.#moduleRegistry.set(name, { deps, factory, exports: undefined });
1026
+ };
1027
+ const babelHelpers = {
1028
+ extends: Object.assign,
1029
+ inheritsLoose: (sub, sup) => {
1030
+ sub.prototype = Object.create(sup.prototype);
1031
+ sub.prototype.constructor = sub;
1032
+ sub.__proto__ = sup;
1033
+ },
1034
+ objectWithoutPropertiesLoose: (source, excluded) => {
1035
+ if (source == null)
1036
+ return {};
1037
+ const target = {};
1038
+ for (const key of Object.keys(source)) {
1039
+ if (excluded.indexOf(key) >= 0)
1040
+ continue;
1041
+ target[key] = source[key];
1042
+ }
1043
+ return target;
1044
+ },
1045
+ taggedTemplateLiteralLoose: (strings, raw) => {
1046
+ if (!raw)
1047
+ raw = strings.slice(0);
1048
+ strings.raw = raw;
1049
+ return strings;
1050
+ },
1051
+ wrapNativeSuper: (Class) => Class,
1052
+ };
1053
+ const addRunDependency = (dep) => {
1054
+ if (dep === "loading-workers" && this.#workersLoadedCount >= PTHREAD_POOL_SIZE) {
1055
+ setImmediate(() => this.#removeRunDependencyCallback?.(dep));
1056
+ }
1057
+ };
1058
+ const removeRunDependency = (_dep) => { };
1059
+ this.#removeRunDependencyCallback = removeRunDependency;
1060
+ const webCrypto = {
1061
+ getRandomValues: (arr) => {
1062
+ if (!arr || !ArrayBuffer.isView(arr)) {
1063
+ throw new TypeError("crypto.getRandomValues expects a TypedArray");
1064
+ }
1065
+ const bytes = Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength);
1066
+ randomFillSync(bytes);
1067
+ return arr;
1068
+ },
1069
+ };
1070
+ const selfObj = {
1071
+ __swData: { dynamic_data: { hsdp: {}, dynamic_modules: [] } },
1072
+ WhatsAppVoipWasmCallbacks: wasmCallbacks,
1073
+ WhatsAppVoipWasmWorkerCompatibleCallbacks: wasmCallbacks,
1074
+ crypto: webCrypto,
1075
+ };
1076
+ selfObj.self = selfObj;
1077
+ selfObj.window = selfObj;
1078
+ selfObj.globalThis = selfObj;
1079
+ if (typeof global !== "undefined") {
1080
+ global.WhatsAppVoipWasmCallbacks = wasmCallbacks;
1081
+ global.WhatsAppVoipWasmWorkerCompatibleCallbacks = wasmCallbacks;
1082
+ }
1083
+ const context = vm.createContext({
1084
+ self: selfObj, globalThis: selfObj, global: selfObj, window: selfObj,
1085
+ console, setTimeout, setInterval, clearTimeout, clearInterval,
1086
+ queueMicrotask, performance, babelHelpers, __d,
1087
+ require: this.#requireModule,
1088
+ addRunDependency, removeRunDependency,
1089
+ WebAssembly, SharedArrayBuffer,
1090
+ Atomics: this.#createAtomicsWrapper(memory),
1091
+ Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array,
1092
+ Float32Array, Float64Array, BigInt64Array, BigUint64Array,
1093
+ ArrayBuffer, DataView, Error, TypeError, RangeError, Promise,
1094
+ Map, Set, WeakMap, WeakSet, Symbol, Object, Array, String, Number,
1095
+ Boolean, Math, Date, JSON, RegExp, Function, Proxy, Reflect,
1096
+ crypto: webCrypto,
1097
+ WhatsAppVoipWasmCallbacks: wasmCallbacks,
1098
+ WhatsAppVoipWasmWorkerCompatibleCallbacks: wasmCallbacks,
1099
+ navigator: {
1100
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
1101
+ hardwareConcurrency: 4,
1102
+ },
1103
+ process: undefined,
1104
+ document: { currentScript: null },
1105
+ location: { href: "file:///wasm" },
1106
+ Worker: class {
1107
+ constructor() { }
1108
+ postMessage() { }
1109
+ terminate() { }
1110
+ addEventListener() { }
1111
+ },
1112
+ fetch: async () => { throw new Error("fetch not supported"); },
1113
+ XMLHttpRequest: class {
1114
+ open() { }
1115
+ send() { }
1116
+ setRequestHeader() { }
1117
+ },
1118
+ Blob: class {
1119
+ constructor() { }
1120
+ },
1121
+ URL: { createObjectURL: () => "blob:fake", revokeObjectURL: () => { } },
1122
+ Image: class {
1123
+ src = "";
1124
+ onload = null;
1125
+ onerror = null;
1126
+ },
1127
+ Audio: class {
1128
+ src = "";
1129
+ addEventListener() { }
1130
+ },
1131
+ __NODE_PTHREAD: {
1132
+ getUnusedWorker: () => {
1133
+ if (this.#unusedWorkers.length === 0)
1134
+ return null;
1135
+ const worker = this.#unusedWorkers.pop();
1136
+ this.#runningWorkers.push(worker);
1137
+ return worker;
1138
+ },
1139
+ returnWorkerToPool: (worker) => {
1140
+ const idx = this.#runningWorkers.indexOf(worker);
1141
+ if (idx >= 0) {
1142
+ this.#runningWorkers.splice(idx, 1);
1143
+ this.#unusedWorkers.push(worker);
1144
+ }
1145
+ },
1146
+ spawnThread: (params) => {
1147
+ const worker = this.#unusedWorkers.pop();
1148
+ if (!worker)
1149
+ return 6;
1150
+ this.#runningWorkers.push(worker);
1151
+ this.#pthreads[params.pthread_ptr] = worker;
1152
+ worker.pthread_ptr = params.pthread_ptr;
1153
+ const pthreadTable = this.#instance?.PThread?.pthreads;
1154
+ if (pthreadTable)
1155
+ pthreadTable[params.pthread_ptr] = worker;
1156
+ worker.postMessage({
1157
+ cmd: "run",
1158
+ start_routine: params.startRoutine,
1159
+ arg: params.arg,
1160
+ pthread_ptr: params.pthread_ptr,
1161
+ });
1162
+ return 0;
1163
+ },
1164
+ unusedWorkersCount: () => this.#unusedWorkers.length,
1165
+ runningWorkersCount: () => this.#runningWorkers.length,
1166
+ },
1167
+ __IS_NODE_PTHREAD_ENV: true,
1168
+ });
1169
+ context.self = context;
1170
+ context.globalThis = context;
1171
+ context.global = context;
1172
+ context.window = context;
1173
+ return context;
1174
+ };
1175
+ #createAtomicsWrapper = (_memory) => {
1176
+ const atomicsWrapper = {
1177
+ add: Atomics.add.bind(Atomics),
1178
+ and: Atomics.and.bind(Atomics),
1179
+ compareExchange: Atomics.compareExchange.bind(Atomics),
1180
+ exchange: Atomics.exchange.bind(Atomics),
1181
+ isLockFree: Atomics.isLockFree.bind(Atomics),
1182
+ load: Atomics.load.bind(Atomics),
1183
+ or: Atomics.or.bind(Atomics),
1184
+ store: Atomics.store.bind(Atomics),
1185
+ sub: Atomics.sub.bind(Atomics),
1186
+ xor: Atomics.xor.bind(Atomics),
1187
+ notify: (typedArray, index, count) => {
1188
+ try {
1189
+ return Atomics.notify(typedArray, index, count);
1190
+ }
1191
+ catch (e) {
1192
+ if (e?.message?.includes("futex_wake") || e?.message?.includes("main_browser_thread"))
1193
+ return 0;
1194
+ throw e;
1195
+ }
1196
+ },
1197
+ waitAsync: Atomics.waitAsync
1198
+ ? Atomics.waitAsync.bind(Atomics)
1199
+ : () => ({ async: true, value: Promise.resolve("ok") }),
1200
+ wait: (typedArray, index, value, timeout) => {
1201
+ const currentValue = Atomics.load(typedArray, index);
1202
+ if (currentValue !== value)
1203
+ return "not-equal";
1204
+ if (timeout !== undefined && timeout <= 0)
1205
+ return "timed-out";
1206
+ return "timed-out";
1207
+ },
1208
+ [Symbol.toStringTag]: "Atomics",
1209
+ };
1210
+ return atomicsWrapper;
1211
+ };
1212
+ }
1213
+ _a = WasmEngine;
1214
+ export default WasmEngine;