@agentshouse/mdruntime 0.1.0-c8c4b020a3ec7 → 0.1.0-c8ca66ac9e99d

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