@pmate/chat-sdk 0.12.0-next.67 → 0.12.0-next.69
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 +73 -65
- package/lib/advanced.d.ts +10 -0
- package/lib/advanced.js +10 -0
- package/lib/chat.d.ts +4 -130
- package/lib/chat.js +9 -217
- package/lib/chatContract.d.ts +144 -0
- package/lib/chatContract.js +1 -0
- package/lib/chatError.d.ts +11 -0
- package/lib/chatError.js +13 -0
- package/lib/chatTypes.d.ts +119 -0
- package/lib/chatTypes.js +1 -0
- package/lib/credentials.d.ts +26 -0
- package/lib/credentials.js +109 -0
- package/lib/framework.d.ts +11 -0
- package/lib/framework.js +12 -0
- package/lib/history.d.ts +4 -0
- package/lib/history.js +8 -0
- package/lib/hooks/useChatSnapshot.js +4 -3
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/internal/chatEngine.d.ts +131 -0
- package/lib/internal/chatEngine.js +280 -0
- package/lib/internal/chatRegistry.d.ts +4 -0
- package/lib/internal/chatRegistry.js +4 -0
- package/lib/internal/createPublicChat.d.ts +4 -0
- package/lib/internal/createPublicChat.js +52 -0
- package/lib/messageView.d.ts +2 -33
- package/lib/messageView.js +1 -1
- package/lib/runtime/chatClient.d.ts +1 -0
- package/lib/runtime/chatClient.js +7 -2
- package/lib/runtime/chatError.d.ts +2 -11
- package/lib/runtime/chatError.js +3 -14
- package/lib/runtime/chatSession.d.ts +1 -0
- package/lib/runtime/chatSession.js +14 -1
- package/lib/runtime/createChat.d.ts +6 -2
- package/lib/runtime/createChat.js +17 -7
- package/lib/runtime/messageClient.d.ts +5 -0
- package/lib/runtime/messageClient.js +32 -0
- package/lib/runtime/messageStore.d.ts +2 -0
- package/lib/runtime/messageStore.js +12 -0
- package/lib/runtime/readCounts.js +1 -1
- package/lib/runtime/readSync.js +1 -1
- package/lib/runtime/socialReadClient.d.ts +1 -1
- package/lib/runtime/socialReadClient.js +4 -4
- package/package.json +16 -1
|
@@ -100,6 +100,38 @@ export class MessageClient {
|
|
|
100
100
|
const current = await this.storage.scopeAccess();
|
|
101
101
|
return { version: current.version, allowed: !this.scopeDenied && !current.revoked && current.version === this.scopeVersion };
|
|
102
102
|
}
|
|
103
|
+
authorizingScope;
|
|
104
|
+
/** Recovery belongs to the SDK; withdrawn instances never regain authority. */
|
|
105
|
+
authorizeScope() {
|
|
106
|
+
return this.authorizingScope ??= (async () => {
|
|
107
|
+
await this.ensureScope();
|
|
108
|
+
if (this.closed || this.closing || this.scopeDenied)
|
|
109
|
+
throw accessRevoked();
|
|
110
|
+
const access = await this.scopeAccess();
|
|
111
|
+
if (access.allowed)
|
|
112
|
+
return;
|
|
113
|
+
if (access.version !== this.scopeVersion) {
|
|
114
|
+
this.denyScope();
|
|
115
|
+
throw accessRevoked();
|
|
116
|
+
}
|
|
117
|
+
if (!this.options.transport.authorizeScope)
|
|
118
|
+
throw accessRevoked();
|
|
119
|
+
try {
|
|
120
|
+
await this.options.transport.authorizeScope(this.abort.signal);
|
|
121
|
+
if (this.closed || this.closing || this.scopeDenied)
|
|
122
|
+
throw accessRevoked();
|
|
123
|
+
await this.storage.authorizeScope(this.scopeVersion);
|
|
124
|
+
if (this.closed || this.closing || this.scopeDenied)
|
|
125
|
+
throw accessRevoked();
|
|
126
|
+
this.scopeAllowed = true;
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if ([401, 403].includes(error?.status ?? 0) || String(error).includes("MESSAGE_SCOPE_CHANGED"))
|
|
130
|
+
this.denyScope();
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
})().finally(() => { this.authorizingScope = undefined; });
|
|
134
|
+
}
|
|
103
135
|
getSnapshot(thread) { return this.watches.get(thread)?.snapshot; }
|
|
104
136
|
/** Exact authoritative row only; migration-only rows never prove delivery. */
|
|
105
137
|
async cachedMessage(thread, id) {
|
|
@@ -82,6 +82,8 @@ export declare class IndexedDbMessageStore<T extends StoredMessage> {
|
|
|
82
82
|
private finishPage;
|
|
83
83
|
windows(): Promise<CoverageRow[]>;
|
|
84
84
|
scopeAccess(): Promise<MessageScopeAccess>;
|
|
85
|
+
/** Only fresh, server-confirmed authorization may lift this durable tombstone. */
|
|
86
|
+
authorizeScope(expectedVersion: number): Promise<void>;
|
|
85
87
|
private scopedCoverage;
|
|
86
88
|
private clearScopeRows;
|
|
87
89
|
/** Persist the denial first; failed cleanup must never make cached rows readable. */
|
|
@@ -230,6 +230,18 @@ export class IndexedDbMessageStore {
|
|
|
230
230
|
throw new Error("INVALID_MESSAGE_SCOPE_ACCESS");
|
|
231
231
|
return access;
|
|
232
232
|
}
|
|
233
|
+
/** Only fresh, server-confirmed authorization may lift this durable tombstone. */
|
|
234
|
+
async authorizeScope(expectedVersion) {
|
|
235
|
+
await this.db.transaction("rw", [this.rows, this.legacy, this.coverage, this.accessRows, this.controls], async () => {
|
|
236
|
+
const access = await this.scopeAccess();
|
|
237
|
+
if (access.version !== expectedVersion)
|
|
238
|
+
throw new Error("MESSAGE_SCOPE_CHANGED");
|
|
239
|
+
if (!access.revoked)
|
|
240
|
+
return;
|
|
241
|
+
await this.clearScopeRows();
|
|
242
|
+
await this.accessRows.put({ ...access, revoked: false });
|
|
243
|
+
});
|
|
244
|
+
}
|
|
233
245
|
scopedCoverage(coverage, access) {
|
|
234
246
|
return access.version ? { ...coverage, complete: coverage?.complete ?? false, scopeVersion: access.version } : coverage;
|
|
235
247
|
}
|
|
@@ -51,7 +51,7 @@ export class ReadCountsClient {
|
|
|
51
51
|
const scopeVersion = this.options.access ? await assertResponseCacheAccess(this.options.access) : undefined;
|
|
52
52
|
const response = await (this.options.fetch ?? fetch)(`${this.options.hubHttpUrl.replace(/\/$/, "")}${path}`, { method: "POST", headers: { authorization: `Bearer ${this.options.token}`, "content-type": "application/json" }, body: JSON.stringify(body), signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(8000)]) : AbortSignal.timeout(8000), redirect: "error" });
|
|
53
53
|
if (!response.ok)
|
|
54
|
-
throw new Error(`READ_COUNTS_HTTP_${response.status}`);
|
|
54
|
+
throw Object.assign(new Error(`READ_COUNTS_HTTP_${response.status}`), { status: response.status });
|
|
55
55
|
const envelope = await response.json();
|
|
56
56
|
if (envelope.success !== true)
|
|
57
57
|
throw new Error("INVALID_READ_COUNTS_RESPONSE");
|
package/lib/runtime/readSync.js
CHANGED
|
@@ -40,7 +40,7 @@ export class ReadSyncClient {
|
|
|
40
40
|
...init, signal: timeout.signal, headers: { ...init.headers, authorization: `Bearer ${this.options.sessionToken}` },
|
|
41
41
|
});
|
|
42
42
|
if (!response.ok)
|
|
43
|
-
throw new Error(`READ_SYNC_HTTP_${response.status}`);
|
|
43
|
+
throw Object.assign(new Error(`READ_SYNC_HTTP_${response.status}`), { status: response.status });
|
|
44
44
|
const envelope = await response.json();
|
|
45
45
|
if (envelope.success !== true || envelope.data === undefined)
|
|
46
46
|
throw new Error("INVALID_READ_SYNC_RESPONSE");
|
|
@@ -46,7 +46,7 @@ export declare class SocialReadClient {
|
|
|
46
46
|
private readonly options;
|
|
47
47
|
constructor(options: SocialReadClientOptions);
|
|
48
48
|
/** Without teamId includes current Teams and unassigned contact metadata for migration. */
|
|
49
|
-
contacts(teamId?: string): Promise<ChatReaderContact[]>;
|
|
49
|
+
contacts(teamId?: string, signal?: AbortSignal): Promise<ChatReaderContact[]>;
|
|
50
50
|
unread(teamId?: string): Promise<ChatReaderUnread[]>;
|
|
51
51
|
messages(input: {
|
|
52
52
|
threadId: string;
|
|
@@ -14,8 +14,8 @@ export class SocialReadClient {
|
|
|
14
14
|
options;
|
|
15
15
|
constructor(options) { this.options = { ...options }; }
|
|
16
16
|
/** Without teamId includes current Teams and unassigned contact metadata for migration. */
|
|
17
|
-
contacts(teamId) {
|
|
18
|
-
return this.list("contacts", this.teamQuery(teamId));
|
|
17
|
+
contacts(teamId, signal) {
|
|
18
|
+
return this.list("contacts", this.teamQuery(teamId), signal);
|
|
19
19
|
}
|
|
20
20
|
unread(teamId) {
|
|
21
21
|
return this.list("unread", this.teamQuery(teamId));
|
|
@@ -58,8 +58,8 @@ export class SocialReadClient {
|
|
|
58
58
|
if (this.options.appId === "pmate:merry" && !teamId)
|
|
59
59
|
throw new SocialReadError(400, "TEAM_ID_REQUIRED");
|
|
60
60
|
}
|
|
61
|
-
async list(action, query) {
|
|
62
|
-
const value = await this.request(action, query);
|
|
61
|
+
async list(action, query, signal) {
|
|
62
|
+
const value = await this.request(action, query, signal);
|
|
63
63
|
if (!Array.isArray(value))
|
|
64
64
|
throw new SocialReadError(502, "INVALID_SOCIAL_READ_RESPONSE");
|
|
65
65
|
return value;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pmate/chat-sdk",
|
|
3
|
-
"version": "0.12.0-next.
|
|
3
|
+
"version": "0.12.0-next.69",
|
|
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",
|
|
@@ -33,6 +33,21 @@
|
|
|
33
33
|
"types": "./lib/chat.d.ts",
|
|
34
34
|
"import": "./lib/chat.js",
|
|
35
35
|
"default": "./lib/chat.js"
|
|
36
|
+
},
|
|
37
|
+
"./advanced": {
|
|
38
|
+
"types": "./lib/advanced.d.ts",
|
|
39
|
+
"import": "./lib/advanced.js",
|
|
40
|
+
"default": "./lib/advanced.js"
|
|
41
|
+
},
|
|
42
|
+
"./framework": {
|
|
43
|
+
"types": "./lib/framework.d.ts",
|
|
44
|
+
"import": "./lib/framework.js",
|
|
45
|
+
"default": "./lib/framework.js"
|
|
46
|
+
},
|
|
47
|
+
"./history": {
|
|
48
|
+
"types": "./lib/history.d.ts",
|
|
49
|
+
"import": "./lib/history.js",
|
|
50
|
+
"default": "./lib/history.js"
|
|
36
51
|
}
|
|
37
52
|
},
|
|
38
53
|
"publishConfig": {
|