zapo-js 1.3.0 → 1.5.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 (113) hide show
  1. package/dist/appstate/types.d.ts +1 -2
  2. package/dist/auth/WaAuthClient.d.ts +25 -0
  3. package/dist/auth/WaAuthClient.js +63 -7
  4. package/dist/auth/credentials-flow.d.ts +8 -0
  5. package/dist/auth/credentials-flow.js +35 -2
  6. package/dist/auth/pairing/WaShortcakeFlow.d.ts +93 -0
  7. package/dist/auth/pairing/WaShortcakeFlow.js +210 -0
  8. package/dist/auth/pairing/companion-host.d.ts +105 -0
  9. package/dist/auth/pairing/companion-host.js +148 -0
  10. package/dist/auth/pairing/pairing-code-crypto.d.ts +1 -0
  11. package/dist/auth/pairing/pairing-code-crypto.js +3 -2
  12. package/dist/auth/pairing/shortcake-crypto.d.ts +59 -0
  13. package/dist/auth/pairing/shortcake-crypto.js +106 -0
  14. package/dist/auth/types.d.ts +20 -9
  15. package/dist/client/WaClient.d.ts +15 -5
  16. package/dist/client/WaClient.js +46 -12
  17. package/dist/client/WaClientFactory.d.ts +3 -0
  18. package/dist/client/WaClientFactory.js +27 -7
  19. package/dist/client/coordinators/WaAppStateMutationCoordinator.d.ts +8 -0
  20. package/dist/client/coordinators/WaAppStateMutationCoordinator.js +37 -1
  21. package/dist/client/coordinators/WaGroupCoordinator.js +6 -1
  22. package/dist/client/coordinators/WaMessageDispatchCoordinator.d.ts +3 -1
  23. package/dist/client/coordinators/WaMessageDispatchCoordinator.js +13 -3
  24. package/dist/client/coordinators/WaMobileCoordinator.d.ts +192 -0
  25. package/dist/client/coordinators/WaMobileCoordinator.js +645 -0
  26. package/dist/client/coordinators/WaRetryCoordinator.js +1 -0
  27. package/dist/client/messaging/companion-host.d.ts +43 -0
  28. package/dist/client/messaging/companion-host.js +61 -0
  29. package/dist/client/messaging/key-protocol.js +25 -1
  30. package/dist/client/persistence/companion-host.d.ts +36 -0
  31. package/dist/client/persistence/companion-host.js +57 -0
  32. package/dist/client/plugins/install.d.ts +2 -1
  33. package/dist/client/plugins/install.js +44 -11
  34. package/dist/client/types.d.ts +90 -7
  35. package/dist/esm/auth/WaAuthClient.js +64 -8
  36. package/dist/esm/auth/credentials-flow.js +36 -3
  37. package/dist/esm/auth/pairing/WaShortcakeFlow.js +206 -0
  38. package/dist/esm/auth/pairing/companion-host.js +140 -0
  39. package/dist/esm/auth/pairing/pairing-code-crypto.js +2 -2
  40. package/dist/esm/auth/pairing/shortcake-crypto.js +99 -0
  41. package/dist/esm/client/WaClient.js +46 -12
  42. package/dist/esm/client/WaClientFactory.js +28 -8
  43. package/dist/esm/client/coordinators/WaAppStateMutationCoordinator.js +37 -1
  44. package/dist/esm/client/coordinators/WaGroupCoordinator.js +6 -1
  45. package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +14 -4
  46. package/dist/esm/client/coordinators/WaMobileCoordinator.js +641 -0
  47. package/dist/esm/client/coordinators/WaRetryCoordinator.js +1 -0
  48. package/dist/esm/client/messaging/companion-host.js +56 -0
  49. package/dist/esm/client/messaging/key-protocol.js +25 -1
  50. package/dist/esm/client/persistence/companion-host.js +54 -0
  51. package/dist/esm/client/plugins/install.js +44 -11
  52. package/dist/esm/index.js +5 -2
  53. package/dist/esm/message/WaMessageClient.js +3 -2
  54. package/dist/esm/message/context-info.js +4 -0
  55. package/dist/esm/protocol/constants.js +4 -3
  56. package/dist/esm/protocol/device-identity.js +18 -0
  57. package/dist/esm/protocol/group.js +6 -0
  58. package/dist/esm/protocol/message.js +12 -0
  59. package/dist/esm/protocol/nodes.js +17 -1
  60. package/dist/esm/protocol/notification.js +3 -1
  61. package/dist/esm/retry/replay.js +7 -15
  62. package/dist/esm/signal/attestation/WaAdvSignature.js +67 -2
  63. package/dist/esm/signal/attestation/constants.js +2 -0
  64. package/dist/esm/signal/index.js +1 -0
  65. package/dist/esm/transport/index.js +1 -1
  66. package/dist/esm/transport/node/builders/group.js +4 -3
  67. package/dist/esm/transport/node/builders/mobile.js +113 -0
  68. package/dist/esm/transport/node/builders/shortcake.js +50 -0
  69. package/dist/esm/transport/noise/WaClientPayload.js +17 -8
  70. package/dist/esm/transport/wa-version-fetcher.js +152 -0
  71. package/dist/esm/util/index.js +1 -0
  72. package/dist/index.d.ts +11 -4
  73. package/dist/index.js +13 -4
  74. package/dist/message/WaMessageClient.js +2 -1
  75. package/dist/message/context-info.d.ts +2 -0
  76. package/dist/message/context-info.js +5 -0
  77. package/dist/protocol/constants.d.ts +6 -4
  78. package/dist/protocol/constants.js +8 -2
  79. package/dist/protocol/device-identity.d.ts +25 -0
  80. package/dist/protocol/device-identity.js +21 -0
  81. package/dist/protocol/group.d.ts +6 -0
  82. package/dist/protocol/group.js +7 -1
  83. package/dist/protocol/message.d.ts +14 -0
  84. package/dist/protocol/message.js +13 -1
  85. package/dist/protocol/nodes.d.ts +16 -0
  86. package/dist/protocol/nodes.js +17 -1
  87. package/dist/protocol/notification.d.ts +2 -0
  88. package/dist/protocol/notification.js +3 -1
  89. package/dist/retry/replay.d.ts +1 -0
  90. package/dist/retry/replay.js +6 -14
  91. package/dist/signal/attestation/WaAdvSignature.d.ts +50 -1
  92. package/dist/signal/attestation/WaAdvSignature.js +71 -1
  93. package/dist/signal/attestation/constants.d.ts +2 -0
  94. package/dist/signal/attestation/constants.js +3 -1
  95. package/dist/signal/index.d.ts +1 -0
  96. package/dist/signal/index.js +13 -1
  97. package/dist/transport/index.d.ts +2 -2
  98. package/dist/transport/index.js +4 -3
  99. package/dist/transport/node/builders/group.js +4 -3
  100. package/dist/transport/node/builders/mobile.d.ts +68 -0
  101. package/dist/transport/node/builders/mobile.js +120 -0
  102. package/dist/transport/node/builders/shortcake.d.ts +19 -0
  103. package/dist/transport/node/builders/shortcake.js +57 -0
  104. package/dist/transport/noise/WaClientPayload.d.ts +2 -0
  105. package/dist/transport/noise/WaClientPayload.js +17 -7
  106. package/dist/transport/wa-version-fetcher.d.ts +92 -0
  107. package/dist/transport/wa-version-fetcher.js +156 -0
  108. package/dist/util/index.d.ts +1 -0
  109. package/dist/util/index.js +3 -1
  110. package/package.json +1 -1
  111. package/dist/esm/transport/wa-web-version-fetcher.js +0 -91
  112. package/dist/transport/wa-web-version-fetcher.d.ts +0 -44
  113. package/dist/transport/wa-web-version-fetcher.js +0 -94
@@ -13,6 +13,7 @@ import { WaIncomingNodeCoordinator } from './coordinators/WaIncomingNodeCoordina
13
13
  import { createLowLevelCoordinator } from './coordinators/WaLowLevelCoordinator.js';
14
14
  import { WaMessageCoordinator } from './coordinators/WaMessageCoordinator.js';
15
15
  import { WaMessageDispatchCoordinator } from './coordinators/WaMessageDispatchCoordinator.js';
16
+ import { WaMobileCoordinator } from './coordinators/WaMobileCoordinator.js';
16
17
  import { createNewsletterCoordinator } from './coordinators/WaNewsletterCoordinator.js';
17
18
  import { WaOfflineResumeCoordinator } from './coordinators/WaOfflineResumeCoordinator.js';
18
19
  import { WaPassiveTasksCoordinator } from './coordinators/WaPassiveTasksCoordinator.js';
@@ -38,7 +39,7 @@ import { processNewsletterLiveUpdates } from '../message/kinds/newsletter.js';
38
39
  import { handleIncomingMessageAck } from '../message/primitives/incoming.js';
39
40
  import { createPeerDataOperationRequester } from '../message/primitives/peer-data-operation.js';
40
41
  import { WaMessageClient } from '../message/WaMessageClient.js';
41
- import { getWaCompanionPlatformId, WA_DEFAULTS, WA_DISCONNECT_REASONS, WA_NEWSLETTER_NOTIFICATION_TAGS, WA_NODE_TAGS, WA_NOTIFICATION_TYPES, WA_PRIVACY_TOKEN_NOTIFICATION_TYPE } from '../protocol/constants.js';
42
+ import { resolveWaDeviceIdentity, WA_DEFAULTS, WA_DISCONNECT_REASONS, WA_NEWSLETTER_NOTIFICATION_TAGS, WA_NODE_TAGS, WA_NOTIFICATION_TYPES, WA_PRIVACY_TOKEN_NOTIFICATION_TYPE } from '../protocol/constants.js';
42
43
  import { isNewsletterJid, isOwnAccountJid, parseSignalAddressFromJid, toUserJid } from '../protocol/jid.js';
43
44
  import { WA_PRESENCE_TYPES } from '../protocol/presence.js';
44
45
  import { createOutboundRetryTracker } from '../retry/tracker.js';
@@ -61,7 +62,6 @@ import { WaNodeTransport } from '../transport/node/WaNodeTransport.js';
61
62
  import { isProxyTransport, toProxyAgent } from '../transport/proxy.js';
62
63
  import { createServerClock } from '../util/clock.js';
63
64
  import { toError } from '../util/primitives.js';
64
- import { getRuntimeOsDisplayName } from '../util/runtime.js';
65
65
  function assertProxyTransport(value, path) {
66
66
  if (value === undefined) {
67
67
  return;
@@ -85,7 +85,7 @@ function validateProxyOptions(options) {
85
85
  }
86
86
  export function resolveWaClientBase(options, logger) {
87
87
  validateProxyOptions(options);
88
- const deviceBrowser = options.deviceBrowser ?? WA_DEFAULTS.DEVICE_BROWSER;
88
+ const device = resolveWaDeviceIdentity(options);
89
89
  const sessionId = options.sessionId.trim();
90
90
  if (sessionId.length === 0) {
91
91
  throw new Error('sessionId must be a non-empty string');
@@ -94,9 +94,9 @@ export function resolveWaClientBase(options, logger) {
94
94
  const normalizedOptions = Object.freeze({
95
95
  ...options,
96
96
  sessionId,
97
- deviceBrowser,
98
- deviceOsDisplayName: options.deviceOsDisplayName ?? getRuntimeOsDisplayName(),
99
- devicePlatform: options.devicePlatform ?? getWaCompanionPlatformId(deviceBrowser),
97
+ deviceBrowser: device.browser,
98
+ deviceOsDisplayName: device.osDisplayName,
99
+ devicePlatform: device.platform,
100
100
  urls: options.urls ?? options.chatSocketUrls ?? WA_DEFAULTS.CHAT_SOCKET_URLS,
101
101
  iqTimeoutMs: options.iqTimeoutMs ?? WA_DEFAULTS.IQ_TIMEOUT_MS,
102
102
  nodeQueryTimeoutMs: options.nodeQueryTimeoutMs ?? WA_DEFAULTS.NODE_QUERY_TIMEOUT_MS,
@@ -348,7 +348,9 @@ export function buildWaClientDependencies(input) {
348
348
  runtime.emitEvent('auth_paired', { credentials });
349
349
  scheduleReconnectAfterPairing();
350
350
  },
351
- onError: (error) => runtime.handleError(error)
351
+ onError: (error) => runtime.handleError(error),
352
+ onPasskeyRequired: (hasSigner) => runtime.emitEvent('auth_passkey_required', { hasSigner }),
353
+ signPasskeyAssertion: options.signPasskeyAssertion
352
354
  }
353
355
  });
354
356
  const getCurrentCredentials = authClient.getCurrentCredentials.bind(authClient);
@@ -449,6 +451,7 @@ export function buildWaClientDependencies(input) {
449
451
  });
450
452
  messageDispatch = new WaMessageDispatchCoordinator({
451
453
  logger,
454
+ emitMessageSend: (event) => runtime.emitEvent('message_send', event),
452
455
  messageClient,
453
456
  retryTracker,
454
457
  sessionResolver,
@@ -585,6 +588,7 @@ export function buildWaClientDependencies(input) {
585
588
  serverClock,
586
589
  emitSnapshotMutations: options.chatEvents?.emitSnapshotMutations === true,
587
590
  emitMutation: (event) => runtime.emitEvent('mutation', event),
591
+ emitMutationSend: (event) => runtime.emitEvent('mutation_send', event),
588
592
  nctSaltSink: (salt) => trustedContactToken.handleNctSaltSync(salt),
589
593
  contactSink: runtime.persistContact,
590
594
  pushNameSink: (name) => {
@@ -1011,6 +1015,20 @@ export function buildWaClientDependencies(input) {
1011
1015
  isConnected: () => connectionManager?.isConnected() ?? false,
1012
1016
  defaultIqTimeoutMs: options.iqTimeoutMs
1013
1017
  });
1018
+ const mobileCoordinator = new WaMobileCoordinator({
1019
+ logger,
1020
+ authClient,
1021
+ messageDispatch,
1022
+ chatCoordinator: appStateMutations,
1023
+ deviceSync: signalDeviceSync,
1024
+ appStateStore: sessionStore.appState,
1025
+ queryWithContext: runtime.queryWithContext,
1026
+ emitEvent: runtime.emitEvent,
1027
+ registerIncomingHandler: (registration) => incomingNode.registerIncomingHandler(registration),
1028
+ isMobilePrimary,
1029
+ persistence: options.companionHost?.persistence,
1030
+ includePem: options.companionHost?.includePem
1031
+ });
1014
1032
  return {
1015
1033
  nodeTransport,
1016
1034
  nodeOrchestrator,
@@ -1051,6 +1069,8 @@ export function buildWaClientDependencies(input) {
1051
1069
  connectionManager,
1052
1070
  trustedContactToken,
1053
1071
  abPropsCoordinator,
1054
- peerDataOperation
1072
+ peerDataOperation,
1073
+ mobileCoordinator,
1074
+ isMobilePrimary
1055
1075
  };
1056
1076
  }
@@ -11,6 +11,8 @@ const WA_APP_STATE_MUTATION_FLUSH_SUCCESS_STATES = new Set([
11
11
  WA_APP_STATE_COLLECTION_STATES.SUCCESS_HAS_MORE
12
12
  ]);
13
13
  const WA_APP_STATE_ARCHIVE_RANGE_DEFAULT_LIMIT = 256;
14
+ /** Placeholder MACs for locally-emitted mutation events (the parser ignores them). */
15
+ const WA_EMPTY_MUTATION_MAC = new Uint8Array(0);
14
16
  function buildMutationIndexFromSchema(schema, indexArgs) {
15
17
  const parts = new Array(schema.indexParts.length);
16
18
  const args = indexArgs;
@@ -157,7 +159,6 @@ function buildRemoveMutationFromSchema(input) {
157
159
  collection: input.schema.collection,
158
160
  operation: 'remove',
159
161
  index: buildMutationIndexFromSchema(input.schema, input.indexArgs),
160
- previousValue: { timestamp: input.timestamp },
161
162
  version: input.schema.version,
162
163
  timestamp: input.timestamp
163
164
  };
@@ -177,6 +178,7 @@ export class WaAppStateMutationCoordinator {
177
178
  this.serverClock = options.serverClock;
178
179
  this.archiveRangeLimit = resolvePositive(options.archiveRangeLimit, WA_APP_STATE_ARCHIVE_RANGE_DEFAULT_LIMIT, 'WaAppStateMutationCoordinatorOptions.archiveRangeLimit');
179
180
  this.emitMutation = options.emitMutation;
181
+ this.emitMutationSend = options.emitMutationSend;
180
182
  this.emitSnapshotMutations = options.emitSnapshotMutations === true;
181
183
  this.nctSaltSink = options.nctSaltSink;
182
184
  this.contactSink = options.contactSink;
@@ -614,9 +616,43 @@ export class WaAppStateMutationCoordinator {
614
616
  async enqueueAndFlush(mutations) {
615
617
  for (const mutation of mutations) {
616
618
  this.enqueueMutation(mutation);
619
+ this.emitLocalMutation(mutation);
617
620
  }
618
621
  await this.flushMutations();
619
622
  }
623
+ /**
624
+ * Surfaces an action this client is sending via the outbound `mutation_send`
625
+ * event, so consumers can observe their own change at action time. Parse
626
+ * failures are swallowed (best-effort).
627
+ */
628
+ emitLocalMutation(input) {
629
+ if (!this.emitMutationSend)
630
+ return;
631
+ const value = input.operation === 'set' ? input.value : null;
632
+ try {
633
+ const event = parseAppStateMutationEvent({
634
+ collection: input.collection,
635
+ operation: input.operation,
636
+ source: 'local',
637
+ index: input.index,
638
+ value,
639
+ version: input.version,
640
+ indexMac: WA_EMPTY_MUTATION_MAC,
641
+ valueMac: WA_EMPTY_MUTATION_MAC,
642
+ keyId: WA_EMPTY_MUTATION_MAC,
643
+ timestamp: input.timestamp
644
+ });
645
+ if (event)
646
+ this.emitMutationSend(event);
647
+ }
648
+ catch (error) {
649
+ this.logger.debug('failed to emit local app-state mutation event', {
650
+ collection: input.collection,
651
+ index: input.index,
652
+ message: toError(error).message
653
+ });
654
+ }
655
+ }
620
656
  enqueueMutation(mutation) {
621
657
  const key = `${mutation.collection}\u0001${mutation.index}`;
622
658
  if (this.pendingMutations.has(key)) {
@@ -2,6 +2,7 @@ import { parseParticipants as parseGroupEventParticipants } from '../events/grou
2
2
  import { WA_DEFAULTS } from '../../protocol/defaults.js';
3
3
  import { WA_GROUP_PARTICIPANT_TYPES } from '../../protocol/group.js';
4
4
  import { parseJidFull } from '../../protocol/jid.js';
5
+ import { WA_ADDRESSING_MODES } from '../../protocol/message.js';
5
6
  import { WA_IQ_TYPES, WA_NODE_TAGS, WA_XMLNS } from '../../protocol/nodes.js';
6
7
  import { WA_GROUP_NOTIFICATION_TAGS } from '../../protocol/notification.js';
7
8
  import { buildListParticipatingGroupsIq } from '../../transport/node/builders/account-sync.js';
@@ -87,7 +88,11 @@ function parseGroupCommons(target) {
87
88
  ? findNodeChild(membershipApprovalNode, WA_NODE_TAGS.GROUP_JOIN)
88
89
  : undefined;
89
90
  const addressingModeRaw = attrs.addressing_mode;
90
- const addressingMode = addressingModeRaw === 'lid' ? 'lid' : addressingModeRaw === 'pn' ? 'pn' : undefined;
91
+ const addressingMode = addressingModeRaw === WA_ADDRESSING_MODES.LID
92
+ ? WA_ADDRESSING_MODES.LID
93
+ : addressingModeRaw === WA_ADDRESSING_MODES.PN
94
+ ? WA_ADDRESSING_MODES.PN
95
+ : undefined;
91
96
  const rawJid = attrs.id ?? attrs.jid ?? '';
92
97
  const jid = rawJid && !rawJid.includes('@') ? `${rawJid}@${WA_DEFAULTS.GROUP_SERVER}` : rawJid;
93
98
  return {
@@ -11,7 +11,7 @@ import { wrapDeviceSentMessage } from '../../message/encode/device-sent.js';
11
11
  import { writeRandomPadMax16 } from '../../message/encode/padding.js';
12
12
  import { buildBotInvokeProtoCopy, extractInvokedBotJid, genBotMsgSecret } from '../../message/kinds/bot.js';
13
13
  import { proto } from '../../proto.js';
14
- import { WA_DEFAULTS, WA_NACK_REASONS } from '../../protocol/constants.js';
14
+ import { WA_ADDRESSING_MODES, WA_DEFAULTS, WA_NACK_REASONS } from '../../protocol/constants.js';
15
15
  import { canonicalizeOwnAccountJid, isBotJid, isGroupJid, isHostedDeviceJid, isLidJid, isNewsletterJid, isUserJid, normalizeDeviceJid, normalizeRecipientJid, parseJidFull, parseSignalAddressFromJid, signalAddressKey, toUserJid } from '../../protocol/jid.js';
16
16
  import { encodeBinaryNode } from '../../transport/binary/index.js';
17
17
  import { buildButtonAddonNode, buildDirectMessageFanoutNode, buildGroupSenderKeyMessageNode, buildMetaNode } from '../../transport/node/builders/message.js';
@@ -200,6 +200,16 @@ export class WaMessageDispatchCoordinator {
200
200
  }
201
201
  : withViewOnce;
202
202
  const upload = built.upload;
203
+ if (this.deps.emitMessageSend) {
204
+ try {
205
+ this.deps.emitMessageSend({ to: recipientJid, id: sendOptions.id, message });
206
+ }
207
+ catch (error) {
208
+ this.deps.logger.debug('message_send emit failed', {
209
+ message: toError(error).message
210
+ });
211
+ }
212
+ }
203
213
  const messageWithOverride = options.messageSecret
204
214
  ? {
205
215
  ...message,
@@ -891,17 +901,17 @@ export class WaMessageDispatchCoordinator {
891
901
  resolveGroupAddressingMode(participantUserJids, groupJid) {
892
902
  for (let index = 0; index < participantUserJids.length; index += 1) {
893
903
  if (isLidJid(participantUserJids[index])) {
894
- return 'lid';
904
+ return WA_ADDRESSING_MODES.LID;
895
905
  }
896
906
  }
897
907
  this.deps.logger.trace('group addressing mode resolved to pn (default)', {
898
908
  groupJid,
899
909
  participants: participantUserJids.length
900
910
  });
901
- return 'pn';
911
+ return WA_ADDRESSING_MODES.PN;
902
912
  }
903
913
  resolveSenderForAddressingMode(addressingMode, meJid) {
904
- if (addressingMode === 'lid') {
914
+ if (addressingMode === WA_ADDRESSING_MODES.LID) {
905
915
  const meLid = this.deps.getCurrentCredentials()?.meLid;
906
916
  if (meLid && meLid.includes('@')) {
907
917
  try {