@alessiofrittoli/exception 1.0.0 → 1.2.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Exception 🚦
2
2
 
3
- Version 1.0.0
3
+ Version 1.2.0
4
4
 
5
5
  ## Handle errors with ease
6
6
 
@@ -80,7 +80,7 @@ import Exception from '@alessiofrittoli/exception'
80
80
 
81
81
  try {
82
82
  throw new Exception( 'Resource not found', {
83
- code : 'ERRNOTFOUND',
83
+ code : 'ERR:NOTFOUND',
84
84
  status : 404,
85
85
  } )
86
86
  } catch ( error ) {
@@ -94,11 +94,13 @@ try {
94
94
 
95
95
  A utility method to check if an object is an instance of the `Exception` class.
96
96
 
97
+ It supports also a JSON representation of the `Exception` class (commonly returned by server responses).
98
+
97
99
  ###### Example
98
100
 
99
101
  ```ts
100
102
  try {
101
- throw new Exception( 'Something went wrong', { code: 'ERRUNKNOWN' } )
103
+ throw new Exception( 'Something went wrong', { code: 'ERR:UNKNOWN' } )
102
104
  } catch ( error ) {
103
105
  if ( Exception.isException( error ) ) {
104
106
  // we can safely access `Exception` properties
@@ -109,6 +111,18 @@ try {
109
111
  }
110
112
  ```
111
113
 
114
+ ```ts
115
+ /** Simulates JSON Exception returned by a server JSON Response. */
116
+ const error = (
117
+ JSON.parse( JSON.stringify( new Exception( 'Exception with custom name.', {
118
+ code: 0,
119
+ name: 'AbortError',
120
+ } ) ) )
121
+ )
122
+
123
+ console.log( Exception.isException( error ) ) // Outputs: true
124
+ ```
125
+
112
126
  ---
113
127
 
114
128
  #### Usage Scenarios
@@ -120,25 +134,121 @@ The `Exception` class is ideal for creating domain-specific errors with addition
120
134
  ###### Example
121
135
 
122
136
  ```ts
123
- enum ErrorCode
137
+ try {
138
+ await fetch( ... )
139
+ } catch ( error ) {
140
+ // error thrown by the server: Exception( 'Wrong value.', { code: ErrorCode.WRONG_VALUE, status: 422 } )
141
+ if ( Exception.isException( error ) ) {
142
+ console.log( error.code ) // `error.code` is type of `ErrorCode`.
143
+ }
144
+ }
145
+ ```
146
+
147
+ ---
148
+
149
+ #### `ErrorCode` enum
150
+
151
+ The `ErrorCode` enum is a utility that provides pre-defined constants for the most common error codes.\
152
+ It is designed to simplify error handling and improve feedback quality returned to the user.
153
+
154
+ <details>
155
+
156
+ <summary>Constants Overview</summary>
157
+
158
+ | Constant | Value | Description |
159
+ |---------------|------------------|---------------------------------------------------------|
160
+ | `UNKNOWN` | `ERR:UNKNOWN` | Returned when an unexpected error occured. |
161
+ | `ABORT` | `ERR:ABORT` | Returned when user abort the request. |
162
+ | `EMPTY_VALUE` | `ERR:EMPTYVALUE` | Returned when a required value is "falsy". |
163
+ | `WRONG_VALUE` | `ERR:WRONGVALUE` | Return when a required value is not the expected value. |
164
+ | `EXPIRED` | `ERR:EXPIRED` | Returned when a requested has been performed too late. |
165
+ | `TOO_EARLY` | `ERR:TOOEARLY` | Returned when a request has been performed too early. |
166
+
167
+ </details>
168
+
169
+ ---
170
+
171
+ <details>
172
+
173
+ <summary>Extending the `enum` in your project</summary>
174
+
175
+ For obvious reasons the default `ErrorCode` provided by this library might be not enough to cover all the error cases in your project.
176
+
177
+ To fill this gap, you can "extend" the `ErrorCode` enum by doing so:
178
+
179
+ ```ts
180
+ // myproject/src/error-code.ts
181
+ import ExceptionCode from '@alessiofrittoli/exception/code'
182
+
183
+ /** Your project custom `ErrorCode`. */
184
+ export enum MyProjectErrorCode
124
185
  {
125
- EMPTY_VALUE = 'ERREMPTYVALUE',
126
- WRONG_FORMAT= 'ERRWRONGFORMAT',
127
- EXPIRED = 'ERREXPIRED',
128
- TOO_EARLY = 'ERRTOOEARLY',
129
- NOT_ALLOWED = 'ERRNOTALLOWED',
186
+ INVALID_SIGN = 'ERR:INVALIDSIGN',
130
187
  }
131
188
 
189
+ const ErrorCode = { ExceptionCode, MyProjectErrorCode }
190
+ type ErrorCode = MergedEnumValue<typeof ErrorCode>
191
+
192
+ export default ErrorCode
193
+ ```
194
+
195
+ ⚠️ The Type `MergedEnumValue<T>` is globally delcared from `@alessiofrittoli/type-utils` so make sure to install it if needed.
196
+
197
+ </details>
198
+
199
+ ---
200
+
201
+ <details>
202
+
203
+ <summary>Usage</summary>
204
+
205
+ #### Using the default `ErrorCode` to throw a new `Exception`
206
+
207
+ ```ts
208
+ import Exception from '@alessiofrittoli/exception'
209
+ import ErrorCode from '@alessiofrittoli/exception/code'
210
+
211
+ throw new Exception( 'Password is a required field to log you in.', {
212
+ code : ErrorCode.EMPTY_VALUE,
213
+ status : 422,
214
+ } )
215
+ ```
216
+
217
+ #### Using custom `ErrorCode` to throw a new `Exception`
218
+
219
+ ```ts
220
+ import Exception from '@alessiofrittoli/exception'
221
+ import ErrorCode from '@/error-code' // previously created in `myproject/src/error-code.ts`
222
+
223
+ throw new Exception( 'Invalid signature.', {
224
+ code : ErrorCode.MyProjectErrorCode.INVALID_SIGN,
225
+ status : 403,
226
+ } )
227
+ ```
228
+
229
+ #### Using `ErrorCode` to handle errors
230
+
231
+ ```ts
232
+ import ErrorCode from '@/error-code' // previously created in `myproject/src/error-code.ts`
233
+
132
234
  try {
133
- await fetch( ... )
235
+ ...
134
236
  } catch ( error ) {
135
- // error thrown by the server: Exception( 'Wrong value.', { code: ErrorCode.WRONG_FORMAT, status: 422 } )
136
237
  if ( Exception.isException<string, ErrorCode>( error ) ) {
137
- console.log( error.code ) // `error.code` is type of `ErrorCode`.
238
+ switch ( error.code ) { // `error.code` is now type of `ErrorCode` (custom).
239
+ case ErrorCode.MyProjectErrorCode.INVALID_SIGN:
240
+ console.log( 'The signature is not valid.' )
241
+ break
242
+ case ErrorCode.ExceptionCode.UNKNOWN:
243
+ default:
244
+ console.log( 'Unexpected error occured.' )
245
+ }
138
246
  }
139
247
  }
140
248
  ```
141
249
 
250
+ </details>
251
+
142
252
  ---
143
253
 
144
254
  ### Development
@@ -196,6 +306,16 @@ pnpm test:jest
196
306
  pnpm test:jest:jsdom
197
307
  ```
198
308
 
309
+ Run tests with coverage.
310
+
311
+ An HTTP server is then started to serve coverage files from `./coverage` folder.
312
+
313
+ ⚠️ You may see a blank page the first time you run this command. Simply refresh the browser to see the updates.
314
+
315
+ ```bash
316
+ pnpm test:coverage
317
+ ```
318
+
199
319
  ---
200
320
 
201
321
  ### Contributing
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Most common error codes.
3
+ *
4
+ */
5
+ declare enum ErrorCode {
6
+ UNKNOWN = "ERR:UNKNOWN",
7
+ ABORT = "ERR:ABORT",
8
+ EMPTY_VALUE = "ERR:EMPTYVALUE",
9
+ WRONG_VALUE = "ERR:WRONGVALUE",
10
+ EXPIRED = "ERR:EXPIRED",
11
+ TOO_EARLY = "ERR:TOOEARLY"
12
+ }
13
+
14
+ export { ErrorCode as default };
package/dist/code.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Most common error codes.
3
+ *
4
+ */
5
+ declare enum ErrorCode {
6
+ UNKNOWN = "ERR:UNKNOWN",
7
+ ABORT = "ERR:ABORT",
8
+ EMPTY_VALUE = "ERR:EMPTYVALUE",
9
+ WRONG_VALUE = "ERR:WRONGVALUE",
10
+ EXPIRED = "ERR:EXPIRED",
11
+ TOO_EARLY = "ERR:TOOEARLY"
12
+ }
13
+
14
+ export { ErrorCode as default };
package/dist/code.js ADDED
@@ -0,0 +1 @@
1
+ 'use strict';var E=(R=>(R.UNKNOWN="ERR:UNKNOWN",R.ABORT="ERR:ABORT",R.EMPTY_VALUE="ERR:EMPTYVALUE",R.WRONG_VALUE="ERR:WRONGVALUE",R.EXPIRED="ERR:EXPIRED",R.TOO_EARLY="ERR:TOOEARLY",R))(E||{}),A=E;module.exports=A;
package/dist/code.mjs ADDED
@@ -0,0 +1 @@
1
+ var E=(R=>(R.UNKNOWN="ERR:UNKNOWN",R.ABORT="ERR:ABORT",R.EMPTY_VALUE="ERR:EMPTYVALUE",R.WRONG_VALUE="ERR:WRONGVALUE",R.EXPIRED="ERR:EXPIRED",R.TOO_EARLY="ERR:TOOEARLY",R))(E||{}),N=E;export{N as default};
package/dist/index.d.mts CHANGED
@@ -1,15 +1,18 @@
1
- interface ExceptionOptions<TCode = number> extends ErrorOptions {
1
+ import ErrorCode from './code.mjs';
2
+
3
+ interface ExceptionOptions<TCode = ErrorCode> extends ErrorOptions {
2
4
  code: TCode;
3
5
  name?: string;
4
6
  status?: number;
5
7
  }
6
- declare class Exception<TMessage = string, TCode = number> extends Error implements ExceptionOptions<TCode> {
8
+ declare class Exception<TMessage = string, TCode = ErrorCode> extends Error implements ExceptionOptions<TCode> {
7
9
  message: TMessage;
8
10
  status: number | undefined;
9
11
  name: string;
12
+ typename: 'Exception';
10
13
  code: TCode;
11
14
  constructor(message: TMessage, options: ExceptionOptions<TCode>);
12
- static isException<TMessage = string, TCode = number>(error: any): error is Exception<TMessage, TCode>;
15
+ static isException<TMessage = string, TCode = ErrorCode>(error: any): error is Exception<TMessage, TCode>;
13
16
  }
14
17
 
15
18
  export { type ExceptionOptions, Exception as default };
package/dist/index.d.ts CHANGED
@@ -1,15 +1,18 @@
1
- interface ExceptionOptions<TCode = number> extends ErrorOptions {
1
+ import ErrorCode from './code.js';
2
+
3
+ interface ExceptionOptions<TCode = ErrorCode> extends ErrorOptions {
2
4
  code: TCode;
3
5
  name?: string;
4
6
  status?: number;
5
7
  }
6
- declare class Exception<TMessage = string, TCode = number> extends Error implements ExceptionOptions<TCode> {
8
+ declare class Exception<TMessage = string, TCode = ErrorCode> extends Error implements ExceptionOptions<TCode> {
7
9
  message: TMessage;
8
10
  status: number | undefined;
9
11
  name: string;
12
+ typename: 'Exception';
10
13
  code: TCode;
11
14
  constructor(message: TMessage, options: ExceptionOptions<TCode>);
12
- static isException<TMessage = string, TCode = number>(error: any): error is Exception<TMessage, TCode>;
15
+ static isException<TMessage = string, TCode = ErrorCode>(error: any): error is Exception<TMessage, TCode>;
13
16
  }
14
17
 
15
18
  export { type ExceptionOptions, Exception as default };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var t=class n extends Error{message;status;name;code;constructor(s,e){super("",e),this.name=e.name||"Exception",this.message=s,this.code=e.code,this.status=e.status;}static isException(s){return s instanceof n}},o=t;module.exports=o;
1
+ 'use strict';var s=class o extends Error{message;status;name;typename;code;constructor(e,t){super("",t),this.typename="Exception",this.name=t.name||this.typename,this.message=e,this.code=t.code,this.status=t.status;}static isException(e){return e instanceof o||typeof e=="object"&&"typename"in e&&e.typename==="Exception"}},a=s;module.exports=a;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- var t=class n extends Error{message;status;name;code;constructor(s,e){super("",e),this.name=e.name||"Exception",this.message=s,this.code=e.code,this.status=e.status;}static isException(s){return s instanceof n}},a=t;export{a as default};
1
+ var s=class o extends Error{message;status;name;typename;code;constructor(e,t){super("",t),this.typename="Exception",this.name=t.name||this.typename,this.message=e,this.code=t.code,this.status=t.status;}static isException(e){return e instanceof o||typeof e=="object"&&"typename"in e&&e.typename==="Exception"}},r=s;export{r as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alessiofrittoli/exception",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Handle errors with ease",
5
5
  "author": {
6
6
  "name": "Alessio Frittoli",
@@ -27,14 +27,28 @@
27
27
  "files": [
28
28
  "dist"
29
29
  ],
30
+ "exports": {
31
+ ".": {
32
+ "import": "./dist/index.mjs",
33
+ "require": "./dist/index.js"
34
+ },
35
+ "./code": {
36
+ "types": "./dist/code.d.ts",
37
+ "import": "./dist/code.mjs",
38
+ "require": "./dist/code.js"
39
+ }
40
+ },
30
41
  "sideEffects": false,
31
42
  "scripts": {
32
- "build": "eslint && pnpm test:ci && tsup",
43
+ "build": "pnpm lint && pnpm test:ci && tsup",
33
44
  "lint": "eslint",
34
45
  "test": "jest --watchAll --verbose",
35
46
  "test:jsdom": "JSDOM=true pnpm test",
36
47
  "test:ci": "jest --ci --verbose",
37
48
  "test:ci:jsdom": "JSDOM=true pnpm test:ci",
49
+ "test:serve-coverage": "http-server ./coverage/lcov-report --gzip true -p 0 -o --silent",
50
+ "test:coverage": "concurrently --prefix none --kill-others \"pnpm jest --watchAll --verbose --coverage\" \"pnpm test:serve-coverage\"",
51
+ "test:coverage:jsdom": "JSDOM=true pnpm test:coverage",
38
52
  "test:jest": "pnpm test jest.test.ts",
39
53
  "test:jest:jsdom": "JSDOM=true pnpm test:jest",
40
54
  "test:exception": "pnpm test exception.test.ts"
@@ -44,15 +58,18 @@
44
58
  "@jest/globals": "^29.7.0",
45
59
  "@types/jest": "^29.5.14",
46
60
  "@types/node": "^22.10.2",
61
+ "concurrently": "^9.1.0",
47
62
  "dotenv": "^16.4.7",
63
+ "esbuild": "0.24.0",
48
64
  "eslint": "^9.17.0",
49
- "globals": "^15.13.0",
65
+ "globals": "^15.14.0",
66
+ "http-server": "^14.1.1",
50
67
  "jest": "^29.7.0",
51
68
  "jest-environment-jsdom": "^29.7.0",
52
69
  "ts-jest": "^29.2.5",
53
70
  "ts-node": "^10.9.2",
54
71
  "tsup": "^8.3.5",
55
72
  "typescript": "^5.7.2",
56
- "typescript-eslint": "^8.18.0"
73
+ "typescript-eslint": "^8.18.1"
57
74
  }
58
75
  }