wrapped.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. 'use strict';
  2. var assert = require('assert');
  3. var util = require('util');
  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. var hasOwnProperty = Object.prototype.hasOwnProperty;
  10. var FUNCTION_FIELD_WHITELIST = Object.getOwnPropertyNames(WrappedError)
  11. module.exports = WrappedError;
  12. function WrappedError(options) {
  13. assert(options, 'WrappedError: must specify options');
  14. assert(options.type, 'WrappedError: must specify type');
  15. assert(options.message, 'WrappedError: must specify message');
  16. assert(!has(options, 'cause'),
  17. 'WrappedError: cause field is reserved');
  18. assert(!has(options, 'fullType'),
  19. 'WrappedError: fullType field is reserved');
  20. assert(!has(options, 'causeMessage'),
  21. 'WrappedError: causeMessage field is reserved');
  22. assert(!has(options, 'origMessage'),
  23. 'WrappedError: origMessage field is reserved');
  24. var copyArgs = {}
  25. extend(copyArgs, options)
  26. for (var i = 0; i < FUNCTION_FIELD_WHITELIST.length; i++) {
  27. delete copyArgs[FUNCTION_FIELD_WHITELIST[i]]
  28. }
  29. var createTypedError = TypedError(options);
  30. extend(createError, copyArgs);
  31. createError._name = options.name;
  32. return createError;
  33. function createError(cause, opts) {
  34. /*eslint max-statements: [2, 25]*/
  35. assert(cause, 'an error is required');
  36. assert(isError(cause),
  37. 'WrappedError: first argument must be an error');
  38. var causeMessage = cause.message;
  39. if (causeMessage.indexOf('{causeMessage}') >= 0) {
  40. // recover
  41. causeMessage = causeMessage.replace(
  42. causeMessageRegex,
  43. '$INVALID_CAUSE_MESSAGE_LITERAL'
  44. );
  45. }
  46. if (causeMessage.indexOf('{origMessage}') >= 0) {
  47. causeMessage = causeMessage.replace(
  48. origMessageRegex,
  49. '$INVALID_ORIG_MESSAGE_LITERAL'
  50. );
  51. }
  52. var nodeCause = false;
  53. var errOptions = {}
  54. extend(errOptions, opts)
  55. extend(errOptions, {
  56. causeMessage: causeMessage,
  57. origMessage: causeMessage
  58. });
  59. if (has(cause, 'code') && !has(errOptions, 'code')) {
  60. errOptions.code = cause.code;
  61. }
  62. if (has(cause, 'errno') && !has(errOptions, 'errno')) {
  63. errOptions.errno = cause.errno;
  64. nodeCause = true;
  65. }
  66. if (has(cause, 'syscall') && !has(errOptions, 'syscall')) {
  67. errOptions.syscall = cause.syscall;
  68. nodeCause = true;
  69. }
  70. var causeType = cause.type;
  71. if (!causeType && nodeCause) {
  72. causeType = 'error.wrapped-io.' +
  73. (cause.syscall || 'unknown') + '.' +
  74. (cause.errno || 'unknown');
  75. } else {
  76. causeType = 'error.wrapped-unknown';
  77. }
  78. errOptions.fullType = options.type + '~!~' +
  79. (cause.fullType || cause.type || causeType);
  80. var err = createTypedError(errOptions);
  81. Object.defineProperty(err, 'cause', {
  82. value: cause,
  83. configurable: true,
  84. enumerable: false
  85. });
  86. return err;
  87. }
  88. }
  89. function has(obj, key) {
  90. return Object.prototype.hasOwnProperty.call(obj, key);
  91. }
  92. function isError(err) {
  93. return util.isError(err) || objectToString.call(err) === ERROR_TYPE;
  94. }
  95. function extend(target, source) {
  96. for (var key in source) {
  97. if (hasOwnProperty.call(source, key)) {
  98. target[key] = source[key]
  99. }
  100. }
  101. }