Skip to content

Commit 3b27b76

Browse files
UzlopakFdawgs
andauthored
feat: implement instanceof (#149)
* feat: implement instanceof * improve test * add test * why not? * rework test * log * normalize Path for macos and win * try to fix windows run * simplify * log more * use lastIndexOf instead of indexOf to avoid issues in GH Runner with Windows * hope this fixes it * log more * log more * another try to fix the crossplatform issues * another try * ignore the first character of testDirectoryPrefix in hope it fixes the issue * another try * modify Readme * Apply suggestions from code review Co-authored-by: Frazer Smith <frazer.dev@icloud.com> Signed-off-by: Aras Abbasi <aras.abbasi@googlemail.com> --------- Signed-off-by: Aras Abbasi <aras.abbasi@googlemail.com> Co-authored-by: Frazer Smith <frazer.dev@icloud.com>
1 parent d669b15 commit 3b27b76

File tree

6 files changed

+411
-2
lines changed

6 files changed

+411
-2
lines changed

README.md

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ npm i @fastify/error
1515

1616
The module exports a function that you can use for consistent error objects, it takes 4 parameters:
1717

18-
```
18+
```js
1919
createError(code, message [, statusCode [, Base [, captureStackTrace]]])
2020
```
2121

@@ -58,6 +58,83 @@ new CustomError('world')
5858
new CustomError(1)
5959
```
6060

61+
### instanceof
62+
63+
All errors created with `createError` will be instances of the base error constructor you provided, or `Error` if none was provided.
64+
65+
```js
66+
const createError = require('@fastify/error')
67+
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
68+
const customError = new CustomError('world')
69+
70+
console.log(customError instanceof CustomError) // true
71+
console.log(customError instanceof TypeError) // true
72+
console.log(customError instanceof Error) // true
73+
```
74+
75+
All instantiated errors are instances of the `FastifyError` class, which can be required directly from the module.
76+
77+
```js
78+
const { createError, FastifyError } = require('@fastify/error')
79+
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
80+
const customError = new CustomError('world')
81+
82+
console.log(customError instanceof FastifyError) // true
83+
```
84+
85+
A `FastifyError` created by `createError` can extend another `FastifyError` while maintaining correct `instanceof` behavior.
86+
87+
```js
88+
const { createError, FastifyError } = require('@fastify/error')
89+
90+
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
91+
const ChildCustomError = createError('CHILD_ERROR_CODE', 'Hello %s', 500, CustomError)
92+
93+
const customError = new ChildCustomError('world')
94+
95+
console.log(customError instanceof ChildCustomError) // true
96+
console.log(customError instanceof CustomError) // true
97+
console.log(customError instanceof FastifyError) // true
98+
console.log(customError instanceof TypeError) // true
99+
console.log(customError instanceof Error) // true
100+
```
101+
102+
If `fastify-error` is installed multiple times directly or as a transitive dependency, `instanceof` checks for errors created by `createError` will still work correctly across these installations, as long as their error codes (e.g., `FST_ERR_CUSTOM_ERROR`) are identical.
103+
104+
```js
105+
const { createError, FastifyError } = require('@fastify/error')
106+
107+
// CustomError from `@fastify/some-plugin` is created with `createError` and
108+
// has its own `@fastify/error` installation as dependency. CustomError has
109+
// FST_ERR_CUSTOM_ERROR as code.
110+
const { CustomError: CustomErrorFromPlugin } = require('@fastify/some-plugin')
111+
112+
const CustomError = createError('FST_ERR_CUSTOM_ERROR', 'Hello %s', 500)
113+
114+
const customError = new CustomError('world')
115+
const customErrorFromPlugin = new CustomErrorFromPlugin('world')
116+
117+
console.log(customError instanceof CustomError) // true
118+
console.log(customError instanceof CustomErrorFromPlugin) // true
119+
console.log(customErrorFromPlugin instanceof CustomError) // true
120+
console.log(customErrorFromPlugin instanceof CustomErrorFromPlugin) // true
121+
```
122+
123+
Changing the code of an instantiated Error will not change the result of the `instanceof` operator.
124+
125+
```js
126+
const { createError, FastifyError } = require('@fastify/error')
127+
128+
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
129+
const AnotherCustomError = createError('ANOTHER_ERROR_CODE', 'Hello %s', 500, CustomError)
130+
131+
const customError = new CustomError('world')
132+
customError.code = 'ANOTHER_ERROR_CODE'
133+
134+
console.log(customError instanceof CustomError) // true
135+
console.log(customError instanceof AnotherCustomError) // false
136+
```
137+
61138
## License
62139

63140
Licensed under [MIT](./LICENSE).

index.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,23 @@ function toString () {
66
return `${this.name} [${this.code}]: ${this.message}`
77
}
88

9+
const FastifyGenericErrorSymbol = Symbol.for('fastify-error-generic')
10+
911
function createError (code, message, statusCode = 500, Base = Error, captureStackTrace = createError.captureStackTrace) {
12+
const shouldCreateFastifyGenericError = code === FastifyGenericErrorSymbol
13+
14+
if (shouldCreateFastifyGenericError) {
15+
code = 'FST_ERR'
16+
}
17+
1018
if (!code) throw new Error('Fastify error code must not be empty')
1119
if (!message) throw new Error('Fastify error message must not be empty')
1220

1321
code = code.toUpperCase()
1422
!statusCode && (statusCode = undefined)
1523

24+
const FastifySpecificErrorSymbol = Symbol.for(`fastify-error ${code}`)
25+
1626
function FastifyError (...args) {
1727
if (!new.target) {
1828
return new FastifyError(...args)
@@ -38,9 +48,41 @@ function createError (code, message, statusCode = 500, Base = Error, captureStac
3848
enumerable: false,
3949
writable: true,
4050
configurable: true
51+
},
52+
[FastifyGenericErrorSymbol]: {
53+
value: true,
54+
enumerable: false,
55+
writable: false,
56+
configurable: false
57+
},
58+
[FastifySpecificErrorSymbol]: {
59+
value: true,
60+
enumerable: false,
61+
writable: false,
62+
configurable: false
4163
}
4264
})
4365

66+
if (shouldCreateFastifyGenericError) {
67+
Object.defineProperty(FastifyError, Symbol.hasInstance, {
68+
value (instance) {
69+
return instance && instance[FastifyGenericErrorSymbol]
70+
},
71+
configurable: false,
72+
writable: false,
73+
enumerable: false
74+
})
75+
} else {
76+
Object.defineProperty(FastifyError, Symbol.hasInstance, {
77+
value (instance) {
78+
return instance && instance[FastifySpecificErrorSymbol]
79+
},
80+
configurable: false,
81+
writable: false,
82+
enumerable: false
83+
})
84+
}
85+
4486
FastifyError.prototype[Symbol.toStringTag] = 'Error'
4587

4688
FastifyError.prototype.toString = toString
@@ -50,6 +92,9 @@ function createError (code, message, statusCode = 500, Base = Error, captureStac
5092

5193
createError.captureStackTrace = true
5294

95+
const FastifyErrorConstructor = createError(FastifyGenericErrorSymbol, 'Fastify Error', 500, Error)
96+
5397
module.exports = createError
98+
module.exports.FastifyError = FastifyErrorConstructor
5499
module.exports.default = createError
55100
module.exports.createError = createError

test/index.test.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict'
22

33
const test = require('node:test')
4-
const createError = require('..')
4+
const { createError, FastifyError } = require('..')
55

66
test('Create error with zero parameter', (t) => {
77
t.plan(6)
@@ -221,3 +221,12 @@ test('Create an error with last argument null', (t) => {
221221
t.assert.ok(err instanceof Error)
222222
t.assert.ifError(err.cause)
223223
})
224+
225+
test('check if FastifyError is instantiable', (t) => {
226+
t.plan(2)
227+
228+
const err = new FastifyError()
229+
230+
t.assert.ok(err instanceof FastifyError)
231+
t.assert.ok(err instanceof Error)
232+
})

0 commit comments

Comments
 (0)