123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. 'use strict';
  2. var extend = require('xtend/mutable');
  3. var assert = require('assert');
  4. var TypedError = require('./typed.js');
  5. var objectToString = Object.prototype.toString;
  6. var ERROR_TYPE = '[object Error]';
  7. var causeMessageRegex = /\{causeMessage\}/g;
  8. var origMessageRegex = /\{origMessage\}/g;
  9. module.exports = WrappedError;
  10. function WrappedError(options) {
  11. assert(options, 'WrappedError: must specify options');
  12. assert(options.type, 'WrappedError: must specify type');
  13. assert(options.message, 'WrappedError: must specify message');
  14. assert(!has(options, 'cause'),
  15. 'WrappedError: cause field is reserved');
  16. assert(!has(options, 'fullType'),
  17. 'WrappedError: fullType field is reserved');
  18. assert(!has(options, 'causeMessage'),
  19. 'WrappedError: causeMessage field is reserved');
  20. assert(!has(options, 'origMessage'),
  21. 'WrappedError: origMessage field is reserved');
  22. var createTypedError = TypedError(options);
  23. extend(createError, options);
  24. createError._name = options.name;
  25. return createError;
  26. function createError(cause, opts) {
  27. /*eslint max-statements: [2, 25]*/
  28. assert(cause, 'an error is required');
  29. assert(isError(cause),
  30. 'WrappedError: first argument must be an error');
  31. var causeMessage = cause.message;
  32. if (causeMessage.indexOf('{causeMessage}') >= 0) {
  33. // recover
  34. causeMessage = causeMessage.replace(
  35. causeMessageRegex,
  36. '$INVALID_CAUSE_MESSAGE_LITERAL'
  37. );
  38. }
  39. if (causeMessage.indexOf('{origMessage}') >= 0) {
  40. causeMessage = causeMessage.replace(
  41. origMessageRegex,
  42. '$INVALID_ORIG_MESSAGE_LITERAL'
  43. );
  44. }
  45. var nodeCause = false;
  46. var errOptions = extend({}, opts, {
  47. causeMessage: causeMessage,
  48. origMessage: causeMessage
  49. });
  50. if (has(cause, 'code') && !has(errOptions, 'code')) {
  51. errOptions.code = cause.code;
  52. }
  53. if (has(cause, 'errno') && !has(errOptions, 'errno')) {
  54. errOptions.errno = cause.errno;
  55. nodeCause = true;
  56. }
  57. if (has(cause, 'syscall') && !has(errOptions, 'syscall')) {
  58. errOptions.syscall = cause.syscall;
  59. nodeCause = true;
  60. }
  61. var causeType = cause.type;
  62. if (!causeType && nodeCause) {
  63. causeType = 'error.wrapped-io.' +
  64. (cause.syscall || 'unknown') + '.' +
  65. (cause.errno || 'unknown');
  66. } else {
  67. causeType = 'error.wrapped-unknown';
  68. }
  69. errOptions.fullType = options.type + '~!~' +
  70. (cause.fullType || cause.type || causeType);
  71. var err = createTypedError(errOptions);
  72. Object.defineProperty(err, 'cause', {
  73. value: cause,
  74. configurable: true,
  75. enumerable: false
  76. });
  77. return err;
  78. }
  79. }
  80. function has(obj, key) {
  81. return Object.prototype.hasOwnProperty.call(obj, key);
  82. }
  83. function isError(err) {
  84. return objectToString.call(err) === ERROR_TYPE;
  85. }