@openmarket/rooms-client 0.6.0 → 0.7.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/agent/armed-mode.js +7 -3
- package/dist/agent/clients/common.d.ts +5 -0
- package/dist/agent/topic-inbox.d.ts +6 -0
- package/dist/agent/topic-inbox.js +20 -5
- package/dist/shared/rooms-protocol.d.ts +82 -0
- package/dist/social-client.d.ts +174 -5
- package/dist/social-client.js +341 -5
- package/dist/social-types.d.ts +164 -1
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
- package/src/agent/armed-mode.ts +7 -3
- package/src/agent/clients/common.ts +5 -0
- package/src/agent/topic-inbox.ts +23 -5
- package/src/shared/rooms-protocol.ts +85 -0
- package/src/social-client.ts +567 -6
- package/src/social-types.ts +175 -1
- package/src/types.ts +3 -0
package/src/agent/topic-inbox.ts
CHANGED
|
@@ -26,6 +26,20 @@ import type {
|
|
|
26
26
|
/** The reserved bucket key for untopicked (linear) messages. */
|
|
27
27
|
export const UNTOPICKED_BUCKET = "";
|
|
28
28
|
|
|
29
|
+
/** Effective attention for a bucket: an elapsed timed mute reads as the
|
|
30
|
+
* base attention ('default'). The store expires timed mutes lazily (no
|
|
31
|
+
* deadline-triggered frame), so a hydrated inbox would otherwise stay
|
|
32
|
+
* muted past the deadline until the next hydration. A null or absent
|
|
33
|
+
* deadline keeps the stored attention (permanent mute). */
|
|
34
|
+
export function resolveTopicAttention(
|
|
35
|
+
state: Pick<RoomTopicBucketState, "attention" | "muteUntil">,
|
|
36
|
+
nowMs: number = Date.now(),
|
|
37
|
+
): RoomTopicAttention {
|
|
38
|
+
if (state.attention !== "mute" || state.muteUntil == null) return state.attention;
|
|
39
|
+
const deadline = Date.parse(state.muteUntil);
|
|
40
|
+
return Number.isFinite(deadline) && deadline <= nowMs ? "default" : "mute";
|
|
41
|
+
}
|
|
42
|
+
|
|
29
43
|
export interface TopicInboxItem {
|
|
30
44
|
/** Bucket key: a topicId, or UNTOPICKED_BUCKET for the linear lane. */
|
|
31
45
|
key: string;
|
|
@@ -141,7 +155,7 @@ export class RoomTopicInbox {
|
|
|
141
155
|
}
|
|
142
156
|
|
|
143
157
|
attentionFor(topicId: string): RoomTopicAttention {
|
|
144
|
-
return this.bucketFor(topicId)
|
|
158
|
+
return resolveTopicAttention(this.bucketFor(topicId));
|
|
145
159
|
}
|
|
146
160
|
|
|
147
161
|
/** A registry mutation arrived (TOPIC_UPDATED, or a verb result echo). */
|
|
@@ -196,6 +210,10 @@ export class RoomTopicInbox {
|
|
|
196
210
|
this.buckets.set(topicId, bucket);
|
|
197
211
|
}
|
|
198
212
|
bucket.attention = attention;
|
|
213
|
+
// The mirror knows only the new attention: a stale deadline from a
|
|
214
|
+
// previous timed mute must not expire it. The next hydration restores
|
|
215
|
+
// any server-side deadline.
|
|
216
|
+
delete bucket.muteUntil;
|
|
199
217
|
}
|
|
200
218
|
|
|
201
219
|
/**
|
|
@@ -224,7 +242,7 @@ export class RoomTopicInbox {
|
|
|
224
242
|
channelUnread(): number {
|
|
225
243
|
let total = 0;
|
|
226
244
|
for (const bucket of this.buckets.values()) {
|
|
227
|
-
if (bucket
|
|
245
|
+
if (resolveTopicAttention(bucket) === "mute") continue;
|
|
228
246
|
total += bucket.unreadCount;
|
|
229
247
|
}
|
|
230
248
|
return total;
|
|
@@ -241,7 +259,7 @@ export class RoomTopicInbox {
|
|
|
241
259
|
inboxItems(): TopicInboxItem[] {
|
|
242
260
|
const items: TopicInboxItem[] = [];
|
|
243
261
|
const untopicked = this.bucketFor(UNTOPICKED_BUCKET);
|
|
244
|
-
if (untopicked.unreadCount > 0 && untopicked
|
|
262
|
+
if (untopicked.unreadCount > 0 && resolveTopicAttention(untopicked) !== "mute") {
|
|
245
263
|
items.push({
|
|
246
264
|
key: UNTOPICKED_BUCKET,
|
|
247
265
|
topic: undefined,
|
|
@@ -254,11 +272,11 @@ export class RoomTopicInbox {
|
|
|
254
272
|
for (const topic of this.topics.values()) {
|
|
255
273
|
if (topic.mergedInto) continue;
|
|
256
274
|
const state = this.bucketFor(topic.topicId);
|
|
257
|
-
if (state
|
|
275
|
+
if (resolveTopicAttention(state) === "mute") continue;
|
|
258
276
|
let aliasUnread = 0;
|
|
259
277
|
for (const alias of this.aliasesOf(topic.topicId)) {
|
|
260
278
|
const aliasState = this.bucketFor(alias);
|
|
261
|
-
if (aliasState
|
|
279
|
+
if (resolveTopicAttention(aliasState) === "mute") continue;
|
|
262
280
|
aliasUnread += aliasState.unreadCount;
|
|
263
281
|
}
|
|
264
282
|
if (state.unreadCount + aliasUnread === 0) continue;
|
|
@@ -250,6 +250,19 @@ export interface RoomLedgerEntry {
|
|
|
250
250
|
since?: string | number | null | undefined;
|
|
251
251
|
reactions?: Array<{ emoji: string; count: number }> | undefined;
|
|
252
252
|
editedAt?: string | number | Date | undefined;
|
|
253
|
+
/** Database-serialized edit counter: positive integer, strictly
|
|
254
|
+
* increasing per event row, incremented on every successful text or
|
|
255
|
+
* caption edit; absent means never edited. Orders caption edits where
|
|
256
|
+
* wall-clock editedAt stamps can tie or skew across store replicas.
|
|
257
|
+
* Missing on stores older than edit revisions. */
|
|
258
|
+
editRevision?: number | undefined;
|
|
259
|
+
/** Database-serialized card-mutation counter: positive integer,
|
|
260
|
+
* strictly increasing per event row, bumped on every unfurl card
|
|
261
|
+
* mutation (attach, edit-drop, purge); absent means cards were never
|
|
262
|
+
* mutated. Consumers apply card patches only at or above their known
|
|
263
|
+
* revision, making stale post-removal unfurl frames inert. Missing on
|
|
264
|
+
* stores older than unfurl revisions. */
|
|
265
|
+
unfurlsRevision?: number | undefined;
|
|
253
266
|
deletedAt?: string | number | Date | undefined;
|
|
254
267
|
deletedBy?: string | undefined;
|
|
255
268
|
replyCount?: number | undefined;
|
|
@@ -258,6 +271,12 @@ export interface RoomLedgerEntry {
|
|
|
258
271
|
pinnedBy?: string | null | undefined;
|
|
259
272
|
pinnedAt?: string | number | Date | null | undefined;
|
|
260
273
|
attachments?: RoomAttachment[] | undefined;
|
|
274
|
+
/** Counts-only poll summary when this message carries a poll. Additive:
|
|
275
|
+
* non-poll entries omit it, and live updates ride ENTRY_UPDATED. */
|
|
276
|
+
poll?: RoomPollSummary | null | undefined;
|
|
277
|
+
/** Server-rendered link unfurl cards. Additive: entries without links
|
|
278
|
+
* (and servers older than the unfurl worker) omit it. */
|
|
279
|
+
unfurls?: RoomUnfurl[] | undefined;
|
|
261
280
|
roomRole?: RoomRole | null | undefined;
|
|
262
281
|
metadata?: (Record<string, unknown> & Partial<RoomForwardMetadata> & Partial<RoomWebhookMetadata>) | undefined;
|
|
263
282
|
}
|
|
@@ -313,6 +332,49 @@ export interface RoomAttachment {
|
|
|
313
332
|
*/
|
|
314
333
|
export type RoomAttachmentInput = RoomAttachment & { key: string };
|
|
315
334
|
|
|
335
|
+
export interface RoomPollOption {
|
|
336
|
+
id: string;
|
|
337
|
+
text: string;
|
|
338
|
+
/** Vote count for this option. Counts only, never voter identities
|
|
339
|
+
* (voters are fetched on demand, exactly like reactors). */
|
|
340
|
+
count: number;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Counts-only poll summary denormalized onto the carrying message (a poll
|
|
345
|
+
* IS a message). Vote/close/edit updates ride the existing ENTRY_UPDATED
|
|
346
|
+
* frame with the refreshed summary; the server's side collections stay the
|
|
347
|
+
* authoritative copy.
|
|
348
|
+
*/
|
|
349
|
+
export interface RoomPollSummary {
|
|
350
|
+
question: string;
|
|
351
|
+
options: RoomPollOption[];
|
|
352
|
+
multi: boolean;
|
|
353
|
+
/** ISO date when the poll auto-closes; null or absent means open-ended. */
|
|
354
|
+
closesAt?: string | null | undefined;
|
|
355
|
+
closed: boolean;
|
|
356
|
+
/** Store-serialized poll ordering revision, strictly increasing per
|
|
357
|
+
* poll: consumers reject frames below their known revision so
|
|
358
|
+
* bus-reordered vote/close updates cannot regress tallies. Present
|
|
359
|
+
* where the relay forwards it; missing on older relays. */
|
|
360
|
+
revision?: number | undefined;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Server-rendered link unfurl card on an entry. `url` is the canonical
|
|
365
|
+
* URL; `thumbnailKey` is the re-hosted copy in the attachments bucket,
|
|
366
|
+
* signed through the member-scoped attachment path. The origin's image URL
|
|
367
|
+
* never crosses the wire. Cards appear via ENTRY_UPDATED when the unfurl
|
|
368
|
+
* worker lands.
|
|
369
|
+
*/
|
|
370
|
+
export interface RoomUnfurl {
|
|
371
|
+
url: string;
|
|
372
|
+
title?: string | undefined;
|
|
373
|
+
description?: string | undefined;
|
|
374
|
+
siteName?: string | undefined;
|
|
375
|
+
thumbnailKey?: string | undefined;
|
|
376
|
+
}
|
|
377
|
+
|
|
316
378
|
/**
|
|
317
379
|
* A registry-backed topic inside a channel. The id is permanent: rename
|
|
318
380
|
* changes `name` only, and links (`om://topic/<topicId>`) keep resolving.
|
|
@@ -383,6 +445,21 @@ export interface RoomReadStatePayload {
|
|
|
383
445
|
* Present when the store surfaces it and on ROOM_SET_ATTENTION acks
|
|
384
446
|
* (which reply READ_STATE scoped to the one room, readState empty). */
|
|
385
447
|
attention?: Record<string, RoomTopicAttention> | undefined;
|
|
448
|
+
/** Per-room timed-mute expiry (ISO date), keyed like attention. Rides
|
|
449
|
+
* beside a 'mute' attention entry when the mute carries an expiry; null
|
|
450
|
+
* marks an explicitly cleared or permanent mute. A lapsed expiry never
|
|
451
|
+
* appears: the server resolves it at read time and the attention value
|
|
452
|
+
* reads as its base ('default'). Missing on servers older than
|
|
453
|
+
* mute-with-duration. */
|
|
454
|
+
muteUntil?: Record<string, string | null> | undefined;
|
|
455
|
+
/** Per-room attention commit revision, keyed like attention: a
|
|
456
|
+
* database-serialized, strictly increasing counter per (user, room),
|
|
457
|
+
* present only for rooms whose attention has ever been committed.
|
|
458
|
+
* ORDERING CONTRACT: a consumer that has synced revision R for a room
|
|
459
|
+
* must ignore snapshot attention entries whose revision is <= R (a
|
|
460
|
+
* snapshot read before a newer commit can arrive after that commit's
|
|
461
|
+
* sync frame). Missing on stores older than attention revisions. */
|
|
462
|
+
attentionRevision?: Record<string, number> | undefined;
|
|
386
463
|
/** Unread mention counts by room, server-computed from the notify
|
|
387
464
|
* worker's ROOM_MENTION rows so badges survive reload and clear through
|
|
388
465
|
* the ack path. Unlike readState/attention the map is SPARSE AND
|
|
@@ -995,6 +1072,14 @@ export interface RoomTopicBucketState {
|
|
|
995
1072
|
unreadCount: number;
|
|
996
1073
|
cursor: number;
|
|
997
1074
|
attention: RoomTopicAttention;
|
|
1075
|
+
/** Standing timed-mute deadline (ISO date; null = permanent mute).
|
|
1076
|
+
* Present only on the room-wide '' bucket and only while its attention
|
|
1077
|
+
* is an active 'mute'; never on topic buckets. Same semantics as the
|
|
1078
|
+
* read-state muteUntil: the store expires timed mutes lazily, so
|
|
1079
|
+
* consumers resolve an elapsed deadline back to the base attention
|
|
1080
|
+
* ('default') at read time (see resolveTopicAttention). Missing on
|
|
1081
|
+
* servers older than mute-with-duration. */
|
|
1082
|
+
muteUntil?: string | null | undefined;
|
|
998
1083
|
}
|
|
999
1084
|
|
|
1000
1085
|
export interface RoomTopicResultPayload {
|