feat: first e2e test with gateway working :)

pull/715/head
Travis Fischer 2025-06-03 19:46:02 +07:00
rodzic e2eeefef89
commit a63c75f99e
21 zmienionych plików z 306 dodań i 131 usunięć

2
.gitignore vendored
Wyświetl plik

@ -47,3 +47,5 @@ apps/api/auth-db-temp.json
.dev.vars .dev.vars
.dev.vars.* .dev.vars.*
.wrangler

Wyświetl plik

@ -19,7 +19,7 @@
}, },
"scripts": { "scripts": {
"build": "tsup", "build": "tsup",
"dev": "tsup --watch", "dev": "tsx src/server.ts",
"clean": "del dist", "clean": "del dist",
"test": "run-s test:*", "test": "run-s test:*",
"test:lint": "eslint .", "test:lint": "eslint .",

Wyświetl plik

@ -23,6 +23,28 @@ export const authRouter = issuer({
// access: 60 * 60 * 24 * 366, // 1 year // access: 60 * 60 * 24 * 366, // 1 year
// refresh: 60 * 60 * 24 * 365 * 5 // 5 years // 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: { providers: {
github: GithubProvider({ github: GithubProvider({
clientID: env.GITHUB_CLIENT_ID, clientID: env.GITHUB_CLIENT_ID,

Wyświetl plik

@ -1,5 +1,5 @@
import { assert } from '@agentic/platform-core' 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 { z } from '@hono/zod-openapi'
import type { consumersRelations } from './schema/consumer' import type { consumersRelations } from './schema/consumer'
@ -53,7 +53,7 @@ export const projectIdentifierSchema = z
export const deploymentIdentifierSchema = z export const deploymentIdentifierSchema = z
.string() .string()
.refine((id) => validators.deploymentIdentifier(id), { .refine((id) => !!parseFaasIdentifier(id), {
message: 'Invalid deployment identifier' message: 'Invalid deployment identifier'
}) })
.describe( .describe(

Wyświetl plik

@ -5,4 +5,4 @@
# a local .env file in order to run this project. # a local .env file in order to run this project.
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
AGENTIC_API_REFRESH_TOKEN= AGENTIC_API_ACCESS_TOKEN=

Wyświetl plik

@ -1,44 +1,11 @@
import path from 'node:path' import { expect, test } from 'vitest'
import { fileURLToPath } from 'node:url'
import { loadAgenticConfig } from '@agentic/platform' test(
import { describe, expect, test } from 'vitest' `${fixture}`,
{
import { client } from './client' timeout: 60_000
},
const fixtures = [ async () => {
// 'basic-raw-free-ts', 'dev/test-basic-openapi@8d1a4900'
// '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'
) )
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)
}
)
}

Wyświetl plik

@ -33,6 +33,7 @@
"@agentic/platform-core": "workspace:*", "@agentic/platform-core": "workspace:*",
"@agentic/platform-types": "workspace:*", "@agentic/platform-types": "workspace:*",
"@agentic/platform-validators": "workspace:*", "@agentic/platform-validators": "workspace:*",
"@cfworker/json-schema": "^4.1.1",
"@hono/zod-validator": "catalog:", "@hono/zod-validator": "catalog:",
"@modelcontextprotocol/sdk": "catalog:", "@modelcontextprotocol/sdk": "catalog:",
"eventid": "catalog:", "eventid": "catalog:",

Wyświetl plik

@ -1,24 +1,52 @@
import type { import type {
AdminDeployment, AdminDeployment,
OpenAPIToolOperation OpenAPIToolOperation,
Tool
} from '@agentic/platform-types' } from '@agentic/platform-types'
import { assert } from '@agentic/platform-core' import { assert } from '@agentic/platform-core'
import { validateJsonSchema } from './validate-json-schema'
export async function createRequestForOpenAPIOperation({ export async function createRequestForOpenAPIOperation({
request, request,
tool,
operation, operation,
deployment deployment
}: { }: {
request: Request request: Request
tool: Tool
operation: OpenAPIToolOperation operation: OpenAPIToolOperation
deployment: AdminDeployment deployment: AdminDeployment
}): Promise<Request> { }): Promise<Request> {
const tempInitialRequest = request.clone() assert(
const tempInitialRequestBody: any = await tempInitialRequest.json() 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<string, any> = {}
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 // 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 bodyParams = params.filter(([_key, source]) => source === 'body')
const formDataParams = params.filter( const formDataParams = params.filter(
([_key, source]) => source === 'formData' ([_key, source]) => source === 'formData'
@ -36,19 +64,21 @@ export async function createRequestForOpenAPIOperation({
const headers: Record<string, string> = {} const headers: Record<string, string> = {}
if (headerParams.length > 0) { if (headerParams.length > 0) {
for (const [key] of headerParams) { 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) { for (const [key] of cookieParams) {
headers[key] = tempInitialRequestBody[key] as string headers[key] = String(incomingRequestParams[key])
} }
let body: string | undefined let body: string | undefined
if (bodyParams.length > 0) { if (bodyParams.length > 0) {
body = JSON.stringify( body = JSON.stringify(
Object.fromEntries( 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) { } else if (formDataParams.length > 0) {
body = JSON.stringify( body = JSON.stringify(
Object.fromEntries( 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 let path = operation.path
if (pathParams.length > 0) { if (pathParams.length > 0) {
for (const [key] of pathParams) { for (const [key] of pathParams) {
const value: string = tempInitialRequestBody[key] const value: string = incomingRequestParams[key]
assert(value, 400, `Missing required parameter "${key}"`) assert(value, 400, `Missing required parameter "${key}"`)
const pathParamPlaceholder = `{${key}}` const pathParamPlaceholder = `{${key}}`
@ -87,9 +117,8 @@ export async function createRequestForOpenAPIOperation({
const query = new URLSearchParams() const query = new URLSearchParams()
for (const [key] of queryParams) { for (const [key] of queryParams) {
query.set(key, tempInitialRequestBody[key] as string) query.set(key, incomingRequestParams[key] as string)
} }
const queryString = query.toString() const queryString = query.toString()
const originRequestUrl = `${deployment.originUrl}${path}${ const originRequestUrl = `${deployment.originUrl}${path}${
queryString ? `?${queryString}` : '' queryString ? `?${queryString}` : ''

Wyświetl plik

@ -17,14 +17,36 @@ export function getTool({
.at(-1) .at(-1)
assert(toolName, 404, `Invalid tool path "${toolPath}"`) assert(toolName, 404, `Invalid tool path "${toolPath}"`)
const tool = deployment.tools.find((tool) => { let tool = deployment.tools.find((tool) => tool.name === toolName)
if (tool.name === toolName) {
return true
}
return false if (!tool) {
}) if (deployment.originAdapter.type === 'openapi') {
assert(tool, 404, `Tool not found "${toolPath}"`) 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') { if (deployment.originAdapter.type === 'openapi') {
const operation = deployment.originAdapter.toolToOperationMap[tool.name] const operation = deployment.originAdapter.toolToOperationMap[tool.name]
@ -34,9 +56,9 @@ export function getTool({
`OpenAPI operation not found for tool "${tool.name}"` `OpenAPI operation not found for tool "${tool.name}"`
) )
assert( assert(
operation.method.toUpperCase() === method.toUpperCase(), method === 'GET' || method === 'POST',
405, 405,
`Invalid HTTP method "${method.toUpperCase()}" for tool "${tool.name}"` `Invalid HTTP method "${method}" for tool "${tool.name}"`
) )
return { return {

Wyświetl plik

@ -23,8 +23,8 @@ export async function resolveOriginRequest(
const ip = req.headers.get('cf-connecting-ip') || undefined const ip = req.headers.get('cf-connecting-ip') || undefined
const requestUrl = new URL(req.url) const requestUrl = new URL(req.url)
const { search, pathname } = requestUrl const { pathname } = requestUrl
const method = req.method.toLowerCase() const { method } = req
const requestPathParts = pathname.split('/') const requestPathParts = pathname.split('/')
// TODO: the isMCPRequest logic needs to be completely redone. // TODO: the isMCPRequest logic needs to be completely redone.
@ -44,7 +44,6 @@ export async function resolveOriginRequest(
console.log('request', { console.log('request', {
method, method,
pathname, pathname,
search,
deploymentIdentifier: deployment.identifier, deploymentIdentifier: deployment.identifier,
toolPath, toolPath,
tool tool
@ -172,11 +171,12 @@ export async function resolveOriginRequest(
originRequest = await createRequestForOpenAPIOperation({ originRequest = await createRequestForOpenAPIOperation({
request: req, request: req,
tool,
operation, operation,
deployment deployment
}) })
} else { } else {
const originRequestUrl = `${deployment.originUrl}${toolPath}${search}` const originRequestUrl = `${deployment.originUrl}${toolPath}${requestUrl.search}`
originRequest = new Request(originRequestUrl, req) originRequest = new Request(originRequestUrl, req)
} }

Wyświetl plik

@ -0,0 +1,33 @@
import { HttpError } from '@agentic/platform-core'
import { Validator } from '@cfworker/json-schema'
export function validateJsonSchema<
T extends Record<string, unknown> = Record<string, unknown>
>({
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
})
}

Wyświetl plik

@ -7,6 +7,10 @@
}, },
"types": ["./src/worker.d.ts"] "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"] "exclude": ["node_modules", "dist"]
} }

Wyświetl plik

@ -7,6 +7,7 @@
"name": "agentic-api-gateway", "name": "agentic-api-gateway",
"main": "src/worker.ts", "main": "src/worker.ts",
"compatibility_date": "2025-05-25", "compatibility_date": "2025-05-25",
"compatibility_flags": ["nodejs_compat"],
"migrations": [ "migrations": [
{ {
"tag": "v1", "tag": "v1",

Wyświetl plik

@ -59,6 +59,10 @@ export class AgenticApiClient {
this.ky = ky.extend({ this.ky = ky.extend({
prefixUrl: apiBaseUrl, prefixUrl: apiBaseUrl,
// Set a longer timeout on localhost to account for backend debugging / breakpoints.
timeout: apiBaseUrl.startsWith('http://localhost') ? 120_000 : undefined,
headers: apiKey headers: apiKey
? { ? {
Authorization: `Bearer ${apiKey}` Authorization: `Bearer ${apiKey}`

Wyświetl plik

@ -64,9 +64,13 @@ export function assert(
} }
if (typeof statusCodeOrMessage === 'number') { if (typeof statusCodeOrMessage === 'number') {
throw new HttpError({ statusCode: statusCodeOrMessage, message }) const error = new HttpError({ statusCode: statusCodeOrMessage, message })
Error.captureStackTrace(error, assert)
throw error
} else { } else {
throw new Error(statusCodeOrMessage ?? message) const error = new Error(statusCodeOrMessage ?? message)
Error.captureStackTrace(error, assert)
throw error
} }
} }

Wyświetl plik

@ -25,9 +25,13 @@
"dependencies": { "dependencies": {
"@agentic/platform-core": "workspace:*", "@agentic/platform-core": "workspace:*",
"@agentic/platform-types": "workspace:*", "@agentic/platform-types": "workspace:*",
"@apideck/better-ajv-errors": "^0.3.6",
"@redocly/openapi-core": "catalog:", "@redocly/openapi-core": "catalog:",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"camelcase": "catalog:", "camelcase": "catalog:",
"decamelize": "catalog:" "decamelize": "catalog:",
"fast-uri": "^3.0.6"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

Wyświetl plik

@ -1,4 +1,5 @@
export * from './get-tools-from-openapi-spec' export * from './get-tools-from-openapi-spec'
export * from './redocly-config' export * from './redocly-config'
export type * from './types' export type * from './types'
export * from './validate-json-schema'
export * from './validate-openapi-spec' export * from './validate-openapi-spec'

Wyświetl plik

@ -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<string, unknown> = Record<string, unknown>
>({
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<T>
if (!validate) {
validate = ajv.compile<T>(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
})
}

Wyświetl plik

@ -15,13 +15,8 @@ export function parseFaasIdentifier(
uri = pathname uri = pathname
} catch {} } catch {}
if (uri.startsWith('/')) { uri = uri.replaceAll(/^\//g, '')
uri = uri.slice(1) uri = uri.replaceAll(/\/$/g, '')
}
if (uri.endsWith('/')) {
uri = uri.slice(0, -1)
}
if (!uri.length) { if (!uri.length) {
return return

Wyświetl plik

@ -8,8 +8,9 @@ export const passwordRe = /^.{3,1024}$/
export const projectNameRe = /^[a-z0-9-]{2,64}$/ export const projectNameRe = /^[a-z0-9-]{2,64}$/
export const deploymentHashRe = /^[a-z0-9]{8}$/ export const deploymentHashRe = /^[a-z0-9]{8}$/
export const projectRe = /^[a-zA-Z0-9-]{1,64}\/[a-z0-9-]{3,64}$/ export const projectIdentifierRe = /^[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 deploymentIdentifierRe =
/^[a-zA-Z0-9-]{1,64}\/[a-z0-9-]{3,64}@[a-z0-9]{8}$/
// tool names may be any valid JavaScript identifier // tool names may be any valid JavaScript identifier
// TODO: should tool names be any label? // TODO: should tool names be any label?
@ -41,11 +42,11 @@ export function deploymentHash(value?: string): boolean {
} }
export function projectIdentifier(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 { export function deploymentIdentifier(value?: string): boolean {
return !!value && deploymentRe.test(value) return !!value && deploymentIdentifierRe.test(value)
} }
export function toolName(value?: string): boolean { export function toolName(value?: string): boolean {

Wyświetl plik

@ -382,6 +382,9 @@ importers:
'@agentic/platform-validators': '@agentic/platform-validators':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/validators version: link:../../packages/validators
'@cfworker/json-schema':
specifier: ^4.1.1
version: 4.1.1
'@hono/zod-validator': '@hono/zod-validator':
specifier: 'catalog:' specifier: 'catalog:'
version: 0.6.0(hono@4.7.10)(zod@3.25.36) version: 0.6.0(hono@4.7.10)(zod@3.25.36)
@ -514,15 +517,27 @@ importers:
'@agentic/platform-types': '@agentic/platform-types':
specifier: workspace:* specifier: workspace:*
version: link:../types version: link:../types
'@apideck/better-ajv-errors':
specifier: ^0.3.6
version: 0.3.6(ajv@8.17.1)
'@redocly/openapi-core': '@redocly/openapi-core':
specifier: 'catalog:' specifier: 'catalog:'
version: 1.34.3(supports-color@10.0.0) 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: camelcase:
specifier: 'catalog:' specifier: 'catalog:'
version: 8.0.0 version: 8.0.0
decamelize: decamelize:
specifier: 'catalog:' specifier: 'catalog:'
version: 6.0.0 version: 6.0.0
fast-uri:
specifier: ^3.0.6
version: 3.0.6
devDependencies: devDependencies:
'@hono/node-server': '@hono/node-server':
specifier: 'catalog:' specifier: 'catalog:'
@ -613,6 +628,12 @@ importers:
packages: 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': '@asteasolutions/zod-to-openapi@7.3.0':
resolution: {integrity: sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==} resolution: {integrity: sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==}
peerDependencies: peerDependencies:
@ -630,6 +651,9 @@ packages:
resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==} resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@cfworker/json-schema@4.1.1':
resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==}
'@clack/core@0.5.0': '@clack/core@0.5.0':
resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==} resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==}
@ -1020,10 +1044,6 @@ packages:
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 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': '@eslint/js@9.27.0':
resolution: {integrity: sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==} resolution: {integrity: sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@ -1396,9 +1416,6 @@ packages:
resolution: {integrity: sha512-Q8nFIagNLIZgM2odAraelMcDssapc+lF+y3OlcIPxyAU+knefO8KmozGqfnma1xegRDP4z5M73ABsamn72bOcA==} resolution: {integrity: sha512-Q8nFIagNLIZgM2odAraelMcDssapc+lF+y3OlcIPxyAU+knefO8KmozGqfnma1xegRDP4z5M73ABsamn72bOcA==}
engines: {node: '>= 20'} engines: {node: '>= 20'}
'@octokit/openapi-types@25.0.0':
resolution: {integrity: sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==}
'@octokit/openapi-types@25.1.0': '@octokit/openapi-types@25.1.0':
resolution: {integrity: sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==} resolution: {integrity: sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==}
@ -1443,9 +1460,6 @@ packages:
resolution: {integrity: sha512-iYj4SJG/2bbhh+iIpFmG5u49DtJ4lipQ+aPakjL9OKpsGY93wM8w06gvFbEQxcMsZcCvk5th5KkIm2m8o14aWA==} resolution: {integrity: sha512-iYj4SJG/2bbhh+iIpFmG5u49DtJ4lipQ+aPakjL9OKpsGY93wM8w06gvFbEQxcMsZcCvk5th5KkIm2m8o14aWA==}
engines: {node: '>= 20'} engines: {node: '>= 20'}
'@octokit/types@14.0.0':
resolution: {integrity: sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==}
'@octokit/types@14.1.0': '@octokit/types@14.1.0':
resolution: {integrity: sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==} resolution: {integrity: sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==}
@ -1928,9 +1942,6 @@ packages:
'@types/mysql@2.15.26': '@types/mysql@2.15.26':
resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} 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': '@types/node@22.15.24':
resolution: {integrity: sha512-w9CZGm9RDjzTh/D+hFwlBJ3ziUaVw7oufKA3vOFSOZlzmW9AkZnfjPb+DLnrV6qtgL/LNmP0/2zBNCFHL3F0ng==} resolution: {integrity: sha512-w9CZGm9RDjzTh/D+hFwlBJ3ziUaVw7oufKA3vOFSOZlzmW9AkZnfjPb+DLnrV6qtgL/LNmP0/2zBNCFHL3F0ng==}
@ -3358,6 +3369,9 @@ packages:
json-schema-typed@8.0.1: json-schema-typed@8.0.1:
resolution: {integrity: sha512-XQmWYj2Sm4kn4WeTYvmpKEbyPsL7nBsb647c7pMe6l02/yx2+Jfc4dT6UZkEXnIUb5LhD55r2HPsJ1milQ4rDg==} resolution: {integrity: sha512-XQmWYj2Sm4kn4WeTYvmpKEbyPsL7nBsb647c7pMe6l02/yx2+Jfc4dT6UZkEXnIUb5LhD55r2HPsJ1milQ4rDg==}
json-schema@0.4.0:
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
json-stable-stringify-without-jsonify@1.0.1: json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
@ -3365,6 +3379,10 @@ packages:
resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
hasBin: true hasBin: true
jsonpointer@5.0.1:
resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
engines: {node: '>=0.10.0'}
jsx-ast-utils@3.3.5: jsx-ast-utils@3.3.5:
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
engines: {node: '>=4.0'} engines: {node: '>=4.0'}
@ -3395,6 +3413,10 @@ packages:
resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
engines: {node: '>=0.10'} 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: levn@0.4.1:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
@ -4666,6 +4688,13 @@ packages:
snapshots: 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)': '@asteasolutions/zod-to-openapi@7.3.0(zod@3.25.36)':
dependencies: dependencies:
openapi3-ts: 4.4.0 openapi3-ts: 4.4.0
@ -4683,6 +4712,8 @@ snapshots:
dependencies: dependencies:
regenerator-runtime: 0.14.1 regenerator-runtime: 0.14.1
'@cfworker/json-schema@4.1.1': {}
'@clack/core@0.5.0': '@clack/core@0.5.0':
dependencies: dependencies:
picocolors: 1.1.1 picocolors: 1.1.1
@ -4935,8 +4966,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@eslint/js@9.26.0': {}
'@eslint/js@9.27.0': {} '@eslint/js@9.27.0': {}
'@eslint/object-schema@2.1.6': {} '@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))': '@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: dependencies:
'@eslint/js': 9.26.0 '@eslint/js': 9.27.0
'@total-typescript/ts-reset': 0.6.1 '@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)) '@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) eslint: 9.27.0(jiti@2.4.2)
@ -5178,7 +5207,7 @@ snapshots:
'@octokit/core': 7.0.2 '@octokit/core': 7.0.2
'@octokit/oauth-app': 8.0.1 '@octokit/oauth-app': 8.0.1
'@octokit/plugin-paginate-rest': 13.0.0(@octokit/core@7.0.2) '@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/webhooks': 14.0.0
'@octokit/auth-app@8.0.1': '@octokit/auth-app@8.0.1':
@ -5187,7 +5216,7 @@ snapshots:
'@octokit/auth-oauth-user': 6.0.0 '@octokit/auth-oauth-user': 6.0.0
'@octokit/request': 10.0.2 '@octokit/request': 10.0.2
'@octokit/request-error': 7.0.0 '@octokit/request-error': 7.0.0
'@octokit/types': 14.0.0 '@octokit/types': 14.1.0
toad-cache: 3.7.0 toad-cache: 3.7.0
universal-github-app-jwt: 2.2.2 universal-github-app-jwt: 2.2.2
universal-user-agent: 7.0.3 universal-user-agent: 7.0.3
@ -5197,14 +5226,14 @@ snapshots:
'@octokit/auth-oauth-device': 8.0.1 '@octokit/auth-oauth-device': 8.0.1
'@octokit/auth-oauth-user': 6.0.0 '@octokit/auth-oauth-user': 6.0.0
'@octokit/request': 10.0.2 '@octokit/request': 10.0.2
'@octokit/types': 14.0.0 '@octokit/types': 14.1.0
universal-user-agent: 7.0.3 universal-user-agent: 7.0.3
'@octokit/auth-oauth-device@8.0.1': '@octokit/auth-oauth-device@8.0.1':
dependencies: dependencies:
'@octokit/oauth-methods': 6.0.0 '@octokit/oauth-methods': 6.0.0
'@octokit/request': 10.0.2 '@octokit/request': 10.0.2
'@octokit/types': 14.0.0 '@octokit/types': 14.1.0
universal-user-agent: 7.0.3 universal-user-agent: 7.0.3
'@octokit/auth-oauth-user@6.0.0': '@octokit/auth-oauth-user@6.0.0':
@ -5212,7 +5241,7 @@ snapshots:
'@octokit/auth-oauth-device': 8.0.1 '@octokit/auth-oauth-device': 8.0.1
'@octokit/oauth-methods': 6.0.0 '@octokit/oauth-methods': 6.0.0
'@octokit/request': 10.0.2 '@octokit/request': 10.0.2
'@octokit/types': 14.0.0 '@octokit/types': 14.1.0
universal-user-agent: 7.0.3 universal-user-agent: 7.0.3
'@octokit/auth-token@6.0.0': {} '@octokit/auth-token@6.0.0': {}
@ -5220,7 +5249,7 @@ snapshots:
'@octokit/auth-unauthenticated@7.0.1': '@octokit/auth-unauthenticated@7.0.1':
dependencies: dependencies:
'@octokit/request-error': 7.0.0 '@octokit/request-error': 7.0.0
'@octokit/types': 14.0.0 '@octokit/types': 14.1.0
'@octokit/core@7.0.2': '@octokit/core@7.0.2':
dependencies: dependencies:
@ -5228,19 +5257,19 @@ snapshots:
'@octokit/graphql': 9.0.1 '@octokit/graphql': 9.0.1
'@octokit/request': 10.0.2 '@octokit/request': 10.0.2
'@octokit/request-error': 7.0.0 '@octokit/request-error': 7.0.0
'@octokit/types': 14.0.0 '@octokit/types': 14.1.0
before-after-hook: 4.0.0 before-after-hook: 4.0.0
universal-user-agent: 7.0.3 universal-user-agent: 7.0.3
'@octokit/endpoint@11.0.0': '@octokit/endpoint@11.0.0':
dependencies: dependencies:
'@octokit/types': 14.0.0 '@octokit/types': 14.1.0
universal-user-agent: 7.0.3 universal-user-agent: 7.0.3
'@octokit/graphql@9.0.1': '@octokit/graphql@9.0.1':
dependencies: dependencies:
'@octokit/request': 10.0.2 '@octokit/request': 10.0.2
'@octokit/types': 14.0.0 '@octokit/types': 14.1.0
universal-user-agent: 7.0.3 universal-user-agent: 7.0.3
'@octokit/oauth-app@8.0.1': '@octokit/oauth-app@8.0.1':
@ -5261,9 +5290,7 @@ snapshots:
'@octokit/oauth-authorization-url': 8.0.0 '@octokit/oauth-authorization-url': 8.0.0
'@octokit/request': 10.0.2 '@octokit/request': 10.0.2
'@octokit/request-error': 7.0.0 '@octokit/request-error': 7.0.0
'@octokit/types': 14.0.0 '@octokit/types': 14.1.0
'@octokit/openapi-types@25.0.0': {}
'@octokit/openapi-types@25.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)': '@octokit/plugin-paginate-rest@13.0.0(@octokit/core@7.0.2)':
dependencies: dependencies:
'@octokit/core': 7.0.2 '@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)': '@octokit/plugin-rest-endpoint-methods@16.0.0(@octokit/core@7.0.2)':
dependencies: dependencies:
@ -5287,31 +5314,27 @@ snapshots:
dependencies: dependencies:
'@octokit/core': 7.0.2 '@octokit/core': 7.0.2
'@octokit/request-error': 7.0.0 '@octokit/request-error': 7.0.0
'@octokit/types': 14.0.0 '@octokit/types': 14.1.0
bottleneck: 2.19.5 bottleneck: 2.19.5
'@octokit/plugin-throttling@11.0.1(@octokit/core@7.0.2)': '@octokit/plugin-throttling@11.0.1(@octokit/core@7.0.2)':
dependencies: dependencies:
'@octokit/core': 7.0.2 '@octokit/core': 7.0.2
'@octokit/types': 14.0.0 '@octokit/types': 14.1.0
bottleneck: 2.19.5 bottleneck: 2.19.5
'@octokit/request-error@7.0.0': '@octokit/request-error@7.0.0':
dependencies: dependencies:
'@octokit/types': 14.0.0 '@octokit/types': 14.1.0
'@octokit/request@10.0.2': '@octokit/request@10.0.2':
dependencies: dependencies:
'@octokit/endpoint': 11.0.0 '@octokit/endpoint': 11.0.0
'@octokit/request-error': 7.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 fast-content-type-parse: 3.0.0
universal-user-agent: 7.0.3 universal-user-agent: 7.0.3
'@octokit/types@14.0.0':
dependencies:
'@octokit/openapi-types': 25.0.0
'@octokit/types@14.1.0': '@octokit/types@14.1.0':
dependencies: dependencies:
'@octokit/openapi-types': 25.1.0 '@octokit/openapi-types': 25.1.0
@ -5814,7 +5837,7 @@ snapshots:
'@types/connect@3.4.38': '@types/connect@3.4.38':
dependencies: dependencies:
'@types/node': 22.15.21 '@types/node': 22.15.24
'@types/estree@1.0.7': {} '@types/estree@1.0.7': {}
@ -5826,11 +5849,7 @@ snapshots:
'@types/mysql@2.15.26': '@types/mysql@2.15.26':
dependencies: dependencies:
'@types/node': 22.15.21 '@types/node': 22.15.24
'@types/node@22.15.21':
dependencies:
undici-types: 6.21.0
'@types/node@22.15.24': '@types/node@22.15.24':
dependencies: dependencies:
@ -5844,7 +5863,7 @@ snapshots:
'@types/pg@8.6.1': '@types/pg@8.6.1':
dependencies: dependencies:
'@types/node': 22.15.21 '@types/node': 22.15.24
pg-protocol: 1.10.0 pg-protocol: 1.10.0
pg-types: 2.2.0 pg-types: 2.2.0
@ -5854,7 +5873,7 @@ snapshots:
'@types/tedious@4.0.14': '@types/tedious@4.0.14':
dependencies: 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)': '@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: dependencies:
@ -7379,12 +7398,16 @@ snapshots:
json-schema-typed@8.0.1: {} json-schema-typed@8.0.1: {}
json-schema@0.4.0: {}
json-stable-stringify-without-jsonify@1.0.1: {} json-stable-stringify-without-jsonify@1.0.1: {}
json5@1.0.2: json5@1.0.2:
dependencies: dependencies:
minimist: 1.2.8 minimist: 1.2.8
jsonpointer@5.0.1: {}
jsx-ast-utils@3.3.5: jsx-ast-utils@3.3.5:
dependencies: dependencies:
array-includes: 3.1.8 array-includes: 3.1.8
@ -7425,6 +7448,8 @@ snapshots:
dependencies: dependencies:
language-subtag-registry: 0.3.23 language-subtag-registry: 0.3.23
leven@3.1.0: {}
levn@0.4.1: levn@0.4.1:
dependencies: dependencies:
prelude-ls: 1.2.1 prelude-ls: 1.2.1
@ -7657,7 +7682,7 @@ snapshots:
'@octokit/plugin-retry': 8.0.1(@octokit/core@7.0.2) '@octokit/plugin-retry': 8.0.1(@octokit/core@7.0.2)
'@octokit/plugin-throttling': 11.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/request-error': 7.0.0
'@octokit/types': 14.0.0 '@octokit/types': 14.1.0
'@octokit/webhooks': 14.0.0 '@octokit/webhooks': 14.0.0
ohash@2.0.11: {} ohash@2.0.11: {}