joi 11.3.2 → 12.0.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/README.md +1 -1
- package/lib/errors.js +5 -1
- package/lib/index.js +19 -18
- package/lib/schemas.js +2 -1
- package/lib/types/alternatives/index.js +30 -15
- package/lib/types/any/index.js +9 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ Imagine you run facebook and you want visitors to sign up on the website with re
|
|
|
26
26
|
This is joi, joi allows you to create *blueprints* or *schemas* for JavaScript objects (an object that stores information) to ensure *validation* of key information.
|
|
27
27
|
|
|
28
28
|
# API
|
|
29
|
-
See the detailed [API Reference](https://github.com/hapijs/joi/blob/
|
|
29
|
+
See the detailed [API Reference](https://github.com/hapijs/joi/blob/v12.0.0/API.md).
|
|
30
30
|
|
|
31
31
|
# Example
|
|
32
32
|
|
package/lib/errors.js
CHANGED
|
@@ -88,6 +88,10 @@ exports.Err = class {
|
|
|
88
88
|
|
|
89
89
|
format = format || Hoek.reach(localized, this.type) || Hoek.reach(Language.errors, this.type);
|
|
90
90
|
|
|
91
|
+
if (format === undefined) {
|
|
92
|
+
return `Error code "${this.type}" is not defined, your custom type is missing the correct language definition`;
|
|
93
|
+
}
|
|
94
|
+
|
|
91
95
|
let wrapArrays = Hoek.reach(localized, 'messages.wrapArrays');
|
|
92
96
|
if (typeof wrapArrays !== 'boolean') {
|
|
93
97
|
wrapArrays = Language.errors.messages.wrapArrays;
|
|
@@ -116,7 +120,7 @@ exports.Err = class {
|
|
|
116
120
|
|
|
117
121
|
const value = Hoek.reach(this.context, name);
|
|
118
122
|
const normalized = internals.stringify(value, wrapArrays);
|
|
119
|
-
return (isSecure ? Hoek.escapeHtml(normalized) : normalized);
|
|
123
|
+
return (isSecure && this.options.escapeHtml ? Hoek.escapeHtml(normalized) : normalized);
|
|
120
124
|
});
|
|
121
125
|
}
|
|
122
126
|
|
package/lib/index.js
CHANGED
|
@@ -26,6 +26,8 @@ const internals = {
|
|
|
26
26
|
|
|
27
27
|
internals.applyDefaults = function (schema) {
|
|
28
28
|
|
|
29
|
+
Hoek.assert(this, 'Must be invoked on a Joi instance.');
|
|
30
|
+
|
|
29
31
|
if (this._defaults) {
|
|
30
32
|
schema = this._defaults(schema);
|
|
31
33
|
}
|
|
@@ -47,12 +49,12 @@ internals.root = function () {
|
|
|
47
49
|
|
|
48
50
|
Hoek.assert(arguments.length === 0, 'Joi.any() does not allow arguments.');
|
|
49
51
|
|
|
50
|
-
return internals.applyDefaults.call(this
|
|
52
|
+
return internals.applyDefaults.call(this, any);
|
|
51
53
|
};
|
|
52
54
|
|
|
53
55
|
root.alternatives = root.alt = function () {
|
|
54
56
|
|
|
55
|
-
const alternatives = internals.applyDefaults.call(this
|
|
57
|
+
const alternatives = internals.applyDefaults.call(this, internals.alternatives);
|
|
56
58
|
return arguments.length ? alternatives.try.apply(alternatives, arguments) : alternatives;
|
|
57
59
|
};
|
|
58
60
|
|
|
@@ -60,47 +62,47 @@ internals.root = function () {
|
|
|
60
62
|
|
|
61
63
|
Hoek.assert(arguments.length === 0, 'Joi.array() does not allow arguments.');
|
|
62
64
|
|
|
63
|
-
return internals.applyDefaults.call(this
|
|
65
|
+
return internals.applyDefaults.call(this, internals.array);
|
|
64
66
|
};
|
|
65
67
|
|
|
66
68
|
root.boolean = root.bool = function () {
|
|
67
69
|
|
|
68
70
|
Hoek.assert(arguments.length === 0, 'Joi.boolean() does not allow arguments.');
|
|
69
71
|
|
|
70
|
-
return internals.applyDefaults.call(this
|
|
72
|
+
return internals.applyDefaults.call(this, internals.boolean);
|
|
71
73
|
};
|
|
72
74
|
|
|
73
75
|
root.binary = function () {
|
|
74
76
|
|
|
75
77
|
Hoek.assert(arguments.length === 0, 'Joi.binary() does not allow arguments.');
|
|
76
78
|
|
|
77
|
-
return internals.applyDefaults.call(this
|
|
79
|
+
return internals.applyDefaults.call(this, internals.binary);
|
|
78
80
|
};
|
|
79
81
|
|
|
80
82
|
root.date = function () {
|
|
81
83
|
|
|
82
84
|
Hoek.assert(arguments.length === 0, 'Joi.date() does not allow arguments.');
|
|
83
85
|
|
|
84
|
-
return internals.applyDefaults.call(this
|
|
86
|
+
return internals.applyDefaults.call(this, internals.date);
|
|
85
87
|
};
|
|
86
88
|
|
|
87
89
|
root.func = function () {
|
|
88
90
|
|
|
89
91
|
Hoek.assert(arguments.length === 0, 'Joi.func() does not allow arguments.');
|
|
90
92
|
|
|
91
|
-
return internals.applyDefaults.call(this
|
|
93
|
+
return internals.applyDefaults.call(this, internals.func);
|
|
92
94
|
};
|
|
93
95
|
|
|
94
96
|
root.number = function () {
|
|
95
97
|
|
|
96
98
|
Hoek.assert(arguments.length === 0, 'Joi.number() does not allow arguments.');
|
|
97
99
|
|
|
98
|
-
return internals.applyDefaults.call(this
|
|
100
|
+
return internals.applyDefaults.call(this, internals.number);
|
|
99
101
|
};
|
|
100
102
|
|
|
101
103
|
root.object = function () {
|
|
102
104
|
|
|
103
|
-
const object = internals.applyDefaults.call(this
|
|
105
|
+
const object = internals.applyDefaults.call(this, internals.object);
|
|
104
106
|
return arguments.length ? object.keys.apply(object, arguments) : object;
|
|
105
107
|
};
|
|
106
108
|
|
|
@@ -108,7 +110,7 @@ internals.root = function () {
|
|
|
108
110
|
|
|
109
111
|
Hoek.assert(arguments.length === 0, 'Joi.string() does not allow arguments.');
|
|
110
112
|
|
|
111
|
-
return internals.applyDefaults.call(this
|
|
113
|
+
return internals.applyDefaults.call(this, internals.string);
|
|
112
114
|
};
|
|
113
115
|
|
|
114
116
|
root.ref = function () {
|
|
@@ -246,11 +248,10 @@ internals.root = function () {
|
|
|
246
248
|
const extensions = Hoek.flatten(Array.prototype.slice.call(arguments));
|
|
247
249
|
Hoek.assert(extensions.length > 0, 'You need to provide at least one extension');
|
|
248
250
|
|
|
249
|
-
|
|
250
|
-
currentJoi.assert(extensions, root.extensionsSchema);
|
|
251
|
+
this.assert(extensions, root.extensionsSchema);
|
|
251
252
|
|
|
252
|
-
const joi = Object.create(
|
|
253
|
-
Object.assign(joi,
|
|
253
|
+
const joi = Object.create(this.any());
|
|
254
|
+
Object.assign(joi, this);
|
|
254
255
|
|
|
255
256
|
for (let i = 0; i < extensions.length; ++i) {
|
|
256
257
|
let extension = extensions[i];
|
|
@@ -259,9 +260,9 @@ internals.root = function () {
|
|
|
259
260
|
extension = extension(joi);
|
|
260
261
|
}
|
|
261
262
|
|
|
262
|
-
|
|
263
|
+
this.assert(extension, root.extensionSchema);
|
|
263
264
|
|
|
264
|
-
const base = (extension.base ||
|
|
265
|
+
const base = (extension.base || this.any()).clone(); // Cloning because we're going to override language afterwards
|
|
265
266
|
const ctor = base.constructor;
|
|
266
267
|
const type = class extends ctor { // eslint-disable-line no-loop-func
|
|
267
268
|
|
|
@@ -333,7 +334,7 @@ internals.root = function () {
|
|
|
333
334
|
const ruleArgs = rule.params ?
|
|
334
335
|
(rule.params instanceof Any ? rule.params._inner.children.map((k) => k.key) : Object.keys(rule.params)) :
|
|
335
336
|
[];
|
|
336
|
-
const validateArgs = rule.params ? Cast.schema(
|
|
337
|
+
const validateArgs = rule.params ? Cast.schema(this, rule.params) : null;
|
|
337
338
|
|
|
338
339
|
type.prototype[rule.name] = function () { // eslint-disable-line no-loop-func
|
|
339
340
|
|
|
@@ -396,7 +397,7 @@ internals.root = function () {
|
|
|
396
397
|
const instance = new type();
|
|
397
398
|
joi[extension.name] = function () {
|
|
398
399
|
|
|
399
|
-
return internals.applyDefaults.call(this
|
|
400
|
+
return internals.applyDefaults.call(this, instance);
|
|
400
401
|
};
|
|
401
402
|
}
|
|
402
403
|
|
package/lib/schemas.js
CHANGED
|
@@ -31,9 +31,10 @@ internals.Alternatives = class extends Any {
|
|
|
31
31
|
|
|
32
32
|
for (let i = 0; i < il; ++i) {
|
|
33
33
|
const item = this._inner.matches[i];
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
34
|
+
if (!item.schema) {
|
|
35
|
+
const schema = item.peek || item.is;
|
|
36
|
+
const input = item.is ? item.ref(state.reference || state.parent, options) : value;
|
|
37
|
+
const failed = schema._validate(input, null, options, state.parent).errors;
|
|
37
38
|
|
|
38
39
|
if (failed) {
|
|
39
40
|
if (item.otherwise) {
|
|
@@ -51,7 +52,7 @@ internals.Alternatives = class extends Any {
|
|
|
51
52
|
continue;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
const result = schema._validate(value, state, options);
|
|
55
|
+
const result = item.schema._validate(value, state, options);
|
|
55
56
|
if (!result.errors) { // Found a valid match
|
|
56
57
|
return result;
|
|
57
58
|
}
|
|
@@ -84,25 +85,35 @@ internals.Alternatives = class extends Any {
|
|
|
84
85
|
return obj;
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
when(
|
|
88
|
+
when(condition, options) {
|
|
88
89
|
|
|
89
|
-
|
|
90
|
+
let schemaCondition = false;
|
|
91
|
+
Hoek.assert(Ref.isRef(condition) || typeof condition === 'string' || (schemaCondition = condition instanceof Any), 'Invalid condition:', condition);
|
|
90
92
|
Hoek.assert(options, 'Missing options');
|
|
91
93
|
Hoek.assert(typeof options === 'object', 'Invalid options');
|
|
92
|
-
|
|
94
|
+
if (schemaCondition) {
|
|
95
|
+
Hoek.assert(!options.hasOwnProperty('is'), '"is" can not be used with a schema condition');
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
Hoek.assert(options.hasOwnProperty('is'), 'Missing "is" directive');
|
|
99
|
+
}
|
|
93
100
|
Hoek.assert(options.then !== undefined || options.otherwise !== undefined, 'options must have at least one of "then" or "otherwise"');
|
|
94
101
|
|
|
95
102
|
const obj = this.clone();
|
|
96
|
-
let is
|
|
103
|
+
let is;
|
|
104
|
+
if (!schemaCondition) {
|
|
105
|
+
is = Cast.schema(this._currentJoi, options.is);
|
|
97
106
|
|
|
98
|
-
|
|
107
|
+
if (options.is === null || !(Ref.isRef(options.is) || options.is instanceof Any)) {
|
|
99
108
|
|
|
100
|
-
|
|
101
|
-
|
|
109
|
+
// Only apply required if this wasn't already a schema or a ref, we'll suppose people know what they're doing
|
|
110
|
+
is = is.required();
|
|
111
|
+
}
|
|
102
112
|
}
|
|
103
113
|
|
|
104
114
|
const item = {
|
|
105
|
-
ref: Cast.ref(
|
|
115
|
+
ref: schemaCondition ? null : Cast.ref(condition),
|
|
116
|
+
peek: schemaCondition ? condition : null,
|
|
106
117
|
is,
|
|
107
118
|
then: options.then !== undefined ? Cast.schema(this._currentJoi, options.then) : undefined,
|
|
108
119
|
otherwise: options.otherwise !== undefined ? Cast.schema(this._currentJoi, options.otherwise) : undefined
|
|
@@ -114,8 +125,10 @@ internals.Alternatives = class extends Any {
|
|
|
114
125
|
item.otherwise = item.otherwise && obj._baseType.concat(item.otherwise);
|
|
115
126
|
}
|
|
116
127
|
|
|
117
|
-
|
|
118
|
-
|
|
128
|
+
if (!schemaCondition) {
|
|
129
|
+
Ref.push(obj._refs, item.ref);
|
|
130
|
+
obj._refs = obj._refs.concat(item.is._refs);
|
|
131
|
+
}
|
|
119
132
|
|
|
120
133
|
if (item.then && item.then._refs) {
|
|
121
134
|
obj._refs = obj._refs.concat(item.then._refs);
|
|
@@ -146,9 +159,11 @@ internals.Alternatives = class extends Any {
|
|
|
146
159
|
|
|
147
160
|
// when()
|
|
148
161
|
|
|
149
|
-
const when = {
|
|
162
|
+
const when = item.is ? {
|
|
150
163
|
ref: item.ref.toString(),
|
|
151
164
|
is: item.is.describe()
|
|
165
|
+
} : {
|
|
166
|
+
peek: item.peek.describe()
|
|
152
167
|
};
|
|
153
168
|
|
|
154
169
|
if (item.then) {
|
package/lib/types/any/index.js
CHANGED
|
@@ -25,7 +25,8 @@ internals.defaults = {
|
|
|
25
25
|
language: {},
|
|
26
26
|
presence: 'optional',
|
|
27
27
|
strip: false,
|
|
28
|
-
noDefaults: false
|
|
28
|
+
noDefaults: false,
|
|
29
|
+
escapeHtml: false
|
|
29
30
|
|
|
30
31
|
// context: null
|
|
31
32
|
};
|
|
@@ -385,7 +386,7 @@ module.exports = internals.Any = class {
|
|
|
385
386
|
return obj;
|
|
386
387
|
}
|
|
387
388
|
|
|
388
|
-
when(
|
|
389
|
+
when(condition, options) {
|
|
389
390
|
|
|
390
391
|
Hoek.assert(options && typeof options === 'object', 'Invalid options');
|
|
391
392
|
Hoek.assert(options.then !== undefined || options.otherwise !== undefined, 'options must have at least one of "then" or "otherwise"');
|
|
@@ -394,7 +395,12 @@ module.exports = internals.Any = class {
|
|
|
394
395
|
const otherwise = options.hasOwnProperty('otherwise') ? this.concat(Cast.schema(this._currentJoi, options.otherwise)) : undefined;
|
|
395
396
|
|
|
396
397
|
Alternatives = Alternatives || require('../alternatives');
|
|
397
|
-
|
|
398
|
+
|
|
399
|
+
const alternativeOptions = { then, otherwise };
|
|
400
|
+
if (Object.prototype.hasOwnProperty.call(options, 'is')) {
|
|
401
|
+
alternativeOptions.is = options.is;
|
|
402
|
+
}
|
|
403
|
+
const obj = Alternatives.when(condition, alternativeOptions);
|
|
398
404
|
obj._flags.presence = 'ignore';
|
|
399
405
|
obj._baseType = this;
|
|
400
406
|
|