joi 18.2.1 → 18.2.2

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/annotate.js CHANGED
@@ -147,12 +147,12 @@ internals.serializer = function () {
147
147
  value = annotated;
148
148
  }
149
149
  else {
150
- for (const errorKey in annotations.errors) {
150
+ for (const errorKey of Object.keys(annotations.errors)) {
151
151
  value[`${errorKey}_$key$_${annotations.errors[errorKey].sort().join(', ')}_$end$_`] = value[errorKey];
152
152
  value[errorKey] = undefined;
153
153
  }
154
154
 
155
- for (const missingKey in annotations.missing) {
155
+ for (const missingKey of Object.keys(annotations.missing)) {
156
156
  value[`_$miss$_${missingKey}|${annotations.missing[missingKey]}_$end$_`] = '__missing__';
157
157
  }
158
158
  }
package/lib/base.js CHANGED
@@ -118,7 +118,7 @@ internals.Base = class {
118
118
 
119
119
  for (const rule of this._rules) {
120
120
  const definition = this._definition.rules[rule.name];
121
- if (definition.jsonSchema && typesOverlap) {
121
+ if (definition.jsonSchema && typesOverlap && !rule._resolve.length) {
122
122
  schema = definition.jsonSchema(rule, schema, isOnly, mode, subOptions);
123
123
  }
124
124
  }
@@ -244,7 +244,7 @@ internals.Base = class {
244
244
 
245
245
  const obj = this.clone();
246
246
  obj.$_terms.alterations = obj.$_terms.alterations || [];
247
- for (const target in targets) {
247
+ for (const target of Object.keys(targets)) {
248
248
  const adjuster = targets[target];
249
249
  assert(typeof adjuster === 'function', 'Alteration adjuster for', target, 'must be a function');
250
250
  obj.$_terms.alterations.push({ target, adjuster });
@@ -579,7 +579,7 @@ internals.Base = class {
579
579
 
580
580
  // Terms
581
581
 
582
- for (const key in source.$_terms) {
582
+ for (const key of Object.keys(source.$_terms)) {
583
583
  const terms = source.$_terms[key];
584
584
  if (!terms) {
585
585
  if (!obj.$_terms[key]) {
@@ -683,7 +683,7 @@ internals.Base = class {
683
683
  const original = obj._rules[i];
684
684
  const rule = clone(original);
685
685
 
686
- for (const name in options) {
686
+ for (const name of Object.keys(options)) {
687
687
  def.modifiers[name](rule, options[name]);
688
688
  assert(rule.name === original.name, 'Cannot change rule name');
689
689
  }
@@ -763,7 +763,7 @@ internals.Base = class {
763
763
  assert(options && typeof options === 'object', 'Invalid options');
764
764
  assert(options.name && typeof options.name === 'string', 'Invalid rule name');
765
765
 
766
- for (const key in options) {
766
+ for (const key of Object.keys(options)) {
767
767
  assert(key[0] !== '_', 'Cannot set private rule properties');
768
768
  }
769
769
 
@@ -783,7 +783,7 @@ internals.Base = class {
783
783
  if (args) {
784
784
  assert(Object.keys(args).length === 1 || Object.keys(args).length === this._definition.rules[rule.name].args.length, 'Invalid rule definition for', this.type, rule.name);
785
785
 
786
- for (const key in args) {
786
+ for (const key of Object.keys(args)) {
787
787
  let arg = args[key];
788
788
 
789
789
  if (definition.argsByName) {
@@ -993,15 +993,17 @@ internals.Base = class {
993
993
  target._cache = null;
994
994
 
995
995
  target.$_terms = {};
996
- for (const key in this.$_terms) {
996
+ for (const key of Object.keys(this.$_terms)) {
997
997
  target.$_terms[key] = this.$_terms[key] ? this.$_terms[key].slice() : null;
998
998
  }
999
999
 
1000
1000
  // Backwards compatibility
1001
1001
 
1002
1002
  target.$_super = {};
1003
- for (const override in this.$_super) {
1004
- target.$_super[override] = this._super[override].bind(target);
1003
+ if (this.$_super) {
1004
+ for (const override of Object.keys(this.$_super)) {
1005
+ target.$_super[override] = this._super[override].bind(target);
1006
+ }
1005
1007
  }
1006
1008
 
1007
1009
  return target;
@@ -1013,7 +1015,7 @@ internals.Base = class {
1013
1015
  obj._reset();
1014
1016
 
1015
1017
  const terms = obj._definition.terms;
1016
- for (const name in terms) {
1018
+ for (const name of Object.keys(terms)) {
1017
1019
  const term = terms[name];
1018
1020
  obj.$_terms[name] = term.init;
1019
1021
  }
package/lib/compile.js CHANGED
@@ -152,7 +152,7 @@ internals.walk = function (schema) {
152
152
 
153
153
  assert(Object.getPrototypeOf(schema) === Object.getPrototypeOf({}), 'Schema can only contain plain objects');
154
154
 
155
- for (const key in schema) {
155
+ for (const key of Object.keys(schema)) {
156
156
  const compiler = internals.walk(schema[key]);
157
157
  if (compiler) {
158
158
  return compiler;
package/lib/extend.js CHANGED
@@ -35,7 +35,7 @@ exports.type = function (from, options) {
35
35
 
36
36
  const terms = Object.assign({}, parent.terms);
37
37
  if (def.terms) {
38
- for (const name in def.terms) { // Only apply own terms
38
+ for (const name of Object.keys(def.terms)) { // Only apply own terms
39
39
  const term = def.terms[name];
40
40
  assert(schema.$_terms[name] === undefined, 'Invalid term override for', def.type, name);
41
41
  schema.$_terms[name] = term.init;
@@ -79,7 +79,7 @@ exports.type = function (from, options) {
79
79
 
80
80
  const rules = Object.assign({}, parent.rules);
81
81
  if (def.rules) {
82
- for (const name in def.rules) {
82
+ for (const name of Object.keys(def.rules)) {
83
83
  const rule = def.rules[name];
84
84
  assert(typeof rule === 'object', 'Invalid rule definition for', def.type, name);
85
85
 
@@ -139,7 +139,7 @@ exports.type = function (from, options) {
139
139
 
140
140
  const modifiers = Object.assign({}, parent.modifiers);
141
141
  if (def.modifiers) {
142
- for (const name in def.modifiers) {
142
+ for (const name of Object.keys(def.modifiers)) {
143
143
  assert(!prototype[name], 'Rule conflict in', def.type, name);
144
144
 
145
145
  const modifier = def.modifiers[name];
@@ -162,7 +162,7 @@ exports.type = function (from, options) {
162
162
  if (def.overrides) {
163
163
  prototype._super = base;
164
164
  schema.$_super = {}; // Backwards compatibility
165
- for (const override in def.overrides) {
165
+ for (const override of Object.keys(def.overrides)) {
166
166
  assert(base[override], 'Cannot override missing', override);
167
167
  def.overrides[override][Common.symbols.parent] = base[override];
168
168
  schema.$_super[override] = base[override].bind(schema); // Backwards compatibility
package/lib/index.js CHANGED
@@ -73,7 +73,7 @@ internals.root = function () {
73
73
 
74
74
  // Aliases
75
75
 
76
- for (const alias in internals.aliases) {
76
+ for (const alias of Object.keys(internals.aliases)) {
77
77
  const target = internals.aliases[alias];
78
78
  root[alias] = root[target];
79
79
  }
@@ -206,7 +206,7 @@ internals.methods = {
206
206
  types[type] = this[type]();
207
207
  }
208
208
 
209
- for (const target in internals.aliases) {
209
+ for (const target of Object.keys(internals.aliases)) {
210
210
  types[target] = this[target]();
211
211
  }
212
212
 
package/lib/manifest.js CHANGED
@@ -27,7 +27,7 @@ exports.describe = function (schema) {
27
27
 
28
28
  // Flags
29
29
 
30
- for (const flag in schema._flags) {
30
+ for (const flag of Object.keys(schema._flags)) {
31
31
  if (flag[0] !== '_') {
32
32
  desc.flags[flag] = internals.describe(schema._flags[flag]);
33
33
  }
@@ -67,7 +67,7 @@ exports.describe = function (schema) {
67
67
 
68
68
  const item = { name: rule.name };
69
69
 
70
- for (const custom in def.modifiers) {
70
+ for (const custom of Object.keys(def.modifiers)) {
71
71
  if (rule[custom] !== undefined) {
72
72
  item[custom] = internals.describe(rule[custom]);
73
73
  }
@@ -75,7 +75,7 @@ exports.describe = function (schema) {
75
75
 
76
76
  if (rule.args) {
77
77
  item.args = {};
78
- for (const key in rule.args) {
78
+ for (const key of Object.keys(rule.args)) {
79
79
  const arg = rule.args[key];
80
80
  if (key === 'options' &&
81
81
  !Object.keys(arg).length) {
@@ -100,7 +100,7 @@ exports.describe = function (schema) {
100
100
 
101
101
  // Terms (must be last to verify no name conflicts)
102
102
 
103
- for (const term in schema.$_terms) {
103
+ for (const term of Object.keys(schema.$_terms)) {
104
104
  if (term[0] === '_') {
105
105
  continue;
106
106
  }
@@ -222,7 +222,7 @@ internals.describe = function (item, options = {}) {
222
222
  }
223
223
 
224
224
  const normalized = {};
225
- for (const key in item) {
225
+ for (const key of Object.keys(item)) {
226
226
  const value = item[key];
227
227
  if (value === undefined) {
228
228
  continue;
@@ -261,7 +261,7 @@ internals.Builder = class {
261
261
  // Flags
262
262
 
263
263
  if (desc.flags) {
264
- for (const flag in desc.flags) {
264
+ for (const flag of Object.keys(desc.flags)) {
265
265
  const setter = def.flags[flag] && def.flags[flag].setter || flag;
266
266
  assert(typeof schema[setter] === 'function', 'Invalid flag', flag, 'for type', desc.type);
267
267
  schema = schema[setter](this.build(desc.flags[flag]));
@@ -293,7 +293,7 @@ internals.Builder = class {
293
293
  const args = [];
294
294
  if (rule.args) {
295
295
  const built = {};
296
- for (const key in rule.args) {
296
+ for (const key of Object.keys(rule.args)) {
297
297
  built[key] = this.build(rule.args[key], { assign: key });
298
298
  }
299
299
 
@@ -318,7 +318,7 @@ internals.Builder = class {
318
318
  // Ruleset
319
319
 
320
320
  const options = {};
321
- for (const custom in def.modifiers) {
321
+ for (const custom of Object.keys(def.modifiers)) {
322
322
  if (rule[custom] !== undefined) {
323
323
  options[custom] = this.build(rule[custom]);
324
324
  }
@@ -333,7 +333,7 @@ internals.Builder = class {
333
333
  // Terms
334
334
 
335
335
  const terms = {};
336
- for (const key in desc) {
336
+ for (const key of Object.keys(desc)) {
337
337
  if (['allow', 'flags', 'invalid', 'whens', 'preferences', 'rules', 'type'].includes(key)) {
338
338
  continue;
339
339
  }
@@ -358,7 +358,7 @@ internals.Builder = class {
358
358
 
359
359
  if (typeof manifest === 'object') {
360
360
  terms[key] = {};
361
- for (const name in desc[key]) {
361
+ for (const name of Object.keys(desc[key])) {
362
362
  const value = desc[key][name];
363
363
  terms[key][name] = this.parse(value);
364
364
  }
@@ -449,7 +449,7 @@ internals.Builder = class {
449
449
  }
450
450
 
451
451
  const normalized = {};
452
- for (const key in desc) {
452
+ for (const key of Object.keys(desc)) {
453
453
  normalized[key] = this.build(desc[key], { assign: key });
454
454
  }
455
455
 
package/lib/messages.js CHANGED
@@ -30,7 +30,7 @@ exports.compile = function (messages, target) {
30
30
 
31
31
  target = target ? clone(target) : {};
32
32
 
33
- for (let code in messages) {
33
+ for (const code of Object.keys(messages)) {
34
34
  const message = messages[code];
35
35
 
36
36
  if (code === 'root' ||
@@ -52,18 +52,18 @@ exports.compile = function (messages, target) {
52
52
  const language = code;
53
53
  target[language] = target[language] || {};
54
54
 
55
- for (code in message) {
56
- const localized = message[code];
55
+ for (const key of Object.keys(message)) {
56
+ const localized = message[key];
57
57
 
58
- if (code === 'root' ||
58
+ if (key === 'root' ||
59
59
  Template.isTemplate(localized)) {
60
60
 
61
- target[language][code] = localized;
61
+ target[language][key] = localized;
62
62
  continue;
63
63
  }
64
64
 
65
- assert(typeof localized === 'string', 'Invalid message for', code, 'in', language);
66
- target[language][code] = new Template(localized);
65
+ assert(typeof localized === 'string', 'Invalid message for', key, 'in', language);
66
+ target[language][key] = new Template(localized);
67
67
  }
68
68
  }
69
69
 
@@ -76,7 +76,7 @@ exports.decompile = function (messages) {
76
76
  // By error code { 'number.min': <string | template> }
77
77
 
78
78
  const target = {};
79
- for (let code in messages) {
79
+ for (const code of Object.keys(messages)) {
80
80
  const message = messages[code];
81
81
 
82
82
  if (code === 'root') {
@@ -94,15 +94,15 @@ exports.decompile = function (messages) {
94
94
  const language = code;
95
95
  target[language] = {};
96
96
 
97
- for (code in message) {
98
- const localized = message[code];
97
+ for (const key of Object.keys(message)) {
98
+ const localized = message[key];
99
99
 
100
- if (code === 'root') {
100
+ if (key === 'root') {
101
101
  target[language].root = localized;
102
102
  continue;
103
103
  }
104
104
 
105
- target[language][code] = localized.describe({ compact: true });
105
+ target[language][key] = localized.describe({ compact: true });
106
106
  }
107
107
  }
108
108
 
@@ -136,7 +136,7 @@ exports.merge = function (base, extended) {
136
136
 
137
137
  const target = clone(base);
138
138
 
139
- for (let code in extended) {
139
+ for (const code of Object.keys(extended)) {
140
140
  const message = extended[code];
141
141
 
142
142
  if (code === 'root' ||
@@ -158,18 +158,18 @@ exports.merge = function (base, extended) {
158
158
  const language = code;
159
159
  target[language] = target[language] || {};
160
160
 
161
- for (code in message) {
162
- const localized = message[code];
161
+ for (const key of Object.keys(message)) {
162
+ const localized = message[key];
163
163
 
164
- if (code === 'root' ||
164
+ if (key === 'root' ||
165
165
  Template.isTemplate(localized)) {
166
166
 
167
- target[language][code] = localized;
167
+ target[language][key] = localized;
168
168
  continue;
169
169
  }
170
170
 
171
- assert(typeof localized === 'string', 'Invalid message for', code, 'in', language);
172
- target[language][code] = new Template(localized);
171
+ assert(typeof localized === 'string', 'Invalid message for', key, 'in', language);
172
+ target[language][key] = new Template(localized);
173
173
  }
174
174
  }
175
175
 
package/lib/modify.js CHANGED
@@ -171,7 +171,7 @@ exports.schema = function (schema, options) {
171
171
 
172
172
  let obj;
173
173
 
174
- for (const name in schema._flags) {
174
+ for (const name of Object.keys(schema._flags)) {
175
175
  if (name[0] === '_') {
176
176
  continue;
177
177
  }
@@ -199,7 +199,7 @@ exports.schema = function (schema, options) {
199
199
  }
200
200
  }
201
201
 
202
- for (const name in schema.$_terms) {
202
+ for (const name of Object.keys(schema.$_terms)) {
203
203
  if (name[0] === '_') {
204
204
  continue;
205
205
  }
@@ -251,7 +251,7 @@ internals.scan = function (item, source, options, _path, _key) {
251
251
  return result;
252
252
  }
253
253
 
254
- for (const key in item) {
254
+ for (const key of Object.keys(item)) {
255
255
  if (key[0] === '_') {
256
256
  continue;
257
257
  }
@@ -181,6 +181,9 @@ module.exports = Any.extend({
181
181
  if (matchMode === 'one') {
182
182
  res.oneOf = matches;
183
183
  }
184
+ else if (matchMode === 'all') {
185
+ res.allOf = matches;
186
+ }
184
187
  else {
185
188
  res.anyOf = matches;
186
189
  }
package/lib/types/any.js CHANGED
@@ -112,7 +112,7 @@ module.exports = Base.extend({
112
112
 
113
113
  build(obj, desc) {
114
114
 
115
- for (const key in desc) {
115
+ for (const key of Object.keys(desc)) {
116
116
  const values = desc[key];
117
117
 
118
118
  if (['examples', 'externals', 'metas', 'notes', 'tags'].includes(key)) {
@@ -93,7 +93,7 @@ module.exports = Any.extend({
93
93
 
94
94
  if (ordered.length) {
95
95
  res.unevaluatedItems = items;
96
- res.minItems = ordered.length;
96
+ internals.setOrderedMinItems(res, ordered);
97
97
  }
98
98
  else {
99
99
  res.items = items;
@@ -103,7 +103,7 @@ module.exports = Any.extend({
103
103
  // No additional items allowed beyond the ordered ones
104
104
 
105
105
  res.unevaluatedItems = false;
106
- res.minItems = ordered.length;
106
+ internals.setOrderedMinItems(res, ordered);
107
107
  res.maxItems = ordered.length;
108
108
  }
109
109
 
@@ -111,7 +111,9 @@ module.exports = Any.extend({
111
111
 
112
112
  const contains = [];
113
113
  for (const rule of schema._rules) {
114
- if (rule.name === 'has') {
114
+ if (rule.name === 'has' &&
115
+ !rule.args.schema._refs.refs.length) {
116
+
115
117
  contains.push(rule.args.schema.$_jsonSchema(mode, options));
116
118
  }
117
119
  }
@@ -660,7 +662,9 @@ module.exports = Any.extend({
660
662
  },
661
663
  jsonSchema(rule, res) {
662
664
 
663
- res.uniqueItems = true;
665
+ if (!rule.args.comparator) {
666
+ res.uniqueItems = true;
667
+ }
664
668
 
665
669
  return res;
666
670
  },
@@ -768,6 +772,29 @@ module.exports = Any.extend({
768
772
 
769
773
  // Helpers
770
774
 
775
+ internals.setOrderedMinItems = function (res, ordered) {
776
+
777
+ // Ordered items are optional by default; the array only needs to reach the
778
+ // last explicitly required position.
779
+ const minItems = internals.orderedMinItems(ordered);
780
+ if (minItems) {
781
+ res.minItems = minItems;
782
+ }
783
+ };
784
+
785
+
786
+ internals.orderedMinItems = function (ordered) {
787
+
788
+ for (let i = ordered.length - 1; i >= 0; --i) {
789
+ if (ordered[i]._flags.presence === 'required') {
790
+ return i + 1;
791
+ }
792
+ }
793
+
794
+ return 0;
795
+ };
796
+
797
+
771
798
  internals.fillMissedErrors = function (schema, errors, requireds, value, state, prefs) {
772
799
 
773
800
  const knownMisses = [];
package/lib/types/keys.js CHANGED
@@ -61,6 +61,11 @@ module.exports = Any.extend({
61
61
  for (const child of schema.$_terms.keys) {
62
62
  const jsonSchema = child.schema.$_jsonSchema(mode, options);
63
63
  res.properties[child.key] = jsonSchema;
64
+
65
+ if (child.schema._flags.id) {
66
+ options.$defs[child.schema._flags.id] = jsonSchema;
67
+ }
68
+
64
69
  if (child.schema._flags.presence === 'required' ||
65
70
  (mode === 'output' && child.schema._flags.default !== undefined)) {
66
71
 
@@ -320,7 +325,7 @@ module.exports = Any.extend({
320
325
  }
321
326
  else {
322
327
  obj.$_terms.keys = obj.$_terms.keys ? obj.$_terms.keys.filter((child) => !schema.hasOwnProperty(child.key)) : new internals.Keys();
323
- for (const key in schema) {
328
+ for (const key of Object.keys(schema)) {
324
329
  Common.tryWithPath(() => obj.$_terms.keys.push({ key, schema: this.$_compile(schema[key]) }), key);
325
330
  }
326
331
  }
@@ -920,7 +925,7 @@ internals.rename = function (schema, value, state, prefs, errors) {
920
925
  }
921
926
  }
922
927
  else {
923
- for (const from in value) {
928
+ for (const from of Object.keys(value)) {
924
929
  if (value[from] === undefined &&
925
930
  rename.options.ignoreUndefined) {
926
931
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "joi",
3
3
  "description": "Object schema validation",
4
- "version": "18.2.1",
4
+ "version": "18.2.2",
5
5
  "repository": {
6
6
  "url": "git://github.com/hapijs/joi.git",
7
7
  "type": "git"
@@ -37,6 +37,7 @@
37
37
  "@hapi/lab": "^26.0.0",
38
38
  "@types/node": "^20.17.47",
39
39
  "ajv": "^8.18.0",
40
+ "ajv-formats": "^3.0.1",
40
41
  "typescript": "^5.8.3"
41
42
  },
42
43
  "scripts": {