@@ -2,14 +2,15 @@ import { t, type AnyElysia, type TSchema, type InputSchema } from 'elysia'
22import type { HookContainer , StandardSchemaV1Like } from 'elysia/types'
33
44import type { OpenAPIV3 } from 'openapi-types'
5- import { Kind , type TProperties } from '@sinclair/typebox'
5+ import { Kind , TAnySchema , type TProperties } from '@sinclair/typebox'
66
77import type {
88 AdditionalReference ,
99 AdditionalReferences ,
1010 ElysiaOpenAPIConfig ,
1111 MapJsonSchema
1212} from './types'
13+ import { defineConfig } from 'tsup'
1314
1415export const capitalize = ( word : string ) =>
1516 word . charAt ( 0 ) . toUpperCase ( ) + word . slice ( 1 )
@@ -121,7 +122,7 @@ const unwrapReference = <T extends OpenAPIV3.SchemaObject | undefined>(
121122 const name = ref . slice ( ref . lastIndexOf ( '/' ) + 1 )
122123 if ( ref && definitions [ name ] ) schema = definitions [ name ] as T
123124
124- return schema as any
125+ return enumToOpenApi ( schema ) as any
125126}
126127
127128export const unwrapSchema = (
@@ -131,15 +132,15 @@ export const unwrapSchema = (
131132 if ( ! schema ) return
132133
133134 if ( typeof schema === 'string' ) schema = toRef ( schema )
134- if ( Kind in schema ) return schema
135+ if ( Kind in schema ) return enumToOpenApi ( schema )
135136
136137 if ( Kind in schema || ! schema ?. [ '~standard' ] ) return
137138
138139 // @ts -ignore
139140 const vendor = schema [ '~standard' ] . vendor
140141
141142 if ( mapJsonSchema ?. [ vendor ] && typeof mapJsonSchema [ vendor ] === 'function' )
142- return mapJsonSchema [ vendor ] ( schema )
143+ return enumToOpenApi ( mapJsonSchema [ vendor ] ( schema ) )
143144
144145 switch ( vendor ) {
145146 case 'zod' :
@@ -197,52 +198,62 @@ export const unwrapSchema = (
197198
198199 if ( vendor === 'arktype' )
199200 // @ts -ignore
200- return schema ?. toJsonSchema ?.( )
201+ return enumToOpenApi ( schema ?. toJsonSchema ?.( ) )
201202
202203 // @ts -ignore
203- return schema . toJSONSchema ?.( ) ?? schema ?. toJsonSchema ?.( )
204+ return enumToOpenApi ( schema . toJSONSchema ?.( ) ?? schema ?. toJsonSchema ?.( ) )
204205}
205206
206- export const convertEnumToOpenApi = ( schema : any ) : any => {
207- if ( ! schema || typeof schema !== 'object' ) return schema
208-
209- if (
210- schema [ Kind ] === 'Union' &&
211- schema . anyOf &&
212- Array . isArray ( schema . anyOf ) &&
213- schema . anyOf . length > 0 &&
214- schema . anyOf . every (
215- ( item : any ) =>
216- item && typeof item === 'object' && item . const !== undefined
217- )
218- ) {
219- const enumValues = schema . anyOf . map ( ( item : any ) => item . const )
207+ export const enumToOpenApi = <
208+ T extends
209+ | TAnySchema
210+ | OpenAPIV3 . SchemaObject
211+ | OpenAPIV3 . ReferenceObject
212+ | undefined
213+ > (
214+ _schema : T
215+ ) : T => {
216+ if ( ! _schema || typeof _schema !== 'object' ) return _schema
220217
221- return {
222- type : 'string' ,
223- enum : enumValues
224- }
218+ if ( Kind in _schema ) {
219+ const schema = _schema as TAnySchema
220+
221+ if (
222+ schema [ Kind ] === 'Union' &&
223+ schema . anyOf &&
224+ Array . isArray ( schema . anyOf ) &&
225+ schema . anyOf . length > 0 &&
226+ schema . anyOf . every (
227+ ( item ) =>
228+ item && typeof item === 'object' && item . const !== undefined
229+ )
230+ )
231+ return {
232+ type : 'string' ,
233+ enum : schema . anyOf . map ( ( item ) => item . const )
234+ } as any
225235 }
226236
237+ const schema = _schema as OpenAPIV3 . SchemaObject
238+
227239 if ( schema . type === 'object' && schema . properties ) {
228- const convertedProperties : any = { }
229- for ( const [ key , value ] of Object . entries ( schema . properties ) ) {
230- convertedProperties [ key ] = convertEnumToOpenApi ( value )
231- }
240+ const properties : Record < string , unknown > = { }
241+ for ( const [ key , value ] of Object . entries ( schema . properties ) )
242+ properties [ key ] = enumToOpenApi ( value )
243+
232244 return {
233245 ...schema ,
234- properties : convertedProperties
235- }
246+ properties
247+ } as T
236248 }
237249
238- if ( schema . type === 'array' && schema . items ) {
250+ if ( schema . type === 'array' && schema . items )
239251 return {
240252 ...schema ,
241- items : convertEnumToOpenApi ( schema . items )
242- }
243- }
253+ items : enumToOpenApi ( schema . items )
254+ } as T
244255
245- return schema
256+ return schema as T
246257}
247258
248259/**
@@ -337,7 +348,6 @@ export function toOpenAPISchema(
337348 ( hooks . response as TSchema ) . $ref ||
338349 ( hooks . response as any ) [ '~standard' ]
339350 )
340- // @ts -ignore
341351 hooks . response = {
342352 200 : hooks . response as any
343353 }
@@ -384,25 +394,16 @@ export function toOpenAPISchema(
384394 definitions
385395 )
386396
387- if ( params && params . type === 'object' && params . properties ) {
388- const convertedProperties : any = { }
397+ if ( params && params . type === 'object' && params . properties )
389398 for ( const [ paramName , paramSchema ] of Object . entries (
390399 params . properties
391- ) ) {
392- convertedProperties [ paramName ] =
393- convertEnumToOpenApi ( paramSchema )
394- }
395-
396- for ( const [ paramName , paramSchema ] of Object . entries (
397- convertedProperties
398400 ) )
399401 parameters . push ( {
400402 name : paramName ,
401403 in : 'path' ,
402404 required : true , // Path parameters are always required
403405 schema : paramSchema
404406 } )
405- }
406407 }
407408
408409 // Handle query parameters
@@ -413,17 +414,9 @@ export function toOpenAPISchema(
413414 )
414415
415416 if ( query && query . type === 'object' && query . properties ) {
416- const convertedProperties : any = { }
417- for ( const [ queryName , querySchema ] of Object . entries (
418- query . properties
419- ) ) {
420- convertedProperties [ queryName ] =
421- convertEnumToOpenApi ( querySchema )
422- }
423-
424417 const required = query . required || [ ]
425418 for ( const [ queryName , querySchema ] of Object . entries (
426- convertedProperties
419+ query . properties
427420 ) )
428421 parameters . push ( {
429422 name : queryName ,
@@ -442,17 +435,9 @@ export function toOpenAPISchema(
442435 )
443436
444437 if ( headers && headers . type === 'object' && headers . properties ) {
445- const convertedProperties : any = { }
446- for ( const [ headerName , headerSchema ] of Object . entries (
447- headers . properties
448- ) ) {
449- convertedProperties [ headerName ] =
450- convertEnumToOpenApi ( headerSchema )
451- }
452-
453438 const required = headers . required || [ ]
454439 for ( const [ headerName , headerSchema ] of Object . entries (
455- convertedProperties
440+ headers . properties
456441 ) )
457442 parameters . push ( {
458443 name : headerName ,
@@ -471,17 +456,9 @@ export function toOpenAPISchema(
471456 )
472457
473458 if ( cookie && cookie . type === 'object' && cookie . properties ) {
474- const convertedProperties : any = { }
475- for ( const [ cookieName , cookieSchema ] of Object . entries (
476- cookie . properties
477- ) ) {
478- convertedProperties [ cookieName ] =
479- convertEnumToOpenApi ( cookieSchema )
480- }
481-
482459 const required = cookie . required || [ ]
483460 for ( const [ cookieName , cookieSchema ] of Object . entries (
484- convertedProperties
461+ cookie . properties
485462 ) )
486463 parameters . push ( {
487464 name : cookieName ,
@@ -500,11 +477,11 @@ export function toOpenAPISchema(
500477 const body = unwrapSchema ( hooks . body , vendors )
501478
502479 if ( body ) {
503- const convertedBody = convertEnumToOpenApi ( body )
504-
505480 // @ts -ignore
506- const { type : _type , description, $ref, ...options } = convertedBody
507- const type = _type as string | undefined
481+ const { type, description, $ref, ...options } = unwrapReference (
482+ body ,
483+ definitions
484+ )
508485
509486 // @ts -ignore
510487 if ( hooks . parse ) {
@@ -522,24 +499,26 @@ export function toOpenAPISchema(
522499 switch ( parser . fn ) {
523500 case 'text' :
524501 case 'text/plain' :
525- content [ 'text/plain' ] = { schema : convertedBody }
502+ content [ 'text/plain' ] = { schema : body }
526503 continue
527504
528505 case 'urlencoded' :
529506 case 'application/x-www-form-urlencoded' :
530507 content [ 'application/x-www-form-urlencoded' ] = {
531- schema : convertedBody
508+ schema : body
532509 }
533510 continue
534511
535512 case 'json' :
536513 case 'application/json' :
537- content [ 'application/json' ] = { schema : convertedBody }
514+ content [ 'application/json' ] = { schema : body }
538515 continue
539516
540517 case 'formdata' :
541518 case 'multipart/form-data' :
542- content [ 'multipart/form-data' ] = { schema : convertedBody }
519+ content [ 'multipart/form-data' ] = {
520+ schema : body
521+ }
543522 continue
544523 }
545524 }
@@ -558,17 +537,19 @@ export function toOpenAPISchema(
558537 type === 'integer' ||
559538 type === 'boolean'
560539 ? {
561- 'text/plain' : convertedBody
540+ 'text/plain' : {
541+ schema : body
542+ }
562543 }
563544 : {
564545 'application/json' : {
565- schema : convertedBody
546+ schema : body
566547 } ,
567548 'application/x-www-form-urlencoded' : {
568- schema : convertedBody
549+ schema : body
569550 } ,
570551 'multipart/form-data' : {
571- schema : convertedBody
552+ schema : body
572553 }
573554 } ,
574555 required : true
@@ -593,10 +574,9 @@ export function toOpenAPISchema(
593574
594575 if ( ! response ) continue
595576
596- const convertedResponse = convertEnumToOpenApi ( response )
597577 // @ts -ignore Must exclude $ref from root options
598- const { type : _type , description, $ref, ...options } = convertedResponse
599- const type = _type as string | undefined
578+ const { type, description, $ref, ...options } =
579+ unwrapReference ( response , definitions )
600580
601581 operation . responses [ status ] = {
602582 description :
@@ -605,19 +585,19 @@ export function toOpenAPISchema(
605585 type === 'void' ||
606586 type === 'null' ||
607587 type === 'undefined'
608- ? ( convertedResponse as any )
588+ ? ( { type , description } as any )
609589 : type === 'string' ||
610590 type === 'number' ||
611591 type === 'integer' ||
612592 type === 'boolean'
613593 ? {
614594 'text/plain' : {
615- schema : convertedResponse
595+ schema : response
616596 }
617597 }
618598 : {
619599 'application/json' : {
620- schema : convertedResponse
600+ schema : response
621601 }
622602 }
623603 }
@@ -626,14 +606,12 @@ export function toOpenAPISchema(
626606 const response = unwrapSchema ( hooks . response as any , vendors )
627607
628608 if ( response ) {
629- const convertedResponse = convertEnumToOpenApi ( response )
630-
631609 // @ts -ignore
632610 const {
633611 type : _type ,
634612 description,
635613 ...options
636- } = convertedResponse
614+ } = unwrapReference ( response , definitions )
637615 const type = _type as string | undefined
638616
639617 // It's a single schema, default to 200
@@ -643,19 +621,19 @@ export function toOpenAPISchema(
643621 type === 'void' ||
644622 type === 'null' ||
645623 type === 'undefined'
646- ? ( convertedResponse as any )
624+ ? ( { type , description } as any )
647625 : type === 'string' ||
648626 type === 'number' ||
649627 type === 'integer' ||
650628 type === 'boolean'
651629 ? {
652630 'text/plain' : {
653- schema : convertedResponse
631+ schema : response
654632 }
655633 }
656634 : {
657635 'application/json' : {
658- schema : convertedResponse
636+ schema : response
659637 }
660638 }
661639 }
@@ -664,7 +642,8 @@ export function toOpenAPISchema(
664642 }
665643
666644 for ( let path of getPossiblePath ( route . path ) ) {
667- const operationId = toOperationId ( route . method , path )
645+ const operationId =
646+ hooks . detail ?. operationId ?? toOperationId ( route . method , path )
668647
669648 path = path . replace ( / : ( [ ^ / ] + ) / g, '{$1}' )
670649
0 commit comments