@agentshouse/mdruntime 0.1.0-c0136de3edfbe → 0.1.0-c01d4615bfe46
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 +16 -15
- package/dist/acceptance.d.ts +0 -3
- package/dist/acceptance.d.ts.map +1 -1
- package/dist/acceptance.js +74 -164
- 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 +122 -68
- package/dist/adapter.d.ts +49 -55
- 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/bootstrap.d.ts +1 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +6 -18
- 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.map +1 -1
- package/dist/change.js +4 -2
- package/dist/chat-types.d.ts +2 -2
- package/dist/chat-types.d.ts.map +1 -1
- package/dist/chat-types.js +11 -2
- package/dist/client.d.ts +13 -19
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +35 -19
- package/dist/collection.d.ts +22 -4
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +12 -2
- package/dist/compile.d.ts +2 -0
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +31 -24
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +32 -24
- package/dist/conversation-live.d.ts +147 -0
- package/dist/conversation-live.d.ts.map +1 -0
- package/dist/conversation-live.js +258 -0
- package/dist/conversation-row.d.ts.map +1 -1
- package/dist/conversation-row.js +7 -1
- package/dist/conversation.d.ts +52 -44
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +956 -1029
- 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 +13 -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 +1 -0
- package/dist/figures.d.ts.map +1 -1
- package/dist/figures.js +2 -2
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +2 -1
- 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 +18 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +46 -11
- package/dist/index.d.ts +17 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -6
- package/dist/install.d.ts +2 -4
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +53 -83
- package/dist/lifecycle.d.ts +1 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +45 -35
- package/dist/listing.d.ts +6 -11
- package/dist/listing.d.ts.map +1 -1
- package/dist/listing.js +25 -61
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +3 -3
- 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 +10 -22
- package/dist/media.d.ts +1 -1
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +10 -13
- package/dist/memory.d.ts +7 -3
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +58 -27
- package/dist/messages.d.ts +10 -6
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +26 -36
- 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 +95 -135
- package/dist/observation.d.ts +3 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +22 -21
- 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 +7 -10
- package/dist/outcome.d.ts +10 -1
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +23 -2
- package/dist/overlay.d.ts +5 -1
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +36 -21
- 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/paths.d.ts +1 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +22 -0
- package/dist/preparation.d.ts.map +1 -1
- package/dist/preparation.js +25 -24
- package/dist/presence.d.ts +52 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +220 -0
- package/dist/queries.d.ts +1 -1
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +24 -14
- package/dist/query.d.ts +0 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/questions.d.ts +2 -5
- package/dist/questions.d.ts.map +1 -1
- package/dist/questions.js +3 -6
- package/dist/queue.d.ts +40 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +450 -0
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +68 -23
- package/dist/react.d.ts +11 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +33 -1
- package/dist/readiness.d.ts +1 -2
- package/dist/readiness.d.ts.map +1 -1
- package/dist/reads.d.ts +4 -1
- package/dist/reads.d.ts.map +1 -1
- package/dist/reads.js +47 -5
- 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 -10
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +15 -16
- 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 +4 -6
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +48 -59
- package/dist/room-drafts.d.ts.map +1 -1
- package/dist/room-drafts.js +17 -16
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +3 -4
- package/dist/runtime.d.ts +11 -19
- package/dist/runtime.d.ts.map +1 -1
- package/dist/save.d.ts +42 -86
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +333 -163
- package/dist/saves.d.ts +2 -40
- package/dist/saves.d.ts.map +1 -1
- package/dist/saves.js +109 -703
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +2 -3
- package/dist/shared.d.ts +1 -6
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +13 -95
- package/dist/shown.d.ts +6 -4
- package/dist/shown.d.ts.map +1 -1
- package/dist/shown.js +43 -35
- 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 +0 -7
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +5 -4
- package/dist/store/records.d.ts +11 -8
- package/dist/store/records.d.ts.map +1 -1
- package/dist/store/records.js +43 -12
- 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 +12 -7
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +38 -13
- package/dist/stored.d.ts +2 -1
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +27 -15
- package/dist/surface.d.ts +7 -4
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +30 -25
- package/dist/sync.d.ts +0 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +93 -89
- package/dist/tables.d.ts +19 -11
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +58 -21
- 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 +2 -2
- 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 -43
- 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 { apply, ARRAY_INDEX, ATTACHMENT, links, parse, pointerSegments, references, relative, relativize, retarget, roomReferences, serialize, target, } from '@agentshouse/mdmodel';
|
|
1
|
+
import { apply, ARRAY_INDEX, ATTACHMENT, create, equalValue, links, parse, pointerSegments, references, relative, relativize, retarget, roomReferences, serialize, target, } from '@agentshouse/mdmodel';
|
|
3
2
|
import { immutable, leaving, queuedFile, departure, } from './change.js';
|
|
4
|
-
import { collectionOf, documentsOf, governing, } from './collection.js';
|
|
5
|
-
import { changeBatchIdentity } from './identity.js';
|
|
6
|
-
import { overlaying } from './overlay.js';
|
|
7
|
-
import { ancestors
|
|
8
|
-
import { sorting } from './shared.js';
|
|
3
|
+
import { collectionOf, definitionsAt, documentsOf, governing, } from './collection.js';
|
|
4
|
+
import { changeBatchIdentity, roomDraftRevision } from './identity.js';
|
|
5
|
+
import { composed, overlaying } from './overlay.js';
|
|
6
|
+
import { ancestors } from './paths.js';
|
|
7
|
+
import { entriesIn, sorting } from './shared.js';
|
|
8
|
+
import { changedParts, changesOf, moveTarget, 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,15 +46,22 @@ 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 edited(document, changes) {
|
|
55
|
+
function edited(document, changes, definition) {
|
|
55
56
|
const parts = new Map();
|
|
56
57
|
for (const change of changes) {
|
|
58
|
+
if (change.kind === 'field')
|
|
59
|
+
parts.set(`field ${change.name}`, {
|
|
60
|
+
kind: 'field',
|
|
61
|
+
name: change.name,
|
|
62
|
+
value: change.value,
|
|
63
|
+
prior: document.fields[change.name],
|
|
64
|
+
});
|
|
57
65
|
if (change.kind === 'preamble')
|
|
58
66
|
parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
|
|
59
67
|
if (change.kind === 'section')
|
|
@@ -66,11 +74,16 @@ function edited(document, changes) {
|
|
|
66
74
|
}
|
|
67
75
|
if (parts.size === 0)
|
|
68
76
|
return document;
|
|
69
|
-
const applied = apply(document, [...parts.values()]);
|
|
77
|
+
const applied = apply(document, [...parts.values()], definition);
|
|
70
78
|
return applied.outcome === 'applied' ? applied.document : document;
|
|
71
79
|
}
|
|
72
80
|
function bodies(path, before, after) {
|
|
73
81
|
const changes = [];
|
|
82
|
+
for (const [name, value] of Object.entries(after.fields)) {
|
|
83
|
+
if (equalValue(before.fields[name], value))
|
|
84
|
+
continue;
|
|
85
|
+
changes.push({ kind: 'field', path, name, value });
|
|
86
|
+
}
|
|
74
87
|
if (after.preamble !== before.preamble) {
|
|
75
88
|
changes.push({ kind: 'preamble', path, value: after.preamble });
|
|
76
89
|
}
|
|
@@ -81,7 +94,7 @@ function bodies(path, before, after) {
|
|
|
81
94
|
}
|
|
82
95
|
return changes;
|
|
83
96
|
}
|
|
84
|
-
function relinks(path, moved, before, moves) {
|
|
97
|
+
function relinks(path, moved, before, moves, definition) {
|
|
85
98
|
const changes = [];
|
|
86
99
|
for (const link of links(before, path)) {
|
|
87
100
|
const destination = moves.get(link.target) ?? link.target;
|
|
@@ -94,18 +107,9 @@ function relinks(path, moved, before, moves) {
|
|
|
94
107
|
}
|
|
95
108
|
if (changes.length === 0)
|
|
96
109
|
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
|
-
}
|
|
110
|
+
const applied = apply(before, changes, definition);
|
|
111
|
+
if (applied.outcome === 'refuse')
|
|
112
|
+
return refusedBy(applied.refusal, path);
|
|
109
113
|
const after = applied.document;
|
|
110
114
|
if (after === before)
|
|
111
115
|
return { kind: 'changes', changes: [] };
|
|
@@ -115,7 +119,18 @@ function relinks(path, moved, before, moves) {
|
|
|
115
119
|
return { kind: 'changes', changes: bodies(path, before, after) };
|
|
116
120
|
}
|
|
117
121
|
function partKey(change) {
|
|
118
|
-
return change.kind === 'section'
|
|
122
|
+
return change.kind === 'section' || change.kind === 'field'
|
|
123
|
+
? `${change.kind} ${change.name}`
|
|
124
|
+
: change.kind;
|
|
125
|
+
}
|
|
126
|
+
const NAMES_A_SKIP_TESTS = 64;
|
|
127
|
+
const ENCODED = /[%&\\]/u;
|
|
128
|
+
function movedNames(moves) {
|
|
129
|
+
const names = new Set([...moves.keys()].map((path) => path.slice(path.lastIndexOf('/') + 1)));
|
|
130
|
+
return names.size > NAMES_A_SKIP_TESTS ? null : [...names];
|
|
131
|
+
}
|
|
132
|
+
function cannotLink(text, names) {
|
|
133
|
+
return names !== null && !ENCODED.test(text) && !names.some((name) => text.includes(name));
|
|
119
134
|
}
|
|
120
135
|
function* rewrites(changes, visible) {
|
|
121
136
|
const moves = new Map();
|
|
@@ -142,17 +157,20 @@ function* rewrites(changes, visible) {
|
|
|
142
157
|
}
|
|
143
158
|
if (moves.size === 0)
|
|
144
159
|
return { kind: 'changes', changes };
|
|
160
|
+
const names = movedNames(moves);
|
|
145
161
|
const merged = [...changes];
|
|
146
162
|
const added = [];
|
|
147
163
|
for (const [path, document] of yield* documentsOf(visible.collection)) {
|
|
148
164
|
if (settled.has(path))
|
|
149
165
|
continue;
|
|
150
166
|
const moved = moves.get(path) ?? path;
|
|
151
|
-
const definition =
|
|
167
|
+
const definition = definitionsAt(visible.collection.composition, path)(document.type);
|
|
152
168
|
const repointing = repointed(document, definition, path, moved, moves, settled);
|
|
153
169
|
added.push(...repointing);
|
|
154
170
|
const own = parts.get(path) ?? [];
|
|
155
|
-
const relinked =
|
|
171
|
+
const relinked = moved === path && own.length === 0 && cannotLink(document.content, names)
|
|
172
|
+
? { kind: 'changes', changes: [] }
|
|
173
|
+
: relinks(path, moved, edited(document, own.map((index) => merged[index]), definition), moves, definition);
|
|
156
174
|
if (relinked.kind === 'refused')
|
|
157
175
|
return relinked;
|
|
158
176
|
const keyed = new Map();
|
|
@@ -165,7 +183,7 @@ function* rewrites(changes, visible) {
|
|
|
165
183
|
if (change.kind === 'replace') {
|
|
166
184
|
for (const index of own) {
|
|
167
185
|
const kind = merged[index]?.kind;
|
|
168
|
-
if (kind === 'section' || kind === 'preamble')
|
|
186
|
+
if (kind === 'field' || kind === 'section' || kind === 'preamble')
|
|
169
187
|
merged[index] = null;
|
|
170
188
|
}
|
|
171
189
|
added.push(change);
|
|
@@ -221,25 +239,21 @@ function crossed(document, definition, path, to) {
|
|
|
221
239
|
}
|
|
222
240
|
return changes;
|
|
223
241
|
}
|
|
224
|
-
function rewritten(path, document, changes) {
|
|
242
|
+
function rewritten(path, document, changes, definition) {
|
|
225
243
|
if (changes.length === 0)
|
|
226
244
|
return [];
|
|
227
|
-
const applied = apply(document, changes);
|
|
245
|
+
const applied = apply(document, changes, definition);
|
|
228
246
|
if (applied.outcome !== 'applied')
|
|
229
247
|
return [];
|
|
230
248
|
const after = applied.document;
|
|
231
249
|
if (document.type === undefined)
|
|
232
250
|
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
|
-
];
|
|
251
|
+
return bodies(path, document, after);
|
|
238
252
|
}
|
|
239
253
|
function roomPathOf(handle, path) {
|
|
240
254
|
return `/rooms/${handle}/${path}`;
|
|
241
255
|
}
|
|
242
|
-
|
|
256
|
+
function* leavingRewritten(batch, visible) {
|
|
243
257
|
const between = batch.between;
|
|
244
258
|
if (between === undefined)
|
|
245
259
|
return batch;
|
|
@@ -248,8 +262,8 @@ export function* leavingRewritten(batch, visible) {
|
|
|
248
262
|
for (const [path, document] of yield* documentsOf(visible.collection)) {
|
|
249
263
|
if (moved.has(path))
|
|
250
264
|
continue;
|
|
251
|
-
const definition =
|
|
252
|
-
derived.push(...rewritten(path, document, crossed(document, definition, path, (named) => moved.get(named))));
|
|
265
|
+
const definition = definitionsAt(visible.collection.composition, path)(document.type);
|
|
266
|
+
derived.push(...rewritten(path, document, crossed(document, definition, path, (named) => moved.get(named)), definition));
|
|
253
267
|
yield ROW_WEIGHT + visible.collection.text[path].length;
|
|
254
268
|
}
|
|
255
269
|
if (derived.length === 0)
|
|
@@ -291,7 +305,7 @@ function* incoming(collection, removed, candidates) {
|
|
|
291
305
|
for (const [path, document] of candidates === null
|
|
292
306
|
? yield* documentsOf(collection)
|
|
293
307
|
: read(candidates)) {
|
|
294
|
-
const definition =
|
|
308
|
+
const definition = definitionsAt(collection.composition, path)(document.type);
|
|
295
309
|
for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
|
|
296
310
|
if (to.includes(ATTACHMENT))
|
|
297
311
|
continue;
|
|
@@ -334,15 +348,9 @@ function guard(changes, input, removing) {
|
|
|
334
348
|
const occupied = (path) => input.folders.has(path) ? path : (ancestors(path).find((one) => existing.has(one)) ?? null);
|
|
335
349
|
for (const [index, change] of changes.entries()) {
|
|
336
350
|
for (const path of affected(change)) {
|
|
337
|
-
const
|
|
338
|
-
if (
|
|
339
|
-
return refuse(
|
|
340
|
-
if (covered(input.providerOwnedPaths, normalized)) {
|
|
341
|
-
return refuse('provider-owned-path', normalized);
|
|
342
|
-
}
|
|
343
|
-
for (const operation of change.kind === 'remove' ? removing : WRITE)
|
|
344
|
-
if (!input.local && !covered(input.operations[operation], normalized))
|
|
345
|
-
return refuse(`${operation}-grant`, normalized);
|
|
351
|
+
const refused = input.rule(path, change.kind === 'remove' ? removing : WRITE);
|
|
352
|
+
if (refused !== null)
|
|
353
|
+
return refuse(refused.code, refused.path);
|
|
346
354
|
}
|
|
347
355
|
if (change.kind === 'create' || change.kind === 'arrival') {
|
|
348
356
|
const taken = existing.has(change.path) ? change.path : occupied(change.path);
|
|
@@ -403,9 +411,9 @@ function guard(changes, input, removing) {
|
|
|
403
411
|
return null;
|
|
404
412
|
}
|
|
405
413
|
function unrevised(input, path) {
|
|
406
|
-
return
|
|
414
|
+
return !input.local && !input.queued.has(path) && input.visible.revisions[path] === undefined;
|
|
407
415
|
}
|
|
408
|
-
|
|
416
|
+
function queuedDocuments(queue) {
|
|
409
417
|
const paths = new Set();
|
|
410
418
|
for (const batch of queue) {
|
|
411
419
|
for (const change of batch.changes) {
|
|
@@ -541,56 +549,69 @@ function* exceeding(limits, changes, effective, resulting) {
|
|
|
541
549
|
function shellRelative(text, path, handle, definition) {
|
|
542
550
|
if (!text.includes(`/rooms/${handle}/`))
|
|
543
551
|
return null;
|
|
544
|
-
const parsed = parse(text, path);
|
|
552
|
+
const parsed = parse(text, path, definition);
|
|
545
553
|
if (!parsed.ok)
|
|
546
554
|
return null;
|
|
547
555
|
const result = relativize(parsed.document, path, handle, definition(parsed.document.type));
|
|
548
556
|
return result.outcome === 'applied' && result.document.content !== text ? result.document : null;
|
|
549
557
|
}
|
|
550
558
|
function relativeField(path, name, value, handle, definition) {
|
|
551
|
-
|
|
552
|
-
if (definition === undefined ||
|
|
553
|
-
!opened.ok ||
|
|
554
|
-
!JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
559
|
+
if (definition === undefined || !JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
555
560
|
return value;
|
|
556
|
-
const held = apply(
|
|
561
|
+
const held = apply(create(definition.name, definition), [{ kind: 'field', name, value, prior: undefined }], definition);
|
|
557
562
|
if (held.outcome !== 'applied')
|
|
558
563
|
return value;
|
|
559
564
|
const result = relativize(held.document, path, handle, definition);
|
|
560
565
|
return result.outcome === 'applied' ? result.document.fields[name] : value;
|
|
561
566
|
}
|
|
562
567
|
function relativeChanges(input) {
|
|
568
|
+
const changes = [];
|
|
569
|
+
for (const change of input.changes) {
|
|
570
|
+
const next = relativeChange(change, input);
|
|
571
|
+
if ('refusal' in next)
|
|
572
|
+
return next;
|
|
573
|
+
changes.push(next.change);
|
|
574
|
+
}
|
|
575
|
+
return { kind: 'changes', changes };
|
|
576
|
+
}
|
|
577
|
+
function relativeChange(change, input) {
|
|
563
578
|
const { handle, composition, visible } = input;
|
|
564
|
-
const typed = (path) => (type) => definitionOf(governing(composition, path), type);
|
|
565
579
|
const body = (path, text) => shellRelative(text, path, handle, () => undefined)?.content ?? text;
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
580
|
+
const definition = definitionsAt(composition, change.path);
|
|
581
|
+
if (change.kind === 'field') {
|
|
582
|
+
const type = visible.collection.document(change.path)?.type;
|
|
583
|
+
const value = relativeField(change.path, change.name, change.value, handle, definition(type));
|
|
584
|
+
return { change: value === change.value ? change : { ...change, value } };
|
|
585
|
+
}
|
|
586
|
+
if (change.kind === 'section' || change.kind === 'preamble') {
|
|
587
|
+
const value = body(change.path, change.value);
|
|
588
|
+
return { change: value === change.value ? change : { ...change, value } };
|
|
589
|
+
}
|
|
590
|
+
if (change.kind === 'replace' || (change.kind === 'create' && 'content' in change)) {
|
|
591
|
+
const content = shellRelative(change.content, change.path, handle, definition)?.content ?? change.content;
|
|
592
|
+
return { change: content === change.content ? change : { ...change, content } };
|
|
593
|
+
}
|
|
594
|
+
if (change.kind !== 'create')
|
|
595
|
+
return { change };
|
|
596
|
+
const type = typeof change.fields.type === 'string' ? change.fields.type : undefined;
|
|
597
|
+
const fields = Object.fromEntries(Object.entries(change.fields).map(([name, value]) => [
|
|
598
|
+
name,
|
|
599
|
+
relativeField(change.path, name, value, handle, definition(type)),
|
|
600
|
+
]));
|
|
601
|
+
const sections = Object.fromEntries(Object.entries(change.sections).map(([name, value]) => [name, body(change.path, value)]));
|
|
602
|
+
const written = composed(change.path, fields, sections, composition);
|
|
603
|
+
if (written.outcome === 'refuse')
|
|
604
|
+
return refusedBy(written.refusal, change.path);
|
|
605
|
+
if (written.outcome === 'stale')
|
|
606
|
+
throw new Error(`the creation of ${change.path} is stale`);
|
|
607
|
+
if (Object.keys(sections).length === 0)
|
|
608
|
+
return { change: { ...change, fields, sections } };
|
|
609
|
+
return {
|
|
610
|
+
change: { kind: 'create', path: change.path, content: serialize(written.document) },
|
|
611
|
+
};
|
|
591
612
|
}
|
|
592
613
|
function unqueueable(file) {
|
|
593
|
-
return
|
|
614
|
+
return refuse('unknown-revision', file);
|
|
594
615
|
}
|
|
595
616
|
function isPart(change) {
|
|
596
617
|
return change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble';
|
|
@@ -598,8 +619,29 @@ function isPart(change) {
|
|
|
598
619
|
function touched(change) {
|
|
599
620
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
600
621
|
}
|
|
601
|
-
function partsApplied(content, path, changes) {
|
|
602
|
-
const
|
|
622
|
+
function partsApplied(content, path, changes, composition) {
|
|
623
|
+
const applied = partsResult(content, path, changes, composition);
|
|
624
|
+
return applied?.outcome === 'applied' ? serialize(applied.document) : null;
|
|
625
|
+
}
|
|
626
|
+
function unapplied(changes, visible, composition) {
|
|
627
|
+
const parted = new Map();
|
|
628
|
+
for (const change of changes) {
|
|
629
|
+
if (isPart(change))
|
|
630
|
+
parted.set(change.path, [...(parted.get(change.path) ?? []), change]);
|
|
631
|
+
}
|
|
632
|
+
for (const [path, parts] of parted) {
|
|
633
|
+
const content = visible.documents[path];
|
|
634
|
+
if (content === undefined)
|
|
635
|
+
continue;
|
|
636
|
+
const applied = partsResult(content, path, parts, composition);
|
|
637
|
+
if (applied?.outcome === 'refuse')
|
|
638
|
+
return refusedBy(applied.refusal, path);
|
|
639
|
+
}
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
function partsResult(content, path, changes, composition) {
|
|
643
|
+
const definitions = definitionsAt(composition, path);
|
|
644
|
+
const parsed = parse(content, path, definitions);
|
|
603
645
|
if (!parsed.ok)
|
|
604
646
|
return null;
|
|
605
647
|
const document = parsed.document;
|
|
@@ -622,12 +664,11 @@ function partsApplied(content, path, changes) {
|
|
|
622
664
|
else
|
|
623
665
|
parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
|
|
624
666
|
}
|
|
625
|
-
|
|
626
|
-
return applied.outcome === 'applied' ? serialize(applied.document) : null;
|
|
667
|
+
return apply(document, [...parts.values()], definitions(document.type));
|
|
627
668
|
}
|
|
628
|
-
function settledParts(changes, deferred) {
|
|
669
|
+
function settledParts(changes, deferred, composition) {
|
|
629
670
|
for (const [held, parts] of deferred) {
|
|
630
|
-
const content = partsApplied(held.content, held.path, parts);
|
|
671
|
+
const content = partsApplied(held.content, held.path, parts, composition);
|
|
631
672
|
if (content === null)
|
|
632
673
|
return held.path;
|
|
633
674
|
changes[changes.indexOf(held)] = { ...held, content };
|
|
@@ -702,7 +743,9 @@ function* relinked(batch, move, before, after, appending) {
|
|
|
702
743
|
for (const change of derived.changes) {
|
|
703
744
|
if (change === move)
|
|
704
745
|
continue;
|
|
705
|
-
const unsettled = isPart(change)
|
|
746
|
+
const unsettled = isPart(change)
|
|
747
|
+
? null
|
|
748
|
+
: settledParts(changes, deferred, after.collection.composition);
|
|
706
749
|
if (unsettled !== null)
|
|
707
750
|
return unqueueable(unsettled);
|
|
708
751
|
const outcome = folded(changes, change, appending, deferred);
|
|
@@ -713,7 +756,7 @@ function* relinked(batch, move, before, after, appending) {
|
|
|
713
756
|
if (outcome !== 'skipped')
|
|
714
757
|
changed = true;
|
|
715
758
|
}
|
|
716
|
-
const unsettled = settledParts(changes, deferred);
|
|
759
|
+
const unsettled = settledParts(changes, deferred, after.collection.composition);
|
|
717
760
|
if (unsettled !== null)
|
|
718
761
|
return unqueueable(unsettled);
|
|
719
762
|
if (!changed)
|
|
@@ -774,11 +817,10 @@ function withdrawnFile(batch, change, at) {
|
|
|
774
817
|
];
|
|
775
818
|
return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
|
|
776
819
|
}
|
|
777
|
-
|
|
820
|
+
function* requeuing(queue, changes, visible, changeable, state) {
|
|
778
821
|
const next = [...queue];
|
|
779
822
|
const origins = new Map();
|
|
780
823
|
const withdrawn = new Map();
|
|
781
|
-
const owners = new Set();
|
|
782
824
|
const queued = new Set();
|
|
783
825
|
const kept = [];
|
|
784
826
|
const originOf = (batch) => origins.get(batch) ?? batch.id;
|
|
@@ -824,7 +866,6 @@ export function* requeuing(queue, changes, visible, changeable, state) {
|
|
|
824
866
|
if (replaced !== null)
|
|
825
867
|
origins.set(replaced, origin);
|
|
826
868
|
}
|
|
827
|
-
owners.add(originOf(owner));
|
|
828
869
|
next.splice(index, following.length, ...rebuilt.filter((batch) => batch !== null));
|
|
829
870
|
queued.add(change.path);
|
|
830
871
|
}
|
|
@@ -834,56 +875,13 @@ export function* requeuing(queue, changes, visible, changeable, state) {
|
|
|
834
875
|
changes: kept,
|
|
835
876
|
queued,
|
|
836
877
|
withdrawn,
|
|
837
|
-
owners,
|
|
838
878
|
};
|
|
839
879
|
}
|
|
840
|
-
function
|
|
841
|
-
return precondition.kind === 'field' ||
|
|
842
|
-
precondition.kind === 'section' ||
|
|
843
|
-
precondition.kind === 'preamble'
|
|
844
|
-
? identity(precondition)
|
|
845
|
-
: `path ${precondition.path}`;
|
|
846
|
-
}
|
|
847
|
-
export function joined(queue, owner, extra) {
|
|
848
|
-
const later = new Set(queue.slice(queue.indexOf(owner) + 1).flatMap((batch) => batch.changes.flatMap(touched)));
|
|
849
|
-
const clash = extra.changes.flatMap(touched).find((path) => later.has(path));
|
|
850
|
-
if (clash !== undefined)
|
|
851
|
-
return unqueueable(clash);
|
|
852
|
-
const changes = [...owner.changes];
|
|
853
|
-
const deferred = new Map();
|
|
854
|
-
for (const change of extra.changes) {
|
|
855
|
-
const unsettled = isPart(change) ? null : settledParts(changes, deferred);
|
|
856
|
-
if (unsettled !== null)
|
|
857
|
-
return unqueueable(unsettled);
|
|
858
|
-
if (folded(changes, change, true, deferred) === 'refused')
|
|
859
|
-
return unqueueable(change.path);
|
|
860
|
-
}
|
|
861
|
-
const unsettled = settledParts(changes, deferred);
|
|
862
|
-
if (unsettled !== null)
|
|
863
|
-
return unqueueable(unsettled);
|
|
864
|
-
const held = new Set(owner.preconditions.map(covering));
|
|
865
|
-
const written = new Set(owner.changes.flatMap((change) => change.kind === 'create' || change.kind === 'replace' ? [change.path] : []));
|
|
866
|
-
const preconditions = [
|
|
867
|
-
...owner.preconditions,
|
|
868
|
-
...extra.preconditions.filter((precondition) => {
|
|
869
|
-
const key = covering(precondition);
|
|
870
|
-
return (!held.has(key) && (key === `path ${precondition.path}` || !written.has(precondition.path)));
|
|
871
|
-
}),
|
|
872
|
-
];
|
|
873
|
-
const batch = immutable({
|
|
874
|
-
...owner,
|
|
875
|
-
...(extra.validation === undefined ? {} : { validation: extra.validation }),
|
|
876
|
-
id: changeBatchIdentity(),
|
|
877
|
-
changes,
|
|
878
|
-
preconditions,
|
|
879
|
-
});
|
|
880
|
-
return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
|
|
881
|
-
}
|
|
882
|
-
export function* limiting(queue, batch, limits, state) {
|
|
880
|
+
function* limiting(queue, batch, limits, state) {
|
|
883
881
|
const at = queue.indexOf(batch);
|
|
884
882
|
const rewritten = yield* providerRewritten(batch, yield* state(queue.slice(0, at)));
|
|
885
883
|
const limited = yield* exceeding(limits, batch.changes, rewritten.changes, yield* state(queue.slice(0, at + 1)));
|
|
886
|
-
return limited?.
|
|
884
|
+
return limited?.refusal ?? null;
|
|
887
885
|
}
|
|
888
886
|
function typedText(content) {
|
|
889
887
|
if (content === undefined)
|
|
@@ -900,7 +898,7 @@ function touchedLibrary(resulting, touched, composition) {
|
|
|
900
898
|
})
|
|
901
899
|
.sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0));
|
|
902
900
|
const contents = new Map(entries.map(({ path, content }) => [path, content]));
|
|
903
|
-
const binaries = new Set(
|
|
901
|
+
const binaries = new Set(Array.from(entriesIn(resulting.binaries), ([path]) => path));
|
|
904
902
|
return validatedCollection(() => Promise.resolve({ entries, next: null }), async (paths) => {
|
|
905
903
|
const known = new Map();
|
|
906
904
|
const pace = paced();
|
|
@@ -909,7 +907,7 @@ function touchedLibrary(resulting, touched, composition) {
|
|
|
909
907
|
if (binaries.has(path))
|
|
910
908
|
known.set(path, { type: undefined, binary: true });
|
|
911
909
|
else if (content !== undefined && content !== null) {
|
|
912
|
-
const read = parse(content, path);
|
|
910
|
+
const read = parse(content, path, definitionsAt(composition, path));
|
|
913
911
|
if (read.ok)
|
|
914
912
|
known.set(path, { type: read.document.type ?? 'any', binary: false });
|
|
915
913
|
await pace(content.length + ROW_WEIGHT);
|
|
@@ -947,6 +945,7 @@ async function judging(visible, resulting, composition, validate, removed, touch
|
|
|
947
945
|
retryable: false,
|
|
948
946
|
file: verdict.file,
|
|
949
947
|
...(verdict.location === undefined ? {} : { location: verdict.location }),
|
|
948
|
+
...(verdict.line === undefined ? {} : { line: verdict.line }),
|
|
950
949
|
...(verdict.referrers === undefined ? {} : { referrers: verdict.referrers }),
|
|
951
950
|
},
|
|
952
951
|
};
|
|
@@ -962,8 +961,6 @@ function placed(reference) {
|
|
|
962
961
|
const key = roomPathOf(reference.handle, reference.path);
|
|
963
962
|
if (reference.kind === 'link')
|
|
964
963
|
return `link ${key}`;
|
|
965
|
-
if (reference.kind === 'item')
|
|
966
|
-
return `section ${reference.section} ${key}`;
|
|
967
964
|
const pointer = reference.pointer
|
|
968
965
|
.split('/')
|
|
969
966
|
.map((segment) => (ARRAY_INDEX.test(segment) ? '-' : segment))
|
|
@@ -973,10 +970,11 @@ function placed(reference) {
|
|
|
973
970
|
function roomReferencesIn(path, content, composition) {
|
|
974
971
|
if (content === undefined)
|
|
975
972
|
return [];
|
|
976
|
-
const
|
|
973
|
+
const definitions = definitionsAt(composition, path);
|
|
974
|
+
const parsed = parse(content, path, definitions);
|
|
977
975
|
if (!parsed.ok)
|
|
978
976
|
return [];
|
|
979
|
-
return roomReferences(parsed.document,
|
|
977
|
+
return roomReferences(parsed.document, definitions(parsed.document.type));
|
|
980
978
|
}
|
|
981
979
|
function* admittedRooms(changes, visible, resulting, composition) {
|
|
982
980
|
const from = new Map();
|
|
@@ -994,7 +992,7 @@ function* admittedRooms(changes, visible, resulting, composition) {
|
|
|
994
992
|
continue;
|
|
995
993
|
if (held.has(placed(reference)))
|
|
996
994
|
continue;
|
|
997
|
-
return refuse('unresolved-reference', path, reference.
|
|
995
|
+
return refuse('unresolved-reference', path, reference.pointer);
|
|
998
996
|
}
|
|
999
997
|
yield ROW_WEIGHT + now.length;
|
|
1000
998
|
}
|
|
@@ -1012,7 +1010,10 @@ function validationOf(validated) {
|
|
|
1012
1010
|
};
|
|
1013
1011
|
}
|
|
1014
1012
|
async function batching(input) {
|
|
1015
|
-
const
|
|
1013
|
+
const relative = relativeChanges(input);
|
|
1014
|
+
if (relative.kind === 'refused')
|
|
1015
|
+
return relative;
|
|
1016
|
+
const { changes } = relative;
|
|
1016
1017
|
const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
|
|
1017
1018
|
if (structural !== undefined && input.unmodeled) {
|
|
1018
1019
|
return refuse('model-unavailable', structural.path);
|
|
@@ -1022,12 +1023,12 @@ async function batching(input) {
|
|
|
1022
1023
|
const rewritten = await stepped(rewrites(changes, input.visible));
|
|
1023
1024
|
if (rewritten.kind === 'refused')
|
|
1024
1025
|
return rewritten;
|
|
1025
|
-
const refusal = guard(changes, input, WRITE);
|
|
1026
|
+
const refusal = guard(changes, input, WRITE) ?? unapplied(changes, input.visible, input.composition);
|
|
1026
1027
|
if (refusal !== null)
|
|
1027
1028
|
return refusal;
|
|
1028
1029
|
const preconditions = await stepped(preconditionsOf(changes, input.visible, input.bases));
|
|
1029
1030
|
const predicted = await stepped(anticipated(changes, preconditions, rewritten.changes, input.visible));
|
|
1030
|
-
const resulting = await stepped(overlaying(input.visible, [{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted }]));
|
|
1031
|
+
const resulting = await stepped(overlaying(input.visible, [{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted }], input.composition));
|
|
1031
1032
|
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))]);
|
|
1032
1033
|
if (judged.kind === 'refused')
|
|
1033
1034
|
return judged;
|
|
@@ -1049,7 +1050,7 @@ async function batching(input) {
|
|
|
1049
1050
|
}),
|
|
1050
1051
|
};
|
|
1051
1052
|
}
|
|
1052
|
-
|
|
1053
|
+
const MOVE_DOCUMENTS = 'move-documents';
|
|
1053
1054
|
function heldAt(visible, path) {
|
|
1054
1055
|
return visible.documents[path] !== undefined || visible.binaries[path] !== undefined;
|
|
1055
1056
|
}
|
|
@@ -1058,7 +1059,7 @@ function arrivingText(path, to, source, moved) {
|
|
|
1058
1059
|
const document = source.visible.collection.document(path);
|
|
1059
1060
|
if (document === undefined)
|
|
1060
1061
|
return content;
|
|
1061
|
-
const definition =
|
|
1062
|
+
const definition = definitionsAt(source.composition, path)(document.type);
|
|
1062
1063
|
const changes = crossed(document, definition, path, (named, href) => {
|
|
1063
1064
|
const at = moved.get(named);
|
|
1064
1065
|
if (at !== undefined)
|
|
@@ -1067,7 +1068,7 @@ function arrivingText(path, to, source, moved) {
|
|
|
1067
1068
|
});
|
|
1068
1069
|
if (changes.length === 0)
|
|
1069
1070
|
return content;
|
|
1070
|
-
const applied = apply(document, changes);
|
|
1071
|
+
const applied = apply(document, changes, definition);
|
|
1071
1072
|
return applied.outcome === 'applied' ? serialize(applied.document) : content;
|
|
1072
1073
|
}
|
|
1073
1074
|
function arrivalOf({ path, to }, source, moved) {
|
|
@@ -1101,8 +1102,6 @@ function arrivalOf({ path, to }, source, moved) {
|
|
|
1101
1102
|
}
|
|
1102
1103
|
async function moving(input) {
|
|
1103
1104
|
const { source, destination, moves } = input;
|
|
1104
|
-
if (moves.length === 0)
|
|
1105
|
-
return refuse('no-change');
|
|
1106
1105
|
const removals = moves.map(({ path }) => ({ kind: 'remove', path }));
|
|
1107
1106
|
const leaves = guard(removals, { ...source, local: input.local, bases: [] }, MOVE_OUT);
|
|
1108
1107
|
if (leaves !== null)
|
|
@@ -1141,7 +1140,7 @@ async function moving(input) {
|
|
|
1141
1140
|
between,
|
|
1142
1141
|
};
|
|
1143
1142
|
const effective = await stepped(leavingRewritten(leavingBatch, source.visible));
|
|
1144
|
-
const left = await stepped(overlaying(source.visible, [effective]));
|
|
1143
|
+
const left = await stepped(overlaying(source.visible, [effective], source.composition));
|
|
1145
1144
|
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))]);
|
|
1146
1145
|
if (sourceJudged.kind === 'refused')
|
|
1147
1146
|
return sourceJudged;
|
|
@@ -1153,7 +1152,7 @@ async function moving(input) {
|
|
|
1153
1152
|
...written,
|
|
1154
1153
|
between,
|
|
1155
1154
|
};
|
|
1156
|
-
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch]));
|
|
1155
|
+
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch], destination.composition));
|
|
1157
1156
|
const destinationJudged = await judging(destination.visible, arrived, destination.composition, destination.validate, new Set(), moves.map(({ to }) => to));
|
|
1158
1157
|
if (destinationJudged.kind === 'refused')
|
|
1159
1158
|
return destinationJudged;
|
|
@@ -1163,9 +1162,180 @@ async function moving(input) {
|
|
|
1163
1162
|
return limited;
|
|
1164
1163
|
return { kind: 'moved', leaving: immutable(leavingBatch), arriving: immutable(arrivingBatch) };
|
|
1165
1164
|
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1165
|
+
function unreadable(saved, visible, composition) {
|
|
1166
|
+
const path = saved.find(({ path: at, parts }) => (parts.fields !== undefined ||
|
|
1167
|
+
parts.sections !== undefined ||
|
|
1168
|
+
parts.preamble !== undefined) &&
|
|
1169
|
+
typedText(visible.documents[at]) &&
|
|
1170
|
+
governing(composition, at).status === 'unavailable')?.path;
|
|
1171
|
+
return path === undefined ? null : refuse('model-unavailable', path);
|
|
1172
|
+
}
|
|
1173
|
+
function changedUnderneath(saved, visible) {
|
|
1174
|
+
const parts = saved.flatMap(({ path, parts: held }) => changedParts(path, held, visible));
|
|
1175
|
+
return parts.length === 0
|
|
1176
|
+
? null
|
|
1177
|
+
: { kind: 'refused', refusal: { code: 'changed-underneath', retryable: false, parts } };
|
|
1178
|
+
}
|
|
1179
|
+
function sideOf(room, visible) {
|
|
1180
|
+
const { record } = room;
|
|
1181
|
+
return {
|
|
1182
|
+
room: record.locator,
|
|
1183
|
+
handle: record.declaration.handle,
|
|
1184
|
+
baseRoomVersion: record.roomVersion,
|
|
1185
|
+
composition: room.composition,
|
|
1186
|
+
rule: room.rule,
|
|
1187
|
+
visible,
|
|
1188
|
+
folders: room.folders,
|
|
1189
|
+
queued: queuedDocuments(room.queue),
|
|
1190
|
+
unmodeled: !room.modelled,
|
|
1191
|
+
validate: room.validate,
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
function carriedDrafts(ask, from, into) {
|
|
1195
|
+
return new Map(ask.saved.flatMap(({ path, parts }) => {
|
|
1196
|
+
const record = from.drafts.get(path);
|
|
1197
|
+
if (parts.move === undefined || record === undefined || ask.consumed.has(path))
|
|
1198
|
+
return [];
|
|
1199
|
+
const to = moveTarget(parts.move);
|
|
1200
|
+
if (into.drafts.has(to))
|
|
1201
|
+
return [];
|
|
1202
|
+
const locator = into.record.locator;
|
|
1203
|
+
return [[path, { ...record, locator, path: to, revision: roomDraftRevision() }]];
|
|
1204
|
+
}));
|
|
1205
|
+
}
|
|
1206
|
+
async function plannedMove(ask, source, into) {
|
|
1207
|
+
const destination = into.record.locator;
|
|
1208
|
+
const mixed = ask.saved.find(({ parts }) => Object.keys(parts).length !== 1 || parts.move?.room !== destination);
|
|
1209
|
+
if (mixed !== undefined)
|
|
1210
|
+
return refuse('mixed-room-move', mixed.path);
|
|
1211
|
+
const leavingVisible = await stepped(source.replay(source.queue));
|
|
1212
|
+
const changed = changedUnderneath(ask.saved, leavingVisible);
|
|
1213
|
+
if (changed !== null)
|
|
1214
|
+
return changed;
|
|
1215
|
+
const moves = ask.saved.map(({ path, parts }) => ({ path, to: moveTarget(parts.move) }));
|
|
1216
|
+
const built = await moving({
|
|
1217
|
+
source: sideOf(source, leavingVisible),
|
|
1218
|
+
destination: sideOf(into, await stepped(into.replay(into.queue))),
|
|
1219
|
+
moves,
|
|
1220
|
+
providerLimits: ask.providerLimits,
|
|
1221
|
+
local: ask.local,
|
|
1222
|
+
...(ask.writtenBy === undefined ? {} : { writtenBy: ask.writtenBy }),
|
|
1223
|
+
});
|
|
1224
|
+
if (built.kind === 'refused')
|
|
1225
|
+
return built;
|
|
1226
|
+
const { id } = built.leaving;
|
|
1227
|
+
const carried = carriedDrafts(ask, source, into);
|
|
1228
|
+
return {
|
|
1229
|
+
kind: 'planned',
|
|
1230
|
+
identifier: id,
|
|
1231
|
+
rooms: [
|
|
1232
|
+
{
|
|
1233
|
+
locator: source.record.locator,
|
|
1234
|
+
queue: [...source.queue, built.leaving],
|
|
1235
|
+
saved: new Map([[id, ask.saved]]),
|
|
1236
|
+
consumed: [...ask.consumed, ...carried.keys()],
|
|
1237
|
+
carried: [],
|
|
1238
|
+
},
|
|
1239
|
+
{
|
|
1240
|
+
locator: destination,
|
|
1241
|
+
queue: [...into.queue, built.arriving],
|
|
1242
|
+
saved: new Map([
|
|
1243
|
+
[id, [...carried.values()].map(({ path }) => ({ path, parts: {} }))],
|
|
1244
|
+
]),
|
|
1245
|
+
consumed: [],
|
|
1246
|
+
carried: [...carried.values()],
|
|
1247
|
+
},
|
|
1248
|
+
],
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
async function plannedWithin(ask, room) {
|
|
1252
|
+
const { record } = room;
|
|
1253
|
+
const visible = await stepped(room.replay(room.queue));
|
|
1254
|
+
const changed = unreadable(ask.saved, visible, room.composition) ?? changedUnderneath(ask.saved, visible);
|
|
1255
|
+
if (changed !== null)
|
|
1256
|
+
return changed;
|
|
1257
|
+
const parts = ask.saved.map(({ path, parts: held }) => changesOf(path, held));
|
|
1258
|
+
const changes = parts.flatMap(({ changes: made }) => made);
|
|
1259
|
+
const requeue = await stepped(requeuing(room.queue, changes, visible, (batch) => room.changeable.has(batch.id), room.replay));
|
|
1260
|
+
if (requeue.kind === 'refused')
|
|
1261
|
+
return requeue;
|
|
1262
|
+
const foreign = [...requeue.withdrawn.keys()].some((origin) => {
|
|
1263
|
+
const writer = room.queue.find((batch) => batch.id === origin)?.writtenBy;
|
|
1264
|
+
return writer?.app !== ask.writtenBy?.app || writer?.release !== ask.writtenBy?.release;
|
|
1265
|
+
});
|
|
1266
|
+
if (foreign)
|
|
1267
|
+
return unqueueable([...requeue.queued].at(-1));
|
|
1268
|
+
const input = {
|
|
1269
|
+
local: ask.local,
|
|
1270
|
+
baseRoomVersion: record.roomVersion,
|
|
1271
|
+
composition: room.composition,
|
|
1272
|
+
rule: room.rule,
|
|
1273
|
+
providerLimits: ask.providerLimits,
|
|
1274
|
+
handle: record.declaration.handle,
|
|
1275
|
+
visible,
|
|
1276
|
+
folders: room.folders,
|
|
1277
|
+
changes,
|
|
1278
|
+
bases: parts.flatMap(({ bases }) => bases),
|
|
1279
|
+
queued: queuedDocuments(room.queue),
|
|
1280
|
+
writtenBy: ask.writtenBy,
|
|
1281
|
+
unmodeled: !room.modelled,
|
|
1282
|
+
validate: room.validate,
|
|
1283
|
+
};
|
|
1284
|
+
const rewritten = requeue.withdrawn.size > 0;
|
|
1285
|
+
if (rewritten) {
|
|
1286
|
+
const whole = await batching({
|
|
1287
|
+
...input,
|
|
1288
|
+
providerLimits: [],
|
|
1289
|
+
queued: new Set([...input.queued, ...requeue.queued]),
|
|
1290
|
+
});
|
|
1291
|
+
if (whole.kind === 'refused')
|
|
1292
|
+
return whole;
|
|
1293
|
+
}
|
|
1294
|
+
const own = !rewritten
|
|
1295
|
+
? await batching(input)
|
|
1296
|
+
: requeue.changes.length === 0
|
|
1297
|
+
? null
|
|
1298
|
+
: await batching({
|
|
1299
|
+
...input,
|
|
1300
|
+
visible: await stepped(room.replay(requeue.queue)),
|
|
1301
|
+
changes: requeue.changes,
|
|
1302
|
+
queued: queuedDocuments(requeue.queue),
|
|
1303
|
+
});
|
|
1304
|
+
if (own?.kind === 'refused')
|
|
1305
|
+
return own;
|
|
1306
|
+
const queue = own === null ? requeue.queue : [...requeue.queue, own.batch];
|
|
1307
|
+
const saved = new Map();
|
|
1308
|
+
for (const [origin, rebuilt] of requeue.withdrawn) {
|
|
1309
|
+
if (rebuilt === null)
|
|
1310
|
+
continue;
|
|
1311
|
+
const limited = await stepped(limiting(queue, rebuilt, ask.providerLimits, room.replay));
|
|
1312
|
+
if (limited !== null)
|
|
1313
|
+
return { kind: 'refused', refusal: limited };
|
|
1314
|
+
saved.set(rebuilt.id, withdrawnDrafts(room.saved.get(origin) ?? [], changes));
|
|
1315
|
+
}
|
|
1316
|
+
if (own !== null) {
|
|
1317
|
+
saved.set(own.batch.id, ask.saved.filter(({ path }) => !requeue.queued.has(path)));
|
|
1318
|
+
}
|
|
1319
|
+
const carried = carriedDrafts(ask, room, room);
|
|
1320
|
+
return {
|
|
1321
|
+
kind: 'planned',
|
|
1322
|
+
identifier: own?.batch.id ??
|
|
1323
|
+
[...requeue.withdrawn].map(([origin, rebuilt]) => rebuilt?.id ?? origin).at(-1),
|
|
1324
|
+
rooms: [
|
|
1325
|
+
{
|
|
1326
|
+
locator: record.locator,
|
|
1327
|
+
queue,
|
|
1328
|
+
saved,
|
|
1329
|
+
consumed: [...ask.consumed, ...carried.keys()],
|
|
1330
|
+
carried: [...carried.values()],
|
|
1331
|
+
},
|
|
1332
|
+
],
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
export function destination(locator, saved) {
|
|
1336
|
+
const into = saved.find(({ parts }) => parts.move?.room !== undefined && parts.move.room !== locator);
|
|
1337
|
+
return into?.parts.move?.room ?? null;
|
|
1168
1338
|
}
|
|
1169
|
-
export function
|
|
1170
|
-
return
|
|
1339
|
+
export function planSave(ask, room, into) {
|
|
1340
|
+
return into === undefined ? plannedWithin(ask, room) : plannedMove(ask, room, into);
|
|
1171
1341
|
}
|