diff --git a/package.json b/package.json index f780ebbc93..db952e7666 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "helmet": "^5.0.0", "joi": "^17.3.0", "js-yaml": "^4.1.0", - "json-schema-to-ts": "^2.5.3", + "json-schema-to-ts": "2.5.4", "knex": "^2.0.0", "log4js": "^6.0.0", "make-fetch-happen": "^10.1.2", @@ -120,12 +120,14 @@ "semver": "^7.3.5", "serve-favicon": "^2.5.0", "stoppable": "^1.1.0", + "ts-toolbelt": "^9.6.0", "type-is": "^1.6.18", "unleash-client": "^3.15.0", "unleash-frontend": "4.14.0-beta.6", "uuid": "^8.3.2" }, "devDependencies": { + "@apidevtools/swagger-parser": "^10.1.0", "@babel/core": "7.18.9", "@types/bcryptjs": "2.4.2", "@types/express": "4.17.13", diff --git a/src/lib/openapi/index.ts b/src/lib/openapi/index.ts index 31309970b1..62e1252bbb 100644 --- a/src/lib/openapi/index.ts +++ b/src/lib/openapi/index.ts @@ -30,7 +30,6 @@ import { environmentSchema } from './spec/environment-schema'; import { environmentsSchema } from './spec/environments-schema'; import { eventSchema } from './spec/event-schema'; import { eventsSchema } from './spec/events-schema'; -import { exportParametersSchema } from './spec/export-parameters-schema'; import { featureEnvironmentMetricsSchema } from './spec/feature-environment-metrics-schema'; import { featureEnvironmentSchema } from './spec/feature-environment-schema'; import { featureEventsSchema } from './spec/feature-events-schema'; @@ -139,7 +138,6 @@ export const schemas = { environmentsSchema, eventSchema, eventsSchema, - exportParametersSchema, featureEnvironmentMetricsSchema, featureEnvironmentSchema, featureEventsSchema, diff --git a/src/lib/openapi/spec/client-features-query-schema.ts b/src/lib/openapi/spec/client-features-query-schema.ts index d96576de8f..89ea44d3a2 100644 --- a/src/lib/openapi/spec/client-features-query-schema.ts +++ b/src/lib/openapi/spec/client-features-query-schema.ts @@ -3,7 +3,6 @@ import { FromSchema } from 'json-schema-to-ts'; export const clientFeaturesQuerySchema = { $id: '#/components/schemas/clientFeaturesQuerySchema', type: 'object', - required: [], additionalProperties: false, properties: { tag: { diff --git a/src/lib/openapi/spec/export-parameters-schema.ts b/src/lib/openapi/spec/export-parameters-schema.ts deleted file mode 100644 index 0dceaada20..0000000000 --- a/src/lib/openapi/spec/export-parameters-schema.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { FromSchema } from 'json-schema-to-ts'; - -export const exportParametersSchema = { - $id: '#/components/schemas/exportParametersSchema', - type: 'object', - properties: { - format: { - type: 'string', - }, - download: { - type: 'boolean', - }, - strategies: { - type: 'boolean', - }, - featureToggles: { - type: 'boolean', - }, - projects: { - type: 'boolean', - }, - tags: { - type: 'boolean', - }, - environments: { - type: 'boolean', - }, - }, - components: {}, -} as const; - -export type ExportParametersSchema = FromSchema; diff --git a/src/lib/openapi/spec/export-query-parameters.ts b/src/lib/openapi/spec/export-query-parameters.ts new file mode 100644 index 0000000000..2896710878 --- /dev/null +++ b/src/lib/openapi/spec/export-query-parameters.ts @@ -0,0 +1,143 @@ +import { FromQueryParams } from '../util/from-query-params'; + +export const exportQueryParameters = [ + { + name: 'format', + schema: { + type: 'string', + enum: ['json', 'yaml'], + default: 'json', + }, + description: 'Desired export format. Must be either `json` or `yaml`.', + in: 'query', + }, + { + name: 'download', + schema: { + default: false, + anyOf: [ + { + type: 'boolean', + }, + { + type: 'string', + minLength: 1, + }, + { + type: 'number', + }, + ], + }, + description: 'Whether exported data should be downloaded as a file.', + in: 'query', + }, + { + name: 'strategies', + schema: { + default: true, + anyOf: [ + { + type: 'boolean', + }, + { + type: 'string', + minLength: 1, + }, + { + type: 'number', + }, + ], + }, + description: + 'Whether strategies should be included in the exported data.', + in: 'query', + }, + { + name: 'featureToggles', + schema: { + anyOf: [ + { + type: 'boolean', + }, + { + type: 'string', + minLength: 1, + }, + { + type: 'number', + }, + ], + default: true, + }, + description: + 'Whether feature toggles should be included in the exported data.', + in: 'query', + }, + { + name: 'projects', + schema: { + anyOf: [ + { + type: 'boolean', + }, + { + type: 'string', + minLength: 1, + }, + { + type: 'number', + }, + ], + default: true, + }, + description: + 'Whether projects should be included in the exported data.', + in: 'query', + }, + { + name: 'tags', + schema: { + anyOf: [ + { + type: 'boolean', + }, + { + type: 'string', + minLength: 1, + }, + { + type: 'number', + }, + ], + default: true, + }, + description: + 'Whether tag types, tags, and feature_tags should be included in the exported data.', + in: 'query', + }, + { + name: 'environments', + schema: { + anyOf: [ + { + type: 'boolean', + }, + { + type: 'string', + minLength: 1, + }, + { + type: 'number', + }, + ], + default: true, + }, + description: + 'Whether environments should be included in the exported data.', + in: 'query', + }, +] as const; + +export type ExportQueryParameters = FromQueryParams< + typeof exportQueryParameters +>; diff --git a/src/lib/openapi/spec/feedback-schema.ts b/src/lib/openapi/spec/feedback-schema.ts index 390c2870a9..ca23d2576d 100644 --- a/src/lib/openapi/spec/feedback-schema.ts +++ b/src/lib/openapi/spec/feedback-schema.ts @@ -4,7 +4,6 @@ export const feedbackSchema = { $id: '#/components/schemas/feedbackSchema', type: 'object', additionalProperties: false, - required: [], properties: { userId: { type: 'number', diff --git a/src/lib/openapi/spec/playground-feature-schema.ts b/src/lib/openapi/spec/playground-feature-schema.ts index d65004ad2f..f658534670 100644 --- a/src/lib/openapi/spec/playground-feature-schema.ts +++ b/src/lib/openapi/spec/playground-feature-schema.ts @@ -10,9 +10,9 @@ export const playgroundFeatureSchema = { additionalProperties: false, required: ['name', 'projectId', 'isEnabled', 'variant', 'variants'], properties: { - name: { type: 'string', examples: ['my-feature'] }, - projectId: { type: 'string', examples: ['my-project'] }, - isEnabled: { type: 'boolean', examples: [true] }, + name: { type: 'string', example: 'my-feature' }, + projectId: { type: 'string', example: 'my-project' }, + isEnabled: { type: 'boolean', example: true }, variant: { type: 'object', additionalProperties: false, @@ -34,7 +34,7 @@ export const playgroundFeatureSchema = { }, }, nullable: true, - examples: ['green'], + example: { name: 'green', enabled: true }, }, variants: { type: 'array', items: { $ref: variantSchema.$id } }, }, diff --git a/src/lib/openapi/spec/playground-request-schema.ts b/src/lib/openapi/spec/playground-request-schema.ts index def85ca996..4e61b74ea9 100644 --- a/src/lib/openapi/spec/playground-request-schema.ts +++ b/src/lib/openapi/spec/playground-request-schema.ts @@ -8,13 +8,13 @@ export const playgroundRequestSchema = { type: 'object', required: ['environment', 'context'], properties: { - environment: { type: 'string', examples: ['development'] }, + environment: { type: 'string', example: 'development' }, projects: { oneOf: [ { type: 'array', items: { type: 'string' }, - examples: ['my-project', 'my-other-project'], + example: ['my-project'], description: 'A list of projects to check for toggles in.', }, { diff --git a/src/lib/openapi/spec/sdk-context-schema.ts b/src/lib/openapi/spec/sdk-context-schema.ts index de390df859..49ac25666f 100644 --- a/src/lib/openapi/spec/sdk-context-schema.ts +++ b/src/lib/openapi/spec/sdk-context-schema.ts @@ -6,40 +6,38 @@ export const sdkContextSchema = { type: 'object', additionalProperties: { type: 'string', - examples: ['top-level custom context value'], + example: 'top-level custom context value', }, required: ['appName'], properties: { appName: { type: 'string', minLength: 1, - examples: ['My cool application.'], + example: 'My cool application.', }, currentTime: { type: 'string', format: 'date-time', - examples: ['2022-07-05T12:56:41+02:00'], + example: '2022-07-05T12:56:41+02:00', }, environment: { type: 'string', deprecated: true }, properties: { type: 'object', additionalProperties: { type: 'string' }, - examples: [ - { - customContextField: 'this is one!', - otherCustomField: 3, - }, - ], + example: { + customContextField: 'this is one!', + otherCustomField: '3', + }, }, remoteAddress: { type: 'string', - examples: ['192.168.1.1'], + example: '192.168.1.1', }, sessionId: { type: 'string', - examples: ['b65e7b23-fec0-4814-a129-0e9861ef18fc'], + example: 'b65e7b23-fec0-4814-a129-0e9861ef18fc', }, - userId: { type: 'string', examples: ['username@provider.com'] }, + userId: { type: 'string', example: 'username@provider.com' }, }, components: {}, } as const; diff --git a/src/lib/openapi/util/from-query-params.ts b/src/lib/openapi/util/from-query-params.ts new file mode 100644 index 0000000000..26993789e6 --- /dev/null +++ b/src/lib/openapi/util/from-query-params.ts @@ -0,0 +1,32 @@ +// module to create typescript types from query param lists. Based on +// input in this GitHub issue: +// https://github.com/ThomasAribart/json-schema-to-ts/issues/82 +import { FromSchema, JSONSchema } from 'json-schema-to-ts'; + +import { O, L, A } from 'ts-toolbelt'; + +type OpenApiParam = { + readonly name: string; + readonly schema: JSONSchema; + // Parameter types: + // https://swagger.io/docs/specification/describing-parameters/#types + readonly in: 'query' | 'path' | 'header' | 'cookie'; +}; + +type RecurseOnParams< + P extends readonly OpenApiParam[], + R extends O.Object = {}, +> = { + continue: RecurseOnParams< + L.Tail

, + L.Head

['in'] extends 'query' + ? R & { + [key in L.Head

['name']]: FromSchema['schema']>; + } + : R + >; + stop: A.Compute; +}[P extends readonly [OpenApiParam, ...OpenApiParam[]] ? 'continue' : 'stop']; + +export type FromQueryParams

= + RecurseOnParams

; diff --git a/src/lib/openapi/validate.ts b/src/lib/openapi/validate.ts index 36b9a678fd..7d2e59ba78 100644 --- a/src/lib/openapi/validate.ts +++ b/src/lib/openapi/validate.ts @@ -16,6 +16,10 @@ const ajv = new Ajv({ addFormats(ajv, ['date-time']); +// example was superseded by examples in openapi 3.1, but we're still on 3.0, so +// let's add it back in! +ajv.addKeyword('example'); + export const validateSchema = ( schema: SchemaId, data: unknown, diff --git a/src/lib/routes/admin-api/state.ts b/src/lib/routes/admin-api/state.ts index 1e22ebbeea..7ef15f03fe 100644 --- a/src/lib/routes/admin-api/state.ts +++ b/src/lib/routes/admin-api/state.ts @@ -14,8 +14,12 @@ import { IAuthRequest } from '../unleash-types'; import { OpenApiService } from '../../services/openapi-service'; import { createRequestSchema } from '../../openapi/util/create-request-schema'; import { createResponseSchema } from '../../openapi/util/create-response-schema'; -import { ExportParametersSchema } from '../../openapi/spec/export-parameters-schema'; +import { + exportQueryParameters, + ExportQueryParameters, +} from '../../openapi/spec/export-query-parameters'; import { emptyResponse } from '../../openapi/util/standard-responses'; +import { OpenAPIV3 } from 'openapi-types'; const upload = multer({ limits: { fileSize: 5242880 } }); const paramToBool = (param, def) => { @@ -75,11 +79,8 @@ class StateController extends Controller { responses: { 200: createResponseSchema('stateSchema'), }, - parameters: [ - { - $ref: '#/components/schema/exportParametersSchema', - }, - ], + parameters: + exportQueryParameters as unknown as OpenAPIV3.ParameterObject[], }), ], }); @@ -114,7 +115,7 @@ class StateController extends Controller { } async export( - req: Request, + req: Request, res: Response, ): Promise { const { format } = req.query; diff --git a/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap b/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap index 925dbcd4cd..a25504da7b 100644 --- a/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap +++ b/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap @@ -553,7 +553,6 @@ Object { "type": "array", }, }, - "required": Array [], "type": "object", }, "clientFeaturesSchema": Object { @@ -1018,32 +1017,6 @@ Object { ], "type": "object", }, - "exportParametersSchema": Object { - "properties": Object { - "download": Object { - "type": "boolean", - }, - "environments": Object { - "type": "boolean", - }, - "featureToggles": Object { - "type": "boolean", - }, - "format": Object { - "type": "string", - }, - "projects": Object { - "type": "boolean", - }, - "strategies": Object { - "type": "boolean", - }, - "tags": Object { - "type": "boolean", - }, - }, - "type": "object", - }, "featureEnvironmentMetricsSchema": Object { "additionalProperties": false, "properties": Object { @@ -1465,7 +1438,6 @@ Object { "type": "number", }, }, - "required": Array [], "type": "object", }, "groupSchema": Object { @@ -1834,28 +1806,23 @@ Object { "description": "A simplified feature toggle model intended for the Unleash playground.", "properties": Object { "isEnabled": Object { - "examples": Array [ - true, - ], + "example": true, "type": "boolean", }, "name": Object { - "examples": Array [ - "my-feature", - ], + "example": "my-feature", "type": "string", }, "projectId": Object { - "examples": Array [ - "my-project", - ], + "example": "my-project", "type": "string", }, "variant": Object { "additionalProperties": false, - "examples": Array [ - "green", - ], + "example": Object { + "enabled": true, + "name": "green", + }, "nullable": true, "properties": Object { "enabled": Object { @@ -1915,18 +1882,15 @@ Object { "$ref": "#/components/schemas/sdkContextSchema", }, "environment": Object { - "examples": Array [ - "development", - ], + "example": "development", "type": "string", }, "projects": Object { "oneOf": Array [ Object { "description": "A list of projects to check for toggles in.", - "examples": Array [ + "example": Array [ "my-project", - "my-other-project", ], "items": Object { "type": "string", @@ -2074,24 +2038,18 @@ Object { }, "sdkContextSchema": Object { "additionalProperties": Object { - "examples": Array [ - "top-level custom context value", - ], + "example": "top-level custom context value", "type": "string", }, "description": "The Unleash context as modeled in client SDKs", "properties": Object { "appName": Object { - "examples": Array [ - "My cool application.", - ], + "example": "My cool application.", "minLength": 1, "type": "string", }, "currentTime": Object { - "examples": Array [ - "2022-07-05T12:56:41+02:00", - ], + "example": "2022-07-05T12:56:41+02:00", "format": "date-time", "type": "string", }, @@ -2103,30 +2061,22 @@ Object { "additionalProperties": Object { "type": "string", }, - "examples": Array [ - Object { - "customContextField": "this is one!", - "otherCustomField": 3, - }, - ], + "example": Object { + "customContextField": "this is one!", + "otherCustomField": "3", + }, "type": "object", }, "remoteAddress": Object { - "examples": Array [ - "192.168.1.1", - ], + "example": "192.168.1.1", "type": "string", }, "sessionId": Object { - "examples": Array [ - "b65e7b23-fec0-4814-a129-0e9861ef18fc", - ], + "example": "b65e7b23-fec0-4814-a129-0e9861ef18fc", "type": "string", }, "userId": Object { - "examples": Array [ - "username@provider.com", - ], + "example": "username@provider.com", "type": "string", }, }, @@ -5168,7 +5118,137 @@ If the provided project does not exist, the list of events will be empty.", "operationId": "export", "parameters": Array [ Object { - "$ref": "#/components/schema/exportParametersSchema", + "description": "Desired export format. Must be either \`json\` or \`yaml\`.", + "in": "query", + "name": "format", + "schema": Object { + "default": "json", + "enum": Array [ + "json", + "yaml", + ], + "type": "string", + }, + }, + Object { + "description": "Whether exported data should be downloaded as a file.", + "in": "query", + "name": "download", + "schema": Object { + "anyOf": Array [ + Object { + "type": "boolean", + }, + Object { + "minLength": 1, + "type": "string", + }, + Object { + "type": "number", + }, + ], + "default": false, + }, + }, + Object { + "description": "Whether strategies should be included in the exported data.", + "in": "query", + "name": "strategies", + "schema": Object { + "anyOf": Array [ + Object { + "type": "boolean", + }, + Object { + "minLength": 1, + "type": "string", + }, + Object { + "type": "number", + }, + ], + "default": true, + }, + }, + Object { + "description": "Whether feature toggles should be included in the exported data.", + "in": "query", + "name": "featureToggles", + "schema": Object { + "anyOf": Array [ + Object { + "type": "boolean", + }, + Object { + "minLength": 1, + "type": "string", + }, + Object { + "type": "number", + }, + ], + "default": true, + }, + }, + Object { + "description": "Whether projects should be included in the exported data.", + "in": "query", + "name": "projects", + "schema": Object { + "anyOf": Array [ + Object { + "type": "boolean", + }, + Object { + "minLength": 1, + "type": "string", + }, + Object { + "type": "number", + }, + ], + "default": true, + }, + }, + Object { + "description": "Whether tag types, tags, and feature_tags should be included in the exported data.", + "in": "query", + "name": "tags", + "schema": Object { + "anyOf": Array [ + Object { + "type": "boolean", + }, + Object { + "minLength": 1, + "type": "string", + }, + Object { + "type": "number", + }, + ], + "default": true, + }, + }, + Object { + "description": "Whether environments should be included in the exported data.", + "in": "query", + "name": "environments", + "schema": Object { + "anyOf": Array [ + Object { + "type": "boolean", + }, + Object { + "minLength": 1, + "type": "string", + }, + Object { + "type": "number", + }, + ], + "default": true, + }, }, ], "responses": Object { diff --git a/src/test/e2e/api/openapi/openapi.e2e.test.ts b/src/test/e2e/api/openapi/openapi.e2e.test.ts index ab40fe249a..6378b59fe2 100644 --- a/src/test/e2e/api/openapi/openapi.e2e.test.ts +++ b/src/test/e2e/api/openapi/openapi.e2e.test.ts @@ -1,6 +1,7 @@ import { setupApp } from '../../helpers/test-helper'; import dbInit from '../../helpers/database-init'; import getLogger from '../../../fixtures/no-logger'; +import SwaggerParser from '@apidevtools/swagger-parser'; let app; let db; @@ -36,3 +37,19 @@ test('should serve the OpenAPI spec', async () => { expect(res.body).toMatchSnapshot(); }); }); + +test('the generated OpenAPI spec is valid', async () => { + const { body } = await app.request + .get('/docs/openapi.json') + .expect('Content-Type', /json/) + .expect(200); + + // this throws if the swagger parser can't parse it correctly + // also parses examples, but _does_ do some string coercion in examples + try { + await SwaggerParser.validate(body); + } catch (err) { + console.error(err); + return false; + } +}); diff --git a/yarn.lock b/yarn.lock index 435f9f98de..ae65948a31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,6 +9,15 @@ dependencies: "@jridgewell/trace-mapping" "^0.3.0" +"@apidevtools/json-schema-ref-parser@9.0.6": + version "9.0.6" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz#5d9000a3ac1fd25404da886da6b266adcd99cf1c" + integrity sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg== + dependencies: + "@jsdevtools/ono" "^7.1.3" + call-me-maybe "^1.0.1" + js-yaml "^3.13.1" + "@apidevtools/json-schema-ref-parser@^9.0.6": version "9.0.9" resolved "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz" @@ -19,7 +28,7 @@ call-me-maybe "^1.0.1" js-yaml "^4.1.0" -"@apidevtools/openapi-schemas@^2.0.4": +"@apidevtools/openapi-schemas@^2.0.4", "@apidevtools/openapi-schemas@^2.1.0": version "2.1.0" resolved "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz" integrity sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ== @@ -41,6 +50,19 @@ call-me-maybe "^1.0.1" z-schema "^5.0.1" +"@apidevtools/swagger-parser@^10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz#a987d71e5be61feb623203be0c96e5985b192ab6" + integrity sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw== + dependencies: + "@apidevtools/json-schema-ref-parser" "9.0.6" + "@apidevtools/openapi-schemas" "^2.1.0" + "@apidevtools/swagger-methods" "^3.0.2" + "@jsdevtools/ono" "^7.1.3" + ajv "^8.6.3" + ajv-draft-04 "^1.0.0" + call-me-maybe "^1.0.1" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13": version "7.12.13" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz" @@ -1417,6 +1439,11 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +ajv-draft-04@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz#3b64761b268ba0b9e668f0b41ba53fce0ad77fc8" + integrity sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw== + ajv-formats@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" @@ -1434,7 +1461,7 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.11.0: +ajv@^8.0.0, ajv@^8.11.0, ajv@^8.6.3: version "8.11.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== @@ -4928,10 +4955,10 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== -json-schema-to-ts@^2.5.3: - version "2.5.3" - resolved "https://registry.yarnpkg.com/json-schema-to-ts/-/json-schema-to-ts-2.5.3.tgz#10a1ad27a3cc6117ae9c652cc583a9e0ed10f0c8" - integrity sha512-2vABI+1IZNkChaPfLu7PG192ZY9gvRY00RbuN3VGlNNZkvYRpIECdBZPBVMe41r3wX0sl9emjRyhHT3gTm7HIg== +json-schema-to-ts@2.5.4: + version "2.5.4" + resolved "https://registry.yarnpkg.com/json-schema-to-ts/-/json-schema-to-ts-2.5.4.tgz#64008cf5e203284289922bd622bff82043a1a4ed" + integrity sha512-wlaYrGg+aYq0aEjSDY3cAFNzJVD2GvdrVIlvMdrbOLwkaMarXBiX+k0qm5Myb2aI3xjvdqsZoGs63JPS/M8+dg== dependencies: "@types/json-schema" "^7.0.9" ts-algebra "^1.1.1" @@ -7658,7 +7685,7 @@ ts-node@10.9.1: ts-toolbelt@^9.6.0: version "9.6.0" - resolved "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz" + resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz#50a25426cfed500d4a09bd1b3afb6f28879edfd5" integrity sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w== tsc-watch@5.0.3: