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.
- package/LICENSE.md +10 -0
- package/README.md +9 -118
- package/dist/joi-browser.min.js +1 -0
- package/lib/annotate.js +175 -0
- package/lib/base.js +1068 -0
- package/lib/cache.js +143 -0
- package/lib/common.js +216 -0
- package/lib/compile.js +283 -0
- package/lib/errors.js +160 -269
- package/lib/extend.js +312 -0
- package/lib/index.d.ts +2200 -0
- package/lib/index.js +179 -347
- package/lib/manifest.js +476 -0
- package/lib/messages.js +178 -0
- package/lib/modify.js +267 -0
- package/lib/ref.js +386 -25
- package/lib/schemas.js +291 -15
- package/lib/state.js +152 -0
- package/lib/template.js +427 -0
- package/lib/trace.js +346 -0
- package/lib/types/alternatives.js +329 -0
- package/lib/types/any.js +174 -0
- package/lib/types/array.js +775 -0
- package/lib/types/binary.js +98 -0
- package/lib/types/boolean.js +150 -0
- package/lib/types/date.js +233 -0
- package/lib/types/function.js +93 -0
- package/lib/types/keys.js +1043 -0
- package/lib/types/link.js +168 -0
- package/lib/types/number.js +335 -0
- package/lib/types/object.js +22 -0
- package/lib/types/string.js +820 -0
- package/lib/types/symbol.js +102 -0
- package/lib/validator.js +650 -0
- package/lib/values.js +263 -0
- package/package.json +34 -29
- package/CHANGELOG.md +0 -3
- package/LICENSE +0 -25
- package/lib/cast.js +0 -64
- package/lib/language.js +0 -166
- package/lib/set.js +0 -191
- package/lib/types/alternatives/index.js +0 -218
- package/lib/types/any/index.js +0 -978
- package/lib/types/any/settings.js +0 -36
- package/lib/types/array/index.js +0 -707
- package/lib/types/binary/index.js +0 -100
- package/lib/types/boolean/index.js +0 -100
- package/lib/types/date/index.js +0 -182
- package/lib/types/func/index.js +0 -90
- package/lib/types/lazy/index.js +0 -82
- package/lib/types/number/index.js +0 -248
- package/lib/types/object/index.js +0 -957
- package/lib/types/state.js +0 -11
- package/lib/types/string/index.js +0 -703
- package/lib/types/string/ip.js +0 -54
- package/lib/types/string/rfc3986.js +0 -219
- package/lib/types/string/uri.js +0 -46
- package/lib/types/symbol/index.js +0 -93
- package/lib/types/symbols.js +0 -5
package/lib/types/array/index.js
DELETED
|
@@ -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();
|