@remoteoss/json-schema-form 0.4.4-dev.20230830125207 → 0.4.6-dev.20230911130751

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,15 @@
1
+ #### 0.4.5-beta.0 (2023-08-31)
2
+
3
+ ##### Changes
4
+
5
+ * 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))
6
+
7
+ #### 0.4.4-beta.0 (2023-08-30)
8
+
9
+ ##### Chores
10
+
11
+ * **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))
12
+
1
13
  #### 0.4.3-beta.0 (2023-08-09)
2
14
 
3
15
  ##### 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.4.4-dev.20230830125207
5
- Generated: Wed, 30 Aug 2023 12:52:17 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.4.6-dev.20230911130751
5
+ Generated: Mon, 11 Sep 2023 13:08:22 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -411,6 +411,103 @@ var import_flow = __toESM(require("lodash/flow"));
411
411
  var import_noop = __toESM(require("lodash/noop"));
412
412
  var import_randexp = require("randexp");
413
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
+ scopes.set(key, createValidationsScope(jsonSchema));
421
+ Object.entries(jsonSchema?.properties ?? {}).filter(([, property]) => property.type === "object" || property.type === "array").forEach(([key2, property]) => {
422
+ if (property.type === "array") {
423
+ createScopes(property.items, `${key2}[]`);
424
+ } else {
425
+ createScopes(property, key2);
426
+ }
427
+ });
428
+ }
429
+ createScopes(schema);
430
+ return {
431
+ scopes,
432
+ getScope(name = "root") {
433
+ return scopes.get(name);
434
+ }
435
+ };
436
+ }
437
+ function createValidationsScope(schema) {
438
+ const validationMap = /* @__PURE__ */ new Map();
439
+ const computedValuesMap = /* @__PURE__ */ new Map();
440
+ const logic = schema?.["x-jsf-logic"] ?? {
441
+ validations: {},
442
+ computedValues: {}
443
+ };
444
+ const validations = Object.entries(logic.validations ?? {});
445
+ const computedValues = Object.entries(logic.computedValues ?? {});
446
+ validations.forEach(([id, validation]) => {
447
+ validationMap.set(id, validation);
448
+ });
449
+ computedValues.forEach(([id, computedValue]) => {
450
+ computedValuesMap.set(id, computedValue);
451
+ });
452
+ function validate(rule, values) {
453
+ return import_json_logic_js.default.apply(rule, replaceUndefinedValuesWithNulls(values));
454
+ }
455
+ return {
456
+ validationMap,
457
+ computedValuesMap,
458
+ validate,
459
+ applyValidationRuleInCondition(id, values) {
460
+ const validation = validationMap.get(id);
461
+ return validate(validation.rule, values);
462
+ },
463
+ applyComputedValueInField(id, values) {
464
+ const validation = computedValuesMap.get(id);
465
+ return validate(validation.rule, values);
466
+ },
467
+ applyComputedValueRuleInCondition(id, values) {
468
+ const validation = computedValuesMap.get(id);
469
+ return validate(validation.rule, values);
470
+ }
471
+ };
472
+ }
473
+ function replaceUndefinedValuesWithNulls(values = {}) {
474
+ return Object.entries(values).reduce((prev, [key, value]) => {
475
+ return { ...prev, [key]: value === void 0 ? null : value };
476
+ }, {});
477
+ }
478
+ function yupSchemaWithCustomJSONLogic({ field, logic, config, id }) {
479
+ const { parentID = "root" } = config;
480
+ const validation = logic.getScope(parentID).validationMap.get(id);
481
+ return (yupSchema) => yupSchema.test(
482
+ `${field.name}-validation-${id}`,
483
+ validation?.errorMessage ?? "This field is invalid.",
484
+ (value, { parent }) => {
485
+ if (value === void 0 && !field.required)
486
+ return true;
487
+ return import_json_logic_js.default.apply(validation.rule, parent);
488
+ }
489
+ );
490
+ }
491
+ function calculateComputedAttributes(fieldParams, { parentID = "root" } = {}) {
492
+ return ({ logic, formValues }) => {
493
+ const { computedAttributes } = fieldParams;
494
+ const attributes = Object.fromEntries(
495
+ Object.entries(computedAttributes).map(handleComputedAttribute(logic, formValues, parentID)).filter(([, value]) => value !== null)
496
+ );
497
+ return attributes;
498
+ };
499
+ }
500
+ function handleComputedAttribute(logic, formValues, parentID) {
501
+ return ([key, value]) => {
502
+ if (key === "const")
503
+ return [key, logic.getScope(parentID).applyComputedValueInField(value, formValues)];
504
+ if (typeof value === "string") {
505
+ return [key, logic.getScope(parentID).applyComputedValueInField(value, formValues)];
506
+ }
507
+ };
508
+ }
509
+
510
+ // src/yupSchema.js
414
511
  var DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
415
512
  var baseString = (0, import_yup.string)().trim();
416
513
  var todayDateHint = (/* @__PURE__ */ new Date()).toISOString().substring(0, 10);
@@ -533,7 +630,7 @@ var getYupSchema = ({ inputType, ...field }) => {
533
630
  }
534
631
  return yupSchemas[inputType] || yupSchemasToJsonTypes[jsonType];
535
632
  };
536
- function buildYupSchema(field, config) {
633
+ function buildYupSchema(field, config, logic) {
537
634
  const { inputType, jsonType: jsonTypeValue, errorMessage = {}, ...propertyFields } = field;
538
635
  const isCheckboxBoolean = typeof propertyFields.checkboxValue === "boolean";
539
636
  let baseSchema;
@@ -606,6 +703,13 @@ function buildYupSchema(field, config) {
606
703
  }) : true
607
704
  );
608
705
  }
706
+ function withConst(yupSchema) {
707
+ return yupSchema.test(
708
+ "isConst",
709
+ errorMessage.const ?? errorMessageFromConfig.const ?? `The only accepted value is ${propertyFields.const}.`,
710
+ (value) => propertyFields.required === false && value === void 0 || value === null || value === propertyFields.const
711
+ );
712
+ }
609
713
  function withBaseSchema() {
610
714
  const customErrorMsg = errorMessage.type || errorMessageFromConfig.type;
611
715
  if (customErrorMsg) {
@@ -673,6 +777,14 @@ function buildYupSchema(field, config) {
673
777
  if (propertyFields.accept) {
674
778
  validators.push(withFileFormat);
675
779
  }
780
+ if (propertyFields.const) {
781
+ validators.push(withConst);
782
+ }
783
+ if (propertyFields.requiredValidations) {
784
+ propertyFields.requiredValidations.forEach(
785
+ (id) => validators.push(yupSchemaWithCustomJSONLogic({ field, id, logic, config }))
786
+ );
787
+ }
676
788
  return (0, import_flow.default)(validators);
677
789
  }
678
790
  function getNoSortEdges(fields = []) {
@@ -712,8 +824,8 @@ function hasType(type, typeName) {
712
824
  function getField(fieldName, fields) {
713
825
  return fields.find(({ name }) => name === fieldName);
714
826
  }
715
- function validateFieldSchema(field, value) {
716
- const validator = buildYupSchema(field);
827
+ function validateFieldSchema(field, value, logic) {
828
+ const validator = buildYupSchema(field, {}, logic);
717
829
  return validator().isValidSync(value);
718
830
  }
719
831
  function compareFormValueWithSchemaValue(formValue, schemaValue) {
@@ -787,7 +899,7 @@ function getPrefillValues(fields, initialValues = {}) {
787
899
  });
788
900
  return initialValues;
789
901
  }
790
- function updateField(field, requiredFields, node, formValues) {
902
+ function updateField(field, requiredFields, node, formValues, logic, config) {
791
903
  if (!field) {
792
904
  return;
793
905
  }
@@ -809,6 +921,17 @@ function updateField(field, requiredFields, node, formValues) {
809
921
  }
810
922
  }
811
923
  });
924
+ if (field.getComputedAttributes) {
925
+ const computedFieldValues = field.getComputedAttributes({
926
+ field,
927
+ isRequired: fieldIsRequired,
928
+ node,
929
+ formValues,
930
+ config,
931
+ logic
932
+ });
933
+ updateValues(computedFieldValues);
934
+ }
812
935
  if (field.calculateConditionalProperties) {
813
936
  const newFieldValues = field.calculateConditionalProperties(fieldIsRequired, node);
814
937
  updateValues(newFieldValues);
@@ -822,33 +945,46 @@ function updateField(field, requiredFields, node, formValues) {
822
945
  updateValues(newFieldValues);
823
946
  }
824
947
  }
825
- function processNode(node, formValues, formFields, accRequired = /* @__PURE__ */ new Set()) {
948
+ function processNode({
949
+ node,
950
+ formValues,
951
+ formFields,
952
+ accRequired = /* @__PURE__ */ new Set(),
953
+ parentID = "root",
954
+ logic
955
+ }) {
826
956
  const requiredFields = new Set(accRequired);
827
957
  Object.keys(node.properties ?? []).forEach((fieldName) => {
828
958
  const field = getField(fieldName, formFields);
829
- updateField(field, requiredFields, node, formValues);
959
+ updateField(field, requiredFields, node, formValues, logic, { parentID });
830
960
  });
831
961
  node.required?.forEach((fieldName) => {
832
962
  requiredFields.add(fieldName);
833
- updateField(getField(fieldName, formFields), requiredFields, node, formValues);
963
+ updateField(getField(fieldName, formFields), requiredFields, node, formValues, logic, {
964
+ parentID
965
+ });
834
966
  });
835
967
  if (node.if) {
836
- const matchesCondition = checkIfConditionMatches(node, formValues, formFields);
968
+ const matchesCondition = checkIfConditionMatches(node, formValues, formFields, logic);
837
969
  if (matchesCondition && node.then) {
838
- const { required: branchRequired } = processNode(
839
- node.then,
970
+ const { required: branchRequired } = processNode({
971
+ node: node.then,
840
972
  formValues,
841
973
  formFields,
842
- requiredFields
843
- );
974
+ accRequired: requiredFields,
975
+ parentID,
976
+ logic
977
+ });
844
978
  branchRequired.forEach((field) => requiredFields.add(field));
845
979
  } else if (node.else) {
846
- const { required: branchRequired } = processNode(
847
- node.else,
980
+ const { required: branchRequired } = processNode({
981
+ node: node.else,
848
982
  formValues,
849
983
  formFields,
850
- requiredFields
851
- );
984
+ accRequired: requiredFields,
985
+ parentID,
986
+ logic
987
+ });
852
988
  branchRequired.forEach((field) => requiredFields.add(field));
853
989
  }
854
990
  }
@@ -860,12 +996,21 @@ function processNode(node, formValues, formFields, accRequired = /* @__PURE__ */
860
996
  node.anyOf.forEach(({ required = [] }) => {
861
997
  required.forEach((fieldName) => {
862
998
  const field = getField(fieldName, formFields);
863
- updateField(field, requiredFields, node, formValues);
999
+ updateField(field, requiredFields, node, formValues, logic, { parentID });
864
1000
  });
865
1001
  });
866
1002
  }
867
1003
  if (node.allOf) {
868
- node.allOf.map((allOfNode) => processNode(allOfNode, formValues, formFields, requiredFields)).forEach(({ required: allOfItemRequired }) => {
1004
+ node.allOf.map(
1005
+ (allOfNode) => processNode({
1006
+ node: allOfNode,
1007
+ formValues,
1008
+ formFields,
1009
+ accRequired: requiredFields,
1010
+ parentID,
1011
+ logic
1012
+ })
1013
+ ).forEach(({ required: allOfItemRequired }) => {
869
1014
  allOfItemRequired.forEach(requiredFields.add, requiredFields);
870
1015
  });
871
1016
  }
@@ -873,7 +1018,13 @@ function processNode(node, formValues, formFields, accRequired = /* @__PURE__ */
873
1018
  Object.entries(node.properties).forEach(([name, nestedNode]) => {
874
1019
  const inputType = getInputType(nestedNode);
875
1020
  if (inputType === supportedTypes.FIELDSET) {
876
- processNode(nestedNode, formValues[name] || {}, getField(name, formFields).fields);
1021
+ processNode({
1022
+ node: nestedNode,
1023
+ formValues: formValues[name] || {},
1024
+ formFields: getField(name, formFields).fields,
1025
+ parentID: name,
1026
+ logic
1027
+ });
877
1028
  }
878
1029
  });
879
1030
  }
@@ -891,11 +1042,11 @@ function clearValuesIfNotVisible(fields, formValues) {
891
1042
  }
892
1043
  });
893
1044
  }
894
- function updateFieldsProperties(fields, formValues, jsonSchema) {
1045
+ function updateFieldsProperties(fields, formValues, jsonSchema, logic) {
895
1046
  if (!jsonSchema?.properties) {
896
1047
  return;
897
1048
  }
898
- processNode(jsonSchema, formValues, fields);
1049
+ processNode({ node: jsonSchema, formValues, formFields: fields, logic });
899
1050
  clearValuesIfNotVisible(fields, formValues);
900
1051
  }
901
1052
  var notNullOption = (opt) => opt.const !== null;
@@ -938,11 +1089,16 @@ function extractParametersFromNode(schemaNode) {
938
1089
  }
939
1090
  const presentation = pickXKey(schemaNode, "presentation") ?? {};
940
1091
  const errorMessage = pickXKey(schemaNode, "errorMessage") ?? {};
1092
+ const requiredValidations = schemaNode["x-jsf-logic-validations"];
1093
+ const computedAttributes = schemaNode["x-jsf-logic-computedAttrs"];
1094
+ const decoratedComputedAttributes = getDecoratedComputedAttributes(computedAttributes);
941
1095
  const node = (0, import_omit.default)(schemaNode, ["x-jsf-presentation", "presentation"]);
942
1096
  const description = presentation?.description || node.description;
943
1097
  const statementDescription = containsHTML(presentation.statement?.description) ? wrapWithSpan(presentation.statement.description, { class: "jsf-statement" }) : presentation.statement?.description;
944
1098
  return (0, import_omitBy.default)(
945
1099
  {
1100
+ const: node.const,
1101
+ ...node.const && node.default ? { value: node.const } : {},
946
1102
  label: node.title,
947
1103
  readOnly: node.readOnly,
948
1104
  ...node.deprecated && {
@@ -977,6 +1133,8 @@ function extractParametersFromNode(schemaNode) {
977
1133
  },
978
1134
  // Handle [name].presentation
979
1135
  ...presentation,
1136
+ requiredValidations,
1137
+ computedAttributes: decoratedComputedAttributes,
980
1138
  description: containsHTML(description) ? wrapWithSpan(description, {
981
1139
  class: "jsf-description"
982
1140
  }) : description,
@@ -1013,8 +1171,8 @@ function yupToFormErrors(yupError) {
1013
1171
  }
1014
1172
  return errors;
1015
1173
  }
1016
- var handleValuesChange = (fields, jsonSchema, config) => (values) => {
1017
- updateFieldsProperties(fields, values, jsonSchema);
1174
+ var handleValuesChange = (fields, jsonSchema, config, logic) => (values) => {
1175
+ updateFieldsProperties(fields, values, jsonSchema, logic);
1018
1176
  const lazySchema = (0, import_yup2.lazy)(() => buildCompleteYupSchema(fields, config));
1019
1177
  let errors;
1020
1178
  try {
@@ -1033,6 +1191,12 @@ var handleValuesChange = (fields, jsonSchema, config) => (values) => {
1033
1191
  formErrors: yupToFormErrors(errors)
1034
1192
  };
1035
1193
  };
1194
+ function getDecoratedComputedAttributes(computedAttributes) {
1195
+ return {
1196
+ ...computedAttributes ?? {},
1197
+ ...computedAttributes?.const && computedAttributes?.default ? { value: computedAttributes.const } : {}
1198
+ };
1199
+ }
1036
1200
 
1037
1201
  // src/calculateConditionalProperties.js
1038
1202
  function isFieldRequired(node, field) {
@@ -1234,6 +1398,9 @@ function applyFieldsDependencies(fieldsParameters, node) {
1234
1398
  applyFieldsDependencies(fieldsParameters, condition);
1235
1399
  });
1236
1400
  }
1401
+ if (node?.["x-jsf-logic"]) {
1402
+ applyFieldsDependencies(fieldsParameters, node["x-jsf-logic"]);
1403
+ }
1237
1404
  }
1238
1405
  function getCustomPropertiesForField(fieldParams, config) {
1239
1406
  return config?.customProperties?.[fieldParams.name];
@@ -1245,15 +1412,16 @@ function getComposeFunctionForField(fieldParams, hasCustomizations) {
1245
1412
  }
1246
1413
  return composeFn;
1247
1414
  }
1248
- function buildField(fieldParams, config, scopedJsonSchema) {
1415
+ function buildField(fieldParams, config, scopedJsonSchema, logic) {
1249
1416
  const customProperties = getCustomPropertiesForField(fieldParams, config);
1250
1417
  const composeFn = getComposeFunctionForField(fieldParams, !!customProperties);
1251
- const yupSchema = buildYupSchema(fieldParams, config);
1418
+ const yupSchema = buildYupSchema(fieldParams, config, logic);
1252
1419
  const calculateConditionalFieldsClosure = fieldParams.isDynamic && calculateConditionalProperties(fieldParams, customProperties);
1253
1420
  const calculateCustomValidationPropertiesClosure = calculateCustomValidationProperties(
1254
1421
  fieldParams,
1255
1422
  customProperties
1256
1423
  );
1424
+ const getComputedAttributes = Object.keys(fieldParams.computedAttributes).length > 0 && calculateComputedAttributes(fieldParams, config);
1257
1425
  const hasCustomValidations = !!customProperties && (0, import_size.default)((0, import_pick2.default)(customProperties, SUPPORTED_CUSTOM_VALIDATION_FIELD_PARAMS)) > 0;
1258
1426
  const finalFieldParams = {
1259
1427
  // invalid attribute cleanup
@@ -1266,6 +1434,7 @@ function buildField(fieldParams, config, scopedJsonSchema) {
1266
1434
  ...hasCustomValidations && {
1267
1435
  calculateCustomValidationProperties: calculateCustomValidationPropertiesClosure
1268
1436
  },
1437
+ ...getComputedAttributes && { getComputedAttributes },
1269
1438
  // field customization properties
1270
1439
  ...customProperties && { fieldCustomization: customProperties },
1271
1440
  // base schema
@@ -1274,7 +1443,7 @@ function buildField(fieldParams, config, scopedJsonSchema) {
1274
1443
  };
1275
1444
  return composeFn(finalFieldParams);
1276
1445
  }
1277
- function getFieldsFromJSONSchema(scopedJsonSchema, config) {
1446
+ function getFieldsFromJSONSchema(scopedJsonSchema, config, logic) {
1278
1447
  if (!scopedJsonSchema) {
1279
1448
  return [];
1280
1449
  }
@@ -1298,11 +1467,11 @@ function getFieldsFromJSONSchema(scopedJsonSchema, config) {
1298
1467
  fields: () => groupArrayFields,
1299
1468
  addFieldText: fieldParams.addFieldText
1300
1469
  };
1301
- buildField(fieldParams, config, scopedJsonSchema).forEach((groupField) => {
1470
+ buildField(fieldParams, config, scopedJsonSchema, logic).forEach((groupField) => {
1302
1471
  fields.push(groupField);
1303
1472
  });
1304
1473
  } else {
1305
- fields.push(buildField(fieldParams, config, scopedJsonSchema));
1474
+ fields.push(buildField(fieldParams, config, scopedJsonSchema, logic));
1306
1475
  }
1307
1476
  });
1308
1477
  return fields;
@@ -1313,9 +1482,15 @@ function createHeadlessForm(jsonSchema, customConfig = {}) {
1313
1482
  ...customConfig
1314
1483
  };
1315
1484
  try {
1316
- const fields = getFieldsFromJSONSchema(jsonSchema, config);
1317
- const handleValidation = handleValuesChange(fields, jsonSchema, config);
1318
- updateFieldsProperties(fields, getPrefillValues(fields, config.initialValues), jsonSchema);
1485
+ const logic = createValidationChecker(jsonSchema);
1486
+ const fields = getFieldsFromJSONSchema(jsonSchema, config, logic);
1487
+ const handleValidation = handleValuesChange(fields, jsonSchema, config, logic);
1488
+ updateFieldsProperties(
1489
+ fields,
1490
+ getPrefillValues(fields, config.initialValues),
1491
+ jsonSchema,
1492
+ logic
1493
+ );
1319
1494
  return {
1320
1495
  fields,
1321
1496
  handleValidation,