├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── common.js ├── index.js ├── lib ├── alreadyInUse.js ├── argument.js ├── argumentNull.js ├── authenticationRequired.js ├── data │ ├── data.js │ └── status-codes.js ├── helpers │ └── class-generator.js ├── http-status.js ├── internal │ └── globalize.js ├── invalid-operation.js ├── io │ ├── file-load.js │ ├── file-not-found.js │ └── io.js ├── middleware │ ├── crashProtector.js │ └── errorHandler.js ├── not-found.js ├── not-supported.js ├── notPermitted.js ├── timeout.js └── validation.js ├── package.json └── tests ├── already-in-use.js ├── argument-null.js ├── argument.js ├── authentication-required.js ├── connection.js ├── data ├── data.js ├── memcached.js ├── mongodb.js ├── redis.js ├── rollback.js ├── sql.js └── transaction.js ├── error.js ├── helpers ├── class-generator.js └── util.js ├── http-status.js ├── invalid-operation.js ├── io ├── directory-not-found.js ├── drive-not-found.js ├── end-of-stream.js ├── file-load.js ├── file-not-found.js ├── io.js └── socket.js ├── log.js ├── middleware └── errorHandler.js ├── not-found.js ├── not-implemented.js ├── not-permitted.js ├── not-supported.js ├── out-of-memory.js ├── range.js ├── reference.js ├── stack-overflow.js ├── support └── index.js ├── syntax.js ├── timeout.js ├── type.js ├── uri.js └── validation.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | node_modules 15 | npm-debug.log -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.1" 4 | - "6.10" 5 | - "8.0" 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at open-source@shutterstock.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute 2 | 3 | Please contribute! Here are some things that would be great: 4 | - [Open an issue!](https://github.com/shutterstock/node-common-errors/issues/new) 5 | - Open a pull request! 6 | - Say hi! :wave: 7 | 8 | Please abide by the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). 9 | 10 | For more on contributing at [@shutterstock](https://github.com/shutterstock), check out [our welcome repository](https://github.com/shutterstock/welcome). 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2017 Shutterstock Images, LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | common-errors 2 | =========== 3 | 4 | > Common error classes and utility functions 5 | 6 | * Full suite of node.js Error classes like most other modern languages 7 | * Append stack traces from other asynchronously generated Errors 8 | * Generate your own custom Error classes in one line 9 | * Map HTTP status codes to Errors for automatic handling in web services and applications 10 | 11 | [![Build Status](https://secure.travis-ci.org/shutterstock/node-common-errors.png)](http://travis-ci.org/shutterstock/node-common-errors) 12 | 13 | ## Install 14 | ```npm install common-errors``` 15 | 16 | ## Class Directory 17 | 18 | ### Common Error Classes 19 | 20 | * [AlreadyInUseError](#alreadyinuse) 21 | * [ArgumentError](#argument) 22 | * [ArgumentNullError](#argumentnull) 23 | * [AuthenticationRequiredError](#authrequired) 24 | * [ConnectionError](#connection) 25 | * [DataError](#data) 26 | * [MemcacedError](#memcached) 27 | * [MongoDBError](#mongodb) 28 | * [RedisError](#redis) 29 | * [RollbackError](#rollback) 30 | * [SQLError](#sql) 31 | * [TransactionError](#transaction) 32 | * [Error](#error) 33 | * [HttpStatusError](#httpstatus) 34 | * [InvalidOperationError](#invalidoperation) 35 | * [IOError](#io) 36 | * [DirectoryNotFoundError](#directorynotfound) 37 | * [DriveNotFoundError](#drivenotfound) 38 | * [EndOfStreamError](#endofstream) 39 | * [FileLoadError](#fileload) 40 | * [FileNotFoundError](#filenotfound) 41 | * [SocketError](#socket) 42 | * [NotFoundError](#notfound) 43 | * [NotImplementedError](#notimplemented) 44 | * [NotPermittedError](#notpermitted) 45 | * [NotSupportedError](#notsupported) 46 | * [OutOfMemoryError](#outofmemory) 47 | * [RangeError](#range) 48 | * [ReferenceError](#reference) 49 | * [StackOverflowError](#stackoverflow) 50 | * [SyntaxError](#syntax) 51 | * [TimeoutError](#timeout) 52 | * [TypeError](#type) 53 | * [URIError](#uri) 54 | * [ValidationError](#validation) 55 | 56 | 57 | ### Utility Functions 58 | 59 | * [log](#log) 60 | * [prependCurrentStack](#prependCurrentStack) 61 | * [generateClass](#generateClass) 62 | 63 | ### Express Middleware Functions 64 | 65 | * [Crash Protector](#crashprotector) 66 | * [Error Handler](#errorhandler) 67 | 68 | ## Common Error Classes 69 | 70 | 71 | 72 | ### AlreadyInUseError 73 | 74 | Applicable when a resource is already in use, for example unique key constraints like a username. 75 | 76 | new AlreadyInUseError(entityName, arg1, [arg2, arg3, arg4, ...]) 77 | 78 | __Arguments__ 79 | 80 | * `entityName` - the entity that owns the protected resource 81 | * `args` - the fields or attributes that are already in use 82 | 83 | ```js 84 | // Example 85 | throw new errors.AlreadyInUseError('user', 'username'); 86 | ``` 87 | 88 | --------------------------------------- 89 | 90 | 91 | 92 | ### ArgumentError 93 | 94 | Applicable when there's a generic problem with an argument received by a function call. 95 | 96 | new ArgumentError(argumentName[, inner_error]) 97 | 98 | __Arguments__ 99 | 100 | * `argumentName` - the name of the argument that has a problem 101 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 102 | 103 | ```js 104 | // Example 105 | throw new errors.ArgumentError('username', err); 106 | ``` 107 | 108 | --------------------------------------- 109 | 110 | 111 | 112 | ### ArgumentNullError 113 | 114 | Applicable when an argument received by a function call is null/undefined or empty. 115 | 116 | new ArgumentNullError(argumentName[, inner_error]) 117 | 118 | __Arguments__ 119 | 120 | * `argumentName` - the name of the argument that is null 121 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 122 | 123 | ```js 124 | // Example 125 | throw new errors.ArgumentNullError('username', err); 126 | ``` 127 | 128 | --------------------------------------- 129 | 130 | 131 | 132 | ### AuthenticationRequiredError 133 | 134 | Applicable when an operation requires authentication 135 | 136 | new AuthenticationRequiredError(message, [inner_error]) 137 | 138 | __Arguments__ 139 | 140 | * `message` - any message 141 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 142 | 143 | ```js 144 | // Example 145 | throw new errors.AuthenticationRequiredError("Please provide authentication.", err) 146 | ``` 147 | 148 | --------------------------------------- 149 | 150 | 151 | 152 | ### ConnectionError 153 | 154 | Applicable when an error occurs on a connection. 155 | 156 | new ConnectionError(message[, inner_error]) 157 | 158 | __Arguments__ 159 | 160 | * `message` - any message 161 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 162 | 163 | ```js 164 | // Example 165 | throw new errors.ConnectionError('database connection no longer available', err); 166 | ``` 167 | 168 | --------------------------------------- 169 | 170 | 171 | 172 | ### DataError 173 | 174 | Applicable when an error occurs on or with an external data source. 175 | 176 | new DataError(message[, inner_error]) 177 | 178 | __Arguments__ 179 | 180 | * `message` - any message 181 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 182 | 183 | ```js 184 | // Example 185 | throw new errors.data.DataError('Too many rows returned from database', err); 186 | ``` 187 | 188 | --------------------------------------- 189 | 190 | 191 | 192 | ### MemcachedError 193 | 194 | Applicable when an error occurs while using memcached. 195 | 196 | new MemcachedError(message[, inner_error]) 197 | 198 | __Arguments__ 199 | 200 | * `message` - any message 201 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 202 | 203 | ```js 204 | // Example 205 | throw new errors.data.MemcachedError('Expected value not found', err); 206 | ``` 207 | 208 | --------------------------------------- 209 | 210 | 211 | 212 | ### MongoDBError 213 | 214 | Applicable when an error occurs while using MongoDB. 215 | 216 | new MongoDBError(message[, inner_error]) 217 | 218 | __Arguments__ 219 | 220 | * `message` - any message 221 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 222 | 223 | ```js 224 | // Example 225 | throw new errors.data.MongoDBError('Retrieved value not in expected format', err); 226 | ``` 227 | 228 | --------------------------------------- 229 | 230 | 231 | 232 | ### RedisError 233 | 234 | Applicable when an error occurs while using redis. 235 | 236 | new RedisError(message[, inner_error]) 237 | 238 | __Arguments__ 239 | 240 | * `message` - any message 241 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 242 | 243 | ```js 244 | // Example 245 | throw new errors.data.RedisError('expected value not found in redis', err); 246 | ``` 247 | 248 | --------------------------------------- 249 | 250 | 251 | 252 | ### RollbackError 253 | 254 | Applicable when a transaction was unexpectedly rolled back. 255 | 256 | new RollbackError(message[, inner_error]) 257 | 258 | __Arguments__ 259 | 260 | * `message` - any message 261 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 262 | 263 | ```js 264 | // Example 265 | throw new errors.data.RollbackError('database transaction was unexpectedly rolled back', err); 266 | ``` 267 | 268 | --------------------------------------- 269 | 270 | 271 | 272 | ### SQLError 273 | 274 | Applicable when an error occurs while using a SQL database. 275 | 276 | new SQLError(message[, inner_error]) 277 | 278 | __Arguments__ 279 | 280 | * `message` - any message 281 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 282 | 283 | ```js 284 | // Example 285 | throw new errors.data.SQLError('foreign key constraint violated', err); 286 | ``` 287 | 288 | --------------------------------------- 289 | 290 | 291 | 292 | ### TransactionError 293 | 294 | Applicable when an error unexpectedly interrupts a transaction. 295 | 296 | new TransactionError(message[, inner_error]) 297 | 298 | __Arguments__ 299 | 300 | * `message` - any message 301 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 302 | 303 | ```js 304 | // Example 305 | throw new errors.data.TransactionError('transaction already complete', err); 306 | ``` 307 | 308 | --------------------------------------- 309 | 310 | 311 | 312 | ### Error 313 | 314 | This is roughly the same as the native Error class. It additionally supports an inner_error attribute. 315 | 316 | new Error(message, [inner_error]) 317 | 318 | __Arguments__ 319 | 320 | * `message` - any message 321 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 322 | 323 | ```js 324 | // Example 325 | throw new errors.Error("Please provide authentication.", err) 326 | ``` 327 | 328 | --------------------------------------- 329 | 330 | 331 | 332 | ### HttpStatusError 333 | 334 | Represents a message and a HTTP status code. 335 | 336 | new HttpStatusError(status_code[, message]) 337 | 338 | __Arguments__ 339 | 340 | * `status_code` - any HTTP status code integer 341 | * `message` - any message 342 | 343 | ```js 344 | // Example 345 | throw new errors.HttpStatusError(404, "Not Found"); 346 | ``` 347 | 348 | new HttpStatusError(err[, req]) 349 | 350 | Figure out a proper status code and message from a given error. The current mapping of error codes 351 | to HTTP status codes is as follows: 352 | 353 | ```javascript 354 | { 355 | "ValidationError": 400, 356 | "ArgumentError": 400, 357 | "AuthenticationRequiredError": 401, 358 | "NotPermittedError": 403, 359 | "ArgumentNullError": 404 // , or 400 depending on what's wrong with the request 360 | "NotFoundError": 404, 361 | "NotSupportedError": 405, 362 | "AlreadyInUseError": 409, 363 | } 364 | ``` 365 | 366 | To change the mappings, modify `HttpStatusError.message_map` and `HttpStatusError.code_map` 367 | 368 | __Arguments__ 369 | 370 | * `err` - any instanceof Error 371 | * `req` - the request object 372 | 373 | 374 | 375 | ```js 376 | // Example 377 | throw new errors.HttpStatusError(err, req); 378 | ``` 379 | 380 | --------------------------------------- 381 | 382 | 383 | 384 | ### InvalidOperationError 385 | 386 | Applicable when an invalid operation occurs. 387 | 388 | new InvalidOperationError(message[, inner_error]) 389 | 390 | __Arguments__ 391 | 392 | * `message` - any message 393 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 394 | 395 | ```js 396 | // Example 397 | throw new errors.InvalidOperationError('divide by zero', err); 398 | ``` 399 | 400 | --------------------------------------- 401 | 402 | 403 | 404 | ### IOError 405 | 406 | Base class for Errors while accessing information using streams, files and directories. 407 | 408 | new IOError(message[, inner_error]) 409 | 410 | __Arguments__ 411 | 412 | * `message` - any message 413 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 414 | 415 | ```js 416 | // Example 417 | throw new errors.io.IOError("Could not open file", err) 418 | ``` 419 | 420 | --------------------------------------- 421 | 422 | 423 | 424 | ### DirectoryNotFoundError 425 | 426 | Applicable when part of a file or directory cannot be found. 427 | 428 | new DirectoryNotFoundError(message[, inner_error]) 429 | 430 | __Arguments__ 431 | 432 | * `message` - any message 433 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 434 | 435 | ```js 436 | // Example 437 | throw new errors.io.DirectoryNotFoundError("/var/log", err) 438 | ``` 439 | 440 | --------------------------------------- 441 | 442 | 443 | 444 | ### DriveNotFoundError 445 | 446 | Applicable when trying to access a drive or share that is not available. 447 | 448 | new DriveNotFoundError(message[, inner_error]) 449 | 450 | __Arguments__ 451 | 452 | * `message` - any message 453 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 454 | 455 | ```js 456 | // Example 457 | throw new errors.io.DriveNotFoundError("c", err) 458 | ``` 459 | 460 | --------------------------------------- 461 | 462 | 463 | 464 | ### EndOfStreamError 465 | 466 | Applicable when reading is attempted past the end of a stream. 467 | 468 | new EndOfStreamError(message[, inner_error]) 469 | 470 | __Arguments__ 471 | 472 | * `message` - any message 473 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 474 | 475 | ```js 476 | // Example 477 | throw new errors.io.EndOfStreamError("EOS while reading header", err) 478 | ``` 479 | 480 | --------------------------------------- 481 | 482 | 483 | 484 | ### FileLoadError 485 | 486 | Applicable when a file is found and read but cannot be loaded. 487 | 488 | new FileLoadError(message[, inner_error]) 489 | 490 | __Arguments__ 491 | 492 | * `file_name` - any message 493 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 494 | 495 | ```js 496 | // Example 497 | throw new errors.io.FileLoadError("./package.json", err) 498 | ``` 499 | 500 | --------------------------------------- 501 | 502 | 503 | 504 | ### FileNotFoundError 505 | 506 | Applicable when an attempt to access a file that does not exist on disk fails. 507 | 508 | new FileNotFoundError(message[, inner_error]) 509 | 510 | __Arguments__ 511 | 512 | * `file_name` - any message 513 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 514 | 515 | ```js 516 | // Example 517 | throw new errors.io.FileNotFoundError("./package.json", err) 518 | ``` 519 | 520 | --------------------------------------- 521 | 522 | 523 | 524 | ### SocketError 525 | 526 | Applicable when an error occurs on a socket. 527 | 528 | new SocketError(message[, inner_error]) 529 | 530 | __Arguments__ 531 | 532 | * `message` - any message 533 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 534 | 535 | ```js 536 | // Example 537 | throw new errors.SocketError('socket no longer available', err); 538 | ``` 539 | 540 | --------------------------------------- 541 | 542 | 543 | 544 | ### NotFoundError 545 | 546 | Applicable when an attempt to retrieve data yielded no result. 547 | 548 | new NotFoundError(entity_name[, inner_error]) 549 | 550 | __Arguments__ 551 | 552 | * `entity_name` - a description for what was not found 553 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 554 | 555 | ```js 556 | // Example 557 | throw new errors.NotFoundError("User", err) 558 | ``` 559 | 560 | --------------------------------------- 561 | 562 | 563 | 564 | ### NotImplementedError 565 | 566 | Applicable when a requested method or operation is not implemented. 567 | 568 | new NotImplementedError(message[, inner_error]) 569 | 570 | __Arguments__ 571 | 572 | * `message` - any message 573 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 574 | 575 | ```js 576 | // Example 577 | throw new errors.NotImplementedError("Method is not yet implemented.", err) 578 | ``` 579 | 580 | --------------------------------------- 581 | 582 | 583 | 584 | ### NotPermittedError 585 | 586 | Applicable when an operation is not permitted 587 | 588 | new NotPermittedError(message[, inner_error]) 589 | 590 | __Arguments__ 591 | 592 | * `message` - any message 593 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 594 | 595 | ```js 596 | // Example 597 | throw new errors.NotPermittedError("username cannot be changed once set.", err) 598 | ``` 599 | 600 | --------------------------------------- 601 | 602 | 603 | 604 | ### NotSupportedError 605 | 606 | Applicable when a certain condition is not supported by your application. 607 | 608 | new NotSupportedError(message[, inner_error]) 609 | 610 | __Arguments__ 611 | 612 | * `message` - a message 613 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 614 | 615 | ```js 616 | // Example 617 | throw new errors.NotSupportedError('Zero values', err); 618 | ``` 619 | 620 | --------------------------------------- 621 | 622 | 623 | 624 | ### OutOfMemoryError 625 | 626 | Applicable when there is not enough memory to continue the execution of a program. 627 | 628 | new OutOfMemoryError(message[, inner_error]) 629 | 630 | __Arguments__ 631 | 632 | * `message` - a message 633 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 634 | 635 | ```js 636 | // Example 637 | throw new errors.OutOfMemoryError('Maximum mem size exceeded.', err); 638 | ``` 639 | 640 | --------------------------------------- 641 | 642 | 643 | 644 | ### RangeError 645 | 646 | Represents an error that occurs when a numeric variable or parameter is outside of its valid range. This is roughly the same as the native RangeError class. It additionally supports an inner_error attribute. 647 | 648 | new RangeError(message[, inner_error]) 649 | 650 | __Arguments__ 651 | 652 | * `message` - a message 653 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 654 | 655 | ```js 656 | // Example 657 | throw new errors.RangeError("Value must be between " + MIN + " and " + MAX, err); 658 | ``` 659 | 660 | --------------------------------------- 661 | 662 | 663 | 664 | ### ReferenceError 665 | 666 | Represents an error when a non-existent variable is referenced. This is roughly the same as the native ReferenceError class. It additionally supports an inner_error attribute. 667 | 668 | new ReferenceError(message[, inner_error]) 669 | 670 | __Arguments__ 671 | 672 | * `message` - a message 673 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 674 | 675 | ```js 676 | // Example 677 | throw new errors.ReferenceError("x is not defined", err); 678 | ``` 679 | 680 | --------------------------------------- 681 | 682 | 683 | 684 | ### StackOverflowError 685 | 686 | Applicable when the execution stack overflows because it contains too many nested method calls. 687 | 688 | new StackOverflowError(message[, inner_error]) 689 | 690 | __Arguments__ 691 | 692 | * `message` - a message 693 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 694 | 695 | ```js 696 | // Example 697 | throw new errors.StackOverflowError('Stack overflow detected.', err); 698 | ``` 699 | 700 | --------------------------------------- 701 | 702 | 703 | 704 | ### SyntaxError 705 | 706 | Represents an error when trying to interpret syntactically invalid code. This is roughly the same as the native SyntaxError class. It additionally supports an inner_error attribute. 707 | 708 | new SyntaxError(message[, inner_error]) 709 | 710 | __Arguments__ 711 | 712 | * `message` - a message 713 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 714 | 715 | ```js 716 | // Example 717 | throw new errors.SyntaxError("Unexpected token a", err); 718 | ``` 719 | 720 | --------------------------------------- 721 | 722 | 723 | 724 | ### TimeoutError 725 | 726 | Applicable when an operation takes longer than the alloted amount. 727 | 728 | new TimeoutError(time[, inner_error]) 729 | 730 | __Arguments__ 731 | 732 | * `time` - a time duration 733 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 734 | 735 | ```js 736 | // Example 737 | throw new errors.TimeoutError('100ms', err); 738 | ``` 739 | 740 | --------------------------------------- 741 | 742 | 743 | 744 | ### TypeError 745 | 746 | Represents an error when a value is not of the expected type. This is roughly the same as the native TypeError class. It additionally supports an inner_error attribute. 747 | 748 | new TypeError(message[, inner_error]) 749 | 750 | __Arguments__ 751 | 752 | * `message` - a message 753 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 754 | 755 | ```js 756 | // Example 757 | throw new errors.TypeError("number is not a function", err); 758 | ``` 759 | 760 | --------------------------------------- 761 | 762 | 763 | 764 | ### URIError 765 | 766 | Represents an error when a value is not of the expected type. This is roughly the same as the native URIError class. It additionally supports an inner_error attribute. 767 | 768 | new URIError(message[, inner_error]) 769 | 770 | __Arguments__ 771 | 772 | * `message` - a message 773 | * `inner_error` - the Error instance that caused the current error. Stack trace will be appended. 774 | 775 | ```js 776 | // Example 777 | throw new errors.URIError("URI malformed", err); 778 | ``` 779 | 780 | --------------------------------------- 781 | 782 | 783 | 784 | ### ValidationError 785 | 786 | Useful for denoting a problem with a user-defined value. Generally, you won't throw this error. 787 | It serializes to JSON, and it can also function as an envelope for multiple errors. 788 | 789 | new ValidationError(message, [code], [field]) 790 | 791 | __Arguments__ 792 | 793 | * `message` - any message 794 | * `code` - an optional error code 795 | * `field` - an optional description of the data 796 | 797 | __Methods__ 798 | 799 | * `addError(error)` - add an error object to the `errors` array, and return `this`. 800 | * `addErrors(errors)` - append an array of error objects to the `errors` array, and return `this`. 801 | 802 | ```js 803 | // Example 804 | function validateUsername(username){ 805 | var errors = new errors.ValidationError(); 806 | if(username.length < 3) errors.addError(new errors.ValidationError("username must be at least two characters long", "VAL_MIN_USERNAME_LENGTH", "username")); 807 | if(/-%$*&!/.test(username)) errors.addError(new errors.ValidationError("username may not contain special characters", "VAL_USERNAME_SPECIALCHARS", "username")); 808 | return errors; 809 | } 810 | ``` 811 | 812 | ## Utility Functions 813 | 814 | 815 | 816 | ### Log 817 | 818 | Modifies an error's stack to include the current stack and logs it to *stderr*. Useful for logging errors received by a callback. 819 | 820 | log(err[, message]) 821 | 822 | __Arguments__ 823 | 824 | * `err` - any error or error message received from a callback 825 | * `message` - any message you'd like to prepend 826 | 827 | ```js 828 | // Example 829 | mysql.query('SELECT * `FROM` users', function(err, results){ 830 | if(err) return errors.log(err, "Had trouble retrieving users."); 831 | console.log(results); 832 | }); 833 | ``` 834 | 835 | 836 | 837 | ### prependCurrentStack 838 | 839 | Modifies an error's stack to include the current stack without logging it. Useful for logging errors received by a callback. 840 | 841 | prependCurrentStack(err) 842 | 843 | __Arguments__ 844 | 845 | * `err` - any error or error message received from a callback 846 | 847 | ```js 848 | // Example 849 | mysql.query('SELECT * `FROM` users', function(err, results){ 850 | if(err) { 851 | return errors.prependCurrentStack(err); // caller has better idea of source of err 852 | } 853 | console.log(results); 854 | }); 855 | ``` 856 | 857 | 858 | 859 | ### generateClass 860 | 861 | Simple interface for generating a new Error class type. 862 | 863 | helpers.generateClass(name[, options]) 864 | 865 | __Arguments__ 866 | 867 | * `name` - The full name of the new Error class 868 | * `options` 869 | * `extends` - The base class for the new Error class. Default is `Error`. 870 | * `globalize` - Boolean (default `true`) to store the Error in global space so that the Error is equivalent to others included from other versions of the module. 871 | * `args` - Array of names of values to accept and store from the class constructor. Default is `['message', 'inner_error']`. 872 | * `generateMessage` - A function for defining a custom error message. 873 | 874 | ```js 875 | // Example 876 | var ArgumentNullError = helpers.generateClass("ArgumentNullError", { 877 | extends: ArgumentError, 878 | args: ['argumentName'], 879 | generateMessage: function(){ 880 | return "Missing argument: " + this.argumentName; 881 | } 882 | }); 883 | 884 | throw new ArgumentNullError("username"); 885 | ``` 886 | 887 | 888 | ## Express Middleware Functions 889 | 890 | 891 | 892 | ### Crash Protector 893 | 894 | Express middleware for preventing the web server from crashing when an error is thrown from an asynchronous context. 895 | Any error that would have caused a crash is logged to *stderr*. 896 | 897 | ```js 898 | // Example 899 | var app = express(); 900 | 901 | app.use(express.static(__dirname + '/../public')); 902 | app.use(express.bodyParser()); 903 | app.use(errors.middleware.crashProtector()); 904 | 905 | //insert new middleware here 906 | 907 | app.get('/healthcheck', function (req, res, next){res.send('YESOK')}); 908 | 909 | app.use(app.router); 910 | app.use(errors.middleware.errorHandler); 911 | 912 | module.exports = app; 913 | ``` 914 | 915 | 916 | 917 | ### Error Handler 918 | 919 | Express middleware that translates common errors into HTTP status codes and messages. 920 | 921 | ```js 922 | // Example 923 | var app = express(); 924 | 925 | app.use(express.static(__dirname + '/../public')); 926 | app.use(express.bodyParser()); 927 | app.use(errors.middleware.crashProtector()); 928 | 929 | //insert new middleware here 930 | 931 | app.get('/healthcheck', function (req, res, next){res.send('YESOK')}); 932 | 933 | app.use(app.router); 934 | app.use(errors.middleware.errorHandler); 935 | 936 | module.exports = app; 937 | ``` 938 | 939 | ## Authors 940 | 941 | This library was developed by David Fenster at [Shutterstock](http://www.shutterstock.com) 942 | 943 | ## Contribute 944 | 945 | Please do! Check out our [Contributing guidelines](CONTRIBUTING.md). 946 | 947 | ## License 948 | 949 | [MIT](LICENSE) © 2013-2017 Shutterstock Images, LLC 950 | -------------------------------------------------------------------------------- /common.js: -------------------------------------------------------------------------------- 1 | var exports = module.exports = { 2 | helpers: { 3 | generateClass: require('./lib/helpers/class-generator') 4 | }, 5 | }; 6 | 7 | exports.AlreadyInUseError = exports.AlreadyInUse = require('./lib/alreadyInUse'); 8 | exports.ArgumentError = exports.Argument = require('./lib/argument'); 9 | exports.ArgumentNullError = exports.ArgumentNull = require('./lib/argumentNull'); 10 | exports.AuthenticationRequiredError = exports.AuthenticationRequired = require('./lib/authenticationRequired'); 11 | exports.ConnectionError = exports.helpers.generateClass('ConnectionError'); 12 | exports.Error = exports.helpers.generateClass('Error'); 13 | exports.CommonError = Error; 14 | exports.HttpStatusError = exports.HttpStatus = require('./lib/http-status'); 15 | exports.InvalidOperationError = require('./lib/invalid-operation'); 16 | exports.NotFoundError = require('./lib/not-found'); 17 | exports.NotImplementedError = exports.helpers.generateClass('NotImplementedError'), 18 | exports.NotSupportedError = exports.NotSupported = require('./lib/not-supported'); 19 | exports.NotPermittedError = exports.NotPermitted = require('./lib/notPermitted'); 20 | exports.OutOfMemoryError = exports.helpers.generateClass('OutOfMemoryError'); 21 | exports.RangeError = exports.helpers.generateClass('RangeError', { extends: RangeError }); 22 | exports.ReferenceError = exports.helpers.generateClass('ReferenceError', { extends: ReferenceError }); 23 | exports.StackOverflowError = exports.helpers.generateClass('StackOverflowError'); 24 | exports.SyntaxError = exports.helpers.generateClass('SyntaxError', { extends: SyntaxError }); 25 | exports.TimeoutError = require('./lib/timeout.js') 26 | exports.TypeError = exports.helpers.generateClass('TypeError', { extends: TypeError }); 27 | exports.URIError = exports.helpers.generateClass('URIError', { extends: URIError }); 28 | exports.ValidationError = exports.Validation = require('./lib/validation'); 29 | 30 | exports.io = { 31 | IOError: require('./lib/io/io') 32 | }; 33 | exports.io.DirectoryNotFoundError = exports.helpers.generateClass('DirectoryNotFoundError', { extends: exports.io.IOError }); 34 | exports.io.DriveNotFoundError = exports.helpers.generateClass('DriveNotFoundError', { extends: exports.io.IOError }); 35 | exports.io.EndOfStreamError = exports.helpers.generateClass('EndOfStreamError', { extends: exports.io.IOError }); 36 | exports.io.FileLoadError = require('./lib/io/file-load'); 37 | exports.io.FileNotFoundError = require('./lib/io/file-not-found'); 38 | exports.io.SocketError = exports.helpers.generateClass('SocketError', { extends: exports.io.IOError }); 39 | 40 | exports.data = { 41 | DataError: require('./lib/data/data') 42 | }; 43 | exports.data.MemcachedError = exports.helpers.generateClass('MemcachedError', { extends: exports.data.DataError }); 44 | exports.data.MongoDBError = exports.helpers.generateClass('MongoDBError', { extends: exports.data.DataError }); 45 | exports.data.RedisError = exports.helpers.generateClass('RedisError', { extends: exports.data.DataError }); 46 | exports.data.RollbackError = exports.helpers.generateClass('RollbackError', { extends: exports.data.DataError }); 47 | exports.data.SQLError = exports.helpers.generateClass('SQLError', { extends: exports.data.DataError }); 48 | exports.data.TransactionError = exports.helpers.generateClass('TransactionError', { extends: exports.data.DataError }); 49 | 50 | 51 | 52 | exports.Generic = exports.helpers.generateClass('GenericError'); //deprecated 53 | 54 | 55 | var logErrorDeprecationWarning = false; 56 | module.exports.logError = function(err, cb) { 57 | if (!logErrorDeprecationWarning) console.warn("logError is deprecated. Use log instead."); 58 | logErrorDeprecationWarning = true; 59 | 60 | if (err && !err.isLogged) { 61 | err.isLogged = true; 62 | console.error(err); 63 | } 64 | if (cb) cb(err); 65 | }; 66 | 67 | module.exports.log = function(err, message) { 68 | if (typeof err == 'string') { 69 | err = new module.exports.Error(err); 70 | } else { 71 | if (message) { 72 | err.message = message; 73 | } 74 | err = module.exports.prependCurrentStack(err, 3); 75 | } 76 | if (err) { 77 | console.error(err && err.stack || err); 78 | err.isLogged = true; 79 | } 80 | return err; 81 | } 82 | 83 | module.exports.prependCurrentStack = function(err, offset_) { 84 | var linesToSkip = (typeof offset_ === 'undefined') ? 2 : offset_; 85 | var stackToPrepend = (new Error()).stack.split("\n").slice(linesToSkip); 86 | var mainStack = (err.stack || '').split("\n"); 87 | var errTitle = mainStack.shift(); 88 | err.stack = [errTitle].concat(stackToPrepend, "====", mainStack).join("\n"); 89 | return err; 90 | }; 91 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./common'); 2 | 3 | module.exports.middleware = { 4 | errorHandler: require('./lib/middleware/errorHandler'), 5 | crashProtector: require('./lib/middleware/crashProtector') 6 | }; 7 | -------------------------------------------------------------------------------- /lib/alreadyInUse.js: -------------------------------------------------------------------------------- 1 | var generateClass = require('./helpers/class-generator'); 2 | module.exports = generateClass("AlreadyInUseError", { 3 | args: ['entity_name', 'arg1', 'arg2', 'arg3', 'arg4'], 4 | generateMessage: function(){ 5 | var args = Array.prototype.slice.call(this.args, 1); 6 | return "The specified '" + this.entity_name + "' value is already in use for: " + args.join(', '); 7 | } 8 | }) 9 | -------------------------------------------------------------------------------- /lib/argument.js: -------------------------------------------------------------------------------- 1 | var generateClass = require('./helpers/class-generator'); 2 | module.exports = generateClass("ArgumentError", { 3 | args: ['argumentName', 'inner_error'], 4 | generateMessage: function(){ 5 | return "Invalid or missing argument supplied: " + this.argumentName; 6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lib/argumentNull.js: -------------------------------------------------------------------------------- 1 | var generateClass = require('./helpers/class-generator'); 2 | module.exports = generateClass("ArgumentNullError", { 3 | args: ['argumentName', 'inner_error'], 4 | extends: require('./argument'), 5 | generateMessage: function(){ 6 | return "Missing argument: " + this.argumentName; 7 | } 8 | }) 9 | -------------------------------------------------------------------------------- /lib/authenticationRequired.js: -------------------------------------------------------------------------------- 1 | var generateClass = require('./helpers/class-generator'); 2 | module.exports = generateClass("AuthenticationRequiredError", { 3 | args: ['message', 'inner_error'], 4 | generateMessage: function(){ 5 | return "An attempt was made to perform an operation without authentication: " + this.message; 6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lib/data/data.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../helpers/class-generator')('DataError'); -------------------------------------------------------------------------------- /lib/data/status-codes.js: -------------------------------------------------------------------------------- 1 | /* A dump of current https://nodejs.org/docs/latest/api/http.html#http_http_status_codes to avoid including native node modules in browser usage */ 2 | module.exports = { 3 | '100': 'Continue', 4 | '101': 'Switching Protocols', 5 | '102': 'Processing', 6 | '103': 'Early Hints', 7 | '200': 'OK', 8 | '201': 'Created', 9 | '202': 'Accepted', 10 | '203': 'Non-Authoritative Information', 11 | '204': 'No Content', 12 | '205': 'Reset Content', 13 | '206': 'Partial Content', 14 | '207': 'Multi-Status', 15 | '208': 'Already Reported', 16 | '226': 'IM Used', 17 | '300': 'Multiple Choices', 18 | '301': 'Moved Permanently', 19 | '302': 'Found', 20 | '303': 'See Other', 21 | '304': 'Not Modified', 22 | '305': 'Use Proxy', 23 | '307': 'Temporary Redirect', 24 | '308': 'Permanent Redirect', 25 | '400': 'Bad Request', 26 | '401': 'Unauthorized', 27 | '402': 'Payment Required', 28 | '403': 'Forbidden', 29 | '404': 'Not Found', 30 | '405': 'Method Not Allowed', 31 | '406': 'Not Acceptable', 32 | '407': 'Proxy Authentication Required', 33 | '408': 'Request Timeout', 34 | '409': 'Conflict', 35 | '410': 'Gone', 36 | '411': 'Length Required', 37 | '412': 'Precondition Failed', 38 | '413': 'Payload Too Large', 39 | '414': 'URI Too Long', 40 | '415': 'Unsupported Media Type', 41 | '416': 'Range Not Satisfiable', 42 | '417': 'Expectation Failed', 43 | '418': 'I\'m a Teapot', 44 | '421': 'Misdirected Request', 45 | '422': 'Unprocessable Entity', 46 | '423': 'Locked', 47 | '424': 'Failed Dependency', 48 | '425': 'Unordered Collection', 49 | '426': 'Upgrade Required', 50 | '428': 'Precondition Required', 51 | '429': 'Too Many Requests', 52 | '431': 'Request Header Fields Too Large', 53 | '451': 'Unavailable For Legal Reasons', 54 | '500': 'Internal Server Error', 55 | '501': 'Not Implemented', 56 | '502': 'Bad Gateway', 57 | '503': 'Service Unavailable', 58 | '504': 'Gateway Timeout', 59 | '505': 'HTTP Version Not Supported', 60 | '506': 'Variant Also Negotiates', 61 | '507': 'Insufficient Storage', 62 | '508': 'Loop Detected', 63 | '509': 'Bandwidth Limit Exceeded', 64 | '510': 'Not Extended', 65 | '511': 'Network Authentication Required' 66 | }; 67 | -------------------------------------------------------------------------------- /lib/helpers/class-generator.js: -------------------------------------------------------------------------------- 1 | var globalize = require('../internal/globalize'); 2 | 3 | module.exports = function generateErrorClass(name, options){ 4 | options = options || {}; 5 | if(options.subclass) console.warn("options.subclass is deprecated. use options.extends instead."); 6 | options.extends = options.extends || options.subclass || Error; 7 | options.args = options.args || ['message', 'inner_error']; 8 | options.generateMessage = options.generateMessage || null; 9 | options.globalize = options.globalize === false ? false : true; 10 | 11 | validateInput(name); 12 | validateArrayInput(options.args); 13 | 14 | var classConstructor = function classConstructor(){ 15 | if(this.global_initialize) this.global_initialize(Class); 16 | 17 | this.args = arguments; 18 | for(var i = 0; i= 500 ? 500 : 400]; 37 | this.message = message || http_message; 38 | if(!this.stack) Error.captureStackTrace(this, HttpStatusError); 39 | if(message) this.stack = http_message + "\n" + this.stack; 40 | } 41 | 42 | HttpStatusError.prototype = Object.create(Error.prototype); 43 | HttpStatusError.prototype.constructor = HttpStatusError; 44 | 45 | var code_map = HttpStatusError.code_map = { 46 | "ValidationError": 400, 47 | "ArgumentError": 400, 48 | "AuthenticationRequiredError": 401, 49 | "NotPermittedError": 403, 50 | "ArgumentNullError": function(err, req){ 51 | var method = req && req.method || 'GET'; 52 | var params = req && req.params || {}; 53 | var route_path = req && req.route && req.route.path || ''; 54 | 55 | if(/GET|HEAD/i.test(method) || params.hasOwnProperty(err.argumentName) || new RegExp(":" + err.argumentName + '').test(route_path + '/')) err.status_code = 404; 56 | else err.status_code = 400; 57 | err.message = err.message.replace(new RegExp("^Missing argument: (" + err.argumentName + ")$"), 'Not Found: "$1"' ); 58 | }, 59 | "NotFoundError": 404, 60 | "NotSupportedError": 405, 61 | "AlreadyInUseError": 409, 62 | }; 63 | 64 | var codes = {}; 65 | Object.keys(STATUS_CODES).forEach(function(key){ 66 | codes[key] = STATUS_CODES[key]; 67 | }); 68 | var message_map = HttpStatusError.message_map = codes; 69 | 70 | module.exports = HttpStatusError; 71 | -------------------------------------------------------------------------------- /lib/internal/globalize.js: -------------------------------------------------------------------------------- 1 | var key = "__COMMON-ERRORS-TYPES__"; 2 | var global_errors = global[key] = global[key] || {}; 3 | 4 | module.exports = function global_extend(Class) { 5 | Class.__original_prototype__ = Class.prototype; 6 | var global_class = global_errors[Class.name] = global_errors[Class.name] || Class; 7 | Class.prototype = Class.__global_prototype__ = global_class.prototype; 8 | Class.prototype.global_initialize = Class.prototype.global_initialize || function global_initialize(Class){ 9 | var proto_keys = Object.keys(Class.__original_prototype__); 10 | for(var i = 0; i= 4) { 26 | errorHandler = middleware; 27 | break; 28 | } else if(app.router === middleware.handle) foundRouter = true; 29 | } 30 | return errorHandler; 31 | } catch(e) { 32 | console.error("Crash protector error", e); 33 | } 34 | } -------------------------------------------------------------------------------- /lib/middleware/errorHandler.js: -------------------------------------------------------------------------------- 1 | var HttpStatusError = require('../http-status'); 2 | 3 | module.exports = function errorHandler(err, req, res, next){ 4 | if(!err) { 5 | if(next) return next(); 6 | else return res.end(); 7 | } 8 | 9 | err = new HttpStatusError(err, req); 10 | if(err.status_code >= 500) { 11 | console.error(err.stack); 12 | err.message = HttpStatusError.message_map[500]; //hide the real error from user agent. 13 | } 14 | 15 | res.status(err.status_code).send(err.message); 16 | } 17 | -------------------------------------------------------------------------------- /lib/not-found.js: -------------------------------------------------------------------------------- 1 | var generateClass = require('./helpers/class-generator'); 2 | module.exports = generateClass("NotFoundError", { 3 | args: ['entity_name', 'inner_error'], 4 | generateMessage: function(){ 5 | return 'Not Found: "' + this.entity_name + '"'; 6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lib/not-supported.js: -------------------------------------------------------------------------------- 1 | var generateClass = require('./helpers/class-generator'); 2 | module.exports = generateClass("NotSupportedError", { 3 | args: ['message', 'inner_error'], 4 | generateMessage: function(){ 5 | return "Not Supported: " + this.message; 6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lib/notPermitted.js: -------------------------------------------------------------------------------- 1 | var generateClass = require('./helpers/class-generator'); 2 | module.exports = generateClass("NotPermittedError", { 3 | args: ['message', 'inner_error'], 4 | generateMessage: function(){ 5 | return "An attempt was made to perform an operation that is not permitted: " + this.message; 6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lib/timeout.js: -------------------------------------------------------------------------------- 1 | var generateClass = require('./helpers/class-generator'); 2 | module.exports = generateClass("TimeoutError", { 3 | args: ['time', 'inner_error'], 4 | generateMessage: function(){ 5 | if(/^\d/.test(this.time)) return "Timeout of '" + this.time + "' exceeded"; 6 | else return "Timeout exceeded: " + this.time; 7 | } 8 | }) 9 | -------------------------------------------------------------------------------- /lib/validation.js: -------------------------------------------------------------------------------- 1 | var generateClass = require('./helpers/class-generator'); 2 | var ArgumentError = require('./argument'); 3 | 4 | var ValidationError = module.exports = generateClass("ValidationError", { 5 | args: ['message', 'code', 'field'] 6 | }); 7 | 8 | ValidationError.prototype.addError = function addError(error) { 9 | this.errors = this.errors || []; 10 | this.errors.push(error); 11 | return this; 12 | } 13 | 14 | ValidationError.prototype.addErrors = function addErrors(errors) { 15 | if(!(errors instanceof Array)) throw new ArgumentError("errors"); 16 | 17 | this.errors = this.errors || []; 18 | Array.prototype.push.apply(this.errors, errors); 19 | return this; 20 | } 21 | 22 | ValidationError.prototype.generateMessage = function generateMessage(){ 23 | return this.message || "Validation failed."; 24 | } 25 | 26 | ValidationError.prototype.toJSON = function toJSON(){ 27 | var o = {}; 28 | if(this.errors) { 29 | if(this.message) o.message = this.message; 30 | o.errors = this.errors.map(function(error){ 31 | return error.toJSON(); 32 | }); 33 | } else { 34 | if(this.message) o.text = this.message; 35 | if(this.code) o.code = this.code; 36 | if(this.field) o.field = this.field; 37 | } 38 | return o; 39 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "common-errors", 3 | "author": "David Fenster ", 4 | "description": "Common error classes and utility functions", 5 | "version": "1.2.0", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/shutterstock/node-common-errors.git" 9 | }, 10 | "license": "MIT", 11 | "dependencies": {}, 12 | "devDependencies": { 13 | "bluebird": "^3.5.0", 14 | "body-parser": "^1.17.2", 15 | "common-errors": "git://github.com/shutterstock/node-common-errors.git#v1.0.2", 16 | "express": "4.x.x", 17 | "express3": "git://github.com/dfenster/express3.git", 18 | "mocha": "^3.5.0", 19 | "sinon": "^3.0.0", 20 | "supertest": "^3.0.0" 21 | }, 22 | "keywords": [ 23 | "error", 24 | "errors", 25 | "common errors", 26 | "exception", 27 | "exceptions", 28 | "validation", 29 | "standard", 30 | "argument", 31 | "null", 32 | "database", 33 | "data", 34 | "mysql", 35 | "sql", 36 | "db", 37 | "memcached", 38 | "redis", 39 | "transaction", 40 | "rollback", 41 | "connection", 42 | "status codes", 43 | "log", 44 | "crash", 45 | "error handler" 46 | ], 47 | "scripts": { 48 | "test": "mocha --recursive tests" 49 | }, 50 | "main": "index.js", 51 | "module": "common.js", 52 | "engines": { 53 | "node": ">=0.8" 54 | }, 55 | "homepage": "https://github.com/shutterstock/node-common-errors", 56 | "bugs": "https://github.com/shutterstock/node-common-errors/issues", 57 | "contributors": [ 58 | "David Fenster ", 59 | "Ben Kovacevich ", 60 | "Richard Littauer ", 61 | "Elliot Foster ", 62 | "Mark Stosberg ", 63 | "Ilya Shaisultanov ", 64 | "Mark ", 65 | "Michael Giuliana ", 66 | "skhvan1111 " 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /tests/already-in-use.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var Promise = require('bluebird'); 3 | var errors = require('..'); 4 | var name = "AlreadyInUseError"; 5 | var Err = errors[name]; 6 | 7 | describe(name, function(){ 8 | it("should work", function(){ 9 | var error = new Err("Entity", 'att1', 'att2', 'att3', 'att4'); 10 | assert.equal(error.name, name), 'Its name is correct.'; 11 | assert.equal(error.message, "The specified 'Entity' value is already in use for: att1, att2, att3, att4"); 12 | assert.ok(new RegExp(error.name + ": " + error.message + "\n(.*\n)+").test(error.stack), "Stack is good"); 13 | assert.ok(error instanceof Error, "It is an instanceof Error"); 14 | 15 | var caught_error_in_promise = false; 16 | var promise = new Promise(function(res, rej) { res(true); }).then(function(){ 17 | throw new Err("test error"); 18 | }).catch(Error, function(e){ 19 | caught_error_in_promise = true; 20 | }).finally(function(){ 21 | assert.ok(caught_error_in_promise, "caught promise error"); 22 | }); 23 | 24 | }); 25 | }); 26 | 27 | -------------------------------------------------------------------------------- /tests/argument-null.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('ArgumentNullError', { 3 | message: "arg1", 4 | message_to_assert: "Missing argument: arg1", 5 | extends: require('../lib/argument') 6 | }); -------------------------------------------------------------------------------- /tests/argument.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('ArgumentError', { 3 | message: "arg1", 4 | message_to_assert: "Invalid or missing argument supplied: arg1" 5 | }); -------------------------------------------------------------------------------- /tests/authentication-required.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('AuthenticationRequiredError', { 3 | message: "password is incorrect", 4 | message_to_assert: "An attempt was made to perform an operation without authentication: password is incorrect" 5 | }); -------------------------------------------------------------------------------- /tests/connection.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('ConnectionError'); -------------------------------------------------------------------------------- /tests/data/data.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('DataError', { 3 | full_name: 'data.DataError' 4 | }); -------------------------------------------------------------------------------- /tests/data/memcached.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('MemcachedError', { 3 | full_name: 'data.MemcachedError', 4 | extends: require('../../lib/data/data') 5 | }); -------------------------------------------------------------------------------- /tests/data/mongodb.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('MongoDBError', { 3 | full_name: 'data.MongoDBError', 4 | extends: require('../../lib/data/data') 5 | }); -------------------------------------------------------------------------------- /tests/data/redis.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('RedisError', { 3 | full_name: 'data.RedisError', 4 | extends: require('../../lib/data/data') 5 | }); -------------------------------------------------------------------------------- /tests/data/rollback.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('RollbackError', { 3 | full_name: 'data.RollbackError', 4 | extends: require('../../lib/data/data') 5 | }); -------------------------------------------------------------------------------- /tests/data/sql.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('SQLError', { 3 | full_name: 'data.SQLError', 4 | extends: require('../../lib/data/data') 5 | }); -------------------------------------------------------------------------------- /tests/data/transaction.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('TransactionError', { 3 | full_name: 'data.TransactionError', 4 | extends: require('../../lib/data/data') 5 | }); -------------------------------------------------------------------------------- /tests/error.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('Error'); -------------------------------------------------------------------------------- /tests/helpers/class-generator.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var classGenerator = require('../../lib/helpers/class-generator'); 3 | var errors = require('../../'); 4 | 5 | describe('classGenerator', function(){ 6 | it("should work", function(){ 7 | var Err = classGenerator("Err", {args: ['message', 'inner_error', 'arg1', 'arg2']}); 8 | assert.ok(Err, 'Error returned'); 9 | assert.equal(Err.length, 4, "has proper constructor arguments"); 10 | assert.equal(Err.super_, Error, "is instanceof Error"); 11 | assert.equal(Err.prototype instanceof Error, true, "is instanceof Error"); 12 | assert.equal(Err.name, "Err", "has proper name"); 13 | }); 14 | 15 | it('should be safe to use a name - strings only', function(){ 16 | var caught_error; 17 | try { 18 | var Err = classGenerator(2); 19 | } catch(e) { 20 | caught_error = e; 21 | } 22 | assert.ok(caught_error, "using a non-string is unsafe."); 23 | }); 24 | 25 | it('should be safe to use a name - word characters only', function(){ 26 | var caught_error; 27 | try { 28 | var Err = classGenerator("New Error"); 29 | } catch(e) { 30 | caught_error = e; 31 | } 32 | assert.ok(caught_error, "using a non-string is unsafe."); 33 | }); 34 | 35 | it('should be safe to use custom arguments - array only', function(){ 36 | var caught_error; 37 | try { 38 | var Err = classGenerator("NewError", {args: {}}); 39 | } catch(e) { 40 | caught_error = e; 41 | } 42 | assert.ok(caught_error, "using a non-array is unsafe."); 43 | }); 44 | 45 | it('should be safe to use custom arguments - array of strings only', function(){ 46 | var caught_error; 47 | try { 48 | var Err = classGenerator("NewError", {args: ['arg1', 2]}); 49 | } catch(e) { 50 | caught_error = e; 51 | } 52 | assert.ok(caught_error, "using a non-strings in args array is unsafe"); 53 | }); 54 | 55 | it('should be safe to use custom arguments - array of strings only', function(){ 56 | var caught_error; 57 | try { 58 | var Err = classGenerator("NewError", {args: ['arg1', 'arg 2']}); 59 | } catch(e) { 60 | caught_error = e; 61 | } 62 | assert.ok(caught_error, "using non-word characters in args array is unsafe"); 63 | }); 64 | 65 | it('should globalize errors', function(){ 66 | var GlobalError = classGenerator("SomeError"); 67 | var GlobalError2 = classGenerator("SomeError"); 68 | var LocalError = classGenerator("SomeError", {globalize: false}); 69 | 70 | assert.ok(new GlobalError() instanceof Error); 71 | assert.ok(new GlobalError2() instanceof Error); 72 | assert.ok(new LocalError() instanceof Error); 73 | assert.ok(new GlobalError() instanceof GlobalError2); 74 | assert.ok(new GlobalError2() instanceof GlobalError); 75 | assert.ok(!(new GlobalError() instanceof LocalError)); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /tests/helpers/util.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var errors = require('../../'); 3 | var EventEmitter = require('events').EventEmitter; 4 | 5 | describe('prependCurrentStack', function(){ 6 | it("should work", function(){ 7 | var event = new EventEmitter(); 8 | function foo(cb) { 9 | return bar(cb); 10 | } 11 | function bar(cb) { 12 | return cb(new Error("Inner Error")); 13 | } 14 | 15 | event.on('error', function final(err){ 16 | errors.prependCurrentStack(err); 17 | assert.ok(err, "Got an error"); 18 | var stack_split = err.stack.split('==='); 19 | assert.ok(/Error: Inner Error\n\s*at EventEmitter.final.*/.test(stack_split[0])); 20 | assert.ok(/\n\s*at bar.*\n\s*at foo/.test(stack_split[1]), "Stack is good"); 21 | }); 22 | 23 | foo(function callback(err){ 24 | process.nextTick(function emitEvent(){ 25 | event.emit('error', err); 26 | }) 27 | }) 28 | }); 29 | }); 30 | 31 | describe("global namespace", function(){ 32 | it("should maintain a global namespace for instanceof", function(){ 33 | var external_errors = require('common-errors'); 34 | var TestError = errors.helpers.generateClass('Error'); 35 | var err = new errors.Error("This is an error"); 36 | var external_err = new external_errors.Error("This is an error"); 37 | var test_err = new TestError("This is an error"); 38 | 39 | assert.notEqual(err.stack, test_err.stack, 'err and new err stacks are not equal'); 40 | assert.notEqual(err.stack, external_err.stack, 'err and external err stacks are not equal'); 41 | assert.notEqual(test_err.stack, external_err.stack, 'new err and external err stacks are not equal'); 42 | assert.equal(err.stack.replace(/:\d+:\d+/, ''), test_err.stack.replace(/:\d+:\d+/, ''), 'err and new err have almost the same stack'); 43 | assert.equal(err.stack.replace(/:\d+:\d+/, ''), external_err.stack.replace(/:\d+:\d+/, ''), 'err and external err have almost the same stack'); 44 | assert.equal(test_err.stack.replace(/:\d+:\d+/, ''), external_err.stack.replace(/:\d+:\d+/, ''), 'new err and external error have almost the same stack'); 45 | 46 | assert.ok(err instanceof errors.Error, 'err instanceof its own class'); 47 | assert.ok(err instanceof TestError, 'err instanceof a new Error'); 48 | assert.ok(err instanceof external_errors.Error, 'err instanceof an external Error'); 49 | assert.ok(test_err instanceof errors.Error, 'new err instanceof Error'); 50 | assert.ok(test_err instanceof TestError, 'new err instanceof itself'); 51 | assert.ok(test_err instanceof external_errors.Error, 'new err instanceof an external Error'); 52 | assert.ok(external_err instanceof errors.Error, 'external error instanceof Error'); 53 | assert.ok(external_err instanceof TestError, 'external error instanceof new Error'); 54 | assert.ok(external_err instanceof external_errors.Error, 'external error instanceof itself'); 55 | }); 56 | }); 57 | 58 | 59 | -------------------------------------------------------------------------------- /tests/http-status.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var HttpStatusError = require('../').HttpStatusError; 3 | 4 | describe("HttpStatusError", function(){ 5 | function performBasicAssertions(error){ 6 | assert.equal(error.name, "HttpStatusError"), 'Its name is correct.'; 7 | assert.ok(new RegExp(error.name + ": " + error.message.replace(/\)/g, '\\)').replace(/\(/g, '\\(') + "\n(.*\n)+").test(error.stack), "Stack is good"); 8 | assert.ok(error instanceof Error, Error, "It is an instanceof Error"); 9 | } 10 | 11 | it("should work with status code and message", function(){ 12 | var error = new HttpStatusError(403, "You got a 403"); 13 | assert.equal(error.message, "You got a 403"); 14 | assert.equal(error.status, 403); 15 | assert.equal(error.status_code, 403); 16 | assert.equal(error.statusCode, 403); 17 | performBasicAssertions(error); 18 | }); 19 | 20 | it("should work with status code", function(){ 21 | var error = new HttpStatusError(403); 22 | assert.equal(error.message, "(403) Forbidden"); 23 | assert.equal(error.name, "HttpStatusError"); 24 | assert.equal(error.status, 403); 25 | assert.equal(error.status_code, 403); 26 | assert.equal(error.statusCode, 403); 27 | performBasicAssertions(error); 28 | }); 29 | 30 | it("should work with status code and message without new", function(){ 31 | var error = HttpStatusError(403, "You got a 403"); 32 | assert.equal(error.message, "You got a 403"); 33 | assert.equal(error.status, 403); 34 | assert.equal(error.status_code, 403); 35 | assert.equal(error.statusCode, 403); 36 | performBasicAssertions(error); 37 | }); 38 | 39 | it("should work with status code without new", function(){ 40 | var error = HttpStatusError(403); 41 | assert.equal(error.message, "(403) Forbidden"); 42 | assert.equal(error.status, 403); 43 | assert.equal(error.status_code, 403); 44 | assert.equal(error.statusCode, 403); 45 | performBasicAssertions(error); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /tests/invalid-operation.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('InvalidOperationError', { 3 | message_to_assert: "Invalid Operation: test message" 4 | }); -------------------------------------------------------------------------------- /tests/io/directory-not-found.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('DirectoryNotFoundError', { 3 | full_name: 'io.DirectoryNotFoundError', 4 | extends: require('../../lib/io/io') 5 | }); -------------------------------------------------------------------------------- /tests/io/drive-not-found.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('DriveNotFoundError', { 3 | full_name: 'io.DriveNotFoundError', 4 | extends: require('../../lib/io/io') 5 | }); -------------------------------------------------------------------------------- /tests/io/end-of-stream.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('EndOfStreamError', { 3 | full_name: 'io.EndOfStreamError', 4 | extends: require('../../lib/io/io') 5 | }); -------------------------------------------------------------------------------- /tests/io/file-load.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('FileLoadError', { 3 | full_name: 'io.FileLoadError', 4 | extends: require('../../lib/io/io'), 5 | message_to_assert: "Unable to load file: test message" 6 | }); -------------------------------------------------------------------------------- /tests/io/file-not-found.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('FileNotFoundError', { 3 | full_name: 'io.FileNotFoundError', 4 | extends: require('../../lib/io/io'), 5 | message_to_assert: "File not found: test message" 6 | }); -------------------------------------------------------------------------------- /tests/io/io.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('IOError', { 3 | full_name: 'io.IOError' 4 | }); -------------------------------------------------------------------------------- /tests/io/socket.js: -------------------------------------------------------------------------------- 1 | var support = require('../support'); 2 | support.testError('SocketError', { 3 | full_name: 'io.SocketError', 4 | extends: require('../../lib/io/io') 5 | }); -------------------------------------------------------------------------------- /tests/log.js: -------------------------------------------------------------------------------- 1 | var errors = require('../'); 2 | var assert = require('assert'); 3 | 4 | describe("errors.log", function(){ 5 | 6 | var consoleFunctions = {}; 7 | Object.keys(console).forEach(function(key){ 8 | consoleFunctions[key] = console[key]; 9 | }); 10 | 11 | var errorLog; 12 | 13 | beforeEach(function(cb){ 14 | errorLog = []; 15 | 16 | console.error = function(message){ 17 | if(message && message.message) errorLog.push(message.message); 18 | else errorLog.push(message); 19 | }; 20 | 21 | cb(); 22 | }); 23 | 24 | afterEach(function(cb){ 25 | Object.keys(consoleFunctions).forEach(function(key){ 26 | console[key] = consoleFunctions[key]; 27 | }); 28 | cb(); 29 | }) 30 | 31 | var logAnError = function (err, message) { 32 | return errors.log(err, message) 33 | } 34 | 35 | it("should log a message", function(){ 36 | var err = new Error("This is a assert."); 37 | var err2 = logAnError(err, "Panic!"); 38 | assert.equal(errorLog.length, 1); 39 | assert.ok(/Error: Panic!/.test(errorLog[0]), 'message matches'); 40 | assert.ok(err2.isLogged); 41 | assert.ok(err2 instanceof Error, 'we have a javascript error object'); 42 | 43 | var stack = err2.stack.split("\n"); 44 | assert.ok(/logAnError/.test(stack[1]), 'we prepend the current stack trace'); 45 | var splitterIndex = stack.indexOf('===='); 46 | assert.ok(splitterIndex, 'we spit the stacks with "===="'); 47 | assert.ok(/Context\./.test(stack.slice(splitterIndex)[1]), 'we keep the old stack trace'); 48 | }); 49 | 50 | it("log string error", function(){ 51 | var err2 = logAnError("Panic!"); 52 | assert.equal(errorLog.length, 1); 53 | assert.ok(/Error: Panic!/.test(errorLog[0]), 'message matches'); 54 | assert.ok(err2.isLogged); 55 | assert.ok(err2 instanceof errors.Error, 'we have an error'); 56 | assert.ok(!/====[ ]/.test(errorLog[0]), "we don't prepend any stack traces"); 57 | }) 58 | 59 | it("log error, no message", function(){ 60 | var err = new Error("This is a assert."); 61 | var err2 = logAnError(err); 62 | assert.equal(errorLog.length, 1); 63 | assert.ok(/Error: This is a assert./.test(errorLog[0]), "message matches"); 64 | 65 | var stack = err2.stack.split("\n"); 66 | assert.ok(/logAnError/.test(stack[1]), 'we prepend the current stack trace'); 67 | var splitterIndex = stack.indexOf('===='); 68 | assert.ok(splitterIndex, 'we spit the stacks with "===="'); 69 | assert.ok(/Context\./.test(stack.slice(splitterIndex)[1]), 'we keep the old stack trace'); 70 | 71 | assert.ok(err2 instanceof Error, 'we have a javascript error object'); 72 | assert.ok(err2.isLogged); 73 | }) 74 | 75 | it("log generic error", function(){ 76 | var err = new errors.Generic("This is a assert."); 77 | var err2 = logAnError(err); 78 | assert.equal(errorLog.length, 1); 79 | assert.ok(/GenericError: This is a assert./.test(errorLog[0]), "message matches"); 80 | assert.ok(err2 instanceof errors.Generic, 'we have a generic error'); 81 | assert.ok(err2.isLogged); 82 | 83 | var stack = err2.stack.split("\n"); 84 | assert.ok(/logAnError/.test(stack[1]), 'we prepend the current stack trace'); 85 | var splitterIndex = stack.indexOf('===='); 86 | assert.ok(splitterIndex, 'we spit the stacks with "===="'); 87 | assert.ok(/Context\./.test(stack.slice(splitterIndex)[1]), 'we keep the old stack trace'); 88 | }); 89 | }) -------------------------------------------------------------------------------- /tests/middleware/errorHandler.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var sinon = require('sinon'); 3 | var supertest = require('supertest'); 4 | var express3 = require('express3'); 5 | var express4 = require('express'); 6 | var body_parser = require('body-parser'); 7 | var errors = require('../../'); 8 | var errorHandler = errors.middleware.errorHandler; 9 | 10 | var sandbox = sinon.sandbox.create(); 11 | 12 | describe("errorHandler", function(){ 13 | var err; 14 | 15 | var app = new express4(); 16 | app.use(body_parser.json()); 17 | app.all('/error', function(req, res, next){ return next(err); }); 18 | app.post('/error/:test', function(req, res, next){ return next(err); }); 19 | app.use(errorHandler); 20 | var request = new supertest(app); 21 | 22 | beforeEach(function(){ 23 | sandbox.stub(console, 'error').callsFake(function(message){ }); 24 | }); 25 | 26 | afterEach(function(){ 27 | sandbox.restore(); 28 | }) 29 | 30 | it("should handle NotPermittedError", function(done){ 31 | err = new errors.NotPermitted("don't do that"); 32 | request.get('/error').end(function(err, res){ 33 | assert.equal(res.res.statusCode, 403); 34 | assert.equal(res.text, "An attempt was made to perform an operation that is not permitted: don't do that"); 35 | assert.ok(!console.error.called, "console.error not called"); 36 | done(); 37 | }); 38 | }); 39 | 40 | it("should handle AuthenticationRequired", function(done){ 41 | err = new errors.AuthenticationRequired("gime password"); 42 | request.get('/error').end(function(err, res){ 43 | assert.equal(res.res.statusCode, 401); 44 | assert.equal(res.text, "An attempt was made to perform an operation without authentication: gime password"); 45 | assert.ok(!console.error.called, "console.error not called"); 46 | done(); 47 | }); 48 | }); 49 | 50 | it("should handle Validation", function(done){ 51 | err = new errors.Validation("bad") 52 | request.get('/error').end(function(err, res){ 53 | assert.equal(res.res.statusCode, 400); 54 | assert.equal(res.text, "bad"); 55 | assert.ok(!console.error.called, "console.error not called"); 56 | done(); 57 | }); 58 | }); 59 | 60 | it("should handle AlreadyInUse", function(done){ 61 | err = new errors.AlreadyInUse("bad", "test"); 62 | request.get('/error').end(function(err, res){ 63 | assert.equal(res.res.statusCode, 409); 64 | assert.equal(res.text, "The specified 'bad' value is already in use for: test"); 65 | assert.ok(!console.error.called, "console.error not called"); 66 | done(); 67 | }); 68 | }); 69 | 70 | it("should handle ArgumentNull", function(done){ 71 | err = new errors.ArgumentNull("test"); 72 | request.get('/error').end(function(err, res){ 73 | assert.equal(res.res.statusCode, 404); 74 | assert.equal(res.text, "Not Found: \"test\""); 75 | assert.ok(!console.error.called, "console.error not called"); 76 | done(); 77 | }); 78 | }); 79 | 80 | it("should handle ArgumentNull route param", function(done){ 81 | err = new errors.ArgumentNull("test"); 82 | request.post('/error/1').end(function(err, res){ 83 | assert.equal(res.res.statusCode, 404); 84 | assert.equal(res.text, "Not Found: \"test\""); 85 | assert.ok(!console.error.called, "console.error not called"); 86 | done(); 87 | }); 88 | }); 89 | 90 | it("should handle ArgumentNull route param in express3", function(done){ 91 | err = new errors.ArgumentNull("test"); 92 | 93 | var app = express3(); 94 | app.use(app.router) 95 | app.use(errorHandler); 96 | app.post('/error/:test', function(req, res, next){ 97 | return next(err); 98 | }); 99 | 100 | var request = new supertest(app); 101 | 102 | request.post('/error/1').end(function(err, res){ 103 | assert.equal(res.res.statusCode, 404); 104 | assert.equal(res.text, "Not Found: \"test\""); 105 | assert.ok(!console.error.called, "console.error not called"); 106 | done(); 107 | }); 108 | }); 109 | 110 | it("should handle ArgumentNull POST", function(done){ 111 | err = new errors.ArgumentNull("test"); 112 | request.post('/error').send({test:1}).end(function(err, res){ 113 | assert.equal(res.res.statusCode, 400); 114 | assert.equal(res.text, "Not Found: \"test\""); 115 | assert.ok(!console.error.called, "console.error not called"); 116 | done(); 117 | }); 118 | }); 119 | 120 | it("should handle NotFoundError", function(done){ 121 | err = new errors.NotFoundError("test"); 122 | request.get('/error').end(function(err, res){ 123 | assert.equal(res.res.statusCode, 404); 124 | assert.equal(res.text, "Not Found: \"test\""); 125 | assert.ok(!console.error.called, "console.error not called"); 126 | done(); 127 | }); 128 | }); 129 | 130 | it("should handle Error", function(done){ 131 | err = new Error("test"); 132 | request.get('/error').end(function(err, res){ 133 | assert.equal(res.res.statusCode, 500); 134 | assert.equal(res.text, "Internal Server Error"); 135 | assert.ok(console.error.called); 136 | assert.ok(/test/.test(console.error.getCall(0).args[0])); 137 | done(); 138 | }); 139 | }); 140 | 141 | it("should handle Error status", function(done){ 142 | err = new Error("test"); 143 | err.status = 544; 144 | request.get('/error').end(function(err, res){ 145 | assert.equal(res.res.statusCode, 544); 146 | assert.equal(res.text, "Internal Server Error"); 147 | assert.ok(console.error.called); 148 | assert.ok(/test/.test(console.error.getCall(0).args[0])); 149 | done(); 150 | }); 151 | }); 152 | 153 | it("should handle Error status", function(done){ 154 | err = new Error("test"); 155 | err.statusCode = 544; 156 | request.get('/error').end(function(err, res){ 157 | assert.equal(res.res.statusCode, 544); 158 | assert.equal(res.text, "Internal Server Error"); 159 | assert.ok(console.error.called); 160 | assert.ok(/test/.test(console.error.getCall(0).args[0])); 161 | done(); 162 | }); 163 | }); 164 | 165 | it("should handle Error status", function(done){ 166 | err = new Error("test"); 167 | err.status_code = 544; 168 | request.get('/error').end(function(err, res){ 169 | assert.equal(res.res.statusCode, 544); 170 | assert.equal(res.text, "Internal Server Error"); 171 | assert.ok(console.error.called); 172 | assert.ok(/test/.test(console.error.getCall(0).args[0])); 173 | done(); 174 | }); 175 | }); 176 | 177 | it("should handle HttpStatusError (deprecated)", function(done){ 178 | err = new errors.HttpStatus('custom status message', 544); 179 | request.get('/error').end(function(err, res){ 180 | assert.equal(res.res.statusCode, 544); 181 | assert.equal(res.text, "Internal Server Error"); 182 | assert.ok(console.error.called); 183 | assert.ok(/custom status message/.test(console.error.getCall(0).args[0])); 184 | done(); 185 | }); 186 | }); 187 | 188 | it("should handle HttpStatusError", function(done){ 189 | err = new errors.HttpStatus(544, 'custom status message') 190 | request.get('/error').end(function(err, res){ 191 | assert.equal(res.res.statusCode, 544); 192 | assert.equal(res.text, "Internal Server Error"); 193 | assert.ok(console.error.called); 194 | assert.ok(/custom status message/.test(console.error.getCall(0).args[0])); 195 | done(); 196 | }); 197 | }); 198 | 199 | it("should handle HttpStatusError 400", function(done){ 200 | err = new errors.HttpStatus(444, 'custom status message') 201 | request.get('/error').end(function(err, res){ 202 | assert.equal(res.res.statusCode, 444); 203 | assert.equal(res.text, "custom status message"); 204 | assert.ok(!console.error.called); 205 | done(); 206 | }); 207 | }); 208 | 209 | it("should work with express 3", function(done){ 210 | var app = express3(); 211 | app.use(app.router) 212 | app.use(errorHandler); 213 | app.get('/error', function(req, res, next){ 214 | return next(new Error("This is a test")); 215 | }); 216 | 217 | var request = new supertest(app); 218 | request.get('/error') 219 | .end(function(err, res){ 220 | assert.equal(res.res.statusCode, 500); 221 | done(); 222 | }) 223 | }) 224 | 225 | }); 226 | -------------------------------------------------------------------------------- /tests/not-found.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('NotFoundError', { 3 | message: "entity name", 4 | message_to_assert: 'Not Found: "entity name"' 5 | }); -------------------------------------------------------------------------------- /tests/not-implemented.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('NotImplementedError'); -------------------------------------------------------------------------------- /tests/not-permitted.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('NotPermittedError', { 3 | message_to_assert: "An attempt was made to perform an operation that is not permitted: test message" 4 | }); -------------------------------------------------------------------------------- /tests/not-supported.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('NotSupportedError', { 3 | message_to_assert: "Not Supported: test message" 4 | }); -------------------------------------------------------------------------------- /tests/out-of-memory.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('OutOfMemoryError'); -------------------------------------------------------------------------------- /tests/range.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('RangeError', { extends: RangeError }); -------------------------------------------------------------------------------- /tests/reference.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('ReferenceError', { extends: ReferenceError }); -------------------------------------------------------------------------------- /tests/stack-overflow.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('StackOverflowError'); -------------------------------------------------------------------------------- /tests/support/index.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var errors = require('../..'); 3 | var Promise = require('bluebird'); 4 | 5 | module.exports.testError = function testError(name, opts){ 6 | opts = opts || {}; 7 | opts.message = opts.message || "test message"; 8 | opts.message_to_assert = opts.message_to_assert || opts.message; 9 | opts.extends = opts.extends || Error; 10 | opts.full_name = opts.full_name || name; 11 | 12 | function performAssertions(Err, error) { 13 | assert.equal(error.name, name), 'Its name is correct.'; 14 | assert.equal(error.message, opts.message_to_assert); 15 | assert.ok(new RegExp(error.name + ": " + error.message + "\n(.*\n)+").test(error.stack), "Stack is good"); 16 | assert.equal(Err.super_.name, opts.extends.name, "It is an instance of" + opts.extends.name); 17 | var sampleError = new Err(error); 18 | assert.equal(sampleError.name, name, "Has the expected type " + name); 19 | assert.ok(error instanceof Error, "It is an instanceof Error"); 20 | assert.ok(error instanceof opts.extends, "It is an instanceof " + opts.extends.name); 21 | 22 | var caught_error_in_promise = false; 23 | var promise = new Promise(function(res, rej) { res(true); }).then(function(){ 24 | throw new Err("test error"); 25 | }).catch(opts.extends, function(e){ 26 | caught_error_in_promise = true; 27 | }).finally(function(){ 28 | assert.ok(caught_error_in_promise, "caught promise error"); 29 | }); 30 | } 31 | 32 | describe(name, function(){ 33 | var Err = errors; 34 | (opts.full_name).split('.').forEach(function(dir){ Err = Err[dir]; }); 35 | 36 | it("should work", function(){ 37 | assert.ok(Err, name + " exists."); 38 | var inner_error = new Error("inner error"); 39 | var error = new Err(opts.message); 40 | 41 | performAssertions(Err, error); 42 | }); 43 | 44 | it("should work without new", function(){ 45 | assert.ok(Err, name + " exists."); 46 | var inner_error = new Error("inner error"); 47 | var error = Err(opts.message); 48 | 49 | performAssertions(Err, error); 50 | }); 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /tests/syntax.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('SyntaxError', { extends: SyntaxError }); -------------------------------------------------------------------------------- /tests/timeout.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var errors = require('..'); 3 | var name = "TimeoutError"; 4 | var Err = errors[name]; 5 | 6 | describe(name, function(){ 7 | it("should work for durations", function(){ 8 | var error = new Err(100); 9 | assert.equal(error.name, name), 'Its name is correct.'; 10 | assert.equal(error.message, "Timeout of '100' exceeded"); 11 | assert.ok(new RegExp(error.name + ": " + error.message + "\n(.*\n)+").test(error.stack), "Stack is good"); 12 | assert.ok(error instanceof Error, "It is an instanceof Error"); 13 | }); 14 | 15 | it("should work", function(){ 16 | var error = new Err("query took too long"); 17 | assert.equal(error.name, name), 'Its name is correct.'; 18 | assert.equal(error.message, "Timeout exceeded: query took too long"); 19 | assert.ok(new RegExp(error.name + ": " + error.message + "\n(.*\n)+").test(error.stack), "Stack is good"); 20 | assert.ok(error instanceof Error, "It is an instanceof Error"); 21 | }); 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /tests/type.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('TypeError', { extends: TypeError }); -------------------------------------------------------------------------------- /tests/uri.js: -------------------------------------------------------------------------------- 1 | var support = require('./support'); 2 | support.testError('URIError', { extends: URIError }); -------------------------------------------------------------------------------- /tests/validation.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var errors = require('..'); 3 | var name = "ValidationError"; 4 | var Err = errors[name]; 5 | 6 | describe(name, function(){ 7 | it("should work", function(){ 8 | var error = new Err("This is a validation message.", 'VAL_CODE_1', 'name'); 9 | assert.equal(error.name, name), 'Its name is correct.'; 10 | assert.equal(error.message, "This is a validation message."); 11 | assert.equal(error.field, "name"); 12 | assert.deepEqual(error.toJSON(), {"text":"This is a validation message.","field":"name","code":"VAL_CODE_1"}); 13 | assert.ok(new RegExp(error.name + ": " + error.message + "\n(.*\n)+").test(error.stack), "Stack is good"); 14 | assert.ok(error instanceof Error, "It is an instanceof Error"); 15 | }); 16 | 17 | describe("nested errors", function(){ 18 | it("should addError", function(){ 19 | var error = new Err().addError(new Err("This is a validation message.", 'VAL_CODE_1', 'name')); 20 | assert.equal(error.name, name), 'Its name is correct.'; 21 | assert.equal(error.message, "Validation failed."); 22 | assert.deepEqual(error.toJSON(), { 23 | message: "Validation failed.", 24 | errors: [{ 25 | "text":"This is a validation message.", 26 | "field":"name", 27 | "code":"VAL_CODE_1" 28 | }] 29 | }); 30 | assert.ok(new RegExp(error.name + ": " + error.message + "\n(.*\n)+").test(error.stack), "Stack is good"); 31 | assert.ok(error instanceof Error, "It is an instanceof Error"); 32 | }); 33 | 34 | it("should addErrors", function(){ 35 | var error = new Err().addErrors([new Err("This is a validation message.", 'VAL_CODE_1', 'name')]); 36 | assert.equal(error.name, name), 'Its name is correct.'; 37 | assert.equal(error.message, "Validation failed."); 38 | assert.deepEqual(error.toJSON(), { 39 | message: "Validation failed.", 40 | errors: [{ 41 | "text":"This is a validation message.", 42 | "field":"name", 43 | "code":"VAL_CODE_1" 44 | }] 45 | }); 46 | assert.ok(new RegExp(error.name + ": " + error.message + "\n(.*\n)+").test(error.stack), "Stack is good"); 47 | assert.ok(error instanceof Error, "It is an instanceof Error"); 48 | }); 49 | 50 | it("should require an Array for addErrors", function(){ 51 | var caught_error; 52 | try { 53 | var error = new Err().addErrors(1); 54 | } catch(e) { 55 | caught_error = e; 56 | } 57 | 58 | assert.ok(caught_error instanceof errors['ArgumentError']) 59 | }); 60 | }); 61 | }); 62 | --------------------------------------------------------------------------------