second
[josuexyz/.git] / node_modules / http-errors / index.js
1 /*!
2  * http-errors
3  * Copyright(c) 2014 Jonathan Ong
4  * Copyright(c) 2016 Douglas Christopher Wilson
5  * MIT Licensed
6  */
7
8 'use strict'
9
10 /**
11  * Module dependencies.
12  * @private
13  */
14
15 var deprecate = require('depd')('http-errors')
16 var setPrototypeOf = require('setprototypeof')
17 var statuses = require('statuses')
18 var inherits = require('inherits')
19
20 /**
21  * Module exports.
22  * @public
23  */
24
25 module.exports = createError
26 module.exports.HttpError = createHttpErrorConstructor()
27
28 // Populate exports for all constructors
29 populateConstructorExports(module.exports, statuses.codes, module.exports.HttpError)
30
31 /**
32  * Get the code class of a status code.
33  * @private
34  */
35
36 function codeClass (status) {
37   return Number(String(status).charAt(0) + '00')
38 }
39
40 /**
41  * Create a new HTTP Error.
42  *
43  * @returns {Error}
44  * @public
45  */
46
47 function createError () {
48   // so much arity going on ~_~
49   var err
50   var msg
51   var status = 500
52   var props = {}
53   for (var i = 0; i < arguments.length; i++) {
54     var arg = arguments[i]
55     if (arg instanceof Error) {
56       err = arg
57       status = err.status || err.statusCode || status
58       continue
59     }
60     switch (typeof arg) {
61       case 'string':
62         msg = arg
63         break
64       case 'number':
65         status = arg
66         if (i !== 0) {
67           deprecate('non-first-argument status code; replace with createError(' + arg + ', ...)')
68         }
69         break
70       case 'object':
71         props = arg
72         break
73     }
74   }
75
76   if (typeof status === 'number' && (status < 400 || status >= 600)) {
77     deprecate('non-error status code; use only 4xx or 5xx status codes')
78   }
79
80   if (typeof status !== 'number' ||
81     (!statuses[status] && (status < 400 || status >= 600))) {
82     status = 500
83   }
84
85   // constructor
86   var HttpError = createError[status] || createError[codeClass(status)]
87
88   if (!err) {
89     // create error
90     err = HttpError
91       ? new HttpError(msg)
92       : new Error(msg || statuses[status])
93     Error.captureStackTrace(err, createError)
94   }
95
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
100   }
101
102   for (var key in props) {
103     if (key !== 'status' && key !== 'statusCode') {
104       err[key] = props[key]
105     }
106   }
107
108   return err
109 }
110
111 /**
112  * Create HTTP error abstract base class.
113  * @private
114  */
115
116 function createHttpErrorConstructor () {
117   function HttpError () {
118     throw new TypeError('cannot construct abstract class')
119   }
120
121   inherits(HttpError, Error)
122
123   return HttpError
124 }
125
126 /**
127  * Create a constructor for a client error.
128  * @private
129  */
130
131 function createClientErrorConstructor (HttpError, name, code) {
132   var className = name.match(/Error$/) ? name : name + 'Error'
133
134   function ClientError (message) {
135     // create the error object
136     var msg = message != null ? message : statuses[code]
137     var err = new Error(msg)
138
139     // capture a stack trace to the construction point
140     Error.captureStackTrace(err, ClientError)
141
142     // adjust the [[Prototype]]
143     setPrototypeOf(err, ClientError.prototype)
144
145     // redefine the error message
146     Object.defineProperty(err, 'message', {
147       enumerable: true,
148       configurable: true,
149       value: msg,
150       writable: true
151     })
152
153     // redefine the error name
154     Object.defineProperty(err, 'name', {
155       enumerable: false,
156       configurable: true,
157       value: className,
158       writable: true
159     })
160
161     return err
162   }
163
164   inherits(ClientError, HttpError)
165
166   ClientError.prototype.status = code
167   ClientError.prototype.statusCode = code
168   ClientError.prototype.expose = true
169
170   return ClientError
171 }
172
173 /**
174  * Create a constructor for a server error.
175  * @private
176  */
177
178 function createServerErrorConstructor (HttpError, name, code) {
179   var className = name.match(/Error$/) ? name : name + 'Error'
180
181   function ServerError (message) {
182     // create the error object
183     var msg = message != null ? message : statuses[code]
184     var err = new Error(msg)
185
186     // capture a stack trace to the construction point
187     Error.captureStackTrace(err, ServerError)
188
189     // adjust the [[Prototype]]
190     setPrototypeOf(err, ServerError.prototype)
191
192     // redefine the error message
193     Object.defineProperty(err, 'message', {
194       enumerable: true,
195       configurable: true,
196       value: msg,
197       writable: true
198     })
199
200     // redefine the error name
201     Object.defineProperty(err, 'name', {
202       enumerable: false,
203       configurable: true,
204       value: className,
205       writable: true
206     })
207
208     return err
209   }
210
211   inherits(ServerError, HttpError)
212
213   ServerError.prototype.status = code
214   ServerError.prototype.statusCode = code
215   ServerError.prototype.expose = false
216
217   return ServerError
218 }
219
220 /**
221  * Populate the exports object with constructors for every error class.
222  * @private
223  */
224
225 function populateConstructorExports (exports, codes, HttpError) {
226   codes.forEach(function forEachCode (code) {
227     var CodeError
228     var name = toIdentifier(statuses[code])
229
230     switch (codeClass(code)) {
231       case 400:
232         CodeError = createClientErrorConstructor(HttpError, name, code)
233         break
234       case 500:
235         CodeError = createServerErrorConstructor(HttpError, name, code)
236         break
237     }
238
239     if (CodeError) {
240       // export the constructor
241       exports[code] = CodeError
242       exports[name] = CodeError
243     }
244   })
245
246   // backwards-compatibility
247   exports["I'mateapot"] = deprecate.function(exports.ImATeapot,
248     '"I\'mateapot"; use "ImATeapot" instead')
249 }
250
251 /**
252  * Convert a string of words to a JavaScript identifier.
253  * @private
254  */
255
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, '')
260 }