3 * Copyright(c) 2014 Jonathan Ong
4 * Copyright(c) 2016 Douglas Christopher Wilson
11 * Module dependencies.
15 var deprecate = require('depd')('http-errors')
16 var setPrototypeOf = require('setprototypeof')
17 var statuses = require('statuses')
18 var inherits = require('inherits')
25 module.exports = createError
26 module.exports.HttpError = createHttpErrorConstructor()
28 // Populate exports for all constructors
29 populateConstructorExports(module.exports, statuses.codes, module.exports.HttpError)
32 * Get the code class of a status code.
36 function codeClass (status) {
37 return Number(String(status).charAt(0) + '00')
41 * Create a new HTTP Error.
47 function createError () {
48 // so much arity going on ~_~
53 for (var i = 0; i < arguments.length; i++) {
54 var arg = arguments[i]
55 if (arg instanceof Error) {
57 status = err.status || err.statusCode || status
67 deprecate('non-first-argument status code; replace with createError(' + arg + ', ...)')
76 if (typeof status === 'number' && (status < 400 || status >= 600)) {
77 deprecate('non-error status code; use only 4xx or 5xx status codes')
80 if (typeof status !== 'number' ||
81 (!statuses[status] && (status < 400 || status >= 600))) {
86 var HttpError = createError[status] || createError[codeClass(status)]
92 : new Error(msg || statuses[status])
93 Error.captureStackTrace(err, createError)
96 if (!HttpError || !(err instanceof HttpError) || err.status !== status) {
97 // add properties to generic error
98 err.expose = status < 500
99 err.status = err.statusCode = status
102 for (var key in props) {
103 if (key !== 'status' && key !== 'statusCode') {
104 err[key] = props[key]
112 * Create HTTP error abstract base class.
116 function createHttpErrorConstructor () {
117 function HttpError () {
118 throw new TypeError('cannot construct abstract class')
121 inherits(HttpError, Error)
127 * Create a constructor for a client error.
131 function createClientErrorConstructor (HttpError, name, code) {
132 var className = name.match(/Error$/) ? name : name + 'Error'
134 function ClientError (message) {
135 // create the error object
136 var msg = message != null ? message : statuses[code]
137 var err = new Error(msg)
139 // capture a stack trace to the construction point
140 Error.captureStackTrace(err, ClientError)
142 // adjust the [[Prototype]]
143 setPrototypeOf(err, ClientError.prototype)
145 // redefine the error message
146 Object.defineProperty(err, 'message', {
153 // redefine the error name
154 Object.defineProperty(err, 'name', {
164 inherits(ClientError, HttpError)
166 ClientError.prototype.status = code
167 ClientError.prototype.statusCode = code
168 ClientError.prototype.expose = true
174 * Create a constructor for a server error.
178 function createServerErrorConstructor (HttpError, name, code) {
179 var className = name.match(/Error$/) ? name : name + 'Error'
181 function ServerError (message) {
182 // create the error object
183 var msg = message != null ? message : statuses[code]
184 var err = new Error(msg)
186 // capture a stack trace to the construction point
187 Error.captureStackTrace(err, ServerError)
189 // adjust the [[Prototype]]
190 setPrototypeOf(err, ServerError.prototype)
192 // redefine the error message
193 Object.defineProperty(err, 'message', {
200 // redefine the error name
201 Object.defineProperty(err, 'name', {
211 inherits(ServerError, HttpError)
213 ServerError.prototype.status = code
214 ServerError.prototype.statusCode = code
215 ServerError.prototype.expose = false
221 * Populate the exports object with constructors for every error class.
225 function populateConstructorExports (exports, codes, HttpError) {
226 codes.forEach(function forEachCode (code) {
228 var name = toIdentifier(statuses[code])
230 switch (codeClass(code)) {
232 CodeError = createClientErrorConstructor(HttpError, name, code)
235 CodeError = createServerErrorConstructor(HttpError, name, code)
240 // export the constructor
241 exports[code] = CodeError
242 exports[name] = CodeError
246 // backwards-compatibility
247 exports["I'mateapot"] = deprecate.function(exports.ImATeapot,
248 '"I\'mateapot"; use "ImATeapot" instead')
252 * Convert a string of words to a JavaScript identifier.
256 function toIdentifier (str) {
257 return str.split(' ').map(function (token) {
258 return token.slice(0, 1).toUpperCase() + token.slice(1)
259 }).join('').replace(/[^ _0-9a-z]/gi, '')