@agentshouse/mdruntime 0.1.0-c0bed82db8d73 → 0.1.0-c0c9861edaba9
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/README.md +2 -1
- package/dist/adapter.d.ts +0 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +1 -0
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +8 -1
- package/dist/bootstrap.d.ts +8 -4
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +76 -41
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +1 -2
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +65 -19
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +228 -217
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +5 -2
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +2 -1
- package/dist/memory.d.ts +1 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +2 -6
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +1 -1
- package/dist/query.d.ts +6 -0
- package/dist/query.d.ts.map +1 -1
- package/dist/reach.d.ts +6 -0
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +23 -6
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +1 -5
- package/dist/replica.d.ts +1 -2
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +2 -2
- package/dist/rows.d.ts +15 -5
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +102 -71
- package/dist/store.d.ts +4 -9
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +10 -7
- package/dist/stored.d.ts +10 -3
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +110 -70
- package/dist/tables.d.ts +5 -7
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +5 -5
- package/package.json +1 -2
- package/AGENTS.md +0 -312
package/dist/connection.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { escapePointer, parseFrontmatter, roomTarget, target } from '@agentshouse/mdmodel';
|
|
2
|
-
import { attachedModel,
|
|
2
|
+
import { attachedModel, builtInModel, roomTypesOf } from './attached-model.js';
|
|
3
3
|
import { cached, modelTypes, NO_MODEL, sameModel, } from './model.js';
|
|
4
4
|
import { observing, whole } from './observation.js';
|
|
5
5
|
import { filterOf, identityOf, planOf, plain, queryKey, } from './query.js';
|
|
@@ -8,7 +8,7 @@ import { seededResult } from './seed.js';
|
|
|
8
8
|
import { documentKey, roomOfKey } from './rows.js';
|
|
9
9
|
import { searchPlanOf } from './search.js';
|
|
10
10
|
import { capped, MAXIMUM_WINDOW } from './windows.js';
|
|
11
|
-
import { bootstrapRoom } from './bootstrap.js';
|
|
11
|
+
import { bootstrapRoom, NOTHING_DONE } from './bootstrap.js';
|
|
12
12
|
import { changeSet, mergeChangeSets } from './change-set.js';
|
|
13
13
|
import { createConversationStore, } from './conversation.js';
|
|
14
14
|
import { leaving as leavingIn, partnerOf, queuedFile, } from './change.js';
|
|
@@ -54,7 +54,10 @@ const CONVERSATION_LANE = 'conversations';
|
|
|
54
54
|
const ROOM_READS = 256;
|
|
55
55
|
const ROOM_ATTEMPTS = 16;
|
|
56
56
|
const ERASE_BATCH = 512;
|
|
57
|
-
const
|
|
57
|
+
const DERIVE_BYTES = 1 << 23;
|
|
58
|
+
const DERIVE_FLOOR = 1 << 18;
|
|
59
|
+
// An index build aims at this much database work, so a Save, a query or the next part's progress waits behind at most one short build.
|
|
60
|
+
const DERIVE_AIM_MS = 250;
|
|
58
61
|
const WALK_BYTES = 1 << 20;
|
|
59
62
|
const QUERY_BYTES = 1 << 20;
|
|
60
63
|
const STAGE_BYTES = 1 << 20;
|
|
@@ -62,14 +65,9 @@ const STAGE_FLOOR = 1 << 16;
|
|
|
62
65
|
// A batch aims at this much database work, so a Save or a query waits behind at most one short write.
|
|
63
66
|
const STAGE_AIM_MS = 50;
|
|
64
67
|
const PARTS_HELD = 4;
|
|
68
|
+
const QUEUED_BYTES = 1 << 23;
|
|
65
69
|
// Bytes still arriving are reported at most this often, well inside the 2 s progress bound, so readiness is not rebuilt for every chunk the network hands over.
|
|
66
70
|
const ARRIVAL_REPORT_MS = 250;
|
|
67
|
-
const INSTALL_STEPS = [
|
|
68
|
-
'writing',
|
|
69
|
-
'downloading',
|
|
70
|
-
'replaying',
|
|
71
|
-
'listing',
|
|
72
|
-
];
|
|
73
71
|
const CLEAN_CHANGES = 2_000;
|
|
74
72
|
// A Room that locks offline stays readable this long after the connection drops, so a laptop waking or changing network keeps working while a disconnected device locks soon after.
|
|
75
73
|
const LOCK_GRACE_MS = 120_000;
|
|
@@ -114,9 +112,9 @@ export function composeRuntime(options) {
|
|
|
114
112
|
const rooms = new FencedMap(isFenced);
|
|
115
113
|
const counted = new Map();
|
|
116
114
|
const unreadable = new Set();
|
|
117
|
-
const
|
|
118
|
-
const models = new FencedMap(
|
|
119
|
-
const liveModels = new FencedMap(
|
|
115
|
+
const builtIn = builtInModel(roomTypes);
|
|
116
|
+
const models = new FencedMap(isFenced);
|
|
117
|
+
const liveModels = new FencedMap(isFenced);
|
|
120
118
|
const invalidModels = new Set();
|
|
121
119
|
const discovered = new Map();
|
|
122
120
|
const reads = doorReads(options.adapter, (locator) => discovered.get(locator), () => generation);
|
|
@@ -124,23 +122,19 @@ export function composeRuntime(options) {
|
|
|
124
122
|
const scopedRefusals = new Map();
|
|
125
123
|
const sourceRefusals = new Map();
|
|
126
124
|
const declarationRefusals = new Set();
|
|
127
|
-
function
|
|
128
|
-
return
|
|
125
|
+
function modelOf(locator) {
|
|
126
|
+
return models.get(locator) ?? NO_MODEL;
|
|
129
127
|
}
|
|
130
|
-
function
|
|
131
|
-
return
|
|
132
|
-
}
|
|
133
|
-
function savedModel(locator, attached) {
|
|
134
|
-
return invalidModels.has(modelKey(locator, attached))
|
|
128
|
+
function savedModel(locator) {
|
|
129
|
+
return invalidModels.has(locator)
|
|
135
130
|
? { status: 'unavailable', reason: 'revoked' }
|
|
136
|
-
: modelOf(locator
|
|
131
|
+
: modelOf(locator);
|
|
132
|
+
}
|
|
133
|
+
function composed(record) {
|
|
134
|
+
return compositionOf(record, builtIn);
|
|
137
135
|
}
|
|
138
136
|
function compositionFor(record) {
|
|
139
|
-
return
|
|
140
|
-
...record,
|
|
141
|
-
model: savedModel(record.locator, 'collection'),
|
|
142
|
-
selectionModel: savedModel(record.locator, 'typed-selection'),
|
|
143
|
-
});
|
|
137
|
+
return composed({ ...record, model: savedModel(record.locator) });
|
|
144
138
|
}
|
|
145
139
|
let refusal = null;
|
|
146
140
|
let storageLoss = null;
|
|
@@ -218,13 +212,27 @@ export function composeRuntime(options) {
|
|
|
218
212
|
const catching = new Set();
|
|
219
213
|
const transfers = new FencedMap(isFenced);
|
|
220
214
|
const installs = new Map();
|
|
215
|
+
const keptParts = new Map();
|
|
221
216
|
const arrivals = new Map();
|
|
217
|
+
const underived = new Map();
|
|
222
218
|
let arrivalReported = 0;
|
|
223
219
|
let onScreen = options.shell.onScreen;
|
|
224
220
|
const shownWaiters = [];
|
|
225
221
|
let partsFree = PARTS_HELD;
|
|
222
|
+
let queuedBytes = 0;
|
|
226
223
|
const partWaiters = [];
|
|
227
|
-
|
|
224
|
+
const stageBudget = {
|
|
225
|
+
bytes: STAGE_FLOOR * 4,
|
|
226
|
+
floor: STAGE_FLOOR,
|
|
227
|
+
ceiling: STAGE_BYTES,
|
|
228
|
+
aim: STAGE_AIM_MS,
|
|
229
|
+
};
|
|
230
|
+
const deriveBudget = {
|
|
231
|
+
bytes: DERIVE_FLOOR * 4,
|
|
232
|
+
floor: DERIVE_FLOOR,
|
|
233
|
+
ceiling: DERIVE_BYTES,
|
|
234
|
+
aim: DERIVE_AIM_MS,
|
|
235
|
+
};
|
|
228
236
|
let installWrites = Promise.resolve();
|
|
229
237
|
const conversations = createConversationStore({
|
|
230
238
|
agents: declaredAgents,
|
|
@@ -276,7 +284,7 @@ export function composeRuntime(options) {
|
|
|
276
284
|
return undefined;
|
|
277
285
|
const local = await openLocal();
|
|
278
286
|
const slice = await stepped(slicing(await local.entries(held.record.key, 'visible', paths), paths));
|
|
279
|
-
return { ...slice, collection: collectionOf(slice.documents,
|
|
287
|
+
return { ...slice, collection: collectionOf(slice.documents, composed(held.record)) };
|
|
280
288
|
}
|
|
281
289
|
function roomDraftOf(record, visible) {
|
|
282
290
|
return structuredClone({
|
|
@@ -483,7 +491,7 @@ export function composeRuntime(options) {
|
|
|
483
491
|
const governed = governs(reach, room);
|
|
484
492
|
const attached = [
|
|
485
493
|
...(governed.collection ? [modelOf(room.locator)] : []),
|
|
486
|
-
...(governed.selection ? [
|
|
494
|
+
...(governed.selection ? [builtIn] : []),
|
|
487
495
|
];
|
|
488
496
|
return {
|
|
489
497
|
locator: room.locator,
|
|
@@ -567,7 +575,10 @@ export function composeRuntime(options) {
|
|
|
567
575
|
if (held === undefined || closed || held.indexed)
|
|
568
576
|
return;
|
|
569
577
|
const key = held.record.key;
|
|
570
|
-
const done = await exclusive(async () =>
|
|
578
|
+
const done = await exclusive(async () => {
|
|
579
|
+
const local = await openLocal();
|
|
580
|
+
return timed(deriveBudget, () => local.rederive(key, deriveBudget.bytes));
|
|
581
|
+
});
|
|
571
582
|
const now = replicas.get(locator);
|
|
572
583
|
if (now?.record.key !== key)
|
|
573
584
|
continue;
|
|
@@ -610,7 +621,11 @@ export function composeRuntime(options) {
|
|
|
610
621
|
})();
|
|
611
622
|
}
|
|
612
623
|
function roomsOfReach(reach) {
|
|
613
|
-
return new Set([
|
|
624
|
+
return new Set([
|
|
625
|
+
...reach.rooms,
|
|
626
|
+
...reach.documents.map(roomOfKey),
|
|
627
|
+
...reach.areas.flatMap((area) => (area.types.length === 0 ? [] : [area.room])),
|
|
628
|
+
]);
|
|
614
629
|
}
|
|
615
630
|
function reachedRooms(plan) {
|
|
616
631
|
const reached = plan.reach === null ? null : roomsOfReach(plan.reach);
|
|
@@ -729,7 +744,9 @@ export function composeRuntime(options) {
|
|
|
729
744
|
const parts = { done: 0, total: 0 };
|
|
730
745
|
const bytes = { done: 0, total: 0 };
|
|
731
746
|
const received = { done: 0, total: 0 };
|
|
732
|
-
|
|
747
|
+
let installing = 0;
|
|
748
|
+
let unlisted = false;
|
|
749
|
+
let listed = false;
|
|
733
750
|
let waiting = false;
|
|
734
751
|
for (const { room, state } of coverage) {
|
|
735
752
|
if (state === 'unadmitted')
|
|
@@ -738,30 +755,40 @@ export function composeRuntime(options) {
|
|
|
738
755
|
if (state === 'complete')
|
|
739
756
|
rooms.done += 1;
|
|
740
757
|
const transfer = transfers.get(room);
|
|
741
|
-
const
|
|
742
|
-
const inventory = transfer?.inventory ??
|
|
758
|
+
const replica = replicas.get(room)?.record.inventory ?? null;
|
|
759
|
+
const inventory = transfer?.inventory ?? replica ?? installs.get(room) ?? null;
|
|
760
|
+
const underway = state === 'acquiring' || (installs.has(room) && state !== 'complete');
|
|
761
|
+
if (underway)
|
|
762
|
+
installing += 1;
|
|
763
|
+
if (underway && inventory === null)
|
|
764
|
+
unlisted = true;
|
|
743
765
|
if (inventory !== null) {
|
|
744
|
-
|
|
745
|
-
const
|
|
766
|
+
listed = true;
|
|
767
|
+
const done = transfer?.done ?? (replica === null ? NOTHING_DONE : inventory);
|
|
746
768
|
parts.total += inventory.parts;
|
|
747
769
|
bytes.total += inventory.bytes;
|
|
748
770
|
parts.done += done.parts;
|
|
749
771
|
bytes.done += done.bytes;
|
|
750
|
-
if (share !== null && share.entries > 0) {
|
|
751
|
-
bytes.done += Math.floor((share.bytes * share.written) / share.entries);
|
|
752
|
-
}
|
|
753
772
|
received.total += inventory.bytes;
|
|
754
|
-
received.done += done.bytes
|
|
755
|
-
for (const
|
|
756
|
-
|
|
773
|
+
received.done += done.bytes;
|
|
774
|
+
for (const held of [keptParts.get(room), arrivals.get(room)]) {
|
|
775
|
+
for (const arrived of held?.values() ?? [])
|
|
776
|
+
received.done += arrived;
|
|
777
|
+
}
|
|
757
778
|
}
|
|
758
|
-
|
|
759
|
-
if (installing)
|
|
760
|
-
steps.add(held.step);
|
|
761
|
-
waiting ||= installing || state === 'acquiring';
|
|
779
|
+
waiting ||= underway;
|
|
762
780
|
}
|
|
781
|
+
const step = installing === 0
|
|
782
|
+
? null
|
|
783
|
+
: !listed
|
|
784
|
+
? 'listing'
|
|
785
|
+
: unlisted || received.done < received.total
|
|
786
|
+
? 'downloading'
|
|
787
|
+
: bytes.done < bytes.total
|
|
788
|
+
? 'writing'
|
|
789
|
+
: 'replaying';
|
|
763
790
|
return {
|
|
764
|
-
step
|
|
791
|
+
step,
|
|
765
792
|
paused: waiting && !onScreen ? 'hidden' : null,
|
|
766
793
|
rooms,
|
|
767
794
|
parts,
|
|
@@ -1029,7 +1056,7 @@ export function composeRuntime(options) {
|
|
|
1029
1056
|
const records = [...replicas].map(([locator, held]) => ({
|
|
1030
1057
|
locator,
|
|
1031
1058
|
held,
|
|
1032
|
-
record: { ...held.record, model: revoked
|
|
1059
|
+
record: { ...held.record, model: revoked },
|
|
1033
1060
|
}));
|
|
1034
1061
|
let answer = null;
|
|
1035
1062
|
try {
|
|
@@ -1039,18 +1066,14 @@ export function composeRuntime(options) {
|
|
|
1039
1066
|
locator,
|
|
1040
1067
|
key: record.key,
|
|
1041
1068
|
record: storedRecord(record),
|
|
1042
|
-
composition:
|
|
1069
|
+
composition: composed(record),
|
|
1043
1070
|
})),
|
|
1044
1071
|
});
|
|
1045
1072
|
}
|
|
1046
1073
|
catch {
|
|
1047
1074
|
journal.record('model revocation persistence failed');
|
|
1048
1075
|
}
|
|
1049
|
-
const keys = new Set(models.keys());
|
|
1050
|
-
for (const locator of rooms.keys()) {
|
|
1051
|
-
keys.add(modelKey(locator, 'collection'));
|
|
1052
|
-
keys.add(modelKey(locator, 'typed-selection'));
|
|
1053
|
-
}
|
|
1076
|
+
const keys = new Set([...models.keys(), ...rooms.keys()]);
|
|
1054
1077
|
for (const key of keys) {
|
|
1055
1078
|
invalidModels.add(key);
|
|
1056
1079
|
models.set(key, revoked);
|
|
@@ -1105,7 +1128,7 @@ export function composeRuntime(options) {
|
|
|
1105
1128
|
? null
|
|
1106
1129
|
: {
|
|
1107
1130
|
roots: selection.roots,
|
|
1108
|
-
model: modelTypes(
|
|
1131
|
+
model: modelTypes(builtIn),
|
|
1109
1132
|
},
|
|
1110
1133
|
providerOwnedPaths: room.providerOwnedPaths,
|
|
1111
1134
|
roomVersion: replicas.get(room.locator)?.record.roomVersion ?? null,
|
|
@@ -1194,7 +1217,7 @@ export function composeRuntime(options) {
|
|
|
1194
1217
|
queuesWithoutReplica.delete(locator);
|
|
1195
1218
|
rooms.delete(locator);
|
|
1196
1219
|
roomDrafts.delete(locator);
|
|
1197
|
-
|
|
1220
|
+
untransferred(locator);
|
|
1198
1221
|
catching.delete(locator);
|
|
1199
1222
|
discovered.delete(locator);
|
|
1200
1223
|
announced.delete(locator);
|
|
@@ -1205,12 +1228,9 @@ export function composeRuntime(options) {
|
|
|
1205
1228
|
scopedRefusals.delete(locator);
|
|
1206
1229
|
sourceRefusals.delete(locator);
|
|
1207
1230
|
declarationRefusals.delete(locator);
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
liveModels.delete(key);
|
|
1212
|
-
invalidModels.delete(key);
|
|
1213
|
-
}
|
|
1231
|
+
models.delete(locator);
|
|
1232
|
+
liveModels.delete(locator);
|
|
1233
|
+
invalidModels.delete(locator);
|
|
1214
1234
|
journal.record(`room lost ${locator}`);
|
|
1215
1235
|
}
|
|
1216
1236
|
if (fresh.length === 0)
|
|
@@ -1258,7 +1278,14 @@ export function composeRuntime(options) {
|
|
|
1258
1278
|
});
|
|
1259
1279
|
await eraseFenced(fresh);
|
|
1260
1280
|
}
|
|
1261
|
-
function
|
|
1281
|
+
function retyped(persisted) {
|
|
1282
|
+
if (persisted.status === 'unavailable')
|
|
1283
|
+
return persisted;
|
|
1284
|
+
const { state } = attachedModel({ kind: 'model', snapshot: persisted.snapshot }, NO_MODEL, roomTypes);
|
|
1285
|
+
return state.status === 'unavailable' ? state : { ...persisted, types: state.types };
|
|
1286
|
+
}
|
|
1287
|
+
function restored(key, held) {
|
|
1288
|
+
const persisted = retyped(held);
|
|
1262
1289
|
const prior = models.get(key);
|
|
1263
1290
|
if (prior !== undefined &&
|
|
1264
1291
|
prior.status !== 'unavailable' &&
|
|
@@ -1291,6 +1318,10 @@ export function composeRuntime(options) {
|
|
|
1291
1318
|
transfers.clear();
|
|
1292
1319
|
for (const [locator, record] of held.transfers)
|
|
1293
1320
|
transfers.set(locator, record);
|
|
1321
|
+
arrivals.clear();
|
|
1322
|
+
keptParts.clear();
|
|
1323
|
+
for (const [locator, parts] of held.kept)
|
|
1324
|
+
keptParts.set(locator, new Map(parts));
|
|
1294
1325
|
limits = held.providerLimits;
|
|
1295
1326
|
capabilities = held.capabilities;
|
|
1296
1327
|
member = held.member;
|
|
@@ -1316,9 +1347,7 @@ export function composeRuntime(options) {
|
|
|
1316
1347
|
records.forEach((stored, index) => {
|
|
1317
1348
|
if (fences.has(stored.locator))
|
|
1318
1349
|
return;
|
|
1319
|
-
const
|
|
1320
|
-
const selectionModel = restored(modelKey(stored.locator, 'typed-selection'), stored.selectionModel);
|
|
1321
|
-
const record = { ...stored, model, selectionModel };
|
|
1350
|
+
const record = { ...stored, model: restored(stored.locator, stored.model) };
|
|
1322
1351
|
replicas.set(record.locator, {
|
|
1323
1352
|
record,
|
|
1324
1353
|
queue: queuedBatches.get(record.locator) ?? [],
|
|
@@ -1336,13 +1365,10 @@ export function composeRuntime(options) {
|
|
|
1336
1365
|
roomVersion: record.roomVersion,
|
|
1337
1366
|
operations: record.operations,
|
|
1338
1367
|
replica: record.replica,
|
|
1339
|
-
model: modelTypes(model),
|
|
1368
|
+
model: modelTypes(record.model),
|
|
1340
1369
|
typedSelection: record.typedSelection === null
|
|
1341
1370
|
? null
|
|
1342
|
-
: {
|
|
1343
|
-
roots: record.typedSelection.roots,
|
|
1344
|
-
model: modelTypes(selectionModel),
|
|
1345
|
-
},
|
|
1371
|
+
: { roots: record.typedSelection.roots, model: modelTypes(builtIn) },
|
|
1346
1372
|
});
|
|
1347
1373
|
}
|
|
1348
1374
|
});
|
|
@@ -1529,7 +1555,7 @@ export function composeRuntime(options) {
|
|
|
1529
1555
|
return { count: answer.counts[locator] ?? replicas.get(locator)?.count ?? 0, indexed };
|
|
1530
1556
|
}
|
|
1531
1557
|
function replayed(record, confirmedLayer, queue) {
|
|
1532
|
-
return stepped(replaying(confirmedLayer, queue, record.locator,
|
|
1558
|
+
return stepped(replaying(confirmedLayer, queue, record.locator, composed(record)));
|
|
1533
1559
|
}
|
|
1534
1560
|
async function overlayDiff(record, before, visible, touched) {
|
|
1535
1561
|
const kept = before !== undefined && before.record.key === record.key
|
|
@@ -1592,7 +1618,7 @@ export function composeRuntime(options) {
|
|
|
1592
1618
|
const confirmedLayer = overrides === undefined
|
|
1593
1619
|
? read
|
|
1594
1620
|
: await stepped(slicingWith(read, overrides.set, overrides.removed));
|
|
1595
|
-
const composition =
|
|
1621
|
+
const composition = composed(record);
|
|
1596
1622
|
const { withdrawn, kept } = await stepped(standing(confirmedLayer, queue, withdrawable(locator), localSaves, locator, composition));
|
|
1597
1623
|
const founded = kept.filter((batch) => !queue.includes(batch));
|
|
1598
1624
|
const queueChanged = kept.length !== previousQueue.length || founded.length > 0;
|
|
@@ -1698,35 +1724,25 @@ export function composeRuntime(options) {
|
|
|
1698
1724
|
};
|
|
1699
1725
|
}
|
|
1700
1726
|
function adoptModels(locator, record) {
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
if (model.status === 'current')
|
|
1709
|
-
liveModels.set(key, JSON.stringify(model.snapshot));
|
|
1710
|
-
else if (model.status === 'unavailable')
|
|
1711
|
-
liveModels.delete(key);
|
|
1712
|
-
}
|
|
1727
|
+
const model = record.model;
|
|
1728
|
+
models.set(locator, model);
|
|
1729
|
+
invalidModels.delete(locator);
|
|
1730
|
+
if (model.status === 'current')
|
|
1731
|
+
liveModels.set(locator, JSON.stringify(model.snapshot));
|
|
1732
|
+
else if (model.status === 'unavailable')
|
|
1733
|
+
liveModels.delete(locator);
|
|
1713
1734
|
}
|
|
1714
1735
|
function remodel(locator, answers, held) {
|
|
1715
|
-
const
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
};
|
|
1726
|
-
return {
|
|
1727
|
-
collection: taken(answers?.collection, held.collection, 'collection'),
|
|
1728
|
-
selection: taken(answers?.selection, held.selection, 'typed-selection'),
|
|
1729
|
-
};
|
|
1736
|
+
const answer = answers?.collection;
|
|
1737
|
+
if (answer === undefined)
|
|
1738
|
+
return held;
|
|
1739
|
+
const { state, refused } = attachedModel(answer, held.collection, roomTypes);
|
|
1740
|
+
if (refused !== null) {
|
|
1741
|
+
const where = refused.location === undefined ? '' : ` ${refused.location}`;
|
|
1742
|
+
journal.record(`model invalid ${locator} collection ${refused.identity} ${refused.rule}${where}`);
|
|
1743
|
+
}
|
|
1744
|
+
models.set(locator, state);
|
|
1745
|
+
return { collection: state };
|
|
1730
1746
|
}
|
|
1731
1747
|
function publish(local, room, record, landing, live) {
|
|
1732
1748
|
const landed = exclusive(async () => {
|
|
@@ -1753,14 +1769,14 @@ export function composeRuntime(options) {
|
|
|
1753
1769
|
? {
|
|
1754
1770
|
room: true,
|
|
1755
1771
|
view: 'refreshed',
|
|
1756
|
-
commit: { refreshed: true, composition:
|
|
1772
|
+
commit: { refreshed: true, composition: composed(record) },
|
|
1757
1773
|
}
|
|
1758
1774
|
: {
|
|
1759
1775
|
room: true,
|
|
1760
1776
|
view: landing.refreshed ? 'refreshed' : 'confirmed',
|
|
1761
1777
|
commit: {
|
|
1762
1778
|
published: { from: prior?.record.key ?? null },
|
|
1763
|
-
composition:
|
|
1779
|
+
composition: composed(record),
|
|
1764
1780
|
...(landing.refreshed ? { refreshed: true } : {}),
|
|
1765
1781
|
},
|
|
1766
1782
|
});
|
|
@@ -1772,7 +1788,7 @@ export function composeRuntime(options) {
|
|
|
1772
1788
|
return true;
|
|
1773
1789
|
}
|
|
1774
1790
|
if (landing.kind === 'bootstrapped') {
|
|
1775
|
-
|
|
1791
|
+
untransferred(room.locator);
|
|
1776
1792
|
catching.delete(room.locator);
|
|
1777
1793
|
sweep();
|
|
1778
1794
|
}
|
|
@@ -1870,7 +1886,7 @@ export function composeRuntime(options) {
|
|
|
1870
1886
|
: (await local.referrers(record.key, 'confirmed', removed, CHECKED_LINKS)).map(({ path }) => path);
|
|
1871
1887
|
const confirmedLayer = await sliceAt(record, 'confirmed', replica.queue, [...paths, ...referring], moved);
|
|
1872
1888
|
const { visible } = await replayed(record, confirmedLayer, replica.queue);
|
|
1873
|
-
const composition =
|
|
1889
|
+
const composition = composed(record);
|
|
1874
1890
|
return {
|
|
1875
1891
|
record,
|
|
1876
1892
|
confirmed: confirmedLayer,
|
|
@@ -1886,13 +1902,7 @@ export function composeRuntime(options) {
|
|
|
1886
1902
|
return false;
|
|
1887
1903
|
const local = await openLocal();
|
|
1888
1904
|
const roots = record.typedSelection?.roots ?? [];
|
|
1889
|
-
|
|
1890
|
-
(await local.typedWithin(record.key, 'visible', roots, false))) {
|
|
1891
|
-
return true;
|
|
1892
|
-
}
|
|
1893
|
-
return (composition.selection !== null &&
|
|
1894
|
-
composition.selection.model.status === 'unavailable' &&
|
|
1895
|
-
(await local.typedWithin(record.key, 'visible', roots, true)));
|
|
1905
|
+
return local.typedWithin(record.key, 'visible', roots, false);
|
|
1896
1906
|
}
|
|
1897
1907
|
function textDiffers(left, right) {
|
|
1898
1908
|
return ((left?.content ?? undefined) !== (right?.content ?? undefined) ||
|
|
@@ -1997,7 +2007,7 @@ export function composeRuntime(options) {
|
|
|
1997
2007
|
const lookup = overriddenLookup(lookupOf(local, record.key, 'confirmed'), overrides);
|
|
1998
2008
|
const clean = available(composition) &&
|
|
1999
2009
|
changed.length <= CLEAN_CHANGES &&
|
|
2000
|
-
(await changedCleanly(changed,
|
|
2010
|
+
(await changedCleanly(changed, composed(record), composition, {
|
|
2001
2011
|
lookup,
|
|
2002
2012
|
texts: textsOf(local, record.key, 'confirmed'),
|
|
2003
2013
|
referrers: referrersOf(local, record.key, 'confirmed'),
|
|
@@ -2754,8 +2764,8 @@ export function composeRuntime(options) {
|
|
|
2754
2764
|
}
|
|
2755
2765
|
}
|
|
2756
2766
|
async function validatedRoom(local, record, view, previous, overrides = null) {
|
|
2757
|
-
const composition =
|
|
2758
|
-
const modeled = record.model.status !== 'unavailable' || record.
|
|
2767
|
+
const composition = composed(record);
|
|
2768
|
+
const modeled = record.model.status !== 'unavailable' || record.typedSelection !== null;
|
|
2759
2769
|
const lookup = overrides === null
|
|
2760
2770
|
? lookupOf(local, record.key, view)
|
|
2761
2771
|
: overriddenLookup(lookupOf(local, record.key, view), overrides);
|
|
@@ -2789,11 +2799,11 @@ export function composeRuntime(options) {
|
|
|
2789
2799
|
}
|
|
2790
2800
|
const named = changes === null ? null : changes.map(({ path }) => path);
|
|
2791
2801
|
const textChanged = changes === null || changes.length > 0 || reshaped;
|
|
2792
|
-
const modelsChanged = previous === null || !sameComposition(
|
|
2802
|
+
const modelsChanged = previous === null || !sameComposition(composed(previous.record), composition);
|
|
2793
2803
|
if (modeled && (textChanged || modelsChanged)) {
|
|
2794
2804
|
const clean = previous !== null &&
|
|
2795
2805
|
changes !== null &&
|
|
2796
|
-
(await changedCleanly(changes,
|
|
2806
|
+
(await changedCleanly(changes, composed(previous.record), composition, {
|
|
2797
2807
|
lookup,
|
|
2798
2808
|
texts,
|
|
2799
2809
|
referrers: referrersOf(local, record.key, 'confirmed'),
|
|
@@ -2817,7 +2827,7 @@ export function composeRuntime(options) {
|
|
|
2817
2827
|
async turn() {
|
|
2818
2828
|
if (replaying === null)
|
|
2819
2829
|
return;
|
|
2820
|
-
installing(replaying
|
|
2830
|
+
installing(replaying);
|
|
2821
2831
|
await screened();
|
|
2822
2832
|
},
|
|
2823
2833
|
prior: async (paths) => new Map((await local.entries(key, 'refreshed', paths)).map((entry) => [entry.path, entry])),
|
|
@@ -2831,7 +2841,7 @@ export function composeRuntime(options) {
|
|
|
2831
2841
|
const staged = await write(async () => {
|
|
2832
2842
|
if (!liveness())
|
|
2833
2843
|
return false;
|
|
2834
|
-
await timed(() => local.refresh(key, last
|
|
2844
|
+
await timed(stageBudget, () => local.refresh(key, last
|
|
2835
2845
|
? { ...page, entries }
|
|
2836
2846
|
: { entries, removals: [], folders: [], folderRemovals: [] }));
|
|
2837
2847
|
return true;
|
|
@@ -2850,21 +2860,21 @@ export function composeRuntime(options) {
|
|
|
2850
2860
|
let spent = 0;
|
|
2851
2861
|
while (end < entries.length) {
|
|
2852
2862
|
const size = (entries[end].content?.length ?? 0) + ROW_WEIGHT;
|
|
2853
|
-
if (spent + size >
|
|
2863
|
+
if (spent + size > stageBudget.bytes && end > from)
|
|
2854
2864
|
break;
|
|
2855
2865
|
spent += size;
|
|
2856
2866
|
end += 1;
|
|
2857
2867
|
}
|
|
2858
2868
|
return end;
|
|
2859
2869
|
}
|
|
2860
|
-
async function timed(work) {
|
|
2870
|
+
async function timed(budget, work) {
|
|
2861
2871
|
const started = performance.now();
|
|
2862
2872
|
const done = await work();
|
|
2863
2873
|
const spent = performance.now() - started;
|
|
2864
|
-
if (spent <
|
|
2865
|
-
|
|
2866
|
-
else if (spent >
|
|
2867
|
-
|
|
2874
|
+
if (spent < budget.aim / 2)
|
|
2875
|
+
budget.bytes = Math.min(budget.ceiling, Math.round(budget.bytes * 1.5));
|
|
2876
|
+
else if (spent > budget.aim * 2) {
|
|
2877
|
+
budget.bytes = Math.max(budget.floor, Math.round(budget.bytes / 1.5));
|
|
2868
2878
|
}
|
|
2869
2879
|
return done;
|
|
2870
2880
|
}
|
|
@@ -2880,25 +2890,34 @@ export function composeRuntime(options) {
|
|
|
2880
2890
|
return new Promise((resolve) => shownWaiters.push(resolve));
|
|
2881
2891
|
}
|
|
2882
2892
|
async function heldPart() {
|
|
2883
|
-
while (partsFree === 0)
|
|
2893
|
+
while (partsFree === 0 || queuedBytes >= QUEUED_BYTES) {
|
|
2884
2894
|
await new Promise((admit) => partWaiters.push(admit));
|
|
2895
|
+
}
|
|
2885
2896
|
partsFree -= 1;
|
|
2886
|
-
return
|
|
2897
|
+
return once(() => {
|
|
2898
|
+
partsFree += 1;
|
|
2899
|
+
});
|
|
2887
2900
|
}
|
|
2888
|
-
function
|
|
2901
|
+
function once(release) {
|
|
2889
2902
|
let released = false;
|
|
2890
2903
|
return () => {
|
|
2891
2904
|
if (released)
|
|
2892
2905
|
return;
|
|
2893
2906
|
released = true;
|
|
2894
|
-
|
|
2895
|
-
partWaiters.
|
|
2907
|
+
release();
|
|
2908
|
+
for (const admit of partWaiters.splice(0))
|
|
2909
|
+
admit();
|
|
2896
2910
|
};
|
|
2897
2911
|
}
|
|
2898
|
-
function installing(locator,
|
|
2899
|
-
installs.set(locator,
|
|
2912
|
+
function installing(locator, inventory = null) {
|
|
2913
|
+
installs.set(locator, inventory ?? installs.get(locator) ?? null);
|
|
2900
2914
|
queueReadiness();
|
|
2901
2915
|
}
|
|
2916
|
+
function untransferred(locator) {
|
|
2917
|
+
transfers.delete(locator);
|
|
2918
|
+
keptParts.delete(locator);
|
|
2919
|
+
underived.delete(locator);
|
|
2920
|
+
}
|
|
2902
2921
|
function installed(locator) {
|
|
2903
2922
|
if (installs.delete(locator))
|
|
2904
2923
|
queueReadiness();
|
|
@@ -2965,11 +2984,34 @@ export function composeRuntime(options) {
|
|
|
2965
2984
|
heldContents.set(key, made);
|
|
2966
2985
|
return made;
|
|
2967
2986
|
}
|
|
2987
|
+
async function keptTogether({ local, parts }) {
|
|
2988
|
+
const live = parts.filter((one) => one.live() && transfers.has(one.locator));
|
|
2989
|
+
await local.keepParts(live.map(({ locator, token, part }) => ({ locator, token, part })));
|
|
2990
|
+
for (const { locator, token, part } of live) {
|
|
2991
|
+
const held = keptParts.get(locator) ?? new Map();
|
|
2992
|
+
keptParts.set(locator, held.set(token, part.byteLength));
|
|
2993
|
+
arrivals.get(locator)?.delete(token);
|
|
2994
|
+
}
|
|
2995
|
+
queueReadiness();
|
|
2996
|
+
}
|
|
2968
2997
|
function staging(local, liveness) {
|
|
2998
|
+
const derived = async (locator, key) => {
|
|
2999
|
+
underived.delete(locator);
|
|
3000
|
+
for (;;) {
|
|
3001
|
+
await screened();
|
|
3002
|
+
if (!liveness())
|
|
3003
|
+
return false;
|
|
3004
|
+
const done = await installWrite(async () => liveness() ? timed(deriveBudget, () => local.rederive(key, deriveBudget.bytes)) : null);
|
|
3005
|
+
if (done === null)
|
|
3006
|
+
return false;
|
|
3007
|
+
if (done)
|
|
3008
|
+
return true;
|
|
3009
|
+
}
|
|
3010
|
+
};
|
|
2969
3011
|
return {
|
|
2970
3012
|
held: (locator) => transfers.get(locator) ?? null,
|
|
2971
3013
|
async turn(locator, step) {
|
|
2972
|
-
installing(locator
|
|
3014
|
+
installing(locator);
|
|
2973
3015
|
for (;;) {
|
|
2974
3016
|
await screened();
|
|
2975
3017
|
const release = step === 'downloading' ? await heldPart() : () => { };
|
|
@@ -2978,17 +3020,17 @@ export function composeRuntime(options) {
|
|
|
2978
3020
|
release();
|
|
2979
3021
|
}
|
|
2980
3022
|
},
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
3023
|
+
queued(bytes) {
|
|
3024
|
+
queuedBytes += bytes;
|
|
3025
|
+
return once(() => {
|
|
3026
|
+
queuedBytes -= bytes;
|
|
3027
|
+
});
|
|
2986
3028
|
},
|
|
2987
3029
|
arriving(locator) {
|
|
2988
3030
|
let ended = false;
|
|
2989
3031
|
return {
|
|
2990
3032
|
at(token, bytes) {
|
|
2991
|
-
if (ended)
|
|
3033
|
+
if (ended || keptParts.get(locator)?.has(token) === true)
|
|
2992
3034
|
return;
|
|
2993
3035
|
const held = arrivals.get(locator) ?? new Map();
|
|
2994
3036
|
arrivals.set(locator, held.set(token, bytes));
|
|
@@ -3006,64 +3048,44 @@ export function composeRuntime(options) {
|
|
|
3006
3048
|
};
|
|
3007
3049
|
},
|
|
3008
3050
|
received: (locator, token) => local.transferPart(locator, token),
|
|
3009
|
-
keep: (locator, token, received) =>
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
}),
|
|
3051
|
+
keep: (locator, token, received) => gathered('kept parts', { local, parts: [{ locator, token, part: received, live: liveness }] }, (earlier, later) => earlier.local === later.local
|
|
3052
|
+
? { local: earlier.local, parts: [...earlier.parts, ...later.parts] }
|
|
3053
|
+
: null, keptTogether),
|
|
3013
3054
|
announced(locator) {
|
|
3014
3055
|
const later = announced.get(locator);
|
|
3015
3056
|
return later === undefined ? [] : [later];
|
|
3016
3057
|
},
|
|
3017
|
-
async stage(record, page, composition,
|
|
3058
|
+
async stage(record, page, composition, written) {
|
|
3018
3059
|
const { locator } = record;
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
if (token !== null)
|
|
3025
|
-
arrivals.get(locator)?.delete(token);
|
|
3026
|
-
installing(locator, 'writing', { written: from, entries, bytes: whole });
|
|
3027
|
-
for (let written = from;;) {
|
|
3028
|
-
await screened();
|
|
3060
|
+
installing(locator, record.inventory);
|
|
3061
|
+
await screened();
|
|
3062
|
+
if (!liveness())
|
|
3063
|
+
return null;
|
|
3064
|
+
const key = await installWrite(async () => {
|
|
3029
3065
|
if (!liveness())
|
|
3030
3066
|
return null;
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
}
|
|
3042
|
-
: { ...held, writing: { token, written: end } };
|
|
3043
|
-
const key = await installWrite(async () => {
|
|
3044
|
-
if (!liveness())
|
|
3045
|
-
return null;
|
|
3046
|
-
return timed(() => local.stage(next, composition, {
|
|
3047
|
-
entries: page.entries.slice(written, end),
|
|
3048
|
-
folders: last ? page.folders : [],
|
|
3049
|
-
}, last && received !== null && token !== null ? token : undefined));
|
|
3050
|
-
});
|
|
3051
|
-
if (key === null)
|
|
3052
|
-
return null;
|
|
3053
|
-
const arrived = !transfers.has(locator);
|
|
3054
|
-
held = { ...next, key };
|
|
3055
|
-
transfers.set(locator, held);
|
|
3056
|
-
if (arrived)
|
|
3057
|
-
liveQueries.recover();
|
|
3058
|
-
if (last) {
|
|
3059
|
-
installing(locator, held.pageToken === null ? 'replaying' : 'downloading');
|
|
3060
|
-
liveQueries.recover();
|
|
3061
|
-
return held;
|
|
3062
|
-
}
|
|
3063
|
-
installing(locator, 'writing', { written: end, entries, bytes: whole });
|
|
3064
|
-
written = end;
|
|
3067
|
+
return local.stage(record, composition, page, written ?? undefined);
|
|
3068
|
+
});
|
|
3069
|
+
if (key === null || !liveness())
|
|
3070
|
+
return null;
|
|
3071
|
+
const arrived = !transfers.has(locator);
|
|
3072
|
+
const held = { ...record, key };
|
|
3073
|
+
transfers.set(locator, held);
|
|
3074
|
+
if (written !== null) {
|
|
3075
|
+
keptParts.get(locator)?.delete(written);
|
|
3076
|
+
arrivals.get(locator)?.delete(written);
|
|
3065
3077
|
}
|
|
3078
|
+
if (arrived)
|
|
3079
|
+
liveQueries.recover();
|
|
3080
|
+
installing(locator);
|
|
3081
|
+
liveQueries.recover();
|
|
3082
|
+
const owed = page.entries.reduce((sum, entry) => sum + (entry.content?.length ?? 0), underived.get(locator) ?? 0);
|
|
3083
|
+
underived.set(locator, owed);
|
|
3084
|
+
if (owed < deriveBudget.bytes)
|
|
3085
|
+
return held;
|
|
3086
|
+
return (await derived(locator, key)) ? held : null;
|
|
3066
3087
|
},
|
|
3088
|
+
derive: derived,
|
|
3067
3089
|
async refreshing(locator, key) {
|
|
3068
3090
|
await exclusive(() => local.unrefresh(key));
|
|
3069
3091
|
return refreshingOf(local, key, liveness, locator);
|
|
@@ -3072,7 +3094,7 @@ export function composeRuntime(options) {
|
|
|
3072
3094
|
if (!liveness())
|
|
3073
3095
|
return;
|
|
3074
3096
|
await local.abandon(locator);
|
|
3075
|
-
|
|
3097
|
+
untransferred(locator);
|
|
3076
3098
|
liveQueries.recover();
|
|
3077
3099
|
sweep();
|
|
3078
3100
|
}),
|
|
@@ -3080,7 +3102,7 @@ export function composeRuntime(options) {
|
|
|
3080
3102
|
if (!liveness())
|
|
3081
3103
|
return;
|
|
3082
3104
|
await local.abandon(locator, true);
|
|
3083
|
-
|
|
3105
|
+
untransferred(locator);
|
|
3084
3106
|
liveQueries.recover();
|
|
3085
3107
|
}),
|
|
3086
3108
|
release: (key) => exclusive(async () => {
|
|
@@ -3106,10 +3128,7 @@ export function composeRuntime(options) {
|
|
|
3106
3128
|
liveQueries.recover();
|
|
3107
3129
|
}
|
|
3108
3130
|
try {
|
|
3109
|
-
const result = await bootstrapRoom(reads, authorized, room, {
|
|
3110
|
-
collection: modelOf(room.locator),
|
|
3111
|
-
selection: modelOf(room.locator, 'typed-selection'),
|
|
3112
|
-
}, (models, held) => (liveness() ? remodel(room.locator, models, held) : held), liveness, staging(local, liveness), reusable(room, local));
|
|
3131
|
+
const result = await bootstrapRoom(reads, authorized, room, { collection: modelOf(room.locator) }, builtIn, (models, held) => (liveness() ? remodel(room.locator, models, held) : held), liveness, staging(local, liveness), reusable(room, local));
|
|
3113
3132
|
const detail = result.kind === 'staged' ? result.record.roomVersion : result.kind;
|
|
3114
3133
|
journal.record(`transfer bootstrap ${room.locator} ${detail}`);
|
|
3115
3134
|
if (result.kind !== 'staged') {
|
|
@@ -3175,7 +3194,7 @@ export function composeRuntime(options) {
|
|
|
3175
3194
|
options.adapter.reached(room.locator, now, refreshed.record.roomVersion);
|
|
3176
3195
|
return reached ? null : advance(room, local, authorized, live);
|
|
3177
3196
|
}
|
|
3178
|
-
await retainModel(local, room,
|
|
3197
|
+
await retainModel(local, room, modelOf(room.locator));
|
|
3179
3198
|
return null;
|
|
3180
3199
|
}
|
|
3181
3200
|
if (refreshed.kind !== 'beyond-horizon')
|
|
@@ -3771,9 +3790,9 @@ export function composeRuntime(options) {
|
|
|
3771
3790
|
release();
|
|
3772
3791
|
};
|
|
3773
3792
|
}
|
|
3774
|
-
async function retainModel(local, room,
|
|
3775
|
-
const key =
|
|
3776
|
-
const previous = modelOf(room.locator
|
|
3793
|
+
async function retainModel(local, room, next) {
|
|
3794
|
+
const key = room.locator;
|
|
3795
|
+
const previous = modelOf(room.locator);
|
|
3777
3796
|
const heldBefore = replicas.get(room.locator)?.record;
|
|
3778
3797
|
if (JSON.stringify(previous) === JSON.stringify(next) &&
|
|
3779
3798
|
(heldBefore === undefined ||
|
|
@@ -3797,11 +3816,11 @@ export function composeRuntime(options) {
|
|
|
3797
3816
|
operations: room.operations,
|
|
3798
3817
|
replica: room.replica,
|
|
3799
3818
|
typedSelection: room.typedSelection ?? null,
|
|
3800
|
-
|
|
3819
|
+
model: next,
|
|
3801
3820
|
};
|
|
3802
3821
|
let read = 'staged';
|
|
3803
3822
|
if (!sameMembership(declared, held.record)) {
|
|
3804
|
-
const verdict = await validatedCollection(walkOf(local, held.record.key, 'confirmed'), lookupOf(local, held.record.key, 'confirmed'),
|
|
3823
|
+
const verdict = await validatedCollection(walkOf(local, held.record.key, 'confirmed'), lookupOf(local, held.record.key, 'confirmed'), composed(declared));
|
|
3805
3824
|
read =
|
|
3806
3825
|
verdict.verdict !== 'refuse'
|
|
3807
3826
|
? 'staged'
|
|
@@ -3822,7 +3841,7 @@ export function composeRuntime(options) {
|
|
|
3822
3841
|
key: record.key,
|
|
3823
3842
|
record: storedRecord(record),
|
|
3824
3843
|
roots: record.typedSelection?.roots ?? [],
|
|
3825
|
-
composition:
|
|
3844
|
+
composition: composed(record),
|
|
3826
3845
|
overlay: diff.overlay,
|
|
3827
3846
|
},
|
|
3828
3847
|
],
|
|
@@ -3869,14 +3888,14 @@ export function composeRuntime(options) {
|
|
|
3869
3888
|
});
|
|
3870
3889
|
let scopedAway = room;
|
|
3871
3890
|
if (result.scope === 'model')
|
|
3872
|
-
await retainModel(local, room,
|
|
3891
|
+
await retainModel(local, room, {
|
|
3873
3892
|
status: 'unavailable',
|
|
3874
3893
|
reason: 'not-readable',
|
|
3875
3894
|
});
|
|
3876
3895
|
else if (result.scope !== 'operation') {
|
|
3877
3896
|
unreadable.add(room.locator);
|
|
3878
3897
|
scopedAway = { ...room, replica: { ...room.replica, admission: 'unavailable' } };
|
|
3879
|
-
await retainModel(local, scopedAway,
|
|
3898
|
+
await retainModel(local, scopedAway, modelOf(room.locator));
|
|
3880
3899
|
}
|
|
3881
3900
|
if (!continuing())
|
|
3882
3901
|
return;
|
|
@@ -3926,7 +3945,7 @@ export function composeRuntime(options) {
|
|
|
3926
3945
|
return false;
|
|
3927
3946
|
}
|
|
3928
3947
|
if (prior !== undefined)
|
|
3929
|
-
await retainModel(local, room,
|
|
3948
|
+
await retainModel(local, room, modelOf(room.locator));
|
|
3930
3949
|
if (discoveryPaused || authorization === undefined)
|
|
3931
3950
|
return false;
|
|
3932
3951
|
return room.replica.admission === 'admitted';
|
|
@@ -3939,10 +3958,8 @@ export function composeRuntime(options) {
|
|
|
3939
3958
|
await withhold(withheld, room, local, progress);
|
|
3940
3959
|
}
|
|
3941
3960
|
function revoked(locator) {
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
return model.status === 'unavailable' && model.reason === 'revoked';
|
|
3945
|
-
});
|
|
3961
|
+
const model = modelOf(locator);
|
|
3962
|
+
return model.status === 'unavailable' && model.reason === 'revoked';
|
|
3946
3963
|
}
|
|
3947
3964
|
async function loadRoom(room, local, progress, live) {
|
|
3948
3965
|
if (!live() || authorization === undefined)
|
|
@@ -4000,10 +4017,7 @@ export function composeRuntime(options) {
|
|
|
4000
4017
|
journal.record(`connection discover ${result.kind}`);
|
|
4001
4018
|
if (result.kind === 'transport-uncertainty') {
|
|
4002
4019
|
for (const room of rooms.values()) {
|
|
4003
|
-
await retainModel(local, room,
|
|
4004
|
-
if (room.typedSelection !== null) {
|
|
4005
|
-
await retainModel(local, room, 'typed-selection', cached(modelOf(room.locator, 'typed-selection')));
|
|
4006
|
-
}
|
|
4020
|
+
await retainModel(local, room, cached(modelOf(room.locator)));
|
|
4007
4021
|
}
|
|
4008
4022
|
progress.reachable = false;
|
|
4009
4023
|
progress.complete = false;
|
|
@@ -4285,12 +4299,9 @@ export function composeRuntime(options) {
|
|
|
4285
4299
|
}
|
|
4286
4300
|
}
|
|
4287
4301
|
async function confirmModels(room, local) {
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
await retainModel(local, room, attached, { ...model, status: 'current' });
|
|
4292
|
-
}
|
|
4293
|
-
}
|
|
4302
|
+
const model = modelOf(room.locator);
|
|
4303
|
+
if (model.status === 'cached')
|
|
4304
|
+
await retainModel(local, room, { ...model, status: 'current' });
|
|
4294
4305
|
}
|
|
4295
4306
|
function submitLater(locator) {
|
|
4296
4307
|
if (localSaves)
|
|
@@ -4872,7 +4883,7 @@ export function composeRuntime(options) {
|
|
|
4872
4883
|
typedSelection: rooms.get(locator)?.typedSelection === undefined ||
|
|
4873
4884
|
rooms.get(locator)?.typedSelection === null
|
|
4874
4885
|
? null
|
|
4875
|
-
: modelTypes(
|
|
4886
|
+
: modelTypes(builtIn),
|
|
4876
4887
|
};
|
|
4877
4888
|
},
|
|
4878
4889
|
roomDraft: {
|