joi 14.2.0 → 17.2.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.
Files changed (59) hide show
  1. package/LICENSE.md +10 -0
  2. package/README.md +9 -118
  3. package/dist/joi-browser.min.js +1 -0
  4. package/lib/annotate.js +175 -0
  5. package/lib/base.js +1068 -0
  6. package/lib/cache.js +143 -0
  7. package/lib/common.js +216 -0
  8. package/lib/compile.js +283 -0
  9. package/lib/errors.js +160 -269
  10. package/lib/extend.js +312 -0
  11. package/lib/index.d.ts +2200 -0
  12. package/lib/index.js +179 -347
  13. package/lib/manifest.js +476 -0
  14. package/lib/messages.js +178 -0
  15. package/lib/modify.js +267 -0
  16. package/lib/ref.js +386 -25
  17. package/lib/schemas.js +291 -15
  18. package/lib/state.js +152 -0
  19. package/lib/template.js +427 -0
  20. package/lib/trace.js +346 -0
  21. package/lib/types/alternatives.js +329 -0
  22. package/lib/types/any.js +174 -0
  23. package/lib/types/array.js +775 -0
  24. package/lib/types/binary.js +98 -0
  25. package/lib/types/boolean.js +150 -0
  26. package/lib/types/date.js +233 -0
  27. package/lib/types/function.js +93 -0
  28. package/lib/types/keys.js +1043 -0
  29. package/lib/types/link.js +168 -0
  30. package/lib/types/number.js +335 -0
  31. package/lib/types/object.js +22 -0
  32. package/lib/types/string.js +820 -0
  33. package/lib/types/symbol.js +102 -0
  34. package/lib/validator.js +650 -0
  35. package/lib/values.js +263 -0
  36. package/package.json +34 -29
  37. package/CHANGELOG.md +0 -3
  38. package/LICENSE +0 -25
  39. package/lib/cast.js +0 -64
  40. package/lib/language.js +0 -166
  41. package/lib/set.js +0 -191
  42. package/lib/types/alternatives/index.js +0 -218
  43. package/lib/types/any/index.js +0 -978
  44. package/lib/types/any/settings.js +0 -36
  45. package/lib/types/array/index.js +0 -707
  46. package/lib/types/binary/index.js +0 -100
  47. package/lib/types/boolean/index.js +0 -100
  48. package/lib/types/date/index.js +0 -182
  49. package/lib/types/func/index.js +0 -90
  50. package/lib/types/lazy/index.js +0 -82
  51. package/lib/types/number/index.js +0 -248
  52. package/lib/types/object/index.js +0 -957
  53. package/lib/types/state.js +0 -11
  54. package/lib/types/string/index.js +0 -703
  55. package/lib/types/string/ip.js +0 -54
  56. package/lib/types/string/rfc3986.js +0 -219
  57. package/lib/types/string/uri.js +0 -46
  58. package/lib/types/symbol/index.js +0 -93
  59. package/lib/types/symbols.js +0 -5
@@ -1,957 +0,0 @@
1
- 'use strict';
2
-
3
- // Load modules
4
-
5
- const Hoek = require('hoek');
6
- const Topo = require('topo');
7
- const Any = require('../any');
8
- const Errors = require('../../errors');
9
- const Cast = require('../../cast');
10
- const State = require('../state');
11
-
12
-
13
- // Declare internals
14
-
15
- const internals = {};
16
-
17
-
18
- internals.Object = class extends Any {
19
-
20
- constructor() {
21
-
22
- super();
23
- this._type = 'object';
24
- this._inner.children = null;
25
- this._inner.renames = [];
26
- this._inner.dependencies = [];
27
- this._inner.patterns = [];
28
- }
29
-
30
- _init(...args) {
31
-
32
- return args.length ? this.keys(...args) : this;
33
- }
34
-
35
- _base(value, state, options) {
36
-
37
- let target = value;
38
- const errors = [];
39
- const finish = () => {
40
-
41
- return {
42
- value: target,
43
- errors: errors.length ? errors : null
44
- };
45
- };
46
-
47
- if (typeof value === 'string' &&
48
- options.convert) {
49
-
50
- value = internals.safeParse(value);
51
- }
52
-
53
- const type = this._flags.func ? 'function' : 'object';
54
- if (!value ||
55
- typeof value !== type ||
56
- Array.isArray(value)) {
57
-
58
- errors.push(this.createError(type + '.base', { value }, state, options));
59
- return finish();
60
- }
61
-
62
- // Skip if there are no other rules to test
63
-
64
- if (!this._inner.renames.length &&
65
- !this._inner.dependencies.length &&
66
- !this._inner.children && // null allows any keys
67
- !this._inner.patterns.length) {
68
-
69
- target = value;
70
- return finish();
71
- }
72
-
73
- // Ensure target is a local copy (parsed) or shallow copy
74
-
75
- if (target === value) {
76
- if (type === 'object') {
77
- target = Object.create(Object.getPrototypeOf(value));
78
- }
79
- else {
80
- target = function (...args) {
81
-
82
- return value.apply(this, args);
83
- };
84
-
85
- target.prototype = Hoek.clone(value.prototype);
86
- }
87
-
88
- const valueKeys = Object.keys(value);
89
- for (let i = 0; i < valueKeys.length; ++i) {
90
- target[valueKeys[i]] = value[valueKeys[i]];
91
- }
92
- }
93
- else {
94
- target = value;
95
- }
96
-
97
- // Rename keys
98
-
99
- const renamed = {};
100
- for (let i = 0; i < this._inner.renames.length; ++i) {
101
- const rename = this._inner.renames[i];
102
-
103
- if (rename.isRegExp) {
104
- const targetKeys = Object.keys(target);
105
- const matchedTargetKeys = [];
106
-
107
- for (let j = 0; j < targetKeys.length; ++j) {
108
- if (rename.from.test(targetKeys[j])) {
109
- matchedTargetKeys.push(targetKeys[j]);
110
- }
111
- }
112
-
113
- const allUndefined = matchedTargetKeys.every((key) => target[key] === undefined);
114
- if (rename.options.ignoreUndefined && allUndefined) {
115
- continue;
116
- }
117
-
118
- if (!rename.options.multiple &&
119
- renamed[rename.to]) {
120
-
121
- errors.push(this.createError('object.rename.regex.multiple', { from: matchedTargetKeys, to: rename.to }, state, options));
122
- if (options.abortEarly) {
123
- return finish();
124
- }
125
- }
126
-
127
- if (Object.prototype.hasOwnProperty.call(target, rename.to) &&
128
- !rename.options.override &&
129
- !renamed[rename.to]) {
130
-
131
- errors.push(this.createError('object.rename.regex.override', { from: matchedTargetKeys, to: rename.to }, state, options));
132
- if (options.abortEarly) {
133
- return finish();
134
- }
135
- }
136
-
137
- if (allUndefined) {
138
- delete target[rename.to];
139
- }
140
- else {
141
- target[rename.to] = target[matchedTargetKeys[matchedTargetKeys.length - 1]];
142
- }
143
-
144
- renamed[rename.to] = true;
145
-
146
- if (!rename.options.alias) {
147
- for (let j = 0; j < matchedTargetKeys.length; ++j) {
148
- delete target[matchedTargetKeys[j]];
149
- }
150
- }
151
- }
152
- else {
153
- if (rename.options.ignoreUndefined && target[rename.from] === undefined) {
154
- continue;
155
- }
156
-
157
- if (!rename.options.multiple &&
158
- renamed[rename.to]) {
159
-
160
- errors.push(this.createError('object.rename.multiple', { from: rename.from, to: rename.to }, state, options));
161
- if (options.abortEarly) {
162
- return finish();
163
- }
164
- }
165
-
166
- if (Object.prototype.hasOwnProperty.call(target, rename.to) &&
167
- !rename.options.override &&
168
- !renamed[rename.to]) {
169
-
170
- errors.push(this.createError('object.rename.override', { from: rename.from, to: rename.to }, state, options));
171
- if (options.abortEarly) {
172
- return finish();
173
- }
174
- }
175
-
176
- if (target[rename.from] === undefined) {
177
- delete target[rename.to];
178
- }
179
- else {
180
- target[rename.to] = target[rename.from];
181
- }
182
-
183
- renamed[rename.to] = true;
184
-
185
- if (!rename.options.alias) {
186
- delete target[rename.from];
187
- }
188
- }
189
- }
190
-
191
- // Validate schema
192
-
193
- if (!this._inner.children && // null allows any keys
194
- !this._inner.patterns.length &&
195
- !this._inner.dependencies.length) {
196
-
197
- return finish();
198
- }
199
-
200
- const unprocessed = new Set(Object.keys(target));
201
-
202
- if (this._inner.children) {
203
- const stripProps = [];
204
-
205
- for (let i = 0; i < this._inner.children.length; ++i) {
206
- const child = this._inner.children[i];
207
- const key = child.key;
208
- const item = target[key];
209
-
210
- unprocessed.delete(key);
211
-
212
- const localState = new State(key, [...state.path, key], target, state.reference);
213
- const result = child.schema._validate(item, localState, options);
214
- if (result.errors) {
215
- errors.push(this.createError('object.child', { key, child: child.schema._getLabel(key), reason: result.errors }, localState, options));
216
-
217
- if (options.abortEarly) {
218
- return finish();
219
- }
220
- }
221
- else {
222
- if (child.schema._flags.strip || (result.value === undefined && result.value !== item)) {
223
- stripProps.push(key);
224
- target[key] = result.finalValue;
225
- }
226
- else if (result.value !== undefined) {
227
- target[key] = result.value;
228
- }
229
- }
230
- }
231
-
232
- for (let i = 0; i < stripProps.length; ++i) {
233
- delete target[stripProps[i]];
234
- }
235
- }
236
-
237
- // Unknown keys
238
-
239
- if (unprocessed.size && this._inner.patterns.length) {
240
-
241
- for (const key of unprocessed) {
242
- const localState = new State(key, [...state.path, key], target, state.reference);
243
- const item = target[key];
244
-
245
- for (let i = 0; i < this._inner.patterns.length; ++i) {
246
- const pattern = this._inner.patterns[i];
247
-
248
- if (pattern.regex ?
249
- pattern.regex.test(key) :
250
- !pattern.schema.validate(key).error) {
251
-
252
- unprocessed.delete(key);
253
-
254
- const result = pattern.rule._validate(item, localState, options);
255
- if (result.errors) {
256
- errors.push(this.createError('object.child', {
257
- key,
258
- child: pattern.rule._getLabel(key),
259
- reason: result.errors
260
- }, localState, options));
261
-
262
- if (options.abortEarly) {
263
- return finish();
264
- }
265
- }
266
-
267
- target[key] = result.value;
268
- }
269
- }
270
- }
271
- }
272
-
273
- if (unprocessed.size && (this._inner.children || this._inner.patterns.length)) {
274
- if ((options.stripUnknown && this._flags.allowUnknown !== true) ||
275
- options.skipFunctions) {
276
-
277
- const stripUnknown = options.stripUnknown
278
- ? (options.stripUnknown === true ? true : !!options.stripUnknown.objects)
279
- : false;
280
-
281
-
282
- for (const key of unprocessed) {
283
- if (stripUnknown) {
284
- delete target[key];
285
- unprocessed.delete(key);
286
- }
287
- else if (typeof target[key] === 'function') {
288
- unprocessed.delete(key);
289
- }
290
- }
291
- }
292
-
293
- if ((this._flags.allowUnknown !== undefined ? !this._flags.allowUnknown : !options.allowUnknown)) {
294
-
295
- for (const unprocessedKey of unprocessed) {
296
- errors.push(this.createError('object.allowUnknown', { child: unprocessedKey, value: target[unprocessedKey] }, {
297
- key: unprocessedKey,
298
- path: [...state.path, unprocessedKey]
299
- }, options, {}));
300
- }
301
- }
302
- }
303
-
304
- // Validate dependencies
305
-
306
- for (let i = 0; i < this._inner.dependencies.length; ++i) {
307
- const dep = this._inner.dependencies[i];
308
- const hasKey = dep.key !== null;
309
- const splitKey = hasKey && dep.key.split('.');
310
- const localState = hasKey ? new State(splitKey[splitKey.length - 1], [...state.path, ...splitKey]) : new State(null, state.path);
311
- const err = internals[dep.type].call(this, dep.key, hasKey && Hoek.reach(target, dep.key), dep.peers, target, localState, options);
312
- if (err instanceof Errors.Err) {
313
- errors.push(err);
314
- if (options.abortEarly) {
315
- return finish();
316
- }
317
- }
318
- }
319
-
320
- return finish();
321
- }
322
-
323
- keys(schema) {
324
-
325
- Hoek.assert(schema === null || schema === undefined || typeof schema === 'object', 'Object schema must be a valid object');
326
- Hoek.assert(!schema || !(schema instanceof Any), 'Object schema cannot be a joi schema');
327
-
328
- const obj = this.clone();
329
-
330
- if (!schema) {
331
- obj._inner.children = null;
332
- return obj;
333
- }
334
-
335
- const children = Object.keys(schema);
336
-
337
- if (!children.length) {
338
- obj._inner.children = [];
339
- return obj;
340
- }
341
-
342
- const topo = new Topo();
343
- if (obj._inner.children) {
344
- for (let i = 0; i < obj._inner.children.length; ++i) {
345
- const child = obj._inner.children[i];
346
-
347
- // Only add the key if we are not going to replace it later
348
- if (!children.includes(child.key)) {
349
- topo.add(child, { after: child._refs, group: child.key });
350
- }
351
- }
352
- }
353
-
354
- for (let i = 0; i < children.length; ++i) {
355
- const key = children[i];
356
- const child = schema[key];
357
- try {
358
- const cast = Cast.schema(this._currentJoi, child);
359
- topo.add({ key, schema: cast }, { after: cast._refs, group: key });
360
- }
361
- catch (castErr) {
362
- if (castErr.hasOwnProperty('path')) {
363
- castErr.path = key + '.' + castErr.path;
364
- }
365
- else {
366
- castErr.path = key;
367
- }
368
-
369
- throw castErr;
370
- }
371
- }
372
-
373
- obj._inner.children = topo.nodes;
374
-
375
- return obj;
376
- }
377
-
378
- append(schema) {
379
- // Skip any changes
380
- if (schema === null || schema === undefined || Object.keys(schema).length === 0) {
381
- return this;
382
- }
383
-
384
- return this.keys(schema);
385
- }
386
-
387
- unknown(allow) {
388
-
389
- const value = allow !== false;
390
-
391
- if (this._flags.allowUnknown === value) {
392
- return this;
393
- }
394
-
395
- const obj = this.clone();
396
- obj._flags.allowUnknown = value;
397
- return obj;
398
- }
399
-
400
- length(limit) {
401
-
402
- Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');
403
-
404
- return this._test('length', limit, function (value, state, options) {
405
-
406
- if (Object.keys(value).length === limit) {
407
- return value;
408
- }
409
-
410
- return this.createError('object.length', { limit, value }, state, options);
411
- });
412
- }
413
-
414
- min(limit) {
415
-
416
- Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');
417
-
418
- return this._test('min', limit, function (value, state, options) {
419
-
420
- if (Object.keys(value).length >= limit) {
421
- return value;
422
- }
423
-
424
- return this.createError('object.min', { limit, value }, state, options);
425
- });
426
- }
427
-
428
- max(limit) {
429
-
430
- Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');
431
-
432
- return this._test('max', limit, function (value, state, options) {
433
-
434
- if (Object.keys(value).length <= limit) {
435
- return value;
436
- }
437
-
438
- return this.createError('object.max', { limit, value }, state, options);
439
- });
440
- }
441
-
442
- pattern(pattern, schema) {
443
-
444
- const isRegExp = pattern instanceof RegExp;
445
- Hoek.assert(isRegExp || pattern instanceof Any, 'pattern must be a regex or schema');
446
- Hoek.assert(schema !== undefined, 'Invalid rule');
447
-
448
- if (isRegExp) {
449
- Hoek.assert(!pattern.flags.includes('g') && !pattern.flags.includes('y'), 'pattern should not use global or sticky mode');
450
- }
451
-
452
- try {
453
- schema = Cast.schema(this._currentJoi, schema);
454
- }
455
- catch (castErr) {
456
- if (castErr.hasOwnProperty('path')) {
457
- castErr.message = `${castErr.message}(${castErr.path})`;
458
- }
459
-
460
- throw castErr;
461
- }
462
-
463
- const obj = this.clone();
464
- if (isRegExp) {
465
- obj._inner.patterns.push({ regex: pattern, rule: schema });
466
- }
467
- else {
468
- obj._inner.patterns.push({ schema: pattern, rule: schema });
469
- }
470
-
471
- return obj;
472
- }
473
-
474
- schema() {
475
-
476
- return this._test('schema', null, function (value, state, options) {
477
-
478
- if (value instanceof Any) {
479
- return value;
480
- }
481
-
482
- return this.createError('object.schema', null, state, options);
483
- });
484
- }
485
-
486
- with(key, peers) {
487
-
488
- Hoek.assert(arguments.length === 2, 'Invalid number of arguments, expected 2.');
489
-
490
- return this._dependency('with', key, peers);
491
- }
492
-
493
- without(key, peers) {
494
-
495
- Hoek.assert(arguments.length === 2, 'Invalid number of arguments, expected 2.');
496
-
497
- return this._dependency('without', key, peers);
498
- }
499
-
500
- xor(...peers) {
501
-
502
- peers = Hoek.flatten(peers);
503
- return this._dependency('xor', null, peers);
504
- }
505
-
506
- oxor(...peers) {
507
-
508
- return this._dependency('oxor', null, peers);
509
- }
510
-
511
- or(...peers) {
512
-
513
- peers = Hoek.flatten(peers);
514
- return this._dependency('or', null, peers);
515
- }
516
-
517
- and(...peers) {
518
-
519
- peers = Hoek.flatten(peers);
520
- return this._dependency('and', null, peers);
521
- }
522
-
523
- nand(...peers) {
524
-
525
- peers = Hoek.flatten(peers);
526
- return this._dependency('nand', null, peers);
527
- }
528
-
529
- requiredKeys(...children) {
530
-
531
- children = Hoek.flatten(children);
532
- return this.applyFunctionToChildren(children, 'required');
533
- }
534
-
535
- optionalKeys(...children) {
536
-
537
- children = Hoek.flatten(children);
538
- return this.applyFunctionToChildren(children, 'optional');
539
- }
540
-
541
- forbiddenKeys(...children) {
542
-
543
- children = Hoek.flatten(children);
544
- return this.applyFunctionToChildren(children, 'forbidden');
545
- }
546
-
547
- rename(from, to, options) {
548
-
549
- Hoek.assert(typeof from === 'string' || from instanceof RegExp, 'Rename missing the from argument');
550
- Hoek.assert(typeof to === 'string', 'Rename missing the to argument');
551
- Hoek.assert(to !== from, 'Cannot rename key to same name:', from);
552
-
553
- for (let i = 0; i < this._inner.renames.length; ++i) {
554
- Hoek.assert(this._inner.renames[i].from !== from, 'Cannot rename the same key multiple times');
555
- }
556
-
557
- const obj = this.clone();
558
-
559
- obj._inner.renames.push({
560
- from,
561
- to,
562
- options: Hoek.applyToDefaults(internals.renameDefaults, options || {}),
563
- isRegExp: from instanceof RegExp
564
- });
565
-
566
- return obj;
567
- }
568
-
569
- applyFunctionToChildren(children, fn, args = [], root) {
570
-
571
- children = [].concat(children);
572
- Hoek.assert(children.length > 0, 'expected at least one children');
573
-
574
- const groupedChildren = internals.groupChildren(children);
575
- let obj;
576
-
577
- if ('' in groupedChildren) {
578
- obj = this[fn](...args);
579
- delete groupedChildren[''];
580
- }
581
- else {
582
- obj = this.clone();
583
- }
584
-
585
- if (obj._inner.children) {
586
- root = root ? (root + '.') : '';
587
-
588
- for (let i = 0; i < obj._inner.children.length; ++i) {
589
- const child = obj._inner.children[i];
590
- const group = groupedChildren[child.key];
591
-
592
- if (group) {
593
- obj._inner.children[i] = {
594
- key: child.key,
595
- _refs: child._refs,
596
- schema: child.schema.applyFunctionToChildren(group, fn, args, root + child.key)
597
- };
598
-
599
- delete groupedChildren[child.key];
600
- }
601
- }
602
- }
603
-
604
- const remaining = Object.keys(groupedChildren);
605
- Hoek.assert(remaining.length === 0, 'unknown key(s)', remaining.join(', '));
606
-
607
- return obj;
608
- }
609
-
610
- _dependency(type, key, peers) {
611
-
612
- peers = [].concat(peers);
613
- for (let i = 0; i < peers.length; ++i) {
614
- Hoek.assert(typeof peers[i] === 'string', type, 'peers must be a string or array of strings');
615
- }
616
-
617
- const obj = this.clone();
618
- obj._inner.dependencies.push({ type, key, peers });
619
- return obj;
620
- }
621
-
622
- describe(shallow) {
623
-
624
- const description = super.describe();
625
-
626
- if (description.rules) {
627
- for (let i = 0; i < description.rules.length; ++i) {
628
- const rule = description.rules[i];
629
- // Coverage off for future-proof descriptions, only object().assert() is use right now
630
- if (/* $lab:coverage:off$ */rule.arg &&
631
- typeof rule.arg === 'object' &&
632
- rule.arg.schema &&
633
- rule.arg.ref /* $lab:coverage:on$ */) {
634
- rule.arg = {
635
- schema: rule.arg.schema.describe(),
636
- ref: rule.arg.ref.toString()
637
- };
638
- }
639
- }
640
- }
641
-
642
- if (this._inner.children &&
643
- !shallow) {
644
-
645
- description.children = {};
646
- for (let i = 0; i < this._inner.children.length; ++i) {
647
- const child = this._inner.children[i];
648
- description.children[child.key] = child.schema.describe();
649
- }
650
- }
651
-
652
- if (this._inner.dependencies.length) {
653
- description.dependencies = Hoek.clone(this._inner.dependencies);
654
- }
655
-
656
- if (this._inner.patterns.length) {
657
- description.patterns = [];
658
-
659
- for (let i = 0; i < this._inner.patterns.length; ++i) {
660
- const pattern = this._inner.patterns[i];
661
- if (pattern.regex) {
662
- description.patterns.push({ regex: pattern.regex.toString(), rule: pattern.rule.describe() });
663
- }
664
- else {
665
- description.patterns.push({ schema: pattern.schema.describe(), rule: pattern.rule.describe() });
666
- }
667
- }
668
- }
669
-
670
- if (this._inner.renames.length > 0) {
671
- description.renames = Hoek.clone(this._inner.renames);
672
- }
673
-
674
- return description;
675
- }
676
-
677
- assert(ref, schema, message) {
678
-
679
- ref = Cast.ref(ref);
680
- Hoek.assert(ref.isContext || ref.depth > 1, 'Cannot use assertions for root level references - use direct key rules instead');
681
- message = message || 'pass the assertion test';
682
- Hoek.assert(typeof message === 'string', 'Message must be a string');
683
-
684
- try {
685
- schema = Cast.schema(this._currentJoi, schema);
686
- }
687
- catch (castErr) {
688
- if (castErr.hasOwnProperty('path')) {
689
- castErr.message = `${castErr.message}(${castErr.path})`;
690
- }
691
-
692
- throw castErr;
693
- }
694
-
695
- const key = ref.path[ref.path.length - 1];
696
- const path = ref.path.join('.');
697
-
698
- return this._test('assert', { schema, ref }, function (value, state, options) {
699
-
700
- const result = schema._validate(ref(value), null, options, value);
701
- if (!result.errors) {
702
- return value;
703
- }
704
-
705
- const localState = new State(key, ref.path, state.parent, state.reference);
706
- return this.createError('object.assert', { ref: path, message }, localState, options);
707
- });
708
- }
709
-
710
- type(constructor, name = constructor.name) {
711
-
712
- Hoek.assert(typeof constructor === 'function', 'type must be a constructor function');
713
- const typeData = {
714
- name,
715
- ctor: constructor
716
- };
717
-
718
- return this._test('type', typeData, function (value, state, options) {
719
-
720
- if (value instanceof constructor) {
721
- return value;
722
- }
723
-
724
- return this.createError('object.type', { type: typeData.name, value }, state, options);
725
- });
726
- }
727
- };
728
-
729
- internals.safeParse = function (value) {
730
-
731
- try {
732
- return JSON.parse(value);
733
- }
734
- catch (parseErr) {}
735
-
736
- return value;
737
- };
738
-
739
-
740
- internals.renameDefaults = {
741
- alias: false, // Keep old value in place
742
- multiple: false, // Allow renaming multiple keys into the same target
743
- override: false // Overrides an existing key
744
- };
745
-
746
-
747
- internals.groupChildren = function (children) {
748
-
749
- children.sort();
750
-
751
- const grouped = {};
752
-
753
- for (let i = 0; i < children.length; ++i) {
754
- const child = children[i];
755
- Hoek.assert(typeof child === 'string', 'children must be strings');
756
- const group = child.split('.')[0];
757
- const childGroup = grouped[group] = (grouped[group] || []);
758
- childGroup.push(child.substring(group.length + 1));
759
- }
760
-
761
- return grouped;
762
- };
763
-
764
-
765
- internals.keysToLabels = function (schema, keys) {
766
-
767
- const children = schema._inner.children;
768
-
769
- if (!children) {
770
- return keys;
771
- }
772
-
773
- const findLabel = function (key) {
774
-
775
- const matchingChild = schema._currentJoi.reach(schema, key);
776
- return matchingChild ? matchingChild._getLabel(key) : key;
777
- };
778
-
779
- if (Array.isArray(keys)) {
780
- return keys.map(findLabel);
781
- }
782
-
783
- return findLabel(keys);
784
- };
785
-
786
-
787
- internals.with = function (key, value, peers, parent, state, options) {
788
-
789
- if (value === undefined) {
790
- return;
791
- }
792
-
793
- for (let i = 0; i < peers.length; ++i) {
794
-
795
- const peer = peers[i];
796
- const keysExist = Hoek.reach(parent, peer);
797
- if (keysExist === undefined) {
798
-
799
- return this.createError('object.with', {
800
- main: key,
801
- mainWithLabel: internals.keysToLabels(this, key),
802
- peer,
803
- peerWithLabel: internals.keysToLabels(this, peer)
804
- }, state, options);
805
- }
806
- }
807
- };
808
-
809
-
810
- internals.without = function (key, value, peers, parent, state, options) {
811
-
812
- if (value === undefined) {
813
- return;
814
- }
815
-
816
- for (let i = 0; i < peers.length; ++i) {
817
- const peer = peers[i];
818
- const keysExist = Hoek.reach(parent, peer);
819
- if (keysExist !== undefined) {
820
-
821
- return this.createError('object.without', {
822
- main: key,
823
- mainWithLabel: internals.keysToLabels(this, key),
824
- peer,
825
- peerWithLabel: internals.keysToLabels(this, peer)
826
- }, state, options);
827
- }
828
- }
829
- };
830
-
831
-
832
- internals.xor = function (key, value, peers, parent, state, options) {
833
-
834
- const present = [];
835
- for (let i = 0; i < peers.length; ++i) {
836
- const peer = peers[i];
837
- const keysExist = Hoek.reach(parent, peer);
838
- if (keysExist !== undefined) {
839
- present.push(peer);
840
- }
841
- }
842
-
843
- if (present.length === 1) {
844
- return;
845
- }
846
-
847
- const context = { peers, peersWithLabels: internals.keysToLabels(this, peers) };
848
-
849
- if (present.length === 0) {
850
- return this.createError('object.missing', context, state, options);
851
- }
852
-
853
- context.present = present;
854
- context.presentWithLabels = internals.keysToLabels(this, present);
855
-
856
- return this.createError('object.xor', context, state, options);
857
- };
858
-
859
-
860
- internals.oxor = function (key, value, peers, parent, state, options) {
861
-
862
- const present = [];
863
- for (let i = 0; i < peers.length; ++i) {
864
- const peer = peers[i];
865
- const keysExist = Hoek.reach(parent, peer);
866
- if (keysExist !== undefined) {
867
- present.push(peer);
868
- }
869
- }
870
-
871
- if (!present.length ||
872
- present.length === 1) {
873
-
874
- return;
875
- }
876
-
877
- const context = { peers, peersWithLabels: internals.keysToLabels(this, peers) };
878
- context.present = present;
879
- context.presentWithLabels = internals.keysToLabels(this, present);
880
-
881
- return this.createError('object.oxor', context, state, options);
882
- };
883
-
884
-
885
- internals.or = function (key, value, peers, parent, state, options) {
886
-
887
- for (let i = 0; i < peers.length; ++i) {
888
- const peer = peers[i];
889
- const keysExist = Hoek.reach(parent, peer);
890
- if (keysExist !== undefined) {
891
- return;
892
- }
893
- }
894
-
895
- return this.createError('object.missing', {
896
- peers,
897
- peersWithLabels: internals.keysToLabels(this, peers)
898
- }, state, options);
899
- };
900
-
901
-
902
- internals.and = function (key, value, peers, parent, state, options) {
903
-
904
- const missing = [];
905
- const present = [];
906
- const count = peers.length;
907
- for (let i = 0; i < count; ++i) {
908
- const peer = peers[i];
909
- const keysExist = Hoek.reach(parent, peer);
910
- if (keysExist === undefined) {
911
-
912
- missing.push(peer);
913
- }
914
- else {
915
- present.push(peer);
916
- }
917
- }
918
-
919
- const aon = (missing.length === count || present.length === count);
920
-
921
- if (!aon) {
922
-
923
- return this.createError('object.and', {
924
- present,
925
- presentWithLabels: internals.keysToLabels(this, present),
926
- missing,
927
- missingWithLabels: internals.keysToLabels(this, missing)
928
- }, state, options);
929
- }
930
- };
931
-
932
-
933
- internals.nand = function (key, value, peers, parent, state, options) {
934
-
935
- const present = [];
936
- for (let i = 0; i < peers.length; ++i) {
937
- const peer = peers[i];
938
- const keysExist = Hoek.reach(parent, peer);
939
- if (keysExist !== undefined) {
940
-
941
- present.push(peer);
942
- }
943
- }
944
-
945
- const main = peers[0];
946
- const values = peers.slice(1);
947
- const allPresent = (present.length === peers.length);
948
- return allPresent ? this.createError('object.nand', {
949
- main,
950
- mainWithLabel: internals.keysToLabels(this, main),
951
- peers: values,
952
- peersWithLabels: internals.keysToLabels(this, values)
953
- }, state, options) : null;
954
- };
955
-
956
-
957
- module.exports = new internals.Object();