diff --git a/README.md b/README.md index 722637d..ac5871a 100644 --- a/README.md +++ b/README.md @@ -5,22 +5,33 @@ Tool for generation samples based on OpenAPI payload/response schema ## Features -- deterministic (given a particular input, will always produce the same output) -- Supports `allOf` + +- Deterministic (given a particular input, will always produce the same output) +- Supports compound keywords: `allOf`, `oneOf`, `anyOf`, `if/then/else` - Supports `additionalProperties` - Uses `default`, `const`, `enum` and `examples` where possible -- Full array support: supports `minItems`, and tuples (`items` as an array) +- Good array support: supports `contains`, `minItems`, `maxItems`, and tuples (`items` as an array) - Supports `minLength`, `maxLength`, `min`, `max`, `exclusiveMinimum`, `exclusiveMaximum` -- Supports the next `string` formats: +- Supports the following `string` formats: - email + - idn-email - password - date-time - date + - time - ipv4 - ipv6 - hostname + - idn-hostname - uri + - uri-reference + - uri-template + - iri + - iri-reference - uuid + - json-pointer + - relative-json-pointer + - regex - Infers schema type automatically following same rules as [json-schema-faker](https://www.npmjs.com/package/json-schema-faker#inferred-types) - Support for `$ref` resolving diff --git a/package.json b/package.json index e77bd1c..71d35c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "openapi-sampler", - "version": "1.0.0-beta.18", + "version": "1.0.0", "description": "Tool for generation samples based on OpenAPI payload/response schema", "main": "dist/openapi-sampler.js", "module": "src/openapi-sampler.js", @@ -36,6 +36,8 @@ "@babel/core": "^7.7.2", "@babel/preset-env": "^7.7.1", "@babel/register": "^7.7.0", + "ajv": "^8.1.0", + "ajv-formats": "^2.0.2", "babel-eslint": "^10.0.3", "babel-loader": "^8.0.6", "babel-plugin-istanbul": "^5.2.0", @@ -58,6 +60,7 @@ "gulp-rename": "^1.4.0", "gulp-sourcemaps": "^2.6.5", "gulp-uglify": "^3.0.2", + "it-each": "^0.4.0", "json-loader": "^0.5.7", "karma": "^4.4.1", "karma-babel-preprocessor": "^8.0.1", @@ -78,6 +81,7 @@ "vinyl-source-stream": "^2.0.0" }, "dependencies": { - "json-pointer": "^0.6.0" + "@types/json-schema": "^7.0.7", + "json-pointer": "^0.6.1" } } diff --git a/src/infer.js b/src/infer.js index b618363..e3f73cd 100644 --- a/src/infer.js +++ b/src/infer.js @@ -26,7 +26,7 @@ const schemaKeywordTypes = { export function inferType(schema) { if (schema.type !== undefined) { - return schema.type; + return Array.isArray(schema.type) ? schema.type.length === 0 ? null : schema.type[0] : schema.type; } const keywords = Object.keys(schemaKeywordTypes); for (var i = 0; i < keywords.length; i++) { diff --git a/src/openapi-sampler.js b/src/openapi-sampler.js index ff4f828..89733c9 100644 --- a/src/openapi-sampler.js +++ b/src/openapi-sampler.js @@ -8,7 +8,7 @@ const defaults = { maxSampleDepth: 15, }; -export function sample(schema, options, spec) { +export function sample(schema, options, spec = schema) { let opts = Object.assign({}, defaults, options); clearCache(); return traverse(schema, opts, spec).value; diff --git a/src/samplers/array.js b/src/samplers/array.js index 2ef706c..cd9bacf 100644 --- a/src/samplers/array.js +++ b/src/samplers/array.js @@ -2,20 +2,22 @@ import { traverse } from '../traverse'; export function sampleArray(schema, options = {}, spec, context) { const depth = (context && context.depth || 1); - let arrayLength = schema.minItems || 1; - if (Array.isArray(schema.items)) { - arrayLength = Math.max(arrayLength, schema.items.length); + let arrayLength = Math.min('maxItems' in schema ? schema.maxItems : Infinity, schema.minItems || 1); + // for the sake of simplicity, we're treating `contains` in a similar way to `items` + const items = schema.items || schema.contains; + if (Array.isArray(items)) { + arrayLength = Math.max(arrayLength, items.length); } let itemSchemaGetter = itemNumber => { if (Array.isArray(schema.items)) { - return schema.items[itemNumber] || {}; + return items[itemNumber] || {}; } - return schema.items || {}; + return items || {}; }; let res = []; - if (!schema.items) return res; + if (!items) return res; for (let i = 0; i < arrayLength; i++) { let itemSchema = itemSchemaGetter(i); diff --git a/src/samplers/number.js b/src/samplers/number.js index d012904..dcfc387 100644 --- a/src/samplers/number.js +++ b/src/samplers/number.js @@ -1,27 +1,44 @@ export function sampleNumber(schema) { - let res; - if (schema.maximum && schema.minimum) { - res = schema.exclusiveMinimum ? Math.floor(schema.minimum) + 1 : schema.minimum; - if ((schema.exclusiveMaximum && res >= schema.maximum) || - ((!schema.exclusiveMaximum && res > schema.maximum))) { - res = (schema.maximum + schema.minimum) / 2; + let res = 0; + if (typeof schema.exclusiveMinimum === 'boolean' || typeof schema.exclusiveMaximum === 'boolean') { //legacy support for jsonschema draft 4 of exclusiveMaximum/exclusiveMinimum as booleans + if (schema.maximum && schema.minimum) { + res = schema.exclusiveMinimum ? Math.floor(schema.minimum) + 1 : schema.minimum; + if ((schema.exclusiveMaximum && res >= schema.maximum) || + ((!schema.exclusiveMaximum && res > schema.maximum))) { + res = (schema.maximum + schema.minimum) / 2; + } + return res; } - return res; - } - if (schema.minimum) { - if (schema.exclusiveMinimum) { - return Math.floor(schema.minimum) + 1; - } else { + if (schema.minimum) { + if (schema.exclusiveMinimum) { + return Math.floor(schema.minimum) + 1; + } else { + return schema.minimum; + } + } + if (schema.maximum) { + if (schema.exclusiveMaximum) { + return (schema.maximum > 0) ? 0 : Math.floor(schema.maximum) - 1; + } else { + return (schema.maximum > 0) ? 0 : schema.maximum; + } + } + } else { + if (schema.minimum) { return schema.minimum; } - } - if (schema.maximum) { - if (schema.exclusiveMaximum) { - return (schema.maximum > 0) ? 0 : Math.floor(schema.maximum) - 1; - } else { - return (schema.maximum > 0) ? 0 : schema.maximum; + if (schema.exclusiveMinimum) { + res = Math.floor(schema.exclusiveMinimum) + 1; + + if (res === schema.exclusiveMaximum) { + res = (res + Math.floor(schema.exclusiveMaximum) - 1) / 2; + } + } else if (schema.exclusiveMaximum) { + res = Math.floor(schema.exclusiveMaximum) - 1; + } else if (schema.maximum) { + res = schema.maximum; } } - return 0; + return res; } diff --git a/src/samplers/string.js b/src/samplers/string.js index 5667150..ded822d 100644 --- a/src/samplers/string.js +++ b/src/samplers/string.js @@ -17,8 +17,8 @@ function passwordSample(min, max) { return res; } -function commonDateTimeSample(min, max, omitTime) { - let res = toRFCDateTime(new Date('2019-08-24T14:15:22.123Z'), omitTime, false); +function commonDateTimeSample({ min, max, omitTime, omitDate }) { + let res = toRFCDateTime(new Date('2019-08-24T14:15:22.123Z'), omitTime, omitDate, false); if (res.length < min) { console.warn(`Using minLength = ${min} is incorrect with format "date-time"`); } @@ -29,11 +29,15 @@ function commonDateTimeSample(min, max, omitTime) { } function dateTimeSample(min, max) { - return commonDateTimeSample(min, max); + return commonDateTimeSample({ min, max, omitTime: false, omitDate: false }); } function dateSample(min, max) { - return commonDateTimeSample(min, max, true); + return commonDateTimeSample({ min, max, omitTime: true, omitDate: false }); +} + +function timeSample(min, max) { + return commonDateTimeSample({ min, max, omitTime: false, omitDate: true }).slice(1); } function defaultSample(min, max) { @@ -60,21 +64,59 @@ function uriSample() { return 'http://example.com'; } +function uriReferenceSample() { + return '../dictionary'; +} + +function uriTemplateSample() { + return 'http://example.com/{endpoint}'; +} + +function iriSample() { + return 'http://example.com'; +} + +function iriReferenceSample() { + return '../dictionary'; +} + function uuidSample(_min, _max, propertyName) { return uuid(propertyName || 'id'); } +function jsonPointerSample() { + return '/json/pointer'; +} + +function relativeJsonPointerSample() { + return '1/relative/json/pointer'; +} + +function regexSample() { + return '/regex/'; +} + const stringFormats = { 'email': emailSample, + 'idn-email': emailSample, // https://tools.ietf.org/html/rfc6531#section-3.3 'password': passwordSample, 'date-time': dateTimeSample, 'date': dateSample, + 'time': timeSample, // full-time in https://tools.ietf.org/html/rfc3339#section-5.6 'ipv4': ipv4Sample, 'ipv6': ipv6Sample, 'hostname': hostnameSample, + 'idn-hostname': hostnameSample, // https://tools.ietf.org/html/rfc5890#section-2.3.2.3 + 'iri': iriSample, // https://tools.ietf.org/html/rfc3987 + 'iri-reference': iriReferenceSample, 'uri': uriSample, + 'uri-reference': uriReferenceSample, // either a URI or relative-reference https://tools.ietf.org/html/rfc3986#section-4.1 + 'uri-template': uriTemplateSample, 'uuid': uuidSample, - 'default': defaultSample + 'default': defaultSample, + 'json-pointer': jsonPointerSample, + 'relative-json-pointer': relativeJsonPointerSample, // https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01 + 'regex': regexSample, }; export function sampleString(schema, options, spec, context) { diff --git a/src/traverse.js b/src/traverse.js index 6d71282..d84a79a 100644 --- a/src/traverse.js +++ b/src/traverse.js @@ -1,7 +1,7 @@ import { _samplers } from './openapi-sampler'; import { allOfSample } from './allOf'; import { inferType } from './infer'; -import { getResultForCircular, popSchemaStack } from './utils'; +import { getResultForCircular, mergeDeep, popSchemaStack } from './utils'; import JsonPointer from 'json-pointer'; let $refCache = {}; @@ -28,9 +28,6 @@ export function traverse(schema, options, spec, context) { } if (schema.$ref) { - if (!spec) { - throw new Error('Your schema contains $ref. You must provide full specification in the third parameter.'); - } let ref = decodeURIComponent(schema.$ref); if (ref.startsWith('#')) { ref = ref.substring(1); @@ -86,6 +83,10 @@ export function traverse(schema, options, spec, context) { return traverse(schema.anyOf[0], options, spec, context); } + if (schema.if && schema.then) { + return traverse(mergeDeep(schema.if, schema.then), options, spec, context); + } + let example = null; let type = null; if (schema.default !== undefined) { @@ -98,6 +99,9 @@ export function traverse(schema, options, spec, context) { example = schema.examples[0]; } else { type = schema.type; + if (Array.isArray(type) && schema.type.length > 0) { + type = schema.type[0]; + } if (!type) { type = inferType(schema); } diff --git a/src/types.d.ts b/src/types.d.ts new file mode 100644 index 0000000..55713b2 --- /dev/null +++ b/src/types.d.ts @@ -0,0 +1,10 @@ +import type { JSONSchema7 } from 'json-schema'; + +export interface Options { + readonly skipNonRequired?: boolean; + readonly skipReadOnly?: boolean; + readonly skipWriteOnly?: boolean; + readonly quiet?: boolean; +} + +export function sample(schema: JSONSchema7, options?: Options, document?: object): unknown; diff --git a/src/utils.js b/src/utils.js index 1059c12..9a68fc7 100644 --- a/src/utils.js +++ b/src/utils.js @@ -7,10 +7,10 @@ function pad(number) { return number; } -export function toRFCDateTime(date, omitTime, milliseconds) { - var res = date.getUTCFullYear() + +export function toRFCDateTime(date, omitTime, omitDate, milliseconds) { + var res = omitDate ? '' : (date.getUTCFullYear() + '-' + pad(date.getUTCMonth() + 1) + - '-' + pad(date.getUTCDate()); + '-' + pad(date.getUTCDate())); if (!omitTime) { res += 'T' + pad(date.getUTCHours()) + ':' + pad(date.getUTCMinutes()) + @@ -92,4 +92,4 @@ function jsf32(a, b, c, d) { d = a + t | 0; return (d >>> 0) / 4294967296; } -} \ No newline at end of file +} diff --git a/test/integration.spec.js b/test/integration.spec.js index 921f0b9..6b4fad9 100644 --- a/test/integration.spec.js +++ b/test/integration.spec.js @@ -44,6 +44,24 @@ describe('Integration', function() { expect(result).to.deep.equal(expected); }); + it('should support type array', function() { + schema = { + 'type': ['string', 'number'] + }; + result = OpenAPISampler.sample(schema); + expected = 'string'; + expect(result).to.deep.equal(expected); + }); + + it('should use null for null', function() { + schema = { + type: 'null' + }; + result = OpenAPISampler.sample(schema); + expected = null; + expect(result).to.deep.equal(expected); + }); + it('should use null if type is not specified', function() { schema = { }; @@ -51,6 +69,15 @@ describe('Integration', function() { expected = null; expect(result).to.deep.equal(expected); }); + + it('should use null if type array is empty', function() { + schema = { + type: [] + }; + result = OpenAPISampler.sample(schema); + expected = null; + expect(result).to.deep.equal(expected); + }); }); describe('Objects', function() { @@ -428,64 +455,84 @@ describe('Integration', function() { }); }); - describe('oneOf and anyOf', function() { - it('should support oneOf', function() { + describe('Compound keywords', () => { + it('should support basic if/then/else usage', () => { schema = { - oneOf: [ - { - type: 'string' - }, - { - type: 'number' - } - ] + type: 'object', + if: {properties: {foo: {type: 'string', format: 'email'}}}, + then: {properties: {bar: {type: 'string'}}}, + else: {properties: {baz: {type: 'number'}}}, }; - result = OpenAPISampler.sample(schema); - expected = 'string'; - expect(result).to.equal(expected); - }); - it('should support anyOf', function() { - schema = { - anyOf: [ - { - type: 'string' - }, - { - type: 'number' - } - ] - }; result = OpenAPISampler.sample(schema); - expected = 'string'; - expect(result).to.equal(expected); - }); - - it('should prefer oneOf if anyOf and oneOf are on the same level ', function() { - schema = { - anyOf: [ - { - type: 'string' - } - ], - oneOf: [ - { - type: 'number' - } - ] + expected = { + foo: 'user@example.com', + bar: 'string' }; - result = OpenAPISampler.sample(schema); - expected = 0; - expect(result).to.equal(expected); + expect(result).to.deep.equal(expected); + }) + + describe('oneOf and anyOf', function () { + it('should support oneOf', function () { + schema = { + oneOf: [ + { + type: 'string' + }, + { + type: 'number' + } + ] + }; + result = OpenAPISampler.sample(schema); + expected = 'string'; + expect(result).to.equal(expected); + }); + + it('should support anyOf', function () { + schema = { + anyOf: [ + { + type: 'string' + }, + { + type: 'number' + } + ] + }; + result = OpenAPISampler.sample(schema); + expected = 'string'; + expect(result).to.equal(expected); + }); + + it('should prefer oneOf if anyOf and oneOf are on the same level ', function () { + schema = { + anyOf: [ + { + type: 'string' + } + ], + oneOf: [ + { + type: 'number' + } + ] + }; + result = OpenAPISampler.sample(schema); + expected = 0; + expect(result).to.equal(expected); + }); }); }); describe('$refs', function() { it('should follow $ref', function() { - schema = { - $ref: '#/defs/Schema' - }; - const spec = { + const schema = { + properties: { + test: { + $ref: '#/defs/Schema' + } + }, defs: { Schema: { type: 'object', @@ -497,9 +544,11 @@ describe('Integration', function() { } } }; - result = OpenAPISampler.sample(schema, {}, spec); + result = OpenAPISampler.sample(schema, {}); expected = { - a: 'string' + test: { + a: 'string' + } }; expect(result).to.deep.equal(expected); }); @@ -567,7 +616,7 @@ describe('Integration', function() { }; expect(() => OpenAPISampler.sample(schema)).to - .throw(/You must provide full specification in the third parameter/); + .throw(/Invalid reference token: defs/); }); it('should ignore readOnly params if referenced', function() { diff --git a/test/unit/array.spec.js b/test/unit/array.spec.js index 664fe27..5c2acb8 100644 --- a/test/unit/array.spec.js +++ b/test/unit/array.spec.js @@ -11,6 +11,13 @@ describe('sampleArray', () => { it('should return elements of correct type', () => { res = sampleArray({items: {type: 'number'}}); expect(res).to.deep.equal([0]); + res = sampleArray({contains: {type: 'number'}}); + expect(res).to.deep.equal([0]); + }); + + it('should return correct number of elements based on maxItems', () => { + res = sampleArray({items: {type: 'number'}, maxItems: 0}); + expect(res).to.deep.equal([]); }); it('should return correct number of elements based on minItems', () => { @@ -18,7 +25,7 @@ describe('sampleArray', () => { expect(res).to.deep.equal([0, 0, 0]); }); - it('should correcly sample tuples', () => { + it('should correctly sample tuples', () => { res = sampleArray({items: [{type: 'number'}, {type: 'string'}, {}]}); expect(res).to.deep.equal([0, 'string', null]); }); diff --git a/test/unit/number.spec.js b/test/unit/number.spec.js index 53c926a..0b350ea 100644 --- a/test/unit/number.spec.js +++ b/test/unit/number.spec.js @@ -37,20 +37,46 @@ describe('sampleNumber', () => { expect(res).to.equal(-4); }); + it('should return maximum -1 if maximum is negative and exclusiveMaximum', () => { + res = sampleNumber({maximum: -3, exclusiveMaximum: true, minimum: 1, exclusiveMinimum: true}); + expect(res).to.equal(-1); + }); + it('should return minimum if both minimum and maximum are specified', () => { res = sampleNumber({maximum: 10, minimum: 3}); expect(res).to.equal(3); }); - // (2, 3) -> 2.5 - it('should return middle point if integer is not possible', () => { - res = sampleNumber({minimum: 2, maximum: 3, exclusiveMinimum: true, exclusiveMaximum: true}); - expect(res).to.equal(2.5); + it('should return exclusiveMinimum + 1 if exclusiveMinimum is specified for openapi3.1', () => { + res = sampleNumber({exclusiveMinimum: 3}); + expect(res).to.equal(4); }); - // (2, 3] -> 3 - it('should return closer to minimum possible int', () => { - res = sampleNumber({minimum: 2, maximum: 3, exclusiveMinimum: true}); - expect(res).to.equal(3); + it('should return exclusiveMaximum - 1 if exclusiveMaximum is specified for openapi3.1', () => { + res = sampleNumber({exclusiveMaximum: -3}); + expect(res).to.equal(-4); }); + + // (2, 3) -> 2.5 + it('should return middle point if boundary integer is not possible for openapi3.1', () => { + res = sampleNumber({exclusiveMinimum: 2, exclusiveMaximum: 3}); + expect(res).to.equal(2.5); + }); + + // [2, 3] -> 2 + // (8, 13) -> 9 + it('should return closer to minimum possible int for openapi3.1', () => { + res = sampleNumber({minimum: 2, maximum: 3}); + expect(res).to.equal(2); + res = sampleNumber({exclusiveMinimum: 8, exclusiveMaximum: 13}); + expect(res).to.equal(9); + }); + + it('should return closer to minimum possible int for openapi3.1', () => { + res = sampleNumber({minimum: 2, maximum: 3}); + expect(res).to.equal(2); + res = sampleNumber({exclusiveMinimum: 8, exclusiveMaximum: 13}); + expect(res).to.equal(9); + }); + }); diff --git a/test/unit/string.spec.js b/test/unit/string.spec.js index 8cec2e3..63d4a15 100644 --- a/test/unit/string.spec.js +++ b/test/unit/string.spec.js @@ -1,5 +1,14 @@ import { sampleString } from '../../src/samplers/string.js'; +import Ajv from 'ajv'; +import addFormats from 'ajv-formats'; + +const ajv = new Ajv({ allErrors: true, messages: true, strict: true }); + +addFormats(ajv); + +require('it-each')(); + const IPV4_REGEXP = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; const IPV6_REGEXP = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/; const HOSTNAME_REGEXP = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/; @@ -74,6 +83,21 @@ describe('sampleString', () => { expect(res).to.equal('2019-08-24T14:15:22Z') }); + it('should return deterministic time string for format date-time', () => { + res = sampleString({format: 'time'}); + expect(res).to.equal('14:15:22Z'); + }); + + it('should not throw if incorrect maxLength applied to time', () => { + res = sampleString({format: 'time', maxLength: 5}); + expect(res).to.equal('14:15:22Z') + }); + + it('should not throw if incorrect minLength applied to time', () => { + res = sampleString({format: 'time', minLength: 100}); + expect(res).to.equal('14:15:22Z') + }); + it('should return ip for ipv4 format', () => { res = sampleString({format: 'ipv4'}); expect(res).to.match(IPV4_REGEXP); @@ -105,4 +129,29 @@ describe('sampleString', () => { expect(res).to.match(UUID_REGEXP); expect(res).to.equal('fb4274c7-4fcd-4035-8958-a680548957ff'); }); + + it.each([ + 'email', + // 'idn-email', // unsupported by ajv-formats + // 'password', // unsupported by ajv-formats + 'date-time', + 'date', + 'time', + 'ipv4', + 'ipv6', + 'hostname', + // 'idn-hostname', // unsupported by ajv-formats + 'uri', + 'uri-reference', + 'uri-template', + // 'iri', // unsupported by ajv-formats + // 'iri-reference', // unsupported by ajv-formats + 'uuid', + 'json-pointer', + 'relative-json-pointer', + 'regex' + ], 'should return valid %s', format => { + const schema = {type: 'string',format}; + expect(ajv.compile(schema)(sampleString(schema))).to.be.true; + }); }); diff --git a/yarn.lock b/yarn.lock index 2eff5ba..3562bb7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -880,6 +880,11 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/json-schema@^7.0.7": + version "7.0.7" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== + "@types/minimatch@*", "@types/minimatch@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -964,6 +969,13 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +ajv-formats@^2.0.2: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.0.tgz#96eaf83e38d32108b66d82a9cb0cfa24886cdfeb" + integrity sha512-USH2jBb+C/hIpwD2iRjp0pe0k+MvzG0mlSn/FIdCgQhUb9ALPRjt2KIQdfZDS9r0ZIeUAg7gOu9KL0PFqGqr5Q== + dependencies: + ajv "^8.0.0" + ajv@^6.10.0, ajv@^6.5.5, ajv@^6.9.1: version "6.10.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" @@ -974,6 +986,16 @@ ajv@^6.10.0, ajv@^6.5.5, ajv@^6.9.1: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.1.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.5.0.tgz#695528274bcb5afc865446aa275484049a18ae4b" + integrity sha512-Y2l399Tt1AguU3BPRP9Fn4eN+Or+StUGWCUpbnFyXSo8NZ9S4uj+AG2pjs5apK+ZMOwYOz1+a+VKvKH7CudXgQ== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" @@ -2644,6 +2666,11 @@ domain-browser@^1.2.0: resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== +dot-notes@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/dot-notes/-/dot-notes-3.1.1.tgz#7ae7e1a948134529f3768b2fb13f4c8fa92a57cf" + integrity sha1-eufhqUgTRSnzdosvsT9Mj6kqV88= + duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -3120,6 +3147,11 @@ fast-deep-equal@^2.0.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + fast-glob@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.0.4.tgz#d484a41005cb6faeb399b951fd1bd70ddaebb602" @@ -4539,6 +4571,13 @@ istanbul@^0.4.0, istanbul@~0.4.5: which "^1.1.1" wordwrap "^1.0.0" +it-each@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/it-each/-/it-each-0.4.0.tgz#d7bd0538766471e77e0d22034ab91c252ca834c4" + integrity sha512-fbOxSUiOByQnkgoFUEKPfzAuoUZ0mQRAXdWWsfI53gMJZ2oyhPcJBOCFx8yuMM36yP6OUUL3LgilYEqBiSACmQ== + dependencies: + dot-notes "3.1.1" + js-levenshtein@^1.1.3: version "1.1.6" resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" @@ -4597,10 +4636,10 @@ json-parse-better-errors@^1.0.1: resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== -json-pointer@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/json-pointer/-/json-pointer-0.6.0.tgz#8e500550a6aac5464a473377da57aa6cc22828d7" - integrity sha1-jlAFUKaqxUZKRzN32leqbMIoKNc= +json-pointer@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/json-pointer/-/json-pointer-0.6.1.tgz#3c6caa6ac139e2599f5a1659d39852154015054d" + integrity sha512-3OvjqKdCBvH41DLpV4iSt6v2XhZXV1bPB4OROuknvUXI7ZQNofieCPkmE26stEJ9zdQuvIxDHCuYhfgxFAAs+Q== dependencies: foreach "^2.0.4" @@ -4609,6 +4648,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -6451,6 +6495,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"