1- import { ZodError , core } from 'zod'
21import { TAG_NAMES } from '$promptl/constants'
32import {
43 ChainStepTag ,
@@ -10,8 +9,7 @@ import {
109} from '$promptl/parser/interfaces'
1110import { ContentTypeTagName , MessageRole } from '$promptl/types'
1211import { Scalar , Node as YAMLItem , YAMLMap , YAMLSeq } from 'yaml'
13-
14- type ZodIssue = core . $ZodIssue
12+ import { ZodError , ZodIssue , ZodIssueCode } from 'zod'
1513
1614export function isIterable ( obj : unknown ) : obj is Iterable < unknown > {
1715 return ( obj as Iterable < unknown > ) ?. [ Symbol . iterator ] !== undefined
@@ -78,7 +76,7 @@ export function tagAttributeIsLiteral(tag: ElementTag, name: string): boolean {
7876type YAMLItemRange = [ number , number ] | undefined
7977export function findYAMLItemPosition (
8078 parent : YAMLItem ,
81- path : PropertyKey [ ] ,
79+ path : ( string | number ) [ ] ,
8280) : YAMLItemRange {
8381 const parentRange : YAMLItemRange = parent ?. range
8482 ? [ parent . range [ 0 ] , parent . range [ 1 ] ]
@@ -103,78 +101,145 @@ export function findYAMLItemPosition(
103101}
104102
105103export function isZodError ( error : unknown ) : error is ZodError {
106- return error instanceof ZodError
107- }
104+ if ( ! ( error instanceof Error ) ) return false
108105
109- function collectAllLeafIssues ( issue : ZodIssue ) : ZodIssue [ ] {
110- if ( issue . code === 'invalid_union' ) {
111- issue . errors
112- return issue . errors . flatMap ( ( issues ) =>
113- issues . flatMap ( collectAllLeafIssues ) ,
114- )
115- }
116- return [ issue ]
106+ if ( error instanceof ZodError ) return true
107+ if ( error . constructor . name === 'ZodError' ) return true
108+ if ( 'issues' in error && error . issues instanceof Array ) return true
109+
110+ return false
117111}
118112
119- function getZodIssueMessage ( issue : ZodIssue ) : string {
113+ function collectAllLeafIssues ( issue : ZodIssue ) : ZodIssue [ ] {
120114 switch ( issue . code ) {
121- case 'invalid_type' : {
122- const attr = issue . path . at ( - 1 )
123- return typeof attr === 'string'
124- ? `Expected type \`${ issue . expected } \` for attribute "${ attr } ", but received \`${ issue . input } \`.`
125- : `Expected type \`${ issue . expected } \`, but received \`${ issue . input } \`.`
115+ case ZodIssueCode . invalid_union : {
116+ // invalid_union.issue.unionErrors is ZodError[]
117+ const unionErrs : ZodError [ ] = ( issue as any ) . unionErrors ?? [ ]
118+ return unionErrs . flatMap ( ( nestedZodError ) =>
119+ nestedZodError . issues . flatMap ( ( nestedIssue ) =>
120+ collectAllLeafIssues ( nestedIssue ) ,
121+ ) ,
122+ )
126123 }
127124
128- case 'invalid_value' : {
129- const attr = issue . path . at ( - 1 )
130- const literalValues = issue . values . join ( ', ' )
131- return typeof attr === 'string'
132- ? `Expected literal \`${ literalValues } \` for attribute "${ attr } ", but received \`${ issue . input } \`.`
133- : `Expected literal \`${ literalValues } \`, but received \`${ issue . input } \`.`
125+ case ZodIssueCode . invalid_arguments : {
126+ // invalid_arguments.issue.argumentsError is ZodError
127+ const argsErr : ZodError | undefined = ( issue as any ) . argumentsError
128+ if ( argsErr ) {
129+ return argsErr . issues . flatMap ( ( nestedIssue ) =>
130+ collectAllLeafIssues ( nestedIssue ) ,
131+ )
132+ }
133+ return [ issue ]
134134 }
135135
136- case 'unrecognized_keys' :
137- return `Unrecognized keys: ${ issue . keys . join ( ', ' ) } .`
138-
139- case 'invalid_union' :
140- return `Invalid union: ${ issue . message } `
141-
142- case 'too_small' :
143- return `Value is too small: ${ issue . message || 'Value does not meet minimum size.' } `
144-
145- case 'too_big' :
146- return `Value is too big: ${ issue . message || 'Value exceeds maximum size.' } `
147-
148- case 'not_multiple_of' :
149- return `Value is not a multiple of ${ issue . divisor } : ${ issue . message || 'Value does not meet multiple of condition.' } `
150-
151- case 'custom' :
152- return `Custom validation error: ${ issue . message || 'No additional message provided.' } `
136+ case ZodIssueCode . invalid_return_type : {
137+ // invalid_return_type.issue.returnTypeError is ZodError
138+ const retErr : ZodError | undefined = ( issue as any ) . returnTypeError
139+ if ( retErr ) {
140+ return retErr . issues . flatMap ( ( nestedIssue ) =>
141+ collectAllLeafIssues ( nestedIssue ) ,
142+ )
143+ }
144+ return [ issue ]
145+ }
153146
154- case 'invalid_key' :
155- return `Invalid key: ${ issue . message || 'Key validation failed.' } `
147+ default :
148+ // Any other issue code is considered a “leaf” (no deeper nested ZodError)
149+ return [ issue ]
150+ }
151+ }
156152
157- case 'invalid_format' :
158- return `Invalid format: ${ issue . message || `Expected format ${ issue . format } .` } `
153+ function getZodIssueMessage ( issue : ZodIssue ) : string {
154+ if ( issue . code === ZodIssueCode . invalid_type ) {
155+ const attribute = issue . path [ issue . path . length - 1 ]
156+ if ( typeof attribute === 'string' ) {
157+ return `Expected type \`${ issue . expected } \` for attribute "${ attribute } ", but received \`${ issue . received } \`.`
158+ }
159159
160- case 'invalid_element' :
161- return `Invalid element: ${ issue . message || 'Element validation failed.' } `
160+ return `Expected type \`${ issue . expected } \`, but received \`${ issue . received } \`.`
161+ }
162+ if ( issue . code === ZodIssueCode . invalid_literal ) {
163+ const attribute = issue . path [ issue . path . length - 1 ]
164+ if ( typeof attribute === 'string' ) {
165+ return `Expected literal \`${ issue . expected } \` for attribute "${ attribute } ", but received \`${ issue . received } \`.`
166+ }
162167
163- default :
164- // The types are exhaustive, but we don't want to miss any new ones
165- return 'Unknown validation error.'
168+ return `Expected literal \`${ issue . expected } \`, but received \`${ issue . received } \`.`
166169 }
170+ if ( issue . code === ZodIssueCode . unrecognized_keys ) {
171+ return `Unrecognized keys: ${ issue . keys . join ( ', ' ) } .`
172+ }
173+ if ( issue . code === ZodIssueCode . invalid_union ) {
174+ return `Invalid union: ${ issue . unionErrors
175+ . map ( ( err ) => err . message )
176+ . join ( ', ' ) } `
177+ }
178+ if ( issue . code === ZodIssueCode . invalid_union_discriminator ) {
179+ return `Invalid union discriminator. Expected one of: ${ issue . options . join (
180+ ', ' ,
181+ ) } .`
182+ }
183+ if ( issue . code === ZodIssueCode . invalid_enum_value ) {
184+ return `Invalid enum value: ${ issue . received } . Expected one of: ${ issue . options . join (
185+ ', ' ,
186+ ) } .`
187+ }
188+ if ( issue . code === ZodIssueCode . invalid_arguments ) {
189+ return `Invalid arguments: ${ issue . argumentsError . issues
190+ . map ( ( err ) => err . message )
191+ . join ( ', ' ) } `
192+ }
193+ if ( issue . code === ZodIssueCode . invalid_return_type ) {
194+ return `Invalid return type: ${ issue . returnTypeError . issues
195+ . map ( ( err ) => err . message )
196+ . join ( ', ' ) } `
197+ }
198+ if ( issue . code === ZodIssueCode . invalid_date ) {
199+ return `Invalid date: ${ issue . message || 'Invalid date format.' } `
200+ }
201+ if ( issue . code === ZodIssueCode . invalid_string ) {
202+ return `Invalid string: ${ issue . message || 'String does not match expected format.' } `
203+ }
204+ if ( issue . code === ZodIssueCode . too_small ) {
205+ return `Value is too small: ${ issue . message || 'Value does not meet minimum size.' } `
206+ }
207+ if ( issue . code === ZodIssueCode . too_big ) {
208+ return `Value is too big: ${ issue . message || 'Value exceeds maximum size.' } `
209+ }
210+ if ( issue . code === ZodIssueCode . invalid_intersection_types ) {
211+ return `Invalid intersection types: ${ issue . message || 'Types do not match.' } `
212+ }
213+ if ( issue . code === ZodIssueCode . not_multiple_of ) {
214+ return `Value is not a multiple of ${ issue . multipleOf } : ${ issue . message || 'Value does not meet multiple of condition.' } `
215+ }
216+ if ( issue . code === ZodIssueCode . not_finite ) {
217+ return `Value is not finite: ${ issue . message || 'Value must be a finite number.' } `
218+ }
219+ if ( issue . code === ZodIssueCode . custom ) {
220+ return `Custom validation error: ${ issue . message || 'No additional message provided.' } `
221+ }
222+ // For any other issue code, return the message directly
223+ return ( issue as ZodIssue ) . message || 'Unknown validation error.'
167224}
168225
169226export function getMostSpecificError ( error : ZodIssue ) : {
170227 message : string
171- path : PropertyKey [ ]
228+ path : ( string | number ) [ ]
172229} {
173230 const allIssues = collectAllLeafIssues ( error )
174- const mostSpecific = allIssues . reduce (
175- ( acc , cur ) => ( cur . path . length > acc . path . length ? cur : acc ) ,
176- allIssues [ 0 ] ! ,
177- )
231+
232+ if ( allIssues . length === 0 ) {
233+ return { message : error . message , path : [ ] }
234+ }
235+
236+ let mostSpecific = allIssues [ 0 ] !
237+ for ( const issue of allIssues ) {
238+ if ( issue . path . length > mostSpecific . path . length ) {
239+ mostSpecific = issue
240+ }
241+ }
242+
178243 return {
179244 message : getZodIssueMessage ( mostSpecific ) ,
180245 path : mostSpecific . path ,
0 commit comments