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