@remoteoss/json-schema-form 0.6.4-beta.0 → 0.6.5-dev.20230918083235

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.6.4-beta.0
5
- Generated: Fri, 15 Sep 2023 12:36:13 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.6.5-dev.20230918083235
5
+ Generated: Mon, 18 Sep 2023 08:33:03 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 checkIfConditionMatchesProperties(node, formValues, formFields, logic) {
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".
@@ -127,10 +127,11 @@ function checkIfConditionMatches(node, formValues, formFields) {
127
127
  return currentProperty.enum.includes(value);
128
128
  }
129
129
  if (currentProperty.properties) {
130
- return checkIfConditionMatches(
130
+ return checkIfConditionMatchesProperties(
131
131
  { if: currentProperty },
132
132
  formValues[name],
133
- getField(name, formFields).fields
133
+ getField(name, formFields).fields,
134
+ logic
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, logic, parentID) {
151
+ const validationsMatch = Object.entries(node.if.validations ?? {}).every(([name, property]) => {
152
+ const currentValue = logic.getScope(parentID).applyValidationRuleInCondition(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 = logic.getScope(parentID).applyComputedValueRuleInCondition(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,261 +424,14 @@ 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"));
412
433
  var import_randexp = require("randexp");
413
434
  var import_yup = require("yup");
414
-
415
- // src/jsonLogic.js
416
- var import_json_logic_js = __toESM(require("json-logic-js"));
417
- function createValidationChecker(schema) {
418
- const scopes = /* @__PURE__ */ new Map();
419
- function createScopes(jsonSchema, key = "root") {
420
- const sampleEmptyObject = buildSampleEmptyObject(schema);
421
- scopes.set(key, createValidationsScope(jsonSchema));
422
- Object.entries(jsonSchema?.properties ?? {}).filter(([, property]) => property.type === "object" || property.type === "array").forEach(([key2, property]) => {
423
- if (property.type === "array") {
424
- createScopes(property.items, `${key2}[]`);
425
- } else {
426
- createScopes(property, key2);
427
- }
428
- });
429
- validateInlineRules(jsonSchema, sampleEmptyObject);
430
- }
431
- createScopes(schema);
432
- return {
433
- scopes,
434
- getScope(name = "root") {
435
- return scopes.get(name);
436
- }
437
- };
438
- }
439
- function createValidationsScope(schema) {
440
- const validationMap = /* @__PURE__ */ new Map();
441
- const computedValuesMap = /* @__PURE__ */ new Map();
442
- const logic = schema?.["x-jsf-logic"] ?? {
443
- validations: {},
444
- computedValues: {}
445
- };
446
- const validations = Object.entries(logic.validations ?? {});
447
- const computedValues = Object.entries(logic.computedValues ?? {});
448
- const sampleEmptyObject = buildSampleEmptyObject(schema);
449
- validations.forEach(([id, validation]) => {
450
- if (!validation.rule) {
451
- throw Error(`[json-schema-form] json-logic error: Validation "${id}" has missing rule.`);
452
- }
453
- checkRuleIntegrity(validation.rule, id, sampleEmptyObject);
454
- validationMap.set(id, validation);
455
- });
456
- computedValues.forEach(([id, computedValue]) => {
457
- if (!computedValue.rule) {
458
- throw Error(`[json-schema-form] json-logic error: Computed value "${id}" has missing rule.`);
459
- }
460
- checkRuleIntegrity(computedValue.rule, id, sampleEmptyObject);
461
- computedValuesMap.set(id, computedValue);
462
- });
463
- function validate(rule, values) {
464
- return import_json_logic_js.default.apply(rule, replaceUndefinedValuesWithNulls(values));
465
- }
466
- return {
467
- validationMap,
468
- computedValuesMap,
469
- validate,
470
- applyValidationRuleInCondition(id, values) {
471
- const validation = validationMap.get(id);
472
- return validate(validation.rule, values);
473
- },
474
- applyComputedValueInField(id, values, fieldName) {
475
- const validation = computedValuesMap.get(id);
476
- if (validation === void 0) {
477
- throw Error(
478
- `[json-schema-form] json-logic error: Computed value "${id}" doesn't exist in field "${fieldName}".`
479
- );
480
- }
481
- return validate(validation.rule, values);
482
- },
483
- applyComputedValueRuleInCondition(id, values) {
484
- const validation = computedValuesMap.get(id);
485
- return validate(validation.rule, values);
486
- }
487
- };
488
- }
489
- function replaceUndefinedValuesWithNulls(values = {}) {
490
- return Object.entries(values).reduce((prev, [key, value]) => {
491
- return { ...prev, [key]: value === void 0 ? null : value };
492
- }, {});
493
- }
494
- function yupSchemaWithCustomJSONLogic({ field, logic, config, id }) {
495
- const { parentID = "root" } = config;
496
- const validation = logic.getScope(parentID).validationMap.get(id);
497
- if (validation === void 0) {
498
- throw Error(
499
- `[json-schema-form] json-logic error: "${field.name}" required validation "${id}" doesn't exist.`
500
- );
501
- }
502
- return (yupSchema) => yupSchema.test(
503
- `${field.name}-validation-${id}`,
504
- validation?.errorMessage ?? "This field is invalid.",
505
- (value, { parent }) => {
506
- if (value === void 0 && !field.required)
507
- return true;
508
- return import_json_logic_js.default.apply(validation.rule, parent);
509
- }
510
- );
511
- }
512
- var HANDLEBARS_REGEX = /\{\{([^{}]+)\}\}/g;
513
- function replaceHandlebarsTemplates({
514
- value: toReplace,
515
- logic,
516
- formValues,
517
- parentID,
518
- name: fieldName
519
- }) {
520
- if (typeof toReplace === "string") {
521
- return toReplace.replace(HANDLEBARS_REGEX, (match, key) => {
522
- return logic.getScope(parentID).applyComputedValueInField(key.trim(), formValues, fieldName);
523
- });
524
- } else if (typeof toReplace === "object") {
525
- const { value, ...rules } = toReplace;
526
- if (Object.keys(rules).length > 1 && !value) {
527
- throw Error("Cannot define multiple rules without a template string with key `value`.");
528
- }
529
- const computedTemplateValue = Object.entries(rules).reduce((prev, [key, rule]) => {
530
- const computedValue = logic.getScope(parentID).validate(rule, formValues);
531
- return prev.replaceAll(`{{${key}}}`, computedValue);
532
- }, value);
533
- return computedTemplateValue.replace(/\{\{([^{}]+)\}\}/g, (match, key) => {
534
- return logic.getScope(parentID).applyComputedValueInField(key.trim(), formValues, fieldName);
535
- });
536
- }
537
- return toReplace;
538
- }
539
- function calculateComputedAttributes(fieldParams, { parentID = "root" } = {}) {
540
- return ({ logic, isRequired, config, formValues }) => {
541
- const { name, computedAttributes } = fieldParams;
542
- const attributes = Object.fromEntries(
543
- Object.entries(computedAttributes).map(handleComputedAttribute(logic, formValues, parentID, name)).filter(([, value]) => value !== null)
544
- );
545
- return {
546
- ...attributes,
547
- schema: buildYupSchema(
548
- { ...fieldParams, ...attributes, required: isRequired },
549
- config,
550
- logic
551
- )
552
- };
553
- };
554
- }
555
- function handleComputedAttribute(logic, formValues, parentID, name) {
556
- return ([key, value]) => {
557
- switch (key) {
558
- case "description":
559
- return [key, replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
560
- case "title":
561
- return ["label", replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
562
- case "x-jsf-errorMessage":
563
- return [
564
- "errorMessage",
565
- handleNestedObjectForComputedValues(value, formValues, parentID, logic, name)
566
- ];
567
- case "x-jsf-presentation": {
568
- if (value.statement) {
569
- return [
570
- "statement",
571
- handleNestedObjectForComputedValues(value.statement, formValues, parentID, logic, name)
572
- ];
573
- }
574
- return [
575
- key,
576
- handleNestedObjectForComputedValues(value.statement, formValues, parentID, logic, name)
577
- ];
578
- }
579
- case "const":
580
- default: {
581
- if (typeof value === "object" && value.rule) {
582
- return [key, logic.getScope(parentID).validate(value.rule, formValues)];
583
- }
584
- return [key, logic.getScope(parentID).applyComputedValueInField(value, formValues, name)];
585
- }
586
- }
587
- };
588
- }
589
- function handleNestedObjectForComputedValues(values, formValues, parentID, logic, name) {
590
- return Object.fromEntries(
591
- Object.entries(values).map(([key, value]) => {
592
- return [key, replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
593
- })
594
- );
595
- }
596
- function buildSampleEmptyObject(schema = {}) {
597
- const sample = {};
598
- if (typeof schema !== "object" || !schema.properties) {
599
- return schema;
600
- }
601
- for (const key in schema.properties) {
602
- if (schema.properties[key].type === "object") {
603
- sample[key] = buildSampleEmptyObject(schema.properties[key]);
604
- } else if (schema.properties[key].type === "array") {
605
- const itemSchema = schema.properties[key].items;
606
- sample[key] = buildSampleEmptyObject(itemSchema);
607
- } else {
608
- sample[key] = true;
609
- }
610
- }
611
- return sample;
612
- }
613
- function validateInlineRules(jsonSchema, sampleEmptyObject) {
614
- const properties = (jsonSchema?.properties || jsonSchema?.items?.properties) ?? {};
615
- Object.entries(properties).filter(([, property]) => property["x-jsf-logic-computedAttrs"] !== void 0).forEach(([fieldName, property]) => {
616
- Object.entries(property["x-jsf-logic-computedAttrs"]).filter(([, value]) => typeof value === "object").forEach(([key, item]) => {
617
- Object.values(item).forEach((rule) => {
618
- checkRuleIntegrity(
619
- rule,
620
- fieldName,
621
- sampleEmptyObject,
622
- (item2) => `[json-schema-form] json-logic error: fieldName "${item2.var}" doesn't exist in field "${fieldName}.x-jsf-logic-computedAttrs.${key}".`
623
- );
624
- });
625
- });
626
- });
627
- }
628
- function checkRuleIntegrity(rule, id, data, errorMessage = (item) => `[json-schema-form] json-logic error: rule "${id}" has no variable "${item.var}".`) {
629
- Object.entries(rule ?? {}).map(([operator, subRule]) => {
630
- if (!Array.isArray(subRule) && subRule !== null && subRule !== void 0)
631
- return;
632
- throwIfUnknownOperator(operator, subRule, id);
633
- subRule.map((item) => {
634
- const isVar = item !== null && typeof item === "object" && Object.hasOwn(item, "var");
635
- if (isVar) {
636
- const exists = import_json_logic_js.default.apply({ var: removeIndicesFromPath(item.var) }, data);
637
- if (exists === null) {
638
- throw Error(errorMessage(item));
639
- }
640
- } else {
641
- checkRuleIntegrity(item, id, data);
642
- }
643
- });
644
- });
645
- }
646
- function throwIfUnknownOperator(operator, subRule, id) {
647
- try {
648
- import_json_logic_js.default.apply({ [operator]: subRule });
649
- } catch (e) {
650
- if (e.message === `Unrecognized operation ${operator}`) {
651
- throw Error(
652
- `[json-schema-form] json-logic error: in "${id}" rule there is an unknown operator "${operator}".`
653
- );
654
- }
655
- }
656
- }
657
- var regexToGetIndices = /\.\d+\./g;
658
- function removeIndicesFromPath(path) {
659
- const intermediatePath = path.replace(regexToGetIndices, ".");
660
- return intermediatePath.replace(/\.\d+$/, "");
661
- }
662
-
663
- // src/yupSchema.js
664
435
  var DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
665
436
  var baseString = (0, import_yup.string)().trim();
666
437
  var todayDateHint = (/* @__PURE__ */ new Date()).toISOString().substring(0, 10);
@@ -856,118 +627,414 @@ function buildYupSchema(field, config, logic) {
856
627
  }) : true
857
628
  );
858
629
  }
859
- function withConst(yupSchema) {
860
- return yupSchema.test(
861
- "isConst",
862
- errorMessage.const ?? errorMessageFromConfig.const ?? `The only accepted value is ${propertyFields.const}.`,
863
- (value) => propertyFields.required === false && value === void 0 || value === null || value === propertyFields.const
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
+ }
637
+ function withBaseSchema() {
638
+ const customErrorMsg = errorMessage.type || errorMessageFromConfig.type;
639
+ if (customErrorMsg) {
640
+ return baseSchema.typeError(customErrorMsg);
641
+ }
642
+ return baseSchema;
643
+ }
644
+ function buildFieldSetSchema(innerFields) {
645
+ const fieldSetShape = {};
646
+ innerFields.forEach((fieldSetfield) => {
647
+ if (fieldSetfield.fields) {
648
+ fieldSetShape[fieldSetfield.name] = (0, import_yup.object)().shape(
649
+ buildFieldSetSchema(fieldSetfield.fields)
650
+ );
651
+ } else {
652
+ fieldSetShape[fieldSetfield.name] = buildYupSchema(
653
+ {
654
+ ...fieldSetfield,
655
+ inputType: fieldSetfield.type
656
+ },
657
+ config
658
+ )();
659
+ }
660
+ });
661
+ return fieldSetShape;
662
+ }
663
+ function buildGroupArraySchema() {
664
+ return (0, import_yup.object)().shape(
665
+ propertyFields.nthFieldGroup.fields().reduce(
666
+ (schema, groupArrayField) => ({
667
+ ...schema,
668
+ [groupArrayField.name]: buildYupSchema(groupArrayField, config)()
669
+ }),
670
+ {}
671
+ )
672
+ );
673
+ }
674
+ const validators = [withBaseSchema];
675
+ if (inputType === supportedTypes.GROUP_ARRAY) {
676
+ validators[0] = () => withBaseSchema().of(buildGroupArraySchema());
677
+ } else if (inputType === supportedTypes.FIELDSET) {
678
+ validators[0] = () => withBaseSchema().shape(buildFieldSetSchema(propertyFields.fields));
679
+ }
680
+ if (propertyFields.required) {
681
+ validators.push(withRequired);
682
+ }
683
+ if (typeof propertyFields.minimum !== "undefined") {
684
+ validators.push(withMin);
685
+ }
686
+ if (typeof propertyFields.minLength !== "undefined") {
687
+ validators.push(withMinLength);
688
+ }
689
+ if (propertyFields.maximum !== void 0) {
690
+ validators.push(withMax);
691
+ }
692
+ if (propertyFields.maxLength) {
693
+ validators.push(withMaxLength);
694
+ }
695
+ if (propertyFields.pattern) {
696
+ validators.push(withMatches);
697
+ }
698
+ if (propertyFields.maxFileSize) {
699
+ validators.push(withMaxFileSize);
700
+ }
701
+ if (propertyFields.accept) {
702
+ validators.push(withFileFormat);
703
+ }
704
+ if (propertyFields.const) {
705
+ validators.push(withConst);
706
+ }
707
+ if (propertyFields.jsonLogicValidations) {
708
+ propertyFields.jsonLogicValidations.forEach(
709
+ (id) => validators.push(yupSchemaWithCustomJSONLogic({ field, id, logic, config }))
710
+ );
711
+ }
712
+ return (0, import_flow.default)(validators);
713
+ }
714
+ function getNoSortEdges(fields = []) {
715
+ return fields.reduce((list, field) => {
716
+ if (field.noSortEdges) {
717
+ list.push(field.name);
718
+ }
719
+ return list;
720
+ }, []);
721
+ }
722
+ function getSchema(fields = [], config) {
723
+ const newSchema = {};
724
+ fields.forEach((field) => {
725
+ if (field.schema) {
726
+ if (field.name) {
727
+ if (field.inputType === supportedTypes.FIELDSET) {
728
+ const fieldsetSchema = buildYupSchema(field, config)();
729
+ newSchema[field.name] = fieldsetSchema;
730
+ } else {
731
+ newSchema[field.name] = field.schema;
732
+ }
733
+ } else {
734
+ Object.assign(newSchema, getSchema(field.fields, config));
735
+ }
736
+ }
737
+ });
738
+ return newSchema;
739
+ }
740
+ function buildCompleteYupSchema(fields, config) {
741
+ return (0, import_yup.object)().shape(getSchema(fields, config), getNoSortEdges(fields));
742
+ }
743
+
744
+ // src/jsonLogic.js
745
+ function createValidationChecker(schema) {
746
+ const scopes = /* @__PURE__ */ new Map();
747
+ function createScopes(jsonSchema, key = "root") {
748
+ const sampleEmptyObject = buildSampleEmptyObject(schema);
749
+ scopes.set(key, createValidationsScope(jsonSchema));
750
+ Object.entries(jsonSchema?.properties ?? {}).filter(([, property]) => property.type === "object" || property.type === "array").forEach(([key2, property]) => {
751
+ if (property.type === "array") {
752
+ createScopes(property.items, `${key2}[]`);
753
+ } else {
754
+ createScopes(property, key2);
755
+ }
756
+ });
757
+ validateInlineRules(jsonSchema, sampleEmptyObject);
758
+ }
759
+ createScopes(schema);
760
+ return {
761
+ scopes,
762
+ getScope(name = "root") {
763
+ return scopes.get(name);
764
+ }
765
+ };
766
+ }
767
+ function createValidationsScope(schema) {
768
+ const validationMap = /* @__PURE__ */ new Map();
769
+ const computedValuesMap = /* @__PURE__ */ new Map();
770
+ const logic = schema?.["x-jsf-logic"] ?? {
771
+ validations: {},
772
+ computedValues: {}
773
+ };
774
+ const validations = Object.entries(logic.validations ?? {});
775
+ const computedValues = Object.entries(logic.computedValues ?? {});
776
+ const sampleEmptyObject = buildSampleEmptyObject(schema);
777
+ validations.forEach(([id, validation]) => {
778
+ if (!validation.rule) {
779
+ throw Error(`[json-schema-form] json-logic error: Validation "${id}" has missing rule.`);
780
+ }
781
+ checkRuleIntegrity(validation.rule, id, sampleEmptyObject);
782
+ validationMap.set(id, validation);
783
+ });
784
+ computedValues.forEach(([id, computedValue]) => {
785
+ if (!computedValue.rule) {
786
+ throw Error(`[json-schema-form] json-logic error: Computed value "${id}" has missing rule.`);
787
+ }
788
+ checkRuleIntegrity(computedValue.rule, id, sampleEmptyObject);
789
+ computedValuesMap.set(id, computedValue);
790
+ });
791
+ function validate(rule, values) {
792
+ return import_json_logic_js.default.apply(
793
+ rule,
794
+ replaceUndefinedValuesWithNulls({ ...sampleEmptyObject, ...values })
795
+ );
796
+ }
797
+ return {
798
+ validationMap,
799
+ computedValuesMap,
800
+ validate,
801
+ applyValidationRuleInCondition(id, values) {
802
+ const validation = validationMap.get(id);
803
+ return validate(validation.rule, values);
804
+ },
805
+ applyComputedValueInField(id, values, fieldName) {
806
+ const validation = computedValuesMap.get(id);
807
+ if (validation === void 0) {
808
+ throw Error(
809
+ `[json-schema-form] json-logic error: Computed value "${id}" doesn't exist in field "${fieldName}".`
810
+ );
811
+ }
812
+ return validate(validation.rule, values);
813
+ },
814
+ applyComputedValueRuleInCondition(id, values) {
815
+ const validation = computedValuesMap.get(id);
816
+ return validate(validation.rule, values);
817
+ }
818
+ };
819
+ }
820
+ function replaceUndefinedValuesWithNulls(values = {}) {
821
+ return Object.entries(values).reduce((prev, [key, value]) => {
822
+ return { ...prev, [key]: value === void 0 || value === null ? NaN : value };
823
+ }, {});
824
+ }
825
+ function yupSchemaWithCustomJSONLogic({ field, logic, config, id }) {
826
+ const { parentID = "root" } = config;
827
+ const validation = logic.getScope(parentID).validationMap.get(id);
828
+ if (validation === void 0) {
829
+ throw Error(
830
+ `[json-schema-form] json-logic error: "${field.name}" required validation "${id}" doesn't exist.`
831
+ );
832
+ }
833
+ return (yupSchema) => yupSchema.test(
834
+ `${field.name}-validation-${id}`,
835
+ validation?.errorMessage ?? "This field is invalid.",
836
+ (value, { parent }) => {
837
+ if (value === void 0 && !field.required)
838
+ return true;
839
+ return import_json_logic_js.default.apply(validation.rule, parent);
840
+ }
841
+ );
842
+ }
843
+ var HANDLEBARS_REGEX = /\{\{([^{}]+)\}\}/g;
844
+ function replaceHandlebarsTemplates({
845
+ value: toReplace,
846
+ logic,
847
+ formValues,
848
+ parentID,
849
+ name: fieldName
850
+ }) {
851
+ if (typeof toReplace === "string") {
852
+ return toReplace.replace(HANDLEBARS_REGEX, (match, key) => {
853
+ return logic.getScope(parentID).applyComputedValueInField(key.trim(), formValues, fieldName);
854
+ });
855
+ } else if (typeof toReplace === "object") {
856
+ const { value, ...rules } = toReplace;
857
+ if (Object.keys(rules).length > 1 && !value) {
858
+ throw Error("Cannot define multiple rules without a template string with key `value`.");
859
+ }
860
+ const computedTemplateValue = Object.entries(rules).reduce((prev, [key, rule]) => {
861
+ const computedValue = logic.getScope(parentID).validate(rule, formValues);
862
+ return prev.replaceAll(`{{${key}}}`, computedValue);
863
+ }, value);
864
+ return computedTemplateValue.replace(/\{\{([^{}]+)\}\}/g, (match, key) => {
865
+ return logic.getScope(parentID).applyComputedValueInField(key.trim(), formValues, fieldName);
866
+ });
867
+ }
868
+ return toReplace;
869
+ }
870
+ function calculateComputedAttributes(fieldParams, { parentID = "root" } = {}) {
871
+ return ({ logic, isRequired, config, formValues }) => {
872
+ const { name, computedAttributes } = fieldParams;
873
+ const attributes = Object.fromEntries(
874
+ Object.entries(computedAttributes).map(handleComputedAttribute(logic, formValues, parentID, name)).filter(([, value]) => value !== null)
864
875
  );
876
+ return {
877
+ ...attributes,
878
+ schema: buildYupSchema(
879
+ { ...fieldParams, ...attributes, required: isRequired },
880
+ config,
881
+ logic
882
+ )
883
+ };
884
+ };
885
+ }
886
+ function handleComputedAttribute(logic, formValues, parentID, name) {
887
+ return ([key, value]) => {
888
+ switch (key) {
889
+ case "description":
890
+ return [key, replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
891
+ case "title":
892
+ return ["label", replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
893
+ case "x-jsf-errorMessage":
894
+ return [
895
+ "errorMessage",
896
+ handleNestedObjectForComputedValues(value, formValues, parentID, logic, name)
897
+ ];
898
+ case "x-jsf-presentation": {
899
+ if (value.statement) {
900
+ return [
901
+ "statement",
902
+ handleNestedObjectForComputedValues(value.statement, formValues, parentID, logic, name)
903
+ ];
904
+ }
905
+ return [
906
+ key,
907
+ handleNestedObjectForComputedValues(value.statement, formValues, parentID, logic, name)
908
+ ];
909
+ }
910
+ case "const":
911
+ default: {
912
+ if (typeof value === "object" && value.rule) {
913
+ return [key, logic.getScope(parentID).validate(value.rule, formValues)];
914
+ }
915
+ return [key, logic.getScope(parentID).applyComputedValueInField(value, formValues, name)];
916
+ }
917
+ }
918
+ };
919
+ }
920
+ function handleNestedObjectForComputedValues(values, formValues, parentID, logic, name) {
921
+ return Object.fromEntries(
922
+ Object.entries(values).map(([key, value]) => {
923
+ return [key, replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
924
+ })
925
+ );
926
+ }
927
+ function buildSampleEmptyObject(schema = {}) {
928
+ const sample = {};
929
+ if (typeof schema !== "object" || !schema.properties) {
930
+ return schema;
865
931
  }
866
- function withBaseSchema() {
867
- const customErrorMsg = errorMessage.type || errorMessageFromConfig.type;
868
- if (customErrorMsg) {
869
- return baseSchema.typeError(customErrorMsg);
932
+ for (const key in schema.properties) {
933
+ if (schema.properties[key].type === "object") {
934
+ sample[key] = buildSampleEmptyObject(schema.properties[key]);
935
+ } else if (schema.properties[key].type === "array") {
936
+ const itemSchema = schema.properties[key].items;
937
+ sample[key] = buildSampleEmptyObject(itemSchema);
938
+ } else {
939
+ sample[key] = true;
870
940
  }
871
- return baseSchema;
872
941
  }
873
- function buildFieldSetSchema(innerFields) {
874
- const fieldSetShape = {};
875
- innerFields.forEach((fieldSetfield) => {
876
- if (fieldSetfield.fields) {
877
- fieldSetShape[fieldSetfield.name] = (0, import_yup.object)().shape(
878
- buildFieldSetSchema(fieldSetfield.fields)
942
+ return sample;
943
+ }
944
+ function validateInlineRules(jsonSchema, sampleEmptyObject) {
945
+ const properties = (jsonSchema?.properties || jsonSchema?.items?.properties) ?? {};
946
+ Object.entries(properties).filter(([, property]) => property["x-jsf-logic-computedAttrs"] !== void 0).forEach(([fieldName, property]) => {
947
+ Object.entries(property["x-jsf-logic-computedAttrs"]).filter(([, value]) => typeof value === "object").forEach(([key, item]) => {
948
+ Object.values(item).forEach((rule) => {
949
+ checkRuleIntegrity(
950
+ rule,
951
+ fieldName,
952
+ sampleEmptyObject,
953
+ (item2) => `[json-schema-form] json-logic error: fieldName "${item2.var}" doesn't exist in field "${fieldName}.x-jsf-logic-computedAttrs.${key}".`
879
954
  );
955
+ });
956
+ });
957
+ });
958
+ }
959
+ function checkRuleIntegrity(rule, id, data, errorMessage = (item) => `[json-schema-form] json-logic error: rule "${id}" has no variable "${item.var}".`) {
960
+ Object.entries(rule ?? {}).map(([operator, subRule]) => {
961
+ if (!Array.isArray(subRule) && subRule !== null && subRule !== void 0)
962
+ return;
963
+ throwIfUnknownOperator(operator, subRule, id);
964
+ subRule.map((item) => {
965
+ const isVar = item !== null && typeof item === "object" && Object.hasOwn(item, "var");
966
+ if (isVar) {
967
+ const exists = import_json_logic_js.default.apply({ var: removeIndicesFromPath(item.var) }, data);
968
+ if (exists === null) {
969
+ throw Error(errorMessage(item));
970
+ }
880
971
  } else {
881
- fieldSetShape[fieldSetfield.name] = buildYupSchema(
882
- {
883
- ...fieldSetfield,
884
- inputType: fieldSetfield.type
885
- },
886
- config
887
- )();
972
+ checkRuleIntegrity(item, id, data);
888
973
  }
889
974
  });
890
- return fieldSetShape;
891
- }
892
- function buildGroupArraySchema() {
893
- return (0, import_yup.object)().shape(
894
- propertyFields.nthFieldGroup.fields().reduce(
895
- (schema, groupArrayField) => ({
896
- ...schema,
897
- [groupArrayField.name]: buildYupSchema(groupArrayField, config)()
898
- }),
899
- {}
900
- )
901
- );
902
- }
903
- const validators = [withBaseSchema];
904
- if (inputType === supportedTypes.GROUP_ARRAY) {
905
- validators[0] = () => withBaseSchema().of(buildGroupArraySchema());
906
- } else if (inputType === supportedTypes.FIELDSET) {
907
- validators[0] = () => withBaseSchema().shape(buildFieldSetSchema(propertyFields.fields));
908
- }
909
- if (propertyFields.required) {
910
- validators.push(withRequired);
911
- }
912
- if (typeof propertyFields.minimum !== "undefined") {
913
- validators.push(withMin);
914
- }
915
- if (typeof propertyFields.minLength !== "undefined") {
916
- validators.push(withMinLength);
917
- }
918
- if (propertyFields.maximum !== void 0) {
919
- validators.push(withMax);
920
- }
921
- if (propertyFields.maxLength) {
922
- validators.push(withMaxLength);
923
- }
924
- if (propertyFields.pattern) {
925
- validators.push(withMatches);
926
- }
927
- if (propertyFields.maxFileSize) {
928
- validators.push(withMaxFileSize);
929
- }
930
- if (propertyFields.accept) {
931
- validators.push(withFileFormat);
975
+ });
976
+ }
977
+ function throwIfUnknownOperator(operator, subRule, id) {
978
+ try {
979
+ import_json_logic_js.default.apply({ [operator]: subRule });
980
+ } catch (e) {
981
+ if (e.message === `Unrecognized operation ${operator}`) {
982
+ throw Error(
983
+ `[json-schema-form] json-logic error: in "${id}" rule there is an unknown operator "${operator}".`
984
+ );
985
+ }
932
986
  }
933
- if (propertyFields.const) {
934
- validators.push(withConst);
987
+ }
988
+ var regexToGetIndices = /\.\d+\./g;
989
+ function removeIndicesFromPath(path) {
990
+ const intermediatePath = path.replace(regexToGetIndices, ".");
991
+ return intermediatePath.replace(/\.\d+$/, "");
992
+ }
993
+ function processJSONLogicNode({
994
+ node,
995
+ formFields,
996
+ formValues,
997
+ accRequired,
998
+ parentID,
999
+ logic
1000
+ }) {
1001
+ const requiredFields = new Set(accRequired);
1002
+ if (node.allOf) {
1003
+ node.allOf.map(
1004
+ (allOfNode) => processJSONLogicNode({ node: allOfNode, formValues, formFields, logic, parentID })
1005
+ ).forEach(({ required: allOfItemRequired }) => {
1006
+ allOfItemRequired.forEach(requiredFields.add, requiredFields);
1007
+ });
935
1008
  }
936
- if (propertyFields.requiredValidations) {
937
- propertyFields.requiredValidations.forEach(
938
- (id) => validators.push(yupSchemaWithCustomJSONLogic({ field, id, logic, config }))
1009
+ if (node.if) {
1010
+ const matchesPropertyCondition = checkIfConditionMatchesProperties(
1011
+ node,
1012
+ formValues,
1013
+ formFields,
1014
+ logic
939
1015
  );
940
- }
941
- return (0, import_flow.default)(validators);
942
- }
943
- function getNoSortEdges(fields = []) {
944
- return fields.reduce((list, field) => {
945
- if (field.noSortEdges) {
946
- list.push(field.name);
1016
+ const matchesValidationsAndComputedValues = matchesPropertyCondition && checkIfMatchesValidationsAndComputedValues(node, formValues, logic, parentID);
1017
+ const isConditionMatch = matchesPropertyCondition && matchesValidationsAndComputedValues;
1018
+ let nextNode;
1019
+ if (isConditionMatch && node.then) {
1020
+ nextNode = node.then;
947
1021
  }
948
- return list;
949
- }, []);
950
- }
951
- function getSchema(fields = [], config) {
952
- const newSchema = {};
953
- fields.forEach((field) => {
954
- if (field.schema) {
955
- if (field.name) {
956
- if (field.inputType === supportedTypes.FIELDSET) {
957
- const fieldsetSchema = buildYupSchema(field, config)();
958
- newSchema[field.name] = fieldsetSchema;
959
- } else {
960
- newSchema[field.name] = field.schema;
961
- }
962
- } else {
963
- Object.assign(newSchema, getSchema(field.fields, config));
964
- }
1022
+ if (!isConditionMatch && node.else) {
1023
+ nextNode = node.else;
965
1024
  }
966
- });
967
- return newSchema;
968
- }
969
- function buildCompleteYupSchema(fields, config) {
970
- return (0, import_yup.object)().shape(getSchema(fields, config), getNoSortEdges(fields));
1025
+ if (nextNode) {
1026
+ const { required: branchRequired } = processNode({
1027
+ node: nextNode,
1028
+ formValues,
1029
+ formFields,
1030
+ accRequired,
1031
+ logic,
1032
+ parentID
1033
+ });
1034
+ branchRequired.forEach((field) => requiredFields.add(field));
1035
+ }
1036
+ }
1037
+ return { required: requiredFields };
971
1038
  }
972
1039
 
973
1040
  // src/helpers.js
@@ -1086,7 +1153,11 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
1086
1153
  updateValues(computedFieldValues);
1087
1154
  }
1088
1155
  if (field.calculateConditionalProperties) {
1089
- const newFieldValues = field.calculateConditionalProperties(fieldIsRequired, node);
1156
+ const newFieldValues = field.calculateConditionalProperties({
1157
+ isRequired: fieldIsRequired,
1158
+ conditionBranch: node,
1159
+ formValues
1160
+ });
1090
1161
  updateValues(newFieldValues);
1091
1162
  }
1092
1163
  if (field.calculateCustomValidationProperties) {
@@ -1118,7 +1189,7 @@ function processNode({
1118
1189
  });
1119
1190
  });
1120
1191
  if (node.if) {
1121
- const matchesCondition = checkIfConditionMatches(node, formValues, formFields, logic);
1192
+ const matchesCondition = checkIfConditionMatchesProperties(node, formValues, formFields, logic);
1122
1193
  if (matchesCondition && node.then) {
1123
1194
  const { required: branchRequired } = processNode({
1124
1195
  node: node.then,
@@ -1181,6 +1252,17 @@ function processNode({
1181
1252
  }
1182
1253
  });
1183
1254
  }
1255
+ if (node["x-jsf-logic"]) {
1256
+ const { required: requiredFromLogic } = processJSONLogicNode({
1257
+ node: node["x-jsf-logic"],
1258
+ formValues,
1259
+ formFields,
1260
+ accRequired: requiredFields,
1261
+ parentID,
1262
+ logic
1263
+ });
1264
+ requiredFromLogic.forEach((field) => requiredFields.add(field));
1265
+ }
1184
1266
  return {
1185
1267
  required: requiredFields
1186
1268
  };
@@ -1242,7 +1324,7 @@ function extractParametersFromNode(schemaNode) {
1242
1324
  }
1243
1325
  const presentation = pickXKey(schemaNode, "presentation") ?? {};
1244
1326
  const errorMessage = pickXKey(schemaNode, "errorMessage") ?? {};
1245
- const requiredValidations = schemaNode["x-jsf-logic-validations"];
1327
+ const jsonLogicValidations = schemaNode["x-jsf-logic-validations"];
1246
1328
  const computedAttributes = schemaNode["x-jsf-logic-computedAttrs"];
1247
1329
  const decoratedComputedAttributes = getDecoratedComputedAttributes(computedAttributes);
1248
1330
  const node = (0, import_omit.default)(schemaNode, ["x-jsf-presentation", "presentation"]);
@@ -1286,7 +1368,7 @@ function extractParametersFromNode(schemaNode) {
1286
1368
  },
1287
1369
  // Handle [name].presentation
1288
1370
  ...presentation,
1289
- requiredValidations,
1371
+ jsonLogicValidations,
1290
1372
  computedAttributes: decoratedComputedAttributes,
1291
1373
  description: containsHTML(description) ? wrapWithSpan(description, {
1292
1374
  class: "jsf-description"
@@ -1386,8 +1468,8 @@ function rebuildFieldset(fields, property) {
1386
1468
  required: isFieldRequired(property, field)
1387
1469
  }));
1388
1470
  }
1389
- function calculateConditionalProperties(fieldParams, customProperties) {
1390
- return (isRequired, conditionBranch) => {
1471
+ function calculateConditionalProperties({ fieldParams, customProperties, logic, config }) {
1472
+ return ({ isRequired, conditionBranch, formValues }) => {
1391
1473
  const conditionalProperty = conditionBranch?.properties?.[fieldParams.name];
1392
1474
  if (conditionalProperty) {
1393
1475
  const presentation = pickXKey(conditionalProperty, "presentation") ?? {};
@@ -1401,17 +1483,31 @@ function calculateConditionalProperties(fieldParams, customProperties) {
1401
1483
  fieldSetFields = rebuildFieldset(fieldParams.fields, conditionalProperty);
1402
1484
  newFieldParams.fields = fieldSetFields;
1403
1485
  }
1486
+ const { computedAttributes, ...restNewFieldParams } = newFieldParams;
1487
+ const calculatedComputedAttributes = computedAttributes ? calculateComputedAttributes(newFieldParams, config)({ logic, formValues }) : {};
1488
+ const jsonLogicValidations = [
1489
+ ...fieldParams.jsonLogicValidations ?? [],
1490
+ ...restNewFieldParams.jsonLogicValidations ?? []
1491
+ ];
1404
1492
  const base = {
1405
1493
  isVisible: true,
1406
1494
  required: isRequired,
1407
1495
  ...presentation?.inputType && { type: presentation.inputType },
1408
- schema: buildYupSchema({
1409
- ...fieldParams,
1410
- ...newFieldParams,
1411
- // If there are inner fields (case of fieldset) they need to be updated based on the condition
1412
- fields: fieldSetFields,
1413
- required: isRequired
1414
- })
1496
+ ...calculatedComputedAttributes,
1497
+ ...calculatedComputedAttributes.value ? { value: calculatedComputedAttributes.value } : { value: void 0 },
1498
+ schema: buildYupSchema(
1499
+ {
1500
+ ...fieldParams,
1501
+ ...restNewFieldParams,
1502
+ ...calculatedComputedAttributes,
1503
+ jsonLogicValidations,
1504
+ // If there are inner fields (case of fieldset) they need to be updated based on the condition
1505
+ fields: fieldSetFields,
1506
+ required: isRequired
1507
+ },
1508
+ config,
1509
+ logic
1510
+ )
1415
1511
  };
1416
1512
  return (0, import_omit2.default)((0, import_merge2.default)(base, presentation, newFieldParams), ["inputType"]);
1417
1513
  }
@@ -1505,14 +1601,19 @@ function sortByOrderOrPosition(a, b, order) {
1505
1601
  function removeInvalidAttributes(fields) {
1506
1602
  return (0, import_omit3.default)(fields, ["items", "maxFileSize", "isDynamic"]);
1507
1603
  }
1508
- function buildFieldParameters(name, fieldProperties, required = [], config = {}) {
1604
+ function buildFieldParameters(name, fieldProperties, required = [], config = {}, logic) {
1509
1605
  const { position } = pickXKey(fieldProperties, "presentation") ?? {};
1510
1606
  let fields;
1511
1607
  const inputType = getInputType(fieldProperties, config.strictInputType, name);
1512
1608
  if (inputType === supportedTypes.FIELDSET) {
1513
- fields = getFieldsFromJSONSchema(fieldProperties, {
1514
- customProperties: (0, import_get3.default)(config, `customProperties.${name}`, {})
1515
- });
1609
+ fields = getFieldsFromJSONSchema(
1610
+ fieldProperties,
1611
+ {
1612
+ customProperties: (0, import_get3.default)(config, `customProperties.${name}`, {}),
1613
+ parentID: name
1614
+ },
1615
+ logic
1616
+ );
1516
1617
  }
1517
1618
  const result = {
1518
1619
  name,
@@ -1569,7 +1670,7 @@ function buildField(fieldParams, config, scopedJsonSchema, logic) {
1569
1670
  const customProperties = getCustomPropertiesForField(fieldParams, config);
1570
1671
  const composeFn = getComposeFunctionForField(fieldParams, !!customProperties);
1571
1672
  const yupSchema = buildYupSchema(fieldParams, config, logic);
1572
- const calculateConditionalFieldsClosure = fieldParams.isDynamic && calculateConditionalProperties(fieldParams, customProperties);
1673
+ const calculateConditionalFieldsClosure = fieldParams.isDynamic && calculateConditionalProperties({ fieldParams, customProperties, logic, config });
1573
1674
  const calculateCustomValidationPropertiesClosure = calculateCustomValidationProperties(
1574
1675
  fieldParams,
1575
1676
  customProperties