├── .gitignore ├── package.json ├── readme.md ├── index.js └── test └── tests.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-errors", 3 | "version": "1.0.0", 4 | "description": "Errors constructor for simple error handling.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "istanbul cover _mocha -- -R spec" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/machadogj/node-simple-errors.git" 12 | }, 13 | "devDependencies": { 14 | "mocha": "*", 15 | "istanbul": "*" 16 | }, 17 | "keywords": [ 18 | "error" 19 | ], 20 | "author": "Gustavo Machado", 21 | "license": "BSD-2-Clause", 22 | "dependencies": { 23 | "errno": "^0.1.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | #simple-errors 2 | Factory methods for easy error handling. 3 | 4 | ##Installation 5 | 6 | ```sh 7 | npm install simple-errors 8 | ``` 9 | 10 | ##Usage 11 | Just make sure to require the module somewhere in your code (only once) 12 | 13 | ```js 14 | require('simple-errors'); 15 | ``` 16 | 17 | Then use the factory methods for creating errors: 18 | 19 | ```js 20 | //simplest of errors 21 | var err = Error.create(); 22 | 23 | //with message 24 | var err = Error.create('foo'); 25 | console.log(err.message === 'foo'); //prints true 26 | 27 | //with data 28 | var err = Error.create('foo', { foo: 'bar' }); 29 | console.log(err.message === 'foo'); //prints true 30 | console.log(err.foo === 'bar'); //prints true 31 | 32 | //with data and inner error 33 | var err = Error.create('foo', { foo: 'bar' }, err); 34 | console.log(err.message === 'foo'); //prints true 35 | console.log(err.foo === 'bar'); //prints true 36 | console.log(err.inner === err); //prints true 37 | 38 | //helper for status codes (for use with connect & express) 39 | var err = Error.http(500); 40 | console.log(err.status === 500); //prints true 41 | 42 | //helper for status codes, with message, data and error 43 | var err = Error.http(500, 'foo', {foo: 'bar'}, err); 44 | console.log(err.status === 500); //prints true 45 | console.log(err.message === 'foo'); //prints true 46 | console.log(err.foo === 'bar'); //prints true 47 | console.log(err.inner === err); //prints true 48 | 49 | //helper method for turning the Error instance into a json object 50 | var err = Error.create(); 51 | var obj = Error.toJson(err); //use this to print the entire error with stack. 52 | 53 | ``` 54 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * simple-errors module 3 | * 4 | * Create errors with simple factory methods for easy error handling. 5 | * License: BSD 6 | */ 7 | 8 | /** 9 | * Use Error.create(msg [, data [, inner]]) to create Error instances. 10 | * function create 11 | * @param msg (string) 12 | * @param data (any type) adds metadata to the Error instance 13 | * @param inner (any type) for chaining errors 14 | * @api public 15 | */ 16 | 17 | var errno = require('errno'); 18 | 19 | Error.create = function (msg, data, inner) { 20 | "use strict"; 21 | var err, 22 | innerValue; 23 | data = data || {}; 24 | 25 | err = new Error(msg || "Unknown error"); 26 | 27 | innerValue = inner || (data instanceof Error ? data : null); 28 | if (innerValue) { 29 | err.inner = innerValue; 30 | } 31 | 32 | if (data instanceof Array || typeof data !== 'object') { 33 | err.data = data; 34 | } else { 35 | Object.keys(data).forEach(function (key) { 36 | err[key] = data[key]; 37 | }); 38 | } 39 | 40 | if (!err.status) { 41 | err.status = (err.inner && err.inner.status) ? err.inner.status : 500; 42 | } 43 | 44 | err.description = innerValue ? errmsg(innerValue) : ''; 45 | 46 | return err; 47 | }; 48 | 49 | /** 50 | * Use Error.http([code] [, msg [, data [, inner]]]) to create Error instances with status codes. 51 | * @param code (numeric) adds status property to the error. 52 | * @param msg (string) 53 | * @param data (any type) adds metadata to the Error instance 54 | * @param inner (Error|string) for chaining errors 55 | */ 56 | Error.http = function (code, msg, data, inner) { 57 | "use strict"; 58 | if (typeof(code)==='string') { 59 | inner = data; 60 | data = msg; 61 | msg = code; 62 | code = 500; 63 | } 64 | 65 | code = code || 500; 66 | msg = msg || statusCodes["" + code] || 'Unknown error'; 67 | 68 | var err = Error.create(msg, data, inner); 69 | err.status = code; 70 | return err; 71 | }; 72 | 73 | /** 74 | * Turn an Error instance into a json object recursively. Use this function 75 | * for printing the entire error (even the stacks). 76 | * @param err (Error|Object) 77 | * @api public 78 | */ 79 | Error.toJson = function ( err ) { 80 | "use strict"; 81 | if (typeof(err)==='string') return { message: err}; 82 | 83 | var info = {}; 84 | if (err instanceof Error) { 85 | info.message = err.message; 86 | info.stack = err.stack && err.stack.split("\n"); 87 | } 88 | 89 | if (typeof(err) === 'object') { 90 | for (var prop in err) { 91 | var value = err[prop]; 92 | info[prop] = (value instanceof Error) ? Error.toJson(value) : value; 93 | } 94 | } 95 | 96 | return info; 97 | }; 98 | 99 | /** 100 | * Get the public error message recursively through every inner errors 101 | * A public error is every error instance with a property 'public' set to true 102 | * @param err (Error|Object) 103 | * @api public 104 | */ 105 | Error.publicMessage = function (err) { 106 | "use strict"; 107 | var msg = "", 108 | statusCode = err.status; 109 | 110 | if (statusCode && Number(statusCode) < 500) { 111 | msg += err.message; 112 | } 113 | 114 | if (err.inner) { 115 | var innerMessage = Error.publicMessage(err.inner); 116 | msg += innerMessage !== "" ? " - " + innerMessage : ""; 117 | } 118 | return msg; 119 | }; 120 | 121 | var statusCodes = { 122 | "400": "Bad Request", 123 | "401": "Unauthorized", 124 | "402": "Payment Required", 125 | "403": "Forbidden", 126 | "404": "Not Found", 127 | "405": "Method Not Allowed", 128 | "406": "Not Acceptable", 129 | "407": "Proxy Authentication Required", 130 | "408": "Request Timeout", 131 | "409": "Conflict", 132 | "410": "Gone", 133 | "411": "Length Required", 134 | "412": "Precondition Failed", 135 | "413": "Request Entity Too Large", 136 | "414": "Request-URI Too Long", 137 | "415": "Unsupported Media Type", 138 | "416": "Requested Range Not Satisfiable", 139 | "417": "Expectation Failed", 140 | "418": "I'm a teapot", 141 | "422": "Unprocessable Entity", 142 | "423": "Locked", 143 | "424": "Failed Dependency", 144 | "425": "Unordered Collection", 145 | "426": "Upgrade Required", 146 | "428": "Precondition Required", 147 | "429": "Too Many Requests", 148 | "431": "Request Header Fields Too Large", 149 | "500": "Internal Server Error", 150 | "501": "Not Implemented", 151 | "502": "Bad Gateway", 152 | "503": "Service Unavailable", 153 | "504": "Gateway Timeout", 154 | "505": "HTTP Version Not Supported", 155 | "506": "Variant Also Negotiates", 156 | "507": "Insufficient Storage", 157 | "508": "Loop Detected", 158 | "510": "Not Extended", 159 | "511": "Network Authentication Required" 160 | }; 161 | 162 | 163 | 164 | function errmsg(err) { 165 | var str = 'Error: '; 166 | if (errno.errno[err.errno]) { 167 | str += errno.errno[err.errno].description; 168 | } else { 169 | str += err.message; 170 | } 171 | 172 | if (err.path) { 173 | str += ' [' + err.path + ']'; 174 | } 175 | return str; 176 | } -------------------------------------------------------------------------------- /test/tests.js: -------------------------------------------------------------------------------- 1 | /*global describe, it */ 2 | require('../index.js'); 3 | 4 | var assert = require('assert'); 5 | var fs = require('fs'); 6 | 7 | describe("simple-errors", function () { 8 | "use strict"; 9 | 10 | it("should add create factory to Error class", function () { 11 | assert.ok(Error.create); 12 | }); 13 | 14 | it("should add http factory to Error class", function () { 15 | assert.ok(Error.http); 16 | }); 17 | 18 | it("should add toJson helper method", function () { 19 | assert.ok(Error.toJson); 20 | }); 21 | 22 | describe("create", function () { 23 | 24 | it("should return an Error instance", function () { 25 | 26 | var err = Error.create('foo'); 27 | 28 | assert.ok(err instanceof Error); 29 | assert.equal('foo', err.message); 30 | }); 31 | 32 | it("should use 'Unknown error' by default", function () { 33 | 34 | var err = Error.create(); 35 | 36 | assert.equal('Unknown error', err.message); 37 | }); 38 | 39 | it("should accept a string as data", function () { 40 | 41 | var err = Error.create('foo', 'bar'); 42 | 43 | assert.equal('bar', err.data); 44 | }); 45 | 46 | it("should accept another Error as inner", function () { 47 | 48 | var err = Error.create('foo', new Error('bar')); 49 | 50 | assert.equal(undefined, err.data); 51 | assert.ok(err.inner instanceof Error); 52 | }); 53 | 54 | it("should add properties defined in data", function () { 55 | 56 | var err = Error.create('foo', {status: 200}); 57 | 58 | assert.equal(200, err.status); 59 | }); 60 | 61 | it("should accept an inner error in string format", function () { 62 | 63 | var err = Error.create('foo', {}, 'boo'); 64 | 65 | assert.equal('boo', err.inner); 66 | }); 67 | 68 | it('should have a valid description message', function () { 69 | fs.readFile('thisisnotarealfile.txt', function (err, data) { 70 | assert.ok(err); 71 | var se = Error.create('foo', err); 72 | assert.equal('Error: no such file or directory [thisisnotarealfile.txt]', 73 | se.description); 74 | }); 75 | }); 76 | }); 77 | 78 | describe("http", function () { 79 | 80 | it("should use 500 as default", function () { 81 | 82 | var err = Error.http(); 83 | 84 | assert.equal('Internal Server Error', err.message); 85 | assert.equal(500, err.status); 86 | }); 87 | 88 | it("should use 500 as default", function () { 89 | 90 | var err = Error.http('foo'); 91 | 92 | assert.equal('foo', err.message); 93 | assert.equal(500, err.status); 94 | }); 95 | 96 | it("should add a default description", function () { 97 | 98 | var err = Error.http(400); 99 | 100 | assert.equal('Bad Request', err.message); 101 | assert.equal(400, err.status); 102 | }); 103 | 104 | it("should add string data to error", function () { 105 | 106 | var err = Error.http(400, "foo", "bar"); 107 | 108 | assert.equal('foo', err.message); 109 | assert.equal('bar', err.data); 110 | assert.equal(400, err.status); 111 | }); 112 | 113 | it("should add object data to error", function () { 114 | 115 | var err = Error.http(400, "foo", {x: "bar"}); 116 | 117 | assert.equal('foo', err.message); 118 | assert.equal('bar', err.x); 119 | assert.equal(400, err.status); 120 | }); 121 | 122 | it("should add inner error", function () { 123 | 124 | var err = Error.http(400, "foo", {}, "something wrong"); 125 | 126 | assert.equal('foo', err.message); 127 | assert.equal('something wrong', err.inner); 128 | assert.equal(400, err.status); 129 | }); 130 | }); 131 | 132 | describe("toJson", function () { 133 | 134 | it("should return an object with all the properties", function () { 135 | var err = Error.create('foo', { foo: 'bar' }, 'boo'), 136 | obj = Error.toJson(err); 137 | 138 | assert.ok(!(obj instanceof Error)); 139 | assert.equal('foo', obj.message); 140 | assert.equal('bar', obj.foo); 141 | assert.equal('boo', obj.inner); 142 | }); 143 | 144 | it("should have stack in the keys collection", function () { 145 | var err = Error.create('foo', { foo: 'bar' }, 'boo'), 146 | obj = Error.toJson(err); 147 | 148 | assert.ok(Object.keys(obj).indexOf('stack') >= 0); 149 | }); 150 | 151 | it("should recursively turn errors into literals", function () { 152 | var err = Error.create('foo', {}, Error.create('bar')), 153 | obj = Error.toJson(err); 154 | 155 | assert.equal('bar', obj.inner.message); 156 | }); 157 | 158 | it("should support a 'string' in inner", function () { 159 | var err = Error.create('foo', {}, "something bad happened"); 160 | 161 | assert.equal("something bad happened", Error.toJson(err).inner); 162 | }); 163 | 164 | it("should return non Error objects", function () { 165 | var err = Error.toJson({foo: 'bar'}); 166 | 167 | assert.deepEqual({foo: 'bar'}, err); 168 | }); 169 | }); 170 | 171 | describe("Inner errors", function () { 172 | it("should set inner status code to new error", function () { 173 | var innerError = Error.http(401, "You don't have access to the resource"), 174 | error = Error.create("The username doesn't have access.", innerError); 175 | 176 | assert.strictEqual(innerError.status, 401); 177 | assert.strictEqual(error.status, 401); 178 | }); 179 | 180 | it("should show public message only", function () { 181 | var publicMessage = "My public Error message", 182 | innerError = new Error("Test Inner error"), 183 | error = Error.http(403, publicMessage, innerError); 184 | 185 | assert.ok(error); 186 | assert.equal(publicMessage, Error.publicMessage(error)); 187 | }); 188 | 189 | it("should show public message only without inner", function () { 190 | var publicMessage = "My public Error message", 191 | error = Error.http(302, publicMessage); 192 | 193 | assert.ok(error); 194 | assert.equal(publicMessage, Error.publicMessage(error)); 195 | }); 196 | 197 | it("should not show any message", function () { 198 | var publicMessage = "My public Error message", 199 | error = Error.create(publicMessage, {public: false}); 200 | 201 | assert.ok(error); 202 | assert.equal("", Error.publicMessage(error)); 203 | }); 204 | 205 | it("should show every public error message", function () { 206 | var publicMessage1 = "The table name is incorrect", 207 | publicMessage2 = "The User can't be created. Please try again with a different name", 208 | internalInnerError = new Error("Error in DB query. Table 'test' does not exists"), 209 | dbError = new Error(internalInnerError), 210 | publicError1 = Error.http(400, publicMessage1, dbError), 211 | publicError2 = Error.http(400, publicMessage2, null, publicError1); 212 | 213 | assert.ok(publicError2); 214 | assert.equal(publicMessage2 + " - " + publicMessage1, Error.publicMessage(publicError2)); 215 | }); 216 | 217 | it("should not show a private message", function () { 218 | var publicMessage1 = "The table 'users' is invalid", 219 | publicMessage2 = "The User can't be created. Please try again with a different name", 220 | internalInnerError = new Error("Error in DB query. Table 'test' does not exists"), 221 | dbError = new Error(internalInnerError), 222 | publicError1 = Error.create(publicMessage1, dbError), 223 | publicError2 = Error.http(400, publicMessage2, publicError1); 224 | 225 | assert.ok(publicError2); 226 | assert.equal(publicMessage2, Error.publicMessage(publicError2)); 227 | }); 228 | }); 229 | }); --------------------------------------------------------------------------------