@sanity/validation 2.33.4-shopify.8 → 2.34.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/dist/dts/index.d.ts +1 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/util/requestIdleCallback.d.ts +3 -0
- package/dist/dts/util/requestIdleCallback.d.ts.map +1 -0
- package/dist/dts/validateDocument.d.ts +3 -1
- package/dist/dts/validateDocument.d.ts.map +1 -1
- package/lib/index.js +11 -1
- package/lib/util/requestIdleCallback.js +35 -0
- package/lib/validateDocument.js +149 -138
- package/package.json +6 -5
- package/src/index.ts +2 -0
- package/src/util/requestIdleCallback.ts +29 -0
- package/src/validateDocument.test.ts +0 -77
- package/src/validateDocument.ts +97 -60
package/dist/dts/index.d.ts
CHANGED
package/dist/dts/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,QAAQ,CAAA;AAC9B,OAAO,gBAAgB,MAAM,oBAAoB,CAAA;AACjD,OAAO,eAAe,MAAM,mBAAmB,CAAA;AAC/C,OAAO,mBAAmB,MAAM,uBAAuB,CAAA;;;;;;;AAEvD,wBAAwF;AACxF,OAAO,EAAC,SAAS,IAAI,IAAI,EAAE,gBAAgB,EAAE,eAAe,EAAE,mBAAmB,EAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,QAAQ,CAAA;AAC9B,OAAO,gBAAgB,MAAM,oBAAoB,CAAA;AACjD,OAAO,eAAe,MAAM,mBAAmB,CAAA;AAC/C,OAAO,mBAAmB,MAAM,uBAAuB,CAAA;;;;;;;AAEvD,wBAAwF;AACxF,OAAO,EAAC,SAAS,IAAI,IAAI,EAAE,gBAAgB,EAAE,eAAe,EAAE,mBAAmB,EAAC,CAAA;AAElF,OAAO,EAAC,0BAA0B,EAAC,MAAM,oBAAoB,CAAA"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const requestIdleCallback: ((callback: IdleRequestCallback, options?: IdleRequestOptions | undefined) => number) & typeof globalThis.requestIdleCallback;
|
|
2
|
+
export declare const cancelIdleCallback: ((handle: number) => void) & typeof globalThis.cancelIdleCallback;
|
|
3
|
+
//# sourceMappingURL=requestIdleCallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestIdleCallback.d.ts","sourceRoot":"","sources":["../../../src/util/requestIdleCallback.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,mBAAmB,+HAAwD,CAAA;AACxF,eAAO,MAAM,kBAAkB,mEAAsD,CAAA"}
|
|
@@ -1,9 +1,11 @@
|
|
|
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
7
|
export default function validateDocument(doc: SanityDocument, schema: Schema, context?: Pick<ValidationContext, 'getDocumentExists'>): Promise<ValidationMarker[]>;
|
|
8
|
+
export declare function validateDocumentObservable(doc: SanityDocument, schema: Schema, context?: Pick<ValidationContext, 'getDocumentExists'>): Observable<ValidationMarker[]>;
|
|
7
9
|
/**
|
|
8
10
|
* this is used make optional properties required by replacing optionals with
|
|
9
11
|
* `T[P] | undefined`. this is used to prevent errors in `validateItem` where
|
|
@@ -17,6 +19,6 @@ declare type ExplicitUndefined<T> = {
|
|
|
17
19
|
declare type ValidateItemOptions = {
|
|
18
20
|
value: unknown;
|
|
19
21
|
} & ExplicitUndefined<ValidationContext>;
|
|
20
|
-
export declare function validateItem(
|
|
22
|
+
export declare function validateItem(opts: ValidateItemOptions): Promise<ValidationMarker[]>;
|
|
21
23
|
export {};
|
|
22
24
|
//# 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;AAczD;;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,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAE7B;AAED,wBAAgB,0BAA0B,CACxC,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,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAEnF"}
|
package/lib/index.js
CHANGED
|
@@ -28,15 +28,25 @@ Object.defineProperty(exports, "validateDocument", {
|
|
|
28
28
|
return _validateDocument.default;
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
|
+
Object.defineProperty(exports, "validateDocumentObservable", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
get: function get() {
|
|
34
|
+
return _validateDocument.validateDocumentObservable;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
31
37
|
|
|
32
38
|
var _Rule = _interopRequireDefault(require("./Rule"));
|
|
33
39
|
|
|
34
|
-
var _validateDocument =
|
|
40
|
+
var _validateDocument = _interopRequireWildcard(require("./validateDocument"));
|
|
35
41
|
|
|
36
42
|
var _inferFromSchema = _interopRequireDefault(require("./inferFromSchema"));
|
|
37
43
|
|
|
38
44
|
var _inferFromSchemaType = _interopRequireDefault(require("./inferFromSchemaType"));
|
|
39
45
|
|
|
46
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
47
|
+
|
|
48
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
49
|
+
|
|
40
50
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
41
51
|
|
|
42
52
|
var _default = {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.requestIdleCallback = exports.cancelIdleCallback = void 0;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Simple requestIdleCallback polyfill
|
|
10
|
+
* Can be removed when all browsers support requestIdleCallback: https://caniuse.com/requestidlecallback
|
|
11
|
+
* @param callback
|
|
12
|
+
* @param options
|
|
13
|
+
*/
|
|
14
|
+
var requestIdleCallbackShim = function requestIdleCallbackShim(callback, options) {
|
|
15
|
+
var start = Date.now();
|
|
16
|
+
return window.setTimeout(() => {
|
|
17
|
+
callback({
|
|
18
|
+
didTimeout: false,
|
|
19
|
+
|
|
20
|
+
timeRemaining() {
|
|
21
|
+
return Math.max(0, Date.now() - start);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
});
|
|
25
|
+
}, 0);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
var cancelIdleCallbackShim = function cancelIdleCallbackShim(handle) {
|
|
29
|
+
return window.clearTimeout(handle);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
var requestIdleCallback = window.requestIdleCallback || requestIdleCallbackShim;
|
|
33
|
+
exports.requestIdleCallback = requestIdleCallback;
|
|
34
|
+
var cancelIdleCallback = window.cancelIdleCallback || cancelIdleCallbackShim;
|
|
35
|
+
exports.cancelIdleCallback = cancelIdleCallback;
|
package/lib/validateDocument.js
CHANGED
|
@@ -5,14 +5,23 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = validateDocument;
|
|
7
7
|
exports.resolveTypeForArrayItem = resolveTypeForArrayItem;
|
|
8
|
+
exports.validateDocumentObservable = validateDocumentObservable;
|
|
8
9
|
exports.validateItem = validateItem;
|
|
9
10
|
|
|
10
11
|
var _uniqBy2 = _interopRequireDefault(require("lodash/uniqBy"));
|
|
11
12
|
|
|
13
|
+
var _flatten2 = _interopRequireDefault(require("lodash/flatten"));
|
|
14
|
+
|
|
12
15
|
var _types = require("@sanity/types");
|
|
13
16
|
|
|
17
|
+
var _rxjs = require("rxjs");
|
|
18
|
+
|
|
19
|
+
var _operators = require("rxjs/operators");
|
|
20
|
+
|
|
14
21
|
var _typeString = _interopRequireDefault(require("./util/typeString"));
|
|
15
22
|
|
|
23
|
+
var _requestIdleCallback = require("./util/requestIdleCallback");
|
|
24
|
+
|
|
16
25
|
var _ValidationError = _interopRequireDefault(require("./ValidationError"));
|
|
17
26
|
|
|
18
27
|
var _normalizeValidationRules = _interopRequireDefault(require("./util/normalizeValidationRules"));
|
|
@@ -43,10 +52,6 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
|
|
|
43
52
|
|
|
44
53
|
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
54
|
|
|
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
55
|
var isRecord = maybeRecord => typeof maybeRecord === 'object' && maybeRecord !== null && !Array.isArray(maybeRecord);
|
|
51
56
|
|
|
52
57
|
var isNonNullable = value => value !== null && value !== undefined;
|
|
@@ -74,8 +79,36 @@ function resolveTypeForArrayItem(item, candidates) {
|
|
|
74
79
|
}) || candidates.find(candidate => candidate.name === itemType) || candidates.find(candidate => candidate.name === 'object' && primitive === 'object');
|
|
75
80
|
}
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
82
|
+
var EMPTY_MARKERS = [];
|
|
83
|
+
|
|
84
|
+
function validateDocument(doc, schema, context) {
|
|
85
|
+
return validateDocumentObservable(doc, schema, context).toPromise();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function validateDocumentObservable(doc, schema, context) {
|
|
89
|
+
var documentType = schema.get(doc._type);
|
|
90
|
+
|
|
91
|
+
if (!documentType) {
|
|
92
|
+
console.warn('Schema type for object type "%s" not found, skipping validation', doc._type);
|
|
93
|
+
return (0, _rxjs.of)(EMPTY_MARKERS);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return validateItemObservable({
|
|
97
|
+
parent: undefined,
|
|
98
|
+
value: doc,
|
|
99
|
+
path: [],
|
|
100
|
+
document: doc,
|
|
101
|
+
type: documentType,
|
|
102
|
+
getDocumentExists: context === null || context === void 0 ? void 0 : context.getDocumentExists
|
|
103
|
+
}).pipe((0, _operators.catchError)(err => {
|
|
104
|
+
console.error(err);
|
|
105
|
+
return (0, _rxjs.of)([{
|
|
106
|
+
type: 'validation',
|
|
107
|
+
level: 'error',
|
|
108
|
+
path: [],
|
|
109
|
+
item: new _ValidationError.default(err === null || err === void 0 ? void 0 : err.message)
|
|
110
|
+
}]);
|
|
111
|
+
}));
|
|
79
112
|
}
|
|
80
113
|
/**
|
|
81
114
|
* this is used make optional properties required by replacing optionals with
|
|
@@ -86,146 +119,124 @@ function validateDocument(_x, _x2, _x3) {
|
|
|
86
119
|
*/
|
|
87
120
|
|
|
88
121
|
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
var documentType = schema.get(doc._type);
|
|
92
|
-
|
|
93
|
-
if (!documentType) {
|
|
94
|
-
console.warn('Schema type for object type "%s" not found, skipping validation', doc._type);
|
|
95
|
-
return [];
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
return yield validateItem({
|
|
100
|
-
parent: undefined,
|
|
101
|
-
value: doc,
|
|
102
|
-
path: [],
|
|
103
|
-
document: doc,
|
|
104
|
-
type: documentType,
|
|
105
|
-
getDocumentExists: context === null || context === void 0 ? void 0 : context.getDocumentExists
|
|
106
|
-
});
|
|
107
|
-
} catch (err) {
|
|
108
|
-
console.error(err);
|
|
109
|
-
return [{
|
|
110
|
-
type: 'validation',
|
|
111
|
-
level: 'error',
|
|
112
|
-
path: [],
|
|
113
|
-
item: new _ValidationError.default(err === null || err === void 0 ? void 0 : err.message)
|
|
114
|
-
}];
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
return _validateDocument.apply(this, arguments);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function validateItem(_x4) {
|
|
121
|
-
return _validateItem.apply(this, arguments);
|
|
122
|
+
function validateItem(opts) {
|
|
123
|
+
return validateItemObservable(opts).toPromise();
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
function
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
126
|
+
function validateItemObservable(_ref) {
|
|
127
|
+
var value = _ref.value,
|
|
128
|
+
type = _ref.type,
|
|
129
|
+
_ref$path = _ref.path,
|
|
130
|
+
path = _ref$path === void 0 ? [] : _ref$path,
|
|
131
|
+
parent = _ref.parent,
|
|
132
|
+
restOfContext = _objectWithoutProperties(_ref, _excluded);
|
|
133
|
+
|
|
134
|
+
var rules = (0, _normalizeValidationRules.default)(type); // run validation for the current value
|
|
135
|
+
|
|
136
|
+
var selfChecks = rules.map(rule => (0, _rxjs.defer)(() => rule.validate(value, _objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
137
|
+
parent,
|
|
138
|
+
path,
|
|
139
|
+
type
|
|
140
|
+
})))); // run validation for nested values (conditionally)
|
|
141
|
+
|
|
142
|
+
var fieldChecks = [];
|
|
143
|
+
var selfIsRequired = rules.some(rule => rule.isRequired());
|
|
144
|
+
var shouldRunNestedObjectValidation = // run nested validation for objects
|
|
145
|
+
(type === null || type === void 0 ? void 0 : type.jsonType) === 'object' && ( // if the value is truthy
|
|
146
|
+
!!value || // or
|
|
147
|
+
// (the value is null or undefined) and the top-level value is required
|
|
148
|
+
(value === null || value === undefined) && selfIsRequired);
|
|
149
|
+
|
|
150
|
+
if (shouldRunNestedObjectValidation) {
|
|
151
|
+
var fieldTypes = type.fields.reduce((acc, field) => {
|
|
152
|
+
acc[field.name] = field.type;
|
|
153
|
+
return acc;
|
|
154
|
+
}, {}); // Validation for rules set at the object level with `Rule.fields({/* ... */})`
|
|
155
|
+
|
|
156
|
+
fieldChecks = fieldChecks.concat(rules.map(rule => rule._fieldRules).filter(isNonNullable).flatMap(fieldResults => Object.entries(fieldResults)).flatMap(_ref2 => {
|
|
157
|
+
var _ref3 = _slicedToArray(_ref2, 2),
|
|
158
|
+
name = _ref3[0],
|
|
159
|
+
validation = _ref3[1];
|
|
160
|
+
|
|
161
|
+
var fieldType = fieldTypes[name];
|
|
162
|
+
return (0, _normalizeValidationRules.default)(_objectSpread(_objectSpread({}, fieldType), {}, {
|
|
163
|
+
validation
|
|
164
|
+
})).map(subRule => {
|
|
165
|
+
var nestedValue = isRecord(value) ? value[name] : undefined;
|
|
166
|
+
return (0, _rxjs.defer)(() => subRule.validate(nestedValue, _objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
167
|
+
parent: value,
|
|
168
|
+
path: path.concat(name),
|
|
169
|
+
type: fieldType
|
|
170
|
+
})));
|
|
171
|
+
});
|
|
172
|
+
})); // Validation from each field's schema `validation: Rule => {/* ... */}` function
|
|
173
|
+
|
|
174
|
+
fieldChecks = fieldChecks.concat(type.fields.map(field => validateItemObservable(_objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
175
|
+
parent: value,
|
|
176
|
+
value: isRecord(value) ? value[field.name] : undefined,
|
|
177
|
+
path: path.concat(field.name),
|
|
178
|
+
type: field.type
|
|
179
|
+
}))));
|
|
180
|
+
} // note: unlike objects, arrays should not run nested validation for undefined
|
|
181
|
+
// values because we won't have a valid path to put a marker (i.e. missing the
|
|
182
|
+
// key or index in the path) and the downstream form builder won't have a
|
|
183
|
+
// valid target component
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
var shouldRunNestedValidationForArrays = (type === null || type === void 0 ? void 0 : type.jsonType) === 'array' && Array.isArray(value);
|
|
187
|
+
|
|
188
|
+
if (shouldRunNestedValidationForArrays) {
|
|
189
|
+
fieldChecks = fieldChecks.concat(value.map(item => validateItemObservable(_objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
190
|
+
parent: value,
|
|
191
|
+
value: item,
|
|
192
|
+
path: path.concat((0, _types.isKeyedObject)(item) ? {
|
|
193
|
+
_key: item._key
|
|
194
|
+
} : value.indexOf(item)),
|
|
195
|
+
type: resolveTypeForArrayItem(item, type.of)
|
|
196
|
+
}))));
|
|
197
|
+
} // markDefs also do no run nested validation if the parent object is undefined
|
|
198
|
+
// for a similar reason to arrays
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
var shouldRunNestedValidationForMarkDefs = (0, _types.isBlock)(value) && value.markDefs.length && (0, _types.isBlockSchemaType)(type);
|
|
202
|
+
|
|
203
|
+
if (shouldRunNestedValidationForMarkDefs) {
|
|
204
|
+
var _type$fields = _slicedToArray(type.fields, 1),
|
|
205
|
+
spanChildrenField = _type$fields[0];
|
|
206
|
+
|
|
207
|
+
var spanType = spanChildrenField.type.of.find(_types.isSpanSchemaType);
|
|
208
|
+
var annotations = ((spanType === null || spanType === void 0 ? void 0 : spanType.annotations) || []).reduce((acc, annotationType) => {
|
|
209
|
+
acc.set(annotationType.name, annotationType);
|
|
210
|
+
return acc;
|
|
211
|
+
}, new Map());
|
|
212
|
+
fieldChecks = fieldChecks.concat(value.markDefs.map(markDef => validateItemObservable(_objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
213
|
+
parent: value,
|
|
214
|
+
value: markDef,
|
|
215
|
+
path: path.concat(['markDefs', {
|
|
216
|
+
_key: markDef._key
|
|
217
|
+
}]),
|
|
218
|
+
type: annotations.get(markDef._type)
|
|
219
|
+
}))));
|
|
220
|
+
}
|
|
220
221
|
|
|
221
|
-
|
|
222
|
+
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.map)(_flatten2.default), (0, _operators.map)(results => {
|
|
223
|
+
// run `uniqBy` if `_fieldRules` are present because they can
|
|
222
224
|
// cause repeat markers
|
|
223
|
-
|
|
224
225
|
if (rules.some(rule => rule._fieldRules)) {
|
|
225
226
|
return (0, _uniqBy2.default)(results, rule => JSON.stringify(rule));
|
|
226
227
|
}
|
|
227
228
|
|
|
228
229
|
return results;
|
|
230
|
+
}));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function idle(timeout) {
|
|
234
|
+
return new _rxjs.Observable(observer => {
|
|
235
|
+
var handle = (0, _requestIdleCallback.requestIdleCallback)(() => {
|
|
236
|
+
observer.complete();
|
|
237
|
+
}, timeout ? {
|
|
238
|
+
timeout
|
|
239
|
+
} : undefined);
|
|
240
|
+
return () => (0, _requestIdleCallback.cancelIdleCallback)(handle);
|
|
229
241
|
});
|
|
230
|
-
return _validateItem.apply(this, arguments);
|
|
231
242
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/validation",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.34.0",
|
|
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.
|
|
36
|
+
"@sanity/schema": "2.33.2"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@sanity/types": "2.
|
|
39
|
+
"@sanity/types": "2.34.0",
|
|
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": "271991702d6db370f2157232d738445e968ee546"
|
|
44
45
|
}
|
package/src/index.ts
CHANGED
|
@@ -5,3 +5,5 @@ import inferFromSchemaType from './inferFromSchemaType'
|
|
|
5
5
|
|
|
6
6
|
export default {Rule: RuleClass, validateDocument, inferFromSchema, inferFromSchemaType}
|
|
7
7
|
export {RuleClass as Rule, validateDocument, inferFromSchema, inferFromSchemaType}
|
|
8
|
+
|
|
9
|
+
export {validateDocumentObservable} from './validateDocument'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple requestIdleCallback polyfill
|
|
3
|
+
* Can be removed when all browsers support requestIdleCallback: https://caniuse.com/requestidlecallback
|
|
4
|
+
* @param callback
|
|
5
|
+
* @param options
|
|
6
|
+
*/
|
|
7
|
+
const requestIdleCallbackShim: typeof window.requestIdleCallback = function requestIdleCallbackShim(
|
|
8
|
+
callback,
|
|
9
|
+
options?
|
|
10
|
+
): number {
|
|
11
|
+
const start = Date.now()
|
|
12
|
+
return window.setTimeout(() => {
|
|
13
|
+
callback({
|
|
14
|
+
didTimeout: false,
|
|
15
|
+
timeRemaining() {
|
|
16
|
+
return Math.max(0, Date.now() - start)
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
}, 0)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const cancelIdleCallbackShim: typeof window.cancelIdleCallback = function cancelIdleCallbackShim(
|
|
23
|
+
handle: number
|
|
24
|
+
): void {
|
|
25
|
+
return window.clearTimeout(handle)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const requestIdleCallback = window.requestIdleCallback || requestIdleCallbackShim
|
|
29
|
+
export const cancelIdleCallback = window.cancelIdleCallback || cancelIdleCallbackShim
|
|
@@ -687,81 +687,4 @@ describe('validateItem', () => {
|
|
|
687
687
|
},
|
|
688
688
|
])
|
|
689
689
|
})
|
|
690
|
-
|
|
691
|
-
it('runs all nested validation checks concurrently', async () => {
|
|
692
|
-
type Resolver = (value: true) => void
|
|
693
|
-
const resolvers = new Set<Resolver>()
|
|
694
|
-
const customValidationFn = () => new Promise<true>((resolve) => resolvers.add(resolve))
|
|
695
|
-
|
|
696
|
-
const schema: Schema = createSchema({
|
|
697
|
-
types: [
|
|
698
|
-
{
|
|
699
|
-
name: 'root',
|
|
700
|
-
type: 'object',
|
|
701
|
-
fields: [
|
|
702
|
-
{
|
|
703
|
-
name: 'level1Object',
|
|
704
|
-
type: 'object',
|
|
705
|
-
validation: (rule: Rule) => [
|
|
706
|
-
rule.custom(customValidationFn),
|
|
707
|
-
rule.fields({
|
|
708
|
-
level2String: (r) => r.custom(customValidationFn),
|
|
709
|
-
}),
|
|
710
|
-
],
|
|
711
|
-
fields: [
|
|
712
|
-
{
|
|
713
|
-
name: 'level2String',
|
|
714
|
-
type: 'string',
|
|
715
|
-
validation: (rule: Rule) => rule.custom(customValidationFn),
|
|
716
|
-
},
|
|
717
|
-
],
|
|
718
|
-
},
|
|
719
|
-
{
|
|
720
|
-
name: 'level1Array',
|
|
721
|
-
type: 'array',
|
|
722
|
-
validation: (rule: Rule) => rule.custom(customValidationFn),
|
|
723
|
-
of: [
|
|
724
|
-
{
|
|
725
|
-
type: 'object',
|
|
726
|
-
fields: [
|
|
727
|
-
{
|
|
728
|
-
name: 'level2Number',
|
|
729
|
-
type: 'number',
|
|
730
|
-
validation: (rule: Rule) => rule.custom(customValidationFn),
|
|
731
|
-
},
|
|
732
|
-
],
|
|
733
|
-
},
|
|
734
|
-
],
|
|
735
|
-
},
|
|
736
|
-
],
|
|
737
|
-
validation: (rule: Rule) => rule.custom(customValidationFn),
|
|
738
|
-
},
|
|
739
|
-
],
|
|
740
|
-
})
|
|
741
|
-
|
|
742
|
-
const value = {
|
|
743
|
-
level1Object: {level3String: 'a string'},
|
|
744
|
-
level1Array: [{level2Number: 5}],
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
const resultPromise = validateItem({
|
|
748
|
-
value,
|
|
749
|
-
document: undefined,
|
|
750
|
-
parent: undefined,
|
|
751
|
-
path: undefined,
|
|
752
|
-
type: schema.get('root'),
|
|
753
|
-
getDocumentExists: undefined,
|
|
754
|
-
})
|
|
755
|
-
|
|
756
|
-
// after `validateItem(...)` has been initiated, all of the custom
|
|
757
|
-
// validation calls should have fired so the resolver size should equal the
|
|
758
|
-
// amount of total fields
|
|
759
|
-
expect(resolvers.size).toBe(6)
|
|
760
|
-
|
|
761
|
-
for (const resolver of resolvers) {
|
|
762
|
-
resolver(true)
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
await resultPromise
|
|
766
|
-
})
|
|
767
690
|
})
|
package/src/validateDocument.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
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 {catchError, map, mergeAll, mergeMap, toArray} from 'rxjs/operators'
|
|
15
|
+
import {flatten, uniqBy} from 'lodash'
|
|
14
16
|
import typeString from './util/typeString'
|
|
17
|
+
import {requestIdleCallback, cancelIdleCallback} from './util/requestIdleCallback'
|
|
15
18
|
import ValidationErrorClass from './ValidationError'
|
|
16
19
|
import normalizeValidationRules from './util/normalizeValidationRules'
|
|
17
20
|
|
|
@@ -45,38 +48,47 @@ export function resolveTypeForArrayItem(
|
|
|
45
48
|
candidates.find((candidate) => candidate.name === 'object' && primitive === 'object')
|
|
46
49
|
)
|
|
47
50
|
}
|
|
51
|
+
const EMPTY_MARKERS: ValidationMarker[] = []
|
|
48
52
|
|
|
49
|
-
export default
|
|
53
|
+
export default function validateDocument(
|
|
50
54
|
doc: SanityDocument,
|
|
51
55
|
schema: Schema,
|
|
52
56
|
context?: Pick<ValidationContext, 'getDocumentExists'>
|
|
53
57
|
): Promise<ValidationMarker[]> {
|
|
58
|
+
return validateDocumentObservable(doc, schema, context).toPromise()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function validateDocumentObservable(
|
|
62
|
+
doc: SanityDocument,
|
|
63
|
+
schema: Schema,
|
|
64
|
+
context?: Pick<ValidationContext, 'getDocumentExists'>
|
|
65
|
+
): Observable<ValidationMarker[]> {
|
|
54
66
|
const documentType = schema.get(doc._type)
|
|
55
67
|
if (!documentType) {
|
|
56
68
|
console.warn('Schema type for object type "%s" not found, skipping validation', doc._type)
|
|
57
|
-
return
|
|
69
|
+
return of(EMPTY_MARKERS)
|
|
58
70
|
}
|
|
59
71
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
return validateItemObservable({
|
|
73
|
+
parent: undefined,
|
|
74
|
+
value: doc,
|
|
75
|
+
path: [],
|
|
76
|
+
document: doc,
|
|
77
|
+
type: documentType,
|
|
78
|
+
getDocumentExists: context?.getDocumentExists,
|
|
79
|
+
}).pipe(
|
|
80
|
+
catchError((err) => {
|
|
81
|
+
console.error(err)
|
|
82
|
+
return of([
|
|
83
|
+
{
|
|
84
|
+
type: 'validation' as const,
|
|
85
|
+
level: 'error' as const,
|
|
86
|
+
path: [],
|
|
87
|
+
item: new ValidationErrorClass(err?.message),
|
|
88
|
+
},
|
|
89
|
+
])
|
|
68
90
|
})
|
|
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
|
-
}
|
|
91
|
+
)
|
|
80
92
|
}
|
|
81
93
|
|
|
82
94
|
/**
|
|
@@ -94,27 +106,32 @@ type ValidateItemOptions = {
|
|
|
94
106
|
value: unknown
|
|
95
107
|
} & ExplicitUndefined<ValidationContext>
|
|
96
108
|
|
|
97
|
-
export
|
|
109
|
+
export function validateItem(opts: ValidateItemOptions): Promise<ValidationMarker[]> {
|
|
110
|
+
return validateItemObservable(opts).toPromise()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function validateItemObservable({
|
|
98
114
|
value,
|
|
99
115
|
type,
|
|
100
116
|
path = [],
|
|
101
117
|
parent,
|
|
102
118
|
...restOfContext
|
|
103
|
-
}: ValidateItemOptions):
|
|
119
|
+
}: ValidateItemOptions): Observable<ValidationMarker[]> {
|
|
104
120
|
const rules = normalizeValidationRules(type)
|
|
105
|
-
|
|
106
121
|
// run validation for the current value
|
|
107
122
|
const selfChecks = rules.map((rule) =>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
defer(() =>
|
|
124
|
+
rule.validate(value, {
|
|
125
|
+
...restOfContext,
|
|
126
|
+
parent,
|
|
127
|
+
path,
|
|
128
|
+
type,
|
|
129
|
+
})
|
|
130
|
+
)
|
|
114
131
|
)
|
|
115
132
|
|
|
116
133
|
// run validation for nested values (conditionally)
|
|
117
|
-
let
|
|
134
|
+
let fieldChecks: Array<Observable<ValidationMarker[]>> = []
|
|
118
135
|
|
|
119
136
|
const selfIsRequired = rules.some((rule) => rule.isRequired())
|
|
120
137
|
const shouldRunNestedObjectValidation =
|
|
@@ -132,7 +149,7 @@ export async function validateItem({
|
|
|
132
149
|
}, {})
|
|
133
150
|
|
|
134
151
|
// Validation for rules set at the object level with `Rule.fields({/* ... */})`
|
|
135
|
-
|
|
152
|
+
fieldChecks = fieldChecks.concat(
|
|
136
153
|
rules
|
|
137
154
|
.map((rule) => rule._fieldRules)
|
|
138
155
|
.filter(isNonNullable)
|
|
@@ -141,20 +158,22 @@ export async function validateItem({
|
|
|
141
158
|
const fieldType = fieldTypes[name]
|
|
142
159
|
return normalizeValidationRules({...fieldType, validation}).map((subRule) => {
|
|
143
160
|
const nestedValue = isRecord(value) ? value[name] : undefined
|
|
144
|
-
return
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
161
|
+
return defer(() =>
|
|
162
|
+
subRule.validate(nestedValue, {
|
|
163
|
+
...restOfContext,
|
|
164
|
+
parent: value,
|
|
165
|
+
path: path.concat(name),
|
|
166
|
+
type: fieldType,
|
|
167
|
+
})
|
|
168
|
+
)
|
|
150
169
|
})
|
|
151
170
|
})
|
|
152
171
|
)
|
|
153
172
|
|
|
154
173
|
// Validation from each field's schema `validation: Rule => {/* ... */}` function
|
|
155
|
-
|
|
174
|
+
fieldChecks = fieldChecks.concat(
|
|
156
175
|
type.fields.map((field) =>
|
|
157
|
-
|
|
176
|
+
validateItemObservable({
|
|
158
177
|
...restOfContext,
|
|
159
178
|
parent: value,
|
|
160
179
|
value: isRecord(value) ? value[field.name] : undefined,
|
|
@@ -172,9 +191,9 @@ export async function validateItem({
|
|
|
172
191
|
const shouldRunNestedValidationForArrays = type?.jsonType === 'array' && Array.isArray(value)
|
|
173
192
|
|
|
174
193
|
if (shouldRunNestedValidationForArrays) {
|
|
175
|
-
|
|
194
|
+
fieldChecks = fieldChecks.concat(
|
|
176
195
|
value.map((item) =>
|
|
177
|
-
|
|
196
|
+
validateItemObservable({
|
|
178
197
|
...restOfContext,
|
|
179
198
|
parent: value,
|
|
180
199
|
value: item,
|
|
@@ -195,16 +214,16 @@ export async function validateItem({
|
|
|
195
214
|
const spanType = spanChildrenField.type.of.find(isSpanSchemaType)
|
|
196
215
|
|
|
197
216
|
const annotations = (spanType?.annotations || []).reduce<Map<string, SchemaType>>(
|
|
198
|
-
(
|
|
199
|
-
|
|
200
|
-
return
|
|
217
|
+
(acc, annotationType) => {
|
|
218
|
+
acc.set(annotationType.name, annotationType)
|
|
219
|
+
return acc
|
|
201
220
|
},
|
|
202
221
|
new Map()
|
|
203
222
|
)
|
|
204
223
|
|
|
205
|
-
|
|
224
|
+
fieldChecks = fieldChecks.concat(
|
|
206
225
|
value.markDefs.map((markDef) =>
|
|
207
|
-
|
|
226
|
+
validateItemObservable({
|
|
208
227
|
...restOfContext,
|
|
209
228
|
parent: value,
|
|
210
229
|
value: markDef,
|
|
@@ -215,13 +234,31 @@ export async function validateItem({
|
|
|
215
234
|
)
|
|
216
235
|
}
|
|
217
236
|
|
|
218
|
-
|
|
237
|
+
return defer(() => merge([...selfChecks, ...fieldChecks])).pipe(
|
|
238
|
+
mergeMap((validateNode) => concat(idle(), validateNode), 40),
|
|
239
|
+
mergeAll(),
|
|
240
|
+
toArray(),
|
|
241
|
+
map(flatten),
|
|
242
|
+
map((results) => {
|
|
243
|
+
// run `uniqBy` if `_fieldRules` are present because they can
|
|
244
|
+
// cause repeat markers
|
|
245
|
+
if (rules.some((rule) => rule._fieldRules)) {
|
|
246
|
+
return uniqBy(results, (rule) => JSON.stringify(rule))
|
|
247
|
+
}
|
|
248
|
+
return results
|
|
249
|
+
})
|
|
250
|
+
)
|
|
251
|
+
}
|
|
219
252
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
253
|
+
function idle(timeout?: number): Observable<never> {
|
|
254
|
+
return new Observable<never>((observer) => {
|
|
255
|
+
const handle = requestIdleCallback(
|
|
256
|
+
() => {
|
|
257
|
+
observer.complete()
|
|
258
|
+
},
|
|
259
|
+
timeout ? {timeout} : undefined
|
|
260
|
+
)
|
|
225
261
|
|
|
226
|
-
|
|
262
|
+
return () => cancelIdleCallback(handle)
|
|
263
|
+
})
|
|
227
264
|
}
|