@agentshouse/mdruntime 0.1.0-cacb4c3f6734e → 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 +277 -222
- package/README.md +20 -10
- package/dist/adapter.d.ts +95 -20
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +15 -2
- 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 +18 -5
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +113 -37
- package/dist/change-set.d.ts +7 -2
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +54 -16
- package/dist/change.d.ts +15 -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 +27 -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 +192 -0
- package/dist/chat/messages.d.ts +22 -0
- package/dist/chat/messages.d.ts.map +1 -0
- package/dist/chat/messages.js +171 -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 +31 -0
- package/dist/chat/overlay.d.ts.map +1 -0
- package/dist/chat/overlay.js +97 -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 +63 -0
- package/dist/chat/provider.d.ts.map +1 -0
- package/dist/chat/provider.js +630 -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 +30 -13
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +79 -16
- package/dist/collection.d.ts +23 -13
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +349 -36
- package/dist/connection.d.ts +50 -16
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1968 -424
- package/dist/conversation.d.ts +31 -10
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +521 -170
- 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/failure.d.ts +13 -0
- package/dist/failure.d.ts.map +1 -0
- package/dist/failure.js +19 -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 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +102 -19
- package/dist/identity.d.ts +5 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +19 -1
- package/dist/index.d.ts +16 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/memory.d.ts +11 -4
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +91 -24
- package/dist/model.d.ts +14 -4
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +12 -1
- package/dist/observation.d.ts +7 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +29 -12
- package/dist/open.d.ts +2 -0
- package/dist/open.d.ts.map +1 -0
- package/dist/open.js +1 -0
- package/dist/outcome.d.ts +38 -3
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +45 -1
- package/dist/overlay.d.ts +18 -5
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +247 -165
- package/dist/query.d.ts +60 -4
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +41 -38
- package/dist/reach.d.ts +8 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +73 -24
- package/dist/react.d.ts +31 -20
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +13 -21
- 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 +13 -3
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +72 -42
- package/dist/replica.d.ts +17 -8
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +165 -79
- package/dist/room-draft.d.ts +38 -9
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +141 -33
- package/dist/rows.d.ts +10 -13
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +19 -14
- package/dist/save.d.ts +35 -3
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +522 -107
- package/dist/search.d.ts +52 -8
- 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 +6 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +0 -8
- package/dist/store.d.ts +91 -18
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +704 -283
- 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 +157 -33
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +65 -31
- 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,15 +1,18 @@
|
|
|
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 { governing,
|
|
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
|
-
|
|
8
|
+
import { differences } from './shared.js';
|
|
9
|
+
import { finished, ROW_WEIGHT, stepped } from './turn.js';
|
|
10
|
+
function refuse(code, file, location) {
|
|
9
11
|
return {
|
|
10
12
|
kind: 'refused',
|
|
11
13
|
refusal: {
|
|
12
|
-
|
|
14
|
+
code,
|
|
15
|
+
retryable: false,
|
|
13
16
|
...(file === undefined ? {} : { file }),
|
|
14
17
|
...(location === undefined ? {} : { location }),
|
|
15
18
|
},
|
|
@@ -19,7 +22,7 @@ function repointed(document, definition, from, to, moves, settled) {
|
|
|
19
22
|
const parsed = parse(document.content, from);
|
|
20
23
|
if (!parsed.ok)
|
|
21
24
|
return [];
|
|
22
|
-
|
|
25
|
+
const changes = [];
|
|
23
26
|
const names = new Set();
|
|
24
27
|
for (const { pointer, value } of definition === undefined
|
|
25
28
|
? []
|
|
@@ -31,38 +34,39 @@ function repointed(document, definition, from, to, moves, settled) {
|
|
|
31
34
|
const destination = moves.get(named) ?? named;
|
|
32
35
|
if (target(to, value) === destination)
|
|
33
36
|
continue;
|
|
34
|
-
|
|
37
|
+
changes.push({
|
|
35
38
|
kind: 'reference',
|
|
36
39
|
pointer,
|
|
37
40
|
value: retarget(value, relative(to, destination)),
|
|
38
41
|
prior: value,
|
|
39
42
|
});
|
|
40
|
-
if (applied.outcome !== 'applied')
|
|
41
|
-
continue;
|
|
42
|
-
next = applied.document;
|
|
43
43
|
names.add(name);
|
|
44
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;
|
|
45
51
|
return [...names].map((name) => ({ kind: 'field', path: from, name, value: next.fields[name] }));
|
|
46
52
|
}
|
|
47
|
-
function edited(document,
|
|
48
|
-
|
|
53
|
+
function edited(document, changes) {
|
|
54
|
+
const parts = new Map();
|
|
49
55
|
for (const change of changes) {
|
|
50
|
-
if (change.
|
|
51
|
-
|
|
52
|
-
if (change.kind
|
|
53
|
-
|
|
54
|
-
const applied = apply(next, change.kind === 'preamble'
|
|
55
|
-
? { kind: 'preamble', value: change.value, prior: next.preamble }
|
|
56
|
-
: {
|
|
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}`, {
|
|
57
60
|
kind: 'section',
|
|
58
61
|
name: change.name,
|
|
59
62
|
value: change.value,
|
|
60
|
-
prior:
|
|
63
|
+
prior: document.sections[change.name] ?? '',
|
|
61
64
|
});
|
|
62
|
-
if (applied.outcome === 'applied')
|
|
63
|
-
next = applied.document;
|
|
64
65
|
}
|
|
65
|
-
|
|
66
|
+
if (parts.size === 0)
|
|
67
|
+
return document;
|
|
68
|
+
const applied = apply(document, [...parts.values()]);
|
|
69
|
+
return applied.outcome === 'applied' ? applied.document : document;
|
|
66
70
|
}
|
|
67
71
|
function bodies(path, before, after) {
|
|
68
72
|
const changes = [];
|
|
@@ -77,7 +81,7 @@ function bodies(path, before, after) {
|
|
|
77
81
|
return changes;
|
|
78
82
|
}
|
|
79
83
|
function relinks(path, moved, before, moves) {
|
|
80
|
-
|
|
84
|
+
const changes = [];
|
|
81
85
|
for (const link of links(before, path)) {
|
|
82
86
|
const destination = moves.get(link.target) ?? link.target;
|
|
83
87
|
if (destination === link.target && moved === path)
|
|
@@ -85,19 +89,23 @@ function relinks(path, moved, before, moves) {
|
|
|
85
89
|
const to = relative(moved, destination);
|
|
86
90
|
if (to === relative(path, link.target))
|
|
87
91
|
continue;
|
|
88
|
-
|
|
89
|
-
if (applied.outcome === 'refuse') {
|
|
90
|
-
return {
|
|
91
|
-
kind: 'refused',
|
|
92
|
-
refusal: {
|
|
93
|
-
rule: applied.refusal.rule,
|
|
94
|
-
file: path,
|
|
95
|
-
...(applied.refusal.location === undefined ? {} : { location: applied.refusal.location }),
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
after = applied.document;
|
|
92
|
+
changes.push({ kind: 'link', href: link.href, to });
|
|
100
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
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const after = applied.document;
|
|
101
109
|
if (after === before)
|
|
102
110
|
return { kind: 'changes', changes: [] };
|
|
103
111
|
if (before.type === undefined) {
|
|
@@ -105,74 +113,93 @@ function relinks(path, moved, before, moves) {
|
|
|
105
113
|
}
|
|
106
114
|
return { kind: 'changes', changes: bodies(path, before, after) };
|
|
107
115
|
}
|
|
108
|
-
function
|
|
109
|
-
|
|
110
|
-
const item = merged[index];
|
|
111
|
-
if (item.path !== path)
|
|
112
|
-
continue;
|
|
113
|
-
if (item.kind === 'section' || item.kind === 'preamble')
|
|
114
|
-
merged.splice(index, 1);
|
|
115
|
-
}
|
|
116
|
+
function partKey(change) {
|
|
117
|
+
return change.kind === 'section' ? `section ${change.name}` : change.kind;
|
|
116
118
|
}
|
|
117
|
-
function rewrites(changes, visible) {
|
|
119
|
+
function* rewrites(changes, visible) {
|
|
118
120
|
const moves = new Map();
|
|
119
121
|
const settled = new Set();
|
|
120
|
-
|
|
122
|
+
const parts = new Map();
|
|
123
|
+
for (const [index, change] of changes.entries()) {
|
|
121
124
|
if (change.kind === 'move')
|
|
122
125
|
moves.set(change.path, change.to);
|
|
123
|
-
if (change.kind === 'remove' ||
|
|
126
|
+
if (change.kind === 'remove' ||
|
|
127
|
+
change.kind === 'replace' ||
|
|
128
|
+
change.kind === 'create' ||
|
|
129
|
+
change.kind === 'binary') {
|
|
124
130
|
settled.add(change.path);
|
|
125
131
|
}
|
|
126
132
|
if (change.kind === 'field')
|
|
127
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
|
+
}
|
|
128
141
|
}
|
|
129
142
|
if (moves.size === 0)
|
|
130
143
|
return { kind: 'changes', changes };
|
|
131
144
|
const merged = [...changes];
|
|
132
145
|
const added = [];
|
|
133
|
-
for (const [path, document] of
|
|
146
|
+
for (const [path, document] of documentsOf(visible.collection)) {
|
|
134
147
|
if (settled.has(path))
|
|
135
148
|
continue;
|
|
136
149
|
const moved = moves.get(path) ?? path;
|
|
137
150
|
const definition = definitionOf(governing(visible.collection.composition, path), document.type);
|
|
138
151
|
added.push(...repointed(document, definition, path, moved, moves, settled));
|
|
139
|
-
const
|
|
152
|
+
const own = parts.get(path) ?? [];
|
|
153
|
+
const relinked = relinks(path, moved, edited(document, own.map((index) => merged[index])), moves);
|
|
140
154
|
if (relinked.kind === 'refused')
|
|
141
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
|
+
}
|
|
142
162
|
for (const change of relinked.changes) {
|
|
143
163
|
if (change.kind === 'replace') {
|
|
144
|
-
|
|
164
|
+
for (const index of own) {
|
|
165
|
+
const kind = merged[index]?.kind;
|
|
166
|
+
if (kind === 'section' || kind === 'preamble')
|
|
167
|
+
merged[index] = null;
|
|
168
|
+
}
|
|
145
169
|
added.push(change);
|
|
146
170
|
continue;
|
|
147
171
|
}
|
|
148
|
-
const index =
|
|
149
|
-
|
|
150
|
-
(change.kind !== 'section' || (item.kind === 'section' && item.name === change.name)));
|
|
151
|
-
if (index === -1)
|
|
172
|
+
const index = keyed.get(partKey(change));
|
|
173
|
+
if (index === undefined)
|
|
152
174
|
added.push(change);
|
|
153
175
|
else
|
|
154
176
|
merged[index] = change;
|
|
155
177
|
}
|
|
178
|
+
yield ROW_WEIGHT + visible.collection.text[path].length;
|
|
156
179
|
}
|
|
157
|
-
return {
|
|
180
|
+
return {
|
|
181
|
+
kind: 'changes',
|
|
182
|
+
changes: [...added, ...merged.filter((change) => change !== null)],
|
|
183
|
+
};
|
|
158
184
|
}
|
|
159
|
-
function anticipated(changes, preconditions, rewritten, visible) {
|
|
160
|
-
const
|
|
185
|
+
function* anticipated(changes, preconditions, rewritten, visible) {
|
|
186
|
+
const given = new Set(changes);
|
|
187
|
+
const derived = rewritten.filter((change) => !given.has(change));
|
|
161
188
|
return {
|
|
162
189
|
changes: rewritten,
|
|
163
|
-
preconditions: [...preconditionsOf(derived, visible, []), ...preconditions],
|
|
190
|
+
preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...preconditions],
|
|
164
191
|
};
|
|
165
192
|
}
|
|
166
|
-
export function providerRewritten(batch, visible) {
|
|
167
|
-
const rewritten = rewrites(batch.changes, visible);
|
|
193
|
+
export function* providerRewritten(batch, visible) {
|
|
194
|
+
const rewritten = yield* rewrites(batch.changes, visible);
|
|
168
195
|
if (rewritten.kind === 'refused')
|
|
169
196
|
return batch;
|
|
170
197
|
return {
|
|
171
198
|
...batch,
|
|
172
|
-
...anticipated(batch.changes, batch.preconditions, rewritten.changes, visible),
|
|
199
|
+
...(yield* anticipated(batch.changes, batch.preconditions, rewritten.changes, visible)),
|
|
173
200
|
};
|
|
174
201
|
}
|
|
175
|
-
function incoming(collection, removed) {
|
|
202
|
+
function* incoming(collection, removed, candidates) {
|
|
176
203
|
const found = new Map();
|
|
177
204
|
const note = (target, file) => {
|
|
178
205
|
if (!removed.has(target))
|
|
@@ -181,7 +208,14 @@ function incoming(collection, removed) {
|
|
|
181
208
|
referrers.add(file);
|
|
182
209
|
found.set(target, referrers);
|
|
183
210
|
};
|
|
184
|
-
|
|
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)) {
|
|
185
219
|
const definition = definitionOf(governing(collection.composition, path), document.type);
|
|
186
220
|
for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
|
|
187
221
|
if (to.includes(ATTACHMENT))
|
|
@@ -190,6 +224,7 @@ function incoming(collection, removed) {
|
|
|
190
224
|
if (named !== null)
|
|
191
225
|
note(named, path);
|
|
192
226
|
}
|
|
227
|
+
yield ROW_WEIGHT + collection.text[path].length;
|
|
193
228
|
}
|
|
194
229
|
const [first] = [...found.keys()].sort();
|
|
195
230
|
if (first === undefined)
|
|
@@ -200,18 +235,26 @@ function incoming(collection, removed) {
|
|
|
200
235
|
every.add(file);
|
|
201
236
|
return {
|
|
202
237
|
kind: 'refused',
|
|
203
|
-
refusal: {
|
|
238
|
+
refusal: {
|
|
239
|
+
code: 'referenced-missing-file',
|
|
240
|
+
retryable: false,
|
|
241
|
+
file: first,
|
|
242
|
+
referrers: [...every].sort(),
|
|
243
|
+
},
|
|
204
244
|
};
|
|
205
245
|
}
|
|
206
246
|
function affected(change) {
|
|
207
247
|
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
208
248
|
}
|
|
209
249
|
function guard(changes, input) {
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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()) {
|
|
215
258
|
for (const path of affected(change)) {
|
|
216
259
|
const normalized = normalize(path);
|
|
217
260
|
if (normalized === null || normalized === '')
|
|
@@ -220,7 +263,7 @@ function guard(changes, input) {
|
|
|
220
263
|
return refuse('provider-owned-path', normalized);
|
|
221
264
|
}
|
|
222
265
|
const operation = change.kind === 'remove' ? 'delete' : 'write';
|
|
223
|
-
if (!covered(input.operations[operation], normalized))
|
|
266
|
+
if (!input.local && !covered(input.operations[operation], normalized))
|
|
224
267
|
return refuse(`${operation}-grant`, normalized);
|
|
225
268
|
}
|
|
226
269
|
if (change.kind === 'create') {
|
|
@@ -229,12 +272,26 @@ function guard(changes, input) {
|
|
|
229
272
|
existing.add(change.path);
|
|
230
273
|
continue;
|
|
231
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
|
+
}
|
|
232
289
|
if (change.kind === 'move') {
|
|
233
290
|
if (!existing.has(change.path))
|
|
234
291
|
return refuse('path-absent', change.path);
|
|
235
292
|
if (existing.has(change.to))
|
|
236
293
|
return refuse('path-collision', change.to);
|
|
237
|
-
if (
|
|
294
|
+
if (unrevised(input, change.path)) {
|
|
238
295
|
return refuse('unknown-revision', change.path);
|
|
239
296
|
}
|
|
240
297
|
existing.delete(change.path);
|
|
@@ -244,30 +301,53 @@ function guard(changes, input) {
|
|
|
244
301
|
if (!existing.has(change.path))
|
|
245
302
|
return refuse('path-absent', change.path);
|
|
246
303
|
if (change.kind === 'remove') {
|
|
247
|
-
if (
|
|
304
|
+
if (unrevised(input, change.path)) {
|
|
248
305
|
return refuse('unknown-revision', change.path);
|
|
249
306
|
}
|
|
250
307
|
existing.delete(change.path);
|
|
251
308
|
continue;
|
|
252
309
|
}
|
|
253
310
|
if (change.kind === 'replace') {
|
|
254
|
-
if (input
|
|
311
|
+
if (unrevised(input, change.path)) {
|
|
255
312
|
return refuse('unknown-revision', change.path);
|
|
256
313
|
}
|
|
257
314
|
continue;
|
|
258
315
|
}
|
|
259
|
-
if (input.visible.collection.
|
|
316
|
+
if (input.visible.collection.document(change.path) === undefined) {
|
|
260
317
|
return refuse('path-absent', change.path);
|
|
261
318
|
}
|
|
262
319
|
}
|
|
263
320
|
return null;
|
|
264
321
|
}
|
|
265
|
-
function
|
|
266
|
-
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}`;
|
|
267
344
|
}
|
|
268
345
|
function existence(visible, path) {
|
|
269
346
|
const revision = visible.revisions[path];
|
|
270
|
-
|
|
347
|
+
if (revision !== undefined)
|
|
348
|
+
return { kind: 'revision', path, revision };
|
|
349
|
+
const content = visible.documents[path];
|
|
350
|
+
return content === undefined ? { kind: 'present', path } : { kind: 'content', path, content };
|
|
271
351
|
}
|
|
272
352
|
function sameTarget(precondition, change) {
|
|
273
353
|
if (precondition.kind !== change.kind || precondition.path !== change.path)
|
|
@@ -275,25 +355,25 @@ function sameTarget(precondition, change) {
|
|
|
275
355
|
return ((precondition.kind !== 'field' && precondition.kind !== 'section') ||
|
|
276
356
|
((change.kind === 'field' || change.kind === 'section') && precondition.name === change.name));
|
|
277
357
|
}
|
|
278
|
-
function preconditionsOf(changes, visible, bases) {
|
|
358
|
+
function* preconditionsOf(changes, visible, bases) {
|
|
279
359
|
const preconditions = [];
|
|
280
360
|
const seen = new Set();
|
|
281
361
|
const add = (precondition) => {
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
if (seen.has(identity))
|
|
362
|
+
const held = identity(precondition);
|
|
363
|
+
if (seen.has(held))
|
|
285
364
|
return;
|
|
286
|
-
seen.add(
|
|
365
|
+
seen.add(held);
|
|
287
366
|
preconditions.push(precondition);
|
|
288
367
|
};
|
|
289
368
|
for (const change of changes) {
|
|
369
|
+
yield ROW_WEIGHT;
|
|
290
370
|
if (change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble') {
|
|
291
371
|
const based = bases.find((base) => sameTarget(base, change));
|
|
292
372
|
if (based !== undefined) {
|
|
293
373
|
add(based);
|
|
294
374
|
continue;
|
|
295
375
|
}
|
|
296
|
-
const document = visible.collection.
|
|
376
|
+
const document = visible.collection.document(change.path);
|
|
297
377
|
if (change.kind === 'field') {
|
|
298
378
|
add({
|
|
299
379
|
kind: 'field',
|
|
@@ -318,6 +398,13 @@ function preconditionsOf(changes, visible, bases) {
|
|
|
318
398
|
add({ kind: 'absent', path: change.path });
|
|
319
399
|
continue;
|
|
320
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
|
+
}
|
|
321
408
|
add(existence(visible, change.path));
|
|
322
409
|
if (change.kind === 'move')
|
|
323
410
|
add({ kind: 'absent', path: change.to });
|
|
@@ -327,10 +414,10 @@ function preconditionsOf(changes, visible, bases) {
|
|
|
327
414
|
function depth(path) {
|
|
328
415
|
return path.split('/').length;
|
|
329
416
|
}
|
|
330
|
-
function
|
|
331
|
-
const written =
|
|
417
|
+
function* exceeding(limits, changes, effective, resulting) {
|
|
418
|
+
const written = effective.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
332
419
|
const created = changes
|
|
333
|
-
.filter((change) => change.kind === 'create' || change.kind === 'move')
|
|
420
|
+
.filter((change) => change.kind === 'create' || change.kind === 'move' || change.kind === 'binary')
|
|
334
421
|
.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
335
422
|
for (const limit of limits) {
|
|
336
423
|
const value = limit.value;
|
|
@@ -344,9 +431,13 @@ function exceeded(limits, changes, resulting) {
|
|
|
344
431
|
return refuse('provider-limit', undefined, limit.name);
|
|
345
432
|
}
|
|
346
433
|
if (limit.name === 'document-size') {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
+
}
|
|
350
441
|
}
|
|
351
442
|
if (limit.name === 'path-length') {
|
|
352
443
|
const long = created.find((path) => path.length > value);
|
|
@@ -376,7 +467,7 @@ function relativeField(path, name, value, handle, definition) {
|
|
|
376
467
|
!opened.ok ||
|
|
377
468
|
!JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
|
|
378
469
|
return value;
|
|
379
|
-
const held = apply(opened.document, { kind: 'field', name, value, prior: undefined });
|
|
470
|
+
const held = apply(opened.document, [{ kind: 'field', name, value, prior: undefined }]);
|
|
380
471
|
if (held.outcome !== 'applied')
|
|
381
472
|
return value;
|
|
382
473
|
const result = relativize(held.document, path, handle, definition);
|
|
@@ -389,7 +480,7 @@ function relativeChanges(input) {
|
|
|
389
480
|
return input.changes.map((change) => {
|
|
390
481
|
const definition = typed(change.path);
|
|
391
482
|
if (change.kind === 'field') {
|
|
392
|
-
const type = visible.collection.
|
|
483
|
+
const type = visible.collection.document(change.path)?.type;
|
|
393
484
|
const value = relativeField(change.path, change.name, change.value, handle, definition(type));
|
|
394
485
|
return value === change.value ? change : { ...change, value };
|
|
395
486
|
}
|
|
@@ -412,30 +503,338 @@ function relativeChanges(input) {
|
|
|
412
503
|
return { ...change, fields, sections };
|
|
413
504
|
});
|
|
414
505
|
}
|
|
415
|
-
|
|
506
|
+
function* unmodeledType(collection, composition) {
|
|
507
|
+
if (available(composition))
|
|
508
|
+
return false;
|
|
509
|
+
for (const [path, document] of documentsOf(collection)) {
|
|
510
|
+
if (document.type !== undefined &&
|
|
511
|
+
document.type !== 'any' &&
|
|
512
|
+
governing(composition, path).status === 'unavailable')
|
|
513
|
+
return true;
|
|
514
|
+
yield ROW_WEIGHT + collection.text[path].length;
|
|
515
|
+
}
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
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) {
|
|
416
813
|
const changes = relativeChanges(input);
|
|
417
814
|
const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
|
|
418
815
|
if (structural !== undefined &&
|
|
419
|
-
|
|
420
|
-
document.type !== 'any' &&
|
|
421
|
-
governing(input.composition, path).status === 'unavailable'))
|
|
816
|
+
(yield* unmodeledType(input.visible.collection, input.composition)))
|
|
422
817
|
return refuse('model-unavailable', structural.path);
|
|
423
818
|
if (changes.length === 0)
|
|
424
819
|
return refuse('no-change');
|
|
425
|
-
const rewritten = rewrites(changes, input.visible);
|
|
820
|
+
const rewritten = yield* rewrites(changes, input.visible);
|
|
426
821
|
if (rewritten.kind === 'refused')
|
|
427
822
|
return rewritten;
|
|
428
823
|
const refusal = guard(changes, input);
|
|
429
824
|
if (refusal !== null)
|
|
430
825
|
return refusal;
|
|
431
|
-
const preconditions = preconditionsOf(changes, input.visible, input.bases);
|
|
432
|
-
const predicted = anticipated(changes, preconditions, rewritten.changes, input.visible);
|
|
433
|
-
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, [
|
|
434
829
|
{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted },
|
|
435
830
|
]);
|
|
831
|
+
const clean = input.clean !== undefined && available(input.composition) ? input.clean : null;
|
|
436
832
|
const removed = new Set(changes.filter((change) => change.kind === 'remove').map((change) => change.path));
|
|
437
833
|
if (removed.size > 0) {
|
|
438
|
-
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))]);
|
|
439
838
|
if (referenced !== null)
|
|
440
839
|
return referenced;
|
|
441
840
|
}
|
|
@@ -445,27 +844,40 @@ export function buildBatch(input) {
|
|
|
445
844
|
const parsed = parse(content);
|
|
446
845
|
return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
|
|
447
846
|
};
|
|
448
|
-
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
|
+
}
|
|
449
856
|
const needsModel = typedPaths.length > 0;
|
|
450
857
|
const withoutModel = typedPaths.find((path) => governing(input.composition, path).status === 'unavailable');
|
|
451
858
|
if (withoutModel !== undefined)
|
|
452
859
|
return refuse('model-unavailable', withoutModel);
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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);
|
|
457
868
|
if (verdict.verdict === 'refuse') {
|
|
458
869
|
return {
|
|
459
870
|
kind: 'refused',
|
|
460
871
|
refusal: {
|
|
461
|
-
|
|
872
|
+
code: verdict.rule,
|
|
873
|
+
retryable: false,
|
|
462
874
|
file: verdict.file,
|
|
463
875
|
...(verdict.location === undefined ? {} : { location: verdict.location }),
|
|
464
876
|
...(verdict.referrers === undefined ? {} : { referrers: verdict.referrers }),
|
|
465
877
|
},
|
|
466
878
|
};
|
|
467
879
|
}
|
|
468
|
-
const limited =
|
|
880
|
+
const limited = yield* exceeding(input.providerLimits, changes, predicted.changes, resulting);
|
|
469
881
|
if (limited !== null)
|
|
470
882
|
return limited;
|
|
471
883
|
const validated = typedPaths
|
|
@@ -491,3 +903,6 @@ export function buildBatch(input) {
|
|
|
491
903
|
}),
|
|
492
904
|
};
|
|
493
905
|
}
|
|
906
|
+
export function buildBatch(input) {
|
|
907
|
+
return stepped(batching(input));
|
|
908
|
+
}
|