Spaces:
Runtime error
Runtime error
File size: 17,048 Bytes
46252cd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | /**
* Committed engine capability matrix: for each `IWhatsAppEngine` method, the REAL availability on
* each adapter — `wwjs` = whatsapp-web.js (the default engine), `baileys` = the browser-free
* alternative.
*
* Two fields tell the story:
* - `status`: 'supported' (the capability genuinely works end-to-end) or 'not-available' (the method
* either throws `EngineNotSupportedError`/`ChannelMediaNotSupportedError` at the adapter boundary
* → HTTP 501, OR the adapter claims support but the underlying library cannot deliver — a
* phantom-support case surfaced by source verification, e.g. wwjs catalog methods that log
* "not implemented" and return null/[] without throwing).
* - `rootCause` (present only when `not-available`): WHY it is not available, so a contributor knows
* exactly where to start. Three values:
* 'adapter-gap' — the underlying library HAS the capability; only the OpenWA adapter
* wiring is missing. FIXABLE in this repo (a PR that calls the library
* symbol the evidence points at).
* 'library-limitation' — the underlying library exposes NO first-class symbol for this op. Not
* fixable without a raw-proto/fork effort or an event-cache hack.
* 'uncertain' — source trace was inconclusive; needs a live spike.
*
* `evidence` cites the library symbol(s) that were inspected, so an engineer can open the exact file
* and start wiring immediately. REQUIRED when at least one adapter is `not-available`; may also
* annotate a newly-wired `supported` row with the symbols it now calls.
*
* This is a SNAPSHOT. `engine-parity.spec.ts` enforces exact matrix-key↔interface-method correspondence
* and the throw-invariants it can observe in live adapter method bodies. It does not read the operator
* documentation and cannot classify non-throwing phantom stubs. The `status`, `rootCause`, and
* `evidence` fields therefore remain hand-curated, source-traced annotations that must be reviewed as
* adapters are wired or libraries change.
*
* NOTE on phantom support: the drift gate's throw-heuristic cannot see adapter methods that silently
* stub (return null/[] + a warn log) without throwing. The matrix BELOW is the source-of-truth: three
* wwjs entries (getCatalog/getProducts/getProduct) are marked `not-available` here even though their
* adapter bodies do not throw — the library has no API for them, so the adapter stubs. If the drift
* gate is extended to assert against this matrix, it must consult `status`, not just the throw
* pattern, for these rows (or the adapter stubs must start throwing). getContactStatus/
* getContactStatuses were on this list until #714 wired them on whatsapp-web.js; their rows say
* `supported` and the adapter really does read stories, so they no longer belong here.
*/
export type CapabilityStatus = 'supported' | 'not-available';
export type RootCause = 'adapter-gap' | 'library-limitation' | 'uncertain';
export interface AdapterCapability {
status: CapabilityStatus;
/** Present only when `status === 'not-available'`. */
rootCause?: RootCause;
}
export interface MethodCapability {
wwjs: AdapterCapability;
baileys: AdapterCapability;
/** Cited library symbols (baileys; wwjs). Required when at least one adapter is not-available. */
evidence?: string;
}
export const ENGINE_CAPABILITY_MATRIX: Record<string, MethodCapability> = {
addLabelToChat: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
addParticipants: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
blockContact: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
checkNumberExists: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
createGroup: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
deleteChat: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
deleteMessage: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
deleteStatus: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
demoteParticipants: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
destroy: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
disconnect: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
editMessage: {
wwjs: { status: 'supported' },
baileys: { status: 'supported' },
evidence:
'wwjs Message.edit(content,options?) (index.d.ts:1362; MessageEditOptions:1600); baileys Editable.edit?: WAMessageKey on the text content variant (Types/Message.d.ts:86, AnyRegularMessageContent:168) via sendMessage(jid,{text,edit:key})',
},
forceDestroy: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
forwardMessage: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getCatalog: {
wwjs: { status: 'not-available', rootCause: 'library-limitation' },
baileys: { status: 'not-available', rootCause: 'adapter-gap' },
evidence:
'baileys Socket/business.d.ts:7 getCatalog({jid,limit,cursor}) + getCollections (business.d.ts:11) — adapter unwired (returns Product[]+cursor, not Catalog metadata; medium-confidence shape synthesis); wwjs index.d.ts has NO Client.getCatalog (0 hits), adapter stubs to null @WhatsAppWebJsAdapter.getCatalog',
},
getChannelById: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getChannelMessages: {
wwjs: { status: 'supported' },
baileys: { status: 'not-available', rootCause: 'adapter-gap' },
evidence:
'baileys Socket/newsletter.d.ts:19 newsletterFetchMessages(jid,count,since,after) returns RAW BinaryNode of <message_updates> (newsletter.js:149) — adapter unwired AND no exposed library parser (BinaryNode→ChannelMessage mapping is the work); wwjs Channel.fetchMessages (Channel.js:327)',
},
getChatHistory: {
wwjs: { status: 'supported' },
baileys: { status: 'not-available', rootCause: 'library-limitation' },
evidence:
'baileys only fetchMessageHistory(count,oldestKey,oldestTs) (Socket/business.d.ts:25) returns a sync-token string; messages arrive later via messaging-history.set event — no synchronous per-chat fetchMessages; wwjs Chat.fetchMessages (Chat.js)',
},
getChatLabels: {
wwjs: { status: 'supported' },
baileys: { status: 'not-available', rootCause: 'library-limitation' },
evidence:
'baileys no getChatLabels in lib/**/*.d.ts; Types/LabelAssociation.d.ts defines ChatLabelAssociation but no query fn (only addChatLabel/removeChatLabel writes @chats.d.ts:70-71); wwjs Client.getChatLabels (Client.js:2838)',
},
getChats: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getContactById: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getContactStatus: {
wwjs: { status: 'supported' },
baileys: { status: 'not-available', rootCause: 'library-limitation' },
evidence:
'baileys fetchStatus (Socket/chats.d.ts:42 via USyncStatusProtocol) = about/profile text only, NOT 24h stories — no story getter in lib',
},
getContactStatuses: {
wwjs: { status: 'supported' },
baileys: { status: 'not-available', rootCause: 'library-limitation' },
evidence: 'baileys fetchStatus = about text only; no story enumerate in lib',
},
getContacts: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getGroupInfo: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getGroupInviteCode: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getGroups: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getLabelById: {
wwjs: { status: 'supported' },
baileys: { status: 'not-available', rootCause: 'library-limitation' },
evidence:
'baileys no getLabel/getLabelById in lib/**/*.d.ts (Types/Label.d.ts has only Label interface + LabelColor enum + LabelActionBody); derivable only from an app-state-sync label cache; wwjs Client.getLabelById (Client.js:2825)',
},
getLabels: {
wwjs: { status: 'supported' },
baileys: { status: 'not-available', rootCause: 'library-limitation' },
evidence:
'baileys no getLabel/fetchLabel in lib/**/*.d.ts; chats.d.ts:69-73 + business.d.ts:162-166 expose ONLY writes; derivable only from an app-state-sync event cache; wwjs Client.getLabels (Client.js:2747)',
},
getMessageReactions: {
wwjs: { status: 'supported' },
baileys: { status: 'not-available', rootCause: 'library-limitation' },
evidence:
'baileys no getReactions/fetchReactions; reactions exist only as event-augmented WAMessage.reactions (proto.IReaction @WAProto/index.d.ts:10623) via messages.reaction event; adapter does not persist them into its store; wwjs Message.getReactions (Message.js)',
},
getNumberId: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getPhoneNumber: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getProduct: {
wwjs: { status: 'not-available', rootCause: 'library-limitation' },
baileys: { status: 'not-available', rootCause: 'adapter-gap' },
evidence:
'baileys only getCatalog (Socket/business.d.ts:7); getProduct = getCatalog then find-by-id (compose-and-filter, loads whole page; medium-confidence); wwjs no Client.getProduct — only page-internal getProductMetadata (Utils.js:1253), not a public Client fn; adapter stubs to null @WhatsAppWebJsAdapter.getProduct',
},
getProducts: {
wwjs: { status: 'not-available', rootCause: 'library-limitation' },
baileys: { status: 'not-available', rootCause: 'adapter-gap' },
evidence:
'baileys Socket/business.d.ts:7 getCatalog({jid,limit,cursor}) → {products, nextPageCursor} — adapter unwired; wwjs no Client.getProducts in index.d.ts (0 hits); adapter stubs to empty @WhatsAppWebJsAdapter.getProducts',
},
getProfilePicture: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getPushName: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getQRCode: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getStatus: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
getSubscribedChannels: {
wwjs: { status: 'supported' },
baileys: { status: 'not-available', rootCause: 'library-limitation' },
evidence:
'baileys no enumerate-newsletters fn; all 23 Socket/newsletter.d.ts exports are per-jid (newsletterMetadata requires a key; newsletterSubscribers returns the count of ONE). Only the newsletter EVENT surfaces jids opportunistically (incremental, not list-all); wwjs Client.getChannels (Client.js:1680)',
},
initialize: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
joinGroupViaInviteCode: {
wwjs: { status: 'supported' },
baileys: { status: 'supported' },
evidence:
'wwjs Client.acceptInvite(inviteCode) → res.gid._serialized (index.d.ts:23; Client.js:1836-1844); baileys groupAcceptInvite(code) → string|undefined (Socket/groups.d.ts:25) — undefined mapped to a thrown error',
},
leaveGroup: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
logout: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
markUnread: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
postImageStatus: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
postTextStatus: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
postVideoStatus: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
promoteParticipants: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
reactToMessage: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
removeLabelFromChat: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
removeParticipants: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
rejectCall: {
wwjs: { status: 'supported' },
baileys: { status: 'supported' },
evidence:
"wwjs Call.reject() (index.d.ts:2417) on the live Call cached from the client 'call' event (index.d.ts:643); baileys rejectCall(callId, callFrom) (Socket/messages-recv.d.ts:10) with the raw `from` JID cached from the 'offer' call event (Types/Call.d.ts)",
},
replyToMessage: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
requestPairingCode: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
resolveContactPhone: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
revokeGroupInviteCode: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
sendAudioMessage: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
sendCatalog: {
wwjs: { status: 'not-available', rootCause: 'library-limitation' },
baileys: { status: 'not-available', rootCause: 'library-limitation' },
evidence:
'baileys AnyMessageContent (Types/Message.d.ts:166-210) has no catalog key — only {product} single-product + product_catalog_edit/add/delete CRUD (Socket/business.js:294-362); wwjs no Client.sendCatalog in index.d.ts (0 hits)',
},
sendChatState: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
sendContactMessage: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
sendDocumentMessage: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
sendImageMessage: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
sendLocationMessage: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
sendPollMessage: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
sendProduct: {
wwjs: { status: 'not-available', rootCause: 'library-limitation' },
baileys: { status: 'not-available', rootCause: 'adapter-gap' },
evidence:
'baileys AnyRegularMessageContent {product: WASendableProduct} (Types/Message.d.ts:203) built in messages.js:397 — adapter unwired (2-step: getCatalog lookup for image/title/price THEN sendMessage); wwjs no Client.sendProduct — Product/Order are inbound-only parsers',
},
sendSeen: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
sendStickerMessage: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
sendTextMessage: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
sendVideoMessage: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
setGroupDescription: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
setGroupEphemeral: {
wwjs: { status: 'not-available', rootCause: 'library-limitation' },
baileys: { status: 'supported' },
evidence:
'wwjs 1.34.7 exposes NO ephemeral setter — 0 hits for ephemeral in index.d.ts; only a create-time messageTimer option (Client.js:2371); adapter throws EngineNotSupportedError; baileys groupToggleEphemeral(jid, ephemeralExpiration) (Socket/groups.d.ts:40)',
},
setGroupInfoAdminsOnly: {
wwjs: { status: 'supported' },
baileys: { status: 'supported' },
evidence:
"wwjs GroupChat.setInfoAdminsOnly(adminsOnly?) (index.d.ts:2216; sets groupMetadata.restrict, GroupChat.js:544); baileys groupSettingUpdate(jid, 'locked'|'unlocked') (Socket/groups.d.ts:41)",
},
setGroupMessagesAdminsOnly: {
wwjs: { status: 'supported' },
baileys: { status: 'supported' },
evidence:
"wwjs GroupChat.setMessagesAdminsOnly(adminsOnly?) (index.d.ts:2210; sets groupMetadata.announce, GroupChat.js:513); baileys groupSettingUpdate(jid, 'announcement'|'not_announcement') (Socket/groups.d.ts:41)",
},
setGroupSubject: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
setProfileName: {
wwjs: { status: 'supported' },
baileys: { status: 'supported' },
evidence:
'wwjs Client.setDisplayName(displayName) → boolean (index.d.ts:251; false → adapter throws); baileys updateProfileName(name) (Socket/chats.d.ts:50)',
},
setProfilePicture: {
wwjs: { status: 'supported' },
baileys: { status: 'supported' },
evidence:
'wwjs Client.setProfilePicture(MessageMedia) → boolean (index.d.ts:336; false → adapter throws); baileys updateProfilePicture(ownJid, WAMediaUpload) (Socket/chats.d.ts:44)',
},
setProfileStatus: {
wwjs: { status: 'supported' },
baileys: { status: 'supported' },
evidence:
'wwjs Client.setStatus(status) (index.d.ts:245); baileys updateProfileStatus(status) (Socket/chats.d.ts:49)',
},
subscribeToChannel: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
unblockContact: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
unsubscribeFromChannel: { wwjs: { status: 'supported' }, baileys: { status: 'supported' } },
};
|