@agentshouse/mdruntime 0.1.0-c069136dcb4e8 → 0.1.0-c076b00ebf42a
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 +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 +108 -85
- 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 +20 -60
- package/dist/bootstrap.d.ts +9 -5
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +204 -85
- 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 +29 -22
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +36 -20
- package/dist/collection.d.ts +26 -4
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +19 -2
- package/dist/compile.d.ts +8 -6
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +286 -336
- package/dist/connection.d.ts +5 -142
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +110 -4970
- 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 +150 -0
- package/dist/conversation-live.d.ts.map +1 -0
- package/dist/conversation-live.js +259 -0
- package/dist/conversation-row.d.ts +35 -0
- package/dist/conversation-row.d.ts.map +1 -0
- package/dist/conversation-row.js +55 -0
- package/dist/conversation.d.ts +65 -44
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +999 -881
- 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 -0
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +4 -0
- package/dist/entry.d.ts +2 -2
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +18 -8
- 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 +199 -0
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +2 -1
- package/dist/holdings.d.ts +29 -0
- package/dist/holdings.d.ts.map +1 -0
- package/dist/holdings.js +95 -0
- package/dist/host.d.ts +21 -7
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +77 -41
- 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 +60 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +537 -0
- package/dist/lifecycle.d.ts +7 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +278 -0
- package/dist/listing.d.ts +52 -0
- package/dist/listing.d.ts.map +1 -0
- package/dist/listing.js +152 -0
- package/dist/live.d.ts +9 -6
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +118 -36
- 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 +1 -1
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +10 -13
- package/dist/memory.d.ts +15 -6
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +178 -89
- package/dist/messages.d.ts +35 -5
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +108 -36
- package/dist/model.d.ts +2 -10
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -14
- 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 +28 -13
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +185 -81
- 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 +11 -1
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +26 -2
- 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 +1 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +22 -0
- package/dist/preparation.d.ts +41 -0
- package/dist/preparation.d.ts.map +1 -0
- package/dist/preparation.js +314 -0
- package/dist/presence.d.ts +58 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +233 -0
- package/dist/queries.d.ts +16 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +308 -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 +16 -15
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +248 -147
- package/dist/react.d.ts +17 -3
- 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 +9 -6
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +3 -5
- package/dist/reads.d.ts +39 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +178 -0
- package/dist/recovery.d.ts +3 -1
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +38 -8
- package/dist/refresh.d.ts +2 -1
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +31 -3
- 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 +11 -16
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +64 -30
- 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 +7 -8
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +113 -111
- 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 +16 -22
- 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 -86
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +344 -174
- package/dist/saves.d.ts +32 -0
- package/dist/saves.d.ts.map +1 -0
- package/dist/saves.js +409 -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 -6
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +13 -95
- package/dist/shown.d.ts +33 -0
- package/dist/shown.d.ts.map +1 -0
- package/dist/shown.js +309 -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 +0 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +0 -6
- package/dist/store/installs.d.ts +30 -0
- package/dist/store/installs.d.ts.map +1 -0
- package/dist/store/installs.js +40 -0
- package/dist/store/readiness.d.ts +11 -0
- package/dist/store/readiness.d.ts.map +1 -0
- package/dist/store/readiness.js +68 -0
- package/dist/store/records.d.ts +217 -0
- package/dist/store/records.d.ts.map +1 -0
- package/dist/store/records.js +464 -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/rooms-shown.d.ts +8 -0
- package/dist/store/rooms-shown.d.ts.map +1 -0
- package/dist/store/rooms-shown.js +13 -0
- package/dist/store/space.d.ts +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 +113 -144
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +171 -364
- package/dist/stored.d.ts +72 -35
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +340 -197
- package/dist/surface.d.ts +89 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +222 -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 +124 -30
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +242 -62
- package/dist/tallies.d.ts +18 -0
- package/dist/tallies.d.ts.map +1 -0
- package/dist/tallies.js +128 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +9 -12
- 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 +58 -9
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +21 -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 -370
- 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 -156
package/dist/save.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { apply, ARRAY_INDEX, ATTACHMENT, links, parse, pointerSegments, references, relative, relativize, retarget, roomReferences, serialize, target, } from '@agentshouse/mdmodel';
|
|
1
|
+
import { apply, ARRAY_INDEX, ATTACHMENT, create, equalValue, links, parse, pointerSegments, references, relative, relativize, retarget, roomReferences, serialize, target, } from '@agentshouse/mdmodel';
|
|
3
2
|
import { immutable, leaving, queuedFile, departure, } from './change.js';
|
|
4
|
-
import { collectionOf, documentsOf, governing, } from './collection.js';
|
|
5
|
-
import { changeBatchIdentity } from './identity.js';
|
|
6
|
-
import { overlaying } from './overlay.js';
|
|
7
|
-
import { ancestors
|
|
8
|
-
import { sorting } from './shared.js';
|
|
3
|
+
import { collectionOf, definitionsAt, documentsOf, governing, 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';
|
|
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 = {
|
|
@@ -332,15 +348,9 @@ function guard(changes, input, removing) {
|
|
|
332
348
|
const occupied = (path) => input.folders.has(path) ? path : (ancestors(path).find((one) => existing.has(one)) ?? null);
|
|
333
349
|
for (const [index, change] of changes.entries()) {
|
|
334
350
|
for (const path of affected(change)) {
|
|
335
|
-
const
|
|
336
|
-
if (
|
|
337
|
-
return refuse(
|
|
338
|
-
if (covered(input.providerOwnedPaths, normalized)) {
|
|
339
|
-
return refuse('provider-owned-path', normalized);
|
|
340
|
-
}
|
|
341
|
-
const operation = change.kind === 'remove' ? removing : 'write';
|
|
342
|
-
if (!input.local && !covered(input.operations[operation], normalized))
|
|
343
|
-
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);
|
|
344
354
|
}
|
|
345
355
|
if (change.kind === 'create' || change.kind === 'arrival') {
|
|
346
356
|
const taken = existing.has(change.path) ? change.path : occupied(change.path);
|
|
@@ -401,9 +411,9 @@ function guard(changes, input, removing) {
|
|
|
401
411
|
return null;
|
|
402
412
|
}
|
|
403
413
|
function unrevised(input, path) {
|
|
404
|
-
return
|
|
414
|
+
return !input.local && !input.queued.has(path) && input.visible.revisions[path] === undefined;
|
|
405
415
|
}
|
|
406
|
-
|
|
416
|
+
function queuedDocuments(queue) {
|
|
407
417
|
const paths = new Set();
|
|
408
418
|
for (const batch of queue) {
|
|
409
419
|
for (const change of batch.changes) {
|
|
@@ -539,56 +549,69 @@ function* exceeding(limits, changes, effective, resulting) {
|
|
|
539
549
|
function shellRelative(text, path, handle, definition) {
|
|
540
550
|
if (!text.includes(`/rooms/${handle}/`))
|
|
541
551
|
return null;
|
|
542
|
-
const parsed = parse(text, path);
|
|
552
|
+
const parsed = parse(text, path, definition);
|
|
543
553
|
if (!parsed.ok)
|
|
544
554
|
return null;
|
|
545
555
|
const result = relativize(parsed.document, path, handle, definition(parsed.document.type));
|
|
546
556
|
return result.outcome === 'applied' && result.document.content !== text ? result.document : null;
|
|
547
557
|
}
|
|
548
558
|
function relativeField(path, name, value, handle, definition) {
|
|
549
|
-
|
|
550
|
-
if (definition === undefined ||
|
|
551
|
-
!opened.ok ||
|
|
552
|
-
!JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
559
|
+
if (definition === undefined || !JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
553
560
|
return value;
|
|
554
|
-
const held = apply(
|
|
561
|
+
const held = apply(create(definition.name, definition), [{ kind: 'field', name, value, prior: undefined }], definition);
|
|
555
562
|
if (held.outcome !== 'applied')
|
|
556
563
|
return value;
|
|
557
564
|
const result = relativize(held.document, path, handle, definition);
|
|
558
565
|
return result.outcome === 'applied' ? result.document.fields[name] : value;
|
|
559
566
|
}
|
|
560
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) {
|
|
561
578
|
const { handle, composition, visible } = input;
|
|
562
|
-
const typed = (path) => (type) => definitionOf(governing(composition, path), type);
|
|
563
579
|
const body = (path, text) => shellRelative(text, path, handle, () => undefined)?.content ?? text;
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
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
|
+
};
|
|
589
612
|
}
|
|
590
613
|
function unqueueable(file) {
|
|
591
|
-
return
|
|
614
|
+
return refuse('unknown-revision', file);
|
|
592
615
|
}
|
|
593
616
|
function isPart(change) {
|
|
594
617
|
return change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble';
|
|
@@ -596,8 +619,29 @@ function isPart(change) {
|
|
|
596
619
|
function touched(change) {
|
|
597
620
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
598
621
|
}
|
|
599
|
-
function partsApplied(content, path, changes) {
|
|
600
|
-
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);
|
|
601
645
|
if (!parsed.ok)
|
|
602
646
|
return null;
|
|
603
647
|
const document = parsed.document;
|
|
@@ -620,12 +664,11 @@ function partsApplied(content, path, changes) {
|
|
|
620
664
|
else
|
|
621
665
|
parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
|
|
622
666
|
}
|
|
623
|
-
|
|
624
|
-
return applied.outcome === 'applied' ? serialize(applied.document) : null;
|
|
667
|
+
return apply(document, [...parts.values()], definitions(document.type));
|
|
625
668
|
}
|
|
626
|
-
function settledParts(changes, deferred) {
|
|
669
|
+
function settledParts(changes, deferred, composition) {
|
|
627
670
|
for (const [held, parts] of deferred) {
|
|
628
|
-
const content = partsApplied(held.content, held.path, parts);
|
|
671
|
+
const content = partsApplied(held.content, held.path, parts, composition);
|
|
629
672
|
if (content === null)
|
|
630
673
|
return held.path;
|
|
631
674
|
changes[changes.indexOf(held)] = { ...held, content };
|
|
@@ -700,7 +743,9 @@ function* relinked(batch, move, before, after, appending) {
|
|
|
700
743
|
for (const change of derived.changes) {
|
|
701
744
|
if (change === move)
|
|
702
745
|
continue;
|
|
703
|
-
const unsettled = isPart(change)
|
|
746
|
+
const unsettled = isPart(change)
|
|
747
|
+
? null
|
|
748
|
+
: settledParts(changes, deferred, after.collection.composition);
|
|
704
749
|
if (unsettled !== null)
|
|
705
750
|
return unqueueable(unsettled);
|
|
706
751
|
const outcome = folded(changes, change, appending, deferred);
|
|
@@ -711,7 +756,7 @@ function* relinked(batch, move, before, after, appending) {
|
|
|
711
756
|
if (outcome !== 'skipped')
|
|
712
757
|
changed = true;
|
|
713
758
|
}
|
|
714
|
-
const unsettled = settledParts(changes, deferred);
|
|
759
|
+
const unsettled = settledParts(changes, deferred, after.collection.composition);
|
|
715
760
|
if (unsettled !== null)
|
|
716
761
|
return unqueueable(unsettled);
|
|
717
762
|
if (!changed)
|
|
@@ -772,11 +817,10 @@ function withdrawnFile(batch, change, at) {
|
|
|
772
817
|
];
|
|
773
818
|
return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
|
|
774
819
|
}
|
|
775
|
-
|
|
820
|
+
function* requeuing(queue, changes, visible, changeable, state) {
|
|
776
821
|
const next = [...queue];
|
|
777
822
|
const origins = new Map();
|
|
778
823
|
const withdrawn = new Map();
|
|
779
|
-
const owners = new Set();
|
|
780
824
|
const queued = new Set();
|
|
781
825
|
const kept = [];
|
|
782
826
|
const originOf = (batch) => origins.get(batch) ?? batch.id;
|
|
@@ -822,7 +866,6 @@ export function* requeuing(queue, changes, visible, changeable, state) {
|
|
|
822
866
|
if (replaced !== null)
|
|
823
867
|
origins.set(replaced, origin);
|
|
824
868
|
}
|
|
825
|
-
owners.add(originOf(owner));
|
|
826
869
|
next.splice(index, following.length, ...rebuilt.filter((batch) => batch !== null));
|
|
827
870
|
queued.add(change.path);
|
|
828
871
|
}
|
|
@@ -832,62 +875,16 @@ export function* requeuing(queue, changes, visible, changeable, state) {
|
|
|
832
875
|
changes: kept,
|
|
833
876
|
queued,
|
|
834
877
|
withdrawn,
|
|
835
|
-
owners,
|
|
836
878
|
};
|
|
837
879
|
}
|
|
838
|
-
function
|
|
839
|
-
return precondition.kind === 'field' ||
|
|
840
|
-
precondition.kind === 'section' ||
|
|
841
|
-
precondition.kind === 'preamble'
|
|
842
|
-
? identity(precondition)
|
|
843
|
-
: `path ${precondition.path}`;
|
|
844
|
-
}
|
|
845
|
-
export function joined(queue, owner, extra) {
|
|
846
|
-
const later = new Set(queue.slice(queue.indexOf(owner) + 1).flatMap((batch) => batch.changes.flatMap(touched)));
|
|
847
|
-
const clash = extra.changes.flatMap(touched).find((path) => later.has(path));
|
|
848
|
-
if (clash !== undefined)
|
|
849
|
-
return unqueueable(clash);
|
|
850
|
-
const changes = [...owner.changes];
|
|
851
|
-
const deferred = new Map();
|
|
852
|
-
for (const change of extra.changes) {
|
|
853
|
-
const unsettled = isPart(change) ? null : settledParts(changes, deferred);
|
|
854
|
-
if (unsettled !== null)
|
|
855
|
-
return unqueueable(unsettled);
|
|
856
|
-
if (folded(changes, change, true, deferred) === 'refused')
|
|
857
|
-
return unqueueable(change.path);
|
|
858
|
-
}
|
|
859
|
-
const unsettled = settledParts(changes, deferred);
|
|
860
|
-
if (unsettled !== null)
|
|
861
|
-
return unqueueable(unsettled);
|
|
862
|
-
const held = new Set(owner.preconditions.map(covering));
|
|
863
|
-
const written = new Set(owner.changes.flatMap((change) => change.kind === 'create' || change.kind === 'replace' ? [change.path] : []));
|
|
864
|
-
const preconditions = [
|
|
865
|
-
...owner.preconditions,
|
|
866
|
-
...extra.preconditions.filter((precondition) => {
|
|
867
|
-
const key = covering(precondition);
|
|
868
|
-
return (!held.has(key) && (key === `path ${precondition.path}` || !written.has(precondition.path)));
|
|
869
|
-
}),
|
|
870
|
-
];
|
|
871
|
-
const batch = immutable({
|
|
872
|
-
...owner,
|
|
873
|
-
...(extra.validation === undefined ? {} : { validation: extra.validation }),
|
|
874
|
-
id: changeBatchIdentity(),
|
|
875
|
-
changes,
|
|
876
|
-
preconditions,
|
|
877
|
-
});
|
|
878
|
-
return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
|
|
879
|
-
}
|
|
880
|
-
export function* limiting(queue, batch, limits, state) {
|
|
880
|
+
function* limiting(queue, batch, limits, state) {
|
|
881
881
|
const at = queue.indexOf(batch);
|
|
882
882
|
const rewritten = yield* providerRewritten(batch, yield* state(queue.slice(0, at)));
|
|
883
883
|
const limited = yield* exceeding(limits, batch.changes, rewritten.changes, yield* state(queue.slice(0, at + 1)));
|
|
884
|
-
return limited?.
|
|
884
|
+
return limited?.refusal ?? null;
|
|
885
885
|
}
|
|
886
|
-
function
|
|
887
|
-
|
|
888
|
-
return false;
|
|
889
|
-
const parsed = parse(content);
|
|
890
|
-
return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
|
|
886
|
+
function typedContent(content) {
|
|
887
|
+
return content !== undefined && typedText(parse(content));
|
|
891
888
|
}
|
|
892
889
|
function touchedLibrary(resulting, touched, composition) {
|
|
893
890
|
const entries = touched
|
|
@@ -898,7 +895,7 @@ function touchedLibrary(resulting, touched, composition) {
|
|
|
898
895
|
})
|
|
899
896
|
.sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0));
|
|
900
897
|
const contents = new Map(entries.map(({ path, content }) => [path, content]));
|
|
901
|
-
const binaries = new Set(
|
|
898
|
+
const binaries = new Set(Array.from(entriesIn(resulting.binaries), ([path]) => path));
|
|
902
899
|
return validatedCollection(() => Promise.resolve({ entries, next: null }), async (paths) => {
|
|
903
900
|
const known = new Map();
|
|
904
901
|
const pace = paced();
|
|
@@ -907,9 +904,9 @@ function touchedLibrary(resulting, touched, composition) {
|
|
|
907
904
|
if (binaries.has(path))
|
|
908
905
|
known.set(path, { type: undefined, binary: true });
|
|
909
906
|
else if (content !== undefined && content !== null) {
|
|
910
|
-
const
|
|
911
|
-
if (
|
|
912
|
-
known.set(path, { type
|
|
907
|
+
const type = textType(parse(content, path, definitionsAt(composition, path)));
|
|
908
|
+
if (type !== undefined)
|
|
909
|
+
known.set(path, { type, binary: false });
|
|
913
910
|
await pace(content.length + ROW_WEIGHT);
|
|
914
911
|
}
|
|
915
912
|
}
|
|
@@ -927,7 +924,7 @@ async function judging(visible, resulting, composition, validate, removed, touch
|
|
|
927
924
|
for (const path of touched) {
|
|
928
925
|
const before = visible.documents[path];
|
|
929
926
|
const after = resulting.documents[path];
|
|
930
|
-
if (
|
|
927
|
+
if (typedContent(before) || typedContent(after))
|
|
931
928
|
typedPaths.push(path);
|
|
932
929
|
await pace((before?.length ?? 0) + (after?.length ?? 0) + ROW_WEIGHT);
|
|
933
930
|
}
|
|
@@ -945,6 +942,7 @@ async function judging(visible, resulting, composition, validate, removed, touch
|
|
|
945
942
|
retryable: false,
|
|
946
943
|
file: verdict.file,
|
|
947
944
|
...(verdict.location === undefined ? {} : { location: verdict.location }),
|
|
945
|
+
...(verdict.line === undefined ? {} : { line: verdict.line }),
|
|
948
946
|
...(verdict.referrers === undefined ? {} : { referrers: verdict.referrers }),
|
|
949
947
|
},
|
|
950
948
|
};
|
|
@@ -960,8 +958,6 @@ function placed(reference) {
|
|
|
960
958
|
const key = roomPathOf(reference.handle, reference.path);
|
|
961
959
|
if (reference.kind === 'link')
|
|
962
960
|
return `link ${key}`;
|
|
963
|
-
if (reference.kind === 'item')
|
|
964
|
-
return `section ${reference.section} ${key}`;
|
|
965
961
|
const pointer = reference.pointer
|
|
966
962
|
.split('/')
|
|
967
963
|
.map((segment) => (ARRAY_INDEX.test(segment) ? '-' : segment))
|
|
@@ -971,10 +967,11 @@ function placed(reference) {
|
|
|
971
967
|
function roomReferencesIn(path, content, composition) {
|
|
972
968
|
if (content === undefined)
|
|
973
969
|
return [];
|
|
974
|
-
const
|
|
970
|
+
const definitions = definitionsAt(composition, path);
|
|
971
|
+
const parsed = parse(content, path, definitions);
|
|
975
972
|
if (!parsed.ok)
|
|
976
973
|
return [];
|
|
977
|
-
return roomReferences(
|
|
974
|
+
return roomReferences(parsed.document, definitions(parsed.document.type));
|
|
978
975
|
}
|
|
979
976
|
function* admittedRooms(changes, visible, resulting, composition) {
|
|
980
977
|
const from = new Map();
|
|
@@ -992,7 +989,7 @@ function* admittedRooms(changes, visible, resulting, composition) {
|
|
|
992
989
|
continue;
|
|
993
990
|
if (held.has(placed(reference)))
|
|
994
991
|
continue;
|
|
995
|
-
return refuse('unresolved-reference', path, reference.
|
|
992
|
+
return refuse('unresolved-reference', path, reference.pointer);
|
|
996
993
|
}
|
|
997
994
|
yield ROW_WEIGHT + now.length;
|
|
998
995
|
}
|
|
@@ -1010,7 +1007,10 @@ function validationOf(validated) {
|
|
|
1010
1007
|
};
|
|
1011
1008
|
}
|
|
1012
1009
|
async function batching(input) {
|
|
1013
|
-
const
|
|
1010
|
+
const relative = relativeChanges(input);
|
|
1011
|
+
if (relative.kind === 'refused')
|
|
1012
|
+
return relative;
|
|
1013
|
+
const { changes } = relative;
|
|
1014
1014
|
const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
|
|
1015
1015
|
if (structural !== undefined && input.unmodeled) {
|
|
1016
1016
|
return refuse('model-unavailable', structural.path);
|
|
@@ -1020,12 +1020,12 @@ async function batching(input) {
|
|
|
1020
1020
|
const rewritten = await stepped(rewrites(changes, input.visible));
|
|
1021
1021
|
if (rewritten.kind === 'refused')
|
|
1022
1022
|
return rewritten;
|
|
1023
|
-
const refusal = guard(changes, input,
|
|
1023
|
+
const refusal = guard(changes, input, WRITE) ?? unapplied(changes, input.visible, input.composition);
|
|
1024
1024
|
if (refusal !== null)
|
|
1025
1025
|
return refusal;
|
|
1026
1026
|
const preconditions = await stepped(preconditionsOf(changes, input.visible, input.bases));
|
|
1027
1027
|
const predicted = await stepped(anticipated(changes, preconditions, rewritten.changes, input.visible));
|
|
1028
|
-
const resulting = await stepped(overlaying(input.visible, [{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted }]));
|
|
1028
|
+
const resulting = await stepped(overlaying(input.visible, [{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted }], input.composition));
|
|
1029
1029
|
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))]);
|
|
1030
1030
|
if (judged.kind === 'refused')
|
|
1031
1031
|
return judged;
|
|
@@ -1047,7 +1047,7 @@ async function batching(input) {
|
|
|
1047
1047
|
}),
|
|
1048
1048
|
};
|
|
1049
1049
|
}
|
|
1050
|
-
|
|
1050
|
+
const MOVE_DOCUMENTS = 'move-documents';
|
|
1051
1051
|
function heldAt(visible, path) {
|
|
1052
1052
|
return visible.documents[path] !== undefined || visible.binaries[path] !== undefined;
|
|
1053
1053
|
}
|
|
@@ -1056,7 +1056,7 @@ function arrivingText(path, to, source, moved) {
|
|
|
1056
1056
|
const document = source.visible.collection.document(path);
|
|
1057
1057
|
if (document === undefined)
|
|
1058
1058
|
return content;
|
|
1059
|
-
const definition =
|
|
1059
|
+
const definition = definitionsAt(source.composition, path)(document.type);
|
|
1060
1060
|
const changes = crossed(document, definition, path, (named, href) => {
|
|
1061
1061
|
const at = moved.get(named);
|
|
1062
1062
|
if (at !== undefined)
|
|
@@ -1065,13 +1065,14 @@ function arrivingText(path, to, source, moved) {
|
|
|
1065
1065
|
});
|
|
1066
1066
|
if (changes.length === 0)
|
|
1067
1067
|
return content;
|
|
1068
|
-
const applied = apply(document, changes);
|
|
1068
|
+
const applied = apply(document, changes, definition);
|
|
1069
1069
|
return applied.outcome === 'applied' ? serialize(applied.document) : content;
|
|
1070
1070
|
}
|
|
1071
1071
|
function arrivalOf({ path, to }, source, moved) {
|
|
1072
|
-
const { documents, revisions, fileIds, changes, binaries } = source.visible;
|
|
1072
|
+
const { documents, revisions, fileIds, created, changes, binaries } = source.visible;
|
|
1073
1073
|
const kept = {
|
|
1074
1074
|
...(fileIds[path] === undefined ? {} : { fileId: fileIds[path] }),
|
|
1075
|
+
...(created[path] === undefined ? {} : { created: created[path] }),
|
|
1075
1076
|
...(changes[path] === undefined ? {} : { change: changes[path] }),
|
|
1076
1077
|
};
|
|
1077
1078
|
const heads = {
|
|
@@ -1098,10 +1099,8 @@ function arrivalOf({ path, to }, source, moved) {
|
|
|
1098
1099
|
}
|
|
1099
1100
|
async function moving(input) {
|
|
1100
1101
|
const { source, destination, moves } = input;
|
|
1101
|
-
if (moves.length === 0)
|
|
1102
|
-
return refuse('no-change');
|
|
1103
1102
|
const removals = moves.map(({ path }) => ({ kind: 'remove', path }));
|
|
1104
|
-
const leaves = guard(removals, { ...source, local: input.local, bases: [] },
|
|
1103
|
+
const leaves = guard(removals, { ...source, local: input.local, bases: [] }, MOVE_OUT);
|
|
1105
1104
|
if (leaves !== null)
|
|
1106
1105
|
return leaves;
|
|
1107
1106
|
const uploading = moves.find(({ path }) => source.visible.binaries[path]?.state === 'pending');
|
|
@@ -1109,7 +1108,7 @@ async function moving(input) {
|
|
|
1109
1108
|
return refuse('unknown-revision', uploading.path);
|
|
1110
1109
|
const moved = new Map(moves.map(({ path, to }) => [path, to]));
|
|
1111
1110
|
const arrivals = moves.map((move) => arrivalOf(move, source, moved));
|
|
1112
|
-
const arrives = guard(arrivals, { ...destination, local: input.local, bases: [] },
|
|
1111
|
+
const arrives = guard(arrivals, { ...destination, local: input.local, bases: [] }, WRITE);
|
|
1113
1112
|
if (arrives !== null)
|
|
1114
1113
|
return arrives;
|
|
1115
1114
|
const bound = input.providerLimits.find((limit) => limit.name === MOVE_DOCUMENTS)?.value;
|
|
@@ -1138,7 +1137,7 @@ async function moving(input) {
|
|
|
1138
1137
|
between,
|
|
1139
1138
|
};
|
|
1140
1139
|
const effective = await stepped(leavingRewritten(leavingBatch, source.visible));
|
|
1141
|
-
const left = await stepped(overlaying(source.visible, [effective]));
|
|
1140
|
+
const left = await stepped(overlaying(source.visible, [effective], source.composition));
|
|
1142
1141
|
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))]);
|
|
1143
1142
|
if (sourceJudged.kind === 'refused')
|
|
1144
1143
|
return sourceJudged;
|
|
@@ -1150,7 +1149,7 @@ async function moving(input) {
|
|
|
1150
1149
|
...written,
|
|
1151
1150
|
between,
|
|
1152
1151
|
};
|
|
1153
|
-
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch]));
|
|
1152
|
+
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch], destination.composition));
|
|
1154
1153
|
const destinationJudged = await judging(destination.visible, arrived, destination.composition, destination.validate, new Set(), moves.map(({ to }) => to));
|
|
1155
1154
|
if (destinationJudged.kind === 'refused')
|
|
1156
1155
|
return destinationJudged;
|
|
@@ -1160,9 +1159,180 @@ async function moving(input) {
|
|
|
1160
1159
|
return limited;
|
|
1161
1160
|
return { kind: 'moved', leaving: immutable(leavingBatch), arriving: immutable(arrivingBatch) };
|
|
1162
1161
|
}
|
|
1163
|
-
|
|
1164
|
-
|
|
1162
|
+
function unreadable(saved, visible, composition) {
|
|
1163
|
+
const path = saved.find(({ path: at, parts }) => (parts.fields !== undefined ||
|
|
1164
|
+
parts.sections !== undefined ||
|
|
1165
|
+
parts.preamble !== undefined) &&
|
|
1166
|
+
typedContent(visible.documents[at]) &&
|
|
1167
|
+
governing(composition, at).status === 'unavailable')?.path;
|
|
1168
|
+
return path === undefined ? null : refuse('model-unavailable', path);
|
|
1169
|
+
}
|
|
1170
|
+
function changedUnderneath(saved, visible) {
|
|
1171
|
+
const parts = saved.flatMap(({ path, parts: held }) => changedParts(path, held, visible));
|
|
1172
|
+
return parts.length === 0
|
|
1173
|
+
? null
|
|
1174
|
+
: { kind: 'refused', refusal: { code: 'changed-underneath', retryable: false, parts } };
|
|
1175
|
+
}
|
|
1176
|
+
function sideOf(room, visible) {
|
|
1177
|
+
const { record } = room;
|
|
1178
|
+
return {
|
|
1179
|
+
room: record.locator,
|
|
1180
|
+
handle: record.declaration.handle,
|
|
1181
|
+
baseRoomVersion: record.roomVersion,
|
|
1182
|
+
composition: room.composition,
|
|
1183
|
+
rule: room.rule,
|
|
1184
|
+
visible,
|
|
1185
|
+
folders: room.folders,
|
|
1186
|
+
queued: queuedDocuments(room.queue),
|
|
1187
|
+
unmodeled: !room.modelled,
|
|
1188
|
+
validate: room.validate,
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
function carriedDrafts(ask, from, into) {
|
|
1192
|
+
return new Map(ask.saved.flatMap(({ path, parts }) => {
|
|
1193
|
+
const record = from.drafts.get(path);
|
|
1194
|
+
if (parts.move === undefined || record === undefined || ask.consumed.has(path))
|
|
1195
|
+
return [];
|
|
1196
|
+
const to = moveTarget(parts.move);
|
|
1197
|
+
if (into.drafts.has(to))
|
|
1198
|
+
return [];
|
|
1199
|
+
const locator = into.record.locator;
|
|
1200
|
+
return [[path, { ...record, locator, path: to, revision: roomDraftRevision() }]];
|
|
1201
|
+
}));
|
|
1202
|
+
}
|
|
1203
|
+
async function plannedMove(ask, source, into) {
|
|
1204
|
+
const destination = into.record.locator;
|
|
1205
|
+
const mixed = ask.saved.find(({ parts }) => Object.keys(parts).length !== 1 || parts.move?.room !== destination);
|
|
1206
|
+
if (mixed !== undefined)
|
|
1207
|
+
return refuse('mixed-room-move', mixed.path);
|
|
1208
|
+
const leavingVisible = await stepped(source.replay(source.queue));
|
|
1209
|
+
const changed = changedUnderneath(ask.saved, leavingVisible);
|
|
1210
|
+
if (changed !== null)
|
|
1211
|
+
return changed;
|
|
1212
|
+
const moves = ask.saved.map(({ path, parts }) => ({ path, to: moveTarget(parts.move) }));
|
|
1213
|
+
const built = await moving({
|
|
1214
|
+
source: sideOf(source, leavingVisible),
|
|
1215
|
+
destination: sideOf(into, await stepped(into.replay(into.queue))),
|
|
1216
|
+
moves,
|
|
1217
|
+
providerLimits: ask.providerLimits,
|
|
1218
|
+
local: ask.local,
|
|
1219
|
+
...(ask.writtenBy === undefined ? {} : { writtenBy: ask.writtenBy }),
|
|
1220
|
+
});
|
|
1221
|
+
if (built.kind === 'refused')
|
|
1222
|
+
return built;
|
|
1223
|
+
const { id } = built.leaving;
|
|
1224
|
+
const carried = carriedDrafts(ask, source, into);
|
|
1225
|
+
return {
|
|
1226
|
+
kind: 'planned',
|
|
1227
|
+
identifier: id,
|
|
1228
|
+
rooms: [
|
|
1229
|
+
{
|
|
1230
|
+
locator: source.record.locator,
|
|
1231
|
+
queue: [...source.queue, built.leaving],
|
|
1232
|
+
saved: new Map([[id, ask.saved]]),
|
|
1233
|
+
consumed: [...ask.consumed, ...carried.keys()],
|
|
1234
|
+
carried: [],
|
|
1235
|
+
},
|
|
1236
|
+
{
|
|
1237
|
+
locator: destination,
|
|
1238
|
+
queue: [...into.queue, built.arriving],
|
|
1239
|
+
saved: new Map([
|
|
1240
|
+
[id, [...carried.values()].map(({ path }) => ({ path, parts: {} }))],
|
|
1241
|
+
]),
|
|
1242
|
+
consumed: [],
|
|
1243
|
+
carried: [...carried.values()],
|
|
1244
|
+
},
|
|
1245
|
+
],
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
async function plannedWithin(ask, room) {
|
|
1249
|
+
const { record } = room;
|
|
1250
|
+
const visible = await stepped(room.replay(room.queue));
|
|
1251
|
+
const changed = unreadable(ask.saved, visible, room.composition) ?? changedUnderneath(ask.saved, visible);
|
|
1252
|
+
if (changed !== null)
|
|
1253
|
+
return changed;
|
|
1254
|
+
const parts = ask.saved.map(({ path, parts: held }) => changesOf(path, held));
|
|
1255
|
+
const changes = parts.flatMap(({ changes: made }) => made);
|
|
1256
|
+
const requeue = await stepped(requeuing(room.queue, changes, visible, (batch) => room.changeable.has(batch.id), room.replay));
|
|
1257
|
+
if (requeue.kind === 'refused')
|
|
1258
|
+
return requeue;
|
|
1259
|
+
const foreign = [...requeue.withdrawn.keys()].some((origin) => {
|
|
1260
|
+
const writer = room.queue.find((batch) => batch.id === origin)?.writtenBy;
|
|
1261
|
+
return writer?.app !== ask.writtenBy?.app || writer?.release !== ask.writtenBy?.release;
|
|
1262
|
+
});
|
|
1263
|
+
if (foreign)
|
|
1264
|
+
return unqueueable([...requeue.queued].at(-1));
|
|
1265
|
+
const input = {
|
|
1266
|
+
local: ask.local,
|
|
1267
|
+
baseRoomVersion: record.roomVersion,
|
|
1268
|
+
composition: room.composition,
|
|
1269
|
+
rule: room.rule,
|
|
1270
|
+
providerLimits: ask.providerLimits,
|
|
1271
|
+
handle: record.declaration.handle,
|
|
1272
|
+
visible,
|
|
1273
|
+
folders: room.folders,
|
|
1274
|
+
changes,
|
|
1275
|
+
bases: parts.flatMap(({ bases }) => bases),
|
|
1276
|
+
queued: queuedDocuments(room.queue),
|
|
1277
|
+
writtenBy: ask.writtenBy,
|
|
1278
|
+
unmodeled: !room.modelled,
|
|
1279
|
+
validate: room.validate,
|
|
1280
|
+
};
|
|
1281
|
+
const rewritten = requeue.withdrawn.size > 0;
|
|
1282
|
+
if (rewritten) {
|
|
1283
|
+
const whole = await batching({
|
|
1284
|
+
...input,
|
|
1285
|
+
providerLimits: [],
|
|
1286
|
+
queued: new Set([...input.queued, ...requeue.queued]),
|
|
1287
|
+
});
|
|
1288
|
+
if (whole.kind === 'refused')
|
|
1289
|
+
return whole;
|
|
1290
|
+
}
|
|
1291
|
+
const own = !rewritten
|
|
1292
|
+
? await batching(input)
|
|
1293
|
+
: requeue.changes.length === 0
|
|
1294
|
+
? null
|
|
1295
|
+
: await batching({
|
|
1296
|
+
...input,
|
|
1297
|
+
visible: await stepped(room.replay(requeue.queue)),
|
|
1298
|
+
changes: requeue.changes,
|
|
1299
|
+
queued: queuedDocuments(requeue.queue),
|
|
1300
|
+
});
|
|
1301
|
+
if (own?.kind === 'refused')
|
|
1302
|
+
return own;
|
|
1303
|
+
const queue = own === null ? requeue.queue : [...requeue.queue, own.batch];
|
|
1304
|
+
const saved = new Map();
|
|
1305
|
+
for (const [origin, rebuilt] of requeue.withdrawn) {
|
|
1306
|
+
if (rebuilt === null)
|
|
1307
|
+
continue;
|
|
1308
|
+
const limited = await stepped(limiting(queue, rebuilt, ask.providerLimits, room.replay));
|
|
1309
|
+
if (limited !== null)
|
|
1310
|
+
return { kind: 'refused', refusal: limited };
|
|
1311
|
+
saved.set(rebuilt.id, withdrawnDrafts(room.saved.get(origin) ?? [], changes));
|
|
1312
|
+
}
|
|
1313
|
+
if (own !== null) {
|
|
1314
|
+
saved.set(own.batch.id, ask.saved.filter(({ path }) => !requeue.queued.has(path)));
|
|
1315
|
+
}
|
|
1316
|
+
const carried = carriedDrafts(ask, room, room);
|
|
1317
|
+
return {
|
|
1318
|
+
kind: 'planned',
|
|
1319
|
+
identifier: own?.batch.id ??
|
|
1320
|
+
[...requeue.withdrawn].map(([origin, rebuilt]) => rebuilt?.id ?? origin).at(-1),
|
|
1321
|
+
rooms: [
|
|
1322
|
+
{
|
|
1323
|
+
locator: record.locator,
|
|
1324
|
+
queue,
|
|
1325
|
+
saved,
|
|
1326
|
+
consumed: [...ask.consumed, ...carried.keys()],
|
|
1327
|
+
carried: [...carried.values()],
|
|
1328
|
+
},
|
|
1329
|
+
],
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
export function destination(locator, saved) {
|
|
1333
|
+
const into = saved.find(({ parts }) => parts.move?.room !== undefined && parts.move.room !== locator);
|
|
1334
|
+
return into?.parts.move?.room ?? null;
|
|
1165
1335
|
}
|
|
1166
|
-
export function
|
|
1167
|
-
return
|
|
1336
|
+
export function planSave(ask, room, into) {
|
|
1337
|
+
return into === undefined ? plannedWithin(ask, room) : plannedMove(ask, room, into);
|
|
1168
1338
|
}
|