@palbase/web 1.5.0 → 1.6.1
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-DvsLE_qz.d.cts → analytics-facade-ATGUv2-f.d.cts} +152 -4
- package/dist/{analytics-facade-BQlTfsEm.d.ts → analytics-facade-DhcTP_kw.d.ts} +152 -4
- package/dist/{chunk-MBA2NAKS.js → chunk-EQAHM3ZJ.js} +602 -48
- package/dist/chunk-EQAHM3ZJ.js.map +1 -0
- package/dist/index.cjs +601 -47
- 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 +601 -47
- 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 +601 -47
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.js +1 -1
- package/dist/next/index.cjs +601 -47
- 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-Cl7TeyCR.d.ts → pb-TAUNVyT6.d.ts} +1 -1
- package/dist/{pb-a86jrG9h.d.cts → pb-VzJvX7Gg.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 +2 -2
- package/dist/chunk-MBA2NAKS.js.map +0 -1
|
@@ -700,6 +700,24 @@ declare class PalbeFlags {
|
|
|
700
700
|
destroy(): void;
|
|
701
701
|
}
|
|
702
702
|
|
|
703
|
+
/**
|
|
704
|
+
* Pure deadline math — the web mirror of iOS DeadlineCalculator (Codex-finalized
|
|
705
|
+
* restart conversion rule). Identical algorithm; web uses ms internally and
|
|
706
|
+
* compares against ttlSeconds (converted). Clock readings are passed in so it is
|
|
707
|
+
* fully table-testable and byte-identical with iOS.
|
|
708
|
+
*
|
|
709
|
+
* NOTE on the send-vs-arrival clamp: like iOS T3, this Calculator stays PURE — it
|
|
710
|
+
* consumes an already-resolved anchor + the current clock readings. The
|
|
711
|
+
* `min(sender_send_ts + ttl, first_arrival_monotonic + ttl)` effective-deadline
|
|
712
|
+
* basis is applied at ARM time by the caller (Chat, T10), which picks the anchor's
|
|
713
|
+
* monotonic baseline; the Calculator only converts that anchor into remaining/purge.
|
|
714
|
+
*/
|
|
715
|
+
interface AnchorTriple {
|
|
716
|
+
mAnchorMs: number;
|
|
717
|
+
wAnchorEpochMs: number;
|
|
718
|
+
bAnchorToken: string;
|
|
719
|
+
}
|
|
720
|
+
|
|
703
721
|
/** A direct (1:1) chat or a multi-party group. */
|
|
704
722
|
type ChatKind = 'direct' | 'group';
|
|
705
723
|
/** A draft (local-only, not yet on the server) or an active (materialized) chat. */
|
|
@@ -797,6 +815,16 @@ interface ChatMessage {
|
|
|
797
815
|
* DOMINATES); when it is edited they reflect the EDIT's replacement `body_ranges`.
|
|
798
816
|
*/
|
|
799
817
|
readonly mentions: ResolvedMention[];
|
|
818
|
+
/**
|
|
819
|
+
* The LOCAL purge deadline for this message (monotonic-derived, never a wire field).
|
|
820
|
+
* `null` when this message has no TTL. The UI may show a countdown; the durable truth
|
|
821
|
+
* is the persisted anchor + a re-check on every load (disappearing T10). A message's
|
|
822
|
+
* effective TTL is its OWN per-message `expiry` if present, else the chat default
|
|
823
|
+
* `timer_set` active AS OF this message's order (forward-only — a message ordered
|
|
824
|
+
* BEFORE the governing `timer_set` inherits nothing; own-expiry WINS over the default).
|
|
825
|
+
* Mirrors iOS `ChatMessage.effectiveExpiry` (which it derives from).
|
|
826
|
+
*/
|
|
827
|
+
readonly expiresAt: Date | null;
|
|
800
828
|
}
|
|
801
829
|
/** The receipt from a send — the server's monotonic sequence + accepted epoch. */
|
|
802
830
|
interface SentReceipt {
|
|
@@ -859,6 +887,13 @@ interface DeletePayload {
|
|
|
859
887
|
targetClientMsgId: string;
|
|
860
888
|
scope: string;
|
|
861
889
|
}
|
|
890
|
+
/** Per-message TTL spec (decoded). senderSendTs present iff start === 'send'. */
|
|
891
|
+
interface ExpirySpec {
|
|
892
|
+
v: number;
|
|
893
|
+
ttlSeconds: number;
|
|
894
|
+
start: 'send' | 'read';
|
|
895
|
+
senderSendTs: number | null;
|
|
896
|
+
}
|
|
862
897
|
|
|
863
898
|
/** A decoded incoming message handed to chat listeners. */
|
|
864
899
|
interface IncomingMessage {
|
|
@@ -893,6 +928,16 @@ interface IncomingMessage {
|
|
|
893
928
|
* with `body_ranges` OR on an `'edit'` (the edit's replacement ranges). The Chat
|
|
894
929
|
* normalizes + resolves roster names → `ChatMessage.mentions` (mentions T6). */
|
|
895
930
|
bodyRanges?: MentionRange[] | null;
|
|
931
|
+
/** The decoded `timer_set` payload, present only when `envelopeType === 'timer_set'`
|
|
932
|
+
* (disappearing T10). Lets the Chat route a chat-default timer into its TimerFold
|
|
933
|
+
* instead of appending a visible bubble. */
|
|
934
|
+
timer?: {
|
|
935
|
+
ttlSeconds: number | null;
|
|
936
|
+
start: 'send' | 'read';
|
|
937
|
+
} | null;
|
|
938
|
+
/** The decoded per-message TTL spec, present only on a `'text'` bubble that carried an
|
|
939
|
+
* `expiry` (disappearing T10). Lets the Chat arm the message's purge on first display. */
|
|
940
|
+
expiry?: ExpirySpec | null;
|
|
896
941
|
}
|
|
897
942
|
|
|
898
943
|
/** The intended participants of a draft chat (held until the first send). */
|
|
@@ -912,7 +957,7 @@ interface ChatBackend {
|
|
|
912
957
|
selfUserId: string;
|
|
913
958
|
/** Materialize a draft → the active group (DM get-or-create / group create+add). */
|
|
914
959
|
materialize(draft: ChatDraft): Promise<MessagingGroup>;
|
|
915
|
-
sendText(group: MessagingGroup, text: string, replyTo?: ReplyRef | null, bodyRanges?: MentionRange[] | null): Promise<{
|
|
960
|
+
sendText(group: MessagingGroup, text: string, replyTo?: ReplyRef | null, bodyRanges?: MentionRange[] | null, expiry?: ExpirySpec | null): Promise<{
|
|
916
961
|
receipt: SentReceipt;
|
|
917
962
|
clientMsgId: string;
|
|
918
963
|
}>;
|
|
@@ -969,6 +1014,31 @@ interface ChatBackend {
|
|
|
969
1014
|
loadElevated(group: MessagingGroup): Promise<string[]>;
|
|
970
1015
|
/** Persist this chat's self-elevation dedup keys (durable, no wire). */
|
|
971
1016
|
saveElevated(group: MessagingGroup, keys: string[]): Promise<void>;
|
|
1017
|
+
/** Send a per-chat default disappearing-timer control envelope (`timer_set`). Goes
|
|
1018
|
+
* through the SAME MLS application path as `sendText` — server-blind, never a bubble.
|
|
1019
|
+
* `ttlSeconds === null` disables the default. Returns the receipt + the wire clientMsgId. */
|
|
1020
|
+
sendTimerSet(group: MessagingGroup, args: {
|
|
1021
|
+
clientMsgId: string;
|
|
1022
|
+
ttlSeconds: number | null;
|
|
1023
|
+
start: 'send' | 'read';
|
|
1024
|
+
}): Promise<{
|
|
1025
|
+
receipt: SentReceipt;
|
|
1026
|
+
clientMsgId: string;
|
|
1027
|
+
}>;
|
|
1028
|
+
/** The persisted INTEGER `server_seq` tombstone set for a chat — drives transcript
|
|
1029
|
+
* exclusion (cold-launch) + redelivery drop (live arrival). Durable, no wire. */
|
|
1030
|
+
tombstonedSeqs(group: MessagingGroup): Promise<Set<number>>;
|
|
1031
|
+
/** The persisted STRING `client_msg_id` purge set — drives the orphan-fold resolver
|
|
1032
|
+
* (`authorOfTarget → 'purged'`) so a late edit DROPs / a late delete no-ops. */
|
|
1033
|
+
purgedClientMsgIds(group: MessagingGroup): Promise<Set<string>>;
|
|
1034
|
+
/** The persisted write-once anchor triple for a `clientMsgId`, or null if none. */
|
|
1035
|
+
anchor(group: MessagingGroup, clientMsgId: string): Promise<AnchorTriple | null>;
|
|
1036
|
+
/** Write-once: capture the first-arrival/first-read anchor triple for a `clientMsgId`
|
|
1037
|
+
* (a second call for the same id is a no-op, so the deadline never resets). */
|
|
1038
|
+
writeAnchorOnce(group: MessagingGroup, clientMsgId: string, a: AnchorTriple): Promise<void>;
|
|
1039
|
+
/** Tombstone-first purge commit point: persist the int `server_seq` + string
|
|
1040
|
+
* `client_msg_id` purge id in ONE durable record (idempotent). */
|
|
1041
|
+
tombstone(group: MessagingGroup, serverSeq: number, clientMsgId: string): Promise<void>;
|
|
972
1042
|
}
|
|
973
1043
|
declare class Chat {
|
|
974
1044
|
/** Stable, URL/log-safe id (the grp_ display id once active; a reserved local id while draft). */
|
|
@@ -994,6 +1064,22 @@ declare class Chat {
|
|
|
994
1064
|
/** The single authoritative delete-for-everyone fold (live + own-send + history).
|
|
995
1065
|
* A tombstone scrubs its target in place (delete DOMINATES edit at render). */
|
|
996
1066
|
private readonly deleteFold;
|
|
1067
|
+
/** The per-chat default disappearing-timer fold — the latest valid `timer_set` (LWW
|
|
1068
|
+
* on (epoch, serverSeq), author = the resolved MLS sender). A `timer_set` is NEVER a
|
|
1069
|
+
* bubble; it routes here. The active default governs a subsequent bubble that carries
|
|
1070
|
+
* no per-message expiry (disappearing T10). */
|
|
1071
|
+
private readonly timerFold;
|
|
1072
|
+
/** Advisory in-memory purge timers, keyed by serverSeq. The DURABLE truth is the
|
|
1073
|
+
* persisted anchor + a re-check on every load; this just drives live eviction while
|
|
1074
|
+
* the tab is open. Cancelled when the message purges (disappearing T10). */
|
|
1075
|
+
private readonly purgeTimers;
|
|
1076
|
+
/** In-memory mirror of the durable `purgedClientMsgIds` set (the STRING namespace),
|
|
1077
|
+
* hydrated from `backend.purgedClientMsgIds` and grown by each live purge. Consulted
|
|
1078
|
+
* by `authorOfTarget` so a late edit/delete targeting a TTL-purged message resolves to
|
|
1079
|
+
* `'purged'` (DROP / no-op — never resurrects). Namespace-separate from the int seq
|
|
1080
|
+
* tombstone (disappearing T10). */
|
|
1081
|
+
private readonly purgedCids;
|
|
1082
|
+
private purgedLoaded;
|
|
997
1083
|
/** delete-for-me suppression keys (clientMsgId, or `seq:<serverSeq>` for legacy)
|
|
998
1084
|
* — the message is OMITTED from this view. Local + persisted per chat, NO wire. */
|
|
999
1085
|
private readonly suppressed;
|
|
@@ -1063,6 +1149,10 @@ declare class Chat {
|
|
|
1063
1149
|
get title(): string;
|
|
1064
1150
|
presence(userId: string): PresenceState | null;
|
|
1065
1151
|
private ensureWired;
|
|
1152
|
+
/** Hydrate the durable `purgedClientMsgIds` set (once) into the in-memory mirror so the
|
|
1153
|
+
* live Edit/Delete fold author-gate sees TTL-purged targets as 'purged' on cold launch
|
|
1154
|
+
* (disappearing T10). No re-emit: it only gates the orphan-fold resolution. */
|
|
1155
|
+
private loadPurged;
|
|
1066
1156
|
/** Hydrate the persisted delete-for-me suppression keys (once), then re-emit so
|
|
1067
1157
|
* any already-surfaced suppressed message is omitted (cold-launch parity). */
|
|
1068
1158
|
private loadSuppressed;
|
|
@@ -1073,9 +1163,45 @@ declare class Chat {
|
|
|
1073
1163
|
private mergeHistory;
|
|
1074
1164
|
/** @internal — called by the backend's live subscription. */
|
|
1075
1165
|
ingestLive(incoming: IncomingMessage): Promise<void>;
|
|
1076
|
-
/**
|
|
1077
|
-
*
|
|
1078
|
-
|
|
1166
|
+
/** Normalize a decoded `IncomingMessage.expiry` / `StoredMessage.expiry` into the
|
|
1167
|
+
* `ExpirySpec` the arm path consumes (or null when absent). */
|
|
1168
|
+
private toExpirySpec;
|
|
1169
|
+
/** The chat-default expiry derived from the active `timer_set` fold, as an `ExpirySpec`
|
|
1170
|
+
* so a bubble with no per-message expiry inherits it. null when no default is active or
|
|
1171
|
+
* the default was explicitly DISABLED (`ttlSeconds === null`). `senderSendTs` is null —
|
|
1172
|
+
* the default has no per-message sender clock; the arrival anchor drives the deadline
|
|
1173
|
+
* (mirrors iOS `defaultExpiry()`). */
|
|
1174
|
+
private defaultExpiry;
|
|
1175
|
+
/** The surfaced display deadline for an effective expiry (a local, monotonic-derived
|
|
1176
|
+
* value — the wall-clock projection of the TTL from now). null for a non-disappearing
|
|
1177
|
+
* message. The durable purge is driven by `armPurge`'s write-once anchor; this is the
|
|
1178
|
+
* UI countdown baseline. */
|
|
1179
|
+
private deadlineFor;
|
|
1180
|
+
/** Arm a message's TTL purge on first decrypt-and-display. Captures the WRITE-ONCE
|
|
1181
|
+
* monotonic/wall/boot anchor (so the deadline survives a reload — a re-arm after relaunch
|
|
1182
|
+
* reads back the ORIGINAL capture, never a fresh one → the deadline never resets),
|
|
1183
|
+
* computes the remaining time via `remainingSeconds`, applies the send-anchor clamp
|
|
1184
|
+
* `min(sender_send_ts+ttl, first_arrival+ttl)` (read-anchor uses the write-once first-read
|
|
1185
|
+
* capture), then either purges immediately or schedules an advisory `setTimeout`. A null
|
|
1186
|
+
* expiry / empty clientMsgId is a no-op. Mirrors iOS `armPurge`. */
|
|
1187
|
+
private armPurge;
|
|
1188
|
+
/** Re-arm a purge from a derived deadline (cold-launch hydrate path). The deadline is
|
|
1189
|
+
* the projection's monotonic-derived `expiresAt`; schedule an advisory timer for the
|
|
1190
|
+
* remaining time (purge immediately if the deadline has already passed). The durable
|
|
1191
|
+
* tombstone is written by `purge` when it fires (the crash-safe commit point). */
|
|
1192
|
+
private armFromDeadline;
|
|
1193
|
+
/** Purge message M (TTL eviction). TOMBSTONE-FIRST (the crash-safe commit point):
|
|
1194
|
+
* persist the `server_seq` tombstone + the `client_msg_id` purge id in ONE durable
|
|
1195
|
+
* record, THEN drop M's body from `messageList` + `emit()`, then re-evaluate any HELD
|
|
1196
|
+
* edit / PARKED delete targeting the now-purged cid so an orphan annotation DROPs/no-ops
|
|
1197
|
+
* (the resolver now returns `'purged'`). Idempotent. Mirrors iOS `purge`. */
|
|
1198
|
+
private purge;
|
|
1199
|
+
/** The Edit/Delete fold author-gate input via {@link AuthorResolution} (disappearing
|
|
1200
|
+
* T10 — the orphan-aware resolver): `'purged'` when the target's clientMsgId is in the
|
|
1201
|
+
* durable purge set (a late edit DROPs / a late delete no-ops — never resurrects a
|
|
1202
|
+
* disappeared message); `'author'` when its author is locally known → run the
|
|
1203
|
+
* author-gate; `'unknown'` otherwise → HOLD. The live twin of `projectHistory`'s
|
|
1204
|
+
* resolver. Captured as a bound arrow so it can be passed to the pure folds. */
|
|
1079
1205
|
private readonly authorOfTarget;
|
|
1080
1206
|
/** Resolve a message's decode-time mention spans: NORMALIZE the raw `body_ranges`
|
|
1081
1207
|
* against `text` (the pinned `normalizeMentionRangesUtf16` cross-SDK contract) then
|
|
@@ -1150,7 +1276,29 @@ declare class Chat {
|
|
|
1150
1276
|
send(text: string, opts?: {
|
|
1151
1277
|
replyTo?: ChatMessage;
|
|
1152
1278
|
mentions?: MentionRange[];
|
|
1279
|
+
expiresIn?: {
|
|
1280
|
+
ttlSeconds: number;
|
|
1281
|
+
start?: 'send' | 'read';
|
|
1282
|
+
};
|
|
1153
1283
|
}): Promise<SentReceipt>;
|
|
1284
|
+
/** Resolve a send's effective per-message expiry at COMPOSE TIME: the caller's explicit
|
|
1285
|
+
* `expiresIn` if present, ELSE the chat's active default timer stamped onto the message
|
|
1286
|
+
* NOW (the durable record per spec §"Compose-time stamping"). A `send`-anchored expiry
|
|
1287
|
+
* (explicit or default-inherited) stamps `senderSendTs` = the sender's compose epoch
|
|
1288
|
+
* seconds; a `read`-anchored one carries none (the deadline is the recipient's local
|
|
1289
|
+
* first-read). Returns null when there is neither an explicit expiry nor an active
|
|
1290
|
+
* default (a plain, non-disappearing send). Mirrors the iOS compose-time stamping. */
|
|
1291
|
+
private composeExpiry;
|
|
1292
|
+
/** Set (or DISABLE) this chat's DEFAULT disappearing timer. Emits a `timer_set` control
|
|
1293
|
+
* envelope (server-blind — an opaque application message, NEVER a bubble) and folds the
|
|
1294
|
+
* own-set locally so the default applies immediately to subsequent sends that carry no
|
|
1295
|
+
* per-message expiry. `ttlSeconds === null` DISABLES the default. FIRE-AND-FORGET
|
|
1296
|
+
* ADVISORY: returns on the LOCAL emit only; it exposes NO "active for all peers" signal.
|
|
1297
|
+
* Mirrors iOS `setDisappearing(ttlSeconds:start:)`. */
|
|
1298
|
+
setDisappearing(opts: {
|
|
1299
|
+
ttlSeconds: number | null;
|
|
1300
|
+
start?: 'send' | 'read';
|
|
1301
|
+
}): Promise<void>;
|
|
1154
1302
|
private appendOwnSend;
|
|
1155
1303
|
private materializeIfNeeded;
|
|
1156
1304
|
addMemberUser(userId: string): Promise<void>;
|
|
@@ -700,6 +700,24 @@ declare class PalbeFlags {
|
|
|
700
700
|
destroy(): void;
|
|
701
701
|
}
|
|
702
702
|
|
|
703
|
+
/**
|
|
704
|
+
* Pure deadline math — the web mirror of iOS DeadlineCalculator (Codex-finalized
|
|
705
|
+
* restart conversion rule). Identical algorithm; web uses ms internally and
|
|
706
|
+
* compares against ttlSeconds (converted). Clock readings are passed in so it is
|
|
707
|
+
* fully table-testable and byte-identical with iOS.
|
|
708
|
+
*
|
|
709
|
+
* NOTE on the send-vs-arrival clamp: like iOS T3, this Calculator stays PURE — it
|
|
710
|
+
* consumes an already-resolved anchor + the current clock readings. The
|
|
711
|
+
* `min(sender_send_ts + ttl, first_arrival_monotonic + ttl)` effective-deadline
|
|
712
|
+
* basis is applied at ARM time by the caller (Chat, T10), which picks the anchor's
|
|
713
|
+
* monotonic baseline; the Calculator only converts that anchor into remaining/purge.
|
|
714
|
+
*/
|
|
715
|
+
interface AnchorTriple {
|
|
716
|
+
mAnchorMs: number;
|
|
717
|
+
wAnchorEpochMs: number;
|
|
718
|
+
bAnchorToken: string;
|
|
719
|
+
}
|
|
720
|
+
|
|
703
721
|
/** A direct (1:1) chat or a multi-party group. */
|
|
704
722
|
type ChatKind = 'direct' | 'group';
|
|
705
723
|
/** A draft (local-only, not yet on the server) or an active (materialized) chat. */
|
|
@@ -797,6 +815,16 @@ interface ChatMessage {
|
|
|
797
815
|
* DOMINATES); when it is edited they reflect the EDIT's replacement `body_ranges`.
|
|
798
816
|
*/
|
|
799
817
|
readonly mentions: ResolvedMention[];
|
|
818
|
+
/**
|
|
819
|
+
* The LOCAL purge deadline for this message (monotonic-derived, never a wire field).
|
|
820
|
+
* `null` when this message has no TTL. The UI may show a countdown; the durable truth
|
|
821
|
+
* is the persisted anchor + a re-check on every load (disappearing T10). A message's
|
|
822
|
+
* effective TTL is its OWN per-message `expiry` if present, else the chat default
|
|
823
|
+
* `timer_set` active AS OF this message's order (forward-only — a message ordered
|
|
824
|
+
* BEFORE the governing `timer_set` inherits nothing; own-expiry WINS over the default).
|
|
825
|
+
* Mirrors iOS `ChatMessage.effectiveExpiry` (which it derives from).
|
|
826
|
+
*/
|
|
827
|
+
readonly expiresAt: Date | null;
|
|
800
828
|
}
|
|
801
829
|
/** The receipt from a send — the server's monotonic sequence + accepted epoch. */
|
|
802
830
|
interface SentReceipt {
|
|
@@ -859,6 +887,13 @@ interface DeletePayload {
|
|
|
859
887
|
targetClientMsgId: string;
|
|
860
888
|
scope: string;
|
|
861
889
|
}
|
|
890
|
+
/** Per-message TTL spec (decoded). senderSendTs present iff start === 'send'. */
|
|
891
|
+
interface ExpirySpec {
|
|
892
|
+
v: number;
|
|
893
|
+
ttlSeconds: number;
|
|
894
|
+
start: 'send' | 'read';
|
|
895
|
+
senderSendTs: number | null;
|
|
896
|
+
}
|
|
862
897
|
|
|
863
898
|
/** A decoded incoming message handed to chat listeners. */
|
|
864
899
|
interface IncomingMessage {
|
|
@@ -893,6 +928,16 @@ interface IncomingMessage {
|
|
|
893
928
|
* with `body_ranges` OR on an `'edit'` (the edit's replacement ranges). The Chat
|
|
894
929
|
* normalizes + resolves roster names → `ChatMessage.mentions` (mentions T6). */
|
|
895
930
|
bodyRanges?: MentionRange[] | null;
|
|
931
|
+
/** The decoded `timer_set` payload, present only when `envelopeType === 'timer_set'`
|
|
932
|
+
* (disappearing T10). Lets the Chat route a chat-default timer into its TimerFold
|
|
933
|
+
* instead of appending a visible bubble. */
|
|
934
|
+
timer?: {
|
|
935
|
+
ttlSeconds: number | null;
|
|
936
|
+
start: 'send' | 'read';
|
|
937
|
+
} | null;
|
|
938
|
+
/** The decoded per-message TTL spec, present only on a `'text'` bubble that carried an
|
|
939
|
+
* `expiry` (disappearing T10). Lets the Chat arm the message's purge on first display. */
|
|
940
|
+
expiry?: ExpirySpec | null;
|
|
896
941
|
}
|
|
897
942
|
|
|
898
943
|
/** The intended participants of a draft chat (held until the first send). */
|
|
@@ -912,7 +957,7 @@ interface ChatBackend {
|
|
|
912
957
|
selfUserId: string;
|
|
913
958
|
/** Materialize a draft → the active group (DM get-or-create / group create+add). */
|
|
914
959
|
materialize(draft: ChatDraft): Promise<MessagingGroup>;
|
|
915
|
-
sendText(group: MessagingGroup, text: string, replyTo?: ReplyRef | null, bodyRanges?: MentionRange[] | null): Promise<{
|
|
960
|
+
sendText(group: MessagingGroup, text: string, replyTo?: ReplyRef | null, bodyRanges?: MentionRange[] | null, expiry?: ExpirySpec | null): Promise<{
|
|
916
961
|
receipt: SentReceipt;
|
|
917
962
|
clientMsgId: string;
|
|
918
963
|
}>;
|
|
@@ -969,6 +1014,31 @@ interface ChatBackend {
|
|
|
969
1014
|
loadElevated(group: MessagingGroup): Promise<string[]>;
|
|
970
1015
|
/** Persist this chat's self-elevation dedup keys (durable, no wire). */
|
|
971
1016
|
saveElevated(group: MessagingGroup, keys: string[]): Promise<void>;
|
|
1017
|
+
/** Send a per-chat default disappearing-timer control envelope (`timer_set`). Goes
|
|
1018
|
+
* through the SAME MLS application path as `sendText` — server-blind, never a bubble.
|
|
1019
|
+
* `ttlSeconds === null` disables the default. Returns the receipt + the wire clientMsgId. */
|
|
1020
|
+
sendTimerSet(group: MessagingGroup, args: {
|
|
1021
|
+
clientMsgId: string;
|
|
1022
|
+
ttlSeconds: number | null;
|
|
1023
|
+
start: 'send' | 'read';
|
|
1024
|
+
}): Promise<{
|
|
1025
|
+
receipt: SentReceipt;
|
|
1026
|
+
clientMsgId: string;
|
|
1027
|
+
}>;
|
|
1028
|
+
/** The persisted INTEGER `server_seq` tombstone set for a chat — drives transcript
|
|
1029
|
+
* exclusion (cold-launch) + redelivery drop (live arrival). Durable, no wire. */
|
|
1030
|
+
tombstonedSeqs(group: MessagingGroup): Promise<Set<number>>;
|
|
1031
|
+
/** The persisted STRING `client_msg_id` purge set — drives the orphan-fold resolver
|
|
1032
|
+
* (`authorOfTarget → 'purged'`) so a late edit DROPs / a late delete no-ops. */
|
|
1033
|
+
purgedClientMsgIds(group: MessagingGroup): Promise<Set<string>>;
|
|
1034
|
+
/** The persisted write-once anchor triple for a `clientMsgId`, or null if none. */
|
|
1035
|
+
anchor(group: MessagingGroup, clientMsgId: string): Promise<AnchorTriple | null>;
|
|
1036
|
+
/** Write-once: capture the first-arrival/first-read anchor triple for a `clientMsgId`
|
|
1037
|
+
* (a second call for the same id is a no-op, so the deadline never resets). */
|
|
1038
|
+
writeAnchorOnce(group: MessagingGroup, clientMsgId: string, a: AnchorTriple): Promise<void>;
|
|
1039
|
+
/** Tombstone-first purge commit point: persist the int `server_seq` + string
|
|
1040
|
+
* `client_msg_id` purge id in ONE durable record (idempotent). */
|
|
1041
|
+
tombstone(group: MessagingGroup, serverSeq: number, clientMsgId: string): Promise<void>;
|
|
972
1042
|
}
|
|
973
1043
|
declare class Chat {
|
|
974
1044
|
/** Stable, URL/log-safe id (the grp_ display id once active; a reserved local id while draft). */
|
|
@@ -994,6 +1064,22 @@ declare class Chat {
|
|
|
994
1064
|
/** The single authoritative delete-for-everyone fold (live + own-send + history).
|
|
995
1065
|
* A tombstone scrubs its target in place (delete DOMINATES edit at render). */
|
|
996
1066
|
private readonly deleteFold;
|
|
1067
|
+
/** The per-chat default disappearing-timer fold — the latest valid `timer_set` (LWW
|
|
1068
|
+
* on (epoch, serverSeq), author = the resolved MLS sender). A `timer_set` is NEVER a
|
|
1069
|
+
* bubble; it routes here. The active default governs a subsequent bubble that carries
|
|
1070
|
+
* no per-message expiry (disappearing T10). */
|
|
1071
|
+
private readonly timerFold;
|
|
1072
|
+
/** Advisory in-memory purge timers, keyed by serverSeq. The DURABLE truth is the
|
|
1073
|
+
* persisted anchor + a re-check on every load; this just drives live eviction while
|
|
1074
|
+
* the tab is open. Cancelled when the message purges (disappearing T10). */
|
|
1075
|
+
private readonly purgeTimers;
|
|
1076
|
+
/** In-memory mirror of the durable `purgedClientMsgIds` set (the STRING namespace),
|
|
1077
|
+
* hydrated from `backend.purgedClientMsgIds` and grown by each live purge. Consulted
|
|
1078
|
+
* by `authorOfTarget` so a late edit/delete targeting a TTL-purged message resolves to
|
|
1079
|
+
* `'purged'` (DROP / no-op — never resurrects). Namespace-separate from the int seq
|
|
1080
|
+
* tombstone (disappearing T10). */
|
|
1081
|
+
private readonly purgedCids;
|
|
1082
|
+
private purgedLoaded;
|
|
997
1083
|
/** delete-for-me suppression keys (clientMsgId, or `seq:<serverSeq>` for legacy)
|
|
998
1084
|
* — the message is OMITTED from this view. Local + persisted per chat, NO wire. */
|
|
999
1085
|
private readonly suppressed;
|
|
@@ -1063,6 +1149,10 @@ declare class Chat {
|
|
|
1063
1149
|
get title(): string;
|
|
1064
1150
|
presence(userId: string): PresenceState | null;
|
|
1065
1151
|
private ensureWired;
|
|
1152
|
+
/** Hydrate the durable `purgedClientMsgIds` set (once) into the in-memory mirror so the
|
|
1153
|
+
* live Edit/Delete fold author-gate sees TTL-purged targets as 'purged' on cold launch
|
|
1154
|
+
* (disappearing T10). No re-emit: it only gates the orphan-fold resolution. */
|
|
1155
|
+
private loadPurged;
|
|
1066
1156
|
/** Hydrate the persisted delete-for-me suppression keys (once), then re-emit so
|
|
1067
1157
|
* any already-surfaced suppressed message is omitted (cold-launch parity). */
|
|
1068
1158
|
private loadSuppressed;
|
|
@@ -1073,9 +1163,45 @@ declare class Chat {
|
|
|
1073
1163
|
private mergeHistory;
|
|
1074
1164
|
/** @internal — called by the backend's live subscription. */
|
|
1075
1165
|
ingestLive(incoming: IncomingMessage): Promise<void>;
|
|
1076
|
-
/**
|
|
1077
|
-
*
|
|
1078
|
-
|
|
1166
|
+
/** Normalize a decoded `IncomingMessage.expiry` / `StoredMessage.expiry` into the
|
|
1167
|
+
* `ExpirySpec` the arm path consumes (or null when absent). */
|
|
1168
|
+
private toExpirySpec;
|
|
1169
|
+
/** The chat-default expiry derived from the active `timer_set` fold, as an `ExpirySpec`
|
|
1170
|
+
* so a bubble with no per-message expiry inherits it. null when no default is active or
|
|
1171
|
+
* the default was explicitly DISABLED (`ttlSeconds === null`). `senderSendTs` is null —
|
|
1172
|
+
* the default has no per-message sender clock; the arrival anchor drives the deadline
|
|
1173
|
+
* (mirrors iOS `defaultExpiry()`). */
|
|
1174
|
+
private defaultExpiry;
|
|
1175
|
+
/** The surfaced display deadline for an effective expiry (a local, monotonic-derived
|
|
1176
|
+
* value — the wall-clock projection of the TTL from now). null for a non-disappearing
|
|
1177
|
+
* message. The durable purge is driven by `armPurge`'s write-once anchor; this is the
|
|
1178
|
+
* UI countdown baseline. */
|
|
1179
|
+
private deadlineFor;
|
|
1180
|
+
/** Arm a message's TTL purge on first decrypt-and-display. Captures the WRITE-ONCE
|
|
1181
|
+
* monotonic/wall/boot anchor (so the deadline survives a reload — a re-arm after relaunch
|
|
1182
|
+
* reads back the ORIGINAL capture, never a fresh one → the deadline never resets),
|
|
1183
|
+
* computes the remaining time via `remainingSeconds`, applies the send-anchor clamp
|
|
1184
|
+
* `min(sender_send_ts+ttl, first_arrival+ttl)` (read-anchor uses the write-once first-read
|
|
1185
|
+
* capture), then either purges immediately or schedules an advisory `setTimeout`. A null
|
|
1186
|
+
* expiry / empty clientMsgId is a no-op. Mirrors iOS `armPurge`. */
|
|
1187
|
+
private armPurge;
|
|
1188
|
+
/** Re-arm a purge from a derived deadline (cold-launch hydrate path). The deadline is
|
|
1189
|
+
* the projection's monotonic-derived `expiresAt`; schedule an advisory timer for the
|
|
1190
|
+
* remaining time (purge immediately if the deadline has already passed). The durable
|
|
1191
|
+
* tombstone is written by `purge` when it fires (the crash-safe commit point). */
|
|
1192
|
+
private armFromDeadline;
|
|
1193
|
+
/** Purge message M (TTL eviction). TOMBSTONE-FIRST (the crash-safe commit point):
|
|
1194
|
+
* persist the `server_seq` tombstone + the `client_msg_id` purge id in ONE durable
|
|
1195
|
+
* record, THEN drop M's body from `messageList` + `emit()`, then re-evaluate any HELD
|
|
1196
|
+
* edit / PARKED delete targeting the now-purged cid so an orphan annotation DROPs/no-ops
|
|
1197
|
+
* (the resolver now returns `'purged'`). Idempotent. Mirrors iOS `purge`. */
|
|
1198
|
+
private purge;
|
|
1199
|
+
/** The Edit/Delete fold author-gate input via {@link AuthorResolution} (disappearing
|
|
1200
|
+
* T10 — the orphan-aware resolver): `'purged'` when the target's clientMsgId is in the
|
|
1201
|
+
* durable purge set (a late edit DROPs / a late delete no-ops — never resurrects a
|
|
1202
|
+
* disappeared message); `'author'` when its author is locally known → run the
|
|
1203
|
+
* author-gate; `'unknown'` otherwise → HOLD. The live twin of `projectHistory`'s
|
|
1204
|
+
* resolver. Captured as a bound arrow so it can be passed to the pure folds. */
|
|
1079
1205
|
private readonly authorOfTarget;
|
|
1080
1206
|
/** Resolve a message's decode-time mention spans: NORMALIZE the raw `body_ranges`
|
|
1081
1207
|
* against `text` (the pinned `normalizeMentionRangesUtf16` cross-SDK contract) then
|
|
@@ -1150,7 +1276,29 @@ declare class Chat {
|
|
|
1150
1276
|
send(text: string, opts?: {
|
|
1151
1277
|
replyTo?: ChatMessage;
|
|
1152
1278
|
mentions?: MentionRange[];
|
|
1279
|
+
expiresIn?: {
|
|
1280
|
+
ttlSeconds: number;
|
|
1281
|
+
start?: 'send' | 'read';
|
|
1282
|
+
};
|
|
1153
1283
|
}): Promise<SentReceipt>;
|
|
1284
|
+
/** Resolve a send's effective per-message expiry at COMPOSE TIME: the caller's explicit
|
|
1285
|
+
* `expiresIn` if present, ELSE the chat's active default timer stamped onto the message
|
|
1286
|
+
* NOW (the durable record per spec §"Compose-time stamping"). A `send`-anchored expiry
|
|
1287
|
+
* (explicit or default-inherited) stamps `senderSendTs` = the sender's compose epoch
|
|
1288
|
+
* seconds; a `read`-anchored one carries none (the deadline is the recipient's local
|
|
1289
|
+
* first-read). Returns null when there is neither an explicit expiry nor an active
|
|
1290
|
+
* default (a plain, non-disappearing send). Mirrors the iOS compose-time stamping. */
|
|
1291
|
+
private composeExpiry;
|
|
1292
|
+
/** Set (or DISABLE) this chat's DEFAULT disappearing timer. Emits a `timer_set` control
|
|
1293
|
+
* envelope (server-blind — an opaque application message, NEVER a bubble) and folds the
|
|
1294
|
+
* own-set locally so the default applies immediately to subsequent sends that carry no
|
|
1295
|
+
* per-message expiry. `ttlSeconds === null` DISABLES the default. FIRE-AND-FORGET
|
|
1296
|
+
* ADVISORY: returns on the LOCAL emit only; it exposes NO "active for all peers" signal.
|
|
1297
|
+
* Mirrors iOS `setDisappearing(ttlSeconds:start:)`. */
|
|
1298
|
+
setDisappearing(opts: {
|
|
1299
|
+
ttlSeconds: number | null;
|
|
1300
|
+
start?: 'send' | 'read';
|
|
1301
|
+
}): Promise<void>;
|
|
1154
1302
|
private appendOwnSend;
|
|
1155
1303
|
private materializeIfNeeded;
|
|
1156
1304
|
addMemberUser(userId: string): Promise<void>;
|