@@ -5,13 +5,37 @@ interface ErrorLike {
55 name ?: string
66 _tag ?: string
77 message ?: string
8- data ?: Record < string , any >
8+ data ?: Record < string , unknown >
9+ }
10+
11+ type ConfigIssue = { message : string ; path : string [ ] }
12+
13+ function isRecord ( input : unknown ) : input is Record < string , unknown > {
14+ return typeof input === "object" && input !== null
915}
1016
1117function isTaggedError ( error : unknown , tag : string ) : boolean {
12- return (
13- typeof error === "object" && error !== null && "_tag" in error && ( error as Record < string , unknown > ) . _tag === tag
14- )
18+ return isRecord ( error ) && error . _tag === tag
19+ }
20+
21+ function configData ( input : unknown , tag : string ) : Record < string , unknown > | undefined {
22+ if ( ! isRecord ( input ) ) return undefined
23+ if ( input . name === tag && isRecord ( input . data ) ) return input . data
24+ if ( input . _tag === tag ) return input
25+ return undefined
26+ }
27+
28+ function stringField ( input : Record < string , unknown > , key : string ) : string | undefined {
29+ return typeof input [ key ] === "string" ? input [ key ] : undefined
30+ }
31+
32+ function configIssues ( input : Record < string , unknown > ) : ConfigIssue [ ] {
33+ return Array . isArray ( input . issues )
34+ ? input . issues . filter ( ( issue ) : issue is ConfigIssue => {
35+ if ( ! isRecord ( issue ) ) return false
36+ return typeof issue . message === "string" && Array . isArray ( issue . path ) && issue . path . every ( ( x ) => typeof x === "string" )
37+ } )
38+ : [ ]
1539}
1640
1741export function FormatError ( input : unknown ) {
@@ -35,7 +59,7 @@ export function FormatError(input: unknown) {
3559 // ProviderModelNotFoundError: { providerID: string, modelID: string, suggestions?: string[] }
3660 if ( NamedError . hasName ( input , "ProviderModelNotFoundError" ) ) {
3761 const data = ( input as ErrorLike ) . data
38- const suggestions : string [ ] = Array . isArray ( data ?. suggestions ) ? data . suggestions : [ ]
62+ const suggestions = Array . isArray ( data ?. suggestions ) ? data . suggestions . filter ( ( x ) => typeof x === "string" ) : [ ]
3963 return [
4064 `Model not found: ${ data ?. providerID } /${ data ?. modelID } ` ,
4165 ...( suggestions . length ? [ "Did you mean: " + suggestions . join ( ", " ) ] : [ ] ) ,
@@ -50,28 +74,30 @@ export function FormatError(input: unknown) {
5074 }
5175
5276 // ConfigJsonError: { path: string, message?: string }
53- if ( NamedError . hasName ( input , "ConfigJsonError" ) ) {
54- const data = ( input as ErrorLike ) . data
55- return `Config file at ${ data ?. path } is not valid JSON(C)` + ( data ?. message ? `: ${ data . message } ` : "" )
77+ const configJson = configData ( input , "ConfigJsonError" )
78+ if ( configJson ) {
79+ const message = stringField ( configJson , "message" )
80+ return `Config file at ${ stringField ( configJson , "path" ) } is not valid JSON(C)` + ( message ? `: ${ message } ` : "" )
5681 }
5782
5883 // ConfigDirectoryTypoError: { dir: string, path: string, suggestion: string }
59- if ( NamedError . hasName ( input , "ConfigDirectoryTypoError" ) ) {
60- const data = ( input as ErrorLike ) . data
61- return `Directory "${ data ?. dir } " in ${ data ?. path } is not valid. Rename the directory to "${ data ?. suggestion } " or remove it. This is a common typo.`
84+ const configDirectoryTypo = configData ( input , "ConfigDirectoryTypoError" )
85+ if ( configDirectoryTypo ) {
86+ return `Directory "${ stringField ( configDirectoryTypo , " dir" ) } " in ${ stringField ( configDirectoryTypo , " path" ) } is not valid. Rename the directory to "${ stringField ( configDirectoryTypo , " suggestion" ) } " or remove it. This is a common typo.`
6287 }
6388
6489 // ConfigFrontmatterError: { message: string }
65- if ( NamedError . hasName ( input , "ConfigFrontmatterError" ) ) {
66- return ( input as ErrorLike ) . data ?. message ?? ""
90+ const configFrontmatter = configData ( input , "ConfigFrontmatterError" )
91+ if ( configFrontmatter ) {
92+ return stringField ( configFrontmatter , "message" ) ?? ""
6793 }
6894
6995 // ConfigInvalidError: { path?: string, message?: string, issues?: Array<{ message: string, path: string[] }> }
70- if ( NamedError . hasName ( input , "ConfigInvalidError" ) ) {
71- const data = ( input as ErrorLike ) . data
72- const path = data ?. path
73- const message = data ?. message
74- const issues : Array < { message : string ; path : string [ ] } > = Array . isArray ( data ?. issues ) ? data . issues : [ ]
96+ const configInvalid = configData ( input , "ConfigInvalidError" )
97+ if ( configInvalid ) {
98+ const path = stringField ( configInvalid , " path" )
99+ const message = stringField ( configInvalid , " message" )
100+ const issues = configIssues ( configInvalid )
75101 return [
76102 `Configuration is invalid${ path && path !== "config" ? ` at ${ path } ` : "" } ` + ( message ? `: ${ message } ` : "" ) ,
77103 ...issues . map ( ( issue ) => "↳ " + issue . message + " " + issue . path . join ( "." ) ) ,
0 commit comments