@sanity/validation 2.30.1-purple-unicorn.964 → 3.0.0-dev-preview.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.
Files changed (48) hide show
  1. package/lib/dts/src/Rule.js +317 -312
  2. package/lib/dts/src/ValidationError.js +16 -16
  3. package/lib/dts/src/index.js +7 -7
  4. package/lib/dts/src/inferFromSchema.js +11 -11
  5. package/lib/dts/src/inferFromSchemaType.js +24 -24
  6. package/lib/dts/src/util/convertToValidationMarker.js +44 -53
  7. package/lib/dts/src/util/deepEquals.js +50 -49
  8. package/lib/dts/src/util/escapeRegex.js +4 -4
  9. package/lib/dts/src/util/normalizeValidationRules.js +87 -78
  10. package/lib/dts/src/util/normalizeValidationRules.test.d.ts +2 -2
  11. package/lib/dts/src/util/normalizeValidationRules.test.js +149 -149
  12. package/lib/dts/src/util/pathToString.js +16 -16
  13. package/lib/dts/src/util/typeString.js +17 -14
  14. package/lib/dts/src/util/typeString.test.d.ts +2 -2
  15. package/lib/dts/src/util/typeString.test.js +23 -24
  16. package/lib/dts/src/validateDocument.js +132 -157
  17. package/lib/dts/src/validateDocument.test.d.ts +2 -2
  18. package/lib/dts/src/validateDocument.test.js +664 -678
  19. package/lib/dts/src/validators/arrayValidator.js +75 -75
  20. package/lib/dts/src/validators/booleanValidator.js +11 -11
  21. package/lib/dts/src/validators/dateValidator.js +74 -68
  22. package/lib/dts/src/validators/genericValidator.js +89 -87
  23. package/lib/dts/src/validators/numberValidator.js +46 -48
  24. package/lib/dts/src/validators/objectValidator.js +47 -47
  25. package/lib/dts/src/validators/slugValidator.js +68 -74
  26. package/lib/dts/src/validators/stringValidator.js +93 -93
  27. package/lib/dts/test/array.test.d.ts +2 -2
  28. package/lib/dts/test/array.test.js +54 -78
  29. package/lib/dts/test/children.test.d.ts +2 -2
  30. package/lib/dts/test/children.test.js +69 -87
  31. package/lib/dts/test/createSchema.d.ts +6 -3
  32. package/lib/dts/test/createSchema.js +17 -17
  33. package/lib/dts/test/generics.test.d.ts +2 -2
  34. package/lib/dts/test/generics.test.js +59 -59
  35. package/lib/dts/test/infer.test.d.ts +2 -2
  36. package/lib/dts/test/infer.test.js +236 -285
  37. package/lib/dts/test/mocks/mockSanityClient.d.ts +4 -5
  38. package/lib/dts/test/mocks/mockSanityClient.d.ts.map +1 -1
  39. package/lib/dts/test/mocks/mockSanityClient.js +6 -6
  40. package/lib/dts/test/nullExport.d.ts +3 -3
  41. package/lib/dts/test/nullExport.js +2 -2
  42. package/lib/dts/test/numbers.test.d.ts +2 -2
  43. package/lib/dts/test/numbers.test.js +56 -62
  44. package/lib/dts/test/strings.test.d.ts +2 -2
  45. package/lib/dts/test/strings.test.js +99 -150
  46. package/lib/dts/tsconfig.lib.tsbuildinfo +1 -1
  47. package/lib/dts/tsconfig.tsbuildinfo +1 -1
  48. package/package.json +4 -4
@@ -1,34 +1,42 @@
1
- var _a
2
- import {cloneDeep, get} from 'lodash'
3
- import ValidationErrorClass from './ValidationError'
4
- import escapeRegex from './util/escapeRegex'
5
- import {convertToValidationMarker} from './util/convertToValidationMarker'
6
- import pathToString from './util/pathToString'
7
- import genericValidator from './validators/genericValidator'
8
- import booleanValidator from './validators/booleanValidator'
9
- import numberValidator from './validators/numberValidator'
10
- import stringValidator from './validators/stringValidator'
11
- import arrayValidator from './validators/arrayValidator'
12
- import objectValidator from './validators/objectValidator'
13
- import dateValidator from './validators/dateValidator'
1
+ var _a;
2
+ import { cloneDeep, get } from 'lodash';
3
+ import ValidationErrorClass from './ValidationError';
4
+ import escapeRegex from './util/escapeRegex';
5
+ import { convertToValidationMarker } from './util/convertToValidationMarker';
6
+ import pathToString from './util/pathToString';
7
+ import genericValidator from './validators/genericValidator';
8
+ import booleanValidator from './validators/booleanValidator';
9
+ import numberValidator from './validators/numberValidator';
10
+ import stringValidator from './validators/stringValidator';
11
+ import arrayValidator from './validators/arrayValidator';
12
+ import objectValidator from './validators/objectValidator';
13
+ import dateValidator from './validators/dateValidator';
14
14
  const typeValidators = {
15
- Boolean: booleanValidator,
16
- Number: numberValidator,
17
- String: stringValidator,
18
- Array: arrayValidator,
19
- Object: objectValidator,
20
- Date: dateValidator,
21
- }
15
+ Boolean: booleanValidator,
16
+ Number: numberValidator,
17
+ String: stringValidator,
18
+ Array: arrayValidator,
19
+ Object: objectValidator,
20
+ Date: dateValidator,
21
+ };
22
22
  const getBaseType = (type) => {
23
- return type && type.type ? getBaseType(type.type) : type
24
- }
23
+ return type && type.type ? getBaseType(type.type) : type;
24
+ };
25
25
  const isFieldRef = (constraint) => {
26
- if (typeof constraint !== 'object' || !constraint) return false
27
- return constraint.type === Rule.FIELD_REF
28
- }
29
- const EMPTY_ARRAY = []
30
- const FIELD_REF = Symbol('FIELD_REF')
31
- const ruleConstraintTypes = ['Array', 'Boolean', 'Date', 'Number', 'Object', 'String']
26
+ if (typeof constraint !== 'object' || !constraint)
27
+ return false;
28
+ return constraint.type === Rule.FIELD_REF;
29
+ };
30
+ const EMPTY_ARRAY = [];
31
+ const FIELD_REF = Symbol('FIELD_REF');
32
+ const ruleConstraintTypes = [
33
+ 'Array',
34
+ 'Boolean',
35
+ 'Date',
36
+ 'Number',
37
+ 'Object',
38
+ 'String',
39
+ ];
32
40
  // Note: `RuleClass` and `Rule` are split to fit the current `@sanity/types`
33
41
  // setup. Classes are a bit weird in the `@sanity/types` package because classes
34
42
  // create an actual javascript class while simultaneously creating a type
@@ -44,288 +52,285 @@ const ruleConstraintTypes = ['Array', 'Boolean', 'Date', 'Number', 'Object', 'St
44
52
  // This package exports the RuleClass as a value without implicitly exporting
45
53
  // an instance definition. This should help reminder downstream users to import
46
54
  // from the `@sanity/types` package.
47
- const Rule =
48
- ((_a = class Rule {
49
- constructor(typeDef) {
50
- this._type = undefined
51
- this._level = undefined
52
- this._required = undefined
53
- this._typeDef = undefined
54
- this._message = undefined
55
- this._rules = []
56
- this._fieldRules = undefined
57
- // Alias to static method, since we often have access to an _instance_ of a rule but not the actual Rule class
58
- this.valueOfField = Rule.valueOfField.bind(Rule)
59
- this._typeDef = typeDef
60
- this.reset()
61
- }
62
- _mergeRequired(next) {
63
- if (this._required === 'required' || next._required === 'required') return 'required'
64
- if (this._required === 'optional' || next._required === 'optional') return 'optional'
65
- return undefined
66
- }
67
- error(message) {
68
- const rule = this.clone()
69
- rule._level = 'error'
70
- rule._message = message || undefined
71
- return rule
72
- }
73
- warning(message) {
74
- const rule = this.clone()
75
- rule._level = 'warning'
76
- rule._message = message || undefined
77
- return rule
78
- }
79
- info(message) {
80
- const rule = this.clone()
81
- rule._level = 'info'
82
- rule._message = message || undefined
83
- return rule
84
- }
85
- reset() {
86
- this._type = this._type || undefined
87
- this._rules = (this._rules || []).filter((rule) => rule.flag === 'type')
88
- this._message = undefined
89
- this._required = undefined
90
- this._level = 'error'
91
- this._fieldRules = undefined
92
- return this
93
- }
94
- isRequired() {
95
- return this._required === 'required'
96
- }
97
- clone() {
98
- const rule = new Rule()
99
- rule._type = this._type
100
- rule._message = this._message
101
- rule._required = this._required
102
- rule._rules = cloneDeep(this._rules)
103
- rule._level = this._level
104
- rule._fieldRules = this._fieldRules
105
- rule._typeDef = this._typeDef
106
- return rule
107
- }
108
- cloneWithRules(rules) {
109
- const rule = this.clone()
110
- const newRules = new Set()
111
- rules.forEach((curr) => {
112
- if (curr.flag === 'type') {
113
- rule._type = curr.constraint
114
- }
115
- newRules.add(curr.flag)
116
- })
117
- rule._rules = rule._rules
118
- .filter((curr) => {
119
- const disallowDuplicate = ['type', 'uri', 'email'].includes(curr.flag)
120
- const isDuplicate = newRules.has(curr.flag)
121
- return !(disallowDuplicate && isDuplicate)
122
- })
123
- .concat(rules)
124
- return rule
125
- }
126
- merge(rule) {
127
- if (this._type && rule._type && this._type !== rule._type) {
128
- throw new Error('merge() failed: conflicting types')
129
- }
130
- const newRule = this.cloneWithRules(rule._rules)
131
- newRule._type = this._type || rule._type
132
- newRule._message = this._message || rule._message
133
- newRule._required = this._mergeRequired(rule)
134
- newRule._level = this._level === 'error' ? rule._level : this._level
135
- return newRule
136
- }
137
- // Validation flag setters
138
- type(targetType) {
139
- const type = `${targetType.slice(0, 1).toUpperCase()}${targetType.slice(1)}`
140
- if (!ruleConstraintTypes.includes(type)) {
141
- throw new Error(`Unknown type "${targetType}"`)
142
- }
143
- const rule = this.cloneWithRules([{flag: 'type', constraint: type}])
144
- rule._type = type
145
- return rule
146
- }
147
- all(children) {
148
- return this.cloneWithRules([{flag: 'all', constraint: children}])
149
- }
150
- either(children) {
151
- return this.cloneWithRules([{flag: 'either', constraint: children}])
152
- }
153
- // Shared rules
154
- optional() {
155
- const rule = this.cloneWithRules([{flag: 'presence', constraint: 'optional'}])
156
- rule._required = 'optional'
157
- return rule
158
- }
159
- required() {
160
- const rule = this.cloneWithRules([{flag: 'presence', constraint: 'required'}])
161
- rule._required = 'required'
162
- return rule
163
- }
164
- custom(fn) {
165
- return this.cloneWithRules([{flag: 'custom', constraint: fn}])
166
- }
167
- /**
168
- * @deprecated use `Rule.custom` instead
169
- */
170
- block(fn) {
171
- return this.cloneWithRules([{flag: 'custom', constraint: fn}])
172
- }
173
- min(len) {
174
- return this.cloneWithRules([{flag: 'min', constraint: len}])
175
- }
176
- max(len) {
177
- return this.cloneWithRules([{flag: 'max', constraint: len}])
178
- }
179
- length(len) {
180
- return this.cloneWithRules([{flag: 'length', constraint: len}])
181
- }
182
- valid(value) {
183
- const values = Array.isArray(value) ? value : [value]
184
- return this.cloneWithRules([{flag: 'valid', constraint: values}])
185
- }
186
- // Numbers only
187
- integer() {
188
- return this.cloneWithRules([{flag: 'integer'}])
189
- }
190
- precision(limit) {
191
- return this.cloneWithRules([{flag: 'precision', constraint: limit}])
192
- }
193
- positive() {
194
- return this.cloneWithRules([{flag: 'min', constraint: 0}])
195
- }
196
- negative() {
197
- return this.cloneWithRules([{flag: 'lessThan', constraint: 0}])
198
- }
199
- greaterThan(num) {
200
- return this.cloneWithRules([{flag: 'greaterThan', constraint: num}])
201
- }
202
- lessThan(num) {
203
- return this.cloneWithRules([{flag: 'lessThan', constraint: num}])
204
- }
205
- // String only
206
- uppercase() {
207
- return this.cloneWithRules([{flag: 'stringCasing', constraint: 'uppercase'}])
208
- }
209
- lowercase() {
210
- return this.cloneWithRules([{flag: 'stringCasing', constraint: 'lowercase'}])
211
- }
212
- regex(pattern, a, b) {
213
- const name = typeof a === 'string' ? a : a?.name ?? b?.name
214
- const invert = typeof a === 'string' ? false : a?.invert ?? b?.invert
215
- const constraint = {
216
- pattern,
217
- name,
218
- invert: invert || false,
219
- }
220
- return this.cloneWithRules([{flag: 'regex', constraint}])
221
- }
222
- email() {
223
- return this.cloneWithRules([{flag: 'email'}])
224
- }
225
- uri(opts) {
226
- const optsScheme = opts?.scheme || ['http', 'https']
227
- const schemes = Array.isArray(optsScheme) ? optsScheme : [optsScheme]
228
- if (!schemes.length) {
229
- throw new Error('scheme must have at least 1 scheme specified')
230
- }
231
- const constraint = {
232
- options: {
233
- scheme: schemes.map((scheme) => {
234
- if (!(scheme instanceof RegExp) && typeof scheme !== 'string') {
235
- throw new Error('scheme must be a RegExp or a String')
55
+ const Rule = (_a = class Rule {
56
+ constructor(typeDef) {
57
+ this._type = undefined;
58
+ this._level = undefined;
59
+ this._required = undefined;
60
+ this._typeDef = undefined;
61
+ this._message = undefined;
62
+ this._rules = [];
63
+ this._fieldRules = undefined;
64
+ // Alias to static method, since we often have access to an _instance_ of a rule but not the actual Rule class
65
+ this.valueOfField = Rule.valueOfField.bind(Rule);
66
+ this._typeDef = typeDef;
67
+ this.reset();
68
+ }
69
+ _mergeRequired(next) {
70
+ if (this._required === 'required' || next._required === 'required')
71
+ return 'required';
72
+ if (this._required === 'optional' || next._required === 'optional')
73
+ return 'optional';
74
+ return undefined;
75
+ }
76
+ error(message) {
77
+ const rule = this.clone();
78
+ rule._level = 'error';
79
+ rule._message = message || undefined;
80
+ return rule;
81
+ }
82
+ warning(message) {
83
+ const rule = this.clone();
84
+ rule._level = 'warning';
85
+ rule._message = message || undefined;
86
+ return rule;
87
+ }
88
+ info(message) {
89
+ const rule = this.clone();
90
+ rule._level = 'info';
91
+ rule._message = message || undefined;
92
+ return rule;
93
+ }
94
+ reset() {
95
+ this._type = this._type || undefined;
96
+ this._rules = (this._rules || []).filter((rule) => rule.flag === 'type');
97
+ this._message = undefined;
98
+ this._required = undefined;
99
+ this._level = 'error';
100
+ this._fieldRules = undefined;
101
+ return this;
102
+ }
103
+ isRequired() {
104
+ return this._required === 'required';
105
+ }
106
+ clone() {
107
+ const rule = new Rule();
108
+ rule._type = this._type;
109
+ rule._message = this._message;
110
+ rule._required = this._required;
111
+ rule._rules = cloneDeep(this._rules);
112
+ rule._level = this._level;
113
+ rule._fieldRules = this._fieldRules;
114
+ rule._typeDef = this._typeDef;
115
+ return rule;
116
+ }
117
+ cloneWithRules(rules) {
118
+ const rule = this.clone();
119
+ const newRules = new Set();
120
+ rules.forEach((curr) => {
121
+ if (curr.flag === 'type') {
122
+ rule._type = curr.constraint;
123
+ }
124
+ newRules.add(curr.flag);
125
+ });
126
+ rule._rules = rule._rules
127
+ .filter((curr) => {
128
+ const disallowDuplicate = ['type', 'uri', 'email'].includes(curr.flag);
129
+ const isDuplicate = newRules.has(curr.flag);
130
+ return !(disallowDuplicate && isDuplicate);
131
+ })
132
+ .concat(rules);
133
+ return rule;
134
+ }
135
+ merge(rule) {
136
+ if (this._type && rule._type && this._type !== rule._type) {
137
+ throw new Error('merge() failed: conflicting types');
138
+ }
139
+ const newRule = this.cloneWithRules(rule._rules);
140
+ newRule._type = this._type || rule._type;
141
+ newRule._message = this._message || rule._message;
142
+ newRule._required = this._mergeRequired(rule);
143
+ newRule._level = this._level === 'error' ? rule._level : this._level;
144
+ return newRule;
145
+ }
146
+ // Validation flag setters
147
+ type(targetType) {
148
+ const type = `${targetType.slice(0, 1).toUpperCase()}${targetType.slice(1)}`;
149
+ if (!ruleConstraintTypes.includes(type)) {
150
+ throw new Error(`Unknown type "${targetType}"`);
151
+ }
152
+ const rule = this.cloneWithRules([{ flag: 'type', constraint: type }]);
153
+ rule._type = type;
154
+ return rule;
155
+ }
156
+ all(children) {
157
+ return this.cloneWithRules([{ flag: 'all', constraint: children }]);
158
+ }
159
+ either(children) {
160
+ return this.cloneWithRules([{ flag: 'either', constraint: children }]);
161
+ }
162
+ // Shared rules
163
+ optional() {
164
+ const rule = this.cloneWithRules([{ flag: 'presence', constraint: 'optional' }]);
165
+ rule._required = 'optional';
166
+ return rule;
167
+ }
168
+ required() {
169
+ const rule = this.cloneWithRules([{ flag: 'presence', constraint: 'required' }]);
170
+ rule._required = 'required';
171
+ return rule;
172
+ }
173
+ custom(fn) {
174
+ return this.cloneWithRules([{ flag: 'custom', constraint: fn }]);
175
+ }
176
+ /**
177
+ * @deprecated use `Rule.custom` instead
178
+ */
179
+ block(fn) {
180
+ return this.cloneWithRules([{ flag: 'custom', constraint: fn }]);
181
+ }
182
+ min(len) {
183
+ return this.cloneWithRules([{ flag: 'min', constraint: len }]);
184
+ }
185
+ max(len) {
186
+ return this.cloneWithRules([{ flag: 'max', constraint: len }]);
187
+ }
188
+ length(len) {
189
+ return this.cloneWithRules([{ flag: 'length', constraint: len }]);
190
+ }
191
+ valid(value) {
192
+ const values = Array.isArray(value) ? value : [value];
193
+ return this.cloneWithRules([{ flag: 'valid', constraint: values }]);
194
+ }
195
+ // Numbers only
196
+ integer() {
197
+ return this.cloneWithRules([{ flag: 'integer' }]);
198
+ }
199
+ precision(limit) {
200
+ return this.cloneWithRules([{ flag: 'precision', constraint: limit }]);
201
+ }
202
+ positive() {
203
+ return this.cloneWithRules([{ flag: 'min', constraint: 0 }]);
204
+ }
205
+ negative() {
206
+ return this.cloneWithRules([{ flag: 'lessThan', constraint: 0 }]);
207
+ }
208
+ greaterThan(num) {
209
+ return this.cloneWithRules([{ flag: 'greaterThan', constraint: num }]);
210
+ }
211
+ lessThan(num) {
212
+ return this.cloneWithRules([{ flag: 'lessThan', constraint: num }]);
213
+ }
214
+ // String only
215
+ uppercase() {
216
+ return this.cloneWithRules([{ flag: 'stringCasing', constraint: 'uppercase' }]);
217
+ }
218
+ lowercase() {
219
+ return this.cloneWithRules([{ flag: 'stringCasing', constraint: 'lowercase' }]);
220
+ }
221
+ regex(pattern, a, b) {
222
+ const name = typeof a === 'string' ? a : a?.name ?? b?.name;
223
+ const invert = typeof a === 'string' ? false : a?.invert ?? b?.invert;
224
+ const constraint = {
225
+ pattern,
226
+ name,
227
+ invert: invert || false,
228
+ };
229
+ return this.cloneWithRules([{ flag: 'regex', constraint }]);
230
+ }
231
+ email() {
232
+ return this.cloneWithRules([{ flag: 'email' }]);
233
+ }
234
+ uri(opts) {
235
+ const optsScheme = opts?.scheme || ['http', 'https'];
236
+ const schemes = Array.isArray(optsScheme) ? optsScheme : [optsScheme];
237
+ if (!schemes.length) {
238
+ throw new Error('scheme must have at least 1 scheme specified');
236
239
  }
237
- return typeof scheme === 'string' ? new RegExp(`^${escapeRegex(scheme)}$`) : scheme
238
- }),
239
- allowRelative: opts?.allowRelative || false,
240
- relativeOnly: opts?.relativeOnly || false,
241
- allowCredentials: opts?.allowCredentials || false,
242
- },
243
- }
244
- return this.cloneWithRules([{flag: 'uri', constraint}])
245
- }
246
- // Array only
247
- unique() {
248
- return this.cloneWithRules([{flag: 'unique'}])
249
- }
250
- // Objects only
251
- reference() {
252
- return this.cloneWithRules([{flag: 'reference'}])
253
- }
254
- fields(rules) {
255
- if (this._type !== 'Object') {
256
- throw new Error('fields() can only be called on an object type')
257
- }
258
- const rule = this.cloneWithRules([])
259
- rule._fieldRules = rules
260
- return rule
261
- }
262
- assetRequired() {
263
- const base = getBaseType(this._typeDef)
264
- let assetType
265
- if (base && ['image', 'file'].includes(base.name)) {
266
- assetType = base.name === 'image' ? 'Image' : 'File'
267
- } else {
268
- assetType = 'Asset'
269
- }
270
- return this.cloneWithRules([{flag: 'assetRequired', constraint: {assetType}}])
271
- }
272
- async validate(value, context) {
273
- if (!context) {
274
- throw new Error('missing context')
275
- }
276
- const valueIsEmpty = value === null || value === undefined
277
- // Short-circuit on optional, empty fields
278
- if (valueIsEmpty && this._required === 'optional') {
279
- return EMPTY_ARRAY
280
- }
281
- const rules =
282
- // Run only the _custom_ functions if the rule is not set to required or optional
283
- this._required === undefined && valueIsEmpty
284
- ? this._rules.filter((curr) => curr.flag === 'custom')
285
- : this._rules
286
- const validators = (this._type && typeValidators[this._type]) || genericValidator
287
- const results = await Promise.all(
288
- rules.map(async (curr) => {
289
- if (curr.flag === undefined) {
290
- throw new Error('Invalid rule, did not contain "flag"-property')
291
- }
292
- const validator = validators[curr.flag]
293
- if (!validator) {
294
- const forType = this._type ? `type "${this._type}"` : 'rule without declared type'
295
- throw new Error(`Validator for flag "${curr.flag}" not found for ${forType}`)
296
- }
297
- let specConstraint = 'constraint' in curr ? curr.constraint : null
298
- if (isFieldRef(specConstraint)) {
299
- specConstraint = get(context.parent, specConstraint.path)
300
- }
301
- let result
302
- try {
303
- result = await validator(specConstraint, value, this._message, context)
304
- } catch (err) {
305
- const errorFromException = new ValidationErrorClass(
306
- `${pathToString(context.path)}: Exception occurred while validating value: ${
307
- err.message
308
- }`
309
- )
310
- return convertToValidationMarker(errorFromException, 'error', context)
311
- }
312
- return convertToValidationMarker(result, this._level, context)
313
- })
314
- )
315
- return results.flat()
316
- }
317
- }),
318
- (_a.FIELD_REF = FIELD_REF),
319
- (_a.array = (def) => new _a(def).type('Array')),
320
- (_a.object = (def) => new _a(def).type('Object')),
321
- (_a.string = (def) => new _a(def).type('String')),
322
- (_a.number = (def) => new _a(def).type('Number')),
323
- (_a.boolean = (def) => new _a(def).type('Boolean')),
324
- (_a.dateTime = (def) => new _a(def).type('Date')),
325
- (_a.valueOfField = (path) => ({
326
- type: FIELD_REF,
327
- path,
328
- })),
329
- _a)
330
- export default Rule
331
- //# sourceMappingURL=Rule.js.map
240
+ const constraint = {
241
+ options: {
242
+ scheme: schemes.map((scheme) => {
243
+ if (!(scheme instanceof RegExp) && typeof scheme !== 'string') {
244
+ throw new Error('scheme must be a RegExp or a String');
245
+ }
246
+ return typeof scheme === 'string' ? new RegExp(`^${escapeRegex(scheme)}$`) : scheme;
247
+ }),
248
+ allowRelative: opts?.allowRelative || false,
249
+ relativeOnly: opts?.relativeOnly || false,
250
+ allowCredentials: opts?.allowCredentials || false,
251
+ },
252
+ };
253
+ return this.cloneWithRules([{ flag: 'uri', constraint }]);
254
+ }
255
+ // Array only
256
+ unique() {
257
+ return this.cloneWithRules([{ flag: 'unique' }]);
258
+ }
259
+ // Objects only
260
+ reference() {
261
+ return this.cloneWithRules([{ flag: 'reference' }]);
262
+ }
263
+ fields(rules) {
264
+ if (this._type !== 'Object') {
265
+ throw new Error('fields() can only be called on an object type');
266
+ }
267
+ const rule = this.cloneWithRules([]);
268
+ rule._fieldRules = rules;
269
+ return rule;
270
+ }
271
+ assetRequired() {
272
+ const base = getBaseType(this._typeDef);
273
+ let assetType;
274
+ if (base && ['image', 'file'].includes(base.name)) {
275
+ assetType = base.name === 'image' ? 'Image' : 'File';
276
+ }
277
+ else {
278
+ assetType = 'Asset';
279
+ }
280
+ return this.cloneWithRules([{ flag: 'assetRequired', constraint: { assetType } }]);
281
+ }
282
+ async validate(value, context) {
283
+ if (!context) {
284
+ throw new Error('missing context');
285
+ }
286
+ const valueIsEmpty = value === null || value === undefined;
287
+ // Short-circuit on optional, empty fields
288
+ if (valueIsEmpty && this._required === 'optional') {
289
+ return EMPTY_ARRAY;
290
+ }
291
+ const rules =
292
+ // Run only the _custom_ functions if the rule is not set to required or optional
293
+ this._required === undefined && valueIsEmpty
294
+ ? this._rules.filter((curr) => curr.flag === 'custom')
295
+ : this._rules;
296
+ const validators = (this._type && typeValidators[this._type]) || genericValidator;
297
+ const results = await Promise.all(rules.map(async (curr) => {
298
+ if (curr.flag === undefined) {
299
+ throw new Error('Invalid rule, did not contain "flag"-property');
300
+ }
301
+ const validator = validators[curr.flag];
302
+ if (!validator) {
303
+ const forType = this._type ? `type "${this._type}"` : 'rule without declared type';
304
+ throw new Error(`Validator for flag "${curr.flag}" not found for ${forType}`);
305
+ }
306
+ let specConstraint = 'constraint' in curr ? curr.constraint : null;
307
+ if (isFieldRef(specConstraint)) {
308
+ specConstraint = get(context.parent, specConstraint.path);
309
+ }
310
+ let result;
311
+ try {
312
+ result = await validator(specConstraint, value, this._message, context);
313
+ }
314
+ catch (err) {
315
+ const errorFromException = new ValidationErrorClass(`${pathToString(context.path)}: Exception occurred while validating value: ${err.message}`);
316
+ return convertToValidationMarker(errorFromException, 'error', context);
317
+ }
318
+ return convertToValidationMarker(result, this._level, context);
319
+ }));
320
+ return results.flat();
321
+ }
322
+ },
323
+ _a.FIELD_REF = FIELD_REF,
324
+ _a.array = (def) => new _a(def).type('Array'),
325
+ _a.object = (def) => new _a(def).type('Object'),
326
+ _a.string = (def) => new _a(def).type('String'),
327
+ _a.number = (def) => new _a(def).type('Number'),
328
+ _a.boolean = (def) => new _a(def).type('Boolean'),
329
+ _a.dateTime = (def) => new _a(def).type('Date'),
330
+ _a.valueOfField = (path) => ({
331
+ type: FIELD_REF,
332
+ path,
333
+ }),
334
+ _a);
335
+ export default Rule;
336
+ //# sourceMappingURL=Rule.js.map