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/lib/manifest.js CHANGED
@@ -256,7 +256,7 @@ internals.Builder = class {
256
256
 
257
257
  // Type
258
258
 
259
- let schema = this.joi[desc.type]();
259
+ let schema = this.joi[desc.type]()._bare();
260
260
  const def = schema._definition;
261
261
 
262
262
  // Flags
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, Object.assign({}, options, { in: true }));
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', // Copied
97
- 'depth', 'key', 'root', 'display' // Overridden
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
- const variable = part.slice(raw ? 0 : 1, end);
82
- const dynamic = this._ref(internals.decode(variable), raw);
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.errors);
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
- const ends = part.ref && part.ref.type === 'local' && part.ref.key === 'label' && prefs.errors.wrap.label;
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
- return { ref: refs[0], raw, refs };
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.$_super.label(name);
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 "{{#ref}}" which {{#reason}}',
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
  }
@@ -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 "{#patternLabel}"',
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 "{{#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 larger than or equal to "{{#limit}}"',
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.$_super.default(value, options);
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 "{{#type}}"',
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': '"{{#mainWithLabel}}" must not exist simultaneously with {{#peersWithLabels}}',
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 "{{#from}}" because multiple renames are disabled and another key was already renamed to "{{#to}}"',
542
- 'object.rename.override': '{{#label}} cannot rename "{{#from}}" because override is disabled and target "{{#to}}" exists',
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': '"{{#mainWithLabel}}" missing required peer "{{#peerWithLabel}}"',
546
- 'object.without': '"{{#mainWithLabel}}" conflict with forbidden peer "{{#peerWithLabel}}"',
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
  });
@@ -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 larger than or equal to {{#limit}}',
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
- .replace(/\.0+$/, '')
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
- .replace(/^(-?)0+([1-9])/, '$1$2');
321
+ // Remove leading zeros
322
+ .replace(/^(-?)0+([0-9])/, '$1$2');
319
323
 
320
324
  if (str.includes('.') &&
321
325
  str.endsWith('0')) {
@@ -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
- const regex = new RegExp(`^([\\[{\\(]?)[0-9A-F]{8}([:-]?)[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');
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 "{[.]}" fails to match the required pattern: {{#regex}}',
706
- 'string.pattern.name': '{{#label}} with value "{[.]}" fails to match the {{#name}} pattern',
707
- 'string.pattern.invert.base': '{{#label}} with value "{[.]}" matches the inverted pattern: {{#regex}}',
708
- 'string.pattern.invert.name': '{{#label}} with value "{[.]}" matches the inverted {{#name}} pattern',
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 ? 'ignore' : prefs.presence);
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
- (ret[0] instanceof Errors.Report || ret[0] instanceof Error)) {
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
- "name": "joi",
3
- "description": "Object schema validation",
4
- "version": "17.1.1",
5
- "repository": "git://github.com/sideway/joi",
6
- "main": "lib/index.js",
7
- "browser": "dist/joi-browser.min.js",
8
- "files": [
9
- "lib/**/*",
10
- "dist/*"
11
- ],
12
- "keywords": [
13
- "schema",
14
- "validation"
15
- ],
16
- "dependencies": {
17
- "@hapi/address": "^4.0.1",
18
- "@hapi/formula": "^2.0.0",
19
- "@hapi/hoek": "^9.0.0",
20
- "@hapi/pinpoint": "^2.0.0",
21
- "@hapi/topo": "^5.0.0"
22
- },
23
- "devDependencies": {
24
- "@hapi/bourne": "2.x.x",
25
- "@hapi/code": "8.x.x",
26
- "@hapi/lab": "22.x.x",
27
- "@hapi/joi-legacy-test": "npm:@hapi/joi@15.x.x"
28
- },
29
- "scripts": {
30
- "prepublishOnly": "cd browser && npm install && npm run build",
31
- "test": "lab -t 100 -a @hapi/code -L",
32
- "test-cov-html": "lab -r html -o coverage.html -a @hapi/code"
33
- },
34
- "license": "BSD-3-Clause"
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
  }