├── .gitignore
├── .jshintrc
├── .travis.yml
├── README.md
├── build.js
├── cli.js
├── custom.js
├── errno.js
├── package.json
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [ ]
3 | , "bitwise": false
4 | , "camelcase": false
5 | , "curly": false
6 | , "eqeqeq": false
7 | , "forin": false
8 | , "immed": false
9 | , "latedef": false
10 | , "noarg": true
11 | , "noempty": true
12 | , "nonew": true
13 | , "plusplus": false
14 | , "quotmark": true
15 | , "regexp": false
16 | , "undef": true
17 | , "unused": true
18 | , "strict": false
19 | , "trailing": true
20 | , "maxlen": 120
21 | , "asi": true
22 | , "boss": true
23 | , "debug": true
24 | , "eqnull": true
25 | , "esnext": true
26 | , "evil": true
27 | , "expr": true
28 | , "funcscope": false
29 | , "globalstrict": false
30 | , "iterator": false
31 | , "lastsemic": true
32 | , "laxbreak": true
33 | , "laxcomma": true
34 | , "loopfunc": true
35 | , "multistr": false
36 | , "onecase": false
37 | , "proto": false
38 | , "regexdash": false
39 | , "scripturl": true
40 | , "smarttabs": false
41 | , "shadow": false
42 | , "sub": true
43 | , "supernew": false
44 | , "validthis": true
45 | , "browser": true
46 | , "couch": false
47 | , "devel": false
48 | , "dojo": false
49 | , "mootools": false
50 | , "node": true
51 | , "nonstandard": true
52 | , "prototypejs": false
53 | , "rhino": false
54 | , "worker": true
55 | , "wsh": false
56 | , "nomen": false
57 | , "onevar": false
58 | , "passfail": false
59 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | language: node_js
4 |
5 | node_js:
6 | - 14
7 | - 12
8 | - 10
9 |
10 | arch:
11 | - amd64
12 | - ppc64le
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # node-errno
2 |
3 | > Better [libuv](https://github.com/libuv/libuv)/[Node.js](https://nodejs.org)/[io.js](https://iojs.org) error handling & reporting. Available in npm as *errno*.
4 |
5 | [](https://www.npmjs.com/package/errno)
6 | [](http://travis-ci.org/rvagg/node-errno)
7 | [](https://www.npmjs.com/package/errno)
8 |
9 | * [errno exposed](#errnoexposed)
10 | * [Custom errors](#customerrors)
11 |
12 |
13 | ## errno exposed
14 |
15 | Ever find yourself needing more details about Node.js errors? Me too, so *node-errno* contains the errno mappings direct from libuv so you can use them in your code.
16 |
17 | **By errno:**
18 |
19 | ```js
20 | require('errno').errno[3]
21 | // → {
22 | // "errno": 3,
23 | // "code": "EACCES",
24 | // "description": "permission denied"
25 | // }
26 | ```
27 |
28 | **By code:**
29 |
30 | ```js
31 | require('errno').code.ENOTEMPTY
32 | // → {
33 | // "errno": 53,
34 | // "code": "ENOTEMPTY",
35 | // "description": "directory not empty"
36 | // }
37 | ```
38 |
39 | **Make your errors more descriptive:**
40 |
41 | ```js
42 | var errno = require('errno')
43 |
44 | function errmsg(err) {
45 | var str = 'Error: '
46 | // if it's a libuv error then get the description from errno
47 | if (errno.errno[err.errno])
48 | str += errno.errno[err.errno].description
49 | else
50 | str += err.message
51 |
52 | // if it's a `fs` error then it'll have a 'path' property
53 | if (err.path)
54 | str += ' [' + err.path + ']'
55 |
56 | return str
57 | }
58 |
59 | var fs = require('fs')
60 |
61 | fs.readFile('thisisnotarealfile.txt', function (err, data) {
62 | if (err)
63 | console.log(errmsg(err))
64 | })
65 | ```
66 |
67 | **Use as a command line tool:**
68 |
69 | ```
70 | ~ $ errno 53
71 | {
72 | "errno": 53,
73 | "code": "ENOTEMPTY",
74 | "description": "directory not empty"
75 | }
76 | ~ $ errno EROFS
77 | {
78 | "errno": 56,
79 | "code": "EROFS",
80 | "description": "read-only file system"
81 | }
82 | ~ $ errno foo
83 | No such errno/code: "foo"
84 | ```
85 |
86 | Supply no arguments for the full list. Error codes are processed case-insensitive.
87 |
88 | You will need to install with `npm install errno -g` if you want the `errno` command to be available without supplying a full path to the node_modules installation.
89 |
90 |
91 | ## Custom errors
92 |
93 | Use `errno.custom.createError()` to create custom `Error` objects to throw around in your Node.js library. Create error hierarchies so `instanceof` becomes a useful tool in tracking errors. Call-stack is correctly captured at the time you create an instance of the error object, plus a `cause` property will make available the original error object if you pass one in to the constructor.
94 |
95 | ```js
96 | var create = require('errno').custom.createError
97 | var MyError = create('MyError') // inherits from Error
98 | var SpecificError = create('SpecificError', MyError) // inherits from MyError
99 | var OtherError = create('OtherError', MyError)
100 |
101 | // use them!
102 | if (condition) throw new SpecificError('Eeek! Something bad happened')
103 |
104 | if (err) return callback(new OtherError(err))
105 | ```
106 |
107 | Also available is a `errno.custom.FilesystemError` with in-built access to errno properties:
108 |
109 | ```js
110 | fs.readFile('foo', function (err, data) {
111 | if (err) return callback(new errno.custom.FilesystemError(err))
112 | // do something else
113 | })
114 | ```
115 |
116 | The resulting error object passed through the callback will have the following properties: `code`, `errno`, `path` and `message` will contain a descriptive human-readable message.
117 |
118 | ## Contributors
119 |
120 | * [bahamas10](https://github.com/bahamas10) (Dave Eddy) - Added CLI
121 | * [ralphtheninja](https://github.com/ralphtheninja) (Lars-Magnus Skog)
122 |
123 | ## Copyright & Licence
124 |
125 | *Copyright (c) 2012-2015 [Rod Vagg](https://github.com/rvagg) ([@rvagg](https://twitter.com/rvagg))*
126 |
127 | Made available under the MIT licence:
128 |
129 | Permission is hereby granted, free of charge, to any person obtaining a copy
130 | of this software and associated documentation files (the "Software"), to deal
131 | in the Software without restriction, including without limitation the rights
132 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
133 | copies of the Software, and to permit persons to whom the Software is furnished
134 | to do so, subject to the following conditions:
135 |
136 | The above copyright notice and this permission notice shall be included in all
137 | copies or substantial portions of the Software.
138 |
139 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
140 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
141 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
142 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
143 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
144 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
145 | SOFTWARE.
146 |
--------------------------------------------------------------------------------
/build.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var request = require('request')
4 | , fs = require('fs')
5 |
6 | , uvheadloc = 'https://raw.github.com/joyent/libuv/master/include/uv.h'
7 | , defreg = /^\s*XX\(\s*([\-\d]+),\s*([A-Z]+),\s*"([^"]*)"\s*\)\s*\\?$/
8 |
9 |
10 | request(uvheadloc, function (err, response) {
11 | if (err)
12 | throw err
13 |
14 | var data, out
15 |
16 | data = response.body
17 | .split('\n')
18 | .map(function (line) { return line.match(defreg) })
19 | .filter(function (match) { return match })
20 | .map(function (match) { return {
21 | errno: parseInt(match[1], 10)
22 | , code: match[2]
23 | , description: match[3]
24 | }})
25 |
26 | out = 'var all = module.exports.all = ' + JSON.stringify(data, 0, 1) + '\n\n'
27 |
28 | out += '\nmodule.exports.errno = {\n '
29 | + data.map(function (e, i) {
30 | return '\'' + e.errno + '\': all[' + i + ']'
31 | }).join('\n , ')
32 | + '\n}\n\n'
33 |
34 | out += '\nmodule.exports.code = {\n '
35 | + data.map(function (e, i) {
36 | return '\'' + e.code + '\': all[' + i + ']'
37 | }).join('\n , ')
38 | + '\n}\n\n'
39 |
40 | out += '\nmodule.exports.custom = require("./custom")(module.exports)\n'
41 |
42 | fs.writeFile('errno.js', out)
43 | })
--------------------------------------------------------------------------------
/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var errno = require('./')
4 | , arg = process.argv[2]
5 | , data, code
6 |
7 | if (arg === undefined) {
8 | console.log(JSON.stringify(errno.code, null, 2))
9 | process.exit(0)
10 | }
11 |
12 | if ((code = +arg) == arg)
13 | data = errno.errno[code]
14 | else
15 | data = errno.code[arg] || errno.code[arg.toUpperCase()]
16 |
17 | if (data)
18 | console.log(JSON.stringify(data, null, 2))
19 | else {
20 | console.error('No such errno/code: "' + arg + '"')
21 | process.exit(1)
22 | }
23 |
--------------------------------------------------------------------------------
/custom.js:
--------------------------------------------------------------------------------
1 | var prr = require('prr')
2 |
3 | function init (type, message, cause) {
4 | if (!!message && typeof message != 'string') {
5 | message = message.message || message.name
6 | }
7 | prr(this, {
8 | type : type
9 | , name : type
10 | // can be passed just a 'cause'
11 | , cause : typeof message != 'string' ? message : cause
12 | , message : message
13 | }, 'ewr')
14 | }
15 |
16 | // generic prototype, not intended to be actually used - helpful for `instanceof`
17 | function CustomError (message, cause) {
18 | Error.call(this)
19 | if (Error.captureStackTrace)
20 | Error.captureStackTrace(this, this.constructor)
21 | init.call(this, 'CustomError', message, cause)
22 | }
23 |
24 | CustomError.prototype = new Error()
25 |
26 | function createError (errno, type, proto) {
27 | var err = function (message, cause) {
28 | init.call(this, type, message, cause)
29 | //TODO: the specificity here is stupid, errno should be available everywhere
30 | if (type == 'FilesystemError') {
31 | this.code = this.cause.code
32 | this.path = this.cause.path
33 | this.errno = this.cause.errno
34 | this.message =
35 | (errno.errno[this.cause.errno]
36 | ? errno.errno[this.cause.errno].description
37 | : this.cause.message)
38 | + (this.cause.path ? ' [' + this.cause.path + ']' : '')
39 | }
40 | Error.call(this)
41 | if (Error.captureStackTrace)
42 | Error.captureStackTrace(this, err)
43 | }
44 | err.prototype = !!proto ? new proto() : new CustomError()
45 | return err
46 | }
47 |
48 | module.exports = function (errno) {
49 | var ce = function (type, proto) {
50 | return createError(errno, type, proto)
51 | }
52 | return {
53 | CustomError : CustomError
54 | , FilesystemError : ce('FilesystemError')
55 | , createError : ce
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/errno.js:
--------------------------------------------------------------------------------
1 | var all = module.exports.all = [
2 | {
3 | errno: -2,
4 | code: 'ENOENT',
5 | description: 'no such file or directory'
6 | },
7 | {
8 | errno: -1,
9 | code: 'UNKNOWN',
10 | description: 'unknown error'
11 | },
12 | {
13 | errno: 0,
14 | code: 'OK',
15 | description: 'success'
16 | },
17 | {
18 | errno: 1,
19 | code: 'EOF',
20 | description: 'end of file'
21 | },
22 | {
23 | errno: 2,
24 | code: 'EADDRINFO',
25 | description: 'getaddrinfo error'
26 | },
27 | {
28 | errno: 3,
29 | code: 'EACCES',
30 | description: 'permission denied'
31 | },
32 | {
33 | errno: 4,
34 | code: 'EAGAIN',
35 | description: 'resource temporarily unavailable'
36 | },
37 | {
38 | errno: 5,
39 | code: 'EADDRINUSE',
40 | description: 'address already in use'
41 | },
42 | {
43 | errno: 6,
44 | code: 'EADDRNOTAVAIL',
45 | description: 'address not available'
46 | },
47 | {
48 | errno: 7,
49 | code: 'EAFNOSUPPORT',
50 | description: 'address family not supported'
51 | },
52 | {
53 | errno: 8,
54 | code: 'EALREADY',
55 | description: 'connection already in progress'
56 | },
57 | {
58 | errno: 9,
59 | code: 'EBADF',
60 | description: 'bad file descriptor'
61 | },
62 | {
63 | errno: 10,
64 | code: 'EBUSY',
65 | description: 'resource busy or locked'
66 | },
67 | {
68 | errno: 11,
69 | code: 'ECONNABORTED',
70 | description: 'software caused connection abort'
71 | },
72 | {
73 | errno: 12,
74 | code: 'ECONNREFUSED',
75 | description: 'connection refused'
76 | },
77 | {
78 | errno: 13,
79 | code: 'ECONNRESET',
80 | description: 'connection reset by peer'
81 | },
82 | {
83 | errno: 14,
84 | code: 'EDESTADDRREQ',
85 | description: 'destination address required'
86 | },
87 | {
88 | errno: 15,
89 | code: 'EFAULT',
90 | description: 'bad address in system call argument'
91 | },
92 | {
93 | errno: 16,
94 | code: 'EHOSTUNREACH',
95 | description: 'host is unreachable'
96 | },
97 | {
98 | errno: 17,
99 | code: 'EINTR',
100 | description: 'interrupted system call'
101 | },
102 | {
103 | errno: 18,
104 | code: 'EINVAL',
105 | description: 'invalid argument'
106 | },
107 | {
108 | errno: 19,
109 | code: 'EISCONN',
110 | description: 'socket is already connected'
111 | },
112 | {
113 | errno: 20,
114 | code: 'EMFILE',
115 | description: 'too many open files'
116 | },
117 | {
118 | errno: 21,
119 | code: 'EMSGSIZE',
120 | description: 'message too long'
121 | },
122 | {
123 | errno: 22,
124 | code: 'ENETDOWN',
125 | description: 'network is down'
126 | },
127 | {
128 | errno: 23,
129 | code: 'ENETUNREACH',
130 | description: 'network is unreachable'
131 | },
132 | {
133 | errno: 24,
134 | code: 'ENFILE',
135 | description: 'file table overflow'
136 | },
137 | {
138 | errno: 25,
139 | code: 'ENOBUFS',
140 | description: 'no buffer space available'
141 | },
142 | {
143 | errno: 26,
144 | code: 'ENOMEM',
145 | description: 'not enough memory'
146 | },
147 | {
148 | errno: 27,
149 | code: 'ENOTDIR',
150 | description: 'not a directory'
151 | },
152 | {
153 | errno: 28,
154 | code: 'EISDIR',
155 | description: 'illegal operation on a directory'
156 | },
157 | {
158 | errno: 29,
159 | code: 'ENONET',
160 | description: 'machine is not on the network'
161 | },
162 | {
163 | errno: 31,
164 | code: 'ENOTCONN',
165 | description: 'socket is not connected'
166 | },
167 | {
168 | errno: 32,
169 | code: 'ENOTSOCK',
170 | description: 'socket operation on non-socket'
171 | },
172 | {
173 | errno: 33,
174 | code: 'ENOTSUP',
175 | description: 'operation not supported on socket'
176 | },
177 | {
178 | errno: 34,
179 | code: 'ENOENT',
180 | description: 'no such file or directory'
181 | },
182 | {
183 | errno: 35,
184 | code: 'ENOSYS',
185 | description: 'function not implemented'
186 | },
187 | {
188 | errno: 36,
189 | code: 'EPIPE',
190 | description: 'broken pipe'
191 | },
192 | {
193 | errno: 37,
194 | code: 'EPROTO',
195 | description: 'protocol error'
196 | },
197 | {
198 | errno: 38,
199 | code: 'EPROTONOSUPPORT',
200 | description: 'protocol not supported'
201 | },
202 | {
203 | errno: 39,
204 | code: 'EPROTOTYPE',
205 | description: 'protocol wrong type for socket'
206 | },
207 | {
208 | errno: 40,
209 | code: 'ETIMEDOUT',
210 | description: 'connection timed out'
211 | },
212 | {
213 | errno: 41,
214 | code: 'ECHARSET',
215 | description: 'invalid Unicode character'
216 | },
217 | {
218 | errno: 42,
219 | code: 'EAIFAMNOSUPPORT',
220 | description: 'address family for hostname not supported'
221 | },
222 | {
223 | errno: 44,
224 | code: 'EAISERVICE',
225 | description: 'servname not supported for ai_socktype'
226 | },
227 | {
228 | errno: 45,
229 | code: 'EAISOCKTYPE',
230 | description: 'ai_socktype not supported'
231 | },
232 | {
233 | errno: 46,
234 | code: 'ESHUTDOWN',
235 | description: 'cannot send after transport endpoint shutdown'
236 | },
237 | {
238 | errno: 47,
239 | code: 'EEXIST',
240 | description: 'file already exists'
241 | },
242 | {
243 | errno: 48,
244 | code: 'ESRCH',
245 | description: 'no such process'
246 | },
247 | {
248 | errno: 49,
249 | code: 'ENAMETOOLONG',
250 | description: 'name too long'
251 | },
252 | {
253 | errno: 50,
254 | code: 'EPERM',
255 | description: 'operation not permitted'
256 | },
257 | {
258 | errno: 51,
259 | code: 'ELOOP',
260 | description: 'too many symbolic links encountered'
261 | },
262 | {
263 | errno: 52,
264 | code: 'EXDEV',
265 | description: 'cross-device link not permitted'
266 | },
267 | {
268 | errno: 53,
269 | code: 'ENOTEMPTY',
270 | description: 'directory not empty'
271 | },
272 | {
273 | errno: 54,
274 | code: 'ENOSPC',
275 | description: 'no space left on device'
276 | },
277 | {
278 | errno: 55,
279 | code: 'EIO',
280 | description: 'i/o error'
281 | },
282 | {
283 | errno: 56,
284 | code: 'EROFS',
285 | description: 'read-only file system'
286 | },
287 | {
288 | errno: 57,
289 | code: 'ENODEV',
290 | description: 'no such device'
291 | },
292 | {
293 | errno: 58,
294 | code: 'ESPIPE',
295 | description: 'invalid seek'
296 | },
297 | {
298 | errno: 59,
299 | code: 'ECANCELED',
300 | description: 'operation canceled'
301 | }
302 | ]
303 |
304 | module.exports.errno = {}
305 | module.exports.code = {}
306 |
307 | all.forEach(function (error) {
308 | module.exports.errno[error.errno] = error
309 | module.exports.code[error.code] = error
310 | })
311 |
312 | module.exports.custom = require('./custom')(module.exports)
313 | module.exports.create = module.exports.custom.createError
314 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "errno",
3 | "authors": [
4 | "Rod Vagg @rvagg (https://github.com/rvagg)"
5 | ],
6 | "description": "libuv errno details exposed",
7 | "keywords": [
8 | "errors",
9 | "errno",
10 | "libuv"
11 | ],
12 | "version": "1.0.0",
13 | "main": "errno.js",
14 | "dependencies": {
15 | "prr": "~1.0.1"
16 | },
17 | "bin": {
18 | "errno": "./cli.js"
19 | },
20 | "devDependencies": {
21 | "error-stack-parser": "^2.0.1",
22 | "inherits": "^2.0.3",
23 | "tape": "~4.8.0"
24 | },
25 | "repository": {
26 | "type": "git",
27 | "url": "https://github.com/rvagg/node-errno.git"
28 | },
29 | "license": "MIT",
30 | "scripts": {
31 | "test": "node --use_strict test.js"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | var test = require('tape')
2 | , inherits = require('inherits')
3 | , ErrorStackParser = require('error-stack-parser')
4 | , errno = require('./')
5 |
6 | test('sanity checks', function (t) {
7 | t.ok(errno.all, 'errno.all not found')
8 | t.ok(errno.errno, 'errno.errno not found')
9 | t.ok(errno.code, 'errno.code not found')
10 |
11 | t.equal(errno.all.length, 60, 'found ' + errno.all.length + ', expected 60')
12 | t.equal(errno.errno['-1'], errno.all[1], 'errno -1 not second element')
13 |
14 | t.equal(errno.code['UNKNOWN'], errno.all[1], 'code UNKNOWN not second element')
15 |
16 | t.equal(errno.errno[1], errno.all[3], 'errno 1 not fourth element')
17 |
18 | t.equal(errno.code['EOF'], errno.all[3], 'code EOF not fourth element')
19 | t.end()
20 | })
21 |
22 | test('custom errors', function (t) {
23 | const Cust = errno.create('FooNotBarError')
24 | const cust = new Cust('foo is not bar')
25 |
26 | t.equal(cust.name, 'FooNotBarError', 'correct custom name')
27 | t.equal(cust.type, 'FooNotBarError', 'correct custom type')
28 | t.equal(cust.message, 'foo is not bar', 'correct custom message')
29 | t.notOk(cust.cause, 'no cause')
30 | t.end()
31 | })
32 |
33 | test('callstack', function (t) {
34 | const MyError = errno.create('MyError')
35 |
36 | function lastFunction (ErrorType, cb) {
37 | process.nextTick(cb, new ErrorType('oh noes!'))
38 | }
39 |
40 | function secondLastFunction (ErrorType, cb) {
41 | lastFunction(ErrorType, cb)
42 | }
43 |
44 | function testFrames (t) {
45 | return function (err) {
46 | const stack = ErrorStackParser.parse(err)
47 | t.same(stack[0].functionName, 'lastFunction', 'last stack frame ok')
48 | t.same(stack[1].functionName, 'secondLastFunction', 'second last stack frame ok')
49 | t.end()
50 | }
51 | }
52 |
53 | t.test('custom error, default prototype', function (t) {
54 | secondLastFunction(MyError, testFrames(t))
55 | })
56 |
57 | t.test('custom error, custom prototype', function (t) {
58 | const MyError2 = errno.create('MyError2', MyError)
59 | secondLastFunction(MyError2, testFrames(t))
60 | })
61 |
62 | t.test('custom error, using inheritance', function (t) {
63 | const CustomError = errno.custom.CustomError
64 |
65 | function MyError3 (message, cause) {
66 | CustomError.call(this, message, cause)
67 | }
68 |
69 | inherits(MyError3, CustomError)
70 |
71 | secondLastFunction(MyError3, testFrames(t))
72 | })
73 | })
74 |
75 | test('error without message', function (t) {
76 | const Cust = errno.create('WriteError')
77 | const cust = new Cust({
78 | code: 22,
79 | message: '',
80 | name: 'QuotaExceededError'
81 | })
82 |
83 | t.equal(cust.name, 'WriteError', 'correct custom name')
84 | t.equal(cust.type, 'WriteError', 'correct custom type')
85 | t.equal(cust.message, 'QuotaExceededError', 'message is the name')
86 | t.notOk(cust.cause, 'no cause')
87 | t.end()
88 | })
89 |
--------------------------------------------------------------------------------