developer-ai 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +241 -0
- package/bin/developer-ai.js +2 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +219 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +82 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +115 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +29 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/constants.d.ts +8 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +8 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/agent.d.ts +38 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js +155 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/system-prompt.d.ts +6 -0
- package/dist/core/system-prompt.d.ts.map +1 -0
- package/dist/core/system-prompt.js +44 -0
- package/dist/core/system-prompt.js.map +1 -0
- package/dist/core/types.d.ts +42 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +6 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +13 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +202 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/providers/ollama.d.ts +13 -0
- package/dist/providers/ollama.d.ts.map +1 -0
- package/dist/providers/ollama.js +60 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/openai.d.ts +9 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +40 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/skills/loader.d.ts +25 -0
- package/dist/skills/loader.d.ts.map +1 -0
- package/dist/skills/loader.js +93 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/tests/tools.test.d.ts +2 -0
- package/dist/tests/tools.test.d.ts.map +1 -0
- package/dist/tests/tools.test.js +170 -0
- package/dist/tests/tools.test.js.map +1 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +19 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-files.d.ts +3 -0
- package/dist/tools/list-files.d.ts.map +1 -0
- package/dist/tools/list-files.js +60 -0
- package/dist/tools/list-files.js.map +1 -0
- package/dist/tools/read-file.d.ts +3 -0
- package/dist/tools/read-file.d.ts.map +1 -0
- package/dist/tools/read-file.js +46 -0
- package/dist/tools/read-file.js.map +1 -0
- package/dist/tools/registry.d.ts +24 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +37 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/run-command.d.ts +3 -0
- package/dist/tools/run-command.d.ts.map +1 -0
- package/dist/tools/run-command.js +114 -0
- package/dist/tools/run-command.js.map +1 -0
- package/dist/tools/search-text.d.ts +3 -0
- package/dist/tools/search-text.d.ts.map +1 -0
- package/dist/tools/search-text.js +103 -0
- package/dist/tools/search-text.js.map +1 -0
- package/dist/tools/utils.d.ts +6 -0
- package/dist/tools/utils.d.ts.map +1 -0
- package/dist/tools/utils.js +14 -0
- package/dist/tools/utils.js.map +1 -0
- package/dist/tools/web-search.d.ts +3 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +80 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/write-file.d.ts +3 -0
- package/dist/tools/write-file.d.ts.map +1 -0
- package/dist/tools/write-file.js +66 -0
- package/dist/tools/write-file.js.map +1 -0
- package/package.json +54 -0
- package/skills/accessibility/SKILL.md +496 -0
- package/skills/api-design/SKILL.md +419 -0
- package/skills/code-review/SKILL.md +267 -0
- package/skills/debugging/SKILL.md +332 -0
- package/skills/documentation/SKILL.md +496 -0
- package/skills/error-handling/SKILL.md +504 -0
- package/skills/git-workflow/SKILL.md +448 -0
- package/skills/human-like-coding/SKILL.md +400 -0
- package/skills/performance-optimization/SKILL.md +412 -0
- package/skills/prompt-engineering/SKILL.md +362 -0
- package/skills/refactoring/SKILL.md +457 -0
- package/skills/security-audit/SKILL.md +453 -0
- package/skills/testing-strategy/SKILL.md +501 -0
- package/skills/webapp-testing/SKILL.md +309 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: error-handling
|
|
3
|
+
description: Guide for implementing robust error handling. Use when designing error handling strategies, creating custom errors, implementing retry logic, or improving error messages. Covers error types, try-catch patterns, async error handling, and user-facing errors.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Error Handling Skill
|
|
7
|
+
|
|
8
|
+
This skill provides guidance for implementing robust, user-friendly error handling in applications.
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
Good error handling improves debugging, user experience, and system reliability. This skill covers patterns for catching, processing, and communicating errors effectively.
|
|
13
|
+
|
|
14
|
+
## Error Hierarchy
|
|
15
|
+
|
|
16
|
+
### Custom Error Classes
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
// Base application error
|
|
20
|
+
class AppError extends Error {
|
|
21
|
+
constructor(message, statusCode = 500, code = 'INTERNAL_ERROR') {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = this.constructor.name;
|
|
24
|
+
this.statusCode = statusCode;
|
|
25
|
+
this.code = code;
|
|
26
|
+
this.isOperational = true;
|
|
27
|
+
Error.captureStackTrace(this, this.constructor);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Specific error types
|
|
32
|
+
class ValidationError extends AppError {
|
|
33
|
+
constructor(message, details = []) {
|
|
34
|
+
super(message, 400, 'VALIDATION_ERROR');
|
|
35
|
+
this.details = details;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class NotFoundError extends AppError {
|
|
40
|
+
constructor(resource = 'Resource') {
|
|
41
|
+
super(`${resource} not found`, 404, 'NOT_FOUND');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class UnauthorizedError extends AppError {
|
|
46
|
+
constructor(message = 'Authentication required') {
|
|
47
|
+
super(message, 401, 'UNAUTHORIZED');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class ForbiddenError extends AppError {
|
|
52
|
+
constructor(message = 'Access denied') {
|
|
53
|
+
super(message, 403, 'FORBIDDEN');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class ConflictError extends AppError {
|
|
58
|
+
constructor(message = 'Resource conflict') {
|
|
59
|
+
super(message, 409, 'CONFLICT');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Usage
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
function getUser(id) {
|
|
68
|
+
const user = users.find(u => u.id === id);
|
|
69
|
+
if (!user) {
|
|
70
|
+
throw new NotFoundError('User');
|
|
71
|
+
}
|
|
72
|
+
return user;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function validateEmail(email) {
|
|
76
|
+
if (!email.includes('@')) {
|
|
77
|
+
throw new ValidationError('Invalid email format', [
|
|
78
|
+
{ field: 'email', message: 'Must contain @' }
|
|
79
|
+
]);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Try-Catch Patterns
|
|
85
|
+
|
|
86
|
+
### Basic Try-Catch
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
try {
|
|
90
|
+
const result = riskyOperation();
|
|
91
|
+
return result;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
// Handle specific errors differently
|
|
94
|
+
if (error instanceof ValidationError) {
|
|
95
|
+
return { error: error.details };
|
|
96
|
+
}
|
|
97
|
+
if (error instanceof NotFoundError) {
|
|
98
|
+
return { error: 'Item not found' };
|
|
99
|
+
}
|
|
100
|
+
// Re-throw unexpected errors
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### With Finally
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
let connection;
|
|
109
|
+
try {
|
|
110
|
+
connection = await database.connect();
|
|
111
|
+
const result = await connection.query(sql);
|
|
112
|
+
return result;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
logger.error('Database query failed', { error, sql });
|
|
115
|
+
throw new AppError('Database operation failed');
|
|
116
|
+
} finally {
|
|
117
|
+
// Always cleanup
|
|
118
|
+
if (connection) {
|
|
119
|
+
await connection.release();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Wrapping Errors
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
async function fetchUserData(userId) {
|
|
128
|
+
try {
|
|
129
|
+
const response = await fetch(`/api/users/${userId}`);
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
throw new Error(`HTTP ${response.status}`);
|
|
132
|
+
}
|
|
133
|
+
return await response.json();
|
|
134
|
+
} catch (error) {
|
|
135
|
+
// Wrap with context
|
|
136
|
+
throw new AppError(
|
|
137
|
+
`Failed to fetch user ${userId}: ${error.message}`,
|
|
138
|
+
500,
|
|
139
|
+
'FETCH_ERROR'
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Async Error Handling
|
|
146
|
+
|
|
147
|
+
### Async/Await
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
// With try-catch
|
|
151
|
+
async function processOrder(orderId) {
|
|
152
|
+
try {
|
|
153
|
+
const order = await getOrder(orderId);
|
|
154
|
+
const payment = await processPayment(order);
|
|
155
|
+
await updateInventory(order.items);
|
|
156
|
+
await sendConfirmation(order);
|
|
157
|
+
return { success: true, payment };
|
|
158
|
+
} catch (error) {
|
|
159
|
+
logger.error('Order processing failed', { orderId, error });
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Express async wrapper
|
|
165
|
+
const asyncHandler = (fn) => (req, res, next) => {
|
|
166
|
+
Promise.resolve(fn(req, res, next)).catch(next);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
app.get('/orders/:id', asyncHandler(async (req, res) => {
|
|
170
|
+
const order = await getOrder(req.params.id);
|
|
171
|
+
res.json(order);
|
|
172
|
+
}));
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Promise Error Handling
|
|
176
|
+
|
|
177
|
+
```javascript
|
|
178
|
+
// Chain with catch
|
|
179
|
+
fetchData()
|
|
180
|
+
.then(processData)
|
|
181
|
+
.then(saveResult)
|
|
182
|
+
.catch(handleError);
|
|
183
|
+
|
|
184
|
+
// Multiple promises
|
|
185
|
+
const results = await Promise.allSettled([
|
|
186
|
+
fetchUsers(),
|
|
187
|
+
fetchOrders(),
|
|
188
|
+
fetchProducts(),
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
const errors = results
|
|
192
|
+
.filter(r => r.status === 'rejected')
|
|
193
|
+
.map(r => r.reason);
|
|
194
|
+
|
|
195
|
+
if (errors.length) {
|
|
196
|
+
logger.warn('Some operations failed', { errors });
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Timeout Handling
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
function withTimeout(promise, ms) {
|
|
204
|
+
const timeout = new Promise((_, reject) => {
|
|
205
|
+
setTimeout(() => reject(new Error('Operation timed out')), ms);
|
|
206
|
+
});
|
|
207
|
+
return Promise.race([promise, timeout]);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
const result = await withTimeout(fetchData(), 5000);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
if (error.message === 'Operation timed out') {
|
|
214
|
+
// Handle timeout specifically
|
|
215
|
+
}
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Express Error Handling
|
|
221
|
+
|
|
222
|
+
### Error Middleware
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
// Error handler middleware (must have 4 parameters)
|
|
226
|
+
function errorHandler(err, req, res, next) {
|
|
227
|
+
// Log error
|
|
228
|
+
logger.error({
|
|
229
|
+
message: err.message,
|
|
230
|
+
stack: err.stack,
|
|
231
|
+
path: req.path,
|
|
232
|
+
method: req.method,
|
|
233
|
+
body: req.body,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Operational errors - safe to send to client
|
|
237
|
+
if (err.isOperational) {
|
|
238
|
+
return res.status(err.statusCode).json({
|
|
239
|
+
error: {
|
|
240
|
+
code: err.code,
|
|
241
|
+
message: err.message,
|
|
242
|
+
...(err.details && { details: err.details }),
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Programming errors - don't leak details
|
|
248
|
+
res.status(500).json({
|
|
249
|
+
error: {
|
|
250
|
+
code: 'INTERNAL_ERROR',
|
|
251
|
+
message: 'Something went wrong',
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// 404 handler
|
|
257
|
+
function notFoundHandler(req, res) {
|
|
258
|
+
res.status(404).json({
|
|
259
|
+
error: {
|
|
260
|
+
code: 'NOT_FOUND',
|
|
261
|
+
message: `Route ${req.method} ${req.path} not found`,
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Apply middleware
|
|
267
|
+
app.use(notFoundHandler);
|
|
268
|
+
app.use(errorHandler);
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Validation Error Handling
|
|
272
|
+
|
|
273
|
+
```javascript
|
|
274
|
+
const { validationResult } = require('express-validator');
|
|
275
|
+
|
|
276
|
+
function handleValidationErrors(req, res, next) {
|
|
277
|
+
const errors = validationResult(req);
|
|
278
|
+
|
|
279
|
+
if (!errors.isEmpty()) {
|
|
280
|
+
throw new ValidationError('Validation failed',
|
|
281
|
+
errors.array().map(e => ({
|
|
282
|
+
field: e.path,
|
|
283
|
+
message: e.msg,
|
|
284
|
+
}))
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
next();
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## React Error Handling
|
|
293
|
+
|
|
294
|
+
### Error Boundaries
|
|
295
|
+
|
|
296
|
+
```jsx
|
|
297
|
+
class ErrorBoundary extends React.Component {
|
|
298
|
+
constructor(props) {
|
|
299
|
+
super(props);
|
|
300
|
+
this.state = { hasError: false, error: null };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
static getDerivedStateFromError(error) {
|
|
304
|
+
return { hasError: true, error };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
componentDidCatch(error, errorInfo) {
|
|
308
|
+
// Log to error reporting service
|
|
309
|
+
logErrorToService(error, errorInfo);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
render() {
|
|
313
|
+
if (this.state.hasError) {
|
|
314
|
+
return this.props.fallback || <ErrorFallback error={this.state.error} />;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return this.props.children;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Usage
|
|
322
|
+
<ErrorBoundary fallback={<div>Something went wrong</div>}>
|
|
323
|
+
<MyComponent />
|
|
324
|
+
</ErrorBoundary>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Async Error Handling in React
|
|
328
|
+
|
|
329
|
+
```jsx
|
|
330
|
+
function useAsync(asyncFn, dependencies = []) {
|
|
331
|
+
const [state, setState] = useState({
|
|
332
|
+
data: null,
|
|
333
|
+
error: null,
|
|
334
|
+
loading: true,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
useEffect(() => {
|
|
338
|
+
setState(s => ({ ...s, loading: true }));
|
|
339
|
+
|
|
340
|
+
asyncFn()
|
|
341
|
+
.then(data => setState({ data, error: null, loading: false }))
|
|
342
|
+
.catch(error => setState({ data: null, error, loading: false }));
|
|
343
|
+
}, dependencies);
|
|
344
|
+
|
|
345
|
+
return state;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Usage
|
|
349
|
+
function UserProfile({ userId }) {
|
|
350
|
+
const { data: user, error, loading } = useAsync(
|
|
351
|
+
() => fetchUser(userId),
|
|
352
|
+
[userId]
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
if (loading) return <Loading />;
|
|
356
|
+
if (error) return <ErrorMessage error={error} />;
|
|
357
|
+
return <Profile user={user} />;
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Retry Logic
|
|
362
|
+
|
|
363
|
+
### Exponential Backoff
|
|
364
|
+
|
|
365
|
+
```javascript
|
|
366
|
+
async function withRetry(fn, options = {}) {
|
|
367
|
+
const {
|
|
368
|
+
maxAttempts = 3,
|
|
369
|
+
baseDelay = 1000,
|
|
370
|
+
maxDelay = 10000,
|
|
371
|
+
shouldRetry = () => true,
|
|
372
|
+
} = options;
|
|
373
|
+
|
|
374
|
+
let lastError;
|
|
375
|
+
|
|
376
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
377
|
+
try {
|
|
378
|
+
return await fn();
|
|
379
|
+
} catch (error) {
|
|
380
|
+
lastError = error;
|
|
381
|
+
|
|
382
|
+
if (attempt === maxAttempts || !shouldRetry(error)) {
|
|
383
|
+
throw error;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const delay = Math.min(
|
|
387
|
+
baseDelay * Math.pow(2, attempt - 1),
|
|
388
|
+
maxDelay
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
throw lastError;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Usage
|
|
399
|
+
const data = await withRetry(
|
|
400
|
+
() => fetchFromAPI('/data'),
|
|
401
|
+
{
|
|
402
|
+
maxAttempts: 3,
|
|
403
|
+
shouldRetry: (error) => error.status >= 500,
|
|
404
|
+
}
|
|
405
|
+
);
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
## Error Messages
|
|
409
|
+
|
|
410
|
+
### User-Facing Messages
|
|
411
|
+
|
|
412
|
+
```javascript
|
|
413
|
+
const UserMessages = {
|
|
414
|
+
VALIDATION_ERROR: 'Please check your input and try again.',
|
|
415
|
+
NOT_FOUND: 'The requested item could not be found.',
|
|
416
|
+
UNAUTHORIZED: 'Please log in to continue.',
|
|
417
|
+
FORBIDDEN: 'You don\'t have permission to do this.',
|
|
418
|
+
CONFLICT: 'This item already exists.',
|
|
419
|
+
NETWORK_ERROR: 'Unable to connect. Please check your internet.',
|
|
420
|
+
INTERNAL_ERROR: 'Something went wrong. Please try again later.',
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
function getUserMessage(error) {
|
|
424
|
+
return UserMessages[error.code] || UserMessages.INTERNAL_ERROR;
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Developer-Facing Messages
|
|
429
|
+
|
|
430
|
+
```javascript
|
|
431
|
+
// Include context for debugging
|
|
432
|
+
throw new AppError(
|
|
433
|
+
`Failed to process order ${orderId}: Payment declined for user ${userId}`,
|
|
434
|
+
400,
|
|
435
|
+
'PAYMENT_DECLINED'
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
// Structured logging
|
|
439
|
+
logger.error('Order processing failed', {
|
|
440
|
+
orderId,
|
|
441
|
+
userId,
|
|
442
|
+
amount,
|
|
443
|
+
paymentMethod,
|
|
444
|
+
error: error.message,
|
|
445
|
+
stack: error.stack,
|
|
446
|
+
});
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
## Error Logging
|
|
450
|
+
|
|
451
|
+
### Structured Logging
|
|
452
|
+
|
|
453
|
+
```javascript
|
|
454
|
+
const winston = require('winston');
|
|
455
|
+
|
|
456
|
+
const logger = winston.createLogger({
|
|
457
|
+
level: 'info',
|
|
458
|
+
format: winston.format.combine(
|
|
459
|
+
winston.format.timestamp(),
|
|
460
|
+
winston.format.json()
|
|
461
|
+
),
|
|
462
|
+
defaultMeta: { service: 'api' },
|
|
463
|
+
transports: [
|
|
464
|
+
new winston.transports.File({ filename: 'error.log', level: 'error' }),
|
|
465
|
+
new winston.transports.File({ filename: 'combined.log' }),
|
|
466
|
+
],
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// Log with context
|
|
470
|
+
function logError(error, context = {}) {
|
|
471
|
+
logger.error({
|
|
472
|
+
message: error.message,
|
|
473
|
+
code: error.code,
|
|
474
|
+
stack: error.stack,
|
|
475
|
+
...context,
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
## Error Handling Checklist
|
|
481
|
+
|
|
482
|
+
### Implementation
|
|
483
|
+
- [ ] Custom error classes created
|
|
484
|
+
- [ ] Error middleware configured
|
|
485
|
+
- [ ] Async errors handled
|
|
486
|
+
- [ ] Error boundaries in React
|
|
487
|
+
|
|
488
|
+
### Logging
|
|
489
|
+
- [ ] Errors logged with context
|
|
490
|
+
- [ ] Stack traces captured
|
|
491
|
+
- [ ] No sensitive data in logs
|
|
492
|
+
- [ ] Log levels appropriate
|
|
493
|
+
|
|
494
|
+
### User Experience
|
|
495
|
+
- [ ] User-friendly error messages
|
|
496
|
+
- [ ] Actionable feedback provided
|
|
497
|
+
- [ ] Recovery options available
|
|
498
|
+
- [ ] Loading/error states handled
|
|
499
|
+
|
|
500
|
+
### Monitoring
|
|
501
|
+
- [ ] Error tracking configured
|
|
502
|
+
- [ ] Alerts for critical errors
|
|
503
|
+
- [ ] Error trends monitored
|
|
504
|
+
- [ ] Resolution time tracked
|