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