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
package/lib/index.js CHANGED
@@ -1,450 +1,282 @@
1
1
  'use strict';
2
2
 
3
- // Load modules
3
+ const Assert = require('@hapi/hoek/lib/assert');
4
+ const Clone = require('@hapi/hoek/lib/clone');
4
5
 
5
- const Hoek = require('hoek');
6
- const Any = require('./types/any');
7
- const Cast = require('./cast');
6
+ const Cache = require('./cache');
7
+ const Common = require('./common');
8
+ const Compile = require('./compile');
8
9
  const Errors = require('./errors');
9
- const Lazy = require('./types/lazy');
10
+ const Extend = require('./extend');
11
+ const Manifest = require('./manifest');
10
12
  const Ref = require('./ref');
11
- const Settings = require('./types/any/settings');
13
+ const Template = require('./template');
14
+ const Trace = require('./trace');
12
15
 
16
+ let Schemas;
13
17
 
14
- // Declare internals
15
18
 
16
19
  const internals = {
17
- alternatives: require('./types/alternatives'),
18
- array: require('./types/array'),
19
- boolean: require('./types/boolean'),
20
- binary: require('./types/binary'),
21
- date: require('./types/date'),
22
- func: require('./types/func'),
23
- number: require('./types/number'),
24
- object: require('./types/object'),
25
- string: require('./types/string'),
26
- symbol: require('./types/symbol')
27
- };
28
-
29
- internals.callWithDefaults = function (schema, args) {
30
-
31
- Hoek.assert(this, 'Must be invoked on a Joi instance.');
32
-
33
- if (this._defaults) {
34
- schema = this._defaults(schema);
20
+ types: {
21
+ alternatives: require('./types/alternatives'),
22
+ any: require('./types/any'),
23
+ array: require('./types/array'),
24
+ boolean: require('./types/boolean'),
25
+ date: require('./types/date'),
26
+ function: require('./types/function'),
27
+ link: require('./types/link'),
28
+ number: require('./types/number'),
29
+ object: require('./types/object'),
30
+ string: require('./types/string'),
31
+ symbol: require('./types/symbol')
32
+ },
33
+ aliases: {
34
+ alt: 'alternatives',
35
+ bool: 'boolean',
36
+ func: 'function'
35
37
  }
36
-
37
- schema._currentJoi = this;
38
-
39
- return schema._init(...args);
40
38
  };
41
39
 
42
- internals.root = function () {
43
-
44
- const any = new Any();
45
-
46
- const root = any.clone();
47
- Any.prototype._currentJoi = root;
48
- root._currentJoi = root;
49
-
50
- root.any = function (...args) {
51
-
52
- Hoek.assert(args.length === 0, 'Joi.any() does not allow arguments.');
53
-
54
- return internals.callWithDefaults.call(this, any, args);
55
- };
56
-
57
- root.alternatives = root.alt = function (...args) {
58
40
 
59
- return internals.callWithDefaults.call(this, internals.alternatives, args);
60
- };
61
-
62
- root.array = function (...args) {
41
+ if (Buffer) { // $lab:coverage:ignore$
42
+ internals.types.binary = require('./types/binary');
43
+ }
63
44
 
64
- Hoek.assert(args.length === 0, 'Joi.array() does not allow arguments.');
65
-
66
- return internals.callWithDefaults.call(this, internals.array, args);
67
- };
68
45
 
69
- root.boolean = root.bool = function (...args) {
70
-
71
- Hoek.assert(args.length === 0, 'Joi.boolean() does not allow arguments.');
46
+ internals.root = function () {
72
47
 
73
- return internals.callWithDefaults.call(this, internals.boolean, args);
48
+ const root = {
49
+ _types: new Set(Object.keys(internals.types))
74
50
  };
75
51
 
76
- root.binary = function (...args) {
77
-
78
- Hoek.assert(args.length === 0, 'Joi.binary() does not allow arguments.');
52
+ // Types
79
53
 
80
- return internals.callWithDefaults.call(this, internals.binary, args);
81
- };
54
+ for (const type of root._types) {
55
+ root[type] = function (...args) {
82
56
 
83
- root.date = function (...args) {
57
+ Assert(!args.length || ['alternatives', 'link', 'object'].includes(type), 'The', type, 'type does not allow arguments');
58
+ return internals.generate(this, internals.types[type], args);
59
+ };
60
+ }
84
61
 
85
- Hoek.assert(args.length === 0, 'Joi.date() does not allow arguments.');
62
+ // Shortcuts
86
63
 
87
- return internals.callWithDefaults.call(this, internals.date, args);
88
- };
64
+ for (const method of ['allow', 'custom', 'disallow', 'equal', 'exist', 'forbidden', 'invalid', 'not', 'only', 'optional', 'options', 'prefs', 'preferences', 'required', 'strip', 'valid', 'when']) {
65
+ root[method] = function (...args) {
89
66
 
90
- root.func = function (...args) {
67
+ return this.any()[method](...args);
68
+ };
69
+ }
91
70
 
92
- Hoek.assert(args.length === 0, 'Joi.func() does not allow arguments.');
71
+ // Methods
93
72
 
94
- return internals.callWithDefaults.call(this, internals.func, args);
95
- };
73
+ Object.assign(root, internals.methods);
96
74
 
97
- root.number = function (...args) {
75
+ // Aliases
98
76
 
99
- Hoek.assert(args.length === 0, 'Joi.number() does not allow arguments.');
77
+ for (const alias in internals.aliases) {
78
+ const target = internals.aliases[alias];
79
+ root[alias] = root[target];
80
+ }
100
81
 
101
- return internals.callWithDefaults.call(this, internals.number, args);
102
- };
82
+ root.x = root.expression;
103
83
 
104
- root.object = function (...args) {
84
+ // Trace
105
85
 
106
- return internals.callWithDefaults.call(this, internals.object, args);
107
- };
86
+ if (Trace.setup) { // $lab:coverage:ignore$
87
+ Trace.setup(root);
88
+ }
108
89
 
109
- root.string = function (...args) {
90
+ return root;
91
+ };
110
92
 
111
- Hoek.assert(args.length === 0, 'Joi.string() does not allow arguments.');
112
93
 
113
- return internals.callWithDefaults.call(this, internals.string, args);
114
- };
94
+ internals.methods = {
115
95
 
116
- root.symbol = function (...args) {
96
+ ValidationError: Errors.ValidationError,
97
+ version: Common.version,
98
+ cache: Cache.provider,
117
99
 
118
- Hoek.assert(args.length === 0, 'Joi.symbol() does not allow arguments.');
100
+ assert(value, schema, ...args /* [message], [options] */) {
119
101
 
120
- return internals.callWithDefaults.call(this, internals.symbol, args);
121
- };
102
+ internals.assert(value, schema, true, args);
103
+ },
122
104
 
123
- root.ref = function (...args) {
105
+ attempt(value, schema, ...args /* [message], [options] */) {
124
106
 
125
- return Ref.create(...args);
126
- };
107
+ return internals.assert(value, schema, false, args);
108
+ },
127
109
 
128
- root.isRef = function (ref) {
110
+ build(desc) {
129
111
 
130
- return Ref.isRef(ref);
131
- };
112
+ Assert(typeof Manifest.build === 'function', 'Manifest functionality disabled');
113
+ return Manifest.build(this, desc);
114
+ },
132
115
 
133
- root.validate = function (value, ...args /*, [schema], [options], callback */) {
116
+ checkPreferences(prefs) {
134
117
 
135
- const last = args[args.length - 1];
136
- const callback = typeof last === 'function' ? last : null;
118
+ Common.checkPreferences(prefs);
119
+ },
137
120
 
138
- const count = args.length - (callback ? 1 : 0);
139
- if (count === 0) {
140
- return any.validate(value, callback);
141
- }
121
+ compile(schema, options) {
142
122
 
143
- const options = count === 2 ? args[1] : undefined;
144
- const schema = root.compile(args[0]);
123
+ return Compile.compile(this, schema, options);
124
+ },
145
125
 
146
- return schema._validateWithOptions(value, options, callback);
147
- };
126
+ defaults(modifier) {
148
127
 
149
- root.describe = function (...args) {
128
+ Assert(typeof modifier === 'function', 'modifier must be a function');
150
129
 
151
- const schema = args.length ? root.compile(args[0]) : any;
152
- return schema.describe();
153
- };
130
+ const joi = Object.assign({}, this);
131
+ for (const type of joi._types) {
132
+ const schema = modifier(joi[type]());
133
+ Assert(Common.isSchema(schema), 'modifier must return a valid schema object');
154
134
 
155
- root.compile = function (schema) {
135
+ joi[type] = function (...args) {
156
136
 
157
- try {
158
- return Cast.schema(this, schema);
159
- }
160
- catch (err) {
161
- if (err.hasOwnProperty('path')) {
162
- err.message = err.message + '(' + err.path + ')';
163
- }
164
-
165
- throw err;
137
+ return internals.generate(this, schema, args);
138
+ };
166
139
  }
167
- };
168
-
169
- root.assert = function (value, schema, message) {
170
-
171
- root.attempt(value, schema, message);
172
- };
173
-
174
- root.attempt = function (value, schema, message) {
175
-
176
- const result = root.validate(value, schema);
177
- const error = result.error;
178
- if (error) {
179
- if (!message) {
180
- if (typeof error.annotate === 'function') {
181
- error.message = error.annotate();
182
- }
183
-
184
- throw error;
185
- }
186
-
187
- if (!(message instanceof Error)) {
188
- if (typeof error.annotate === 'function') {
189
- error.message = `${message} ${error.annotate()}`;
190
- }
191
140
 
192
- throw error;
193
- }
141
+ return joi;
142
+ },
194
143
 
195
- throw message;
196
- }
144
+ expression(...args) {
197
145
 
198
- return result.value;
199
- };
146
+ return new Template(...args);
147
+ },
200
148
 
201
- root.reach = function (schema, path) {
149
+ extend(...extensions) {
202
150
 
203
- Hoek.assert(schema && schema instanceof Any, 'you must provide a joi schema');
204
- Hoek.assert(Array.isArray(path) || typeof path === 'string', 'path must be a string or an array of strings');
151
+ Common.verifyFlat(extensions, 'extend');
205
152
 
206
- const reach = (sourceSchema, schemaPath) => {
153
+ Schemas = Schemas || require('./schemas');
207
154
 
208
- if (!schemaPath.length) {
209
- return sourceSchema;
210
- }
155
+ Assert(extensions.length, 'You need to provide at least one extension');
156
+ this.assert(extensions, Schemas.extensions);
211
157
 
212
- const children = sourceSchema._inner.children;
213
- if (!children) {
214
- return;
215
- }
158
+ const joi = Object.assign({}, this);
159
+ joi._types = new Set(joi._types);
216
160
 
217
- const key = schemaPath.shift();
218
- for (let i = 0; i < children.length; ++i) {
219
- const child = children[i];
220
- if (child.key === key) {
221
- return reach(child.schema, schemaPath);
222
- }
161
+ for (let extension of extensions) {
162
+ if (typeof extension === 'function') {
163
+ extension = extension(joi);
223
164
  }
224
- };
225
165
 
226
- const schemaPath = typeof path === 'string' ? (path ? path.split('.') : []) : path.slice();
166
+ this.assert(extension, Schemas.extension);
227
167
 
228
- return reach(schema, schemaPath);
229
- };
230
-
231
- root.lazy = function (...args) {
232
-
233
- return internals.callWithDefaults.call(this, Lazy, args);
234
- };
168
+ const expanded = internals.expandExtension(extension, joi);
169
+ for (const item of expanded) {
170
+ Assert(joi[item.type] === undefined || joi._types.has(item.type), 'Cannot override name', item.type);
235
171
 
236
- root.defaults = function (fn) {
172
+ const base = item.base || this.any();
173
+ const schema = Extend.type(base, item);
237
174
 
238
- Hoek.assert(typeof fn === 'function', 'Defaults must be a function');
175
+ joi._types.add(item.type);
176
+ joi[item.type] = function (...args) {
239
177
 
240
- let joi = Object.create(this.any());
241
- joi = fn(joi);
242
-
243
- Hoek.assert(joi && joi instanceof this.constructor, 'defaults() must return a schema');
244
-
245
- Object.assign(joi, this, joi.clone()); // Re-add the types from `this` but also keep the settings from joi's potential new defaults
246
-
247
- joi._defaults = (schema) => {
248
-
249
- if (this._defaults) {
250
- schema = this._defaults(schema);
251
- Hoek.assert(schema instanceof this.constructor, 'defaults() must return a schema');
178
+ return internals.generate(this, schema, args);
179
+ };
252
180
  }
253
-
254
- schema = fn(schema);
255
- Hoek.assert(schema instanceof this.constructor, 'defaults() must return a schema');
256
- return schema;
257
- };
181
+ }
258
182
 
259
183
  return joi;
260
- };
184
+ },
261
185
 
262
- root.extend = function (...args) {
186
+ isError: Errors.ValidationError.isError,
187
+ isExpression: Template.isTemplate,
188
+ isRef: Ref.isRef,
189
+ isSchema: Common.isSchema,
263
190
 
264
- const extensions = Hoek.flatten(args);
265
- Hoek.assert(extensions.length > 0, 'You need to provide at least one extension');
191
+ in(...args) {
266
192
 
267
- this.assert(extensions, root.extensionsSchema);
193
+ return Ref.in(...args);
194
+ },
268
195
 
269
- const joi = Object.create(this.any());
270
- Object.assign(joi, this);
196
+ override: Common.symbols.override,
271
197
 
272
- for (let i = 0; i < extensions.length; ++i) {
273
- let extension = extensions[i];
198
+ ref(...args) {
274
199
 
275
- if (typeof extension === 'function') {
276
- extension = extension(joi);
277
- }
200
+ return Ref.create(...args);
201
+ },
278
202
 
279
- this.assert(extension, root.extensionSchema);
203
+ types() {
280
204
 
281
- const base = (extension.base || this.any()).clone(); // Cloning because we're going to override language afterwards
282
- const ctor = base.constructor;
283
- const type = class extends ctor { // eslint-disable-line no-loop-func
205
+ const types = {};
206
+ for (const type of this._types) {
207
+ types[type] = this[type]();
208
+ }
284
209
 
285
- constructor() {
210
+ for (const target in internals.aliases) {
211
+ types[target] = this[target]();
212
+ }
286
213
 
287
- super();
288
- if (extension.base) {
289
- Object.assign(this, base);
290
- }
214
+ return types;
215
+ }
216
+ };
291
217
 
292
- this._type = extension.name;
293
218
 
294
- if (extension.language) {
295
- this._settings = Settings.concat(this._settings, {
296
- language: {
297
- [extension.name]: extension.language
298
- }
299
- });
300
- }
301
- }
219
+ // Helpers
302
220
 
303
- };
221
+ internals.assert = function (value, schema, annotate, args /* [message], [options] */) {
304
222
 
305
- if (extension.coerce) {
306
- type.prototype._coerce = function (value, state, options) {
223
+ const message = args[0] instanceof Error || typeof args[0] === 'string' ? args[0] : null;
224
+ const options = message ? args[1] : args[0];
225
+ const result = schema.validate(value, Common.preferences({ errors: { stack: true } }, options || {}));
307
226
 
308
- if (ctor.prototype._coerce) {
309
- const baseRet = ctor.prototype._coerce.call(this, value, state, options);
227
+ let error = result.error;
228
+ if (!error) {
229
+ return result.value;
230
+ }
310
231
 
311
- if (baseRet.errors) {
312
- return baseRet;
313
- }
232
+ if (message instanceof Error) {
233
+ throw message;
234
+ }
314
235
 
315
- value = baseRet.value;
316
- }
236
+ const display = annotate && typeof error.annotate === 'function' ? error.annotate() : error.message;
317
237
 
318
- const ret = extension.coerce.call(this, value, state, options);
319
- if (ret instanceof Errors.Err) {
320
- return { value, errors: ret };
321
- }
238
+ if (error instanceof Errors.ValidationError === false) {
239
+ error = Clone(error);
240
+ }
322
241
 
323
- return { value: ret };
324
- };
325
- }
242
+ error.message = message ? `${message} ${display}` : display;
243
+ throw error;
244
+ };
326
245
 
327
- if (extension.pre) {
328
- type.prototype._base = function (value, state, options) {
329
246
 
330
- if (ctor.prototype._base) {
331
- const baseRet = ctor.prototype._base.call(this, value, state, options);
247
+ internals.generate = function (root, schema, args) {
332
248
 
333
- if (baseRet.errors) {
334
- return baseRet;
335
- }
249
+ Assert(root, 'Must be invoked on a Joi instance.');
336
250
 
337
- value = baseRet.value;
338
- }
251
+ schema.$_root = root;
339
252
 
340
- const ret = extension.pre.call(this, value, state, options);
341
- if (ret instanceof Errors.Err) {
342
- return { value, errors: ret };
343
- }
253
+ if (!schema._definition.args ||
254
+ !args.length) {
344
255
 
345
- return { value: ret };
346
- };
347
- }
256
+ return schema;
257
+ }
348
258
 
349
- if (extension.rules) {
350
- for (let j = 0; j < extension.rules.length; ++j) {
351
- const rule = extension.rules[j];
352
- const ruleArgs = rule.params ?
353
- (rule.params instanceof Any ? rule.params._inner.children.map((k) => k.key) : Object.keys(rule.params)) :
354
- [];
355
- const validateArgs = rule.params ? Cast.schema(this, rule.params) : null;
356
-
357
- type.prototype[rule.name] = function (...rArgs) { // eslint-disable-line no-loop-func
358
-
359
- if (rArgs.length > ruleArgs.length) {
360
- throw new Error('Unexpected number of arguments');
361
- }
362
-
363
- let hasRef = false;
364
- let arg = {};
365
-
366
- for (let k = 0; k < ruleArgs.length; ++k) {
367
- arg[ruleArgs[k]] = rArgs[k];
368
- if (!hasRef && Ref.isRef(rArgs[k])) {
369
- hasRef = true;
370
- }
371
- }
372
-
373
- if (validateArgs) {
374
- arg = joi.attempt(arg, validateArgs);
375
- }
376
-
377
- let schema;
378
- if (rule.validate) {
379
- const validate = function (value, state, options) {
380
-
381
- return rule.validate.call(this, arg, value, state, options);
382
- };
383
-
384
- schema = this._test(rule.name, arg, validate, {
385
- description: rule.description,
386
- hasRef
387
- });
388
- }
389
- else {
390
- schema = this.clone();
391
- }
392
-
393
- if (rule.setup) {
394
- const newSchema = rule.setup.call(schema, arg);
395
- if (newSchema !== undefined) {
396
- Hoek.assert(newSchema instanceof Any, `Setup of extension Joi.${this._type}().${rule.name}() must return undefined or a Joi object`);
397
- schema = newSchema;
398
- }
399
- }
400
-
401
- return schema;
402
- };
403
- }
404
- }
259
+ return schema._definition.args(schema, ...args);
260
+ };
405
261
 
406
- if (extension.describe) {
407
- type.prototype.describe = function () {
408
262
 
409
- const description = ctor.prototype.describe.call(this);
410
- return extension.describe.call(this, description);
411
- };
412
- }
263
+ internals.expandExtension = function (extension, joi) {
413
264
 
414
- const instance = new type();
415
- joi[extension.name] = function (...extArgs) {
265
+ if (typeof extension.type === 'string') {
266
+ return [extension];
267
+ }
416
268
 
417
- return internals.callWithDefaults.call(this, instance, extArgs);
418
- };
269
+ const extended = [];
270
+ for (const type of joi._types) {
271
+ if (extension.type.test(type)) {
272
+ const item = Object.assign({}, extension);
273
+ item.type = type;
274
+ item.base = joi[type]();
275
+ extended.push(item);
419
276
  }
277
+ }
420
278
 
421
- return joi;
422
- };
423
-
424
- root.extensionSchema = internals.object.keys({
425
- base: internals.object.type(Any, 'Joi object'),
426
- name: internals.string.required(),
427
- coerce: internals.func.arity(3),
428
- pre: internals.func.arity(3),
429
- language: internals.object,
430
- describe: internals.func.arity(1),
431
- rules: internals.array.items(internals.object.keys({
432
- name: internals.string.required(),
433
- setup: internals.func.arity(1),
434
- validate: internals.func.arity(4),
435
- params: [
436
- internals.object.pattern(/.*/, internals.object.type(Any, 'Joi object')),
437
- internals.object.type(internals.object.constructor, 'Joi object')
438
- ],
439
- description: [internals.string, internals.func.arity(1)]
440
- }).or('setup', 'validate'))
441
- }).strict();
442
-
443
- root.extensionsSchema = internals.array.items([internals.object, internals.func.arity(1)]).strict();
444
-
445
- root.version = require('../package.json').version;
446
-
447
- return root;
279
+ return extended;
448
280
  };
449
281
 
450
282