@remoteoss/json-schema-form 0.4.5-beta.0 → 0.5.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
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
+
1
8
  #### 0.4.5-beta.0 (2023-08-31)
2
9
 
3
10
  ##### Changes
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.5-beta.0
5
- Generated: Thu, 31 Aug 2023 07:23:05 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.5.0-beta.0
5
+ Generated: Tue, 12 Sep 2023 07:51:25 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;
@@ -683,6 +780,11 @@ function buildYupSchema(field, config) {
683
780
  if (propertyFields.const) {
684
781
  validators.push(withConst);
685
782
  }
783
+ if (propertyFields.requiredValidations) {
784
+ propertyFields.requiredValidations.forEach(
785
+ (id) => validators.push(yupSchemaWithCustomJSONLogic({ field, id, logic, config }))
786
+ );
787
+ }
686
788
  return (0, import_flow.default)(validators);
687
789
  }
688
790
  function getNoSortEdges(fields = []) {
@@ -722,8 +824,8 @@ function hasType(type, typeName) {
722
824
  function getField(fieldName, fields) {
723
825
  return fields.find(({ name }) => name === fieldName);
724
826
  }
725
- function validateFieldSchema(field, value) {
726
- const validator = buildYupSchema(field);
827
+ function validateFieldSchema(field, value, logic) {
828
+ const validator = buildYupSchema(field, {}, logic);
727
829
  return validator().isValidSync(value);
728
830
  }
729
831
  function compareFormValueWithSchemaValue(formValue, schemaValue) {
@@ -797,7 +899,7 @@ function getPrefillValues(fields, initialValues = {}) {
797
899
  });
798
900
  return initialValues;
799
901
  }
800
- function updateField(field, requiredFields, node, formValues) {
902
+ function updateField(field, requiredFields, node, formValues, logic, config) {
801
903
  if (!field) {
802
904
  return;
803
905
  }
@@ -819,6 +921,17 @@ function updateField(field, requiredFields, node, formValues) {
819
921
  }
820
922
  }
821
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
+ }
822
935
  if (field.calculateConditionalProperties) {
823
936
  const newFieldValues = field.calculateConditionalProperties(fieldIsRequired, node);
824
937
  updateValues(newFieldValues);
@@ -832,33 +945,46 @@ function updateField(field, requiredFields, node, formValues) {
832
945
  updateValues(newFieldValues);
833
946
  }
834
947
  }
835
- 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
+ }) {
836
956
  const requiredFields = new Set(accRequired);
837
957
  Object.keys(node.properties ?? []).forEach((fieldName) => {
838
958
  const field = getField(fieldName, formFields);
839
- updateField(field, requiredFields, node, formValues);
959
+ updateField(field, requiredFields, node, formValues, logic, { parentID });
840
960
  });
841
961
  node.required?.forEach((fieldName) => {
842
962
  requiredFields.add(fieldName);
843
- updateField(getField(fieldName, formFields), requiredFields, node, formValues);
963
+ updateField(getField(fieldName, formFields), requiredFields, node, formValues, logic, {
964
+ parentID
965
+ });
844
966
  });
845
967
  if (node.if) {
846
- const matchesCondition = checkIfConditionMatches(node, formValues, formFields);
968
+ const matchesCondition = checkIfConditionMatches(node, formValues, formFields, logic);
847
969
  if (matchesCondition && node.then) {
848
- const { required: branchRequired } = processNode(
849
- node.then,
970
+ const { required: branchRequired } = processNode({
971
+ node: node.then,
850
972
  formValues,
851
973
  formFields,
852
- requiredFields
853
- );
974
+ accRequired: requiredFields,
975
+ parentID,
976
+ logic
977
+ });
854
978
  branchRequired.forEach((field) => requiredFields.add(field));
855
979
  } else if (node.else) {
856
- const { required: branchRequired } = processNode(
857
- node.else,
980
+ const { required: branchRequired } = processNode({
981
+ node: node.else,
858
982
  formValues,
859
983
  formFields,
860
- requiredFields
861
- );
984
+ accRequired: requiredFields,
985
+ parentID,
986
+ logic
987
+ });
862
988
  branchRequired.forEach((field) => requiredFields.add(field));
863
989
  }
864
990
  }
@@ -870,12 +996,21 @@ function processNode(node, formValues, formFields, accRequired = /* @__PURE__ */
870
996
  node.anyOf.forEach(({ required = [] }) => {
871
997
  required.forEach((fieldName) => {
872
998
  const field = getField(fieldName, formFields);
873
- updateField(field, requiredFields, node, formValues);
999
+ updateField(field, requiredFields, node, formValues, logic, { parentID });
874
1000
  });
875
1001
  });
876
1002
  }
877
1003
  if (node.allOf) {
878
- 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 }) => {
879
1014
  allOfItemRequired.forEach(requiredFields.add, requiredFields);
880
1015
  });
881
1016
  }
@@ -883,7 +1018,13 @@ function processNode(node, formValues, formFields, accRequired = /* @__PURE__ */
883
1018
  Object.entries(node.properties).forEach(([name, nestedNode]) => {
884
1019
  const inputType = getInputType(nestedNode);
885
1020
  if (inputType === supportedTypes.FIELDSET) {
886
- 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
+ });
887
1028
  }
888
1029
  });
889
1030
  }
@@ -901,11 +1042,11 @@ function clearValuesIfNotVisible(fields, formValues) {
901
1042
  }
902
1043
  });
903
1044
  }
904
- function updateFieldsProperties(fields, formValues, jsonSchema) {
1045
+ function updateFieldsProperties(fields, formValues, jsonSchema, logic) {
905
1046
  if (!jsonSchema?.properties) {
906
1047
  return;
907
1048
  }
908
- processNode(jsonSchema, formValues, fields);
1049
+ processNode({ node: jsonSchema, formValues, formFields: fields, logic });
909
1050
  clearValuesIfNotVisible(fields, formValues);
910
1051
  }
911
1052
  var notNullOption = (opt) => opt.const !== null;
@@ -948,12 +1089,16 @@ function extractParametersFromNode(schemaNode) {
948
1089
  }
949
1090
  const presentation = pickXKey(schemaNode, "presentation") ?? {};
950
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);
951
1095
  const node = (0, import_omit.default)(schemaNode, ["x-jsf-presentation", "presentation"]);
952
1096
  const description = presentation?.description || node.description;
953
1097
  const statementDescription = containsHTML(presentation.statement?.description) ? wrapWithSpan(presentation.statement.description, { class: "jsf-statement" }) : presentation.statement?.description;
954
1098
  return (0, import_omitBy.default)(
955
1099
  {
956
1100
  const: node.const,
1101
+ ...node.const && node.default ? { value: node.const } : {},
957
1102
  label: node.title,
958
1103
  readOnly: node.readOnly,
959
1104
  ...node.deprecated && {
@@ -988,6 +1133,8 @@ function extractParametersFromNode(schemaNode) {
988
1133
  },
989
1134
  // Handle [name].presentation
990
1135
  ...presentation,
1136
+ requiredValidations,
1137
+ computedAttributes: decoratedComputedAttributes,
991
1138
  description: containsHTML(description) ? wrapWithSpan(description, {
992
1139
  class: "jsf-description"
993
1140
  }) : description,
@@ -1024,8 +1171,8 @@ function yupToFormErrors(yupError) {
1024
1171
  }
1025
1172
  return errors;
1026
1173
  }
1027
- var handleValuesChange = (fields, jsonSchema, config) => (values) => {
1028
- updateFieldsProperties(fields, values, jsonSchema);
1174
+ var handleValuesChange = (fields, jsonSchema, config, logic) => (values) => {
1175
+ updateFieldsProperties(fields, values, jsonSchema, logic);
1029
1176
  const lazySchema = (0, import_yup2.lazy)(() => buildCompleteYupSchema(fields, config));
1030
1177
  let errors;
1031
1178
  try {
@@ -1044,6 +1191,12 @@ var handleValuesChange = (fields, jsonSchema, config) => (values) => {
1044
1191
  formErrors: yupToFormErrors(errors)
1045
1192
  };
1046
1193
  };
1194
+ function getDecoratedComputedAttributes(computedAttributes) {
1195
+ return {
1196
+ ...computedAttributes ?? {},
1197
+ ...computedAttributes?.const && computedAttributes?.default ? { value: computedAttributes.const } : {}
1198
+ };
1199
+ }
1047
1200
 
1048
1201
  // src/calculateConditionalProperties.js
1049
1202
  function isFieldRequired(node, field) {
@@ -1245,6 +1398,9 @@ function applyFieldsDependencies(fieldsParameters, node) {
1245
1398
  applyFieldsDependencies(fieldsParameters, condition);
1246
1399
  });
1247
1400
  }
1401
+ if (node?.["x-jsf-logic"]) {
1402
+ applyFieldsDependencies(fieldsParameters, node["x-jsf-logic"]);
1403
+ }
1248
1404
  }
1249
1405
  function getCustomPropertiesForField(fieldParams, config) {
1250
1406
  return config?.customProperties?.[fieldParams.name];
@@ -1256,15 +1412,16 @@ function getComposeFunctionForField(fieldParams, hasCustomizations) {
1256
1412
  }
1257
1413
  return composeFn;
1258
1414
  }
1259
- function buildField(fieldParams, config, scopedJsonSchema) {
1415
+ function buildField(fieldParams, config, scopedJsonSchema, logic) {
1260
1416
  const customProperties = getCustomPropertiesForField(fieldParams, config);
1261
1417
  const composeFn = getComposeFunctionForField(fieldParams, !!customProperties);
1262
- const yupSchema = buildYupSchema(fieldParams, config);
1418
+ const yupSchema = buildYupSchema(fieldParams, config, logic);
1263
1419
  const calculateConditionalFieldsClosure = fieldParams.isDynamic && calculateConditionalProperties(fieldParams, customProperties);
1264
1420
  const calculateCustomValidationPropertiesClosure = calculateCustomValidationProperties(
1265
1421
  fieldParams,
1266
1422
  customProperties
1267
1423
  );
1424
+ const getComputedAttributes = Object.keys(fieldParams.computedAttributes).length > 0 && calculateComputedAttributes(fieldParams, config);
1268
1425
  const hasCustomValidations = !!customProperties && (0, import_size.default)((0, import_pick2.default)(customProperties, SUPPORTED_CUSTOM_VALIDATION_FIELD_PARAMS)) > 0;
1269
1426
  const finalFieldParams = {
1270
1427
  // invalid attribute cleanup
@@ -1277,6 +1434,7 @@ function buildField(fieldParams, config, scopedJsonSchema) {
1277
1434
  ...hasCustomValidations && {
1278
1435
  calculateCustomValidationProperties: calculateCustomValidationPropertiesClosure
1279
1436
  },
1437
+ ...getComputedAttributes && { getComputedAttributes },
1280
1438
  // field customization properties
1281
1439
  ...customProperties && { fieldCustomization: customProperties },
1282
1440
  // base schema
@@ -1285,7 +1443,7 @@ function buildField(fieldParams, config, scopedJsonSchema) {
1285
1443
  };
1286
1444
  return composeFn(finalFieldParams);
1287
1445
  }
1288
- function getFieldsFromJSONSchema(scopedJsonSchema, config) {
1446
+ function getFieldsFromJSONSchema(scopedJsonSchema, config, logic) {
1289
1447
  if (!scopedJsonSchema) {
1290
1448
  return [];
1291
1449
  }
@@ -1309,11 +1467,11 @@ function getFieldsFromJSONSchema(scopedJsonSchema, config) {
1309
1467
  fields: () => groupArrayFields,
1310
1468
  addFieldText: fieldParams.addFieldText
1311
1469
  };
1312
- buildField(fieldParams, config, scopedJsonSchema).forEach((groupField) => {
1470
+ buildField(fieldParams, config, scopedJsonSchema, logic).forEach((groupField) => {
1313
1471
  fields.push(groupField);
1314
1472
  });
1315
1473
  } else {
1316
- fields.push(buildField(fieldParams, config, scopedJsonSchema));
1474
+ fields.push(buildField(fieldParams, config, scopedJsonSchema, logic));
1317
1475
  }
1318
1476
  });
1319
1477
  return fields;
@@ -1324,9 +1482,15 @@ function createHeadlessForm(jsonSchema, customConfig = {}) {
1324
1482
  ...customConfig
1325
1483
  };
1326
1484
  try {
1327
- const fields = getFieldsFromJSONSchema(jsonSchema, config);
1328
- const handleValidation = handleValuesChange(fields, jsonSchema, config);
1329
- 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
+ );
1330
1494
  return {
1331
1495
  fields,
1332
1496
  handleValidation,