From c034c9eef390009249a9b7b97af90d0d49c3db87 Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Mon, 19 May 2025 18:22:49 +0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/package.json | 5 +- .../api-v1/deployments/create-deployment.ts | 6 +- apps/api/src/db/schema/schemas.ts | 49 +++-- .../validate-deployment-origin-adapter.ts | 33 +++- apps/api/src/lib/types.ts | 2 + apps/api/src/lib/validate-openapi-spec.ts | 135 ++++++++++++++ pnpm-lock.yaml | 176 ++++++++++++++++-- 7 files changed, 361 insertions(+), 45 deletions(-) create mode 100644 apps/api/src/lib/validate-openapi-spec.ts diff --git a/apps/api/package.json b/apps/api/package.json index 1e25e155..982c0023 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -44,6 +44,7 @@ "@hono/zod-openapi": "^0.19.6", "@hono/zod-validator": "^0.5.0", "@paralleldrive/cuid2": "^2.2.2", + "@redocly/openapi-core": "^1.34.3", "@sentry/node": "^9.19.0", "bcryptjs": "^3.0.2", "eventid": "^2.0.1", @@ -52,6 +53,7 @@ "hono": "^4.7.9", "jsonwebtoken": "^9.0.2", "p-all": "^5.0.0", + "parse-json": "^8.3.0", "postgres": "^3.4.5", "restore-cursor": "catalog:", "semver": "^7.7.2", @@ -64,6 +66,7 @@ "@types/jsonwebtoken": "^9.0.9", "@types/semver": "^7.7.0", "drizzle-kit": "^0.31.1", - "drizzle-orm": "^0.43.1" + "drizzle-orm": "^0.43.1", + "openapi-typescript": "^7.8.0" } } diff --git a/apps/api/src/api-v1/deployments/create-deployment.ts b/apps/api/src/api-v1/deployments/create-deployment.ts index 6321325a..6e72ed99 100644 --- a/apps/api/src/api-v1/deployments/create-deployment.ts +++ b/apps/api/src/api-v1/deployments/create-deployment.ts @@ -59,6 +59,7 @@ export function registerV1DeploymentsCreateDeployment( const { publish } = c.req.valid('query') const body = c.req.valid('json') const teamMember = c.get('teamMember') + const logger = c.get('logger') const { projectId } = body // validatePricingPlans(ctx, pricingPlans) @@ -86,7 +87,7 @@ export function registerV1DeploymentsCreateDeployment( assert( version, 400, - `Deployment version is required to publish deployment "${deploymentId}"` + `Deployment "version" field is required to publish deployment "${deploymentId}"` ) } @@ -101,7 +102,8 @@ export function registerV1DeploymentsCreateDeployment( // Validate OpenAPI originUrl and originAdapter await validateDeploymentOriginAdapter({ ...pick(body, 'originUrl', 'originAdapter'), - deploymentId + deploymentId, + logger }) let [[deployment]] = await db.transaction(async (tx) => { diff --git a/apps/api/src/db/schema/schemas.ts b/apps/api/src/db/schema/schemas.ts index 8fae7f50..9aa1a8d4 100644 --- a/apps/api/src/db/schema/schemas.ts +++ b/apps/api/src/db/schema/schemas.ts @@ -1,4 +1,5 @@ import { z } from '@hono/zod-openapi' +import parseJson from 'parse-json' export const authProviderTypeSchema = z .enum(['github', 'google', 'spotify', 'twitter', 'linkedin', 'stripe']) @@ -452,19 +453,6 @@ export const commonDeploymentOriginAdapterSchema = z.object({ // internalType: deploymentOriginAdapterInternalTypeSchema.optional() }) -const jsonLiteralSchema = z.union([ - z.string(), - z.number(), - z.boolean(), - z.null() -]) -type JsonLiteral = z.infer -type Json = JsonLiteral | { [key: string]: Json } | Json[] -const jsonSchema: z.ZodType = z.lazy(() => - z.union([jsonLiteralSchema, z.array(jsonSchema), z.record(jsonSchema)]) -) -const jsonObjectSchema = z.record(jsonSchema) - // TODO: add future support for: // - external mcp // - internal docker @@ -476,11 +464,36 @@ export const deploymentOriginAdapterSchema = z z .object({ type: z.literal('openapi'), - version: z.enum(['3.0', '3.1']), - // TODO: Make sure origin API servers are hidden in this embedded OpenAPI spec - spec: jsonObjectSchema.describe( - 'JSON OpenAPI spec for the origin API server.' - ) + // NOTE: The origin API servers should be hidden in the embedded + // OpenAPI spec, because clients should only be aware of the upstream + // Agentic API gateway. + spec: z + .string() + .refine( + (spec) => { + try { + parseJson(spec) + } catch { + return false + } + }, + (data) => { + try { + parseJson(data) + } catch (err: any) { + return { + message: `Invalid OpenAPI spec: ${err.message}` + } + } + + return { + message: 'Invalid OpenAPI spec' + } + } + ) + .describe( + 'JSON stringified OpenAPI spec describing the origin API server.' + ) }) .merge(commonDeploymentOriginAdapterSchema), diff --git a/apps/api/src/lib/deployments/validate-deployment-origin-adapter.ts b/apps/api/src/lib/deployments/validate-deployment-origin-adapter.ts index ffa0f20e..6bede714 100644 --- a/apps/api/src/lib/deployments/validate-deployment-origin-adapter.ts +++ b/apps/api/src/lib/deployments/validate-deployment-origin-adapter.ts @@ -1,14 +1,23 @@ import type { DeploymentOriginAdapter } from '@/db/schema' +import type { Logger } from '@/lib/logger' import { assert } from '@/lib/utils' +import { validateOpenAPISpec } from '@/lib/validate-openapi-spec' +/** + * Validates and normalizes the origin adapter config for a deployment. + * + * NOTE: This method may mutate `originAdapter.spec`. + */ export async function validateDeploymentOriginAdapter({ deploymentId, originUrl, - originAdapter + originAdapter, + logger }: { deploymentId: string originUrl: string originAdapter: DeploymentOriginAdapter + logger: Logger }): Promise { assert( originUrl, @@ -17,17 +26,29 @@ export async function validateDeploymentOriginAdapter({ ) if (originAdapter.type === 'openapi') { - // TODO: Validate OpenAPI spec assert( originAdapter.spec, 400, - `OpenAPI spec is required for deployment "${deploymentId}"` + `OpenAPI spec is required for deployment "${deploymentId}" with origin adapter type set to "openapi"` ) - // TODO: Validate OpenAPI spec version is the same as `originAdapter.version` + // Validate and normalize the OpenAPI spec + const openapiSpec = await validateOpenAPISpec(originAdapter.spec, { + logger + }) - // TODO: Remove origin servers from the OpenAPI spec and if they exist, ensure - // that `originUrl` matches. + // Remove origin servers from the OpenAPI spec. + // TODO: Ensure that `originUrl` matches any origin servers in the openapi spec? + delete openapiSpec.servers + + // TODO: Additional, agentic-specific validation of the OpenAPI spec's + // operations to ensure they are valid AI functions. + + // TODO: Simplify OpenAPI spec by removing any query params and headers + // specific to the Agentic API gateway. + + // Update the openapi spec with the normalized version + originAdapter.spec = JSON.stringify(openapiSpec) } else { assert( originAdapter.type === 'raw', diff --git a/apps/api/src/lib/types.ts b/apps/api/src/lib/types.ts index 0cd775a0..9594156c 100644 --- a/apps/api/src/lib/types.ts +++ b/apps/api/src/lib/types.ts @@ -5,6 +5,8 @@ import type { RawTeamMember, RawUser } from '@/db' import type { Env } from './env' import type { Logger } from './logger' +export type { OpenAPI3 as LooseOpenAPI3Spec } from 'openapi-typescript' + export type Environment = Env['NODE_ENV'] export type Service = 'api' diff --git a/apps/api/src/lib/validate-openapi-spec.ts b/apps/api/src/lib/validate-openapi-spec.ts new file mode 100644 index 00000000..3c54f909 --- /dev/null +++ b/apps/api/src/lib/validate-openapi-spec.ts @@ -0,0 +1,135 @@ +import { + BaseResolver, + bundle, + type Config as RedoclyConfig, + createConfig, + type Document, + lintDocument, + type NormalizedProblem, + Source +} from '@redocly/openapi-core' +import parseJson from 'parse-json' + +import type { Logger } from './logger' +import type { LooseOpenAPI3Spec } from './types' + +function _processProblems( + problems: NormalizedProblem[], + { + logger, + silent + }: { + logger: Logger + silent: boolean + } +) { + if (problems.length) { + let errorMessage: string | undefined + + for (const problem of problems) { + const problemLocation = problem.location?.[0]?.pointer + const problemMessage = problemLocation + ? `${problem.message} at ${problemLocation}` + : problem.message + + if (problem.severity === 'error') { + errorMessage = problemMessage + logger.error('openapi spec error', problemMessage) + } else if (!silent) { + logger.warn('openapi spec warning', problemMessage) + } + } + + if (errorMessage) { + throw new Error(errorMessage) + } + } +} + +/** + * Validates an OpenAPI spec and bundles it into a single, normalized schema. + * + * The input `source` should be a JSON stringified OpenAPI spec (3.0 or 3.1). + */ +export async function validateOpenAPISpec( + source: string, + { + logger, + redoclyConfig, + silent = false + }: { + logger: Logger + redoclyConfig?: RedoclyConfig + silent?: boolean + } +): Promise { + if (!redoclyConfig) { + redoclyConfig = await createConfig( + { + rules: { + // throw error on duplicate operationIds + 'operation-operationId-unique': { severity: 'error' } + } + }, + { extends: ['minimal'] } + ) + } + + logger.debug('Parsing openapi spec') + const resolver = new BaseResolver(redoclyConfig.resolve) + + let parsed: any + try { + parsed = parseJson(source) + } catch (err: any) { + throw new Error(`Invalid OpenAPI spec: ${err.message}`) + } + + const document: Document = { + source: new Source('', source, 'application/json'), + parsed + } + logger.debug('Parsed openapi spec') + + // Check for OpenAPI 3 or greater + const openapiVersion = Number.parseFloat(document.parsed.openapi) + if ( + document.parsed.swagger || + !document.parsed.openapi || + Number.isNaN(openapiVersion) || + openapiVersion < 3 || + openapiVersion >= 4 + ) { + if (document.parsed.swagger) { + throw new Error( + 'Unsupported Swagger version: 2.x. Use OpenAPI 3.x instead.' + ) + } + + if (document.parsed.openapi || openapiVersion < 3 || openapiVersion >= 4) { + throw new Error(`Unsupported OpenAPI version: ${document.parsed.openapi}`) + } + + throw new Error('Unsupported schema format, expected `openapi: 3.x`') + } + + logger.debug('>>> Linting openapi spec') + const problems = await lintDocument({ + document, + config: redoclyConfig.styleguide, + externalRefResolver: resolver + }) + _processProblems(problems, { silent, logger }) + logger.debug('<<< Linting openapi spec') + + logger.debug('>>> Bundling openapi spec') + const bundled = await bundle({ + config: redoclyConfig, + dereference: false, + doc: document + }) + _processProblems(bundled.problems, { silent, logger }) + logger.debug('<<< Bundling openapi spec') + + return bundled.bundle.parsed +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 190cfe45..41ae3c73 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -149,6 +149,9 @@ importers: '@paralleldrive/cuid2': specifier: ^2.2.2 version: 2.2.2 + '@redocly/openapi-core': + specifier: ^1.34.3 + version: 1.34.3(supports-color@10.0.0) '@sentry/node': specifier: ^9.19.0 version: 9.19.0 @@ -173,6 +176,9 @@ importers: p-all: specifier: ^5.0.0 version: 5.0.0 + parse-json: + specifier: ^8.3.0 + version: 8.3.0 postgres: specifier: ^3.4.5 version: 3.4.5 @@ -207,6 +213,9 @@ importers: drizzle-orm: specifier: ^0.43.1 version: 0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(postgres@3.4.5) + openapi-typescript: + specifier: ^7.8.0 + version: 7.8.0(typescript@5.8.3) packages/validators: dependencies: @@ -965,6 +974,16 @@ packages: peerDependencies: '@opentelemetry/api': ^1.8 + '@redocly/ajv@8.11.2': + resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==} + + '@redocly/config@0.22.2': + resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==} + + '@redocly/openapi-core@1.34.3': + resolution: {integrity: sha512-3arRdUp1fNx55itnjKiUhO6t4Mf91TsrTIYINDNLAZPS0TPd5YpiXRctwjel0qqWoOOhjA34cZ3m4dksLDFUYg==} + engines: {node: '>=18.17.0', npm: '>=9.5.0'} + '@rollup/rollup-android-arm-eabi@4.40.0': resolution: {integrity: sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==} cpu: [arm] @@ -1256,9 +1275,17 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + ansi-escapes@7.0.0: resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} engines: {node: '>=18'} @@ -1425,6 +1452,9 @@ packages: resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -1459,6 +1489,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + colorette@1.4.0: + resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} + colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} @@ -2141,6 +2174,10 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -2343,6 +2380,10 @@ packages: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} + js-levenshtein@1.1.6: + resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} + engines: {node: '>=0.10.0'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2370,6 +2411,9 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -2524,6 +2568,10 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -2623,6 +2671,12 @@ packages: resolution: {integrity: sha512-M7CJbmv7UCopc0neRKdzfoGWaVZC+xC1925GitKH9EAqYFzX9//25Q7oX4+jw0tiCCj+t5l6VZh8UPH23NZkMA==} hasBin: true + openapi-typescript@7.8.0: + resolution: {integrity: sha512-1EeVWmDzi16A+siQlo/SwSGIT7HwaFAVjvMA7/jG5HMLSnrUOzPL7uSTRZZa4v/LCRxHTApHKtNY6glApEoiUQ==} + hasBin: true + peerDependencies: + typescript: ^5.x + openapi3-ts@4.4.0: resolution: {integrity: sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==} @@ -2857,6 +2911,10 @@ packages: resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} hasBin: true + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + require-in-the-middle@7.5.2: resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==} engines: {node: '>=8.6.0'} @@ -3129,6 +3187,10 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true + supports-color@10.0.0: + resolution: {integrity: sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==} + engines: {node: '>=18'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -3329,6 +3391,9 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + uri-js-replace@1.0.1: + resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==} + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -3488,11 +3553,18 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + yaml-ast-parser@0.0.43: + resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==} + yaml@2.7.1: resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} engines: {node: '>= 14'} hasBin: true + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -3693,7 +3765,7 @@ snapshots: '@eslint/config-array@0.20.0': dependencies: '@eslint/object-schema': 2.1.6 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -3707,7 +3779,7 @@ snapshots: '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) espree: 10.3.0 globals: 14.0.0 ignore: 5.3.2 @@ -4122,6 +4194,29 @@ snapshots: transitivePeerDependencies: - supports-color + '@redocly/ajv@8.11.2': + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js-replace: 1.0.1 + + '@redocly/config@0.22.2': {} + + '@redocly/openapi-core@1.34.3(supports-color@10.0.0)': + dependencies: + '@redocly/ajv': 8.11.2 + '@redocly/config': 0.22.2 + colorette: 1.4.0 + https-proxy-agent: 7.0.6(supports-color@10.0.0) + js-levenshtein: 1.1.6 + js-yaml: 4.1.0 + minimatch: 5.1.6 + pluralize: 8.0.0 + yaml-ast-parser: 0.0.43 + transitivePeerDependencies: + - supports-color + '@rollup/rollup-android-arm-eabi@4.40.0': optional: true @@ -4318,7 +4413,7 @@ snapshots: '@typescript-eslint/types': 8.31.0 '@typescript-eslint/typescript-estree': 8.31.0(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.31.0 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) eslint: 9.26.0 typescript: 5.8.3 transitivePeerDependencies: @@ -4333,7 +4428,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 8.31.0(typescript@5.8.3) '@typescript-eslint/utils': 8.31.0(eslint@9.26.0)(typescript@5.8.3) - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) eslint: 9.26.0 ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 @@ -4346,7 +4441,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.31.0 '@typescript-eslint/visitor-keys': 8.31.0 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 @@ -4435,6 +4530,8 @@ snapshots: acorn@8.14.1: {} + agent-base@7.1.3: {} + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -4442,6 +4539,8 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ansi-colors@4.1.3: {} + ansi-escapes@7.0.0: dependencies: environment: 1.1.0 @@ -4549,7 +4648,7 @@ snapshots: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) http-errors: 2.0.0 iconv-lite: 0.6.3 on-finished: 2.4.1 @@ -4630,6 +4729,8 @@ snapshots: chalk@5.4.1: {} + change-case@5.4.4: {} + check-error@2.1.1: {} chokidar@4.0.3: @@ -4659,6 +4760,8 @@ snapshots: color-name@1.1.4: {} + colorette@1.4.0: {} + colorette@2.0.20: {} commander@13.1.0: {} @@ -4722,9 +4825,11 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.4.1: + debug@4.4.1(supports-color@10.0.0): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 10.0.0 decircular@0.1.1: {} @@ -4911,7 +5016,7 @@ snapshots: esbuild-register@3.6.0(esbuild@0.25.4): dependencies: - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) esbuild: 0.25.4 transitivePeerDependencies: - supports-color @@ -5149,7 +5254,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) escape-string-regexp: 4.0.0 eslint-scope: 8.3.0 eslint-visitor-keys: 4.2.0 @@ -5224,7 +5329,7 @@ snapshots: content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -5280,7 +5385,7 @@ snapshots: finalhandler@2.1.0: dependencies: - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 @@ -5448,6 +5553,13 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + https-proxy-agent@7.0.6(supports-color@10.0.0): + dependencies: + agent-base: 7.1.3 + debug: 4.4.1(supports-color@10.0.0) + transitivePeerDependencies: + - supports-color + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -5640,6 +5752,8 @@ snapshots: joycon@3.1.1: {} + js-levenshtein@1.1.6: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -5656,6 +5770,8 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json5@1.0.2: @@ -5716,7 +5832,7 @@ snapshots: dependencies: chalk: 5.4.1 commander: 13.1.0 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) lilconfig: 3.1.3 listr2: 8.3.3 micromatch: 4.0.8 @@ -5815,6 +5931,10 @@ snapshots: dependencies: brace-expansion: 1.1.11 + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -5920,6 +6040,16 @@ snapshots: dependencies: which-pm-runs: 1.1.0 + openapi-typescript@7.8.0(typescript@5.8.3): + dependencies: + '@redocly/openapi-core': 1.34.3(supports-color@10.0.0) + ansi-colors: 4.1.3 + change-case: 5.4.4 + parse-json: 8.3.0 + supports-color: 10.0.0 + typescript: 5.8.3 + yargs-parser: 21.1.1 + openapi3-ts@4.4.0: dependencies: yaml: 2.7.1 @@ -6125,9 +6255,11 @@ snapshots: dependencies: jsesc: 3.0.2 + require-from-string@2.0.2: {} + require-in-the-middle@7.5.2: dependencies: - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) module-details-from-path: 1.0.4 resolve: 1.22.10 transitivePeerDependencies: @@ -6190,7 +6322,7 @@ snapshots: router@2.2.0: dependencies: - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 @@ -6235,7 +6367,7 @@ snapshots: send@1.2.0: dependencies: - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -6477,6 +6609,8 @@ snapshots: pirates: 4.0.7 ts-interface-checker: 0.1.13 + supports-color@10.0.0: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -6547,7 +6681,7 @@ snapshots: cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.2 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) esbuild: 0.25.4 joycon: 3.1.1 picocolors: 1.1.1 @@ -6680,6 +6814,8 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + uri-js-replace@1.0.1: {} + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -6696,7 +6832,7 @@ snapshots: vite-node@3.1.3(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.7.1): dependencies: cac: 6.7.14 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 6.3.3(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.7.1) @@ -6749,7 +6885,7 @@ snapshots: '@vitest/spy': 3.1.3 '@vitest/utils': 3.1.3 chai: 5.2.0 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) expect-type: 1.2.1 magic-string: 0.30.17 pathe: 2.0.3 @@ -6866,8 +7002,12 @@ snapshots: xtend@4.0.2: {} + yaml-ast-parser@0.0.43: {} + yaml@2.7.1: {} + yargs-parser@21.1.1: {} + yocto-queue@0.1.0: {} zod-to-json-schema@3.24.5(zod@3.24.4):