@sanity/validation 2.33.3-performance-opts.5 → 2.33.3-performance-opts.10
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.
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { SanityDocument, Schema, SchemaType, ValidationContext, ValidationMarker } from '@sanity/types';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
2
3
|
/**
|
|
3
4
|
* @internal
|
|
4
5
|
*/
|
|
5
6
|
export declare function resolveTypeForArrayItem(item: unknown, candidates: SchemaType[]): SchemaType | undefined;
|
|
6
|
-
export default function validateDocument(doc: SanityDocument, schema: Schema, context?: Pick<ValidationContext, 'getDocumentExists'>):
|
|
7
|
+
export default function validateDocument(doc: SanityDocument, schema: Schema, context?: Pick<ValidationContext, 'getDocumentExists'>): Observable<ValidationMarker[]>;
|
|
7
8
|
/**
|
|
8
9
|
* this is used make optional properties required by replacing optionals with
|
|
9
10
|
* `T[P] | undefined`. this is used to prevent errors in `validateItem` where
|
|
@@ -17,6 +18,6 @@ declare type ExplicitUndefined<T> = {
|
|
|
17
18
|
declare type ValidateItemOptions = {
|
|
18
19
|
value: unknown;
|
|
19
20
|
} & ExplicitUndefined<ValidationContext>;
|
|
20
|
-
export declare function validateItem({ value, type, path, parent, ...restOfContext }: ValidateItemOptions):
|
|
21
|
+
export declare function validateItem({ value, type, path, parent, ...restOfContext }: ValidateItemOptions): Observable<ValidationMarker[]>;
|
|
21
22
|
export {};
|
|
22
23
|
//# sourceMappingURL=validateDocument.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validateDocument.d.ts","sourceRoot":"","sources":["../../src/validateDocument.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"validateDocument.d.ts","sourceRoot":"","sources":["../../src/validateDocument.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,cAAc,EACd,MAAM,EACN,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAuB,UAAU,EAAK,MAAM,MAAM,CAAA;AAuBzD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,UAAU,EAAE,GACvB,UAAU,GAAG,SAAS,CAiBxB;AAGD,MAAM,CAAC,OAAO,UAAU,gBAAgB,CACtC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,GACrD,UAAU,CAAC,gBAAgB,EAAE,CAAC,CA2BhC;AAED;;;;;;GAMG;AACH,aAAK,iBAAiB,CAAC,CAAC,IAAI;KACzB,CAAC,IAAI,MAAM,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS;CAC5F,CAAA;AAED,aAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE,OAAO,CAAA;CACf,GAAG,iBAAiB,CAAC,iBAAiB,CAAC,CAAA;AAExC,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,IAAI,EACJ,IAAS,EACT,MAAM,EACN,GAAG,aAAa,EACjB,EAAE,mBAAmB,GAAG,UAAU,CAAC,gBAAgB,EAAE,CAAC,CAsItD"}
|
package/lib/validateDocument.js
CHANGED
|
@@ -9,8 +9,14 @@ exports.validateItem = validateItem;
|
|
|
9
9
|
|
|
10
10
|
var _uniqBy2 = _interopRequireDefault(require("lodash/uniqBy"));
|
|
11
11
|
|
|
12
|
+
var _flatten2 = _interopRequireDefault(require("lodash/flatten"));
|
|
13
|
+
|
|
12
14
|
var _types = require("@sanity/types");
|
|
13
15
|
|
|
16
|
+
var _rxjs = require("rxjs");
|
|
17
|
+
|
|
18
|
+
var _operators = require("rxjs/operators");
|
|
19
|
+
|
|
14
20
|
var _typeString = _interopRequireDefault(require("./util/typeString"));
|
|
15
21
|
|
|
16
22
|
var _ValidationError = _interopRequireDefault(require("./ValidationError"));
|
|
@@ -43,10 +49,6 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
|
|
|
43
49
|
|
|
44
50
|
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
45
51
|
|
|
46
|
-
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
47
|
-
|
|
48
|
-
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
49
|
-
|
|
50
52
|
var isRecord = maybeRecord => typeof maybeRecord === 'object' && maybeRecord !== null && !Array.isArray(maybeRecord);
|
|
51
53
|
|
|
52
54
|
var isNonNullable = value => value !== null && value !== undefined;
|
|
@@ -74,8 +76,32 @@ function resolveTypeForArrayItem(item, candidates) {
|
|
|
74
76
|
}) || candidates.find(candidate => candidate.name === itemType) || candidates.find(candidate => candidate.name === 'object' && primitive === 'object');
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
var EMPTY_MARKERS = [];
|
|
80
|
+
|
|
81
|
+
function validateDocument(doc, schema, context) {
|
|
82
|
+
var documentType = schema.get(doc._type);
|
|
83
|
+
|
|
84
|
+
if (!documentType) {
|
|
85
|
+
console.warn('Schema type for object type "%s" not found, skipping validation', doc._type);
|
|
86
|
+
return (0, _rxjs.of)(EMPTY_MARKERS);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return validateItem({
|
|
90
|
+
parent: undefined,
|
|
91
|
+
value: doc,
|
|
92
|
+
path: [],
|
|
93
|
+
document: doc,
|
|
94
|
+
type: documentType,
|
|
95
|
+
getDocumentExists: context === null || context === void 0 ? void 0 : context.getDocumentExists
|
|
96
|
+
}).pipe((0, _operators.catchError)(err => {
|
|
97
|
+
console.error(err);
|
|
98
|
+
return (0, _rxjs.of)([{
|
|
99
|
+
type: 'validation',
|
|
100
|
+
level: 'error',
|
|
101
|
+
path: [],
|
|
102
|
+
item: new _ValidationError.default(err === null || err === void 0 ? void 0 : err.message)
|
|
103
|
+
}]);
|
|
104
|
+
}));
|
|
79
105
|
}
|
|
80
106
|
/**
|
|
81
107
|
* this is used make optional properties required by replacing optionals with
|
|
@@ -86,149 +112,120 @@ function validateDocument(_x, _x2, _x3) {
|
|
|
86
112
|
*/
|
|
87
113
|
|
|
88
114
|
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
115
|
+
function validateItem(_ref) {
|
|
116
|
+
var value = _ref.value,
|
|
117
|
+
type = _ref.type,
|
|
118
|
+
_ref$path = _ref.path,
|
|
119
|
+
path = _ref$path === void 0 ? [] : _ref$path,
|
|
120
|
+
parent = _ref.parent,
|
|
121
|
+
restOfContext = _objectWithoutProperties(_ref, _excluded);
|
|
122
|
+
|
|
123
|
+
var rules = (0, _normalizeValidationRules.default)(type); // run validation for the current value
|
|
124
|
+
|
|
125
|
+
var selfChecks = rules.map(rule => (0, _rxjs.defer)(() => rule.validate(value, _objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
126
|
+
parent,
|
|
127
|
+
path,
|
|
128
|
+
type
|
|
129
|
+
})))); // run validation for nested values (conditionally)
|
|
130
|
+
|
|
131
|
+
var fieldChecks = [];
|
|
132
|
+
var selfIsRequired = rules.some(rule => rule.isRequired());
|
|
133
|
+
var shouldRunNestedObjectValidation = // run nested validation for objects
|
|
134
|
+
(type === null || type === void 0 ? void 0 : type.jsonType) === 'object' && ( // if the value is truthy
|
|
135
|
+
!!value || // or
|
|
136
|
+
// (the value is null or undefined) and the top-level value is required
|
|
137
|
+
(value === null || value === undefined) && selfIsRequired);
|
|
138
|
+
|
|
139
|
+
if (shouldRunNestedObjectValidation) {
|
|
140
|
+
var fieldTypes = type.fields.reduce((acc, field) => {
|
|
141
|
+
acc[field.name] = field.type;
|
|
142
|
+
return acc;
|
|
143
|
+
}, {}); // Validation for rules set at the object level with `Rule.fields({/* ... */})`
|
|
144
|
+
|
|
145
|
+
fieldChecks = fieldChecks.concat(rules.map(rule => rule._fieldRules).filter(isNonNullable).flatMap(fieldResults => Object.entries(fieldResults)).flatMap(_ref2 => {
|
|
146
|
+
var _ref3 = _slicedToArray(_ref2, 2),
|
|
147
|
+
name = _ref3[0],
|
|
148
|
+
validation = _ref3[1];
|
|
149
|
+
|
|
150
|
+
var fieldType = fieldTypes[name];
|
|
151
|
+
return (0, _normalizeValidationRules.default)(_objectSpread(_objectSpread({}, fieldType), {}, {
|
|
152
|
+
validation
|
|
153
|
+
})).map(subRule => {
|
|
154
|
+
var nestedValue = isRecord(value) ? value[name] : undefined;
|
|
155
|
+
return (0, _rxjs.defer)(() => subRule.validate(nestedValue, _objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
156
|
+
parent: value,
|
|
157
|
+
path: path.concat(name),
|
|
158
|
+
type: fieldType
|
|
159
|
+
})));
|
|
106
160
|
});
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}, {}); // Validation for rules set at the object level with `Rule.fields({/* ... */})`
|
|
157
|
-
|
|
158
|
-
nestedChecks = nestedChecks.concat(rules.map(rule => rule._fieldRules).filter(isNonNullable).flatMap(fieldResults => Object.entries(fieldResults)).flatMap(_ref2 => {
|
|
159
|
-
var _ref3 = _slicedToArray(_ref2, 2),
|
|
160
|
-
name = _ref3[0],
|
|
161
|
-
validation = _ref3[1];
|
|
162
|
-
|
|
163
|
-
var fieldType = fieldTypes[name];
|
|
164
|
-
return (0, _normalizeValidationRules.default)(_objectSpread(_objectSpread({}, fieldType), {}, {
|
|
165
|
-
validation
|
|
166
|
-
})).map(subRule => {
|
|
167
|
-
var nestedValue = isRecord(value) ? value[name] : undefined;
|
|
168
|
-
return subRule.validate(nestedValue, _objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
169
|
-
parent: value,
|
|
170
|
-
path: path.concat(name),
|
|
171
|
-
type: fieldType
|
|
172
|
-
}));
|
|
173
|
-
});
|
|
174
|
-
})); // Validation from each field's schema `validation: Rule => {/* ... */}` function
|
|
175
|
-
|
|
176
|
-
nestedChecks = nestedChecks.concat(type.fields.map(field => validateItem(_objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
177
|
-
parent: value,
|
|
178
|
-
value: isRecord(value) ? value[field.name] : undefined,
|
|
179
|
-
path: path.concat(field.name),
|
|
180
|
-
type: field.type
|
|
181
|
-
}))));
|
|
182
|
-
} // note: unlike objects, arrays should not run nested validation for undefined
|
|
183
|
-
// values because we won't have a valid path to put a marker (i.e. missing the
|
|
184
|
-
// key or index in the path) and the downstream form builder won't have a
|
|
185
|
-
// valid target component
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
var shouldRunNestedValidationForArrays = (type === null || type === void 0 ? void 0 : type.jsonType) === 'array' && Array.isArray(value);
|
|
189
|
-
|
|
190
|
-
if (shouldRunNestedValidationForArrays) {
|
|
191
|
-
nestedChecks = nestedChecks.concat(value.map(item => validateItem(_objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
192
|
-
parent: value,
|
|
193
|
-
value: item,
|
|
194
|
-
path: path.concat((0, _types.isKeyedObject)(item) ? {
|
|
195
|
-
_key: item._key
|
|
196
|
-
} : value.indexOf(item)),
|
|
197
|
-
type: resolveTypeForArrayItem(item, type.of)
|
|
198
|
-
}))));
|
|
199
|
-
} // markDefs also do no run nested validation if the parent object is undefined
|
|
200
|
-
// for a similar reason to arrays
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
var shouldRunNestedValidationForMarkDefs = (0, _types.isBlock)(value) && value.markDefs.length && (0, _types.isBlockSchemaType)(type);
|
|
204
|
-
|
|
205
|
-
if (shouldRunNestedValidationForMarkDefs) {
|
|
206
|
-
var _type$fields = _slicedToArray(type.fields, 1),
|
|
207
|
-
spanChildrenField = _type$fields[0];
|
|
208
|
-
|
|
209
|
-
var spanType = spanChildrenField.type.of.find(_types.isSpanSchemaType);
|
|
210
|
-
var annotations = ((spanType === null || spanType === void 0 ? void 0 : spanType.annotations) || []).reduce((map, annotationType) => {
|
|
211
|
-
map.set(annotationType.name, annotationType);
|
|
212
|
-
return map;
|
|
213
|
-
}, new Map());
|
|
214
|
-
nestedChecks = nestedChecks.concat(value.markDefs.map(markDef => validateItem(_objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
215
|
-
parent: value,
|
|
216
|
-
value: markDef,
|
|
217
|
-
path: path.concat(['markDefs', {
|
|
218
|
-
_key: markDef._key
|
|
219
|
-
}]),
|
|
220
|
-
type: annotations.get(markDef._type)
|
|
221
|
-
}))));
|
|
222
|
-
}
|
|
161
|
+
})); // Validation from each field's schema `validation: Rule => {/* ... */}` function
|
|
162
|
+
|
|
163
|
+
fieldChecks = fieldChecks.concat(type.fields.map(field => validateItem(_objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
164
|
+
parent: value,
|
|
165
|
+
value: isRecord(value) ? value[field.name] : undefined,
|
|
166
|
+
path: path.concat(field.name),
|
|
167
|
+
type: field.type
|
|
168
|
+
}))));
|
|
169
|
+
} // note: unlike objects, arrays should not run nested validation for undefined
|
|
170
|
+
// values because we won't have a valid path to put a marker (i.e. missing the
|
|
171
|
+
// key or index in the path) and the downstream form builder won't have a
|
|
172
|
+
// valid target component
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
var shouldRunNestedValidationForArrays = (type === null || type === void 0 ? void 0 : type.jsonType) === 'array' && Array.isArray(value);
|
|
176
|
+
|
|
177
|
+
if (shouldRunNestedValidationForArrays) {
|
|
178
|
+
fieldChecks = fieldChecks.concat(value.map(item => validateItem(_objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
179
|
+
parent: value,
|
|
180
|
+
value: item,
|
|
181
|
+
path: path.concat((0, _types.isKeyedObject)(item) ? {
|
|
182
|
+
_key: item._key
|
|
183
|
+
} : value.indexOf(item)),
|
|
184
|
+
type: resolveTypeForArrayItem(item, type.of)
|
|
185
|
+
}))));
|
|
186
|
+
} // markDefs also do no run nested validation if the parent object is undefined
|
|
187
|
+
// for a similar reason to arrays
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
var shouldRunNestedValidationForMarkDefs = (0, _types.isBlock)(value) && value.markDefs.length && (0, _types.isBlockSchemaType)(type);
|
|
191
|
+
|
|
192
|
+
if (shouldRunNestedValidationForMarkDefs) {
|
|
193
|
+
var _type$fields = _slicedToArray(type.fields, 1),
|
|
194
|
+
spanChildrenField = _type$fields[0];
|
|
195
|
+
|
|
196
|
+
var spanType = spanChildrenField.type.of.find(_types.isSpanSchemaType);
|
|
197
|
+
var annotations = ((spanType === null || spanType === void 0 ? void 0 : spanType.annotations) || []).reduce((map, annotationType) => {
|
|
198
|
+
map.set(annotationType.name, annotationType);
|
|
199
|
+
return map;
|
|
200
|
+
}, new Map());
|
|
201
|
+
fieldChecks = fieldChecks.concat(value.markDefs.map(markDef => validateItem(_objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
202
|
+
parent: value,
|
|
203
|
+
value: markDef,
|
|
204
|
+
path: path.concat(['markDefs', {
|
|
205
|
+
_key: markDef._key
|
|
206
|
+
}]),
|
|
207
|
+
type: annotations.get(markDef._type)
|
|
208
|
+
}))));
|
|
209
|
+
}
|
|
223
210
|
|
|
224
|
-
|
|
211
|
+
return (0, _rxjs.defer)(() => (0, _rxjs.merge)([...selfChecks, ...fieldChecks])).pipe((0, _operators.mergeMap)(validateNode => (0, _rxjs.concat)(idle(), validateNode), 40), (0, _operators.mergeAll)(), (0, _operators.toArray)(), (0, _operators.filter)(arr => arr.length > 0), (0, _operators.map)(_flatten2.default), (0, _operators.map)(results => {
|
|
212
|
+
// run `uniqBy` if `_fieldRules` are present because they can
|
|
225
213
|
// cause repeat markers
|
|
226
|
-
|
|
227
214
|
if (rules.some(rule => rule._fieldRules)) {
|
|
228
215
|
return (0, _uniqBy2.default)(results, rule => JSON.stringify(rule));
|
|
229
216
|
}
|
|
230
217
|
|
|
231
218
|
return results;
|
|
219
|
+
}), (0, _operators.filter)(arr => arr.length > 0));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function idle(timeout) {
|
|
223
|
+
return new _rxjs.Observable(observer => {
|
|
224
|
+
var handle = requestIdleCallback(() => {
|
|
225
|
+
observer.complete();
|
|
226
|
+
}, timeout ? {
|
|
227
|
+
timeout
|
|
228
|
+
} : undefined);
|
|
229
|
+
return () => cancelIdleCallback(handle);
|
|
232
230
|
});
|
|
233
|
-
return _validateItem.apply(this, arguments);
|
|
234
231
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/validation",
|
|
3
|
-
"version": "2.33.3-performance-opts.
|
|
3
|
+
"version": "2.33.3-performance-opts.10+9048d6b799",
|
|
4
4
|
"description": "Validation and warning infrastructure for Sanity projects",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"types": "./dist/dts",
|
|
@@ -33,12 +33,13 @@
|
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@sanity/client": "^3.3.3",
|
|
36
|
-
"@sanity/schema": "2.33.3-performance-opts.
|
|
36
|
+
"@sanity/schema": "2.33.3-performance-opts.10+9048d6b799"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@sanity/types": "2.33.3-performance-opts.
|
|
39
|
+
"@sanity/types": "2.33.3-performance-opts.10+9048d6b799",
|
|
40
40
|
"date-fns": "^2.16.1",
|
|
41
|
-
"lodash": "^4.17.15"
|
|
41
|
+
"lodash": "^4.17.15",
|
|
42
|
+
"rxjs": "^6.5.3"
|
|
42
43
|
},
|
|
43
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "9048d6b79973532d481902a30f37c0bd700a2836"
|
|
44
45
|
}
|
package/src/validateDocument.ts
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
|
+
isBlock,
|
|
3
|
+
isBlockSchemaType,
|
|
4
|
+
isKeyedObject,
|
|
5
|
+
isSpanSchemaType,
|
|
6
|
+
isTypedObject,
|
|
2
7
|
SanityDocument,
|
|
3
8
|
Schema,
|
|
4
9
|
SchemaType,
|
|
5
10
|
ValidationContext,
|
|
6
11
|
ValidationMarker,
|
|
7
|
-
isKeyedObject,
|
|
8
|
-
isTypedObject,
|
|
9
|
-
isBlock,
|
|
10
|
-
isBlockSchemaType,
|
|
11
|
-
isSpanSchemaType,
|
|
12
12
|
} from '@sanity/types'
|
|
13
|
-
import {
|
|
13
|
+
import {concat, defer, merge, Observable, of} from 'rxjs'
|
|
14
|
+
import {
|
|
15
|
+
catchError,
|
|
16
|
+
combineAll,
|
|
17
|
+
concatMap,
|
|
18
|
+
filter,
|
|
19
|
+
map,
|
|
20
|
+
mergeAll,
|
|
21
|
+
mergeMap,
|
|
22
|
+
tap,
|
|
23
|
+
toArray,
|
|
24
|
+
} from 'rxjs/operators'
|
|
25
|
+
import {flatten, uniqBy} from 'lodash'
|
|
14
26
|
import typeString from './util/typeString'
|
|
15
27
|
import ValidationErrorClass from './ValidationError'
|
|
16
28
|
import normalizeValidationRules from './util/normalizeValidationRules'
|
|
@@ -45,38 +57,39 @@ export function resolveTypeForArrayItem(
|
|
|
45
57
|
candidates.find((candidate) => candidate.name === 'object' && primitive === 'object')
|
|
46
58
|
)
|
|
47
59
|
}
|
|
60
|
+
const EMPTY_MARKERS: ValidationMarker[] = []
|
|
48
61
|
|
|
49
|
-
export default
|
|
62
|
+
export default function validateDocument(
|
|
50
63
|
doc: SanityDocument,
|
|
51
64
|
schema: Schema,
|
|
52
65
|
context?: Pick<ValidationContext, 'getDocumentExists'>
|
|
53
|
-
):
|
|
66
|
+
): Observable<ValidationMarker[]> {
|
|
54
67
|
const documentType = schema.get(doc._type)
|
|
55
68
|
if (!documentType) {
|
|
56
69
|
console.warn('Schema type for object type "%s" not found, skipping validation', doc._type)
|
|
57
|
-
return
|
|
70
|
+
return of(EMPTY_MARKERS)
|
|
58
71
|
}
|
|
59
72
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
return validateItem({
|
|
74
|
+
parent: undefined,
|
|
75
|
+
value: doc,
|
|
76
|
+
path: [],
|
|
77
|
+
document: doc,
|
|
78
|
+
type: documentType,
|
|
79
|
+
getDocumentExists: context?.getDocumentExists,
|
|
80
|
+
}).pipe(
|
|
81
|
+
catchError((err) => {
|
|
82
|
+
console.error(err)
|
|
83
|
+
return of([
|
|
84
|
+
{
|
|
85
|
+
type: 'validation' as const,
|
|
86
|
+
level: 'error' as const,
|
|
87
|
+
path: [],
|
|
88
|
+
item: new ValidationErrorClass(err?.message),
|
|
89
|
+
},
|
|
90
|
+
])
|
|
68
91
|
})
|
|
69
|
-
|
|
70
|
-
console.error(err)
|
|
71
|
-
return [
|
|
72
|
-
{
|
|
73
|
-
type: 'validation',
|
|
74
|
-
level: 'error',
|
|
75
|
-
path: [],
|
|
76
|
-
item: new ValidationErrorClass(err?.message),
|
|
77
|
-
},
|
|
78
|
-
]
|
|
79
|
-
}
|
|
92
|
+
)
|
|
80
93
|
}
|
|
81
94
|
|
|
82
95
|
/**
|
|
@@ -94,30 +107,28 @@ type ValidateItemOptions = {
|
|
|
94
107
|
value: unknown
|
|
95
108
|
} & ExplicitUndefined<ValidationContext>
|
|
96
109
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
export async function validateItem({
|
|
110
|
+
export function validateItem({
|
|
100
111
|
value,
|
|
101
112
|
type,
|
|
102
113
|
path = [],
|
|
103
114
|
parent,
|
|
104
115
|
...restOfContext
|
|
105
|
-
}: ValidateItemOptions):
|
|
106
|
-
await idle()
|
|
116
|
+
}: ValidateItemOptions): Observable<ValidationMarker[]> {
|
|
107
117
|
const rules = normalizeValidationRules(type)
|
|
108
|
-
|
|
109
118
|
// run validation for the current value
|
|
110
119
|
const selfChecks = rules.map((rule) =>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
defer(() =>
|
|
121
|
+
rule.validate(value, {
|
|
122
|
+
...restOfContext,
|
|
123
|
+
parent,
|
|
124
|
+
path,
|
|
125
|
+
type,
|
|
126
|
+
})
|
|
127
|
+
)
|
|
117
128
|
)
|
|
118
129
|
|
|
119
130
|
// run validation for nested values (conditionally)
|
|
120
|
-
let
|
|
131
|
+
let fieldChecks: Array<Observable<ValidationMarker[]>> = []
|
|
121
132
|
|
|
122
133
|
const selfIsRequired = rules.some((rule) => rule.isRequired())
|
|
123
134
|
const shouldRunNestedObjectValidation =
|
|
@@ -135,7 +146,7 @@ export async function validateItem({
|
|
|
135
146
|
}, {})
|
|
136
147
|
|
|
137
148
|
// Validation for rules set at the object level with `Rule.fields({/* ... */})`
|
|
138
|
-
|
|
149
|
+
fieldChecks = fieldChecks.concat(
|
|
139
150
|
rules
|
|
140
151
|
.map((rule) => rule._fieldRules)
|
|
141
152
|
.filter(isNonNullable)
|
|
@@ -144,18 +155,20 @@ export async function validateItem({
|
|
|
144
155
|
const fieldType = fieldTypes[name]
|
|
145
156
|
return normalizeValidationRules({...fieldType, validation}).map((subRule) => {
|
|
146
157
|
const nestedValue = isRecord(value) ? value[name] : undefined
|
|
147
|
-
return
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
return defer(() =>
|
|
159
|
+
subRule.validate(nestedValue, {
|
|
160
|
+
...restOfContext,
|
|
161
|
+
parent: value,
|
|
162
|
+
path: path.concat(name),
|
|
163
|
+
type: fieldType,
|
|
164
|
+
})
|
|
165
|
+
)
|
|
153
166
|
})
|
|
154
167
|
})
|
|
155
168
|
)
|
|
156
169
|
|
|
157
170
|
// Validation from each field's schema `validation: Rule => {/* ... */}` function
|
|
158
|
-
|
|
171
|
+
fieldChecks = fieldChecks.concat(
|
|
159
172
|
type.fields.map((field) =>
|
|
160
173
|
validateItem({
|
|
161
174
|
...restOfContext,
|
|
@@ -175,7 +188,7 @@ export async function validateItem({
|
|
|
175
188
|
const shouldRunNestedValidationForArrays = type?.jsonType === 'array' && Array.isArray(value)
|
|
176
189
|
|
|
177
190
|
if (shouldRunNestedValidationForArrays) {
|
|
178
|
-
|
|
191
|
+
fieldChecks = fieldChecks.concat(
|
|
179
192
|
value.map((item) =>
|
|
180
193
|
validateItem({
|
|
181
194
|
...restOfContext,
|
|
@@ -205,7 +218,7 @@ export async function validateItem({
|
|
|
205
218
|
new Map()
|
|
206
219
|
)
|
|
207
220
|
|
|
208
|
-
|
|
221
|
+
fieldChecks = fieldChecks.concat(
|
|
209
222
|
value.markDefs.map((markDef) =>
|
|
210
223
|
validateItem({
|
|
211
224
|
...restOfContext,
|
|
@@ -218,13 +231,33 @@ export async function validateItem({
|
|
|
218
231
|
)
|
|
219
232
|
}
|
|
220
233
|
|
|
221
|
-
|
|
234
|
+
return defer(() => merge([...selfChecks, ...fieldChecks])).pipe(
|
|
235
|
+
mergeMap((validateNode) => concat(idle(), validateNode), 40),
|
|
236
|
+
mergeAll(),
|
|
237
|
+
toArray(),
|
|
238
|
+
filter((arr) => arr.length > 0),
|
|
239
|
+
map(flatten),
|
|
240
|
+
map((results) => {
|
|
241
|
+
// run `uniqBy` if `_fieldRules` are present because they can
|
|
242
|
+
// cause repeat markers
|
|
243
|
+
if (rules.some((rule) => rule._fieldRules)) {
|
|
244
|
+
return uniqBy(results, (rule) => JSON.stringify(rule))
|
|
245
|
+
}
|
|
246
|
+
return results
|
|
247
|
+
}),
|
|
248
|
+
filter((arr) => arr.length > 0)
|
|
249
|
+
)
|
|
250
|
+
}
|
|
222
251
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
252
|
+
function idle(timeout?: number): Observable<never> {
|
|
253
|
+
return new Observable<never>((observer) => {
|
|
254
|
+
const handle = requestIdleCallback(
|
|
255
|
+
() => {
|
|
256
|
+
observer.complete()
|
|
257
|
+
},
|
|
258
|
+
timeout ? {timeout} : undefined
|
|
259
|
+
)
|
|
228
260
|
|
|
229
|
-
|
|
261
|
+
return () => cancelIdleCallback(handle)
|
|
262
|
+
})
|
|
230
263
|
}
|