@agentshouse/mdruntime 0.1.0-cd0f0e6405606 → 0.1.0-cd26b960b7262
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 +6 -0
- package/dist/acceptance.d.ts.map +1 -0
- package/dist/acceptance.js +385 -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 +143 -85
- 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 +30 -11
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +320 -111
- 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 +9 -16
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +11 -89
- 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 +33 -12
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +87 -9
- package/dist/collection.d.ts +11 -22
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +39 -356
- 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 -137
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +101 -3611
- 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 +136 -0
- package/dist/conversation-live.d.ts.map +1 -0
- package/dist/conversation-live.js +248 -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 +48 -44
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +892 -754
- 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 +175 -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 -29
- package/dist/expectation.d.ts +4 -2
- package/dist/expectation.d.ts.map +1 -1
- package/dist/expectation.js +33 -11
- 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 +4 -7
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +67 -39
- 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 +33 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/install.d.ts +56 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +554 -0
- package/dist/job.d.ts +33 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +54 -0
- package/dist/lifecycle.d.ts +7 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +268 -0
- package/dist/listing.d.ts +53 -0
- package/dist/listing.d.ts.map +1 -0
- package/dist/listing.js +176 -0
- package/dist/live.d.ts +23 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +163 -0
- package/dist/locks.d.ts +9 -0
- package/dist/locks.d.ts.map +1 -0
- package/dist/locks.js +61 -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 +14 -6
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +239 -106
- package/dist/messages.d.ts +52 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +162 -0
- package/dist/model.d.ts +8 -10
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +6 -18
- package/dist/models.d.ts +14 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +195 -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 +15 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +98 -14
- 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 +57 -0
- package/dist/outcome.d.ts +44 -9
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +41 -2
- package/dist/overlay.d.ts +3 -6
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +46 -136
- package/dist/{chat/parts.d.ts → parts.d.ts} +12 -9
- package/dist/parts.d.ts.map +1 -0
- package/dist/{chat/parts.js → parts.js} +53 -46
- 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 +226 -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 +21 -207
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -493
- 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/reach.d.ts +20 -9
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +278 -103
- package/dist/react.d.ts +14 -2
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +10 -1
- 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 +36 -10
- package/dist/reads.d.ts.map +1 -1
- package/dist/reads.js +157 -84
- 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 +18 -15
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +143 -58
- 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 +56 -38
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +313 -197
- package/dist/room-draft.d.ts +25 -8
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +153 -65
- 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 -31
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +59 -123
- package/dist/runtime.d.ts +43 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1 -0
- package/dist/save.d.ts +56 -15
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +407 -121
- package/dist/saves.d.ts +70 -0
- package/dist/saves.d.ts.map +1 -0
- package/dist/saves.js +1005 -0
- package/dist/search.d.ts +12 -95
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +10 -266
- 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 +5 -9
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +66 -121
- package/dist/shown.d.ts +31 -0
- package/dist/shown.d.ts.map +1 -0
- package/dist/shown.js +292 -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 +44 -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 +70 -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 +212 -0
- package/dist/store/records.d.ts.map +1 -0
- package/dist/store/records.js +431 -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 -164
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +136 -752
- 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 +223 -0
- package/dist/sync.d.ts +19 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +532 -0
- package/dist/tables.d.ts +416 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +740 -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 +2 -2
- package/dist/turn.d.ts.map +1 -1
- package/dist/turn.js +34 -23
- 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 +1 -16
- package/dist/windows.d.ts.map +1 -1
- package/dist/windows.js +2 -79
- package/dist/wire.d.ts +34 -6
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +12 -2
- package/package.json +18 -19
- package/AGENTS.md +0 -299
- 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/earlier.d.ts +0 -10
- package/dist/chat/earlier.d.ts.map +0 -1
- package/dist/chat/earlier.js +0 -54
- 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 -61
- 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 -27
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -99
- package/dist/chat/markdown.d.ts +0 -21
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -246
- 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 -22
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -171
- 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.map +0 -1
- package/dist/chat/pieces.d.ts +0 -62
- package/dist/chat/pieces.d.ts.map +0 -1
- package/dist/chat/pieces.js +0 -1110
- 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 -630
- package/dist/chat/question.d.ts +0 -9
- package/dist/chat/question.d.ts.map +0 -1
- package/dist/chat/question.js +0 -69
- 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 -34
- package/dist/chat/ready.d.ts.map +0 -1
- package/dist/chat/ready.js +0 -91
- package/dist/chat/steady.d.ts +0 -3
- package/dist/chat/steady.d.ts.map +0 -1
- package/dist/chat/steady.js +0 -26
- 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 -27
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -824
package/dist/save.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { definitionOf } from './model.js';
|
|
2
|
-
import { apply, ATTACHMENT, links, parse, pointerSegments, references, relative, relativize, retarget, serialize, target, } from '@agentshouse/mdmodel';
|
|
3
|
-
import { immutable, queuedFile, departure, } from './change.js';
|
|
4
|
-
import {
|
|
2
|
+
import { apply, ARRAY_INDEX, ATTACHMENT, links, parse, pointerSegments, references, relative, relativize, retarget, roomReferences, serialize, target, } from '@agentshouse/mdmodel';
|
|
3
|
+
import { immutable, leaving, queuedFile, departure, } from './change.js';
|
|
4
|
+
import { collectionOf, documentsOf, governing, } from './collection.js';
|
|
5
5
|
import { changeBatchIdentity } from './identity.js';
|
|
6
|
-
import {
|
|
7
|
-
import { covered, normalize } from './paths.js';
|
|
8
|
-
import {
|
|
9
|
-
import { ROW_WEIGHT, stepped } from './turn.js';
|
|
6
|
+
import { overlaying } from './overlay.js';
|
|
7
|
+
import { ancestors, authoredAt, covered, normalize } from './paths.js';
|
|
8
|
+
import { entriesIn, sorting } from './shared.js';
|
|
9
|
+
import { paced, ROW_WEIGHT, stepped } from './turn.js';
|
|
10
|
+
import { validatedCollection } from './validation.js';
|
|
10
11
|
function refuse(code, file, location) {
|
|
11
12
|
return {
|
|
12
13
|
kind: 'refused',
|
|
@@ -19,9 +20,6 @@ function refuse(code, file, location) {
|
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
function repointed(document, definition, from, to, moves, settled) {
|
|
22
|
-
const parsed = parse(document.content, from);
|
|
23
|
-
if (!parsed.ok)
|
|
24
|
-
return [];
|
|
25
23
|
const changes = [];
|
|
26
24
|
const names = new Set();
|
|
27
25
|
for (const { pointer, value } of definition === undefined
|
|
@@ -44,7 +42,7 @@ function repointed(document, definition, from, to, moves, settled) {
|
|
|
44
42
|
}
|
|
45
43
|
if (changes.length === 0)
|
|
46
44
|
return [];
|
|
47
|
-
const applied = apply(
|
|
45
|
+
const applied = apply(document, changes);
|
|
48
46
|
if (applied.outcome !== 'applied')
|
|
49
47
|
return [];
|
|
50
48
|
const next = applied.document;
|
|
@@ -116,7 +114,16 @@ function relinks(path, moved, before, moves) {
|
|
|
116
114
|
function partKey(change) {
|
|
117
115
|
return change.kind === 'section' ? `section ${change.name}` : change.kind;
|
|
118
116
|
}
|
|
119
|
-
|
|
117
|
+
const NAMES_A_SKIP_TESTS = 64;
|
|
118
|
+
const ENCODED = /[%&\\]/u;
|
|
119
|
+
function movedNames(moves) {
|
|
120
|
+
const names = new Set([...moves.keys()].map((path) => path.slice(path.lastIndexOf('/') + 1)));
|
|
121
|
+
return names.size > NAMES_A_SKIP_TESTS ? null : [...names];
|
|
122
|
+
}
|
|
123
|
+
function cannotLink(text, names) {
|
|
124
|
+
return names !== null && !ENCODED.test(text) && !names.some((name) => text.includes(name));
|
|
125
|
+
}
|
|
126
|
+
function* rewrites(changes, visible) {
|
|
120
127
|
const moves = new Map();
|
|
121
128
|
const settled = new Set();
|
|
122
129
|
const parts = new Map();
|
|
@@ -141,16 +148,20 @@ function rewrites(changes, visible) {
|
|
|
141
148
|
}
|
|
142
149
|
if (moves.size === 0)
|
|
143
150
|
return { kind: 'changes', changes };
|
|
151
|
+
const names = movedNames(moves);
|
|
144
152
|
const merged = [...changes];
|
|
145
153
|
const added = [];
|
|
146
|
-
for (const [path, document] of documentsOf(visible.collection)) {
|
|
154
|
+
for (const [path, document] of yield* documentsOf(visible.collection)) {
|
|
147
155
|
if (settled.has(path))
|
|
148
156
|
continue;
|
|
149
157
|
const moved = moves.get(path) ?? path;
|
|
150
158
|
const definition = definitionOf(governing(visible.collection.composition, path), document.type);
|
|
151
|
-
|
|
159
|
+
const repointing = repointed(document, definition, path, moved, moves, settled);
|
|
160
|
+
added.push(...repointing);
|
|
152
161
|
const own = parts.get(path) ?? [];
|
|
153
|
-
const relinked =
|
|
162
|
+
const relinked = moved === path && own.length === 0 && cannotLink(document.content, names)
|
|
163
|
+
? { kind: 'changes', changes: [] }
|
|
164
|
+
: relinks(path, moved, edited(document, own.map((index) => merged[index])), moves);
|
|
154
165
|
if (relinked.kind === 'refused')
|
|
155
166
|
return relinked;
|
|
156
167
|
const keyed = new Map();
|
|
@@ -175,29 +186,97 @@ function rewrites(changes, visible) {
|
|
|
175
186
|
else
|
|
176
187
|
merged[index] = change;
|
|
177
188
|
}
|
|
189
|
+
const text = visible.collection.text[path].length;
|
|
190
|
+
const rewritten = repointing.length > 0 || relinked.changes.length > 0;
|
|
191
|
+
yield ROW_WEIGHT + text + (rewritten ? text : 0);
|
|
178
192
|
}
|
|
179
193
|
return {
|
|
180
194
|
kind: 'changes',
|
|
181
195
|
changes: [...added, ...merged.filter((change) => change !== null)],
|
|
182
196
|
};
|
|
183
197
|
}
|
|
184
|
-
function anticipated(changes, preconditions, rewritten, visible) {
|
|
198
|
+
function* anticipated(changes, preconditions, rewritten, visible) {
|
|
185
199
|
const given = new Set(changes);
|
|
186
200
|
const derived = rewritten.filter((change) => !given.has(change));
|
|
187
201
|
return {
|
|
188
202
|
changes: rewritten,
|
|
189
|
-
preconditions: [...preconditionsOf(derived, visible, []), ...preconditions],
|
|
203
|
+
preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...preconditions],
|
|
190
204
|
};
|
|
191
205
|
}
|
|
192
|
-
export function providerRewritten(batch, visible) {
|
|
193
|
-
const rewritten = rewrites(batch.changes, visible);
|
|
206
|
+
export function* providerRewritten(batch, visible) {
|
|
207
|
+
const rewritten = yield* rewrites(batch.changes, visible);
|
|
194
208
|
if (rewritten.kind === 'refused')
|
|
195
209
|
return batch;
|
|
196
210
|
return {
|
|
197
211
|
...batch,
|
|
198
|
-
...anticipated(batch.changes, batch.preconditions, rewritten.changes, visible),
|
|
212
|
+
...(yield* anticipated(batch.changes, batch.preconditions, rewritten.changes, visible)),
|
|
199
213
|
};
|
|
200
214
|
}
|
|
215
|
+
function crossed(document, definition, path, to) {
|
|
216
|
+
const changes = [];
|
|
217
|
+
for (const link of links(document, path)) {
|
|
218
|
+
const written = to(link.target, link.href);
|
|
219
|
+
if (written !== undefined)
|
|
220
|
+
changes.push({ kind: 'link', href: link.href, to: written });
|
|
221
|
+
}
|
|
222
|
+
for (const { pointer, value } of definition === undefined
|
|
223
|
+
? []
|
|
224
|
+
: references(document, definition)) {
|
|
225
|
+
const named = target(path, value);
|
|
226
|
+
const written = named === null ? undefined : to(named, value);
|
|
227
|
+
if (written !== undefined) {
|
|
228
|
+
changes.push({ kind: 'reference', pointer, value: retarget(value, written), prior: value });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return changes;
|
|
232
|
+
}
|
|
233
|
+
function rewritten(path, document, changes) {
|
|
234
|
+
if (changes.length === 0)
|
|
235
|
+
return [];
|
|
236
|
+
const applied = apply(document, changes);
|
|
237
|
+
if (applied.outcome !== 'applied')
|
|
238
|
+
return [];
|
|
239
|
+
const after = applied.document;
|
|
240
|
+
if (document.type === undefined)
|
|
241
|
+
return [{ kind: 'replace', path, content: serialize(after) }];
|
|
242
|
+
const names = new Set(changes.flatMap((change) => change.kind === 'reference' ? [pointerSegments(change.pointer)[0] ?? ''] : []));
|
|
243
|
+
return [
|
|
244
|
+
...[...names].map((name) => ({ kind: 'field', path, name, value: after.fields[name] })),
|
|
245
|
+
...bodies(path, document, after),
|
|
246
|
+
];
|
|
247
|
+
}
|
|
248
|
+
function roomPathOf(handle, path) {
|
|
249
|
+
return `/rooms/${handle}/${path}`;
|
|
250
|
+
}
|
|
251
|
+
export function* leavingRewritten(batch, visible) {
|
|
252
|
+
const between = batch.between;
|
|
253
|
+
if (between === undefined)
|
|
254
|
+
return batch;
|
|
255
|
+
const moved = new Map(between.moves.map(({ path, to }) => [path, roomPathOf(between.destination.handle, to)]));
|
|
256
|
+
const derived = [];
|
|
257
|
+
for (const [path, document] of yield* documentsOf(visible.collection)) {
|
|
258
|
+
if (moved.has(path))
|
|
259
|
+
continue;
|
|
260
|
+
const definition = definitionOf(governing(visible.collection.composition, path), document.type);
|
|
261
|
+
derived.push(...rewritten(path, document, crossed(document, definition, path, (named) => moved.get(named))));
|
|
262
|
+
yield ROW_WEIGHT + visible.collection.text[path].length;
|
|
263
|
+
}
|
|
264
|
+
if (derived.length === 0)
|
|
265
|
+
return batch;
|
|
266
|
+
return {
|
|
267
|
+
...batch,
|
|
268
|
+
changes: [...derived, ...batch.changes],
|
|
269
|
+
preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...batch.preconditions],
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
export function rewriting(batch, locator) {
|
|
273
|
+
return leaving(batch, locator) || batch.changes.some((change) => change.kind === 'move');
|
|
274
|
+
}
|
|
275
|
+
export function* effectiveIn(batch, locator, visible) {
|
|
276
|
+
return leaving(batch, locator)
|
|
277
|
+
? yield* leavingRewritten(batch, visible)
|
|
278
|
+
: yield* providerRewritten(batch, visible);
|
|
279
|
+
}
|
|
201
280
|
function* incoming(collection, removed, candidates) {
|
|
202
281
|
const found = new Map();
|
|
203
282
|
const note = (target, file) => {
|
|
@@ -208,13 +287,19 @@ function* incoming(collection, removed, candidates) {
|
|
|
208
287
|
found.set(target, referrers);
|
|
209
288
|
};
|
|
210
289
|
const read = function* (paths) {
|
|
211
|
-
|
|
290
|
+
const seen = new Set();
|
|
291
|
+
for (const path of paths) {
|
|
292
|
+
if (seen.has(path))
|
|
293
|
+
continue;
|
|
294
|
+
seen.add(path);
|
|
212
295
|
const document = collection.document(path);
|
|
213
296
|
if (document !== undefined)
|
|
214
297
|
yield [path, document];
|
|
215
298
|
}
|
|
216
299
|
};
|
|
217
|
-
for (const [path, document] of candidates === null
|
|
300
|
+
for (const [path, document] of candidates === null
|
|
301
|
+
? yield* documentsOf(collection)
|
|
302
|
+
: read(candidates)) {
|
|
218
303
|
const definition = definitionOf(governing(collection.composition, path), document.type);
|
|
219
304
|
for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
|
|
220
305
|
if (to.includes(ATTACHMENT))
|
|
@@ -225,7 +310,7 @@ function* incoming(collection, removed, candidates) {
|
|
|
225
310
|
}
|
|
226
311
|
yield ROW_WEIGHT + collection.text[path].length;
|
|
227
312
|
}
|
|
228
|
-
const [first] =
|
|
313
|
+
const [first] = yield* sorting(found.keys());
|
|
229
314
|
if (first === undefined)
|
|
230
315
|
return null;
|
|
231
316
|
const every = new Set();
|
|
@@ -238,14 +323,16 @@ function* incoming(collection, removed, candidates) {
|
|
|
238
323
|
code: 'referenced-missing-file',
|
|
239
324
|
retryable: false,
|
|
240
325
|
file: first,
|
|
241
|
-
referrers:
|
|
326
|
+
referrers: yield* sorting(every),
|
|
242
327
|
},
|
|
243
328
|
};
|
|
244
329
|
}
|
|
245
330
|
function affected(change) {
|
|
246
331
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
247
332
|
}
|
|
248
|
-
|
|
333
|
+
const WRITE = ['write'];
|
|
334
|
+
const MOVE_OUT = ['write', 'erase'];
|
|
335
|
+
function guard(changes, input, removing) {
|
|
249
336
|
const overridden = new Map();
|
|
250
337
|
const existing = {
|
|
251
338
|
has: (path) => overridden.get(path) ??
|
|
@@ -253,21 +340,26 @@ function guard(changes, input) {
|
|
|
253
340
|
add: (path) => void overridden.set(path, true),
|
|
254
341
|
delete: (path) => void overridden.set(path, false),
|
|
255
342
|
};
|
|
343
|
+
const occupied = (path) => input.folders.has(path) ? path : (ancestors(path).find((one) => existing.has(one)) ?? null);
|
|
256
344
|
for (const [index, change] of changes.entries()) {
|
|
257
345
|
for (const path of affected(change)) {
|
|
258
346
|
const normalized = normalize(path);
|
|
259
347
|
if (normalized === null || normalized === '')
|
|
260
348
|
return refuse('room-root', path);
|
|
261
|
-
if (covered(input.providerOwnedPaths, normalized)
|
|
349
|
+
if (covered(input.providerOwnedPaths, normalized) ||
|
|
350
|
+
(input.authoredPaths !== undefined && !authoredAt(input.authoredPaths, normalized))) {
|
|
262
351
|
return refuse('provider-owned-path', normalized);
|
|
263
352
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
353
|
+
if (!input.pathRule(normalized))
|
|
354
|
+
return refuse('invalid-path', normalized);
|
|
355
|
+
for (const operation of change.kind === 'remove' ? removing : WRITE)
|
|
356
|
+
if (!input.local && !covered(input.operations[operation], normalized))
|
|
357
|
+
return refuse(`${operation}-grant`, normalized);
|
|
267
358
|
}
|
|
268
|
-
if (change.kind === 'create') {
|
|
269
|
-
|
|
270
|
-
|
|
359
|
+
if (change.kind === 'create' || change.kind === 'arrival') {
|
|
360
|
+
const taken = existing.has(change.path) ? change.path : occupied(change.path);
|
|
361
|
+
if (taken !== null)
|
|
362
|
+
return refuse('path-collision', taken);
|
|
271
363
|
existing.add(change.path);
|
|
272
364
|
continue;
|
|
273
365
|
}
|
|
@@ -277,6 +369,9 @@ function guard(changes, input) {
|
|
|
277
369
|
input.bases.some((base) => base.kind === 'absent' && base.path === change.path))) {
|
|
278
370
|
return refuse('path-collision', change.path);
|
|
279
371
|
}
|
|
372
|
+
const taken = occupied(change.path);
|
|
373
|
+
if (taken !== null)
|
|
374
|
+
return refuse('path-collision', taken);
|
|
280
375
|
if (!input.local &&
|
|
281
376
|
existing.has(change.path) &&
|
|
282
377
|
input.visible.revisions[departure(changes, index)] === undefined) {
|
|
@@ -288,8 +383,9 @@ function guard(changes, input) {
|
|
|
288
383
|
if (change.kind === 'move') {
|
|
289
384
|
if (!existing.has(change.path))
|
|
290
385
|
return refuse('path-absent', change.path);
|
|
291
|
-
|
|
292
|
-
|
|
386
|
+
const taken = existing.has(change.to) ? change.to : occupied(change.to);
|
|
387
|
+
if (taken !== null)
|
|
388
|
+
return refuse('path-collision', taken);
|
|
293
389
|
if (unrevised(input, change.path)) {
|
|
294
390
|
return refuse('unknown-revision', change.path);
|
|
295
391
|
}
|
|
@@ -325,7 +421,7 @@ export function queuedDocuments(queue) {
|
|
|
325
421
|
const paths = new Set();
|
|
326
422
|
for (const batch of queue) {
|
|
327
423
|
for (const change of batch.changes) {
|
|
328
|
-
if (change.kind === 'binary')
|
|
424
|
+
if (change.kind === 'binary' || (change.kind === 'arrival' && !('content' in change)))
|
|
329
425
|
continue;
|
|
330
426
|
if (change.kind === 'remove')
|
|
331
427
|
paths.delete(change.path);
|
|
@@ -354,7 +450,7 @@ function sameTarget(precondition, change) {
|
|
|
354
450
|
return ((precondition.kind !== 'field' && precondition.kind !== 'section') ||
|
|
355
451
|
((change.kind === 'field' || change.kind === 'section') && precondition.name === change.name));
|
|
356
452
|
}
|
|
357
|
-
function preconditionsOf(changes, visible, bases) {
|
|
453
|
+
function* preconditionsOf(changes, visible, bases) {
|
|
358
454
|
const preconditions = [];
|
|
359
455
|
const seen = new Set();
|
|
360
456
|
const add = (precondition) => {
|
|
@@ -365,6 +461,7 @@ function preconditionsOf(changes, visible, bases) {
|
|
|
365
461
|
preconditions.push(precondition);
|
|
366
462
|
};
|
|
367
463
|
for (const change of changes) {
|
|
464
|
+
yield ROW_WEIGHT;
|
|
368
465
|
if (change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble') {
|
|
369
466
|
const based = bases.find((base) => sameTarget(base, change));
|
|
370
467
|
if (based !== undefined) {
|
|
@@ -412,10 +509,13 @@ function preconditionsOf(changes, visible, bases) {
|
|
|
412
509
|
function depth(path) {
|
|
413
510
|
return path.split('/').length;
|
|
414
511
|
}
|
|
415
|
-
function
|
|
512
|
+
function* exceeding(limits, changes, effective, resulting) {
|
|
416
513
|
const written = effective.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
417
514
|
const created = changes
|
|
418
|
-
.filter((change) => change.kind === 'create' ||
|
|
515
|
+
.filter((change) => change.kind === 'create' ||
|
|
516
|
+
change.kind === 'move' ||
|
|
517
|
+
change.kind === 'binary' ||
|
|
518
|
+
change.kind === 'arrival')
|
|
419
519
|
.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
420
520
|
for (const limit of limits) {
|
|
421
521
|
const value = limit.value;
|
|
@@ -429,9 +529,13 @@ function exceeded(limits, changes, effective, resulting) {
|
|
|
429
529
|
return refuse('provider-limit', undefined, limit.name);
|
|
430
530
|
}
|
|
431
531
|
if (limit.name === 'document-size') {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
532
|
+
for (const path of written) {
|
|
533
|
+
const text = resulting.documents[path] ?? '';
|
|
534
|
+
if (new TextEncoder().encode(text).byteLength > value) {
|
|
535
|
+
return refuse('provider-limit', path, limit.name);
|
|
536
|
+
}
|
|
537
|
+
yield ROW_WEIGHT + text.length;
|
|
538
|
+
}
|
|
435
539
|
}
|
|
436
540
|
if (limit.name === 'path-length') {
|
|
437
541
|
const long = created.find((path) => path.length > value);
|
|
@@ -497,18 +601,6 @@ function relativeChanges(input) {
|
|
|
497
601
|
return { ...change, fields, sections };
|
|
498
602
|
});
|
|
499
603
|
}
|
|
500
|
-
function* unmodeledType(collection, composition) {
|
|
501
|
-
if (available(composition))
|
|
502
|
-
return false;
|
|
503
|
-
for (const [path, document] of documentsOf(collection)) {
|
|
504
|
-
if (document.type !== undefined &&
|
|
505
|
-
document.type !== 'any' &&
|
|
506
|
-
governing(composition, path).status === 'unavailable')
|
|
507
|
-
return true;
|
|
508
|
-
yield ROW_WEIGHT + collection.text[path].length;
|
|
509
|
-
}
|
|
510
|
-
return false;
|
|
511
|
-
}
|
|
512
604
|
function unqueueable(file) {
|
|
513
605
|
return { kind: 'refused', refusal: { code: 'unknown-revision', retryable: false, file } };
|
|
514
606
|
}
|
|
@@ -611,8 +703,8 @@ function folded(changes, change, appending, deferred) {
|
|
|
611
703
|
changes.push(change);
|
|
612
704
|
return 'appended';
|
|
613
705
|
}
|
|
614
|
-
function relinked(batch, move, before, after, appending) {
|
|
615
|
-
const derived = rewrites([move], after);
|
|
706
|
+
function* relinked(batch, move, before, after, appending) {
|
|
707
|
+
const derived = yield* rewrites([move], after);
|
|
616
708
|
if (derived.kind === 'refused')
|
|
617
709
|
return derived;
|
|
618
710
|
const changes = [...batch.changes];
|
|
@@ -639,6 +731,7 @@ function relinked(batch, move, before, after, appending) {
|
|
|
639
731
|
if (!changed)
|
|
640
732
|
return { kind: 'relinked', batch };
|
|
641
733
|
const held = new Set(batch.preconditions.map(identity));
|
|
734
|
+
const added = yield* preconditionsOf(appended, before, []);
|
|
642
735
|
return {
|
|
643
736
|
kind: 'relinked',
|
|
644
737
|
batch: immutable({
|
|
@@ -647,7 +740,7 @@ function relinked(batch, move, before, after, appending) {
|
|
|
647
740
|
changes,
|
|
648
741
|
preconditions: [
|
|
649
742
|
...batch.preconditions,
|
|
650
|
-
...
|
|
743
|
+
...added.filter((precondition) => !held.has(identity(precondition))),
|
|
651
744
|
],
|
|
652
745
|
}),
|
|
653
746
|
};
|
|
@@ -693,7 +786,7 @@ function withdrawnFile(batch, change, at) {
|
|
|
693
786
|
];
|
|
694
787
|
return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
|
|
695
788
|
}
|
|
696
|
-
export function
|
|
789
|
+
export function* requeuing(queue, changes, visible, changeable, state) {
|
|
697
790
|
const next = [...queue];
|
|
698
791
|
const origins = new Map();
|
|
699
792
|
const withdrawn = new Map();
|
|
@@ -716,19 +809,19 @@ export function requeued(queue, changes, visible, changeable, state) {
|
|
|
716
809
|
return unqueueable(change.path);
|
|
717
810
|
const following = next.slice(index);
|
|
718
811
|
const rebuilt = [];
|
|
719
|
-
let before = change.kind === 'move' ? state(next.slice(0, index)) : visible;
|
|
812
|
+
let before = change.kind === 'move' ? yield* state(next.slice(0, index)) : visible;
|
|
720
813
|
for (const [offset, batch] of following.entries()) {
|
|
721
814
|
let current = batch;
|
|
722
815
|
if (change.kind === 'move') {
|
|
723
|
-
const after = state(next.slice(0, index + offset + 1));
|
|
816
|
+
const after = yield* state(next.slice(0, index + offset + 1));
|
|
724
817
|
const appending = offset === 0 && created(batch, queuedFile(batch, change.path, held.localId));
|
|
725
|
-
const relinking = relinked(batch, change, before, after, appending);
|
|
818
|
+
const relinking = yield* relinked(batch, change, before, after, appending);
|
|
726
819
|
if (relinking.kind === 'refused')
|
|
727
820
|
return relinking;
|
|
728
821
|
current = relinking.batch;
|
|
729
822
|
before = after;
|
|
730
823
|
}
|
|
731
|
-
if (current !== batch && !open(batch))
|
|
824
|
+
if (current !== batch && (!open(batch) || batch.between !== undefined))
|
|
732
825
|
return unqueueable(change.path);
|
|
733
826
|
rebuilt.push(offset === 0
|
|
734
827
|
? withdrawnFile(current, change, queuedFile(current, change.path, held.localId))
|
|
@@ -798,60 +891,66 @@ export function joined(queue, owner, extra) {
|
|
|
798
891
|
});
|
|
799
892
|
return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
|
|
800
893
|
}
|
|
801
|
-
export function
|
|
894
|
+
export function* limiting(queue, batch, limits, state) {
|
|
802
895
|
const at = queue.indexOf(batch);
|
|
803
|
-
const
|
|
804
|
-
|
|
896
|
+
const rewritten = yield* providerRewritten(batch, yield* state(queue.slice(0, at)));
|
|
897
|
+
const limited = yield* exceeding(limits, batch.changes, rewritten.changes, yield* state(queue.slice(0, at + 1)));
|
|
898
|
+
return limited?.refusal ?? null;
|
|
805
899
|
}
|
|
806
|
-
function
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
const
|
|
821
|
-
const
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
900
|
+
function typedText(content) {
|
|
901
|
+
if (content === undefined)
|
|
902
|
+
return false;
|
|
903
|
+
const parsed = parse(content);
|
|
904
|
+
return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
|
|
905
|
+
}
|
|
906
|
+
function touchedLibrary(resulting, touched, composition) {
|
|
907
|
+
const entries = touched
|
|
908
|
+
.flatMap((path) => {
|
|
909
|
+
const content = resulting.documents[path];
|
|
910
|
+
const binary = resulting.binaries[path] !== undefined;
|
|
911
|
+
return content === undefined && !binary ? [] : [{ path, content: content ?? null }];
|
|
912
|
+
})
|
|
913
|
+
.sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0));
|
|
914
|
+
const contents = new Map(entries.map(({ path, content }) => [path, content]));
|
|
915
|
+
const binaries = new Set(Array.from(entriesIn(resulting.binaries), ([path]) => path));
|
|
916
|
+
return validatedCollection(() => Promise.resolve({ entries, next: null }), async (paths) => {
|
|
917
|
+
const known = new Map();
|
|
918
|
+
const pace = paced();
|
|
919
|
+
for (const path of paths) {
|
|
920
|
+
const content = contents.get(path);
|
|
921
|
+
if (binaries.has(path))
|
|
922
|
+
known.set(path, { type: undefined, binary: true });
|
|
923
|
+
else if (content !== undefined && content !== null) {
|
|
924
|
+
const read = parse(content, path);
|
|
925
|
+
if (read.ok)
|
|
926
|
+
known.set(path, { type: read.document.type ?? 'any', binary: false });
|
|
927
|
+
await pace(content.length + ROW_WEIGHT);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return known;
|
|
931
|
+
}, composition);
|
|
932
|
+
}
|
|
933
|
+
async function judging(visible, resulting, composition, validate, removed, touched) {
|
|
827
934
|
if (removed.size > 0) {
|
|
828
|
-
const
|
|
829
|
-
const referenced = yield* incoming(collectionOf(resulting.documents, input.composition), removed, clean === null || referrers === null
|
|
830
|
-
? null
|
|
831
|
-
: [...referrers, ...(yield* differences(clean.text, resulting.documents))]);
|
|
935
|
+
const referenced = await stepped(incoming(collectionOf(resulting.documents, composition), removed, null));
|
|
832
936
|
if (referenced !== null)
|
|
833
937
|
return referenced;
|
|
834
938
|
}
|
|
835
|
-
const
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
const
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
const withoutModel = typedPaths.find((path) => governing(
|
|
939
|
+
const typedPaths = [];
|
|
940
|
+
const pace = paced();
|
|
941
|
+
for (const path of touched) {
|
|
942
|
+
const before = visible.documents[path];
|
|
943
|
+
const after = resulting.documents[path];
|
|
944
|
+
if (typedText(before) || typedText(after))
|
|
945
|
+
typedPaths.push(path);
|
|
946
|
+
await pace((before?.length ?? 0) + (after?.length ?? 0) + ROW_WEIGHT);
|
|
947
|
+
}
|
|
948
|
+
const withoutModel = typedPaths.find((path) => governing(composition, path).status === 'unavailable');
|
|
845
949
|
if (withoutModel !== undefined)
|
|
846
950
|
return refuse('model-unavailable', withoutModel);
|
|
847
|
-
const verdict =
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
: yield* validateCollection(needsModel
|
|
851
|
-
? resulting.documents
|
|
852
|
-
: Object.fromEntries(touched.flatMap((path) => resulting.documents[path] === undefined
|
|
853
|
-
? []
|
|
854
|
-
: [[path, resulting.documents[path]]])), Object.keys(resulting.binaries), input.composition);
|
|
951
|
+
const verdict = typedPaths.length > 0
|
|
952
|
+
? await validate(resulting, touched)
|
|
953
|
+
: await touchedLibrary(resulting, touched, composition);
|
|
855
954
|
if (verdict.verdict === 'refuse') {
|
|
856
955
|
return {
|
|
857
956
|
kind: 'refused',
|
|
@@ -864,12 +963,92 @@ function* batching(input) {
|
|
|
864
963
|
},
|
|
865
964
|
};
|
|
866
965
|
}
|
|
867
|
-
|
|
966
|
+
return {
|
|
967
|
+
kind: 'judged',
|
|
968
|
+
validated: typedPaths
|
|
969
|
+
.map((path) => governing(composition, path))
|
|
970
|
+
.find((model) => model.status !== 'unavailable'),
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
function placed(reference) {
|
|
974
|
+
const key = roomPathOf(reference.handle, reference.path);
|
|
975
|
+
if (reference.kind === 'link')
|
|
976
|
+
return `link ${key}`;
|
|
977
|
+
if (reference.kind === 'item')
|
|
978
|
+
return `section ${reference.section} ${key}`;
|
|
979
|
+
const pointer = reference.pointer
|
|
980
|
+
.split('/')
|
|
981
|
+
.map((segment) => (ARRAY_INDEX.test(segment) ? '-' : segment))
|
|
982
|
+
.join('/');
|
|
983
|
+
return `field ${pointer} ${key}`;
|
|
984
|
+
}
|
|
985
|
+
function roomReferencesIn(path, content, composition) {
|
|
986
|
+
if (content === undefined)
|
|
987
|
+
return [];
|
|
988
|
+
const parsed = parse(content, path);
|
|
989
|
+
if (!parsed.ok)
|
|
990
|
+
return [];
|
|
991
|
+
return roomReferences(parsed.document, definitionOf(governing(composition, path), parsed.document.type));
|
|
992
|
+
}
|
|
993
|
+
function* admittedRooms(changes, visible, resulting, composition) {
|
|
994
|
+
const from = new Map();
|
|
995
|
+
for (const [index, change] of changes.entries()) {
|
|
996
|
+
if (change.kind === 'move')
|
|
997
|
+
from.set(change.to, departure(changes, index));
|
|
998
|
+
}
|
|
999
|
+
for (const path of [...new Set(changes.flatMap(affected))].sort()) {
|
|
1000
|
+
const now = resulting.documents[path];
|
|
1001
|
+
if (now === undefined)
|
|
1002
|
+
continue;
|
|
1003
|
+
const held = new Set(roomReferencesIn(path, visible.documents[from.get(path) ?? path], composition).map(placed));
|
|
1004
|
+
for (const reference of roomReferencesIn(path, now, composition)) {
|
|
1005
|
+
if (reference.kind === 'link' || reference.crossRoom === true)
|
|
1006
|
+
continue;
|
|
1007
|
+
if (held.has(placed(reference)))
|
|
1008
|
+
continue;
|
|
1009
|
+
return refuse('unresolved-reference', path, reference.kind === 'field' ? reference.pointer : reference.section);
|
|
1010
|
+
}
|
|
1011
|
+
yield ROW_WEIGHT + now.length;
|
|
1012
|
+
}
|
|
1013
|
+
return null;
|
|
1014
|
+
}
|
|
1015
|
+
function validationOf(validated) {
|
|
1016
|
+
return validated === undefined
|
|
1017
|
+
? {}
|
|
1018
|
+
: {
|
|
1019
|
+
validation: {
|
|
1020
|
+
identity: validated.snapshot.identity,
|
|
1021
|
+
version: validated.snapshot.version,
|
|
1022
|
+
source: validated.status,
|
|
1023
|
+
},
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
async function batching(input) {
|
|
1027
|
+
const changes = relativeChanges(input);
|
|
1028
|
+
const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
|
|
1029
|
+
if (structural !== undefined && input.unmodeled) {
|
|
1030
|
+
return refuse('model-unavailable', structural.path);
|
|
1031
|
+
}
|
|
1032
|
+
if (changes.length === 0)
|
|
1033
|
+
return refuse('no-change');
|
|
1034
|
+
const rewritten = await stepped(rewrites(changes, input.visible));
|
|
1035
|
+
if (rewritten.kind === 'refused')
|
|
1036
|
+
return rewritten;
|
|
1037
|
+
const refusal = guard(changes, input, WRITE);
|
|
1038
|
+
if (refusal !== null)
|
|
1039
|
+
return refusal;
|
|
1040
|
+
const preconditions = await stepped(preconditionsOf(changes, input.visible, input.bases));
|
|
1041
|
+
const predicted = await stepped(anticipated(changes, preconditions, rewritten.changes, input.visible));
|
|
1042
|
+
const resulting = await stepped(overlaying(input.visible, [{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted }]));
|
|
1043
|
+
const judged = await judging(input.visible, resulting, input.composition, input.validate, new Set(changes.filter((change) => change.kind === 'remove').map((change) => change.path)), [...new Set(predicted.changes.flatMap(affected))]);
|
|
1044
|
+
if (judged.kind === 'refused')
|
|
1045
|
+
return judged;
|
|
1046
|
+
const crossing = await stepped(admittedRooms(changes, input.visible, resulting, input.composition));
|
|
1047
|
+
if (crossing !== null)
|
|
1048
|
+
return crossing;
|
|
1049
|
+
const limited = await stepped(exceeding(input.providerLimits, changes, predicted.changes, resulting));
|
|
868
1050
|
if (limited !== null)
|
|
869
1051
|
return limited;
|
|
870
|
-
const validated = typedPaths
|
|
871
|
-
.map((path) => governing(input.composition, path))
|
|
872
|
-
.find((model) => model.status !== 'unavailable');
|
|
873
1052
|
return {
|
|
874
1053
|
kind: 'batch',
|
|
875
1054
|
batch: immutable({
|
|
@@ -878,18 +1057,125 @@ function* batching(input) {
|
|
|
878
1057
|
changes,
|
|
879
1058
|
preconditions,
|
|
880
1059
|
...(input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy }),
|
|
881
|
-
...(validated
|
|
882
|
-
? {}
|
|
883
|
-
: {
|
|
884
|
-
validation: {
|
|
885
|
-
identity: validated.snapshot.identity,
|
|
886
|
-
version: validated.snapshot.version,
|
|
887
|
-
source: validated.status,
|
|
888
|
-
},
|
|
889
|
-
}),
|
|
1060
|
+
...validationOf(judged.validated),
|
|
890
1061
|
}),
|
|
891
1062
|
};
|
|
892
1063
|
}
|
|
1064
|
+
export const MOVE_DOCUMENTS = 'move-documents';
|
|
1065
|
+
function heldAt(visible, path) {
|
|
1066
|
+
return visible.documents[path] !== undefined || visible.binaries[path] !== undefined;
|
|
1067
|
+
}
|
|
1068
|
+
function arrivingText(path, to, source, moved) {
|
|
1069
|
+
const content = source.visible.documents[path];
|
|
1070
|
+
const document = source.visible.collection.document(path);
|
|
1071
|
+
if (document === undefined)
|
|
1072
|
+
return content;
|
|
1073
|
+
const definition = definitionOf(governing(source.composition, path), document.type);
|
|
1074
|
+
const changes = crossed(document, definition, path, (named, href) => {
|
|
1075
|
+
const at = moved.get(named);
|
|
1076
|
+
if (at !== undefined)
|
|
1077
|
+
return target(to, href) === at ? undefined : relative(to, at);
|
|
1078
|
+
return heldAt(source.visible, named) ? roomPathOf(source.handle, named) : undefined;
|
|
1079
|
+
});
|
|
1080
|
+
if (changes.length === 0)
|
|
1081
|
+
return content;
|
|
1082
|
+
const applied = apply(document, changes);
|
|
1083
|
+
return applied.outcome === 'applied' ? serialize(applied.document) : content;
|
|
1084
|
+
}
|
|
1085
|
+
function arrivalOf({ path, to }, source, moved) {
|
|
1086
|
+
const { documents, revisions, fileIds, created, changes, binaries } = source.visible;
|
|
1087
|
+
const kept = {
|
|
1088
|
+
...(fileIds[path] === undefined ? {} : { fileId: fileIds[path] }),
|
|
1089
|
+
...(created[path] === undefined ? {} : { created: created[path] }),
|
|
1090
|
+
...(changes[path] === undefined ? {} : { change: changes[path] }),
|
|
1091
|
+
};
|
|
1092
|
+
const heads = {
|
|
1093
|
+
...(revisions[path] === undefined ? {} : { revision: revisions[path] }),
|
|
1094
|
+
...kept,
|
|
1095
|
+
};
|
|
1096
|
+
const binary = binaries[path];
|
|
1097
|
+
if (binary?.state === 'accepted') {
|
|
1098
|
+
const { byteLength, mediaType, sha256 } = binary;
|
|
1099
|
+
return {
|
|
1100
|
+
kind: 'arrival',
|
|
1101
|
+
path: to,
|
|
1102
|
+
binary: { byteLength, mediaType, sha256 },
|
|
1103
|
+
...heads,
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
const content = arrivingText(path, to, source, moved);
|
|
1107
|
+
return {
|
|
1108
|
+
kind: 'arrival',
|
|
1109
|
+
path: to,
|
|
1110
|
+
content,
|
|
1111
|
+
...(content === documents[path] ? heads : kept),
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
async function moving(input) {
|
|
1115
|
+
const { source, destination, moves, pathRule } = input;
|
|
1116
|
+
const removals = moves.map(({ path }) => ({ kind: 'remove', path }));
|
|
1117
|
+
const leaves = guard(removals, { ...source, local: input.local, bases: [], pathRule }, MOVE_OUT);
|
|
1118
|
+
if (leaves !== null)
|
|
1119
|
+
return leaves;
|
|
1120
|
+
const uploading = moves.find(({ path }) => source.visible.binaries[path]?.state === 'pending');
|
|
1121
|
+
if (uploading !== undefined)
|
|
1122
|
+
return refuse('unknown-revision', uploading.path);
|
|
1123
|
+
const moved = new Map(moves.map(({ path, to }) => [path, to]));
|
|
1124
|
+
const arrivals = moves.map((move) => arrivalOf(move, source, moved));
|
|
1125
|
+
const arrives = guard(arrivals, { ...destination, local: input.local, bases: [], pathRule }, WRITE);
|
|
1126
|
+
if (arrives !== null)
|
|
1127
|
+
return arrives;
|
|
1128
|
+
const bound = input.providerLimits.find((limit) => limit.name === MOVE_DOCUMENTS)?.value;
|
|
1129
|
+
if (typeof bound === 'number' && moves.length > bound) {
|
|
1130
|
+
return refuse('provider-limit', undefined, MOVE_DOCUMENTS);
|
|
1131
|
+
}
|
|
1132
|
+
if (source.unmodeled)
|
|
1133
|
+
return refuse('model-unavailable', moves[0].path);
|
|
1134
|
+
const id = changeBatchIdentity();
|
|
1135
|
+
const between = {
|
|
1136
|
+
source: { room: source.room, baseRoomVersion: source.baseRoomVersion },
|
|
1137
|
+
destination: {
|
|
1138
|
+
room: destination.room,
|
|
1139
|
+
baseRoomVersion: destination.baseRoomVersion,
|
|
1140
|
+
handle: destination.handle,
|
|
1141
|
+
},
|
|
1142
|
+
moves,
|
|
1143
|
+
};
|
|
1144
|
+
const written = input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy };
|
|
1145
|
+
const leavingBatch = {
|
|
1146
|
+
id,
|
|
1147
|
+
baseRoomVersion: source.baseRoomVersion,
|
|
1148
|
+
changes: removals,
|
|
1149
|
+
preconditions: moves.map(({ path }) => existence(source.visible, path)),
|
|
1150
|
+
...written,
|
|
1151
|
+
between,
|
|
1152
|
+
};
|
|
1153
|
+
const effective = await stepped(leavingRewritten(leavingBatch, source.visible));
|
|
1154
|
+
const left = await stepped(overlaying(source.visible, [effective]));
|
|
1155
|
+
const sourceJudged = await judging(source.visible, left, source.composition, source.validate, new Set(moves.map(({ path }) => path)), [...new Set(effective.changes.map((change) => change.path))]);
|
|
1156
|
+
if (sourceJudged.kind === 'refused')
|
|
1157
|
+
return sourceJudged;
|
|
1158
|
+
const arrivingBatch = {
|
|
1159
|
+
id,
|
|
1160
|
+
baseRoomVersion: destination.baseRoomVersion,
|
|
1161
|
+
changes: arrivals,
|
|
1162
|
+
preconditions: moves.map(({ to }) => ({ kind: 'absent', path: to })),
|
|
1163
|
+
...written,
|
|
1164
|
+
between,
|
|
1165
|
+
};
|
|
1166
|
+
const arrived = await stepped(overlaying(destination.visible, [arrivingBatch]));
|
|
1167
|
+
const destinationJudged = await judging(destination.visible, arrived, destination.composition, destination.validate, new Set(), moves.map(({ to }) => to));
|
|
1168
|
+
if (destinationJudged.kind === 'refused')
|
|
1169
|
+
return destinationJudged;
|
|
1170
|
+
const limited = (await stepped(exceeding(input.providerLimits, removals, effective.changes, left))) ??
|
|
1171
|
+
(await stepped(exceeding(input.providerLimits, arrivals, arrivals, arrived)));
|
|
1172
|
+
if (limited !== null)
|
|
1173
|
+
return limited;
|
|
1174
|
+
return { kind: 'moved', leaving: immutable(leavingBatch), arriving: immutable(arrivingBatch) };
|
|
1175
|
+
}
|
|
1176
|
+
export function buildMove(input) {
|
|
1177
|
+
return moving(input);
|
|
1178
|
+
}
|
|
893
1179
|
export function buildBatch(input) {
|
|
894
|
-
return
|
|
1180
|
+
return batching(input);
|
|
895
1181
|
}
|