@agentshouse/mdruntime 0.1.0-cc5745cd9af96 → 0.1.0-ccbb89b0ca151
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 +18 -15
- package/dist/adapter.d.ts +56 -32
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +5 -1
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +21 -4
- package/dist/binary.d.ts +0 -5
- package/dist/binary.d.ts.map +1 -1
- package/dist/binary.js +1 -1
- package/dist/bootstrap.d.ts +30 -8
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +186 -85
- package/dist/chain.d.ts +11 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +64 -0
- package/dist/change-set.d.ts +6 -16
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +11 -94
- package/dist/change.d.ts +31 -1
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +10 -1
- package/dist/client.d.ts +9 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +80 -6
- package/dist/collection.d.ts +6 -20
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +31 -354
- package/dist/compile.d.ts +16 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +765 -0
- package/dist/connection.d.ts +16 -9
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +2399 -952
- package/dist/conversation.d.ts +3 -3
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +80 -68
- package/dist/digest.d.ts +7 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +113 -0
- package/dist/durability.d.ts +3 -5
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +3 -1
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +18 -24
- package/dist/file.d.ts +2 -19
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +7 -122
- package/dist/fulltext.d.ts +27 -0
- package/dist/fulltext.d.ts.map +1 -0
- package/dist/fulltext.js +368 -0
- package/dist/host.d.ts +4 -4
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +45 -15
- package/dist/identity.d.ts +0 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -3
- package/dist/index.d.ts +11 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/live.d.ts +22 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +124 -0
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +195 -0
- package/dist/memory.d.ts +2 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +63 -22
- package/dist/messages.d.ts +28 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +90 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +1 -1
- package/dist/name.d.ts +4 -0
- package/dist/name.d.ts.map +1 -0
- package/dist/name.js +122 -0
- package/dist/observation.d.ts +12 -0
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +84 -0
- package/dist/opened.d.ts +22 -0
- package/dist/opened.d.ts.map +1 -0
- package/dist/opened.js +107 -0
- package/dist/outcome.d.ts +40 -5
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +36 -2
- package/dist/overlay.d.ts +1 -4
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +22 -148
- package/dist/query.d.ts +11 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +13 -1
- package/dist/reach.d.ts +11 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +198 -69
- package/dist/readiness.d.ts +20 -9
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +6 -6
- package/dist/reads.d.ts.map +1 -1
- package/dist/reads.js +4 -2
- package/dist/recovery.d.ts +2 -2
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +8 -7
- package/dist/refresh.d.ts +15 -14
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +113 -75
- package/dist/replica.d.ts +56 -39
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +277 -197
- package/dist/room-draft.d.ts +4 -1
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +54 -13
- package/dist/rows.d.ts +38 -31
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +173 -101
- package/dist/save.d.ts +49 -14
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +352 -98
- package/dist/search.d.ts +11 -43
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +0 -213
- package/dist/shared.d.ts +4 -3
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +67 -34
- package/dist/store.d.ts +83 -95
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +317 -720
- package/dist/stored.d.ts +152 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +607 -0
- package/dist/tables.d.ts +352 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +624 -0
- package/dist/turn.d.ts +1 -1
- package/dist/turn.d.ts.map +1 -1
- package/dist/turn.js +31 -20
- package/dist/validation.d.ts +30 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +162 -0
- package/dist/windows.d.ts +2 -6
- package/dist/windows.d.ts.map +1 -1
- package/dist/windows.js +30 -34
- package/dist/wire.d.ts +17 -1
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +5 -1
- package/dist/words.d.ts +19 -0
- package/dist/words.d.ts.map +1 -0
- package/dist/words.js +150 -0
- package/package.json +18 -19
- package/AGENTS.md +0 -299
- package/dist/chat/acts.d.ts +0 -33
- package/dist/chat/acts.d.ts.map +0 -1
- package/dist/chat/acts.js +0 -36
- package/dist/chat/context.d.ts +0 -3
- package/dist/chat/context.d.ts.map +0 -1
- package/dist/chat/context.js +0 -10
- package/dist/chat/earlier.d.ts +0 -10
- package/dist/chat/earlier.d.ts.map +0 -1
- package/dist/chat/earlier.js +0 -54
- package/dist/chat/input.d.ts +0 -9
- package/dist/chat/input.d.ts.map +0 -1
- package/dist/chat/input.js +0 -99
- package/dist/chat/kit.d.ts +0 -61
- package/dist/chat/kit.d.ts.map +0 -1
- package/dist/chat/kit.js +0 -123
- package/dist/chat/lasting.d.ts +0 -6
- package/dist/chat/lasting.d.ts.map +0 -1
- package/dist/chat/lasting.js +0 -19
- package/dist/chat/list.d.ts +0 -28
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -103
- package/dist/chat/markdown.d.ts +0 -21
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -246
- package/dist/chat/message.d.ts +0 -7
- package/dist/chat/message.d.ts.map +0 -1
- package/dist/chat/message.js +0 -202
- package/dist/chat/messages.d.ts +0 -24
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -187
- package/dist/chat/name.d.ts +0 -6
- package/dist/chat/name.d.ts.map +0 -1
- package/dist/chat/name.js +0 -37
- package/dist/chat/opened.d.ts +0 -11
- package/dist/chat/opened.d.ts.map +0 -1
- package/dist/chat/opened.js +0 -17
- package/dist/chat/overlay.d.ts +0 -32
- package/dist/chat/overlay.d.ts.map +0 -1
- package/dist/chat/overlay.js +0 -107
- package/dist/chat/parts.d.ts +0 -57
- package/dist/chat/parts.d.ts.map +0 -1
- package/dist/chat/parts.js +0 -119
- package/dist/chat/pieces.d.ts +0 -62
- package/dist/chat/pieces.d.ts.map +0 -1
- package/dist/chat/pieces.js +0 -1110
- package/dist/chat/process.d.ts +0 -20
- package/dist/chat/process.d.ts.map +0 -1
- package/dist/chat/process.js +0 -32
- package/dist/chat/provider.d.ts +0 -64
- package/dist/chat/provider.d.ts.map +0 -1
- package/dist/chat/provider.js +0 -638
- package/dist/chat/question.d.ts +0 -9
- package/dist/chat/question.d.ts.map +0 -1
- package/dist/chat/question.js +0 -69
- package/dist/chat/questions.d.ts +0 -31
- package/dist/chat/questions.d.ts.map +0 -1
- package/dist/chat/questions.js +0 -111
- package/dist/chat/read.d.ts +0 -5
- package/dist/chat/read.d.ts.map +0 -1
- package/dist/chat/read.js +0 -13
- package/dist/chat/ready.d.ts +0 -34
- package/dist/chat/ready.d.ts.map +0 -1
- package/dist/chat/ready.js +0 -91
- package/dist/chat/steady.d.ts +0 -3
- package/dist/chat/steady.d.ts.map +0 -1
- package/dist/chat/steady.js +0 -26
- package/dist/chat/style.d.ts +0 -4
- package/dist/chat/style.d.ts.map +0 -1
- package/dist/chat/style.js +0 -302
- package/dist/chat.d.ts +0 -14
- package/dist/chat.d.ts.map +0 -1
- package/dist/chat.js +0 -8
- package/dist/read-side.d.ts +0 -27
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -826
package/dist/save.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { definitionOf } from './model.js';
|
|
2
|
-
import { apply, ATTACHMENT, links, parse, pointerSegments, references, relative, relativize, retarget, serialize, target, } from '@agentshouse/mdmodel';
|
|
3
|
-
import { immutable, queuedFile, departure, } from './change.js';
|
|
4
|
-
import {
|
|
2
|
+
import { apply, ARRAY_INDEX, ATTACHMENT, links, parse, pointerSegments, references, relative, relativize, retarget, roomReferences, serialize, target, } from '@agentshouse/mdmodel';
|
|
3
|
+
import { immutable, leaving, queuedFile, departure, } from './change.js';
|
|
4
|
+
import { collectionOf, documentsOf, governing, } from './collection.js';
|
|
5
5
|
import { changeBatchIdentity } from './identity.js';
|
|
6
6
|
import { overlaying } from './overlay.js';
|
|
7
7
|
import { covered, normalize } from './paths.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { sorting } from './shared.js';
|
|
9
|
+
import { paced, ROW_WEIGHT, stepped } from './turn.js';
|
|
10
|
+
import { validatedCollection } from './validation.js';
|
|
10
11
|
function refuse(code, file, location) {
|
|
11
12
|
return {
|
|
12
13
|
kind: 'refused',
|
|
@@ -143,12 +144,13 @@ function* rewrites(changes, visible) {
|
|
|
143
144
|
return { kind: 'changes', changes };
|
|
144
145
|
const merged = [...changes];
|
|
145
146
|
const added = [];
|
|
146
|
-
for (const [path, document] of documentsOf(visible.collection)) {
|
|
147
|
+
for (const [path, document] of yield* documentsOf(visible.collection)) {
|
|
147
148
|
if (settled.has(path))
|
|
148
149
|
continue;
|
|
149
150
|
const moved = moves.get(path) ?? path;
|
|
150
151
|
const definition = definitionOf(governing(visible.collection.composition, path), document.type);
|
|
151
|
-
|
|
152
|
+
const repointing = repointed(document, definition, path, moved, moves, settled);
|
|
153
|
+
added.push(...repointing);
|
|
152
154
|
const own = parts.get(path) ?? [];
|
|
153
155
|
const relinked = relinks(path, moved, edited(document, own.map((index) => merged[index])), moves);
|
|
154
156
|
if (relinked.kind === 'refused')
|
|
@@ -175,7 +177,9 @@ function* rewrites(changes, visible) {
|
|
|
175
177
|
else
|
|
176
178
|
merged[index] = change;
|
|
177
179
|
}
|
|
178
|
-
|
|
180
|
+
const text = visible.collection.text[path].length;
|
|
181
|
+
const rewritten = repointing.length > 0 || relinked.changes.length > 0;
|
|
182
|
+
yield ROW_WEIGHT + text + (rewritten ? text : 0);
|
|
179
183
|
}
|
|
180
184
|
return {
|
|
181
185
|
kind: 'changes',
|
|
@@ -199,6 +203,71 @@ export function* providerRewritten(batch, visible) {
|
|
|
199
203
|
...(yield* anticipated(batch.changes, batch.preconditions, rewritten.changes, visible)),
|
|
200
204
|
};
|
|
201
205
|
}
|
|
206
|
+
function crossed(document, definition, path, to) {
|
|
207
|
+
const changes = [];
|
|
208
|
+
for (const link of links(document, path)) {
|
|
209
|
+
const written = to(link.target, link.href);
|
|
210
|
+
if (written !== undefined)
|
|
211
|
+
changes.push({ kind: 'link', href: link.href, to: written });
|
|
212
|
+
}
|
|
213
|
+
for (const { pointer, value } of definition === undefined
|
|
214
|
+
? []
|
|
215
|
+
: references(document, definition)) {
|
|
216
|
+
const named = target(path, value);
|
|
217
|
+
const written = named === null ? undefined : to(named, value);
|
|
218
|
+
if (written !== undefined) {
|
|
219
|
+
changes.push({ kind: 'reference', pointer, value: retarget(value, written), prior: value });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return changes;
|
|
223
|
+
}
|
|
224
|
+
function rewritten(path, document, changes) {
|
|
225
|
+
if (changes.length === 0)
|
|
226
|
+
return [];
|
|
227
|
+
const applied = apply(document, changes);
|
|
228
|
+
if (applied.outcome !== 'applied')
|
|
229
|
+
return [];
|
|
230
|
+
const after = applied.document;
|
|
231
|
+
if (document.type === undefined)
|
|
232
|
+
return [{ kind: 'replace', path, content: serialize(after) }];
|
|
233
|
+
const names = new Set(changes.flatMap((change) => change.kind === 'reference' ? [pointerSegments(change.pointer)[0] ?? ''] : []));
|
|
234
|
+
return [
|
|
235
|
+
...[...names].map((name) => ({ kind: 'field', path, name, value: after.fields[name] })),
|
|
236
|
+
...bodies(path, document, after),
|
|
237
|
+
];
|
|
238
|
+
}
|
|
239
|
+
function roomPathOf(handle, path) {
|
|
240
|
+
return `/rooms/${handle}/${path}`;
|
|
241
|
+
}
|
|
242
|
+
export function* leavingRewritten(batch, visible) {
|
|
243
|
+
const between = batch.between;
|
|
244
|
+
if (between === undefined)
|
|
245
|
+
return batch;
|
|
246
|
+
const moved = new Map(between.moves.map(({ path, to }) => [path, roomPathOf(between.destination.handle, to)]));
|
|
247
|
+
const derived = [];
|
|
248
|
+
for (const [path, document] of yield* documentsOf(visible.collection)) {
|
|
249
|
+
if (moved.has(path))
|
|
250
|
+
continue;
|
|
251
|
+
const definition = definitionOf(governing(visible.collection.composition, path), document.type);
|
|
252
|
+
derived.push(...rewritten(path, document, crossed(document, definition, path, (named) => moved.get(named))));
|
|
253
|
+
yield ROW_WEIGHT + visible.collection.text[path].length;
|
|
254
|
+
}
|
|
255
|
+
if (derived.length === 0)
|
|
256
|
+
return batch;
|
|
257
|
+
return {
|
|
258
|
+
...batch,
|
|
259
|
+
changes: [...derived, ...batch.changes],
|
|
260
|
+
preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...batch.preconditions],
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
export function rewriting(batch, locator) {
|
|
264
|
+
return leaving(batch, locator) || batch.changes.some((change) => change.kind === 'move');
|
|
265
|
+
}
|
|
266
|
+
export function* effectiveIn(batch, locator, visible) {
|
|
267
|
+
return leaving(batch, locator)
|
|
268
|
+
? yield* leavingRewritten(batch, visible)
|
|
269
|
+
: yield* providerRewritten(batch, visible);
|
|
270
|
+
}
|
|
202
271
|
function* incoming(collection, removed, candidates) {
|
|
203
272
|
const found = new Map();
|
|
204
273
|
const note = (target, file) => {
|
|
@@ -209,13 +278,19 @@ function* incoming(collection, removed, candidates) {
|
|
|
209
278
|
found.set(target, referrers);
|
|
210
279
|
};
|
|
211
280
|
const read = function* (paths) {
|
|
212
|
-
|
|
281
|
+
const seen = new Set();
|
|
282
|
+
for (const path of paths) {
|
|
283
|
+
if (seen.has(path))
|
|
284
|
+
continue;
|
|
285
|
+
seen.add(path);
|
|
213
286
|
const document = collection.document(path);
|
|
214
287
|
if (document !== undefined)
|
|
215
288
|
yield [path, document];
|
|
216
289
|
}
|
|
217
290
|
};
|
|
218
|
-
for (const [path, document] of candidates === null
|
|
291
|
+
for (const [path, document] of candidates === null
|
|
292
|
+
? yield* documentsOf(collection)
|
|
293
|
+
: read(candidates)) {
|
|
219
294
|
const definition = definitionOf(governing(collection.composition, path), document.type);
|
|
220
295
|
for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
|
|
221
296
|
if (to.includes(ATTACHMENT))
|
|
@@ -226,7 +301,7 @@ function* incoming(collection, removed, candidates) {
|
|
|
226
301
|
}
|
|
227
302
|
yield ROW_WEIGHT + collection.text[path].length;
|
|
228
303
|
}
|
|
229
|
-
const [first] =
|
|
304
|
+
const [first] = yield* sorting(found.keys());
|
|
230
305
|
if (first === undefined)
|
|
231
306
|
return null;
|
|
232
307
|
const every = new Set();
|
|
@@ -239,14 +314,14 @@ function* incoming(collection, removed, candidates) {
|
|
|
239
314
|
code: 'referenced-missing-file',
|
|
240
315
|
retryable: false,
|
|
241
316
|
file: first,
|
|
242
|
-
referrers:
|
|
317
|
+
referrers: yield* sorting(every),
|
|
243
318
|
},
|
|
244
319
|
};
|
|
245
320
|
}
|
|
246
321
|
function affected(change) {
|
|
247
322
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
248
323
|
}
|
|
249
|
-
function guard(changes, input) {
|
|
324
|
+
function guard(changes, input, removing) {
|
|
250
325
|
const overridden = new Map();
|
|
251
326
|
const existing = {
|
|
252
327
|
has: (path) => overridden.get(path) ??
|
|
@@ -262,11 +337,11 @@ function guard(changes, input) {
|
|
|
262
337
|
if (covered(input.providerOwnedPaths, normalized)) {
|
|
263
338
|
return refuse('provider-owned-path', normalized);
|
|
264
339
|
}
|
|
265
|
-
const operation = change.kind === 'remove' ?
|
|
340
|
+
const operation = change.kind === 'remove' ? removing : 'write';
|
|
266
341
|
if (!input.local && !covered(input.operations[operation], normalized))
|
|
267
342
|
return refuse(`${operation}-grant`, normalized);
|
|
268
343
|
}
|
|
269
|
-
if (change.kind === 'create') {
|
|
344
|
+
if (change.kind === 'create' || change.kind === 'arrival') {
|
|
270
345
|
if (existing.has(change.path))
|
|
271
346
|
return refuse('path-collision', change.path);
|
|
272
347
|
existing.add(change.path);
|
|
@@ -326,7 +401,7 @@ export function queuedDocuments(queue) {
|
|
|
326
401
|
const paths = new Set();
|
|
327
402
|
for (const batch of queue) {
|
|
328
403
|
for (const change of batch.changes) {
|
|
329
|
-
if (change.kind === 'binary')
|
|
404
|
+
if (change.kind === 'binary' || (change.kind === 'arrival' && !('content' in change)))
|
|
330
405
|
continue;
|
|
331
406
|
if (change.kind === 'remove')
|
|
332
407
|
paths.delete(change.path);
|
|
@@ -417,7 +492,10 @@ function depth(path) {
|
|
|
417
492
|
function* exceeding(limits, changes, effective, resulting) {
|
|
418
493
|
const written = effective.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
419
494
|
const created = changes
|
|
420
|
-
.filter((change) => change.kind === 'create' ||
|
|
495
|
+
.filter((change) => change.kind === 'create' ||
|
|
496
|
+
change.kind === 'move' ||
|
|
497
|
+
change.kind === 'binary' ||
|
|
498
|
+
change.kind === 'arrival')
|
|
421
499
|
.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
422
500
|
for (const limit of limits) {
|
|
423
501
|
const value = limit.value;
|
|
@@ -503,18 +581,6 @@ function relativeChanges(input) {
|
|
|
503
581
|
return { ...change, fields, sections };
|
|
504
582
|
});
|
|
505
583
|
}
|
|
506
|
-
function* unmodeledType(collection, composition) {
|
|
507
|
-
if (available(composition))
|
|
508
|
-
return false;
|
|
509
|
-
for (const [path, document] of documentsOf(collection)) {
|
|
510
|
-
if (document.type !== undefined &&
|
|
511
|
-
document.type !== 'any' &&
|
|
512
|
-
governing(composition, path).status === 'unavailable')
|
|
513
|
-
return true;
|
|
514
|
-
yield ROW_WEIGHT + collection.text[path].length;
|
|
515
|
-
}
|
|
516
|
-
return false;
|
|
517
|
-
}
|
|
518
584
|
function unqueueable(file) {
|
|
519
585
|
return { kind: 'refused', refusal: { code: 'unknown-revision', retryable: false, file } };
|
|
520
586
|
}
|
|
@@ -617,8 +683,8 @@ function folded(changes, change, appending, deferred) {
|
|
|
617
683
|
changes.push(change);
|
|
618
684
|
return 'appended';
|
|
619
685
|
}
|
|
620
|
-
function relinked(batch, move, before, after, appending) {
|
|
621
|
-
const derived =
|
|
686
|
+
function* relinked(batch, move, before, after, appending) {
|
|
687
|
+
const derived = yield* rewrites([move], after);
|
|
622
688
|
if (derived.kind === 'refused')
|
|
623
689
|
return derived;
|
|
624
690
|
const changes = [...batch.changes];
|
|
@@ -645,6 +711,7 @@ function relinked(batch, move, before, after, appending) {
|
|
|
645
711
|
if (!changed)
|
|
646
712
|
return { kind: 'relinked', batch };
|
|
647
713
|
const held = new Set(batch.preconditions.map(identity));
|
|
714
|
+
const added = yield* preconditionsOf(appended, before, []);
|
|
648
715
|
return {
|
|
649
716
|
kind: 'relinked',
|
|
650
717
|
batch: immutable({
|
|
@@ -653,7 +720,7 @@ function relinked(batch, move, before, after, appending) {
|
|
|
653
720
|
changes,
|
|
654
721
|
preconditions: [
|
|
655
722
|
...batch.preconditions,
|
|
656
|
-
...
|
|
723
|
+
...added.filter((precondition) => !held.has(identity(precondition))),
|
|
657
724
|
],
|
|
658
725
|
}),
|
|
659
726
|
};
|
|
@@ -699,7 +766,7 @@ function withdrawnFile(batch, change, at) {
|
|
|
699
766
|
];
|
|
700
767
|
return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
|
|
701
768
|
}
|
|
702
|
-
export function
|
|
769
|
+
export function* requeuing(queue, changes, visible, changeable, state) {
|
|
703
770
|
const next = [...queue];
|
|
704
771
|
const origins = new Map();
|
|
705
772
|
const withdrawn = new Map();
|
|
@@ -722,19 +789,19 @@ export function requeued(queue, changes, visible, changeable, state) {
|
|
|
722
789
|
return unqueueable(change.path);
|
|
723
790
|
const following = next.slice(index);
|
|
724
791
|
const rebuilt = [];
|
|
725
|
-
let before = change.kind === 'move' ? state(next.slice(0, index)) : visible;
|
|
792
|
+
let before = change.kind === 'move' ? yield* state(next.slice(0, index)) : visible;
|
|
726
793
|
for (const [offset, batch] of following.entries()) {
|
|
727
794
|
let current = batch;
|
|
728
795
|
if (change.kind === 'move') {
|
|
729
|
-
const after = state(next.slice(0, index + offset + 1));
|
|
796
|
+
const after = yield* state(next.slice(0, index + offset + 1));
|
|
730
797
|
const appending = offset === 0 && created(batch, queuedFile(batch, change.path, held.localId));
|
|
731
|
-
const relinking = relinked(batch, change, before, after, appending);
|
|
798
|
+
const relinking = yield* relinked(batch, change, before, after, appending);
|
|
732
799
|
if (relinking.kind === 'refused')
|
|
733
800
|
return relinking;
|
|
734
801
|
current = relinking.batch;
|
|
735
802
|
before = after;
|
|
736
803
|
}
|
|
737
|
-
if (current !== batch && !open(batch))
|
|
804
|
+
if (current !== batch && (!open(batch) || batch.between !== undefined))
|
|
738
805
|
return unqueueable(change.path);
|
|
739
806
|
rebuilt.push(offset === 0
|
|
740
807
|
? withdrawnFile(current, change, queuedFile(current, change.path, held.localId))
|
|
@@ -804,67 +871,66 @@ export function joined(queue, owner, extra) {
|
|
|
804
871
|
});
|
|
805
872
|
return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
|
|
806
873
|
}
|
|
807
|
-
export function
|
|
874
|
+
export function* limiting(queue, batch, limits, state) {
|
|
808
875
|
const at = queue.indexOf(batch);
|
|
809
|
-
const
|
|
876
|
+
const rewritten = yield* providerRewritten(batch, yield* state(queue.slice(0, at)));
|
|
877
|
+
const limited = yield* exceeding(limits, batch.changes, rewritten.changes, yield* state(queue.slice(0, at + 1)));
|
|
810
878
|
return limited?.kind === 'refused' ? limited.refusal : null;
|
|
811
879
|
}
|
|
812
|
-
function
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
const
|
|
827
|
-
const
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
880
|
+
function typedText(content) {
|
|
881
|
+
if (content === undefined)
|
|
882
|
+
return false;
|
|
883
|
+
const parsed = parse(content);
|
|
884
|
+
return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
|
|
885
|
+
}
|
|
886
|
+
function touchedLibrary(resulting, touched, composition) {
|
|
887
|
+
const entries = touched
|
|
888
|
+
.flatMap((path) => {
|
|
889
|
+
const content = resulting.documents[path];
|
|
890
|
+
const binary = resulting.binaries[path] !== undefined;
|
|
891
|
+
return content === undefined && !binary ? [] : [{ path, content: content ?? null }];
|
|
892
|
+
})
|
|
893
|
+
.sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0));
|
|
894
|
+
const contents = new Map(entries.map(({ path, content }) => [path, content]));
|
|
895
|
+
const binaries = new Set(Object.keys(resulting.binaries).filter((path) => resulting.binaries[path] !== undefined));
|
|
896
|
+
return validatedCollection(() => Promise.resolve({ entries, next: null }), async (paths) => {
|
|
897
|
+
const known = new Map();
|
|
898
|
+
const pace = paced();
|
|
899
|
+
for (const path of paths) {
|
|
900
|
+
const content = contents.get(path);
|
|
901
|
+
if (binaries.has(path))
|
|
902
|
+
known.set(path, { type: undefined, binary: true });
|
|
903
|
+
else if (content !== undefined && content !== null) {
|
|
904
|
+
const read = parse(content, path);
|
|
905
|
+
if (read.ok)
|
|
906
|
+
known.set(path, { type: read.document.type ?? 'any', binary: false });
|
|
907
|
+
await pace(content.length + ROW_WEIGHT);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return known;
|
|
911
|
+
}, composition);
|
|
912
|
+
}
|
|
913
|
+
async function judging(visible, resulting, composition, validate, removed, touched) {
|
|
833
914
|
if (removed.size > 0) {
|
|
834
|
-
const
|
|
835
|
-
const referenced = yield* incoming(collectionOf(resulting.documents, input.composition), removed, clean === null || referrers === null
|
|
836
|
-
? null
|
|
837
|
-
: [...referrers, ...(yield* differences(clean.text, resulting.documents))]);
|
|
915
|
+
const referenced = await stepped(incoming(collectionOf(resulting.documents, composition), removed, null));
|
|
838
916
|
if (referenced !== null)
|
|
839
917
|
return referenced;
|
|
840
918
|
}
|
|
841
|
-
const typed = (content) => {
|
|
842
|
-
if (content === undefined)
|
|
843
|
-
return false;
|
|
844
|
-
const parsed = parse(content);
|
|
845
|
-
return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
|
|
846
|
-
};
|
|
847
|
-
const touched = [...new Set(predicted.changes.flatMap(affected))];
|
|
848
919
|
const typedPaths = [];
|
|
920
|
+
const pace = paced();
|
|
849
921
|
for (const path of touched) {
|
|
850
|
-
const before =
|
|
922
|
+
const before = visible.documents[path];
|
|
851
923
|
const after = resulting.documents[path];
|
|
852
|
-
if (
|
|
924
|
+
if (typedText(before) || typedText(after))
|
|
853
925
|
typedPaths.push(path);
|
|
854
|
-
|
|
926
|
+
await pace((before?.length ?? 0) + (after?.length ?? 0) + ROW_WEIGHT);
|
|
855
927
|
}
|
|
856
|
-
const
|
|
857
|
-
const withoutModel = typedPaths.find((path) => governing(input.composition, path).status === 'unavailable');
|
|
928
|
+
const withoutModel = typedPaths.find((path) => governing(composition, path).status === 'unavailable');
|
|
858
929
|
if (withoutModel !== undefined)
|
|
859
930
|
return refuse('model-unavailable', withoutModel);
|
|
860
|
-
const verdict =
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
: yield* validateCollection(needsModel
|
|
864
|
-
? resulting.documents
|
|
865
|
-
: Object.fromEntries(touched.flatMap((path) => resulting.documents[path] === undefined
|
|
866
|
-
? []
|
|
867
|
-
: [[path, resulting.documents[path]]])), Object.keys(resulting.binaries), input.composition);
|
|
931
|
+
const verdict = typedPaths.length > 0
|
|
932
|
+
? await validate(resulting, touched)
|
|
933
|
+
: await touchedLibrary(resulting, touched, composition);
|
|
868
934
|
if (verdict.verdict === 'refuse') {
|
|
869
935
|
return {
|
|
870
936
|
kind: 'refused',
|
|
@@ -877,12 +943,92 @@ function* batching(input) {
|
|
|
877
943
|
},
|
|
878
944
|
};
|
|
879
945
|
}
|
|
880
|
-
|
|
946
|
+
return {
|
|
947
|
+
kind: 'judged',
|
|
948
|
+
validated: typedPaths
|
|
949
|
+
.map((path) => governing(composition, path))
|
|
950
|
+
.find((model) => model.status !== 'unavailable'),
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
function placed(reference) {
|
|
954
|
+
const key = roomPathOf(reference.handle, reference.path);
|
|
955
|
+
if (reference.kind === 'link')
|
|
956
|
+
return `link ${key}`;
|
|
957
|
+
if (reference.kind === 'item')
|
|
958
|
+
return `section ${reference.section} ${key}`;
|
|
959
|
+
const pointer = reference.pointer
|
|
960
|
+
.split('/')
|
|
961
|
+
.map((segment) => (ARRAY_INDEX.test(segment) ? '-' : segment))
|
|
962
|
+
.join('/');
|
|
963
|
+
return `field ${pointer} ${key}`;
|
|
964
|
+
}
|
|
965
|
+
function roomReferencesIn(path, content, composition) {
|
|
966
|
+
if (content === undefined)
|
|
967
|
+
return [];
|
|
968
|
+
const parsed = parse(content, path);
|
|
969
|
+
if (!parsed.ok)
|
|
970
|
+
return [];
|
|
971
|
+
return roomReferences(path, content, definitionOf(governing(composition, path), parsed.document.type));
|
|
972
|
+
}
|
|
973
|
+
function* admittedRooms(changes, visible, resulting, composition) {
|
|
974
|
+
const from = new Map();
|
|
975
|
+
for (const [index, change] of changes.entries()) {
|
|
976
|
+
if (change.kind === 'move')
|
|
977
|
+
from.set(change.to, departure(changes, index));
|
|
978
|
+
}
|
|
979
|
+
for (const path of [...new Set(changes.flatMap(affected))].sort()) {
|
|
980
|
+
const now = resulting.documents[path];
|
|
981
|
+
if (now === undefined)
|
|
982
|
+
continue;
|
|
983
|
+
const held = new Set(roomReferencesIn(path, visible.documents[from.get(path) ?? path], composition).map(placed));
|
|
984
|
+
for (const reference of roomReferencesIn(path, now, composition)) {
|
|
985
|
+
if (reference.kind === 'link' || reference.crossRoom === true)
|
|
986
|
+
continue;
|
|
987
|
+
if (held.has(placed(reference)))
|
|
988
|
+
continue;
|
|
989
|
+
return refuse('unresolved-reference', path, reference.kind === 'field' ? reference.pointer : reference.section);
|
|
990
|
+
}
|
|
991
|
+
yield ROW_WEIGHT + now.length;
|
|
992
|
+
}
|
|
993
|
+
return null;
|
|
994
|
+
}
|
|
995
|
+
function validationOf(validated) {
|
|
996
|
+
return validated === undefined
|
|
997
|
+
? {}
|
|
998
|
+
: {
|
|
999
|
+
validation: {
|
|
1000
|
+
identity: validated.snapshot.identity,
|
|
1001
|
+
version: validated.snapshot.version,
|
|
1002
|
+
source: validated.status,
|
|
1003
|
+
},
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
async function batching(input) {
|
|
1007
|
+
const changes = relativeChanges(input);
|
|
1008
|
+
const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
|
|
1009
|
+
if (structural !== undefined && input.unmodeled) {
|
|
1010
|
+
return refuse('model-unavailable', structural.path);
|
|
1011
|
+
}
|
|
1012
|
+
if (changes.length === 0)
|
|
1013
|
+
return refuse('no-change');
|
|
1014
|
+
const rewritten = await stepped(rewrites(changes, input.visible));
|
|
1015
|
+
if (rewritten.kind === 'refused')
|
|
1016
|
+
return rewritten;
|
|
1017
|
+
const refusal = guard(changes, input, 'delete');
|
|
1018
|
+
if (refusal !== null)
|
|
1019
|
+
return refusal;
|
|
1020
|
+
const preconditions = await stepped(preconditionsOf(changes, input.visible, input.bases));
|
|
1021
|
+
const predicted = await stepped(anticipated(changes, preconditions, rewritten.changes, input.visible));
|
|
1022
|
+
const resulting = await stepped(overlaying(input.visible, [{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted }]));
|
|
1023
|
+
const judged = await judging(input.visible, resulting, input.composition, input.validate, new Set(changes.filter((change) => change.kind === 'remove').map((change) => change.path)), [...new Set(predicted.changes.flatMap(affected))]);
|
|
1024
|
+
if (judged.kind === 'refused')
|
|
1025
|
+
return judged;
|
|
1026
|
+
const crossing = await stepped(admittedRooms(changes, input.visible, resulting, input.composition));
|
|
1027
|
+
if (crossing !== null)
|
|
1028
|
+
return crossing;
|
|
1029
|
+
const limited = await stepped(exceeding(input.providerLimits, changes, predicted.changes, resulting));
|
|
881
1030
|
if (limited !== null)
|
|
882
1031
|
return limited;
|
|
883
|
-
const validated = typedPaths
|
|
884
|
-
.map((path) => governing(input.composition, path))
|
|
885
|
-
.find((model) => model.status !== 'unavailable');
|
|
886
1032
|
return {
|
|
887
1033
|
kind: 'batch',
|
|
888
1034
|
batch: immutable({
|
|
@@ -891,18 +1037,126 @@ function* batching(input) {
|
|
|
891
1037
|
changes,
|
|
892
1038
|
preconditions,
|
|
893
1039
|
...(input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy }),
|
|
894
|
-
...(validated
|
|
895
|
-
? {}
|
|
896
|
-
: {
|
|
897
|
-
validation: {
|
|
898
|
-
identity: validated.snapshot.identity,
|
|
899
|
-
version: validated.snapshot.version,
|
|
900
|
-
source: validated.status,
|
|
901
|
-
},
|
|
902
|
-
}),
|
|
1040
|
+
...validationOf(judged.validated),
|
|
903
1041
|
}),
|
|
904
1042
|
};
|
|
905
1043
|
}
|
|
1044
|
+
export const MOVE_DOCUMENTS = 'move-documents';
|
|
1045
|
+
function heldAt(visible, path) {
|
|
1046
|
+
return visible.documents[path] !== undefined || visible.binaries[path] !== undefined;
|
|
1047
|
+
}
|
|
1048
|
+
function arrivingText(path, to, source, moved) {
|
|
1049
|
+
const content = source.visible.documents[path];
|
|
1050
|
+
const document = source.visible.collection.document(path);
|
|
1051
|
+
if (document === undefined)
|
|
1052
|
+
return content;
|
|
1053
|
+
const definition = definitionOf(governing(source.composition, path), document.type);
|
|
1054
|
+
const changes = crossed(document, definition, path, (named, href) => {
|
|
1055
|
+
const at = moved.get(named);
|
|
1056
|
+
if (at !== undefined)
|
|
1057
|
+
return target(to, href) === at ? undefined : relative(to, at);
|
|
1058
|
+
return heldAt(source.visible, named) ? roomPathOf(source.handle, named) : undefined;
|
|
1059
|
+
});
|
|
1060
|
+
if (changes.length === 0)
|
|
1061
|
+
return content;
|
|
1062
|
+
const applied = apply(document, changes);
|
|
1063
|
+
return applied.outcome === 'applied' ? serialize(applied.document) : content;
|
|
1064
|
+
}
|
|
1065
|
+
function arrivalOf({ path, to }, source, moved) {
|
|
1066
|
+
const { documents, revisions, fileIds, changes, binaries } = source.visible;
|
|
1067
|
+
const kept = {
|
|
1068
|
+
...(fileIds[path] === undefined ? {} : { fileId: fileIds[path] }),
|
|
1069
|
+
...(changes[path] === undefined ? {} : { change: changes[path] }),
|
|
1070
|
+
};
|
|
1071
|
+
const heads = {
|
|
1072
|
+
...(revisions[path] === undefined ? {} : { revision: revisions[path] }),
|
|
1073
|
+
...kept,
|
|
1074
|
+
};
|
|
1075
|
+
const binary = binaries[path];
|
|
1076
|
+
if (binary?.state === 'accepted') {
|
|
1077
|
+
const { byteLength, mediaType, sha256 } = binary;
|
|
1078
|
+
return {
|
|
1079
|
+
kind: 'arrival',
|
|
1080
|
+
path: to,
|
|
1081
|
+
binary: { byteLength, mediaType, sha256 },
|
|
1082
|
+
...heads,
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
const content = arrivingText(path, to, source, moved);
|
|
1086
|
+
return {
|
|
1087
|
+
kind: 'arrival',
|
|
1088
|
+
path: to,
|
|
1089
|
+
content,
|
|
1090
|
+
...(content === documents[path] ? heads : kept),
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
async function moving(input) {
|
|
1094
|
+
const { source, destination, moves } = input;
|
|
1095
|
+
if (moves.length === 0)
|
|
1096
|
+
return refuse('no-change');
|
|
1097
|
+
const removals = moves.map(({ path }) => ({ kind: 'remove', path }));
|
|
1098
|
+
const leaves = guard(removals, { ...source, local: input.local, bases: [] }, 'write');
|
|
1099
|
+
if (leaves !== null)
|
|
1100
|
+
return leaves;
|
|
1101
|
+
const uploading = moves.find(({ path }) => source.visible.binaries[path]?.state === 'pending');
|
|
1102
|
+
if (uploading !== undefined)
|
|
1103
|
+
return refuse('unknown-revision', uploading.path);
|
|
1104
|
+
const moved = new Map(moves.map(({ path, to }) => [path, to]));
|
|
1105
|
+
const arrivals = moves.map((move) => arrivalOf(move, source, moved));
|
|
1106
|
+
const arrives = guard(arrivals, { ...destination, local: input.local, bases: [] }, 'write');
|
|
1107
|
+
if (arrives !== null)
|
|
1108
|
+
return arrives;
|
|
1109
|
+
const bound = input.providerLimits.find((limit) => limit.name === MOVE_DOCUMENTS)?.value;
|
|
1110
|
+
if (typeof bound === 'number' && moves.length > bound) {
|
|
1111
|
+
return refuse('provider-limit', undefined, MOVE_DOCUMENTS);
|
|
1112
|
+
}
|
|
1113
|
+
if (source.unmodeled)
|
|
1114
|
+
return refuse('model-unavailable', moves[0].path);
|
|
1115
|
+
const id = changeBatchIdentity();
|
|
1116
|
+
const between = {
|
|
1117
|
+
source: { room: source.room, baseRoomVersion: source.baseRoomVersion },
|
|
1118
|
+
destination: {
|
|
1119
|
+
room: destination.room,
|
|
1120
|
+
baseRoomVersion: destination.baseRoomVersion,
|
|
1121
|
+
handle: destination.handle,
|
|
1122
|
+
},
|
|
1123
|
+
moves,
|
|
1124
|
+
};
|
|
1125
|
+
const written = input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy };
|
|
1126
|
+
const leavingBatch = {
|
|
1127
|
+
id,
|
|
1128
|
+
baseRoomVersion: source.baseRoomVersion,
|
|
1129
|
+
changes: removals,
|
|
1130
|
+
preconditions: moves.map(({ path }) => existence(source.visible, path)),
|
|
1131
|
+
...written,
|
|
1132
|
+
between,
|
|
1133
|
+
};
|
|
1134
|
+
const effective = await stepped(leavingRewritten(leavingBatch, source.visible));
|
|
1135
|
+
const left = await stepped(overlaying(source.visible, [effective]));
|
|
1136
|
+
const sourceJudged = await judging(source.visible, left, source.composition, source.validate, new Set(moves.map(({ path }) => path)), [...new Set(effective.changes.map((change) => change.path))]);
|
|
1137
|
+
if (sourceJudged.kind === 'refused')
|
|
1138
|
+
return sourceJudged;
|
|
1139
|
+
const arrivingBatch = {
|
|
1140
|
+
id,
|
|
1141
|
+
baseRoomVersion: destination.baseRoomVersion,
|
|
1142
|
+
changes: arrivals,
|
|
1143
|
+
preconditions: moves.map(({ to }) => ({ kind: 'absent', path: to })),
|
|
1144
|
+
...written,
|
|
1145
|
+
between,
|
|
1146
|
+
};
|
|
1147
|
+
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch]));
|
|
1148
|
+
const destinationJudged = await judging(destination.visible, arrived, destination.composition, destination.validate, new Set(), moves.map(({ to }) => to));
|
|
1149
|
+
if (destinationJudged.kind === 'refused')
|
|
1150
|
+
return destinationJudged;
|
|
1151
|
+
const limited = (await stepped(exceeding(input.providerLimits, removals, effective.changes, left))) ??
|
|
1152
|
+
(await stepped(exceeding(input.providerLimits, arrivals, arrivals, arrived)));
|
|
1153
|
+
if (limited !== null)
|
|
1154
|
+
return limited;
|
|
1155
|
+
return { kind: 'moved', leaving: immutable(leavingBatch), arriving: immutable(arrivingBatch) };
|
|
1156
|
+
}
|
|
1157
|
+
export function buildMove(input) {
|
|
1158
|
+
return moving(input);
|
|
1159
|
+
}
|
|
906
1160
|
export function buildBatch(input) {
|
|
907
|
-
return
|
|
1161
|
+
return batching(input);
|
|
908
1162
|
}
|