@agentshouse/mdruntime 0.1.0-c1db74d14e5fa → 0.1.0-c1f45f238a4a6
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 +19 -16
- package/dist/acceptance.d.ts +3 -0
- package/dist/acceptance.d.ts.map +1 -0
- package/dist/acceptance.js +309 -0
- package/dist/actions.d.ts +20 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +251 -0
- package/dist/adapter.d.ts +166 -121
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +7 -10
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +40 -41
- package/dist/binary.d.ts +0 -5
- package/dist/binary.d.ts.map +1 -1
- package/dist/binary.js +1 -1
- package/dist/bootstrap.d.ts +31 -11
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +309 -109
- 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 +12 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +64 -0
- package/dist/change-set.d.ts +13 -16
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +17 -88
- package/dist/change.d.ts +32 -1
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +14 -3
- package/dist/chat-types.d.ts +5 -0
- package/dist/chat-types.d.ts.map +1 -0
- package/dist/chat-types.js +13 -0
- package/dist/client.d.ts +38 -12
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +105 -15
- package/dist/collection.d.ts +29 -21
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +48 -290
- package/dist/compile.d.ts +16 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +711 -0
- package/dist/connection.d.ts +7 -129
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +99 -3343
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/{chat/context.js → context.js} +1 -4
- package/dist/conversation-live.d.ts +135 -0
- package/dist/conversation-live.d.ts.map +1 -0
- package/dist/conversation-live.js +247 -0
- package/dist/conversation-row.d.ts +33 -0
- package/dist/conversation-row.d.ts.map +1 -0
- package/dist/conversation-row.js +53 -0
- package/dist/conversation.d.ts +54 -45
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +981 -691
- package/dist/declaration.d.ts +10 -0
- package/dist/declaration.d.ts.map +1 -0
- package/dist/declaration.js +24 -0
- package/dist/digest.d.ts +7 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +113 -0
- package/dist/discovery.d.ts +10 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +177 -0
- package/dist/durability.d.ts +16 -4
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +5 -2
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +33 -28
- package/dist/expectation.d.ts +4 -2
- package/dist/expectation.d.ts.map +1 -1
- package/dist/expectation.js +33 -11
- package/dist/failure.d.ts +1 -0
- package/dist/failure.d.ts.map +1 -1
- package/dist/failure.js +3 -0
- package/dist/figures.d.ts +49 -0
- package/dist/figures.d.ts.map +1 -0
- package/dist/figures.js +197 -0
- package/dist/file.d.ts +2 -19
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +7 -122
- 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 +24 -10
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +116 -27
- package/dist/identity.d.ts +0 -2
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -6
- package/dist/index.d.ts +35 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -1
- package/dist/install.d.ts +55 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +543 -0
- package/dist/job.d.ts +5 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +7 -0
- package/dist/lifecycle.d.ts +7 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +271 -0
- package/dist/listing.d.ts +52 -0
- package/dist/listing.d.ts.map +1 -0
- package/dist/listing.js +168 -0
- package/dist/live.d.ts +23 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +165 -0
- package/dist/locks.d.ts +9 -0
- package/dist/locks.d.ts.map +1 -0
- package/dist/locks.js +62 -0
- package/dist/log.d.ts +1 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/lost.d.ts +5 -0
- package/dist/lost.d.ts.map +1 -0
- package/dist/lost.js +61 -0
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +218 -0
- package/dist/memory.d.ts +22 -8
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +253 -105
- package/dist/messages.d.ts +49 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +148 -0
- package/dist/model.d.ts +11 -13
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +7 -24
- package/dist/models.d.ts +10 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +170 -0
- package/dist/name.d.ts +4 -0
- package/dist/name.d.ts.map +1 -0
- package/dist/name.js +122 -0
- package/dist/observation.d.ts +17 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +107 -17
- package/dist/opened.d.ts +22 -0
- package/dist/opened.d.ts.map +1 -0
- package/dist/opened.js +107 -0
- package/dist/opening.d.ts +4 -0
- package/dist/opening.d.ts.map +1 -0
- package/dist/opening.js +59 -0
- package/dist/outcome.d.ts +49 -8
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +42 -0
- package/dist/overlay.d.ts +5 -5
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +127 -166
- package/dist/parts.d.ts +11 -0
- package/dist/parts.d.ts.map +1 -0
- package/dist/parts.js +21 -0
- package/dist/paths.d.ts +2 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +26 -0
- package/dist/preparation.d.ts +20 -0
- package/dist/preparation.d.ts.map +1 -0
- package/dist/preparation.js +233 -0
- package/dist/queries.d.ts +16 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +272 -0
- package/dist/query.d.ts +27 -186
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -464
- package/dist/{chat/questions.d.ts → questions.d.ts} +5 -2
- package/dist/questions.d.ts.map +1 -0
- package/dist/{chat/questions.js → questions.js} +13 -2
- package/dist/queue.d.ts +32 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +402 -0
- package/dist/reach.d.ts +19 -9
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +291 -102
- package/dist/react.d.ts +16 -4
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +17 -18
- package/dist/{chat/read.d.ts → read.d.ts} +2 -0
- package/dist/read.d.ts.map +1 -0
- package/dist/{chat/read.js → read.js} +4 -0
- package/dist/readiness.d.ts +13 -17
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +2 -8
- package/dist/reads.d.ts +37 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +174 -0
- package/dist/recovery.d.ts +3 -2
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +43 -8
- package/dist/refresh.d.ts +17 -14
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +145 -54
- package/dist/refusal.d.ts +5 -0
- package/dist/refusal.d.ts.map +1 -0
- package/dist/refusal.js +57 -0
- package/dist/replica.d.ts +50 -33
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +338 -189
- package/dist/room-draft.d.ts +39 -9
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +186 -86
- 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 +31 -29
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +60 -118
- package/dist/runtime.d.ts +32 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1 -0
- package/dist/save.d.ts +54 -16
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +557 -209
- package/dist/saves.d.ts +32 -0
- package/dist/saves.d.ts.map +1 -0
- package/dist/saves.js +679 -0
- package/dist/search.d.ts +15 -77
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +10 -184
- package/dist/seed.d.ts +25 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +145 -0
- package/dist/shared.d.ts +6 -7
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +75 -104
- package/dist/shown.d.ts +32 -0
- package/dist/shown.d.ts.map +1 -0
- package/dist/shown.js +299 -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 +20 -0
- package/dist/socket/lanes.d.ts.map +1 -0
- package/dist/socket/lanes.js +77 -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/rooms.d.ts +41 -0
- package/dist/socket/rooms.d.ts.map +1 -0
- package/dist/socket/rooms.js +90 -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 +69 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +175 -0
- package/dist/source.d.ts +61 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +82 -0
- package/dist/status.d.ts +4 -5
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -7
- package/dist/store/fences.d.ts +16 -0
- package/dist/store/fences.d.ts.map +1 -0
- package/dist/store/fences.js +26 -0
- package/dist/store/installs.d.ts +30 -0
- package/dist/store/installs.d.ts.map +1 -0
- package/dist/store/installs.js +34 -0
- package/dist/store/readiness.d.ts +10 -0
- package/dist/store/readiness.d.ts.map +1 -0
- package/dist/store/readiness.js +52 -0
- package/dist/store/records.d.ts +210 -0
- package/dist/store/records.d.ts.map +1 -0
- package/dist/store/records.js +428 -0
- package/dist/store/room-held.d.ts +8 -0
- package/dist/store/room-held.d.ts.map +1 -0
- package/dist/store/room-held.js +17 -0
- package/dist/store/space.d.ts +11 -0
- package/dist/store/space.d.ts.map +1 -0
- package/dist/store/space.js +41 -0
- package/dist/store/watchers.d.ts +17 -0
- package/dist/store/watchers.d.ts.map +1 -0
- package/dist/store/watchers.js +56 -0
- package/dist/store/writes.d.ts +24 -0
- package/dist/store/writes.d.ts.map +1 -0
- package/dist/store/writes.js +125 -0
- package/dist/store.d.ts +106 -148
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +136 -700
- package/dist/stored.d.ts +156 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +649 -0
- package/dist/surface.d.ts +88 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +224 -0
- package/dist/sync.d.ts +19 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +530 -0
- package/dist/tables.d.ts +412 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +737 -0
- package/dist/tallies.d.ts +18 -0
- package/dist/tallies.d.ts.map +1 -0
- package/dist/tallies.js +129 -0
- package/dist/transfer.d.ts +0 -5
- package/dist/transfer.d.ts.map +1 -1
- package/dist/turn.d.ts +5 -3
- package/dist/turn.d.ts.map +1 -1
- package/dist/turn.js +51 -21
- package/dist/validation.d.ts +29 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +104 -0
- package/dist/windows.d.ts +11 -0
- package/dist/windows.d.ts.map +1 -0
- package/dist/windows.js +16 -0
- package/dist/wire.d.ts +183 -37
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +63 -34
- package/package.json +19 -19
- package/AGENTS.md +0 -300
- package/dist/acquisition.d.ts +0 -14
- package/dist/acquisition.d.ts.map +0 -1
- package/dist/acquisition.js +0 -24
- package/dist/chat/acts.d.ts +0 -33
- package/dist/chat/acts.d.ts.map +0 -1
- package/dist/chat/acts.js +0 -36
- package/dist/chat/context.d.ts +0 -3
- package/dist/chat/context.d.ts.map +0 -1
- package/dist/chat/input.d.ts +0 -9
- package/dist/chat/input.d.ts.map +0 -1
- package/dist/chat/input.js +0 -99
- package/dist/chat/kit.d.ts +0 -60
- package/dist/chat/kit.d.ts.map +0 -1
- package/dist/chat/kit.js +0 -123
- package/dist/chat/lasting.d.ts +0 -6
- package/dist/chat/lasting.d.ts.map +0 -1
- package/dist/chat/lasting.js +0 -19
- package/dist/chat/list.d.ts +0 -26
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -62
- package/dist/chat/markdown.d.ts +0 -20
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -188
- package/dist/chat/message.d.ts +0 -6
- package/dist/chat/message.d.ts.map +0 -1
- package/dist/chat/message.js +0 -192
- package/dist/chat/messages.d.ts +0 -21
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -151
- package/dist/chat/name.d.ts +0 -6
- package/dist/chat/name.d.ts.map +0 -1
- package/dist/chat/name.js +0 -37
- package/dist/chat/opened.d.ts +0 -11
- package/dist/chat/opened.d.ts.map +0 -1
- package/dist/chat/opened.js +0 -17
- package/dist/chat/overlay.d.ts +0 -31
- package/dist/chat/overlay.d.ts.map +0 -1
- package/dist/chat/overlay.js +0 -97
- package/dist/chat/parts.d.ts +0 -57
- package/dist/chat/parts.d.ts.map +0 -1
- package/dist/chat/parts.js +0 -119
- package/dist/chat/process.d.ts +0 -20
- package/dist/chat/process.d.ts.map +0 -1
- package/dist/chat/process.js +0 -32
- package/dist/chat/provider.d.ts +0 -63
- package/dist/chat/provider.d.ts.map +0 -1
- package/dist/chat/provider.js +0 -620
- package/dist/chat/question.d.ts +0 -9
- package/dist/chat/question.d.ts.map +0 -1
- package/dist/chat/question.js +0 -80
- package/dist/chat/questions.d.ts.map +0 -1
- package/dist/chat/read.d.ts.map +0 -1
- package/dist/chat/ready.d.ts +0 -33
- package/dist/chat/ready.d.ts.map +0 -1
- package/dist/chat/ready.js +0 -91
- package/dist/chat/style.d.ts +0 -4
- package/dist/chat/style.d.ts.map +0 -1
- package/dist/chat/style.js +0 -302
- package/dist/chat.d.ts +0 -14
- package/dist/chat.d.ts.map +0 -1
- package/dist/chat.js +0 -8
- package/dist/fence.d.ts +0 -6
- package/dist/fence.d.ts.map +0 -1
- package/dist/fence.js +0 -10
- package/dist/read-side.d.ts +0 -25
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -639
package/dist/save.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { available, changedCleanly, collectionOf, documentsOf, governing, referring, validateCollection, } from './collection.js';
|
|
1
|
+
import { apply, ARRAY_INDEX, ATTACHMENT, create, links, parse, pointerSegments, references, relative, relativize, retarget, roomReferences, serialize, target, } from '@agentshouse/mdmodel';
|
|
2
|
+
import { immutable, leaving, queuedFile, departure, } from './change.js';
|
|
3
|
+
import { collectionOf, definitionsAt, documentsOf, governing, } from './collection.js';
|
|
5
4
|
import { changeBatchIdentity } from './identity.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { overlaying } from './overlay.js';
|
|
6
|
+
import { ancestors } from './paths.js';
|
|
7
|
+
import { entriesIn, sorting } from './shared.js';
|
|
8
|
+
import { paced, ROW_WEIGHT, stepped } from './turn.js';
|
|
9
|
+
import { validatedCollection } from './validation.js';
|
|
8
10
|
function refuse(code, file, location) {
|
|
9
11
|
return {
|
|
10
12
|
kind: 'refused',
|
|
@@ -17,10 +19,7 @@ function refuse(code, file, location) {
|
|
|
17
19
|
};
|
|
18
20
|
}
|
|
19
21
|
function repointed(document, definition, from, to, moves, settled) {
|
|
20
|
-
const
|
|
21
|
-
if (!parsed.ok)
|
|
22
|
-
return [];
|
|
23
|
-
let next = parsed.document;
|
|
22
|
+
const changes = [];
|
|
24
23
|
const names = new Set();
|
|
25
24
|
for (const { pointer, value } of definition === undefined
|
|
26
25
|
? []
|
|
@@ -32,38 +31,39 @@ function repointed(document, definition, from, to, moves, settled) {
|
|
|
32
31
|
const destination = moves.get(named) ?? named;
|
|
33
32
|
if (target(to, value) === destination)
|
|
34
33
|
continue;
|
|
35
|
-
|
|
34
|
+
changes.push({
|
|
36
35
|
kind: 'reference',
|
|
37
36
|
pointer,
|
|
38
37
|
value: retarget(value, relative(to, destination)),
|
|
39
38
|
prior: value,
|
|
40
39
|
});
|
|
41
|
-
if (applied.outcome !== 'applied')
|
|
42
|
-
continue;
|
|
43
|
-
next = applied.document;
|
|
44
40
|
names.add(name);
|
|
45
41
|
}
|
|
42
|
+
if (changes.length === 0)
|
|
43
|
+
return [];
|
|
44
|
+
const applied = apply(document, changes, definition);
|
|
45
|
+
if (applied.outcome !== 'applied')
|
|
46
|
+
return [];
|
|
47
|
+
const next = applied.document;
|
|
46
48
|
return [...names].map((name) => ({ kind: 'field', path: from, name, value: next.fields[name] }));
|
|
47
49
|
}
|
|
48
|
-
function edited(document,
|
|
49
|
-
|
|
50
|
+
function edited(document, changes, definition) {
|
|
51
|
+
const parts = new Map();
|
|
50
52
|
for (const change of changes) {
|
|
51
|
-
if (change.
|
|
52
|
-
|
|
53
|
-
if (change.kind
|
|
54
|
-
|
|
55
|
-
const applied = apply(next, change.kind === 'preamble'
|
|
56
|
-
? { kind: 'preamble', value: change.value, prior: next.preamble }
|
|
57
|
-
: {
|
|
53
|
+
if (change.kind === 'preamble')
|
|
54
|
+
parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
|
|
55
|
+
if (change.kind === 'section')
|
|
56
|
+
parts.set(`section ${change.name}`, {
|
|
58
57
|
kind: 'section',
|
|
59
58
|
name: change.name,
|
|
60
59
|
value: change.value,
|
|
61
|
-
prior:
|
|
60
|
+
prior: document.sections[change.name] ?? '',
|
|
62
61
|
});
|
|
63
|
-
if (applied.outcome === 'applied')
|
|
64
|
-
next = applied.document;
|
|
65
62
|
}
|
|
66
|
-
|
|
63
|
+
if (parts.size === 0)
|
|
64
|
+
return document;
|
|
65
|
+
const applied = apply(document, [...parts.values()], definition);
|
|
66
|
+
return applied.outcome === 'applied' ? applied.document : document;
|
|
67
67
|
}
|
|
68
68
|
function bodies(path, before, after) {
|
|
69
69
|
const changes = [];
|
|
@@ -77,8 +77,8 @@ function bodies(path, before, after) {
|
|
|
77
77
|
}
|
|
78
78
|
return changes;
|
|
79
79
|
}
|
|
80
|
-
function relinks(path, moved, before, moves) {
|
|
81
|
-
|
|
80
|
+
function relinks(path, moved, before, moves, definition) {
|
|
81
|
+
const changes = [];
|
|
82
82
|
for (const link of links(before, path)) {
|
|
83
83
|
const destination = moves.get(link.target) ?? link.target;
|
|
84
84
|
if (destination === link.target && moved === path)
|
|
@@ -86,20 +86,23 @@ function relinks(path, moved, before, moves) {
|
|
|
86
86
|
const to = relative(moved, destination);
|
|
87
87
|
if (to === relative(path, link.target))
|
|
88
88
|
continue;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
89
|
+
changes.push({ kind: 'link', href: link.href, to });
|
|
90
|
+
}
|
|
91
|
+
if (changes.length === 0)
|
|
92
|
+
return { kind: 'changes', changes: [] };
|
|
93
|
+
const applied = apply(before, changes, definition);
|
|
94
|
+
if (applied.outcome === 'refuse') {
|
|
95
|
+
return {
|
|
96
|
+
kind: 'refused',
|
|
97
|
+
refusal: {
|
|
98
|
+
code: applied.refusal.rule,
|
|
99
|
+
retryable: false,
|
|
100
|
+
file: path,
|
|
101
|
+
...(applied.refusal.location === undefined ? {} : { location: applied.refusal.location }),
|
|
102
|
+
},
|
|
103
|
+
};
|
|
102
104
|
}
|
|
105
|
+
const after = applied.document;
|
|
103
106
|
if (after === before)
|
|
104
107
|
return { kind: 'changes', changes: [] };
|
|
105
108
|
if (before.type === undefined) {
|
|
@@ -107,19 +110,23 @@ function relinks(path, moved, before, moves) {
|
|
|
107
110
|
}
|
|
108
111
|
return { kind: 'changes', changes: bodies(path, before, after) };
|
|
109
112
|
}
|
|
110
|
-
function
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
function partKey(change) {
|
|
114
|
+
return change.kind === 'section' ? `section ${change.name}` : change.kind;
|
|
115
|
+
}
|
|
116
|
+
const NAMES_A_SKIP_TESTS = 64;
|
|
117
|
+
const ENCODED = /[%&\\]/u;
|
|
118
|
+
function movedNames(moves) {
|
|
119
|
+
const names = new Set([...moves.keys()].map((path) => path.slice(path.lastIndexOf('/') + 1)));
|
|
120
|
+
return names.size > NAMES_A_SKIP_TESTS ? null : [...names];
|
|
118
121
|
}
|
|
119
|
-
function
|
|
122
|
+
function cannotLink(text, names) {
|
|
123
|
+
return names !== null && !ENCODED.test(text) && !names.some((name) => text.includes(name));
|
|
124
|
+
}
|
|
125
|
+
function* rewrites(changes, visible) {
|
|
120
126
|
const moves = new Map();
|
|
121
127
|
const settled = new Set();
|
|
122
|
-
|
|
128
|
+
const parts = new Map();
|
|
129
|
+
for (const [index, change] of changes.entries()) {
|
|
123
130
|
if (change.kind === 'move')
|
|
124
131
|
moves.set(change.path, change.to);
|
|
125
132
|
if (change.kind === 'remove' ||
|
|
@@ -130,54 +137,146 @@ function rewrites(changes, visible) {
|
|
|
130
137
|
}
|
|
131
138
|
if (change.kind === 'field')
|
|
132
139
|
settled.add(`${change.path} ${change.name}`);
|
|
140
|
+
if (isPart(change)) {
|
|
141
|
+
const held = parts.get(change.path);
|
|
142
|
+
if (held === undefined)
|
|
143
|
+
parts.set(change.path, [index]);
|
|
144
|
+
else
|
|
145
|
+
held.push(index);
|
|
146
|
+
}
|
|
133
147
|
}
|
|
134
148
|
if (moves.size === 0)
|
|
135
149
|
return { kind: 'changes', changes };
|
|
150
|
+
const names = movedNames(moves);
|
|
136
151
|
const merged = [...changes];
|
|
137
152
|
const added = [];
|
|
138
|
-
for (const [path, document] of documentsOf(visible.collection)) {
|
|
153
|
+
for (const [path, document] of yield* documentsOf(visible.collection)) {
|
|
139
154
|
if (settled.has(path))
|
|
140
155
|
continue;
|
|
141
156
|
const moved = moves.get(path) ?? path;
|
|
142
|
-
const definition =
|
|
143
|
-
|
|
144
|
-
|
|
157
|
+
const definition = definitionsAt(visible.collection.composition, path)(document.type);
|
|
158
|
+
const repointing = repointed(document, definition, path, moved, moves, settled);
|
|
159
|
+
added.push(...repointing);
|
|
160
|
+
const own = parts.get(path) ?? [];
|
|
161
|
+
const relinked = moved === path && own.length === 0 && cannotLink(document.content, names)
|
|
162
|
+
? { kind: 'changes', changes: [] }
|
|
163
|
+
: relinks(path, moved, edited(document, own.map((index) => merged[index]), definition), moves, definition);
|
|
145
164
|
if (relinked.kind === 'refused')
|
|
146
165
|
return relinked;
|
|
166
|
+
const keyed = new Map();
|
|
167
|
+
for (const index of own) {
|
|
168
|
+
const key = partKey(merged[index]);
|
|
169
|
+
if (!keyed.has(key))
|
|
170
|
+
keyed.set(key, index);
|
|
171
|
+
}
|
|
147
172
|
for (const change of relinked.changes) {
|
|
148
173
|
if (change.kind === 'replace') {
|
|
149
|
-
|
|
174
|
+
for (const index of own) {
|
|
175
|
+
const kind = merged[index]?.kind;
|
|
176
|
+
if (kind === 'section' || kind === 'preamble')
|
|
177
|
+
merged[index] = null;
|
|
178
|
+
}
|
|
150
179
|
added.push(change);
|
|
151
180
|
continue;
|
|
152
181
|
}
|
|
153
|
-
const index =
|
|
154
|
-
|
|
155
|
-
(change.kind !== 'section' || (item.kind === 'section' && item.name === change.name)));
|
|
156
|
-
if (index === -1)
|
|
182
|
+
const index = keyed.get(partKey(change));
|
|
183
|
+
if (index === undefined)
|
|
157
184
|
added.push(change);
|
|
158
185
|
else
|
|
159
186
|
merged[index] = change;
|
|
160
187
|
}
|
|
188
|
+
const text = visible.collection.text[path].length;
|
|
189
|
+
const rewritten = repointing.length > 0 || relinked.changes.length > 0;
|
|
190
|
+
yield ROW_WEIGHT + text + (rewritten ? text : 0);
|
|
161
191
|
}
|
|
162
|
-
return {
|
|
192
|
+
return {
|
|
193
|
+
kind: 'changes',
|
|
194
|
+
changes: [...added, ...merged.filter((change) => change !== null)],
|
|
195
|
+
};
|
|
163
196
|
}
|
|
164
|
-
function anticipated(changes, preconditions, rewritten, visible) {
|
|
165
|
-
const
|
|
197
|
+
function* anticipated(changes, preconditions, rewritten, visible) {
|
|
198
|
+
const given = new Set(changes);
|
|
199
|
+
const derived = rewritten.filter((change) => !given.has(change));
|
|
166
200
|
return {
|
|
167
201
|
changes: rewritten,
|
|
168
|
-
preconditions: [...preconditionsOf(derived, visible, []), ...preconditions],
|
|
202
|
+
preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...preconditions],
|
|
169
203
|
};
|
|
170
204
|
}
|
|
171
|
-
export function providerRewritten(batch, visible) {
|
|
172
|
-
const rewritten = rewrites(batch.changes, visible);
|
|
205
|
+
export function* providerRewritten(batch, visible) {
|
|
206
|
+
const rewritten = yield* rewrites(batch.changes, visible);
|
|
173
207
|
if (rewritten.kind === 'refused')
|
|
174
208
|
return batch;
|
|
175
209
|
return {
|
|
176
210
|
...batch,
|
|
177
|
-
...anticipated(batch.changes, batch.preconditions, rewritten.changes, visible),
|
|
211
|
+
...(yield* anticipated(batch.changes, batch.preconditions, rewritten.changes, visible)),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function crossed(document, definition, path, to) {
|
|
215
|
+
const changes = [];
|
|
216
|
+
for (const link of links(document, path)) {
|
|
217
|
+
const written = to(link.target, link.href);
|
|
218
|
+
if (written !== undefined)
|
|
219
|
+
changes.push({ kind: 'link', href: link.href, to: written });
|
|
220
|
+
}
|
|
221
|
+
for (const { pointer, value } of definition === undefined
|
|
222
|
+
? []
|
|
223
|
+
: references(document, definition)) {
|
|
224
|
+
const named = target(path, value);
|
|
225
|
+
const written = named === null ? undefined : to(named, value);
|
|
226
|
+
if (written !== undefined) {
|
|
227
|
+
changes.push({ kind: 'reference', pointer, value: retarget(value, written), prior: value });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return changes;
|
|
231
|
+
}
|
|
232
|
+
function rewritten(path, document, changes, definition) {
|
|
233
|
+
if (changes.length === 0)
|
|
234
|
+
return [];
|
|
235
|
+
const applied = apply(document, changes, definition);
|
|
236
|
+
if (applied.outcome !== 'applied')
|
|
237
|
+
return [];
|
|
238
|
+
const after = applied.document;
|
|
239
|
+
if (document.type === undefined)
|
|
240
|
+
return [{ kind: 'replace', path, content: serialize(after) }];
|
|
241
|
+
const names = new Set(changes.flatMap((change) => change.kind === 'reference' ? [pointerSegments(change.pointer)[0] ?? ''] : []));
|
|
242
|
+
return [
|
|
243
|
+
...[...names].map((name) => ({ kind: 'field', path, name, value: after.fields[name] })),
|
|
244
|
+
...bodies(path, document, after),
|
|
245
|
+
];
|
|
246
|
+
}
|
|
247
|
+
function roomPathOf(handle, path) {
|
|
248
|
+
return `/rooms/${handle}/${path}`;
|
|
249
|
+
}
|
|
250
|
+
export function* leavingRewritten(batch, visible) {
|
|
251
|
+
const between = batch.between;
|
|
252
|
+
if (between === undefined)
|
|
253
|
+
return batch;
|
|
254
|
+
const moved = new Map(between.moves.map(({ path, to }) => [path, roomPathOf(between.destination.handle, to)]));
|
|
255
|
+
const derived = [];
|
|
256
|
+
for (const [path, document] of yield* documentsOf(visible.collection)) {
|
|
257
|
+
if (moved.has(path))
|
|
258
|
+
continue;
|
|
259
|
+
const definition = definitionsAt(visible.collection.composition, path)(document.type);
|
|
260
|
+
derived.push(...rewritten(path, document, crossed(document, definition, path, (named) => moved.get(named)), definition));
|
|
261
|
+
yield ROW_WEIGHT + visible.collection.text[path].length;
|
|
262
|
+
}
|
|
263
|
+
if (derived.length === 0)
|
|
264
|
+
return batch;
|
|
265
|
+
return {
|
|
266
|
+
...batch,
|
|
267
|
+
changes: [...derived, ...batch.changes],
|
|
268
|
+
preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...batch.preconditions],
|
|
178
269
|
};
|
|
179
270
|
}
|
|
180
|
-
function
|
|
271
|
+
export function rewriting(batch, locator) {
|
|
272
|
+
return leaving(batch, locator) || batch.changes.some((change) => change.kind === 'move');
|
|
273
|
+
}
|
|
274
|
+
export function* effectiveIn(batch, locator, visible) {
|
|
275
|
+
return leaving(batch, locator)
|
|
276
|
+
? yield* leavingRewritten(batch, visible)
|
|
277
|
+
: yield* providerRewritten(batch, visible);
|
|
278
|
+
}
|
|
279
|
+
function* incoming(collection, removed, candidates) {
|
|
181
280
|
const found = new Map();
|
|
182
281
|
const note = (target, file) => {
|
|
183
282
|
if (!removed.has(target))
|
|
@@ -186,14 +285,21 @@ function incoming(collection, removed, candidates) {
|
|
|
186
285
|
referrers.add(file);
|
|
187
286
|
found.set(target, referrers);
|
|
188
287
|
};
|
|
189
|
-
const read =
|
|
190
|
-
|
|
191
|
-
|
|
288
|
+
const read = function* (paths) {
|
|
289
|
+
const seen = new Set();
|
|
290
|
+
for (const path of paths) {
|
|
291
|
+
if (seen.has(path))
|
|
292
|
+
continue;
|
|
293
|
+
seen.add(path);
|
|
192
294
|
const document = collection.document(path);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
295
|
+
if (document !== undefined)
|
|
296
|
+
yield [path, document];
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
for (const [path, document] of candidates === null
|
|
300
|
+
? yield* documentsOf(collection)
|
|
301
|
+
: read(candidates)) {
|
|
302
|
+
const definition = definitionsAt(collection.composition, path)(document.type);
|
|
197
303
|
for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
|
|
198
304
|
if (to.includes(ATTACHMENT))
|
|
199
305
|
continue;
|
|
@@ -201,8 +307,9 @@ function incoming(collection, removed, candidates) {
|
|
|
201
307
|
if (named !== null)
|
|
202
308
|
note(named, path);
|
|
203
309
|
}
|
|
310
|
+
yield ROW_WEIGHT + collection.text[path].length;
|
|
204
311
|
}
|
|
205
|
-
const [first] =
|
|
312
|
+
const [first] = yield* sorting(found.keys());
|
|
206
313
|
if (first === undefined)
|
|
207
314
|
return null;
|
|
208
315
|
const every = new Set();
|
|
@@ -215,14 +322,16 @@ function incoming(collection, removed, candidates) {
|
|
|
215
322
|
code: 'referenced-missing-file',
|
|
216
323
|
retryable: false,
|
|
217
324
|
file: first,
|
|
218
|
-
referrers:
|
|
325
|
+
referrers: yield* sorting(every),
|
|
219
326
|
},
|
|
220
327
|
};
|
|
221
328
|
}
|
|
222
329
|
function affected(change) {
|
|
223
330
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
224
331
|
}
|
|
225
|
-
|
|
332
|
+
const WRITE = ['write'];
|
|
333
|
+
const MOVE_OUT = ['write', 'erase'];
|
|
334
|
+
function guard(changes, input, removing) {
|
|
226
335
|
const overridden = new Map();
|
|
227
336
|
const existing = {
|
|
228
337
|
has: (path) => overridden.get(path) ??
|
|
@@ -230,21 +339,17 @@ function guard(changes, input) {
|
|
|
230
339
|
add: (path) => void overridden.set(path, true),
|
|
231
340
|
delete: (path) => void overridden.set(path, false),
|
|
232
341
|
};
|
|
342
|
+
const occupied = (path) => input.folders.has(path) ? path : (ancestors(path).find((one) => existing.has(one)) ?? null);
|
|
233
343
|
for (const [index, change] of changes.entries()) {
|
|
234
344
|
for (const path of affected(change)) {
|
|
235
|
-
const
|
|
236
|
-
if (
|
|
237
|
-
return refuse(
|
|
238
|
-
if (covered(input.providerOwnedPaths, normalized)) {
|
|
239
|
-
return refuse('provider-owned-path', normalized);
|
|
240
|
-
}
|
|
241
|
-
const operation = change.kind === 'remove' ? 'delete' : 'write';
|
|
242
|
-
if (!input.local && !covered(input.operations[operation], normalized))
|
|
243
|
-
return refuse(`${operation}-grant`, normalized);
|
|
345
|
+
const refused = input.rule(path, change.kind === 'remove' ? removing : WRITE);
|
|
346
|
+
if (refused !== null)
|
|
347
|
+
return refuse(refused.code, refused.path);
|
|
244
348
|
}
|
|
245
|
-
if (change.kind === 'create') {
|
|
246
|
-
|
|
247
|
-
|
|
349
|
+
if (change.kind === 'create' || change.kind === 'arrival') {
|
|
350
|
+
const taken = existing.has(change.path) ? change.path : occupied(change.path);
|
|
351
|
+
if (taken !== null)
|
|
352
|
+
return refuse('path-collision', taken);
|
|
248
353
|
existing.add(change.path);
|
|
249
354
|
continue;
|
|
250
355
|
}
|
|
@@ -254,6 +359,9 @@ function guard(changes, input) {
|
|
|
254
359
|
input.bases.some((base) => base.kind === 'absent' && base.path === change.path))) {
|
|
255
360
|
return refuse('path-collision', change.path);
|
|
256
361
|
}
|
|
362
|
+
const taken = occupied(change.path);
|
|
363
|
+
if (taken !== null)
|
|
364
|
+
return refuse('path-collision', taken);
|
|
257
365
|
if (!input.local &&
|
|
258
366
|
existing.has(change.path) &&
|
|
259
367
|
input.visible.revisions[departure(changes, index)] === undefined) {
|
|
@@ -265,8 +373,9 @@ function guard(changes, input) {
|
|
|
265
373
|
if (change.kind === 'move') {
|
|
266
374
|
if (!existing.has(change.path))
|
|
267
375
|
return refuse('path-absent', change.path);
|
|
268
|
-
|
|
269
|
-
|
|
376
|
+
const taken = existing.has(change.to) ? change.to : occupied(change.to);
|
|
377
|
+
if (taken !== null)
|
|
378
|
+
return refuse('path-collision', taken);
|
|
270
379
|
if (unrevised(input, change.path)) {
|
|
271
380
|
return refuse('unknown-revision', change.path);
|
|
272
381
|
}
|
|
@@ -284,7 +393,7 @@ function guard(changes, input) {
|
|
|
284
393
|
continue;
|
|
285
394
|
}
|
|
286
395
|
if (change.kind === 'replace') {
|
|
287
|
-
if (
|
|
396
|
+
if (unrevised(input, change.path)) {
|
|
288
397
|
return refuse('unknown-revision', change.path);
|
|
289
398
|
}
|
|
290
399
|
continue;
|
|
@@ -298,6 +407,22 @@ function guard(changes, input) {
|
|
|
298
407
|
function unrevised(input, path) {
|
|
299
408
|
return (!input.local && input.queued?.has(path) !== true && input.visible.revisions[path] === undefined);
|
|
300
409
|
}
|
|
410
|
+
export function queuedDocuments(queue) {
|
|
411
|
+
const paths = new Set();
|
|
412
|
+
for (const batch of queue) {
|
|
413
|
+
for (const change of batch.changes) {
|
|
414
|
+
if (change.kind === 'binary' || (change.kind === 'arrival' && !('content' in change)))
|
|
415
|
+
continue;
|
|
416
|
+
if (change.kind === 'remove')
|
|
417
|
+
paths.delete(change.path);
|
|
418
|
+
else if (change.kind !== 'move')
|
|
419
|
+
paths.add(change.path);
|
|
420
|
+
else if (paths.delete(change.path))
|
|
421
|
+
paths.add(change.to);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return paths;
|
|
425
|
+
}
|
|
301
426
|
function identity(precondition) {
|
|
302
427
|
const part = precondition.kind === 'field' || precondition.kind === 'section' ? precondition.name : '';
|
|
303
428
|
return `${precondition.kind} ${precondition.path} ${part}`;
|
|
@@ -315,7 +440,7 @@ function sameTarget(precondition, change) {
|
|
|
315
440
|
return ((precondition.kind !== 'field' && precondition.kind !== 'section') ||
|
|
316
441
|
((change.kind === 'field' || change.kind === 'section') && precondition.name === change.name));
|
|
317
442
|
}
|
|
318
|
-
function preconditionsOf(changes, visible, bases) {
|
|
443
|
+
function* preconditionsOf(changes, visible, bases) {
|
|
319
444
|
const preconditions = [];
|
|
320
445
|
const seen = new Set();
|
|
321
446
|
const add = (precondition) => {
|
|
@@ -326,6 +451,7 @@ function preconditionsOf(changes, visible, bases) {
|
|
|
326
451
|
preconditions.push(precondition);
|
|
327
452
|
};
|
|
328
453
|
for (const change of changes) {
|
|
454
|
+
yield ROW_WEIGHT;
|
|
329
455
|
if (change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble') {
|
|
330
456
|
const based = bases.find((base) => sameTarget(base, change));
|
|
331
457
|
if (based !== undefined) {
|
|
@@ -373,10 +499,13 @@ function preconditionsOf(changes, visible, bases) {
|
|
|
373
499
|
function depth(path) {
|
|
374
500
|
return path.split('/').length;
|
|
375
501
|
}
|
|
376
|
-
function
|
|
502
|
+
function* exceeding(limits, changes, effective, resulting) {
|
|
377
503
|
const written = effective.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
378
504
|
const created = changes
|
|
379
|
-
.filter((change) => change.kind === 'create' ||
|
|
505
|
+
.filter((change) => change.kind === 'create' ||
|
|
506
|
+
change.kind === 'move' ||
|
|
507
|
+
change.kind === 'binary' ||
|
|
508
|
+
change.kind === 'arrival')
|
|
380
509
|
.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
381
510
|
for (const limit of limits) {
|
|
382
511
|
const value = limit.value;
|
|
@@ -390,9 +519,13 @@ function exceeded(limits, changes, effective, resulting) {
|
|
|
390
519
|
return refuse('provider-limit', undefined, limit.name);
|
|
391
520
|
}
|
|
392
521
|
if (limit.name === 'document-size') {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
522
|
+
for (const path of written) {
|
|
523
|
+
const text = resulting.documents[path] ?? '';
|
|
524
|
+
if (new TextEncoder().encode(text).byteLength > value) {
|
|
525
|
+
return refuse('provider-limit', path, limit.name);
|
|
526
|
+
}
|
|
527
|
+
yield ROW_WEIGHT + text.length;
|
|
528
|
+
}
|
|
396
529
|
}
|
|
397
530
|
if (limit.name === 'path-length') {
|
|
398
531
|
const long = created.find((path) => path.length > value);
|
|
@@ -410,19 +543,16 @@ function exceeded(limits, changes, effective, resulting) {
|
|
|
410
543
|
function shellRelative(text, path, handle, definition) {
|
|
411
544
|
if (!text.includes(`/rooms/${handle}/`))
|
|
412
545
|
return null;
|
|
413
|
-
const parsed = parse(text, path);
|
|
546
|
+
const parsed = parse(text, path, definition);
|
|
414
547
|
if (!parsed.ok)
|
|
415
548
|
return null;
|
|
416
549
|
const result = relativize(parsed.document, path, handle, definition(parsed.document.type));
|
|
417
550
|
return result.outcome === 'applied' && result.document.content !== text ? result.document : null;
|
|
418
551
|
}
|
|
419
552
|
function relativeField(path, name, value, handle, definition) {
|
|
420
|
-
|
|
421
|
-
if (definition === undefined ||
|
|
422
|
-
!opened.ok ||
|
|
423
|
-
!JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
553
|
+
if (definition === undefined || !JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
424
554
|
return value;
|
|
425
|
-
const held = apply(
|
|
555
|
+
const held = apply(create(definition.name, definition), [{ kind: 'field', name, value, prior: undefined }], definition);
|
|
426
556
|
if (held.outcome !== 'applied')
|
|
427
557
|
return value;
|
|
428
558
|
const result = relativize(held.document, path, handle, definition);
|
|
@@ -430,7 +560,7 @@ function relativeField(path, name, value, handle, definition) {
|
|
|
430
560
|
}
|
|
431
561
|
function relativeChanges(input) {
|
|
432
562
|
const { handle, composition, visible } = input;
|
|
433
|
-
const typed = (path) => (
|
|
563
|
+
const typed = (path) => definitionsAt(composition, path);
|
|
434
564
|
const body = (path, text) => shellRelative(text, path, handle, () => undefined)?.content ?? text;
|
|
435
565
|
return input.changes.map((change) => {
|
|
436
566
|
const definition = typed(change.path);
|
|
@@ -458,17 +588,6 @@ function relativeChanges(input) {
|
|
|
458
588
|
return { ...change, fields, sections };
|
|
459
589
|
});
|
|
460
590
|
}
|
|
461
|
-
function unmodeledType(collection, composition) {
|
|
462
|
-
if (available(composition))
|
|
463
|
-
return false;
|
|
464
|
-
for (const [path, document] of documentsOf(collection)) {
|
|
465
|
-
if (document.type !== undefined &&
|
|
466
|
-
document.type !== 'any' &&
|
|
467
|
-
governing(composition, path).status === 'unavailable')
|
|
468
|
-
return true;
|
|
469
|
-
}
|
|
470
|
-
return false;
|
|
471
|
-
}
|
|
472
591
|
function unqueueable(file) {
|
|
473
592
|
return { kind: 'refused', refusal: { code: 'unknown-revision', retryable: false, file } };
|
|
474
593
|
}
|
|
@@ -478,29 +597,45 @@ function isPart(change) {
|
|
|
478
597
|
function touched(change) {
|
|
479
598
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
480
599
|
}
|
|
481
|
-
function
|
|
482
|
-
const
|
|
600
|
+
function partsApplied(content, path, changes, composition) {
|
|
601
|
+
const definitions = definitionsAt(composition, path);
|
|
602
|
+
const parsed = parse(content, path, definitions);
|
|
483
603
|
if (!parsed.ok)
|
|
484
604
|
return null;
|
|
485
605
|
const document = parsed.document;
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
606
|
+
const parts = new Map();
|
|
607
|
+
for (const change of changes) {
|
|
608
|
+
if (change.kind === 'field')
|
|
609
|
+
parts.set(`field ${change.name}`, {
|
|
610
|
+
kind: 'field',
|
|
611
|
+
name: change.name,
|
|
612
|
+
value: change.value,
|
|
613
|
+
prior: document.fields[change.name],
|
|
614
|
+
});
|
|
615
|
+
else if (change.kind === 'section')
|
|
616
|
+
parts.set(`section ${change.name}`, {
|
|
495
617
|
kind: 'section',
|
|
496
618
|
name: change.name,
|
|
497
619
|
value: change.value,
|
|
498
|
-
prior: document.sections[change.name],
|
|
499
|
-
}
|
|
500
|
-
|
|
620
|
+
prior: document.sections[change.name] ?? '',
|
|
621
|
+
});
|
|
622
|
+
else
|
|
623
|
+
parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
|
|
624
|
+
}
|
|
625
|
+
const applied = apply(document, [...parts.values()], definitions(document.type));
|
|
501
626
|
return applied.outcome === 'applied' ? serialize(applied.document) : null;
|
|
502
627
|
}
|
|
503
|
-
function
|
|
628
|
+
function settledParts(changes, deferred, composition) {
|
|
629
|
+
for (const [held, parts] of deferred) {
|
|
630
|
+
const content = partsApplied(held.content, held.path, parts, composition);
|
|
631
|
+
if (content === null)
|
|
632
|
+
return held.path;
|
|
633
|
+
changes[changes.indexOf(held)] = { ...held, content };
|
|
634
|
+
}
|
|
635
|
+
deferred.clear();
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
function folded(changes, change, appending, deferred) {
|
|
504
639
|
const held = changes.find((item) => item.path === change.path && (item.kind === 'create' || item.kind === 'replace'));
|
|
505
640
|
const whole = held === undefined ? -1 : changes.indexOf(held);
|
|
506
641
|
const occupied = changes.some((item) => !isPart(item) && item !== held && touched(item).includes(change.path));
|
|
@@ -521,10 +656,11 @@ function folded(changes, change, appending) {
|
|
|
521
656
|
return 'appended';
|
|
522
657
|
}
|
|
523
658
|
if ('content' in held) {
|
|
524
|
-
const
|
|
525
|
-
if (
|
|
526
|
-
|
|
527
|
-
|
|
659
|
+
const parts = deferred.get(held);
|
|
660
|
+
if (parts === undefined)
|
|
661
|
+
deferred.set(held, [change]);
|
|
662
|
+
else
|
|
663
|
+
parts.push(change);
|
|
528
664
|
return 'folded';
|
|
529
665
|
}
|
|
530
666
|
if (change.kind === 'preamble')
|
|
@@ -555,17 +691,23 @@ function folded(changes, change, appending) {
|
|
|
555
691
|
changes.push(change);
|
|
556
692
|
return 'appended';
|
|
557
693
|
}
|
|
558
|
-
function relinked(batch, move, before, after, appending) {
|
|
559
|
-
const derived = rewrites([move], after);
|
|
694
|
+
function* relinked(batch, move, before, after, appending) {
|
|
695
|
+
const derived = yield* rewrites([move], after);
|
|
560
696
|
if (derived.kind === 'refused')
|
|
561
697
|
return derived;
|
|
562
698
|
const changes = [...batch.changes];
|
|
563
699
|
const appended = [];
|
|
564
700
|
let changed = false;
|
|
701
|
+
const deferred = new Map();
|
|
565
702
|
for (const change of derived.changes) {
|
|
566
703
|
if (change === move)
|
|
567
704
|
continue;
|
|
568
|
-
const
|
|
705
|
+
const unsettled = isPart(change)
|
|
706
|
+
? null
|
|
707
|
+
: settledParts(changes, deferred, after.collection.composition);
|
|
708
|
+
if (unsettled !== null)
|
|
709
|
+
return unqueueable(unsettled);
|
|
710
|
+
const outcome = folded(changes, change, appending, deferred);
|
|
569
711
|
if (outcome === 'refused')
|
|
570
712
|
return unqueueable(change.path);
|
|
571
713
|
if (outcome === 'appended')
|
|
@@ -573,9 +715,13 @@ function relinked(batch, move, before, after, appending) {
|
|
|
573
715
|
if (outcome !== 'skipped')
|
|
574
716
|
changed = true;
|
|
575
717
|
}
|
|
718
|
+
const unsettled = settledParts(changes, deferred, after.collection.composition);
|
|
719
|
+
if (unsettled !== null)
|
|
720
|
+
return unqueueable(unsettled);
|
|
576
721
|
if (!changed)
|
|
577
722
|
return { kind: 'relinked', batch };
|
|
578
723
|
const held = new Set(batch.preconditions.map(identity));
|
|
724
|
+
const added = yield* preconditionsOf(appended, before, []);
|
|
579
725
|
return {
|
|
580
726
|
kind: 'relinked',
|
|
581
727
|
batch: immutable({
|
|
@@ -584,7 +730,7 @@ function relinked(batch, move, before, after, appending) {
|
|
|
584
730
|
changes,
|
|
585
731
|
preconditions: [
|
|
586
732
|
...batch.preconditions,
|
|
587
|
-
...
|
|
733
|
+
...added.filter((precondition) => !held.has(identity(precondition))),
|
|
588
734
|
],
|
|
589
735
|
}),
|
|
590
736
|
};
|
|
@@ -630,7 +776,7 @@ function withdrawnFile(batch, change, at) {
|
|
|
630
776
|
];
|
|
631
777
|
return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
|
|
632
778
|
}
|
|
633
|
-
export function
|
|
779
|
+
export function* requeuing(queue, changes, visible, changeable, state) {
|
|
634
780
|
const next = [...queue];
|
|
635
781
|
const origins = new Map();
|
|
636
782
|
const withdrawn = new Map();
|
|
@@ -653,19 +799,19 @@ export function requeued(queue, changes, visible, changeable, state) {
|
|
|
653
799
|
return unqueueable(change.path);
|
|
654
800
|
const following = next.slice(index);
|
|
655
801
|
const rebuilt = [];
|
|
656
|
-
let before = change.kind === 'move' ? state(next.slice(0, index)) : visible;
|
|
802
|
+
let before = change.kind === 'move' ? yield* state(next.slice(0, index)) : visible;
|
|
657
803
|
for (const [offset, batch] of following.entries()) {
|
|
658
804
|
let current = batch;
|
|
659
805
|
if (change.kind === 'move') {
|
|
660
|
-
const after = state(next.slice(0, index + offset + 1));
|
|
806
|
+
const after = yield* state(next.slice(0, index + offset + 1));
|
|
661
807
|
const appending = offset === 0 && created(batch, queuedFile(batch, change.path, held.localId));
|
|
662
|
-
const relinking = relinked(batch, change, before, after, appending);
|
|
808
|
+
const relinking = yield* relinked(batch, change, before, after, appending);
|
|
663
809
|
if (relinking.kind === 'refused')
|
|
664
810
|
return relinking;
|
|
665
811
|
current = relinking.batch;
|
|
666
812
|
before = after;
|
|
667
813
|
}
|
|
668
|
-
if (current !== batch && !open(batch))
|
|
814
|
+
if (current !== batch && (!open(batch) || batch.between !== undefined))
|
|
669
815
|
return unqueueable(change.path);
|
|
670
816
|
rebuilt.push(offset === 0
|
|
671
817
|
? withdrawnFile(current, change, queuedFile(current, change.path, held.localId))
|
|
@@ -700,16 +846,23 @@ function covering(precondition) {
|
|
|
700
846
|
? identity(precondition)
|
|
701
847
|
: `path ${precondition.path}`;
|
|
702
848
|
}
|
|
703
|
-
export function joined(queue, owner, extra) {
|
|
849
|
+
export function joined(queue, owner, extra, composition) {
|
|
704
850
|
const later = new Set(queue.slice(queue.indexOf(owner) + 1).flatMap((batch) => batch.changes.flatMap(touched)));
|
|
705
851
|
const clash = extra.changes.flatMap(touched).find((path) => later.has(path));
|
|
706
852
|
if (clash !== undefined)
|
|
707
853
|
return unqueueable(clash);
|
|
708
854
|
const changes = [...owner.changes];
|
|
855
|
+
const deferred = new Map();
|
|
709
856
|
for (const change of extra.changes) {
|
|
710
|
-
|
|
857
|
+
const unsettled = isPart(change) ? null : settledParts(changes, deferred, composition);
|
|
858
|
+
if (unsettled !== null)
|
|
859
|
+
return unqueueable(unsettled);
|
|
860
|
+
if (folded(changes, change, true, deferred) === 'refused')
|
|
711
861
|
return unqueueable(change.path);
|
|
712
862
|
}
|
|
863
|
+
const unsettled = settledParts(changes, deferred, composition);
|
|
864
|
+
if (unsettled !== null)
|
|
865
|
+
return unqueueable(unsettled);
|
|
713
866
|
const held = new Set(owner.preconditions.map(covering));
|
|
714
867
|
const written = new Set(owner.changes.flatMap((change) => change.kind === 'create' || change.kind === 'replace' ? [change.path] : []));
|
|
715
868
|
const preconditions = [
|
|
@@ -728,62 +881,66 @@ export function joined(queue, owner, extra) {
|
|
|
728
881
|
});
|
|
729
882
|
return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
|
|
730
883
|
}
|
|
731
|
-
export function
|
|
884
|
+
export function* limiting(queue, batch, limits, state) {
|
|
732
885
|
const at = queue.indexOf(batch);
|
|
733
|
-
const
|
|
734
|
-
|
|
886
|
+
const rewritten = yield* providerRewritten(batch, yield* state(queue.slice(0, at)));
|
|
887
|
+
const limited = yield* exceeding(limits, batch.changes, rewritten.changes, yield* state(queue.slice(0, at + 1)));
|
|
888
|
+
return limited?.refusal ?? null;
|
|
735
889
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
const
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
const
|
|
751
|
-
const
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
890
|
+
function typedText(content) {
|
|
891
|
+
if (content === undefined)
|
|
892
|
+
return false;
|
|
893
|
+
const parsed = parse(content);
|
|
894
|
+
return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
|
|
895
|
+
}
|
|
896
|
+
function touchedLibrary(resulting, touched, composition) {
|
|
897
|
+
const entries = touched
|
|
898
|
+
.flatMap((path) => {
|
|
899
|
+
const content = resulting.documents[path];
|
|
900
|
+
const binary = resulting.binaries[path] !== undefined;
|
|
901
|
+
return content === undefined && !binary ? [] : [{ path, content: content ?? null }];
|
|
902
|
+
})
|
|
903
|
+
.sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0));
|
|
904
|
+
const contents = new Map(entries.map(({ path, content }) => [path, content]));
|
|
905
|
+
const binaries = new Set(Array.from(entriesIn(resulting.binaries), ([path]) => path));
|
|
906
|
+
return validatedCollection(() => Promise.resolve({ entries, next: null }), async (paths) => {
|
|
907
|
+
const known = new Map();
|
|
908
|
+
const pace = paced();
|
|
909
|
+
for (const path of paths) {
|
|
910
|
+
const content = contents.get(path);
|
|
911
|
+
if (binaries.has(path))
|
|
912
|
+
known.set(path, { type: undefined, binary: true });
|
|
913
|
+
else if (content !== undefined && content !== null) {
|
|
914
|
+
const read = parse(content, path, definitionsAt(composition, path));
|
|
915
|
+
if (read.ok)
|
|
916
|
+
known.set(path, { type: read.document.type ?? 'any', binary: false });
|
|
917
|
+
await pace(content.length + ROW_WEIGHT);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
return known;
|
|
921
|
+
}, composition);
|
|
922
|
+
}
|
|
923
|
+
async function judging(visible, resulting, composition, validate, removed, touched) {
|
|
761
924
|
if (removed.size > 0) {
|
|
762
|
-
const
|
|
763
|
-
const referenced = incoming(collectionOf(resulting.documents, input.composition), removed, referrers === null ? null : [...referrers, ...predicted.changes.flatMap(affected)]);
|
|
925
|
+
const referenced = await stepped(incoming(collectionOf(resulting.documents, composition), removed, null));
|
|
764
926
|
if (referenced !== null)
|
|
765
927
|
return referenced;
|
|
766
928
|
}
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
const withoutModel = typedPaths.find((path) => governing(
|
|
929
|
+
const typedPaths = [];
|
|
930
|
+
const pace = paced();
|
|
931
|
+
for (const path of touched) {
|
|
932
|
+
const before = visible.documents[path];
|
|
933
|
+
const after = resulting.documents[path];
|
|
934
|
+
if (typedText(before) || typedText(after))
|
|
935
|
+
typedPaths.push(path);
|
|
936
|
+
await pace((before?.length ?? 0) + (after?.length ?? 0) + ROW_WEIGHT);
|
|
937
|
+
}
|
|
938
|
+
const withoutModel = typedPaths.find((path) => governing(composition, path).status === 'unavailable');
|
|
777
939
|
if (withoutModel !== undefined)
|
|
778
940
|
return refuse('model-unavailable', withoutModel);
|
|
779
|
-
const verdict =
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
: validateCollection(needsModel
|
|
783
|
-
? resulting.documents
|
|
784
|
-
: Object.fromEntries(touched.flatMap((path) => resulting.documents[path] === undefined
|
|
785
|
-
? []
|
|
786
|
-
: [[path, resulting.documents[path]]])), Object.keys(resulting.binaries), input.composition);
|
|
941
|
+
const verdict = typedPaths.length > 0
|
|
942
|
+
? await validate(resulting, touched)
|
|
943
|
+
: await touchedLibrary(resulting, touched, composition);
|
|
787
944
|
if (verdict.verdict === 'refuse') {
|
|
788
945
|
return {
|
|
789
946
|
kind: 'refused',
|
|
@@ -796,12 +953,93 @@ export function buildBatch(input) {
|
|
|
796
953
|
},
|
|
797
954
|
};
|
|
798
955
|
}
|
|
799
|
-
|
|
956
|
+
return {
|
|
957
|
+
kind: 'judged',
|
|
958
|
+
validated: typedPaths
|
|
959
|
+
.map((path) => governing(composition, path))
|
|
960
|
+
.find((model) => model.status !== 'unavailable'),
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
function placed(reference) {
|
|
964
|
+
const key = roomPathOf(reference.handle, reference.path);
|
|
965
|
+
if (reference.kind === 'link')
|
|
966
|
+
return `link ${key}`;
|
|
967
|
+
if (reference.kind === 'item')
|
|
968
|
+
return `section ${reference.section} ${key}`;
|
|
969
|
+
const pointer = reference.pointer
|
|
970
|
+
.split('/')
|
|
971
|
+
.map((segment) => (ARRAY_INDEX.test(segment) ? '-' : segment))
|
|
972
|
+
.join('/');
|
|
973
|
+
return `field ${pointer} ${key}`;
|
|
974
|
+
}
|
|
975
|
+
function roomReferencesIn(path, content, composition) {
|
|
976
|
+
if (content === undefined)
|
|
977
|
+
return [];
|
|
978
|
+
const definitions = definitionsAt(composition, path);
|
|
979
|
+
const parsed = parse(content, path, definitions);
|
|
980
|
+
if (!parsed.ok)
|
|
981
|
+
return [];
|
|
982
|
+
return roomReferences(parsed.document, definitions(parsed.document.type));
|
|
983
|
+
}
|
|
984
|
+
function* admittedRooms(changes, visible, resulting, composition) {
|
|
985
|
+
const from = new Map();
|
|
986
|
+
for (const [index, change] of changes.entries()) {
|
|
987
|
+
if (change.kind === 'move')
|
|
988
|
+
from.set(change.to, departure(changes, index));
|
|
989
|
+
}
|
|
990
|
+
for (const path of [...new Set(changes.flatMap(affected))].sort()) {
|
|
991
|
+
const now = resulting.documents[path];
|
|
992
|
+
if (now === undefined)
|
|
993
|
+
continue;
|
|
994
|
+
const held = new Set(roomReferencesIn(path, visible.documents[from.get(path) ?? path], composition).map(placed));
|
|
995
|
+
for (const reference of roomReferencesIn(path, now, composition)) {
|
|
996
|
+
if (reference.kind === 'link' || reference.crossRoom === true)
|
|
997
|
+
continue;
|
|
998
|
+
if (held.has(placed(reference)))
|
|
999
|
+
continue;
|
|
1000
|
+
return refuse('unresolved-reference', path, reference.kind === 'field' ? reference.pointer : reference.section);
|
|
1001
|
+
}
|
|
1002
|
+
yield ROW_WEIGHT + now.length;
|
|
1003
|
+
}
|
|
1004
|
+
return null;
|
|
1005
|
+
}
|
|
1006
|
+
function validationOf(validated) {
|
|
1007
|
+
return validated === undefined
|
|
1008
|
+
? {}
|
|
1009
|
+
: {
|
|
1010
|
+
validation: {
|
|
1011
|
+
identity: validated.snapshot.identity,
|
|
1012
|
+
version: validated.snapshot.version,
|
|
1013
|
+
source: validated.status,
|
|
1014
|
+
},
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
async function batching(input) {
|
|
1018
|
+
const changes = relativeChanges(input);
|
|
1019
|
+
const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
|
|
1020
|
+
if (structural !== undefined && input.unmodeled) {
|
|
1021
|
+
return refuse('model-unavailable', structural.path);
|
|
1022
|
+
}
|
|
1023
|
+
if (changes.length === 0)
|
|
1024
|
+
return refuse('no-change');
|
|
1025
|
+
const rewritten = await stepped(rewrites(changes, input.visible));
|
|
1026
|
+
if (rewritten.kind === 'refused')
|
|
1027
|
+
return rewritten;
|
|
1028
|
+
const refusal = guard(changes, input, WRITE);
|
|
1029
|
+
if (refusal !== null)
|
|
1030
|
+
return refusal;
|
|
1031
|
+
const preconditions = await stepped(preconditionsOf(changes, input.visible, input.bases));
|
|
1032
|
+
const predicted = await stepped(anticipated(changes, preconditions, rewritten.changes, input.visible));
|
|
1033
|
+
const resulting = await stepped(overlaying(input.visible, [{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted }], input.composition));
|
|
1034
|
+
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))]);
|
|
1035
|
+
if (judged.kind === 'refused')
|
|
1036
|
+
return judged;
|
|
1037
|
+
const crossing = await stepped(admittedRooms(changes, input.visible, resulting, input.composition));
|
|
1038
|
+
if (crossing !== null)
|
|
1039
|
+
return crossing;
|
|
1040
|
+
const limited = await stepped(exceeding(input.providerLimits, changes, predicted.changes, resulting));
|
|
800
1041
|
if (limited !== null)
|
|
801
1042
|
return limited;
|
|
802
|
-
const validated = typedPaths
|
|
803
|
-
.map((path) => governing(input.composition, path))
|
|
804
|
-
.find((model) => model.status !== 'unavailable');
|
|
805
1043
|
return {
|
|
806
1044
|
kind: 'batch',
|
|
807
1045
|
batch: immutable({
|
|
@@ -810,15 +1048,125 @@ export function buildBatch(input) {
|
|
|
810
1048
|
changes,
|
|
811
1049
|
preconditions,
|
|
812
1050
|
...(input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy }),
|
|
813
|
-
...(validated
|
|
814
|
-
? {}
|
|
815
|
-
: {
|
|
816
|
-
validation: {
|
|
817
|
-
identity: validated.snapshot.identity,
|
|
818
|
-
version: validated.snapshot.version,
|
|
819
|
-
source: validated.status,
|
|
820
|
-
},
|
|
821
|
-
}),
|
|
1051
|
+
...validationOf(judged.validated),
|
|
822
1052
|
}),
|
|
823
1053
|
};
|
|
824
1054
|
}
|
|
1055
|
+
export const MOVE_DOCUMENTS = 'move-documents';
|
|
1056
|
+
function heldAt(visible, path) {
|
|
1057
|
+
return visible.documents[path] !== undefined || visible.binaries[path] !== undefined;
|
|
1058
|
+
}
|
|
1059
|
+
function arrivingText(path, to, source, moved) {
|
|
1060
|
+
const content = source.visible.documents[path];
|
|
1061
|
+
const document = source.visible.collection.document(path);
|
|
1062
|
+
if (document === undefined)
|
|
1063
|
+
return content;
|
|
1064
|
+
const definition = definitionsAt(source.composition, path)(document.type);
|
|
1065
|
+
const changes = crossed(document, definition, path, (named, href) => {
|
|
1066
|
+
const at = moved.get(named);
|
|
1067
|
+
if (at !== undefined)
|
|
1068
|
+
return target(to, href) === at ? undefined : relative(to, at);
|
|
1069
|
+
return heldAt(source.visible, named) ? roomPathOf(source.handle, named) : undefined;
|
|
1070
|
+
});
|
|
1071
|
+
if (changes.length === 0)
|
|
1072
|
+
return content;
|
|
1073
|
+
const applied = apply(document, changes, definition);
|
|
1074
|
+
return applied.outcome === 'applied' ? serialize(applied.document) : content;
|
|
1075
|
+
}
|
|
1076
|
+
function arrivalOf({ path, to }, source, moved) {
|
|
1077
|
+
const { documents, revisions, fileIds, created, changes, binaries } = source.visible;
|
|
1078
|
+
const kept = {
|
|
1079
|
+
...(fileIds[path] === undefined ? {} : { fileId: fileIds[path] }),
|
|
1080
|
+
...(created[path] === undefined ? {} : { created: created[path] }),
|
|
1081
|
+
...(changes[path] === undefined ? {} : { change: changes[path] }),
|
|
1082
|
+
};
|
|
1083
|
+
const heads = {
|
|
1084
|
+
...(revisions[path] === undefined ? {} : { revision: revisions[path] }),
|
|
1085
|
+
...kept,
|
|
1086
|
+
};
|
|
1087
|
+
const binary = binaries[path];
|
|
1088
|
+
if (binary?.state === 'accepted') {
|
|
1089
|
+
const { byteLength, mediaType, sha256 } = binary;
|
|
1090
|
+
return {
|
|
1091
|
+
kind: 'arrival',
|
|
1092
|
+
path: to,
|
|
1093
|
+
binary: { byteLength, mediaType, sha256 },
|
|
1094
|
+
...heads,
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
const content = arrivingText(path, to, source, moved);
|
|
1098
|
+
return {
|
|
1099
|
+
kind: 'arrival',
|
|
1100
|
+
path: to,
|
|
1101
|
+
content,
|
|
1102
|
+
...(content === documents[path] ? heads : kept),
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
async function moving(input) {
|
|
1106
|
+
const { source, destination, moves } = input;
|
|
1107
|
+
const removals = moves.map(({ path }) => ({ kind: 'remove', path }));
|
|
1108
|
+
const leaves = guard(removals, { ...source, local: input.local, bases: [] }, MOVE_OUT);
|
|
1109
|
+
if (leaves !== null)
|
|
1110
|
+
return leaves;
|
|
1111
|
+
const uploading = moves.find(({ path }) => source.visible.binaries[path]?.state === 'pending');
|
|
1112
|
+
if (uploading !== undefined)
|
|
1113
|
+
return refuse('unknown-revision', uploading.path);
|
|
1114
|
+
const moved = new Map(moves.map(({ path, to }) => [path, to]));
|
|
1115
|
+
const arrivals = moves.map((move) => arrivalOf(move, source, moved));
|
|
1116
|
+
const arrives = guard(arrivals, { ...destination, local: input.local, bases: [] }, WRITE);
|
|
1117
|
+
if (arrives !== null)
|
|
1118
|
+
return arrives;
|
|
1119
|
+
const bound = input.providerLimits.find((limit) => limit.name === MOVE_DOCUMENTS)?.value;
|
|
1120
|
+
if (typeof bound === 'number' && moves.length > bound) {
|
|
1121
|
+
return refuse('provider-limit', undefined, MOVE_DOCUMENTS);
|
|
1122
|
+
}
|
|
1123
|
+
if (source.unmodeled)
|
|
1124
|
+
return refuse('model-unavailable', moves[0].path);
|
|
1125
|
+
const id = changeBatchIdentity();
|
|
1126
|
+
const between = {
|
|
1127
|
+
source: { room: source.room, baseRoomVersion: source.baseRoomVersion },
|
|
1128
|
+
destination: {
|
|
1129
|
+
room: destination.room,
|
|
1130
|
+
baseRoomVersion: destination.baseRoomVersion,
|
|
1131
|
+
handle: destination.handle,
|
|
1132
|
+
},
|
|
1133
|
+
moves,
|
|
1134
|
+
};
|
|
1135
|
+
const written = input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy };
|
|
1136
|
+
const leavingBatch = {
|
|
1137
|
+
id,
|
|
1138
|
+
baseRoomVersion: source.baseRoomVersion,
|
|
1139
|
+
changes: removals,
|
|
1140
|
+
preconditions: moves.map(({ path }) => existence(source.visible, path)),
|
|
1141
|
+
...written,
|
|
1142
|
+
between,
|
|
1143
|
+
};
|
|
1144
|
+
const effective = await stepped(leavingRewritten(leavingBatch, source.visible));
|
|
1145
|
+
const left = await stepped(overlaying(source.visible, [effective], source.composition));
|
|
1146
|
+
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))]);
|
|
1147
|
+
if (sourceJudged.kind === 'refused')
|
|
1148
|
+
return sourceJudged;
|
|
1149
|
+
const arrivingBatch = {
|
|
1150
|
+
id,
|
|
1151
|
+
baseRoomVersion: destination.baseRoomVersion,
|
|
1152
|
+
changes: arrivals,
|
|
1153
|
+
preconditions: moves.map(({ to }) => ({ kind: 'absent', path: to })),
|
|
1154
|
+
...written,
|
|
1155
|
+
between,
|
|
1156
|
+
};
|
|
1157
|
+
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch], destination.composition));
|
|
1158
|
+
const destinationJudged = await judging(destination.visible, arrived, destination.composition, destination.validate, new Set(), moves.map(({ to }) => to));
|
|
1159
|
+
if (destinationJudged.kind === 'refused')
|
|
1160
|
+
return destinationJudged;
|
|
1161
|
+
const limited = (await stepped(exceeding(input.providerLimits, removals, effective.changes, left))) ??
|
|
1162
|
+
(await stepped(exceeding(input.providerLimits, arrivals, arrivals, arrived)));
|
|
1163
|
+
if (limited !== null)
|
|
1164
|
+
return limited;
|
|
1165
|
+
return { kind: 'moved', leaving: immutable(leavingBatch), arriving: immutable(arrivingBatch) };
|
|
1166
|
+
}
|
|
1167
|
+
export function buildMove(input) {
|
|
1168
|
+
return moving(input);
|
|
1169
|
+
}
|
|
1170
|
+
export function buildBatch(input) {
|
|
1171
|
+
return batching(input);
|
|
1172
|
+
}
|