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

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.5.0-dev.20230810172154
5
- Generated: Thu, 10 Aug 2023 17:22:35 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.5.0-dev.20230901130231
5
+ Generated: Fri, 01 Sep 2023 13:03:04 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,103 @@ 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
+ 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
407
511
  var DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
408
512
  var baseString = (0, import_yup.string)().trim();
409
513
  var todayDateHint = (/* @__PURE__ */ new Date()).toISOString().substring(0, 10);
@@ -526,7 +630,7 @@ var getYupSchema = ({ inputType, ...field }) => {
526
630
  }
527
631
  return yupSchemas[inputType] || yupSchemasToJsonTypes[jsonType];
528
632
  };
529
- function buildYupSchema(field, config) {
633
+ function buildYupSchema(field, config, logic) {
530
634
  const { inputType, jsonType: jsonTypeValue, errorMessage = {}, ...propertyFields } = field;
531
635
  const isCheckboxBoolean = typeof propertyFields.checkboxValue === "boolean";
532
636
  let baseSchema;
@@ -599,6 +703,13 @@ function buildYupSchema(field, config) {
599
703
  }) : true
600
704
  );
601
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
+ }
602
713
  function withBaseSchema() {
603
714
  const customErrorMsg = errorMessage.type || errorMessageFromConfig.type;
604
715
  if (customErrorMsg) {
@@ -666,6 +777,14 @@ function buildYupSchema(field, config) {
666
777
  if (propertyFields.accept) {
667
778
  validators.push(withFileFormat);
668
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
+ }
669
788
  return (0, import_flow.default)(validators);
670
789
  }
671
790
  function getNoSortEdges(fields = []) {
@@ -705,8 +824,8 @@ function hasType(type, typeName) {
705
824
  function getField(fieldName, fields) {
706
825
  return fields.find(({ name }) => name === fieldName);
707
826
  }
708
- function validateFieldSchema(field, value) {
709
- const validator = buildYupSchema(field);
827
+ function validateFieldSchema(field, value, logic) {
828
+ const validator = buildYupSchema(field, {}, logic);
710
829
  return validator().isValidSync(value);
711
830
  }
712
831
  function compareFormValueWithSchemaValue(formValue, schemaValue) {
@@ -742,7 +861,16 @@ function getPrefillSubFieldValues(field, defaultValues, parentFieldKeyPath) {
742
861
  initialValue[field.name] = subFieldValues;
743
862
  }
744
863
  } else {
745
- initialValue = getPrefillValues([field], initialValue);
864
+ if (typeof initialValue !== "object") {
865
+ console.warn(
866
+ `Field "${parentFieldKeyPath}"'s value is "${initialValue}", but should be type object.`
867
+ );
868
+ initialValue = getPrefillValues([field], {
869
+ // TODO nested fieldsets are not handled
870
+ });
871
+ } else {
872
+ initialValue = getPrefillValues([field], initialValue);
873
+ }
746
874
  }
747
875
  return initialValue;
748
876
  }
@@ -771,7 +899,7 @@ function getPrefillValues(fields, initialValues = {}) {
771
899
  });
772
900
  return initialValues;
773
901
  }
774
- function updateField(field, requiredFields, node, formValues) {
902
+ function updateField(field, requiredFields, node, formValues, logic, config) {
775
903
  if (!field) {
776
904
  return;
777
905
  }
@@ -793,6 +921,17 @@ function updateField(field, requiredFields, node, formValues) {
793
921
  }
794
922
  }
795
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
+ }
796
935
  if (field.calculateConditionalProperties) {
797
936
  const newFieldValues = field.calculateConditionalProperties(fieldIsRequired, node);
798
937
  updateValues(newFieldValues);
@@ -806,33 +945,46 @@ function updateField(field, requiredFields, node, formValues) {
806
945
  updateValues(newFieldValues);
807
946
  }
808
947
  }
809
- 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
+ }) {
810
956
  const requiredFields = new Set(accRequired);
811
957
  Object.keys(node.properties ?? []).forEach((fieldName) => {
812
958
  const field = getField(fieldName, formFields);
813
- updateField(field, requiredFields, node, formValues);
959
+ updateField(field, requiredFields, node, formValues, logic, { parentID });
814
960
  });
815
961
  node.required?.forEach((fieldName) => {
816
962
  requiredFields.add(fieldName);
817
- updateField(getField(fieldName, formFields), requiredFields, node, formValues);
963
+ updateField(getField(fieldName, formFields), requiredFields, node, formValues, logic, {
964
+ parentID
965
+ });
818
966
  });
819
967
  if (node.if) {
820
- const matchesCondition = checkIfConditionMatches(node, formValues, formFields);
968
+ const matchesCondition = checkIfConditionMatches(node, formValues, formFields, logic);
821
969
  if (matchesCondition && node.then) {
822
- const { required: branchRequired } = processNode(
823
- node.then,
970
+ const { required: branchRequired } = processNode({
971
+ node: node.then,
824
972
  formValues,
825
973
  formFields,
826
- requiredFields
827
- );
974
+ accRequired: requiredFields,
975
+ parentID,
976
+ logic
977
+ });
828
978
  branchRequired.forEach((field) => requiredFields.add(field));
829
979
  } else if (node.else) {
830
- const { required: branchRequired } = processNode(
831
- node.else,
980
+ const { required: branchRequired } = processNode({
981
+ node: node.else,
832
982
  formValues,
833
983
  formFields,
834
- requiredFields
835
- );
984
+ accRequired: requiredFields,
985
+ parentID,
986
+ logic
987
+ });
836
988
  branchRequired.forEach((field) => requiredFields.add(field));
837
989
  }
838
990
  }
@@ -844,12 +996,21 @@ function processNode(node, formValues, formFields, accRequired = /* @__PURE__ */
844
996
  node.anyOf.forEach(({ required = [] }) => {
845
997
  required.forEach((fieldName) => {
846
998
  const field = getField(fieldName, formFields);
847
- updateField(field, requiredFields, node, formValues);
999
+ updateField(field, requiredFields, node, formValues, logic, { parentID });
848
1000
  });
849
1001
  });
850
1002
  }
851
1003
  if (node.allOf) {
852
- 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 }) => {
853
1014
  allOfItemRequired.forEach(requiredFields.add, requiredFields);
854
1015
  });
855
1016
  }
@@ -857,7 +1018,13 @@ function processNode(node, formValues, formFields, accRequired = /* @__PURE__ */
857
1018
  Object.entries(node.properties).forEach(([name, nestedNode]) => {
858
1019
  const inputType = getInputType(nestedNode);
859
1020
  if (inputType === supportedTypes.FIELDSET) {
860
- 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
+ });
861
1028
  }
862
1029
  });
863
1030
  }
@@ -875,11 +1042,11 @@ function clearValuesIfNotVisible(fields, formValues) {
875
1042
  }
876
1043
  });
877
1044
  }
878
- function updateFieldsProperties(fields, formValues, jsonSchema) {
1045
+ function updateFieldsProperties(fields, formValues, jsonSchema, logic) {
879
1046
  if (!jsonSchema?.properties) {
880
1047
  return;
881
1048
  }
882
- processNode(jsonSchema, formValues, fields);
1049
+ processNode({ node: jsonSchema, formValues, formFields: fields, logic });
883
1050
  clearValuesIfNotVisible(fields, formValues);
884
1051
  }
885
1052
  var notNullOption = (opt) => opt.const !== null;
@@ -922,11 +1089,15 @@ function extractParametersFromNode(schemaNode) {
922
1089
  }
923
1090
  const presentation = pickXKey(schemaNode, "presentation") ?? {};
924
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);
925
1095
  const node = (0, import_omit.default)(schemaNode, ["x-jsf-presentation", "presentation"]);
926
1096
  const description = presentation?.description || node.description;
927
- const statementDescription = presentation.statement?.description;
1097
+ const statementDescription = containsHTML(presentation.statement?.description) ? wrapWithSpan(presentation.statement.description, { class: "jsf-statement" }) : presentation.statement?.description;
928
1098
  return (0, import_omitBy.default)(
929
1099
  {
1100
+ const: node.const,
930
1101
  label: node.title,
931
1102
  readOnly: node.readOnly,
932
1103
  ...node.deprecated && {
@@ -961,8 +1132,12 @@ function extractParametersFromNode(schemaNode) {
961
1132
  },
962
1133
  // Handle [name].presentation
963
1134
  ...presentation,
964
- description,
965
- extra: presentation.extra,
1135
+ requiredValidations,
1136
+ computedAttributes: decoratedComputedAttributes,
1137
+ description: containsHTML(description) ? wrapWithSpan(description, {
1138
+ class: "jsf-description"
1139
+ }) : description,
1140
+ extra: containsHTML(presentation.extra) ? wrapWithSpan(presentation.extra, { class: "jsf-extra" }) : presentation.extra,
966
1141
  statement: presentation.statement && {
967
1142
  ...presentation.statement,
968
1143
  description: statementDescription
@@ -995,8 +1170,8 @@ function yupToFormErrors(yupError) {
995
1170
  }
996
1171
  return errors;
997
1172
  }
998
- var handleValuesChange = (fields, jsonSchema, config) => (values) => {
999
- updateFieldsProperties(fields, values, jsonSchema);
1173
+ var handleValuesChange = (fields, jsonSchema, config, logic) => (values) => {
1174
+ updateFieldsProperties(fields, values, jsonSchema, logic);
1000
1175
  const lazySchema = (0, import_yup2.lazy)(() => buildCompleteYupSchema(fields, config));
1001
1176
  let errors;
1002
1177
  try {
@@ -1015,6 +1190,12 @@ var handleValuesChange = (fields, jsonSchema, config) => (values) => {
1015
1190
  formErrors: yupToFormErrors(errors)
1016
1191
  };
1017
1192
  };
1193
+ function getDecoratedComputedAttributes(computedAttributes) {
1194
+ return {
1195
+ ...computedAttributes ?? {},
1196
+ ...computedAttributes?.const && computedAttributes?.default ? { value: computedAttributes.const } : {}
1197
+ };
1198
+ }
1018
1199
 
1019
1200
  // src/calculateConditionalProperties.js
1020
1201
  function isFieldRequired(node, field) {
@@ -1216,6 +1397,9 @@ function applyFieldsDependencies(fieldsParameters, node) {
1216
1397
  applyFieldsDependencies(fieldsParameters, condition);
1217
1398
  });
1218
1399
  }
1400
+ if (node?.["x-jsf-logic"]) {
1401
+ applyFieldsDependencies(fieldsParameters, node["x-jsf-logic"]);
1402
+ }
1219
1403
  }
1220
1404
  function getCustomPropertiesForField(fieldParams, config) {
1221
1405
  return config?.customProperties?.[fieldParams.name];
@@ -1227,15 +1411,16 @@ function getComposeFunctionForField(fieldParams, hasCustomizations) {
1227
1411
  }
1228
1412
  return composeFn;
1229
1413
  }
1230
- function buildField(fieldParams, config, scopedJsonSchema) {
1414
+ function buildField(fieldParams, config, scopedJsonSchema, logic) {
1231
1415
  const customProperties = getCustomPropertiesForField(fieldParams, config);
1232
1416
  const composeFn = getComposeFunctionForField(fieldParams, !!customProperties);
1233
- const yupSchema = buildYupSchema(fieldParams, config);
1417
+ const yupSchema = buildYupSchema(fieldParams, config, logic);
1234
1418
  const calculateConditionalFieldsClosure = fieldParams.isDynamic && calculateConditionalProperties(fieldParams, customProperties);
1235
1419
  const calculateCustomValidationPropertiesClosure = calculateCustomValidationProperties(
1236
1420
  fieldParams,
1237
1421
  customProperties
1238
1422
  );
1423
+ const getComputedAttributes = Object.keys(fieldParams.computedAttributes).length > 0 && calculateComputedAttributes(fieldParams, config);
1239
1424
  const hasCustomValidations = !!customProperties && (0, import_size.default)((0, import_pick2.default)(customProperties, SUPPORTED_CUSTOM_VALIDATION_FIELD_PARAMS)) > 0;
1240
1425
  const finalFieldParams = {
1241
1426
  // invalid attribute cleanup
@@ -1248,6 +1433,7 @@ function buildField(fieldParams, config, scopedJsonSchema) {
1248
1433
  ...hasCustomValidations && {
1249
1434
  calculateCustomValidationProperties: calculateCustomValidationPropertiesClosure
1250
1435
  },
1436
+ ...getComputedAttributes && { getComputedAttributes },
1251
1437
  // field customization properties
1252
1438
  ...customProperties && { fieldCustomization: customProperties },
1253
1439
  // base schema
@@ -1256,7 +1442,7 @@ function buildField(fieldParams, config, scopedJsonSchema) {
1256
1442
  };
1257
1443
  return composeFn(finalFieldParams);
1258
1444
  }
1259
- function getFieldsFromJSONSchema(scopedJsonSchema, config) {
1445
+ function getFieldsFromJSONSchema(scopedJsonSchema, config, logic) {
1260
1446
  if (!scopedJsonSchema) {
1261
1447
  return [];
1262
1448
  }
@@ -1280,11 +1466,11 @@ function getFieldsFromJSONSchema(scopedJsonSchema, config) {
1280
1466
  fields: () => groupArrayFields,
1281
1467
  addFieldText: fieldParams.addFieldText
1282
1468
  };
1283
- buildField(fieldParams, config, scopedJsonSchema).forEach((groupField) => {
1469
+ buildField(fieldParams, config, scopedJsonSchema, logic).forEach((groupField) => {
1284
1470
  fields.push(groupField);
1285
1471
  });
1286
1472
  } else {
1287
- fields.push(buildField(fieldParams, config, scopedJsonSchema));
1473
+ fields.push(buildField(fieldParams, config, scopedJsonSchema, logic));
1288
1474
  }
1289
1475
  });
1290
1476
  return fields;
@@ -1295,9 +1481,15 @@ function createHeadlessForm(jsonSchema, customConfig = {}) {
1295
1481
  ...customConfig
1296
1482
  };
1297
1483
  try {
1298
- const fields = getFieldsFromJSONSchema(jsonSchema, config);
1299
- const handleValidation = handleValuesChange(fields, jsonSchema, config);
1300
- updateFieldsProperties(fields, getPrefillValues(fields, config.initialValues), jsonSchema);
1484
+ const logic = createValidationChecker(jsonSchema);
1485
+ const fields = getFieldsFromJSONSchema(jsonSchema, config, logic);
1486
+ const handleValidation = handleValuesChange(fields, jsonSchema, config, logic);
1487
+ updateFieldsProperties(
1488
+ fields,
1489
+ getPrefillValues(fields, config.initialValues),
1490
+ jsonSchema,
1491
+ logic
1492
+ );
1301
1493
  return {
1302
1494
  fields,
1303
1495
  handleValidation,