ai-flow-dev 2.7.0 → 2.8.1
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/LICENSE +21 -21
- package/README.md +573 -570
- package/package.json +74 -74
- package/prompts/backend/flow-build-phase-0.md +535 -535
- package/prompts/backend/flow-build-phase-1.md +626 -626
- package/prompts/backend/flow-build-phase-10.md +340 -340
- package/prompts/backend/flow-build-phase-2.md +573 -573
- package/prompts/backend/flow-build-phase-3.md +834 -834
- package/prompts/backend/flow-build-phase-4.md +554 -554
- package/prompts/backend/flow-build-phase-5.md +703 -703
- package/prompts/backend/flow-build-phase-6.md +524 -524
- package/prompts/backend/flow-build-phase-7.md +1001 -1001
- package/prompts/backend/flow-build-phase-8.md +1407 -1407
- package/prompts/backend/flow-build-phase-9.md +477 -477
- package/prompts/backend/flow-build.md +137 -137
- package/prompts/backend/flow-check-review.md +656 -20
- package/prompts/backend/flow-check-test.md +526 -14
- package/prompts/backend/flow-check.md +717 -67
- package/prompts/backend/flow-commit.md +88 -119
- package/prompts/backend/flow-docs-sync.md +354 -354
- package/prompts/backend/flow-finish.md +919 -0
- package/prompts/backend/flow-release.md +949 -0
- package/prompts/backend/flow-work-feature.md +61 -61
- package/prompts/backend/flow-work-fix.md +46 -46
- package/prompts/backend/flow-work-refactor.md +48 -48
- package/prompts/backend/flow-work-resume.md +34 -34
- package/prompts/backend/flow-work.md +1098 -1286
- package/prompts/desktop/flow-build-phase-0.md +359 -359
- package/prompts/desktop/flow-build-phase-1.md +295 -295
- package/prompts/desktop/flow-build-phase-10.md +357 -357
- package/prompts/desktop/flow-build-phase-2.md +282 -282
- package/prompts/desktop/flow-build-phase-3.md +291 -291
- package/prompts/desktop/flow-build-phase-4.md +308 -308
- package/prompts/desktop/flow-build-phase-5.md +269 -269
- package/prompts/desktop/flow-build-phase-6.md +350 -350
- package/prompts/desktop/flow-build-phase-7.md +297 -297
- package/prompts/desktop/flow-build-phase-8.md +541 -541
- package/prompts/desktop/flow-build-phase-9.md +439 -439
- package/prompts/desktop/flow-build.md +156 -156
- package/prompts/desktop/flow-check-review.md +656 -20
- package/prompts/desktop/flow-check-test.md +526 -14
- package/prompts/desktop/flow-check.md +717 -67
- package/prompts/desktop/flow-commit.md +88 -119
- package/prompts/desktop/flow-docs-sync.md +354 -354
- package/prompts/desktop/flow-finish.md +919 -0
- package/prompts/desktop/flow-release.md +662 -0
- package/prompts/desktop/flow-work-feature.md +61 -61
- package/prompts/desktop/flow-work-fix.md +46 -46
- package/prompts/desktop/flow-work-refactor.md +48 -48
- package/prompts/desktop/flow-work-resume.md +34 -34
- package/prompts/desktop/flow-work.md +1202 -1390
- package/prompts/frontend/flow-build-phase-0.md +425 -425
- package/prompts/frontend/flow-build-phase-1.md +626 -626
- package/prompts/frontend/flow-build-phase-10.md +33 -33
- package/prompts/frontend/flow-build-phase-2.md +573 -573
- package/prompts/frontend/flow-build-phase-3.md +782 -782
- package/prompts/frontend/flow-build-phase-4.md +554 -554
- package/prompts/frontend/flow-build-phase-5.md +703 -703
- package/prompts/frontend/flow-build-phase-6.md +524 -524
- package/prompts/frontend/flow-build-phase-7.md +1001 -1001
- package/prompts/frontend/flow-build-phase-8.md +872 -872
- package/prompts/frontend/flow-build-phase-9.md +94 -94
- package/prompts/frontend/flow-build.md +137 -137
- package/prompts/frontend/flow-check-review.md +656 -20
- package/prompts/frontend/flow-check-test.md +526 -14
- package/prompts/frontend/flow-check.md +717 -67
- package/prompts/frontend/flow-commit.md +88 -119
- package/prompts/frontend/flow-docs-sync.md +550 -550
- package/prompts/frontend/flow-finish.md +919 -0
- package/prompts/frontend/flow-release.md +519 -0
- package/prompts/frontend/flow-work-api.md +1547 -0
- package/prompts/frontend/flow-work-feature.md +61 -61
- package/prompts/frontend/flow-work-fix.md +38 -38
- package/prompts/frontend/flow-work-refactor.md +48 -48
- package/prompts/frontend/flow-work-resume.md +34 -34
- package/prompts/frontend/flow-work.md +1595 -1320
- package/prompts/mobile/flow-build-phase-0.md +425 -425
- package/prompts/mobile/flow-build-phase-1.md +626 -626
- package/prompts/mobile/flow-build-phase-10.md +32 -32
- package/prompts/mobile/flow-build-phase-2.md +573 -573
- package/prompts/mobile/flow-build-phase-3.md +782 -782
- package/prompts/mobile/flow-build-phase-4.md +554 -554
- package/prompts/mobile/flow-build-phase-5.md +703 -703
- package/prompts/mobile/flow-build-phase-6.md +524 -524
- package/prompts/mobile/flow-build-phase-7.md +1001 -1001
- package/prompts/mobile/flow-build-phase-8.md +888 -888
- package/prompts/mobile/flow-build-phase-9.md +90 -90
- package/prompts/mobile/flow-build.md +135 -135
- package/prompts/mobile/flow-check-review.md +656 -20
- package/prompts/mobile/flow-check-test.md +526 -14
- package/prompts/mobile/flow-check.md +717 -67
- package/prompts/mobile/flow-commit.md +88 -119
- package/prompts/mobile/flow-docs-sync.md +620 -620
- package/prompts/mobile/flow-finish.md +919 -0
- package/prompts/mobile/flow-release.md +751 -0
- package/prompts/mobile/flow-work-api.md +1500 -0
- package/prompts/mobile/flow-work-feature.md +61 -61
- package/prompts/mobile/flow-work-fix.md +46 -46
- package/prompts/mobile/flow-work-refactor.md +48 -48
- package/prompts/mobile/flow-work-resume.md +34 -34
- package/prompts/mobile/flow-work.md +1605 -1329
- package/prompts/shared/mermaid-guidelines.md +102 -102
- package/prompts/shared/scope-levels.md +114 -114
- package/prompts/shared/smart-skip-preflight.md +214 -214
- package/prompts/shared/story-points.md +55 -55
- package/prompts/shared/task-format.md +74 -74
- package/prompts/shared/task-summary-template.md +277 -277
- package/templates/AGENT.template.md +443 -443
- package/templates/backend/.clauderules.template +112 -112
- package/templates/backend/.cursorrules.template +102 -102
- package/templates/backend/README.template.md +2 -2
- package/templates/backend/ai-instructions.template.md +2 -2
- package/templates/backend/copilot-instructions.template.md +2 -2
- package/templates/backend/docs/api.template.md +320 -320
- package/templates/backend/docs/business-flows.template.md +97 -97
- package/templates/backend/docs/code-standards.template.md +2 -2
- package/templates/backend/docs/contributing.template.md +3 -3
- package/templates/backend/docs/data-model.template.md +520 -520
- package/templates/backend/docs/testing.template.md +2 -2
- package/templates/backend/project-brief.template.md +2 -2
- package/templates/backend/specs/configuration.template.md +2 -2
- package/templates/backend/specs/security.template.md +2 -2
- package/templates/desktop/.clauderules.template +112 -112
- package/templates/desktop/.cursorrules.template +102 -102
- package/templates/desktop/README.template.md +170 -170
- package/templates/desktop/ai-instructions.template.md +366 -366
- package/templates/desktop/copilot-instructions.template.md +140 -140
- package/templates/desktop/docs/docs/api.template.md +320 -320
- package/templates/desktop/docs/docs/architecture.template.md +724 -724
- package/templates/desktop/docs/docs/business-flows.template.md +102 -102
- package/templates/desktop/docs/docs/code-standards.template.md +792 -792
- package/templates/desktop/docs/docs/contributing.template.md +149 -149
- package/templates/desktop/docs/docs/data-model.template.md +520 -520
- package/templates/desktop/docs/docs/operations.template.md +720 -720
- package/templates/desktop/docs/docs/testing.template.md +722 -722
- package/templates/desktop/project-brief.template.md +150 -150
- package/templates/desktop/specs/specs/configuration.template.md +121 -121
- package/templates/desktop/specs/specs/security.template.md +392 -392
- package/templates/frontend/README.template.md +2 -2
- package/templates/frontend/ai-instructions.template.md +2 -2
- package/templates/frontend/docs/api-integration.template.md +362 -362
- package/templates/frontend/docs/components.template.md +2 -2
- package/templates/frontend/docs/error-handling.template.md +360 -360
- package/templates/frontend/docs/operations.template.md +107 -107
- package/templates/frontend/docs/performance.template.md +124 -124
- package/templates/frontend/docs/pwa.template.md +119 -119
- package/templates/frontend/docs/state-management.template.md +2 -2
- package/templates/frontend/docs/styling.template.md +2 -2
- package/templates/frontend/docs/testing.template.md +2 -2
- package/templates/frontend/project-brief.template.md +2 -2
- package/templates/frontend/specs/accessibility.template.md +95 -95
- package/templates/frontend/specs/configuration.template.md +2 -2
- package/templates/frontend/specs/security.template.md +175 -175
- package/templates/fullstack/README.template.md +252 -252
- package/templates/fullstack/ai-instructions.template.md +444 -444
- package/templates/fullstack/project-brief.template.md +157 -157
- package/templates/fullstack/specs/configuration.template.md +340 -340
- package/templates/mobile/README.template.md +167 -167
- package/templates/mobile/ai-instructions.template.md +196 -196
- package/templates/mobile/docs/app-store.template.md +135 -135
- package/templates/mobile/docs/architecture.template.md +63 -63
- package/templates/mobile/docs/native-features.template.md +94 -94
- package/templates/mobile/docs/navigation.template.md +59 -59
- package/templates/mobile/docs/offline-strategy.template.md +65 -65
- package/templates/mobile/docs/permissions.template.md +56 -56
- package/templates/mobile/docs/state-management.template.md +85 -85
- package/templates/mobile/docs/testing.template.md +109 -109
- package/templates/mobile/project-brief.template.md +69 -69
- package/templates/mobile/specs/build-configuration.template.md +91 -91
- package/templates/mobile/specs/deployment.template.md +92 -92
- package/templates/work.template.md +61 -47
|
@@ -1,369 +1,369 @@
|
|
|
1
|
-
# Error Handling
|
|
2
|
-
|
|
3
|
-
> Error handling strategies and best practices for {{PROJECT_NAME}}
|
|
1
|
+
# Error Handling
|
|
2
|
+
|
|
3
|
+
> Error handling strategies and best practices for {{PROJECT_NAME}}
|
|
4
4
|
---
|
|
5
|
-
## 🎯 Error Handling Strategy
|
|
6
|
-
|
|
7
|
-
**Approach:** {{ERROR_HANDLING_STRATEGY}}
|
|
8
|
-
**Error Logging:** {{ERROR_LOGGING_TOOL}}
|
|
9
|
-
**Recovery Strategy:** {{ERROR_RECOVERY_STRATEGY}}
|
|
5
|
+
## 🎯 Error Handling Strategy
|
|
6
|
+
|
|
7
|
+
**Approach:** {{ERROR_HANDLING_STRATEGY}}
|
|
8
|
+
**Error Logging:** {{ERROR_LOGGING_TOOL}}
|
|
9
|
+
**Recovery Strategy:** {{ERROR_RECOVERY_STRATEGY}}
|
|
10
10
|
---
|
|
11
|
-
## 🛡️ Error Boundaries (React)
|
|
12
|
-
|
|
13
|
-
### Basic Error Boundary
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
// components/ErrorBoundary.tsx
|
|
17
|
-
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
|
18
|
-
|
|
19
|
-
interface Props {
|
|
20
|
-
children: ReactNode;
|
|
21
|
-
fallback?: ReactNode;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface State {
|
|
25
|
-
hasError: boolean;
|
|
26
|
-
error: Error | null;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export class ErrorBoundary extends Component<Props, State> {
|
|
30
|
-
constructor(props: Props) {
|
|
31
|
-
super(props);
|
|
32
|
-
this.state = { hasError: false, error: null };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
static getDerivedStateFromError(error: Error): State {
|
|
36
|
-
return { hasError: true, error };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
40
|
-
// Log error to error tracking service
|
|
41
|
-
console.error('Error caught by boundary:', error, errorInfo);
|
|
42
|
-
|
|
43
|
-
// Send to Sentry/LogRocket
|
|
44
|
-
if (window.Sentry) {
|
|
45
|
-
window.Sentry.captureException(error, { contexts: { react: errorInfo } });
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
render() {
|
|
50
|
-
if (this.state.hasError) {
|
|
51
|
-
return this.props.fallback || (
|
|
52
|
-
<div className="error-boundary">
|
|
53
|
-
<h2>Something went wrong</h2>
|
|
54
|
-
<button onClick={() => this.setState({ hasError: false, error: null })}>
|
|
55
|
-
Try again
|
|
56
|
-
</button>
|
|
57
|
-
</div>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return this.props.children;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Usage
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
// App.tsx
|
|
70
|
-
<ErrorBoundary fallback={<ErrorFallback />}>
|
|
71
|
-
<App />
|
|
72
|
-
</ErrorBoundary>
|
|
73
|
-
```
|
|
11
|
+
## 🛡️ Error Boundaries (React)
|
|
12
|
+
|
|
13
|
+
### Basic Error Boundary
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
// components/ErrorBoundary.tsx
|
|
17
|
+
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
fallback?: ReactNode;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface State {
|
|
25
|
+
hasError: boolean;
|
|
26
|
+
error: Error | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class ErrorBoundary extends Component<Props, State> {
|
|
30
|
+
constructor(props: Props) {
|
|
31
|
+
super(props);
|
|
32
|
+
this.state = { hasError: false, error: null };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static getDerivedStateFromError(error: Error): State {
|
|
36
|
+
return { hasError: true, error };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
40
|
+
// Log error to error tracking service
|
|
41
|
+
console.error('Error caught by boundary:', error, errorInfo);
|
|
42
|
+
|
|
43
|
+
// Send to Sentry/LogRocket
|
|
44
|
+
if (window.Sentry) {
|
|
45
|
+
window.Sentry.captureException(error, { contexts: { react: errorInfo } });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
render() {
|
|
50
|
+
if (this.state.hasError) {
|
|
51
|
+
return this.props.fallback || (
|
|
52
|
+
<div className="error-boundary">
|
|
53
|
+
<h2>Something went wrong</h2>
|
|
54
|
+
<button onClick={() => this.setState({ hasError: false, error: null })}>
|
|
55
|
+
Try again
|
|
56
|
+
</button>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return this.props.children;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Usage
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// App.tsx
|
|
70
|
+
<ErrorBoundary fallback={<ErrorFallback />}>
|
|
71
|
+
<App />
|
|
72
|
+
</ErrorBoundary>
|
|
73
|
+
```
|
|
74
74
|
---
|
|
75
|
-
## 🔄 Global Error Handlers
|
|
76
|
-
|
|
77
|
-
### React Global Error Handler
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
// utils/globalErrorHandler.ts
|
|
81
|
-
export function setupGlobalErrorHandler() {
|
|
82
|
-
// Unhandled errors
|
|
83
|
-
window.addEventListener('error', (event) => {
|
|
84
|
-
console.error('Global error:', event.error);
|
|
85
|
-
logError(event.error, { type: 'unhandled' });
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// Unhandled promise rejections
|
|
89
|
-
window.addEventListener('unhandledrejection', (event) => {
|
|
90
|
-
console.error('Unhandled rejection:', event.reason);
|
|
91
|
-
logError(event.reason, { type: 'unhandledRejection' });
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Vue Global Error Handler
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
// main.ts
|
|
100
|
-
app.config.errorHandler = (err, instance, info) => {
|
|
101
|
-
console.error('Vue error:', err, info);
|
|
102
|
-
logError(err, { component: instance?.$options.name, info });
|
|
103
|
-
};
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### Angular Global Error Handler
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
// services/global-error-handler.service.ts
|
|
110
|
-
@Injectable()
|
|
111
|
-
export class GlobalErrorHandler implements ErrorHandler {
|
|
112
|
-
constructor(private errorLoggingService: ErrorLoggingService) {}
|
|
113
|
-
|
|
114
|
-
handleError(error: Error): void {
|
|
115
|
-
console.error('Angular error:', error);
|
|
116
|
-
this.errorLoggingService.logError(error);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
```
|
|
75
|
+
## 🔄 Global Error Handlers
|
|
76
|
+
|
|
77
|
+
### React Global Error Handler
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// utils/globalErrorHandler.ts
|
|
81
|
+
export function setupGlobalErrorHandler() {
|
|
82
|
+
// Unhandled errors
|
|
83
|
+
window.addEventListener('error', (event) => {
|
|
84
|
+
console.error('Global error:', event.error);
|
|
85
|
+
logError(event.error, { type: 'unhandled' });
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Unhandled promise rejections
|
|
89
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
90
|
+
console.error('Unhandled rejection:', event.reason);
|
|
91
|
+
logError(event.reason, { type: 'unhandledRejection' });
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Vue Global Error Handler
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// main.ts
|
|
100
|
+
app.config.errorHandler = (err, instance, info) => {
|
|
101
|
+
console.error('Vue error:', err, info);
|
|
102
|
+
logError(err, { component: instance?.$options.name, info });
|
|
103
|
+
};
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Angular Global Error Handler
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// services/global-error-handler.service.ts
|
|
110
|
+
@Injectable()
|
|
111
|
+
export class GlobalErrorHandler implements ErrorHandler {
|
|
112
|
+
constructor(private errorLoggingService: ErrorLoggingService) {}
|
|
113
|
+
|
|
114
|
+
handleError(error: Error): void {
|
|
115
|
+
console.error('Angular error:', error);
|
|
116
|
+
this.errorLoggingService.logError(error);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
120
|
---
|
|
121
|
-
## 📡 API Error Handling
|
|
122
|
-
|
|
123
|
-
### Error Types
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
// types/errors.ts
|
|
127
|
-
export class ApiError extends Error {
|
|
128
|
-
constructor(
|
|
129
|
-
public status: number,
|
|
130
|
-
public message: string,
|
|
131
|
-
public code?: string,
|
|
132
|
-
public details?: unknown
|
|
133
|
-
) {
|
|
134
|
-
super(message);
|
|
135
|
-
this.name = 'ApiError';
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export class NetworkError extends Error {
|
|
140
|
-
constructor(message = 'Network error') {
|
|
141
|
-
super(message);
|
|
142
|
-
this.name = 'NetworkError';
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export class TimeoutError extends Error {
|
|
147
|
-
constructor(message = 'Request timeout') {
|
|
148
|
-
super(message);
|
|
149
|
-
this.name = 'TimeoutError';
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### Error Handling by Status Code
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
// utils/apiErrorHandler.ts
|
|
158
|
-
export function handleApiError(error: unknown): {
|
|
159
|
-
message: string;
|
|
160
|
-
action: 'retry' | 'redirect' | 'show' | 'ignore';
|
|
161
|
-
} {
|
|
162
|
-
if (error instanceof ApiError) {
|
|
163
|
-
switch (error.status) {
|
|
164
|
-
case 400:
|
|
165
|
-
return {
|
|
166
|
-
message: 'Invalid request. Please check your input.',
|
|
167
|
-
action: 'show',
|
|
168
|
-
};
|
|
169
|
-
case 401:
|
|
170
|
-
return {
|
|
171
|
-
message: 'Session expired. Please log in again.',
|
|
172
|
-
action: 'redirect', // Redirect to login
|
|
173
|
-
};
|
|
174
|
-
case 403:
|
|
175
|
-
return {
|
|
176
|
-
message: 'Access denied.',
|
|
177
|
-
action: 'show',
|
|
178
|
-
};
|
|
179
|
-
case 404:
|
|
180
|
-
return {
|
|
181
|
-
message: 'Resource not found.',
|
|
182
|
-
action: 'show',
|
|
183
|
-
};
|
|
184
|
-
case 429:
|
|
185
|
-
return {
|
|
186
|
-
message: 'Too many requests. Please try again later.',
|
|
187
|
-
action: 'retry',
|
|
188
|
-
};
|
|
189
|
-
case 500:
|
|
190
|
-
case 502:
|
|
191
|
-
case 503:
|
|
192
|
-
case 504:
|
|
193
|
-
return {
|
|
194
|
-
message: 'Server error. Please try again.',
|
|
195
|
-
action: 'retry',
|
|
196
|
-
};
|
|
197
|
-
default:
|
|
198
|
-
return {
|
|
199
|
-
message: 'An error occurred. Please try again.',
|
|
200
|
-
action: 'show',
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (error instanceof NetworkError) {
|
|
206
|
-
return {
|
|
207
|
-
message: 'Network error. Please check your connection.',
|
|
208
|
-
action: 'retry',
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (error instanceof TimeoutError) {
|
|
213
|
-
return {
|
|
214
|
-
message: 'Request timed out. Please try again.',
|
|
215
|
-
action: 'retry',
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return {
|
|
220
|
-
message: 'An unexpected error occurred.',
|
|
221
|
-
action: 'show',
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
```
|
|
121
|
+
## 📡 API Error Handling
|
|
122
|
+
|
|
123
|
+
### Error Types
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// types/errors.ts
|
|
127
|
+
export class ApiError extends Error {
|
|
128
|
+
constructor(
|
|
129
|
+
public status: number,
|
|
130
|
+
public message: string,
|
|
131
|
+
public code?: string,
|
|
132
|
+
public details?: unknown
|
|
133
|
+
) {
|
|
134
|
+
super(message);
|
|
135
|
+
this.name = 'ApiError';
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export class NetworkError extends Error {
|
|
140
|
+
constructor(message = 'Network error') {
|
|
141
|
+
super(message);
|
|
142
|
+
this.name = 'NetworkError';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export class TimeoutError extends Error {
|
|
147
|
+
constructor(message = 'Request timeout') {
|
|
148
|
+
super(message);
|
|
149
|
+
this.name = 'TimeoutError';
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Error Handling by Status Code
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// utils/apiErrorHandler.ts
|
|
158
|
+
export function handleApiError(error: unknown): {
|
|
159
|
+
message: string;
|
|
160
|
+
action: 'retry' | 'redirect' | 'show' | 'ignore';
|
|
161
|
+
} {
|
|
162
|
+
if (error instanceof ApiError) {
|
|
163
|
+
switch (error.status) {
|
|
164
|
+
case 400:
|
|
165
|
+
return {
|
|
166
|
+
message: 'Invalid request. Please check your input.',
|
|
167
|
+
action: 'show',
|
|
168
|
+
};
|
|
169
|
+
case 401:
|
|
170
|
+
return {
|
|
171
|
+
message: 'Session expired. Please log in again.',
|
|
172
|
+
action: 'redirect', // Redirect to login
|
|
173
|
+
};
|
|
174
|
+
case 403:
|
|
175
|
+
return {
|
|
176
|
+
message: 'Access denied.',
|
|
177
|
+
action: 'show',
|
|
178
|
+
};
|
|
179
|
+
case 404:
|
|
180
|
+
return {
|
|
181
|
+
message: 'Resource not found.',
|
|
182
|
+
action: 'show',
|
|
183
|
+
};
|
|
184
|
+
case 429:
|
|
185
|
+
return {
|
|
186
|
+
message: 'Too many requests. Please try again later.',
|
|
187
|
+
action: 'retry',
|
|
188
|
+
};
|
|
189
|
+
case 500:
|
|
190
|
+
case 502:
|
|
191
|
+
case 503:
|
|
192
|
+
case 504:
|
|
193
|
+
return {
|
|
194
|
+
message: 'Server error. Please try again.',
|
|
195
|
+
action: 'retry',
|
|
196
|
+
};
|
|
197
|
+
default:
|
|
198
|
+
return {
|
|
199
|
+
message: 'An error occurred. Please try again.',
|
|
200
|
+
action: 'show',
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (error instanceof NetworkError) {
|
|
206
|
+
return {
|
|
207
|
+
message: 'Network error. Please check your connection.',
|
|
208
|
+
action: 'retry',
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (error instanceof TimeoutError) {
|
|
213
|
+
return {
|
|
214
|
+
message: 'Request timed out. Please try again.',
|
|
215
|
+
action: 'retry',
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
message: 'An unexpected error occurred.',
|
|
221
|
+
action: 'show',
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
225
|
---
|
|
226
|
-
## 🔁 Retry Logic
|
|
227
|
-
|
|
228
|
-
### Exponential Backoff Retry
|
|
229
|
-
|
|
230
|
-
```typescript
|
|
231
|
-
// utils/retry.ts
|
|
232
|
-
export async function retryWithBackoff<T>(
|
|
233
|
-
fn: () => Promise<T>,
|
|
234
|
-
maxRetries = 3,
|
|
235
|
-
initialDelay = 1000
|
|
236
|
-
): Promise<T> {
|
|
237
|
-
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
238
|
-
try {
|
|
239
|
-
return await fn();
|
|
240
|
-
} catch (error) {
|
|
241
|
-
if (attempt === maxRetries - 1) throw error;
|
|
242
|
-
|
|
243
|
-
// Don't retry on 4xx errors (except 429)
|
|
244
|
-
if (error instanceof ApiError && error.status >= 400 && error.status < 500 && error.status !== 429) {
|
|
245
|
-
throw error;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Exponential backoff: 1s, 2s, 4s
|
|
249
|
-
const delay = initialDelay * Math.pow(2, attempt);
|
|
250
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
throw new Error('Max retries exceeded');
|
|
254
|
-
}
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### Usage with TanStack Query
|
|
258
|
-
|
|
259
|
-
```typescript
|
|
260
|
-
import { useQuery } from '@tanstack/react-query';
|
|
261
|
-
|
|
262
|
-
const { data } = useQuery({
|
|
263
|
-
queryKey: ['users', id],
|
|
264
|
-
queryFn: () => usersService.getById(id),
|
|
265
|
-
retry: (failureCount, error) => {
|
|
266
|
-
// Don't retry on 4xx errors
|
|
267
|
-
if (error instanceof ApiError && error.status >= 400 && error.status < 500) {
|
|
268
|
-
return false;
|
|
269
|
-
}
|
|
270
|
-
return failureCount < 3;
|
|
271
|
-
},
|
|
272
|
-
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
|
|
273
|
-
});
|
|
274
|
-
```
|
|
226
|
+
## 🔁 Retry Logic
|
|
227
|
+
|
|
228
|
+
### Exponential Backoff Retry
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
// utils/retry.ts
|
|
232
|
+
export async function retryWithBackoff<T>(
|
|
233
|
+
fn: () => Promise<T>,
|
|
234
|
+
maxRetries = 3,
|
|
235
|
+
initialDelay = 1000
|
|
236
|
+
): Promise<T> {
|
|
237
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
238
|
+
try {
|
|
239
|
+
return await fn();
|
|
240
|
+
} catch (error) {
|
|
241
|
+
if (attempt === maxRetries - 1) throw error;
|
|
242
|
+
|
|
243
|
+
// Don't retry on 4xx errors (except 429)
|
|
244
|
+
if (error instanceof ApiError && error.status >= 400 && error.status < 500 && error.status !== 429) {
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Exponential backoff: 1s, 2s, 4s
|
|
249
|
+
const delay = initialDelay * Math.pow(2, attempt);
|
|
250
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
throw new Error('Max retries exceeded');
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Usage with TanStack Query
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import { useQuery } from '@tanstack/react-query';
|
|
261
|
+
|
|
262
|
+
const { data } = useQuery({
|
|
263
|
+
queryKey: ['users', id],
|
|
264
|
+
queryFn: () => usersService.getById(id),
|
|
265
|
+
retry: (failureCount, error) => {
|
|
266
|
+
// Don't retry on 4xx errors
|
|
267
|
+
if (error instanceof ApiError && error.status >= 400 && error.status < 500) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
return failureCount < 3;
|
|
271
|
+
},
|
|
272
|
+
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
|
|
273
|
+
});
|
|
274
|
+
```
|
|
275
275
|
---
|
|
276
|
-
## 📝 Error Logging
|
|
277
|
-
|
|
278
|
-
### Error Logging Service
|
|
279
|
-
|
|
280
|
-
```typescript
|
|
281
|
-
// services/errorLogging.ts
|
|
282
|
-
import * as Sentry from '@sentry/react';
|
|
283
|
-
|
|
284
|
-
export function logError(error: Error, context?: Record<string, unknown>) {
|
|
285
|
-
// Development: Console
|
|
286
|
-
if (import.meta.env.DEV) {
|
|
287
|
-
console.error('Error:', error, context);
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Production: Sentry
|
|
292
|
-
Sentry.captureException(error, {
|
|
293
|
-
contexts: {
|
|
294
|
-
custom: context || {},
|
|
295
|
-
},
|
|
296
|
-
tags: {
|
|
297
|
-
component: context?.component as string,
|
|
298
|
-
},
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
### User Context
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
// Add user context to errors
|
|
307
|
-
Sentry.setUser({
|
|
308
|
-
id: user.id,
|
|
309
|
-
email: user.email,
|
|
310
|
-
username: user.username,
|
|
311
|
-
});
|
|
312
|
-
```
|
|
276
|
+
## 📝 Error Logging
|
|
277
|
+
|
|
278
|
+
### Error Logging Service
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
// services/errorLogging.ts
|
|
282
|
+
import * as Sentry from '@sentry/react';
|
|
283
|
+
|
|
284
|
+
export function logError(error: Error, context?: Record<string, unknown>) {
|
|
285
|
+
// Development: Console
|
|
286
|
+
if (import.meta.env.DEV) {
|
|
287
|
+
console.error('Error:', error, context);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Production: Sentry
|
|
292
|
+
Sentry.captureException(error, {
|
|
293
|
+
contexts: {
|
|
294
|
+
custom: context || {},
|
|
295
|
+
},
|
|
296
|
+
tags: {
|
|
297
|
+
component: context?.component as string,
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### User Context
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// Add user context to errors
|
|
307
|
+
Sentry.setUser({
|
|
308
|
+
id: user.id,
|
|
309
|
+
email: user.email,
|
|
310
|
+
username: user.username,
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
313
|
---
|
|
314
|
-
## 🎨 Error UI Components
|
|
315
|
-
|
|
316
|
-
### Error Display Component
|
|
317
|
-
|
|
318
|
-
```typescript
|
|
319
|
-
// components/ErrorDisplay.tsx
|
|
320
|
-
interface ErrorDisplayProps {
|
|
321
|
-
error: Error;
|
|
322
|
-
onRetry?: () => void;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
export const ErrorDisplay: React.FC<ErrorDisplayProps> = ({ error, onRetry }) => {
|
|
326
|
-
const { message, action } = handleApiError(error);
|
|
327
|
-
|
|
328
|
-
return (
|
|
329
|
-
<div className="error-display">
|
|
330
|
-
<h3>Oops! Something went wrong</h3>
|
|
331
|
-
<p>{message}</p>
|
|
332
|
-
{action === 'retry' && onRetry && (
|
|
333
|
-
<button onClick={onRetry}>Try Again</button>
|
|
334
|
-
)}
|
|
335
|
-
</div>
|
|
336
|
-
);
|
|
337
|
-
};
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
### Fallback UI
|
|
341
|
-
|
|
342
|
-
```typescript
|
|
343
|
-
// components/ErrorFallback.tsx
|
|
344
|
-
export const ErrorFallback: React.FC<{ error: Error; resetError: () => void }> = ({
|
|
345
|
-
error,
|
|
346
|
-
resetError,
|
|
347
|
-
}) => {
|
|
348
|
-
return (
|
|
349
|
-
<div className="error-fallback">
|
|
350
|
-
<h2>Something went wrong</h2>
|
|
351
|
-
<pre>{error.message}</pre>
|
|
352
|
-
<button onClick={resetError}>Try again</button>
|
|
353
|
-
</div>
|
|
354
|
-
);
|
|
355
|
-
};
|
|
356
|
-
```
|
|
314
|
+
## 🎨 Error UI Components
|
|
315
|
+
|
|
316
|
+
### Error Display Component
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
// components/ErrorDisplay.tsx
|
|
320
|
+
interface ErrorDisplayProps {
|
|
321
|
+
error: Error;
|
|
322
|
+
onRetry?: () => void;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export const ErrorDisplay: React.FC<ErrorDisplayProps> = ({ error, onRetry }) => {
|
|
326
|
+
const { message, action } = handleApiError(error);
|
|
327
|
+
|
|
328
|
+
return (
|
|
329
|
+
<div className="error-display">
|
|
330
|
+
<h3>Oops! Something went wrong</h3>
|
|
331
|
+
<p>{message}</p>
|
|
332
|
+
{action === 'retry' && onRetry && (
|
|
333
|
+
<button onClick={onRetry}>Try Again</button>
|
|
334
|
+
)}
|
|
335
|
+
</div>
|
|
336
|
+
);
|
|
337
|
+
};
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Fallback UI
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
// components/ErrorFallback.tsx
|
|
344
|
+
export const ErrorFallback: React.FC<{ error: Error; resetError: () => void }> = ({
|
|
345
|
+
error,
|
|
346
|
+
resetError,
|
|
347
|
+
}) => {
|
|
348
|
+
return (
|
|
349
|
+
<div className="error-fallback">
|
|
350
|
+
<h2>Something went wrong</h2>
|
|
351
|
+
<pre>{error.message}</pre>
|
|
352
|
+
<button onClick={resetError}>Try again</button>
|
|
353
|
+
</div>
|
|
354
|
+
);
|
|
355
|
+
};
|
|
356
|
+
```
|
|
357
357
|
---
|
|
358
|
-
## 🔗 Related Documents
|
|
359
|
-
|
|
360
|
-
- [API Integration](api-integration.md) - API error handling
|
|
361
|
-
- [State Management](state-management.md) - Error state management
|
|
362
|
-
- [Security](security.md) - Security error handling
|
|
358
|
+
## 🔗 Related Documents
|
|
359
|
+
|
|
360
|
+
- [API Integration](api-integration.md) - API error handling
|
|
361
|
+
- [State Management](state-management.md) - Error state management
|
|
362
|
+
- [Security](security.md) - Security error handling
|
|
363
363
|
---
|
|
364
|
-
**Last Updated:** {{GENERATION_DATE}}
|
|
365
|
-
|
|
366
|
-
**Error Handling Strategy:** {{ERROR_HANDLING_STRATEGY}}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
364
|
+
**Last Updated:** {{GENERATION_DATE}}
|
|
365
|
+
|
|
366
|
+
**Error Handling Strategy:** {{ERROR_HANDLING_STRATEGY}}
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
|