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/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.lazy = function (fn) {
175
+ root.extend = function () {
177
176
 
178
- return Lazy.set(fn);
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
- value = internals.safeParse(value);
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 rename = this._inner.renames[i];
99
+ const item = this._inner.renames[i];
105
100
 
106
- if (rename.options.ignoreUndefined && target[rename.from] === undefined) {
101
+ if (item.options.ignoreUndefined && target[item.from] === undefined) {
107
102
  continue;
108
103
  }
109
104
 
110
- if (!rename.options.multiple &&
111
- renamed[rename.to]) {
105
+ if (!item.options.multiple &&
106
+ renamed[item.to]) {
112
107
 
113
- errors.push(this.createError('object.rename.multiple', { from: rename.from, to: rename.to }, state, options));
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, rename.to) &&
120
- !rename.options.override &&
121
- !renamed[rename.to]) {
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: rename.from, to: rename.to }, state, options));
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[rename.from] === undefined) {
130
- delete target[rename.to];
124
+ if (target[item.from] === undefined) {
125
+ delete target[item.to];
131
126
  }
132
127
  else {
133
- target[rename.to] = target[rename.from];
128
+ target[item.to] = target[item.from];
134
129
  }
135
130
 
136
- renamed[rename.to] = true;
131
+ renamed[item.to] = true;
137
132
 
138
- if (!rename.options.alias) {
139
- delete target[rename.from];
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": "8.4.2",
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
- "moment": "2.x.x"
19
+ "items": "2.x.x",
20
+ "moment": "2.x.x",
21
+ "topo": "2.x.x"
21
22
  },
22
23
  "devDependencies": {
23
- "code": "3.x.x",
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"