| 'use strict'; |
| |
| var assert = require('assert'); |
| var util = require('util'); |
| |
| var TypedError = require('./typed.js'); |
| |
| var objectToString = Object.prototype.toString; |
| var ERROR_TYPE = '[object Error]'; |
| var causeMessageRegex = /\{causeMessage\}/g; |
| var origMessageRegex = /\{origMessage\}/g; |
| var hasOwnProperty = Object.prototype.hasOwnProperty; |
| |
| var FUNCTION_FIELD_WHITELIST = Object.getOwnPropertyNames(WrappedError) |
| |
| module.exports = WrappedError; |
| |
| function WrappedError(options) { |
| assert(options, 'WrappedError: must specify options'); |
| assert(options.type, 'WrappedError: must specify type'); |
| assert(options.message, 'WrappedError: must specify message'); |
| |
| assert(!has(options, 'cause'), |
| 'WrappedError: cause field is reserved'); |
| assert(!has(options, 'fullType'), |
| 'WrappedError: fullType field is reserved'); |
| assert(!has(options, 'causeMessage'), |
| 'WrappedError: causeMessage field is reserved'); |
| assert(!has(options, 'origMessage'), |
| 'WrappedError: origMessage field is reserved'); |
| |
| var copyArgs = {} |
| extend(copyArgs, options) |
| for (var i = 0; i < FUNCTION_FIELD_WHITELIST.length; i++) { |
| delete copyArgs[FUNCTION_FIELD_WHITELIST[i]] |
| } |
| |
| var createTypedError = TypedError(options); |
| extend(createError, copyArgs); |
| createError._name = options.name; |
| |
| return createError; |
| |
| function createError(cause, opts) { |
| /*eslint max-statements: [2, 25]*/ |
| assert(cause, 'an error is required'); |
| assert(isError(cause), |
| 'WrappedError: first argument must be an error'); |
| |
| var causeMessage = cause.message; |
| if (causeMessage.indexOf('{causeMessage}') >= 0) { |
| // recover |
| causeMessage = causeMessage.replace( |
| causeMessageRegex, |
| '$INVALID_CAUSE_MESSAGE_LITERAL' |
| ); |
| } |
| if (causeMessage.indexOf('{origMessage}') >= 0) { |
| causeMessage = causeMessage.replace( |
| origMessageRegex, |
| '$INVALID_ORIG_MESSAGE_LITERAL' |
| ); |
| } |
| |
| var nodeCause = false; |
| var errOptions = {} |
| extend(errOptions, opts) |
| extend(errOptions, { |
| causeMessage: causeMessage, |
| origMessage: causeMessage |
| }); |
| |
| if (has(cause, 'code') && !has(errOptions, 'code')) { |
| errOptions.code = cause.code; |
| } |
| |
| if (has(cause, 'errno') && !has(errOptions, 'errno')) { |
| errOptions.errno = cause.errno; |
| nodeCause = true; |
| } |
| |
| if (has(cause, 'syscall') && !has(errOptions, 'syscall')) { |
| errOptions.syscall = cause.syscall; |
| nodeCause = true; |
| } |
| |
| var causeType = cause.type; |
| if (!causeType && nodeCause) { |
| causeType = 'error.wrapped-io.' + |
| (cause.syscall || 'unknown') + '.' + |
| (cause.errno || 'unknown'); |
| } else { |
| causeType = 'error.wrapped-unknown'; |
| } |
| |
| errOptions.fullType = options.type + '~!~' + |
| (cause.fullType || cause.type || causeType); |
| |
| var err = createTypedError(errOptions); |
| |
| Object.defineProperty(err, 'cause', { |
| value: cause, |
| configurable: true, |
| enumerable: false |
| }); |
| return err; |
| } |
| } |
| |
| function has(obj, key) { |
| return Object.prototype.hasOwnProperty.call(obj, key); |
| } |
| |
| function isError(err) { |
| return util.isError(err) || objectToString.call(err) === ERROR_TYPE; |
| } |
| |
| function extend(target, source) { |
| for (var key in source) { |
| if (hasOwnProperty.call(source, key)) { |
| target[key] = source[key] |
| } |
| } |
| } |