@palbase/web 1.2.1 → 1.4.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.
@@ -756,6 +756,24 @@ interface ChatMessage {
756
756
  * a visible message of their own — they only mutate the target's tally.
757
757
  */
758
758
  readonly reactions: Record<string, string[]>;
759
+ /**
760
+ * Whether this message has been edited (edit-by-supersession). Defaults to
761
+ * `false`. Once any valid author edit folds onto this message it stays `true`
762
+ * forever (write-once, even if edited back to the original text). The rendered
763
+ * `text` reflects the LATEST edit's text (`edit ?? original`). An edit is never
764
+ * a visible message of its own — it only overlays the target's text + flag.
765
+ */
766
+ readonly edited: boolean;
767
+ /**
768
+ * Whether this message has been deleted-for-everyone (a cooperative encrypted
769
+ * tombstone from the original sender). Defaults to `false`. Once a valid
770
+ * tombstone folds onto this message it stays `true` (absorbing/sticky within
771
+ * retention). A deleted message renders ONLY the neutral "deleted" descriptor
772
+ * as its `text`, with reactions + reply + edit HIDDEN — delete DOMINATES edit.
773
+ * A delete is never a visible message of its own. (delete-for-me is a separate,
774
+ * local-only suppression that omits the message from this view entirely.)
775
+ */
776
+ readonly isDeleted: boolean;
759
777
  }
760
778
  /** The receipt from a send — the server's monotonic sequence + accepted epoch. */
761
779
  interface SentReceipt {
@@ -801,6 +819,14 @@ interface ReactionPayload {
801
819
  emoji: string;
802
820
  op: 'add' | 'remove';
803
821
  }
822
+ interface EditPayload {
823
+ targetClientMsgId: string;
824
+ newText: string;
825
+ }
826
+ interface DeletePayload {
827
+ targetClientMsgId: string;
828
+ scope: string;
829
+ }
804
830
 
805
831
  /** A decoded incoming message handed to chat listeners. */
806
832
  interface IncomingMessage {
@@ -824,6 +850,13 @@ interface IncomingMessage {
824
850
  envelopeType?: string;
825
851
  /** The decoded reaction payload, present only when `envelopeType === 'reaction'`. */
826
852
  reaction?: ReactionPayload | null;
853
+ /** The decoded edit payload, present only when `envelopeType === 'edit'`. Lets
854
+ * the Chat route an edit into its EditFold instead of appending a visible bubble. */
855
+ edit?: EditPayload | null;
856
+ /** The decoded delete payload, present only when `envelopeType === 'delete'`. Lets
857
+ * the Chat route a delete-for-everyone tombstone into its DeleteFold instead of
858
+ * appending a visible bubble. */
859
+ delete?: DeletePayload | null;
827
860
  }
828
861
 
829
862
  /** The intended participants of a draft chat (held until the first send). */
@@ -858,6 +891,26 @@ interface ChatBackend {
858
891
  receipt: SentReceipt;
859
892
  clientMsgId: string;
860
893
  }>;
894
+ /** Send an edit (edit-by-supersession on a target message) through the same MLS
895
+ * application path as `sendText`. Returns the receipt + the wire clientMsgId. */
896
+ sendEdit(group: MessagingGroup, args: {
897
+ clientMsgId: string;
898
+ targetClientMsgId: string;
899
+ newText: string;
900
+ }): Promise<{
901
+ receipt: SentReceipt;
902
+ clientMsgId: string;
903
+ }>;
904
+ /** Send a delete-for-everyone tombstone on a target message through the same MLS
905
+ * application path as `sendText` (the server stays blind — a delete is just
906
+ * another opaque application message). Returns the receipt + the wire clientMsgId. */
907
+ sendDelete(group: MessagingGroup, args: {
908
+ clientMsgId: string;
909
+ targetClientMsgId: string;
910
+ }): Promise<{
911
+ receipt: SentReceipt;
912
+ clientMsgId: string;
913
+ }>;
861
914
  history(group: MessagingGroup, limit: number, before?: number): Promise<ChatMessage[]>;
862
915
  members(group: MessagingGroup): Promise<ChatMember[]>;
863
916
  addMember(group: MessagingGroup, userId: string): Promise<void>;
@@ -870,6 +923,10 @@ interface ChatBackend {
870
923
  subscribeLive(group: MessagingGroup, chat: Chat): Unsubscribe$1;
871
924
  /** Resolve a sender device id → its owning user id (for senderUserId). */
872
925
  userIdForDevice(group: MessagingGroup, deviceId: string): Promise<string | null>;
926
+ /** Load this chat's persisted delete-for-me suppression keys (durable, no wire). */
927
+ loadSuppressed(group: MessagingGroup): Promise<string[]>;
928
+ /** Persist this chat's delete-for-me suppression keys (durable, no wire). */
929
+ saveSuppressed(group: MessagingGroup, keys: string[]): Promise<void>;
873
930
  }
874
931
  declare class Chat {
875
932
  /** Stable, URL/log-safe id (the grp_ display id once active; a reserved local id while draft). */
@@ -890,6 +947,23 @@ declare class Chat {
890
947
  private readonly byClientMsgId;
891
948
  /** The single authoritative reaction fold for this chat (live + own-send + history). */
892
949
  private readonly reactionFold;
950
+ /** The single authoritative edit fold for this chat (live + own-send + history). */
951
+ private readonly editFold;
952
+ /** The single authoritative delete-for-everyone fold (live + own-send + history).
953
+ * A tombstone scrubs its target in place (delete DOMINATES edit at render). */
954
+ private readonly deleteFold;
955
+ /** delete-for-me suppression keys (clientMsgId, or `seq:<serverSeq>` for legacy)
956
+ * — the message is OMITTED from this view. Local + persisted per chat, NO wire. */
957
+ private readonly suppressed;
958
+ /** True once the persisted suppression set has been loaded (so the omit applies
959
+ * even on the cold-launch hydrate path before a fresh deleteForMe). */
960
+ private suppressedLoaded;
961
+ /** Per-target BASE (original) text, so the rendered text is `edit ?? original`.
962
+ * Seeded once per target (write-once base) so a 2nd own edit can't corrupt it. */
963
+ private readonly originalTextByClientMsgId;
964
+ /** Per-target AUTHOR userId — the EditFold author-gate input (filled at bubble
965
+ * projection time from senderUserId; '' = resolved-but-unknown peer). */
966
+ private readonly authorByClientMsgId;
893
967
  private loadedEarliestSeq;
894
968
  private historyLoaded;
895
969
  private wired;
@@ -915,13 +989,38 @@ declare class Chat {
915
989
  get typing(): readonly ChatMember[];
916
990
  get lastMessage(): ChatMessage | null;
917
991
  get unreadCount(): number;
992
+ /**
993
+ * The RENDER PRECEDENCE — the single composition point (live AND history project
994
+ * through it identically). Over the raw `messageList` (which already carries the
995
+ * folded edit text + reactions + reply):
996
+ * (1) in the delete-for-me suppression set → OMIT the message entirely;
997
+ * (2) else tombstoned (delete-for-everyone) → the neutral "deleted" descriptor
998
+ * with reactions/reply/edit HIDDEN (delete DOMINATES edit — short-circuit);
999
+ * (3) else the row as-is (edit overlay + reactions + reply already applied).
1000
+ * Pure over (messageList, deleteFold, suppressed) — recomputed on every read so a
1001
+ * just-folded delete / just-suppressed key takes effect without rewriting rows.
1002
+ */
1003
+ private surfaced;
1004
+ /** The delete-for-me suppression key: clientMsgId when present, else `seq:<n>`. */
1005
+ private suppressionKey;
918
1006
  get title(): string;
919
1007
  presence(userId: string): PresenceState | null;
920
1008
  private ensureWired;
1009
+ /** Hydrate the persisted delete-for-me suppression keys (once), then re-emit so
1010
+ * any already-surfaced suppressed message is omitted (cold-launch parity). */
1011
+ private loadSuppressed;
921
1012
  private hydrateHistory;
922
1013
  private mergeHistory;
923
1014
  /** @internal — called by the backend's live subscription. */
924
1015
  ingestLive(incoming: IncomingMessage): Promise<void>;
1016
+ /** The EditFold author-gate input: the target message's resolved author userId
1017
+ * (null = target unknown/dangling → the fold HOLDs). Captured as a bound arrow
1018
+ * so it can be passed to the pure EditFold. */
1019
+ private readonly authorOfTarget;
1020
+ /** Seed the per-target base text + author for the edit fold. Base is write-once
1021
+ * (a later own/peer edit must not overwrite the original we render against). The
1022
+ * author is (re)recorded whenever a non-empty resolution is available. */
1023
+ private seedEditBase;
925
1024
  /**
926
1025
  * Rebuild the target message's `reactions` from the authoritative fold and
927
1026
  * re-emit. No-op when the target isn't present yet (its tally is attached the
@@ -934,6 +1033,22 @@ declare class Chat {
934
1033
  * attached upstream (the coordinator's page-local history fold) is preserved.
935
1034
  */
936
1035
  private applyReactionTally;
1036
+ /**
1037
+ * Rebuild the target message's rendered `text` + `edited` flag from the
1038
+ * authoritative edit fold and re-emit, PRESERVING `.reactions` and `.replyTo`
1039
+ * (the reaction-polish lesson — never clobber). text = `editFold.text(cid) ??
1040
+ * base`; base is the seeded original so a forged/ignored edit leaves it intact.
1041
+ * No-op when the target isn't present yet (the fold already recorded it; the
1042
+ * overlay applies the moment the target lands) or when unchanged.
1043
+ */
1044
+ private recomputeEdit;
1045
+ /**
1046
+ * Overlay the authoritative edit fold's winning text + flag onto a message as it
1047
+ * is appended/merged. The fold WINS when it has an edit for this target;
1048
+ * otherwise the upstream `text`/`edited` (e.g. the coordinator's page-local
1049
+ * history fold) is preserved. PRESERVES reactions + replyTo.
1050
+ */
1051
+ private applyEditOverlay;
937
1052
  /** @internal — called by the backend's conv subscription. */
938
1053
  applyConv(event: string, payload: Record<string, unknown>): void;
939
1054
  private kindOf;
@@ -963,6 +1078,28 @@ declare class Chat {
963
1078
  /** Remove this user's emoji reaction from a message (op:'remove'). */
964
1079
  unreact(message: ChatMessage, emoji: string): Promise<void>;
965
1080
  private sendReaction;
1081
+ /** Edit an own text message (edit-by-supersession). No-op if the message isn't
1082
+ * editable (empty clientMsgId, or not a `text` kind). The edit folds locally
1083
+ * with the server receipt's `(epoch, serverSeq)` so the target's text updates
1084
+ * instantly; the durable echo on the next pump is a fold no-op (dedup on the
1085
+ * SAME wire clientMsgId). Never appends a bubble; PRESERVES the target's
1086
+ * reactions + reply context. Only the original author's edits count — for an own
1087
+ * message self IS the author, so the author-gate passes. */
1088
+ edit(message: ChatMessage, newText: string): Promise<void>;
1089
+ /** Delete a message for EVERYONE (a cooperative encrypted tombstone). Only the
1090
+ * ORIGINAL SENDER can do this — for an own message self IS the author, so the
1091
+ * author-gate passes. Legacy messages (empty clientMsgId) are DISABLED (a
1092
+ * tombstone keys on the target's clientMsgId, which they lack) — no-op. Sends a
1093
+ * `type:'delete'` envelope through the SAME MLS path as a text message (the
1094
+ * server stays blind), folds the own delete locally so the target scrubs in
1095
+ * place instantly (the durable echo dedups on the SAME wire clientMsgId), and
1096
+ * re-emits. NEVER appends a bubble. delete-for-me'ing the target becomes moot. */
1097
+ deleteForEveryone(message: ChatMessage): Promise<void>;
1098
+ /** Delete a message for ME only — a LOCAL, per-device suppression. NO wire, NO
1099
+ * attribution, no server contact: the message is OMITTED from THIS view and the
1100
+ * suppression key persists per chat (survives reload). The key is the message's
1101
+ * clientMsgId when present, else `seq:<serverSeq>` for legacy messages. */
1102
+ deleteForMe(message: ChatMessage): Promise<void>;
966
1103
  }
967
1104
 
968
1105
  declare class PalbeMessaging {
@@ -1354,4 +1491,4 @@ declare class PalbeAnalytics {
1354
1491
  private post;
1355
1492
  }
1356
1493
 
1357
- export { type AnalyticsProperties as A, type RealtimeConnectionState as B, Call as C, type RealtimeHandler as D, type RealtimePayload as E, type FlagsView as F, type RealtimeStatus as G, type RealtimeStatusSnapshot as H, type RealtimeSubscription as I, type PalbeRuntime as J, buildRuntime as K, type MagicLinkResult as M, type OAuthExchangeResult as O, PalbeAnalytics as P, 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 MessageDirection as r, PalbeAuth as s, PalbeCalls as t, type PalbeConfig as u, PalbeFlags as v, PalbeMessaging as w, type PalbeOAuthConfig as x, PalbeRealtime as y, type PresenceState as z };
1494
+ export { type AnalyticsProperties as A, type RealtimeConnectionState as B, Call as C, type RealtimeHandler as D, type RealtimePayload as E, type FlagsView as F, type RealtimeStatus as G, type RealtimeStatusSnapshot as H, type RealtimeSubscription as I, type ResolvedReply as J, type PalbeRuntime as K, buildRuntime as L, type MagicLinkResult as M, type OAuthExchangeResult as O, PalbeAnalytics as P, 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 MessageDirection as r, PalbeAuth as s, PalbeCalls as t, type PalbeConfig as u, PalbeFlags as v, PalbeMessaging as w, type PalbeOAuthConfig as x, PalbeRealtime as y, type PresenceState as z };
@@ -756,6 +756,24 @@ interface ChatMessage {
756
756
  * a visible message of their own — they only mutate the target's tally.
757
757
  */
758
758
  readonly reactions: Record<string, string[]>;
759
+ /**
760
+ * Whether this message has been edited (edit-by-supersession). Defaults to
761
+ * `false`. Once any valid author edit folds onto this message it stays `true`
762
+ * forever (write-once, even if edited back to the original text). The rendered
763
+ * `text` reflects the LATEST edit's text (`edit ?? original`). An edit is never
764
+ * a visible message of its own — it only overlays the target's text + flag.
765
+ */
766
+ readonly edited: boolean;
767
+ /**
768
+ * Whether this message has been deleted-for-everyone (a cooperative encrypted
769
+ * tombstone from the original sender). Defaults to `false`. Once a valid
770
+ * tombstone folds onto this message it stays `true` (absorbing/sticky within
771
+ * retention). A deleted message renders ONLY the neutral "deleted" descriptor
772
+ * as its `text`, with reactions + reply + edit HIDDEN — delete DOMINATES edit.
773
+ * A delete is never a visible message of its own. (delete-for-me is a separate,
774
+ * local-only suppression that omits the message from this view entirely.)
775
+ */
776
+ readonly isDeleted: boolean;
759
777
  }
760
778
  /** The receipt from a send — the server's monotonic sequence + accepted epoch. */
761
779
  interface SentReceipt {
@@ -801,6 +819,14 @@ interface ReactionPayload {
801
819
  emoji: string;
802
820
  op: 'add' | 'remove';
803
821
  }
822
+ interface EditPayload {
823
+ targetClientMsgId: string;
824
+ newText: string;
825
+ }
826
+ interface DeletePayload {
827
+ targetClientMsgId: string;
828
+ scope: string;
829
+ }
804
830
 
805
831
  /** A decoded incoming message handed to chat listeners. */
806
832
  interface IncomingMessage {
@@ -824,6 +850,13 @@ interface IncomingMessage {
824
850
  envelopeType?: string;
825
851
  /** The decoded reaction payload, present only when `envelopeType === 'reaction'`. */
826
852
  reaction?: ReactionPayload | null;
853
+ /** The decoded edit payload, present only when `envelopeType === 'edit'`. Lets
854
+ * the Chat route an edit into its EditFold instead of appending a visible bubble. */
855
+ edit?: EditPayload | null;
856
+ /** The decoded delete payload, present only when `envelopeType === 'delete'`. Lets
857
+ * the Chat route a delete-for-everyone tombstone into its DeleteFold instead of
858
+ * appending a visible bubble. */
859
+ delete?: DeletePayload | null;
827
860
  }
828
861
 
829
862
  /** The intended participants of a draft chat (held until the first send). */
@@ -858,6 +891,26 @@ interface ChatBackend {
858
891
  receipt: SentReceipt;
859
892
  clientMsgId: string;
860
893
  }>;
894
+ /** Send an edit (edit-by-supersession on a target message) through the same MLS
895
+ * application path as `sendText`. Returns the receipt + the wire clientMsgId. */
896
+ sendEdit(group: MessagingGroup, args: {
897
+ clientMsgId: string;
898
+ targetClientMsgId: string;
899
+ newText: string;
900
+ }): Promise<{
901
+ receipt: SentReceipt;
902
+ clientMsgId: string;
903
+ }>;
904
+ /** Send a delete-for-everyone tombstone on a target message through the same MLS
905
+ * application path as `sendText` (the server stays blind — a delete is just
906
+ * another opaque application message). Returns the receipt + the wire clientMsgId. */
907
+ sendDelete(group: MessagingGroup, args: {
908
+ clientMsgId: string;
909
+ targetClientMsgId: string;
910
+ }): Promise<{
911
+ receipt: SentReceipt;
912
+ clientMsgId: string;
913
+ }>;
861
914
  history(group: MessagingGroup, limit: number, before?: number): Promise<ChatMessage[]>;
862
915
  members(group: MessagingGroup): Promise<ChatMember[]>;
863
916
  addMember(group: MessagingGroup, userId: string): Promise<void>;
@@ -870,6 +923,10 @@ interface ChatBackend {
870
923
  subscribeLive(group: MessagingGroup, chat: Chat): Unsubscribe$1;
871
924
  /** Resolve a sender device id → its owning user id (for senderUserId). */
872
925
  userIdForDevice(group: MessagingGroup, deviceId: string): Promise<string | null>;
926
+ /** Load this chat's persisted delete-for-me suppression keys (durable, no wire). */
927
+ loadSuppressed(group: MessagingGroup): Promise<string[]>;
928
+ /** Persist this chat's delete-for-me suppression keys (durable, no wire). */
929
+ saveSuppressed(group: MessagingGroup, keys: string[]): Promise<void>;
873
930
  }
874
931
  declare class Chat {
875
932
  /** Stable, URL/log-safe id (the grp_ display id once active; a reserved local id while draft). */
@@ -890,6 +947,23 @@ declare class Chat {
890
947
  private readonly byClientMsgId;
891
948
  /** The single authoritative reaction fold for this chat (live + own-send + history). */
892
949
  private readonly reactionFold;
950
+ /** The single authoritative edit fold for this chat (live + own-send + history). */
951
+ private readonly editFold;
952
+ /** The single authoritative delete-for-everyone fold (live + own-send + history).
953
+ * A tombstone scrubs its target in place (delete DOMINATES edit at render). */
954
+ private readonly deleteFold;
955
+ /** delete-for-me suppression keys (clientMsgId, or `seq:<serverSeq>` for legacy)
956
+ * — the message is OMITTED from this view. Local + persisted per chat, NO wire. */
957
+ private readonly suppressed;
958
+ /** True once the persisted suppression set has been loaded (so the omit applies
959
+ * even on the cold-launch hydrate path before a fresh deleteForMe). */
960
+ private suppressedLoaded;
961
+ /** Per-target BASE (original) text, so the rendered text is `edit ?? original`.
962
+ * Seeded once per target (write-once base) so a 2nd own edit can't corrupt it. */
963
+ private readonly originalTextByClientMsgId;
964
+ /** Per-target AUTHOR userId — the EditFold author-gate input (filled at bubble
965
+ * projection time from senderUserId; '' = resolved-but-unknown peer). */
966
+ private readonly authorByClientMsgId;
893
967
  private loadedEarliestSeq;
894
968
  private historyLoaded;
895
969
  private wired;
@@ -915,13 +989,38 @@ declare class Chat {
915
989
  get typing(): readonly ChatMember[];
916
990
  get lastMessage(): ChatMessage | null;
917
991
  get unreadCount(): number;
992
+ /**
993
+ * The RENDER PRECEDENCE — the single composition point (live AND history project
994
+ * through it identically). Over the raw `messageList` (which already carries the
995
+ * folded edit text + reactions + reply):
996
+ * (1) in the delete-for-me suppression set → OMIT the message entirely;
997
+ * (2) else tombstoned (delete-for-everyone) → the neutral "deleted" descriptor
998
+ * with reactions/reply/edit HIDDEN (delete DOMINATES edit — short-circuit);
999
+ * (3) else the row as-is (edit overlay + reactions + reply already applied).
1000
+ * Pure over (messageList, deleteFold, suppressed) — recomputed on every read so a
1001
+ * just-folded delete / just-suppressed key takes effect without rewriting rows.
1002
+ */
1003
+ private surfaced;
1004
+ /** The delete-for-me suppression key: clientMsgId when present, else `seq:<n>`. */
1005
+ private suppressionKey;
918
1006
  get title(): string;
919
1007
  presence(userId: string): PresenceState | null;
920
1008
  private ensureWired;
1009
+ /** Hydrate the persisted delete-for-me suppression keys (once), then re-emit so
1010
+ * any already-surfaced suppressed message is omitted (cold-launch parity). */
1011
+ private loadSuppressed;
921
1012
  private hydrateHistory;
922
1013
  private mergeHistory;
923
1014
  /** @internal — called by the backend's live subscription. */
924
1015
  ingestLive(incoming: IncomingMessage): Promise<void>;
1016
+ /** The EditFold author-gate input: the target message's resolved author userId
1017
+ * (null = target unknown/dangling → the fold HOLDs). Captured as a bound arrow
1018
+ * so it can be passed to the pure EditFold. */
1019
+ private readonly authorOfTarget;
1020
+ /** Seed the per-target base text + author for the edit fold. Base is write-once
1021
+ * (a later own/peer edit must not overwrite the original we render against). The
1022
+ * author is (re)recorded whenever a non-empty resolution is available. */
1023
+ private seedEditBase;
925
1024
  /**
926
1025
  * Rebuild the target message's `reactions` from the authoritative fold and
927
1026
  * re-emit. No-op when the target isn't present yet (its tally is attached the
@@ -934,6 +1033,22 @@ declare class Chat {
934
1033
  * attached upstream (the coordinator's page-local history fold) is preserved.
935
1034
  */
936
1035
  private applyReactionTally;
1036
+ /**
1037
+ * Rebuild the target message's rendered `text` + `edited` flag from the
1038
+ * authoritative edit fold and re-emit, PRESERVING `.reactions` and `.replyTo`
1039
+ * (the reaction-polish lesson — never clobber). text = `editFold.text(cid) ??
1040
+ * base`; base is the seeded original so a forged/ignored edit leaves it intact.
1041
+ * No-op when the target isn't present yet (the fold already recorded it; the
1042
+ * overlay applies the moment the target lands) or when unchanged.
1043
+ */
1044
+ private recomputeEdit;
1045
+ /**
1046
+ * Overlay the authoritative edit fold's winning text + flag onto a message as it
1047
+ * is appended/merged. The fold WINS when it has an edit for this target;
1048
+ * otherwise the upstream `text`/`edited` (e.g. the coordinator's page-local
1049
+ * history fold) is preserved. PRESERVES reactions + replyTo.
1050
+ */
1051
+ private applyEditOverlay;
937
1052
  /** @internal — called by the backend's conv subscription. */
938
1053
  applyConv(event: string, payload: Record<string, unknown>): void;
939
1054
  private kindOf;
@@ -963,6 +1078,28 @@ declare class Chat {
963
1078
  /** Remove this user's emoji reaction from a message (op:'remove'). */
964
1079
  unreact(message: ChatMessage, emoji: string): Promise<void>;
965
1080
  private sendReaction;
1081
+ /** Edit an own text message (edit-by-supersession). No-op if the message isn't
1082
+ * editable (empty clientMsgId, or not a `text` kind). The edit folds locally
1083
+ * with the server receipt's `(epoch, serverSeq)` so the target's text updates
1084
+ * instantly; the durable echo on the next pump is a fold no-op (dedup on the
1085
+ * SAME wire clientMsgId). Never appends a bubble; PRESERVES the target's
1086
+ * reactions + reply context. Only the original author's edits count — for an own
1087
+ * message self IS the author, so the author-gate passes. */
1088
+ edit(message: ChatMessage, newText: string): Promise<void>;
1089
+ /** Delete a message for EVERYONE (a cooperative encrypted tombstone). Only the
1090
+ * ORIGINAL SENDER can do this — for an own message self IS the author, so the
1091
+ * author-gate passes. Legacy messages (empty clientMsgId) are DISABLED (a
1092
+ * tombstone keys on the target's clientMsgId, which they lack) — no-op. Sends a
1093
+ * `type:'delete'` envelope through the SAME MLS path as a text message (the
1094
+ * server stays blind), folds the own delete locally so the target scrubs in
1095
+ * place instantly (the durable echo dedups on the SAME wire clientMsgId), and
1096
+ * re-emits. NEVER appends a bubble. delete-for-me'ing the target becomes moot. */
1097
+ deleteForEveryone(message: ChatMessage): Promise<void>;
1098
+ /** Delete a message for ME only — a LOCAL, per-device suppression. NO wire, NO
1099
+ * attribution, no server contact: the message is OMITTED from THIS view and the
1100
+ * suppression key persists per chat (survives reload). The key is the message's
1101
+ * clientMsgId when present, else `seq:<serverSeq>` for legacy messages. */
1102
+ deleteForMe(message: ChatMessage): Promise<void>;
966
1103
  }
967
1104
 
968
1105
  declare class PalbeMessaging {
@@ -1354,4 +1491,4 @@ declare class PalbeAnalytics {
1354
1491
  private post;
1355
1492
  }
1356
1493
 
1357
- export { type AnalyticsProperties as A, type RealtimeConnectionState as B, Call as C, type RealtimeHandler as D, type RealtimePayload as E, type FlagsView as F, type RealtimeStatus as G, type RealtimeStatusSnapshot as H, type RealtimeSubscription as I, type PalbeRuntime as J, buildRuntime as K, type MagicLinkResult as M, type OAuthExchangeResult as O, PalbeAnalytics as P, 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 MessageDirection as r, PalbeAuth as s, PalbeCalls as t, type PalbeConfig as u, PalbeFlags as v, PalbeMessaging as w, type PalbeOAuthConfig as x, PalbeRealtime as y, type PresenceState as z };
1494
+ export { type AnalyticsProperties as A, type RealtimeConnectionState as B, Call as C, type RealtimeHandler as D, type RealtimePayload as E, type FlagsView as F, type RealtimeStatus as G, type RealtimeStatusSnapshot as H, type RealtimeSubscription as I, type ResolvedReply as J, type PalbeRuntime as K, buildRuntime as L, type MagicLinkResult as M, type OAuthExchangeResult as O, PalbeAnalytics as P, 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 MessageDirection as r, PalbeAuth as s, PalbeCalls as t, type PalbeConfig as u, PalbeFlags as v, PalbeMessaging as w, type PalbeOAuthConfig as x, PalbeRealtime as y, type PresenceState as z };