@@ -65,10 +65,10 @@ export interface OpenAPIContext {
6565 method : Method ;
6666}
6767
68- export type Overrider = (
68+ export type Depicter = (
6969 zodCtx : { zodSchema : $ZodType ; jsonSchema : JSONSchema . BaseSchema } ,
7070 oasCtx : OpenAPIContext ,
71- ) => void ;
71+ ) => JSONSchema . BaseSchema | SchemaObject ;
7272
7373/** @desc Using defaultIsHeader when returns null or undefined */
7474export type IsHeader = (
@@ -77,7 +77,7 @@ export type IsHeader = (
7777 path : string ,
7878) => boolean | null | undefined ;
7979
80- export type BrandHandling = Record < string | symbol , Overrider > ;
80+ export type BrandHandling = Record < string | symbol , Depicter > ;
8181
8282interface ReqResHandlingProps < S extends $ZodType >
8383 extends Omit < OpenAPIContext , "isResponse" > {
@@ -104,34 +104,36 @@ const samples = {
104104export const reformatParamsInPath = ( path : string ) =>
105105 path . replace ( routePathParamsRegex , ( param ) => `{${ param . slice ( 1 ) } }` ) ;
106106
107- export const onDefault : Overrider = ( { zodSchema, jsonSchema } ) =>
108- ( jsonSchema . default =
107+ export const onDefault : Depicter = ( { zodSchema, jsonSchema } ) => ( {
108+ ...jsonSchema ,
109+ default :
109110 globalRegistry . get ( zodSchema ) ?. [ metaSymbol ] ?. defaultLabel ??
110- jsonSchema . default ) ;
111+ jsonSchema . default ,
112+ } ) ;
111113
112- export const onUpload : Overrider = ( { jsonSchema } , ctx ) => {
114+ export const onUpload : Depicter = ( { } , ctx ) => {
113115 if ( ctx . isResponse )
114116 throw new DocumentationError ( "Please use ez.upload() only for input." , ctx ) ;
115- Object . assign ( jsonSchema , { type : "string" , format : "binary" } ) ;
117+ return { type : "string" , format : "binary" } ;
116118} ;
117119
118- export const onFile : Overrider = ( { jsonSchema } ) => {
119- delete jsonSchema . anyOf ; // undo default
120- Object . assign ( jsonSchema , {
121- type : "string" ,
122- format :
123- jsonSchema . type === "string"
124- ? jsonSchema . format === "base64"
125- ? "byte"
126- : "file"
127- : "binary" ,
128- } ) ;
129- } ;
120+ export const onFile : Depicter = ( { jsonSchema } ) => ( {
121+ type : "string" ,
122+ format :
123+ jsonSchema . type === "string"
124+ ? jsonSchema . format === "base64"
125+ ? "byte"
126+ : "file"
127+ : "binary" ,
128+ } ) ;
130129
131- export const onUnion : Overrider = ( { zodSchema, jsonSchema } ) => {
132- if ( ! zodSchema . _zod . disc ) return ;
130+ export const onUnion : Depicter = ( { zodSchema, jsonSchema } ) => {
131+ if ( ! zodSchema . _zod . disc ) return jsonSchema ;
133132 const propertyName = Array . from ( zodSchema . _zod . disc . keys ( ) ) . pop ( ) ;
134- jsonSchema . discriminator ??= { propertyName } ;
133+ return {
134+ ...jsonSchema ,
135+ discriminator : jsonSchema . discriminator ?? { propertyName } ,
136+ } ;
135137} ;
136138
137139const propsMerger = ( a : unknown , b : unknown ) => {
@@ -180,22 +182,20 @@ const intersect = (
180182 return R . map ( ( fn ) => fn ( left , right ) , suitable ) ;
181183} ;
182184
183- export const onIntersection : Overrider = ( { jsonSchema } ) => {
184- if ( ! jsonSchema . allOf ) return ;
185- try {
186- const attempt = intersect ( jsonSchema . allOf ) ;
187- delete jsonSchema . allOf ; // undo default
188- Object . assign ( jsonSchema , attempt ) ;
189- } catch { }
185+ export const onIntersection : Depicter = ( { jsonSchema } ) => {
186+ if ( jsonSchema . allOf ) {
187+ try {
188+ return intersect ( jsonSchema . allOf ) ;
189+ } catch { }
190+ }
191+ return jsonSchema ;
190192} ;
191193
192194/** @since OAS 3.1 nullable replaced with type array having null */
193- export const onNullable : Overrider = ( { jsonSchema } ) => {
194- if ( ! jsonSchema . anyOf ) return ;
195+ export const onNullable : Depicter = ( { jsonSchema } ) => {
196+ if ( ! jsonSchema . anyOf ) return jsonSchema ;
195197 const original = jsonSchema . anyOf [ 0 ] ;
196- Object . assign ( original , { type : makeNullableType ( original . type ) } ) ;
197- Object . assign ( jsonSchema , original ) ;
198- delete jsonSchema . anyOf ;
198+ return Object . assign ( original , { type : makeNullableType ( original . type ) } ) ;
199199} ;
200200
201201const isSupportedType = ( subject : string ) : subject is SchemaObjectType =>
@@ -226,49 +226,47 @@ const ensureCompliance = ({
226226 return valid ;
227227} ;
228228
229- export const onDateIn : Overrider = ( { jsonSchema } , ctx ) => {
229+ export const onDateIn : Depicter = ( { } , ctx ) => {
230230 if ( ctx . isResponse )
231231 throw new DocumentationError ( "Please use ez.dateOut() for output." , ctx ) ;
232- delete jsonSchema . anyOf ; // undo default
233- Object . assign ( jsonSchema , {
232+ return {
234233 description : "YYYY-MM-DDTHH:mm:ss.sssZ" ,
235234 type : "string" ,
236235 format : "date-time" ,
237236 pattern : / ^ \d { 4 } - \d { 2 } - \d { 2 } ( T \d { 2 } : \d { 2 } : \d { 2 } ( \. \d + ) ? ) ? Z ? $ / . source ,
238237 externalDocs : {
239238 url : isoDateDocumentationUrl ,
240239 } ,
241- } ) ;
240+ } ;
242241} ;
243242
244- export const onDateOut : Overrider = ( { jsonSchema } , ctx ) => {
243+ export const onDateOut : Depicter = ( { } , ctx ) => {
245244 if ( ! ctx . isResponse )
246245 throw new DocumentationError ( "Please use ez.dateIn() for input." , ctx ) ;
247- Object . assign ( jsonSchema , {
246+ return {
248247 description : "YYYY-MM-DDTHH:mm:ss.sssZ" ,
249248 type : "string" ,
250249 format : "date-time" ,
251250 externalDocs : {
252251 url : isoDateDocumentationUrl ,
253252 } ,
254- } ) ;
253+ } ;
255254} ;
256255
257- export const onBigInt : Overrider = ( { jsonSchema } ) =>
258- Object . assign ( jsonSchema , {
259- type : "string" ,
260- format : "bigint" ,
261- pattern : / ^ - ? \d + $ / . source ,
262- } ) ;
256+ export const onBigInt : Depicter = ( ) => ( {
257+ type : "string" ,
258+ format : "bigint" ,
259+ pattern : / ^ - ? \d + $ / . source ,
260+ } ) ;
263261
264262/**
265263 * @since OAS 3.1 using prefixItems for depicting tuples
266264 * @since 17.5.0 added rest handling, fixed tuple type
267265 */
268- export const onTuple : Overrider = ( { zodSchema, jsonSchema } ) => {
269- if ( ( zodSchema as $ZodTuple ) . _zod . def . rest !== null ) return ;
266+ export const onTuple : Depicter = ( { zodSchema, jsonSchema } ) => {
267+ if ( ( zodSchema as $ZodTuple ) . _zod . def . rest !== null ) return jsonSchema ;
270268 // does not appear to support items:false, so not:{} is a recommended alias
271- jsonSchema . items = { not : { } } ;
269+ return { ... jsonSchema , items : { not : { } } } ;
272270} ;
273271
274272const makeSample = ( depicted : SchemaObject ) => {
@@ -292,44 +290,43 @@ const makeNullableType = (
292290 ) ;
293291} ;
294292
295- export const onPipeline : Overrider = ( { zodSchema, jsonSchema } , ctx ) => {
293+ export const onPipeline : Depicter = ( { zodSchema, jsonSchema } , ctx ) => {
296294 const target = ( zodSchema as $ZodPipe ) . _zod . def [
297295 ctx . isResponse ? "out" : "in"
298296 ] ;
299297 const opposite = ( zodSchema as $ZodPipe ) . _zod . def [
300298 ctx . isResponse ? "in" : "out"
301299 ] ;
302- if ( ! isSchema < $ZodTransform > ( target , "transform" ) ) return ;
300+ if ( ! isSchema < $ZodTransform > ( target , "transform" ) ) return jsonSchema ;
303301 const opposingDepiction = depict ( opposite , { ctx } ) ;
304302 if ( isSchemaObject ( opposingDepiction ) ) {
305303 if ( ! ctx . isResponse ) {
306304 const { type : opposingType , ...rest } = opposingDepiction ;
307- Object . assign ( jsonSchema , {
305+ return {
308306 ...rest ,
309307 format : `${ rest . format || opposingType } (preprocessed)` ,
310- } ) ;
308+ } ;
311309 } else {
312310 const targetType = getTransformedType (
313311 target ,
314312 makeSample ( opposingDepiction ) ,
315313 ) ;
316314 if ( targetType && [ "number" , "string" , "boolean" ] . includes ( targetType ) ) {
317- Object . assign ( jsonSchema , {
315+ return {
318316 type : targetType as "number" | "string" | "boolean" ,
319- } ) ;
317+ } ;
320318 }
321319 }
322320 }
321+ return jsonSchema ;
323322} ;
324323
325- export const onRaw : Overrider = ( { jsonSchema } ) => {
326- if ( jsonSchema . type !== "object" ) return ;
324+ export const onRaw : Depicter = ( { jsonSchema } ) => {
325+ if ( jsonSchema . type !== "object" ) return jsonSchema ;
327326 const objSchema = jsonSchema as JSONSchema . ObjectSchema ;
328- if ( ! objSchema . properties ) return ;
329- if ( ! ( "raw" in objSchema . properties ) ) return ;
330- Object . assign ( jsonSchema , objSchema . properties . raw ) ;
331- delete jsonSchema . properties ; // undo default
332- delete jsonSchema . required ;
327+ if ( ! objSchema . properties ) return jsonSchema ;
328+ if ( ! ( "raw" in objSchema . properties ) ) return jsonSchema ;
329+ return objSchema . properties . raw ;
333330} ;
334331
335332const enumerateExamples = ( examples : unknown [ ] ) : ExamplesObject | undefined =>
@@ -425,7 +422,7 @@ export const depictRequestParams = ({
425422 : undefined ;
426423 if ( ! location ) return acc ;
427424 const depicted = depict ( paramSchema , {
428- rules : { ...brandHandling , ...overrides } ,
425+ rules : { ...brandHandling , ...depicters } ,
429426 ctx : { isResponse : false , makeRef, path, method } ,
430427 } ) ;
431428 const result =
@@ -446,7 +443,7 @@ export const depictRequestParams = ({
446443 ) ;
447444} ;
448445
449- const overrides : Partial < Record < FirstPartyKind | ProprietaryBrand , Overrider > > =
446+ const depicters : Partial < Record < FirstPartyKind | ProprietaryBrand , Depicter > > =
450447 {
451448 nullable : onNullable ,
452449 default : onDefault ,
@@ -462,15 +459,17 @@ const overrides: Partial<Record<FirstPartyKind | ProprietaryBrand, Overrider>> =
462459 [ ezRawBrand ] : onRaw ,
463460 } ;
464461
465- const onEach : Overrider = ( { zodSchema, jsonSchema } , { isResponse } ) => {
462+ const onEach : Depicter = ( { zodSchema, jsonSchema } , { isResponse } ) => {
463+ const result = { ...jsonSchema } ;
466464 if ( ! isResponse && doesAccept ( zodSchema , null ) )
467- Object . assign ( jsonSchema , { type : makeNullableType ( jsonSchema . type ) } ) ;
465+ Object . assign ( result , { type : makeNullableType ( jsonSchema . type ) } ) ;
468466 const examples = getExamples ( {
469467 schema : zodSchema ,
470468 variant : isResponse ? "parsed" : "original" ,
471469 validate : true ,
472470 } ) ;
473- if ( examples . length ) jsonSchema . examples = examples . slice ( ) ;
471+ if ( examples . length ) result . examples = examples . slice ( ) ;
472+ return result ;
474473} ;
475474
476475/**
@@ -514,7 +513,7 @@ const unref = (
514513
515514const depict = (
516515 subject : $ZodType ,
517- { ctx, rules = overrides } : { ctx : OpenAPIContext ; rules ?: BrandHandling } ,
516+ { ctx, rules = depicters } : { ctx : OpenAPIContext ; rules ?: BrandHandling } ,
518517) => {
519518 const { $defs = { } , properties = { } } = z . toJSONSchema (
520519 z . object ( { subject } ) , // avoiding "document root" references
@@ -525,10 +524,16 @@ const depict = (
525524 unref ( zodCtx . jsonSchema ) ;
526525 const { brand } =
527526 globalRegistry . get ( zodCtx . zodSchema ) ?. [ metaSymbol ] ?? { } ;
528- rules [
529- brand && brand in rules ? brand : zodCtx . zodSchema . _zod . def . type
530- ] ?.( zodCtx , ctx ) ;
531- onEach ( zodCtx , ctx ) ;
527+ const depicter =
528+ rules [
529+ brand && brand in rules ? brand : zodCtx . zodSchema . _zod . def . type
530+ ] ;
531+ if ( depicter ) {
532+ const overrides = { ...depicter ( zodCtx , ctx ) } ;
533+ for ( const key in zodCtx . jsonSchema ) delete zodCtx . jsonSchema [ key ] ;
534+ Object . assign ( zodCtx . jsonSchema , overrides ) ;
535+ }
536+ Object . assign ( zodCtx . jsonSchema , onEach ( zodCtx , ctx ) ) ;
532537 } ,
533538 } ,
534539 ) as JSONSchema . ObjectSchema ;
@@ -587,7 +592,7 @@ export const depictResponse = ({
587592 if ( ! mimeTypes ) return { description } ;
588593 const depictedSchema = excludeExamplesFromDepiction (
589594 depict ( schema , {
590- rules : { ...brandHandling , ...overrides } ,
595+ rules : { ...brandHandling , ...depicters } ,
591596 ctx : { isResponse : true , makeRef, path, method } ,
592597 } ) ,
593598 ) ;
@@ -709,7 +714,7 @@ export const depictBody = ({
709714} ) => {
710715 const [ withoutParams , hasRequired ] = excludeParamsFromDepiction (
711716 depict ( schema , {
712- rules : { ...brandHandling , ...overrides } ,
717+ rules : { ...brandHandling , ...depicters } ,
713718 ctx : { isResponse : false , makeRef, path, method } ,
714719 } ) ,
715720 paramNames ,
0 commit comments