1
- import { ZuiExtensionObject } from '../../ui/types'
2
1
import z from '../../z'
3
2
import * as json from '../common/json-schema'
4
3
import * as err from '../common/errors'
@@ -35,30 +34,39 @@ export function toJsonSchema(schema: z.Schema): json.ZuiJsonSchema {
35
34
} )
36
35
37
36
case z . ZodFirstPartyTypeKind . ZodBoolean :
38
- return { type : 'boolean' , 'x-zui' : def [ 'x-zui' ] } satisfies json . BooleanSchema
37
+ return {
38
+ type : 'boolean' ,
39
+ description : def . description ,
40
+ 'x-zui' : def [ 'x-zui' ] ,
41
+ } satisfies json . BooleanSchema
39
42
40
43
case z . ZodFirstPartyTypeKind . ZodDate :
41
44
throw new err . UnsupportedZuiToJsonSchemaError ( z . ZodFirstPartyTypeKind . ZodDate , {
42
45
suggestedAlternative : 'use z.string().datetime() instead' ,
43
46
} )
44
47
45
48
case z . ZodFirstPartyTypeKind . ZodUndefined :
46
- return undefinedSchema ( def [ 'x-zui' ] )
49
+ return undefinedSchema ( def )
47
50
48
51
case z . ZodFirstPartyTypeKind . ZodNull :
49
- return nullSchema ( def [ 'x-zui' ] )
52
+ return nullSchema ( def )
50
53
51
54
case z . ZodFirstPartyTypeKind . ZodAny :
52
- return { 'x-zui' : def [ 'x-zui' ] } satisfies json . AnySchema
55
+ return {
56
+ description : def . description ,
57
+ 'x-zui' : def [ 'x-zui' ] ,
58
+ } satisfies json . AnySchema
53
59
54
60
case z . ZodFirstPartyTypeKind . ZodUnknown :
55
61
return {
62
+ description : def . description ,
56
63
'x-zui' : { ...def [ 'x-zui' ] , def : { typeName : z . ZodFirstPartyTypeKind . ZodUnknown } } ,
57
64
}
58
65
59
66
case z . ZodFirstPartyTypeKind . ZodNever :
60
67
return {
61
68
not : true ,
69
+ description : def . description ,
62
70
'x-zui' : def [ 'x-zui' ] ,
63
71
} satisfies json . NeverSchema
64
72
@@ -70,30 +78,40 @@ export function toJsonSchema(schema: z.Schema): json.ZuiJsonSchema {
70
78
71
79
case z . ZodFirstPartyTypeKind . ZodObject :
72
80
const shape = Object . entries ( def . shape ( ) )
73
- const required = shape . filter ( ( [ _ , value ] ) => ! value . isOptional ( ) ) . map ( ( [ key ] ) => key )
81
+ const requiredProperties = shape . filter ( ( [ _ , value ] ) => ! value . isOptional ( ) )
82
+ const required = requiredProperties . length ? requiredProperties . map ( ( [ key ] ) => key ) : undefined
74
83
const properties = shape
75
84
. map ( ( [ key , value ] ) => [ key , _toRequired ( value ) ] satisfies [ string , z . ZodType ] )
76
85
. map ( ( [ key , value ] ) => [ key , toJsonSchema ( value ) ] satisfies [ string , json . ZuiJsonSchema ] )
77
86
78
- const zAdditionalProperties = ( schema as z . ZodObject ) . additionalProperties ( )
79
- const additionalProperties = zAdditionalProperties ? toJsonSchema ( zAdditionalProperties ) : undefined
87
+ let additionalProperties : json . ObjectSchema [ 'additionalProperties' ] = undefined
88
+ if ( def . unknownKeys instanceof z . ZodType ) {
89
+ additionalProperties = toJsonSchema ( def . unknownKeys )
90
+ } else if ( def . unknownKeys === 'passthrough' ) {
91
+ additionalProperties = true
92
+ } else if ( def . unknownKeys === 'strict' ) {
93
+ additionalProperties = false
94
+ }
80
95
81
96
return {
82
97
type : 'object' ,
98
+ description : def . description ,
83
99
properties : Object . fromEntries ( properties ) ,
84
100
required,
85
- 'x-zui' : def [ 'x-zui' ] ,
86
101
additionalProperties,
102
+ 'x-zui' : def [ 'x-zui' ] ,
87
103
} satisfies json . ObjectSchema
88
104
89
105
case z . ZodFirstPartyTypeKind . ZodUnion :
90
106
return {
107
+ description : def . description ,
91
108
anyOf : def . options . map ( ( option ) => toJsonSchema ( option ) ) ,
92
109
'x-zui' : def [ 'x-zui' ] ,
93
110
} satisfies json . UnionSchema
94
111
95
112
case z . ZodFirstPartyTypeKind . ZodDiscriminatedUnion :
96
113
return {
114
+ description : def . description ,
97
115
anyOf : def . options . map ( ( option ) => toJsonSchema ( option ) ) ,
98
116
'x-zui' : {
99
117
...def [ 'x-zui' ] ,
@@ -103,6 +121,7 @@ export function toJsonSchema(schema: z.Schema): json.ZuiJsonSchema {
103
121
104
122
case z . ZodFirstPartyTypeKind . ZodIntersection :
105
123
return {
124
+ description : def . description ,
106
125
allOf : [ toJsonSchema ( def . left ) , toJsonSchema ( def . right ) ] ,
107
126
'x-zui' : def [ 'x-zui' ] ,
108
127
} satisfies json . IntersectionSchema
@@ -113,6 +132,7 @@ export function toJsonSchema(schema: z.Schema): json.ZuiJsonSchema {
113
132
case z . ZodFirstPartyTypeKind . ZodRecord :
114
133
return {
115
134
type : 'object' ,
135
+ description : def . description ,
116
136
additionalProperties : toJsonSchema ( def . valueType ) ,
117
137
'x-zui' : def [ 'x-zui' ] ,
118
138
} satisfies json . RecordSchema
@@ -133,25 +153,28 @@ export function toJsonSchema(schema: z.Schema): json.ZuiJsonSchema {
133
153
if ( typeof def . value === 'string' ) {
134
154
return {
135
155
type : 'string' ,
156
+ description : def . description ,
136
157
const : def . value ,
137
158
'x-zui' : def [ 'x-zui' ] ,
138
159
} satisfies json . LiteralStringSchema
139
160
} else if ( typeof def . value === 'number' ) {
140
161
return {
141
162
type : 'number' ,
163
+ description : def . description ,
142
164
const : def . value ,
143
165
'x-zui' : def [ 'x-zui' ] ,
144
166
} satisfies json . LiteralNumberSchema
145
167
} else if ( typeof def . value === 'boolean' ) {
146
168
return {
147
169
type : 'boolean' ,
170
+ description : def . description ,
148
171
const : def . value ,
149
172
'x-zui' : def [ 'x-zui' ] ,
150
173
} satisfies json . LiteralBooleanSchema
151
174
} else if ( def . value === null ) {
152
- return nullSchema ( def [ 'x-zui' ] )
175
+ return nullSchema ( def )
153
176
} else if ( def . value === undefined ) {
154
- return undefinedSchema ( def [ 'x-zui' ] )
177
+ return undefinedSchema ( def )
155
178
} else {
156
179
z . util . assertEqual < bigint | symbol , typeof def . value > ( true )
157
180
const unsupportedLiteral = typeof def . value
@@ -161,6 +184,7 @@ export function toJsonSchema(schema: z.Schema): json.ZuiJsonSchema {
161
184
case z . ZodFirstPartyTypeKind . ZodEnum :
162
185
return {
163
186
type : 'string' ,
187
+ description : def . description ,
164
188
enum : def . values ,
165
189
'x-zui' : def [ 'x-zui' ] ,
166
190
} satisfies json . EnumSchema
@@ -173,6 +197,7 @@ export function toJsonSchema(schema: z.Schema): json.ZuiJsonSchema {
173
197
174
198
case z . ZodFirstPartyTypeKind . ZodOptional :
175
199
return {
200
+ description : def . description ,
176
201
anyOf : [ toJsonSchema ( def . innerType ) , undefinedSchema ( ) ] ,
177
202
'x-zui' : {
178
203
...def [ 'x-zui' ] ,
@@ -192,8 +217,8 @@ export function toJsonSchema(schema: z.Schema): json.ZuiJsonSchema {
192
217
case z . ZodFirstPartyTypeKind . ZodDefault :
193
218
// ZodDefault is not treated as a metadata root so we don't need to preserve x-zui
194
219
return {
195
- default : def . defaultValue ( ) ,
196
220
...toJsonSchema ( def . innerType ) ,
221
+ default : def . defaultValue ( ) ,
197
222
}
198
223
199
224
case z . ZodFirstPartyTypeKind . ZodCatch :
@@ -215,13 +240,14 @@ export function toJsonSchema(schema: z.Schema): json.ZuiJsonSchema {
215
240
case z . ZodFirstPartyTypeKind . ZodReadonly :
216
241
// ZodReadonly is not treated as a metadata root so we don't need to preserve x-zui
217
242
return {
218
- readOnly : true ,
219
243
...toJsonSchema ( def . innerType ) ,
244
+ readOnly : true ,
220
245
}
221
246
222
247
case z . ZodFirstPartyTypeKind . ZodRef :
223
248
return {
224
249
$ref : def . uri ,
250
+ description : def . description ,
225
251
'x-zui' : def [ 'x-zui' ] ,
226
252
}
227
253
@@ -259,12 +285,14 @@ const _toRequired = (schema: z.ZodType): z.ZodType => {
259
285
return newSchema
260
286
}
261
287
262
- const undefinedSchema = ( xZui ?: ZuiExtensionObject ) : json . UndefinedSchema => ( {
288
+ const undefinedSchema = ( def ?: z . ZodTypeDef ) : json . UndefinedSchema => ( {
263
289
not : true ,
264
- 'x-zui' : { ...xZui , def : { typeName : z . ZodFirstPartyTypeKind . ZodUndefined } } ,
290
+ description : def ?. description ,
291
+ 'x-zui' : { ...def ?. [ 'x-zui' ] , def : { typeName : z . ZodFirstPartyTypeKind . ZodUndefined } } ,
265
292
} )
266
293
267
- const nullSchema = ( xZui ?: ZuiExtensionObject ) : json . NullSchema => ( {
294
+ const nullSchema = ( def ?: z . ZodTypeDef ) : json . NullSchema => ( {
268
295
type : 'null' ,
269
- 'x-zui' : xZui ,
296
+ description : def ?. description ,
297
+ 'x-zui' : def ?. [ 'x-zui' ] ,
270
298
} )
0 commit comments