@oxidezap/whatsapp-rust-bridge 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -116,6 +116,19 @@ export interface AppStateSyncKey {
116
116
  timestamp: number | string;
117
117
  }
118
118
 
119
+ /** Why, and with what, a session connected without a freshly resolved version. Only the browser version source falls back this way; see the source constants in the client crate's `version` module for the reason. */
120
+ export interface AppVersionFallback {
121
+ /** The version the session actually connected with. */
122
+ version: [number, number, number];
123
+ /** True when that version is the one compiled into this library, so its staleness is the release's age. False when the device already carried a different one, whose provenance this does not claim to know: it may have been resolved earlier or supplied by the caller. */
124
+ compiled_default: boolean;
125
+ /** What stopped the resolution. Worth distinguishing, because one is routine and the other is news. */
126
+ reason: AppVersionFallbackReason;
127
+ }
128
+
129
+ /** Why a version could not be resolved. */
130
+ export type AppVersionFallbackReason = "SourceUnreachable" | "SourceUnparsable";
131
+
119
132
  export interface ArchiveUpdate {
120
133
  /** The chat being archived or unarchived. */
121
134
  jid: Jid;
@@ -346,6 +359,12 @@ export interface ConnectFailure {
346
359
  /** Wire codes: 400=Generic, 401=LoggedOut, 402=TempBanned, 403=AccountLocked, 406=UnknownLogout, 405=ClientOutdated, 409=BadUserAgent, 413=CatExpired, 414=CatInvalid, 415=NotFound, 418=ClientUnknown, 500=InternalServerError, 501=Experimental, 503=ServiceUnavailable */
347
360
  export type ConnectFailureReason = number;
348
361
 
362
+ /** The session is authenticated and has asked the server to leave passive mode. That request is best effort: a failure to go active is logged and the connection is announced anyway, on the same reasoning as below, so treat this as "the client believes stanzas should be flowing" rather than a guarantee that the server agrees. After a fresh pairing the client waits for the critical app-state collections before publishing this, so the push name and blocklist are normally in place by now. It waits, but it does not withhold: a critical collection the server refused or could not deliver is reported as [`AppStateSyncFailed`] and the connection is announced regardless, because a session already delivering messages is not one a consumer should be left believing never opened. */
363
+ export interface Connected {
364
+ /** Present when version resolution could not reach its source and the session connected on the version the device already held. Absent on every normal connect, so `Some` is the whole signal: a consumer that cares can warn, refuse, or pin a version of its own. */
365
+ app_version_fallback?: AppVersionFallback | null;
366
+ }
367
+
349
368
  /** A contact changed their phone number. Emitted from `<notification type="contacts"><modify old="..." new="..." old_lid="..." new_lid="..."/>`. The library updates the global LID-PN cache when both `old_lid` and `new_lid` are present, mirroring `WAWebDBCreateLidPnMappings`. No Signal session is wiped (WA Web `WAWebHandleContactNotification` also leaves sessions intact). Group participant updates arrive via separate `w:gp2` notifications, so per-group caches are not touched here. Consumers can subscribe and refresh their own caches if needed. */
350
369
  export interface ContactNumberChanged {
351
370
  /** Old phone number JID. */
@@ -480,21 +499,19 @@ export interface DeviceElement {
480
499
  lid?: Jid | null;
481
500
  }
482
501
 
483
- /** Device information for registry tracking. */
502
+ /** Device information for registry tracking. Packed into 8 bytes rather than the 16 the obvious three fields occupy: a `u32` device id, an `Option<u32>` key index and a `bool` carry 5 bytes of information and 11 bytes of alignment padding, and a device registry holds one of these per device per known contact. The device id is a `u16` because that is what it is on the wire — [`Jid::device`] has always been one — and the hosted flag and the key index's presence share one byte. Serialized as the `{device_id, key_index, is_hosted}` object the previous layout wrote, so stored device-list blobs are unchanged in both directions. */
484
503
  export interface DeviceInfo {
485
- /** The device ID (0 = primary device, 1+ = companion devices) */
486
504
  device_id: number;
487
- /** The key index, if known */
505
+ /** Meaningful only when [`Self::HAS_KEY_INDEX`] is set. */
488
506
  key_index?: number | null;
489
- /** Whether the device uses the hosted PN/LID address space. */
490
507
  is_hosted: boolean;
491
508
  }
492
509
 
493
- /** Device list record matching WhatsApp Web's DeviceListRecord structure. */
510
+ /** Device list record matching WhatsApp Web's DeviceListRecord structure. Serialized through a private shadow struct whose fields are the `String`, `Vec` and `Option<String>` the previous layout used. Deriving serde on the compact fields directly would stamp a second set of `Box<[T]>` and `Option<Box<str>>` codecs into every crate that persists a record, for a blob that is byte-for-byte the same either way. */
494
511
  export interface DeviceListRecord {
495
- /** The user part of the JID (phone number or LID) */
512
+ /** The user part of the JID (phone number or LID) `Arc<str>`, so the registry cache can key the record by exactly this string instead of allocating a second copy of it: every write stores the record under its own `user`, and the two used to be separate `String` allocations of identical content. */
496
513
  user: string;
497
- /** List of known devices for this user */
514
+ /** List of known devices for this user. Boxed rather than a `Vec`: the list is built once and then read for the life of the cache entry, so the capacity field is dead weight — and worse, `retain_devices_by_key_index` shortens it without releasing the capacity, leaving the dropped devices' slots resident. Mutations go through [`DeviceListRecord::edit_devices`]. */
498
515
  devices: DeviceInfo[];
499
516
  /** Timestamp when this record was last updated */
500
517
  timestamp: number | string;
@@ -706,7 +723,7 @@ export interface GroupInfo {
706
723
  addressing_mode: AddressingMode;
707
724
  /** Whether this group is a Community Announcement Group (WA Web `isCag`, derived from `default_sub_group`). `None` means the persisted blob predates the field, so the answer is unknown and callers must re-query. */
708
725
  is_community_announce?: boolean | null;
709
- /** Maps a LID user identifier (the `user` part of the LID JID) to the corresponding phone-number JID. This is used for device queries since LID usync requests may not work reliably. */
726
+ /** LID→PN mappings, sorted by the LID user part, looked up by binary search. Used for device queries, since LID usync requests may not work reliably. A sorted slice rather than a `HashMap`: a 1024-member group needs 2048 hashbrown buckets, so roughly half the map's bytes were empty slots, and the entries are read far more often than they are written (member changes arrive on notifications; lookups run once per participant per send). Serialized as the `lid_to_pn_map` object the previous layout wrote, so the persisted `group_metadata` blob is unchanged. */
710
727
  lid_to_pn_map: Record<string, Jid>;
711
728
  }
712
729
 
@@ -717,7 +734,7 @@ export type GroupNotificationAction =
717
734
  | { type: "promote"; participants: GroupParticipantInfo[] }
718
735
  | { type: "demote"; participants: GroupParticipantInfo[] }
719
736
  | { type: "modify"; participants: GroupParticipantInfo[] }
720
- | { type: "subject"; subject: string; subject_owner?: Jid | null; subject_time?: number | string | null }
737
+ | { type: "subject"; subject: string; subject_owner?: Jid | null; subject_owner_pn?: Jid | null; subject_owner_username?: string | null; subject_time?: number | string | null }
721
738
  | { type: "description"; id: string; description?: string | null }
722
739
  | { type: "locked"; threshold?: string | null }
723
740
  | { type: "unlocked" }
@@ -807,7 +824,7 @@ export interface GroupUpdate {
807
824
  is_lid_addressing_mode: boolean;
808
825
  /** Whether participant identity information was incomplete in the source stanza. */
809
826
  has_incomplete_participant_information: boolean;
810
- /** The specific action */
827
+ /** The specific action. Boxed, like the `action` of every sync-action payload in this file (`ContactUpdate`, `PinUpdate`, `MuteUpdate`, …): at 288 bytes it made `GroupUpdate` the largest variant of `Event`, and `Event` is what sizes the single `Arc` allocation every dispatch makes, group update or not. */
811
828
  action: GroupNotificationAction;
812
829
  }
813
830
 
@@ -827,6 +844,10 @@ export interface IdentityChange {
827
844
  export interface InboundMessage {
828
845
  message: import('./proto-types').proto.IMessage;
829
846
  info: MessageInfo;
847
+ /** Ephemeral duration in seconds, from the decrypted message's `contextInfo.expiration`. Lives here rather than on `info` because it is only known after decryption, and `info` is shared with every `<enc>` of the stanza by then: writing it there cost a deep copy of the whole `MessageInfo` on every disappearing-chat message. */
848
+ ephemeral_expiration?: number | null;
849
+ /** Parent post key when `message` is a decrypted CAG channel comment (`enc_comment_message`). The inner `Message` proto has no slot for the threading link, so it surfaces here. Boxed: rare. */
850
+ comment_target?: import('./proto-types').proto.IMessageKey | null;
830
851
  }
831
852
 
832
853
  export interface IncomingCall {
@@ -843,6 +864,8 @@ export interface IncomingCall {
843
864
  timestamp: number;
844
865
  offline: boolean;
845
866
  action: CallAction;
867
+ /** The rotation the sending device announced on this stanza's `<video>` child, in `0..=3`. Only an `<offer>` and an `<accept>` carry one; `None` everywhere else, and for a stanza whose value was out of range. A video-from-start peer announces its camera rotation exactly once, in that stanza, and sends no `<video>` of its own until the camera actually turns -- so dropping this leaves every frame of a call from a sideways camera stamped upright. On the payload rather than inside [`CallAction::Offer`] / [`Accept`]: those variants are plain struct variants, so a new field there breaks every consumer that destructures them without a `..` rest. This struct is `#[non_exhaustive]` with a `bon` builder, which is exactly the shape the `Event` stability policy reserves for a payload that has to grow. [`Accept`]: CallAction::Accept */
868
+ video_orientation?: number | null;
846
869
  /** Group snapshot embedded in an initial offer or active-call invitation. */
847
870
  group?: GroupCallUpdate | null;
848
871
  }
@@ -960,6 +983,7 @@ export interface MessageInfo {
960
983
  server_id: number;
961
984
  /** The envelope's `type` attribute. `None` when the stanza carried none. */
962
985
  type?: StanzaMessageType | null;
986
+ /** The sender's `notify` display name. Inline up to 24 bytes, which covers most names, so a message does not allocate for it. */
963
987
  push_name: string;
964
988
  timestamp: number;
965
989
  category: MessageCategory;
@@ -967,13 +991,14 @@ export interface MessageInfo {
967
991
  /** The `mediatype` the stanza's `<enc>` nodes declared, aggregated to one value per message. A fan-out stanza carries one `<enc>` per device and the attribute is a property of the message, not of a device copy, so the first `<enc>` that carries one wins in the order the client enumerates them: the direct `<enc>` children first, then this device's under `<participants><to>`. Divergent values across a fan-out are not reconciled and the later ones are dropped; a consumer that needs per-node values reads them from [`DecryptedPayload`](crate::types::events::DecryptedPayload). Those fan-out nodes are a wider source than WA Web's parser, which maps only the direct `<enc>` children. The two agree on every stanza seen so far, since the attribute describes the message and every device copy repeats it, so the wider read only fills the field on a stanza whose direct children carry nothing. `None` when no `<enc>` carried the attribute. */
968
992
  media_type?: EncMediaType | null;
969
993
  edit: EditAttribute;
994
+ /** The `<bot>` child. Boxed: most messages carry none. */
970
995
  bot_info?: MsgBotInfo | null;
971
- meta_info: MsgMetaInfo;
996
+ /** The `<meta>` and `<reporting>` children, `None` when the stanza carries neither. Boxed: it is 280 bytes of mostly-absent fields, and every `MessageInfo` is retained per message through the commit batch and every consumer that keeps a message. */
997
+ meta_info?: MsgMetaInfo | null;
972
998
  /** Decoded `<verified_name>` child cert of business senders; the display name is in `.name`. Boxed: most messages carry none. */
973
999
  verified_name?: VerifiedName | null;
1000
+ /** Set on a self-fanout of an own outgoing message. Boxed: rare. */
974
1001
  device_sent_meta?: DeviceSentMeta | null;
975
- /** Ephemeral duration in seconds, extracted from `contextInfo.expiration`. */
976
- ephemeral_expiration?: number | null;
977
1002
  /** Whether this message was delivered during offline sync. */
978
1003
  is_offline: boolean;
979
1004
  /** Set when this message was recovered via PDO rather than normal decryption. Contains the PDO request message ID. */
@@ -986,8 +1011,6 @@ export interface MessageInfo {
986
1011
  verified_name_serial?: number | string | null;
987
1012
  /** Envelope `peer_recipient_pn` attr. Present on companion-device self-synced DM stanzas to identify the peer's PN (so the receipt goes to the right routing target). */
988
1013
  peer_recipient_pn?: Jid | null;
989
- /** Parent post key when the dispatched message is a decrypted CAG channel comment (`enc_comment_message`). The inner `Message` proto has no slot for the threading link, so it surfaces here. */
990
- comment_target?: import('./proto-types').proto.IMessageKey | null;
991
1014
  /** Broadcast-contact-list recipients from `<participants><to jid>` on an incoming broadcast/status stanza. Populated only for broadcasts; used to validate a `deviceSentMessage.phash` (WA Web `validateBclHash`). Empty otherwise. */
992
1015
  bcl_participants: Jid[];
993
1016
  }
@@ -1111,7 +1134,7 @@ export interface MuteUpdate {
1111
1134
  from_full_sync: boolean;
1112
1135
  }
1113
1136
 
1114
- /** Wire codes: 421=StaleGroupAddressingMode, 475=NewChatMessagesCapped, 487=ParsingError, 488=UnrecognizedStanza, 489=UnrecognizedStanzaClass, 490=UnrecognizedStanzaType, 491=InvalidProtobuf, 493=InvalidHostedCompanionStanza, 495=MissingMessageSecret, 496=SignalErrorOldCounter, 499=MessageDeletedOnPeer, 500=UnhandledError, 550=UnsupportedAdminRevoke, 551=UnsupportedLIDGroup, 552=DBOperationFailed */
1137
+ /** Wire codes: 415=UnsupportedMessage, 421=StaleGroupAddressingMode, 475=NewChatMessagesCapped, 487=ParsingError, 488=UnrecognizedStanza, 489=UnrecognizedStanzaClass, 490=UnrecognizedStanzaType, 491=InvalidProtobuf, 493=InvalidHostedCompanionStanza, 495=MissingMessageSecret, 496=SignalErrorOldCounter, 499=MessageDeletedOnPeer, 500=UnhandledError, 550=UnsupportedAdminRevoke, 551=UnsupportedLIDGroup, 552=DBOperationFailed */
1115
1138
  export type NackReason = number;
1116
1139
 
1117
1140
  /** A newsletter live update notification, typically containing updated reaction counts for one or more messages. */
@@ -1142,6 +1165,14 @@ export interface OfflineSyncCompleted {
1142
1165
  count: number;
1143
1166
  }
1144
1167
 
1168
+ /** An offline backlog drain ended without its `<ib><offline>` end marker, because the connection went away first. This is the counterpart of [`OfflineSyncCompleted`], not a variant of it: the drain did not finish, the client is not caught up, and the remainder of the backlog is still queued server-side. Nothing was lost — an offline message is only acked through the aggregate receipt flush that a *completed* drain performs, so everything undelivered (and the last open batch) is redelivered on the next connection, where a fresh [`OfflineSyncPreview`] announces it. A consumer that gates "caught up" UI or startup work on [`OfflineSyncCompleted`] should treat this as "not caught up, wait for the next preview" rather than as completion. */
1169
+ export interface OfflineSyncInterrupted {
1170
+ /** What the preview announced for this drain. */
1171
+ total: number;
1172
+ /** Offline stanzas processed before the connection ended. Never larger than `total` in practice, but the server owns both numbers, so treat the pair as a progress report rather than an invariant. */
1173
+ delivered: number;
1174
+ }
1175
+
1145
1176
  /** `total` is authoritative; the per-kind counts need not sum to it. */
1146
1177
  export interface OfflineSyncPreview {
1147
1178
  total: number;
@@ -1474,6 +1505,8 @@ export interface UsyncBotPrompt {
1474
1505
 
1475
1506
  export interface UsyncBusinessResult {
1476
1507
  verified_name?: VerifiedName | null;
1508
+ /** Phone-number JID the server attaches to `<business>` when the queried user was addressed by LID. It is the only place a username lookup can learn the PN, since such a query never carries one. */
1509
+ pn_jid?: Jid | null;
1477
1510
  }
1478
1511
 
1479
1512
  export interface UsyncContactResult {
@@ -1980,7 +2013,7 @@ interface WasmWhatsAppClient {
1980
2013
 
1981
2014
  /**
1982
2015
  * A catalog product. Only `id` is guaranteed; the server omits rather than
1983
- * blanks, so an absent name is absent, not `\"\"`.
2016
+ * blanks, so an absent name is absent, not `""`.
1984
2017
  */
1985
2018
  export interface ProductResult {
1986
2019
  id: string;
@@ -1990,7 +2023,7 @@ export interface ProductResult {
1990
2023
  url?: string;
1991
2024
  /**
1992
2025
  * The link-shimmed form of `url`, carried alongside it rather than
1993
- * instead. Which to open is a consumer\'s call.
2026
+ * instead. Which to open is a consumer's call.
1994
2027
  */
1995
2028
  shimmedUrl?: string;
1996
2029
  price?: PriceResult;
@@ -2011,9 +2044,9 @@ export interface ProductResult {
2011
2044
  }
2012
2045
 
2013
2046
  /**
2014
- * A delta on the account\'s own business profile.
2047
+ * A delta on the account's own business profile.
2015
2048
  *
2016
- * Absent means \"leave alone\"; an empty value means \"clear\" (`\"\"` for text,
2049
+ * Absent means "leave alone"; an empty value means "clear" (`""` for text,
2017
2050
  * `[]` for `websites`). The core rejects a delta with nothing set.
2018
2051
  */
2019
2052
  export interface BusinessProfileUpdateInput {
@@ -2045,7 +2078,7 @@ export interface OrderProductResult {
2045
2078
  images: ProductImageResult[];
2046
2079
  /**
2047
2080
  * Empty for a product with no variants. Without it a variant order cannot
2048
- * be fulfilled: id, name and price are shared across one listing\'s
2081
+ * be fulfilled: id, name and price are shared across one listing's
2049
2082
  * variants.
2050
2083
  */
2051
2084
  variantProperties: VariantPropertyResult[];
@@ -2091,7 +2124,7 @@ export interface ParticipantChangeResult {
2091
2124
  }
2092
2125
 
2093
2126
  /**
2094
- * A postal address, as sent for a product\'s importer of record.
2127
+ * A postal address, as sent for a product's importer of record.
2095
2128
  */
2096
2129
  export interface ImporterAddressResult {
2097
2130
  street1?: string;
@@ -2103,16 +2136,16 @@ export interface ImporterAddressResult {
2103
2136
  }
2104
2137
 
2105
2138
  /**
2106
- * A price, in thousandths of the currency\'s main unit.
2139
+ * A price, in thousandths of the currency's main unit.
2107
2140
  *
2108
2141
  * WhatsApp scales money by 1000, not 100, and the protobuf field is an
2109
2142
  * `int64`. `amount1000` therefore crosses as a **string**: a `number` is exact
2110
2143
  * only below 2^53, and a large order would be silently wrong rather than
2111
- * rejected. Dividing by 1000 for display is the consumer\'s decision.
2144
+ * rejected. Dividing by 1000 for display is the consumer's decision.
2112
2145
  */
2113
2146
  export interface PriceResult {
2114
2147
  /**
2115
- * Thousandths of one currency unit: `\"1990\"` is 1.99 in `currency`.
2148
+ * Thousandths of one currency unit: `"1990"` is 1.99 in `currency`.
2116
2149
  */
2117
2150
  amount1000: string;
2118
2151
  /**
@@ -2164,6 +2197,11 @@ export interface UserInfoResult {
2164
2197
  * the server returned no device list.
2165
2198
  */
2166
2199
  devices: number[];
2200
+ /**
2201
+ * Meta username, without the display-only `@` prefix. Absent when the
2202
+ * server reported none, which is also how it reports a deleted one.
2203
+ */
2204
+ username?: string;
2167
2205
  }
2168
2206
 
2169
2207
  /**
@@ -2187,7 +2225,7 @@ export interface CommunitySubgroupResult {
2187
2225
  }
2188
2226
 
2189
2227
  /**
2190
- * Allocation churn attributed by whatsapp-rust\'s own `AllocMeter` to tasks
2228
+ * Allocation churn attributed by whatsapp-rust's own `AllocMeter` to tasks
2191
2229
  * spawned for this client. Available in diagnostics builds only.
2192
2230
  */
2193
2231
  export interface CoreAllocationSnapshotResult {
@@ -2222,7 +2260,7 @@ export interface CoreSpanAllocationSnapshot {
2222
2260
  }
2223
2261
 
2224
2262
  /**
2225
- * An admin\'s published profile on a newsletter.
2263
+ * An admin's published profile on a newsletter.
2226
2264
  */
2227
2265
  export interface NewsletterAdminProfileResult {
2228
2266
  id?: string;
@@ -2372,7 +2410,7 @@ export interface ParticipantAddRequestResult {
2372
2410
 
2373
2411
  /**
2374
2412
  * Key of an existing message targeted by `sendReaction` / `sendCommentBytes`.
2375
- * The chat JID comes from the method\'s `jid` argument; `participant` is the
2413
+ * The chat JID comes from the method's `jid` argument; `participant` is the
2376
2414
  * original sender (required for group/status targets).
2377
2415
  */
2378
2416
  export interface TargetMessageKey {
@@ -2389,7 +2427,7 @@ export type MediaType = "image" | "video" | "audio" | "document" | "sticker" | "
2389
2427
  /**
2390
2428
  * Mirrors `device_props.HistorySyncConfig`. Only fields a consumer would
2391
2429
  * realistically tune are exposed individually; partial overrides merge into
2392
- * `wacore::store::default_history_sync_config()` so callers don\'t accidentally
2430
+ * `wacore::store::default_history_sync_config()` so callers don't accidentally
2393
2431
  * drop the WA-Web-aligned support_* claims by setting just one field.
2394
2432
  */
2395
2433
  export interface DeviceHistorySyncConfig {
@@ -2406,7 +2444,7 @@ export interface DeviceHistorySyncConfig {
2406
2444
 
2407
2445
  /**
2408
2446
  * Mirrors `device_props.PlatformType`. The display value the phone shows in
2409
- * \"Linked Devices\" — and the type WhatsApp\'s server uses to decide whether
2447
+ * "Linked Devices" — and the type WhatsApp's server uses to decide whether
2410
2448
  * features like view-once are deliverable as payload or as `absent` stub.
2411
2449
  * Variant names render as `SCREAMING_SNAKE_CASE` in TS to match the proto
2412
2450
  * enum identifiers callers see in WhatsApp documentation / wire dumps.
@@ -2478,7 +2516,7 @@ export interface CommunityLinkFailureResult {
2478
2516
  export interface NewsletterFollowerResult {
2479
2517
  jid: string;
2480
2518
  /**
2481
- * Withheld by the server when the follower\'s privacy settings hide it.
2519
+ * Withheld by the server when the follower's privacy settings hide it.
2482
2520
  */
2483
2521
  phoneJid?: string;
2484
2522
  displayName?: string;
@@ -2552,10 +2590,10 @@ export interface CatalogResult {
2552
2590
  }
2553
2591
 
2554
2592
  /**
2555
- * One page of a business\'s collections.
2593
+ * One page of a business's collections.
2556
2594
  *
2557
2595
  * Forward cursor only — the collections paging object has no `before`, and
2558
- * the asymmetry with the catalog is the wire\'s, not an oversight.
2596
+ * the asymmetry with the catalog is the wire's, not an oversight.
2559
2597
  */
2560
2598
  export interface CollectionsResult {
2561
2599
  collections: CollectionResult[];
@@ -2586,7 +2624,7 @@ export interface BusinessHoursUpdateInput {
2586
2624
 
2587
2625
  /**
2588
2626
  * Optional Noise-payload overrides applied on top of every preset.
2589
- * Leaving any field `None` preserves wacore\'s default, which matches
2627
+ * Leaving any field `None` preserves wacore's default, which matches
2590
2628
  * WA Web (notably `phoneId` stays unset on the wire).
2591
2629
  */
2592
2630
  export interface ClientProfileOverrides {
@@ -2597,13 +2635,13 @@ export interface ClientProfileOverrides {
2597
2635
  }
2598
2636
 
2599
2637
  /**
2600
- * Options for `getCatalog`. Omitted fields take the core\'s own defaults; no
2638
+ * Options for `getCatalog`. Omitted fields take the core's own defaults; no
2601
2639
  * second default is applied here.
2602
2640
  */
2603
2641
  export interface CatalogOptionsInput {
2604
2642
  limit?: number | undefined;
2605
2643
  /**
2606
- * Cursor from a previous page\'s `afterCursor`.
2644
+ * Cursor from a previous page's `afterCursor`.
2607
2645
  */
2608
2646
  after?: string | undefined;
2609
2647
  imageWidth?: number | undefined;
@@ -2629,7 +2667,7 @@ export interface CollectionOptionsInput {
2629
2667
  }
2630
2668
 
2631
2669
  /**
2632
- * Per-mode colours for a bot\'s card.
2670
+ * Per-mode colours for a bot's card.
2633
2671
  */
2634
2672
  export interface BotThemeResult {
2635
2673
  mode: string;
@@ -2652,7 +2690,7 @@ export type PresenceStatus = "available" | "unavailable";
2652
2690
  * Public error shape that crosses the WASM→JS boundary.
2653
2691
  *
2654
2692
  * Variants are intentionally flat — no `#[from]` on enum variants, no
2655
- * `#[serde(flatten)]`. Translation from the core\'s typed errors happens in
2693
+ * `#[serde(flatten)]`. Translation from the core's typed errors happens in
2656
2694
  * `From` impls below by walking the source chain. This keeps the JS object
2657
2695
  * shape predictable and the codegen / `Tsify` output simple.
2658
2696
  */
@@ -2696,7 +2734,7 @@ export interface EncryptMediaResult {
2696
2734
  * Result from `fetchNewChatMessageCappingInfo`.
2697
2735
  *
2698
2736
  * Every field is optional because the server omits the ones that do not apply
2699
- * to an account\'s tier. `remainingQuota` is the core\'s own derivation, present
2737
+ * to an account's tier. `remainingQuota` is the core's own derivation, present
2700
2738
  * only when both quota fields are.
2701
2739
  */
2702
2740
  export interface NewChatMessageCappingResult {
@@ -2724,7 +2762,7 @@ export interface FetchStatusResult {
2724
2762
  *
2725
2763
  * Sections arrive as the server grouped them. The core keeps every section
2726
2764
  * because a bot can be carried by a `category` or `featured` section and by no
2727
- * other, so flattening is a consumer\'s decision, not the bridge\'s.
2765
+ * other, so flattening is a consumer's decision, not the bridge's.
2728
2766
  */
2729
2767
  export interface BotListResult {
2730
2768
  version: string;
@@ -2874,6 +2912,28 @@ export interface OrderResult {
2874
2912
  creationTimestamp?: number;
2875
2913
  }
2876
2914
 
2915
+ /**
2916
+ * Result from `getUsername`: this account's own Meta username.
2917
+ *
2918
+ * Every field is optional because the server omits the ones that do not
2919
+ * apply. An account with no username at all comes back as `null` from the
2920
+ * method rather than as an all-absent object.
2921
+ */
2922
+ export interface OwnUsernameResult {
2923
+ /**
2924
+ * The handle, without the display-only `@` prefix.
2925
+ */
2926
+ username?: string;
2927
+ /**
2928
+ * `ACTIVE` or `RESERVED`.
2929
+ */
2930
+ state?: string;
2931
+ /**
2932
+ * The numeric username key that guards lookups of this account by handle.
2933
+ */
2934
+ key?: string;
2935
+ }
2936
+
2877
2937
  /**
2878
2938
  * Result from `groupRequestParticipantsList`.
2879
2939
  */
@@ -2887,7 +2947,7 @@ export interface MembershipRequestResult {
2887
2947
  *
2888
2948
  * Mirrors the core `IsOnWhatsAppResult` so callers get the LID/PN counterpart
2889
2949
  * and business flag from the same usync round trip — no follow-up
2890
- * `fetchUserInfo` IQ needed for the common \"check + enrich\" flow.
2950
+ * `fetchUserInfo` IQ needed for the common "check + enrich" flow.
2891
2951
  */
2892
2952
  export interface IsOnWhatsAppResult {
2893
2953
  jid: string;
@@ -2906,6 +2966,11 @@ export interface IsOnWhatsAppResult {
2906
2966
  * Verified business name from the usync `<business><verified_name>` cert, if any.
2907
2967
  */
2908
2968
  verifiedName?: string;
2969
+ /**
2970
+ * Meta username, without the display-only `@` prefix. Absent when the
2971
+ * server reported none, which is also how it reports a deleted one.
2972
+ */
2973
+ username?: string;
2909
2974
  }
2910
2975
 
2911
2976
  /**
@@ -2978,14 +3043,14 @@ export interface CommunityLinkResult {
2978
3043
  /**
2979
3044
  * Selects which `ClientProfile` preset to use for the noise-handshake
2980
3045
  * `ClientPayload.UserAgent`. Independent of `DeviceProps`: `setDeviceProps`
2981
- * controls the \"Linked Devices\" display on the phone, this controls what
3046
+ * controls the "Linked Devices" display on the phone, this controls what
2982
3047
  * the server sees in the noise layer.
2983
3048
  *
2984
- * Use `{ preset: \'android\', osVersion: \'13\' }` to advertise
3049
+ * Use `{ preset: 'android', osVersion: '13' }` to advertise
2985
3050
  * `UserAgent.platform = ANDROID` with `web_info` omitted.
2986
3051
  *
2987
3052
  * Every variant flattens the [`ClientProfileOverrides`] fields, so the
2988
- * JS literal is flat (e.g. `{ preset: \'web\', phoneId: \'fixed-id\' }`).
3053
+ * JS literal is flat (e.g. `{ preset: 'web', phoneId: 'fixed-id' }`).
2989
3054
  */
2990
3055
  export type ClientProfileInput = ({ preset: "web" } & {} & ClientProfileOverrides) | ({ preset: "android" } & { osVersion: string } & ClientProfileOverrides) | ({ preset: "smbAndroid" } & { osVersion: string } & ClientProfileOverrides) | ({ preset: "ios" } & { osVersion: string } & ClientProfileOverrides) | ({ preset: "macos" } & { osVersion: string } & ClientProfileOverrides) | ({ preset: "windows" } & { osVersion: string } & ClientProfileOverrides);
2991
3056
 
@@ -3033,15 +3098,24 @@ export interface CoverPhotoUploadInput {
3033
3098
  }
3034
3099
 
3035
3100
  /**
3036
- * What the client\'s connection state means for work handed to it now.
3101
+ * What `findByUsername` learned about a Meta username.
3037
3102
  *
3038
- * The core\'s `Reachability`, carried across unflattened. A refused call says
3103
+ * Mirrors the core `UsernameLookup`, which is a three-way answer rather than
3104
+ * an optional user: a username the server confirms but will not resolve
3105
+ * without the account's username key is neither a hit nor a miss.
3106
+ */
3107
+ export type UsernameLookupResult = { status: "notFound" } | { status: "keyRequired"; username?: string } | { status: "found"; jid: string; pnJid?: string; username?: string; isBusiness: boolean; verifiedName?: string };
3108
+
3109
+ /**
3110
+ * What the client's connection state means for work handed to it now.
3111
+ *
3112
+ * The core's `Reachability`, carried across unflattened. A refused call says
3039
3113
  * what happened to that attempt; this says what the client is, which is the
3040
3114
  * only thing that answers whether asking again is worth it — so it is read
3041
3115
  * when the question comes up rather than stamped onto an error that is a fact
3042
3116
  * about one instant.
3043
3117
  *
3044
- * `unknown` is not one of the core\'s: the enum is `#[non_exhaustive]`, and a
3118
+ * `unknown` is not one of the core's: the enum is `#[non_exhaustive]`, and a
3045
3119
  * state added upstream has no name here yet. Naming the gap beats reporting it
3046
3120
  * as one of its neighbours.
3047
3121
  */
@@ -3082,7 +3156,7 @@ export interface HistorySyncAllocationSnapshot {
3082
3156
  largestCompressedBytes: number;
3083
3157
  largestDecompressedBytes: number;
3084
3158
  /**
3085
- * Compressed history events waiting in the bridge\'s ordered event queue.
3159
+ * Compressed history events waiting in the bridge's ordered event queue.
3086
3160
  */
3087
3161
  queuedEvents: number;
3088
3162
  queuedCompressedBytes: number;
@@ -3330,7 +3404,7 @@ export interface WasmAllocationSnapshot {
3330
3404
  largestAllocationBytes: number;
3331
3405
  /**
3332
3406
  * Mutually exclusive allocation-site buckets. Core task allocation is a
3333
- * second measurement through the core\'s `AllocMeter`, exposed on the
3407
+ * second measurement through the core's `AllocMeter`, exposed on the
3334
3408
  * client for cross-checking this host-side scope.
3335
3409
  */
3336
3410
  other: AllocationBucketSnapshot;
@@ -3386,6 +3460,7 @@ export type WhatsAppEvent =
3386
3460
  | { type: 'self_push_name_updated'; data: SelfPushNameUpdated }
3387
3461
  | { type: 'offline_sync_preview'; data: OfflineSyncPreview }
3388
3462
  | { type: 'offline_sync_completed'; data: OfflineSyncCompleted }
3463
+ | { type: 'offline_sync_interrupted'; data: OfflineSyncInterrupted }
3389
3464
  | { type: 'dirty_state'; data: { dirty_type: DirtyType; timestamp?: number | null } }
3390
3465
  | { type: 'device_list_update'; data: DeviceListUpdate }
3391
3466
  | { type: 'identity_change'; data: IdentityChange }
@@ -3678,6 +3753,19 @@ export class WasmWhatsAppClient {
3678
3753
  * Fetch user status/about text for one or more JIDs.
3679
3754
  */
3680
3755
  fetchStatus(jids: string[]): Promise<FetchStatusResult[]>;
3756
+ /**
3757
+ * Resolve a Meta username to the account behind it.
3758
+ *
3759
+ * **Experimental.** The core builds the request exactly as WhatsApp Web
3760
+ * does, but no capture of a server answering it backs the implementation,
3761
+ * so a rejection here is not necessarily a bug.
3762
+ *
3763
+ * `username` is the bare handle; a leading `@` is display-only and the
3764
+ * core strips it. `usernameKey` is the account's numeric username key,
3765
+ * which some accounts require before the server discloses an identity at
3766
+ * all — without it the answer is `{ status: "keyRequired" }`.
3767
+ */
3768
+ findByUsername(username: string, username_key?: string | null): Promise<UsernameLookupResult>;
3681
3769
  /**
3682
3770
  * Get the ADV signed device identity (account), if available.
3683
3771
  * Exposes the persisted account identity to credential consumers.
@@ -3763,6 +3851,15 @@ export class WasmWhatsAppClient {
3763
3851
  * query takes neither.
3764
3852
  */
3765
3853
  getUSyncDevices(jids: string[], _use_cache: boolean, _ignore_zero_devices: boolean): Promise<any>;
3854
+ /**
3855
+ * Read this account's own Meta username, its state and its username key.
3856
+ *
3857
+ * `null` means no username is set: the server answers 404 and the core
3858
+ * reads it that way. Only the read is exposed — setting a username or its
3859
+ * key changes the account's identity in a way the server does not undo,
3860
+ * so the core leaves those two MEX operations unwrapped.
3861
+ */
3862
+ getUsername(): Promise<OwnUsernameResult | undefined>;
3766
3863
  /**
3767
3864
  * Join a group using an invite code.
3768
3865
  */
@@ -4579,8 +4676,8 @@ export interface InitOutput {
4579
4676
  readonly decryptPollVotePayload: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number, l: number, m: number) => void;
4580
4677
  readonly encodeSenderKeyRecordComponents: (a: number, b: number) => void;
4581
4678
  readonly encodeSessionRecordComponents: (a: number, b: number) => void;
4582
- readonly generateKeyPair: () => number;
4583
- readonly getEnabledFeatures: () => number;
4679
+ readonly generateKeyPair: (a: number) => void;
4680
+ readonly getEnabledFeatures: (a: number) => void;
4584
4681
  readonly getPublicFromPrivateKey: (a: number, b: number, c: number) => void;
4585
4682
  readonly getWasmAllocationSnapshot: (a: number) => void;
4586
4683
  readonly hasLogger: () => number;
@@ -4629,13 +4726,14 @@ export interface InitOutput {
4629
4726
  readonly wasmwhatsappclient_fetchReachoutTimelock: (a: number) => number;
4630
4727
  readonly wasmwhatsappclient_fetchStatus: (a: number, b: number, c: number) => number;
4631
4728
  readonly wasmwhatsappclient_fetchUserInfo: (a: number, b: number, c: number) => number;
4729
+ readonly wasmwhatsappclient_findByUsername: (a: number, b: number, c: number, d: number, e: number) => number;
4632
4730
  readonly wasmwhatsappclient_getAccount: (a: number) => number;
4633
4731
  readonly wasmwhatsappclient_getBotList: (a: number) => number;
4634
4732
  readonly wasmwhatsappclient_getBusinessProfile: (a: number, b: number, c: number) => number;
4635
4733
  readonly wasmwhatsappclient_getCatalog: (a: number, b: number, c: number, d: number) => number;
4636
4734
  readonly wasmwhatsappclient_getCollections: (a: number, b: number, c: number, d: number) => number;
4637
4735
  readonly wasmwhatsappclient_getCommunitySubgroups: (a: number, b: number, c: number) => number;
4638
- readonly wasmwhatsappclient_getCoreAllocationSnapshot: (a: number) => number;
4736
+ readonly wasmwhatsappclient_getCoreAllocationSnapshot: (a: number, b: number) => void;
4639
4737
  readonly wasmwhatsappclient_getGroupMetadata: (a: number, b: number, c: number) => number;
4640
4738
  readonly wasmwhatsappclient_getJid: (a: number) => number;
4641
4739
  readonly wasmwhatsappclient_getLid: (a: number) => number;
@@ -4644,6 +4742,7 @@ export interface InitOutput {
4644
4742
  readonly wasmwhatsappclient_getOrder: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number;
4645
4743
  readonly wasmwhatsappclient_getPushName: (a: number) => number;
4646
4744
  readonly wasmwhatsappclient_getUSyncDevices: (a: number, b: number, c: number, d: number, e: number) => number;
4745
+ readonly wasmwhatsappclient_getUsername: (a: number) => number;
4647
4746
  readonly wasmwhatsappclient_groupAcceptInvite: (a: number, b: number, c: number) => number;
4648
4747
  readonly wasmwhatsappclient_groupAcceptInviteV4: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => number;
4649
4748
  readonly wasmwhatsappclient_groupFetchAllParticipating: (a: number) => number;
@@ -4698,7 +4797,7 @@ export interface InitOutput {
4698
4797
  readonly wasmwhatsappclient_profilePictureUrl: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
4699
4798
  readonly wasmwhatsappclient_queryNode: (a: number, b: number, c: number, d: number) => number;
4700
4799
  readonly wasmwhatsappclient_queryUsync: (a: number, b: number) => number;
4701
- readonly wasmwhatsappclient_reachability: (a: number) => number;
4800
+ readonly wasmwhatsappclient_reachability: (a: number, b: number) => void;
4702
4801
  readonly wasmwhatsappclient_readMessages: (a: number, b: number) => number;
4703
4802
  readonly wasmwhatsappclient_reconnect: (a: number) => number;
4704
4803
  readonly wasmwhatsappclient_refreshPreKeys: (a: number, b: number) => number;
@@ -4784,12 +4883,12 @@ export interface InitOutput {
4784
4883
  readonly intounderlyingsink_write: (a: number, b: number) => number;
4785
4884
  readonly intounderlyingsource_cancel: (a: number) => void;
4786
4885
  readonly intounderlyingsource_pull: (a: number, b: number) => number;
4787
- readonly __wasm_bindgen_func_elem_4417: (a: number, b: number, c: number) => void;
4788
- readonly __wasm_bindgen_func_elem_26574: (a: number, b: number, c: number, d: number) => void;
4789
- readonly __wasm_bindgen_func_elem_26576: (a: number, b: number, c: number, d: number) => void;
4790
- readonly __wasm_bindgen_func_elem_9408: (a: number, b: number, c: number) => void;
4791
- readonly __wasm_bindgen_func_elem_4416: (a: number, b: number, c: number) => void;
4792
- readonly __wasm_bindgen_func_elem_4415: (a: number, b: number) => void;
4886
+ readonly __wasm_bindgen_func_elem_4571: (a: number, b: number, c: number) => void;
4887
+ readonly __wasm_bindgen_func_elem_27550: (a: number, b: number, c: number, d: number) => void;
4888
+ readonly __wasm_bindgen_func_elem_27552: (a: number, b: number, c: number, d: number) => void;
4889
+ readonly __wasm_bindgen_func_elem_9675: (a: number, b: number, c: number) => void;
4890
+ readonly __wasm_bindgen_func_elem_4570: (a: number, b: number, c: number) => void;
4891
+ readonly __wasm_bindgen_func_elem_4569: (a: number, b: number) => void;
4793
4892
  readonly __wbindgen_export: (a: number, b: number) => number;
4794
4893
  readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
4795
4894
  readonly __wbindgen_export3: (a: number) => void;
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxidezap/whatsapp-rust-bridge",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "description": "A high-performance utilities for WhatsApp, powered by Rust and WebAssembly.",
5
5
  "author": "João Lucas <jlucaso@hotmail.com>",
6
6
  "license": "MIT",
@@ -64,6 +64,7 @@
64
64
  "@bufbuild/protobuf": "^2.12.0",
65
65
  "@types/bun": "^1.3.13",
66
66
  "@types/node": "^25.6.0",
67
+ "pkg-pr-new": "^0.0.88",
67
68
  "ts-proto": "^2.11.6",
68
69
  "typescript": "^6.0.3"
69
70
  },