1+ import { ZodError , core } from 'zod'
12import { TAG_NAMES } from '$promptl/constants'
23import {
34 ChainStepTag ,
@@ -9,7 +10,8 @@ import {
910} from '$promptl/parser/interfaces'
1011import { ContentTypeTagName , MessageRole } from '$promptl/types'
1112import { Scalar , Node as YAMLItem , YAMLMap , YAMLSeq } from 'yaml'
12- import { ZodError , ZodIssue , ZodIssueCode } from 'zod'
13+
14+ type ZodIssue = core . $ZodIssue
1315
1416export function isIterable ( obj : unknown ) : obj is Iterable < unknown > {
1517 return ( obj as Iterable < unknown > ) ?. [ Symbol . iterator ] !== undefined
@@ -76,7 +78,7 @@ export function tagAttributeIsLiteral(tag: ElementTag, name: string): boolean {
7678type YAMLItemRange = [ number , number ] | undefined
7779export function findYAMLItemPosition (
7880 parent : YAMLItem ,
79- path : ( string | number ) [ ] ,
81+ path : PropertyKey [ ] ,
8082) : YAMLItemRange {
8183 const parentRange : YAMLItemRange = parent ?. range
8284 ? [ parent . range [ 0 ] , parent . range [ 1 ] ]
@@ -101,145 +103,78 @@ export function findYAMLItemPosition(
101103}
102104
103105export function isZodError ( error : unknown ) : error is ZodError {
104- if ( ! ( error instanceof Error ) ) return false
105-
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
106+ return error instanceof ZodError
111107}
112108
113109function 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 ]
117+ }
118+
119+ function getZodIssueMessage ( issue : ZodIssue ) : string {
114120 switch ( issue . code ) {
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- )
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 } \`.`
123126 }
124127
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 ]
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 } \`.`
134134 }
135135
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- }
136+ case 'unrecognized_keys' :
137+ return `Unrecognized keys: ${ issue . keys . join ( ', ' ) } .`
146138
147- default :
148- // Any other issue code is considered a “leaf” (no deeper nested ZodError)
149- return [ issue ]
150- }
151- }
139+ case 'invalid_union' :
140+ return `Invalid union: ${ issue . message } `
152141
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- }
142+ case 'too_small' :
143+ return `Value is too small: ${ issue . message || 'Value does not meet minimum size.' } `
159144
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- }
145+ case 'too_big' :
146+ return `Value is too big: ${ issue . message || 'Value exceeds maximum size.' } `
167147
168- return `Expected literal \`${ issue . expected } \`, but received \`${ issue . received } \`.`
169- }
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.' } `
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.' } `
153+
154+ case 'invalid_key' :
155+ return `Invalid key: ${ issue . message || 'Key validation failed.' } `
156+
157+ case 'invalid_format' :
158+ return `Invalid format: ${ issue . message || `Expected format ${ issue . format } .` } `
159+
160+ case 'invalid_element' :
161+ return `Invalid element: ${ issue . message || 'Element validation failed.' } `
162+
163+ default :
164+ // The types are exhaustive, but we don't want to miss any new ones
165+ return 'Unknown validation error.'
221166 }
222- // For any other issue code, return the message directly
223- return ( issue as ZodIssue ) . message || 'Unknown validation error.'
224167}
225168
226169export function getMostSpecificError ( error : ZodIssue ) : {
227170 message : string
228- path : ( string | number ) [ ]
171+ path : PropertyKey [ ]
229172} {
230173 const allIssues = collectAllLeafIssues ( error )
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-
174+ const mostSpecific = allIssues . reduce (
175+ ( acc , cur ) => ( cur . path . length > acc . path . length ? cur : acc ) ,
176+ allIssues [ 0 ] ! ,
177+ )
243178 return {
244179 message : getZodIssueMessage ( mostSpecific ) ,
245180 path : mostSpecific . path ,
0 commit comments