@openmarket/rooms-client 0.7.0 → 0.8.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/client.d.ts +1 -0
- package/dist/client.js +6 -0
- package/dist/shared/rooms-protocol.d.ts +18 -5
- package/dist/shared/rooms-protocol.js +1 -0
- package/dist/social-client.d.ts +22 -6
- package/dist/social-client.js +10 -5
- package/dist/social-types.d.ts +2 -0
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
- package/src/agent/armed-mode.ts +1 -3
- package/src/client.ts +7 -0
- package/src/shared/rooms-protocol.ts +26 -7
- package/src/social-client.ts +30 -8
- package/src/social-types.ts +2 -0
- package/src/types.ts +1 -0
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.
|
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,6 +98,7 @@ 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";
|
|
@@ -278,10 +279,11 @@ export interface RoomWebhookMetadata {
|
|
|
278
279
|
}
|
|
279
280
|
export interface RoomAttachment {
|
|
280
281
|
/**
|
|
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
|
|
282
|
+
* Object key under room-attachments/ or dm-attachments/; clients sign it
|
|
283
|
+
* at render time. Absent while scanStatus is "pending" on entries served
|
|
284
|
+
* to recipients: the store withholds the signable key until the scan
|
|
285
|
+
* clears, so render the metadata as a placeholder and do not attempt
|
|
286
|
+
* signing without a key.
|
|
285
287
|
*/
|
|
286
288
|
key?: string | undefined;
|
|
287
289
|
name: string;
|
|
@@ -292,7 +294,7 @@ export interface RoomAttachment {
|
|
|
292
294
|
/**
|
|
293
295
|
* A postable attachment reference. Outbound frames must always carry the
|
|
294
296
|
* stored key (senders reference their own uploads, which include it); only
|
|
295
|
-
*
|
|
297
|
+
* recipient views of pending entries may be keyless, and those must never be
|
|
296
298
|
* forwarded back into a post.
|
|
297
299
|
*/
|
|
298
300
|
export type RoomAttachmentInput = RoomAttachment & {
|
|
@@ -619,6 +621,7 @@ export interface RoomDmSendPayload {
|
|
|
619
621
|
/** DM-attachment key or legacy public image URL; stored on the message's
|
|
620
622
|
* imageUrl. Additive — text-only sends omit it. */
|
|
621
623
|
imageUrl?: string;
|
|
624
|
+
attachments?: RoomAttachmentInput[];
|
|
622
625
|
metadata?: RoomForwardMetadata | undefined;
|
|
623
626
|
}
|
|
624
627
|
/** Client → server: send a DM whose parent is another message in the same conversation. */
|
|
@@ -677,6 +680,12 @@ export interface RoomDmReactionUpdatedPayload {
|
|
|
677
680
|
}>;
|
|
678
681
|
participant: RoomDmParticipant;
|
|
679
682
|
}
|
|
683
|
+
export interface RoomDmAttachmentUpdatedPayload {
|
|
684
|
+
conversationId: string;
|
|
685
|
+
messageId: string;
|
|
686
|
+
scanStatus: "clean" | "held";
|
|
687
|
+
attachments: RoomAttachment[];
|
|
688
|
+
}
|
|
680
689
|
export interface RoomDmPinPayload {
|
|
681
690
|
requestId?: string;
|
|
682
691
|
conversationId?: string;
|
|
@@ -1471,6 +1480,10 @@ export type RoomServerMessage = {
|
|
|
1471
1480
|
type: typeof RoomServerMessageType.DM_REACTION_UPDATED;
|
|
1472
1481
|
payload: RoomDmReactionUpdatedPayload;
|
|
1473
1482
|
timestamp: number;
|
|
1483
|
+
} | {
|
|
1484
|
+
type: typeof RoomServerMessageType.DM_ATTACHMENT_UPDATED;
|
|
1485
|
+
payload: RoomDmAttachmentUpdatedPayload;
|
|
1486
|
+
timestamp: number;
|
|
1474
1487
|
} | {
|
|
1475
1488
|
type: typeof RoomServerMessageType.DM_PIN_UPDATED;
|
|
1476
1489
|
payload: RoomDmPinUpdatedPayload;
|
|
@@ -97,6 +97,7 @@ 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",
|
package/dist/social-client.d.ts
CHANGED
|
@@ -450,13 +450,27 @@ export declare function markRoomUnread(config: RoomSocialConfig, room: string, s
|
|
|
450
450
|
* one. The pinned ROOM_SET_ATTENTION frame (setRoomAttentionOnClient)
|
|
451
451
|
* predates `untilMs` and stays the live-session path for permanent
|
|
452
452
|
* postures; servers older than mute-with-duration strip the field and land
|
|
453
|
-
* the mute permanent, so a requested `untilMs` the echo omits
|
|
454
|
-
*
|
|
455
|
-
* unbounded mute the caller believes
|
|
456
|
-
*
|
|
453
|
+
* the mute permanent, so a requested `untilMs` the echo omits is a DROPPED
|
|
454
|
+
* DEADLINE. By default that fail-closes here: the attention is reset and
|
|
455
|
+
* the call throws rather than leave an unbounded mute the caller believes
|
|
456
|
+
* timed. Callers with a richer fail-closed seam of their own opt out via
|
|
457
|
+
* `onDroppedDeadline: "resolve"` and own the handling (see the option).
|
|
458
|
+
* Echoes the persisted attention; `muteUntil` is the ISO expiry, null when
|
|
459
|
+
* none stands (which, under "resolve", is how a dropped deadline reads).
|
|
457
460
|
*/
|
|
458
461
|
export declare function setRoomAttentionRest(config: RoomSocialConfig, room: string, attention: RoomTopicAttention, options?: {
|
|
459
462
|
untilMs?: number | undefined;
|
|
463
|
+
/** What to do when a requested `untilMs` comes back without an echoed
|
|
464
|
+
* `muteUntil` (the server predates timed mutes and landed the mute
|
|
465
|
+
* PERMANENT). Default "undo-and-throw": reset attention to 'default'
|
|
466
|
+
* and throw RoomSocialError 502, so no unbounded mute stands. Pass
|
|
467
|
+
* "resolve" ONLY when the caller implements its own dropped-deadline
|
|
468
|
+
* handling: the call then performs no undo write and no throw, and
|
|
469
|
+
* resolves with `muteUntil: null` for the caller to act on. Canonical
|
|
470
|
+
* example: the GUI session seam, which undoes to the last
|
|
471
|
+
* WS-confirmed attention, richer than this library's
|
|
472
|
+
* undo-to-default. */
|
|
473
|
+
onDroppedDeadline?: "undo-and-throw" | "resolve" | undefined;
|
|
460
474
|
}): Promise<{
|
|
461
475
|
attention: RoomTopicAttention;
|
|
462
476
|
muteUntil: string | null;
|
|
@@ -590,8 +604,10 @@ export declare function socialRequest<T>(config: RoomSocialConfig, path: string,
|
|
|
590
604
|
formData?: FormData;
|
|
591
605
|
headers?: Record<string, string>;
|
|
592
606
|
/** Fetch cache mode; token-bearing reads pass "no-store" so posting
|
|
593
|
-
* credentials never persist in a browser HTTP cache.
|
|
594
|
-
|
|
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";
|
|
595
611
|
}): Promise<T>;
|
|
596
612
|
export declare function cleanUsername(username: string): string;
|
|
597
613
|
export declare function cleanRoomId(room: string): string;
|
package/dist/social-client.js
CHANGED
|
@@ -724,10 +724,13 @@ export async function markRoomUnread(config, room, seq) {
|
|
|
724
724
|
* one. The pinned ROOM_SET_ATTENTION frame (setRoomAttentionOnClient)
|
|
725
725
|
* predates `untilMs` and stays the live-session path for permanent
|
|
726
726
|
* postures; servers older than mute-with-duration strip the field and land
|
|
727
|
-
* the mute permanent, so a requested `untilMs` the echo omits
|
|
728
|
-
*
|
|
729
|
-
* unbounded mute the caller believes
|
|
730
|
-
*
|
|
727
|
+
* the mute permanent, so a requested `untilMs` the echo omits is a DROPPED
|
|
728
|
+
* DEADLINE. By default that fail-closes here: the attention is reset and
|
|
729
|
+
* the call throws rather than leave an unbounded mute the caller believes
|
|
730
|
+
* timed. Callers with a richer fail-closed seam of their own opt out via
|
|
731
|
+
* `onDroppedDeadline: "resolve"` and own the handling (see the option).
|
|
732
|
+
* Echoes the persisted attention; `muteUntil` is the ISO expiry, null when
|
|
733
|
+
* none stands (which, under "resolve", is how a dropped deadline reads).
|
|
731
734
|
*/
|
|
732
735
|
export async function setRoomAttentionRest(config, room, attention, options = {}) {
|
|
733
736
|
const body = { attention };
|
|
@@ -738,7 +741,9 @@ export async function setRoomAttentionRest(config, room, attention, options = {}
|
|
|
738
741
|
body,
|
|
739
742
|
});
|
|
740
743
|
const muteUntil = typeof result.muteUntil === "string" ? result.muteUntil : null;
|
|
741
|
-
if (options.untilMs !== undefined &&
|
|
744
|
+
if (options.untilMs !== undefined &&
|
|
745
|
+
muteUntil === null &&
|
|
746
|
+
options.onDroppedDeadline !== "resolve") {
|
|
742
747
|
// The undo cannot know the caller's prior attention, so it deliberately
|
|
743
748
|
// lands 'default' (the same neutral a lapsed timed mute reads as, see
|
|
744
749
|
// resolveTopicAttention) rather than leave the unbounded mute standing.
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmarket/rooms-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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(
|
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,6 +101,7 @@ 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",
|
|
@@ -278,7 +279,9 @@ export interface RoomLedgerEntry {
|
|
|
278
279
|
* (and servers older than the unfurl worker) omit it. */
|
|
279
280
|
unfurls?: RoomUnfurl[] | undefined;
|
|
280
281
|
roomRole?: RoomRole | null | undefined;
|
|
281
|
-
metadata?:
|
|
282
|
+
metadata?:
|
|
283
|
+
| (Record<string, unknown> & Partial<RoomForwardMetadata> & Partial<RoomWebhookMetadata>)
|
|
284
|
+
| undefined;
|
|
282
285
|
}
|
|
283
286
|
|
|
284
287
|
export type RoomForwardedFrom =
|
|
@@ -312,10 +315,11 @@ export interface RoomWebhookMetadata {
|
|
|
312
315
|
|
|
313
316
|
export interface RoomAttachment {
|
|
314
317
|
/**
|
|
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
|
|
318
|
+
* Object key under room-attachments/ or dm-attachments/; clients sign it
|
|
319
|
+
* at render time. Absent while scanStatus is "pending" on entries served
|
|
320
|
+
* to recipients: the store withholds the signable key until the scan
|
|
321
|
+
* clears, so render the metadata as a placeholder and do not attempt
|
|
322
|
+
* signing without a key.
|
|
319
323
|
*/
|
|
320
324
|
key?: string | undefined;
|
|
321
325
|
name: string;
|
|
@@ -327,7 +331,7 @@ export interface RoomAttachment {
|
|
|
327
331
|
/**
|
|
328
332
|
* A postable attachment reference. Outbound frames must always carry the
|
|
329
333
|
* stored key (senders reference their own uploads, which include it); only
|
|
330
|
-
*
|
|
334
|
+
* recipient views of pending entries may be keyless, and those must never be
|
|
331
335
|
* forwarded back into a post.
|
|
332
336
|
*/
|
|
333
337
|
export type RoomAttachmentInput = RoomAttachment & { key: string };
|
|
@@ -683,7 +687,9 @@ export interface RoomDmMessage {
|
|
|
683
687
|
createdAt?: string | number | undefined;
|
|
684
688
|
/** Sealed DM: content is '' and the opaque envelope rides here. */
|
|
685
689
|
secret?: RoomLedgerSecretPayload | null | undefined;
|
|
686
|
-
metadata?:
|
|
690
|
+
metadata?:
|
|
691
|
+
| (Record<string, unknown> & Partial<RoomForwardMetadata> & Partial<RoomWebhookMetadata>)
|
|
692
|
+
| undefined;
|
|
687
693
|
/** Legacy public chat-image URL (renders as-is, no signing). */
|
|
688
694
|
imageUrl?: string | null | undefined;
|
|
689
695
|
/** Private, signed, scanned DM attachments (dm-attachments/ keyspace).
|
|
@@ -718,6 +724,7 @@ export interface RoomDmSendPayload {
|
|
|
718
724
|
/** DM-attachment key or legacy public image URL; stored on the message's
|
|
719
725
|
* imageUrl. Additive — text-only sends omit it. */
|
|
720
726
|
imageUrl?: string;
|
|
727
|
+
attachments?: RoomAttachmentInput[];
|
|
721
728
|
metadata?: RoomForwardMetadata | undefined;
|
|
722
729
|
}
|
|
723
730
|
|
|
@@ -781,6 +788,13 @@ export interface RoomDmReactionUpdatedPayload {
|
|
|
781
788
|
participant: RoomDmParticipant;
|
|
782
789
|
}
|
|
783
790
|
|
|
791
|
+
export interface RoomDmAttachmentUpdatedPayload {
|
|
792
|
+
conversationId: string;
|
|
793
|
+
messageId: string;
|
|
794
|
+
scanStatus: "clean" | "held";
|
|
795
|
+
attachments: RoomAttachment[];
|
|
796
|
+
}
|
|
797
|
+
|
|
784
798
|
export interface RoomDmPinPayload {
|
|
785
799
|
requestId?: string;
|
|
786
800
|
conversationId?: string;
|
|
@@ -1653,6 +1667,11 @@ export type RoomServerMessage =
|
|
|
1653
1667
|
payload: RoomDmReactionUpdatedPayload;
|
|
1654
1668
|
timestamp: number;
|
|
1655
1669
|
}
|
|
1670
|
+
| {
|
|
1671
|
+
type: typeof RoomServerMessageType.DM_ATTACHMENT_UPDATED;
|
|
1672
|
+
payload: RoomDmAttachmentUpdatedPayload;
|
|
1673
|
+
timestamp: number;
|
|
1674
|
+
}
|
|
1656
1675
|
| {
|
|
1657
1676
|
type: typeof RoomServerMessageType.DM_PIN_UPDATED;
|
|
1658
1677
|
payload: RoomDmPinUpdatedPayload;
|
package/src/social-client.ts
CHANGED
|
@@ -1456,16 +1456,32 @@ export async function markRoomUnread(
|
|
|
1456
1456
|
* one. The pinned ROOM_SET_ATTENTION frame (setRoomAttentionOnClient)
|
|
1457
1457
|
* predates `untilMs` and stays the live-session path for permanent
|
|
1458
1458
|
* postures; servers older than mute-with-duration strip the field and land
|
|
1459
|
-
* the mute permanent, so a requested `untilMs` the echo omits
|
|
1460
|
-
*
|
|
1461
|
-
* unbounded mute the caller believes
|
|
1462
|
-
*
|
|
1459
|
+
* the mute permanent, so a requested `untilMs` the echo omits is a DROPPED
|
|
1460
|
+
* DEADLINE. By default that fail-closes here: the attention is reset and
|
|
1461
|
+
* the call throws rather than leave an unbounded mute the caller believes
|
|
1462
|
+
* timed. Callers with a richer fail-closed seam of their own opt out via
|
|
1463
|
+
* `onDroppedDeadline: "resolve"` and own the handling (see the option).
|
|
1464
|
+
* Echoes the persisted attention; `muteUntil` is the ISO expiry, null when
|
|
1465
|
+
* none stands (which, under "resolve", is how a dropped deadline reads).
|
|
1463
1466
|
*/
|
|
1464
1467
|
export async function setRoomAttentionRest(
|
|
1465
1468
|
config: RoomSocialConfig,
|
|
1466
1469
|
room: string,
|
|
1467
1470
|
attention: RoomTopicAttention,
|
|
1468
|
-
options: {
|
|
1471
|
+
options: {
|
|
1472
|
+
untilMs?: number | undefined;
|
|
1473
|
+
/** What to do when a requested `untilMs` comes back without an echoed
|
|
1474
|
+
* `muteUntil` (the server predates timed mutes and landed the mute
|
|
1475
|
+
* PERMANENT). Default "undo-and-throw": reset attention to 'default'
|
|
1476
|
+
* and throw RoomSocialError 502, so no unbounded mute stands. Pass
|
|
1477
|
+
* "resolve" ONLY when the caller implements its own dropped-deadline
|
|
1478
|
+
* handling: the call then performs no undo write and no throw, and
|
|
1479
|
+
* resolves with `muteUntil: null` for the caller to act on. Canonical
|
|
1480
|
+
* example: the GUI session seam, which undoes to the last
|
|
1481
|
+
* WS-confirmed attention, richer than this library's
|
|
1482
|
+
* undo-to-default. */
|
|
1483
|
+
onDroppedDeadline?: "undo-and-throw" | "resolve" | undefined;
|
|
1484
|
+
} = {},
|
|
1469
1485
|
): Promise<{ attention: RoomTopicAttention; muteUntil: string | null }> {
|
|
1470
1486
|
const body: { attention: RoomTopicAttention; untilMs?: number } = { attention };
|
|
1471
1487
|
if (options.untilMs !== undefined) body.untilMs = options.untilMs;
|
|
@@ -1479,7 +1495,11 @@ export async function setRoomAttentionRest(
|
|
|
1479
1495
|
body,
|
|
1480
1496
|
});
|
|
1481
1497
|
const muteUntil = typeof result.muteUntil === "string" ? result.muteUntil : null;
|
|
1482
|
-
if (
|
|
1498
|
+
if (
|
|
1499
|
+
options.untilMs !== undefined &&
|
|
1500
|
+
muteUntil === null &&
|
|
1501
|
+
options.onDroppedDeadline !== "resolve"
|
|
1502
|
+
) {
|
|
1483
1503
|
// The undo cannot know the caller's prior attention, so it deliberately
|
|
1484
1504
|
// lands 'default' (the same neutral a lapsed timed mute reads as, see
|
|
1485
1505
|
// resolveTopicAttention) rather than leave the unbounded mute standing.
|
|
@@ -1914,8 +1934,10 @@ export async function socialRequest<T>(
|
|
|
1914
1934
|
formData?: FormData;
|
|
1915
1935
|
headers?: Record<string, string>;
|
|
1916
1936
|
/** Fetch cache mode; token-bearing reads pass "no-store" so posting
|
|
1917
|
-
* credentials never persist in a browser HTTP cache.
|
|
1918
|
-
|
|
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";
|
|
1919
1941
|
} = {},
|
|
1920
1942
|
): Promise<T> {
|
|
1921
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. */
|