@agentshouse/mdruntime 0.1.0-c967d1ca66fee → 0.1.0-c97456d63baba

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