@agentshouse/mdruntime 0.1.0-alpha.9 → 0.1.0-c30ed2b8e04d5
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 +217 -0
- package/README.md +43 -0
- package/dist/adapter.d.ts +184 -34
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +1 -10
- package/dist/attached-model.d.ts +11 -0
- package/dist/attached-model.d.ts.map +1 -0
- package/dist/attached-model.js +34 -0
- 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 +7 -9
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +29 -28
- package/dist/change-set.d.ts +33 -0
- package/dist/change-set.d.ts.map +1 -0
- package/dist/change-set.js +83 -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 +38 -32
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +114 -183
- package/dist/collection.d.ts +13 -16
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +45 -88
- package/dist/connection.d.ts +47 -44
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +1542 -1158
- package/dist/conversation.d.ts +44 -22
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +540 -328
- package/dist/durability.js +6 -6
- package/dist/file.d.ts +2 -2
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +48 -71
- package/dist/host.d.ts +12 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +97 -119
- 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 +12 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/log.js +5 -6
- package/dist/memory.d.ts +15 -7
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +255 -220
- package/dist/model.d.ts +10 -29
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +18 -21
- package/dist/observation.d.ts +27 -0
- package/dist/observation.d.ts.map +1 -0
- package/dist/observation.js +95 -0
- package/dist/outcome.d.ts +10 -29
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +1 -23
- 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 +215 -10
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +421 -15
- package/dist/reach.d.ts +40 -0
- package/dist/reach.d.ts.map +1 -0
- package/dist/reach.js +277 -0
- package/dist/read-side.d.ts +18 -0
- package/dist/read-side.d.ts.map +1 -0
- package/dist/read-side.js +377 -0
- package/dist/refresh.d.ts +5 -5
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +49 -30
- package/dist/replica.d.ts +25 -5
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +227 -38
- package/dist/room-draft.d.ts +127 -0
- package/dist/room-draft.d.ts.map +1 -0
- package/dist/room-draft.js +329 -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 +6 -4
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +262 -350
- 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 +4 -6
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +9 -13
- package/dist/store.d.ts +59 -32
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +338 -134
- 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 +51 -16
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +77 -108
- package/package.json +6 -12
- package/dist/adapter.js.map +0 -1
- package/dist/bootstrap.js.map +0 -1
- package/dist/change.js.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/collection.js.map +0 -1
- package/dist/connection.js.map +0 -1
- package/dist/conversation.js.map +0 -1
- package/dist/durability.js.map +0 -1
- package/dist/file.js.map +0 -1
- package/dist/host.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/wire.js.map +0 -1
package/dist/save.js
CHANGED
|
@@ -1,350 +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, 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';
|
|
19
|
+
return spec.kind === 'reference' || (spec.kind === 'list' && spec.of.kind === 'reference');
|
|
90
20
|
}
|
|
91
|
-
function
|
|
92
|
-
return types.find((declared)=>declared.name === type);
|
|
93
|
-
}
|
|
94
|
-
function retarget(value, from, to, moves) {
|
|
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(
|
|
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(
|
|
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
|
-
];
|
|
304
|
-
}
|
|
305
|
-
function definitionAddresses(composition) {
|
|
306
|
-
const addresses = new Set();
|
|
307
|
-
if (composition.model.status !== 'unavailable') {
|
|
308
|
-
for (const path of Object.keys(composition.model.snapshot.definitions))addresses.add(path);
|
|
309
|
-
}
|
|
310
|
-
if (composition.selection !== null && composition.selection.model.status !== 'unavailable') {
|
|
311
|
-
for (const path of Object.keys(composition.selection.model.snapshot.definitions)){
|
|
312
|
-
addresses.add(path);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
return addresses;
|
|
214
|
+
return change.kind === 'move' ? [change.path, change.to] : [change.path];
|
|
316
215
|
}
|
|
317
216
|
function guard(changes, input) {
|
|
318
217
|
const existing = new Set([
|
|
319
218
|
...Object.keys(input.visible.documents),
|
|
320
|
-
...input.visible.binaryTargets
|
|
219
|
+
...input.visible.binaryTargets,
|
|
321
220
|
]);
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
for (const path of affected(change)){
|
|
221
|
+
for (const change of changes) {
|
|
222
|
+
for (const path of affected(change)) {
|
|
325
223
|
const normalized = normalize(path);
|
|
326
|
-
if (normalized === null || normalized === '')
|
|
327
|
-
|
|
224
|
+
if (normalized === null || normalized === '')
|
|
225
|
+
return refuse('room-root', path);
|
|
328
226
|
if (covered(input.providerOwnedPaths, normalized)) {
|
|
329
227
|
return refuse('provider-owned-path', normalized);
|
|
330
228
|
}
|
|
331
229
|
const operation = change.kind === 'remove' ? 'delete' : 'write';
|
|
332
|
-
if (!covered(input.operations[operation], normalized))
|
|
230
|
+
if (!covered(input.operations[operation], normalized))
|
|
231
|
+
return refuse(`${operation}-grant`, normalized);
|
|
333
232
|
}
|
|
334
233
|
if (change.kind === 'create') {
|
|
335
|
-
if (existing.has(change.path))
|
|
234
|
+
if (existing.has(change.path))
|
|
235
|
+
return refuse('path-collision', change.path);
|
|
336
236
|
existing.add(change.path);
|
|
337
237
|
continue;
|
|
338
238
|
}
|
|
339
239
|
if (change.kind === 'move') {
|
|
340
|
-
if (!existing.has(change.path))
|
|
341
|
-
|
|
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
|
+
}
|
|
342
247
|
existing.delete(change.path);
|
|
343
248
|
existing.add(change.to);
|
|
344
249
|
continue;
|
|
345
250
|
}
|
|
346
|
-
if (!existing.has(change.path))
|
|
251
|
+
if (!existing.has(change.path))
|
|
252
|
+
return refuse('path-absent', change.path);
|
|
347
253
|
if (change.kind === 'remove') {
|
|
254
|
+
if (unrevisionedDocument(input.visible, change.path)) {
|
|
255
|
+
return refuse('unknown-revision', change.path);
|
|
256
|
+
}
|
|
348
257
|
existing.delete(change.path);
|
|
349
258
|
continue;
|
|
350
259
|
}
|
|
@@ -354,77 +263,71 @@ function guard(changes, input) {
|
|
|
354
263
|
}
|
|
355
264
|
continue;
|
|
356
265
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
if (change.kind === 'section' && document.sections[change.name] === undefined) {
|
|
360
|
-
return refuse('required-section', change.path, change.name);
|
|
266
|
+
if (input.visible.collection.documents[change.path] === undefined) {
|
|
267
|
+
return refuse('path-absent', change.path);
|
|
361
268
|
}
|
|
362
269
|
}
|
|
363
270
|
return null;
|
|
364
271
|
}
|
|
272
|
+
function unrevisionedDocument(visible, path) {
|
|
273
|
+
return visible.revisions[path] === undefined && !visible.binaryTargets.includes(path);
|
|
274
|
+
}
|
|
365
275
|
function existence(visible, path) {
|
|
366
276
|
const revision = visible.revisions[path];
|
|
367
|
-
return revision === undefined ? {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
};
|
|
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));
|
|
375
284
|
}
|
|
376
|
-
function preconditionsOf(changes, visible) {
|
|
285
|
+
function preconditionsOf(changes, visible, bases) {
|
|
377
286
|
const preconditions = [];
|
|
378
287
|
const seen = new Set();
|
|
379
|
-
const add = (precondition)=>{
|
|
288
|
+
const add = (precondition) => {
|
|
380
289
|
const part = precondition.kind === 'field' || precondition.kind === 'section' ? precondition.name : '';
|
|
381
290
|
const identity = `${precondition.kind} ${precondition.path} ${part}`;
|
|
382
|
-
if (seen.has(identity))
|
|
291
|
+
if (seen.has(identity))
|
|
292
|
+
return;
|
|
383
293
|
seen.add(identity);
|
|
384
294
|
preconditions.push(precondition);
|
|
385
295
|
};
|
|
386
|
-
for (const change of changes){
|
|
387
|
-
if (change.kind === 'field') {
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
value: document.fields[change.name]
|
|
394
|
-
});
|
|
395
|
-
continue;
|
|
396
|
-
}
|
|
397
|
-
if (change.kind === 'section') {
|
|
398
|
-
const document = visible.collection.documents[change.path];
|
|
399
|
-
add({
|
|
400
|
-
kind: 'section',
|
|
401
|
-
path: change.path,
|
|
402
|
-
name: change.name,
|
|
403
|
-
value: document.sections[change.name]
|
|
404
|
-
});
|
|
405
|
-
continue;
|
|
406
|
-
}
|
|
407
|
-
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
|
+
}
|
|
408
303
|
const document = visible.collection.documents[change.path];
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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 });
|
|
414
322
|
continue;
|
|
415
323
|
}
|
|
416
324
|
if (change.kind === 'create') {
|
|
417
|
-
add({
|
|
418
|
-
kind: 'absent',
|
|
419
|
-
path: change.path
|
|
420
|
-
});
|
|
325
|
+
add({ kind: 'absent', path: change.path });
|
|
421
326
|
continue;
|
|
422
327
|
}
|
|
423
328
|
add(existence(visible, change.path));
|
|
424
|
-
if (change.kind === 'move')
|
|
425
|
-
kind: 'absent',
|
|
426
|
-
path: change.to
|
|
427
|
-
});
|
|
329
|
+
if (change.kind === 'move')
|
|
330
|
+
add({ kind: 'absent', path: change.to });
|
|
428
331
|
}
|
|
429
332
|
return preconditions;
|
|
430
333
|
}
|
|
@@ -432,90 +335,98 @@ function depth(path) {
|
|
|
432
335
|
return path.split('/').length;
|
|
433
336
|
}
|
|
434
337
|
function exceeded(limits, changes, resulting) {
|
|
435
|
-
const written = changes.map((change)=>change.kind === 'move' ? change.to : change.path);
|
|
436
|
-
const created = changes
|
|
437
|
-
|
|
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) {
|
|
438
343
|
const value = limit.value;
|
|
439
|
-
if (typeof value !== 'number')
|
|
344
|
+
if (typeof value !== 'number')
|
|
345
|
+
continue;
|
|
440
346
|
if (limit.name === 'change-batch-operations' && changes.length > value) {
|
|
441
347
|
return refuse('provider-limit', undefined, limit.name);
|
|
442
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
|
+
}
|
|
443
353
|
if (limit.name === 'document-size') {
|
|
444
|
-
const large = written.find((path)=>(resulting.documents[path] ?? '').length > value);
|
|
445
|
-
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);
|
|
446
357
|
}
|
|
447
358
|
if (limit.name === 'path-length') {
|
|
448
|
-
const long = created.find((path)=>path.length > value);
|
|
449
|
-
if (long !== undefined)
|
|
359
|
+
const long = created.find((path) => path.length > value);
|
|
360
|
+
if (long !== undefined)
|
|
361
|
+
return refuse('provider-limit', long, limit.name);
|
|
450
362
|
}
|
|
451
363
|
if (limit.name === 'path-depth') {
|
|
452
|
-
const deep = created.find((path)=>depth(path) > value);
|
|
453
|
-
if (deep !== undefined)
|
|
364
|
+
const deep = created.find((path) => depth(path) > value);
|
|
365
|
+
if (deep !== undefined)
|
|
366
|
+
return refuse('provider-limit', deep, limit.name);
|
|
454
367
|
}
|
|
455
368
|
}
|
|
456
369
|
return null;
|
|
457
370
|
}
|
|
458
371
|
export function buildBatch(input) {
|
|
459
|
-
const structural = input.changes.find((change)=>change.kind === 'move' || change.kind === 'remove');
|
|
460
|
-
if (structural !== undefined &&
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
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.changes;
|
|
467
384
|
const refusal = guard(changes, input);
|
|
468
|
-
if (refusal !== null)
|
|
469
|
-
|
|
385
|
+
if (refusal !== null)
|
|
386
|
+
return refusal;
|
|
387
|
+
const preconditions = preconditionsOf(changes, input.visible, input.bases);
|
|
388
|
+
const predicted = anticipated(changes, preconditions, rewritten.changes, input.visible);
|
|
470
389
|
const resulting = overlay(input.visible, [
|
|
471
|
-
{
|
|
472
|
-
id: '',
|
|
473
|
-
baseRoomVersion: input.baseRoomVersion,
|
|
474
|
-
changes,
|
|
475
|
-
preconditions
|
|
476
|
-
}
|
|
390
|
+
{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted },
|
|
477
391
|
]);
|
|
478
|
-
const removed = new Set(changes.filter((change)=>change.kind === 'remove').map((change)=>change.path));
|
|
392
|
+
const removed = new Set(changes.filter((change) => change.kind === 'remove').map((change) => change.path));
|
|
479
393
|
if (removed.size > 0) {
|
|
480
394
|
const referenced = incoming(parseCollection(resulting.documents, input.composition), removed);
|
|
481
|
-
if (referenced !== null)
|
|
395
|
+
if (referenced !== null)
|
|
396
|
+
return referenced;
|
|
482
397
|
}
|
|
483
|
-
const typed = (content)=>{
|
|
484
|
-
if (content === undefined)
|
|
398
|
+
const typed = (content) => {
|
|
399
|
+
if (content === undefined)
|
|
400
|
+
return false;
|
|
485
401
|
const parsed = parse(content);
|
|
486
402
|
return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
|
|
487
403
|
};
|
|
488
|
-
const typedPaths = [
|
|
489
|
-
...new Set(changes.flatMap(affected))
|
|
490
|
-
].filter((path)=>typed(input.visible.documents[path]) || typed(resulting.documents[path]));
|
|
404
|
+
const typedPaths = [...new Set(predicted.changes.flatMap(affected))].filter((path) => typed(input.visible.documents[path]) || typed(resulting.documents[path]));
|
|
491
405
|
const needsModel = typedPaths.length > 0;
|
|
492
|
-
const withoutModel = typedPaths.find((path)=>governing(input.composition, path).status === 'unavailable');
|
|
493
|
-
if (
|
|
494
|
-
return refuse('model-unavailable', withoutModel
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
406
|
+
const withoutModel = typedPaths.find((path) => governing(input.composition, path).status === 'unavailable');
|
|
407
|
+
if (withoutModel !== undefined)
|
|
408
|
+
return refuse('model-unavailable', withoutModel);
|
|
409
|
+
const validationText = needsModel
|
|
410
|
+
? resulting.documents
|
|
411
|
+
: Object.fromEntries(Object.entries(resulting.documents).filter(([path]) => predicted.changes.some((change) => affected(change).includes(path))));
|
|
412
|
+
const verdict = validateCollection(validationText, resulting.binaryTargets, input.composition);
|
|
498
413
|
if (verdict.verdict === 'refuse') {
|
|
499
414
|
return {
|
|
500
415
|
kind: 'refused',
|
|
501
416
|
refusal: {
|
|
502
|
-
...verdict.source === 'model' ? {
|
|
503
|
-
source: 'model'
|
|
504
|
-
} : {},
|
|
505
417
|
rule: verdict.rule,
|
|
506
418
|
file: verdict.file,
|
|
507
|
-
...verdict.location === undefined ? {} : {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
...verdict.referrers === undefined ? {} : {
|
|
511
|
-
referrers: verdict.referrers
|
|
512
|
-
}
|
|
513
|
-
}
|
|
419
|
+
...(verdict.location === undefined ? {} : { location: verdict.location }),
|
|
420
|
+
...(verdict.referrers === undefined ? {} : { referrers: verdict.referrers }),
|
|
421
|
+
},
|
|
514
422
|
};
|
|
515
423
|
}
|
|
516
424
|
const limited = exceeded(input.providerLimits, changes, resulting);
|
|
517
|
-
if (limited !== null)
|
|
518
|
-
|
|
425
|
+
if (limited !== null)
|
|
426
|
+
return limited;
|
|
427
|
+
const validated = typedPaths
|
|
428
|
+
.map((path) => governing(input.composition, path))
|
|
429
|
+
.find((model) => model.status !== 'unavailable');
|
|
519
430
|
return {
|
|
520
431
|
kind: 'batch',
|
|
521
432
|
batch: immutable({
|
|
@@ -523,15 +434,16 @@ export function buildBatch(input) {
|
|
|
523
434
|
baseRoomVersion: input.baseRoomVersion,
|
|
524
435
|
changes,
|
|
525
436
|
preconditions,
|
|
526
|
-
...
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
437
|
+
...(input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy }),
|
|
438
|
+
...(validated === undefined
|
|
439
|
+
? {}
|
|
440
|
+
: {
|
|
441
|
+
validation: {
|
|
442
|
+
identity: validated.snapshot.identity,
|
|
443
|
+
version: validated.snapshot.version,
|
|
444
|
+
source: validated.status,
|
|
445
|
+
},
|
|
446
|
+
}),
|
|
447
|
+
}),
|
|
534
448
|
};
|
|
535
449
|
}
|
|
536
|
-
|
|
537
|
-
//# sourceMappingURL=save.js.map
|