44 * SPDX-License-Identifier: Apache-2.0
55 */
66
7- import { Schema , Type } from '@google/genai' ;
8- import { z , ZodObject , ZodTypeAny } from 'zod' ;
7+ import { Schema , Type } from '@google/genai' ;
8+ import { z as z3 } from 'zod/v3' ;
9+ import { z as z4 , toJSONSchema as toJSONSchemaV4 } from 'zod/v4' ;
10+
11+ type ZodSchema < T = any > = z3 . ZodType < T > | z4 . ZodType < T > ;
12+
13+ type SchemaLike = ZodSchema | Schema ;
14+
15+ function isZodSchema ( obj : unknown ) : obj is ZodSchema {
16+ return (
17+ obj !== null &&
18+ typeof obj === "object" &&
19+ "parse" in obj &&
20+ typeof ( obj as { parse : unknown } ) . parse === "function" &&
21+ "safeParse" in obj &&
22+ typeof ( obj as { safeParse : unknown } ) . safeParse === "function"
23+ ) ;
24+ }
25+
26+ function isZodV3Schema ( obj : unknown ) : obj is z3 . ZodTypeAny {
27+ return isZodSchema ( obj ) && ! ( "_zod" in obj ) ;
28+ }
29+
30+ function isZodV4Schema ( obj : unknown ) : obj is z4 . ZodType {
31+ return isZodSchema ( obj ) && "_zod" in obj ;
32+ }
33+
34+ function getZodTypeName ( schema : z3 . ZodTypeAny | z4 . ZodType ) : string | undefined {
35+ const schemaAny = schema as any ;
36+
37+ if ( schemaAny . _def ?. typeName ) {
38+ return schemaAny . _def . typeName ;
39+ }
40+
41+ const zod4Type = schemaAny . _def ?. type ;
42+ if ( typeof zod4Type === 'string' && zod4Type ) {
43+ return 'Zod' + zod4Type . charAt ( 0 ) . toUpperCase ( ) + zod4Type . slice ( 1 ) ;
44+ }
45+
46+ return undefined ;
47+ }
948
1049/**
11- * Returns true if the given object is a V3 ZodObject.
50+ * Returns true if the given object is a ZodObject (supports both Zod v3 and v4) .
1251 */
13- export function isZodObject ( obj : unknown ) : obj is ZodObject < any > {
14- return (
15- obj !== null && typeof obj === 'object' &&
16- ( obj as any ) . _def ?. typeName === 'ZodObject' ) ;
52+ export function isZodObject ( obj : unknown ) : obj is z3 . ZodObject < any > | z4 . ZodObject < any > {
53+ return isZodSchema ( obj ) && getZodTypeName ( obj ) === 'ZodObject' ;
1754}
1855
1956// TODO(b/425992518): consider conversion to FunctionDeclaration directly.
20-
21- function parseZodType ( zodType : ZodTypeAny ) : Schema | undefined {
57+ function parseZodV3Type ( zodType : z3 . ZodTypeAny ) : Schema | undefined {
2258 const def = zodType . _def ;
2359 if ( ! def ) {
2460 return { } ;
@@ -35,7 +71,7 @@ function parseZodType(zodType: ZodTypeAny): Schema|undefined {
3571 } ;
3672
3773 switch ( def . typeName ) {
38- case z . ZodFirstPartyTypeKind . ZodString :
74+ case z3 . ZodFirstPartyTypeKind . ZodString :
3975 result . type = Type . STRING ;
4076 for ( const check of def . checks || [ ] ) {
4177 if ( check . kind === 'min' )
@@ -53,7 +89,7 @@ function parseZodType(zodType: ZodTypeAny): Schema|undefined {
5389 }
5490 return returnResult ( result ) ;
5591
56- case z . ZodFirstPartyTypeKind . ZodNumber :
92+ case z3 . ZodFirstPartyTypeKind . ZodNumber :
5793 result . type = Type . NUMBER ;
5894 for ( const check of def . checks || [ ] ) {
5995 if ( check . kind === 'min' )
@@ -65,23 +101,23 @@ function parseZodType(zodType: ZodTypeAny): Schema|undefined {
65101 }
66102 return returnResult ( result ) ;
67103
68- case z . ZodFirstPartyTypeKind . ZodBoolean :
104+ case z3 . ZodFirstPartyTypeKind . ZodBoolean :
69105 result . type = Type . BOOLEAN ;
70106 return returnResult ( result ) ;
71107
72- case z . ZodFirstPartyTypeKind . ZodArray :
108+ case z3 . ZodFirstPartyTypeKind . ZodArray :
73109 result . type = Type . ARRAY ;
74- result . items = parseZodType ( def . type ) ;
110+ result . items = parseZodV3Type ( def . type ) ;
75111 if ( def . minLength ) result . minItems = def . minLength . value . toString ( ) ;
76112 if ( def . maxLength ) result . maxItems = def . maxLength . value . toString ( ) ;
77113 return returnResult ( result ) ;
78114
79- case z . ZodFirstPartyTypeKind . ZodObject : {
80- const nestedSchema = zodObjectToSchema ( zodType as ZodObject < any > ) ;
115+ case z3 . ZodFirstPartyTypeKind . ZodObject : {
116+ const nestedSchema = zodObjectToSchema ( zodType as z3 . ZodObject < any > ) ;
81117 return nestedSchema as Schema ;
82118 }
83119
84- case z . ZodFirstPartyTypeKind . ZodLiteral :
120+ case z3 . ZodFirstPartyTypeKind . ZodLiteral :
85121 const literalType = typeof def . value ;
86122 result . enum = [ def . value . toString ( ) ] ;
87123
@@ -99,71 +135,67 @@ function parseZodType(zodType: ZodTypeAny): Schema|undefined {
99135
100136 return returnResult ( result ) ;
101137
102- case z . ZodFirstPartyTypeKind . ZodEnum :
138+ case z3 . ZodFirstPartyTypeKind . ZodEnum :
103139 result . type = Type . STRING ;
104140 result . enum = def . values ;
105141 return returnResult ( result ) ;
106142
107- case z . ZodFirstPartyTypeKind . ZodNativeEnum :
143+ case z3 . ZodFirstPartyTypeKind . ZodNativeEnum :
108144 result . type = Type . STRING ;
109145 result . enum = Object . values ( def . values ) ;
110146 return returnResult ( result ) ;
111147
112- case z . ZodFirstPartyTypeKind . ZodUnion :
113- result . anyOf = def . options . map ( parseZodType ) ;
148+ case z3 . ZodFirstPartyTypeKind . ZodUnion :
149+ result . anyOf = def . options . map ( parseZodV3Type ) ;
114150 return returnResult ( result ) ;
115151
116- case z . ZodFirstPartyTypeKind . ZodOptional :
117- return parseZodType ( def . innerType ) ;
118- case z . ZodFirstPartyTypeKind . ZodNullable :
119- const nullableInner = parseZodType ( def . innerType ) ;
152+ case z3 . ZodFirstPartyTypeKind . ZodOptional :
153+ return parseZodV3Type ( def . innerType ) ;
154+ case z3 . ZodFirstPartyTypeKind . ZodNullable :
155+ const nullableInner = parseZodV3Type ( def . innerType ) ;
120156 return nullableInner ?
121- returnResult ( {
122- anyOf : [ nullableInner , { type : Type . NULL } ] ,
123- ...( description && { description} )
124- } ) :
125- returnResult ( { type : Type . NULL , ...( description && { description} ) } ) ;
126- case z . ZodFirstPartyTypeKind . ZodDefault :
127- const defaultInner = parseZodType ( def . innerType ) ;
157+ returnResult ( {
158+ anyOf : [ nullableInner , { type : Type . NULL } ] ,
159+ ...( description && { description } )
160+ } ) :
161+ returnResult ( { type : Type . NULL , ...( description && { description } ) } ) ;
162+ case z3 . ZodFirstPartyTypeKind . ZodDefault :
163+ const defaultInner = parseZodV3Type ( def . innerType ) ;
128164 if ( defaultInner ) defaultInner . default = def . defaultValue ( ) ;
129165 return defaultInner ;
130- case z . ZodFirstPartyTypeKind . ZodBranded :
131- return parseZodType ( def . type ) ;
132- case z . ZodFirstPartyTypeKind . ZodReadonly :
133- return parseZodType ( def . innerType ) ;
134- case z . ZodFirstPartyTypeKind . ZodNull :
166+ case z3 . ZodFirstPartyTypeKind . ZodBranded :
167+ return parseZodV3Type ( def . type ) ;
168+ case z3 . ZodFirstPartyTypeKind . ZodReadonly :
169+ return parseZodV3Type ( def . innerType ) ;
170+ case z3 . ZodFirstPartyTypeKind . ZodNull :
135171 result . type = Type . NULL ;
136172 return returnResult ( result ) ;
137- case z . ZodFirstPartyTypeKind . ZodAny :
138- case z . ZodFirstPartyTypeKind . ZodUnknown :
139- return returnResult ( { ...( description && { description} ) } ) ;
173+ case z3 . ZodFirstPartyTypeKind . ZodAny :
174+ case z3 . ZodFirstPartyTypeKind . ZodUnknown :
175+ return returnResult ( { ...( description && { description } ) } ) ;
140176 default :
141177 throw new Error ( `Unsupported Zod type: ${ def . typeName } ` ) ;
142178 }
143179}
144180
145- export function zodObjectToSchema ( schema : ZodObject < any > ) : Schema {
146- if ( schema . _def . typeName !== z . ZodFirstPartyTypeKind . ZodObject ) {
147- throw new Error ( 'Expected a ZodObject' ) ;
148- }
149-
181+ function toJsonSchemaZ3 ( schema : z3 . ZodObject < z3 . ZodRawShape > ) : Schema {
150182 const shape = schema . shape ;
151183 const properties : Record < string , Schema > = { } ;
152184 const required : string [ ] = [ ] ;
153185
154186 for ( const key in shape ) {
155187 const fieldSchema = shape [ key ] ;
156- const parsedField = parseZodType ( fieldSchema ) ;
188+ const parsedField = parseZodV3Type ( fieldSchema ) ;
157189 if ( parsedField ) {
158190 properties [ key ] = parsedField ;
159191 }
160192
161193 let currentSchema = fieldSchema ;
162194 let isOptional = false ;
163195 while ( currentSchema . _def . typeName ===
164- z . ZodFirstPartyTypeKind . ZodOptional ||
165- currentSchema . _def . typeName === z . ZodFirstPartyTypeKind . ZodDefault ) {
166- isOptional = true ;
196+ z3 . ZodFirstPartyTypeKind . ZodOptional ||
197+ currentSchema . _def . typeName === z3 . ZodFirstPartyTypeKind . ZodDefault ) {
198+ isOptional = true ;
167199 currentSchema = currentSchema . _def . innerType ;
168200 }
169201 if ( ! isOptional ) {
@@ -172,16 +204,38 @@ export function zodObjectToSchema(schema: ZodObject<any>): Schema {
172204 }
173205
174206 const catchall = schema . _def . catchall ;
175- let additionalProperties : boolean | Schema = false ;
176- if ( catchall && catchall . _def . typeName !== z . ZodFirstPartyTypeKind . ZodNever ) {
177- additionalProperties = parseZodType ( catchall ) || true ;
207+ let additionalProperties : boolean | Schema = false ;
208+ if ( catchall && catchall . _def . typeName !== z3 . ZodFirstPartyTypeKind . ZodNever ) {
209+ additionalProperties = parseZodV3Type ( catchall ) || true ;
178210 } else {
179211 additionalProperties = schema . _def . unknownKeys === 'passthrough' ;
180212 }
181213 return {
182214 type : Type . OBJECT ,
183215 properties,
184216 required : required . length > 0 ? required : [ ] ,
185- ...( schema . _def . description ? { description : schema . _def . description } : { } ) ,
217+ ...( schema . _def . description ? { description : schema . _def . description } : { } ) ,
186218 } ;
187219}
220+
221+ export function zodObjectToSchema ( schema : z3 . ZodObject < z3 . ZodRawShape > | z4 . ZodObject < z4 . ZodRawShape > ) : Schema {
222+ if ( ! isZodObject ( schema ) ) {
223+ throw new Error ( 'Expected a Zod Object' ) ;
224+ }
225+
226+ if ( isZodV4Schema ( schema ) ) {
227+ return toJSONSchemaV4 ( schema , {
228+ target : 'openapi-3.0' , override ( ctx ) {
229+ if ( ctx . jsonSchema . additionalProperties !== undefined ) {
230+ delete ctx . jsonSchema . additionalProperties ;
231+ }
232+ } ,
233+ } ) as Schema ;
234+ }
235+
236+ if ( isZodV3Schema ( schema ) ) {
237+ return toJsonSchemaZ3 ( schema ) ;
238+ }
239+
240+ throw new Error ( 'Unsupported Zod schema version.' ) ;
241+ }
0 commit comments