@abtnode/util 1.7.3 → 1.7.6

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
@@ -1,5 +1,6 @@
1
1
  // You must install pm2 as project dependency
2
2
  const pm2 = require('pm2'); // eslint-disable-line import/no-extraneous-dependencies
3
+ const semver = require('semver');
3
4
  const { promisify } = require('util');
4
5
 
5
6
  const api = [
@@ -20,6 +21,23 @@ const api = [
20
21
  'stop',
21
22
  ];
22
23
 
24
+ const nodeArgs = [];
25
+ // https://stackoverflow.com/questions/69692842/error-message-error0308010cdigital-envelope-routinesunsupported
26
+ if (semver.gte(process.version, '17.0.0')) {
27
+ nodeArgs.push('--openssl-legacy-provider');
28
+ }
29
+
30
+ const originStart = pm2.start.bind(pm2);
31
+ pm2.start = (opts, cb) => {
32
+ if (opts.node_args) {
33
+ opts.node_args = `${opts.node_args} ${nodeArgs.join(' ')}`.trim();
34
+ } else {
35
+ opts.node_args = nodeArgs.join(' ');
36
+ }
37
+
38
+ return originStart(opts, cb);
39
+ };
40
+
23
41
  api.forEach((x) => {
24
42
  if (typeof pm2[x] === 'function') {
25
43
  pm2[`${x}Async`] = promisify(pm2[x]).bind(pm2);
@@ -0,0 +1,17 @@
1
+ // 请求 4xx/5xx 的情况会判定为 accessible = true
2
+ // (基于 script tag 的检测方式, 会将 404 请求判定为 accessible = false)
3
+ // TODO: 存在 Mixed Content 问题
4
+ module.exports = async (url, timeout = 5000) => {
5
+ let timer;
6
+ try {
7
+ const controller = new AbortController();
8
+ timer = setTimeout(() => controller.abort(), timeout);
9
+ // eslint-disable-next-line no-undef
10
+ await fetch(url, { method: 'HEAD', mode: 'no-cors', signal: controller.signal });
11
+ return true;
12
+ } catch (e) {
13
+ return false;
14
+ } finally {
15
+ clearTimeout(timer);
16
+ }
17
+ };
@@ -0,0 +1,14 @@
1
+ const axios = require('axios');
2
+
3
+ module.exports = async (url, timeout = 5000) => {
4
+ try {
5
+ await axios.head(url, {
6
+ timeout,
7
+ // 只要有响应状态码就认为可访问 (包括 4xx/5xx)
8
+ validateStatus: null,
9
+ });
10
+ return true;
11
+ } catch (e) {
12
+ return false;
13
+ }
14
+ };
@@ -0,0 +1,64 @@
1
+ const checkURLAccessibleInBrowser = require('./check-accessible-browser');
2
+
3
+ /**
4
+ * 用于评估 url 可访问性/使用优先级 (考虑 node/browser 两种环境)
5
+ */
6
+ const evaluateURL = async (url, options = {}) => {
7
+ const { timeout = 5000, checkAccessible = checkURLAccessibleInBrowser } = options;
8
+ const { protocol, port, hostname } = new URL(url);
9
+ // 先进行计分,https优先级最高
10
+ let score = protocol === 'https:' ? 1000 : 0;
11
+ if (hostname.endsWith('.ip.abtnet.io')) {
12
+ // ip echo
13
+ score += 20;
14
+ } else {
15
+ // 带端口优先权放后
16
+ if (port) {
17
+ score -= 1;
18
+ }
19
+ if (/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.test(hostname)) {
20
+ // 纯ip地址
21
+ score += 1;
22
+ } else {
23
+ // 自定义域名
24
+ score += 200;
25
+ // 子层越多越靠后
26
+ score -= hostname.split('.').length;
27
+ }
28
+ }
29
+ // 不可访问, 减 20000 分 (这里将 4xx~5xx 也认为是可访问)
30
+ let accessible = false;
31
+ if (checkAccessible) {
32
+ accessible = await checkAccessible(url, timeout);
33
+ if (!accessible) {
34
+ score -= 20000;
35
+ }
36
+ }
37
+ return { url, score, accessible };
38
+ };
39
+
40
+ /**
41
+ * 用于评估一组 url 的可访问性/使用优先级, 返回一组按 score 从高到低排序的 urls
42
+ */
43
+ const evaluateURLs = async (urls, options = {}) => {
44
+ const results = await Promise.all(
45
+ urls.map(async (url) => {
46
+ try {
47
+ return await evaluateURL(url, options);
48
+ } catch (e) {
49
+ console.error(e);
50
+ return { url, score: Number.MIN_SAFE_INTEGER, accessible: false };
51
+ }
52
+ })
53
+ );
54
+ // 有些情况下可能不需要排序, 可以传入 sort: false
55
+ if (options.sort === false) {
56
+ return results;
57
+ }
58
+ return results.sort((a, b) => b.score - a.score);
59
+ };
60
+
61
+ module.exports = {
62
+ evaluateURL,
63
+ evaluateURLs,
64
+ };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.7.3",
6
+ "version": "1.7.6",
7
7
  "description": "ArcBlock's JavaScript utility",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -18,9 +18,9 @@
18
18
  "author": "polunzh <polunzh@gmail.com> (http://github.com/polunzh)",
19
19
  "license": "MIT",
20
20
  "dependencies": {
21
- "@ocap/mcrypto": "^1.15.3",
22
- "@ocap/util": "^1.15.3",
23
- "@ocap/wallet": "^1.15.3",
21
+ "@ocap/mcrypto": "^1.16.0",
22
+ "@ocap/util": "^1.16.0",
23
+ "@ocap/wallet": "^1.16.0",
24
24
  "axios": "^0.25.0",
25
25
  "axios-mock-adapter": "^1.20.0",
26
26
  "axon": "^2.0.3",
@@ -38,6 +38,7 @@
38
38
  "parallel-transform": "^1.2.0",
39
39
  "public-ip": "^4.0.2",
40
40
  "pump": "^3.0.0",
41
+ "semver": "^7.3.5",
41
42
  "semver-sort": "^0.0.4",
42
43
  "shelljs": "^0.8.4",
43
44
  "stream-to-promise": "^3.0.0",
@@ -50,8 +51,8 @@
50
51
  "devDependencies": {
51
52
  "detect-port": "^1.3.0",
52
53
  "express": "^4.17.1",
53
- "fs-extra": "^10.0.0",
54
+ "fs-extra": "^10.0.1",
54
55
  "jest": "^27.4.5"
55
56
  },
56
- "gitHead": "2b965b42a59a2d98642018749a6afb6e4014a698"
57
+ "gitHead": "47a9dbd6ea74419ff586336824ebb9b2fe7694aa"
57
58
  }
@@ -1,109 +0,0 @@
1
- const http = require('http');
2
- const https = require('https');
3
-
4
- // 根据url获取状态码
5
- // 更换使用XMLHttpRequest可兼容浏览器
6
- const getStatusCode = (url, timeout = 5000) =>
7
- new Promise((resolve, reject) => {
8
- const requestFunc = url.startsWith('https://') ? https : http;
9
-
10
- let timer;
11
- try {
12
- const resp = requestFunc
13
- .get(url, (res) => {
14
- res.destroy();
15
- clearTimeout(timer);
16
- resolve(res.statusCode);
17
- })
18
- .on('error', () => {
19
- clearTimeout(timer);
20
- reject();
21
- });
22
-
23
- // 超时终止响应
24
- timer = setTimeout(() => resp.destroy(new Error('access link timed out')), timeout);
25
- } catch (err) {
26
- // 报错属于完全不能访问的网页,优先级最低
27
- clearTimeout(timer);
28
- reject();
29
- }
30
- });
31
-
32
- /**
33
- * Filter an optimal address from the array
34
- * 对urls进行排序,按照以下顺序
35
- * 优先级最高:能正常访问的地址 => 有响应但不正常(404) => 异常地址(error)
36
- * 次优先级:https => 自定义域名 => 域名级别(一级>二级>...) => ip echo 地址 => http => 自定义...
37
- *
38
- * @param {Array} urls array of urls
39
- * @param {Number} timeout request max time (ms)
40
- * @returns url object
41
- */
42
- const sortPriorityUrl = async (urls, timeout = 5000) => {
43
- const urlsData = urls.map((e) => {
44
- // 先进行计分,https优先级最高
45
- const { protocol, port, hostname } = new URL(e.url);
46
-
47
- let score = protocol === 'https:' ? 1000 : 0;
48
-
49
- if (hostname.endsWith('.ip.abtnet.io')) {
50
- // ip echo
51
- score += 20;
52
- } else {
53
- // 带端口号
54
- if (port) {
55
- // 带端口优先权放后
56
- score -= 1;
57
- }
58
-
59
- if (/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.test(hostname)) {
60
- // 纯ip地址
61
- score += 1;
62
- } else {
63
- // 自定义域名
64
- score += 200;
65
-
66
- // 子层越多越靠后
67
- score -= hostname.split('.').length;
68
- }
69
- }
70
-
71
- return {
72
- score,
73
- url: e.url,
74
- };
75
- });
76
-
77
- // 不能访问的地址靠后
78
- await Promise.all(
79
- urlsData.map(
80
- (e) =>
81
- new Promise((resolve) => {
82
- getStatusCode(e.url, timeout)
83
- .then((statusCode) => {
84
- if (/^4\d{2}/.test(statusCode)) {
85
- e.score -= 10000; // 虽然4xx,起码有响应,扣分相对error少
86
- }
87
- e.status = statusCode;
88
- resolve(e);
89
- })
90
- .catch(() => {
91
- // 报错属于完全不能访问的网页,优先级最低
92
- e.score -= 20000;
93
- resolve(e);
94
- });
95
- })
96
- )
97
- );
98
-
99
- // 根据分数进行排序
100
- const sortedUrls = urlsData
101
- .sort((a, b) => b.score - a.score)
102
- .map((e) => {
103
- return { url: e.url, status: e.status };
104
- });
105
-
106
- return sortedUrls;
107
- };
108
-
109
- module.exports = sortPriorityUrl;