@openmarket/rooms-client 0.7.1 → 0.9.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/README.md +20 -1
- package/dist/agent/library-model.d.ts +14 -1
- package/dist/agent/library-model.js +26 -5
- package/dist/client.d.ts +1 -0
- package/dist/client.js +6 -0
- package/dist/shared/rooms-protocol.d.ts +41 -5
- package/dist/shared/rooms-protocol.js +3 -0
- package/dist/social-client.d.ts +4 -2
- package/dist/social-types.d.ts +2 -0
- package/dist/types.d.ts +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/package.json +1 -1
- package/src/agent/armed-mode.ts +1 -3
- package/src/agent/library-model.ts +25 -5
- package/src/client.ts +7 -0
- package/src/shared/rooms-protocol.ts +53 -7
- package/src/social-client.ts +4 -2
- package/src/social-types.ts +2 -0
- package/src/types.ts +1 -0
- package/src/version.ts +2 -2
package/README.md
CHANGED
|
@@ -22,4 +22,23 @@ Bun consumers resolve raw TypeScript via the `bun` export condition; Node and bu
|
|
|
22
22
|
|
|
23
23
|
## Versioning
|
|
24
24
|
|
|
25
|
-
The npm version is
|
|
25
|
+
The npm version is released separately from the OpenMarket application. The
|
|
26
|
+
monorepo release script deliberately skips this package's `package.json`;
|
|
27
|
+
`src/version.ts` is the exception because it carries the wire/runner version.
|
|
28
|
+
|
|
29
|
+
Prepare a version commit with the guarded package gates:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
bun run rooms-client:release prepare 0.8.0
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
After that version commit is merged, publish only from a clean, current
|
|
36
|
+
`main` checkout:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
bun run rooms-client:release publish 0.8.0
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The publish command verifies npm identity and version availability, reruns
|
|
43
|
+
the package gates, publishes, and prints the update commands for
|
|
44
|
+
`openmarket-chat` and `openmarket-chat-cloud`. It never commits or pushes.
|
|
@@ -24,7 +24,9 @@ export declare class LibraryModel {
|
|
|
24
24
|
refresh(): Promise<boolean>;
|
|
25
25
|
private refreshSpace;
|
|
26
26
|
private applyChanges;
|
|
27
|
-
/** Upsert after our own write so the UI reflects it without waiting a poll.
|
|
27
|
+
/** Upsert after our own write so the UI reflects it without waiting a poll.
|
|
28
|
+
* Every local write (archive included) advances the receipt: our write IS
|
|
29
|
+
* the head we would otherwise refetch. */
|
|
28
30
|
applyLocalWrite(doc: RoomDoc): void;
|
|
29
31
|
docsFor(spaceId: string): RoomDoc[];
|
|
30
32
|
recentFor(spaceId: string, limit?: number): RoomDoc[];
|
|
@@ -34,6 +36,17 @@ export declare class LibraryModel {
|
|
|
34
36
|
freshCount(spaceId: string): number;
|
|
35
37
|
isFresh(spaceId: string, docId: string): boolean;
|
|
36
38
|
markSeen(spaceId: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Record a server poke (DOC_CHANGED) that the space's change head moved.
|
|
41
|
+
* Bookkeeping only: latestSeq ratchets up (never down) so atHead() reports
|
|
42
|
+
* stale until a refresh catches up. Returns whether a refresh would fetch
|
|
43
|
+
* anything: false for untracked spaces, true for a seqless poke (an
|
|
44
|
+
* unconditional "something changed"), otherwise whether the poked head is
|
|
45
|
+
* ahead of the cursor. Callers own debounce/refresh scheduling, and must
|
|
46
|
+
* re-check atHead() after a refresh completes: the refresh() single-flight
|
|
47
|
+
* guard drops a poke that lands mid-refresh.
|
|
48
|
+
*/
|
|
49
|
+
noteRemoteHead(spaceId: string, latestSeq?: number): boolean;
|
|
37
50
|
/** True when the cache has provably caught up to the space's change head. */
|
|
38
51
|
atHead(spaceId: string): boolean;
|
|
39
52
|
syncedOnce(spaceId: string): boolean;
|
|
@@ -95,21 +95,23 @@ export class LibraryModel {
|
|
|
95
95
|
}
|
|
96
96
|
return changed;
|
|
97
97
|
}
|
|
98
|
-
/** Upsert after our own write so the UI reflects it without waiting a poll.
|
|
98
|
+
/** Upsert after our own write so the UI reflects it without waiting a poll.
|
|
99
|
+
* Every local write (archive included) advances the receipt: our write IS
|
|
100
|
+
* the head we would otherwise refetch. */
|
|
99
101
|
applyLocalWrite(doc) {
|
|
100
102
|
const state = this.spaces.get(doc.spaceId);
|
|
101
103
|
if (!state)
|
|
102
104
|
return;
|
|
105
|
+
if (doc.spaceDocSeq > state.cursor) {
|
|
106
|
+
state.cursor = doc.spaceDocSeq;
|
|
107
|
+
state.latestSeq = Math.max(state.latestSeq, doc.spaceDocSeq);
|
|
108
|
+
}
|
|
103
109
|
if (doc.status === "archived") {
|
|
104
110
|
state.docs.delete(doc.docId);
|
|
105
111
|
state.freshDocIds.delete(doc.docId);
|
|
106
112
|
return;
|
|
107
113
|
}
|
|
108
114
|
state.docs.set(doc.docId, doc);
|
|
109
|
-
if (doc.spaceDocSeq > state.cursor) {
|
|
110
|
-
state.cursor = doc.spaceDocSeq;
|
|
111
|
-
state.latestSeq = Math.max(state.latestSeq, doc.spaceDocSeq);
|
|
112
|
-
}
|
|
113
115
|
}
|
|
114
116
|
docsFor(spaceId) {
|
|
115
117
|
const state = this.spaces.get(spaceId);
|
|
@@ -157,6 +159,25 @@ export class LibraryModel {
|
|
|
157
159
|
markSeen(spaceId) {
|
|
158
160
|
this.spaces.get(spaceId)?.freshDocIds.clear();
|
|
159
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Record a server poke (DOC_CHANGED) that the space's change head moved.
|
|
164
|
+
* Bookkeeping only: latestSeq ratchets up (never down) so atHead() reports
|
|
165
|
+
* stale until a refresh catches up. Returns whether a refresh would fetch
|
|
166
|
+
* anything: false for untracked spaces, true for a seqless poke (an
|
|
167
|
+
* unconditional "something changed"), otherwise whether the poked head is
|
|
168
|
+
* ahead of the cursor. Callers own debounce/refresh scheduling, and must
|
|
169
|
+
* re-check atHead() after a refresh completes: the refresh() single-flight
|
|
170
|
+
* guard drops a poke that lands mid-refresh.
|
|
171
|
+
*/
|
|
172
|
+
noteRemoteHead(spaceId, latestSeq) {
|
|
173
|
+
const state = this.spaces.get(spaceId);
|
|
174
|
+
if (!state)
|
|
175
|
+
return false;
|
|
176
|
+
if (latestSeq === undefined)
|
|
177
|
+
return true;
|
|
178
|
+
state.latestSeq = Math.max(state.latestSeq, latestSeq);
|
|
179
|
+
return latestSeq > state.cursor;
|
|
180
|
+
}
|
|
160
181
|
/** True when the cache has provably caught up to the space's change head. */
|
|
161
182
|
atHead(spaceId) {
|
|
162
183
|
const state = this.spaces.get(spaceId);
|
package/dist/client.d.ts
CHANGED
|
@@ -212,6 +212,7 @@ export interface RoomDmSendRequest {
|
|
|
212
212
|
* URL. Stored on the message's imageUrl; the client signs keys on render.
|
|
213
213
|
* Additive — omitted for text-only sends. */
|
|
214
214
|
imageUrl?: string;
|
|
215
|
+
attachments?: RoomAttachmentInput[];
|
|
215
216
|
metadata?: RoomForwardMetadata;
|
|
216
217
|
}
|
|
217
218
|
export interface RoomDmReplyRequest extends RoomDmSendRequest {
|
package/dist/client.js
CHANGED
|
@@ -489,6 +489,9 @@ export async function sendRoomDmOnClient(client, request, timeoutMs) {
|
|
|
489
489
|
...(request.username ? { username: request.username } : {}),
|
|
490
490
|
...(request.conversationId ? { conversationId: request.conversationId } : {}),
|
|
491
491
|
...(request.imageUrl ? { imageUrl: request.imageUrl } : {}),
|
|
492
|
+
...(request.attachments?.length
|
|
493
|
+
? { attachments: toRoomAttachmentPayloads(request.attachments) }
|
|
494
|
+
: {}),
|
|
492
495
|
...(request.metadata ? { metadata: request.metadata } : {}),
|
|
493
496
|
},
|
|
494
497
|
timestamp: Date.now(),
|
|
@@ -511,6 +514,9 @@ export async function replyRoomDmOnClient(client, request, timeoutMs) {
|
|
|
511
514
|
...(request.username ? { username: request.username } : {}),
|
|
512
515
|
...(request.conversationId ? { conversationId: request.conversationId } : {}),
|
|
513
516
|
...(request.imageUrl ? { imageUrl: request.imageUrl } : {}),
|
|
517
|
+
...(request.attachments?.length
|
|
518
|
+
? { attachments: toRoomAttachmentPayloads(request.attachments) }
|
|
519
|
+
: {}),
|
|
514
520
|
...(request.metadata ? { metadata: request.metadata } : {}),
|
|
515
521
|
},
|
|
516
522
|
timestamp: Date.now(),
|
|
@@ -98,11 +98,14 @@ export declare const RoomServerMessageType: {
|
|
|
98
98
|
readonly DM_MESSAGE_EDITED: "DM_MESSAGE_EDITED";
|
|
99
99
|
readonly DM_MESSAGE_DELETED: "DM_MESSAGE_DELETED";
|
|
100
100
|
readonly DM_REACTION_UPDATED: "DM_REACTION_UPDATED";
|
|
101
|
+
readonly DM_ATTACHMENT_UPDATED: "DM_ATTACHMENT_UPDATED";
|
|
101
102
|
readonly DM_PIN_UPDATED: "DM_PIN_UPDATED";
|
|
102
103
|
readonly DM_PINS: "DM_PINS";
|
|
103
104
|
readonly TOPIC: "TOPIC";
|
|
104
105
|
readonly TOPICS: "TOPICS";
|
|
105
106
|
readonly TOPIC_UPDATED: "TOPIC_UPDATED";
|
|
107
|
+
readonly DOC_CHANGED: "DOC_CHANGED";
|
|
108
|
+
readonly FRIEND_UPDATED: "FRIEND_UPDATED";
|
|
106
109
|
};
|
|
107
110
|
export declare function addBounded<T>(set: Set<T>, value: T, max: number): void;
|
|
108
111
|
export type RoomServerMessageType = (typeof RoomServerMessageType)[keyof typeof RoomServerMessageType];
|
|
@@ -278,10 +281,11 @@ export interface RoomWebhookMetadata {
|
|
|
278
281
|
}
|
|
279
282
|
export interface RoomAttachment {
|
|
280
283
|
/**
|
|
281
|
-
* Object key under room-attachments/; clients sign it
|
|
282
|
-
* Absent while scanStatus is "pending" on entries served
|
|
283
|
-
* store withholds the signable key until the scan
|
|
284
|
-
* metadata as a placeholder and do not attempt
|
|
284
|
+
* Object key under room-attachments/ or dm-attachments/; clients sign it
|
|
285
|
+
* at render time. Absent while scanStatus is "pending" on entries served
|
|
286
|
+
* to recipients: the store withholds the signable key until the scan
|
|
287
|
+
* clears, so render the metadata as a placeholder and do not attempt
|
|
288
|
+
* signing without a key.
|
|
285
289
|
*/
|
|
286
290
|
key?: string | undefined;
|
|
287
291
|
name: string;
|
|
@@ -292,7 +296,7 @@ export interface RoomAttachment {
|
|
|
292
296
|
/**
|
|
293
297
|
* A postable attachment reference. Outbound frames must always carry the
|
|
294
298
|
* stored key (senders reference their own uploads, which include it); only
|
|
295
|
-
*
|
|
299
|
+
* recipient views of pending entries may be keyless, and those must never be
|
|
296
300
|
* forwarded back into a post.
|
|
297
301
|
*/
|
|
298
302
|
export type RoomAttachmentInput = RoomAttachment & {
|
|
@@ -619,6 +623,7 @@ export interface RoomDmSendPayload {
|
|
|
619
623
|
/** DM-attachment key or legacy public image URL; stored on the message's
|
|
620
624
|
* imageUrl. Additive — text-only sends omit it. */
|
|
621
625
|
imageUrl?: string;
|
|
626
|
+
attachments?: RoomAttachmentInput[];
|
|
622
627
|
metadata?: RoomForwardMetadata | undefined;
|
|
623
628
|
}
|
|
624
629
|
/** Client → server: send a DM whose parent is another message in the same conversation. */
|
|
@@ -677,6 +682,12 @@ export interface RoomDmReactionUpdatedPayload {
|
|
|
677
682
|
}>;
|
|
678
683
|
participant: RoomDmParticipant;
|
|
679
684
|
}
|
|
685
|
+
export interface RoomDmAttachmentUpdatedPayload {
|
|
686
|
+
conversationId: string;
|
|
687
|
+
messageId: string;
|
|
688
|
+
scanStatus: "clean" | "held";
|
|
689
|
+
attachments: RoomAttachment[];
|
|
690
|
+
}
|
|
680
691
|
export interface RoomDmPinPayload {
|
|
681
692
|
requestId?: string;
|
|
682
693
|
conversationId?: string;
|
|
@@ -966,6 +977,19 @@ export interface RoomTopicUpdatedPayload {
|
|
|
966
977
|
room: string;
|
|
967
978
|
topic: RoomTopic;
|
|
968
979
|
}
|
|
980
|
+
/** Poke: a space's doc-library change head moved (create/edit/archive/restore).
|
|
981
|
+
* Deliberately content-free: clients refetch deltas over REST. */
|
|
982
|
+
export interface RoomDocChangedPayload {
|
|
983
|
+
spaceId: string;
|
|
984
|
+
/** The space's change head after the mutation, when the server knows it
|
|
985
|
+
* cheaply; absent still means "something changed, refetch". */
|
|
986
|
+
latestSeq?: number | undefined;
|
|
987
|
+
}
|
|
988
|
+
/** Poke: the recipient's friendship graph changed. Clients refetch over REST. */
|
|
989
|
+
export interface RoomFriendUpdatedPayload {
|
|
990
|
+
kind: "request" | "accepted" | "rejected" | "removed";
|
|
991
|
+
friendshipId?: string | undefined;
|
|
992
|
+
}
|
|
969
993
|
export type RoomClientMessage = {
|
|
970
994
|
type: typeof RoomClientMessageType.AUTHENTICATE;
|
|
971
995
|
payload: {
|
|
@@ -1471,6 +1495,10 @@ export type RoomServerMessage = {
|
|
|
1471
1495
|
type: typeof RoomServerMessageType.DM_REACTION_UPDATED;
|
|
1472
1496
|
payload: RoomDmReactionUpdatedPayload;
|
|
1473
1497
|
timestamp: number;
|
|
1498
|
+
} | {
|
|
1499
|
+
type: typeof RoomServerMessageType.DM_ATTACHMENT_UPDATED;
|
|
1500
|
+
payload: RoomDmAttachmentUpdatedPayload;
|
|
1501
|
+
timestamp: number;
|
|
1474
1502
|
} | {
|
|
1475
1503
|
type: typeof RoomServerMessageType.DM_PIN_UPDATED;
|
|
1476
1504
|
payload: RoomDmPinUpdatedPayload;
|
|
@@ -1491,6 +1519,14 @@ export type RoomServerMessage = {
|
|
|
1491
1519
|
type: typeof RoomServerMessageType.TOPIC_UPDATED;
|
|
1492
1520
|
payload: RoomTopicUpdatedPayload;
|
|
1493
1521
|
timestamp: number;
|
|
1522
|
+
} | {
|
|
1523
|
+
type: typeof RoomServerMessageType.DOC_CHANGED;
|
|
1524
|
+
payload: RoomDocChangedPayload;
|
|
1525
|
+
timestamp: number;
|
|
1526
|
+
} | {
|
|
1527
|
+
type: typeof RoomServerMessageType.FRIEND_UPDATED;
|
|
1528
|
+
payload: RoomFriendUpdatedPayload;
|
|
1529
|
+
timestamp: number;
|
|
1494
1530
|
};
|
|
1495
1531
|
export type RoomServerMessageOf<T extends RoomServerMessageType> = Extract<RoomServerMessage, {
|
|
1496
1532
|
type: T;
|
|
@@ -97,11 +97,14 @@ export const RoomServerMessageType = {
|
|
|
97
97
|
DM_MESSAGE_EDITED: "DM_MESSAGE_EDITED",
|
|
98
98
|
DM_MESSAGE_DELETED: "DM_MESSAGE_DELETED",
|
|
99
99
|
DM_REACTION_UPDATED: "DM_REACTION_UPDATED",
|
|
100
|
+
DM_ATTACHMENT_UPDATED: "DM_ATTACHMENT_UPDATED",
|
|
100
101
|
DM_PIN_UPDATED: "DM_PIN_UPDATED",
|
|
101
102
|
DM_PINS: "DM_PINS",
|
|
102
103
|
TOPIC: "TOPIC",
|
|
103
104
|
TOPICS: "TOPICS",
|
|
104
105
|
TOPIC_UPDATED: "TOPIC_UPDATED",
|
|
106
|
+
DOC_CHANGED: "DOC_CHANGED",
|
|
107
|
+
FRIEND_UPDATED: "FRIEND_UPDATED",
|
|
105
108
|
};
|
|
106
109
|
export function addBounded(set, value, max) {
|
|
107
110
|
set.add(value);
|
package/dist/social-client.d.ts
CHANGED
|
@@ -604,8 +604,10 @@ export declare function socialRequest<T>(config: RoomSocialConfig, path: string,
|
|
|
604
604
|
formData?: FormData;
|
|
605
605
|
headers?: Record<string, string>;
|
|
606
606
|
/** Fetch cache mode; token-bearing reads pass "no-store" so posting
|
|
607
|
-
* credentials never persist in a browser HTTP cache.
|
|
608
|
-
|
|
607
|
+
* credentials never persist in a browser HTTP cache. Spelled as the
|
|
608
|
+
* literal union rather than DOM's RequestCache: this package
|
|
609
|
+
* typechecks without the DOM lib. */
|
|
610
|
+
cache?: "default" | "force-cache" | "no-cache" | "no-store" | "only-if-cached" | "reload";
|
|
609
611
|
}): Promise<T>;
|
|
610
612
|
export declare function cleanUsername(username: string): string;
|
|
611
613
|
export declare function cleanRoomId(room: string): string;
|
package/dist/social-types.d.ts
CHANGED
|
@@ -125,6 +125,8 @@ export interface RoomDmMessage {
|
|
|
125
125
|
/** DM image: a private dm-attachments/ key (signed on render) or a legacy
|
|
126
126
|
* public URL. Additive — old messages carry none. */
|
|
127
127
|
imageUrl?: string | null;
|
|
128
|
+
/** Private scanned attachments. Keys are absent until the bytes are clean. */
|
|
129
|
+
attachments?: RoomAttachment[];
|
|
128
130
|
/** Aggregated reactions (per-emoji userId lists). Additive. */
|
|
129
131
|
reactions?: Array<{
|
|
130
132
|
emoji: string;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { RoomAccessMode, RoomAttachment, RoomAttachmentInput, RoomAuditEntry, RoomAuditLogPayload, RoomAuditLogRequestPayload, RoomAuthSuccessPayload, RoomBackfillPayload, RoomClientMessage, RoomClientPlatform, RoomCreatedPayload, RoomCreatePayload, RoomDmHistoryPayload, RoomDmHistoryResultPayload, RoomDmInboxUpdatedPayload, RoomDmMessage, RoomDmMessageDeletedPayload, RoomDmMessageEditedPayload, RoomDmMessagePayload, RoomDmOpenedPayload, RoomDmOpenPayload, RoomDmParticipant, RoomDmPinsResultPayload, RoomDmPinUpdatedPayload, RoomDmReactionUpdatedPayload, RoomDmReadPayload, RoomDmReplyPayload, RoomDmSendPayload, RoomEntryUpdatedPayload, RoomErrorPayload, RoomForwardedFrom, RoomForwardMetadata, RoomFriendPresence, RoomInvitedPayload, RoomInvitePayload, RoomLedgerEntry, RoomMember, RoomMemberRemovedPayload, RoomMemberRoleChangedPayload, RoomMemberTier, RoomMessagePayload, RoomMeta, RoomModerateMemberPayload, RoomMutedPayload, RoomPinsPayload, RoomPollOption, RoomPollSummary, RoomPresenceDeltaPayload, RoomPresencePayload, RoomReactorsPayload, RoomReadStatePayload, RoomRemoveMemberPayload, RoomReportedPayload, RoomReportPayload, RoomResyncPayload, RoomRole, RoomRoleChangePayload, RoomSearchPayload, RoomSearchResultsPayload, RoomSearchScope, RoomServerMessage, RoomServerMessageOf, RoomSetAttentionPayload, RoomSetReadOnlyPayload, RoomSpace, RoomSpaceJoinedPayload, RoomSpacesPayload, RoomThreadPayload, RoomTopic, RoomTopicAttention, RoomTopicBucketState, RoomTopicCreatePayload, RoomTopicListPayload, RoomTopicMergePayload, RoomTopicMovePayload, RoomTopicRenamePayload, RoomTopicResolvePayload, RoomTopicResultPayload, RoomTopicSetAttentionPayload, RoomTopicsPolicy, RoomTopicsResultPayload, RoomTopicUpdatedPayload, RoomTypingPayload, RoomUnfurl, RoomUserStatus, RoomWhoPayload, } from "./shared/rooms-protocol.js";
|
|
1
|
+
export type { RoomAccessMode, RoomAttachment, RoomAttachmentInput, RoomAuditEntry, RoomAuditLogPayload, RoomAuditLogRequestPayload, RoomAuthSuccessPayload, RoomBackfillPayload, RoomClientMessage, RoomClientPlatform, RoomCreatedPayload, RoomCreatePayload, RoomDmAttachmentUpdatedPayload, RoomDmHistoryPayload, RoomDmHistoryResultPayload, RoomDmInboxUpdatedPayload, RoomDmMessage, RoomDmMessageDeletedPayload, RoomDmMessageEditedPayload, RoomDmMessagePayload, RoomDmOpenedPayload, RoomDmOpenPayload, RoomDmParticipant, RoomDmPinsResultPayload, RoomDmPinUpdatedPayload, RoomDmReactionUpdatedPayload, RoomDmReadPayload, RoomDmReplyPayload, RoomDmSendPayload, RoomEntryUpdatedPayload, RoomErrorPayload, RoomForwardedFrom, RoomForwardMetadata, RoomFriendPresence, RoomInvitedPayload, RoomInvitePayload, RoomLedgerEntry, RoomMember, RoomMemberRemovedPayload, RoomMemberRoleChangedPayload, RoomMemberTier, RoomMessagePayload, RoomMeta, RoomModerateMemberPayload, RoomMutedPayload, RoomPinsPayload, RoomPollOption, RoomPollSummary, RoomPresenceDeltaPayload, RoomPresencePayload, RoomReactorsPayload, RoomReadStatePayload, RoomRemoveMemberPayload, RoomReportedPayload, RoomReportPayload, RoomResyncPayload, RoomRole, RoomRoleChangePayload, RoomSearchPayload, RoomSearchResultsPayload, RoomSearchScope, RoomServerMessage, RoomServerMessageOf, RoomSetAttentionPayload, RoomSetReadOnlyPayload, RoomSpace, RoomSpaceJoinedPayload, RoomSpacesPayload, RoomThreadPayload, RoomTopic, RoomTopicAttention, RoomTopicBucketState, RoomTopicCreatePayload, RoomTopicListPayload, RoomTopicMergePayload, RoomTopicMovePayload, RoomTopicRenamePayload, RoomTopicResolvePayload, RoomTopicResultPayload, RoomTopicSetAttentionPayload, RoomTopicsPolicy, RoomTopicsResultPayload, RoomTopicUpdatedPayload, RoomTypingPayload, RoomUnfurl, RoomUserStatus, RoomWhoPayload, } from "./shared/rooms-protocol.js";
|
|
2
2
|
export { applyRoomPresenceDelta, isSystemRoomLedgerEntryType, ROOM_PRESENCE_SAMPLE_LIMIT, RoomClientMessageType, RoomLedgerEntryType, RoomServerMessageType, SUPPORTED_ROOM_PROTOCOL_VERSION, } from "./shared/rooms-protocol.js";
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
2
|
-
export declare const RUNNER_VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.9.0";
|
|
2
|
+
export declare const RUNNER_VERSION = "0.9.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
2
|
-
export const RUNNER_VERSION = "0.
|
|
1
|
+
export const VERSION = "0.9.0";
|
|
2
|
+
export const RUNNER_VERSION = "0.9.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmarket/rooms-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "OM Rooms protocol client: wire types, WebSocket + REST clients, and chat view-models. Browser-safe (no node:/bun: imports).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
package/src/agent/armed-mode.ts
CHANGED
|
@@ -170,9 +170,7 @@ export function ownSeqsOf(entries: ReadonlyArray<RoomLedgerEntry>, ownUserId: st
|
|
|
170
170
|
* autonomous turn, and webhook chatter cannot satisfy the
|
|
171
171
|
* human-activity brake. */
|
|
172
172
|
function isWebhookEntry(entry: RoomLedgerEntry): boolean {
|
|
173
|
-
return Boolean(
|
|
174
|
-
(entry.metadata as { webhook?: unknown } | undefined)?.webhook,
|
|
175
|
-
);
|
|
173
|
+
return Boolean((entry.metadata as { webhook?: unknown } | undefined)?.webhook);
|
|
176
174
|
}
|
|
177
175
|
|
|
178
176
|
export function isTriggeringEntry(
|
|
@@ -129,20 +129,22 @@ export class LibraryModel {
|
|
|
129
129
|
return changed;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
/** Upsert after our own write so the UI reflects it without waiting a poll.
|
|
132
|
+
/** Upsert after our own write so the UI reflects it without waiting a poll.
|
|
133
|
+
* Every local write (archive included) advances the receipt: our write IS
|
|
134
|
+
* the head we would otherwise refetch. */
|
|
133
135
|
applyLocalWrite(doc: RoomDoc): void {
|
|
134
136
|
const state = this.spaces.get(doc.spaceId);
|
|
135
137
|
if (!state) return;
|
|
138
|
+
if (doc.spaceDocSeq > state.cursor) {
|
|
139
|
+
state.cursor = doc.spaceDocSeq;
|
|
140
|
+
state.latestSeq = Math.max(state.latestSeq, doc.spaceDocSeq);
|
|
141
|
+
}
|
|
136
142
|
if (doc.status === "archived") {
|
|
137
143
|
state.docs.delete(doc.docId);
|
|
138
144
|
state.freshDocIds.delete(doc.docId);
|
|
139
145
|
return;
|
|
140
146
|
}
|
|
141
147
|
state.docs.set(doc.docId, doc);
|
|
142
|
-
if (doc.spaceDocSeq > state.cursor) {
|
|
143
|
-
state.cursor = doc.spaceDocSeq;
|
|
144
|
-
state.latestSeq = Math.max(state.latestSeq, doc.spaceDocSeq);
|
|
145
|
-
}
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
docsFor(spaceId: string): RoomDoc[] {
|
|
@@ -193,6 +195,24 @@ export class LibraryModel {
|
|
|
193
195
|
this.spaces.get(spaceId)?.freshDocIds.clear();
|
|
194
196
|
}
|
|
195
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Record a server poke (DOC_CHANGED) that the space's change head moved.
|
|
200
|
+
* Bookkeeping only: latestSeq ratchets up (never down) so atHead() reports
|
|
201
|
+
* stale until a refresh catches up. Returns whether a refresh would fetch
|
|
202
|
+
* anything: false for untracked spaces, true for a seqless poke (an
|
|
203
|
+
* unconditional "something changed"), otherwise whether the poked head is
|
|
204
|
+
* ahead of the cursor. Callers own debounce/refresh scheduling, and must
|
|
205
|
+
* re-check atHead() after a refresh completes: the refresh() single-flight
|
|
206
|
+
* guard drops a poke that lands mid-refresh.
|
|
207
|
+
*/
|
|
208
|
+
noteRemoteHead(spaceId: string, latestSeq?: number): boolean {
|
|
209
|
+
const state = this.spaces.get(spaceId);
|
|
210
|
+
if (!state) return false;
|
|
211
|
+
if (latestSeq === undefined) return true;
|
|
212
|
+
state.latestSeq = Math.max(state.latestSeq, latestSeq);
|
|
213
|
+
return latestSeq > state.cursor;
|
|
214
|
+
}
|
|
215
|
+
|
|
196
216
|
/** True when the cache has provably caught up to the space's change head. */
|
|
197
217
|
atHead(spaceId: string): boolean {
|
|
198
218
|
const state = this.spaces.get(spaceId);
|
package/src/client.ts
CHANGED
|
@@ -297,6 +297,7 @@ export interface RoomDmSendRequest {
|
|
|
297
297
|
* URL. Stored on the message's imageUrl; the client signs keys on render.
|
|
298
298
|
* Additive — omitted for text-only sends. */
|
|
299
299
|
imageUrl?: string;
|
|
300
|
+
attachments?: RoomAttachmentInput[];
|
|
300
301
|
metadata?: RoomForwardMetadata;
|
|
301
302
|
}
|
|
302
303
|
|
|
@@ -994,6 +995,9 @@ export async function sendRoomDmOnClient(
|
|
|
994
995
|
...(request.username ? { username: request.username } : {}),
|
|
995
996
|
...(request.conversationId ? { conversationId: request.conversationId } : {}),
|
|
996
997
|
...(request.imageUrl ? { imageUrl: request.imageUrl } : {}),
|
|
998
|
+
...(request.attachments?.length
|
|
999
|
+
? { attachments: toRoomAttachmentPayloads(request.attachments) }
|
|
1000
|
+
: {}),
|
|
997
1001
|
...(request.metadata ? { metadata: request.metadata } : {}),
|
|
998
1002
|
},
|
|
999
1003
|
timestamp: Date.now(),
|
|
@@ -1027,6 +1031,9 @@ export async function replyRoomDmOnClient(
|
|
|
1027
1031
|
...(request.username ? { username: request.username } : {}),
|
|
1028
1032
|
...(request.conversationId ? { conversationId: request.conversationId } : {}),
|
|
1029
1033
|
...(request.imageUrl ? { imageUrl: request.imageUrl } : {}),
|
|
1034
|
+
...(request.attachments?.length
|
|
1035
|
+
? { attachments: toRoomAttachmentPayloads(request.attachments) }
|
|
1036
|
+
: {}),
|
|
1030
1037
|
...(request.metadata ? { metadata: request.metadata } : {}),
|
|
1031
1038
|
},
|
|
1032
1039
|
timestamp: Date.now(),
|
|
@@ -101,11 +101,14 @@ export const RoomServerMessageType = {
|
|
|
101
101
|
DM_MESSAGE_EDITED: "DM_MESSAGE_EDITED",
|
|
102
102
|
DM_MESSAGE_DELETED: "DM_MESSAGE_DELETED",
|
|
103
103
|
DM_REACTION_UPDATED: "DM_REACTION_UPDATED",
|
|
104
|
+
DM_ATTACHMENT_UPDATED: "DM_ATTACHMENT_UPDATED",
|
|
104
105
|
DM_PIN_UPDATED: "DM_PIN_UPDATED",
|
|
105
106
|
DM_PINS: "DM_PINS",
|
|
106
107
|
TOPIC: "TOPIC",
|
|
107
108
|
TOPICS: "TOPICS",
|
|
108
109
|
TOPIC_UPDATED: "TOPIC_UPDATED",
|
|
110
|
+
DOC_CHANGED: "DOC_CHANGED",
|
|
111
|
+
FRIEND_UPDATED: "FRIEND_UPDATED",
|
|
109
112
|
} as const;
|
|
110
113
|
|
|
111
114
|
export function addBounded<T>(set: Set<T>, value: T, max: number): void {
|
|
@@ -278,7 +281,9 @@ export interface RoomLedgerEntry {
|
|
|
278
281
|
* (and servers older than the unfurl worker) omit it. */
|
|
279
282
|
unfurls?: RoomUnfurl[] | undefined;
|
|
280
283
|
roomRole?: RoomRole | null | undefined;
|
|
281
|
-
metadata?:
|
|
284
|
+
metadata?:
|
|
285
|
+
| (Record<string, unknown> & Partial<RoomForwardMetadata> & Partial<RoomWebhookMetadata>)
|
|
286
|
+
| undefined;
|
|
282
287
|
}
|
|
283
288
|
|
|
284
289
|
export type RoomForwardedFrom =
|
|
@@ -312,10 +317,11 @@ export interface RoomWebhookMetadata {
|
|
|
312
317
|
|
|
313
318
|
export interface RoomAttachment {
|
|
314
319
|
/**
|
|
315
|
-
* Object key under room-attachments/; clients sign it
|
|
316
|
-
* Absent while scanStatus is "pending" on entries served
|
|
317
|
-
* store withholds the signable key until the scan
|
|
318
|
-
* metadata as a placeholder and do not attempt
|
|
320
|
+
* Object key under room-attachments/ or dm-attachments/; clients sign it
|
|
321
|
+
* at render time. Absent while scanStatus is "pending" on entries served
|
|
322
|
+
* to recipients: the store withholds the signable key until the scan
|
|
323
|
+
* clears, so render the metadata as a placeholder and do not attempt
|
|
324
|
+
* signing without a key.
|
|
319
325
|
*/
|
|
320
326
|
key?: string | undefined;
|
|
321
327
|
name: string;
|
|
@@ -327,7 +333,7 @@ export interface RoomAttachment {
|
|
|
327
333
|
/**
|
|
328
334
|
* A postable attachment reference. Outbound frames must always carry the
|
|
329
335
|
* stored key (senders reference their own uploads, which include it); only
|
|
330
|
-
*
|
|
336
|
+
* recipient views of pending entries may be keyless, and those must never be
|
|
331
337
|
* forwarded back into a post.
|
|
332
338
|
*/
|
|
333
339
|
export type RoomAttachmentInput = RoomAttachment & { key: string };
|
|
@@ -683,7 +689,9 @@ export interface RoomDmMessage {
|
|
|
683
689
|
createdAt?: string | number | undefined;
|
|
684
690
|
/** Sealed DM: content is '' and the opaque envelope rides here. */
|
|
685
691
|
secret?: RoomLedgerSecretPayload | null | undefined;
|
|
686
|
-
metadata?:
|
|
692
|
+
metadata?:
|
|
693
|
+
| (Record<string, unknown> & Partial<RoomForwardMetadata> & Partial<RoomWebhookMetadata>)
|
|
694
|
+
| undefined;
|
|
687
695
|
/** Legacy public chat-image URL (renders as-is, no signing). */
|
|
688
696
|
imageUrl?: string | null | undefined;
|
|
689
697
|
/** Private, signed, scanned DM attachments (dm-attachments/ keyspace).
|
|
@@ -718,6 +726,7 @@ export interface RoomDmSendPayload {
|
|
|
718
726
|
/** DM-attachment key or legacy public image URL; stored on the message's
|
|
719
727
|
* imageUrl. Additive — text-only sends omit it. */
|
|
720
728
|
imageUrl?: string;
|
|
729
|
+
attachments?: RoomAttachmentInput[];
|
|
721
730
|
metadata?: RoomForwardMetadata | undefined;
|
|
722
731
|
}
|
|
723
732
|
|
|
@@ -781,6 +790,13 @@ export interface RoomDmReactionUpdatedPayload {
|
|
|
781
790
|
participant: RoomDmParticipant;
|
|
782
791
|
}
|
|
783
792
|
|
|
793
|
+
export interface RoomDmAttachmentUpdatedPayload {
|
|
794
|
+
conversationId: string;
|
|
795
|
+
messageId: string;
|
|
796
|
+
scanStatus: "clean" | "held";
|
|
797
|
+
attachments: RoomAttachment[];
|
|
798
|
+
}
|
|
799
|
+
|
|
784
800
|
export interface RoomDmPinPayload {
|
|
785
801
|
requestId?: string;
|
|
786
802
|
conversationId?: string;
|
|
@@ -1104,6 +1120,21 @@ export interface RoomTopicUpdatedPayload {
|
|
|
1104
1120
|
topic: RoomTopic;
|
|
1105
1121
|
}
|
|
1106
1122
|
|
|
1123
|
+
/** Poke: a space's doc-library change head moved (create/edit/archive/restore).
|
|
1124
|
+
* Deliberately content-free: clients refetch deltas over REST. */
|
|
1125
|
+
export interface RoomDocChangedPayload {
|
|
1126
|
+
spaceId: string;
|
|
1127
|
+
/** The space's change head after the mutation, when the server knows it
|
|
1128
|
+
* cheaply; absent still means "something changed, refetch". */
|
|
1129
|
+
latestSeq?: number | undefined;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/** Poke: the recipient's friendship graph changed. Clients refetch over REST. */
|
|
1133
|
+
export interface RoomFriendUpdatedPayload {
|
|
1134
|
+
kind: "request" | "accepted" | "rejected" | "removed";
|
|
1135
|
+
friendshipId?: string | undefined;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1107
1138
|
export type RoomClientMessage =
|
|
1108
1139
|
| {
|
|
1109
1140
|
type: typeof RoomClientMessageType.AUTHENTICATE;
|
|
@@ -1653,6 +1684,11 @@ export type RoomServerMessage =
|
|
|
1653
1684
|
payload: RoomDmReactionUpdatedPayload;
|
|
1654
1685
|
timestamp: number;
|
|
1655
1686
|
}
|
|
1687
|
+
| {
|
|
1688
|
+
type: typeof RoomServerMessageType.DM_ATTACHMENT_UPDATED;
|
|
1689
|
+
payload: RoomDmAttachmentUpdatedPayload;
|
|
1690
|
+
timestamp: number;
|
|
1691
|
+
}
|
|
1656
1692
|
| {
|
|
1657
1693
|
type: typeof RoomServerMessageType.DM_PIN_UPDATED;
|
|
1658
1694
|
payload: RoomDmPinUpdatedPayload;
|
|
@@ -1677,6 +1713,16 @@ export type RoomServerMessage =
|
|
|
1677
1713
|
type: typeof RoomServerMessageType.TOPIC_UPDATED;
|
|
1678
1714
|
payload: RoomTopicUpdatedPayload;
|
|
1679
1715
|
timestamp: number;
|
|
1716
|
+
}
|
|
1717
|
+
| {
|
|
1718
|
+
type: typeof RoomServerMessageType.DOC_CHANGED;
|
|
1719
|
+
payload: RoomDocChangedPayload;
|
|
1720
|
+
timestamp: number;
|
|
1721
|
+
}
|
|
1722
|
+
| {
|
|
1723
|
+
type: typeof RoomServerMessageType.FRIEND_UPDATED;
|
|
1724
|
+
payload: RoomFriendUpdatedPayload;
|
|
1725
|
+
timestamp: number;
|
|
1680
1726
|
};
|
|
1681
1727
|
|
|
1682
1728
|
export type RoomServerMessageOf<T extends RoomServerMessageType> = Extract<
|
package/src/social-client.ts
CHANGED
|
@@ -1934,8 +1934,10 @@ export async function socialRequest<T>(
|
|
|
1934
1934
|
formData?: FormData;
|
|
1935
1935
|
headers?: Record<string, string>;
|
|
1936
1936
|
/** Fetch cache mode; token-bearing reads pass "no-store" so posting
|
|
1937
|
-
* credentials never persist in a browser HTTP cache.
|
|
1938
|
-
|
|
1937
|
+
* credentials never persist in a browser HTTP cache. Spelled as the
|
|
1938
|
+
* literal union rather than DOM's RequestCache: this package
|
|
1939
|
+
* typechecks without the DOM lib. */
|
|
1940
|
+
cache?: "default" | "force-cache" | "no-cache" | "no-store" | "only-if-cached" | "reload";
|
|
1939
1941
|
} = {},
|
|
1940
1942
|
): Promise<T> {
|
|
1941
1943
|
const url = `${trimTrailingSlash(config.apiUrl)}${path}`;
|
package/src/social-types.ts
CHANGED
|
@@ -147,6 +147,8 @@ export interface RoomDmMessage {
|
|
|
147
147
|
/** DM image: a private dm-attachments/ key (signed on render) or a legacy
|
|
148
148
|
* public URL. Additive — old messages carry none. */
|
|
149
149
|
imageUrl?: string | null;
|
|
150
|
+
/** Private scanned attachments. Keys are absent until the bytes are clean. */
|
|
151
|
+
attachments?: RoomAttachment[];
|
|
150
152
|
/** Aggregated reactions (per-emoji userId lists). Additive. */
|
|
151
153
|
reactions?: Array<{ emoji: string; users: string[] }>;
|
|
152
154
|
/** Shared pin state. Additive; absent means unpinned. */
|
package/src/types.ts
CHANGED
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
2
|
-
export const RUNNER_VERSION = "0.
|
|
1
|
+
export const VERSION = "0.9.0";
|
|
2
|
+
export const RUNNER_VERSION = "0.9.0";
|