UNPKG

24.4 kBMarkdownView Raw
1# ajv-keywords
2
3Custom JSON-Schema keywords for [Ajv](https://github.com/epoberezkin/ajv) validator
4
5[![build](https://github.com/ajv-validator/ajv-keywords/workflows/build/badge.svg)](https://github.com/ajv-validator/ajv-keywords/actions?query=workflow%3Abuild)
6[![npm](https://img.shields.io/npm/v/ajv-keywords.svg)](https://www.npmjs.com/package/ajv-keywords)
7[![npm downloads](https://img.shields.io/npm/dm/ajv-keywords.svg)](https://www.npmjs.com/package/ajv-keywords)
8[![coverage](https://coveralls.io/repos/github/ajv-validator/ajv-keywords/badge.svg?branch=master)](https://coveralls.io/github/ajv-validator/ajv-keywords?branch=master)
9[![gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv)
10
11**Please note**: This readme file is for [ajv-keywords v5.0.0](https://github.com/ajv-validator/ajv-keywords/releases/tag/v5.0.0) that should be used with [ajv v8](https://github.com/ajv-validator/ajv).
12
13[ajv-keywords v3](https://github.com/ajv-validator/ajv-keywords/tree/v3) should be used with [ajv v6](https://github.com/ajv-validator/ajv/tree/v6).
14
15## Contents
16
17- [Install](#install)
18- [Usage](#usage)
19- [Keywords](#keywords)
20 - [Types](#types)
21 - [typeof](#typeof)
22 - [instanceof](#instanceof)<sup>\+</sup>
23 - [Keywords for numbers](#keywords-for-numbers)
24 - [range and exclusiveRange](#range-and-exclusiverange)
25 - [Keywords for strings](#keywords-for-strings)
26 - [regexp](#regexp)
27 - [transform](#transform)<sup>\*</sup>
28 - [Keywords for arrays](#keywords-for-arrays)
29 - [uniqueItemProperties](#uniqueitemproperties)<sup>\+</sup>
30 - [Keywords for objects](#keywords-for-objects)
31 - [allRequired](#allrequired)
32 - [anyRequired](#anyrequired)
33 - [oneRequired](#onerequired)
34 - [patternRequired](#patternrequired)
35 - [prohibited](#prohibited)
36 - [deepProperties](#deepproperties)
37 - [deepRequired](#deeprequired)
38 - [dynamicDefaults](#dynamicdefaults)<sup>\*</sup><sup>\+</sup>
39 - [Keywords for all types](#keywords-for-all-types)
40 - [select/selectCases/selectDefault](#selectselectcasesselectdefault)
41- [Security contact](#security-contact)
42- [Open-source software support](#open-source-software-support)
43- [License](#license)
44
45<sup>\*</sup> - keywords that modify data
46<sup>\+</sup> - keywords that are not supported in [standalone validation code](https://github.com/ajv-validator/ajv/blob/master/docs/standalone.md)
47
48## Install
49
50To install version 4 to use with [Ajv v7](https://github.com/ajv-validator/ajv):
51
52```
53npm install ajv-keywords
54```
55
56## Usage
57
58To add all available keywords:
59
60```javascript
61const Ajv = require("ajv")
62const ajv = new Ajv()
63require("ajv-keywords")(ajv)
64
65ajv.validate({instanceof: "RegExp"}, /.*/) // true
66ajv.validate({instanceof: "RegExp"}, ".*") // false
67```
68
69To add a single keyword:
70
71```javascript
72require("ajv-keywords")(ajv, "instanceof")
73```
74
75To add multiple keywords:
76
77```javascript
78require("ajv-keywords")(ajv, ["typeof", "instanceof"])
79```
80
81To add a single keyword directly (to avoid adding unused code):
82
83```javascript
84require("ajv-keywords/dist/keywords/select")(ajv, opts)
85```
86
87To add all keywords via Ajv options:
88
89```javascript
90const ajv = new Ajv({keywords: require("ajv-keywords/dist/definitions")(opts)})
91```
92
93To add one or several keywords via options:
94
95```javascript
96const ajv = new Ajv({
97 keywords: [
98 require("ajv-keywords/dist/definitions/typeof")(),
99 require("ajv-keywords/dist/definitions/instanceof")(),
100 // select exports an array of 3 definitions - see "select" in docs
101 ...require("ajv-keywords/dist/definitions/select")(opts),
102 ],
103})
104```
105
106`opts` is an optional object with a property `defaultMeta` - URI of meta-schema to use for keywords that use subschemas (`select` and `deepProperties`). The default is `"http://json-schema.org/schema"`.
107
108## Keywords
109
110### Types
111
112#### `typeof`
113
114Based on JavaScript `typeof` operation.
115
116The value of the keyword should be a string (`"undefined"`, `"string"`, `"number"`, `"object"`, `"function"`, `"boolean"` or `"symbol"`) or an array of strings.
117
118To pass validation the result of `typeof` operation on the value should be equal to the string (or one of the strings in the array).
119
120```javascript
121ajv.validate({typeof: "undefined"}, undefined) // true
122ajv.validate({typeof: "undefined"}, null) // false
123ajv.validate({typeof: ["undefined", "object"]}, null) // true
124```
125
126#### `instanceof`
127
128Based on JavaScript `instanceof` operation.
129
130The value of the keyword should be a string (`"Object"`, `"Array"`, `"Function"`, `"Number"`, `"String"`, `"Date"`, `"RegExp"` or `"Promise"`) or an array of strings.
131
132To pass validation the result of `data instanceof ...` operation on the value should be true:
133
134```javascript
135ajv.validate({instanceof: "Array"}, []) // true
136ajv.validate({instanceof: "Array"}, {}) // false
137ajv.validate({instanceof: ["Array", "Function"]}, function () {}) // true
138```
139
140You can add your own constructor function to be recognised by this keyword:
141
142```javascript
143class MyClass {}
144const instanceofDef = require("ajv-keywords/dist/definitions/instanceof")
145instanceofDef.CONSTRUCTORS.MyClass = MyClass
146ajv.validate({instanceof: "MyClass"}, new MyClass()) // true
147```
148
149**Please note**: currently `instanceof` is not supported in [standalone validation code](https://github.com/ajv-validator/ajv/blob/master/docs/standalone.md) - it has to be implemented as [`code` keyword](https://github.com/ajv-validator/ajv/blob/master/docs/keywords.md#define-keyword-with-code-generation-function) to support it (PR is welcome).
150
151### Keywords for numbers
152
153#### `range` and `exclusiveRange`
154
155Syntax sugar for the combination of minimum and maximum keywords (or exclusiveMinimum and exclusiveMaximum), also fails schema compilation if there are no numbers in the range.
156
157The value of these keywords must be an array consisting of two numbers, the second must be greater or equal than the first one.
158
159If the validated value is not a number the validation passes, otherwise to pass validation the value should be greater (or equal) than the first number and smaller (or equal) than the second number in the array.
160
161```javascript
162const schema = {type: "number", range: [1, 3]}
163ajv.validate(schema, 1) // true
164ajv.validate(schema, 2) // true
165ajv.validate(schema, 3) // true
166ajv.validate(schema, 0.99) // false
167ajv.validate(schema, 3.01) // false
168
169const schema = {type: "number", exclusiveRange: [1, 3]}
170ajv.validate(schema, 1.01) // true
171ajv.validate(schema, 2) // true
172ajv.validate(schema, 2.99) // true
173ajv.validate(schema, 1) // false
174ajv.validate(schema, 3) // false
175```
176
177### Keywords for strings
178
179#### `regexp`
180
181This keyword allows to use regular expressions with flags in schemas, and also without `"u"` flag when needed (the standard `pattern` keyword does not support flags and implies the presence of `"u"` flag).
182
183This keyword applies only to strings. If the data is not a string, the validation succeeds.
184
185The value of this keyword can be either a string (the result of `regexp.toString()`) or an object with the properties `pattern` and `flags` (the same strings that should be passed to RegExp constructor).
186
187```javascript
188const schema = {
189 type: "object",
190 properties: {
191 foo: {type: "string", regexp: "/foo/i"},
192 bar: {type: "string", regexp: {pattern: "bar", flags: "i"}},
193 },
194}
195
196const validData = {
197 foo: "Food",
198 bar: "Barmen",
199}
200
201const invalidData = {
202 foo: "fog",
203 bar: "bad",
204}
205```
206
207#### `transform`
208
209This keyword allows a string to be modified during validation.
210
211This keyword applies only to strings. If the data is not a string, the `transform` keyword is ignored.
212
213A standalone string cannot be modified, i.e. `data = 'a'; ajv.validate(schema, data);`, because strings are passed by value
214
215**Supported transformations:**
216
217- `trim`: remove whitespace from start and end
218- `trimStart`/`trimLeft`: remove whitespace from start
219- `trimEnd`/`trimRight`: remove whitespace from end
220- `toLowerCase`: convert to lower case
221- `toUpperCase`: convert to upper case
222- `toEnumCase`: change string case to be equal to one of `enum` values in the schema
223
224Transformations are applied in the order they are listed.
225
226Note: `toEnumCase` requires that all allowed values are unique when case insensitive.
227
228**Example: multiple transformations**
229
230```javascript
231require("ajv-keywords")(ajv, "transform")
232
233const schema = {
234 type: "array",
235 items: {
236 type: "string",
237 transform: ["trim", "toLowerCase"],
238 },
239}
240
241const data = [" MixCase "]
242ajv.validate(schema, data)
243console.log(data) // ['mixcase']
244```
245
246**Example: `enumcase`**
247
248```javascript
249require("ajv-keywords")(ajv, ["transform"])
250
251const schema = {
252 type: "array",
253 items: {
254 type: "string",
255 transform: ["trim", "toEnumCase"],
256 enum: ["pH"],
257 },
258}
259
260const data = ["ph", " Ph", "PH", "pH "]
261ajv.validate(schema, data)
262console.log(data) // ['pH','pH','pH','pH']
263```
264
265### Keywords for arrays
266
267#### `uniqueItemProperties`
268
269The keyword allows to check that some properties in array items are unique.
270
271This keyword applies only to arrays. If the data is not an array, the validation succeeds.
272
273The value of this keyword must be an array of strings - property names that should have unique values across all items.
274
275```javascript
276const schema = {
277 type: "array",
278 uniqueItemProperties: ["id", "name"],
279}
280
281const validData = [{id: 1}, {id: 2}, {id: 3}]
282
283const invalidData1 = [
284 {id: 1},
285 {id: 1}, // duplicate "id"
286 {id: 3},
287]
288
289const invalidData2 = [
290 {id: 1, name: "taco"},
291 {id: 2, name: "taco"}, // duplicate "name"
292 {id: 3, name: "salsa"},
293]
294```
295
296This keyword is contributed by [@blainesch](https://github.com/blainesch).
297
298**Please note**: currently `uniqueItemProperties` is not supported in [standalone validation code](https://github.com/ajv-validator/ajv/blob/master/docs/standalone.md) - it has to be implemented as [`code` keyword](https://github.com/ajv-validator/ajv/blob/master/docs/keywords.md#define-keyword-with-code-generation-function) to support it (PR is welcome).
299
300### Keywords for objects
301
302#### `allRequired`
303
304This keyword allows to require the presence of all properties used in `properties` keyword in the same schema object.
305
306This keyword applies only to objects. If the data is not an object, the validation succeeds.
307
308The value of this keyword must be boolean.
309
310If the value of the keyword is `false`, the validation succeeds.
311
312If the value of the keyword is `true`, the validation succeeds if the data contains all properties defined in `properties` keyword (in the same schema object).
313
314If the `properties` keyword is not present in the same schema object, schema compilation will throw exception.
315
316```javascript
317const schema = {
318 type: "object",
319 properties: {
320 foo: {type: "number"},
321 bar: {type: "number"},
322 },
323 allRequired: true,
324}
325
326const validData = {foo: 1, bar: 2}
327const alsoValidData = {foo: 1, bar: 2, baz: 3}
328
329const invalidDataList = [{}, {foo: 1}, {bar: 2}]
330```
331
332#### `anyRequired`
333
334This keyword allows to require the presence of any (at least one) property from the list.
335
336This keyword applies only to objects. If the data is not an object, the validation succeeds.
337
338The value of this keyword must be an array of strings, each string being a property name. For data object to be valid at least one of the properties in this array should be present in the object.
339
340```javascript
341const schema = {
342 type: "object",
343 anyRequired: ["foo", "bar"],
344}
345
346const validData = {foo: 1}
347const alsoValidData = {foo: 1, bar: 2}
348
349const invalidDataList = [{}, {baz: 3}]
350```
351
352#### `oneRequired`
353
354This keyword allows to require the presence of only one property from the list.
355
356This keyword applies only to objects. If the data is not an object, the validation succeeds.
357
358The value of this keyword must be an array of strings, each string being a property name. For data object to be valid exactly one of the properties in this array should be present in the object.
359
360```javascript
361const schema = {
362 type: "object",
363 oneRequired: ["foo", "bar"],
364}
365
366const validData = {foo: 1}
367const alsoValidData = {bar: 2, baz: 3}
368
369const invalidDataList = [{}, {baz: 3}, {foo: 1, bar: 2}]
370```
371
372#### `patternRequired`
373
374This keyword allows to require the presence of properties that match some pattern(s).
375
376This keyword applies only to objects. If the data is not an object, the validation succeeds.
377
378The value of this keyword should be an array of strings, each string being a regular expression. For data object to be valid each regular expression in this array should match at least one property name in the data object.
379
380If the array contains multiple regular expressions, more than one expression can match the same property name.
381
382```javascript
383const schema = {
384 type: "object",
385 patternRequired: ["f.*o", "b.*r"],
386}
387
388const validData = {foo: 1, bar: 2}
389const alsoValidData = {foobar: 3}
390
391const invalidDataList = [{}, {foo: 1}, {bar: 2}]
392```
393
394#### `prohibited`
395
396This keyword allows to prohibit that any of the properties in the list is present in the object.
397
398This keyword applies only to objects. If the data is not an object, the validation succeeds.
399
400The value of this keyword should be an array of strings, each string being a property name. For data object to be valid none of the properties in this array should be present in the object.
401
402```javascript
403const schema = {
404 type: "object",
405 prohibited: ["foo", "bar"],
406}
407
408const validData = {baz: 1}
409const alsoValidData = {}
410
411const invalidDataList = [{foo: 1}, {bar: 2}, {foo: 1, bar: 2}]
412```
413
414**Please note**: `{prohibited: ['foo', 'bar']}` is equivalent to `{not: {anyRequired: ['foo', 'bar']}}` (i.e. it has the same validation result for any data).
415
416#### `deepProperties`
417
418This keyword allows to validate deep properties (identified by JSON pointers).
419
420This keyword applies only to objects. If the data is not an object, the validation succeeds.
421
422The value should be an object, where keys are JSON pointers to the data, starting from the current position in data, and the values are JSON schemas. For data object to be valid the value of each JSON pointer should be valid according to the corresponding schema.
423
424```javascript
425const schema = {
426 type: "object",
427 deepProperties: {
428 "/users/1/role": {enum: ["admin"]},
429 },
430}
431
432const validData = {
433 users: [
434 {},
435 {
436 id: 123,
437 role: "admin",
438 },
439 ],
440}
441
442const alsoValidData = {
443 users: {
444 1: {
445 id: 123,
446 role: "admin",
447 },
448 },
449}
450
451const invalidData = {
452 users: [
453 {},
454 {
455 id: 123,
456 role: "user",
457 },
458 ],
459}
460
461const alsoInvalidData = {
462 users: {
463 1: {
464 id: 123,
465 role: "user",
466 },
467 },
468}
469```
470
471#### `deepRequired`
472
473This keyword allows to check that some deep properties (identified by JSON pointers) are available.
474
475This keyword applies only to objects. If the data is not an object, the validation succeeds.
476
477The value should be an array of JSON pointers to the data, starting from the current position in data. For data object to be valid each JSON pointer should be some existing part of the data.
478
479```javascript
480const schema = {
481 type: "object",
482 deepRequired: ["/users/1/role"],
483}
484
485const validData = {
486 users: [
487 {},
488 {
489 id: 123,
490 role: "admin",
491 },
492 ],
493}
494
495const invalidData = {
496 users: [
497 {},
498 {
499 id: 123,
500 },
501 ],
502}
503```
504
505See [json-schema-org/json-schema-spec#203](https://github.com/json-schema-org/json-schema-spec/issues/203#issue-197211916) for an example of the equivalent schema without `deepRequired` keyword.
506
507### Keywords for all types
508
509#### `select`/`selectCases`/`selectDefault`
510
511**Please note**: these keywords are deprecated. It is recommended to use OpenAPI [discriminator](https://ajv.js.org/json-schema.html#discriminator) keyword supported by Ajv v8 instead of `select`.
512
513These keywords allow to choose the schema to validate the data based on the value of some property in the validated data.
514
515These keywords must be present in the same schema object (`selectDefault` is optional).
516
517The value of `select` keyword should be a [\$data reference](https://github.com/ajv-validator/ajv/blob/master/docs/validation.md#data-reference) that points to any primitive JSON type (string, number, boolean or null) in the data that is validated. You can also use a constant of primitive type as the value of this keyword (e.g., for debugging purposes).
518
519The value of `selectCases` keyword must be an object where each property name is a possible string representation of the value of `select` keyword and each property value is a corresponding schema (from draft-06 it can be boolean) that must be used to validate the data.
520
521The value of `selectDefault` keyword is a schema (also can be boolean) that must be used to validate the data in case `selectCases` has no key equal to the stringified value of `select` keyword.
522
523The validation succeeds in one of the following cases:
524
525- the validation of data using selected schema succeeds,
526- none of the schemas is selected for validation,
527- the value of select is undefined (no property in the data that the data reference points to).
528
529If `select` value (in data) is not a primitive type the validation fails.
530
531This keyword correctly tracks evaluated properties and items to work with `unevaluatedProperties` and `unevaluatedItems` keywords - only properties and items from the subschema that was used (one of `selectCases` subschemas or `selectDefault` subschema) are marked as evaluated.
532
533**Please note**: these keywords require Ajv `$data` option to support [\$data reference](https://github.com/ajv-validator/ajv/blob/master/docs/validation.md#data-reference).
534
535```javascript
536require("ajv-keywords")(ajv, "select")
537
538const schema = {
539 type: "object",
540 required: ["kind"],
541 properties: {
542 kind: {type: "string"},
543 },
544 select: {$data: "0/kind"},
545 selectCases: {
546 foo: {
547 required: ["foo"],
548 properties: {
549 kind: {},
550 foo: {type: "string"},
551 },
552 additionalProperties: false,
553 },
554 bar: {
555 required: ["bar"],
556 properties: {
557 kind: {},
558 bar: {type: "number"},
559 },
560 additionalProperties: false,
561 },
562 },
563 selectDefault: {
564 propertyNames: {
565 not: {enum: ["foo", "bar"]},
566 },
567 },
568}
569
570const validDataList = [
571 {kind: "foo", foo: "any"},
572 {kind: "bar", bar: 1},
573 {kind: "anything_else", not_bar_or_foo: "any value"},
574]
575
576const invalidDataList = [
577 {kind: "foo"}, // no property foo
578 {kind: "bar"}, // no property bar
579 {kind: "foo", foo: "any", another: "any value"}, // additional property
580 {kind: "bar", bar: 1, another: "any value"}, // additional property
581 {kind: "anything_else", foo: "any"}, // property foo not allowed
582 {kind: "anything_else", bar: 1}, // property bar not allowed
583]
584```
585
586#### `dynamicDefaults`
587
588This keyword allows to assign dynamic defaults to properties, such as timestamps, unique IDs etc.
589
590This keyword only works if `useDefaults` options is used and not inside `anyOf` keywords etc., in the same way as [default keyword treated by Ajv](https://github.com/epoberezkin/ajv#assigning-defaults).
591
592The keyword should be added on the object level. Its value should be an object with each property corresponding to a property name, in the same way as in standard `properties` keyword. The value of each property can be:
593
594- an identifier of dynamic default function (a string)
595- an object with properties `func` (an identifier) and `args` (an object with parameters that will be passed to this function during schema compilation - see examples).
596
597The properties used in `dynamicDefaults` should not be added to `required` keyword in the same schema (or validation will fail), because unlike `default` this keyword is processed after validation.
598
599There are several predefined dynamic default functions:
600
601- `"timestamp"` - current timestamp in milliseconds
602- `"datetime"` - current date and time as string (ISO, valid according to `date-time` format)
603- `"date"` - current date as string (ISO, valid according to `date` format)
604- `"time"` - current time as string (ISO, valid according to `time` format)
605- `"random"` - pseudo-random number in [0, 1) interval
606- `"randomint"` - pseudo-random integer number. If string is used as a property value, the function will randomly return 0 or 1. If object `{ func: 'randomint', args: { max: N } }` is used then the default will be an integer number in [0, N) interval.
607- `"seq"` - sequential integer number starting from 0. If string is used as a property value, the default sequence will be used. If object `{ func: 'seq', args: { name: 'foo'} }` is used then the sequence with name `"foo"` will be used. Sequences are global, even if different ajv instances are used.
608
609```javascript
610const schema = {
611 type: "object",
612 dynamicDefaults: {
613 ts: "datetime",
614 r: {func: "randomint", args: {max: 100}},
615 id: {func: "seq", args: {name: "id"}},
616 },
617 properties: {
618 ts: {
619 type: "string",
620 format: "date-time",
621 },
622 r: {
623 type: "integer",
624 minimum: 0,
625 exclusiveMaximum: 100,
626 },
627 id: {
628 type: "integer",
629 minimum: 0,
630 },
631 },
632}
633
634const data = {}
635ajv.validate(data) // true
636data // { ts: '2016-12-01T22:07:28.829Z', r: 25, id: 0 }
637
638const data1 = {}
639ajv.validate(data1) // true
640data1 // { ts: '2016-12-01T22:07:29.832Z', r: 68, id: 1 }
641
642ajv.validate(data1) // true
643data1 // didn't change, as all properties were defined
644```
645
646When using the `useDefaults` option value `"empty"`, properties and items equal to `null` or `""` (empty string) will be considered missing and assigned defaults. Use `allOf` [compound keyword](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#compound-keywords) to execute `dynamicDefaults` before validation.
647
648```javascript
649const schema = {
650 type: "object",
651 allOf: [
652 {
653 dynamicDefaults: {
654 ts: "datetime",
655 r: {func: "randomint", args: {min: 5, max: 100}},
656 id: {func: "seq", args: {name: "id"}},
657 },
658 },
659 {
660 properties: {
661 ts: {
662 type: "string",
663 },
664 r: {
665 type: "number",
666 minimum: 5,
667 exclusiveMaximum: 100,
668 },
669 id: {
670 type: "integer",
671 minimum: 0,
672 },
673 },
674 },
675 ],
676}
677
678const data = {ts: "", r: null}
679ajv.validate(data) // true
680data // { ts: '2016-12-01T22:07:28.829Z', r: 25, id: 0 }
681```
682
683You can add your own dynamic default function to be recognised by this keyword:
684
685```javascript
686const uuid = require("uuid")
687
688const def = require("ajv-keywords/dist/definitions/dynamicDefaults")
689def.DEFAULTS.uuid = () => uuid.v4
690
691const schema = {
692 dynamicDefaults: {id: "uuid"},
693 properties: {id: {type: "string", format: "uuid"}},
694}
695
696const data = {}
697ajv.validate(schema, data) // true
698data // { id: 'a1183fbe-697b-4030-9bcc-cfeb282a9150' };
699
700const data1 = {}
701ajv.validate(schema, data1) // true
702data1 // { id: '5b008de7-1669-467a-a5c6-70fa244d7209' }
703```
704
705You also can define dynamic default that accept parameters, e.g. version of uuid:
706
707```javascript
708const uuid = require("uuid")
709
710function getUuid(args) {
711 const version = "v" + ((arvs && args.v) || "4")
712 return uuid[version]
713}
714
715const def = require("ajv-keywords/dist/definitions/dynamicDefaults")
716def.DEFAULTS.uuid = getUuid
717
718const schema = {
719 dynamicDefaults: {
720 id1: "uuid", // v4
721 id2: {func: "uuid", v: 4}, // v4
722 id3: {func: "uuid", v: 1}, // v1
723 },
724}
725```
726
727**Please note**: dynamic default functions are differentiated by the number of parameters they have (`function.length`). Functions that do not expect default must have one non-optional argument so that `function.length` > 0.
728
729`dynamicDefaults` is not supported in [standalone validation code](https://github.com/ajv-validator/ajv/blob/master/docs/standalone.md).
730
731## Security contact
732
733To report a security vulnerability, please use the
734[Tidelift security contact](https://tidelift.com/security).
735Tidelift will coordinate the fix and disclosure.
736
737Please do NOT report security vulnerabilities via GitHub issues.
738
739## Open-source software support
740
741Ajv-keywords is a part of [Tidelift subscription](https://tidelift.com/subscription/pkg/npm-ajv-keywords?utm_source=npm-ajv-keywords&utm_medium=referral&utm_campaign=readme) - it provides a centralised support to open-source software users, in addition to the support provided by software maintainers.
742
743## License
744
745[MIT](https://github.com/epoberezkin/ajv-keywords/blob/master/LICENSE)