xshell 1.0.30 → 1.0.32

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.32",
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,17 @@ 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
+ colors: boolean;
39
+ constructor({ port, remote }?: {
40
+ port?: number;
37
41
  remote?: Remote;
38
42
  });
39
43
  /** start http server and listen */
@@ -48,6 +52,8 @@ export declare class Server {
48
52
  _router(ctx: Context, next: Next): Promise<void>;
49
53
  router(ctx: Context): Promise<boolean>;
50
54
  logger(ctx: Context): void;
55
+ proxy(ctx: Context, url: string | URL, headers_?: Record<string, string>): Promise<void>;
56
+ static filter_response_headers(headers: Headers): {};
51
57
  try_send(ctx: Context, fp: string, { root, log_404, }: {
52
58
  root: string;
53
59
  log_404?: boolean;
package/server.js CHANGED
@@ -1,6 +1,8 @@
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';
5
+ import util from 'util';
4
6
  // --- 3rd party
5
7
  import upath from 'upath';
6
8
  import qs from 'qs';
@@ -11,18 +13,31 @@ import { default as Koa } from 'koa';
11
13
  import KoaCors from '@koa/cors';
12
14
  import KoaCompress from 'koa-compress';
13
15
  import { userAgent as KoaUserAgent } from 'koa-useragent';
16
+ // --- my libs
17
+ import { request as _request } from './net.js';
14
18
  import { stream_to_buffer, inspect, output_width, assert } from './utils.js';
15
19
  import { fstat } from './file.js';
16
20
  // ------------ my server
17
21
  export class Server {
22
+ /** proxy 时需要丢弃的 resposne headers */
23
+ static drop_response_headers = new Set([
24
+ // : 开头的 key
25
+ 'connection',
26
+ 'content-encoding',
27
+ 'keep-alive',
28
+ 'transfer-encoding',
29
+ 'x-powered-by'
30
+ ]);
18
31
  app;
19
32
  handler;
20
33
  port;
21
34
  server_http;
22
35
  server_ws;
23
36
  remote;
24
- constructor(port, { remote } = {}) {
25
- this.port = port;
37
+ colors = util.inspect.defaultOptions.colors;
38
+ constructor({ port, remote } = {}) {
39
+ if (port !== undefined)
40
+ this.port = port;
26
41
  if (remote)
27
42
  this.remote = remote;
28
43
  }
@@ -149,6 +164,7 @@ export class Server {
149
164
  return false;
150
165
  }
151
166
  logger(ctx) {
167
+ const { colors } = this;
152
168
  const { request } = ctx;
153
169
  const { query, body, path, _path, protocol, host, req: { httpVersion: http_version }, ip, } = request;
154
170
  let { method } = request;
@@ -166,7 +182,7 @@ export class Server {
166
182
  if (ua.isDesktop)
167
183
  t += 'desktop';
168
184
  if (ua.isBot)
169
- t += `${t ? ' ' : ''}${'robot'.blue}`;
185
+ t += `${t ? ' ' : ''}${colors ? 'robot'.blue : 'robot'}`;
170
186
  if (ua.platform !== 'unknown' && !ua.os.startsWith('Windows'))
171
187
  t += '/' + ua.platform.toLowerCase().replace('apple mac', 'mac');
172
188
  if (ua.os !== 'unknown' && ua.platform !== 'Android')
@@ -183,7 +199,7 @@ export class Server {
183
199
  s += `${`${protocol.pad(5)}/${http_version}`.pad(10)} `;
184
200
  // --- method
185
201
  method = method.toLowerCase();
186
- s += method === 'get' ? method.pad(10) : method.pad(10).yellow;
202
+ s += (method === 'get' || !colors) ? method.pad(10) : method.pad(10).yellow;
187
203
  // --- host
188
204
  s += `${host.pad(20)} `;
189
205
  // --- path
@@ -191,7 +207,7 @@ export class Server {
191
207
  if (path.toLowerCase() !== _path.toLowerCase())
192
208
  return `${_path.blue} → ${path}`;
193
209
  if (!path.includes('.'))
194
- return path.yellow;
210
+ return colors ? path.yellow : path;
195
211
  return path;
196
212
  })();
197
213
  // --- query
@@ -209,6 +225,45 @@ export class Server {
209
225
  // --- print log
210
226
  console.log(s);
211
227
  }
228
+ async proxy(ctx, url, headers_ = {}) {
229
+ const { request: { method, headers, query, body } } = ctx;
230
+ let { response } = ctx;
231
+ try {
232
+ const response_ = await _request(url, {
233
+ method: method,
234
+ queries: query,
235
+ body,
236
+ headers: {
237
+ ...headers,
238
+ ...headers_
239
+ },
240
+ raw: true,
241
+ });
242
+ response.status = response_.status;
243
+ response.set(Server.filter_response_headers(response_.headers));
244
+ response.body = Readable.fromWeb(response_.body);
245
+ }
246
+ catch (error) {
247
+ console.log(error);
248
+ if (error.response) {
249
+ const { status, headers, text } = error.response;
250
+ response.status = status;
251
+ response.set(Server.filter_response_headers(headers));
252
+ response.body = text;
253
+ }
254
+ else {
255
+ response.status = 500;
256
+ response.body = inspect(error, { colors: false });
257
+ }
258
+ }
259
+ }
260
+ static filter_response_headers(headers) {
261
+ let headers_ = {};
262
+ for (const [key, value] of headers)
263
+ if (!key.startsWith(':') && !this.drop_response_headers.has(key))
264
+ headers_[key] = value;
265
+ return headers_;
266
+ }
212
267
  async try_send(ctx, fp, { root, log_404, }) {
213
268
  const { request: { _path, path, method }, response, } = ctx;
214
269
  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