@agentshouse/mdruntime 0.1.0-ca9afacd5aebd → 0.1.0-cb0eb0d87ff49

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 (104) hide show
  1. package/AGENTS.md +114 -123
  2. package/dist/adapter.d.ts +26 -3
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/attempt.d.ts.map +1 -1
  5. package/dist/attempt.js +14 -1
  6. package/dist/bootstrap.d.ts +2 -2
  7. package/dist/bootstrap.d.ts.map +1 -1
  8. package/dist/bootstrap.js +28 -6
  9. package/dist/change-set.d.ts +2 -0
  10. package/dist/change-set.d.ts.map +1 -1
  11. package/dist/change-set.js +34 -9
  12. package/dist/chat/acts.d.ts +3 -4
  13. package/dist/chat/acts.d.ts.map +1 -1
  14. package/dist/chat/acts.js +10 -10
  15. package/dist/chat/list.d.ts +6 -0
  16. package/dist/chat/list.d.ts.map +1 -1
  17. package/dist/chat/list.js +6 -3
  18. package/dist/chat/markdown.d.ts +2 -1
  19. package/dist/chat/markdown.d.ts.map +1 -1
  20. package/dist/chat/markdown.js +7 -6
  21. package/dist/chat/message.d.ts.map +1 -1
  22. package/dist/chat/message.js +2 -2
  23. package/dist/chat/provider.d.ts.map +1 -1
  24. package/dist/chat/provider.js +25 -50
  25. package/dist/chat/ready.d.ts.map +1 -1
  26. package/dist/chat/ready.js +2 -2
  27. package/dist/chat/style.js +3 -3
  28. package/dist/client.d.ts +7 -7
  29. package/dist/client.d.ts.map +1 -1
  30. package/dist/client.js +1 -1
  31. package/dist/collection.d.ts +17 -3
  32. package/dist/collection.d.ts.map +1 -1
  33. package/dist/collection.js +230 -19
  34. package/dist/connection.d.ts +8 -5
  35. package/dist/connection.d.ts.map +1 -1
  36. package/dist/connection.js +858 -301
  37. package/dist/conversation.d.ts +12 -7
  38. package/dist/conversation.d.ts.map +1 -1
  39. package/dist/conversation.js +258 -130
  40. package/dist/entry.d.ts +17 -0
  41. package/dist/entry.d.ts.map +1 -0
  42. package/dist/entry.js +156 -0
  43. package/dist/fence.d.ts +6 -0
  44. package/dist/fence.d.ts.map +1 -0
  45. package/dist/fence.js +10 -0
  46. package/dist/host.d.ts +4 -2
  47. package/dist/host.d.ts.map +1 -1
  48. package/dist/host.js +9 -5
  49. package/dist/identity.d.ts +2 -0
  50. package/dist/identity.d.ts.map +1 -1
  51. package/dist/identity.js +6 -0
  52. package/dist/index.d.ts +5 -4
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +1 -0
  55. package/dist/memory.d.ts +1 -0
  56. package/dist/memory.d.ts.map +1 -1
  57. package/dist/memory.js +10 -1
  58. package/dist/model.d.ts +1 -0
  59. package/dist/model.d.ts.map +1 -1
  60. package/dist/model.js +9 -0
  61. package/dist/observation.d.ts +5 -2
  62. package/dist/observation.d.ts.map +1 -1
  63. package/dist/observation.js +22 -11
  64. package/dist/outcome.d.ts +4 -0
  65. package/dist/outcome.d.ts.map +1 -1
  66. package/dist/outcome.js +1 -0
  67. package/dist/overlay.d.ts +1 -0
  68. package/dist/overlay.d.ts.map +1 -1
  69. package/dist/overlay.js +33 -54
  70. package/dist/query.d.ts +28 -3
  71. package/dist/query.d.ts.map +1 -1
  72. package/dist/reach.js +2 -2
  73. package/dist/react.d.ts +3 -3
  74. package/dist/react.d.ts.map +1 -1
  75. package/dist/read-side.d.ts +13 -6
  76. package/dist/read-side.d.ts.map +1 -1
  77. package/dist/read-side.js +282 -83
  78. package/dist/recovery.d.ts +25 -0
  79. package/dist/recovery.d.ts.map +1 -0
  80. package/dist/recovery.js +197 -0
  81. package/dist/refresh.d.ts.map +1 -1
  82. package/dist/refresh.js +11 -10
  83. package/dist/replica.d.ts +1 -1
  84. package/dist/replica.d.ts.map +1 -1
  85. package/dist/replica.js +49 -32
  86. package/dist/rows.d.ts +4 -13
  87. package/dist/rows.d.ts.map +1 -1
  88. package/dist/rows.js +2 -12
  89. package/dist/save.d.ts +6 -2
  90. package/dist/save.d.ts.map +1 -1
  91. package/dist/save.js +42 -18
  92. package/dist/search.d.ts +28 -6
  93. package/dist/search.d.ts.map +1 -1
  94. package/dist/search.js +109 -44
  95. package/dist/shared.d.ts +11 -0
  96. package/dist/shared.d.ts.map +1 -0
  97. package/dist/shared.js +368 -0
  98. package/dist/store.d.ts +35 -10
  99. package/dist/store.d.ts.map +1 -1
  100. package/dist/store.js +275 -74
  101. package/dist/turn.d.ts +7 -0
  102. package/dist/turn.d.ts.map +1 -0
  103. package/dist/turn.js +45 -0
  104. package/package.json +12 -8
package/dist/store.js CHANGED
@@ -1,10 +1,14 @@
1
1
  import { sameBinary, sameChange } from './overlay.js';
2
+ import { differing } from './shared.js';
2
3
  const DATABASE = 'mdruntime';
3
4
  const AUTHORED = 'mdruntime-authored';
5
+ const FENCES = 'fences';
4
6
  const ROOMS = 'rooms';
5
7
  const DOCUMENTS = 'documents';
6
8
  const BINARY_FILES = 'binaryFiles';
7
9
  const TRANSFERS = 'transfers';
10
+ const RECEIVED = 'received';
11
+ const RECEIVED_PARTS = 'receivedParts';
8
12
  const PENDING_CHANGES = 'pendingChanges';
9
13
  const DECLARATIONS = 'declarations';
10
14
  const ROOM_DRAFTS = 'roomDrafts';
@@ -17,16 +21,37 @@ const FACTS = 'facts';
17
21
  const CURRENT = 'current';
18
22
  const MEMBER = 'member';
19
23
  const GENERATION = 'generation';
20
- export const STORE_SCHEMA = 23;
21
- export const AUTHORED_SCHEMA = 1;
24
+ const HOLDER = 'holder';
25
+ const BINDING = 'binding';
26
+ export const STORE_SCHEMA = 25;
27
+ const REPLICA_SHAPE = 23;
28
+ export const AUTHORED_SCHEMA = 2;
22
29
  const AUTHORED_STORES = [
23
30
  [QUEUES, { keyPath: 'locator' }],
24
31
  [PENDING_CHANGES, { keyPath: ['locator', 'id'] }],
25
32
  [ROOM_DRAFTS, { keyPath: ['locator', 'path'] }],
26
33
  [CHAT_DRAFTS, { keyPath: 'conversation' }],
27
34
  [FACTS, undefined],
35
+ [FENCES, { keyPath: 'locator' }],
36
+ ];
37
+ const REPLICA_STORES = [
38
+ [ROOMS, { keyPath: 'locator' }],
39
+ [DOCUMENTS, { keyPath: ['locator', 'transfer', 'path'] }],
40
+ [BINARY_FILES, { keyPath: ['locator', 'transfer', 'path'] }],
41
+ [TRANSFERS, { keyPath: 'locator' }],
42
+ [RECEIVED, { keyPath: 'key' }],
43
+ [RECEIVED_PARTS, { keyPath: ['key', 'index'] }],
44
+ [DECLARATIONS, undefined],
45
+ [PRESENTATION, { keyPath: 'app' }],
46
+ [CONVERSATIONS, { keyPath: 'conversation' }],
47
+ [MESSAGES, { keyPath: ['conversation', 'identity'] }],
28
48
  ];
29
49
  const READ_BATCH = 256;
50
+ const MESSAGE_INDEXES = [
51
+ ['conversation', 'conversation'],
52
+ ['position', ['conversation', 'position']],
53
+ ['turn', ['conversation', 'turn']],
54
+ ];
30
55
  function settled(request) {
31
56
  return new Promise((resolve, reject) => {
32
57
  request.onsuccess = () => resolve(request.result);
@@ -69,6 +94,9 @@ function amendPending(database, locator, id, amendment) {
69
94
  };
70
95
  return completed(transaction);
71
96
  }
97
+ function accepted({ conversation: _held, ...message }) {
98
+ return message;
99
+ }
72
100
  function roomRecord(record) {
73
101
  const { documents, binaries, revisions, changes, written, ...room } = record;
74
102
  return room;
@@ -141,29 +169,29 @@ function binaryFileRecord(locator, transfer, layer, path) {
141
169
  }
142
170
  function writeLayer(transaction, locator, transfer, next, base) {
143
171
  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
172
  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])) {
173
+ const paths = base === null
174
+ ? new Set([...Object.keys(next.documents), ...Object.keys(next.binaries)])
175
+ : new Set([
176
+ ...differing(base.documents, next.documents),
177
+ ...differing(base.revisions, next.revisions),
178
+ ...differing(base.written, next.written),
179
+ ...differing(base.changes, next.changes, sameChange),
180
+ ...differing(base.binaries, next.binaries, sameBinary),
181
+ ]);
182
+ for (const path of paths) {
183
+ if (next.documents[path] !== undefined) {
184
+ documents.put(documentRecord(locator, transfer, next, path));
185
+ }
186
+ else if (base?.documents[path] !== undefined) {
187
+ documents.delete([locator, transfer, path]);
188
+ }
189
+ if (next.binaries[path] !== undefined) {
165
190
  files.put(binaryFileRecord(locator, transfer, next, path));
166
191
  }
192
+ else if (base?.binaries[path] !== undefined) {
193
+ files.delete([locator, transfer, path]);
194
+ }
167
195
  }
168
196
  }
169
197
  function removeRoomEntries(store, locator) {
@@ -182,26 +210,86 @@ function removeDatabase(name) {
182
210
  removal.onerror = () => reject(removal.error);
183
211
  });
184
212
  }
185
- export async function removeRuntimeStore() {
186
- await Promise.all([removeDatabase(DATABASE), removeDatabase(AUTHORED)]);
213
+ export async function authoredHolder() {
214
+ const authored = await openAuthored();
215
+ try {
216
+ const transaction = authored.transaction(FACTS, 'readonly');
217
+ const held = transaction.objectStore(FACTS).get(HOLDER);
218
+ await completed(transaction);
219
+ return typeof held.result === 'string' ? held.result : null;
220
+ }
221
+ finally {
222
+ authored.close();
223
+ }
224
+ }
225
+ export async function authoredBy(holder) {
226
+ const authored = await openAuthored();
227
+ try {
228
+ const transaction = authored.transaction(FACTS, 'readwrite');
229
+ const facts = transaction.objectStore(FACTS);
230
+ const held = facts.get(HOLDER);
231
+ let owned = true;
232
+ held.onsuccess = () => {
233
+ if (held.result === undefined)
234
+ facts.put(holder, HOLDER);
235
+ else
236
+ owned = held.result === holder;
237
+ };
238
+ await completed(transaction);
239
+ return owned;
240
+ }
241
+ finally {
242
+ authored.close();
243
+ }
244
+ }
245
+ export async function authoredBinding() {
246
+ const authored = await openAuthored();
247
+ try {
248
+ const transaction = authored.transaction(FACTS, 'readonly');
249
+ const held = transaction.objectStore(FACTS).get(BINDING);
250
+ await completed(transaction);
251
+ return typeof held.result === 'string' ? held.result : null;
252
+ }
253
+ finally {
254
+ authored.close();
255
+ }
256
+ }
257
+ export async function bindAuthored(binding) {
258
+ const authored = await openAuthored();
259
+ try {
260
+ const transaction = authored.transaction(FACTS, 'readwrite');
261
+ transaction.objectStore(FACTS).put(binding, BINDING);
262
+ await completed(transaction);
263
+ }
264
+ finally {
265
+ authored.close();
266
+ }
267
+ }
268
+ export async function removeRuntimeStore(holder) {
269
+ await removeDatabase(DATABASE);
270
+ if (holder !== undefined && (await authoredBy(holder)))
271
+ return true;
272
+ await removeDatabase(AUTHORED);
273
+ return false;
187
274
  }
188
275
  function openReplica() {
189
276
  const opening = indexedDB.open(DATABASE, STORE_SCHEMA);
190
- opening.onupgradeneeded = () => {
277
+ opening.onupgradeneeded = (event) => {
191
278
  const database = opening.result;
192
- for (const name of [...database.objectStoreNames])
193
- database.deleteObjectStore(name);
194
- database.createObjectStore(ROOMS, { keyPath: 'locator' });
195
- database.createObjectStore(DOCUMENTS, { keyPath: ['locator', 'transfer', 'path'] });
196
- database.createObjectStore(BINARY_FILES, { keyPath: ['locator', 'transfer', 'path'] });
197
- database.createObjectStore(TRANSFERS, { keyPath: 'locator' });
198
- database.createObjectStore(DECLARATIONS);
199
- database.createObjectStore(PRESENTATION, { keyPath: 'app' });
200
- database.createObjectStore(CONVERSATIONS, { keyPath: 'conversation' });
201
- const messages = database.createObjectStore(MESSAGES, {
202
- keyPath: ['conversation', 'identity'],
203
- });
204
- messages.createIndex('conversation', 'conversation');
279
+ if (event.oldVersion < REPLICA_SHAPE) {
280
+ for (const name of [...database.objectStoreNames])
281
+ database.deleteObjectStore(name);
282
+ }
283
+ for (const [name, parameters] of REPLICA_STORES) {
284
+ if (database.objectStoreNames.contains(name))
285
+ continue;
286
+ database.createObjectStore(name, parameters);
287
+ }
288
+ const messages = opening.transaction.objectStore(MESSAGES);
289
+ for (const [name, keyPath] of MESSAGE_INDEXES) {
290
+ if (!messages.indexNames.contains(name))
291
+ messages.createIndex(name, keyPath);
292
+ }
205
293
  };
206
294
  return settled(opening);
207
295
  }
@@ -216,7 +304,7 @@ function openAuthored() {
216
304
  };
217
305
  return settled(opening);
218
306
  }
219
- export async function openRuntimeStore() {
307
+ export async function openRuntimeStore(fenced) {
220
308
  const authored = await openAuthored();
221
309
  const database = await openReplica().catch((error) => {
222
310
  authored.close();
@@ -227,12 +315,14 @@ export async function openRuntimeStore() {
227
315
  const transaction = database.transaction([ROOMS, DOCUMENTS, BINARY_FILES], 'readonly');
228
316
  const rooms = await settled(transaction.objectStore(ROOMS).getAll());
229
317
  const records = [];
230
- for (const room of rooms) {
318
+ for (const room of rooms.filter((held) => !fenced(held.locator))) {
231
319
  records.push({ ...room, ...(await readLayer(transaction, room.locator, room.transfer)) });
232
320
  }
233
321
  return records;
234
322
  },
235
323
  async keepAuthored({ locator, queue, saved, roomDrafts, consumedRoomDrafts }) {
324
+ if (fenced(locator))
325
+ return;
236
326
  const transaction = authored.transaction([QUEUES, PENDING_CHANGES, ROOM_DRAFTS], 'readwrite');
237
327
  const order = transaction.objectStore(QUEUES);
238
328
  const pending = transaction.objectStore(PENDING_CHANGES);
@@ -269,7 +359,7 @@ export async function openRuntimeStore() {
269
359
  await completed(transaction);
270
360
  },
271
361
  async write({ next, previous, stored }) {
272
- if (next === previous)
362
+ if (next === previous || fenced(next.locator))
273
363
  return;
274
364
  const transaction = database.transaction([ROOMS, DOCUMENTS, BINARY_FILES, TRANSFERS], 'readwrite');
275
365
  transaction.objectStore(ROOMS).put(roomRecord(next));
@@ -289,6 +379,8 @@ export async function openRuntimeStore() {
289
379
  const rooms = transaction.objectStore(ROOMS);
290
380
  const revoked = { status: 'unavailable', reason: 'revoked' };
291
381
  for (const record of records) {
382
+ if (fenced(record.locator))
383
+ continue;
292
384
  rooms.put(roomRecord({ ...record, model: revoked, selectionModel: revoked }));
293
385
  }
294
386
  await completed(transaction);
@@ -329,7 +421,9 @@ export async function openRuntimeStore() {
329
421
  settled(transaction.objectStore(PENDING_CHANGES).getAll()),
330
422
  ]);
331
423
  const byId = new Map(pending.map((record) => [`${record.locator}\0${record.id}`, record]));
332
- return queues.map((queue) => ({
424
+ return queues
425
+ .filter((queue) => !fenced(queue.locator))
426
+ .map((queue) => ({
333
427
  locator: queue.locator,
334
428
  pending: queue.order.map((id) => {
335
429
  const record = byId.get(`${queue.locator}\0${id}`);
@@ -340,18 +434,22 @@ export async function openRuntimeStore() {
340
434
  }),
341
435
  }));
342
436
  },
343
- submit(locator, id, submission, lost) {
344
- return amendPending(authored, locator, id, { submission, lost });
437
+ async submit(locator, id, submission, lost) {
438
+ if (!fenced(locator))
439
+ await amendPending(authored, locator, id, { submission, lost });
345
440
  },
346
- accept(locator, id, roomVersion) {
347
- return amendPending(authored, locator, id, { accepted: roomVersion });
441
+ async accept(locator, id, roomVersion) {
442
+ if (!fenced(locator))
443
+ await amendPending(authored, locator, id, { accepted: roomVersion });
348
444
  },
349
- keepUpload(locator, id, upload) {
350
- return amendPending(authored, locator, id, { upload });
445
+ async keepUpload(locator, id, upload) {
446
+ if (!fenced(locator))
447
+ await amendPending(authored, locator, id, { upload });
351
448
  },
352
449
  async roomDrafts() {
353
450
  const transaction = authored.transaction(ROOM_DRAFTS, 'readonly');
354
- return settled(transaction.objectStore(ROOM_DRAFTS).getAll());
451
+ const held = await settled(transaction.objectStore(ROOM_DRAFTS).getAll());
452
+ return held.filter((record) => !fenced(record.locator));
355
453
  },
356
454
  async presentation() {
357
455
  const transaction = database.transaction(PRESENTATION, 'readonly');
@@ -364,39 +462,91 @@ export async function openRuntimeStore() {
364
462
  },
365
463
  async conversations() {
366
464
  const transaction = database.transaction([CONVERSATIONS, MESSAGES], 'readonly');
367
- const [reads, messages] = await Promise.all([
368
- settled(transaction.objectStore(CONVERSATIONS).getAll()),
369
- settled(transaction.objectStore(MESSAGES).getAll()),
370
- ]);
371
- const held = new Map();
372
- for (const { conversation, read } of reads) {
373
- held.set(conversation, { conversation, read, messages: [] });
374
- }
375
- for (const { conversation, ...message } of messages) {
376
- const entry = held.get(conversation) ?? { conversation, read: undefined, messages: [] };
377
- entry.messages.push(message);
378
- held.set(conversation, entry);
379
- }
465
+ const records = await settled(transaction.objectStore(CONVERSATIONS).getAll());
466
+ const held = new Map(records.map(({ conversation, read, partial }) => [
467
+ conversation,
468
+ { conversation, read, partial: partial === true },
469
+ ]));
470
+ const keys = transaction.objectStore(MESSAGES).index('conversation');
471
+ await new Promise((resolve, reject) => {
472
+ const cursor = keys.openKeyCursor(null, 'nextunique');
473
+ cursor.onerror = () => reject(cursor.error);
474
+ cursor.onsuccess = () => {
475
+ const at = cursor.result;
476
+ if (at === null) {
477
+ resolve();
478
+ return;
479
+ }
480
+ const conversation = at.key;
481
+ if (!held.has(conversation)) {
482
+ held.set(conversation, { conversation, read: undefined, partial: true });
483
+ }
484
+ at.continue();
485
+ };
486
+ });
380
487
  return [...held.values()];
381
488
  },
382
- async keepMessages({ conversation, messages, read, sealed }) {
383
- if (sealed === true) {
384
- const sealing = authored.transaction(CHAT_DRAFTS, 'readwrite');
385
- sealing.objectStore(CHAT_DRAFTS).delete(conversation);
386
- await completed(sealing);
387
- }
489
+ async keepMessages({ conversation, messages, read }) {
388
490
  const transaction = database.transaction([CONVERSATIONS, MESSAGES], 'readwrite');
389
491
  const stored = transaction.objectStore(MESSAGES);
390
492
  for (const message of messages) {
391
493
  stored.put({ ...message, conversation });
392
494
  }
393
495
  if (read !== undefined) {
394
- transaction
395
- .objectStore(CONVERSATIONS)
396
- .put({ conversation, read });
496
+ transaction.objectStore(CONVERSATIONS).put({
497
+ conversation,
498
+ read: read.at,
499
+ ...(read.partial ? { partial: true } : {}),
500
+ });
397
501
  }
398
502
  await completed(transaction);
399
503
  },
504
+ async messageWindow(conversation, limit) {
505
+ const transaction = database.transaction(MESSAGES, 'readonly');
506
+ const index = transaction.objectStore(MESSAGES).index('position');
507
+ const range = IDBKeyRange.bound([conversation], [conversation, []]);
508
+ if (limit === undefined) {
509
+ return (await settled(index.getAll(range))).map(accepted);
510
+ }
511
+ const latest = [];
512
+ if (limit === 0)
513
+ return latest;
514
+ await new Promise((resolve, reject) => {
515
+ const cursor = index.openCursor(range, 'prev');
516
+ cursor.onerror = () => reject(cursor.error);
517
+ cursor.onsuccess = () => {
518
+ const at = cursor.result;
519
+ if (at === null) {
520
+ resolve();
521
+ return;
522
+ }
523
+ latest.push(accepted(at.value));
524
+ if (latest.length === limit)
525
+ resolve();
526
+ else
527
+ at.continue();
528
+ };
529
+ });
530
+ return latest.reverse();
531
+ },
532
+ async messagesAfter(conversation, identity) {
533
+ const transaction = database.transaction(MESSAGES, 'readonly');
534
+ const range = identity === null
535
+ ? IDBKeyRange.bound([conversation], [conversation, []])
536
+ : IDBKeyRange.bound([conversation, identity], [conversation, []], true);
537
+ const held = await settled(transaction.objectStore(MESSAGES).getAll(range, READ_BATCH));
538
+ return held.map(accepted);
539
+ },
540
+ async messageIdentities(conversation) {
541
+ const transaction = database.transaction(MESSAGES, 'readonly');
542
+ const keys = await settled(transaction.objectStore(MESSAGES).index('conversation').getAllKeys(conversation));
543
+ return keys.map((key) => key[1]);
544
+ },
545
+ async turnArrived(conversation, turn) {
546
+ const transaction = database.transaction(MESSAGES, 'readonly');
547
+ const held = await settled(transaction.objectStore(MESSAGES).index('turn').count([conversation, turn]));
548
+ return held > 0;
549
+ },
400
550
  async dropConversations(conversations) {
401
551
  const drafts = authored.transaction(CHAT_DRAFTS, 'readwrite');
402
552
  for (const conversation of conversations)
@@ -430,18 +580,28 @@ export async function openRuntimeStore() {
430
580
  await completed(transaction);
431
581
  },
432
582
  async keepRoomDraft(record) {
583
+ if (fenced(record.locator))
584
+ return;
433
585
  const transaction = authored.transaction(ROOM_DRAFTS, 'readwrite');
434
586
  transaction.objectStore(ROOM_DRAFTS).put(record);
435
587
  await completed(transaction);
436
588
  },
437
589
  async discardRoomDraft(locator, path) {
590
+ if (fenced(locator))
591
+ return;
438
592
  const transaction = authored.transaction(ROOM_DRAFTS, 'readwrite');
439
593
  transaction.objectStore(ROOM_DRAFTS).delete([locator, path]);
440
594
  await completed(transaction);
441
595
  },
442
- async dropRooms(locators) {
443
- const work = authored.transaction([QUEUES, PENDING_CHANGES, ROOM_DRAFTS], 'readwrite');
596
+ async fences() {
597
+ const transaction = authored.transaction(FENCES, 'readonly');
598
+ const held = await settled(transaction.objectStore(FENCES).getAll());
599
+ return held.map((fence) => fence.locator);
600
+ },
601
+ async erase(locators) {
602
+ const work = authored.transaction([FENCES, QUEUES, PENDING_CHANGES, ROOM_DRAFTS], 'readwrite');
444
603
  for (const locator of locators) {
604
+ work.objectStore(FENCES).put({ locator });
445
605
  work.objectStore(QUEUES).delete(locator);
446
606
  for (const name of [PENDING_CHANGES, ROOM_DRAFTS]) {
447
607
  removeRoomEntries(work.objectStore(name), locator);
@@ -456,22 +616,37 @@ export async function openRuntimeStore() {
456
616
  transaction.objectStore(BINARY_FILES).delete(underRoom(locator));
457
617
  }
458
618
  await completed(transaction);
619
+ const fences = authored.transaction(FENCES, 'readwrite');
620
+ for (const locator of locators)
621
+ fences.objectStore(FENCES).delete(locator);
622
+ await completed(fences);
623
+ },
624
+ async discard(locator) {
625
+ const transaction = authored.transaction([QUEUES, PENDING_CHANGES], 'readwrite');
626
+ transaction.objectStore(QUEUES).delete(locator);
627
+ removeRoomEntries(transaction.objectStore(PENDING_CHANGES), locator);
628
+ await completed(transaction);
459
629
  },
460
630
  async transfers() {
461
631
  const transaction = database.transaction(TRANSFERS, 'readonly');
462
- return settled(transaction.objectStore(TRANSFERS).getAll());
632
+ const held = await settled(transaction.objectStore(TRANSFERS).getAll());
633
+ return held.filter((record) => !fenced(record.locator));
463
634
  },
464
635
  staged(record) {
465
636
  const transaction = database.transaction([DOCUMENTS, BINARY_FILES], 'readonly');
466
637
  return readLayer(transaction, record.locator, record.transfer);
467
638
  },
468
639
  async stage(record, page) {
640
+ if (fenced(record.locator))
641
+ return;
469
642
  const transaction = database.transaction([TRANSFERS, DOCUMENTS, BINARY_FILES], 'readwrite');
470
643
  writeLayer(transaction, record.locator, record.transfer, page, null);
471
644
  transaction.objectStore(TRANSFERS).put(record);
472
645
  await completed(transaction);
473
646
  },
474
647
  async abandon(locator) {
648
+ if (fenced(locator))
649
+ return;
475
650
  const transaction = database.transaction([TRANSFERS, DOCUMENTS, BINARY_FILES], 'readwrite');
476
651
  const transfers = transaction.objectStore(TRANSFERS);
477
652
  const held = transfers.get(locator);
@@ -486,6 +661,32 @@ export async function openRuntimeStore() {
486
661
  };
487
662
  await completed(transaction);
488
663
  },
664
+ async receivedHeads() {
665
+ const transaction = database.transaction(RECEIVED, 'readonly');
666
+ return settled(transaction.objectStore(RECEIVED).getAll());
667
+ },
668
+ async receivedParts(key, count) {
669
+ const transaction = database.transaction(RECEIVED_PARTS, 'readonly');
670
+ const kept = await settled(transaction
671
+ .objectStore(RECEIVED_PARTS)
672
+ .getAll(IDBKeyRange.bound([key, 0], [key, count], false, true)));
673
+ return kept.map((part) => part.value);
674
+ },
675
+ async keepReceived(key, head, part) {
676
+ const transaction = database.transaction([RECEIVED, RECEIVED_PARTS], 'readwrite');
677
+ transaction.objectStore(RECEIVED).put({ key, head });
678
+ if (part !== undefined)
679
+ transaction.objectStore(RECEIVED_PARTS).put({ key, ...part });
680
+ await completed(transaction);
681
+ },
682
+ async dropReceived(keys) {
683
+ const transaction = database.transaction([RECEIVED, RECEIVED_PARTS], 'readwrite');
684
+ for (const key of keys) {
685
+ transaction.objectStore(RECEIVED).delete(key);
686
+ transaction.objectStore(RECEIVED_PARTS).delete(IDBKeyRange.bound([key], [key, []]));
687
+ }
688
+ await completed(transaction);
689
+ },
489
690
  async generation() {
490
691
  const transaction = authored.transaction(FACTS, 'readonly');
491
692
  const held = await settled(transaction.objectStore(FACTS).get(GENERATION));
package/dist/turn.d.ts ADDED
@@ -0,0 +1,7 @@
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) => R): Later<R>;
5
+ export declare function inUnits<T>(items: Iterable<T>, each: (item: T) => number): Later<void>;
6
+ export declare function inChunks<T>(items: readonly T[], weigh: (item: T) => number, each: (chunk: readonly T[]) => void): Promise<void>;
7
+ //# 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,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAE5E;AAaD,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAOrF;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,IAAI,GAClC,OAAO,CAAC,IAAI,CAAC,CAUf"}
package/dist/turn.js ADDED
@@ -0,0 +1,45 @@
1
+ export function yielded() {
2
+ return new Promise((resolve) => {
3
+ const channel = new MessageChannel();
4
+ channel.port1.onmessage = () => {
5
+ channel.port1.close();
6
+ resolve();
7
+ };
8
+ channel.port2.postMessage(null);
9
+ });
10
+ }
11
+ const UNIT = 1 << 20;
12
+ export const ROW_WEIGHT = 256;
13
+ export function after(value, next) {
14
+ return value instanceof Promise ? value.then(next) : next(value);
15
+ }
16
+ async function resumed(iterator, each) {
17
+ let spent = UNIT;
18
+ for (let next = iterator.next(); next.done !== true; next = iterator.next()) {
19
+ if (spent >= UNIT) {
20
+ await yielded();
21
+ spent = 0;
22
+ }
23
+ spent += each(next.value);
24
+ }
25
+ }
26
+ export function inUnits(items, each) {
27
+ const iterator = items[Symbol.iterator]();
28
+ let spent = 0;
29
+ for (let next = iterator.next(); next.done !== true; next = iterator.next()) {
30
+ spent += each(next.value);
31
+ if (spent >= UNIT)
32
+ return resumed(iterator, each);
33
+ }
34
+ }
35
+ export async function inChunks(items, weigh, each) {
36
+ for (let at = 0; at < items.length;) {
37
+ await yielded();
38
+ const chunk = [];
39
+ for (let spent = 0; at < items.length && spent < UNIT; at += 1) {
40
+ chunk.push(items[at]);
41
+ spent += weigh(items[at]);
42
+ }
43
+ each(chunk);
44
+ }
45
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentshouse/mdruntime",
3
- "version": "0.1.0-ca9afacd5aebd",
3
+ "version": "0.1.0-cb0eb0d87ff49",
4
4
  "description": "The headless core of MD App",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,27 +14,31 @@
14
14
  "exports": {
15
15
  ".": {
16
16
  "types": "./dist/index.d.ts",
17
- "import": "./dist/index.js"
17
+ "default": "./dist/index.js"
18
18
  },
19
19
  "./host": {
20
20
  "types": "./dist/host.d.ts",
21
- "import": "./dist/host.js"
21
+ "default": "./dist/host.js"
22
22
  },
23
23
  "./client": {
24
24
  "types": "./dist/client.d.ts",
25
- "import": "./dist/client.js"
25
+ "default": "./dist/client.js"
26
+ },
27
+ "./entry": {
28
+ "types": "./dist/entry.d.ts",
29
+ "default": "./dist/entry.js"
26
30
  },
27
31
  "./memory": {
28
32
  "types": "./dist/memory.d.ts",
29
- "import": "./dist/memory.js"
33
+ "default": "./dist/memory.js"
30
34
  },
31
35
  "./react": {
32
36
  "types": "./dist/react.d.ts",
33
- "import": "./dist/react.js"
37
+ "default": "./dist/react.js"
34
38
  },
35
39
  "./chat": {
36
40
  "types": "./dist/chat.d.ts",
37
- "import": "./dist/chat.js"
41
+ "default": "./dist/chat.js"
38
42
  },
39
43
  "./package.json": "./package.json"
40
44
  },
@@ -45,7 +49,7 @@
45
49
  "LICENSE"
46
50
  ],
47
51
  "dependencies": {
48
- "@agentshouse/mdmodel": "0.1.0-ce29d61d6f303",
52
+ "@agentshouse/mdmodel": "0.1.0-cad192ab63d5f",
49
53
  "@assistant-ui/react-markdown": "0.14.20",
50
54
  "@tanstack/db": "0.9.2",
51
55
  "lucide-react": "1.47.0",