@remoteoss/json-schema-form 0.5.0-dev.20230810172154 → 0.5.1-dev.20230913083845

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/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ #### 0.5.0-beta.0 (2023-09-12)
2
+
3
+ ##### Changes
4
+
5
+ - Computed Attributes ([#36](https://github.com/remoteoss/json-schema-form/pull/36)) ([80c29589](https://github.com/remoteoss/json-schema-form/commit/80c29589ac0972e0f33add70a59df15a46db1b43))
6
+ - JSON Logic Skeleton ([#35](https://github.com/remoteoss/json-schema-form/pull/35)) ([63149ae8](https://github.com/remoteoss/json-schema-form/commit/63149ae863cf1b5ad76a3b2a49c7f343e55ce07b))
7
+
8
+ #### 0.4.5-beta.0 (2023-08-31)
9
+
10
+ ##### Changes
11
+
12
+ * Allow validation of consts to support single values ([#34](https://github.com/remoteoss/json-schema-form/pull/34)) ([bf07870d](https://github.com/remoteoss/json-schema-form/commit/bf07870d407d9b9b078882a078b9e4c7928df868))
13
+
14
+ #### 0.4.4-beta.0 (2023-08-30)
15
+
16
+ ##### Chores
17
+
18
+ * **fieldset:** ignore values not matching the field type ([#44](https://github.com/remoteoss/json-schema-form/pull/44)) ([f0af54e5](https://github.com/remoteoss/json-schema-form/commit/f0af54e5d425fb78524ab150bb31629d00369a61))
19
+
1
20
  #### 0.4.3-beta.0 (2023-08-09)
2
21
 
3
22
  ##### Bug fixes
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
3
  Copyright (c) 2023 Remote Technology, Inc.
4
- NPM Package: @remoteoss/json-schema-form@0.5.0-dev.20230810172154
5
- Generated: Thu, 10 Aug 2023 17:22:35 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.5.1-dev.20230913083845
5
+ Generated: Wed, 13 Sep 2023 08:39:03 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -91,6 +91,13 @@ function convertDiskSizeFromTo(from, to) {
91
91
  return value * Math.pow(1024, units.indexOf(from.toLowerCase())) / Math.pow(1024, units.indexOf(to.toLowerCase()));
92
92
  };
93
93
  }
94
+ function containsHTML(str = "") {
95
+ return /<[a-z][\s\S]*>/i.test(str);
96
+ }
97
+ function wrapWithSpan(html, properties = {}) {
98
+ const attributes = Object.entries(properties).reduce((acc, [key, value]) => `${acc}${key}="${value}" `, "").trim();
99
+ return `<span ${attributes}>${html}</span>`;
100
+ }
94
101
  function hasProperty(object2, propertyName) {
95
102
  return Object.prototype.hasOwnProperty.call(object2, propertyName);
96
103
  }
@@ -404,6 +411,252 @@ var import_flow = __toESM(require("lodash/flow"));
404
411
  var import_noop = __toESM(require("lodash/noop"));
405
412
  var import_randexp = require("randexp");
406
413
  var import_yup = require("yup");
414
+
415
+ // src/jsonLogic.js
416
+ var import_json_logic_js = __toESM(require("json-logic-js"));
417
+ function createValidationChecker(schema) {
418
+ const scopes = /* @__PURE__ */ new Map();
419
+ function createScopes(jsonSchema, key = "root") {
420
+ const sampleEmptyObject = buildSampleEmptyObject(schema);
421
+ scopes.set(key, createValidationsScope(jsonSchema));
422
+ Object.entries(jsonSchema?.properties ?? {}).filter(([, property]) => property.type === "object" || property.type === "array").forEach(([key2, property]) => {
423
+ if (property.type === "array") {
424
+ createScopes(property.items, `${key2}[]`);
425
+ } else {
426
+ createScopes(property, key2);
427
+ }
428
+ });
429
+ validateInlineRules(jsonSchema, sampleEmptyObject);
430
+ }
431
+ createScopes(schema);
432
+ return {
433
+ scopes,
434
+ getScope(name = "root") {
435
+ return scopes.get(name);
436
+ }
437
+ };
438
+ }
439
+ function createValidationsScope(schema) {
440
+ const validationMap = /* @__PURE__ */ new Map();
441
+ const computedValuesMap = /* @__PURE__ */ new Map();
442
+ const logic = schema?.["x-jsf-logic"] ?? {
443
+ validations: {},
444
+ computedValues: {}
445
+ };
446
+ const validations = Object.entries(logic.validations ?? {});
447
+ const computedValues = Object.entries(logic.computedValues ?? {});
448
+ const sampleEmptyObject = buildSampleEmptyObject(schema);
449
+ validations.forEach(([id, validation]) => {
450
+ if (!validation.rule) {
451
+ throw Error(`[json-schema-form] json-logic error: Validation "${id}" has missing rule.`);
452
+ }
453
+ checkRuleIntegrity(validation.rule, id, sampleEmptyObject);
454
+ validationMap.set(id, validation);
455
+ });
456
+ computedValues.forEach(([id, computedValue]) => {
457
+ if (!computedValue.rule) {
458
+ throw Error(`[json-schema-form] json-logic error: Computed value "${id}" has missing rule.`);
459
+ }
460
+ checkRuleIntegrity(computedValue.rule, id, sampleEmptyObject);
461
+ computedValuesMap.set(id, computedValue);
462
+ });
463
+ function validate(rule, values) {
464
+ return import_json_logic_js.default.apply(rule, replaceUndefinedValuesWithNulls(values));
465
+ }
466
+ return {
467
+ validationMap,
468
+ computedValuesMap,
469
+ validate,
470
+ applyValidationRuleInCondition(id, values) {
471
+ const validation = validationMap.get(id);
472
+ return validate(validation.rule, values);
473
+ },
474
+ applyComputedValueInField(id, values, fieldName) {
475
+ const validation = computedValuesMap.get(id);
476
+ if (validation === void 0) {
477
+ throw Error(
478
+ `[json-schema-form] json-logic error: Computed value "${id}" doesn't exist in field "${fieldName}".`
479
+ );
480
+ }
481
+ return validate(validation.rule, values);
482
+ },
483
+ applyComputedValueRuleInCondition(id, values) {
484
+ const validation = computedValuesMap.get(id);
485
+ return validate(validation.rule, values);
486
+ }
487
+ };
488
+ }
489
+ function replaceUndefinedValuesWithNulls(values = {}) {
490
+ return Object.entries(values).reduce((prev, [key, value]) => {
491
+ return { ...prev, [key]: value === void 0 ? null : value };
492
+ }, {});
493
+ }
494
+ function yupSchemaWithCustomJSONLogic({ field, logic, config, id }) {
495
+ const { parentID = "root" } = config;
496
+ const validation = logic.getScope(parentID).validationMap.get(id);
497
+ if (validation === void 0) {
498
+ throw Error(
499
+ `[json-schema-form] json-logic error: "${field.name}" required validation "${id}" doesn't exist.`
500
+ );
501
+ }
502
+ return (yupSchema) => yupSchema.test(
503
+ `${field.name}-validation-${id}`,
504
+ validation?.errorMessage ?? "This field is invalid.",
505
+ (value, { parent }) => {
506
+ if (value === void 0 && !field.required)
507
+ return true;
508
+ return import_json_logic_js.default.apply(validation.rule, parent);
509
+ }
510
+ );
511
+ }
512
+ var HANDLEBARS_REGEX = /\{\{([^{}]+)\}\}/g;
513
+ function replaceHandlebarsTemplates({
514
+ value: toReplace,
515
+ logic,
516
+ formValues,
517
+ parentID,
518
+ name: fieldName
519
+ }) {
520
+ if (typeof toReplace === "string") {
521
+ return toReplace.replace(HANDLEBARS_REGEX, (match, key) => {
522
+ return logic.getScope(parentID).applyComputedValueInField(key.trim(), formValues, fieldName);
523
+ });
524
+ } else if (typeof toReplace === "object") {
525
+ const { value, ...rules } = toReplace;
526
+ if (Object.keys(rules).length > 1 && !value) {
527
+ throw Error("Cannot define multiple rules without a template string with key `value`.");
528
+ }
529
+ const computedTemplateValue = Object.entries(rules).reduce((prev, [key, rule]) => {
530
+ const computedValue = logic.getScope(parentID).evaluateValidation(rule, formValues);
531
+ return prev.replaceAll(`{{${key}}}`, computedValue);
532
+ }, value);
533
+ return computedTemplateValue.replace(/\{\{([^{}]+)\}\}/g, (match, key) => {
534
+ return logic.getScope(parentID).applyComputedValueInField(key.trim(), formValues, fieldName);
535
+ });
536
+ }
537
+ return toReplace;
538
+ }
539
+ function calculateComputedAttributes(fieldParams, { parentID = "root" } = {}) {
540
+ return ({ logic, isRequired, config, formValues }) => {
541
+ const { name, computedAttributes } = fieldParams;
542
+ const attributes = Object.fromEntries(
543
+ Object.entries(computedAttributes).map(handleComputedAttribute(logic, formValues, parentID, name)).filter(([, value]) => value !== null)
544
+ );
545
+ return {
546
+ ...attributes,
547
+ schema: buildYupSchema(
548
+ { ...fieldParams, ...attributes, required: isRequired },
549
+ config,
550
+ logic
551
+ )
552
+ };
553
+ };
554
+ }
555
+ function handleComputedAttribute(logic, formValues, parentID, name) {
556
+ return ([key, value]) => {
557
+ switch (key) {
558
+ case "description":
559
+ return [key, replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
560
+ case "title":
561
+ return ["label", replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
562
+ case "x-jsf-errorMessage":
563
+ return [
564
+ "errorMessage",
565
+ handleNestedObjectForComputedValues(value, formValues, parentID, logic, name)
566
+ ];
567
+ case "x-jsf-presentation": {
568
+ if (value.statement) {
569
+ return [
570
+ "statement",
571
+ handleNestedObjectForComputedValues(value.statement, formValues, parentID, logic, name)
572
+ ];
573
+ }
574
+ return [
575
+ key,
576
+ handleNestedObjectForComputedValues(value.statement, formValues, parentID, logic, name)
577
+ ];
578
+ }
579
+ case "const":
580
+ default:
581
+ return [key, logic.getScope(parentID).applyComputedValueInField(value, formValues, name)];
582
+ }
583
+ };
584
+ }
585
+ function handleNestedObjectForComputedValues(values, formValues, parentID, logic, name) {
586
+ return Object.fromEntries(
587
+ Object.entries(values).map(([key, value]) => {
588
+ return [key, replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
589
+ })
590
+ );
591
+ }
592
+ function buildSampleEmptyObject(schema = {}) {
593
+ const sample = {};
594
+ if (typeof schema !== "object" || !schema.properties) {
595
+ return schema;
596
+ }
597
+ for (const key in schema.properties) {
598
+ if (schema.properties[key].type === "object") {
599
+ sample[key] = buildSampleEmptyObject(schema.properties[key]);
600
+ } else if (schema.properties[key].type === "array") {
601
+ const itemSchema = schema.properties[key].items;
602
+ sample[key] = buildSampleEmptyObject(itemSchema);
603
+ } else {
604
+ sample[key] = true;
605
+ }
606
+ }
607
+ return sample;
608
+ }
609
+ function validateInlineRules(jsonSchema, sampleEmptyObject) {
610
+ const properties = (jsonSchema?.properties || jsonSchema?.items?.properties) ?? {};
611
+ Object.entries(properties).filter(([, property]) => property["x-jsf-logic-computedAttrs"] !== void 0).forEach(([fieldName, property]) => {
612
+ Object.entries(property["x-jsf-logic-computedAttrs"]).filter(([, value]) => typeof value === "object").forEach(([key, item]) => {
613
+ Object.values(item).forEach((rule) => {
614
+ checkRuleIntegrity(
615
+ rule,
616
+ fieldName,
617
+ sampleEmptyObject,
618
+ (item2) => `[json-schema-form] json-logic error: fieldName "${item2.var}" doesn't exist in field "${fieldName}.x-jsf-logic-computedAttrs.${key}".`
619
+ );
620
+ });
621
+ });
622
+ });
623
+ }
624
+ function checkRuleIntegrity(rule, id, data, errorMessage = (item) => `[json-schema-form] json-logic error: rule "${id}" has no variable "${item.var}".`) {
625
+ Object.entries(rule ?? {}).map(([operator, subRule]) => {
626
+ if (!Array.isArray(subRule) && subRule !== null && subRule !== void 0)
627
+ return;
628
+ throwIfUnknownOperator(operator, subRule, id);
629
+ subRule.map((item) => {
630
+ const isVar = item !== null && typeof item === "object" && Object.hasOwn(item, "var");
631
+ if (isVar) {
632
+ const exists = import_json_logic_js.default.apply({ var: removeIndicesFromPath(item.var) }, data);
633
+ if (exists === null) {
634
+ throw Error(errorMessage(item));
635
+ }
636
+ } else {
637
+ checkRuleIntegrity(item, id, data);
638
+ }
639
+ });
640
+ });
641
+ }
642
+ function throwIfUnknownOperator(operator, subRule, id) {
643
+ try {
644
+ import_json_logic_js.default.apply({ [operator]: subRule });
645
+ } catch (e) {
646
+ if (e.message === `Unrecognized operation ${operator}`) {
647
+ throw Error(
648
+ `[json-schema-form] json-logic error: in "${id}" rule there is an unknown operator "${operator}".`
649
+ );
650
+ }
651
+ }
652
+ }
653
+ var regexToGetIndices = /\.\d+\./g;
654
+ function removeIndicesFromPath(path) {
655
+ const intermediatePath = path.replace(regexToGetIndices, ".");
656
+ return intermediatePath.replace(/\.\d+$/, "");
657
+ }
658
+
659
+ // src/yupSchema.js
407
660
  var DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
408
661
  var baseString = (0, import_yup.string)().trim();
409
662
  var todayDateHint = (/* @__PURE__ */ new Date()).toISOString().substring(0, 10);
@@ -526,7 +779,7 @@ var getYupSchema = ({ inputType, ...field }) => {
526
779
  }
527
780
  return yupSchemas[inputType] || yupSchemasToJsonTypes[jsonType];
528
781
  };
529
- function buildYupSchema(field, config) {
782
+ function buildYupSchema(field, config, logic) {
530
783
  const { inputType, jsonType: jsonTypeValue, errorMessage = {}, ...propertyFields } = field;
531
784
  const isCheckboxBoolean = typeof propertyFields.checkboxValue === "boolean";
532
785
  let baseSchema;
@@ -599,6 +852,13 @@ function buildYupSchema(field, config) {
599
852
  }) : true
600
853
  );
601
854
  }
855
+ function withConst(yupSchema) {
856
+ return yupSchema.test(
857
+ "isConst",
858
+ errorMessage.const ?? errorMessageFromConfig.const ?? `The only accepted value is ${propertyFields.const}.`,
859
+ (value) => propertyFields.required === false && value === void 0 || value === null || value === propertyFields.const
860
+ );
861
+ }
602
862
  function withBaseSchema() {
603
863
  const customErrorMsg = errorMessage.type || errorMessageFromConfig.type;
604
864
  if (customErrorMsg) {
@@ -666,6 +926,14 @@ function buildYupSchema(field, config) {
666
926
  if (propertyFields.accept) {
667
927
  validators.push(withFileFormat);
668
928
  }
929
+ if (propertyFields.const) {
930
+ validators.push(withConst);
931
+ }
932
+ if (propertyFields.requiredValidations) {
933
+ propertyFields.requiredValidations.forEach(
934
+ (id) => validators.push(yupSchemaWithCustomJSONLogic({ field, id, logic, config }))
935
+ );
936
+ }
669
937
  return (0, import_flow.default)(validators);
670
938
  }
671
939
  function getNoSortEdges(fields = []) {
@@ -705,8 +973,8 @@ function hasType(type, typeName) {
705
973
  function getField(fieldName, fields) {
706
974
  return fields.find(({ name }) => name === fieldName);
707
975
  }
708
- function validateFieldSchema(field, value) {
709
- const validator = buildYupSchema(field);
976
+ function validateFieldSchema(field, value, logic) {
977
+ const validator = buildYupSchema(field, {}, logic);
710
978
  return validator().isValidSync(value);
711
979
  }
712
980
  function compareFormValueWithSchemaValue(formValue, schemaValue) {
@@ -742,7 +1010,16 @@ function getPrefillSubFieldValues(field, defaultValues, parentFieldKeyPath) {
742
1010
  initialValue[field.name] = subFieldValues;
743
1011
  }
744
1012
  } else {
745
- initialValue = getPrefillValues([field], initialValue);
1013
+ if (typeof initialValue !== "object") {
1014
+ console.warn(
1015
+ `Field "${parentFieldKeyPath}"'s value is "${initialValue}", but should be type object.`
1016
+ );
1017
+ initialValue = getPrefillValues([field], {
1018
+ // TODO nested fieldsets are not handled
1019
+ });
1020
+ } else {
1021
+ initialValue = getPrefillValues([field], initialValue);
1022
+ }
746
1023
  }
747
1024
  return initialValue;
748
1025
  }
@@ -771,7 +1048,7 @@ function getPrefillValues(fields, initialValues = {}) {
771
1048
  });
772
1049
  return initialValues;
773
1050
  }
774
- function updateField(field, requiredFields, node, formValues) {
1051
+ function updateField(field, requiredFields, node, formValues, logic, config) {
775
1052
  if (!field) {
776
1053
  return;
777
1054
  }
@@ -793,6 +1070,17 @@ function updateField(field, requiredFields, node, formValues) {
793
1070
  }
794
1071
  }
795
1072
  });
1073
+ if (field.getComputedAttributes) {
1074
+ const computedFieldValues = field.getComputedAttributes({
1075
+ field,
1076
+ isRequired: fieldIsRequired,
1077
+ node,
1078
+ formValues,
1079
+ config,
1080
+ logic
1081
+ });
1082
+ updateValues(computedFieldValues);
1083
+ }
796
1084
  if (field.calculateConditionalProperties) {
797
1085
  const newFieldValues = field.calculateConditionalProperties(fieldIsRequired, node);
798
1086
  updateValues(newFieldValues);
@@ -806,33 +1094,46 @@ function updateField(field, requiredFields, node, formValues) {
806
1094
  updateValues(newFieldValues);
807
1095
  }
808
1096
  }
809
- function processNode(node, formValues, formFields, accRequired = /* @__PURE__ */ new Set()) {
1097
+ function processNode({
1098
+ node,
1099
+ formValues,
1100
+ formFields,
1101
+ accRequired = /* @__PURE__ */ new Set(),
1102
+ parentID = "root",
1103
+ logic
1104
+ }) {
810
1105
  const requiredFields = new Set(accRequired);
811
1106
  Object.keys(node.properties ?? []).forEach((fieldName) => {
812
1107
  const field = getField(fieldName, formFields);
813
- updateField(field, requiredFields, node, formValues);
1108
+ updateField(field, requiredFields, node, formValues, logic, { parentID });
814
1109
  });
815
1110
  node.required?.forEach((fieldName) => {
816
1111
  requiredFields.add(fieldName);
817
- updateField(getField(fieldName, formFields), requiredFields, node, formValues);
1112
+ updateField(getField(fieldName, formFields), requiredFields, node, formValues, logic, {
1113
+ parentID
1114
+ });
818
1115
  });
819
1116
  if (node.if) {
820
- const matchesCondition = checkIfConditionMatches(node, formValues, formFields);
1117
+ const matchesCondition = checkIfConditionMatches(node, formValues, formFields, logic);
821
1118
  if (matchesCondition && node.then) {
822
- const { required: branchRequired } = processNode(
823
- node.then,
1119
+ const { required: branchRequired } = processNode({
1120
+ node: node.then,
824
1121
  formValues,
825
1122
  formFields,
826
- requiredFields
827
- );
1123
+ accRequired: requiredFields,
1124
+ parentID,
1125
+ logic
1126
+ });
828
1127
  branchRequired.forEach((field) => requiredFields.add(field));
829
1128
  } else if (node.else) {
830
- const { required: branchRequired } = processNode(
831
- node.else,
1129
+ const { required: branchRequired } = processNode({
1130
+ node: node.else,
832
1131
  formValues,
833
1132
  formFields,
834
- requiredFields
835
- );
1133
+ accRequired: requiredFields,
1134
+ parentID,
1135
+ logic
1136
+ });
836
1137
  branchRequired.forEach((field) => requiredFields.add(field));
837
1138
  }
838
1139
  }
@@ -844,12 +1145,21 @@ function processNode(node, formValues, formFields, accRequired = /* @__PURE__ */
844
1145
  node.anyOf.forEach(({ required = [] }) => {
845
1146
  required.forEach((fieldName) => {
846
1147
  const field = getField(fieldName, formFields);
847
- updateField(field, requiredFields, node, formValues);
1148
+ updateField(field, requiredFields, node, formValues, logic, { parentID });
848
1149
  });
849
1150
  });
850
1151
  }
851
1152
  if (node.allOf) {
852
- node.allOf.map((allOfNode) => processNode(allOfNode, formValues, formFields, requiredFields)).forEach(({ required: allOfItemRequired }) => {
1153
+ node.allOf.map(
1154
+ (allOfNode) => processNode({
1155
+ node: allOfNode,
1156
+ formValues,
1157
+ formFields,
1158
+ accRequired: requiredFields,
1159
+ parentID,
1160
+ logic
1161
+ })
1162
+ ).forEach(({ required: allOfItemRequired }) => {
853
1163
  allOfItemRequired.forEach(requiredFields.add, requiredFields);
854
1164
  });
855
1165
  }
@@ -857,7 +1167,13 @@ function processNode(node, formValues, formFields, accRequired = /* @__PURE__ */
857
1167
  Object.entries(node.properties).forEach(([name, nestedNode]) => {
858
1168
  const inputType = getInputType(nestedNode);
859
1169
  if (inputType === supportedTypes.FIELDSET) {
860
- processNode(nestedNode, formValues[name] || {}, getField(name, formFields).fields);
1170
+ processNode({
1171
+ node: nestedNode,
1172
+ formValues: formValues[name] || {},
1173
+ formFields: getField(name, formFields).fields,
1174
+ parentID: name,
1175
+ logic
1176
+ });
861
1177
  }
862
1178
  });
863
1179
  }
@@ -875,11 +1191,11 @@ function clearValuesIfNotVisible(fields, formValues) {
875
1191
  }
876
1192
  });
877
1193
  }
878
- function updateFieldsProperties(fields, formValues, jsonSchema) {
1194
+ function updateFieldsProperties(fields, formValues, jsonSchema, logic) {
879
1195
  if (!jsonSchema?.properties) {
880
1196
  return;
881
1197
  }
882
- processNode(jsonSchema, formValues, fields);
1198
+ processNode({ node: jsonSchema, formValues, formFields: fields, logic });
883
1199
  clearValuesIfNotVisible(fields, formValues);
884
1200
  }
885
1201
  var notNullOption = (opt) => opt.const !== null;
@@ -922,11 +1238,16 @@ function extractParametersFromNode(schemaNode) {
922
1238
  }
923
1239
  const presentation = pickXKey(schemaNode, "presentation") ?? {};
924
1240
  const errorMessage = pickXKey(schemaNode, "errorMessage") ?? {};
1241
+ const requiredValidations = schemaNode["x-jsf-logic-validations"];
1242
+ const computedAttributes = schemaNode["x-jsf-logic-computedAttrs"];
1243
+ const decoratedComputedAttributes = getDecoratedComputedAttributes(computedAttributes);
925
1244
  const node = (0, import_omit.default)(schemaNode, ["x-jsf-presentation", "presentation"]);
926
1245
  const description = presentation?.description || node.description;
927
- const statementDescription = presentation.statement?.description;
1246
+ const statementDescription = containsHTML(presentation.statement?.description) ? wrapWithSpan(presentation.statement.description, { class: "jsf-statement" }) : presentation.statement?.description;
928
1247
  return (0, import_omitBy.default)(
929
1248
  {
1249
+ const: node.const,
1250
+ ...node.const && node.default ? { value: node.const } : {},
930
1251
  label: node.title,
931
1252
  readOnly: node.readOnly,
932
1253
  ...node.deprecated && {
@@ -961,8 +1282,12 @@ function extractParametersFromNode(schemaNode) {
961
1282
  },
962
1283
  // Handle [name].presentation
963
1284
  ...presentation,
964
- description,
965
- extra: presentation.extra,
1285
+ requiredValidations,
1286
+ computedAttributes: decoratedComputedAttributes,
1287
+ description: containsHTML(description) ? wrapWithSpan(description, {
1288
+ class: "jsf-description"
1289
+ }) : description,
1290
+ extra: containsHTML(presentation.extra) ? wrapWithSpan(presentation.extra, { class: "jsf-extra" }) : presentation.extra,
966
1291
  statement: presentation.statement && {
967
1292
  ...presentation.statement,
968
1293
  description: statementDescription
@@ -995,8 +1320,8 @@ function yupToFormErrors(yupError) {
995
1320
  }
996
1321
  return errors;
997
1322
  }
998
- var handleValuesChange = (fields, jsonSchema, config) => (values) => {
999
- updateFieldsProperties(fields, values, jsonSchema);
1323
+ var handleValuesChange = (fields, jsonSchema, config, logic) => (values) => {
1324
+ updateFieldsProperties(fields, values, jsonSchema, logic);
1000
1325
  const lazySchema = (0, import_yup2.lazy)(() => buildCompleteYupSchema(fields, config));
1001
1326
  let errors;
1002
1327
  try {
@@ -1015,6 +1340,12 @@ var handleValuesChange = (fields, jsonSchema, config) => (values) => {
1015
1340
  formErrors: yupToFormErrors(errors)
1016
1341
  };
1017
1342
  };
1343
+ function getDecoratedComputedAttributes(computedAttributes) {
1344
+ return {
1345
+ ...computedAttributes ?? {},
1346
+ ...computedAttributes?.const && computedAttributes?.default ? { value: computedAttributes.const } : {}
1347
+ };
1348
+ }
1018
1349
 
1019
1350
  // src/calculateConditionalProperties.js
1020
1351
  function isFieldRequired(node, field) {
@@ -1216,6 +1547,9 @@ function applyFieldsDependencies(fieldsParameters, node) {
1216
1547
  applyFieldsDependencies(fieldsParameters, condition);
1217
1548
  });
1218
1549
  }
1550
+ if (node?.["x-jsf-logic"]) {
1551
+ applyFieldsDependencies(fieldsParameters, node["x-jsf-logic"]);
1552
+ }
1219
1553
  }
1220
1554
  function getCustomPropertiesForField(fieldParams, config) {
1221
1555
  return config?.customProperties?.[fieldParams.name];
@@ -1227,15 +1561,16 @@ function getComposeFunctionForField(fieldParams, hasCustomizations) {
1227
1561
  }
1228
1562
  return composeFn;
1229
1563
  }
1230
- function buildField(fieldParams, config, scopedJsonSchema) {
1564
+ function buildField(fieldParams, config, scopedJsonSchema, logic) {
1231
1565
  const customProperties = getCustomPropertiesForField(fieldParams, config);
1232
1566
  const composeFn = getComposeFunctionForField(fieldParams, !!customProperties);
1233
- const yupSchema = buildYupSchema(fieldParams, config);
1567
+ const yupSchema = buildYupSchema(fieldParams, config, logic);
1234
1568
  const calculateConditionalFieldsClosure = fieldParams.isDynamic && calculateConditionalProperties(fieldParams, customProperties);
1235
1569
  const calculateCustomValidationPropertiesClosure = calculateCustomValidationProperties(
1236
1570
  fieldParams,
1237
1571
  customProperties
1238
1572
  );
1573
+ const getComputedAttributes = Object.keys(fieldParams.computedAttributes).length > 0 && calculateComputedAttributes(fieldParams, config);
1239
1574
  const hasCustomValidations = !!customProperties && (0, import_size.default)((0, import_pick2.default)(customProperties, SUPPORTED_CUSTOM_VALIDATION_FIELD_PARAMS)) > 0;
1240
1575
  const finalFieldParams = {
1241
1576
  // invalid attribute cleanup
@@ -1248,6 +1583,7 @@ function buildField(fieldParams, config, scopedJsonSchema) {
1248
1583
  ...hasCustomValidations && {
1249
1584
  calculateCustomValidationProperties: calculateCustomValidationPropertiesClosure
1250
1585
  },
1586
+ ...getComputedAttributes && { getComputedAttributes },
1251
1587
  // field customization properties
1252
1588
  ...customProperties && { fieldCustomization: customProperties },
1253
1589
  // base schema
@@ -1256,7 +1592,7 @@ function buildField(fieldParams, config, scopedJsonSchema) {
1256
1592
  };
1257
1593
  return composeFn(finalFieldParams);
1258
1594
  }
1259
- function getFieldsFromJSONSchema(scopedJsonSchema, config) {
1595
+ function getFieldsFromJSONSchema(scopedJsonSchema, config, logic) {
1260
1596
  if (!scopedJsonSchema) {
1261
1597
  return [];
1262
1598
  }
@@ -1280,11 +1616,11 @@ function getFieldsFromJSONSchema(scopedJsonSchema, config) {
1280
1616
  fields: () => groupArrayFields,
1281
1617
  addFieldText: fieldParams.addFieldText
1282
1618
  };
1283
- buildField(fieldParams, config, scopedJsonSchema).forEach((groupField) => {
1619
+ buildField(fieldParams, config, scopedJsonSchema, logic).forEach((groupField) => {
1284
1620
  fields.push(groupField);
1285
1621
  });
1286
1622
  } else {
1287
- fields.push(buildField(fieldParams, config, scopedJsonSchema));
1623
+ fields.push(buildField(fieldParams, config, scopedJsonSchema, logic));
1288
1624
  }
1289
1625
  });
1290
1626
  return fields;
@@ -1295,9 +1631,15 @@ function createHeadlessForm(jsonSchema, customConfig = {}) {
1295
1631
  ...customConfig
1296
1632
  };
1297
1633
  try {
1298
- const fields = getFieldsFromJSONSchema(jsonSchema, config);
1299
- const handleValidation = handleValuesChange(fields, jsonSchema, config);
1300
- updateFieldsProperties(fields, getPrefillValues(fields, config.initialValues), jsonSchema);
1634
+ const logic = createValidationChecker(jsonSchema);
1635
+ const fields = getFieldsFromJSONSchema(jsonSchema, config, logic);
1636
+ const handleValidation = handleValuesChange(fields, jsonSchema, config, logic);
1637
+ updateFieldsProperties(
1638
+ fields,
1639
+ getPrefillValues(fields, config.initialValues),
1640
+ jsonSchema,
1641
+ logic
1642
+ );
1301
1643
  return {
1302
1644
  fields,
1303
1645
  handleValidation,