UNPKG

16.7 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _util = require('util');
8
9var _util2 = _interopRequireDefault(_util);
10
11var _debug = require('debug');
12
13var _debug2 = _interopRequireDefault(_debug);
14
15var _Data = require('../Data');
16
17var _Data2 = _interopRequireDefault(_Data);
18
19var _Error = require('../Error');
20
21var _Error2 = _interopRequireDefault(_Error);
22
23var _Reference = require('../Reference');
24
25var _Reference2 = _interopRequireDefault(_Reference);
26
27function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28
29class Schema {
30
31 constructor(base) {
32 if (base) this.base = base;
33 this.debug = (0, _debug2.default)(`validator:${this.constructor.name.replace(/Schema/, '').toLowerCase()}`);
34 this._title = this.constructor.name.replace(/(.)Schema/, '$1');
35 this._detail = 'should be defined with';
36 this._rules = {
37 descriptor: [],
38 check: [],
39 validator: []
40 };
41 this._setting = {};
42
43 this._rules.descriptor.push(this._emptyDescriptor, this._optionalDescriptor, this._rawDescriptor);
44 this._rules.validator.push(this._emptyValidator, this._optionalValidator, this._rawValidator);
45 }
46
47 inspect(depth, options) {
48 const newOptions = Object.assign({}, options, {
49 depth: options.depth === null ? null : options.depth - 1
50 });
51 const base = this.base ? `base=${_util2.default.inspect(this.base)} ` : '';
52 const padding = ' '.repeat(5);
53 const inner = _util2.default.inspect(this._setting, newOptions).replace(/\n/g, `\n${padding}`);
54 return `${options.stylize(this.constructor.name, 'class')} ${base}${inner} `;
55 }
56
57 title(title) {
58 this._title = title;
59 return this;
60 }
61 detail(detail) {
62 this._detail = detail;
63 return this;
64 }
65 schema(path) {
66 let obj = this;
67 var _iteratorNormalCompletion = true;
68 var _didIteratorError = false;
69 var _iteratorError = undefined;
70
71 try {
72 for (var _iterator = path.split(/\//)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
73 const key = _step.value;
74
75 if (obj instanceof Schema) obj = obj._setting[key];else if (obj instanceof Map) obj = obj.get(key);else obj = obj[key];
76 }
77 } catch (err) {
78 _didIteratorError = true;
79 _iteratorError = err;
80 } finally {
81 try {
82 if (!_iteratorNormalCompletion && _iterator.return) {
83 _iterator.return();
84 }
85 } finally {
86 if (_didIteratorError) {
87 throw _iteratorError;
88 }
89 }
90 }
91
92 if (!(obj instanceof Schema)) throw new Error(`No schema element under ${path} found`);
93 return obj;
94 }
95
96 _setError(name, msg) {
97 throw new Error(msg || `In ${this.constructor.name} you are not allowed to set
98 ${name} manually`);
99 }
100 _setFlag(name, flag = true) {
101 if (flag) this._setting[name] = flag;else delete this._setting[name];
102 return this;
103 }
104 _setAny(name, value) {
105 if (value) this._setting[name] = value;else delete this._setting[name];
106 return this;
107 }
108 _isReference(name) {
109 return this._setting[name] instanceof _Reference2.default;
110 }
111 _checkBoolean(name) {
112 let value;
113 switch (typeof this._check[name]) {
114 case 'undefined':
115 case 'boolean':
116 break;
117 case 'string':
118 value = this._check[name].toLowerCase();
119 if (value === undefined) this._check[name] = false;else if (Array.isArray(value)) this._check[name] = value.length > 0;else if (typeof value === 'object') this._check[name] = Object.keys(value).length > 0;else if (['yes', 1, '1', 'true', 't', '+'].includes(value)) this._check[name] = true;else if (['no', 0, '0', 'false', 'f', '', '-'].includes(value)) this._check[name] = false;
120 break;
121 default:
122 throw new Error(`No boolean value for \`${name}\` setting given in \
123${this._setting[name] && this._setting[name].description || this._setting[name]}`);
124 }
125 }
126 _checkString(name) {
127 const check = this._check;
128 if (check[name] && typeof check[name] !== 'string') {
129 throw new Error(`No string value for \`${name}\` setting given in \
130${this._setting[name] && this._setting[name].description || this._setting[name]}`);
131 }
132 }
133 _checkArrayString(name) {
134 this._checkArray(name);
135 const check = this._check;
136 if (check[name]) {
137 check[name].forEach(e => {
138 if (typeof e !== 'string') {
139 throw new Error(`No string value for \`${name}\` setting given in \
140 ${this._setting[name] && this._setting[name].description || this._setting[name]}`);
141 }
142 });
143 }
144 }
145 _checkNumber(name) {
146 const check = this._check;
147 if (check[name] && typeof check[name] !== 'number') {
148 throw new Error(`No numerical value for \`${name}\` setting given in \
149${this._setting[name] && this._setting[name].description || this._setting[name]}`);
150 }
151 }
152 _checkArray(name) {
153 const check = this._check;
154 if (check[name]) {
155 if (check[name] instanceof Set) check[name] = Array.from(check[name]);else if (!Array.isArray(check[name])) {
156 if (typeof check[name] === 'object') check[name] = Object.keys(check[name]);else check[name] = [check[name]];
157 }
158 }
159 }
160 _checkObject(name) {
161 const check = this._check;
162 if (!check[name]) check[name] = {};else if (typeof check[name] !== 'object') {
163 throw new Error(`No object for \`${name}\` setting given in \
164${this._setting[name] && this._setting[name].description || this._setting[name]}`);
165 }
166 }
167 _checkMatch(name) {
168 const check = this._check;
169 if (check[name] === undefined) return;
170 if (check[name] instanceof RegExp) return;
171 const e = check[name].toString();
172 if (e.match(/^\/([^\\/]|\\.)+\/[gi]*$/)) {
173 const parts = e.match(/([^\\/]|\\.)+/g);
174 if (parts.length < 1 || parts.length > 2) {
175 throw new Error(`Could not convert ${_util2.default.inspect(check[name])} to regular expression`);
176 }
177 check[name] = new RegExp(parts[0], parts[1]);
178 }
179 check[name] = check[name].toString();
180 }
181 _checkArrayMatch(name) {
182 this._checkArray(name);
183 const check = this._check;
184 if (check[name] === undefined) return;
185 check[name] = check[name].map(e => {
186 if (e instanceof RegExp) return e;
187 const el = e.toString();
188 if (el.match(/^\/([^\\/]|\\.)+\/[gi]*$/)) {
189 const parts = el.match(/([^\\/]|\\.)+/g);
190 if (parts.length < 1 || parts.length > 2) {
191 throw new Error(`Could not convert ${_util2.default.inspect(e)} to regular expression`);
192 }
193 return new RegExp(parts[0], parts[1]);
194 }
195 return el;
196 });
197 }
198
199 stripEmpty(flag) {
200 return this._setFlag('stripEmpty', flag);
201 }
202
203 _emptyDescriptor() {
204 const set = this._setting;
205 if (set.stripEmpty instanceof _Reference2.default) {
206 return `Empty values are set to \`undefined\` depending on ${set.stripEmpty.description}.\n`;
207 }
208 return set.stripEmpty ? 'Empty values are set to `undefined`.\n' : '';
209 }
210
211 _emptyValidator(data) {
212 const check = this._check;
213 try {
214 this._checkBoolean('stripEmpty');
215 } catch (err) {
216 return Promise.reject(new _Error2.default(this, data, err.message));
217 }
218 if (check.stripEmpty && (data.value === '' || data.value === null || Array.isArray(data.value) && !data.value.length || Object.keys(data.value).length === 0 && data.value.constructor === Object)) {
219 data.value = undefined;
220 }
221 return Promise.resolve();
222 }
223
224 required(flag) {
225 const set = this._setting;
226 if (set.forbidden && !this._isReference('forbidden')) {
227 throw new Error('This is already `forbidden` and can´t be also be `required`');
228 }
229 return this._setFlag('required', flag);
230 }
231 forbidden(flag) {
232 const set = this._setting;
233 if (set.required && !this._isReference('required')) {
234 throw new Error('This is already `required` and can´t be also be `forbidden`');
235 }
236 return this._setFlag('forbidden', flag);
237 }
238 default(value) {
239 return this._setAny('default', value);
240 }
241
242 _optionalDescriptor() {
243 const set = this._setting;
244 let msg = '';
245 if (set.default) {
246 const value = set.default instanceof _Reference2.default ? set.default.description : _util2.default.inspect(set.default);
247 msg += `It will default to ${value} if not set. `;
248 }
249 if (set.required) {
250 if (set.required instanceof _Reference2.default) {
251 msg += `It is required depending on ${set.required.description}. `;
252 } else msg += 'It is required and has to be set with a value. ';
253 }
254 if (set.forbidden) {
255 if (set.forbidden instanceof _Reference2.default) {
256 msg += `It is forbidden depending on ${set.forbidden.description}. `;
257 } else msg += 'It is forbidden and could not contain a value. ';
258 }
259 return msg.replace(/ $/, '\n');
260 }
261
262 _optionalValidator(data) {
263 const check = this._check;
264 try {
265 this._checkBoolean('required');
266 this._checkBoolean('forbidden');
267 } catch (err) {
268 return Promise.reject(new _Error2.default(this, data, err.message));
269 }
270
271 if (data.value === undefined && check.default) data.value = check.default;
272
273 if (check.required && data.value === undefined) {
274 return Promise.reject(new _Error2.default(this, data, 'This element is mandatory!'));
275 }
276 if (check.forbidden && data.value !== undefined) {
277 return Promise.reject(new _Error2.default(this, data, 'This element is forbidden!'));
278 }
279 if (data.value === undefined) return Promise.reject();
280 return Promise.resolve();
281 }
282
283 raw(flag) {
284 return this._setFlag('raw', flag);
285 }
286
287 _rawDescriptor() {
288 const set = this._setting;
289 if (set.raw instanceof _Reference2.default) {
290 return `The original value is used depending on ${set.raw.description}.\n`;
291 }
292 return set.raw ? 'After validation the original value is used.\n' : '';
293 }
294
295 _rawValidator(data) {
296 const check = this._check;
297 try {
298 this._checkBoolean('raw');
299 } catch (err) {
300 return Promise.reject(new _Error2.default(this, data, err.message));
301 }
302 if (check.raw) data.value = data.orig;
303 return Promise.resolve();
304 }
305
306 get clone() {
307 return Object.assign(Object.create(this), this);
308 }
309
310 get description() {
311 let msg = '';
312
313 if (this.base) {
314 msg += `Use as base: ${_util2.default.inspect(this.base instanceof _Data2.default ? this.base.value : this.base).trim()}. `;
315 }
316
317 this._rules.descriptor.forEach(rule => {
318 if (rule) msg += rule.call(this);
319 });
320 return msg.trim();
321 }
322
323 _validate(value, source, options) {
324 if (this.debug.enabled) {
325 this.debug(_util2.default.inspect(value));
326 this.debug(` ${_util2.default.inspect(this)}`);
327 }
328 const data = value instanceof _Data2.default ? value : new _Data2.default(value, source, options);
329 if (this.base) {
330 data.value = this.base instanceof _Data2.default ? this.base.value : this.base;
331 }
332 let p = Promise.resolve();
333
334 if (data.value instanceof _Reference2.default) {
335 p = p.then(() => data.value.raw().resolve(data)).then(res => {
336 data.value = res;
337 if (this.debug.enabled) this.debug(` Use base: ${_util2.default.inspect(data)}`);
338 });
339 }
340
341 const par = [];
342 this._check = {};
343 const set = this._setting;
344 var _iteratorNormalCompletion2 = true;
345 var _didIteratorError2 = false;
346 var _iteratorError2 = undefined;
347
348 try {
349 for (var _iterator2 = Object.keys(set)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
350 const key = _step2.value;
351
352 let raw = set[key];
353 if (raw instanceof Set) raw = Array.from(raw);
354 if (raw instanceof _Reference2.default) {
355 par.push(raw.resolve(data).then(res => {
356 this._check[key] = res;
357 }));
358 } else if (Array.isArray(raw)) {
359 this._check[key] = [];
360 var _iteratorNormalCompletion3 = true;
361 var _didIteratorError3 = false;
362 var _iteratorError3 = undefined;
363
364 try {
365 for (var _iterator3 = raw.keys()[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
366 const i = _step3.value;
367
368 const e = raw[i];
369 if (e instanceof _Reference2.default) {
370 this._check[key][i] = null;
371 par.push(e.resolve(data).then(res => {
372 this._check[key][i] = res;
373 }));
374 } else if (Array.isArray(e)) {
375 this._check[key][i] = [];
376 var _iteratorNormalCompletion4 = true;
377 var _didIteratorError4 = false;
378 var _iteratorError4 = undefined;
379
380 try {
381 for (var _iterator4 = e.keys()[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
382 const j = _step4.value;
383
384 const sub = e[j];
385 if (sub instanceof _Reference2.default) {
386 this._check[key][i][j] = null;
387 par.push(sub.resolve(data).then(res => {
388 this._check[key][i][j] = res;
389 }));
390 } else this._check[key][i][j] = sub;
391 }
392 } catch (err) {
393 _didIteratorError4 = true;
394 _iteratorError4 = err;
395 } finally {
396 try {
397 if (!_iteratorNormalCompletion4 && _iterator4.return) {
398 _iterator4.return();
399 }
400 } finally {
401 if (_didIteratorError4) {
402 throw _iteratorError4;
403 }
404 }
405 }
406 } else this._check[key].push(e);
407 }
408 } catch (err) {
409 _didIteratorError3 = true;
410 _iteratorError3 = err;
411 } finally {
412 try {
413 if (!_iteratorNormalCompletion3 && _iterator3.return) {
414 _iterator3.return();
415 }
416 } finally {
417 if (_didIteratorError3) {
418 throw _iteratorError3;
419 }
420 }
421 }
422 } else if (raw instanceof Map) {
423 this._check[key] = {};
424 var _iteratorNormalCompletion5 = true;
425 var _didIteratorError5 = false;
426 var _iteratorError5 = undefined;
427
428 try {
429 for (var _iterator5 = raw.keys()[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
430 const k = _step5.value;
431
432 const e = raw.get(k);
433 if (e instanceof _Reference2.default) {
434 this._check[key][k] = null;
435 par.push(e.resolve(data).then(res => {
436 this._check[key][k] = res;
437 }));
438 } else this._check[key][k] = e;
439 }
440 } catch (err) {
441 _didIteratorError5 = true;
442 _iteratorError5 = err;
443 } finally {
444 try {
445 if (!_iteratorNormalCompletion5 && _iterator5.return) {
446 _iterator5.return();
447 }
448 } finally {
449 if (_didIteratorError5) {
450 throw _iteratorError5;
451 }
452 }
453 }
454 } else this._check[key] = raw;
455 }
456 } catch (err) {
457 _didIteratorError2 = true;
458 _iteratorError2 = err;
459 } finally {
460 try {
461 if (!_iteratorNormalCompletion2 && _iterator2.return) {
462 _iterator2.return();
463 }
464 } finally {
465 if (_didIteratorError2) {
466 throw _iteratorError2;
467 }
468 }
469 }
470
471 p = p.then(() => Promise.all(par));
472
473 this._rules.validator.forEach(rule => {
474 p = p.then(() => rule.call(this, data));
475 });
476
477 return p.then(() => {
478 if (this.debug.enabled) this.debug(`=> ${_util2.default.inspect(data)}`);
479 data.done(data.value);
480 return data;
481 }).catch(err => {
482 if (this.debug.enabled) {
483 if (err) this.debug(`=> ${_util2.default.inspect(err)}`);else this.debug(`=> ${_util2.default.inspect(data)}`);
484 }
485 return err ? Promise.reject(err) : data;
486 });
487 }
488
489 validate(value, source, options) {
490 return this._validate(value, source, options).then(data => data.value);
491 }
492}
493
494exports.default = Schema;
\No newline at end of file