utilitas 1989.8.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/index.js +40 -0
- package/lib/cache.js +281 -0
- package/lib/dbio.js +291 -0
- package/lib/email.js +120 -0
- package/lib/encryption.js +62 -0
- package/lib/event.js +118 -0
- package/lib/network.js +105 -0
- package/lib/sentinel.js +28 -0
- package/lib/shell.js +44 -0
- package/lib/shot.js +77 -0
- package/lib/sms.js +68 -0
- package/lib/storage.js +115 -0
- package/lib/tape.js +97 -0
- package/lib/uoid.js +38 -0
- package/lib/utilitas.js +660 -0
- package/package.json +45 -0
package/lib/email.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const log = (content) => { return utilitas.modLog(content, 'email'); };
|
|
4
|
+
|
|
5
|
+
let senderName, senderEmail, provider, engine, client;
|
|
6
|
+
|
|
7
|
+
const throwInvalidProvider = (message, status) => {
|
|
8
|
+
utilitas.throwError(message || 'Invalid email provider.', status || 500);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const init = async (options) => {
|
|
12
|
+
if (options) {
|
|
13
|
+
utilitas.assert(options.senderEmail, 'Sender email is required.', 500);
|
|
14
|
+
senderEmail = options.senderEmail;
|
|
15
|
+
senderName = options.senderName || (await utilitas.which()).name;
|
|
16
|
+
provider = utilitas.trim(options.provider, { case: 'UP' });
|
|
17
|
+
switch (provider) {
|
|
18
|
+
case 'MAILGUN':
|
|
19
|
+
engine = require('mailgun-js');
|
|
20
|
+
client = engine(options);
|
|
21
|
+
break;
|
|
22
|
+
case 'MAILJET':
|
|
23
|
+
engine = require('node-mailjet');
|
|
24
|
+
client = engine.connect(options.apiKey, options.apiSecret);
|
|
25
|
+
break;
|
|
26
|
+
default:
|
|
27
|
+
throwInvalidProvider();
|
|
28
|
+
}
|
|
29
|
+
if (!options.silent) {
|
|
30
|
+
log(`Initialized: ${senderName} <${senderEmail}> via ${provider}.`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
utilitas.assert(client, 'Email client has not been initialized.', 501);
|
|
34
|
+
return client;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const getSenderName = () => {
|
|
38
|
+
return senderName;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const rawSend = async (data) => {
|
|
42
|
+
let [payload, instance, fromName, fromEmail] = [null, await init(),
|
|
43
|
+
data.senderName || senderName, data.senderEmail || senderEmail];
|
|
44
|
+
switch (provider) {
|
|
45
|
+
case 'MAILGUN':
|
|
46
|
+
payload = Object.assign({
|
|
47
|
+
from: `${fromName} <${fromEmail}>`,
|
|
48
|
+
to: data.to,
|
|
49
|
+
subject: data.subject,
|
|
50
|
+
text: data.text,
|
|
51
|
+
}, data.html ? { html: data.html } : {});
|
|
52
|
+
return await instance.messages().send(payload);
|
|
53
|
+
case 'MAILJET':
|
|
54
|
+
payload = Object.assign({
|
|
55
|
+
'FromEmail': fromEmail,
|
|
56
|
+
'FromName': fromName,
|
|
57
|
+
'Subject': data.subject,
|
|
58
|
+
'Recipients': data.to.map((eml) => { return { Email: eml }; }),
|
|
59
|
+
'Text-part': data.text,
|
|
60
|
+
}, data.html ? { 'Html-part': data.html } : {});
|
|
61
|
+
return await instance.post('send').request(payload);
|
|
62
|
+
default:
|
|
63
|
+
throwInvalidProvider();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const send = async (email, subject, text, html, args, options) => {
|
|
68
|
+
options = options || {};
|
|
69
|
+
email = utilitas.ensureArray(email);
|
|
70
|
+
for (let i in email) {
|
|
71
|
+
email[i] = utilitas.trim(email[i]).toLowerCase();
|
|
72
|
+
utilitas.assertEmail(email[i], 'Invalid email.', 500);
|
|
73
|
+
}
|
|
74
|
+
subject = utilitas.trim(subject);
|
|
75
|
+
text = utilitas.trim(text);
|
|
76
|
+
html = utilitas.trim(html);
|
|
77
|
+
utilitas.assert(subject, 'Subject is required.', 500);
|
|
78
|
+
utilitas.assert(text, 'Text body is required.', 500);
|
|
79
|
+
for (let i in args || {}) {
|
|
80
|
+
subject = subject.replace(new RegExp(`{{${i}}}`, 'ig'), args[i]);
|
|
81
|
+
text = text.replace(new RegExp(`{{${i}}}`, 'ig'), args[i]);
|
|
82
|
+
html = html.replace(new RegExp(`{{${i}}}`, 'ig'), args[i]);
|
|
83
|
+
}
|
|
84
|
+
return await rawSend(Object.assign({
|
|
85
|
+
senderName: options.senderName, senderEmail: options.senderEmail,
|
|
86
|
+
to: utilitas.uniqueArray(email), subject, text
|
|
87
|
+
}, html ? { html } : {}));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = {
|
|
91
|
+
getSenderName,
|
|
92
|
+
init,
|
|
93
|
+
rawSend,
|
|
94
|
+
send,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const utilitas = require('./utilitas');
|
|
98
|
+
|
|
99
|
+
// (async () => {
|
|
100
|
+
// init({
|
|
101
|
+
// provider: 'MAILGUN',
|
|
102
|
+
// apiKey: '',
|
|
103
|
+
// domain: '',
|
|
104
|
+
// senderName: 'Leask Wong',
|
|
105
|
+
// senderEmail: 'i@leaskh.com'
|
|
106
|
+
// });
|
|
107
|
+
// init({
|
|
108
|
+
// provider: 'MAILJET',
|
|
109
|
+
// apiKey: '',
|
|
110
|
+
// apiSecret: '',
|
|
111
|
+
// senderName: 'Leask Wong',
|
|
112
|
+
// senderEmail: 'i@leaskh.com'
|
|
113
|
+
// });
|
|
114
|
+
// console.log(await send(
|
|
115
|
+
// 'i@leaskh.com',
|
|
116
|
+
// 'Hello, World!',
|
|
117
|
+
// 'The quick brown fox jumps over the lazy dog.',
|
|
118
|
+
// '<html>The quick brown fox jumps over the lazy dog. (HTML)</html>'
|
|
119
|
+
// ));
|
|
120
|
+
// })();
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const defaultAlgorithm = 'sha256';
|
|
4
|
+
|
|
5
|
+
const getSortedQueryString = (object) => {
|
|
6
|
+
const sorted = {};
|
|
7
|
+
Object.keys(object).sort().map(key => { sorted[key] = object[key]; });
|
|
8
|
+
return qs.stringify(sorted);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// algorithm = 'sha1', 'md5', 'sha256', 'sha512'...
|
|
12
|
+
const hash = (string, algorithm = defaultAlgorithm) => {
|
|
13
|
+
return crypto.createHash(algorithm).update(string).digest('hex');
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// algorithm = 'sha1', 'md5', 'sha256', 'sha512'...
|
|
17
|
+
const hashFile = (filename, algorithm = defaultAlgorithm) => new Promise(
|
|
18
|
+
(resolve) => {
|
|
19
|
+
const hash = crypto.createHash(algorithm);
|
|
20
|
+
fs.createReadStream(filename)
|
|
21
|
+
.on('data', data => hash.update(data))
|
|
22
|
+
.on('end', () => resolve(hash.digest('hex')));
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const sha256 = (string) => {
|
|
27
|
+
return hash(string);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const sha256File = async (filename) => {
|
|
31
|
+
return await hashFile(filename);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const random = (length) => {
|
|
35
|
+
return crypto.randomBytes(length);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const randomString = (length = 128, encoding = 'HEX') => {
|
|
39
|
+
let byteLength = Math.ceil(~~length / 2);
|
|
40
|
+
byteLength = byteLength > 0 ? byteLength : 1;
|
|
41
|
+
return random(byteLength).toString(encoding).substr(0, length);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const digestObject = (object, algorithm) => {
|
|
45
|
+
return hash(getSortedQueryString(object), algorithm);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
defaultAlgorithm,
|
|
50
|
+
digestObject,
|
|
51
|
+
getSortedQueryString,
|
|
52
|
+
hash,
|
|
53
|
+
hashFile,
|
|
54
|
+
random,
|
|
55
|
+
randomString,
|
|
56
|
+
sha256,
|
|
57
|
+
sha256File,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const crypto = require('crypto');
|
|
61
|
+
const fs = require('fs');
|
|
62
|
+
const qs = require('qs');
|
package/lib/event.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const jobs = {};
|
|
4
|
+
|
|
5
|
+
let timer = null;
|
|
6
|
+
|
|
7
|
+
const sToMs = (sec) => { return 1000 * (isNaN(sec = Number(sec)) ? 0 : sec); };
|
|
8
|
+
|
|
9
|
+
const unLock = (name) => { return jobs[name].lock = 0; };
|
|
10
|
+
|
|
11
|
+
const list = () => { return jobs; };
|
|
12
|
+
|
|
13
|
+
const log = (content, job, options) => {
|
|
14
|
+
options = Object.assign({ time: true }, options || {});
|
|
15
|
+
if (!job || !jobs[job] || !jobs[job].silent
|
|
16
|
+
|| options.force || content instanceof Error) {
|
|
17
|
+
utilitas.modLog(
|
|
18
|
+
content, utilitas.basename(__filename) + (job ? ` > ${job}` : ''),
|
|
19
|
+
options
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const tryLock = (name, now, timeout) => {
|
|
25
|
+
return (jobs[name].lock + timeout > now)
|
|
26
|
+
? jobs[name].lock
|
|
27
|
+
: !(jobs[name].lock = now);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const exec = async () => {
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
for (let i in jobs) {
|
|
33
|
+
if (jobs[i].lastRun + jobs[i].interval < now) {
|
|
34
|
+
jobs[i].lastRun = now;
|
|
35
|
+
try {
|
|
36
|
+
if (tryLock(i, now, jobs[i].timeout)) {
|
|
37
|
+
log('Locked, skipped.', i);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
log('Emit...', i);
|
|
41
|
+
await jobs[i].function();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
log(err, i);
|
|
44
|
+
}
|
|
45
|
+
log('Done.', i);
|
|
46
|
+
unLock(i);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const loop = async (func, interval, tout, delay, name, options = {}) => {
|
|
52
|
+
timer = timer || log('Initialized.') || setInterval(exec, 1000 * 1);
|
|
53
|
+
log('Scheduled.', name, { force: true });
|
|
54
|
+
await utilitas.timeout((delay = sToMs(delay)));
|
|
55
|
+
jobs[(name = name || uuidv4())] = {
|
|
56
|
+
function: func,
|
|
57
|
+
interval: sToMs(interval),
|
|
58
|
+
timeout: sToMs(tout),
|
|
59
|
+
delay: delay,
|
|
60
|
+
lastRun: 0,
|
|
61
|
+
lock: 0,
|
|
62
|
+
silent: !!options.silent,
|
|
63
|
+
end: options.end,
|
|
64
|
+
};
|
|
65
|
+
return timer;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const load = async (module, options = {}) => {
|
|
69
|
+
utilitas.assert(module && module.func, 'Event function is required.', 500);
|
|
70
|
+
return await loop(
|
|
71
|
+
module.func, module.interval, module.tout, module.delay, module.name,
|
|
72
|
+
options
|
|
73
|
+
);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const bulk = async (absDir, options) => {
|
|
77
|
+
options = options || {};
|
|
78
|
+
const pms = [];
|
|
79
|
+
log(`SERVICES: ${absDir}`);
|
|
80
|
+
(fs.readdirSync(absDir) || []).filter((file) => {
|
|
81
|
+
return /\.js$/i.test(file) && file.indexOf('.') !== 0;
|
|
82
|
+
}).forEach((file) => {
|
|
83
|
+
const filename = file.replace(/^(.*)\.js$/i, '$1');
|
|
84
|
+
const mod = require(path.join(absDir, file));
|
|
85
|
+
if (mod.run) {
|
|
86
|
+
mod.name = mod.name || filename;
|
|
87
|
+
pms.push(load(mod, options));
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
return await Promise.all(pms);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const end = async () => {
|
|
94
|
+
clearInterval(timer);
|
|
95
|
+
timer = -1;
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
for (let i in jobs) {
|
|
98
|
+
if (jobs[i].end) { try { await jobs[i].end(); } catch (e) { }; }
|
|
99
|
+
while (tryLock(i, now, jobs[i].timeout)) {
|
|
100
|
+
log('Waiting...', i); await utilitas.timeout(1000);
|
|
101
|
+
}
|
|
102
|
+
log('End.', i);
|
|
103
|
+
}
|
|
104
|
+
log('Terminated.');
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
module.exports = {
|
|
108
|
+
bulk,
|
|
109
|
+
list,
|
|
110
|
+
load,
|
|
111
|
+
loop,
|
|
112
|
+
end,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const utilitas = require('./utilitas');
|
|
116
|
+
const uuidv4 = require('uuid').v4;
|
|
117
|
+
const path = require('path');
|
|
118
|
+
const fs = require('fs');
|
package/lib/network.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const log = (content) => { return utilitas.modLog(content, 'network'); };
|
|
4
|
+
|
|
5
|
+
const ping = async (host, options = { timeout: 3, min_reply: 3 }) => {
|
|
6
|
+
await shell.assertExist('ping');
|
|
7
|
+
return await libPing.promise.probe(host, options);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const pickFastestHost = async (hosts, options = {}) => {
|
|
11
|
+
let [reqHosts, pingResp, pasResp, result]
|
|
12
|
+
= [utilitas.ensureArray(hosts), [], [], null];
|
|
13
|
+
reqHosts.map(x => {
|
|
14
|
+
try { x = new URL(x).hostname; } catch (e) { }
|
|
15
|
+
pingResp.push(ping(x));
|
|
16
|
+
});
|
|
17
|
+
pingResp = await Promise.all(pingResp);
|
|
18
|
+
pingResp.map(x => {
|
|
19
|
+
if (x.alive) { pasResp.push(x); }
|
|
20
|
+
if (options.debug) {
|
|
21
|
+
let logs = [];
|
|
22
|
+
for (let i in x) {
|
|
23
|
+
if (!['output', 'times', 'stddev'].includes(i)) {
|
|
24
|
+
logs.push(`${i}: ${x[i]}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
log(`ping > ${logs.join(', ')}`);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
if (pingResp.length && pasResp.length) {
|
|
31
|
+
pasResp.sort((x, y) => {
|
|
32
|
+
return Number(x.packetLoss) - Number(y.packetLoss)
|
|
33
|
+
|| Number(x.avg) - Number(y.avg);
|
|
34
|
+
});
|
|
35
|
+
for (let x of reqHosts) {
|
|
36
|
+
if (x.includes(pasResp[0].host)) { result = x; break; }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!result) {
|
|
40
|
+
if (options.forcePick) { result = reqHosts[0]; } else {
|
|
41
|
+
utilitas.throwError('All hosts cannot be connected.', 500);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (options.debug) { log(`picked > ${result}`); }
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const httping = async (url, options = { timeout: 3 }) => { // @todo: timeout
|
|
49
|
+
let [response, error, stTime] = [null, null, new Date()];
|
|
50
|
+
try { response = await fetch(url); } catch (e) { error = e; }
|
|
51
|
+
return {
|
|
52
|
+
url, response, error, response_time: error ? null : new Date() - stTime
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const pickFastestHttpServer = async (urls, options = {}) => {
|
|
57
|
+
urls = utilitas.ensureArray(urls);
|
|
58
|
+
const resp = await Promise.all(urls.map(u => { return httping(u); }));
|
|
59
|
+
const result = [];
|
|
60
|
+
resp.map(x => {
|
|
61
|
+
try { delete r.response; } catch (e) { }
|
|
62
|
+
let logs = [];
|
|
63
|
+
if (options.debug) {
|
|
64
|
+
for (let i in x) {
|
|
65
|
+
if (!['error', 'response'].includes(i)) {
|
|
66
|
+
logs.push(`${i}: ${x[i]}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
log(`httping > ${logs.join(', ')}`);
|
|
70
|
+
}
|
|
71
|
+
if (!x.error) { result.push(x); }
|
|
72
|
+
});
|
|
73
|
+
result.sort((x, y) => { return x.response_time - y.response_time; });
|
|
74
|
+
let pick = result.length ? result[0].url : null;
|
|
75
|
+
if (!pick) {
|
|
76
|
+
if (options.forcePick) { pick = urls[0]; } else {
|
|
77
|
+
utilitas.throwError('All hosts cannot be connected.', 500);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (options.debug) { log(`picked > ${pick}`); }
|
|
81
|
+
return pick;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const getCurrentPosition = async () => {
|
|
85
|
+
const ip = await publicIp.v4();
|
|
86
|
+
utilitas.assert(ip, 'Network is unreachable.', 500);
|
|
87
|
+
const loc = await geoIp.lookup(ip);
|
|
88
|
+
utilitas.assert(loc, 'Error detecting geolocation.', 500);
|
|
89
|
+
return Object.assign(loc, { ip });
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
module.exports = {
|
|
93
|
+
getCurrentPosition,
|
|
94
|
+
httping,
|
|
95
|
+
pickFastestHost,
|
|
96
|
+
pickFastestHttpServer,
|
|
97
|
+
ping,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const utilitas = require('./utilitas');
|
|
101
|
+
const publicIp = require('public-ip');
|
|
102
|
+
const libPing = require('ping');
|
|
103
|
+
const fetch = require('node-fetch').default;
|
|
104
|
+
const geoIp = require('fast-geoip');
|
|
105
|
+
const shell = require('./shell');
|
package/lib/sentinel.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const log = (str, opts) => { return utilitas.modLog(str, 'sentinel', opts); };
|
|
4
|
+
|
|
5
|
+
let sentry = null;
|
|
6
|
+
|
|
7
|
+
const integrations = (integrations) => {
|
|
8
|
+
return integrations.filter(x => {
|
|
9
|
+
if (x.name === 'OnUnhandledRejection') { x._options.mode = 'strict'; }
|
|
10
|
+
return x;
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const init = async (options) => {
|
|
15
|
+
if (options) {
|
|
16
|
+
sentry = require('@sentry/node');
|
|
17
|
+
sentry.init(Object.assign({ integrations }, options));
|
|
18
|
+
if (!options.silent) { log(`Initialized, dsn: ${options.dsn} .`); }
|
|
19
|
+
}
|
|
20
|
+
utilitas.assert(sentry, 'Sentry has not been initialized.', 501);
|
|
21
|
+
return sentry;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
init,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const utilitas = require('./utilitas');
|
package/lib/shell.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const log = (content) => { return utilitas.modLog(content, 'shell'); };
|
|
4
|
+
const vf = (cmd, cbf) => { log(`Can not run in browser: ${cmd}`); cbf(); };
|
|
5
|
+
|
|
6
|
+
const assertCommand = (command) => {
|
|
7
|
+
utilitas.assert(command, 'Command is required.', 500);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const exec = async (command, options = {}) => {
|
|
11
|
+
assertCommand(command);
|
|
12
|
+
const { stdout, stderr } = await pmsExec(command);
|
|
13
|
+
utilitas.assert(!stderr, stderr, 500);
|
|
14
|
+
return stdout;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const which = async (bin) => {
|
|
18
|
+
assertCommand(bin);
|
|
19
|
+
let binPath = null;
|
|
20
|
+
try { binPath = await exec(`which ${bin}`); } catch (err) { }
|
|
21
|
+
return binPath;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const exist = async (bin) => {
|
|
25
|
+
assertCommand(bin);
|
|
26
|
+
return !!await which(bin);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const assertExist = async (bin, er, code = 500) => {
|
|
30
|
+
assertCommand(bin);
|
|
31
|
+
utilitas.assert(await exist(bin), er || `Command not found: ${bin}.`, code);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
assertExist,
|
|
36
|
+
exec,
|
|
37
|
+
exist,
|
|
38
|
+
which,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const childProcess = require('child_process');
|
|
42
|
+
const utilitas = require('./utilitas');
|
|
43
|
+
const util = require('util');
|
|
44
|
+
const pmsExec = util.promisify(childProcess && childProcess?.exec || vf);
|