@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.
Files changed (45) hide show
  1. package/API.md +85 -75
  2. package/README.md +1 -1
  3. package/dist/blocks.d.ts.map +1 -1
  4. package/dist/blocks.js +29 -7
  5. package/dist/condition.d.ts +23 -9
  6. package/dist/condition.d.ts.map +1 -1
  7. package/dist/condition.js +202 -86
  8. package/dist/definition.d.ts +2 -1
  9. package/dist/definition.d.ts.map +1 -1
  10. package/dist/definition.js +96 -20
  11. package/dist/divide.js +9 -9
  12. package/dist/document.d.ts +4 -4
  13. package/dist/document.d.ts.map +1 -1
  14. package/dist/document.js +38 -38
  15. package/dist/index.d.ts +7 -6
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +4 -4
  18. package/dist/links.d.ts +1 -0
  19. package/dist/links.d.ts.map +1 -1
  20. package/dist/links.js +85 -39
  21. package/dist/paths.d.ts.map +1 -1
  22. package/dist/paths.js +4 -1
  23. package/dist/pattern.d.ts +10 -0
  24. package/dist/pattern.d.ts.map +1 -0
  25. package/dist/pattern.js +355 -0
  26. package/dist/project.d.ts.map +1 -1
  27. package/dist/project.js +3 -5
  28. package/dist/schema.d.ts +1 -0
  29. package/dist/schema.d.ts.map +1 -1
  30. package/dist/schema.js +55 -4
  31. package/dist/types.d.ts +9 -4
  32. package/dist/types.d.ts.map +1 -1
  33. package/dist/validate.d.ts +25 -4
  34. package/dist/validate.d.ts.map +1 -1
  35. package/dist/validate.js +163 -65
  36. package/dist/yaml.d.ts.map +1 -1
  37. package/dist/yaml.js +27 -1
  38. package/fixtures/README.md +9 -4
  39. package/fixtures/cases/conditional-section/context.json +1 -0
  40. package/fixtures/cases/conditional-section/expected.json +6 -0
  41. package/fixtures/cases/conditional-section/library/questions/hosting.md +9 -0
  42. package/fixtures/cases/conditional-section/library/questions/payments.md +12 -0
  43. package/fixtures/cases/conditional-section/schema.yaml +14 -0
  44. package/fixtures/division.json +26 -26
  45. package/package.json +7 -2
package/dist/condition.js CHANGED
@@ -1,13 +1,26 @@
1
- import { ARRAY_INDEX, escapePointer, fieldFacts, itemFacts, memberFacts, pointerSegments, } from './definition.js';
1
+ import { ARRAY_INDEX, escapePointer, fieldFacts, itemFacts, memberFacts, pointerFacts, pointerSegments, } from './definition.js';
2
2
  import { roomPath, target } from './paths.js';
3
- import { isRecord, plain } from './yaml.js';
4
- export const WRITE_DOORS = ['browser', 'agent', 'client'];
3
+ import { plain } from './yaml.js';
4
+ export const CONDITION_OPERATORS = [
5
+ 'eq',
6
+ 'in',
7
+ 'exists',
8
+ 'contains',
9
+ 'gt',
10
+ 'gte',
11
+ 'lt',
12
+ 'lte',
13
+ 'prefix',
14
+ ];
15
+ export const TEXT_FIELD = '/text';
16
+ export const WRITE_DOORS = ['browser', 'agent', 'client', 'function'];
5
17
  const PROSE = { kind: 'prose' };
6
18
  const NULL = { kind: 'null' };
7
19
  const UNREADABLE = { kind: 'unreadable' };
8
20
  const UNREAD = { present: undefined, values: [undefined], items: [undefined] };
9
21
  const NO_ITEM = { present: false, values: [], items: [] };
10
22
  const DATE = /^\d{4}-\d{2}-\d{2}$/u;
23
+ const HEADER = /^\/headers\/[!#$%&'*+.^_`|~0-9a-z-]+$/u;
11
24
  const DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(?:[Zz]|([+-])(\d{2}):(\d{2}))$/u;
12
25
  export function factKind(facts) {
13
26
  if (facts.reference !== undefined)
@@ -71,8 +84,7 @@ function factOf(value, declared, place) {
71
84
  return UNREADABLE;
72
85
  if (kind !== 'reference')
73
86
  return { kind, value };
74
- const reached = target(place.path, value) ??
75
- (declared.crossRoom === true ? roomPath(value) : null);
87
+ const reached = target(place.path, value) ?? roomPath(value);
76
88
  const resolved = reached === null ? undefined : place.resolve(reached);
77
89
  if (resolved === undefined)
78
90
  return UNREADABLE;
@@ -182,10 +194,9 @@ function documentRead({ reached, several, missing }, held) {
182
194
  };
183
195
  }
184
196
  function readDocument(facts, path) {
185
- if (path === '/path')
186
- return single({ kind: 'path', value: facts.path });
187
- if (path === '/type')
188
- return facts.type === null ? undefined : single({ kind: 'string', value: facts.type });
197
+ const own = tested(DOCUMENT_TESTS, path);
198
+ if (own !== undefined)
199
+ return own.read(facts, path);
189
200
  if (path.startsWith('/sections/'))
190
201
  return facts.sections.includes(path.slice('/sections/'.length))
191
202
  ? single({ kind: 'prose' })
@@ -196,22 +207,147 @@ function readDocument(facts, path) {
196
207
  return documentRead(walked(rootOf(facts, '/members'), path.slice('/members'.length)), memberHeld);
197
208
  return undefined;
198
209
  }
199
- function readActor(actor, path) {
200
- const value = path === '/user'
201
- ? actor.user
202
- : path === '/site'
203
- ? actor.site
204
- : path === '/door'
205
- ? actor.door
206
- : undefined;
207
- return value === undefined ? undefined : single({ kind: 'actor', value });
208
- }
209
210
  export function datePointers(fields) {
210
211
  return Object.entries(fields)
211
212
  .filter(([, facts]) => facts.type === 'string' && facts.format === 'date')
212
- .map(([name]) => `/fields/${name}`)
213
+ .map(([name]) => `/fields/${escapePointer(name)}`)
213
214
  .sort();
214
215
  }
216
+ export function valuedChange(definition, field) {
217
+ if (!field.startsWith('/fields/'))
218
+ return false;
219
+ const reached = pointerFacts(definition, field.slice('/fields'.length));
220
+ return (reached !== undefined &&
221
+ !reached.through &&
222
+ reached.facts.type !== 'array' &&
223
+ !reached.facts.prose &&
224
+ factKind(reached.facts) !== null);
225
+ }
226
+ export function changeFields(definition) {
227
+ return [
228
+ ...Object.keys(definition.fields).map((name) => `/fields/${escapePointer(name)}`),
229
+ ...Object.keys(definition.sections).map((heading) => `/sections/${heading}`),
230
+ ];
231
+ }
232
+ export function memberPointers(definition, memberType) {
233
+ return Object.entries(definition.fields)
234
+ .filter(([, facts]) => (facts.type === 'array' ? facts.items : facts)?.reference?.includes(memberType) === true)
235
+ .map(([name]) => `/members/${escapePointer(name)}`);
236
+ }
237
+ function kindOperators(kind) {
238
+ switch (kind) {
239
+ case 'string':
240
+ return ['eq', 'in', 'prefix', 'exists'];
241
+ case 'boolean':
242
+ return ['eq', 'exists'];
243
+ case 'number':
244
+ case 'date':
245
+ case 'time':
246
+ return ['eq', 'in', 'gt', 'gte', 'lt', 'lte', 'exists'];
247
+ default:
248
+ return ['eq', 'in', 'exists'];
249
+ }
250
+ }
251
+ function pointerOperators({ facts, through }) {
252
+ const scalar = facts.type === 'array' ? facts.items : facts;
253
+ const kind = scalar === undefined || scalar.prose ? null : factKind(scalar);
254
+ if (kind === null)
255
+ return ['exists'];
256
+ if (facts.type === 'array')
257
+ return ['contains', 'exists'];
258
+ const own = kindOperators(kind);
259
+ return through ? [...own.filter((operator) => operator !== 'exists'), 'contains', 'exists'] : own;
260
+ }
261
+ function typeOperators(definition, path, memberType) {
262
+ if (path.startsWith('/sections/'))
263
+ return Object.hasOwn(definition.sections, path.slice('/sections/'.length)) ? ['exists'] : [];
264
+ const pointer = path.startsWith('/fields/')
265
+ ? path.slice('/fields'.length)
266
+ : memberType !== null && memberPointers(definition, memberType).includes(path)
267
+ ? path.slice('/members'.length)
268
+ : undefined;
269
+ const reached = pointer === undefined ? undefined : pointerFacts(definition, pointer);
270
+ return reached === undefined ? [] : pointerOperators(reached);
271
+ }
272
+ function envelope(path, kind, value) {
273
+ return {
274
+ path,
275
+ names: (named) => named === path,
276
+ kind,
277
+ operators: kindOperators(kind),
278
+ read: (facts) => {
279
+ const held = value(facts);
280
+ return held === null || held === undefined ? undefined : single({ kind, value: held });
281
+ },
282
+ };
283
+ }
284
+ function tested(tests, path) {
285
+ return tests.find(({ names }) => names(path));
286
+ }
287
+ function reader(tests, facts) {
288
+ return (path) => tested(tests, path)?.read(facts, path);
289
+ }
290
+ const DOCUMENT_TESTS = [
291
+ {
292
+ path: '/path',
293
+ names: (named) => named === '/path',
294
+ kind: 'string',
295
+ operators: ['eq', 'in', 'prefix'],
296
+ read: (facts) => single({ kind: 'string', value: facts.path }),
297
+ },
298
+ envelope('/type', 'enum', (facts) => facts.type),
299
+ ];
300
+ const ACTOR_TESTS = [
301
+ envelope('/user', 'enum', (actor) => actor.user),
302
+ envelope('/site', 'enum', (actor) => actor.site),
303
+ envelope('/app', 'enum', (actor) => actor.app),
304
+ envelope('/door', 'enum', (actor) => actor.door),
305
+ ];
306
+ const ITEM_TESTS = [
307
+ envelope('/delivery', 'enum', (item) => item.delivery),
308
+ envelope('/source', 'enum', (item) => item.source),
309
+ envelope('/container', 'string', (item) => item.container),
310
+ envelope('/author', 'string', (item) => item.author),
311
+ {
312
+ path: '/headers/<lowercase name>',
313
+ names: (named) => HEADER.test(named),
314
+ kind: 'string',
315
+ operators: kindOperators('string'),
316
+ read: (item, path) => {
317
+ const name = path.slice('/headers/'.length);
318
+ return Object.hasOwn(item.headers, name)
319
+ ? single({ kind: 'string', value: item.headers[name] })
320
+ : undefined;
321
+ },
322
+ },
323
+ {
324
+ path: '/json/<JSON Pointer>',
325
+ names: (named) => named.startsWith('/json/'),
326
+ kind: null,
327
+ operators: CONDITION_OPERATORS,
328
+ read: (item, path) => item.json === null ? UNREAD : jsonRead(pointed(item.json.value, path.slice('/json'.length))),
329
+ },
330
+ ];
331
+ export const DOCUMENT_PATHS = DOCUMENT_TESTS.map(({ path }) => path);
332
+ export const ACTOR_PATHS = ACTOR_TESTS.map(({ path }) => path);
333
+ export const ITEM_PATHS = ITEM_TESTS.map(({ path }) => path);
334
+ export function testOperators(definitions, path, memberType) {
335
+ const own = tested(DOCUMENT_TESTS, path);
336
+ if (own !== undefined)
337
+ return own.operators;
338
+ return [
339
+ ...new Set(definitions.flatMap((definition) => typeOperators(definition, path, memberType))),
340
+ ];
341
+ }
342
+ export function actorOperators(path) {
343
+ return tested(ACTOR_TESTS, path)?.operators ?? [];
344
+ }
345
+ export function itemOperators(path) {
346
+ return tested(ITEM_TESTS, path)?.operators ?? [];
347
+ }
348
+ export function itemKind(path) {
349
+ return tested(ITEM_TESTS, path)?.kind ?? null;
350
+ }
215
351
  export function dated(value) {
216
352
  return (typeof value === 'string' && DATE.test(value) && instant(`${value}T00:00:00Z`) !== undefined);
217
353
  }
@@ -240,11 +376,8 @@ export function instant(value) {
240
376
  return (BigInt(at.getTime()) - BigInt(offset) * 60000n) * 1000000n + BigInt(fraction);
241
377
  }
242
378
  export function pointed(value, pointer) {
243
- if (pointer === '')
244
- return value;
245
379
  let reached = value;
246
- for (const part of pointer.slice(1).split('/')) {
247
- const token = part.replaceAll('~1', '/').replaceAll('~0', '~');
380
+ for (const token of pointerSegments(pointer)) {
248
381
  if (Array.isArray(reached)) {
249
382
  if (!ARRAY_INDEX.test(token))
250
383
  return undefined;
@@ -283,44 +416,15 @@ function jsonRead(value) {
283
416
  return single({ kind: 'prose' });
284
417
  return { present: true, values: [], items: value.filter(scalar).map(jsonHeld), loose: true };
285
418
  }
286
- function readItem(item, path) {
287
- if (path.startsWith('/json/')) {
288
- if (item.json === null)
289
- return UNREAD;
290
- return jsonRead(pointed(item.json.value, path.slice('/json'.length)));
291
- }
292
- if (path.startsWith('/headers/')) {
293
- const name = path.slice('/headers/'.length);
294
- return Object.hasOwn(item.headers, name)
295
- ? single({ kind: 'string', value: item.headers[name] })
296
- : undefined;
297
- }
298
- if (path === '/has_attachment')
299
- return single({ kind: 'boolean', value: item.has_attachment });
300
- const value = path === '/delivery'
301
- ? item.delivery
302
- : path === '/source'
303
- ? item.source
304
- : path === '/container'
305
- ? item.container
306
- : path === '/author'
307
- ? item.author
308
- : null;
309
- return value === null ? undefined : single({ kind: 'string', value });
310
- }
311
- function wanted(value, owner) {
312
- return isRecord(value) && value.owner === true ? owner : value;
313
- }
314
- function equal(held, value, owner) {
419
+ function equal(held, value) {
315
420
  if (!('value' in held))
316
421
  return undefined;
317
- const expected = wanted(value, owner);
318
- if (typeof expected !== typeof held.value)
422
+ if (typeof value !== typeof held.value)
319
423
  return undefined;
320
424
  if (held.kind !== 'time')
321
- return expected === held.value;
425
+ return value === held.value;
322
426
  const left = instant(held.value);
323
- const right = instant(expected);
427
+ const right = instant(value);
324
428
  return left === undefined || right === undefined ? undefined : left === right;
325
429
  }
326
430
  function sign(left, right) {
@@ -357,11 +461,11 @@ function allOf(truths) {
357
461
  return false;
358
462
  return truths.some((truth) => truth === undefined) ? undefined : true;
359
463
  }
360
- function compared(test, held, owner) {
464
+ function compared(test, held) {
361
465
  if ('eq' in test)
362
- return equal(held, test.eq, owner);
466
+ return equal(held, test.eq);
363
467
  if ('in' in test)
364
- return anyOf(test.in.map((value) => equal(held, value, owner)));
468
+ return anyOf(test.in.map((value) => equal(held, value)));
365
469
  if ('prefix' in test) {
366
470
  if (!('value' in held) || typeof held.value !== 'string')
367
471
  return undefined;
@@ -377,7 +481,7 @@ function compared(test, held, owner) {
377
481
  return ordered(held, test.lt, (sign) => sign < 0);
378
482
  return ordered(held, test.lte, (sign) => sign <= 0);
379
483
  }
380
- function leaf(test, read, owner) {
484
+ function leaf(test, read) {
381
485
  if ('exists' in test) {
382
486
  const present = read === undefined ? false : read.present;
383
487
  return present === undefined ? undefined : present === test.exists;
@@ -387,26 +491,37 @@ function leaf(test, read, owner) {
387
491
  if ('contains' in test) {
388
492
  if (read.items === null)
389
493
  return undefined;
390
- const truths = read.items.map((item) => item === undefined ? undefined : equal(item, test.contains, owner));
494
+ const truths = read.items.map((item) => item === undefined ? undefined : equal(item, test.contains));
391
495
  return read.loose === true && !truths.includes(true) ? false : anyOf(truths);
392
496
  }
393
497
  if (read.values.length === 0)
394
498
  return read === NO_ITEM ? false : undefined;
395
- return anyOf(read.values.map((value) => (value === undefined ? undefined : compared(test, value, owner))));
499
+ return anyOf(read.values.map((value) => (value === undefined ? undefined : compared(test, value))));
396
500
  }
397
- function evaluated(test, read, owner) {
501
+ function evaluated(test, read) {
398
502
  if ('all' in test)
399
- return allOf(test.all.map((part) => evaluated(part, read, owner)));
503
+ return allOf(test.all.map((part) => evaluated(part, read)));
400
504
  if ('any' in test)
401
- return anyOf(test.any.map((part) => evaluated(part, read, owner)));
505
+ return anyOf(test.any.map((part) => evaluated(part, read)));
402
506
  if ('not' in test) {
403
- const truth = evaluated(test.not, read, owner);
507
+ const truth = evaluated(test.not, read);
404
508
  return truth === undefined ? undefined : !truth;
405
509
  }
406
- return leaf(test, read(test.path), owner);
510
+ return leaf(test, read(test.path));
407
511
  }
408
- export function holds(test, document, owner) {
409
- return evaluated(test, (path) => readDocument(document, path), owner);
512
+ export function members(document, path) {
513
+ if (!path.startsWith('/members/'))
514
+ return [];
515
+ const read = readDocument(document, path);
516
+ const values = read === undefined ? [] : [...read.values, ...(read.items ?? [])];
517
+ return [
518
+ ...new Set(values.flatMap((value) => value !== undefined && value.kind === 'member' && 'value' in value
519
+ ? [String(value.value)]
520
+ : [])),
521
+ ].sort();
522
+ }
523
+ export function holds(test, document) {
524
+ return evaluated(test, (path) => readDocument(document, path));
410
525
  }
411
526
  function inScope(scope, document) {
412
527
  if (document === null)
@@ -417,10 +532,10 @@ function inScope(scope, document) {
417
532
  return document.id === scope.document;
418
533
  return scope.folder === '/' || document.path.startsWith(`${scope.folder}/`);
419
534
  }
420
- function matched(condition, test, document, owner) {
535
+ function matched(condition, test, document) {
421
536
  if (!inScope(condition.scope, document))
422
537
  return false;
423
- return test === undefined || holds(test, document, owner) === true;
538
+ return test === undefined || holds(test, document) === true;
424
539
  }
425
540
  function reachedAt(document, field) {
426
541
  if (document === null)
@@ -453,10 +568,10 @@ function valueOf(fact) {
453
568
  function signature(reached) {
454
569
  return JSON.stringify(reached.map(({ fact }) => valueOf(fact)));
455
570
  }
456
- function among(values, document, field, owner) {
457
- return holds({ path: field, in: values }, document, owner) === true;
571
+ function among(values, document, field) {
572
+ return holds({ path: field, in: values }, document) === true;
458
573
  }
459
- function fieldChanged(condition, event, owner) {
574
+ function fieldChanged(condition, event) {
460
575
  const { field, from, to } = condition;
461
576
  const before = reachedAt(event.before, field);
462
577
  const after = reachedAt(event.after, field);
@@ -470,32 +585,33 @@ function fieldChanged(condition, event, owner) {
470
585
  return false;
471
586
  if (from === 'new'
472
587
  ? event.before !== null
473
- : from !== undefined && (event.before === null || !among(from, event.before, field, owner)))
588
+ : from !== undefined && (event.before === null || !among(from, event.before, field)))
474
589
  return false;
475
590
  if (to === 'deleted'
476
591
  ? event.after !== null
477
- : to !== undefined && (event.after === null || !among(to, event.after, field, owner)))
592
+ : to !== undefined && (event.after === null || !among(to, event.after, field)))
478
593
  return false;
479
594
  return true;
480
595
  }
481
- export function fires(condition, event, owner) {
482
- if (condition.by !== undefined &&
483
- evaluated(condition.by, (path) => readActor(event.by, path), owner) !== true)
596
+ export function fires(condition, event) {
597
+ if (condition.moved_between_rooms !== undefined &&
598
+ condition.moved_between_rooms !== event.moved_between_rooms)
599
+ return false;
600
+ if (condition.by !== undefined && evaluated(condition.by, reader(ACTOR_TESTS, event.by)) !== true)
484
601
  return false;
485
602
  if (condition.mode === 'starts_matching') {
486
603
  if (event.before === null && !condition.new_documents)
487
604
  return false;
488
- return (matched(condition, condition.match, event.after, owner) &&
489
- !matched(condition, condition.match, event.before, owner));
605
+ return (matched(condition, condition.match, event.after) &&
606
+ !matched(condition, condition.match, event.before));
490
607
  }
491
608
  const side = event.after ?? event.before;
492
- return matched(condition, condition.match, side, owner) && fieldChanged(condition, event, owner);
609
+ return matched(condition, condition.match, side) && fieldChanged(condition, event);
493
610
  }
494
- export function itemFires(condition, item, owner) {
611
+ export function itemFires(condition, item) {
495
612
  if (condition.source !== undefined && item.source !== condition.source)
496
613
  return false;
497
614
  if (condition.address !== undefined && item.address !== condition.address)
498
615
  return false;
499
- return (condition.match === undefined ||
500
- evaluated(condition.match, (path) => readItem(item, path), owner) === true);
616
+ return (condition.match === undefined || evaluated(condition.match, reader(ITEM_TESTS, item)) === true);
501
617
  }
@@ -60,6 +60,7 @@ export declare const JSON_TYPES: readonly JsonType[];
60
60
  export declare const BUILT_IN_TYPE = "any";
61
61
  export declare const ATTACHMENT = "attachment";
62
62
  export declare const ARRAY_INDEX: RegExp;
63
+ export declare const JSON_POINTER: RegExp;
63
64
  export declare function escapePointer(key: string): string;
64
65
  export declare function pointerSegments(pointer: string): string[];
65
66
  export declare function refTarget(reference: string, defs: Readonly<Record<string, unknown>>): {
@@ -77,5 +78,5 @@ export type PointerFacts = {
77
78
  export declare function crossedFacts(facts: ValueFacts): ValueFacts[];
78
79
  export declare function fieldFacts(definition: Definition, key: string): ValueFacts | undefined;
79
80
  export declare function pointerFacts(definition: Definition, pointer: string): PointerFacts | undefined;
80
- export declare function readSchema(text: string): VerifiedModel | Failure;
81
+ export declare function readSchema(text: string, provided?: VerifiedModel): VerifiedModel | Failure;
81
82
  //# sourceMappingURL=definition.d.ts.map
@@ -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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"}
@@ -1,4 +1,5 @@
1
1
  import { MEMBER_FIELD, RECEIVED_FIELD, SITE_FIELD, TYPE_FIELD } from './fields.js';
2
+ import { linearPattern } from './pattern.js';
2
3
  import { equalValue, isRecord, jsValue, parseYamlMapping, YamlError } from './yaml.js';
3
4
  export const JSON_TYPES = [
4
5
  'string',
@@ -24,9 +25,13 @@ const ANNOTATIONS = new Set([
24
25
  'writeOnly',
25
26
  ]);
26
27
  const FIELD_RULES = ['change_roles', 'initial_roles', 'transitions'];
28
+ const PROVIDED_KEYWORDS = ['create_roles', 'delete_roles'];
29
+ const NO_TYPES = Object.freeze({ types: Object.freeze({}) });
27
30
  const DEFS_REFERENCE = '#/$defs/';
31
+ const UNBOUNDED_FORMAT = 'url';
28
32
  const UNRESOLVED = /[\u0000-\u001f\u007f%[\]\\^{|}]|\s$/u;
29
33
  export const ARRAY_INDEX = /^(?:0|[1-9][0-9]*)$/u;
34
+ export const JSON_POINTER = /^(?:\/(?:[^~/]|~[01])*)*$/u;
30
35
  const HOUSE_KEYS = {
31
36
  [TYPE_FIELD]: { prose: false },
32
37
  [SITE_FIELD]: { prose: false },
@@ -45,12 +50,12 @@ export function pointerSegments(pointer) {
45
50
  return pointer.split('/').slice(1).map(unescapePointer);
46
51
  }
47
52
  export function refTarget(reference, defs) {
48
- if (!reference.startsWith(DEFS_REFERENCE) || UNRESOLVED.test(reference))
49
- return undefined;
50
- const segments = reference.slice(DEFS_REFERENCE.length).split('/');
51
- const [name = '', ...rest] = segments.map(unescapePointer);
52
- if (segments.some((segment) => escapePointer(unescapePointer(segment)) !== segment))
53
+ const pointer = reference.slice('#'.length);
54
+ if (!reference.startsWith(DEFS_REFERENCE) ||
55
+ UNRESOLVED.test(reference) ||
56
+ !JSON_POINTER.test(pointer))
53
57
  return undefined;
58
+ const [, name = '', ...rest] = pointerSegments(pointer);
54
59
  if (!Object.hasOwn(defs, name))
55
60
  return undefined;
56
61
  let node = defs[name];
@@ -81,15 +86,7 @@ export function refTarget(reference, defs) {
81
86
  return { name, node };
82
87
  }
83
88
  function compiles(pattern) {
84
- if (typeof pattern !== 'string')
85
- return false;
86
- try {
87
- new RegExp(pattern, 'u');
88
- return true;
89
- }
90
- catch {
91
- return false;
92
- }
89
+ return typeof pattern === 'string' && linearPattern(pattern) !== null;
93
90
  }
94
91
  function finite(value) {
95
92
  return typeof value === 'number' && Number.isFinite(value);
@@ -199,7 +196,7 @@ const KEYWORDS = {
199
196
  uniqueItems: FLAG,
200
197
  required: strings,
201
198
  dependentRequired: (value) => isRecord(value) && Object.values(value).every(strings),
202
- format: TEXT,
199
+ format: (value) => typeof value === 'string' && value !== UNBOUNDED_FORMAT,
203
200
  contentEncoding: TEXT,
204
201
  contentMediaType: TEXT,
205
202
  title: TEXT,
@@ -447,7 +444,7 @@ export function itemFacts(facts, index) {
447
444
  export function memberFacts(facts, key) {
448
445
  if (facts.properties !== undefined && Object.hasOwn(facts.properties, key))
449
446
  return facts.properties[key];
450
- const pattern = facts.patterns?.find((one) => new RegExp(one.pattern, 'u').test(key));
447
+ const pattern = facts.patterns?.find((one) => linearPattern(one.pattern).test(key));
451
448
  return pattern === undefined ? facts.additional : pattern.facts;
452
449
  }
453
450
  export function crossedFacts(facts) {
@@ -550,6 +547,56 @@ function readSections(value, at, context) {
550
547
  }
551
548
  return { sections };
552
549
  }
550
+ function branchSectionsFailure(sections, declared, where) {
551
+ if (!isRecord(sections))
552
+ return refuse('sections', where);
553
+ for (const [name, spec] of Object.entries(sections)) {
554
+ const place = `${where}/${escapePointer(name)}`;
555
+ const own = isRecord(declared) && Object.hasOwn(declared, name) ? declared[name] : undefined;
556
+ if (!isRecord(spec) || !isRecord(own))
557
+ return refuse('sections', place);
558
+ const { required, ...kind } = spec;
559
+ const { required: _required, ...ownKind } = own;
560
+ if (required !== undefined && typeof required !== 'boolean')
561
+ return refuse('sections', `${place}/required`);
562
+ if (!equalValue(kind, ownKind))
563
+ return refuse('sections', place);
564
+ }
565
+ return undefined;
566
+ }
567
+ function withoutBranchSections(schema, declared, at) {
568
+ const kept = { ...schema };
569
+ if (Array.isArray(schema.allOf)) {
570
+ const entries = [];
571
+ for (const [index, entry] of schema.allOf.entries()) {
572
+ const read = isRecord(entry)
573
+ ? withoutBranchSections(entry, declared, `${at}/allOf/${index}`)
574
+ : { schema: entry };
575
+ if ('failure' in read)
576
+ return read;
577
+ entries.push(read.schema);
578
+ }
579
+ kept.allOf = entries;
580
+ }
581
+ if (schema.if === undefined)
582
+ return { schema: kept };
583
+ for (const branch of ['then', 'else']) {
584
+ const held = schema[branch];
585
+ if (!isRecord(held))
586
+ continue;
587
+ const { sections, ...rest } = held;
588
+ const failure = sections === undefined
589
+ ? undefined
590
+ : branchSectionsFailure(sections, declared, `${at}/${branch}/sections`);
591
+ if (failure !== undefined)
592
+ return { failure };
593
+ const read = withoutBranchSections(rest, declared, `${at}/${branch}`);
594
+ if ('failure' in read)
595
+ return read;
596
+ kept[branch] = read.schema;
597
+ }
598
+ return { schema: kept };
599
+ }
553
600
  function deepFreeze(value) {
554
601
  if (value !== null && typeof value === 'object' && !Object.isFrozen(value)) {
555
602
  Object.freeze(value);
@@ -657,6 +704,26 @@ function unruled(schema) {
657
704
  }
658
705
  return { schema: written.size === 0 ? schema : { ...schema, properties }, written };
659
706
  }
707
+ function providedType(name, value, provided) {
708
+ const at = `/${escapePointer(name)}`;
709
+ if (provided.nameable?.includes(name) !== true)
710
+ return refuse('provided-type', at);
711
+ if (!isRecord(value))
712
+ return refuse('schema', at);
713
+ const declared = Object.keys(value).find((key) => !PROVIDED_KEYWORDS.includes(key));
714
+ if (declared !== undefined)
715
+ return refuse('provided-type', `${at}/${escapePointer(declared)}`);
716
+ const { create_roles: createRoles, delete_roles: deleteRoles } = value;
717
+ if (createRoles !== undefined && !roleNames(createRoles))
718
+ return refuse('create_roles', `${at}/create_roles`);
719
+ if (deleteRoles !== undefined && !roleNames(deleteRoles))
720
+ return refuse('delete_roles', `${at}/delete_roles`);
721
+ return {
722
+ ...provided.types[name],
723
+ ...(createRoles === undefined ? {} : { createRoles }),
724
+ ...(deleteRoles === undefined ? {} : { deleteRoles }),
725
+ };
726
+ }
660
727
  function readType(name, value, context) {
661
728
  const at = `/${escapePointer(name)}`;
662
729
  if (!isRecord(value))
@@ -673,7 +740,10 @@ function readType(name, value, context) {
673
740
  : readSections(declared, `${at}/sections`, context);
674
741
  if ('failure' in read)
675
742
  return read.failure;
676
- const { schema, written } = unruled(ruled);
743
+ const unbranched = withoutBranchSections(ruled, declared, at);
744
+ if ('failure' in unbranched)
745
+ return unbranched.failure;
746
+ const { schema, written } = unruled(unbranched.schema);
677
747
  const failure = schemaFailure(schema, at, context);
678
748
  if (failure !== undefined)
679
749
  return failure;
@@ -712,7 +782,7 @@ function readType(name, value, context) {
712
782
  function lineAt(text, offset) {
713
783
  return text.slice(0, offset).split(/\r\n|\r|\n/).length;
714
784
  }
715
- export function readSchema(text) {
785
+ export function readSchema(text, provided = NO_TYPES) {
716
786
  let root;
717
787
  try {
718
788
  root = jsValue(parseYamlMapping(text, 0, text.length));
@@ -730,7 +800,7 @@ export function readSchema(text) {
730
800
  if (name === '' || name.startsWith('$') || name === BUILT_IN_TYPE || name === ATTACHMENT)
731
801
  return refuse('schema-type-name', `/${escapePointer(name)}`);
732
802
  }
733
- const context = { defs, types: new Set(names) };
803
+ const context = { defs, types: new Set([...names, ...Object.keys(provided.types)]) };
734
804
  for (const [name, def] of Object.entries(defs)) {
735
805
  const failure = schemaFailure(def, `/$defs/${escapePointer(name)}`, context);
736
806
  if (failure !== undefined)
@@ -739,7 +809,9 @@ export function readSchema(text) {
739
809
  const types = Object.create(null);
740
810
  let marked = false;
741
811
  for (const name of names) {
742
- const definition = readType(name, root[name], context);
812
+ const definition = Object.hasOwn(provided.types, name)
813
+ ? providedType(name, root[name], provided)
814
+ : readType(name, root[name], context);
743
815
  if ('rule' in definition)
744
816
  return definition;
745
817
  if (definition.memberDocument) {
@@ -749,5 +821,9 @@ export function readSchema(text) {
749
821
  }
750
822
  types[name] = deepFreeze(definition);
751
823
  }
824
+ for (const [name, definition] of Object.entries(provided.types)) {
825
+ if (!Object.hasOwn(types, name))
826
+ types[name] = definition;
827
+ }
752
828
  return Object.freeze({ types: Object.freeze(types) });
753
829
  }