@pmate/chat-sdk 0.12.0-next.18 → 0.12.0-next.19
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.
|
@@ -71,6 +71,8 @@ export declare class ChatSession<T extends StoredMessage> {
|
|
|
71
71
|
/** Initializes local state and background reading sync without opening a
|
|
72
72
|
* second transport when the host already owns calls/presence. */
|
|
73
73
|
initialize(): Promise<void>;
|
|
74
|
+
/** One-time migration only: never establishes trusted history coverage. */
|
|
75
|
+
importLegacyMessages(threadId: string, messages: T[]): Promise<void>;
|
|
74
76
|
getReadingSnapshot(): import("./readIndex.js").ReadIndexState | undefined;
|
|
75
77
|
subscribeReading(listener: () => void): () => void;
|
|
76
78
|
refreshReading(): void;
|
|
@@ -117,6 +117,8 @@ export class ChatSession {
|
|
|
117
117
|
this.assertOpen();
|
|
118
118
|
})().catch(error => { this.initializing = undefined; throw error; });
|
|
119
119
|
}
|
|
120
|
+
/** One-time migration only: never establishes trusted history coverage. */
|
|
121
|
+
importLegacyMessages(threadId, messages) { this.assertOpen(); return this.messages.importLegacy(threadId, messages); }
|
|
120
122
|
getReadingSnapshot() { return this.reading.getSnapshot(); }
|
|
121
123
|
subscribeReading(listener) { this.assertOpen(); return this.reading.subscribe(listener); }
|
|
122
124
|
refreshReading() { this.assertOpen(); this.reading.refresh(); }
|
|
@@ -196,6 +198,8 @@ export class ChatSession {
|
|
|
196
198
|
break;
|
|
197
199
|
}
|
|
198
200
|
pages.push(page.messages);
|
|
201
|
+
if (!page.complete)
|
|
202
|
+
return { messages: pages.reverse().flat(), hasEarlier: true, complete: false, resetVersion: version };
|
|
199
203
|
}
|
|
200
204
|
if (restart)
|
|
201
205
|
continue;
|
|
@@ -54,6 +54,7 @@ export declare class MessageClient<T extends StoredMessage> {
|
|
|
54
54
|
private queueBytes;
|
|
55
55
|
private pendingBytes;
|
|
56
56
|
private pendingRecords;
|
|
57
|
+
private legacyImports;
|
|
57
58
|
constructor(options: MessageClientOptions<T>);
|
|
58
59
|
getSnapshot(thread: string): MessageSnapshot<T> | undefined;
|
|
59
60
|
subscribe(thread: string, listener: () => void): () => void;
|
|
@@ -63,6 +64,9 @@ export declare class MessageClient<T extends StoredMessage> {
|
|
|
63
64
|
read(thread: string, limit?: number, refresh?: boolean): Promise<MessageSnapshot<T>>;
|
|
64
65
|
/** Application connection/wake events invalidate, rather than fetching directly. */
|
|
65
66
|
invalidate(thread: string): void;
|
|
67
|
+
private fallback;
|
|
68
|
+
/** Migration batches remain separate from authoritative message coverage. */
|
|
69
|
+
importLegacy(thread: string, messages: T[]): Promise<void>;
|
|
66
70
|
private reload;
|
|
67
71
|
/** Events are visible only after their atomic batch commits. */
|
|
68
72
|
receive(thread: string, messages: T[]): Promise<void>;
|
|
@@ -75,12 +79,7 @@ export declare class MessageClient<T extends StoredMessage> {
|
|
|
75
79
|
earlier(thread: string, before: {
|
|
76
80
|
timestamp: number;
|
|
77
81
|
id: string;
|
|
78
|
-
}, limit?: number): Promise<
|
|
79
|
-
messages: T[];
|
|
80
|
-
hasEarlier: boolean;
|
|
81
|
-
resetVersion: number;
|
|
82
|
-
complete: boolean;
|
|
83
|
-
}>;
|
|
82
|
+
}, limit?: number): Promise<MessageSnapshot<T>>;
|
|
84
83
|
private fillWindow;
|
|
85
84
|
private denyView;
|
|
86
85
|
revoke(thread: string): Promise<void>;
|
|
@@ -23,6 +23,7 @@ export class MessageClient {
|
|
|
23
23
|
queueBytes;
|
|
24
24
|
pendingBytes = 0;
|
|
25
25
|
pendingRecords = 0;
|
|
26
|
+
legacyImports = 0;
|
|
26
27
|
constructor(options) {
|
|
27
28
|
this.options = options;
|
|
28
29
|
this.storage = options.storage ?? new IndexedDbMessageStore(options.scope, options.merge ?? mergeStoredMessage);
|
|
@@ -53,6 +54,11 @@ export class MessageClient {
|
|
|
53
54
|
options.onError?.(Object.assign(new Error("MESSAGE_ACCESS_REVOKED"), { status: 403 }), denied.thread);
|
|
54
55
|
})();
|
|
55
56
|
}
|
|
57
|
+
else if (event.data?.type === "legacy" && typeof event.data.thread === "string" && this.watches.has(event.data.thread)) {
|
|
58
|
+
const thread = event.data.thread;
|
|
59
|
+
void this.storage.read(thread, 1).then(page => this.reload(thread, !page.coverage?.complete)).catch(error => { if (!this.closed)
|
|
60
|
+
options.onError?.(error, thread); });
|
|
61
|
+
}
|
|
56
62
|
else if (typeof event.data === "string" && this.watches.has(event.data))
|
|
57
63
|
void this.reload(event.data).catch(error => { if (!this.closed)
|
|
58
64
|
options.onError?.(error, event.data); });
|
|
@@ -98,8 +104,11 @@ export class MessageClient {
|
|
|
98
104
|
const local = await this.storage.read(thread, limit);
|
|
99
105
|
if (refresh || !local.coverage?.complete || local.coverage.revoked || this.revoked.has(thread)) {
|
|
100
106
|
if (typeof navigator !== "undefined" && navigator.onLine === false) {
|
|
101
|
-
if (!refresh &&
|
|
102
|
-
|
|
107
|
+
if (!refresh && !local.coverage?.revoked) {
|
|
108
|
+
const fallback = await this.fallback(thread, limit);
|
|
109
|
+
if (fallback.messages.length)
|
|
110
|
+
return fallback;
|
|
111
|
+
}
|
|
103
112
|
throw new Error("MESSAGE_OFFLINE_UNAVAILABLE");
|
|
104
113
|
}
|
|
105
114
|
this.revoked.delete(thread);
|
|
@@ -107,10 +116,13 @@ export class MessageClient {
|
|
|
107
116
|
await this.synchronize(thread);
|
|
108
117
|
}
|
|
109
118
|
catch (error) {
|
|
110
|
-
if (refresh ||
|
|
119
|
+
if (refresh || [401, 403].includes(error?.status ?? 0))
|
|
120
|
+
throw error;
|
|
121
|
+
const fallback = await this.fallback(thread, limit);
|
|
122
|
+
if (!fallback.messages.length)
|
|
111
123
|
throw error;
|
|
112
124
|
this.options.onError?.(error, thread);
|
|
113
|
-
return
|
|
125
|
+
return fallback;
|
|
114
126
|
}
|
|
115
127
|
}
|
|
116
128
|
if (this.closed)
|
|
@@ -122,12 +134,11 @@ export class MessageClient {
|
|
|
122
134
|
catch (error) {
|
|
123
135
|
if ([401, 403].includes(error?.status ?? 0))
|
|
124
136
|
throw error;
|
|
125
|
-
const cached = await this.
|
|
137
|
+
const cached = await this.fallback(thread, limit);
|
|
126
138
|
if (!cached.messages.length)
|
|
127
139
|
throw error;
|
|
128
140
|
this.options.onError?.(error, thread);
|
|
129
|
-
|
|
130
|
-
return { messages: cached.messages, hasEarlier: true, complete: false, resetVersion: cached.coverage?.resetVersion ?? 0 };
|
|
141
|
+
return cached;
|
|
131
142
|
}
|
|
132
143
|
const snapshot = await this.reload(thread);
|
|
133
144
|
if (local.coverage?.complete && Date.now() - (local.coverage.syncedAt ?? 0) > (this.options.freshnessMs ?? 30_000)
|
|
@@ -144,9 +155,45 @@ export class MessageClient {
|
|
|
144
155
|
if (!this.closed && !this.closing && !this.revoked.has(thread))
|
|
145
156
|
this.syncTasks.schedule(thread, () => this.synchronize(thread));
|
|
146
157
|
}
|
|
147
|
-
async
|
|
158
|
+
async fallback(thread, limit, before) {
|
|
159
|
+
const page = await this.storage.readFallback(thread, limit, before);
|
|
160
|
+
if (!before && page.messages.length)
|
|
161
|
+
await this.reload(thread, true);
|
|
162
|
+
return { messages: page.messages, hasEarlier: true, complete: false, resetVersion: page.coverage?.resetVersion ?? 0 };
|
|
163
|
+
}
|
|
164
|
+
/** Migration batches remain separate from authoritative message coverage. */
|
|
165
|
+
importLegacy(thread, messages) {
|
|
166
|
+
if (this.closed || this.closing)
|
|
167
|
+
return Promise.reject(new Error("MESSAGE_CLIENT_UNAVAILABLE"));
|
|
168
|
+
if (messages.length > 128)
|
|
169
|
+
return Promise.reject(new Error("MESSAGE_BATCH_TOO_LARGE"));
|
|
170
|
+
if (this.legacyImports >= 4)
|
|
171
|
+
return Promise.reject(new Error("MESSAGE_STORAGE_BACKPRESSURE"));
|
|
172
|
+
let records;
|
|
173
|
+
try {
|
|
174
|
+
let bytes = 0;
|
|
175
|
+
for (const message of messages) {
|
|
176
|
+
bytes += this.messageBytes(message);
|
|
177
|
+
if (bytes > Math.min(this.transactionBytes, 1_048_576))
|
|
178
|
+
throw new Error("MESSAGE_BATCH_TOO_LARGE");
|
|
179
|
+
}
|
|
180
|
+
records = structuredClone(messages);
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
return Promise.reject(error);
|
|
184
|
+
}
|
|
185
|
+
this.legacyImports++;
|
|
186
|
+
const task = this.storage.importLegacy(thread, records).then(async () => {
|
|
187
|
+
const page = await this.storage.read(thread, 1);
|
|
188
|
+
await this.reload(thread, !page.coverage?.complete);
|
|
189
|
+
this.channel?.postMessage({ type: "legacy", thread });
|
|
190
|
+
}).finally(() => { this.legacyImports--; this.activeWrites.delete(task); });
|
|
191
|
+
this.activeWrites.add(task);
|
|
192
|
+
return task;
|
|
193
|
+
}
|
|
194
|
+
async reload(thread, fallback = false) {
|
|
148
195
|
const limit = 100;
|
|
149
|
-
const page = await this.storage.read(thread, limit);
|
|
196
|
+
const page = await (fallback ? this.storage.readFallback(thread, limit) : this.storage.read(thread, limit));
|
|
150
197
|
if (this.closed)
|
|
151
198
|
throw new Error("MESSAGE_CLIENT_CLOSED");
|
|
152
199
|
if (this.revoked.has(thread)) {
|
|
@@ -160,7 +207,7 @@ export class MessageClient {
|
|
|
160
207
|
return old && JSON.stringify(old) === JSON.stringify(message) ? old : message;
|
|
161
208
|
});
|
|
162
209
|
const old = watch?.snapshot;
|
|
163
|
-
const resetVersion = Math.max(page.coverage?.resetVersion ?? 0, this.revoked.has(thread) ? watch?.snapshot.resetVersion ?? 0 : 0), complete = !this.revoked.has(thread) && (page.coverage?.complete ?? false);
|
|
210
|
+
const resetVersion = Math.max(page.coverage?.resetVersion ?? 0, this.revoked.has(thread) ? watch?.snapshot.resetVersion ?? 0 : 0), complete = !fallback && !this.revoked.has(thread) && (page.coverage?.complete ?? false);
|
|
164
211
|
const hasEarlier = !this.revoked.has(thread) && (page.hasEarlier || Boolean(page.coverage?.nextBefore));
|
|
165
212
|
if (old && old.resetVersion === resetVersion && old.complete === complete && old.hasEarlier === hasEarlier && old.messages.length === messages.length && old.messages.every((message, index) => message === messages[index]))
|
|
166
213
|
return old;
|
|
@@ -340,9 +387,23 @@ export class MessageClient {
|
|
|
340
387
|
throw new Error("MESSAGE_CLIENT_CLOSED");
|
|
341
388
|
if (this.revoked.has(thread))
|
|
342
389
|
throw new Error("MESSAGE_ACCESS_REVOKED");
|
|
343
|
-
|
|
344
|
-
await this.
|
|
345
|
-
|
|
390
|
+
try {
|
|
391
|
+
if (!(await this.storage.read(thread, 1)).coverage?.complete) {
|
|
392
|
+
if (typeof navigator !== "undefined" && navigator.onLine === false)
|
|
393
|
+
throw new Error("MESSAGE_OFFLINE_UNAVAILABLE");
|
|
394
|
+
await this.synchronize(thread);
|
|
395
|
+
}
|
|
396
|
+
return await this.fillWindow(thread, limit, before);
|
|
397
|
+
}
|
|
398
|
+
catch (error) {
|
|
399
|
+
if ([401, 403].includes(error?.status ?? 0))
|
|
400
|
+
throw error;
|
|
401
|
+
const fallback = await this.fallback(thread, limit, before);
|
|
402
|
+
if (!fallback.messages.length)
|
|
403
|
+
throw error;
|
|
404
|
+
this.options.onError?.(error, thread);
|
|
405
|
+
return fallback;
|
|
406
|
+
}
|
|
346
407
|
}
|
|
347
408
|
async fillWindow(thread, limit, before) {
|
|
348
409
|
let page = await this.storage.read(thread, limit, before);
|
|
@@ -30,6 +30,7 @@ export declare class IndexedDbMessageStore<T extends StoredMessage> {
|
|
|
30
30
|
private merge;
|
|
31
31
|
private db;
|
|
32
32
|
private rows;
|
|
33
|
+
private legacy;
|
|
33
34
|
private coverage;
|
|
34
35
|
private key;
|
|
35
36
|
constructor(scope: MessageScope, merge: (previous: T | undefined, incoming: T) => T, options?: {
|
|
@@ -45,6 +46,17 @@ export declare class IndexedDbMessageStore<T extends StoredMessage> {
|
|
|
45
46
|
hasEarlier: boolean;
|
|
46
47
|
coverage: CoverageRow | undefined;
|
|
47
48
|
}>;
|
|
49
|
+
/** Migration records never claim coverage or overwrite authoritative rows. */
|
|
50
|
+
importLegacy(thread: string, messages: T[]): Promise<number>;
|
|
51
|
+
/** Bounded fallback only; callers must report an incomplete window. */
|
|
52
|
+
readFallback(thread: string, limit?: number, before?: {
|
|
53
|
+
timestamp: number;
|
|
54
|
+
id: string;
|
|
55
|
+
}): Promise<{
|
|
56
|
+
messages: T[];
|
|
57
|
+
hasEarlier: boolean;
|
|
58
|
+
coverage: CoverageRow | undefined;
|
|
59
|
+
}>;
|
|
48
60
|
commit(thread: string, messages: T[], coverage?: MessageCoverage, guard?: {
|
|
49
61
|
version: number;
|
|
50
62
|
authoritative?: boolean;
|
|
@@ -6,6 +6,7 @@ export class IndexedDbMessageStore {
|
|
|
6
6
|
merge;
|
|
7
7
|
db;
|
|
8
8
|
rows;
|
|
9
|
+
legacy;
|
|
9
10
|
coverage;
|
|
10
11
|
key;
|
|
11
12
|
constructor(scope, merge, options = {}) {
|
|
@@ -13,6 +14,8 @@ export class IndexedDbMessageStore {
|
|
|
13
14
|
this.key = messageScopeKey(scope);
|
|
14
15
|
this.db = new Dexie(options.name ?? "pmate-chat-messages", { indexedDB: options.indexedDB ?? globalThis.indexedDB, IDBKeyRange: globalThis.IDBKeyRange });
|
|
15
16
|
this.db.version(1).stores({ messages: "[scope+thread+id],[scope+thread+time+id],[scope+thread],scope", coverage: "[scope+thread],scope" });
|
|
17
|
+
this.db.version(2).stores({ legacyMessages: "[scope+thread+id],[scope+thread+time+id],[scope+thread],scope" });
|
|
18
|
+
this.legacy = this.db.table("legacyMessages");
|
|
16
19
|
this.rows = this.db.table("messages");
|
|
17
20
|
this.coverage = this.db.table("coverage");
|
|
18
21
|
this.db.on("versionchange", () => { this.db.close({ disableAutoOpen: false }); return false; });
|
|
@@ -27,32 +30,87 @@ export class IndexedDbMessageStore {
|
|
|
27
30
|
return { messages: records.slice(0, limit).reverse().map(row => row.value), hasEarlier: records.length > limit, coverage: await this.coverage.get([this.key, thread]) };
|
|
28
31
|
});
|
|
29
32
|
}
|
|
33
|
+
/** Migration records never claim coverage or overwrite authoritative rows. */
|
|
34
|
+
async importLegacy(thread, messages) {
|
|
35
|
+
if (messages.length > 128)
|
|
36
|
+
throw new Error("MESSAGE_BATCH_TOO_LARGE");
|
|
37
|
+
let bytes = 0;
|
|
38
|
+
for (const message of messages) {
|
|
39
|
+
if (message.threadId !== thread || !message.id || !Number.isFinite(message.timestamp))
|
|
40
|
+
throw new Error("MESSAGE_SCOPE_MISMATCH");
|
|
41
|
+
bytes += new TextEncoder().encode(JSON.stringify(message)).byteLength;
|
|
42
|
+
if (bytes > 1_048_576)
|
|
43
|
+
throw new Error("MESSAGE_BATCH_TOO_LARGE");
|
|
44
|
+
}
|
|
45
|
+
return this.db.transaction("rw", this.rows, this.legacy, this.coverage, async () => {
|
|
46
|
+
const coverage = await this.coverage.get([this.key, thread]);
|
|
47
|
+
if (coverage?.revoked)
|
|
48
|
+
throw new Error("MESSAGE_ACCESS_REVOKED");
|
|
49
|
+
if ((coverage?.resetVersion ?? 0) > 0)
|
|
50
|
+
throw new Error("MESSAGE_PROJECTION_CHANGED");
|
|
51
|
+
const ids = [...new Set(messages.map(message => message.id))], keys = ids.map(id => [this.key, thread, id]);
|
|
52
|
+
const authoritative = await this.rows.bulkGet(keys), previous = await this.legacy.bulkGet(keys);
|
|
53
|
+
const known = new Set(authoritative.flatMap(row => row ? [row.id] : []));
|
|
54
|
+
const latest = new Map(previous.flatMap(row => row ? [[row.id, row.value]] : [])), changed = new Map();
|
|
55
|
+
for (const message of messages) {
|
|
56
|
+
if (known.has(message.id))
|
|
57
|
+
continue;
|
|
58
|
+
const old = latest.get(message.id), value = this.merge(old, message);
|
|
59
|
+
if (value.id !== message.id || value.threadId !== thread || !Number.isFinite(value.timestamp))
|
|
60
|
+
throw new Error("MESSAGE_SCOPE_MISMATCH");
|
|
61
|
+
latest.set(message.id, value);
|
|
62
|
+
if (JSON.stringify(old) !== JSON.stringify(value))
|
|
63
|
+
changed.set(message.id, { scope: this.key, thread, id: value.id, time: value.timestamp, value });
|
|
64
|
+
}
|
|
65
|
+
if (changed.size)
|
|
66
|
+
await this.legacy.bulkPut([...changed.values()]);
|
|
67
|
+
return changed.size;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/** Bounded fallback only; callers must report an incomplete window. */
|
|
71
|
+
async readFallback(thread, limit = 100, before) {
|
|
72
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1000)
|
|
73
|
+
throw new Error("INVALID_MESSAGE_LIMIT");
|
|
74
|
+
return this.db.transaction("r", this.rows, this.legacy, this.coverage, async () => {
|
|
75
|
+
const coverage = await this.coverage.get([this.key, thread]);
|
|
76
|
+
if (coverage?.revoked)
|
|
77
|
+
return { messages: [], hasEarlier: false, coverage };
|
|
78
|
+
const read = (table) => table.where("[scope+thread+time+id]").between([this.key, thread, Dexie.minKey, Dexie.minKey], [this.key, thread, before?.timestamp ?? Dexie.maxKey, before?.id ?? Dexie.maxKey], true, !before).reverse().limit(limit + 1).toArray();
|
|
79
|
+
const [canonical, legacy] = await Promise.all([read(this.rows), read(this.legacy)]);
|
|
80
|
+
const rows = [...new Map([...legacy, ...canonical].map(row => [row.id, row])).values()].sort((a, b) => a.time - b.time || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
81
|
+
return { messages: rows.slice(-limit).map(row => row.value), hasEarlier: rows.length > limit, coverage };
|
|
82
|
+
});
|
|
83
|
+
}
|
|
30
84
|
async commit(thread, messages, coverage, guard) {
|
|
31
85
|
for (const message of messages)
|
|
32
86
|
if (message.threadId !== thread || !message.id || !Number.isFinite(message.timestamp))
|
|
33
87
|
throw new Error("MESSAGE_SCOPE_MISMATCH");
|
|
34
88
|
if (messages.length > 1000)
|
|
35
89
|
throw new Error("MESSAGE_BATCH_TOO_LARGE");
|
|
36
|
-
return this.db.transaction("rw", this.rows, this.coverage, async () => {
|
|
90
|
+
return this.db.transaction("rw", this.rows, this.legacy, this.coverage, async () => {
|
|
37
91
|
const current = await this.coverage.get([this.key, thread]);
|
|
38
92
|
if (guard && (current?.resetVersion ?? 0) !== guard.version)
|
|
39
93
|
throw new Error("MESSAGE_PROJECTION_CHANGED");
|
|
40
94
|
if (guard && current?.revoked && !guard.authoritative)
|
|
41
95
|
throw new Error("MESSAGE_ACCESS_REVOKED");
|
|
42
96
|
const ids = [...new Set(messages.map(message => message.id))];
|
|
43
|
-
const
|
|
44
|
-
const
|
|
97
|
+
const keys = ids.map(id => [this.key, thread, id]);
|
|
98
|
+
const previous = await this.rows.bulkGet(keys), legacy = await this.legacy.bulkGet(keys);
|
|
99
|
+
const canonicalIds = new Set(previous.flatMap(row => row ? [row.id] : []));
|
|
100
|
+
const latest = new Map([...legacy, ...previous].flatMap(row => row ? [[row.id, row.value]] : []));
|
|
45
101
|
const changed = new Map();
|
|
46
102
|
for (const message of messages) {
|
|
47
103
|
const old = latest.get(message.id), value = this.merge(old, message);
|
|
48
104
|
if (value.id !== message.id || value.threadId !== thread || !Number.isFinite(value.timestamp))
|
|
49
105
|
throw new Error("MESSAGE_SCOPE_MISMATCH");
|
|
50
106
|
latest.set(message.id, value);
|
|
51
|
-
if (JSON.stringify(old) !== JSON.stringify(value))
|
|
107
|
+
if (!canonicalIds.has(message.id) || JSON.stringify(old) !== JSON.stringify(value))
|
|
52
108
|
changed.set(message.id, { scope: this.key, thread, id: value.id, time: value.timestamp, value });
|
|
53
109
|
}
|
|
54
110
|
if (changed.size)
|
|
55
111
|
await this.rows.bulkPut([...changed.values()]);
|
|
112
|
+
if (ids.length)
|
|
113
|
+
await this.legacy.bulkDelete(ids.map(id => [this.key, thread, id]));
|
|
56
114
|
if (coverage) {
|
|
57
115
|
const old = await this.coverage.get([this.key, thread]), next = { ...old, ...coverage, scope: this.key, thread };
|
|
58
116
|
if (JSON.stringify(old) !== JSON.stringify(next))
|
|
@@ -66,22 +124,25 @@ export class IndexedDbMessageStore {
|
|
|
66
124
|
}
|
|
67
125
|
/** A durable epoch prevents another tab's pre-reset request from restoring rows. */
|
|
68
126
|
async resetThread(thread, revoked = false) {
|
|
69
|
-
return this.db.transaction("rw", this.rows, this.coverage, async () => {
|
|
127
|
+
return this.db.transaction("rw", this.rows, this.legacy, this.coverage, async () => {
|
|
70
128
|
const old = await this.coverage.get([this.key, thread]);
|
|
71
129
|
const resetVersion = (old?.resetVersion ?? 0) + 1;
|
|
72
130
|
await this.rows.where("[scope+thread]").equals([this.key, thread]).delete();
|
|
131
|
+
await this.legacy.where("[scope+thread]").equals([this.key, thread]).delete();
|
|
73
132
|
await this.coverage.put({ scope: this.key, thread, complete: false, resetVersion, revoked });
|
|
74
133
|
return resetVersion;
|
|
75
134
|
});
|
|
76
135
|
}
|
|
77
136
|
async removeThread(thread) {
|
|
78
|
-
await this.db.transaction("rw", this.rows, this.coverage, async () => {
|
|
137
|
+
await this.db.transaction("rw", this.rows, this.legacy, this.coverage, async () => {
|
|
79
138
|
await this.rows.where("[scope+thread]").equals([this.key, thread]).delete();
|
|
139
|
+
await this.legacy.where("[scope+thread]").equals([this.key, thread]).delete();
|
|
80
140
|
await this.coverage.delete([this.key, thread]);
|
|
81
141
|
});
|
|
82
142
|
}
|
|
83
143
|
async clear() {
|
|
84
|
-
await this.db.transaction("rw", this.rows, this.coverage, async () => {
|
|
144
|
+
await this.db.transaction("rw", this.rows, this.legacy, this.coverage, async () => {
|
|
145
|
+
await this.legacy.where("scope").equals(this.key).delete();
|
|
85
146
|
await this.rows.where("scope").equals(this.key).delete();
|
|
86
147
|
await this.coverage.where("scope").equals(this.key).delete();
|
|
87
148
|
});
|
package/package.json
CHANGED