@bombillazo/error-x 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Hector Ayala
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,352 @@
1
+ # error-x
2
+
3
+ A smart, isomorphic, and satisfying error library for TypeScript applications. Provides type-safe error handling with great DX, solving common pain points like unknown error types, lost stack traces, async error handling, and error serialization.
4
+
5
+ ## Features
6
+
7
+ - 🎯 **Type-safe error handling** for great DX
8
+ - 🔄 **Smart error conversion** from various formats (API responses, strings, Error objects)
9
+ - 📝 **Auto-formatted messages and error codes** with proper capitalization and punctuation
10
+ - 👤 **User-friendly messages** separate from technical messages
11
+ - 🕒 **Automatic timestamps** for error tracking
12
+ - 🔗 **Error chaining** with cause preservation and stack trace preservation
13
+ - 📊 **Flexible metadata** for additional context
14
+ - 🎛️ **Error handling options** for UI behavior and application actions
15
+ - 📦 **Serialization/deserialization** support for network transfer and storage
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pnpm add @bombillazo/error-x
21
+ # or
22
+ npm install @bombillazo/error-x
23
+ # or
24
+ yarn add @bombillazo/error-x
25
+ ```
26
+
27
+ > [!WARNING]
28
+ >
29
+ > This library is currently in pre-v1.0 development. While we strive to minimize breaking changes, the API may evolve based on feedback and real-world usage. We recommend pinning to specific versions and reviewing release notes when updating.
30
+ >
31
+ > Once we reach version 1.0, we plan to minimize API changes and follow semantic versioning.
32
+
33
+ ## Quick Start
34
+
35
+ ```typescript
36
+ import { ErrorX, HandlingTargets, type HandlingTarget, type ErrorAction } from 'error-x'
37
+
38
+ // Minimal usage (all parameters optional)
39
+ const error = new ErrorX()
40
+
41
+ // Simple usage
42
+ const error = new ErrorX({ message: 'Database connection failed' })
43
+
44
+ // With full options
45
+ const error = new ErrorX({
46
+ message: 'User authentication failed',
47
+ name: 'AuthError',
48
+ code: 'AUTH_FAILED',
49
+ uiMessage: 'Please check your credentials and try again',
50
+ cause: originalError, // Chain errors while preserving stack traces
51
+ metadata: {
52
+ userId: 123,
53
+ loginAttempt: 3,
54
+ },
55
+ actions: [
56
+ { action: 'notify', payload: { targets: [HandlingTargets.TOAST, HandlingTargets.BANNER] } },
57
+ { action: 'redirect', payload: { redirectURL: '/login', delay: 1000 } },
58
+ { action: 'custom', payload: { type: 'analytics', event: 'auth_failed', userId: 123, category: 'errors', severity: 'high' } }
59
+ ]
60
+ })
61
+ ```
62
+
63
+ ## Documentation
64
+
65
+ ### API Reference
66
+
67
+ For complete API documentation with detailed descriptions, examples, and type information, see:
68
+
69
+ - **[📖 Complete API Documentation](docs/api/error-x.md)** - Full API reference with examples
70
+ - **[🏗️ ErrorX Class](docs/api/error-x.errorx.md)** - Main ErrorX class documentation
71
+ - **[🔧 Types](docs/api/error-x.md#type-aliases)** - All available types and interfaces
72
+
73
+ ### Constructor
74
+
75
+ ```typescript
76
+ new ErrorX(options?: {
77
+ name?: string // Optional: Error type
78
+ message?: string // Optional: Technical error message (default: 'An error occurred')
79
+ code?: string | number // Optional: Error code (auto-generated from name if not provided)
80
+ uiMessage?: string // Optional: User-friendly message
81
+ cause?: Error | unknown // Optional: Original error that caused this (preserves stack traces)
82
+ metadata?: Record<string, any> // Optional: Additional context data
83
+ actions?: ErrorAction[] // Optional: Configuration for application actions to perform when error occurs
84
+ })
85
+ ```
86
+
87
+ **All parameters are optional** - ErrorX uses sensible defaults and auto-generates missing values.
88
+
89
+ ### Properties
90
+
91
+ | Property | Type | Default Value | Description |
92
+ | --------- | ---------------------------- | ------------------------------------- | ----------------------------------------------------------------- |
93
+ | name | `string` | `'Error'` | Error type/title |
94
+ | code | `string` | Auto-generated from name or `'ERROR'` | Error identifier (auto-generated from name in UPPER_SNAKE_CASE) |
95
+ | message | `string` | `'An error occurred'` | Auto-formatted technical error message |
96
+ | uiMessage | `string \| undefined` | `undefined` | User-friendly message for display |
97
+ | stack | `string` | Auto-generated | Stack trace with preservation and cleaning (inherited from Error) |
98
+ | cause | `unknown` | `undefined` | Original error that caused this (preserves full error chain) |
99
+ | timestamp | `Date` | `new Date()` | When the error was created (readonly) |
100
+ | metadata | `Record<string, any> \| undefined` | `undefined` | Additional context and data |
101
+ | actions | `ErrorAction[] \| undefined` | `undefined` | Array of actions to perform when error occurs (readonly) |
102
+
103
+ ### Actions System
104
+
105
+ The `actions` property allows errors to trigger application logic, passing along the necessary data. Your application error handler can route or execute these actions to achieve the desired behavior.
106
+
107
+ `actions` accepts an array of `ErrorAction` objects. The library provides predefined action types with type-safe payloads, and a `CustomAction` type for application-specific actions.
108
+
109
+ #### Action Types
110
+
111
+ | Action Type | Action Value | Required Payload | Description |
112
+ | ----------- | ------------ | ---------------- | ----------- |
113
+ | NotifyAction | `'notify'` | `{ targets: HandlingTarget[], ...any }` | Display notification in specified UI targets |
114
+ | LogoutAction | `'logout'` | `{ ...any }` (optional) | Log out the current user |
115
+ | RedirectAction | `'redirect'` | `{ redirectURL: string, ...any }` | Redirect to a specific URL |
116
+ | CustomAction | `'custom'` | `{ ...any }` (optional) | Application-specific actions with flexible payload structure |
117
+
118
+ ```typescript
119
+ import { HandlingTargets, type ErrorAction, type CustomAction } from 'error-x'
120
+
121
+ // Predefined actions with typed payloads
122
+ const error1 = new ErrorX({
123
+ message: 'Payment failed',
124
+ actions: [
125
+ { action: 'notify', payload: { targets: [HandlingTargets.MODAL] } },
126
+ { action: 'redirect', payload: { redirectURL: '/payment', delay: 2000 } }
127
+ ]
128
+ })
129
+
130
+ // Logout action
131
+ const error2 = new ErrorX({
132
+ message: 'Session expired',
133
+ actions: [
134
+ { action: 'logout', payload: { clearStorage: true } },
135
+ { action: 'notify', payload: { targets: [HandlingTargets.TOAST] } }
136
+ ]
137
+ })
138
+
139
+ // Custom actions for application-specific logic
140
+ const error3 = new ErrorX({
141
+ message: 'API rate limit exceeded',
142
+ actions: [
143
+ {
144
+ action: 'custom',
145
+ payload: {
146
+ type: 'show-rate-limit-modal',
147
+ resetTime: Date.now() + 60000,
148
+ message: 'Too many requests. Please wait.'
149
+ }
150
+ },
151
+ {
152
+ action: 'custom',
153
+ payload: {
154
+ type: 'analytics-track',
155
+ event: 'rate_limit_hit',
156
+ severity: 'warning',
157
+ category: 'api'
158
+ }
159
+ },
160
+ {
161
+ action: 'custom',
162
+ payload: {
163
+ type: 'cache-request',
164
+ retryAfter: 60,
165
+ endpoint: '/api/users'
166
+ }
167
+ }
168
+ ]
169
+ })
170
+ ```
171
+
172
+ ### Notify Targets
173
+
174
+ For the `NotifyAction`, notify targets can be predefined enum values or custom strings for flexibility:
175
+
176
+ #### Predefined Display Targets
177
+
178
+ | Target | Enum Value | Description |
179
+ | ------ | ---------- | ----------- |
180
+ | MODAL | `'modal'` | Display in a modal dialog |
181
+ | TOAST | `'toast'` | Display as a toast notification |
182
+ | INLINE | `'inline'` | Display inline with content |
183
+ | BANNER | `'banner'` | Display as a banner/alert bar |
184
+ | CONSOLE | `'console'` | Log to browser/server console |
185
+ | LOGGER | `'logger'` | Send to logging service |
186
+ | NOTIFICATION | `'notification'` | System notification |
187
+
188
+ ```typescript
189
+ import { HandlingTargets, type HandlingTarget } from 'error-x'
190
+
191
+ const error = new ErrorX({
192
+ message: 'Mixed error',
193
+ actions: [
194
+ {
195
+ action: 'notify',
196
+ payload: {
197
+ targets: [
198
+ HandlingTargets.CONSOLE, // Predefined
199
+ 'my-custom-logger', // Custom
200
+ HandlingTargets.BANNER, // Predefined
201
+ 'analytics-tracker' // Custom
202
+ ]
203
+ }
204
+ }
205
+ ]
206
+ })
207
+ ```
208
+
209
+ ## Smart Features
210
+
211
+ ### Auto Code Generation
212
+
213
+ Error codes are automatically generated from the error name:
214
+
215
+ ```typescript
216
+ new ErrorX({ message: 'Failed', name: 'DatabaseError' })
217
+ // code: 'DATABASE_ERROR'
218
+
219
+ new ErrorX({ message: 'Failed', name: 'userAuthError' })
220
+ // code: 'USER_AUTH_ERROR'
221
+
222
+ new ErrorX({ message: 'Failed', name: 'API Timeout' })
223
+ // code: 'API_TIMEOUT'
224
+ ```
225
+
226
+ ### Message Formatting
227
+
228
+ Messages are automatically formatted with proper capitalization and punctuation:
229
+
230
+ ```typescript
231
+ new ErrorX({ message: 'database connection failed' })
232
+ // message: 'Database connection failed.'
233
+
234
+ new ErrorX({ message: 'user not found. please check credentials' })
235
+ // message: 'User not found. Please check credentials.'
236
+ ```
237
+
238
+ ## Usage Examples
239
+
240
+ ### Basic Error Handling
241
+
242
+ ```typescript
243
+ import { ErrorX } from 'error-x'
244
+
245
+ function validateUser(user: unknown) {
246
+ if (!user) {
247
+ throw new ErrorX({
248
+ message: 'User validation failed: user is required',
249
+ name: 'ValidationError',
250
+ code: 'USER_REQUIRED',
251
+ uiMessage: 'Please provide user information',
252
+ metadata: { field: 'user', received: user }
253
+ })
254
+ }
255
+ }
256
+ ```
257
+
258
+ ### API Error Handling
259
+
260
+ ```typescript
261
+ async function fetchUser(id: string) {
262
+ try {
263
+ const response = await fetch(`/api/users/${id}`)
264
+ if (!response.ok) {
265
+ throw new ErrorX({
266
+ message: `Failed to fetch user: ${response.statusText}`,
267
+ code: `HTTP_${response.status}`,
268
+ uiMessage: 'Unable to load user data',
269
+ metadata: { status: response.status, statusText: response.statusText }
270
+ })
271
+ }
272
+ return response.json()
273
+ } catch (error) {
274
+ // Convert any error to ErrorX and add context
275
+ const errorX = ErrorX.toErrorX(error)
276
+ throw errorX.withMetadata({
277
+ userId: id,
278
+ operation: 'fetchUser',
279
+ })
280
+ }
281
+ }
282
+ ```
283
+
284
+ ### Error Chaining and Stack Trace Preservation
285
+
286
+ ```typescript
287
+ try {
288
+ await database.transaction(async (tx) => {
289
+ await tx.users.create(userData)
290
+ })
291
+ } catch (dbError) {
292
+ // Create new ErrorX while preserving the original error in the cause chain
293
+ const error = new ErrorX({
294
+ message: 'User creation failed',
295
+ name: 'UserCreationError',
296
+ code: 'USER_CREATE_FAILED',
297
+ uiMessage: 'Unable to create user account',
298
+ cause: dbError, // Preserves original stack trace and error details
299
+ metadata: {
300
+ operation: 'userRegistration',
301
+ userData: { email: userData.email } // Don't log sensitive data
302
+ }
303
+ })
304
+
305
+ // Add more context while preserving the error chain
306
+ throw error.withMetadata({
307
+ requestId: generateRequestId(),
308
+ userAgent: request.headers['user-agent']
309
+ })
310
+ }
311
+ ```
312
+
313
+ ## FAQ
314
+
315
+ ### Why use action type "custom" instead of an open string type for CustomAction?
316
+
317
+ The `ErrorAction` type uses a discriminated union based on the `action` property. When you use arbitrary values instead of the predefined action types (`'notify'`, `'logout'`, `'redirect'`, `'custom'`), it breaks TypeScript's ability to properly narrow the payload types.
318
+
319
+ **The Problem:** If `ErrorAction` allowed any string as the action type, TypeScript would default to the most permissive payload type (`{ ...any }`) for all actions, causing type definition to leak between different action types.
320
+
321
+ ```typescript
322
+ // ❌ Cannot be done - breaks discriminated union
323
+ const error = new ErrorX({
324
+ actions: [
325
+ { action: 'analytics', payload: { event: 'error' } }, // Loses type safety
326
+ { action: 'notify', payload: { targets: ['toast'] } }, // Payload type becomes too permissive
327
+ { action: 'redirect', payload: { redirectURL: '/home' } } // Required properties not enforced
328
+ ]
329
+ })
330
+
331
+ // ✅ Do this - maintains proper type discrimination
332
+ const error = new ErrorX({
333
+ actions: [
334
+ { action: 'custom', payload: { type: 'analytics', event: 'error' } },
335
+ { action: 'notify', payload: { targets: ['toast'] } }, // Properly typed with required 'targets'
336
+ { action: 'redirect', payload: { redirectURL: '/home' } } // Properly typed with required 'redirectURL'
337
+ ]
338
+ })
339
+ ```
340
+
341
+ **The Solution:** Using `action: 'custom'` with a discriminating `type` property in the payload preserves the discriminated union while allowing unlimited flexibility for custom actions. This approach:
342
+
343
+ - Maintains type safety for predefined actions (`notify`, `logout`, `redirect`)
344
+ - Provides a structured way to handle custom application logic
345
+ - Allows your error handlers to properly switch on action types
346
+ - Enables you to create your own discriminated unions within custom payloads
347
+
348
+ Ideally, we would support custom action types directly in the action. If there is a solution to this problem, we are more than happy to review it. Please open an issue or PR!.
349
+
350
+ ## License
351
+
352
+ MIT