1- // genkit-tools/common/src/utils/errors.ts
2-
31/**
42 * Copyright 2024 Google LLC
53 *
1614 * limitations under the License.
1715 */
1816
17+ // Connection error codes for different runtimes
18+ const CONNECTION_ERROR_CODES = {
19+ NODE_ECONNREFUSED : 'ECONNREFUSED' ,
20+ BUN_CONNECTION_REFUSED : 'ConnectionRefused' ,
21+ ECONNRESET : 'ECONNRESET' ,
22+ } as const ;
23+
24+ const CONNECTION_ERROR_PATTERNS = [
25+ 'ECONNREFUSED' ,
26+ 'Connection refused' ,
27+ 'ConnectionRefused' ,
28+ 'connect ECONNREFUSED' ,
29+ ] as const ;
30+
31+ type ErrorWithCode = {
32+ code ?: string ;
33+ message ?: string ;
34+ cause ?: ErrorWithCode ;
35+ } ;
36+
1937/**
2038 * Checks if an error is a connection refused error across Node.js and Bun runtimes.
2139 *
@@ -27,58 +45,57 @@ export function isConnectionRefusedError(error: unknown): boolean {
2745 return false ;
2846 }
2947
30- // Handle plain objects with a code property (Bun fetch errors)
31- if ( typeof error === 'object' && 'code' in error ) {
32- const code = ( error as any ) . code ;
33- if (
34- code === 'ECONNREFUSED' || // Node.js
35- code === 'ConnectionRefused' || // Bun
36- code === 'ECONNRESET' // Connection reset (also indicates server is down)
37- ) {
38- return true ;
39- }
48+ const errorCode = getErrorCode ( error ) ;
49+ if ( errorCode && isConnectionErrorCode ( errorCode ) ) {
50+ return true ;
4051 }
4152
42- // Handle Error instances
43- if ( error instanceof Error ) {
44- // Direct error code
45- if ( 'code' in error && typeof error . code === 'string' ) {
46- const code = error . code ;
47- if (
48- code === 'ECONNREFUSED' ||
49- code === 'ConnectionRefused' ||
50- code === 'ECONNRESET'
51- ) {
52- return true ;
53- }
54- }
55-
56- // Node.js style with cause
57- if (
58- 'cause' in error &&
59- error . cause &&
60- typeof error . cause === 'object' &&
61- 'code' in error . cause &&
62- error . cause . code === 'ECONNREFUSED'
63- ) {
64- return true ;
65- }
66-
67- // Fallback: check error message
68- if (
69- error . message &&
70- ( error . message . includes ( 'ECONNREFUSED' ) ||
71- error . message . includes ( 'Connection refused' ) ||
72- error . message . includes ( 'ConnectionRefused' ) ||
73- error . message . includes ( 'connect ECONNREFUSED' ) )
74- ) {
75- return true ;
76- }
53+ // Fallback: check error message
54+ if ( isErrorWithMessage ( error ) ) {
55+ return CONNECTION_ERROR_PATTERNS . some ( ( pattern ) =>
56+ error . message . includes ( pattern )
57+ ) ;
7758 }
7859
7960 return false ;
8061}
8162
63+ /**
64+ * Helper function to check if a code is a connection error code.
65+ */
66+ function isConnectionErrorCode ( code : string ) : boolean {
67+ return Object . values ( CONNECTION_ERROR_CODES ) . includes (
68+ code as ( typeof CONNECTION_ERROR_CODES ) [ keyof typeof CONNECTION_ERROR_CODES ]
69+ ) ;
70+ }
71+
72+ /**
73+ * Type guard to check if an error has a message property.
74+ */
75+ function isErrorWithMessage ( error : unknown ) : error is { message : string } {
76+ return (
77+ typeof error === 'object' &&
78+ error !== null &&
79+ 'message' in error &&
80+ typeof ( error as any ) . message === 'string'
81+ ) ;
82+ }
83+
84+ /**
85+ * Extracts error code from an object, handling nested structures.
86+ */
87+ function extractErrorCode ( obj : unknown ) : string | undefined {
88+ if (
89+ typeof obj === 'object' &&
90+ obj !== null &&
91+ 'code' in obj &&
92+ typeof ( obj as ErrorWithCode ) . code === 'string'
93+ ) {
94+ return ( obj as ErrorWithCode ) . code ;
95+ }
96+ return undefined ;
97+ }
98+
8299/**
83100 * Gets the error code from an error object, handling both Node.js and Bun styles.
84101 */
@@ -87,32 +104,33 @@ export function getErrorCode(error: unknown): string | undefined {
87104 return undefined ;
88105 }
89106
90- // Handle plain objects with a code property
91- if (
92- typeof error === 'object' &&
93- 'code' in error &&
94- typeof ( error as any ) . code === 'string'
95- ) {
96- return ( error as any ) . code ;
107+ // Direct error code
108+ const directCode = extractErrorCode ( error ) ;
109+ if ( directCode ) {
110+ return directCode ;
97111 }
98112
99- // Handle Error instances
100- if ( error instanceof Error ) {
101- // Direct error code
102- if ( 'code' in error && typeof error . code === 'string' ) {
103- return error . code ;
113+ // Node.js style with cause
114+ if ( typeof error === 'object' && error !== null && 'cause' in error ) {
115+ const causeCode = extractErrorCode ( ( error as ErrorWithCode ) . cause ) ;
116+ if ( causeCode ) {
117+ return causeCode ;
104118 }
119+ }
105120
106- // Node.js style with cause
107- if (
108- 'cause' in error &&
109- error . cause &&
110- typeof error . cause === 'object' &&
111- 'code' in error . cause &&
112- typeof error . cause . code === 'string'
113- ) {
114- return error . cause . code ;
115- }
121+ return undefined ;
122+ }
123+
124+ /**
125+ * Extracts error message from various error formats.
126+ */
127+ function extractErrorMessage ( error : unknown ) : string | undefined {
128+ if ( error instanceof Error ) {
129+ return error . message ;
130+ }
131+
132+ if ( isErrorWithMessage ( error ) ) {
133+ return error . message ;
116134 }
117135
118136 return undefined ;
@@ -122,18 +140,14 @@ export function getErrorCode(error: unknown): string | undefined {
122140 * Safely extracts error details for logging.
123141 */
124142export function getErrorDetails ( error : unknown ) : string {
125- if ( ! error ) {
143+ if ( error === null || error === undefined ) {
126144 return 'Unknown error' ;
127145 }
128146
129147 const code = getErrorCode ( error ) ;
148+ const message = extractErrorMessage ( error ) ;
130149
131- if ( error instanceof Error ) {
132- return code ? `${ error . message } (${ code } )` : error . message ;
133- }
134-
135- if ( typeof error === 'object' && 'message' in error ) {
136- const message = ( error as any ) . message ;
150+ if ( message ) {
137151 return code ? `${ message } (${ code } )` : message ;
138152 }
139153
0 commit comments