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