@agentshouse/mdruntime 0.1.0-cd507e16e5fc0 → 0.1.0-cd574a610e9c8
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 +21 -19
- package/dist/acceptance.d.ts +0 -3
- package/dist/acceptance.d.ts.map +1 -1
- package/dist/acceptance.js +75 -165
- package/dist/act-lanes.d.ts +30 -0
- package/dist/act-lanes.d.ts.map +1 -0
- package/dist/act-lanes.js +225 -0
- package/dist/actions.d.ts +8 -2
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +118 -67
- package/dist/adapter.d.ts +90 -58
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +0 -1
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +0 -5
- package/dist/authored.d.ts +88 -0
- package/dist/authored.d.ts.map +1 -0
- package/dist/authored.js +345 -0
- package/dist/bootstrap.d.ts +1 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +6 -19
- package/dist/change-set.d.ts +4 -0
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +6 -0
- package/dist/change.d.ts +4 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +7 -2
- package/dist/chat-types.d.ts +0 -2
- package/dist/chat-types.d.ts.map +1 -1
- package/dist/chat-types.js +0 -2
- package/dist/client.d.ts +13 -20
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +36 -20
- package/dist/collection.d.ts +25 -4
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +19 -2
- package/dist/column.d.ts +5 -0
- package/dist/column.d.ts.map +1 -0
- package/dist/column.js +9 -0
- package/dist/compile.d.ts +2 -0
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +31 -25
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +31 -24
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -1
- package/dist/conversation-live.d.ts +26 -12
- package/dist/conversation-live.d.ts.map +1 -1
- package/dist/conversation-live.js +23 -12
- package/dist/conversation-row.d.ts +8 -1
- package/dist/conversation-row.d.ts.map +1 -1
- package/dist/conversation-row.js +20 -7
- package/dist/conversation.d.ts +37 -12
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +320 -234
- package/dist/declaration.d.ts +10 -0
- package/dist/declaration.d.ts.map +1 -0
- package/dist/declaration.js +24 -0
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +19 -44
- package/dist/durability.d.ts +4 -0
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +4 -0
- package/dist/entry.js +1 -1
- package/dist/expectation.d.ts +26 -13
- package/dist/expectation.d.ts.map +1 -1
- package/dist/expectation.js +72 -19
- package/dist/failure.d.ts +3 -0
- package/dist/failure.d.ts.map +1 -1
- package/dist/failure.js +6 -0
- package/dist/figures.d.ts.map +1 -1
- package/dist/figures.js +4 -2
- package/dist/file.d.ts +1 -1
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +3 -2
- package/dist/host.d.ts +18 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +59 -15
- package/dist/index.d.ts +16 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -7
- package/dist/install.d.ts +10 -6
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +92 -111
- package/dist/lifecycle.d.ts +1 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +54 -40
- package/dist/listing.d.ts +11 -12
- package/dist/listing.d.ts.map +1 -1
- package/dist/listing.js +48 -72
- package/dist/live.d.ts +5 -3
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +74 -33
- package/dist/loaded.d.ts +5 -0
- package/dist/loaded.d.ts.map +1 -0
- package/dist/loaded.js +40 -0
- package/dist/locks.d.ts +2 -4
- package/dist/locks.d.ts.map +1 -1
- package/dist/locks.js +10 -39
- package/dist/lost.d.ts.map +1 -1
- package/dist/lost.js +11 -23
- package/dist/media.d.ts +6 -1
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +68 -17
- package/dist/memory.d.ts +8 -3
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +92 -33
- package/dist/messages.d.ts +12 -6
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +30 -38
- package/dist/model.d.ts +0 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -5
- package/dist/models.d.ts +4 -8
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +97 -139
- package/dist/observation.d.ts +27 -12
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +184 -85
- package/dist/opened.d.ts +1 -1
- package/dist/opened.d.ts.map +1 -1
- package/dist/opened.js +5 -5
- package/dist/opening.d.ts.map +1 -1
- package/dist/opening.js +5 -9
- package/dist/outcome.d.ts +13 -1
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +32 -2
- package/dist/overlay.d.ts +6 -2
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +37 -24
- package/dist/page-metadata.d.ts +8 -0
- package/dist/page-metadata.d.ts.map +1 -0
- package/dist/page-metadata.js +19 -0
- package/dist/parts.d.ts +3 -58
- package/dist/parts.d.ts.map +1 -1
- package/dist/parts.js +10 -115
- package/dist/preparation.d.ts +27 -6
- package/dist/preparation.d.ts.map +1 -1
- package/dist/preparation.js +156 -68
- package/dist/presence.d.ts +58 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +231 -0
- package/dist/queries.d.ts +2 -2
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +67 -31
- package/dist/query.d.ts +0 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/questions.d.ts +1 -5
- package/dist/questions.d.ts.map +1 -1
- package/dist/questions.js +1 -7
- package/dist/queue.d.ts +40 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +480 -0
- package/dist/reach.d.ts +2 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +88 -26
- package/dist/react.d.ts +8 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +33 -1
- package/dist/readiness.d.ts +9 -3
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +3 -5
- package/dist/reads.d.ts +4 -2
- package/dist/reads.d.ts.map +1 -1
- package/dist/reads.js +11 -4
- package/dist/recovery.d.ts +3 -1
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +29 -7
- package/dist/refresh.js +2 -2
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +5 -6
- package/dist/replica.d.ts +5 -11
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +15 -17
- package/dist/retried.d.ts +30 -0
- package/dist/retried.d.ts.map +1 -0
- package/dist/retried.js +72 -0
- package/dist/room-draft.d.ts +31 -8
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +273 -66
- package/dist/room-drafts.d.ts.map +1 -1
- package/dist/room-drafts.js +22 -18
- package/dist/room-list.d.ts +10 -0
- package/dist/room-list.d.ts.map +1 -0
- package/dist/room-list.js +45 -0
- package/dist/rows.d.ts +2 -1
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +9 -10
- package/dist/runtime.d.ts +9 -18
- package/dist/runtime.d.ts.map +1 -1
- package/dist/save.d.ts +42 -90
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +327 -229
- package/dist/saves.d.ts +4 -40
- package/dist/saves.d.ts.map +1 -1
- package/dist/saves.js +143 -714
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +4 -5
- package/dist/shared.d.ts +0 -5
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +4 -90
- package/dist/shown.d.ts +7 -5
- package/dist/shown.d.ts.map +1 -1
- package/dist/shown.js +77 -63
- package/dist/socket/lanes.d.ts +12 -4
- package/dist/socket/lanes.d.ts.map +1 -1
- package/dist/socket/lanes.js +34 -8
- package/dist/socket/session.d.ts +0 -1
- package/dist/socket/session.d.ts.map +1 -1
- package/dist/socket.d.ts +3 -6
- package/dist/socket.d.ts.map +1 -1
- package/dist/socket.js +5 -16
- package/dist/source.d.ts +5 -7
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +18 -6
- package/dist/store/installs.d.ts +2 -2
- package/dist/store/installs.d.ts.map +1 -1
- package/dist/store/installs.js +10 -4
- package/dist/store/readiness.d.ts +5 -4
- package/dist/store/readiness.d.ts.map +1 -1
- package/dist/store/readiness.js +28 -12
- package/dist/store/records.d.ts +18 -7
- package/dist/store/records.d.ts.map +1 -1
- package/dist/store/records.js +58 -24
- package/dist/store/rooms-shown.d.ts +8 -0
- package/dist/store/rooms-shown.d.ts.map +1 -0
- package/dist/store/rooms-shown.js +13 -0
- package/dist/store/space.d.ts +1 -1
- package/dist/store/space.d.ts.map +1 -1
- package/dist/store/space.js +0 -2
- package/dist/store/watchers.js +1 -2
- package/dist/store.d.ts +23 -14
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +46 -17
- package/dist/stored.d.ts +25 -14
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +83 -32
- package/dist/surface.d.ts +5 -3
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +25 -23
- package/dist/sync.d.ts +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +96 -92
- package/dist/tables.d.ts +16 -57
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +91 -332
- package/dist/tallies.d.ts.map +1 -1
- package/dist/tallies.js +6 -7
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +4 -10
- package/dist/wire.d.ts +53 -7
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +17 -4
- package/package.json +3 -2
- package/dist/attempt.d.ts +0 -16
- package/dist/attempt.d.ts.map +0 -1
- package/dist/attempt.js +0 -70
- package/dist/job.d.ts +0 -33
- package/dist/job.d.ts.map +0 -1
- package/dist/job.js +0 -54
- package/dist/log.d.ts +0 -6
- package/dist/log.d.ts.map +0 -1
- package/dist/log.js +0 -14
- package/dist/socket/rooms.d.ts +0 -44
- package/dist/socket/rooms.d.ts.map +0 -1
- package/dist/socket/rooms.js +0 -90
- package/dist/store/fences.d.ts +0 -16
- package/dist/store/fences.d.ts.map +0 -1
- package/dist/store/fences.js +0 -26
package/dist/save.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { ancestors, authoredAt, covered, normalize } from './paths.js';
|
|
1
|
+
import { apply, ARRAY_INDEX, ATTACHMENT, create, links, parse, pointerSegments, references, relative, relativize, retarget, roomReferences, serialize, target, } from '@agentshouse/mdmodel';
|
|
2
|
+
import { immutable, isPart, leaving, queuedFile, departure, } from './change.js';
|
|
3
|
+
import { collectionOf, definitionsAt, documentsOf, governing, textType, typedText, } from './collection.js';
|
|
4
|
+
import { changeBatchIdentity, roomDraftRevision } from './identity.js';
|
|
5
|
+
import { composed, overlaying } from './overlay.js';
|
|
6
|
+
import { ancestors } from './paths.js';
|
|
8
7
|
import { entriesIn, sorting } from './shared.js';
|
|
8
|
+
import { applyParts, changedParts, changesOf, moveTarget, partsBetween, withdrawnDrafts, } from './room-draft.js';
|
|
9
9
|
import { paced, ROW_WEIGHT, stepped } from './turn.js';
|
|
10
10
|
import { validatedCollection } from './validation.js';
|
|
11
|
-
function refuse(code, file, location) {
|
|
11
|
+
function refuse(code, file, location, line) {
|
|
12
12
|
return {
|
|
13
13
|
kind: 'refused',
|
|
14
14
|
refusal: {
|
|
@@ -16,13 +16,14 @@ function refuse(code, file, location) {
|
|
|
16
16
|
retryable: false,
|
|
17
17
|
...(file === undefined ? {} : { file }),
|
|
18
18
|
...(location === undefined ? {} : { location }),
|
|
19
|
+
...(line === undefined ? {} : { line }),
|
|
19
20
|
},
|
|
20
21
|
};
|
|
21
22
|
}
|
|
23
|
+
function refusedBy(refusal, file) {
|
|
24
|
+
return refuse(refusal.rule, file, refusal.location, refusal.line);
|
|
25
|
+
}
|
|
22
26
|
function repointed(document, definition, from, to, moves, settled) {
|
|
23
|
-
const parsed = parse(document.content, from);
|
|
24
|
-
if (!parsed.ok)
|
|
25
|
-
return [];
|
|
26
27
|
const changes = [];
|
|
27
28
|
const names = new Set();
|
|
28
29
|
for (const { pointer, value } of definition === undefined
|
|
@@ -45,43 +46,13 @@ function repointed(document, definition, from, to, moves, settled) {
|
|
|
45
46
|
}
|
|
46
47
|
if (changes.length === 0)
|
|
47
48
|
return [];
|
|
48
|
-
const applied = apply(
|
|
49
|
+
const applied = apply(document, changes, definition);
|
|
49
50
|
if (applied.outcome !== 'applied')
|
|
50
51
|
return [];
|
|
51
52
|
const next = applied.document;
|
|
52
53
|
return [...names].map((name) => ({ kind: 'field', path: from, name, value: next.fields[name] }));
|
|
53
54
|
}
|
|
54
|
-
function
|
|
55
|
-
const parts = new Map();
|
|
56
|
-
for (const change of changes) {
|
|
57
|
-
if (change.kind === 'preamble')
|
|
58
|
-
parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
|
|
59
|
-
if (change.kind === 'section')
|
|
60
|
-
parts.set(`section ${change.name}`, {
|
|
61
|
-
kind: 'section',
|
|
62
|
-
name: change.name,
|
|
63
|
-
value: change.value,
|
|
64
|
-
prior: document.sections[change.name] ?? '',
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
if (parts.size === 0)
|
|
68
|
-
return document;
|
|
69
|
-
const applied = apply(document, [...parts.values()]);
|
|
70
|
-
return applied.outcome === 'applied' ? applied.document : document;
|
|
71
|
-
}
|
|
72
|
-
function bodies(path, before, after) {
|
|
73
|
-
const changes = [];
|
|
74
|
-
if (after.preamble !== before.preamble) {
|
|
75
|
-
changes.push({ kind: 'preamble', path, value: after.preamble });
|
|
76
|
-
}
|
|
77
|
-
for (const [name, value] of Object.entries(after.sections)) {
|
|
78
|
-
if (before.sections[name] === value)
|
|
79
|
-
continue;
|
|
80
|
-
changes.push({ kind: 'section', path, name, value });
|
|
81
|
-
}
|
|
82
|
-
return changes;
|
|
83
|
-
}
|
|
84
|
-
function relinks(path, moved, before, moves) {
|
|
55
|
+
function relinks(path, moved, before, moves, definition) {
|
|
85
56
|
const changes = [];
|
|
86
57
|
for (const link of links(before, path)) {
|
|
87
58
|
const destination = moves.get(link.target) ?? link.target;
|
|
@@ -94,28 +65,30 @@ function relinks(path, moved, before, moves) {
|
|
|
94
65
|
}
|
|
95
66
|
if (changes.length === 0)
|
|
96
67
|
return { kind: 'changes', changes: [] };
|
|
97
|
-
const applied = apply(before, changes);
|
|
98
|
-
if (applied.outcome === 'refuse')
|
|
99
|
-
return
|
|
100
|
-
kind: 'refused',
|
|
101
|
-
refusal: {
|
|
102
|
-
code: applied.refusal.rule,
|
|
103
|
-
retryable: false,
|
|
104
|
-
file: path,
|
|
105
|
-
...(applied.refusal.location === undefined ? {} : { location: applied.refusal.location }),
|
|
106
|
-
},
|
|
107
|
-
};
|
|
108
|
-
}
|
|
68
|
+
const applied = apply(before, changes, definition);
|
|
69
|
+
if (applied.outcome === 'refuse')
|
|
70
|
+
return refusedBy(applied.refusal, path);
|
|
109
71
|
const after = applied.document;
|
|
110
72
|
if (after === before)
|
|
111
73
|
return { kind: 'changes', changes: [] };
|
|
112
74
|
if (before.type === undefined) {
|
|
113
75
|
return { kind: 'changes', changes: [{ kind: 'replace', path, content: serialize(after) }] };
|
|
114
76
|
}
|
|
115
|
-
return { kind: 'changes', changes:
|
|
77
|
+
return { kind: 'changes', changes: partsBetween(path, before, after) };
|
|
116
78
|
}
|
|
117
79
|
function partKey(change) {
|
|
118
|
-
return change.kind === 'section'
|
|
80
|
+
return change.kind === 'section' || change.kind === 'field'
|
|
81
|
+
? `${change.kind} ${change.name}`
|
|
82
|
+
: change.kind;
|
|
83
|
+
}
|
|
84
|
+
const NAMES_A_SKIP_TESTS = 64;
|
|
85
|
+
const ENCODED = /[%&\\]/u;
|
|
86
|
+
function movedNames(moves) {
|
|
87
|
+
const names = new Set([...moves.keys()].map((path) => path.slice(path.lastIndexOf('/') + 1)));
|
|
88
|
+
return names.size > NAMES_A_SKIP_TESTS ? null : [...names];
|
|
89
|
+
}
|
|
90
|
+
function cannotLink(text, names) {
|
|
91
|
+
return names !== null && !ENCODED.test(text) && !names.some((name) => text.includes(name));
|
|
119
92
|
}
|
|
120
93
|
function* rewrites(changes, visible) {
|
|
121
94
|
const moves = new Map();
|
|
@@ -142,17 +115,21 @@ function* rewrites(changes, visible) {
|
|
|
142
115
|
}
|
|
143
116
|
if (moves.size === 0)
|
|
144
117
|
return { kind: 'changes', changes };
|
|
118
|
+
const names = movedNames(moves);
|
|
145
119
|
const merged = [...changes];
|
|
146
120
|
const added = [];
|
|
147
121
|
for (const [path, document] of yield* documentsOf(visible.collection)) {
|
|
148
122
|
if (settled.has(path))
|
|
149
123
|
continue;
|
|
150
124
|
const moved = moves.get(path) ?? path;
|
|
151
|
-
const definition =
|
|
125
|
+
const definition = definitionsAt(visible.collection.composition, path)(document.type);
|
|
152
126
|
const repointing = repointed(document, definition, path, moved, moves, settled);
|
|
153
127
|
added.push(...repointing);
|
|
154
128
|
const own = parts.get(path) ?? [];
|
|
155
|
-
const
|
|
129
|
+
const edited = applyParts(document, own.map((index) => merged[index]), definition);
|
|
130
|
+
const relinked = moved === path && own.length === 0 && cannotLink(document.content, names)
|
|
131
|
+
? { kind: 'changes', changes: [] }
|
|
132
|
+
: relinks(path, moved, edited.outcome === 'applied' ? edited.document : document, moves, definition);
|
|
156
133
|
if (relinked.kind === 'refused')
|
|
157
134
|
return relinked;
|
|
158
135
|
const keyed = new Map();
|
|
@@ -165,7 +142,7 @@ function* rewrites(changes, visible) {
|
|
|
165
142
|
if (change.kind === 'replace') {
|
|
166
143
|
for (const index of own) {
|
|
167
144
|
const kind = merged[index]?.kind;
|
|
168
|
-
if (kind === 'section' || kind === 'preamble')
|
|
145
|
+
if (kind === 'field' || kind === 'section' || kind === 'preamble')
|
|
169
146
|
merged[index] = null;
|
|
170
147
|
}
|
|
171
148
|
added.push(change);
|
|
@@ -221,25 +198,21 @@ function crossed(document, definition, path, to) {
|
|
|
221
198
|
}
|
|
222
199
|
return changes;
|
|
223
200
|
}
|
|
224
|
-
function rewritten(path, document, changes) {
|
|
201
|
+
function rewritten(path, document, changes, definition) {
|
|
225
202
|
if (changes.length === 0)
|
|
226
203
|
return [];
|
|
227
|
-
const applied = apply(document, changes);
|
|
204
|
+
const applied = apply(document, changes, definition);
|
|
228
205
|
if (applied.outcome !== 'applied')
|
|
229
206
|
return [];
|
|
230
207
|
const after = applied.document;
|
|
231
208
|
if (document.type === undefined)
|
|
232
209
|
return [{ kind: 'replace', path, content: serialize(after) }];
|
|
233
|
-
|
|
234
|
-
return [
|
|
235
|
-
...[...names].map((name) => ({ kind: 'field', path, name, value: after.fields[name] })),
|
|
236
|
-
...bodies(path, document, after),
|
|
237
|
-
];
|
|
210
|
+
return partsBetween(path, document, after);
|
|
238
211
|
}
|
|
239
212
|
function roomPathOf(handle, path) {
|
|
240
213
|
return `/rooms/${handle}/${path}`;
|
|
241
214
|
}
|
|
242
|
-
|
|
215
|
+
function* leavingRewritten(batch, visible) {
|
|
243
216
|
const between = batch.between;
|
|
244
217
|
if (between === undefined)
|
|
245
218
|
return batch;
|
|
@@ -248,8 +221,8 @@ export function* leavingRewritten(batch, visible) {
|
|
|
248
221
|
for (const [path, document] of yield* documentsOf(visible.collection)) {
|
|
249
222
|
if (moved.has(path))
|
|
250
223
|
continue;
|
|
251
|
-
const definition =
|
|
252
|
-
derived.push(...rewritten(path, document, crossed(document, definition, path, (named) => moved.get(named))));
|
|
224
|
+
const definition = definitionsAt(visible.collection.composition, path)(document.type);
|
|
225
|
+
derived.push(...rewritten(path, document, crossed(document, definition, path, (named) => moved.get(named)), definition));
|
|
253
226
|
yield ROW_WEIGHT + visible.collection.text[path].length;
|
|
254
227
|
}
|
|
255
228
|
if (derived.length === 0)
|
|
@@ -291,7 +264,7 @@ function* incoming(collection, removed, candidates) {
|
|
|
291
264
|
for (const [path, document] of candidates === null
|
|
292
265
|
? yield* documentsOf(collection)
|
|
293
266
|
: read(candidates)) {
|
|
294
|
-
const definition =
|
|
267
|
+
const definition = definitionsAt(collection.composition, path)(document.type);
|
|
295
268
|
for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
|
|
296
269
|
if (to.includes(ATTACHMENT))
|
|
297
270
|
continue;
|
|
@@ -334,18 +307,9 @@ function guard(changes, input, removing) {
|
|
|
334
307
|
const occupied = (path) => input.folders.has(path) ? path : (ancestors(path).find((one) => existing.has(one)) ?? null);
|
|
335
308
|
for (const [index, change] of changes.entries()) {
|
|
336
309
|
for (const path of affected(change)) {
|
|
337
|
-
const
|
|
338
|
-
if (
|
|
339
|
-
return refuse(
|
|
340
|
-
if (covered(input.providerOwnedPaths, normalized) ||
|
|
341
|
-
(input.authoredPaths !== undefined && !authoredAt(input.authoredPaths, normalized))) {
|
|
342
|
-
return refuse('provider-owned-path', normalized);
|
|
343
|
-
}
|
|
344
|
-
if (!input.pathRule(normalized))
|
|
345
|
-
return refuse('invalid-path', normalized);
|
|
346
|
-
for (const operation of change.kind === 'remove' ? removing : WRITE)
|
|
347
|
-
if (!input.local && !covered(input.operations[operation], normalized))
|
|
348
|
-
return refuse(`${operation}-grant`, normalized);
|
|
310
|
+
const refused = input.rule(path, change.kind === 'remove' ? removing : WRITE);
|
|
311
|
+
if (refused !== null)
|
|
312
|
+
return refuse(refused.code, refused.path);
|
|
349
313
|
}
|
|
350
314
|
if (change.kind === 'create' || change.kind === 'arrival') {
|
|
351
315
|
const taken = existing.has(change.path) ? change.path : occupied(change.path);
|
|
@@ -406,9 +370,9 @@ function guard(changes, input, removing) {
|
|
|
406
370
|
return null;
|
|
407
371
|
}
|
|
408
372
|
function unrevised(input, path) {
|
|
409
|
-
return
|
|
373
|
+
return !input.local && !input.queued.has(path) && input.visible.revisions[path] === undefined;
|
|
410
374
|
}
|
|
411
|
-
|
|
375
|
+
function queuedDocuments(queue) {
|
|
412
376
|
const paths = new Set();
|
|
413
377
|
for (const batch of queue) {
|
|
414
378
|
for (const change of batch.changes) {
|
|
@@ -453,7 +417,7 @@ function* preconditionsOf(changes, visible, bases) {
|
|
|
453
417
|
};
|
|
454
418
|
for (const change of changes) {
|
|
455
419
|
yield ROW_WEIGHT;
|
|
456
|
-
if (change
|
|
420
|
+
if (isPart(change)) {
|
|
457
421
|
const based = bases.find((base) => sameTarget(base, change));
|
|
458
422
|
if (based !== undefined) {
|
|
459
423
|
add(based);
|
|
@@ -544,93 +508,101 @@ function* exceeding(limits, changes, effective, resulting) {
|
|
|
544
508
|
function shellRelative(text, path, handle, definition) {
|
|
545
509
|
if (!text.includes(`/rooms/${handle}/`))
|
|
546
510
|
return null;
|
|
547
|
-
const parsed = parse(text, path);
|
|
511
|
+
const parsed = parse(text, path, definition);
|
|
548
512
|
if (!parsed.ok)
|
|
549
513
|
return null;
|
|
550
514
|
const result = relativize(parsed.document, path, handle, definition(parsed.document.type));
|
|
551
515
|
return result.outcome === 'applied' && result.document.content !== text ? result.document : null;
|
|
552
516
|
}
|
|
553
517
|
function relativeField(path, name, value, handle, definition) {
|
|
554
|
-
|
|
555
|
-
if (definition === undefined ||
|
|
556
|
-
!opened.ok ||
|
|
557
|
-
!JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
518
|
+
if (definition === undefined || !JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
558
519
|
return value;
|
|
559
|
-
const held = apply(
|
|
520
|
+
const held = apply(create(definition.name, definition), [{ kind: 'field', name, value, prior: undefined }], definition);
|
|
560
521
|
if (held.outcome !== 'applied')
|
|
561
522
|
return value;
|
|
562
523
|
const result = relativize(held.document, path, handle, definition);
|
|
563
524
|
return result.outcome === 'applied' ? result.document.fields[name] : value;
|
|
564
525
|
}
|
|
565
526
|
function relativeChanges(input) {
|
|
527
|
+
const changes = [];
|
|
528
|
+
for (const change of input.changes) {
|
|
529
|
+
const next = relativeChange(change, input);
|
|
530
|
+
if ('refusal' in next)
|
|
531
|
+
return next;
|
|
532
|
+
changes.push(next.change);
|
|
533
|
+
}
|
|
534
|
+
return { kind: 'changes', changes };
|
|
535
|
+
}
|
|
536
|
+
function relativeChange(change, input) {
|
|
566
537
|
const { handle, composition, visible } = input;
|
|
567
|
-
const typed = (path) => (type) => definitionOf(governing(composition, path), type);
|
|
568
538
|
const body = (path, text) => shellRelative(text, path, handle, () => undefined)?.content ?? text;
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
539
|
+
const definition = definitionsAt(composition, change.path);
|
|
540
|
+
if (change.kind === 'field') {
|
|
541
|
+
const type = visible.collection.document(change.path)?.type;
|
|
542
|
+
const value = relativeField(change.path, change.name, change.value, handle, definition(type));
|
|
543
|
+
return { change: value === change.value ? change : { ...change, value } };
|
|
544
|
+
}
|
|
545
|
+
if (change.kind === 'section' || change.kind === 'preamble') {
|
|
546
|
+
const value = body(change.path, change.value);
|
|
547
|
+
return { change: value === change.value ? change : { ...change, value } };
|
|
548
|
+
}
|
|
549
|
+
if (change.kind === 'replace' || (change.kind === 'create' && 'content' in change)) {
|
|
550
|
+
const content = shellRelative(change.content, change.path, handle, definition)?.content ?? change.content;
|
|
551
|
+
return { change: content === change.content ? change : { ...change, content } };
|
|
552
|
+
}
|
|
553
|
+
if (change.kind !== 'create')
|
|
554
|
+
return { change };
|
|
555
|
+
const type = typeof change.fields.type === 'string' ? change.fields.type : undefined;
|
|
556
|
+
const fields = Object.fromEntries(Object.entries(change.fields).map(([name, value]) => [
|
|
557
|
+
name,
|
|
558
|
+
relativeField(change.path, name, value, handle, definition(type)),
|
|
559
|
+
]));
|
|
560
|
+
const sections = Object.fromEntries(Object.entries(change.sections).map(([name, value]) => [name, body(change.path, value)]));
|
|
561
|
+
const written = composed(change.path, fields, sections, composition);
|
|
562
|
+
if (written.outcome === 'refuse')
|
|
563
|
+
return refusedBy(written.refusal, change.path);
|
|
564
|
+
if (written.outcome === 'stale')
|
|
565
|
+
throw new Error(`the creation of ${change.path} is stale`);
|
|
566
|
+
if (Object.keys(sections).length === 0)
|
|
567
|
+
return { change: { ...change, fields, sections } };
|
|
568
|
+
return {
|
|
569
|
+
change: { kind: 'create', path: change.path, content: serialize(written.document) },
|
|
570
|
+
};
|
|
594
571
|
}
|
|
595
572
|
function unqueueable(file) {
|
|
596
|
-
return
|
|
597
|
-
}
|
|
598
|
-
function isPart(change) {
|
|
599
|
-
return change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble';
|
|
573
|
+
return refuse('unknown-revision', file);
|
|
600
574
|
}
|
|
601
575
|
function touched(change) {
|
|
602
576
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
603
577
|
}
|
|
604
|
-
function partsApplied(content, path, changes) {
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
const
|
|
578
|
+
function partsApplied(content, path, changes, composition) {
|
|
579
|
+
const applied = partsResult(content, path, changes, composition);
|
|
580
|
+
return applied?.outcome === 'applied' ? serialize(applied.document) : null;
|
|
581
|
+
}
|
|
582
|
+
function unapplied(changes, visible, composition) {
|
|
583
|
+
const parted = new Map();
|
|
610
584
|
for (const change of changes) {
|
|
611
|
-
if (change
|
|
612
|
-
|
|
613
|
-
kind: 'field',
|
|
614
|
-
name: change.name,
|
|
615
|
-
value: change.value,
|
|
616
|
-
prior: document.fields[change.name],
|
|
617
|
-
});
|
|
618
|
-
else if (change.kind === 'section')
|
|
619
|
-
parts.set(`section ${change.name}`, {
|
|
620
|
-
kind: 'section',
|
|
621
|
-
name: change.name,
|
|
622
|
-
value: change.value,
|
|
623
|
-
prior: document.sections[change.name] ?? '',
|
|
624
|
-
});
|
|
625
|
-
else
|
|
626
|
-
parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
|
|
585
|
+
if (isPart(change))
|
|
586
|
+
parted.set(change.path, [...(parted.get(change.path) ?? []), change]);
|
|
627
587
|
}
|
|
628
|
-
const
|
|
629
|
-
|
|
588
|
+
for (const [path, parts] of parted) {
|
|
589
|
+
const content = visible.documents[path];
|
|
590
|
+
if (content === undefined)
|
|
591
|
+
continue;
|
|
592
|
+
const applied = partsResult(content, path, parts, composition);
|
|
593
|
+
if (applied?.outcome === 'refuse')
|
|
594
|
+
return refusedBy(applied.refusal, path);
|
|
595
|
+
}
|
|
596
|
+
return null;
|
|
630
597
|
}
|
|
631
|
-
function
|
|
598
|
+
function partsResult(content, path, changes, composition) {
|
|
599
|
+
const definitions = definitionsAt(composition, path);
|
|
600
|
+
const parsed = parse(content, path, definitions);
|
|
601
|
+
return parsed.ok ? applyParts(parsed.document, changes, definitions(parsed.document.type)) : null;
|
|
602
|
+
}
|
|
603
|
+
function settledParts(changes, deferred, composition) {
|
|
632
604
|
for (const [held, parts] of deferred) {
|
|
633
|
-
const content = partsApplied(held.content, held.path, parts);
|
|
605
|
+
const content = partsApplied(held.content, held.path, parts, composition);
|
|
634
606
|
if (content === null)
|
|
635
607
|
return held.path;
|
|
636
608
|
changes[changes.indexOf(held)] = { ...held, content };
|
|
@@ -705,7 +677,9 @@ function* relinked(batch, move, before, after, appending) {
|
|
|
705
677
|
for (const change of derived.changes) {
|
|
706
678
|
if (change === move)
|
|
707
679
|
continue;
|
|
708
|
-
const unsettled = isPart(change)
|
|
680
|
+
const unsettled = isPart(change)
|
|
681
|
+
? null
|
|
682
|
+
: settledParts(changes, deferred, after.collection.composition);
|
|
709
683
|
if (unsettled !== null)
|
|
710
684
|
return unqueueable(unsettled);
|
|
711
685
|
const outcome = folded(changes, change, appending, deferred);
|
|
@@ -716,7 +690,7 @@ function* relinked(batch, move, before, after, appending) {
|
|
|
716
690
|
if (outcome !== 'skipped')
|
|
717
691
|
changed = true;
|
|
718
692
|
}
|
|
719
|
-
const unsettled = settledParts(changes, deferred);
|
|
693
|
+
const unsettled = settledParts(changes, deferred, after.collection.composition);
|
|
720
694
|
if (unsettled !== null)
|
|
721
695
|
return unqueueable(unsettled);
|
|
722
696
|
if (!changed)
|
|
@@ -777,11 +751,10 @@ function withdrawnFile(batch, change, at) {
|
|
|
777
751
|
];
|
|
778
752
|
return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
|
|
779
753
|
}
|
|
780
|
-
|
|
754
|
+
function* requeuing(queue, changes, visible, changeable, state) {
|
|
781
755
|
const next = [...queue];
|
|
782
756
|
const origins = new Map();
|
|
783
757
|
const withdrawn = new Map();
|
|
784
|
-
const owners = new Set();
|
|
785
758
|
const queued = new Set();
|
|
786
759
|
const kept = [];
|
|
787
760
|
const originOf = (batch) => origins.get(batch) ?? batch.id;
|
|
@@ -827,7 +800,6 @@ export function* requeuing(queue, changes, visible, changeable, state) {
|
|
|
827
800
|
if (replaced !== null)
|
|
828
801
|
origins.set(replaced, origin);
|
|
829
802
|
}
|
|
830
|
-
owners.add(originOf(owner));
|
|
831
803
|
next.splice(index, following.length, ...rebuilt.filter((batch) => batch !== null));
|
|
832
804
|
queued.add(change.path);
|
|
833
805
|
}
|
|
@@ -837,62 +809,16 @@ export function* requeuing(queue, changes, visible, changeable, state) {
|
|
|
837
809
|
changes: kept,
|
|
838
810
|
queued,
|
|
839
811
|
withdrawn,
|
|
840
|
-
owners,
|
|
841
812
|
};
|
|
842
813
|
}
|
|
843
|
-
function
|
|
844
|
-
return precondition.kind === 'field' ||
|
|
845
|
-
precondition.kind === 'section' ||
|
|
846
|
-
precondition.kind === 'preamble'
|
|
847
|
-
? identity(precondition)
|
|
848
|
-
: `path ${precondition.path}`;
|
|
849
|
-
}
|
|
850
|
-
export function joined(queue, owner, extra) {
|
|
851
|
-
const later = new Set(queue.slice(queue.indexOf(owner) + 1).flatMap((batch) => batch.changes.flatMap(touched)));
|
|
852
|
-
const clash = extra.changes.flatMap(touched).find((path) => later.has(path));
|
|
853
|
-
if (clash !== undefined)
|
|
854
|
-
return unqueueable(clash);
|
|
855
|
-
const changes = [...owner.changes];
|
|
856
|
-
const deferred = new Map();
|
|
857
|
-
for (const change of extra.changes) {
|
|
858
|
-
const unsettled = isPart(change) ? null : settledParts(changes, deferred);
|
|
859
|
-
if (unsettled !== null)
|
|
860
|
-
return unqueueable(unsettled);
|
|
861
|
-
if (folded(changes, change, true, deferred) === 'refused')
|
|
862
|
-
return unqueueable(change.path);
|
|
863
|
-
}
|
|
864
|
-
const unsettled = settledParts(changes, deferred);
|
|
865
|
-
if (unsettled !== null)
|
|
866
|
-
return unqueueable(unsettled);
|
|
867
|
-
const held = new Set(owner.preconditions.map(covering));
|
|
868
|
-
const written = new Set(owner.changes.flatMap((change) => change.kind === 'create' || change.kind === 'replace' ? [change.path] : []));
|
|
869
|
-
const preconditions = [
|
|
870
|
-
...owner.preconditions,
|
|
871
|
-
...extra.preconditions.filter((precondition) => {
|
|
872
|
-
const key = covering(precondition);
|
|
873
|
-
return (!held.has(key) && (key === `path ${precondition.path}` || !written.has(precondition.path)));
|
|
874
|
-
}),
|
|
875
|
-
];
|
|
876
|
-
const batch = immutable({
|
|
877
|
-
...owner,
|
|
878
|
-
...(extra.validation === undefined ? {} : { validation: extra.validation }),
|
|
879
|
-
id: changeBatchIdentity(),
|
|
880
|
-
changes,
|
|
881
|
-
preconditions,
|
|
882
|
-
});
|
|
883
|
-
return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
|
|
884
|
-
}
|
|
885
|
-
export function* limiting(queue, batch, limits, state) {
|
|
814
|
+
function* limiting(queue, batch, limits, state) {
|
|
886
815
|
const at = queue.indexOf(batch);
|
|
887
816
|
const rewritten = yield* providerRewritten(batch, yield* state(queue.slice(0, at)));
|
|
888
817
|
const limited = yield* exceeding(limits, batch.changes, rewritten.changes, yield* state(queue.slice(0, at + 1)));
|
|
889
|
-
return limited?.
|
|
818
|
+
return limited?.refusal ?? null;
|
|
890
819
|
}
|
|
891
|
-
function
|
|
892
|
-
|
|
893
|
-
return false;
|
|
894
|
-
const parsed = parse(content);
|
|
895
|
-
return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
|
|
820
|
+
function typedContent(content) {
|
|
821
|
+
return content !== undefined && typedText(parse(content));
|
|
896
822
|
}
|
|
897
823
|
function touchedLibrary(resulting, touched, composition) {
|
|
898
824
|
const entries = touched
|
|
@@ -912,9 +838,9 @@ function touchedLibrary(resulting, touched, composition) {
|
|
|
912
838
|
if (binaries.has(path))
|
|
913
839
|
known.set(path, { type: undefined, binary: true });
|
|
914
840
|
else if (content !== undefined && content !== null) {
|
|
915
|
-
const
|
|
916
|
-
if (
|
|
917
|
-
known.set(path, { type
|
|
841
|
+
const type = textType(parse(content, path, definitionsAt(composition, path)));
|
|
842
|
+
if (type !== undefined)
|
|
843
|
+
known.set(path, { type, binary: false });
|
|
918
844
|
await pace(content.length + ROW_WEIGHT);
|
|
919
845
|
}
|
|
920
846
|
}
|
|
@@ -932,7 +858,7 @@ async function judging(visible, resulting, composition, validate, removed, touch
|
|
|
932
858
|
for (const path of touched) {
|
|
933
859
|
const before = visible.documents[path];
|
|
934
860
|
const after = resulting.documents[path];
|
|
935
|
-
if (
|
|
861
|
+
if (typedContent(before) || typedContent(after))
|
|
936
862
|
typedPaths.push(path);
|
|
937
863
|
await pace((before?.length ?? 0) + (after?.length ?? 0) + ROW_WEIGHT);
|
|
938
864
|
}
|
|
@@ -950,6 +876,7 @@ async function judging(visible, resulting, composition, validate, removed, touch
|
|
|
950
876
|
retryable: false,
|
|
951
877
|
file: verdict.file,
|
|
952
878
|
...(verdict.location === undefined ? {} : { location: verdict.location }),
|
|
879
|
+
...(verdict.line === undefined ? {} : { line: verdict.line }),
|
|
953
880
|
...(verdict.referrers === undefined ? {} : { referrers: verdict.referrers }),
|
|
954
881
|
},
|
|
955
882
|
};
|
|
@@ -965,8 +892,6 @@ function placed(reference) {
|
|
|
965
892
|
const key = roomPathOf(reference.handle, reference.path);
|
|
966
893
|
if (reference.kind === 'link')
|
|
967
894
|
return `link ${key}`;
|
|
968
|
-
if (reference.kind === 'item')
|
|
969
|
-
return `section ${reference.section} ${key}`;
|
|
970
895
|
const pointer = reference.pointer
|
|
971
896
|
.split('/')
|
|
972
897
|
.map((segment) => (ARRAY_INDEX.test(segment) ? '-' : segment))
|
|
@@ -976,10 +901,11 @@ function placed(reference) {
|
|
|
976
901
|
function roomReferencesIn(path, content, composition) {
|
|
977
902
|
if (content === undefined)
|
|
978
903
|
return [];
|
|
979
|
-
const
|
|
904
|
+
const definitions = definitionsAt(composition, path);
|
|
905
|
+
const parsed = parse(content, path, definitions);
|
|
980
906
|
if (!parsed.ok)
|
|
981
907
|
return [];
|
|
982
|
-
return roomReferences(parsed.document,
|
|
908
|
+
return roomReferences(parsed.document, definitions(parsed.document.type));
|
|
983
909
|
}
|
|
984
910
|
function* admittedRooms(changes, visible, resulting, composition) {
|
|
985
911
|
const from = new Map();
|
|
@@ -997,7 +923,7 @@ function* admittedRooms(changes, visible, resulting, composition) {
|
|
|
997
923
|
continue;
|
|
998
924
|
if (held.has(placed(reference)))
|
|
999
925
|
continue;
|
|
1000
|
-
return refuse('unresolved-reference', path, reference.
|
|
926
|
+
return refuse('unresolved-reference', path, reference.pointer);
|
|
1001
927
|
}
|
|
1002
928
|
yield ROW_WEIGHT + now.length;
|
|
1003
929
|
}
|
|
@@ -1015,7 +941,10 @@ function validationOf(validated) {
|
|
|
1015
941
|
};
|
|
1016
942
|
}
|
|
1017
943
|
async function batching(input) {
|
|
1018
|
-
const
|
|
944
|
+
const relative = relativeChanges(input);
|
|
945
|
+
if (relative.kind === 'refused')
|
|
946
|
+
return relative;
|
|
947
|
+
const { changes } = relative;
|
|
1019
948
|
const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
|
|
1020
949
|
if (structural !== undefined && input.unmodeled) {
|
|
1021
950
|
return refuse('model-unavailable', structural.path);
|
|
@@ -1025,12 +954,12 @@ async function batching(input) {
|
|
|
1025
954
|
const rewritten = await stepped(rewrites(changes, input.visible));
|
|
1026
955
|
if (rewritten.kind === 'refused')
|
|
1027
956
|
return rewritten;
|
|
1028
|
-
const refusal = guard(changes, input, WRITE);
|
|
957
|
+
const refusal = guard(changes, input, WRITE) ?? unapplied(changes, input.visible, input.composition);
|
|
1029
958
|
if (refusal !== null)
|
|
1030
959
|
return refusal;
|
|
1031
960
|
const preconditions = await stepped(preconditionsOf(changes, input.visible, input.bases));
|
|
1032
961
|
const predicted = await stepped(anticipated(changes, preconditions, rewritten.changes, input.visible));
|
|
1033
|
-
const resulting = await stepped(overlaying(input.visible, [{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted }]));
|
|
962
|
+
const resulting = await stepped(overlaying(input.visible, [{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted }], input.composition));
|
|
1034
963
|
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))]);
|
|
1035
964
|
if (judged.kind === 'refused')
|
|
1036
965
|
return judged;
|
|
@@ -1052,7 +981,7 @@ async function batching(input) {
|
|
|
1052
981
|
}),
|
|
1053
982
|
};
|
|
1054
983
|
}
|
|
1055
|
-
|
|
984
|
+
const MOVE_DOCUMENTS = 'move-documents';
|
|
1056
985
|
function heldAt(visible, path) {
|
|
1057
986
|
return visible.documents[path] !== undefined || visible.binaries[path] !== undefined;
|
|
1058
987
|
}
|
|
@@ -1061,7 +990,7 @@ function arrivingText(path, to, source, moved) {
|
|
|
1061
990
|
const document = source.visible.collection.document(path);
|
|
1062
991
|
if (document === undefined)
|
|
1063
992
|
return content;
|
|
1064
|
-
const definition =
|
|
993
|
+
const definition = definitionsAt(source.composition, path)(document.type);
|
|
1065
994
|
const changes = crossed(document, definition, path, (named, href) => {
|
|
1066
995
|
const at = moved.get(named);
|
|
1067
996
|
if (at !== undefined)
|
|
@@ -1070,7 +999,7 @@ function arrivingText(path, to, source, moved) {
|
|
|
1070
999
|
});
|
|
1071
1000
|
if (changes.length === 0)
|
|
1072
1001
|
return content;
|
|
1073
|
-
const applied = apply(document, changes);
|
|
1002
|
+
const applied = apply(document, changes, definition);
|
|
1074
1003
|
return applied.outcome === 'applied' ? serialize(applied.document) : content;
|
|
1075
1004
|
}
|
|
1076
1005
|
function arrivalOf({ path, to }, source, moved) {
|
|
@@ -1103,11 +1032,9 @@ function arrivalOf({ path, to }, source, moved) {
|
|
|
1103
1032
|
};
|
|
1104
1033
|
}
|
|
1105
1034
|
async function moving(input) {
|
|
1106
|
-
const { source, destination, moves
|
|
1107
|
-
if (moves.length === 0)
|
|
1108
|
-
return refuse('no-change');
|
|
1035
|
+
const { source, destination, moves } = input;
|
|
1109
1036
|
const removals = moves.map(({ path }) => ({ kind: 'remove', path }));
|
|
1110
|
-
const leaves = guard(removals, { ...source, local: input.local, bases: []
|
|
1037
|
+
const leaves = guard(removals, { ...source, local: input.local, bases: [] }, MOVE_OUT);
|
|
1111
1038
|
if (leaves !== null)
|
|
1112
1039
|
return leaves;
|
|
1113
1040
|
const uploading = moves.find(({ path }) => source.visible.binaries[path]?.state === 'pending');
|
|
@@ -1115,7 +1042,7 @@ async function moving(input) {
|
|
|
1115
1042
|
return refuse('unknown-revision', uploading.path);
|
|
1116
1043
|
const moved = new Map(moves.map(({ path, to }) => [path, to]));
|
|
1117
1044
|
const arrivals = moves.map((move) => arrivalOf(move, source, moved));
|
|
1118
|
-
const arrives = guard(arrivals, { ...destination, local: input.local, bases: []
|
|
1045
|
+
const arrives = guard(arrivals, { ...destination, local: input.local, bases: [] }, WRITE);
|
|
1119
1046
|
if (arrives !== null)
|
|
1120
1047
|
return arrives;
|
|
1121
1048
|
const bound = input.providerLimits.find((limit) => limit.name === MOVE_DOCUMENTS)?.value;
|
|
@@ -1144,7 +1071,7 @@ async function moving(input) {
|
|
|
1144
1071
|
between,
|
|
1145
1072
|
};
|
|
1146
1073
|
const effective = await stepped(leavingRewritten(leavingBatch, source.visible));
|
|
1147
|
-
const left = await stepped(overlaying(source.visible, [effective]));
|
|
1074
|
+
const left = await stepped(overlaying(source.visible, [effective], source.composition));
|
|
1148
1075
|
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))]);
|
|
1149
1076
|
if (sourceJudged.kind === 'refused')
|
|
1150
1077
|
return sourceJudged;
|
|
@@ -1156,7 +1083,7 @@ async function moving(input) {
|
|
|
1156
1083
|
...written,
|
|
1157
1084
|
between,
|
|
1158
1085
|
};
|
|
1159
|
-
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch]));
|
|
1086
|
+
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch], destination.composition));
|
|
1160
1087
|
const destinationJudged = await judging(destination.visible, arrived, destination.composition, destination.validate, new Set(), moves.map(({ to }) => to));
|
|
1161
1088
|
if (destinationJudged.kind === 'refused')
|
|
1162
1089
|
return destinationJudged;
|
|
@@ -1166,9 +1093,180 @@ async function moving(input) {
|
|
|
1166
1093
|
return limited;
|
|
1167
1094
|
return { kind: 'moved', leaving: immutable(leavingBatch), arriving: immutable(arrivingBatch) };
|
|
1168
1095
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1096
|
+
function unreadable(saved, visible, composition) {
|
|
1097
|
+
const path = saved.find(({ path: at, parts }) => (parts.fields !== undefined ||
|
|
1098
|
+
parts.sections !== undefined ||
|
|
1099
|
+
parts.preamble !== undefined) &&
|
|
1100
|
+
typedContent(visible.documents[at]) &&
|
|
1101
|
+
governing(composition, at).status === 'unavailable')?.path;
|
|
1102
|
+
return path === undefined ? null : refuse('model-unavailable', path);
|
|
1103
|
+
}
|
|
1104
|
+
function changedUnderneath(saved, visible) {
|
|
1105
|
+
const parts = saved.flatMap(({ path, parts: held }) => changedParts(path, held, visible));
|
|
1106
|
+
return parts.length === 0
|
|
1107
|
+
? null
|
|
1108
|
+
: { kind: 'refused', refusal: { code: 'changed-underneath', retryable: false, parts } };
|
|
1109
|
+
}
|
|
1110
|
+
function sideOf(room, visible) {
|
|
1111
|
+
const { record } = room;
|
|
1112
|
+
return {
|
|
1113
|
+
room: record.locator,
|
|
1114
|
+
handle: record.declaration.handle,
|
|
1115
|
+
baseRoomVersion: record.roomVersion,
|
|
1116
|
+
composition: room.composition,
|
|
1117
|
+
rule: room.rule,
|
|
1118
|
+
visible,
|
|
1119
|
+
folders: room.folders,
|
|
1120
|
+
queued: queuedDocuments(room.queue),
|
|
1121
|
+
unmodeled: !room.modelled,
|
|
1122
|
+
validate: room.validate,
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
function carriedDrafts(ask, from, into) {
|
|
1126
|
+
return new Map(ask.saved.flatMap(({ path, parts }) => {
|
|
1127
|
+
const record = from.drafts.get(path);
|
|
1128
|
+
if (parts.move === undefined || record === undefined || ask.consumed.has(path))
|
|
1129
|
+
return [];
|
|
1130
|
+
const to = moveTarget(parts.move);
|
|
1131
|
+
if (into.drafts.has(to))
|
|
1132
|
+
return [];
|
|
1133
|
+
const locator = into.record.locator;
|
|
1134
|
+
return [[path, { ...record, locator, path: to, revision: roomDraftRevision() }]];
|
|
1135
|
+
}));
|
|
1136
|
+
}
|
|
1137
|
+
async function plannedMove(ask, source, into) {
|
|
1138
|
+
const destination = into.record.locator;
|
|
1139
|
+
const mixed = ask.saved.find(({ parts }) => Object.keys(parts).length !== 1 || parts.move?.room !== destination);
|
|
1140
|
+
if (mixed !== undefined)
|
|
1141
|
+
return refuse('mixed-room-move', mixed.path);
|
|
1142
|
+
const leavingVisible = await stepped(source.replay(source.queue));
|
|
1143
|
+
const changed = changedUnderneath(ask.saved, leavingVisible);
|
|
1144
|
+
if (changed !== null)
|
|
1145
|
+
return changed;
|
|
1146
|
+
const moves = ask.saved.map(({ path, parts }) => ({ path, to: moveTarget(parts.move) }));
|
|
1147
|
+
const built = await moving({
|
|
1148
|
+
source: sideOf(source, leavingVisible),
|
|
1149
|
+
destination: sideOf(into, await stepped(into.replay(into.queue))),
|
|
1150
|
+
moves,
|
|
1151
|
+
providerLimits: ask.providerLimits,
|
|
1152
|
+
local: ask.local,
|
|
1153
|
+
...(ask.writtenBy === undefined ? {} : { writtenBy: ask.writtenBy }),
|
|
1154
|
+
});
|
|
1155
|
+
if (built.kind === 'refused')
|
|
1156
|
+
return built;
|
|
1157
|
+
const { id } = built.leaving;
|
|
1158
|
+
const carried = carriedDrafts(ask, source, into);
|
|
1159
|
+
return {
|
|
1160
|
+
kind: 'planned',
|
|
1161
|
+
identifier: id,
|
|
1162
|
+
rooms: [
|
|
1163
|
+
{
|
|
1164
|
+
locator: source.record.locator,
|
|
1165
|
+
queue: [...source.queue, built.leaving],
|
|
1166
|
+
saved: new Map([[id, ask.saved]]),
|
|
1167
|
+
consumed: [...ask.consumed, ...carried.keys()],
|
|
1168
|
+
carried: [],
|
|
1169
|
+
},
|
|
1170
|
+
{
|
|
1171
|
+
locator: destination,
|
|
1172
|
+
queue: [...into.queue, built.arriving],
|
|
1173
|
+
saved: new Map([
|
|
1174
|
+
[id, [...carried.values()].map(({ path }) => ({ path, parts: {} }))],
|
|
1175
|
+
]),
|
|
1176
|
+
consumed: [],
|
|
1177
|
+
carried: [...carried.values()],
|
|
1178
|
+
},
|
|
1179
|
+
],
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
async function plannedWithin(ask, room) {
|
|
1183
|
+
const { record } = room;
|
|
1184
|
+
const visible = await stepped(room.replay(room.queue));
|
|
1185
|
+
const changed = unreadable(ask.saved, visible, room.composition) ?? changedUnderneath(ask.saved, visible);
|
|
1186
|
+
if (changed !== null)
|
|
1187
|
+
return changed;
|
|
1188
|
+
const parts = ask.saved.map(({ path, parts: held }) => changesOf(path, held));
|
|
1189
|
+
const changes = parts.flatMap(({ changes: made }) => made);
|
|
1190
|
+
const requeue = await stepped(requeuing(room.queue, changes, visible, (batch) => room.changeable.has(batch.id), room.replay));
|
|
1191
|
+
if (requeue.kind === 'refused')
|
|
1192
|
+
return requeue;
|
|
1193
|
+
const foreign = [...requeue.withdrawn.keys()].some((origin) => {
|
|
1194
|
+
const writer = room.queue.find((batch) => batch.id === origin)?.writtenBy;
|
|
1195
|
+
return writer?.app !== ask.writtenBy?.app || writer?.release !== ask.writtenBy?.release;
|
|
1196
|
+
});
|
|
1197
|
+
if (foreign)
|
|
1198
|
+
return unqueueable([...requeue.queued].at(-1));
|
|
1199
|
+
const input = {
|
|
1200
|
+
local: ask.local,
|
|
1201
|
+
baseRoomVersion: record.roomVersion,
|
|
1202
|
+
composition: room.composition,
|
|
1203
|
+
rule: room.rule,
|
|
1204
|
+
providerLimits: ask.providerLimits,
|
|
1205
|
+
handle: record.declaration.handle,
|
|
1206
|
+
visible,
|
|
1207
|
+
folders: room.folders,
|
|
1208
|
+
changes,
|
|
1209
|
+
bases: parts.flatMap(({ bases }) => bases),
|
|
1210
|
+
queued: queuedDocuments(room.queue),
|
|
1211
|
+
writtenBy: ask.writtenBy,
|
|
1212
|
+
unmodeled: !room.modelled,
|
|
1213
|
+
validate: room.validate,
|
|
1214
|
+
};
|
|
1215
|
+
const rewritten = requeue.withdrawn.size > 0;
|
|
1216
|
+
if (rewritten) {
|
|
1217
|
+
const whole = await batching({
|
|
1218
|
+
...input,
|
|
1219
|
+
providerLimits: [],
|
|
1220
|
+
queued: new Set([...input.queued, ...requeue.queued]),
|
|
1221
|
+
});
|
|
1222
|
+
if (whole.kind === 'refused')
|
|
1223
|
+
return whole;
|
|
1224
|
+
}
|
|
1225
|
+
const own = !rewritten
|
|
1226
|
+
? await batching(input)
|
|
1227
|
+
: requeue.changes.length === 0
|
|
1228
|
+
? null
|
|
1229
|
+
: await batching({
|
|
1230
|
+
...input,
|
|
1231
|
+
visible: await stepped(room.replay(requeue.queue)),
|
|
1232
|
+
changes: requeue.changes,
|
|
1233
|
+
queued: queuedDocuments(requeue.queue),
|
|
1234
|
+
});
|
|
1235
|
+
if (own?.kind === 'refused')
|
|
1236
|
+
return own;
|
|
1237
|
+
const queue = own === null ? requeue.queue : [...requeue.queue, own.batch];
|
|
1238
|
+
const saved = new Map();
|
|
1239
|
+
for (const [origin, rebuilt] of requeue.withdrawn) {
|
|
1240
|
+
if (rebuilt === null)
|
|
1241
|
+
continue;
|
|
1242
|
+
const limited = await stepped(limiting(queue, rebuilt, ask.providerLimits, room.replay));
|
|
1243
|
+
if (limited !== null)
|
|
1244
|
+
return { kind: 'refused', refusal: limited };
|
|
1245
|
+
saved.set(rebuilt.id, withdrawnDrafts(room.saved.get(origin) ?? [], changes));
|
|
1246
|
+
}
|
|
1247
|
+
if (own !== null) {
|
|
1248
|
+
saved.set(own.batch.id, ask.saved.filter(({ path }) => !requeue.queued.has(path)));
|
|
1249
|
+
}
|
|
1250
|
+
const carried = carriedDrafts(ask, room, room);
|
|
1251
|
+
return {
|
|
1252
|
+
kind: 'planned',
|
|
1253
|
+
identifier: own?.batch.id ??
|
|
1254
|
+
[...requeue.withdrawn].map(([origin, rebuilt]) => rebuilt?.id ?? origin).at(-1),
|
|
1255
|
+
rooms: [
|
|
1256
|
+
{
|
|
1257
|
+
locator: record.locator,
|
|
1258
|
+
queue,
|
|
1259
|
+
saved,
|
|
1260
|
+
consumed: [...ask.consumed, ...carried.keys()],
|
|
1261
|
+
carried: [...carried.values()],
|
|
1262
|
+
},
|
|
1263
|
+
],
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
export function destination(locator, saved) {
|
|
1267
|
+
const into = saved.find(({ parts }) => parts.move?.room !== undefined && parts.move.room !== locator);
|
|
1268
|
+
return into?.parts.move?.room ?? null;
|
|
1171
1269
|
}
|
|
1172
|
-
export function
|
|
1173
|
-
return
|
|
1270
|
+
export function planSave(ask, room, into) {
|
|
1271
|
+
return into === undefined ? plannedWithin(ask, room) : plannedMove(ask, room, into);
|
|
1174
1272
|
}
|