joi 17.1.1 → 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/README.md +1 -5
- package/dist/joi-browser.min.js +1 -1
- package/lib/base.js +35 -0
- package/lib/common.js +5 -2
- package/lib/extend.js +3 -2
- package/lib/index.d.ts +2200 -0
- package/lib/manifest.js +1 -1
- package/lib/ref.js +8 -6
- package/lib/schemas.js +8 -1
- package/lib/template.js +26 -9
- package/lib/types/alternatives.js +1 -1
- package/lib/types/any.js +1 -1
- package/lib/types/array.js +4 -3
- package/lib/types/date.js +4 -4
- package/lib/types/keys.js +8 -7
- package/lib/types/number.js +7 -3
- package/lib/types/string.js +34 -10
- package/lib/validator.js +36 -3
- package/package.json +34 -33
package/lib/manifest.js
CHANGED
package/lib/ref.js
CHANGED
|
@@ -25,7 +25,7 @@ const internals = {
|
|
|
25
25
|
exports.create = function (key, options = {}) {
|
|
26
26
|
|
|
27
27
|
Assert(typeof key === 'string', 'Invalid reference key:', key);
|
|
28
|
-
Common.assertOptions(options, ['adjust', 'ancestor', 'in', 'iterables', 'map', 'prefix', 'separator']);
|
|
28
|
+
Common.assertOptions(options, ['adjust', 'ancestor', 'in', 'iterables', 'map', 'prefix', 'render', 'separator']);
|
|
29
29
|
Assert(!options.prefix || typeof options.prefix === 'object', 'options.prefix must be of type object');
|
|
30
30
|
|
|
31
31
|
const ref = Object.assign({}, internals.defaults, options);
|
|
@@ -77,7 +77,7 @@ exports.create = function (key, options = {}) {
|
|
|
77
77
|
|
|
78
78
|
exports.in = function (key, options = {}) {
|
|
79
79
|
|
|
80
|
-
return exports.create(key,
|
|
80
|
+
return exports.create(key, { ...options, in: true });
|
|
81
81
|
};
|
|
82
82
|
|
|
83
83
|
|
|
@@ -93,8 +93,8 @@ internals.Ref = class {
|
|
|
93
93
|
|
|
94
94
|
Assert(typeof options === 'object', 'Invalid reference construction');
|
|
95
95
|
Common.assertOptions(options, [
|
|
96
|
-
'adjust', 'ancestor', 'in', 'iterables', 'map', 'path', 'separator', 'type',
|
|
97
|
-
'depth', 'key', 'root', 'display'
|
|
96
|
+
'adjust', 'ancestor', 'in', 'iterables', 'map', 'path', 'render', 'separator', 'type', // Copied
|
|
97
|
+
'depth', 'key', 'root', 'display' // Overridden
|
|
98
98
|
]);
|
|
99
99
|
|
|
100
100
|
Assert([false, undefined].includes(options.separator) || typeof options.separator === 'string' && options.separator.length === 1, 'Invalid separator');
|
|
@@ -211,8 +211,10 @@ internals.Ref = class {
|
|
|
211
211
|
ref.map = [...this.map];
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
for (const key of ['adjust', 'iterables']) {
|
|
215
|
-
if (this[key] !== null
|
|
214
|
+
for (const key of ['adjust', 'iterables', 'render']) {
|
|
215
|
+
if (this[key] !== null &&
|
|
216
|
+
this[key] !== undefined) {
|
|
217
|
+
|
|
216
218
|
ref[key] = this[key];
|
|
217
219
|
}
|
|
218
220
|
}
|
package/lib/schemas.js
CHANGED
|
@@ -17,6 +17,7 @@ internals.wrap = Joi.string()
|
|
|
17
17
|
exports.preferences = Joi.object({
|
|
18
18
|
allowUnknown: Joi.boolean(),
|
|
19
19
|
abortEarly: Joi.boolean(),
|
|
20
|
+
artifacts: Joi.boolean(),
|
|
20
21
|
cache: Joi.boolean(),
|
|
21
22
|
context: Joi.object(),
|
|
22
23
|
convert: Joi.boolean(),
|
|
@@ -89,6 +90,10 @@ exports.extension = Joi.object({
|
|
|
89
90
|
])
|
|
90
91
|
.required(),
|
|
91
92
|
args: Joi.function(),
|
|
93
|
+
cast: Joi.object().pattern(internals.nameRx, Joi.object({
|
|
94
|
+
from: Joi.function().maxArity(1).required(),
|
|
95
|
+
to: Joi.function().minArity(1).maxArity(2).required()
|
|
96
|
+
})),
|
|
92
97
|
base: Joi.object().schema()
|
|
93
98
|
.when('type', { is: Joi.object().regex(), then: Joi.forbidden() }),
|
|
94
99
|
coerce: [
|
|
@@ -157,7 +162,8 @@ internals.desc = {
|
|
|
157
162
|
map: Joi.array().items(Joi.array().length(2)).min(1),
|
|
158
163
|
adjust: Joi.function(),
|
|
159
164
|
iterables: Joi.boolean(),
|
|
160
|
-
in: Joi.boolean()
|
|
165
|
+
in: Joi.boolean(),
|
|
166
|
+
render: Joi.boolean()
|
|
161
167
|
})
|
|
162
168
|
.required()
|
|
163
169
|
}),
|
|
@@ -244,6 +250,7 @@ exports.description = Joi.object({
|
|
|
244
250
|
preferences: {
|
|
245
251
|
allowUnknown: Joi.boolean(),
|
|
246
252
|
abortEarly: Joi.boolean(),
|
|
253
|
+
artifacts: Joi.boolean(),
|
|
247
254
|
cache: Joi.boolean(),
|
|
248
255
|
convert: Joi.boolean(),
|
|
249
256
|
dateFormat: ['date', 'iso', 'string', 'time', 'utc'],
|
package/lib/template.js
CHANGED
|
@@ -78,8 +78,13 @@ module.exports = exports = internals.Template = class {
|
|
|
78
78
|
continue;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
const
|
|
81
|
+
let variable = part.slice(raw ? 0 : 1, end);
|
|
82
|
+
const wrapped = variable[0] === ':';
|
|
83
|
+
if (wrapped) {
|
|
84
|
+
variable = variable.slice(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const dynamic = this._ref(internals.decode(variable), { raw, wrapped });
|
|
83
88
|
processed.push(dynamic);
|
|
84
89
|
if (typeof dynamic !== 'string') {
|
|
85
90
|
refs = true;
|
|
@@ -184,11 +189,10 @@ module.exports = exports = internals.Template = class {
|
|
|
184
189
|
}
|
|
185
190
|
else {
|
|
186
191
|
const rendered = this._part(part, /* context -> [*/ value, state, prefs, local, options /*] */);
|
|
187
|
-
const string = internals.stringify(rendered, prefs, options
|
|
192
|
+
const string = internals.stringify(rendered, value, state, prefs, local, options);
|
|
188
193
|
if (string !== undefined) {
|
|
189
194
|
const result = part.raw || (options.errors && options.errors.escapeHtml) === false ? string : EscapeHtml(string);
|
|
190
|
-
|
|
191
|
-
parts.push(internals.wrap(result, ends));
|
|
195
|
+
parts.push(internals.wrap(result, part.wrapped && prefs.errors.wrap.label));
|
|
192
196
|
}
|
|
193
197
|
}
|
|
194
198
|
}
|
|
@@ -196,7 +200,7 @@ module.exports = exports = internals.Template = class {
|
|
|
196
200
|
return parts.join('');
|
|
197
201
|
}
|
|
198
202
|
|
|
199
|
-
_ref(content, raw) {
|
|
203
|
+
_ref(content, { raw, wrapped }) {
|
|
200
204
|
|
|
201
205
|
const refs = [];
|
|
202
206
|
const reference = (variable) => {
|
|
@@ -216,7 +220,8 @@ module.exports = exports = internals.Template = class {
|
|
|
216
220
|
|
|
217
221
|
if (formula.single) {
|
|
218
222
|
if (formula.single.type === 'reference') {
|
|
219
|
-
|
|
223
|
+
const ref = refs[0];
|
|
224
|
+
return { ref, raw, refs, wrapped: wrapped || ref.type === 'local' && ref.key === 'label' };
|
|
220
225
|
}
|
|
221
226
|
|
|
222
227
|
return internals.stringify(formula.single.value);
|
|
@@ -302,10 +307,18 @@ internals.wrap = function (value, ends) {
|
|
|
302
307
|
};
|
|
303
308
|
|
|
304
309
|
|
|
305
|
-
internals.stringify = function (value, prefs, options) {
|
|
310
|
+
internals.stringify = function (value, original, state, prefs, local, options) {
|
|
306
311
|
|
|
307
312
|
const type = typeof value;
|
|
308
313
|
|
|
314
|
+
let skipWrap = false;
|
|
315
|
+
if (Ref.isRef(value) &&
|
|
316
|
+
value.render) {
|
|
317
|
+
|
|
318
|
+
skipWrap = value.in;
|
|
319
|
+
value = value.resolve(original, state, prefs, local, { in: value.in, ...options });
|
|
320
|
+
}
|
|
321
|
+
|
|
309
322
|
if (value === null) {
|
|
310
323
|
return 'null';
|
|
311
324
|
}
|
|
@@ -344,7 +357,11 @@ internals.stringify = function (value, prefs, options) {
|
|
|
344
357
|
|
|
345
358
|
let partial = '';
|
|
346
359
|
for (const item of value) {
|
|
347
|
-
partial = partial + (partial.length ? ', ' : '') + internals.stringify(item, prefs, options);
|
|
360
|
+
partial = partial + (partial.length ? ', ' : '') + internals.stringify(item, original, state, prefs, local, options);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (skipWrap) {
|
|
364
|
+
return partial;
|
|
348
365
|
}
|
|
349
366
|
|
|
350
367
|
return internals.wrap(partial, prefs.errors.wrap.array);
|
|
@@ -183,7 +183,7 @@ module.exports = Any.extend({
|
|
|
183
183
|
|
|
184
184
|
label(name) {
|
|
185
185
|
|
|
186
|
-
const obj = this.$
|
|
186
|
+
const obj = this.$_parent('label', name);
|
|
187
187
|
const each = (item, source) => (source.path[0] !== 'is' ? item.label(name) : undefined);
|
|
188
188
|
return obj.$_modify({ each, ref: false });
|
|
189
189
|
}
|
package/lib/types/any.js
CHANGED
|
@@ -167,7 +167,7 @@ module.exports = Base.extend({
|
|
|
167
167
|
'any.failover': '{{#label}} threw an error when running failover method',
|
|
168
168
|
'any.invalid': '{{#label}} contains an invalid value',
|
|
169
169
|
'any.only': '{{#label}} must be {if(#valids.length == 1, "", "one of ")}{{#valids}}',
|
|
170
|
-
'any.ref': '{{#label}} {{#arg}} references
|
|
170
|
+
'any.ref': '{{#label}} {{#arg}} references {{:#ref}} which {{#reason}}',
|
|
171
171
|
'any.required': '{{#label}} is required',
|
|
172
172
|
'any.unknown': '{{#label}} is not allowed'
|
|
173
173
|
}
|
package/lib/types/array.js
CHANGED
|
@@ -114,7 +114,7 @@ module.exports = Any.extend({
|
|
|
114
114
|
|
|
115
115
|
return obj.$_mutateRebuild();
|
|
116
116
|
},
|
|
117
|
-
validate(value, { schema, error, state, prefs }) {
|
|
117
|
+
validate(value, { schema, error, state, prefs, errorsArray }) {
|
|
118
118
|
|
|
119
119
|
const requireds = schema.$_terms._requireds.slice();
|
|
120
120
|
const ordereds = schema.$_terms.ordered.slice();
|
|
@@ -123,7 +123,8 @@ module.exports = Any.extend({
|
|
|
123
123
|
const wasArray = !value[Common.symbols.arraySingle];
|
|
124
124
|
delete value[Common.symbols.arraySingle];
|
|
125
125
|
|
|
126
|
-
const errors =
|
|
126
|
+
const errors = errorsArray();
|
|
127
|
+
|
|
127
128
|
let il = value.length;
|
|
128
129
|
for (let i = 0; i < il; ++i) {
|
|
129
130
|
const item = value[i];
|
|
@@ -611,7 +612,7 @@ module.exports = Any.extend({
|
|
|
611
612
|
messages: {
|
|
612
613
|
'array.base': '{{#label}} must be an array',
|
|
613
614
|
'array.excludes': '{{#label}} contains an excluded value',
|
|
614
|
-
'array.hasKnown': '{{#label}} does not contain at least one required match for type
|
|
615
|
+
'array.hasKnown': '{{#label}} does not contain at least one required match for type {:#patternLabel}',
|
|
615
616
|
'array.hasUnknown': '{{#label}} does not contain at least one required match',
|
|
616
617
|
'array.includes': '{{#label}} does not match any of the allowed types',
|
|
617
618
|
'array.includesRequiredBoth': '{{#label}} does not contain {{#knownMisses}} and {{#unknownMisses}} other required value(s)',
|
package/lib/types/date.js
CHANGED
|
@@ -149,10 +149,10 @@ module.exports = Any.extend({
|
|
|
149
149
|
messages: {
|
|
150
150
|
'date.base': '{{#label}} must be a valid date',
|
|
151
151
|
'date.format': '{{#label}} must be in {msg("date.format." + #format) || #format} format',
|
|
152
|
-
'date.greater': '{{#label}} must be greater than
|
|
153
|
-
'date.less': '{{#label}} must be less than
|
|
154
|
-
'date.max': '{{#label}} must be less than or equal to
|
|
155
|
-
'date.min': '{{#label}} must be
|
|
152
|
+
'date.greater': '{{#label}} must be greater than {{:#limit}}',
|
|
153
|
+
'date.less': '{{#label}} must be less than {{:#limit}}',
|
|
154
|
+
'date.max': '{{#label}} must be less than or equal to {{:#limit}}',
|
|
155
|
+
'date.min': '{{#label}} must be greater than or equal to {{:#limit}}',
|
|
156
156
|
|
|
157
157
|
// Messages used in date.format
|
|
158
158
|
|
package/lib/types/keys.js
CHANGED
|
@@ -27,6 +27,7 @@ module.exports = Any.extend({
|
|
|
27
27
|
type: '_keys',
|
|
28
28
|
|
|
29
29
|
properties: {
|
|
30
|
+
|
|
30
31
|
typeof: 'object'
|
|
31
32
|
},
|
|
32
33
|
|
|
@@ -478,7 +479,7 @@ module.exports = Any.extend({
|
|
|
478
479
|
value = Common.symbols.deepDefault;
|
|
479
480
|
}
|
|
480
481
|
|
|
481
|
-
return this.$
|
|
482
|
+
return this.$_parent('default', value, options);
|
|
482
483
|
}
|
|
483
484
|
},
|
|
484
485
|
|
|
@@ -528,22 +529,22 @@ module.exports = Any.extend({
|
|
|
528
529
|
'object.and': '{{#label}} contains {{#presentWithLabels}} without its required peers {{#missingWithLabels}}',
|
|
529
530
|
'object.assert': '{{#label}} is invalid because {if(#subject.key, `"` + #subject.key + `" failed to ` + (#message || "pass the assertion test"), #message || "the assertion failed")}',
|
|
530
531
|
'object.base': '{{#label}} must be of type {{#type}}',
|
|
531
|
-
'object.instance': '{{#label}} must be an instance of
|
|
532
|
+
'object.instance': '{{#label}} must be an instance of {{:#type}}',
|
|
532
533
|
'object.length': '{{#label}} must have {{#limit}} key{if(#limit == 1, "", "s")}',
|
|
533
534
|
'object.max': '{{#label}} must have less than or equal to {{#limit}} key{if(#limit == 1, "", "s")}',
|
|
534
535
|
'object.min': '{{#label}} must have at least {{#limit}} key{if(#limit == 1, "", "s")}',
|
|
535
536
|
'object.missing': '{{#label}} must contain at least one of {{#peersWithLabels}}',
|
|
536
|
-
'object.nand': '
|
|
537
|
+
'object.nand': '{{:#mainWithLabel}} must not exist simultaneously with {{#peersWithLabels}}',
|
|
537
538
|
'object.oxor': '{{#label}} contains a conflict between optional exclusive peers {{#peersWithLabels}}',
|
|
538
539
|
'object.pattern.match': '{{#label}} keys failed to match pattern requirements',
|
|
539
540
|
'object.refType': '{{#label}} must be a Joi reference',
|
|
540
541
|
'object.regex': '{{#label}} must be a RegExp object',
|
|
541
|
-
'object.rename.multiple': '{{#label}} cannot rename
|
|
542
|
-
'object.rename.override': '{{#label}} cannot rename
|
|
542
|
+
'object.rename.multiple': '{{#label}} cannot rename {{:#from}} because multiple renames are disabled and another key was already renamed to {{:#to}}',
|
|
543
|
+
'object.rename.override': '{{#label}} cannot rename {{:#from}} because override is disabled and target {{:#to}} exists',
|
|
543
544
|
'object.schema': '{{#label}} must be a Joi schema of {{#type}} type',
|
|
544
545
|
'object.unknown': '{{#label}} is not allowed',
|
|
545
|
-
'object.with': '
|
|
546
|
-
'object.without': '
|
|
546
|
+
'object.with': '{{:#mainWithLabel}} missing required peer {{:#peerWithLabel}}',
|
|
547
|
+
'object.without': '{{:#mainWithLabel}} conflict with forbidden peer {{:#peerWithLabel}}',
|
|
547
548
|
'object.xor': '{{#label}} contains a conflict between exclusive peers {{#peersWithLabels}}'
|
|
548
549
|
}
|
|
549
550
|
});
|
package/lib/types/number.js
CHANGED
|
@@ -284,7 +284,7 @@ module.exports = Any.extend({
|
|
|
284
284
|
'number.integer': '{{#label}} must be an integer',
|
|
285
285
|
'number.less': '{{#label}} must be less than {{#limit}}',
|
|
286
286
|
'number.max': '{{#label}} must be less than or equal to {{#limit}}',
|
|
287
|
-
'number.min': '{{#label}} must be
|
|
287
|
+
'number.min': '{{#label}} must be greater than or equal to {{#limit}}',
|
|
288
288
|
'number.multiple': '{{#label}} must be a multiple of {{#multiple}}',
|
|
289
289
|
'number.negative': '{{#label}} must be a negative number',
|
|
290
290
|
'number.port': '{{#label}} must be a valid port',
|
|
@@ -312,10 +312,14 @@ internals.normalizeExponent = function (str) {
|
|
|
312
312
|
internals.normalizeDecimal = function (str) {
|
|
313
313
|
|
|
314
314
|
str = str
|
|
315
|
+
// Remove leading plus signs
|
|
315
316
|
.replace(/^\+/, '')
|
|
316
|
-
|
|
317
|
+
// Remove trailing zeros if there is a decimal point and unecessary decimal points
|
|
318
|
+
.replace(/\.0*$/, '')
|
|
319
|
+
// Add a integer 0 if the numbers starts with a decimal point
|
|
317
320
|
.replace(/^(-?)\.([^\.]*)$/, '$10.$2')
|
|
318
|
-
|
|
321
|
+
// Remove leading zeros
|
|
322
|
+
.replace(/^(-?)0+([0-9])/, '$1$2');
|
|
319
323
|
|
|
320
324
|
if (str.includes('.') &&
|
|
321
325
|
str.endsWith('0')) {
|
package/lib/types/string.js
CHANGED
|
@@ -41,8 +41,8 @@ const internals = {
|
|
|
41
41
|
uuidv4: '4',
|
|
42
42
|
uuidv5: '5'
|
|
43
43
|
},
|
|
44
|
+
guidSeparators: new Set([undefined, true, false, '-', ':']),
|
|
44
45
|
|
|
45
|
-
cidrPresences: ['required', 'optional', 'forbidden'],
|
|
46
46
|
normalizationForms: ['NFC', 'NFD', 'NFKC', 'NFKD']
|
|
47
47
|
};
|
|
48
48
|
|
|
@@ -257,7 +257,7 @@ module.exports = Any.extend({
|
|
|
257
257
|
method(options) {
|
|
258
258
|
|
|
259
259
|
if (options) {
|
|
260
|
-
Common.assertOptions(options, ['allowUnicode', 'minDomainSegments', 'tlds']);
|
|
260
|
+
Common.assertOptions(options, ['allowUnicode', 'maxDomainSegments', 'minDomainSegments', 'tlds']);
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
const address = internals.addressOptions(options);
|
|
@@ -276,7 +276,7 @@ module.exports = Any.extend({
|
|
|
276
276
|
email: {
|
|
277
277
|
method(options = {}) {
|
|
278
278
|
|
|
279
|
-
Common.assertOptions(options, ['allowUnicode', 'ignoreLength', 'minDomainSegments', 'multiple', 'separator', 'tlds']);
|
|
279
|
+
Common.assertOptions(options, ['allowUnicode', 'ignoreLength', 'maxDomainSegments', 'minDomainSegments', 'multiple', 'separator', 'tlds']);
|
|
280
280
|
Assert(options.multiple === undefined || typeof options.multiple === 'boolean', 'multiple option must be an boolean');
|
|
281
281
|
|
|
282
282
|
const address = internals.addressOptions(options);
|
|
@@ -306,7 +306,7 @@ module.exports = Any.extend({
|
|
|
306
306
|
alias: 'uuid',
|
|
307
307
|
method(options = {}) {
|
|
308
308
|
|
|
309
|
-
Common.assertOptions(options, ['version']);
|
|
309
|
+
Common.assertOptions(options, ['version', 'separator']);
|
|
310
310
|
|
|
311
311
|
let versionNumbers = '';
|
|
312
312
|
|
|
@@ -328,7 +328,12 @@ module.exports = Any.extend({
|
|
|
328
328
|
}
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
-
|
|
331
|
+
Assert(internals.guidSeparators.has(options.separator), 'separator must be one of true, false, "-", or ":"');
|
|
332
|
+
const separator = options.separator === undefined ? '[:-]?' :
|
|
333
|
+
options.separator === true ? '[:-]' :
|
|
334
|
+
options.separator === false ? '[]?' : `\\${options.separator}`;
|
|
335
|
+
|
|
336
|
+
const regex = new RegExp(`^([\\[{\\(]?)[0-9A-F]{8}(${separator})[0-9A-F]{4}\\2?[${versionNumbers || '0-9A-F'}][0-9A-F]{3}\\2?[${versionNumbers ? '89AB' : '0-9A-F'}][0-9A-F]{3}\\2?[0-9A-F]{12}([\\]}\\)]?)$`, 'i');
|
|
332
337
|
|
|
333
338
|
return this.$_addRule({ name: 'guid', args: { options }, regex });
|
|
334
339
|
},
|
|
@@ -627,7 +632,7 @@ module.exports = Any.extend({
|
|
|
627
632
|
Common.assertOptions(options, ['allowRelative', 'allowQuerySquareBrackets', 'domain', 'relativeOnly', 'scheme']);
|
|
628
633
|
|
|
629
634
|
if (options.domain) {
|
|
630
|
-
Common.assertOptions(options.domain, ['allowUnicode', 'minDomainSegments', 'tlds']);
|
|
635
|
+
Common.assertOptions(options.domain, ['allowUnicode', 'maxDomainSegments', 'minDomainSegments', 'tlds']);
|
|
631
636
|
}
|
|
632
637
|
|
|
633
638
|
const { regex, scheme } = Uri.regex(options);
|
|
@@ -702,10 +707,10 @@ module.exports = Any.extend({
|
|
|
702
707
|
'string.min': '{{#label}} length must be at least {{#limit}} characters long',
|
|
703
708
|
'string.normalize': '{{#label}} must be unicode normalized in the {{#form}} form',
|
|
704
709
|
'string.token': '{{#label}} must only contain alpha-numeric and underscore characters',
|
|
705
|
-
'string.pattern.base': '{{#label}} with value
|
|
706
|
-
'string.pattern.name': '{{#label}} with value
|
|
707
|
-
'string.pattern.invert.base': '{{#label}} with value
|
|
708
|
-
'string.pattern.invert.name': '{{#label}} with value
|
|
710
|
+
'string.pattern.base': '{{#label}} with value {:[.]} fails to match the required pattern: {{#regex}}',
|
|
711
|
+
'string.pattern.name': '{{#label}} with value {:[.]} fails to match the {{#name}} pattern',
|
|
712
|
+
'string.pattern.invert.base': '{{#label}} with value {:[.]} matches the inverted pattern: {{#regex}}',
|
|
713
|
+
'string.pattern.invert.name': '{{#label}} with value {:[.]} matches the inverted {{#name}} pattern',
|
|
709
714
|
'string.trim': '{{#label}} must not have leading or trailing whitespace',
|
|
710
715
|
'string.uri': '{{#label}} must be a valid uri',
|
|
711
716
|
'string.uriCustomScheme': '{{#label}} must be a valid uri with a scheme matching the {{#scheme}} pattern',
|
|
@@ -728,6 +733,11 @@ internals.addressOptions = function (options) {
|
|
|
728
733
|
Assert(options.minDomainSegments === undefined ||
|
|
729
734
|
Number.isSafeInteger(options.minDomainSegments) && options.minDomainSegments > 0, 'minDomainSegments must be a positive integer');
|
|
730
735
|
|
|
736
|
+
// maxDomainSegments
|
|
737
|
+
|
|
738
|
+
Assert(options.maxDomainSegments === undefined ||
|
|
739
|
+
Number.isSafeInteger(options.maxDomainSegments) && options.maxDomainSegments > 0, 'maxDomainSegments must be a positive integer');
|
|
740
|
+
|
|
731
741
|
// tlds
|
|
732
742
|
|
|
733
743
|
if (options.tlds === false) {
|
|
@@ -751,6 +761,7 @@ internals.addressOptions = function (options) {
|
|
|
751
761
|
|
|
752
762
|
Assert(options.tlds.deny instanceof Set, 'tlds.deny must be an array, Set, or boolean');
|
|
753
763
|
Assert(!options.tlds.allow, 'Cannot specify both tlds.allow and tlds.deny lists');
|
|
764
|
+
internals.validateTlds(options.tlds.deny, 'tlds.deny');
|
|
754
765
|
return options;
|
|
755
766
|
}
|
|
756
767
|
|
|
@@ -769,16 +780,29 @@ internals.addressOptions = function (options) {
|
|
|
769
780
|
}
|
|
770
781
|
|
|
771
782
|
Assert(options.tlds.allow instanceof Set, 'tlds.allow must be an array, Set, or boolean');
|
|
783
|
+
internals.validateTlds(options.tlds.allow, 'tlds.allow');
|
|
772
784
|
return options;
|
|
773
785
|
};
|
|
774
786
|
|
|
775
787
|
|
|
788
|
+
internals.validateTlds = function (set, source) {
|
|
789
|
+
|
|
790
|
+
for (const tld of set) {
|
|
791
|
+
Assert(Domain.isValid(tld, { minDomainSegments: 1, maxDomainSegments: 1 }), `${source} must contain valid top level domain names`);
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
|
|
776
796
|
internals.isoDate = function (value) {
|
|
777
797
|
|
|
778
798
|
if (!Common.isIsoDate(value)) {
|
|
779
799
|
return null;
|
|
780
800
|
}
|
|
781
801
|
|
|
802
|
+
if (/.*T.*[+-]\d\d$/.test(value)) { // Add missing trailing zeros to timeshift
|
|
803
|
+
value += '00';
|
|
804
|
+
}
|
|
805
|
+
|
|
782
806
|
const date = new Date(value);
|
|
783
807
|
if (isNaN(date.getTime())) {
|
|
784
808
|
return null;
|
package/lib/validator.js
CHANGED
|
@@ -20,6 +20,7 @@ exports.entry = function (value, schema, prefs) {
|
|
|
20
20
|
let settings = Common.defaults;
|
|
21
21
|
if (prefs) {
|
|
22
22
|
Assert(prefs.warnings === undefined, 'Cannot override warnings preference in synchronous validation');
|
|
23
|
+
Assert(prefs.artifacts === undefined, 'Cannot override artifacts preference in synchronous validation');
|
|
23
24
|
settings = Common.preferences(Common.defaults, prefs);
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -39,6 +40,10 @@ exports.entry = function (value, schema, prefs) {
|
|
|
39
40
|
outcome.debug = result.mainstay.debug;
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
if (result.mainstay.artifacts) {
|
|
44
|
+
outcome.artifacts = result.mainstay.artifacts;
|
|
45
|
+
}
|
|
46
|
+
|
|
42
47
|
return outcome;
|
|
43
48
|
};
|
|
44
49
|
|
|
@@ -98,7 +103,8 @@ exports.entryAsync = async function (value, schema, prefs) {
|
|
|
98
103
|
}
|
|
99
104
|
|
|
100
105
|
if (!settings.warnings &&
|
|
101
|
-
!settings.debug
|
|
106
|
+
!settings.debug &&
|
|
107
|
+
!settings.artifacts) {
|
|
102
108
|
|
|
103
109
|
return result.value;
|
|
104
110
|
}
|
|
@@ -112,6 +118,10 @@ exports.entryAsync = async function (value, schema, prefs) {
|
|
|
112
118
|
outcome.debug = mainstay.debug;
|
|
113
119
|
}
|
|
114
120
|
|
|
121
|
+
if (mainstay.artifacts) {
|
|
122
|
+
outcome.artifacts = mainstay.artifacts;
|
|
123
|
+
}
|
|
124
|
+
|
|
115
125
|
return outcome;
|
|
116
126
|
};
|
|
117
127
|
|
|
@@ -190,6 +200,7 @@ exports.validate = function (value, schema, state, prefs, overrides = {}) {
|
|
|
190
200
|
schema,
|
|
191
201
|
state,
|
|
192
202
|
error: createError,
|
|
203
|
+
errorsArray: internals.errorsArray,
|
|
193
204
|
warn: (code, local, localState) => state.mainstay.warnings.push(createError(code, local, localState)),
|
|
194
205
|
message: (messages, local) => schema.$_createError('custom', value, local, state, prefs, { messages })
|
|
195
206
|
};
|
|
@@ -244,7 +255,7 @@ exports.validate = function (value, schema, state, prefs, overrides = {}) {
|
|
|
244
255
|
|
|
245
256
|
// Presence requirements (required, optional, forbidden)
|
|
246
257
|
|
|
247
|
-
const presence = overrides.presence || schema._flags.presence || (schema._flags._endedSwitch ?
|
|
258
|
+
const presence = overrides.presence || schema._flags.presence || (schema._flags._endedSwitch ? null : prefs.presence);
|
|
248
259
|
if (value === undefined) {
|
|
249
260
|
if (presence === 'forbidden') {
|
|
250
261
|
return internals.finalize(value, null, helpers);
|
|
@@ -416,7 +427,7 @@ internals.rule = function (ret, rule) {
|
|
|
416
427
|
}
|
|
417
428
|
|
|
418
429
|
if (Array.isArray(ret) &&
|
|
419
|
-
|
|
430
|
+
ret[Common.symbols.errors]) {
|
|
420
431
|
|
|
421
432
|
ret.forEach((report) => internals.error(report, rule));
|
|
422
433
|
return { errors: ret, value: null };
|
|
@@ -523,6 +534,20 @@ internals.finalize = function (value, errors, helpers) {
|
|
|
523
534
|
schema._cache.set(helpers.original, result);
|
|
524
535
|
}
|
|
525
536
|
|
|
537
|
+
// Artifacts
|
|
538
|
+
|
|
539
|
+
if (value !== undefined &&
|
|
540
|
+
!result.errors &&
|
|
541
|
+
schema._flags.artifact !== undefined) {
|
|
542
|
+
|
|
543
|
+
state.mainstay.artifacts = state.mainstay.artifacts || new Map();
|
|
544
|
+
if (!state.mainstay.artifacts.has(schema._flags.artifact)) {
|
|
545
|
+
state.mainstay.artifacts.set(schema._flags.artifact, []);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
state.mainstay.artifacts.get(schema._flags.artifact).push(state.path);
|
|
549
|
+
}
|
|
550
|
+
|
|
526
551
|
return result;
|
|
527
552
|
};
|
|
528
553
|
|
|
@@ -615,3 +640,11 @@ internals.ignore = {
|
|
|
615
640
|
resolve: Ignore,
|
|
616
641
|
value: Ignore
|
|
617
642
|
};
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
internals.errorsArray = function () {
|
|
646
|
+
|
|
647
|
+
const errors = [];
|
|
648
|
+
errors[Common.symbols.errors] = true;
|
|
649
|
+
return errors;
|
|
650
|
+
};
|
package/package.json
CHANGED
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
2
|
+
"name": "joi",
|
|
3
|
+
"description": "Object schema validation",
|
|
4
|
+
"version": "17.2.0",
|
|
5
|
+
"repository": "git://github.com/sideway/joi",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/index.d.ts",
|
|
8
|
+
"browser": "dist/joi-browser.min.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"lib/**/*",
|
|
11
|
+
"dist/*"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"schema",
|
|
15
|
+
"validation"
|
|
16
|
+
],
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@hapi/address": "^4.1.0",
|
|
19
|
+
"@hapi/formula": "^2.0.0",
|
|
20
|
+
"@hapi/hoek": "^9.0.0",
|
|
21
|
+
"@hapi/pinpoint": "^2.0.0",
|
|
22
|
+
"@hapi/topo": "^5.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@hapi/bourne": "2.x.x",
|
|
26
|
+
"@hapi/code": "8.x.x",
|
|
27
|
+
"@hapi/lab": "23.x.x",
|
|
28
|
+
"@hapi/joi-legacy-test": "npm:@hapi/joi@15.x.x"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"prepublishOnly": "cd browser && npm install && npm run build",
|
|
32
|
+
"test": "lab -t 100 -a @hapi/code -L -Y",
|
|
33
|
+
"test-cov-html": "lab -r html -o coverage.html -a @hapi/code"
|
|
34
|
+
},
|
|
35
|
+
"license": "BSD-3-Clause"
|
|
35
36
|
}
|