@@ -36,7 +36,10 @@ async function main() {
3636 postProcess : [ "prettier" ] ,
3737 } ,
3838 plugins : [
39- "zod" ,
39+ {
40+ name : "zod" ,
41+ "~resolvers" : createDeserializationResolvers ( ) ,
42+ } ,
4043 { bigInt : false , name : "@hey-api/transformers" } ,
4144 "@hey-api/typescript" ,
4245 ] ,
@@ -177,6 +180,211 @@ function updateDocs(src, schemaDefs) {
177180 return result ;
178181}
179182
183+ function createDeserializationResolvers ( ) {
184+ return {
185+ array ( ctx ) {
186+ const base = ctx . schema [ "x-deserialize-skip-invalid-items" ]
187+ ? vecSkipErrorExpression ( ctx )
188+ : ctx . nodes . base ( ctx ) ;
189+
190+ ctx . chain . current = base ;
191+ const lengthResult = ctx . nodes . length ( ctx ) ;
192+ if ( lengthResult ) {
193+ ctx . chain . current = lengthResult ;
194+ } else {
195+ const minLengthResult = ctx . nodes . minLength ( ctx ) ;
196+ if ( minLengthResult ) ctx . chain . current = minLengthResult ;
197+ const maxLengthResult = ctx . nodes . maxLength ( ctx ) ;
198+ if ( maxLengthResult ) ctx . chain . current = maxLengthResult ;
199+ }
200+
201+ return ctx . chain . current ;
202+ } ,
203+
204+ object ( ctx ) {
205+ if ( ! hasDefaultOnErrorProperties ( ctx . schema ) ) return undefined ;
206+
207+ const shape = ctx . $ . object ( ) . pretty ( ) ;
208+ for ( const name in ctx . schema . properties ) {
209+ const property = ctx . schema . properties [ name ] ;
210+ const isRequired = ctx . schema . required ?. includes ( name ) === true ;
211+ const propertyResult = ctx . walk (
212+ property ,
213+ childContext (
214+ { path : ctx . path , plugin : ctx . plugin } ,
215+ "properties" ,
216+ name ,
217+ ) ,
218+ ) ;
219+ ctx . _childResults . push ( propertyResult ) ;
220+
221+ const finalExpression = propertyExpression (
222+ ctx ,
223+ name ,
224+ property ,
225+ propertyResult ,
226+ isRequired ,
227+ ) ;
228+
229+ shape . prop (
230+ name ,
231+ property [ "x-deserialize-default-on-error" ]
232+ ? defaultOnErrorExpression (
233+ ctx ,
234+ finalExpression ,
235+ property ,
236+ isRequired ,
237+ )
238+ : finalExpression ,
239+ ) ;
240+ }
241+
242+ const defaultShape = ctx . nodes . shape ;
243+ ctx . nodes . shape = ( ) => shape ;
244+ const base = ctx . nodes . base ( ctx ) ;
245+ ctx . nodes . shape = defaultShape ;
246+ return base ;
247+ } ,
248+ } ;
249+ }
250+
251+ function childContext ( ctx , ...segments ) {
252+ return {
253+ path : ref ( [ ...fromRef ( ctx . path ) , ...segments ] ) ,
254+ plugin : ctx . plugin ,
255+ } ;
256+ }
257+
258+ function ref ( path ) {
259+ return { "~ref" : path } ;
260+ }
261+
262+ function fromRef ( ref ) {
263+ return ref ?. [ "~ref" ] ;
264+ }
265+
266+ function jsonPointerPath ( ref ) {
267+ return `#/${ fromRef ( ref ) . map ( jsonPointerSegment ) . join ( "/" ) } ` ;
268+ }
269+
270+ function jsonPointerSegment ( segment ) {
271+ return String ( segment ) . replaceAll ( "~" , "~0" ) . replaceAll ( "/" , "~1" ) ;
272+ }
273+
274+ function hasDefaultOnErrorProperties ( schema ) {
275+ return Object . values ( schema . properties ?? { } ) . some (
276+ ( property ) => property [ "x-deserialize-default-on-error" ] ,
277+ ) ;
278+ }
279+
280+ function propertyExpression ( ctx , name , property , propertyResult , isRequired ) {
281+ if ( ! property [ "x-deserialize-skip-invalid-items" ] ) {
282+ return ctx . applyModifiers ( propertyResult , { optional : ! isRequired } ) . chain ;
283+ }
284+
285+ const itemSchema = getArrayItemSchema ( property ) ;
286+ if ( ! itemSchema ) {
287+ throw new Error (
288+ `Unable to apply x-deserialize-skip-invalid-items to ${ jsonPointerPath ( childContext ( ctx , "properties" , name ) . path ) } ` ,
289+ ) ;
290+ }
291+
292+ const itemResult = ctx . walk (
293+ itemSchema ,
294+ childContext (
295+ { path : ctx . path , plugin : ctx . plugin } ,
296+ "properties" ,
297+ name ,
298+ "items" ,
299+ 0 ,
300+ ) ,
301+ ) ;
302+
303+ return ctx . applyModifiers (
304+ {
305+ chain : ctx
306+ . $ ( schemaDeserializeSymbol ( ctx . plugin , "vecSkipError" ) )
307+ . call ( ctx . applyModifiers ( itemResult , { optional : false } ) . chain ) ,
308+ meta : propertyResult . meta ,
309+ } ,
310+ { optional : ! isRequired } ,
311+ ) . chain ;
312+ }
313+
314+ function getArrayItemSchema ( schema ) {
315+ if ( schema . type === "array" && schema . items ) {
316+ return Array . isArray ( schema . items ) ? schema . items [ 0 ] : schema . items ;
317+ }
318+
319+ const items = Array . isArray ( schema . items ) ? schema . items : [ ] ;
320+ for ( const item of items ) {
321+ const itemSchema = getArrayItemSchema ( item ) ;
322+ if ( itemSchema ) return itemSchema ;
323+ }
324+
325+ return undefined ;
326+ }
327+
328+ function vecSkipErrorExpression ( ctx ) {
329+ const vecSkipError = schemaDeserializeSymbol ( ctx . plugin , "vecSkipError" ) ;
330+
331+ if ( ctx . childResults . length !== 1 ) {
332+ throw new Error (
333+ `Unable to apply x-deserialize-skip-invalid-items to ${ jsonPointerPath ( ctx . path ) } ` ,
334+ ) ;
335+ }
336+
337+ return ctx
338+ . $ ( vecSkipError )
339+ . call ( ctx . applyModifiers ( ctx . childResults [ 0 ] , { optional : false } ) . chain ) ;
340+ }
341+
342+ function defaultOnErrorExpression ( ctx , schemaExpression , schema , isRequired ) {
343+ const helper = schemaDeserializeSymbol (
344+ ctx . plugin ,
345+ isRequired ? "requiredDefaultOnError" : "defaultOnError" ,
346+ ) ;
347+
348+ return ctx
349+ . $ ( helper )
350+ . call (
351+ schemaExpression ,
352+ fallbackFunctionExpression ( ctx , schema , isRequired ) ,
353+ ) ;
354+ }
355+
356+ function schemaDeserializeSymbol ( plugin , name ) {
357+ return plugin . symbolOnce ( name , {
358+ external : "../schema-deserialize.js" ,
359+ } ) ;
360+ }
361+
362+ function fallbackFunctionExpression ( ctx , schema , isRequired ) {
363+ return ctx . $ . func ( ) . do (
364+ ctx . $ . return ( fallbackValueExpression ( ctx , schema , isRequired ) ) ,
365+ ) ;
366+ }
367+
368+ function fallbackValueExpression ( ctx , schema , isRequired ) {
369+ if ( Object . hasOwn ( schema , "default" ) ) {
370+ return ctx . $ . fromValue ( schema . default ) ;
371+ }
372+
373+ if ( isArraySchema ( schema ) && ( isRequired || ! isNullableSchema ( schema ) ) ) {
374+ return ctx . $ . array ( ) ;
375+ }
376+
377+ return ctx . $ . id ( "undefined" ) ;
378+ }
379+
380+ function isArraySchema ( schema ) {
381+ return schema . type === "array" ;
382+ }
383+
384+ function isNullableSchema ( schema ) {
385+ return schema . nullable === true ;
386+ }
387+
180388function injectDocIfMissing ( src , exportStr , description ) {
181389 const idx = src . indexOf ( exportStr ) ;
182390 if ( idx === - 1 ) return src ;
0 commit comments