From 4f2ceaa17b966c60a010e51c109ae544e3ab1f42 Mon Sep 17 00:00:00 2001 From: Sergio Ferreira Date: Sun, 31 Dec 2023 09:57:37 +0000 Subject: [PATCH 01/60] feat(openapi): support version 3.1 --- src/framework/openapi.schema.validator.ts | 32 +- .../openapi.v3_1.modified.schema.json | 1407 +++++++++++++++++ 2 files changed, 1431 insertions(+), 8 deletions(-) create mode 100644 src/framework/openapi.v3_1.modified.schema.json diff --git a/src/framework/openapi.schema.validator.ts b/src/framework/openapi.schema.validator.ts index 03da0129..b378bd62 100644 --- a/src/framework/openapi.schema.validator.ts +++ b/src/framework/openapi.schema.validator.ts @@ -6,8 +6,12 @@ import AjvDraft4, { import addFormats from 'ajv-formats'; // https://github.com/OAI/OpenAPI-Specification/blob/master/schemas/v3.0/schema.json import * as openapi3Schema from './openapi.v3.schema.json'; +// https://github.com/OAI/OpenAPI-Specification/blob/master/schemas/v3.1/schema.json with dynamic refs replaced due to AJV bug - https://github.com/ajv-validator/ajv/issues/1745 +import * as openapi31Schema from './openapi.v3_1.modified.schema.json'; import { OpenAPIV3 } from './types.js'; +import Ajv2020 from 'ajv/dist/2020'; + export interface OpenAPISchemaValidatorOpts { version: string; validateApiSpec: boolean; @@ -17,7 +21,6 @@ export class OpenAPISchemaValidator { private validator: ValidateFunction; constructor(opts: OpenAPISchemaValidatorOpts) { const options: Options = { - schemaId: 'id', allErrors: true, validateFormats: true, coerceTypes: false, @@ -29,15 +32,28 @@ export class OpenAPISchemaValidator { options.validateSchema = false; } - const v = new AjvDraft4(options); - addFormats(v, ['email', 'regex', 'uri', 'uri-reference']); - - const ver = opts.version && parseInt(String(opts.version), 10); + const ver = opts.version && parseFloat(String(opts.version)); if (!ver) throw Error('version missing from OpenAPI specification'); - if (ver != 3) throw Error('OpenAPI v3 specification version is required'); + if (parseInt(ver.toString()) != 3) throw Error('OpenAPI v3 specification version is required'); + + let ajvInstance; + let schema; + + if (ver === 3) { + schema = openapi3Schema; + ajvInstance = new AjvDraft4(options); + } else if (ver === 3.1) { + schema = openapi31Schema; + ajvInstance = new Ajv2020(options); + ajvInstance.addFormat('media-range', true); // TODO: Validate media-range format as defined in https://www.rfc-editor.org/rfc/rfc9110.html#name-collected-abnf + } else { + throw new Error('OpenAPI v3 specification 3.0 and 3.1 supported'); + } + + addFormats(ajvInstance, ['email', 'regex', 'uri', 'uri-reference']); - v.addSchema(openapi3Schema); - this.validator = v.compile(openapi3Schema); + ajvInstance.addSchema(schema); + this.validator = ajvInstance.compile(schema); } public validate(openapiDoc: OpenAPIV3.Document): { diff --git a/src/framework/openapi.v3_1.modified.schema.json b/src/framework/openapi.v3_1.modified.schema.json new file mode 100644 index 00000000..76e4df1d --- /dev/null +++ b/src/framework/openapi.v3_1.modified.schema.json @@ -0,0 +1,1407 @@ +{ + "$id": "https://spec.openapis.org/oas/3.1/schema/2022-10-07", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The description of OpenAPI v3.1.x documents without schema validation, as defined by https://spec.openapis.org/oas/v3.1.0", + "type": "object", + "properties": { + "openapi": { + "type": "string", + "pattern": "^3\\.1\\.\\d+(-.+)?$" + }, + "info": { + "$ref": "#/$defs/info" + }, + "jsonSchemaDialect": { + "type": "string", + "format": "uri", + "default": "https://spec.openapis.org/oas/3.1/dialect/base" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + }, + "default": [{ + "url": "/" + }] + }, + "paths": { + "$ref": "#/$defs/paths" + }, + "webhooks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + }, + "components": { + "$ref": "#/$defs/components" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/$defs/tag" + } + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": [ + "openapi", + "info" + ], + "anyOf": [{ + "required": [ + "paths" + ] + }, + { + "required": [ + "components" + ] + }, + { + "required": [ + "webhooks" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "info": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#info-object", + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "termsOfService": { + "type": "string", + "format": "uri" + }, + "contact": { + "$ref": "#/$defs/contact" + }, + "license": { + "$ref": "#/$defs/license" + }, + "version": { + "type": "string" + } + }, + "required": [ + "title", + "version" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "contact": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#contact-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "license": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#license-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "name" + ], + "dependentSchemas": { + "identifier": { + "not": { + "required": [ + "url" + ] + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#server-object", + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri-reference" + }, + "description": { + "type": "string" + }, + "variables": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/server-variable" + } + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server-variable": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#server-variable-object", + "type": "object", + "properties": { + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "default": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "default" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "components": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#components-object", + "type": "object", + "properties": { + "schemas": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/schema" + } + }, + "responses": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/response-or-reference" + } + }, + "parameters": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + }, + "requestBodies": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/request-body-or-reference" + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "securitySchemes": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/security-scheme-or-reference" + } + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "pathItems": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + } + }, + "patternProperties": { + "^(schemas|responses|parameters|examples|requestBodies|headers|securitySchemes|links|callbacks|pathItems)$": { + "$comment": "Enumerating all of the property names in the regex above is necessary for unevaluatedProperties to work as expected", + "propertyNames": { + "pattern": "^[a-zA-Z0-9._-]+$" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "paths": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#paths-object", + "type": "object", + "patternProperties": { + "^/": { + "$ref": "#/$defs/path-item" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "path-item": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#path-item-object", + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "get": { + "$ref": "#/$defs/operation" + }, + "put": { + "$ref": "#/$defs/operation" + }, + "post": { + "$ref": "#/$defs/operation" + }, + "delete": { + "$ref": "#/$defs/operation" + }, + "options": { + "$ref": "#/$defs/operation" + }, + "head": { + "$ref": "#/$defs/operation" + }, + "patch": { + "$ref": "#/$defs/operation" + }, + "trace": { + "$ref": "#/$defs/operation" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "path-item-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/path-item" + } + }, + "operation": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#operation-object", + "type": "object", + "properties": { + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "requestBody": { + "$ref": "#/$defs/request-body-or-reference" + }, + "responses": { + "$ref": "#/$defs/responses" + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "external-documentation": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#external-documentation-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#parameter-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "header", + "path", + "cookie" + ] + }, + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "schema": { + "$ref": "#/$defs/schema" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "required": [ + "name", + "in" + ], + "oneOf": [{ + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], + "if": { + "properties": { + "in": { + "const": "query" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "allowEmptyValue": { + "default": false, + "type": "boolean" + } + } + }, + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "type": "string" + }, + "explode": { + "type": "boolean" + } + }, + "allOf": [{ + "$ref": "#/$defs/examples" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-path" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie" + }, + { + "$ref": "#/$defs/styles-for-form" + } + ], + "$defs": { + "styles-for-path": { + "if": { + "properties": { + "in": { + "const": "path" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "simple", + "enum": [ + "matrix", + "label", + "simple" + ] + }, + "required": { + "const": true + } + }, + "required": [ + "required" + ] + } + }, + "styles-for-header": { + "if": { + "properties": { + "in": { + "const": "header" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + } + } + } + }, + "styles-for-query": { + "if": { + "properties": { + "in": { + "const": "query" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + } + } + }, + "styles-for-cookie": { + "if": { + "properties": { + "in": { + "const": "cookie" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "form", + "const": "form" + } + } + } + } + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/parameter" + } + }, + "request-body": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#request-body-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "content": { + "$ref": "#/$defs/content" + }, + "required": { + "default": false, + "type": "boolean" + } + }, + "required": [ + "content" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "request-body-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/request-body" + } + }, + "content": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#fixed-fields-10", + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/media-type" + }, + "propertyNames": { + "format": "media-range" + } + }, + "media-type": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#media-type-object", + "type": "object", + "properties": { + "schema": { + "$ref": "#/$defs/schema" + }, + "encoding": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/encoding" + } + } + }, + "allOf": [{ + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/examples" + } + ], + "unevaluatedProperties": false + }, + "encoding": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#encoding-object", + "type": "object", + "properties": { + "contentType": { + "type": "string", + "format": "media-range" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + }, + "allOf": [{ + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/styles-for-form" + } + ], + "unevaluatedProperties": false + }, + "responses": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#responses-object", + "type": "object", + "properties": { + "default": { + "$ref": "#/$defs/response-or-reference" + } + }, + "patternProperties": { + "^[1-5](?:[0-9]{2}|XX)$": { + "$ref": "#/$defs/response-or-reference" + } + }, + "minProperties": 1, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "if": { + "$comment": "either default, or at least one response code property must exist", + "patternProperties": { + "^[1-5](?:[0-9]{2}|XX)$": false + } + }, + "then": { + "required": ["default"] + } + }, + "response": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#response-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "content": { + "$ref": "#/$defs/content" + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + } + }, + "required": [ + "description" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "response-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/response" + } + }, + "callbacks": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#callback-object", + "type": "object", + "$ref": "#/$defs/specification-extensions", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + }, + "callbacks-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/callbacks" + } + }, + "example": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#example-object", + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "value": true, + "externalValue": { + "type": "string", + "format": "uri" + } + }, + "not": { + "required": [ + "value", + "externalValue" + ] + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "example-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/example" + } + }, + "link": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#link-object", + "type": "object", + "properties": { + "operationRef": { + "type": "string", + "format": "uri-reference" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "$ref": "#/$defs/map-of-strings" + }, + "requestBody": true, + "description": { + "type": "string" + }, + "body": { + "$ref": "#/$defs/server" + } + }, + "oneOf": [{ + "required": [ + "operationRef" + ] + }, + { + "required": [ + "operationId" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "link-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/link" + } + }, + "header": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#header-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "schema": { + "$ref": "#/$defs/schema" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "oneOf": [{ + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + }, + "explode": { + "default": false, + "type": "boolean" + } + }, + "$ref": "#/$defs/examples" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "header-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/header" + } + }, + "tag": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#tag-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": [ + "name" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "reference": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#reference-object", + "type": "object", + "properties": { + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "schema": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#schema-object", + "$dynamicAnchor": "meta", + "type": [ + "object", + "boolean" + ] + }, + "security-scheme": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#security-scheme-object", + "type": "object", + "properties": { + "type": { + "enum": [ + "apiKey", + "http", + "mutualTLS", + "oauth2", + "openIdConnect" + ] + }, + "description": { + "type": "string" + } + }, + "required": [ + "type" + ], + "allOf": [{ + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-apikey" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http-bearer" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oauth2" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oidc" + } + ], + "unevaluatedProperties": false, + "$defs": { + "type-apikey": { + "if": { + "properties": { + "type": { + "const": "apiKey" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "header", + "cookie" + ] + } + }, + "required": [ + "name", + "in" + ] + } + }, + "type-http": { + "if": { + "properties": { + "type": { + "const": "http" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "scheme": { + "type": "string" + } + }, + "required": [ + "scheme" + ] + } + }, + "type-http-bearer": { + "if": { + "properties": { + "type": { + "const": "http" + }, + "scheme": { + "type": "string", + "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" + } + }, + "required": [ + "type", + "scheme" + ] + }, + "then": { + "properties": { + "bearerFormat": { + "type": "string" + } + } + } + }, + "type-oauth2": { + "if": { + "properties": { + "type": { + "const": "oauth2" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "flows": { + "$ref": "#/$defs/oauth-flows" + } + }, + "required": [ + "flows" + ] + } + }, + "type-oidc": { + "if": { + "properties": { + "type": { + "const": "openIdConnect" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "openIdConnectUrl": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "openIdConnectUrl" + ] + } + } + } + }, + "security-scheme-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/security-scheme" + } + }, + "oauth-flows": { + "type": "object", + "properties": { + "implicit": { + "$ref": "#/$defs/oauth-flows/$defs/implicit" + }, + "password": { + "$ref": "#/$defs/oauth-flows/$defs/password" + }, + "clientCredentials": { + "$ref": "#/$defs/oauth-flows/$defs/client-credentials" + }, + "authorizationCode": { + "$ref": "#/$defs/oauth-flows/$defs/authorization-code" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "implicit": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "password": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "client-credentials": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "authorization-code": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + } + } + }, + "security-requirement": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#security-requirement-object", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "specification-extensions": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#specification-extensions", + "patternProperties": { + "^x-": true + } + }, + "examples": { + "properties": { + "example": true, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + } + } + }, + "map-of-strings": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "styles-for-form": { + "if": { + "properties": { + "style": { + "const": "form" + } + }, + "required": [ + "style" + ] + }, + "then": { + "properties": { + "explode": { + "default": true + } + } + }, + "else": { + "properties": { + "explode": { + "default": false + } + } + } + } + } +} \ No newline at end of file From 1894d84bcce0d2721f7fa491c290122240e9d771 Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 5 Feb 2024 00:22:24 +0000 Subject: [PATCH 02/60] test(openapi_3.1): ensure that an API with webhooks and no routes is supported --- test/openapi_3.1/README.md | 3 +++ test/openapi_3.1/resources/webhook.yaml | 35 +++++++++++++++++++++++++ test/openapi_3.1/webhook.spec.ts | 31 ++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 test/openapi_3.1/README.md create mode 100644 test/openapi_3.1/resources/webhook.yaml create mode 100644 test/openapi_3.1/webhook.spec.ts diff --git a/test/openapi_3.1/README.md b/test/openapi_3.1/README.md new file mode 100644 index 00000000..defce399 --- /dev/null +++ b/test/openapi_3.1/README.md @@ -0,0 +1,3 @@ +# Open API 3.1 tests + +This folder, and its subfolders, contain tests for OpenAPI specification 3.1 diff --git a/test/openapi_3.1/resources/webhook.yaml b/test/openapi_3.1/resources/webhook.yaml new file mode 100644 index 00000000..27bbd8e2 --- /dev/null +++ b/test/openapi_3.1/resources/webhook.yaml @@ -0,0 +1,35 @@ +# From https://github.com/OAI/OpenAPI-Specification/blob/main/examples/v3.1/webhook-example.yaml +openapi: 3.1.0 +info: + title: Webhook Example + version: 1.0.0 +# Since OAS 3.1.0 the paths element isn't necessary. Now a valid OpenAPI Document can describe only paths, webhooks, or even only reusable components +webhooks: + # Each webhook needs a name + newPet: + # This is a Path Item Object, the only difference is that the request is initiated by the API provider + post: + requestBody: + description: Information about a new pet in the system + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + responses: + "200": + description: Return a 200 status to indicate that the data was received successfully + +components: + schemas: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string \ No newline at end of file diff --git a/test/openapi_3.1/webhook.spec.ts b/test/openapi_3.1/webhook.spec.ts new file mode 100644 index 00000000..e088c12a --- /dev/null +++ b/test/openapi_3.1/webhook.spec.ts @@ -0,0 +1,31 @@ +import { expect } from "chai"; +import * as request from 'supertest'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('webhook support - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'webhook.yaml'); + app = await createApp( + { apiSpec, validateRequests: true }, + 3005, + undefined, + false, + ); + }); + + after(() => { + app.server.close(); + }); + + it('should support an API that only has webhooks defined, but provides no routes', () => { + // The webhook is not made available by the provider API, so the request will return 404 + // This test ensures that the request flow happens normally without any interruptions due to being a webhook + return request(app) + .get(`${app.basePath}/webhook`) + .expect(404); + }); + +}) \ No newline at end of file From bb53f78965eb3048a14ba9351393f7fb156ac8ef Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 5 Feb 2024 00:25:12 +0000 Subject: [PATCH 03/60] feat(openapi_3.1): adds open api 3.1 type --- src/framework/ajv/index.ts | 6 +- src/framework/index.ts | 8 +-- src/framework/openapi.context.ts | 2 +- src/framework/openapi.schema.validator.ts | 2 +- src/framework/openapi.spec.loader.ts | 60 ++++++++++--------- src/framework/types.ts | 17 ++++-- src/middlewares/openapi.metadata.ts | 2 +- src/middlewares/openapi.multipart.ts | 2 +- src/middlewares/openapi.request.validator.ts | 11 ++-- src/middlewares/openapi.response.validator.ts | 4 +- src/middlewares/openapi.security.ts | 2 +- .../parsers/req.parameter.mutator.ts | 4 +- src/middlewares/parsers/schema.parse.ts | 4 +- .../parsers/schema.preprocessor.ts | 22 ++++--- src/middlewares/parsers/util.ts | 2 +- src/openapi.validator.ts | 10 ++-- src/resolvers.ts | 4 +- test/440.spec.ts | 2 +- test/478.spec.ts | 2 +- test/535.spec.ts | 4 +- test/577.spec.ts | 4 +- test/699.spec.ts | 1 - test/821.spec.ts | 2 +- test/allow.header.spec.ts | 2 +- test/invalid.apispec.spec.ts | 2 +- test/no.components.spec.ts | 2 +- test/petstore.spec.ts | 2 +- test/user-request-url.router.spec.ts | 4 +- 28 files changed, 102 insertions(+), 87 deletions(-) diff --git a/src/framework/ajv/index.ts b/src/framework/ajv/index.ts index 067467c4..edbc0404 100644 --- a/src/framework/ajv/index.ts +++ b/src/framework/ajv/index.ts @@ -11,21 +11,21 @@ interface SerDesSchema extends Partial { } export function createRequestAjv( - openApiSpec: OpenAPIV3.Document, + openApiSpec: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: Options = {}, ): AjvDraft4 { return createAjv(openApiSpec, options); } export function createResponseAjv( - openApiSpec: OpenAPIV3.Document, + openApiSpec: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: Options = {}, ): AjvDraft4 { return createAjv(openApiSpec, options, false); } function createAjv( - openApiSpec: OpenAPIV3.Document, + openApiSpec: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: Options = {}, request = true, ): AjvDraft4 { diff --git a/src/framework/index.ts b/src/framework/index.ts index 471d6183..8fe2ceb9 100644 --- a/src/framework/index.ts +++ b/src/framework/index.ts @@ -75,7 +75,7 @@ export class OpenAPIFramework { private loadSpec( filePath: string | object, $refParser: { mode: 'bundle' | 'dereference' } = { mode: 'bundle' }, - ): Promise { + ): Promise { // Because of this issue ( https://github.com/APIDevTools/json-schema-ref-parser/issues/101#issuecomment-421755168 ) // We need this workaround ( use '$RefParser.dereference' instead of '$RefParser.bundle' ) if asked by user if (typeof filePath === 'string') { @@ -87,7 +87,7 @@ export class OpenAPIFramework { $refParser.mode === 'dereference' ? $RefParser.dereference(absolutePath) : $RefParser.bundle(absolutePath); - return doc as Promise; + return doc as Promise; } else { throw new Error( `${this.loggingPrefix}spec could not be read at ${filePath}`, @@ -98,10 +98,10 @@ export class OpenAPIFramework { $refParser.mode === 'dereference' ? $RefParser.dereference(filePath) : $RefParser.bundle(filePath); - return doc as Promise; + return doc as Promise; } - private sortApiDocTags(apiDoc: OpenAPIV3.Document): void { + private sortApiDocTags(apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1): void { if (apiDoc && Array.isArray(apiDoc.tags)) { apiDoc.tags.sort((a, b): number => { return a.name < b.name ? -1 : 1; diff --git a/src/framework/openapi.context.ts b/src/framework/openapi.context.ts index 2df421f0..3f8e654e 100644 --- a/src/framework/openapi.context.ts +++ b/src/framework/openapi.context.ts @@ -6,7 +6,7 @@ export interface RoutePair { openApiRoute: string; } export class OpenApiContext { - public readonly apiDoc: OpenAPIV3.Document; + public readonly apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; public readonly expressRouteMap = {}; public readonly openApiRouteMap = {}; public readonly routes: RouteMetadata[] = []; diff --git a/src/framework/openapi.schema.validator.ts b/src/framework/openapi.schema.validator.ts index b378bd62..81a20f7b 100644 --- a/src/framework/openapi.schema.validator.ts +++ b/src/framework/openapi.schema.validator.ts @@ -56,7 +56,7 @@ export class OpenAPISchemaValidator { this.validator = ajvInstance.compile(schema); } - public validate(openapiDoc: OpenAPIV3.Document): { + public validate(openapiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1): { errors: Array | null; } { const valid = this.validator(openapiDoc); diff --git a/src/framework/openapi.spec.loader.ts b/src/framework/openapi.spec.loader.ts index bc0d075f..07c9ce52 100644 --- a/src/framework/openapi.spec.loader.ts +++ b/src/framework/openapi.spec.loader.ts @@ -6,7 +6,7 @@ import { } from './types'; export interface Spec { - apiDoc: OpenAPIV3.Document; + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; basePaths: string[]; routes: RouteMetadata[]; } @@ -20,7 +20,7 @@ export interface RouteMetadata { } interface DiscoveredRoutes { - apiDoc: OpenAPIV3.Document; + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; basePaths: string[]; routes: RouteMetadata[]; } @@ -47,7 +47,7 @@ export class OpenApiSpecLoader { const routes: RouteMetadata[] = []; const toExpressParams = this.toExpressParams; // const basePaths = this.framework.basePaths; - // let apiDoc: OpenAPIV3.Document = null; + // let apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1 = null; // let basePaths: string[] = null; const { apiDoc, basePaths } = await this.framework.initialize({ visitApi(ctx: OpenAPIFrameworkAPIContext): void { @@ -55,34 +55,36 @@ export class OpenApiSpecLoader { const basePaths = ctx.basePaths; for (const bpa of basePaths) { const bp = bpa.replace(/\/$/, ''); - for (const [path, methods] of Object.entries(apiDoc.paths)) { - for (const [method, schema] of Object.entries(methods)) { - if ( - method.startsWith('x-') || - ['parameters', 'summary', 'description'].includes(method) - ) { - continue; - } - const pathParams = new Set(); - const parameters = [...schema.parameters ?? [], ...methods.parameters ?? []] - for (const param of parameters) { - if (param.in === 'path') { - pathParams.add(param.name); + if (apiDoc.paths) { + for (const [path, methods] of Object.entries(apiDoc.paths)) { + for (const [method, schema] of Object.entries(methods)) { + if ( + method.startsWith('x-') || + ['parameters', 'summary', 'description'].includes(method) + ) { + continue; + } + const pathParams = new Set(); + const parameters = [...schema.parameters ?? [], ...methods.parameters ?? []] + for (const param of parameters) { + if (param.in === 'path') { + pathParams.add(param.name); + } } + const openApiRoute = `${bp}${path}`; + const expressRoute = `${openApiRoute}` + .split(':') + .map(toExpressParams) + .join('\\:'); + + routes.push({ + basePath: bp, + expressRoute, + openApiRoute, + method: method.toUpperCase(), + pathParams: Array.from(pathParams), + }); } - const openApiRoute = `${bp}${path}`; - const expressRoute = `${openApiRoute}` - .split(':') - .map(toExpressParams) - .join('\\:'); - - routes.push({ - basePath: bp, - expressRoute, - openApiRoute, - method: method.toUpperCase(), - pathParams: Array.from(pathParams), - }); } } } diff --git a/src/framework/types.ts b/src/framework/types.ts index 201e6fbf..342a434b 100644 --- a/src/framework/types.ts +++ b/src/framework/types.ts @@ -21,7 +21,7 @@ export interface ValidationSchema extends ParametersSchema { } export interface OpenAPIFrameworkInit { - apiDoc: OpenAPIV3.Document; + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; basePaths: string[]; } export type SecurityHandlers = { @@ -109,7 +109,7 @@ export type SerDesMap = { }; export interface OpenApiValidatorOpts { - apiSpec: OpenAPIV3.Document | string; + apiSpec: OpenAPIV3.DocumentV3 | string; validateApiSpec?: boolean; validateResponses?: boolean | ValidateResponseOpts; validateRequests?: boolean | ValidateRequestOpts; @@ -152,7 +152,7 @@ export interface NormalizedOpenApiValidatorOpts extends OpenApiValidatorOpts { } export namespace OpenAPIV3 { - export interface Document { + export interface DocumentV3 { openapi: string; info: InfoObject; servers?: ServerObject[]; @@ -163,6 +163,13 @@ export namespace OpenAPIV3 { externalDocs?: ExternalDocumentationObject; } + export interface DocumentV3_1 extends Omit { + paths?: DocumentV3['paths'] + webhooks: { + [name: string]: PathItemObject | ReferenceObject + } + } + export interface InfoObject { title: string; description?: string; @@ -474,7 +481,7 @@ export interface OpenAPIFrameworkPathObject { } interface OpenAPIFrameworkArgs { - apiDoc: OpenAPIV3.Document | string; + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1 | string; validateApiSpec?: boolean; $refParser?: { mode: 'bundle' | 'dereference'; @@ -484,7 +491,7 @@ interface OpenAPIFrameworkArgs { export interface OpenAPIFrameworkAPIContext { // basePaths: BasePath[]; basePaths: string[]; - getApiDoc(): OpenAPIV3.Document; + getApiDoc(): OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; } export interface OpenAPIFrameworkVisitor { diff --git a/src/middlewares/openapi.metadata.ts b/src/middlewares/openapi.metadata.ts index 2dae08a2..c2852cbb 100644 --- a/src/middlewares/openapi.metadata.ts +++ b/src/middlewares/openapi.metadata.ts @@ -15,7 +15,7 @@ import { httpMethods } from './parsers/schema.preprocessor'; export function applyOpenApiMetadata( openApiContext: OpenApiContext, - responseApiDoc: OpenAPIV3.Document, + responseApiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, ): OpenApiRequestHandler { return (req: OpenApiRequest, res: Response, next: NextFunction): void => { // note base path is empty when path is fully qualified i.e. req.path.startsWith('') diff --git a/src/middlewares/openapi.multipart.ts b/src/middlewares/openapi.multipart.ts index 5871ccce..930fed08 100644 --- a/src/middlewares/openapi.multipart.ts +++ b/src/middlewares/openapi.multipart.ts @@ -15,7 +15,7 @@ import { MulterError } from 'multer'; const multer = require('multer'); export function multipart( - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: MultipartOpts, ): OpenApiRequestHandler { const mult = multer(options.multerOpts); diff --git a/src/middlewares/openapi.request.validator.ts b/src/middlewares/openapi.request.validator.ts index 47748cb2..40efea19 100644 --- a/src/middlewares/openapi.request.validator.ts +++ b/src/middlewares/openapi.request.validator.ts @@ -21,6 +21,7 @@ import { import { BodySchemaParser } from './parsers/body.parse'; import { ParametersSchemaParser } from './parsers/schema.parse'; import { RequestParameterMutator } from './parsers/req.parameter.mutator'; +import { debug } from 'console'; type OperationObject = OpenAPIV3.OperationObject; type SchemaObject = OpenAPIV3.SchemaObject; @@ -31,13 +32,13 @@ type ApiKeySecurityScheme = OpenAPIV3.ApiKeySecurityScheme; export class RequestValidator { private middlewareCache: { [key: string]: RequestHandler } = {}; - private apiDoc: OpenAPIV3.Document; + private apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; private ajv: Ajv; private ajvBody: Ajv; private requestOpts: ValidateRequestOpts = {}; constructor( - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: RequestValidatorOptions = {}, ) { this.middlewareCache = {}; @@ -267,7 +268,7 @@ export class RequestValidator { } class Validator { - private readonly apiDoc: OpenAPIV3.Document; + private readonly apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; readonly schemaGeneral: object; readonly schemaBody: object; readonly validatorGeneral: ValidateFunction; @@ -275,7 +276,7 @@ class Validator { readonly allSchemaProperties: ValidationSchema; constructor( - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, parametersSchema: ParametersSchema, bodySchema: BodySchema, ajv: { @@ -329,7 +330,7 @@ class Validator { class Security { public static queryParam( - apiDocs: OpenAPIV3.Document, + apiDocs: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, schema: OperationObject, ): string[] { const hasPathSecurity = schema.security?.length > 0 ?? false; diff --git a/src/middlewares/openapi.response.validator.ts b/src/middlewares/openapi.response.validator.ts index 6166ca5a..6fdad5bf 100644 --- a/src/middlewares/openapi.response.validator.ts +++ b/src/middlewares/openapi.response.validator.ts @@ -27,14 +27,14 @@ interface ValidateResult { } export class ResponseValidator { private ajvBody: Ajv; - private spec: OpenAPIV3.Document; + private spec: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; private validatorsCache: { [key: string]: { [key: string]: ValidateFunction }; } = {}; private eovOptions: ValidateResponseOpts; constructor( - openApiSpec: OpenAPIV3.Document, + openApiSpec: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: Options = {}, eovOptions: ValidateResponseOpts = {}, ) { diff --git a/src/middlewares/openapi.security.ts b/src/middlewares/openapi.security.ts index b3dbfe98..ee163138 100644 --- a/src/middlewares/openapi.security.ts +++ b/src/middlewares/openapi.security.ts @@ -23,7 +23,7 @@ interface SecurityHandlerResult { error?: string; } export function security( - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, securityHandlers: SecurityHandlers, ): OpenApiRequestHandler { return async (req, res, next) => { diff --git a/src/middlewares/parsers/req.parameter.mutator.ts b/src/middlewares/parsers/req.parameter.mutator.ts index 00ece3cd..cebe0b05 100644 --- a/src/middlewares/parsers/req.parameter.mutator.ts +++ b/src/middlewares/parsers/req.parameter.mutator.ts @@ -39,14 +39,14 @@ type Schema = ReferenceObject | SchemaObject; * the request is mutated to accomodate various styles and types e.g. form, explode, deepObject, etc */ export class RequestParameterMutator { - private _apiDocs: OpenAPIV3.Document; + private _apiDocs: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; private path: string; private ajv: Ajv; private parsedSchema: ValidationSchema; constructor( ajv: Ajv, - apiDocs: OpenAPIV3.Document, + apiDocs: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, path: string, parsedSchema: ValidationSchema, ) { diff --git a/src/middlewares/parsers/schema.parse.ts b/src/middlewares/parsers/schema.parse.ts index cbd58e47..fb5d380c 100644 --- a/src/middlewares/parsers/schema.parse.ts +++ b/src/middlewares/parsers/schema.parse.ts @@ -17,9 +17,9 @@ type Parameter = OpenAPIV3.ReferenceObject | OpenAPIV3.ParameterObject; */ export class ParametersSchemaParser { private _ajv: Ajv; - private _apiDocs: OpenAPIV3.Document; + private _apiDocs: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; - constructor(ajv: Ajv, apiDocs: OpenAPIV3.Document) { + constructor(ajv: Ajv, apiDocs: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1) { this._ajv = ajv; this._apiDocs = apiDocs; } diff --git a/src/middlewares/parsers/schema.preprocessor.ts b/src/middlewares/parsers/schema.preprocessor.ts index 283e8d4b..40d0babb 100644 --- a/src/middlewares/parsers/schema.preprocessor.ts +++ b/src/middlewares/parsers/schema.preprocessor.ts @@ -91,12 +91,12 @@ export const httpMethods = new Set([ ]); export class SchemaPreprocessor { private ajv: Ajv; - private apiDoc: OpenAPIV3.Document; - private apiDocRes: OpenAPIV3.Document; + private apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; + private apiDocRes: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; private serDesMap: SerDesMap; private responseOpts: ValidateResponseOpts; constructor( - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, ajvOptions: Options, validateResponsesOpts: ValidateResponseOpts, ) { @@ -108,22 +108,28 @@ export class SchemaPreprocessor { public preProcess() { const componentSchemas = this.gatherComponentSchemaNodes(); - const r = this.gatherSchemaNodesFromPaths(); + let r; + + if (this.apiDoc.paths) { + r = this.gatherSchemaNodesFromPaths(); + } // Now that we've processed paths, clone a response spec if we are validating responses this.apiDocRes = !!this.responseOpts ? cloneDeep(this.apiDoc) : null; const schemaNodes = { schemas: componentSchemas, - requestBodies: r.requestBodies, - responses: r.responses, - requestParameters: r.requestParameters, + requestBodies: r?.requestBodies, + responses: r?.responses, + requestParameters: r?.requestParameters, }; // Traverse the schemas - this.traverseSchemas(schemaNodes, (parent, schema, opts) => + if (r) { + this.traverseSchemas(schemaNodes, (parent, schema, opts) => this.schemaVisitor(parent, schema, opts), ); + } return { apiDoc: this.apiDoc, diff --git a/src/middlewares/parsers/util.ts b/src/middlewares/parsers/util.ts index aa0642e3..0c2119e3 100644 --- a/src/middlewares/parsers/util.ts +++ b/src/middlewares/parsers/util.ts @@ -4,7 +4,7 @@ import ajv = require('ajv'); import { OpenAPIFramework } from '../../framework'; export function dereferenceParameter( - apiDocs: OpenAPIV3.Document, + apiDocs: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, parameter: OpenAPIV3.ReferenceObject | OpenAPIV3.ParameterObject, ): OpenAPIV3.ParameterObject { // TODO this should recurse or use ajv.getSchema - if implemented as such, may want to cache the result diff --git a/src/openapi.validator.ts b/src/openapi.validator.ts index dd12fed2..4332521f 100644 --- a/src/openapi.validator.ts +++ b/src/openapi.validator.ts @@ -261,26 +261,26 @@ export class OpenApiValidator { private metadataMiddleware( context: OpenApiContext, - responseApiDoc: OpenAPIV3.Document, + responseApiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, ) { return middlewares.applyOpenApiMetadata(context, responseApiDoc); } - private multipartMiddleware(apiDoc: OpenAPIV3.Document) { + private multipartMiddleware(apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1) { return middlewares.multipart(apiDoc, { multerOpts: this.options.fileUploader, ajvOpts: this.ajvOpts.multipart, }); } - private securityMiddleware(apiDoc: OpenAPIV3.Document) { + private securityMiddleware(apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1) { const securityHandlers = (( this.options.validateSecurity ))?.handlers; return middlewares.security(apiDoc, securityHandlers); } - private requestValidationMiddleware(apiDoc: OpenAPIV3.Document) { + private requestValidationMiddleware(apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1) { const requestValidator = new middlewares.RequestValidator( apiDoc, this.ajvOpts.request, @@ -288,7 +288,7 @@ export class OpenApiValidator { return (req, res, next) => requestValidator.validate(req, res, next); } - private responseValidationMiddleware(apiDoc: OpenAPIV3.Document) { + private responseValidationMiddleware(apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1) { return new middlewares.ResponseValidator( apiDoc, this.ajvOpts.response, diff --git a/src/resolvers.ts b/src/resolvers.ts index 3092e3a3..18aba622 100644 --- a/src/resolvers.ts +++ b/src/resolvers.ts @@ -7,7 +7,7 @@ const cache = {}; export function defaultResolver( handlersPath: string, route: RouteMetadata, - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, ): RequestHandler { const tmpModules = {}; const { basePath, expressRoute, openApiRoute, method } = route; @@ -51,7 +51,7 @@ export function defaultResolver( export function modulePathResolver( handlersPath: string, route: RouteMetadata, - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, ): RequestHandler { const pathKey = route.openApiRoute.substring(route.basePath.length); const schema = apiDoc.paths[pathKey][route.method.toLowerCase()]; diff --git a/test/440.spec.ts b/test/440.spec.ts index e105f07e..50960245 100644 --- a/test/440.spec.ts +++ b/test/440.spec.ts @@ -9,7 +9,7 @@ describe(packageJson.name, () => { before(async () => { // Set up the express app - const apiSpec: OpenAPIV3.Document = { + const apiSpec: OpenAPIV3.DocumentV3 = { openapi: '3.0.0', info: { title: 'Api test', version: '1.0.0' }, servers: [{ url: '/api' }], diff --git a/test/478.spec.ts b/test/478.spec.ts index 5825bc22..232ae659 100644 --- a/test/478.spec.ts +++ b/test/478.spec.ts @@ -53,7 +53,7 @@ describe('issue #478', () => { .expect(200)); }); -function apiSpec(): OpenAPIV3.Document { +function apiSpec(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.3', info: { diff --git a/test/535.spec.ts b/test/535.spec.ts index c07beee8..a7dd79bb 100644 --- a/test/535.spec.ts +++ b/test/535.spec.ts @@ -19,7 +19,7 @@ describe('#535 - calling `middleware()` multiple times', () => { }); async function createApp( - apiSpec: OpenAPIV3.Document, + apiSpec: OpenAPIV3.DocumentV3, ): Promise { const app = express(); @@ -39,7 +39,7 @@ async function createApp( return app; } -function createApiSpec(): OpenAPIV3.Document { +function createApiSpec(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.3', info: { diff --git a/test/577.spec.ts b/test/577.spec.ts index 4c9b26fa..a142d45a 100644 --- a/test/577.spec.ts +++ b/test/577.spec.ts @@ -21,7 +21,7 @@ describe('#577 - Exclude response validation that is not in api spec', () => { }); async function createApp( - apiSpec: OpenAPIV3.Document, + apiSpec: OpenAPIV3.DocumentV3, ): Promise { const app = express(); @@ -51,7 +51,7 @@ async function createApp( return app; } -function createApiSpec(): OpenAPIV3.Document { +function createApiSpec(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.3', info: { diff --git a/test/699.spec.ts b/test/699.spec.ts index 2997299b..4e3399e2 100644 --- a/test/699.spec.ts +++ b/test/699.spec.ts @@ -182,7 +182,6 @@ describe('699 serialize response components only', () => { 3005, (app) => { app.get([`${app.basePath}/users/:id?`], (req, res) => { - debugger; if (typeof req.params.id !== 'string') { throw new Error("Should be not be deserialized to ObjectId object"); } diff --git a/test/821.spec.ts b/test/821.spec.ts index 42eda867..5b9bc75d 100644 --- a/test/821.spec.ts +++ b/test/821.spec.ts @@ -30,7 +30,7 @@ describe('issue #821 - serialization inside addiotionalProperties', () => { }); async function createApp( - apiSpec: OpenAPIV3.Document | string, + apiSpec: OpenAPIV3.DocumentV3 | string, ): Promise { const app = express(); diff --git a/test/allow.header.spec.ts b/test/allow.header.spec.ts index 25620c81..04415919 100644 --- a/test/allow.header.spec.ts +++ b/test/allow.header.spec.ts @@ -50,7 +50,7 @@ async function createApp(): Promise { return app; } -function createApiSpec(): OpenAPIV3.Document { +function createApiSpec(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.3', info: { diff --git a/test/invalid.apispec.spec.ts b/test/invalid.apispec.spec.ts index 2b7b02e8..0ded5b10 100644 --- a/test/invalid.apispec.spec.ts +++ b/test/invalid.apispec.spec.ts @@ -41,7 +41,7 @@ async function createApp( return app; } -function createApiSpec(): OpenAPIV3.Document { +function createApiSpec(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.3', info: { diff --git a/test/no.components.spec.ts b/test/no.components.spec.ts index 0373bd3f..866f371b 100644 --- a/test/no.components.spec.ts +++ b/test/no.components.spec.ts @@ -33,7 +33,7 @@ describe('no components', () => { })); }); -function apiDoc(): OpenAPIV3.Document { +function apiDoc(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.1', info: { diff --git a/test/petstore.spec.ts b/test/petstore.spec.ts index 1edf43e7..8588f5b5 100644 --- a/test/petstore.spec.ts +++ b/test/petstore.spec.ts @@ -36,7 +36,7 @@ describe('petstore', () => { request(app).get(`${app.basePath}/pets`).expect(200)); }); -function petstoreSpec(): OpenAPIV3.Document { +function petstoreSpec(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.0', info: { diff --git a/test/user-request-url.router.spec.ts b/test/user-request-url.router.spec.ts index 4bba5dee..d4470ba4 100644 --- a/test/user-request-url.router.spec.ts +++ b/test/user-request-url.router.spec.ts @@ -112,7 +112,7 @@ function defaultResponse(): OpenAPIV3.ResponseObject { type of id in path and id in the response here defined as simple string with minLength */ -const gatewaySpec: OpenAPIV3.Document = { +const gatewaySpec: OpenAPIV3.DocumentV3 = { openapi: '3.0.0', info: { version: '1.0.0', title: 'test bug OpenApiValidator' }, servers: [{ url: 'http://localhost:3000/api' }], @@ -168,7 +168,7 @@ const gatewaySpec: OpenAPIV3.Document = { represents spec of the child router. We route request from main app (gateway) to this router. This router has its own schema, routes and validation formats. In particular, we force id in the path and id in the response to be uuid. */ -const childRouterSpec: OpenAPIV3.Document = { +const childRouterSpec: OpenAPIV3.DocumentV3 = { openapi: '3.0.0', info: { version: '1.0.0', title: 'test bug OpenApiValidator' }, servers: [{ url: 'http://localhost:3000/' }], From 0c4f24cad89a37efe1ae384387e3971e7383c48e Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 5 Feb 2024 00:42:49 +0000 Subject: [PATCH 04/60] chore(test-scripts): run mocha with --extension instead of glob to pick up subdirectories Mocha was not picking up the tests in subdirectories with the provided glob. Adding --extension with the tests extension and setting the root test folder in tests fixed it --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index ba87f904..97455435 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "scripts": { "compile": "rimraf dist && tsc", "compile:release": "rimraf dist && tsc --sourceMap false", - "test": "mocha -r source-map-support/register -r ts-node/register --files --recursive -R spec test/**/*.spec.ts", - "test:debug": "mocha -r source-map-support/register -r ts-node/register --inspect-brk --files --recursive test/**/*.spec.ts", - "test:coverage": "nyc mocha -r source-map-support/register -r ts-node/register --recursive test/**/*.spec.ts", + "test": "mocha -r source-map-support/register -r ts-node/register --files --recursive -R spec --extension .spec.ts test", + "test:debug": "mocha -r source-map-support/register -r ts-node/register --inspect-brk --files --recursive --extension .spec.ts test", + "test:coverage": "nyc mocha -r source-map-support/register -r ts-node/register --recursive --extension .spec.ts test", "test:reset": "rimraf node_modules && npm i && npm run compile && npm t", "coveralls": "cat coverage/lcov.info | coveralls -v", "codacy": "bash <(curl -Ls https://coverage.codacy.com/get.sh) report -r coverage/lcov.info", From 289bd03333756c41cf916157f4c6af10a8dd2834 Mon Sep 17 00:00:00 2001 From: sf97 Date: Thu, 29 Feb 2024 23:28:45 +0000 Subject: [PATCH 05/60] test(openapi-3.1): adds test to ensure an API with only components is considered valid --- test/openapi_3.1/components.spec.ts | 30 ++++++++++++++++++++++ test/openapi_3.1/resources/components.yaml | 5 ++++ 2 files changed, 35 insertions(+) create mode 100644 test/openapi_3.1/components.spec.ts create mode 100644 test/openapi_3.1/resources/components.yaml diff --git a/test/openapi_3.1/components.spec.ts b/test/openapi_3.1/components.spec.ts new file mode 100644 index 00000000..958c81bb --- /dev/null +++ b/test/openapi_3.1/components.spec.ts @@ -0,0 +1,30 @@ +import * as request from 'supertest'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('components support - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'components.yaml'); + app = await createApp( + { apiSpec, validateRequests: true }, + 3005, + undefined, + false, + ); + }); + + after(() => { + app.server.close(); + }); + + it('should support an API that only has components defined, but provides no routes', () => { + // The component is not made available by the provider API, so the request will return 404 + // This test ensures that the request flow happens normally without any interruptions due to being a component + return request(app) + .get(`${app.basePath}/components`) + .expect(404); + }); + +}) \ No newline at end of file diff --git a/test/openapi_3.1/resources/components.yaml b/test/openapi_3.1/resources/components.yaml new file mode 100644 index 00000000..d9d51035 --- /dev/null +++ b/test/openapi_3.1/resources/components.yaml @@ -0,0 +1,5 @@ +openapi: 3.1.0 +info: + title: API + version: 1.0.0 +components: {} \ No newline at end of file From 0e6ae31f290f2ce87f0140e84e7dbea51a5d52b0 Mon Sep 17 00:00:00 2001 From: sf97 Date: Thu, 29 Feb 2024 23:29:27 +0000 Subject: [PATCH 06/60] test(openapi-3.1): remove unnecessary import --- test/openapi_3.1/webhook.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/openapi_3.1/webhook.spec.ts b/test/openapi_3.1/webhook.spec.ts index e088c12a..58e81b1d 100644 --- a/test/openapi_3.1/webhook.spec.ts +++ b/test/openapi_3.1/webhook.spec.ts @@ -1,4 +1,3 @@ -import { expect } from "chai"; import * as request from 'supertest'; import { createApp } from "../common/app"; import { join } from "path"; From 31613122b10982d864788183cdccb94ad8338463 Mon Sep 17 00:00:00 2001 From: sf97 Date: Thu, 29 Feb 2024 23:42:36 +0000 Subject: [PATCH 07/60] test(openapi-3.1): add support for summary in info object --- src/framework/types.ts | 9 ++++-- test/openapi_3.1/info_summary.spec.ts | 30 ++++++++++++++++++++ test/openapi_3.1/resources/info_summary.yaml | 7 +++++ 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 test/openapi_3.1/info_summary.spec.ts create mode 100644 test/openapi_3.1/resources/info_summary.yaml diff --git a/src/framework/types.ts b/src/framework/types.ts index 342a434b..2c82ac53 100644 --- a/src/framework/types.ts +++ b/src/framework/types.ts @@ -163,8 +163,9 @@ export namespace OpenAPIV3 { externalDocs?: ExternalDocumentationObject; } - export interface DocumentV3_1 extends Omit { - paths?: DocumentV3['paths'] + export interface DocumentV3_1 extends Omit { + paths?: DocumentV3['paths']; + info: InfoObjectV3_1; webhooks: { [name: string]: PathItemObject | ReferenceObject } @@ -179,6 +180,10 @@ export namespace OpenAPIV3 { version: string; } + interface InfoObjectV3_1 extends InfoObject { + summary: string; + } + export interface ContactObject { name?: string; url?: string; diff --git a/test/openapi_3.1/info_summary.spec.ts b/test/openapi_3.1/info_summary.spec.ts new file mode 100644 index 00000000..20e8c5dc --- /dev/null +++ b/test/openapi_3.1/info_summary.spec.ts @@ -0,0 +1,30 @@ +import * as request from 'supertest'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('summary support - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'info_summary.yaml'); + app = await createApp( + { apiSpec, validateRequests: true }, + 3005, + undefined, + false, + ); + }); + + after(() => { + app.server.close(); + }); + + it('should support an API that has an info with a summary defined', () => { + // The endpoint is not made available by the provider API, so the request will return 404 + // This test ensures that the request flow happens normally without any interruptions + return request(app) + .get(`${app.basePath}/webhook`) + .expect(404); + }); + +}) \ No newline at end of file diff --git a/test/openapi_3.1/resources/info_summary.yaml b/test/openapi_3.1/resources/info_summary.yaml new file mode 100644 index 00000000..27e120cd --- /dev/null +++ b/test/openapi_3.1/resources/info_summary.yaml @@ -0,0 +1,7 @@ +# From https://github.com/OAI/OpenAPI-Specification/blob/6f386968654fd483720aba0177e618e87a5d612d/tests/v3.1/pass/info_summary.yaml +openapi: 3.1.0 +info: + title: API + summary: My lovely API + version: 1.0.0 +components: {} \ No newline at end of file From 7698f028bc139cd7cce119474d69efb3fe94408a Mon Sep 17 00:00:00 2001 From: sf97 Date: Thu, 29 Feb 2024 23:53:55 +0000 Subject: [PATCH 08/60] test(openapi-3.1): add support for identifier in license --- test/openapi_3.1/license_identifier.spec.ts | 30 +++++++++++++++++++ test/openapi_3.1/resources/components.yaml | 1 + .../resources/license_identifier.yaml | 10 +++++++ 3 files changed, 41 insertions(+) create mode 100644 test/openapi_3.1/license_identifier.spec.ts create mode 100644 test/openapi_3.1/resources/license_identifier.yaml diff --git a/test/openapi_3.1/license_identifier.spec.ts b/test/openapi_3.1/license_identifier.spec.ts new file mode 100644 index 00000000..7f615a93 --- /dev/null +++ b/test/openapi_3.1/license_identifier.spec.ts @@ -0,0 +1,30 @@ +import * as request from 'supertest'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('identifier support - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'license_identifier.yaml'); + app = await createApp( + { apiSpec, validateRequests: true }, + 3005, + undefined, + false, + ); + }); + + after(() => { + app.server.close(); + }); + + it('should support an API that has an info with a summary defined', () => { + // The endpoint is not made available by the provider API, so the request will return 404 + // This test ensures that the request flow happens normally without any interruptions + return request(app) + .get(`${app.basePath}/webhook`) + .expect(404); + }); + +}) \ No newline at end of file diff --git a/test/openapi_3.1/resources/components.yaml b/test/openapi_3.1/resources/components.yaml index d9d51035..b968adec 100644 --- a/test/openapi_3.1/resources/components.yaml +++ b/test/openapi_3.1/resources/components.yaml @@ -1,3 +1,4 @@ +# From https://github.com/OAI/OpenAPI-Specification/blob/6f386968654fd483720aba0177e618e87a5d612d/tests/v3.1/pass/minimal_comp.yaml openapi: 3.1.0 info: title: API diff --git a/test/openapi_3.1/resources/license_identifier.yaml b/test/openapi_3.1/resources/license_identifier.yaml new file mode 100644 index 00000000..85dd47a1 --- /dev/null +++ b/test/openapi_3.1/resources/license_identifier.yaml @@ -0,0 +1,10 @@ +# From https://github.com/OAI/OpenAPI-Specification/blob/6f386968654fd483720aba0177e618e87a5d612d/tests/v3.1/pass/license_identifier.yaml +openapi: 3.1.0 +info: + title: API + summary: My lovely API + version: 1.0.0 + license: + name: Apache + identifier: Apache-2.0 +components: {} \ No newline at end of file From 15d90498e699097e7e067c231f1c6ba85b656f6f Mon Sep 17 00:00:00 2001 From: sf97 Date: Sun, 31 Mar 2024 12:45:50 +0100 Subject: [PATCH 09/60] test(openapi_3.1): ensure API with type set to null works correctly --- test/openapi_3.1/resources/type_null.yaml | 23 ++++++++++++++ test/openapi_3.1/type_null.spec.ts | 37 +++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 test/openapi_3.1/resources/type_null.yaml create mode 100644 test/openapi_3.1/type_null.spec.ts diff --git a/test/openapi_3.1/resources/type_null.yaml b/test/openapi_3.1/resources/type_null.yaml new file mode 100644 index 00000000..54d885f6 --- /dev/null +++ b/test/openapi_3.1/resources/type_null.yaml @@ -0,0 +1,23 @@ +openapi: 3.1.0 +info: + title: API + version: 1.0.0 +servers: + - url: /v1 +paths: + /entity: + get: + summary: test + description: GETS my entity + responses: + '200': + description: OK + content: + application/json: + schema: + title: Entity + type: object + properties: + property: + type: ['string', 'null'] + \ No newline at end of file diff --git a/test/openapi_3.1/type_null.spec.ts b/test/openapi_3.1/type_null.spec.ts new file mode 100644 index 00000000..452626c2 --- /dev/null +++ b/test/openapi_3.1/type_null.spec.ts @@ -0,0 +1,37 @@ +import * as request from 'supertest'; +import * as express from 'express'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('type null support - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'type_null.yaml'); + app = await createApp( + { apiSpec, validateRequests: true, validateResponses: true }, + 3005, + (app) => app.use( + express + .Router() + .get(`/v1/entity`, (req, res) => + res.status(200).json({ + property: null + }), + ), + ) + ); + }); + + after(() => { + app.server.close(); + }); + + // In OpenAPI 3.1, nullable = true was replaced by types = [..., null]. This test ensure that it works with Express OpenAPI Validator + it('should support an API with types set to null', async () => { + return request(app) + .get(`${app.basePath}/entity`) + .expect(200); + }); + +}) \ No newline at end of file From 05874154d0d6137b70c502a909ede1d0f8dfc816 Mon Sep 17 00:00:00 2001 From: sf97 Date: Sun, 31 Mar 2024 13:19:34 +0100 Subject: [PATCH 10/60] test(open_api3.1): ensure that methods with non-explicit semantics allow request body --- ...non_defined_semantics_request_body.spec.ts | 48 ++++++++++++++++ .../non_defined_semantics_request_body.yaml | 55 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 test/openapi_3.1/non_defined_semantics_request_body.spec.ts create mode 100644 test/openapi_3.1/resources/non_defined_semantics_request_body.yaml diff --git a/test/openapi_3.1/non_defined_semantics_request_body.spec.ts b/test/openapi_3.1/non_defined_semantics_request_body.spec.ts new file mode 100644 index 00000000..e843a587 --- /dev/null +++ b/test/openapi_3.1/non_defined_semantics_request_body.spec.ts @@ -0,0 +1,48 @@ +import * as request from 'supertest'; +import * as express from 'express'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('Request body in operations without well defined semantics - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'non_defined_semantics_request_body.yaml'); + app = await createApp( + { apiSpec, validateRequests: true, validateResponses: true }, + 3005, + (app) => app.use( + express + .Router() + .get(`/v1/entity`, (req, res) => + res.status(200).json({ + property: null + }), + ), + ) + ); + }); + + after(() => { + app.server.close(); + }); + + // In OpenAPI 3.0, methods that RFC7231 does not have explicitly defined semantics for request body (GET, HEAD, DELETE) do not allow request body + // In OpenAPI 3.1, request body is allowed for these methods. This test ensures that GET it is correctly handled + it('should validate a request body on GET', async () => { + return request(app) + .get(`${app.basePath}/entity`) + .set('Content-Type', 'application/json') + .send({request: 123}) + .expect(400); + }); + + // Ensures that DELETE it is correctly handled + it('should validate a request body on DELETE', async () => { + return request(app) + .delete(`${app.basePath}/entity`) + .set('Content-Type', 'application/json') + .send({request: 123}) + .expect(400); + }); +}) \ No newline at end of file diff --git a/test/openapi_3.1/resources/non_defined_semantics_request_body.yaml b/test/openapi_3.1/resources/non_defined_semantics_request_body.yaml new file mode 100644 index 00000000..5114a27a --- /dev/null +++ b/test/openapi_3.1/resources/non_defined_semantics_request_body.yaml @@ -0,0 +1,55 @@ +openapi: 3.1.0 +info: + title: API + version: 1.0.0 +servers: + - url: /v1 +components: + schemas: + EntityRequest: + type: object + properties: + request: + type: string +paths: + /entity: + get: + description: GETS my entity + requestBody: + description: Request body for entity + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EntityRequest' + responses: + '200': + description: OK + content: + application/json: + schema: + title: Entity + type: object + properties: + property: + type: ['string', 'null'] + delete: + description: DELETE my entity + requestBody: + description: Request body for entity + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EntityRequest' + responses: + '200': + description: OK + content: + application/json: + schema: + title: Entity + type: object + properties: + property: + type: ['string', 'null'] \ No newline at end of file From 415de44b8d2701742858177567a4a5bb6f1aa45f Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 1 Apr 2024 00:14:36 +0100 Subject: [PATCH 11/60] test(open_api3.1): ensure 500 is returned when server variable has no default --- .../resources/server_variable_no_default.yaml | 13 ++++++++++++ test/openapi_3.1/server_variable.spec.ts | 21 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 test/openapi_3.1/resources/server_variable_no_default.yaml create mode 100644 test/openapi_3.1/server_variable.spec.ts diff --git a/test/openapi_3.1/resources/server_variable_no_default.yaml b/test/openapi_3.1/resources/server_variable_no_default.yaml new file mode 100644 index 00000000..8408bf44 --- /dev/null +++ b/test/openapi_3.1/resources/server_variable_no_default.yaml @@ -0,0 +1,13 @@ +# Adapted from https://github.com/OAI/OpenAPI-Specification/blob/77c7b9a522ab6fb83a49e8088fa600e93da4f44e/tests/v3.1/fail/server_enum_empty.yaml + +openapi: 3.1.0 +info: + title: API + version: 1.0.0 +servers: + - url: https://example.com/test + variables: + var: + enum: ['a', 'b'] +components: + {} \ No newline at end of file diff --git a/test/openapi_3.1/server_variable.spec.ts b/test/openapi_3.1/server_variable.spec.ts new file mode 100644 index 00000000..d790b9c6 --- /dev/null +++ b/test/openapi_3.1/server_variable.spec.ts @@ -0,0 +1,21 @@ +import * as request from 'supertest'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('server variable - OpenAPI 3.1', () => { + it('returns 500 when server variable has no default property', async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'server_variable_no_default.yaml'); + const app = await createApp( + { apiSpec, validateRequests: true, validateResponses: true }, + 3005, + undefined, + false, + ) as any; + + await request(app) + .get(`${app.basePath}`) + .expect(500); + + app.server.close(); + }); +}) \ No newline at end of file From 7bcf518c6e87d6230075b883f45e2ea4ad78d66c Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 1 Apr 2024 00:32:58 +0100 Subject: [PATCH 12/60] feat(openapi_3.1): ensure API supports an endpoint without response --- .../parsers/schema.preprocessor.ts | 2 +- test/openapi_3.1/path_no_response.spec.ts | 36 +++++++++++++++++++ .../resources/path_no_response.yaml | 11 ++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 test/openapi_3.1/path_no_response.spec.ts create mode 100644 test/openapi_3.1/resources/path_no_response.yaml diff --git a/src/middlewares/parsers/schema.preprocessor.ts b/src/middlewares/parsers/schema.preprocessor.ts index 40d0babb..fc82c582 100644 --- a/src/middlewares/parsers/schema.preprocessor.ts +++ b/src/middlewares/parsers/schema.preprocessor.ts @@ -504,7 +504,7 @@ export class SchemaPreprocessor { const op = node.schema; const responses = op.responses; - if (!responses) return; + if (!responses) return []; const schemas: Root[] = []; for (const [statusCode, response] of Object.entries(responses)) { diff --git a/test/openapi_3.1/path_no_response.spec.ts b/test/openapi_3.1/path_no_response.spec.ts new file mode 100644 index 00000000..b40619e3 --- /dev/null +++ b/test/openapi_3.1/path_no_response.spec.ts @@ -0,0 +1,36 @@ +import * as request from 'supertest'; +import * as express from 'express'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('operation object without response - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'path_no_response.yaml'); + app = await createApp( + { apiSpec, validateRequests: true, validateResponses: true }, + 3005, + (app) => app.use( + express + .Router() + .get(`/v1`, (req, res) => + res.status(200).end(), + ), + ) + ); + app + }); + + after(() => { + app.server.close(); + }); + + // In OpenAPI 3.1 it's possible to have a path without a response defined + it('should support endpoint with defined operation object without response', () => { + return request(app) + .get(`${app.basePath}`) + .expect(200); + }); + +}) \ No newline at end of file diff --git a/test/openapi_3.1/resources/path_no_response.yaml b/test/openapi_3.1/resources/path_no_response.yaml new file mode 100644 index 00000000..5426037b --- /dev/null +++ b/test/openapi_3.1/resources/path_no_response.yaml @@ -0,0 +1,11 @@ +# Adapted from https://github.com/OAI/OpenAPI-Specification/blob/77c7b9a522ab6fb83a49e8088fa600e93da4f44e/tests/v3.1/pass/path_no_response.yaml + +openapi: 3.1.0 +info: + title: API + version: 1.0.0 +servers: + - url: /v1 +paths: + /: + get: {} \ No newline at end of file From 45842613f358dd840f848785006efd800b71c9cd Mon Sep 17 00:00:00 2001 From: sf97 Date: Sun, 28 Apr 2024 19:45:42 +0100 Subject: [PATCH 13/60] feat(openapi_3.1): add full type support for open api 3.1 --- src/framework/types.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/framework/types.ts b/src/framework/types.ts index fcc8f294..a294d52c 100644 --- a/src/framework/types.ts +++ b/src/framework/types.ts @@ -163,9 +163,14 @@ export namespace OpenAPIV3 { externalDocs?: ExternalDocumentationObject; } - export interface DocumentV3_1 extends Omit { + interface ComponentsV3_1 extends ComponentsObject { + pathItems?: { [path: string]: PathItemObject | ReferenceObject } + } + + export interface DocumentV3_1 extends Omit { paths?: DocumentV3['paths']; info: InfoObjectV3_1; + components: ComponentsV3_1; webhooks: { [name: string]: PathItemObject | ReferenceObject } From 9c581667ee601b36763bf64b75f1b2f5c7d1c1eb Mon Sep 17 00:00:00 2001 From: sf97 Date: Sun, 28 Apr 2024 20:08:56 +0100 Subject: [PATCH 14/60] test(openapi_3.1): adds test for path item support in components --- .../openapi_3.1/components_path_items.spec.ts | 34 +++++++++++++++++++ .../resources/components_path_items.yaml | 21 ++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 test/openapi_3.1/components_path_items.spec.ts create mode 100644 test/openapi_3.1/resources/components_path_items.yaml diff --git a/test/openapi_3.1/components_path_items.spec.ts b/test/openapi_3.1/components_path_items.spec.ts new file mode 100644 index 00000000..cbd282da --- /dev/null +++ b/test/openapi_3.1/components_path_items.spec.ts @@ -0,0 +1,34 @@ +import * as request from 'supertest'; +import * as express from 'express'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('component path item support - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'components_path_items.yaml'); + app = await createApp( + { apiSpec, validateRequests: true, validateResponses: true }, + 3005, + (app) => app.use( + express + .Router() + .get(`/v1/entity`, (req, res) => + res.status(200).json({}), + ), + ) + ); + }); + + after(() => { + app.server.close(); + }); + + it('should support path item on components', async () => { + return request(app) + .get(`${app.basePath}/entity`) + .expect(200); + }); + +}) \ No newline at end of file diff --git a/test/openapi_3.1/resources/components_path_items.yaml b/test/openapi_3.1/resources/components_path_items.yaml new file mode 100644 index 00000000..030e6757 --- /dev/null +++ b/test/openapi_3.1/resources/components_path_items.yaml @@ -0,0 +1,21 @@ +openapi: 3.1.0 +info: + title: Example specification + version: "1.0" +servers: + - url: /v1 +components: + pathItems: + entity: + get: + description: 'test' + responses: + 200: + description: GETS my entity + content: + application/json: + schema: + type: object +paths: + /entity: + $ref: '#/components/pathItems/entity' From 58975899747357f1d3ce511715b18950110f7178 Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 13 May 2024 23:27:33 +0100 Subject: [PATCH 15/60] fix(openapi_3.1_schema): update schema to fix bug --- .../openapi.v3_1.modified.schema.json | 311 +++++------------- 1 file changed, 86 insertions(+), 225 deletions(-) diff --git a/src/framework/openapi.v3_1.modified.schema.json b/src/framework/openapi.v3_1.modified.schema.json index 76e4df1d..f34616fe 100644 --- a/src/framework/openapi.v3_1.modified.schema.json +++ b/src/framework/openapi.v3_1.modified.schema.json @@ -21,9 +21,11 @@ "items": { "$ref": "#/$defs/server" }, - "default": [{ - "url": "/" - }] + "default": [ + { + "url": "/" + } + ] }, "paths": { "$ref": "#/$defs/paths" @@ -31,7 +33,7 @@ "webhooks": { "type": "object", "additionalProperties": { - "$ref": "#/$defs/path-item-or-reference" + "$ref": "#/$defs/path-item" } }, "components": { @@ -53,24 +55,16 @@ "$ref": "#/$defs/external-documentation" } }, - "required": [ - "openapi", - "info" - ], - "anyOf": [{ - "required": [ - "paths" - ] + "required": ["openapi", "info"], + "anyOf": [ + { + "required": ["paths"] }, { - "required": [ - "components" - ] + "required": ["components"] }, { - "required": [ - "webhooks" - ] + "required": ["webhooks"] } ], "$ref": "#/$defs/specification-extensions", @@ -103,10 +97,7 @@ "type": "string" } }, - "required": [ - "title", - "version" - ], + "required": ["title", "version"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -144,15 +135,11 @@ "format": "uri" } }, - "required": [ - "name" - ], + "required": ["name"], "dependentSchemas": { "identifier": { "not": { - "required": [ - "url" - ] + "required": ["url"] } } }, @@ -164,8 +151,7 @@ "type": "object", "properties": { "url": { - "type": "string", - "format": "uri-reference" + "type": "string" }, "description": { "type": "string" @@ -177,9 +163,7 @@ } } }, - "required": [ - "url" - ], + "required": ["url"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -201,9 +185,7 @@ "type": "string" } }, - "required": [ - "default" - ], + "required": ["default"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -268,7 +250,7 @@ "pathItems": { "type": "object", "additionalProperties": { - "$ref": "#/$defs/path-item-or-reference" + "$ref": "#/$defs/path-item" } } }, @@ -298,6 +280,10 @@ "$comment": "https://spec.openapis.org/oas/v3.1.0#path-item-object", "type": "object", "properties": { + "$ref": { + "type": "string", + "format": "uri-reference" + }, "summary": { "type": "string" }, @@ -344,20 +330,6 @@ "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, - "path-item-or-reference": { - "if": { - "type": "object", - "required": [ - "$ref" - ] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/path-item" - } - }, "operation": { "$comment": "https://spec.openapis.org/oas/v3.1.0#operation-object", "type": "object", @@ -430,9 +402,7 @@ "format": "uri" } }, - "required": [ - "url" - ], + "required": ["url"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -444,12 +414,7 @@ "type": "string" }, "in": { - "enum": [ - "query", - "header", - "path", - "cookie" - ] + "enum": ["query", "header", "path", "cookie"] }, "description": { "type": "string" @@ -471,19 +436,13 @@ "maxProperties": 1 } }, - "required": [ - "name", - "in" - ], - "oneOf": [{ - "required": [ - "schema" - ] + "required": ["name", "in"], + "oneOf": [ + { + "required": ["schema"] }, { - "required": [ - "content" - ] + "required": ["content"] } ], "if": { @@ -492,9 +451,7 @@ "const": "query" } }, - "required": [ - "in" - ] + "required": ["in"] }, "then": { "properties": { @@ -514,7 +471,8 @@ "type": "boolean" } }, - "allOf": [{ + "allOf": [ + { "$ref": "#/$defs/examples" }, { @@ -541,27 +499,19 @@ "const": "path" } }, - "required": [ - "in" - ] + "required": ["in"] }, "then": { "properties": { "style": { "default": "simple", - "enum": [ - "matrix", - "label", - "simple" - ] + "enum": ["matrix", "label", "simple"] }, "required": { "const": true } }, - "required": [ - "required" - ] + "required": ["required"] } }, "styles-for-header": { @@ -571,9 +521,7 @@ "const": "header" } }, - "required": [ - "in" - ] + "required": ["in"] }, "then": { "properties": { @@ -591,9 +539,7 @@ "const": "query" } }, - "required": [ - "in" - ] + "required": ["in"] }, "then": { "properties": { @@ -620,9 +566,7 @@ "const": "cookie" } }, - "required": [ - "in" - ] + "required": ["in"] }, "then": { "properties": { @@ -642,9 +586,7 @@ "parameter-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -668,18 +610,14 @@ "type": "boolean" } }, - "required": [ - "content" - ], + "required": ["content"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, "request-body-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -712,7 +650,8 @@ } } }, - "allOf": [{ + "allOf": [ + { "$ref": "#/$defs/specification-extensions" }, { @@ -737,12 +676,7 @@ }, "style": { "default": "form", - "enum": [ - "form", - "spaceDelimited", - "pipeDelimited", - "deepObject" - ] + "enum": ["form", "spaceDelimited", "pipeDelimited", "deepObject"] }, "explode": { "type": "boolean" @@ -752,7 +686,8 @@ "type": "boolean" } }, - "allOf": [{ + "allOf": [ + { "$ref": "#/$defs/specification-extensions" }, { @@ -810,18 +745,14 @@ } } }, - "required": [ - "description" - ], + "required": ["description"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, "response-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -835,15 +766,13 @@ "type": "object", "$ref": "#/$defs/specification-extensions", "additionalProperties": { - "$ref": "#/$defs/path-item-or-reference" + "$ref": "#/$defs/path-item" } }, "callbacks-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -869,10 +798,7 @@ } }, "not": { - "required": [ - "value", - "externalValue" - ] + "required": ["value", "externalValue"] }, "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false @@ -880,9 +806,7 @@ "example-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -913,15 +837,12 @@ "$ref": "#/$defs/server" } }, - "oneOf": [{ - "required": [ - "operationRef" - ] + "oneOf": [ + { + "required": ["operationRef"] }, { - "required": [ - "operationId" - ] + "required": ["operationId"] } ], "$ref": "#/$defs/specification-extensions", @@ -930,9 +851,7 @@ "link-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -965,15 +884,12 @@ "maxProperties": 1 } }, - "oneOf": [{ - "required": [ - "schema" - ] + "oneOf": [ + { + "required": ["schema"] }, { - "required": [ - "content" - ] + "required": ["content"] } ], "dependentSchemas": { @@ -997,9 +913,7 @@ "header-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -1022,9 +936,7 @@ "$ref": "#/$defs/external-documentation" } }, - "required": [ - "name" - ], + "required": ["name"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -1047,32 +959,22 @@ "schema": { "$comment": "https://spec.openapis.org/oas/v3.1.0#schema-object", "$dynamicAnchor": "meta", - "type": [ - "object", - "boolean" - ] + "type": ["object", "boolean"] }, "security-scheme": { "$comment": "https://spec.openapis.org/oas/v3.1.0#security-scheme-object", "type": "object", "properties": { "type": { - "enum": [ - "apiKey", - "http", - "mutualTLS", - "oauth2", - "openIdConnect" - ] + "enum": ["apiKey", "http", "mutualTLS", "oauth2", "openIdConnect"] }, "description": { "type": "string" } }, - "required": [ - "type" - ], - "allOf": [{ + "required": ["type"], + "allOf": [ + { "$ref": "#/$defs/specification-extensions" }, { @@ -1100,9 +1002,7 @@ "const": "apiKey" } }, - "required": [ - "type" - ] + "required": ["type"] }, "then": { "properties": { @@ -1110,17 +1010,10 @@ "type": "string" }, "in": { - "enum": [ - "query", - "header", - "cookie" - ] + "enum": ["query", "header", "cookie"] } }, - "required": [ - "name", - "in" - ] + "required": ["name", "in"] } }, "type-http": { @@ -1130,9 +1023,7 @@ "const": "http" } }, - "required": [ - "type" - ] + "required": ["type"] }, "then": { "properties": { @@ -1140,9 +1031,7 @@ "type": "string" } }, - "required": [ - "scheme" - ] + "required": ["scheme"] } }, "type-http-bearer": { @@ -1156,10 +1045,7 @@ "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" } }, - "required": [ - "type", - "scheme" - ] + "required": ["type", "scheme"] }, "then": { "properties": { @@ -1176,9 +1062,7 @@ "const": "oauth2" } }, - "required": [ - "type" - ] + "required": ["type"] }, "then": { "properties": { @@ -1186,9 +1070,7 @@ "$ref": "#/$defs/oauth-flows" } }, - "required": [ - "flows" - ] + "required": ["flows"] } }, "type-oidc": { @@ -1198,9 +1080,7 @@ "const": "openIdConnect" } }, - "required": [ - "type" - ] + "required": ["type"] }, "then": { "properties": { @@ -1209,9 +1089,7 @@ "format": "uri" } }, - "required": [ - "openIdConnectUrl" - ] + "required": ["openIdConnectUrl"] } } } @@ -1219,9 +1097,7 @@ "security-scheme-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -1264,10 +1140,7 @@ "$ref": "#/$defs/map-of-strings" } }, - "required": [ - "authorizationUrl", - "scopes" - ], + "required": ["authorizationUrl", "scopes"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -1286,10 +1159,7 @@ "$ref": "#/$defs/map-of-strings" } }, - "required": [ - "tokenUrl", - "scopes" - ], + "required": ["tokenUrl", "scopes"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -1308,10 +1178,7 @@ "$ref": "#/$defs/map-of-strings" } }, - "required": [ - "tokenUrl", - "scopes" - ], + "required": ["tokenUrl", "scopes"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -1334,11 +1201,7 @@ "$ref": "#/$defs/map-of-strings" } }, - "required": [ - "authorizationUrl", - "tokenUrl", - "scopes" - ], + "required": ["authorizationUrl", "tokenUrl", "scopes"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false } @@ -1384,9 +1247,7 @@ "const": "form" } }, - "required": [ - "style" - ] + "required": ["style"] }, "then": { "properties": { @@ -1404,4 +1265,4 @@ } } } -} \ No newline at end of file +} From 965441718baeec5fc75029dfbb0b147c318bb1e4 Mon Sep 17 00:00:00 2001 From: sf97 Date: Thu, 16 May 2024 23:19:17 +0100 Subject: [PATCH 16/60] feat(openapi_3.1): support reusable path items --- src/middlewares/parsers/schema.preprocessor.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/middlewares/parsers/schema.preprocessor.ts b/src/middlewares/parsers/schema.preprocessor.ts index 6b18d7a2..5c8ca580 100644 --- a/src/middlewares/parsers/schema.preprocessor.ts +++ b/src/middlewares/parsers/schema.preprocessor.ts @@ -157,6 +157,11 @@ export class SchemaPreprocessor { for (const [p, pi] of Object.entries(this.apiDoc.paths)) { const pathItem = this.resolveSchema(pi); + + // Since OpenAPI 3.1, paths can be a #ref to reusable path items + // The following line mutates the paths item to dereference the reference, so that we can process as a POJO, as we would if it wasn't a reference + this.apiDoc.paths[p] = pathItem; + for (const method of Object.keys(pathItem)) { if (httpMethods.has(method)) { const operation = pathItem[method]; From ddc0ebc1c34c0399cf956ddb77c9d63454890964 Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 20 May 2024 20:51:59 +0100 Subject: [PATCH 17/60] style(linting): fix linting issues --- src/middlewares/openapi.request.validator.ts | 1 - src/middlewares/parsers/schema.preprocessor.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/middlewares/openapi.request.validator.ts b/src/middlewares/openapi.request.validator.ts index 06e2527e..527254ef 100644 --- a/src/middlewares/openapi.request.validator.ts +++ b/src/middlewares/openapi.request.validator.ts @@ -21,7 +21,6 @@ import { import { BodySchemaParser } from './parsers/body.parse'; import { ParametersSchemaParser } from './parsers/schema.parse'; import { RequestParameterMutator } from './parsers/req.parameter.mutator'; -import { debug } from 'console'; type OperationObject = OpenAPIV3.OperationObject; type SchemaObject = OpenAPIV3.SchemaObject; diff --git a/src/middlewares/parsers/schema.preprocessor.ts b/src/middlewares/parsers/schema.preprocessor.ts index 5c8ca580..b6f756e8 100644 --- a/src/middlewares/parsers/schema.preprocessor.ts +++ b/src/middlewares/parsers/schema.preprocessor.ts @@ -127,8 +127,8 @@ export class SchemaPreprocessor { // Traverse the schemas if (r) { this.traverseSchemas(schemaNodes, (parent, schema, opts) => - this.schemaVisitor(parent, schema, opts), - ); + this.schemaVisitor(parent, schema, opts), + ); } return { From 2cae4b5592ed56b12d02643f3013655a9bd30174 Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 20 May 2024 21:00:46 +0100 Subject: [PATCH 18/60] style(openapi): improve readability of version validation --- src/framework/openapi.schema.validator.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/framework/openapi.schema.validator.ts b/src/framework/openapi.schema.validator.ts index 81a20f7b..46d40cd6 100644 --- a/src/framework/openapi.schema.validator.ts +++ b/src/framework/openapi.schema.validator.ts @@ -32,22 +32,26 @@ export class OpenAPISchemaValidator { options.validateSchema = false; } - const ver = opts.version && parseFloat(String(opts.version)); - if (!ver) throw Error('version missing from OpenAPI specification'); - if (parseInt(ver.toString()) != 3) throw Error('OpenAPI v3 specification version is required'); + const [ok, major, minor] = /^(\d+)\.(\d+).(\d+)?$/.exec(opts.version); + + if (!ok) { + throw Error('Version missing from OpenAPI specification') + }; + + if (major !== '3' || minor !== '0' && minor !== '1') { + throw new Error('OpenAPI v3.0 or v3.1 specification version is required'); + } let ajvInstance; let schema; - if (ver === 3) { + if (minor === '0') { schema = openapi3Schema; ajvInstance = new AjvDraft4(options); - } else if (ver === 3.1) { + } else if (minor == '1') { schema = openapi31Schema; ajvInstance = new Ajv2020(options); ajvInstance.addFormat('media-range', true); // TODO: Validate media-range format as defined in https://www.rfc-editor.org/rfc/rfc9110.html#name-collected-abnf - } else { - throw new Error('OpenAPI v3 specification 3.0 and 3.1 supported'); } addFormats(ajvInstance, ['email', 'regex', 'uri', 'uri-reference']); From 81b45eef26526b47b454f04aac5ff24b08e3a925 Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 20 May 2024 21:24:46 +0100 Subject: [PATCH 19/60] docs(schema-validator): clearly state why media-range attribute is not defined --- src/framework/openapi.schema.validator.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/framework/openapi.schema.validator.ts b/src/framework/openapi.schema.validator.ts index 46d40cd6..025f201f 100644 --- a/src/framework/openapi.schema.validator.ts +++ b/src/framework/openapi.schema.validator.ts @@ -51,7 +51,12 @@ export class OpenAPISchemaValidator { } else if (minor == '1') { schema = openapi31Schema; ajvInstance = new Ajv2020(options); - ajvInstance.addFormat('media-range', true); // TODO: Validate media-range format as defined in https://www.rfc-editor.org/rfc/rfc9110.html#name-collected-abnf + + // Open API 3.1 has a custom "media-range" attribute defined in its schema, but the spec does not define it. "It's not really intended to be validated" + // https://github.com/OAI/OpenAPI-Specification/issues/2714#issuecomment-923185689 + // Since the schema is non-normative (https://github.com/OAI/OpenAPI-Specification/pull/3355#issuecomment-1915695294) we will only validate that it's a string + // as the spec states + ajvInstance.addFormat('media-range', true); } addFormats(ajvInstance, ['email', 'regex', 'uri', 'uri-reference']); From ebb9b25bcab761c37c02768ee815b9f4fd68b8d7 Mon Sep 17 00:00:00 2001 From: carmine Date: Sat, 24 Aug 2024 11:30:16 -0400 Subject: [PATCH 20/60] version 6.0.0-alpha.1 with initial OAS-3.1 support (from PR https://github.com/cdimascio/express-openapi-validator/pull/882) --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7d3a6237..235567f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "express-openapi-validator", - "version": "5.3.3", + "version": "6.0.0-alpha.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "express-openapi-validator", - "version": "5.3.3", + "version": "6.0.0-alpha.1", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.7.0", diff --git a/package.json b/package.json index 1e59122c..b994f522 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "express-openapi-validator", - "version": "5.3.3", + "version": "6.0.0-alpha.1", "description": "Automatically validate API requests and responses with OpenAPI 3 and Express.", "main": "dist/index.js", "scripts": { From 334e2de24aa7f49d0910b299c2de8967a59e11fb Mon Sep 17 00:00:00 2001 From: carmine Date: Sat, 31 Aug 2024 19:51:13 -0400 Subject: [PATCH 21/60] v6.0.0-alpha.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 235567f3..e638110b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "express-openapi-validator", - "version": "6.0.0-alpha.1", + "version": "6.0.0-alpha.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "express-openapi-validator", - "version": "6.0.0-alpha.1", + "version": "6.0.0-alpha.2", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.7.0", diff --git a/package.json b/package.json index b994f522..b8d6a241 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "express-openapi-validator", - "version": "6.0.0-alpha.1", + "version": "6.0.0-alpha.2", "description": "Automatically validate API requests and responses with OpenAPI 3 and Express.", "main": "dist/index.js", "scripts": { From 8f43042e4738b1f8236f55dd5dfa67b1f8cc6e0d Mon Sep 17 00:00:00 2001 From: Sergio Ferreira Date: Sun, 31 Dec 2023 09:57:37 +0000 Subject: [PATCH 22/60] feat(openapi): support version 3.1 --- src/framework/openapi.schema.validator.ts | 32 +- .../openapi.v3_1.modified.schema.json | 1407 +++++++++++++++++ 2 files changed, 1431 insertions(+), 8 deletions(-) create mode 100644 src/framework/openapi.v3_1.modified.schema.json diff --git a/src/framework/openapi.schema.validator.ts b/src/framework/openapi.schema.validator.ts index 03da0129..b378bd62 100644 --- a/src/framework/openapi.schema.validator.ts +++ b/src/framework/openapi.schema.validator.ts @@ -6,8 +6,12 @@ import AjvDraft4, { import addFormats from 'ajv-formats'; // https://github.com/OAI/OpenAPI-Specification/blob/master/schemas/v3.0/schema.json import * as openapi3Schema from './openapi.v3.schema.json'; +// https://github.com/OAI/OpenAPI-Specification/blob/master/schemas/v3.1/schema.json with dynamic refs replaced due to AJV bug - https://github.com/ajv-validator/ajv/issues/1745 +import * as openapi31Schema from './openapi.v3_1.modified.schema.json'; import { OpenAPIV3 } from './types.js'; +import Ajv2020 from 'ajv/dist/2020'; + export interface OpenAPISchemaValidatorOpts { version: string; validateApiSpec: boolean; @@ -17,7 +21,6 @@ export class OpenAPISchemaValidator { private validator: ValidateFunction; constructor(opts: OpenAPISchemaValidatorOpts) { const options: Options = { - schemaId: 'id', allErrors: true, validateFormats: true, coerceTypes: false, @@ -29,15 +32,28 @@ export class OpenAPISchemaValidator { options.validateSchema = false; } - const v = new AjvDraft4(options); - addFormats(v, ['email', 'regex', 'uri', 'uri-reference']); - - const ver = opts.version && parseInt(String(opts.version), 10); + const ver = opts.version && parseFloat(String(opts.version)); if (!ver) throw Error('version missing from OpenAPI specification'); - if (ver != 3) throw Error('OpenAPI v3 specification version is required'); + if (parseInt(ver.toString()) != 3) throw Error('OpenAPI v3 specification version is required'); + + let ajvInstance; + let schema; + + if (ver === 3) { + schema = openapi3Schema; + ajvInstance = new AjvDraft4(options); + } else if (ver === 3.1) { + schema = openapi31Schema; + ajvInstance = new Ajv2020(options); + ajvInstance.addFormat('media-range', true); // TODO: Validate media-range format as defined in https://www.rfc-editor.org/rfc/rfc9110.html#name-collected-abnf + } else { + throw new Error('OpenAPI v3 specification 3.0 and 3.1 supported'); + } + + addFormats(ajvInstance, ['email', 'regex', 'uri', 'uri-reference']); - v.addSchema(openapi3Schema); - this.validator = v.compile(openapi3Schema); + ajvInstance.addSchema(schema); + this.validator = ajvInstance.compile(schema); } public validate(openapiDoc: OpenAPIV3.Document): { diff --git a/src/framework/openapi.v3_1.modified.schema.json b/src/framework/openapi.v3_1.modified.schema.json new file mode 100644 index 00000000..76e4df1d --- /dev/null +++ b/src/framework/openapi.v3_1.modified.schema.json @@ -0,0 +1,1407 @@ +{ + "$id": "https://spec.openapis.org/oas/3.1/schema/2022-10-07", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The description of OpenAPI v3.1.x documents without schema validation, as defined by https://spec.openapis.org/oas/v3.1.0", + "type": "object", + "properties": { + "openapi": { + "type": "string", + "pattern": "^3\\.1\\.\\d+(-.+)?$" + }, + "info": { + "$ref": "#/$defs/info" + }, + "jsonSchemaDialect": { + "type": "string", + "format": "uri", + "default": "https://spec.openapis.org/oas/3.1/dialect/base" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + }, + "default": [{ + "url": "/" + }] + }, + "paths": { + "$ref": "#/$defs/paths" + }, + "webhooks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + }, + "components": { + "$ref": "#/$defs/components" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/$defs/tag" + } + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": [ + "openapi", + "info" + ], + "anyOf": [{ + "required": [ + "paths" + ] + }, + { + "required": [ + "components" + ] + }, + { + "required": [ + "webhooks" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "info": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#info-object", + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "termsOfService": { + "type": "string", + "format": "uri" + }, + "contact": { + "$ref": "#/$defs/contact" + }, + "license": { + "$ref": "#/$defs/license" + }, + "version": { + "type": "string" + } + }, + "required": [ + "title", + "version" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "contact": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#contact-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "license": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#license-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "name" + ], + "dependentSchemas": { + "identifier": { + "not": { + "required": [ + "url" + ] + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#server-object", + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri-reference" + }, + "description": { + "type": "string" + }, + "variables": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/server-variable" + } + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server-variable": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#server-variable-object", + "type": "object", + "properties": { + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "default": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "default" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "components": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#components-object", + "type": "object", + "properties": { + "schemas": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/schema" + } + }, + "responses": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/response-or-reference" + } + }, + "parameters": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + }, + "requestBodies": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/request-body-or-reference" + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "securitySchemes": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/security-scheme-or-reference" + } + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "pathItems": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + } + }, + "patternProperties": { + "^(schemas|responses|parameters|examples|requestBodies|headers|securitySchemes|links|callbacks|pathItems)$": { + "$comment": "Enumerating all of the property names in the regex above is necessary for unevaluatedProperties to work as expected", + "propertyNames": { + "pattern": "^[a-zA-Z0-9._-]+$" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "paths": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#paths-object", + "type": "object", + "patternProperties": { + "^/": { + "$ref": "#/$defs/path-item" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "path-item": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#path-item-object", + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "get": { + "$ref": "#/$defs/operation" + }, + "put": { + "$ref": "#/$defs/operation" + }, + "post": { + "$ref": "#/$defs/operation" + }, + "delete": { + "$ref": "#/$defs/operation" + }, + "options": { + "$ref": "#/$defs/operation" + }, + "head": { + "$ref": "#/$defs/operation" + }, + "patch": { + "$ref": "#/$defs/operation" + }, + "trace": { + "$ref": "#/$defs/operation" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "path-item-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/path-item" + } + }, + "operation": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#operation-object", + "type": "object", + "properties": { + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "requestBody": { + "$ref": "#/$defs/request-body-or-reference" + }, + "responses": { + "$ref": "#/$defs/responses" + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "external-documentation": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#external-documentation-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "url" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#parameter-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "header", + "path", + "cookie" + ] + }, + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "schema": { + "$ref": "#/$defs/schema" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "required": [ + "name", + "in" + ], + "oneOf": [{ + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], + "if": { + "properties": { + "in": { + "const": "query" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "allowEmptyValue": { + "default": false, + "type": "boolean" + } + } + }, + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "type": "string" + }, + "explode": { + "type": "boolean" + } + }, + "allOf": [{ + "$ref": "#/$defs/examples" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-path" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie" + }, + { + "$ref": "#/$defs/styles-for-form" + } + ], + "$defs": { + "styles-for-path": { + "if": { + "properties": { + "in": { + "const": "path" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "simple", + "enum": [ + "matrix", + "label", + "simple" + ] + }, + "required": { + "const": true + } + }, + "required": [ + "required" + ] + } + }, + "styles-for-header": { + "if": { + "properties": { + "in": { + "const": "header" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + } + } + } + }, + "styles-for-query": { + "if": { + "properties": { + "in": { + "const": "query" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + } + } + }, + "styles-for-cookie": { + "if": { + "properties": { + "in": { + "const": "cookie" + } + }, + "required": [ + "in" + ] + }, + "then": { + "properties": { + "style": { + "default": "form", + "const": "form" + } + } + } + } + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/parameter" + } + }, + "request-body": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#request-body-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "content": { + "$ref": "#/$defs/content" + }, + "required": { + "default": false, + "type": "boolean" + } + }, + "required": [ + "content" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "request-body-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/request-body" + } + }, + "content": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#fixed-fields-10", + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/media-type" + }, + "propertyNames": { + "format": "media-range" + } + }, + "media-type": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#media-type-object", + "type": "object", + "properties": { + "schema": { + "$ref": "#/$defs/schema" + }, + "encoding": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/encoding" + } + } + }, + "allOf": [{ + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/examples" + } + ], + "unevaluatedProperties": false + }, + "encoding": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#encoding-object", + "type": "object", + "properties": { + "contentType": { + "type": "string", + "format": "media-range" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + }, + "allOf": [{ + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/styles-for-form" + } + ], + "unevaluatedProperties": false + }, + "responses": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#responses-object", + "type": "object", + "properties": { + "default": { + "$ref": "#/$defs/response-or-reference" + } + }, + "patternProperties": { + "^[1-5](?:[0-9]{2}|XX)$": { + "$ref": "#/$defs/response-or-reference" + } + }, + "minProperties": 1, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "if": { + "$comment": "either default, or at least one response code property must exist", + "patternProperties": { + "^[1-5](?:[0-9]{2}|XX)$": false + } + }, + "then": { + "required": ["default"] + } + }, + "response": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#response-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "content": { + "$ref": "#/$defs/content" + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + } + }, + "required": [ + "description" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "response-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/response" + } + }, + "callbacks": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#callback-object", + "type": "object", + "$ref": "#/$defs/specification-extensions", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + }, + "callbacks-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/callbacks" + } + }, + "example": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#example-object", + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "value": true, + "externalValue": { + "type": "string", + "format": "uri" + } + }, + "not": { + "required": [ + "value", + "externalValue" + ] + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "example-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/example" + } + }, + "link": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#link-object", + "type": "object", + "properties": { + "operationRef": { + "type": "string", + "format": "uri-reference" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "$ref": "#/$defs/map-of-strings" + }, + "requestBody": true, + "description": { + "type": "string" + }, + "body": { + "$ref": "#/$defs/server" + } + }, + "oneOf": [{ + "required": [ + "operationRef" + ] + }, + { + "required": [ + "operationId" + ] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "link-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/link" + } + }, + "header": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#header-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "schema": { + "$ref": "#/$defs/schema" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "oneOf": [{ + "required": [ + "schema" + ] + }, + { + "required": [ + "content" + ] + } + ], + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + }, + "explode": { + "default": false, + "type": "boolean" + } + }, + "$ref": "#/$defs/examples" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "header-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/header" + } + }, + "tag": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#tag-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": [ + "name" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "reference": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#reference-object", + "type": "object", + "properties": { + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "schema": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#schema-object", + "$dynamicAnchor": "meta", + "type": [ + "object", + "boolean" + ] + }, + "security-scheme": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#security-scheme-object", + "type": "object", + "properties": { + "type": { + "enum": [ + "apiKey", + "http", + "mutualTLS", + "oauth2", + "openIdConnect" + ] + }, + "description": { + "type": "string" + } + }, + "required": [ + "type" + ], + "allOf": [{ + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-apikey" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http-bearer" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oauth2" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oidc" + } + ], + "unevaluatedProperties": false, + "$defs": { + "type-apikey": { + "if": { + "properties": { + "type": { + "const": "apiKey" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": [ + "query", + "header", + "cookie" + ] + } + }, + "required": [ + "name", + "in" + ] + } + }, + "type-http": { + "if": { + "properties": { + "type": { + "const": "http" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "scheme": { + "type": "string" + } + }, + "required": [ + "scheme" + ] + } + }, + "type-http-bearer": { + "if": { + "properties": { + "type": { + "const": "http" + }, + "scheme": { + "type": "string", + "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" + } + }, + "required": [ + "type", + "scheme" + ] + }, + "then": { + "properties": { + "bearerFormat": { + "type": "string" + } + } + } + }, + "type-oauth2": { + "if": { + "properties": { + "type": { + "const": "oauth2" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "flows": { + "$ref": "#/$defs/oauth-flows" + } + }, + "required": [ + "flows" + ] + } + }, + "type-oidc": { + "if": { + "properties": { + "type": { + "const": "openIdConnect" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "openIdConnectUrl": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "openIdConnectUrl" + ] + } + } + } + }, + "security-scheme-or-reference": { + "if": { + "type": "object", + "required": [ + "$ref" + ] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/security-scheme" + } + }, + "oauth-flows": { + "type": "object", + "properties": { + "implicit": { + "$ref": "#/$defs/oauth-flows/$defs/implicit" + }, + "password": { + "$ref": "#/$defs/oauth-flows/$defs/password" + }, + "clientCredentials": { + "$ref": "#/$defs/oauth-flows/$defs/client-credentials" + }, + "authorizationCode": { + "$ref": "#/$defs/oauth-flows/$defs/authorization-code" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "implicit": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "password": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "client-credentials": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "authorization-code": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": [ + "authorizationUrl", + "tokenUrl", + "scopes" + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + } + } + }, + "security-requirement": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#security-requirement-object", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "specification-extensions": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#specification-extensions", + "patternProperties": { + "^x-": true + } + }, + "examples": { + "properties": { + "example": true, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + } + } + }, + "map-of-strings": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "styles-for-form": { + "if": { + "properties": { + "style": { + "const": "form" + } + }, + "required": [ + "style" + ] + }, + "then": { + "properties": { + "explode": { + "default": true + } + } + }, + "else": { + "properties": { + "explode": { + "default": false + } + } + } + } + } +} \ No newline at end of file From ad10f8b6ab0c2d4f83dbce08e0a3958ce6528f17 Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 5 Feb 2024 00:22:24 +0000 Subject: [PATCH 23/60] test(openapi_3.1): ensure that an API with webhooks and no routes is supported --- test/openapi_3.1/README.md | 3 +++ test/openapi_3.1/resources/webhook.yaml | 35 +++++++++++++++++++++++++ test/openapi_3.1/webhook.spec.ts | 31 ++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 test/openapi_3.1/README.md create mode 100644 test/openapi_3.1/resources/webhook.yaml create mode 100644 test/openapi_3.1/webhook.spec.ts diff --git a/test/openapi_3.1/README.md b/test/openapi_3.1/README.md new file mode 100644 index 00000000..defce399 --- /dev/null +++ b/test/openapi_3.1/README.md @@ -0,0 +1,3 @@ +# Open API 3.1 tests + +This folder, and its subfolders, contain tests for OpenAPI specification 3.1 diff --git a/test/openapi_3.1/resources/webhook.yaml b/test/openapi_3.1/resources/webhook.yaml new file mode 100644 index 00000000..27bbd8e2 --- /dev/null +++ b/test/openapi_3.1/resources/webhook.yaml @@ -0,0 +1,35 @@ +# From https://github.com/OAI/OpenAPI-Specification/blob/main/examples/v3.1/webhook-example.yaml +openapi: 3.1.0 +info: + title: Webhook Example + version: 1.0.0 +# Since OAS 3.1.0 the paths element isn't necessary. Now a valid OpenAPI Document can describe only paths, webhooks, or even only reusable components +webhooks: + # Each webhook needs a name + newPet: + # This is a Path Item Object, the only difference is that the request is initiated by the API provider + post: + requestBody: + description: Information about a new pet in the system + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + responses: + "200": + description: Return a 200 status to indicate that the data was received successfully + +components: + schemas: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string \ No newline at end of file diff --git a/test/openapi_3.1/webhook.spec.ts b/test/openapi_3.1/webhook.spec.ts new file mode 100644 index 00000000..e088c12a --- /dev/null +++ b/test/openapi_3.1/webhook.spec.ts @@ -0,0 +1,31 @@ +import { expect } from "chai"; +import * as request from 'supertest'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('webhook support - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'webhook.yaml'); + app = await createApp( + { apiSpec, validateRequests: true }, + 3005, + undefined, + false, + ); + }); + + after(() => { + app.server.close(); + }); + + it('should support an API that only has webhooks defined, but provides no routes', () => { + // The webhook is not made available by the provider API, so the request will return 404 + // This test ensures that the request flow happens normally without any interruptions due to being a webhook + return request(app) + .get(`${app.basePath}/webhook`) + .expect(404); + }); + +}) \ No newline at end of file From c3052433b6e0128aea28380b6221c1a2ade7281a Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 5 Feb 2024 00:25:12 +0000 Subject: [PATCH 24/60] feat(openapi_3.1): adds open api 3.1 type --- src/framework/ajv/index.ts | 6 +- src/framework/index.ts | 8 +-- src/framework/openapi.context.ts | 2 +- src/framework/openapi.schema.validator.ts | 2 +- src/framework/openapi.spec.loader.ts | 60 ++++++++++--------- src/framework/types.ts | 17 ++++-- src/middlewares/openapi.metadata.ts | 2 +- src/middlewares/openapi.multipart.ts | 2 +- src/middlewares/openapi.request.validator.ts | 11 ++-- src/middlewares/openapi.response.validator.ts | 4 +- src/middlewares/openapi.security.ts | 2 +- .../parsers/req.parameter.mutator.ts | 4 +- src/middlewares/parsers/schema.parse.ts | 4 +- .../parsers/schema.preprocessor.ts | 22 ++++--- src/middlewares/parsers/util.ts | 2 +- src/openapi.validator.ts | 12 ++-- src/resolvers.ts | 4 +- test/440.spec.ts | 2 +- test/478.spec.ts | 2 +- test/535.spec.ts | 4 +- test/577.spec.ts | 4 +- test/821.spec.ts | 2 +- test/allow.header.spec.ts | 2 +- test/invalid.apispec.spec.ts | 2 +- test/no.components.spec.ts | 2 +- test/petstore.spec.ts | 2 +- test/user-request-url.router.spec.ts | 4 +- 27 files changed, 103 insertions(+), 87 deletions(-) diff --git a/src/framework/ajv/index.ts b/src/framework/ajv/index.ts index dd439438..238e1ae7 100644 --- a/src/framework/ajv/index.ts +++ b/src/framework/ajv/index.ts @@ -11,21 +11,21 @@ interface SerDesSchema extends Partial { } export function createRequestAjv( - openApiSpec: OpenAPIV3.Document, + openApiSpec: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: Options = {}, ): AjvDraft4 { return createAjv(openApiSpec, options); } export function createResponseAjv( - openApiSpec: OpenAPIV3.Document, + openApiSpec: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: Options = {}, ): AjvDraft4 { return createAjv(openApiSpec, options, false); } function createAjv( - openApiSpec: OpenAPIV3.Document, + openApiSpec: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: Options = {}, request = true, ): AjvDraft4 { diff --git a/src/framework/index.ts b/src/framework/index.ts index 471d6183..8fe2ceb9 100644 --- a/src/framework/index.ts +++ b/src/framework/index.ts @@ -75,7 +75,7 @@ export class OpenAPIFramework { private loadSpec( filePath: string | object, $refParser: { mode: 'bundle' | 'dereference' } = { mode: 'bundle' }, - ): Promise { + ): Promise { // Because of this issue ( https://github.com/APIDevTools/json-schema-ref-parser/issues/101#issuecomment-421755168 ) // We need this workaround ( use '$RefParser.dereference' instead of '$RefParser.bundle' ) if asked by user if (typeof filePath === 'string') { @@ -87,7 +87,7 @@ export class OpenAPIFramework { $refParser.mode === 'dereference' ? $RefParser.dereference(absolutePath) : $RefParser.bundle(absolutePath); - return doc as Promise; + return doc as Promise; } else { throw new Error( `${this.loggingPrefix}spec could not be read at ${filePath}`, @@ -98,10 +98,10 @@ export class OpenAPIFramework { $refParser.mode === 'dereference' ? $RefParser.dereference(filePath) : $RefParser.bundle(filePath); - return doc as Promise; + return doc as Promise; } - private sortApiDocTags(apiDoc: OpenAPIV3.Document): void { + private sortApiDocTags(apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1): void { if (apiDoc && Array.isArray(apiDoc.tags)) { apiDoc.tags.sort((a, b): number => { return a.name < b.name ? -1 : 1; diff --git a/src/framework/openapi.context.ts b/src/framework/openapi.context.ts index 8d6fc23f..eab583d7 100644 --- a/src/framework/openapi.context.ts +++ b/src/framework/openapi.context.ts @@ -6,7 +6,7 @@ export interface RoutePair { openApiRoute: string; } export class OpenApiContext { - public readonly apiDoc: OpenAPIV3.Document; + public readonly apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; public readonly expressRouteMap = {}; public readonly openApiRouteMap = {}; public readonly routes: RouteMetadata[] = []; diff --git a/src/framework/openapi.schema.validator.ts b/src/framework/openapi.schema.validator.ts index b378bd62..81a20f7b 100644 --- a/src/framework/openapi.schema.validator.ts +++ b/src/framework/openapi.schema.validator.ts @@ -56,7 +56,7 @@ export class OpenAPISchemaValidator { this.validator = ajvInstance.compile(schema); } - public validate(openapiDoc: OpenAPIV3.Document): { + public validate(openapiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1): { errors: Array | null; } { const valid = this.validator(openapiDoc); diff --git a/src/framework/openapi.spec.loader.ts b/src/framework/openapi.spec.loader.ts index 3ccb1486..e50d4346 100644 --- a/src/framework/openapi.spec.loader.ts +++ b/src/framework/openapi.spec.loader.ts @@ -6,7 +6,7 @@ import { } from './types'; export interface Spec { - apiDoc: OpenAPIV3.Document; + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; basePaths: string[]; routes: RouteMetadata[]; serial: number; @@ -21,7 +21,7 @@ export interface RouteMetadata { } interface DiscoveredRoutes { - apiDoc: OpenAPIV3.Document; + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; basePaths: string[]; routes: RouteMetadata[]; serial: number; @@ -52,7 +52,7 @@ export class OpenApiSpecLoader { const routes: RouteMetadata[] = []; const toExpressParams = this.toExpressParams; // const basePaths = this.framework.basePaths; - // let apiDoc: OpenAPIV3.Document = null; + // let apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1 = null; // let basePaths: string[] = null; const { apiDoc, basePaths } = await this.framework.initialize({ visitApi(ctx: OpenAPIFrameworkAPIContext): void { @@ -60,34 +60,36 @@ export class OpenApiSpecLoader { const basePaths = ctx.basePaths; for (const bpa of basePaths) { const bp = bpa.replace(/\/$/, ''); - for (const [path, methods] of Object.entries(apiDoc.paths)) { - for (const [method, schema] of Object.entries(methods)) { - if ( - method.startsWith('x-') || - ['parameters', 'summary', 'description'].includes(method) - ) { - continue; - } - const pathParams = new Set(); - const parameters = [...schema.parameters ?? [], ...methods.parameters ?? []] - for (const param of parameters) { - if (param.in === 'path') { - pathParams.add(param.name); + if (apiDoc.paths) { + for (const [path, methods] of Object.entries(apiDoc.paths)) { + for (const [method, schema] of Object.entries(methods)) { + if ( + method.startsWith('x-') || + ['parameters', 'summary', 'description'].includes(method) + ) { + continue; + } + const pathParams = new Set(); + const parameters = [...schema.parameters ?? [], ...methods.parameters ?? []] + for (const param of parameters) { + if (param.in === 'path') { + pathParams.add(param.name); + } } + const openApiRoute = `${bp}${path}`; + const expressRoute = `${openApiRoute}` + .split(':') + .map(toExpressParams) + .join('\\:'); + + routes.push({ + basePath: bp, + expressRoute, + openApiRoute, + method: method.toUpperCase(), + pathParams: Array.from(pathParams), + }); } - const openApiRoute = `${bp}${path}`; - const expressRoute = `${openApiRoute}` - .split(':') - .map(toExpressParams) - .join('\\:'); - - routes.push({ - basePath: bp, - expressRoute, - openApiRoute, - method: method.toUpperCase(), - pathParams: Array.from(pathParams), - }); } } } diff --git a/src/framework/types.ts b/src/framework/types.ts index 2df2ea07..d2638c50 100644 --- a/src/framework/types.ts +++ b/src/framework/types.ts @@ -22,7 +22,7 @@ export interface ValidationSchema extends ParametersSchema { } export interface OpenAPIFrameworkInit { - apiDoc: OpenAPIV3.Document; + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; basePaths: string[]; } export type SecurityHandlers = { @@ -146,7 +146,7 @@ type DeepImmutableObject = { } export interface OpenApiValidatorOpts { - apiSpec: DeepImmutable | string; + apiSpec: DeepImmutable | DeepImmutable | string; validateApiSpec?: boolean; validateResponses?: boolean | ValidateResponseOpts; validateRequests?: boolean | ValidateRequestOpts; @@ -189,7 +189,7 @@ export interface NormalizedOpenApiValidatorOpts extends OpenApiValidatorOpts { } export namespace OpenAPIV3 { - export interface Document { + export interface DocumentV3 { openapi: string; info: InfoObject; servers?: ServerObject[]; @@ -200,6 +200,13 @@ export namespace OpenAPIV3 { externalDocs?: ExternalDocumentationObject; } + export interface DocumentV3_1 extends Omit { + paths?: DocumentV3['paths'] + webhooks: { + [name: string]: PathItemObject | ReferenceObject + } + } + export interface InfoObject { title: string; description?: string; @@ -512,7 +519,7 @@ export interface OpenAPIFrameworkPathObject { } interface OpenAPIFrameworkArgs { - apiDoc: OpenAPIV3.Document | string; + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1 | string; validateApiSpec?: boolean; $refParser?: { mode: 'bundle' | 'dereference'; @@ -522,7 +529,7 @@ interface OpenAPIFrameworkArgs { export interface OpenAPIFrameworkAPIContext { // basePaths: BasePath[]; basePaths: string[]; - getApiDoc(): OpenAPIV3.Document; + getApiDoc(): OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; } export interface OpenAPIFrameworkVisitor { diff --git a/src/middlewares/openapi.metadata.ts b/src/middlewares/openapi.metadata.ts index 6d217be3..317273a3 100644 --- a/src/middlewares/openapi.metadata.ts +++ b/src/middlewares/openapi.metadata.ts @@ -15,7 +15,7 @@ import { httpMethods } from './parsers/schema.preprocessor'; export function applyOpenApiMetadata( openApiContext: OpenApiContext, - responseApiDoc: OpenAPIV3.Document, + responseApiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, ): OpenApiRequestHandler { return (req: OpenApiRequest, res: Response, next: NextFunction): void => { // note base path is empty when path is fully qualified i.e. req.path.startsWith('') diff --git a/src/middlewares/openapi.multipart.ts b/src/middlewares/openapi.multipart.ts index e868b300..97a5fc47 100644 --- a/src/middlewares/openapi.multipart.ts +++ b/src/middlewares/openapi.multipart.ts @@ -15,7 +15,7 @@ import { MulterError } from 'multer'; const multer = require('multer'); export function multipart( - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: MultipartOpts, ): OpenApiRequestHandler { const mult = multer(options.multerOpts); diff --git a/src/middlewares/openapi.request.validator.ts b/src/middlewares/openapi.request.validator.ts index 96f8d9b0..1e01fa19 100644 --- a/src/middlewares/openapi.request.validator.ts +++ b/src/middlewares/openapi.request.validator.ts @@ -21,6 +21,7 @@ import { import { BodySchemaParser } from './parsers/body.parse'; import { ParametersSchemaParser } from './parsers/schema.parse'; import { RequestParameterMutator } from './parsers/req.parameter.mutator'; +import { debug } from 'console'; type OperationObject = OpenAPIV3.OperationObject; type SchemaObject = OpenAPIV3.SchemaObject; @@ -31,13 +32,13 @@ type ApiKeySecurityScheme = OpenAPIV3.ApiKeySecurityScheme; export class RequestValidator { private middlewareCache: { [key: string]: RequestHandler } = {}; - private apiDoc: OpenAPIV3.Document; + private apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; private ajv: Ajv; private ajvBody: Ajv; private requestOpts: ValidateRequestOpts = {}; constructor( - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: RequestValidatorOptions = {}, ) { this.middlewareCache = {}; @@ -278,7 +279,7 @@ export class RequestValidator { } class Validator { - private readonly apiDoc: OpenAPIV3.Document; + private readonly apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; readonly schemaGeneral: object; readonly schemaBody: object; readonly validatorGeneral: ValidateFunction; @@ -286,7 +287,7 @@ class Validator { readonly allSchemaProperties: ValidationSchema; constructor( - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, parametersSchema: ParametersSchema, bodySchema: BodySchema, ajv: { @@ -340,7 +341,7 @@ class Validator { class Security { public static queryParam( - apiDocs: OpenAPIV3.Document, + apiDocs: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, schema: OperationObject, ): string[] { const hasPathSecurity = schema.security?.length > 0 ?? false; diff --git a/src/middlewares/openapi.response.validator.ts b/src/middlewares/openapi.response.validator.ts index a187ad0a..75b467ea 100644 --- a/src/middlewares/openapi.response.validator.ts +++ b/src/middlewares/openapi.response.validator.ts @@ -27,7 +27,7 @@ interface ValidateResult { } export class ResponseValidator { private ajvBody: Ajv; - private spec: OpenAPIV3.Document; + private spec: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; private validatorsCache: { [key: string]: { [key: string]: ValidateFunction }; } = {}; @@ -35,7 +35,7 @@ export class ResponseValidator { private serial: number; constructor( - openApiSpec: OpenAPIV3.Document, + openApiSpec: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: Options = {}, eovOptions: ValidateResponseOpts = {}, serial: number = -1, diff --git a/src/middlewares/openapi.security.ts b/src/middlewares/openapi.security.ts index 8f43af14..d4c62db4 100644 --- a/src/middlewares/openapi.security.ts +++ b/src/middlewares/openapi.security.ts @@ -42,7 +42,7 @@ function didOneSchemaPassValidation(results: (SecurityHandlerResult | SecurityHa } export function security( - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, securityHandlers: SecurityHandlers, ): OpenApiRequestHandler { return async (req, res, next) => { diff --git a/src/middlewares/parsers/req.parameter.mutator.ts b/src/middlewares/parsers/req.parameter.mutator.ts index 5b92654c..43da1f96 100644 --- a/src/middlewares/parsers/req.parameter.mutator.ts +++ b/src/middlewares/parsers/req.parameter.mutator.ts @@ -39,14 +39,14 @@ type Schema = ReferenceObject | SchemaObject; * the request is mutated to accomodate various styles and types e.g. form, explode, deepObject, etc */ export class RequestParameterMutator { - private _apiDocs: OpenAPIV3.Document; + private _apiDocs: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; private path: string; private ajv: Ajv; private parsedSchema: ValidationSchema; constructor( ajv: Ajv, - apiDocs: OpenAPIV3.Document, + apiDocs: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, path: string, parsedSchema: ValidationSchema, ) { diff --git a/src/middlewares/parsers/schema.parse.ts b/src/middlewares/parsers/schema.parse.ts index cbd58e47..fb5d380c 100644 --- a/src/middlewares/parsers/schema.parse.ts +++ b/src/middlewares/parsers/schema.parse.ts @@ -17,9 +17,9 @@ type Parameter = OpenAPIV3.ReferenceObject | OpenAPIV3.ParameterObject; */ export class ParametersSchemaParser { private _ajv: Ajv; - private _apiDocs: OpenAPIV3.Document; + private _apiDocs: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; - constructor(ajv: Ajv, apiDocs: OpenAPIV3.Document) { + constructor(ajv: Ajv, apiDocs: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1) { this._ajv = ajv; this._apiDocs = apiDocs; } diff --git a/src/middlewares/parsers/schema.preprocessor.ts b/src/middlewares/parsers/schema.preprocessor.ts index 8e14f4c0..c5315429 100644 --- a/src/middlewares/parsers/schema.preprocessor.ts +++ b/src/middlewares/parsers/schema.preprocessor.ts @@ -91,12 +91,12 @@ export const httpMethods = new Set([ ]); export class SchemaPreprocessor { private ajv: Ajv; - private apiDoc: OpenAPIV3.Document; - private apiDocRes: OpenAPIV3.Document; + private apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; + private apiDocRes: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1; private serDesMap: SerDesMap; private responseOpts: ValidateResponseOpts; constructor( - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, ajvOptions: Options, validateResponsesOpts: ValidateResponseOpts, ) { @@ -108,22 +108,28 @@ export class SchemaPreprocessor { public preProcess() { const componentSchemas = this.gatherComponentSchemaNodes(); - const r = this.gatherSchemaNodesFromPaths(); + let r; + + if (this.apiDoc.paths) { + r = this.gatherSchemaNodesFromPaths(); + } // Now that we've processed paths, clone a response spec if we are validating responses this.apiDocRes = !!this.responseOpts ? cloneDeep(this.apiDoc) : null; const schemaNodes = { schemas: componentSchemas, - requestBodies: r.requestBodies, - responses: r.responses, - requestParameters: r.requestParameters, + requestBodies: r?.requestBodies, + responses: r?.responses, + requestParameters: r?.requestParameters, }; // Traverse the schemas - this.traverseSchemas(schemaNodes, (parent, schema, opts) => + if (r) { + this.traverseSchemas(schemaNodes, (parent, schema, opts) => this.schemaVisitor(parent, schema, opts), ); + } return { apiDoc: this.apiDoc, diff --git a/src/middlewares/parsers/util.ts b/src/middlewares/parsers/util.ts index aa0642e3..0c2119e3 100644 --- a/src/middlewares/parsers/util.ts +++ b/src/middlewares/parsers/util.ts @@ -4,7 +4,7 @@ import ajv = require('ajv'); import { OpenAPIFramework } from '../../framework'; export function dereferenceParameter( - apiDocs: OpenAPIV3.Document, + apiDocs: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, parameter: OpenAPIV3.ReferenceObject | OpenAPIV3.ParameterObject, ): OpenAPIV3.ParameterObject { // TODO this should recurse or use ajv.getSchema - if implemented as such, may want to cache the result diff --git a/src/openapi.validator.ts b/src/openapi.validator.ts index 3265c187..047c9a97 100644 --- a/src/openapi.validator.ts +++ b/src/openapi.validator.ts @@ -37,7 +37,7 @@ export { interface MiddlewareContext { context: OpenApiContext, - responseApiDoc: OpenAPIV3.Document, + responseApiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, error: any, } @@ -264,26 +264,26 @@ export class OpenApiValidator { private metadataMiddleware( context: OpenApiContext, - responseApiDoc: OpenAPIV3.Document, + responseApiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, ) { return middlewares.applyOpenApiMetadata(context, responseApiDoc); } - private multipartMiddleware(apiDoc: OpenAPIV3.Document) { + private multipartMiddleware(apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1) { return middlewares.multipart(apiDoc, { multerOpts: this.options.fileUploader, ajvOpts: this.ajvOpts.multipart, }); } - private securityMiddleware(apiDoc: OpenAPIV3.Document) { + private securityMiddleware(apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1) { const securityHandlers = (( this.options.validateSecurity ))?.handlers; return middlewares.security(apiDoc, securityHandlers); } - private requestValidationMiddleware(apiDoc: OpenAPIV3.Document) { + private requestValidationMiddleware(apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1) { const requestValidator = new middlewares.RequestValidator( apiDoc, this.ajvOpts.request, @@ -291,7 +291,7 @@ export class OpenApiValidator { return (req, res, next) => requestValidator.validate(req, res, next); } - private responseValidationMiddleware(apiDoc: OpenAPIV3.Document, serial: number) { + private responseValidationMiddleware(apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, serial: number) { return new middlewares.ResponseValidator( apiDoc, this.ajvOpts.response, diff --git a/src/resolvers.ts b/src/resolvers.ts index 3092e3a3..18aba622 100644 --- a/src/resolvers.ts +++ b/src/resolvers.ts @@ -7,7 +7,7 @@ const cache = {}; export function defaultResolver( handlersPath: string, route: RouteMetadata, - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, ): RequestHandler { const tmpModules = {}; const { basePath, expressRoute, openApiRoute, method } = route; @@ -51,7 +51,7 @@ export function defaultResolver( export function modulePathResolver( handlersPath: string, route: RouteMetadata, - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, ): RequestHandler { const pathKey = route.openApiRoute.substring(route.basePath.length); const schema = apiDoc.paths[pathKey][route.method.toLowerCase()]; diff --git a/test/440.spec.ts b/test/440.spec.ts index e105f07e..50960245 100644 --- a/test/440.spec.ts +++ b/test/440.spec.ts @@ -9,7 +9,7 @@ describe(packageJson.name, () => { before(async () => { // Set up the express app - const apiSpec: OpenAPIV3.Document = { + const apiSpec: OpenAPIV3.DocumentV3 = { openapi: '3.0.0', info: { title: 'Api test', version: '1.0.0' }, servers: [{ url: '/api' }], diff --git a/test/478.spec.ts b/test/478.spec.ts index 5825bc22..232ae659 100644 --- a/test/478.spec.ts +++ b/test/478.spec.ts @@ -53,7 +53,7 @@ describe('issue #478', () => { .expect(200)); }); -function apiSpec(): OpenAPIV3.Document { +function apiSpec(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.3', info: { diff --git a/test/535.spec.ts b/test/535.spec.ts index c07beee8..a7dd79bb 100644 --- a/test/535.spec.ts +++ b/test/535.spec.ts @@ -19,7 +19,7 @@ describe('#535 - calling `middleware()` multiple times', () => { }); async function createApp( - apiSpec: OpenAPIV3.Document, + apiSpec: OpenAPIV3.DocumentV3, ): Promise { const app = express(); @@ -39,7 +39,7 @@ async function createApp( return app; } -function createApiSpec(): OpenAPIV3.Document { +function createApiSpec(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.3', info: { diff --git a/test/577.spec.ts b/test/577.spec.ts index 4c9b26fa..a142d45a 100644 --- a/test/577.spec.ts +++ b/test/577.spec.ts @@ -21,7 +21,7 @@ describe('#577 - Exclude response validation that is not in api spec', () => { }); async function createApp( - apiSpec: OpenAPIV3.Document, + apiSpec: OpenAPIV3.DocumentV3, ): Promise { const app = express(); @@ -51,7 +51,7 @@ async function createApp( return app; } -function createApiSpec(): OpenAPIV3.Document { +function createApiSpec(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.3', info: { diff --git a/test/821.spec.ts b/test/821.spec.ts index 42eda867..5b9bc75d 100644 --- a/test/821.spec.ts +++ b/test/821.spec.ts @@ -30,7 +30,7 @@ describe('issue #821 - serialization inside addiotionalProperties', () => { }); async function createApp( - apiSpec: OpenAPIV3.Document | string, + apiSpec: OpenAPIV3.DocumentV3 | string, ): Promise { const app = express(); diff --git a/test/allow.header.spec.ts b/test/allow.header.spec.ts index 25620c81..04415919 100644 --- a/test/allow.header.spec.ts +++ b/test/allow.header.spec.ts @@ -50,7 +50,7 @@ async function createApp(): Promise { return app; } -function createApiSpec(): OpenAPIV3.Document { +function createApiSpec(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.3', info: { diff --git a/test/invalid.apispec.spec.ts b/test/invalid.apispec.spec.ts index 2b7b02e8..0ded5b10 100644 --- a/test/invalid.apispec.spec.ts +++ b/test/invalid.apispec.spec.ts @@ -41,7 +41,7 @@ async function createApp( return app; } -function createApiSpec(): OpenAPIV3.Document { +function createApiSpec(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.3', info: { diff --git a/test/no.components.spec.ts b/test/no.components.spec.ts index 0373bd3f..866f371b 100644 --- a/test/no.components.spec.ts +++ b/test/no.components.spec.ts @@ -33,7 +33,7 @@ describe('no components', () => { })); }); -function apiDoc(): OpenAPIV3.Document { +function apiDoc(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.1', info: { diff --git a/test/petstore.spec.ts b/test/petstore.spec.ts index 1edf43e7..8588f5b5 100644 --- a/test/petstore.spec.ts +++ b/test/petstore.spec.ts @@ -36,7 +36,7 @@ describe('petstore', () => { request(app).get(`${app.basePath}/pets`).expect(200)); }); -function petstoreSpec(): OpenAPIV3.Document { +function petstoreSpec(): OpenAPIV3.DocumentV3 { return { openapi: '3.0.0', info: { diff --git a/test/user-request-url.router.spec.ts b/test/user-request-url.router.spec.ts index 4bba5dee..d4470ba4 100644 --- a/test/user-request-url.router.spec.ts +++ b/test/user-request-url.router.spec.ts @@ -112,7 +112,7 @@ function defaultResponse(): OpenAPIV3.ResponseObject { type of id in path and id in the response here defined as simple string with minLength */ -const gatewaySpec: OpenAPIV3.Document = { +const gatewaySpec: OpenAPIV3.DocumentV3 = { openapi: '3.0.0', info: { version: '1.0.0', title: 'test bug OpenApiValidator' }, servers: [{ url: 'http://localhost:3000/api' }], @@ -168,7 +168,7 @@ const gatewaySpec: OpenAPIV3.Document = { represents spec of the child router. We route request from main app (gateway) to this router. This router has its own schema, routes and validation formats. In particular, we force id in the path and id in the response to be uuid. */ -const childRouterSpec: OpenAPIV3.Document = { +const childRouterSpec: OpenAPIV3.DocumentV3 = { openapi: '3.0.0', info: { version: '1.0.0', title: 'test bug OpenApiValidator' }, servers: [{ url: 'http://localhost:3000/' }], From e49db6ad91f7997846e9c3f94194713eb1b569da Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 5 Feb 2024 00:42:49 +0000 Subject: [PATCH 25/60] chore(test-scripts): run mocha with --extension instead of glob to pick up subdirectories Mocha was not picking up the tests in subdirectories with the provided glob. Adding --extension with the tests extension and setting the root test folder in tests fixed it --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1bd291ed..8622afd8 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "scripts": { "compile": "rimraf dist && tsc", "compile:release": "rimraf dist && tsc --sourceMap false", - "test": "mocha -r source-map-support/register -r ts-node/register --files --recursive -R spec test/**/*.spec.ts", - "test:debug": "mocha -r source-map-support/register -r ts-node/register --inspect-brk --files --recursive test/**/*.spec.ts", - "test:coverage": "nyc mocha -r source-map-support/register -r ts-node/register --recursive test/**/*.spec.ts", + "test": "mocha -r source-map-support/register -r ts-node/register --files --recursive -R spec --extension .spec.ts test", + "test:debug": "mocha -r source-map-support/register -r ts-node/register --inspect-brk --files --recursive --extension .spec.ts test", + "test:coverage": "nyc mocha -r source-map-support/register -r ts-node/register --recursive --extension .spec.ts test", "test:reset": "rimraf node_modules && npm i && npm run compile && npm t", "coveralls": "cat coverage/lcov.info | coveralls -v", "codacy": "bash <(curl -Ls https://coverage.codacy.com/get.sh) report -r coverage/lcov.info", From a66845b8dc25d5a56dd316f616ac69ea1971a415 Mon Sep 17 00:00:00 2001 From: sf97 Date: Thu, 29 Feb 2024 23:28:45 +0000 Subject: [PATCH 26/60] test(openapi-3.1): adds test to ensure an API with only components is considered valid --- test/openapi_3.1/components.spec.ts | 30 ++++++++++++++++++++++ test/openapi_3.1/resources/components.yaml | 5 ++++ 2 files changed, 35 insertions(+) create mode 100644 test/openapi_3.1/components.spec.ts create mode 100644 test/openapi_3.1/resources/components.yaml diff --git a/test/openapi_3.1/components.spec.ts b/test/openapi_3.1/components.spec.ts new file mode 100644 index 00000000..958c81bb --- /dev/null +++ b/test/openapi_3.1/components.spec.ts @@ -0,0 +1,30 @@ +import * as request from 'supertest'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('components support - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'components.yaml'); + app = await createApp( + { apiSpec, validateRequests: true }, + 3005, + undefined, + false, + ); + }); + + after(() => { + app.server.close(); + }); + + it('should support an API that only has components defined, but provides no routes', () => { + // The component is not made available by the provider API, so the request will return 404 + // This test ensures that the request flow happens normally without any interruptions due to being a component + return request(app) + .get(`${app.basePath}/components`) + .expect(404); + }); + +}) \ No newline at end of file diff --git a/test/openapi_3.1/resources/components.yaml b/test/openapi_3.1/resources/components.yaml new file mode 100644 index 00000000..d9d51035 --- /dev/null +++ b/test/openapi_3.1/resources/components.yaml @@ -0,0 +1,5 @@ +openapi: 3.1.0 +info: + title: API + version: 1.0.0 +components: {} \ No newline at end of file From f346d9d8a682d600104d780f2362a44f599fd9da Mon Sep 17 00:00:00 2001 From: sf97 Date: Thu, 29 Feb 2024 23:29:27 +0000 Subject: [PATCH 27/60] test(openapi-3.1): remove unnecessary import --- test/openapi_3.1/webhook.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/openapi_3.1/webhook.spec.ts b/test/openapi_3.1/webhook.spec.ts index e088c12a..58e81b1d 100644 --- a/test/openapi_3.1/webhook.spec.ts +++ b/test/openapi_3.1/webhook.spec.ts @@ -1,4 +1,3 @@ -import { expect } from "chai"; import * as request from 'supertest'; import { createApp } from "../common/app"; import { join } from "path"; From a2732998f6cabc17c8e505b595bda08c07f4462c Mon Sep 17 00:00:00 2001 From: sf97 Date: Thu, 29 Feb 2024 23:42:36 +0000 Subject: [PATCH 28/60] test(openapi-3.1): add support for summary in info object --- src/framework/types.ts | 9 ++++-- test/openapi_3.1/info_summary.spec.ts | 30 ++++++++++++++++++++ test/openapi_3.1/resources/info_summary.yaml | 7 +++++ 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 test/openapi_3.1/info_summary.spec.ts create mode 100644 test/openapi_3.1/resources/info_summary.yaml diff --git a/src/framework/types.ts b/src/framework/types.ts index d2638c50..863b7bf8 100644 --- a/src/framework/types.ts +++ b/src/framework/types.ts @@ -200,8 +200,9 @@ export namespace OpenAPIV3 { externalDocs?: ExternalDocumentationObject; } - export interface DocumentV3_1 extends Omit { - paths?: DocumentV3['paths'] + export interface DocumentV3_1 extends Omit { + paths?: DocumentV3['paths']; + info: InfoObjectV3_1; webhooks: { [name: string]: PathItemObject | ReferenceObject } @@ -216,6 +217,10 @@ export namespace OpenAPIV3 { version: string; } + interface InfoObjectV3_1 extends InfoObject { + summary: string; + } + export interface ContactObject { name?: string; url?: string; diff --git a/test/openapi_3.1/info_summary.spec.ts b/test/openapi_3.1/info_summary.spec.ts new file mode 100644 index 00000000..20e8c5dc --- /dev/null +++ b/test/openapi_3.1/info_summary.spec.ts @@ -0,0 +1,30 @@ +import * as request from 'supertest'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('summary support - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'info_summary.yaml'); + app = await createApp( + { apiSpec, validateRequests: true }, + 3005, + undefined, + false, + ); + }); + + after(() => { + app.server.close(); + }); + + it('should support an API that has an info with a summary defined', () => { + // The endpoint is not made available by the provider API, so the request will return 404 + // This test ensures that the request flow happens normally without any interruptions + return request(app) + .get(`${app.basePath}/webhook`) + .expect(404); + }); + +}) \ No newline at end of file diff --git a/test/openapi_3.1/resources/info_summary.yaml b/test/openapi_3.1/resources/info_summary.yaml new file mode 100644 index 00000000..27e120cd --- /dev/null +++ b/test/openapi_3.1/resources/info_summary.yaml @@ -0,0 +1,7 @@ +# From https://github.com/OAI/OpenAPI-Specification/blob/6f386968654fd483720aba0177e618e87a5d612d/tests/v3.1/pass/info_summary.yaml +openapi: 3.1.0 +info: + title: API + summary: My lovely API + version: 1.0.0 +components: {} \ No newline at end of file From 97a84f663ccf39c611b5c6503eae87ed42987c9a Mon Sep 17 00:00:00 2001 From: sf97 Date: Thu, 29 Feb 2024 23:53:55 +0000 Subject: [PATCH 29/60] test(openapi-3.1): add support for identifier in license --- test/openapi_3.1/license_identifier.spec.ts | 30 +++++++++++++++++++ test/openapi_3.1/resources/components.yaml | 1 + .../resources/license_identifier.yaml | 10 +++++++ 3 files changed, 41 insertions(+) create mode 100644 test/openapi_3.1/license_identifier.spec.ts create mode 100644 test/openapi_3.1/resources/license_identifier.yaml diff --git a/test/openapi_3.1/license_identifier.spec.ts b/test/openapi_3.1/license_identifier.spec.ts new file mode 100644 index 00000000..7f615a93 --- /dev/null +++ b/test/openapi_3.1/license_identifier.spec.ts @@ -0,0 +1,30 @@ +import * as request from 'supertest'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('identifier support - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'license_identifier.yaml'); + app = await createApp( + { apiSpec, validateRequests: true }, + 3005, + undefined, + false, + ); + }); + + after(() => { + app.server.close(); + }); + + it('should support an API that has an info with a summary defined', () => { + // The endpoint is not made available by the provider API, so the request will return 404 + // This test ensures that the request flow happens normally without any interruptions + return request(app) + .get(`${app.basePath}/webhook`) + .expect(404); + }); + +}) \ No newline at end of file diff --git a/test/openapi_3.1/resources/components.yaml b/test/openapi_3.1/resources/components.yaml index d9d51035..b968adec 100644 --- a/test/openapi_3.1/resources/components.yaml +++ b/test/openapi_3.1/resources/components.yaml @@ -1,3 +1,4 @@ +# From https://github.com/OAI/OpenAPI-Specification/blob/6f386968654fd483720aba0177e618e87a5d612d/tests/v3.1/pass/minimal_comp.yaml openapi: 3.1.0 info: title: API diff --git a/test/openapi_3.1/resources/license_identifier.yaml b/test/openapi_3.1/resources/license_identifier.yaml new file mode 100644 index 00000000..85dd47a1 --- /dev/null +++ b/test/openapi_3.1/resources/license_identifier.yaml @@ -0,0 +1,10 @@ +# From https://github.com/OAI/OpenAPI-Specification/blob/6f386968654fd483720aba0177e618e87a5d612d/tests/v3.1/pass/license_identifier.yaml +openapi: 3.1.0 +info: + title: API + summary: My lovely API + version: 1.0.0 + license: + name: Apache + identifier: Apache-2.0 +components: {} \ No newline at end of file From 7262edd9a676fa8a02dfcf1872fd6c64b8e76ae1 Mon Sep 17 00:00:00 2001 From: sf97 Date: Sun, 31 Mar 2024 12:45:50 +0100 Subject: [PATCH 30/60] test(openapi_3.1): ensure API with type set to null works correctly --- test/openapi_3.1/resources/type_null.yaml | 23 ++++++++++++++ test/openapi_3.1/type_null.spec.ts | 37 +++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 test/openapi_3.1/resources/type_null.yaml create mode 100644 test/openapi_3.1/type_null.spec.ts diff --git a/test/openapi_3.1/resources/type_null.yaml b/test/openapi_3.1/resources/type_null.yaml new file mode 100644 index 00000000..54d885f6 --- /dev/null +++ b/test/openapi_3.1/resources/type_null.yaml @@ -0,0 +1,23 @@ +openapi: 3.1.0 +info: + title: API + version: 1.0.0 +servers: + - url: /v1 +paths: + /entity: + get: + summary: test + description: GETS my entity + responses: + '200': + description: OK + content: + application/json: + schema: + title: Entity + type: object + properties: + property: + type: ['string', 'null'] + \ No newline at end of file diff --git a/test/openapi_3.1/type_null.spec.ts b/test/openapi_3.1/type_null.spec.ts new file mode 100644 index 00000000..452626c2 --- /dev/null +++ b/test/openapi_3.1/type_null.spec.ts @@ -0,0 +1,37 @@ +import * as request from 'supertest'; +import * as express from 'express'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('type null support - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'type_null.yaml'); + app = await createApp( + { apiSpec, validateRequests: true, validateResponses: true }, + 3005, + (app) => app.use( + express + .Router() + .get(`/v1/entity`, (req, res) => + res.status(200).json({ + property: null + }), + ), + ) + ); + }); + + after(() => { + app.server.close(); + }); + + // In OpenAPI 3.1, nullable = true was replaced by types = [..., null]. This test ensure that it works with Express OpenAPI Validator + it('should support an API with types set to null', async () => { + return request(app) + .get(`${app.basePath}/entity`) + .expect(200); + }); + +}) \ No newline at end of file From b757ff3e10e1ed9b4efb4ab52ec60d8e59d8859c Mon Sep 17 00:00:00 2001 From: sf97 Date: Sun, 31 Mar 2024 13:19:34 +0100 Subject: [PATCH 31/60] test(open_api3.1): ensure that methods with non-explicit semantics allow request body --- ...non_defined_semantics_request_body.spec.ts | 48 ++++++++++++++++ .../non_defined_semantics_request_body.yaml | 55 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 test/openapi_3.1/non_defined_semantics_request_body.spec.ts create mode 100644 test/openapi_3.1/resources/non_defined_semantics_request_body.yaml diff --git a/test/openapi_3.1/non_defined_semantics_request_body.spec.ts b/test/openapi_3.1/non_defined_semantics_request_body.spec.ts new file mode 100644 index 00000000..e843a587 --- /dev/null +++ b/test/openapi_3.1/non_defined_semantics_request_body.spec.ts @@ -0,0 +1,48 @@ +import * as request from 'supertest'; +import * as express from 'express'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('Request body in operations without well defined semantics - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'non_defined_semantics_request_body.yaml'); + app = await createApp( + { apiSpec, validateRequests: true, validateResponses: true }, + 3005, + (app) => app.use( + express + .Router() + .get(`/v1/entity`, (req, res) => + res.status(200).json({ + property: null + }), + ), + ) + ); + }); + + after(() => { + app.server.close(); + }); + + // In OpenAPI 3.0, methods that RFC7231 does not have explicitly defined semantics for request body (GET, HEAD, DELETE) do not allow request body + // In OpenAPI 3.1, request body is allowed for these methods. This test ensures that GET it is correctly handled + it('should validate a request body on GET', async () => { + return request(app) + .get(`${app.basePath}/entity`) + .set('Content-Type', 'application/json') + .send({request: 123}) + .expect(400); + }); + + // Ensures that DELETE it is correctly handled + it('should validate a request body on DELETE', async () => { + return request(app) + .delete(`${app.basePath}/entity`) + .set('Content-Type', 'application/json') + .send({request: 123}) + .expect(400); + }); +}) \ No newline at end of file diff --git a/test/openapi_3.1/resources/non_defined_semantics_request_body.yaml b/test/openapi_3.1/resources/non_defined_semantics_request_body.yaml new file mode 100644 index 00000000..5114a27a --- /dev/null +++ b/test/openapi_3.1/resources/non_defined_semantics_request_body.yaml @@ -0,0 +1,55 @@ +openapi: 3.1.0 +info: + title: API + version: 1.0.0 +servers: + - url: /v1 +components: + schemas: + EntityRequest: + type: object + properties: + request: + type: string +paths: + /entity: + get: + description: GETS my entity + requestBody: + description: Request body for entity + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EntityRequest' + responses: + '200': + description: OK + content: + application/json: + schema: + title: Entity + type: object + properties: + property: + type: ['string', 'null'] + delete: + description: DELETE my entity + requestBody: + description: Request body for entity + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EntityRequest' + responses: + '200': + description: OK + content: + application/json: + schema: + title: Entity + type: object + properties: + property: + type: ['string', 'null'] \ No newline at end of file From 8ad7a4ffa40573bb89a34a82b346fc267908c9bc Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 1 Apr 2024 00:14:36 +0100 Subject: [PATCH 32/60] test(open_api3.1): ensure 500 is returned when server variable has no default --- .../resources/server_variable_no_default.yaml | 13 ++++++++++++ test/openapi_3.1/server_variable.spec.ts | 21 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 test/openapi_3.1/resources/server_variable_no_default.yaml create mode 100644 test/openapi_3.1/server_variable.spec.ts diff --git a/test/openapi_3.1/resources/server_variable_no_default.yaml b/test/openapi_3.1/resources/server_variable_no_default.yaml new file mode 100644 index 00000000..8408bf44 --- /dev/null +++ b/test/openapi_3.1/resources/server_variable_no_default.yaml @@ -0,0 +1,13 @@ +# Adapted from https://github.com/OAI/OpenAPI-Specification/blob/77c7b9a522ab6fb83a49e8088fa600e93da4f44e/tests/v3.1/fail/server_enum_empty.yaml + +openapi: 3.1.0 +info: + title: API + version: 1.0.0 +servers: + - url: https://example.com/test + variables: + var: + enum: ['a', 'b'] +components: + {} \ No newline at end of file diff --git a/test/openapi_3.1/server_variable.spec.ts b/test/openapi_3.1/server_variable.spec.ts new file mode 100644 index 00000000..d790b9c6 --- /dev/null +++ b/test/openapi_3.1/server_variable.spec.ts @@ -0,0 +1,21 @@ +import * as request from 'supertest'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('server variable - OpenAPI 3.1', () => { + it('returns 500 when server variable has no default property', async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'server_variable_no_default.yaml'); + const app = await createApp( + { apiSpec, validateRequests: true, validateResponses: true }, + 3005, + undefined, + false, + ) as any; + + await request(app) + .get(`${app.basePath}`) + .expect(500); + + app.server.close(); + }); +}) \ No newline at end of file From 44939eed0fed5809d103b9cdf1c44744636676b4 Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 1 Apr 2024 00:32:58 +0100 Subject: [PATCH 33/60] feat(openapi_3.1): ensure API supports an endpoint without response --- .../parsers/schema.preprocessor.ts | 2 +- test/openapi_3.1/path_no_response.spec.ts | 36 +++++++++++++++++++ .../resources/path_no_response.yaml | 11 ++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 test/openapi_3.1/path_no_response.spec.ts create mode 100644 test/openapi_3.1/resources/path_no_response.yaml diff --git a/src/middlewares/parsers/schema.preprocessor.ts b/src/middlewares/parsers/schema.preprocessor.ts index c5315429..6b18d7a2 100644 --- a/src/middlewares/parsers/schema.preprocessor.ts +++ b/src/middlewares/parsers/schema.preprocessor.ts @@ -541,7 +541,7 @@ export class SchemaPreprocessor { const op = node.schema; const responses = op.responses; - if (!responses) return; + if (!responses) return []; const schemas: Root[] = []; for (const [statusCode, response] of Object.entries(responses)) { diff --git a/test/openapi_3.1/path_no_response.spec.ts b/test/openapi_3.1/path_no_response.spec.ts new file mode 100644 index 00000000..b40619e3 --- /dev/null +++ b/test/openapi_3.1/path_no_response.spec.ts @@ -0,0 +1,36 @@ +import * as request from 'supertest'; +import * as express from 'express'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('operation object without response - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'path_no_response.yaml'); + app = await createApp( + { apiSpec, validateRequests: true, validateResponses: true }, + 3005, + (app) => app.use( + express + .Router() + .get(`/v1`, (req, res) => + res.status(200).end(), + ), + ) + ); + app + }); + + after(() => { + app.server.close(); + }); + + // In OpenAPI 3.1 it's possible to have a path without a response defined + it('should support endpoint with defined operation object without response', () => { + return request(app) + .get(`${app.basePath}`) + .expect(200); + }); + +}) \ No newline at end of file diff --git a/test/openapi_3.1/resources/path_no_response.yaml b/test/openapi_3.1/resources/path_no_response.yaml new file mode 100644 index 00000000..5426037b --- /dev/null +++ b/test/openapi_3.1/resources/path_no_response.yaml @@ -0,0 +1,11 @@ +# Adapted from https://github.com/OAI/OpenAPI-Specification/blob/77c7b9a522ab6fb83a49e8088fa600e93da4f44e/tests/v3.1/pass/path_no_response.yaml + +openapi: 3.1.0 +info: + title: API + version: 1.0.0 +servers: + - url: /v1 +paths: + /: + get: {} \ No newline at end of file From 47fb245657c25c5a1332afa2ea41e3d50b9568b6 Mon Sep 17 00:00:00 2001 From: sf97 Date: Sun, 28 Apr 2024 19:45:42 +0100 Subject: [PATCH 34/60] feat(openapi_3.1): add full type support for open api 3.1 --- src/framework/types.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/framework/types.ts b/src/framework/types.ts index 863b7bf8..fb426946 100644 --- a/src/framework/types.ts +++ b/src/framework/types.ts @@ -200,9 +200,14 @@ export namespace OpenAPIV3 { externalDocs?: ExternalDocumentationObject; } - export interface DocumentV3_1 extends Omit { + interface ComponentsV3_1 extends ComponentsObject { + pathItems?: { [path: string]: PathItemObject | ReferenceObject } + } + + export interface DocumentV3_1 extends Omit { paths?: DocumentV3['paths']; info: InfoObjectV3_1; + components: ComponentsV3_1; webhooks: { [name: string]: PathItemObject | ReferenceObject } From 6d14d8ca8085abd2ae7ffc7652287722677758e9 Mon Sep 17 00:00:00 2001 From: sf97 Date: Sun, 28 Apr 2024 20:08:56 +0100 Subject: [PATCH 35/60] test(openapi_3.1): adds test for path item support in components --- .../openapi_3.1/components_path_items.spec.ts | 34 +++++++++++++++++++ .../resources/components_path_items.yaml | 21 ++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 test/openapi_3.1/components_path_items.spec.ts create mode 100644 test/openapi_3.1/resources/components_path_items.yaml diff --git a/test/openapi_3.1/components_path_items.spec.ts b/test/openapi_3.1/components_path_items.spec.ts new file mode 100644 index 00000000..cbd282da --- /dev/null +++ b/test/openapi_3.1/components_path_items.spec.ts @@ -0,0 +1,34 @@ +import * as request from 'supertest'; +import * as express from 'express'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('component path item support - OpenAPI 3.1', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'components_path_items.yaml'); + app = await createApp( + { apiSpec, validateRequests: true, validateResponses: true }, + 3005, + (app) => app.use( + express + .Router() + .get(`/v1/entity`, (req, res) => + res.status(200).json({}), + ), + ) + ); + }); + + after(() => { + app.server.close(); + }); + + it('should support path item on components', async () => { + return request(app) + .get(`${app.basePath}/entity`) + .expect(200); + }); + +}) \ No newline at end of file diff --git a/test/openapi_3.1/resources/components_path_items.yaml b/test/openapi_3.1/resources/components_path_items.yaml new file mode 100644 index 00000000..030e6757 --- /dev/null +++ b/test/openapi_3.1/resources/components_path_items.yaml @@ -0,0 +1,21 @@ +openapi: 3.1.0 +info: + title: Example specification + version: "1.0" +servers: + - url: /v1 +components: + pathItems: + entity: + get: + description: 'test' + responses: + 200: + description: GETS my entity + content: + application/json: + schema: + type: object +paths: + /entity: + $ref: '#/components/pathItems/entity' From b250c7fe496ae48912e8ec023a56f4e9f415b5b5 Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 13 May 2024 23:27:33 +0100 Subject: [PATCH 36/60] fix(openapi_3.1_schema): update schema to fix bug --- .../openapi.v3_1.modified.schema.json | 311 +++++------------- 1 file changed, 86 insertions(+), 225 deletions(-) diff --git a/src/framework/openapi.v3_1.modified.schema.json b/src/framework/openapi.v3_1.modified.schema.json index 76e4df1d..f34616fe 100644 --- a/src/framework/openapi.v3_1.modified.schema.json +++ b/src/framework/openapi.v3_1.modified.schema.json @@ -21,9 +21,11 @@ "items": { "$ref": "#/$defs/server" }, - "default": [{ - "url": "/" - }] + "default": [ + { + "url": "/" + } + ] }, "paths": { "$ref": "#/$defs/paths" @@ -31,7 +33,7 @@ "webhooks": { "type": "object", "additionalProperties": { - "$ref": "#/$defs/path-item-or-reference" + "$ref": "#/$defs/path-item" } }, "components": { @@ -53,24 +55,16 @@ "$ref": "#/$defs/external-documentation" } }, - "required": [ - "openapi", - "info" - ], - "anyOf": [{ - "required": [ - "paths" - ] + "required": ["openapi", "info"], + "anyOf": [ + { + "required": ["paths"] }, { - "required": [ - "components" - ] + "required": ["components"] }, { - "required": [ - "webhooks" - ] + "required": ["webhooks"] } ], "$ref": "#/$defs/specification-extensions", @@ -103,10 +97,7 @@ "type": "string" } }, - "required": [ - "title", - "version" - ], + "required": ["title", "version"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -144,15 +135,11 @@ "format": "uri" } }, - "required": [ - "name" - ], + "required": ["name"], "dependentSchemas": { "identifier": { "not": { - "required": [ - "url" - ] + "required": ["url"] } } }, @@ -164,8 +151,7 @@ "type": "object", "properties": { "url": { - "type": "string", - "format": "uri-reference" + "type": "string" }, "description": { "type": "string" @@ -177,9 +163,7 @@ } } }, - "required": [ - "url" - ], + "required": ["url"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -201,9 +185,7 @@ "type": "string" } }, - "required": [ - "default" - ], + "required": ["default"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -268,7 +250,7 @@ "pathItems": { "type": "object", "additionalProperties": { - "$ref": "#/$defs/path-item-or-reference" + "$ref": "#/$defs/path-item" } } }, @@ -298,6 +280,10 @@ "$comment": "https://spec.openapis.org/oas/v3.1.0#path-item-object", "type": "object", "properties": { + "$ref": { + "type": "string", + "format": "uri-reference" + }, "summary": { "type": "string" }, @@ -344,20 +330,6 @@ "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, - "path-item-or-reference": { - "if": { - "type": "object", - "required": [ - "$ref" - ] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/path-item" - } - }, "operation": { "$comment": "https://spec.openapis.org/oas/v3.1.0#operation-object", "type": "object", @@ -430,9 +402,7 @@ "format": "uri" } }, - "required": [ - "url" - ], + "required": ["url"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -444,12 +414,7 @@ "type": "string" }, "in": { - "enum": [ - "query", - "header", - "path", - "cookie" - ] + "enum": ["query", "header", "path", "cookie"] }, "description": { "type": "string" @@ -471,19 +436,13 @@ "maxProperties": 1 } }, - "required": [ - "name", - "in" - ], - "oneOf": [{ - "required": [ - "schema" - ] + "required": ["name", "in"], + "oneOf": [ + { + "required": ["schema"] }, { - "required": [ - "content" - ] + "required": ["content"] } ], "if": { @@ -492,9 +451,7 @@ "const": "query" } }, - "required": [ - "in" - ] + "required": ["in"] }, "then": { "properties": { @@ -514,7 +471,8 @@ "type": "boolean" } }, - "allOf": [{ + "allOf": [ + { "$ref": "#/$defs/examples" }, { @@ -541,27 +499,19 @@ "const": "path" } }, - "required": [ - "in" - ] + "required": ["in"] }, "then": { "properties": { "style": { "default": "simple", - "enum": [ - "matrix", - "label", - "simple" - ] + "enum": ["matrix", "label", "simple"] }, "required": { "const": true } }, - "required": [ - "required" - ] + "required": ["required"] } }, "styles-for-header": { @@ -571,9 +521,7 @@ "const": "header" } }, - "required": [ - "in" - ] + "required": ["in"] }, "then": { "properties": { @@ -591,9 +539,7 @@ "const": "query" } }, - "required": [ - "in" - ] + "required": ["in"] }, "then": { "properties": { @@ -620,9 +566,7 @@ "const": "cookie" } }, - "required": [ - "in" - ] + "required": ["in"] }, "then": { "properties": { @@ -642,9 +586,7 @@ "parameter-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -668,18 +610,14 @@ "type": "boolean" } }, - "required": [ - "content" - ], + "required": ["content"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, "request-body-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -712,7 +650,8 @@ } } }, - "allOf": [{ + "allOf": [ + { "$ref": "#/$defs/specification-extensions" }, { @@ -737,12 +676,7 @@ }, "style": { "default": "form", - "enum": [ - "form", - "spaceDelimited", - "pipeDelimited", - "deepObject" - ] + "enum": ["form", "spaceDelimited", "pipeDelimited", "deepObject"] }, "explode": { "type": "boolean" @@ -752,7 +686,8 @@ "type": "boolean" } }, - "allOf": [{ + "allOf": [ + { "$ref": "#/$defs/specification-extensions" }, { @@ -810,18 +745,14 @@ } } }, - "required": [ - "description" - ], + "required": ["description"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, "response-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -835,15 +766,13 @@ "type": "object", "$ref": "#/$defs/specification-extensions", "additionalProperties": { - "$ref": "#/$defs/path-item-or-reference" + "$ref": "#/$defs/path-item" } }, "callbacks-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -869,10 +798,7 @@ } }, "not": { - "required": [ - "value", - "externalValue" - ] + "required": ["value", "externalValue"] }, "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false @@ -880,9 +806,7 @@ "example-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -913,15 +837,12 @@ "$ref": "#/$defs/server" } }, - "oneOf": [{ - "required": [ - "operationRef" - ] + "oneOf": [ + { + "required": ["operationRef"] }, { - "required": [ - "operationId" - ] + "required": ["operationId"] } ], "$ref": "#/$defs/specification-extensions", @@ -930,9 +851,7 @@ "link-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -965,15 +884,12 @@ "maxProperties": 1 } }, - "oneOf": [{ - "required": [ - "schema" - ] + "oneOf": [ + { + "required": ["schema"] }, { - "required": [ - "content" - ] + "required": ["content"] } ], "dependentSchemas": { @@ -997,9 +913,7 @@ "header-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -1022,9 +936,7 @@ "$ref": "#/$defs/external-documentation" } }, - "required": [ - "name" - ], + "required": ["name"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -1047,32 +959,22 @@ "schema": { "$comment": "https://spec.openapis.org/oas/v3.1.0#schema-object", "$dynamicAnchor": "meta", - "type": [ - "object", - "boolean" - ] + "type": ["object", "boolean"] }, "security-scheme": { "$comment": "https://spec.openapis.org/oas/v3.1.0#security-scheme-object", "type": "object", "properties": { "type": { - "enum": [ - "apiKey", - "http", - "mutualTLS", - "oauth2", - "openIdConnect" - ] + "enum": ["apiKey", "http", "mutualTLS", "oauth2", "openIdConnect"] }, "description": { "type": "string" } }, - "required": [ - "type" - ], - "allOf": [{ + "required": ["type"], + "allOf": [ + { "$ref": "#/$defs/specification-extensions" }, { @@ -1100,9 +1002,7 @@ "const": "apiKey" } }, - "required": [ - "type" - ] + "required": ["type"] }, "then": { "properties": { @@ -1110,17 +1010,10 @@ "type": "string" }, "in": { - "enum": [ - "query", - "header", - "cookie" - ] + "enum": ["query", "header", "cookie"] } }, - "required": [ - "name", - "in" - ] + "required": ["name", "in"] } }, "type-http": { @@ -1130,9 +1023,7 @@ "const": "http" } }, - "required": [ - "type" - ] + "required": ["type"] }, "then": { "properties": { @@ -1140,9 +1031,7 @@ "type": "string" } }, - "required": [ - "scheme" - ] + "required": ["scheme"] } }, "type-http-bearer": { @@ -1156,10 +1045,7 @@ "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" } }, - "required": [ - "type", - "scheme" - ] + "required": ["type", "scheme"] }, "then": { "properties": { @@ -1176,9 +1062,7 @@ "const": "oauth2" } }, - "required": [ - "type" - ] + "required": ["type"] }, "then": { "properties": { @@ -1186,9 +1070,7 @@ "$ref": "#/$defs/oauth-flows" } }, - "required": [ - "flows" - ] + "required": ["flows"] } }, "type-oidc": { @@ -1198,9 +1080,7 @@ "const": "openIdConnect" } }, - "required": [ - "type" - ] + "required": ["type"] }, "then": { "properties": { @@ -1209,9 +1089,7 @@ "format": "uri" } }, - "required": [ - "openIdConnectUrl" - ] + "required": ["openIdConnectUrl"] } } } @@ -1219,9 +1097,7 @@ "security-scheme-or-reference": { "if": { "type": "object", - "required": [ - "$ref" - ] + "required": ["$ref"] }, "then": { "$ref": "#/$defs/reference" @@ -1264,10 +1140,7 @@ "$ref": "#/$defs/map-of-strings" } }, - "required": [ - "authorizationUrl", - "scopes" - ], + "required": ["authorizationUrl", "scopes"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -1286,10 +1159,7 @@ "$ref": "#/$defs/map-of-strings" } }, - "required": [ - "tokenUrl", - "scopes" - ], + "required": ["tokenUrl", "scopes"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -1308,10 +1178,7 @@ "$ref": "#/$defs/map-of-strings" } }, - "required": [ - "tokenUrl", - "scopes" - ], + "required": ["tokenUrl", "scopes"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false }, @@ -1334,11 +1201,7 @@ "$ref": "#/$defs/map-of-strings" } }, - "required": [ - "authorizationUrl", - "tokenUrl", - "scopes" - ], + "required": ["authorizationUrl", "tokenUrl", "scopes"], "$ref": "#/$defs/specification-extensions", "unevaluatedProperties": false } @@ -1384,9 +1247,7 @@ "const": "form" } }, - "required": [ - "style" - ] + "required": ["style"] }, "then": { "properties": { @@ -1404,4 +1265,4 @@ } } } -} \ No newline at end of file +} From 0daf9f3b8695e252229c3aba9561e3977a2d1509 Mon Sep 17 00:00:00 2001 From: sf97 Date: Thu, 16 May 2024 23:19:17 +0100 Subject: [PATCH 37/60] feat(openapi_3.1): support reusable path items --- src/middlewares/parsers/schema.preprocessor.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/middlewares/parsers/schema.preprocessor.ts b/src/middlewares/parsers/schema.preprocessor.ts index 6b18d7a2..5c8ca580 100644 --- a/src/middlewares/parsers/schema.preprocessor.ts +++ b/src/middlewares/parsers/schema.preprocessor.ts @@ -157,6 +157,11 @@ export class SchemaPreprocessor { for (const [p, pi] of Object.entries(this.apiDoc.paths)) { const pathItem = this.resolveSchema(pi); + + // Since OpenAPI 3.1, paths can be a #ref to reusable path items + // The following line mutates the paths item to dereference the reference, so that we can process as a POJO, as we would if it wasn't a reference + this.apiDoc.paths[p] = pathItem; + for (const method of Object.keys(pathItem)) { if (httpMethods.has(method)) { const operation = pathItem[method]; From 448d239ef73ea29e809a32fede9037da6ad30567 Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 20 May 2024 20:51:59 +0100 Subject: [PATCH 38/60] style(linting): fix linting issues --- src/middlewares/openapi.request.validator.ts | 1 - src/middlewares/parsers/schema.preprocessor.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/middlewares/openapi.request.validator.ts b/src/middlewares/openapi.request.validator.ts index 1e01fa19..3b795724 100644 --- a/src/middlewares/openapi.request.validator.ts +++ b/src/middlewares/openapi.request.validator.ts @@ -21,7 +21,6 @@ import { import { BodySchemaParser } from './parsers/body.parse'; import { ParametersSchemaParser } from './parsers/schema.parse'; import { RequestParameterMutator } from './parsers/req.parameter.mutator'; -import { debug } from 'console'; type OperationObject = OpenAPIV3.OperationObject; type SchemaObject = OpenAPIV3.SchemaObject; diff --git a/src/middlewares/parsers/schema.preprocessor.ts b/src/middlewares/parsers/schema.preprocessor.ts index 5c8ca580..b6f756e8 100644 --- a/src/middlewares/parsers/schema.preprocessor.ts +++ b/src/middlewares/parsers/schema.preprocessor.ts @@ -127,8 +127,8 @@ export class SchemaPreprocessor { // Traverse the schemas if (r) { this.traverseSchemas(schemaNodes, (parent, schema, opts) => - this.schemaVisitor(parent, schema, opts), - ); + this.schemaVisitor(parent, schema, opts), + ); } return { From b4cbb1a15ab35bd4f37dab6829468d2aea666120 Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 20 May 2024 21:00:46 +0100 Subject: [PATCH 39/60] style(openapi): improve readability of version validation --- src/framework/openapi.schema.validator.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/framework/openapi.schema.validator.ts b/src/framework/openapi.schema.validator.ts index 81a20f7b..46d40cd6 100644 --- a/src/framework/openapi.schema.validator.ts +++ b/src/framework/openapi.schema.validator.ts @@ -32,22 +32,26 @@ export class OpenAPISchemaValidator { options.validateSchema = false; } - const ver = opts.version && parseFloat(String(opts.version)); - if (!ver) throw Error('version missing from OpenAPI specification'); - if (parseInt(ver.toString()) != 3) throw Error('OpenAPI v3 specification version is required'); + const [ok, major, minor] = /^(\d+)\.(\d+).(\d+)?$/.exec(opts.version); + + if (!ok) { + throw Error('Version missing from OpenAPI specification') + }; + + if (major !== '3' || minor !== '0' && minor !== '1') { + throw new Error('OpenAPI v3.0 or v3.1 specification version is required'); + } let ajvInstance; let schema; - if (ver === 3) { + if (minor === '0') { schema = openapi3Schema; ajvInstance = new AjvDraft4(options); - } else if (ver === 3.1) { + } else if (minor == '1') { schema = openapi31Schema; ajvInstance = new Ajv2020(options); ajvInstance.addFormat('media-range', true); // TODO: Validate media-range format as defined in https://www.rfc-editor.org/rfc/rfc9110.html#name-collected-abnf - } else { - throw new Error('OpenAPI v3 specification 3.0 and 3.1 supported'); } addFormats(ajvInstance, ['email', 'regex', 'uri', 'uri-reference']); From 1c15ceed8998f6d7e973a42ad7ce0bd2b3d618f0 Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 20 May 2024 21:24:46 +0100 Subject: [PATCH 40/60] docs(schema-validator): clearly state why media-range attribute is not defined --- src/framework/openapi.schema.validator.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/framework/openapi.schema.validator.ts b/src/framework/openapi.schema.validator.ts index 46d40cd6..025f201f 100644 --- a/src/framework/openapi.schema.validator.ts +++ b/src/framework/openapi.schema.validator.ts @@ -51,7 +51,12 @@ export class OpenAPISchemaValidator { } else if (minor == '1') { schema = openapi31Schema; ajvInstance = new Ajv2020(options); - ajvInstance.addFormat('media-range', true); // TODO: Validate media-range format as defined in https://www.rfc-editor.org/rfc/rfc9110.html#name-collected-abnf + + // Open API 3.1 has a custom "media-range" attribute defined in its schema, but the spec does not define it. "It's not really intended to be validated" + // https://github.com/OAI/OpenAPI-Specification/issues/2714#issuecomment-923185689 + // Since the schema is non-normative (https://github.com/OAI/OpenAPI-Specification/pull/3355#issuecomment-1915695294) we will only validate that it's a string + // as the spec states + ajvInstance.addFormat('media-range', true); } addFormats(ajvInstance, ['email', 'regex', 'uri', 'uri-reference']); From f842723e84efefcdc815ac8306c38bab2d679120 Mon Sep 17 00:00:00 2001 From: carmine Date: Sat, 24 Aug 2024 11:30:16 -0400 Subject: [PATCH 41/60] version 6.0.0-alpha.1 with initial OAS-3.1 support (from PR https://github.com/cdimascio/express-openapi-validator/pull/882) --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b3bff6fc..235567f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "express-openapi-validator", - "version": "5.3.4", + "version": "6.0.0-alpha.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "express-openapi-validator", - "version": "5.3.4", + "version": "6.0.0-alpha.1", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.7.0", diff --git a/package.json b/package.json index 8622afd8..b994f522 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "express-openapi-validator", - "version": "5.3.4", + "version": "6.0.0-alpha.1", "description": "Automatically validate API requests and responses with OpenAPI 3 and Express.", "main": "dist/index.js", "scripts": { From f5b31dc5b5d25c30cbf03fb009a07815e5e77ba3 Mon Sep 17 00:00:00 2001 From: carmine Date: Sat, 31 Aug 2024 19:51:13 -0400 Subject: [PATCH 42/60] v6.0.0-alpha.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 235567f3..e638110b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "express-openapi-validator", - "version": "6.0.0-alpha.1", + "version": "6.0.0-alpha.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "express-openapi-validator", - "version": "6.0.0-alpha.1", + "version": "6.0.0-alpha.2", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.7.0", diff --git a/package.json b/package.json index b994f522..b8d6a241 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "express-openapi-validator", - "version": "6.0.0-alpha.1", + "version": "6.0.0-alpha.2", "description": "Automatically validate API requests and responses with OpenAPI 3 and Express.", "main": "dist/index.js", "scripts": { From ee85d8d1820838df27e675423071bf4339d42685 Mon Sep 17 00:00:00 2001 From: carmine Date: Sat, 31 Aug 2024 20:16:50 -0400 Subject: [PATCH 43/60] alpha.3 --- package-lock.json | 4 ++-- package.json | 2 +- src/framework/types.ts | 2 +- test/default.export.fn.spec.ts | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index e638110b..c6c393cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "express-openapi-validator", - "version": "6.0.0-alpha.2", + "version": "6.0.0-alpha.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "express-openapi-validator", - "version": "6.0.0-alpha.2", + "version": "6.0.0-alpha.3", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.7.0", diff --git a/package.json b/package.json index b8d6a241..b52cf720 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "express-openapi-validator", - "version": "6.0.0-alpha.2", + "version": "6.0.0-alpha.3", "description": "Automatically validate API requests and responses with OpenAPI 3 and Express.", "main": "dist/index.js", "scripts": { diff --git a/src/framework/types.ts b/src/framework/types.ts index fb426946..5924e1eb 100644 --- a/src/framework/types.ts +++ b/src/framework/types.ts @@ -81,7 +81,7 @@ export type OperationHandlerOptions = { resolver: ( handlersPath: string, route: RouteMetadata, - apiDoc: OpenAPIV3.Document, + apiDoc: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, ) => unknown; }; diff --git a/test/default.export.fn.spec.ts b/test/default.export.fn.spec.ts index d0cdaebe..e01a356e 100644 --- a/test/default.export.fn.spec.ts +++ b/test/default.export.fn.spec.ts @@ -3,6 +3,7 @@ import * as OpenApiValidator from '../src'; import { expect } from 'chai'; import * as request from 'supertest'; import * as path from 'path'; +import {OpenAPIV3} from "../src/framework/types"; describe('default export resolver', () => { let server = null; @@ -11,7 +12,7 @@ describe('default export resolver', () => { before(async () => { app.use( OpenApiValidator.middleware({ - apiSpec: { + apiSpec: { openapi: '3.0.0', info: { version: '1.0.0', title: 'test bug OpenApiValidator' }, paths: { From 03efbf3434fb54fec2cd852b1704f62bacbca236 Mon Sep 17 00:00:00 2001 From: Sergio Ferreira Date: Sun, 31 Dec 2023 09:57:37 +0000 Subject: [PATCH 44/60] feat(openapi): support version 3.1 --- src/framework/openapi.v3_1.modified.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/openapi.v3_1.modified.schema.json b/src/framework/openapi.v3_1.modified.schema.json index f34616fe..61f0ae44 100644 --- a/src/framework/openapi.v3_1.modified.schema.json +++ b/src/framework/openapi.v3_1.modified.schema.json @@ -1265,4 +1265,4 @@ } } } -} +} \ No newline at end of file From 6333ae0032121214f45a0d734f4de6a83e69988b Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 5 Feb 2024 00:25:12 +0000 Subject: [PATCH 45/60] feat(openapi_3.1): adds open api 3.1 type --- src/middlewares/openapi.request.validator.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/middlewares/openapi.request.validator.ts b/src/middlewares/openapi.request.validator.ts index 3b795724..1e01fa19 100644 --- a/src/middlewares/openapi.request.validator.ts +++ b/src/middlewares/openapi.request.validator.ts @@ -21,6 +21,7 @@ import { import { BodySchemaParser } from './parsers/body.parse'; import { ParametersSchemaParser } from './parsers/schema.parse'; import { RequestParameterMutator } from './parsers/req.parameter.mutator'; +import { debug } from 'console'; type OperationObject = OpenAPIV3.OperationObject; type SchemaObject = OpenAPIV3.SchemaObject; From f23ce0c80eabd69e66410c34b02db2499a3ee951 Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 13 May 2024 23:27:33 +0100 Subject: [PATCH 46/60] fix(openapi_3.1_schema): update schema to fix bug --- src/framework/openapi.v3_1.modified.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/openapi.v3_1.modified.schema.json b/src/framework/openapi.v3_1.modified.schema.json index 61f0ae44..f34616fe 100644 --- a/src/framework/openapi.v3_1.modified.schema.json +++ b/src/framework/openapi.v3_1.modified.schema.json @@ -1265,4 +1265,4 @@ } } } -} \ No newline at end of file +} From e3daa22cca4ba5bb449efffea37aa0195d4139dc Mon Sep 17 00:00:00 2001 From: sf97 Date: Mon, 20 May 2024 20:51:59 +0100 Subject: [PATCH 47/60] style(linting): fix linting issues --- src/middlewares/openapi.request.validator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/middlewares/openapi.request.validator.ts b/src/middlewares/openapi.request.validator.ts index 1e01fa19..3b795724 100644 --- a/src/middlewares/openapi.request.validator.ts +++ b/src/middlewares/openapi.request.validator.ts @@ -21,7 +21,6 @@ import { import { BodySchemaParser } from './parsers/body.parse'; import { ParametersSchemaParser } from './parsers/schema.parse'; import { RequestParameterMutator } from './parsers/req.parameter.mutator'; -import { debug } from 'console'; type OperationObject = OpenAPIV3.OperationObject; type SchemaObject = OpenAPIV3.SchemaObject; From ef8e7ba4d722fc80bc57d54d847dba006bb029f7 Mon Sep 17 00:00:00 2001 From: Luis Philipe Fidelis Date: Sun, 10 Nov 2024 11:12:07 -0300 Subject: [PATCH 48/60] fix: instantiate Ajv2020 for OAS 3.1 (#1009) * chore: create factories for ajvInstance and schema * test: writing some tests * chore: removing ts from editorconfig * chore: add eslint --- examples/3-eov-operations/package-lock.json | 2547 ++++++++++++++++- examples/3-eov-operations/package.json | 6 +- examples/9-nestjs/package.json | 3 + src/framework/ajv/factory.ts | 25 + src/framework/ajv/index.ts | 17 +- src/framework/openapi.schema.validator.ts | 38 +- src/framework/openapi/assert.version.ts | 19 + src/framework/openapi/factory.schema.ts | 16 + src/framework/types.ts | 4 + .../resources/unevaluated_properties.yaml | 28 + .../unevaluated_properties.spec.ts | 45 + 11 files changed, 2655 insertions(+), 93 deletions(-) create mode 100644 src/framework/ajv/factory.ts create mode 100644 src/framework/openapi/assert.version.ts create mode 100644 src/framework/openapi/factory.schema.ts create mode 100644 test/openapi_3.1/resources/unevaluated_properties.yaml create mode 100644 test/openapi_3.1/unevaluated_properties.spec.ts diff --git a/examples/3-eov-operations/package-lock.json b/examples/3-eov-operations/package-lock.json index 31899032..71d26328 100644 --- a/examples/3-eov-operations/package-lock.json +++ b/examples/3-eov-operations/package-lock.json @@ -13,8 +13,12 @@ "morgan": "^1.10.0" }, "devDependencies": { + "@eslint/js": "^9.14.0", + "eslint": "^9.14.0", + "globals": "^15.12.0", "nodemon": "^2.0.4", - "prettier": "^2.1.1" + "prettier": "^2.1.1", + "typescript-eslint": "^8.13.0" } }, "node_modules/@apidevtools/json-schema-ref-parser": { @@ -34,11 +38,302 @@ "url": "https://github.com/sponsors/philsturgeon" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/@eslint/core": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", + "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/@eslint/js": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz", + "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", + "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@jsdevtools/ono": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -56,6 +351,12 @@ "@types/node": "*" } }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, "node_modules/@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", @@ -139,6 +440,305 @@ "@types/send": "*" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz", + "integrity": "sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.13.0", + "@typescript-eslint/type-utils": "8.13.0", + "@typescript-eslint/utils": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.13.0.tgz", + "integrity": "sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.13.0", + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/typescript-estree": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz", + "integrity": "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz", + "integrity": "sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.13.0", + "@typescript-eslint/utils": "8.13.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/@typescript-eslint/types": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.13.0.tgz", + "integrity": "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz", + "integrity": "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.13.0.tgz", + "integrity": "sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.13.0", + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/typescript-estree": "8.13.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz", + "integrity": "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.13.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -159,6 +759,27 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -204,6 +825,21 @@ } } }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -310,12 +946,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -367,6 +1003,52 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -394,6 +1076,24 @@ "fsevents": "~2.3.2" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -478,6 +1178,20 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "node_modules/cross-spawn": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -486,6 +1200,12 @@ "ms": "2.0.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -536,38 +1256,254 @@ "license": "MIT", "peer": true, "engines": { - "node": ">= 0.8" + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT", + "peer": true + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.14.0.tgz", + "integrity": "sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.7.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.14.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.0", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" } }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "license": "MIT", - "peer": true, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, "dependencies": { - "get-intrinsic": "^1.2.4" + "estraverse": "^5.2.0" }, "engines": { - "node": ">= 0.4" + "node": ">=4.0" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "peer": true, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=4.0" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT", - "peer": true + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/etag": { "version": "1.8.1", @@ -692,16 +1628,65 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, "node_modules/fast-uri": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", "license": "MIT" }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -742,6 +1727,41 @@ "node": ">= 0.8" } }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -818,6 +1838,18 @@ "node": ">= 6" } }, + "node_modules/globals": { + "version": "15.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz", + "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -831,6 +1863,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -922,12 +1960,46 @@ "node": ">=0.10.0" } }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", "dev": true }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -990,6 +2062,12 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1002,11 +2080,60 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -1017,6 +2144,12 @@ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/media-typer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", @@ -1032,6 +2165,15 @@ "license": "MIT", "peer": true }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -1042,6 +2184,19 @@ "node": ">= 0.6" } }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -1142,6 +2297,12 @@ "node": ">= 6.0.0" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -1264,6 +2425,65 @@ "@jsdevtools/ono": "7.1.3" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1274,6 +2494,24 @@ "node": ">= 0.8" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-to-regexp": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", @@ -1292,6 +2530,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/prettier": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.1.tgz", @@ -1329,6 +2576,15 @@ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -1345,6 +2601,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -1405,6 +2681,48 @@ "node": ">=0.10.0" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -1502,16 +2820,37 @@ "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC", + "peer": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC", - "peer": true - }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -1578,6 +2917,18 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -1590,6 +2941,12 @@ "node": ">=4" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1624,6 +2981,30 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/ts-api-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", + "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1649,6 +3030,43 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.13.0.tgz", + "integrity": "sha512-vIMpDRJrQd70au2G8w34mPps0ezFSPMEX4pXkTzUkrNbRX+36ais2ksGWN0esZL+ZMaFJEneOBHzCgSqle7DHw==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.13.0", + "@typescript-eslint/parser": "8.13.0", + "@typescript-eslint/utils": "8.13.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -1670,6 +3088,15 @@ "node": ">= 0.8" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -1695,6 +3122,30 @@ "node": ">= 0.8" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -1702,6 +3153,18 @@ "engines": { "node": ">=0.4" } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } }, "dependencies": { @@ -1715,11 +3178,209 @@ "js-yaml": "^4.1.0" } }, + "@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.4.3" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + } + } + }, + "@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true + }, + "@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "requires": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "@eslint/core": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", + "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "@eslint/js": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz", + "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==", + "dev": true + }, + "@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true + }, + "@eslint/plugin-kit": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", + "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "dev": true, + "requires": { + "levn": "^0.4.1" + } + }, + "@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true + }, + "@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "requires": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "dependencies": { + "@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true + } + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true + }, "@jsdevtools/ono": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, "@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -1737,6 +3398,12 @@ "@types/node": "*" } }, + "@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, "@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", @@ -1809,14 +3476,193 @@ "@types/node": "*" } }, - "@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "requires": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz", + "integrity": "sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.13.0", + "@typescript-eslint/type-utils": "8.13.0", + "@typescript-eslint/utils": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + } + }, + "@typescript-eslint/parser": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.13.0.tgz", + "integrity": "sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "8.13.0", + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/typescript-estree": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0", + "debug": "^4.3.4" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz", + "integrity": "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz", + "integrity": "sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "8.13.0", + "@typescript-eslint/utils": "8.13.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "dependencies": { + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "@typescript-eslint/types": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.13.0.tgz", + "integrity": "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz", + "integrity": "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==", + "dev": true, + "requires": { + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/visitor-keys": "8.13.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true + } + } + }, + "@typescript-eslint/utils": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.13.0.tgz", + "integrity": "sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.13.0", + "@typescript-eslint/types": "8.13.0", + "@typescript-eslint/typescript-estree": "8.13.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz", + "integrity": "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==", + "dev": true, "requires": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" + "@typescript-eslint/types": "8.13.0", + "eslint-visitor-keys": "^3.4.3" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + } } }, "abbrev": { @@ -1835,6 +3681,19 @@ "negotiator": "0.6.3" } }, + "acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, "ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -1860,6 +3719,15 @@ "ajv": "^8.0.0" } }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -1948,12 +3816,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "buffer-from": { @@ -1988,6 +3856,39 @@ "set-function-length": "^1.2.1" } }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -2004,6 +3905,21 @@ "readdirp": "~3.6.0" } }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2060,6 +3976,17 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "cross-spawn": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2068,6 +3995,12 @@ "ms": "2.0.0" } }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -2122,6 +4055,156 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "peer": true }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.14.0.tgz", + "integrity": "sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.7.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.14.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.0", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true + }, + "espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "requires": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + } + }, + "esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -2215,15 +4298,58 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, "fast-uri": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" }, + "fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "requires": { + "flat-cache": "^4.0.0" + } + }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -2255,6 +4381,32 @@ } } }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "requires": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + } + }, + "flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -2302,6 +4454,12 @@ "is-glob": "^4.0.1" } }, + "globals": { + "version": "15.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz", + "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==", + "dev": true + }, "gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -2311,6 +4469,12 @@ "get-intrinsic": "^1.1.3" } }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2369,12 +4533,34 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true + }, "ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", "dev": true }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -2421,6 +4607,12 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2429,11 +4621,51 @@ "argparse": "^2.0.1" } }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -2444,6 +4676,12 @@ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "media-typer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", @@ -2455,12 +4693,28 @@ "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", "peer": true }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "peer": true }, + "micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "requires": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + } + }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -2533,6 +4787,12 @@ "xtend": "^4.0.0" } }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -2621,12 +4881,65 @@ "@jsdevtools/ono": "7.1.3" } }, + "optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "peer": true }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, "path-to-regexp": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", @@ -2638,6 +4951,12 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, "prettier": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.1.tgz", @@ -2665,6 +4984,12 @@ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true + }, "qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -2674,6 +4999,12 @@ "side-channel": "^1.0.4" } }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -2720,6 +5051,27 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -2807,6 +5159,21 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "peer": true }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, "side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -2855,6 +5222,12 @@ "safe-buffer": "~5.1.0" } }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -2864,6 +5237,12 @@ "has-flag": "^3.0.0" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2888,6 +5267,22 @@ "nopt": "~1.0.10" } }, + "ts-api-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", + "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==", + "dev": true, + "requires": {} + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -2909,6 +5304,24 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, + "typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "peer": true + }, + "typescript-eslint": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.13.0.tgz", + "integrity": "sha512-vIMpDRJrQd70au2G8w34mPps0ezFSPMEX4pXkTzUkrNbRX+36ais2ksGWN0esZL+ZMaFJEneOBHzCgSqle7DHw==", + "dev": true, + "requires": { + "@typescript-eslint/eslint-plugin": "8.13.0", + "@typescript-eslint/parser": "8.13.0", + "@typescript-eslint/utils": "8.13.0" + } + }, "undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -2926,6 +5339,15 @@ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "peer": true }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2943,10 +5365,31 @@ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "peer": true }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/examples/3-eov-operations/package.json b/examples/3-eov-operations/package.json index d1a09c76..ce233f36 100644 --- a/examples/3-eov-operations/package.json +++ b/examples/3-eov-operations/package.json @@ -16,7 +16,11 @@ "morgan": "^1.10.0" }, "devDependencies": { + "@eslint/js": "^9.14.0", + "eslint": "^9.14.0", + "globals": "^15.12.0", "nodemon": "^2.0.4", - "prettier": "^2.1.1" + "prettier": "^2.1.1", + "typescript-eslint": "^8.13.0" } } diff --git a/examples/9-nestjs/package.json b/examples/9-nestjs/package.json index fa776b33..993e8ade 100644 --- a/examples/9-nestjs/package.json +++ b/examples/9-nestjs/package.json @@ -26,6 +26,9 @@ "shx": "^0.3.3" }, "devDependencies": { + "@eslint/js": "^9.14.0", + "eslint": "^9.14.0", + "globals": "^15.12.0", "@nestjs/cli": "^10.4.5", "@nestjs/testing": "^10.3.8", "@types/jest": "^27.0.2", diff --git a/src/framework/ajv/factory.ts b/src/framework/ajv/factory.ts new file mode 100644 index 00000000..9571bc5a --- /dev/null +++ b/src/framework/ajv/factory.ts @@ -0,0 +1,25 @@ +import { Options } from "ajv"; +import AjvDraft4 from 'ajv-draft-04'; +import Ajv2020 from 'ajv/dist/2020'; +import { assertVersion } from "../openapi/assert.version"; +import { AjvInstance } from "../types"; + +export const factoryAjv = (version: string, options: Options): AjvInstance => { + const { minor } = assertVersion(version) + + let ajvInstance: AjvInstance + + if (minor === '0') { + ajvInstance = new AjvDraft4(options); + } else if (minor == '1') { + ajvInstance = new Ajv2020(options); + + // Open API 3.1 has a custom "media-range" attribute defined in its schema, but the spec does not define it. "It's not really intended to be validated" + // https://github.com/OAI/OpenAPI-Specification/issues/2714#issuecomment-923185689 + // Since the schema is non-normative (https://github.com/OAI/OpenAPI-Specification/pull/3355#issuecomment-1915695294) we will only validate that it's a string + // as the spec states + ajvInstance.addFormat('media-range', true); + } + + return ajvInstance +} \ No newline at end of file diff --git a/src/framework/ajv/index.ts b/src/framework/ajv/index.ts index 238e1ae7..3e204b79 100644 --- a/src/framework/ajv/index.ts +++ b/src/framework/ajv/index.ts @@ -1,10 +1,12 @@ import AjvDraft4 from 'ajv-draft-04'; +import Ajv2020 from 'ajv/dist/2020' import { DataValidateFunction } from 'ajv/dist/types'; import ajvType from 'ajv/dist/vocabularies/jtd/type'; import addFormats from 'ajv-formats'; import { formats } from './formats'; -import { OpenAPIV3, Options, SerDes } from '../types'; +import { AjvInstance, OpenAPIV3, Options, SerDes } from '../types'; import * as traverse from 'json-schema-traverse'; +import { factoryAjv } from './factory'; interface SerDesSchema extends Partial { kind?: 'req' | 'res'; @@ -13,14 +15,14 @@ interface SerDesSchema extends Partial { export function createRequestAjv( openApiSpec: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: Options = {}, -): AjvDraft4 { +): AjvInstance { return createAjv(openApiSpec, options); } export function createResponseAjv( openApiSpec: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: Options = {}, -): AjvDraft4 { +): AjvInstance { return createAjv(openApiSpec, options, false); } @@ -28,12 +30,13 @@ function createAjv( openApiSpec: OpenAPIV3.DocumentV3 | OpenAPIV3.DocumentV3_1, options: Options = {}, request = true, -): AjvDraft4 { +): AjvInstance { const { ajvFormats, ...ajvOptions } = options; - const ajv = new AjvDraft4({ + + const ajv = factoryAjv(openApiSpec.openapi, { ...ajvOptions, - formats: formats, - }); + formats + }) // Clean openApiSpec traverse(openApiSpec, { allKeys: true }, (schema => { diff --git a/src/framework/openapi.schema.validator.ts b/src/framework/openapi.schema.validator.ts index 025f201f..3da4b035 100644 --- a/src/framework/openapi.schema.validator.ts +++ b/src/framework/openapi.schema.validator.ts @@ -1,16 +1,12 @@ -import AjvDraft4, { +import { ErrorObject, Options, ValidateFunction, } from 'ajv-draft-04'; import addFormats from 'ajv-formats'; -// https://github.com/OAI/OpenAPI-Specification/blob/master/schemas/v3.0/schema.json -import * as openapi3Schema from './openapi.v3.schema.json'; -// https://github.com/OAI/OpenAPI-Specification/blob/master/schemas/v3.1/schema.json with dynamic refs replaced due to AJV bug - https://github.com/ajv-validator/ajv/issues/1745 -import * as openapi31Schema from './openapi.v3_1.modified.schema.json'; import { OpenAPIV3 } from './types.js'; - -import Ajv2020 from 'ajv/dist/2020'; +import { factoryAjv } from './ajv/factory'; +import { factorySchema } from './openapi/factory.schema'; export interface OpenAPISchemaValidatorOpts { version: string; @@ -32,32 +28,8 @@ export class OpenAPISchemaValidator { options.validateSchema = false; } - const [ok, major, minor] = /^(\d+)\.(\d+).(\d+)?$/.exec(opts.version); - - if (!ok) { - throw Error('Version missing from OpenAPI specification') - }; - - if (major !== '3' || minor !== '0' && minor !== '1') { - throw new Error('OpenAPI v3.0 or v3.1 specification version is required'); - } - - let ajvInstance; - let schema; - - if (minor === '0') { - schema = openapi3Schema; - ajvInstance = new AjvDraft4(options); - } else if (minor == '1') { - schema = openapi31Schema; - ajvInstance = new Ajv2020(options); - - // Open API 3.1 has a custom "media-range" attribute defined in its schema, but the spec does not define it. "It's not really intended to be validated" - // https://github.com/OAI/OpenAPI-Specification/issues/2714#issuecomment-923185689 - // Since the schema is non-normative (https://github.com/OAI/OpenAPI-Specification/pull/3355#issuecomment-1915695294) we will only validate that it's a string - // as the spec states - ajvInstance.addFormat('media-range', true); - } + const ajvInstance = factoryAjv(opts.version, options) + const schema = factorySchema(opts.version) addFormats(ajvInstance, ['email', 'regex', 'uri', 'uri-reference']); diff --git a/src/framework/openapi/assert.version.ts b/src/framework/openapi/assert.version.ts new file mode 100644 index 00000000..f5c6e05e --- /dev/null +++ b/src/framework/openapi/assert.version.ts @@ -0,0 +1,19 @@ +/** + * Asserts open api version + * + * @param openApiVersion SemVer version + * @returns destructured major and minor + */ +export const assertVersion = (openApiVersion: string) => { + const [ok, major, minor] = /^(\d+)\.(\d+).(\d+)?$/.exec(openApiVersion); + + if (!ok) { + throw Error('Version missing from OpenAPI specification') + }; + + if (major !== '3' || minor !== '0' && minor !== '1') { + throw new Error('OpenAPI v3.0 or v3.1 specification version is required'); + } + + return { major, minor } +} \ No newline at end of file diff --git a/src/framework/openapi/factory.schema.ts b/src/framework/openapi/factory.schema.ts new file mode 100644 index 00000000..2c78e1a0 --- /dev/null +++ b/src/framework/openapi/factory.schema.ts @@ -0,0 +1,16 @@ +import { assertVersion } from "./assert.version"; + +// https://github.com/OAI/OpenAPI-Specification/blob/master/schemas/v3.0/schema.json +import * as openapi3Schema from '../openapi.v3.schema.json'; +// https://github.com/OAI/OpenAPI-Specification/blob/master/schemas/v3.1/schema.json with dynamic refs replaced due to AJV bug - https://github.com/ajv-validator/ajv/issues/1745 +import * as openapi31Schema from '../openapi.v3_1.modified.schema.json'; + +export const factorySchema = (version: string): Object => { + const { minor } = assertVersion(version); + + if (minor === '0') { + return openapi3Schema; + } + + return openapi31Schema +} \ No newline at end of file diff --git a/src/framework/types.ts b/src/framework/types.ts index 5924e1eb..08b3c35c 100644 --- a/src/framework/types.ts +++ b/src/framework/types.ts @@ -3,8 +3,12 @@ import * as multer from 'multer'; import { FormatsPluginOptions } from 'ajv-formats'; import { Request, Response, NextFunction, RequestHandler } from 'express'; import { RouteMetadata } from './openapi.spec.loader'; +import AjvDraft4 from 'ajv-draft-04'; +import Ajv2020 from 'ajv/dist/2020'; export { OpenAPIFrameworkArgs }; +export type AjvInstance = AjvDraft4 | Ajv2020 + export type BodySchema = | OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject diff --git a/test/openapi_3.1/resources/unevaluated_properties.yaml b/test/openapi_3.1/resources/unevaluated_properties.yaml new file mode 100644 index 00000000..cc40845a --- /dev/null +++ b/test/openapi_3.1/resources/unevaluated_properties.yaml @@ -0,0 +1,28 @@ +openapi: 3.1.0 +info: + title: API + version: 1.0.0 +servers: + - url: /v1 +components: + schemas: + EntityRequest: + type: object + properties: + request: + type: string + unevaluatedProperties: false +paths: + /entity: + post: + description: POSTs my entity + requestBody: + description: Request body for entity + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EntityRequest' + responses: + '204': + description: No Content \ No newline at end of file diff --git a/test/openapi_3.1/unevaluated_properties.spec.ts b/test/openapi_3.1/unevaluated_properties.spec.ts new file mode 100644 index 00000000..edb84535 --- /dev/null +++ b/test/openapi_3.1/unevaluated_properties.spec.ts @@ -0,0 +1,45 @@ +import * as request from 'supertest'; +import * as express from 'express'; +import { createApp } from "../common/app"; +import { join } from "path"; + +describe('Unevaluated Properties in requests', () => { + let app; + + before(async () => { + const apiSpec = join('test', 'openapi_3.1', 'resources', 'unevaluated_properties.yaml'); + app = await createApp( + { apiSpec, validateRequests: true }, + 3005, + (app) => app.use( + express + .Router() + .post(`/v1/entity`, (_req, res) => + res.status(204).json(), + ), + ) + ); + }); + + after(() => { + app.server.close(); + }); + + + it('should reject request body with unevaluated properties', async () => { + return request(app) + .post(`${app.basePath}/entity`) + .set('Content-Type', 'application/json') + .send({request: '123', additionalProperty: '321'}) + .expect(400); + }); + + it('should accept request body without unevaluated properties', async () => { + return request(app) + .post(`${app.basePath}/entity`) + .set('Content-Type', 'application/json') + .send({request: '123' }) + .expect(204); + }); + +}) \ No newline at end of file From e0e6a430f27a317027026339c6f29ec4961bcd94 Mon Sep 17 00:00:00 2001 From: carmine Date: Mon, 11 Nov 2024 18:47:09 -0500 Subject: [PATCH 49/60] update alpha 3.1 version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e5dcbe04..8273f86d 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ npm install express-openapi-validator # experimental OAS 3.1 in alpha (contributions welcome - see branch `oas-3.1` and pr-882 # please provide feedback on (issue-573) -npm install express-openapi-validator@6.0.0-alpha.4 +npm install express-openapi-validator@6.0.0-alpha.5 ``` ## Usage From 178da063ad5043364384559aea8941809aea477e Mon Sep 17 00:00:00 2001 From: Carmine DiMascio Date: Thu, 19 Dec 2024 16:03:58 -0500 Subject: [PATCH 50/60] fix: examples/4-eov-operations-babel/package.json & examples/4-eov-operations-babel/package-lock.json to reduce vulnerabilities (#1021) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-PATHTOREGEXP-8482416 Co-authored-by: snyk-bot --- .../4-eov-operations-babel/package-lock.json | 55 +++++++++---------- examples/4-eov-operations-babel/package.json | 2 +- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/examples/4-eov-operations-babel/package-lock.json b/examples/4-eov-operations-babel/package-lock.json index 953633df..bcf5ae0a 100644 --- a/examples/4-eov-operations-babel/package-lock.json +++ b/examples/4-eov-operations-babel/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "express": "^4.21.1", + "express": "^4.21.2", "express-openapi-validator": "^5.3.7", "morgan": "^1.10.0" }, @@ -2633,9 +2633,9 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -2657,7 +2657,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -2672,6 +2672,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express-openapi-validator": { @@ -2715,15 +2719,6 @@ "node": ">=16" } }, - "node_modules/express/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/express/node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -3705,9 +3700,9 @@ "dev": true }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, "node_modules/picocolors": { @@ -6282,9 +6277,9 @@ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -6305,7 +6300,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -6319,11 +6314,6 @@ "vary": "~1.1.2" }, "dependencies": { - "encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" - }, "on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -7062,9 +7052,9 @@ "dev": true }, "path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "picocolors": { "version": "1.0.0", @@ -7322,6 +7312,13 @@ "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" + }, + "dependencies": { + "encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + } } }, "set-function-length": { diff --git a/examples/4-eov-operations-babel/package.json b/examples/4-eov-operations-babel/package.json index b592f6e0..675a450a 100644 --- a/examples/4-eov-operations-babel/package.json +++ b/examples/4-eov-operations-babel/package.json @@ -14,7 +14,7 @@ "author": "", "license": "MIT", "dependencies": { - "express": "^4.21.1", + "express": "^4.21.2", "express-openapi-validator": "^5.3.7", "morgan": "^1.10.0" }, From b80f11ebf13f0fb149cf482229b9f4838544fd82 Mon Sep 17 00:00:00 2001 From: Carmine DiMascio Date: Thu, 19 Dec 2024 16:04:13 -0500 Subject: [PATCH 51/60] fix: package.json & package-lock.json to reduce vulnerabilities (#1017) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-PATHTOREGEXP-8482416 Co-authored-by: snyk-bot --- package-lock.json | 23 ++++++++++++++--------- package.json | 2 +- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index d6975c24..2cadc82b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,7 @@ "commitizen": "^4.2.5", "cookie-parser": "^1.4.6", "coveralls": "^3.1.1", - "express": "^4.21.1", + "express": "^4.21.2", "mocha": "^9.2.2", "morgan": "^1.10.0", "nodemon": "^3.0.1", @@ -2576,9 +2576,9 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dev": true, "license": "MIT", "dependencies": { @@ -2601,7 +2601,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -2616,6 +2616,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/encodeurl": { @@ -2640,10 +2644,11 @@ } }, "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", - "dev": true + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true, + "license": "MIT" }, "node_modules/express/node_modules/safe-buffer": { "version": "5.2.1", diff --git a/package.json b/package.json index b5d6bac3..03abe0c9 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "commitizen": "^4.2.5", "cookie-parser": "^1.4.6", "coveralls": "^3.1.1", - "express": "^4.21.1", + "express": "^4.21.2", "mocha": "^9.2.2", "morgan": "^1.10.0", "nodemon": "^3.0.1", From 1f1da169da33bc6a6d98829f5667752aed8cd235 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:04:26 -0500 Subject: [PATCH 52/60] chore(deps): bump path-to-regexp and express in /examples/1-standard (#1016) Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together. Updates `path-to-regexp` from 0.1.10 to 0.1.12 - [Release notes](https://github.com/pillarjs/path-to-regexp/releases) - [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md) - [Commits](https://github.com/pillarjs/path-to-regexp/compare/v0.1.10...v0.1.12) Updates `express` from 4.21.0 to 4.21.2 - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/4.21.2/History.md) - [Commits](https://github.com/expressjs/express/compare/4.21.0...4.21.2) --- updated-dependencies: - dependency-name: path-to-regexp dependency-type: indirect - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/1-standard/package-lock.json | 53 ++++++++++++++------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/examples/1-standard/package-lock.json b/examples/1-standard/package-lock.json index 46bf4269..38d842d7 100644 --- a/examples/1-standard/package-lock.json +++ b/examples/1-standard/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "express-openapi-validator": "^5.3.6" + "express-openapi-validator": "5.3.6" }, "devDependencies": { "nodemon": "^2.0.13" @@ -428,10 +428,9 @@ } }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "license": "MIT", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "peer": true, "engines": { "node": ">= 0.6" @@ -557,10 +556,9 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", - "license": "MIT", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "peer": true, "dependencies": { "accepts": "~1.3.8", @@ -568,7 +566,7 @@ "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -582,7 +580,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -597,6 +595,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express-openapi-validator": { @@ -1207,10 +1209,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", - "license": "MIT", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "peer": true }, "node_modules/picomatch": { @@ -1964,9 +1965,9 @@ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "peer": true }, "cookie-signature": { @@ -2052,9 +2053,9 @@ "peer": true }, "express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "peer": true, "requires": { "accepts": "~1.3.8", @@ -2062,7 +2063,7 @@ "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -2076,7 +2077,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -2514,9 +2515,9 @@ "peer": true }, "path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "peer": true }, "picomatch": { From 5152b7e91422ef53ad87d29ce2b284758ab94169 Mon Sep 17 00:00:00 2001 From: Carmine DiMascio Date: Thu, 19 Dec 2024 16:04:53 -0500 Subject: [PATCH 53/60] fix: upgrade express-openapi-validator from 5.3.7 to 5.3.8 (#1011) Snyk has created this PR to upgrade express-openapi-validator from 5.3.7 to 5.3.8. See this package in npm: express-openapi-validator See this project in Snyk: https://app.snyk.io/org/cdimascio/project/53639b22-8ff0-4bd5-97c3-ae30b20a20f4?utm_source=github&utm_medium=referral&page=upgrade-pr Co-authored-by: snyk-bot --- .../package-lock.json | 14 +++++++------- .../2-standard-multiple-api-specs/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/2-standard-multiple-api-specs/package-lock.json b/examples/2-standard-multiple-api-specs/package-lock.json index 2bc4f0ce..aa2e2b6e 100644 --- a/examples/2-standard-multiple-api-specs/package-lock.json +++ b/examples/2-standard-multiple-api-specs/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "express-openapi-validator": "^5.3.7", + "express-openapi-validator": "^5.3.8", "morgan": "^1.10.0" }, "devDependencies": { @@ -638,9 +638,9 @@ } }, "node_modules/express-openapi-validator": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/express-openapi-validator/-/express-openapi-validator-5.3.7.tgz", - "integrity": "sha512-AFlIXvICrPWJvWtrsfpL212kyvmyzThcZ1ASnnsRqmzxSo/3SV+J7M1oEsFYbqo7AYQPGtSBKy7eheGM6wPwag==", + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/express-openapi-validator/-/express-openapi-validator-5.3.8.tgz", + "integrity": "sha512-dADXgq8JwjCdWbL88Ts8ki5weeqOcdJnz6uJFJon9BqCJyj33J8nZgBAnptHLo8OQH8DrwjlJyDMdukncBUNtA==", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.7.0", @@ -2220,9 +2220,9 @@ } }, "express-openapi-validator": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/express-openapi-validator/-/express-openapi-validator-5.3.7.tgz", - "integrity": "sha512-AFlIXvICrPWJvWtrsfpL212kyvmyzThcZ1ASnnsRqmzxSo/3SV+J7M1oEsFYbqo7AYQPGtSBKy7eheGM6wPwag==", + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/express-openapi-validator/-/express-openapi-validator-5.3.8.tgz", + "integrity": "sha512-dADXgq8JwjCdWbL88Ts8ki5weeqOcdJnz6uJFJon9BqCJyj33J8nZgBAnptHLo8OQH8DrwjlJyDMdukncBUNtA==", "requires": { "@apidevtools/json-schema-ref-parser": "^11.7.0", "@types/multer": "^1.4.12", diff --git a/examples/2-standard-multiple-api-specs/package.json b/examples/2-standard-multiple-api-specs/package.json index 5cb79fa2..bd21cde2 100644 --- a/examples/2-standard-multiple-api-specs/package.json +++ b/examples/2-standard-multiple-api-specs/package.json @@ -12,7 +12,7 @@ "author": "", "license": "MIT", "dependencies": { - "express-openapi-validator": "^5.3.7", + "express-openapi-validator": "^5.3.8", "morgan": "^1.10.0" }, "devDependencies": { From eaf83ecb5a5376eaf80cf919fee683e805fabdd5 Mon Sep 17 00:00:00 2001 From: Carmine DiMascio Date: Thu, 19 Dec 2024 16:05:16 -0500 Subject: [PATCH 54/60] fix: upgrade express-openapi-validator from 5.3.6 to 5.3.7 (#1008) Snyk has created this PR to upgrade express-openapi-validator from 5.3.6 to 5.3.7. See this package in npm: express-openapi-validator See this project in Snyk: https://app.snyk.io/org/cdimascio/project/0ac9a5bd-9a7f-4c0e-bf8b-51d0bd4c4448?utm_source=github&utm_medium=referral&page=upgrade-pr Co-authored-by: snyk-bot --- examples/3-eov-operations/package-lock.json | 138 +++++++++++--------- examples/3-eov-operations/package.json | 2 +- 2 files changed, 78 insertions(+), 62 deletions(-) diff --git a/examples/3-eov-operations/package-lock.json b/examples/3-eov-operations/package-lock.json index d2df0b1c..b7acaf9c 100644 --- a/examples/3-eov-operations/package-lock.json +++ b/examples/3-eov-operations/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "express-openapi-validator": "^5.3.6", + "express-openapi-validator": "^5.3.7", "morgan": "^1.10.0" }, "devDependencies": { @@ -338,6 +338,7 @@ "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "license": "MIT", "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -347,6 +348,7 @@ "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -358,20 +360,22 @@ "dev": true }, "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", + "license": "MIT", "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", + "@types/express-serve-static-core": "^5.0.0", "@types/qs": "*", "@types/serve-static": "*" } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.3", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.3.tgz", - "integrity": "sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz", + "integrity": "sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==", + "license": "MIT", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -382,7 +386,8 @@ "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -393,38 +398,44 @@ "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" }, "node_modules/@types/multer": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz", - "integrity": "sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==", + "version": "1.4.12", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", + "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", + "license": "MIT", "dependencies": { "@types/express": "*" } }, "node_modules/@types/node": { - "version": "20.13.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.13.0.tgz", - "integrity": "sha512-FM6AOb3khNkNIXPnHFDYaHerSv8uN22C91z098AnGccVu+Pcdhi+pNUFDi0iLmPIsVE0JBD0KVS7mzUYt4nRzQ==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.8" } }, "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" + "version": "6.9.17", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", + "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", + "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "license": "MIT", "dependencies": { "@types/mime": "^1", "@types/node": "*" @@ -434,6 +445,7 @@ "version": "1.15.7", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "license": "MIT", "dependencies": { "@types/http-errors": "*", "@types/node": "*", @@ -1546,13 +1558,13 @@ } }, "node_modules/express-openapi-validator": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/express-openapi-validator/-/express-openapi-validator-5.3.6.tgz", - "integrity": "sha512-T6ApZs7+UZFY/XFmpplkNgjkwIZ/mTJpn1eE58tKDYlyOugVkMj8AQlSozi+Kn5QiZHnuepWwMcEZs8/2wxwSA==", + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/express-openapi-validator/-/express-openapi-validator-5.3.7.tgz", + "integrity": "sha512-AFlIXvICrPWJvWtrsfpL212kyvmyzThcZ1ASnnsRqmzxSo/3SV+J7M1oEsFYbqo7AYQPGtSBKy7eheGM6wPwag==", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.7.0", - "@types/multer": "^1.4.11", + "@types/multer": "^1.4.12", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^2.1.1", @@ -1563,7 +1575,7 @@ "media-typer": "^1.1.0", "multer": "^1.4.5-lts.1", "ono": "^7.1.3", - "path-to-regexp": "^6.3.0" + "path-to-regexp": "^8.1.0" }, "peerDependencies": { "express": "*" @@ -2486,10 +2498,13 @@ } }, "node_modules/path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "license": "MIT" + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } }, "node_modules/picomatch": { "version": "2.3.1", @@ -3043,9 +3058,10 @@ "dev": true }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" }, "node_modules/unpipe": { "version": "1.0.0", @@ -3373,20 +3389,20 @@ "dev": true }, "@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", "requires": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", + "@types/express-serve-static-core": "^5.0.0", "@types/qs": "*", "@types/serve-static": "*" } }, "@types/express-serve-static-core": { - "version": "4.19.3", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.3.tgz", - "integrity": "sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz", + "integrity": "sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==", "requires": { "@types/node": "*", "@types/qs": "*", @@ -3410,25 +3426,25 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "@types/multer": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz", - "integrity": "sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==", + "version": "1.4.12", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", + "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", "requires": { "@types/express": "*" } }, "@types/node": { - "version": "20.13.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.13.0.tgz", - "integrity": "sha512-FM6AOb3khNkNIXPnHFDYaHerSv8uN22C91z098AnGccVu+Pcdhi+pNUFDi0iLmPIsVE0JBD0KVS7mzUYt4nRzQ==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "requires": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.8" } }, "@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" + "version": "6.9.17", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", + "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==" }, "@types/range-parser": { "version": "1.2.7", @@ -4242,12 +4258,12 @@ } }, "express-openapi-validator": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/express-openapi-validator/-/express-openapi-validator-5.3.6.tgz", - "integrity": "sha512-T6ApZs7+UZFY/XFmpplkNgjkwIZ/mTJpn1eE58tKDYlyOugVkMj8AQlSozi+Kn5QiZHnuepWwMcEZs8/2wxwSA==", + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/express-openapi-validator/-/express-openapi-validator-5.3.7.tgz", + "integrity": "sha512-AFlIXvICrPWJvWtrsfpL212kyvmyzThcZ1ASnnsRqmzxSo/3SV+J7M1oEsFYbqo7AYQPGtSBKy7eheGM6wPwag==", "requires": { "@apidevtools/json-schema-ref-parser": "^11.7.0", - "@types/multer": "^1.4.11", + "@types/multer": "^1.4.12", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^2.1.1", @@ -4258,7 +4274,7 @@ "media-typer": "^1.1.0", "multer": "^1.4.5-lts.1", "ono": "^7.1.3", - "path-to-regexp": "^6.3.0" + "path-to-regexp": "^8.1.0" } }, "fast-deep-equal": { @@ -4909,9 +4925,9 @@ "dev": true }, "path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==" + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==" }, "picomatch": { "version": "2.3.1", @@ -5303,9 +5319,9 @@ "dev": true }, "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "unpipe": { "version": "1.0.0", diff --git a/examples/3-eov-operations/package.json b/examples/3-eov-operations/package.json index ce233f36..325cc190 100644 --- a/examples/3-eov-operations/package.json +++ b/examples/3-eov-operations/package.json @@ -12,7 +12,7 @@ "author": "", "license": "MIT", "dependencies": { - "express-openapi-validator": "^5.3.6", + "express-openapi-validator": "^5.3.7", "morgan": "^1.10.0" }, "devDependencies": { From bc7ed2aa47f9b5361b78963086814996c860d106 Mon Sep 17 00:00:00 2001 From: Carmine DiMascio Date: Thu, 19 Dec 2024 16:05:36 -0500 Subject: [PATCH 55/60] fix: upgrade @apidevtools/json-schema-ref-parser from 11.7.0 to 11.7.2 (#1006) Snyk has created this PR to upgrade @apidevtools/json-schema-ref-parser from 11.7.0 to 11.7.2. See this package in npm: @apidevtools/json-schema-ref-parser See this project in Snyk: https://app.snyk.io/org/cdimascio/project/f63fb44e-f154-45ba-b1f0-20d49ea578ce?utm_source=github&utm_medium=referral&page=upgrade-pr Co-authored-by: snyk-bot --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2cadc82b..da6bf178 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "6.0.0-alpha.5", "license": "MIT", "dependencies": { - "@apidevtools/json-schema-ref-parser": "^11.7.0", + "@apidevtools/json-schema-ref-parser": "^11.7.2", "@types/multer": "^1.4.12", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", @@ -52,9 +52,9 @@ } }, "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.0.tgz", - "integrity": "sha512-pRrmXMCwnmrkS3MLgAIW5dXRzeTv6GLjkjb4HmxNnvAKXN1Nfzp4KmGADBQvlVUcqi+a5D+hfGDLLnd5NnYxog==", + "version": "11.7.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.2.tgz", + "integrity": "sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==", "license": "MIT", "dependencies": { "@jsdevtools/ono": "^7.1.3", diff --git a/package.json b/package.json index 03abe0c9..305e265f 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "author": "Carmine DiMascio ", "license": "MIT", "dependencies": { - "@apidevtools/json-schema-ref-parser": "^11.7.0", + "@apidevtools/json-schema-ref-parser": "^11.7.2", "@types/multer": "^1.4.12", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", From 35424c92e6d730d8eed1a7bbd28b4dd3bcc10f4e Mon Sep 17 00:00:00 2001 From: carmine Date: Sat, 21 Dec 2024 21:45:47 -0500 Subject: [PATCH 56/60] adds standard example for oas-3.1 --- examples/1-standard-oas-3.1/README.md | 28 + examples/1-standard-oas-3.1/api.yaml | 121 ++++ examples/1-standard-oas-3.1/app.js | 91 +++ examples/1-standard-oas-3.1/package-lock.json | 663 ++++++++++++++++++ examples/1-standard-oas-3.1/package.json | 20 + examples/1-standard-oas-3.1/services/index.js | 44 ++ 6 files changed, 967 insertions(+) create mode 100644 examples/1-standard-oas-3.1/README.md create mode 100644 examples/1-standard-oas-3.1/api.yaml create mode 100644 examples/1-standard-oas-3.1/app.js create mode 100644 examples/1-standard-oas-3.1/package-lock.json create mode 100644 examples/1-standard-oas-3.1/package.json create mode 100644 examples/1-standard-oas-3.1/services/index.js diff --git a/examples/1-standard-oas-3.1/README.md b/examples/1-standard-oas-3.1/README.md new file mode 100644 index 00000000..591f452f --- /dev/null +++ b/examples/1-standard-oas-3.1/README.md @@ -0,0 +1,28 @@ +# example + +simple example using express-openapi-validator + +## Install + +```shell +npm run deps && npm i +``` + +## Run + +From this `1-standard` directory, run: + +```shell +npm start +``` + +## Try + +```shell +## call ping +curl http://localhost:3000/v1/ping + +## call pets +## the call below should return 400 since it requires additional parameters +curl http://localhost:3000/v1/pets +``` diff --git a/examples/1-standard-oas-3.1/api.yaml b/examples/1-standard-oas-3.1/api.yaml new file mode 100644 index 00000000..796ba7d6 --- /dev/null +++ b/examples/1-standard-oas-3.1/api.yaml @@ -0,0 +1,121 @@ +openapi: 3.1.0 +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT + url: https://opensource.org/licenses/MIT +servers: + - url: http://petstore.swagger.io/v1 +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + responses: + '200': + description: A paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Pets' + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + post: + summary: Create a pet + operationId: createPets + tags: + - pets + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + responses: + '201': + description: Null response + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /pets/{petId}: + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +components: + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Pets: + type: array + items: + $ref: '#/components/schemas/Pet' + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/examples/1-standard-oas-3.1/app.js b/examples/1-standard-oas-3.1/app.js new file mode 100644 index 00000000..4683cb05 --- /dev/null +++ b/examples/1-standard-oas-3.1/app.js @@ -0,0 +1,91 @@ +const express = require('express'); +const path = require('path'); +const http = require('http'); +const cookieParser = require('cookie-parser'); // Add if using cookie auth +const { Pets } = require('./services'); +const OpenApiValidator = require('express-openapi-validator'); + +const port = 3000; +const app = express(); +const apiSpec = path.join(__dirname, 'api.yaml'); + +// 1. Install bodyParsers for the request types your API will support +app.use(express.urlencoded({ extended: false })); +app.use(express.text()); +app.use(express.json()); +app.use(cookieParser()); // Add if using cookie auth enables req.cookies + +// Optionally serve the API spec +app.use('/spec', express.static(apiSpec)); + +// 2. Install the OpenApiValidator on your express app +app.use( + OpenApiValidator.middleware({ + apiSpec, + validateRequests: { + coerceTypes: true + }, + validateResponses: true, // default false + }), +); + +const pets = new Pets(); +// 3. Add routes +app.get('/v1/ping', function (req, res, next) { + res.send('pong'); +}); +app.get('/v1/pets', function (req, res, next) { + res.json(pets.findAll(req.query)); +}); + +app.post('/v1/pets', function (req, res, next) { + res.status(201).json(pets.create(req.body)); +}); + +app.delete('/v1/pets/:id', function (req, res, next) { + res.json(pets.delete(req.params.id)); +}); + +app.get('/v1/pets/:id', function (req, res, next) { + console.log(req.params.id) + const pet = pets.findById(req.params.id); + console.log(pet); + return pet + ? res.json({ ...pet }) + : res.status(404).json({ message: 'not found', code: 23 }); +}); + +// 3a. Add a route upload file(s) +app.post('/v1/pets/:id/photos', function (req, res, next) { + // DO something with the file + // files are found in req.files + // non file multipar params are in req.body['my-param'] + console.log(req.files); + + res.json({ + files_metadata: req.files.map((f) => ({ + originalname: f.originalname, + encoding: f.encoding, + mimetype: f.mimetype, + // Buffer of file conents + // buffer: f.buffer, + })), + }); +}); + +// 4. Create a custom error handler +app.use((err, req, res, next) => { + // format errors + // console.log(err) + res.status(err.status || 500).json({ + message: err.message, + errors: err.errors, + code: err.status ?? 500, + }); +}); + + +http.createServer(app).listen(port); +console.log(`Listening on port ${port}`); + +module.exports = app; diff --git a/examples/1-standard-oas-3.1/package-lock.json b/examples/1-standard-oas-3.1/package-lock.json new file mode 100644 index 00000000..75278363 --- /dev/null +++ b/examples/1-standard-oas-3.1/package-lock.json @@ -0,0 +1,663 @@ +{ + "name": "example", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "example", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "express-openapi-validator": "file:../../dist/index.js" + }, + "devDependencies": { + "nodemon": "^2.0.22" + } + }, + "../../dist/index.js": {}, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/express-openapi-validator": { + "resolved": "../../dist/index.js", + "link": true + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nodemon": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dev": true, + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + } + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "express-openapi-validator": { + "version": "file:../../dist/index.js" + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "nodemon": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", + "dev": true, + "requires": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true + }, + "simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dev": true, + "requires": { + "semver": "~7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "requires": { + "nopt": "~1.0.10" + } + }, + "undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + } + } +} diff --git a/examples/1-standard-oas-3.1/package.json b/examples/1-standard-oas-3.1/package.json new file mode 100644 index 00000000..bbc8f723 --- /dev/null +++ b/examples/1-standard-oas-3.1/package.json @@ -0,0 +1,20 @@ +{ + "name": "example", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node app.js", + "deps": "cd ../../ && npm i && npm run compile", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": { + "express-openapi-validator": "file:../../dist/index.js" + }, + "devDependencies": { + "nodemon": "^2.0.22" + } +} diff --git a/examples/1-standard-oas-3.1/services/index.js b/examples/1-standard-oas-3.1/services/index.js new file mode 100644 index 00000000..952dc10a --- /dev/null +++ b/examples/1-standard-oas-3.1/services/index.js @@ -0,0 +1,44 @@ +let data = [ + { + id: 1, + name: 'sparky', + type: 'dog', + tags: ['sweet'], + }, + { + id: 2, + name: 'buzz', + type: 'cat', + tags: ['purrfect'], + }, + { + id: 3, + name: 'max', + type: 'dog', + tags: [], + }, +]; + +module.exports.Pets = class { + constructor() { + this.id = 4; + } + findAll({ type, limit }) { + return data.filter(d => !type || d.type === type).slice(0, limit); + } + + findById(id) { + console.log(id, data, data.filter(p => p.id === id), typeof id); + return data.filter(p => p.id === id)[0]; + } + + create(pet) { + const npet = { id: this.id++, ...pet }; + data.push(npet); + return npet; + } + + delete(id) { + return data.filter(e => e.id !== id); + } +}; From 3050e7f1486e3856cccd98c8c9fde73a818ddb80 Mon Sep 17 00:00:00 2001 From: carmine Date: Mon, 23 Dec 2024 13:35:45 -0500 Subject: [PATCH 57/60] v6.0.0.alpha.6 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index da6bf178..ed31f423 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "express-openapi-validator", - "version": "6.0.0-alpha.5", + "version": "6.0.0-alpha.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "express-openapi-validator", - "version": "6.0.0-alpha.5", + "version": "6.0.0-alpha.6", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.7.2", diff --git a/package.json b/package.json index 305e265f..146f348b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "express-openapi-validator", - "version": "6.0.0-alpha.5", + "version": "6.0.0-alpha.6", "description": "Automatically validate API requests and responses with OpenAPI 3 and Express.", "main": "dist/index.js", "scripts": { From 9b4e1602ac6b36c5cd813916ebfc22fb6a649ad3 Mon Sep 17 00:00:00 2001 From: carmine Date: Mon, 23 Dec 2024 20:27:34 -0500 Subject: [PATCH 58/60] update oas3.1 example --- examples/1-standard-oas-3.1/README.md | 144 ++++++++++++++++++++++++++ examples/1-standard-oas-3.1/api.yaml | 44 +++++++- examples/1-standard-oas-3.1/app.js | 5 - test.txt | 1 + 4 files changed, 185 insertions(+), 9 deletions(-) create mode 100644 test.txt diff --git a/examples/1-standard-oas-3.1/README.md b/examples/1-standard-oas-3.1/README.md index 591f452f..27e6e48b 100644 --- a/examples/1-standard-oas-3.1/README.md +++ b/examples/1-standard-oas-3.1/README.md @@ -26,3 +26,147 @@ curl http://localhost:3000/v1/ping ## the call below should return 400 since it requires additional parameters curl http://localhost:3000/v1/pets ``` + +## Text examples + +### GET /pets + +success +```shell +curl -s 'localhost:3000/v1/pets?limit=5' |jq +[ + { + "id": 1, + "name": "sparky", + "type": "dog", + "tags": [ + "sweet" + ] + }, + { + "id": 2, + "name": "buzz", + "type": "cat", + "tags": [ + "purrfect" + ] + }, + { + "id": 3, + "name": "max", + "type": "dog", + "tags": [] + } +] +``` + +error +```shell + curl -s 'localhost:3000/v1/pets' |jq +{ + "message": "request/query must have required property 'limit'", + "errors": [ + { + "path": "/query/limit", + "message": "must have required property 'limit'", + "errorCode": "required.openapi.validation" + } + ], + "code": 400 +} +``` + +### POST /pets + +success +```shell +curl -s -XPOST 'localhost:3000/v1/pets' -d '{"id": 1, "name": "jobe"}' -H 'Content-type: application/json'|jq +{ + "id": 1, + "name": "jobe" +} +``` + +error +```shell +curl -s -XPOST 'localhost:3000/v1/pets' -d '{"id": "sdfsf", "name": "jobe"}' -H 'Content-type: application/json'|jq +{ + "message": "request/body/id must be integer", + "errors": [ + { + "path": "/body/id", + "message": "must be integer", + "errorCode": "type.openapi.validation" + } + ], + "code": 400 +} +``` + +### GET /pets/:id + +success +```shell +curl -s 'localhost:3000/v1/pets/1' |jq +{ + "id": 1, + "name": "sparky", + "type": "dog", + "tags": [ + "sweet" + ] +} +``` + +error: bad id type +```shell +curl -s 'localhost:3000/v1/pets/lkl' |jq +{ + "message": "request/params/petId must be number", + "errors": [ + { + "path": "/params/petId", + "message": "must be number", + "errorCode": "type.openapi.validation" + } + ], + "code": 400 +} +``` + + +### /v1/pets/1/photos + +success +```shell +curl -XPOST 'localhost:3000/v1/pets/1/photos' -H 'Content-type: multipart/form-data' -F 'file=@test.txt'|jq + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 304 100 90 100 214 25996 61813 --:--:-- --:--:-- --:--:-- 98k +{ + "files_metadata": [ + { + "originalname": "test.txt", + "encoding": "7bit", + "mimetype": "text/plain" + } + ] +} +``` + +error: no file +```shell + curl -s -XPOST 'localhost:3000/v1/pets/1/photos' -H 'Content-type: multipart/form-data' |jq +{ + "message": "request/body must have required property 'file'", + "errors": [ + { + "path": "/body/file", + "message": "must have required property 'file'", + "errorCode": "required.openapi.validation" + } + ], + "code": 400 +} +``` + diff --git a/examples/1-standard-oas-3.1/api.yaml b/examples/1-standard-oas-3.1/api.yaml index 796ba7d6..b236c4c0 100644 --- a/examples/1-standard-oas-3.1/api.yaml +++ b/examples/1-standard-oas-3.1/api.yaml @@ -18,7 +18,7 @@ paths: - name: limit in: query description: How many items to return at one time (max 100) - required: false + required: true schema: type: integer format: int32 @@ -63,19 +63,19 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - /pets/{petId}: + /pets/{id}: get: summary: Info for a specific pet operationId: showPetById tags: - pets parameters: - - name: petId + - name: id in: path required: true description: The id of the pet to retrieve schema: - type: string + type: number responses: '200': description: Expected response to a valid request @@ -89,6 +89,42 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + + /pets/{id}/photos: + post: + summary: upload a photo of the pet + operationId: uploadPetPhoto + parameters: + - name: id + in: path + description: ID of pet to fetch + required: true + schema: + type: integer + format: int64 + requestBody: + content: + multipart/form-data: + schema: + type: object + required: + - file + properties: + file: + description: The photo + type: string + format: binary + required: true + responses: + '200': + description: Null response + content: + application/json: + schema: + type: object + properties: + success: + type: boolean components: schemas: Pet: diff --git a/examples/1-standard-oas-3.1/app.js b/examples/1-standard-oas-3.1/app.js index 4683cb05..8ee5d613 100644 --- a/examples/1-standard-oas-3.1/app.js +++ b/examples/1-standard-oas-3.1/app.js @@ -22,9 +22,6 @@ app.use('/spec', express.static(apiSpec)); app.use( OpenApiValidator.middleware({ apiSpec, - validateRequests: { - coerceTypes: true - }, validateResponses: true, // default false }), ); @@ -47,9 +44,7 @@ app.delete('/v1/pets/:id', function (req, res, next) { }); app.get('/v1/pets/:id', function (req, res, next) { - console.log(req.params.id) const pet = pets.findById(req.params.id); - console.log(pet); return pet ? res.json({ ...pet }) : res.status(404).json({ message: 'not found', code: 23 }); diff --git a/test.txt b/test.txt new file mode 100644 index 00000000..9c9a6556 --- /dev/null +++ b/test.txt @@ -0,0 +1 @@ +My file conents From f7167ff5dad292590b38c1f2879e66e354cf30e6 Mon Sep 17 00:00:00 2001 From: carmine Date: Tue, 24 Dec 2024 09:16:26 -0500 Subject: [PATCH 59/60] v5.4.0 - adds initial oas3.1 support --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ed31f423..c40c07b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "express-openapi-validator", - "version": "6.0.0-alpha.6", + "version": "5.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "express-openapi-validator", - "version": "6.0.0-alpha.6", + "version": "5.4.0", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.7.2", diff --git a/package.json b/package.json index 146f348b..0fc6c221 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "express-openapi-validator", - "version": "6.0.0-alpha.6", + "version": "5.4.0", "description": "Automatically validate API requests and responses with OpenAPI 3 and Express.", "main": "dist/index.js", "scripts": { From f33f2c64a9df69c795f94643bb84e7f7d408b031 Mon Sep 17 00:00:00 2001 From: carmine Date: Tue, 24 Dec 2024 09:32:02 -0500 Subject: [PATCH 60/60] updates readme for oas3.1 --- README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 8273f86d..cd230aae 100644 --- a/README.md +++ b/README.md @@ -29,16 +29,12 @@ [NestJS](https://github.com/cdimascio/express-openapi-validator/tree/master/examples/9-nestjs) [Koa](https://github.com/cdimascio/express-openapi-validator/tree/lerna-fastify/packages/koa-openapi-validator) and [Fastify](https://github.com/cdimascio/express-openapi-validator/tree/lerna-fastify/packages/fastify-openapi-validator) now available! 🚀 -[OAS 3.1 (experimental)](https://github.com/cdimascio/express-openapi-validator/pull/882) +[OAS 3.1 (beta)](https://github.com/cdimascio/express-openapi-validator/pull/882) is available in v5.4.0! ## Install ```shell npm install express-openapi-validator - -# experimental OAS 3.1 in alpha (contributions welcome - see branch `oas-3.1` and pr-882 -# please provide feedback on (issue-573) -npm install express-openapi-validator@6.0.0-alpha.5 ``` ## Usage