@agentshouse/mdruntime 0.1.0-cd26b960b7262 → 0.1.0-cd30853a1b15e
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 +72 -158
- 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 +81 -56
- 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 +3 -0
- 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 +10 -19
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +30 -18
- 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 +17 -5
- 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 +32 -10
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +317 -232
- 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 +25 -14
- package/dist/expectation.d.ts.map +1 -1
- package/dist/expectation.js +51 -20
- 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.map +1 -1
- package/dist/file.js +2 -1
- package/dist/host.d.ts +18 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +55 -15
- package/dist/index.d.ts +15 -10
- 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 -109
- 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 -31
- 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 +5 -0
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +65 -14
- package/dist/memory.d.ts +7 -3
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +56 -26
- package/dist/messages.d.ts +12 -6
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +25 -27
- 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 +173 -69
- package/dist/opened.d.ts +1 -1
- package/dist/opened.d.ts.map +1 -1
- package/dist/opened.js +2 -2
- package/dist/opening.d.ts.map +1 -1
- package/dist/opening.js +5 -9
- package/dist/outcome.d.ts +7 -0
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +23 -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 -59
- package/dist/parts.d.ts.map +1 -1
- package/dist/parts.js +10 -123
- package/dist/preparation.d.ts +27 -6
- package/dist/preparation.d.ts.map +1 -1
- package/dist/preparation.js +154 -66
- 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 +66 -30
- 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 +83 -25
- package/dist/react.d.ts +8 -5
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +28 -5
- 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 -5
- package/dist/replica.d.ts +5 -11
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +14 -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 +30 -5
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +270 -63
- package/dist/room-drafts.d.ts.map +1 -1
- package/dist/room-drafts.js +8 -5
- 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 +314 -223
- package/dist/saves.d.ts +2 -40
- package/dist/saves.d.ts.map +1 -1
- package/dist/saves.js +124 -703
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +4 -4
- package/dist/shown.d.ts +7 -5
- package/dist/shown.d.ts.map +1 -1
- package/dist/shown.js +75 -58
- 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.d.ts +3 -6
- package/dist/socket.d.ts.map +1 -1
- package/dist/socket.js +3 -14
- package/dist/source.d.ts +5 -7
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +13 -2
- 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 +17 -6
- package/dist/store/records.d.ts.map +1 -1
- package/dist/store/records.js +56 -22
- 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.d.ts +23 -14
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +46 -17
- package/dist/stored.d.ts +24 -13
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +80 -29
- package/dist/surface.d.ts +5 -3
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +23 -21
- package/dist/sync.d.ts +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +89 -85
- package/dist/tables.d.ts +16 -56
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +86 -327
- package/dist/tallies.d.ts.map +1 -1
- package/dist/tallies.js +1 -2
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +4 -10
- package/dist/wire.d.ts +45 -7
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +15 -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,9 +16,13 @@ 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
27
|
const changes = [];
|
|
24
28
|
const names = new Set();
|
|
@@ -42,43 +46,13 @@ function repointed(document, definition, from, to, moves, settled) {
|
|
|
42
46
|
}
|
|
43
47
|
if (changes.length === 0)
|
|
44
48
|
return [];
|
|
45
|
-
const applied = apply(document, changes);
|
|
49
|
+
const applied = apply(document, changes, definition);
|
|
46
50
|
if (applied.outcome !== 'applied')
|
|
47
51
|
return [];
|
|
48
52
|
const next = applied.document;
|
|
49
53
|
return [...names].map((name) => ({ kind: 'field', path: from, name, value: next.fields[name] }));
|
|
50
54
|
}
|
|
51
|
-
function
|
|
52
|
-
const parts = new Map();
|
|
53
|
-
for (const change of changes) {
|
|
54
|
-
if (change.kind === 'preamble')
|
|
55
|
-
parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
|
|
56
|
-
if (change.kind === 'section')
|
|
57
|
-
parts.set(`section ${change.name}`, {
|
|
58
|
-
kind: 'section',
|
|
59
|
-
name: change.name,
|
|
60
|
-
value: change.value,
|
|
61
|
-
prior: document.sections[change.name] ?? '',
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
if (parts.size === 0)
|
|
65
|
-
return document;
|
|
66
|
-
const applied = apply(document, [...parts.values()]);
|
|
67
|
-
return applied.outcome === 'applied' ? applied.document : document;
|
|
68
|
-
}
|
|
69
|
-
function bodies(path, before, after) {
|
|
70
|
-
const changes = [];
|
|
71
|
-
if (after.preamble !== before.preamble) {
|
|
72
|
-
changes.push({ kind: 'preamble', path, value: after.preamble });
|
|
73
|
-
}
|
|
74
|
-
for (const [name, value] of Object.entries(after.sections)) {
|
|
75
|
-
if (before.sections[name] === value)
|
|
76
|
-
continue;
|
|
77
|
-
changes.push({ kind: 'section', path, name, value });
|
|
78
|
-
}
|
|
79
|
-
return changes;
|
|
80
|
-
}
|
|
81
|
-
function relinks(path, moved, before, moves) {
|
|
55
|
+
function relinks(path, moved, before, moves, definition) {
|
|
82
56
|
const changes = [];
|
|
83
57
|
for (const link of links(before, path)) {
|
|
84
58
|
const destination = moves.get(link.target) ?? link.target;
|
|
@@ -91,28 +65,21 @@ function relinks(path, moved, before, moves) {
|
|
|
91
65
|
}
|
|
92
66
|
if (changes.length === 0)
|
|
93
67
|
return { kind: 'changes', changes: [] };
|
|
94
|
-
const applied = apply(before, changes);
|
|
95
|
-
if (applied.outcome === 'refuse')
|
|
96
|
-
return
|
|
97
|
-
kind: 'refused',
|
|
98
|
-
refusal: {
|
|
99
|
-
code: applied.refusal.rule,
|
|
100
|
-
retryable: false,
|
|
101
|
-
file: path,
|
|
102
|
-
...(applied.refusal.location === undefined ? {} : { location: applied.refusal.location }),
|
|
103
|
-
},
|
|
104
|
-
};
|
|
105
|
-
}
|
|
68
|
+
const applied = apply(before, changes, definition);
|
|
69
|
+
if (applied.outcome === 'refuse')
|
|
70
|
+
return refusedBy(applied.refusal, path);
|
|
106
71
|
const after = applied.document;
|
|
107
72
|
if (after === before)
|
|
108
73
|
return { kind: 'changes', changes: [] };
|
|
109
74
|
if (before.type === undefined) {
|
|
110
75
|
return { kind: 'changes', changes: [{ kind: 'replace', path, content: serialize(after) }] };
|
|
111
76
|
}
|
|
112
|
-
return { kind: 'changes', changes:
|
|
77
|
+
return { kind: 'changes', changes: partsBetween(path, before, after) };
|
|
113
78
|
}
|
|
114
79
|
function partKey(change) {
|
|
115
|
-
return change.kind === 'section'
|
|
80
|
+
return change.kind === 'section' || change.kind === 'field'
|
|
81
|
+
? `${change.kind} ${change.name}`
|
|
82
|
+
: change.kind;
|
|
116
83
|
}
|
|
117
84
|
const NAMES_A_SKIP_TESTS = 64;
|
|
118
85
|
const ENCODED = /[%&\\]/u;
|
|
@@ -155,13 +122,14 @@ function* rewrites(changes, visible) {
|
|
|
155
122
|
if (settled.has(path))
|
|
156
123
|
continue;
|
|
157
124
|
const moved = moves.get(path) ?? path;
|
|
158
|
-
const definition =
|
|
125
|
+
const definition = definitionsAt(visible.collection.composition, path)(document.type);
|
|
159
126
|
const repointing = repointed(document, definition, path, moved, moves, settled);
|
|
160
127
|
added.push(...repointing);
|
|
161
128
|
const own = parts.get(path) ?? [];
|
|
129
|
+
const edited = applyParts(document, own.map((index) => merged[index]), definition);
|
|
162
130
|
const relinked = moved === path && own.length === 0 && cannotLink(document.content, names)
|
|
163
131
|
? { kind: 'changes', changes: [] }
|
|
164
|
-
: relinks(path, moved, edited
|
|
132
|
+
: relinks(path, moved, edited.outcome === 'applied' ? edited.document : document, moves, definition);
|
|
165
133
|
if (relinked.kind === 'refused')
|
|
166
134
|
return relinked;
|
|
167
135
|
const keyed = new Map();
|
|
@@ -174,7 +142,7 @@ function* rewrites(changes, visible) {
|
|
|
174
142
|
if (change.kind === 'replace') {
|
|
175
143
|
for (const index of own) {
|
|
176
144
|
const kind = merged[index]?.kind;
|
|
177
|
-
if (kind === 'section' || kind === 'preamble')
|
|
145
|
+
if (kind === 'field' || kind === 'section' || kind === 'preamble')
|
|
178
146
|
merged[index] = null;
|
|
179
147
|
}
|
|
180
148
|
added.push(change);
|
|
@@ -230,25 +198,21 @@ function crossed(document, definition, path, to) {
|
|
|
230
198
|
}
|
|
231
199
|
return changes;
|
|
232
200
|
}
|
|
233
|
-
function rewritten(path, document, changes) {
|
|
201
|
+
function rewritten(path, document, changes, definition) {
|
|
234
202
|
if (changes.length === 0)
|
|
235
203
|
return [];
|
|
236
|
-
const applied = apply(document, changes);
|
|
204
|
+
const applied = apply(document, changes, definition);
|
|
237
205
|
if (applied.outcome !== 'applied')
|
|
238
206
|
return [];
|
|
239
207
|
const after = applied.document;
|
|
240
208
|
if (document.type === undefined)
|
|
241
209
|
return [{ kind: 'replace', path, content: serialize(after) }];
|
|
242
|
-
|
|
243
|
-
return [
|
|
244
|
-
...[...names].map((name) => ({ kind: 'field', path, name, value: after.fields[name] })),
|
|
245
|
-
...bodies(path, document, after),
|
|
246
|
-
];
|
|
210
|
+
return partsBetween(path, document, after);
|
|
247
211
|
}
|
|
248
212
|
function roomPathOf(handle, path) {
|
|
249
213
|
return `/rooms/${handle}/${path}`;
|
|
250
214
|
}
|
|
251
|
-
|
|
215
|
+
function* leavingRewritten(batch, visible) {
|
|
252
216
|
const between = batch.between;
|
|
253
217
|
if (between === undefined)
|
|
254
218
|
return batch;
|
|
@@ -257,8 +221,8 @@ export function* leavingRewritten(batch, visible) {
|
|
|
257
221
|
for (const [path, document] of yield* documentsOf(visible.collection)) {
|
|
258
222
|
if (moved.has(path))
|
|
259
223
|
continue;
|
|
260
|
-
const definition =
|
|
261
|
-
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));
|
|
262
226
|
yield ROW_WEIGHT + visible.collection.text[path].length;
|
|
263
227
|
}
|
|
264
228
|
if (derived.length === 0)
|
|
@@ -300,7 +264,7 @@ function* incoming(collection, removed, candidates) {
|
|
|
300
264
|
for (const [path, document] of candidates === null
|
|
301
265
|
? yield* documentsOf(collection)
|
|
302
266
|
: read(candidates)) {
|
|
303
|
-
const definition =
|
|
267
|
+
const definition = definitionsAt(collection.composition, path)(document.type);
|
|
304
268
|
for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
|
|
305
269
|
if (to.includes(ATTACHMENT))
|
|
306
270
|
continue;
|
|
@@ -343,18 +307,9 @@ function guard(changes, input, removing) {
|
|
|
343
307
|
const occupied = (path) => input.folders.has(path) ? path : (ancestors(path).find((one) => existing.has(one)) ?? null);
|
|
344
308
|
for (const [index, change] of changes.entries()) {
|
|
345
309
|
for (const path of affected(change)) {
|
|
346
|
-
const
|
|
347
|
-
if (
|
|
348
|
-
return refuse(
|
|
349
|
-
if (covered(input.providerOwnedPaths, normalized) ||
|
|
350
|
-
(input.authoredPaths !== undefined && !authoredAt(input.authoredPaths, normalized))) {
|
|
351
|
-
return refuse('provider-owned-path', normalized);
|
|
352
|
-
}
|
|
353
|
-
if (!input.pathRule(normalized))
|
|
354
|
-
return refuse('invalid-path', normalized);
|
|
355
|
-
for (const operation of change.kind === 'remove' ? removing : WRITE)
|
|
356
|
-
if (!input.local && !covered(input.operations[operation], normalized))
|
|
357
|
-
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);
|
|
358
313
|
}
|
|
359
314
|
if (change.kind === 'create' || change.kind === 'arrival') {
|
|
360
315
|
const taken = existing.has(change.path) ? change.path : occupied(change.path);
|
|
@@ -415,9 +370,9 @@ function guard(changes, input, removing) {
|
|
|
415
370
|
return null;
|
|
416
371
|
}
|
|
417
372
|
function unrevised(input, path) {
|
|
418
|
-
return
|
|
373
|
+
return !input.local && !input.queued.has(path) && input.visible.revisions[path] === undefined;
|
|
419
374
|
}
|
|
420
|
-
|
|
375
|
+
function queuedDocuments(queue) {
|
|
421
376
|
const paths = new Set();
|
|
422
377
|
for (const batch of queue) {
|
|
423
378
|
for (const change of batch.changes) {
|
|
@@ -462,7 +417,7 @@ function* preconditionsOf(changes, visible, bases) {
|
|
|
462
417
|
};
|
|
463
418
|
for (const change of changes) {
|
|
464
419
|
yield ROW_WEIGHT;
|
|
465
|
-
if (change
|
|
420
|
+
if (isPart(change)) {
|
|
466
421
|
const based = bases.find((base) => sameTarget(base, change));
|
|
467
422
|
if (based !== undefined) {
|
|
468
423
|
add(based);
|
|
@@ -553,93 +508,101 @@ function* exceeding(limits, changes, effective, resulting) {
|
|
|
553
508
|
function shellRelative(text, path, handle, definition) {
|
|
554
509
|
if (!text.includes(`/rooms/${handle}/`))
|
|
555
510
|
return null;
|
|
556
|
-
const parsed = parse(text, path);
|
|
511
|
+
const parsed = parse(text, path, definition);
|
|
557
512
|
if (!parsed.ok)
|
|
558
513
|
return null;
|
|
559
514
|
const result = relativize(parsed.document, path, handle, definition(parsed.document.type));
|
|
560
515
|
return result.outcome === 'applied' && result.document.content !== text ? result.document : null;
|
|
561
516
|
}
|
|
562
517
|
function relativeField(path, name, value, handle, definition) {
|
|
563
|
-
|
|
564
|
-
if (definition === undefined ||
|
|
565
|
-
!opened.ok ||
|
|
566
|
-
!JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
518
|
+
if (definition === undefined || !JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
567
519
|
return value;
|
|
568
|
-
const held = apply(
|
|
520
|
+
const held = apply(create(definition.name, definition), [{ kind: 'field', name, value, prior: undefined }], definition);
|
|
569
521
|
if (held.outcome !== 'applied')
|
|
570
522
|
return value;
|
|
571
523
|
const result = relativize(held.document, path, handle, definition);
|
|
572
524
|
return result.outcome === 'applied' ? result.document.fields[name] : value;
|
|
573
525
|
}
|
|
574
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) {
|
|
575
537
|
const { handle, composition, visible } = input;
|
|
576
|
-
const typed = (path) => (type) => definitionOf(governing(composition, path), type);
|
|
577
538
|
const body = (path, text) => shellRelative(text, path, handle, () => undefined)?.content ?? text;
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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
|
+
};
|
|
603
571
|
}
|
|
604
572
|
function unqueueable(file) {
|
|
605
|
-
return
|
|
606
|
-
}
|
|
607
|
-
function isPart(change) {
|
|
608
|
-
return change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble';
|
|
573
|
+
return refuse('unknown-revision', file);
|
|
609
574
|
}
|
|
610
575
|
function touched(change) {
|
|
611
576
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
612
577
|
}
|
|
613
|
-
function partsApplied(content, path, changes) {
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
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();
|
|
619
584
|
for (const change of changes) {
|
|
620
|
-
if (change
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
name: change.name,
|
|
631
|
-
value: change.value,
|
|
632
|
-
prior: document.sections[change.name] ?? '',
|
|
633
|
-
});
|
|
634
|
-
else
|
|
635
|
-
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]);
|
|
587
|
+
}
|
|
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);
|
|
636
595
|
}
|
|
637
|
-
|
|
638
|
-
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
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;
|
|
639
602
|
}
|
|
640
|
-
function settledParts(changes, deferred) {
|
|
603
|
+
function settledParts(changes, deferred, composition) {
|
|
641
604
|
for (const [held, parts] of deferred) {
|
|
642
|
-
const content = partsApplied(held.content, held.path, parts);
|
|
605
|
+
const content = partsApplied(held.content, held.path, parts, composition);
|
|
643
606
|
if (content === null)
|
|
644
607
|
return held.path;
|
|
645
608
|
changes[changes.indexOf(held)] = { ...held, content };
|
|
@@ -714,7 +677,9 @@ function* relinked(batch, move, before, after, appending) {
|
|
|
714
677
|
for (const change of derived.changes) {
|
|
715
678
|
if (change === move)
|
|
716
679
|
continue;
|
|
717
|
-
const unsettled = isPart(change)
|
|
680
|
+
const unsettled = isPart(change)
|
|
681
|
+
? null
|
|
682
|
+
: settledParts(changes, deferred, after.collection.composition);
|
|
718
683
|
if (unsettled !== null)
|
|
719
684
|
return unqueueable(unsettled);
|
|
720
685
|
const outcome = folded(changes, change, appending, deferred);
|
|
@@ -725,7 +690,7 @@ function* relinked(batch, move, before, after, appending) {
|
|
|
725
690
|
if (outcome !== 'skipped')
|
|
726
691
|
changed = true;
|
|
727
692
|
}
|
|
728
|
-
const unsettled = settledParts(changes, deferred);
|
|
693
|
+
const unsettled = settledParts(changes, deferred, after.collection.composition);
|
|
729
694
|
if (unsettled !== null)
|
|
730
695
|
return unqueueable(unsettled);
|
|
731
696
|
if (!changed)
|
|
@@ -786,11 +751,10 @@ function withdrawnFile(batch, change, at) {
|
|
|
786
751
|
];
|
|
787
752
|
return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
|
|
788
753
|
}
|
|
789
|
-
|
|
754
|
+
function* requeuing(queue, changes, visible, changeable, state) {
|
|
790
755
|
const next = [...queue];
|
|
791
756
|
const origins = new Map();
|
|
792
757
|
const withdrawn = new Map();
|
|
793
|
-
const owners = new Set();
|
|
794
758
|
const queued = new Set();
|
|
795
759
|
const kept = [];
|
|
796
760
|
const originOf = (batch) => origins.get(batch) ?? batch.id;
|
|
@@ -836,7 +800,6 @@ export function* requeuing(queue, changes, visible, changeable, state) {
|
|
|
836
800
|
if (replaced !== null)
|
|
837
801
|
origins.set(replaced, origin);
|
|
838
802
|
}
|
|
839
|
-
owners.add(originOf(owner));
|
|
840
803
|
next.splice(index, following.length, ...rebuilt.filter((batch) => batch !== null));
|
|
841
804
|
queued.add(change.path);
|
|
842
805
|
}
|
|
@@ -846,62 +809,16 @@ export function* requeuing(queue, changes, visible, changeable, state) {
|
|
|
846
809
|
changes: kept,
|
|
847
810
|
queued,
|
|
848
811
|
withdrawn,
|
|
849
|
-
owners,
|
|
850
812
|
};
|
|
851
813
|
}
|
|
852
|
-
function
|
|
853
|
-
return precondition.kind === 'field' ||
|
|
854
|
-
precondition.kind === 'section' ||
|
|
855
|
-
precondition.kind === 'preamble'
|
|
856
|
-
? identity(precondition)
|
|
857
|
-
: `path ${precondition.path}`;
|
|
858
|
-
}
|
|
859
|
-
export function joined(queue, owner, extra) {
|
|
860
|
-
const later = new Set(queue.slice(queue.indexOf(owner) + 1).flatMap((batch) => batch.changes.flatMap(touched)));
|
|
861
|
-
const clash = extra.changes.flatMap(touched).find((path) => later.has(path));
|
|
862
|
-
if (clash !== undefined)
|
|
863
|
-
return unqueueable(clash);
|
|
864
|
-
const changes = [...owner.changes];
|
|
865
|
-
const deferred = new Map();
|
|
866
|
-
for (const change of extra.changes) {
|
|
867
|
-
const unsettled = isPart(change) ? null : settledParts(changes, deferred);
|
|
868
|
-
if (unsettled !== null)
|
|
869
|
-
return unqueueable(unsettled);
|
|
870
|
-
if (folded(changes, change, true, deferred) === 'refused')
|
|
871
|
-
return unqueueable(change.path);
|
|
872
|
-
}
|
|
873
|
-
const unsettled = settledParts(changes, deferred);
|
|
874
|
-
if (unsettled !== null)
|
|
875
|
-
return unqueueable(unsettled);
|
|
876
|
-
const held = new Set(owner.preconditions.map(covering));
|
|
877
|
-
const written = new Set(owner.changes.flatMap((change) => change.kind === 'create' || change.kind === 'replace' ? [change.path] : []));
|
|
878
|
-
const preconditions = [
|
|
879
|
-
...owner.preconditions,
|
|
880
|
-
...extra.preconditions.filter((precondition) => {
|
|
881
|
-
const key = covering(precondition);
|
|
882
|
-
return (!held.has(key) && (key === `path ${precondition.path}` || !written.has(precondition.path)));
|
|
883
|
-
}),
|
|
884
|
-
];
|
|
885
|
-
const batch = immutable({
|
|
886
|
-
...owner,
|
|
887
|
-
...(extra.validation === undefined ? {} : { validation: extra.validation }),
|
|
888
|
-
id: changeBatchIdentity(),
|
|
889
|
-
changes,
|
|
890
|
-
preconditions,
|
|
891
|
-
});
|
|
892
|
-
return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
|
|
893
|
-
}
|
|
894
|
-
export function* limiting(queue, batch, limits, state) {
|
|
814
|
+
function* limiting(queue, batch, limits, state) {
|
|
895
815
|
const at = queue.indexOf(batch);
|
|
896
816
|
const rewritten = yield* providerRewritten(batch, yield* state(queue.slice(0, at)));
|
|
897
817
|
const limited = yield* exceeding(limits, batch.changes, rewritten.changes, yield* state(queue.slice(0, at + 1)));
|
|
898
818
|
return limited?.refusal ?? null;
|
|
899
819
|
}
|
|
900
|
-
function
|
|
901
|
-
|
|
902
|
-
return false;
|
|
903
|
-
const parsed = parse(content);
|
|
904
|
-
return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
|
|
820
|
+
function typedContent(content) {
|
|
821
|
+
return content !== undefined && typedText(parse(content));
|
|
905
822
|
}
|
|
906
823
|
function touchedLibrary(resulting, touched, composition) {
|
|
907
824
|
const entries = touched
|
|
@@ -921,9 +838,9 @@ function touchedLibrary(resulting, touched, composition) {
|
|
|
921
838
|
if (binaries.has(path))
|
|
922
839
|
known.set(path, { type: undefined, binary: true });
|
|
923
840
|
else if (content !== undefined && content !== null) {
|
|
924
|
-
const
|
|
925
|
-
if (
|
|
926
|
-
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 });
|
|
927
844
|
await pace(content.length + ROW_WEIGHT);
|
|
928
845
|
}
|
|
929
846
|
}
|
|
@@ -941,7 +858,7 @@ async function judging(visible, resulting, composition, validate, removed, touch
|
|
|
941
858
|
for (const path of touched) {
|
|
942
859
|
const before = visible.documents[path];
|
|
943
860
|
const after = resulting.documents[path];
|
|
944
|
-
if (
|
|
861
|
+
if (typedContent(before) || typedContent(after))
|
|
945
862
|
typedPaths.push(path);
|
|
946
863
|
await pace((before?.length ?? 0) + (after?.length ?? 0) + ROW_WEIGHT);
|
|
947
864
|
}
|
|
@@ -959,6 +876,7 @@ async function judging(visible, resulting, composition, validate, removed, touch
|
|
|
959
876
|
retryable: false,
|
|
960
877
|
file: verdict.file,
|
|
961
878
|
...(verdict.location === undefined ? {} : { location: verdict.location }),
|
|
879
|
+
...(verdict.line === undefined ? {} : { line: verdict.line }),
|
|
962
880
|
...(verdict.referrers === undefined ? {} : { referrers: verdict.referrers }),
|
|
963
881
|
},
|
|
964
882
|
};
|
|
@@ -974,8 +892,6 @@ function placed(reference) {
|
|
|
974
892
|
const key = roomPathOf(reference.handle, reference.path);
|
|
975
893
|
if (reference.kind === 'link')
|
|
976
894
|
return `link ${key}`;
|
|
977
|
-
if (reference.kind === 'item')
|
|
978
|
-
return `section ${reference.section} ${key}`;
|
|
979
895
|
const pointer = reference.pointer
|
|
980
896
|
.split('/')
|
|
981
897
|
.map((segment) => (ARRAY_INDEX.test(segment) ? '-' : segment))
|
|
@@ -985,10 +901,11 @@ function placed(reference) {
|
|
|
985
901
|
function roomReferencesIn(path, content, composition) {
|
|
986
902
|
if (content === undefined)
|
|
987
903
|
return [];
|
|
988
|
-
const
|
|
904
|
+
const definitions = definitionsAt(composition, path);
|
|
905
|
+
const parsed = parse(content, path, definitions);
|
|
989
906
|
if (!parsed.ok)
|
|
990
907
|
return [];
|
|
991
|
-
return roomReferences(parsed.document,
|
|
908
|
+
return roomReferences(parsed.document, definitions(parsed.document.type));
|
|
992
909
|
}
|
|
993
910
|
function* admittedRooms(changes, visible, resulting, composition) {
|
|
994
911
|
const from = new Map();
|
|
@@ -1006,7 +923,7 @@ function* admittedRooms(changes, visible, resulting, composition) {
|
|
|
1006
923
|
continue;
|
|
1007
924
|
if (held.has(placed(reference)))
|
|
1008
925
|
continue;
|
|
1009
|
-
return refuse('unresolved-reference', path, reference.
|
|
926
|
+
return refuse('unresolved-reference', path, reference.pointer);
|
|
1010
927
|
}
|
|
1011
928
|
yield ROW_WEIGHT + now.length;
|
|
1012
929
|
}
|
|
@@ -1024,7 +941,10 @@ function validationOf(validated) {
|
|
|
1024
941
|
};
|
|
1025
942
|
}
|
|
1026
943
|
async function batching(input) {
|
|
1027
|
-
const
|
|
944
|
+
const relative = relativeChanges(input);
|
|
945
|
+
if (relative.kind === 'refused')
|
|
946
|
+
return relative;
|
|
947
|
+
const { changes } = relative;
|
|
1028
948
|
const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
|
|
1029
949
|
if (structural !== undefined && input.unmodeled) {
|
|
1030
950
|
return refuse('model-unavailable', structural.path);
|
|
@@ -1034,12 +954,12 @@ async function batching(input) {
|
|
|
1034
954
|
const rewritten = await stepped(rewrites(changes, input.visible));
|
|
1035
955
|
if (rewritten.kind === 'refused')
|
|
1036
956
|
return rewritten;
|
|
1037
|
-
const refusal = guard(changes, input, WRITE);
|
|
957
|
+
const refusal = guard(changes, input, WRITE) ?? unapplied(changes, input.visible, input.composition);
|
|
1038
958
|
if (refusal !== null)
|
|
1039
959
|
return refusal;
|
|
1040
960
|
const preconditions = await stepped(preconditionsOf(changes, input.visible, input.bases));
|
|
1041
961
|
const predicted = await stepped(anticipated(changes, preconditions, rewritten.changes, input.visible));
|
|
1042
|
-
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));
|
|
1043
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))]);
|
|
1044
964
|
if (judged.kind === 'refused')
|
|
1045
965
|
return judged;
|
|
@@ -1061,7 +981,7 @@ async function batching(input) {
|
|
|
1061
981
|
}),
|
|
1062
982
|
};
|
|
1063
983
|
}
|
|
1064
|
-
|
|
984
|
+
const MOVE_DOCUMENTS = 'move-documents';
|
|
1065
985
|
function heldAt(visible, path) {
|
|
1066
986
|
return visible.documents[path] !== undefined || visible.binaries[path] !== undefined;
|
|
1067
987
|
}
|
|
@@ -1070,7 +990,7 @@ function arrivingText(path, to, source, moved) {
|
|
|
1070
990
|
const document = source.visible.collection.document(path);
|
|
1071
991
|
if (document === undefined)
|
|
1072
992
|
return content;
|
|
1073
|
-
const definition =
|
|
993
|
+
const definition = definitionsAt(source.composition, path)(document.type);
|
|
1074
994
|
const changes = crossed(document, definition, path, (named, href) => {
|
|
1075
995
|
const at = moved.get(named);
|
|
1076
996
|
if (at !== undefined)
|
|
@@ -1079,7 +999,7 @@ function arrivingText(path, to, source, moved) {
|
|
|
1079
999
|
});
|
|
1080
1000
|
if (changes.length === 0)
|
|
1081
1001
|
return content;
|
|
1082
|
-
const applied = apply(document, changes);
|
|
1002
|
+
const applied = apply(document, changes, definition);
|
|
1083
1003
|
return applied.outcome === 'applied' ? serialize(applied.document) : content;
|
|
1084
1004
|
}
|
|
1085
1005
|
function arrivalOf({ path, to }, source, moved) {
|
|
@@ -1112,9 +1032,9 @@ function arrivalOf({ path, to }, source, moved) {
|
|
|
1112
1032
|
};
|
|
1113
1033
|
}
|
|
1114
1034
|
async function moving(input) {
|
|
1115
|
-
const { source, destination, moves
|
|
1035
|
+
const { source, destination, moves } = input;
|
|
1116
1036
|
const removals = moves.map(({ path }) => ({ kind: 'remove', path }));
|
|
1117
|
-
const leaves = guard(removals, { ...source, local: input.local, bases: []
|
|
1037
|
+
const leaves = guard(removals, { ...source, local: input.local, bases: [] }, MOVE_OUT);
|
|
1118
1038
|
if (leaves !== null)
|
|
1119
1039
|
return leaves;
|
|
1120
1040
|
const uploading = moves.find(({ path }) => source.visible.binaries[path]?.state === 'pending');
|
|
@@ -1122,7 +1042,7 @@ async function moving(input) {
|
|
|
1122
1042
|
return refuse('unknown-revision', uploading.path);
|
|
1123
1043
|
const moved = new Map(moves.map(({ path, to }) => [path, to]));
|
|
1124
1044
|
const arrivals = moves.map((move) => arrivalOf(move, source, moved));
|
|
1125
|
-
const arrives = guard(arrivals, { ...destination, local: input.local, bases: []
|
|
1045
|
+
const arrives = guard(arrivals, { ...destination, local: input.local, bases: [] }, WRITE);
|
|
1126
1046
|
if (arrives !== null)
|
|
1127
1047
|
return arrives;
|
|
1128
1048
|
const bound = input.providerLimits.find((limit) => limit.name === MOVE_DOCUMENTS)?.value;
|
|
@@ -1151,7 +1071,7 @@ async function moving(input) {
|
|
|
1151
1071
|
between,
|
|
1152
1072
|
};
|
|
1153
1073
|
const effective = await stepped(leavingRewritten(leavingBatch, source.visible));
|
|
1154
|
-
const left = await stepped(overlaying(source.visible, [effective]));
|
|
1074
|
+
const left = await stepped(overlaying(source.visible, [effective], source.composition));
|
|
1155
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))]);
|
|
1156
1076
|
if (sourceJudged.kind === 'refused')
|
|
1157
1077
|
return sourceJudged;
|
|
@@ -1163,7 +1083,7 @@ async function moving(input) {
|
|
|
1163
1083
|
...written,
|
|
1164
1084
|
between,
|
|
1165
1085
|
};
|
|
1166
|
-
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch]));
|
|
1086
|
+
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch], destination.composition));
|
|
1167
1087
|
const destinationJudged = await judging(destination.visible, arrived, destination.composition, destination.validate, new Set(), moves.map(({ to }) => to));
|
|
1168
1088
|
if (destinationJudged.kind === 'refused')
|
|
1169
1089
|
return destinationJudged;
|
|
@@ -1173,9 +1093,180 @@ async function moving(input) {
|
|
|
1173
1093
|
return limited;
|
|
1174
1094
|
return { kind: 'moved', leaving: immutable(leavingBatch), arriving: immutable(arrivingBatch) };
|
|
1175
1095
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
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;
|
|
1178
1269
|
}
|
|
1179
|
-
export function
|
|
1180
|
-
return
|
|
1270
|
+
export function planSave(ask, room, into) {
|
|
1271
|
+
return into === undefined ? plannedWithin(ask, room) : plannedMove(ask, room, into);
|
|
1181
1272
|
}
|