@alannxd/baileys 6.1.4 → 6.1.7

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 (55) hide show
  1. package/LICENSE +4 -1
  2. package/README.md +205 -281
  3. package/lib/Defaults/index.js +1 -1
  4. package/lib/Socket/chats.js +5 -0
  5. package/lib/Socket/graphql.js +716 -0
  6. package/lib/Socket/index.js +9 -1
  7. package/lib/Socket/interop.js +463 -0
  8. package/lib/Socket/message-builder.js +475 -0
  9. package/lib/Socket/messages-recv.js +1 -1
  10. package/lib/Socket/messages-send.js +106 -30
  11. package/lib/Socket/mex.js +51 -39
  12. package/lib/Socket/newsletter.js +729 -242
  13. package/lib/Socket/privacy.js +318 -0
  14. package/lib/Socket/socket.js +163 -16
  15. package/lib/Socket/username.js +225 -136
  16. package/lib/Store/make-in-memory-store.js +47 -1
  17. package/lib/Types/Mex.js +76 -2
  18. package/lib/Utils/index.js +1 -0
  19. package/lib/Utils/messages-media.js +82 -0
  20. package/lib/Utils/messages.js +143 -2
  21. package/lib/Utils/sticker.js +133 -0
  22. package/lib/Utils/use-sqlite-auth-state.js +162 -0
  23. package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +53 -0
  24. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
  25. package/lib/WAUSync/Protocols/USyncContactProtocol.js +52 -50
  26. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +55 -52
  27. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +28 -25
  28. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +58 -0
  29. package/lib/WAUSync/Protocols/USyncLIDProtocol.js +30 -0
  30. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +32 -0
  31. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +28 -0
  32. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +37 -36
  33. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +38 -0
  34. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +25 -23
  35. package/lib/WAUSync/Protocols/index.js +12 -6
  36. package/lib/WAUSync/USyncQuery.js +149 -96
  37. package/lib/WAUSync/USyncUser.js +54 -29
  38. package/lib/WAUSync/index.js +3 -4
  39. package/lib/index.js +2 -3
  40. package/package.json +20 -21
  41. package/WAProto/fix-import.js +0 -29
  42. package/lib/Defaults/baileys-version.json +0 -3
  43. package/lib/Defaults/phonenumber-mcc.json +0 -223
  44. package/lib/Signal/Group/queue-job.js +0 -57
  45. package/lib/Socket/Client/abstract-socket-client.js +0 -13
  46. package/lib/Socket/Client/mobile-socket-client.js +0 -65
  47. package/lib/Socket/Client/web-socket-client.js +0 -62
  48. package/lib/Socket/community.js +0 -392
  49. package/lib/Socket/dugong.js +0 -637
  50. package/lib/Socket/registration.js +0 -167
  51. package/lib/Socket/usync.js +0 -69
  52. package/lib/Types/Newsletter.js +0 -38
  53. package/lib/Utils/baileys-event-stream.js +0 -63
  54. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +0 -51
  55. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +0 -29
@@ -2,20 +2,20 @@ import NodeCache from '@cacheable/node-cache';
2
2
  import { Boom } from '@hapi/boom';
3
3
  import { proto } from '../../WAProto/index.js';
4
4
  import { DEFAULT_CACHE_TTLS, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
5
- import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, DEF_MEDIA_HOST, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds, setBotMessageSecret } from '../Utils/index.js';
5
+ import { aggregateMessageKeysNotFromMe, assertMediaContent, assertMeId, bindWaitForEvent, decryptMediaRetryData, DEF_MEDIA_HOST, encodeNewsletterMessage, encodeSignedDeviceIdentity, encodeWAMessage, encryptMediaRetryRequest, extractDeviceJids, generateMessageIDV2, generateIOSMessageID, generateParticipantHashV2, generateWAMessage, getStatusCodeForMediaRetry, getUrlFromDirectPath, getWAUploadToServer, MessageRetryManager, normalizeMessageContent, parseAndInjectE2ESessions, unixTimestampSeconds, setBotMessageSecret } from '../Utils/index.js';
6
6
  import { getUrlInfo } from '../Utils/link-preview.js';
7
7
  import { makeKeyedMutex, makeMutex } from '../Utils/make-mutex.js';
8
8
  import { getMessageReportingToken, shouldIncludeReportingToken } from '../Utils/reporting-utils.js';
9
9
  import { buildMergedTcTokenIndexWrite, isTcTokenExpired, resolveIssuanceJid, resolveTcTokenJid, shouldSendNewTcToken, storeTcTokensFromIqResult } from '../Utils/tc-token-utils.js';
10
10
  import { areJidsSameUser, getBinaryNodeChild, getBinaryNodeChildren, isHostedLidUser, isHostedPnUser, isJidBot, isJidGroup, isJidMetaAI, isLidUser, isPnUser, jidDecode, jidEncode, jidNormalizedUser, PSA_WID, S_WHATSAPP_NET, getAdditionalNode, getBinaryNodeFilter, getBinaryFilteredBizBot, isInteropUser } from '../WABinary/index.js';
11
11
  import { USyncQuery, USyncUser } from '../WAUSync/index.js';
12
- import { makeNewsletterSocket } from './newsletter.js';
12
+ import { makeUsernameSocket } from './username.js';
13
13
  import imup from './luxu.js';
14
14
  import * as Utils_1 from '../Utils/index.js';
15
- import { randomBytes } from 'crypto';
15
+ import { randomBytes, createHmac } from 'crypto';
16
16
  export const makeMessagesSocket = (config) => {
17
17
  const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount, aiLabel } = config;
18
- const sock = makeNewsletterSocket(config);
18
+ const sock = makeUsernameSocket(config);
19
19
  const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, registerSocketEndHandler } = sock;
20
20
  const getLIDForPN = signalRepository.lidMapping.getLIDForPN.bind(signalRepository.lidMapping);
21
21
  /**
@@ -433,17 +433,22 @@ export const makeMessagesSocket = (config) => {
433
433
  message,
434
434
  {
435
435
  messageId: msgId,
436
- participant = false,
436
+ participant,
437
+ participants: retryParticipants,
438
+ isSecret,
439
+ protected: protectedSend,
437
440
  additionalAttributes,
438
441
  additionalNodes,
439
- useUserDevicesCache,
442
+ useUserDevicesCache,
440
443
  useCachedGroupMetadata,
441
- statusJidList
444
+ statusJidList,
445
+ cstoken = true
442
446
  }
443
447
  ) => {
444
448
  const meId = authState.creds.me.id
445
449
  const meLid = authState.creds.me?.lid
446
- const isRetryResend = Boolean(participant?.jid)
450
+ const isRetryResend = Boolean(retryParticipants?.jid && retryParticipants?.count != null)
451
+ const isRecipientOnly = Boolean(participant?.jid)
447
452
  let shouldIncludeDeviceIdentity = isRetryResend
448
453
  const statusJid = 'status@broadcast'
449
454
  const { user, server } = jidDecode(jid)
@@ -454,7 +459,8 @@ export const makeMessagesSocket = (config) => {
454
459
  const isInterop = isInteropUser(jid)
455
460
  const isGroupOrStatus = isGroup || isStatus
456
461
  const finalJid = jid
457
- msgId = msgId || generateMessageIDV2(meId)
462
+ const iosBros = config.browser[0] === "iOS" || config.browser[1] === "Safari";
463
+ msgId = iosBros ? generateIOSMessageID() : msgId ?? generateMessageIDV2(meId)
458
464
  useUserDevicesCache = useUserDevicesCache!== false
459
465
  useCachedGroupMetadata = useCachedGroupMetadata!== false &&!isStatus
460
466
  const participants = []
@@ -467,6 +473,16 @@ export const makeMessagesSocket = (config) => {
467
473
  messageContextInfo: message.messageContextInfo
468
474
  }
469
475
  const extraAttrs = {}
476
+ if (isRetryResend) {
477
+ if (!isGroup &&!isStatus) {
478
+ additionalAttributes = { ...additionalAttributes, device_fanout: 'false' }
479
+ }
480
+ const { user: retryUser, device: retryDevice } = jidDecode(retryParticipants.jid)
481
+ devices.push({ user: retryUser, device: retryDevice, jid: retryParticipants.jid })
482
+ }
483
+ if ((isRecipientOnly || isSecret || protectedSend) &&!isGroup &&!isStatus) {
484
+ additionalAttributes = { ...additionalAttributes, device_fanout: 'false' }
485
+ }
470
486
  const regexGroupOld = /^(\d{1,15})-(\d+)@g\.us$/
471
487
  const messages = normalizeMessageContent(message)
472
488
  const buttonType = getButtonType(messages)
@@ -508,7 +524,7 @@ export const makeMessagesSocket = (config) => {
508
524
  return groupData
509
525
  })(),
510
526
  (async () => {
511
- if (!participant &&!isStatus) {
527
+ if (!retryParticipants &&!isStatus) {
512
528
  const result = await authState.keys.get('sender-key-memory', [jid])
513
529
  return result[jid] || {}
514
530
  }
@@ -648,7 +664,7 @@ export const makeMessagesSocket = (config) => {
648
664
  for (const device of devices) {
649
665
  const deviceJid = device.jid
650
666
  const hasKey =!!senderKeyMap[deviceJid]
651
- if (!hasKey ||!!participant &&!isHostedLidUser(deviceJid) &&!isHostedPnUser(deviceJid) && device.device!== 99) {
667
+ if (!hasKey ||!!retryParticipants &&!isHostedLidUser(deviceJid) &&!isHostedPnUser(deviceJid) && device.device!== 99) {
652
668
  senderKeyRecipients.push(deviceJid)
653
669
  senderKeyMap[deviceJid] = true
654
670
  }
@@ -677,7 +693,7 @@ export const makeMessagesSocket = (config) => {
677
693
  logger.debug({ to: jid, ownId }, 'Using PN identity for @s.whatsapp.net conversation')
678
694
  }
679
695
  const { user: ownUser } = jidDecode(ownId)
680
- if (!participant) {
696
+ if (!isRetryResend) {
681
697
  const patchedForReporting = await patchMessageBeforeSending(message, [jid])
682
698
  reportingMessage = Array.isArray(patchedForReporting)
683
699
  ? patchedForReporting.find(item => item.recipientJid === jid) || patchedForReporting[0]
@@ -714,22 +730,25 @@ export const makeMessagesSocket = (config) => {
714
730
  continue
715
731
  }
716
732
  const isMe = user === mePnUser || user === meLidUser
733
+ const hasDevice = jid.includes(':')
717
734
  let ptcp = false
718
- if (participant) {
735
+ if (retryParticipants) {
719
736
  if (!isJidGroup(jid) && !isStatus) {
720
737
  if (!(!isMe)) ptcp = true
721
738
  } else {
722
739
  ptcp = false
723
740
  }
724
741
  }
725
- if (!ptcp) {
726
- if (isMe) {
727
- meRecipients.push(jid)
728
- } else {
729
- otherRecipients.push(jid)
730
- }
731
- allRecipients.push(jid)
742
+ if (ptcp) continue
743
+ if (isRecipientOnly && isMe) continue
744
+ if (isSecret && !(!isMe && !hasDevice)) continue
745
+ if (protectedSend && !isMe && hasDevice) continue
746
+ if (isMe) {
747
+ meRecipients.push(jid)
748
+ } else {
749
+ otherRecipients.push(jid)
732
750
  }
751
+ allRecipients.push(jid)
733
752
  }
734
753
  await assertSessions(allRecipients)
735
754
  const [
@@ -746,18 +765,38 @@ export const makeMessagesSocket = (config) => {
746
765
  shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2
747
766
  }
748
767
  if (isRetryResend) {
749
- const isParticipantLid = jidDecode(participant.jid).server === 'lid'
750
- const isMe = areJidsSameUser(participant.jid, isParticipantLid? meLid : meId)
768
+ const isParticipantLid = isLidUser(retryParticipants.jid)
769
+ const isMe = areJidsSameUser(retryParticipants.jid, isParticipantLid? meLid : meId)
770
+ let messageToSend = message
771
+ if (isGroupOrStatus) {
772
+ let groupSenderIdentity
773
+ try {
774
+ if (meLid && await signalRepository.hasSenderKey({ group: destinationJid, meId: meLid })) {
775
+ groupSenderIdentity = meLid
776
+ } else if (await signalRepository.hasSenderKey({ group: destinationJid, meId })) {
777
+ groupSenderIdentity = meId
778
+ }
779
+ if (groupSenderIdentity) {
780
+ const skdm = await signalRepository.getSenderKeyDistributionMessage({ group: destinationJid, meId: groupSenderIdentity })
781
+ messageToSend = {
782
+ ...message,
783
+ senderKeyDistributionMessage: { groupId: destinationJid, axolotlSenderKeyDistributionMessage: skdm }
784
+ }
785
+ }
786
+ } catch (err) {
787
+ logger.warn({ err, jid: destinationJid }, 'failed to build SKDM for retry, sending without it')
788
+ }
789
+ }
751
790
  const encodedMessageToSend = isMe
752
- ? encodeWAMessage({ deviceSentMessage: { destinationJid, message } })
753
- : encodeWAMessage(message)
791
+ ? encodeWAMessage({ deviceSentMessage: { destinationJid, message: messageToSend } })
792
+ : encodeWAMessage(messageToSend)
754
793
  const { type, ciphertext: encryptedContent } = await signalRepository.encryptMessage({
755
794
  data: encodedMessageToSend,
756
- jid: participant.jid
795
+ jid: retryParticipants.jid
757
796
  })
758
797
  binaryNodeContent.push({
759
798
  tag: 'enc',
760
- attrs: { v: '2', type, count: (participant.count?? 0).toString() },
799
+ attrs: { v: '2', type, count: (retryParticipants.count?? 0).toString() },
761
800
  content: encryptedContent
762
801
  })
763
802
  }
@@ -777,6 +816,17 @@ export const makeMessagesSocket = (config) => {
777
816
  tag: 'message',
778
817
  attrs: { id: msgId, to: destinationJid, type: getMessageType(message),...(additionalAttributes || {}) },
779
818
  content: binaryNodeContent
819
+ }
820
+ if (isRetryResend) {
821
+ if (isJidGroup(destinationJid)) {
822
+ stanza.attrs.to = destinationJid
823
+ stanza.attrs.participant = retryParticipants.jid
824
+ } else if (areJidsSameUser(retryParticipants.jid, meId)) {
825
+ stanza.attrs.to = retryParticipants.jid
826
+ stanza.attrs.recipient = destinationJid
827
+ } else {
828
+ stanza.attrs.to = retryParticipants.jid
829
+ }
780
830
  }
781
831
  if (shouldIncludeDeviceIdentity) {
782
832
  stanza.content.push({ tag: 'device-identity', attrs: {}, content: encodeSignedDeviceIdentity(authState.creds.account, true) })
@@ -799,7 +849,7 @@ export const makeMessagesSocket = (config) => {
799
849
  if (!isNewsletter &&!isRetryResend && reportingMessage?.messageContextInfo?.messageSecret && shouldIncludeReportingToken(reportingMessage)) {
800
850
  try {
801
851
  const encoded = encodeWAMessage(reportingMessage)
802
- const reportingKey = { id: msgId, fromMe: true, remoteJid: destinationJid, participant: participant?.jid }
852
+ const reportingKey = { id: msgId, fromMe: true, remoteJid: destinationJid, participant: retryParticipants?.jid }
803
853
  const reportingNode = await getMessageReportingToken(encoded, reportingMessage, reportingKey)
804
854
  if (reportingNode) {
805
855
  stanza.content.push(reportingNode)
@@ -845,6 +895,26 @@ export const makeMessagesSocket = (config) => {
845
895
  }
846
896
  if (tcTokenBuffer?.length && sock.serverProps.privacyTokenOn1to1) {
847
897
  stanza.content.push({ tag: 'tctoken', attrs: {}, content: tcTokenBuffer })
898
+ }
899
+ if (cstoken && is1on1Send &&!tcTokenBuffer?.length) {
900
+ try {
901
+ const lidJid = await resolveTcTokenJid(destinationJid, getLIDForPN)
902
+ if (isLidUser(lidJid)) {
903
+ const saltRes = await authState.keys.get('nct-salt', ['default'])
904
+ const nctSalt = saltRes?.default
905
+ if (nctSalt?.length) {
906
+ const cs = createHmac('sha256', Buffer.from(nctSalt)).update(Buffer.from(lidJid, 'utf8')).digest()
907
+ stanza.content.push({ tag: 'cstoken', attrs: {}, content: cs })
908
+ logger.debug({ jid: destinationJid, lid: lidJid }, 'attached cstoken (nct fallback)')
909
+ } else {
910
+ logger.debug({ jid: destinationJid }, 'cstoken requested but no nct salt stored yet')
911
+ }
912
+ } else {
913
+ logger.debug({ jid: destinationJid }, 'cstoken requested but recipient has no LID')
914
+ }
915
+ } catch (err) {
916
+ logger.debug({ jid: destinationJid, err: err?.message }, 'cstoken attach failed')
917
+ }
848
918
  }
849
919
  if (additionalNodes && additionalNodes.length > 0 &&!didPushAdditional) {
850
920
  stanza.content.push(...additionalNodes)
@@ -877,7 +947,7 @@ export const makeMessagesSocket = (config) => {
877
947
  .catch(err => logger.debug({ jid: destinationJid, err: err?.message }, 'fire-and-forget tctoken issuance failed'))
878
948
  .finally(() => inFlightTcTokenIssuance.delete(tcTokenJid))
879
949
  }
880
- if (messageRetryManager &&!participant) {
950
+ if (messageRetryManager &&!retryParticipants) {
881
951
  messageRetryManager.addRecentMessage(destinationJid, msgId, message)
882
952
  }
883
953
  if (isInterop &&!isRetryResend) {
@@ -934,6 +1004,9 @@ export const makeMessagesSocket = (config) => {
934
1004
  else if (message.stickerMessage) {
935
1005
  return 'sticker';
936
1006
  }
1007
+ else if (message.stickerPackMessage) {
1008
+ return 'sticker';
1009
+ }
937
1010
  else if (message.listMessage) {
938
1011
  return 'list';
939
1012
  }
@@ -1265,7 +1338,7 @@ export const makeMessagesSocket = (config) => {
1265
1338
  sendMessage: async (jid, content, options = {}) => {
1266
1339
  const userJid = authState.creds.me.id;
1267
1340
  const luki = new imup(Utils_1, waUploadToServer, relayMessage)
1268
- const { quoted, participant = false } = options;
1341
+ const { quoted, participant = false, isSecret, protected: protectedSend, cstoken } = options;
1269
1342
  const messageType = luki.detectType(content);
1270
1343
  if (typeof content === 'object' &&
1271
1344
  'disappearingMessagesInChat' in content &&
@@ -1374,7 +1447,10 @@ export const makeMessagesSocket = (config) => {
1374
1447
  additionalAttributes,
1375
1448
  statusJidList: options.statusJidList,
1376
1449
  additionalNodes: aiLabel ? additionalNodes : options.additionalNodes,
1377
- participant
1450
+ participants: participant || undefined,
1451
+ isSecret,
1452
+ protected: protectedSend,
1453
+ ...(cstoken !== undefined ? { cstoken } : {})
1378
1454
  });
1379
1455
  if (config.emitOwnEvents) {
1380
1456
  process.nextTick(async () => {
package/lib/Socket/mex.js CHANGED
@@ -1,42 +1,54 @@
1
- import { Boom } from '@hapi/boom';
2
- import { getBinaryNodeChild, S_WHATSAPP_NET } from '../WABinary/index.js';
1
+ import { Boom } from '@hapi/boom'
2
+ import { S_WHATSAPP_NET, getBinaryNodeChild } from '../WABinary/index.js'
3
+
3
4
  const wMexQuery = (variables, queryId, query, generateMessageTag) => {
4
- return query({
5
- tag: 'iq',
6
- attrs: {
7
- id: generateMessageTag(),
8
- type: 'get',
9
- to: S_WHATSAPP_NET,
10
- xmlns: 'w:mex'
11
- },
12
- content: [
13
- {
14
- tag: 'query',
15
- attrs: { query_id: queryId },
16
- content: Buffer.from(JSON.stringify({ variables }), 'utf-8')
17
- }
18
- ]
19
- });
20
- };
5
+ return query({
6
+ tag: 'iq',
7
+ attrs: {
8
+ id: generateMessageTag(),
9
+ type: 'get',
10
+ to: S_WHATSAPP_NET,
11
+ xmlns: 'w:mex'
12
+ },
13
+ content: [
14
+ {
15
+ tag: 'query',
16
+ attrs: { query_id: queryId },
17
+ content: Buffer.from(JSON.stringify({ variables }), 'utf-8')
18
+ }
19
+ ]
20
+ })
21
+ }
22
+
21
23
  export const executeWMexQuery = async (variables, queryId, dataPath, query, generateMessageTag) => {
22
- const result = await wMexQuery(variables, queryId, query, generateMessageTag);
23
- const child = getBinaryNodeChild(result, 'result');
24
- if (child?.content) {
25
- const data = JSON.parse(child.content.toString());
26
- if (data.errors && data.errors.length > 0) {
27
- const errorMessages = data.errors.map((err) => err.message || 'Unknown error').join(', ');
28
- const firstError = data.errors[0];
29
- const errorCode = firstError.extensions?.error_code || 400;
30
- throw new Boom(`GraphQL server error: ${errorMessages}`, { statusCode: errorCode, data: firstError });
31
- }
32
- const response = dataPath ? data?.data?.[dataPath] : data?.data;
33
- if (typeof response !== 'undefined') {
34
- return response;
35
- }
24
+ const result = await wMexQuery(variables, queryId, query, generateMessageTag)
25
+ const child = getBinaryNodeChild(result, 'result')
26
+ if (child?.content) {
27
+ const str = child.content.toString('utf-8').replace(/^\x00+/, '').trim()
28
+ let data
29
+ try {
30
+ data = JSON.parse(str)
31
+ } catch {
32
+ data = str
36
33
  }
37
- const action = (dataPath || '').startsWith('xwa2_')
38
- ? dataPath.substring(5).replace(/_/g, ' ')
39
- : dataPath?.replace(/_/g, ' ');
40
- throw new Boom(`Failed to ${action}, unexpected response structure.`, { statusCode: 400, data: result });
41
- };
42
- //# sourceMappingURL=mex.js.map
34
+ if (typeof data === 'string') {
35
+ return data
36
+ }
37
+ if (data.errors && data.errors.length > 0) {
38
+ const errorMessages = data.errors.map(err => err.message || 'Unknown error').join(', ')
39
+ const firstError = data.errors[0]
40
+ const errorCode = firstError.extensions?.error_code || 400
41
+ throw new Boom(`GraphQL server error: ${errorMessages}`, { statusCode: errorCode, data: firstError })
42
+ }
43
+
44
+ const response = dataPath ? data?.data?.[dataPath] : data?.data
45
+ if (typeof response !== 'undefined') {
46
+ return response
47
+ }
48
+ }
49
+
50
+ const action = (dataPath || '').startsWith('xwa2_')
51
+ ? dataPath.substring(5).replace(/_/g, '')
52
+ : dataPath?.replace(/_/g, '')
53
+ throw new Boom(`Failed to ${action}, unexpected response structure.`, { statusCode: 400, data: result })
54
+ }