@agentshouse/mdruntime 0.1.0-cc5745cd9af96 → 0.1.0-ccbb89b0ca151

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