-
Notifications
You must be signed in to change notification settings - Fork 218
/
Copy pathindex.js
206 lines (186 loc) · 6.56 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/*
* Copyright (c) 2015-present, Vitaly Tomilov
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Removal or modification of this copyright notice is prohibited.
*/
const npm = {
path: require('path'),
util: require('util'),
patterns: require('../patterns')
};
////////////////////////////////////////////
// Simpler check for null/undefined;
function isNull(value) {
return value === null || value === undefined;
}
////////////////////////////////////////////////////////
// Verifies parameter for being a non-empty text string;
function isText(txt) {
return txt && typeof txt === 'string' && /\S/.test(txt);
}
///////////////////////////////////////////////////////////
// Approximates the environment as being for development.
//
// Proper configuration is having NODE_ENV = 'development', but this
// method only checks for 'dev' being present, and regardless of the case.
function isDev() {
const env = process.env.NODE_ENV || '';
return env.toLowerCase().indexOf('dev') !== -1;
}
/////////////////////////////////////////////
// Adds properties from source to the target,
// making them read-only and enumerable.
function addReadProperties(target, source) {
for (const p in source) {
addReadProp(target, p, source[p]);
}
}
///////////////////////////////////////////////////////
// Adds a read-only, non-deletable enumerable property.
function addReadProp(obj, name, value, hidden) {
Object.defineProperty(obj, name, {
value,
configurable: false,
enumerable: !hidden,
writable: false
});
}
//////////////////////////////////////////////////////////////
// Converts a connection string or object into its safe copy:
// if password is present, it is masked with symbol '#'.
function getSafeConnection(cn) {
const maskPassword = cs => cs.replace(/:(?![/])([^@]+)/, (_, m) => ':' + new Array(m.length + 1).join('#'));
if (typeof cn === 'object') {
const copy = Object.assign({}, cn);
if (typeof copy.password === 'string') {
copy.password = copy.password.replace(/./g, '#');
}
if (typeof copy.connectionString === 'string') {
copy.connectionString = maskPassword(copy.connectionString);
}
return copy;
}
return maskPassword(cn);
}
///////////////////////////////////////////
// Returns a space gap for console output;
function messageGap(level) {
return ' '.repeat(level * 4);
}
/////////////////////////////////////////
// Provides platform-neutral inheritance;
function inherits(child, parent) {
child.prototype.__proto__ = parent.prototype;
}
// istanbul ignore next
function getLocalStack(startIdx, maxLines) {
// from the call stack, we take up to maximum lines,
// starting with specified line index:
startIdx = startIdx || 0;
const endIdx = maxLines > 0 ? startIdx + maxLines : undefined;
return new Error().stack
.split('\n')
.filter(line => line.match(/\(.+\)/))
.slice(startIdx, endIdx)
.join('\n');
}
//////////////////////////////
// Internal error container;
function InternalError(error) {
this.error = error;
}
/////////////////////////////////////////////////////////////////
// Parses a property name, and gets its name from the object,
// if the property exists. Returns object {valid, has, target, value}:
// - valid - true/false, whether the syntax is valid
// - has - a flag that property exists; set when 'valid' = true
// - target - the target object that contains the property; set when 'has' = true
// - value - the value; set when 'has' = true
function getIfHas(obj, prop) {
const result = {valid: true};
if (prop.indexOf('.') === -1) {
result.has = prop in obj;
result.target = obj;
if (result.has) {
result.value = obj[prop];
}
} else {
const names = prop.split('.');
let missing, target;
for (let i = 0; i < names.length; i++) {
const n = names[i];
if (!n) {
result.valid = false;
return result;
}
if (!missing && hasProperty(obj, n)) {
target = obj;
obj = obj[n];
} else {
missing = true;
}
}
result.has = !missing;
if (result.has) {
result.target = target;
result.value = obj;
}
}
return result;
}
/////////////////////////////////////////////////////////////////////////
// Checks if the property exists in the object or value or its prototype;
function hasProperty(value, prop) {
return (value && typeof value === 'object' && prop in value) ||
value !== null && value !== undefined && value[prop] !== undefined;
}
////////////////////////////////////////////////////////
// Adds prototype inspection
function addInspection(type, cb) {
type.prototype[npm.util.inspect.custom] = cb;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Identifies a general connectivity error, after which no more queries can be executed.
// This is for detecting when to skip executing ROLLBACK for a failed transaction.
function isConnectivityError(err) {
const code = err && typeof err.code === 'string' && err.code;
const cls = code && code.substring(0, 2); // Error Class
// istanbul ignore next (we cannot test-cover all error cases):
return code === 'ECONNRESET' || (cls === '08' && code !== '08P01') || (cls === '57' && code !== '57014');
// Code 'ECONNRESET' - Connectivity issue handled by the driver.
// Class 08 - Connection Exception (except for 08P01, for protocol violation).
// Class 57 - Operator Intervention (except for 57014, for cancelled queries).
//
// ERROR CODES: https://www.postgresql.org/docs/9.6/static/errcodes-appendix.html
}
///////////////////////////////////////////////////////////////
// Does JSON.stringify, with support for BigInt (irreversible)
function toJson(data) {
if (data !== undefined) {
return JSON.stringify(data, (_, v) => typeof v === 'bigint' ? `${v}#bigint` : v)
.replace(/"(-?\d+)#bigint"/g, (_, a) => a);
}
}
const exp = {
toJson,
getIfHas,
addInspection,
InternalError,
getLocalStack,
isText,
isNull,
isDev,
addReadProp,
addReadProperties,
getSafeConnection,
messageGap,
inherits,
isConnectivityError
};
const mainFile = process.argv[1];
// istanbul ignore next
exp.startDir = mainFile ? npm.path.dirname(mainFile) : process.cwd();
module.exports = exp;