joi 8.4.2 → 9.0.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/.eslintignore +2 -0
- package/.github/CONTRIBUTING.md +14 -0
- package/.github/ISSUE_TEMPLATE.md +19 -0
- package/.npmignore +21 -0
- package/.travis.yml +8 -0
- package/API.md +1690 -0
- package/README.md +1 -1
- package/examples/conditionalRequire.js +52 -0
- package/examples/customMessage.js +31 -0
- package/examples/multipleWhen.js +26 -0
- package/examples/timestamps.js +22 -0
- package/generate-readme-toc.js +49 -0
- package/images/joi.png +0 -0
- package/images/validation.png +0 -0
- package/lib/any.js +74 -35
- package/lib/array.js +11 -44
- package/lib/boolean.js +9 -2
- package/lib/date.js +0 -3
- package/lib/errors.js +21 -22
- package/lib/index.js +142 -3
- package/lib/language.js +2 -6
- package/lib/object.js +36 -33
- package/lib/string.js +0 -17
- package/package.json +5 -5
- package/test/alternatives.js +490 -0
- package/test/any.js +1751 -0
- package/test/array.js +1049 -0
- package/test/binary.js +190 -0
- package/test/boolean.js +163 -0
- package/test/date.js +441 -0
- package/test/errors.js +512 -0
- package/test/function.js +224 -0
- package/test/helper.js +65 -0
- package/test/index.js +2450 -0
- package/test/number.js +836 -0
- package/test/object.js +1318 -0
- package/test/ref.js +470 -0
- package/test/string.js +3206 -0
- package/lib/lazy.js +0 -51
- package/lib/schemas.js +0 -25
package/lib/index.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
const Hoek = require('hoek');
|
|
6
6
|
const Any = require('./any');
|
|
7
7
|
const Cast = require('./cast');
|
|
8
|
-
const Lazy = require('./lazy');
|
|
9
8
|
const Ref = require('./ref');
|
|
10
9
|
|
|
11
10
|
|
|
@@ -173,13 +172,153 @@ internals.root = function () {
|
|
|
173
172
|
}
|
|
174
173
|
};
|
|
175
174
|
|
|
176
|
-
root.
|
|
175
|
+
root.extend = function () {
|
|
177
176
|
|
|
178
|
-
|
|
177
|
+
const extensions = Hoek.flatten(Array.prototype.slice.call(arguments));
|
|
178
|
+
Hoek.assert(extensions.length > 0, 'You need to provide at least one extension');
|
|
179
|
+
|
|
180
|
+
this.assert(extensions, internals.extensionsSchema);
|
|
181
|
+
|
|
182
|
+
const joi = Object.assign({}, this);
|
|
183
|
+
|
|
184
|
+
for (let i = 0; i < extensions.length; ++i) {
|
|
185
|
+
const extension = extensions[i];
|
|
186
|
+
const base = (extension.base || this.any()).clone(); // Cloning because we're going to override language afterwards
|
|
187
|
+
const ctor = base.constructor;
|
|
188
|
+
const type = function () { // eslint-disable-line no-loop-func
|
|
189
|
+
|
|
190
|
+
ctor.call(this);
|
|
191
|
+
|
|
192
|
+
if (extension.base) {
|
|
193
|
+
Object.assign(this, base);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
this._type = extension.name;
|
|
197
|
+
|
|
198
|
+
if (extension.language) {
|
|
199
|
+
this._settings = this._settings || { language: {} };
|
|
200
|
+
this._settings.language = Hoek.applyToDefaults(this._settings.language, {
|
|
201
|
+
[extension.name]: extension.language
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
Hoek.inherits(type, ctor);
|
|
206
|
+
|
|
207
|
+
if (extension.pre) {
|
|
208
|
+
type.prototype._base = function (value, state, options) {
|
|
209
|
+
|
|
210
|
+
if (ctor.prototype._base) {
|
|
211
|
+
const baseRet = ctor.prototype._base.call(this, value, state, options);
|
|
212
|
+
|
|
213
|
+
if (baseRet.errors) {
|
|
214
|
+
return baseRet;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
value = baseRet.value;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const ret = extension.pre.call(this, value, state, options);
|
|
221
|
+
if (ret && ret.isJoi) {
|
|
222
|
+
return { value, errors: ret };
|
|
223
|
+
}
|
|
224
|
+
else if (ret !== null) {
|
|
225
|
+
return { value: ret };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return { value };
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (extension.rules) {
|
|
233
|
+
for (let j = 0; j < extension.rules.length; ++j) {
|
|
234
|
+
const rule = extension.rules[j];
|
|
235
|
+
const ruleArgs = rule.params ?
|
|
236
|
+
(rule.params instanceof Any ? rule.params._inner.children.map((k) => k.key) : Object.keys(rule.params)) :
|
|
237
|
+
[];
|
|
238
|
+
const validateArgs = rule.params ? Cast.schema(rule.params) : null;
|
|
239
|
+
|
|
240
|
+
type.prototype[rule.name] = function () { // eslint-disable-line no-loop-func
|
|
241
|
+
|
|
242
|
+
if (arguments.length > ruleArgs.length) {
|
|
243
|
+
throw new Error('Unexpected number of arguments');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const args = Array.prototype.slice.call(arguments);
|
|
247
|
+
let hasRef = false;
|
|
248
|
+
const arg = {};
|
|
249
|
+
|
|
250
|
+
for (let k = 0; k < ruleArgs.length; ++k) {
|
|
251
|
+
arg[ruleArgs[k]] = args[k];
|
|
252
|
+
if (!hasRef && Ref.isRef(args[k])) {
|
|
253
|
+
hasRef = true;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (validateArgs) {
|
|
258
|
+
joi.assert(arg, validateArgs);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let schema;
|
|
262
|
+
if (rule.validate) {
|
|
263
|
+
const validate = rule.validate.bind(this, arg);
|
|
264
|
+
|
|
265
|
+
schema = this._test(rule.name, arg, validate, {
|
|
266
|
+
description: rule.description,
|
|
267
|
+
hasRef
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
schema = this.clone();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (rule.setup) {
|
|
275
|
+
rule.setup.call(schema, arg);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return schema;
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (extension.describe) {
|
|
284
|
+
type.prototype.describe = function () {
|
|
285
|
+
|
|
286
|
+
const description = ctor.prototype.describe.call(this);
|
|
287
|
+
return extension.describe.call(this, description);
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const instance = new type();
|
|
292
|
+
joi[extension.name] = function () {
|
|
293
|
+
|
|
294
|
+
return instance;
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return joi;
|
|
179
299
|
};
|
|
180
300
|
|
|
181
301
|
return root;
|
|
182
302
|
};
|
|
183
303
|
|
|
184
304
|
|
|
305
|
+
internals.extensionsSchema = internals.array.items(internals.object.keys({
|
|
306
|
+
base: internals.object.type(Any, 'Joi object'),
|
|
307
|
+
name: internals.string.required(),
|
|
308
|
+
pre: internals.object._func().arity(3),
|
|
309
|
+
language: internals.object,
|
|
310
|
+
describe: internals.object._func().arity(1),
|
|
311
|
+
rules: internals.array.items(internals.object.keys({
|
|
312
|
+
name: internals.string.required(),
|
|
313
|
+
setup: internals.object._func().arity(1),
|
|
314
|
+
validate: internals.object._func().arity(4),
|
|
315
|
+
params: [
|
|
316
|
+
internals.object.pattern(/.*/, internals.object.type(Any, 'Joi object')),
|
|
317
|
+
internals.object.type(internals.object.constructor, 'Joi object')
|
|
318
|
+
],
|
|
319
|
+
description: [internals.string, internals.object._func().arity(1)]
|
|
320
|
+
}).or('setup', 'validate'))
|
|
321
|
+
})).strict();
|
|
322
|
+
|
|
323
|
+
|
|
185
324
|
module.exports = internals.root();
|
package/lib/language.js
CHANGED
|
@@ -55,7 +55,6 @@ exports.errors = {
|
|
|
55
55
|
},
|
|
56
56
|
date: {
|
|
57
57
|
base: 'must be a number of milliseconds or valid date string',
|
|
58
|
-
strict: 'must be a valid date',
|
|
59
58
|
min: 'must be larger than or equal to "{{limit}}"',
|
|
60
59
|
max: 'must be less than or equal to "{{limit}}"',
|
|
61
60
|
isoDate: 'must be a valid ISO 8601 date',
|
|
@@ -69,11 +68,8 @@ exports.errors = {
|
|
|
69
68
|
base: 'must be a Function',
|
|
70
69
|
arity: 'must have an arity of {{n}}',
|
|
71
70
|
minArity: 'must have an arity greater or equal to {{n}}',
|
|
72
|
-
maxArity: 'must have an arity lesser or equal to {{n}}'
|
|
73
|
-
|
|
74
|
-
lazy: {
|
|
75
|
-
base: '!!schema error: lazy schema must be set',
|
|
76
|
-
schema: '!!schema error: lazy schema function must return a schema'
|
|
71
|
+
maxArity: 'must have an arity lesser or equal to {{n}}',
|
|
72
|
+
ref: 'must be a Joi reference'
|
|
77
73
|
},
|
|
78
74
|
object: {
|
|
79
75
|
base: 'must be an object',
|
package/lib/object.js
CHANGED
|
@@ -6,6 +6,7 @@ const Hoek = require('hoek');
|
|
|
6
6
|
const Topo = require('topo');
|
|
7
7
|
const Any = require('./any');
|
|
8
8
|
const Cast = require('./cast');
|
|
9
|
+
const Ref = require('./ref');
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
// Declare internals
|
|
@@ -25,15 +26,6 @@ internals.Object = function () {
|
|
|
25
26
|
|
|
26
27
|
Hoek.inherits(internals.Object, Any);
|
|
27
28
|
|
|
28
|
-
internals.safeParse = function (value) {
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
return JSON.parse(value);
|
|
32
|
-
}
|
|
33
|
-
catch (parseErr) {}
|
|
34
|
-
|
|
35
|
-
return value;
|
|
36
|
-
};
|
|
37
29
|
|
|
38
30
|
internals.Object.prototype._base = function (value, state, options) {
|
|
39
31
|
|
|
@@ -50,7 +42,10 @@ internals.Object.prototype._base = function (value, state, options) {
|
|
|
50
42
|
if (typeof value === 'string' &&
|
|
51
43
|
options.convert) {
|
|
52
44
|
|
|
53
|
-
|
|
45
|
+
try {
|
|
46
|
+
value = JSON.parse(value);
|
|
47
|
+
}
|
|
48
|
+
catch (parseErr) { }
|
|
54
49
|
}
|
|
55
50
|
|
|
56
51
|
const type = this._flags.func ? 'function' : 'object';
|
|
@@ -101,42 +96,42 @@ internals.Object.prototype._base = function (value, state, options) {
|
|
|
101
96
|
|
|
102
97
|
const renamed = {};
|
|
103
98
|
for (let i = 0; i < this._inner.renames.length; ++i) {
|
|
104
|
-
const
|
|
99
|
+
const item = this._inner.renames[i];
|
|
105
100
|
|
|
106
|
-
if (
|
|
101
|
+
if (item.options.ignoreUndefined && target[item.from] === undefined) {
|
|
107
102
|
continue;
|
|
108
103
|
}
|
|
109
104
|
|
|
110
|
-
if (!
|
|
111
|
-
renamed[
|
|
105
|
+
if (!item.options.multiple &&
|
|
106
|
+
renamed[item.to]) {
|
|
112
107
|
|
|
113
|
-
errors.push(this.createError('object.rename.multiple', { from:
|
|
108
|
+
errors.push(this.createError('object.rename.multiple', { from: item.from, to: item.to }, state, options));
|
|
114
109
|
if (options.abortEarly) {
|
|
115
110
|
return finish();
|
|
116
111
|
}
|
|
117
112
|
}
|
|
118
113
|
|
|
119
|
-
if (Object.prototype.hasOwnProperty.call(target,
|
|
120
|
-
!
|
|
121
|
-
!renamed[
|
|
114
|
+
if (Object.prototype.hasOwnProperty.call(target, item.to) &&
|
|
115
|
+
!item.options.override &&
|
|
116
|
+
!renamed[item.to]) {
|
|
122
117
|
|
|
123
|
-
errors.push(this.createError('object.rename.override', { from:
|
|
118
|
+
errors.push(this.createError('object.rename.override', { from: item.from, to: item.to }, state, options));
|
|
124
119
|
if (options.abortEarly) {
|
|
125
120
|
return finish();
|
|
126
121
|
}
|
|
127
122
|
}
|
|
128
123
|
|
|
129
|
-
if (target[
|
|
130
|
-
delete target[
|
|
124
|
+
if (target[item.from] === undefined) {
|
|
125
|
+
delete target[item.to];
|
|
131
126
|
}
|
|
132
127
|
else {
|
|
133
|
-
target[
|
|
128
|
+
target[item.to] = target[item.from];
|
|
134
129
|
}
|
|
135
130
|
|
|
136
|
-
renamed[
|
|
131
|
+
renamed[item.to] = true;
|
|
137
132
|
|
|
138
|
-
if (!
|
|
139
|
-
delete target[
|
|
133
|
+
if (!item.options.alias) {
|
|
134
|
+
delete target[item.from];
|
|
140
135
|
}
|
|
141
136
|
}
|
|
142
137
|
|
|
@@ -191,8 +186,6 @@ internals.Object.prototype._base = function (value, state, options) {
|
|
|
191
186
|
|
|
192
187
|
for (let i = 0; i < unprocessedKeys.length; ++i) {
|
|
193
188
|
const key = unprocessedKeys[i];
|
|
194
|
-
const localState = { key, path: (state.path ? state.path + '.' : '') + key, parent: target, reference: state.reference };
|
|
195
|
-
const item = target[key];
|
|
196
189
|
|
|
197
190
|
for (let j = 0; j < this._inner.patterns.length; ++j) {
|
|
198
191
|
const pattern = this._inner.patterns[j];
|
|
@@ -200,6 +193,8 @@ internals.Object.prototype._base = function (value, state, options) {
|
|
|
200
193
|
if (pattern.regex.test(key)) {
|
|
201
194
|
delete unprocessed[key];
|
|
202
195
|
|
|
196
|
+
const item = target[key];
|
|
197
|
+
const localState = { key, path: (state.path ? state.path + '.' : '') + key, parent: target, reference: state.reference };
|
|
203
198
|
const result = pattern.rule._validate(item, localState, options);
|
|
204
199
|
if (result.errors) {
|
|
205
200
|
errors.push(this.createError('object.child', { key, reason: result.errors }, localState, options));
|
|
@@ -223,15 +218,10 @@ internals.Object.prototype._base = function (value, state, options) {
|
|
|
223
218
|
if (options.stripUnknown ||
|
|
224
219
|
options.skipFunctions) {
|
|
225
220
|
|
|
226
|
-
const stripUnknown = options.stripUnknown
|
|
227
|
-
? (options.stripUnknown === true ? true : !!options.stripUnknown.objects)
|
|
228
|
-
: false;
|
|
229
|
-
|
|
230
|
-
|
|
231
221
|
for (let i = 0; i < unprocessedKeys.length; ++i) {
|
|
232
222
|
const key = unprocessedKeys[i];
|
|
233
223
|
|
|
234
|
-
if (stripUnknown) {
|
|
224
|
+
if (options.stripUnknown) {
|
|
235
225
|
delete target[key];
|
|
236
226
|
delete unprocessed[key];
|
|
237
227
|
}
|
|
@@ -823,4 +813,17 @@ internals.Object.prototype.type = function (constructor, name) {
|
|
|
823
813
|
};
|
|
824
814
|
|
|
825
815
|
|
|
816
|
+
internals.Object.prototype.ref = function () {
|
|
817
|
+
|
|
818
|
+
return this._test('ref', null, (value, state, options) => {
|
|
819
|
+
|
|
820
|
+
if (Ref.isRef(value)) {
|
|
821
|
+
return null;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
return this.createError('function.ref', null, state, options);
|
|
825
|
+
});
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
|
|
826
829
|
module.exports = new internals.Object();
|
package/lib/string.js
CHANGED
|
@@ -79,16 +79,6 @@ internals.String.prototype._base = function (value, state, options) {
|
|
|
79
79
|
value = value.replace(replacement.pattern, replacement.replacement);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
-
|
|
83
|
-
if (this._flags.truncate) {
|
|
84
|
-
for (let i = 0; i < this._tests.length; ++i) {
|
|
85
|
-
const test = this._tests[i];
|
|
86
|
-
if (test.name === 'max') {
|
|
87
|
-
value = value.slice(0, test.arg);
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
82
|
}
|
|
93
83
|
|
|
94
84
|
return {
|
|
@@ -481,11 +471,4 @@ internals.String.prototype.replace = function (pattern, replacement) {
|
|
|
481
471
|
return obj;
|
|
482
472
|
};
|
|
483
473
|
|
|
484
|
-
internals.String.prototype.truncate = function (enabled) {
|
|
485
|
-
|
|
486
|
-
const obj = this.clone();
|
|
487
|
-
obj._flags.truncate = enabled === undefined ? true : !!enabled;
|
|
488
|
-
return obj;
|
|
489
|
-
};
|
|
490
|
-
|
|
491
474
|
module.exports = new internals.String();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "joi",
|
|
3
3
|
"description": "Object schema validation",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "9.0.0-0",
|
|
5
5
|
"homepage": "https://github.com/hapijs/joi",
|
|
6
6
|
"repository": "git://github.com/hapijs/joi",
|
|
7
7
|
"main": "lib/index.js",
|
|
@@ -15,18 +15,18 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"hoek": "4.x.x",
|
|
18
|
-
"topo": "2.x.x",
|
|
19
18
|
"isemail": "2.x.x",
|
|
20
|
-
"
|
|
19
|
+
"items": "2.x.x",
|
|
20
|
+
"moment": "2.x.x",
|
|
21
|
+
"topo": "2.x.x"
|
|
21
22
|
},
|
|
22
23
|
"devDependencies": {
|
|
23
|
-
"code": "
|
|
24
|
+
"code": "2.x.x",
|
|
24
25
|
"lab": "10.x.x",
|
|
25
26
|
"markdown-toc": "0.12.x"
|
|
26
27
|
},
|
|
27
28
|
"scripts": {
|
|
28
29
|
"test": "lab -t 100 -a code -L",
|
|
29
|
-
"test-debug": "node $NODE_DEBUG_OPTION ./node_modules/.bin/lab -a code",
|
|
30
30
|
"test-cov-html": "lab -r html -o coverage.html -a code",
|
|
31
31
|
"toc": "node generate-readme-toc.js",
|
|
32
32
|
"version": "npm run toc && git add API.md README.md"
|