xshell 1.3.5 → 1.3.7

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/builder.d.ts CHANGED
@@ -53,6 +53,8 @@ interface AssetOption {
53
53
  src: string;
54
54
  /** 相对 fpd_out 的路径,或者绝对路径,默认等于 src 的值 */
55
55
  out?: string;
56
+ /** 直接设置 html 的路径,强制覆盖 */
57
+ value?: string;
56
58
  }
57
59
  type Assets = {
58
60
  productions?: (string | AssetOption)[];
@@ -127,7 +129,7 @@ export declare class Bundler {
127
129
  - target: 目标环境 nodejs | web
128
130
  - fpd_root: 项目根目录
129
131
  - fpd_out: 输出文件目录
130
- - fpd_cache: webpack 缓存目录
132
+ - fpdt_cache?: webpack 缓存目录,可以设置为 undefined 使用默认路径 node_modules/.cache/webpack/
131
133
  - entry: 入口文件
132
134
  - options?: 打包配置, 按常用顺序排列
133
135
  - production?: `true` webpack mode 设置为 'production'
package/builder.js CHANGED
@@ -104,7 +104,7 @@ const dependencies = {
104
104
  function get_asset_out(asset) {
105
105
  return typeof asset === 'string'
106
106
  ? asset
107
- : asset.out || asset.src;
107
+ : asset.value || asset.out || asset.src;
108
108
  }
109
109
  function get_vendor_asset_out(asset) {
110
110
  return typeof asset === 'string'
@@ -152,7 +152,7 @@ export class Bundler {
152
152
  - target: 目标环境 nodejs | web
153
153
  - fpd_root: 项目根目录
154
154
  - fpd_out: 输出文件目录
155
- - fpd_cache: webpack 缓存目录
155
+ - fpdt_cache?: webpack 缓存目录,可以设置为 undefined 使用默认路径 node_modules/.cache/webpack/
156
156
  - entry: 入口文件
157
157
  - options?: 打包配置, 按常用顺序排列
158
158
  - production?: `true` webpack mode 设置为 'production'
package/development.js CHANGED
@@ -26,10 +26,10 @@ export function process_stdin(on_key, on_exit) {
26
26
  }
27
27
  /** 自动配置项目 vscode 设置 */
28
28
  export async function setup_vscode_settings(fpd_root) {
29
- if (!ramdisk) {
30
- const fp_settings = `${fpd_root}.vscode/settings.json`;
31
- if (!fexists(fp_settings, noprint))
32
- await fcopy(`${fp_settings.strip_end('json')}template.json`, fp_settings);
33
- }
29
+ if (ramdisk)
30
+ return;
31
+ const fp_settings = `${fpd_root}.vscode/settings.json`;
32
+ if (!fexists(fp_settings, noprint))
33
+ await fcopy(`${fp_settings.strip_end('json')}template.json`, fp_settings);
34
34
  }
35
35
  //# sourceMappingURL=development.js.map
package/file.js CHANGED
@@ -747,8 +747,10 @@ export async function ftail(fp, handler, { print = true } = {}) {
747
747
  }
748
748
  /** 打开一个文件并搜索替换某个 pattern */
749
749
  export async function freplace(fp, pattern, replacement, { print = true } = {}) {
750
- await fwrite(fp, (await fread(fp, { print }))
751
- .replaceAll(pattern, replacement), { print });
750
+ await fwrite(fp, (await fread(fp, noprint))
751
+ .replaceAll(pattern, replacement), noprint);
752
+ if (print)
753
+ console.log('替换了:', fp);
752
754
  }
753
755
  /** 同步文件或文件夹 fp_src 到 fp_dst ,返回 fp_dst
754
756
  - fp_src: 源文件或文件夹
package/fzip.js CHANGED
@@ -60,9 +60,7 @@ async function _zip(data, fp_zip, { dirname, print = { files: true, info: true }
60
60
  });
61
61
  ostream.once('error', reject);
62
62
  archive.once('error', reject);
63
- archive.on('warning', error => {
64
- console.log(error);
65
- });
63
+ archive.on('warning', console.warn);
66
64
  archive.pipe(ostream);
67
65
  for (const fp in entries) {
68
66
  const fdata = entries[fp];
package/net.common.d.ts CHANGED
@@ -11,23 +11,6 @@ export interface BearerAuth {
11
11
  type: 'bearer';
12
12
  token: string;
13
13
  }
14
- /** 自动保持 remote 连接,只对 initiator 且传入 url 有效,
15
- - 在未连接,或 websocket on_error 检测到连接断开后以 reconnect_interval 间隔尝试 remote.call(func) 重连
16
- - 在连接状态下以 heartbeat_interval 间隔发送心跳包确保连接状态
17
-
18
- 配置选项:
19
- - func?: 连接后调用的函数,通常为某个 register 函数,将自身注册到 server
20
- - args?: 连接后调用函数时传递的参数,与 func 配合使用
21
- - reconnect_interval?: `1000 * 5` 首次重连失败后的后续尝试间隔 (单位: ms)
22
- - heartbeat_interval?: `1000 * 60` 发送心跳包确保连接的间隔 (单位: ms)
23
- - error_delay?: `2000` 遇到错误时首次尝试重连的延时 (单位: ms) */
24
- export interface RemoteKeeperOptions {
25
- func?: string;
26
- args?: any[];
27
- reconnect_interval?: number;
28
- heartbeat_interval?: number;
29
- error_delay?: number;
30
- }
31
14
  export declare const WebSocketConnecting = 0;
32
15
  export declare const WebSocketOpen = 1;
33
16
  export declare const WebSocketClosing = 2;
@@ -49,6 +32,21 @@ export declare class WebSocketConnectionError extends Error {
49
32
  static get_reason_string(code: number | string, reason?: string): string | number;
50
33
  constructor(url: string, protocols: string[] | undefined, event?: CloseEvent | ErrorEvent, message?: string);
51
34
  }
35
+ export interface ConnectWebSocketPrint {
36
+ connect: boolean;
37
+ error: boolean;
38
+ close: boolean;
39
+ }
40
+ export interface ConnectWebsocketOptions {
41
+ print?: boolean | ConnectWebSocketPrint;
42
+ protocols?: string[];
43
+ max_payload?: number;
44
+ proxy?: string;
45
+ on_message(data: ArrayBuffer | string, websocket: WebSocket): any;
46
+ on_error?(error: WebSocketConnectionError, websocket: WebSocket): any;
47
+ on_close?(event: CloseEvent, websocket: WebSocket): any;
48
+ connect?: (typeof import('tls'))['connect'] | (typeof import('net'))['connect'];
49
+ }
52
50
  /** 连接 websocket url, 设置各种事件监听器。在 open 事件后 resolve, 返回 websocket
53
51
  遇到 error, close 事件时会创建 WebSocketConnectionError:
54
52
  - reject 掉返回的 promise (若此时未 settle)
@@ -57,7 +55,9 @@ export declare class WebSocketConnectionError extends Error {
57
55
  https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/bufferedAmount
58
56
  - url
59
57
  - options:
60
- - print?: 是否打印连接、关闭信息
58
+ - print?: boolean 或者 ConnectWebSocketPrint,可以控制是否打印连接、正常断开、关闭信息
59
+ - connect: `true` 连接建立
60
+ - error: `true` 出错
61
61
  - protocols?
62
62
  - max_payload?: `8 gb` (仅 nodejs 环境有效)
63
63
  - proxy?: string (仅 nodejs 环境有效)
@@ -66,17 +66,10 @@ export declare class WebSocketConnectionError extends Error {
66
66
  - on_error?: 在 websocket 出错和非正常关闭 (close, error 事件) 时都调用,可以根据 error.type 来区分,error 的类型是 WebSocketConnectionError,
67
67
  type 为 'close' 时有 code 和 reason 属性
68
68
  - on_close?: 和 websocket 的 'close' 事件不相同,只在正常关闭 (close code 为 1000) 时才调用,否则都会调用 on_error
69
- https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes */
69
+ https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes
70
+ - connect?: nodejs 环境下需要指定 ip, port 手动连接 tls server 时使用,有 agent 时不生效 */
70
71
  export declare function connect_websocket(url: string | URL, { print, protocols, max_payload, // 8 GB
71
- proxy, on_message, on_error, on_close, }: {
72
- print?: boolean;
73
- protocols?: string[];
74
- max_payload?: number;
75
- proxy?: string;
76
- on_message(data: ArrayBuffer | string, websocket: WebSocket): any;
77
- on_error?(error: WebSocketConnectionError, websocket: WebSocket): any;
78
- on_close?(event: CloseEvent, websocket: WebSocket): any;
79
- }): Promise<WebSocket>;
72
+ proxy, on_message, on_error, on_close, connect }: ConnectWebsocketOptions): Promise<WebSocket>;
80
73
  /** 接收到消息后的处理函数
81
74
  返回值会自动被 await,然后可能被封装为 message 回传 */
82
75
  export type MessageHandler<TData = any> = (message: Message<TData>, remote: Remote) => void | any | Promise<void | any>;
@@ -102,6 +95,8 @@ export interface RemoteOptions {
102
95
  on_error?(error: WebSocketConnectionError | Error, remote: Remote): void;
103
96
  /** 使用者自定义的,在重连之前调用的函数,比如可以修改 url,仅连接发起方有效 */
104
97
  on_reconnect?(this: Remote): Promise<void>;
98
+ /** 使用者自定义的,在连接建立,包括首次连接和重连 之后调用的函数,比如可以重新调用一些函数,仅连接发起方有效 */
99
+ on_connected?(this: Remote): void;
105
100
  }
106
101
  /** 通过创建 remote 对象对 websocket rpc 进行抽象
107
102
  使用 remote.call() 进行一元 rpc
@@ -148,10 +143,11 @@ export declare class Remote {
148
143
  error: WebSocketConnectionError | Error;
149
144
  /** 作为 websocket 连接发起方,传入 url
150
145
  作为 websocket 连接接收方,传入 websocket + name */
151
- constructor({ name, url, websocket, funcs, print, verbose, probe, args, on_error, on_reconnect }?: RemoteOptions);
146
+ constructor({ name, url, websocket, funcs, print, verbose, probe, args, on_error, on_reconnect, on_connected }?: RemoteOptions);
152
147
  /** 统一处理首次连接和连接后的 websocket 错误 */
153
148
  _on_error: (error: WebSocketConnectionError | Error) => void;
154
149
  on_reconnect?: (this: Remote) => Promise<void>;
150
+ on_connected?: (this: Remote) => void;
155
151
  reconnect: () => Promise<void>;
156
152
  _on_message: (data: ArrayBuffer) => void;
157
153
  /** 使用者自定义的在 websocket 连接出错时,或者 handlers 出错时的处理
package/net.common.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { platform } from "./platform.common.js";
2
2
  import { rethrow } from "./prototype.common.js"; // .bracket()
3
3
  import { message_symbol, pack, parse } from "./io.common.js";
4
- import { check, defer, delay, genid, Lock, set_error_message, timeout, TimeoutError } from "./utils.common.js";
4
+ import { check, defer, defer2, delay, genid, Lock, nowstr, set_error_message, timeout, TimeoutError } from "./utils.common.js";
5
5
  import { t } from "./i18n/instance.js";
6
6
  /** 对于 request() 函数来说无意义的 headers,会自动过滤掉 */
7
7
  export const drop_request_headers = new Set([
@@ -50,16 +50,7 @@ export class WebSocketConnectionError extends Error {
50
50
  if (event)
51
51
  if ((type = event.type) === 'error') {
52
52
  ({ error } = event);
53
- if (error) {
54
- ({ code, reason } = error);
55
- message = `${t('连接被关闭')}: ` + [
56
- error.message,
57
- WebSocketConnectionError.get_reason_string(code, reason),
58
- error.syscall ? `${error.syscall}(${error.errno})` : ''
59
- ].filter(Boolean).join(' ');
60
- }
61
- else
62
- message = t('连接被关闭');
53
+ message = error ? `${t('连接被关闭')}: ${error.message}` : t('连接被关闭');
63
54
  }
64
55
  else {
65
56
  ({ code, reason } = event);
@@ -67,7 +58,7 @@ export class WebSocketConnectionError extends Error {
67
58
  }
68
59
  else
69
60
  message = t('连接被关闭');
70
- super(new Date().to_time_str() + ' ' +
61
+ super(nowstr() + ' ' +
71
62
  (url || '传入') +
72
63
  (protocols ? ' ' + protocols.join(', ').bracket() : '') +
73
64
  ' ' + message);
@@ -97,7 +88,9 @@ let websocket_proxy_agents = {};
97
88
  https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/bufferedAmount
98
89
  - url
99
90
  - options:
100
- - print?: 是否打印连接、关闭信息
91
+ - print?: boolean 或者 ConnectWebSocketPrint,可以控制是否打印连接、正常断开、关闭信息
92
+ - connect: `true` 连接建立
93
+ - error: `true` 出错
101
94
  - protocols?
102
95
  - max_payload?: `8 gb` (仅 nodejs 环境有效)
103
96
  - proxy?: string (仅 nodejs 环境有效)
@@ -106,10 +99,21 @@ let websocket_proxy_agents = {};
106
99
  - on_error?: 在 websocket 出错和非正常关闭 (close, error 事件) 时都调用,可以根据 error.type 来区分,error 的类型是 WebSocketConnectionError,
107
100
  type 为 'close' 时有 code 和 reason 属性
108
101
  - on_close?: 和 websocket 的 'close' 事件不相同,只在正常关闭 (close code 为 1000) 时才调用,否则都会调用 on_error
109
- https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes */
110
- export async function connect_websocket(url, { print = true, protocols, max_payload = 2 ** 33, // 8 GB
111
- proxy, on_message, on_error, on_close, }) {
102
+ https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes
103
+ - connect?: nodejs 环境下需要指定 ip, port 手动连接 tls server 时使用,有 agent 时不生效 */
104
+ export async function connect_websocket(url, { print = {
105
+ connect: true,
106
+ error: true,
107
+ close: true
108
+ }, protocols, max_payload = 2 ** 33, // 8 GB
109
+ proxy, on_message, on_error, on_close, connect }) {
112
110
  const { nodejs } = platform;
111
+ if (typeof print === 'boolean')
112
+ print = {
113
+ connect: print,
114
+ error: print,
115
+ close: print
116
+ };
113
117
  let websocket = new (await platform.get_websocket())(url, protocols,
114
118
  // @ts-ignore
115
119
  nodejs ?
@@ -119,78 +123,60 @@ proxy, on_message, on_error, on_close, }) {
119
123
  allowSynchronousEvents: true,
120
124
  ...proxy ? {
121
125
  agent: websocket_proxy_agents[proxy] ??= new (await platform.get_https_proxy_agent())(proxy)
122
- } : {}
126
+ } : {},
127
+ ...connect ? { createConnection: connect } : {}
123
128
  }
124
129
  :
125
130
  undefined);
126
131
  // https://stackoverflow.com/questions/11821096/what-is-the-difference-between-an-arraybuffer-and-a-blob/39951543
127
132
  websocket.binaryType = 'arraybuffer';
128
- return new Promise((resolve, reject) => {
129
- let settled = false;
130
- // websocket error, close 事件只选一个最详细的构造 WebSocketConnectionError 并调用 on_error
131
- let errored;
132
- websocket.addEventListener('open', event => {
133
- if (print)
134
- console.log(websocket.url +
135
- (websocket.protocol ? ' ' + websocket.protocol.bracket() : '') +
136
- t(' 已连接'));
137
- settled = true;
138
- resolve(websocket);
139
- }, { once: true });
140
- // error 事件会先于 close 事件
141
- // node.js 环境下 error 事件的错误信息比较多,浏览器环境下 close 的错误信息比较多
142
- // 在浏览器环境下延后处理 error 事件,放到微任务队列之后的 timers 队列中
143
- // https://blog.insiderattack.net/promises-next-ticks-and-immediates-nodejs-event-loop-part-3-9226cbe7a6aa
144
- function on_close_or_error(event) {
145
- websocket.removeEventListener('message', _on_message);
146
- const { type } = event;
147
- // 正常关闭
148
- if (type === 'close' && event.code === 1000) {
149
- if (on_close)
150
- on_close(event, websocket);
151
- else if (print)
152
- console.log(`${websocket.url} 已正常关闭`);
153
- return;
154
- }
155
- // 观察一段时间,在 nodejs 中应该没有先 close 再 error 的情况
156
- if (errored === 'close' && platform.nodejs) {
157
- console.warn('在 nodejs 中应该没有先 close 再 error 的情况', event.error);
158
- errored = null;
159
- }
160
- // 已经调用过 on_error 了
161
- if (errored)
162
- return;
163
- errored = type;
164
- const error = new WebSocketConnectionError(websocket.url, protocols, event);
165
- if (!settled) {
166
- settled = true;
167
- reject(error);
168
- return;
169
- }
170
- if (on_error)
171
- on_error(error, websocket);
172
- else if (print)
173
- console.log(error);
133
+ let pwebsocket = defer2();
134
+ websocket.addEventListener('open', event => {
135
+ if (print.connect)
136
+ console.log(websocket.url +
137
+ (websocket.protocol ? ' ' + websocket.protocol.bracket() : '') +
138
+ t(' 已连接'));
139
+ pwebsocket.resolve(websocket);
140
+ }, { once: true });
141
+ // websocket error, close 事件只选一个最详细的构造 WebSocketConnectionError 并调用 on_error
142
+ // error 事件会先于 close 事件
143
+ // node.js 环境下 error 事件的错误信息比较多,而浏览器环境下 close 的错误信息比较多
144
+ // 在浏览器环境下延后处理 error 事件,放到微任务队列之后的 timers 队列中
145
+ // https://blog.insiderattack.net/promises-next-ticks-and-immediates-nodejs-event-loop-part-3-9226cbe7a6aa
146
+ let errored;
147
+ function on_close_or_error(event) {
148
+ const { type } = event;
149
+ // 正常关闭
150
+ if (type === 'close' && event.code === 1000) {
151
+ if (on_close)
152
+ on_close(event, websocket);
153
+ else if (print.close)
154
+ console.log(`${websocket.url} ${t('已正常关闭')}`);
155
+ return;
174
156
  }
175
- // 检查实际情况下 error 事件会先于 close 事件,观察一段时间后可删去
176
- let closed = false;
177
- websocket.addEventListener('error', (event) => {
178
- if (closed)
179
- console.warn('close 事件先于 error 事件发生,奇怪!');
180
- if (nodejs)
181
- on_close_or_error(event);
182
- else
183
- setTimeout(() => { on_close_or_error(event); });
184
- }, { once: true });
185
- websocket.addEventListener('close', event => {
186
- closed = true;
157
+ // 已经调用过 on_error
158
+ if (errored)
159
+ return;
160
+ errored = type;
161
+ const error = new WebSocketConnectionError(websocket.url, protocols, event);
162
+ if (!pwebsocket.settled)
163
+ pwebsocket.reject(error);
164
+ else if (on_error)
165
+ on_error(error, websocket);
166
+ else if (print.error)
167
+ console.error(error.message);
168
+ }
169
+ websocket.addEventListener('error', (event) => {
170
+ if (nodejs)
187
171
  on_close_or_error(event);
188
- }, { once: true });
189
- function _on_message(event) {
190
- on_message(event.data, websocket);
191
- }
192
- websocket.addEventListener('message', _on_message);
172
+ else
173
+ setTimeout(() => { on_close_or_error(event); });
174
+ }, { once: true });
175
+ websocket.addEventListener('close', on_close_or_error, { once: true });
176
+ websocket.addEventListener('message', event => {
177
+ on_message(event.data, websocket);
193
178
  });
179
+ return pwebsocket;
194
180
  }
195
181
  /** 通过创建 remote 对象对 websocket rpc 进行抽象
196
182
  使用 remote.call() 进行一元 rpc
@@ -238,7 +224,7 @@ export class Remote {
238
224
  error;
239
225
  /** 作为 websocket 连接发起方,传入 url
240
226
  作为 websocket 连接接收方,传入 websocket + name */
241
- constructor({ name, url, websocket, funcs, print, verbose, probe, args, on_error, on_reconnect } = {}) {
227
+ constructor({ name, url, websocket, funcs, print, verbose, probe, args, on_error, on_reconnect, on_connected } = {}) {
242
228
  if (name)
243
229
  this.name = name;
244
230
  if (url) {
@@ -246,17 +232,19 @@ export class Remote {
246
232
  this.url = url;
247
233
  if (on_reconnect)
248
234
  this.on_reconnect = on_reconnect;
235
+ if (on_connected)
236
+ this.on_connected = on_connected;
249
237
  }
250
238
  else { // 连接接收方,一定是 nodejs 环境
251
239
  check(websocket, '构建 Remote 时需传入 url 或者 websocket');
252
240
  this.lwebsocket.resource = websocket;
253
241
  websocket.binaryType = 'arraybuffer';
254
242
  // 参考 connect_websocket 实现
255
- let errored = false;
243
+ let errored;
256
244
  const on_close_or_error = (event) => {
257
- websocket.removeEventListener('message', _on_message);
245
+ const { type } = event;
258
246
  // 正常关闭
259
- if (event.type === 'close' && event.code === 1000) {
247
+ if (type === 'close' && event.code === 1000) {
260
248
  if (print)
261
249
  console.log(`${this.name || websocket.url || '传入连接'} 已正常关闭`);
262
250
  return;
@@ -264,24 +252,14 @@ export class Remote {
264
252
  // 已经调用过 on_error 了
265
253
  if (errored)
266
254
  return;
267
- errored = true;
255
+ errored = type;
268
256
  this._on_error(new WebSocketConnectionError(this.name || websocket.url, websocket.protocol ? [websocket.protocol] : undefined, event));
269
257
  };
270
- // 检查实际情况下 error 事件会先于 close 事件,观察一段时间后可删去
271
- let closed = false;
272
- websocket.addEventListener('error', (event) => {
273
- if (closed)
274
- console.warn('close 事件先于 error 事件发生,奇怪!');
275
- on_close_or_error(event);
276
- }, { once: true });
277
- websocket.addEventListener('close', event => {
278
- closed = true;
279
- on_close_or_error(event);
280
- }, { once: true });
281
- const _on_message = (event) => {
258
+ websocket.addEventListener('error', on_close_or_error, { once: true });
259
+ websocket.addEventListener('close', on_close_or_error, { once: true });
260
+ websocket.addEventListener('message', event => {
282
261
  this._on_message(event.data);
283
- };
284
- websocket.addEventListener('message', _on_message);
262
+ });
285
263
  }
286
264
  if (funcs) {
287
265
  check(!funcs.echo);
@@ -316,6 +294,7 @@ export class Remote {
316
294
  this.on_error(error, this);
317
295
  };
318
296
  on_reconnect;
297
+ on_connected;
319
298
  reconnect = async () => {
320
299
  this.reconnecting = null;
321
300
  if (this.on_reconnect)
@@ -389,6 +368,7 @@ export class Remote {
389
368
  this.error = null;
390
369
  if (this.args)
391
370
  await this.call('register', this.args);
371
+ this.on_connected?.();
392
372
  };
393
373
  /** 尝试建立连接,通常用于开始保活
394
374
  重连错误会在 this.connect 里面调用 this._on_error 处理 */
package/net.d.ts CHANGED
@@ -2,7 +2,7 @@ import net from 'node:net';
2
2
  import { type Readable } from 'node:stream';
3
3
  import type { Cookie, CookieJar, MemoryCookieStore } from 'tough-cookie';
4
4
  import type { Encoding } from './file.ts';
5
- import { inspect, type Deferred2 } from './utils.ts';
5
+ import { inspect, type Deferred2, type OnTimeout } from './utils.ts';
6
6
  import { type BasicAuth, type BearerAuth } from './net.common.ts';
7
7
  export * from './net.common.ts';
8
8
  export declare enum MyProxy {
@@ -111,7 +111,9 @@ export declare function request(url: string | URL, options: RequestOptions): Pro
111
111
  /** 发起 http 请求并将响应体作为 json 解析 */
112
112
  export declare function request_json<T = any>(url: string | URL, options?: RequestOptions): Promise<T>;
113
113
  export interface ConnectOptions {
114
+ local_port?: number;
114
115
  timeout?: number;
116
+ on_timeout?: OnTimeout;
115
117
  print?: {
116
118
  error: boolean;
117
119
  connect: boolean;
@@ -119,12 +121,14 @@ export interface ConnectOptions {
119
121
  }
120
122
  /** 建立 tcp 连接,超时时间为 2 秒 (timeout 选项),返回已连接的 socket
121
123
  连接中遇到的错误会 reject,之后取消监听 error 事件,由调用者 attach 处理
124
+ 连接超时则自动 socket.resetAndDestroy
122
125
  - hostname: 要连接到的 ip 或域名
123
126
  - port: 要连接的端口
124
127
  - options?:
128
+ - local_port?: `随机端口` 使用的本地端口
125
129
  - timeout?: `2000` 超时时间
126
130
  - print?: 打印连接日志,错误日志
127
131
  - error?: `true`
128
132
  - connect: `false` */
129
- export declare function connect(hostname: string, port: number, { timeout: _timeout, print }?: ConnectOptions): Deferred2<net.Socket>;
133
+ export declare function connect(hostname: string, port: number, { local_port, timeout: _timeout, on_timeout, print }?: ConnectOptions): Deferred2<net.Socket>;
130
134
  export declare function get_socket_ip(socket: net.Socket): string;
package/net.js CHANGED
@@ -3,7 +3,7 @@ import net from 'node:net';
3
3
  import { buffer as stream_to_buffer, text as stream_to_text } from 'node:stream/consumers';
4
4
  import { pipeline, isReadable } from 'node:stream';
5
5
  import { noop } from "./prototype.js";
6
- import { inspect, assert, delay, map_values, unique, timeout, check, colored, encode, TimeoutError, set_error_message, defer2 } from "./utils.js";
6
+ import { inspect, assert, delay, map_values, unique, timeout, check, colored, encode, TimeoutError, set_error_message, defer2, nowstr } from "./utils.js";
7
7
  import { drop_request_headers } from "./net.common.js";
8
8
  export * from "./net.common.js";
9
9
  export var MyProxy;
@@ -295,22 +295,25 @@ export async function request_json(url, options) {
295
295
  }
296
296
  /** 建立 tcp 连接,超时时间为 2 秒 (timeout 选项),返回已连接的 socket
297
297
  连接中遇到的错误会 reject,之后取消监听 error 事件,由调用者 attach 处理
298
+ 连接超时则自动 socket.resetAndDestroy
298
299
  - hostname: 要连接到的 ip 或域名
299
300
  - port: 要连接的端口
300
301
  - options?:
302
+ - local_port?: `随机端口` 使用的本地端口
301
303
  - timeout?: `2000` 超时时间
302
304
  - print?: 打印连接日志,错误日志
303
305
  - error?: `true`
304
306
  - connect: `false` */
305
- export function connect(hostname, port, { timeout: _timeout = 2000, print = { error: true, connect: false } } = {}) {
307
+ export function connect(hostname, port, { local_port, timeout: _timeout = 2000, on_timeout, print = { error: true, connect: false } } = {}) {
306
308
  let pconnected = defer2();
307
309
  let timeouted = false;
308
310
  function get_message(message) {
309
- return `tcp://${hostname}:${port} ${message}`;
311
+ return `${nowstr()} tcp://${hostname}:${port} ${message}`;
310
312
  }
311
313
  let socket = net.connect({
312
314
  host: hostname,
313
315
  port,
316
+ localPort: local_port,
314
317
  noDelay: true
315
318
  }, () => {
316
319
  if (pconnected.settled) {
@@ -341,11 +344,21 @@ export function connect(hostname, port, { timeout: _timeout = 2000, print = { er
341
344
  }
342
345
  };
343
346
  socket.once('error', on_error);
344
- let timeout = setTimeout(() => {
347
+ let timeout = setTimeout(async () => {
345
348
  // 执行到这里一定没有 settled
346
349
  timeouted = true;
347
350
  socket.resetAndDestroy();
348
- pconnected.reject(new TimeoutError(get_message('超时了')));
351
+ if (typeof on_timeout === 'function') {
352
+ pconnected.resolve(null);
353
+ try {
354
+ await on_timeout(new TimeoutError(get_message('超时了')));
355
+ }
356
+ catch (error) {
357
+ pconnected.reject(error);
358
+ }
359
+ }
360
+ else
361
+ pconnected.reject(new TimeoutError(on_timeout || get_message('超时了')));
349
362
  }, _timeout);
350
363
  return pconnected;
351
364
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xshell",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "bin": {
@@ -53,12 +53,12 @@
53
53
  "@babel/parser": "^7.28.5",
54
54
  "@babel/traverse": "^7.28.5",
55
55
  "@koa/cors": "^5.0.0",
56
- "@stylistic/eslint-plugin": "^5.5.0",
56
+ "@stylistic/eslint-plugin": "^5.6.1",
57
57
  "@svgr/webpack": "^8.1.0",
58
58
  "@types/sass-loader": "^8.0.10",
59
- "@typescript-eslint/eslint-plugin": "^8.46.3",
60
- "@typescript-eslint/parser": "^8.46.3",
61
- "@typescript-eslint/utils": "^8.46.3",
59
+ "@typescript-eslint/eslint-plugin": "^8.47.0",
60
+ "@typescript-eslint/parser": "^8.47.0",
61
+ "@typescript-eslint/utils": "^8.47.0",
62
62
  "archiver": "^7.0.1",
63
63
  "chalk": "^5.6.2",
64
64
  "commander": "^14.0.2",
@@ -68,17 +68,17 @@
68
68
  "eslint-plugin-import": "^2.32.0",
69
69
  "eslint-plugin-react": "^7.37.5",
70
70
  "https-proxy-agent": "^7.0.6",
71
- "i18next": "^25.6.1",
71
+ "i18next": "^25.6.3",
72
72
  "i18next-scanner": "^4.6.0",
73
73
  "koa": "^3.1.1",
74
74
  "koa-compress": "^5.1.1",
75
75
  "license-webpack-plugin": "^4.0.2",
76
76
  "mime-types": "^3.0.1",
77
- "p-map": "^7.0.3",
77
+ "p-map": "^7.0.4",
78
78
  "react": "^19.2.0",
79
- "react-i18next": "^16.2.4",
79
+ "react-i18next": "^16.3.4",
80
80
  "resolve-path": "^1.4.0",
81
- "sass": "^1.93.3",
81
+ "sass": "^1.94.1",
82
82
  "sass-loader": "^16.0.6",
83
83
  "source-map-loader": "^5.0.0",
84
84
  "strip-ansi": "^7.1.2",
@@ -88,8 +88,8 @@
88
88
  "tslib": "^2.8.1",
89
89
  "typescript": "^5.9.3",
90
90
  "undici": "^7.16.0",
91
- "webpack": "^5.102.1",
92
- "webpack-bundle-analyzer": "^4.10.2",
91
+ "webpack": "^5.103.0",
92
+ "webpack-bundle-analyzer": "^5.0.1",
93
93
  "ws": "^8.18.3"
94
94
  },
95
95
  "devDependencies": {
@@ -101,10 +101,10 @@
101
101
  "@types/koa": "^3.0.1",
102
102
  "@types/koa-compress": "^4.0.7",
103
103
  "@types/mime-types": "^3.0.1",
104
- "@types/node": "^24.10.0",
105
- "@types/react": "^19.2.2",
104
+ "@types/node": "^24.10.1",
105
+ "@types/react": "^19.2.6",
106
106
  "@types/tough-cookie": "^4.0.5",
107
- "@types/vscode": "^1.105.0",
107
+ "@types/vscode": "^1.106.1",
108
108
  "@types/webpack-bundle-analyzer": "^4.7.0",
109
109
  "@types/ws": "^8.18.1"
110
110
  }
package/utils.common.d.ts CHANGED
@@ -63,6 +63,7 @@ export declare function decode(buffer: Uint8Array): string;
63
63
  /** 比较两个 buffer 内容是否相同,第二个可以传入 string 自动编码转换后比较,
64
64
  高频调用时建议提前编码 right 并缓存 */
65
65
  export declare function buffer_equals(left: Uint8Array, right: Uint8Array | string): boolean;
66
+ export type OnTimeout = string | ((error: TimeoutError) => void | Promise<void>);
66
67
  /** 在指定的时间 (milliseconds) 内运行某个任务,超时之后抛出错误或调用 on_timeout
67
68
  - milliseconds: 限时毫秒数
68
69
  - action?: 要等待运行的任务, async function 或 promise
@@ -74,7 +75,7 @@ export declare function buffer_equals(left: Uint8Array, right: Uint8Array | stri
74
75
  - on_timeout 报错: timeout 函数最终抛出这个错误
75
76
  - 若不传: 直接抛出 TimeoutError
76
77
  - print?: `true` 打印已超时任务的错误 */
77
- export declare function timeout<TReturn>(milliseconds: number, action: Promise<TReturn> | (() => Promise<TReturn>), on_timeout?: string | ((error: TimeoutError) => void | Promise<void>), print?: boolean): Deferred2<TReturn>;
78
+ export declare function timeout<TReturn>(milliseconds: number, action: Promise<TReturn> | (() => Promise<TReturn>), on_timeout?: OnTimeout, print?: boolean): Deferred2<TReturn>;
78
79
  /** 轮询尝试 action 共 times 次,每次间隔 duration
79
80
  action 返回 trusy 值时认为成功,返回 action 的结果
80
81
  如果次数用尽仍然失败,返回 null */
package/utils.common.js CHANGED
@@ -215,7 +215,7 @@ export function timeout(milliseconds, action, on_timeout, print = true) {
215
215
  paction.then(result => {
216
216
  if (presult.settled) {
217
217
  if (print)
218
- console.log('已超时任务最终完成了');
218
+ console.log('已超时任务最终完成了:', result);
219
219
  }
220
220
  else if (!waiting_on_timeout) {
221
221
  presult.resolve(result);
package/utils.js CHANGED
@@ -31,14 +31,14 @@ export function typed_array_to_buffer(view) {
31
31
  - hour: 0 - 23 之间的整数,在这个点执行
32
32
  - action */
33
33
  export async function schedule_everyday(hour, action) {
34
- const now = new Date();
34
+ const now = Date.now();
35
35
  let target = new Date();
36
36
  target.setHours(hour, 0, 0, 0);
37
37
  // 如果目标时间已过,设定为明天的时间
38
- if (now.getTime() > target.getTime())
38
+ if (now > target.getTime())
39
39
  target.setDate(target.getDate() + 1);
40
40
  // 等时间到
41
- await delay(target.getTime() - now.getTime());
41
+ await delay(target.getTime() - now);
42
42
  for (;;) {
43
43
  try {
44
44
  await action();