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