@agentshouse/mdruntime 0.1.0-ca9afacd5aebd → 0.1.0-caee7332d2235
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 +271 -290
- package/README.md +8 -8
- package/dist/adapter.d.ts +52 -10
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attempt.d.ts.map +1 -1
- package/dist/attempt.js +14 -1
- package/dist/bootstrap.d.ts +3 -2
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +33 -7
- package/dist/change-set.d.ts +7 -2
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +41 -10
- package/dist/chat/acts.d.ts +3 -4
- package/dist/chat/acts.d.ts.map +1 -1
- package/dist/chat/acts.js +10 -10
- 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/kit.d.ts +2 -1
- package/dist/chat/kit.d.ts.map +1 -1
- package/dist/chat/kit.js +3 -3
- package/dist/chat/list.d.ts +8 -1
- package/dist/chat/list.d.ts.map +1 -1
- package/dist/chat/list.js +48 -8
- package/dist/chat/markdown.d.ts +3 -1
- package/dist/chat/markdown.d.ts.map +1 -1
- package/dist/chat/markdown.js +85 -26
- package/dist/chat/message.d.ts.map +1 -1
- package/dist/chat/message.js +3 -3
- package/dist/chat/messages.d.ts +1 -0
- package/dist/chat/messages.d.ts.map +1 -1
- package/dist/chat/messages.js +35 -15
- 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/provider.d.ts +1 -1
- package/dist/chat/provider.d.ts.map +1 -1
- package/dist/chat/provider.js +65 -80
- package/dist/chat/question.d.ts +1 -1
- package/dist/chat/question.d.ts.map +1 -1
- package/dist/chat/question.js +7 -18
- package/dist/chat/ready.d.ts +1 -0
- package/dist/chat/ready.d.ts.map +1 -1
- package/dist/chat/ready.js +4 -4
- 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.js +4 -4
- package/dist/chat.d.ts +1 -1
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +1 -1
- package/dist/client.d.ts +17 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +19 -6
- 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 +31 -9
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1602 -386
- package/dist/conversation.d.ts +20 -7
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +474 -163
- 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/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 +94 -12
- package/dist/identity.d.ts +2 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +6 -0
- package/dist/index.d.ts +10 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/memory.d.ts +8 -2
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +33 -6
- 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 +12 -2
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +4 -0
- package/dist/overlay.d.ts +5 -3
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +203 -159
- package/dist/query.d.ts +56 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +36 -7
- package/dist/reach.d.ts +6 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +30 -5
- package/dist/react.d.ts +8 -6
- 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 +486 -100
- 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 +44 -18
- package/dist/replica.d.ts +11 -5
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +111 -56
- package/dist/room-draft.d.ts +13 -1
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +11 -12
- package/dist/rows.d.ts +6 -13
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +10 -13
- package/dist/save.d.ts +10 -4
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +216 -108
- 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 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/store.d.ts +61 -12
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +580 -268
- 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 +152 -32
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +52 -31
- package/package.json +12 -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
3
|
import { immutable, queuedFile, departure, } from './change.js';
|
|
4
|
-
import { collectionOf, documentsOf, governing, validateCollection, } from './collection.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,19 +113,14 @@ 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
126
|
if (change.kind === 'remove' ||
|
|
@@ -130,6 +131,13 @@ function rewrites(changes, visible) {
|
|
|
130
131
|
}
|
|
131
132
|
if (change.kind === 'field')
|
|
132
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
|
+
}
|
|
133
141
|
}
|
|
134
142
|
if (moves.size === 0)
|
|
135
143
|
return { kind: 'changes', changes };
|
|
@@ -141,43 +149,57 @@ function rewrites(changes, visible) {
|
|
|
141
149
|
const moved = moves.get(path) ?? path;
|
|
142
150
|
const definition = definitionOf(governing(visible.collection.composition, path), document.type);
|
|
143
151
|
added.push(...repointed(document, definition, path, moved, moves, settled));
|
|
144
|
-
const
|
|
152
|
+
const own = parts.get(path) ?? [];
|
|
153
|
+
const relinked = relinks(path, moved, edited(document, own.map((index) => merged[index])), moves);
|
|
145
154
|
if (relinked.kind === 'refused')
|
|
146
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
|
+
}
|
|
147
162
|
for (const change of relinked.changes) {
|
|
148
163
|
if (change.kind === 'replace') {
|
|
149
|
-
|
|
164
|
+
for (const index of own) {
|
|
165
|
+
const kind = merged[index]?.kind;
|
|
166
|
+
if (kind === 'section' || kind === 'preamble')
|
|
167
|
+
merged[index] = null;
|
|
168
|
+
}
|
|
150
169
|
added.push(change);
|
|
151
170
|
continue;
|
|
152
171
|
}
|
|
153
|
-
const index =
|
|
154
|
-
|
|
155
|
-
(change.kind !== 'section' || (item.kind === 'section' && item.name === change.name)));
|
|
156
|
-
if (index === -1)
|
|
172
|
+
const index = keyed.get(partKey(change));
|
|
173
|
+
if (index === undefined)
|
|
157
174
|
added.push(change);
|
|
158
175
|
else
|
|
159
176
|
merged[index] = change;
|
|
160
177
|
}
|
|
178
|
+
yield ROW_WEIGHT + visible.collection.text[path].length;
|
|
161
179
|
}
|
|
162
|
-
return {
|
|
180
|
+
return {
|
|
181
|
+
kind: 'changes',
|
|
182
|
+
changes: [...added, ...merged.filter((change) => change !== null)],
|
|
183
|
+
};
|
|
163
184
|
}
|
|
164
|
-
function anticipated(changes, preconditions, rewritten, visible) {
|
|
165
|
-
const
|
|
185
|
+
function* anticipated(changes, preconditions, rewritten, visible) {
|
|
186
|
+
const given = new Set(changes);
|
|
187
|
+
const derived = rewritten.filter((change) => !given.has(change));
|
|
166
188
|
return {
|
|
167
189
|
changes: rewritten,
|
|
168
|
-
preconditions: [...preconditionsOf(derived, visible, []), ...preconditions],
|
|
190
|
+
preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...preconditions],
|
|
169
191
|
};
|
|
170
192
|
}
|
|
171
|
-
export function providerRewritten(batch, visible) {
|
|
172
|
-
const rewritten = rewrites(batch.changes, visible);
|
|
193
|
+
export function* providerRewritten(batch, visible) {
|
|
194
|
+
const rewritten = yield* rewrites(batch.changes, visible);
|
|
173
195
|
if (rewritten.kind === 'refused')
|
|
174
196
|
return batch;
|
|
175
197
|
return {
|
|
176
198
|
...batch,
|
|
177
|
-
...anticipated(batch.changes, batch.preconditions, rewritten.changes, visible),
|
|
199
|
+
...(yield* anticipated(batch.changes, batch.preconditions, rewritten.changes, visible)),
|
|
178
200
|
};
|
|
179
201
|
}
|
|
180
|
-
function incoming(collection, removed) {
|
|
202
|
+
function* incoming(collection, removed, candidates) {
|
|
181
203
|
const found = new Map();
|
|
182
204
|
const note = (target, file) => {
|
|
183
205
|
if (!removed.has(target))
|
|
@@ -186,7 +208,14 @@ function incoming(collection, removed) {
|
|
|
186
208
|
referrers.add(file);
|
|
187
209
|
found.set(target, referrers);
|
|
188
210
|
};
|
|
189
|
-
|
|
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)) {
|
|
190
219
|
const definition = definitionOf(governing(collection.composition, path), document.type);
|
|
191
220
|
for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
|
|
192
221
|
if (to.includes(ATTACHMENT))
|
|
@@ -195,6 +224,7 @@ function incoming(collection, removed) {
|
|
|
195
224
|
if (named !== null)
|
|
196
225
|
note(named, path);
|
|
197
226
|
}
|
|
227
|
+
yield ROW_WEIGHT + collection.text[path].length;
|
|
198
228
|
}
|
|
199
229
|
const [first] = [...found.keys()].sort();
|
|
200
230
|
if (first === undefined)
|
|
@@ -217,10 +247,13 @@ function affected(change) {
|
|
|
217
247
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
218
248
|
}
|
|
219
249
|
function guard(changes, input) {
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
+
};
|
|
224
257
|
for (const [index, change] of changes.entries()) {
|
|
225
258
|
for (const path of affected(change)) {
|
|
226
259
|
const normalized = normalize(path);
|
|
@@ -275,7 +308,7 @@ function guard(changes, input) {
|
|
|
275
308
|
continue;
|
|
276
309
|
}
|
|
277
310
|
if (change.kind === 'replace') {
|
|
278
|
-
if (
|
|
311
|
+
if (unrevised(input, change.path)) {
|
|
279
312
|
return refuse('unknown-revision', change.path);
|
|
280
313
|
}
|
|
281
314
|
continue;
|
|
@@ -289,6 +322,22 @@ function guard(changes, input) {
|
|
|
289
322
|
function unrevised(input, path) {
|
|
290
323
|
return (!input.local && input.queued?.has(path) !== true && input.visible.revisions[path] === undefined);
|
|
291
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
|
+
}
|
|
292
341
|
function identity(precondition) {
|
|
293
342
|
const part = precondition.kind === 'field' || precondition.kind === 'section' ? precondition.name : '';
|
|
294
343
|
return `${precondition.kind} ${precondition.path} ${part}`;
|
|
@@ -306,7 +355,7 @@ function sameTarget(precondition, change) {
|
|
|
306
355
|
return ((precondition.kind !== 'field' && precondition.kind !== 'section') ||
|
|
307
356
|
((change.kind === 'field' || change.kind === 'section') && precondition.name === change.name));
|
|
308
357
|
}
|
|
309
|
-
function preconditionsOf(changes, visible, bases) {
|
|
358
|
+
function* preconditionsOf(changes, visible, bases) {
|
|
310
359
|
const preconditions = [];
|
|
311
360
|
const seen = new Set();
|
|
312
361
|
const add = (precondition) => {
|
|
@@ -317,6 +366,7 @@ function preconditionsOf(changes, visible, bases) {
|
|
|
317
366
|
preconditions.push(precondition);
|
|
318
367
|
};
|
|
319
368
|
for (const change of changes) {
|
|
369
|
+
yield ROW_WEIGHT;
|
|
320
370
|
if (change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble') {
|
|
321
371
|
const based = bases.find((base) => sameTarget(base, change));
|
|
322
372
|
if (based !== undefined) {
|
|
@@ -364,8 +414,8 @@ function preconditionsOf(changes, visible, bases) {
|
|
|
364
414
|
function depth(path) {
|
|
365
415
|
return path.split('/').length;
|
|
366
416
|
}
|
|
367
|
-
function
|
|
368
|
-
const written =
|
|
417
|
+
function* exceeding(limits, changes, effective, resulting) {
|
|
418
|
+
const written = effective.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
369
419
|
const created = changes
|
|
370
420
|
.filter((change) => change.kind === 'create' || change.kind === 'move' || change.kind === 'binary')
|
|
371
421
|
.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
@@ -381,9 +431,13 @@ function exceeded(limits, changes, resulting) {
|
|
|
381
431
|
return refuse('provider-limit', undefined, limit.name);
|
|
382
432
|
}
|
|
383
433
|
if (limit.name === 'document-size') {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
+
}
|
|
387
441
|
}
|
|
388
442
|
if (limit.name === 'path-length') {
|
|
389
443
|
const long = created.find((path) => path.length > value);
|
|
@@ -413,7 +467,7 @@ function relativeField(path, name, value, handle, definition) {
|
|
|
413
467
|
!opened.ok ||
|
|
414
468
|
!JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
415
469
|
return value;
|
|
416
|
-
const held = apply(opened.document, { kind: 'field', name, value, prior: undefined });
|
|
470
|
+
const held = apply(opened.document, [{ kind: 'field', name, value, prior: undefined }]);
|
|
417
471
|
if (held.outcome !== 'applied')
|
|
418
472
|
return value;
|
|
419
473
|
const result = relativize(held.document, path, handle, definition);
|
|
@@ -449,12 +503,15 @@ function relativeChanges(input) {
|
|
|
449
503
|
return { ...change, fields, sections };
|
|
450
504
|
});
|
|
451
505
|
}
|
|
452
|
-
function unmodeledType(collection, composition) {
|
|
506
|
+
function* unmodeledType(collection, composition) {
|
|
507
|
+
if (available(composition))
|
|
508
|
+
return false;
|
|
453
509
|
for (const [path, document] of documentsOf(collection)) {
|
|
454
510
|
if (document.type !== undefined &&
|
|
455
511
|
document.type !== 'any' &&
|
|
456
512
|
governing(composition, path).status === 'unavailable')
|
|
457
513
|
return true;
|
|
514
|
+
yield ROW_WEIGHT + collection.text[path].length;
|
|
458
515
|
}
|
|
459
516
|
return false;
|
|
460
517
|
}
|
|
@@ -467,29 +524,44 @@ function isPart(change) {
|
|
|
467
524
|
function touched(change) {
|
|
468
525
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
469
526
|
}
|
|
470
|
-
function
|
|
527
|
+
function partsApplied(content, path, changes) {
|
|
471
528
|
const parsed = parse(content, path);
|
|
472
529
|
if (!parsed.ok)
|
|
473
530
|
return null;
|
|
474
531
|
const document = parsed.document;
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
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}`, {
|
|
484
543
|
kind: 'section',
|
|
485
544
|
name: change.name,
|
|
486
545
|
value: change.value,
|
|
487
|
-
prior: document.sections[change.name],
|
|
488
|
-
}
|
|
489
|
-
|
|
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()]);
|
|
490
552
|
return applied.outcome === 'applied' ? serialize(applied.document) : null;
|
|
491
553
|
}
|
|
492
|
-
function
|
|
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) {
|
|
493
565
|
const held = changes.find((item) => item.path === change.path && (item.kind === 'create' || item.kind === 'replace'));
|
|
494
566
|
const whole = held === undefined ? -1 : changes.indexOf(held);
|
|
495
567
|
const occupied = changes.some((item) => !isPart(item) && item !== held && touched(item).includes(change.path));
|
|
@@ -510,10 +582,11 @@ function folded(changes, change, appending) {
|
|
|
510
582
|
return 'appended';
|
|
511
583
|
}
|
|
512
584
|
if ('content' in held) {
|
|
513
|
-
const
|
|
514
|
-
if (
|
|
515
|
-
|
|
516
|
-
|
|
585
|
+
const parts = deferred.get(held);
|
|
586
|
+
if (parts === undefined)
|
|
587
|
+
deferred.set(held, [change]);
|
|
588
|
+
else
|
|
589
|
+
parts.push(change);
|
|
517
590
|
return 'folded';
|
|
518
591
|
}
|
|
519
592
|
if (change.kind === 'preamble')
|
|
@@ -545,16 +618,20 @@ function folded(changes, change, appending) {
|
|
|
545
618
|
return 'appended';
|
|
546
619
|
}
|
|
547
620
|
function relinked(batch, move, before, after, appending) {
|
|
548
|
-
const derived = rewrites([move], after);
|
|
621
|
+
const derived = finished(rewrites([move], after));
|
|
549
622
|
if (derived.kind === 'refused')
|
|
550
623
|
return derived;
|
|
551
624
|
const changes = [...batch.changes];
|
|
552
625
|
const appended = [];
|
|
553
626
|
let changed = false;
|
|
627
|
+
const deferred = new Map();
|
|
554
628
|
for (const change of derived.changes) {
|
|
555
629
|
if (change === move)
|
|
556
630
|
continue;
|
|
557
|
-
const
|
|
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);
|
|
558
635
|
if (outcome === 'refused')
|
|
559
636
|
return unqueueable(change.path);
|
|
560
637
|
if (outcome === 'appended')
|
|
@@ -562,6 +639,9 @@ function relinked(batch, move, before, after, appending) {
|
|
|
562
639
|
if (outcome !== 'skipped')
|
|
563
640
|
changed = true;
|
|
564
641
|
}
|
|
642
|
+
const unsettled = settledParts(changes, deferred);
|
|
643
|
+
if (unsettled !== null)
|
|
644
|
+
return unqueueable(unsettled);
|
|
565
645
|
if (!changed)
|
|
566
646
|
return { kind: 'relinked', batch };
|
|
567
647
|
const held = new Set(batch.preconditions.map(identity));
|
|
@@ -573,7 +653,7 @@ function relinked(batch, move, before, after, appending) {
|
|
|
573
653
|
changes,
|
|
574
654
|
preconditions: [
|
|
575
655
|
...batch.preconditions,
|
|
576
|
-
...preconditionsOf(appended, before, []).filter((precondition) => !held.has(identity(precondition))),
|
|
656
|
+
...finished(preconditionsOf(appended, before, [])).filter((precondition) => !held.has(identity(precondition))),
|
|
577
657
|
],
|
|
578
658
|
}),
|
|
579
659
|
};
|
|
@@ -695,10 +775,17 @@ export function joined(queue, owner, extra) {
|
|
|
695
775
|
if (clash !== undefined)
|
|
696
776
|
return unqueueable(clash);
|
|
697
777
|
const changes = [...owner.changes];
|
|
778
|
+
const deferred = new Map();
|
|
698
779
|
for (const change of extra.changes) {
|
|
699
|
-
|
|
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')
|
|
700
784
|
return unqueueable(change.path);
|
|
701
785
|
}
|
|
786
|
+
const unsettled = settledParts(changes, deferred);
|
|
787
|
+
if (unsettled !== null)
|
|
788
|
+
return unqueueable(unsettled);
|
|
702
789
|
const held = new Set(owner.preconditions.map(covering));
|
|
703
790
|
const written = new Set(owner.changes.flatMap((change) => change.kind === 'create' || change.kind === 'replace' ? [change.path] : []));
|
|
704
791
|
const preconditions = [
|
|
@@ -718,30 +805,36 @@ export function joined(queue, owner, extra) {
|
|
|
718
805
|
return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
|
|
719
806
|
}
|
|
720
807
|
export function limitedIn(queue, batch, limits, state) {
|
|
721
|
-
const
|
|
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))));
|
|
722
810
|
return limited?.kind === 'refused' ? limited.refusal : null;
|
|
723
811
|
}
|
|
724
|
-
|
|
812
|
+
function* batching(input) {
|
|
725
813
|
const changes = relativeChanges(input);
|
|
726
814
|
const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
|
|
727
|
-
if (structural !== undefined &&
|
|
815
|
+
if (structural !== undefined &&
|
|
816
|
+
(yield* unmodeledType(input.visible.collection, input.composition)))
|
|
728
817
|
return refuse('model-unavailable', structural.path);
|
|
729
818
|
if (changes.length === 0)
|
|
730
819
|
return refuse('no-change');
|
|
731
|
-
const rewritten = rewrites(changes, input.visible);
|
|
820
|
+
const rewritten = yield* rewrites(changes, input.visible);
|
|
732
821
|
if (rewritten.kind === 'refused')
|
|
733
822
|
return rewritten;
|
|
734
823
|
const refusal = guard(changes, input);
|
|
735
824
|
if (refusal !== null)
|
|
736
825
|
return refusal;
|
|
737
|
-
const preconditions = preconditionsOf(changes, input.visible, input.bases);
|
|
738
|
-
const predicted = anticipated(changes, preconditions, rewritten.changes, input.visible);
|
|
739
|
-
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, [
|
|
740
829
|
{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted },
|
|
741
830
|
]);
|
|
831
|
+
const clean = input.clean !== undefined && available(input.composition) ? input.clean : null;
|
|
742
832
|
const removed = new Set(changes.filter((change) => change.kind === 'remove').map((change) => change.path));
|
|
743
833
|
if (removed.size > 0) {
|
|
744
|
-
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))]);
|
|
745
838
|
if (referenced !== null)
|
|
746
839
|
return referenced;
|
|
747
840
|
}
|
|
@@ -751,15 +844,27 @@ export function buildBatch(input) {
|
|
|
751
844
|
const parsed = parse(content);
|
|
752
845
|
return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
|
|
753
846
|
};
|
|
754
|
-
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
|
+
}
|
|
755
856
|
const needsModel = typedPaths.length > 0;
|
|
756
857
|
const withoutModel = typedPaths.find((path) => governing(input.composition, path).status === 'unavailable');
|
|
757
858
|
if (withoutModel !== undefined)
|
|
758
859
|
return refuse('model-unavailable', withoutModel);
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
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);
|
|
763
868
|
if (verdict.verdict === 'refuse') {
|
|
764
869
|
return {
|
|
765
870
|
kind: 'refused',
|
|
@@ -772,7 +877,7 @@ export function buildBatch(input) {
|
|
|
772
877
|
},
|
|
773
878
|
};
|
|
774
879
|
}
|
|
775
|
-
const limited =
|
|
880
|
+
const limited = yield* exceeding(input.providerLimits, changes, predicted.changes, resulting);
|
|
776
881
|
if (limited !== null)
|
|
777
882
|
return limited;
|
|
778
883
|
const validated = typedPaths
|
|
@@ -798,3 +903,6 @@ export function buildBatch(input) {
|
|
|
798
903
|
}),
|
|
799
904
|
};
|
|
800
905
|
}
|
|
906
|
+
export function buildBatch(input) {
|
|
907
|
+
return stepped(batching(input));
|
|
908
|
+
}
|