joi 18.2.1 → 18.2.6
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/dist/joi-browser.min.js +1 -1
- package/dist/joi-browser.min.js.map +1 -0
- package/dist/joi-browser.min.mjs +5896 -0
- package/dist/joi-browser.min.mjs.map +1 -0
- package/lib/annotate.js +2 -2
- package/lib/base.js +12 -10
- package/lib/compile.js +1 -1
- package/lib/extend.js +4 -4
- package/lib/index.js +2 -2
- package/lib/manifest.js +11 -11
- package/lib/messages.js +29 -21
- package/lib/modify.js +3 -3
- package/lib/types/alternatives.js +3 -0
- package/lib/types/any.js +1 -1
- package/lib/types/array.js +31 -4
- package/lib/types/keys.js +25 -3
- package/lib/types/string.js +16 -4
- package/package.json +2 -1
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1004
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
33
|
+
for (const code of Object.keys(messages)) {
|
|
34
34
|
const message = messages[code];
|
|
35
35
|
|
|
36
36
|
if (code === 'root' ||
|
|
@@ -50,20 +50,24 @@ exports.compile = function (messages, target) {
|
|
|
50
50
|
assert(typeof message === 'object' && !Array.isArray(message), 'Invalid message for', code);
|
|
51
51
|
|
|
52
52
|
const language = code;
|
|
53
|
-
target[language] = target[language] || {};
|
|
54
53
|
|
|
55
|
-
|
|
56
|
-
const localized = message[code];
|
|
54
|
+
// Don't reuse an inherited object, otherwise a language named __proto__ or constructor writes on the prototype
|
|
57
55
|
|
|
58
|
-
|
|
56
|
+
const localizedTarget = Object.hasOwn(target, language) ? target[language] : {};
|
|
57
|
+
target[language] = localizedTarget;
|
|
58
|
+
|
|
59
|
+
for (const key of Object.keys(message)) {
|
|
60
|
+
const localized = message[key];
|
|
61
|
+
|
|
62
|
+
if (key === 'root' ||
|
|
59
63
|
Template.isTemplate(localized)) {
|
|
60
64
|
|
|
61
|
-
|
|
65
|
+
localizedTarget[key] = localized;
|
|
62
66
|
continue;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
assert(typeof localized === 'string', 'Invalid message for',
|
|
66
|
-
|
|
69
|
+
assert(typeof localized === 'string', 'Invalid message for', key, 'in', language);
|
|
70
|
+
localizedTarget[key] = new Template(localized);
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
|
|
@@ -76,7 +80,7 @@ exports.decompile = function (messages) {
|
|
|
76
80
|
// By error code { 'number.min': <string | template> }
|
|
77
81
|
|
|
78
82
|
const target = {};
|
|
79
|
-
for (
|
|
83
|
+
for (const code of Object.keys(messages)) {
|
|
80
84
|
const message = messages[code];
|
|
81
85
|
|
|
82
86
|
if (code === 'root') {
|
|
@@ -94,15 +98,15 @@ exports.decompile = function (messages) {
|
|
|
94
98
|
const language = code;
|
|
95
99
|
target[language] = {};
|
|
96
100
|
|
|
97
|
-
for (
|
|
98
|
-
const localized = message[
|
|
101
|
+
for (const key of Object.keys(message)) {
|
|
102
|
+
const localized = message[key];
|
|
99
103
|
|
|
100
|
-
if (
|
|
104
|
+
if (key === 'root') {
|
|
101
105
|
target[language].root = localized;
|
|
102
106
|
continue;
|
|
103
107
|
}
|
|
104
108
|
|
|
105
|
-
target[language][
|
|
109
|
+
target[language][key] = localized.describe({ compact: true });
|
|
106
110
|
}
|
|
107
111
|
}
|
|
108
112
|
|
|
@@ -136,7 +140,7 @@ exports.merge = function (base, extended) {
|
|
|
136
140
|
|
|
137
141
|
const target = clone(base);
|
|
138
142
|
|
|
139
|
-
for (
|
|
143
|
+
for (const code of Object.keys(extended)) {
|
|
140
144
|
const message = extended[code];
|
|
141
145
|
|
|
142
146
|
if (code === 'root' ||
|
|
@@ -156,20 +160,24 @@ exports.merge = function (base, extended) {
|
|
|
156
160
|
assert(typeof message === 'object' && !Array.isArray(message), 'Invalid message for', code);
|
|
157
161
|
|
|
158
162
|
const language = code;
|
|
159
|
-
target[language] = target[language] || {};
|
|
160
163
|
|
|
161
|
-
|
|
162
|
-
|
|
164
|
+
// Same as in compile(), don't reuse an inherited object
|
|
165
|
+
|
|
166
|
+
const localizedTarget = Object.hasOwn(target, language) ? target[language] : {};
|
|
167
|
+
target[language] = localizedTarget;
|
|
168
|
+
|
|
169
|
+
for (const key of Object.keys(message)) {
|
|
170
|
+
const localized = message[key];
|
|
163
171
|
|
|
164
|
-
if (
|
|
172
|
+
if (key === 'root' ||
|
|
165
173
|
Template.isTemplate(localized)) {
|
|
166
174
|
|
|
167
|
-
|
|
175
|
+
localizedTarget[key] = localized;
|
|
168
176
|
continue;
|
|
169
177
|
}
|
|
170
178
|
|
|
171
|
-
assert(typeof localized === 'string', 'Invalid message for',
|
|
172
|
-
|
|
179
|
+
assert(typeof localized === 'string', 'Invalid message for', key, 'in', language);
|
|
180
|
+
localizedTarget[key] = new Template(localized);
|
|
173
181
|
}
|
|
174
182
|
}
|
|
175
183
|
|
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
|
|
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
|
|
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
|
|
254
|
+
for (const key of Object.keys(item)) {
|
|
255
255
|
if (key[0] === '_') {
|
|
256
256
|
continue;
|
|
257
257
|
}
|
package/lib/types/any.js
CHANGED
package/lib/types/array.js
CHANGED
|
@@ -93,7 +93,7 @@ module.exports = Any.extend({
|
|
|
93
93
|
|
|
94
94
|
if (ordered.length) {
|
|
95
95
|
res.unevaluatedItems = items;
|
|
96
|
-
res
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
}
|
|
@@ -650,6 +655,7 @@ module.exports = Any.extend({
|
|
|
650
655
|
'object.regex': '{{#label}} must be a RegExp object',
|
|
651
656
|
'object.rename.multiple': '{{#label}} cannot rename {{:#from}} because multiple renames are disabled and another key was already renamed to {{:#to}}',
|
|
652
657
|
'object.rename.override': '{{#label}} cannot rename {{:#from}} because override is disabled and target {{:#to}} exists',
|
|
658
|
+
'object.rename.proto': '{{#label}} cannot rename {{:#from}} because target {{:#to}} is a reserved key',
|
|
653
659
|
'object.schema': '{{#label}} must be a Joi schema of {{#type}} type',
|
|
654
660
|
'object.unknown': '{{#label}} is not allowed',
|
|
655
661
|
'object.with': '{{:#mainWithLabel}} missing required peer {{:#peerWithLabel}}',
|
|
@@ -670,8 +676,15 @@ internals.clone = function (value, prefs) {
|
|
|
670
676
|
return Clone(value, { shallow: true });
|
|
671
677
|
}
|
|
672
678
|
|
|
673
|
-
const
|
|
679
|
+
const proto = Object.getPrototypeOf(value);
|
|
680
|
+
const clone = Object.create(proto);
|
|
674
681
|
Object.assign(clone, value);
|
|
682
|
+
|
|
683
|
+
// Restore the prototype in case of pre-existing prototype pollution
|
|
684
|
+
if (Object.getPrototypeOf(clone) !== proto) {
|
|
685
|
+
Object.setPrototypeOf(clone, proto);
|
|
686
|
+
}
|
|
687
|
+
|
|
675
688
|
return clone;
|
|
676
689
|
}
|
|
677
690
|
|
|
@@ -920,7 +933,7 @@ internals.rename = function (schema, value, state, prefs, errors) {
|
|
|
920
933
|
}
|
|
921
934
|
}
|
|
922
935
|
else {
|
|
923
|
-
for (const from
|
|
936
|
+
for (const from of Object.keys(value)) {
|
|
924
937
|
if (value[from] === undefined &&
|
|
925
938
|
rename.options.ignoreUndefined) {
|
|
926
939
|
|
|
@@ -970,6 +983,15 @@ internals.rename = function (schema, value, state, prefs, errors) {
|
|
|
970
983
|
}
|
|
971
984
|
}
|
|
972
985
|
|
|
986
|
+
if (to === '__proto__') {
|
|
987
|
+
errors.push(schema.$_createError('object.rename.proto', value, { from, to, pattern }, state, prefs));
|
|
988
|
+
if (prefs.abortEarly) {
|
|
989
|
+
return false;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
|
|
973
995
|
if (value[from] === undefined) {
|
|
974
996
|
delete value[to];
|
|
975
997
|
}
|
package/lib/types/string.js
CHANGED
|
@@ -313,7 +313,7 @@ module.exports = Any.extend({
|
|
|
313
313
|
email: {
|
|
314
314
|
method(options = {}) {
|
|
315
315
|
|
|
316
|
-
Common.assertOptions(options, ['allowFullyQualified', 'allowUnicode', 'ignoreLength', 'maxDomainSegments', 'minDomainSegments', 'multiple', 'separator', 'tlds']);
|
|
316
|
+
Common.assertOptions(options, ['allowFullyQualified', 'allowUnicode', 'allowUnderscore', 'ignoreLength', 'maxDomainSegments', 'minDomainSegments', 'multiple', 'separator', 'tlds']);
|
|
317
317
|
assert(options.multiple === undefined || typeof options.multiple === 'boolean', 'multiple option must be an boolean');
|
|
318
318
|
|
|
319
319
|
const address = internals.addressOptions(options);
|
|
@@ -693,7 +693,19 @@ module.exports = Any.extend({
|
|
|
693
693
|
},
|
|
694
694
|
jsonSchema(rule, res) {
|
|
695
695
|
|
|
696
|
-
|
|
696
|
+
const pattern = rule.args.regex.source;
|
|
697
|
+
|
|
698
|
+
if (res.allOf) {
|
|
699
|
+
res.allOf.push({ pattern });
|
|
700
|
+
}
|
|
701
|
+
else if (res.pattern !== undefined) {
|
|
702
|
+
res.allOf = [{ pattern: res.pattern }, { pattern }];
|
|
703
|
+
delete res.pattern;
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
res.pattern = pattern;
|
|
707
|
+
}
|
|
708
|
+
|
|
697
709
|
return res;
|
|
698
710
|
},
|
|
699
711
|
args: ['regex', 'options'],
|
|
@@ -965,8 +977,8 @@ internals.isoDate = function (value) {
|
|
|
965
977
|
return null;
|
|
966
978
|
}
|
|
967
979
|
|
|
968
|
-
if (
|
|
969
|
-
value += '00';
|
|
980
|
+
if (/T.*[+-]\d\d$/.test(value)) { // Add missing separator and trailing zeros to timeshift
|
|
981
|
+
value += ':00';
|
|
970
982
|
}
|
|
971
983
|
|
|
972
984
|
const date = new Date(value);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "joi",
|
|
3
3
|
"description": "Object schema validation",
|
|
4
|
-
"version": "18.2.
|
|
4
|
+
"version": "18.2.6",
|
|
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": {
|