@agentshouse/mdruntime 0.1.0-c967d1ca66fee → 0.1.0-ca14c49c06d90

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