Skip to content

Valibot metadata.ref fails with incorrect replacement of #/$defs #8

@jagregory

Description

@jagregory

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.

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions