xshell 1.0.28 → 1.0.31
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/.eslintrc.json +175 -0
- package/file.js +1 -1
- package/i18n/index.js +1 -2
- package/i18n/rwdict.js +1 -1
- package/i18n/scanner/checker.js +4 -4
- package/i18n/scanner/parser.js +20 -22
- package/net.browser.js +22 -13
- package/net.d.ts +2 -1
- package/net.js +23 -11
- package/package.json +29 -24
- package/process.js +1 -1
- package/prototype.browser.js +3 -3
- package/prototype.js +4 -4
- package/repl.js +3 -1
- package/server.d.ts +7 -2
- package/server.js +56 -5
- package/utils.browser.d.ts +2 -1
- package/utils.browser.js +3 -1
- package/utils.d.ts +3 -2
- package/utils.js +6 -4
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./.eslintrc.schema.json",
|
|
3
|
+
|
|
4
|
+
"root": true,
|
|
5
|
+
|
|
6
|
+
"ignorePatterns": [
|
|
7
|
+
"**/*.d.ts"
|
|
8
|
+
],
|
|
9
|
+
|
|
10
|
+
"parser": "@typescript-eslint/parser",
|
|
11
|
+
"parserOptions": {
|
|
12
|
+
"ecmaVersion": "latest",
|
|
13
|
+
"sourceType": "module",
|
|
14
|
+
"project": "./tsconfig.json",
|
|
15
|
+
"ecmaFeatures": {
|
|
16
|
+
"jsx": true
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
"plugins": [
|
|
21
|
+
"@typescript-eslint",
|
|
22
|
+
"react",
|
|
23
|
+
"xlint"
|
|
24
|
+
],
|
|
25
|
+
|
|
26
|
+
"settings": {
|
|
27
|
+
"react": {
|
|
28
|
+
"version": "detect"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
"env": {
|
|
33
|
+
"node": true,
|
|
34
|
+
"browser": true
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
"rules": {
|
|
38
|
+
"xlint/fold-jsdoc-comments": "error",
|
|
39
|
+
|
|
40
|
+
// 取代 nonblock-statement-body-position
|
|
41
|
+
"xlint/nonblock-statement-body-position-with-indentation": "error",
|
|
42
|
+
|
|
43
|
+
"xlint/empty-bracket-spacing": "error",
|
|
44
|
+
|
|
45
|
+
// a + b**c
|
|
46
|
+
"xlint/space-infix-ops-except-exponentiation": "error",
|
|
47
|
+
|
|
48
|
+
"xlint/space-in-for-statement": "error",
|
|
49
|
+
|
|
50
|
+
"xlint/jsx-no-redundant-parenthesis-in-return": "error",
|
|
51
|
+
|
|
52
|
+
"xlint/keep-indent": "error",
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
"@typescript-eslint/semi": ["error", "never"],
|
|
56
|
+
"@typescript-eslint/no-extra-semi": "error",
|
|
57
|
+
"semi-style": ["error", "first"],
|
|
58
|
+
|
|
59
|
+
// 使用 ===
|
|
60
|
+
"eqeqeq": "error",
|
|
61
|
+
|
|
62
|
+
// 父类尽量返回 this 类型
|
|
63
|
+
"@typescript-eslint/prefer-return-this-type": "error",
|
|
64
|
+
|
|
65
|
+
// 尽量使用 . 访问属性而不是 []
|
|
66
|
+
"@typescript-eslint/dot-notation": "error",
|
|
67
|
+
|
|
68
|
+
// 必须 throw Error
|
|
69
|
+
"@typescript-eslint/no-throw-literal": "error",
|
|
70
|
+
|
|
71
|
+
// ------------ async
|
|
72
|
+
// 返回 Promise 的函数一定要标记为 async 函数
|
|
73
|
+
"@typescript-eslint/promise-function-async": "error",
|
|
74
|
+
|
|
75
|
+
// 不要 return await promise, 直接 return promise, 除非外面有 try catch
|
|
76
|
+
"@typescript-eslint/return-await": "error",
|
|
77
|
+
|
|
78
|
+
// ------------ 括号
|
|
79
|
+
|
|
80
|
+
// a => { } 而不是 (a) => { }
|
|
81
|
+
"arrow-parens": ["error", "as-needed", { "requireForBlockBody": false }],
|
|
82
|
+
|
|
83
|
+
// 不要多余的大括号
|
|
84
|
+
// if (true)
|
|
85
|
+
// console.log()
|
|
86
|
+
"curly": ["error", "multi"],
|
|
87
|
+
|
|
88
|
+
// 简单属性不要冗余的大括号 <Component prop='simple-value' />
|
|
89
|
+
"react/jsx-curly-brace-presence": ["error", "never"],
|
|
90
|
+
|
|
91
|
+
// ------------ 空格
|
|
92
|
+
|
|
93
|
+
// { a, b } 这样的对象,大括号里面要有空格
|
|
94
|
+
"@typescript-eslint/object-curly-spacing": ["error", "always"],
|
|
95
|
+
|
|
96
|
+
// [a, b, c]
|
|
97
|
+
"@typescript-eslint/comma-spacing": "error",
|
|
98
|
+
|
|
99
|
+
// foo()
|
|
100
|
+
"@typescript-eslint/func-call-spacing": "error",
|
|
101
|
+
|
|
102
|
+
// a => { } 中箭头左右两边空格
|
|
103
|
+
"arrow-spacing": ["error"],
|
|
104
|
+
|
|
105
|
+
// 注释双斜杠后面要有空格
|
|
106
|
+
"spaced-comment": ["error", "always", { "markers": ["/"] }],
|
|
107
|
+
|
|
108
|
+
// 函数声明中,名称后面要有空格
|
|
109
|
+
"@typescript-eslint/space-before-function-paren": "error",
|
|
110
|
+
|
|
111
|
+
// { return true } 这样的 block 大括号里面要有空格
|
|
112
|
+
"block-spacing": ["error", "always"],
|
|
113
|
+
|
|
114
|
+
// aaa: 123
|
|
115
|
+
"@typescript-eslint/key-spacing": ["error", { "beforeColon": false, "afterColon": true, "mode": "minimum" }],
|
|
116
|
+
|
|
117
|
+
// aaa: string
|
|
118
|
+
"@typescript-eslint/type-annotation-spacing": "error",
|
|
119
|
+
|
|
120
|
+
// if ()
|
|
121
|
+
"@typescript-eslint/keyword-spacing": ["error", { "before": true, "after": true }],
|
|
122
|
+
|
|
123
|
+
// if (1) { }
|
|
124
|
+
"@typescript-eslint/space-before-blocks": "error",
|
|
125
|
+
|
|
126
|
+
// case 1: ...
|
|
127
|
+
"switch-colon-spacing": "error",
|
|
128
|
+
|
|
129
|
+
// <Hello name={firstname} />
|
|
130
|
+
"react/jsx-equals-spacing": ["error", "never"],
|
|
131
|
+
|
|
132
|
+
// 不允许使用 tab
|
|
133
|
+
"no-tabs": "error",
|
|
134
|
+
|
|
135
|
+
// 使用 \n 换行
|
|
136
|
+
"linebreak-style": ["error", "unix"],
|
|
137
|
+
|
|
138
|
+
// 文件以 \n 结尾
|
|
139
|
+
"eol-last": ["error", "always"],
|
|
140
|
+
|
|
141
|
+
// ------------ 引号
|
|
142
|
+
|
|
143
|
+
// 用单引号
|
|
144
|
+
"jsx-quotes": ["error", "prefer-single"],
|
|
145
|
+
|
|
146
|
+
// 用单引号
|
|
147
|
+
"quotes": ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": false }],
|
|
148
|
+
|
|
149
|
+
// 不要冗余的引号包裹属性
|
|
150
|
+
"quote-props": ["error", "as-needed", { "keywords": false, "unnecessary": true }],
|
|
151
|
+
|
|
152
|
+
// ------------ 其它
|
|
153
|
+
// boolean 属性不要冗余的 ={true} <Component boolprop />
|
|
154
|
+
"react/jsx-boolean-value": ["error", "never"],
|
|
155
|
+
|
|
156
|
+
// 没有 children 的 Component 写成闭合标签 <Component />
|
|
157
|
+
"react/self-closing-comp": "error",
|
|
158
|
+
|
|
159
|
+
// 单行类型声明用 `,` 分割,多行类型声明结尾不要加分号
|
|
160
|
+
"@typescript-eslint/member-delimiter-style": ["error", {
|
|
161
|
+
"multiline": {
|
|
162
|
+
"delimiter": "none",
|
|
163
|
+
"requireLast": false
|
|
164
|
+
},
|
|
165
|
+
"singleline": {
|
|
166
|
+
"delimiter": "comma",
|
|
167
|
+
"requireLast": false
|
|
168
|
+
}
|
|
169
|
+
}],
|
|
170
|
+
|
|
171
|
+
"@typescript-eslint/prefer-includes": "error",
|
|
172
|
+
|
|
173
|
+
"@typescript-eslint/prefer-regexp-exec": "error"
|
|
174
|
+
}
|
|
175
|
+
}
|
package/file.js
CHANGED
|
@@ -134,7 +134,7 @@ export async function flist(fpd, options = {}) {
|
|
|
134
134
|
:
|
|
135
135
|
fp))).flat();
|
|
136
136
|
else if (stats)
|
|
137
|
-
return Promise.all(fps.map(fp => fstat(absolute ? fp : fpd + fp)));
|
|
137
|
+
return Promise.all(fps.map(async (fp) => fstat(absolute ? fp : fpd + fp)));
|
|
138
138
|
else
|
|
139
139
|
return fps;
|
|
140
140
|
}
|
package/i18n/index.js
CHANGED
|
@@ -6,7 +6,7 @@ export const LANGUAGES = ['zh', 'en', 'ja', 'ko'];
|
|
|
6
6
|
提供翻译文本功能,自动解析当前语言
|
|
7
7
|
@see https://github.com/ShenHongFei/xshell/tree/master/i18n
|
|
8
8
|
*/
|
|
9
|
-
class I18N {
|
|
9
|
+
export class I18N {
|
|
10
10
|
static LANGUAGE_REGEXP = /^(zh|en|ja|jp|ko)$/;
|
|
11
11
|
/** (ISO 639-1 标准语言代码) 可能取 zh, en, ja, ko */
|
|
12
12
|
language;
|
|
@@ -116,5 +116,4 @@ class I18N {
|
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
-
export { I18N };
|
|
120
119
|
//# sourceMappingURL=index.js.map
|
package/i18n/rwdict.js
CHANGED
|
@@ -62,7 +62,7 @@ export class RWDict extends Dict {
|
|
|
62
62
|
console.error(`${`已存在 ${id} 词条:`.red} ${JSON.stringify(item)}`);
|
|
63
63
|
console.error(`${'M? '.yellow}${_translation.replace(/\n/g, '\\n')} → ${translation.replace(/\n/g, '\\n')}`);
|
|
64
64
|
if (!dryrun)
|
|
65
|
-
console.error(
|
|
65
|
+
console.error('如要更新翻译请设置 { overwrite: true },否则使用 i18n.t(\'text\', { context: \'xxx\' }) 标记文本以区分。\n');
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
68
|
else {
|
package/i18n/scanner/checker.js
CHANGED
|
@@ -5,14 +5,14 @@ let Trans = 0;
|
|
|
5
5
|
function is_t(node) {
|
|
6
6
|
if (types.isCallExpression(node)) {
|
|
7
7
|
// t('chtext')
|
|
8
|
-
if (types.isIdentifier(node.callee) && node.callee.name ===
|
|
8
|
+
if (types.isIdentifier(node.callee) && node.callee.name === 't')
|
|
9
9
|
return true;
|
|
10
10
|
// i18n.t('chtext') | i18n.__('chtext')
|
|
11
11
|
if (types.isMemberExpression(node.callee) &&
|
|
12
12
|
types.isIdentifier(node.callee.object) &&
|
|
13
13
|
types.isIdentifier(node.callee.property) &&
|
|
14
|
-
node.callee.object.name ===
|
|
15
|
-
(node.callee.property.name ===
|
|
14
|
+
node.callee.object.name === 'i18n' &&
|
|
15
|
+
(node.callee.property.name === 't' || node.callee.property.name === '__'))
|
|
16
16
|
return true;
|
|
17
17
|
}
|
|
18
18
|
return false;
|
|
@@ -22,7 +22,7 @@ function is_trans(node) {
|
|
|
22
22
|
return (types.isJSXElement(node) &&
|
|
23
23
|
types.isJSXOpeningElement(node.openingElement) &&
|
|
24
24
|
types.isJSXIdentifier(node.openingElement.name) &&
|
|
25
|
-
node.openingElement.name.name ===
|
|
25
|
+
node.openingElement.name.name === 'Trans');
|
|
26
26
|
}
|
|
27
27
|
const has_unmarked_chinese_characters = (str) => !t && !Trans && /[\u4e00-\u9fa5]/.test(str);
|
|
28
28
|
export function Checker({ filepath }) {
|
package/i18n/scanner/parser.js
CHANGED
|
@@ -28,7 +28,7 @@ export function mix_parse_trans_from_string_by_babel(parser) {
|
|
|
28
28
|
return;
|
|
29
29
|
const getLiteralValue = literal => {
|
|
30
30
|
if (t.isTemplateLiteral(literal))
|
|
31
|
-
return literal.quasis.map(element => element.value.cooked).join(
|
|
31
|
+
return literal.quasis.map(element => element.value.cooked).join('');
|
|
32
32
|
return literal.value;
|
|
33
33
|
};
|
|
34
34
|
const attr = castArray(node.openingElement.attributes).reduce((acc, attribute) => {
|
|
@@ -53,26 +53,24 @@ export function mix_parse_trans_from_string_by_babel(parser) {
|
|
|
53
53
|
if (t.isLiteral(property.value))
|
|
54
54
|
obj[property.key.name] = getLiteralValue(property.value);
|
|
55
55
|
else // Unable to get value of the property
|
|
56
|
-
obj[property.key.name] =
|
|
56
|
+
obj[property.key.name] = '';
|
|
57
57
|
return obj;
|
|
58
58
|
}, {});
|
|
59
|
-
/**
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
* ```
|
|
66
|
-
*/
|
|
59
|
+
/** 防止 count 被忽略,如
|
|
60
|
+
```jsx
|
|
61
|
+
<Trans count={arr.length}>
|
|
62
|
+
一二三{{ count: arr.length }}
|
|
63
|
+
</Trans>
|
|
64
|
+
``` */
|
|
67
65
|
}
|
|
68
|
-
else if (name ===
|
|
66
|
+
else if (name === 'count')
|
|
69
67
|
acc[name] = 0;
|
|
70
68
|
}
|
|
71
69
|
return acc;
|
|
72
70
|
}, {});
|
|
73
71
|
const transKey = trim(attr[i18nKey]);
|
|
74
|
-
const defaultsString = attr[defaultsKey] ||
|
|
75
|
-
if (typeof defaultsString !==
|
|
72
|
+
const defaultsString = attr[defaultsKey] || '';
|
|
73
|
+
if (typeof defaultsString !== 'string')
|
|
76
74
|
this.log(`defaults value must be a static string, saw ${defaultsString.yellow}`);
|
|
77
75
|
// https://www.i18next.com/translation-function/essentials#overview-options
|
|
78
76
|
const tOptions = attr.tOptions;
|
|
@@ -81,10 +79,10 @@ export function mix_parse_trans_from_string_by_babel(parser) {
|
|
|
81
79
|
defaultValue: defaultsString || nodes_to_string(node.children, filepath, on_error),
|
|
82
80
|
fallbackKey,
|
|
83
81
|
};
|
|
84
|
-
if (Object.prototype.hasOwnProperty.call(attr,
|
|
82
|
+
if (Object.prototype.hasOwnProperty.call(attr, 'count'))
|
|
85
83
|
options.count = Number(attr.count) || 0;
|
|
86
|
-
if (Object.prototype.hasOwnProperty.call(attr,
|
|
87
|
-
if (typeof options.ns !==
|
|
84
|
+
if (Object.prototype.hasOwnProperty.call(attr, 'ns')) {
|
|
85
|
+
if (typeof options.ns !== 'string')
|
|
88
86
|
this.log(`The ns attribute must be a string, saw ${attr.ns?.yellow}`);
|
|
89
87
|
options.ns = attr.ns;
|
|
90
88
|
}
|
|
@@ -103,11 +101,11 @@ export function mix_parse_trans_from_string_by_babel(parser) {
|
|
|
103
101
|
on_error(() => {
|
|
104
102
|
console.error('');
|
|
105
103
|
const { line, column } = (err && err.loc) || { line: 1, column: 1 };
|
|
106
|
-
console.error([filepath, line, column].join(
|
|
104
|
+
console.error([filepath, line, column].join(':').yellow);
|
|
107
105
|
console.error(`Unable to parse ${component?.blue} component.\n`.red);
|
|
108
106
|
if (!filepath)
|
|
109
107
|
console.error(String(code).red);
|
|
110
|
-
console.error((
|
|
108
|
+
console.error((' ' + err.message).red);
|
|
111
109
|
});
|
|
112
110
|
}
|
|
113
111
|
return this;
|
|
@@ -119,9 +117,9 @@ function nodes_to_string(nodes, filepath, onError) {
|
|
|
119
117
|
nodes.forEach((node, i) => {
|
|
120
118
|
if (t.isJSXText(node) || t.isStringLiteral(node)) {
|
|
121
119
|
const value = node.value
|
|
122
|
-
.replace(/^[\r\n]+\s*/g,
|
|
123
|
-
.replace(/[\r\n]+\s*$/g,
|
|
124
|
-
.replace(/[\r\n]+\s*/g,
|
|
120
|
+
.replace(/^[\r\n]+\s*/g, '') // remove leading spaces containing a leading newline character
|
|
121
|
+
.replace(/[\r\n]+\s*$/g, '') // remove trailing spaces containing a leading newline character
|
|
122
|
+
.replace(/[\r\n]+\s*/g, ' '); // replace spaces containing a leading newline character with a single space character
|
|
125
123
|
if (!value)
|
|
126
124
|
return;
|
|
127
125
|
memo += value;
|
|
@@ -139,7 +137,7 @@ function nodes_to_string(nodes, filepath, onError) {
|
|
|
139
137
|
onError(() => {
|
|
140
138
|
const { line, column } = (node.expression && node.expression.loc.start) || { line: 1, column: 1 };
|
|
141
139
|
console.error('');
|
|
142
|
-
console.error([filepath, line, column].join(
|
|
140
|
+
console.error([filepath, line, column].join(':').yellow);
|
|
143
141
|
console.error('Unsupported JSX expression. Only static values or {{interpolation}} blocks are supported.'.red);
|
|
144
142
|
});
|
|
145
143
|
}
|
package/net.browser.js
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { t } from './i18n/instance.js';
|
|
2
2
|
import './prototype.browser.js'; // to_time_str()
|
|
3
3
|
import { assert, concat, genid, delay, Lock } from './utils.browser.js';
|
|
4
|
+
// ------------------------------------ fetch, request
|
|
5
|
+
const drop_request_headers = new Set([
|
|
6
|
+
// : 开头的 key
|
|
7
|
+
// sec-*
|
|
8
|
+
'accept-charset',
|
|
9
|
+
'connection',
|
|
10
|
+
'content-length',
|
|
11
|
+
'keep-alive',
|
|
12
|
+
'trailer',
|
|
13
|
+
'transfer-encoding',
|
|
14
|
+
'upgrade',
|
|
15
|
+
]);
|
|
4
16
|
async function fetch_retry(url, options, timeout, retries = 0, count = 0) {
|
|
5
17
|
try {
|
|
6
18
|
options.signal = AbortSignal.timeout(timeout);
|
|
@@ -40,16 +52,16 @@ export async function request(url, { method, queries, headers: _headers, body, t
|
|
|
40
52
|
if (_headers)
|
|
41
53
|
if (_headers instanceof Headers)
|
|
42
54
|
// @ts-ignore: ts 类型不支持,实际上已经有了
|
|
43
|
-
for (const [key, value] of _headers)
|
|
44
|
-
|
|
55
|
+
for (const [key, value] of _headers) {
|
|
56
|
+
if (!key.startsWith(':') && !key.startsWith('sec-') && !drop_request_headers.has(key))
|
|
57
|
+
headers.set(key, value);
|
|
58
|
+
}
|
|
45
59
|
else
|
|
46
|
-
for (const key in _headers)
|
|
47
|
-
|
|
48
|
-
if (!value.startsWith(':')) { // 可能在 http/2 的 response 中会有这样开头的保留 headers, 在透传时忽略比较好
|
|
60
|
+
for (const key in _headers)
|
|
61
|
+
if (!key.startsWith(':') && !key.startsWith('sec-') && !drop_request_headers.has(key)) { // 可能在 http/2 的 response 中会有这样开头的保留 headers, 在透传时忽略比较好
|
|
49
62
|
assert(key === key.toLowerCase(), t('传入 request 的 headers 参数中 key 应该都是小写的,实际为 {{key}}', { key }));
|
|
50
|
-
headers.set(key,
|
|
63
|
+
headers.set(key, _headers[key]);
|
|
51
64
|
}
|
|
52
|
-
}
|
|
53
65
|
let options = {
|
|
54
66
|
...method ? { method } : {},
|
|
55
67
|
keepalive: true,
|
|
@@ -188,12 +200,11 @@ export async function connect_websocket(url, { protocols, on_message, on_error,
|
|
|
188
200
|
console.log(`${websocket.url} ${t('已正常关闭')}`);
|
|
189
201
|
else { // 异常关闭,认为发生了错误,进行错误处理
|
|
190
202
|
const error = new WebSocketConnectionError(websocket, event, `${t('连接被关闭')}, code: ${event.code}${event.reason ? `, ${t('原因')}: ${event.reason}` : ''}`);
|
|
191
|
-
if (settled)
|
|
203
|
+
if (settled)
|
|
192
204
|
if (on_error)
|
|
193
205
|
on_error(error, websocket);
|
|
194
206
|
else // 既然用户不传 on_error, 就当 unhandled error 抛出来
|
|
195
207
|
throw error;
|
|
196
|
-
}
|
|
197
208
|
else {
|
|
198
209
|
settled = true;
|
|
199
210
|
reject(error);
|
|
@@ -205,12 +216,11 @@ export async function connect_websocket(url, { protocols, on_message, on_error,
|
|
|
205
216
|
// https://blog.insiderattack.net/promises-next-ticks-and-immediates-nodejs-event-loop-part-3-9226cbe7a6aa
|
|
206
217
|
// close 的错误信息比较多,这里延后触发 error 事件,放到微任务队列之后的 timers 队列中
|
|
207
218
|
setTimeout(() => {
|
|
208
|
-
if (settled)
|
|
219
|
+
if (settled)
|
|
209
220
|
if (on_error)
|
|
210
221
|
on_error(error, websocket);
|
|
211
222
|
else // 既然用户不传 on_error, 就当 unhandled error 抛出来
|
|
212
223
|
throw error;
|
|
213
|
-
}
|
|
214
224
|
else {
|
|
215
225
|
settled = true;
|
|
216
226
|
reject(error);
|
|
@@ -319,7 +329,7 @@ export class Remote {
|
|
|
319
329
|
/** 幂等,保证 websocket 已连接,否则抛出异常,不需要手动调用,在其它方法中已默认自动重连
|
|
320
330
|
作为接收方需要传入使用的 websocket 连接,确保这个这个连接的状态 */
|
|
321
331
|
async connect(websocket) {
|
|
322
|
-
if (this.initiator)
|
|
332
|
+
if (this.initiator)
|
|
323
333
|
if (this.lwebsocket.resource?.readyState === WebSocket.OPEN)
|
|
324
334
|
return;
|
|
325
335
|
else if (!this.url)
|
|
@@ -342,7 +352,6 @@ export class Remote {
|
|
|
342
352
|
on_error: this.on_error.bind(this)
|
|
343
353
|
});
|
|
344
354
|
});
|
|
345
|
-
}
|
|
346
355
|
else if (websocket.readyState === WebSocket.OPEN)
|
|
347
356
|
return;
|
|
348
357
|
else
|
package/net.d.ts
CHANGED
|
@@ -14,10 +14,11 @@ export declare enum MyProxy {
|
|
|
14
14
|
socks5 = "http://localhost:10080",
|
|
15
15
|
whistle = "http://localhost:8899"
|
|
16
16
|
}
|
|
17
|
+
export { Headers };
|
|
17
18
|
export declare const cookies: {
|
|
18
19
|
store: MemoryCookieStore;
|
|
19
20
|
jar: CookieJar;
|
|
20
|
-
get(domain_or_url: string, str?: boolean): Cookie[] | Promise<
|
|
21
|
+
get(domain_or_url: string, str?: boolean): Cookie[] | Promise<Cookie[] | string>;
|
|
21
22
|
};
|
|
22
23
|
export { Cookie };
|
|
23
24
|
export interface BasicAuth {
|
package/net.js
CHANGED
|
@@ -9,7 +9,8 @@ export var MyProxy;
|
|
|
9
9
|
(function (MyProxy) {
|
|
10
10
|
MyProxy["socks5"] = "http://localhost:10080";
|
|
11
11
|
MyProxy["whistle"] = "http://localhost:8899";
|
|
12
|
-
})(MyProxy
|
|
12
|
+
})(MyProxy || (MyProxy = {}));
|
|
13
|
+
export { Headers };
|
|
13
14
|
// ------------------------------------ fetch, request
|
|
14
15
|
let cookie_store = new MemoryCookieStore();
|
|
15
16
|
export const cookies = {
|
|
@@ -31,6 +32,18 @@ export const cookies = {
|
|
|
31
32
|
},
|
|
32
33
|
};
|
|
33
34
|
export { Cookie };
|
|
35
|
+
/** 对于 request() 函数来说无意义的 headers,会自动过滤掉 */
|
|
36
|
+
const drop_request_headers = new Set([
|
|
37
|
+
// : 开头的 key
|
|
38
|
+
// sec-*
|
|
39
|
+
'accept-charset',
|
|
40
|
+
'connection',
|
|
41
|
+
'content-length',
|
|
42
|
+
'keep-alive',
|
|
43
|
+
'trailer',
|
|
44
|
+
'transfer-encoding',
|
|
45
|
+
'upgrade',
|
|
46
|
+
]);
|
|
34
47
|
let proxy_agents = {};
|
|
35
48
|
const fetch_cookie = make_fetch_cookie(fetch, cookies.jar, false);
|
|
36
49
|
async function fetch_retry(url, options, timeout, retries = 0, count = 0) {
|
|
@@ -66,7 +79,7 @@ export async function request(url, { method, queries, headers: _headers, body, t
|
|
|
66
79
|
// --- headers, http/2 开始都用小写的 headers
|
|
67
80
|
let headers = new Headers({
|
|
68
81
|
'accept-language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,ja-JP;q=0.6,ja;q=0.5',
|
|
69
|
-
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
82
|
+
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
|
|
70
83
|
});
|
|
71
84
|
if (body !== undefined)
|
|
72
85
|
headers.set('content-type', type);
|
|
@@ -78,16 +91,16 @@ export async function request(url, { method, queries, headers: _headers, body, t
|
|
|
78
91
|
.join('; '));
|
|
79
92
|
if (_headers)
|
|
80
93
|
if (_headers instanceof Headers)
|
|
81
|
-
for (const [key, value] of _headers)
|
|
82
|
-
|
|
94
|
+
for (const [key, value] of _headers) {
|
|
95
|
+
if (!key.startsWith(':') && !key.startsWith('sec-') && !drop_request_headers.has(key))
|
|
96
|
+
headers.set(key, value);
|
|
97
|
+
}
|
|
83
98
|
else
|
|
84
|
-
for (const key in _headers)
|
|
85
|
-
|
|
86
|
-
if (!value.startsWith(':')) { // 可能在 http/2 的 response 中会有这样开头的保留 headers, 在透传时忽略比较好
|
|
99
|
+
for (const key in _headers)
|
|
100
|
+
if (!key.startsWith(':') && !key.startsWith('sec-') && !drop_request_headers.has(key)) { // 可能在 http/2 的 response 中会有这样开头的保留 headers, 在透传时忽略比较好
|
|
87
101
|
assert(key === key.toLowerCase(), t('传入 request 的 headers 参数中 key 应该都是小写的,实际为 {{key}}', { key }));
|
|
88
|
-
headers.set(key,
|
|
102
|
+
headers.set(key, _headers[key]);
|
|
89
103
|
}
|
|
90
|
-
}
|
|
91
104
|
let options = {
|
|
92
105
|
...method ? { method } : {},
|
|
93
106
|
dispatcher: (() => {
|
|
@@ -359,12 +372,11 @@ on_message, on_error, on_close }) {
|
|
|
359
372
|
});
|
|
360
373
|
websocket.addEventListener('error', event => {
|
|
361
374
|
const error = new WebSocketConnectionError(websocket, event, event.error?.message);
|
|
362
|
-
if (settled)
|
|
375
|
+
if (settled)
|
|
363
376
|
if (on_error)
|
|
364
377
|
on_error(error, websocket);
|
|
365
378
|
else // 既然用户不传 on_error, 就当 unhandled error 抛出来
|
|
366
379
|
throw error;
|
|
367
|
-
}
|
|
368
380
|
else {
|
|
369
381
|
settled = true;
|
|
370
382
|
reject(error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xshell",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.31",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"interactive programming"
|
|
17
17
|
],
|
|
18
18
|
"engines": {
|
|
19
|
-
"node": ">=
|
|
20
|
-
"vscode": ">=1.
|
|
19
|
+
"node": ">=20.3.0",
|
|
20
|
+
"vscode": ">=1.79.0"
|
|
21
21
|
},
|
|
22
22
|
"author": "ShenHongFei <shen.hongfei@outlook.com> (https://github.com/ShenHongFei)",
|
|
23
23
|
"publisher": "ShenHongFei",
|
|
@@ -45,24 +45,24 @@
|
|
|
45
45
|
]
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@babel/core": "^7.
|
|
49
|
-
"@babel/parser": "^7.
|
|
50
|
-
"@babel/traverse": "^7.
|
|
48
|
+
"@babel/core": "^7.22.6",
|
|
49
|
+
"@babel/parser": "^7.22.6",
|
|
50
|
+
"@babel/traverse": "^7.22.6",
|
|
51
51
|
"@koa/cors": "^4.0.0",
|
|
52
|
-
"@types/ws": "^8.5.
|
|
52
|
+
"@types/ws": "^8.5.5",
|
|
53
53
|
"byte-size": "^8.1.1",
|
|
54
|
-
"chalk": "^5.
|
|
55
|
-
"chardet": "^1.
|
|
54
|
+
"chalk": "^5.3.0",
|
|
55
|
+
"chardet": "^1.6.0",
|
|
56
56
|
"cli-table3": "^0.6.3",
|
|
57
57
|
"cli-truncate": "^3.1.0",
|
|
58
58
|
"colors": "^1.4.0",
|
|
59
|
-
"commander": "^
|
|
59
|
+
"commander": "^11.0.0",
|
|
60
60
|
"emoji-regex": "^10.2.1",
|
|
61
61
|
"fetch-cookie": "^2.1.0",
|
|
62
62
|
"fs-extra": "^11.1.1",
|
|
63
63
|
"gulp-sort": "^2.0.0",
|
|
64
64
|
"hash-string": "^1.0.0",
|
|
65
|
-
"i18next": "^
|
|
65
|
+
"i18next": "^23.2.7",
|
|
66
66
|
"i18next-scanner": "^4.2.0",
|
|
67
67
|
"js-cookie": "^3.0.5",
|
|
68
68
|
"koa": "^2.14.2",
|
|
@@ -73,22 +73,22 @@
|
|
|
73
73
|
"ora": "^6.3.1",
|
|
74
74
|
"qs": "^6.11.2",
|
|
75
75
|
"react": "^18.2.0",
|
|
76
|
-
"react-i18next": "^
|
|
76
|
+
"react-i18next": "^13.0.1",
|
|
77
77
|
"resolve-path": "^1.4.0",
|
|
78
|
-
"strip-ansi": "^7.0
|
|
78
|
+
"strip-ansi": "^7.1.0",
|
|
79
79
|
"through2": "^4.0.2",
|
|
80
|
-
"tough-cookie": "^4.1.
|
|
81
|
-
"tslib": "^2.
|
|
82
|
-
"typescript": "^5.
|
|
80
|
+
"tough-cookie": "^4.1.3",
|
|
81
|
+
"tslib": "^2.6.0",
|
|
82
|
+
"typescript": "^5.1.6",
|
|
83
83
|
"undici": "^5.22.1",
|
|
84
84
|
"upath": "^2.0.1",
|
|
85
85
|
"vinyl": "^3.0.0",
|
|
86
|
-
"vinyl-fs": "^
|
|
86
|
+
"vinyl-fs": "^4.0.0",
|
|
87
87
|
"ws": "^8.13.0"
|
|
88
88
|
},
|
|
89
89
|
"devDependencies": {
|
|
90
|
-
"@babel/types": "^7.
|
|
91
|
-
"@types/babel__traverse": "^7.
|
|
90
|
+
"@babel/types": "^7.22.5",
|
|
91
|
+
"@types/babel__traverse": "^7.20.1",
|
|
92
92
|
"@types/byte-size": "^8.1.0",
|
|
93
93
|
"@types/chardet": "^0.8.1",
|
|
94
94
|
"@types/fs-extra": "^11.0.1",
|
|
@@ -96,16 +96,21 @@
|
|
|
96
96
|
"@types/js-cookie": "^3.0.3",
|
|
97
97
|
"@types/koa": "^2.13.6",
|
|
98
98
|
"@types/koa-compress": "^4.0.3",
|
|
99
|
-
"@types/lodash": "^4.14.
|
|
100
|
-
"@types/node": "^20.
|
|
99
|
+
"@types/lodash": "^4.14.195",
|
|
100
|
+
"@types/node": "^20.3.3",
|
|
101
101
|
"@types/qs": "^6.9.7",
|
|
102
|
-
"@types/react": "^18.2.
|
|
102
|
+
"@types/react": "^18.2.14",
|
|
103
103
|
"@types/through2": "^2.0.38",
|
|
104
104
|
"@types/tough-cookie": "^4.0.2",
|
|
105
105
|
"@types/vinyl-fs": "^3.0.2",
|
|
106
|
-
"@types/vscode": "^1.
|
|
106
|
+
"@types/vscode": "^1.79.1",
|
|
107
|
+
"@typescript-eslint/eslint-plugin": "^5.61.0",
|
|
108
|
+
"@typescript-eslint/parser": "^5.61.0",
|
|
109
|
+
"eslint": "^8.44.0",
|
|
110
|
+
"eslint-plugin-react": "^7.32.2",
|
|
111
|
+
"eslint-plugin-xlint": "^1.0.6",
|
|
107
112
|
"source-map-loader": "^4.0.1",
|
|
108
|
-
"ts-loader": "^9.4.
|
|
113
|
+
"ts-loader": "^9.4.4"
|
|
109
114
|
},
|
|
110
115
|
"scripts": {
|
|
111
116
|
"start": "node --title=xshell --inspect=0.0.0.0:8420 ./xshell.js",
|
package/process.js
CHANGED
|
@@ -181,7 +181,7 @@ export const exe_winterm = `C:/Users/${userInfo().username}/AppData/Local/Micros
|
|
|
181
181
|
- args: 调用参数 call parameter
|
|
182
182
|
- options?: WinTermOptions
|
|
183
183
|
*/
|
|
184
|
-
export function term(exe, args = [], { cwd = 'd:/t/', print = true, title,
|
|
184
|
+
export async function term(exe, args = [], { cwd = 'd:/t/', print = true, title,
|
|
185
185
|
// env
|
|
186
186
|
} = {}) {
|
|
187
187
|
return start(exe_winterm, [
|
package/prototype.browser.js
CHANGED
|
@@ -245,7 +245,7 @@ Object.defineProperties(String.prototype, {
|
|
|
245
245
|
let indent = 0;
|
|
246
246
|
for (; i < this.length; i++)
|
|
247
247
|
if (this[i] === ' ')
|
|
248
|
-
indent
|
|
248
|
+
indent++;
|
|
249
249
|
else if (this[i] === '\t')
|
|
250
250
|
indent += 4;
|
|
251
251
|
else
|
|
@@ -260,8 +260,8 @@ Object.defineProperties(String.prototype, {
|
|
|
260
260
|
return this;
|
|
261
261
|
let text_;
|
|
262
262
|
text_ = this
|
|
263
|
-
.replace(new RegExp(cjk +
|
|
264
|
-
.replace(new RegExp(
|
|
263
|
+
.replace(new RegExp(cjk + '([\'"])', 'g'), '$1 $2')
|
|
264
|
+
.replace(new RegExp('([\'"])' + cjk, 'g'), '$1 $2')
|
|
265
265
|
.replace(/(["']+)\s*(.+?)\s*(["']+)/g, '$1$2$3')
|
|
266
266
|
.replace(new RegExp(cjk + '([\\+\\-\\*\\/=&\\\\\\|<>])([A-Za-z0-9])', 'g'), '$1 $2 $3')
|
|
267
267
|
.replace(new RegExp('([A-Za-z0-9])([\\+\\-\\*\\/=&\\\\\\|<>])' + cjk, 'g'), '$1 $2 $3');
|
package/prototype.js
CHANGED
|
@@ -252,7 +252,7 @@ if (!global.my_prototype_defined) {
|
|
|
252
252
|
let indent = 0;
|
|
253
253
|
for (; i < this.length; i++)
|
|
254
254
|
if (this[i] === ' ')
|
|
255
|
-
indent
|
|
255
|
+
indent++;
|
|
256
256
|
else if (this[i] === '\t')
|
|
257
257
|
indent += 4;
|
|
258
258
|
else
|
|
@@ -282,8 +282,8 @@ if (!global.my_prototype_defined) {
|
|
|
282
282
|
return this;
|
|
283
283
|
let text_;
|
|
284
284
|
text_ = this
|
|
285
|
-
.replace(new RegExp(cjk +
|
|
286
|
-
.replace(new RegExp(
|
|
285
|
+
.replace(new RegExp(cjk + '([\'"])', 'g'), '$1 $2')
|
|
286
|
+
.replace(new RegExp('([\'"])' + cjk, 'g'), '$1 $2')
|
|
287
287
|
.replace(/(["']+)\s*(.+?)\s*(["']+)/g, '$1$2$3')
|
|
288
288
|
.replace(new RegExp(cjk + '([\\+\\-\\*\\/=&\\\\\\|<>])([A-Za-z0-9])', 'g'), '$1 $2 $3')
|
|
289
289
|
.replace(new RegExp('([A-Za-z0-9])([\\+\\-\\*\\/=&\\\\\\|<>])' + cjk, 'g'), '$1 $2 $3');
|
|
@@ -454,7 +454,7 @@ if (!global.my_prototype_defined) {
|
|
|
454
454
|
},
|
|
455
455
|
indent2to4() {
|
|
456
456
|
return this.split_indents()
|
|
457
|
-
.map(line => ' '.repeat(
|
|
457
|
+
.map(line => ' '.repeat(line.indent * 2) + line.text);
|
|
458
458
|
},
|
|
459
459
|
join_lines(append = true) {
|
|
460
460
|
return `${this.join('\n')}${append ? '\n' : ''}`;
|
package/repl.js
CHANGED
|
@@ -273,7 +273,8 @@ export async function start_repl() {
|
|
|
273
273
|
(async () => {
|
|
274
274
|
// --- http server
|
|
275
275
|
let { Server } = await import('./server.js');
|
|
276
|
-
server = new Server(
|
|
276
|
+
server = new Server({
|
|
277
|
+
port: 8421,
|
|
277
278
|
remote: new Remote({
|
|
278
279
|
funcs: {
|
|
279
280
|
echo({ data: [data] }) {
|
|
@@ -311,6 +312,7 @@ export async function pollute_global() {
|
|
|
311
312
|
if (mod?.__esModule)
|
|
312
313
|
return mod;
|
|
313
314
|
let result = {};
|
|
315
|
+
// eslint-disable-next-line eqeqeq
|
|
314
316
|
if (mod != null)
|
|
315
317
|
for (let k in mod)
|
|
316
318
|
if (Object.hasOwnProperty.call(mod, k))
|
package/server.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ declare module 'koa' {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
import { type Remote } from './net.js';
|
|
20
|
+
import { type Remote, type Headers } from './net.js';
|
|
21
21
|
declare module 'http' {
|
|
22
22
|
interface IncomingMessage {
|
|
23
23
|
body?: Buffer;
|
|
@@ -27,13 +27,16 @@ declare module 'http' {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
export declare class Server {
|
|
30
|
+
/** proxy 时需要丢弃的 resposne headers */
|
|
31
|
+
static drop_response_headers: Set<string>;
|
|
30
32
|
app: Koa;
|
|
31
33
|
handler: ReturnType<Koa['callback']>;
|
|
32
34
|
port: number;
|
|
33
35
|
server_http: HttpServer;
|
|
34
36
|
server_ws?: WebSocketServer;
|
|
35
37
|
remote?: Remote;
|
|
36
|
-
constructor(port
|
|
38
|
+
constructor({ port, remote }?: {
|
|
39
|
+
port?: number;
|
|
37
40
|
remote?: Remote;
|
|
38
41
|
});
|
|
39
42
|
/** start http server and listen */
|
|
@@ -48,6 +51,8 @@ export declare class Server {
|
|
|
48
51
|
_router(ctx: Context, next: Next): Promise<void>;
|
|
49
52
|
router(ctx: Context): Promise<boolean>;
|
|
50
53
|
logger(ctx: Context): void;
|
|
54
|
+
proxy(ctx: Context, url: string | URL, headers_?: Record<string, string>): Promise<void>;
|
|
55
|
+
static filter_response_headers(headers: Headers): {};
|
|
51
56
|
try_send(ctx: Context, fp: string, { root, log_404, }: {
|
|
52
57
|
root: string;
|
|
53
58
|
log_404?: boolean;
|
package/server.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createServer as http_create_server } from 'http';
|
|
2
2
|
import zlib from 'zlib';
|
|
3
3
|
import { createReadStream } from 'fs';
|
|
4
|
+
import { Readable } from 'stream';
|
|
4
5
|
// --- 3rd party
|
|
5
6
|
import upath from 'upath';
|
|
6
7
|
import qs from 'qs';
|
|
@@ -11,18 +12,30 @@ import { default as Koa } from 'koa';
|
|
|
11
12
|
import KoaCors from '@koa/cors';
|
|
12
13
|
import KoaCompress from 'koa-compress';
|
|
13
14
|
import { userAgent as KoaUserAgent } from 'koa-useragent';
|
|
15
|
+
// --- my libs
|
|
16
|
+
import { request as _request } from './net.js';
|
|
14
17
|
import { stream_to_buffer, inspect, output_width, assert } from './utils.js';
|
|
15
18
|
import { fstat } from './file.js';
|
|
16
19
|
// ------------ my server
|
|
17
20
|
export class Server {
|
|
21
|
+
/** proxy 时需要丢弃的 resposne headers */
|
|
22
|
+
static drop_response_headers = new Set([
|
|
23
|
+
// : 开头的 key
|
|
24
|
+
'connection',
|
|
25
|
+
'content-encoding',
|
|
26
|
+
'keep-alive',
|
|
27
|
+
'transfer-encoding',
|
|
28
|
+
'x-powered-by'
|
|
29
|
+
]);
|
|
18
30
|
app;
|
|
19
31
|
handler;
|
|
20
32
|
port;
|
|
21
33
|
server_http;
|
|
22
34
|
server_ws;
|
|
23
35
|
remote;
|
|
24
|
-
constructor(port,
|
|
25
|
-
|
|
36
|
+
constructor({ port, remote } = {}) {
|
|
37
|
+
if (port !== undefined)
|
|
38
|
+
this.port = port;
|
|
26
39
|
if (remote)
|
|
27
40
|
this.remote = remote;
|
|
28
41
|
}
|
|
@@ -122,9 +135,8 @@ export class Server {
|
|
|
122
135
|
request.body = JSON.parse(req.body.toString());
|
|
123
136
|
else if (ctx.is('application/x-www-form-urlencoded'))
|
|
124
137
|
request.body = qs.parse(req.body.toString());
|
|
125
|
-
else if (ctx.is('multipart/form-data'))
|
|
138
|
+
else if (ctx.is('multipart/form-data'))
|
|
126
139
|
throw new Error('multipart/form-data is not supported');
|
|
127
|
-
}
|
|
128
140
|
else
|
|
129
141
|
request.body = req.body;
|
|
130
142
|
}
|
|
@@ -210,6 +222,45 @@ export class Server {
|
|
|
210
222
|
// --- print log
|
|
211
223
|
console.log(s);
|
|
212
224
|
}
|
|
225
|
+
async proxy(ctx, url, headers_ = {}) {
|
|
226
|
+
const { request: { method, headers, query, body } } = ctx;
|
|
227
|
+
let { response } = ctx;
|
|
228
|
+
try {
|
|
229
|
+
const response_ = await _request(url, {
|
|
230
|
+
method: method,
|
|
231
|
+
queries: query,
|
|
232
|
+
body,
|
|
233
|
+
headers: {
|
|
234
|
+
...headers,
|
|
235
|
+
...headers_
|
|
236
|
+
},
|
|
237
|
+
raw: true,
|
|
238
|
+
});
|
|
239
|
+
response.status = response_.status;
|
|
240
|
+
response.set(Server.filter_response_headers(response_.headers));
|
|
241
|
+
response.body = Readable.fromWeb(response_.body);
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
console.log(error);
|
|
245
|
+
if (error.response) {
|
|
246
|
+
const { status, headers, text } = error.response;
|
|
247
|
+
response.status = status;
|
|
248
|
+
response.set(Server.filter_response_headers(headers));
|
|
249
|
+
response.body = text;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
response.status = 500;
|
|
253
|
+
response.body = inspect(error, { colors: false });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
static filter_response_headers(headers) {
|
|
258
|
+
let headers_ = {};
|
|
259
|
+
for (const [key, value] of headers)
|
|
260
|
+
if (!key.startsWith(':') && !this.drop_response_headers.has(key))
|
|
261
|
+
headers_[key] = value;
|
|
262
|
+
return headers_;
|
|
263
|
+
}
|
|
213
264
|
async try_send(ctx, fp, { root, log_404, }) {
|
|
214
265
|
const { request: { _path, path, method }, response, } = ctx;
|
|
215
266
|
if (!(typeof response.body === 'undefined') || response.status !== 404)
|
|
@@ -300,7 +351,7 @@ export class Server {
|
|
|
300
351
|
assert(stats.size <= Number.MAX_SAFE_INTEGER);
|
|
301
352
|
let start = range[1] ? parseInt(range[1]) : undefined;
|
|
302
353
|
let end = range[2] ? parseInt(range[2]) : Number(stats.size) - 1;
|
|
303
|
-
if (
|
|
354
|
+
if (start === undefined) {
|
|
304
355
|
start = Number(stats.size) - end;
|
|
305
356
|
end = Number(stats.size) - 1;
|
|
306
357
|
}
|
package/utils.browser.d.ts
CHANGED
|
@@ -11,7 +11,8 @@ export interface Deferred<TValue> extends Promise<TValue> {
|
|
|
11
11
|
reject(reason?: Error): void;
|
|
12
12
|
}
|
|
13
13
|
/** 创建一个 promise,后续可调用 promise.resolve, promise.reject 方法设置其状态和值
|
|
14
|
-
- initial?: `undefined` 传入非 undefined 值(包括 null)时直接设置为 resolved 状态
|
|
14
|
+
- initial?: `undefined` 传入非 undefined 值(包括 null)时直接设置为 resolved 状态
|
|
15
|
+
注: 下面的方法不能标记为 aysnc function, 否则会对返回值再做一层 Promise.resolve() 导致 reject, resolve 属性丢失 */
|
|
15
16
|
export declare function defer<TValue>(initial?: TValue): Deferred<TValue>;
|
|
16
17
|
export interface LockedAction<TResource, TResult> {
|
|
17
18
|
(resource: TResource): TResult | Promise<TResult>;
|
package/utils.browser.js
CHANGED
|
@@ -17,7 +17,9 @@ export async function timeout(milliseconds) {
|
|
|
17
17
|
throw error;
|
|
18
18
|
}
|
|
19
19
|
/** 创建一个 promise,后续可调用 promise.resolve, promise.reject 方法设置其状态和值
|
|
20
|
-
- initial?: `undefined` 传入非 undefined 值(包括 null)时直接设置为 resolved 状态
|
|
20
|
+
- initial?: `undefined` 传入非 undefined 值(包括 null)时直接设置为 resolved 状态
|
|
21
|
+
注: 下面的方法不能标记为 aysnc function, 否则会对返回值再做一层 Promise.resolve() 导致 reject, resolve 属性丢失 */
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
21
23
|
export function defer(initial) {
|
|
22
24
|
if (initial === undefined) {
|
|
23
25
|
let resolve;
|
package/utils.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import './prototype.js';
|
|
|
8
8
|
export declare const noop: () => void;
|
|
9
9
|
/** `230` term 字符宽度 (实际上有 240) */
|
|
10
10
|
export declare const output_width = 230;
|
|
11
|
-
export declare function set_inspect_options(): void;
|
|
11
|
+
export declare function set_inspect_options(colors?: boolean): void;
|
|
12
12
|
export declare function assert(assertion: any, message?: string): never | void;
|
|
13
13
|
export declare function dedent(templ: TemplateStringsArray | string, ...values: any[]): string;
|
|
14
14
|
/** 数组或 iterable 去重(可按 selector 去重)
|
|
@@ -51,7 +51,8 @@ export interface Deferred<TValue> extends Promise<TValue> {
|
|
|
51
51
|
reject(reason?: Error): void;
|
|
52
52
|
}
|
|
53
53
|
/** 创建一个 promise,后续可调用 promise.resolve, promise.reject 方法设置其状态和值
|
|
54
|
-
- initial?: `undefined` 传入非 undefined 值(包括 null)时直接设置为 resolved 状态
|
|
54
|
+
- initial?: `undefined` 传入非 undefined 值(包括 null)时直接设置为 resolved 状态
|
|
55
|
+
注: 下面的方法不能标记为 aysnc function, 否则会对返回值再做一层 Promise.resolve() 导致 reject, resolve 属性丢失 */
|
|
55
56
|
export declare function defer<TValue>(initial?: TValue): Deferred<TValue>;
|
|
56
57
|
export interface LockedAction<TResource, TResult> {
|
|
57
58
|
(resource: TResource): TResult | Promise<TResult>;
|
package/utils.js
CHANGED
|
@@ -6,11 +6,11 @@ import './prototype.js';
|
|
|
6
6
|
export const noop = () => { };
|
|
7
7
|
/** `230` term 字符宽度 (实际上有 240) */
|
|
8
8
|
export const output_width = 230;
|
|
9
|
-
export function set_inspect_options() {
|
|
9
|
+
export function set_inspect_options(colors = true) {
|
|
10
10
|
util.inspect.defaultOptions.maxArrayLength = 40;
|
|
11
11
|
util.inspect.defaultOptions.maxStringLength = 10000;
|
|
12
12
|
util.inspect.defaultOptions.breakLength = output_width;
|
|
13
|
-
util.inspect.defaultOptions.colors =
|
|
13
|
+
util.inspect.defaultOptions.colors = colors;
|
|
14
14
|
util.inspect.defaultOptions.compact = false;
|
|
15
15
|
util.inspect.defaultOptions.getters = true;
|
|
16
16
|
util.inspect.defaultOptions.depth = 2;
|
|
@@ -156,7 +156,9 @@ export async function timeout(milliseconds) {
|
|
|
156
156
|
throw error;
|
|
157
157
|
}
|
|
158
158
|
/** 创建一个 promise,后续可调用 promise.resolve, promise.reject 方法设置其状态和值
|
|
159
|
-
- initial?: `undefined` 传入非 undefined 值(包括 null)时直接设置为 resolved 状态
|
|
159
|
+
- initial?: `undefined` 传入非 undefined 值(包括 null)时直接设置为 resolved 状态
|
|
160
|
+
注: 下面的方法不能标记为 aysnc function, 否则会对返回值再做一层 Promise.resolve() 导致 reject, resolve 属性丢失 */
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
160
162
|
export function defer(initial) {
|
|
161
163
|
if (initial === undefined) {
|
|
162
164
|
let resolve;
|
|
@@ -266,7 +268,7 @@ export function inspect(obj, options = {}) {
|
|
|
266
268
|
}
|
|
267
269
|
(function (inspect) {
|
|
268
270
|
inspect.custom = util.inspect.custom;
|
|
269
|
-
})(inspect
|
|
271
|
+
})(inspect || (inspect = {}));
|
|
270
272
|
// ------------------------------------ stream
|
|
271
273
|
/** npm map-stream
|
|
272
274
|
filter will reemit the data if cb(err,pass) pass is truthy
|