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
|
@@ -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();
|