@palbase/web 1.3.0 → 1.5.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.
- package/dist/{analytics-facade-Cq7R-VH_.d.ts → analytics-facade-BQlTfsEm.d.ts} +168 -4
- package/dist/{analytics-facade-B8UATgS4.d.cts → analytics-facade-DvsLE_qz.d.cts} +168 -4
- package/dist/{chunk-UX43AB4W.js → chunk-MBA2NAKS.js} +694 -44
- package/dist/chunk-MBA2NAKS.js.map +1 -0
- package/dist/index.cjs +693 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/internal.cjs +693 -43
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +3 -3
- package/dist/internal.d.ts +3 -3
- package/dist/internal.js +1 -1
- package/dist/next/client.cjs +693 -43
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.js +1 -1
- package/dist/next/index.cjs +693 -43
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +2 -2
- package/dist/next/index.d.ts +2 -2
- package/dist/next/index.js +1 -1
- package/dist/{pb-DSJiH1uV.d.ts → pb-Cl7TeyCR.d.ts} +1 -1
- package/dist/{pb-DivIiUGR.d.cts → pb-a86jrG9h.d.cts} +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-UX43AB4W.js.map +0 -1
|
@@ -710,6 +710,20 @@ type ChatRole = 'owner' | 'admin' | 'member';
|
|
|
710
710
|
type MessageDirection = 'incoming' | 'outgoing';
|
|
711
711
|
/** A renderable message kind. `system` = a membership commit / control frame. */
|
|
712
712
|
type ChatMessageKind = 'text' | 'media' | 'system';
|
|
713
|
+
/**
|
|
714
|
+
* A resolved mention span inside a message body. Produced at decode/merge time by
|
|
715
|
+
* NORMALIZING the wire `body_ranges` (the pinned `normalizeMentionRangesUtf16`
|
|
716
|
+
* contract) against the message text, then resolving the `mentionedUserId` to a
|
|
717
|
+
* roster display name. Offsets are UTF-16 code units, half-open `[start, start+length)`.
|
|
718
|
+
* `displayName` is `null` when the mentioned user is not in the local roster (the
|
|
719
|
+
* renderer falls back to the underlying `text` slice). Mirrors iOS `ResolvedMention`.
|
|
720
|
+
*/
|
|
721
|
+
interface ResolvedMention {
|
|
722
|
+
readonly start: number;
|
|
723
|
+
readonly length: number;
|
|
724
|
+
readonly mentionedUserId: string;
|
|
725
|
+
readonly displayName: string | null;
|
|
726
|
+
}
|
|
713
727
|
/** A participating USER (not a device). For a direct chat: `[me, peer]`. */
|
|
714
728
|
interface ChatMember {
|
|
715
729
|
/** == userId (stable identity). */
|
|
@@ -764,6 +778,25 @@ interface ChatMessage {
|
|
|
764
778
|
* a visible message of its own — it only overlays the target's text + flag.
|
|
765
779
|
*/
|
|
766
780
|
readonly edited: boolean;
|
|
781
|
+
/**
|
|
782
|
+
* Whether this message has been deleted-for-everyone (a cooperative encrypted
|
|
783
|
+
* tombstone from the original sender). Defaults to `false`. Once a valid
|
|
784
|
+
* tombstone folds onto this message it stays `true` (absorbing/sticky within
|
|
785
|
+
* retention). A deleted message renders ONLY the neutral "deleted" descriptor
|
|
786
|
+
* as its `text`, with reactions + reply + edit HIDDEN — delete DOMINATES edit.
|
|
787
|
+
* A delete is never a visible message of its own. (delete-for-me is a separate,
|
|
788
|
+
* local-only suppression that omits the message from this view entirely.)
|
|
789
|
+
*/
|
|
790
|
+
readonly isDeleted: boolean;
|
|
791
|
+
/**
|
|
792
|
+
* The resolved mention spans on this message (defaults to `[]`). A mention is a
|
|
793
|
+
* NORMAL bubble — these spans only annotate the existing `text` for highlighting,
|
|
794
|
+
* they never make a message non-bubble. Recomputed by the Chat as the roster
|
|
795
|
+
* resolves (display names are live, not snapshotted). When this message is
|
|
796
|
+
* tombstoned (deleted-for-everyone) the spans are scrubbed to `[]` (delete
|
|
797
|
+
* DOMINATES); when it is edited they reflect the EDIT's replacement `body_ranges`.
|
|
798
|
+
*/
|
|
799
|
+
readonly mentions: ResolvedMention[];
|
|
767
800
|
}
|
|
768
801
|
/** The receipt from a send — the server's monotonic sequence + accepted epoch. */
|
|
769
802
|
interface SentReceipt {
|
|
@@ -804,6 +837,15 @@ interface ReplyRef {
|
|
|
804
837
|
client_msg_id: string;
|
|
805
838
|
preview?: QuotePreview;
|
|
806
839
|
}
|
|
840
|
+
/** A single mention range inside a message body.
|
|
841
|
+
* Offsets are UTF-16 code units, half-open [start, start+length).
|
|
842
|
+
* Wire: snake_case (mentioned_user_id, body_ranges) — web does RAW JSON, no case-convert.
|
|
843
|
+
*/
|
|
844
|
+
interface MentionRange {
|
|
845
|
+
start: number;
|
|
846
|
+
length: number;
|
|
847
|
+
mentionedUserId: string;
|
|
848
|
+
}
|
|
807
849
|
interface ReactionPayload {
|
|
808
850
|
targetClientMsgId: string;
|
|
809
851
|
emoji: string;
|
|
@@ -813,6 +855,10 @@ interface EditPayload {
|
|
|
813
855
|
targetClientMsgId: string;
|
|
814
856
|
newText: string;
|
|
815
857
|
}
|
|
858
|
+
interface DeletePayload {
|
|
859
|
+
targetClientMsgId: string;
|
|
860
|
+
scope: string;
|
|
861
|
+
}
|
|
816
862
|
|
|
817
863
|
/** A decoded incoming message handed to chat listeners. */
|
|
818
864
|
interface IncomingMessage {
|
|
@@ -839,6 +885,14 @@ interface IncomingMessage {
|
|
|
839
885
|
/** The decoded edit payload, present only when `envelopeType === 'edit'`. Lets
|
|
840
886
|
* the Chat route an edit into its EditFold instead of appending a visible bubble. */
|
|
841
887
|
edit?: EditPayload | null;
|
|
888
|
+
/** The decoded delete payload, present only when `envelopeType === 'delete'`. Lets
|
|
889
|
+
* the Chat route a delete-for-everyone tombstone into its DeleteFold instead of
|
|
890
|
+
* appending a visible bubble. */
|
|
891
|
+
delete?: DeletePayload | null;
|
|
892
|
+
/** The decoded mention ranges (raw, un-normalized). Present on a `'text'` bubble
|
|
893
|
+
* with `body_ranges` OR on an `'edit'` (the edit's replacement ranges). The Chat
|
|
894
|
+
* normalizes + resolves roster names → `ChatMessage.mentions` (mentions T6). */
|
|
895
|
+
bodyRanges?: MentionRange[] | null;
|
|
842
896
|
}
|
|
843
897
|
|
|
844
898
|
/** The intended participants of a draft chat (held until the first send). */
|
|
@@ -858,7 +912,7 @@ interface ChatBackend {
|
|
|
858
912
|
selfUserId: string;
|
|
859
913
|
/** Materialize a draft → the active group (DM get-or-create / group create+add). */
|
|
860
914
|
materialize(draft: ChatDraft): Promise<MessagingGroup>;
|
|
861
|
-
sendText(group: MessagingGroup, text: string, replyTo?: ReplyRef | null): Promise<{
|
|
915
|
+
sendText(group: MessagingGroup, text: string, replyTo?: ReplyRef | null, bodyRanges?: MentionRange[] | null): Promise<{
|
|
862
916
|
receipt: SentReceipt;
|
|
863
917
|
clientMsgId: string;
|
|
864
918
|
}>;
|
|
@@ -879,6 +933,17 @@ interface ChatBackend {
|
|
|
879
933
|
clientMsgId: string;
|
|
880
934
|
targetClientMsgId: string;
|
|
881
935
|
newText: string;
|
|
936
|
+
bodyRanges?: MentionRange[] | null;
|
|
937
|
+
}): Promise<{
|
|
938
|
+
receipt: SentReceipt;
|
|
939
|
+
clientMsgId: string;
|
|
940
|
+
}>;
|
|
941
|
+
/** Send a delete-for-everyone tombstone on a target message through the same MLS
|
|
942
|
+
* application path as `sendText` (the server stays blind — a delete is just
|
|
943
|
+
* another opaque application message). Returns the receipt + the wire clientMsgId. */
|
|
944
|
+
sendDelete(group: MessagingGroup, args: {
|
|
945
|
+
clientMsgId: string;
|
|
946
|
+
targetClientMsgId: string;
|
|
882
947
|
}): Promise<{
|
|
883
948
|
receipt: SentReceipt;
|
|
884
949
|
clientMsgId: string;
|
|
@@ -895,6 +960,15 @@ interface ChatBackend {
|
|
|
895
960
|
subscribeLive(group: MessagingGroup, chat: Chat): Unsubscribe$1;
|
|
896
961
|
/** Resolve a sender device id → its owning user id (for senderUserId). */
|
|
897
962
|
userIdForDevice(group: MessagingGroup, deviceId: string): Promise<string | null>;
|
|
963
|
+
/** Load this chat's persisted delete-for-me suppression keys (durable, no wire). */
|
|
964
|
+
loadSuppressed(group: MessagingGroup): Promise<string[]>;
|
|
965
|
+
/** Persist this chat's delete-for-me suppression keys (durable, no wire). */
|
|
966
|
+
saveSuppressed(group: MessagingGroup, keys: string[]): Promise<void>;
|
|
967
|
+
/** Load this chat's persisted self-elevation dedup keys (durable, no wire) so a
|
|
968
|
+
* re-delivered mention does not re-fire `onMentionElevation` after a cold launch. */
|
|
969
|
+
loadElevated(group: MessagingGroup): Promise<string[]>;
|
|
970
|
+
/** Persist this chat's self-elevation dedup keys (durable, no wire). */
|
|
971
|
+
saveElevated(group: MessagingGroup, keys: string[]): Promise<void>;
|
|
898
972
|
}
|
|
899
973
|
declare class Chat {
|
|
900
974
|
/** Stable, URL/log-safe id (the grp_ display id once active; a reserved local id while draft). */
|
|
@@ -917,6 +991,22 @@ declare class Chat {
|
|
|
917
991
|
private readonly reactionFold;
|
|
918
992
|
/** The single authoritative edit fold for this chat (live + own-send + history). */
|
|
919
993
|
private readonly editFold;
|
|
994
|
+
/** The single authoritative delete-for-everyone fold (live + own-send + history).
|
|
995
|
+
* A tombstone scrubs its target in place (delete DOMINATES edit at render). */
|
|
996
|
+
private readonly deleteFold;
|
|
997
|
+
/** delete-for-me suppression keys (clientMsgId, or `seq:<serverSeq>` for legacy)
|
|
998
|
+
* — the message is OMITTED from this view. Local + persisted per chat, NO wire. */
|
|
999
|
+
private readonly suppressed;
|
|
1000
|
+
/** True once the persisted suppression set has been loaded (so the omit applies
|
|
1001
|
+
* even on the cold-launch hydrate path before a fresh deleteForMe). */
|
|
1002
|
+
private suppressedLoaded;
|
|
1003
|
+
/** Self-elevation dedup keys (`<selfUserId>|<clientMsgId or seq:n>`). Once a
|
|
1004
|
+
* mention of me from another sender fires `onMentionElevation`, its key lands here
|
|
1005
|
+
* + is persisted, so a re-delivery / cold-launch re-hydrate never re-fires. */
|
|
1006
|
+
private readonly elevated;
|
|
1007
|
+
/** True once the persisted elevation set has been loaded (so a re-delivered mention
|
|
1008
|
+
* on the cold-launch hydrate path dedups against the persisted decision). */
|
|
1009
|
+
private elevatedLoaded;
|
|
920
1010
|
/** Per-target BASE (original) text, so the rendered text is `edit ?? original`.
|
|
921
1011
|
* Seeded once per target (write-once base) so a 2nd own edit can't corrupt it. */
|
|
922
1012
|
private readonly originalTextByClientMsgId;
|
|
@@ -928,6 +1018,14 @@ declare class Chat {
|
|
|
928
1018
|
private wired;
|
|
929
1019
|
private liveUnsub;
|
|
930
1020
|
private readonly listeners;
|
|
1021
|
+
/**
|
|
1022
|
+
* Fires ONCE per `(selfUserId, clientMsgId)` when an INCOMING message mentions THIS
|
|
1023
|
+
* user from ANOTHER sender (not an edit). The dedup survives re-delivery + reload
|
|
1024
|
+
* via the persisted elevation set, so this never double-fires for one mention. The
|
|
1025
|
+
* app wires it to a buzz/badge (e.g. an in-app banner). Best-effort cooperative —
|
|
1026
|
+
* the SDK guarantees the DECISION, not the buzz. Mirrors iOS `Chat.onMentionElevation`.
|
|
1027
|
+
*/
|
|
1028
|
+
onMentionElevation?: (message: ChatMessage) => void;
|
|
931
1029
|
/** @internal — obtain via pb.messaging.directChat / groupChat / chat(id). */
|
|
932
1030
|
constructor(args: {
|
|
933
1031
|
group: MessagingGroup;
|
|
@@ -948,9 +1046,29 @@ declare class Chat {
|
|
|
948
1046
|
get typing(): readonly ChatMember[];
|
|
949
1047
|
get lastMessage(): ChatMessage | null;
|
|
950
1048
|
get unreadCount(): number;
|
|
1049
|
+
/**
|
|
1050
|
+
* The RENDER PRECEDENCE — the single composition point (live AND history project
|
|
1051
|
+
* through it identically). Over the raw `messageList` (which already carries the
|
|
1052
|
+
* folded edit text + reactions + reply):
|
|
1053
|
+
* (1) in the delete-for-me suppression set → OMIT the message entirely;
|
|
1054
|
+
* (2) else tombstoned (delete-for-everyone) → the neutral "deleted" descriptor
|
|
1055
|
+
* with reactions/reply/edit HIDDEN (delete DOMINATES edit — short-circuit);
|
|
1056
|
+
* (3) else the row as-is (edit overlay + reactions + reply already applied).
|
|
1057
|
+
* Pure over (messageList, deleteFold, suppressed) — recomputed on every read so a
|
|
1058
|
+
* just-folded delete / just-suppressed key takes effect without rewriting rows.
|
|
1059
|
+
*/
|
|
1060
|
+
private surfaced;
|
|
1061
|
+
/** The delete-for-me suppression key: clientMsgId when present, else `seq:<n>`. */
|
|
1062
|
+
private suppressionKey;
|
|
951
1063
|
get title(): string;
|
|
952
1064
|
presence(userId: string): PresenceState | null;
|
|
953
1065
|
private ensureWired;
|
|
1066
|
+
/** Hydrate the persisted delete-for-me suppression keys (once), then re-emit so
|
|
1067
|
+
* any already-surfaced suppressed message is omitted (cold-launch parity). */
|
|
1068
|
+
private loadSuppressed;
|
|
1069
|
+
/** Hydrate the persisted self-elevation dedup keys (once). No re-emit: the set only
|
|
1070
|
+
* gates the elevation DECISION, it does not change what renders. */
|
|
1071
|
+
private loadElevated;
|
|
954
1072
|
private hydrateHistory;
|
|
955
1073
|
private mergeHistory;
|
|
956
1074
|
/** @internal — called by the backend's live subscription. */
|
|
@@ -959,6 +1077,31 @@ declare class Chat {
|
|
|
959
1077
|
* (null = target unknown/dangling → the fold HOLDs). Captured as a bound arrow
|
|
960
1078
|
* so it can be passed to the pure EditFold. */
|
|
961
1079
|
private readonly authorOfTarget;
|
|
1080
|
+
/** Resolve a message's decode-time mention spans: NORMALIZE the raw `body_ranges`
|
|
1081
|
+
* against `text` (the pinned `normalizeMentionRangesUtf16` cross-SDK contract) then
|
|
1082
|
+
* resolve each surviving range's `mentionedUserId` to a roster display name. An id
|
|
1083
|
+
* not in the roster resolves to `null` (the renderer falls back to the `text` slice).
|
|
1084
|
+
* Pure over (text, bodyRanges, memberCache); never throws. Mirrors iOS T3. */
|
|
1085
|
+
private resolveMentions;
|
|
1086
|
+
/** Re-resolve the roster display name on already-NORMALIZED spans (the history
|
|
1087
|
+
* projection produces them with null names — resolution is LIVE, not snapshotted).
|
|
1088
|
+
* A member rename then reflects on old messages. Returns the message unchanged when
|
|
1089
|
+
* it has no mentions (the common case) or no name changed. Mirrors iOS T3. */
|
|
1090
|
+
private resolveMentionNames;
|
|
1091
|
+
/** The WINNING edit's resolved mentions for a target (normalize its replacement
|
|
1092
|
+
* ranges against the new text + roster names), or `[]` if no winning edit / no
|
|
1093
|
+
* ranges. The edited message's mentions reflect the EDIT's ranges (mirrors iOS T3). */
|
|
1094
|
+
private editMentions;
|
|
1095
|
+
/** Resolve a userId → its roster display name (null if not a known member). */
|
|
1096
|
+
private displayNameOf;
|
|
1097
|
+
/** Compute the SELF-ELEVATION decision for a freshly-ingested INCOMING bubble and,
|
|
1098
|
+
* when it fires, record the dedup key (persisted) + invoke `onMentionElevation`.
|
|
1099
|
+
* Gate (mirrors iOS T3): a surviving mention targets THIS user AND the sender is not
|
|
1100
|
+
* me AND it's NOT an edit AND the `(selfUserId, clientMsgId|seq)` key isn't already
|
|
1101
|
+
* elevated. Dedup-once: the in-memory set gates the session, the persisted set
|
|
1102
|
+
* survives reload. An EDIT never reaches here (it folds, not a bubble) — the
|
|
1103
|
+
* `envelopeType !== 'edit'` guard is belt-and-braces. */
|
|
1104
|
+
private elevateIfMentioned;
|
|
962
1105
|
/** Seed the per-target base text + author for the edit fold. Base is write-once
|
|
963
1106
|
* (a later own/peer edit must not overwrite the original we render against). The
|
|
964
1107
|
* author is (re)recorded whenever a non-empty resolution is available. */
|
|
@@ -999,10 +1142,14 @@ declare class Chat {
|
|
|
999
1142
|
/** Page older messages in. Returns how many were prepended. */
|
|
1000
1143
|
loadEarlier(limit?: number): Promise<number>;
|
|
1001
1144
|
private refreshMembers;
|
|
1145
|
+
/** Re-resolve roster display names across the whole transcript (called on a roster
|
|
1146
|
+
* change). Re-emits only if any name actually changed. */
|
|
1147
|
+
private reresolveAllMentionNames;
|
|
1002
1148
|
private seedMembersFromGroup;
|
|
1003
1149
|
private seedDraftMembers;
|
|
1004
1150
|
send(text: string, opts?: {
|
|
1005
1151
|
replyTo?: ChatMessage;
|
|
1152
|
+
mentions?: MentionRange[];
|
|
1006
1153
|
}): Promise<SentReceipt>;
|
|
1007
1154
|
private appendOwnSend;
|
|
1008
1155
|
private materializeIfNeeded;
|
|
@@ -1026,8 +1173,25 @@ declare class Chat {
|
|
|
1026
1173
|
* instantly; the durable echo on the next pump is a fold no-op (dedup on the
|
|
1027
1174
|
* SAME wire clientMsgId). Never appends a bubble; PRESERVES the target's
|
|
1028
1175
|
* reactions + reply context. Only the original author's edits count — for an own
|
|
1029
|
-
* message self IS the author, so the author-gate passes.
|
|
1030
|
-
|
|
1176
|
+
* message self IS the author, so the author-gate passes. `opts.mentions` carries the
|
|
1177
|
+
* edit's REPLACEMENT mention ranges → the edited message's mentions reflect them. */
|
|
1178
|
+
edit(message: ChatMessage, newText: string, opts?: {
|
|
1179
|
+
mentions?: MentionRange[];
|
|
1180
|
+
}): Promise<void>;
|
|
1181
|
+
/** Delete a message for EVERYONE (a cooperative encrypted tombstone). Only the
|
|
1182
|
+
* ORIGINAL SENDER can do this — for an own message self IS the author, so the
|
|
1183
|
+
* author-gate passes. Legacy messages (empty clientMsgId) are DISABLED (a
|
|
1184
|
+
* tombstone keys on the target's clientMsgId, which they lack) — no-op. Sends a
|
|
1185
|
+
* `type:'delete'` envelope through the SAME MLS path as a text message (the
|
|
1186
|
+
* server stays blind), folds the own delete locally so the target scrubs in
|
|
1187
|
+
* place instantly (the durable echo dedups on the SAME wire clientMsgId), and
|
|
1188
|
+
* re-emits. NEVER appends a bubble. delete-for-me'ing the target becomes moot. */
|
|
1189
|
+
deleteForEveryone(message: ChatMessage): Promise<void>;
|
|
1190
|
+
/** Delete a message for ME only — a LOCAL, per-device suppression. NO wire, NO
|
|
1191
|
+
* attribution, no server contact: the message is OMITTED from THIS view and the
|
|
1192
|
+
* suppression key persists per chat (survives reload). The key is the message's
|
|
1193
|
+
* clientMsgId when present, else `seq:<serverSeq>` for legacy messages. */
|
|
1194
|
+
deleteForMe(message: ChatMessage): Promise<void>;
|
|
1031
1195
|
}
|
|
1032
1196
|
|
|
1033
1197
|
declare class PalbeMessaging {
|
|
@@ -1419,4 +1583,4 @@ declare class PalbeAnalytics {
|
|
|
1419
1583
|
private post;
|
|
1420
1584
|
}
|
|
1421
1585
|
|
|
1422
|
-
export { type AnalyticsProperties as A, type
|
|
1586
|
+
export { type AnalyticsProperties as A, type PresenceState as B, Call as C, type RealtimeConnectionState as D, type RealtimeHandler as E, type FlagsView as F, type RealtimePayload as G, type RealtimeStatus as H, type RealtimeStatusSnapshot as I, type RealtimeSubscription as J, type ResolvedMention as K, type ResolvedReply as L, type MagicLinkResult as M, type PalbeRuntime as N, type OAuthExchangeResult as O, PalbeAnalytics as P, buildRuntime as Q, RealtimeChannel as R, type SentReceipt as S, type Unsubscribe as U, type AuthChangeEvent as a, type AuthState as b, type AuthSuccess as c, type AuthUser as d, type CallChangeCallback as e, type CallParticipant as f, type CallState as g, Chat as h, type ChatBackend as i, type ChatDraft as j, type ChatKind as k, type ChatMember as l, type ChatMessage as m, type ChatMessageKind as n, type ChatRole as o, type ChatState as p, type CommsPrefs as q, type MentionRange as r, type MessageDirection as s, PalbeAuth as t, PalbeCalls as u, type PalbeConfig as v, PalbeFlags as w, PalbeMessaging as x, type PalbeOAuthConfig as y, PalbeRealtime as z };
|
|
@@ -710,6 +710,20 @@ type ChatRole = 'owner' | 'admin' | 'member';
|
|
|
710
710
|
type MessageDirection = 'incoming' | 'outgoing';
|
|
711
711
|
/** A renderable message kind. `system` = a membership commit / control frame. */
|
|
712
712
|
type ChatMessageKind = 'text' | 'media' | 'system';
|
|
713
|
+
/**
|
|
714
|
+
* A resolved mention span inside a message body. Produced at decode/merge time by
|
|
715
|
+
* NORMALIZING the wire `body_ranges` (the pinned `normalizeMentionRangesUtf16`
|
|
716
|
+
* contract) against the message text, then resolving the `mentionedUserId` to a
|
|
717
|
+
* roster display name. Offsets are UTF-16 code units, half-open `[start, start+length)`.
|
|
718
|
+
* `displayName` is `null` when the mentioned user is not in the local roster (the
|
|
719
|
+
* renderer falls back to the underlying `text` slice). Mirrors iOS `ResolvedMention`.
|
|
720
|
+
*/
|
|
721
|
+
interface ResolvedMention {
|
|
722
|
+
readonly start: number;
|
|
723
|
+
readonly length: number;
|
|
724
|
+
readonly mentionedUserId: string;
|
|
725
|
+
readonly displayName: string | null;
|
|
726
|
+
}
|
|
713
727
|
/** A participating USER (not a device). For a direct chat: `[me, peer]`. */
|
|
714
728
|
interface ChatMember {
|
|
715
729
|
/** == userId (stable identity). */
|
|
@@ -764,6 +778,25 @@ interface ChatMessage {
|
|
|
764
778
|
* a visible message of its own — it only overlays the target's text + flag.
|
|
765
779
|
*/
|
|
766
780
|
readonly edited: boolean;
|
|
781
|
+
/**
|
|
782
|
+
* Whether this message has been deleted-for-everyone (a cooperative encrypted
|
|
783
|
+
* tombstone from the original sender). Defaults to `false`. Once a valid
|
|
784
|
+
* tombstone folds onto this message it stays `true` (absorbing/sticky within
|
|
785
|
+
* retention). A deleted message renders ONLY the neutral "deleted" descriptor
|
|
786
|
+
* as its `text`, with reactions + reply + edit HIDDEN — delete DOMINATES edit.
|
|
787
|
+
* A delete is never a visible message of its own. (delete-for-me is a separate,
|
|
788
|
+
* local-only suppression that omits the message from this view entirely.)
|
|
789
|
+
*/
|
|
790
|
+
readonly isDeleted: boolean;
|
|
791
|
+
/**
|
|
792
|
+
* The resolved mention spans on this message (defaults to `[]`). A mention is a
|
|
793
|
+
* NORMAL bubble — these spans only annotate the existing `text` for highlighting,
|
|
794
|
+
* they never make a message non-bubble. Recomputed by the Chat as the roster
|
|
795
|
+
* resolves (display names are live, not snapshotted). When this message is
|
|
796
|
+
* tombstoned (deleted-for-everyone) the spans are scrubbed to `[]` (delete
|
|
797
|
+
* DOMINATES); when it is edited they reflect the EDIT's replacement `body_ranges`.
|
|
798
|
+
*/
|
|
799
|
+
readonly mentions: ResolvedMention[];
|
|
767
800
|
}
|
|
768
801
|
/** The receipt from a send — the server's monotonic sequence + accepted epoch. */
|
|
769
802
|
interface SentReceipt {
|
|
@@ -804,6 +837,15 @@ interface ReplyRef {
|
|
|
804
837
|
client_msg_id: string;
|
|
805
838
|
preview?: QuotePreview;
|
|
806
839
|
}
|
|
840
|
+
/** A single mention range inside a message body.
|
|
841
|
+
* Offsets are UTF-16 code units, half-open [start, start+length).
|
|
842
|
+
* Wire: snake_case (mentioned_user_id, body_ranges) — web does RAW JSON, no case-convert.
|
|
843
|
+
*/
|
|
844
|
+
interface MentionRange {
|
|
845
|
+
start: number;
|
|
846
|
+
length: number;
|
|
847
|
+
mentionedUserId: string;
|
|
848
|
+
}
|
|
807
849
|
interface ReactionPayload {
|
|
808
850
|
targetClientMsgId: string;
|
|
809
851
|
emoji: string;
|
|
@@ -813,6 +855,10 @@ interface EditPayload {
|
|
|
813
855
|
targetClientMsgId: string;
|
|
814
856
|
newText: string;
|
|
815
857
|
}
|
|
858
|
+
interface DeletePayload {
|
|
859
|
+
targetClientMsgId: string;
|
|
860
|
+
scope: string;
|
|
861
|
+
}
|
|
816
862
|
|
|
817
863
|
/** A decoded incoming message handed to chat listeners. */
|
|
818
864
|
interface IncomingMessage {
|
|
@@ -839,6 +885,14 @@ interface IncomingMessage {
|
|
|
839
885
|
/** The decoded edit payload, present only when `envelopeType === 'edit'`. Lets
|
|
840
886
|
* the Chat route an edit into its EditFold instead of appending a visible bubble. */
|
|
841
887
|
edit?: EditPayload | null;
|
|
888
|
+
/** The decoded delete payload, present only when `envelopeType === 'delete'`. Lets
|
|
889
|
+
* the Chat route a delete-for-everyone tombstone into its DeleteFold instead of
|
|
890
|
+
* appending a visible bubble. */
|
|
891
|
+
delete?: DeletePayload | null;
|
|
892
|
+
/** The decoded mention ranges (raw, un-normalized). Present on a `'text'` bubble
|
|
893
|
+
* with `body_ranges` OR on an `'edit'` (the edit's replacement ranges). The Chat
|
|
894
|
+
* normalizes + resolves roster names → `ChatMessage.mentions` (mentions T6). */
|
|
895
|
+
bodyRanges?: MentionRange[] | null;
|
|
842
896
|
}
|
|
843
897
|
|
|
844
898
|
/** The intended participants of a draft chat (held until the first send). */
|
|
@@ -858,7 +912,7 @@ interface ChatBackend {
|
|
|
858
912
|
selfUserId: string;
|
|
859
913
|
/** Materialize a draft → the active group (DM get-or-create / group create+add). */
|
|
860
914
|
materialize(draft: ChatDraft): Promise<MessagingGroup>;
|
|
861
|
-
sendText(group: MessagingGroup, text: string, replyTo?: ReplyRef | null): Promise<{
|
|
915
|
+
sendText(group: MessagingGroup, text: string, replyTo?: ReplyRef | null, bodyRanges?: MentionRange[] | null): Promise<{
|
|
862
916
|
receipt: SentReceipt;
|
|
863
917
|
clientMsgId: string;
|
|
864
918
|
}>;
|
|
@@ -879,6 +933,17 @@ interface ChatBackend {
|
|
|
879
933
|
clientMsgId: string;
|
|
880
934
|
targetClientMsgId: string;
|
|
881
935
|
newText: string;
|
|
936
|
+
bodyRanges?: MentionRange[] | null;
|
|
937
|
+
}): Promise<{
|
|
938
|
+
receipt: SentReceipt;
|
|
939
|
+
clientMsgId: string;
|
|
940
|
+
}>;
|
|
941
|
+
/** Send a delete-for-everyone tombstone on a target message through the same MLS
|
|
942
|
+
* application path as `sendText` (the server stays blind — a delete is just
|
|
943
|
+
* another opaque application message). Returns the receipt + the wire clientMsgId. */
|
|
944
|
+
sendDelete(group: MessagingGroup, args: {
|
|
945
|
+
clientMsgId: string;
|
|
946
|
+
targetClientMsgId: string;
|
|
882
947
|
}): Promise<{
|
|
883
948
|
receipt: SentReceipt;
|
|
884
949
|
clientMsgId: string;
|
|
@@ -895,6 +960,15 @@ interface ChatBackend {
|
|
|
895
960
|
subscribeLive(group: MessagingGroup, chat: Chat): Unsubscribe$1;
|
|
896
961
|
/** Resolve a sender device id → its owning user id (for senderUserId). */
|
|
897
962
|
userIdForDevice(group: MessagingGroup, deviceId: string): Promise<string | null>;
|
|
963
|
+
/** Load this chat's persisted delete-for-me suppression keys (durable, no wire). */
|
|
964
|
+
loadSuppressed(group: MessagingGroup): Promise<string[]>;
|
|
965
|
+
/** Persist this chat's delete-for-me suppression keys (durable, no wire). */
|
|
966
|
+
saveSuppressed(group: MessagingGroup, keys: string[]): Promise<void>;
|
|
967
|
+
/** Load this chat's persisted self-elevation dedup keys (durable, no wire) so a
|
|
968
|
+
* re-delivered mention does not re-fire `onMentionElevation` after a cold launch. */
|
|
969
|
+
loadElevated(group: MessagingGroup): Promise<string[]>;
|
|
970
|
+
/** Persist this chat's self-elevation dedup keys (durable, no wire). */
|
|
971
|
+
saveElevated(group: MessagingGroup, keys: string[]): Promise<void>;
|
|
898
972
|
}
|
|
899
973
|
declare class Chat {
|
|
900
974
|
/** Stable, URL/log-safe id (the grp_ display id once active; a reserved local id while draft). */
|
|
@@ -917,6 +991,22 @@ declare class Chat {
|
|
|
917
991
|
private readonly reactionFold;
|
|
918
992
|
/** The single authoritative edit fold for this chat (live + own-send + history). */
|
|
919
993
|
private readonly editFold;
|
|
994
|
+
/** The single authoritative delete-for-everyone fold (live + own-send + history).
|
|
995
|
+
* A tombstone scrubs its target in place (delete DOMINATES edit at render). */
|
|
996
|
+
private readonly deleteFold;
|
|
997
|
+
/** delete-for-me suppression keys (clientMsgId, or `seq:<serverSeq>` for legacy)
|
|
998
|
+
* — the message is OMITTED from this view. Local + persisted per chat, NO wire. */
|
|
999
|
+
private readonly suppressed;
|
|
1000
|
+
/** True once the persisted suppression set has been loaded (so the omit applies
|
|
1001
|
+
* even on the cold-launch hydrate path before a fresh deleteForMe). */
|
|
1002
|
+
private suppressedLoaded;
|
|
1003
|
+
/** Self-elevation dedup keys (`<selfUserId>|<clientMsgId or seq:n>`). Once a
|
|
1004
|
+
* mention of me from another sender fires `onMentionElevation`, its key lands here
|
|
1005
|
+
* + is persisted, so a re-delivery / cold-launch re-hydrate never re-fires. */
|
|
1006
|
+
private readonly elevated;
|
|
1007
|
+
/** True once the persisted elevation set has been loaded (so a re-delivered mention
|
|
1008
|
+
* on the cold-launch hydrate path dedups against the persisted decision). */
|
|
1009
|
+
private elevatedLoaded;
|
|
920
1010
|
/** Per-target BASE (original) text, so the rendered text is `edit ?? original`.
|
|
921
1011
|
* Seeded once per target (write-once base) so a 2nd own edit can't corrupt it. */
|
|
922
1012
|
private readonly originalTextByClientMsgId;
|
|
@@ -928,6 +1018,14 @@ declare class Chat {
|
|
|
928
1018
|
private wired;
|
|
929
1019
|
private liveUnsub;
|
|
930
1020
|
private readonly listeners;
|
|
1021
|
+
/**
|
|
1022
|
+
* Fires ONCE per `(selfUserId, clientMsgId)` when an INCOMING message mentions THIS
|
|
1023
|
+
* user from ANOTHER sender (not an edit). The dedup survives re-delivery + reload
|
|
1024
|
+
* via the persisted elevation set, so this never double-fires for one mention. The
|
|
1025
|
+
* app wires it to a buzz/badge (e.g. an in-app banner). Best-effort cooperative —
|
|
1026
|
+
* the SDK guarantees the DECISION, not the buzz. Mirrors iOS `Chat.onMentionElevation`.
|
|
1027
|
+
*/
|
|
1028
|
+
onMentionElevation?: (message: ChatMessage) => void;
|
|
931
1029
|
/** @internal — obtain via pb.messaging.directChat / groupChat / chat(id). */
|
|
932
1030
|
constructor(args: {
|
|
933
1031
|
group: MessagingGroup;
|
|
@@ -948,9 +1046,29 @@ declare class Chat {
|
|
|
948
1046
|
get typing(): readonly ChatMember[];
|
|
949
1047
|
get lastMessage(): ChatMessage | null;
|
|
950
1048
|
get unreadCount(): number;
|
|
1049
|
+
/**
|
|
1050
|
+
* The RENDER PRECEDENCE — the single composition point (live AND history project
|
|
1051
|
+
* through it identically). Over the raw `messageList` (which already carries the
|
|
1052
|
+
* folded edit text + reactions + reply):
|
|
1053
|
+
* (1) in the delete-for-me suppression set → OMIT the message entirely;
|
|
1054
|
+
* (2) else tombstoned (delete-for-everyone) → the neutral "deleted" descriptor
|
|
1055
|
+
* with reactions/reply/edit HIDDEN (delete DOMINATES edit — short-circuit);
|
|
1056
|
+
* (3) else the row as-is (edit overlay + reactions + reply already applied).
|
|
1057
|
+
* Pure over (messageList, deleteFold, suppressed) — recomputed on every read so a
|
|
1058
|
+
* just-folded delete / just-suppressed key takes effect without rewriting rows.
|
|
1059
|
+
*/
|
|
1060
|
+
private surfaced;
|
|
1061
|
+
/** The delete-for-me suppression key: clientMsgId when present, else `seq:<n>`. */
|
|
1062
|
+
private suppressionKey;
|
|
951
1063
|
get title(): string;
|
|
952
1064
|
presence(userId: string): PresenceState | null;
|
|
953
1065
|
private ensureWired;
|
|
1066
|
+
/** Hydrate the persisted delete-for-me suppression keys (once), then re-emit so
|
|
1067
|
+
* any already-surfaced suppressed message is omitted (cold-launch parity). */
|
|
1068
|
+
private loadSuppressed;
|
|
1069
|
+
/** Hydrate the persisted self-elevation dedup keys (once). No re-emit: the set only
|
|
1070
|
+
* gates the elevation DECISION, it does not change what renders. */
|
|
1071
|
+
private loadElevated;
|
|
954
1072
|
private hydrateHistory;
|
|
955
1073
|
private mergeHistory;
|
|
956
1074
|
/** @internal — called by the backend's live subscription. */
|
|
@@ -959,6 +1077,31 @@ declare class Chat {
|
|
|
959
1077
|
* (null = target unknown/dangling → the fold HOLDs). Captured as a bound arrow
|
|
960
1078
|
* so it can be passed to the pure EditFold. */
|
|
961
1079
|
private readonly authorOfTarget;
|
|
1080
|
+
/** Resolve a message's decode-time mention spans: NORMALIZE the raw `body_ranges`
|
|
1081
|
+
* against `text` (the pinned `normalizeMentionRangesUtf16` cross-SDK contract) then
|
|
1082
|
+
* resolve each surviving range's `mentionedUserId` to a roster display name. An id
|
|
1083
|
+
* not in the roster resolves to `null` (the renderer falls back to the `text` slice).
|
|
1084
|
+
* Pure over (text, bodyRanges, memberCache); never throws. Mirrors iOS T3. */
|
|
1085
|
+
private resolveMentions;
|
|
1086
|
+
/** Re-resolve the roster display name on already-NORMALIZED spans (the history
|
|
1087
|
+
* projection produces them with null names — resolution is LIVE, not snapshotted).
|
|
1088
|
+
* A member rename then reflects on old messages. Returns the message unchanged when
|
|
1089
|
+
* it has no mentions (the common case) or no name changed. Mirrors iOS T3. */
|
|
1090
|
+
private resolveMentionNames;
|
|
1091
|
+
/** The WINNING edit's resolved mentions for a target (normalize its replacement
|
|
1092
|
+
* ranges against the new text + roster names), or `[]` if no winning edit / no
|
|
1093
|
+
* ranges. The edited message's mentions reflect the EDIT's ranges (mirrors iOS T3). */
|
|
1094
|
+
private editMentions;
|
|
1095
|
+
/** Resolve a userId → its roster display name (null if not a known member). */
|
|
1096
|
+
private displayNameOf;
|
|
1097
|
+
/** Compute the SELF-ELEVATION decision for a freshly-ingested INCOMING bubble and,
|
|
1098
|
+
* when it fires, record the dedup key (persisted) + invoke `onMentionElevation`.
|
|
1099
|
+
* Gate (mirrors iOS T3): a surviving mention targets THIS user AND the sender is not
|
|
1100
|
+
* me AND it's NOT an edit AND the `(selfUserId, clientMsgId|seq)` key isn't already
|
|
1101
|
+
* elevated. Dedup-once: the in-memory set gates the session, the persisted set
|
|
1102
|
+
* survives reload. An EDIT never reaches here (it folds, not a bubble) — the
|
|
1103
|
+
* `envelopeType !== 'edit'` guard is belt-and-braces. */
|
|
1104
|
+
private elevateIfMentioned;
|
|
962
1105
|
/** Seed the per-target base text + author for the edit fold. Base is write-once
|
|
963
1106
|
* (a later own/peer edit must not overwrite the original we render against). The
|
|
964
1107
|
* author is (re)recorded whenever a non-empty resolution is available. */
|
|
@@ -999,10 +1142,14 @@ declare class Chat {
|
|
|
999
1142
|
/** Page older messages in. Returns how many were prepended. */
|
|
1000
1143
|
loadEarlier(limit?: number): Promise<number>;
|
|
1001
1144
|
private refreshMembers;
|
|
1145
|
+
/** Re-resolve roster display names across the whole transcript (called on a roster
|
|
1146
|
+
* change). Re-emits only if any name actually changed. */
|
|
1147
|
+
private reresolveAllMentionNames;
|
|
1002
1148
|
private seedMembersFromGroup;
|
|
1003
1149
|
private seedDraftMembers;
|
|
1004
1150
|
send(text: string, opts?: {
|
|
1005
1151
|
replyTo?: ChatMessage;
|
|
1152
|
+
mentions?: MentionRange[];
|
|
1006
1153
|
}): Promise<SentReceipt>;
|
|
1007
1154
|
private appendOwnSend;
|
|
1008
1155
|
private materializeIfNeeded;
|
|
@@ -1026,8 +1173,25 @@ declare class Chat {
|
|
|
1026
1173
|
* instantly; the durable echo on the next pump is a fold no-op (dedup on the
|
|
1027
1174
|
* SAME wire clientMsgId). Never appends a bubble; PRESERVES the target's
|
|
1028
1175
|
* reactions + reply context. Only the original author's edits count — for an own
|
|
1029
|
-
* message self IS the author, so the author-gate passes.
|
|
1030
|
-
|
|
1176
|
+
* message self IS the author, so the author-gate passes. `opts.mentions` carries the
|
|
1177
|
+
* edit's REPLACEMENT mention ranges → the edited message's mentions reflect them. */
|
|
1178
|
+
edit(message: ChatMessage, newText: string, opts?: {
|
|
1179
|
+
mentions?: MentionRange[];
|
|
1180
|
+
}): Promise<void>;
|
|
1181
|
+
/** Delete a message for EVERYONE (a cooperative encrypted tombstone). Only the
|
|
1182
|
+
* ORIGINAL SENDER can do this — for an own message self IS the author, so the
|
|
1183
|
+
* author-gate passes. Legacy messages (empty clientMsgId) are DISABLED (a
|
|
1184
|
+
* tombstone keys on the target's clientMsgId, which they lack) — no-op. Sends a
|
|
1185
|
+
* `type:'delete'` envelope through the SAME MLS path as a text message (the
|
|
1186
|
+
* server stays blind), folds the own delete locally so the target scrubs in
|
|
1187
|
+
* place instantly (the durable echo dedups on the SAME wire clientMsgId), and
|
|
1188
|
+
* re-emits. NEVER appends a bubble. delete-for-me'ing the target becomes moot. */
|
|
1189
|
+
deleteForEveryone(message: ChatMessage): Promise<void>;
|
|
1190
|
+
/** Delete a message for ME only — a LOCAL, per-device suppression. NO wire, NO
|
|
1191
|
+
* attribution, no server contact: the message is OMITTED from THIS view and the
|
|
1192
|
+
* suppression key persists per chat (survives reload). The key is the message's
|
|
1193
|
+
* clientMsgId when present, else `seq:<serverSeq>` for legacy messages. */
|
|
1194
|
+
deleteForMe(message: ChatMessage): Promise<void>;
|
|
1031
1195
|
}
|
|
1032
1196
|
|
|
1033
1197
|
declare class PalbeMessaging {
|
|
@@ -1419,4 +1583,4 @@ declare class PalbeAnalytics {
|
|
|
1419
1583
|
private post;
|
|
1420
1584
|
}
|
|
1421
1585
|
|
|
1422
|
-
export { type AnalyticsProperties as A, type
|
|
1586
|
+
export { type AnalyticsProperties as A, type PresenceState as B, Call as C, type RealtimeConnectionState as D, type RealtimeHandler as E, type FlagsView as F, type RealtimePayload as G, type RealtimeStatus as H, type RealtimeStatusSnapshot as I, type RealtimeSubscription as J, type ResolvedMention as K, type ResolvedReply as L, type MagicLinkResult as M, type PalbeRuntime as N, type OAuthExchangeResult as O, PalbeAnalytics as P, buildRuntime as Q, RealtimeChannel as R, type SentReceipt as S, type Unsubscribe as U, type AuthChangeEvent as a, type AuthState as b, type AuthSuccess as c, type AuthUser as d, type CallChangeCallback as e, type CallParticipant as f, type CallState as g, Chat as h, type ChatBackend as i, type ChatDraft as j, type ChatKind as k, type ChatMember as l, type ChatMessage as m, type ChatMessageKind as n, type ChatRole as o, type ChatState as p, type CommsPrefs as q, type MentionRange as r, type MessageDirection as s, PalbeAuth as t, PalbeCalls as u, type PalbeConfig as v, PalbeFlags as w, PalbeMessaging as x, type PalbeOAuthConfig as y, PalbeRealtime as z };
|