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 +3 -1
- package/builder.js +2 -2
- package/development.js +5 -5
- package/file.js +4 -2
- package/fzip.js +1 -3
- package/net.common.d.ts +25 -29
- package/net.common.js +79 -99
- package/net.d.ts +6 -2
- package/net.js +18 -5
- package/package.json +14 -14
- package/utils.common.d.ts +2 -1
- package/utils.common.js +1 -1
- package/utils.js +3 -3
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
|
-
-
|
|
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
|
-
-
|
|
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 (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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,
|
|
751
|
-
.replaceAll(pattern, replacement),
|
|
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',
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
111
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
|
243
|
+
let errored;
|
|
256
244
|
const on_close_or_error = (event) => {
|
|
257
|
-
|
|
245
|
+
const { type } = event;
|
|
258
246
|
// 正常关闭
|
|
259
|
-
if (
|
|
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 =
|
|
255
|
+
errored = type;
|
|
268
256
|
this._on_error(new WebSocketConnectionError(this.name || websocket.url, websocket.protocol ? [websocket.protocol] : undefined, event));
|
|
269
257
|
};
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
websocket.addEventListener('
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
60
|
-
"@typescript-eslint/parser": "^8.
|
|
61
|
-
"@typescript-eslint/utils": "^8.
|
|
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.
|
|
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.
|
|
77
|
+
"p-map": "^7.0.4",
|
|
78
78
|
"react": "^19.2.0",
|
|
79
|
-
"react-i18next": "^16.
|
|
79
|
+
"react-i18next": "^16.3.4",
|
|
80
80
|
"resolve-path": "^1.4.0",
|
|
81
|
-
"sass": "^1.
|
|
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.
|
|
92
|
-
"webpack-bundle-analyzer": "^
|
|
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.
|
|
105
|
-
"@types/react": "^19.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.
|
|
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?:
|
|
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 =
|
|
34
|
+
const now = Date.now();
|
|
35
35
|
let target = new Date();
|
|
36
36
|
target.setHours(hour, 0, 0, 0);
|
|
37
37
|
// 如果目标时间已过,设定为明天的时间
|
|
38
|
-
if (now
|
|
38
|
+
if (now > target.getTime())
|
|
39
39
|
target.setDate(target.getDate() + 1);
|
|
40
40
|
// 等时间到
|
|
41
|
-
await delay(target.getTime() - now
|
|
41
|
+
await delay(target.getTime() - now);
|
|
42
42
|
for (;;) {
|
|
43
43
|
try {
|
|
44
44
|
await action();
|