@agentshouse/mdruntime 0.1.0-c3903a68dcbbf → 0.1.0-c56daf494ecb4

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 (178) hide show
  1. package/AGENTS.md +184 -92
  2. package/README.md +22 -12
  3. package/dist/adapter.d.ts +125 -63
  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 +93 -39
  18. package/dist/change-set.d.ts.map +1 -1
  19. package/dist/change-set.js +20 -17
  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 -13
  87. package/dist/client.d.ts.map +1 -1
  88. package/dist/client.js +68 -31
  89. package/dist/collection.d.ts +6 -13
  90. package/dist/collection.d.ts.map +1 -1
  91. package/dist/collection.js +56 -29
  92. package/dist/connection.d.ts +28 -33
  93. package/dist/connection.d.ts.map +1 -1
  94. package/dist/connection.js +951 -745
  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/entry.d.ts +17 -0
  99. package/dist/entry.d.ts.map +1 -0
  100. package/dist/entry.js +156 -0
  101. package/dist/expectation.d.ts +21 -0
  102. package/dist/expectation.d.ts.map +1 -0
  103. package/dist/expectation.js +70 -0
  104. package/dist/failure.d.ts +13 -0
  105. package/dist/failure.d.ts.map +1 -0
  106. package/dist/failure.js +19 -0
  107. package/dist/host.d.ts +9 -3
  108. package/dist/host.d.ts.map +1 -1
  109. package/dist/host.js +49 -68
  110. package/dist/identity.d.ts +3 -0
  111. package/dist/identity.d.ts.map +1 -1
  112. package/dist/identity.js +13 -1
  113. package/dist/index.d.ts +9 -5
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.js +3 -1
  116. package/dist/memory.d.ts +14 -7
  117. package/dist/memory.d.ts.map +1 -1
  118. package/dist/memory.js +137 -46
  119. package/dist/model.d.ts +13 -12
  120. package/dist/model.d.ts.map +1 -1
  121. package/dist/model.js +6 -3
  122. package/dist/observation.d.ts +2 -0
  123. package/dist/observation.d.ts.map +1 -1
  124. package/dist/observation.js +24 -14
  125. package/dist/open.d.ts +2 -0
  126. package/dist/open.d.ts.map +1 -0
  127. package/dist/open.js +1 -0
  128. package/dist/outcome.d.ts +32 -23
  129. package/dist/outcome.d.ts.map +1 -1
  130. package/dist/outcome.js +41 -1
  131. package/dist/overlay.d.ts +14 -3
  132. package/dist/overlay.d.ts.map +1 -1
  133. package/dist/overlay.js +75 -37
  134. package/dist/paths.d.ts +0 -1
  135. package/dist/paths.d.ts.map +1 -1
  136. package/dist/paths.js +0 -16
  137. package/dist/query.d.ts +12 -3
  138. package/dist/query.d.ts.map +1 -1
  139. package/dist/query.js +78 -36
  140. package/dist/reach.d.ts +42 -0
  141. package/dist/reach.d.ts.map +1 -0
  142. package/dist/reach.js +294 -0
  143. package/dist/react.d.ts +41 -0
  144. package/dist/react.d.ts.map +1 -0
  145. package/dist/react.js +64 -0
  146. package/dist/read-side.d.ts +4 -0
  147. package/dist/read-side.d.ts.map +1 -1
  148. package/dist/read-side.js +125 -56
  149. package/dist/refresh.d.ts +12 -1
  150. package/dist/refresh.d.ts.map +1 -1
  151. package/dist/refresh.js +44 -36
  152. package/dist/replica.d.ts +9 -6
  153. package/dist/replica.d.ts.map +1 -1
  154. package/dist/replica.js +83 -68
  155. package/dist/room-draft.d.ts +29 -9
  156. package/dist/room-draft.d.ts.map +1 -1
  157. package/dist/room-draft.js +137 -26
  158. package/dist/rows.d.ts +7 -2
  159. package/dist/rows.d.ts.map +1 -1
  160. package/dist/rows.js +85 -40
  161. package/dist/save.d.ts +27 -3
  162. package/dist/save.d.ts.map +1 -1
  163. package/dist/save.js +435 -86
  164. package/dist/search.d.ts +3 -2
  165. package/dist/search.d.ts.map +1 -1
  166. package/dist/status.d.ts +2 -2
  167. package/dist/status.d.ts.map +1 -1
  168. package/dist/status.js +0 -8
  169. package/dist/store.d.ts +41 -25
  170. package/dist/store.d.ts.map +1 -1
  171. package/dist/store.js +315 -191
  172. package/dist/wire.d.ts +58 -19
  173. package/dist/wire.d.ts.map +1 -1
  174. package/dist/wire.js +65 -67
  175. package/package.json +31 -7
  176. package/dist/attachment.d.ts +0 -3
  177. package/dist/attachment.d.ts.map +0 -1
  178. package/dist/attachment.js +0 -8
package/dist/store.js CHANGED
@@ -1,19 +1,33 @@
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
- const OUTCOMES = 'outcomes';
9
9
  const DECLARATIONS = 'declarations';
10
10
  const ROOM_DRAFTS = 'roomDrafts';
11
11
  const PRESENTATION = 'presentation';
12
12
  const CONVERSATIONS = 'conversations';
13
13
  const MESSAGES = 'messages';
14
14
  const CHAT_DRAFTS = 'chatDrafts';
15
+ const QUEUES = 'queues';
16
+ const FACTS = 'facts';
15
17
  const CURRENT = 'current';
16
- const SCHEMA = 19;
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;
17
31
  function settled(request) {
18
32
  return new Promise((resolve, reject) => {
19
33
  request.onsuccess = () => resolve(request.result);
@@ -29,10 +43,8 @@ function completed(transaction) {
29
43
  }
30
44
  const EMPTY_DECLARATIONS = {
31
45
  lastCompleted: null,
32
- generation: null,
33
46
  providerLimits: [],
34
47
  capabilities: [],
35
- slots: [],
36
48
  };
37
49
  function declaration(database) {
38
50
  const transaction = database.transaction(DECLARATIONS, 'readonly');
@@ -58,9 +70,102 @@ function amendPending(database, locator, id, amendment) {
58
70
  };
59
71
  return completed(transaction);
60
72
  }
61
- function roomRecord(record, queue) {
62
- const { documents, binaryTargets, revisions, changes, written, attachments, ...room } = record;
63
- 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
+ }
64
169
  }
65
170
  function removeRoomEntries(store, locator) {
66
171
  const keys = store.getAllKeys();
@@ -71,186 +176,157 @@ function removeRoomEntries(store, locator) {
71
176
  }
72
177
  };
73
178
  }
74
- export function removeRuntimeStore() {
179
+ function removeDatabase(name) {
75
180
  return new Promise((resolve, reject) => {
76
- const removal = indexedDB.deleteDatabase(DATABASE);
181
+ const removal = indexedDB.deleteDatabase(name);
77
182
  removal.onsuccess = () => resolve();
78
183
  removal.onerror = () => reject(removal.error);
79
184
  });
80
185
  }
81
- export async function openRuntimeStore() {
82
- 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);
83
227
  opening.onupgradeneeded = () => {
84
228
  const database = opening.result;
85
229
  for (const name of [...database.objectStoreNames])
86
230
  database.deleteObjectStore(name);
87
231
  database.createObjectStore(ROOMS, { keyPath: 'locator' });
88
- database.createObjectStore(DOCUMENTS, { keyPath: ['locator', 'path'] });
89
- database.createObjectStore(BINARY_TARGETS, { keyPath: ['locator', 'path'] });
90
- database.createObjectStore(ATTACHMENTS, { keyPath: ['locator', 'reference'] });
91
- database.createObjectStore(PENDING_CHANGES, { keyPath: ['locator', 'id'] });
92
- const outcomes = database.createObjectStore(OUTCOMES, { keyPath: 'id' });
93
- outcomes.createIndex('locator', 'locator');
232
+ database.createObjectStore(DOCUMENTS, { keyPath: ['locator', 'transfer', 'path'] });
233
+ database.createObjectStore(BINARY_FILES, { keyPath: ['locator', 'transfer', 'path'] });
234
+ database.createObjectStore(TRANSFERS, { keyPath: 'locator' });
94
235
  database.createObjectStore(DECLARATIONS);
95
- database.createObjectStore(ROOM_DRAFTS, { keyPath: ['locator', 'path'] });
96
236
  database.createObjectStore(PRESENTATION, { keyPath: 'app' });
97
237
  database.createObjectStore(CONVERSATIONS, { keyPath: 'conversation' });
98
238
  const messages = database.createObjectStore(MESSAGES, {
99
239
  keyPath: ['conversation', 'identity'],
100
240
  });
101
241
  messages.createIndex('conversation', 'conversation');
102
- database.createObjectStore(CHAT_DRAFTS, { keyPath: 'conversation' });
103
242
  };
104
- 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
+ });
105
262
  return {
106
263
  async all() {
107
- const transaction = database.transaction([ROOMS, DOCUMENTS, BINARY_TARGETS, ATTACHMENTS], 'readonly');
108
- const [rooms, documents, targets, attachments] = await Promise.all([
109
- settled(transaction.objectStore(ROOMS).getAll()),
110
- settled(transaction.objectStore(DOCUMENTS).getAll()),
111
- settled(transaction.objectStore(BINARY_TARGETS).getAll()),
112
- settled(transaction.objectStore(ATTACHMENTS).getAll()),
113
- ]);
114
- const byRoom = new Map();
115
- for (const { order, ...room } of rooms) {
116
- byRoom.set(room.locator, {
117
- ...room,
118
- documents: {},
119
- revisions: {},
120
- changes: {},
121
- written: {},
122
- binaryTargets: [],
123
- attachments: [],
124
- });
125
- }
126
- const head = (room, held) => {
127
- if (held.revision !== null) {
128
- room.revisions[held.path] = held.revision;
129
- }
130
- if (held.change !== null) {
131
- room.changes[held.path] = held.change;
132
- }
133
- };
134
- for (const document of documents) {
135
- const room = byRoom.get(document.locator);
136
- if (room === undefined)
137
- continue;
138
- room.documents[document.path] = document.content;
139
- if (document.written !== null) {
140
- room.written[document.path] = document.written;
141
- }
142
- head(room, document);
143
- }
144
- for (const target of targets) {
145
- const room = byRoom.get(target.locator);
146
- if (room === undefined)
147
- continue;
148
- room.binaryTargets.push(target.path);
149
- head(room, target);
150
- }
151
- for (const { locator, ...attachment } of attachments) {
152
- const room = byRoom.get(locator);
153
- if (room === undefined)
154
- continue;
155
- room.attachments.push(attachment);
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)) });
156
269
  }
157
- return [...byRoom.values()];
158
- },
159
- async write({ next, previous, queue, previousQueue, saved, outcomes, roomDrafts, consumedRoomDrafts, }) {
160
- const transaction = database.transaction([ROOMS, DOCUMENTS, BINARY_TARGETS, ATTACHMENTS, PENDING_CHANGES, OUTCOMES, ROOM_DRAFTS], 'readwrite');
161
- transaction.objectStore(ROOMS).put(roomRecord(next, queue));
162
- const documents = transaction.objectStore(DOCUMENTS);
163
- for (const path of Object.keys(previous?.documents ?? {})) {
164
- if (next.documents[path] === undefined)
165
- documents.delete([next.locator, path]);
166
- }
167
- for (const [path, content] of Object.entries(next.documents)) {
168
- if (previous?.documents[path] === content &&
169
- previous.revisions[path] === next.revisions[path] &&
170
- previous.written[path] === next.written[path] &&
171
- sameChange(previous.changes[path], next.changes[path]))
172
- continue;
173
- documents.put({
174
- locator: next.locator,
175
- path,
176
- content,
177
- revision: next.revisions[path] ?? null,
178
- change: next.changes[path] ?? null,
179
- written: next.written[path] ?? null,
180
- });
181
- }
182
- const targets = transaction.objectStore(BINARY_TARGETS);
183
- const beforeTargets = new Set(previous?.binaryTargets ?? []);
184
- const afterTargets = new Set(next.binaryTargets);
185
- for (const path of beforeTargets) {
186
- if (!afterTargets.has(path))
187
- targets.delete([next.locator, path]);
188
- }
189
- for (const path of afterTargets) {
190
- if (!beforeTargets.has(path) ||
191
- previous?.revisions[path] !== next.revisions[path] ||
192
- !sameChange(previous?.changes[path], next.changes[path])) {
193
- targets.put({
194
- locator: next.locator,
195
- path,
196
- revision: next.revisions[path] ?? null,
197
- change: next.changes[path] ?? null,
198
- });
199
- }
200
- }
201
- const attachments = transaction.objectStore(ATTACHMENTS);
202
- const held = new Map((previous?.attachments ?? []).map((attachment) => [attachment.reference, attachment]));
203
- const declared = new Set(next.attachments.map((attachment) => attachment.reference));
204
- for (const reference of held.keys()) {
205
- if (!declared.has(reference))
206
- attachments.delete([next.locator, reference]);
207
- }
208
- for (const attachment of next.attachments) {
209
- if (held.get(attachment.reference) === attachment)
210
- continue;
211
- attachments.put({ ...attachment, locator: next.locator });
212
- }
213
- if (previousQueue !== undefined) {
214
- const pending = transaction.objectStore(PENDING_CHANGES);
215
- 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 ?? []);
216
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] });
217
284
  for (const id of before) {
218
285
  if (!after.has(id))
219
- pending.delete([next.locator, id]);
286
+ pending.delete([locator, id]);
220
287
  }
221
288
  for (const batch of queue) {
222
289
  if (!before.has(batch.id)) {
223
290
  pending.put({
224
- locator: next.locator,
291
+ locator,
225
292
  id: batch.id,
226
293
  batch,
227
- drafts: saved ?? [],
294
+ drafts: saved?.get(batch.id) ?? [],
228
295
  submission: { state: 'waiting' },
229
296
  lost: false,
230
297
  });
231
298
  }
232
299
  }
233
- }
234
- const outcomeStore = transaction.objectStore(OUTCOMES);
235
- for (const entry of outcomes ?? []) {
236
- if (entry.outcome === null)
237
- outcomeStore.delete(entry.id);
238
- else
239
- outcomeStore.put({ locator: next.locator, id: entry.id, outcome: entry.outcome });
240
- }
300
+ };
241
301
  const draftStore = transaction.objectStore(ROOM_DRAFTS);
242
302
  for (const path of consumedRoomDrafts ?? [])
243
- draftStore.delete([next.locator, path]);
303
+ draftStore.delete([locator, path]);
244
304
  for (const record of roomDrafts ?? [])
245
305
  draftStore.put(record);
246
306
  await completed(transaction);
247
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
+ },
248
324
  async revokeModels(records) {
249
325
  const transaction = database.transaction(ROOMS, 'readwrite');
250
326
  const rooms = transaction.objectStore(ROOMS);
251
327
  const revoked = { status: 'unavailable', reason: 'revoked' };
252
- for (const { record, queue } of records) {
253
- rooms.put(roomRecord({ ...record, model: revoked, selectionModel: revoked }, queue));
328
+ for (const record of records) {
329
+ rooms.put(roomRecord({ ...record, model: revoked, selectionModel: revoked }));
254
330
  }
255
331
  await completed(transaction);
256
332
  },
@@ -266,47 +342,52 @@ export async function openRuntimeStore() {
266
342
  async capabilities() {
267
343
  return (await declaration(database)).capabilities;
268
344
  },
269
- async slots() {
270
- return (await declaration(database)).slots;
271
- },
272
- declare(limits, capabilities, slots) {
345
+ declare(limits, capabilities) {
273
346
  return updateDeclaration(database, (record) => ({
274
347
  ...record,
275
348
  providerLimits: limits,
276
349
  capabilities,
277
- slots,
278
350
  }));
279
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
+ },
280
362
  async queues() {
281
- const transaction = database.transaction([ROOMS, PENDING_CHANGES], 'readonly');
282
- const [rooms, pending] = await Promise.all([
283
- 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()),
284
366
  settled(transaction.objectStore(PENDING_CHANGES).getAll()),
285
367
  ]);
286
368
  const byId = new Map(pending.map((record) => [`${record.locator}\0${record.id}`, record]));
287
- return rooms.map((room) => ({
288
- locator: room.locator,
289
- pending: room.order.map((id) => {
290
- 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}`);
291
373
  if (record === undefined)
292
- throw new Error(`Missing Pending Change ${room.locator}/${id}`);
293
- const { batch, drafts, submission, lost, upload } = record;
294
- return { batch, state: { drafts, submission, lost, upload } };
374
+ throw new Error(`Missing Pending Change ${queue.locator}/${id}`);
375
+ const { batch, drafts, submission, lost, upload, accepted } = record;
376
+ return { batch, state: { drafts, submission, lost, upload, accepted } };
295
377
  }),
296
378
  }));
297
379
  },
298
- async outcomes() {
299
- const transaction = database.transaction(OUTCOMES, 'readonly');
300
- return settled(transaction.objectStore(OUTCOMES).getAll());
301
- },
302
380
  submit(locator, id, submission, lost) {
303
- return amendPending(database, locator, id, { submission, lost });
381
+ return amendPending(authored, locator, id, { submission, lost });
382
+ },
383
+ accept(locator, id, roomVersion) {
384
+ return amendPending(authored, locator, id, { accepted: roomVersion });
304
385
  },
305
386
  keepUpload(locator, id, upload) {
306
- return amendPending(database, locator, id, { upload });
387
+ return amendPending(authored, locator, id, { upload });
307
388
  },
308
389
  async roomDrafts() {
309
- const transaction = database.transaction(ROOM_DRAFTS, 'readonly');
390
+ const transaction = authored.transaction(ROOM_DRAFTS, 'readonly');
310
391
  return settled(transaction.objectStore(ROOM_DRAFTS).getAll());
311
392
  },
312
393
  async presentation() {
@@ -336,7 +417,12 @@ export async function openRuntimeStore() {
336
417
  return [...held.values()];
337
418
  },
338
419
  async keepMessages({ conversation, messages, read, sealed }) {
339
- 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');
340
426
  const stored = transaction.objectStore(MESSAGES);
341
427
  for (const message of messages) {
342
428
  stored.put({ ...message, conversation });
@@ -346,16 +432,17 @@ export async function openRuntimeStore() {
346
432
  .objectStore(CONVERSATIONS)
347
433
  .put({ conversation, read });
348
434
  }
349
- if (sealed === true)
350
- transaction.objectStore(CHAT_DRAFTS).delete(conversation);
351
435
  await completed(transaction);
352
436
  },
353
437
  async dropConversations(conversations) {
354
- 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');
355
443
  const messages = transaction.objectStore(MESSAGES);
356
444
  for (const conversation of conversations) {
357
445
  transaction.objectStore(CONVERSATIONS).delete(conversation);
358
- transaction.objectStore(CHAT_DRAFTS).delete(conversation);
359
446
  const keys = messages.index('conversation').getAllKeys(conversation);
360
447
  keys.onsuccess = () => {
361
448
  for (const key of keys.result)
@@ -365,52 +452,89 @@ export async function openRuntimeStore() {
365
452
  await completed(transaction);
366
453
  },
367
454
  async chatDrafts() {
368
- const transaction = database.transaction(CHAT_DRAFTS, 'readonly');
369
- 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);
370
458
  },
371
459
  async keepChatDraft(record) {
372
- const transaction = database.transaction(CHAT_DRAFTS, 'readwrite');
460
+ const transaction = authored.transaction(CHAT_DRAFTS, 'readwrite');
373
461
  transaction.objectStore(CHAT_DRAFTS).put(record);
374
462
  await completed(transaction);
375
463
  },
376
464
  async dropChatDraft(conversation) {
377
- const transaction = database.transaction(CHAT_DRAFTS, 'readwrite');
465
+ const transaction = authored.transaction(CHAT_DRAFTS, 'readwrite');
378
466
  transaction.objectStore(CHAT_DRAFTS).delete(conversation);
379
467
  await completed(transaction);
380
468
  },
381
469
  async keepRoomDraft(record) {
382
- const transaction = database.transaction(ROOM_DRAFTS, 'readwrite');
470
+ const transaction = authored.transaction(ROOM_DRAFTS, 'readwrite');
383
471
  transaction.objectStore(ROOM_DRAFTS).put(record);
384
472
  await completed(transaction);
385
473
  },
386
474
  async discardRoomDraft(locator, path) {
387
- const transaction = database.transaction(ROOM_DRAFTS, 'readwrite');
475
+ const transaction = authored.transaction(ROOM_DRAFTS, 'readwrite');
388
476
  transaction.objectStore(ROOM_DRAFTS).delete([locator, path]);
389
477
  await completed(transaction);
390
478
  },
391
479
  async dropRooms(locators) {
392
- const transaction = database.transaction([ROOMS, DOCUMENTS, BINARY_TARGETS, ATTACHMENTS, PENDING_CHANGES, OUTCOMES, ROOM_DRAFTS], 'readwrite');
480
+ const work = authored.transaction([QUEUES, PENDING_CHANGES, ROOM_DRAFTS], 'readwrite');
393
481
  for (const locator of locators) {
394
- transaction.objectStore(ROOMS).delete(locator);
395
- for (const name of [DOCUMENTS, BINARY_TARGETS, ATTACHMENTS, PENDING_CHANGES, ROOM_DRAFTS]) {
396
- 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);
397
485
  }
398
- const outcomes = transaction.objectStore(OUTCOMES);
399
- const ids = outcomes.index('locator').getAllKeys(locator);
400
- ids.onsuccess = () => {
401
- for (const id of ids.result)
402
- outcomes.delete(id);
403
- };
404
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
+ };
405
524
  await completed(transaction);
406
525
  },
407
526
  async generation() {
408
- 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;
409
530
  },
410
- rememberGeneration(value) {
411
- 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);
412
535
  },
413
536
  close() {
537
+ authored.close();
414
538
  database.close();
415
539
  },
416
540
  };