zapo-js 1.1.0 → 1.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.
Files changed (62) hide show
  1. package/README.md +5 -1
  2. package/dist/appstate/sync/WaAppStateSyncClient.d.ts +11 -1
  3. package/dist/appstate/sync/WaAppStateSyncClient.js +36 -15
  4. package/dist/auth/credentials-flow.js +3 -1
  5. package/dist/auth/pairing/WaPairingFlow.js +2 -0
  6. package/dist/client/WaClient.js +26 -20
  7. package/dist/client/WaClientFactory.js +28 -6
  8. package/dist/client/connection/WaConnectionManager.js +3 -0
  9. package/dist/client/coordinators/WaAppStateMutationCoordinator.d.ts +8 -0
  10. package/dist/client/coordinators/WaAppStateMutationCoordinator.js +29 -5
  11. package/dist/client/coordinators/WaIncomingNodeCoordinator.js +1 -1
  12. package/dist/client/coordinators/WaMessageDispatchCoordinator.d.ts +6 -1
  13. package/dist/client/coordinators/WaMessageDispatchCoordinator.js +5 -5
  14. package/dist/client/coordinators/WaPassiveTasksCoordinator.d.ts +6 -1
  15. package/dist/client/coordinators/WaPassiveTasksCoordinator.js +3 -3
  16. package/dist/client/coordinators/WaProfileCoordinator.d.ts +11 -6
  17. package/dist/client/coordinators/WaProfileCoordinator.js +4 -1
  18. package/dist/client/coordinators/WaRetryCoordinator.d.ts +18 -1
  19. package/dist/client/coordinators/WaRetryCoordinator.js +83 -30
  20. package/dist/client/messaging/ignore-key.js +4 -2
  21. package/dist/client/types.d.ts +13 -10
  22. package/dist/esm/appstate/sync/WaAppStateSyncClient.js +36 -15
  23. package/dist/esm/auth/credentials-flow.js +3 -1
  24. package/dist/esm/auth/pairing/WaPairingFlow.js +2 -0
  25. package/dist/esm/client/WaClient.js +26 -20
  26. package/dist/esm/client/WaClientFactory.js +28 -6
  27. package/dist/esm/client/connection/WaConnectionManager.js +3 -0
  28. package/dist/esm/client/coordinators/WaAppStateMutationCoordinator.js +29 -5
  29. package/dist/esm/client/coordinators/WaIncomingNodeCoordinator.js +1 -1
  30. package/dist/esm/client/coordinators/WaMessageDispatchCoordinator.js +6 -6
  31. package/dist/esm/client/coordinators/WaPassiveTasksCoordinator.js +3 -3
  32. package/dist/esm/client/coordinators/WaProfileCoordinator.js +4 -1
  33. package/dist/esm/client/coordinators/WaRetryCoordinator.js +84 -31
  34. package/dist/esm/client/messaging/ignore-key.js +4 -2
  35. package/dist/esm/message/WaMessageClient.js +3 -0
  36. package/dist/esm/message/primitives/incoming.js +20 -5
  37. package/dist/esm/protocol/constants.js +1 -1
  38. package/dist/esm/protocol/message.js +22 -0
  39. package/dist/esm/retry/replay.js +36 -2
  40. package/dist/esm/signal/session/SignalRatchet.js +2 -2
  41. package/dist/esm/transport/WaComms.js +4 -0
  42. package/dist/esm/transport/keepalive/WaKeepAlive.js +4 -0
  43. package/dist/esm/transport/node/WaNodeOrchestrator.js +2 -2
  44. package/dist/esm/transport/node/builders/global.js +3 -0
  45. package/dist/message/WaMessageClient.js +3 -0
  46. package/dist/message/primitives/incoming.d.ts +7 -1
  47. package/dist/message/primitives/incoming.js +20 -5
  48. package/dist/message/types.d.ts +5 -0
  49. package/dist/protocol/constants.d.ts +1 -1
  50. package/dist/protocol/constants.js +3 -2
  51. package/dist/protocol/message.d.ts +22 -0
  52. package/dist/protocol/message.js +23 -1
  53. package/dist/retry/replay.d.ts +12 -0
  54. package/dist/retry/replay.js +36 -2
  55. package/dist/signal/session/SignalRatchet.js +2 -2
  56. package/dist/transport/WaComms.js +4 -0
  57. package/dist/transport/keepalive/WaKeepAlive.js +4 -0
  58. package/dist/transport/node/WaNodeOrchestrator.d.ts +6 -1
  59. package/dist/transport/node/WaNodeOrchestrator.js +2 -2
  60. package/dist/transport/node/builders/global.d.ts +1 -0
  61. package/dist/transport/node/builders/global.js +3 -0
  62. package/package.json +1 -1
@@ -191,6 +191,9 @@ class WaMessageClient {
191
191
  if (input.metaNode) {
192
192
  content.push(input.metaNode);
193
193
  }
194
+ if (input.privacyTokenNode) {
195
+ content.push(input.privacyTokenNode);
196
+ }
194
197
  const node = {
195
198
  tag: constants_1.WA_MESSAGE_TAGS.MESSAGE,
196
199
  attrs,
@@ -16,6 +16,12 @@ interface WaIncomingMessageAckHandlerOptions {
16
16
  readonly emitNewsletterMessageUpdate?: (event: WaIncomingNewsletterMessageUpdateEvent) => void;
17
17
  readonly emitUnhandledStanza?: (event: WaIncomingUnhandledStanzaEvent) => void;
18
18
  }
19
- export declare function buildRecoveredIncomingEvent(webMessageInfo: proto.IWebMessageInfo): WaIncomingMessageEvent;
19
+ /**
20
+ * Rebuilds a `message` event from a recovered {@link proto.IWebMessageInfo}
21
+ * (placeholder resend). `meJid` is the current account user JID, used as the
22
+ * author fallback for self-sent group messages when the proto carries no
23
+ * participant and no `originalSelfAuthorUserJidString`.
24
+ */
25
+ export declare function buildRecoveredIncomingEvent(webMessageInfo: proto.IWebMessageInfo, meJid?: string | null): WaIncomingMessageEvent;
20
26
  export declare function handleIncomingMessageAck(node: BinaryNode, options: WaIncomingMessageAckHandlerOptions): Promise<boolean>;
21
27
  export {};
@@ -73,13 +73,22 @@ function buildIncomingEventRawNode(node) {
73
73
  content: children
74
74
  };
75
75
  }
76
- function buildRecoveredIncomingEvent(webMessageInfo) {
76
+ /**
77
+ * Rebuilds a `message` event from a recovered {@link proto.IWebMessageInfo}
78
+ * (placeholder resend). `meJid` is the current account user JID, used as the
79
+ * author fallback for self-sent group messages when the proto carries no
80
+ * participant and no `originalSelfAuthorUserJidString`.
81
+ */
82
+ function buildRecoveredIncomingEvent(webMessageInfo, meJid) {
77
83
  const key = webMessageInfo.key ?? {};
78
84
  const chatJid = key.remoteJid ?? undefined;
79
85
  const fromMe = key.fromMe === true;
80
- const participant = key.participant ?? undefined;
81
86
  const isGroup = chatJid ? (0, jid_1.isGroupJid)(chatJid) : false;
82
87
  const isBroadcast = chatJid ? (0, jid_1.isBroadcastJid)(chatJid) : false;
88
+ const rawSelfAuthor = webMessageInfo.originalSelfAuthorUserJidString ?? meJid ?? undefined;
89
+ const selfAuthor = fromMe && (isGroup || isBroadcast) && rawSelfAuthor ? (0, jid_1.toUserJid)(rawSelfAuthor) : undefined;
90
+ const participant = webMessageInfo.participant ?? key.participant ?? selfAuthor;
91
+ const pushName = webMessageInfo.pushName ?? undefined;
83
92
  const rawSender = fromMe ? undefined : isGroup || isBroadcast ? participant : chatJid;
84
93
  const sender = rawSender ? (0, jid_1.parseJidFull)(rawSender) : null;
85
94
  const senderDevice = sender?.address.device;
@@ -94,7 +103,8 @@ function buildRecoveredIncomingEvent(webMessageInfo) {
94
103
  attrs: {
95
104
  ...(stanzaId !== undefined ? { id: stanzaId } : {}),
96
105
  ...(chatJid !== undefined ? { from: chatJid } : {}),
97
- ...(participant !== undefined ? { participant } : {})
106
+ ...(participant !== undefined ? { participant } : {}),
107
+ ...(pushName !== undefined ? { notify: pushName } : {})
98
108
  }
99
109
  };
100
110
  return {
@@ -111,6 +121,7 @@ function buildRecoveredIncomingEvent(webMessageInfo) {
111
121
  },
112
122
  timestampSeconds,
113
123
  ...(expirationSeconds !== undefined ? { expirationSeconds } : {}),
124
+ ...(pushName !== undefined ? { pushName } : {}),
114
125
  message
115
126
  };
116
127
  }
@@ -233,7 +244,7 @@ function processMsmsgEncNode(node, encNode, senderJid, options) {
233
244
  encPayload: decoded.encPayload
234
245
  }
235
246
  };
236
- const chatJid = node.attrs.from;
247
+ const chatJid = node.attrs.from ? (0, jid_1.toUserJid)(node.attrs.from) : node.attrs.from;
237
248
  const sender = senderJid ? (0, jid_1.parseJidFull)(senderJid) : null;
238
249
  const isGroup = chatJid ? (0, jid_1.isGroupJid)(chatJid) : false;
239
250
  const isBroadcast = chatJid ? (0, jid_1.isBroadcastJid)(chatJid) : false;
@@ -306,7 +317,11 @@ async function decryptAndProcessEncNode(node, encNode, encType, senderJid, optio
306
317
  }
307
318
  }
308
319
  if (shouldEmitIncomingMessage(message)) {
309
- const chatJid = node.attrs.from;
320
+ // remoteJid is the chat identity, which is deviceless: the device
321
+ // lives in senderDevice (from senderAddress), so strip any `:device`
322
+ // segment the `from` attr carries for 1:1 chats.
323
+ const fromAttr = node.attrs.from;
324
+ const chatJid = fromAttr ? (0, jid_1.toUserJid)(fromAttr) : fromAttr;
310
325
  const isGroup = chatJid ? (0, jid_1.isGroupJid)(chatJid) : false;
311
326
  const isBroadcast = chatJid ? (0, jid_1.isBroadcastJid)(chatJid) : false;
312
327
  const senderUserJid = `${senderAddress.user}@${senderAddress.server}`;
@@ -268,6 +268,11 @@ export interface WaEncryptedMessageInput {
268
268
  readonly participant?: string;
269
269
  readonly deviceFanout?: string;
270
270
  readonly metaNode?: BinaryNode;
271
+ /**
272
+ * Trusted-contact (privacy) token node, appended as a `<tctoken>` child.
273
+ * Recipients that require one nack a token-less send with error 463.
274
+ */
275
+ readonly privacyTokenNode?: BinaryNode;
271
276
  }
272
277
  export interface WaSendReceiptInput {
273
278
  readonly to: string;
@@ -5,7 +5,7 @@ export type { WaConnectionCode, WaConnectionOpenReason, WaDisconnectReason, WaFa
5
5
  export { WA_IQ_TYPES, WA_NODE_TAGS, WA_XMLNS } from './nodes';
6
6
  export { WA_CALL_CHILD_TAGS, WA_CALL_NODE_ATTRS, WA_CALL_PAYLOAD_TAGS, WA_CALL_RECEIPT_PAYLOAD_TAGS } from './call';
7
7
  export type { WaCallPayloadTag } from './call';
8
- export { WA_EDIT_ATTRS, WA_ENC_MEDIA_TYPES, WA_EVENT_META_TYPES, WA_MESSAGE_TAGS, WA_MESSAGE_TYPES, WA_POLL_META_TYPES, WA_RETRYABLE_ACK_CODES, WA_STANZA_MSG_TYPES } from './message';
8
+ export { WA_EDIT_ATTRS, WA_ENC_MEDIA_TYPES, WA_EVENT_META_TYPES, WA_MESSAGE_TAGS, WA_MESSAGE_TYPES, WA_NACK_REASONS, WA_POLL_META_TYPES, WA_RETRYABLE_ACK_CODES, WA_STANZA_MSG_TYPES } from './message';
9
9
  export type { WaOutboundReceiptType } from './message';
10
10
  export { WA_APP_STATE_COLLECTIONS, WA_APP_STATE_COLLECTION_STATES, WA_APP_STATE_ERROR_CODES, WA_APP_STATE_KDF_INFO, WA_APP_STATE_KEY_TYPES, WA_APP_STATE_SYNC_DATA_TYPE } from './appstate';
11
11
  export { getWaMediaHkdfInfo, WA_MEDIA_HKDF_INFO, WA_PREVIEW_MEDIA_HKDF_INFO } from './media';
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WA_PRIVACY_TOKEN_NOTIFICATION_TYPE = exports.WA_PRESENCE_TYPES = exports.WA_PRESENCE_LAST_SENTINELS = exports.WA_CHATSTATE_MEDIA = exports.WA_BUSINESS_HOURS_MODES = exports.WA_BUSINESS_HOURS_DAYS = exports.WA_REGISTRATION_NOTIFICATION_TAGS = exports.WA_NOTIFICATION_TYPES = exports.WA_NEWSLETTER_NOTIFICATION_TAGS = exports.WA_GROUP_NOTIFICATION_TAGS = exports.WA_BUSINESS_NOTIFICATION_TAGS = exports.WA_SUPPORTED_DIRTY_TYPES = exports.WA_DIRTY_TYPES = exports.WA_DIRTY_PROTOCOLS = exports.WA_ACCOUNT_SYNC_PROTOCOLS = exports.WA_PREVIEW_MEDIA_HKDF_INFO = exports.WA_MEDIA_HKDF_INFO = exports.getWaMediaHkdfInfo = exports.WA_APP_STATE_SYNC_DATA_TYPE = exports.WA_APP_STATE_KEY_TYPES = exports.WA_APP_STATE_KDF_INFO = exports.WA_APP_STATE_ERROR_CODES = exports.WA_APP_STATE_COLLECTION_STATES = exports.WA_APP_STATE_COLLECTIONS = exports.WA_STANZA_MSG_TYPES = exports.WA_RETRYABLE_ACK_CODES = exports.WA_POLL_META_TYPES = exports.WA_MESSAGE_TYPES = exports.WA_MESSAGE_TAGS = exports.WA_EVENT_META_TYPES = exports.WA_ENC_MEDIA_TYPES = exports.WA_EDIT_ATTRS = exports.WA_CALL_RECEIPT_PAYLOAD_TAGS = exports.WA_CALL_PAYLOAD_TAGS = exports.WA_CALL_NODE_ATTRS = exports.WA_CALL_CHILD_TAGS = exports.WA_XMLNS = exports.WA_NODE_TAGS = exports.WA_IQ_TYPES = exports.WA_STREAM_SIGNALING = exports.WA_READY_STATES = exports.WA_LOGOUT_REASONS = exports.WA_FAILURE_REASONS = exports.WA_DISCONNECT_REASONS = exports.WA_CONNECTION_REASONS = exports.WA_PAIRING_KDF_INFO = exports.WA_SIGNALING = exports.WA_COMPANION_PLATFORM_IDS = exports.WA_BROWSERS = exports.getWaCompanionPlatformId = void 0;
4
- exports.WA_NEWSLETTER_VIEW_ROLES = exports.WA_NEWSLETTER_VERIFICATION_STATES = exports.WA_NEWSLETTER_STATE_TYPES = exports.WA_NEWSLETTER_SEND_TYPES = exports.WA_NEWSLETTER_ROLES = exports.WA_NEWSLETTER_RECEIVE_TYPES = exports.WA_NEWSLETTER_PICTURE_TYPES = exports.WA_NEWSLETTER_MUTE_VALUES = exports.WA_NEWSLETTER_MUTE_TYPES = exports.WA_NEWSLETTER_FETCH_KEY_TYPES = exports.WA_USYNC_MODES = exports.WA_USYNC_DEFAULTS = exports.WA_USYNC_CONTEXTS = exports.WA_GROUP_PARTICIPANT_TYPES = exports.WA_ABPROPS_REFRESH_BOUNDS = exports.WA_ABPROPS_PROTOCOL_VERSION = exports.resolveAbPropNameByCode = exports.AB_PROP_CONFIGS = exports.WA_EMAIL_XMLNS = exports.WA_EMAIL_TAGS = exports.WA_EMAIL_LIMITS = exports.WA_EMAIL_ERROR_CODES = exports.WA_EMAIL_CONTEXTS = exports.WA_STATUS_DISTRIBUTION_SETTINGS = exports.WA_META_NODE_ATTRS_BOT = exports.WA_BOT_NODE_ATTRS = exports.WA_BOT_MSG_SECRET_BYTES = exports.WA_BOT_MSG_EDIT_TYPES = exports.WA_BOT_MSG_BODY_TYPES = exports.WA_BOT_KNOWN_JIDS = exports.WA_BOT_HKDF_INFO = exports.WA_BOT_DEFAULT_CAPABILITIES = exports.WA_BIZ_BOT_TYPES = exports.WA_DEFAULTS = exports.WA_PRIVACY_VALUES = exports.WA_PRIVACY_TAGS = exports.WA_PRIVACY_SETTING_TO_CATEGORY = exports.WA_PRIVACY_DISALLOWED_LIST_CATEGORIES = exports.WA_PRIVACY_CATEGORY_TO_SETTING = exports.WA_PRIVACY_CATEGORIES = exports.WA_TC_TOKEN_DEFAULTS = exports.WA_PRIVACY_TOKEN_TYPES = exports.WA_PRIVACY_TOKEN_TAGS = void 0;
3
+ exports.WA_PRESENCE_TYPES = exports.WA_PRESENCE_LAST_SENTINELS = exports.WA_CHATSTATE_MEDIA = exports.WA_BUSINESS_HOURS_MODES = exports.WA_BUSINESS_HOURS_DAYS = exports.WA_REGISTRATION_NOTIFICATION_TAGS = exports.WA_NOTIFICATION_TYPES = exports.WA_NEWSLETTER_NOTIFICATION_TAGS = exports.WA_GROUP_NOTIFICATION_TAGS = exports.WA_BUSINESS_NOTIFICATION_TAGS = exports.WA_SUPPORTED_DIRTY_TYPES = exports.WA_DIRTY_TYPES = exports.WA_DIRTY_PROTOCOLS = exports.WA_ACCOUNT_SYNC_PROTOCOLS = exports.WA_PREVIEW_MEDIA_HKDF_INFO = exports.WA_MEDIA_HKDF_INFO = exports.getWaMediaHkdfInfo = exports.WA_APP_STATE_SYNC_DATA_TYPE = exports.WA_APP_STATE_KEY_TYPES = exports.WA_APP_STATE_KDF_INFO = exports.WA_APP_STATE_ERROR_CODES = exports.WA_APP_STATE_COLLECTION_STATES = exports.WA_APP_STATE_COLLECTIONS = exports.WA_STANZA_MSG_TYPES = exports.WA_RETRYABLE_ACK_CODES = exports.WA_POLL_META_TYPES = exports.WA_NACK_REASONS = exports.WA_MESSAGE_TYPES = exports.WA_MESSAGE_TAGS = exports.WA_EVENT_META_TYPES = exports.WA_ENC_MEDIA_TYPES = exports.WA_EDIT_ATTRS = exports.WA_CALL_RECEIPT_PAYLOAD_TAGS = exports.WA_CALL_PAYLOAD_TAGS = exports.WA_CALL_NODE_ATTRS = exports.WA_CALL_CHILD_TAGS = exports.WA_XMLNS = exports.WA_NODE_TAGS = exports.WA_IQ_TYPES = exports.WA_STREAM_SIGNALING = exports.WA_READY_STATES = exports.WA_LOGOUT_REASONS = exports.WA_FAILURE_REASONS = exports.WA_DISCONNECT_REASONS = exports.WA_CONNECTION_REASONS = exports.WA_PAIRING_KDF_INFO = exports.WA_SIGNALING = exports.WA_COMPANION_PLATFORM_IDS = exports.WA_BROWSERS = exports.getWaCompanionPlatformId = void 0;
4
+ exports.WA_NEWSLETTER_VIEW_ROLES = exports.WA_NEWSLETTER_VERIFICATION_STATES = exports.WA_NEWSLETTER_STATE_TYPES = exports.WA_NEWSLETTER_SEND_TYPES = exports.WA_NEWSLETTER_ROLES = exports.WA_NEWSLETTER_RECEIVE_TYPES = exports.WA_NEWSLETTER_PICTURE_TYPES = exports.WA_NEWSLETTER_MUTE_VALUES = exports.WA_NEWSLETTER_MUTE_TYPES = exports.WA_NEWSLETTER_FETCH_KEY_TYPES = exports.WA_USYNC_MODES = exports.WA_USYNC_DEFAULTS = exports.WA_USYNC_CONTEXTS = exports.WA_GROUP_PARTICIPANT_TYPES = exports.WA_ABPROPS_REFRESH_BOUNDS = exports.WA_ABPROPS_PROTOCOL_VERSION = exports.resolveAbPropNameByCode = exports.AB_PROP_CONFIGS = exports.WA_EMAIL_XMLNS = exports.WA_EMAIL_TAGS = exports.WA_EMAIL_LIMITS = exports.WA_EMAIL_ERROR_CODES = exports.WA_EMAIL_CONTEXTS = exports.WA_STATUS_DISTRIBUTION_SETTINGS = exports.WA_META_NODE_ATTRS_BOT = exports.WA_BOT_NODE_ATTRS = exports.WA_BOT_MSG_SECRET_BYTES = exports.WA_BOT_MSG_EDIT_TYPES = exports.WA_BOT_MSG_BODY_TYPES = exports.WA_BOT_KNOWN_JIDS = exports.WA_BOT_HKDF_INFO = exports.WA_BOT_DEFAULT_CAPABILITIES = exports.WA_BIZ_BOT_TYPES = exports.WA_DEFAULTS = exports.WA_PRIVACY_VALUES = exports.WA_PRIVACY_TAGS = exports.WA_PRIVACY_SETTING_TO_CATEGORY = exports.WA_PRIVACY_DISALLOWED_LIST_CATEGORIES = exports.WA_PRIVACY_CATEGORY_TO_SETTING = exports.WA_PRIVACY_CATEGORIES = exports.WA_TC_TOKEN_DEFAULTS = exports.WA_PRIVACY_TOKEN_TYPES = exports.WA_PRIVACY_TOKEN_TAGS = exports.WA_PRIVACY_TOKEN_NOTIFICATION_TYPE = void 0;
5
5
  var browser_1 = require("./browser");
6
6
  Object.defineProperty(exports, "getWaCompanionPlatformId", { enumerable: true, get: function () { return browser_1.getWaCompanionPlatformId; } });
7
7
  Object.defineProperty(exports, "WA_BROWSERS", { enumerable: true, get: function () { return browser_1.WA_BROWSERS; } });
@@ -31,6 +31,7 @@ Object.defineProperty(exports, "WA_ENC_MEDIA_TYPES", { enumerable: true, get: fu
31
31
  Object.defineProperty(exports, "WA_EVENT_META_TYPES", { enumerable: true, get: function () { return message_1.WA_EVENT_META_TYPES; } });
32
32
  Object.defineProperty(exports, "WA_MESSAGE_TAGS", { enumerable: true, get: function () { return message_1.WA_MESSAGE_TAGS; } });
33
33
  Object.defineProperty(exports, "WA_MESSAGE_TYPES", { enumerable: true, get: function () { return message_1.WA_MESSAGE_TYPES; } });
34
+ Object.defineProperty(exports, "WA_NACK_REASONS", { enumerable: true, get: function () { return message_1.WA_NACK_REASONS; } });
34
35
  Object.defineProperty(exports, "WA_POLL_META_TYPES", { enumerable: true, get: function () { return message_1.WA_POLL_META_TYPES; } });
35
36
  Object.defineProperty(exports, "WA_RETRYABLE_ACK_CODES", { enumerable: true, get: function () { return message_1.WA_RETRYABLE_ACK_CODES; } });
36
37
  Object.defineProperty(exports, "WA_STANZA_MSG_TYPES", { enumerable: true, get: function () { return message_1.WA_STANZA_MSG_TYPES; } });
@@ -28,6 +28,28 @@ export declare const WA_MESSAGE_TYPES: Readonly<{
28
28
  }>;
29
29
  export type WaOutboundReceiptType = typeof WA_MESSAGE_TYPES.RECEIPT_TYPE_READ | typeof WA_MESSAGE_TYPES.RECEIPT_TYPE_READ_SELF | typeof WA_MESSAGE_TYPES.RECEIPT_TYPE_PLAYED | typeof WA_MESSAGE_TYPES.RECEIPT_TYPE_PLAYED_SELF | typeof WA_MESSAGE_TYPES.RECEIPT_TYPE_INACTIVE | typeof WA_MESSAGE_TYPES.RECEIPT_TYPE_HISTORY_SYNC;
30
30
  export declare const WA_RETRYABLE_ACK_CODES: readonly ["408", "429", "500", "503"];
31
+ /**
32
+ * Stanza-ack `error` attr codes (nack reasons): sent when nacking a stanza
33
+ * that could not be handled, and matched against the `error` carried by an
34
+ * inbound publish ack (e.g. stale group addressing mode).
35
+ */
36
+ export declare const WA_NACK_REASONS: Readonly<{
37
+ readonly STALE_GROUP_ADDRESSING_MODE: 421;
38
+ readonly NEW_CHAT_MESSAGES_CAPPED: 475;
39
+ readonly PARSING_ERROR: 487;
40
+ readonly UNRECOGNIZED_STANZA: 488;
41
+ readonly UNRECOGNIZED_STANZA_CLASS: 489;
42
+ readonly UNRECOGNIZED_STANZA_TYPE: 490;
43
+ readonly INVALID_PROTOBUF: 491;
44
+ readonly INVALID_HOSTED_COMPANION_STANZA: 493;
45
+ readonly MISSING_MESSAGE_SECRET: 495;
46
+ readonly SIGNAL_ERROR_OLD_COUNTER: 496;
47
+ readonly MESSAGE_DELETED_ON_PEER: 499;
48
+ readonly UNHANDLED_ERROR: 500;
49
+ readonly UNSUPPORTED_ADMIN_REVOKE: 550;
50
+ readonly UNSUPPORTED_LID_GROUP: 551;
51
+ readonly DB_OPERATION_FAILED: 552;
52
+ }>;
31
53
  export declare const WA_STANZA_MSG_TYPES: Readonly<{
32
54
  readonly TEXT: "text";
33
55
  readonly MEDIA: "media";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WA_ENC_MEDIA_TYPES = exports.WA_EVENT_META_TYPES = exports.WA_POLL_META_TYPES = exports.WA_EDIT_ATTRS = exports.WA_STANZA_MSG_TYPES = exports.WA_RETRYABLE_ACK_CODES = exports.WA_MESSAGE_TYPES = exports.WA_MESSAGE_TAGS = void 0;
3
+ exports.WA_ENC_MEDIA_TYPES = exports.WA_EVENT_META_TYPES = exports.WA_POLL_META_TYPES = exports.WA_EDIT_ATTRS = exports.WA_STANZA_MSG_TYPES = exports.WA_NACK_REASONS = exports.WA_RETRYABLE_ACK_CODES = exports.WA_MESSAGE_TYPES = exports.WA_MESSAGE_TAGS = void 0;
4
4
  exports.WA_MESSAGE_TAGS = Object.freeze({
5
5
  MESSAGE: 'message',
6
6
  ENC: 'enc',
@@ -30,6 +30,28 @@ exports.WA_MESSAGE_TYPES = Object.freeze({
30
30
  RECEIPT_TYPE_VIEW: 'view'
31
31
  });
32
32
  exports.WA_RETRYABLE_ACK_CODES = Object.freeze(['408', '429', '500', '503']);
33
+ /**
34
+ * Stanza-ack `error` attr codes (nack reasons): sent when nacking a stanza
35
+ * that could not be handled, and matched against the `error` carried by an
36
+ * inbound publish ack (e.g. stale group addressing mode).
37
+ */
38
+ exports.WA_NACK_REASONS = Object.freeze({
39
+ STALE_GROUP_ADDRESSING_MODE: 421,
40
+ NEW_CHAT_MESSAGES_CAPPED: 475,
41
+ PARSING_ERROR: 487,
42
+ UNRECOGNIZED_STANZA: 488,
43
+ UNRECOGNIZED_STANZA_CLASS: 489,
44
+ UNRECOGNIZED_STANZA_TYPE: 490,
45
+ INVALID_PROTOBUF: 491,
46
+ INVALID_HOSTED_COMPANION_STANZA: 493,
47
+ MISSING_MESSAGE_SECRET: 495,
48
+ SIGNAL_ERROR_OLD_COUNTER: 496,
49
+ MESSAGE_DELETED_ON_PEER: 499,
50
+ UNHANDLED_ERROR: 500,
51
+ UNSUPPORTED_ADMIN_REVOKE: 550,
52
+ UNSUPPORTED_LID_GROUP: 551,
53
+ DB_OPERATION_FAILED: 552
54
+ });
33
55
  exports.WA_STANZA_MSG_TYPES = Object.freeze({
34
56
  TEXT: 'text',
35
57
  MEDIA: 'media',
@@ -5,6 +5,7 @@ import type { WaMessageClient } from '../message/WaMessageClient';
5
5
  import type { WaRetryOutboundMessageRecord } from './types';
6
6
  import type { SignalSessionResolver } from '../signal/session/resolver';
7
7
  import type { SignalProtocol } from '../signal/session/SignalProtocol';
8
+ import type { BinaryNode } from '../transport/types';
8
9
  export interface WaRetryReplayServiceOptions {
9
10
  readonly logger: Logger;
10
11
  readonly messageClient: WaMessageClient;
@@ -12,6 +13,12 @@ export interface WaRetryReplayServiceOptions {
12
13
  readonly sessionResolver: SignalSessionResolver;
13
14
  readonly getCurrentCredentials: () => WaAuthCredentials | null;
14
15
  readonly resolveUserIcdc?: (userJid: string) => Promise<IcdcMeta | null>;
16
+ /**
17
+ * Resolves the trusted-contact (privacy) token node for a recipient user
18
+ * jid. A resend without it is nacked with error 463 by privacy-gated
19
+ * recipients.
20
+ */
21
+ readonly resolvePrivacyTokenNode?: (recipientJid: string) => Promise<BinaryNode | null>;
15
22
  }
16
23
  export type WaRetryResendResult = 'resent' | 'ineligible';
17
24
  /**
@@ -30,6 +37,11 @@ export declare class WaRetryReplayService {
30
37
  resendOutboundMessage(outbound: WaRetryOutboundMessageRecord, requesterJid: string, retryCount: number): Promise<WaRetryResendResult>;
31
38
  private resendPlaintextPayload;
32
39
  private resolveSignedDeviceIdentity;
40
+ /**
41
+ * Resolves the trusted-contact token node for the requester's user jid. A
42
+ * failure (or absent resolver) yields no node and the resend still goes out.
43
+ */
44
+ private resolvePrivacyToken;
33
45
  private refreshRetryPlaintext;
34
46
  private safeUserJid;
35
47
  private resendGroupPlaintextPayload;
@@ -69,6 +69,9 @@ class WaRetryReplayService {
69
69
  const metaNode = (0, jid_1.isHostedDeviceJid)(requesterJid)
70
70
  ? (0, message_1.buildMetaNode)({ sender_intent: 'hosted' })
71
71
  : undefined;
72
+ const privacyTokenNode = requesterIsSelf
73
+ ? undefined
74
+ : await this.resolvePrivacyToken(requesterJid);
72
75
  await this.options.messageClient.sendEncrypted({
73
76
  to: requesterJid,
74
77
  encType: encrypted.type,
@@ -77,7 +80,8 @@ class WaRetryReplayService {
77
80
  id: outbound.messageId,
78
81
  type: payload.type,
79
82
  deviceIdentity,
80
- metaNode
83
+ metaNode,
84
+ privacyTokenNode
81
85
  });
82
86
  return 'resent';
83
87
  }
@@ -91,6 +95,32 @@ class WaRetryReplayService {
91
95
  }
92
96
  return _proto_1.proto.ADVSignedDeviceIdentity.encode(signedIdentity).finish();
93
97
  }
98
+ /**
99
+ * Resolves the trusted-contact token node for the requester's user jid. A
100
+ * failure (or absent resolver) yields no node and the resend still goes out.
101
+ */
102
+ async resolvePrivacyToken(requesterJid) {
103
+ if (!this.options.resolvePrivacyTokenNode) {
104
+ return undefined;
105
+ }
106
+ let recipientUserJid;
107
+ try {
108
+ recipientUserJid = (0, jid_1.toUserJid)(requesterJid);
109
+ }
110
+ catch {
111
+ return undefined;
112
+ }
113
+ try {
114
+ return (await this.options.resolvePrivacyTokenNode(recipientUserJid)) ?? undefined;
115
+ }
116
+ catch (error) {
117
+ this.options.logger.warn('retry resend privacy token resolution failed', {
118
+ to: recipientUserJid,
119
+ message: (0, primitives_1.toError)(error).message
120
+ });
121
+ return undefined;
122
+ }
123
+ }
94
124
  async refreshRetryPlaintext(payload, options) {
95
125
  if (!options.wrapAsDeviceSent && !this.options.resolveUserIcdc) {
96
126
  return null;
@@ -202,6 +232,9 @@ class WaRetryReplayService {
202
232
  const metaNode = (0, jid_1.isHostedDeviceJid)(requesterJid)
203
233
  ? (0, message_1.buildMetaNode)({ sender_intent: 'hosted' })
204
234
  : undefined;
235
+ const privacyTokenNode = this.isRequesterCurrentAccount(requesterJid)
236
+ ? undefined
237
+ : await this.resolvePrivacyToken(requesterJid);
205
238
  await this.options.messageClient.sendEncrypted({
206
239
  to: requesterJid,
207
240
  encType: payload.encType,
@@ -211,7 +244,8 @@ class WaRetryReplayService {
211
244
  type: payload.type,
212
245
  participant: payload.participant,
213
246
  deviceIdentity,
214
- metaNode
247
+ metaNode,
248
+ privacyTokenNode
215
249
  });
216
250
  return 'resent';
217
251
  }
@@ -163,9 +163,9 @@ async function decryptMsg(session, parsed, onPrevSessionDecryptError) {
163
163
  }
164
164
  catch (error) {
165
165
  for (let i = 0; i < session.prevSessions.length; i += 1) {
166
- const decodedPrev = (0, encoding_1.decodeSignalSessionSnapshot)(session.prevSessions[i], `prevSessions[${i}]`);
167
- const prevSession = (0, SignalSession_1.snapshotToRecord)(decodedPrev);
168
166
  try {
167
+ const decodedPrev = (0, encoding_1.decodeSignalSessionSnapshot)(session.prevSessions[i], `prevSessions[${i}]`);
168
+ const prevSession = (0, SignalSession_1.snapshotToRecord)(decodedPrev);
169
169
  const [updatedPrev, plaintext] = await decryptMsgFromSession(prevSession, parsed);
170
170
  const updatedSession = {
171
171
  ...updatedPrev,
@@ -172,6 +172,10 @@ class WaComms {
172
172
  await this.socket.close(1000, 'stop_comms');
173
173
  }
174
174
  async closeSocketAndResume() {
175
+ if (!this.started || this.preventRetry) {
176
+ this.logger.debug('comms resume skipped: comms stopped or retry disabled');
177
+ return;
178
+ }
175
179
  this.logger.debug('comms close socket and resume requested');
176
180
  this.resetConnectionState({
177
181
  started: true,
@@ -104,6 +104,10 @@ class WaKeepAlive {
104
104
  }
105
105
  }
106
106
  catch (error) {
107
+ if (generation !== this.generation) {
108
+ this.logger.trace('keepalive stopped during in-flight ping, not resuming');
109
+ return;
110
+ }
107
111
  this.logger.warn('keepalive ping failed, reconnecting socket', {
108
112
  message: (0, primitives_1.toError)(error).message
109
113
  });
@@ -5,7 +5,12 @@ interface WaNodeOrchestratorOptions {
5
5
  readonly sendNode: (node: BinaryNode) => Promise<void>;
6
6
  readonly defaultTimeoutMs?: number;
7
7
  readonly hostDomain?: string;
8
- readonly mobileIqIdFormat?: boolean;
8
+ /**
9
+ * Resolved lazily on first id-generator creation (post-connect, after
10
+ * credentials load) so a registered mobile session reconnecting without an
11
+ * explicit `mobileTransport` option still picks the mobile id format.
12
+ */
13
+ readonly mobileIqIdFormat?: () => boolean;
9
14
  }
10
15
  /**
11
16
  * Issues outgoing binary nodes and matches incoming responses back to their
@@ -15,7 +15,7 @@ class WaNodeOrchestrator {
15
15
  this.sendNodeFn = options.sendNode;
16
16
  this.defaultTimeoutMs = options.defaultTimeoutMs ?? constants_1.WA_DEFAULTS.NODE_QUERY_TIMEOUT_MS;
17
17
  this.hostDomain = options.hostDomain ?? constants_1.WA_DEFAULTS.HOST_DOMAIN;
18
- this.mobileIqIdFormat = options.mobileIqIdFormat === true;
18
+ this.mobileIqIdFormat = options.mobileIqIdFormat ?? (() => false);
19
19
  this.idGenerator = null;
20
20
  this.idGeneratorReady = null;
21
21
  this.pendingQueries = new Map();
@@ -137,7 +137,7 @@ class WaNodeOrchestrator {
137
137
  if (this.idGenerator) {
138
138
  return this.idGenerator;
139
139
  }
140
- if (this.mobileIqIdFormat) {
140
+ if (this.mobileIqIdFormat()) {
141
141
  this.idGenerator = (0, helpers_1.createMobileNodeIdGenerator)();
142
142
  this.logger.debug('generated stanza prefix (mobile)', {
143
143
  prefix: this.idGenerator.prefix
@@ -15,6 +15,7 @@ export type BuildAckNodeInput = {
15
15
  readonly typeOverride?: string;
16
16
  readonly participant?: string;
17
17
  readonly recipient?: string;
18
+ readonly error?: number | string;
18
19
  } | {
19
20
  readonly kind: 'receipt';
20
21
  readonly node: BinaryNode;
@@ -62,6 +62,9 @@ function buildAckNode(input) {
62
62
  to: input.to,
63
63
  class: _protocol_1.WA_MESSAGE_TYPES.ACK_CLASS_MESSAGE
64
64
  };
65
+ if (input.error !== undefined) {
66
+ attrs.error = String(input.error);
67
+ }
65
68
  const type = input.typeOverride ?? input.node.attrs.type;
66
69
  if (type) {
67
70
  attrs.type = type;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zapo-js",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "High-performance WhatsApp Web TypeScript library",
5
5
  "license": "MIT",
6
6
  "author": "vinikjkkj <contact@vinicius.email> (https://github.com/vinikjkkj)",