zapo-js 0.1.1 → 0.1.2
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/README.md +8 -0
- package/dist/appstate/WaAppStateCrypto.js +18 -25
- package/dist/appstate/WaAppStateSyncClient.js +181 -114
- package/dist/appstate/WaAppStateSyncResponseParser.js +16 -5
- package/dist/appstate/constants.js +4 -3
- package/dist/appstate/utils.js +10 -30
- package/dist/auth/WaAuthClient.js +48 -55
- package/dist/auth/flow/WaAuthCredentialsFlow.js +21 -14
- package/dist/auth/index.js +1 -3
- package/dist/auth/pairing/WaPairingFlow.js +21 -23
- package/dist/auth/pairing/WaQrFlow.js +37 -24
- package/dist/client/WaClient.js +103 -276
- package/dist/client/WaClientFactory.js +227 -110
- package/dist/client/connection/WaConnectionManager.js +292 -0
- package/dist/client/connection/WaKeyShareCoordinator.js +63 -0
- package/dist/client/connection/WaReceiptQueue.js +51 -0
- package/dist/client/coordinators/WaAppStateMutationCoordinator.js +471 -0
- package/dist/client/coordinators/WaGroupCoordinator.js +27 -17
- package/dist/client/coordinators/WaIncomingNodeCoordinator.js +20 -27
- package/dist/client/coordinators/WaMessageDispatchCoordinator.js +231 -686
- package/dist/client/coordinators/WaRetryCoordinator.js +70 -37
- package/dist/client/dirty.js +35 -29
- package/dist/client/events/chat.js +4 -3
- package/dist/client/events/group.js +59 -36
- package/dist/client/history-sync.js +53 -63
- package/dist/client/incoming.js +23 -20
- package/dist/client/mailbox.js +8 -8
- package/dist/client/messages.js +4 -4
- package/dist/client/messaging/fanout.js +189 -0
- package/dist/client/messaging/key-protocol.js +130 -0
- package/dist/client/messaging/participants.js +191 -0
- package/dist/crypto/core/hkdf.js +3 -8
- package/dist/crypto/core/index.js +1 -4
- package/dist/crypto/core/keys.js +2 -3
- package/dist/crypto/core/primitives.js +12 -15
- package/dist/crypto/core/random.js +7 -26
- package/dist/crypto/curves/Ed25519.js +7 -8
- package/dist/crypto/curves/X25519.js +13 -16
- package/dist/crypto/index.js +0 -5
- package/dist/esm/appstate/WaAppStateCrypto.js +6 -13
- package/dist/esm/appstate/WaAppStateSyncClient.js +174 -107
- package/dist/esm/appstate/WaAppStateSyncResponseParser.js +17 -6
- package/dist/esm/appstate/constants.js +3 -2
- package/dist/esm/appstate/utils.js +8 -27
- package/dist/esm/auth/WaAuthClient.js +48 -55
- package/dist/esm/auth/flow/WaAuthCredentialsFlow.js +21 -14
- package/dist/esm/auth/index.js +0 -1
- package/dist/esm/auth/pairing/WaPairingFlow.js +14 -16
- package/dist/esm/auth/pairing/WaQrFlow.js +37 -24
- package/dist/esm/client/WaClient.js +103 -276
- package/dist/esm/client/WaClientFactory.js +227 -110
- package/dist/esm/client/connection/WaConnectionManager.js +288 -0
- package/dist/esm/client/connection/WaKeyShareCoordinator.js +59 -0
- package/dist/esm/client/connection/WaReceiptQueue.js +47 -0
- package/dist/esm/client/coordinators/WaAppStateMutationCoordinator.js +467 -0
- package/dist/esm/client/coordinators/WaGroupCoordinator.js +20 -10
- package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +20 -27
- package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +232 -687
- package/dist/esm/client/coordinators/WaRetryCoordinator.js +71 -38
- package/dist/esm/client/dirty.js +30 -24
- package/dist/esm/client/events/chat.js +4 -3
- package/dist/esm/client/events/group.js +50 -28
- package/dist/esm/client/history-sync.js +50 -60
- package/dist/esm/client/incoming.js +23 -20
- package/dist/esm/client/mailbox.js +8 -8
- package/dist/esm/client/messages.js +1 -1
- package/dist/esm/client/messaging/fanout.js +186 -0
- package/dist/esm/client/messaging/key-protocol.js +127 -0
- package/dist/esm/client/messaging/participants.js +188 -0
- package/dist/esm/crypto/core/hkdf.js +3 -8
- package/dist/esm/crypto/core/index.js +0 -1
- package/dist/esm/crypto/core/keys.js +2 -3
- package/dist/esm/crypto/core/primitives.js +12 -15
- package/dist/esm/crypto/core/random.js +6 -25
- package/dist/esm/crypto/curves/Ed25519.js +4 -5
- package/dist/esm/crypto/curves/X25519.js +10 -13
- package/dist/esm/crypto/index.js +0 -2
- package/dist/esm/infra/log/ConsoleLogger.js +18 -17
- package/dist/esm/infra/log/PinoLogger.js +15 -9
- package/dist/esm/infra/log/types.js +11 -1
- package/dist/esm/infra/perf/BoundedTaskQueue.js +13 -17
- package/dist/esm/media/WaMediaCrypto.js +2 -4
- package/dist/esm/media/WaMediaTransferClient.js +226 -58
- package/dist/esm/media/conn.js +10 -6
- package/dist/esm/media/constants.js +4 -1
- package/dist/esm/message/WaMessageClient.js +4 -13
- package/dist/esm/message/ack.js +6 -6
- package/dist/esm/message/addon-crypto.js +59 -0
- package/dist/esm/message/incoming.js +106 -111
- package/dist/esm/message/index.js +2 -0
- package/dist/esm/message/reporting-token.js +438 -0
- package/dist/esm/message/use-case-secret.js +49 -0
- package/dist/esm/protocol/appstate.js +58 -0
- package/dist/esm/protocol/constants.js +2 -1
- package/dist/esm/protocol/index.js +2 -10
- package/dist/esm/protocol/jid.js +63 -51
- package/dist/esm/protocol/media.js +3 -3
- package/dist/esm/protocol/nodes.js +2 -0
- package/dist/esm/protocol/usync.js +11 -0
- package/dist/esm/retry/index.js +1 -0
- package/dist/esm/retry/outbound.js +4 -5
- package/dist/esm/retry/parse.js +58 -76
- package/dist/esm/retry/replay.js +48 -49
- package/dist/esm/retry/tracker.js +56 -0
- package/dist/esm/signal/api/SignalDeviceSyncApi.js +249 -82
- package/dist/esm/signal/api/SignalDigestSyncApi.js +6 -1
- package/dist/esm/signal/api/SignalIdentitySyncApi.js +49 -34
- package/dist/esm/signal/api/SignalMissingPreKeysSyncApi.js +70 -62
- package/dist/esm/signal/api/SignalSessionSyncApi.js +23 -30
- package/dist/esm/signal/crypto/WaAdvSignature.js +3 -5
- package/dist/esm/signal/group/SenderKeyChain.js +28 -23
- package/dist/esm/signal/group/SenderKeyCodec.js +2 -4
- package/dist/esm/signal/group/SenderKeyManager.js +26 -16
- package/dist/esm/signal/index.js +1 -0
- package/dist/esm/signal/session/SignalProtocol.js +49 -14
- package/dist/esm/signal/session/SignalRatchet.js +24 -15
- package/dist/esm/signal/session/SignalSession.js +14 -9
- package/dist/esm/signal/session/resolver.js +186 -0
- package/dist/esm/signal/store/sqlite.js +16 -37
- package/dist/esm/store/createStore.js +16 -18
- package/dist/esm/store/noop.store.js +3 -6
- package/dist/esm/store/providers/memory/appstate.store.js +30 -6
- package/dist/esm/store/providers/memory/contact.store.js +5 -0
- package/dist/esm/store/providers/memory/device-list.store.js +3 -30
- package/dist/esm/store/providers/memory/message.store.js +11 -5
- package/dist/esm/store/providers/memory/participants.store.js +1 -8
- package/dist/esm/store/providers/memory/sender-key.store.js +5 -7
- package/dist/esm/store/providers/memory/signal.store.js +13 -1
- package/dist/esm/store/providers/memory/thread.store.js +5 -0
- package/dist/esm/store/providers/sqlite/appstate.store.js +82 -1
- package/dist/esm/store/providers/sqlite/connection.js +18 -13
- package/dist/esm/store/providers/sqlite/contact.store.js +31 -18
- package/dist/esm/store/providers/sqlite/device-list.store.js +7 -35
- package/dist/esm/store/providers/sqlite/message.store.js +45 -32
- package/dist/esm/store/providers/sqlite/migrations.js +1 -1
- package/dist/esm/store/providers/sqlite/participants.store.js +1 -9
- package/dist/esm/store/providers/sqlite/retry.store.js +8 -11
- package/dist/esm/store/providers/sqlite/sender-key.store.js +25 -30
- package/dist/esm/store/providers/sqlite/signal.store.js +104 -22
- package/dist/esm/store/providers/sqlite/table-names.js +107 -0
- package/dist/esm/store/providers/sqlite/thread.store.js +35 -22
- package/dist/esm/transport/WaComms.js +25 -23
- package/dist/esm/transport/WaWebSocket.js +115 -12
- package/dist/esm/transport/binary/decoder.js +4 -4
- package/dist/esm/transport/binary/encoder.js +12 -4
- package/dist/esm/transport/index.js +1 -0
- package/dist/esm/transport/keepalive/WaKeepAlive.js +2 -8
- package/dist/esm/transport/node/WaNodeOrchestrator.js +2 -4
- package/dist/esm/transport/node/WaNodeTransport.js +0 -3
- package/dist/esm/transport/node/builders/{accountSync.js → account-sync.js} +16 -36
- package/dist/esm/transport/node/builders/index.js +2 -1
- package/dist/esm/transport/node/builders/message.js +9 -0
- package/dist/esm/transport/node/builders/pairing.js +4 -5
- package/dist/esm/transport/node/builders/usync.js +41 -0
- package/dist/esm/transport/node/helpers.js +107 -5
- package/dist/esm/transport/node/usync.js +35 -0
- package/dist/esm/transport/noise/WaFrameCodec.js +48 -33
- package/dist/esm/transport/noise/WaNoiseCert.js +3 -6
- package/dist/esm/transport/noise/WaNoiseSession.js +17 -10
- package/dist/esm/transport/proxy.js +27 -0
- package/dist/esm/transport/stream/parse.js +13 -48
- package/dist/esm/util/bytes.js +50 -32
- package/dist/esm/util/coercion.js +6 -14
- package/dist/esm/util/primitives.js +39 -14
- package/dist/infra/log/ConsoleLogger.js +18 -17
- package/dist/infra/log/PinoLogger.js +15 -9
- package/dist/infra/log/types.js +12 -0
- package/dist/infra/perf/BoundedTaskQueue.js +13 -17
- package/dist/media/WaMediaCrypto.js +1 -3
- package/dist/media/WaMediaTransferClient.js +259 -58
- package/dist/media/conn.js +10 -6
- package/dist/media/constants.js +4 -1
- package/dist/message/WaMessageClient.js +5 -14
- package/dist/message/ack.js +6 -6
- package/dist/message/addon-crypto.js +65 -0
- package/dist/message/incoming.js +104 -109
- package/dist/message/index.js +2 -0
- package/dist/message/reporting-token.js +443 -0
- package/dist/message/use-case-secret.js +55 -0
- package/dist/protocol/appstate.js +59 -1
- package/dist/protocol/constants.js +7 -1
- package/dist/protocol/index.js +20 -42
- package/dist/protocol/jid.js +64 -51
- package/dist/protocol/media.js +3 -3
- package/dist/protocol/nodes.js +2 -0
- package/dist/protocol/usync.js +14 -0
- package/dist/retry/index.js +3 -1
- package/dist/retry/outbound.js +6 -7
- package/dist/retry/parse.js +57 -75
- package/dist/retry/replay.js +46 -47
- package/dist/retry/tracker.js +59 -0
- package/dist/signal/api/SignalDeviceSyncApi.js +247 -80
- package/dist/signal/api/SignalDigestSyncApi.js +6 -1
- package/dist/signal/api/SignalIdentitySyncApi.js +49 -34
- package/dist/signal/api/SignalMissingPreKeysSyncApi.js +67 -59
- package/dist/signal/api/SignalSessionSyncApi.js +23 -30
- package/dist/signal/crypto/WaAdvSignature.js +2 -4
- package/dist/signal/group/SenderKeyChain.js +27 -22
- package/dist/signal/group/SenderKeyCodec.js +1 -3
- package/dist/signal/group/SenderKeyManager.js +26 -16
- package/dist/signal/index.js +3 -1
- package/dist/signal/session/SignalProtocol.js +49 -14
- package/dist/signal/session/SignalRatchet.js +24 -15
- package/dist/signal/session/SignalSession.js +14 -9
- package/dist/signal/session/resolver.js +189 -0
- package/dist/signal/store/sqlite.js +16 -37
- package/dist/store/createStore.js +16 -18
- package/dist/store/noop.store.js +3 -6
- package/dist/store/providers/memory/appstate.store.js +28 -4
- package/dist/store/providers/memory/contact.store.js +5 -0
- package/dist/store/providers/memory/device-list.store.js +3 -30
- package/dist/store/providers/memory/message.store.js +11 -5
- package/dist/store/providers/memory/participants.store.js +1 -8
- package/dist/store/providers/memory/sender-key.store.js +8 -10
- package/dist/store/providers/memory/signal.store.js +21 -9
- package/dist/store/providers/memory/thread.store.js +5 -0
- package/dist/store/providers/sqlite/appstate.store.js +81 -0
- package/dist/store/providers/sqlite/connection.js +18 -13
- package/dist/store/providers/sqlite/contact.store.js +31 -18
- package/dist/store/providers/sqlite/device-list.store.js +7 -35
- package/dist/store/providers/sqlite/message.store.js +45 -32
- package/dist/store/providers/sqlite/migrations.js +1 -1
- package/dist/store/providers/sqlite/participants.store.js +1 -9
- package/dist/store/providers/sqlite/retry.store.js +8 -11
- package/dist/store/providers/sqlite/sender-key.store.js +24 -29
- package/dist/store/providers/sqlite/signal.store.js +105 -23
- package/dist/store/providers/sqlite/table-names.js +113 -0
- package/dist/store/providers/sqlite/thread.store.js +35 -22
- package/dist/transport/WaComms.js +27 -25
- package/dist/transport/WaWebSocket.js +148 -12
- package/dist/transport/binary/decoder.js +4 -4
- package/dist/transport/binary/encoder.js +12 -4
- package/dist/transport/index.js +7 -1
- package/dist/transport/keepalive/WaKeepAlive.js +1 -7
- package/dist/transport/node/WaNodeOrchestrator.js +2 -4
- package/dist/transport/node/WaNodeTransport.js +0 -3
- package/dist/transport/node/builders/{accountSync.js → account-sync.js} +15 -35
- package/dist/transport/node/builders/index.js +12 -9
- package/dist/transport/node/builders/message.js +9 -0
- package/dist/transport/node/builders/pairing.js +4 -5
- package/dist/transport/node/builders/usync.js +45 -0
- package/dist/transport/node/helpers.js +112 -4
- package/dist/transport/node/usync.js +38 -0
- package/dist/transport/noise/WaFrameCodec.js +47 -32
- package/dist/transport/noise/WaNoiseCert.js +5 -8
- package/dist/transport/noise/WaNoiseSession.js +17 -10
- package/dist/transport/proxy.js +34 -0
- package/dist/transport/stream/parse.js +17 -53
- package/dist/types/appstate/WaAppStateCrypto.d.ts +0 -1
- package/dist/types/appstate/WaAppStateSyncClient.d.ts +5 -2
- package/dist/types/appstate/constants.d.ts +1 -0
- package/dist/types/appstate/store/sqlite.d.ts +4 -18
- package/dist/types/appstate/utils.d.ts +0 -1
- package/dist/types/auth/WaAuthClient.d.ts +10 -12
- package/dist/types/auth/index.d.ts +0 -2
- package/dist/types/auth/pairing/WaQrFlow.d.ts +1 -1
- package/dist/types/auth/types.d.ts +6 -9
- package/dist/types/client/WaClient.d.ts +27 -25
- package/dist/types/client/WaClientFactory.d.ts +22 -23
- package/dist/types/client/connection/WaConnectionManager.d.ts +64 -0
- package/dist/types/client/connection/WaKeyShareCoordinator.d.ts +14 -0
- package/dist/types/client/connection/WaReceiptQueue.d.ts +13 -0
- package/dist/types/client/coordinators/WaAppStateMutationCoordinator.d.ts +46 -0
- package/dist/types/client/coordinators/WaIncomingNodeCoordinator.d.ts +0 -1
- package/dist/types/client/coordinators/WaMessageDispatchCoordinator.d.ts +18 -41
- package/dist/types/client/coordinators/WaRetryCoordinator.d.ts +2 -0
- package/dist/types/client/dirty.d.ts +1 -0
- package/dist/types/client/events/group.d.ts +2 -1
- package/dist/types/client/index.d.ts +1 -1
- package/dist/types/client/messaging/fanout.d.ts +14 -0
- package/dist/types/client/messaging/key-protocol.d.ts +18 -0
- package/dist/types/client/messaging/participants.d.ts +13 -0
- package/dist/types/client/types.d.ts +24 -1
- package/dist/types/crypto/core/hkdf.d.ts +0 -6
- package/dist/types/crypto/core/index.d.ts +0 -1
- package/dist/types/crypto/core/random.d.ts +1 -7
- package/dist/types/crypto/index.d.ts +0 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/infra/log/ConsoleLogger.d.ts +2 -1
- package/dist/types/infra/log/PinoLogger.d.ts +1 -1
- package/dist/types/infra/log/types.d.ts +1 -0
- package/dist/types/infra/perf/BoundedTaskQueue.d.ts +1 -1
- package/dist/types/media/WaMediaTransferClient.d.ts +13 -3
- package/dist/types/media/types.d.ts +5 -0
- package/dist/types/message/addon-crypto.d.ts +25 -0
- package/dist/types/message/index.d.ts +2 -0
- package/dist/types/message/reporting-token.d.ts +19 -0
- package/dist/types/message/use-case-secret.d.ts +20 -0
- package/dist/types/protocol/appstate.d.ts +58 -0
- package/dist/types/protocol/constants.d.ts +2 -1
- package/dist/types/protocol/index.d.ts +2 -10
- package/dist/types/protocol/jid.d.ts +3 -3
- package/dist/types/protocol/nodes.d.ts +2 -0
- package/dist/types/protocol/usync.d.ts +11 -0
- package/dist/types/retry/index.d.ts +1 -0
- package/dist/types/retry/replay.d.ts +0 -4
- package/dist/types/retry/tracker.d.ts +19 -0
- package/dist/types/retry/types.d.ts +4 -3
- package/dist/types/signal/api/SignalDeviceSyncApi.d.ts +13 -1
- package/dist/types/signal/group/SenderKeyCodec.d.ts +4 -6
- package/dist/types/signal/index.d.ts +1 -0
- package/dist/types/signal/session/SignalProtocol.d.ts +9 -0
- package/dist/types/signal/session/resolver.d.ts +17 -0
- package/dist/types/store/contracts/appstate.store.d.ts +3 -0
- package/dist/types/store/contracts/contact.store.d.ts +1 -0
- package/dist/types/store/contracts/device-list.store.d.ts +0 -3
- package/dist/types/store/contracts/message.store.d.ts +1 -0
- package/dist/types/store/contracts/participants.store.d.ts +0 -1
- package/dist/types/store/contracts/sender-key.store.d.ts +0 -1
- package/dist/types/store/contracts/signal.store.d.ts +6 -0
- package/dist/types/store/contracts/thread.store.d.ts +1 -0
- package/dist/types/store/index.d.ts +1 -1
- package/dist/types/store/providers/memory/appstate.store.d.ts +2 -0
- package/dist/types/store/providers/memory/contact.store.d.ts +1 -0
- package/dist/types/store/providers/memory/device-list.store.d.ts +0 -3
- package/dist/types/store/providers/memory/message.store.d.ts +1 -0
- package/dist/types/store/providers/memory/participants.store.d.ts +0 -1
- package/dist/types/store/providers/memory/sender-key.store.d.ts +0 -1
- package/dist/types/store/providers/memory/signal.store.d.ts +6 -0
- package/dist/types/store/providers/memory/thread.store.d.ts +1 -0
- package/dist/types/store/providers/sqlite/appstate.store.d.ts +2 -0
- package/dist/types/store/providers/sqlite/contact.store.d.ts +2 -0
- package/dist/types/store/providers/sqlite/device-list.store.d.ts +0 -3
- package/dist/types/store/providers/sqlite/message.store.d.ts +2 -0
- package/dist/types/store/providers/sqlite/participants.store.d.ts +0 -1
- package/dist/types/store/providers/sqlite/retry.store.d.ts +0 -1
- package/dist/types/store/providers/sqlite/sender-key.store.d.ts +0 -1
- package/dist/types/store/providers/sqlite/signal.store.d.ts +7 -0
- package/dist/types/store/providers/sqlite/table-names.d.ts +5 -0
- package/dist/types/store/providers/sqlite/thread.store.d.ts +2 -0
- package/dist/types/store/types.d.ts +3 -0
- package/dist/types/transport/WaWebSocket.d.ts +3 -0
- package/dist/types/transport/index.d.ts +2 -1
- package/dist/types/transport/keepalive/WaKeepAlive.d.ts +0 -1
- package/dist/types/transport/node/WaNodeTransport.d.ts +0 -9
- package/dist/types/transport/node/builders/group.d.ts +4 -6
- package/dist/types/transport/node/builders/index.d.ts +2 -1
- package/dist/types/transport/node/builders/message.d.ts +14 -25
- package/dist/types/transport/node/builders/retry.d.ts +2 -4
- package/dist/types/transport/node/builders/usync.d.ts +21 -0
- package/dist/types/transport/node/helpers.d.ts +8 -0
- package/dist/types/transport/node/usync.d.ts +2 -0
- package/dist/types/transport/noise/WaFrameCodec.d.ts +3 -0
- package/dist/types/transport/noise/WaNoiseSession.d.ts +1 -0
- package/dist/types/transport/proxy.d.ts +6 -0
- package/dist/types/transport/stream/parse.d.ts +0 -1
- package/dist/types/transport/types.d.ts +18 -1
- package/dist/types/util/bytes.d.ts +5 -0
- package/dist/types/util/primitives.d.ts +3 -0
- package/dist/util/bytes.js +55 -33
- package/dist/util/coercion.js +6 -14
- package/dist/util/primitives.js +42 -14
- package/package.json +26 -5
- package/dist/crypto/core/encoding.js +0 -29
- package/dist/esm/crypto/core/encoding.js +0 -25
- package/dist/esm/util/base64.js +0 -18
- package/dist/esm/util/signal-address.js +0 -5
- package/dist/types/crypto/core/encoding.d.ts +0 -11
- package/dist/types/util/base64.d.ts +0 -4
- package/dist/types/util/signal-address.d.ts +0 -2
- package/dist/util/base64.js +0 -24
- package/dist/util/signal-address.js +0 -8
- /package/dist/types/transport/node/builders/{accountSync.d.ts → account-sync.d.ts} +0 -0
|
@@ -1,11 +1,21 @@
|
|
|
1
|
+
import { signalAddressKey } from '../../../protocol/jid.js';
|
|
1
2
|
import { decodeSignalPreKeyRow, decodeSignalRegistrationRow, decodeSignalRemoteIdentity, decodeSignalSessionRecord, decodeSignalSignedPreKeyRow, encodeSignalSessionRecord, toSignalAddressParts } from '../../../signal/store/sqlite.js';
|
|
2
3
|
import { BaseSqliteStore } from '../../providers/sqlite/BaseSqliteStore.js';
|
|
3
4
|
import { asNumber, asOptionalNumber, asString, toBoolOrUndef, resolvePositive } from '../../../util/coercion.js';
|
|
4
|
-
import { signalAddressKey } from '../../../util/signal-address.js';
|
|
5
5
|
const DEFAULTS = Object.freeze({
|
|
6
6
|
preKeyBatchSize: 500,
|
|
7
7
|
hasSessionBatchSize: 250
|
|
8
8
|
});
|
|
9
|
+
function repeatSqlToken(token, count, separator) {
|
|
10
|
+
if (count <= 1) {
|
|
11
|
+
return token;
|
|
12
|
+
}
|
|
13
|
+
let out = token;
|
|
14
|
+
for (let index = 1; index < count; index += 1) {
|
|
15
|
+
out += separator + token;
|
|
16
|
+
}
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
9
19
|
export class WaSignalSqliteStore extends BaseSqliteStore {
|
|
10
20
|
constructor(options, storeOptions = {}) {
|
|
11
21
|
super(options, ['signal']);
|
|
@@ -141,7 +151,7 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
|
|
|
141
151
|
for (let start = 0; start < uniqueKeyIds.length; start += this.preKeyBatchSize) {
|
|
142
152
|
const end = Math.min(start + this.preKeyBatchSize, uniqueKeyIds.length);
|
|
143
153
|
const batchLength = end - start;
|
|
144
|
-
const placeholders =
|
|
154
|
+
const placeholders = repeatSqlToken('?', batchLength, ', ');
|
|
145
155
|
const params = [this.options.sessionId];
|
|
146
156
|
for (let index = start; index < end; index += 1) {
|
|
147
157
|
params.push(uniqueKeyIds[index]);
|
|
@@ -213,9 +223,7 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
|
|
|
213
223
|
for (let start = 0; start < targets.length; start += this.hasSessionBatchSize) {
|
|
214
224
|
const end = Math.min(start + this.hasSessionBatchSize, targets.length);
|
|
215
225
|
const batchLength = end - start;
|
|
216
|
-
const filters =
|
|
217
|
-
.fill('(user = ? AND server = ? AND device = ?)')
|
|
218
|
-
.join(' OR ');
|
|
226
|
+
const filters = repeatSqlToken('(user = ? AND server = ? AND device = ?)', batchLength, ' OR ');
|
|
219
227
|
const params = [this.options.sessionId];
|
|
220
228
|
for (let index = start; index < end; index += 1) {
|
|
221
229
|
const target = targets[index];
|
|
@@ -232,7 +240,7 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
|
|
|
232
240
|
}));
|
|
233
241
|
}
|
|
234
242
|
}
|
|
235
|
-
return
|
|
243
|
+
return targets.map((target) => existingKeys.has(signalAddressKey(target)));
|
|
236
244
|
}
|
|
237
245
|
async getSession(address) {
|
|
238
246
|
const db = await this.getConnection();
|
|
@@ -242,24 +250,50 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
|
|
|
242
250
|
WHERE session_id = ? AND user = ? AND server = ? AND device = ?`, [this.options.sessionId, target.user, target.server, target.device]);
|
|
243
251
|
return row ? decodeSignalSessionRecord(row.record) : null;
|
|
244
252
|
}
|
|
253
|
+
async getSessionsBatch(addresses) {
|
|
254
|
+
if (addresses.length === 0) {
|
|
255
|
+
return [];
|
|
256
|
+
}
|
|
257
|
+
const db = await this.getConnection();
|
|
258
|
+
const targets = addresses.map((address) => toSignalAddressParts(address));
|
|
259
|
+
const byAddressKey = new Map();
|
|
260
|
+
for (let start = 0; start < targets.length; start += this.hasSessionBatchSize) {
|
|
261
|
+
const end = Math.min(start + this.hasSessionBatchSize, targets.length);
|
|
262
|
+
const batchLength = end - start;
|
|
263
|
+
const filters = repeatSqlToken('(user = ? AND server = ? AND device = ?)', batchLength, ' OR ');
|
|
264
|
+
const params = [this.options.sessionId];
|
|
265
|
+
for (let index = start; index < end; index += 1) {
|
|
266
|
+
const target = targets[index];
|
|
267
|
+
params.push(target.user, target.server, target.device);
|
|
268
|
+
}
|
|
269
|
+
const rows = db.all(`SELECT user, server, device, record
|
|
270
|
+
FROM signal_session
|
|
271
|
+
WHERE session_id = ? AND (${filters})`, params);
|
|
272
|
+
for (const row of rows) {
|
|
273
|
+
byAddressKey.set(signalAddressKey({
|
|
274
|
+
user: asString(row.user, 'signal_session.user'),
|
|
275
|
+
server: asString(row.server, 'signal_session.server'),
|
|
276
|
+
device: asNumber(row.device, 'signal_session.device')
|
|
277
|
+
}), decodeSignalSessionRecord(row.record));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return targets.map((target) => byAddressKey.get(signalAddressKey(target)) ?? null);
|
|
281
|
+
}
|
|
245
282
|
async setSession(address, session) {
|
|
246
283
|
const db = await this.getConnection();
|
|
247
284
|
const target = toSignalAddressParts(address);
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
target.device,
|
|
261
|
-
encodeSignalSessionRecord(session)
|
|
262
|
-
]);
|
|
285
|
+
this.upsertSession(db, target, session);
|
|
286
|
+
}
|
|
287
|
+
async setSessionsBatch(entries) {
|
|
288
|
+
if (entries.length === 0) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
await this.withTransaction((db) => {
|
|
292
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
293
|
+
const entry = entries[index];
|
|
294
|
+
this.upsertSession(db, toSignalAddressParts(entry.address), entry.session);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
263
297
|
}
|
|
264
298
|
async deleteSession(address) {
|
|
265
299
|
const db = await this.getConnection();
|
|
@@ -275,6 +309,35 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
|
|
|
275
309
|
WHERE session_id = ? AND user = ? AND server = ? AND device = ?`, [this.options.sessionId, target.user, target.server, target.device]);
|
|
276
310
|
return row ? decodeSignalRemoteIdentity(row.identity_key) : null;
|
|
277
311
|
}
|
|
312
|
+
async getRemoteIdentities(addresses) {
|
|
313
|
+
if (addresses.length === 0) {
|
|
314
|
+
return [];
|
|
315
|
+
}
|
|
316
|
+
const db = await this.getConnection();
|
|
317
|
+
const targets = addresses.map((address) => toSignalAddressParts(address));
|
|
318
|
+
const byAddressKey = new Map();
|
|
319
|
+
for (let start = 0; start < targets.length; start += this.hasSessionBatchSize) {
|
|
320
|
+
const end = Math.min(start + this.hasSessionBatchSize, targets.length);
|
|
321
|
+
const batchLength = end - start;
|
|
322
|
+
const filters = repeatSqlToken('(user = ? AND server = ? AND device = ?)', batchLength, ' OR ');
|
|
323
|
+
const params = [this.options.sessionId];
|
|
324
|
+
for (let index = start; index < end; index += 1) {
|
|
325
|
+
const target = targets[index];
|
|
326
|
+
params.push(target.user, target.server, target.device);
|
|
327
|
+
}
|
|
328
|
+
const rows = db.all(`SELECT user, server, device, identity_key
|
|
329
|
+
FROM signal_identity
|
|
330
|
+
WHERE session_id = ? AND (${filters})`, params);
|
|
331
|
+
for (const row of rows) {
|
|
332
|
+
byAddressKey.set(signalAddressKey({
|
|
333
|
+
user: asString(row.user, 'signal_identity.user'),
|
|
334
|
+
server: asString(row.server, 'signal_identity.server'),
|
|
335
|
+
device: asNumber(row.device, 'signal_identity.device')
|
|
336
|
+
}), decodeSignalRemoteIdentity(row.identity_key));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return targets.map((target) => byAddressKey.get(signalAddressKey(target)) ?? null);
|
|
340
|
+
}
|
|
278
341
|
async setRemoteIdentity(address, identityKey) {
|
|
279
342
|
const db = await this.getConnection();
|
|
280
343
|
const target = toSignalAddressParts(address);
|
|
@@ -294,7 +357,9 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
|
|
|
294
357
|
async clear() {
|
|
295
358
|
await this.withTransaction((db) => {
|
|
296
359
|
db.run('DELETE FROM signal_registration WHERE session_id = ?', [this.options.sessionId]);
|
|
297
|
-
db.run('DELETE FROM signal_signed_prekey WHERE session_id = ?', [
|
|
360
|
+
db.run('DELETE FROM signal_signed_prekey WHERE session_id = ?', [
|
|
361
|
+
this.options.sessionId
|
|
362
|
+
]);
|
|
298
363
|
db.run('DELETE FROM signal_prekey WHERE session_id = ?', [this.options.sessionId]);
|
|
299
364
|
db.run('DELETE FROM signal_session WHERE session_id = ?', [this.options.sessionId]);
|
|
300
365
|
db.run('DELETE FROM signal_identity WHERE session_id = ?', [this.options.sessionId]);
|
|
@@ -320,6 +385,23 @@ export class WaSignalSqliteStore extends BaseSqliteStore {
|
|
|
320
385
|
record.uploaded === true ? 1 : 0
|
|
321
386
|
]);
|
|
322
387
|
}
|
|
388
|
+
upsertSession(db, target, session) {
|
|
389
|
+
db.run(`INSERT INTO signal_session (
|
|
390
|
+
session_id,
|
|
391
|
+
user,
|
|
392
|
+
server,
|
|
393
|
+
device,
|
|
394
|
+
record
|
|
395
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
396
|
+
ON CONFLICT(session_id, user, server, device) DO UPDATE SET
|
|
397
|
+
record=excluded.record`, [
|
|
398
|
+
this.options.sessionId,
|
|
399
|
+
target.user,
|
|
400
|
+
target.server,
|
|
401
|
+
target.device,
|
|
402
|
+
encodeSignalSessionRecord(session)
|
|
403
|
+
]);
|
|
404
|
+
}
|
|
323
405
|
ensureMetaRow(db) {
|
|
324
406
|
db.run(`INSERT INTO signal_meta (
|
|
325
407
|
session_id,
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const SQLITE_TABLE_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
2
|
+
const WA_SQLITE_TABLE_NAME_ORDER = Object.freeze([
|
|
3
|
+
'wa_migrations',
|
|
4
|
+
'auth_credentials',
|
|
5
|
+
'signal_meta',
|
|
6
|
+
'signal_registration',
|
|
7
|
+
'signal_signed_prekey',
|
|
8
|
+
'signal_prekey',
|
|
9
|
+
'signal_session',
|
|
10
|
+
'signal_identity',
|
|
11
|
+
'sender_keys',
|
|
12
|
+
'sender_key_distribution',
|
|
13
|
+
'appstate_sync_keys',
|
|
14
|
+
'appstate_collection_versions',
|
|
15
|
+
'appstate_collection_index_values',
|
|
16
|
+
'retry_outbound_messages',
|
|
17
|
+
'retry_inbound_counters',
|
|
18
|
+
'mailbox_messages',
|
|
19
|
+
'mailbox_threads',
|
|
20
|
+
'mailbox_contacts',
|
|
21
|
+
'group_participants_cache',
|
|
22
|
+
'device_list_cache'
|
|
23
|
+
]);
|
|
24
|
+
const WA_SQLITE_ALLOWED_TABLE_NAME_SET = new Set(WA_SQLITE_TABLE_NAME_ORDER);
|
|
25
|
+
const WA_SQLITE_ALLOWED_TABLE_NAME_LIST = WA_SQLITE_TABLE_NAME_ORDER.join(', ');
|
|
26
|
+
export const WA_SQLITE_DEFAULT_TABLE_NAMES = Object.freeze({
|
|
27
|
+
wa_migrations: 'wa_migrations',
|
|
28
|
+
auth_credentials: 'auth_credentials',
|
|
29
|
+
signal_meta: 'signal_meta',
|
|
30
|
+
signal_registration: 'signal_registration',
|
|
31
|
+
signal_signed_prekey: 'signal_signed_prekey',
|
|
32
|
+
signal_prekey: 'signal_prekey',
|
|
33
|
+
signal_session: 'signal_session',
|
|
34
|
+
signal_identity: 'signal_identity',
|
|
35
|
+
sender_keys: 'sender_keys',
|
|
36
|
+
sender_key_distribution: 'sender_key_distribution',
|
|
37
|
+
appstate_sync_keys: 'appstate_sync_keys',
|
|
38
|
+
appstate_collection_versions: 'appstate_collection_versions',
|
|
39
|
+
appstate_collection_index_values: 'appstate_collection_index_values',
|
|
40
|
+
retry_outbound_messages: 'retry_outbound_messages',
|
|
41
|
+
retry_inbound_counters: 'retry_inbound_counters',
|
|
42
|
+
mailbox_messages: 'mailbox_messages',
|
|
43
|
+
mailbox_threads: 'mailbox_threads',
|
|
44
|
+
mailbox_contacts: 'mailbox_contacts',
|
|
45
|
+
group_participants_cache: 'group_participants_cache',
|
|
46
|
+
device_list_cache: 'device_list_cache'
|
|
47
|
+
});
|
|
48
|
+
const DEFAULT_SQLITE_TABLE_NAME_SERIALIZATION = serializeSqliteTableNames(WA_SQLITE_DEFAULT_TABLE_NAMES);
|
|
49
|
+
function normalizeTableName(table, rawValue) {
|
|
50
|
+
const value = rawValue.trim();
|
|
51
|
+
if (value.length === 0) {
|
|
52
|
+
throw new Error(`sqlite tableNames.${table} must be a non-empty string`);
|
|
53
|
+
}
|
|
54
|
+
if (!SQLITE_TABLE_NAME_PATTERN.test(value)) {
|
|
55
|
+
throw new Error(`sqlite tableNames.${table} must match ${SQLITE_TABLE_NAME_PATTERN.toString()}`);
|
|
56
|
+
}
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
function assertNoDuplicateTableNames(tableNames) {
|
|
60
|
+
const seen = new Set();
|
|
61
|
+
for (const table of WA_SQLITE_TABLE_NAME_ORDER) {
|
|
62
|
+
const mapped = tableNames[table];
|
|
63
|
+
const normalizedMapped = mapped.toLowerCase();
|
|
64
|
+
if (seen.has(normalizedMapped)) {
|
|
65
|
+
throw new Error(`sqlite tableNames contains duplicate target "${mapped}"`);
|
|
66
|
+
}
|
|
67
|
+
seen.add(normalizedMapped);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function resolveSqliteTableNames(overrides) {
|
|
71
|
+
if (!overrides) {
|
|
72
|
+
return WA_SQLITE_DEFAULT_TABLE_NAMES;
|
|
73
|
+
}
|
|
74
|
+
const entries = Object.entries(overrides);
|
|
75
|
+
if (entries.length === 0) {
|
|
76
|
+
return WA_SQLITE_DEFAULT_TABLE_NAMES;
|
|
77
|
+
}
|
|
78
|
+
const resolved = {
|
|
79
|
+
...WA_SQLITE_DEFAULT_TABLE_NAMES
|
|
80
|
+
};
|
|
81
|
+
for (const [table, rawName] of entries) {
|
|
82
|
+
if (!WA_SQLITE_ALLOWED_TABLE_NAME_SET.has(table)) {
|
|
83
|
+
throw new Error(`unsupported sqlite tableNames key "${table}". Allowed table names: ${WA_SQLITE_ALLOWED_TABLE_NAME_LIST}`);
|
|
84
|
+
}
|
|
85
|
+
if (typeof rawName !== 'string') {
|
|
86
|
+
throw new Error(`sqlite tableNames.${table} must be a string`);
|
|
87
|
+
}
|
|
88
|
+
const tableName = table;
|
|
89
|
+
resolved[tableName] = normalizeTableName(tableName, rawName);
|
|
90
|
+
}
|
|
91
|
+
assertNoDuplicateTableNames(resolved);
|
|
92
|
+
return Object.freeze(resolved);
|
|
93
|
+
}
|
|
94
|
+
export function serializeSqliteTableNames(tableNames) {
|
|
95
|
+
return WA_SQLITE_TABLE_NAME_ORDER.map((table) => `${table}=${tableNames[table]}`).join(';');
|
|
96
|
+
}
|
|
97
|
+
function escapeRegexToken(value) {
|
|
98
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
99
|
+
}
|
|
100
|
+
export function createSqliteTableNameSqlResolver(tableNames) {
|
|
101
|
+
const serialized = serializeSqliteTableNames(tableNames);
|
|
102
|
+
if (serialized === DEFAULT_SQLITE_TABLE_NAME_SERIALIZATION) {
|
|
103
|
+
return (sql) => sql;
|
|
104
|
+
}
|
|
105
|
+
const pattern = new RegExp(`\\b(?:${WA_SQLITE_TABLE_NAME_ORDER.map(escapeRegexToken).join('|')})\\b`, 'g');
|
|
106
|
+
return (sql) => sql.replace(pattern, (token) => tableNames[token] ?? token);
|
|
107
|
+
}
|
|
@@ -20,28 +20,17 @@ export class WaThreadSqliteStore extends BaseSqliteStore {
|
|
|
20
20
|
}
|
|
21
21
|
async upsert(record) {
|
|
22
22
|
const db = await this.getConnection();
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
ephemeral_expiration=COALESCE(excluded.ephemeral_expiration, mailbox_threads.ephemeral_expiration)`, [
|
|
35
|
-
this.options.sessionId,
|
|
36
|
-
record.jid,
|
|
37
|
-
record.name ?? null,
|
|
38
|
-
record.unreadCount ?? null,
|
|
39
|
-
record.archived === undefined ? null : record.archived ? 1 : 0,
|
|
40
|
-
record.pinned ?? null,
|
|
41
|
-
record.muteEndMs ?? null,
|
|
42
|
-
record.markedAsUnread === undefined ? null : record.markedAsUnread ? 1 : 0,
|
|
43
|
-
record.ephemeralExpiration ?? null
|
|
44
|
-
]);
|
|
23
|
+
this.upsertThreadRow(db, record);
|
|
24
|
+
}
|
|
25
|
+
async upsertBatch(records) {
|
|
26
|
+
if (records.length === 0) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
await this.withTransaction((db) => {
|
|
30
|
+
for (const record of records) {
|
|
31
|
+
this.upsertThreadRow(db, record);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
45
34
|
}
|
|
46
35
|
async getByJid(jid) {
|
|
47
36
|
const db = await this.getConnection();
|
|
@@ -69,4 +58,28 @@ export class WaThreadSqliteStore extends BaseSqliteStore {
|
|
|
69
58
|
const db = await this.getConnection();
|
|
70
59
|
db.run('DELETE FROM mailbox_threads WHERE session_id = ?', [this.options.sessionId]);
|
|
71
60
|
}
|
|
61
|
+
upsertThreadRow(db, record) {
|
|
62
|
+
db.run(`INSERT INTO mailbox_threads (
|
|
63
|
+
session_id, jid, name, unread_count, archived, pinned,
|
|
64
|
+
mute_end_ms, marked_as_unread, ephemeral_expiration
|
|
65
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
66
|
+
ON CONFLICT(session_id, jid) DO UPDATE SET
|
|
67
|
+
name=COALESCE(excluded.name, mailbox_threads.name),
|
|
68
|
+
unread_count=COALESCE(excluded.unread_count, mailbox_threads.unread_count),
|
|
69
|
+
archived=COALESCE(excluded.archived, mailbox_threads.archived),
|
|
70
|
+
pinned=COALESCE(excluded.pinned, mailbox_threads.pinned),
|
|
71
|
+
mute_end_ms=COALESCE(excluded.mute_end_ms, mailbox_threads.mute_end_ms),
|
|
72
|
+
marked_as_unread=COALESCE(excluded.marked_as_unread, mailbox_threads.marked_as_unread),
|
|
73
|
+
ephemeral_expiration=COALESCE(excluded.ephemeral_expiration, mailbox_threads.ephemeral_expiration)`, [
|
|
74
|
+
this.options.sessionId,
|
|
75
|
+
record.jid,
|
|
76
|
+
record.name ?? null,
|
|
77
|
+
record.unreadCount ?? null,
|
|
78
|
+
record.archived === undefined ? null : record.archived ? 1 : 0,
|
|
79
|
+
record.pinned ?? null,
|
|
80
|
+
record.muteEndMs ?? null,
|
|
81
|
+
record.markedAsUnread === undefined ? null : record.markedAsUnread ? 1 : 0,
|
|
82
|
+
record.ephemeralExpiration ?? null
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
72
85
|
}
|
|
@@ -3,7 +3,7 @@ import { BoundedTaskQueue, BoundedTaskQueueFullError } from '../infra/perf/Bound
|
|
|
3
3
|
import { WA_DEFAULTS } from '../protocol/constants.js';
|
|
4
4
|
import { WaNoiseSession } from './noise/WaNoiseSession.js';
|
|
5
5
|
import { WaWebSocket } from './WaWebSocket.js';
|
|
6
|
-
import { bytesToBase64UrlSafe } from '../util/
|
|
6
|
+
import { bytesToBase64UrlSafe } from '../util/bytes.js';
|
|
7
7
|
import { EMPTY_BYTES } from '../util/bytes.js';
|
|
8
8
|
import { toError } from '../util/primitives.js';
|
|
9
9
|
const WA_FRAME_HANDLER_QUEUE_MAX_SIZE = 4096;
|
|
@@ -27,6 +27,8 @@ export class WaComms {
|
|
|
27
27
|
url: this.config.url,
|
|
28
28
|
urls: this.config.urls,
|
|
29
29
|
protocols: this.config.protocols,
|
|
30
|
+
dispatcher: this.config.dispatcher,
|
|
31
|
+
agent: this.config.agent,
|
|
30
32
|
timeoutIntervalMs: this.config.timeoutIntervalMs
|
|
31
33
|
}, logger);
|
|
32
34
|
this.socket.setHandlers({
|
|
@@ -144,9 +146,7 @@ export class WaComms {
|
|
|
144
146
|
startHandlingRequests() {
|
|
145
147
|
this.handlingRequests = true;
|
|
146
148
|
this.logger.debug('comms request handling enabled');
|
|
147
|
-
this.frameProcessingQueue = this.frameProcessingQueue
|
|
148
|
-
.catch(() => undefined)
|
|
149
|
-
.then(async () => this.flushPendingFrames());
|
|
149
|
+
this.frameProcessingQueue = this.frameProcessingQueue.then(() => this.flushPendingFrames(), () => this.flushPendingFrames());
|
|
150
150
|
}
|
|
151
151
|
async stopComms() {
|
|
152
152
|
this.logger.info('comms stop requested');
|
|
@@ -272,9 +272,7 @@ export class WaComms {
|
|
|
272
272
|
});
|
|
273
273
|
}
|
|
274
274
|
onSocketMessage(payload) {
|
|
275
|
-
this.frameProcessingQueue = this.frameProcessingQueue
|
|
276
|
-
.catch(() => undefined)
|
|
277
|
-
.then(async () => this.processSocketPayload(payload));
|
|
275
|
+
this.frameProcessingQueue = this.frameProcessingQueue.then(() => this.processSocketPayload(payload), () => this.processSocketPayload(payload));
|
|
278
276
|
}
|
|
279
277
|
async processSocketPayload(payload) {
|
|
280
278
|
if (!this.noiseSession) {
|
|
@@ -423,20 +421,19 @@ export class WaComms {
|
|
|
423
421
|
}
|
|
424
422
|
scheduleDecodedFrame(frame) {
|
|
425
423
|
void this.frameHandlerQueue
|
|
426
|
-
.enqueue(
|
|
424
|
+
.enqueue(() => this.onDecodedFrame(frame))
|
|
427
425
|
.catch((error) => {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
426
|
+
if (error instanceof BoundedTaskQueueFullError) {
|
|
427
|
+
this.logger.warn('frame handler queue is full, resuming socket to preserve bounds', {
|
|
428
|
+
pending: this.frameHandlerQueue.pending(),
|
|
429
|
+
inFlight: this.frameHandlerQueue.inFlight()
|
|
430
|
+
});
|
|
431
|
+
void this.closeSocketAndResume();
|
|
433
432
|
return;
|
|
434
433
|
}
|
|
435
|
-
this.logger.
|
|
436
|
-
|
|
437
|
-
inFlight: this.frameHandlerQueue.inFlight()
|
|
434
|
+
this.logger.error('failed to enqueue decoded frame handler', {
|
|
435
|
+
message: toError(error).message
|
|
438
436
|
});
|
|
439
|
-
void this.onDecodedFrame(frame);
|
|
440
437
|
});
|
|
441
438
|
}
|
|
442
439
|
scheduleReconnect() {
|
|
@@ -460,12 +457,16 @@ export class WaComms {
|
|
|
460
457
|
this.reconnectTimer = null;
|
|
461
458
|
}
|
|
462
459
|
removeWaiter(reject) {
|
|
463
|
-
|
|
464
|
-
|
|
460
|
+
for (let index = 0; index < this.waiters.length; index += 1) {
|
|
461
|
+
const waiter = this.waiters[index];
|
|
462
|
+
if (waiter.reject !== reject) {
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
clearTimeout(waiter.timer);
|
|
466
|
+
this.waiters[index] = this.waiters[this.waiters.length - 1];
|
|
467
|
+
this.waiters.pop();
|
|
465
468
|
return;
|
|
466
469
|
}
|
|
467
|
-
const [waiter] = this.waiters.splice(index, 1);
|
|
468
|
-
clearTimeout(waiter.timer);
|
|
469
470
|
}
|
|
470
471
|
withStickyRoutingCookie(headers) {
|
|
471
472
|
const out = headers ? { ...headers } : {};
|
|
@@ -518,10 +519,11 @@ export class WaComms {
|
|
|
518
519
|
}
|
|
519
520
|
}
|
|
520
521
|
drainWaiters(drain) {
|
|
521
|
-
|
|
522
|
-
|
|
522
|
+
for (let index = this.waiters.length - 1; index >= 0; index -= 1) {
|
|
523
|
+
const waiter = this.waiters[index];
|
|
523
524
|
clearTimeout(waiter.timer);
|
|
524
525
|
drain(waiter);
|
|
525
526
|
}
|
|
527
|
+
this.waiters.length = 0;
|
|
526
528
|
}
|
|
527
529
|
}
|
|
@@ -2,6 +2,45 @@ import { ConsoleLogger } from '../infra/log/ConsoleLogger.js';
|
|
|
2
2
|
import { WA_DEFAULTS, WA_READY_STATES } from '../protocol/constants.js';
|
|
3
3
|
import { TEXT_ENCODER, toBytesView } from '../util/bytes.js';
|
|
4
4
|
import { toError } from '../util/primitives.js';
|
|
5
|
+
const WS_OPTIONAL_MODULE = 'ws';
|
|
6
|
+
function asOptionalNodeWsConstructor(loaded) {
|
|
7
|
+
if (loaded && typeof loaded === 'object') {
|
|
8
|
+
const direct = loaded.WebSocket;
|
|
9
|
+
if (typeof direct === 'function') {
|
|
10
|
+
return direct;
|
|
11
|
+
}
|
|
12
|
+
const fallback = loaded.default;
|
|
13
|
+
if (typeof fallback === 'function') {
|
|
14
|
+
return fallback;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (typeof loaded === 'function') {
|
|
18
|
+
return loaded;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
async function loadOptionalNodeWsConstructor() {
|
|
23
|
+
try {
|
|
24
|
+
const loaded = await import(WS_OPTIONAL_MODULE);
|
|
25
|
+
const constructor = asOptionalNodeWsConstructor(loaded);
|
|
26
|
+
if (constructor) {
|
|
27
|
+
return constructor;
|
|
28
|
+
}
|
|
29
|
+
throw new Error('invalid ws module export');
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const normalized = toError(error);
|
|
33
|
+
const code = normalized.code;
|
|
34
|
+
const message = normalized.message ?? '';
|
|
35
|
+
const isModuleNotFound = (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') &&
|
|
36
|
+
(message.includes(`'${WS_OPTIONAL_MODULE}'`) ||
|
|
37
|
+
message.includes(`"${WS_OPTIONAL_MODULE}"`));
|
|
38
|
+
if (isModuleNotFound) {
|
|
39
|
+
throw new Error('optional dependency "ws" is not installed. Install with: npm i ws');
|
|
40
|
+
}
|
|
41
|
+
throw normalized;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
5
44
|
function resolveWebSocketConstructor() {
|
|
6
45
|
const ctor = globalThis
|
|
7
46
|
.WebSocket;
|
|
@@ -13,12 +52,13 @@ function resolveWebSocketConstructor() {
|
|
|
13
52
|
function resolveSocketUrls(config) {
|
|
14
53
|
const preferredUrls = config.urls;
|
|
15
54
|
if (preferredUrls && preferredUrls.length > 0) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
55
|
+
const unique = [];
|
|
56
|
+
for (const url of preferredUrls)
|
|
57
|
+
if (unique.indexOf(url) === -1)
|
|
58
|
+
unique.push(url);
|
|
59
|
+
return Object.freeze(unique);
|
|
20
60
|
}
|
|
21
|
-
return WA_DEFAULTS.CHAT_SOCKET_URLS;
|
|
61
|
+
return config.url ? Object.freeze([config.url]) : WA_DEFAULTS.CHAT_SOCKET_URLS;
|
|
22
62
|
}
|
|
23
63
|
function resolveSocketRuntime() {
|
|
24
64
|
const maybeNodeProcess = globalThis.process;
|
|
@@ -41,6 +81,7 @@ export class WaWebSocket {
|
|
|
41
81
|
this.handlers = {};
|
|
42
82
|
this.socket = null;
|
|
43
83
|
this.closeWaiter = null;
|
|
84
|
+
this.nodeWsCtorPromise = null;
|
|
44
85
|
}
|
|
45
86
|
setHandlers(handlers) {
|
|
46
87
|
this.handlers = handlers;
|
|
@@ -144,7 +185,7 @@ export class WaWebSocket {
|
|
|
144
185
|
socket.send(data);
|
|
145
186
|
}
|
|
146
187
|
async openSingle(url) {
|
|
147
|
-
const pending = this.createPendingSocket(url);
|
|
188
|
+
const pending = await this.createPendingSocket(url);
|
|
148
189
|
return new Promise((resolve, reject) => {
|
|
149
190
|
this.bindPendingSocket(pending, {
|
|
150
191
|
onOpen: () => {
|
|
@@ -166,7 +207,20 @@ export class WaWebSocket {
|
|
|
166
207
|
});
|
|
167
208
|
}
|
|
168
209
|
async openConcurrently(urls) {
|
|
169
|
-
const
|
|
210
|
+
const setupResults = await Promise.allSettled(urls.map((url) => this.createPendingSocket(url)));
|
|
211
|
+
const pendingSockets = [];
|
|
212
|
+
let setupError = null;
|
|
213
|
+
for (const result of setupResults) {
|
|
214
|
+
if (result.status === 'fulfilled') {
|
|
215
|
+
pendingSockets.push(result.value);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
setupError = setupError ?? toError(result.reason);
|
|
219
|
+
}
|
|
220
|
+
if (setupError) {
|
|
221
|
+
this.releasePendingSocketsAfterSetupFailure(pendingSockets);
|
|
222
|
+
throw setupError;
|
|
223
|
+
}
|
|
170
224
|
return new Promise((resolve, reject) => {
|
|
171
225
|
let done = false;
|
|
172
226
|
let failedCount = 0;
|
|
@@ -210,6 +264,14 @@ export class WaWebSocket {
|
|
|
210
264
|
}
|
|
211
265
|
});
|
|
212
266
|
}
|
|
267
|
+
releasePendingSocketsAfterSetupFailure(entries) {
|
|
268
|
+
for (const entry of entries) {
|
|
269
|
+
if (!this.settlePendingSocket(entry)) {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
this.closeSocketSafe(entry.socket, 1000, 'connect_setup_failed');
|
|
273
|
+
}
|
|
274
|
+
}
|
|
213
275
|
bindRuntimeHandlers(socket) {
|
|
214
276
|
socket.onmessage = (event) => {
|
|
215
277
|
void this.handleMessage(event.data);
|
|
@@ -257,8 +319,8 @@ export class WaWebSocket {
|
|
|
257
319
|
}
|
|
258
320
|
return null;
|
|
259
321
|
}
|
|
260
|
-
createPendingSocket(url) {
|
|
261
|
-
const socket = this.createRawSocket(url);
|
|
322
|
+
async createPendingSocket(url) {
|
|
323
|
+
const socket = await this.createRawSocket(url);
|
|
262
324
|
socket.binaryType = 'arraybuffer';
|
|
263
325
|
this.connectingSockets.add(socket);
|
|
264
326
|
return {
|
|
@@ -351,11 +413,52 @@ export class WaWebSocket {
|
|
|
351
413
|
// no-op
|
|
352
414
|
}
|
|
353
415
|
}
|
|
354
|
-
createRawSocket(url) {
|
|
416
|
+
async createRawSocket(url) {
|
|
355
417
|
const headers = this.config.headers;
|
|
356
|
-
|
|
357
|
-
|
|
418
|
+
const dispatcher = this.config.dispatcher;
|
|
419
|
+
const agent = this.config.agent;
|
|
420
|
+
let hasHeaders = false;
|
|
421
|
+
if (headers) {
|
|
422
|
+
for (const key in headers) {
|
|
423
|
+
if (Object.prototype.hasOwnProperty.call(headers, key)) {
|
|
424
|
+
hasHeaders = true;
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (this.socketRuntime === 'node' && agent) {
|
|
430
|
+
const nodeWsCtor = await this.resolveNodeWsConstructor();
|
|
431
|
+
return new nodeWsCtor(url, this.config.protocols, {
|
|
432
|
+
headers,
|
|
433
|
+
agent
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
if (this.socketRuntime === 'node' && (hasHeaders || dispatcher || agent)) {
|
|
437
|
+
const globalWebSocketCtor = globalThis.WebSocket;
|
|
438
|
+
if (globalWebSocketCtor && this.webSocketCtor === globalWebSocketCtor) {
|
|
439
|
+
const init = {
|
|
440
|
+
protocols: this.config.protocols,
|
|
441
|
+
headers,
|
|
442
|
+
dispatcher,
|
|
443
|
+
agent
|
|
444
|
+
};
|
|
445
|
+
return new this.webSocketCtor(url, init);
|
|
446
|
+
}
|
|
447
|
+
return new this.webSocketCtor(url, this.config.protocols, {
|
|
448
|
+
headers,
|
|
449
|
+
dispatcher,
|
|
450
|
+
agent
|
|
451
|
+
});
|
|
358
452
|
}
|
|
359
453
|
return new this.webSocketCtor(url, this.config.protocols);
|
|
360
454
|
}
|
|
455
|
+
async resolveNodeWsConstructor() {
|
|
456
|
+
if (!this.nodeWsCtorPromise) {
|
|
457
|
+
this.nodeWsCtorPromise = loadOptionalNodeWsConstructor().catch((error) => {
|
|
458
|
+
this.nodeWsCtorPromise = null;
|
|
459
|
+
throw error;
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
return this.nodeWsCtorPromise;
|
|
463
|
+
}
|
|
361
464
|
}
|
|
@@ -238,10 +238,10 @@ function decodeNodeInternal(reader) {
|
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
return
|
|
241
|
+
const node = content === null || content === undefined
|
|
242
|
+
? { tag: tagValue, attrs }
|
|
243
|
+
: { tag: tagValue, attrs, content };
|
|
244
|
+
return node;
|
|
245
245
|
}
|
|
246
246
|
export function decodeBinaryNode(data) {
|
|
247
247
|
const reader = new ByteReader(data);
|