@agentshouse/mdmodel 0.1.0-c2987f5d89720 → 0.1.0-c4fe1bc82bc55
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/API.md +112 -55
- package/dist/blocks.d.ts.map +1 -1
- package/dist/blocks.js +30 -16
- package/dist/condition.d.ts +18 -6
- package/dist/condition.d.ts.map +1 -1
- package/dist/condition.js +208 -111
- package/dist/definition.d.ts +9 -0
- package/dist/definition.d.ts.map +1 -1
- package/dist/definition.js +50 -1
- package/dist/document.d.ts +4 -8
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +32 -14
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +26 -30
- package/dist/yaml.d.ts +6 -1
- package/dist/yaml.d.ts.map +1 -1
- package/dist/yaml.js +33 -14
- package/fixtures/cases/absent-link-dangles/context.json +1 -0
- package/fixtures/cases/absent-link-dangles/expected.json +1 -0
- package/fixtures/cases/absent-link-dangles/library/item.md +8 -0
- package/fixtures/cases/absent-link-dangles/schema.yaml +6 -0
- package/fixtures/cases/absolute-link/context.json +1 -0
- package/fixtures/cases/{missing-binary-image → absolute-link}/expected.json +1 -1
- package/fixtures/cases/absolute-link/library/item.md +8 -0
- package/fixtures/cases/absolute-link/schema.yaml +6 -0
- package/fixtures/cases/binary-image-dangles/expected.json +1 -0
- package/fixtures/cases/binary-link-dangles/expected.json +1 -0
- package/fixtures/cases/quoted-link-definition/library/notes/draft.md +1 -1
- package/fixtures/cases/shortcut-reference-link/library/item.md +1 -1
- package/fixtures/cases/truncated-link-definition/library/notes/draft.md +1 -1
- package/fixtures/cases/unresolved-link/library/item.md +1 -1
- package/package.json +1 -1
- package/fixtures/cases/missing-binary-link/expected.json +0 -5
- /package/fixtures/cases/{missing-binary-image → binary-image-dangles}/context.json +0 -0
- /package/fixtures/cases/{missing-binary-image → binary-image-dangles}/library/notes/draft.md +0 -0
- /package/fixtures/cases/{missing-binary-image → binary-image-dangles}/schema.yaml +0 -0
- /package/fixtures/cases/{missing-binary-link → binary-link-dangles}/context.json +0 -0
- /package/fixtures/cases/{missing-binary-link → binary-link-dangles}/library/notes/draft.md +0 -0
- /package/fixtures/cases/{missing-binary-link → binary-link-dangles}/schema.yaml +0 -0
package/dist/condition.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { ARRAY_INDEX, escapePointer, fieldFacts, itemFacts, memberFacts, pointerSegments, } from './definition.js';
|
|
1
2
|
import { target } from './paths.js';
|
|
2
|
-
import { isRecord } from './yaml.js';
|
|
3
|
+
import { isRecord, plain } from './yaml.js';
|
|
3
4
|
export const WRITE_DOORS = ['browser', 'agent', 'client'];
|
|
5
|
+
const PROSE = { kind: 'prose' };
|
|
6
|
+
const NULL = { kind: 'null' };
|
|
7
|
+
const UNREADABLE = { kind: 'unreadable' };
|
|
8
|
+
const UNREAD = { present: undefined, values: [undefined], items: [undefined] };
|
|
9
|
+
const NO_ITEM = { present: false, values: [], items: [] };
|
|
4
10
|
const DATE = /^\d{4}-\d{2}-\d{2}$/u;
|
|
5
11
|
const DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(?:[Zz]|([+-])(\d{2}):(\d{2}))$/u;
|
|
6
|
-
const ARRAY_INDEX = /^(?:0|[1-9][0-9]*)$/u;
|
|
7
12
|
export function factKind(facts) {
|
|
8
13
|
if (facts.reference !== undefined)
|
|
9
14
|
return 'reference';
|
|
@@ -21,9 +26,6 @@ export function factKind(facts) {
|
|
|
21
26
|
return 'time';
|
|
22
27
|
return 'string';
|
|
23
28
|
}
|
|
24
|
-
function referenced(model, facts) {
|
|
25
|
-
return (facts.reference ?? []).some((to) => Object.hasOwn(model.types, to) && model.types[to].memberDocument);
|
|
26
|
-
}
|
|
27
29
|
function fits(kind, facts, value) {
|
|
28
30
|
if (!scalar(value))
|
|
29
31
|
return false;
|
|
@@ -41,81 +43,156 @@ function fits(kind, facts, value) {
|
|
|
41
43
|
return instant(value) !== undefined;
|
|
42
44
|
return true;
|
|
43
45
|
}
|
|
44
|
-
function
|
|
45
|
-
if (
|
|
46
|
-
return
|
|
46
|
+
function factOf(value, declared, place) {
|
|
47
|
+
if (value === null)
|
|
48
|
+
return NULL;
|
|
49
|
+
if (Array.isArray(value)) {
|
|
50
|
+
if (declared?.type !== undefined && declared.type !== 'array')
|
|
51
|
+
return UNREADABLE;
|
|
52
|
+
return {
|
|
53
|
+
kind: 'list',
|
|
54
|
+
items: value.map((item, index) => factOf(item, declared === undefined ? undefined : itemFacts(declared, index), place)),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (plain(value)) {
|
|
58
|
+
if (declared?.type !== undefined && declared.type !== 'object')
|
|
59
|
+
return UNREADABLE;
|
|
60
|
+
const fields = Object.create(null);
|
|
61
|
+
for (const [key, held] of Object.entries(value))
|
|
62
|
+
fields[key] = factOf(held, declared === undefined ? undefined : memberFacts(declared, key), place);
|
|
63
|
+
return { kind: 'object', fields };
|
|
64
|
+
}
|
|
65
|
+
if (declared?.type === 'array' || declared?.type === 'object')
|
|
66
|
+
return UNREADABLE;
|
|
67
|
+
const kind = declared === undefined || declared.prose ? null : factKind(declared);
|
|
68
|
+
if (kind === null)
|
|
69
|
+
return PROSE;
|
|
70
|
+
if (!fits(kind, declared, value))
|
|
71
|
+
return UNREADABLE;
|
|
47
72
|
if (kind !== 'reference')
|
|
48
|
-
return { value };
|
|
49
|
-
const reached = target(path, value);
|
|
50
|
-
|
|
73
|
+
return { kind, value };
|
|
74
|
+
const reached = target(place.path, value);
|
|
75
|
+
const resolved = reached === null ? undefined : place.resolve(reached);
|
|
76
|
+
if (resolved === undefined)
|
|
77
|
+
return UNREADABLE;
|
|
78
|
+
return resolved.member === undefined
|
|
79
|
+
? { kind, value: resolved.id }
|
|
80
|
+
: { kind, value: resolved.id, member: resolved.member };
|
|
51
81
|
}
|
|
52
82
|
export function facts(document, at, model, resolve) {
|
|
53
83
|
const type = document.type ?? null;
|
|
54
84
|
const definition = type !== null && Object.hasOwn(model.types, type) ? model.types[type] : null;
|
|
55
|
-
const fields =
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (value === undefined || value === null)
|
|
62
|
-
continue;
|
|
63
|
-
const list = declared.type === 'array';
|
|
64
|
-
const facts = list ? declared.items : declared;
|
|
65
|
-
const kind = facts === undefined || facts.prose ? null : factKind(facts);
|
|
66
|
-
if (kind === null) {
|
|
67
|
-
prose.push(`/fields/${name}`);
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
const items = list ? value : [value];
|
|
71
|
-
const held = Array.isArray(items)
|
|
72
|
-
? items.map((item) => fitted(kind, facts, item, at.path, resolve))
|
|
73
|
-
: [undefined];
|
|
74
|
-
if (held.some((item) => item === undefined)) {
|
|
75
|
-
unreadable.push(`/fields/${name}`);
|
|
76
|
-
if (referenced(model, facts))
|
|
77
|
-
unreadable.push(`/members/${name}`);
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
const values = held.map((item) => ('id' in item ? item.id : item.value));
|
|
81
|
-
fields[name] = { kind, value: list ? values : values[0] };
|
|
82
|
-
if (!referenced(model, facts))
|
|
83
|
-
continue;
|
|
84
|
-
const bound = held.flatMap((item) => 'member' in item && item.member !== undefined ? [item.member] : []);
|
|
85
|
-
if (list)
|
|
86
|
-
members[name] = bound;
|
|
87
|
-
else if (bound[0] !== undefined)
|
|
88
|
-
members[name] = bound[0];
|
|
85
|
+
const fields = Object.create(null);
|
|
86
|
+
const place = { path: at.path, resolve };
|
|
87
|
+
for (const [name, value] of Object.entries(document.fields)) {
|
|
88
|
+
const declared = definition === null ? undefined : fieldFacts(definition, name);
|
|
89
|
+
if (declared !== undefined)
|
|
90
|
+
fields[name] = factOf(value, declared, place);
|
|
89
91
|
}
|
|
90
92
|
return {
|
|
91
93
|
id: at.id,
|
|
92
94
|
path: `/${at.path}`,
|
|
93
95
|
type,
|
|
94
96
|
fields,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
sections: Object.keys(document.sections).sort(),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function rootOf(facts, prefix) {
|
|
101
|
+
return { pointer: prefix, fact: { kind: 'object', fields: facts.fields } };
|
|
102
|
+
}
|
|
103
|
+
export function prosePaths(facts) {
|
|
104
|
+
const paths = facts.sections.map((heading) => `/sections/${heading}`);
|
|
105
|
+
const visit = ({ pointer, fact }) => {
|
|
106
|
+
if (fact.kind === 'prose')
|
|
107
|
+
paths.push(pointer);
|
|
108
|
+
if (fact.kind === 'list')
|
|
109
|
+
fact.items.forEach((item, index) => visit({ pointer: `${pointer}/${index}`, fact: item }));
|
|
110
|
+
if (fact.kind === 'object')
|
|
111
|
+
for (const [key, held] of Object.entries(fact.fields))
|
|
112
|
+
visit({ pointer: `${pointer}/${escapePointer(key)}`, fact: held });
|
|
113
|
+
};
|
|
114
|
+
visit(rootOf(facts, '/fields'));
|
|
115
|
+
return paths.sort();
|
|
116
|
+
}
|
|
117
|
+
function walked(start, pointer) {
|
|
118
|
+
let reached = [start];
|
|
119
|
+
let several = false;
|
|
120
|
+
let missing = false;
|
|
121
|
+
const each = (from, next) => from.flatMap((one) => {
|
|
122
|
+
const found = next(one);
|
|
123
|
+
if (found === null)
|
|
124
|
+
missing = true;
|
|
125
|
+
return found ?? [];
|
|
126
|
+
});
|
|
127
|
+
for (const token of pointerSegments(pointer)) {
|
|
128
|
+
const step = ({ pointer: at, fact }) => {
|
|
129
|
+
if (fact.kind === 'unreadable')
|
|
130
|
+
return [{ pointer: at, fact }];
|
|
131
|
+
if (fact.kind === 'object')
|
|
132
|
+
return Object.hasOwn(fact.fields, token)
|
|
133
|
+
? [{ pointer: `${at}/${escapePointer(token)}`, fact: fact.fields[token] }]
|
|
134
|
+
: null;
|
|
135
|
+
if (fact.kind !== 'list')
|
|
136
|
+
return null;
|
|
137
|
+
if (ARRAY_INDEX.test(token)) {
|
|
138
|
+
const item = fact.items[Number(token)];
|
|
139
|
+
return item === undefined ? null : [{ pointer: `${at}/${token}`, fact: item }];
|
|
140
|
+
}
|
|
141
|
+
several = true;
|
|
142
|
+
return each(fact.items.map((item, index) => ({ pointer: `${at}/${index}`, fact: item })), step);
|
|
143
|
+
};
|
|
144
|
+
reached = each(reached, step);
|
|
145
|
+
}
|
|
146
|
+
return { reached, several, missing };
|
|
147
|
+
}
|
|
148
|
+
function expand(fact) {
|
|
149
|
+
return fact.kind === 'list' ? fact.items.flatMap(expand) : [fact];
|
|
150
|
+
}
|
|
151
|
+
function fieldHeld(fact) {
|
|
152
|
+
if (fact.kind === 'unreadable')
|
|
153
|
+
return undefined;
|
|
154
|
+
if (fact.kind === 'null')
|
|
155
|
+
return { kind: 'null' };
|
|
156
|
+
return 'value' in fact ? { kind: fact.kind, value: fact.value } : { kind: 'prose' };
|
|
157
|
+
}
|
|
158
|
+
function memberHeld(fact) {
|
|
159
|
+
if (fact.kind === 'unreadable')
|
|
160
|
+
return undefined;
|
|
161
|
+
return fact.kind === 'reference' && fact.member !== undefined
|
|
162
|
+
? { kind: 'member', value: fact.member }
|
|
163
|
+
: null;
|
|
164
|
+
}
|
|
165
|
+
function single(held) {
|
|
166
|
+
return { present: true, values: [held], items: null };
|
|
167
|
+
}
|
|
168
|
+
function documentRead({ reached, several, missing }, held) {
|
|
169
|
+
if (reached.length === 0)
|
|
170
|
+
return missing ? undefined : NO_ITEM;
|
|
171
|
+
const facts = reached.map(({ fact }) => fact);
|
|
172
|
+
const kept = (all) => all.map(held).filter((value) => value !== null);
|
|
173
|
+
const unknown = missing ? [undefined] : [];
|
|
174
|
+
const values = kept(facts.filter((fact) => fact.kind !== 'list'));
|
|
175
|
+
const listed = several || facts.some((fact) => fact.kind === 'list');
|
|
176
|
+
const present = facts.some((fact) => fact.kind === 'list') || values.some(Boolean);
|
|
177
|
+
return {
|
|
178
|
+
present: present ? true : values.length > 0 ? undefined : false,
|
|
179
|
+
values: [...values, ...unknown],
|
|
180
|
+
items: listed ? [...kept(facts.flatMap(expand)), ...unknown] : null,
|
|
98
181
|
};
|
|
99
182
|
}
|
|
100
183
|
function readDocument(facts, path) {
|
|
101
184
|
if (path === '/path')
|
|
102
|
-
return { kind: 'path', value: facts.path };
|
|
185
|
+
return single({ kind: 'path', value: facts.path });
|
|
103
186
|
if (path === '/type')
|
|
104
|
-
return facts.type === null ? undefined : { kind: 'string', value: facts.type };
|
|
105
|
-
if (
|
|
106
|
-
return
|
|
107
|
-
|
|
108
|
-
return { kind: 'present' };
|
|
109
|
-
if (path.startsWith('/fields/')) {
|
|
110
|
-
const name = path.slice('/fields/'.length);
|
|
111
|
-
return Object.hasOwn(facts.fields, name) ? facts.fields[name] : undefined;
|
|
112
|
-
}
|
|
113
|
-
if (path.startsWith('/members/')) {
|
|
114
|
-
const name = path.slice('/members/'.length);
|
|
115
|
-
return Object.hasOwn(facts.members, name)
|
|
116
|
-
? { kind: 'member', value: facts.members[name] }
|
|
187
|
+
return facts.type === null ? undefined : single({ kind: 'string', value: facts.type });
|
|
188
|
+
if (path.startsWith('/sections/'))
|
|
189
|
+
return facts.sections.includes(path.slice('/sections/'.length))
|
|
190
|
+
? single({ kind: 'prose' })
|
|
117
191
|
: undefined;
|
|
118
|
-
|
|
192
|
+
if (path.startsWith('/fields/'))
|
|
193
|
+
return documentRead(walked(rootOf(facts, '/fields'), path.slice('/fields'.length)), fieldHeld);
|
|
194
|
+
if (path.startsWith('/members/'))
|
|
195
|
+
return documentRead(walked(rootOf(facts, '/members'), path.slice('/members'.length)), memberHeld);
|
|
119
196
|
return undefined;
|
|
120
197
|
}
|
|
121
198
|
function readActor(actor, path) {
|
|
@@ -126,13 +203,7 @@ function readActor(actor, path) {
|
|
|
126
203
|
: path === '/door'
|
|
127
204
|
? actor.door
|
|
128
205
|
: undefined;
|
|
129
|
-
return value === undefined ? undefined : { kind: 'actor', value };
|
|
130
|
-
}
|
|
131
|
-
function plain(value) {
|
|
132
|
-
if (!isRecord(value))
|
|
133
|
-
return false;
|
|
134
|
-
const prototype = Object.getPrototypeOf(value);
|
|
135
|
-
return prototype === Object.prototype || prototype === null;
|
|
206
|
+
return value === undefined ? undefined : single({ kind: 'actor', value });
|
|
136
207
|
}
|
|
137
208
|
export function dated(value) {
|
|
138
209
|
return (typeof value === 'string' && DATE.test(value) && instant(`${value}T00:00:00Z`) !== undefined);
|
|
@@ -187,8 +258,6 @@ function scalar(value) {
|
|
|
187
258
|
(typeof value === 'number' && Number.isFinite(value)));
|
|
188
259
|
}
|
|
189
260
|
function jsonHeld(value) {
|
|
190
|
-
if (value === undefined)
|
|
191
|
-
return undefined;
|
|
192
261
|
if (typeof value === 'string')
|
|
193
262
|
return {
|
|
194
263
|
kind: dated(value) ? 'date' : instant(value) === undefined ? 'string' : 'time',
|
|
@@ -196,26 +265,31 @@ function jsonHeld(value) {
|
|
|
196
265
|
};
|
|
197
266
|
if (typeof value === 'boolean')
|
|
198
267
|
return { kind: 'boolean', value };
|
|
268
|
+
return { kind: 'number', value };
|
|
269
|
+
}
|
|
270
|
+
function jsonRead(value) {
|
|
271
|
+
if (value === undefined)
|
|
272
|
+
return undefined;
|
|
199
273
|
if (scalar(value))
|
|
200
|
-
return
|
|
201
|
-
if (Array.isArray(value))
|
|
202
|
-
return { kind: '
|
|
203
|
-
return {
|
|
274
|
+
return single(jsonHeld(value));
|
|
275
|
+
if (!Array.isArray(value))
|
|
276
|
+
return single({ kind: 'prose' });
|
|
277
|
+
return { present: true, values: [], items: value.filter(scalar).map(jsonHeld), loose: true };
|
|
204
278
|
}
|
|
205
279
|
function readItem(item, path) {
|
|
206
280
|
if (path.startsWith('/json/')) {
|
|
207
281
|
if (item.json === null)
|
|
208
|
-
return
|
|
209
|
-
return
|
|
282
|
+
return UNREAD;
|
|
283
|
+
return jsonRead(pointed(item.json.value, path.slice('/json'.length)));
|
|
210
284
|
}
|
|
211
285
|
if (path.startsWith('/headers/')) {
|
|
212
286
|
const name = path.slice('/headers/'.length);
|
|
213
287
|
return Object.hasOwn(item.headers, name)
|
|
214
|
-
? { kind: 'string', value: item.headers[name] }
|
|
288
|
+
? single({ kind: 'string', value: item.headers[name] })
|
|
215
289
|
: undefined;
|
|
216
290
|
}
|
|
217
291
|
if (path === '/has_attachment')
|
|
218
|
-
return { kind: 'boolean', value: item.has_attachment };
|
|
292
|
+
return single({ kind: 'boolean', value: item.has_attachment });
|
|
219
293
|
const value = path === '/delivery'
|
|
220
294
|
? item.delivery
|
|
221
295
|
: path === '/source'
|
|
@@ -227,13 +301,13 @@ function readItem(item, path) {
|
|
|
227
301
|
: path === '/author'
|
|
228
302
|
? item.author
|
|
229
303
|
: null;
|
|
230
|
-
return value === null ? undefined : { kind: 'string', value };
|
|
304
|
+
return value === null ? undefined : single({ kind: 'string', value });
|
|
231
305
|
}
|
|
232
306
|
function wanted(value, owner) {
|
|
233
307
|
return isRecord(value) && value.owner === true ? owner : value;
|
|
234
308
|
}
|
|
235
309
|
function equal(held, value, owner) {
|
|
236
|
-
if (
|
|
310
|
+
if (!('value' in held))
|
|
237
311
|
return undefined;
|
|
238
312
|
const expected = wanted(value, owner);
|
|
239
313
|
if (typeof expected !== typeof held.value)
|
|
@@ -248,7 +322,7 @@ function sign(left, right) {
|
|
|
248
322
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
249
323
|
}
|
|
250
324
|
function ordered(held, value, compare) {
|
|
251
|
-
if (
|
|
325
|
+
if (!('value' in held))
|
|
252
326
|
return undefined;
|
|
253
327
|
if (held.kind === 'number') {
|
|
254
328
|
if (typeof value !== 'number' || typeof held.value !== 'number')
|
|
@@ -278,29 +352,13 @@ function allOf(truths) {
|
|
|
278
352
|
return false;
|
|
279
353
|
return truths.some((truth) => truth === undefined) ? undefined : true;
|
|
280
354
|
}
|
|
281
|
-
function
|
|
282
|
-
if (held?.kind === 'unreadable')
|
|
283
|
-
return undefined;
|
|
284
|
-
if ('exists' in test)
|
|
285
|
-
return (held !== undefined) === test.exists;
|
|
286
|
-
if (held === undefined)
|
|
287
|
-
return undefined;
|
|
355
|
+
function compared(test, held, owner) {
|
|
288
356
|
if ('eq' in test)
|
|
289
357
|
return equal(held, test.eq, owner);
|
|
290
358
|
if ('in' in test)
|
|
291
359
|
return anyOf(test.in.map((value) => equal(held, value, owner)));
|
|
292
|
-
if ('contains' in test) {
|
|
293
|
-
if (held.kind === 'present' || !Array.isArray(held.value))
|
|
294
|
-
return undefined;
|
|
295
|
-
const expected = wanted(test.contains, owner);
|
|
296
|
-
if (held.value.some((item) => item === expected))
|
|
297
|
-
return true;
|
|
298
|
-
if (held.kind === 'list')
|
|
299
|
-
return false;
|
|
300
|
-
return held.value.every((item) => typeof item === typeof expected) ? false : undefined;
|
|
301
|
-
}
|
|
302
360
|
if ('prefix' in test) {
|
|
303
|
-
if (
|
|
361
|
+
if (!('value' in held) || typeof held.value !== 'string')
|
|
304
362
|
return undefined;
|
|
305
363
|
if (held.kind === 'number' || held.kind === 'reference' || held.kind === 'member')
|
|
306
364
|
return undefined;
|
|
@@ -314,6 +372,23 @@ function leaf(test, held, owner) {
|
|
|
314
372
|
return ordered(held, test.lt, (sign) => sign < 0);
|
|
315
373
|
return ordered(held, test.lte, (sign) => sign <= 0);
|
|
316
374
|
}
|
|
375
|
+
function leaf(test, read, owner) {
|
|
376
|
+
if ('exists' in test) {
|
|
377
|
+
const present = read === undefined ? false : read.present;
|
|
378
|
+
return present === undefined ? undefined : present === test.exists;
|
|
379
|
+
}
|
|
380
|
+
if (read === undefined)
|
|
381
|
+
return undefined;
|
|
382
|
+
if ('contains' in test) {
|
|
383
|
+
if (read.items === null)
|
|
384
|
+
return undefined;
|
|
385
|
+
const truths = read.items.map((item) => item === undefined ? undefined : equal(item, test.contains, owner));
|
|
386
|
+
return read.loose === true && !truths.includes(true) ? false : anyOf(truths);
|
|
387
|
+
}
|
|
388
|
+
if (read.values.length === 0)
|
|
389
|
+
return read === NO_ITEM ? false : undefined;
|
|
390
|
+
return anyOf(read.values.map((value) => (value === undefined ? undefined : compared(test, value, owner))));
|
|
391
|
+
}
|
|
317
392
|
function evaluated(test, read, owner) {
|
|
318
393
|
if ('all' in test)
|
|
319
394
|
return allOf(test.all.map((part) => evaluated(part, read, owner)));
|
|
@@ -342,24 +417,46 @@ function matched(condition, test, document, owner) {
|
|
|
342
417
|
return false;
|
|
343
418
|
return test === undefined || holds(test, document, owner) === true;
|
|
344
419
|
}
|
|
345
|
-
function
|
|
346
|
-
|
|
420
|
+
function reachedAt(document, field) {
|
|
421
|
+
if (document === null)
|
|
422
|
+
return [];
|
|
423
|
+
if (field.startsWith('/sections/'))
|
|
424
|
+
return document.sections.includes(field.slice('/sections/'.length))
|
|
425
|
+
? [{ pointer: field, fact: PROSE }]
|
|
426
|
+
: [];
|
|
427
|
+
if (!field.startsWith('/fields/'))
|
|
428
|
+
return [];
|
|
429
|
+
return walked(rootOf(document, '/fields'), field.slice('/fields'.length)).reached;
|
|
430
|
+
}
|
|
431
|
+
function valueOf(fact) {
|
|
432
|
+
if (fact.kind === 'list')
|
|
433
|
+
return { kind: fact.kind, items: fact.items.map(valueOf) };
|
|
434
|
+
if (fact.kind === 'object')
|
|
435
|
+
return {
|
|
436
|
+
kind: fact.kind,
|
|
437
|
+
fields: Object.keys(fact.fields)
|
|
438
|
+
.sort()
|
|
439
|
+
.map((key) => [key, valueOf(fact.fields[key])]),
|
|
440
|
+
};
|
|
441
|
+
return 'value' in fact ? { kind: fact.kind, value: fact.value } : fact;
|
|
442
|
+
}
|
|
443
|
+
function signature(reached) {
|
|
444
|
+
return JSON.stringify(reached.map(({ fact }) => valueOf(fact)));
|
|
347
445
|
}
|
|
348
446
|
function among(values, document, field, owner) {
|
|
349
447
|
return holds({ path: field, in: values }, document, owner) === true;
|
|
350
448
|
}
|
|
351
449
|
function fieldChanged(condition, event, owner) {
|
|
352
450
|
const { field, from, to } = condition;
|
|
353
|
-
|
|
451
|
+
const before = reachedAt(event.before, field);
|
|
452
|
+
const after = reachedAt(event.after, field);
|
|
453
|
+
const pointers = [field, ...[...before, ...after].map(({ pointer }) => pointer)];
|
|
454
|
+
const prose = event.changed.some((changed) => pointers.some((pointer) => changed === pointer || changed.startsWith(`${pointer}/`)));
|
|
455
|
+
if (from === undefined && to === undefined && prose)
|
|
354
456
|
return true;
|
|
355
|
-
if (
|
|
356
|
-
return false;
|
|
357
|
-
const name = field.slice('/fields/'.length);
|
|
358
|
-
const before = event.before?.fields[name];
|
|
359
|
-
const after = event.after?.fields[name];
|
|
360
|
-
if (before === undefined && after === undefined)
|
|
457
|
+
if (before.length === 0 && after.length === 0)
|
|
361
458
|
return false;
|
|
362
|
-
if (event.before !== null && event.after !== null &&
|
|
459
|
+
if (event.before !== null && event.after !== null && signature(before) === signature(after))
|
|
363
460
|
return false;
|
|
364
461
|
if (from === 'new'
|
|
365
462
|
? event.before !== null
|
package/dist/definition.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export type ValueFacts = {
|
|
|
6
6
|
readonly values?: readonly unknown[];
|
|
7
7
|
readonly reference?: readonly string[];
|
|
8
8
|
readonly prose: boolean;
|
|
9
|
+
readonly conditional?: true;
|
|
9
10
|
readonly prefix?: readonly ValueFacts[];
|
|
10
11
|
readonly items?: ValueFacts;
|
|
11
12
|
readonly properties?: Readonly<Record<string, ValueFacts>>;
|
|
@@ -53,5 +54,13 @@ export declare function refTarget(reference: string, defs: Readonly<Record<strin
|
|
|
53
54
|
export declare function valueFacts(node: unknown, defs: Readonly<Record<string, unknown>>): ValueFacts;
|
|
54
55
|
export declare function itemFacts(facts: ValueFacts, index: number): ValueFacts | undefined;
|
|
55
56
|
export declare function memberFacts(facts: ValueFacts, key: string): ValueFacts | undefined;
|
|
57
|
+
export type PointerFacts = {
|
|
58
|
+
readonly facts: ValueFacts;
|
|
59
|
+
readonly through: boolean;
|
|
60
|
+
readonly conditional: boolean;
|
|
61
|
+
};
|
|
62
|
+
export declare function crossedFacts(facts: ValueFacts): ValueFacts[];
|
|
63
|
+
export declare function fieldFacts(definition: Definition, key: string): ValueFacts | undefined;
|
|
64
|
+
export declare function pointerFacts(definition: Definition, pointer: string): PointerFacts | undefined;
|
|
56
65
|
export declare function readSchema(text: string): VerifiedModel | Failure;
|
|
57
66
|
//# sourceMappingURL=definition.d.ts.map
|
package/dist/definition.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../src/definition.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../src/definition.ts"],"names":[],"mappings":"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"}
|
package/dist/definition.js
CHANGED
|
@@ -306,7 +306,8 @@ function propertiesOf(held, defs, ancestry) {
|
|
|
306
306
|
variants[name] = Object.hasOwn(variants, name) ? joined(variants[name], facts) : facts;
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
|
-
Object.
|
|
309
|
+
for (const [name, facts] of Object.entries(variants))
|
|
310
|
+
properties[name] = { ...facts, conditional: true };
|
|
310
311
|
return Object.keys(properties).length === 0 ? undefined : properties;
|
|
311
312
|
}
|
|
312
313
|
function factsOf(nodes, defs, ancestry) {
|
|
@@ -369,6 +370,54 @@ export function memberFacts(facts, key) {
|
|
|
369
370
|
const pattern = facts.patterns?.find((one) => new RegExp(one.pattern, 'u').test(key));
|
|
370
371
|
return pattern === undefined ? facts.additional : pattern.facts;
|
|
371
372
|
}
|
|
373
|
+
export function crossedFacts(facts) {
|
|
374
|
+
if (facts.type !== 'array')
|
|
375
|
+
return [facts];
|
|
376
|
+
return [...(facts.prefix ?? []), ...(facts.items === undefined ? [] : [facts.items])].flatMap(crossedFacts);
|
|
377
|
+
}
|
|
378
|
+
export function fieldFacts(definition, key) {
|
|
379
|
+
if (Object.hasOwn(definition.fields, key))
|
|
380
|
+
return definition.fields[key];
|
|
381
|
+
if (Object.hasOwn(HOUSE_KEYS, key))
|
|
382
|
+
return undefined;
|
|
383
|
+
return memberFacts(definition.frontmatter, key);
|
|
384
|
+
}
|
|
385
|
+
function stepped({ facts, through, conditional }, token) {
|
|
386
|
+
if (facts.type === 'array' && ARRAY_INDEX.test(token)) {
|
|
387
|
+
const item = itemFacts(facts, Number(token));
|
|
388
|
+
return item === undefined ? [] : [{ facts: item, through, conditional }];
|
|
389
|
+
}
|
|
390
|
+
return crossedFacts(facts).flatMap((crossed) => {
|
|
391
|
+
const member = memberFacts(crossed, token);
|
|
392
|
+
return member === undefined
|
|
393
|
+
? []
|
|
394
|
+
: [
|
|
395
|
+
{
|
|
396
|
+
facts: member,
|
|
397
|
+
through: through || facts.type === 'array',
|
|
398
|
+
conditional: conditional || member.conditional === true,
|
|
399
|
+
},
|
|
400
|
+
];
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
export function pointerFacts(definition, pointer) {
|
|
404
|
+
const [field, ...rest] = pointerSegments(pointer);
|
|
405
|
+
const declared = field === undefined ? undefined : fieldFacts(definition, field);
|
|
406
|
+
if (declared === undefined)
|
|
407
|
+
return undefined;
|
|
408
|
+
let reached = [
|
|
409
|
+
{ facts: declared, through: false, conditional: declared.conditional === true },
|
|
410
|
+
];
|
|
411
|
+
for (const token of rest)
|
|
412
|
+
reached = reached.flatMap((held) => stepped(held, token));
|
|
413
|
+
if (reached.length === 0)
|
|
414
|
+
return undefined;
|
|
415
|
+
return reached.reduce((kept, held) => ({
|
|
416
|
+
facts: joined(kept.facts, held.facts),
|
|
417
|
+
through: kept.through || held.through,
|
|
418
|
+
conditional: kept.conditional || held.conditional,
|
|
419
|
+
}));
|
|
420
|
+
}
|
|
372
421
|
function reachedDefs(value, defs, reached) {
|
|
373
422
|
if (Array.isArray(value)) {
|
|
374
423
|
for (const item of value)
|
package/dist/document.d.ts
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import { type Definition } from './definition.js';
|
|
2
|
-
import type { ApplyResult, Change, Document, Link, ParseResult
|
|
2
|
+
import type { ApplyResult, BodyResult, Change, Document, Link, ParseResult } from './types.js';
|
|
3
3
|
import { type YamlMap } from './yaml.js';
|
|
4
4
|
export declare function parse(content: string, file?: string): ParseResult;
|
|
5
5
|
export declare function relativize(document: Document, file: string, handle: string, definition?: Definition): ApplyResult;
|
|
6
|
-
export declare function relativizeBody(body: string, file: string, handle: string):
|
|
7
|
-
|
|
8
|
-
readonly body: string;
|
|
9
|
-
} | {
|
|
10
|
-
readonly outcome: 'refuse';
|
|
11
|
-
readonly refusal: Refusal;
|
|
12
|
-
};
|
|
6
|
+
export declare function relativizeBody(body: string, file: string, handle: string): BodyResult;
|
|
7
|
+
export declare function relinkBody(body: string, file: string, moves: ReadonlyMap<string, string>): BodyResult;
|
|
13
8
|
export declare function links(document: Document, file: string): readonly Link[];
|
|
9
|
+
export declare function bodyLinks(body: string, file: string): readonly Link[];
|
|
14
10
|
export declare function apply(document: Document, change: Change): ApplyResult;
|
|
15
11
|
export declare function serialize(document: Document): string;
|
|
16
12
|
export declare function frontmatter(document: Document): YamlMap | null;
|
package/dist/document.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMhF,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMhF,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,WAAW,EAEZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAaL,KAAK,OAAO,EAEb,MAAM,WAAW,CAAC;AAwCnB,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,WAAW,CAkD7D;AA8KD,wBAAgB,UAAU,CACxB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,UAAU,GACtB,WAAW,CAYb;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAOrF;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GACjC,UAAU,CAeZ;AAeD,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,IAAI,EAAE,CAEvE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,IAAI,EAAE,CAErE;AAED,wBAAgB,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAOrE;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAEpD;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAAG,IAAI,CAE9D"}
|
package/dist/document.js
CHANGED
|
@@ -4,7 +4,7 @@ import { relative, retarget, SHELL_ROOMS, shellTarget, target } from './paths.js
|
|
|
4
4
|
import { references } from './references.js';
|
|
5
5
|
import { headingSpans, sectionsRecord } from './sections.js';
|
|
6
6
|
import { refusal } from './refuse.js';
|
|
7
|
-
import { bodyStartAfterYaml, bodyStartOf, equalValue, extractFrontmatter, jsValue, lineEnding, mapEntry, parseYamlMapping, serializeJs, serializeKey, YamlError, } from './yaml.js';
|
|
7
|
+
import { bodyStartAfterYaml, bodyStartOf, equalValue, extractFrontmatter, frontmatterValue, jsValue, lineEnding, mapEntry, parseYamlMapping, serializeJs, serializeKey, YamlError, } from './yaml.js';
|
|
8
8
|
const internals = new WeakMap();
|
|
9
9
|
function refuse(file, rule, location) {
|
|
10
10
|
return location === undefined ? refusal(file, rule) : refusal(file, rule, { location });
|
|
@@ -78,6 +78,9 @@ function applyField(document, name, value, prior) {
|
|
|
78
78
|
if (!equalValue(document.fields[name], prior)) {
|
|
79
79
|
return { outcome: 'stale', document };
|
|
80
80
|
}
|
|
81
|
+
if (!frontmatterValue(value)) {
|
|
82
|
+
return { outcome: 'refuse', refusal: refuse('', 'frontmatter-fence', name) };
|
|
83
|
+
}
|
|
81
84
|
let nextContent;
|
|
82
85
|
const existing = mapEntry(internal.root, name);
|
|
83
86
|
if (existing !== undefined) {
|
|
@@ -253,10 +256,27 @@ export function relativizeBody(body, file, handle) {
|
|
|
253
256
|
return { outcome: 'refuse', refusal: refuse(file, 'unresolved-link', elsewhere.href) };
|
|
254
257
|
return { outcome: 'applied', body: relinkedBody };
|
|
255
258
|
}
|
|
256
|
-
export function
|
|
259
|
+
export function relinkBody(body, file, moves) {
|
|
260
|
+
let rewritten = '';
|
|
261
|
+
let cursor = 0;
|
|
262
|
+
for (const match of hrefOccurrences(body)) {
|
|
263
|
+
const from = target(file, match.href);
|
|
264
|
+
const to = from === null ? undefined : moves.get(from);
|
|
265
|
+
if (to === undefined)
|
|
266
|
+
continue;
|
|
267
|
+
const written = retarget(match.href, relative(file, to));
|
|
268
|
+
const text = destinationText(written, match.bracketed);
|
|
269
|
+
if (text === null)
|
|
270
|
+
return { outcome: 'refuse', refusal: refuse(file, 'unwritable-link', written) };
|
|
271
|
+
rewritten += body.slice(cursor, match.start) + text;
|
|
272
|
+
cursor = match.end;
|
|
273
|
+
}
|
|
274
|
+
return { outcome: 'applied', body: rewritten + body.slice(cursor) };
|
|
275
|
+
}
|
|
276
|
+
function linksIn(text, file) {
|
|
257
277
|
const found = [];
|
|
258
278
|
const seen = new Set();
|
|
259
|
-
for (const occurrence of hrefOccurrences(
|
|
279
|
+
for (const occurrence of hrefOccurrences(text)) {
|
|
260
280
|
if (seen.has(occurrence.href))
|
|
261
281
|
continue;
|
|
262
282
|
const resolved = target(file, occurrence.href);
|
|
@@ -267,6 +287,12 @@ export function links(document, file) {
|
|
|
267
287
|
}
|
|
268
288
|
return found;
|
|
269
289
|
}
|
|
290
|
+
export function links(document, file) {
|
|
291
|
+
return linksIn(document.content.slice(bodyStartOf(document.content)), file);
|
|
292
|
+
}
|
|
293
|
+
export function bodyLinks(body, file) {
|
|
294
|
+
return linksIn(body, file);
|
|
295
|
+
}
|
|
270
296
|
export function apply(document, change) {
|
|
271
297
|
if (change.kind === 'link')
|
|
272
298
|
return applyLink(document, change.href, change.to);
|
|
@@ -274,17 +300,9 @@ export function apply(document, change) {
|
|
|
274
300
|
return applyReference(document, change.pointer, change.value, change.prior);
|
|
275
301
|
if (change.kind === 'preamble')
|
|
276
302
|
return applyPreamble(document, change.value, change.prior);
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
return applySection(document, change.name, change.value, change.prior);
|
|
281
|
-
}
|
|
282
|
-
catch (error) {
|
|
283
|
-
if (error instanceof YamlError) {
|
|
284
|
-
return { outcome: 'refuse', refusal: refuse('', 'frontmatter-fence', change.name) };
|
|
285
|
-
}
|
|
286
|
-
throw error;
|
|
287
|
-
}
|
|
303
|
+
if (change.kind === 'field')
|
|
304
|
+
return applyField(document, change.name, change.value, change.prior);
|
|
305
|
+
return applySection(document, change.name, change.value, change.prior);
|
|
288
306
|
}
|
|
289
307
|
export function serialize(document) {
|
|
290
308
|
return document.content;
|