@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.
- package/lib/dts/src/Rule.js +317 -312
- package/lib/dts/src/ValidationError.js +16 -16
- package/lib/dts/src/index.js +7 -7
- package/lib/dts/src/inferFromSchema.js +11 -11
- package/lib/dts/src/inferFromSchemaType.js +24 -24
- package/lib/dts/src/util/convertToValidationMarker.js +44 -53
- package/lib/dts/src/util/deepEquals.js +50 -49
- package/lib/dts/src/util/escapeRegex.js +4 -4
- package/lib/dts/src/util/normalizeValidationRules.js +87 -78
- package/lib/dts/src/util/normalizeValidationRules.test.d.ts +2 -2
- package/lib/dts/src/util/normalizeValidationRules.test.js +149 -149
- package/lib/dts/src/util/pathToString.js +16 -16
- package/lib/dts/src/util/typeString.js +17 -14
- package/lib/dts/src/util/typeString.test.d.ts +2 -2
- package/lib/dts/src/util/typeString.test.js +23 -24
- package/lib/dts/src/validateDocument.js +132 -157
- package/lib/dts/src/validateDocument.test.d.ts +2 -2
- package/lib/dts/src/validateDocument.test.js +664 -678
- package/lib/dts/src/validators/arrayValidator.js +75 -75
- package/lib/dts/src/validators/booleanValidator.js +11 -11
- package/lib/dts/src/validators/dateValidator.js +74 -68
- package/lib/dts/src/validators/genericValidator.js +89 -87
- package/lib/dts/src/validators/numberValidator.js +46 -48
- package/lib/dts/src/validators/objectValidator.js +47 -47
- package/lib/dts/src/validators/slugValidator.js +68 -74
- package/lib/dts/src/validators/stringValidator.js +93 -93
- package/lib/dts/test/array.test.d.ts +2 -2
- package/lib/dts/test/array.test.js +54 -78
- package/lib/dts/test/children.test.d.ts +2 -2
- package/lib/dts/test/children.test.js +69 -87
- package/lib/dts/test/createSchema.d.ts +6 -3
- package/lib/dts/test/createSchema.js +17 -17
- package/lib/dts/test/generics.test.d.ts +2 -2
- package/lib/dts/test/generics.test.js +59 -59
- package/lib/dts/test/infer.test.d.ts +2 -2
- package/lib/dts/test/infer.test.js +236 -285
- package/lib/dts/test/mocks/mockSanityClient.d.ts +4 -5
- package/lib/dts/test/mocks/mockSanityClient.d.ts.map +1 -1
- package/lib/dts/test/mocks/mockSanityClient.js +6 -6
- package/lib/dts/test/nullExport.d.ts +3 -3
- package/lib/dts/test/nullExport.js +2 -2
- package/lib/dts/test/numbers.test.d.ts +2 -2
- package/lib/dts/test/numbers.test.js +56 -62
- package/lib/dts/test/strings.test.d.ts +2 -2
- package/lib/dts/test/strings.test.js +99 -150
- package/lib/dts/tsconfig.lib.tsbuildinfo +1 -1
- package/lib/dts/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -1,167 +1,142 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import {uniqBy} from 'lodash'
|
|
9
|
-
import typeString from './util/typeString'
|
|
10
|
-
import ValidationErrorClass from './ValidationError'
|
|
11
|
-
import normalizeValidationRules from './util/normalizeValidationRules'
|
|
12
|
-
const isRecord = (maybeRecord) =>
|
|
13
|
-
typeof maybeRecord === 'object' && maybeRecord !== null && !Array.isArray(maybeRecord)
|
|
14
|
-
const isNonNullable = (value) => value !== null && value !== undefined
|
|
1
|
+
import { isKeyedObject, isTypedObject, isBlock, isBlockSchemaType, isSpanSchemaType, } from '@sanity/types';
|
|
2
|
+
import { uniqBy } from 'lodash';
|
|
3
|
+
import typeString from './util/typeString';
|
|
4
|
+
import ValidationErrorClass from './ValidationError';
|
|
5
|
+
import normalizeValidationRules from './util/normalizeValidationRules';
|
|
6
|
+
const isRecord = (maybeRecord) => typeof maybeRecord === 'object' && maybeRecord !== null && !Array.isArray(maybeRecord);
|
|
7
|
+
const isNonNullable = (value) => value !== null && value !== undefined;
|
|
15
8
|
/**
|
|
16
9
|
* @internal
|
|
17
10
|
*/
|
|
18
11
|
export function resolveTypeForArrayItem(item, candidates) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
item === undefined || item === null || (!itemType && typeString(item).toLowerCase())
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
candidates.find((candidate) => candidate.name === 'object' && primitive === 'object')
|
|
31
|
-
)
|
|
12
|
+
// if there is only one type available, assume that it's the correct one
|
|
13
|
+
if (candidates.length === 1)
|
|
14
|
+
return candidates[0];
|
|
15
|
+
const itemType = isTypedObject(item) && item._type;
|
|
16
|
+
const primitive = item === undefined || item === null || (!itemType && typeString(item).toLowerCase());
|
|
17
|
+
if (primitive && primitive !== 'object') {
|
|
18
|
+
return candidates.find((candidate) => candidate.jsonType === primitive);
|
|
19
|
+
}
|
|
20
|
+
return (candidates.find((candidate) => candidate.type?.name === itemType) ||
|
|
21
|
+
candidates.find((candidate) => candidate.name === itemType) ||
|
|
22
|
+
candidates.find((candidate) => candidate.name === 'object' && primitive === 'object'));
|
|
32
23
|
}
|
|
33
24
|
export default async function validateDocument(client, doc, schema, context) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
25
|
+
const documentType = schema.get(doc._type);
|
|
26
|
+
if (!documentType) {
|
|
27
|
+
console.warn('Schema type for object type "%s" not found, skipping validation', doc._type);
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
return await validateItem({
|
|
32
|
+
client,
|
|
33
|
+
parent: undefined,
|
|
34
|
+
value: doc,
|
|
35
|
+
path: [],
|
|
36
|
+
document: doc,
|
|
37
|
+
type: documentType,
|
|
38
|
+
getDocumentExists: context?.getDocumentExists,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.error(err);
|
|
43
|
+
return [
|
|
44
|
+
{
|
|
45
|
+
level: 'error',
|
|
46
|
+
path: [],
|
|
47
|
+
item: new ValidationErrorClass(err?.message),
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
}
|
|
59
51
|
}
|
|
60
|
-
export async function validateItem({value, type, path = [], parent, ...restOfContext}) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const selfIsRequired = rules.some((rule) => rule.isRequired())
|
|
74
|
-
const shouldRunNestedObjectValidation =
|
|
52
|
+
export async function validateItem({ value, type, path = [], parent, ...restOfContext }) {
|
|
53
|
+
const rules = normalizeValidationRules(type);
|
|
54
|
+
// run validation for the current value
|
|
55
|
+
const selfChecks = rules.map((rule) => rule.validate(value, {
|
|
56
|
+
...restOfContext,
|
|
57
|
+
parent,
|
|
58
|
+
path,
|
|
59
|
+
type,
|
|
60
|
+
}));
|
|
61
|
+
// run validation for nested values (conditionally)
|
|
62
|
+
let nestedChecks = [];
|
|
63
|
+
const selfIsRequired = rules.some((rule) => rule.isRequired());
|
|
64
|
+
const shouldRunNestedObjectValidation =
|
|
75
65
|
// run nested validation for objects
|
|
76
66
|
type?.jsonType === 'object' &&
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
parent: value,
|
|
152
|
-
value: markDef,
|
|
153
|
-
path: path.concat(['markDefs', {_key: markDef._key}]),
|
|
154
|
-
type: annotations.get(markDef._type),
|
|
155
|
-
})
|
|
156
|
-
)
|
|
157
|
-
)
|
|
158
|
-
}
|
|
159
|
-
const results = (await Promise.all([...selfChecks, ...nestedChecks])).flat()
|
|
160
|
-
// run `uniqBy` if `_fieldRules` are present because they can
|
|
161
|
-
// cause repeat markers
|
|
162
|
-
if (rules.some((rule) => rule._fieldRules)) {
|
|
163
|
-
return uniqBy(results, (rule) => JSON.stringify(rule))
|
|
164
|
-
}
|
|
165
|
-
return results
|
|
67
|
+
// if the value is truthy
|
|
68
|
+
(!!value || // or
|
|
69
|
+
// (the value is null or undefined) and the top-level value is required
|
|
70
|
+
((value === null || value === undefined) && selfIsRequired));
|
|
71
|
+
if (shouldRunNestedObjectValidation) {
|
|
72
|
+
const fieldTypes = type.fields.reduce((acc, field) => {
|
|
73
|
+
acc[field.name] = field.type;
|
|
74
|
+
return acc;
|
|
75
|
+
}, {});
|
|
76
|
+
// Validation for rules set at the object level with `Rule.fields({/* ... */})`
|
|
77
|
+
nestedChecks = nestedChecks.concat(rules
|
|
78
|
+
.map((rule) => rule._fieldRules)
|
|
79
|
+
.filter(isNonNullable)
|
|
80
|
+
.flatMap((fieldResults) => Object.entries(fieldResults))
|
|
81
|
+
.flatMap(([name, validation]) => {
|
|
82
|
+
const fieldType = fieldTypes[name];
|
|
83
|
+
return normalizeValidationRules({ ...fieldType, validation }).map((subRule) => {
|
|
84
|
+
const nestedValue = isRecord(value) ? value[name] : undefined;
|
|
85
|
+
return subRule.validate(nestedValue, {
|
|
86
|
+
...restOfContext,
|
|
87
|
+
parent: value,
|
|
88
|
+
path: path.concat(name),
|
|
89
|
+
type: fieldType,
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
}));
|
|
93
|
+
// Validation from each field's schema `validation: Rule => {/* ... */}` function
|
|
94
|
+
nestedChecks = nestedChecks.concat(type.fields.map((field) => validateItem({
|
|
95
|
+
...restOfContext,
|
|
96
|
+
parent: value,
|
|
97
|
+
value: isRecord(value) ? value[field.name] : undefined,
|
|
98
|
+
path: path.concat(field.name),
|
|
99
|
+
type: field.type,
|
|
100
|
+
})));
|
|
101
|
+
}
|
|
102
|
+
// note: unlike objects, arrays should not run nested validation for undefined
|
|
103
|
+
// values because we won't have a valid path to put a marker (i.e. missing the
|
|
104
|
+
// key or index in the path) and the downstream form builder won't have a
|
|
105
|
+
// valid target component
|
|
106
|
+
const shouldRunNestedValidationForArrays = type?.jsonType === 'array' && Array.isArray(value);
|
|
107
|
+
if (shouldRunNestedValidationForArrays) {
|
|
108
|
+
nestedChecks = nestedChecks.concat(value.map((item) => validateItem({
|
|
109
|
+
...restOfContext,
|
|
110
|
+
parent: value,
|
|
111
|
+
value: item,
|
|
112
|
+
path: path.concat(isKeyedObject(item) ? { _key: item._key } : value.indexOf(item)),
|
|
113
|
+
type: resolveTypeForArrayItem(item, type.of),
|
|
114
|
+
})));
|
|
115
|
+
}
|
|
116
|
+
// markDefs also do no run nested validation if the parent object is undefined
|
|
117
|
+
// for a similar reason to arrays
|
|
118
|
+
const shouldRunNestedValidationForMarkDefs = isBlock(value) && value.markDefs.length && isBlockSchemaType(type);
|
|
119
|
+
if (shouldRunNestedValidationForMarkDefs) {
|
|
120
|
+
const [spanChildrenField] = type.fields;
|
|
121
|
+
const spanType = spanChildrenField.type.of.find(isSpanSchemaType);
|
|
122
|
+
const annotations = (spanType?.annotations || []).reduce((map, annotationType) => {
|
|
123
|
+
map.set(annotationType.name, annotationType);
|
|
124
|
+
return map;
|
|
125
|
+
}, new Map());
|
|
126
|
+
nestedChecks = nestedChecks.concat(value.markDefs.map((markDef) => validateItem({
|
|
127
|
+
...restOfContext,
|
|
128
|
+
parent: value,
|
|
129
|
+
value: markDef,
|
|
130
|
+
path: path.concat(['markDefs', { _key: markDef._key }]),
|
|
131
|
+
type: annotations.get(markDef._type),
|
|
132
|
+
})));
|
|
133
|
+
}
|
|
134
|
+
const results = (await Promise.all([...selfChecks, ...nestedChecks])).flat();
|
|
135
|
+
// run `uniqBy` if `_fieldRules` are present because they can
|
|
136
|
+
// cause repeat markers
|
|
137
|
+
if (rules.some((rule) => rule._fieldRules)) {
|
|
138
|
+
return uniqBy(results, (rule) => JSON.stringify(rule));
|
|
139
|
+
}
|
|
140
|
+
return results;
|
|
166
141
|
}
|
|
167
|
-
//# sourceMappingURL=validateDocument.js.map
|
|
142
|
+
//# sourceMappingURL=validateDocument.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {}
|
|
2
|
-
//# sourceMappingURL=validateDocument.test.d.ts.map
|
|
1
|
+
export {};
|
|
2
|
+
//# sourceMappingURL=validateDocument.test.d.ts.map
|