@agentshouse/mdruntime 0.1.0-c1cc928d1d256 → 0.1.0-c2aaa9c0e3ca4

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