@agentshouse/mdruntime 0.1.0-cec6690d5712e → 0.1.0-cedb03eaa48b6

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