@sanity/validation 3.0.0-dev-preview.12 → 3.0.0-v3-pte.16

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/lib/index.js CHANGED
@@ -4,10 +4,6 @@ import formatDate from 'date-fns/format';
4
4
 
5
5
  // Follows the same pattern as Rule and RuleClass. @see Rule
6
6
  const ValidationError = class ValidationError {
7
- message;
8
- paths;
9
- children;
10
- operation;
11
7
  constructor(message, options = {}) {
12
8
  this.message = message;
13
9
  this.paths = options.paths || [];
@@ -618,6 +614,7 @@ const dateValidators = {
618
614
  },
619
615
  };
620
616
 
617
+ var _a;
621
618
  const typeValidators = {
622
619
  Boolean: booleanValidators,
623
620
  Number: numberValidators,
@@ -659,285 +656,286 @@ const ruleConstraintTypes$1 = [
659
656
  // This package exports the RuleClass as a value without implicitly exporting
660
657
  // an instance definition. This should help reminder downstream users to import
661
658
  // from the `@sanity/types` package.
662
- const Rule = class Rule {
663
- static FIELD_REF = FIELD_REF;
664
- static array = (def) => new Rule(def).type('Array');
665
- static object = (def) => new Rule(def).type('Object');
666
- static string = (def) => new Rule(def).type('String');
667
- static number = (def) => new Rule(def).type('Number');
668
- static boolean = (def) => new Rule(def).type('Boolean');
669
- static dateTime = (def) => new Rule(def).type('Date');
670
- static valueOfField = (path) => ({
671
- type: FIELD_REF,
672
- path,
673
- });
674
- _type = undefined;
675
- _level = undefined;
676
- _required = undefined;
677
- _typeDef = undefined;
678
- _message = undefined;
679
- _rules = [];
680
- _fieldRules = undefined;
681
- constructor(typeDef) {
682
- this._typeDef = typeDef;
683
- this.reset();
684
- }
685
- _mergeRequired(next) {
686
- if (this._required === 'required' || next._required === 'required')
687
- return 'required';
688
- if (this._required === 'optional' || next._required === 'optional')
689
- return 'optional';
690
- return undefined;
691
- }
692
- // Alias to static method, since we often have access to an _instance_ of a rule but not the actual Rule class
693
- valueOfField = Rule.valueOfField.bind(Rule);
694
- error(message) {
695
- const rule = this.clone();
696
- rule._level = 'error';
697
- rule._message = message || undefined;
698
- return rule;
699
- }
700
- warning(message) {
701
- const rule = this.clone();
702
- rule._level = 'warning';
703
- rule._message = message || undefined;
704
- return rule;
705
- }
706
- info(message) {
707
- const rule = this.clone();
708
- rule._level = 'info';
709
- rule._message = message || undefined;
710
- return rule;
711
- }
712
- reset() {
713
- this._type = this._type || undefined;
714
- this._rules = (this._rules || []).filter((rule) => rule.flag === 'type');
715
- this._message = undefined;
716
- this._required = undefined;
717
- this._level = 'error';
718
- this._fieldRules = undefined;
719
- return this;
720
- }
721
- isRequired() {
722
- return this._required === 'required';
723
- }
724
- clone() {
725
- const rule = new Rule();
726
- rule._type = this._type;
727
- rule._message = this._message;
728
- rule._required = this._required;
729
- rule._rules = cloneDeep(this._rules);
730
- rule._level = this._level;
731
- rule._fieldRules = this._fieldRules;
732
- rule._typeDef = this._typeDef;
733
- return rule;
734
- }
735
- cloneWithRules(rules) {
736
- const rule = this.clone();
737
- const newRules = new Set();
738
- rules.forEach((curr) => {
739
- if (curr.flag === 'type') {
740
- rule._type = curr.constraint;
659
+ const Rule = (_a = class Rule {
660
+ constructor(typeDef) {
661
+ this._type = undefined;
662
+ this._level = undefined;
663
+ this._required = undefined;
664
+ this._typeDef = undefined;
665
+ this._message = undefined;
666
+ this._rules = [];
667
+ this._fieldRules = undefined;
668
+ // Alias to static method, since we often have access to an _instance_ of a rule but not the actual Rule class
669
+ this.valueOfField = Rule.valueOfField.bind(Rule);
670
+ this._typeDef = typeDef;
671
+ this.reset();
672
+ }
673
+ _mergeRequired(next) {
674
+ if (this._required === 'required' || next._required === 'required')
675
+ return 'required';
676
+ if (this._required === 'optional' || next._required === 'optional')
677
+ return 'optional';
678
+ return undefined;
679
+ }
680
+ error(message) {
681
+ const rule = this.clone();
682
+ rule._level = 'error';
683
+ rule._message = message || undefined;
684
+ return rule;
685
+ }
686
+ warning(message) {
687
+ const rule = this.clone();
688
+ rule._level = 'warning';
689
+ rule._message = message || undefined;
690
+ return rule;
691
+ }
692
+ info(message) {
693
+ const rule = this.clone();
694
+ rule._level = 'info';
695
+ rule._message = message || undefined;
696
+ return rule;
697
+ }
698
+ reset() {
699
+ this._type = this._type || undefined;
700
+ this._rules = (this._rules || []).filter((rule) => rule.flag === 'type');
701
+ this._message = undefined;
702
+ this._required = undefined;
703
+ this._level = 'error';
704
+ this._fieldRules = undefined;
705
+ return this;
706
+ }
707
+ isRequired() {
708
+ return this._required === 'required';
709
+ }
710
+ clone() {
711
+ const rule = new Rule();
712
+ rule._type = this._type;
713
+ rule._message = this._message;
714
+ rule._required = this._required;
715
+ rule._rules = cloneDeep(this._rules);
716
+ rule._level = this._level;
717
+ rule._fieldRules = this._fieldRules;
718
+ rule._typeDef = this._typeDef;
719
+ return rule;
720
+ }
721
+ cloneWithRules(rules) {
722
+ const rule = this.clone();
723
+ const newRules = new Set();
724
+ rules.forEach((curr) => {
725
+ if (curr.flag === 'type') {
726
+ rule._type = curr.constraint;
727
+ }
728
+ newRules.add(curr.flag);
729
+ });
730
+ rule._rules = rule._rules
731
+ .filter((curr) => {
732
+ const disallowDuplicate = ['type', 'uri', 'email'].includes(curr.flag);
733
+ const isDuplicate = newRules.has(curr.flag);
734
+ return !(disallowDuplicate && isDuplicate);
735
+ })
736
+ .concat(rules);
737
+ return rule;
738
+ }
739
+ merge(rule) {
740
+ if (this._type && rule._type && this._type !== rule._type) {
741
+ throw new Error('merge() failed: conflicting types');
741
742
  }
742
- newRules.add(curr.flag);
743
- });
744
- rule._rules = rule._rules
745
- .filter((curr) => {
746
- const disallowDuplicate = ['type', 'uri', 'email'].includes(curr.flag);
747
- const isDuplicate = newRules.has(curr.flag);
748
- return !(disallowDuplicate && isDuplicate);
749
- })
750
- .concat(rules);
751
- return rule;
752
- }
753
- merge(rule) {
754
- if (this._type && rule._type && this._type !== rule._type) {
755
- throw new Error('merge() failed: conflicting types');
756
- }
757
- const newRule = this.cloneWithRules(rule._rules);
758
- newRule._type = this._type || rule._type;
759
- newRule._message = this._message || rule._message;
760
- newRule._required = this._mergeRequired(rule);
761
- newRule._level = this._level === 'error' ? rule._level : this._level;
762
- return newRule;
763
- }
764
- // Validation flag setters
765
- type(targetType) {
766
- const type = `${targetType.slice(0, 1).toUpperCase()}${targetType.slice(1)}`;
767
- if (!ruleConstraintTypes$1.includes(type)) {
768
- throw new Error(`Unknown type "${targetType}"`);
769
- }
770
- const rule = this.cloneWithRules([{ flag: 'type', constraint: type }]);
771
- rule._type = type;
772
- return rule;
773
- }
774
- all(children) {
775
- return this.cloneWithRules([{ flag: 'all', constraint: children }]);
776
- }
777
- either(children) {
778
- return this.cloneWithRules([{ flag: 'either', constraint: children }]);
779
- }
780
- // Shared rules
781
- optional() {
782
- const rule = this.cloneWithRules([{ flag: 'presence', constraint: 'optional' }]);
783
- rule._required = 'optional';
784
- return rule;
785
- }
786
- required() {
787
- const rule = this.cloneWithRules([{ flag: 'presence', constraint: 'required' }]);
788
- rule._required = 'required';
789
- return rule;
790
- }
791
- custom(fn) {
792
- return this.cloneWithRules([{ flag: 'custom', constraint: fn }]);
793
- }
794
- /**
795
- * @deprecated use `Rule.custom` instead
796
- */
797
- block(fn) {
798
- return this.cloneWithRules([{ flag: 'custom', constraint: fn }]);
799
- }
800
- min(len) {
801
- return this.cloneWithRules([{ flag: 'min', constraint: len }]);
802
- }
803
- max(len) {
804
- return this.cloneWithRules([{ flag: 'max', constraint: len }]);
805
- }
806
- length(len) {
807
- return this.cloneWithRules([{ flag: 'length', constraint: len }]);
808
- }
809
- valid(value) {
810
- const values = Array.isArray(value) ? value : [value];
811
- return this.cloneWithRules([{ flag: 'valid', constraint: values }]);
812
- }
813
- // Numbers only
814
- integer() {
815
- return this.cloneWithRules([{ flag: 'integer' }]);
816
- }
817
- precision(limit) {
818
- return this.cloneWithRules([{ flag: 'precision', constraint: limit }]);
819
- }
820
- positive() {
821
- return this.cloneWithRules([{ flag: 'min', constraint: 0 }]);
822
- }
823
- negative() {
824
- return this.cloneWithRules([{ flag: 'lessThan', constraint: 0 }]);
825
- }
826
- greaterThan(num) {
827
- return this.cloneWithRules([{ flag: 'greaterThan', constraint: num }]);
828
- }
829
- lessThan(num) {
830
- return this.cloneWithRules([{ flag: 'lessThan', constraint: num }]);
831
- }
832
- // String only
833
- uppercase() {
834
- return this.cloneWithRules([{ flag: 'stringCasing', constraint: 'uppercase' }]);
835
- }
836
- lowercase() {
837
- return this.cloneWithRules([{ flag: 'stringCasing', constraint: 'lowercase' }]);
838
- }
839
- regex(pattern, a, b) {
840
- const name = typeof a === 'string' ? a : a?.name ?? b?.name;
841
- const invert = typeof a === 'string' ? false : a?.invert ?? b?.invert;
842
- const constraint = {
843
- pattern,
844
- name,
845
- invert: invert || false,
846
- };
847
- return this.cloneWithRules([{ flag: 'regex', constraint }]);
848
- }
849
- email() {
850
- return this.cloneWithRules([{ flag: 'email' }]);
851
- }
852
- uri(opts) {
853
- const optsScheme = opts?.scheme || ['http', 'https'];
854
- const schemes = Array.isArray(optsScheme) ? optsScheme : [optsScheme];
855
- if (!schemes.length) {
856
- throw new Error('scheme must have at least 1 scheme specified');
857
- }
858
- const constraint = {
859
- options: {
860
- scheme: schemes.map((scheme) => {
861
- if (!(scheme instanceof RegExp) && typeof scheme !== 'string') {
862
- throw new Error('scheme must be a RegExp or a String');
863
- }
864
- return typeof scheme === 'string' ? new RegExp(`^${escapeRegex(scheme)}$`) : scheme;
865
- }),
866
- allowRelative: opts?.allowRelative || false,
867
- relativeOnly: opts?.relativeOnly || false,
868
- allowCredentials: opts?.allowCredentials || false,
869
- },
870
- };
871
- return this.cloneWithRules([{ flag: 'uri', constraint }]);
872
- }
873
- // Array only
874
- unique() {
875
- return this.cloneWithRules([{ flag: 'unique' }]);
876
- }
877
- // Objects only
878
- reference() {
879
- return this.cloneWithRules([{ flag: 'reference' }]);
880
- }
881
- fields(rules) {
882
- if (this._type !== 'Object') {
883
- throw new Error('fields() can only be called on an object type');
743
+ const newRule = this.cloneWithRules(rule._rules);
744
+ newRule._type = this._type || rule._type;
745
+ newRule._message = this._message || rule._message;
746
+ newRule._required = this._mergeRequired(rule);
747
+ newRule._level = this._level === 'error' ? rule._level : this._level;
748
+ return newRule;
749
+ }
750
+ // Validation flag setters
751
+ type(targetType) {
752
+ const type = `${targetType.slice(0, 1).toUpperCase()}${targetType.slice(1)}`;
753
+ if (!ruleConstraintTypes$1.includes(type)) {
754
+ throw new Error(`Unknown type "${targetType}"`);
755
+ }
756
+ const rule = this.cloneWithRules([{ flag: 'type', constraint: type }]);
757
+ rule._type = type;
758
+ return rule;
884
759
  }
885
- const rule = this.cloneWithRules([]);
886
- rule._fieldRules = rules;
887
- return rule;
888
- }
889
- assetRequired() {
890
- const base = getBaseType(this._typeDef);
891
- let assetType;
892
- if (base && ['image', 'file'].includes(base.name)) {
893
- assetType = base.name === 'image' ? 'Image' : 'File';
760
+ all(children) {
761
+ return this.cloneWithRules([{ flag: 'all', constraint: children }]);
894
762
  }
895
- else {
896
- assetType = 'Asset';
763
+ either(children) {
764
+ return this.cloneWithRules([{ flag: 'either', constraint: children }]);
897
765
  }
898
- return this.cloneWithRules([{ flag: 'assetRequired', constraint: { assetType } }]);
899
- }
900
- async validate(value, context) {
901
- if (!context) {
902
- throw new Error('missing context');
903
- }
904
- const valueIsEmpty = value === null || value === undefined;
905
- // Short-circuit on optional, empty fields
906
- if (valueIsEmpty && this._required === 'optional') {
907
- return EMPTY_ARRAY;
908
- }
909
- const rules =
910
- // Run only the _custom_ functions if the rule is not set to required or optional
911
- this._required === undefined && valueIsEmpty
912
- ? this._rules.filter((curr) => curr.flag === 'custom')
913
- : this._rules;
914
- const validators = (this._type && typeValidators[this._type]) || genericValidators;
915
- const results = await Promise.all(rules.map(async (curr) => {
916
- if (curr.flag === undefined) {
917
- throw new Error('Invalid rule, did not contain "flag"-property');
766
+ // Shared rules
767
+ optional() {
768
+ const rule = this.cloneWithRules([{ flag: 'presence', constraint: 'optional' }]);
769
+ rule._required = 'optional';
770
+ return rule;
771
+ }
772
+ required() {
773
+ const rule = this.cloneWithRules([{ flag: 'presence', constraint: 'required' }]);
774
+ rule._required = 'required';
775
+ return rule;
776
+ }
777
+ custom(fn) {
778
+ return this.cloneWithRules([{ flag: 'custom', constraint: fn }]);
779
+ }
780
+ /**
781
+ * @deprecated use `Rule.custom` instead
782
+ */
783
+ block(fn) {
784
+ return this.cloneWithRules([{ flag: 'custom', constraint: fn }]);
785
+ }
786
+ min(len) {
787
+ return this.cloneWithRules([{ flag: 'min', constraint: len }]);
788
+ }
789
+ max(len) {
790
+ return this.cloneWithRules([{ flag: 'max', constraint: len }]);
791
+ }
792
+ length(len) {
793
+ return this.cloneWithRules([{ flag: 'length', constraint: len }]);
794
+ }
795
+ valid(value) {
796
+ const values = Array.isArray(value) ? value : [value];
797
+ return this.cloneWithRules([{ flag: 'valid', constraint: values }]);
798
+ }
799
+ // Numbers only
800
+ integer() {
801
+ return this.cloneWithRules([{ flag: 'integer' }]);
802
+ }
803
+ precision(limit) {
804
+ return this.cloneWithRules([{ flag: 'precision', constraint: limit }]);
805
+ }
806
+ positive() {
807
+ return this.cloneWithRules([{ flag: 'min', constraint: 0 }]);
808
+ }
809
+ negative() {
810
+ return this.cloneWithRules([{ flag: 'lessThan', constraint: 0 }]);
811
+ }
812
+ greaterThan(num) {
813
+ return this.cloneWithRules([{ flag: 'greaterThan', constraint: num }]);
814
+ }
815
+ lessThan(num) {
816
+ return this.cloneWithRules([{ flag: 'lessThan', constraint: num }]);
817
+ }
818
+ // String only
819
+ uppercase() {
820
+ return this.cloneWithRules([{ flag: 'stringCasing', constraint: 'uppercase' }]);
821
+ }
822
+ lowercase() {
823
+ return this.cloneWithRules([{ flag: 'stringCasing', constraint: 'lowercase' }]);
824
+ }
825
+ regex(pattern, a, b) {
826
+ const name = typeof a === 'string' ? a : a?.name ?? b?.name;
827
+ const invert = typeof a === 'string' ? false : a?.invert ?? b?.invert;
828
+ const constraint = {
829
+ pattern,
830
+ name,
831
+ invert: invert || false,
832
+ };
833
+ return this.cloneWithRules([{ flag: 'regex', constraint }]);
834
+ }
835
+ email() {
836
+ return this.cloneWithRules([{ flag: 'email' }]);
837
+ }
838
+ uri(opts) {
839
+ const optsScheme = opts?.scheme || ['http', 'https'];
840
+ const schemes = Array.isArray(optsScheme) ? optsScheme : [optsScheme];
841
+ if (!schemes.length) {
842
+ throw new Error('scheme must have at least 1 scheme specified');
918
843
  }
919
- const validator = validators[curr.flag];
920
- if (!validator) {
921
- const forType = this._type ? `type "${this._type}"` : 'rule without declared type';
922
- throw new Error(`Validator for flag "${curr.flag}" not found for ${forType}`);
844
+ const constraint = {
845
+ options: {
846
+ scheme: schemes.map((scheme) => {
847
+ if (!(scheme instanceof RegExp) && typeof scheme !== 'string') {
848
+ throw new Error('scheme must be a RegExp or a String');
849
+ }
850
+ return typeof scheme === 'string' ? new RegExp(`^${escapeRegex(scheme)}$`) : scheme;
851
+ }),
852
+ allowRelative: opts?.allowRelative || false,
853
+ relativeOnly: opts?.relativeOnly || false,
854
+ allowCredentials: opts?.allowCredentials || false,
855
+ },
856
+ };
857
+ return this.cloneWithRules([{ flag: 'uri', constraint }]);
858
+ }
859
+ // Array only
860
+ unique() {
861
+ return this.cloneWithRules([{ flag: 'unique' }]);
862
+ }
863
+ // Objects only
864
+ reference() {
865
+ return this.cloneWithRules([{ flag: 'reference' }]);
866
+ }
867
+ fields(rules) {
868
+ if (this._type !== 'Object') {
869
+ throw new Error('fields() can only be called on an object type');
923
870
  }
924
- let specConstraint = 'constraint' in curr ? curr.constraint : null;
925
- if (isFieldRef(specConstraint)) {
926
- specConstraint = get(context.parent, specConstraint.path);
871
+ const rule = this.cloneWithRules([]);
872
+ rule._fieldRules = rules;
873
+ return rule;
874
+ }
875
+ assetRequired() {
876
+ const base = getBaseType(this._typeDef);
877
+ let assetType;
878
+ if (base && ['image', 'file'].includes(base.name)) {
879
+ assetType = base.name === 'image' ? 'Image' : 'File';
927
880
  }
928
- let result;
929
- try {
930
- result = await validator(specConstraint, value, this._message, context);
881
+ else {
882
+ assetType = 'Asset';
931
883
  }
932
- catch (err) {
933
- const errorFromException = new ValidationError(`${pathToString(context.path)}: Exception occurred while validating value: ${err.message}`);
934
- return convertToValidationMarker(errorFromException, 'error', context);
884
+ return this.cloneWithRules([{ flag: 'assetRequired', constraint: { assetType } }]);
885
+ }
886
+ async validate(value, context) {
887
+ if (!context) {
888
+ throw new Error('missing context');
935
889
  }
936
- return convertToValidationMarker(result, this._level, context);
937
- }));
938
- return results.flat();
939
- }
940
- };
890
+ const valueIsEmpty = value === null || value === undefined;
891
+ // Short-circuit on optional, empty fields
892
+ if (valueIsEmpty && this._required === 'optional') {
893
+ return EMPTY_ARRAY;
894
+ }
895
+ const rules =
896
+ // Run only the _custom_ functions if the rule is not set to required or optional
897
+ this._required === undefined && valueIsEmpty
898
+ ? this._rules.filter((curr) => curr.flag === 'custom')
899
+ : this._rules;
900
+ const validators = (this._type && typeValidators[this._type]) || genericValidators;
901
+ const results = await Promise.all(rules.map(async (curr) => {
902
+ if (curr.flag === undefined) {
903
+ throw new Error('Invalid rule, did not contain "flag"-property');
904
+ }
905
+ const validator = validators[curr.flag];
906
+ if (!validator) {
907
+ const forType = this._type ? `type "${this._type}"` : 'rule without declared type';
908
+ throw new Error(`Validator for flag "${curr.flag}" not found for ${forType}`);
909
+ }
910
+ let specConstraint = 'constraint' in curr ? curr.constraint : null;
911
+ if (isFieldRef(specConstraint)) {
912
+ specConstraint = get(context.parent, specConstraint.path);
913
+ }
914
+ let result;
915
+ try {
916
+ result = await validator(specConstraint, value, this._message, context);
917
+ }
918
+ catch (err) {
919
+ const errorFromException = new ValidationError(`${pathToString(context.path)}: Exception occurred while validating value: ${err.message}`);
920
+ return convertToValidationMarker(errorFromException, 'error', context);
921
+ }
922
+ return convertToValidationMarker(result, this._level, context);
923
+ }));
924
+ return results.flat();
925
+ }
926
+ },
927
+ _a.FIELD_REF = FIELD_REF,
928
+ _a.array = (def) => new _a(def).type('Array'),
929
+ _a.object = (def) => new _a(def).type('Object'),
930
+ _a.string = (def) => new _a(def).type('String'),
931
+ _a.number = (def) => new _a(def).type('Number'),
932
+ _a.boolean = (def) => new _a(def).type('Boolean'),
933
+ _a.dateTime = (def) => new _a(def).type('Date'),
934
+ _a.valueOfField = (path) => ({
935
+ type: FIELD_REF,
936
+ path,
937
+ }),
938
+ _a);
941
939
 
942
940
  // import getClient from '../getClient'
943
941
  const memoizedWarnOnArraySlug = memoize(warnOnArraySlug);