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 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(`如要更新翻译请设置 { overwrite: true },否则使用 i18n.t('text', { context: 'xxx' }) 标记文本以区分。\n`);
65
+ console.error('如要更新翻译请设置 { overwrite: true },否则使用 i18n.t(\'text\', { context: \'xxx\' }) 标记文本以区分。\n');
66
66
  return;
67
67
  }
68
68
  else {
@@ -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 === "t")
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 === "i18n" &&
15
- (node.callee.property.name === "t" || 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 === "Trans");
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 }) {
@@ -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
- * 防止 count 被忽略,如
61
- * ```jsx
62
- * <Trans count={arr.length}>
63
- * 一二三{{ count: arr.length }}
64
- * </Trans>
65
- * ```
66
- */
59
+ /** 防止 count 被忽略,如
60
+ ```jsx
61
+ <Trans count={arr.length}>
62
+ 一二三{{ count: arr.length }}
63
+ </Trans>
64
+ ``` */
67
65
  }
68
- else if (name === "count")
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 !== "string")
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, "count"))
82
+ if (Object.prototype.hasOwnProperty.call(attr, 'count'))
85
83
  options.count = Number(attr.count) || 0;
86
- if (Object.prototype.hasOwnProperty.call(attr, "ns")) {
87
- if (typeof options.ns !== "string")
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(":").yellow);
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((" " + err.message).red);
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, "") // remove leading spaces containing a leading newline character
123
- .replace(/[\r\n]+\s*$/g, "") // remove trailing spaces containing a leading newline character
124
- .replace(/[\r\n]+\s*/g, " "); // replace spaces containing a leading newline character with a single space character
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(":").yellow);
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
- headers.set(key, value); // Headers 类型中不会有也不允许设置 :path 等 : 开头的 key
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
- const value = _headers[key];
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, value);
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<string> | Promise<Cookie[]>;
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 = MyProxy || (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/111.0.0.0 Safari/537.36',
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
- headers.set(key, value); // Headers 类型中不会有也不允许设置 :path 等 : 开头的 key
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
- const value = _headers[key];
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, value);
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.28",
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": ">=19.9.0",
20
- "vscode": ">=1.77.0"
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.21.8",
49
- "@babel/parser": "^7.21.8",
50
- "@babel/traverse": "^7.21.5",
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.4",
52
+ "@types/ws": "^8.5.5",
53
53
  "byte-size": "^8.1.1",
54
- "chalk": "^5.2.0",
55
- "chardet": "^1.5.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": "^10.0.1",
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": "^22.5.0",
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": "^12.3.1",
76
+ "react-i18next": "^13.0.1",
77
77
  "resolve-path": "^1.4.0",
78
- "strip-ansi": "^7.0.1",
78
+ "strip-ansi": "^7.1.0",
79
79
  "through2": "^4.0.2",
80
- "tough-cookie": "^4.1.2",
81
- "tslib": "^2.5.2",
82
- "typescript": "^5.0.4",
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": "^3.0.3",
86
+ "vinyl-fs": "^4.0.0",
87
87
  "ws": "^8.13.0"
88
88
  },
89
89
  "devDependencies": {
90
- "@babel/types": "^7.21.5",
91
- "@types/babel__traverse": "^7.18.5",
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.194",
100
- "@types/node": "^20.2.1",
99
+ "@types/lodash": "^4.14.195",
100
+ "@types/node": "^20.3.3",
101
101
  "@types/qs": "^6.9.7",
102
- "@types/react": "^18.2.6",
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.78.0",
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.2"
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, [
@@ -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 += 1;
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 + `(['"])`, 'g'), '$1 $2')
264
- .replace(new RegExp(`(['"])` + cjk, 'g'), '$1 $2')
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 += 1;
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 + `(['"])`, 'g'), '$1 $2')
286
- .replace(new RegExp(`(['"])` + cjk, 'g'), '$1 $2')
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(Math.floor(line.indent / 2) * 4) + line.text);
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(8421, {
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: number, { remote }?: {
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, { remote } = {}) {
25
- this.port = port;
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 (typeof start == 'undefined') {
354
+ if (start === undefined) {
304
355
  start = Number(stats.size) - end;
305
356
  end = Number(stats.size) - 1;
306
357
  }
@@ -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 = true;
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 = inspect || (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