@pmate/chat-sdk 0.12.0-next.65 → 0.12.0-next.66

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/lib/chat.d.ts CHANGED
@@ -48,6 +48,17 @@ export type MessageQuery = {
48
48
  all?: boolean;
49
49
  refresh?: boolean;
50
50
  };
51
+ export type ReadersQuery = {
52
+ afterProfileId?: string;
53
+ revision?: number;
54
+ };
55
+ export type GroupChanges = {
56
+ expectedRevision?: string;
57
+ addMembers?: readonly string[];
58
+ removeMembers?: readonly string[];
59
+ title?: string;
60
+ avatar?: string;
61
+ };
51
62
  export type SendOptions = {
52
63
  waitFor?: "accepted" | "delivered";
53
64
  };
@@ -92,7 +103,7 @@ export declare class Chat {
92
103
  private notify;
93
104
  observeMessages(threadId: string, ids: string[], event: "visible" | "played"): Promise<void>;
94
105
  markRead(threadId: string): Promise<void>;
95
- getReaders(threadId: string, messageId: string, page?: Parameters<ChatSession<CachedChatMessage>["getReaders"]>[2], signal?: AbortSignal): Promise<import("./runtime/messageReaders.js").MessageReadersPage>;
106
+ getReaders(threadId: string, messageId: string, page?: ReadersQuery, signal?: AbortSignal): Promise<import("./runtime/messageReaders.js").MessageReadersPage>;
96
107
  watchReaders(threadId: string | undefined, ids: string[], receiver?: string): Promise<void>;
97
108
  handleReach(event: Parameters<ChatSession<CachedChatMessage>["handleReach"]>[0]): Promise<"handled" | undefined>;
98
109
  getVoiceAttachment(threadId: string, messageId: string): Promise<import("./runtime/durableSender.js").VoiceAttachmentLease>;
@@ -113,7 +124,7 @@ export declare class Chat {
113
124
  ownerId: string;
114
125
  memberIds: readonly string[];
115
126
  }>;
116
- updateGroup(threadId: string, changes: Parameters<ChatSession<CachedChatMessage>["updateGroup"]>[1]): Promise<Readonly<Omit<RuntimeGroup, "versionNumber">>>;
127
+ updateGroup(threadId: string, changes: GroupChanges): Promise<Readonly<Omit<RuntimeGroup, "versionNumber">>>;
117
128
  revokeAccess(): Promise<void>;
118
129
  close(): Promise<void>;
119
130
  }
package/lib/chat.js CHANGED
@@ -191,7 +191,13 @@ export class Chat {
191
191
  notify(threadId) { this.listeners.get(threadId)?.forEach(listener => listener()); this.listeners.get("")?.forEach(listener => listener()); }
192
192
  observeMessages(threadId, ids, event) { return this.threadOperation(threadId, async () => { await this.advanced.observeMessages(threadId, ids, event); }); }
193
193
  markRead(threadId) { return this.threadOperation(threadId, async () => { await this.advanced.markRead(threadId); }); }
194
- getReaders(threadId, messageId, page = {}, signal) { return this.threadOperation(threadId, () => this.advanced.getReaders(threadId, messageId, page, signal)); }
194
+ getReaders(threadId, messageId, page = {}, signal) {
195
+ return this.threadOperation(threadId, async () => {
196
+ if (Object.keys(page).some(key => !["afterProfileId", "revision"].includes(key)))
197
+ throw new ChatError("invalid-input", "Unsupported reader query");
198
+ return this.advanced.getReaders(threadId, messageId, page, signal);
199
+ });
200
+ }
195
201
  watchReaders(threadId, ids, receiver) { return this.threadOperation(threadId, () => this.advanced.observeMessages(threadId, ids, "receipts", receiver)); }
196
202
  handleReach(event) { return this.operation(() => this.advanced.handleReach(event)); }
197
203
  getVoiceAttachment(threadId, messageId) { return this.threadOperation(threadId, () => this.advanced.getVoiceAttachment(threadId, messageId)); }
package/lib/index.d.ts CHANGED
@@ -41,5 +41,5 @@ export * from "./runtime/socialReadClient";
41
41
  export * from "./ux/imagePaste";
42
42
  export * from "./hooks/useImageAttachments";
43
43
  export * from "./components/ImageAttachmentTray";
44
- export { createChat, Chat, ChatError, type ChatThreadSnapshot, type ChatMessage as MessageView, type ChatErrorCode, type ConversationTarget, type OutgoingContent, type CreateChatOptions, type ChatSnapshot, type SentMessage, type MessageQuery } from "./chat.js";
44
+ export { createChat, Chat, ChatError, type ChatThreadSnapshot, type ChatMessage as MessageView, type ChatErrorCode, type ConversationTarget, type OutgoingContent, type CreateChatOptions, type ChatSnapshot, type SentMessage, type MessageQuery, type ReadersQuery, type GroupChanges } from "./chat.js";
45
45
  export { useChatSnapshot } from "./hooks/useChatSnapshot.js";
@@ -7,6 +7,7 @@ export type ChatMessage = Readonly<Omit<TimelineItem<CachedChatMessage>, "messag
7
7
  text: string;
8
8
  senderName?: string;
9
9
  senderAvatar?: string;
10
+ timeLabel?: string;
10
11
  image?: Readonly<{
11
12
  url: string;
12
13
  name?: string;
@@ -38,7 +38,7 @@ function content(message) {
38
38
  const imageUrl = media(payload.url) ?? (hint.kind === "image" ? media(hint.imageUrl) : undefined);
39
39
  const messageQuote = quote(payload.quote) ?? quote(hint.quote);
40
40
  const rawText = text(payload.text) || text(object(body.payload).message);
41
- const result = { kind: "text", text: validLegacy ? text(hint.content) : rawText, senderName: text(hint.senderName) || undefined, senderAvatar: media(hint.senderAvatar),
41
+ const result = { kind: "text", text: validLegacy ? text(hint.content) : rawText, senderName: text(hint.senderName) || undefined, senderAvatar: media(hint.senderAvatar), timeLabel: text(hint.createdAt) || undefined,
42
42
  recipientCount: message.threadKind === "dm" ? 1 : validLegacy ? typeof hint.recipientCount === "number" ? hint.recipientCount : undefined : Array.isArray(message.recipients) ? new Set(message.recipients.filter(id => id !== message.from)).size : undefined };
43
43
  if (validLegacy && ["emoji", "system", "attachment", "quote"].includes(String(hint.kind)))
44
44
  result.kind = hint.kind;
@@ -1,3 +1,4 @@
1
+ import { type ChatMessage } from "../messageView.js";
1
2
  import { type MessageScope } from "./messageStore.js";
2
3
  import { type CachedChatMessage } from "./cachedMessage.js";
3
4
  import { type TimelineItem } from "./messageTimeline.js";
@@ -12,6 +13,7 @@ export type MessageLaunchView = {
12
13
  threadId: string;
13
14
  metadata: MessageLaunchMetadata;
14
15
  images: Record<string, string>;
16
+ messages: readonly ChatMessage[]; /** @deprecated Advanced compatibility projection. */
15
17
  timeline: readonly TimelineItem<CachedChatMessage>[];
16
18
  };
17
19
  type Options = {
@@ -1,3 +1,4 @@
1
+ import { createMessagePresenter } from "../messageView.js";
1
2
  import { Dexie } from "dexie";
2
3
  import { IndexedDbMessageStore, messageScopeKey, mergeStoredMessage } from "./messageStore.js";
3
4
  import { DurableChatSender } from "./durableSender.js";
@@ -47,6 +48,7 @@ export async function saveMessageLaunch(input, options = {}) {
47
48
  export function watchMessageLaunch(input, onChange, onError, options = {}) {
48
49
  const { scope, openId, threadId } = input, key = messageScopeKey(scope), store = messages(scope, options), { db, rows } = tickets(options);
49
50
  let closed = false, generation = 0, ticket, timer;
51
+ const present = createMessagePresenter();
50
52
  const channels = [];
51
53
  const stop = () => { if (closed)
52
54
  return; closed = true; generation++; clearTimeout(timer); tasks.close(); channels.forEach(channel => channel.close()); store.close(); db.close(); };
@@ -89,7 +91,9 @@ export function watchMessageLaunch(input, onChange, onError, options = {}) {
89
91
  return;
90
92
  }
91
93
  const timeline = projectMessageTimeline({ appId: scope.appId, profileId: scope.profileId, threadId, messages: page.messages, outgoing, decode: message => decodeCachedChatMessage(message, scope), reading: {} }).slice(-30);
92
- onChange(timeline.length ? { openId, threadId, metadata: ticket.metadata, images: ticket.images, timeline } : undefined);
94
+ const normalized = timeline.map(present), visibleImages = new Set(normalized.flatMap(message => message.image ? [message.image.url] : []));
95
+ const images = Object.fromEntries(Object.entries(ticket.images).filter(([url]) => visibleImages.has(url)));
96
+ onChange(timeline.length ? { openId, threadId, metadata: ticket.metadata, images, messages: normalized, timeline } : undefined);
93
97
  };
94
98
  const tasks = new WindowedTasks(16, fail);
95
99
  if (typeof BroadcastChannel !== "undefined") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmate/chat-sdk",
3
- "version": "0.12.0-next.65",
3
+ "version": "0.12.0-next.66",
4
4
  "description": "Local-first chat SDK, message subscriptions, reading state, and shared chat UI primitives",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",