diff --git a/.gitignore b/.gitignore index 54efdb23..d0f5a58a 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,5 @@ apps/api/auth-db-temp.json .dev.vars .dev.vars.* + +.wrangler \ No newline at end of file diff --git a/apps/api/package.json b/apps/api/package.json index 3b6dc221..a952c094 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -19,7 +19,7 @@ }, "scripts": { "build": "tsup", - "dev": "tsup --watch", + "dev": "tsx src/server.ts", "clean": "del dist", "test": "run-s test:*", "test:lint": "eslint .", diff --git a/apps/api/src/auth.ts b/apps/api/src/auth.ts index 35989102..2c75676a 100644 --- a/apps/api/src/auth.ts +++ b/apps/api/src/auth.ts @@ -23,6 +23,28 @@ export const authRouter = issuer({ // access: 60 * 60 * 24 * 366, // 1 year // refresh: 60 * 60 * 24 * 365 * 5 // 5 years }, + theme: { + title: 'Agentic', + logo: { + dark: 'https://vercel.com/mktng/_next/static/media/vercel-logotype-dark.e8c0a742.svg', + light: + 'https://vercel.com/mktng/_next/static/media/vercel-logotype-light.700a8d26.svg' + }, + background: { + dark: 'black', + light: 'white' + }, + primary: { + dark: 'white', + light: 'black' + }, + font: { + family: 'Geist, sans-serif' + }, + css: ` + @import url('https://fonts.googleapis.com/css2?family=Geist:wght@100;200;300;400;500;600;700;800;900&display=swap'); + ` + }, providers: { github: GithubProvider({ clientID: env.GITHUB_CLIENT_ID, diff --git a/apps/api/src/db/schemas.ts b/apps/api/src/db/schemas.ts index 99e3972b..b9479c6b 100644 --- a/apps/api/src/db/schemas.ts +++ b/apps/api/src/db/schemas.ts @@ -1,5 +1,5 @@ import { assert } from '@agentic/platform-core' -import { validators } from '@agentic/platform-validators' +import { parseFaasIdentifier, validators } from '@agentic/platform-validators' import { z } from '@hono/zod-openapi' import type { consumersRelations } from './schema/consumer' @@ -53,7 +53,7 @@ export const projectIdentifierSchema = z export const deploymentIdentifierSchema = z .string() - .refine((id) => validators.deploymentIdentifier(id), { + .refine((id) => !!parseFaasIdentifier(id), { message: 'Invalid deployment identifier' }) .describe( diff --git a/apps/e2e/.env.example b/apps/e2e/.env.example index 4d5fe1c2..10baa6ed 100644 --- a/apps/e2e/.env.example +++ b/apps/e2e/.env.example @@ -5,4 +5,4 @@ # a local .env file in order to run this project. # ------------------------------------------------------------------------------ -AGENTIC_API_REFRESH_TOKEN= +AGENTIC_API_ACCESS_TOKEN= diff --git a/apps/e2e/src/e2e.test.ts b/apps/e2e/src/e2e.test.ts index 49d44b55..ff605bd5 100644 --- a/apps/e2e/src/e2e.test.ts +++ b/apps/e2e/src/e2e.test.ts @@ -1,44 +1,11 @@ -import path from 'node:path' -import { fileURLToPath } from 'node:url' +import { expect, test } from 'vitest' -import { loadAgenticConfig } from '@agentic/platform' -import { describe, expect, test } from 'vitest' - -import { client } from './client' - -const fixtures = [ - // 'basic-raw-free-ts', - // 'basic-raw-free-json', - // 'pricing-freemium', - // 'pricing-pay-as-you-go', - // 'pricing-3-plans', - // 'pricing-monthly-annual', - // 'pricing-custom-0', - 'basic-openapi' -] - -const fixturesDir = path.join( - fileURLToPath(import.meta.url), - '..', - '..', - '..', - '..', - 'packages', - 'fixtures' +test( + `${fixture}`, + { + timeout: 60_000 + }, + async () => { + 'dev/test-basic-openapi@8d1a4900' + } ) -const validFixturesDir = path.join(fixturesDir, 'valid') - -for (const fixture of fixtures) { - test( - `${fixture}`, - { - timeout: 60_000 - }, - async () => { - const fixtureDir = path.join(validFixturesDir, fixture) - - const config = await loadAgenticConfig({ cwd: fixtureDir }) - const deployment = await client.createDeployment(config) - } - ) -} diff --git a/apps/gateway/package.json b/apps/gateway/package.json index aa93ecaf..8daa66c0 100644 --- a/apps/gateway/package.json +++ b/apps/gateway/package.json @@ -33,6 +33,7 @@ "@agentic/platform-core": "workspace:*", "@agentic/platform-types": "workspace:*", "@agentic/platform-validators": "workspace:*", + "@cfworker/json-schema": "^4.1.1", "@hono/zod-validator": "catalog:", "@modelcontextprotocol/sdk": "catalog:", "eventid": "catalog:", diff --git a/apps/gateway/src/lib/create-request-for-openapi-operation.ts b/apps/gateway/src/lib/create-request-for-openapi-operation.ts index 9dbb7b9b..a6f81874 100644 --- a/apps/gateway/src/lib/create-request-for-openapi-operation.ts +++ b/apps/gateway/src/lib/create-request-for-openapi-operation.ts @@ -1,24 +1,52 @@ import type { AdminDeployment, - OpenAPIToolOperation + OpenAPIToolOperation, + Tool } from '@agentic/platform-types' import { assert } from '@agentic/platform-core' +import { validateJsonSchema } from './validate-json-schema' + export async function createRequestForOpenAPIOperation({ request, + tool, operation, deployment }: { request: Request + tool: Tool operation: OpenAPIToolOperation deployment: AdminDeployment }): Promise { - const tempInitialRequest = request.clone() - const tempInitialRequestBody: any = await tempInitialRequest.json() + assert( + deployment.originAdapter.type === 'openapi', + 500, + `Unexpected origin adapter type: "${deployment.originAdapter.type}"` + ) - const params = Object.entries(operation.parameterSources) + const tempInitialRequest = request.clone() + + let incomingRequestParams: Record = {} + if (request.method === 'GET') { + incomingRequestParams = Object.fromEntries( + new URLSearchParams(tempInitialRequest.url).entries() + ) + } else if (request.method === 'POST') { + incomingRequestParams = (await tempInitialRequest.json()) as Record< + string, + any + > + } + + // TODO: Validate incoming request params against the tool's input JSON schema + validateJsonSchema({ + schema: tool.inputSchema, + data: incomingRequestParams, + errorMessage: `Invalid request parameters for tool "${tool.name}"` + }) // TODO: Make this more efficient by changing the `parameterSources` data structure + const params = Object.entries(operation.parameterSources) const bodyParams = params.filter(([_key, source]) => source === 'body') const formDataParams = params.filter( ([_key, source]) => source === 'formData' @@ -36,19 +64,21 @@ export async function createRequestForOpenAPIOperation({ const headers: Record = {} if (headerParams.length > 0) { for (const [key] of headerParams) { - headers[key] = tempInitialRequest.headers.get(key) as string + headers[key] = + (tempInitialRequest.headers.get(key) as string) ?? + incomingRequestParams[key] } } for (const [key] of cookieParams) { - headers[key] = tempInitialRequestBody[key] as string + headers[key] = String(incomingRequestParams[key]) } let body: string | undefined if (bodyParams.length > 0) { body = JSON.stringify( Object.fromEntries( - bodyParams.map(([key]) => [key, tempInitialRequestBody[key] as any]) + bodyParams.map(([key]) => [key, incomingRequestParams[key]]) ) ) @@ -56,7 +86,7 @@ export async function createRequestForOpenAPIOperation({ } else if (formDataParams.length > 0) { body = JSON.stringify( Object.fromEntries( - formDataParams.map(([key]) => [key, tempInitialRequestBody[key] as any]) + formDataParams.map(([key]) => [key, incomingRequestParams[key]]) ) ) @@ -66,7 +96,7 @@ export async function createRequestForOpenAPIOperation({ let path = operation.path if (pathParams.length > 0) { for (const [key] of pathParams) { - const value: string = tempInitialRequestBody[key] + const value: string = incomingRequestParams[key] assert(value, 400, `Missing required parameter "${key}"`) const pathParamPlaceholder = `{${key}}` @@ -87,9 +117,8 @@ export async function createRequestForOpenAPIOperation({ const query = new URLSearchParams() for (const [key] of queryParams) { - query.set(key, tempInitialRequestBody[key] as string) + query.set(key, incomingRequestParams[key] as string) } - const queryString = query.toString() const originRequestUrl = `${deployment.originUrl}${path}${ queryString ? `?${queryString}` : '' diff --git a/apps/gateway/src/lib/get-tool.ts b/apps/gateway/src/lib/get-tool.ts index a9847f4e..4fa4563c 100644 --- a/apps/gateway/src/lib/get-tool.ts +++ b/apps/gateway/src/lib/get-tool.ts @@ -17,14 +17,36 @@ export function getTool({ .at(-1) assert(toolName, 404, `Invalid tool path "${toolPath}"`) - const tool = deployment.tools.find((tool) => { - if (tool.name === toolName) { - return true - } + let tool = deployment.tools.find((tool) => tool.name === toolName) - return false - }) - assert(tool, 404, `Tool not found "${toolPath}"`) + if (!tool) { + if (deployment.originAdapter.type === 'openapi') { + const operationToolName = Object.entries( + deployment.originAdapter.toolToOperationMap + ).find(([_, operation]) => { + if (operation.operationId === toolName) { + return true + } + + return false + })?.[0] + + if (operationToolName) { + tool = deployment.tools.find((tool) => tool.name === operationToolName) + } + assert( + tool, + 404, + `Tool not found "${toolName}" for deployment "${deployment.identifier}": did you mean "${operationToolName}"?` + ) + } + } + + assert( + tool, + 404, + `Tool not found "${toolName}" for deployment "${deployment.identifier}"` + ) if (deployment.originAdapter.type === 'openapi') { const operation = deployment.originAdapter.toolToOperationMap[tool.name] @@ -34,9 +56,9 @@ export function getTool({ `OpenAPI operation not found for tool "${tool.name}"` ) assert( - operation.method.toUpperCase() === method.toUpperCase(), + method === 'GET' || method === 'POST', 405, - `Invalid HTTP method "${method.toUpperCase()}" for tool "${tool.name}"` + `Invalid HTTP method "${method}" for tool "${tool.name}"` ) return { diff --git a/apps/gateway/src/lib/resolve-origin-request.ts b/apps/gateway/src/lib/resolve-origin-request.ts index 33354259..9f66e8ba 100644 --- a/apps/gateway/src/lib/resolve-origin-request.ts +++ b/apps/gateway/src/lib/resolve-origin-request.ts @@ -23,8 +23,8 @@ export async function resolveOriginRequest( const ip = req.headers.get('cf-connecting-ip') || undefined const requestUrl = new URL(req.url) - const { search, pathname } = requestUrl - const method = req.method.toLowerCase() + const { pathname } = requestUrl + const { method } = req const requestPathParts = pathname.split('/') // TODO: the isMCPRequest logic needs to be completely redone. @@ -44,7 +44,6 @@ export async function resolveOriginRequest( console.log('request', { method, pathname, - search, deploymentIdentifier: deployment.identifier, toolPath, tool @@ -172,11 +171,12 @@ export async function resolveOriginRequest( originRequest = await createRequestForOpenAPIOperation({ request: req, + tool, operation, deployment }) } else { - const originRequestUrl = `${deployment.originUrl}${toolPath}${search}` + const originRequestUrl = `${deployment.originUrl}${toolPath}${requestUrl.search}` originRequest = new Request(originRequestUrl, req) } diff --git a/apps/gateway/src/lib/validate-json-schema.ts b/apps/gateway/src/lib/validate-json-schema.ts new file mode 100644 index 00000000..3d80c634 --- /dev/null +++ b/apps/gateway/src/lib/validate-json-schema.ts @@ -0,0 +1,33 @@ +import { HttpError } from '@agentic/platform-core' +import { Validator } from '@cfworker/json-schema' + +export function validateJsonSchema< + T extends Record = Record +>({ + schema, + data, + errorMessage +}: { + schema: any + data: unknown + errorMessage?: string +}): T { + const validator = new Validator(schema) + + const result = validator.validate(data) + if (result.valid) { + return data as T + } + + const finalErrorMessage = [ + errorMessage, + ...result.errors.map((error) => JSON.stringify(error, null, 2)) + ] + .filter(Boolean) + .join('\n') + + throw new HttpError({ + statusCode: 400, + message: finalErrorMessage + }) +} diff --git a/apps/gateway/tsconfig.json b/apps/gateway/tsconfig.json index 442ffe5a..8e923f86 100644 --- a/apps/gateway/tsconfig.json +++ b/apps/gateway/tsconfig.json @@ -7,6 +7,10 @@ }, "types": ["./src/worker.d.ts"] }, - "include": ["src", "*.config.ts"], + "include": [ + "src", + "*.config.ts", + "../../packages/openapi-utils/src/validate-json-schema.ts" + ], "exclude": ["node_modules", "dist"] } diff --git a/apps/gateway/wrangler.jsonc b/apps/gateway/wrangler.jsonc index 3cd67ac9..dbd845da 100644 --- a/apps/gateway/wrangler.jsonc +++ b/apps/gateway/wrangler.jsonc @@ -7,6 +7,7 @@ "name": "agentic-api-gateway", "main": "src/worker.ts", "compatibility_date": "2025-05-25", + "compatibility_flags": ["nodejs_compat"], "migrations": [ { "tag": "v1", diff --git a/packages/api-client/src/agentic-api-client.ts b/packages/api-client/src/agentic-api-client.ts index 862959c7..330425b3 100644 --- a/packages/api-client/src/agentic-api-client.ts +++ b/packages/api-client/src/agentic-api-client.ts @@ -59,6 +59,10 @@ export class AgenticApiClient { this.ky = ky.extend({ prefixUrl: apiBaseUrl, + + // Set a longer timeout on localhost to account for backend debugging / breakpoints. + timeout: apiBaseUrl.startsWith('http://localhost') ? 120_000 : undefined, + headers: apiKey ? { Authorization: `Bearer ${apiKey}` diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 9b46d99a..291c1169 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -64,9 +64,13 @@ export function assert( } if (typeof statusCodeOrMessage === 'number') { - throw new HttpError({ statusCode: statusCodeOrMessage, message }) + const error = new HttpError({ statusCode: statusCodeOrMessage, message }) + Error.captureStackTrace(error, assert) + throw error } else { - throw new Error(statusCodeOrMessage ?? message) + const error = new Error(statusCodeOrMessage ?? message) + Error.captureStackTrace(error, assert) + throw error } } diff --git a/packages/openapi-utils/package.json b/packages/openapi-utils/package.json index bd33f371..fa3036ff 100644 --- a/packages/openapi-utils/package.json +++ b/packages/openapi-utils/package.json @@ -25,9 +25,13 @@ "dependencies": { "@agentic/platform-core": "workspace:*", "@agentic/platform-types": "workspace:*", + "@apideck/better-ajv-errors": "^0.3.6", "@redocly/openapi-core": "catalog:", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", "camelcase": "catalog:", - "decamelize": "catalog:" + "decamelize": "catalog:", + "fast-uri": "^3.0.6" }, "publishConfig": { "access": "public" diff --git a/packages/openapi-utils/src/index.ts b/packages/openapi-utils/src/index.ts index ed607e8c..7b3490fd 100644 --- a/packages/openapi-utils/src/index.ts +++ b/packages/openapi-utils/src/index.ts @@ -1,4 +1,5 @@ export * from './get-tools-from-openapi-spec' export * from './redocly-config' export type * from './types' +export * from './validate-json-schema' export * from './validate-openapi-spec' diff --git a/packages/openapi-utils/src/validate-json-schema.ts b/packages/openapi-utils/src/validate-json-schema.ts new file mode 100644 index 00000000..ef6e0037 --- /dev/null +++ b/packages/openapi-utils/src/validate-json-schema.ts @@ -0,0 +1,60 @@ +import { hashObject, HttpError } from '@agentic/platform-core' +import { betterAjvErrors } from '@apideck/better-ajv-errors' +import Ajv, { type ValidateFunction } from 'ajv' +import addFormats from 'ajv-formats' +import fastUri from 'fast-uri' + +const globalAjv = new Ajv({ + coerceTypes: true, + useDefaults: true, + removeAdditional: true, + uriResolver: fastUri, + // Explicitly set allErrors to `false`. + // When set to `true`, a DoS attack is possible. + allErrors: false +}) + +// https://github.com/ajv-validator/ajv-formats +addFormats(globalAjv) + +// NOTE: Ajv is not compatible with Cloudflare Workers. +// @see https://github.com/ajv-validator/ajv/issues/2318 + +export function validateJsonSchema< + T extends Record = Record +>({ + schema, + data, + ajv = globalAjv, + errorMessage +}: { + schema: any + data: unknown + ajv?: Ajv + errorMessage?: string +}): T { + const schemaHashKey = hashObject(schema) + let validate = ajv.getSchema(schemaHashKey) as ValidateFunction + if (!validate) { + validate = ajv.compile(schema) + ajv.addSchema(schema, schemaHashKey) + } + + // TODO: Add better error messages + if (ajv.validate(schema, data)) { + return data as T + } + + const errors = betterAjvErrors({ schema, data, errors: ajv.errors }) + const finalErrorMessage = [ + errorMessage, + ...errors.map((error) => JSON.stringify(error, null, 2)) + ] + .filter(Boolean) + .join('\n') + + throw new HttpError({ + statusCode: 400, + message: finalErrorMessage + }) +} diff --git a/packages/validators/src/parse-faas-identifier.ts b/packages/validators/src/parse-faas-identifier.ts index 70eec8dc..a15ac134 100644 --- a/packages/validators/src/parse-faas-identifier.ts +++ b/packages/validators/src/parse-faas-identifier.ts @@ -15,13 +15,8 @@ export function parseFaasIdentifier( uri = pathname } catch {} - if (uri.startsWith('/')) { - uri = uri.slice(1) - } - - if (uri.endsWith('/')) { - uri = uri.slice(0, -1) - } + uri = uri.replaceAll(/^\//g, '') + uri = uri.replaceAll(/\/$/g, '') if (!uri.length) { return diff --git a/packages/validators/src/validators.ts b/packages/validators/src/validators.ts index b5685a88..029d4862 100644 --- a/packages/validators/src/validators.ts +++ b/packages/validators/src/validators.ts @@ -8,8 +8,9 @@ export const passwordRe = /^.{3,1024}$/ export const projectNameRe = /^[a-z0-9-]{2,64}$/ export const deploymentHashRe = /^[a-z0-9]{8}$/ -export const projectRe = /^[a-zA-Z0-9-]{1,64}\/[a-z0-9-]{3,64}$/ -export const deploymentRe = /^[a-zA-Z0-9-]{1,64}\/[a-z0-9-]{3,64}@[a-z0-9]{8}$/ +export const projectIdentifierRe = /^[a-zA-Z0-9-]{1,64}\/[a-z0-9-]{3,64}$/ +export const deploymentIdentifierRe = + /^[a-zA-Z0-9-]{1,64}\/[a-z0-9-]{3,64}@[a-z0-9]{8}$/ // tool names may be any valid JavaScript identifier // TODO: should tool names be any label? @@ -41,11 +42,11 @@ export function deploymentHash(value?: string): boolean { } export function projectIdentifier(value?: string): boolean { - return !!value && projectRe.test(value) + return !!value && projectIdentifierRe.test(value) } export function deploymentIdentifier(value?: string): boolean { - return !!value && deploymentRe.test(value) + return !!value && deploymentIdentifierRe.test(value) } export function toolName(value?: string): boolean { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d0d2dbd1..d9c2a5e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -382,6 +382,9 @@ importers: '@agentic/platform-validators': specifier: workspace:* version: link:../../packages/validators + '@cfworker/json-schema': + specifier: ^4.1.1 + version: 4.1.1 '@hono/zod-validator': specifier: 'catalog:' version: 0.6.0(hono@4.7.10)(zod@3.25.36) @@ -514,15 +517,27 @@ importers: '@agentic/platform-types': specifier: workspace:* version: link:../types + '@apideck/better-ajv-errors': + specifier: ^0.3.6 + version: 0.3.6(ajv@8.17.1) '@redocly/openapi-core': specifier: 'catalog:' version: 1.34.3(supports-color@10.0.0) + ajv: + specifier: ^8.17.1 + version: 8.17.1 + ajv-formats: + specifier: ^3.0.1 + version: 3.0.1(ajv@8.17.1) camelcase: specifier: 'catalog:' version: 8.0.0 decamelize: specifier: 'catalog:' version: 6.0.0 + fast-uri: + specifier: ^3.0.6 + version: 3.0.6 devDependencies: '@hono/node-server': specifier: 'catalog:' @@ -613,6 +628,12 @@ importers: packages: + '@apideck/better-ajv-errors@0.3.6': + resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} + engines: {node: '>=10'} + peerDependencies: + ajv: '>=8' + '@asteasolutions/zod-to-openapi@7.3.0': resolution: {integrity: sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==} peerDependencies: @@ -630,6 +651,9 @@ packages: resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==} engines: {node: '>=6.9.0'} + '@cfworker/json-schema@4.1.1': + resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} + '@clack/core@0.5.0': resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==} @@ -1020,10 +1044,6 @@ packages: resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.26.0': - resolution: {integrity: sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.27.0': resolution: {integrity: sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1396,9 +1416,6 @@ packages: resolution: {integrity: sha512-Q8nFIagNLIZgM2odAraelMcDssapc+lF+y3OlcIPxyAU+knefO8KmozGqfnma1xegRDP4z5M73ABsamn72bOcA==} engines: {node: '>= 20'} - '@octokit/openapi-types@25.0.0': - resolution: {integrity: sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==} - '@octokit/openapi-types@25.1.0': resolution: {integrity: sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==} @@ -1443,9 +1460,6 @@ packages: resolution: {integrity: sha512-iYj4SJG/2bbhh+iIpFmG5u49DtJ4lipQ+aPakjL9OKpsGY93wM8w06gvFbEQxcMsZcCvk5th5KkIm2m8o14aWA==} engines: {node: '>= 20'} - '@octokit/types@14.0.0': - resolution: {integrity: sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==} - '@octokit/types@14.1.0': resolution: {integrity: sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==} @@ -1928,9 +1942,6 @@ packages: '@types/mysql@2.15.26': resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} - '@types/node@22.15.21': - resolution: {integrity: sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==} - '@types/node@22.15.24': resolution: {integrity: sha512-w9CZGm9RDjzTh/D+hFwlBJ3ziUaVw7oufKA3vOFSOZlzmW9AkZnfjPb+DLnrV6qtgL/LNmP0/2zBNCFHL3F0ng==} @@ -3358,6 +3369,9 @@ packages: json-schema-typed@8.0.1: resolution: {integrity: sha512-XQmWYj2Sm4kn4WeTYvmpKEbyPsL7nBsb647c7pMe6l02/yx2+Jfc4dT6UZkEXnIUb5LhD55r2HPsJ1milQ4rDg==} + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -3365,6 +3379,10 @@ packages: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -3395,6 +3413,10 @@ packages: resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} engines: {node: '>=0.10'} + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -4666,6 +4688,13 @@ packages: snapshots: + '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)': + dependencies: + ajv: 8.17.1 + json-schema: 0.4.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + '@asteasolutions/zod-to-openapi@7.3.0(zod@3.25.36)': dependencies: openapi3-ts: 4.4.0 @@ -4683,6 +4712,8 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 + '@cfworker/json-schema@4.1.1': {} + '@clack/core@0.5.0': dependencies: picocolors: 1.1.1 @@ -4935,8 +4966,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.26.0': {} - '@eslint/js@9.27.0': {} '@eslint/object-schema@2.1.6': {} @@ -4955,7 +4984,7 @@ snapshots: '@fisch0920/config@1.1.0(@typescript-eslint/parser@8.31.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(@typescript-eslint/utils@8.31.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(prettier@3.5.3)(typescript@5.8.3)(vitest@3.1.4(@types/node@22.15.24)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))': dependencies: - '@eslint/js': 9.26.0 + '@eslint/js': 9.27.0 '@total-typescript/ts-reset': 0.6.1 '@vitest/eslint-plugin': 1.1.43(@typescript-eslint/utils@8.31.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.1.4(@types/node@22.15.24)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)) eslint: 9.27.0(jiti@2.4.2) @@ -5178,7 +5207,7 @@ snapshots: '@octokit/core': 7.0.2 '@octokit/oauth-app': 8.0.1 '@octokit/plugin-paginate-rest': 13.0.0(@octokit/core@7.0.2) - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 '@octokit/webhooks': 14.0.0 '@octokit/auth-app@8.0.1': @@ -5187,7 +5216,7 @@ snapshots: '@octokit/auth-oauth-user': 6.0.0 '@octokit/request': 10.0.2 '@octokit/request-error': 7.0.0 - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 toad-cache: 3.7.0 universal-github-app-jwt: 2.2.2 universal-user-agent: 7.0.3 @@ -5197,14 +5226,14 @@ snapshots: '@octokit/auth-oauth-device': 8.0.1 '@octokit/auth-oauth-user': 6.0.0 '@octokit/request': 10.0.2 - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 universal-user-agent: 7.0.3 '@octokit/auth-oauth-device@8.0.1': dependencies: '@octokit/oauth-methods': 6.0.0 '@octokit/request': 10.0.2 - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 universal-user-agent: 7.0.3 '@octokit/auth-oauth-user@6.0.0': @@ -5212,7 +5241,7 @@ snapshots: '@octokit/auth-oauth-device': 8.0.1 '@octokit/oauth-methods': 6.0.0 '@octokit/request': 10.0.2 - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 universal-user-agent: 7.0.3 '@octokit/auth-token@6.0.0': {} @@ -5220,7 +5249,7 @@ snapshots: '@octokit/auth-unauthenticated@7.0.1': dependencies: '@octokit/request-error': 7.0.0 - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 '@octokit/core@7.0.2': dependencies: @@ -5228,19 +5257,19 @@ snapshots: '@octokit/graphql': 9.0.1 '@octokit/request': 10.0.2 '@octokit/request-error': 7.0.0 - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 before-after-hook: 4.0.0 universal-user-agent: 7.0.3 '@octokit/endpoint@11.0.0': dependencies: - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 universal-user-agent: 7.0.3 '@octokit/graphql@9.0.1': dependencies: '@octokit/request': 10.0.2 - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 universal-user-agent: 7.0.3 '@octokit/oauth-app@8.0.1': @@ -5261,9 +5290,7 @@ snapshots: '@octokit/oauth-authorization-url': 8.0.0 '@octokit/request': 10.0.2 '@octokit/request-error': 7.0.0 - '@octokit/types': 14.0.0 - - '@octokit/openapi-types@25.0.0': {} + '@octokit/types': 14.1.0 '@octokit/openapi-types@25.1.0': {} @@ -5276,7 +5303,7 @@ snapshots: '@octokit/plugin-paginate-rest@13.0.0(@octokit/core@7.0.2)': dependencies: '@octokit/core': 7.0.2 - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 '@octokit/plugin-rest-endpoint-methods@16.0.0(@octokit/core@7.0.2)': dependencies: @@ -5287,31 +5314,27 @@ snapshots: dependencies: '@octokit/core': 7.0.2 '@octokit/request-error': 7.0.0 - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 bottleneck: 2.19.5 '@octokit/plugin-throttling@11.0.1(@octokit/core@7.0.2)': dependencies: '@octokit/core': 7.0.2 - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 bottleneck: 2.19.5 '@octokit/request-error@7.0.0': dependencies: - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 '@octokit/request@10.0.2': dependencies: '@octokit/endpoint': 11.0.0 '@octokit/request-error': 7.0.0 - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 fast-content-type-parse: 3.0.0 universal-user-agent: 7.0.3 - '@octokit/types@14.0.0': - dependencies: - '@octokit/openapi-types': 25.0.0 - '@octokit/types@14.1.0': dependencies: '@octokit/openapi-types': 25.1.0 @@ -5814,7 +5837,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 22.15.21 + '@types/node': 22.15.24 '@types/estree@1.0.7': {} @@ -5826,11 +5849,7 @@ snapshots: '@types/mysql@2.15.26': dependencies: - '@types/node': 22.15.21 - - '@types/node@22.15.21': - dependencies: - undici-types: 6.21.0 + '@types/node': 22.15.24 '@types/node@22.15.24': dependencies: @@ -5844,7 +5863,7 @@ snapshots: '@types/pg@8.6.1': dependencies: - '@types/node': 22.15.21 + '@types/node': 22.15.24 pg-protocol: 1.10.0 pg-types: 2.2.0 @@ -5854,7 +5873,7 @@ snapshots: '@types/tedious@4.0.14': dependencies: - '@types/node': 22.15.21 + '@types/node': 22.15.24 '@typescript-eslint/eslint-plugin@8.31.0(@typescript-eslint/parser@8.31.0(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.27.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: @@ -7379,12 +7398,16 @@ snapshots: json-schema-typed@8.0.1: {} + json-schema@0.4.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json5@1.0.2: dependencies: minimist: 1.2.8 + jsonpointer@5.0.1: {} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 @@ -7425,6 +7448,8 @@ snapshots: dependencies: language-subtag-registry: 0.3.23 + leven@3.1.0: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -7657,7 +7682,7 @@ snapshots: '@octokit/plugin-retry': 8.0.1(@octokit/core@7.0.2) '@octokit/plugin-throttling': 11.0.1(@octokit/core@7.0.2) '@octokit/request-error': 7.0.0 - '@octokit/types': 14.0.0 + '@octokit/types': 14.1.0 '@octokit/webhooks': 14.0.0 ohash@2.0.11: {}