@oxidezap/baileyrs 0.1.0 → 0.1.1

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.
@@ -813,7 +813,7 @@ const dispatchCanonicalEvent = (canonical, dispatchCtx) => {
813
813
  }
814
814
  catch (err) {
815
815
  // One bad event must not poison the rest of the pipeline.
816
- dispatchCtx.ctx.logger.error({ err, type: canonical.type }, 'dispatcher threw — dropping event');
816
+ dispatchCtx.ctx.reportUnexpectedError(err, `dispatching a '${canonical.type}' event`);
817
817
  }
818
818
  };
819
819
  /** Aggregate adjacent ordinary messages while preserving every side-effecting
@@ -852,7 +852,7 @@ const dispatchCanonicalBatch = (ctx, dispatchCtx, count, canonicalAt) => {
852
852
  }
853
853
  }
854
854
  catch (err) {
855
- ctx.logger.error({ err, type: CANONICAL_MESSAGE_EVENT }, 'dispatcher threw — dropping event');
855
+ ctx.reportUnexpectedError(err, `dispatching a '${CANONICAL_MESSAGE_EVENT}' event`);
856
856
  }
857
857
  }
858
858
  if (pending)
@@ -884,7 +884,7 @@ export const makeEventHandlers = (ctx, callbacks) => {
884
884
  view = decodeMessageWireBatch(batch);
885
885
  }
886
886
  catch (err) {
887
- ctx.logger.error({ err }, 'failed to decode the message wire batch');
887
+ ctx.reportUnexpectedError(err, 'decoding the message wire batch');
888
888
  return;
889
889
  }
890
890
  const { messageData, messageOffsets, infos } = view;
@@ -923,7 +923,7 @@ export const makeEventHandlers = (ctx, callbacks) => {
923
923
  receipts = decodeReceiptWireBatch(batch);
924
924
  }
925
925
  catch (err) {
926
- ctx.logger.error({ err }, 'failed to decode receipt wire batch');
926
+ ctx.reportUnexpectedError(err, 'decoding the receipt wire batch');
927
927
  return;
928
928
  }
929
929
  // The decoded payload matches the single-event wire shape; the union's
@@ -937,7 +937,7 @@ export const makeEventHandlers = (ctx, callbacks) => {
937
937
  acks = decodeServerAckWireBatch(batch);
938
938
  }
939
939
  catch (err) {
940
- ctx.logger.error({ err }, 'failed to decode server-ack wire batch');
940
+ ctx.reportUnexpectedError(err, 'decoding the server-ack wire batch');
941
941
  return;
942
942
  }
943
943
  for (const data of acks)
@@ -2,7 +2,7 @@ import { Buffer } from 'node:buffer';
2
2
  import { type UploadMediaResult } from '@oxidezap/whatsapp-rust-bridge';
3
3
  import { WebSocketClient } from '../Compatibility/websocket-client.js';
4
4
  import { type MediaType } from '../Defaults/index.js';
5
- import type { BinaryNode, AuthenticationCreds, ConnectionState, Contact, ReachoutTimelockState, SignalKeyStoreWithTransaction, UserFacingSocketConfig, WAPrivacyGroupAddValue, WAPrivacyOnlineValue, WAPrivacyValue, WABusinessProfile, WAReadReceiptsValue, WAMessage, WAMessageKey } from '../Types/index.js';
5
+ import type { BinaryNode, AuthenticationCreds, ConnectionState, Contact, ReachoutTimelockState, SignalKeyStoreWithTransaction, UserFacingSocketConfig, WABusinessProfile, WAMessage, WAMessageKey } from '../Types/index.js';
6
6
  import type Long from 'long';
7
7
  import type { MediaDownloadOptions } from '../Utils/messages-media.js';
8
8
  import type { proto } from '../WAProto/runtime.js';
@@ -12,12 +12,33 @@ declare const makeWASocket: (config: UserFacingSocketConfig) => {
12
12
  sendRetryRequest: (node: BinaryNode, forceIncludeKeys?: boolean) => Promise<void>;
13
13
  updateBlockStatus: (jid: string, action: 'block' | 'unblock') => Promise<void>;
14
14
  fetchBlocklist: () => Promise<Array<string | undefined>>;
15
+ getCatalog: ({ jid, limit, cursor }: import("../index.js").GetCatalogOptions) => Promise<import("../index.js").CatalogPage>;
16
+ getCollections: (jid?: string, limit?: number) => Promise<import("../index.js").CollectionsPage>;
17
+ getOrderDetails: (orderId: string, tokenBase64: string, sellerJid?: string) => Promise<import("@oxidezap/whatsapp-rust-bridge").OrderResult>;
18
+ updateBussinesProfile: (args: import("../Types/Bussines.js").UpdateBussinesProfileProps) => Promise<void>;
19
+ updateCoverPhoto: (photo: import("../index.js").WAMediaUpload) => Promise<never>;
20
+ removeCoverPhoto: (id: string) => Promise<void>;
21
+ productCreate: (create: import("../index.js").ProductCreate) => Promise<never>;
22
+ productUpdate: (productId: string, update: import("../index.js").ProductUpdate) => Promise<never>;
23
+ productDelete: (productIds: string[]) => Promise<never>;
15
24
  pinChat: (jid: string, pin: boolean) => Promise<void>;
16
25
  muteChat: (jid: string, muteUntil?: number | null) => Promise<void>;
17
26
  archiveChat: (jid: string, archive: boolean) => Promise<void>;
18
27
  starMessage: (jid: string, messageId: string, star: boolean) => Promise<void>;
19
28
  chatModify: (mod: import("../index.js").ChatModification, jid: string) => Promise<void>;
20
- resyncAppState: (_collections?: readonly import("../index.js").WAPatchName[], _isInitialSync?: boolean) => Promise<void>;
29
+ addOrEditContact: (jid: string, contact: proto.SyncActionValue.IContactAction) => Promise<void>;
30
+ removeContact: (jid: string) => Promise<void>;
31
+ addLabel: (jid: string, labels: import("../Types/Label.js").LabelActionBody) => Promise<void>;
32
+ addChatLabel: (jid: string, labelId: string) => Promise<void>;
33
+ removeChatLabel: (jid: string, labelId: string) => Promise<void>;
34
+ addMessageLabel: (jid: string, messageId: string, labelId: string) => Promise<void>;
35
+ removeMessageLabel: (jid: string, messageId: string, labelId: string) => Promise<void>;
36
+ star: (jid: string, messages: {
37
+ id: string;
38
+ fromMe?: boolean;
39
+ }[], star: boolean) => Promise<void>;
40
+ addOrEditQuickReply: (quickReply: import("../Types/Bussines.js").QuickReplyAction) => Promise<void>;
41
+ removeQuickReply: (timestamp: string) => Promise<void>;
21
42
  onWhatsApp: (...phoneNumber: string[]) => Promise<import("./contacts.js").OnWhatsAppResult[] | undefined>;
22
43
  profilePictureUrl: (jid: string, type?: 'preview' | 'image', timeoutMs?: number) => Promise<string | undefined>;
23
44
  fetchUserInfo: (...jids: string[]) => Promise<Record<string, import("@oxidezap/whatsapp-rust-bridge").UserInfoResult>>;
@@ -74,6 +95,14 @@ declare const makeWASocket: (config: UserFacingSocketConfig) => {
74
95
  communityMemberAddMode: (jid: string, mode: 'admin_add' | 'all_member_add') => Promise<void>;
75
96
  communityJoinApprovalMode: (jid: string, mode: 'on' | 'off') => Promise<void>;
76
97
  communityFetchAllParticipating: () => Promise<Record<string, import("../index.js").GroupMetadata>>;
98
+ waitForSocketOpen: () => Promise<void>;
99
+ upsertMessage: (msg: WAMessage, type: import("../index.js").MessageUpsertType) => Promise<void>;
100
+ onUnexpectedError: (err: Error, msg: string) => void;
101
+ resyncAppState: (collections?: readonly ('critical_block' | 'critical_unblock_low' | 'regular_high' | 'regular_low' | 'regular')[], isInitialSync?: boolean) => Promise<void>;
102
+ updateServerTimeOffset: (node: BinaryNode) => never;
103
+ sendUnifiedSession: () => Promise<never>;
104
+ appPatch: (patchCreate: import("../index.js").WAPatchCreate) => Promise<never>;
105
+ messageRetryManager: null;
77
106
  sendMessage: (jid: string, content: import("../index.js").AnyMessageContent, options?: Omit<import("../index.js").MessageGenerationOptions, 'waClient' | 'logger' | 'userJid' | 'mediaInNote'>) => Promise<WAMessage>;
78
107
  updateMediaMessage: (message: WAMessage) => Promise<WAMessage>;
79
108
  relayMessage: (jid: string, message: proto.IMessage, options: import("../index.js").MessageRelayOptions) => Promise<string>;
@@ -81,12 +110,31 @@ declare const makeWASocket: (config: UserFacingSocketConfig) => {
81
110
  sendReceipt: (jid: string, participant: string | undefined, messageIds: string[], type: import("../index.js").MessageReceiptType) => Promise<void>;
82
111
  sendReceipts: (keys: WAMessageKey[], type: import("../index.js").MessageReceiptType) => Promise<void>;
83
112
  requestPlaceholderResend: (messageKey: WAMessageKey, msgData?: Partial<WAMessage>) => Promise<string | undefined>;
84
- newsletterCreate: (name: string, description?: string) => Promise<import("@oxidezap/whatsapp-rust-bridge").NewsletterMetadataResult>;
85
- newsletterMetadata: (jid: string) => Promise<import("@oxidezap/whatsapp-rust-bridge").NewsletterMetadataResult>;
113
+ newsletterCreate: (name: string, description?: string) => Promise<import("../index.js").NewsletterMetadata>;
114
+ newsletterMetadata: (type: 'invite' | 'jid', key: string) => Promise<import("../index.js").NewsletterMetadata | null>;
115
+ newsletterUpdate: (jid: string, updates: import("../index.js").NewsletterUpdate) => Promise<import("../index.js").NewsletterMetadata>;
116
+ newsletterUpdateName: (jid: string, name: string) => Promise<import("../index.js").NewsletterMetadata>;
117
+ newsletterUpdateDescription: (jid: string, description: string) => Promise<import("../index.js").NewsletterMetadata>;
118
+ newsletterUpdatePicture: (jid: string, content: import("../index.js").WAMediaUpload) => Promise<import("../index.js").NewsletterMetadata>;
119
+ newsletterRemovePicture: (jid: string) => Promise<import("../index.js").NewsletterMetadata>;
120
+ newsletterFollow: (jid: string) => Promise<import("../index.js").NewsletterMetadata>;
121
+ newsletterUnfollow: (jid: string) => Promise<void>;
86
122
  newsletterSubscribe: (jid: string) => Promise<import("@oxidezap/whatsapp-rust-bridge").NewsletterMetadataResult>;
87
123
  newsletterUnsubscribe: (jid: string) => Promise<void>;
124
+ newsletterMute: (jid: string) => Promise<void>;
125
+ newsletterUnmute: (jid: string) => Promise<void>;
126
+ newsletterSubscribers: (jid: string) => Promise<{
127
+ subscribers: number;
128
+ }>;
88
129
  newsletterReactMessage: (jid: string, serverId: string, reaction?: string) => Promise<void>;
89
- newsletterMute: (jid: string, mute: boolean) => Promise<void>;
130
+ newsletterFetchMessages: (jid: string, count: number, since?: number, after?: number) => Promise<import("@oxidezap/whatsapp-rust-bridge").NewsletterMessageResult[]>;
131
+ subscribeNewsletterUpdates: (jid: string) => Promise<{
132
+ duration: string;
133
+ }>;
134
+ newsletterAdminCount: (jid: string) => Promise<number>;
135
+ newsletterChangeOwner: (jid: string, newOwnerJid: string) => Promise<void>;
136
+ newsletterDemote: (jid: string, userJid: string) => Promise<void>;
137
+ newsletterDelete: (jid: string) => Promise<void>;
90
138
  uploadPreKeys: (count?: number) => Promise<void>;
91
139
  uploadPreKeysToServerIfRequired: () => Promise<void>;
92
140
  digestKeyBundle: () => Promise<void>;
@@ -94,6 +142,30 @@ declare const makeWASocket: (config: UserFacingSocketConfig) => {
94
142
  sendPresence: (status: 'available' | 'unavailable') => Promise<void>;
95
143
  presenceSubscribe: (toJid: string) => Promise<void>;
96
144
  sendChatState: (jid: string, state: 'composing' | 'recording' | 'paused') => Promise<void>;
145
+ fetchPrivacySettings: (force?: boolean) => Promise<any>;
146
+ updatePrivacySetting: (category: string, value: string) => Promise<void>;
147
+ updateLastSeenPrivacy: (value: import("../index.js").WAPrivacyValue) => Promise<void>;
148
+ updateOnlinePrivacy: (value: import("../index.js").WAPrivacyOnlineValue) => Promise<void>;
149
+ updateProfilePicturePrivacy: (value: import("../index.js").WAPrivacyValue) => Promise<void>;
150
+ updateStatusPrivacy: (value: import("../index.js").WAPrivacyValue) => Promise<void>;
151
+ updateReadReceiptsPrivacy: (value: import("../index.js").WAReadReceiptsValue) => Promise<void>;
152
+ updateGroupsAddPrivacy: (value: import("../index.js").WAPrivacyGroupAddValue) => Promise<void>;
153
+ updateCallPrivacy: (value: import("../index.js").WAPrivacyCallValue) => Promise<void>;
154
+ updateMessagesPrivacy: (value: import("../index.js").WAPrivacyMessagesValue) => Promise<void>;
155
+ issuePrivacyTokens: (jids: string[], timestamp?: number) => Promise<void>;
156
+ refreshMediaConn: (forceGet?: boolean) => Promise<Omit<import("../index.js").MediaConnInfo, 'hosts'> & {
157
+ hosts: {
158
+ hostname: string;
159
+ }[];
160
+ }>;
161
+ getMediaHost: () => string;
162
+ getBotListV2: () => Promise<import("../index.js").BotListInfo[]>;
163
+ fetchNewChatMessageCap: () => Promise<import("../index.js").NewChatMessageCapInfo & {
164
+ remaining_quota?: number;
165
+ }>;
166
+ cleanDirtyBits: (type: 'account_sync' | 'groups', fromTimestamp?: number | string) => Promise<void>;
167
+ sendPeerDataOperationMessage: (_pdoMessage: unknown) => Promise<never>;
168
+ createCallLink: (_type: 'audio' | 'video', _event?: unknown, _timeoutMs?: number) => Promise<never>;
97
169
  requestPairingCode: (phoneNumber: string, customPairingCode?: string) => Promise<string>;
98
170
  setPushName: (name: string) => Promise<void>;
99
171
  updateProfileName: (name: string) => Promise<void>;
@@ -176,7 +248,14 @@ declare const makeWASocket: (config: UserFacingSocketConfig) => {
176
248
  waitForMessage: <T>(msgId: string, timeoutMs?: number | undefined) => Promise<T | undefined>;
177
249
  query: (node: BinaryNode, timeoutMs?: number) => Promise<BinaryNode>;
178
250
  sendRawMessage: (data: Uint8Array | Buffer) => Promise<void>;
179
- createParticipantNodes: (jids: string[], message: proto.IMessage, extraAttrs?: BinaryNode['attrs']) => Promise<{
251
+ /**
252
+ * `dsmMessage` is accepted so the signature matches upstream, and
253
+ * refused rather than ignored. Upstream uses it to encrypt a different
254
+ * plaintext for the caller's own other devices; the engine encrypts one
255
+ * payload for every recipient, so honouring it is not possible here and
256
+ * dropping it would send those devices the wrong message.
257
+ */
258
+ createParticipantNodes: (jids: string[], message: proto.IMessage, extraAttrs?: BinaryNode['attrs'], dsmMessage?: proto.IMessage) => Promise<{
180
259
  nodes: BinaryNode[];
181
260
  shouldIncludeDeviceIdentity: boolean;
182
261
  }>;
@@ -203,14 +282,6 @@ declare const makeWASocket: (config: UserFacingSocketConfig) => {
203
282
  waUploadToServer: (data: Uint8Array | Buffer, opts: {
204
283
  mediaType: MediaType;
205
284
  }) => Promise<UploadMediaResult>;
206
- fetchPrivacySettings: (force?: boolean) => Promise<any>;
207
- updatePrivacySetting: (category: string, value: string) => Promise<void>;
208
- updateLastSeenPrivacy: (value: WAPrivacyValue) => Promise<void>;
209
- updateOnlinePrivacy: (value: WAPrivacyOnlineValue) => Promise<void>;
210
- updateProfilePicturePrivacy: (value: WAPrivacyValue) => Promise<void>;
211
- updateStatusPrivacy: (value: WAPrivacyValue) => Promise<void>;
212
- updateReadReceiptsPrivacy: (value: WAReadReceiptsValue) => Promise<void>;
213
- updateGroupsAddPrivacy: (value: WAPrivacyGroupAddValue) => Promise<void>;
214
285
  updateDefaultDisappearingMode: (duration: number) => Promise<void>;
215
286
  rejectCall: (callId: string, callFrom: string) => Promise<void>;
216
287
  /**
@@ -21,6 +21,7 @@ import { makeNativeCryptoProvider } from '../Utils/native-crypto-provider.js';
21
21
  import { wrapLegacyStore } from '../Utils/wrap-legacy-store.js';
22
22
  import { assertNodeErrorFree } from '../WABinary/generic-utils.js';
23
23
  import { makeBlockingMethods } from './blocking.js';
24
+ import { makeBusinessMethods } from './business.js';
24
25
  import { makeChatActionMethods } from './chat-actions.js';
25
26
  import { makeContactMethods } from './contacts.js';
26
27
  import { makeCommunityMethods } from './communities.js';
@@ -28,10 +29,13 @@ import { makeBridgeClientOwner } from './bridge-client-owner.js';
28
29
  import { makeTerminalCloseReporter } from './terminal-close-reporter.js';
29
30
  import { makeEventHandlers } from './events.js';
30
31
  import { makeGroupMethods } from './groups.js';
32
+ import { makeInternalMethods, makeUnexpectedErrorReporter } from './internals.js';
31
33
  import { makeMessageMethods } from './messages.js';
32
34
  import { makeNewsletterMethods } from './newsletter.js';
33
35
  import { makePreKeyMethods } from './prekeys.js';
34
36
  import { makePresenceMethods } from './presence.js';
37
+ import { makePrivacyMethods } from './privacy.js';
38
+ import { makeServerQueryMethods } from './server-queries.js';
35
39
  import { makeProfileMethods } from './profile.js';
36
40
  import { mapReachoutTimelock } from './reachout.js';
37
41
  import { makeHttpClient, makeTransport } from './transport.js';
@@ -249,11 +253,18 @@ const makeWASocket = (config) => {
249
253
  let autoReconnectEnabled = true;
250
254
  /** Owns reporting the terminal close: once, after teardown, never not at all. */
251
255
  const terminalClose = makeTerminalCloseReporter({ logger });
256
+ /**
257
+ * Held in a reporter rather than captured, because `sock.onUnexpectedError`
258
+ * is an assignable property: a consumer that replaces it has to be the one
259
+ * the socket's own failure paths reach afterwards.
260
+ */
261
+ const unexpectedErrors = makeUnexpectedErrorReporter(logger);
252
262
  const ctx = {
253
263
  ev,
254
264
  logger,
255
265
  fullConfig,
256
266
  ws,
267
+ reportUnexpectedError: unexpectedErrors.report,
257
268
  getUser: () => user,
258
269
  getMe: () => {
259
270
  const me = auth.creds.me;
@@ -783,7 +794,17 @@ const makeWASocket = (config) => {
783
794
  sendRawMessage: async (data) => {
784
795
  return (await ctx.getClient()).sendRawMessage(data instanceof Uint8Array ? data : new Uint8Array(data));
785
796
  },
786
- createParticipantNodes: async (jids, message, extraAttrs) => {
797
+ /**
798
+ * `dsmMessage` is accepted so the signature matches upstream, and
799
+ * refused rather than ignored. Upstream uses it to encrypt a different
800
+ * plaintext for the caller's own other devices; the engine encrypts one
801
+ * payload for every recipient, so honouring it is not possible here and
802
+ * dropping it would send those devices the wrong message.
803
+ */
804
+ createParticipantNodes: async (jids, message, extraAttrs, dsmMessage) => {
805
+ if (dsmMessage) {
806
+ throw new Boom('createParticipantNodes: dsmMessage is not supported, the engine encrypts one payload for every recipient and cannot substitute a different one for your own devices', { statusCode: 501 });
807
+ }
787
808
  const bytes = encodeProto('Message', message);
788
809
  return (await ctx.getClient()).createParticipantNodesBytes(jids, bytes, extraAttrs ?? {});
789
810
  },
@@ -826,31 +847,7 @@ const makeWASocket = (config) => {
826
847
  const bytes = data instanceof Uint8Array && !Buffer.isBuffer(data) ? data : new Uint8Array(data);
827
848
  return (await ctx.getClient()).uploadMedia(bytes, toBridgeMediaType(opts.mediaType));
828
849
  },
829
- fetchPrivacySettings: async (force) => {
830
- void force;
831
- return (await ctx.getClient()).fetchPrivacySettings();
832
- },
833
- updatePrivacySetting: async (category, value) => {
834
- await (await ctx.getClient()).updatePrivacySetting(category, value);
835
- },
836
- updateLastSeenPrivacy: async (value) => {
837
- await (await ctx.getClient()).updatePrivacySetting('last', value);
838
- },
839
- updateOnlinePrivacy: async (value) => {
840
- await (await ctx.getClient()).updatePrivacySetting('online', value);
841
- },
842
- updateProfilePicturePrivacy: async (value) => {
843
- await (await ctx.getClient()).updatePrivacySetting('profile', value);
844
- },
845
- updateStatusPrivacy: async (value) => {
846
- await (await ctx.getClient()).updatePrivacySetting('status', value);
847
- },
848
- updateReadReceiptsPrivacy: async (value) => {
849
- await (await ctx.getClient()).updatePrivacySetting('readreceipts', value);
850
- },
851
- updateGroupsAddPrivacy: async (value) => {
852
- await (await ctx.getClient()).updatePrivacySetting('groupadd', value);
853
- },
850
+ ...makePrivacyMethods(ctx),
854
851
  updateDefaultDisappearingMode: async (duration) => {
855
852
  await (await ctx.getClient()).updateDefaultDisappearingMode(duration);
856
853
  },
@@ -891,11 +888,14 @@ const makeWASocket = (config) => {
891
888
  ...makeContactMethods(ctx),
892
889
  ...makeProfileMethods(ctx),
893
890
  ...makeChatActionMethods(ctx),
891
+ ...makeInternalMethods(ctx),
894
892
  ...usyncMethods,
895
893
  ...makeStanzaResponseMethods(ctx),
896
894
  ...makePresenceMethods(ctx),
897
895
  ...makeBlockingMethods(ctx),
898
896
  ...makeNewsletterMethods(ctx),
897
+ ...makeBusinessMethods(ctx),
898
+ ...makeServerQueryMethods(ctx),
899
899
  downloadMedia: async (message, type, options = {}) => {
900
900
  return downloadMediaMessage(message, type, options, {
901
901
  logger,
@@ -904,6 +904,16 @@ const makeWASocket = (config) => {
904
904
  });
905
905
  }
906
906
  };
907
+ // Assigning replaces the handler the socket itself reports through, rather
908
+ // than shadowing it with a second one only a consumer could reach.
909
+ Object.defineProperty(sock, 'onUnexpectedError', {
910
+ get: () => unexpectedErrors.handler,
911
+ set: (handler) => {
912
+ unexpectedErrors.handler = handler;
913
+ },
914
+ enumerable: true,
915
+ configurable: true
916
+ });
907
917
  return sock;
908
918
  };
909
919
  export default makeWASocket;
@@ -0,0 +1,88 @@
1
+ import type { BinaryNode, MessageUpsertType, WAMessage, WAPatchCreate } from '../Types/index.js';
2
+ import type { ILogger } from '../Utils/logger.js';
3
+ import type { SocketContext } from './types.js';
4
+ /**
5
+ * The one place an unexpected failure is reported, shared by the socket's
6
+ * `onUnexpectedError` property and by the internal paths that raise them.
7
+ *
8
+ * `report` cannot throw. Some of its callers are bridge callbacks that borrow
9
+ * memory and are contractually forbidden from raising, so a consumer whose
10
+ * handler throws would cost the session its borrowed batches. A handler that
11
+ * fails is reported through the logger and the caller carries on.
12
+ *
13
+ * The return type says `void`, which an `async` handler also satisfies, so a
14
+ * handler that rejects rather than throws is contained the same way. Left
15
+ * loose it would surface as an unhandled rejection, which ends the process
16
+ * under `--unhandled-rejections=strict`.
17
+ */
18
+ export declare const makeUnexpectedErrorReporter: (logger: ILogger) => {
19
+ report: (err: unknown, msg: string) => void;
20
+ handler: (err: unknown, msg: string) => void;
21
+ };
22
+ export declare const makeInternalMethods: (ctx: SocketContext) => {
23
+ /**
24
+ * `waitForSocket`, not `waitForConnected`: upstream waits for the socket
25
+ * to open, which is not the same as being logged in, and the bridge
26
+ * separates the two.
27
+ *
28
+ * A rejection means the socket did not open within one connect attempt,
29
+ * not that it never will: the engine reconnects on its own, and a caller
30
+ * that wants the next attempt waits again.
31
+ */
32
+ waitForSocketOpen: () => Promise<void>;
33
+ /**
34
+ * Publishes a message onto the event bus, which is this layer's own job.
35
+ * Buffered as upstream buffers it, so a replay of many messages
36
+ * consolidates into the batches a listener expects rather than one
37
+ * event per call.
38
+ *
39
+ * The push-name half of upstream's version is not reproduced: contact
40
+ * state belongs to the core, and writing it from here would make two
41
+ * writers for one value.
42
+ */
43
+ upsertMessage: (msg: WAMessage, type: MessageUpsertType) => Promise<void>;
44
+ /**
45
+ * The same reporter the socket's own failure paths use, rather than a
46
+ * second one that only a consumer could reach: a dispatcher that threw
47
+ * or a wire batch that would not decode arrives here. The socket
48
+ * redefines this as an accessor, so assigning to it replaces the
49
+ * handler those paths report through.
50
+ */
51
+ onUnexpectedError: (err: Error, msg: string) => void;
52
+ /**
53
+ * Resolves without forcing anything, and says so once.
54
+ *
55
+ * The engine syncs app state on connect and re-syncs every collection
56
+ * when the server raises the `syncd_app_state` dirty bit, so the state a
57
+ * caller wants current already is. There is no on-demand resync to
58
+ * delegate to, and rejecting would break a call whose intent is met.
59
+ */
60
+ resyncAppState: (collections?: readonly ('critical_block' | 'critical_unblock_low' | 'regular_high' | 'regular_low' | 'regular')[], isInitialSync?: boolean) => Promise<void>;
61
+ /**
62
+ * Refused: the offset is protocol state the core keeps from the stanzas
63
+ * it already reads. A second clock here would drift from the one that
64
+ * actually timestamps outgoing messages.
65
+ */
66
+ updateServerTimeOffset: (node: BinaryNode) => never;
67
+ /**
68
+ * Refused one layer down. The core's session instance is `pub(crate)`,
69
+ * and what exists returns a half-built stanza plus a sequence number, so
70
+ * exposing it would hand JavaScript the job of finishing and sequencing
71
+ * a lifecycle stanza.
72
+ */
73
+ sendUnifiedSession: () => Promise<never>;
74
+ /**
75
+ * Refused: the bridge exposes app-state actions typed per action and
76
+ * deliberately no generic. Accepting a raw patch here would mean
77
+ * building mutation indices and schema versions in TypeScript, beside
78
+ * the copy the core already maintains.
79
+ */
80
+ appPatch: (patchCreate: WAPatchCreate) => Promise<never>;
81
+ /**
82
+ * Null rather than absent, and rather than a shadow. Retry is the
83
+ * engine's, so there is no manager here to hand out, and saying so in
84
+ * the type is more useful than leaving the field missing.
85
+ */
86
+ messageRetryManager: null;
87
+ };
88
+ //# sourceMappingURL=internals.d.ts.map
@@ -0,0 +1,145 @@
1
+ import { Boom } from '../Utils/boom.js';
2
+ /**
3
+ * The one place an unexpected failure is reported, shared by the socket's
4
+ * `onUnexpectedError` property and by the internal paths that raise them.
5
+ *
6
+ * `report` cannot throw. Some of its callers are bridge callbacks that borrow
7
+ * memory and are contractually forbidden from raising, so a consumer whose
8
+ * handler throws would cost the session its borrowed batches. A handler that
9
+ * fails is reported through the logger and the caller carries on.
10
+ *
11
+ * The return type says `void`, which an `async` handler also satisfies, so a
12
+ * handler that rejects rather than throws is contained the same way. Left
13
+ * loose it would surface as an unhandled rejection, which ends the process
14
+ * under `--unhandled-rejections=strict`.
15
+ */
16
+ export const makeUnexpectedErrorReporter = (logger) => {
17
+ const logUnexpected = (err, msg) => logger.error({ err }, `unexpected error in '${msg}'`);
18
+ const logHandlerFailure = (err, msg, reportingError) => {
19
+ logger.error({ err: reportingError }, 'the onUnexpectedError handler threw');
20
+ logUnexpected(err, msg);
21
+ };
22
+ let handler = logUnexpected;
23
+ return {
24
+ report: (err, msg) => {
25
+ try {
26
+ const settled = handler(err, msg);
27
+ if (settled instanceof Promise) {
28
+ settled.catch(reportingError => logHandlerFailure(err, msg, reportingError));
29
+ }
30
+ }
31
+ catch (reportingError) {
32
+ logHandlerFailure(err, msg, reportingError);
33
+ }
34
+ },
35
+ get handler() {
36
+ return handler;
37
+ },
38
+ set handler(next) {
39
+ handler = next;
40
+ }
41
+ };
42
+ };
43
+ /**
44
+ * Upstream has no timeout on `waitForSocketOpen`; the bridge requires one, so
45
+ * one has to be chosen.
46
+ *
47
+ * It is the core's own transport connect ceiling, not `connectTimeoutMs`. That
48
+ * config reaches neither the transport nor the core, so a value below the
49
+ * ceiling would reject a wait while the attempt behind it was still running and
50
+ * about to succeed. A value above it would sit past the point the attempt was
51
+ * already abandoned.
52
+ */
53
+ const TRANSPORT_CONNECT_TIMEOUT_MS = 20000;
54
+ export const makeInternalMethods = (ctx) => {
55
+ /** Warned once per socket rather than per call, as with the other no-ops. */
56
+ let warnedAboutResync = false;
57
+ return {
58
+ /**
59
+ * `waitForSocket`, not `waitForConnected`: upstream waits for the socket
60
+ * to open, which is not the same as being logged in, and the bridge
61
+ * separates the two.
62
+ *
63
+ * A rejection means the socket did not open within one connect attempt,
64
+ * not that it never will: the engine reconnects on its own, and a caller
65
+ * that wants the next attempt waits again.
66
+ */
67
+ waitForSocketOpen: async () => {
68
+ await (await ctx.getClient()).waitForSocket(TRANSPORT_CONNECT_TIMEOUT_MS);
69
+ },
70
+ /**
71
+ * Publishes a message onto the event bus, which is this layer's own job.
72
+ * Buffered as upstream buffers it, so a replay of many messages
73
+ * consolidates into the batches a listener expects rather than one
74
+ * event per call.
75
+ *
76
+ * The push-name half of upstream's version is not reproduced: contact
77
+ * state belongs to the core, and writing it from here would make two
78
+ * writers for one value.
79
+ */
80
+ upsertMessage: ctx.ev.createBufferedFunction(async (msg, type) => {
81
+ ctx.ev.emit('messages.upsert', { messages: [msg], type });
82
+ }),
83
+ /**
84
+ * The same reporter the socket's own failure paths use, rather than a
85
+ * second one that only a consumer could reach: a dispatcher that threw
86
+ * or a wire batch that would not decode arrives here. The socket
87
+ * redefines this as an accessor, so assigning to it replaces the
88
+ * handler those paths report through.
89
+ */
90
+ onUnexpectedError: (err, msg) => {
91
+ ctx.reportUnexpectedError(err, msg);
92
+ },
93
+ /**
94
+ * Resolves without forcing anything, and says so once.
95
+ *
96
+ * The engine syncs app state on connect and re-syncs every collection
97
+ * when the server raises the `syncd_app_state` dirty bit, so the state a
98
+ * caller wants current already is. There is no on-demand resync to
99
+ * delegate to, and rejecting would break a call whose intent is met.
100
+ */
101
+ resyncAppState: async (collections, isInitialSync) => {
102
+ void collections;
103
+ void isInitialSync;
104
+ if (!warnedAboutResync) {
105
+ warnedAboutResync = true;
106
+ ctx.logger.warn('resyncAppState is a no-op: the engine syncs app state on connect and again whenever the server marks it dirty');
107
+ }
108
+ },
109
+ /**
110
+ * Refused: the offset is protocol state the core keeps from the stanzas
111
+ * it already reads. A second clock here would drift from the one that
112
+ * actually timestamps outgoing messages.
113
+ */
114
+ updateServerTimeOffset: (node) => {
115
+ void node;
116
+ throw new Boom('updateServerTimeOffset is not supported: the engine tracks the server clock offset itself, and a second one here would diverge from the one it stamps messages with', { statusCode: 501 });
117
+ },
118
+ /**
119
+ * Refused one layer down. The core's session instance is `pub(crate)`,
120
+ * and what exists returns a half-built stanza plus a sequence number, so
121
+ * exposing it would hand JavaScript the job of finishing and sequencing
122
+ * a lifecycle stanza.
123
+ */
124
+ sendUnifiedSession: async () => {
125
+ throw new Boom('sendUnifiedSession is not supported: it is connection lifecycle machinery the engine owns, not a consumer operation', { statusCode: 501 });
126
+ },
127
+ /**
128
+ * Refused: the bridge exposes app-state actions typed per action and
129
+ * deliberately no generic. Accepting a raw patch here would mean
130
+ * building mutation indices and schema versions in TypeScript, beside
131
+ * the copy the core already maintains.
132
+ */
133
+ appPatch: async (patchCreate) => {
134
+ void patchCreate;
135
+ throw new Boom('appPatch is not supported: use the typed chatModify variants, which is the only app-state surface this package can offer without a second implementation of the patch format', { statusCode: 501 });
136
+ },
137
+ /**
138
+ * Null rather than absent, and rather than a shadow. Retry is the
139
+ * engine's, so there is no manager here to hand out, and saying so in
140
+ * the type is more useful than leaving the field missing.
141
+ */
142
+ messageRetryManager: null
143
+ };
144
+ };
145
+ //# sourceMappingURL=internals.js.map
@@ -1,20 +1,6 @@
1
- import type { AnyMessageContent, MessageGenerationOptions, MessageReceiptType, MessageRelayOptions, WAMessage, WAMessageContent, WAMessageKey } from '../Types/index.js';
1
+ import type { AnyMessageContent, MessageGenerationOptions, MessageReceiptType, MessageRelayOptions, WAMessage, WAMessageKey } from '../Types/index.js';
2
2
  import { WAProto } from '../Types/index.js';
3
3
  import type { SocketContext } from './types.js';
4
- /**
5
- * Drop `messageContextInfo` before handing the proto to the Rust bridge so the
6
- * bridge can fill in its own `messageSecret` / `reportingTokenVersion`.
7
- *
8
- * Exception: pin messages need `messageAddOnDurationInSecs` (86400 / 604800 /
9
- * 2592000 to pin, 0 to unpin). The bridge does not set this field, so we save
10
- * it across the delete and restore it on a fresh contextInfo.
11
- *
12
- * Mutates `msg` in place.
13
- *
14
- * `contentType` is a parameter so the send path can hand over the type it has
15
- * already resolved rather than have it scanned out of `msg` a second time.
16
- */
17
- export declare function stripContextInfoForBridge(msg: WAMessageContent, contentType?: keyof WAProto.IMessage | undefined): void;
18
4
  export declare const makeMessageMethods: (ctx: SocketContext) => {
19
5
  sendMessage: (jid: string, content: AnyMessageContent, options?: Omit<MessageGenerationOptions, 'waClient' | 'logger' | 'userJid' | 'mediaInNote'>) => Promise<WAMessage>;
20
6
  updateMediaMessage: (message: WAMessage) => Promise<WAMessage>;
@@ -13,26 +13,6 @@ function getMediaContent(content) {
13
13
  content?.documentMessage ||
14
14
  content?.stickerMessage);
15
15
  }
16
- /**
17
- * Drop `messageContextInfo` before handing the proto to the Rust bridge so the
18
- * bridge can fill in its own `messageSecret` / `reportingTokenVersion`.
19
- *
20
- * Exception: pin messages need `messageAddOnDurationInSecs` (86400 / 604800 /
21
- * 2592000 to pin, 0 to unpin). The bridge does not set this field, so we save
22
- * it across the delete and restore it on a fresh contextInfo.
23
- *
24
- * Mutates `msg` in place.
25
- *
26
- * `contentType` is a parameter so the send path can hand over the type it has
27
- * already resolved rather than have it scanned out of `msg` a second time.
28
- */
29
- export function stripContextInfoForBridge(msg, contentType = getContentType(msg)) {
30
- const pinAddOnDuration = contentType === 'pinInChatMessage' ? msg.messageContextInfo?.messageAddOnDurationInSecs : undefined;
31
- delete msg.messageContextInfo;
32
- if (pinAddOnDuration !== undefined && pinAddOnDuration !== null) {
33
- msg.messageContextInfo = { messageAddOnDurationInSecs: pinAddOnDuration };
34
- }
35
- }
36
16
  const normalizedUserJids = new WeakMap();
37
17
  const getNormalizedUserJid = (ctx) => {
38
18
  const userId = ctx.getUser()?.id;
@@ -87,7 +67,6 @@ export const makeMessageMethods = (ctx) => ({
87
67
  return fullMsg;
88
68
  }
89
69
  }
90
- stripContextInfoForBridge(msg, contentType);
91
70
  let msgId;
92
71
  const msgBytes = encodeProto('Message', msg);
93
72
  if (jid === 'status@broadcast' && options?.statusJidList?.length) {
@@ -155,7 +134,9 @@ export const makeMessageMethods = (ctx) => ({
155
134
  messageId: plan.messageId,
156
135
  extraNodes: plan.kind === 'retransmission' ? 0 : plan.nodes.length
157
136
  }, 'relayMessage compatibility plan');
158
- stripContextInfoForBridge(message);
137
+ // The message goes to the bridge as the caller built it: the core settles
138
+ // messageSecret / reportingTokenVersion itself, reusing a caller-set secret
139
+ // rather than replacing it, so nothing here has to be dropped.
159
140
  const bytes = encodeProto('Message', message);
160
141
  if (plan.kind === 'retransmission') {
161
142
  await client.retransmitMessageBytes(jid, bytes, plan.input);