Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sequence_sidekick",
"version": "v1.0.19-beta",
"version": "v1.0.20-beta",
"module": "index.ts",
"type": "module",
"scripts": {
Expand All @@ -21,9 +21,11 @@
"lint": "biome check .",
"lint:fix": "biome check . --write",
"tag:release": "sh ./scripts/tag-release.sh",
"check:openapi": "tsx scripts/check-openapi.ts",
"postinstall": "prisma generate"
},
"devDependencies": {
"@apidevtools/swagger-parser": "^10.1.1",
"@biomejs/biome": "1.9.4",
"@types/js-yaml": "^4.0.9",
"@types/node": "^22.13.1",
Expand Down
67 changes: 67 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

150 changes: 150 additions & 0 deletions scripts/check-openapi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#!/usr/bin/env tsx
/**
* Script to validate OpenAPI specification compatibility
* Fetches the OpenAPI spec from a running server and validates it
*/

import SwaggerParser from '@apidevtools/swagger-parser'

// Get server URL from environment or use default
const SERVER_URL = process.env.SERVER_URL || 'http://localhost:7500'

// Common OpenAPI endpoint paths
const POSSIBLE_ENDPOINTS = [
'/documentation/json', // Fastify Swagger UI default
'/openapi.json',
'/swagger.json',
'/documentation/openapi.json'
]

async function fetchOpenAPISpec(): Promise<Record<string, unknown>> {
let lastError: Error | null = null

for (const endpoint of POSSIBLE_ENDPOINTS) {
const url = `${SERVER_URL}${endpoint}`
try {
const response = await fetch(url)
if (response.ok) {
const spec = (await response.json()) as Record<string, unknown>
console.log(`✓ Found OpenAPI spec at: ${url}\n`)
return spec
}
} catch (error) {
if (error instanceof Error) {
lastError = error
}
// Try next endpoint
continue
}
}

// If we get here, none of the endpoints worked
if (lastError && lastError.message.includes('fetch failed')) {
throw new Error(
`Could not connect to server at ${SERVER_URL}. Make sure the server is running.\n` +
`You can start it with: pnpm run dev`
)
}

throw new Error(
`Could not find OpenAPI spec at any of the expected endpoints:\n` +
POSSIBLE_ENDPOINTS.map((ep) => ` - ${SERVER_URL}${ep}`).join('\n') +
`\n\nMake sure the server is running and Swagger is properly configured.`
)
}

async function validateOpenAPI(spec: Record<string, unknown>): Promise<void> {
try {
// Validate the spec structure and references
// Cast to any to satisfy SwaggerParser's type requirements
const api = await SwaggerParser.validate(spec as any, {
validate: {
spec: true,
schema: true
},
dereference: {
circular: false
}
})

// Type guard for API document
const apiDoc = api as unknown as Record<string, unknown>
const info = apiDoc.info as Record<string, unknown> | undefined
const paths = apiDoc.paths as Record<string, unknown> | undefined

console.log('✅ OpenAPI specification is valid!')
console.log(` Title: ${info?.title || 'N/A'}`)
console.log(` Version: ${info?.version || 'N/A'}`)
console.log(
` OpenAPI Version: ${apiDoc.openapi || apiDoc.swagger || 'N/A'}`
)
console.log(` Paths: ${Object.keys(paths || {}).length}`)

// Check for common issues
const warnings: string[] = []

if (!paths || Object.keys(paths).length === 0) {
warnings.push('⚠️ No API paths defined')
}

// Check for missing response schemas
for (const [path, pathItem] of Object.entries(paths || {})) {
if (!pathItem) continue
for (const [method, operation] of Object.entries(pathItem)) {
if (
typeof operation === 'object' &&
operation !== null &&
'responses' in operation
) {
const responses = operation.responses
if (!responses || Object.keys(responses).length === 0) {
warnings.push(
`⚠️ ${method.toUpperCase()} ${path} has no response definitions`
)
}
}
}
}

if (warnings.length > 0) {
console.log('\n⚠️ Warnings:')
warnings.forEach((warning) => console.log(` ${warning}`))
}

console.log('\n✅ All validation checks passed!')
} catch (error) {
if (error instanceof Error) {
console.error('❌ OpenAPI validation failed:')
console.error(` ${error.message}`)
if ('details' in error && Array.isArray(error.details)) {
console.error('\n Details:')
error.details.forEach((detail: unknown) => {
if (typeof detail === 'object' && detail !== null) {
console.error(` - ${JSON.stringify(detail, null, 2)}`)
}
})
}
} else {
console.error('❌ Unknown validation error:', error)
}
process.exit(1)
}
}

async function main() {
console.log(`🔍 Validating OpenAPI specification from ${SERVER_URL}...\n`)

try {
const spec = await fetchOpenAPISpec()
await validateOpenAPI(spec)
} catch (error) {
if (error instanceof Error) {
console.error('❌ Error:', error.message)
} else {
console.error('❌ Unknown error:', error)
}
process.exit(1)
}
}

main()
15 changes: 12 additions & 3 deletions src/routes/contract/utils/debug/getRawTrace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,28 @@ const debugTraceTransactionSchema = {
response: {
200: Type.Object({
result: Type.Object({
trace: Type.Any(),
trace: {
type: 'object',
nullable: true
} as any,
error: Type.Optional(Type.String())
})
}),
'4xx': Type.Object({
result: Type.Object({
trace: Type.Null(),
trace: {
type: 'object',
nullable: true
} as any,
error: Type.String()
})
}),
500: Type.Object({
result: Type.Object({
trace: Type.Null(),
trace: {
type: 'object',
nullable: true
} as any,
error: Type.String()
})
})
Expand Down
10 changes: 8 additions & 2 deletions src/routes/contract/utils/relayer/getTxHashForMetaTxHash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,21 @@ const getTxHashForMetaTxHashSchema = {
200: Type.Object({
result: Type.Object({
data: Type.Object({
txHash: Type.String()
txHash: {
type: 'string',
nullable: true
} as any
})
}),
error: Type.Optional(Type.String())
}),
500: Type.Object({
result: Type.Object({
data: Type.Object({
txHash: Type.Null()
txHash: {
type: 'string',
nullable: true
} as any
}),
error: Type.String()
})
Expand Down
Loading