@agentshouse/mdruntime 0.1.0-c99e5271060cd → 0.1.0-c9b1e59e5baeb
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/AGENTS.md +274 -244
- package/README.md +18 -8
- package/dist/adapter.d.ts +98 -20
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attempt.d.ts +16 -0
- package/dist/attempt.d.ts.map +1 -0
- package/dist/attempt.js +70 -0
- package/dist/binary.d.ts +23 -0
- package/dist/binary.d.ts.map +1 -0
- package/dist/binary.js +8 -0
- package/dist/bootstrap.d.ts +3 -2
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +38 -15
- package/dist/change-set.d.ts +7 -2
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +45 -13
- package/dist/change.d.ts +11 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +26 -1
- package/dist/chat/acts.d.ts +33 -0
- package/dist/chat/acts.d.ts.map +1 -0
- package/dist/chat/acts.js +36 -0
- package/dist/chat/context.d.ts +3 -0
- package/dist/chat/context.d.ts.map +1 -0
- package/dist/chat/context.js +10 -0
- package/dist/chat/earlier.d.ts +10 -0
- package/dist/chat/earlier.d.ts.map +1 -0
- package/dist/chat/earlier.js +54 -0
- package/dist/chat/input.d.ts +9 -0
- package/dist/chat/input.d.ts.map +1 -0
- package/dist/chat/input.js +99 -0
- package/dist/chat/kit.d.ts +61 -0
- package/dist/chat/kit.d.ts.map +1 -0
- package/dist/chat/kit.js +123 -0
- package/dist/chat/lasting.d.ts +6 -0
- package/dist/chat/lasting.d.ts.map +1 -0
- package/dist/chat/lasting.js +19 -0
- package/dist/chat/list.d.ts +28 -0
- package/dist/chat/list.d.ts.map +1 -0
- package/dist/chat/list.js +99 -0
- package/dist/chat/markdown.d.ts +21 -0
- package/dist/chat/markdown.d.ts.map +1 -0
- package/dist/chat/markdown.js +246 -0
- package/dist/chat/message.d.ts +6 -0
- package/dist/chat/message.d.ts.map +1 -0
- package/dist/chat/message.js +199 -0
- package/dist/chat/messages.d.ts +22 -0
- package/dist/chat/messages.d.ts.map +1 -0
- package/dist/chat/messages.js +175 -0
- package/dist/chat/name.d.ts +6 -0
- package/dist/chat/name.d.ts.map +1 -0
- package/dist/chat/name.js +37 -0
- package/dist/chat/opened.d.ts +11 -0
- package/dist/chat/opened.d.ts.map +1 -0
- package/dist/chat/opened.js +17 -0
- package/dist/chat/overlay.d.ts +32 -0
- package/dist/chat/overlay.d.ts.map +1 -0
- package/dist/chat/overlay.js +107 -0
- package/dist/chat/parts.d.ts +57 -0
- package/dist/chat/parts.d.ts.map +1 -0
- package/dist/chat/parts.js +119 -0
- package/dist/chat/pieces.d.ts +62 -0
- package/dist/chat/pieces.d.ts.map +1 -0
- package/dist/chat/pieces.js +1110 -0
- package/dist/chat/process.d.ts +20 -0
- package/dist/chat/process.d.ts.map +1 -0
- package/dist/chat/process.js +32 -0
- package/dist/chat/provider.d.ts +64 -0
- package/dist/chat/provider.d.ts.map +1 -0
- package/dist/chat/provider.js +637 -0
- package/dist/chat/question.d.ts +9 -0
- package/dist/chat/question.d.ts.map +1 -0
- package/dist/chat/question.js +69 -0
- package/dist/chat/questions.d.ts +31 -0
- package/dist/chat/questions.d.ts.map +1 -0
- package/dist/chat/questions.js +111 -0
- package/dist/chat/read.d.ts +5 -0
- package/dist/chat/read.d.ts.map +1 -0
- package/dist/chat/read.js +13 -0
- package/dist/chat/ready.d.ts +34 -0
- package/dist/chat/ready.d.ts.map +1 -0
- package/dist/chat/ready.js +91 -0
- package/dist/chat/steady.d.ts +3 -0
- package/dist/chat/steady.d.ts.map +1 -0
- package/dist/chat/steady.js +26 -0
- package/dist/chat/style.d.ts +4 -0
- package/dist/chat/style.d.ts.map +1 -0
- package/dist/chat/style.js +302 -0
- package/dist/chat.d.ts +14 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +8 -0
- package/dist/client.d.ts +26 -11
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +27 -4
- package/dist/collection.d.ts +20 -5
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +309 -33
- package/dist/connection.d.ts +41 -13
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1819 -407
- package/dist/conversation.d.ts +34 -9
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +552 -168
- package/dist/durability.d.ts +7 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/entry.d.ts +17 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +157 -0
- package/dist/expectation.d.ts +21 -0
- package/dist/expectation.d.ts.map +1 -0
- package/dist/expectation.js +70 -0
- package/dist/fence.d.ts +6 -0
- package/dist/fence.d.ts.map +1 -0
- package/dist/fence.js +10 -0
- package/dist/host.d.ts +15 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +99 -13
- package/dist/identity.d.ts +4 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +12 -0
- package/dist/index.d.ts +15 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/memory.d.ts +10 -4
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +64 -17
- package/dist/model.d.ts +4 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +10 -1
- package/dist/observation.d.ts +7 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +29 -12
- package/dist/outcome.d.ts +17 -1
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +10 -0
- package/dist/overlay.d.ts +18 -5
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +238 -160
- package/dist/query.d.ts +58 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +36 -7
- package/dist/reach.d.ts +8 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +55 -8
- package/dist/react.d.ts +10 -7
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +7 -17
- package/dist/read-side.d.ts +16 -7
- package/dist/read-side.d.ts.map +1 -1
- package/dist/read-side.js +495 -102
- package/dist/readiness.d.ts +53 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +33 -0
- package/dist/reads.d.ts +11 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +98 -0
- package/dist/recovery.d.ts +25 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +197 -0
- package/dist/refresh.d.ts +4 -4
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +51 -30
- package/dist/replica.d.ts +15 -6
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +137 -66
- package/dist/room-draft.d.ts +32 -3
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +129 -12
- package/dist/rows.d.ts +8 -13
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +12 -13
- package/dist/save.d.ts +32 -2
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +495 -97
- package/dist/search.d.ts +49 -6
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +192 -47
- package/dist/seed.d.ts +16 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +75 -0
- package/dist/shared.d.ts +15 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +398 -0
- package/dist/status.d.ts +4 -1
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +0 -8
- package/dist/store.d.ts +71 -21
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +625 -320
- package/dist/turn.d.ts +9 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +64 -0
- package/dist/windows.d.ts +26 -0
- package/dist/windows.d.ts.map +1 -0
- package/dist/windows.js +93 -0
- package/dist/wire.d.ts +153 -30
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +53 -29
- package/package.json +26 -14
- package/dist/attachment.d.ts +0 -3
- package/dist/attachment.d.ts.map +0 -1
- package/dist/attachment.js +0 -8
package/dist/save.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { definitionOf } from './model.js';
|
|
2
2
|
import { apply, ATTACHMENT, links, parse, pointerSegments, references, relative, relativize, retarget, serialize, target, } from '@agentshouse/mdmodel';
|
|
3
|
-
import { immutable, } from './change.js';
|
|
4
|
-
import { collectionOf, documentsOf, governing, validateCollection, } from './collection.js';
|
|
3
|
+
import { immutable, queuedFile, departure, } from './change.js';
|
|
4
|
+
import { available, changedCleanly, collectionOf, documentsOf, governing, referring, validateCollection, } from './collection.js';
|
|
5
5
|
import { changeBatchIdentity } from './identity.js';
|
|
6
|
-
import {
|
|
6
|
+
import { overlaying } from './overlay.js';
|
|
7
7
|
import { covered, normalize } from './paths.js';
|
|
8
|
+
import { differences } from './shared.js';
|
|
9
|
+
import { finished, ROW_WEIGHT, stepped } from './turn.js';
|
|
8
10
|
function refuse(code, file, location) {
|
|
9
11
|
return {
|
|
10
12
|
kind: 'refused',
|
|
@@ -20,7 +22,7 @@ function repointed(document, definition, from, to, moves, settled) {
|
|
|
20
22
|
const parsed = parse(document.content, from);
|
|
21
23
|
if (!parsed.ok)
|
|
22
24
|
return [];
|
|
23
|
-
|
|
25
|
+
const changes = [];
|
|
24
26
|
const names = new Set();
|
|
25
27
|
for (const { pointer, value } of definition === undefined
|
|
26
28
|
? []
|
|
@@ -32,38 +34,39 @@ function repointed(document, definition, from, to, moves, settled) {
|
|
|
32
34
|
const destination = moves.get(named) ?? named;
|
|
33
35
|
if (target(to, value) === destination)
|
|
34
36
|
continue;
|
|
35
|
-
|
|
37
|
+
changes.push({
|
|
36
38
|
kind: 'reference',
|
|
37
39
|
pointer,
|
|
38
40
|
value: retarget(value, relative(to, destination)),
|
|
39
41
|
prior: value,
|
|
40
42
|
});
|
|
41
|
-
if (applied.outcome !== 'applied')
|
|
42
|
-
continue;
|
|
43
|
-
next = applied.document;
|
|
44
43
|
names.add(name);
|
|
45
44
|
}
|
|
45
|
+
if (changes.length === 0)
|
|
46
|
+
return [];
|
|
47
|
+
const applied = apply(parsed.document, changes);
|
|
48
|
+
if (applied.outcome !== 'applied')
|
|
49
|
+
return [];
|
|
50
|
+
const next = applied.document;
|
|
46
51
|
return [...names].map((name) => ({ kind: 'field', path: from, name, value: next.fields[name] }));
|
|
47
52
|
}
|
|
48
|
-
function edited(document,
|
|
49
|
-
|
|
53
|
+
function edited(document, changes) {
|
|
54
|
+
const parts = new Map();
|
|
50
55
|
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
|
-
: {
|
|
56
|
+
if (change.kind === 'preamble')
|
|
57
|
+
parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
|
|
58
|
+
if (change.kind === 'section')
|
|
59
|
+
parts.set(`section ${change.name}`, {
|
|
58
60
|
kind: 'section',
|
|
59
61
|
name: change.name,
|
|
60
62
|
value: change.value,
|
|
61
|
-
prior:
|
|
63
|
+
prior: document.sections[change.name] ?? '',
|
|
62
64
|
});
|
|
63
|
-
if (applied.outcome === 'applied')
|
|
64
|
-
next = applied.document;
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
if (parts.size === 0)
|
|
67
|
+
return document;
|
|
68
|
+
const applied = apply(document, [...parts.values()]);
|
|
69
|
+
return applied.outcome === 'applied' ? applied.document : document;
|
|
67
70
|
}
|
|
68
71
|
function bodies(path, before, after) {
|
|
69
72
|
const changes = [];
|
|
@@ -78,7 +81,7 @@ function bodies(path, before, after) {
|
|
|
78
81
|
return changes;
|
|
79
82
|
}
|
|
80
83
|
function relinks(path, moved, before, moves) {
|
|
81
|
-
|
|
84
|
+
const changes = [];
|
|
82
85
|
for (const link of links(before, path)) {
|
|
83
86
|
const destination = moves.get(link.target) ?? link.target;
|
|
84
87
|
if (destination === link.target && moved === path)
|
|
@@ -86,20 +89,23 @@ function relinks(path, moved, before, moves) {
|
|
|
86
89
|
const to = relative(moved, destination);
|
|
87
90
|
if (to === relative(path, link.target))
|
|
88
91
|
continue;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
92
|
+
changes.push({ kind: 'link', href: link.href, to });
|
|
93
|
+
}
|
|
94
|
+
if (changes.length === 0)
|
|
95
|
+
return { kind: 'changes', changes: [] };
|
|
96
|
+
const applied = apply(before, changes);
|
|
97
|
+
if (applied.outcome === 'refuse') {
|
|
98
|
+
return {
|
|
99
|
+
kind: 'refused',
|
|
100
|
+
refusal: {
|
|
101
|
+
code: applied.refusal.rule,
|
|
102
|
+
retryable: false,
|
|
103
|
+
file: path,
|
|
104
|
+
...(applied.refusal.location === undefined ? {} : { location: applied.refusal.location }),
|
|
105
|
+
},
|
|
106
|
+
};
|
|
102
107
|
}
|
|
108
|
+
const after = applied.document;
|
|
103
109
|
if (after === before)
|
|
104
110
|
return { kind: 'changes', changes: [] };
|
|
105
111
|
if (before.type === undefined) {
|
|
@@ -107,26 +113,31 @@ function relinks(path, moved, before, moves) {
|
|
|
107
113
|
}
|
|
108
114
|
return { kind: 'changes', changes: bodies(path, before, after) };
|
|
109
115
|
}
|
|
110
|
-
function
|
|
111
|
-
|
|
112
|
-
const item = merged[index];
|
|
113
|
-
if (item.path !== path)
|
|
114
|
-
continue;
|
|
115
|
-
if (item.kind === 'section' || item.kind === 'preamble')
|
|
116
|
-
merged.splice(index, 1);
|
|
117
|
-
}
|
|
116
|
+
function partKey(change) {
|
|
117
|
+
return change.kind === 'section' ? `section ${change.name}` : change.kind;
|
|
118
118
|
}
|
|
119
|
-
function rewrites(changes, visible) {
|
|
119
|
+
function* rewrites(changes, visible) {
|
|
120
120
|
const moves = new Map();
|
|
121
121
|
const settled = new Set();
|
|
122
|
-
|
|
122
|
+
const parts = new Map();
|
|
123
|
+
for (const [index, change] of changes.entries()) {
|
|
123
124
|
if (change.kind === 'move')
|
|
124
125
|
moves.set(change.path, change.to);
|
|
125
|
-
if (change.kind === 'remove' ||
|
|
126
|
+
if (change.kind === 'remove' ||
|
|
127
|
+
change.kind === 'replace' ||
|
|
128
|
+
change.kind === 'create' ||
|
|
129
|
+
change.kind === 'binary') {
|
|
126
130
|
settled.add(change.path);
|
|
127
131
|
}
|
|
128
132
|
if (change.kind === 'field')
|
|
129
133
|
settled.add(`${change.path} ${change.name}`);
|
|
134
|
+
if (isPart(change)) {
|
|
135
|
+
const held = parts.get(change.path);
|
|
136
|
+
if (held === undefined)
|
|
137
|
+
parts.set(change.path, [index]);
|
|
138
|
+
else
|
|
139
|
+
held.push(index);
|
|
140
|
+
}
|
|
130
141
|
}
|
|
131
142
|
if (moves.size === 0)
|
|
132
143
|
return { kind: 'changes', changes };
|
|
@@ -138,43 +149,57 @@ function rewrites(changes, visible) {
|
|
|
138
149
|
const moved = moves.get(path) ?? path;
|
|
139
150
|
const definition = definitionOf(governing(visible.collection.composition, path), document.type);
|
|
140
151
|
added.push(...repointed(document, definition, path, moved, moves, settled));
|
|
141
|
-
const
|
|
152
|
+
const own = parts.get(path) ?? [];
|
|
153
|
+
const relinked = relinks(path, moved, edited(document, own.map((index) => merged[index])), moves);
|
|
142
154
|
if (relinked.kind === 'refused')
|
|
143
155
|
return relinked;
|
|
156
|
+
const keyed = new Map();
|
|
157
|
+
for (const index of own) {
|
|
158
|
+
const key = partKey(merged[index]);
|
|
159
|
+
if (!keyed.has(key))
|
|
160
|
+
keyed.set(key, index);
|
|
161
|
+
}
|
|
144
162
|
for (const change of relinked.changes) {
|
|
145
163
|
if (change.kind === 'replace') {
|
|
146
|
-
|
|
164
|
+
for (const index of own) {
|
|
165
|
+
const kind = merged[index]?.kind;
|
|
166
|
+
if (kind === 'section' || kind === 'preamble')
|
|
167
|
+
merged[index] = null;
|
|
168
|
+
}
|
|
147
169
|
added.push(change);
|
|
148
170
|
continue;
|
|
149
171
|
}
|
|
150
|
-
const index =
|
|
151
|
-
|
|
152
|
-
(change.kind !== 'section' || (item.kind === 'section' && item.name === change.name)));
|
|
153
|
-
if (index === -1)
|
|
172
|
+
const index = keyed.get(partKey(change));
|
|
173
|
+
if (index === undefined)
|
|
154
174
|
added.push(change);
|
|
155
175
|
else
|
|
156
176
|
merged[index] = change;
|
|
157
177
|
}
|
|
178
|
+
yield ROW_WEIGHT + visible.collection.text[path].length;
|
|
158
179
|
}
|
|
159
|
-
return {
|
|
180
|
+
return {
|
|
181
|
+
kind: 'changes',
|
|
182
|
+
changes: [...added, ...merged.filter((change) => change !== null)],
|
|
183
|
+
};
|
|
160
184
|
}
|
|
161
|
-
function anticipated(changes, preconditions, rewritten, visible) {
|
|
162
|
-
const
|
|
185
|
+
function* anticipated(changes, preconditions, rewritten, visible) {
|
|
186
|
+
const given = new Set(changes);
|
|
187
|
+
const derived = rewritten.filter((change) => !given.has(change));
|
|
163
188
|
return {
|
|
164
189
|
changes: rewritten,
|
|
165
|
-
preconditions: [...preconditionsOf(derived, visible, []), ...preconditions],
|
|
190
|
+
preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...preconditions],
|
|
166
191
|
};
|
|
167
192
|
}
|
|
168
|
-
export function providerRewritten(batch, visible) {
|
|
169
|
-
const rewritten = rewrites(batch.changes, visible);
|
|
193
|
+
export function* providerRewritten(batch, visible) {
|
|
194
|
+
const rewritten = yield* rewrites(batch.changes, visible);
|
|
170
195
|
if (rewritten.kind === 'refused')
|
|
171
196
|
return batch;
|
|
172
197
|
return {
|
|
173
198
|
...batch,
|
|
174
|
-
...anticipated(batch.changes, batch.preconditions, rewritten.changes, visible),
|
|
199
|
+
...(yield* anticipated(batch.changes, batch.preconditions, rewritten.changes, visible)),
|
|
175
200
|
};
|
|
176
201
|
}
|
|
177
|
-
function incoming(collection, removed) {
|
|
202
|
+
function* incoming(collection, removed, candidates) {
|
|
178
203
|
const found = new Map();
|
|
179
204
|
const note = (target, file) => {
|
|
180
205
|
if (!removed.has(target))
|
|
@@ -183,7 +208,14 @@ function incoming(collection, removed) {
|
|
|
183
208
|
referrers.add(file);
|
|
184
209
|
found.set(target, referrers);
|
|
185
210
|
};
|
|
186
|
-
|
|
211
|
+
const read = function* (paths) {
|
|
212
|
+
for (const path of [...new Set(paths)].sort()) {
|
|
213
|
+
const document = collection.document(path);
|
|
214
|
+
if (document !== undefined)
|
|
215
|
+
yield [path, document];
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
for (const [path, document] of candidates === null ? documentsOf(collection) : read(candidates)) {
|
|
187
219
|
const definition = definitionOf(governing(collection.composition, path), document.type);
|
|
188
220
|
for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
|
|
189
221
|
if (to.includes(ATTACHMENT))
|
|
@@ -192,6 +224,7 @@ function incoming(collection, removed) {
|
|
|
192
224
|
if (named !== null)
|
|
193
225
|
note(named, path);
|
|
194
226
|
}
|
|
227
|
+
yield ROW_WEIGHT + collection.text[path].length;
|
|
195
228
|
}
|
|
196
229
|
const [first] = [...found.keys()].sort();
|
|
197
230
|
if (first === undefined)
|
|
@@ -214,11 +247,14 @@ function affected(change) {
|
|
|
214
247
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
215
248
|
}
|
|
216
249
|
function guard(changes, input) {
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
250
|
+
const overridden = new Map();
|
|
251
|
+
const existing = {
|
|
252
|
+
has: (path) => overridden.get(path) ??
|
|
253
|
+
(Object.hasOwn(input.visible.documents, path) || Object.hasOwn(input.visible.binaries, path)),
|
|
254
|
+
add: (path) => void overridden.set(path, true),
|
|
255
|
+
delete: (path) => void overridden.set(path, false),
|
|
256
|
+
};
|
|
257
|
+
for (const [index, change] of changes.entries()) {
|
|
222
258
|
for (const path of affected(change)) {
|
|
223
259
|
const normalized = normalize(path);
|
|
224
260
|
if (normalized === null || normalized === '')
|
|
@@ -236,12 +272,26 @@ function guard(changes, input) {
|
|
|
236
272
|
existing.add(change.path);
|
|
237
273
|
continue;
|
|
238
274
|
}
|
|
275
|
+
if (change.kind === 'binary') {
|
|
276
|
+
if (input.visible.documents[change.path] !== undefined ||
|
|
277
|
+
(existing.has(change.path) &&
|
|
278
|
+
input.bases.some((base) => base.kind === 'absent' && base.path === change.path))) {
|
|
279
|
+
return refuse('path-collision', change.path);
|
|
280
|
+
}
|
|
281
|
+
if (!input.local &&
|
|
282
|
+
existing.has(change.path) &&
|
|
283
|
+
input.visible.revisions[departure(changes, index)] === undefined) {
|
|
284
|
+
return refuse('unknown-revision', change.path);
|
|
285
|
+
}
|
|
286
|
+
existing.add(change.path);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
239
289
|
if (change.kind === 'move') {
|
|
240
290
|
if (!existing.has(change.path))
|
|
241
291
|
return refuse('path-absent', change.path);
|
|
242
292
|
if (existing.has(change.to))
|
|
243
293
|
return refuse('path-collision', change.to);
|
|
244
|
-
if (
|
|
294
|
+
if (unrevised(input, change.path)) {
|
|
245
295
|
return refuse('unknown-revision', change.path);
|
|
246
296
|
}
|
|
247
297
|
existing.delete(change.path);
|
|
@@ -251,14 +301,14 @@ function guard(changes, input) {
|
|
|
251
301
|
if (!existing.has(change.path))
|
|
252
302
|
return refuse('path-absent', change.path);
|
|
253
303
|
if (change.kind === 'remove') {
|
|
254
|
-
if (
|
|
304
|
+
if (unrevised(input, change.path)) {
|
|
255
305
|
return refuse('unknown-revision', change.path);
|
|
256
306
|
}
|
|
257
307
|
existing.delete(change.path);
|
|
258
308
|
continue;
|
|
259
309
|
}
|
|
260
310
|
if (change.kind === 'replace') {
|
|
261
|
-
if (
|
|
311
|
+
if (unrevised(input, change.path)) {
|
|
262
312
|
return refuse('unknown-revision', change.path);
|
|
263
313
|
}
|
|
264
314
|
continue;
|
|
@@ -269,8 +319,28 @@ function guard(changes, input) {
|
|
|
269
319
|
}
|
|
270
320
|
return null;
|
|
271
321
|
}
|
|
272
|
-
function
|
|
273
|
-
return visible.revisions[path] === undefined
|
|
322
|
+
function unrevised(input, path) {
|
|
323
|
+
return (!input.local && input.queued?.has(path) !== true && input.visible.revisions[path] === undefined);
|
|
324
|
+
}
|
|
325
|
+
export function queuedDocuments(queue) {
|
|
326
|
+
const paths = new Set();
|
|
327
|
+
for (const batch of queue) {
|
|
328
|
+
for (const change of batch.changes) {
|
|
329
|
+
if (change.kind === 'binary')
|
|
330
|
+
continue;
|
|
331
|
+
if (change.kind === 'remove')
|
|
332
|
+
paths.delete(change.path);
|
|
333
|
+
else if (change.kind !== 'move')
|
|
334
|
+
paths.add(change.path);
|
|
335
|
+
else if (paths.delete(change.path))
|
|
336
|
+
paths.add(change.to);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return paths;
|
|
340
|
+
}
|
|
341
|
+
function identity(precondition) {
|
|
342
|
+
const part = precondition.kind === 'field' || precondition.kind === 'section' ? precondition.name : '';
|
|
343
|
+
return `${precondition.kind} ${precondition.path} ${part}`;
|
|
274
344
|
}
|
|
275
345
|
function existence(visible, path) {
|
|
276
346
|
const revision = visible.revisions[path];
|
|
@@ -285,18 +355,18 @@ function sameTarget(precondition, change) {
|
|
|
285
355
|
return ((precondition.kind !== 'field' && precondition.kind !== 'section') ||
|
|
286
356
|
((change.kind === 'field' || change.kind === 'section') && precondition.name === change.name));
|
|
287
357
|
}
|
|
288
|
-
function preconditionsOf(changes, visible, bases) {
|
|
358
|
+
function* preconditionsOf(changes, visible, bases) {
|
|
289
359
|
const preconditions = [];
|
|
290
360
|
const seen = new Set();
|
|
291
361
|
const add = (precondition) => {
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
if (seen.has(identity))
|
|
362
|
+
const held = identity(precondition);
|
|
363
|
+
if (seen.has(held))
|
|
295
364
|
return;
|
|
296
|
-
seen.add(
|
|
365
|
+
seen.add(held);
|
|
297
366
|
preconditions.push(precondition);
|
|
298
367
|
};
|
|
299
368
|
for (const change of changes) {
|
|
369
|
+
yield ROW_WEIGHT;
|
|
300
370
|
if (change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble') {
|
|
301
371
|
const based = bases.find((base) => sameTarget(base, change));
|
|
302
372
|
if (based !== undefined) {
|
|
@@ -328,6 +398,13 @@ function preconditionsOf(changes, visible, bases) {
|
|
|
328
398
|
add({ kind: 'absent', path: change.path });
|
|
329
399
|
continue;
|
|
330
400
|
}
|
|
401
|
+
if (change.kind === 'binary') {
|
|
402
|
+
const revision = visible.revisions[change.path];
|
|
403
|
+
add(visible.binaries[change.path] === undefined || revision === undefined
|
|
404
|
+
? { kind: 'absent', path: change.path }
|
|
405
|
+
: { kind: 'revision', path: change.path, revision });
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
331
408
|
add(existence(visible, change.path));
|
|
332
409
|
if (change.kind === 'move')
|
|
333
410
|
add({ kind: 'absent', path: change.to });
|
|
@@ -337,10 +414,10 @@ function preconditionsOf(changes, visible, bases) {
|
|
|
337
414
|
function depth(path) {
|
|
338
415
|
return path.split('/').length;
|
|
339
416
|
}
|
|
340
|
-
function
|
|
341
|
-
const written =
|
|
417
|
+
function* exceeding(limits, changes, effective, resulting) {
|
|
418
|
+
const written = effective.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
342
419
|
const created = changes
|
|
343
|
-
.filter((change) => change.kind === 'create' || change.kind === 'move')
|
|
420
|
+
.filter((change) => change.kind === 'create' || change.kind === 'move' || change.kind === 'binary')
|
|
344
421
|
.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
345
422
|
for (const limit of limits) {
|
|
346
423
|
const value = limit.value;
|
|
@@ -354,9 +431,13 @@ function exceeded(limits, changes, resulting) {
|
|
|
354
431
|
return refuse('provider-limit', undefined, limit.name);
|
|
355
432
|
}
|
|
356
433
|
if (limit.name === 'document-size') {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
434
|
+
for (const path of written) {
|
|
435
|
+
const text = resulting.documents[path] ?? '';
|
|
436
|
+
if (new TextEncoder().encode(text).byteLength > value) {
|
|
437
|
+
return refuse('provider-limit', path, limit.name);
|
|
438
|
+
}
|
|
439
|
+
yield ROW_WEIGHT + text.length;
|
|
440
|
+
}
|
|
360
441
|
}
|
|
361
442
|
if (limit.name === 'path-length') {
|
|
362
443
|
const long = created.find((path) => path.length > value);
|
|
@@ -386,7 +467,7 @@ function relativeField(path, name, value, handle, definition) {
|
|
|
386
467
|
!opened.ok ||
|
|
387
468
|
!JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
388
469
|
return value;
|
|
389
|
-
const held = apply(opened.document, { kind: 'field', name, value, prior: undefined });
|
|
470
|
+
const held = apply(opened.document, [{ kind: 'field', name, value, prior: undefined }]);
|
|
390
471
|
if (held.outcome !== 'applied')
|
|
391
472
|
return value;
|
|
392
473
|
const result = relativize(held.document, path, handle, definition);
|
|
@@ -422,36 +503,338 @@ function relativeChanges(input) {
|
|
|
422
503
|
return { ...change, fields, sections };
|
|
423
504
|
});
|
|
424
505
|
}
|
|
425
|
-
function unmodeledType(collection, composition) {
|
|
506
|
+
function* unmodeledType(collection, composition) {
|
|
507
|
+
if (available(composition))
|
|
508
|
+
return false;
|
|
426
509
|
for (const [path, document] of documentsOf(collection)) {
|
|
427
510
|
if (document.type !== undefined &&
|
|
428
511
|
document.type !== 'any' &&
|
|
429
512
|
governing(composition, path).status === 'unavailable')
|
|
430
513
|
return true;
|
|
514
|
+
yield ROW_WEIGHT + collection.text[path].length;
|
|
431
515
|
}
|
|
432
516
|
return false;
|
|
433
517
|
}
|
|
434
|
-
|
|
518
|
+
function unqueueable(file) {
|
|
519
|
+
return { kind: 'refused', refusal: { code: 'unknown-revision', retryable: false, file } };
|
|
520
|
+
}
|
|
521
|
+
function isPart(change) {
|
|
522
|
+
return change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble';
|
|
523
|
+
}
|
|
524
|
+
function touched(change) {
|
|
525
|
+
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
526
|
+
}
|
|
527
|
+
function partsApplied(content, path, changes) {
|
|
528
|
+
const parsed = parse(content, path);
|
|
529
|
+
if (!parsed.ok)
|
|
530
|
+
return null;
|
|
531
|
+
const document = parsed.document;
|
|
532
|
+
const parts = new Map();
|
|
533
|
+
for (const change of changes) {
|
|
534
|
+
if (change.kind === 'field')
|
|
535
|
+
parts.set(`field ${change.name}`, {
|
|
536
|
+
kind: 'field',
|
|
537
|
+
name: change.name,
|
|
538
|
+
value: change.value,
|
|
539
|
+
prior: document.fields[change.name],
|
|
540
|
+
});
|
|
541
|
+
else if (change.kind === 'section')
|
|
542
|
+
parts.set(`section ${change.name}`, {
|
|
543
|
+
kind: 'section',
|
|
544
|
+
name: change.name,
|
|
545
|
+
value: change.value,
|
|
546
|
+
prior: document.sections[change.name] ?? '',
|
|
547
|
+
});
|
|
548
|
+
else
|
|
549
|
+
parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
|
|
550
|
+
}
|
|
551
|
+
const applied = apply(document, [...parts.values()]);
|
|
552
|
+
return applied.outcome === 'applied' ? serialize(applied.document) : null;
|
|
553
|
+
}
|
|
554
|
+
function settledParts(changes, deferred) {
|
|
555
|
+
for (const [held, parts] of deferred) {
|
|
556
|
+
const content = partsApplied(held.content, held.path, parts);
|
|
557
|
+
if (content === null)
|
|
558
|
+
return held.path;
|
|
559
|
+
changes[changes.indexOf(held)] = { ...held, content };
|
|
560
|
+
}
|
|
561
|
+
deferred.clear();
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
function folded(changes, change, appending, deferred) {
|
|
565
|
+
const held = changes.find((item) => item.path === change.path && (item.kind === 'create' || item.kind === 'replace'));
|
|
566
|
+
const whole = held === undefined ? -1 : changes.indexOf(held);
|
|
567
|
+
const occupied = changes.some((item) => !isPart(item) && item !== held && touched(item).includes(change.path));
|
|
568
|
+
if (isPart(change)) {
|
|
569
|
+
const same = changes.findIndex((item) => item.kind === change.kind &&
|
|
570
|
+
item.path === change.path &&
|
|
571
|
+
(change.kind === 'preamble' || ('name' in item && item.name === change.name)));
|
|
572
|
+
if (same !== -1) {
|
|
573
|
+
changes[same] = change;
|
|
574
|
+
return 'folded';
|
|
575
|
+
}
|
|
576
|
+
if (held === undefined) {
|
|
577
|
+
if (!appending)
|
|
578
|
+
return 'skipped';
|
|
579
|
+
if (occupied)
|
|
580
|
+
return 'refused';
|
|
581
|
+
changes.push(change);
|
|
582
|
+
return 'appended';
|
|
583
|
+
}
|
|
584
|
+
if ('content' in held) {
|
|
585
|
+
const parts = deferred.get(held);
|
|
586
|
+
if (parts === undefined)
|
|
587
|
+
deferred.set(held, [change]);
|
|
588
|
+
else
|
|
589
|
+
parts.push(change);
|
|
590
|
+
return 'folded';
|
|
591
|
+
}
|
|
592
|
+
if (change.kind === 'preamble')
|
|
593
|
+
return 'refused';
|
|
594
|
+
changes[whole] =
|
|
595
|
+
change.kind === 'field'
|
|
596
|
+
? { ...held, fields: { ...held.fields, [change.name]: change.value } }
|
|
597
|
+
: { ...held, sections: { ...held.sections, [change.name]: change.value } };
|
|
598
|
+
return 'folded';
|
|
599
|
+
}
|
|
600
|
+
if (change.kind === 'replace') {
|
|
601
|
+
if (held !== undefined) {
|
|
602
|
+
if (!('content' in held))
|
|
603
|
+
return 'refused';
|
|
604
|
+
changes[whole] = { ...held, content: change.content };
|
|
605
|
+
return 'folded';
|
|
606
|
+
}
|
|
607
|
+
const parts = changes.filter((item) => isPart(item) && item.path === change.path);
|
|
608
|
+
if (parts.length === 0 && !appending)
|
|
609
|
+
return 'skipped';
|
|
610
|
+
if (occupied)
|
|
611
|
+
return 'refused';
|
|
612
|
+
changes.splice(0, changes.length, ...changes.filter((item) => !parts.includes(item)), change);
|
|
613
|
+
return 'appended';
|
|
614
|
+
}
|
|
615
|
+
if (changes.some((item) => touched(change).some((path) => touched(item).includes(path))))
|
|
616
|
+
return 'refused';
|
|
617
|
+
changes.push(change);
|
|
618
|
+
return 'appended';
|
|
619
|
+
}
|
|
620
|
+
function relinked(batch, move, before, after, appending) {
|
|
621
|
+
const derived = finished(rewrites([move], after));
|
|
622
|
+
if (derived.kind === 'refused')
|
|
623
|
+
return derived;
|
|
624
|
+
const changes = [...batch.changes];
|
|
625
|
+
const appended = [];
|
|
626
|
+
let changed = false;
|
|
627
|
+
const deferred = new Map();
|
|
628
|
+
for (const change of derived.changes) {
|
|
629
|
+
if (change === move)
|
|
630
|
+
continue;
|
|
631
|
+
const unsettled = isPart(change) ? null : settledParts(changes, deferred);
|
|
632
|
+
if (unsettled !== null)
|
|
633
|
+
return unqueueable(unsettled);
|
|
634
|
+
const outcome = folded(changes, change, appending, deferred);
|
|
635
|
+
if (outcome === 'refused')
|
|
636
|
+
return unqueueable(change.path);
|
|
637
|
+
if (outcome === 'appended')
|
|
638
|
+
appended.push(change);
|
|
639
|
+
if (outcome !== 'skipped')
|
|
640
|
+
changed = true;
|
|
641
|
+
}
|
|
642
|
+
const unsettled = settledParts(changes, deferred);
|
|
643
|
+
if (unsettled !== null)
|
|
644
|
+
return unqueueable(unsettled);
|
|
645
|
+
if (!changed)
|
|
646
|
+
return { kind: 'relinked', batch };
|
|
647
|
+
const held = new Set(batch.preconditions.map(identity));
|
|
648
|
+
return {
|
|
649
|
+
kind: 'relinked',
|
|
650
|
+
batch: immutable({
|
|
651
|
+
...batch,
|
|
652
|
+
id: changeBatchIdentity(),
|
|
653
|
+
changes,
|
|
654
|
+
preconditions: [
|
|
655
|
+
...batch.preconditions,
|
|
656
|
+
...finished(preconditionsOf(appended, before, [])).filter((precondition) => !held.has(identity(precondition))),
|
|
657
|
+
],
|
|
658
|
+
}),
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
function leading(changes, at) {
|
|
662
|
+
const from = departure(changes, at);
|
|
663
|
+
return new Set(changes.filter((change, index) => index < at && change.kind === 'move' && departure(changes, index) === from));
|
|
664
|
+
}
|
|
665
|
+
function created(batch, at) {
|
|
666
|
+
const file = batch.changes[at];
|
|
667
|
+
return (leading(batch.changes, at).size === 0 &&
|
|
668
|
+
batch.preconditions.some((precondition) => precondition.kind === 'absent' && precondition.path === file.path));
|
|
669
|
+
}
|
|
670
|
+
function withdrawnFile(batch, change, at) {
|
|
671
|
+
const file = batch.changes[at];
|
|
672
|
+
const from = departure(batch.changes, at);
|
|
673
|
+
const moves = leading(batch.changes, at);
|
|
674
|
+
const fresh = created(batch, at);
|
|
675
|
+
const vacated = new Set([...moves].map((move) => move.to));
|
|
676
|
+
if (fresh)
|
|
677
|
+
vacated.add(file.path);
|
|
678
|
+
const replaced = fresh
|
|
679
|
+
? change.kind === 'remove'
|
|
680
|
+
? []
|
|
681
|
+
: [{ ...file, path: change.to }]
|
|
682
|
+
: change.kind === 'remove'
|
|
683
|
+
? [{ kind: 'remove', path: from }]
|
|
684
|
+
: change.to === from
|
|
685
|
+
? [{ ...file, path: from }]
|
|
686
|
+
: [
|
|
687
|
+
{ kind: 'move', path: from, to: change.to },
|
|
688
|
+
{ ...file, path: change.to },
|
|
689
|
+
];
|
|
690
|
+
const added = change.kind === 'move' && (fresh || change.to !== from)
|
|
691
|
+
? [{ kind: 'absent', path: change.to }]
|
|
692
|
+
: [];
|
|
693
|
+
const changes = batch.changes.flatMap((item) => item === file ? replaced : item.kind === 'move' && moves.has(item) ? [] : [item]);
|
|
694
|
+
if (changes.length === 0)
|
|
695
|
+
return null;
|
|
696
|
+
const preconditions = [
|
|
697
|
+
...added,
|
|
698
|
+
...batch.preconditions.filter((precondition) => precondition.kind !== 'absent' || !vacated.has(precondition.path)),
|
|
699
|
+
];
|
|
700
|
+
return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
|
|
701
|
+
}
|
|
702
|
+
export function requeued(queue, changes, visible, changeable, state) {
|
|
703
|
+
const next = [...queue];
|
|
704
|
+
const origins = new Map();
|
|
705
|
+
const withdrawn = new Map();
|
|
706
|
+
const owners = new Set();
|
|
707
|
+
const queued = new Set();
|
|
708
|
+
const kept = [];
|
|
709
|
+
const originOf = (batch) => origins.get(batch) ?? batch.id;
|
|
710
|
+
const open = (batch) => origins.has(batch) || changeable(batch);
|
|
711
|
+
for (const change of changes) {
|
|
712
|
+
const held = visible.binaries[change.path];
|
|
713
|
+
if ((change.kind !== 'move' && change.kind !== 'remove') || held?.state !== 'pending') {
|
|
714
|
+
kept.push(change);
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
let index = next.length - 1;
|
|
718
|
+
while (index >= 0 && queuedFile(next[index], change.path, held.localId) === -1)
|
|
719
|
+
index -= 1;
|
|
720
|
+
const owner = next[index];
|
|
721
|
+
if (owner === undefined || !open(owner))
|
|
722
|
+
return unqueueable(change.path);
|
|
723
|
+
const following = next.slice(index);
|
|
724
|
+
const rebuilt = [];
|
|
725
|
+
let before = change.kind === 'move' ? state(next.slice(0, index)) : visible;
|
|
726
|
+
for (const [offset, batch] of following.entries()) {
|
|
727
|
+
let current = batch;
|
|
728
|
+
if (change.kind === 'move') {
|
|
729
|
+
const after = state(next.slice(0, index + offset + 1));
|
|
730
|
+
const appending = offset === 0 && created(batch, queuedFile(batch, change.path, held.localId));
|
|
731
|
+
const relinking = relinked(batch, change, before, after, appending);
|
|
732
|
+
if (relinking.kind === 'refused')
|
|
733
|
+
return relinking;
|
|
734
|
+
current = relinking.batch;
|
|
735
|
+
before = after;
|
|
736
|
+
}
|
|
737
|
+
if (current !== batch && !open(batch))
|
|
738
|
+
return unqueueable(change.path);
|
|
739
|
+
rebuilt.push(offset === 0
|
|
740
|
+
? withdrawnFile(current, change, queuedFile(current, change.path, held.localId))
|
|
741
|
+
: current);
|
|
742
|
+
}
|
|
743
|
+
for (const [offset, batch] of following.entries()) {
|
|
744
|
+
const replaced = rebuilt[offset];
|
|
745
|
+
if (replaced === batch)
|
|
746
|
+
continue;
|
|
747
|
+
const origin = originOf(batch);
|
|
748
|
+
withdrawn.set(origin, replaced);
|
|
749
|
+
if (replaced !== null)
|
|
750
|
+
origins.set(replaced, origin);
|
|
751
|
+
}
|
|
752
|
+
owners.add(originOf(owner));
|
|
753
|
+
next.splice(index, following.length, ...rebuilt.filter((batch) => batch !== null));
|
|
754
|
+
queued.add(change.path);
|
|
755
|
+
}
|
|
756
|
+
return {
|
|
757
|
+
kind: 'requeued',
|
|
758
|
+
queue: withdrawn.size === 0 ? queue : next,
|
|
759
|
+
changes: kept,
|
|
760
|
+
queued,
|
|
761
|
+
withdrawn,
|
|
762
|
+
owners,
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
function covering(precondition) {
|
|
766
|
+
return precondition.kind === 'field' ||
|
|
767
|
+
precondition.kind === 'section' ||
|
|
768
|
+
precondition.kind === 'preamble'
|
|
769
|
+
? identity(precondition)
|
|
770
|
+
: `path ${precondition.path}`;
|
|
771
|
+
}
|
|
772
|
+
export function joined(queue, owner, extra) {
|
|
773
|
+
const later = new Set(queue.slice(queue.indexOf(owner) + 1).flatMap((batch) => batch.changes.flatMap(touched)));
|
|
774
|
+
const clash = extra.changes.flatMap(touched).find((path) => later.has(path));
|
|
775
|
+
if (clash !== undefined)
|
|
776
|
+
return unqueueable(clash);
|
|
777
|
+
const changes = [...owner.changes];
|
|
778
|
+
const deferred = new Map();
|
|
779
|
+
for (const change of extra.changes) {
|
|
780
|
+
const unsettled = isPart(change) ? null : settledParts(changes, deferred);
|
|
781
|
+
if (unsettled !== null)
|
|
782
|
+
return unqueueable(unsettled);
|
|
783
|
+
if (folded(changes, change, true, deferred) === 'refused')
|
|
784
|
+
return unqueueable(change.path);
|
|
785
|
+
}
|
|
786
|
+
const unsettled = settledParts(changes, deferred);
|
|
787
|
+
if (unsettled !== null)
|
|
788
|
+
return unqueueable(unsettled);
|
|
789
|
+
const held = new Set(owner.preconditions.map(covering));
|
|
790
|
+
const written = new Set(owner.changes.flatMap((change) => change.kind === 'create' || change.kind === 'replace' ? [change.path] : []));
|
|
791
|
+
const preconditions = [
|
|
792
|
+
...owner.preconditions,
|
|
793
|
+
...extra.preconditions.filter((precondition) => {
|
|
794
|
+
const key = covering(precondition);
|
|
795
|
+
return (!held.has(key) && (key === `path ${precondition.path}` || !written.has(precondition.path)));
|
|
796
|
+
}),
|
|
797
|
+
];
|
|
798
|
+
const batch = immutable({
|
|
799
|
+
...owner,
|
|
800
|
+
...(extra.validation === undefined ? {} : { validation: extra.validation }),
|
|
801
|
+
id: changeBatchIdentity(),
|
|
802
|
+
changes,
|
|
803
|
+
preconditions,
|
|
804
|
+
});
|
|
805
|
+
return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
|
|
806
|
+
}
|
|
807
|
+
export function limitedIn(queue, batch, limits, state) {
|
|
808
|
+
const at = queue.indexOf(batch);
|
|
809
|
+
const limited = finished(exceeding(limits, batch.changes, finished(providerRewritten(batch, state(queue.slice(0, at)))).changes, state(queue.slice(0, at + 1))));
|
|
810
|
+
return limited?.kind === 'refused' ? limited.refusal : null;
|
|
811
|
+
}
|
|
812
|
+
function* batching(input) {
|
|
435
813
|
const changes = relativeChanges(input);
|
|
436
814
|
const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
|
|
437
|
-
if (structural !== undefined &&
|
|
815
|
+
if (structural !== undefined &&
|
|
816
|
+
(yield* unmodeledType(input.visible.collection, input.composition)))
|
|
438
817
|
return refuse('model-unavailable', structural.path);
|
|
439
818
|
if (changes.length === 0)
|
|
440
819
|
return refuse('no-change');
|
|
441
|
-
const rewritten = rewrites(changes, input.visible);
|
|
820
|
+
const rewritten = yield* rewrites(changes, input.visible);
|
|
442
821
|
if (rewritten.kind === 'refused')
|
|
443
822
|
return rewritten;
|
|
444
823
|
const refusal = guard(changes, input);
|
|
445
824
|
if (refusal !== null)
|
|
446
825
|
return refusal;
|
|
447
|
-
const preconditions = preconditionsOf(changes, input.visible, input.bases);
|
|
448
|
-
const predicted = anticipated(changes, preconditions, rewritten.changes, input.visible);
|
|
449
|
-
const resulting =
|
|
826
|
+
const preconditions = yield* preconditionsOf(changes, input.visible, input.bases);
|
|
827
|
+
const predicted = yield* anticipated(changes, preconditions, rewritten.changes, input.visible);
|
|
828
|
+
const resulting = yield* overlaying(input.visible, [
|
|
450
829
|
{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted },
|
|
451
830
|
]);
|
|
831
|
+
const clean = input.clean !== undefined && available(input.composition) ? input.clean : null;
|
|
452
832
|
const removed = new Set(changes.filter((change) => change.kind === 'remove').map((change) => change.path));
|
|
453
833
|
if (removed.size > 0) {
|
|
454
|
-
const
|
|
834
|
+
const referrers = clean === null ? null : referring(clean, input.composition, removed);
|
|
835
|
+
const referenced = yield* incoming(collectionOf(resulting.documents, input.composition), removed, clean === null || referrers === null
|
|
836
|
+
? null
|
|
837
|
+
: [...referrers, ...(yield* differences(clean.text, resulting.documents))]);
|
|
455
838
|
if (referenced !== null)
|
|
456
839
|
return referenced;
|
|
457
840
|
}
|
|
@@ -461,15 +844,27 @@ export function buildBatch(input) {
|
|
|
461
844
|
const parsed = parse(content);
|
|
462
845
|
return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
|
|
463
846
|
};
|
|
464
|
-
const
|
|
847
|
+
const touched = [...new Set(predicted.changes.flatMap(affected))];
|
|
848
|
+
const typedPaths = [];
|
|
849
|
+
for (const path of touched) {
|
|
850
|
+
const before = input.visible.documents[path];
|
|
851
|
+
const after = resulting.documents[path];
|
|
852
|
+
if (typed(before) || typed(after))
|
|
853
|
+
typedPaths.push(path);
|
|
854
|
+
yield ROW_WEIGHT + (before?.length ?? 0) + (after?.length ?? 0);
|
|
855
|
+
}
|
|
465
856
|
const needsModel = typedPaths.length > 0;
|
|
466
857
|
const withoutModel = typedPaths.find((path) => governing(input.composition, path).status === 'unavailable');
|
|
467
858
|
if (withoutModel !== undefined)
|
|
468
859
|
return refuse('model-unavailable', withoutModel);
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
860
|
+
const verdict = clean !== null &&
|
|
861
|
+
(yield* changedCleanly({ text: resulting.documents, binaries: resulting.binaries }, clean, input.composition))
|
|
862
|
+
? { verdict: 'accept' }
|
|
863
|
+
: yield* validateCollection(needsModel
|
|
864
|
+
? resulting.documents
|
|
865
|
+
: Object.fromEntries(touched.flatMap((path) => resulting.documents[path] === undefined
|
|
866
|
+
? []
|
|
867
|
+
: [[path, resulting.documents[path]]])), Object.keys(resulting.binaries), input.composition);
|
|
473
868
|
if (verdict.verdict === 'refuse') {
|
|
474
869
|
return {
|
|
475
870
|
kind: 'refused',
|
|
@@ -482,7 +877,7 @@ export function buildBatch(input) {
|
|
|
482
877
|
},
|
|
483
878
|
};
|
|
484
879
|
}
|
|
485
|
-
const limited =
|
|
880
|
+
const limited = yield* exceeding(input.providerLimits, changes, predicted.changes, resulting);
|
|
486
881
|
if (limited !== null)
|
|
487
882
|
return limited;
|
|
488
883
|
const validated = typedPaths
|
|
@@ -508,3 +903,6 @@ export function buildBatch(input) {
|
|
|
508
903
|
}),
|
|
509
904
|
};
|
|
510
905
|
}
|
|
906
|
+
export function buildBatch(input) {
|
|
907
|
+
return stepped(batching(input));
|
|
908
|
+
}
|