json-2-joi 2.3.2 → 2.4.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 CHANGED
@@ -1,358 +1,359 @@
1
- ![Test](https://github.com/kenspirit/json-2-joi/workflows/Test/badge.svg) [![npm version](https://badge.fury.io/js/json-2-joi.svg)](https://badge.fury.io/js/json-2-joi)
2
-
3
- # json-2-joi
4
-
5
- Converts a JSON schema to a Joi schema for object validation.
6
-
7
- ## Change based on the forked version (enjoi):
8
-
9
- - Provided `refineDescription(schema)`, `allowNull`, `forbidArrayNull`, `strictRequired`, `strictEnum`, `enableEnum` options
10
- - Change cycle reference resovling without self-generating id and rely on $id if defined in JSON schema
11
- - Support `const` keyword in JSON schema
12
- - Support `example` setting for Joi based on JSON schema's `examples`, `default` or `enum`
13
- - Support `contains` keyword in JSON schema using Joi's `has` method
14
- - Support `$anchor` keyword in JSON schema
15
- - Support providing `joiOptions` for JOI default instance creation.
16
- - Support directly passing a Joi instance for schema building.
17
- - Remove `defaults` API. Please refer to section **Reuse JOI Schema Resolver** for similar usage.
18
- - Remove `strictMode` and `useDefaults` option.
19
- - **Big Change** on `$ref` and `subSchemas`. Please refer to [test/test-references.js](test/test-references.js) for detail usage. Currently, four format of `$ref` are supported:
20
- * id
21
- * [baseUri]#anchor
22
- * [baseUri]#/$defs/shared
23
- * [baseUri]#/$defs/shared/properties/level1
24
-
25
- **subSchemas will be modified** for two reason:
26
- * If any subSchema does not have `$id` field, it will be added using the corresponding key in `subSchemas` object.
27
- * New key-value pair will be added into it if the schema or any its subschema being parsed has `$id` field.
28
-
29
- ## Usage
30
-
31
- ### Schema Support
32
-
33
- `json-2-joi` does not support all of json-schema.
34
-
35
- Here is a list of some known missing keyword support still being worked on:
36
-
37
- - `object:patternProperties` - unsupported due to Joi limitations.
38
-
39
- Please file issues for other unsupported features.
40
-
41
- ### API
42
-
43
- - `json2Joi.schema(schema [, options, joiInstance])`
44
- - `schema` - a JSON schema or a string type representation (such as `'integer'`).
45
- - `options` - an (optional) object of additional options such as `subSchemas` and custom `types`.
46
- - `joiInstance` - a (optional) Joi instance to be used. When passed, it will be used directly. `extensions` option does not take effect and the Object/Array auto coerce extensions will not be included as well.
47
- - `json2Joi.resolver(options [, joiInstance])` - Creates a schema resolver based on `options`. `joiInstance` is same as above.
48
-
49
- ### Options
50
-
51
- - `subSchemas` - an (optional) object with keys representing schema ids, and values representing schemas.
52
- - `extensions` - an array of extensions to pass [joi.extend](https://github.com/hapijs/joi/blob/master/API.md#extendextension).
53
- - `joiOptions` - Options directly passed to Joi for instance creation. If the `noDefaults` is set `true`, `default()` will NOT be called and applied to Joi schema.
54
- - `refineDescription(schema)` - an (optional) function to call to apply to the JSON schema so that it's possible to return customized description.
55
- - `refineType(type, format)` - an (optional) function to call to apply to type based on the type and format of the JSON schema.
56
- - `refineSchema(joiSchema, jsonSchema)` - an (optional) function to call to apply to adjust Joi schema base on the original JSON schema. Primary use case is handling `nullable` flag in OpenAPI 3.0
57
- - `allowNull` - Default as `false`. When `true` and the field has no `enum` setting, `null` value will be allowed.
58
- - `forbidArrayNull` - Default as `true`. When `false` and `allowNull` is true, `null` value will be allowed for array type field.
59
- - `strictRequired` - Default as `false`. When `true`, `null` (and empty string additionally for string type) will NOT be allowed for those `required` field in object.
60
- - `strictEnum` - Default as `true`. When `false`, and `allowNull` is `true`, `null` (and empty string additionally for string type) will be added to `enum` list.
61
- - `enableEnum` - Default as `true`. When `false`, `enum` restriction will not be added to Joi Schema.
62
-
63
- Example:
64
-
65
- ```javascript
66
- const Joi = require('joi');
67
- const Json2Joi = require('json-2-joi');
68
-
69
- const schema = Json2Joi.schema({
70
- type: 'object',
71
- properties: {
72
- firstName: {
73
- description: 'First name.',
74
- type: 'string'
75
- },
76
- lastName: {
77
- description: 'Last name.',
78
- type: 'string'
79
- },
80
- age: {
81
- description: 'Age in years',
82
- type: 'integer',
83
- minimum: 1
84
- }
85
- },
86
- 'required': ['firstName', 'lastName']
87
- });
88
-
89
- const { error, value } = schema.validate({firstName: 'John', lastName: 'Doe', age: 45});
90
- ```
91
- schema.additionalProperties !== false
92
- ### joiOptions
93
-
94
- When this option is provided, a Joi instance will be created as `joiInstance = Joi.defaults((schema) => schema.options(joiOptions));` for building Joi Schema.
95
-
96
- As a result:
97
- * `unknown` setting (originally set by schema.additionalProperties !== false) of the schema will take effect only if `allowUnknown` is NOT provided in `joiOptions`.
98
- * Original `useDefaults` option will be replaced by `noDefaults` in `joiOptions`.
99
- * Original `strictMode` option will be replaced by `convert` in `joiOptions`.
100
-
101
- ### Sub Schemas
102
-
103
- Sub-schemas can be provided through the `subSchemas` option for `$ref` values to lookup against.
104
-
105
- Example:
106
-
107
- ```javascript
108
- const schema = Json2Joi.schema({
109
- $id: 'top',
110
- type: 'object',
111
- properties: {
112
- product: {
113
- $ref: 'prod'
114
- },
115
- suggestions: {
116
- type: 'array',
117
- items: {
118
- type: 'object',
119
- properties: {
120
- product: {
121
- $ref: 'product'
122
- },
123
- weight: {
124
- $ref: 'measurement'
125
- }
126
- }
127
- }
128
- }
129
- }
130
- }, {
131
- subSchemas: {
132
- measurement: {
133
- type: 'object',
134
- properties: {
135
- quantity: {
136
- $anchor: 'subNumber',
137
- type: 'number'
138
- },
139
- unit: {
140
- type: 'string'
141
- }
142
- }
143
- },
144
- product: {
145
- $id: 'prod',
146
- type: 'object',
147
- properties: {
148
- isUsed: {
149
- $anchor: 'subBoolean',
150
- type: 'boolean'
151
- },
152
- isDangerous: {
153
- $ref: '#subBoolean'
154
- },
155
- width: {
156
- $ref: 'measurement'
157
- },
158
- length: {
159
- $ref: '#/properties/width'
160
- },
161
- price: {
162
- $ref: 'measurement#subNumber'
163
- }
164
- }
165
- }
166
- }
167
- });
168
- ```
169
-
170
- ### Reuse JOI Schema Resolver
171
-
172
- Sometimes, we might have a base set of `subSchemas` which is used for building a large number of JSON schema and so we do not want to parse these common `subSchemas` again and again. We can first construct a JOI Schema Resolver first and then convert other JSON one by one.
173
-
174
- ```javascript
175
-
176
- const json2Joi = Json2Joi.resolver({
177
- subSchemas: {
178
- measurement: {
179
- type: 'object',
180
- properties: {
181
- quantity: {
182
- $anchor: 'subNumber',
183
- type: 'number'
184
- },
185
- unit: {
186
- type: 'string'
187
- }
188
- }
189
- }
190
- }
191
- });
192
-
193
- const weightSchema = json2Joi.convert({
194
- type: 'object',
195
- properties: {
196
- weight: {
197
- $ref: 'measurement'
198
- }
199
- }
200
- });
201
-
202
- const lengthSchema = json2Joi.convert({
203
- type: 'object',
204
- properties: {
205
- length: {
206
- $ref: 'measurement'
207
- }
208
- }
209
- });
210
- ```
211
-
212
- #### Override options during conversion
213
-
214
- When reusing the Resolver and calling `convert` API, it's possible that different schema might have different requirement.
215
-
216
- It's possible to override these options for each conversion:
217
-
218
- * refineType
219
- * refineSchema
220
- * refineDescription
221
- * allowNull
222
- * forbidArrayNull
223
- * strictRequired
224
- * strictEnum
225
- * enableEnum
226
- * noDefaults (Copied from `joiOptions` for the construction time)
227
-
228
- However, the overriden options do not have impact on the `subSchemas` as they are preprocessed during Resolver construction.
229
-
230
- ```javascript
231
- const subSchemas = {
232
- measurement: {
233
- type: 'object',
234
- properties: {
235
- quantity: {
236
- type: 'number',
237
- enum: [0, 1]
238
- }
239
- }
240
- }
241
- };
242
- const enjoi = Enjoi.resolver({
243
- subSchemas,
244
- enableEnum: false
245
- });
246
-
247
- const jsonSchema = {
248
- type: 'object',
249
- properties: {
250
- weight: {
251
- type: 'object',
252
- properties: {
253
- quantity: {
254
- type: 'number',
255
- enum: [0, 1]
256
- }
257
- }
258
- },
259
- length: {
260
- $ref: 'measurement'
261
- }
262
- }
263
- };
264
-
265
- const schema1 = enjoi.convert(jsonSchema, { enableEnum: true });
266
- const schema2 = enjoi.convert(jsonSchema);
267
-
268
- t.ok(schema1.validate({ weight: { quantity: 2 } }).error, 'follow overridden options');
269
- t.ok(!schema1.validate({ length: { quantity: 2 } }).error, 'no impact on subschemas');
270
- t.ok(!schema2.validate({ weight: { quantity: 2 } }).error, 'still use original options');
271
- ```
272
-
273
- ### Custom Types
274
-
275
- Custom types can be provided through the `extensions` option.
276
-
277
- ```javascript
278
- const schema = Json2Joi.schema({
279
- type: 'thing'
280
- }, {
281
- extensions: [{
282
- type: 'thing',
283
- base: Joi.any()
284
- }]
285
- });
286
- ```
287
-
288
- Also with functions.
289
-
290
- ```javascript
291
- const schema = Json2Joi.schema({
292
- type: 'thing'
293
- }, {
294
- extensions: [{
295
- type: 'thing',
296
- validate(value, helpers) {
297
- if (value !== 'foobar') {
298
- return { value, errors: helpers.error('thing.foobar') };
299
- }
300
- },
301
- messages: {
302
- 'thing.foobar': '{#label} must be \'foobar\''
303
- }
304
- }]
305
- });
306
- ```
307
-
308
- ### Refine Type
309
-
310
- You can use the refine type function to help refine types based on `type` and `format`. This will allow transforming a type for lookup.
311
-
312
- ```javascript
313
- const schema = Json2Joi.schema({
314
- type: 'string',
315
- format: 'email'
316
- }, {
317
- extensions: [{
318
- type: 'email',
319
- base: Joi.string().email()
320
- }],
321
- refineType(type, format) {
322
- if (type === 'string' && format === 'email') {
323
- return 'email';
324
- }
325
- }
326
- });
327
- ```
328
-
329
- This can be used in conjunction with function based `extensions` for additional logic:
330
-
331
- ```javascript
332
- const schemaDesc = {
333
- type: 'string',
334
- format: 'email',
335
- 'x-test': true
336
- }
337
- const schema = Json2Joi.schema(schemaDesc, {
338
- extensions: [{
339
- type: 'email',
340
- validate(value, helpers) {
341
- const validator = schemaDesc['x-test'] ? Joi.string().email().equal('test@example.com') : Joi.string().email();
342
- const validation = validator.validate(value);
343
- if (validation.error) {
344
- return { value, errors: validation.error };
345
- }
346
- }
347
- }],
348
- refineType(type, format) {
349
- if (type === 'string' && format === 'email') {
350
- return 'email';
351
- }
352
- }
353
- });
354
- ```
355
-
356
- ### Extensions
357
-
358
- Refer to Joi documentation on extensions: https://hapi.dev/module/joi/api/?v=17#extensions
1
+ ![Test](https://github.com/kenspirit/json-2-joi/workflows/Test/badge.svg) [![npm version](https://badge.fury.io/js/json-2-joi.svg)](https://badge.fury.io/js/json-2-joi)
2
+
3
+ # json-2-joi
4
+
5
+ Converts a JSON schema to a Joi schema for object validation.
6
+
7
+ ## Change based on the forked version (enjoi):
8
+
9
+ - Provided `refineDescription(schema)`, `allowNull`, `forbidArrayNull`, `strictRequired`, `strictEnum`, `enableEnum`, `customizedNullValues` options
10
+ - Change cycle reference resovling without self-generating id and rely on $id if defined in JSON schema
11
+ - Support `const` keyword in JSON schema
12
+ - Support `example` setting for Joi based on JSON schema's `examples`, `default` or `enum`
13
+ - Support `contains` keyword in JSON schema using Joi's `has` method
14
+ - Support `$anchor` keyword in JSON schema
15
+ - Support providing `joiOptions` for JOI default instance creation.
16
+ - Support directly passing a Joi instance for schema building.
17
+ - Remove `defaults` API. Please refer to section **Reuse JOI Schema Resolver** for similar usage.
18
+ - Remove `strictMode` and `useDefaults` option.
19
+ - **Big Change** on `$ref` and `subSchemas`. Please refer to [test/test-references.js](test/test-references.js) for detail usage. Currently, four format of `$ref` are supported:
20
+ * id
21
+ * [baseUri]#anchor
22
+ * [baseUri]#/$defs/shared
23
+ * [baseUri]#/$defs/shared/properties/level1
24
+
25
+ **subSchemas will be modified** for two reason:
26
+ * If any subSchema does not have `$id` field, it will be added using the corresponding key in `subSchemas` object.
27
+ * New key-value pair will be added into it if the schema or any its subschema being parsed has `$id` field.
28
+
29
+ ## Usage
30
+
31
+ ### Schema Support
32
+
33
+ `json-2-joi` does not support all of json-schema.
34
+
35
+ Here is a list of some known missing keyword support still being worked on:
36
+
37
+ - `object:patternProperties` - unsupported due to Joi limitations.
38
+
39
+ Please file issues for other unsupported features.
40
+
41
+ ### API
42
+
43
+ - `json2Joi.schema(schema [, options, joiInstance])`
44
+ - `schema` - a JSON schema or a string type representation (such as `'integer'`).
45
+ - `options` - an (optional) object of additional options such as `subSchemas` and custom `types`.
46
+ - `joiInstance` - a (optional) Joi instance to be used. When passed, it will be used directly. `extensions` option does not take effect and the Object/Array auto coerce extensions will not be included as well.
47
+ - `json2Joi.resolver(options [, joiInstance])` - Creates a schema resolver based on `options`. `joiInstance` is same as above.
48
+
49
+ ### Options
50
+
51
+ - `subSchemas` - an (optional) object with keys representing schema ids, and values representing schemas.
52
+ - `extensions` - an array of extensions to pass [joi.extend](https://github.com/hapijs/joi/blob/master/API.md#extendextension).
53
+ - `joiOptions` - Options directly passed to Joi for instance creation. If the `noDefaults` is set `true`, `default()` will NOT be called and applied to Joi schema.
54
+ - `refineDescription(schema)` - an (optional) function to call to apply to the JSON schema so that it's possible to return customized description.
55
+ - `refineType(type, format)` - an (optional) function to call to apply to type based on the type and format of the JSON schema.
56
+ - `refineSchema(joiSchema, jsonSchema)` - an (optional) function to call to apply to adjust Joi schema base on the original JSON schema. Primary use case is handling `nullable` flag in OpenAPI 3.0
57
+ - `allowNull` - Default as `false`. When `true` and the field has no `enum` setting, `null` value will be allowed.
58
+ - `customizedNullValues` - Default as `[null]`. When provided, the value in the array will be treated valid null value when schema is 'null' type or other allowing null case.
59
+ - `forbidArrayNull` - Default as `true`. When `false` and `allowNull` is true, `null` value will be allowed for array type field.
60
+ - `strictRequired` - Default as `false`. When `true`, `null` (and empty string additionally for string type) will NOT be allowed for those `required` field in object.
61
+ - `strictEnum` - Default as `true`. When `false`, and `allowNull` is `true`, `null` (and empty string additionally for string type) will be added to `enum` list.
62
+ - `enableEnum` - Default as `true`. When `false`, `enum` restriction will not be added to Joi Schema.
63
+
64
+ Example:
65
+
66
+ ```javascript
67
+ const Joi = require('joi');
68
+ const Json2Joi = require('json-2-joi');
69
+
70
+ const schema = Json2Joi.schema({
71
+ type: 'object',
72
+ properties: {
73
+ firstName: {
74
+ description: 'First name.',
75
+ type: 'string'
76
+ },
77
+ lastName: {
78
+ description: 'Last name.',
79
+ type: 'string'
80
+ },
81
+ age: {
82
+ description: 'Age in years',
83
+ type: 'integer',
84
+ minimum: 1
85
+ }
86
+ },
87
+ 'required': ['firstName', 'lastName']
88
+ });
89
+
90
+ const { error, value } = schema.validate({firstName: 'John', lastName: 'Doe', age: 45});
91
+ ```
92
+ schema.additionalProperties !== false
93
+ ### joiOptions
94
+
95
+ When this option is provided, a Joi instance will be created as `joiInstance = Joi.defaults((schema) => schema.options(joiOptions));` for building Joi Schema.
96
+
97
+ As a result:
98
+ * `unknown` setting (originally set by schema.additionalProperties !== false) of the schema will take effect only if `allowUnknown` is NOT provided in `joiOptions`.
99
+ * Original `useDefaults` option will be replaced by `noDefaults` in `joiOptions`.
100
+ * Original `strictMode` option will be replaced by `convert` in `joiOptions`.
101
+
102
+ ### Sub Schemas
103
+
104
+ Sub-schemas can be provided through the `subSchemas` option for `$ref` values to lookup against.
105
+
106
+ Example:
107
+
108
+ ```javascript
109
+ const schema = Json2Joi.schema({
110
+ $id: 'top',
111
+ type: 'object',
112
+ properties: {
113
+ product: {
114
+ $ref: 'prod'
115
+ },
116
+ suggestions: {
117
+ type: 'array',
118
+ items: {
119
+ type: 'object',
120
+ properties: {
121
+ product: {
122
+ $ref: 'product'
123
+ },
124
+ weight: {
125
+ $ref: 'measurement'
126
+ }
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }, {
132
+ subSchemas: {
133
+ measurement: {
134
+ type: 'object',
135
+ properties: {
136
+ quantity: {
137
+ $anchor: 'subNumber',
138
+ type: 'number'
139
+ },
140
+ unit: {
141
+ type: 'string'
142
+ }
143
+ }
144
+ },
145
+ product: {
146
+ $id: 'prod',
147
+ type: 'object',
148
+ properties: {
149
+ isUsed: {
150
+ $anchor: 'subBoolean',
151
+ type: 'boolean'
152
+ },
153
+ isDangerous: {
154
+ $ref: '#subBoolean'
155
+ },
156
+ width: {
157
+ $ref: 'measurement'
158
+ },
159
+ length: {
160
+ $ref: '#/properties/width'
161
+ },
162
+ price: {
163
+ $ref: 'measurement#subNumber'
164
+ }
165
+ }
166
+ }
167
+ }
168
+ });
169
+ ```
170
+
171
+ ### Reuse JOI Schema Resolver
172
+
173
+ Sometimes, we might have a base set of `subSchemas` which is used for building a large number of JSON schema and so we do not want to parse these common `subSchemas` again and again. We can first construct a JOI Schema Resolver first and then convert other JSON one by one.
174
+
175
+ ```javascript
176
+
177
+ const json2Joi = Json2Joi.resolver({
178
+ subSchemas: {
179
+ measurement: {
180
+ type: 'object',
181
+ properties: {
182
+ quantity: {
183
+ $anchor: 'subNumber',
184
+ type: 'number'
185
+ },
186
+ unit: {
187
+ type: 'string'
188
+ }
189
+ }
190
+ }
191
+ }
192
+ });
193
+
194
+ const weightSchema = json2Joi.convert({
195
+ type: 'object',
196
+ properties: {
197
+ weight: {
198
+ $ref: 'measurement'
199
+ }
200
+ }
201
+ });
202
+
203
+ const lengthSchema = json2Joi.convert({
204
+ type: 'object',
205
+ properties: {
206
+ length: {
207
+ $ref: 'measurement'
208
+ }
209
+ }
210
+ });
211
+ ```
212
+
213
+ #### Override options during conversion
214
+
215
+ When reusing the Resolver and calling `convert` API, it's possible that different schema might have different requirement.
216
+
217
+ It's possible to override these options for each conversion:
218
+
219
+ * refineType
220
+ * refineSchema
221
+ * refineDescription
222
+ * allowNull
223
+ * forbidArrayNull
224
+ * strictRequired
225
+ * strictEnum
226
+ * enableEnum
227
+ * noDefaults (Copied from `joiOptions` for the construction time)
228
+
229
+ However, the overriden options do not have impact on the `subSchemas` as they are preprocessed during Resolver construction.
230
+
231
+ ```javascript
232
+ const subSchemas = {
233
+ measurement: {
234
+ type: 'object',
235
+ properties: {
236
+ quantity: {
237
+ type: 'number',
238
+ enum: [0, 1]
239
+ }
240
+ }
241
+ }
242
+ };
243
+ const enjoi = Enjoi.resolver({
244
+ subSchemas,
245
+ enableEnum: false
246
+ });
247
+
248
+ const jsonSchema = {
249
+ type: 'object',
250
+ properties: {
251
+ weight: {
252
+ type: 'object',
253
+ properties: {
254
+ quantity: {
255
+ type: 'number',
256
+ enum: [0, 1]
257
+ }
258
+ }
259
+ },
260
+ length: {
261
+ $ref: 'measurement'
262
+ }
263
+ }
264
+ };
265
+
266
+ const schema1 = enjoi.convert(jsonSchema, { enableEnum: true });
267
+ const schema2 = enjoi.convert(jsonSchema);
268
+
269
+ t.ok(schema1.validate({ weight: { quantity: 2 } }).error, 'follow overridden options');
270
+ t.ok(!schema1.validate({ length: { quantity: 2 } }).error, 'no impact on subschemas');
271
+ t.ok(!schema2.validate({ weight: { quantity: 2 } }).error, 'still use original options');
272
+ ```
273
+
274
+ ### Custom Types
275
+
276
+ Custom types can be provided through the `extensions` option.
277
+
278
+ ```javascript
279
+ const schema = Json2Joi.schema({
280
+ type: 'thing'
281
+ }, {
282
+ extensions: [{
283
+ type: 'thing',
284
+ base: Joi.any()
285
+ }]
286
+ });
287
+ ```
288
+
289
+ Also with functions.
290
+
291
+ ```javascript
292
+ const schema = Json2Joi.schema({
293
+ type: 'thing'
294
+ }, {
295
+ extensions: [{
296
+ type: 'thing',
297
+ validate(value, helpers) {
298
+ if (value !== 'foobar') {
299
+ return { value, errors: helpers.error('thing.foobar') };
300
+ }
301
+ },
302
+ messages: {
303
+ 'thing.foobar': '{#label} must be \'foobar\''
304
+ }
305
+ }]
306
+ });
307
+ ```
308
+
309
+ ### Refine Type
310
+
311
+ You can use the refine type function to help refine types based on `type` and `format`. This will allow transforming a type for lookup.
312
+
313
+ ```javascript
314
+ const schema = Json2Joi.schema({
315
+ type: 'string',
316
+ format: 'email'
317
+ }, {
318
+ extensions: [{
319
+ type: 'email',
320
+ base: Joi.string().email()
321
+ }],
322
+ refineType(type, format) {
323
+ if (type === 'string' && format === 'email') {
324
+ return 'email';
325
+ }
326
+ }
327
+ });
328
+ ```
329
+
330
+ This can be used in conjunction with function based `extensions` for additional logic:
331
+
332
+ ```javascript
333
+ const schemaDesc = {
334
+ type: 'string',
335
+ format: 'email',
336
+ 'x-test': true
337
+ }
338
+ const schema = Json2Joi.schema(schemaDesc, {
339
+ extensions: [{
340
+ type: 'email',
341
+ validate(value, helpers) {
342
+ const validator = schemaDesc['x-test'] ? Joi.string().email().equal('test@example.com') : Joi.string().email();
343
+ const validation = validator.validate(value);
344
+ if (validation.error) {
345
+ return { value, errors: validation.error };
346
+ }
347
+ }
348
+ }],
349
+ refineType(type, format) {
350
+ if (type === 'string' && format === 'email') {
351
+ return 'email';
352
+ }
353
+ }
354
+ });
355
+ ```
356
+
357
+ ### Extensions
358
+
359
+ Refer to Joi documentation on extensions: https://hapi.dev/module/joi/api/?v=17#extensions