UNPKG

12 kBJavaScriptView Raw
1import _extends from "@babel/runtime/helpers/esm/extends";
2
3function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } it = o[Symbol.iterator](); return it.next.bind(it); }
4
5function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
6
7function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
8
9import has from "lodash-es/has";
10import _snakeCase from "lodash-es/snakeCase";
11import _camelCase from "lodash-es/camelCase";
12import mapKeys from "lodash-es/mapKeys";
13import mapValues from "lodash-es/mapValues";
14import { getter } from 'property-expr';
15import MixedSchema from './mixed';
16import { object as locale } from './locale.js';
17import sortFields from './util/sortFields';
18import sortByKeyOrder from './util/sortByKeyOrder';
19import inherits from './util/inherits';
20import runTests from './util/runTests';
21
22var isObject = function isObject(obj) {
23 return Object.prototype.toString.call(obj) === '[object Object]';
24};
25
26function unknown(ctx, value) {
27 var known = Object.keys(ctx.fields);
28 return Object.keys(value).filter(function (key) {
29 return known.indexOf(key) === -1;
30 });
31}
32
33export default function ObjectSchema(spec) {
34 var _this2 = this;
35
36 if (!(this instanceof ObjectSchema)) return new ObjectSchema(spec);
37 MixedSchema.call(this, {
38 type: 'object',
39 default: function _default() {
40 var _this = this;
41
42 if (!this._nodes.length) return undefined;
43 var dft = {};
44
45 this._nodes.forEach(function (key) {
46 dft[key] = _this.fields[key].default ? _this.fields[key].getDefault() : undefined;
47 });
48
49 return dft;
50 }
51 });
52 this.fields = Object.create(null);
53 this._sortErrors = sortByKeyOrder([]);
54 this._nodes = [];
55 this._excludedEdges = [];
56 this.withMutation(function () {
57 _this2.transform(function coerce(value) {
58 if (typeof value === 'string') {
59 try {
60 value = JSON.parse(value);
61 } catch (err) {
62 value = null;
63 }
64 }
65
66 if (this.isType(value)) return value;
67 return null;
68 });
69
70 if (spec) {
71 _this2.shape(spec);
72 }
73 });
74}
75inherits(ObjectSchema, MixedSchema, {
76 _typeCheck: function _typeCheck(value) {
77 return isObject(value) || typeof value === 'function';
78 },
79 _cast: function _cast(_value, options) {
80 var _this3 = this;
81
82 if (options === void 0) {
83 options = {};
84 }
85
86 var value = MixedSchema.prototype._cast.call(this, _value); //should ignore nulls here
87
88
89 if (value === undefined) return this.getDefault();
90 if (!this._typeCheck(value)) return value;
91 var fields = this.fields;
92 var strip = this._option('stripUnknown', options) === true;
93
94 var props = this._nodes.concat(Object.keys(value).filter(function (v) {
95 return _this3._nodes.indexOf(v) === -1;
96 }));
97
98 var intermediateValue = {}; // is filled during the transform below
99
100 var innerOptions = _extends({}, options, {
101 parent: intermediateValue,
102 __validating: options.__validating || false
103 });
104
105 var isChanged = false;
106
107 for (var _iterator = _createForOfIteratorHelperLoose(props), _step; !(_step = _iterator()).done;) {
108 var prop = _step.value;
109 var field = fields[prop];
110 var exists = has(value, prop);
111
112 if (field) {
113 var fieldValue = void 0;
114 var strict = field._options && field._options.strict; // safe to mutate since this is fired in sequence
115
116 innerOptions.path = (options.path ? options.path + "." : '') + prop;
117 innerOptions.value = value[prop];
118 field = field.resolve(innerOptions);
119
120 if (field._strip === true) {
121 isChanged = isChanged || prop in value;
122 continue;
123 }
124
125 fieldValue = !options.__validating || !strict ? field.cast(value[prop], innerOptions) : value[prop];
126
127 if (fieldValue !== undefined) {
128 intermediateValue[prop] = fieldValue;
129 }
130 } else if (exists && !strip) {
131 intermediateValue[prop] = value[prop];
132 }
133
134 if (intermediateValue[prop] !== value[prop]) {
135 isChanged = true;
136 }
137 }
138
139 return isChanged ? intermediateValue : value;
140 },
141
142 /**
143 * @typedef {Object} Ancestor
144 * @property {Object} schema - a string property of SpecialType
145 * @property {*} value - a number property of SpecialType
146 */
147
148 /**
149 *
150 * @param {*} _value
151 * @param {Object} opts
152 * @param {string=} opts.path
153 * @param {*=} opts.parent
154 * @param {Object=} opts.context
155 * @param {boolean=} opts.sync
156 * @param {boolean=} opts.stripUnknown
157 * @param {boolean=} opts.strict
158 * @param {boolean=} opts.recursive
159 * @param {boolean=} opts.abortEarly
160 * @param {boolean=} opts.__validating
161 * @param {Object=} opts.originalValue
162 * @param {Ancestor[]=} opts.from
163 * @param {Object} [opts.from]
164 * @param {Function} callback
165 */
166 _validate: function _validate(_value, opts, callback) {
167 var _this4 = this;
168
169 if (opts === void 0) {
170 opts = {};
171 }
172
173 var errors = [];
174 var _opts = opts,
175 sync = _opts.sync,
176 _opts$from = _opts.from,
177 from = _opts$from === void 0 ? [] : _opts$from,
178 _opts$originalValue = _opts.originalValue,
179 originalValue = _opts$originalValue === void 0 ? _value : _opts$originalValue,
180 _opts$abortEarly = _opts.abortEarly,
181 abortEarly = _opts$abortEarly === void 0 ? this._options.abortEarly : _opts$abortEarly,
182 _opts$recursive = _opts.recursive,
183 recursive = _opts$recursive === void 0 ? this._options.recursive : _opts$recursive;
184 from = [{
185 schema: this,
186 value: originalValue
187 }].concat(from); // this flag is needed for handling `strict` correctly in the context of
188 // validation vs just casting. e.g strict() on a field is only used when validating
189
190 opts.__validating = true;
191 opts.originalValue = originalValue;
192 opts.from = from;
193
194 MixedSchema.prototype._validate.call(this, _value, opts, function (err, value) {
195 if (err) {
196 if (abortEarly) return void callback(err);
197 errors.push(err);
198 value = err.value;
199 }
200
201 if (!recursive || !isObject(value)) {
202 callback(errors[0] || null, value);
203 return;
204 }
205
206 originalValue = originalValue || value;
207
208 var tests = _this4._nodes.map(function (key) {
209 return function (_, cb) {
210 var path = key.indexOf('.') === -1 ? (opts.path ? opts.path + "." : '') + key : (opts.path || '') + "[\"" + key + "\"]";
211 var field = _this4.fields[key];
212
213 if (field && field.validate) {
214 field.validate(value[key], _extends({}, opts, {
215 path: path,
216 from: from,
217 // inner fields are always strict:
218 // 1. this isn't strict so the casting will also have cast inner values
219 // 2. this is strict in which case the nested values weren't cast either
220 strict: true,
221 parent: value,
222 originalValue: originalValue[key]
223 }), cb);
224 return;
225 }
226
227 cb(null);
228 };
229 });
230
231 runTests({
232 sync: sync,
233 tests: tests,
234 value: value,
235 errors: errors,
236 endEarly: abortEarly,
237 sort: _this4._sortErrors,
238 path: opts.path
239 }, callback);
240 });
241 },
242 concat: function concat(schema) {
243 var next = MixedSchema.prototype.concat.call(this, schema);
244 next._nodes = sortFields(next.fields, next._excludedEdges);
245 return next;
246 },
247 shape: function shape(schema, excludes) {
248 if (excludes === void 0) {
249 excludes = [];
250 }
251
252 var next = this.clone();
253
254 var fields = _extends(next.fields, schema);
255
256 next.fields = fields;
257 next._sortErrors = sortByKeyOrder(Object.keys(fields));
258
259 if (excludes.length) {
260 if (!Array.isArray(excludes[0])) excludes = [excludes];
261 var keys = excludes.map(function (_ref) {
262 var first = _ref[0],
263 second = _ref[1];
264 return first + "-" + second;
265 });
266 next._excludedEdges = next._excludedEdges.concat(keys);
267 }
268
269 next._nodes = sortFields(fields, next._excludedEdges);
270 return next;
271 },
272 pick: function pick(keys) {
273 var picked = {};
274
275 for (var _iterator2 = _createForOfIteratorHelperLoose(keys), _step2; !(_step2 = _iterator2()).done;) {
276 var key = _step2.value;
277 if (this.fields[key]) picked[key] = this.fields[key];
278 }
279
280 return this.clone().withMutation(function (next) {
281 next.fields = {};
282 return next.shape(picked);
283 });
284 },
285 omit: function omit(keys) {
286 var next = this.clone();
287 var fields = next.fields;
288 next.fields = {};
289
290 for (var _iterator3 = _createForOfIteratorHelperLoose(keys), _step3; !(_step3 = _iterator3()).done;) {
291 var key = _step3.value;
292 delete fields[key];
293 }
294
295 return next.withMutation(function (next) {
296 return next.shape(fields);
297 });
298 },
299 from: function from(_from, to, alias) {
300 var fromGetter = getter(_from, true);
301 return this.transform(function (obj) {
302 if (obj == null) return obj;
303 var newObj = obj;
304
305 if (has(obj, _from)) {
306 newObj = _extends({}, obj);
307 if (!alias) delete newObj[_from];
308 newObj[to] = fromGetter(obj);
309 }
310
311 return newObj;
312 });
313 },
314 noUnknown: function noUnknown(noAllow, message) {
315 if (noAllow === void 0) {
316 noAllow = true;
317 }
318
319 if (message === void 0) {
320 message = locale.noUnknown;
321 }
322
323 if (typeof noAllow === 'string') {
324 message = noAllow;
325 noAllow = true;
326 }
327
328 var next = this.test({
329 name: 'noUnknown',
330 exclusive: true,
331 message: message,
332 test: function test(value) {
333 if (value == null) return true;
334 var unknownKeys = unknown(this.schema, value);
335 return !noAllow || unknownKeys.length === 0 || this.createError({
336 params: {
337 unknown: unknownKeys.join(', ')
338 }
339 });
340 }
341 });
342 next._options.stripUnknown = noAllow;
343 return next;
344 },
345 unknown: function unknown(allow, message) {
346 if (allow === void 0) {
347 allow = true;
348 }
349
350 if (message === void 0) {
351 message = locale.noUnknown;
352 }
353
354 return this.noUnknown(!allow, message);
355 },
356 transformKeys: function transformKeys(fn) {
357 return this.transform(function (obj) {
358 return obj && mapKeys(obj, function (_, key) {
359 return fn(key);
360 });
361 });
362 },
363 camelCase: function camelCase() {
364 return this.transformKeys(_camelCase);
365 },
366 snakeCase: function snakeCase() {
367 return this.transformKeys(_snakeCase);
368 },
369 constantCase: function constantCase() {
370 return this.transformKeys(function (key) {
371 return _snakeCase(key).toUpperCase();
372 });
373 },
374 describe: function describe() {
375 var base = MixedSchema.prototype.describe.call(this);
376 base.fields = mapValues(this.fields, function (value) {
377 return value.describe();
378 });
379 return base;
380 }
381});
\No newline at end of file