xshell 1.3.8 → 1.3.9
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/net.common.d.ts +22 -11
- package/net.common.js +26 -30
- package/net.d.ts +3 -1
- package/net.js +5 -2
- package/package.json +3 -3
- package/utils.d.ts +3 -16
- package/utils.js +9 -124
package/net.common.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
import type { Socket } from 'node:net';
|
|
1
2
|
import { type Message } from './io.common.ts';
|
|
2
3
|
import { Lock, TimeoutError } from './utils.common.ts';
|
|
4
|
+
declare global {
|
|
5
|
+
interface WebSocket {
|
|
6
|
+
_socket?: Socket;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
3
9
|
/** 对于 request() 函数来说无意义的 headers,会自动过滤掉 */
|
|
4
10
|
export declare const drop_request_headers: Set<string>;
|
|
5
11
|
export interface BasicAuth {
|
|
@@ -42,6 +48,7 @@ export interface ConnectWebsocketOptions {
|
|
|
42
48
|
protocols?: string[];
|
|
43
49
|
max_payload?: number;
|
|
44
50
|
proxy?: string;
|
|
51
|
+
keep_alive_duration?: number;
|
|
45
52
|
on_message(data: ArrayBuffer | string, websocket: WebSocket): any;
|
|
46
53
|
on_error?(error: WebSocketConnectionError, websocket: WebSocket): any;
|
|
47
54
|
on_close?(event: CloseEvent, websocket: WebSocket): any;
|
|
@@ -61,6 +68,7 @@ export interface ConnectWebsocketOptions {
|
|
|
61
68
|
- protocols?
|
|
62
69
|
- max_payload?: `8 gb` (仅 nodejs 环境有效)
|
|
63
70
|
- proxy?: string (仅 nodejs 环境有效)
|
|
71
|
+
- keep_alive_duration?: 毫秒数,设置后启用操作系统级的 keep alive (仅 nodejs 环境可以有效,浏览器端默认已启用)
|
|
64
72
|
- on_message: 根据 websocket frame 的 opcode 不同 (text frame 或 binary frame),event 中的 data 对应为 ArrayBuffer 或者 string
|
|
65
73
|
https://datatracker.ietf.org/doc/html/rfc6455#section-5.2
|
|
66
74
|
- on_error?: 在 websocket 出错和非正常关闭 (close, error 事件) 时都调用,可以根据 error.type 来区分,error 的类型是 WebSocketConnectionError,
|
|
@@ -69,7 +77,7 @@ export interface ConnectWebsocketOptions {
|
|
|
69
77
|
https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes
|
|
70
78
|
- connect?: nodejs 环境下需要指定 ip, port 手动连接 tls server 时使用,有 agent 时不生效 */
|
|
71
79
|
export declare function connect_websocket(url: string | URL, { print, protocols, max_payload, // 8 GB
|
|
72
|
-
proxy, on_message, on_error, on_close, connect }: ConnectWebsocketOptions): Promise<WebSocket>;
|
|
80
|
+
proxy, keep_alive_duration, on_message, on_error, on_close, connect }: ConnectWebsocketOptions): Promise<WebSocket>;
|
|
73
81
|
/** 接收到消息后的处理函数
|
|
74
82
|
返回值会自动被 await,然后可能被封装为 message 回传 */
|
|
75
83
|
export type MessageHandler<TData = any> = (message: Message<TData>, remote: Remote) => void | any | Promise<void | any>;
|
|
@@ -82,8 +90,12 @@ export interface RemoteOptions {
|
|
|
82
90
|
websocket?: WebSocket;
|
|
83
91
|
/** 能被对端调用的函数 */
|
|
84
92
|
funcs?: Remote['funcs'];
|
|
85
|
-
/**
|
|
86
|
-
|
|
93
|
+
/** 设置后启用操作系统级的 keep alive (仅 nodejs 环境可设置)
|
|
94
|
+
- 浏览器端有默认 tcp keep alive 心跳机制,禁止设置这个参数
|
|
95
|
+
- nodejs 端
|
|
96
|
+
- 作为连接发起方: 在 websocket 连接建立时启用
|
|
97
|
+
- 作为连接接收方: 在根据传入 websocket 创建 Remote 时启用 */
|
|
98
|
+
alive?: boolean;
|
|
87
99
|
/** websocket 连接建立成功时调用对端 register 函数的参数 (仅发起方有效) */
|
|
88
100
|
args?: any[];
|
|
89
101
|
/** `true` 是否打印连接信息、错误信息 */
|
|
@@ -95,7 +107,8 @@ export interface RemoteOptions {
|
|
|
95
107
|
on_error?(error: WebSocketConnectionError | Error, remote: Remote): void;
|
|
96
108
|
/** 使用者自定义的,在重连之前调用的函数,比如可以修改 url,仅连接发起方有效 */
|
|
97
109
|
on_reconnect?(this: Remote): Promise<void>;
|
|
98
|
-
/** 使用者自定义的,在连接建立,包括首次连接和重连
|
|
110
|
+
/** 使用者自定义的,在连接建立,包括首次连接和重连 之后调用的函数,
|
|
111
|
+
比如可以重新调用一些函数,仅连接发起方有效 */
|
|
99
112
|
on_connected?(this: Remote): void;
|
|
100
113
|
}
|
|
101
114
|
/** 通过创建 remote 对象对 websocket rpc 进行抽象
|
|
@@ -120,8 +133,8 @@ export declare class Remote {
|
|
|
120
133
|
url?: string;
|
|
121
134
|
/** 能被对端调用的函数 */
|
|
122
135
|
funcs: Record<string, MessageHandler>;
|
|
123
|
-
/**
|
|
124
|
-
|
|
136
|
+
/** tcp keep alive */
|
|
137
|
+
alive: boolean;
|
|
125
138
|
/** websocket 连接建立成功时调用对端 register 函数的参数 (仅发起方有效) */
|
|
126
139
|
args?: any[];
|
|
127
140
|
/** `true` 是否打印连接信息、错误信息 */
|
|
@@ -133,8 +146,6 @@ export declare class Remote {
|
|
|
133
146
|
/** map<id, message handler>: 通过 (rpc message).id 找到对应的 handler
|
|
134
147
|
一元 rpc 接收方不需要设置 handlers, 发送方需要 */
|
|
135
148
|
handlers: Map<number, MessageHandler<any>>;
|
|
136
|
-
/** 是否正在定时主动检测重连 */
|
|
137
|
-
probing: boolean;
|
|
138
149
|
/** websocket 检测到错误,_on_error 被调用,此时会尝试一段时间后重连 */
|
|
139
150
|
reconnecting: NodeJS.Timeout;
|
|
140
151
|
/** 用户是否手动调用了 disconnect() 主动断开了连接 (仅发起方有效) */
|
|
@@ -143,7 +154,7 @@ export declare class Remote {
|
|
|
143
154
|
error: WebSocketConnectionError | Error;
|
|
144
155
|
/** 作为 websocket 连接发起方,传入 url
|
|
145
156
|
作为 websocket 连接接收方,传入 websocket + name */
|
|
146
|
-
constructor({ name, url, websocket, funcs, print, verbose,
|
|
157
|
+
constructor({ name, url, websocket, funcs, print, verbose, alive, args, on_error, on_reconnect, on_connected }?: RemoteOptions);
|
|
147
158
|
/** 统一处理首次连接和连接后的 websocket 错误 */
|
|
148
159
|
_on_error: (error: WebSocketConnectionError | Error) => void;
|
|
149
160
|
on_reconnect?: (this: Remote) => Promise<void>;
|
|
@@ -168,13 +179,13 @@ export declare class Remote {
|
|
|
168
179
|
/** 尝试建立连接,通常用于开始保活
|
|
169
180
|
重连错误会在 this.connect 里面调用 this._on_error 处理 */
|
|
170
181
|
try_connect(): Promise<void>;
|
|
171
|
-
/** 开始心跳保活 */
|
|
172
|
-
probe_connection(print_timeout?: boolean): Promise<void>;
|
|
173
182
|
/** 检测连接,返回连接错误,出错时触发 on_error 打印错误信息
|
|
174
183
|
返回值:
|
|
175
184
|
- 连接正常返回: undefined
|
|
176
185
|
- 连接错误返回: 实际的连接错误,通常是 WebSocketConnectionError | TimeoutError */
|
|
177
186
|
test(print_timeout?: boolean): Promise<WebSocketConnectionError | TimeoutError | Error>;
|
|
187
|
+
/** node.js 下连接接收方在 Remote 创建后手动启用 tcp keep alive */
|
|
188
|
+
keep_alive(): void;
|
|
178
189
|
/** 手动关闭到对端的 websocket 连接 */
|
|
179
190
|
disconnect(): void;
|
|
180
191
|
/** 发送 message 到对端 remote
|
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, defer2,
|
|
4
|
+
import { check, defer, defer2, 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([
|
|
@@ -94,6 +94,7 @@ let websocket_proxy_agents = {};
|
|
|
94
94
|
- protocols?
|
|
95
95
|
- max_payload?: `8 gb` (仅 nodejs 环境有效)
|
|
96
96
|
- proxy?: string (仅 nodejs 环境有效)
|
|
97
|
+
- keep_alive_duration?: 毫秒数,设置后启用操作系统级的 keep alive (仅 nodejs 环境可以有效,浏览器端默认已启用)
|
|
97
98
|
- on_message: 根据 websocket frame 的 opcode 不同 (text frame 或 binary frame),event 中的 data 对应为 ArrayBuffer 或者 string
|
|
98
99
|
https://datatracker.ietf.org/doc/html/rfc6455#section-5.2
|
|
99
100
|
- on_error?: 在 websocket 出错和非正常关闭 (close, error 事件) 时都调用,可以根据 error.type 来区分,error 的类型是 WebSocketConnectionError,
|
|
@@ -106,7 +107,7 @@ export async function connect_websocket(url, { print = {
|
|
|
106
107
|
error: true,
|
|
107
108
|
close: true
|
|
108
109
|
}, protocols, max_payload = 2 ** 33, // 8 GB
|
|
109
|
-
proxy, on_message, on_error, on_close, connect }) {
|
|
110
|
+
proxy, keep_alive_duration, on_message, on_error, on_close, connect }) {
|
|
110
111
|
const { nodejs } = platform;
|
|
111
112
|
if (typeof print === 'boolean')
|
|
112
113
|
print = {
|
|
@@ -114,6 +115,8 @@ proxy, on_message, on_error, on_close, connect }) {
|
|
|
114
115
|
error: print,
|
|
115
116
|
close: print
|
|
116
117
|
};
|
|
118
|
+
if (keep_alive_duration)
|
|
119
|
+
check(nodejs, 'keep_alive_duration 仅 nodejs 环境有效');
|
|
117
120
|
let websocket = new (await platform.get_websocket())(url, protocols,
|
|
118
121
|
// @ts-ignore
|
|
119
122
|
nodejs ?
|
|
@@ -136,6 +139,8 @@ proxy, on_message, on_error, on_close, connect }) {
|
|
|
136
139
|
console.log(websocket.url +
|
|
137
140
|
(websocket.protocol ? ' ' + websocket.protocol.bracket() : '') +
|
|
138
141
|
t(' 已连接'));
|
|
142
|
+
if (keep_alive_duration)
|
|
143
|
+
websocket._socket.setKeepAlive(true, keep_alive_duration);
|
|
139
144
|
pwebsocket.resolve(websocket);
|
|
140
145
|
}, { once: true });
|
|
141
146
|
// websocket error, close 事件只选一个最详细的构造 WebSocketConnectionError 并调用 on_error
|
|
@@ -200,8 +205,8 @@ export class Remote {
|
|
|
200
205
|
url;
|
|
201
206
|
/** 能被对端调用的函数 */
|
|
202
207
|
funcs;
|
|
203
|
-
/**
|
|
204
|
-
|
|
208
|
+
/** tcp keep alive */
|
|
209
|
+
alive = false;
|
|
205
210
|
/** websocket 连接建立成功时调用对端 register 函数的参数 (仅发起方有效) */
|
|
206
211
|
args;
|
|
207
212
|
/** `true` 是否打印连接信息、错误信息 */
|
|
@@ -214,8 +219,6 @@ export class Remote {
|
|
|
214
219
|
/** map<id, message handler>: 通过 (rpc message).id 找到对应的 handler
|
|
215
220
|
一元 rpc 接收方不需要设置 handlers, 发送方需要 */
|
|
216
221
|
handlers = new Map();
|
|
217
|
-
/** 是否正在定时主动检测重连 */
|
|
218
|
-
probing = false;
|
|
219
222
|
/** websocket 检测到错误,_on_error 被调用,此时会尝试一段时间后重连 */
|
|
220
223
|
reconnecting;
|
|
221
224
|
/** 用户是否手动调用了 disconnect() 主动断开了连接 (仅发起方有效) */
|
|
@@ -224,9 +227,13 @@ export class Remote {
|
|
|
224
227
|
error;
|
|
225
228
|
/** 作为 websocket 连接发起方,传入 url
|
|
226
229
|
作为 websocket 连接接收方,传入 websocket + name */
|
|
227
|
-
constructor({ name, url, websocket, funcs, print, verbose,
|
|
230
|
+
constructor({ name, url, websocket, funcs, print, verbose, alive, args, on_error, on_reconnect, on_connected } = {}) {
|
|
228
231
|
if (name)
|
|
229
232
|
this.name = name;
|
|
233
|
+
if (alive) {
|
|
234
|
+
check(!platform.browser, '浏览器端有默认 tcp keep alive 心跳机制,禁止设置 alive 选项');
|
|
235
|
+
this.alive = true;
|
|
236
|
+
}
|
|
230
237
|
if (url) {
|
|
231
238
|
check(!websocket, '构建 Remote 时 url 和 websocket 只能传其中一个');
|
|
232
239
|
this.url = url;
|
|
@@ -237,8 +244,10 @@ export class Remote {
|
|
|
237
244
|
}
|
|
238
245
|
else { // 连接接收方,一定是 nodejs 环境
|
|
239
246
|
check(websocket, '构建 Remote 时需传入 url 或者 websocket');
|
|
240
|
-
this.lwebsocket.resource = websocket;
|
|
241
247
|
websocket.binaryType = 'arraybuffer';
|
|
248
|
+
if (this.alive)
|
|
249
|
+
websocket._socket.setKeepAlive(true, 30_000);
|
|
250
|
+
this.lwebsocket.resource = websocket;
|
|
242
251
|
// 参考 connect_websocket 实现
|
|
243
252
|
let errored;
|
|
244
253
|
const on_close_or_error = (event) => {
|
|
@@ -265,8 +274,6 @@ export class Remote {
|
|
|
265
274
|
check(!funcs.echo);
|
|
266
275
|
this.funcs = funcs;
|
|
267
276
|
}
|
|
268
|
-
if (probe !== undefined)
|
|
269
|
-
this.probe = probe;
|
|
270
277
|
if (args)
|
|
271
278
|
this.args = args;
|
|
272
279
|
if (print !== undefined)
|
|
@@ -287,7 +294,7 @@ export class Remote {
|
|
|
287
294
|
if (error instanceof WebSocketConnectionError &&
|
|
288
295
|
!this.reconnecting &&
|
|
289
296
|
this.url &&
|
|
290
|
-
|
|
297
|
+
this.funcs) {
|
|
291
298
|
this.reconnecting = setTimeout(this.reconnect, this.error ? 20_000 : 2_000);
|
|
292
299
|
this.error = error;
|
|
293
300
|
}
|
|
@@ -335,9 +342,6 @@ export class Remote {
|
|
|
335
342
|
async connect() {
|
|
336
343
|
if (this.disconnected)
|
|
337
344
|
throw new Error(`remote (${this.name || this.url}) 已调用过 disconnect 断开连接,无法再次 connect`);
|
|
338
|
-
// 首次 connect 开始探测
|
|
339
|
-
if (this.probe && !this.probing)
|
|
340
|
-
this.probe_connection();
|
|
341
345
|
if (this.check_connection())
|
|
342
346
|
return;
|
|
343
347
|
try {
|
|
@@ -363,7 +367,8 @@ export class Remote {
|
|
|
363
367
|
this.lwebsocket.resource = await connect_websocket(this.url, {
|
|
364
368
|
on_message: this._on_message,
|
|
365
369
|
on_error: this._on_error,
|
|
366
|
-
print: this.print
|
|
370
|
+
print: this.print,
|
|
371
|
+
keep_alive_duration: this.alive ? 30_000 : undefined
|
|
367
372
|
});
|
|
368
373
|
this.error = null;
|
|
369
374
|
if (this.args)
|
|
@@ -378,21 +383,6 @@ export class Remote {
|
|
|
378
383
|
}
|
|
379
384
|
catch { }
|
|
380
385
|
}
|
|
381
|
-
/** 开始心跳保活 */
|
|
382
|
-
async probe_connection(print_timeout = true) {
|
|
383
|
-
// 手动调用该函数时也会启用 probe
|
|
384
|
-
this.probe = true;
|
|
385
|
-
this.probing = true;
|
|
386
|
-
for (let timeouted = false;;) {
|
|
387
|
-
await delay(timeouted ? 5_000 : 30_000);
|
|
388
|
-
if (this.disconnected || !this.url && !this.check_connection() || !this.probe)
|
|
389
|
-
break;
|
|
390
|
-
if (this.reconnecting)
|
|
391
|
-
continue;
|
|
392
|
-
// 产生的非 TimeoutError 应该都通过 _on_error 输出了,同时可能触发了重连
|
|
393
|
-
timeouted = (await this.test(print_timeout)) instanceof TimeoutError;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
386
|
/** 检测连接,返回连接错误,出错时触发 on_error 打印错误信息
|
|
397
387
|
返回值:
|
|
398
388
|
- 连接正常返回: undefined
|
|
@@ -416,6 +406,12 @@ export class Remote {
|
|
|
416
406
|
return error;
|
|
417
407
|
}
|
|
418
408
|
}
|
|
409
|
+
/** node.js 下连接接收方在 Remote 创建后手动启用 tcp keep alive */
|
|
410
|
+
keep_alive() {
|
|
411
|
+
check(!this.url && platform.nodejs);
|
|
412
|
+
this.alive = true;
|
|
413
|
+
this.lwebsocket.resource?._socket.setKeepAlive(true, 30_000);
|
|
414
|
+
}
|
|
419
415
|
/** 手动关闭到对端的 websocket 连接 */
|
|
420
416
|
disconnect() {
|
|
421
417
|
this.disconnected = true;
|
package/net.d.ts
CHANGED
|
@@ -114,6 +114,7 @@ export interface ConnectOptions {
|
|
|
114
114
|
local_port?: number;
|
|
115
115
|
timeout?: number;
|
|
116
116
|
on_timeout?: OnTimeout;
|
|
117
|
+
keep_alive_duration?: number;
|
|
117
118
|
print?: {
|
|
118
119
|
error: boolean;
|
|
119
120
|
connect: boolean;
|
|
@@ -127,8 +128,9 @@ export interface ConnectOptions {
|
|
|
127
128
|
- options?:
|
|
128
129
|
- local_port?: `随机端口` 使用的本地端口
|
|
129
130
|
- timeout?: `2000` 超时时间
|
|
131
|
+
- keep_alive_duration?: 毫秒数,设置后启用操作系统级的 keep alive
|
|
130
132
|
- print?: 打印连接日志,错误日志
|
|
131
133
|
- error?: `true`
|
|
132
134
|
- connect: `false` */
|
|
133
|
-
export declare function connect(hostname: string, port: number, { local_port, timeout: _timeout, on_timeout, print }?: ConnectOptions): Deferred2<net.Socket>;
|
|
135
|
+
export declare function connect(hostname: string, port: number, { local_port, timeout: _timeout, on_timeout, keep_alive_duration, print }?: ConnectOptions): Deferred2<net.Socket>;
|
|
134
136
|
export declare function get_socket_ip(socket: net.Socket): string;
|
package/net.js
CHANGED
|
@@ -301,10 +301,11 @@ export async function request_json(url, options) {
|
|
|
301
301
|
- options?:
|
|
302
302
|
- local_port?: `随机端口` 使用的本地端口
|
|
303
303
|
- timeout?: `2000` 超时时间
|
|
304
|
+
- keep_alive_duration?: 毫秒数,设置后启用操作系统级的 keep alive
|
|
304
305
|
- print?: 打印连接日志,错误日志
|
|
305
306
|
- error?: `true`
|
|
306
307
|
- connect: `false` */
|
|
307
|
-
export function connect(hostname, port, { local_port, timeout: _timeout = 2000, on_timeout, print = { error: true, connect: false } } = {}) {
|
|
308
|
+
export function connect(hostname, port, { local_port, timeout: _timeout = 2000, on_timeout, keep_alive_duration, print = { error: true, connect: false } } = {}) {
|
|
308
309
|
let pconnected = defer2();
|
|
309
310
|
let timeouted = false;
|
|
310
311
|
function get_message(message) {
|
|
@@ -314,7 +315,9 @@ export function connect(hostname, port, { local_port, timeout: _timeout = 2000,
|
|
|
314
315
|
host: hostname,
|
|
315
316
|
port,
|
|
316
317
|
localPort: local_port,
|
|
317
|
-
noDelay: true
|
|
318
|
+
noDelay: true,
|
|
319
|
+
keepAlive: Boolean(keep_alive_duration),
|
|
320
|
+
keepAliveInitialDelay: keep_alive_duration
|
|
318
321
|
}, () => {
|
|
319
322
|
if (pconnected.settled) {
|
|
320
323
|
if (print)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xshell",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -76,9 +76,9 @@
|
|
|
76
76
|
"mime-types": "^3.0.1",
|
|
77
77
|
"p-map": "^7.0.4",
|
|
78
78
|
"react": "^19.2.0",
|
|
79
|
-
"react-i18next": "^16.3.
|
|
79
|
+
"react-i18next": "^16.3.5",
|
|
80
80
|
"resolve-path": "^1.4.0",
|
|
81
|
-
"sass": "^1.94.
|
|
81
|
+
"sass": "^1.94.2",
|
|
82
82
|
"sass-loader": "^16.0.6",
|
|
83
83
|
"source-map-loader": "^5.0.0",
|
|
84
84
|
"strip-ansi": "^7.1.2",
|
package/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Writable, Transform, type Readable, type
|
|
1
|
+
import { Writable, Transform, type Readable, type TransformCallback } from 'node:stream';
|
|
2
2
|
import util from 'node:util';
|
|
3
3
|
import './prototype.ts';
|
|
4
4
|
import './platform.ts';
|
|
@@ -31,19 +31,8 @@ export declare namespace inspect {
|
|
|
31
31
|
const custom: typeof util.inspect.custom;
|
|
32
32
|
const defaultOptions: util.InspectOptions;
|
|
33
33
|
}
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
reduce is more tricky
|
|
38
|
-
maybe we want to group the reductions or emit progress updates occasionally
|
|
39
|
-
the most basic reduce just emits one 'data' event after it has recieved 'end'
|
|
40
|
-
|
|
41
|
-
create an event stream and apply function to each .write,
|
|
42
|
-
emitting each response as data unless it's an empty callback
|
|
43
|
-
*/
|
|
44
|
-
export declare function map_stream<Out, In = any>(mapper: (obj: In, cb: Function) => any, options?: {
|
|
45
|
-
failures?: boolean;
|
|
46
|
-
}): Duplex;
|
|
34
|
+
/** 消费一个可读流 */
|
|
35
|
+
export declare function consume_stream(stream: Readable, ignore_error?: boolean): Promise<void>;
|
|
47
36
|
export declare function stream_to_lines(stream: Readable): AsyncGenerator<string, void, unknown>;
|
|
48
37
|
export declare class WritableMemoryStream extends Writable {
|
|
49
38
|
chunks: Uint8Array[];
|
|
@@ -65,8 +54,6 @@ export declare class DecoderStream extends Transform {
|
|
|
65
54
|
_transform(chunk: Uint8Array, encoding: BufferEncoding, callback: TransformCallback): void;
|
|
66
55
|
_flush(callback: TransformCallback): void;
|
|
67
56
|
}
|
|
68
|
-
/** 消费一个可读流 */
|
|
69
|
-
export declare function consume_stream(stream: Readable, ignore_error?: boolean): Promise<void>;
|
|
70
57
|
/** 根据 range 生成整数序列 (iterable)
|
|
71
58
|
- range: 取值为逗号分割的多个可用值或值区间 (不能含有空格),比如:`8321,8322,8300-8310,11000-11999`
|
|
72
59
|
- reverse?: `false` 在 range 内从后往前生成 */
|
package/utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Writable, Transform } from 'node:stream';
|
|
2
2
|
import util from 'node:util';
|
|
3
3
|
import ncrypto from 'node:crypto';
|
|
4
4
|
import "./prototype.js";
|
|
@@ -94,120 +94,16 @@ export function colored(str, color, enabled = inspect.defaultOptions.colors) {
|
|
|
94
94
|
inspect.defaultOptions = util.inspect.defaultOptions;
|
|
95
95
|
})(inspect || (inspect = {}));
|
|
96
96
|
// ------------------------------------ stream
|
|
97
|
-
/**
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
the most basic reduce just emits one 'data' event after it has recieved 'end'
|
|
103
|
-
|
|
104
|
-
create an event stream and apply function to each .write,
|
|
105
|
-
emitting each response as data unless it's an empty callback
|
|
106
|
-
*/
|
|
107
|
-
export function map_stream(mapper, options) {
|
|
108
|
-
options = options || {};
|
|
109
|
-
let inputs = 0, outputs = 0, ended = false, paused = false, destroyed = false, last_written = 0, in_next = false;
|
|
110
|
-
let stream = Object.assign(new Stream(), {
|
|
111
|
-
readable: true,
|
|
112
|
-
writable: true,
|
|
113
|
-
write(data) {
|
|
114
|
-
if (ended)
|
|
115
|
-
throw new Error('map stream is not writable');
|
|
116
|
-
in_next = false;
|
|
117
|
-
inputs++;
|
|
118
|
-
try {
|
|
119
|
-
// catch sync errors and handle them like async errors
|
|
120
|
-
const written = wrapped_mapper(data, inputs, next);
|
|
121
|
-
paused = (written === false);
|
|
122
|
-
return !paused;
|
|
123
|
-
}
|
|
124
|
-
catch (err) {
|
|
125
|
-
// if the callback has been called syncronously, and the error has occured in an listener, throw it again.
|
|
126
|
-
if (in_next)
|
|
127
|
-
throw err;
|
|
128
|
-
next(err);
|
|
129
|
-
return !paused;
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
end(data) {
|
|
133
|
-
if (ended)
|
|
134
|
-
return;
|
|
135
|
-
_end(data);
|
|
136
|
-
},
|
|
137
|
-
destroy() {
|
|
138
|
-
ended = destroyed = true;
|
|
139
|
-
stream.writable = stream.readable = paused = false;
|
|
140
|
-
process.nextTick(function () {
|
|
141
|
-
stream.emit('close');
|
|
142
|
-
});
|
|
143
|
-
},
|
|
144
|
-
pause() {
|
|
145
|
-
paused = true;
|
|
146
|
-
},
|
|
147
|
-
resume() {
|
|
148
|
-
paused = false;
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
let error_event_name = options.failures ? 'failure' : 'error';
|
|
152
|
-
// Items that are not ready to be written yet (because they would come out of order) get stuck in a queue for later.
|
|
153
|
-
let write_queue = {};
|
|
154
|
-
function queue_data(data, number) {
|
|
155
|
-
let next_to_write = last_written + 1;
|
|
156
|
-
if (number === next_to_write) {
|
|
157
|
-
// If it's next, and its not undefined write it
|
|
158
|
-
if (data !== undefined)
|
|
159
|
-
stream.emit('data', data);
|
|
160
|
-
last_written++;
|
|
161
|
-
next_to_write++;
|
|
162
|
-
}
|
|
163
|
-
else
|
|
164
|
-
// Otherwise queue it for later.
|
|
165
|
-
write_queue[number] = data;
|
|
166
|
-
// If the next value is in the queue, write it
|
|
167
|
-
if (Object.prototype.hasOwnProperty.call(write_queue, next_to_write)) {
|
|
168
|
-
let data_to_write = write_queue[next_to_write];
|
|
169
|
-
delete write_queue[next_to_write];
|
|
170
|
-
return queue_data(data_to_write, next_to_write);
|
|
171
|
-
}
|
|
172
|
-
outputs++;
|
|
173
|
-
if (inputs === outputs) {
|
|
174
|
-
if (paused) {
|
|
175
|
-
paused = false;
|
|
176
|
-
stream.emit('drain'); // written all the incoming events
|
|
177
|
-
}
|
|
178
|
-
if (ended)
|
|
179
|
-
_end();
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
function next(err, data, number) {
|
|
183
|
-
if (destroyed)
|
|
184
|
-
return;
|
|
185
|
-
in_next = true;
|
|
186
|
-
if (!err || options.failures)
|
|
187
|
-
queue_data(data, number);
|
|
188
|
-
if (err)
|
|
189
|
-
stream.emit(error_event_name, err);
|
|
190
|
-
in_next = false;
|
|
191
|
-
}
|
|
192
|
-
/** Wrap the mapper function by calling its callback with the order number of the item in the stream. */
|
|
193
|
-
function wrapped_mapper(input, number, callback) {
|
|
194
|
-
return mapper.call(null, input, function (err, data) {
|
|
195
|
-
callback(err, data, number);
|
|
196
|
-
});
|
|
97
|
+
/** 消费一个可读流 */
|
|
98
|
+
export async function consume_stream(stream, ignore_error = false) {
|
|
99
|
+
try {
|
|
100
|
+
for await (const chunk of stream)
|
|
101
|
+
;
|
|
197
102
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
stream.writable = false;
|
|
202
|
-
if (data !== undefined)
|
|
203
|
-
return queue_data(data, inputs);
|
|
204
|
-
else if (inputs === outputs) { // wait for processing
|
|
205
|
-
stream.readable = false;
|
|
206
|
-
stream.emit('end');
|
|
207
|
-
stream.destroy();
|
|
208
|
-
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (!ignore_error)
|
|
105
|
+
throw error;
|
|
209
106
|
}
|
|
210
|
-
return stream;
|
|
211
107
|
}
|
|
212
108
|
export async function* stream_to_lines(stream) {
|
|
213
109
|
let buf = '';
|
|
@@ -269,17 +165,6 @@ export class DecoderStream extends Transform {
|
|
|
269
165
|
callback();
|
|
270
166
|
}
|
|
271
167
|
}
|
|
272
|
-
/** 消费一个可读流 */
|
|
273
|
-
export async function consume_stream(stream, ignore_error = false) {
|
|
274
|
-
try {
|
|
275
|
-
for await (const chunk of stream)
|
|
276
|
-
;
|
|
277
|
-
}
|
|
278
|
-
catch (error) {
|
|
279
|
-
if (!ignore_error)
|
|
280
|
-
throw error;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
168
|
/** 根据 range 生成整数序列 (iterable)
|
|
284
169
|
- range: 取值为逗号分割的多个可用值或值区间 (不能含有空格),比如:`8321,8322,8300-8310,11000-11999`
|
|
285
170
|
- reverse?: `false` 在 range 内从后往前生成 */
|