@agentshouse/mdruntime 0.1.0-c698c0ebd65bd → 0.1.0-c6b1e70f2da66
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 +19 -16
- package/dist/acceptance.d.ts +6 -0
- package/dist/acceptance.d.ts.map +1 -0
- package/dist/acceptance.js +387 -0
- package/dist/actions.d.ts +20 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +251 -0
- package/dist/adapter.d.ts +155 -86
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +7 -10
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +40 -41
- 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 +31 -11
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +324 -93
- package/dist/cell.d.ts +6 -0
- package/dist/cell.d.ts.map +1 -0
- package/dist/cell.js +9 -0
- package/dist/chain.d.ts +12 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +64 -0
- package/dist/change-set.d.ts +9 -14
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +11 -63
- package/dist/change.d.ts +32 -1
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +14 -3
- package/dist/chat-types.d.ts +5 -0
- package/dist/chat-types.d.ts.map +1 -0
- package/dist/chat-types.js +13 -0
- package/dist/client.d.ts +45 -16
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +104 -13
- package/dist/collection.d.ts +31 -9
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +49 -80
- package/dist/compile.d.ts +16 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +711 -0
- package/dist/connection.d.ts +8 -117
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +106 -2486
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/{chat/context.js → context.js} +1 -4
- package/dist/conversation-live.d.ts +135 -0
- package/dist/conversation-live.d.ts.map +1 -0
- package/dist/conversation-live.js +247 -0
- package/dist/conversation-row.d.ts +33 -0
- package/dist/conversation-row.d.ts.map +1 -0
- package/dist/conversation-row.js +53 -0
- package/dist/conversation.d.ts +55 -42
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +985 -649
- package/dist/digest.d.ts +7 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +113 -0
- package/dist/discovery.d.ts +10 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +175 -0
- package/dist/durability.d.ts +19 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +7 -4
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +33 -28
- package/dist/expectation.d.ts +4 -2
- package/dist/expectation.d.ts.map +1 -1
- package/dist/expectation.js +33 -11
- package/dist/failure.d.ts +1 -0
- package/dist/failure.d.ts.map +1 -1
- package/dist/failure.js +3 -0
- package/dist/figures.d.ts +49 -0
- package/dist/figures.d.ts.map +1 -0
- package/dist/figures.js +197 -0
- package/dist/file.d.ts +2 -19
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +7 -122
- package/dist/holdings.d.ts +29 -0
- package/dist/holdings.d.ts.map +1 -0
- package/dist/holdings.js +95 -0
- package/dist/host.d.ts +11 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +127 -20
- package/dist/index.d.ts +35 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/install.d.ts +56 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +556 -0
- package/dist/job.d.ts +5 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +7 -0
- package/dist/lifecycle.d.ts +7 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +270 -0
- package/dist/listing.d.ts +53 -0
- package/dist/listing.d.ts.map +1 -0
- package/dist/listing.js +176 -0
- package/dist/live.d.ts +23 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +165 -0
- package/dist/locks.d.ts +9 -0
- package/dist/locks.d.ts.map +1 -0
- package/dist/locks.js +62 -0
- package/dist/log.d.ts +1 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/lost.d.ts +5 -0
- package/dist/lost.d.ts.map +1 -0
- package/dist/lost.js +61 -0
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +218 -0
- package/dist/memory.d.ts +19 -6
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +251 -97
- package/dist/messages.d.ts +49 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +148 -0
- package/dist/model.d.ts +11 -12
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +7 -15
- package/dist/models.d.ts +14 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +197 -0
- 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 +22 -4
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +127 -26
- package/dist/opened.d.ts +22 -0
- package/dist/opened.d.ts.map +1 -0
- package/dist/opened.js +107 -0
- package/dist/opening.d.ts +4 -0
- package/dist/opening.d.ts.map +1 -0
- package/dist/opening.js +60 -0
- package/dist/outcome.d.ts +49 -8
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +42 -0
- package/dist/overlay.d.ts +5 -4
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +132 -192
- package/dist/parts.d.ts +11 -0
- package/dist/parts.d.ts.map +1 -0
- package/dist/parts.js +21 -0
- package/dist/paths.d.ts +2 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +26 -0
- package/dist/preparation.d.ts +20 -0
- package/dist/preparation.d.ts.map +1 -0
- package/dist/preparation.js +233 -0
- package/dist/queries.d.ts +16 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +272 -0
- package/dist/query.d.ts +49 -183
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -464
- package/dist/{chat/questions.d.ts → questions.d.ts} +5 -2
- package/dist/questions.d.ts.map +1 -0
- package/dist/{chat/questions.js → questions.js} +13 -2
- package/dist/reach.d.ts +21 -7
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +292 -91
- package/dist/react.d.ts +20 -6
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +17 -18
- package/dist/{chat/read.d.ts → read.d.ts} +2 -0
- package/dist/read.d.ts.map +1 -0
- package/dist/{chat/read.js → read.js} +4 -0
- package/dist/readiness.d.ts +49 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +27 -0
- package/dist/reads.d.ts +37 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +174 -0
- package/dist/recovery.d.ts +26 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +232 -0
- package/dist/refresh.d.ts +17 -14
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +143 -51
- package/dist/refusal.d.ts +5 -0
- package/dist/refusal.d.ts.map +1 -0
- package/dist/refusal.js +57 -0
- package/dist/replica.d.ts +56 -33
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +338 -159
- package/dist/room-draft.d.ts +39 -9
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +186 -86
- package/dist/room-drafts.d.ts +24 -0
- package/dist/room-drafts.d.ts.map +1 -0
- package/dist/room-drafts.js +217 -0
- package/dist/rows.d.ts +30 -37
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +60 -128
- package/dist/runtime.d.ts +43 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1 -0
- package/dist/save.d.ts +58 -10
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +569 -189
- package/dist/saves.d.ts +70 -0
- package/dist/saves.d.ts.map +1 -0
- package/dist/saves.js +1008 -0
- package/dist/search.d.ts +15 -55
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +10 -121
- package/dist/seed.d.ts +25 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +145 -0
- package/dist/shared.d.ts +10 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +339 -0
- package/dist/shown.d.ts +31 -0
- package/dist/shown.d.ts.map +1 -0
- package/dist/shown.js +290 -0
- package/dist/socket/admission.d.ts +7 -0
- package/dist/socket/admission.d.ts.map +1 -0
- package/dist/socket/admission.js +39 -0
- package/dist/socket/answers.d.ts +6 -0
- package/dist/socket/answers.d.ts.map +1 -0
- package/dist/socket/answers.js +20 -0
- package/dist/socket/lanes.d.ts +20 -0
- package/dist/socket/lanes.d.ts.map +1 -0
- package/dist/socket/lanes.js +77 -0
- package/dist/socket/reads.d.ts +10 -0
- package/dist/socket/reads.d.ts.map +1 -0
- package/dist/socket/reads.js +31 -0
- package/dist/socket/rooms.d.ts +44 -0
- package/dist/socket/rooms.d.ts.map +1 -0
- package/dist/socket/rooms.js +90 -0
- package/dist/socket/session.d.ts +38 -0
- package/dist/socket/session.d.ts.map +1 -0
- package/dist/socket/session.js +23 -0
- package/dist/socket.d.ts +70 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +175 -0
- package/dist/source.d.ts +61 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +82 -0
- package/dist/status.d.ts +5 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -7
- package/dist/store/fences.d.ts +16 -0
- package/dist/store/fences.d.ts.map +1 -0
- package/dist/store/fences.js +26 -0
- package/dist/store/installs.d.ts +30 -0
- package/dist/store/installs.d.ts.map +1 -0
- package/dist/store/installs.js +34 -0
- package/dist/store/readiness.d.ts +10 -0
- package/dist/store/readiness.d.ts.map +1 -0
- package/dist/store/readiness.js +52 -0
- package/dist/store/records.d.ts +210 -0
- package/dist/store/records.d.ts.map +1 -0
- package/dist/store/records.js +428 -0
- package/dist/store/room-held.d.ts +8 -0
- package/dist/store/room-held.d.ts.map +1 -0
- package/dist/store/room-held.js +17 -0
- package/dist/store/space.d.ts +11 -0
- package/dist/store/space.d.ts.map +1 -0
- package/dist/store/space.js +41 -0
- package/dist/store/watchers.d.ts +17 -0
- package/dist/store/watchers.d.ts.map +1 -0
- package/dist/store/watchers.js +56 -0
- package/dist/store/writes.d.ts +24 -0
- package/dist/store/writes.d.ts.map +1 -0
- package/dist/store/writes.js +125 -0
- package/dist/store.d.ts +106 -127
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +136 -575
- package/dist/stored.d.ts +156 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +649 -0
- package/dist/surface.d.ts +88 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +224 -0
- package/dist/sync.d.ts +19 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +532 -0
- package/dist/tables.d.ts +412 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +737 -0
- package/dist/tallies.d.ts +18 -0
- package/dist/tallies.d.ts.map +1 -0
- package/dist/tallies.js +129 -0
- package/dist/transfer.d.ts +0 -5
- package/dist/transfer.d.ts.map +1 -1
- package/dist/turn.d.ts +9 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +75 -0
- package/dist/validation.d.ts +29 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +104 -0
- package/dist/windows.d.ts +11 -0
- package/dist/windows.d.ts.map +1 -0
- package/dist/windows.js +16 -0
- package/dist/wire.d.ts +181 -33
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +62 -31
- package/package.json +19 -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/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 -60
- 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 -26
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -62
- package/dist/chat/markdown.d.ts +0 -20
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -188
- package/dist/chat/message.d.ts +0 -6
- package/dist/chat/message.d.ts.map +0 -1
- package/dist/chat/message.js +0 -192
- package/dist/chat/messages.d.ts +0 -21
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -151
- 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 -31
- package/dist/chat/overlay.d.ts.map +0 -1
- package/dist/chat/overlay.js +0 -97
- 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/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 -63
- package/dist/chat/provider.d.ts.map +0 -1
- package/dist/chat/provider.js +0 -620
- package/dist/chat/question.d.ts +0 -9
- package/dist/chat/question.d.ts.map +0 -1
- package/dist/chat/question.js +0 -80
- package/dist/chat/questions.d.ts.map +0 -1
- package/dist/chat/read.d.ts.map +0 -1
- package/dist/chat/ready.d.ts +0 -33
- package/dist/chat/ready.d.ts.map +0 -1
- package/dist/chat/ready.js +0 -91
- 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/fence.d.ts +0 -6
- package/dist/fence.d.ts.map +0 -1
- package/dist/fence.js +0 -10
- package/dist/read-side.d.ts +0 -18
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -440
package/dist/save.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { collectionOf, documentsOf, governing, validateCollection, } from './collection.js';
|
|
1
|
+
import { apply, ARRAY_INDEX, ATTACHMENT, create, links, parse, pointerSegments, references, relative, relativize, retarget, roomReferences, serialize, target, } from '@agentshouse/mdmodel';
|
|
2
|
+
import { immutable, leaving, queuedFile, departure, } from './change.js';
|
|
3
|
+
import { collectionOf, definitionsAt, documentsOf, governing, } from './collection.js';
|
|
5
4
|
import { changeBatchIdentity } from './identity.js';
|
|
6
|
-
import {
|
|
7
|
-
import { covered, normalize } from './paths.js';
|
|
5
|
+
import { overlaying } from './overlay.js';
|
|
6
|
+
import { ancestors, authoredAt, covered, normalize } from './paths.js';
|
|
7
|
+
import { entriesIn, sorting } from './shared.js';
|
|
8
|
+
import { paced, ROW_WEIGHT, stepped } from './turn.js';
|
|
9
|
+
import { validatedCollection } from './validation.js';
|
|
8
10
|
function refuse(code, file, location) {
|
|
9
11
|
return {
|
|
10
12
|
kind: 'refused',
|
|
@@ -17,10 +19,7 @@ function refuse(code, file, location) {
|
|
|
17
19
|
};
|
|
18
20
|
}
|
|
19
21
|
function repointed(document, definition, from, to, moves, settled) {
|
|
20
|
-
const
|
|
21
|
-
if (!parsed.ok)
|
|
22
|
-
return [];
|
|
23
|
-
let next = parsed.document;
|
|
22
|
+
const changes = [];
|
|
24
23
|
const names = new Set();
|
|
25
24
|
for (const { pointer, value } of definition === undefined
|
|
26
25
|
? []
|
|
@@ -32,38 +31,39 @@ function repointed(document, definition, from, to, moves, settled) {
|
|
|
32
31
|
const destination = moves.get(named) ?? named;
|
|
33
32
|
if (target(to, value) === destination)
|
|
34
33
|
continue;
|
|
35
|
-
|
|
34
|
+
changes.push({
|
|
36
35
|
kind: 'reference',
|
|
37
36
|
pointer,
|
|
38
37
|
value: retarget(value, relative(to, destination)),
|
|
39
38
|
prior: value,
|
|
40
39
|
});
|
|
41
|
-
if (applied.outcome !== 'applied')
|
|
42
|
-
continue;
|
|
43
|
-
next = applied.document;
|
|
44
40
|
names.add(name);
|
|
45
41
|
}
|
|
42
|
+
if (changes.length === 0)
|
|
43
|
+
return [];
|
|
44
|
+
const applied = apply(document, changes, definition);
|
|
45
|
+
if (applied.outcome !== 'applied')
|
|
46
|
+
return [];
|
|
47
|
+
const next = applied.document;
|
|
46
48
|
return [...names].map((name) => ({ kind: 'field', path: from, name, value: next.fields[name] }));
|
|
47
49
|
}
|
|
48
|
-
function edited(document,
|
|
49
|
-
|
|
50
|
+
function edited(document, changes, definition) {
|
|
51
|
+
const parts = new Map();
|
|
50
52
|
for (const change of changes) {
|
|
51
|
-
if (change.
|
|
52
|
-
|
|
53
|
-
if (change.kind
|
|
54
|
-
|
|
55
|
-
const applied = apply(next, change.kind === 'preamble'
|
|
56
|
-
? { kind: 'preamble', value: change.value, prior: next.preamble }
|
|
57
|
-
: {
|
|
53
|
+
if (change.kind === 'preamble')
|
|
54
|
+
parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
|
|
55
|
+
if (change.kind === 'section')
|
|
56
|
+
parts.set(`section ${change.name}`, {
|
|
58
57
|
kind: 'section',
|
|
59
58
|
name: change.name,
|
|
60
59
|
value: change.value,
|
|
61
|
-
prior:
|
|
60
|
+
prior: document.sections[change.name] ?? '',
|
|
62
61
|
});
|
|
63
|
-
if (applied.outcome === 'applied')
|
|
64
|
-
next = applied.document;
|
|
65
62
|
}
|
|
66
|
-
|
|
63
|
+
if (parts.size === 0)
|
|
64
|
+
return document;
|
|
65
|
+
const applied = apply(document, [...parts.values()], definition);
|
|
66
|
+
return applied.outcome === 'applied' ? applied.document : document;
|
|
67
67
|
}
|
|
68
68
|
function bodies(path, before, after) {
|
|
69
69
|
const changes = [];
|
|
@@ -77,8 +77,8 @@ function bodies(path, before, after) {
|
|
|
77
77
|
}
|
|
78
78
|
return changes;
|
|
79
79
|
}
|
|
80
|
-
function relinks(path, moved, before, moves) {
|
|
81
|
-
|
|
80
|
+
function relinks(path, moved, before, moves, definition) {
|
|
81
|
+
const changes = [];
|
|
82
82
|
for (const link of links(before, path)) {
|
|
83
83
|
const destination = moves.get(link.target) ?? link.target;
|
|
84
84
|
if (destination === link.target && moved === path)
|
|
@@ -86,20 +86,23 @@ function relinks(path, moved, before, moves) {
|
|
|
86
86
|
const to = relative(moved, destination);
|
|
87
87
|
if (to === relative(path, link.target))
|
|
88
88
|
continue;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
89
|
+
changes.push({ kind: 'link', href: link.href, to });
|
|
90
|
+
}
|
|
91
|
+
if (changes.length === 0)
|
|
92
|
+
return { kind: 'changes', changes: [] };
|
|
93
|
+
const applied = apply(before, changes, definition);
|
|
94
|
+
if (applied.outcome === 'refuse') {
|
|
95
|
+
return {
|
|
96
|
+
kind: 'refused',
|
|
97
|
+
refusal: {
|
|
98
|
+
code: applied.refusal.rule,
|
|
99
|
+
retryable: false,
|
|
100
|
+
file: path,
|
|
101
|
+
...(applied.refusal.location === undefined ? {} : { location: applied.refusal.location }),
|
|
102
|
+
},
|
|
103
|
+
};
|
|
102
104
|
}
|
|
105
|
+
const after = applied.document;
|
|
103
106
|
if (after === before)
|
|
104
107
|
return { kind: 'changes', changes: [] };
|
|
105
108
|
if (before.type === undefined) {
|
|
@@ -107,19 +110,23 @@ function relinks(path, moved, before, moves) {
|
|
|
107
110
|
}
|
|
108
111
|
return { kind: 'changes', changes: bodies(path, before, after) };
|
|
109
112
|
}
|
|
110
|
-
function
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
function partKey(change) {
|
|
114
|
+
return change.kind === 'section' ? `section ${change.name}` : change.kind;
|
|
115
|
+
}
|
|
116
|
+
const NAMES_A_SKIP_TESTS = 64;
|
|
117
|
+
const ENCODED = /[%&\\]/u;
|
|
118
|
+
function movedNames(moves) {
|
|
119
|
+
const names = new Set([...moves.keys()].map((path) => path.slice(path.lastIndexOf('/') + 1)));
|
|
120
|
+
return names.size > NAMES_A_SKIP_TESTS ? null : [...names];
|
|
118
121
|
}
|
|
119
|
-
function
|
|
122
|
+
function cannotLink(text, names) {
|
|
123
|
+
return names !== null && !ENCODED.test(text) && !names.some((name) => text.includes(name));
|
|
124
|
+
}
|
|
125
|
+
function* rewrites(changes, visible) {
|
|
120
126
|
const moves = new Map();
|
|
121
127
|
const settled = new Set();
|
|
122
|
-
|
|
128
|
+
const parts = new Map();
|
|
129
|
+
for (const [index, change] of changes.entries()) {
|
|
123
130
|
if (change.kind === 'move')
|
|
124
131
|
moves.set(change.path, change.to);
|
|
125
132
|
if (change.kind === 'remove' ||
|
|
@@ -130,54 +137,146 @@ function rewrites(changes, visible) {
|
|
|
130
137
|
}
|
|
131
138
|
if (change.kind === 'field')
|
|
132
139
|
settled.add(`${change.path} ${change.name}`);
|
|
140
|
+
if (isPart(change)) {
|
|
141
|
+
const held = parts.get(change.path);
|
|
142
|
+
if (held === undefined)
|
|
143
|
+
parts.set(change.path, [index]);
|
|
144
|
+
else
|
|
145
|
+
held.push(index);
|
|
146
|
+
}
|
|
133
147
|
}
|
|
134
148
|
if (moves.size === 0)
|
|
135
149
|
return { kind: 'changes', changes };
|
|
150
|
+
const names = movedNames(moves);
|
|
136
151
|
const merged = [...changes];
|
|
137
152
|
const added = [];
|
|
138
|
-
for (const [path, document] of documentsOf(visible.collection)) {
|
|
153
|
+
for (const [path, document] of yield* documentsOf(visible.collection)) {
|
|
139
154
|
if (settled.has(path))
|
|
140
155
|
continue;
|
|
141
156
|
const moved = moves.get(path) ?? path;
|
|
142
|
-
const definition =
|
|
143
|
-
|
|
144
|
-
|
|
157
|
+
const definition = definitionsAt(visible.collection.composition, path)(document.type);
|
|
158
|
+
const repointing = repointed(document, definition, path, moved, moves, settled);
|
|
159
|
+
added.push(...repointing);
|
|
160
|
+
const own = parts.get(path) ?? [];
|
|
161
|
+
const relinked = moved === path && own.length === 0 && cannotLink(document.content, names)
|
|
162
|
+
? { kind: 'changes', changes: [] }
|
|
163
|
+
: relinks(path, moved, edited(document, own.map((index) => merged[index]), definition), moves, definition);
|
|
145
164
|
if (relinked.kind === 'refused')
|
|
146
165
|
return relinked;
|
|
166
|
+
const keyed = new Map();
|
|
167
|
+
for (const index of own) {
|
|
168
|
+
const key = partKey(merged[index]);
|
|
169
|
+
if (!keyed.has(key))
|
|
170
|
+
keyed.set(key, index);
|
|
171
|
+
}
|
|
147
172
|
for (const change of relinked.changes) {
|
|
148
173
|
if (change.kind === 'replace') {
|
|
149
|
-
|
|
174
|
+
for (const index of own) {
|
|
175
|
+
const kind = merged[index]?.kind;
|
|
176
|
+
if (kind === 'section' || kind === 'preamble')
|
|
177
|
+
merged[index] = null;
|
|
178
|
+
}
|
|
150
179
|
added.push(change);
|
|
151
180
|
continue;
|
|
152
181
|
}
|
|
153
|
-
const index =
|
|
154
|
-
|
|
155
|
-
(change.kind !== 'section' || (item.kind === 'section' && item.name === change.name)));
|
|
156
|
-
if (index === -1)
|
|
182
|
+
const index = keyed.get(partKey(change));
|
|
183
|
+
if (index === undefined)
|
|
157
184
|
added.push(change);
|
|
158
185
|
else
|
|
159
186
|
merged[index] = change;
|
|
160
187
|
}
|
|
188
|
+
const text = visible.collection.text[path].length;
|
|
189
|
+
const rewritten = repointing.length > 0 || relinked.changes.length > 0;
|
|
190
|
+
yield ROW_WEIGHT + text + (rewritten ? text : 0);
|
|
161
191
|
}
|
|
162
|
-
return {
|
|
192
|
+
return {
|
|
193
|
+
kind: 'changes',
|
|
194
|
+
changes: [...added, ...merged.filter((change) => change !== null)],
|
|
195
|
+
};
|
|
163
196
|
}
|
|
164
|
-
function anticipated(changes, preconditions, rewritten, visible) {
|
|
165
|
-
const
|
|
197
|
+
function* anticipated(changes, preconditions, rewritten, visible) {
|
|
198
|
+
const given = new Set(changes);
|
|
199
|
+
const derived = rewritten.filter((change) => !given.has(change));
|
|
166
200
|
return {
|
|
167
201
|
changes: rewritten,
|
|
168
|
-
preconditions: [...preconditionsOf(derived, visible, []), ...preconditions],
|
|
202
|
+
preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...preconditions],
|
|
169
203
|
};
|
|
170
204
|
}
|
|
171
|
-
export function providerRewritten(batch, visible) {
|
|
172
|
-
const rewritten = rewrites(batch.changes, visible);
|
|
205
|
+
export function* providerRewritten(batch, visible) {
|
|
206
|
+
const rewritten = yield* rewrites(batch.changes, visible);
|
|
173
207
|
if (rewritten.kind === 'refused')
|
|
174
208
|
return batch;
|
|
175
209
|
return {
|
|
176
210
|
...batch,
|
|
177
|
-
...anticipated(batch.changes, batch.preconditions, rewritten.changes, visible),
|
|
211
|
+
...(yield* anticipated(batch.changes, batch.preconditions, rewritten.changes, visible)),
|
|
178
212
|
};
|
|
179
213
|
}
|
|
180
|
-
function
|
|
214
|
+
function crossed(document, definition, path, to) {
|
|
215
|
+
const changes = [];
|
|
216
|
+
for (const link of links(document, path)) {
|
|
217
|
+
const written = to(link.target, link.href);
|
|
218
|
+
if (written !== undefined)
|
|
219
|
+
changes.push({ kind: 'link', href: link.href, to: written });
|
|
220
|
+
}
|
|
221
|
+
for (const { pointer, value } of definition === undefined
|
|
222
|
+
? []
|
|
223
|
+
: references(document, definition)) {
|
|
224
|
+
const named = target(path, value);
|
|
225
|
+
const written = named === null ? undefined : to(named, value);
|
|
226
|
+
if (written !== undefined) {
|
|
227
|
+
changes.push({ kind: 'reference', pointer, value: retarget(value, written), prior: value });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return changes;
|
|
231
|
+
}
|
|
232
|
+
function rewritten(path, document, changes, definition) {
|
|
233
|
+
if (changes.length === 0)
|
|
234
|
+
return [];
|
|
235
|
+
const applied = apply(document, changes, definition);
|
|
236
|
+
if (applied.outcome !== 'applied')
|
|
237
|
+
return [];
|
|
238
|
+
const after = applied.document;
|
|
239
|
+
if (document.type === undefined)
|
|
240
|
+
return [{ kind: 'replace', path, content: serialize(after) }];
|
|
241
|
+
const names = new Set(changes.flatMap((change) => change.kind === 'reference' ? [pointerSegments(change.pointer)[0] ?? ''] : []));
|
|
242
|
+
return [
|
|
243
|
+
...[...names].map((name) => ({ kind: 'field', path, name, value: after.fields[name] })),
|
|
244
|
+
...bodies(path, document, after),
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
function roomPathOf(handle, path) {
|
|
248
|
+
return `/rooms/${handle}/${path}`;
|
|
249
|
+
}
|
|
250
|
+
export function* leavingRewritten(batch, visible) {
|
|
251
|
+
const between = batch.between;
|
|
252
|
+
if (between === undefined)
|
|
253
|
+
return batch;
|
|
254
|
+
const moved = new Map(between.moves.map(({ path, to }) => [path, roomPathOf(between.destination.handle, to)]));
|
|
255
|
+
const derived = [];
|
|
256
|
+
for (const [path, document] of yield* documentsOf(visible.collection)) {
|
|
257
|
+
if (moved.has(path))
|
|
258
|
+
continue;
|
|
259
|
+
const definition = definitionsAt(visible.collection.composition, path)(document.type);
|
|
260
|
+
derived.push(...rewritten(path, document, crossed(document, definition, path, (named) => moved.get(named)), definition));
|
|
261
|
+
yield ROW_WEIGHT + visible.collection.text[path].length;
|
|
262
|
+
}
|
|
263
|
+
if (derived.length === 0)
|
|
264
|
+
return batch;
|
|
265
|
+
return {
|
|
266
|
+
...batch,
|
|
267
|
+
changes: [...derived, ...batch.changes],
|
|
268
|
+
preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...batch.preconditions],
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
export function rewriting(batch, locator) {
|
|
272
|
+
return leaving(batch, locator) || batch.changes.some((change) => change.kind === 'move');
|
|
273
|
+
}
|
|
274
|
+
export function* effectiveIn(batch, locator, visible) {
|
|
275
|
+
return leaving(batch, locator)
|
|
276
|
+
? yield* leavingRewritten(batch, visible)
|
|
277
|
+
: yield* providerRewritten(batch, visible);
|
|
278
|
+
}
|
|
279
|
+
function* incoming(collection, removed, candidates) {
|
|
181
280
|
const found = new Map();
|
|
182
281
|
const note = (target, file) => {
|
|
183
282
|
if (!removed.has(target))
|
|
@@ -186,8 +285,21 @@ function incoming(collection, removed) {
|
|
|
186
285
|
referrers.add(file);
|
|
187
286
|
found.set(target, referrers);
|
|
188
287
|
};
|
|
189
|
-
|
|
190
|
-
const
|
|
288
|
+
const read = function* (paths) {
|
|
289
|
+
const seen = new Set();
|
|
290
|
+
for (const path of paths) {
|
|
291
|
+
if (seen.has(path))
|
|
292
|
+
continue;
|
|
293
|
+
seen.add(path);
|
|
294
|
+
const document = collection.document(path);
|
|
295
|
+
if (document !== undefined)
|
|
296
|
+
yield [path, document];
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
for (const [path, document] of candidates === null
|
|
300
|
+
? yield* documentsOf(collection)
|
|
301
|
+
: read(candidates)) {
|
|
302
|
+
const definition = definitionsAt(collection.composition, path)(document.type);
|
|
191
303
|
for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
|
|
192
304
|
if (to.includes(ATTACHMENT))
|
|
193
305
|
continue;
|
|
@@ -195,8 +307,9 @@ function incoming(collection, removed) {
|
|
|
195
307
|
if (named !== null)
|
|
196
308
|
note(named, path);
|
|
197
309
|
}
|
|
310
|
+
yield ROW_WEIGHT + collection.text[path].length;
|
|
198
311
|
}
|
|
199
|
-
const [first] =
|
|
312
|
+
const [first] = yield* sorting(found.keys());
|
|
200
313
|
if (first === undefined)
|
|
201
314
|
return null;
|
|
202
315
|
const every = new Set();
|
|
@@ -209,33 +322,43 @@ function incoming(collection, removed) {
|
|
|
209
322
|
code: 'referenced-missing-file',
|
|
210
323
|
retryable: false,
|
|
211
324
|
file: first,
|
|
212
|
-
referrers:
|
|
325
|
+
referrers: yield* sorting(every),
|
|
213
326
|
},
|
|
214
327
|
};
|
|
215
328
|
}
|
|
216
329
|
function affected(change) {
|
|
217
330
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
218
331
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
332
|
+
const WRITE = ['write'];
|
|
333
|
+
const MOVE_OUT = ['write', 'erase'];
|
|
334
|
+
function guard(changes, input, removing) {
|
|
335
|
+
const overridden = new Map();
|
|
336
|
+
const existing = {
|
|
337
|
+
has: (path) => overridden.get(path) ??
|
|
338
|
+
(Object.hasOwn(input.visible.documents, path) || Object.hasOwn(input.visible.binaries, path)),
|
|
339
|
+
add: (path) => void overridden.set(path, true),
|
|
340
|
+
delete: (path) => void overridden.set(path, false),
|
|
341
|
+
};
|
|
342
|
+
const occupied = (path) => input.folders.has(path) ? path : (ancestors(path).find((one) => existing.has(one)) ?? null);
|
|
224
343
|
for (const [index, change] of changes.entries()) {
|
|
225
344
|
for (const path of affected(change)) {
|
|
226
345
|
const normalized = normalize(path);
|
|
227
346
|
if (normalized === null || normalized === '')
|
|
228
347
|
return refuse('room-root', path);
|
|
229
|
-
if (covered(input.providerOwnedPaths, normalized)
|
|
348
|
+
if (covered(input.providerOwnedPaths, normalized) ||
|
|
349
|
+
(input.authoredPaths !== undefined && !authoredAt(input.authoredPaths, normalized))) {
|
|
230
350
|
return refuse('provider-owned-path', normalized);
|
|
231
351
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
352
|
+
if (!input.pathRule(normalized))
|
|
353
|
+
return refuse('invalid-path', normalized);
|
|
354
|
+
for (const operation of change.kind === 'remove' ? removing : WRITE)
|
|
355
|
+
if (!input.local && !covered(input.operations[operation], normalized))
|
|
356
|
+
return refuse(`${operation}-grant`, normalized);
|
|
235
357
|
}
|
|
236
|
-
if (change.kind === 'create') {
|
|
237
|
-
|
|
238
|
-
|
|
358
|
+
if (change.kind === 'create' || change.kind === 'arrival') {
|
|
359
|
+
const taken = existing.has(change.path) ? change.path : occupied(change.path);
|
|
360
|
+
if (taken !== null)
|
|
361
|
+
return refuse('path-collision', taken);
|
|
239
362
|
existing.add(change.path);
|
|
240
363
|
continue;
|
|
241
364
|
}
|
|
@@ -245,6 +368,9 @@ function guard(changes, input) {
|
|
|
245
368
|
input.bases.some((base) => base.kind === 'absent' && base.path === change.path))) {
|
|
246
369
|
return refuse('path-collision', change.path);
|
|
247
370
|
}
|
|
371
|
+
const taken = occupied(change.path);
|
|
372
|
+
if (taken !== null)
|
|
373
|
+
return refuse('path-collision', taken);
|
|
248
374
|
if (!input.local &&
|
|
249
375
|
existing.has(change.path) &&
|
|
250
376
|
input.visible.revisions[departure(changes, index)] === undefined) {
|
|
@@ -256,8 +382,9 @@ function guard(changes, input) {
|
|
|
256
382
|
if (change.kind === 'move') {
|
|
257
383
|
if (!existing.has(change.path))
|
|
258
384
|
return refuse('path-absent', change.path);
|
|
259
|
-
|
|
260
|
-
|
|
385
|
+
const taken = existing.has(change.to) ? change.to : occupied(change.to);
|
|
386
|
+
if (taken !== null)
|
|
387
|
+
return refuse('path-collision', taken);
|
|
261
388
|
if (unrevised(input, change.path)) {
|
|
262
389
|
return refuse('unknown-revision', change.path);
|
|
263
390
|
}
|
|
@@ -275,7 +402,7 @@ function guard(changes, input) {
|
|
|
275
402
|
continue;
|
|
276
403
|
}
|
|
277
404
|
if (change.kind === 'replace') {
|
|
278
|
-
if (
|
|
405
|
+
if (unrevised(input, change.path)) {
|
|
279
406
|
return refuse('unknown-revision', change.path);
|
|
280
407
|
}
|
|
281
408
|
continue;
|
|
@@ -289,6 +416,22 @@ function guard(changes, input) {
|
|
|
289
416
|
function unrevised(input, path) {
|
|
290
417
|
return (!input.local && input.queued?.has(path) !== true && input.visible.revisions[path] === undefined);
|
|
291
418
|
}
|
|
419
|
+
export function queuedDocuments(queue) {
|
|
420
|
+
const paths = new Set();
|
|
421
|
+
for (const batch of queue) {
|
|
422
|
+
for (const change of batch.changes) {
|
|
423
|
+
if (change.kind === 'binary' || (change.kind === 'arrival' && !('content' in change)))
|
|
424
|
+
continue;
|
|
425
|
+
if (change.kind === 'remove')
|
|
426
|
+
paths.delete(change.path);
|
|
427
|
+
else if (change.kind !== 'move')
|
|
428
|
+
paths.add(change.path);
|
|
429
|
+
else if (paths.delete(change.path))
|
|
430
|
+
paths.add(change.to);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return paths;
|
|
434
|
+
}
|
|
292
435
|
function identity(precondition) {
|
|
293
436
|
const part = precondition.kind === 'field' || precondition.kind === 'section' ? precondition.name : '';
|
|
294
437
|
return `${precondition.kind} ${precondition.path} ${part}`;
|
|
@@ -306,7 +449,7 @@ function sameTarget(precondition, change) {
|
|
|
306
449
|
return ((precondition.kind !== 'field' && precondition.kind !== 'section') ||
|
|
307
450
|
((change.kind === 'field' || change.kind === 'section') && precondition.name === change.name));
|
|
308
451
|
}
|
|
309
|
-
function preconditionsOf(changes, visible, bases) {
|
|
452
|
+
function* preconditionsOf(changes, visible, bases) {
|
|
310
453
|
const preconditions = [];
|
|
311
454
|
const seen = new Set();
|
|
312
455
|
const add = (precondition) => {
|
|
@@ -317,6 +460,7 @@ function preconditionsOf(changes, visible, bases) {
|
|
|
317
460
|
preconditions.push(precondition);
|
|
318
461
|
};
|
|
319
462
|
for (const change of changes) {
|
|
463
|
+
yield ROW_WEIGHT;
|
|
320
464
|
if (change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble') {
|
|
321
465
|
const based = bases.find((base) => sameTarget(base, change));
|
|
322
466
|
if (based !== undefined) {
|
|
@@ -364,10 +508,13 @@ function preconditionsOf(changes, visible, bases) {
|
|
|
364
508
|
function depth(path) {
|
|
365
509
|
return path.split('/').length;
|
|
366
510
|
}
|
|
367
|
-
function
|
|
511
|
+
function* exceeding(limits, changes, effective, resulting) {
|
|
368
512
|
const written = effective.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
369
513
|
const created = changes
|
|
370
|
-
.filter((change) => change.kind === 'create' ||
|
|
514
|
+
.filter((change) => change.kind === 'create' ||
|
|
515
|
+
change.kind === 'move' ||
|
|
516
|
+
change.kind === 'binary' ||
|
|
517
|
+
change.kind === 'arrival')
|
|
371
518
|
.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
372
519
|
for (const limit of limits) {
|
|
373
520
|
const value = limit.value;
|
|
@@ -381,9 +528,13 @@ function exceeded(limits, changes, effective, resulting) {
|
|
|
381
528
|
return refuse('provider-limit', undefined, limit.name);
|
|
382
529
|
}
|
|
383
530
|
if (limit.name === 'document-size') {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
531
|
+
for (const path of written) {
|
|
532
|
+
const text = resulting.documents[path] ?? '';
|
|
533
|
+
if (new TextEncoder().encode(text).byteLength > value) {
|
|
534
|
+
return refuse('provider-limit', path, limit.name);
|
|
535
|
+
}
|
|
536
|
+
yield ROW_WEIGHT + text.length;
|
|
537
|
+
}
|
|
387
538
|
}
|
|
388
539
|
if (limit.name === 'path-length') {
|
|
389
540
|
const long = created.find((path) => path.length > value);
|
|
@@ -401,19 +552,16 @@ function exceeded(limits, changes, effective, resulting) {
|
|
|
401
552
|
function shellRelative(text, path, handle, definition) {
|
|
402
553
|
if (!text.includes(`/rooms/${handle}/`))
|
|
403
554
|
return null;
|
|
404
|
-
const parsed = parse(text, path);
|
|
555
|
+
const parsed = parse(text, path, definition);
|
|
405
556
|
if (!parsed.ok)
|
|
406
557
|
return null;
|
|
407
558
|
const result = relativize(parsed.document, path, handle, definition(parsed.document.type));
|
|
408
559
|
return result.outcome === 'applied' && result.document.content !== text ? result.document : null;
|
|
409
560
|
}
|
|
410
561
|
function relativeField(path, name, value, handle, definition) {
|
|
411
|
-
|
|
412
|
-
if (definition === undefined ||
|
|
413
|
-
!opened.ok ||
|
|
414
|
-
!JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
562
|
+
if (definition === undefined || !JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
415
563
|
return value;
|
|
416
|
-
const held = apply(
|
|
564
|
+
const held = apply(create(definition.name, definition), [{ kind: 'field', name, value, prior: undefined }], definition);
|
|
417
565
|
if (held.outcome !== 'applied')
|
|
418
566
|
return value;
|
|
419
567
|
const result = relativize(held.document, path, handle, definition);
|
|
@@ -421,7 +569,7 @@ function relativeField(path, name, value, handle, definition) {
|
|
|
421
569
|
}
|
|
422
570
|
function relativeChanges(input) {
|
|
423
571
|
const { handle, composition, visible } = input;
|
|
424
|
-
const typed = (path) => (
|
|
572
|
+
const typed = (path) => definitionsAt(composition, path);
|
|
425
573
|
const body = (path, text) => shellRelative(text, path, handle, () => undefined)?.content ?? text;
|
|
426
574
|
return input.changes.map((change) => {
|
|
427
575
|
const definition = typed(change.path);
|
|
@@ -449,15 +597,6 @@ function relativeChanges(input) {
|
|
|
449
597
|
return { ...change, fields, sections };
|
|
450
598
|
});
|
|
451
599
|
}
|
|
452
|
-
function unmodeledType(collection, composition) {
|
|
453
|
-
for (const [path, document] of documentsOf(collection)) {
|
|
454
|
-
if (document.type !== undefined &&
|
|
455
|
-
document.type !== 'any' &&
|
|
456
|
-
governing(composition, path).status === 'unavailable')
|
|
457
|
-
return true;
|
|
458
|
-
}
|
|
459
|
-
return false;
|
|
460
|
-
}
|
|
461
600
|
function unqueueable(file) {
|
|
462
601
|
return { kind: 'refused', refusal: { code: 'unknown-revision', retryable: false, file } };
|
|
463
602
|
}
|
|
@@ -467,29 +606,45 @@ function isPart(change) {
|
|
|
467
606
|
function touched(change) {
|
|
468
607
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
469
608
|
}
|
|
470
|
-
function
|
|
471
|
-
const
|
|
609
|
+
function partsApplied(content, path, changes, composition) {
|
|
610
|
+
const definitions = definitionsAt(composition, path);
|
|
611
|
+
const parsed = parse(content, path, definitions);
|
|
472
612
|
if (!parsed.ok)
|
|
473
613
|
return null;
|
|
474
614
|
const document = parsed.document;
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
615
|
+
const parts = new Map();
|
|
616
|
+
for (const change of changes) {
|
|
617
|
+
if (change.kind === 'field')
|
|
618
|
+
parts.set(`field ${change.name}`, {
|
|
619
|
+
kind: 'field',
|
|
620
|
+
name: change.name,
|
|
621
|
+
value: change.value,
|
|
622
|
+
prior: document.fields[change.name],
|
|
623
|
+
});
|
|
624
|
+
else if (change.kind === 'section')
|
|
625
|
+
parts.set(`section ${change.name}`, {
|
|
484
626
|
kind: 'section',
|
|
485
627
|
name: change.name,
|
|
486
628
|
value: change.value,
|
|
487
|
-
prior: document.sections[change.name],
|
|
488
|
-
}
|
|
489
|
-
|
|
629
|
+
prior: document.sections[change.name] ?? '',
|
|
630
|
+
});
|
|
631
|
+
else
|
|
632
|
+
parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
|
|
633
|
+
}
|
|
634
|
+
const applied = apply(document, [...parts.values()], definitions(document.type));
|
|
490
635
|
return applied.outcome === 'applied' ? serialize(applied.document) : null;
|
|
491
636
|
}
|
|
492
|
-
function
|
|
637
|
+
function settledParts(changes, deferred, composition) {
|
|
638
|
+
for (const [held, parts] of deferred) {
|
|
639
|
+
const content = partsApplied(held.content, held.path, parts, composition);
|
|
640
|
+
if (content === null)
|
|
641
|
+
return held.path;
|
|
642
|
+
changes[changes.indexOf(held)] = { ...held, content };
|
|
643
|
+
}
|
|
644
|
+
deferred.clear();
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
function folded(changes, change, appending, deferred) {
|
|
493
648
|
const held = changes.find((item) => item.path === change.path && (item.kind === 'create' || item.kind === 'replace'));
|
|
494
649
|
const whole = held === undefined ? -1 : changes.indexOf(held);
|
|
495
650
|
const occupied = changes.some((item) => !isPart(item) && item !== held && touched(item).includes(change.path));
|
|
@@ -510,10 +665,11 @@ function folded(changes, change, appending) {
|
|
|
510
665
|
return 'appended';
|
|
511
666
|
}
|
|
512
667
|
if ('content' in held) {
|
|
513
|
-
const
|
|
514
|
-
if (
|
|
515
|
-
|
|
516
|
-
|
|
668
|
+
const parts = deferred.get(held);
|
|
669
|
+
if (parts === undefined)
|
|
670
|
+
deferred.set(held, [change]);
|
|
671
|
+
else
|
|
672
|
+
parts.push(change);
|
|
517
673
|
return 'folded';
|
|
518
674
|
}
|
|
519
675
|
if (change.kind === 'preamble')
|
|
@@ -544,17 +700,23 @@ function folded(changes, change, appending) {
|
|
|
544
700
|
changes.push(change);
|
|
545
701
|
return 'appended';
|
|
546
702
|
}
|
|
547
|
-
function relinked(batch, move, before, after, appending) {
|
|
548
|
-
const derived = rewrites([move], after);
|
|
703
|
+
function* relinked(batch, move, before, after, appending) {
|
|
704
|
+
const derived = yield* rewrites([move], after);
|
|
549
705
|
if (derived.kind === 'refused')
|
|
550
706
|
return derived;
|
|
551
707
|
const changes = [...batch.changes];
|
|
552
708
|
const appended = [];
|
|
553
709
|
let changed = false;
|
|
710
|
+
const deferred = new Map();
|
|
554
711
|
for (const change of derived.changes) {
|
|
555
712
|
if (change === move)
|
|
556
713
|
continue;
|
|
557
|
-
const
|
|
714
|
+
const unsettled = isPart(change)
|
|
715
|
+
? null
|
|
716
|
+
: settledParts(changes, deferred, after.collection.composition);
|
|
717
|
+
if (unsettled !== null)
|
|
718
|
+
return unqueueable(unsettled);
|
|
719
|
+
const outcome = folded(changes, change, appending, deferred);
|
|
558
720
|
if (outcome === 'refused')
|
|
559
721
|
return unqueueable(change.path);
|
|
560
722
|
if (outcome === 'appended')
|
|
@@ -562,9 +724,13 @@ function relinked(batch, move, before, after, appending) {
|
|
|
562
724
|
if (outcome !== 'skipped')
|
|
563
725
|
changed = true;
|
|
564
726
|
}
|
|
727
|
+
const unsettled = settledParts(changes, deferred, after.collection.composition);
|
|
728
|
+
if (unsettled !== null)
|
|
729
|
+
return unqueueable(unsettled);
|
|
565
730
|
if (!changed)
|
|
566
731
|
return { kind: 'relinked', batch };
|
|
567
732
|
const held = new Set(batch.preconditions.map(identity));
|
|
733
|
+
const added = yield* preconditionsOf(appended, before, []);
|
|
568
734
|
return {
|
|
569
735
|
kind: 'relinked',
|
|
570
736
|
batch: immutable({
|
|
@@ -573,7 +739,7 @@ function relinked(batch, move, before, after, appending) {
|
|
|
573
739
|
changes,
|
|
574
740
|
preconditions: [
|
|
575
741
|
...batch.preconditions,
|
|
576
|
-
...
|
|
742
|
+
...added.filter((precondition) => !held.has(identity(precondition))),
|
|
577
743
|
],
|
|
578
744
|
}),
|
|
579
745
|
};
|
|
@@ -619,7 +785,7 @@ function withdrawnFile(batch, change, at) {
|
|
|
619
785
|
];
|
|
620
786
|
return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
|
|
621
787
|
}
|
|
622
|
-
export function
|
|
788
|
+
export function* requeuing(queue, changes, visible, changeable, state) {
|
|
623
789
|
const next = [...queue];
|
|
624
790
|
const origins = new Map();
|
|
625
791
|
const withdrawn = new Map();
|
|
@@ -642,19 +808,19 @@ export function requeued(queue, changes, visible, changeable, state) {
|
|
|
642
808
|
return unqueueable(change.path);
|
|
643
809
|
const following = next.slice(index);
|
|
644
810
|
const rebuilt = [];
|
|
645
|
-
let before = change.kind === 'move' ? state(next.slice(0, index)) : visible;
|
|
811
|
+
let before = change.kind === 'move' ? yield* state(next.slice(0, index)) : visible;
|
|
646
812
|
for (const [offset, batch] of following.entries()) {
|
|
647
813
|
let current = batch;
|
|
648
814
|
if (change.kind === 'move') {
|
|
649
|
-
const after = state(next.slice(0, index + offset + 1));
|
|
815
|
+
const after = yield* state(next.slice(0, index + offset + 1));
|
|
650
816
|
const appending = offset === 0 && created(batch, queuedFile(batch, change.path, held.localId));
|
|
651
|
-
const relinking = relinked(batch, change, before, after, appending);
|
|
817
|
+
const relinking = yield* relinked(batch, change, before, after, appending);
|
|
652
818
|
if (relinking.kind === 'refused')
|
|
653
819
|
return relinking;
|
|
654
820
|
current = relinking.batch;
|
|
655
821
|
before = after;
|
|
656
822
|
}
|
|
657
|
-
if (current !== batch && !open(batch))
|
|
823
|
+
if (current !== batch && (!open(batch) || batch.between !== undefined))
|
|
658
824
|
return unqueueable(change.path);
|
|
659
825
|
rebuilt.push(offset === 0
|
|
660
826
|
? withdrawnFile(current, change, queuedFile(current, change.path, held.localId))
|
|
@@ -689,16 +855,23 @@ function covering(precondition) {
|
|
|
689
855
|
? identity(precondition)
|
|
690
856
|
: `path ${precondition.path}`;
|
|
691
857
|
}
|
|
692
|
-
export function joined(queue, owner, extra) {
|
|
858
|
+
export function joined(queue, owner, extra, composition) {
|
|
693
859
|
const later = new Set(queue.slice(queue.indexOf(owner) + 1).flatMap((batch) => batch.changes.flatMap(touched)));
|
|
694
860
|
const clash = extra.changes.flatMap(touched).find((path) => later.has(path));
|
|
695
861
|
if (clash !== undefined)
|
|
696
862
|
return unqueueable(clash);
|
|
697
863
|
const changes = [...owner.changes];
|
|
864
|
+
const deferred = new Map();
|
|
698
865
|
for (const change of extra.changes) {
|
|
699
|
-
|
|
866
|
+
const unsettled = isPart(change) ? null : settledParts(changes, deferred, composition);
|
|
867
|
+
if (unsettled !== null)
|
|
868
|
+
return unqueueable(unsettled);
|
|
869
|
+
if (folded(changes, change, true, deferred) === 'refused')
|
|
700
870
|
return unqueueable(change.path);
|
|
701
871
|
}
|
|
872
|
+
const unsettled = settledParts(changes, deferred, composition);
|
|
873
|
+
if (unsettled !== null)
|
|
874
|
+
return unqueueable(unsettled);
|
|
702
875
|
const held = new Set(owner.preconditions.map(covering));
|
|
703
876
|
const written = new Set(owner.changes.flatMap((change) => change.kind === 'create' || change.kind === 'replace' ? [change.path] : []));
|
|
704
877
|
const preconditions = [
|
|
@@ -717,50 +890,66 @@ export function joined(queue, owner, extra) {
|
|
|
717
890
|
});
|
|
718
891
|
return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
|
|
719
892
|
}
|
|
720
|
-
export function
|
|
893
|
+
export function* limiting(queue, batch, limits, state) {
|
|
721
894
|
const at = queue.indexOf(batch);
|
|
722
|
-
const
|
|
723
|
-
|
|
895
|
+
const rewritten = yield* providerRewritten(batch, yield* state(queue.slice(0, at)));
|
|
896
|
+
const limited = yield* exceeding(limits, batch.changes, rewritten.changes, yield* state(queue.slice(0, at + 1)));
|
|
897
|
+
return limited?.refusal ?? null;
|
|
724
898
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
const
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
899
|
+
function typedText(content) {
|
|
900
|
+
if (content === undefined)
|
|
901
|
+
return false;
|
|
902
|
+
const parsed = parse(content);
|
|
903
|
+
return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
|
|
904
|
+
}
|
|
905
|
+
function touchedLibrary(resulting, touched, composition) {
|
|
906
|
+
const entries = touched
|
|
907
|
+
.flatMap((path) => {
|
|
908
|
+
const content = resulting.documents[path];
|
|
909
|
+
const binary = resulting.binaries[path] !== undefined;
|
|
910
|
+
return content === undefined && !binary ? [] : [{ path, content: content ?? null }];
|
|
911
|
+
})
|
|
912
|
+
.sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0));
|
|
913
|
+
const contents = new Map(entries.map(({ path, content }) => [path, content]));
|
|
914
|
+
const binaries = new Set(Array.from(entriesIn(resulting.binaries), ([path]) => path));
|
|
915
|
+
return validatedCollection(() => Promise.resolve({ entries, next: null }), async (paths) => {
|
|
916
|
+
const known = new Map();
|
|
917
|
+
const pace = paced();
|
|
918
|
+
for (const path of paths) {
|
|
919
|
+
const content = contents.get(path);
|
|
920
|
+
if (binaries.has(path))
|
|
921
|
+
known.set(path, { type: undefined, binary: true });
|
|
922
|
+
else if (content !== undefined && content !== null) {
|
|
923
|
+
const read = parse(content, path, definitionsAt(composition, path));
|
|
924
|
+
if (read.ok)
|
|
925
|
+
known.set(path, { type: read.document.type ?? 'any', binary: false });
|
|
926
|
+
await pace(content.length + ROW_WEIGHT);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
return known;
|
|
930
|
+
}, composition);
|
|
931
|
+
}
|
|
932
|
+
async function judging(visible, resulting, composition, validate, removed, touched) {
|
|
744
933
|
if (removed.size > 0) {
|
|
745
|
-
const referenced = incoming(collectionOf(resulting.documents,
|
|
934
|
+
const referenced = await stepped(incoming(collectionOf(resulting.documents, composition), removed, null));
|
|
746
935
|
if (referenced !== null)
|
|
747
936
|
return referenced;
|
|
748
937
|
}
|
|
749
|
-
const
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
938
|
+
const typedPaths = [];
|
|
939
|
+
const pace = paced();
|
|
940
|
+
for (const path of touched) {
|
|
941
|
+
const before = visible.documents[path];
|
|
942
|
+
const after = resulting.documents[path];
|
|
943
|
+
if (typedText(before) || typedText(after))
|
|
944
|
+
typedPaths.push(path);
|
|
945
|
+
await pace((before?.length ?? 0) + (after?.length ?? 0) + ROW_WEIGHT);
|
|
946
|
+
}
|
|
947
|
+
const withoutModel = typedPaths.find((path) => governing(composition, path).status === 'unavailable');
|
|
758
948
|
if (withoutModel !== undefined)
|
|
759
949
|
return refuse('model-unavailable', withoutModel);
|
|
760
|
-
const
|
|
761
|
-
? resulting
|
|
762
|
-
:
|
|
763
|
-
const verdict = validateCollection(validationText, Object.keys(resulting.binaries), input.composition);
|
|
950
|
+
const verdict = typedPaths.length > 0
|
|
951
|
+
? await validate(resulting, touched)
|
|
952
|
+
: await touchedLibrary(resulting, touched, composition);
|
|
764
953
|
if (verdict.verdict === 'refuse') {
|
|
765
954
|
return {
|
|
766
955
|
kind: 'refused',
|
|
@@ -773,12 +962,93 @@ export function buildBatch(input) {
|
|
|
773
962
|
},
|
|
774
963
|
};
|
|
775
964
|
}
|
|
776
|
-
|
|
965
|
+
return {
|
|
966
|
+
kind: 'judged',
|
|
967
|
+
validated: typedPaths
|
|
968
|
+
.map((path) => governing(composition, path))
|
|
969
|
+
.find((model) => model.status !== 'unavailable'),
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
function placed(reference) {
|
|
973
|
+
const key = roomPathOf(reference.handle, reference.path);
|
|
974
|
+
if (reference.kind === 'link')
|
|
975
|
+
return `link ${key}`;
|
|
976
|
+
if (reference.kind === 'item')
|
|
977
|
+
return `section ${reference.section} ${key}`;
|
|
978
|
+
const pointer = reference.pointer
|
|
979
|
+
.split('/')
|
|
980
|
+
.map((segment) => (ARRAY_INDEX.test(segment) ? '-' : segment))
|
|
981
|
+
.join('/');
|
|
982
|
+
return `field ${pointer} ${key}`;
|
|
983
|
+
}
|
|
984
|
+
function roomReferencesIn(path, content, composition) {
|
|
985
|
+
if (content === undefined)
|
|
986
|
+
return [];
|
|
987
|
+
const definitions = definitionsAt(composition, path);
|
|
988
|
+
const parsed = parse(content, path, definitions);
|
|
989
|
+
if (!parsed.ok)
|
|
990
|
+
return [];
|
|
991
|
+
return roomReferences(parsed.document, definitions(parsed.document.type));
|
|
992
|
+
}
|
|
993
|
+
function* admittedRooms(changes, visible, resulting, composition) {
|
|
994
|
+
const from = new Map();
|
|
995
|
+
for (const [index, change] of changes.entries()) {
|
|
996
|
+
if (change.kind === 'move')
|
|
997
|
+
from.set(change.to, departure(changes, index));
|
|
998
|
+
}
|
|
999
|
+
for (const path of [...new Set(changes.flatMap(affected))].sort()) {
|
|
1000
|
+
const now = resulting.documents[path];
|
|
1001
|
+
if (now === undefined)
|
|
1002
|
+
continue;
|
|
1003
|
+
const held = new Set(roomReferencesIn(path, visible.documents[from.get(path) ?? path], composition).map(placed));
|
|
1004
|
+
for (const reference of roomReferencesIn(path, now, composition)) {
|
|
1005
|
+
if (reference.kind === 'link' || reference.crossRoom === true)
|
|
1006
|
+
continue;
|
|
1007
|
+
if (held.has(placed(reference)))
|
|
1008
|
+
continue;
|
|
1009
|
+
return refuse('unresolved-reference', path, reference.kind === 'field' ? reference.pointer : reference.section);
|
|
1010
|
+
}
|
|
1011
|
+
yield ROW_WEIGHT + now.length;
|
|
1012
|
+
}
|
|
1013
|
+
return null;
|
|
1014
|
+
}
|
|
1015
|
+
function validationOf(validated) {
|
|
1016
|
+
return validated === undefined
|
|
1017
|
+
? {}
|
|
1018
|
+
: {
|
|
1019
|
+
validation: {
|
|
1020
|
+
identity: validated.snapshot.identity,
|
|
1021
|
+
version: validated.snapshot.version,
|
|
1022
|
+
source: validated.status,
|
|
1023
|
+
},
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
async function batching(input) {
|
|
1027
|
+
const changes = relativeChanges(input);
|
|
1028
|
+
const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
|
|
1029
|
+
if (structural !== undefined && input.unmodeled) {
|
|
1030
|
+
return refuse('model-unavailable', structural.path);
|
|
1031
|
+
}
|
|
1032
|
+
if (changes.length === 0)
|
|
1033
|
+
return refuse('no-change');
|
|
1034
|
+
const rewritten = await stepped(rewrites(changes, input.visible));
|
|
1035
|
+
if (rewritten.kind === 'refused')
|
|
1036
|
+
return rewritten;
|
|
1037
|
+
const refusal = guard(changes, input, WRITE);
|
|
1038
|
+
if (refusal !== null)
|
|
1039
|
+
return refusal;
|
|
1040
|
+
const preconditions = await stepped(preconditionsOf(changes, input.visible, input.bases));
|
|
1041
|
+
const predicted = await stepped(anticipated(changes, preconditions, rewritten.changes, input.visible));
|
|
1042
|
+
const resulting = await stepped(overlaying(input.visible, [{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted }], input.composition));
|
|
1043
|
+
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))]);
|
|
1044
|
+
if (judged.kind === 'refused')
|
|
1045
|
+
return judged;
|
|
1046
|
+
const crossing = await stepped(admittedRooms(changes, input.visible, resulting, input.composition));
|
|
1047
|
+
if (crossing !== null)
|
|
1048
|
+
return crossing;
|
|
1049
|
+
const limited = await stepped(exceeding(input.providerLimits, changes, predicted.changes, resulting));
|
|
777
1050
|
if (limited !== null)
|
|
778
1051
|
return limited;
|
|
779
|
-
const validated = typedPaths
|
|
780
|
-
.map((path) => governing(input.composition, path))
|
|
781
|
-
.find((model) => model.status !== 'unavailable');
|
|
782
1052
|
return {
|
|
783
1053
|
kind: 'batch',
|
|
784
1054
|
batch: immutable({
|
|
@@ -787,15 +1057,125 @@ export function buildBatch(input) {
|
|
|
787
1057
|
changes,
|
|
788
1058
|
preconditions,
|
|
789
1059
|
...(input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy }),
|
|
790
|
-
...(validated
|
|
791
|
-
? {}
|
|
792
|
-
: {
|
|
793
|
-
validation: {
|
|
794
|
-
identity: validated.snapshot.identity,
|
|
795
|
-
version: validated.snapshot.version,
|
|
796
|
-
source: validated.status,
|
|
797
|
-
},
|
|
798
|
-
}),
|
|
1060
|
+
...validationOf(judged.validated),
|
|
799
1061
|
}),
|
|
800
1062
|
};
|
|
801
1063
|
}
|
|
1064
|
+
export const MOVE_DOCUMENTS = 'move-documents';
|
|
1065
|
+
function heldAt(visible, path) {
|
|
1066
|
+
return visible.documents[path] !== undefined || visible.binaries[path] !== undefined;
|
|
1067
|
+
}
|
|
1068
|
+
function arrivingText(path, to, source, moved) {
|
|
1069
|
+
const content = source.visible.documents[path];
|
|
1070
|
+
const document = source.visible.collection.document(path);
|
|
1071
|
+
if (document === undefined)
|
|
1072
|
+
return content;
|
|
1073
|
+
const definition = definitionsAt(source.composition, path)(document.type);
|
|
1074
|
+
const changes = crossed(document, definition, path, (named, href) => {
|
|
1075
|
+
const at = moved.get(named);
|
|
1076
|
+
if (at !== undefined)
|
|
1077
|
+
return target(to, href) === at ? undefined : relative(to, at);
|
|
1078
|
+
return heldAt(source.visible, named) ? roomPathOf(source.handle, named) : undefined;
|
|
1079
|
+
});
|
|
1080
|
+
if (changes.length === 0)
|
|
1081
|
+
return content;
|
|
1082
|
+
const applied = apply(document, changes, definition);
|
|
1083
|
+
return applied.outcome === 'applied' ? serialize(applied.document) : content;
|
|
1084
|
+
}
|
|
1085
|
+
function arrivalOf({ path, to }, source, moved) {
|
|
1086
|
+
const { documents, revisions, fileIds, created, changes, binaries } = source.visible;
|
|
1087
|
+
const kept = {
|
|
1088
|
+
...(fileIds[path] === undefined ? {} : { fileId: fileIds[path] }),
|
|
1089
|
+
...(created[path] === undefined ? {} : { created: created[path] }),
|
|
1090
|
+
...(changes[path] === undefined ? {} : { change: changes[path] }),
|
|
1091
|
+
};
|
|
1092
|
+
const heads = {
|
|
1093
|
+
...(revisions[path] === undefined ? {} : { revision: revisions[path] }),
|
|
1094
|
+
...kept,
|
|
1095
|
+
};
|
|
1096
|
+
const binary = binaries[path];
|
|
1097
|
+
if (binary?.state === 'accepted') {
|
|
1098
|
+
const { byteLength, mediaType, sha256 } = binary;
|
|
1099
|
+
return {
|
|
1100
|
+
kind: 'arrival',
|
|
1101
|
+
path: to,
|
|
1102
|
+
binary: { byteLength, mediaType, sha256 },
|
|
1103
|
+
...heads,
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
const content = arrivingText(path, to, source, moved);
|
|
1107
|
+
return {
|
|
1108
|
+
kind: 'arrival',
|
|
1109
|
+
path: to,
|
|
1110
|
+
content,
|
|
1111
|
+
...(content === documents[path] ? heads : kept),
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
async function moving(input) {
|
|
1115
|
+
const { source, destination, moves, pathRule } = input;
|
|
1116
|
+
const removals = moves.map(({ path }) => ({ kind: 'remove', path }));
|
|
1117
|
+
const leaves = guard(removals, { ...source, local: input.local, bases: [], pathRule }, MOVE_OUT);
|
|
1118
|
+
if (leaves !== null)
|
|
1119
|
+
return leaves;
|
|
1120
|
+
const uploading = moves.find(({ path }) => source.visible.binaries[path]?.state === 'pending');
|
|
1121
|
+
if (uploading !== undefined)
|
|
1122
|
+
return refuse('unknown-revision', uploading.path);
|
|
1123
|
+
const moved = new Map(moves.map(({ path, to }) => [path, to]));
|
|
1124
|
+
const arrivals = moves.map((move) => arrivalOf(move, source, moved));
|
|
1125
|
+
const arrives = guard(arrivals, { ...destination, local: input.local, bases: [], pathRule }, WRITE);
|
|
1126
|
+
if (arrives !== null)
|
|
1127
|
+
return arrives;
|
|
1128
|
+
const bound = input.providerLimits.find((limit) => limit.name === MOVE_DOCUMENTS)?.value;
|
|
1129
|
+
if (typeof bound === 'number' && moves.length > bound) {
|
|
1130
|
+
return refuse('provider-limit', undefined, MOVE_DOCUMENTS);
|
|
1131
|
+
}
|
|
1132
|
+
if (source.unmodeled)
|
|
1133
|
+
return refuse('model-unavailable', moves[0].path);
|
|
1134
|
+
const id = changeBatchIdentity();
|
|
1135
|
+
const between = {
|
|
1136
|
+
source: { room: source.room, baseRoomVersion: source.baseRoomVersion },
|
|
1137
|
+
destination: {
|
|
1138
|
+
room: destination.room,
|
|
1139
|
+
baseRoomVersion: destination.baseRoomVersion,
|
|
1140
|
+
handle: destination.handle,
|
|
1141
|
+
},
|
|
1142
|
+
moves,
|
|
1143
|
+
};
|
|
1144
|
+
const written = input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy };
|
|
1145
|
+
const leavingBatch = {
|
|
1146
|
+
id,
|
|
1147
|
+
baseRoomVersion: source.baseRoomVersion,
|
|
1148
|
+
changes: removals,
|
|
1149
|
+
preconditions: moves.map(({ path }) => existence(source.visible, path)),
|
|
1150
|
+
...written,
|
|
1151
|
+
between,
|
|
1152
|
+
};
|
|
1153
|
+
const effective = await stepped(leavingRewritten(leavingBatch, source.visible));
|
|
1154
|
+
const left = await stepped(overlaying(source.visible, [effective], source.composition));
|
|
1155
|
+
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))]);
|
|
1156
|
+
if (sourceJudged.kind === 'refused')
|
|
1157
|
+
return sourceJudged;
|
|
1158
|
+
const arrivingBatch = {
|
|
1159
|
+
id,
|
|
1160
|
+
baseRoomVersion: destination.baseRoomVersion,
|
|
1161
|
+
changes: arrivals,
|
|
1162
|
+
preconditions: moves.map(({ to }) => ({ kind: 'absent', path: to })),
|
|
1163
|
+
...written,
|
|
1164
|
+
between,
|
|
1165
|
+
};
|
|
1166
|
+
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch], destination.composition));
|
|
1167
|
+
const destinationJudged = await judging(destination.visible, arrived, destination.composition, destination.validate, new Set(), moves.map(({ to }) => to));
|
|
1168
|
+
if (destinationJudged.kind === 'refused')
|
|
1169
|
+
return destinationJudged;
|
|
1170
|
+
const limited = (await stepped(exceeding(input.providerLimits, removals, effective.changes, left))) ??
|
|
1171
|
+
(await stepped(exceeding(input.providerLimits, arrivals, arrivals, arrived)));
|
|
1172
|
+
if (limited !== null)
|
|
1173
|
+
return limited;
|
|
1174
|
+
return { kind: 'moved', leaving: immutable(leavingBatch), arriving: immutable(arrivingBatch) };
|
|
1175
|
+
}
|
|
1176
|
+
export function buildMove(input) {
|
|
1177
|
+
return moving(input);
|
|
1178
|
+
}
|
|
1179
|
+
export function buildBatch(input) {
|
|
1180
|
+
return batching(input);
|
|
1181
|
+
}
|