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,707 +0,0 @@
1
- 'use strict';
2
-
3
- // Load modules
4
-
5
- const Hoek = require('hoek');
6
-
7
- const Any = require('../any');
8
- const Cast = require('../../cast');
9
- const Ref = require('../../ref');
10
- const State = require('../state');
11
-
12
-
13
- // Declare internals
14
-
15
- const internals = {};
16
-
17
-
18
- internals.fastSplice = function (arr, i) {
19
-
20
- let pos = i;
21
- while (pos < arr.length) {
22
- arr[pos++] = arr[pos];
23
- }
24
-
25
- --arr.length;
26
- };
27
-
28
-
29
- internals.Array = class extends Any {
30
-
31
- constructor() {
32
-
33
- super();
34
- this._type = 'array';
35
- this._inner.items = [];
36
- this._inner.ordereds = [];
37
- this._inner.inclusions = [];
38
- this._inner.exclusions = [];
39
- this._inner.requireds = [];
40
- this._flags.sparse = false;
41
- }
42
-
43
- _base(value, state, options) {
44
-
45
- const result = {
46
- value
47
- };
48
-
49
- if (typeof value === 'string' &&
50
- options.convert) {
51
-
52
- internals.safeParse(value, result);
53
- }
54
-
55
- let isArray = Array.isArray(result.value);
56
- const wasArray = isArray;
57
- if (options.convert && this._flags.single && !isArray) {
58
- result.value = [result.value];
59
- isArray = true;
60
- }
61
-
62
- if (!isArray) {
63
- result.errors = this.createError('array.base', null, state, options);
64
- return result;
65
- }
66
-
67
- if (this._inner.inclusions.length ||
68
- this._inner.exclusions.length ||
69
- this._inner.requireds.length ||
70
- this._inner.ordereds.length ||
71
- !this._flags.sparse) {
72
-
73
- // Clone the array so that we don't modify the original
74
- if (wasArray) {
75
- result.value = result.value.slice(0);
76
- }
77
-
78
- result.errors = this._checkItems(result.value, wasArray, state, options);
79
-
80
- if (result.errors && wasArray && options.convert && this._flags.single) {
81
-
82
- // Attempt a 2nd pass by putting the array inside one.
83
- const previousErrors = result.errors;
84
-
85
- result.value = [result.value];
86
- result.errors = this._checkItems(result.value, wasArray, state, options);
87
-
88
- if (result.errors) {
89
-
90
- // Restore previous errors and value since this didn't validate either.
91
- result.errors = previousErrors;
92
- result.value = result.value[0];
93
- }
94
- }
95
- }
96
-
97
- return result;
98
- }
99
-
100
- _checkItems(items, wasArray, state, options) {
101
-
102
- const errors = [];
103
- let errored;
104
-
105
- const requireds = this._inner.requireds.slice();
106
- const ordereds = this._inner.ordereds.slice();
107
- const inclusions = [...this._inner.inclusions, ...requireds];
108
-
109
- let il = items.length;
110
- for (let i = 0; i < il; ++i) {
111
- errored = false;
112
- const item = items[i];
113
- let isValid = false;
114
- const key = wasArray ? i : state.key;
115
- const path = wasArray ? [...state.path, i] : state.path;
116
- const localState = new State(key, path, state.parent, state.reference);
117
- let res;
118
-
119
- // Sparse
120
-
121
- if (!this._flags.sparse && item === undefined) {
122
- errors.push(this.createError('array.sparse', null, { key: state.key, path: localState.path, pos: i }, options));
123
-
124
- if (options.abortEarly) {
125
- return errors;
126
- }
127
-
128
- ordereds.shift();
129
-
130
- continue;
131
- }
132
-
133
- // Exclusions
134
-
135
- for (let j = 0; j < this._inner.exclusions.length; ++j) {
136
- res = this._inner.exclusions[j]._validate(item, localState, {}); // Not passing options to use defaults
137
-
138
- if (!res.errors) {
139
- errors.push(this.createError(wasArray ? 'array.excludes' : 'array.excludesSingle', { pos: i, value: item }, { key: state.key, path: localState.path }, options));
140
- errored = true;
141
-
142
- if (options.abortEarly) {
143
- return errors;
144
- }
145
-
146
- ordereds.shift();
147
-
148
- break;
149
- }
150
- }
151
-
152
- if (errored) {
153
- continue;
154
- }
155
-
156
- // Ordered
157
- if (this._inner.ordereds.length) {
158
- if (ordereds.length > 0) {
159
- const ordered = ordereds.shift();
160
- res = ordered._validate(item, localState, options);
161
- if (!res.errors) {
162
- if (ordered._flags.strip) {
163
- internals.fastSplice(items, i);
164
- --i;
165
- --il;
166
- }
167
- else if (!this._flags.sparse && res.value === undefined) {
168
- errors.push(this.createError('array.sparse', null, { key: state.key, path: localState.path, pos: i }, options));
169
-
170
- if (options.abortEarly) {
171
- return errors;
172
- }
173
-
174
- continue;
175
- }
176
- else {
177
- items[i] = res.value;
178
- }
179
- }
180
- else {
181
- errors.push(this.createError('array.ordered', { pos: i, reason: res.errors, value: item }, { key: state.key, path: localState.path }, options));
182
- if (options.abortEarly) {
183
- return errors;
184
- }
185
- }
186
-
187
- continue;
188
- }
189
- else if (!this._inner.items.length) {
190
- errors.push(this.createError('array.orderedLength', { pos: i, limit: this._inner.ordereds.length }, { key: state.key, path: localState.path }, options));
191
- if (options.abortEarly) {
192
- return errors;
193
- }
194
-
195
- continue;
196
- }
197
- }
198
-
199
- // Requireds
200
-
201
- const requiredChecks = [];
202
- let jl = requireds.length;
203
- for (let j = 0; j < jl; ++j) {
204
- res = requiredChecks[j] = requireds[j]._validate(item, localState, options);
205
- if (!res.errors) {
206
- items[i] = res.value;
207
- isValid = true;
208
- internals.fastSplice(requireds, j);
209
- --j;
210
- --jl;
211
-
212
- if (!this._flags.sparse && res.value === undefined) {
213
- errors.push(this.createError('array.sparse', null, { key: state.key, path: localState.path, pos: i }, options));
214
-
215
- if (options.abortEarly) {
216
- return errors;
217
- }
218
- }
219
-
220
- break;
221
- }
222
- }
223
-
224
- if (isValid) {
225
- continue;
226
- }
227
-
228
- // Inclusions
229
-
230
- const stripUnknown = options.stripUnknown && !!options.stripUnknown.arrays || false;
231
-
232
- jl = inclusions.length;
233
- for (let j = 0; j < jl; ++j) {
234
- const inclusion = inclusions[j];
235
-
236
- // Avoid re-running requireds that already didn't match in the previous loop
237
- const previousCheck = requireds.indexOf(inclusion);
238
- if (previousCheck !== -1) {
239
- res = requiredChecks[previousCheck];
240
- }
241
- else {
242
- res = inclusion._validate(item, localState, options);
243
-
244
- if (!res.errors) {
245
- if (inclusion._flags.strip) {
246
- internals.fastSplice(items, i);
247
- --i;
248
- --il;
249
- }
250
- else if (!this._flags.sparse && res.value === undefined) {
251
- errors.push(this.createError('array.sparse', null, { key: state.key, path: localState.path, pos: i }, options));
252
- errored = true;
253
- }
254
- else {
255
- items[i] = res.value;
256
- }
257
-
258
- isValid = true;
259
- break;
260
- }
261
- }
262
-
263
- // Return the actual error if only one inclusion defined
264
- if (jl === 1) {
265
- if (stripUnknown) {
266
- internals.fastSplice(items, i);
267
- --i;
268
- --il;
269
- isValid = true;
270
- break;
271
- }
272
-
273
- errors.push(this.createError(wasArray ? 'array.includesOne' : 'array.includesOneSingle', { pos: i, reason: res.errors, value: item }, { key: state.key, path: localState.path }, options));
274
- errored = true;
275
-
276
- if (options.abortEarly) {
277
- return errors;
278
- }
279
-
280
- break;
281
- }
282
- }
283
-
284
- if (errored) {
285
- continue;
286
- }
287
-
288
- if (this._inner.inclusions.length && !isValid) {
289
- if (stripUnknown) {
290
- internals.fastSplice(items, i);
291
- --i;
292
- --il;
293
- continue;
294
- }
295
-
296
- errors.push(this.createError(wasArray ? 'array.includes' : 'array.includesSingle', { pos: i, value: item }, { key: state.key, path: localState.path }, options));
297
-
298
- if (options.abortEarly) {
299
- return errors;
300
- }
301
- }
302
- }
303
-
304
- if (requireds.length) {
305
- this._fillMissedErrors(errors, requireds, state, options);
306
- }
307
-
308
- if (ordereds.length) {
309
- this._fillOrderedErrors(errors, ordereds, state, options);
310
- }
311
-
312
- return errors.length ? errors : null;
313
- }
314
-
315
- describe() {
316
-
317
- const description = super.describe();
318
-
319
- if (this._inner.ordereds.length) {
320
- description.orderedItems = [];
321
-
322
- for (let i = 0; i < this._inner.ordereds.length; ++i) {
323
- description.orderedItems.push(this._inner.ordereds[i].describe());
324
- }
325
- }
326
-
327
- if (this._inner.items.length) {
328
- description.items = [];
329
-
330
- for (let i = 0; i < this._inner.items.length; ++i) {
331
- description.items.push(this._inner.items[i].describe());
332
- }
333
- }
334
-
335
- if (description.rules) {
336
- for (let i = 0; i < description.rules.length; ++i) {
337
- const rule = description.rules[i];
338
- if (rule.name === 'has') {
339
- rule.arg = rule.arg.describe();
340
- }
341
- }
342
- }
343
-
344
- return description;
345
- }
346
-
347
- items(...schemas) {
348
-
349
- const obj = this.clone();
350
-
351
- Hoek.flatten(schemas).forEach((type, index) => {
352
-
353
- try {
354
- type = Cast.schema(this._currentJoi, type);
355
- }
356
- catch (castErr) {
357
- if (castErr.hasOwnProperty('path')) {
358
- castErr.path = index + '.' + castErr.path;
359
- }
360
- else {
361
- castErr.path = index;
362
- }
363
-
364
- castErr.message = `${castErr.message}(${castErr.path})`;
365
- throw castErr;
366
- }
367
-
368
- obj._inner.items.push(type);
369
-
370
- if (type._flags.presence === 'required') {
371
- obj._inner.requireds.push(type);
372
- }
373
- else if (type._flags.presence === 'forbidden') {
374
- obj._inner.exclusions.push(type.optional());
375
- }
376
- else {
377
- obj._inner.inclusions.push(type);
378
- }
379
- });
380
-
381
- return obj;
382
- }
383
-
384
- ordered(...schemas) {
385
-
386
- const obj = this.clone();
387
-
388
- Hoek.flatten(schemas).forEach((type, index) => {
389
-
390
- try {
391
- type = Cast.schema(this._currentJoi, type);
392
- }
393
- catch (castErr) {
394
- if (castErr.hasOwnProperty('path')) {
395
- castErr.path = index + '.' + castErr.path;
396
- }
397
- else {
398
- castErr.path = index;
399
- }
400
-
401
- castErr.message = `${castErr.message}(${castErr.path})`;
402
- throw castErr;
403
- }
404
-
405
- obj._inner.ordereds.push(type);
406
- });
407
-
408
- return obj;
409
- }
410
-
411
- min(limit) {
412
-
413
- const isRef = Ref.isRef(limit);
414
-
415
- Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');
416
-
417
- return this._testUnique('min', limit, function (value, state, options) {
418
-
419
- let compareTo;
420
- if (isRef) {
421
- compareTo = limit(state.reference || state.parent, options);
422
-
423
- if (!(Number.isSafeInteger(compareTo) && compareTo >= 0)) {
424
- return this.createError('array.ref', { ref: limit, value: compareTo }, state, options);
425
- }
426
- }
427
- else {
428
- compareTo = limit;
429
- }
430
-
431
- if (value.length >= compareTo) {
432
- return value;
433
- }
434
-
435
- return this.createError('array.min', { limit, value }, state, options);
436
- });
437
- }
438
-
439
- max(limit) {
440
-
441
- const isRef = Ref.isRef(limit);
442
-
443
- Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');
444
-
445
- return this._testUnique('max', limit, function (value, state, options) {
446
-
447
- let compareTo;
448
- if (isRef) {
449
- compareTo = limit(state.reference || state.parent, options);
450
-
451
- if (!(Number.isSafeInteger(compareTo) && compareTo >= 0)) {
452
- return this.createError('array.ref', { ref: limit.key }, state, options);
453
- }
454
- }
455
- else {
456
- compareTo = limit;
457
- }
458
-
459
- if (value.length <= compareTo) {
460
- return value;
461
- }
462
-
463
- return this.createError('array.max', { limit, value }, state, options);
464
- });
465
- }
466
-
467
- length(limit) {
468
-
469
- const isRef = Ref.isRef(limit);
470
-
471
- Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');
472
-
473
- return this._testUnique('length', limit, function (value, state, options) {
474
-
475
- let compareTo;
476
- if (isRef) {
477
- compareTo = limit(state.reference || state.parent, options);
478
-
479
- if (!(Number.isSafeInteger(compareTo) && compareTo >= 0)) {
480
- return this.createError('array.ref', { ref: limit.key }, state, options);
481
- }
482
- }
483
- else {
484
- compareTo = limit;
485
- }
486
-
487
- if (value.length === compareTo) {
488
- return value;
489
- }
490
-
491
- return this.createError('array.length', { limit, value }, state, options);
492
- });
493
- }
494
-
495
- has(schema) {
496
-
497
- try {
498
- schema = Cast.schema(this._currentJoi, schema);
499
- }
500
- catch (castErr) {
501
- if (castErr.hasOwnProperty('path')) {
502
- castErr.message = `${castErr.message}(${castErr.path})`;
503
- }
504
-
505
- throw castErr;
506
- }
507
-
508
- return this._test('has', schema, function (value, state, options) {
509
-
510
- const isValid = value.some((item, idx) => {
511
-
512
- const localState = new State(idx, [...state.path, idx], state.key, state.reference);
513
- return !schema._validate(item, localState, options).errors;
514
- });
515
-
516
- if (isValid) {
517
- return value;
518
- }
519
-
520
- const patternLabel = schema._getLabel();
521
- if (patternLabel) {
522
- return this.createError('array.hasKnown', { patternLabel }, state, options);
523
- }
524
-
525
- return this.createError('array.hasUnknown', null, state, options);
526
- });
527
- }
528
-
529
- unique(comparator, configs) {
530
-
531
- Hoek.assert(comparator === undefined ||
532
- typeof comparator === 'function' ||
533
- typeof comparator === 'string', 'comparator must be a function or a string');
534
-
535
- Hoek.assert(configs === undefined ||
536
- typeof configs === 'object', 'configs must be an object');
537
-
538
- const settings = {
539
- ignoreUndefined: (configs && configs.ignoreUndefined) || false
540
- };
541
-
542
-
543
- if (typeof comparator === 'string') {
544
- settings.path = comparator;
545
- }
546
- else if (typeof comparator === 'function') {
547
- settings.comparator = comparator;
548
- }
549
-
550
- return this._test('unique', settings, function (value, state, options) {
551
-
552
- const found = {
553
- string: Object.create(null),
554
- number: Object.create(null),
555
- undefined: Object.create(null),
556
- boolean: Object.create(null),
557
- object: new Map(),
558
- function: new Map(),
559
- custom: new Map()
560
- };
561
-
562
- const compare = settings.comparator || Hoek.deepEqual;
563
- const ignoreUndefined = settings.ignoreUndefined;
564
-
565
- for (let i = 0; i < value.length; ++i) {
566
- const item = settings.path ? Hoek.reach(value[i], settings.path) : value[i];
567
- const records = settings.comparator ? found.custom : found[typeof item];
568
-
569
- // All available types are supported, so it's not possible to reach 100% coverage without ignoring this line.
570
- // I still want to keep the test for future js versions with new types (eg. Symbol).
571
- if (/* $lab:coverage:off$ */ records /* $lab:coverage:on$ */) {
572
- if (records instanceof Map) {
573
- const entries = records.entries();
574
- let current;
575
- while (!(current = entries.next()).done) {
576
- if (compare(current.value[0], item)) {
577
- const localState = new State(state.key, [...state.path, i], state.parent, state.reference);
578
- const context = {
579
- pos: i,
580
- value: value[i],
581
- dupePos: current.value[1],
582
- dupeValue: value[current.value[1]]
583
- };
584
-
585
- if (settings.path) {
586
- context.path = settings.path;
587
- }
588
-
589
- return this.createError('array.unique', context, localState, options);
590
- }
591
- }
592
-
593
- records.set(item, i);
594
- }
595
- else {
596
- if ((!ignoreUndefined || item !== undefined) && records[item] !== undefined) {
597
- const localState = new State(state.key, [...state.path, i], state.parent, state.reference);
598
-
599
- const context = {
600
- pos: i,
601
- value: value[i],
602
- dupePos: records[item],
603
- dupeValue: value[records[item]]
604
- };
605
-
606
- if (settings.path) {
607
- context.path = settings.path;
608
- }
609
-
610
- return this.createError('array.unique', context, localState, options);
611
- }
612
-
613
- records[item] = i;
614
- }
615
- }
616
- }
617
-
618
- return value;
619
- });
620
- }
621
-
622
- sparse(enabled) {
623
-
624
- const value = enabled === undefined ? true : !!enabled;
625
-
626
- if (this._flags.sparse === value) {
627
- return this;
628
- }
629
-
630
- const obj = this.clone();
631
- obj._flags.sparse = value;
632
- return obj;
633
- }
634
-
635
- single(enabled) {
636
-
637
- const value = enabled === undefined ? true : !!enabled;
638
-
639
- if (this._flags.single === value) {
640
- return this;
641
- }
642
-
643
- const obj = this.clone();
644
- obj._flags.single = value;
645
- return obj;
646
- }
647
-
648
- _fillMissedErrors(errors, requireds, state, options) {
649
-
650
- const knownMisses = [];
651
- let unknownMisses = 0;
652
- for (let i = 0; i < requireds.length; ++i) {
653
- const label = requireds[i]._getLabel();
654
- if (label) {
655
- knownMisses.push(label);
656
- }
657
- else {
658
- ++unknownMisses;
659
- }
660
- }
661
-
662
- if (knownMisses.length) {
663
- if (unknownMisses) {
664
- errors.push(this.createError('array.includesRequiredBoth', { knownMisses, unknownMisses }, { key: state.key, path: state.path }, options));
665
- }
666
- else {
667
- errors.push(this.createError('array.includesRequiredKnowns', { knownMisses }, { key: state.key, path: state.path }, options));
668
- }
669
- }
670
- else {
671
- errors.push(this.createError('array.includesRequiredUnknowns', { unknownMisses }, { key: state.key, path: state.path }, options));
672
- }
673
- }
674
-
675
-
676
- _fillOrderedErrors(errors, ordereds, state, options) {
677
-
678
- const requiredOrdereds = [];
679
-
680
- for (let i = 0; i < ordereds.length; ++i) {
681
- const presence = Hoek.reach(ordereds[i], '_flags.presence');
682
- if (presence === 'required') {
683
- requiredOrdereds.push(ordereds[i]);
684
- }
685
- }
686
-
687
- if (requiredOrdereds.length) {
688
- this._fillMissedErrors(errors, requiredOrdereds, state, options);
689
- }
690
- }
691
-
692
- };
693
-
694
-
695
- internals.safeParse = function (value, result) {
696
-
697
- try {
698
- const converted = JSON.parse(value);
699
- if (Array.isArray(converted)) {
700
- result.value = converted;
701
- }
702
- }
703
- catch (e) { }
704
- };
705
-
706
-
707
- module.exports = new internals.Array();