joi 18.0.2 → 18.1.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/dist/joi-browser.min.js +1 -1
- package/lib/base.js +172 -1
- package/lib/common.js +19 -0
- package/lib/extend.js +6 -0
- package/lib/index.d.ts +9 -2
- package/lib/schemas.js +3 -1
- package/lib/types/alternatives.js +43 -0
- package/lib/types/array.js +106 -0
- package/lib/types/binary.js +24 -0
- package/lib/types/date.js +48 -2
- package/lib/types/keys.js +80 -0
- package/lib/types/link.js +23 -0
- package/lib/types/number.js +36 -0
- package/lib/types/string.js +104 -0
- package/lib/types/symbol.js +12 -0
- package/package.json +3 -2
package/lib/base.js
CHANGED
|
@@ -16,7 +16,12 @@ const Validator = require('./validator');
|
|
|
16
16
|
const Values = require('./values');
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
const internals = {
|
|
19
|
+
const internals = {
|
|
20
|
+
standardTypes: new Set(['string', 'number', 'integer', 'boolean', 'object', 'array', 'null']),
|
|
21
|
+
jsonSchemaTarget: 'draft-2020-12',
|
|
22
|
+
primitiveTypes: new Set(['string', 'number', 'boolean']),
|
|
23
|
+
nullSchema: () => ({ type: 'null' })
|
|
24
|
+
};
|
|
20
25
|
|
|
21
26
|
|
|
22
27
|
internals.Base = class {
|
|
@@ -62,6 +67,168 @@ internals.Base = class {
|
|
|
62
67
|
return Manifest.describe(this);
|
|
63
68
|
}
|
|
64
69
|
|
|
70
|
+
$_jsonSchema(mode, options = {}) {
|
|
71
|
+
|
|
72
|
+
if (options.target !== undefined &&
|
|
73
|
+
options.target !== internals.jsonSchemaTarget) {
|
|
74
|
+
|
|
75
|
+
throw new Error(`Unsupported JSON Schema target: ${options.target}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const rootCall = !options.$defs;
|
|
79
|
+
const defs = options.$defs ?? {};
|
|
80
|
+
|
|
81
|
+
let schema = {};
|
|
82
|
+
|
|
83
|
+
const isTypeAny = this.type === 'any';
|
|
84
|
+
const isOnly = this._flags.only;
|
|
85
|
+
|
|
86
|
+
const valids = this._valids && Array.from(this._valids._values).filter((v) => v !== null);
|
|
87
|
+
let typesOverlap = true;
|
|
88
|
+
|
|
89
|
+
// If 'only' is set, check if the allowed values' types overlap with the schema type
|
|
90
|
+
|
|
91
|
+
if (valids && valids.length && isOnly && !isTypeAny) {
|
|
92
|
+
const types = new Set(valids.map((v) => typeof v));
|
|
93
|
+
typesOverlap = types.has(this.type) || (this.type === 'date' && types.has('object'));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Set the JSON Schema 'type' if it's a standard type and there's an overlap
|
|
97
|
+
|
|
98
|
+
if (!isTypeAny && typesOverlap && internals.standardTypes.has(this.type)) {
|
|
99
|
+
schema.type = this.type;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (this._flags.description) {
|
|
103
|
+
schema.description = this._flags.description;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (this._flags.default !== undefined && typeof this._flags.default !== 'function') {
|
|
107
|
+
schema.default = this._flags.default;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Apply type-specific JSON Schema conversion
|
|
111
|
+
|
|
112
|
+
const subOptions = { ...options, $defs: defs };
|
|
113
|
+
if (this._definition.jsonSchema && typesOverlap) {
|
|
114
|
+
schema = this._definition.jsonSchema(this, schema, mode, subOptions);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Apply rule-specific JSON Schema conversions
|
|
118
|
+
|
|
119
|
+
for (const rule of this._rules) {
|
|
120
|
+
const definition = this._definition.rules[rule.name];
|
|
121
|
+
if (definition.jsonSchema && typesOverlap) {
|
|
122
|
+
schema = definition.jsonSchema(rule, schema, isOnly, mode, subOptions);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Handle shared schemas
|
|
127
|
+
|
|
128
|
+
if (this.$_terms.shared) {
|
|
129
|
+
for (const shared of this.$_terms.shared) {
|
|
130
|
+
defs[shared._flags.id] = shared.$_jsonSchema(mode, subOptions);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (rootCall && Object.keys(defs).length) {
|
|
135
|
+
schema.$defs = defs;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Handle allowed values (valids)
|
|
139
|
+
|
|
140
|
+
if (this._valids) {
|
|
141
|
+
|
|
142
|
+
const values = valids.filter((v) => typeof v !== 'symbol');
|
|
143
|
+
if (values.length) {
|
|
144
|
+
if (this._flags.only) {
|
|
145
|
+
schema.enum = values;
|
|
146
|
+
|
|
147
|
+
const list = Common.intersect(new Set(values.map((v) => typeof v)), internals.primitiveTypes);
|
|
148
|
+
|
|
149
|
+
if (list.size) {
|
|
150
|
+
const types = [...list];
|
|
151
|
+
schema.type = types.length === 1 ? types[0] : types;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// If values are allowed but not exclusive, add them via 'anyOf' if they differ from the main type
|
|
156
|
+
|
|
157
|
+
const otherTypes = values.filter((v) => typeof v !== this.type || isTypeAny);
|
|
158
|
+
if (otherTypes.length && !(isTypeAny && !isOnly)) {
|
|
159
|
+
if (!schema.anyOf) {
|
|
160
|
+
schema = {
|
|
161
|
+
anyOf: [schema]
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
schema.anyOf.push({ enum: otherTypes });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Handle 'null' if it's an allowed value
|
|
172
|
+
|
|
173
|
+
if (this._valids && this._valids.has(null) && !(isTypeAny && !isOnly)) {
|
|
174
|
+
if (this._valids.length === 1 && (isTypeAny || isOnly)) {
|
|
175
|
+
schema.type = 'null';
|
|
176
|
+
}
|
|
177
|
+
else if (schema.type) {
|
|
178
|
+
schema.type = [schema.type, 'null'];
|
|
179
|
+
}
|
|
180
|
+
else if (schema.anyOf) {
|
|
181
|
+
schema.anyOf.unshift(internals.nullSchema());
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
schema = {
|
|
185
|
+
anyOf: [
|
|
186
|
+
internals.nullSchema(),
|
|
187
|
+
schema
|
|
188
|
+
]
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Handle conditionals (whens) by generating multiple possible schemas combined with 'anyOf'
|
|
194
|
+
|
|
195
|
+
if (this.$_terms.whens) {
|
|
196
|
+
|
|
197
|
+
const base = this.clone();
|
|
198
|
+
base.$_terms.whens = null;
|
|
199
|
+
|
|
200
|
+
const matches = [];
|
|
201
|
+
for (const when of this.$_terms.whens) {
|
|
202
|
+
const tests = when.is ? [when] : when.switch;
|
|
203
|
+
for (let i = 0; i < tests.length; ++i) {
|
|
204
|
+
const test = tests[i];
|
|
205
|
+
if (test.then) {
|
|
206
|
+
matches.push(base.concat(test.then).$_jsonSchema(mode, subOptions));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (test.otherwise) {
|
|
210
|
+
matches.push(base.concat(test.otherwise).$_jsonSchema(mode, subOptions));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!test.then || (i === tests.length - 1 && !test.otherwise)) {
|
|
214
|
+
matches.push(base.$_jsonSchema(mode, subOptions));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const results = [];
|
|
220
|
+
for (const match of matches) {
|
|
221
|
+
if (!results.some((r) => deepEqual(r, match))) {
|
|
222
|
+
results.push(match);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return { anyOf: results };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return schema;
|
|
230
|
+
}
|
|
231
|
+
|
|
65
232
|
// Rules
|
|
66
233
|
|
|
67
234
|
allow(...values) {
|
|
@@ -1116,6 +1283,10 @@ internals.Base = class {
|
|
|
1116
1283
|
}
|
|
1117
1284
|
|
|
1118
1285
|
return mapToStandardError(result.error);
|
|
1286
|
+
},
|
|
1287
|
+
jsonSchema: {
|
|
1288
|
+
input: (options) => this.$_jsonSchema('input', options),
|
|
1289
|
+
output: (options) => this.$_jsonSchema('output', options)
|
|
1119
1290
|
}
|
|
1120
1291
|
};
|
|
1121
1292
|
}
|
package/lib/common.js
CHANGED
|
@@ -99,6 +99,25 @@ exports.default = function (value, defaultValue) {
|
|
|
99
99
|
};
|
|
100
100
|
|
|
101
101
|
|
|
102
|
+
exports.intersect = function (set, other) {
|
|
103
|
+
|
|
104
|
+
/* $lab:coverage:off$ */
|
|
105
|
+
if (typeof set.intersection === 'function') {
|
|
106
|
+
return set.intersection(other);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const result = new Set();
|
|
110
|
+
for (const item of set) {
|
|
111
|
+
if (other.has(item)) {
|
|
112
|
+
result.add(item);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return result;
|
|
117
|
+
/* $lab:coverage:on$ */
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
|
|
102
121
|
exports.isIsoDate = function (date) {
|
|
103
122
|
|
|
104
123
|
return internals.isoDate.test(date);
|
package/lib/extend.js
CHANGED
|
@@ -129,6 +129,12 @@ exports.type = function (from, options) {
|
|
|
129
129
|
|
|
130
130
|
def.rules = rules;
|
|
131
131
|
|
|
132
|
+
// JSON Schema
|
|
133
|
+
|
|
134
|
+
if (!def.jsonSchema) {
|
|
135
|
+
def.jsonSchema = parent.jsonSchema;
|
|
136
|
+
}
|
|
137
|
+
|
|
132
138
|
// Modifiers
|
|
133
139
|
|
|
134
140
|
const modifiers = Object.assign({}, parent.modifiers);
|
package/lib/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// TypeScript Version: 2.8
|
|
9
9
|
|
|
10
10
|
// TODO express type of Schema in a type-parameter (.default, .valid, .example etc)
|
|
11
|
-
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
11
|
+
import type { StandardJSONSchemaV1, StandardSchemaV1 } from "@standard-schema/spec";
|
|
12
12
|
|
|
13
13
|
declare namespace Joi {
|
|
14
14
|
type Types =
|
|
@@ -1003,7 +1003,14 @@ declare namespace Joi {
|
|
|
1003
1003
|
|
|
1004
1004
|
interface AnySchema<TSchema = any>
|
|
1005
1005
|
extends SchemaInternals,
|
|
1006
|
-
StandardSchemaV1<TSchema
|
|
1006
|
+
StandardSchemaV1<any, TSchema>,
|
|
1007
|
+
StandardJSONSchemaV1<any, TSchema> {
|
|
1008
|
+
/**
|
|
1009
|
+
* The Standard properties.
|
|
1010
|
+
*/
|
|
1011
|
+
readonly "~standard": StandardSchemaV1.Props<any, TSchema> &
|
|
1012
|
+
StandardJSONSchemaV1.Props<any, TSchema>;
|
|
1013
|
+
|
|
1007
1014
|
/**
|
|
1008
1015
|
* Flags of current schema.
|
|
1009
1016
|
*/
|
package/lib/schemas.js
CHANGED
|
@@ -80,7 +80,8 @@ internals.rule = Joi.object({
|
|
|
80
80
|
manifest: Joi.boolean(),
|
|
81
81
|
method: Joi.function().allow(false),
|
|
82
82
|
multi: Joi.boolean(),
|
|
83
|
-
validate: Joi.function()
|
|
83
|
+
validate: Joi.function(),
|
|
84
|
+
jsonSchema: Joi.function()
|
|
84
85
|
});
|
|
85
86
|
|
|
86
87
|
|
|
@@ -114,6 +115,7 @@ exports.extension = Joi.object({
|
|
|
114
115
|
prepare: Joi.function().maxArity(3),
|
|
115
116
|
rebuild: Joi.function().arity(1),
|
|
116
117
|
rules: Joi.object().pattern(internals.nameRx, internals.rule),
|
|
118
|
+
jsonSchema: Joi.function(),
|
|
117
119
|
terms: Joi.object().pattern(internals.nameRx, Joi.object({
|
|
118
120
|
init: Joi.array().allow(null).required(),
|
|
119
121
|
manifest: Joi.object().pattern(/.+/, [
|
|
@@ -146,6 +146,49 @@ module.exports = Any.extend({
|
|
|
146
146
|
return internals.errors(errors, helpers);
|
|
147
147
|
},
|
|
148
148
|
|
|
149
|
+
jsonSchema(schema, res, mode, options) {
|
|
150
|
+
|
|
151
|
+
const matches = [];
|
|
152
|
+
|
|
153
|
+
// Collect all alternative schemas from 'matches' term
|
|
154
|
+
|
|
155
|
+
for (const match of schema.$_terms.matches) {
|
|
156
|
+
if (match.schema) {
|
|
157
|
+
matches.push(match.schema.$_jsonSchema(mode, options));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// Handle conditional matches (when/switch)
|
|
161
|
+
|
|
162
|
+
const tests = match.is ? [match] : match.switch;
|
|
163
|
+
for (const test of tests) {
|
|
164
|
+
if (test.then) {
|
|
165
|
+
matches.push(test.then.$_jsonSchema(mode, options));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (test.otherwise) {
|
|
169
|
+
matches.push(test.otherwise.$_jsonSchema(mode, options));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (matches.length) {
|
|
176
|
+
delete res.type;
|
|
177
|
+
|
|
178
|
+
// Map alternatives to 'anyOf' or 'oneOf' based on the match flag
|
|
179
|
+
|
|
180
|
+
const matchMode = schema._flags.match ?? 'any';
|
|
181
|
+
if (matchMode === 'one') {
|
|
182
|
+
res.oneOf = matches;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
res.anyOf = matches;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return res;
|
|
190
|
+
},
|
|
191
|
+
|
|
149
192
|
rules: {
|
|
150
193
|
|
|
151
194
|
conditional: {
|
package/lib/types/array.js
CHANGED
|
@@ -68,6 +68,87 @@ module.exports = Any.extend({
|
|
|
68
68
|
return { value: value.slice() }; // Clone the array so that we don't modify the original
|
|
69
69
|
},
|
|
70
70
|
|
|
71
|
+
jsonSchema(schema, res, mode, options) {
|
|
72
|
+
|
|
73
|
+
const ordered = schema.$_terms.ordered;
|
|
74
|
+
|
|
75
|
+
// Handle ordered items (tuple-like) using 'prefixItems'
|
|
76
|
+
|
|
77
|
+
if (ordered.length) {
|
|
78
|
+
res.prefixItems = ordered.map((item) => item.$_jsonSchema(mode, options));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (schema.$_terms.items.length) {
|
|
82
|
+
let items;
|
|
83
|
+
if (schema.$_terms.items.length === 1) {
|
|
84
|
+
items = schema.$_terms.items[0].$_jsonSchema(mode, options);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
items = {
|
|
88
|
+
anyOf: schema.$_terms.items.map((item) => item.$_jsonSchema(mode, options))
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// If there are ordered items, remaining items are 'unevaluatedItems'
|
|
93
|
+
|
|
94
|
+
if (ordered.length) {
|
|
95
|
+
res.unevaluatedItems = items;
|
|
96
|
+
res.minItems = ordered.length;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
res.items = items;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else if (ordered.length) {
|
|
103
|
+
// No additional items allowed beyond the ordered ones
|
|
104
|
+
|
|
105
|
+
res.unevaluatedItems = false;
|
|
106
|
+
res.minItems = ordered.length;
|
|
107
|
+
res.maxItems = ordered.length;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Map 'has' rules to 'contains' in JSON Schema
|
|
111
|
+
|
|
112
|
+
const contains = [];
|
|
113
|
+
for (const rule of schema._rules) {
|
|
114
|
+
if (rule.name === 'has') {
|
|
115
|
+
contains.push(rule.args.schema.$_jsonSchema(mode, options));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (contains.length) {
|
|
120
|
+
if (contains.length === 1) {
|
|
121
|
+
res.contains = contains[0];
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
res.allOf = contains.map((item) => ({ contains: item }));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (schema._flags.single &&
|
|
129
|
+
schema.$_terms.items.length) {
|
|
130
|
+
|
|
131
|
+
let items;
|
|
132
|
+
if (schema.$_terms.items.length === 1) {
|
|
133
|
+
items = schema.$_terms.items[0].$_jsonSchema(mode, options);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
items = {
|
|
137
|
+
anyOf: schema.$_terms.items.map((item) => item.$_jsonSchema(mode, options))
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
res = {
|
|
142
|
+
anyOf: [
|
|
143
|
+
res,
|
|
144
|
+
items
|
|
145
|
+
]
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return res;
|
|
150
|
+
},
|
|
151
|
+
|
|
71
152
|
rules: {
|
|
72
153
|
|
|
73
154
|
has: {
|
|
@@ -368,6 +449,13 @@ module.exports = Any.extend({
|
|
|
368
449
|
|
|
369
450
|
return helpers.error('array.' + name, { limit: args.limit, value });
|
|
370
451
|
},
|
|
452
|
+
jsonSchema(rule, res) {
|
|
453
|
+
|
|
454
|
+
res.minItems = rule.args.limit;
|
|
455
|
+
res.maxItems = rule.args.limit;
|
|
456
|
+
|
|
457
|
+
return res;
|
|
458
|
+
},
|
|
371
459
|
args: [
|
|
372
460
|
{
|
|
373
461
|
name: 'limit',
|
|
@@ -382,6 +470,12 @@ module.exports = Any.extend({
|
|
|
382
470
|
method(limit) {
|
|
383
471
|
|
|
384
472
|
return this.$_addRule({ name: 'max', method: 'length', args: { limit }, operator: '<=' });
|
|
473
|
+
},
|
|
474
|
+
jsonSchema(rule, res) {
|
|
475
|
+
|
|
476
|
+
res.maxItems = rule.args.limit;
|
|
477
|
+
|
|
478
|
+
return res;
|
|
385
479
|
}
|
|
386
480
|
},
|
|
387
481
|
|
|
@@ -389,6 +483,12 @@ module.exports = Any.extend({
|
|
|
389
483
|
method(limit) {
|
|
390
484
|
|
|
391
485
|
return this.$_addRule({ name: 'min', method: 'length', args: { limit }, operator: '>=' });
|
|
486
|
+
},
|
|
487
|
+
jsonSchema(rule, res) {
|
|
488
|
+
|
|
489
|
+
res.minItems = rule.args.limit;
|
|
490
|
+
|
|
491
|
+
return res;
|
|
392
492
|
}
|
|
393
493
|
},
|
|
394
494
|
|
|
@@ -558,6 +658,12 @@ module.exports = Any.extend({
|
|
|
558
658
|
|
|
559
659
|
return value;
|
|
560
660
|
},
|
|
661
|
+
jsonSchema(rule, res) {
|
|
662
|
+
|
|
663
|
+
res.uniqueItems = true;
|
|
664
|
+
|
|
665
|
+
return res;
|
|
666
|
+
},
|
|
561
667
|
args: ['comparator', 'options'],
|
|
562
668
|
multi: true
|
|
563
669
|
}
|
package/lib/types/binary.js
CHANGED
|
@@ -33,6 +33,14 @@ module.exports = Any.extend({
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
|
|
36
|
+
jsonSchema(schema, res, mode, options) {
|
|
37
|
+
|
|
38
|
+
res.type = 'string';
|
|
39
|
+
res.format = 'binary';
|
|
40
|
+
|
|
41
|
+
return res;
|
|
42
|
+
},
|
|
43
|
+
|
|
36
44
|
rules: {
|
|
37
45
|
encoding: {
|
|
38
46
|
method(encoding) {
|
|
@@ -56,6 +64,12 @@ module.exports = Any.extend({
|
|
|
56
64
|
|
|
57
65
|
return helpers.error('binary.' + name, { limit: args.limit, value });
|
|
58
66
|
},
|
|
67
|
+
jsonSchema(rule, res) {
|
|
68
|
+
|
|
69
|
+
res.minLength = rule.args.limit;
|
|
70
|
+
res.maxLength = rule.args.limit;
|
|
71
|
+
return res;
|
|
72
|
+
},
|
|
59
73
|
args: [
|
|
60
74
|
{
|
|
61
75
|
name: 'limit',
|
|
@@ -70,6 +84,11 @@ module.exports = Any.extend({
|
|
|
70
84
|
method(limit) {
|
|
71
85
|
|
|
72
86
|
return this.$_addRule({ name: 'max', method: 'length', args: { limit }, operator: '<=' });
|
|
87
|
+
},
|
|
88
|
+
jsonSchema(rule, res) {
|
|
89
|
+
|
|
90
|
+
res.maxLength = rule.args.limit;
|
|
91
|
+
return res;
|
|
73
92
|
}
|
|
74
93
|
},
|
|
75
94
|
|
|
@@ -77,6 +96,11 @@ module.exports = Any.extend({
|
|
|
77
96
|
method(limit) {
|
|
78
97
|
|
|
79
98
|
return this.$_addRule({ name: 'min', method: 'length', args: { limit }, operator: '>=' });
|
|
99
|
+
},
|
|
100
|
+
jsonSchema(rule, res) {
|
|
101
|
+
|
|
102
|
+
res.minLength = rule.args.limit;
|
|
103
|
+
return res;
|
|
80
104
|
}
|
|
81
105
|
}
|
|
82
106
|
},
|
package/lib/types/date.js
CHANGED
|
@@ -7,7 +7,9 @@ const Common = require('../common');
|
|
|
7
7
|
const Template = require('../template');
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
const internals = {
|
|
10
|
+
const internals = {
|
|
11
|
+
formats: ['iso', 'javascript', 'unix']
|
|
12
|
+
};
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
internals.isDate = function (value) {
|
|
@@ -48,6 +50,14 @@ module.exports = Any.extend({
|
|
|
48
50
|
return { value, errors: error('date.format', { format }) };
|
|
49
51
|
},
|
|
50
52
|
|
|
53
|
+
jsonSchema(schema, res, mode, options) {
|
|
54
|
+
|
|
55
|
+
res.type = 'string';
|
|
56
|
+
res.format = 'date-time';
|
|
57
|
+
|
|
58
|
+
return res;
|
|
59
|
+
},
|
|
60
|
+
|
|
51
61
|
rules: {
|
|
52
62
|
|
|
53
63
|
compare: {
|
|
@@ -78,7 +88,7 @@ module.exports = Any.extend({
|
|
|
78
88
|
format: {
|
|
79
89
|
method(format) {
|
|
80
90
|
|
|
81
|
-
assert(
|
|
91
|
+
assert(internals.formats.includes(format), 'Unknown date format', format);
|
|
82
92
|
|
|
83
93
|
return this.$_setFlag('format', format);
|
|
84
94
|
}
|
|
@@ -88,6 +98,15 @@ module.exports = Any.extend({
|
|
|
88
98
|
method(date) {
|
|
89
99
|
|
|
90
100
|
return this.$_addRule({ name: 'greater', method: 'compare', args: { date }, operator: '>' });
|
|
101
|
+
},
|
|
102
|
+
jsonSchema(rule, res) {
|
|
103
|
+
|
|
104
|
+
const date = rule.args.date;
|
|
105
|
+
if (date instanceof Date) {
|
|
106
|
+
res['x-constraint'] = { ...res['x-constraint'], greater: date.toISOString() };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return res;
|
|
91
110
|
}
|
|
92
111
|
},
|
|
93
112
|
|
|
@@ -102,6 +121,15 @@ module.exports = Any.extend({
|
|
|
102
121
|
method(date) {
|
|
103
122
|
|
|
104
123
|
return this.$_addRule({ name: 'less', method: 'compare', args: { date }, operator: '<' });
|
|
124
|
+
},
|
|
125
|
+
jsonSchema(rule, res) {
|
|
126
|
+
|
|
127
|
+
const date = rule.args.date;
|
|
128
|
+
if (date instanceof Date) {
|
|
129
|
+
res['x-constraint'] = { ...res['x-constraint'], less: date.toISOString() };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return res;
|
|
105
133
|
}
|
|
106
134
|
},
|
|
107
135
|
|
|
@@ -109,6 +137,15 @@ module.exports = Any.extend({
|
|
|
109
137
|
method(date) {
|
|
110
138
|
|
|
111
139
|
return this.$_addRule({ name: 'max', method: 'compare', args: { date }, operator: '<=' });
|
|
140
|
+
},
|
|
141
|
+
jsonSchema(rule, res) {
|
|
142
|
+
|
|
143
|
+
const date = rule.args.date;
|
|
144
|
+
if (date instanceof Date) {
|
|
145
|
+
res['x-constraint'] = { ...res['x-constraint'], max: date.toISOString() };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return res;
|
|
112
149
|
}
|
|
113
150
|
},
|
|
114
151
|
|
|
@@ -116,6 +153,15 @@ module.exports = Any.extend({
|
|
|
116
153
|
method(date) {
|
|
117
154
|
|
|
118
155
|
return this.$_addRule({ name: 'min', method: 'compare', args: { date }, operator: '>=' });
|
|
156
|
+
},
|
|
157
|
+
jsonSchema(rule, res) {
|
|
158
|
+
|
|
159
|
+
const date = rule.args.date;
|
|
160
|
+
if (date instanceof Date) {
|
|
161
|
+
res['x-constraint'] = { ...res['x-constraint'], min: date.toISOString() };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return res;
|
|
119
165
|
}
|
|
120
166
|
},
|
|
121
167
|
|