@agentshouse/mdruntime 0.1.0-ca9afacd5aebd → 0.1.0-caee7332d2235
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/AGENTS.md +271 -290
- package/README.md +8 -8
- package/dist/adapter.d.ts +52 -10
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attempt.d.ts.map +1 -1
- package/dist/attempt.js +14 -1
- package/dist/bootstrap.d.ts +3 -2
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +33 -7
- package/dist/change-set.d.ts +7 -2
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +41 -10
- package/dist/chat/acts.d.ts +3 -4
- package/dist/chat/acts.d.ts.map +1 -1
- package/dist/chat/acts.js +10 -10
- package/dist/chat/earlier.d.ts +10 -0
- package/dist/chat/earlier.d.ts.map +1 -0
- package/dist/chat/earlier.js +54 -0
- package/dist/chat/kit.d.ts +2 -1
- package/dist/chat/kit.d.ts.map +1 -1
- package/dist/chat/kit.js +3 -3
- package/dist/chat/list.d.ts +8 -1
- package/dist/chat/list.d.ts.map +1 -1
- package/dist/chat/list.js +48 -8
- package/dist/chat/markdown.d.ts +3 -1
- package/dist/chat/markdown.d.ts.map +1 -1
- package/dist/chat/markdown.js +85 -26
- package/dist/chat/message.d.ts.map +1 -1
- package/dist/chat/message.js +3 -3
- package/dist/chat/messages.d.ts +1 -0
- package/dist/chat/messages.d.ts.map +1 -1
- package/dist/chat/messages.js +35 -15
- package/dist/chat/pieces.d.ts +62 -0
- package/dist/chat/pieces.d.ts.map +1 -0
- package/dist/chat/pieces.js +1110 -0
- package/dist/chat/provider.d.ts +1 -1
- package/dist/chat/provider.d.ts.map +1 -1
- package/dist/chat/provider.js +65 -80
- package/dist/chat/question.d.ts +1 -1
- package/dist/chat/question.d.ts.map +1 -1
- package/dist/chat/question.js +7 -18
- package/dist/chat/ready.d.ts +1 -0
- package/dist/chat/ready.d.ts.map +1 -1
- package/dist/chat/ready.js +4 -4
- package/dist/chat/steady.d.ts +3 -0
- package/dist/chat/steady.d.ts.map +1 -0
- package/dist/chat/steady.js +26 -0
- package/dist/chat/style.js +4 -4
- package/dist/chat.d.ts +1 -1
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +1 -1
- package/dist/client.d.ts +17 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +19 -6
- package/dist/collection.d.ts +20 -5
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +309 -33
- package/dist/connection.d.ts +31 -9
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1602 -386
- package/dist/conversation.d.ts +20 -7
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +474 -163
- package/dist/durability.d.ts +7 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/entry.d.ts +17 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +157 -0
- package/dist/fence.d.ts +6 -0
- package/dist/fence.d.ts.map +1 -0
- package/dist/fence.js +10 -0
- package/dist/host.d.ts +15 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +94 -12
- package/dist/identity.d.ts +2 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +6 -0
- package/dist/index.d.ts +10 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/memory.d.ts +8 -2
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +33 -6
- package/dist/model.d.ts +4 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +10 -1
- package/dist/observation.d.ts +7 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +29 -12
- package/dist/outcome.d.ts +12 -2
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +4 -0
- package/dist/overlay.d.ts +5 -3
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +203 -159
- package/dist/query.d.ts +56 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +36 -7
- package/dist/reach.d.ts +6 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +30 -5
- package/dist/react.d.ts +8 -6
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +7 -17
- package/dist/read-side.d.ts +16 -7
- package/dist/read-side.d.ts.map +1 -1
- package/dist/read-side.js +486 -100
- package/dist/readiness.d.ts +53 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +33 -0
- package/dist/reads.d.ts +11 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +98 -0
- package/dist/recovery.d.ts +25 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +197 -0
- package/dist/refresh.d.ts +4 -4
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +44 -18
- package/dist/replica.d.ts +11 -5
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +111 -56
- package/dist/room-draft.d.ts +13 -1
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +11 -12
- package/dist/rows.d.ts +6 -13
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +10 -13
- package/dist/save.d.ts +10 -4
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +216 -108
- package/dist/search.d.ts +49 -6
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +192 -47
- package/dist/seed.d.ts +16 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +75 -0
- package/dist/shared.d.ts +15 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +398 -0
- package/dist/status.d.ts +4 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/store.d.ts +61 -12
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +580 -268
- package/dist/turn.d.ts +9 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +64 -0
- package/dist/windows.d.ts +26 -0
- package/dist/windows.d.ts.map +1 -0
- package/dist/windows.js +93 -0
- package/dist/wire.d.ts +152 -32
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +52 -31
- package/package.json +12 -8
package/dist/store.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { sameBinary, sameChange } from './overlay.js';
|
|
2
|
+
import { differences } from './shared.js';
|
|
3
|
+
import { inUnits, ROW_WEIGHT, stepped } from './turn.js';
|
|
4
|
+
import { precedes } from './windows.js';
|
|
2
5
|
const DATABASE = 'mdruntime';
|
|
3
6
|
const AUTHORED = 'mdruntime-authored';
|
|
7
|
+
const FENCES = 'fences';
|
|
4
8
|
const ROOMS = 'rooms';
|
|
5
9
|
const DOCUMENTS = 'documents';
|
|
6
10
|
const BINARY_FILES = 'binaryFiles';
|
|
7
11
|
const TRANSFERS = 'transfers';
|
|
12
|
+
const RECEIVED = 'received';
|
|
13
|
+
const RECEIVED_PARTS = 'receivedParts';
|
|
8
14
|
const PENDING_CHANGES = 'pendingChanges';
|
|
9
15
|
const DECLARATIONS = 'declarations';
|
|
10
16
|
const ROOM_DRAFTS = 'roomDrafts';
|
|
@@ -12,32 +18,97 @@ const PRESENTATION = 'presentation';
|
|
|
12
18
|
const CONVERSATIONS = 'conversations';
|
|
13
19
|
const MESSAGES = 'messages';
|
|
14
20
|
const CHAT_DRAFTS = 'chatDrafts';
|
|
21
|
+
const CALLS = 'calls';
|
|
15
22
|
const QUEUES = 'queues';
|
|
16
23
|
const FACTS = 'facts';
|
|
17
24
|
const CURRENT = 'current';
|
|
18
25
|
const MEMBER = 'member';
|
|
26
|
+
const TARGETS = 'targets';
|
|
19
27
|
const GENERATION = 'generation';
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
const HOLDER = 'holder';
|
|
29
|
+
const BINDING = 'binding';
|
|
30
|
+
export const STORE_SCHEMA = 25;
|
|
31
|
+
const REPLICA_SHAPE = 23;
|
|
32
|
+
export const AUTHORED_SCHEMA = 3;
|
|
22
33
|
const AUTHORED_STORES = [
|
|
23
34
|
[QUEUES, { keyPath: 'locator' }],
|
|
24
35
|
[PENDING_CHANGES, { keyPath: ['locator', 'id'] }],
|
|
25
36
|
[ROOM_DRAFTS, { keyPath: ['locator', 'path'] }],
|
|
26
37
|
[CHAT_DRAFTS, { keyPath: 'conversation' }],
|
|
38
|
+
[CALLS, { keyPath: 'operation' }],
|
|
27
39
|
[FACTS, undefined],
|
|
40
|
+
[FENCES, { keyPath: 'locator' }],
|
|
41
|
+
];
|
|
42
|
+
const REPLICA_STORES = [
|
|
43
|
+
[ROOMS, { keyPath: 'locator' }],
|
|
44
|
+
[DOCUMENTS, { keyPath: ['locator', 'transfer', 'path'] }],
|
|
45
|
+
[BINARY_FILES, { keyPath: ['locator', 'transfer', 'path'] }],
|
|
46
|
+
[TRANSFERS, { keyPath: 'locator' }],
|
|
47
|
+
[RECEIVED, { keyPath: 'key' }],
|
|
48
|
+
[RECEIVED_PARTS, { keyPath: ['key', 'index'] }],
|
|
49
|
+
[DECLARATIONS, undefined],
|
|
50
|
+
[PRESENTATION, { keyPath: 'app' }],
|
|
51
|
+
[CONVERSATIONS, { keyPath: 'conversation' }],
|
|
52
|
+
[MESSAGES, { keyPath: ['conversation', 'identity'] }],
|
|
28
53
|
];
|
|
29
54
|
const READ_BATCH = 256;
|
|
55
|
+
const MESSAGE_INDEXES = [
|
|
56
|
+
['conversation', 'conversation'],
|
|
57
|
+
['position', ['conversation', 'position']],
|
|
58
|
+
['turn', ['conversation', 'turn']],
|
|
59
|
+
];
|
|
30
60
|
function settled(request) {
|
|
31
61
|
return new Promise((resolve, reject) => {
|
|
32
62
|
request.onsuccess = () => resolve(request.result);
|
|
33
63
|
request.onerror = () => reject(request.error);
|
|
34
64
|
});
|
|
35
65
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
66
|
+
const turns = new Map();
|
|
67
|
+
function idle(database) {
|
|
68
|
+
return turns.get(database.name) ?? Promise.resolve();
|
|
69
|
+
}
|
|
70
|
+
export function transacted(database, stores, mode, work) {
|
|
71
|
+
let release = () => { };
|
|
72
|
+
const turn = new Promise((resolve) => {
|
|
73
|
+
release = resolve;
|
|
74
|
+
});
|
|
75
|
+
const before = idle(database);
|
|
76
|
+
const queued = before.then(() => turn);
|
|
77
|
+
turns.set(database.name, queued);
|
|
78
|
+
void queued.then(() => {
|
|
79
|
+
if (turns.get(database.name) === queued)
|
|
80
|
+
turns.delete(database.name);
|
|
81
|
+
});
|
|
82
|
+
return before.then(() => {
|
|
83
|
+
let transaction;
|
|
84
|
+
try {
|
|
85
|
+
transaction = database.transaction(stores, mode);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
release();
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
const committed = new Promise((resolve, reject) => {
|
|
92
|
+
transaction.oncomplete = () => {
|
|
93
|
+
release();
|
|
94
|
+
resolve();
|
|
95
|
+
};
|
|
96
|
+
transaction.onabort = () => {
|
|
97
|
+
release();
|
|
98
|
+
reject(transaction.error);
|
|
99
|
+
};
|
|
100
|
+
transaction.onerror = () => reject(transaction.error);
|
|
101
|
+
});
|
|
102
|
+
let value;
|
|
103
|
+
try {
|
|
104
|
+
value = work(transaction);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
void committed.catch(() => { });
|
|
108
|
+
transaction.abort();
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
return Promise.all([value, committed]).then(([held]) => held);
|
|
41
112
|
});
|
|
42
113
|
}
|
|
43
114
|
const EMPTY_DECLARATIONS = {
|
|
@@ -46,31 +117,33 @@ const EMPTY_DECLARATIONS = {
|
|
|
46
117
|
capabilities: [],
|
|
47
118
|
};
|
|
48
119
|
function declaration(database) {
|
|
49
|
-
|
|
50
|
-
return settled(transaction.objectStore(DECLARATIONS).get(CURRENT)).then((record) => record ?? EMPTY_DECLARATIONS);
|
|
120
|
+
return transacted(database, DECLARATIONS, 'readonly', (transaction) => settled(transaction.objectStore(DECLARATIONS).get(CURRENT))).then((record) => record ?? EMPTY_DECLARATIONS);
|
|
51
121
|
}
|
|
52
122
|
function updateDeclaration(database, update) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
123
|
+
return transacted(database, DECLARATIONS, 'readwrite', (transaction) => {
|
|
124
|
+
const store = transaction.objectStore(DECLARATIONS);
|
|
125
|
+
const prior = store.get(CURRENT);
|
|
126
|
+
prior.onsuccess = () => {
|
|
127
|
+
store.put(update(prior.result ?? EMPTY_DECLARATIONS), CURRENT);
|
|
128
|
+
};
|
|
129
|
+
});
|
|
60
130
|
}
|
|
61
131
|
function amendPending(database, locator, id, amendment) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
132
|
+
return transacted(database, PENDING_CHANGES, 'readwrite', (transaction) => {
|
|
133
|
+
const pending = transaction.objectStore(PENDING_CHANGES);
|
|
134
|
+
const request = pending.get([locator, id]);
|
|
135
|
+
request.onsuccess = () => {
|
|
136
|
+
const record = request.result;
|
|
137
|
+
if (record !== undefined)
|
|
138
|
+
pending.put({ ...record, ...amendment });
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
function accepted({ conversation: _held, ...message }) {
|
|
143
|
+
return message;
|
|
71
144
|
}
|
|
72
145
|
function roomRecord(record) {
|
|
73
|
-
const { documents, binaries, revisions, changes, written, ...room } = record;
|
|
146
|
+
const { documents, binaries, revisions, fileIds, changes, written, ...room } = record;
|
|
74
147
|
return room;
|
|
75
148
|
}
|
|
76
149
|
function chatDraftRecord(record) {
|
|
@@ -96,13 +169,15 @@ async function readTransfer(store, locator, transfer, receive) {
|
|
|
96
169
|
}
|
|
97
170
|
}
|
|
98
171
|
function emptyLayer() {
|
|
99
|
-
return { documents: {}, binaries: {}, revisions: {}, changes: {}, written: {} };
|
|
172
|
+
return { documents: {}, binaries: {}, revisions: {}, fileIds: {}, changes: {}, written: {} };
|
|
100
173
|
}
|
|
101
174
|
async function readLayer(transaction, locator, transfer) {
|
|
102
175
|
const layer = emptyLayer();
|
|
103
176
|
const head = (held) => {
|
|
104
177
|
if (held.revision !== null)
|
|
105
178
|
layer.revisions[held.path] = held.revision;
|
|
179
|
+
if (held.fileId !== null)
|
|
180
|
+
layer.fileIds[held.path] = held.fileId;
|
|
106
181
|
if (held.change !== null)
|
|
107
182
|
layer.changes[held.path] = held.change;
|
|
108
183
|
};
|
|
@@ -125,6 +200,7 @@ function documentRecord(locator, transfer, layer, path) {
|
|
|
125
200
|
path,
|
|
126
201
|
content: layer.documents[path],
|
|
127
202
|
revision: layer.revisions[path] ?? null,
|
|
203
|
+
fileId: layer.fileIds[path] ?? null,
|
|
128
204
|
change: layer.changes[path] ?? null,
|
|
129
205
|
written: layer.written[path] ?? null,
|
|
130
206
|
};
|
|
@@ -135,36 +211,49 @@ function binaryFileRecord(locator, transfer, layer, path) {
|
|
|
135
211
|
transfer,
|
|
136
212
|
path,
|
|
137
213
|
revision: layer.revisions[path] ?? null,
|
|
214
|
+
fileId: layer.fileIds[path] ?? null,
|
|
138
215
|
change: layer.changes[path] ?? null,
|
|
139
216
|
binary: layer.binaries[path],
|
|
140
217
|
};
|
|
141
218
|
}
|
|
142
|
-
function
|
|
219
|
+
function* changedIn(next, base) {
|
|
220
|
+
if (base === null)
|
|
221
|
+
return new Set([...Object.keys(next.documents), ...Object.keys(next.binaries)]);
|
|
222
|
+
return new Set([
|
|
223
|
+
...(yield* differences(base.documents, next.documents)),
|
|
224
|
+
...(yield* differences(base.revisions, next.revisions)),
|
|
225
|
+
...(yield* differences(base.fileIds, next.fileIds)),
|
|
226
|
+
...(yield* differences(base.written, next.written)),
|
|
227
|
+
...(yield* differences(base.changes, next.changes, sameChange)),
|
|
228
|
+
...(yield* differences(base.binaries, next.binaries, sameBinary)),
|
|
229
|
+
]);
|
|
230
|
+
}
|
|
231
|
+
function writeLayer(transaction, locator, transfer, layer) {
|
|
232
|
+
const { next, base, paths } = layer;
|
|
143
233
|
const documents = transaction.objectStore(DOCUMENTS);
|
|
144
|
-
for (const path of Object.keys(base?.documents ?? {})) {
|
|
145
|
-
if (next.documents[path] === undefined)
|
|
146
|
-
documents.delete([locator, transfer, path]);
|
|
147
|
-
}
|
|
148
|
-
for (const [path, content] of Object.entries(next.documents)) {
|
|
149
|
-
if (base?.documents[path] === content &&
|
|
150
|
-
base.revisions[path] === next.revisions[path] &&
|
|
151
|
-
base.written[path] === next.written[path] &&
|
|
152
|
-
sameChange(base.changes[path], next.changes[path]))
|
|
153
|
-
continue;
|
|
154
|
-
documents.put(documentRecord(locator, transfer, next, path));
|
|
155
|
-
}
|
|
156
234
|
const files = transaction.objectStore(BINARY_FILES);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
for (const path of Object.keys(next.binaries)) {
|
|
162
|
-
if (!sameBinary(base?.binaries[path], next.binaries[path]) ||
|
|
163
|
-
base?.revisions[path] !== next.revisions[path] ||
|
|
164
|
-
!sameChange(base?.changes[path], next.changes[path])) {
|
|
165
|
-
files.put(binaryFileRecord(locator, transfer, next, path));
|
|
235
|
+
let last = null;
|
|
236
|
+
return inUnits(paths, (path) => {
|
|
237
|
+
if (next.documents[path] !== undefined) {
|
|
238
|
+
last = documents.put(documentRecord(locator, transfer, next, path));
|
|
166
239
|
}
|
|
167
|
-
|
|
240
|
+
else if (base?.documents[path] !== undefined) {
|
|
241
|
+
last = documents.delete([locator, transfer, path]);
|
|
242
|
+
}
|
|
243
|
+
if (next.binaries[path] !== undefined) {
|
|
244
|
+
last = files.put(binaryFileRecord(locator, transfer, next, path));
|
|
245
|
+
}
|
|
246
|
+
else if (base?.binaries[path] !== undefined) {
|
|
247
|
+
last = files.delete([locator, transfer, path]);
|
|
248
|
+
}
|
|
249
|
+
return ROW_WEIGHT + (next.documents[path]?.length ?? 0);
|
|
250
|
+
}, () => { }, () => {
|
|
251
|
+
const request = last;
|
|
252
|
+
last = null;
|
|
253
|
+
return request === null
|
|
254
|
+
? Promise.resolve()
|
|
255
|
+
: new Promise((resolve) => request.addEventListener('success', () => resolve()));
|
|
256
|
+
});
|
|
168
257
|
}
|
|
169
258
|
function removeRoomEntries(store, locator) {
|
|
170
259
|
const keys = store.getAllKeys();
|
|
@@ -182,116 +271,206 @@ function removeDatabase(name) {
|
|
|
182
271
|
removal.onerror = () => reject(removal.error);
|
|
183
272
|
});
|
|
184
273
|
}
|
|
185
|
-
export async function
|
|
186
|
-
await
|
|
274
|
+
export async function authoredHolder() {
|
|
275
|
+
const authored = await openAuthored();
|
|
276
|
+
try {
|
|
277
|
+
const held = await transacted(authored, FACTS, 'readonly', (transaction) => settled(transaction.objectStore(FACTS).get(HOLDER)));
|
|
278
|
+
return typeof held === 'string' ? held : null;
|
|
279
|
+
}
|
|
280
|
+
finally {
|
|
281
|
+
authored.close();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
export async function authoredBy(holder) {
|
|
285
|
+
const authored = await openAuthored();
|
|
286
|
+
try {
|
|
287
|
+
let owned = true;
|
|
288
|
+
await transacted(authored, FACTS, 'readwrite', (transaction) => {
|
|
289
|
+
const facts = transaction.objectStore(FACTS);
|
|
290
|
+
const held = facts.get(HOLDER);
|
|
291
|
+
held.onsuccess = () => {
|
|
292
|
+
if (held.result === undefined)
|
|
293
|
+
facts.put(holder, HOLDER);
|
|
294
|
+
else
|
|
295
|
+
owned = held.result === holder;
|
|
296
|
+
};
|
|
297
|
+
});
|
|
298
|
+
return owned;
|
|
299
|
+
}
|
|
300
|
+
finally {
|
|
301
|
+
authored.close();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
export async function authoredBinding() {
|
|
305
|
+
const authored = await openAuthored();
|
|
306
|
+
try {
|
|
307
|
+
const held = await transacted(authored, FACTS, 'readonly', (transaction) => settled(transaction.objectStore(FACTS).get(BINDING)));
|
|
308
|
+
return typeof held === 'string' ? held : null;
|
|
309
|
+
}
|
|
310
|
+
finally {
|
|
311
|
+
authored.close();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
export async function bindAuthored(binding) {
|
|
315
|
+
const authored = await openAuthored();
|
|
316
|
+
try {
|
|
317
|
+
await transacted(authored, FACTS, 'readwrite', (transaction) => {
|
|
318
|
+
transaction.objectStore(FACTS).put(binding, BINDING);
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
finally {
|
|
322
|
+
authored.close();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
export async function removeRuntimeStore(holder) {
|
|
326
|
+
await removeDatabase(DATABASE);
|
|
327
|
+
if (holder !== undefined && (await authoredBy(holder)))
|
|
328
|
+
return true;
|
|
329
|
+
await removeDatabase(AUTHORED);
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
export function openDatabase(name, version, upgrade) {
|
|
333
|
+
return new Promise((resolve, reject) => {
|
|
334
|
+
const opening = indexedDB.open(name, version);
|
|
335
|
+
opening.onupgradeneeded = (event) => upgrade(opening, event.oldVersion);
|
|
336
|
+
opening.onsuccess = () => resolve(opening.result);
|
|
337
|
+
opening.onerror = () => {
|
|
338
|
+
if (opening.error?.name !== 'VersionError') {
|
|
339
|
+
reject(opening.error);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
removeDatabase(name)
|
|
343
|
+
.then(() => openDatabase(name, version, upgrade))
|
|
344
|
+
.then(resolve, reject);
|
|
345
|
+
};
|
|
346
|
+
});
|
|
187
347
|
}
|
|
188
348
|
function openReplica() {
|
|
189
|
-
|
|
190
|
-
opening.onupgradeneeded = () => {
|
|
349
|
+
return openDatabase(DATABASE, STORE_SCHEMA, (opening, oldVersion) => {
|
|
191
350
|
const database = opening.result;
|
|
192
|
-
|
|
193
|
-
database.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const messages =
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
351
|
+
if (oldVersion < REPLICA_SHAPE) {
|
|
352
|
+
for (const name of [...database.objectStoreNames])
|
|
353
|
+
database.deleteObjectStore(name);
|
|
354
|
+
}
|
|
355
|
+
for (const [name, parameters] of REPLICA_STORES) {
|
|
356
|
+
if (database.objectStoreNames.contains(name))
|
|
357
|
+
continue;
|
|
358
|
+
database.createObjectStore(name, parameters);
|
|
359
|
+
}
|
|
360
|
+
const messages = opening.transaction.objectStore(MESSAGES);
|
|
361
|
+
for (const [name, keyPath] of MESSAGE_INDEXES) {
|
|
362
|
+
if (!messages.indexNames.contains(name))
|
|
363
|
+
messages.createIndex(name, keyPath);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
207
366
|
}
|
|
208
367
|
function openAuthored() {
|
|
209
|
-
|
|
210
|
-
opening.onupgradeneeded = () => {
|
|
368
|
+
return openDatabase(AUTHORED, AUTHORED_SCHEMA, (opening) => {
|
|
211
369
|
const database = opening.result;
|
|
212
370
|
for (const [name, parameters] of AUTHORED_STORES) {
|
|
213
371
|
if (!database.objectStoreNames.contains(name))
|
|
214
372
|
database.createObjectStore(name, parameters);
|
|
215
373
|
}
|
|
216
|
-
};
|
|
217
|
-
return settled(opening);
|
|
374
|
+
});
|
|
218
375
|
}
|
|
219
|
-
export async function openRuntimeStore() {
|
|
376
|
+
export async function openRuntimeStore(fenced) {
|
|
220
377
|
const authored = await openAuthored();
|
|
221
378
|
const database = await openReplica().catch((error) => {
|
|
222
379
|
authored.close();
|
|
223
380
|
throw error;
|
|
224
381
|
});
|
|
225
382
|
return {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
async keepAuthored({ locator, queue, saved, roomDrafts, consumedRoomDrafts }) {
|
|
236
|
-
const transaction = authored.transaction([QUEUES, PENDING_CHANGES, ROOM_DRAFTS], 'readwrite');
|
|
237
|
-
const order = transaction.objectStore(QUEUES);
|
|
238
|
-
const pending = transaction.objectStore(PENDING_CHANGES);
|
|
239
|
-
const held = order.get(locator);
|
|
240
|
-
held.onsuccess = () => {
|
|
241
|
-
const before = new Set(held.result?.order ?? []);
|
|
242
|
-
const after = new Set(queue.map((batch) => batch.id));
|
|
243
|
-
if (queue.length === 0)
|
|
244
|
-
order.delete(locator);
|
|
245
|
-
else
|
|
246
|
-
order.put({ locator, order: [...after] });
|
|
247
|
-
for (const id of before) {
|
|
248
|
-
if (!after.has(id))
|
|
249
|
-
pending.delete([locator, id]);
|
|
383
|
+
all() {
|
|
384
|
+
return transacted(database, [ROOMS, DOCUMENTS, BINARY_FILES], 'readonly', async (transaction) => {
|
|
385
|
+
const rooms = await settled(transaction.objectStore(ROOMS).getAll());
|
|
386
|
+
const records = [];
|
|
387
|
+
for (const room of rooms.filter((held) => !fenced(held.locator))) {
|
|
388
|
+
records.push({
|
|
389
|
+
...room,
|
|
390
|
+
...(await readLayer(transaction, room.locator, room.transfer)),
|
|
391
|
+
});
|
|
250
392
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
393
|
+
return records;
|
|
394
|
+
});
|
|
395
|
+
},
|
|
396
|
+
async keepAuthored({ locator, queue, founded, saved, roomDrafts, consumedRoomDrafts }) {
|
|
397
|
+
if (fenced(locator))
|
|
398
|
+
return;
|
|
399
|
+
await transacted(authored, [QUEUES, PENDING_CHANGES, ROOM_DRAFTS], 'readwrite', (transaction) => {
|
|
400
|
+
const order = transaction.objectStore(QUEUES);
|
|
401
|
+
const pending = transaction.objectStore(PENDING_CHANGES);
|
|
402
|
+
const held = order.get(locator);
|
|
403
|
+
held.onsuccess = () => {
|
|
404
|
+
const before = new Set(held.result?.order ?? []);
|
|
405
|
+
const after = new Set(queue.map((batch) => batch.id));
|
|
406
|
+
if (queue.length === 0)
|
|
407
|
+
order.delete(locator);
|
|
408
|
+
else
|
|
409
|
+
order.put({ locator, order: [...after] });
|
|
410
|
+
for (const id of before) {
|
|
411
|
+
if (!after.has(id))
|
|
412
|
+
pending.delete([locator, id]);
|
|
261
413
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
414
|
+
for (const batch of queue) {
|
|
415
|
+
if (!before.has(batch.id)) {
|
|
416
|
+
pending.put({
|
|
417
|
+
locator,
|
|
418
|
+
id: batch.id,
|
|
419
|
+
batch,
|
|
420
|
+
drafts: saved?.get(batch.id) ?? [],
|
|
421
|
+
submission: { state: 'waiting' },
|
|
422
|
+
lost: false,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
for (const batch of founded ?? []) {
|
|
427
|
+
if (!before.has(batch.id))
|
|
428
|
+
continue;
|
|
429
|
+
const stored = pending.get([locator, batch.id]);
|
|
430
|
+
stored.onsuccess = () => {
|
|
431
|
+
const record = stored.result;
|
|
432
|
+
if (record !== undefined)
|
|
433
|
+
pending.put({ ...record, batch });
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
const draftStore = transaction.objectStore(ROOM_DRAFTS);
|
|
438
|
+
for (const path of consumedRoomDrafts ?? [])
|
|
439
|
+
draftStore.delete([locator, path]);
|
|
440
|
+
for (const record of roomDrafts ?? [])
|
|
441
|
+
draftStore.put(record);
|
|
442
|
+
});
|
|
270
443
|
},
|
|
271
444
|
async write({ next, previous, stored }) {
|
|
272
|
-
if (next === previous)
|
|
445
|
+
if (next === previous || fenced(next.locator))
|
|
273
446
|
return;
|
|
274
|
-
const transaction = database.transaction([ROOMS, DOCUMENTS, BINARY_FILES, TRANSFERS], 'readwrite');
|
|
275
|
-
transaction.objectStore(ROOMS).put(roomRecord(next));
|
|
276
447
|
const moved = previous?.transfer !== next.transfer;
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
448
|
+
const base = moved ? (stored ?? null) : previous;
|
|
449
|
+
const paths = await stepped(changedIn(next, base));
|
|
450
|
+
if (fenced(next.locator))
|
|
451
|
+
return;
|
|
452
|
+
await transacted(database, [ROOMS, DOCUMENTS, BINARY_FILES, TRANSFERS], 'readwrite', (transaction) => {
|
|
453
|
+
transaction.objectStore(ROOMS).put(roomRecord(next));
|
|
454
|
+
if (moved && previous !== null) {
|
|
455
|
+
const range = underTransfer(previous.locator, previous.transfer);
|
|
456
|
+
transaction.objectStore(DOCUMENTS).delete(range);
|
|
457
|
+
transaction.objectStore(BINARY_FILES).delete(range);
|
|
458
|
+
}
|
|
459
|
+
if (moved)
|
|
460
|
+
transaction.objectStore(TRANSFERS).delete(next.locator);
|
|
461
|
+
return writeLayer(transaction, next.locator, next.transfer, { next, base, paths });
|
|
462
|
+
});
|
|
286
463
|
},
|
|
287
464
|
async revokeModels(records) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
465
|
+
await transacted(database, ROOMS, 'readwrite', (transaction) => {
|
|
466
|
+
const rooms = transaction.objectStore(ROOMS);
|
|
467
|
+
const revoked = { status: 'unavailable', reason: 'revoked' };
|
|
468
|
+
for (const record of records) {
|
|
469
|
+
if (fenced(record.locator))
|
|
470
|
+
continue;
|
|
471
|
+
rooms.put(roomRecord({ ...record, model: revoked, selectionModel: revoked }));
|
|
472
|
+
}
|
|
473
|
+
});
|
|
295
474
|
},
|
|
296
475
|
async lastCompleted() {
|
|
297
476
|
return (await declaration(database)).lastCompleted;
|
|
@@ -313,23 +492,32 @@ export async function openRuntimeStore() {
|
|
|
313
492
|
}));
|
|
314
493
|
},
|
|
315
494
|
async member() {
|
|
316
|
-
const
|
|
317
|
-
const held = await settled(transaction.objectStore(DECLARATIONS).get(MEMBER));
|
|
495
|
+
const held = await transacted(database, DECLARATIONS, 'readonly', (transaction) => settled(transaction.objectStore(DECLARATIONS).get(MEMBER)));
|
|
318
496
|
return held ?? null;
|
|
319
497
|
},
|
|
320
498
|
async keepMember(member) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
499
|
+
await transacted(database, DECLARATIONS, 'readwrite', (transaction) => {
|
|
500
|
+
transaction.objectStore(DECLARATIONS).put(member, MEMBER);
|
|
501
|
+
});
|
|
502
|
+
},
|
|
503
|
+
async targets() {
|
|
504
|
+
const held = await transacted(database, DECLARATIONS, 'readonly', (transaction) => settled(transaction.objectStore(DECLARATIONS).get(TARGETS)));
|
|
505
|
+
return held ?? {};
|
|
506
|
+
},
|
|
507
|
+
async keepTargets(targets) {
|
|
508
|
+
await transacted(database, DECLARATIONS, 'readwrite', (transaction) => {
|
|
509
|
+
transaction.objectStore(DECLARATIONS).put(targets, TARGETS);
|
|
510
|
+
});
|
|
324
511
|
},
|
|
325
512
|
async queues() {
|
|
326
|
-
const
|
|
327
|
-
const [queues, pending] = await Promise.all([
|
|
513
|
+
const [queues, pending] = await transacted(authored, [QUEUES, PENDING_CHANGES], 'readonly', (transaction) => Promise.all([
|
|
328
514
|
settled(transaction.objectStore(QUEUES).getAll()),
|
|
329
515
|
settled(transaction.objectStore(PENDING_CHANGES).getAll()),
|
|
330
|
-
]);
|
|
516
|
+
]));
|
|
331
517
|
const byId = new Map(pending.map((record) => [`${record.locator}\0${record.id}`, record]));
|
|
332
|
-
return queues
|
|
518
|
+
return queues
|
|
519
|
+
.filter((queue) => !fenced(queue.locator))
|
|
520
|
+
.map((queue) => ({
|
|
333
521
|
locator: queue.locator,
|
|
334
522
|
pending: queue.order.map((id) => {
|
|
335
523
|
const record = byId.get(`${queue.locator}\0${id}`);
|
|
@@ -340,165 +528,289 @@ export async function openRuntimeStore() {
|
|
|
340
528
|
}),
|
|
341
529
|
}));
|
|
342
530
|
},
|
|
343
|
-
submit(locator, id, submission, lost) {
|
|
344
|
-
|
|
531
|
+
async submit(locator, id, submission, lost) {
|
|
532
|
+
if (!fenced(locator))
|
|
533
|
+
await amendPending(authored, locator, id, { submission, lost });
|
|
345
534
|
},
|
|
346
|
-
accept(locator, id, roomVersion) {
|
|
347
|
-
|
|
535
|
+
async accept(locator, id, roomVersion) {
|
|
536
|
+
if (!fenced(locator))
|
|
537
|
+
await amendPending(authored, locator, id, { accepted: roomVersion });
|
|
348
538
|
},
|
|
349
|
-
keepUpload(locator, id, upload) {
|
|
350
|
-
|
|
539
|
+
async keepUpload(locator, id, upload) {
|
|
540
|
+
if (!fenced(locator))
|
|
541
|
+
await amendPending(authored, locator, id, { upload });
|
|
351
542
|
},
|
|
352
543
|
async roomDrafts() {
|
|
353
|
-
const
|
|
354
|
-
return
|
|
544
|
+
const held = await transacted(authored, ROOM_DRAFTS, 'readonly', (transaction) => settled(transaction.objectStore(ROOM_DRAFTS).getAll()));
|
|
545
|
+
return held.filter((record) => !fenced(record.locator));
|
|
355
546
|
},
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
return settled(transaction.objectStore(PRESENTATION).getAll());
|
|
547
|
+
presentation() {
|
|
548
|
+
return transacted(database, PRESENTATION, 'readonly', (transaction) => settled(transaction.objectStore(PRESENTATION).getAll()));
|
|
359
549
|
},
|
|
360
550
|
async present(record) {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
},
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
transaction
|
|
395
|
-
|
|
396
|
-
.put({
|
|
397
|
-
|
|
398
|
-
|
|
551
|
+
await transacted(database, PRESENTATION, 'readwrite', (transaction) => {
|
|
552
|
+
transaction.objectStore(PRESENTATION).put(record);
|
|
553
|
+
});
|
|
554
|
+
},
|
|
555
|
+
conversations() {
|
|
556
|
+
return transacted(database, [CONVERSATIONS, MESSAGES], 'readonly', async (transaction) => {
|
|
557
|
+
const records = await settled(transaction.objectStore(CONVERSATIONS).getAll());
|
|
558
|
+
const held = new Map(records.map(({ conversation, read, partial }) => [
|
|
559
|
+
conversation,
|
|
560
|
+
{ conversation, read, partial: partial === true },
|
|
561
|
+
]));
|
|
562
|
+
const keys = transaction.objectStore(MESSAGES).index('conversation');
|
|
563
|
+
await new Promise((resolve, reject) => {
|
|
564
|
+
const cursor = keys.openKeyCursor(null, 'nextunique');
|
|
565
|
+
cursor.onerror = () => reject(cursor.error);
|
|
566
|
+
cursor.onsuccess = () => {
|
|
567
|
+
const at = cursor.result;
|
|
568
|
+
if (at === null) {
|
|
569
|
+
resolve();
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
const conversation = at.key;
|
|
573
|
+
if (!held.has(conversation)) {
|
|
574
|
+
held.set(conversation, { conversation, read: undefined, partial: true });
|
|
575
|
+
}
|
|
576
|
+
at.continue();
|
|
577
|
+
};
|
|
578
|
+
});
|
|
579
|
+
return [...held.values()];
|
|
580
|
+
});
|
|
581
|
+
},
|
|
582
|
+
async keepMessages({ conversation, messages, read }) {
|
|
583
|
+
await transacted(database, [CONVERSATIONS, MESSAGES], 'readwrite', (transaction) => {
|
|
584
|
+
const stored = transaction.objectStore(MESSAGES);
|
|
585
|
+
for (const message of messages) {
|
|
586
|
+
stored.put({ ...message, conversation });
|
|
587
|
+
}
|
|
588
|
+
if (read !== undefined) {
|
|
589
|
+
transaction.objectStore(CONVERSATIONS).put({
|
|
590
|
+
conversation,
|
|
591
|
+
read: read.at,
|
|
592
|
+
...(read.partial ? { partial: true } : {}),
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
},
|
|
597
|
+
messageWindow(conversation, limit, before) {
|
|
598
|
+
return transacted(database, MESSAGES, 'readonly', async (transaction) => {
|
|
599
|
+
const index = transaction.objectStore(MESSAGES).index('position');
|
|
600
|
+
const range = IDBKeyRange.bound([conversation], [conversation, before?.[0] ?? []]);
|
|
601
|
+
const latest = [];
|
|
602
|
+
let more = false;
|
|
603
|
+
await new Promise((resolve, reject) => {
|
|
604
|
+
const cursor = index.openCursor(range, 'prev');
|
|
605
|
+
cursor.onerror = () => reject(cursor.error);
|
|
606
|
+
cursor.onsuccess = () => {
|
|
607
|
+
const at = cursor.result;
|
|
608
|
+
if (at === null) {
|
|
609
|
+
resolve();
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
const record = at.value;
|
|
613
|
+
if (!precedes(record, before)) {
|
|
614
|
+
at.continue();
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
if (latest.length === limit) {
|
|
618
|
+
more = true;
|
|
619
|
+
resolve();
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
latest.push(accepted(record));
|
|
623
|
+
at.continue();
|
|
624
|
+
};
|
|
625
|
+
});
|
|
626
|
+
return { messages: latest.reverse(), more };
|
|
627
|
+
});
|
|
628
|
+
},
|
|
629
|
+
messageCount(conversation) {
|
|
630
|
+
return transacted(database, MESSAGES, 'readonly', (transaction) => settled(transaction.objectStore(MESSAGES).index('conversation').count(conversation)));
|
|
631
|
+
},
|
|
632
|
+
async messagesAfter(conversation, identity) {
|
|
633
|
+
const range = identity === null
|
|
634
|
+
? IDBKeyRange.bound([conversation], [conversation, []])
|
|
635
|
+
: IDBKeyRange.bound([conversation, identity], [conversation, []], true);
|
|
636
|
+
const held = await transacted(database, MESSAGES, 'readonly', (transaction) => settled(transaction.objectStore(MESSAGES).getAll(range, READ_BATCH)));
|
|
637
|
+
return held.map(accepted);
|
|
638
|
+
},
|
|
639
|
+
async messageIdentities(conversation) {
|
|
640
|
+
const keys = await transacted(database, MESSAGES, 'readonly', (transaction) => settled(transaction.objectStore(MESSAGES).index('conversation').getAllKeys(conversation)));
|
|
641
|
+
return keys.map((key) => key[1]);
|
|
642
|
+
},
|
|
643
|
+
async turnArrived(conversation, turn) {
|
|
644
|
+
const held = await transacted(database, MESSAGES, 'readonly', (transaction) => settled(transaction.objectStore(MESSAGES).index('turn').count([conversation, turn])));
|
|
645
|
+
return held > 0;
|
|
399
646
|
},
|
|
400
647
|
async dropConversations(conversations) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
648
|
+
await transacted(authored, CHAT_DRAFTS, 'readwrite', (drafts) => {
|
|
649
|
+
for (const conversation of conversations)
|
|
650
|
+
drafts.objectStore(CHAT_DRAFTS).delete(conversation);
|
|
651
|
+
});
|
|
652
|
+
await transacted(database, [CONVERSATIONS, MESSAGES], 'readwrite', (transaction) => {
|
|
653
|
+
const messages = transaction.objectStore(MESSAGES);
|
|
654
|
+
for (const conversation of conversations) {
|
|
655
|
+
transaction.objectStore(CONVERSATIONS).delete(conversation);
|
|
656
|
+
const keys = messages.index('conversation').getAllKeys(conversation);
|
|
657
|
+
keys.onsuccess = () => {
|
|
658
|
+
for (const key of keys.result)
|
|
659
|
+
messages.delete(key);
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
});
|
|
416
663
|
},
|
|
417
664
|
async chatDrafts() {
|
|
418
|
-
const
|
|
419
|
-
const held = await settled(transaction.objectStore(CHAT_DRAFTS).getAll());
|
|
665
|
+
const held = await transacted(authored, CHAT_DRAFTS, 'readonly', (transaction) => settled(transaction.objectStore(CHAT_DRAFTS).getAll()));
|
|
420
666
|
return held.map(chatDraftRecord);
|
|
421
667
|
},
|
|
422
|
-
async
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
668
|
+
async keepChatDrafts(records) {
|
|
669
|
+
await transacted(authored, CHAT_DRAFTS, 'readwrite', (transaction) => {
|
|
670
|
+
for (const record of records)
|
|
671
|
+
transaction.objectStore(CHAT_DRAFTS).put(record);
|
|
672
|
+
});
|
|
426
673
|
},
|
|
427
674
|
async dropChatDraft(conversation) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
675
|
+
await transacted(authored, CHAT_DRAFTS, 'readwrite', (transaction) => {
|
|
676
|
+
transaction.objectStore(CHAT_DRAFTS).delete(conversation);
|
|
677
|
+
});
|
|
678
|
+
},
|
|
679
|
+
calls() {
|
|
680
|
+
return transacted(authored, CALLS, 'readonly', (transaction) => settled(transaction.objectStore(CALLS).getAll()));
|
|
681
|
+
},
|
|
682
|
+
async keepCall(record) {
|
|
683
|
+
await transacted(authored, CALLS, 'readwrite', (transaction) => {
|
|
684
|
+
transaction.objectStore(CALLS).put(record);
|
|
685
|
+
});
|
|
686
|
+
},
|
|
687
|
+
async dropCall(operation) {
|
|
688
|
+
await transacted(authored, CALLS, 'readwrite', (transaction) => {
|
|
689
|
+
transaction.objectStore(CALLS).delete(operation);
|
|
690
|
+
});
|
|
431
691
|
},
|
|
432
692
|
async keepRoomDraft(record) {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
await
|
|
693
|
+
if (fenced(record.locator))
|
|
694
|
+
return;
|
|
695
|
+
await transacted(authored, ROOM_DRAFTS, 'readwrite', (transaction) => {
|
|
696
|
+
transaction.objectStore(ROOM_DRAFTS).put(record);
|
|
697
|
+
});
|
|
436
698
|
},
|
|
437
699
|
async discardRoomDraft(locator, path) {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
await
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
700
|
+
if (fenced(locator))
|
|
701
|
+
return;
|
|
702
|
+
await transacted(authored, ROOM_DRAFTS, 'readwrite', (transaction) => {
|
|
703
|
+
transaction.objectStore(ROOM_DRAFTS).delete([locator, path]);
|
|
704
|
+
});
|
|
705
|
+
},
|
|
706
|
+
async fences() {
|
|
707
|
+
const held = await transacted(authored, FENCES, 'readonly', (transaction) => settled(transaction.objectStore(FENCES).getAll()));
|
|
708
|
+
return held.map((fence) => fence.locator);
|
|
709
|
+
},
|
|
710
|
+
async erase(locators) {
|
|
711
|
+
await transacted(authored, [FENCES, QUEUES, PENDING_CHANGES, ROOM_DRAFTS], 'readwrite', (work) => {
|
|
712
|
+
for (const locator of locators) {
|
|
713
|
+
work.objectStore(FENCES).put({ locator });
|
|
714
|
+
work.objectStore(QUEUES).delete(locator);
|
|
715
|
+
for (const name of [PENDING_CHANGES, ROOM_DRAFTS]) {
|
|
716
|
+
removeRoomEntries(work.objectStore(name), locator);
|
|
717
|
+
}
|
|
448
718
|
}
|
|
449
|
-
}
|
|
450
|
-
await
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
}
|
|
458
|
-
await
|
|
719
|
+
});
|
|
720
|
+
await transacted(database, [ROOMS, DOCUMENTS, BINARY_FILES, TRANSFERS], 'readwrite', (transaction) => {
|
|
721
|
+
for (const locator of locators) {
|
|
722
|
+
transaction.objectStore(ROOMS).delete(locator);
|
|
723
|
+
transaction.objectStore(TRANSFERS).delete(locator);
|
|
724
|
+
transaction.objectStore(DOCUMENTS).delete(underRoom(locator));
|
|
725
|
+
transaction.objectStore(BINARY_FILES).delete(underRoom(locator));
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
await transacted(authored, FENCES, 'readwrite', (fences) => {
|
|
729
|
+
for (const locator of locators)
|
|
730
|
+
fences.objectStore(FENCES).delete(locator);
|
|
731
|
+
});
|
|
732
|
+
},
|
|
733
|
+
async discard(locator) {
|
|
734
|
+
await transacted(authored, [QUEUES, PENDING_CHANGES], 'readwrite', (transaction) => {
|
|
735
|
+
transaction.objectStore(QUEUES).delete(locator);
|
|
736
|
+
removeRoomEntries(transaction.objectStore(PENDING_CHANGES), locator);
|
|
737
|
+
});
|
|
459
738
|
},
|
|
460
739
|
async transfers() {
|
|
461
|
-
const
|
|
462
|
-
return
|
|
740
|
+
const held = await transacted(database, TRANSFERS, 'readonly', (transaction) => settled(transaction.objectStore(TRANSFERS).getAll()));
|
|
741
|
+
return held.filter((record) => !fenced(record.locator));
|
|
463
742
|
},
|
|
464
743
|
staged(record) {
|
|
465
|
-
|
|
466
|
-
return readLayer(transaction, record.locator, record.transfer);
|
|
744
|
+
return transacted(database, [DOCUMENTS, BINARY_FILES], 'readonly', (transaction) => readLayer(transaction, record.locator, record.transfer));
|
|
467
745
|
},
|
|
468
746
|
async stage(record, page) {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
747
|
+
if (fenced(record.locator))
|
|
748
|
+
return;
|
|
749
|
+
const paths = await stepped(changedIn(page, null));
|
|
750
|
+
if (fenced(record.locator))
|
|
751
|
+
return;
|
|
752
|
+
await transacted(database, [TRANSFERS, DOCUMENTS, BINARY_FILES], 'readwrite', (transaction) => {
|
|
753
|
+
transaction.objectStore(TRANSFERS).put(record);
|
|
754
|
+
return writeLayer(transaction, record.locator, record.transfer, {
|
|
755
|
+
next: page,
|
|
756
|
+
base: null,
|
|
757
|
+
paths,
|
|
758
|
+
});
|
|
759
|
+
});
|
|
473
760
|
},
|
|
474
761
|
async abandon(locator) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
762
|
+
if (fenced(locator))
|
|
763
|
+
return;
|
|
764
|
+
await transacted(database, [TRANSFERS, DOCUMENTS, BINARY_FILES], 'readwrite', (transaction) => {
|
|
765
|
+
const transfers = transaction.objectStore(TRANSFERS);
|
|
766
|
+
const held = transfers.get(locator);
|
|
767
|
+
held.onsuccess = () => {
|
|
768
|
+
const record = held.result;
|
|
769
|
+
if (record === undefined)
|
|
770
|
+
return;
|
|
771
|
+
transfers.delete(locator);
|
|
772
|
+
const range = underTransfer(locator, record.transfer);
|
|
773
|
+
transaction.objectStore(DOCUMENTS).delete(range);
|
|
774
|
+
transaction.objectStore(BINARY_FILES).delete(range);
|
|
775
|
+
};
|
|
776
|
+
});
|
|
777
|
+
},
|
|
778
|
+
receivedHeads() {
|
|
779
|
+
return transacted(database, RECEIVED, 'readonly', (transaction) => settled(transaction.objectStore(RECEIVED).getAll()));
|
|
780
|
+
},
|
|
781
|
+
async receivedParts(key, count) {
|
|
782
|
+
const kept = await transacted(database, RECEIVED_PARTS, 'readonly', (transaction) => settled(transaction
|
|
783
|
+
.objectStore(RECEIVED_PARTS)
|
|
784
|
+
.getAll(IDBKeyRange.bound([key, 0], [key, count], false, true))));
|
|
785
|
+
return kept.map((part) => part.value);
|
|
786
|
+
},
|
|
787
|
+
async keepReceived(key, head, part) {
|
|
788
|
+
await transacted(database, [RECEIVED, RECEIVED_PARTS], 'readwrite', (transaction) => {
|
|
789
|
+
transaction.objectStore(RECEIVED).put({ key, head });
|
|
790
|
+
if (part !== undefined)
|
|
791
|
+
transaction.objectStore(RECEIVED_PARTS).put({ key, ...part });
|
|
792
|
+
});
|
|
793
|
+
},
|
|
794
|
+
async dropReceived(keys) {
|
|
795
|
+
await transacted(database, [RECEIVED, RECEIVED_PARTS], 'readwrite', (transaction) => {
|
|
796
|
+
for (const key of keys) {
|
|
797
|
+
transaction.objectStore(RECEIVED).delete(key);
|
|
798
|
+
transaction.objectStore(RECEIVED_PARTS).delete(IDBKeyRange.bound([key], [key, []]));
|
|
799
|
+
}
|
|
800
|
+
});
|
|
488
801
|
},
|
|
489
802
|
async generation() {
|
|
490
|
-
const
|
|
491
|
-
const held = await settled(transaction.objectStore(FACTS).get(GENERATION));
|
|
803
|
+
const held = await transacted(authored, FACTS, 'readonly', (transaction) => settled(transaction.objectStore(FACTS).get(GENERATION)));
|
|
492
804
|
return held ?? null;
|
|
493
805
|
},
|
|
494
806
|
async rememberGeneration(value) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
807
|
+
await transacted(authored, FACTS, 'readwrite', (transaction) => {
|
|
808
|
+
transaction.objectStore(FACTS).put(value, GENERATION);
|
|
809
|
+
});
|
|
498
810
|
},
|
|
499
811
|
close() {
|
|
500
|
-
authored
|
|
501
|
-
|
|
812
|
+
for (const held of [authored, database])
|
|
813
|
+
void idle(held).then(() => held.close());
|
|
502
814
|
},
|
|
503
815
|
};
|
|
504
816
|
}
|