@nerxa/socketon 0.0.0-stage → 1.21.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.
- package/EXPERIMENTAL.md +350 -0
- package/LICENSE +24 -0
- package/README.md +6317 -2
- package/WAProto/index.d.ts +17301 -0
- package/WAProto/index.js +125088 -0
- package/engine-requirements.js +14 -0
- package/package.json +100 -4
- package/src/Defaults/index.d.ts +152 -0
- package/src/Defaults/index.js +164 -0
- package/src/MessageBuilder/bot-signature.d.ts +22 -0
- package/src/MessageBuilder/bot-signature.js +122 -0
- package/src/MessageBuilder/extras.d.ts +449 -0
- package/src/MessageBuilder/extras.js +1356 -0
- package/src/MessageBuilder/index.d.ts +210 -0
- package/src/MessageBuilder/index.js +3594 -0
- package/src/MessageBuilder/metaai.d.ts +438 -0
- package/src/MessageBuilder/metaai.js +890 -0
- package/src/Signal/Group/ciphertext-message.d.ts +9 -0
- package/src/Signal/Group/ciphertext-message.js +11 -0
- package/src/Signal/Group/group-session-builder.d.ts +7 -0
- package/src/Signal/Group/group-session-builder.js +27 -0
- package/src/Signal/Group/group_cipher.d.ts +11 -0
- package/src/Signal/Group/group_cipher.js +78 -0
- package/src/Signal/Group/index.d.ts +11 -0
- package/src/Signal/Group/index.js +11 -0
- package/src/Signal/Group/keyhelper.d.ts +6 -0
- package/src/Signal/Group/keyhelper.js +17 -0
- package/src/Signal/Group/sender-chain-key.d.ts +13 -0
- package/src/Signal/Group/sender-chain-key.js +25 -0
- package/src/Signal/Group/sender-key-distribution-message.d.ts +16 -0
- package/src/Signal/Group/sender-key-distribution-message.js +62 -0
- package/src/Signal/Group/sender-key-message.d.ts +18 -0
- package/src/Signal/Group/sender-key-message.js +65 -0
- package/src/Signal/Group/sender-key-name.d.ts +11 -0
- package/src/Signal/Group/sender-key-name.js +47 -0
- package/src/Signal/Group/sender-key-record.d.ts +12 -0
- package/src/Signal/Group/sender-key-record.js +40 -0
- package/src/Signal/Group/sender-key-state.d.ts +16 -0
- package/src/Signal/Group/sender-key-state.js +83 -0
- package/src/Signal/Group/sender-message-key.d.ts +11 -0
- package/src/Signal/Group/sender-message-key.js +25 -0
- package/src/Signal/libsignal.d.ts +54 -0
- package/src/Signal/libsignal.js +427 -0
- package/src/Signal/lid-mapping.d.ts +20 -0
- package/src/Signal/lid-mapping.js +274 -0
- package/src/Socket/Client/index.d.ts +2 -0
- package/src/Socket/Client/index.js +3 -0
- package/src/Socket/Client/tcp.js +177 -0
- package/src/Socket/Client/types.d.ts +5 -0
- package/src/Socket/Client/types.js +9 -0
- package/src/Socket/Client/websocket.d.ts +12 -0
- package/src/Socket/Client/websocket.js +53 -0
- package/src/Socket/business.d.ts +431 -0
- package/src/Socket/business.js +379 -0
- package/src/Socket/chats.d.ts +247 -0
- package/src/Socket/chats.js +1484 -0
- package/src/Socket/communities.d.ts +598 -0
- package/src/Socket/communities.js +421 -0
- package/src/Socket/email.js +145 -0
- package/src/Socket/groups.d.ts +298 -0
- package/src/Socket/groups.js +339 -0
- package/src/Socket/index.d.ts +581 -0
- package/src/Socket/index.js +37 -0
- package/src/Socket/messages-recv.d.ts +370 -0
- package/src/Socket/messages-recv.js +1864 -0
- package/src/Socket/messages-send.d.ts +390 -0
- package/src/Socket/messages-send.js +1574 -0
- package/src/Socket/mex.d.ts +1 -0
- package/src/Socket/mex.js +44 -0
- package/src/Socket/mobile-builders.js +72 -0
- package/src/Socket/mobile.js +424 -0
- package/src/Socket/newsletter.d.ts +468 -0
- package/src/Socket/newsletter.js +671 -0
- package/src/Socket/socket.d.ts +77 -0
- package/src/Socket/socket.js +1106 -0
- package/src/Store/index.d.ts +3 -0
- package/src/Store/index.js +3 -0
- package/src/Store/make-in-memory-store.d.ts +62 -0
- package/src/Store/make-in-memory-store.js +396 -0
- package/src/Store/make-ordered-dictionary.d.ts +12 -0
- package/src/Store/make-ordered-dictionary.js +78 -0
- package/src/Store/object-repository.d.ts +10 -0
- package/src/Store/object-repository.js +23 -0
- package/src/Types/Auth.d.ts +1 -0
- package/src/Types/Auth.js +1 -0
- package/src/Types/Bussines.d.ts +1 -0
- package/src/Types/Bussines.js +1 -0
- package/src/Types/Call.d.ts +1 -0
- package/src/Types/Call.js +1 -0
- package/src/Types/Chat.d.ts +2 -0
- package/src/Types/Chat.js +24 -0
- package/src/Types/Contact.d.ts +1 -0
- package/src/Types/Contact.js +1 -0
- package/src/Types/Events.d.ts +1 -0
- package/src/Types/Events.js +0 -0
- package/src/Types/GroupMetadata.d.ts +5 -0
- package/src/Types/GroupMetadata.js +6 -0
- package/src/Types/Label.d.ts +2 -0
- package/src/Types/Label.js +24 -0
- package/src/Types/LabelAssociation.d.ts +2 -0
- package/src/Types/LabelAssociation.js +6 -0
- package/src/Types/Message.d.ts +13 -0
- package/src/Types/Message.js +23 -0
- package/src/Types/Mex.d.ts +2 -0
- package/src/Types/Mex.js +95 -0
- package/src/Types/Product.d.ts +1 -0
- package/src/Types/Product.js +1 -0
- package/src/Types/Signal.d.ts +1 -0
- package/src/Types/Signal.js +0 -0
- package/src/Types/Socket.d.ts +1 -0
- package/src/Types/Socket.js +0 -0
- package/src/Types/State.d.ts +5 -0
- package/src/Types/State.js +54 -0
- package/src/Types/USync.d.ts +1 -0
- package/src/Types/USync.js +0 -0
- package/src/Types/index.d.ts +13 -0
- package/src/Types/index.js +25 -0
- package/src/Utils/auth-store.d.ts +29 -0
- package/src/Utils/auth-store.js +76 -0
- package/src/Utils/auth-utils.d.ts +58 -0
- package/src/Utils/auth-utils.js +273 -0
- package/src/Utils/browser-utils.d.ts +9 -0
- package/src/Utils/browser-utils.js +28 -0
- package/src/Utils/business.d.ts +49 -0
- package/src/Utils/business.js +226 -0
- package/src/Utils/chat-utils.d.ts +408 -0
- package/src/Utils/chat-utils.js +846 -0
- package/src/Utils/companion-reg-client-utils.d.ts +4 -0
- package/src/Utils/companion-reg-client-utils.js +39 -0
- package/src/Utils/crypto.d.ts +39 -0
- package/src/Utils/crypto.js +110 -0
- package/src/Utils/decode-wa-message.d.ts +99 -0
- package/src/Utils/decode-wa-message.js +373 -0
- package/src/Utils/event-buffer.d.ts +22 -0
- package/src/Utils/event-buffer.js +624 -0
- package/src/Utils/generics.d.ts +63 -0
- package/src/Utils/generics.js +375 -0
- package/src/Utils/group-metadata-cache.d.ts +4 -0
- package/src/Utils/group-metadata-cache.js +25 -0
- package/src/Utils/history.d.ts +75 -0
- package/src/Utils/history.js +131 -0
- package/src/Utils/html-app.d.ts +19 -0
- package/src/Utils/html-app.js +96 -0
- package/src/Utils/html-multiplayer.d.ts +10 -0
- package/src/Utils/html-multiplayer.js +94 -0
- package/src/Utils/identity-change-handler.d.ts +13 -0
- package/src/Utils/identity-change-handler.js +48 -0
- package/src/Utils/index.d.ts +44 -0
- package/src/Utils/index.js +44 -0
- package/src/Utils/link-preview-metadata.d.ts +47 -0
- package/src/Utils/link-preview-metadata.js +88 -0
- package/src/Utils/link-preview.d.ts +12 -0
- package/src/Utils/link-preview.js +79 -0
- package/src/Utils/logger.d.ts +2 -0
- package/src/Utils/logger.js +2 -0
- package/src/Utils/lt-hash.d.ts +6 -0
- package/src/Utils/lt-hash.js +3 -0
- package/src/Utils/make-mutex.d.ts +6 -0
- package/src/Utils/make-mutex.js +32 -0
- package/src/Utils/message-retry-manager.d.ts +79 -0
- package/src/Utils/message-retry-manager.js +255 -0
- package/src/Utils/messages-media.d.ts +136 -0
- package/src/Utils/messages-media.js +814 -0
- package/src/Utils/messages.d.ts +62 -0
- package/src/Utils/messages.js +2223 -0
- package/src/Utils/mobile-companion-crypto.js +191 -0
- package/src/Utils/modern-messages.d.ts +150 -0
- package/src/Utils/modern-messages.js +496 -0
- package/src/Utils/native-flow.d.ts +11 -0
- package/src/Utils/native-flow.js +115 -0
- package/src/Utils/newsletter-status.d.ts +158 -0
- package/src/Utils/newsletter-status.js +651 -0
- package/src/Utils/noise-handler.d.ts +20 -0
- package/src/Utils/noise-handler.js +200 -0
- package/src/Utils/offline-node-processor.d.ts +9 -0
- package/src/Utils/offline-node-processor.js +33 -0
- package/src/Utils/optional-media.d.ts +3 -0
- package/src/Utils/optional-media.js +45 -0
- package/src/Utils/payment-messages.d.ts +66 -0
- package/src/Utils/payment-messages.js +160 -0
- package/src/Utils/pre-key-manager.d.ts +25 -0
- package/src/Utils/pre-key-manager.js +95 -0
- package/src/Utils/process-message.d.ts +60 -0
- package/src/Utils/process-message.js +688 -0
- package/src/Utils/reporting-utils.d.ts +12 -0
- package/src/Utils/reporting-utils.js +256 -0
- package/src/Utils/scheduled-message.d.ts +51 -0
- package/src/Utils/scheduled-message.js +112 -0
- package/src/Utils/sender-key-memory.d.ts +19 -0
- package/src/Utils/sender-key-memory.js +51 -0
- package/src/Utils/signal.d.ts +82 -0
- package/src/Utils/signal.js +193 -0
- package/src/Utils/stanza-ack.d.ts +16 -0
- package/src/Utils/stanza-ack.js +30 -0
- package/src/Utils/status-stickers.d.ts +127 -0
- package/src/Utils/status-stickers.js +260 -0
- package/src/Utils/sync-action-utils.d.ts +2 -0
- package/src/Utils/sync-action-utils.js +44 -0
- package/src/Utils/tc-token-utils.d.ts +29 -0
- package/src/Utils/tc-token-utils.js +155 -0
- package/src/Utils/ttl-cache.d.ts +28 -0
- package/src/Utils/ttl-cache.js +120 -0
- package/src/Utils/use-mongo-auth-state.d.ts +21 -0
- package/src/Utils/use-mongo-auth-state.js +67 -0
- package/src/Utils/use-multi-file-auth-state.d.ts +11 -0
- package/src/Utils/use-multi-file-auth-state.js +106 -0
- package/src/Utils/use-mysql-auth-state.d.ts +19 -0
- package/src/Utils/use-mysql-auth-state.js +79 -0
- package/src/Utils/use-postgres-auth-state.d.ts +19 -0
- package/src/Utils/use-postgres-auth-state.js +79 -0
- package/src/Utils/use-redis-auth-state.d.ts +19 -0
- package/src/Utils/use-redis-auth-state.js +89 -0
- package/src/Utils/use-single-file-auth-state.d.ts +10 -0
- package/src/Utils/use-single-file-auth-state.js +108 -0
- package/src/Utils/use-sqlite-auth-state.d.ts +12 -0
- package/src/Utils/use-sqlite-auth-state.js +120 -0
- package/src/Utils/useNekoDBAuth.d.ts +19 -0
- package/src/Utils/useNekoDBAuth.js +111 -0
- package/src/Utils/username.d.ts +12 -0
- package/src/Utils/username.js +58 -0
- package/src/Utils/validate-connection.d.ts +43 -0
- package/src/Utils/validate-connection.js +277 -0
- package/src/Utils/voice-recognition.d.ts +53 -0
- package/src/Utils/voice-recognition.js +219 -0
- package/src/Voip/audio-feeder.d.ts +24 -0
- package/src/Voip/audio-feeder.js +268 -0
- package/src/Voip/index.d.ts +88 -0
- package/src/Voip/index.js +936 -0
- package/src/Voip/relay-transport.d.ts +5 -0
- package/src/Voip/relay-transport.js +824 -0
- package/src/Voip/signaling.d.ts +11 -0
- package/src/Voip/signaling.js +1099 -0
- package/src/Voip/types.d.ts +2 -0
- package/src/Voip/types.js +12 -0
- package/src/Voip/video-feeder.d.ts +24 -0
- package/src/Voip/video-feeder.js +268 -0
- package/src/Voip/wasm-engine.d.ts +26 -0
- package/src/Voip/wasm-engine.js +1307 -0
- package/src/Voip/worker-bootstrap.js +1023 -0
- package/src/WABinary/constants.d.ts +22 -0
- package/src/WABinary/constants.js +1300 -0
- package/src/WABinary/decode.d.ts +9 -0
- package/src/WABinary/decode.js +261 -0
- package/src/WABinary/encode.d.ts +2 -0
- package/src/WABinary/encode.js +219 -0
- package/src/WABinary/generic-utils.d.ts +76 -0
- package/src/WABinary/generic-utils.js +214 -0
- package/src/WABinary/index.d.ts +5 -0
- package/src/WABinary/index.js +5 -0
- package/src/WABinary/jid-utils.d.ts +30 -0
- package/src/WABinary/jid-utils.js +97 -0
- package/src/WABinary/types.d.ts +1 -0
- package/src/WABinary/types.js +0 -0
- package/src/WAM/BinaryInfo.d.ts +7 -0
- package/src/WAM/BinaryInfo.js +9 -0
- package/src/WAM/constants.d.ts +34926 -0
- package/src/WAM/constants.js +22852 -0
- package/src/WAM/encode.d.ts +1 -0
- package/src/WAM/encode.js +148 -0
- package/src/WAM/index.d.ts +3 -0
- package/src/WAM/index.js +3 -0
- package/src/WAUSync/Protocols/USyncContactProtocol.d.ts +36 -0
- package/src/WAUSync/Protocols/USyncContactProtocol.js +50 -0
- package/src/WAUSync/Protocols/USyncDeviceProtocol.d.ts +22 -0
- package/src/WAUSync/Protocols/USyncDeviceProtocol.js +51 -0
- package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +12 -0
- package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.js +26 -0
- package/src/WAUSync/Protocols/USyncStatusProtocol.d.ts +12 -0
- package/src/WAUSync/Protocols/USyncStatusProtocol.js +37 -0
- package/src/WAUSync/Protocols/USyncUsernameProtocol.d.ts +9 -0
- package/src/WAUSync/Protocols/USyncUsernameProtocol.js +23 -0
- package/src/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +38 -0
- package/src/WAUSync/Protocols/UsyncBotProfileProtocol.js +49 -0
- package/src/WAUSync/Protocols/UsyncLIDProtocol.d.ts +14 -0
- package/src/WAUSync/Protocols/UsyncLIDProtocol.js +28 -0
- package/src/WAUSync/Protocols/index.d.ts +5 -0
- package/src/WAUSync/Protocols/index.js +5 -0
- package/src/WAUSync/USyncQuery.d.ts +20 -0
- package/src/WAUSync/USyncQuery.js +93 -0
- package/src/WAUSync/USyncUser.d.ts +16 -0
- package/src/WAUSync/USyncUser.js +30 -0
- package/src/WAUSync/index.d.ts +3 -0
- package/src/WAUSync/index.js +3 -0
- package/src/assets/wasm/README.md +76 -0
- package/src/assets/wasm/loader.js +5 -0
- package/src/assets/wasm/whatsapp.wasm +0 -0
- package/src/assets/wasm/worker-modules.js +273 -0
- package/src/index.d.ts +43 -0
- 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,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
|
+
}
|