@agentshouse/mdruntime 0.1.0-c44866799613e → 0.1.0-c44d5cc00aee5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -17
- package/dist/acceptance.d.ts +3 -0
- package/dist/acceptance.d.ts.map +1 -0
- package/dist/acceptance.js +299 -0
- 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 +26 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +302 -0
- package/dist/adapter.d.ts +110 -112
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +3 -11
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +28 -51
- package/dist/bootstrap.d.ts +15 -18
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +252 -158
- package/dist/cell.d.ts +6 -0
- package/dist/cell.d.ts.map +1 -0
- package/dist/cell.js +9 -0
- package/dist/chain.d.ts +2 -1
- package/dist/chain.d.ts.map +1 -1
- 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 +1 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +4 -2
- package/dist/chat-types.d.ts +3 -0
- package/dist/chat-types.d.ts.map +1 -0
- package/dist/chat-types.js +11 -0
- package/dist/client.d.ts +27 -21
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +36 -17
- package/dist/collection.d.ts +23 -7
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +13 -19
- package/dist/compile.d.ts +8 -6
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +288 -329
- package/dist/connection.d.ts +5 -143
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +109 -5104
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +7 -0
- package/dist/conversation-live.d.ts +143 -0
- package/dist/conversation-live.d.ts.map +1 -0
- package/dist/conversation-live.js +254 -0
- package/dist/conversation-row.d.ts +33 -0
- package/dist/conversation-row.d.ts.map +1 -0
- package/dist/conversation-row.js +53 -0
- package/dist/conversation.d.ts +65 -48
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +987 -838
- 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 +10 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +150 -0
- package/dist/durability.d.ts +19 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +4 -0
- package/dist/entry.d.ts +3 -2
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +19 -9
- 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 +49 -0
- package/dist/figures.d.ts.map +1 -0
- package/dist/figures.js +197 -0
- 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 +21 -8
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +73 -40
- package/dist/identity.d.ts +0 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -3
- package/dist/index.d.ts +34 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -5
- package/dist/install.d.ts +54 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +530 -0
- package/dist/lifecycle.d.ts +7 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +277 -0
- package/dist/listing.d.ts +48 -0
- package/dist/listing.d.ts.map +1 -0
- package/dist/listing.js +139 -0
- package/dist/live.d.ts +5 -4
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +64 -23
- package/dist/locks.d.ts +7 -0
- package/dist/locks.d.ts.map +1 -0
- package/dist/locks.js +32 -0
- package/dist/lost.d.ts +5 -0
- package/dist/lost.d.ts.map +1 -0
- package/dist/lost.js +49 -0
- package/dist/media.d.ts +2 -2
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +23 -13
- package/dist/memory.d.ts +17 -7
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +210 -97
- package/dist/messages.d.ts +33 -5
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +104 -36
- package/dist/model.d.ts +2 -11
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -23
- package/dist/models.d.ts +10 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +153 -0
- package/dist/observation.d.ts +4 -3
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +23 -17
- 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 +4 -0
- package/dist/opening.d.ts.map +1 -0
- package/dist/opening.js +53 -0
- package/dist/outcome.d.ts +7 -4
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +18 -0
- package/dist/overlay.d.ts +6 -1
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +47 -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 +4 -0
- package/dist/parts.d.ts.map +1 -0
- package/dist/parts.js +13 -0
- package/dist/paths.d.ts +2 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +26 -0
- package/dist/preparation.d.ts +20 -0
- package/dist/preparation.d.ts.map +1 -0
- package/dist/preparation.js +227 -0
- 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 +16 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +282 -0
- package/dist/query.d.ts +14 -210
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -505
- package/dist/questions.d.ts +29 -0
- package/dist/questions.d.ts.map +1 -0
- package/dist/questions.js +115 -0
- 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 +15 -12
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +240 -149
- package/dist/react.d.ts +15 -2
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +33 -1
- package/dist/read.d.ts +7 -0
- package/dist/read.d.ts.map +1 -0
- package/dist/read.js +17 -0
- package/dist/readiness.d.ts +4 -19
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +4 -10
- package/dist/reads.d.ts +38 -10
- package/dist/reads.d.ts.map +1 -1
- package/dist/reads.js +163 -85
- package/dist/recovery.d.ts +3 -0
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +57 -1
- package/dist/refresh.d.ts +7 -5
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +35 -7
- package/dist/refusal.d.ts +5 -0
- package/dist/refusal.d.ts.map +1 -0
- package/dist/refusal.js +55 -0
- package/dist/replica.d.ts +12 -18
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +64 -34
- 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 +24 -10
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +142 -106
- package/dist/room-drafts.d.ts +24 -0
- package/dist/room-drafts.d.ts.map +1 -0
- package/dist/room-drafts.js +217 -0
- package/dist/rows.d.ts +14 -21
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +49 -186
- package/dist/runtime.d.ts +34 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1 -0
- package/dist/save.d.ts +42 -84
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +349 -170
- package/dist/saves.d.ts +32 -0
- package/dist/saves.d.ts.map +1 -0
- package/dist/saves.js +410 -0
- package/dist/search.d.ts +12 -63
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +9 -52
- package/dist/seed.d.ts +11 -2
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +71 -1
- package/dist/shared.d.ts +1 -7
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +13 -105
- package/dist/shown.d.ts +33 -0
- package/dist/shown.d.ts.map +1 -0
- package/dist/shown.js +302 -0
- package/dist/socket/admission.d.ts +7 -0
- package/dist/socket/admission.d.ts.map +1 -0
- package/dist/socket/admission.js +39 -0
- package/dist/socket/answers.d.ts +6 -0
- package/dist/socket/answers.d.ts.map +1 -0
- package/dist/socket/answers.js +20 -0
- package/dist/socket/lanes.d.ts +28 -0
- package/dist/socket/lanes.d.ts.map +1 -0
- package/dist/socket/lanes.js +103 -0
- package/dist/socket/reads.d.ts +10 -0
- package/dist/socket/reads.d.ts.map +1 -0
- package/dist/socket/reads.js +31 -0
- package/dist/socket/session.d.ts +38 -0
- package/dist/socket/session.d.ts.map +1 -0
- package/dist/socket/session.js +23 -0
- package/dist/socket.d.ts +67 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +164 -0
- package/dist/source.d.ts +54 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +82 -0
- package/dist/status.d.ts +4 -5
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -7
- package/dist/store/installs.d.ts +30 -0
- package/dist/store/installs.d.ts.map +1 -0
- package/dist/store/installs.js +34 -0
- package/dist/store/readiness.d.ts +10 -0
- package/dist/store/readiness.d.ts.map +1 -0
- package/dist/store/readiness.js +52 -0
- package/dist/store/records.d.ts +214 -0
- package/dist/store/records.d.ts.map +1 -0
- package/dist/store/records.js +438 -0
- package/dist/store/room-held.d.ts +8 -0
- package/dist/store/room-held.d.ts.map +1 -0
- package/dist/store/room-held.js +17 -0
- package/dist/store/space.d.ts +11 -0
- package/dist/store/space.d.ts.map +1 -0
- package/dist/store/space.js +41 -0
- package/dist/store/watchers.d.ts +17 -0
- package/dist/store/watchers.d.ts.map +1 -0
- package/dist/store/watchers.js +56 -0
- package/dist/store/writes.d.ts +24 -0
- package/dist/store/writes.d.ts.map +1 -0
- package/dist/store/writes.js +125 -0
- package/dist/store.d.ts +120 -171
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +166 -414
- package/dist/stored.d.ts +62 -57
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +317 -239
- package/dist/surface.d.ts +88 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +220 -0
- package/dist/sync.d.ts +19 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +535 -0
- package/dist/tables.d.ts +126 -65
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +250 -113
- package/dist/tallies.d.ts +18 -0
- package/dist/tallies.d.ts.map +1 -0
- package/dist/tallies.js +128 -0
- package/dist/transfer.d.ts +0 -5
- package/dist/transfer.d.ts.map +1 -1
- package/dist/validation.d.ts +3 -4
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +42 -100
- package/dist/windows.d.ts +1 -12
- package/dist/windows.d.ts.map +1 -1
- package/dist/windows.js +2 -75
- package/dist/wire.d.ts +65 -9
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +22 -5
- 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/fence.d.ts +0 -6
- package/dist/fence.d.ts.map +0 -1
- package/dist/fence.js +0 -10
- package/dist/fulltext.d.ts +0 -27
- package/dist/fulltext.d.ts.map +0 -1
- package/dist/fulltext.js +0 -368
- package/dist/log.d.ts +0 -5
- package/dist/log.d.ts.map +0 -1
- package/dist/log.js +0 -14
- package/dist/words.d.ts +0 -19
- package/dist/words.d.ts.map +0 -1
- package/dist/words.js +0 -150
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 {
|
|
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;
|
|
@@ -321,6 +335,8 @@ function* incoming(collection, removed, candidates) {
|
|
|
321
335
|
function affected(change) {
|
|
322
336
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
323
337
|
}
|
|
338
|
+
const WRITE = ['write'];
|
|
339
|
+
const MOVE_OUT = ['write', 'erase'];
|
|
324
340
|
function guard(changes, input, removing) {
|
|
325
341
|
const overridden = new Map();
|
|
326
342
|
const existing = {
|
|
@@ -329,21 +345,17 @@ function guard(changes, input, removing) {
|
|
|
329
345
|
add: (path) => void overridden.set(path, true),
|
|
330
346
|
delete: (path) => void overridden.set(path, false),
|
|
331
347
|
};
|
|
348
|
+
const occupied = (path) => input.folders.has(path) ? path : (ancestors(path).find((one) => existing.has(one)) ?? null);
|
|
332
349
|
for (const [index, change] of changes.entries()) {
|
|
333
350
|
for (const path of affected(change)) {
|
|
334
|
-
const
|
|
335
|
-
if (
|
|
336
|
-
return refuse(
|
|
337
|
-
if (covered(input.providerOwnedPaths, normalized)) {
|
|
338
|
-
return refuse('provider-owned-path', normalized);
|
|
339
|
-
}
|
|
340
|
-
const operation = change.kind === 'remove' ? removing : 'write';
|
|
341
|
-
if (!input.local && !covered(input.operations[operation], normalized))
|
|
342
|
-
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);
|
|
343
354
|
}
|
|
344
355
|
if (change.kind === 'create' || change.kind === 'arrival') {
|
|
345
|
-
|
|
346
|
-
|
|
356
|
+
const taken = existing.has(change.path) ? change.path : occupied(change.path);
|
|
357
|
+
if (taken !== null)
|
|
358
|
+
return refuse('path-collision', taken);
|
|
347
359
|
existing.add(change.path);
|
|
348
360
|
continue;
|
|
349
361
|
}
|
|
@@ -353,6 +365,9 @@ function guard(changes, input, removing) {
|
|
|
353
365
|
input.bases.some((base) => base.kind === 'absent' && base.path === change.path))) {
|
|
354
366
|
return refuse('path-collision', change.path);
|
|
355
367
|
}
|
|
368
|
+
const taken = occupied(change.path);
|
|
369
|
+
if (taken !== null)
|
|
370
|
+
return refuse('path-collision', taken);
|
|
356
371
|
if (!input.local &&
|
|
357
372
|
existing.has(change.path) &&
|
|
358
373
|
input.visible.revisions[departure(changes, index)] === undefined) {
|
|
@@ -364,8 +379,9 @@ function guard(changes, input, removing) {
|
|
|
364
379
|
if (change.kind === 'move') {
|
|
365
380
|
if (!existing.has(change.path))
|
|
366
381
|
return refuse('path-absent', change.path);
|
|
367
|
-
|
|
368
|
-
|
|
382
|
+
const taken = existing.has(change.to) ? change.to : occupied(change.to);
|
|
383
|
+
if (taken !== null)
|
|
384
|
+
return refuse('path-collision', taken);
|
|
369
385
|
if (unrevised(input, change.path)) {
|
|
370
386
|
return refuse('unknown-revision', change.path);
|
|
371
387
|
}
|
|
@@ -395,9 +411,9 @@ function guard(changes, input, removing) {
|
|
|
395
411
|
return null;
|
|
396
412
|
}
|
|
397
413
|
function unrevised(input, path) {
|
|
398
|
-
return
|
|
414
|
+
return !input.local && !input.queued.has(path) && input.visible.revisions[path] === undefined;
|
|
399
415
|
}
|
|
400
|
-
|
|
416
|
+
function queuedDocuments(queue) {
|
|
401
417
|
const paths = new Set();
|
|
402
418
|
for (const batch of queue) {
|
|
403
419
|
for (const change of batch.changes) {
|
|
@@ -533,56 +549,69 @@ function* exceeding(limits, changes, effective, resulting) {
|
|
|
533
549
|
function shellRelative(text, path, handle, definition) {
|
|
534
550
|
if (!text.includes(`/rooms/${handle}/`))
|
|
535
551
|
return null;
|
|
536
|
-
const parsed = parse(text, path);
|
|
552
|
+
const parsed = parse(text, path, definition);
|
|
537
553
|
if (!parsed.ok)
|
|
538
554
|
return null;
|
|
539
555
|
const result = relativize(parsed.document, path, handle, definition(parsed.document.type));
|
|
540
556
|
return result.outcome === 'applied' && result.document.content !== text ? result.document : null;
|
|
541
557
|
}
|
|
542
558
|
function relativeField(path, name, value, handle, definition) {
|
|
543
|
-
|
|
544
|
-
if (definition === undefined ||
|
|
545
|
-
!opened.ok ||
|
|
546
|
-
!JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
559
|
+
if (definition === undefined || !JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
547
560
|
return value;
|
|
548
|
-
const held = apply(
|
|
561
|
+
const held = apply(create(definition.name, definition), [{ kind: 'field', name, value, prior: undefined }], definition);
|
|
549
562
|
if (held.outcome !== 'applied')
|
|
550
563
|
return value;
|
|
551
564
|
const result = relativize(held.document, path, handle, definition);
|
|
552
565
|
return result.outcome === 'applied' ? result.document.fields[name] : value;
|
|
553
566
|
}
|
|
554
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) {
|
|
555
578
|
const { handle, composition, visible } = input;
|
|
556
|
-
const typed = (path) => (type) => definitionOf(governing(composition, path), type);
|
|
557
579
|
const body = (path, text) => shellRelative(text, path, handle, () => undefined)?.content ?? text;
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
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
|
+
};
|
|
583
612
|
}
|
|
584
613
|
function unqueueable(file) {
|
|
585
|
-
return
|
|
614
|
+
return refuse('unknown-revision', file);
|
|
586
615
|
}
|
|
587
616
|
function isPart(change) {
|
|
588
617
|
return change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble';
|
|
@@ -590,8 +619,29 @@ function isPart(change) {
|
|
|
590
619
|
function touched(change) {
|
|
591
620
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
592
621
|
}
|
|
593
|
-
function partsApplied(content, path, changes) {
|
|
594
|
-
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);
|
|
595
645
|
if (!parsed.ok)
|
|
596
646
|
return null;
|
|
597
647
|
const document = parsed.document;
|
|
@@ -614,12 +664,11 @@ function partsApplied(content, path, changes) {
|
|
|
614
664
|
else
|
|
615
665
|
parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
|
|
616
666
|
}
|
|
617
|
-
|
|
618
|
-
return applied.outcome === 'applied' ? serialize(applied.document) : null;
|
|
667
|
+
return apply(document, [...parts.values()], definitions(document.type));
|
|
619
668
|
}
|
|
620
|
-
function settledParts(changes, deferred) {
|
|
669
|
+
function settledParts(changes, deferred, composition) {
|
|
621
670
|
for (const [held, parts] of deferred) {
|
|
622
|
-
const content = partsApplied(held.content, held.path, parts);
|
|
671
|
+
const content = partsApplied(held.content, held.path, parts, composition);
|
|
623
672
|
if (content === null)
|
|
624
673
|
return held.path;
|
|
625
674
|
changes[changes.indexOf(held)] = { ...held, content };
|
|
@@ -694,7 +743,9 @@ function* relinked(batch, move, before, after, appending) {
|
|
|
694
743
|
for (const change of derived.changes) {
|
|
695
744
|
if (change === move)
|
|
696
745
|
continue;
|
|
697
|
-
const unsettled = isPart(change)
|
|
746
|
+
const unsettled = isPart(change)
|
|
747
|
+
? null
|
|
748
|
+
: settledParts(changes, deferred, after.collection.composition);
|
|
698
749
|
if (unsettled !== null)
|
|
699
750
|
return unqueueable(unsettled);
|
|
700
751
|
const outcome = folded(changes, change, appending, deferred);
|
|
@@ -705,7 +756,7 @@ function* relinked(batch, move, before, after, appending) {
|
|
|
705
756
|
if (outcome !== 'skipped')
|
|
706
757
|
changed = true;
|
|
707
758
|
}
|
|
708
|
-
const unsettled = settledParts(changes, deferred);
|
|
759
|
+
const unsettled = settledParts(changes, deferred, after.collection.composition);
|
|
709
760
|
if (unsettled !== null)
|
|
710
761
|
return unqueueable(unsettled);
|
|
711
762
|
if (!changed)
|
|
@@ -766,11 +817,10 @@ function withdrawnFile(batch, change, at) {
|
|
|
766
817
|
];
|
|
767
818
|
return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
|
|
768
819
|
}
|
|
769
|
-
|
|
820
|
+
function* requeuing(queue, changes, visible, changeable, state) {
|
|
770
821
|
const next = [...queue];
|
|
771
822
|
const origins = new Map();
|
|
772
823
|
const withdrawn = new Map();
|
|
773
|
-
const owners = new Set();
|
|
774
824
|
const queued = new Set();
|
|
775
825
|
const kept = [];
|
|
776
826
|
const originOf = (batch) => origins.get(batch) ?? batch.id;
|
|
@@ -816,7 +866,6 @@ export function* requeuing(queue, changes, visible, changeable, state) {
|
|
|
816
866
|
if (replaced !== null)
|
|
817
867
|
origins.set(replaced, origin);
|
|
818
868
|
}
|
|
819
|
-
owners.add(originOf(owner));
|
|
820
869
|
next.splice(index, following.length, ...rebuilt.filter((batch) => batch !== null));
|
|
821
870
|
queued.add(change.path);
|
|
822
871
|
}
|
|
@@ -826,56 +875,13 @@ export function* requeuing(queue, changes, visible, changeable, state) {
|
|
|
826
875
|
changes: kept,
|
|
827
876
|
queued,
|
|
828
877
|
withdrawn,
|
|
829
|
-
owners,
|
|
830
878
|
};
|
|
831
879
|
}
|
|
832
|
-
function
|
|
833
|
-
return precondition.kind === 'field' ||
|
|
834
|
-
precondition.kind === 'section' ||
|
|
835
|
-
precondition.kind === 'preamble'
|
|
836
|
-
? identity(precondition)
|
|
837
|
-
: `path ${precondition.path}`;
|
|
838
|
-
}
|
|
839
|
-
export function joined(queue, owner, extra) {
|
|
840
|
-
const later = new Set(queue.slice(queue.indexOf(owner) + 1).flatMap((batch) => batch.changes.flatMap(touched)));
|
|
841
|
-
const clash = extra.changes.flatMap(touched).find((path) => later.has(path));
|
|
842
|
-
if (clash !== undefined)
|
|
843
|
-
return unqueueable(clash);
|
|
844
|
-
const changes = [...owner.changes];
|
|
845
|
-
const deferred = new Map();
|
|
846
|
-
for (const change of extra.changes) {
|
|
847
|
-
const unsettled = isPart(change) ? null : settledParts(changes, deferred);
|
|
848
|
-
if (unsettled !== null)
|
|
849
|
-
return unqueueable(unsettled);
|
|
850
|
-
if (folded(changes, change, true, deferred) === 'refused')
|
|
851
|
-
return unqueueable(change.path);
|
|
852
|
-
}
|
|
853
|
-
const unsettled = settledParts(changes, deferred);
|
|
854
|
-
if (unsettled !== null)
|
|
855
|
-
return unqueueable(unsettled);
|
|
856
|
-
const held = new Set(owner.preconditions.map(covering));
|
|
857
|
-
const written = new Set(owner.changes.flatMap((change) => change.kind === 'create' || change.kind === 'replace' ? [change.path] : []));
|
|
858
|
-
const preconditions = [
|
|
859
|
-
...owner.preconditions,
|
|
860
|
-
...extra.preconditions.filter((precondition) => {
|
|
861
|
-
const key = covering(precondition);
|
|
862
|
-
return (!held.has(key) && (key === `path ${precondition.path}` || !written.has(precondition.path)));
|
|
863
|
-
}),
|
|
864
|
-
];
|
|
865
|
-
const batch = immutable({
|
|
866
|
-
...owner,
|
|
867
|
-
...(extra.validation === undefined ? {} : { validation: extra.validation }),
|
|
868
|
-
id: changeBatchIdentity(),
|
|
869
|
-
changes,
|
|
870
|
-
preconditions,
|
|
871
|
-
});
|
|
872
|
-
return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
|
|
873
|
-
}
|
|
874
|
-
export function* limiting(queue, batch, limits, state) {
|
|
880
|
+
function* limiting(queue, batch, limits, state) {
|
|
875
881
|
const at = queue.indexOf(batch);
|
|
876
882
|
const rewritten = yield* providerRewritten(batch, yield* state(queue.slice(0, at)));
|
|
877
883
|
const limited = yield* exceeding(limits, batch.changes, rewritten.changes, yield* state(queue.slice(0, at + 1)));
|
|
878
|
-
return limited?.
|
|
884
|
+
return limited?.refusal ?? null;
|
|
879
885
|
}
|
|
880
886
|
function typedText(content) {
|
|
881
887
|
if (content === undefined)
|
|
@@ -892,7 +898,7 @@ function touchedLibrary(resulting, touched, composition) {
|
|
|
892
898
|
})
|
|
893
899
|
.sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0));
|
|
894
900
|
const contents = new Map(entries.map(({ path, content }) => [path, content]));
|
|
895
|
-
const binaries = new Set(
|
|
901
|
+
const binaries = new Set(Array.from(entriesIn(resulting.binaries), ([path]) => path));
|
|
896
902
|
return validatedCollection(() => Promise.resolve({ entries, next: null }), async (paths) => {
|
|
897
903
|
const known = new Map();
|
|
898
904
|
const pace = paced();
|
|
@@ -901,7 +907,7 @@ function touchedLibrary(resulting, touched, composition) {
|
|
|
901
907
|
if (binaries.has(path))
|
|
902
908
|
known.set(path, { type: undefined, binary: true });
|
|
903
909
|
else if (content !== undefined && content !== null) {
|
|
904
|
-
const read = parse(content, path);
|
|
910
|
+
const read = parse(content, path, definitionsAt(composition, path));
|
|
905
911
|
if (read.ok)
|
|
906
912
|
known.set(path, { type: read.document.type ?? 'any', binary: false });
|
|
907
913
|
await pace(content.length + ROW_WEIGHT);
|
|
@@ -939,6 +945,7 @@ async function judging(visible, resulting, composition, validate, removed, touch
|
|
|
939
945
|
retryable: false,
|
|
940
946
|
file: verdict.file,
|
|
941
947
|
...(verdict.location === undefined ? {} : { location: verdict.location }),
|
|
948
|
+
...(verdict.line === undefined ? {} : { line: verdict.line }),
|
|
942
949
|
...(verdict.referrers === undefined ? {} : { referrers: verdict.referrers }),
|
|
943
950
|
},
|
|
944
951
|
};
|
|
@@ -954,8 +961,6 @@ function placed(reference) {
|
|
|
954
961
|
const key = roomPathOf(reference.handle, reference.path);
|
|
955
962
|
if (reference.kind === 'link')
|
|
956
963
|
return `link ${key}`;
|
|
957
|
-
if (reference.kind === 'item')
|
|
958
|
-
return `section ${reference.section} ${key}`;
|
|
959
964
|
const pointer = reference.pointer
|
|
960
965
|
.split('/')
|
|
961
966
|
.map((segment) => (ARRAY_INDEX.test(segment) ? '-' : segment))
|
|
@@ -965,10 +970,11 @@ function placed(reference) {
|
|
|
965
970
|
function roomReferencesIn(path, content, composition) {
|
|
966
971
|
if (content === undefined)
|
|
967
972
|
return [];
|
|
968
|
-
const
|
|
973
|
+
const definitions = definitionsAt(composition, path);
|
|
974
|
+
const parsed = parse(content, path, definitions);
|
|
969
975
|
if (!parsed.ok)
|
|
970
976
|
return [];
|
|
971
|
-
return roomReferences(
|
|
977
|
+
return roomReferences(parsed.document, definitions(parsed.document.type));
|
|
972
978
|
}
|
|
973
979
|
function* admittedRooms(changes, visible, resulting, composition) {
|
|
974
980
|
const from = new Map();
|
|
@@ -986,7 +992,7 @@ function* admittedRooms(changes, visible, resulting, composition) {
|
|
|
986
992
|
continue;
|
|
987
993
|
if (held.has(placed(reference)))
|
|
988
994
|
continue;
|
|
989
|
-
return refuse('unresolved-reference', path, reference.
|
|
995
|
+
return refuse('unresolved-reference', path, reference.pointer);
|
|
990
996
|
}
|
|
991
997
|
yield ROW_WEIGHT + now.length;
|
|
992
998
|
}
|
|
@@ -1004,7 +1010,10 @@ function validationOf(validated) {
|
|
|
1004
1010
|
};
|
|
1005
1011
|
}
|
|
1006
1012
|
async function batching(input) {
|
|
1007
|
-
const
|
|
1013
|
+
const relative = relativeChanges(input);
|
|
1014
|
+
if (relative.kind === 'refused')
|
|
1015
|
+
return relative;
|
|
1016
|
+
const { changes } = relative;
|
|
1008
1017
|
const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
|
|
1009
1018
|
if (structural !== undefined && input.unmodeled) {
|
|
1010
1019
|
return refuse('model-unavailable', structural.path);
|
|
@@ -1014,12 +1023,12 @@ async function batching(input) {
|
|
|
1014
1023
|
const rewritten = await stepped(rewrites(changes, input.visible));
|
|
1015
1024
|
if (rewritten.kind === 'refused')
|
|
1016
1025
|
return rewritten;
|
|
1017
|
-
const refusal = guard(changes, input,
|
|
1026
|
+
const refusal = guard(changes, input, WRITE) ?? unapplied(changes, input.visible, input.composition);
|
|
1018
1027
|
if (refusal !== null)
|
|
1019
1028
|
return refusal;
|
|
1020
1029
|
const preconditions = await stepped(preconditionsOf(changes, input.visible, input.bases));
|
|
1021
1030
|
const predicted = await stepped(anticipated(changes, preconditions, rewritten.changes, input.visible));
|
|
1022
|
-
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));
|
|
1023
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))]);
|
|
1024
1033
|
if (judged.kind === 'refused')
|
|
1025
1034
|
return judged;
|
|
@@ -1041,7 +1050,7 @@ async function batching(input) {
|
|
|
1041
1050
|
}),
|
|
1042
1051
|
};
|
|
1043
1052
|
}
|
|
1044
|
-
|
|
1053
|
+
const MOVE_DOCUMENTS = 'move-documents';
|
|
1045
1054
|
function heldAt(visible, path) {
|
|
1046
1055
|
return visible.documents[path] !== undefined || visible.binaries[path] !== undefined;
|
|
1047
1056
|
}
|
|
@@ -1050,7 +1059,7 @@ function arrivingText(path, to, source, moved) {
|
|
|
1050
1059
|
const document = source.visible.collection.document(path);
|
|
1051
1060
|
if (document === undefined)
|
|
1052
1061
|
return content;
|
|
1053
|
-
const definition =
|
|
1062
|
+
const definition = definitionsAt(source.composition, path)(document.type);
|
|
1054
1063
|
const changes = crossed(document, definition, path, (named, href) => {
|
|
1055
1064
|
const at = moved.get(named);
|
|
1056
1065
|
if (at !== undefined)
|
|
@@ -1059,13 +1068,14 @@ function arrivingText(path, to, source, moved) {
|
|
|
1059
1068
|
});
|
|
1060
1069
|
if (changes.length === 0)
|
|
1061
1070
|
return content;
|
|
1062
|
-
const applied = apply(document, changes);
|
|
1071
|
+
const applied = apply(document, changes, definition);
|
|
1063
1072
|
return applied.outcome === 'applied' ? serialize(applied.document) : content;
|
|
1064
1073
|
}
|
|
1065
1074
|
function arrivalOf({ path, to }, source, moved) {
|
|
1066
|
-
const { documents, revisions, fileIds, changes, binaries } = source.visible;
|
|
1075
|
+
const { documents, revisions, fileIds, created, changes, binaries } = source.visible;
|
|
1067
1076
|
const kept = {
|
|
1068
1077
|
...(fileIds[path] === undefined ? {} : { fileId: fileIds[path] }),
|
|
1078
|
+
...(created[path] === undefined ? {} : { created: created[path] }),
|
|
1069
1079
|
...(changes[path] === undefined ? {} : { change: changes[path] }),
|
|
1070
1080
|
};
|
|
1071
1081
|
const heads = {
|
|
@@ -1092,10 +1102,8 @@ function arrivalOf({ path, to }, source, moved) {
|
|
|
1092
1102
|
}
|
|
1093
1103
|
async function moving(input) {
|
|
1094
1104
|
const { source, destination, moves } = input;
|
|
1095
|
-
if (moves.length === 0)
|
|
1096
|
-
return refuse('no-change');
|
|
1097
1105
|
const removals = moves.map(({ path }) => ({ kind: 'remove', path }));
|
|
1098
|
-
const leaves = guard(removals, { ...source, local: input.local, bases: [] },
|
|
1106
|
+
const leaves = guard(removals, { ...source, local: input.local, bases: [] }, MOVE_OUT);
|
|
1099
1107
|
if (leaves !== null)
|
|
1100
1108
|
return leaves;
|
|
1101
1109
|
const uploading = moves.find(({ path }) => source.visible.binaries[path]?.state === 'pending');
|
|
@@ -1103,7 +1111,7 @@ async function moving(input) {
|
|
|
1103
1111
|
return refuse('unknown-revision', uploading.path);
|
|
1104
1112
|
const moved = new Map(moves.map(({ path, to }) => [path, to]));
|
|
1105
1113
|
const arrivals = moves.map((move) => arrivalOf(move, source, moved));
|
|
1106
|
-
const arrives = guard(arrivals, { ...destination, local: input.local, bases: [] },
|
|
1114
|
+
const arrives = guard(arrivals, { ...destination, local: input.local, bases: [] }, WRITE);
|
|
1107
1115
|
if (arrives !== null)
|
|
1108
1116
|
return arrives;
|
|
1109
1117
|
const bound = input.providerLimits.find((limit) => limit.name === MOVE_DOCUMENTS)?.value;
|
|
@@ -1132,7 +1140,7 @@ async function moving(input) {
|
|
|
1132
1140
|
between,
|
|
1133
1141
|
};
|
|
1134
1142
|
const effective = await stepped(leavingRewritten(leavingBatch, source.visible));
|
|
1135
|
-
const left = await stepped(overlaying(source.visible, [effective]));
|
|
1143
|
+
const left = await stepped(overlaying(source.visible, [effective], source.composition));
|
|
1136
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))]);
|
|
1137
1145
|
if (sourceJudged.kind === 'refused')
|
|
1138
1146
|
return sourceJudged;
|
|
@@ -1144,7 +1152,7 @@ async function moving(input) {
|
|
|
1144
1152
|
...written,
|
|
1145
1153
|
between,
|
|
1146
1154
|
};
|
|
1147
|
-
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch]));
|
|
1155
|
+
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch], destination.composition));
|
|
1148
1156
|
const destinationJudged = await judging(destination.visible, arrived, destination.composition, destination.validate, new Set(), moves.map(({ to }) => to));
|
|
1149
1157
|
if (destinationJudged.kind === 'refused')
|
|
1150
1158
|
return destinationJudged;
|
|
@@ -1154,9 +1162,180 @@ async function moving(input) {
|
|
|
1154
1162
|
return limited;
|
|
1155
1163
|
return { kind: 'moved', leaving: immutable(leavingBatch), arriving: immutable(arrivingBatch) };
|
|
1156
1164
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
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;
|
|
1159
1338
|
}
|
|
1160
|
-
export function
|
|
1161
|
-
return
|
|
1339
|
+
export function planSave(ask, room, into) {
|
|
1340
|
+
return into === undefined ? plannedWithin(ask, room) : plannedMove(ask, room, into);
|
|
1162
1341
|
}
|