@agentshouse/mdruntime 0.1.0-c5d3fa6fb67bc → 0.1.0-c6636685d0c4b

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