@agentshouse/mdruntime 0.1.0-c16fc8caac7ff → 0.1.0-c1c2b722d3dcd

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