• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

agentic-dev-library / thumbcode / 21120281856

18 Jan 2026 11:13PM UTC coverage: 25.507% (-0.02%) from 25.529%
21120281856

Pull #57

github

web-flow
Merge cd5077fde into 79c7ce934
Pull Request #57: Implement Comprehensive Accessibility Features

260 of 1595 branches covered (16.3%)

Branch coverage included in aggregate %.

0 of 29 new or added lines in 5 files covered. (0.0%)

2 existing lines in 2 files now uncovered.

621 of 1859 relevant lines covered (33.41%)

1.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.25
/src/lib/error-handler.ts
1
/**
2
 * Global Error Handler
3
 *
4
 * Centralized error handling for the application.
5
 * Catches unhandled errors and provides consistent error processing.
6
 */
7

8
import { logger } from './logger';
9

10
// Type for React Native's ErrorUtils
11
interface ErrorUtilsType {
12
  setGlobalHandler: (handler: (error: Error, isFatal?: boolean) => void) => void;
13
  getGlobalHandler: () => ((error: Error, isFatal?: boolean) => void) | undefined;
14
}
15

16
// Access React Native's ErrorUtils
17
function getErrorUtils(): ErrorUtilsType | undefined {
NEW
18
  return (global as unknown as { ErrorUtils?: ErrorUtilsType }).ErrorUtils;
×
19
}
20

21
export type ErrorSeverity = 'low' | 'medium' | 'high' | 'critical';
22

23
export interface AppError extends Error {
24
  code?: string;
25
  severity?: ErrorSeverity;
26
  userMessage?: string;
27
  context?: Record<string, unknown>;
28
  recoverable?: boolean;
29
}
30

31
/**
32
 * Error codes for categorization
33
 */
34
export const ErrorCodes = {
1✔
35
  // Network errors
36
  NETWORK_OFFLINE: 'NETWORK_OFFLINE',
37
  NETWORK_TIMEOUT: 'NETWORK_TIMEOUT',
38
  NETWORK_ERROR: 'NETWORK_ERROR',
39

40
  // API errors
41
  API_ERROR: 'API_ERROR',
42
  API_UNAUTHORIZED: 'API_UNAUTHORIZED',
43
  API_FORBIDDEN: 'API_FORBIDDEN',
44
  API_NOT_FOUND: 'API_NOT_FOUND',
45
  API_RATE_LIMITED: 'API_RATE_LIMITED',
46

47
  // Auth errors
48
  AUTH_EXPIRED: 'AUTH_EXPIRED',
49
  AUTH_INVALID: 'AUTH_INVALID',
50
  AUTH_MISSING: 'AUTH_MISSING',
51

52
  // Storage errors
53
  STORAGE_FULL: 'STORAGE_FULL',
54
  STORAGE_ERROR: 'STORAGE_ERROR',
55

56
  // Git errors
57
  GIT_CLONE_FAILED: 'GIT_CLONE_FAILED',
58
  GIT_PUSH_FAILED: 'GIT_PUSH_FAILED',
59
  GIT_PULL_FAILED: 'GIT_PULL_FAILED',
60
  GIT_MERGE_CONFLICT: 'GIT_MERGE_CONFLICT',
61

62
  // Agent errors
63
  AGENT_TIMEOUT: 'AGENT_TIMEOUT',
64
  AGENT_ERROR: 'AGENT_ERROR',
65

66
  // Generic
67
  UNKNOWN: 'UNKNOWN',
68
} as const;
69

70
export type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
71

72
/**
73
 * User-friendly error messages
74
 */
75
const userMessages: Record<ErrorCode, string> = {
1✔
76
  [ErrorCodes.NETWORK_OFFLINE]: 'You appear to be offline. Please check your connection.',
77
  [ErrorCodes.NETWORK_TIMEOUT]: 'The request timed out. Please try again.',
78
  [ErrorCodes.NETWORK_ERROR]: 'A network error occurred. Please try again.',
79
  [ErrorCodes.API_ERROR]: 'Something went wrong. Please try again.',
80
  [ErrorCodes.API_UNAUTHORIZED]: 'Your session has expired. Please sign in again.',
81
  [ErrorCodes.API_FORBIDDEN]: "You don't have permission to perform this action.",
82
  [ErrorCodes.API_NOT_FOUND]: 'The requested resource was not found.',
83
  [ErrorCodes.API_RATE_LIMITED]: "You've made too many requests. Please wait a moment.",
84
  [ErrorCodes.AUTH_EXPIRED]: 'Your session has expired. Please sign in again.',
85
  [ErrorCodes.AUTH_INVALID]: 'Invalid credentials. Please check and try again.',
86
  [ErrorCodes.AUTH_MISSING]: 'Please sign in to continue.',
87
  [ErrorCodes.STORAGE_FULL]: 'Device storage is full. Please free up some space.',
88
  [ErrorCodes.STORAGE_ERROR]: 'Failed to save data. Please try again.',
89
  [ErrorCodes.GIT_CLONE_FAILED]: 'Failed to clone repository. Please check your connection.',
90
  [ErrorCodes.GIT_PUSH_FAILED]: 'Failed to push changes. Please try again.',
91
  [ErrorCodes.GIT_PULL_FAILED]: 'Failed to pull changes. Please try again.',
92
  [ErrorCodes.GIT_MERGE_CONFLICT]: 'Merge conflict detected. Please resolve manually.',
93
  [ErrorCodes.AGENT_TIMEOUT]: 'The agent took too long to respond. Please try again.',
94
  [ErrorCodes.AGENT_ERROR]: 'The agent encountered an error. Please try again.',
95
  [ErrorCodes.UNKNOWN]: 'Something unexpected happened. Please try again.',
96
};
97

98
/**
99
 * Create a standardized app error
100
 */
101
export function createAppError(
102
  message: string,
103
  options: {
2✔
104
    code?: ErrorCode;
105
    severity?: ErrorSeverity;
106
    userMessage?: string;
107
    context?: Record<string, unknown>;
108
    recoverable?: boolean;
109
    cause?: Error;
110
  } = {}
111
): AppError {
112
  const error = new Error(message) as AppError;
21✔
113
  error.name = 'AppError';
21✔
114
  error.code = options.code || ErrorCodes.UNKNOWN;
21✔
115
  error.severity = options.severity || 'medium';
21✔
116
  error.userMessage = options.userMessage || userMessages[error.code as ErrorCode];
21✔
117
  error.context = options.context;
21✔
118
  error.recoverable = options.recoverable ?? true;
21✔
119
  error.cause = options.cause;
21✔
120
  return error;
21✔
121
}
122

123
/**
124
 * Parse an error into a standardized AppError
125
 */
126
export function parseError(error: unknown): AppError {
127
  // Already an AppError
128
  if (isAppError(error)) {
16✔
129
    return error;
3✔
130
  }
131

132
  // Standard Error
133
  if (error instanceof Error) {
13✔
134
    // Check for network errors
135
    if (error.message.includes('Network request failed')) {
8✔
136
      return createAppError(error.message, {
1✔
137
        code: ErrorCodes.NETWORK_ERROR,
138
        severity: 'medium',
139
        cause: error,
140
      });
141
    }
142

143
    // Check for timeout errors
144
    if (error.message.includes('timeout') || error.name === 'TimeoutError') {
7✔
145
      return createAppError(error.message, {
1✔
146
        code: ErrorCodes.NETWORK_TIMEOUT,
147
        severity: 'low',
148
        cause: error,
149
      });
150
    }
151

152
    // Generic error conversion
153
    return createAppError(error.message, {
6✔
154
      code: ErrorCodes.UNKNOWN,
155
      severity: 'medium',
156
      cause: error,
157
    });
158
  }
159

160
  // String error
161
  if (typeof error === 'string') {
5✔
162
    return createAppError(error, {
2✔
163
      code: ErrorCodes.UNKNOWN,
164
      severity: 'medium',
165
    });
166
  }
167

168
  // Unknown error type
169
  return createAppError('An unknown error occurred', {
3✔
170
    code: ErrorCodes.UNKNOWN,
171
    severity: 'medium',
172
    context: { originalError: String(error) },
173
  });
174
}
175

176
/**
177
 * Type guard for AppError
178
 */
179
export function isAppError(error: unknown): error is AppError {
180
  return error instanceof Error && 'code' in error && 'severity' in error;
27✔
181
}
182

183
/**
184
 * Get user-friendly message for an error
185
 */
186
export function getUserMessage(error: unknown): string {
187
  const appError = parseError(error);
3✔
188
  return appError.userMessage || userMessages[ErrorCodes.UNKNOWN];
3!
189
}
190

191
/**
192
 * Error handler callbacks
193
 */
194
type ErrorCallback = (error: AppError) => void;
195

196
const errorCallbacks: Set<ErrorCallback> = new Set();
1✔
197

198
/**
199
 * Register a global error callback
200
 */
201
export function onError(callback: ErrorCallback): () => void {
202
  errorCallbacks.add(callback);
2✔
203
  return () => errorCallbacks.delete(callback);
2✔
204
}
205

206
/**
207
 * Handle an error globally
208
 */
209
export function handleError(error: unknown, context?: Record<string, unknown>): AppError {
210
  const appError = parseError(error);
5✔
211

212
  // Add context if provided
213
  if (context) {
5✔
214
    appError.context = { ...appError.context, ...context };
1✔
215
  }
216

217
  // Log the error
218
  const logLevel = appError.severity === 'critical' ? 'fatal' : 'error';
5!
219
  logger[logLevel](`[${appError.code}] ${appError.message}`, appError, appError.context);
5✔
220

221
  // Notify callbacks
222
  for (const callback of errorCallbacks) {
5✔
223
    try {
2✔
224
      callback(appError);
2✔
225
    } catch (callbackError) {
226
      logger.warn('Error callback threw an exception', { error: String(callbackError) });
×
227
    }
228
  }
229

230
  return appError;
5✔
231
}
232

233
/**
234
 * Setup global error handlers for React Native
235
 */
236
export function setupGlobalErrorHandlers(): void {
237
  const errorUtils = getErrorUtils();
×
238

239
  if (errorUtils) {
×
240
    // Handle unhandled promise rejections
241
    const originalHandler = errorUtils.getGlobalHandler?.();
×
242

243
    errorUtils.setGlobalHandler?.((error: Error, isFatal?: boolean) => {
×
244
      handleError(error, { isFatal, source: 'globalHandler' });
×
245

246
      // Call original handler
247
      if (originalHandler) {
×
248
        originalHandler(error, isFatal);
×
249
      }
250
    });
251
  }
252

253
  logger.info('Global error handlers initialized');
×
254
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc