xshell 1.0.30 → 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/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/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,
package/net.d.ts CHANGED
@@ -14,6 +14,7 @@ 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;
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: (() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xshell",
3
- "version": "1.0.30",
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.22.1",
49
- "@babel/parser": "^7.22.4",
50
- "@babel/traverse": "^7.22.4",
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
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.22.4",
91
- "@types/babel__traverse": "^7.20.0",
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",
@@ -97,20 +97,20 @@
97
97
  "@types/koa": "^2.13.6",
98
98
  "@types/koa-compress": "^4.0.3",
99
99
  "@types/lodash": "^4.14.195",
100
- "@types/node": "^20.2.5",
100
+ "@types/node": "^20.3.3",
101
101
  "@types/qs": "^6.9.7",
102
- "@types/react": "^18.2.7",
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.1",
107
- "@typescript-eslint/eslint-plugin": "^5.59.8",
108
- "@typescript-eslint/parser": "^5.59.8",
109
- "eslint": "^8.41.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
110
  "eslint-plugin-react": "^7.32.2",
111
111
  "eslint-plugin-xlint": "^1.0.6",
112
112
  "source-map-loader": "^4.0.1",
113
- "ts-loader": "^9.4.3"
113
+ "ts-loader": "^9.4.4"
114
114
  },
115
115
  "scripts": {
116
116
  "start": "node --title=xshell --inspect=0.0.0.0:8420 ./xshell.js",
@@ -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
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
@@ -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] }) {
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
  }
@@ -209,6 +222,45 @@ export class Server {
209
222
  // --- print log
210
223
  console.log(s);
211
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
+ }
212
264
  async try_send(ctx, fp, { root, log_404, }) {
213
265
  const { request: { _path, path, method }, response, } = ctx;
214
266
  if (!(typeof response.body === 'undefined') || response.status !== 404)
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 去重)
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;
@@ -268,7 +268,7 @@ export function inspect(obj, options = {}) {
268
268
  }
269
269
  (function (inspect) {
270
270
  inspect.custom = util.inspect.custom;
271
- })(inspect = inspect || (inspect = {}));
271
+ })(inspect || (inspect = {}));
272
272
  // ------------------------------------ stream
273
273
  /** npm map-stream
274
274
  filter will reemit the data if cb(err,pass) pass is truthy