@agentshouse/mdruntime 0.1.0-c9b1e59e5baeb → 0.1.0-ca10e63188fc5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -15
- package/dist/adapter.d.ts +58 -32
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +5 -1
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +21 -4
- package/dist/binary.d.ts +0 -5
- package/dist/binary.d.ts.map +1 -1
- package/dist/binary.js +1 -1
- package/dist/bootstrap.d.ts +30 -8
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +187 -85
- package/dist/chain.d.ts +11 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +64 -0
- package/dist/change-set.d.ts +6 -16
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +11 -94
- package/dist/change.d.ts +31 -1
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +10 -1
- package/dist/client.d.ts +9 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +80 -6
- package/dist/collection.d.ts +11 -20
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +53 -354
- package/dist/compile.d.ts +16 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +765 -0
- package/dist/connection.d.ts +17 -9
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +2431 -953
- package/dist/conversation.d.ts +4 -3
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +81 -68
- 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 +3 -5
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +3 -1
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +18 -24
- package/dist/file.d.ts +2 -19
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +7 -122
- package/dist/fulltext.d.ts +27 -0
- package/dist/fulltext.d.ts.map +1 -0
- package/dist/fulltext.js +368 -0
- package/dist/host.d.ts +4 -4
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +45 -15
- package/dist/identity.d.ts +0 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -3
- package/dist/index.d.ts +11 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/live.d.ts +22 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +124 -0
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +205 -0
- package/dist/memory.d.ts +2 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +63 -22
- package/dist/messages.d.ts +28 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +90 -0
- package/dist/model.d.ts +6 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +7 -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 +13 -0
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +84 -0
- 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 +40 -5
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +38 -2
- package/dist/overlay.d.ts +1 -4
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +22 -148
- package/dist/query.d.ts +11 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +13 -1
- package/dist/reach.d.ts +11 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +198 -69
- package/dist/readiness.d.ts +20 -9
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +6 -6
- package/dist/reads.d.ts.map +1 -1
- package/dist/reads.js +4 -2
- package/dist/recovery.d.ts +2 -2
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +8 -7
- package/dist/refresh.d.ts +15 -14
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +113 -75
- package/dist/replica.d.ts +56 -39
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +278 -197
- package/dist/room-draft.d.ts +4 -1
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +54 -13
- package/dist/rows.d.ts +38 -31
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +173 -101
- package/dist/save.d.ts +49 -14
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +352 -98
- package/dist/search.d.ts +11 -43
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +0 -213
- package/dist/shared.d.ts +4 -3
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +67 -34
- package/dist/store.d.ts +92 -96
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +329 -720
- package/dist/stored.d.ts +152 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +571 -0
- package/dist/tables.d.ts +354 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +624 -0
- package/dist/turn.d.ts +1 -1
- package/dist/turn.d.ts.map +1 -1
- package/dist/turn.js +31 -20
- package/dist/validation.d.ts +30 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +162 -0
- package/dist/windows.d.ts +2 -6
- package/dist/windows.d.ts.map +1 -1
- package/dist/windows.js +30 -34
- package/dist/wire.d.ts +17 -1
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +5 -1
- package/dist/words.d.ts +19 -0
- package/dist/words.d.ts.map +1 -0
- package/dist/words.js +150 -0
- package/package.json +18 -19
- package/AGENTS.md +0 -299
- package/dist/chat/acts.d.ts +0 -33
- package/dist/chat/acts.d.ts.map +0 -1
- package/dist/chat/acts.js +0 -36
- package/dist/chat/context.d.ts +0 -3
- package/dist/chat/context.d.ts.map +0 -1
- package/dist/chat/context.js +0 -10
- package/dist/chat/earlier.d.ts +0 -10
- package/dist/chat/earlier.d.ts.map +0 -1
- package/dist/chat/earlier.js +0 -54
- package/dist/chat/input.d.ts +0 -9
- package/dist/chat/input.d.ts.map +0 -1
- package/dist/chat/input.js +0 -99
- package/dist/chat/kit.d.ts +0 -61
- package/dist/chat/kit.d.ts.map +0 -1
- package/dist/chat/kit.js +0 -123
- package/dist/chat/lasting.d.ts +0 -6
- package/dist/chat/lasting.d.ts.map +0 -1
- package/dist/chat/lasting.js +0 -19
- package/dist/chat/list.d.ts +0 -28
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -99
- package/dist/chat/markdown.d.ts +0 -21
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -246
- package/dist/chat/message.d.ts +0 -6
- package/dist/chat/message.d.ts.map +0 -1
- package/dist/chat/message.js +0 -199
- package/dist/chat/messages.d.ts +0 -22
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -175
- package/dist/chat/name.d.ts +0 -6
- package/dist/chat/name.d.ts.map +0 -1
- package/dist/chat/name.js +0 -37
- package/dist/chat/opened.d.ts +0 -11
- package/dist/chat/opened.d.ts.map +0 -1
- package/dist/chat/opened.js +0 -17
- package/dist/chat/overlay.d.ts +0 -32
- package/dist/chat/overlay.d.ts.map +0 -1
- package/dist/chat/overlay.js +0 -107
- package/dist/chat/parts.d.ts +0 -57
- package/dist/chat/parts.d.ts.map +0 -1
- package/dist/chat/parts.js +0 -119
- package/dist/chat/pieces.d.ts +0 -62
- package/dist/chat/pieces.d.ts.map +0 -1
- package/dist/chat/pieces.js +0 -1110
- package/dist/chat/process.d.ts +0 -20
- package/dist/chat/process.d.ts.map +0 -1
- package/dist/chat/process.js +0 -32
- package/dist/chat/provider.d.ts +0 -64
- package/dist/chat/provider.d.ts.map +0 -1
- package/dist/chat/provider.js +0 -637
- package/dist/chat/question.d.ts +0 -9
- package/dist/chat/question.d.ts.map +0 -1
- package/dist/chat/question.js +0 -69
- package/dist/chat/questions.d.ts +0 -31
- package/dist/chat/questions.d.ts.map +0 -1
- package/dist/chat/questions.js +0 -111
- package/dist/chat/read.d.ts +0 -5
- package/dist/chat/read.d.ts.map +0 -1
- package/dist/chat/read.js +0 -13
- package/dist/chat/ready.d.ts +0 -34
- package/dist/chat/ready.d.ts.map +0 -1
- package/dist/chat/ready.js +0 -91
- package/dist/chat/steady.d.ts +0 -3
- package/dist/chat/steady.d.ts.map +0 -1
- package/dist/chat/steady.js +0 -26
- package/dist/chat/style.d.ts +0 -4
- package/dist/chat/style.d.ts.map +0 -1
- package/dist/chat/style.js +0 -302
- package/dist/chat.d.ts +0 -14
- package/dist/chat.d.ts.map +0 -1
- package/dist/chat.js +0 -8
- package/dist/read-side.d.ts +0 -27
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -826
package/dist/store.js
CHANGED
|
@@ -1,816 +1,425 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const FENCES = 'fences';
|
|
8
|
-
const ROOMS = 'rooms';
|
|
9
|
-
const DOCUMENTS = 'documents';
|
|
10
|
-
const BINARY_FILES = 'binaryFiles';
|
|
11
|
-
const TRANSFERS = 'transfers';
|
|
12
|
-
const RECEIVED = 'received';
|
|
13
|
-
const RECEIVED_PARTS = 'receivedParts';
|
|
14
|
-
const PENDING_CHANGES = 'pendingChanges';
|
|
15
|
-
const DECLARATIONS = 'declarations';
|
|
16
|
-
const ROOM_DRAFTS = 'roomDrafts';
|
|
17
|
-
const PRESENTATION = 'presentation';
|
|
18
|
-
const CONVERSATIONS = 'conversations';
|
|
19
|
-
const MESSAGES = 'messages';
|
|
20
|
-
const CHAT_DRAFTS = 'chatDrafts';
|
|
21
|
-
const CALLS = 'calls';
|
|
22
|
-
const QUEUES = 'queues';
|
|
23
|
-
const FACTS = 'facts';
|
|
24
|
-
const CURRENT = 'current';
|
|
25
|
-
const MEMBER = 'member';
|
|
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
|
-
];
|
|
54
|
-
const READ_BATCH = 256;
|
|
55
|
-
const MESSAGE_INDEXES = [
|
|
56
|
-
['conversation', 'conversation'],
|
|
57
|
-
['position', ['conversation', 'position']],
|
|
58
|
-
['turn', ['conversation', 'turn']],
|
|
59
|
-
];
|
|
60
|
-
function settled(request) {
|
|
61
|
-
return new Promise((resolve, reject) => {
|
|
62
|
-
request.onsuccess = () => resolve(request.result);
|
|
63
|
-
request.onerror = () => reject(request.error);
|
|
64
|
-
});
|
|
65
|
-
}
|
|
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);
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
const EMPTY_DECLARATIONS = {
|
|
115
|
-
lastCompleted: null,
|
|
116
|
-
providerLimits: [],
|
|
117
|
-
capabilities: [],
|
|
118
|
-
};
|
|
119
|
-
function declaration(database) {
|
|
120
|
-
return transacted(database, DECLARATIONS, 'readonly', (transaction) => settled(transaction.objectStore(DECLARATIONS).get(CURRENT))).then((record) => record ?? EMPTY_DECLARATIONS);
|
|
1
|
+
import { composedOf } from './collection.js';
|
|
2
|
+
import { binaryLocalId } from './identity.js';
|
|
3
|
+
import { storedModel } from './model.js';
|
|
4
|
+
const FILE = '\u0000file';
|
|
5
|
+
function batchFiles(batch) {
|
|
6
|
+
return batch.changes.flatMap((change) => (change.kind === 'binary' ? [change.localId] : []));
|
|
121
7
|
}
|
|
122
|
-
function
|
|
123
|
-
return
|
|
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
|
-
});
|
|
8
|
+
function draftFiles(drafts) {
|
|
9
|
+
return drafts.flatMap(({ parts }) => (parts.file === undefined ? [] : [parts.file.localId]));
|
|
130
10
|
}
|
|
131
|
-
function
|
|
132
|
-
return
|
|
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;
|
|
144
|
-
}
|
|
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 ?? '' };
|
|
151
|
-
}
|
|
152
|
-
function underTransfer(locator, transfer, after) {
|
|
153
|
-
return after === undefined
|
|
154
|
-
? IDBKeyRange.bound([locator, transfer], [locator, transfer, []])
|
|
155
|
-
: IDBKeyRange.bound([locator, transfer, after], [locator, transfer, []], true);
|
|
156
|
-
}
|
|
157
|
-
function underRoom(locator) {
|
|
158
|
-
return IDBKeyRange.bound([locator], [locator, []]);
|
|
159
|
-
}
|
|
160
|
-
async function readTransfer(store, locator, transfer, receive) {
|
|
161
|
-
let range = underTransfer(locator, transfer);
|
|
162
|
-
for (;;) {
|
|
163
|
-
const batch = await settled(store.getAll(range, READ_BATCH));
|
|
164
|
-
for (const record of batch)
|
|
165
|
-
receive(record);
|
|
166
|
-
if (batch.length < READ_BATCH)
|
|
167
|
-
return;
|
|
168
|
-
range = underTransfer(locator, transfer, batch[batch.length - 1].path);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
function emptyLayer() {
|
|
172
|
-
return { documents: {}, binaries: {}, revisions: {}, fileIds: {}, changes: {}, written: {} };
|
|
173
|
-
}
|
|
174
|
-
async function readLayer(transaction, locator, transfer) {
|
|
175
|
-
const layer = emptyLayer();
|
|
176
|
-
const head = (held) => {
|
|
177
|
-
if (held.revision !== null)
|
|
178
|
-
layer.revisions[held.path] = held.revision;
|
|
179
|
-
if (held.fileId !== null)
|
|
180
|
-
layer.fileIds[held.path] = held.fileId;
|
|
181
|
-
if (held.change !== null)
|
|
182
|
-
layer.changes[held.path] = held.change;
|
|
183
|
-
};
|
|
184
|
-
await readTransfer(transaction.objectStore(DOCUMENTS), locator, transfer, (document) => {
|
|
185
|
-
layer.documents[document.path] = document.content;
|
|
186
|
-
if (document.written !== null)
|
|
187
|
-
layer.written[document.path] = document.written;
|
|
188
|
-
head(document);
|
|
189
|
-
});
|
|
190
|
-
await readTransfer(transaction.objectStore(BINARY_FILES), locator, transfer, (file) => {
|
|
191
|
-
layer.binaries[file.path] = file.binary;
|
|
192
|
-
head(file);
|
|
193
|
-
});
|
|
194
|
-
return layer;
|
|
195
|
-
}
|
|
196
|
-
function documentRecord(locator, transfer, layer, path) {
|
|
197
|
-
return {
|
|
198
|
-
locator,
|
|
199
|
-
transfer,
|
|
200
|
-
path,
|
|
201
|
-
content: layer.documents[path],
|
|
202
|
-
revision: layer.revisions[path] ?? null,
|
|
203
|
-
fileId: layer.fileIds[path] ?? null,
|
|
204
|
-
change: layer.changes[path] ?? null,
|
|
205
|
-
written: layer.written[path] ?? null,
|
|
206
|
-
};
|
|
11
|
+
function roomDraftRow(record) {
|
|
12
|
+
return { path: record.path, record: JSON.stringify(record), files: draftFiles([record]) };
|
|
207
13
|
}
|
|
208
|
-
function
|
|
14
|
+
function authoredRows({ locator, queue, founded, saved, roomDrafts, consumedRoomDrafts, accepted }, held) {
|
|
209
15
|
return {
|
|
210
16
|
locator,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
17
|
+
...(queue === undefined
|
|
18
|
+
? {}
|
|
19
|
+
: {
|
|
20
|
+
order: queue.map(({ id }) => id),
|
|
21
|
+
added: queue
|
|
22
|
+
.filter(({ id }) => held?.has(id) !== true)
|
|
23
|
+
.map((batch) => {
|
|
24
|
+
const drafts = saved?.get(batch.id) ?? [];
|
|
25
|
+
return {
|
|
26
|
+
id: batch.id,
|
|
27
|
+
batch: JSON.stringify(batch),
|
|
28
|
+
state: JSON.stringify({
|
|
29
|
+
drafts,
|
|
30
|
+
submission: { state: 'waiting' },
|
|
31
|
+
lost: false,
|
|
32
|
+
}),
|
|
33
|
+
files: [...batchFiles(batch), ...draftFiles(drafts)],
|
|
34
|
+
};
|
|
35
|
+
}),
|
|
36
|
+
}),
|
|
37
|
+
...(founded === undefined
|
|
38
|
+
? {}
|
|
39
|
+
: {
|
|
40
|
+
founded: founded.map((batch) => ({
|
|
41
|
+
id: batch.id,
|
|
42
|
+
batch: JSON.stringify(batch),
|
|
43
|
+
files: batchFiles(batch),
|
|
44
|
+
})),
|
|
45
|
+
}),
|
|
46
|
+
...(roomDrafts === undefined ? {} : { roomDrafts: roomDrafts.map(roomDraftRow) }),
|
|
47
|
+
...(consumedRoomDrafts === undefined ? {} : { consumedRoomDrafts }),
|
|
48
|
+
...(accepted === undefined
|
|
49
|
+
? {}
|
|
50
|
+
: {
|
|
51
|
+
amendments: accepted.map(({ id, roomVersion }) => ({
|
|
52
|
+
id,
|
|
53
|
+
amendment: JSON.stringify({ accepted: roomVersion }),
|
|
54
|
+
})),
|
|
55
|
+
}),
|
|
217
56
|
};
|
|
218
57
|
}
|
|
219
|
-
function
|
|
220
|
-
|
|
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;
|
|
233
|
-
const documents = transaction.objectStore(DOCUMENTS);
|
|
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));
|
|
239
|
-
}
|
|
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
|
-
});
|
|
58
|
+
function marked(value) {
|
|
59
|
+
return typeof value === 'object' && value !== null && FILE in value;
|
|
257
60
|
}
|
|
258
|
-
function
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
61
|
+
function keyed(keys, value) {
|
|
62
|
+
const stored = [];
|
|
63
|
+
const files = [];
|
|
64
|
+
const record = JSON.stringify(value, (_name, held) => {
|
|
65
|
+
if (!(held instanceof Blob))
|
|
66
|
+
return held;
|
|
67
|
+
let key = keys.get(held);
|
|
68
|
+
if (key === undefined) {
|
|
69
|
+
key = binaryLocalId();
|
|
70
|
+
keys.set(held, key);
|
|
264
71
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
return new Promise((resolve, reject) => {
|
|
269
|
-
const removal = indexedDB.deleteDatabase(name);
|
|
270
|
-
removal.onsuccess = () => resolve();
|
|
271
|
-
removal.onerror = () => reject(removal.error);
|
|
72
|
+
stored.push({ key, blob: held });
|
|
73
|
+
files.push(key);
|
|
74
|
+
return { [FILE]: key, type: held.type };
|
|
272
75
|
});
|
|
76
|
+
return { record, files, stored };
|
|
273
77
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
return
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
-
}
|
|
78
|
+
async function filed(value, read) {
|
|
79
|
+
if (Array.isArray(value))
|
|
80
|
+
return Promise.all(value.map((item) => filed(item, read)));
|
|
81
|
+
if (marked(value))
|
|
82
|
+
return read(value[FILE], value.type);
|
|
83
|
+
if (typeof value !== 'object' || value === null)
|
|
84
|
+
return value;
|
|
85
|
+
const restored = {};
|
|
86
|
+
for (const [name, item] of Object.entries(value))
|
|
87
|
+
restored[name] = await filed(item, read);
|
|
88
|
+
return restored;
|
|
303
89
|
}
|
|
304
|
-
|
|
305
|
-
|
|
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
|
-
}
|
|
90
|
+
function json(value, fallback) {
|
|
91
|
+
return value === undefined ? fallback : JSON.parse(value);
|
|
313
92
|
}
|
|
314
|
-
export
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
await
|
|
318
|
-
|
|
93
|
+
export function openRuntimeStore(fenced, authored) {
|
|
94
|
+
const keys = new WeakMap();
|
|
95
|
+
const read = async (key, type) => {
|
|
96
|
+
const blob = await authored.ask({ kind: 'authored.file', key, type });
|
|
97
|
+
keys.set(blob, key);
|
|
98
|
+
return blob;
|
|
99
|
+
};
|
|
100
|
+
const restored = async (records) => Promise.all(records.map(async (record) => (await filed(JSON.parse(record), read))));
|
|
101
|
+
const amend = async (locator, id, amendment) => {
|
|
102
|
+
if (fenced(locator))
|
|
103
|
+
return;
|
|
104
|
+
await authored.ask({
|
|
105
|
+
kind: 'authored.amend',
|
|
106
|
+
locator,
|
|
107
|
+
id,
|
|
108
|
+
amendment: JSON.stringify(amendment),
|
|
319
109
|
});
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
return false;
|
|
331
|
-
}
|
|
332
|
-
export function openDatabase(name, version, upgrade) {
|
|
333
|
-
return new Promise((resolve, reject) => {
|
|
334
|
-
const opening = indexedDB.open(name, version);
|
|
335
|
-
opening.onupgradeneeded = (event) => upgrade(opening, event.oldVersion);
|
|
336
|
-
opening.onsuccess = () => resolve(opening.result);
|
|
337
|
-
opening.onerror = () => {
|
|
338
|
-
if (opening.error?.name !== 'VersionError') {
|
|
339
|
-
reject(opening.error);
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
removeDatabase(name)
|
|
343
|
-
.then(() => openDatabase(name, version, upgrade))
|
|
344
|
-
.then(resolve, reject);
|
|
345
|
-
};
|
|
346
|
-
});
|
|
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);
|
|
354
|
-
}
|
|
355
|
-
for (const [name, parameters] of REPLICA_STORES) {
|
|
356
|
-
if (database.objectStoreNames.contains(name))
|
|
357
|
-
continue;
|
|
358
|
-
database.createObjectStore(name, parameters);
|
|
359
|
-
}
|
|
360
|
-
const messages = opening.transaction.objectStore(MESSAGES);
|
|
361
|
-
for (const [name, keyPath] of MESSAGE_INDEXES) {
|
|
362
|
-
if (!messages.indexNames.contains(name))
|
|
363
|
-
messages.createIndex(name, keyPath);
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
function openAuthored() {
|
|
368
|
-
return openDatabase(AUTHORED, AUTHORED_SCHEMA, (opening) => {
|
|
369
|
-
const database = opening.result;
|
|
370
|
-
for (const [name, parameters] of AUTHORED_STORES) {
|
|
371
|
-
if (!database.objectStoreNames.contains(name))
|
|
372
|
-
database.createObjectStore(name, parameters);
|
|
110
|
+
};
|
|
111
|
+
const fact = (name) => authored.ask({ kind: 'authored.fact', name });
|
|
112
|
+
const queued = new Map();
|
|
113
|
+
const changed = new Map();
|
|
114
|
+
const change = (key) => changed.set(key, (changed.get(key) ?? 0) + 1);
|
|
115
|
+
const rows = (writes) => writes.map((write) => authoredRows(write, queued.get(write.locator)));
|
|
116
|
+
const noted = (writes) => {
|
|
117
|
+
for (const { locator, queue } of writes) {
|
|
118
|
+
if (queue !== undefined)
|
|
119
|
+
queued.set(locator, new Set(queue.map(({ id }) => id)));
|
|
373
120
|
}
|
|
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
|
-
});
|
|
121
|
+
};
|
|
382
122
|
return {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
123
|
+
async opened() {
|
|
124
|
+
const held = await authored.ask({ kind: 'replica.open' });
|
|
125
|
+
const unindexed = new Set(held.keys.flatMap(({ key, rederiving, searching }) => rederiving === null && searching === null ? [] : [key]));
|
|
126
|
+
const overlaid = new Map();
|
|
127
|
+
for (const { key, path } of held.overlaid) {
|
|
128
|
+
const paths = overlaid.get(key) ?? new Set();
|
|
129
|
+
paths.add(path);
|
|
130
|
+
overlaid.set(key, paths);
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
rooms: new Map(held.rooms
|
|
134
|
+
.filter(({ locator }) => !fenced(locator))
|
|
135
|
+
.map(({ locator, key, record }) => [
|
|
136
|
+
locator,
|
|
137
|
+
{ ...JSON.parse(record), key },
|
|
138
|
+
])),
|
|
139
|
+
transfers: new Map(held.transfers
|
|
140
|
+
.filter(({ locator }) => !fenced(locator))
|
|
141
|
+
.map(({ locator, key, record }) => [
|
|
142
|
+
locator,
|
|
143
|
+
{ ...JSON.parse(record), key },
|
|
144
|
+
])),
|
|
145
|
+
kept: held.kept
|
|
146
|
+
.filter(({ locator }) => !fenced(locator))
|
|
147
|
+
.reduce((parts, { locator, token, bytes }) => parts.set(locator, new Map(parts.get(locator)).set(token, bytes)), new Map()),
|
|
148
|
+
unindexed,
|
|
149
|
+
overlaid,
|
|
150
|
+
erasing: held.erasing.length > 0,
|
|
151
|
+
orphans: held.orphans,
|
|
152
|
+
lastCompleted: json(held.facts.lastCompleted, null),
|
|
153
|
+
providerLimits: json(held.facts.providerLimits, []),
|
|
154
|
+
capabilities: json(held.facts.capabilities, []),
|
|
155
|
+
member: json(held.facts.member, null),
|
|
156
|
+
targets: json(held.facts.targets, {}),
|
|
157
|
+
presentation: held.presentation.map(({ app, value }) => ({
|
|
158
|
+
app,
|
|
159
|
+
value: JSON.parse(value),
|
|
160
|
+
})),
|
|
161
|
+
};
|
|
395
162
|
},
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
};
|
|
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
|
-
});
|
|
163
|
+
counts(rooms) {
|
|
164
|
+
return authored.ask({ kind: 'replica.counts', rooms });
|
|
443
165
|
},
|
|
444
|
-
|
|
445
|
-
|
|
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);
|
|
458
|
-
}
|
|
459
|
-
if (moved)
|
|
460
|
-
transaction.objectStore(TRANSFERS).delete(next.locator);
|
|
461
|
-
return writeLayer(transaction, next.locator, next.transfer, { next, base, paths });
|
|
462
|
-
});
|
|
166
|
+
query(plan, rooms, budget) {
|
|
167
|
+
return authored.ask({ kind: 'replica.query', plan, rooms, budget });
|
|
463
168
|
},
|
|
464
|
-
|
|
465
|
-
|
|
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
|
-
});
|
|
169
|
+
changes(keys) {
|
|
170
|
+
return keys.reduce((total, key) => total + (changed.get(key) ?? 0), 0);
|
|
474
171
|
},
|
|
475
|
-
|
|
476
|
-
return (
|
|
172
|
+
entries(key, view, paths) {
|
|
173
|
+
return authored.ask({ kind: 'replica.entries', key, view, paths });
|
|
477
174
|
},
|
|
478
|
-
|
|
479
|
-
return
|
|
175
|
+
typed(key, view, paths) {
|
|
176
|
+
return authored.ask({ kind: 'replica.typed', key, view, paths });
|
|
480
177
|
},
|
|
481
|
-
|
|
482
|
-
return (
|
|
178
|
+
referrers(key, view, targets, kinds) {
|
|
179
|
+
return authored.ask({ kind: 'replica.referrers', key, view, targets, kinds });
|
|
483
180
|
},
|
|
484
|
-
|
|
485
|
-
return (
|
|
181
|
+
walk(key, view, after, bytes) {
|
|
182
|
+
return authored.ask({ kind: 'replica.walk', key, view, after, bytes });
|
|
486
183
|
},
|
|
487
|
-
|
|
488
|
-
return
|
|
489
|
-
...record,
|
|
490
|
-
providerLimits: limits,
|
|
491
|
-
capabilities,
|
|
492
|
-
}));
|
|
184
|
+
files(key, view, after, limit) {
|
|
185
|
+
return authored.ask({ kind: 'replica.files', key, view, after, limit });
|
|
493
186
|
},
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
return held ?? null;
|
|
187
|
+
typedWithin(key, view, roots, within) {
|
|
188
|
+
return authored.ask({ kind: 'replica.typedWithin', key, view, roots, within });
|
|
497
189
|
},
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
190
|
+
search(ask) {
|
|
191
|
+
return authored.ask({ kind: 'replica.search', ...ask });
|
|
192
|
+
},
|
|
193
|
+
async stage(record, composition, page, written) {
|
|
194
|
+
if (fenced(record.locator))
|
|
195
|
+
return null;
|
|
196
|
+
const { key, ...kept } = record;
|
|
197
|
+
const stored = {
|
|
198
|
+
...kept,
|
|
199
|
+
models: { collection: storedModel(kept.models.collection) },
|
|
200
|
+
};
|
|
201
|
+
return authored.ask({
|
|
202
|
+
kind: 'replica.stage',
|
|
203
|
+
locator: record.locator,
|
|
204
|
+
transfer: record.transfer,
|
|
205
|
+
record: JSON.stringify(stored),
|
|
206
|
+
key,
|
|
207
|
+
fingerprint: composedOf(composition).fingerprint,
|
|
208
|
+
entries: page.entries,
|
|
209
|
+
folders: page.folders,
|
|
210
|
+
...(written === undefined ? {} : { written }),
|
|
501
211
|
});
|
|
502
212
|
},
|
|
503
|
-
async
|
|
504
|
-
|
|
505
|
-
|
|
213
|
+
async transferPart(locator, token) {
|
|
214
|
+
if (fenced(locator))
|
|
215
|
+
return null;
|
|
216
|
+
return authored.ask({ kind: 'replica.transferPart', locator, token });
|
|
217
|
+
},
|
|
218
|
+
async keepParts(parts) {
|
|
219
|
+
const kept = parts.filter(({ locator }) => !fenced(locator));
|
|
220
|
+
if (kept.length > 0)
|
|
221
|
+
await authored.ask({ kind: 'replica.keepParts', parts: kept });
|
|
222
|
+
},
|
|
223
|
+
async refresh(key, composition, page) {
|
|
224
|
+
await authored.ask({
|
|
225
|
+
kind: 'replica.refresh',
|
|
226
|
+
key,
|
|
227
|
+
composed: composedOf(composition),
|
|
228
|
+
entries: page.entries,
|
|
229
|
+
removals: page.removals,
|
|
230
|
+
folders: page.folders,
|
|
231
|
+
folderRemovals: page.folderRemovals,
|
|
232
|
+
});
|
|
233
|
+
},
|
|
234
|
+
async unrefresh(key) {
|
|
235
|
+
await authored.ask({ kind: 'replica.unrefresh', key });
|
|
506
236
|
},
|
|
507
|
-
async
|
|
508
|
-
|
|
509
|
-
|
|
237
|
+
async commit({ rooms, writes = [], files = [], whole = false, discards = [] }) {
|
|
238
|
+
const kept = whole ? writes : writes.filter(({ locator }) => !fenced(locator));
|
|
239
|
+
const refused = whole ? kept.find(({ locator }) => fenced(locator)) : undefined;
|
|
240
|
+
if (refused !== undefined) {
|
|
241
|
+
return { fenced: refused.locator, counts: {}, changed: {}, indexed: {} };
|
|
242
|
+
}
|
|
243
|
+
for (const room of rooms)
|
|
244
|
+
change(room.key);
|
|
245
|
+
const committed = await authored.ask({
|
|
246
|
+
kind: 'replica.commit',
|
|
247
|
+
...(kept.length === 0 && files.length === 0 && discards.length === 0
|
|
248
|
+
? {}
|
|
249
|
+
: { authored: { writes: rows(kept), files, whole, discards } }),
|
|
250
|
+
rooms: rooms.filter(({ locator }) => !fenced(locator)),
|
|
510
251
|
});
|
|
252
|
+
if (committed.fenced === null || !whole)
|
|
253
|
+
noted(kept);
|
|
254
|
+
for (const locator of discards)
|
|
255
|
+
queued.delete(locator);
|
|
256
|
+
return committed;
|
|
257
|
+
},
|
|
258
|
+
async fence(locators) {
|
|
259
|
+
await authored.ask({ kind: 'replica.fence', locators });
|
|
260
|
+
for (const locator of locators)
|
|
261
|
+
queued.delete(locator);
|
|
262
|
+
},
|
|
263
|
+
async erase(locator, limit) {
|
|
264
|
+
return (await authored.ask({ kind: 'replica.erase', locator, limit })).remaining;
|
|
265
|
+
},
|
|
266
|
+
async abandon(locator, kept = false) {
|
|
267
|
+
if (fenced(locator))
|
|
268
|
+
return null;
|
|
269
|
+
return authored.ask({ kind: 'replica.abandon', locator, kept });
|
|
270
|
+
},
|
|
271
|
+
async release(key) {
|
|
272
|
+
await authored.ask({ kind: 'replica.release', key });
|
|
273
|
+
},
|
|
274
|
+
async rederive(key, composition, bytes) {
|
|
275
|
+
change(key);
|
|
276
|
+
return (await authored.ask({
|
|
277
|
+
kind: 'replica.rederive',
|
|
278
|
+
key,
|
|
279
|
+
composed: composedOf(composition),
|
|
280
|
+
bytes,
|
|
281
|
+
})).done;
|
|
282
|
+
},
|
|
283
|
+
async fact(name, value) {
|
|
284
|
+
await authored.ask({ kind: 'replica.fact', name, value: JSON.stringify(value) });
|
|
511
285
|
},
|
|
512
286
|
async queues() {
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
287
|
+
const { queues, pending } = await authored.ask({ kind: 'authored.queues' });
|
|
288
|
+
queued.clear();
|
|
289
|
+
for (const queue of queues)
|
|
290
|
+
queued.set(queue.locator, new Set(queue.ids));
|
|
517
291
|
const byId = new Map(pending.map((record) => [`${record.locator}\0${record.id}`, record]));
|
|
518
292
|
return queues
|
|
519
293
|
.filter((queue) => !fenced(queue.locator))
|
|
520
294
|
.map((queue) => ({
|
|
521
295
|
locator: queue.locator,
|
|
522
|
-
pending: queue.
|
|
296
|
+
pending: queue.ids.map((id) => {
|
|
523
297
|
const record = byId.get(`${queue.locator}\0${id}`);
|
|
524
298
|
if (record === undefined)
|
|
525
299
|
throw new Error(`Missing Pending Change ${queue.locator}/${id}`);
|
|
526
|
-
|
|
527
|
-
|
|
300
|
+
return {
|
|
301
|
+
batch: JSON.parse(record.batch),
|
|
302
|
+
state: JSON.parse(record.state),
|
|
303
|
+
};
|
|
528
304
|
}),
|
|
529
305
|
}));
|
|
530
306
|
},
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
await amendPending(authored, locator, id, { submission, lost });
|
|
534
|
-
},
|
|
535
|
-
async accept(locator, id, roomVersion) {
|
|
536
|
-
if (!fenced(locator))
|
|
537
|
-
await amendPending(authored, locator, id, { accepted: roomVersion });
|
|
307
|
+
submit(locator, id, submission, lost) {
|
|
308
|
+
return amend(locator, id, { submission, lost });
|
|
538
309
|
},
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
await amendPending(authored, locator, id, { upload });
|
|
310
|
+
keepUpload(locator, id, upload) {
|
|
311
|
+
return amend(locator, id, { upload });
|
|
542
312
|
},
|
|
543
313
|
async roomDrafts() {
|
|
544
|
-
const held = await
|
|
545
|
-
return held
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
return transacted(database, PRESENTATION, 'readonly', (transaction) => settled(transaction.objectStore(PRESENTATION).getAll()));
|
|
314
|
+
const held = await authored.ask({ kind: 'authored.roomDrafts' });
|
|
315
|
+
return held
|
|
316
|
+
.map((record) => JSON.parse(record))
|
|
317
|
+
.filter((record) => !fenced(record.locator));
|
|
549
318
|
},
|
|
550
319
|
async present(record) {
|
|
551
|
-
await
|
|
552
|
-
|
|
320
|
+
await authored.ask({
|
|
321
|
+
kind: 'replica.present',
|
|
322
|
+
app: record.app,
|
|
323
|
+
value: JSON.stringify(record.value ?? null),
|
|
553
324
|
});
|
|
554
325
|
},
|
|
555
326
|
conversations() {
|
|
556
|
-
return
|
|
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
|
-
});
|
|
327
|
+
return authored.ask({ kind: 'conversation.held' });
|
|
581
328
|
},
|
|
582
|
-
async keepMessages(
|
|
583
|
-
await
|
|
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
|
-
});
|
|
329
|
+
async keepMessages(write) {
|
|
330
|
+
await authored.ask({ kind: 'conversation.keep', ...write });
|
|
596
331
|
},
|
|
597
332
|
messageWindow(conversation, limit, before) {
|
|
598
|
-
return
|
|
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
|
-
});
|
|
333
|
+
return authored.ask({ kind: 'conversation.window', conversation, limit, before });
|
|
628
334
|
},
|
|
629
335
|
messageCount(conversation) {
|
|
630
|
-
return
|
|
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);
|
|
336
|
+
return authored.ask({ kind: 'conversation.count', conversation });
|
|
638
337
|
},
|
|
639
|
-
|
|
640
|
-
|
|
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;
|
|
338
|
+
turnArrived(conversation, turn) {
|
|
339
|
+
return authored.ask({ kind: 'conversation.turn', conversation, turn });
|
|
646
340
|
},
|
|
647
341
|
async dropConversations(conversations) {
|
|
648
|
-
await
|
|
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
|
-
});
|
|
342
|
+
await authored.ask({ kind: 'conversation.drop', conversations });
|
|
663
343
|
},
|
|
664
344
|
async chatDrafts() {
|
|
665
|
-
const
|
|
666
|
-
return
|
|
345
|
+
const records = await authored.ask({ kind: 'authored.chatDrafts' });
|
|
346
|
+
return records.map((record) => JSON.parse(record));
|
|
667
347
|
},
|
|
668
348
|
async keepChatDrafts(records) {
|
|
669
|
-
await
|
|
670
|
-
|
|
671
|
-
|
|
349
|
+
await authored.ask({
|
|
350
|
+
kind: 'authored.chatDraft',
|
|
351
|
+
kept: records.map((record) => ({
|
|
352
|
+
conversation: record.conversation,
|
|
353
|
+
record: JSON.stringify(record),
|
|
354
|
+
})),
|
|
355
|
+
dropped: [],
|
|
672
356
|
});
|
|
673
357
|
},
|
|
674
358
|
async dropChatDraft(conversation) {
|
|
675
|
-
await
|
|
676
|
-
transaction.objectStore(CHAT_DRAFTS).delete(conversation);
|
|
677
|
-
});
|
|
359
|
+
await authored.ask({ kind: 'authored.chatDraft', kept: [], dropped: [conversation] });
|
|
678
360
|
},
|
|
679
|
-
calls() {
|
|
680
|
-
return
|
|
361
|
+
async calls() {
|
|
362
|
+
return restored(await authored.ask({ kind: 'authored.calls' }));
|
|
681
363
|
},
|
|
682
364
|
async keepCall(record) {
|
|
683
|
-
|
|
684
|
-
|
|
365
|
+
const { stored, ...kept } = keyed(keys, record);
|
|
366
|
+
await authored.ask({
|
|
367
|
+
kind: 'authored.call',
|
|
368
|
+
operation: record.operation,
|
|
369
|
+
kept,
|
|
370
|
+
files: stored,
|
|
685
371
|
});
|
|
686
372
|
},
|
|
687
373
|
async dropCall(operation) {
|
|
688
|
-
await
|
|
689
|
-
transaction.objectStore(CALLS).delete(operation);
|
|
690
|
-
});
|
|
374
|
+
await authored.ask({ kind: 'authored.call', operation, kept: null, files: [] });
|
|
691
375
|
},
|
|
692
|
-
async keepRoomDraft(record) {
|
|
376
|
+
async keepRoomDraft(record, files) {
|
|
693
377
|
if (fenced(record.locator))
|
|
694
378
|
return;
|
|
695
|
-
|
|
696
|
-
|
|
379
|
+
const { path, ...kept } = roomDraftRow(record);
|
|
380
|
+
await authored.ask({
|
|
381
|
+
kind: 'authored.roomDraft',
|
|
382
|
+
locator: record.locator,
|
|
383
|
+
path,
|
|
384
|
+
kept,
|
|
385
|
+
files,
|
|
697
386
|
});
|
|
698
387
|
},
|
|
699
388
|
async discardRoomDraft(locator, path) {
|
|
700
389
|
if (fenced(locator))
|
|
701
390
|
return;
|
|
702
|
-
await
|
|
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
|
-
}
|
|
718
|
-
}
|
|
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
|
-
});
|
|
391
|
+
await authored.ask({ kind: 'authored.roomDraft', locator, path, kept: null, files: [] });
|
|
732
392
|
},
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
transaction.objectStore(QUEUES).delete(locator);
|
|
736
|
-
removeRoomEntries(transaction.objectStore(PENDING_CHANGES), locator);
|
|
737
|
-
});
|
|
738
|
-
},
|
|
739
|
-
async transfers() {
|
|
740
|
-
const held = await transacted(database, TRANSFERS, 'readonly', (transaction) => settled(transaction.objectStore(TRANSFERS).getAll()));
|
|
741
|
-
return held.filter((record) => !fenced(record.locator));
|
|
742
|
-
},
|
|
743
|
-
staged(record) {
|
|
744
|
-
return transacted(database, [DOCUMENTS, BINARY_FILES], 'readonly', (transaction) => readLayer(transaction, record.locator, record.transfer));
|
|
745
|
-
},
|
|
746
|
-
async stage(record, page) {
|
|
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
|
-
});
|
|
760
|
-
},
|
|
761
|
-
async abandon(locator) {
|
|
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
|
-
});
|
|
393
|
+
fences() {
|
|
394
|
+
return authored.ask({ kind: 'authored.fences' });
|
|
777
395
|
},
|
|
778
396
|
receivedHeads() {
|
|
779
|
-
return
|
|
397
|
+
return authored.ask({ kind: 'replica.received' });
|
|
780
398
|
},
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
.objectStore(RECEIVED_PARTS)
|
|
784
|
-
.getAll(IDBKeyRange.bound([key, 0], [key, count], false, true))));
|
|
785
|
-
return kept.map((part) => part.value);
|
|
399
|
+
receivedParts(key, count) {
|
|
400
|
+
return authored.ask({ kind: 'replica.receivedParts', key, count });
|
|
786
401
|
},
|
|
787
402
|
async keepReceived(key, head, part) {
|
|
788
|
-
await
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
403
|
+
await authored.ask({
|
|
404
|
+
kind: 'replica.receive',
|
|
405
|
+
key,
|
|
406
|
+
head: JSON.stringify(head),
|
|
407
|
+
...(part === undefined
|
|
408
|
+
? {}
|
|
409
|
+
: { part: { index: part.index, value: JSON.stringify(part.value) } }),
|
|
792
410
|
});
|
|
793
411
|
},
|
|
794
412
|
async dropReceived(keys) {
|
|
795
|
-
await
|
|
796
|
-
for (const key of keys) {
|
|
797
|
-
transaction.objectStore(RECEIVED).delete(key);
|
|
798
|
-
transaction.objectStore(RECEIVED_PARTS).delete(IDBKeyRange.bound([key], [key, []]));
|
|
799
|
-
}
|
|
800
|
-
});
|
|
413
|
+
await authored.ask({ kind: 'replica.unreceive', keys });
|
|
801
414
|
},
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
return held ?? null;
|
|
415
|
+
generation() {
|
|
416
|
+
return fact('generation');
|
|
805
417
|
},
|
|
806
418
|
async rememberGeneration(value) {
|
|
807
|
-
await
|
|
808
|
-
transaction.objectStore(FACTS).put(value, GENERATION);
|
|
809
|
-
});
|
|
419
|
+
await authored.ask({ kind: 'authored.remember', name: 'generation', value });
|
|
810
420
|
},
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
void idle(held).then(() => held.close());
|
|
421
|
+
file(key, type) {
|
|
422
|
+
return authored.ask({ kind: 'authored.file', key, type });
|
|
814
423
|
},
|
|
815
424
|
};
|
|
816
425
|
}
|