@agentshouse/mdruntime 0.1.0-alpha.20 → 0.1.0-alpha.21
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.
- package/dist/change-set.d.ts +33 -0
- package/dist/change-set.d.ts.map +1 -0
- package/dist/change-set.js +74 -0
- package/dist/change-set.js.map +1 -0
- package/dist/collection.d.ts +8 -2
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +8 -3
- package/dist/collection.js.map +1 -1
- package/dist/connection.d.ts +1 -0
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +240 -155
- package/dist/connection.js.map +1 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +1 -0
- package/dist/host.js.map +1 -1
- package/dist/overlay.d.ts +2 -0
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +106 -0
- package/dist/overlay.js.map +1 -1
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +1 -1
- package/dist/refresh.js.map +1 -1
- package/dist/replica.d.ts +12 -3
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +61 -26
- package/dist/replica.js.map +1 -1
- package/dist/store.d.ts +24 -5
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +289 -87
- package/dist/store.js.map +1 -1
- package/package.json +1 -1
package/dist/connection.js
CHANGED
|
@@ -2,6 +2,7 @@ import { rawQuery } from './query.js';
|
|
|
2
2
|
import { cached, modelTypes, NO_MODEL, sameModel } from './model.js';
|
|
3
3
|
import { ROOM_CONTRACT_VERSION, VERSION_1_CAPABILITIES } from './adapter.js';
|
|
4
4
|
import { bootstrapRoom } from './bootstrap.js';
|
|
5
|
+
import { changeSet, mergeChangeSets } from './change-set.js';
|
|
5
6
|
import { createConversationStore } from './conversation.js';
|
|
6
7
|
import { MD_MODEL, PRESENTED, validRoot } from './collection.js';
|
|
7
8
|
import { createLog } from './log.js';
|
|
@@ -142,10 +143,6 @@ export function createMdRuntime(options) {
|
|
|
142
143
|
});
|
|
143
144
|
return entries;
|
|
144
145
|
}
|
|
145
|
-
async function readDrafts(opened) {
|
|
146
|
-
await hydrate(opened);
|
|
147
|
-
return snapshotDrafts();
|
|
148
|
-
}
|
|
149
146
|
function publishDrafts(waitingOnly) {
|
|
150
147
|
if (closed) return;
|
|
151
148
|
const value = snapshotDrafts();
|
|
@@ -171,13 +168,15 @@ export function createMdRuntime(options) {
|
|
|
171
168
|
if (listener.app === app && listener.ready) listener.notify(presentationOf(app));
|
|
172
169
|
}
|
|
173
170
|
}
|
|
174
|
-
function commitDraft(work) {
|
|
171
|
+
function commitDraft(locator, path, work) {
|
|
175
172
|
return exclusive(async ()=>{
|
|
176
173
|
const local = await openLocal();
|
|
177
|
-
await hydrate(local);
|
|
178
174
|
await work(local);
|
|
179
|
-
|
|
180
|
-
|
|
175
|
+
publishChangeSet(changeSet(locator, null, null, {
|
|
176
|
+
drafts: [
|
|
177
|
+
path
|
|
178
|
+
]
|
|
179
|
+
}));
|
|
181
180
|
});
|
|
182
181
|
}
|
|
183
182
|
function pending() {
|
|
@@ -214,19 +213,13 @@ export function createMdRuntime(options) {
|
|
|
214
213
|
reset
|
|
215
214
|
};
|
|
216
215
|
}
|
|
217
|
-
function
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
async function persistQueue(local, locator, batches) {
|
|
226
|
-
await local.writeQueue({
|
|
227
|
-
locator,
|
|
228
|
-
batches,
|
|
229
|
-
outcomes: storedOutcomes(batches)
|
|
216
|
+
async function persistQueue(local, replica, batches, changedOutcomes = []) {
|
|
217
|
+
await local.write({
|
|
218
|
+
next: replica.confirmed.record,
|
|
219
|
+
previous: replica.confirmed.record,
|
|
220
|
+
queue: batches,
|
|
221
|
+
previousQueue: replica.queue,
|
|
222
|
+
outcomes: changedOutcomes
|
|
230
223
|
});
|
|
231
224
|
}
|
|
232
225
|
function listed() {
|
|
@@ -308,6 +301,13 @@ export function createMdRuntime(options) {
|
|
|
308
301
|
if (subscription.locator === locator) subscription.notify(entries);
|
|
309
302
|
}
|
|
310
303
|
}
|
|
304
|
+
function publishChangeSet(set) {
|
|
305
|
+
if (set.rooms.length > 0) emitRooms();
|
|
306
|
+
if (set.documents.length > 0 || set.query) emitVisible();
|
|
307
|
+
for (const locator of set.queues)emitQueue(locator);
|
|
308
|
+
if (set.drafts.length > 0) emitDrafts();
|
|
309
|
+
for (const app of set.presentations)emitPresentation(app);
|
|
310
|
+
}
|
|
311
311
|
function stopTimer() {
|
|
312
312
|
if (timer === undefined) return;
|
|
313
313
|
clearInterval(timer);
|
|
@@ -319,55 +319,76 @@ export function createMdRuntime(options) {
|
|
|
319
319
|
void enqueue();
|
|
320
320
|
}, period);
|
|
321
321
|
}
|
|
322
|
-
function refuse(reason, provider = null) {
|
|
323
|
-
if (reason === 'revoked-authorization') {
|
|
324
|
-
const revoked = {
|
|
325
|
-
status: 'unavailable',
|
|
326
|
-
reason: 'revoked'
|
|
327
|
-
};
|
|
328
|
-
const attachments = [
|
|
329
|
-
'collection',
|
|
330
|
-
'typed-selection'
|
|
331
|
-
];
|
|
332
|
-
const keys = new Set(models.keys());
|
|
333
|
-
for (const locator of rooms.keys()){
|
|
334
|
-
for (const attached of attachments)keys.add(modelKey(locator, attached));
|
|
335
|
-
}
|
|
336
|
-
for (const key of keys){
|
|
337
|
-
invalidModels.add(key);
|
|
338
|
-
modelEpoch.set(key, (modelEpoch.get(key) ?? 0) + 1);
|
|
339
|
-
models.set(key, revoked);
|
|
340
|
-
liveModels.delete(key);
|
|
341
|
-
}
|
|
342
|
-
for (const [locator, room] of rooms)rooms.set(locator, {
|
|
343
|
-
...room,
|
|
344
|
-
model: modelTypes(revoked),
|
|
345
|
-
typedSelection: room.typedSelection === null ? null : {
|
|
346
|
-
...room.typedSelection,
|
|
347
|
-
model: modelTypes(revoked)
|
|
348
|
-
}
|
|
349
|
-
});
|
|
350
|
-
emitRooms();
|
|
351
|
-
void exclusive(async ()=>{
|
|
352
|
-
const local = await openLocal();
|
|
353
|
-
for (const record of (await local.all()))await local.publish({
|
|
354
|
-
...record,
|
|
355
|
-
model: revoked,
|
|
356
|
-
selectionModel: revoked
|
|
357
|
-
});
|
|
358
|
-
}).catch(()=>{
|
|
359
|
-
journal.record('model invalidation durable-write failure');
|
|
360
|
-
});
|
|
361
|
-
}
|
|
322
|
+
async function refuse(reason, provider = null) {
|
|
362
323
|
stopTimer();
|
|
363
324
|
discoveryPaused = true;
|
|
364
|
-
|
|
365
|
-
reason
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
325
|
+
try {
|
|
326
|
+
if (reason === 'revoked-authorization') {
|
|
327
|
+
await exclusive(async ()=>{
|
|
328
|
+
const revoked = {
|
|
329
|
+
status: 'unavailable',
|
|
330
|
+
reason: 'revoked'
|
|
331
|
+
};
|
|
332
|
+
try {
|
|
333
|
+
const local = await openLocal();
|
|
334
|
+
await local.revokeModels([
|
|
335
|
+
...replicas.values()
|
|
336
|
+
].map((replica)=>({
|
|
337
|
+
record: replica.confirmed.record,
|
|
338
|
+
queue: replica.queue
|
|
339
|
+
})));
|
|
340
|
+
} catch {
|
|
341
|
+
journal.record('model revocation persistence failed');
|
|
342
|
+
}
|
|
343
|
+
const keys = new Set(models.keys());
|
|
344
|
+
for (const locator of rooms.keys()){
|
|
345
|
+
keys.add(modelKey(locator, 'collection'));
|
|
346
|
+
keys.add(modelKey(locator, 'typed-selection'));
|
|
347
|
+
}
|
|
348
|
+
for (const key of keys){
|
|
349
|
+
invalidModels.add(key);
|
|
350
|
+
modelEpoch.set(key, (modelEpoch.get(key) ?? 0) + 1);
|
|
351
|
+
models.set(key, revoked);
|
|
352
|
+
liveModels.delete(key);
|
|
353
|
+
}
|
|
354
|
+
const changed = [];
|
|
355
|
+
for (const [locator, room] of rooms){
|
|
356
|
+
const before = replicas.get(locator) ?? null;
|
|
357
|
+
if (before !== null) {
|
|
358
|
+
const record = {
|
|
359
|
+
...before.confirmed.record,
|
|
360
|
+
model: revoked,
|
|
361
|
+
selectionModel: revoked
|
|
362
|
+
};
|
|
363
|
+
const confirmed = readConfirmed(record, before.confirmed);
|
|
364
|
+
if (confirmed === null) throw new Error(`Cannot retype revoked Room ${locator}`);
|
|
365
|
+
replicas.set(locator, replicaOf(confirmed, before.queue, rewriting(), before));
|
|
366
|
+
}
|
|
367
|
+
rooms.set(locator, {
|
|
368
|
+
...room,
|
|
369
|
+
model: modelTypes(revoked),
|
|
370
|
+
typedSelection: room.typedSelection === null ? null : {
|
|
371
|
+
...room.typedSelection,
|
|
372
|
+
model: modelTypes(revoked)
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
changed.push(changeSet(locator, before, replicas.get(locator) ?? null, {
|
|
376
|
+
room: true,
|
|
377
|
+
query: true
|
|
378
|
+
}));
|
|
379
|
+
}
|
|
380
|
+
publishChangeSet(mergeChangeSets(changed));
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
} finally{
|
|
384
|
+
refusal = {
|
|
385
|
+
reason,
|
|
386
|
+
reauthorizationOffered: reason === 'revoked-authorization',
|
|
387
|
+
refusal: provider
|
|
388
|
+
};
|
|
389
|
+
journal.record(`connection refused ${reason}`);
|
|
390
|
+
emitRefusal();
|
|
391
|
+
}
|
|
371
392
|
}
|
|
372
393
|
function list(room, synchronizationAvailable) {
|
|
373
394
|
const selection = room.typedSelection ?? null;
|
|
@@ -417,7 +438,6 @@ export function createMdRuntime(options) {
|
|
|
417
438
|
async function dropUnlisted(listed, local) {
|
|
418
439
|
if (unlistedHeld(listed).length === 0) return;
|
|
419
440
|
await exclusive(async ()=>{
|
|
420
|
-
await hydrate(local);
|
|
421
441
|
const unlisted = unlistedHeld(listed);
|
|
422
442
|
await local.dropRooms(unlisted);
|
|
423
443
|
for (const [batch, locator] of acceptedIn){
|
|
@@ -425,6 +445,14 @@ export function createMdRuntime(options) {
|
|
|
425
445
|
acceptedIn.delete(batch);
|
|
426
446
|
outcomes.delete(batch);
|
|
427
447
|
}
|
|
448
|
+
const removed = unlisted.map((locator)=>changeSet(locator, replicas.get(locator) ?? null, null, {
|
|
449
|
+
room: true,
|
|
450
|
+
queue: true,
|
|
451
|
+
query: true,
|
|
452
|
+
drafts: [
|
|
453
|
+
...drafts.get(locator)?.keys() ?? []
|
|
454
|
+
]
|
|
455
|
+
}));
|
|
428
456
|
for (const locator of unlisted){
|
|
429
457
|
for (const batch of replicas.get(locator)?.queue ?? [])outcomes.delete(batch.id);
|
|
430
458
|
replicas.delete(locator);
|
|
@@ -444,9 +472,7 @@ export function createMdRuntime(options) {
|
|
|
444
472
|
}
|
|
445
473
|
journal.record(`room unlisted ${locator}`);
|
|
446
474
|
}
|
|
447
|
-
|
|
448
|
-
for (const locator of unlisted)emitQueue(locator);
|
|
449
|
-
emitDrafts();
|
|
475
|
+
publishChangeSet(mergeChangeSets(removed));
|
|
450
476
|
moveTo({
|
|
451
477
|
...status,
|
|
452
478
|
...attention()
|
|
@@ -468,9 +494,10 @@ export function createMdRuntime(options) {
|
|
|
468
494
|
return model;
|
|
469
495
|
}
|
|
470
496
|
async function hydrate(opened) {
|
|
471
|
-
const [records, queues, kept, presented, cached, unsent, lastCompleted, declared, named, listedSlots] = await Promise.all([
|
|
497
|
+
const [records, queues, recordedOutcomes, kept, presented, cached, unsent, lastCompleted, declared, named, listedSlots] = await Promise.all([
|
|
472
498
|
opened.all(),
|
|
473
499
|
opened.queues(),
|
|
500
|
+
opened.outcomes(),
|
|
474
501
|
opened.drafts(),
|
|
475
502
|
opened.presentation(),
|
|
476
503
|
opened.conversations(),
|
|
@@ -492,22 +519,25 @@ export function createMdRuntime(options) {
|
|
|
492
519
|
queue.locator,
|
|
493
520
|
queue.batches
|
|
494
521
|
]));
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
522
|
+
outcomes.clear();
|
|
523
|
+
acceptedIn.clear();
|
|
524
|
+
for (const record of recordedOutcomes){
|
|
525
|
+
outcomes.set(record.id, record.outcome);
|
|
526
|
+
if (record.outcome.kind === 'accepted') acceptedIn.set(record.id, record.locator);
|
|
499
527
|
}
|
|
528
|
+
const previousReplicas = new Map(replicas);
|
|
500
529
|
replicas.clear();
|
|
501
530
|
for (const record of records){
|
|
502
531
|
const model = restored(modelKey(record.locator, 'collection'), record.model);
|
|
503
532
|
const selectionModel = restored(modelKey(record.locator, 'typed-selection'), record.selectionModel);
|
|
533
|
+
const prior = previousReplicas.get(record.locator);
|
|
504
534
|
const confirmed = readConfirmed({
|
|
505
535
|
...record,
|
|
506
536
|
model,
|
|
507
537
|
selectionModel
|
|
508
|
-
});
|
|
538
|
+
}, prior?.confirmed);
|
|
509
539
|
if (confirmed === null) continue;
|
|
510
|
-
replicas.set(record.locator, replicaOf(confirmed, queuedBatches.get(record.locator) ?? [], rewriting()));
|
|
540
|
+
replicas.set(record.locator, replicaOf(confirmed, queuedBatches.get(record.locator) ?? [], rewriting(), prior));
|
|
511
541
|
const existing = rooms.get(record.locator);
|
|
512
542
|
if (existing === undefined) list(record, false);
|
|
513
543
|
else {
|
|
@@ -568,7 +598,6 @@ export function createMdRuntime(options) {
|
|
|
568
598
|
function durably(work) {
|
|
569
599
|
return exclusive(async ()=>{
|
|
570
600
|
const local = await openLocal();
|
|
571
|
-
await hydrate(local);
|
|
572
601
|
return work(local);
|
|
573
602
|
});
|
|
574
603
|
}
|
|
@@ -586,25 +615,30 @@ export function createMdRuntime(options) {
|
|
|
586
615
|
}
|
|
587
616
|
function publish(local, room, confirmed) {
|
|
588
617
|
return exclusive(async ()=>{
|
|
589
|
-
|
|
618
|
+
const prior = replicas.get(room.locator);
|
|
590
619
|
const retained = readConfirmed({
|
|
591
620
|
...confirmed.record,
|
|
592
621
|
model: modelOf(room.locator),
|
|
593
622
|
selectionModel: modelOf(room.locator, 'typed-selection')
|
|
594
|
-
});
|
|
623
|
+
}, prior?.confirmed);
|
|
595
624
|
if (retained !== null) {
|
|
596
|
-
await local.
|
|
625
|
+
await local.write({
|
|
626
|
+
next: retained.record,
|
|
627
|
+
previous: prior?.confirmed.record ?? null,
|
|
628
|
+
queue: prior?.queue ?? []
|
|
629
|
+
});
|
|
597
630
|
unreadable.delete(room.locator);
|
|
598
|
-
replicas.set(room.locator, replicaOf(retained,
|
|
631
|
+
replicas.set(room.locator, replicaOf(retained, prior?.queue ?? [], rewriting(), prior));
|
|
599
632
|
}
|
|
600
633
|
list(room, true);
|
|
601
|
-
|
|
602
|
-
|
|
634
|
+
publishChangeSet(changeSet(room.locator, prior ?? null, replicas.get(room.locator) ?? null, {
|
|
635
|
+
room: true,
|
|
636
|
+
query: true
|
|
637
|
+
}));
|
|
603
638
|
});
|
|
604
639
|
}
|
|
605
640
|
async function write(locator, changes, replacing, writtenBy) {
|
|
606
641
|
const local = await openLocal();
|
|
607
|
-
await hydrate(local);
|
|
608
642
|
const replica = replicas.get(locator);
|
|
609
643
|
const room = rooms.get(locator);
|
|
610
644
|
if (replica === undefined || room === undefined) {
|
|
@@ -650,11 +684,19 @@ export function createMdRuntime(options) {
|
|
|
650
684
|
...new Set(changes.map((change)=>change.path))
|
|
651
685
|
];
|
|
652
686
|
try {
|
|
653
|
-
await local.
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
687
|
+
await local.write({
|
|
688
|
+
next: replica.confirmed.record,
|
|
689
|
+
previous: replica.confirmed.record,
|
|
690
|
+
queue: batches,
|
|
691
|
+
previousQueue: replica.queue,
|
|
692
|
+
outcomes: replacing === undefined ? [] : [
|
|
693
|
+
{
|
|
694
|
+
id: replacing,
|
|
695
|
+
outcome: null
|
|
696
|
+
}
|
|
697
|
+
],
|
|
698
|
+
consumedDrafts: consumed
|
|
699
|
+
});
|
|
658
700
|
} catch {
|
|
659
701
|
return {
|
|
660
702
|
outcome: 'refused',
|
|
@@ -663,10 +705,11 @@ export function createMdRuntime(options) {
|
|
|
663
705
|
}
|
|
664
706
|
if (replacing !== undefined) outcomes.delete(replacing);
|
|
665
707
|
for (const path of consumed)forgetDraft(locator, path);
|
|
666
|
-
replicas.set(locator, replicaOf(replica.confirmed, batches, rewriting()));
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
708
|
+
replicas.set(locator, replicaOf(replica.confirmed, batches, rewriting(), replica));
|
|
709
|
+
publishChangeSet(changeSet(locator, replica, replicas.get(locator) ?? null, {
|
|
710
|
+
queue: true,
|
|
711
|
+
drafts: consumed
|
|
712
|
+
}));
|
|
670
713
|
moveTo({
|
|
671
714
|
...saving(status),
|
|
672
715
|
...attention()
|
|
@@ -679,18 +722,23 @@ export function createMdRuntime(options) {
|
|
|
679
722
|
}
|
|
680
723
|
async function discardHead(locator, identifier) {
|
|
681
724
|
const local = await openLocal();
|
|
682
|
-
await hydrate(local);
|
|
683
725
|
const replica = replicas.get(locator);
|
|
684
726
|
const head = replica?.queue[0];
|
|
685
727
|
if (replica === undefined || head === undefined || head.id !== identifier || !refused(outcomes.get(head.id))) {
|
|
686
728
|
return;
|
|
687
729
|
}
|
|
688
730
|
const batches = replica.queue.slice(1);
|
|
731
|
+
await persistQueue(local, replica, batches, [
|
|
732
|
+
{
|
|
733
|
+
id: identifier,
|
|
734
|
+
outcome: null
|
|
735
|
+
}
|
|
736
|
+
]);
|
|
689
737
|
outcomes.delete(identifier);
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
738
|
+
replicas.set(locator, replicaOf(replica.confirmed, batches, rewriting(), replica));
|
|
739
|
+
publishChangeSet(changeSet(locator, replica, replicas.get(locator) ?? null, {
|
|
740
|
+
queue: true
|
|
741
|
+
}));
|
|
694
742
|
moveTo({
|
|
695
743
|
...status,
|
|
696
744
|
state: pending() ? 'pending' : status.state,
|
|
@@ -739,7 +787,7 @@ export function createMdRuntime(options) {
|
|
|
739
787
|
const room = replicas.get(result.locator)?.confirmed.record;
|
|
740
788
|
if (room !== undefined) await scoped(result, room, await openLocal());
|
|
741
789
|
}
|
|
742
|
-
if (result.kind === 'revoked-authorization') refuse('revoked-authorization');
|
|
790
|
+
if (result.kind === 'revoked-authorization') await refuse('revoked-authorization');
|
|
743
791
|
return result;
|
|
744
792
|
} catch {
|
|
745
793
|
journal.record('outcome history transport-uncertainty');
|
|
@@ -792,7 +840,7 @@ export function createMdRuntime(options) {
|
|
|
792
840
|
const room = replicas.get(result.locator)?.confirmed.record;
|
|
793
841
|
if (room !== undefined) await scoped(result, room, await openLocal());
|
|
794
842
|
}
|
|
795
|
-
if (result.kind === 'revoked-authorization') refuse('revoked-authorization');
|
|
843
|
+
if (result.kind === 'revoked-authorization') await refuse('revoked-authorization');
|
|
796
844
|
journal.record(`outcome action ${result.kind}`);
|
|
797
845
|
if (result.kind === 'rejected') journal.record(`refusal rejected ${result.code}`);
|
|
798
846
|
return result;
|
|
@@ -885,7 +933,6 @@ export function createMdRuntime(options) {
|
|
|
885
933
|
};
|
|
886
934
|
}
|
|
887
935
|
async function commitAccept(room, local, submitted, accepted, base, overlay) {
|
|
888
|
-
await hydrate(local);
|
|
889
936
|
const current = replicas.get(room.locator);
|
|
890
937
|
if (current === undefined || !current.queue.some((batch)=>batch.id === submitted.id)) {
|
|
891
938
|
return true;
|
|
@@ -895,14 +942,11 @@ export function createMdRuntime(options) {
|
|
|
895
942
|
...base.record,
|
|
896
943
|
model: modelOf(room.locator),
|
|
897
944
|
selectionModel: modelOf(room.locator, 'typed-selection')
|
|
898
|
-
});
|
|
945
|
+
}, base);
|
|
899
946
|
if (retainedBase === null) return false;
|
|
900
947
|
const applied = overlay ? applyReturned(retainedBase, accepted.documents, accepted.revisions, accepted.removals, accepted.roomVersion, options.modelBinding) : retainedBase;
|
|
901
948
|
if (applied === null) return false;
|
|
902
|
-
|
|
903
|
-
await persistQueue(local, room.locator, remaining);
|
|
904
|
-
acceptedIn.set(submitted.id, room.locator);
|
|
905
|
-
outcomes.set(submitted.id, {
|
|
949
|
+
const acceptedOutcome = {
|
|
906
950
|
kind: 'accepted',
|
|
907
951
|
roomVersion: accepted.roomVersion,
|
|
908
952
|
...accepted.author === undefined ? {} : {
|
|
@@ -911,13 +955,29 @@ export function createMdRuntime(options) {
|
|
|
911
955
|
...accepted.time === undefined ? {} : {
|
|
912
956
|
time: accepted.time
|
|
913
957
|
}
|
|
958
|
+
};
|
|
959
|
+
await local.write({
|
|
960
|
+
next: applied.record,
|
|
961
|
+
previous: current.confirmed.record,
|
|
962
|
+
queue: remaining,
|
|
963
|
+
previousQueue: current.queue,
|
|
964
|
+
outcomes: [
|
|
965
|
+
{
|
|
966
|
+
id: submitted.id,
|
|
967
|
+
outcome: acceptedOutcome
|
|
968
|
+
}
|
|
969
|
+
]
|
|
914
970
|
});
|
|
971
|
+
acceptedIn.set(submitted.id, room.locator);
|
|
972
|
+
outcomes.set(submitted.id, acceptedOutcome);
|
|
915
973
|
unreadable.delete(room.locator);
|
|
916
|
-
replicas.set(room.locator, replicaOf(applied, remaining, rewriting()));
|
|
974
|
+
replicas.set(room.locator, replicaOf(applied, remaining, rewriting(), current));
|
|
917
975
|
list(room, true);
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
976
|
+
publishChangeSet(changeSet(room.locator, current, replicas.get(room.locator) ?? null, {
|
|
977
|
+
room: true,
|
|
978
|
+
queue: true,
|
|
979
|
+
query: true
|
|
980
|
+
}));
|
|
921
981
|
journal.record(`outcome ${room.locator} ${submitted.id} accepted ${accepted.roomVersion}` + (accepted.author === undefined ? '' : ` author ${accepted.author}`) + (accepted.time === undefined ? '' : ` time ${accepted.time}`));
|
|
922
982
|
return true;
|
|
923
983
|
}
|
|
@@ -934,12 +994,18 @@ export function createMdRuntime(options) {
|
|
|
934
994
|
return null;
|
|
935
995
|
}
|
|
936
996
|
await exclusive(async ()=>{
|
|
937
|
-
await hydrate(local);
|
|
938
997
|
const current = replicas.get(room.locator);
|
|
939
998
|
if (current === undefined || current.queue[0]?.id !== head.id) return;
|
|
999
|
+
await persistQueue(local, current, current.queue, [
|
|
1000
|
+
{
|
|
1001
|
+
id: head.id,
|
|
1002
|
+
outcome: null
|
|
1003
|
+
}
|
|
1004
|
+
]);
|
|
940
1005
|
outcomes.delete(head.id);
|
|
941
|
-
|
|
942
|
-
|
|
1006
|
+
publishChangeSet(changeSet(room.locator, current, current, {
|
|
1007
|
+
queue: true
|
|
1008
|
+
}));
|
|
943
1009
|
moveTo({
|
|
944
1010
|
...status,
|
|
945
1011
|
...attention()
|
|
@@ -948,6 +1014,11 @@ export function createMdRuntime(options) {
|
|
|
948
1014
|
}
|
|
949
1015
|
let result;
|
|
950
1016
|
journal.record(`submission ${room.locator} ${head.id}`);
|
|
1017
|
+
if (await local.markSubmitted(room.locator, head.id)) {
|
|
1018
|
+
publishChangeSet(changeSet(room.locator, replica, replica, {
|
|
1019
|
+
queue: true
|
|
1020
|
+
}));
|
|
1021
|
+
}
|
|
951
1022
|
try {
|
|
952
1023
|
result = await options.adapter.mutate(authorized, {
|
|
953
1024
|
locator: room.locator,
|
|
@@ -972,7 +1043,7 @@ export function createMdRuntime(options) {
|
|
|
972
1043
|
return null;
|
|
973
1044
|
}
|
|
974
1045
|
if (result.kind === 'revoked-authorization') {
|
|
975
|
-
refuse('revoked-authorization');
|
|
1046
|
+
await refuse('revoked-authorization');
|
|
976
1047
|
return 'stop';
|
|
977
1048
|
}
|
|
978
1049
|
if (result.kind === 'scoped-refusal') {
|
|
@@ -985,14 +1056,21 @@ export function createMdRuntime(options) {
|
|
|
985
1056
|
if (result.kind !== 'expired') journal.record(`refusal ${result.kind} ${result.code}`);
|
|
986
1057
|
if (result.kind === 'retryable-refusal') progress.complete = false;
|
|
987
1058
|
await exclusive(async ()=>{
|
|
988
|
-
await hydrate(local);
|
|
989
1059
|
const current = replicas.get(room.locator);
|
|
990
1060
|
if (current === undefined || !current.queue.some((batch)=>batch.id === head.id)) {
|
|
991
1061
|
return;
|
|
992
1062
|
}
|
|
993
|
-
|
|
994
|
-
await persistQueue(local,
|
|
995
|
-
|
|
1063
|
+
const recorded = recordRefusal(result);
|
|
1064
|
+
await persistQueue(local, current, current.queue, [
|
|
1065
|
+
{
|
|
1066
|
+
id: head.id,
|
|
1067
|
+
outcome: recorded
|
|
1068
|
+
}
|
|
1069
|
+
]);
|
|
1070
|
+
outcomes.set(head.id, recorded);
|
|
1071
|
+
publishChangeSet(changeSet(room.locator, current, current, {
|
|
1072
|
+
queue: true
|
|
1073
|
+
}));
|
|
996
1074
|
moveTo({
|
|
997
1075
|
...status,
|
|
998
1076
|
...attention()
|
|
@@ -1004,7 +1082,7 @@ export function createMdRuntime(options) {
|
|
|
1004
1082
|
if (from === undefined) return null;
|
|
1005
1083
|
const pulled = await pullRefresh(authorized, from);
|
|
1006
1084
|
if (pulled.kind === 'revoked-authorization') {
|
|
1007
|
-
refuse('revoked-authorization');
|
|
1085
|
+
await refuse('revoked-authorization');
|
|
1008
1086
|
return 'stop';
|
|
1009
1087
|
}
|
|
1010
1088
|
if (pulled.kind === 'scoped-refusal') {
|
|
@@ -1032,7 +1110,7 @@ export function createMdRuntime(options) {
|
|
|
1032
1110
|
if (pulled.kind === 'beyond-horizon') {
|
|
1033
1111
|
const bootstrapped = await pullBootstrap(authorized, room);
|
|
1034
1112
|
if (bootstrapped.kind === 'revoked-authorization') {
|
|
1035
|
-
refuse('revoked-authorization');
|
|
1113
|
+
await refuse('revoked-authorization');
|
|
1036
1114
|
return 'stop';
|
|
1037
1115
|
}
|
|
1038
1116
|
if (bootstrapped.kind === 'scoped-refusal') {
|
|
@@ -1084,7 +1162,6 @@ export function createMdRuntime(options) {
|
|
|
1084
1162
|
liveModels.delete(key);
|
|
1085
1163
|
}
|
|
1086
1164
|
await exclusive(async ()=>{
|
|
1087
|
-
await hydrate(local);
|
|
1088
1165
|
if (next.status !== 'unavailable' && epoch !== (modelEpoch.get(key) ?? 0)) return;
|
|
1089
1166
|
const held = replicas.get(room.locator);
|
|
1090
1167
|
if (held !== undefined) {
|
|
@@ -1100,9 +1177,15 @@ export function createMdRuntime(options) {
|
|
|
1100
1177
|
selectionModel: next
|
|
1101
1178
|
}
|
|
1102
1179
|
};
|
|
1103
|
-
await local.
|
|
1104
|
-
|
|
1105
|
-
|
|
1180
|
+
await local.write({
|
|
1181
|
+
next: record,
|
|
1182
|
+
previous: held.confirmed.record,
|
|
1183
|
+
queue: held.queue
|
|
1184
|
+
});
|
|
1185
|
+
const confirmed = readConfirmed(record, held.confirmed);
|
|
1186
|
+
if (confirmed !== null) {
|
|
1187
|
+
replicas.set(room.locator, replicaOf(confirmed, held.queue, rewriting(), held));
|
|
1188
|
+
}
|
|
1106
1189
|
}
|
|
1107
1190
|
models.set(key, next);
|
|
1108
1191
|
if (next.status === 'current') {
|
|
@@ -1110,8 +1193,11 @@ export function createMdRuntime(options) {
|
|
|
1110
1193
|
invalidModels.delete(key);
|
|
1111
1194
|
}
|
|
1112
1195
|
list(room, room.replica.admission === 'admitted' && !unreadable.has(room.locator));
|
|
1113
|
-
emitRooms();
|
|
1114
|
-
|
|
1196
|
+
if (held === undefined) emitRooms();
|
|
1197
|
+
else publishChangeSet(changeSet(room.locator, held, replicas.get(room.locator) ?? null, {
|
|
1198
|
+
room: true,
|
|
1199
|
+
query: queryChanged
|
|
1200
|
+
}));
|
|
1115
1201
|
});
|
|
1116
1202
|
}
|
|
1117
1203
|
async function acquireModel(local, room, authorized, selection) {
|
|
@@ -1147,11 +1233,7 @@ export function createMdRuntime(options) {
|
|
|
1147
1233
|
}
|
|
1148
1234
|
if (epoch !== (modelEpoch.get(key) ?? 0)) return;
|
|
1149
1235
|
if (answer.kind === 'revoked-authorization') {
|
|
1150
|
-
await
|
|
1151
|
-
status: 'unavailable',
|
|
1152
|
-
reason: 'revoked'
|
|
1153
|
-
});
|
|
1154
|
-
refuse('revoked-authorization');
|
|
1236
|
+
await refuse('revoked-authorization');
|
|
1155
1237
|
return;
|
|
1156
1238
|
}
|
|
1157
1239
|
if (answer.kind === 'transport-uncertainty') {
|
|
@@ -1271,7 +1353,7 @@ export function createMdRuntime(options) {
|
|
|
1271
1353
|
continue;
|
|
1272
1354
|
}
|
|
1273
1355
|
if (withheld.kind === 'revoked-authorization') {
|
|
1274
|
-
refuse('revoked-authorization');
|
|
1356
|
+
await refuse('revoked-authorization');
|
|
1275
1357
|
return;
|
|
1276
1358
|
}
|
|
1277
1359
|
continue;
|
|
@@ -1311,7 +1393,7 @@ export function createMdRuntime(options) {
|
|
|
1311
1393
|
}
|
|
1312
1394
|
if (result.kind === 'contract-unavailable') {
|
|
1313
1395
|
journal.record(`connection contract unavailable ${result.code}`);
|
|
1314
|
-
refuse('missing-required-guarantee', {
|
|
1396
|
+
await refuse('missing-required-guarantee', {
|
|
1315
1397
|
code: result.code,
|
|
1316
1398
|
fields: result.fields
|
|
1317
1399
|
});
|
|
@@ -1319,17 +1401,17 @@ export function createMdRuntime(options) {
|
|
|
1319
1401
|
return;
|
|
1320
1402
|
}
|
|
1321
1403
|
if (result.kind === 'revoked-authorization') {
|
|
1322
|
-
refuse('revoked-authorization');
|
|
1404
|
+
await refuse('revoked-authorization');
|
|
1323
1405
|
notifyReady();
|
|
1324
1406
|
return;
|
|
1325
1407
|
}
|
|
1326
1408
|
if (!result.supportedVersions.includes(ROOM_CONTRACT_VERSION)) {
|
|
1327
|
-
refuse('no-matching-version');
|
|
1409
|
+
await refuse('no-matching-version');
|
|
1328
1410
|
notifyReady();
|
|
1329
1411
|
return;
|
|
1330
1412
|
}
|
|
1331
1413
|
if (result.mdModel.identity !== MD_MODEL.identity || result.mdModel.version !== MD_MODEL.version) {
|
|
1332
|
-
refuse('md-model-version-mismatch');
|
|
1414
|
+
await refuse('md-model-version-mismatch');
|
|
1333
1415
|
notifyReady();
|
|
1334
1416
|
return;
|
|
1335
1417
|
}
|
|
@@ -1347,13 +1429,13 @@ export function createMdRuntime(options) {
|
|
|
1347
1429
|
'unavailable'
|
|
1348
1430
|
].includes(room.replica.admission) || !declaredIdentity(room.attachedModel)) {
|
|
1349
1431
|
journal.record(`invalid model or replica declaration ${room.locator}`);
|
|
1350
|
-
refuse('missing-required-guarantee');
|
|
1432
|
+
await refuse('missing-required-guarantee');
|
|
1351
1433
|
notifyReady();
|
|
1352
1434
|
return;
|
|
1353
1435
|
}
|
|
1354
1436
|
if (!declaredSelection(room)) {
|
|
1355
1437
|
journal.record(`invalid typed selection declaration ${room.locator}`);
|
|
1356
|
-
refuse('missing-required-guarantee');
|
|
1438
|
+
await refuse('missing-required-guarantee');
|
|
1357
1439
|
notifyReady();
|
|
1358
1440
|
return;
|
|
1359
1441
|
}
|
|
@@ -1363,14 +1445,14 @@ export function createMdRuntime(options) {
|
|
|
1363
1445
|
}
|
|
1364
1446
|
}
|
|
1365
1447
|
if (mismatchedRoots.size > 0) {
|
|
1366
|
-
refuse('missing-required-guarantee');
|
|
1448
|
+
await refuse('missing-required-guarantee');
|
|
1367
1449
|
notifyReady();
|
|
1368
1450
|
return;
|
|
1369
1451
|
}
|
|
1370
1452
|
const declared = new Set(result.capabilities.map((capability)=>capability.name));
|
|
1371
1453
|
for (const required of options.requiredGuarantees){
|
|
1372
1454
|
if (!knownCapabilities.has(required) || !declared.has(required)) {
|
|
1373
|
-
refuse('missing-required-guarantee');
|
|
1455
|
+
await refuse('missing-required-guarantee');
|
|
1374
1456
|
notifyReady();
|
|
1375
1457
|
return;
|
|
1376
1458
|
}
|
|
@@ -1508,7 +1590,7 @@ export function createMdRuntime(options) {
|
|
|
1508
1590
|
continue;
|
|
1509
1591
|
}
|
|
1510
1592
|
if (withheld.kind === 'revoked-authorization') {
|
|
1511
|
-
refuse('revoked-authorization');
|
|
1593
|
+
await refuse('revoked-authorization');
|
|
1512
1594
|
return;
|
|
1513
1595
|
}
|
|
1514
1596
|
}
|
|
@@ -1559,6 +1641,9 @@ export function createMdRuntime(options) {
|
|
|
1559
1641
|
});
|
|
1560
1642
|
void enqueue();
|
|
1561
1643
|
return {
|
|
1644
|
+
loaded () {
|
|
1645
|
+
return openLocal().then(()=>undefined);
|
|
1646
|
+
},
|
|
1562
1647
|
rooms () {
|
|
1563
1648
|
return listed();
|
|
1564
1649
|
},
|
|
@@ -1580,8 +1665,8 @@ export function createMdRuntime(options) {
|
|
|
1580
1665
|
draft: {
|
|
1581
1666
|
list () {
|
|
1582
1667
|
return exclusive(async ()=>{
|
|
1583
|
-
|
|
1584
|
-
return
|
|
1668
|
+
await openLocal();
|
|
1669
|
+
return snapshotDrafts();
|
|
1585
1670
|
}).then((entries)=>{
|
|
1586
1671
|
releaseWaitingDrafts();
|
|
1587
1672
|
return entries;
|
|
@@ -1592,7 +1677,7 @@ export function createMdRuntime(options) {
|
|
|
1592
1677
|
return content === undefined ? null : structuredClone(content);
|
|
1593
1678
|
},
|
|
1594
1679
|
replace (locator, path, content) {
|
|
1595
|
-
return commitDraft(async (local)=>{
|
|
1680
|
+
return commitDraft(locator, path, async (local)=>{
|
|
1596
1681
|
const stored = structuredClone(content);
|
|
1597
1682
|
await local.keep({
|
|
1598
1683
|
locator,
|
|
@@ -1603,7 +1688,7 @@ export function createMdRuntime(options) {
|
|
|
1603
1688
|
});
|
|
1604
1689
|
},
|
|
1605
1690
|
discard (locator, path) {
|
|
1606
|
-
return commitDraft(async (local)=>{
|
|
1691
|
+
return commitDraft(locator, path, async (local)=>{
|
|
1607
1692
|
await local.discard(locator, path);
|
|
1608
1693
|
forgetDraft(locator, path);
|
|
1609
1694
|
});
|
|
@@ -1613,22 +1698,22 @@ export function createMdRuntime(options) {
|
|
|
1613
1698
|
presentation: {
|
|
1614
1699
|
read (app) {
|
|
1615
1700
|
return exclusive(async ()=>{
|
|
1616
|
-
await
|
|
1701
|
+
await openLocal();
|
|
1617
1702
|
return presentationOf(app);
|
|
1618
1703
|
});
|
|
1619
1704
|
},
|
|
1620
1705
|
write (app, value) {
|
|
1621
1706
|
return exclusive(async ()=>{
|
|
1622
1707
|
const local = await openLocal();
|
|
1623
|
-
await hydrate(local);
|
|
1624
1708
|
const stored = structuredClone(value);
|
|
1625
1709
|
await local.present({
|
|
1626
1710
|
app,
|
|
1627
1711
|
value: stored
|
|
1628
1712
|
});
|
|
1629
1713
|
presentations.set(app, stored);
|
|
1630
|
-
|
|
1631
|
-
|
|
1714
|
+
publishChangeSet(changeSet('', null, null, {
|
|
1715
|
+
presentation: app
|
|
1716
|
+
}));
|
|
1632
1717
|
});
|
|
1633
1718
|
}
|
|
1634
1719
|
},
|
|
@@ -1674,8 +1759,7 @@ export function createMdRuntime(options) {
|
|
|
1674
1759
|
};
|
|
1675
1760
|
draftListeners.add(listener);
|
|
1676
1761
|
void exclusive(async ()=>{
|
|
1677
|
-
|
|
1678
|
-
await readDrafts(local);
|
|
1762
|
+
await openLocal();
|
|
1679
1763
|
if (closed || !draftListeners.has(listener)) return;
|
|
1680
1764
|
listener.ready = true;
|
|
1681
1765
|
}).then(()=>{
|
|
@@ -1697,7 +1781,7 @@ export function createMdRuntime(options) {
|
|
|
1697
1781
|
};
|
|
1698
1782
|
presentationListeners.add(listener);
|
|
1699
1783
|
void exclusive(async ()=>{
|
|
1700
|
-
await
|
|
1784
|
+
await openLocal();
|
|
1701
1785
|
}).then(()=>{
|
|
1702
1786
|
if (closed || !presentationListeners.has(listener)) return;
|
|
1703
1787
|
listener.ready = true;
|
|
@@ -1764,6 +1848,7 @@ export function createMdRuntime(options) {
|
|
|
1764
1848
|
async reauthorize () {
|
|
1765
1849
|
if (refusal === null || !refusal.reauthorizationOffered) return;
|
|
1766
1850
|
journal.record('connection reauthorize');
|
|
1851
|
+
await mutations;
|
|
1767
1852
|
authorization = undefined;
|
|
1768
1853
|
discoveryPaused = false;
|
|
1769
1854
|
await enqueue();
|