@palbase/web 1.2.0 → 1.3.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,14 @@ 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;
759
767
  }
760
768
  /** The receipt from a send — the server's monotonic sequence + accepted epoch. */
761
769
  interface SentReceipt {
@@ -801,6 +809,10 @@ interface ReactionPayload {
801
809
  emoji: string;
802
810
  op: 'add' | 'remove';
803
811
  }
812
+ interface EditPayload {
813
+ targetClientMsgId: string;
814
+ newText: string;
815
+ }
804
816
 
805
817
  /** A decoded incoming message handed to chat listeners. */
806
818
  interface IncomingMessage {
@@ -824,6 +836,9 @@ interface IncomingMessage {
824
836
  envelopeType?: string;
825
837
  /** The decoded reaction payload, present only when `envelopeType === 'reaction'`. */
826
838
  reaction?: ReactionPayload | null;
839
+ /** The decoded edit payload, present only when `envelopeType === 'edit'`. Lets
840
+ * the Chat route an edit into its EditFold instead of appending a visible bubble. */
841
+ edit?: EditPayload | null;
827
842
  }
828
843
 
829
844
  /** The intended participants of a draft chat (held until the first send). */
@@ -858,6 +873,16 @@ interface ChatBackend {
858
873
  receipt: SentReceipt;
859
874
  clientMsgId: string;
860
875
  }>;
876
+ /** Send an edit (edit-by-supersession on a target message) through the same MLS
877
+ * application path as `sendText`. Returns the receipt + the wire clientMsgId. */
878
+ sendEdit(group: MessagingGroup, args: {
879
+ clientMsgId: string;
880
+ targetClientMsgId: string;
881
+ newText: string;
882
+ }): Promise<{
883
+ receipt: SentReceipt;
884
+ clientMsgId: string;
885
+ }>;
861
886
  history(group: MessagingGroup, limit: number, before?: number): Promise<ChatMessage[]>;
862
887
  members(group: MessagingGroup): Promise<ChatMember[]>;
863
888
  addMember(group: MessagingGroup, userId: string): Promise<void>;
@@ -890,6 +915,14 @@ declare class Chat {
890
915
  private readonly byClientMsgId;
891
916
  /** The single authoritative reaction fold for this chat (live + own-send + history). */
892
917
  private readonly reactionFold;
918
+ /** The single authoritative edit fold for this chat (live + own-send + history). */
919
+ private readonly editFold;
920
+ /** Per-target BASE (original) text, so the rendered text is `edit ?? original`.
921
+ * Seeded once per target (write-once base) so a 2nd own edit can't corrupt it. */
922
+ private readonly originalTextByClientMsgId;
923
+ /** Per-target AUTHOR userId — the EditFold author-gate input (filled at bubble
924
+ * projection time from senderUserId; '' = resolved-but-unknown peer). */
925
+ private readonly authorByClientMsgId;
893
926
  private loadedEarliestSeq;
894
927
  private historyLoaded;
895
928
  private wired;
@@ -922,6 +955,14 @@ declare class Chat {
922
955
  private mergeHistory;
923
956
  /** @internal — called by the backend's live subscription. */
924
957
  ingestLive(incoming: IncomingMessage): Promise<void>;
958
+ /** The EditFold author-gate input: the target message's resolved author userId
959
+ * (null = target unknown/dangling → the fold HOLDs). Captured as a bound arrow
960
+ * so it can be passed to the pure EditFold. */
961
+ private readonly authorOfTarget;
962
+ /** Seed the per-target base text + author for the edit fold. Base is write-once
963
+ * (a later own/peer edit must not overwrite the original we render against). The
964
+ * author is (re)recorded whenever a non-empty resolution is available. */
965
+ private seedEditBase;
925
966
  /**
926
967
  * Rebuild the target message's `reactions` from the authoritative fold and
927
968
  * re-emit. No-op when the target isn't present yet (its tally is attached the
@@ -934,6 +975,22 @@ declare class Chat {
934
975
  * attached upstream (the coordinator's page-local history fold) is preserved.
935
976
  */
936
977
  private applyReactionTally;
978
+ /**
979
+ * Rebuild the target message's rendered `text` + `edited` flag from the
980
+ * authoritative edit fold and re-emit, PRESERVING `.reactions` and `.replyTo`
981
+ * (the reaction-polish lesson — never clobber). text = `editFold.text(cid) ??
982
+ * base`; base is the seeded original so a forged/ignored edit leaves it intact.
983
+ * No-op when the target isn't present yet (the fold already recorded it; the
984
+ * overlay applies the moment the target lands) or when unchanged.
985
+ */
986
+ private recomputeEdit;
987
+ /**
988
+ * Overlay the authoritative edit fold's winning text + flag onto a message as it
989
+ * is appended/merged. The fold WINS when it has an edit for this target;
990
+ * otherwise the upstream `text`/`edited` (e.g. the coordinator's page-local
991
+ * history fold) is preserved. PRESERVES reactions + replyTo.
992
+ */
993
+ private applyEditOverlay;
937
994
  /** @internal — called by the backend's conv subscription. */
938
995
  applyConv(event: string, payload: Record<string, unknown>): void;
939
996
  private kindOf;
@@ -963,6 +1020,14 @@ declare class Chat {
963
1020
  /** Remove this user's emoji reaction from a message (op:'remove'). */
964
1021
  unreact(message: ChatMessage, emoji: string): Promise<void>;
965
1022
  private sendReaction;
1023
+ /** Edit an own text message (edit-by-supersession). No-op if the message isn't
1024
+ * editable (empty clientMsgId, or not a `text` kind). The edit folds locally
1025
+ * with the server receipt's `(epoch, serverSeq)` so the target's text updates
1026
+ * instantly; the durable echo on the next pump is a fold no-op (dedup on the
1027
+ * SAME wire clientMsgId). Never appends a bubble; PRESERVES the target's
1028
+ * 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
+ edit(message: ChatMessage, newText: string): Promise<void>;
966
1031
  }
967
1032
 
968
1033
  declare class PalbeMessaging {
@@ -1354,4 +1419,4 @@ declare class PalbeAnalytics {
1354
1419
  private post;
1355
1420
  }
1356
1421
 
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 };
1422
+ 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,14 @@ 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;
759
767
  }
760
768
  /** The receipt from a send — the server's monotonic sequence + accepted epoch. */
761
769
  interface SentReceipt {
@@ -801,6 +809,10 @@ interface ReactionPayload {
801
809
  emoji: string;
802
810
  op: 'add' | 'remove';
803
811
  }
812
+ interface EditPayload {
813
+ targetClientMsgId: string;
814
+ newText: string;
815
+ }
804
816
 
805
817
  /** A decoded incoming message handed to chat listeners. */
806
818
  interface IncomingMessage {
@@ -824,6 +836,9 @@ interface IncomingMessage {
824
836
  envelopeType?: string;
825
837
  /** The decoded reaction payload, present only when `envelopeType === 'reaction'`. */
826
838
  reaction?: ReactionPayload | null;
839
+ /** The decoded edit payload, present only when `envelopeType === 'edit'`. Lets
840
+ * the Chat route an edit into its EditFold instead of appending a visible bubble. */
841
+ edit?: EditPayload | null;
827
842
  }
828
843
 
829
844
  /** The intended participants of a draft chat (held until the first send). */
@@ -858,6 +873,16 @@ interface ChatBackend {
858
873
  receipt: SentReceipt;
859
874
  clientMsgId: string;
860
875
  }>;
876
+ /** Send an edit (edit-by-supersession on a target message) through the same MLS
877
+ * application path as `sendText`. Returns the receipt + the wire clientMsgId. */
878
+ sendEdit(group: MessagingGroup, args: {
879
+ clientMsgId: string;
880
+ targetClientMsgId: string;
881
+ newText: string;
882
+ }): Promise<{
883
+ receipt: SentReceipt;
884
+ clientMsgId: string;
885
+ }>;
861
886
  history(group: MessagingGroup, limit: number, before?: number): Promise<ChatMessage[]>;
862
887
  members(group: MessagingGroup): Promise<ChatMember[]>;
863
888
  addMember(group: MessagingGroup, userId: string): Promise<void>;
@@ -890,6 +915,14 @@ declare class Chat {
890
915
  private readonly byClientMsgId;
891
916
  /** The single authoritative reaction fold for this chat (live + own-send + history). */
892
917
  private readonly reactionFold;
918
+ /** The single authoritative edit fold for this chat (live + own-send + history). */
919
+ private readonly editFold;
920
+ /** Per-target BASE (original) text, so the rendered text is `edit ?? original`.
921
+ * Seeded once per target (write-once base) so a 2nd own edit can't corrupt it. */
922
+ private readonly originalTextByClientMsgId;
923
+ /** Per-target AUTHOR userId — the EditFold author-gate input (filled at bubble
924
+ * projection time from senderUserId; '' = resolved-but-unknown peer). */
925
+ private readonly authorByClientMsgId;
893
926
  private loadedEarliestSeq;
894
927
  private historyLoaded;
895
928
  private wired;
@@ -922,6 +955,14 @@ declare class Chat {
922
955
  private mergeHistory;
923
956
  /** @internal — called by the backend's live subscription. */
924
957
  ingestLive(incoming: IncomingMessage): Promise<void>;
958
+ /** The EditFold author-gate input: the target message's resolved author userId
959
+ * (null = target unknown/dangling → the fold HOLDs). Captured as a bound arrow
960
+ * so it can be passed to the pure EditFold. */
961
+ private readonly authorOfTarget;
962
+ /** Seed the per-target base text + author for the edit fold. Base is write-once
963
+ * (a later own/peer edit must not overwrite the original we render against). The
964
+ * author is (re)recorded whenever a non-empty resolution is available. */
965
+ private seedEditBase;
925
966
  /**
926
967
  * Rebuild the target message's `reactions` from the authoritative fold and
927
968
  * re-emit. No-op when the target isn't present yet (its tally is attached the
@@ -934,6 +975,22 @@ declare class Chat {
934
975
  * attached upstream (the coordinator's page-local history fold) is preserved.
935
976
  */
936
977
  private applyReactionTally;
978
+ /**
979
+ * Rebuild the target message's rendered `text` + `edited` flag from the
980
+ * authoritative edit fold and re-emit, PRESERVING `.reactions` and `.replyTo`
981
+ * (the reaction-polish lesson — never clobber). text = `editFold.text(cid) ??
982
+ * base`; base is the seeded original so a forged/ignored edit leaves it intact.
983
+ * No-op when the target isn't present yet (the fold already recorded it; the
984
+ * overlay applies the moment the target lands) or when unchanged.
985
+ */
986
+ private recomputeEdit;
987
+ /**
988
+ * Overlay the authoritative edit fold's winning text + flag onto a message as it
989
+ * is appended/merged. The fold WINS when it has an edit for this target;
990
+ * otherwise the upstream `text`/`edited` (e.g. the coordinator's page-local
991
+ * history fold) is preserved. PRESERVES reactions + replyTo.
992
+ */
993
+ private applyEditOverlay;
937
994
  /** @internal — called by the backend's conv subscription. */
938
995
  applyConv(event: string, payload: Record<string, unknown>): void;
939
996
  private kindOf;
@@ -963,6 +1020,14 @@ declare class Chat {
963
1020
  /** Remove this user's emoji reaction from a message (op:'remove'). */
964
1021
  unreact(message: ChatMessage, emoji: string): Promise<void>;
965
1022
  private sendReaction;
1023
+ /** Edit an own text message (edit-by-supersession). No-op if the message isn't
1024
+ * editable (empty clientMsgId, or not a `text` kind). The edit folds locally
1025
+ * with the server receipt's `(epoch, serverSeq)` so the target's text updates
1026
+ * instantly; the durable echo on the next pump is a fold no-op (dedup on the
1027
+ * SAME wire clientMsgId). Never appends a bubble; PRESERVES the target's
1028
+ * 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
+ edit(message: ChatMessage, newText: string): Promise<void>;
966
1031
  }
967
1032
 
968
1033
  declare class PalbeMessaging {
@@ -1354,4 +1419,4 @@ declare class PalbeAnalytics {
1354
1419
  private post;
1355
1420
  }
1356
1421
 
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 };
1422
+ 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 };