-
Notifications
You must be signed in to change notification settings - Fork 5
Description
I'm using hono-openapi, but I think this is just a standard-openapi issue. I'm using Valibot with no specific JSON Schema or OpenAPI customisations, just vanilla Valibot schemas passed to hono-openapi using resolve.
If I have Valibot schemas like so:
const PhoneNumberSchema = pipe(
object({
number: string(),
}),
metadata({
ref: "PhoneNumber",
}),
);
const PersonSchema = object({
id: string(),
phoneNumber: PhoneNumberSchema,
});This works fine. I get an OpenAPI schema with a $ref to the correct location for PhoneNumber #/components/schemas/PhoneNumber.
However, if I add a ref to the Person schema, or in fact any metadata:
const PhoneNumberSchema = pipe(
object({
number: string(),
}),
metadata({
ref: "PhoneNumber",
}),
);
const PersonSchema = pipe(
object({
id: string(),
phoneNumber: PhoneNumberSchema,
}),
metadata({
ref: "Person", // or even without this
}),
);When I generate an OpenAPI schema via hono-openapi I get an schema which has an invalid $ref in it:
"phoneNumber": {
"$ref": "#/components/schemas/ents/schemas/PhoneNumber"
}It looks like to me that the overrideAction for valibot creates a valid ref, but then the convertToOpenAPISchema function interprets the $ref to be an Effect thing and incorrectly tries to replace #/defs in the ref path.
More debugging below if it's helpful
More debugging
I added some tactical console logs to the overrideAction function in the valibot vendor code, and to the convertToOpenAPISchema in convert.ts.
overrideAction: {
valibotAction: {
kind: 'metadata',
type: 'metadata',
reference: [Function: metadata],
metadata: { ref: 'PhoneNumber' }
},
jsonSchema: {
type: 'object',
properties: { number: { type: 'string' } },
required: [ 'number' ]
}
}
convertToOpenAPISchema: { type: 'string' }
convertToOpenAPISchema: {
type: 'object',
properties: { number: { type: 'string' } },
required: [ 'number' ]
}
overrideAction: {
valibotAction: {
kind: 'metadata',
type: 'metadata',
reference: [Function: metadata],
metadata: {}
},
jsonSchema: {
'$schema': 'http://json-schema.org/draft-07/schema#',
type: 'object',
properties: {
id: { type: 'string' },
phoneNumber: { '$ref': '#/components/schemas/PhoneNumber' }
},
required: [ 'id', 'phoneNumber' ]
}
}
convertToOpenAPISchema: { type: 'string' }
convertToOpenAPISchema: { '$ref': '#/components/schemas/PhoneNumber' }
Stripping #/$defs/ prefix from $ref: #/components/schemas/PhoneNumber
convertToOpenAPISchema: {
type: 'object',
properties: {
id: { type: 'string' },
phoneNumber: { '$ref': '#/components/schemas/ents/schemas/PhoneNumber' }
},
required: [ 'id', 'phoneNumber' ]
}
You can see that the convertAction immediately returns the correct ref path which replaces the PhoneNumber schema inside Person. Then presumably as the convertToOpenAPISchema function is applied recursively it receives just the object with the $ref key and triggers what looks like logic for Effect, incorrectly manipulating the schemas path.