@pmate/chat-sdk 0.12.0-next.63 → 0.12.0-next.64

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 CHANGED
@@ -215,7 +215,9 @@ selects receipt counts for the current viewport; clear with `watchReaders(undefi
215
215
  `ChatError` exposes `code`, `retryable`, optional HTTP `status`, and the original
216
216
  `cause`. Recovery categories are `unavailable`, `access-denied`,
217
217
  `storage-unavailable`, `invalid-input`, `conflict`, `closed`, and `busy`.
218
- Permission failures never turn into trusted offline data. Dispose and recreate
218
+ Permission failures withdraw the current scoped session and retire its caches;
219
+ failed cleanup remains in the error cause. They never turn into trusted offline
220
+ data. Dispose and recreate
219
221
  the SDK when account, profile, team or token changes.
220
222
 
221
223
  ### Advanced integration and migration
package/lib/chat.d.ts CHANGED
@@ -70,6 +70,7 @@ export declare class Chat {
70
70
  private closed;
71
71
  constructor(advanced: ChatSession<CachedChatMessage>, options: CreateChatOptions);
72
72
  private operation;
73
+ private deny;
73
74
  private threadOperation;
74
75
  start(): Promise<void>;
75
76
  getConversation(target: ConversationTarget): Promise<string>;
package/lib/chat.js CHANGED
@@ -22,6 +22,18 @@ export class Chat {
22
22
  catch (error) {
23
23
  throw asChatError(error);
24
24
  } }
25
+ async deny(threadId, failure) {
26
+ this.errors.set(threadId, failure);
27
+ this.targets.delete(threadId);
28
+ const retiring = this.advanced.revokeAccess();
29
+ this.notify(threadId);
30
+ try {
31
+ await retiring;
32
+ }
33
+ catch (error) {
34
+ failure.cause = new AggregateError([failure.cause, error], "Access withdrawn; cache cleanup incomplete");
35
+ }
36
+ }
25
37
  threadOperation(threadId, run) {
26
38
  return this.operation(async () => {
27
39
  const denied = threadId ? this.errors.get(threadId) : undefined;
@@ -32,11 +44,8 @@ export class Chat {
32
44
  }
33
45
  catch (error) {
34
46
  const failure = asChatError(error);
35
- if (threadId && failure.code === "access-denied") {
36
- this.errors.set(threadId, failure);
37
- this.targets.delete(threadId);
38
- this.notify(threadId);
39
- }
47
+ if (threadId && failure.code === "access-denied")
48
+ await this.deny(threadId, failure);
40
49
  throw failure;
41
50
  }
42
51
  });
@@ -57,10 +66,12 @@ export class Chat {
57
66
  getSnapshot(threadId) {
58
67
  if (threadId === undefined) {
59
68
  const source = this.advanced.getSnapshot(), previous = this.summary;
60
- const threads = source.threads.map(thread => ({ threadId: thread.threadId, lastMessage: thread.lastItem, group: thread.group, unread: thread.unread, outgoingCount: thread.outgoingCount }));
61
- if (previous && previous.connection === source.connection && previous.badgeCount === source.badgeCount && previous.unreadMessageCount === source.unreadMessageCount && previous.threads.length === threads.length && threads.every((thread, i) => { const old = previous.threads[i]; return thread.threadId === old.threadId && thread.lastMessage === old.lastMessage && thread.group === old.group && thread.unread === old.unread && thread.outgoingCount === old.outgoingCount; }))
69
+ const threads = source.threads.filter(thread => this.errors.get(thread.threadId)?.code !== "access-denied").map(thread => ({ threadId: thread.threadId, lastMessage: thread.lastItem, group: thread.group, unread: thread.unread, outgoingCount: thread.outgoingCount }));
70
+ const unreadMessageCount = source.unreadMessageCount === undefined ? undefined : threads.reduce((sum, thread) => sum + (thread.unread?.unreadMessageCount ?? 0), 0);
71
+ const badgeCount = source.badgeCount === undefined ? undefined : threads.reduce((sum, thread) => sum + (thread.unread?.badgeCount ?? 0), 0);
72
+ if (previous && previous.connection === source.connection && previous.badgeCount === badgeCount && previous.unreadMessageCount === unreadMessageCount && previous.threads.length === threads.length && threads.every((thread, i) => { const old = previous.threads[i]; return thread.threadId === old.threadId && thread.lastMessage === old.lastMessage && thread.group === old.group && thread.unread === old.unread && thread.outgoingCount === old.outgoingCount; }))
62
73
  return previous;
63
- return this.summary = { threads, connection: source.connection, badgeCount: source.badgeCount, unreadMessageCount: source.unreadMessageCount };
74
+ return this.summary = { threads, connection: source.connection, badgeCount, unreadMessageCount };
64
75
  }
65
76
  const source = this.advanced.getSnapshot(threadId), previous = this.views.get(threadId);
66
77
  const error = this.closed ? undefined : this.errors.get(threadId), denied = error?.code === "access-denied";
@@ -87,8 +98,12 @@ export class Chat {
87
98
  catch (error) {
88
99
  const failure = asChatError(error);
89
100
  if (!this.closed) {
90
- this.errors.set(threadId, failure);
91
- this.notify(threadId);
101
+ if (failure.code === "access-denied")
102
+ await this.deny(threadId, failure);
103
+ else {
104
+ this.errors.set(threadId, failure);
105
+ this.notify(threadId);
106
+ }
92
107
  }
93
108
  throw failure;
94
109
  }
@@ -138,7 +153,7 @@ export class Chat {
138
153
  sessionFields(fields) {
139
154
  return fields ? [...new Set(fields.flatMap(field => field === "messages" ? ["timeline", "complete"] : field === "ready" ? ["complete"] : field === "conversation" ? ["messages", "group"] : field === "error" || field === "threadId" ? [] : [field]))] : undefined;
140
155
  }
141
- notify(threadId) { this.listeners.get(threadId)?.forEach(listener => listener()); }
156
+ notify(threadId) { this.listeners.get(threadId)?.forEach(listener => listener()); this.listeners.get("")?.forEach(listener => listener()); }
142
157
  observeMessages(threadId, ids, event) { return this.threadOperation(threadId, () => this.advanced.observeMessages(threadId, ids, event)); }
143
158
  markRead(threadId) { return this.threadOperation(threadId, () => this.advanced.markRead(threadId)); }
144
159
  getReaders(threadId, messageId, page = {}, signal) { return this.threadOperation(threadId, () => this.advanced.getReaders(threadId, messageId, page, signal)); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmate/chat-sdk",
3
- "version": "0.12.0-next.63",
3
+ "version": "0.12.0-next.64",
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",