@remoteoss/json-schema-form 0.4.3-beta.0 → 0.4.4-dev.20230829101351

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/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.4.3-beta.0
5
- Generated: Wed, 09 Aug 2023 08:39:30 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.4.4-dev.20230829101351
5
+ Generated: Tue, 29 Aug 2023 10:14:13 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -103,8 +103,8 @@ function hasProperty(object2, propertyName) {
103
103
  }
104
104
 
105
105
  // src/checkIfConditionMatches.js
106
- function checkIfConditionMatches(node, formValues, formFields) {
107
- return Object.keys(node.if.properties).every((name) => {
106
+ function checkIfConditionMatches(node, formValues, formFields, validations) {
107
+ return Object.keys(node.if.properties ?? {}).every((name) => {
108
108
  const currentProperty = node.if.properties[name];
109
109
  const value = formValues[name];
110
110
  const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
@@ -130,7 +130,8 @@ function checkIfConditionMatches(node, formValues, formFields) {
130
130
  return checkIfConditionMatches(
131
131
  { if: currentProperty },
132
132
  formValues[name],
133
- getField(name, formFields).fields
133
+ getField(name, formFields).fields,
134
+ validations
134
135
  );
135
136
  }
136
137
  const field = getField(name, formFields);
@@ -146,6 +147,23 @@ function checkIfConditionMatches(node, formValues, formFields) {
146
147
  );
147
148
  });
148
149
  }
150
+ function checkIfMatchesValidationsAndComputedValues(node, formValues, validations, parentID) {
151
+ const validationsMatch = Object.entries(node.if.validations ?? {}).every(([name, property]) => {
152
+ const currentValue = validations.getScope(parentID).evaluateValidationRuleInCondition(name, formValues);
153
+ if (Object.hasOwn(property, "const") && currentValue === property.const)
154
+ return true;
155
+ return false;
156
+ });
157
+ const computedValuesMatch = Object.entries(node.if.computedValues ?? {}).every(
158
+ ([name, property]) => {
159
+ const currentValue = validations.getScope(parentID).evaluateComputedValueRuleInCondition(name, formValues);
160
+ if (Object.hasOwn(property, "const") && currentValue === property.const)
161
+ return true;
162
+ return false;
163
+ }
164
+ );
165
+ return computedValuesMatch && validationsMatch;
166
+ }
149
167
 
150
168
  // src/internals/helpers.js
151
169
  var import_merge = __toESM(require("lodash/fp/merge"));
@@ -406,6 +424,9 @@ function _composeFieldCustomClosure(defaultComposeFn) {
406
424
  };
407
425
  }
408
426
 
427
+ // src/jsonLogic.js
428
+ var import_json_logic_js = __toESM(require("json-logic-js"));
429
+
409
430
  // src/yupSchema.js
410
431
  var import_flow = __toESM(require("lodash/flow"));
411
432
  var import_noop = __toESM(require("lodash/noop"));
@@ -533,7 +554,7 @@ var getYupSchema = ({ inputType, ...field }) => {
533
554
  }
534
555
  return yupSchemas[inputType] || yupSchemasToJsonTypes[jsonType];
535
556
  };
536
- function buildYupSchema(field, config) {
557
+ function buildYupSchema(field, config, validations) {
537
558
  const { inputType, jsonType: jsonTypeValue, errorMessage = {}, ...propertyFields } = field;
538
559
  const isCheckboxBoolean = typeof propertyFields.checkboxValue === "boolean";
539
560
  let baseSchema;
@@ -606,6 +627,13 @@ function buildYupSchema(field, config) {
606
627
  }) : true
607
628
  );
608
629
  }
630
+ function withConst(yupSchema) {
631
+ return yupSchema.test(
632
+ "isConst",
633
+ errorMessage.const ?? errorMessageFromConfig.const ?? `The only accepted value is ${propertyFields.const}.`,
634
+ (value) => propertyFields.required === false && value === void 0 || value === null || value === propertyFields.const
635
+ );
636
+ }
609
637
  function withBaseSchema() {
610
638
  const customErrorMsg = errorMessage.type || errorMessageFromConfig.type;
611
639
  if (customErrorMsg) {
@@ -626,7 +654,8 @@ function buildYupSchema(field, config) {
626
654
  ...fieldSetfield,
627
655
  inputType: fieldSetfield.type
628
656
  },
629
- config
657
+ { ...config, parentID: field.name },
658
+ validations
630
659
  )();
631
660
  }
632
661
  });
@@ -637,7 +666,11 @@ function buildYupSchema(field, config) {
637
666
  propertyFields.nthFieldGroup.fields().reduce(
638
667
  (schema, groupArrayField) => ({
639
668
  ...schema,
640
- [groupArrayField.name]: buildYupSchema(groupArrayField, config)()
669
+ [groupArrayField.name]: buildYupSchema(
670
+ groupArrayField,
671
+ { ...config, parentID: `${propertyFields.nthFieldGroup.name}[]` },
672
+ validations
673
+ )()
641
674
  }),
642
675
  {}
643
676
  )
@@ -673,6 +706,14 @@ function buildYupSchema(field, config) {
673
706
  if (propertyFields.accept) {
674
707
  validators.push(withFileFormat);
675
708
  }
709
+ if (propertyFields.const) {
710
+ validators.push(withConst);
711
+ }
712
+ if (propertyFields.requiredValidations) {
713
+ propertyFields.requiredValidations.forEach(
714
+ (id) => validators.push(yupSchemaWithCustomJSONLogic({ field, id, validations, config }))
715
+ );
716
+ }
676
717
  return (0, import_flow.default)(validators);
677
718
  }
678
719
  function getNoSortEdges(fields = []) {
@@ -683,26 +724,322 @@ function getNoSortEdges(fields = []) {
683
724
  return list;
684
725
  }, []);
685
726
  }
686
- function getSchema(fields = [], config) {
727
+ function getSchema(fields = [], config, validations) {
687
728
  const newSchema = {};
688
729
  fields.forEach((field) => {
689
730
  if (field.schema) {
690
731
  if (field.name) {
691
732
  if (field.inputType === supportedTypes.FIELDSET) {
692
- const fieldsetSchema = buildYupSchema(field, config)();
733
+ const fieldsetSchema = buildYupSchema(
734
+ field,
735
+ { ...config, parentID: field.name },
736
+ validations
737
+ )();
693
738
  newSchema[field.name] = fieldsetSchema;
694
739
  } else {
695
740
  newSchema[field.name] = field.schema;
696
741
  }
697
742
  } else {
698
- Object.assign(newSchema, getSchema(field.fields, config));
743
+ Object.assign(newSchema, getSchema(field.fields, config, validations));
699
744
  }
700
745
  }
701
746
  });
702
747
  return newSchema;
703
748
  }
704
- function buildCompleteYupSchema(fields, config) {
705
- return (0, import_yup.object)().shape(getSchema(fields, config), getNoSortEdges(fields));
749
+ function buildCompleteYupSchema(fields, config, validations) {
750
+ return (0, import_yup.object)().shape(getSchema(fields, config, validations), getNoSortEdges(fields));
751
+ }
752
+
753
+ // src/jsonLogic.js
754
+ function createValidationChecker(schema) {
755
+ const scopes = /* @__PURE__ */ new Map();
756
+ function createScopes(jsonSchema, key = "root") {
757
+ const sampleEmptyObject = buildSampleEmptyObject(schema);
758
+ scopes.set(key, createValidationsScope(jsonSchema));
759
+ Object.entries(jsonSchema?.properties ?? {}).filter(([, property]) => property.type === "object" || property.type === "array").forEach(([key2, property]) => {
760
+ if (property.type === "array") {
761
+ createScopes(property.items, `${key2}[]`);
762
+ }
763
+ createScopes(property, key2);
764
+ });
765
+ validateInlineRules(jsonSchema, sampleEmptyObject);
766
+ }
767
+ createScopes(schema);
768
+ return {
769
+ scopes,
770
+ getScope(name = "root") {
771
+ return scopes.get(name);
772
+ }
773
+ };
774
+ }
775
+ function createValidationsScope(schema) {
776
+ const validationMap = /* @__PURE__ */ new Map();
777
+ const computedValuesMap = /* @__PURE__ */ new Map();
778
+ const logic = schema?.["x-jsf-logic"] ?? {
779
+ validations: {},
780
+ computedValues: {}
781
+ };
782
+ const validations = Object.entries(logic.validations ?? {});
783
+ const computedValues = Object.entries(logic.computedValues ?? {});
784
+ const sampleEmptyObject = buildSampleEmptyObject(schema);
785
+ validations.forEach(([id, validation]) => {
786
+ if (!validation.rule) {
787
+ throw Error(`Missing rule for validation with id of: "${id}".`);
788
+ }
789
+ checkRuleIntegrity(validation.rule, id, sampleEmptyObject);
790
+ validationMap.set(id, validation);
791
+ });
792
+ computedValues.forEach(([id, computedValue]) => {
793
+ if (!computedValue.rule) {
794
+ throw Error(`Missing rule for computedValue with id of: "${id}".`);
795
+ }
796
+ checkRuleIntegrity(computedValue.rule, id, sampleEmptyObject);
797
+ computedValuesMap.set(id, computedValue);
798
+ });
799
+ function evaluateValidation(rule, values) {
800
+ return import_json_logic_js.default.apply(rule, clean(values));
801
+ }
802
+ return {
803
+ validationMap,
804
+ computedValuesMap,
805
+ evaluateValidation,
806
+ evaluateValidationRuleInCondition(id, values) {
807
+ const validation = validationMap.get(id);
808
+ if (validation === void 0)
809
+ throw Error(`"${id}" validation in if condition doesn't exist.`);
810
+ return evaluateValidation(validation.rule, values);
811
+ },
812
+ evaluateComputedValueRuleForField(id, values, fieldName) {
813
+ const validation = computedValuesMap.get(id);
814
+ if (validation === void 0)
815
+ throw Error(`"${id}" computedValue in field "${fieldName}" doesn't exist.`);
816
+ return evaluateValidation(validation.rule, values);
817
+ },
818
+ evaluateComputedValueRuleInCondition(id, values) {
819
+ const validation = computedValuesMap.get(id);
820
+ if (validation === void 0)
821
+ throw Error(`"${id}" computedValue in if condition doesn't exist.`);
822
+ return evaluateValidation(validation.rule, values);
823
+ }
824
+ };
825
+ }
826
+ function clean(values = {}) {
827
+ return Object.entries(values).reduce((prev, [key, value]) => {
828
+ return { ...prev, [key]: value === void 0 ? null : value };
829
+ }, {});
830
+ }
831
+ function yupSchemaWithCustomJSONLogic({ field, validations, config, id }) {
832
+ const { parentID = "root" } = config;
833
+ const validation = validations.getScope(parentID).validationMap.get(id);
834
+ if (validation === void 0) {
835
+ throw Error(`Validation "${id}" required for "${field.name}" doesn't exist.`);
836
+ }
837
+ return (yupSchema) => yupSchema.test(
838
+ `${field.name}-validation-${id}`,
839
+ validation?.errorMessage ?? "This field is invalid.",
840
+ (value, { parent }) => {
841
+ if (value === void 0 && !field.required)
842
+ return true;
843
+ return import_json_logic_js.default.apply(validation.rule, parent);
844
+ }
845
+ );
846
+ }
847
+ function replaceHandlebarsTemplates({
848
+ value: toReplace,
849
+ validations,
850
+ formValues,
851
+ parentID,
852
+ name: fieldName
853
+ }) {
854
+ if (typeof toReplace === "string") {
855
+ return toReplace.replace(/\{\{([^{}]+)\}\}/g, (match, key) => {
856
+ return validations.getScope(parentID).evaluateComputedValueRuleForField(key.trim(), formValues, fieldName);
857
+ });
858
+ } else if (typeof toReplace === "object") {
859
+ const { value, ...rules } = toReplace;
860
+ if (Object.keys(rules).length > 1 && !value)
861
+ throw Error("Cannot define multiple rules without a template string with key `value`.");
862
+ const computedTemplateValue = Object.entries(rules).reduce((prev, [key, rule]) => {
863
+ const computedValue = validations.getScope(parentID).evaluateValidation(rule, formValues);
864
+ return prev.replaceAll(`{{${key}}}`, computedValue);
865
+ }, value);
866
+ return computedTemplateValue.replace(/\{\{([^{}]+)\}\}/g, (match, key) => {
867
+ return validations.getScope(parentID).evaluateComputedValueRuleForField(key.trim(), formValues, fieldName);
868
+ });
869
+ }
870
+ }
871
+ function calculateComputedAttributes(fieldParams, { parentID = "root" } = {}) {
872
+ return ({ validations, isRequired, config, formValues }) => {
873
+ const { name, computedAttributes } = fieldParams;
874
+ const attributes = Object.fromEntries(
875
+ Object.entries(computedAttributes).map(handleComputedAttribute(validations, formValues, parentID, name)).filter(([, value]) => value !== null)
876
+ );
877
+ return {
878
+ ...attributes,
879
+ schema: buildYupSchema(
880
+ { ...fieldParams, ...attributes, required: isRequired },
881
+ config,
882
+ validations
883
+ )
884
+ };
885
+ };
886
+ }
887
+ function handleComputedAttribute(validations, formValues, parentID, name) {
888
+ return ([key, value]) => {
889
+ if (key === "description")
890
+ return [key, replaceHandlebarsTemplates({ value, validations, formValues, parentID, name })];
891
+ if (key === "title") {
892
+ return [
893
+ "label",
894
+ replaceHandlebarsTemplates({ value, validations, formValues, parentID, name })
895
+ ];
896
+ }
897
+ if (key === "const")
898
+ return [
899
+ key,
900
+ validations.getScope(parentID).evaluateComputedValueRuleForField(value, formValues, name)
901
+ ];
902
+ if (key === "x-jsf-errorMessage") {
903
+ return [
904
+ "errorMessage",
905
+ handleNestedObjectForComputedValues(value, formValues, parentID, validations, name)
906
+ ];
907
+ }
908
+ if (typeof value === "string") {
909
+ return [
910
+ key,
911
+ validations.getScope(parentID).evaluateComputedValueRuleForField(value, formValues, name)
912
+ ];
913
+ }
914
+ if (key === "x-jsf-presentation" && value.statement) {
915
+ return [
916
+ "statement",
917
+ handleNestedObjectForComputedValues(
918
+ value.statement,
919
+ formValues,
920
+ parentID,
921
+ validations,
922
+ name
923
+ )
924
+ ];
925
+ }
926
+ if (typeof value === "object" && value.rule) {
927
+ return [key, validations.getScope(parentID).evaluateValidation(value.rule, formValues)];
928
+ }
929
+ };
930
+ }
931
+ function handleNestedObjectForComputedValues(values, formValues, parentID, validations, name) {
932
+ return Object.fromEntries(
933
+ Object.entries(values).map(([key, value]) => {
934
+ return [key, replaceHandlebarsTemplates({ value, validations, formValues, parentID, name })];
935
+ })
936
+ );
937
+ }
938
+ function processJSONLogicNode({
939
+ node,
940
+ formFields,
941
+ formValues,
942
+ accRequired,
943
+ parentID,
944
+ validations
945
+ }) {
946
+ const requiredFields = new Set(accRequired);
947
+ if (node.allOf) {
948
+ node.allOf.map(
949
+ (allOfNode) => processJSONLogicNode({ node: allOfNode, formValues, formFields, validations, parentID })
950
+ ).forEach(({ required: allOfItemRequired }) => {
951
+ allOfItemRequired.forEach(requiredFields.add, requiredFields);
952
+ });
953
+ }
954
+ if (node.if) {
955
+ const matchesPropertyCondition = checkIfConditionMatches(
956
+ node,
957
+ formValues,
958
+ formFields,
959
+ validations
960
+ );
961
+ const matchesValidationsAndComputedValues = checkIfMatchesValidationsAndComputedValues(
962
+ node,
963
+ formValues,
964
+ validations,
965
+ parentID
966
+ );
967
+ const isConditionMatch = matchesPropertyCondition && matchesValidationsAndComputedValues;
968
+ if (isConditionMatch && node.then) {
969
+ const { required: branchRequired } = processNode({
970
+ node: node.then,
971
+ formValues,
972
+ formFields,
973
+ accRequired,
974
+ validations
975
+ });
976
+ branchRequired.forEach((field) => requiredFields.add(field));
977
+ }
978
+ if (!isConditionMatch && node.else) {
979
+ const { required: branchRequired } = processNode({
980
+ node: node.else,
981
+ formValues,
982
+ formFields,
983
+ accRequired: requiredFields,
984
+ validations
985
+ });
986
+ branchRequired.forEach((field) => requiredFields.add(field));
987
+ }
988
+ }
989
+ return { required: requiredFields };
990
+ }
991
+ function buildSampleEmptyObject(schema = {}) {
992
+ const sample = {};
993
+ if (typeof schema !== "object" || !schema.properties) {
994
+ return schema;
995
+ }
996
+ for (const key in schema.properties) {
997
+ if (schema.properties[key].type === "object") {
998
+ sample[key] = buildSampleEmptyObject(schema.properties[key]);
999
+ } else if (schema.properties[key].type === "array") {
1000
+ const itemSchema = schema.properties[key].items;
1001
+ sample[key] = buildSampleEmptyObject(itemSchema);
1002
+ } else {
1003
+ sample[key] = true;
1004
+ }
1005
+ }
1006
+ return sample;
1007
+ }
1008
+ function validateInlineRules(jsonSchema, sampleEmptyObject) {
1009
+ const properties = (jsonSchema?.properties || jsonSchema?.items?.properties) ?? {};
1010
+ Object.entries(properties).filter(([, property]) => property["x-jsf-logic-computedAttrs"] !== void 0).forEach(([fieldName, property]) => {
1011
+ Object.entries(property["x-jsf-logic-computedAttrs"]).filter(([, value]) => typeof value === "object").forEach(([key, item]) => {
1012
+ Object.values(item).forEach((rule) => {
1013
+ checkRuleIntegrity(
1014
+ rule,
1015
+ fieldName,
1016
+ sampleEmptyObject,
1017
+ (item2) => `"${item2.var}" in inline rule in property "${fieldName}.x-jsf-logic-computedAttrs.${key}" does not exist as a JSON schema property.`
1018
+ );
1019
+ });
1020
+ });
1021
+ });
1022
+ }
1023
+ function checkRuleIntegrity(rule, id, data, errorMessage = (item) => `"${item.var}" in rule "${id}" does not exist as a JSON schema property.`) {
1024
+ Object.values(rule ?? {}).map((subRule) => {
1025
+ if (!Array.isArray(subRule) && subRule !== null && subRule !== void 0)
1026
+ return;
1027
+ subRule.map((item) => {
1028
+ const isVar = item !== null && typeof item === "object" && Object.hasOwn(item, "var");
1029
+ if (isVar) {
1030
+ const exists = import_json_logic_js.default.apply({ var: removeIndicesFromPath(item.var) }, data);
1031
+ if (exists === null) {
1032
+ throw Error(errorMessage(item));
1033
+ }
1034
+ } else {
1035
+ checkRuleIntegrity(item, id, data);
1036
+ }
1037
+ });
1038
+ });
1039
+ }
1040
+ function removeIndicesFromPath(path) {
1041
+ const intermediatePath = path.replace(/\.\d+\./g, ".");
1042
+ return intermediatePath.replace(/\.\d+$/, "");
706
1043
  }
707
1044
 
708
1045
  // src/helpers.js
@@ -712,8 +1049,8 @@ function hasType(type, typeName) {
712
1049
  function getField(fieldName, fields) {
713
1050
  return fields.find(({ name }) => name === fieldName);
714
1051
  }
715
- function validateFieldSchema(field, value) {
716
- const validator = buildYupSchema(field);
1052
+ function validateFieldSchema(field, value, validations) {
1053
+ const validator = buildYupSchema(field, {}, validations);
717
1054
  return validator().isValidSync(value);
718
1055
  }
719
1056
  function compareFormValueWithSchemaValue(formValue, schemaValue) {
@@ -778,7 +1115,7 @@ function getPrefillValues(fields, initialValues = {}) {
778
1115
  });
779
1116
  return initialValues;
780
1117
  }
781
- function updateField(field, requiredFields, node, formValues) {
1118
+ function updateField(field, requiredFields, node, formValues, validations, config) {
782
1119
  if (!field) {
783
1120
  return;
784
1121
  }
@@ -800,8 +1137,25 @@ function updateField(field, requiredFields, node, formValues) {
800
1137
  }
801
1138
  }
802
1139
  });
1140
+ if (field.getComputedAttributes) {
1141
+ const computedFieldValues = field.getComputedAttributes({
1142
+ field,
1143
+ isRequired: fieldIsRequired,
1144
+ node,
1145
+ formValues,
1146
+ config,
1147
+ validations
1148
+ });
1149
+ updateValues(computedFieldValues);
1150
+ }
803
1151
  if (field.calculateConditionalProperties) {
804
- const newFieldValues = field.calculateConditionalProperties(fieldIsRequired, node);
1152
+ const newFieldValues = field.calculateConditionalProperties(
1153
+ fieldIsRequired,
1154
+ node,
1155
+ validations,
1156
+ config,
1157
+ formValues
1158
+ );
805
1159
  updateValues(newFieldValues);
806
1160
  }
807
1161
  if (field.calculateCustomValidationProperties) {
@@ -813,33 +1167,46 @@ function updateField(field, requiredFields, node, formValues) {
813
1167
  updateValues(newFieldValues);
814
1168
  }
815
1169
  }
816
- function processNode(node, formValues, formFields, accRequired = /* @__PURE__ */ new Set()) {
1170
+ function processNode({
1171
+ node,
1172
+ formValues,
1173
+ formFields,
1174
+ accRequired = /* @__PURE__ */ new Set(),
1175
+ parentID = "root",
1176
+ validations
1177
+ }) {
817
1178
  const requiredFields = new Set(accRequired);
818
1179
  Object.keys(node.properties ?? []).forEach((fieldName) => {
819
1180
  const field = getField(fieldName, formFields);
820
- updateField(field, requiredFields, node, formValues);
1181
+ updateField(field, requiredFields, node, formValues, validations, { parentID });
821
1182
  });
822
1183
  node.required?.forEach((fieldName) => {
823
1184
  requiredFields.add(fieldName);
824
- updateField(getField(fieldName, formFields), requiredFields, node, formValues);
1185
+ updateField(getField(fieldName, formFields), requiredFields, node, formValues, validations, {
1186
+ parentID
1187
+ });
825
1188
  });
826
1189
  if (node.if) {
827
- const matchesCondition = checkIfConditionMatches(node, formValues, formFields);
1190
+ const matchesCondition = checkIfConditionMatches(node, formValues, formFields, validations);
828
1191
  if (matchesCondition && node.then) {
829
- const { required: branchRequired } = processNode(
830
- node.then,
1192
+ const { required: branchRequired } = processNode({
1193
+ node: node.then,
831
1194
  formValues,
832
1195
  formFields,
833
- requiredFields
834
- );
1196
+ accRequired: requiredFields,
1197
+ parentID,
1198
+ validations
1199
+ });
835
1200
  branchRequired.forEach((field) => requiredFields.add(field));
836
1201
  } else if (node.else) {
837
- const { required: branchRequired } = processNode(
838
- node.else,
1202
+ const { required: branchRequired } = processNode({
1203
+ node: node.else,
839
1204
  formValues,
840
1205
  formFields,
841
- requiredFields
842
- );
1206
+ accRequired: requiredFields,
1207
+ parentID,
1208
+ validations
1209
+ });
843
1210
  branchRequired.forEach((field) => requiredFields.add(field));
844
1211
  }
845
1212
  }
@@ -851,12 +1218,21 @@ function processNode(node, formValues, formFields, accRequired = /* @__PURE__ */
851
1218
  node.anyOf.forEach(({ required = [] }) => {
852
1219
  required.forEach((fieldName) => {
853
1220
  const field = getField(fieldName, formFields);
854
- updateField(field, requiredFields, node, formValues);
1221
+ updateField(field, requiredFields, node, formValues, validations, { parentID });
855
1222
  });
856
1223
  });
857
1224
  }
858
1225
  if (node.allOf) {
859
- node.allOf.map((allOfNode) => processNode(allOfNode, formValues, formFields, requiredFields)).forEach(({ required: allOfItemRequired }) => {
1226
+ node.allOf.map(
1227
+ (allOfNode) => processNode({
1228
+ node: allOfNode,
1229
+ formValues,
1230
+ formFields,
1231
+ accRequired: requiredFields,
1232
+ parentID,
1233
+ validations
1234
+ })
1235
+ ).forEach(({ required: allOfItemRequired }) => {
860
1236
  allOfItemRequired.forEach(requiredFields.add, requiredFields);
861
1237
  });
862
1238
  }
@@ -864,10 +1240,27 @@ function processNode(node, formValues, formFields, accRequired = /* @__PURE__ */
864
1240
  Object.entries(node.properties).forEach(([name, nestedNode]) => {
865
1241
  const inputType = getInputType(nestedNode);
866
1242
  if (inputType === supportedTypes.FIELDSET) {
867
- processNode(nestedNode, formValues[name] || {}, getField(name, formFields).fields);
1243
+ processNode({
1244
+ node: nestedNode,
1245
+ formValues: formValues[name] || {},
1246
+ formFields: getField(name, formFields).fields,
1247
+ validations,
1248
+ parentID: name
1249
+ });
868
1250
  }
869
1251
  });
870
1252
  }
1253
+ if (node["x-jsf-logic"]) {
1254
+ const { required: requiredFromLogic } = processJSONLogicNode({
1255
+ node: node["x-jsf-logic"],
1256
+ formValues,
1257
+ formFields,
1258
+ accRequired: requiredFields,
1259
+ parentID,
1260
+ validations
1261
+ });
1262
+ requiredFromLogic.forEach((field) => requiredFields.add(field));
1263
+ }
871
1264
  return {
872
1265
  required: requiredFields
873
1266
  };
@@ -882,11 +1275,11 @@ function clearValuesIfNotVisible(fields, formValues) {
882
1275
  }
883
1276
  });
884
1277
  }
885
- function updateFieldsProperties(fields, formValues, jsonSchema) {
1278
+ function updateFieldsProperties(fields, formValues, jsonSchema, validations) {
886
1279
  if (!jsonSchema?.properties) {
887
1280
  return;
888
1281
  }
889
- processNode(jsonSchema, formValues, fields);
1282
+ processNode({ node: jsonSchema, formValues, formFields: fields, validations });
890
1283
  clearValuesIfNotVisible(fields, formValues);
891
1284
  }
892
1285
  var notNullOption = (opt) => opt.const !== null;
@@ -929,11 +1322,20 @@ function extractParametersFromNode(schemaNode) {
929
1322
  }
930
1323
  const presentation = pickXKey(schemaNode, "presentation") ?? {};
931
1324
  const errorMessage = pickXKey(schemaNode, "errorMessage") ?? {};
1325
+ const requiredValidations = schemaNode["x-jsf-logic-validations"];
1326
+ const computedAttributes = schemaNode["x-jsf-logic-computedAttrs"];
1327
+ const decoratedComputedAttributes = {
1328
+ ...computedAttributes ?? {},
1329
+ ...computedAttributes?.const && computedAttributes?.default ? { value: computedAttributes.const } : {}
1330
+ };
932
1331
  const node = (0, import_omit.default)(schemaNode, ["x-jsf-presentation", "presentation"]);
933
1332
  const description = presentation?.description || node.description;
934
1333
  const statementDescription = containsHTML(presentation.statement?.description) ? wrapWithSpan(presentation.statement.description, { class: "jsf-statement" }) : presentation.statement?.description;
935
1334
  return (0, import_omitBy.default)(
936
1335
  {
1336
+ const: node.const,
1337
+ // This is a "forced value" when both const and default are present.
1338
+ ...node.const && node.default ? { value: node.const } : {},
937
1339
  label: node.title,
938
1340
  readOnly: node.readOnly,
939
1341
  ...node.deprecated && {
@@ -968,6 +1370,8 @@ function extractParametersFromNode(schemaNode) {
968
1370
  },
969
1371
  // Handle [name].presentation
970
1372
  ...presentation,
1373
+ requiredValidations,
1374
+ computedAttributes: decoratedComputedAttributes,
971
1375
  description: containsHTML(description) ? wrapWithSpan(description, {
972
1376
  class: "jsf-description"
973
1377
  }) : description,
@@ -1004,9 +1408,9 @@ function yupToFormErrors(yupError) {
1004
1408
  }
1005
1409
  return errors;
1006
1410
  }
1007
- var handleValuesChange = (fields, jsonSchema, config) => (values) => {
1008
- updateFieldsProperties(fields, values, jsonSchema);
1009
- const lazySchema = (0, import_yup2.lazy)(() => buildCompleteYupSchema(fields, config));
1411
+ var handleValuesChange = (fields, jsonSchema, config, validations) => (values) => {
1412
+ updateFieldsProperties(fields, values, jsonSchema, validations);
1413
+ const lazySchema = (0, import_yup2.lazy)(() => buildCompleteYupSchema(fields, config, validations));
1010
1414
  let errors;
1011
1415
  try {
1012
1416
  lazySchema.validateSync(values, {
@@ -1060,8 +1464,8 @@ function rebuildFieldset(fields, property) {
1060
1464
  required: isFieldRequired(property, field)
1061
1465
  }));
1062
1466
  }
1063
- function calculateConditionalProperties(fieldParams, customProperties) {
1064
- return (isRequired, conditionBranch) => {
1467
+ function calculateConditionalProperties(fieldParams, customProperties, validations, config) {
1468
+ return (isRequired, conditionBranch, __, _, formValues) => {
1065
1469
  const conditionalProperty = conditionBranch?.properties?.[fieldParams.name];
1066
1470
  if (conditionalProperty) {
1067
1471
  const presentation = pickXKey(conditionalProperty, "presentation") ?? {};
@@ -1075,17 +1479,31 @@ function calculateConditionalProperties(fieldParams, customProperties) {
1075
1479
  fieldSetFields = rebuildFieldset(fieldParams.fields, conditionalProperty);
1076
1480
  newFieldParams.fields = fieldSetFields;
1077
1481
  }
1482
+ const { computedAttributes, ...restNewFieldParams } = newFieldParams;
1483
+ const calculatedComputedAttributes = computedAttributes ? calculateComputedAttributes(newFieldParams, config)({ validations, formValues }) : {};
1484
+ const requiredValidations = [
1485
+ ...fieldParams.requiredValidations ?? [],
1486
+ ...restNewFieldParams.requiredValidations ?? []
1487
+ ];
1078
1488
  const base = {
1079
1489
  isVisible: true,
1080
1490
  required: isRequired,
1081
1491
  ...presentation?.inputType && { type: presentation.inputType },
1082
- schema: buildYupSchema({
1083
- ...fieldParams,
1084
- ...newFieldParams,
1085
- // If there are inner fields (case of fieldset) they need to be updated based on the condition
1086
- fields: fieldSetFields,
1087
- required: isRequired
1088
- })
1492
+ ...calculatedComputedAttributes,
1493
+ ...calculatedComputedAttributes.value ? { value: calculatedComputedAttributes.value } : { value: void 0 },
1494
+ schema: buildYupSchema(
1495
+ {
1496
+ ...fieldParams,
1497
+ ...restNewFieldParams,
1498
+ ...calculatedComputedAttributes,
1499
+ requiredValidations,
1500
+ // If there are inner fields (case of fieldset) they need to be updated based on the condition
1501
+ fields: fieldSetFields,
1502
+ required: isRequired
1503
+ },
1504
+ config,
1505
+ validations
1506
+ )
1089
1507
  };
1090
1508
  return (0, import_omit2.default)((0, import_merge2.default)(base, presentation, newFieldParams), ["inputType"]);
1091
1509
  }
@@ -1179,14 +1597,19 @@ function sortByOrderOrPosition(a, b, order) {
1179
1597
  function removeInvalidAttributes(fields) {
1180
1598
  return (0, import_omit3.default)(fields, ["items", "maxFileSize", "isDynamic"]);
1181
1599
  }
1182
- function buildFieldParameters(name, fieldProperties, required = [], config = {}) {
1600
+ function buildFieldParameters(name, fieldProperties, required = [], config = {}, validations) {
1183
1601
  const { position } = pickXKey(fieldProperties, "presentation") ?? {};
1184
1602
  let fields;
1185
1603
  const inputType = getInputType(fieldProperties, config.strictInputType, name);
1186
1604
  if (inputType === supportedTypes.FIELDSET) {
1187
- fields = getFieldsFromJSONSchema(fieldProperties, {
1188
- customProperties: (0, import_get3.default)(config, `customProperties.${name}`, {})
1189
- });
1605
+ fields = getFieldsFromJSONSchema(
1606
+ fieldProperties,
1607
+ {
1608
+ customProperties: (0, import_get3.default)(config, `customProperties.${name}`, {}),
1609
+ parentID: name
1610
+ },
1611
+ validations
1612
+ );
1190
1613
  }
1191
1614
  const result = {
1192
1615
  name,
@@ -1201,9 +1624,9 @@ function buildFieldParameters(name, fieldProperties, required = [], config = {})
1201
1624
  };
1202
1625
  return (0, import_omitBy2.default)(result, import_isNil3.default);
1203
1626
  }
1204
- function convertJSONSchemaPropertiesToFieldParameters({ properties, required, "x-jsf-order": order }, config = {}) {
1627
+ function convertJSONSchemaPropertiesToFieldParameters({ properties, required, "x-jsf-order": order }, config = {}, validations) {
1205
1628
  const sortFields = (a, b) => sortByOrderOrPosition(a, b, order);
1206
- return Object.entries(properties).filter(([, value]) => typeof value === "object").map(([key, value]) => buildFieldParameters(key, value, required, config)).sort(sortFields).map(({ position, ...fieldParams }) => fieldParams);
1629
+ return Object.entries(properties).filter(([, value]) => typeof value === "object").map(([key, value]) => buildFieldParameters(key, value, required, config, validations)).sort(sortFields).map(({ position, ...fieldParams }) => fieldParams);
1207
1630
  }
1208
1631
  function applyFieldsDependencies(fieldsParameters, node) {
1209
1632
  if (node?.then) {
@@ -1225,6 +1648,9 @@ function applyFieldsDependencies(fieldsParameters, node) {
1225
1648
  applyFieldsDependencies(fieldsParameters, condition);
1226
1649
  });
1227
1650
  }
1651
+ if (node?.["x-jsf-logic"]) {
1652
+ applyFieldsDependencies(fieldsParameters, node["x-jsf-logic"]);
1653
+ }
1228
1654
  }
1229
1655
  function getCustomPropertiesForField(fieldParams, config) {
1230
1656
  return config?.customProperties?.[fieldParams.name];
@@ -1236,15 +1662,16 @@ function getComposeFunctionForField(fieldParams, hasCustomizations) {
1236
1662
  }
1237
1663
  return composeFn;
1238
1664
  }
1239
- function buildField(fieldParams, config, scopedJsonSchema) {
1665
+ function buildField(fieldParams, config, scopedJsonSchema, validations) {
1240
1666
  const customProperties = getCustomPropertiesForField(fieldParams, config);
1241
1667
  const composeFn = getComposeFunctionForField(fieldParams, !!customProperties);
1242
- const yupSchema = buildYupSchema(fieldParams, config);
1243
- const calculateConditionalFieldsClosure = fieldParams.isDynamic && calculateConditionalProperties(fieldParams, customProperties);
1668
+ const yupSchema = buildYupSchema(fieldParams, config, validations);
1669
+ const calculateConditionalFieldsClosure = fieldParams.isDynamic && calculateConditionalProperties(fieldParams, customProperties, validations, config);
1244
1670
  const calculateCustomValidationPropertiesClosure = calculateCustomValidationProperties(
1245
1671
  fieldParams,
1246
1672
  customProperties
1247
1673
  );
1674
+ const getComputedAttributes = Object.keys(fieldParams.computedAttributes).length > 0 && calculateComputedAttributes(fieldParams, config);
1248
1675
  const hasCustomValidations = !!customProperties && (0, import_size.default)((0, import_pick2.default)(customProperties, SUPPORTED_CUSTOM_VALIDATION_FIELD_PARAMS)) > 0;
1249
1676
  const finalFieldParams = {
1250
1677
  // invalid attribute cleanup
@@ -1257,6 +1684,7 @@ function buildField(fieldParams, config, scopedJsonSchema) {
1257
1684
  ...hasCustomValidations && {
1258
1685
  calculateCustomValidationProperties: calculateCustomValidationPropertiesClosure
1259
1686
  },
1687
+ ...getComputedAttributes && { getComputedAttributes },
1260
1688
  // field customization properties
1261
1689
  ...customProperties && { fieldCustomization: customProperties },
1262
1690
  // base schema
@@ -1265,11 +1693,15 @@ function buildField(fieldParams, config, scopedJsonSchema) {
1265
1693
  };
1266
1694
  return composeFn(finalFieldParams);
1267
1695
  }
1268
- function getFieldsFromJSONSchema(scopedJsonSchema, config) {
1696
+ function getFieldsFromJSONSchema(scopedJsonSchema, config, validations) {
1269
1697
  if (!scopedJsonSchema) {
1270
1698
  return [];
1271
1699
  }
1272
- const fieldParamsList = convertJSONSchemaPropertiesToFieldParameters(scopedJsonSchema, config);
1700
+ const fieldParamsList = convertJSONSchemaPropertiesToFieldParameters(
1701
+ scopedJsonSchema,
1702
+ config,
1703
+ validations
1704
+ );
1273
1705
  applyFieldsDependencies(fieldParamsList, scopedJsonSchema);
1274
1706
  const fields = [];
1275
1707
  fieldParamsList.forEach((fieldParams) => {
@@ -1289,11 +1721,11 @@ function getFieldsFromJSONSchema(scopedJsonSchema, config) {
1289
1721
  fields: () => groupArrayFields,
1290
1722
  addFieldText: fieldParams.addFieldText
1291
1723
  };
1292
- buildField(fieldParams, config, scopedJsonSchema).forEach((groupField) => {
1724
+ buildField(fieldParams, config, scopedJsonSchema, validations).forEach((groupField) => {
1293
1725
  fields.push(groupField);
1294
1726
  });
1295
1727
  } else {
1296
- fields.push(buildField(fieldParams, config, scopedJsonSchema));
1728
+ fields.push(buildField(fieldParams, config, scopedJsonSchema, validations));
1297
1729
  }
1298
1730
  });
1299
1731
  return fields;
@@ -1304,9 +1736,15 @@ function createHeadlessForm(jsonSchema, customConfig = {}) {
1304
1736
  ...customConfig
1305
1737
  };
1306
1738
  try {
1307
- const fields = getFieldsFromJSONSchema(jsonSchema, config);
1308
- const handleValidation = handleValuesChange(fields, jsonSchema, config);
1309
- updateFieldsProperties(fields, getPrefillValues(fields, config.initialValues), jsonSchema);
1739
+ const validations = createValidationChecker(jsonSchema);
1740
+ const fields = getFieldsFromJSONSchema(jsonSchema, config, validations);
1741
+ const handleValidation = handleValuesChange(fields, jsonSchema, config, validations);
1742
+ updateFieldsProperties(
1743
+ fields,
1744
+ getPrefillValues(fields, config.initialValues),
1745
+ jsonSchema,
1746
+ validations
1747
+ );
1310
1748
  return {
1311
1749
  fields,
1312
1750
  handleValidation,