@abtnode/util 1.15.17 → 1.16.0-beta-8ee536d7

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/lib/async-pm2.js CHANGED
@@ -20,6 +20,19 @@ const api = [
20
20
  'stop',
21
21
  ];
22
22
 
23
+ const nodeArgs = [];
24
+
25
+ const originStart = pm2.start.bind(pm2);
26
+ pm2.start = (opts, cb) => {
27
+ if (opts.node_args) {
28
+ opts.node_args = `${opts.node_args} ${nodeArgs.join(' ')}`.trim();
29
+ } else {
30
+ opts.node_args = nodeArgs.join(' ');
31
+ }
32
+
33
+ return originStart(opts, cb);
34
+ };
35
+
23
36
  api.forEach((x) => {
24
37
  if (typeof pm2[x] === 'function') {
25
38
  pm2[`${x}Async`] = promisify(pm2[x]).bind(pm2);
package/lib/axios.js CHANGED
@@ -1,7 +1,35 @@
1
- // set proxy to false to fix bug with HTTPS_PROXY environment
2
- // https://github.com/axios/axios/issues/3384
3
- const axios = require('axios');
1
+ const axios = require('axios').default;
2
+ const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent');
4
3
 
5
- axios.defaults.proxy = false;
4
+ function proxyInterceptor(config) {
5
+ /* istanbul ignore if */
6
+ if (config.socketPath != null) return config;
7
+
8
+ const { proxy } = config;
9
+
10
+ /* istanbul ignore if */
11
+ if (proxy === false) return config;
12
+
13
+ const httpProxyUrl = process.env.HTTP_PROXY || process.env.http_proxy;
14
+ const httpsProxyUrl = process.env.HTTPS_PROXY || process.env.https_proxy;
15
+
16
+ const proxyOptions = Object.assign(
17
+ { keepAlive: true, keepAliveMsecs: 1000, maxSockets: 256, maxFreeSockets: 256 },
18
+ config.proxyOptions || {}
19
+ );
20
+
21
+ if (httpProxyUrl) {
22
+ config.proxy = false;
23
+ config.httpAgent = new HttpProxyAgent({ proxy: httpProxyUrl, ...proxyOptions });
24
+ }
25
+ if (httpsProxyUrl) {
26
+ config.proxy = false;
27
+ config.httpsAgent = new HttpsProxyAgent({ proxy: httpsProxyUrl, ...proxyOptions });
28
+ }
29
+
30
+ return config;
31
+ }
32
+
33
+ axios.interceptors.request.use(proxyInterceptor);
6
34
 
7
35
  module.exports = axios;
package/lib/base32.js ADDED
@@ -0,0 +1,14 @@
1
+ const { toBuffer } = require('@ocap/util');
2
+ const { base32 } = require('multiformats/bases/base32');
3
+
4
+ const encode = (did) => {
5
+ if (!did) {
6
+ return did;
7
+ }
8
+
9
+ const buffer = toBuffer(did);
10
+
11
+ return base32.encode(buffer);
12
+ };
13
+
14
+ module.exports = { encode };
package/lib/can-pkg-rw.js CHANGED
@@ -3,7 +3,7 @@ const locateNpmGlobalByBinary = require('./locate-npm-global-by-binary');
3
3
  const { canReadAndWriteDir } = require('./fs');
4
4
 
5
5
  module.exports = (cmdName, packageName) => {
6
- const installDir = locateNpmGlobalByBinary(cmdName);
6
+ const installDir = locateNpmGlobalByBinary(cmdName, packageName);
7
7
  if (!installDir) {
8
8
  throw new Error(`${packageName} is not installed as a global package`);
9
9
  }
@@ -1,11 +1,16 @@
1
+ const toLower = require('lodash/toLower');
2
+
1
3
  const WILDCARD = '*';
2
4
 
3
5
  /**
4
6
  * check two domains match or not. e.g. *.arcblock.io matches www.arcblock.io
5
- * @param {String} domain1
6
- * @param {String} domain2
7
+ * @param {String} domainX
8
+ * @param {String} domainY
7
9
  */
8
- const checkDomainMatch = (domain1, domain2) => {
10
+ const checkDomainMatch = (domainX, domainY) => {
11
+ const domain1 = toLower(domainX);
12
+ const domain2 = toLower(domainY);
13
+
9
14
  if (!domain1 || !domain2) return false;
10
15
  if (typeof domain1 !== 'string' || typeof domain2 !== 'string') return false;
11
16
  if (domain1 === domain2) return true;
@@ -0,0 +1,128 @@
1
+ const stringify = require('json-stable-stringify');
2
+ const { toBase64, toBase58 } = require('@ocap/util');
3
+ const joinUrl = require('url-join');
4
+ const pRetry = require('p-retry');
5
+ const debug = require('debug')('@abtnode/util:did-document');
6
+ const axios = require('./axios');
7
+ const { encode: encodeBase32 } = require('./base32');
8
+
9
+ const getDID = (address) => {
10
+ if (!address || typeof address !== 'string') {
11
+ return address;
12
+ }
13
+
14
+ if (address.startsWith('did:abt:')) {
15
+ return address;
16
+ }
17
+
18
+ return `did:abt:${address}`;
19
+ };
20
+
21
+ const update = async ({ services, didRegistryUrl, wallet, alsoKnownAs = [] }) => {
22
+ debug('update did document', { didRegistryUrl });
23
+
24
+ const did = getDID(wallet.address);
25
+ const time = new Date().toISOString();
26
+
27
+ const document = {
28
+ '@context': 'https://www.w3.org/ns/did/v1',
29
+ id: did,
30
+ controller: did,
31
+ service: services,
32
+ alsoKnownAs,
33
+ verificationMethod: [
34
+ {
35
+ id: `${did}#key-1`,
36
+ type: 'Ed25519Signature',
37
+ controller: did,
38
+ publicKeyMultibase: toBase58(wallet.publicKey),
39
+ },
40
+ ],
41
+ authentication: [`${did}#key-1`],
42
+ created: time,
43
+ updated: time,
44
+ };
45
+
46
+ const proof = {
47
+ type: 'Ed25519Signature',
48
+ created: time,
49
+ verificationMethod: `${did}#key-1`,
50
+ jws: toBase64(wallet.sign(stringify(document))),
51
+ };
52
+
53
+ document.proof = proof;
54
+
55
+ return axios.post(joinUrl(didRegistryUrl, '/.well-known/did-resolver/registries'), document, { timeout: 10 * 1000 });
56
+ };
57
+
58
+ const updateWithRetry = async (...args) => pRetry(() => update(...args), { retries: 3 });
59
+
60
+ const getServerServices = ({ ips, wallet, domain }) => {
61
+ const records = ips.map((ip) => ({
62
+ type: 'A',
63
+ rr: encodeBase32(wallet.address),
64
+ value: ip,
65
+ domain,
66
+ }));
67
+
68
+ const services = [
69
+ {
70
+ id: getDID(wallet.address),
71
+ type: 'DNSRecords',
72
+ records,
73
+ },
74
+ ];
75
+
76
+ return services;
77
+ };
78
+
79
+ const getBlockletServices = ({ appPid, daemonDidDomain, domain }) => {
80
+ return [
81
+ {
82
+ id: getDID(appPid),
83
+ type: 'DNSRecords',
84
+ records: [
85
+ {
86
+ type: 'CNAME',
87
+ rr: encodeBase32(appPid),
88
+ value: daemonDidDomain,
89
+ domain,
90
+ },
91
+ ],
92
+ },
93
+ ];
94
+ };
95
+
96
+ const updateServerDocument = async ({ ips, wallet, didRegistryUrl, domain }) => {
97
+ const filteredIps = (ips || []).filter(Boolean);
98
+ if (filteredIps.length === 0) {
99
+ throw new Error('No DID Document to update');
100
+ }
101
+
102
+ const services = getServerServices({ ips: filteredIps, domain, wallet });
103
+
104
+ return updateWithRetry({ services, didRegistryUrl, wallet });
105
+ };
106
+
107
+ const updateBlockletDocument = async ({
108
+ wallet,
109
+ didRegistryUrl,
110
+ domain,
111
+ daemonDidDomain,
112
+ appPid,
113
+ alsoKnownAs = [],
114
+ }) => {
115
+ const services = getBlockletServices({ appPid, daemonDidDomain, domain });
116
+ return updateWithRetry({ services, didRegistryUrl, alsoKnownAs, wallet });
117
+ };
118
+
119
+ const disableDNS = async ({ wallet, didRegistryUrl }) => updateWithRetry({ services: [], didRegistryUrl, wallet });
120
+
121
+ module.exports = {
122
+ updateServerDocument,
123
+ updateBlockletDocument,
124
+ disableDNS,
125
+ getDID,
126
+ getServerServices,
127
+ getBlockletServices,
128
+ };
@@ -7,18 +7,56 @@ const axios = require('./axios');
7
7
  const CANCEL = '__cancel__';
8
8
 
9
9
  /**
10
- * download file
11
- * @param {string} url
12
- * @param {string} dest directory
13
- * @param {Number} timeout unit: ms
14
- * @param {CancelCtrl} cancelCtrl
10
+ *
11
+ *
12
+ * @param {Stream} stream
13
+ * @returns {Promise<string>}
15
14
  */
16
- const downloadFile = async (url, dest, { timeout = 600 * 1000, cancelCtrl } = {}) => {
15
+ async function getErrorMessageFromStream(stream) {
16
+ const str = await streamToString(stream);
17
+
18
+ try {
19
+ const json = JSON.parse(str);
20
+ return json.error;
21
+ } catch (error) {
22
+ return str;
23
+ }
24
+ }
25
+
26
+ /**
27
+ *
28
+ *
29
+ * @param {*} stream
30
+ * @return {Promise<string>}
31
+ */
32
+ async function streamToString(stream) {
33
+ const chunks = [];
34
+
35
+ for await (const chunk of stream) {
36
+ chunks.push(Buffer.from(chunk));
37
+ }
38
+
39
+ return Buffer.concat(chunks).toString('utf-8');
40
+ }
41
+
42
+ /**
43
+ *
44
+ *
45
+ * @param {*} url
46
+ * @param {*} dest directory
47
+ * @param {{
48
+ * timeout: number; // unit: ms
49
+ * cancelCtrl: any;
50
+ * }} [{ timeout = 600 * 1000, cancelCtrl, data }={}]
51
+ * @param {{}} context
52
+ * @return {*}
53
+ */
54
+ const downloadFile = async (url, dest, { timeout = 600 * 1000, cancelCtrl } = {}, context = {}) => {
17
55
  const CONNECTION_TIMEOUT = 20 * 1000;
18
56
  const source = CancelToken.source();
19
57
 
20
58
  try {
21
- const result = await tryWithTimeout(async () => {
59
+ return await tryWithTimeout(async () => {
22
60
  const timer = setTimeout(() => {
23
61
  source.cancel(`Connection timeout: ${Math.ceil(CONNECTION_TIMEOUT / 1000)}s`);
24
62
  }, CONNECTION_TIMEOUT);
@@ -39,6 +77,7 @@ const downloadFile = async (url, dest, { timeout = 600 * 1000, cancelCtrl } = {}
39
77
 
40
78
  const response = await axios({
41
79
  url,
80
+ headers: context?.headers,
42
81
  method: 'GET',
43
82
  responseType: 'stream',
44
83
  cancelToken: source.token,
@@ -61,8 +100,6 @@ const downloadFile = async (url, dest, { timeout = 600 * 1000, cancelCtrl } = {}
61
100
 
62
101
  return dest;
63
102
  }, timeout);
64
-
65
- return result;
66
103
  } catch (err) {
67
104
  if (fs.existsSync(dest)) {
68
105
  fs.unlinkSync(dest);
@@ -76,6 +113,11 @@ const downloadFile = async (url, dest, { timeout = 600 * 1000, cancelCtrl } = {}
76
113
  }
77
114
 
78
115
  source.cancel();
116
+
117
+ if (err?.response?.data) {
118
+ throw new Error(await getErrorMessageFromStream(err.response.data));
119
+ }
120
+
79
121
  throw err;
80
122
  }
81
123
  };
@@ -62,7 +62,7 @@ module.exports = async ({
62
62
  host = '127.0.0.1',
63
63
  port,
64
64
  timeout = 10 * ONE_SECOND,
65
- minConsecutiveTime = 5 * ONE_SECOND,
65
+ minConsecutiveTime = (+process.env.ENDPOINT_CONSECUTIVE_TIME || 5) * ONE_SECOND,
66
66
  }) => {
67
67
  debug('ensure endpoint healthy', { port, minConsecutiveTime });
68
68
 
@@ -0,0 +1,9 @@
1
+ const lodash = require('lodash');
2
+
3
+ /**
4
+ * Replace the back slack('\') with forward slash('/')
5
+ * @param {*} str
6
+ * @param {*} flag RegExp flag, default is 'g'
7
+ * @returns
8
+ */
9
+ module.exports = (str) => lodash.replace(str, new RegExp(lodash.escapeRegExp('\\'), 'g'), '/');
@@ -0,0 +1,28 @@
1
+ const get = require('lodash/get');
2
+
3
+ // Format context: https://github.com/graphql/express-graphql
4
+ module.exports = (ctx = {}) => {
5
+ if (ctx && ctx.user) {
6
+ delete ctx.user.avatar;
7
+ }
8
+
9
+ const safeGet = (key, _default = '') =>
10
+ (typeof ctx.get === 'function' ? ctx.get(key) : get(ctx, `headers[${key}]`)) || _default;
11
+ const port = process.env.NODE_ENV === 'production' ? safeGet('x-real-port') : '';
12
+ const result = {
13
+ ip: safeGet('x-real-ip', '127.0.0.1'),
14
+ ua: safeGet('user-agent'),
15
+ protocol: safeGet('x-real-protocol').replace(/:$/, '') || 'http',
16
+ user: ctx.user || {},
17
+ url: ctx.originalUrl,
18
+ query: ctx.query,
19
+ hostname: safeGet('x-real-hostname'),
20
+ port: Number(port) === 80 ? '' : Number(port),
21
+ nodeMode: ctx.nodeMode,
22
+ referrer: safeGet('referrer'),
23
+ };
24
+
25
+ // 警告,尽量不要在这里传递整个 headers 对象,因为可能会影响后续流程
26
+
27
+ return result;
28
+ };
@@ -0,0 +1,5 @@
1
+ const slugify = require('slugify');
2
+
3
+ const formatName = (name) => slugify(name.replace(/^[@./-]/, '').replace(/[@./_]/g, '-'));
4
+
5
+ module.exports = formatName;
@@ -1,6 +1,7 @@
1
+ const getSize = require('get-folder-size');
1
2
  const exec = require('child_process').execFile;
2
3
 
3
- module.exports = (dir) => {
4
+ const getOnUnixLike = (dir) => {
4
5
  const regex = /^(\d*)/;
5
6
 
6
7
  return new Promise((resolve, reject) => {
@@ -16,3 +17,17 @@ module.exports = (dir) => {
16
17
  });
17
18
  });
18
19
  };
20
+
21
+ const getOnWindows = (dir) => {
22
+ return new Promise((resolve, reject) => {
23
+ getSize(dir, (err, size) => {
24
+ if (err) {
25
+ return reject(err);
26
+ }
27
+
28
+ return resolve(size);
29
+ });
30
+ });
31
+ };
32
+
33
+ module.exports = process.platform === 'win32' ? getOnWindows : getOnUnixLike;
package/lib/get-ip.js CHANGED
@@ -14,23 +14,23 @@ const getExternalIp = async ({ v6 = false, timeout = 5000 } = {}) => {
14
14
  }
15
15
  };
16
16
 
17
- const getIP = async ({ includeV6 = false, timeout = 5000 } = {}) => {
17
+ const getIP = async ({ includeV6 = false, timeout = 5000, includeExternal = true } = {}) => {
18
18
  try {
19
- if (isEC2()) {
19
+ if (await isEC2()) {
20
20
  const [internal, external, internalV6, externalV6] = await Promise.all([
21
21
  getEc2Meta('local-ipv4', timeout),
22
- getEc2Meta('public-ipv4', timeout),
22
+ includeExternal ? getEc2Meta('public-ipv4', timeout) : '',
23
23
  includeV6 ? getEc2Meta('local-ipv6', timeout) : '',
24
- includeV6 ? getEc2Meta('public-ipv6', timeout) : '',
24
+ includeV6 && includeExternal ? getEc2Meta('public-ipv6', timeout) : '',
25
25
  ]);
26
26
  return { internal, external, internalV6, externalV6 };
27
27
  }
28
28
 
29
29
  const [internal, external, internalV6, externalV6] = await Promise.all([
30
30
  internalIp.v4(),
31
- getExternalIp({ timeout }),
31
+ includeExternal ? getExternalIp({ timeout }) : '',
32
32
  includeV6 ? internalIp.v6() : '',
33
- includeV6 ? getExternalIp({ v6: true, timeout }) : '',
33
+ includeV6 && includeExternal ? getExternalIp({ v6: true, timeout }) : '',
34
34
  ]);
35
35
  return { internal, external, internalV6, externalV6 };
36
36
  } catch (err) {
package/lib/is-ec2.js CHANGED
@@ -1,14 +1,15 @@
1
- const fs = require('fs');
1
+ const axios = require('./axios');
2
2
 
3
3
  // Whether we are running in an pre-baked image
4
- function isEC2() {
5
- const identifyFile = '/sys/hypervisor/uuid';
6
- if (fs.existsSync(identifyFile)) {
7
- const identity = fs.readFileSync(identifyFile).toString();
8
- return identity.toLowerCase().startsWith('ec2');
4
+ // refer: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/identify_ec2_instances.html
5
+ async function isEC2() {
6
+ try {
7
+ const url = 'http://169.254.169.254/latest/dynamic/instance-identity/document';
8
+ const { status } = await axios.get(url, { timeout: 1000 });
9
+ return status === 200;
10
+ } catch {
11
+ return false;
9
12
  }
10
-
11
- return false;
12
13
  }
13
14
 
14
15
  module.exports = isEC2;
@@ -0,0 +1,3 @@
1
+ const normalizePathPrefix = require('./normalize-path-prefix');
2
+
3
+ module.exports = (a, b) => normalizePathPrefix(a) === normalizePathPrefix(b);
@@ -1,22 +1,32 @@
1
- /* eslint-disable no-unused-vars, no-console */
2
-
3
1
  const fs = require('fs');
4
2
  const path = require('path');
5
- const shelljs = require('shelljs');
3
+ const shell = require('shelljs');
6
4
 
7
- module.exports = (binaryName) => {
5
+ module.exports = (binaryName, packageName) => {
8
6
  if (!binaryName) {
9
7
  throw new Error('binaryName is required');
10
8
  }
11
9
 
12
- const result = shelljs.which(binaryName);
10
+ let result = shell.which(binaryName);
13
11
  if (!result || !result.stdout) {
14
12
  return '';
15
13
  }
16
14
 
17
- const binPath = result.stdout;
15
+ const binPath = result.stdout.trim();
18
16
 
17
+ // Check if the package is installed via pnpm: only if we have packageName set
19
18
  let binDir = path.dirname(fs.realpathSync(binPath));
19
+ if (packageName) {
20
+ const { stdout: pnpmPath } = shell.which('pnpm') || {};
21
+ if (pnpmPath) {
22
+ result = shell.exec('pnpm bin -g', { silent: true });
23
+ const pnpmBinDir = result.stdout.trim();
24
+ if (binDir === pnpmBinDir) {
25
+ result = shell.exec(`pnpm root -g ${packageName}`, { silent: true });
26
+ return path.join(result.stdout.trim(), packageName);
27
+ }
28
+ }
29
+ }
20
30
 
21
31
  while (binDir && binDir !== path.sep && path.basename(binDir) !== 'node_modules') {
22
32
  const packageJsonPath = path.join(binDir, 'package.json');