@cleocode/contracts 2026.3.38 → 2026.3.39

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/src/errors.ts ADDED
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Central error utilities for CLEO.
3
+ *
4
+ * Provides consistent error handling patterns across the codebase.
5
+ * DRY error formatting, normalization, and transformation utilities.
6
+ *
7
+ * @task T5702
8
+ */
9
+
10
+ /**
11
+ * Normalize any thrown value into a standardized error object.
12
+ *
13
+ * Handles:
14
+ * - Error instances (preserves stack trace info)
15
+ * - Strings (wraps in Error)
16
+ * - Objects with message property
17
+ * - null/undefined (provides fallback)
18
+ *
19
+ * @param error - The thrown value to normalize
20
+ * @param fallbackMessage - Message to use if error provides none
21
+ * @returns Normalized error with consistent shape
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * try {
26
+ * await riskyOperation();
27
+ * } catch (err) {
28
+ * const error = normalizeError(err, 'Operation failed');
29
+ * console.error(error.message);
30
+ * }
31
+ * ```
32
+ */
33
+ export function normalizeError(
34
+ error: unknown,
35
+ fallbackMessage = 'An unexpected error occurred',
36
+ ): Error {
37
+ if (error instanceof Error) {
38
+ return error;
39
+ }
40
+
41
+ if (typeof error === 'string') {
42
+ return new Error(error);
43
+ }
44
+
45
+ if (
46
+ error !== null &&
47
+ typeof error === 'object' &&
48
+ 'message' in error &&
49
+ typeof error.message === 'string'
50
+ ) {
51
+ return new Error(error.message);
52
+ }
53
+
54
+ return new Error(fallbackMessage);
55
+ }
56
+
57
+ /**
58
+ * Extract a human-readable message from any error value.
59
+ *
60
+ * Safe to use on unknown thrown values without type guards.
61
+ *
62
+ * @param error - The error value
63
+ * @param fallback - Fallback message if extraction fails
64
+ * @returns The error message string
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * const message = getErrorMessage(err, 'Unknown error');
69
+ * ```
70
+ */
71
+ export function getErrorMessage(error: unknown, fallback = 'Unknown error'): string {
72
+ if (error instanceof Error) {
73
+ return error.message;
74
+ }
75
+
76
+ if (typeof error === 'string') {
77
+ return error;
78
+ }
79
+
80
+ if (
81
+ error !== null &&
82
+ typeof error === 'object' &&
83
+ 'message' in error &&
84
+ typeof error.message === 'string'
85
+ ) {
86
+ return error.message;
87
+ }
88
+
89
+ return fallback;
90
+ }
91
+
92
+ /**
93
+ * Format error details for logging or display.
94
+ *
95
+ * Includes stack trace for Error instances when includeStack is true.
96
+ *
97
+ * @param error - The error to format
98
+ * @param context - Optional context to prepend
99
+ * @param includeStack - Whether to include stack traces (default: false)
100
+ * @returns Formatted error string
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * console.error(formatError(err, 'Database connection'));
105
+ * // Output: [Database connection] Connection refused
106
+ * ```
107
+ */
108
+ export function formatError(error: unknown, context?: string, includeStack = false): string {
109
+ const message = getErrorMessage(error);
110
+ const prefix = context ? `[${context}] ` : '';
111
+ let result = `${prefix}${message}`;
112
+
113
+ if (includeStack && error instanceof Error && error.stack) {
114
+ result += `\n${error.stack}`;
115
+ }
116
+
117
+ return result;
118
+ }
119
+
120
+ /**
121
+ * Check if an error represents a specific error type by code or name.
122
+ *
123
+ * Useful for conditional error handling based on error types.
124
+ *
125
+ * @param error - The error to check
126
+ * @param codeOrName - The error code or name to match
127
+ * @returns True if the error matches
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * if (isErrorType(err, 'E_NOT_FOUND')) {
132
+ * // Handle not found specifically
133
+ * }
134
+ * ```
135
+ */
136
+ export function isErrorType(error: unknown, codeOrName: string): boolean {
137
+ if (error instanceof Error) {
138
+ if (error.name === codeOrName) {
139
+ return true;
140
+ }
141
+ // Check for custom code property
142
+ if ('code' in error && error.code === codeOrName) {
143
+ return true;
144
+ }
145
+ }
146
+ return false;
147
+ }
148
+
149
+ /**
150
+ * Create a standardized error result object.
151
+ *
152
+ * Common pattern for operations that return { success: boolean, error?: string }
153
+ *
154
+ * @param error - The error value
155
+ * @returns Error result object
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * return createErrorResult(err);
160
+ * // Returns: { success: false, error: "Something went wrong" }
161
+ * ```
162
+ */
163
+ export function createErrorResult(error: unknown): { success: false; error: string } {
164
+ return {
165
+ success: false,
166
+ error: getErrorMessage(error),
167
+ };
168
+ }
169
+
170
+ /**
171
+ * Create a standardized success result object.
172
+ *
173
+ * @returns Success result object
174
+ *
175
+ * @example
176
+ * ```typescript
177
+ * return createSuccessResult();
178
+ * // Returns: { success: true }
179
+ * ```
180
+ */
181
+ export function createSuccessResult(): { success: true } {
182
+ return { success: true };
183
+ }
184
+
185
+ /**
186
+ * Type guard for error results.
187
+ *
188
+ * @param result - The result to check
189
+ * @returns True if the result is an error result
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * const result = await someOperation();
194
+ * if (isErrorResult(result)) {
195
+ * console.error(result.error);
196
+ * }
197
+ * ```
198
+ */
199
+ export function isErrorResult(result: {
200
+ success: boolean;
201
+ error?: string;
202
+ }): result is { success: false; error: string } {
203
+ return !result.success;
204
+ }