@agentshouse/mdmodel 0.1.0-ce8ce7ff57e88 → 0.1.0-cf26f43c2e4d9
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 +85 -75
- package/README.md +1 -1
- package/dist/blocks.d.ts.map +1 -1
- package/dist/blocks.js +29 -7
- package/dist/condition.d.ts +23 -9
- package/dist/condition.d.ts.map +1 -1
- package/dist/condition.js +202 -86
- package/dist/definition.d.ts +2 -1
- package/dist/definition.d.ts.map +1 -1
- package/dist/definition.js +96 -20
- package/dist/divide.js +9 -9
- package/dist/document.d.ts +4 -4
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +38 -38
- package/dist/index.d.ts +7 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/links.d.ts +1 -0
- package/dist/links.d.ts.map +1 -1
- package/dist/links.js +85 -39
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +4 -1
- package/dist/pattern.d.ts +10 -0
- package/dist/pattern.d.ts.map +1 -0
- package/dist/pattern.js +355 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +3 -5
- package/dist/schema.d.ts +1 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +55 -4
- package/dist/types.d.ts +9 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/validate.d.ts +25 -4
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +163 -65
- package/dist/yaml.d.ts.map +1 -1
- package/dist/yaml.js +27 -1
- package/fixtures/README.md +9 -4
- package/fixtures/cases/conditional-section/context.json +1 -0
- package/fixtures/cases/conditional-section/expected.json +6 -0
- package/fixtures/cases/conditional-section/library/questions/hosting.md +9 -0
- package/fixtures/cases/conditional-section/library/questions/payments.md +12 -0
- package/fixtures/cases/conditional-section/schema.yaml +14 -0
- package/fixtures/division.json +26 -26
- package/package.json +7 -2
package/dist/validate.js
CHANGED
|
@@ -4,7 +4,8 @@ import { hrefOccurrences, linkInventory } from './links.js';
|
|
|
4
4
|
import { classifyHref, roomTarget } from './paths.js';
|
|
5
5
|
import { references } from './references.js';
|
|
6
6
|
import { refusal } from './refuse.js';
|
|
7
|
-
import { fitsItem, frontmatterRefusal } from './schema.js';
|
|
7
|
+
import { conditionedSections, fitsItem, frontmatterRefusal } from './schema.js';
|
|
8
|
+
import { openPatternBudget, PatternTooCostly } from './pattern.js';
|
|
8
9
|
import { headingSpans, listItems } from './sections.js';
|
|
9
10
|
import { bodyAfterFrontmatter, bodyStartOf } from './yaml.js';
|
|
10
11
|
function refuse(file, rule, extra) {
|
|
@@ -60,8 +61,8 @@ function itemHrefs(content, items, uses) {
|
|
|
60
61
|
}
|
|
61
62
|
return hrefs;
|
|
62
63
|
}
|
|
63
|
-
export function verify(schema) {
|
|
64
|
-
const read = readSchema(schema);
|
|
64
|
+
export function verify(schema, provided) {
|
|
65
|
+
const read = readSchema(schema, provided);
|
|
65
66
|
if ('types' in read)
|
|
66
67
|
return { verdict: 'accept', model: read };
|
|
67
68
|
return {
|
|
@@ -80,10 +81,14 @@ function structure(file, content, document, definition) {
|
|
|
80
81
|
const spans = headingSpans(content, bodyStartOf(content));
|
|
81
82
|
const uses = usesIn(content);
|
|
82
83
|
const sections = [];
|
|
84
|
+
const conditioned = conditionedSections(definition, document.fields);
|
|
83
85
|
for (const [name, spec] of Object.entries(definition.sections)) {
|
|
84
86
|
const occurrences = spans.filter((one) => one.name === name);
|
|
85
87
|
if (occurrences.length === 0 && spec.required)
|
|
86
88
|
return { refusal: refuse(file, 'required-section', { location: name }) };
|
|
89
|
+
if (conditioned.includes(name) &&
|
|
90
|
+
occurrences.every((span) => content.slice(span.bodyStart, span.bodyEnd).trim() === ''))
|
|
91
|
+
return { refusal: refuse(file, 'conditional-section', { location: name }) };
|
|
87
92
|
if (spec.kind !== 'list')
|
|
88
93
|
continue;
|
|
89
94
|
for (const span of occurrences) {
|
|
@@ -120,7 +125,8 @@ function definitionOf(document, model) {
|
|
|
120
125
|
const kind = instanceType(document);
|
|
121
126
|
return kind === null || !Object.hasOwn(model.types, kind) ? undefined : model.types[kind];
|
|
122
127
|
}
|
|
123
|
-
export function checkDocument(file, content, model) {
|
|
128
|
+
export function checkDocument(file, content, model, context = {}) {
|
|
129
|
+
const anyRoom = context.anyRoom === true;
|
|
124
130
|
const parsed = parse(content, file);
|
|
125
131
|
if (!parsed.ok)
|
|
126
132
|
return { ok: false };
|
|
@@ -137,8 +143,8 @@ export function checkDocument(file, content, model) {
|
|
|
137
143
|
type,
|
|
138
144
|
typed: true,
|
|
139
145
|
references: [
|
|
140
|
-
...references(document, definition).flatMap(({ value, to, crossRoom }) => crossing(file, value, crossRoom) ? [] : [reached(file, value, to)]),
|
|
141
|
-
...read.sections.flatMap(({ to, crossRoom, hrefs }) => hrefs.flatMap((href) =>
|
|
146
|
+
...references(document, definition).flatMap(({ value, to, crossRoom }) => crossing(file, value, anyRoom || crossRoom) ? [] : [reached(file, value, to)]),
|
|
147
|
+
...read.sections.flatMap(({ to, crossRoom, hrefs }) => hrefs.flatMap((href) => crossing(file, href, anyRoom || crossRoom) ? [] : [reached(file, href, to)])),
|
|
142
148
|
],
|
|
143
149
|
};
|
|
144
150
|
}
|
|
@@ -180,90 +186,182 @@ export function roomReferences(file, content, definition) {
|
|
|
180
186
|
if (!parsed.ok)
|
|
181
187
|
return found;
|
|
182
188
|
for (const { pointer, value, to, crossRoom } of references(parsed.document, definition)) {
|
|
183
|
-
const room =
|
|
189
|
+
const room = roomTarget(value);
|
|
184
190
|
if (room !== null)
|
|
185
|
-
found.push({
|
|
191
|
+
found.push({
|
|
192
|
+
kind: 'field',
|
|
193
|
+
pointer,
|
|
194
|
+
value,
|
|
195
|
+
to,
|
|
196
|
+
...room,
|
|
197
|
+
...(crossRoom ? { crossRoom } : {}),
|
|
198
|
+
});
|
|
186
199
|
}
|
|
187
200
|
const uses = usesIn(content);
|
|
188
201
|
for (const span of headingSpans(content, bodyStartOf(content))) {
|
|
189
202
|
const spec = Object.hasOwn(definition.sections, span.name)
|
|
190
203
|
? definition.sections[span.name]
|
|
191
204
|
: undefined;
|
|
192
|
-
if (spec?.kind !== 'list' || spec.items.reference === undefined
|
|
205
|
+
if (spec?.kind !== 'list' || spec.items.reference === undefined)
|
|
193
206
|
continue;
|
|
194
207
|
const listed = listItems(content, span.bodyStart, span.bodyEnd);
|
|
195
208
|
for (const href of (listed === null ? null : itemHrefs(content, listed, uses)) ?? []) {
|
|
196
209
|
const room = roomTarget(href);
|
|
197
210
|
if (room !== null)
|
|
198
|
-
found.push({
|
|
211
|
+
found.push({
|
|
212
|
+
kind: 'item',
|
|
213
|
+
section: span.name,
|
|
214
|
+
href,
|
|
215
|
+
to: spec.items.reference,
|
|
216
|
+
...room,
|
|
217
|
+
...(spec.items.crossRoom === true ? { crossRoom: true } : {}),
|
|
218
|
+
});
|
|
199
219
|
}
|
|
200
220
|
}
|
|
201
221
|
return found;
|
|
202
222
|
}
|
|
223
|
+
function least(refused, file, verdict) {
|
|
224
|
+
return refused === undefined || file < refused.file ? { file, verdict } : refused;
|
|
225
|
+
}
|
|
226
|
+
export function assess(file, content, binary, model = { types: {} }, context = {}) {
|
|
227
|
+
const unread = { file, type: undefined, targets: [], structured: null, fields: [], sections: [] };
|
|
228
|
+
if (binary)
|
|
229
|
+
return { ...unread, read: refuse(file, 'binary-target-document'), linked: true };
|
|
230
|
+
const parsed = parse(content, file);
|
|
231
|
+
if (!parsed.ok)
|
|
232
|
+
return { ...unread, read: { verdict: 'refuse', ...parsed.refusal }, linked: true };
|
|
233
|
+
const document = parsed.document;
|
|
234
|
+
const type = document.type ?? BUILT_IN_TYPE;
|
|
235
|
+
const typed = { file, type, read: null, structured: null };
|
|
236
|
+
if (instanceType(document) === BUILT_IN_TYPE) {
|
|
237
|
+
return { ...typed, targets: [], fields: [], linked: true, sections: [] };
|
|
238
|
+
}
|
|
239
|
+
const definition = definitionOf(document, model);
|
|
240
|
+
try {
|
|
241
|
+
return structured(file, content, typed, document, definition, context);
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
if (!(error instanceof PatternTooCostly))
|
|
245
|
+
throw error;
|
|
246
|
+
return {
|
|
247
|
+
...typed,
|
|
248
|
+
targets: [],
|
|
249
|
+
structured: refuse(file, 'pattern', { location: '' }),
|
|
250
|
+
fields: [],
|
|
251
|
+
linked: true,
|
|
252
|
+
sections: [],
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
function structured(file, content, typed, document, definition, context) {
|
|
257
|
+
const read = structure(file, content, document, definition);
|
|
258
|
+
if ('refusal' in read || definition === undefined) {
|
|
259
|
+
return {
|
|
260
|
+
...typed,
|
|
261
|
+
targets: [],
|
|
262
|
+
structured: 'refusal' in read ? read.refusal : null,
|
|
263
|
+
fields: [],
|
|
264
|
+
linked: true,
|
|
265
|
+
sections: [],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
const anyRoom = context.anyRoom === true;
|
|
269
|
+
const fields = references(document, definition).flatMap(({ pointer, value, to, crossRoom }) => crossing(file, value, anyRoom || crossRoom)
|
|
270
|
+
? []
|
|
271
|
+
: [{ reference: reached(file, value, to), location: pointer }]);
|
|
272
|
+
const sections = read.sections.flatMap(({ section, to, crossRoom, hrefs }) => hrefs.flatMap((href) => crossing(file, href, anyRoom || crossRoom)
|
|
273
|
+
? []
|
|
274
|
+
: [{ reference: reached(file, href, to), location: section }]));
|
|
275
|
+
const targets = new Set();
|
|
276
|
+
for (const { reference } of [...fields, ...sections]) {
|
|
277
|
+
if (reference.target !== null)
|
|
278
|
+
targets.add(reference.target);
|
|
279
|
+
}
|
|
280
|
+
return { ...typed, targets: [...targets], fields, linked: linked(file, content), sections };
|
|
281
|
+
}
|
|
282
|
+
export function verdicts() {
|
|
283
|
+
let read;
|
|
284
|
+
let structured;
|
|
285
|
+
let referred;
|
|
286
|
+
let missed;
|
|
287
|
+
const unreached = (placed, typeOf, binary) => placed.find(({ reference }) => !reaches(reference, typeOf, binary));
|
|
288
|
+
return {
|
|
289
|
+
add(assessment, typeOf, binary) {
|
|
290
|
+
const { file } = assessment;
|
|
291
|
+
if (assessment.read !== null) {
|
|
292
|
+
read = least(read, file, assessment.read);
|
|
293
|
+
return 0;
|
|
294
|
+
}
|
|
295
|
+
if (assessment.structured !== null) {
|
|
296
|
+
structured = least(structured, file, assessment.structured);
|
|
297
|
+
return 0;
|
|
298
|
+
}
|
|
299
|
+
const field = unreached(assessment.fields, typeOf, binary);
|
|
300
|
+
if (field !== undefined) {
|
|
301
|
+
referred = least(referred, file, refuse(file, 'unresolved-reference', { location: field.location }));
|
|
302
|
+
return 0;
|
|
303
|
+
}
|
|
304
|
+
if (!assessment.linked) {
|
|
305
|
+
referred = least(referred, file, refuse(file, 'unresolved-link'));
|
|
306
|
+
return 0;
|
|
307
|
+
}
|
|
308
|
+
const section = unreached(assessment.sections, typeOf, binary);
|
|
309
|
+
if (section !== undefined) {
|
|
310
|
+
referred = least(referred, file, refuse(file, 'unresolved-reference', { location: section.location }));
|
|
311
|
+
return 0;
|
|
312
|
+
}
|
|
313
|
+
const all = [...assessment.fields, ...assessment.sections];
|
|
314
|
+
for (const { reference } of all) {
|
|
315
|
+
if (!missing(reference, typeOf, binary))
|
|
316
|
+
continue;
|
|
317
|
+
const path = reference.target;
|
|
318
|
+
if (missed === undefined || path < missed.path)
|
|
319
|
+
missed = { path, referrers: [] };
|
|
320
|
+
if (path === missed.path && missed.referrers.at(-1) !== file)
|
|
321
|
+
missed.referrers.push(file);
|
|
322
|
+
}
|
|
323
|
+
return all.length;
|
|
324
|
+
},
|
|
325
|
+
verdict() {
|
|
326
|
+
const refused = read ?? structured ?? referred;
|
|
327
|
+
if (refused !== undefined)
|
|
328
|
+
return refused.verdict;
|
|
329
|
+
if (missed !== undefined) {
|
|
330
|
+
return refuse(missed.path, 'referenced-missing-file', {
|
|
331
|
+
referrers: [...missed.referrers].sort(),
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
return { verdict: 'accept' };
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
}
|
|
203
338
|
export function* validating(library, context = { binaryTargets: [] }, model = { types: {} }) {
|
|
204
339
|
const binaryTargets = new Set(context.binaryTargets);
|
|
205
340
|
const binary = (path) => binaryTargets.has(path);
|
|
206
|
-
const files = Object.keys(library).sort();
|
|
207
341
|
const types = new Map();
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (instanceType(parsed.document) !== BUILT_IN_TYPE)
|
|
218
|
-
documents.set(file, parsed.document);
|
|
219
|
-
yield file;
|
|
220
|
-
}
|
|
221
|
-
const sectionReferences = new Map();
|
|
222
|
-
for (const [file, document] of documents) {
|
|
223
|
-
const read = structure(file, library[file], document, definitionOf(document, model));
|
|
224
|
-
if ('refusal' in read)
|
|
225
|
-
return read.refusal;
|
|
226
|
-
sectionReferences.set(file, read.sections);
|
|
227
|
-
yield file;
|
|
228
|
-
}
|
|
229
|
-
let least;
|
|
230
|
-
for (const [file, document] of documents) {
|
|
231
|
-
const definition = definitionOf(document, model);
|
|
232
|
-
const fields = references(document, definition).flatMap(({ pointer, value, to, crossRoom }) => crossing(file, value, crossRoom)
|
|
233
|
-
? []
|
|
234
|
-
: [{ reference: reached(file, value, to), location: pointer }]);
|
|
235
|
-
for (const { reference, location } of fields) {
|
|
236
|
-
if (!reaches(reference, typeOf, binary))
|
|
237
|
-
return refuse(file, 'unresolved-reference', { location });
|
|
342
|
+
const assessments = [];
|
|
343
|
+
const closeBudget = openPatternBudget();
|
|
344
|
+
try {
|
|
345
|
+
for (const [file, content] of library) {
|
|
346
|
+
const assessment = assess(file, content, binary(file), model, context);
|
|
347
|
+
if (assessment.type !== undefined)
|
|
348
|
+
types.set(file, assessment.type);
|
|
349
|
+
assessments.push(assessment);
|
|
350
|
+
yield { path: file, resolved: 0 };
|
|
238
351
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
.
|
|
243
|
-
.flatMap(({ section, to, crossRoom, hrefs }) => hrefs.flatMap((href) => crossing(file, href, crossRoom)
|
|
244
|
-
? []
|
|
245
|
-
: [{ reference: reached(file, href, to), location: section }]));
|
|
246
|
-
for (const { reference, location } of sections) {
|
|
247
|
-
if (!reaches(reference, typeOf, binary))
|
|
248
|
-
return refuse(file, 'unresolved-reference', { location });
|
|
352
|
+
const judged = verdicts();
|
|
353
|
+
for (const assessment of assessments) {
|
|
354
|
+
const resolved = judged.add(assessment, (path) => types.get(path), binary);
|
|
355
|
+
yield { path: assessment.file, resolved };
|
|
249
356
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (least === undefined || path < least.path)
|
|
255
|
-
least = { path, referrers: [] };
|
|
256
|
-
if (path === least.path && least.referrers.at(-1) !== file)
|
|
257
|
-
least.referrers.push(file);
|
|
258
|
-
}
|
|
259
|
-
yield file;
|
|
357
|
+
return judged.verdict();
|
|
358
|
+
}
|
|
359
|
+
finally {
|
|
360
|
+
closeBudget();
|
|
260
361
|
}
|
|
261
|
-
if (least !== undefined)
|
|
262
|
-
return refuse(least.path, 'referenced-missing-file', { referrers: least.referrers });
|
|
263
|
-
return { verdict: 'accept' };
|
|
264
362
|
}
|
|
265
363
|
export function validate(library, context = { binaryTargets: [] }, model = { types: {} }) {
|
|
266
|
-
const steps = validating(library, context, model);
|
|
364
|
+
const steps = validating(new Map(Object.entries(library)), context, model);
|
|
267
365
|
let step = steps.next();
|
|
268
366
|
while (step.done !== true)
|
|
269
367
|
step = steps.next();
|
package/dist/yaml.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../src/yaml.ts"],"names":[],"mappings":"AAAA,qBAAa,SAAU,SAAQ,KAAK;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM;IAAnC,YAAqB,MAAM,EAAE,MAAM,EAGlC;CACF;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../src/yaml.ts"],"names":[],"mappings":"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"}
|
package/dist/yaml.js
CHANGED
|
@@ -6,6 +6,18 @@ export class YamlError extends Error {
|
|
|
6
6
|
this.name = 'YamlError';
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
+
const YAML_DEPTH_MAX = 64;
|
|
10
|
+
function nested(c, parse) {
|
|
11
|
+
if (c.depth >= YAML_DEPTH_MAX)
|
|
12
|
+
throw new YamlError(c.i);
|
|
13
|
+
c.depth += 1;
|
|
14
|
+
try {
|
|
15
|
+
return parse();
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
c.depth -= 1;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
9
21
|
function peek(c) {
|
|
10
22
|
return c.i < c.end ? c.text[c.i] : '';
|
|
11
23
|
}
|
|
@@ -202,6 +214,9 @@ function parseFlowValue(c) {
|
|
|
202
214
|
return parseScalar(c, true);
|
|
203
215
|
}
|
|
204
216
|
function parseFlowMap(c) {
|
|
217
|
+
return nested(c, () => flowMap(c));
|
|
218
|
+
}
|
|
219
|
+
function flowMap(c) {
|
|
205
220
|
const start = c.i;
|
|
206
221
|
if (peek(c) !== '{')
|
|
207
222
|
throw new YamlError(c.i);
|
|
@@ -240,6 +255,9 @@ function parseFlowMap(c) {
|
|
|
240
255
|
throw new YamlError(c.i);
|
|
241
256
|
}
|
|
242
257
|
function parseFlowSeq(c) {
|
|
258
|
+
return nested(c, () => flowSeq(c));
|
|
259
|
+
}
|
|
260
|
+
function flowSeq(c) {
|
|
243
261
|
const start = c.i;
|
|
244
262
|
if (peek(c) !== '[')
|
|
245
263
|
throw new YamlError(c.i);
|
|
@@ -336,6 +354,9 @@ function parseSeqItem(c, indent) {
|
|
|
336
354
|
return parseValue(c, indent);
|
|
337
355
|
}
|
|
338
356
|
function parseBlockSeq(c, indent) {
|
|
357
|
+
return nested(c, () => blockSeq(c, indent));
|
|
358
|
+
}
|
|
359
|
+
function blockSeq(c, indent) {
|
|
339
360
|
const start = c.i;
|
|
340
361
|
const items = [];
|
|
341
362
|
let end = start;
|
|
@@ -355,6 +376,9 @@ function parseBlockSeq(c, indent) {
|
|
|
355
376
|
return { kind: 'seq', items, start, end };
|
|
356
377
|
}
|
|
357
378
|
function parseBlockMap(c, indent, inline = false) {
|
|
379
|
+
return nested(c, () => blockMap(c, indent, inline));
|
|
380
|
+
}
|
|
381
|
+
function blockMap(c, indent, inline) {
|
|
358
382
|
const start = c.i;
|
|
359
383
|
const entries = [];
|
|
360
384
|
const seen = new Set();
|
|
@@ -387,7 +411,7 @@ function parseBlockMap(c, indent, inline = false) {
|
|
|
387
411
|
return { kind: 'map', entries, start, end };
|
|
388
412
|
}
|
|
389
413
|
export function parseYamlMapping(text, start, end) {
|
|
390
|
-
const c = { text, i: start, end };
|
|
414
|
+
const c = { text, i: start, end, depth: 0 };
|
|
391
415
|
skipBlankAndComments(c);
|
|
392
416
|
if (c.i >= end)
|
|
393
417
|
return { kind: 'map', entries: [], start, end };
|
|
@@ -445,6 +469,8 @@ function heldWithin(value, within) {
|
|
|
445
469
|
return Number.isFinite(value);
|
|
446
470
|
if (typeof value !== 'object' || within.includes(value))
|
|
447
471
|
return false;
|
|
472
|
+
if (within.length + 1 >= YAML_DEPTH_MAX)
|
|
473
|
+
return false;
|
|
448
474
|
const path = [...within, value];
|
|
449
475
|
const held = (item) => heldWithin(item, path);
|
|
450
476
|
if (Array.isArray(value))
|
package/fixtures/README.md
CHANGED
|
@@ -51,8 +51,8 @@ change to a value that is not prose leaves the projection alone.
|
|
|
51
51
|
|
|
52
52
|
`division.json` exercises the public `divide` seam separately from the
|
|
53
53
|
validation Libraries. It carries `schema` and `cases` like `projection.json`,
|
|
54
|
-
and each case adds the `
|
|
55
|
-
`divide(document, file, context)` with `{ documentTargets, definition,
|
|
54
|
+
and each case adds the `bound` the division runs under. Parse the source, call
|
|
55
|
+
`divide(document, file, context)` with `{ documentTargets, definition, bound }`,
|
|
56
56
|
and compare against `expected`: either `{ "ok": true, "spans": [...] }`, where
|
|
57
57
|
each span carries the exact `text` to embed and the exact `source` the returned
|
|
58
58
|
`start` and `end` address in that case's source, or `{ "ok": false, "refusal": ... }`. Then
|
|
@@ -107,6 +107,7 @@ schema text. The model's own rules:
|
|
|
107
107
|
| `frontmatter-fence` | A present frontmatter fence does not parse as the accepted YAML subset |
|
|
108
108
|
| `schema-yaml` | A schema text is not a mapping in the accepted YAML subset |
|
|
109
109
|
| `schema-type-name` | A type name is empty, starts with `$`, or is `any` or `attachment` |
|
|
110
|
+
| `provided-type` | A schema names a type its caller provides with a key but its two role keywords |
|
|
110
111
|
| `schema` | A type or subschema is neither a mapping nor a boolean |
|
|
111
112
|
| `schema-keyword` | A schema carries a key that is no JSON Schema 2020-12 keyword the model admits |
|
|
112
113
|
| `$defs` | `$defs` is not a mapping |
|
|
@@ -117,14 +118,18 @@ schema text. The model's own rules:
|
|
|
117
118
|
| `unknown-type` | A file declares a type absent from the governing schema |
|
|
118
119
|
| `undeclared-field` | A closed object carries a key it does not declare |
|
|
119
120
|
| `required-section` | A required section heading is missing |
|
|
121
|
+
| `conditional-section` | A section its type's `if` branch requires is absent or holds no text |
|
|
120
122
|
| `unresolved-reference` | A reference at any depth or a section item reaches no member it admits |
|
|
121
123
|
| `unresolved-link` | A body link is an absolute path or leaves the collection |
|
|
122
124
|
| `structured-section` | A declared list section is not a list of values of its item schema |
|
|
123
125
|
| `referenced-missing-file` | Remaining files still reference an absent path |
|
|
124
126
|
| `unwritable-link` | No Markdown destination form can carry a new link destination |
|
|
125
|
-
| `division-
|
|
127
|
+
| `division-bound` | A division bound is not a whole number of at least 2 |
|
|
126
128
|
|
|
127
|
-
`
|
|
129
|
+
A `then` or `else` of an `if` on a type, in its `allOf` or in such a branch may hold
|
|
130
|
+
`sections`, naming sections the type declares as the type declares them, else `sections`.
|
|
131
|
+
|
|
132
|
+
`unwritable-link` is refused by `apply` and `division-bound` by `divide`, not
|
|
128
133
|
by `validate`, so no case under `cases/` carries either.
|
|
129
134
|
|
|
130
135
|
A reference that admits `attachment` reaches any path in the same Library, so
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "binaryTargets": [] }
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
question:
|
|
2
|
+
properties:
|
|
3
|
+
title: { type: string }
|
|
4
|
+
status: { enum: [open, answered, withdrawn] }
|
|
5
|
+
required: [title, status]
|
|
6
|
+
sections:
|
|
7
|
+
Question: { kind: text, required: true }
|
|
8
|
+
Answer: { kind: text }
|
|
9
|
+
if:
|
|
10
|
+
properties:
|
|
11
|
+
status: { enum: [answered, withdrawn] }
|
|
12
|
+
then:
|
|
13
|
+
sections:
|
|
14
|
+
Answer: { kind: text, required: true }
|