@agentshouse/mdruntime 0.1.0-c99e5271060cd → 0.1.0-c9b1e59e5baeb
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 +274 -244
- package/README.md +18 -8
- package/dist/adapter.d.ts +98 -20
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attempt.d.ts +16 -0
- package/dist/attempt.d.ts.map +1 -0
- package/dist/attempt.js +70 -0
- package/dist/binary.d.ts +23 -0
- package/dist/binary.d.ts.map +1 -0
- package/dist/binary.js +8 -0
- package/dist/bootstrap.d.ts +3 -2
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +38 -15
- package/dist/change-set.d.ts +7 -2
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +45 -13
- package/dist/change.d.ts +11 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +26 -1
- package/dist/chat/acts.d.ts +33 -0
- package/dist/chat/acts.d.ts.map +1 -0
- package/dist/chat/acts.js +36 -0
- package/dist/chat/context.d.ts +3 -0
- package/dist/chat/context.d.ts.map +1 -0
- package/dist/chat/context.js +10 -0
- 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/input.d.ts +9 -0
- package/dist/chat/input.d.ts.map +1 -0
- package/dist/chat/input.js +99 -0
- package/dist/chat/kit.d.ts +61 -0
- package/dist/chat/kit.d.ts.map +1 -0
- package/dist/chat/kit.js +123 -0
- package/dist/chat/lasting.d.ts +6 -0
- package/dist/chat/lasting.d.ts.map +1 -0
- package/dist/chat/lasting.js +19 -0
- package/dist/chat/list.d.ts +28 -0
- package/dist/chat/list.d.ts.map +1 -0
- package/dist/chat/list.js +99 -0
- package/dist/chat/markdown.d.ts +21 -0
- package/dist/chat/markdown.d.ts.map +1 -0
- package/dist/chat/markdown.js +246 -0
- package/dist/chat/message.d.ts +6 -0
- package/dist/chat/message.d.ts.map +1 -0
- package/dist/chat/message.js +199 -0
- package/dist/chat/messages.d.ts +22 -0
- package/dist/chat/messages.d.ts.map +1 -0
- package/dist/chat/messages.js +175 -0
- package/dist/chat/name.d.ts +6 -0
- package/dist/chat/name.d.ts.map +1 -0
- package/dist/chat/name.js +37 -0
- package/dist/chat/opened.d.ts +11 -0
- package/dist/chat/opened.d.ts.map +1 -0
- package/dist/chat/opened.js +17 -0
- package/dist/chat/overlay.d.ts +32 -0
- package/dist/chat/overlay.d.ts.map +1 -0
- package/dist/chat/overlay.js +107 -0
- package/dist/chat/parts.d.ts +57 -0
- package/dist/chat/parts.d.ts.map +1 -0
- package/dist/chat/parts.js +119 -0
- 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/process.d.ts +20 -0
- package/dist/chat/process.d.ts.map +1 -0
- package/dist/chat/process.js +32 -0
- package/dist/chat/provider.d.ts +64 -0
- package/dist/chat/provider.d.ts.map +1 -0
- package/dist/chat/provider.js +637 -0
- package/dist/chat/question.d.ts +9 -0
- package/dist/chat/question.d.ts.map +1 -0
- package/dist/chat/question.js +69 -0
- package/dist/chat/questions.d.ts +31 -0
- package/dist/chat/questions.d.ts.map +1 -0
- package/dist/chat/questions.js +111 -0
- package/dist/chat/read.d.ts +5 -0
- package/dist/chat/read.d.ts.map +1 -0
- package/dist/chat/read.js +13 -0
- package/dist/chat/ready.d.ts +34 -0
- package/dist/chat/ready.d.ts.map +1 -0
- package/dist/chat/ready.js +91 -0
- 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.d.ts +4 -0
- package/dist/chat/style.d.ts.map +1 -0
- package/dist/chat/style.js +302 -0
- package/dist/chat.d.ts +14 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +8 -0
- package/dist/client.d.ts +26 -11
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +27 -4
- 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 +41 -13
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1819 -407
- package/dist/conversation.d.ts +34 -9
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +552 -168
- 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/expectation.d.ts +21 -0
- package/dist/expectation.d.ts.map +1 -0
- package/dist/expectation.js +70 -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 +99 -13
- package/dist/identity.d.ts +4 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +12 -0
- package/dist/index.d.ts +15 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/memory.d.ts +10 -4
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +64 -17
- 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 +17 -1
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +10 -0
- package/dist/overlay.d.ts +18 -5
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +238 -160
- package/dist/query.d.ts +58 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +36 -7
- package/dist/reach.d.ts +8 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +55 -8
- package/dist/react.d.ts +10 -7
- 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 +495 -102
- 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 +51 -30
- package/dist/replica.d.ts +15 -6
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +137 -66
- package/dist/room-draft.d.ts +32 -3
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +129 -12
- package/dist/rows.d.ts +8 -13
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +12 -13
- package/dist/save.d.ts +32 -2
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +495 -97
- 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 -1
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +0 -8
- package/dist/store.d.ts +71 -21
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +625 -320
- 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 +153 -30
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +53 -29
- package/package.json +26 -14
- package/dist/attachment.d.ts +0 -3
- package/dist/attachment.d.ts.map +0 -1
- package/dist/attachment.js +0 -8
package/dist/store.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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';
|
|
3
5
|
const DATABASE = 'mdruntime';
|
|
6
|
+
const AUTHORED = 'mdruntime-authored';
|
|
7
|
+
const FENCES = 'fences';
|
|
4
8
|
const ROOMS = 'rooms';
|
|
5
9
|
const DOCUMENTS = 'documents';
|
|
6
|
-
const
|
|
7
|
-
const ATTACHMENTS = 'attachments';
|
|
10
|
+
const BINARY_FILES = 'binaryFiles';
|
|
8
11
|
const TRANSFERS = 'transfers';
|
|
12
|
+
const RECEIVED = 'received';
|
|
13
|
+
const RECEIVED_PARTS = 'receivedParts';
|
|
9
14
|
const PENDING_CHANGES = 'pendingChanges';
|
|
10
15
|
const DECLARATIONS = 'declarations';
|
|
11
16
|
const ROOM_DRAFTS = 'roomDrafts';
|
|
@@ -13,57 +18,136 @@ const PRESENTATION = 'presentation';
|
|
|
13
18
|
const CONVERSATIONS = 'conversations';
|
|
14
19
|
const MESSAGES = 'messages';
|
|
15
20
|
const CHAT_DRAFTS = 'chatDrafts';
|
|
21
|
+
const CALLS = 'calls';
|
|
22
|
+
const QUEUES = 'queues';
|
|
23
|
+
const FACTS = 'facts';
|
|
16
24
|
const CURRENT = 'current';
|
|
17
25
|
const MEMBER = 'member';
|
|
18
|
-
|
|
19
|
-
const
|
|
26
|
+
const TARGETS = 'targets';
|
|
27
|
+
const GENERATION = 'generation';
|
|
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;
|
|
33
|
+
const AUTHORED_STORES = [
|
|
34
|
+
[QUEUES, { keyPath: 'locator' }],
|
|
35
|
+
[PENDING_CHANGES, { keyPath: ['locator', 'id'] }],
|
|
36
|
+
[ROOM_DRAFTS, { keyPath: ['locator', 'path'] }],
|
|
37
|
+
[CHAT_DRAFTS, { keyPath: 'conversation' }],
|
|
38
|
+
[CALLS, { keyPath: 'operation' }],
|
|
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'] }],
|
|
53
|
+
];
|
|
20
54
|
const READ_BATCH = 256;
|
|
55
|
+
const MESSAGE_INDEXES = [
|
|
56
|
+
['conversation', 'conversation'],
|
|
57
|
+
['position', ['conversation', 'position']],
|
|
58
|
+
['turn', ['conversation', 'turn']],
|
|
59
|
+
];
|
|
21
60
|
function settled(request) {
|
|
22
61
|
return new Promise((resolve, reject) => {
|
|
23
62
|
request.onsuccess = () => resolve(request.result);
|
|
24
63
|
request.onerror = () => reject(request.error);
|
|
25
64
|
});
|
|
26
65
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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);
|
|
32
112
|
});
|
|
33
113
|
}
|
|
34
114
|
const EMPTY_DECLARATIONS = {
|
|
35
115
|
lastCompleted: null,
|
|
36
|
-
generation: null,
|
|
37
116
|
providerLimits: [],
|
|
38
117
|
capabilities: [],
|
|
39
118
|
};
|
|
40
119
|
function declaration(database) {
|
|
41
|
-
|
|
42
|
-
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);
|
|
43
121
|
}
|
|
44
122
|
function updateDeclaration(database, update) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
+
});
|
|
52
130
|
}
|
|
53
131
|
function amendPending(database, locator, id, amendment) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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;
|
|
63
144
|
}
|
|
64
|
-
function roomRecord(record
|
|
65
|
-
const { documents,
|
|
66
|
-
return
|
|
145
|
+
function roomRecord(record) {
|
|
146
|
+
const { documents, binaries, revisions, fileIds, changes, written, ...room } = record;
|
|
147
|
+
return room;
|
|
148
|
+
}
|
|
149
|
+
function chatDraftRecord(record) {
|
|
150
|
+
return { ...record, revision: record.revision ?? '' };
|
|
67
151
|
}
|
|
68
152
|
function underTransfer(locator, transfer, after) {
|
|
69
153
|
return after === undefined
|
|
@@ -85,13 +169,15 @@ async function readTransfer(store, locator, transfer, receive) {
|
|
|
85
169
|
}
|
|
86
170
|
}
|
|
87
171
|
function emptyLayer() {
|
|
88
|
-
return { documents: {},
|
|
172
|
+
return { documents: {}, binaries: {}, revisions: {}, fileIds: {}, changes: {}, written: {} };
|
|
89
173
|
}
|
|
90
174
|
async function readLayer(transaction, locator, transfer) {
|
|
91
175
|
const layer = emptyLayer();
|
|
92
176
|
const head = (held) => {
|
|
93
177
|
if (held.revision !== null)
|
|
94
178
|
layer.revisions[held.path] = held.revision;
|
|
179
|
+
if (held.fileId !== null)
|
|
180
|
+
layer.fileIds[held.path] = held.fileId;
|
|
95
181
|
if (held.change !== null)
|
|
96
182
|
layer.changes[held.path] = held.change;
|
|
97
183
|
};
|
|
@@ -101,9 +187,9 @@ async function readLayer(transaction, locator, transfer) {
|
|
|
101
187
|
layer.written[document.path] = document.written;
|
|
102
188
|
head(document);
|
|
103
189
|
});
|
|
104
|
-
await readTransfer(transaction.objectStore(
|
|
105
|
-
layer.
|
|
106
|
-
head(
|
|
190
|
+
await readTransfer(transaction.objectStore(BINARY_FILES), locator, transfer, (file) => {
|
|
191
|
+
layer.binaries[file.path] = file.binary;
|
|
192
|
+
head(file);
|
|
107
193
|
});
|
|
108
194
|
return layer;
|
|
109
195
|
}
|
|
@@ -114,47 +200,60 @@ function documentRecord(locator, transfer, layer, path) {
|
|
|
114
200
|
path,
|
|
115
201
|
content: layer.documents[path],
|
|
116
202
|
revision: layer.revisions[path] ?? null,
|
|
203
|
+
fileId: layer.fileIds[path] ?? null,
|
|
117
204
|
change: layer.changes[path] ?? null,
|
|
118
205
|
written: layer.written[path] ?? null,
|
|
119
206
|
};
|
|
120
207
|
}
|
|
121
|
-
function
|
|
208
|
+
function binaryFileRecord(locator, transfer, layer, path) {
|
|
122
209
|
return {
|
|
123
210
|
locator,
|
|
124
211
|
transfer,
|
|
125
212
|
path,
|
|
126
213
|
revision: layer.revisions[path] ?? null,
|
|
214
|
+
fileId: layer.fileIds[path] ?? null,
|
|
127
215
|
change: layer.changes[path] ?? null,
|
|
216
|
+
binary: layer.binaries[path],
|
|
128
217
|
};
|
|
129
218
|
}
|
|
130
|
-
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;
|
|
131
233
|
const documents = transaction.objectStore(DOCUMENTS);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (base?.documents[path] === content &&
|
|
138
|
-
base.revisions[path] === next.revisions[path] &&
|
|
139
|
-
base.written[path] === next.written[path] &&
|
|
140
|
-
sameChange(base.changes[path], next.changes[path]))
|
|
141
|
-
continue;
|
|
142
|
-
documents.put(documentRecord(locator, transfer, next, path));
|
|
143
|
-
}
|
|
144
|
-
const targets = transaction.objectStore(BINARY_TARGETS);
|
|
145
|
-
const beforeTargets = new Set(base?.binaryTargets ?? []);
|
|
146
|
-
const afterTargets = new Set(next.binaryTargets);
|
|
147
|
-
for (const path of beforeTargets) {
|
|
148
|
-
if (!afterTargets.has(path))
|
|
149
|
-
targets.delete([locator, transfer, path]);
|
|
150
|
-
}
|
|
151
|
-
for (const path of afterTargets) {
|
|
152
|
-
if (!beforeTargets.has(path) ||
|
|
153
|
-
base?.revisions[path] !== next.revisions[path] ||
|
|
154
|
-
!sameChange(base?.changes[path], next.changes[path])) {
|
|
155
|
-
targets.put(targetRecord(locator, transfer, next, path));
|
|
234
|
+
const files = transaction.objectStore(BINARY_FILES);
|
|
235
|
+
let last = null;
|
|
236
|
+
return inUnits(paths, (path) => {
|
|
237
|
+
if (next.documents[path] !== undefined) {
|
|
238
|
+
last = documents.put(documentRecord(locator, transfer, next, path));
|
|
156
239
|
}
|
|
157
|
-
|
|
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
|
+
});
|
|
158
257
|
}
|
|
159
258
|
function removeRoomEntries(store, locator) {
|
|
160
259
|
const keys = store.getAllKeys();
|
|
@@ -165,157 +264,213 @@ function removeRoomEntries(store, locator) {
|
|
|
165
264
|
}
|
|
166
265
|
};
|
|
167
266
|
}
|
|
168
|
-
|
|
267
|
+
function removeDatabase(name) {
|
|
169
268
|
return new Promise((resolve, reject) => {
|
|
170
|
-
const removal = indexedDB.deleteDatabase(
|
|
269
|
+
const removal = indexedDB.deleteDatabase(name);
|
|
171
270
|
removal.onsuccess = () => resolve();
|
|
172
271
|
removal.onerror = () => reject(removal.error);
|
|
173
272
|
});
|
|
174
273
|
}
|
|
175
|
-
function
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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);
|
|
183
345
|
};
|
|
184
346
|
});
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
if (
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
transfers.set(record.locator, transfer);
|
|
193
|
-
room.update({ ...record, transfer });
|
|
194
|
-
room.continue();
|
|
195
|
-
return;
|
|
347
|
+
}
|
|
348
|
+
function openReplica() {
|
|
349
|
+
return openDatabase(DATABASE, STORE_SCHEMA, (opening, oldVersion) => {
|
|
350
|
+
const database = opening.result;
|
|
351
|
+
if (oldVersion < REPLICA_SHAPE) {
|
|
352
|
+
for (const name of [...database.objectStoreNames])
|
|
353
|
+
database.deleteObjectStore(name);
|
|
196
354
|
}
|
|
197
|
-
for (const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (entry === null) {
|
|
202
|
-
database.deleteObjectStore(from.name);
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
const record = entry.value;
|
|
206
|
-
const transfer = transfers.get(record.locator);
|
|
207
|
-
if (transfer !== undefined)
|
|
208
|
-
to.put({ ...record, transfer });
|
|
209
|
-
entry.continue();
|
|
210
|
-
};
|
|
355
|
+
for (const [name, parameters] of REPLICA_STORES) {
|
|
356
|
+
if (database.objectStoreNames.contains(name))
|
|
357
|
+
continue;
|
|
358
|
+
database.createObjectStore(name, parameters);
|
|
211
359
|
}
|
|
212
|
-
|
|
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
|
+
});
|
|
213
366
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
opening.onupgradeneeded = (event) => {
|
|
367
|
+
function openAuthored() {
|
|
368
|
+
return openDatabase(AUTHORED, AUTHORED_SCHEMA, (opening) => {
|
|
217
369
|
const database = opening.result;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
370
|
+
for (const [name, parameters] of AUTHORED_STORES) {
|
|
371
|
+
if (!database.objectStoreNames.contains(name))
|
|
372
|
+
database.createObjectStore(name, parameters);
|
|
221
373
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
database.createObjectStore(DECLARATIONS);
|
|
231
|
-
database.createObjectStore(ROOM_DRAFTS, { keyPath: ['locator', 'path'] });
|
|
232
|
-
database.createObjectStore(PRESENTATION, { keyPath: 'app' });
|
|
233
|
-
database.createObjectStore(CONVERSATIONS, { keyPath: 'conversation' });
|
|
234
|
-
const messages = database.createObjectStore(MESSAGES, {
|
|
235
|
-
keyPath: ['conversation', 'identity'],
|
|
236
|
-
});
|
|
237
|
-
messages.createIndex('conversation', 'conversation');
|
|
238
|
-
database.createObjectStore(CHAT_DRAFTS, { keyPath: 'conversation' });
|
|
239
|
-
};
|
|
240
|
-
const database = await settled(opening);
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
export async function openRuntimeStore(fenced) {
|
|
377
|
+
const authored = await openAuthored();
|
|
378
|
+
const database = await openReplica().catch((error) => {
|
|
379
|
+
authored.close();
|
|
380
|
+
throw error;
|
|
381
|
+
});
|
|
241
382
|
return {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
records.push({
|
|
252
|
-
...room,
|
|
253
|
-
...layer,
|
|
254
|
-
attachments: attachments.flatMap(({ locator, ...attachment }) => locator === room.locator ? [attachment] : []),
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
return records;
|
|
258
|
-
},
|
|
259
|
-
async write({ next, previous, stored, queue, previousQueue, saved, roomDrafts, consumedRoomDrafts, }) {
|
|
260
|
-
const transaction = database.transaction([ROOMS, DOCUMENTS, BINARY_TARGETS, ATTACHMENTS, TRANSFERS, PENDING_CHANGES, ROOM_DRAFTS], 'readwrite');
|
|
261
|
-
transaction.objectStore(ROOMS).put(roomRecord(next, queue));
|
|
262
|
-
const moved = previous?.transfer !== next.transfer;
|
|
263
|
-
if (moved && previous !== null) {
|
|
264
|
-
const range = underTransfer(previous.locator, previous.transfer);
|
|
265
|
-
transaction.objectStore(DOCUMENTS).delete(range);
|
|
266
|
-
transaction.objectStore(BINARY_TARGETS).delete(range);
|
|
267
|
-
}
|
|
268
|
-
if (moved)
|
|
269
|
-
transaction.objectStore(TRANSFERS).delete(next.locator);
|
|
270
|
-
writeLayer(transaction, next.locator, next.transfer, next, moved ? (stored ?? null) : previous);
|
|
271
|
-
const attachments = transaction.objectStore(ATTACHMENTS);
|
|
272
|
-
const held = new Map((previous?.attachments ?? []).map((attachment) => [attachment.reference, attachment]));
|
|
273
|
-
const declared = new Set(next.attachments.map((attachment) => attachment.reference));
|
|
274
|
-
for (const reference of held.keys()) {
|
|
275
|
-
if (!declared.has(reference))
|
|
276
|
-
attachments.delete([next.locator, reference]);
|
|
277
|
-
}
|
|
278
|
-
for (const attachment of next.attachments) {
|
|
279
|
-
if (held.get(attachment.reference) === attachment)
|
|
280
|
-
continue;
|
|
281
|
-
attachments.put({ ...attachment, locator: next.locator });
|
|
282
|
-
}
|
|
283
|
-
if (previousQueue !== undefined) {
|
|
284
|
-
const pending = transaction.objectStore(PENDING_CHANGES);
|
|
285
|
-
const before = new Set(previousQueue.map((batch) => batch.id));
|
|
286
|
-
const after = new Set(queue.map((batch) => batch.id));
|
|
287
|
-
for (const id of before) {
|
|
288
|
-
if (!after.has(id))
|
|
289
|
-
pending.delete([next.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
|
+
});
|
|
290
392
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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]);
|
|
413
|
+
}
|
|
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
|
+
};
|
|
301
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
|
+
});
|
|
443
|
+
},
|
|
444
|
+
async write({ next, previous, stored }) {
|
|
445
|
+
if (next === previous || fenced(next.locator))
|
|
446
|
+
return;
|
|
447
|
+
const moved = previous?.transfer !== next.transfer;
|
|
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);
|
|
302
458
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
for (const record of roomDrafts ?? [])
|
|
308
|
-
draftStore.put(record);
|
|
309
|
-
await completed(transaction);
|
|
459
|
+
if (moved)
|
|
460
|
+
transaction.objectStore(TRANSFERS).delete(next.locator);
|
|
461
|
+
return writeLayer(transaction, next.locator, next.transfer, { next, base, paths });
|
|
462
|
+
});
|
|
310
463
|
},
|
|
311
464
|
async revokeModels(records) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
+
});
|
|
319
474
|
},
|
|
320
475
|
async lastCompleted() {
|
|
321
476
|
return (await declaration(database)).lastCompleted;
|
|
@@ -337,175 +492,325 @@ export async function openRuntimeStore() {
|
|
|
337
492
|
}));
|
|
338
493
|
},
|
|
339
494
|
async member() {
|
|
340
|
-
const
|
|
341
|
-
const held = await settled(transaction.objectStore(DECLARATIONS).get(MEMBER));
|
|
495
|
+
const held = await transacted(database, DECLARATIONS, 'readonly', (transaction) => settled(transaction.objectStore(DECLARATIONS).get(MEMBER)));
|
|
342
496
|
return held ?? null;
|
|
343
497
|
},
|
|
344
498
|
async keepMember(member) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
+
});
|
|
348
511
|
},
|
|
349
512
|
async queues() {
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
settled(transaction.objectStore(ROOMS).getAll()),
|
|
513
|
+
const [queues, pending] = await transacted(authored, [QUEUES, PENDING_CHANGES], 'readonly', (transaction) => Promise.all([
|
|
514
|
+
settled(transaction.objectStore(QUEUES).getAll()),
|
|
353
515
|
settled(transaction.objectStore(PENDING_CHANGES).getAll()),
|
|
354
|
-
]);
|
|
516
|
+
]));
|
|
355
517
|
const byId = new Map(pending.map((record) => [`${record.locator}\0${record.id}`, record]));
|
|
356
|
-
return
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
518
|
+
return queues
|
|
519
|
+
.filter((queue) => !fenced(queue.locator))
|
|
520
|
+
.map((queue) => ({
|
|
521
|
+
locator: queue.locator,
|
|
522
|
+
pending: queue.order.map((id) => {
|
|
523
|
+
const record = byId.get(`${queue.locator}\0${id}`);
|
|
360
524
|
if (record === undefined)
|
|
361
|
-
throw new Error(`Missing Pending Change ${
|
|
525
|
+
throw new Error(`Missing Pending Change ${queue.locator}/${id}`);
|
|
362
526
|
const { batch, drafts, submission, lost, upload, accepted } = record;
|
|
363
527
|
return { batch, state: { drafts, submission, lost, upload, accepted } };
|
|
364
528
|
}),
|
|
365
529
|
}));
|
|
366
530
|
},
|
|
367
|
-
submit(locator, id, submission, lost) {
|
|
368
|
-
|
|
531
|
+
async submit(locator, id, submission, lost) {
|
|
532
|
+
if (!fenced(locator))
|
|
533
|
+
await amendPending(authored, locator, id, { submission, lost });
|
|
369
534
|
},
|
|
370
|
-
accept(locator, id, roomVersion) {
|
|
371
|
-
|
|
535
|
+
async accept(locator, id, roomVersion) {
|
|
536
|
+
if (!fenced(locator))
|
|
537
|
+
await amendPending(authored, locator, id, { accepted: roomVersion });
|
|
372
538
|
},
|
|
373
|
-
keepUpload(locator, id, upload) {
|
|
374
|
-
|
|
539
|
+
async keepUpload(locator, id, upload) {
|
|
540
|
+
if (!fenced(locator))
|
|
541
|
+
await amendPending(authored, locator, id, { upload });
|
|
375
542
|
},
|
|
376
543
|
async roomDrafts() {
|
|
377
|
-
const
|
|
378
|
-
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));
|
|
379
546
|
},
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
return settled(transaction.objectStore(PRESENTATION).getAll());
|
|
547
|
+
presentation() {
|
|
548
|
+
return transacted(database, PRESENTATION, 'readonly', (transaction) => settled(transaction.objectStore(PRESENTATION).getAll()));
|
|
383
549
|
},
|
|
384
550
|
async present(record) {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
},
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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
|
+
});
|
|
405
628
|
},
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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;
|
|
420
646
|
},
|
|
421
647
|
async dropConversations(conversations) {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
+
});
|
|
434
663
|
},
|
|
435
664
|
async chatDrafts() {
|
|
436
|
-
const
|
|
437
|
-
return
|
|
665
|
+
const held = await transacted(authored, CHAT_DRAFTS, 'readonly', (transaction) => settled(transaction.objectStore(CHAT_DRAFTS).getAll()));
|
|
666
|
+
return held.map(chatDraftRecord);
|
|
438
667
|
},
|
|
439
|
-
async
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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
|
+
});
|
|
443
673
|
},
|
|
444
674
|
async dropChatDraft(conversation) {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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
|
+
});
|
|
448
691
|
},
|
|
449
692
|
async keepRoomDraft(record) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
await
|
|
693
|
+
if (fenced(record.locator))
|
|
694
|
+
return;
|
|
695
|
+
await transacted(authored, ROOM_DRAFTS, 'readwrite', (transaction) => {
|
|
696
|
+
transaction.objectStore(ROOM_DRAFTS).put(record);
|
|
697
|
+
});
|
|
453
698
|
},
|
|
454
699
|
async discardRoomDraft(locator, path) {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
await
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
+
}
|
|
468
718
|
}
|
|
469
|
-
}
|
|
470
|
-
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
|
+
});
|
|
471
738
|
},
|
|
472
739
|
async transfers() {
|
|
473
|
-
const
|
|
474
|
-
return
|
|
740
|
+
const held = await transacted(database, TRANSFERS, 'readonly', (transaction) => settled(transaction.objectStore(TRANSFERS).getAll()));
|
|
741
|
+
return held.filter((record) => !fenced(record.locator));
|
|
475
742
|
},
|
|
476
743
|
staged(record) {
|
|
477
|
-
|
|
478
|
-
return readLayer(transaction, record.locator, record.transfer);
|
|
744
|
+
return transacted(database, [DOCUMENTS, BINARY_FILES], 'readonly', (transaction) => readLayer(transaction, record.locator, record.transfer));
|
|
479
745
|
},
|
|
480
746
|
async stage(record, page) {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
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
|
+
});
|
|
485
760
|
},
|
|
486
761
|
async abandon(locator) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
+
});
|
|
500
801
|
},
|
|
501
802
|
async generation() {
|
|
502
|
-
|
|
803
|
+
const held = await transacted(authored, FACTS, 'readonly', (transaction) => settled(transaction.objectStore(FACTS).get(GENERATION)));
|
|
804
|
+
return held ?? null;
|
|
503
805
|
},
|
|
504
|
-
rememberGeneration(value) {
|
|
505
|
-
|
|
806
|
+
async rememberGeneration(value) {
|
|
807
|
+
await transacted(authored, FACTS, 'readwrite', (transaction) => {
|
|
808
|
+
transaction.objectStore(FACTS).put(value, GENERATION);
|
|
809
|
+
});
|
|
506
810
|
},
|
|
507
811
|
close() {
|
|
508
|
-
database
|
|
812
|
+
for (const held of [authored, database])
|
|
813
|
+
void idle(held).then(() => held.close());
|
|
509
814
|
},
|
|
510
815
|
};
|
|
511
816
|
}
|