@petrarca/sonnet-core 0.7.0 → 0.9.0

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.
@@ -1,11 +1,11 @@
1
- import {
2
- warnLog
3
- } from "../chunk-XAEHYRXC.js";
4
1
  import {
5
2
  FORM_ITEM_ID_FIELD,
6
3
  generateId,
7
4
  isEqual
8
5
  } from "../chunk-XCCOEUVC.js";
6
+ import {
7
+ warnLog
8
+ } from "../chunk-LOWDCP73.js";
9
9
 
10
10
  // src/schema/types.ts
11
11
  var WIDTH_TOKEN_REMS = {
@@ -41,18 +41,34 @@ function isNullableBoolean(property) {
41
41
  const hasNull = property.anyOf.some((s) => s.type === "null");
42
42
  return hasBoolean && hasNull;
43
43
  }
44
- function extractEnumValues(property) {
45
- if (Array.isArray(property.enum) && property.enum.length > 0) {
46
- return property.enum;
44
+ var LIFTABLE_TYPES = /* @__PURE__ */ new Set([
45
+ "string",
46
+ "number",
47
+ "integer",
48
+ "boolean",
49
+ "array"
50
+ ]);
51
+ function liftNullableMember(property) {
52
+ const members = property.anyOf;
53
+ if (!Array.isArray(members) || members.length !== 2) return property;
54
+ const nonNull = members.filter((m) => m.type !== "null");
55
+ if (nonNull.length !== 1) return property;
56
+ const [member] = nonNull;
57
+ if (typeof member.type !== "string" || !LIFTABLE_TYPES.has(member.type)) {
58
+ return property;
47
59
  }
48
- if (Array.isArray(property.anyOf)) {
49
- for (const branch of property.anyOf) {
50
- if (Array.isArray(branch.enum) && branch.enum.length > 0) {
51
- return branch.enum;
52
- }
53
- }
54
- }
55
- return [];
60
+ return { ...member, ...property };
61
+ }
62
+ function extractEnumValues(property) {
63
+ const candidates = [
64
+ property.enum,
65
+ property.type === "array" ? property.items?.enum : void 0,
66
+ ...(property.anyOf ?? []).map((branch) => branch.enum)
67
+ ];
68
+ return candidates.find(isNonEmptyEnum) ?? [];
69
+ }
70
+ function isNonEmptyEnum(values) {
71
+ return Array.isArray(values) && values.length > 0;
56
72
  }
57
73
  function getFieldTitle(fieldName, property) {
58
74
  return property["x-ui-title"] || property.title || fieldName;
@@ -147,9 +163,31 @@ var ajv = new Ajv({
147
163
  strict: false,
148
164
  validateFormats: true,
149
165
  // Allow unknown keywords (for x-ui-widget, x-ui-title, etc.)
150
- strictSchema: false
166
+ strictSchema: false,
167
+ // Enable $data references so a keyword can reference another field's value,
168
+ // e.g. { "const": { "$data": "1/password" } } for "must equal password".
169
+ // This is the declarative path for simple cross-field rules (see the
170
+ // custom-validation design); complex cross-field logic uses form validators.
171
+ $data: true
151
172
  });
152
173
  addFormats(ajv);
174
+ var TIME_OF_DAY = /^([01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d)(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})?$/i;
175
+ function secondsOf(time) {
176
+ const m = TIME_OF_DAY.exec(time);
177
+ if (!m) return void 0;
178
+ return Number(m[1]) * 3600 + Number(m[2]) * 60 + Number(m[3] ?? 0);
179
+ }
180
+ ajv.addFormat("time", {
181
+ type: "string",
182
+ validate: (time) => TIME_OF_DAY.test(time),
183
+ // What formatMinimum / formatMaximum compare by.
184
+ compare: (a, b) => {
185
+ const x = secondsOf(a);
186
+ const y = secondsOf(b);
187
+ if (x === void 0 || y === void 0) return void 0;
188
+ return x === y ? 0 : x < y ? -1 : 1;
189
+ }
190
+ });
153
191
  function resolveSchema(schema, formData) {
154
192
  if (!schema || typeof schema !== "object") {
155
193
  return schema;
@@ -335,25 +373,26 @@ function evaluateUiRule(rule, formData) {
335
373
  }
336
374
 
337
375
  // src/schema/schemaUtils.ts
376
+ var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
377
+ var isGroup = (p) => !!p.properties && p.default === void 0;
378
+ function groupFilled(value, property) {
379
+ if (!isGroup(property) || value !== void 0 && !isPlainObject(value))
380
+ return void 0;
381
+ const filled = applySchemaDefaults(value ?? {}, property);
382
+ return value !== void 0 || Object.keys(filled).length > 0 ? filled : void 0;
383
+ }
338
384
  function applySchemaDefaults(data, schema) {
339
385
  const result = { ...data };
340
386
  const properties = schema.properties ?? {};
341
387
  for (const [fieldName, property] of Object.entries(properties)) {
342
- if (result[fieldName] === void 0 && property.default !== void 0) {
343
- result[fieldName] = property.default;
344
- }
388
+ const value = result[fieldName];
389
+ const filled = value === void 0 && property.default !== void 0 ? property.default : groupFilled(value, property);
390
+ if (filled !== void 0) result[fieldName] = filled;
345
391
  }
346
392
  return result;
347
393
  }
348
394
  function extractSchemaDefaults(schema) {
349
- const defaults = {};
350
- const properties = schema.properties ?? {};
351
- for (const [fieldName, property] of Object.entries(properties)) {
352
- if (property.default !== void 0) {
353
- defaults[fieldName] = property.default;
354
- }
355
- }
356
- return defaults;
395
+ return applySchemaDefaults({}, schema);
357
396
  }
358
397
  function generateJsonSchemaFromVariables(variables, existingSchema) {
359
398
  const existingProperties = existingSchema?.properties ?? {};
@@ -386,6 +425,268 @@ function toJsonSchema(schema) {
386
425
  });
387
426
  }
388
427
 
428
+ // src/schema/coreTypes.ts
429
+ var CORE_TYPES_BASE = "https://schemas.petrarca-labs.com/core/";
430
+ var QUANTITY_TYPE_ID = `${CORE_TYPES_BASE}quantity/v1`;
431
+ var UCUM_SYSTEM = "http://unitsofmeasure.org";
432
+ var QUANTITY_DEFINITION = {
433
+ type: "object",
434
+ title: "Quantity",
435
+ properties: {
436
+ value: { type: "number", title: "Value" },
437
+ unit: { type: "string", title: "Unit", minLength: 1 },
438
+ system: { type: "string", title: "Unit system", format: "uri" },
439
+ comparator: {
440
+ type: "string",
441
+ title: "Comparator",
442
+ enum: ["<", "<=", ">=", ">"]
443
+ }
444
+ },
445
+ required: ["value", "unit"],
446
+ additionalProperties: false,
447
+ // Shown as a quantity wherever it is used: a number and its unit.
448
+ "x-ui-widget": "quantity"
449
+ };
450
+ var QUANTITY_TYPE = {
451
+ id: QUANTITY_TYPE_ID,
452
+ label: "Quantity",
453
+ definition: QUANTITY_DEFINITION
454
+ };
455
+ var CODING_TYPE_ID = `${CORE_TYPES_BASE}coding/v1`;
456
+ function codingKey(c) {
457
+ return `${c.system ?? ""}|${c.code}`;
458
+ }
459
+ var CODING_DEFINITION = {
460
+ type: "object",
461
+ title: "Coded value",
462
+ properties: {
463
+ system: { type: "string", title: "Code system", minLength: 1 },
464
+ code: { type: "string", title: "Code", minLength: 1 },
465
+ version: { type: "string", title: "Version" },
466
+ display: { type: "string", title: "Label" }
467
+ },
468
+ required: ["system", "code"],
469
+ // Shown as a coded value wherever it is used: searched in its list.
470
+ "x-ui-widget": "coded-value"
471
+ };
472
+ var CODING_TYPE = {
473
+ id: CODING_TYPE_ID,
474
+ label: "Coded value",
475
+ definition: CODING_DEFINITION
476
+ };
477
+ var CODEABLE_CONCEPT_TYPE_ID = `${CORE_TYPES_BASE}codeable-concept/v1`;
478
+ var { "x-ui-widget": _codingWidget, ...CODING_FIELDS } = CODING_DEFINITION;
479
+ var CODEABLE_CONCEPT_DEFINITION = {
480
+ type: "object",
481
+ title: "Codeable concept",
482
+ properties: {
483
+ codings: {
484
+ type: "array",
485
+ title: "Codings",
486
+ items: {
487
+ ...CODING_FIELDS,
488
+ title: "Coding",
489
+ properties: {
490
+ ...CODING_FIELDS.properties,
491
+ userSelected: { type: "boolean", title: "Picked by a person" },
492
+ match: {
493
+ type: "string",
494
+ title: "Match",
495
+ enum: ["equivalent", "broader", "narrower"]
496
+ },
497
+ purpose: { ...CODING_FIELDS, title: "Purpose" }
498
+ }
499
+ }
500
+ },
501
+ text: { type: "string", title: "Text" }
502
+ },
503
+ // Shown as a codeable concept wherever it is used: a coded value per list.
504
+ "x-ui-widget": "codeable-concept"
505
+ };
506
+ var CODEABLE_CONCEPT_TYPE = {
507
+ id: CODEABLE_CONCEPT_TYPE_ID,
508
+ label: "Codeable concept",
509
+ definition: CODEABLE_CONCEPT_DEFINITION
510
+ };
511
+ var CORE_VALUE_TYPES = [
512
+ QUANTITY_TYPE,
513
+ CODING_TYPE,
514
+ CODEABLE_CONCEPT_TYPE
515
+ ];
516
+ function valueTypeResolver(types) {
517
+ const byId = new Map(types.map((t) => [t.id, t.definition]));
518
+ return (ref) => byId.get(ref);
519
+ }
520
+ var resolveCore = valueTypeResolver(CORE_VALUE_TYPES);
521
+ function coreType(ref) {
522
+ return resolveCore(ref);
523
+ }
524
+ function coreTypeOf(prop) {
525
+ return prop.$ref && resolveCore(prop.$ref) ? prop.$ref : void 0;
526
+ }
527
+
528
+ // src/schema/binding.ts
529
+ function isAdditional(a) {
530
+ if (!a || typeof a !== "object") return false;
531
+ const { valueSet, system } = a;
532
+ return !!(valueSet || system);
533
+ }
534
+ function bindingOf(prop) {
535
+ const binding = prop["x-binding"];
536
+ if (!binding || typeof binding !== "object") return void 0;
537
+ const { valueSet, system, strength, additional, ...rest } = binding;
538
+ if (!valueSet && !system) return void 0;
539
+ const lists = Array.isArray(additional) ? additional.filter(isAdditional) : [];
540
+ return {
541
+ ...rest,
542
+ ...valueSet ? { valueSet } : {},
543
+ ...system ? { system } : {},
544
+ strength: strength === "extensible" ? "extensible" : "required",
545
+ ...lists.length ? { additional: lists } : {}
546
+ };
547
+ }
548
+ function bindingLists(binding) {
549
+ const { valueSet, system, version, title, additional = [] } = binding;
550
+ const first = {
551
+ ...valueSet ? { valueSet } : {},
552
+ ...system ? { system } : {},
553
+ ...version ? { version } : {},
554
+ ...title ? { title } : {}
555
+ };
556
+ return [first, ...additional];
557
+ }
558
+ var BAR = "|";
559
+ function parseCodeToken(token, bound) {
560
+ const bar = token.lastIndexOf(BAR);
561
+ if (bar < 0) return { ...bound ? { system: bound } : {}, code: token };
562
+ return { system: token.slice(0, bar), code: token.slice(bar + 1) };
563
+ }
564
+ function codeToken(coding, bound) {
565
+ return coding.system && coding.system !== bound ? `${coding.system}${BAR}${coding.code}` : coding.code;
566
+ }
567
+
568
+ // src/schema/codeableConcept.ts
569
+ function conceptOf(value) {
570
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
571
+ }
572
+ function codingsByList(value, lists) {
573
+ const codings = conceptOf(value).codings ?? [];
574
+ const free = new Set(codings.map((_, i) => i));
575
+ const claim = (pick) => {
576
+ const i = [...free].find((j) => pick(codings[j]));
577
+ if (i === void 0) return void 0;
578
+ free.delete(i);
579
+ return codings[i];
580
+ };
581
+ const bySystem = lists.map(
582
+ (l) => l.system ? claim((c) => c.system === l.system) : void 0
583
+ );
584
+ const byList = lists.map(
585
+ (l, i) => l.system ? bySystem[i] : claim(() => true)
586
+ );
587
+ return { byList, others: [...free].map((i) => codings[i]) };
588
+ }
589
+ function listTitle(list) {
590
+ if (list.title) return list.title;
591
+ if (list.purpose) return list.purpose.display ?? list.purpose.code;
592
+ const id = list.system ?? list.valueSet ?? "";
593
+ const parts = id.replace(/^[a-z]+:\/\//i, "").split(/[/#:]/).filter(Boolean);
594
+ return parts.at(-1) ?? id;
595
+ }
596
+ function missingLists(prop, value) {
597
+ const binding = bindingOf(prop);
598
+ if (!binding?.additional?.some((l) => l.required)) return [];
599
+ if (!conceptOf(value).codings?.length) return [];
600
+ const lists = bindingLists(binding);
601
+ const { byList } = codingsByList(value, lists);
602
+ return lists.filter((l, i) => l.required && !byList[i]);
603
+ }
604
+
605
+ // src/schema/valueRef.ts
606
+ function isPlainString(member) {
607
+ return Object.keys(member).length === 1 && member.type === "string";
608
+ }
609
+ function isPlainRef(member) {
610
+ return Object.keys(member).length === 1 && typeof member.$ref === "string";
611
+ }
612
+ function singleRefOf(prop) {
613
+ if (prop.$ref) return { ref: prop.$ref, orText: false };
614
+ const members = prop.anyOf;
615
+ if (!Array.isArray(members) || members.length !== 2) return void 0;
616
+ const [first, second] = members;
617
+ return isPlainRef(first) && isPlainString(second) ? { ref: first.$ref, orText: true } : void 0;
618
+ }
619
+ function itemRefOf(items) {
620
+ if (!items) return void 0;
621
+ const own = Object.keys(items);
622
+ const plain = own.length === 1 && (own[0] === "$ref" || own[0] === "anyOf");
623
+ return plain ? singleRefOf(items) : void 0;
624
+ }
625
+ function valueRefOf(prop) {
626
+ if (prop.type === "array") {
627
+ const item = itemRefOf(prop.items);
628
+ return item && { ...item, multiple: true };
629
+ }
630
+ const single = singleRefOf(prop);
631
+ return single && { ...single, multiple: false };
632
+ }
633
+ function singleKeywords(ref, orText) {
634
+ return orText ? { anyOf: [{ $ref: ref }, { type: "string" }] } : { $ref: ref };
635
+ }
636
+ function valueRefKeywords(ref, orText, multiple = false) {
637
+ const none = {
638
+ $ref: void 0,
639
+ anyOf: void 0,
640
+ type: void 0,
641
+ items: void 0
642
+ };
643
+ return multiple ? { ...none, type: "array", items: singleKeywords(ref, orText) } : { ...none, ...singleKeywords(ref, orText) };
644
+ }
645
+
646
+ // src/schema/unitRanges.ts
647
+ function rangeEntry(unit, range) {
648
+ return {
649
+ if: { properties: { unit: { const: unit } }, required: ["unit"] },
650
+ then: {
651
+ properties: {
652
+ value: {
653
+ ...range.min !== void 0 ? { minimum: range.min } : {},
654
+ ...range.max !== void 0 ? { maximum: range.max } : {}
655
+ }
656
+ }
657
+ }
658
+ };
659
+ }
660
+ var numberOrNone = (v) => v === void 0 || typeof v === "number";
661
+ function namedIn(entry) {
662
+ const value = entry.then?.properties?.value;
663
+ return {
664
+ unit: entry.if?.properties?.unit?.const,
665
+ min: value?.minimum,
666
+ max: value?.maximum
667
+ };
668
+ }
669
+ function readEntry(entry) {
670
+ const { unit, min, max } = namedIn(entry);
671
+ if (typeof unit !== "string" || !numberOrNone(min) || !numberOrNone(max))
672
+ return void 0;
673
+ const range = { min, max };
674
+ return isEqual(entry, rangeEntry(unit, range)) ? [unit, range] : void 0;
675
+ }
676
+ function unitRangesOf(prop) {
677
+ const ranges = {};
678
+ for (const entry of prop.allOf ?? []) {
679
+ const read = readEntry(entry);
680
+ if (!read) return void 0;
681
+ ranges[read[0]] = read[1];
682
+ }
683
+ return ranges;
684
+ }
685
+ function unitRangesKeywords(ranges) {
686
+ const entries = Object.entries(ranges).filter(([, r]) => r.min !== void 0 || r.max !== void 0).map(([unit, r]) => rangeEntry(unit, r));
687
+ return { allOf: entries.length ? entries : void 0 };
688
+ }
689
+
389
690
  // src/schema/resolveRefs.ts
390
691
  function extractDefName(ref) {
391
692
  const match = ref.match(/^#\/(\$defs|definitions)\/(.+)$/);
@@ -397,6 +698,17 @@ function inDocumentResolver(defs) {
397
698
  return name ? defs[name] : void 0;
398
699
  };
399
700
  }
701
+ function withOverrides(target, overrides) {
702
+ const merged = { ...target, ...overrides };
703
+ if (target.properties && overrides.properties) {
704
+ merged.properties = { ...target.properties };
705
+ for (const [key, prop] of Object.entries(overrides.properties))
706
+ merged.properties[key] = { ...target.properties[key], ...prop };
707
+ }
708
+ if (target.required && overrides.required)
709
+ merged.required = [.../* @__PURE__ */ new Set([...target.required, ...overrides.required])];
710
+ return merged;
711
+ }
400
712
  function resolveRef(node, resolve, seen) {
401
713
  const ref = node.$ref;
402
714
  const resolved = resolve(ref);
@@ -406,7 +718,7 @@ function resolveRef(node, resolve, seen) {
406
718
  const overrides = Object.fromEntries(
407
719
  Object.entries(node).filter(([k]) => k !== "$ref")
408
720
  );
409
- const merged = Object.keys(overrides).length > 0 ? { ...resolved, ...overrides } : resolved;
721
+ const merged = Object.keys(overrides).length > 0 ? withOverrides(resolved, overrides) : resolved;
410
722
  const result = resolveNode(merged, resolve, seen);
411
723
  seen.delete(ref);
412
724
  return result;
@@ -455,9 +767,8 @@ function resolveNode(node, resolve, seen) {
455
767
  }
456
768
  function resolveRefs(schema, resolve) {
457
769
  const defs = schema.$defs ?? schema.definitions ?? {};
458
- if (!resolve && Object.keys(defs).length === 0) return schema;
459
- const fallback = inDocumentResolver(defs);
460
- const resolver = resolve ? (ref) => resolve(ref) ?? fallback(ref) : fallback;
770
+ const inDocument = inDocumentResolver(defs);
771
+ const resolver = (ref) => resolve?.(ref) ?? coreType(ref) ?? inDocument(ref);
461
772
  const seen = /* @__PURE__ */ new Set();
462
773
  const resolved = resolveNode(
463
774
  schema,
@@ -467,6 +778,76 @@ function resolveRefs(schema, resolve) {
467
778
  return resolved;
468
779
  }
469
780
 
781
+ // src/schema/contentValidation.ts
782
+ var OWNED_BY_VALIDATE_FORM_DATA = /* @__PURE__ */ new Set([
783
+ "required",
784
+ "minItems",
785
+ "maxItems"
786
+ ]);
787
+ var WRAPPERS = /* @__PURE__ */ new Set(["if"]);
788
+ function unescapePointerToken(token) {
789
+ return token.replace(/~1/g, "/").replace(/~0/g, "~");
790
+ }
791
+ function labelForPointer(schema, pointer) {
792
+ if (pointer === "") return "";
793
+ const tokens = pointer.split("/").slice(1).map(unescapePointerToken);
794
+ const parts = [];
795
+ let node = schema;
796
+ for (const token of tokens) {
797
+ if (/^\d+$/.test(token)) {
798
+ if (parts.length > 0) {
799
+ parts[parts.length - 1] = `${parts[parts.length - 1]}[${Number(token) + 1}]`;
800
+ }
801
+ node = node?.items;
802
+ continue;
803
+ }
804
+ const props = node?.properties;
805
+ const child = props?.[token];
806
+ parts.push(child ? getFieldTitle(token, child) : token);
807
+ node = child;
808
+ }
809
+ return parts.join(" > ");
810
+ }
811
+ var FORMAT_RANGE = {
812
+ formatMinimum: "or later",
813
+ formatExclusiveMinimum: "later",
814
+ formatMaximum: "or earlier",
815
+ formatExclusiveMaximum: "earlier"
816
+ };
817
+ function messageFor(label, err) {
818
+ const what = label || "Value";
819
+ const range = FORMAT_RANGE[err.keyword];
820
+ const limit = err.params.limit;
821
+ if (range && limit !== void 0) {
822
+ const prefix = err.keyword.includes("Exclusive") ? `${range} than` : "";
823
+ return prefix ? `${what} must be ${prefix} ${String(limit)}` : `${what} must be ${String(limit)} ${range}`;
824
+ }
825
+ return `${what} ${err.message ?? "is invalid"}`;
826
+ }
827
+ function validateContentConstraints(schema, data) {
828
+ let validate;
829
+ try {
830
+ validate = ajv.compile(schema);
831
+ } catch {
832
+ return [];
833
+ }
834
+ if (validate(data)) return [];
835
+ const out = [];
836
+ const seen = /* @__PURE__ */ new Set();
837
+ for (const err of validate.errors ?? []) {
838
+ if (OWNED_BY_VALIDATE_FORM_DATA.has(err.keyword) || WRAPPERS.has(err.keyword))
839
+ continue;
840
+ const pointer = err.instancePath;
841
+ const label = labelForPointer(schema, pointer);
842
+ const message = messageFor(label, err);
843
+ const key = `${pointer}\0${message}`;
844
+ if (seen.has(key)) continue;
845
+ seen.add(key);
846
+ out.push({ pointer, message });
847
+ }
848
+ return out;
849
+ }
850
+
470
851
  // src/schema/validateFormData.ts
471
852
  function escapePointerToken(token) {
472
853
  return token.replace(/~/g, "~0").replace(/\//g, "~1");
@@ -474,7 +855,7 @@ function escapePointerToken(token) {
474
855
  function isEmpty(value) {
475
856
  return value === void 0 || value === null || value === "";
476
857
  }
477
- function isPlainObject(value) {
858
+ function isPlainObject2(value) {
478
859
  return value !== null && typeof value === "object" && !Array.isArray(value);
479
860
  }
480
861
  function childContext(parent, fieldName, property) {
@@ -507,7 +888,7 @@ function validateArray(property, value, ctx, out) {
507
888
  const itemProperties = property.items.properties;
508
889
  const itemSchema = property.items;
509
890
  for (let i = 0; i < value.length; i++) {
510
- if (isPlainObject(value[i])) {
891
+ if (isPlainObject2(value[i])) {
511
892
  validateObject(
512
893
  itemProperties,
513
894
  itemSchema,
@@ -519,13 +900,18 @@ function validateArray(property, value, ctx, out) {
519
900
  }
520
901
  }
521
902
  function isNestedObject(property, value) {
522
- return property.type === "object" && !!property.properties && isPlainObject(value);
903
+ return property.type === "object" && !!property.properties && isPlainObject2(value);
523
904
  }
524
905
  function validateField(property, value, required, ctx, out) {
525
906
  if (required && isEmpty(value)) {
526
907
  out.push({ pointer: ctx.pointer, message: `${ctx.label} is required` });
527
908
  return;
528
909
  }
910
+ for (const list of missingLists(property, value))
911
+ out.push({
912
+ pointer: ctx.pointer,
913
+ message: `${ctx.label} needs a code of ${listTitle(list)}`
914
+ });
529
915
  if (isNestedObject(property, value)) {
530
916
  validateObject(property.properties, property, value, ctx, out);
531
917
  return;
@@ -557,6 +943,11 @@ function validateFormData(schema, data) {
557
943
  { label: "", pointer: "" },
558
944
  fieldErrors
559
945
  );
946
+ const structurallyFlagged = new Set(fieldErrors.map((e) => e.pointer));
947
+ for (const err of validateContentConstraints(schema, data)) {
948
+ if (structurallyFlagged.has(err.pointer)) continue;
949
+ fieldErrors.push(err);
950
+ }
560
951
  return {
561
952
  errors: fieldErrors.map((e) => e.message),
562
953
  fieldErrors,
@@ -564,6 +955,34 @@ function validateFormData(schema, data) {
564
955
  };
565
956
  }
566
957
 
958
+ // src/schema/mergeFieldErrors.ts
959
+ function pointerRoot(pointer) {
960
+ const token = pointer.split("/")[1] ?? "";
961
+ return token.replace(/~1/g, "/").replace(/~0/g, "~");
962
+ }
963
+ function mergeFieldErrors(...sources) {
964
+ const out = [];
965
+ const claimedFields = /* @__PURE__ */ new Set();
966
+ const seenFormMessages = /* @__PURE__ */ new Set();
967
+ for (const source of sources) {
968
+ for (const err of source) {
969
+ if (err.pointer === "") {
970
+ if (seenFormMessages.has(err.message)) continue;
971
+ seenFormMessages.add(err.message);
972
+ out.push(err);
973
+ continue;
974
+ }
975
+ const root = pointerRoot(err.pointer);
976
+ if (claimedFields.has(root)) continue;
977
+ out.push(err);
978
+ }
979
+ for (const err of source) {
980
+ if (err.pointer !== "") claimedFields.add(pointerRoot(err.pointer));
981
+ }
982
+ }
983
+ return out;
984
+ }
985
+
567
986
  // src/schema/diffFormData.ts
568
987
  function areBothPlainObjects(a, b) {
569
988
  return a !== null && b !== null && typeof a === "object" && typeof b === "object" && !Array.isArray(a) && !Array.isArray(b);
@@ -708,14 +1127,14 @@ function diffFormData(original, updated) {
708
1127
  }
709
1128
 
710
1129
  // src/schema/ensureFormItemIds.ts
711
- function isPlainObject2(v) {
1130
+ function isPlainObject3(v) {
712
1131
  return typeof v === "object" && v !== null && !Array.isArray(v);
713
1132
  }
714
1133
  function isObjectArray2(property) {
715
1134
  return property.type === "array" && !!property.items?.properties;
716
1135
  }
717
1136
  function stampItem(item, itemSchema) {
718
- if (!isPlainObject2(item)) return item;
1137
+ if (!isPlainObject3(item)) return item;
719
1138
  const withChildren = stampObject(item, itemSchema.properties ?? {});
720
1139
  return FORM_ITEM_ID_FIELD in withChildren ? withChildren : { ...withChildren, [FORM_ITEM_ID_FIELD]: generateId() };
721
1140
  }
@@ -723,7 +1142,7 @@ function stampValue(value, property) {
723
1142
  if (isObjectArray2(property) && Array.isArray(value)) {
724
1143
  return value.map((item) => stampItem(item, property.items));
725
1144
  }
726
- if (property.type === "object" && property.properties && isPlainObject2(value)) {
1145
+ if (property.type === "object" && property.properties && isPlainObject3(value)) {
727
1146
  return stampObject(value, property.properties);
728
1147
  }
729
1148
  return value;
@@ -744,10 +1163,43 @@ function stampObject(data, properties) {
744
1163
  function ensureFormItemIds(data, schema) {
745
1164
  return stampObject(data, getSchemaProperties(schema));
746
1165
  }
1166
+ function stripFormItemIds(data) {
1167
+ const result = {};
1168
+ for (const [key, value] of Object.entries(data)) {
1169
+ if (key === FORM_ITEM_ID_FIELD) continue;
1170
+ if (Array.isArray(value)) {
1171
+ result[key] = value.map(
1172
+ (item) => isPlainObject3(item) ? stripFormItemIds(item) : item
1173
+ );
1174
+ } else if (isPlainObject3(value)) {
1175
+ result[key] = stripFormItemIds(value);
1176
+ } else {
1177
+ result[key] = value;
1178
+ }
1179
+ }
1180
+ return result;
1181
+ }
747
1182
  export {
1183
+ CODEABLE_CONCEPT_TYPE,
1184
+ CODEABLE_CONCEPT_TYPE_ID,
1185
+ CODING_TYPE,
1186
+ CODING_TYPE_ID,
1187
+ CORE_TYPES_BASE,
1188
+ CORE_VALUE_TYPES,
1189
+ QUANTITY_TYPE,
1190
+ QUANTITY_TYPE_ID,
1191
+ UCUM_SYSTEM,
748
1192
  WIDTH_TOKEN_REMS,
749
1193
  ajv,
750
1194
  applySchemaDefaults,
1195
+ bindingLists,
1196
+ bindingOf,
1197
+ codeToken,
1198
+ codingKey,
1199
+ codingsByList,
1200
+ conceptOf,
1201
+ coreType,
1202
+ coreTypeOf,
751
1203
  diffFormData,
752
1204
  ensureFormItemIds,
753
1205
  evaluateUiRule,
@@ -775,11 +1227,23 @@ export {
775
1227
  isFieldRequired,
776
1228
  isNullableBoolean,
777
1229
  isOneOf,
1230
+ liftNullableMember,
1231
+ listTitle,
778
1232
  matchesConst,
1233
+ mergeFieldErrors,
1234
+ missingLists,
1235
+ parseCodeToken,
779
1236
  resolveRefs,
780
1237
  resolveSchema,
1238
+ stripFormItemIds,
781
1239
  toJsonSchema,
1240
+ unitRangesKeywords,
1241
+ unitRangesOf,
1242
+ validateContentConstraints,
782
1243
  validateFormData,
783
- validateSchema
1244
+ validateSchema,
1245
+ valueRefKeywords,
1246
+ valueRefOf,
1247
+ valueTypeResolver
784
1248
  };
785
1249
  //# sourceMappingURL=index.js.map