@agentshouse/mdruntime 0.1.0-alpha.8 → 0.1.0-c3903a68dcbbf
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 +210 -0
- package/README.md +44 -0
- package/dist/adapter.d.ts +223 -25
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +2 -3
- package/dist/attachment.d.ts +3 -0
- package/dist/attachment.d.ts.map +1 -0
- package/dist/attachment.js +8 -0
- package/dist/bootstrap.d.ts +6 -7
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +32 -28
- package/dist/change-set.d.ts +33 -0
- package/dist/change-set.d.ts.map +1 -0
- package/dist/change-set.js +87 -0
- package/dist/change.d.ts +9 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +6 -5
- package/dist/client.d.ts +75 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +171 -0
- package/dist/collection.d.ts +24 -15
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +69 -77
- package/dist/connection.d.ts +79 -36
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1699 -953
- package/dist/conversation.d.ts +79 -0
- package/dist/conversation.d.ts.map +1 -0
- package/dist/conversation.js +557 -0
- package/dist/durability.d.ts +8 -3
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +7 -61
- package/dist/file.d.ts +20 -0
- package/dist/file.d.ts.map +1 -0
- package/dist/file.js +126 -0
- package/dist/host.d.ts +14 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +198 -0
- package/dist/identity.d.ts +1 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +10 -9
- package/dist/index.d.ts +14 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/log.js +5 -6
- package/dist/memory.d.ts +17 -2
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +269 -113
- package/dist/model.d.ts +10 -25
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +18 -21
- package/dist/observation.d.ts +25 -0
- package/dist/observation.d.ts.map +1 -0
- package/dist/observation.js +85 -0
- package/dist/outcome.d.ts +22 -18
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +1 -13
- package/dist/overlay.d.ts +9 -2
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +187 -43
- package/dist/paths.d.ts +0 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +12 -19
- package/dist/query.d.ts +210 -9
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +420 -15
- package/dist/read-side.d.ts +14 -0
- package/dist/read-side.d.ts.map +1 -0
- package/dist/read-side.js +371 -0
- package/dist/refresh.d.ts +4 -5
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +45 -30
- package/dist/replica.d.ts +27 -6
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +237 -30
- package/dist/room-draft.d.ts +124 -0
- package/dist/room-draft.d.ts.map +1 -0
- package/dist/room-draft.js +327 -0
- package/dist/rows.d.ts +41 -0
- package/dist/rows.d.ts.map +1 -0
- package/dist/rows.js +100 -0
- package/dist/save.d.ts +10 -6
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +267 -333
- package/dist/search.d.ts +61 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +125 -0
- package/dist/status.d.ts +9 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +11 -14
- package/dist/store.d.ts +85 -18
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +380 -97
- package/dist/transfer.d.ts +5 -0
- package/dist/transfer.d.ts.map +1 -1
- package/dist/transfer.js +1 -5
- package/dist/wire.d.ts +64 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +124 -0
- package/package.json +15 -13
- package/dist/adapter.js.map +0 -1
- package/dist/bootstrap.js.map +0 -1
- package/dist/change.js.map +0 -1
- package/dist/collection.js.map +0 -1
- package/dist/connection.js.map +0 -1
- package/dist/durability.js.map +0 -1
- package/dist/identity.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/log.js.map +0 -1
- package/dist/memory.js.map +0 -1
- package/dist/model.js.map +0 -1
- package/dist/outcome.js.map +0 -1
- package/dist/overlay.js.map +0 -1
- package/dist/paths.js.map +0 -1
- package/dist/query.js.map +0 -1
- package/dist/refresh.js.map +0 -1
- package/dist/replica.js.map +0 -1
- package/dist/save.js.map +0 -1
- package/dist/status.js.map +0 -1
- package/dist/store.js.map +0 -1
- package/dist/transfer.js.map +0 -1
package/dist/save.js
CHANGED
|
@@ -1,336 +1,259 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { definitionOf } from './model.js';
|
|
2
|
+
import { apply, links, parse, retarget, serialize, target, } from '@agentshouse/mdmodel';
|
|
3
|
+
import { immutable, } from './change.js';
|
|
4
|
+
import { governing, untyped, parseCollection, validateCollection, } from './collection.js';
|
|
4
5
|
import { changeBatchIdentity } from './identity.js';
|
|
5
6
|
import { overlay } from './overlay.js';
|
|
6
|
-
import {
|
|
7
|
+
import { covered, normalize, relative } from './paths.js';
|
|
7
8
|
function refuse(rule, file, location) {
|
|
8
9
|
return {
|
|
9
10
|
kind: 'refused',
|
|
10
11
|
refusal: {
|
|
11
12
|
rule,
|
|
12
|
-
...file === undefined ? {} : {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
...location === undefined ? {} : {
|
|
16
|
-
location
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
function members(visible, prefix) {
|
|
22
|
-
return [
|
|
23
|
-
...Object.keys(visible.documents),
|
|
24
|
-
...visible.binaryTargets
|
|
25
|
-
].filter((path)=>beneath(prefix, path)).sort();
|
|
26
|
-
}
|
|
27
|
-
function spread(change, path, member) {
|
|
28
|
-
if (change.kind === 'remove') return {
|
|
29
|
-
kind: 'remove',
|
|
30
|
-
path: member
|
|
31
|
-
};
|
|
32
|
-
return {
|
|
33
|
-
kind: 'move',
|
|
34
|
-
path: member,
|
|
35
|
-
to: `${change.to}${member.slice(path.length)}`
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
function expand(changes, visible) {
|
|
39
|
-
const expanded = [];
|
|
40
|
-
for (const change of changes){
|
|
41
|
-
if (change.kind !== 'move' && change.kind !== 'remove') {
|
|
42
|
-
expanded.push(change);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
const path = normalize(change.path);
|
|
46
|
-
const to = change.kind === 'move' ? normalize(change.to) : '';
|
|
47
|
-
if (path === null || path === '' || to === null || change.kind === 'move' && to === '') {
|
|
48
|
-
return {
|
|
49
|
-
kind: 'refused',
|
|
50
|
-
refusal: {
|
|
51
|
-
rule: 'room-root',
|
|
52
|
-
file: change.path
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
const structural = change.kind === 'move' ? {
|
|
57
|
-
...change,
|
|
58
|
-
path,
|
|
59
|
-
to
|
|
60
|
-
} : {
|
|
61
|
-
...change,
|
|
62
|
-
path
|
|
63
|
-
};
|
|
64
|
-
const present = visible.documents[path] !== undefined || visible.binaryTargets.includes(path);
|
|
65
|
-
if (present) {
|
|
66
|
-
expanded.push(structural);
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
const beneathIt = members(visible, path);
|
|
70
|
-
if (beneathIt.length === 0) {
|
|
71
|
-
return {
|
|
72
|
-
kind: 'refused',
|
|
73
|
-
refusal: {
|
|
74
|
-
rule: 'path-absent',
|
|
75
|
-
file: change.path
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
for (const member of beneathIt)expanded.push(spread(structural, path, member));
|
|
80
|
-
}
|
|
81
|
-
return {
|
|
82
|
-
kind: 'changes',
|
|
83
|
-
changes: expanded
|
|
13
|
+
...(file === undefined ? {} : { file }),
|
|
14
|
+
...(location === undefined ? {} : { location }),
|
|
15
|
+
},
|
|
84
16
|
};
|
|
85
17
|
}
|
|
86
18
|
function referenceField(spec) {
|
|
87
|
-
|
|
88
|
-
const declared = spec;
|
|
89
|
-
return declared.kind === 'reference' || declared.kind === 'list' && declared.of === 'reference';
|
|
90
|
-
}
|
|
91
|
-
function definitionOf(types, type) {
|
|
92
|
-
return types.find((declared)=>declared.name === type);
|
|
19
|
+
return spec.kind === 'reference' || (spec.kind === 'list' && spec.of.kind === 'reference');
|
|
93
20
|
}
|
|
94
|
-
function
|
|
21
|
+
function repointed(value, from, to, moves) {
|
|
95
22
|
if (Array.isArray(value)) {
|
|
96
|
-
const next = value.map((item)=>
|
|
97
|
-
return next.some((item, index)=>item !== value[index]) ? next : undefined;
|
|
23
|
+
const next = value.map((item) => repointed(item, from, to, moves) ?? item);
|
|
24
|
+
return next.some((item, index) => item !== value[index]) ? next : undefined;
|
|
98
25
|
}
|
|
99
|
-
if (typeof value !== 'string')
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
26
|
+
if (typeof value !== 'string')
|
|
27
|
+
return undefined;
|
|
28
|
+
const named = target(from, value);
|
|
29
|
+
if (named === null)
|
|
30
|
+
return undefined;
|
|
31
|
+
const destination = moves.get(named) ?? named;
|
|
32
|
+
return target(to, value) === destination ? undefined : retarget(value, relative(to, destination));
|
|
104
33
|
}
|
|
105
34
|
function edited(document, path, changes) {
|
|
106
35
|
let next = document;
|
|
107
|
-
for (const change of changes){
|
|
108
|
-
if (change.path !== path)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
prior: next.preamble
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (applied.outcome === 'applied')
|
|
36
|
+
for (const change of changes) {
|
|
37
|
+
if (change.path !== path)
|
|
38
|
+
continue;
|
|
39
|
+
if (change.kind !== 'section' && change.kind !== 'preamble')
|
|
40
|
+
continue;
|
|
41
|
+
const applied = apply(next, change.kind === 'preamble'
|
|
42
|
+
? { kind: 'preamble', value: change.value, prior: next.preamble }
|
|
43
|
+
: {
|
|
44
|
+
kind: 'section',
|
|
45
|
+
name: change.name,
|
|
46
|
+
value: change.value,
|
|
47
|
+
prior: next.sections[change.name],
|
|
48
|
+
});
|
|
49
|
+
if (applied.outcome === 'applied')
|
|
50
|
+
next = applied.document;
|
|
121
51
|
}
|
|
122
52
|
return next;
|
|
123
53
|
}
|
|
124
54
|
function bodies(path, before, after) {
|
|
125
55
|
const changes = [];
|
|
126
56
|
if (after.preamble !== before.preamble) {
|
|
127
|
-
changes.push({
|
|
128
|
-
kind: 'preamble',
|
|
129
|
-
path,
|
|
130
|
-
value: after.preamble
|
|
131
|
-
});
|
|
57
|
+
changes.push({ kind: 'preamble', path, value: after.preamble });
|
|
132
58
|
}
|
|
133
|
-
for (const [name, value] of Object.entries(after.sections)){
|
|
134
|
-
if (before.sections[name] === value)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
path,
|
|
138
|
-
name,
|
|
139
|
-
value
|
|
140
|
-
});
|
|
59
|
+
for (const [name, value] of Object.entries(after.sections)) {
|
|
60
|
+
if (before.sections[name] === value)
|
|
61
|
+
continue;
|
|
62
|
+
changes.push({ kind: 'section', path, name, value });
|
|
141
63
|
}
|
|
142
64
|
return changes;
|
|
143
65
|
}
|
|
144
66
|
function relinks(path, moved, before, moves) {
|
|
145
67
|
let after = before;
|
|
146
|
-
for (const link of links(before, path)){
|
|
68
|
+
for (const link of links(before, path)) {
|
|
147
69
|
const destination = moves.get(link.target) ?? link.target;
|
|
148
|
-
if (destination === link.target && moved === path)
|
|
70
|
+
if (destination === link.target && moved === path)
|
|
71
|
+
continue;
|
|
149
72
|
const to = relative(moved, destination);
|
|
150
|
-
if (to === relative(path, link.target))
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
href: link.href,
|
|
154
|
-
to
|
|
155
|
-
});
|
|
73
|
+
if (to === relative(path, link.target))
|
|
74
|
+
continue;
|
|
75
|
+
const applied = apply(after, { kind: 'link', href: link.href, to });
|
|
156
76
|
if (applied.outcome === 'refuse') {
|
|
157
77
|
return {
|
|
158
78
|
kind: 'refused',
|
|
159
79
|
refusal: {
|
|
160
80
|
rule: applied.refusal.rule,
|
|
161
81
|
file: path,
|
|
162
|
-
...applied.refusal.location === undefined ? {} : {
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
}
|
|
82
|
+
...(applied.refusal.location === undefined ? {} : { location: applied.refusal.location }),
|
|
83
|
+
},
|
|
166
84
|
};
|
|
167
85
|
}
|
|
168
86
|
after = applied.document;
|
|
169
87
|
}
|
|
170
|
-
if (after === before)
|
|
171
|
-
kind: 'changes',
|
|
172
|
-
changes: []
|
|
173
|
-
};
|
|
88
|
+
if (after === before)
|
|
89
|
+
return { kind: 'changes', changes: [] };
|
|
174
90
|
if (before.type === undefined) {
|
|
175
|
-
return {
|
|
176
|
-
kind: 'changes',
|
|
177
|
-
changes: [
|
|
178
|
-
{
|
|
179
|
-
kind: 'replace',
|
|
180
|
-
path,
|
|
181
|
-
content: serialize(after)
|
|
182
|
-
}
|
|
183
|
-
]
|
|
184
|
-
};
|
|
91
|
+
return { kind: 'changes', changes: [{ kind: 'replace', path, content: serialize(after) }] };
|
|
185
92
|
}
|
|
186
|
-
return {
|
|
187
|
-
kind: 'changes',
|
|
188
|
-
changes: bodies(path, before, after)
|
|
189
|
-
};
|
|
93
|
+
return { kind: 'changes', changes: bodies(path, before, after) };
|
|
190
94
|
}
|
|
191
95
|
function subsumed(merged, path) {
|
|
192
|
-
for(let index = merged.length - 1; index >= 0; index -= 1){
|
|
96
|
+
for (let index = merged.length - 1; index >= 0; index -= 1) {
|
|
193
97
|
const item = merged[index];
|
|
194
|
-
if (item.path !== path)
|
|
195
|
-
|
|
98
|
+
if (item.path !== path)
|
|
99
|
+
continue;
|
|
100
|
+
if (item.kind === 'section' || item.kind === 'preamble')
|
|
101
|
+
merged.splice(index, 1);
|
|
196
102
|
}
|
|
197
103
|
}
|
|
198
104
|
function rewrites(changes, visible) {
|
|
199
105
|
const moves = new Map();
|
|
200
106
|
const settled = new Set();
|
|
201
|
-
for (const change of changes){
|
|
202
|
-
if (change.kind === 'move')
|
|
107
|
+
for (const change of changes) {
|
|
108
|
+
if (change.kind === 'move')
|
|
109
|
+
moves.set(change.path, change.to);
|
|
203
110
|
if (change.kind === 'remove' || change.kind === 'replace' || change.kind === 'create') {
|
|
204
111
|
settled.add(change.path);
|
|
205
112
|
}
|
|
206
|
-
if (change.kind === 'field')
|
|
113
|
+
if (change.kind === 'field')
|
|
114
|
+
settled.add(`${change.path} ${change.name}`);
|
|
207
115
|
}
|
|
208
|
-
if (moves.size === 0)
|
|
209
|
-
kind: 'changes',
|
|
210
|
-
|
|
211
|
-
};
|
|
212
|
-
const merged = [
|
|
213
|
-
...changes
|
|
214
|
-
];
|
|
116
|
+
if (moves.size === 0)
|
|
117
|
+
return { kind: 'changes', changes };
|
|
118
|
+
const merged = [...changes];
|
|
215
119
|
const added = [];
|
|
216
|
-
for (const [path, document] of Object.entries(visible.collection.documents)){
|
|
217
|
-
if (settled.has(path))
|
|
120
|
+
for (const [path, document] of Object.entries(visible.collection.documents)) {
|
|
121
|
+
if (settled.has(path))
|
|
122
|
+
continue;
|
|
218
123
|
const moved = moves.get(path) ?? path;
|
|
219
|
-
const definition = definitionOf(visible.collection.
|
|
220
|
-
for (const [name, spec] of Object.entries(definition?.fields ?? {})){
|
|
221
|
-
if (!referenceField(spec) || settled.has(`${path} ${name}`))
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
name,
|
|
228
|
-
value
|
|
229
|
-
});
|
|
124
|
+
const definition = definitionOf(governing(visible.collection.composition, path), document.type);
|
|
125
|
+
for (const [name, spec] of Object.entries(definition?.fields ?? {})) {
|
|
126
|
+
if (!referenceField(spec) || settled.has(`${path} ${name}`))
|
|
127
|
+
continue;
|
|
128
|
+
const value = repointed(document.fields[name], path, moved, moves);
|
|
129
|
+
if (value === undefined)
|
|
130
|
+
continue;
|
|
131
|
+
added.push({ kind: 'field', path, name, value });
|
|
230
132
|
}
|
|
231
133
|
const relinked = relinks(path, moved, edited(document, path, merged), moves);
|
|
232
|
-
if (relinked.kind === 'refused')
|
|
233
|
-
|
|
134
|
+
if (relinked.kind === 'refused')
|
|
135
|
+
return relinked;
|
|
136
|
+
for (const change of relinked.changes) {
|
|
234
137
|
if (change.kind === 'replace') {
|
|
235
138
|
subsumed(merged, path);
|
|
236
139
|
added.push(change);
|
|
237
140
|
continue;
|
|
238
141
|
}
|
|
239
|
-
const index = merged.findIndex((item)=>item.kind === change.kind &&
|
|
240
|
-
|
|
241
|
-
|
|
142
|
+
const index = merged.findIndex((item) => item.kind === change.kind &&
|
|
143
|
+
item.path === path &&
|
|
144
|
+
(change.kind !== 'section' || (item.kind === 'section' && item.name === change.name)));
|
|
145
|
+
if (index === -1)
|
|
146
|
+
added.push(change);
|
|
147
|
+
else
|
|
148
|
+
merged[index] = change;
|
|
242
149
|
}
|
|
243
150
|
}
|
|
151
|
+
return { kind: 'changes', changes: [...added, ...merged] };
|
|
152
|
+
}
|
|
153
|
+
function anticipated(changes, preconditions, rewritten, visible) {
|
|
154
|
+
const derived = rewritten.filter((change) => !changes.includes(change));
|
|
155
|
+
return {
|
|
156
|
+
changes: rewritten,
|
|
157
|
+
preconditions: [...preconditionsOf(derived, visible, []), ...preconditions],
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
export function providerRewritten(batch, visible) {
|
|
161
|
+
const rewritten = rewrites(batch.changes, visible);
|
|
162
|
+
if (rewritten.kind === 'refused')
|
|
163
|
+
return batch;
|
|
244
164
|
return {
|
|
245
|
-
|
|
246
|
-
changes
|
|
247
|
-
...added,
|
|
248
|
-
...merged
|
|
249
|
-
]
|
|
165
|
+
...batch,
|
|
166
|
+
...anticipated(batch.changes, batch.preconditions, rewritten.changes, visible),
|
|
250
167
|
};
|
|
251
168
|
}
|
|
252
169
|
function targets(value, from) {
|
|
253
|
-
const values = Array.isArray(value) ? value : [
|
|
254
|
-
value
|
|
255
|
-
];
|
|
170
|
+
const values = Array.isArray(value) ? value : [value];
|
|
256
171
|
const resolved = [];
|
|
257
|
-
for (const item of values){
|
|
258
|
-
if (typeof item !== 'string')
|
|
259
|
-
|
|
260
|
-
|
|
172
|
+
for (const item of values) {
|
|
173
|
+
if (typeof item !== 'string')
|
|
174
|
+
continue;
|
|
175
|
+
const named = target(from, item);
|
|
176
|
+
if (named !== null)
|
|
177
|
+
resolved.push(named);
|
|
261
178
|
}
|
|
262
179
|
return resolved;
|
|
263
180
|
}
|
|
264
181
|
function incoming(collection, removed) {
|
|
265
182
|
const found = new Map();
|
|
266
|
-
const note = (target, file)=>{
|
|
267
|
-
if (!removed.has(target))
|
|
183
|
+
const note = (target, file) => {
|
|
184
|
+
if (!removed.has(target))
|
|
185
|
+
return;
|
|
268
186
|
const referrers = found.get(target) ?? new Set();
|
|
269
187
|
referrers.add(file);
|
|
270
188
|
found.set(target, referrers);
|
|
271
189
|
};
|
|
272
|
-
for (const [path, document] of Object.entries(collection.documents)){
|
|
273
|
-
const definition = definitionOf(collection.
|
|
274
|
-
for (const [name, spec] of Object.entries(definition?.fields ?? {})){
|
|
275
|
-
if (!referenceField(spec))
|
|
276
|
-
|
|
190
|
+
for (const [path, document] of Object.entries(collection.documents)) {
|
|
191
|
+
const definition = definitionOf(governing(collection.composition, path), document.type);
|
|
192
|
+
for (const [name, spec] of Object.entries(definition?.fields ?? {})) {
|
|
193
|
+
if (!referenceField(spec))
|
|
194
|
+
continue;
|
|
195
|
+
for (const named of targets(document.fields[name], path))
|
|
196
|
+
note(named, path);
|
|
277
197
|
}
|
|
278
|
-
for (const link of links(document, path))
|
|
198
|
+
for (const link of links(document, path))
|
|
199
|
+
note(link.target, path);
|
|
279
200
|
}
|
|
280
|
-
const [first] = [
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (first === undefined) return null;
|
|
201
|
+
const [first] = [...found.keys()].sort();
|
|
202
|
+
if (first === undefined)
|
|
203
|
+
return null;
|
|
284
204
|
const every = new Set();
|
|
285
|
-
for (const referrers of found.values())
|
|
205
|
+
for (const referrers of found.values())
|
|
206
|
+
for (const file of referrers)
|
|
207
|
+
every.add(file);
|
|
286
208
|
return {
|
|
287
209
|
kind: 'refused',
|
|
288
|
-
refusal: {
|
|
289
|
-
rule: 'referenced-missing-file',
|
|
290
|
-
file: first,
|
|
291
|
-
referrers: [
|
|
292
|
-
...every
|
|
293
|
-
].sort()
|
|
294
|
-
}
|
|
210
|
+
refusal: { rule: 'referenced-missing-file', file: first, referrers: [...every].sort() },
|
|
295
211
|
};
|
|
296
212
|
}
|
|
297
213
|
function affected(change) {
|
|
298
|
-
return change.kind === 'move' ? [
|
|
299
|
-
change.path,
|
|
300
|
-
change.to
|
|
301
|
-
] : [
|
|
302
|
-
change.path
|
|
303
|
-
];
|
|
214
|
+
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
304
215
|
}
|
|
305
216
|
function guard(changes, input) {
|
|
306
217
|
const existing = new Set([
|
|
307
218
|
...Object.keys(input.visible.documents),
|
|
308
|
-
...input.visible.binaryTargets
|
|
219
|
+
...input.visible.binaryTargets,
|
|
309
220
|
]);
|
|
310
|
-
for (const change of changes){
|
|
311
|
-
for (const path of affected(change)){
|
|
221
|
+
for (const change of changes) {
|
|
222
|
+
for (const path of affected(change)) {
|
|
312
223
|
const normalized = normalize(path);
|
|
313
|
-
if (normalized === null || normalized === '')
|
|
224
|
+
if (normalized === null || normalized === '')
|
|
225
|
+
return refuse('room-root', path);
|
|
314
226
|
if (covered(input.providerOwnedPaths, normalized)) {
|
|
315
227
|
return refuse('provider-owned-path', normalized);
|
|
316
228
|
}
|
|
317
229
|
const operation = change.kind === 'remove' ? 'delete' : 'write';
|
|
318
|
-
if (!covered(input.operations[operation], normalized))
|
|
230
|
+
if (!covered(input.operations[operation], normalized))
|
|
231
|
+
return refuse(`${operation}-grant`, normalized);
|
|
319
232
|
}
|
|
320
233
|
if (change.kind === 'create') {
|
|
321
|
-
if (existing.has(change.path))
|
|
234
|
+
if (existing.has(change.path))
|
|
235
|
+
return refuse('path-collision', change.path);
|
|
322
236
|
existing.add(change.path);
|
|
323
237
|
continue;
|
|
324
238
|
}
|
|
325
239
|
if (change.kind === 'move') {
|
|
326
|
-
if (!existing.has(change.path))
|
|
327
|
-
|
|
240
|
+
if (!existing.has(change.path))
|
|
241
|
+
return refuse('path-absent', change.path);
|
|
242
|
+
if (existing.has(change.to))
|
|
243
|
+
return refuse('path-collision', change.to);
|
|
244
|
+
if (unrevisionedDocument(input.visible, change.path)) {
|
|
245
|
+
return refuse('unknown-revision', change.path);
|
|
246
|
+
}
|
|
328
247
|
existing.delete(change.path);
|
|
329
248
|
existing.add(change.to);
|
|
330
249
|
continue;
|
|
331
250
|
}
|
|
332
|
-
if (!existing.has(change.path))
|
|
251
|
+
if (!existing.has(change.path))
|
|
252
|
+
return refuse('path-absent', change.path);
|
|
333
253
|
if (change.kind === 'remove') {
|
|
254
|
+
if (unrevisionedDocument(input.visible, change.path)) {
|
|
255
|
+
return refuse('unknown-revision', change.path);
|
|
256
|
+
}
|
|
334
257
|
existing.delete(change.path);
|
|
335
258
|
continue;
|
|
336
259
|
}
|
|
@@ -340,77 +263,71 @@ function guard(changes, input) {
|
|
|
340
263
|
}
|
|
341
264
|
continue;
|
|
342
265
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (change.kind === 'section' && document.sections[change.name] === undefined) {
|
|
346
|
-
return refuse('required-section', change.path, change.name);
|
|
266
|
+
if (input.visible.collection.documents[change.path] === undefined) {
|
|
267
|
+
return refuse('path-absent', change.path);
|
|
347
268
|
}
|
|
348
269
|
}
|
|
349
270
|
return null;
|
|
350
271
|
}
|
|
272
|
+
function unrevisionedDocument(visible, path) {
|
|
273
|
+
return visible.revisions[path] === undefined && !visible.binaryTargets.includes(path);
|
|
274
|
+
}
|
|
351
275
|
function existence(visible, path) {
|
|
352
276
|
const revision = visible.revisions[path];
|
|
353
|
-
return revision === undefined ? {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
};
|
|
277
|
+
return revision === undefined ? { kind: 'present', path } : { kind: 'revision', path, revision };
|
|
278
|
+
}
|
|
279
|
+
function sameTarget(precondition, change) {
|
|
280
|
+
if (precondition.kind !== change.kind || precondition.path !== change.path)
|
|
281
|
+
return false;
|
|
282
|
+
return ((precondition.kind !== 'field' && precondition.kind !== 'section') ||
|
|
283
|
+
((change.kind === 'field' || change.kind === 'section') && precondition.name === change.name));
|
|
361
284
|
}
|
|
362
|
-
function preconditionsOf(changes, visible) {
|
|
285
|
+
function preconditionsOf(changes, visible, bases) {
|
|
363
286
|
const preconditions = [];
|
|
364
287
|
const seen = new Set();
|
|
365
|
-
const add = (precondition)=>{
|
|
288
|
+
const add = (precondition) => {
|
|
366
289
|
const part = precondition.kind === 'field' || precondition.kind === 'section' ? precondition.name : '';
|
|
367
290
|
const identity = `${precondition.kind} ${precondition.path} ${part}`;
|
|
368
|
-
if (seen.has(identity))
|
|
291
|
+
if (seen.has(identity))
|
|
292
|
+
return;
|
|
369
293
|
seen.add(identity);
|
|
370
294
|
preconditions.push(precondition);
|
|
371
295
|
};
|
|
372
|
-
for (const change of changes){
|
|
373
|
-
if (change.kind === 'field') {
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
value: document.fields[change.name]
|
|
380
|
-
});
|
|
381
|
-
continue;
|
|
382
|
-
}
|
|
383
|
-
if (change.kind === 'section') {
|
|
384
|
-
const document = visible.collection.documents[change.path];
|
|
385
|
-
add({
|
|
386
|
-
kind: 'section',
|
|
387
|
-
path: change.path,
|
|
388
|
-
name: change.name,
|
|
389
|
-
value: document.sections[change.name]
|
|
390
|
-
});
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
if (change.kind === 'preamble') {
|
|
296
|
+
for (const change of changes) {
|
|
297
|
+
if (change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble') {
|
|
298
|
+
const based = bases.find((base) => sameTarget(base, change));
|
|
299
|
+
if (based !== undefined) {
|
|
300
|
+
add(based);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
394
303
|
const document = visible.collection.documents[change.path];
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
304
|
+
if (change.kind === 'field') {
|
|
305
|
+
add({
|
|
306
|
+
kind: 'field',
|
|
307
|
+
path: change.path,
|
|
308
|
+
name: change.name,
|
|
309
|
+
value: document.fields[change.name],
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
else if (change.kind === 'section') {
|
|
313
|
+
add({
|
|
314
|
+
kind: 'section',
|
|
315
|
+
path: change.path,
|
|
316
|
+
name: change.name,
|
|
317
|
+
value: document.sections[change.name],
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
else
|
|
321
|
+
add({ kind: 'preamble', path: change.path, value: document.preamble });
|
|
400
322
|
continue;
|
|
401
323
|
}
|
|
402
324
|
if (change.kind === 'create') {
|
|
403
|
-
add({
|
|
404
|
-
kind: 'absent',
|
|
405
|
-
path: change.path
|
|
406
|
-
});
|
|
325
|
+
add({ kind: 'absent', path: change.path });
|
|
407
326
|
continue;
|
|
408
327
|
}
|
|
409
328
|
add(existence(visible, change.path));
|
|
410
|
-
if (change.kind === 'move')
|
|
411
|
-
kind: 'absent',
|
|
412
|
-
path: change.to
|
|
413
|
-
});
|
|
329
|
+
if (change.kind === 'move')
|
|
330
|
+
add({ kind: 'absent', path: change.to });
|
|
414
331
|
}
|
|
415
332
|
return preconditions;
|
|
416
333
|
}
|
|
@@ -418,85 +335,101 @@ function depth(path) {
|
|
|
418
335
|
return path.split('/').length;
|
|
419
336
|
}
|
|
420
337
|
function exceeded(limits, changes, resulting) {
|
|
421
|
-
const written = changes.map((change)=>change.kind === 'move' ? change.to : change.path);
|
|
422
|
-
const created = changes
|
|
423
|
-
|
|
338
|
+
const written = changes.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
339
|
+
const created = changes
|
|
340
|
+
.filter((change) => change.kind === 'create' || change.kind === 'move')
|
|
341
|
+
.map((change) => (change.kind === 'move' ? change.to : change.path));
|
|
342
|
+
for (const limit of limits) {
|
|
424
343
|
const value = limit.value;
|
|
425
|
-
if (typeof value !== 'number')
|
|
344
|
+
if (typeof value !== 'number')
|
|
345
|
+
continue;
|
|
426
346
|
if (limit.name === 'change-batch-operations' && changes.length > value) {
|
|
427
347
|
return refuse('provider-limit', undefined, limit.name);
|
|
428
348
|
}
|
|
349
|
+
if (limit.name === 'change-documents' &&
|
|
350
|
+
new Set(changes.map((change) => change.path)).size > value) {
|
|
351
|
+
return refuse('provider-limit', undefined, limit.name);
|
|
352
|
+
}
|
|
429
353
|
if (limit.name === 'document-size') {
|
|
430
|
-
const large = written.find((path)=>(resulting.documents[path] ?? '').length > value);
|
|
431
|
-
if (large !== undefined)
|
|
354
|
+
const large = written.find((path) => (resulting.documents[path] ?? '').length > value);
|
|
355
|
+
if (large !== undefined)
|
|
356
|
+
return refuse('provider-limit', large, limit.name);
|
|
432
357
|
}
|
|
433
358
|
if (limit.name === 'path-length') {
|
|
434
|
-
const long = created.find((path)=>path.length > value);
|
|
435
|
-
if (long !== undefined)
|
|
359
|
+
const long = created.find((path) => path.length > value);
|
|
360
|
+
if (long !== undefined)
|
|
361
|
+
return refuse('provider-limit', long, limit.name);
|
|
436
362
|
}
|
|
437
363
|
if (limit.name === 'path-depth') {
|
|
438
|
-
const deep = created.find((path)=>depth(path) > value);
|
|
439
|
-
if (deep !== undefined)
|
|
364
|
+
const deep = created.find((path) => depth(path) > value);
|
|
365
|
+
if (deep !== undefined)
|
|
366
|
+
return refuse('provider-limit', deep, limit.name);
|
|
440
367
|
}
|
|
441
368
|
}
|
|
442
369
|
return null;
|
|
443
370
|
}
|
|
444
371
|
export function buildBatch(input) {
|
|
445
|
-
const structural = input.changes.find((change)=>change.kind === 'move' || change.kind === 'remove');
|
|
446
|
-
if (structural !== undefined &&
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
372
|
+
const structural = input.changes.find((change) => change.kind === 'move' || change.kind === 'remove');
|
|
373
|
+
if (structural !== undefined &&
|
|
374
|
+
Object.entries(input.visible.collection.documents).some(([path, document]) => document.type !== undefined &&
|
|
375
|
+
document.type !== 'any' &&
|
|
376
|
+
governing(input.composition, path).status === 'unavailable'))
|
|
377
|
+
return refuse('model-unavailable', structural.path);
|
|
378
|
+
if (input.changes.length === 0)
|
|
379
|
+
return refuse('no-change');
|
|
380
|
+
const rewritten = rewrites(input.changes, input.visible);
|
|
381
|
+
if (rewritten.kind === 'refused')
|
|
382
|
+
return rewritten;
|
|
383
|
+
const changes = input.rewriting === 'provider' ? input.changes : rewritten.changes;
|
|
453
384
|
const refusal = guard(changes, input);
|
|
454
|
-
if (refusal !== null)
|
|
455
|
-
|
|
385
|
+
if (refusal !== null)
|
|
386
|
+
return refusal;
|
|
387
|
+
const preconditions = preconditionsOf(changes, input.visible, input.bases);
|
|
388
|
+
const predicted = input.rewriting === 'provider'
|
|
389
|
+
? anticipated(changes, preconditions, rewritten.changes, input.visible)
|
|
390
|
+
: { changes, preconditions };
|
|
456
391
|
const resulting = overlay(input.visible, [
|
|
457
|
-
{
|
|
458
|
-
id: '',
|
|
459
|
-
baseRoomVersion: input.baseRoomVersion,
|
|
460
|
-
changes,
|
|
461
|
-
preconditions
|
|
462
|
-
}
|
|
392
|
+
{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted },
|
|
463
393
|
]);
|
|
464
|
-
const removed = new Set(changes.filter((change)=>change.kind === 'remove').map((change)=>change.path));
|
|
394
|
+
const removed = new Set(changes.filter((change) => change.kind === 'remove').map((change) => change.path));
|
|
465
395
|
if (removed.size > 0) {
|
|
466
|
-
const referenced = incoming(parseCollection(resulting.documents, input.
|
|
467
|
-
if (referenced !== null)
|
|
396
|
+
const referenced = incoming(parseCollection(resulting.documents, input.composition), removed);
|
|
397
|
+
if (referenced !== null)
|
|
398
|
+
return referenced;
|
|
468
399
|
}
|
|
469
|
-
const typed = (content)=>{
|
|
470
|
-
if (content === undefined)
|
|
400
|
+
const typed = (content) => {
|
|
401
|
+
if (content === undefined)
|
|
402
|
+
return false;
|
|
471
403
|
const parsed = parse(content);
|
|
472
404
|
return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
|
|
473
405
|
};
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
406
|
+
const typedPaths = [...new Set(predicted.changes.flatMap(affected))].filter((path) => !untyped(input.composition.untypedFolders, path) &&
|
|
407
|
+
(typed(input.visible.documents[path]) || typed(resulting.documents[path])));
|
|
408
|
+
const needsModel = typedPaths.length > 0;
|
|
409
|
+
const withoutModel = typedPaths.find((path) => governing(input.composition, path).status === 'unavailable');
|
|
410
|
+
if (withoutModel !== undefined)
|
|
411
|
+
return refuse('model-unavailable', withoutModel);
|
|
412
|
+
const validationText = needsModel
|
|
413
|
+
? resulting.documents
|
|
414
|
+
: Object.fromEntries(Object.entries(resulting.documents).filter(([path]) => predicted.changes.some((change) => affected(change).includes(path))));
|
|
415
|
+
const verdict = validateCollection(validationText, resulting.binaryTargets, input.composition);
|
|
480
416
|
if (verdict.verdict === 'refuse') {
|
|
481
417
|
return {
|
|
482
418
|
kind: 'refused',
|
|
483
419
|
refusal: {
|
|
484
|
-
...verdict.source === 'model' ? {
|
|
485
|
-
source: 'model'
|
|
486
|
-
} : {},
|
|
487
420
|
rule: verdict.rule,
|
|
488
421
|
file: verdict.file,
|
|
489
|
-
...verdict.location === undefined ? {} : {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
...verdict.referrers === undefined ? {} : {
|
|
493
|
-
referrers: verdict.referrers
|
|
494
|
-
}
|
|
495
|
-
}
|
|
422
|
+
...(verdict.location === undefined ? {} : { location: verdict.location }),
|
|
423
|
+
...(verdict.referrers === undefined ? {} : { referrers: verdict.referrers }),
|
|
424
|
+
},
|
|
496
425
|
};
|
|
497
426
|
}
|
|
498
427
|
const limited = exceeded(input.providerLimits, changes, resulting);
|
|
499
|
-
if (limited !== null)
|
|
428
|
+
if (limited !== null)
|
|
429
|
+
return limited;
|
|
430
|
+
const validated = typedPaths
|
|
431
|
+
.map((path) => governing(input.composition, path))
|
|
432
|
+
.find((model) => model.status !== 'unavailable');
|
|
500
433
|
return {
|
|
501
434
|
kind: 'batch',
|
|
502
435
|
batch: immutable({
|
|
@@ -504,15 +437,16 @@ export function buildBatch(input) {
|
|
|
504
437
|
baseRoomVersion: input.baseRoomVersion,
|
|
505
438
|
changes,
|
|
506
439
|
preconditions,
|
|
507
|
-
...
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
440
|
+
...(input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy }),
|
|
441
|
+
...(validated === undefined
|
|
442
|
+
? {}
|
|
443
|
+
: {
|
|
444
|
+
validation: {
|
|
445
|
+
identity: validated.snapshot.identity,
|
|
446
|
+
version: validated.snapshot.version,
|
|
447
|
+
source: validated.status,
|
|
448
|
+
},
|
|
449
|
+
}),
|
|
450
|
+
}),
|
|
515
451
|
};
|
|
516
452
|
}
|
|
517
|
-
|
|
518
|
-
//# sourceMappingURL=save.js.map
|