@pmate/chat-sdk 0.12.0-next.5 → 0.12.0-next.50

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.
Files changed (52) hide show
  1. package/README.md +34 -1
  2. package/lib/runtime/batchedReadIndex.d.ts +16 -3
  3. package/lib/runtime/batchedReadIndex.js +50 -4
  4. package/lib/runtime/cachedMessage.d.ts +12 -0
  5. package/lib/runtime/cachedMessage.js +18 -0
  6. package/lib/runtime/chatClient.d.ts +1 -0
  7. package/lib/runtime/chatClient.js +1 -1
  8. package/lib/runtime/chatSession.d.ts +77 -13
  9. package/lib/runtime/chatSession.js +483 -42
  10. package/lib/runtime/connectivity.d.ts +4 -0
  11. package/lib/runtime/connectivity.js +8 -0
  12. package/lib/runtime/conversationRead.d.ts +13 -0
  13. package/lib/runtime/conversationRead.js +34 -0
  14. package/lib/runtime/conversationReading.d.ts +28 -0
  15. package/lib/runtime/conversationReading.js +47 -0
  16. package/lib/runtime/createChat.d.ts +5 -5
  17. package/lib/runtime/createChat.js +16 -17
  18. package/lib/runtime/durableSender.d.ts +54 -14
  19. package/lib/runtime/durableSender.js +354 -44
  20. package/lib/runtime/index.d.ts +4 -0
  21. package/lib/runtime/index.js +4 -0
  22. package/lib/runtime/messageClient.d.ts +32 -8
  23. package/lib/runtime/messageClient.js +415 -75
  24. package/lib/runtime/messageControls.d.ts +19 -0
  25. package/lib/runtime/messageControls.js +44 -0
  26. package/lib/runtime/messageLaunch.d.ts +42 -0
  27. package/lib/runtime/messageLaunch.js +133 -0
  28. package/lib/runtime/messageQuotes.d.ts +3 -0
  29. package/lib/runtime/messageQuotes.js +17 -0
  30. package/lib/runtime/messageReaders.d.ts +12 -1
  31. package/lib/runtime/messageReaders.js +67 -3
  32. package/lib/runtime/messageStore.d.ts +40 -2
  33. package/lib/runtime/messageStore.js +218 -10
  34. package/lib/runtime/messageTimeline.d.ts +50 -0
  35. package/lib/runtime/messageTimeline.js +120 -0
  36. package/lib/runtime/readCounts.d.ts +15 -1
  37. package/lib/runtime/readCounts.js +96 -8
  38. package/lib/runtime/readIndex.d.ts +55 -6
  39. package/lib/runtime/readIndex.js +363 -14
  40. package/lib/runtime/readSync.d.ts +1 -0
  41. package/lib/runtime/readSync.js +25 -8
  42. package/lib/runtime/readingClient.d.ts +19 -0
  43. package/lib/runtime/readingClient.js +131 -19
  44. package/lib/runtime/readingContent.d.ts +5 -0
  45. package/lib/runtime/readingContent.js +23 -0
  46. package/lib/runtime/recordCache.d.ts +9 -0
  47. package/lib/runtime/recordCache.js +62 -5
  48. package/lib/runtime/responseCache.d.ts +44 -0
  49. package/lib/runtime/responseCache.js +173 -0
  50. package/lib/runtime/voiceUpload.d.ts +2 -0
  51. package/lib/runtime/voiceUpload.js +20 -0
  52. package/package.json +6 -1
package/README.md CHANGED
@@ -142,7 +142,12 @@ The browser entry point is `createChat` from `@pmate/chat-sdk/runtime`:
142
142
  ```ts
143
143
  const chat = createChat({ appId, accountId, profileId, teamId, token, onError })
144
144
  await chat.start()
145
- const page = await chat.getMessages(threadId, { limit: 50 })
145
+ const page = await chat.getMessages(threadId)
146
+ // SDK owns the page size and history boundary; hosts need not construct a cursor.
147
+ const earlier = await chat.getMessages(threadId, { earlier: true })
148
+ // After an authoritative mutation (e.g. history assignment), await reconciliation.
149
+ // Concurrent refreshes share transport; errors do not masquerade as fresh cache.
150
+ const reconciled = await chat.getMessages(threadId, { refresh: true })
146
151
  // Explicit full-history queries reuse cached pages and internally rebase expired cursors.
147
152
  const history = await chat.getMessages(threadId, { all: true })
148
153
  const off = chat.subscribe(threadId, () => render(chat.getSnapshot(threadId)))
@@ -153,9 +158,17 @@ await chat.observeMessages(threadId, [voiceId], "played")
153
158
  await chat.sendMessage(threadId, { to, body, recipients })
154
159
  const readers = await chat.getReaders(threadId, messageId)
155
160
  off(); offSummary()
161
+ // Ordinary navigation/shutdown preserves offline data.
156
162
  await chat.close()
157
163
  ```
158
164
 
165
+ ```ts
166
+ // When team access is withdrawn, use this instead of ordinary close(). It hides
167
+ // snapshots immediately, retires durable caches/outbox and closes this session.
168
+ // Other teams' reading operations remain intact. Create a new session to recover.
169
+ await chat.revokeAccess()
170
+ ```
171
+
159
172
  The facade owns all clients below. Message reads transparently restore IndexedDB
160
173
  windows and fetch missing ranges through the authenticated Hub. Its normalized
161
174
  messages retain the protocol body; product rendering remains with the host.
@@ -198,6 +211,11 @@ legacy cleanup happens after durable migration. Normal reads use memory and writ
198
211
  are merged into bounded IndexedDB transactions. Cache errors must be surfaced by
199
212
  the host, and pending operations are not reported as saved before their promise
200
213
  resolves.
214
+ Control-event batches are bounded by both 128 records and 1 MiB per transaction;
215
+ the queue includes in-flight writes and is limited to 4096 records / 8 MiB.
216
+ `transactionBytes` and `queueBytes` can lower these limits. Oversized operations
217
+ fail before changing the memory snapshot. Repeating an in-flight write waits for
218
+ that physical write, including its failure, rather than acknowledging it early.
201
219
 
202
220
  `DurableChatSender` saves the exact message created by `ChatClient.prepareChat`
203
221
  before transmitting it. `retry(hash)` uses `sendPrepared` with the original hash
@@ -210,3 +228,18 @@ Custom primitive integrations must initialize them with the same authenticated
210
228
  scope, dispose them on identity changes, and wire their transport to an authorized
211
229
  server. Standard browser integrations should use the facade instead. The Merry migration and production validation
212
230
  are tracked in the unified message layer proposal.
231
+
232
+ When the browser reports offline, scheduled message restoration and reading
233
+ refreshes wait for the normal reconnect/wake path. Cached reads remain available;
234
+ unknown receipt counts stay unknown and an uncached reader detail page rejects
235
+ without attempting transport. A network failure arriving after the browser went
236
+ offline does not turn background synchronization into an operation error.
237
+ Authorization failures still revoke the corresponding cached data.
238
+
239
+ Reading entry points check the message scope even before `initialize()` finishes.
240
+ Concurrent observations share the permission lookup and batch their local writes.
241
+ Visibility/playback operations reject another known team's conversation; a
242
+ permission change in another team does not discard this team's observations.
243
+ Ordinary shutdown waits for accepted local reading writes, while access withdrawal
244
+ rejects late results. Retired asynchronous reading entries reject through promises;
245
+ refresh/subscription teardown remains safe.
@@ -1,6 +1,8 @@
1
- import { type ReadIndexPersistence, type ReadIndexState } from "./readIndex.js";
1
+ import { type ReadIndexPersistence, type ReadIndexState, type ReadIndexTransform, type ReadObservationScope } from "./readIndex.js";
2
2
  type Change = {
3
- transform: (state: ReadIndexState) => ReadIndexState;
3
+ weight: number;
4
+ observation?: ReadObservationScope;
5
+ transform: ReadIndexTransform;
4
6
  resolve: (state: ReadIndexState) => void;
5
7
  reject: (error: unknown) => void;
6
8
  };
@@ -13,9 +15,20 @@ export declare class BatchedReadIndex implements ReadIndexPersistence {
13
15
  private queueLimit;
14
16
  private queues;
15
17
  private closed;
18
+ private sourceCounts;
16
19
  constructor(storage: ReadIndexPersistence, windowMs?: number, batchSize?: number, queueLimit?: number);
17
20
  load(appId: string, profileId: string): Promise<ReadIndexState | undefined>;
18
- update(appId: string, profileId: string, transform: Change["transform"]): Promise<ReadIndexState>;
21
+ loadVersioned(appId: string, profileId: string): Promise<{
22
+ state: ReadIndexState | undefined;
23
+ versions: import("./readIndex.js").ReadIndexVersions;
24
+ } | {
25
+ state: ReadIndexState | undefined;
26
+ versions: {};
27
+ }>;
28
+ loadVersions(appId: string, profileId: string): Promise<{}>;
29
+ invalidateTeam(appId: string, profileId: string, teamId: string, version: number, knownThreadIds?: readonly string[]): Promise<ReadIndexState>;
30
+ private weight;
31
+ update(appId: string, profileId: string, transform: Change["transform"], observation?: ReadObservationScope): Promise<ReadIndexState>;
19
32
  private drain;
20
33
  flush(appId: string, profileId: string): Promise<void>;
21
34
  close(): Promise<void>;
@@ -8,6 +8,7 @@ export class BatchedReadIndex {
8
8
  queueLimit;
9
9
  queues = new Map();
10
10
  closed = false;
11
+ sourceCounts = new WeakMap();
11
12
  constructor(storage, windowMs = 16, batchSize = 128, queueLimit = 4096) {
12
13
  this.storage = storage;
13
14
  this.windowMs = windowMs;
@@ -22,7 +23,42 @@ export class BatchedReadIndex {
22
23
  throw new Error("READ_STORAGE_CLOSED");
23
24
  return this.storage.load(appId, profileId);
24
25
  }
25
- update(appId, profileId, transform) {
26
+ async loadVersioned(appId, profileId) {
27
+ await this.flush(appId, profileId);
28
+ if (this.closed)
29
+ throw new Error("READ_STORAGE_CLOSED");
30
+ return this.storage.loadVersioned ? this.storage.loadVersioned(appId, profileId) : { state: await this.storage.load(appId, profileId), versions: {} };
31
+ }
32
+ async loadVersions(appId, profileId) {
33
+ if (this.closed)
34
+ throw new Error("READ_STORAGE_CLOSED");
35
+ return this.storage.loadVersions ? this.storage.loadVersions(appId, profileId) : {};
36
+ }
37
+ async invalidateTeam(appId, profileId, teamId, version, knownThreadIds) {
38
+ if (this.closed)
39
+ throw new Error("READ_STORAGE_CLOSED");
40
+ if (!this.storage.invalidateTeam)
41
+ throw new Error("READ_SCOPE_INVALIDATION_UNAVAILABLE");
42
+ // Do not flush a blocked old HTTP transform first. The durable epoch is the
43
+ // barrier; any queued transform sees it inside its eventual transaction.
44
+ return this.storage.invalidateTeam(appId, profileId, teamId, version, knownThreadIds);
45
+ }
46
+ weight(observation) {
47
+ if (!observation)
48
+ return 1;
49
+ let counts = this.sourceCounts.get(observation.base);
50
+ if (!counts) {
51
+ counts = new Map();
52
+ for (const thread of observation.base.threads) {
53
+ counts.set(thread.id, (counts.get(thread.id) ?? 0) + 1);
54
+ if (thread.conversationId && thread.conversationId !== thread.id)
55
+ counts.set(thread.conversationId, (counts.get(thread.conversationId) ?? 0) + 1);
56
+ }
57
+ this.sourceCounts.set(observation.base, counts);
58
+ }
59
+ return observation.targets.reduce((sum, target) => sum + (counts.get(target.threadId) ?? 1), 0);
60
+ }
61
+ update(appId, profileId, transform, observation) {
26
62
  if (this.closed)
27
63
  return Promise.reject(new Error("READ_STORAGE_CLOSED"));
28
64
  const key = JSON.stringify([appId, profileId]);
@@ -33,7 +69,7 @@ export class BatchedReadIndex {
33
69
  }
34
70
  if (queue.pending.length >= this.queueLimit)
35
71
  return Promise.reject(new Error("READ_STORAGE_BACKPRESSURE"));
36
- const promise = new Promise((resolve, reject) => queue.pending.push({ transform, resolve, reject }));
72
+ const promise = new Promise((resolve, reject) => queue.pending.push({ transform, observation, weight: this.weight(observation), resolve, reject }));
37
73
  if (!queue.running && queue.timer === undefined)
38
74
  queue.timer = setTimeout(() => { queue.timer = undefined; void this.drain(key, queue); }, this.windowMs);
39
75
  return promise;
@@ -43,12 +79,22 @@ export class BatchedReadIndex {
43
79
  return queue.running;
44
80
  clearTimeout(queue.timer);
45
81
  queue.timer = undefined;
46
- const batch = queue.pending.splice(0, this.batchSize);
82
+ const batch = [];
83
+ let weight = 0;
84
+ while (queue.pending.length && batch.length < this.batchSize) {
85
+ const nextWeight = queue.pending[0].weight;
86
+ if (batch.length && weight + nextWeight > 128)
87
+ break;
88
+ weight += nextWeight;
89
+ batch.push(queue.pending.shift());
90
+ }
47
91
  if (!batch.length)
48
92
  return;
49
93
  const run = (async () => {
50
94
  try {
51
- const next = await this.storage.update(queue.appId, queue.profileId, state => batch.reduce((current, change) => change.transform(current), state));
95
+ const first = batch[0].observation;
96
+ const observation = first && batch.every(change => change.observation?.base === first.base) ? { base: first.base, targets: batch.flatMap(change => change.observation.targets) } : undefined;
97
+ const next = await this.storage.update(queue.appId, queue.profileId, (state, versions) => batch.reduce((current, change) => change.transform(current, versions), state), observation);
52
98
  batch.forEach(change => change.resolve(next));
53
99
  }
54
100
  catch (error) {
@@ -0,0 +1,12 @@
1
+ import type { ChatReaderSocialMessage } from "./readerClient.js";
2
+ import type { ChatRuntimeMessage } from "./protocol.js";
3
+ export type CachedChatMessage = ChatReaderSocialMessage & {
4
+ id: string;
5
+ timestamp: number;
6
+ readPolicy?: "visible" | "explicit";
7
+ };
8
+ export declare function decodeCachedChatMessage(message: ChatRuntimeMessage, scope: {
9
+ appId: string;
10
+ profileId: string;
11
+ teamId: string;
12
+ }): CachedChatMessage | undefined;
@@ -0,0 +1,18 @@
1
+ import { chatTeamId } from "./teamScope.js";
2
+ import { dmThreadId, groupThreadId } from "./thread.js";
3
+ export function decodeCachedChatMessage(message, scope) {
4
+ if (message.opcode !== 1 || !message.body || typeof message.body !== "object")
5
+ return;
6
+ const body = message.body;
7
+ const invitation = body.kind === "contact" && ["friendRequest", "requestFriend"].includes(String(body.action));
8
+ if ((!invitation && body.kind !== "message") || (invitation ? body.payload?.appId : body.content?.appId) !== scope.appId || chatTeamId(body) !== scope.teamId)
9
+ return;
10
+ const threadKind = invitation ? "dm" : body.threadKind;
11
+ if (threadKind !== "dm" && threadKind !== "group")
12
+ return;
13
+ if (threadKind === "dm" && message.from !== scope.profileId && message.to !== scope.profileId)
14
+ return;
15
+ const threadId = threadKind === "dm" ? dmThreadId(scope.appId, message.from, message.to, scope.teamId) : groupThreadId(scope.appId, message.to, scope.teamId);
16
+ return { appId: scope.appId, teamId: scope.teamId, threadId, threadKind, messageId: message.hash, id: message.hash,
17
+ timestamp: message.t, serverCreatedAt: message.t, clientCreatedAt: message.t, from: message.from, to: message.to, body: message.body, recipients: message.recipients };
18
+ }
@@ -62,6 +62,7 @@ export declare class ChatClient {
62
62
  profileId: string | undefined;
63
63
  accountId: string | undefined;
64
64
  teamId: string | undefined;
65
+ clientInstanceId: string;
65
66
  };
66
67
  watchConnection(handler: (state: ChatConnectionState) => void): () => void;
67
68
  private setConnection;
@@ -123,7 +123,7 @@ export class ChatClient {
123
123
  /** Non-secret identity used to validate a shared browser transport. */
124
124
  identityScope() {
125
125
  const identity = this.session?.identity ?? this.session?.session?.identity;
126
- return { appId: this.options.appId, profileId: this.profileIdValue ?? this.options.profileId, accountId: identity?.accountId, teamId: this.options.teamId };
126
+ return { appId: this.options.appId, profileId: this.profileIdValue ?? this.options.profileId, accountId: identity?.accountId, teamId: this.options.teamId, clientInstanceId: this.instanceId };
127
127
  }
128
128
  watchConnection(handler) {
129
129
  this.connectionHandlers.add(handler);
@@ -1,36 +1,48 @@
1
+ import { type TimelineItem } from "./messageTimeline.js";
2
+ import { type ReadingCandidate, type MessageReadingState } from "./conversationReading.js";
3
+ import type { ConversationReadMessage } from "./conversationRead.js";
4
+ import { type ReadCountsResult } from "./readCounts.js";
5
+ import type { OnlineReachEvent } from "./reach.js";
1
6
  import { type MessageReadersQuery } from "./messageReaders.js";
2
7
  import { type MessageClientOptions, type MessageSnapshot } from "./messageClient.js";
3
- import { DurableChatSender } from "./durableSender.js";
4
- import { type ReadingClientOptions } from "./readingClient.js";
8
+ import { DurableChatSender, type SendMessageInput, type DurableSenderOptions } from "./durableSender.js";
9
+ import { ReadingClient, type ReadingClientOptions } from "./readingClient.js";
5
10
  import { ChatClient, type ChatClientOptions, type ChatConnectionState } from "./chatClient.js";
6
11
  import { unreadInConversation } from "./readIndex.js";
7
12
  import type { StoredMessage } from "./messageStore.js";
8
- import type { ChatBody, ChatRuntimeMessage } from "./protocol.js";
13
+ import type { ChatRuntimeMessage } from "./protocol.js";
9
14
  export type ChatSessionOptions<T extends StoredMessage> = {
10
15
  messages: MessageClientOptions<T>;
11
16
  connection: ChatClientOptions;
12
17
  sharedConnection?: ChatClient;
18
+ activeThreadId?: string;
19
+ uploadVoice?: DurableSenderOptions["uploadVoice"];
13
20
  reading: ReadingClientOptions;
14
21
  decode: (message: ChatRuntimeMessage) => T | undefined;
22
+ readingCandidate?: (message: T) => ReadingCandidate | undefined;
15
23
  onError: (error: unknown) => void;
16
24
  };
17
25
  export type ChatThreadSnapshot<T> = {
18
26
  messages: readonly T[];
27
+ timeline: readonly TimelineItem<T>[];
19
28
  outgoing: ReturnType<DurableChatSender["snapshot"]>;
20
29
  hasEarlier: boolean;
21
30
  resetVersion: number;
22
31
  complete: boolean;
23
- unread: ReturnType<typeof unreadInConversation>;
32
+ readCounts?: ReadCountsResult;
33
+ unread: ReturnType<typeof unreadInConversation> | undefined;
34
+ messageReading: Readonly<Record<string, MessageReadingState>>;
24
35
  };
25
36
  export type ChatSummarySnapshot<T> = {
26
37
  threads: readonly {
27
38
  threadId: string;
28
39
  lastMessage?: T;
40
+ lastItem?: TimelineItem<T>;
29
41
  unread: ChatThreadSnapshot<T>["unread"];
30
42
  outgoingCount: number;
31
43
  }[];
32
- unreadMessageCount: number;
33
- badgeCount: number;
44
+ unreadMessageCount: number | undefined;
45
+ badgeCount: number | undefined;
34
46
  connection: ChatConnectionState;
35
47
  };
36
48
  /** Owns all browser message clients. Hosts subscribe to stable thread snapshots. */
@@ -40,18 +52,58 @@ export declare class ChatSession<T extends StoredMessage> {
40
52
  private connection;
41
53
  private reading;
42
54
  private readers;
55
+ private counts?;
56
+ private countsReceiver?;
57
+ private countsThread?;
58
+ private countSnapshots;
59
+ private countClosings;
60
+ private countTasks;
43
61
  private sender;
44
62
  private listeners;
45
63
  private snapshots;
64
+ private timelineDecoded;
65
+ private decodeTimeline;
66
+ private historyEdges;
67
+ private earlierReads;
46
68
  private off;
47
69
  private globalListeners;
48
70
  private globalSnapshot?;
71
+ private closedThread;
72
+ private closedSummary;
49
73
  private connectionState;
74
+ private catchUp;
75
+ private readingAllowed;
76
+ private readingListeners;
77
+ private acceptedReading;
78
+ private readingAccess?;
79
+ private readingStarted;
80
+ private readingStarting?;
81
+ private withdrawn;
82
+ private revoking?;
50
83
  private closed;
51
84
  private starting?;
85
+ private initializing?;
52
86
  private closing?;
53
87
  constructor(options: ChatSessionOptions<T>);
88
+ private scheduleCatchUp;
54
89
  start(): Promise<void>;
90
+ /** Initializes local state and background reading sync without opening a
91
+ * second transport when the host already owns calls/presence. */
92
+ initialize(): Promise<void>;
93
+ private startReading;
94
+ /** One-time migration only: never establishes trusted history coverage. */
95
+ importLegacyMessages(threadId: string, messages: T[]): Promise<void>;
96
+ getReadingSnapshot(): import("./readIndex.js").ReadIndexState | undefined;
97
+ subscribeReading(listener: () => void): () => void;
98
+ private setReadingAllowed;
99
+ private readingAuthority;
100
+ private authorizeReading;
101
+ private readingOperation;
102
+ refreshReading(): void;
103
+ synchronizeReading(cursor?: string, profileId?: string): Promise<import("./readIndex.js").ReadIndexState>;
104
+ /** Explicit text-history acknowledgement; unheard audio stays unread. */
105
+ markRead(threadId: string, messages?: readonly ConversationReadMessage[]): Promise<import("./readIndex.js").ReadIndexState>;
106
+ updateReading(transform: Parameters<ReadingClient["update"]>[0]): Promise<import("./readIndex.js").ReadIndexState>;
55
107
  private assertOpen;
56
108
  getMessages(threadId: string, options?: {
57
109
  limit?: number;
@@ -59,24 +111,36 @@ export declare class ChatSession<T extends StoredMessage> {
59
111
  timestamp: number;
60
112
  id: string;
61
113
  };
114
+ earlier?: boolean;
62
115
  all?: boolean;
116
+ refresh?: boolean;
63
117
  }): Promise<MessageSnapshot<T>>;
118
+ private rememberHistoryEdge;
119
+ private readEarlier;
64
120
  private readAll;
65
- sendMessage(threadId: string, input: {
66
- to: string;
67
- body: ChatBody;
68
- recipients?: string[];
69
- }): Promise<ChatRuntimeMessage>;
121
+ sendMessage(threadId: string, input: SendMessageInput): Promise<ChatRuntimeMessage>;
70
122
  getReaders(threadId: string, messageId: string, page?: Omit<MessageReadersQuery, "threadId" | "messageId">, signal?: AbortSignal): Promise<import("./messageReaders.js").MessageReadersPage>;
71
123
  retryMessage(messageId: string): Promise<ChatRuntimeMessage>;
72
- observeMessages(threadId: string, ids: string[], event: "visible" | "played"): Promise<import("./readIndex.js").ReadIndexState>;
124
+ observeMessages(threadId: string, ids: string[], event: "visible" | "played"): ReturnType<ReadingClient["recordVisible"]>;
125
+ observeMessages(threadId: string | undefined, ids: string[], event: "receipts", clientInstanceId?: string): Promise<void>;
126
+ /** Bridge transports can delegate social events without maintaining clients. */
127
+ handleReach(event: OnlineReachEvent): Promise<"handled" | undefined>;
128
+ private wakeCounts;
129
+ private publishCountSnapshot;
130
+ private publishCounts;
131
+ private watchCounts;
73
132
  getSnapshot(): ChatSummarySnapshot<T>;
74
133
  getSnapshot(threadId: string): ChatThreadSnapshot<T>;
134
+ private buildThreadSnapshot;
75
135
  subscribe(listener: () => void): () => void;
76
- subscribe(threadId: string, listener: () => void): () => void;
136
+ subscribe(threadId: string, listener: () => void, fields?: readonly (keyof ChatThreadSnapshot<T>)[]): () => void;
77
137
  private publishAll;
78
138
  private buildGlobal;
79
139
  private publishGlobal;
80
140
  private publish;
141
+ /** Permanently retire this identity/team session. UI withdrawal is immediate;
142
+ * completion means all durable stores have crossed the same authority epoch. */
143
+ revokeAccess(): Promise<void>;
81
144
  close(): Promise<void>;
145
+ private dispose;
82
146
  }